aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2017-12-18 20:12:21 +0000
committerDimitry Andric <dim@FreeBSD.org>2017-12-18 20:12:21 +0000
commiteb1ff93d02b5f17b6b409e83c6d9be585f4a04b3 (patch)
tree7490b4a8943293f251ad733465936e6ec302b3e9
parentbafea25f368c63f0b39789906adfed6e39219e64 (diff)
downloadsrc-eb1ff93d02b5f17b6b409e83c6d9be585f4a04b3.tar.gz
src-eb1ff93d02b5f17b6b409e83c6d9be585f4a04b3.zip
Vendor import of lld trunk r321017:vendor/lld/lld-trunk-r321017
Notes
Notes: svn path=/vendor/lld/dist/; revision=326947 svn path=/vendor/lld/lld-trunk-r321017/; revision=326948; tag=vendor/lld/lld-trunk-r321017
-rw-r--r--.arcconfig2
-rw-r--r--CMakeLists.txt8
-rw-r--r--CODE_OWNERS.TXT3
-rw-r--r--COFF/CMakeLists.txt12
-rw-r--r--COFF/Chunks.cpp99
-rw-r--r--COFF/Chunks.h66
-rw-r--r--COFF/Config.h28
-rw-r--r--COFF/DLL.cpp65
-rw-r--r--COFF/Driver.cpp543
-rw-r--r--COFF/Driver.h43
-rw-r--r--COFF/DriverUtils.cpp216
-rw-r--r--COFF/Error.cpp114
-rw-r--r--COFF/Error.h62
-rw-r--r--COFF/ICF.cpp51
-rw-r--r--COFF/InputFiles.cpp394
-rw-r--r--COFF/InputFiles.h70
-rw-r--r--COFF/LTO.cpp61
-rw-r--r--COFF/LTO.h3
-rw-r--r--COFF/MapFile.cpp10
-rw-r--r--COFF/MarkLive.cpp18
-rw-r--r--COFF/Memory.h52
-rw-r--r--COFF/MinGW.cpp146
-rw-r--r--COFF/MinGW.h38
-rw-r--r--COFF/Options.td64
-rw-r--r--COFF/PDB.cpp553
-rw-r--r--COFF/PDB.h7
-rw-r--r--COFF/Strings.cpp2
-rw-r--r--COFF/Strings.h2
-rw-r--r--COFF/SymbolTable.cpp273
-rw-r--r--COFF/SymbolTable.h35
-rw-r--r--COFF/Symbols.cpp30
-rw-r--r--COFF/Symbols.h146
-rw-r--r--COFF/Writer.cpp331
-rw-r--r--COFF/Writer.h6
-rw-r--r--Common/Args.cpp62
-rw-r--r--Common/CMakeLists.txt32
-rw-r--r--Common/ErrorHandler.cpp (renamed from ELF/Error.cpp)82
-rw-r--r--Common/Memory.cpp23
-rw-r--r--Common/Reproduce.cpp (renamed from lib/Core/Reproduce.cpp)18
-rw-r--r--Common/Strings.cpp32
-rw-r--r--Common/TargetOptionsCommandFlags.cpp (renamed from lib/Core/TargetOptionsCommandFlags.cpp)12
-rw-r--r--Common/Threads.cpp12
-rw-r--r--Common/Version.cpp (renamed from lib/Config/Version.cpp)4
-rw-r--r--ELF/AArch64ErrataFix.cpp648
-rw-r--r--ELF/AArch64ErrataFix.h52
-rw-r--r--ELF/Arch/AArch64.cpp94
-rw-r--r--ELF/Arch/AMDGPU.cpp34
-rw-r--r--ELF/Arch/ARM.cpp189
-rw-r--r--ELF/Arch/AVR.cpp18
-rw-r--r--ELF/Arch/Mips.cpp463
-rw-r--r--ELF/Arch/MipsArchTree.cpp54
-rw-r--r--ELF/Arch/PPC.cpp36
-rw-r--r--ELF/Arch/PPC64.cpp16
-rw-r--r--ELF/Arch/SPARCV9.cpp13
-rw-r--r--ELF/Arch/X86.cpp137
-rw-r--r--ELF/Arch/X86_64.cpp46
-rw-r--r--ELF/Bits.h35
-rw-r--r--ELF/CMakeLists.txt17
-rw-r--r--ELF/Config.h40
-rw-r--r--ELF/Driver.cpp485
-rw-r--r--ELF/Driver.h5
-rw-r--r--ELF/DriverUtils.cpp68
-rw-r--r--ELF/EhFrame.cpp42
-rw-r--r--ELF/EhFrame.h6
-rw-r--r--ELF/Error.h78
-rw-r--r--ELF/Filesystem.cpp45
-rw-r--r--ELF/Filesystem.h3
-rw-r--r--ELF/GdbIndex.cpp88
-rw-r--r--ELF/GdbIndex.h89
-rw-r--r--ELF/ICF.cpp99
-rw-r--r--ELF/InputFiles.cpp541
-rw-r--r--ELF/InputFiles.h105
-rw-r--r--ELF/InputSection.cpp461
-rw-r--r--ELF/InputSection.h199
-rw-r--r--ELF/LTO.cpp63
-rw-r--r--ELF/LTO.h4
-rw-r--r--ELF/LinkerScript.cpp1381
-rw-r--r--ELF/LinkerScript.h211
-rw-r--r--ELF/MapFile.cpp95
-rw-r--r--ELF/MapFile.h6
-rw-r--r--ELF/MarkLive.cpp191
-rw-r--r--ELF/Options.td177
-rw-r--r--ELF/OutputSections.cpp488
-rw-r--r--ELF/OutputSections.h109
-rw-r--r--ELF/Relocations.cpp1078
-rw-r--r--ELF/Relocations.h64
-rw-r--r--ELF/ScriptLexer.cpp44
-rw-r--r--ELF/ScriptLexer.h3
-rw-r--r--ELF/ScriptParser.cpp302
-rw-r--r--ELF/ScriptParser.h5
-rw-r--r--ELF/Strings.cpp31
-rw-r--r--ELF/Strings.h6
-rw-r--r--ELF/SymbolTable.cpp631
-rw-r--r--ELF/SymbolTable.h99
-rw-r--r--ELF/Symbols.cpp253
-rw-r--r--ELF/Symbols.h399
-rw-r--r--ELF/SyntheticSections.cpp1632
-rw-r--r--ELF/SyntheticSections.h351
-rw-r--r--ELF/Target.cpp34
-rw-r--r--ELF/Target.h111
-rw-r--r--ELF/Thunks.cpp237
-rw-r--r--ELF/Thunks.h18
-rw-r--r--ELF/Writer.cpp1609
-rw-r--r--ELF/Writer.h16
-rw-r--r--MinGW/CMakeLists.txt23
-rw-r--r--MinGW/Driver.cpp247
-rw-r--r--MinGW/Options.td68
-rw-r--r--README.md12
-rw-r--r--cmake/modules/AddLLD.cmake12
-rw-r--r--docs/Driver.rst2
-rw-r--r--docs/NewLLD.rst139
-rw-r--r--docs/ReleaseNotes.rst169
-rw-r--r--docs/WebAssembly.rst36
-rw-r--r--docs/_templates/indexsidebar.html2
-rw-r--r--docs/conf.py4
-rw-r--r--docs/index.rst55
-rw-r--r--docs/sphinx_intro.rst36
-rw-r--r--include/lld/Common/Args.h35
-rw-r--r--include/lld/Common/Driver.h (renamed from include/lld/Driver/Driver.h)16
-rw-r--r--include/lld/Common/ErrorHandler.h112
-rw-r--r--include/lld/Common/LLVM.h (renamed from include/lld/Core/LLVM.h)4
-rw-r--r--include/lld/Common/Memory.h (renamed from ELF/Memory.h)15
-rw-r--r--include/lld/Common/Reproduce.h (renamed from include/lld/Core/Reproduce.h)8
-rw-r--r--include/lld/Common/Strings.h23
-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)24
-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/LinkingContext.h4
-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.h3
-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.cpp6
-rw-r--r--lib/ReaderWriter/CMakeLists.txt1
-rw-r--r--lib/ReaderWriter/FileArchive.cpp2
-rw-r--r--lib/ReaderWriter/MachO/ArchHandler.h2
-rw-r--r--lib/ReaderWriter/MachO/CMakeLists.txt2
-rw-r--r--lib/ReaderWriter/MachO/CompactUnwindPass.cpp2
-rw-r--r--lib/ReaderWriter/MachO/FlatNamespaceFile.h2
-rw-r--r--lib/ReaderWriter/MachO/GOTPass.cpp2
-rw-r--r--lib/ReaderWriter/MachO/MachOLinkingContext.cpp2
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFile.h11
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp7
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h2
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp11
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp2
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp2
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp8
-rw-r--r--lib/ReaderWriter/MachO/ObjCPass.cpp2
-rw-r--r--lib/ReaderWriter/MachO/ShimPass.cpp2
-rw-r--r--lib/ReaderWriter/MachO/StubsPass.cpp2
-rw-r--r--lib/ReaderWriter/YAML/ReaderWriterYAML.cpp6
-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/except_handler3.libbin0 -> 1364 bytes
-rw-r--r--test/COFF/Inputs/far-arm-thumb-abs20.s2
-rw-r--r--test/COFF/Inputs/gamma.ll14
-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/locally-imported-def.s4
-rw-r--r--test/COFF/Inputs/locally-imported-imp.s2
-rw-r--r--test/COFF/Inputs/lto-cache.ll10
-rw-r--r--test/COFF/Inputs/pdb-globals.yaml593
-rw-r--r--test/COFF/Inputs/pdb-hashes-1.yaml540
-rw-r--r--test/COFF/Inputs/pdb-hashes-2-missing.yaml321
-rw-r--r--test/COFF/Inputs/pdb-hashes-2.yaml355
-rw-r--r--test/COFF/Inputs/pdb-scopes-a.yaml116
-rw-r--r--test/COFF/Inputs/pdb-scopes-b.yaml100
-rw-r--r--test/COFF/Inputs/pdb_comdat_bar.yaml116
-rw-r--r--test/COFF/Inputs/pdb_comdat_main.yaml116
-rw-r--r--test/COFF/Inputs/pdb_lines_1.yaml118
-rw-r--r--test/COFF/Inputs/pdb_lines_2.yaml70
-rw-r--r--test/COFF/arm-thumb-branch20-error.s10
-rw-r--r--test/COFF/arm64-dynamicbase.s8
-rw-r--r--test/COFF/arm64-import2.test85
-rw-r--r--test/COFF/arm64-relocs-imports.test153
-rw-r--r--test/COFF/armnt-blx23t.test2
-rw-r--r--test/COFF/armnt-branch24t.test2
-rw-r--r--test/COFF/armnt-dynamicbase.test3
-rw-r--r--test/COFF/armnt-imports.test2
-rw-r--r--test/COFF/armnt-mov32t-exec.test2
-rw-r--r--test/COFF/armnt-movt32t.test2
-rw-r--r--test/COFF/common-alignment.test78
-rw-r--r--test/COFF/ctors_dtors_priority.s30
-rw-r--r--test/COFF/debug-dwarf.test19
-rw-r--r--test/COFF/def-export-stdcall.s78
-rw-r--r--test/COFF/delayimports-armnt.yaml106
-rw-r--r--test/COFF/delayimports32.test4
-rw-r--r--test/COFF/dllexport-mingw.s24
-rw-r--r--test/COFF/driver.test3
-rw-r--r--test/COFF/duplicate.test12
-rw-r--r--test/COFF/entry-drectve.test24
-rw-r--r--test/COFF/entry-inference.test8
-rw-r--r--test/COFF/export-all.s86
-rw-r--r--test/COFF/export-arm64.yaml70
-rw-r--r--test/COFF/export-armnt.yaml72
-rw-r--r--test/COFF/export32.test5
-rw-r--r--test/COFF/filename-casing.s14
-rw-r--r--test/COFF/force.test7
-rw-r--r--test/COFF/guardcf.test6
-rw-r--r--test/COFF/hello32.test6
-rw-r--r--test/COFF/icf-associative.test2
-rw-r--r--test/COFF/icf-executable.s18
-rw-r--r--test/COFF/icf-simple.test27
-rw-r--r--test/COFF/icf-xdata.s86
-rw-r--r--test/COFF/include.test13
-rw-r--r--test/COFF/libpath.test12
-rw-r--r--test/COFF/linkrepro-manifest.test12
-rw-r--r--test/COFF/linkrepro-pdb.test9
-rw-r--r--test/COFF/linkrepro-res.test12
-rw-r--r--test/COFF/loadcfg.test21
-rw-r--r--test/COFF/locally-imported-arm64.test61
-rw-r--r--test/COFF/locally-imported-warn-multiple.s14
-rw-r--r--test/COFF/locally-imported.test4
-rw-r--r--test/COFF/long-section-name.test23
-rw-r--r--test/COFF/lto-cache.ll21
-rw-r--r--test/COFF/lto-opt-level.ll12
-rw-r--r--test/COFF/lto-reloc-model.ll19
-rw-r--r--test/COFF/manifest.test16
-rw-r--r--test/COFF/manifestinput-error.test10
-rw-r--r--test/COFF/manifestinput-nowarning.test11
-rw-r--r--test/COFF/manifestinput.test4
-rw-r--r--test/COFF/msvclto-archive.ll6
-rw-r--r--test/COFF/msvclto-order.ll2
-rw-r--r--test/COFF/msvclto.ll2
-rw-r--r--test/COFF/nodefaultlib.test6
-rw-r--r--test/COFF/nopdb.test14
-rw-r--r--test/COFF/options.test8
-rw-r--r--test/COFF/pdata-arm64.yaml87
-rw-r--r--test/COFF/pdb-comdat.test43
-rw-r--r--test/COFF/pdb-diff.test17
-rw-r--r--test/COFF/pdb-global-gc.yaml22
-rw-r--r--test/COFF/pdb-global-hashes.test93
-rw-r--r--test/COFF/pdb-globals.test42
-rw-r--r--test/COFF/pdb-heapsite.yaml1561
-rw-r--r--test/COFF/pdb-import-gc.yaml20
-rw-r--r--test/COFF/pdb-invalid-func-type.yaml2
-rw-r--r--test/COFF/pdb-lib.s6
-rw-r--r--test/COFF/pdb-linker-module.test38
-rw-r--r--test/COFF/pdb-none.test3
-rw-r--r--test/COFF/pdb-options.test2
-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.yaml11
-rw-r--r--test/COFF/pdb-same-name.test23
-rw-r--r--test/COFF/pdb-scopes.test6
-rw-r--r--test/COFF/pdb-secrel-absolute.yaml11
-rw-r--r--test/COFF/pdb-source-lines.test35
-rw-r--r--test/COFF/pdb-symbol-types.yaml20
-rw-r--r--test/COFF/pdb-thunk.yaml2747
-rw-r--r--test/COFF/pdb-type-server-simple.test24
-rw-r--r--test/COFF/pdb.test160
-rw-r--r--test/COFF/reloc-arm.test16
-rw-r--r--test/COFF/reloc-discarded-dwarf.s2
-rw-r--r--test/COFF/reloc-discarded-early.s8
-rw-r--r--test/COFF/reloc-discarded-early2.s9
-rw-r--r--test/COFF/reloc-discarded.s1
-rw-r--r--test/COFF/responsefile.test20
-rw-r--r--test/COFF/rsds.test40
-rw-r--r--test/COFF/safeseh-md.s34
-rw-r--r--test/COFF/safeseh.s15
-rw-r--r--test/COFF/section-size.s14
-rw-r--r--test/COFF/seh-comdat.test66
-rw-r--r--test/COFF/strtab-size.s216
-rw-r--r--test/COFF/subsystem-drectve.test21
-rw-r--r--test/COFF/symtab.test6
-rw-r--r--test/COFF/thinlto.ll2
-rw-r--r--test/COFF/wholearchive.s19
-rw-r--r--test/COFF/wx.s17
-rw-r--r--test/ELF/Inputs/amdgpu-kernel-0.s6
-rw-r--r--test/ELF/Inputs/amdgpu-kernel-1.s6
-rw-r--r--test/ELF/Inputs/amdgpu-kernel-2.obin0 -> 408 bytes
-rw-r--r--test/ELF/Inputs/copy-rel-abs.s13
-rw-r--r--test/ELF/Inputs/copy-rel-large.s4
-rw-r--r--test/ELF/Inputs/copy-rel-pie.s1
-rw-r--r--test/ELF/Inputs/corrupt-version-reference.sobin0 -> 134272 bytes
-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/gc-sections-shared.s3
-rw-r--r--test/ELF/Inputs/gc-sections-shared2.s3
-rwxr-xr-xtest/ELF/Inputs/local-symbol-in-dso.sobin0 -> 5128 bytes
-rw-r--r--test/ELF/Inputs/map-file5.s23
-rw-r--r--test/ELF/Inputs/mips-micro.s12
-rw-r--r--test/ELF/Inputs/shared3.s2
-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/weak-undef-lazy.s3
-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/Inputs/wrap.s5
-rw-r--r--test/ELF/aarch64-abs16.s2
-rw-r--r--test/ELF/aarch64-abs32.s2
-rw-r--r--test/ELF/aarch64-call26-error.s11
-rw-r--r--test/ELF/aarch64-call26-thunk.s21
-rw-r--r--test/ELF/aarch64-cortex-a53-843419-address.s180
-rw-r--r--test/ELF/aarch64-cortex-a53-843419-cli.s10
-rw-r--r--test/ELF/aarch64-cortex-a53-843419-large.s115
-rw-r--r--test/ELF/aarch64-cortex-a53-843419-nopatch.s338
-rw-r--r--test/ELF/aarch64-cortex-a53-843419-recognize.s563
-rw-r--r--test/ELF/aarch64-cortex-a53-843419-thunk.s57
-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-jump26-error.s11
-rw-r--r--test/ELF/aarch64-jump26-thunk.s20
-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-prel16.s2
-rw-r--r--test/ELF/aarch64-prel32.s2
-rw-r--r--test/ELF/aarch64-thunk-pi.s91
-rw-r--r--test/ELF/aarch64-thunk-script.s41
-rw-r--r--test/ELF/aarch64-thunk-section-location.s41
-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/amdgpu-elf-flags-err.s7
-rw-r--r--test/ELF/amdgpu-elf-flags.s10
-rw-r--r--test/ELF/amdgpu-relocs.s15
-rw-r--r--test/ELF/arm-bl-v6.s51
-rw-r--r--test/ELF/arm-blx-v4t.s30
-rw-r--r--test/ELF/arm-branch-error.s19
-rw-r--r--test/ELF/arm-branch-rangethunk.s34
-rw-r--r--test/ELF/arm-branch-undef-weak-plt-thunk.s35
-rw-r--r--test/ELF/arm-copy.s2
-rw-r--r--test/ELF/arm-exidx-dedup.s126
-rw-r--r--test/ELF/arm-exidx-gc.s2
-rw-r--r--test/ELF/arm-exidx-order.s4
-rw-r--r--test/ELF/arm-exidx-sentinel-orphan.s2
-rw-r--r--test/ELF/arm-exidx-shared.s6
-rw-r--r--test/ELF/arm-gnu-ifunc-plt.s56
-rw-r--r--test/ELF/arm-gnu-ifunc.s41
-rw-r--r--test/ELF/arm-got-relative.s2
-rw-r--r--test/ELF/arm-icf-exidx.s2
-rw-r--r--test/ELF/arm-pie-relative.s2
-rw-r--r--test/ELF/arm-plt-reloc.s264
-rw-r--r--test/ELF/arm-static-defines.s2
-rw-r--r--test/ELF/arm-thumb-branch-error.s19
-rw-r--r--test/ELF/arm-thumb-branch-rangethunk.s36
-rw-r--r--test/ELF/arm-thumb-condbranch-thunk.s117
-rw-r--r--test/ELF/arm-thumb-interwork-shared.s37
-rw-r--r--test/ELF/arm-thumb-mix-range-thunk-os.s195
-rw-r--r--test/ELF/arm-thumb-plt-range-thunk-os.s92
-rw-r--r--test/ELF/arm-thumb-plt-reloc.s70
-rw-r--r--test/ELF/arm-thumb-range-thunk-os.s159
-rw-r--r--test/ELF/arm-thumb-thunk-empty-pass.s32
-rw-r--r--test/ELF/arm-thumb-thunk-symbols.s6
-rw-r--r--test/ELF/arm-thunk-edgecase.s37
-rw-r--r--test/ELF/arm-thunk-largesection.s42
-rw-r--r--test/ELF/arm-thunk-linkerscript-dotexpr.s77
-rw-r--r--test/ELF/arm-thunk-linkerscript-large.s176
-rw-r--r--test/ELF/arm-thunk-linkerscript-orphan.s63
-rw-r--r--test/ELF/arm-thunk-linkerscript-sort.s71
-rw-r--r--test/ELF/arm-thunk-linkerscript.s78
-rw-r--r--test/ELF/arm-thunk-multipass.s96
-rw-r--r--test/ELF/arm-thunk-re-add.s123
-rw-r--r--test/ELF/arm-thunk-toolargesection.s19
-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.s4
-rw-r--r--test/ELF/assignment-archive.s27
-rw-r--r--test/ELF/avoid-empty-program-headers.s6
-rw-r--r--test/ELF/basic-aarch64.s16
-rw-r--r--test/ELF/basic-mips.s4
-rw-r--r--test/ELF/basic-ppc.s4
-rw-r--r--test/ELF/basic-sparcv9.s16
-rw-r--r--test/ELF/basic.s18
-rw-r--r--test/ELF/basic32.s16
-rw-r--r--test/ELF/basic64be.s2
-rw-r--r--test/ELF/build-id.s21
-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/conflict-debug-variable.s144
-rw-r--r--test/ELF/conflict-debug-variable2.s160
-rw-r--r--test/ELF/copy-errors.s3
-rw-r--r--test/ELF/copy-rel-abs.s47
-rw-r--r--test/ELF/copy-rel-large.s20
-rw-r--r--test/ELF/copy-rel-pie.s2
-rw-r--r--test/ELF/corrupted-version-reference.s14
-rw-r--r--test/ELF/debug-gc.s4
-rw-r--r--test/ELF/defsym-dynamic.s10
-rw-r--r--test/ELF/defsym.s42
-rw-r--r--test/ELF/driver-access.test2
-rw-r--r--test/ELF/driver.test4
-rw-r--r--test/ELF/duplicated-synthetic-sym.s3
-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-wildcard.s53
-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.s2
-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/edata-no-bss.s18
-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-icf-fde.s95
-rw-r--r--test/ELF/eh-frame-hdr-icf.s15
-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.s2
-rw-r--r--test/ELF/eh-frame.s12
-rw-r--r--test/ELF/emit-relocs-gc.s30
-rw-r--r--test/ELF/emit-relocs-merge.s2
-rw-r--r--test/ELF/emit-relocs-mergeable-i386.s66
-rw-r--r--test/ELF/emit-relocs-mergeable.s53
-rw-r--r--test/ELF/emit-relocs-shared.s2
-rw-r--r--test/ELF/emit-relocs.s13
-rw-r--r--test/ELF/exclude-libs.s6
-rw-r--r--test/ELF/executable-undefined-ignoreall.s13
-rw-r--r--test/ELF/executable-undefined-protected-ignoreall.s8
-rw-r--r--test/ELF/file-access.s13
-rw-r--r--test/ELF/fill-trap.s25
-rw-r--r--test/ELF/filter.s6
-rw-r--r--test/ELF/format-binary-non-ascii.s15
-rw-r--r--test/ELF/gc-collect-undefined.s19
-rw-r--r--test/ELF/gc-merge-local-sym.s2
-rw-r--r--test/ELF/gc-sections-linker-defined-symbol.s18
-rw-r--r--test/ELF/gc-sections-merge-addend.s2
-rw-r--r--test/ELF/gc-sections-merge-implicit-addend.s2
-rw-r--r--test/ELF/gc-sections-merge.s4
-rw-r--r--test/ELF/gc-sections-print.s6
-rw-r--r--test/ELF/gc-sections-shared.s93
-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.s4
-rw-r--r--test/ELF/gdb-index-gc-sections.s2
-rw-r--r--test/ELF/gdb-index-noranges.s49
-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.s30
-rw-r--r--test/ELF/global-offset-table-position-aarch64.s2
-rw-r--r--test/ELF/global-offset-table-position-arm.s2
-rw-r--r--test/ELF/global-offset-table-position-i386.s2
-rw-r--r--test/ELF/global-offset-table-position.s2
-rw-r--r--test/ELF/global_offset_table_shared.s2
-rw-r--r--test/ELF/gnu-hash-table-copy.s30
-rw-r--r--test/ELF/gnu-hash-table-many.s55
-rw-r--r--test/ELF/gnu-hash-table-rwsegment.s20
-rw-r--r--test/ELF/gnu-hash-table.s79
-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-pie-rw.s17
-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-debug-noabs.test33
-rw-r--r--test/ELF/i386-got-and-copy.s2
-rw-r--r--test/ELF/i386-got-value.s36
-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-pc8-pc16-addend.s2
-rw-r--r--test/ELF/i386-reloc-16.s2
-rw-r--r--test/ELF/i386-reloc-8.s2
-rw-r--r--test/ELF/i386-reloc-range.s2
-rw-r--r--test/ELF/i386-reloc8-reloc16-addend.s2
-rw-r--r--test/ELF/i386-tls-ie-shared.s2
-rw-r--r--test/ELF/icf-absolute.s2
-rw-r--r--test/ELF/icf-comdat.s2
-rw-r--r--test/ELF/icf-i386.s2
-rw-r--r--test/ELF/icf-merge-sec.s2
-rw-r--r--test/ELF/icf-merge.s6
-rw-r--r--test/ELF/icf-non-mergeable.s4
-rw-r--r--test/ELF/icf-none.s2
-rw-r--r--test/ELF/icf-symbol-type.s26
-rw-r--r--test/ELF/icf1.s2
-rw-r--r--test/ELF/icf2.s2
-rw-r--r--test/ELF/icf3.s2
-rw-r--r--test/ELF/icf4.s2
-rw-r--r--test/ELF/icf5.s2
-rw-r--r--test/ELF/icf6.s2
-rw-r--r--test/ELF/icf7.s2
-rw-r--r--test/ELF/icf9.s22
-rw-r--r--test/ELF/image-base.s7
-rw-r--r--test/ELF/init_fini_priority.s24
-rw-r--r--test/ELF/invalid-linkerscript.test2
-rw-r--r--test/ELF/invalid-local-symbol-in-dso.s13
-rw-r--r--test/ELF/invalid-undef-section-symbol.test27
-rw-r--r--test/ELF/invalid/Inputs/section-index2.elfbin474 -> 0 bytes
-rw-r--r--test/ELF/invalid/invalid-debug-relocations.test3
-rw-r--r--test/ELF/invalid/invalid-elf.test4
-rw-r--r--test/ELF/invalid/invalid-relocation-x64.test18
-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/copy-rel-symbol-value.s5
-rw-r--r--test/ELF/linkerscript/Inputs/provide-shared.s5
-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.s45
-rw-r--r--test/ELF/linkerscript/arm-exidx-order.s19
-rw-r--r--test/ELF/linkerscript/arm-exidx-sentinel-and-assignment.s24
-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/copy-rel-symbol-value-err.s12
-rw-r--r--test/ELF/linkerscript/copy-rel-symbol-value.s27
-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/eh-frame-reloc-out-of-range.s2
-rw-r--r--test/ELF/linkerscript/emit-reloc-section-names.s22
-rw-r--r--test/ELF/linkerscript/emit-reloc.s2
-rw-r--r--test/ELF/linkerscript/emit-relocs-multiple.s2
-rw-r--r--test/ELF/linkerscript/extend-pt-load.s13
-rw-r--r--test/ELF/linkerscript/filename-spec.s47
-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/linker-script-in-search-path.s19
-rw-r--r--test/ELF/linkerscript/linkerscript.s4
-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/memory3.s23
-rw-r--r--test/ELF/linkerscript/merge-sections.s2
-rw-r--r--test/ELF/linkerscript/no-space.s4
-rw-r--r--test/ELF/linkerscript/noload.s4
-rw-r--r--test/ELF/linkerscript/non-alloc.s2
-rw-r--r--test/ELF/linkerscript/operators.s2
-rw-r--r--test/ELF/linkerscript/orphan-discard.s25
-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.s54
-rw-r--r--test/ELF/linkerscript/out-of-order.s2
-rw-r--r--test/ELF/linkerscript/phdr-check.s4
-rw-r--r--test/ELF/linkerscript/phdrs.s4
-rw-r--r--test/ELF/linkerscript/provide-shared.s13
-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-start.s2
-rw-r--r--test/ELF/linkerscript/sort-non-script.s2
-rw-r--r--test/ELF/linkerscript/subalign.s17
-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-only.s2
-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/thunk-gen-mips.s40
-rw-r--r--test/ELF/linkerscript/unused-synthetic.s10
-rw-r--r--test/ELF/linkerscript/version-linker-symbol.s28
-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/cache.ll14
-rw-r--r--test/ELF/lto/data-ordering-lto.s27
-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.ll21
-rw-r--r--test/ELF/lto/opt-remarks.ll25
-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/shlib-undefined.ll2
-rw-r--r--test/ELF/lto/symbol-ordering-lto.s25
-rw-r--r--test/ELF/lto/thinlto.ll8
-rw-r--r--test/ELF/lto/verify-invalid.ll2
-rw-r--r--test/ELF/lto/wrap-1.ll2
-rw-r--r--test/ELF/lto/wrap-2.ll4
-rw-r--r--test/ELF/many-alloc-sections.s3
-rw-r--r--test/ELF/many-sections.s7
-rw-r--r--test/ELF/map-file.s86
-rw-r--r--test/ELF/merge-align.s34
-rw-r--r--test/ELF/merge-entsize.s27
-rw-r--r--test/ELF/merge-reloc.s19
-rw-r--r--test/ELF/merge-string.s14
-rw-r--r--test/ELF/merge.s2
-rw-r--r--test/ELF/mips-26-n32-n64.s35
-rw-r--r--test/ELF/mips-64-gprel-so.s2
-rw-r--r--test/ELF/mips-64-rels.s6
-rw-r--r--test/ELF/mips-align-err.s2
-rw-r--r--test/ELF/mips-elf-flags-err.s10
-rw-r--r--test/ELF/mips-elf-flags.s29
-rw-r--r--test/ELF/mips-got-page-script.s65
-rw-r--r--test/ELF/mips-got-relocs.s4
-rw-r--r--test/ELF/mips-got-script.s47
-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.s8
-rw-r--r--test/ELF/mips-gprel32-relocs.s8
-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-plt.s91
-rw-r--r--test/ELF/mips-micro-relocs.s59
-rw-r--r--test/ELF/mips-micro-thunks.s47
-rw-r--r--test/ELF/mips-n32-rels.s6
-rw-r--r--test/ELF/mips-out-of-bounds-call16-reloc.s29
-rw-r--r--test/ELF/no-inhibit-exec.s4
-rw-r--r--test/ELF/non-abs-reloc.s2
-rw-r--r--test/ELF/noplt-pie.s2
-rw-r--r--test/ELF/pack-dyn-relocs.s210
-rw-r--r--test/ELF/pie-weak.s7
-rw-r--r--test/ELF/ppc-relocs.s36
-rw-r--r--test/ELF/ppc64-addr16-error.s2
-rw-r--r--test/ELF/pr34660.s25
-rw-r--r--test/ELF/pr34872.s14
-rw-r--r--test/ELF/progname.s2
-rw-r--r--test/ELF/relocatable-comdat2.s35
-rw-r--r--test/ELF/relocatable-common.s5
-rw-r--r--test/ELF/relocatable-compressed-input.s10
-rw-r--r--test/ELF/relocatable.s2
-rw-r--r--test/ELF/relocation-b-aarch64.test48
-rw-r--r--test/ELF/relocation-copy-alias.s8
-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.s2
-rw-r--r--test/ELF/relro-copyrel-bss-script.s40
-rw-r--r--test/ELF/relro-non-contiguous-script-data.s25
-rw-r--r--test/ELF/relro-non-contiguous.s28
-rw-r--r--test/ELF/relro-omagic.s2
-rw-r--r--test/ELF/relro-script.s29
-rw-r--r--test/ELF/reproduce-thin-archive.s6
-rw-r--r--test/ELF/reproduce.s31
-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/segments.s5
-rw-r--r--test/ELF/shared-lazy.s16
-rw-r--r--test/ELF/shared.s6
-rw-r--r--test/ELF/silent-ignore.test20
-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/strip-debug.s27
-rw-r--r--test/ELF/symbol-ordering-file2.s21
-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/trace-symbols.s14
-rw-r--r--test/ELF/typed-undef.s11
-rw-r--r--test/ELF/undef-broken-debug.test47
-rw-r--r--test/ELF/undef-version-script.s6
-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-twice.s4
-rw-r--r--test/ELF/version-script.s10
-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.s12
-rw-r--r--test/ELF/wrap-no-real.s77
-rw-r--r--test/ELF/wrap.s34
-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-reloc-16.s2
-rw-r--r--test/ELF/x86-64-reloc-8.s2
-rw-r--r--test/ELF/x86-64-reloc-error.s4
-rw-r--r--test/ELF/x86-64-reloc-range.s2
-rw-r--r--test/ELF/x86-64-tls-gd-local.s2
-rw-r--r--test/MinGW/driver.test126
-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.cfg270
-rw-r--r--test/lit.cfg.py93
-rw-r--r--test/lit.site.cfg.py.in (renamed from test/lit.site.cfg.in)5
-rw-r--r--test/wasm/Inputs/archive1.ll7
-rw-r--r--test/wasm/Inputs/archive2.ll7
-rw-r--r--test/wasm/Inputs/call-indirect.ll17
-rw-r--r--test/wasm/Inputs/hello.ll15
-rw-r--r--test/wasm/Inputs/hidden.ll11
-rw-r--r--test/wasm/Inputs/many-funcs.ll776
-rw-r--r--test/wasm/Inputs/ret32.ll6
-rw-r--r--test/wasm/Inputs/ret64.ll4
-rw-r--r--test/wasm/Inputs/weak-alias.ll13
-rw-r--r--test/wasm/Inputs/weak-symbol1.ll9
-rw-r--r--test/wasm/Inputs/weak-symbol2.ll9
-rw-r--r--test/wasm/archive.ll31
-rw-r--r--test/wasm/call-indirect.ll112
-rw-r--r--test/wasm/conflict.test6
-rw-r--r--test/wasm/data-layout.ll61
-rw-r--r--test/wasm/entry.ll19
-rw-r--r--test/wasm/function-imports-first.ll42
-rw-r--r--test/wasm/function-imports.ll37
-rw-r--r--test/wasm/function-index.test18
-rw-r--r--test/wasm/import-memory.test13
-rw-r--r--test/wasm/invalid-stack-size.test9
-rw-r--r--test/wasm/lit.local.cfg4
-rw-r--r--test/wasm/load-undefined.ll38
-rw-r--r--test/wasm/local-symbols.ll78
-rw-r--r--test/wasm/many-functions.ll695
-rw-r--r--test/wasm/relocatable.ll194
-rw-r--r--test/wasm/signature-mismatch.ll16
-rw-r--r--test/wasm/stack-pointer.ll64
-rw-r--r--test/wasm/strip-debug.test6
-rw-r--r--test/wasm/symbol-type-mismatch.ll9
-rw-r--r--test/wasm/undefined-entry.test4
-rw-r--r--test/wasm/undefined.ll20
-rw-r--r--test/wasm/version.ll13
-rw-r--r--test/wasm/visibility-hidden.ll46
-rw-r--r--test/wasm/weak-alias-overide.ll92
-rw-r--r--test/wasm/weak-alias.ll82
-rw-r--r--test/wasm/weak-external.ll86
-rw-r--r--test/wasm/weak-symbols.ll93
-rw-r--r--tools/lld/CMakeLists.txt7
-rw-r--r--tools/lld/lld.cpp22
-rw-r--r--unittests/DriverTests/CMakeLists.txt1
-rw-r--r--unittests/DriverTests/DarwinLdDriverTest.cpp2
-rw-r--r--unittests/MachOTests/CMakeLists.txt1
-rwxr-xr-xutils/benchmark.py135
-rw-r--r--utils/link.yaml39
-rw-r--r--wasm/CMakeLists.txt26
-rw-r--r--wasm/Config.h51
-rw-r--r--wasm/Driver.cpp321
-rw-r--r--wasm/InputFiles.cpp303
-rw-r--r--wasm/InputFiles.h153
-rw-r--r--wasm/InputSegment.cpp25
-rw-r--r--wasm/InputSegment.h74
-rw-r--r--wasm/Options.td103
-rw-r--r--wasm/OutputSections.cpp333
-rw-r--r--wasm/OutputSections.h138
-rw-r--r--wasm/OutputSegment.h56
-rw-r--r--wasm/SymbolTable.cpp245
-rw-r--r--wasm/SymbolTable.h72
-rw-r--r--wasm/Symbols.cpp114
-rw-r--r--wasm/Symbols.h128
-rw-r--r--wasm/Writer.cpp724
-rw-r--r--wasm/Writer.h21
-rw-r--r--wasm/WriterUtils.cpp215
-rw-r--r--wasm/WriterUtils.h78
815 files changed, 35716 insertions, 9347 deletions
diff --git a/.arcconfig b/.arcconfig
index ebf4a4a6f8b7..c8a8e079023f 100644
--- a/.arcconfig
+++ b/.arcconfig
@@ -1,4 +1,4 @@
{
- "project_id" : "lld",
+ "repository.callsign" : "LLD",
"conduit_uri" : "https://reviews.llvm.org/"
}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e2ab0e35f1ab..e2fbdbfbbb47 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)
+add_subdirectory(wasm)
diff --git a/CODE_OWNERS.TXT b/CODE_OWNERS.TXT
index 292967e588f0..f019a87553aa 100644
--- a/CODE_OWNERS.TXT
+++ b/CODE_OWNERS.TXT
@@ -17,3 +17,6 @@ N: Lang Hames, Nick Kledzik
E: lhames@gmail.com, kledzik@apple.com
D: Mach-O backend
+N: Sam Clegg
+E: sbc@chromium.org
+D: WebAssembly backend (wasm/*)
diff --git a/COFF/CMakeLists.txt b/COFF/CMakeLists.txt
index e56593497000..4610ccc880fd 100644
--- a/COFF/CMakeLists.txt
+++ b/COFF/CMakeLists.txt
@@ -11,12 +11,12 @@ add_lld_library(lldCOFF
DLL.cpp
Driver.cpp
DriverUtils.cpp
- Error.cpp
ICF.cpp
InputFiles.cpp
LTO.cpp
MapFile.cpp
MarkLive.cpp
+ MinGW.cpp
PDB.cpp
Strings.cpp
SymbolTable.cpp
@@ -26,22 +26,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 7d93c28c86c8..557b02654426 100644
--- a/COFF/Chunks.cpp
+++ b/COFF/Chunks.cpp
@@ -8,10 +8,10 @@
//===----------------------------------------------------------------------===//
#include "Chunks.h"
-#include "Error.h"
#include "InputFiles.h"
#include "Symbols.h"
#include "Writer.h"
+#include "lld/Common/ErrorHandler.h"
#include "llvm/ADT/Twine.h"
#include "llvm/BinaryFormat/COFF.h"
#include "llvm/Object/COFF.h"
@@ -29,17 +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();
-
- // Chunks may be discarded during comdat merging.
- Discarded = false;
+ Alignment = Header->getAlignment();
// If linker GC is disabled, every chunk starts out alive. If linker GC is
// enabled, treat non-comdat sections as roots. Generally optimized object
@@ -62,7 +59,10 @@ static void applySecRel(const SectionChunk *Sec, uint8_t *Off,
fatal("SECREL relocation cannot be applied to absolute symbols");
}
uint64_t SecRel = S - OS->getRVA();
- assert(SecRel < INT32_MAX && "overflow in SECREL relocation");
+ if (SecRel > UINT32_MAX) {
+ error("overflow in SECREL relocation in section: " + Sec->getSectionName());
+ return;
+ }
add32(Off, SecRel);
}
@@ -119,7 +119,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);
@@ -129,6 +129,8 @@ static void applyMOV32T(uint8_t *Off, uint32_t V) {
}
static void applyBranch20T(uint8_t *Off, int32_t V) {
+ if (!isInt<21>(V))
+ fatal("relocation out of range");
uint32_t S = V < 0 ? 1 : 0;
uint32_t J1 = (V >> 19) & 1;
uint32_t J2 = (V >> 18) & 1;
@@ -136,7 +138,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;
@@ -167,36 +169,61 @@ void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, OutputSection *OS,
}
}
-static void applyArm64Addr(uint8_t *Off, uint64_t Imm) {
+// 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, (read32le(Off) & ~Mask) | ImmLo | ImmHi);
+ write32le(Off, (Orig & ~Mask) | ImmLo | ImmHi);
}
// Update the immediate field in a AARCH64 ldr, str, and add instruction.
-static void applyArm64Imm(uint8_t *Off, uint64_t Imm) {
+// 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) << 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) {
- int Size = read32le(Off) >> 30;
- Imm >>= Size;
- applyArm64Imm(Off, 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_ARM64_PAGEBASE_REL21: applyArm64Addr(Off, (S >> 12) - (P >> 12)); break;
- case IMAGE_REL_ARM64_PAGEOFFSET_12A: applyArm64Imm(Off, S & 0xfff); 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;
+ case IMAGE_REL_ARM64_SECREL: applySecRel(this, Off, OS, S); break;
default:
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
}
@@ -224,8 +251,19 @@ void SectionChunk::writeTo(uint8_t *Buf) const {
// 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);
+ auto *Sym =
+ dyn_cast_or_null<Defined>(File->getSymbol(Rel.SymbolTableIndex));
+ if (!Sym) {
+ if (isCodeView() || isDWARF())
+ continue;
+ // Symbols in early discarded sections are represented using null pointers,
+ // so we need to retrieve the name from the object file.
+ COFFSymbolRef Sym =
+ check(File->getCOFFObj()->getSymbol(Rel.SymbolTableIndex));
+ StringRef Name;
+ File->getCOFFObj()->getSymbolName(Sym, Name);
+ fatal("relocation against symbol in discarded section: " + Name);
+ }
Chunk *C = Sym->getChunk();
OutputSection *OS = C ? C->getOutputSection() : nullptr;
@@ -301,8 +339,8 @@ void SectionChunk::getBaserels(std::vector<Baserel> *Res) {
uint8_t Ty = getBaserelType(Rel);
if (Ty == IMAGE_REL_BASED_ABSOLUTE)
continue;
- SymbolBody *Body = File->getSymbolBody(Rel.SymbolTableIndex);
- if (isa<DefinedAbsolute>(Body))
+ Symbol *Target = File->getSymbol(Rel.SymbolTableIndex);
+ if (!Target || isa<DefinedAbsolute>(Target))
continue;
Res->emplace_back(RVA + Rel.VirtualAddress, Ty);
}
@@ -323,12 +361,8 @@ bool SectionChunk::isCOMDAT() const {
void SectionChunk::printDiscardedMessage() const {
// Removed by dead-stripping. If it's removed by ICF, ICF already
// printed out the name, so don't repeat that here.
- if (Sym && this == Repl) {
- if (Discarded)
- message("Discarded comdat symbol " + Sym->getName());
- else if (!Live)
- message("Discarded " + Sym->getName());
- }
+ if (Sym && this == Repl)
+ message("Discarded " + Sym->getName());
}
StringRef SectionChunk::getDebugName() {
@@ -351,7 +385,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 {
@@ -366,7 +400,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 {
@@ -397,10 +431,9 @@ void ImportThunkChunkARM::writeTo(uint8_t *Buf) const {
}
void ImportThunkChunkARM64::writeTo(uint8_t *Buf) const {
- int64_t PageOff = (ImpSymbol->getRVA() >> 12) - (RVA >> 12);
int64_t Off = ImpSymbol->getRVA() & 0xfff;
memcpy(Buf + OutputSectionOff, ImportThunkARM64, sizeof(ImportThunkARM64));
- applyArm64Addr(Buf + OutputSectionOff, PageOff);
+ applyArm64Addr(Buf + OutputSectionOff, ImpSymbol->getRVA(), RVA);
applyArm64Ldr(Buf + OutputSectionOff + 4, Off);
}
@@ -488,8 +521,10 @@ void BaserelChunk::writeTo(uint8_t *Buf) const {
uint8_t Baserel::getDefaultType() {
switch (Config->Machine) {
case AMD64:
+ case ARM64:
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 ece5419e255e..381527ee6ef2 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,9 +33,9 @@ class Baserel;
class Defined;
class DefinedImportData;
class DefinedRegular;
-class ObjectFile;
+class ObjFile;
class OutputSection;
-class SymbolBody;
+class Symbol;
// Mask for section types (code, data, bss, disacardable, etc.)
// and permissions (writable, readable or executable).
@@ -62,7 +62,6 @@ 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; }
// Returns true if this has non-zero data. BSS chunks return
@@ -82,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.
@@ -92,23 +91,22 @@ 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;
- // The alignment of this chunk. The writer uses the value.
- uint32_t Align = 1;
-
// The RVA of this chunk in the output. The writer sets a value.
uint64_t RVA = 0;
+ // The output section for this chunk.
+ OutputSection *Out = nullptr;
+
public:
// The offset from beginning of the output section. The writer sets a value.
uint64_t OutputSectionOff = 0;
-
-protected:
- // The output section for this chunk.
- OutputSection *Out = nullptr;
};
// A chunk corresponding a section of an input file.
@@ -119,23 +117,21 @@ class SectionChunk final : public Chunk {
public:
class symbol_iterator : public llvm::iterator_adaptor_base<
symbol_iterator, const coff_relocation *,
- std::random_access_iterator_tag, SymbolBody *> {
+ std::random_access_iterator_tag, Symbol *> {
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:
symbol_iterator() = default;
- SymbolBody *operator*() const {
- return File->getSymbolBody(I->SymbolTableIndex);
- }
+ Symbol *operator*() const { return File->getSymbol(I->SymbolTableIndex); }
};
- 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;
@@ -163,10 +159,9 @@ public:
void addAssociative(SectionChunk *Child);
StringRef getDebugName() override;
- void setSymbol(DefinedRegular *S) { if (!Sym) Sym = S; }
- // Returns true if the chunk was not dropped by GC or COMDAT deduplication.
- bool isLive() { return Live && !Discarded; }
+ // Returns true if the chunk was not dropped by GC.
+ bool isLive() { return Live; }
// Used by the garbage collector.
void markLive() {
@@ -175,21 +170,16 @@ public:
Live = true;
}
- // Returns true if this chunk was dropped by COMDAT deduplication.
- bool isDiscarded() const { return Discarded; }
-
- // Used by the SymbolTable when discarding unused comdat sections. This is
- // 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 chunk.
- bool isDWARF() const { return 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 {
@@ -213,7 +203,10 @@ public:
const coff_section *Header;
// The file that this chunk was created from.
- ObjectFile *File;
+ ObjFile *File;
+
+ // The COMDAT leader symbol if this is a COMDAT chunk.
+ DefinedRegular *Sym = nullptr;
private:
StringRef SectionName;
@@ -221,18 +214,12 @@ private:
llvm::iterator_range<const coff_relocation *> Relocs;
size_t NumRelocs;
- // True if this chunk was discarded because it was a duplicate comdat section.
- bool Discarded;
-
// Used by the garbage collector.
bool Live;
// Used for ICF (Identical COMDAT Folding)
void replace(SectionChunk *Other);
uint32_t Class[2] = {0, 0};
-
- // Sym points to a section symbol if this is a COMDAT chunk.
- DefinedRegular *Sym = nullptr;
};
// A chunk for common symbols. Common chunks don't have actual data.
@@ -369,6 +356,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 7f8259d016e1..4eb8bae3c622 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>
@@ -26,8 +27,7 @@ using llvm::StringRef;
class DefinedAbsolute;
class DefinedRelative;
class StringChunk;
-struct Symbol;
-class SymbolBody;
+class Symbol;
// Short aliases.
static const auto AMD64 = llvm::COFF::IMAGE_FILE_MACHINE_AMD64;
@@ -39,7 +39,7 @@ static const auto I386 = llvm::COFF::IMAGE_FILE_MACHINE_I386;
struct Export {
StringRef Name; // N in /export:N or /export:E=N
StringRef ExtName; // E in /export:E=N
- SymbolBody *Sym = nullptr;
+ Symbol *Sym = nullptr;
uint16_t Ordinal = 0;
bool Noname = false;
bool Data = false;
@@ -79,24 +79,23 @@ struct Configuration {
llvm::COFF::MachineTypes Machine = IMAGE_FILE_MACHINE_UNKNOWN;
bool Verbose = false;
WindowsSubsystem Subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN;
- SymbolBody *Entry = nullptr;
+ Symbol *Entry = nullptr;
bool NoEntry = false;
std::string OutputFile;
std::string ImportName;
- bool ColorDiagnostics;
bool DoGC = true;
bool DoICF = true;
- uint64_t ErrorLimit = 20;
bool Relocatable = true;
bool Force = false;
bool Debug = false;
- bool WriteSymtab = true;
+ bool DebugDwarf = false;
+ bool DebugGHashes = false;
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;
+ std::vector<Symbol *> GCRoot;
std::set<StringRef> NoDefaultLibs;
bool NoDefaultLibAll = false;
@@ -107,7 +106,7 @@ struct Configuration {
std::vector<Export> Exports;
std::set<std::string> DelayLoads;
std::map<std::string, int> DLLOrder;
- SymbolBody *DelayLoadHelper = nullptr;
+ Symbol *DelayLoadHelper = nullptr;
bool SaveTemps = false;
@@ -123,6 +122,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;
@@ -139,6 +143,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;
@@ -157,13 +164,16 @@ struct Configuration {
uint32_t MinorImageVersion = 0;
uint32_t MajorOSVersion = 6;
uint32_t MinorOSVersion = 0;
+ bool CanExitEarly = false;
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 d76410b67471..847d15d8594a 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 854c3e690981..0e7db7b6ae34 100644
--- a/COFF/Driver.cpp
+++ b/COFF/Driver.cpp
@@ -9,13 +9,15 @@
#include "Driver.h"
#include "Config.h"
-#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 "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+#include "lld/Common/Version.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/BinaryFormat/Magic.h"
@@ -48,20 +50,28 @@ namespace coff {
Configuration *Config;
LinkerDriver *Driver;
-BumpPtrAllocator BAlloc;
-StringSaver Saver{BAlloc};
-std::vector<SpecificAllocBase *> SpecificAllocBase::Instances;
-
-bool link(ArrayRef<const char *> Args, raw_ostream &Diag) {
- ErrorCount = 0;
- ErrorOS = &Diag;
+bool link(ArrayRef<const char *> Args, bool CanExitEarly, raw_ostream &Diag) {
+ errorHandler().LogName = Args[0];
+ errorHandler().ErrorOS = &Diag;
+ errorHandler().ColorDiagnostics = Diag.has_colors();
+ errorHandler().ErrorLimitExceededMsg =
+ "too many errors emitted, stopping now"
+ " (use /ERRORLIMIT:0 to see all errors)";
Config = make<Configuration>();
Config->Argv = {Args.begin(), Args.end()};
- Config->ColorDiagnostics =
- (ErrorOS == &llvm::errs() && Process::StandardErrHasColors());
+ Config->CanExitEarly = CanExitEarly;
+
+ Symtab = make<SymbolTable>();
+
Driver = make<LinkerDriver>();
Driver->link(Args);
- return !ErrorCount;
+
+ // Call exit() if we can to avoid calling destructors.
+ if (CanExitEarly)
+ exitLld(errorCount() ? 1 : 0);
+
+ freeArena();
+ return !errorCount();
}
// Drop directory components and replace extension with ".exe" or ".dll".
@@ -107,30 +117,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");
+
+ 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;
- if (Magic == file_magic::coff_cl_gl_object)
+ 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 +165,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 +173,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 +188,7 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef MB, StringRef SymName,
}
Obj->ParentName = ParentName;
- Symtab.addFile(Obj);
+ Symtab->addFile(Obj);
log("Loaded " + toString(Obj) + " for " + SymName);
}
@@ -170,7 +196,7 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &C,
StringRef SymName,
StringRef ParentName) {
if (!C.getParent()->isThin()) {
- MemoryBufferRef MB = check(
+ MemoryBufferRef MB = CHECK(
C.getMemoryBufferRef(),
"could not get the buffer for the member defining symbol " + SymName);
enqueueTask([=]() { Driver->addArchiveBuffer(MB, SymName, ParentName); });
@@ -178,39 +204,54 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &C,
}
auto Future = std::make_shared<std::future<MBErrPair>>(createFutureForFile(
- check(C.getFullName(),
+ CHECK(C.getFullName(),
"could not get the filename for the member defining symbol " +
SymName)));
enqueueTask([=]() {
auto MBOrErr = Future->get();
if (MBOrErr.second)
- fatal(MBOrErr.second,
- "could not get the buffer for the member defining " + SymName);
+ fatal("could not get the buffer for the member defining " + SymName +
+ ": " + MBOrErr.second.message());
Driver->addArchiveBuffer(takeBuffer(std::move(MBOrErr.first)), SymName,
ParentName);
});
}
static bool isDecorated(StringRef Sym) {
- return Sym.startswith("_") || Sym.startswith("@") || Sym.startswith("?");
+ return Sym.startswith("@") || Sym.contains("@@") || Sym.startswith("?") ||
+ (!Config->MinGW && Sym.contains('@'));
}
// 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_entry:
+ Config->Entry = addUndefined(mangle(Arg->getValue()));
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;
@@ -230,9 +271,14 @@ void LinkerDriver::parseDirectives(StringRef S) {
case OPT_section:
parseSection(Arg->getValue());
break;
+ case OPT_subsystem:
+ parseSubsystem(Arg->getValue(), &Config->Subsystem,
+ &Config->MajorOSVersion, &Config->MinorOSVersion);
+ break;
case OPT_editandcontinue:
case OPT_fastfail:
case OPT_guardsym:
+ case OPT_natvis:
case OPT_throwingnew:
break;
default:
@@ -247,7 +293,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);
@@ -269,13 +315,15 @@ Optional<StringRef> LinkerDriver::findFile(StringRef Filename) {
bool Seen = !VisitedFiles.insert(Path.lower()).second;
if (Seen)
return None;
+ if (Path.endswith_lower(".lib"))
+ VisitedLibs.insert(sys::path::filename(Path));
return Path;
}
// 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);
@@ -310,9 +358,12 @@ void LinkerDriver::addLibSearchPaths() {
}
}
-SymbolBody *LinkerDriver::addUndefined(StringRef Name) {
- SymbolBody *B = Symtab.addUndefined(Name);
- Config->GCRoot.insert(B);
+Symbol *LinkerDriver::addUndefined(StringRef Name) {
+ Symbol *B = Symtab->addUndefined(Name);
+ if (!B->IsGCRoot) {
+ B->IsGCRoot = true;
+ Config->GCRoot.push_back(B);
+ }
return B;
}
@@ -334,8 +385,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)))
return mangle(E[1]);
}
return "";
@@ -344,9 +395,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;
}
@@ -369,9 +420,15 @@ static std::string createResponseFile(const opt::InputArgList &Args,
case OPT_INPUT:
case OPT_defaultlib:
case OPT_libpath:
+ case OPT_manifest:
+ case OPT_manifest_colon:
+ case OPT_manifestdependency:
+ case OPT_manifestfile:
+ case OPT_manifestinput:
+ case OPT_manifestuac:
break;
default:
- OS << toString(Arg) << "\n";
+ OS << toString(*Arg) << "\n";
}
}
@@ -469,15 +526,17 @@ static void createImportLibrary(bool AsLib) {
Exports.push_back(E2);
}
- writeImportLibrary(getImportName(AsLib), getImplibPath(), Exports,
- Config->Machine, false);
+ auto E = writeImportLibrary(getImportName(AsLib), getImplibPath(), Exports,
+ Config->Machine, false);
+ handleAllErrors(std::move(E),
+ [&](ErrorInfoBase &EIB) { error(EIB.message()); });
}
static void parseModuleDefs(StringRef Path) {
- std::unique_ptr<MemoryBuffer> MB = check(
- MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path);
- COFFModuleDefinition M =
- check(parseCOFFModuleDefinition(MB->getMemBufferRef(), Config->Machine));
+ std::unique_ptr<MemoryBuffer> MB = CHECK(
+ MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path);
+ COFFModuleDefinition M = check(parseCOFFModuleDefinition(
+ MB->getMemBufferRef(), Config->Machine, Config->MinGW));
if (Config->OutputFile.empty())
Config->OutputFile = Saver.save(M.OutputFile);
@@ -515,31 +574,12 @@ 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
// archive, we always need to rebuild it.
std::unique_ptr<Archive> File =
- check(Archive::create(MB), "Failed to read " + MB.getBufferIdentifier());
+ CHECK(Archive::create(MB), "Failed to read " + MB.getBufferIdentifier());
if (File->isThin())
return true;
@@ -560,7 +600,7 @@ static bool needsRebuilding(MemoryBufferRef MB) {
// its path is returned.
static Optional<std::string>
filterBitcodeFiles(StringRef Path, std::vector<std::string> &TemporaryFiles) {
- std::unique_ptr<MemoryBuffer> MB = check(
+ std::unique_ptr<MemoryBuffer> MB = CHECK(
MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path);
MemoryBufferRef MBRef = MB->getMemBufferRef();
file_magic Magic = identify_magic(MBRef.getBuffer());
@@ -573,7 +613,7 @@ filterBitcodeFiles(StringRef Path, std::vector<std::string> &TemporaryFiles) {
return Path.str();
std::unique_ptr<Archive> File =
- check(Archive::create(MBRef),
+ CHECK(Archive::create(MBRef),
MBRef.getBufferIdentifier() + ": failed to parse archive");
std::vector<NewArchiveMember> New;
@@ -589,16 +629,17 @@ filterBitcodeFiles(StringRef Path, std::vector<std::string> &TemporaryFiles) {
SmallString<128> S;
if (auto EC = sys::fs::createTemporaryFile("lld-" + sys::path::stem(Path),
".lib", S))
- fatal(EC, "cannot create a temporary file");
+ fatal("cannot create a temporary file: " + EC.message());
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;
}
@@ -610,16 +651,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))
- fatal(EC, "cannot create a temporary file");
+ "lld-" + sys::path::filename(Obj->ParentName), ".obj", Fd, S))
+ fatal("cannot create a temporary file: " + EC.message());
raw_fd_ostream OS(Fd, /*shouldClose*/ true);
- OS << O->MB.getBuffer();
+ OS << Obj->MB.getBuffer();
Temps.push_back(S.str());
Rsp += quote(S) + "\n";
}
@@ -635,7 +676,7 @@ void LinkerDriver::invokeMSVC(opt::InputArgList &Args) {
break;
case OPT_opt:
if (!StringRef(Arg->getValue()).startswith("lld"))
- Rsp += toString(Arg) + " ";
+ Rsp += toString(*Arg) + " ";
break;
case OPT_INPUT: {
if (Optional<StringRef> Path = doFindFile(Arg->getValue())) {
@@ -647,12 +688,12 @@ void LinkerDriver::invokeMSVC(opt::InputArgList &Args) {
break;
}
default:
- Rsp += toString(Arg) + "\n";
+ Rsp += toString(*Arg) + "\n";
}
}
- 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);
@@ -689,6 +730,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.
@@ -704,7 +746,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
StringRef S = Arg->getValue();
if (S.getAsInteger(10, N))
error(Arg->getSpelling() + " number expected, but got " + S);
- Config->ErrorLimit = N;
+ errorHandler().ErrorLimit = N;
}
// Handle /help
@@ -713,6 +755,18 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
return;
}
+ // Handle --version, which is an lld extension. This option is a bit odd
+ // because it doesn't start with "/", but we deliberately chose "--" to
+ // avoid conflict with /version and for compatibility with clang-cl.
+ if (Args.hasArg(OPT_dash_dash_version)) {
+ outs() << getLLDVersion() << "\n";
+ 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");
@@ -728,8 +782,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");
@@ -748,23 +802,26 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Handle /verbose
if (Args.hasArg(OPT_verbose))
Config->Verbose = true;
+ errorHandler().Verbose = Config->Verbose;
// Handle /force or /force:unresolved
- if (Args.hasArg(OPT_force) || Args.hasArg(OPT_force_unresolved))
+ if (Args.hasArg(OPT_force, OPT_force_unresolved))
Config->Force = true;
// Handle /debug
- if (Args.hasArg(OPT_debug)) {
+ if (Args.hasArg(OPT_debug, OPT_debug_dwarf, OPT_debug_ghash)) {
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.
- if (auto *Arg = Args.getLastArg(OPT_pdb))
- Config->PDBPath = Arg->getValue();
+ // Handle /pdb
+ bool ShouldCreatePDB = Args.hasArg(OPT_debug, OPT_debug_ghash);
+ if (ShouldCreatePDB)
+ if (auto *Arg = Args.getLastArg(OPT_pdb))
+ Config->PDBPath = Arg->getValue();
// Handle /noentry
if (Args.hasArg(OPT_noentry)) {
@@ -780,9 +837,18 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
Config->ManifestID = 2;
}
- // Handle /fixed
- if (Args.hasArg(OPT_fixed)) {
- if (Args.hasArg(OPT_dynamicbase)) {
+ // Handle /dynamicbase and /fixed. We can't use hasFlag for /dynamicbase
+ // because we need to explicitly check whether that option or its inverse was
+ // present in the argument list in order to handle /fixed.
+ auto *DynamicBaseArg = Args.getLastArg(OPT_dynamicbase, OPT_dynamicbase_no);
+ if (DynamicBaseArg &&
+ DynamicBaseArg->getOption().getID() == OPT_dynamicbase_no)
+ Config->DynamicBase = false;
+
+ bool Fixed = Args.hasFlag(OPT_fixed, OPT_fixed_no, false);
+ if (Fixed) {
+ if (DynamicBaseArg &&
+ DynamicBaseArg->getOption().getID() == OPT_dynamicbase) {
error("/fixed must not be specified with /dynamicbase");
} else {
Config->Relocatable = false;
@@ -790,8 +856,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
}
}
- if (Args.hasArg(OPT_appcontainer))
- Config->AppContainer = true;
+ // Handle /appcontainer
+ Config->AppContainer =
+ Args.hasFlag(OPT_appcontainer, OPT_appcontainer_no, false);
// Handle /machine
if (auto *Arg = Args.getLastArg(OPT_machine))
@@ -839,54 +906,65 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
if (auto *Arg = Args.getLastArg(OPT_implib))
Config->Implib = Arg->getValue();
- // Handle /opt
+ // Handle /opt.
+ bool DoGC = !Args.hasArg(OPT_debug);
+ unsigned ICFLevel = 1; // 0: off, 1: limited, 2: on
for (auto *Arg : Args.filtered(OPT_opt)) {
std::string Str = StringRef(Arg->getValue()).lower();
SmallVector<StringRef, 1> Vec;
StringRef(Str).split(Vec, ',');
for (StringRef S : Vec) {
- if (S == "noref") {
- Config->DoGC = false;
- Config->DoICF = false;
- continue;
- }
- if (S == "icf" || StringRef(S).startswith("icf=")) {
- Config->DoICF = true;
- continue;
- }
- if (S == "noicf") {
- Config->DoICF = false;
- continue;
- }
- if (StringRef(S).startswith("lldlto=")) {
- StringRef OptLevel = StringRef(S).substr(7);
+ if (S == "ref") {
+ DoGC = true;
+ } else if (S == "noref") {
+ DoGC = false;
+ } else if (S == "icf" || S.startswith("icf=")) {
+ ICFLevel = 2;
+ } else if (S == "noicf") {
+ ICFLevel = 0;
+ } else 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);
+ } else 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);
+ } else 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);
- continue;
- }
- if (S != "ref" && S != "lbr" && S != "nolbr")
+ } else if (S != "lbr" && S != "nolbr")
error("/opt: unknown option: " + S);
}
}
+ // Limited ICF is enabled if GC is enabled and ICF was never mentioned
+ // explicitly.
+ // FIXME: LLD only implements "limited" ICF, i.e. it only merges identical
+ // code. If the user passes /OPT:ICF explicitly, LLD should merge identical
+ // comdat readonly data.
+ if (ICFLevel == 1 && !DoGC)
+ ICFLevel = 0;
+ Config->DoGC = DoGC;
+ Config->DoICF = ICFLevel > 0;
+
// Handle /lldsavetemps
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());
@@ -899,6 +977,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
for (auto *Arg : Args.filtered(OPT_section))
parseSection(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)) {
@@ -932,35 +1014,42 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
}
// Handle miscellaneous boolean flags.
- if (Args.hasArg(OPT_allowisolation_no))
- Config->AllowIsolation = false;
- if (Args.hasArg(OPT_dynamicbase_no))
- Config->DynamicBase = false;
- if (Args.hasArg(OPT_nxcompat_no))
- Config->NxCompat = false;
- if (Args.hasArg(OPT_tsaware_no))
- Config->TerminalServerAware = false;
- if (Args.hasArg(OPT_nosymtab))
- Config->WriteSymtab = false;
+ Config->AllowBind = Args.hasFlag(OPT_allowbind, OPT_allowbind_no, true);
+ Config->AllowIsolation =
+ Args.hasFlag(OPT_allowisolation, OPT_allowisolation_no, true);
+ Config->NxCompat = Args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true);
+ Config->TerminalServerAware = Args.hasFlag(OPT_tsaware, OPT_tsaware_no, true);
+ Config->DebugDwarf = Args.hasArg(OPT_debug_dwarf);
+ Config->DebugGHashes = Args.hasArg(OPT_debug_ghash);
Config->MapFile = getMapFile(Args);
- if (ErrorCount)
+ 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();
@@ -976,7 +1065,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",
@@ -984,28 +1073,36 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
ArrayRef<StringRef>(SearchPaths).slice(1)));
// Handle /largeaddressaware
- if (Config->is64() || Args.hasArg(OPT_largeaddressaware))
- Config->LargeAddressAware = true;
+ Config->LargeAddressAware = Args.hasFlag(
+ OPT_largeaddressaware, OPT_largeaddressaware_no, Config->is64());
// Handle /highentropyva
- if (Config->is64() && !Args.hasArg(OPT_highentropyva_no))
- Config->HighEntropyVA = true;
+ Config->HighEntropyVA =
+ Config->is64() &&
+ Args.hasFlag(OPT_highentropyva, OPT_highentropyva_no, true);
+
+ if (!Config->DynamicBase &&
+ (Config->Machine == ARMNT || Config->Machine == ARM64))
+ error("/dynamicbase:no is not compatible with " +
+ machineToStr(Config->Machine));
// Handle /entry and /dll
if (auto *Arg = Args.getLastArg(OPT_entry)) {
Config->Entry = addUndefined(mangle(Arg->getValue()));
- } else if (Args.hasArg(OPT_dll) && !Config->NoEntry) {
- StringRef S = (Config->Machine == I386) ? "__DllMainCRTStartup@12"
- : "_DllMainCRTStartup";
- Config->Entry = addUndefined(S);
- } else if (!Config->NoEntry) {
- // Windows specific -- If entry point name is not given, we need to
- // infer that from user-defined entry name.
- StringRef S = findDefaultEntry();
- if (S.empty())
- fatal("entry point must be defined");
- Config->Entry = addUndefined(S);
- log("Entry name inferred: " + S);
+ } else if (!Config->Entry && !Config->NoEntry) {
+ if (Args.hasArg(OPT_dll)) {
+ StringRef S = (Config->Machine == I386) ? "__DllMainCRTStartup@12"
+ : "_DllMainCRTStartup";
+ Config->Entry = addUndefined(S);
+ } else {
+ // Windows specific -- If entry point name is not given, we need to
+ // infer that from user-defined entry name.
+ StringRef S = findDefaultEntry();
+ if (S.empty())
+ fatal("entry point must be defined");
+ Config->Entry = addUndefined(S);
+ log("Entry name inferred: " + S);
+ }
}
// Handle /export
@@ -1027,10 +1124,10 @@ 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(/*AsLib=*/true);
- exit(0);
+ return;
}
// Handle /delayload
@@ -1050,34 +1147,32 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
}
// Put the PDB next to the image if no /pdb flag was passed.
- if (Config->Debug && Config->PDBPath.empty()) {
+ if (ShouldCreatePDB && Config->PDBPath.empty()) {
Config->PDBPath = Config->OutputFile;
sys::path::replace_extension(Config->PDBPath, ".pdb");
}
- // Disable PDB generation if the user requested it.
- if (Args.hasArg(OPT_nopdb))
- Config->PDBPath = "";
-
// Set default image base if /base is not given.
if (Config->ImageBase == uint64_t(-1))
Config->ImageBase = getDefaultImageBase();
- Symtab.addSynthetic(mangle("__ImageBase"), nullptr);
+ Symtab->addSynthetic(mangle("__ImageBase"), nullptr);
if (Config->Machine == I386) {
- Symtab.addAbsolute("___safe_se_handler_table", 0);
- 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_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);
+ 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);
+ // Needed for MSVC 2017 15.5 CRT.
+ Symtab->addAbsolute(mangle("__enclave_config"), 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
@@ -1086,7 +1181,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) {
@@ -1094,7 +1189,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
@@ -1102,36 +1197,38 @@ 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 (auto *U = dyn_cast<Undefined>(Sym))
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());
- if (ErrorCount)
+ if (errorCount())
return;
// If /msvclto is given, we use the MSVC linker to link LTO output files.
// This is useful because MSVC link.exe can generate complete PDBs.
if (Args.hasArg(OPT_msvclto)) {
invokeMSVC(Args);
- exit(0);
+ return;
}
// 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.
@@ -1142,14 +1239,34 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
}
// Handle /safeseh.
- if (Args.hasArg(OPT_safeseh)) {
- for (ObjectFile *File : Symtab.ObjectFiles)
+ if (Args.hasFlag(OPT_safeseh, OPT_safeseh_no, false)) {
+ for (ObjFile *File : ObjFile::Instances)
if (!File->SEHCompat)
error("/safeseh: " + File->getName() + " is not compatible with SEH");
- if (ErrorCount)
+ 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);
+ if (!Exporter.shouldExport(Def))
+ return;
+ Export E;
+ E.Name = Def->getName();
+ E.Sym = Def;
+ if (Def->getChunk() &&
+ !(Def->getChunk()->getPermissions() & IMAGE_SCN_MEM_EXECUTE))
+ E.Data = true;
+ 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) {
@@ -1158,23 +1275,45 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
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);
+ 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);
-
- // Call exit to avoid calling destructors.
- exit(0);
+ writeResult();
}
} // namespace coff
diff --git a/COFF/Driver.h b/COFF/Driver.h
index 6879be2eb0c7..63d41cf69093 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"
@@ -36,31 +36,35 @@ using llvm::COFF::WindowsSubsystem;
using llvm::Optional;
// Implemented in MarkLive.cpp.
-void markLive(const std::vector<Chunk *> &Chunks);
+void markLive(ArrayRef<Chunk *> Chunks);
// Implemented in ICF.cpp.
-void doICF(const std::vector<Chunk *> &Chunks);
+void doICF(ArrayRef<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.
@@ -70,10 +74,9 @@ public:
void enqueueArchiveMember(const Archive::Child &C, StringRef SymName,
StringRef ParentName);
-private:
- ArgParser Parser;
- SymbolTable Symtab;
+ MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> MB);
+private:
std::unique_ptr<llvm::TarWriter> Tar; // for /linkrepro
// Opens a file. Path has to be resolved already.
@@ -93,7 +96,7 @@ private:
std::set<std::string> VisitedFiles;
std::set<std::string> VisitedLibs;
- SymbolBody *addUndefined(StringRef Sym);
+ Symbol *addUndefined(StringRef Sym);
StringRef mangle(StringRef Sym);
// Windows specific -- "main" is not the only main function in Windows.
@@ -108,12 +111,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();
@@ -145,6 +147,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);
@@ -167,10 +170,8 @@ 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(ArrayRef<MemoryBufferRef> MBs);
void runMSVCLinker(std::string Rsp, ArrayRef<StringRef> Objects);
diff --git a/COFF/DriverUtils.cpp b/COFF/DriverUtils.cpp
index 39d582469640..07783b51c519 100644
--- a/COFF/DriverUtils.cpp
+++ b/COFF/DriverUtils.cpp
@@ -15,9 +15,9 @@
#include "Config.h"
#include "Driver.h"
-#include "Error.h"
-#include "Memory.h"
#include "Symbols.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/BinaryFormat/COFF.h"
@@ -32,12 +32,11 @@
#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 {
@@ -58,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("unable to find " + Prog + " in PATH: " + EC.message());
StringRef Exe = Saver.save(*ExeOrErr);
Args.insert(Args.begin(), Exe);
@@ -221,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) {
@@ -273,14 +288,14 @@ public:
TemporaryFile(StringRef Prefix, StringRef Extn, StringRef Contents = "") {
SmallString<128> S;
if (auto EC = sys::fs::createTemporaryFile("lld-" + Prefix, Extn, S))
- fatal(EC, "cannot create a temporary file");
+ fatal("cannot create a temporary file: " + EC.message());
Path = S.str();
if (!Contents.empty()) {
std::error_code EC;
raw_fd_ostream OS(Path, EC, sys::fs::F_None);
if (EC)
- fatal(EC, "failed to open " + Path);
+ fatal("failed to open " + Path + ": " + EC.message());
OS << Contents;
}
}
@@ -302,7 +317,7 @@ public:
// is called (you cannot remove an opened file on Windows.)
std::unique_ptr<MemoryBuffer> getMemoryBuffer() {
// IsVolatileSize=true forces MemoryBuffer to not use mmap().
- return check(MemoryBuffer::getFile(Path, /*FileSize=*/-1,
+ return CHECK(MemoryBuffer::getFile(Path, /*FileSize=*/-1,
/*RequiresNullTerminator=*/false,
/*IsVolatileSize=*/true),
"could not open " + Path);
@@ -312,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.
@@ -337,46 +345,77 @@ 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("internal manifest tool failed on default xml: " +
+ toString(std::move(E)));
+
+ for (StringRef Filename : Config->ManifestInput) {
+ std::unique_ptr<MemoryBuffer> Manifest =
+ check(MemoryBuffer::getFile(Filename));
+ if (auto E = Merger.merge(*Manifest.get()))
+ fatal("internal manifest tool failed on file " + Filename + ": " +
+ toString(std::move(E)));
+ }
+
+ 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("failed to open " + Default.Path + ": " + EC.message());
+ 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>
@@ -386,7 +425,8 @@ createMemoryBufferForManifestRes(size_t ManifestSize) {
sizeof(object::WinResHeaderPrefix) + sizeof(object::WinResIDs) +
sizeof(object::WinResHeaderSuffix) + ManifestSize,
object::WIN_RES_DATA_ALIGNMENT);
- return MemoryBuffer::getNewMemBuffer(ResSize);
+ return MemoryBuffer::getNewMemBuffer(ResSize,
+ Config->OutputFile + ".manifest.res");
}
static void writeResFileHeader(char *&Buf) {
@@ -444,7 +484,7 @@ void createSideBySideManifest() {
std::error_code EC;
raw_fd_ostream Out(Path, EC, sys::fs::F_Text);
if (EC)
- fatal(EC, "failed to create manifest");
+ fatal("failed to create manifest: " + EC.message());
Out << createManifestXml();
}
@@ -459,12 +499,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;
@@ -534,7 +574,7 @@ void fixupExports() {
}
for (Export &E : Config->Exports) {
- SymbolBody *Sym = E.Sym;
+ Symbol *Sym = E.Sym;
if (!E.ForwardTo.empty() || !Sym) {
E.SymbolName = E.Name;
} else {
@@ -554,7 +594,7 @@ void fixupExports() {
}
// Uniquefy by name.
- std::map<StringRef, Export *> Map;
+ DenseMap<StringRef, Export *> Map(Config->Exports.size());
std::vector<Export> V;
for (Export &E : Config->Exports) {
auto Pair = Map.insert(std::make_pair(E.ExportName, &E));
@@ -601,10 +641,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(ArrayRef<MemoryBufferRef> MBs) {
object::WindowsResourceParser Parser;
for (MemoryBufferRef MB : MBs) {
@@ -613,14 +651,17 @@ convertResToCOFF(const std::vector<MemoryBufferRef> &MBs) {
if (!RF)
fatal("cannot compile non-resource file as resource");
if (auto EC = Parser.parse(RF))
- fatal(EC, "failed to parse .res file");
+ fatal("failed to parse .res file: " + toString(std::move(EC)));
}
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 std::move(E.get());
+ fatal("failed to write .res to COFF: " + toString(E.takeError()));
+
+ 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.
@@ -651,7 +692,7 @@ 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[] = {
+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},
@@ -659,30 +700,49 @@ static const llvm::opt::OptTable::Info infoTable[] = {
#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);
}
+ // Handle /WX early since it converts missing argument warnings to errors.
+ errorHandler().FatalWarnings = Args.hasFlag(OPT_WX, OPT_WX_no, false);
+
if (MissingCount)
fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument");
for (auto *Arg : Args.filtered(OPT_UNKNOWN))
@@ -693,17 +753,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) {
@@ -712,18 +772,8 @@ 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);
+ COFFOptTable().PrintHelp(outs(), Argv0, "LLVM Linker", false);
}
} // namespace coff
diff --git a/COFF/Error.cpp b/COFF/Error.cpp
deleted file mode 100644
index 34abc280f6bf..000000000000
--- a/COFF/Error.cpp
+++ /dev/null
@@ -1,114 +0,0 @@
-//===- Error.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 "Config.h"
-
-#include "llvm/ADT/Twine.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Support/ManagedStatic.h"
-#include "llvm/Support/Process.h"
-#include "llvm/Support/raw_ostream.h"
-#include <mutex>
-
-#if !defined(_MSC_VER) && !defined(__MINGW32__)
-#include <unistd.h>
-#endif
-
-using namespace llvm;
-
-namespace lld {
-// The functions defined in this file can be called from multiple threads,
-// but outs() or errs() are not thread-safe. We protect them using a mutex.
-static std::mutex Mu;
-
-namespace coff {
-uint64_t ErrorCount;
-raw_ostream *ErrorOS;
-
-static LLVM_ATTRIBUTE_NORETURN void exitLld(int Val) {
- // Dealloc/destroy ManagedStatic variables before calling
- // _exit(). In a non-LTO build, this is a nop. In an LTO
- // build allows us to get the output of -time-passes.
- llvm_shutdown();
-
- outs().flush();
- errs().flush();
- _exit(Val);
-}
-
-static void print(StringRef S, raw_ostream::Colors C) {
- *ErrorOS << Config->Argv[0] << ": ";
- if (Config->ColorDiagnostics) {
- ErrorOS->changeColor(C, true);
- *ErrorOS << S;
- ErrorOS->resetColor();
- } else {
- *ErrorOS << S;
- }
-}
-
-void log(const Twine &Msg) {
- if (Config->Verbose) {
- std::lock_guard<std::mutex> Lock(Mu);
- outs() << Config->Argv[0] << ": " << Msg << "\n";
- outs().flush();
- }
-}
-
-void message(const Twine &Msg) {
- std::lock_guard<std::mutex> Lock(Mu);
- outs() << Msg << "\n";
- outs().flush();
-}
-
-void error(const Twine &Msg) {
- std::lock_guard<std::mutex> Lock(Mu);
-
- if (Config->ErrorLimit == 0 || ErrorCount < Config->ErrorLimit) {
- print("error: ", raw_ostream::RED);
- *ErrorOS << Msg << "\n";
- } else if (ErrorCount == Config->ErrorLimit) {
- print("error: ", raw_ostream::RED);
- *ErrorOS << "too many errors emitted, stopping now"
- << " (use /ERRORLIMIT:0 to see all errors)\n";
- exitLld(1);
- }
-
- ++ErrorCount;
-}
-
-void fatal(const Twine &Msg) {
- if (Config->ColorDiagnostics) {
- errs().changeColor(raw_ostream::RED, /*bold=*/true);
- errs() << "error: ";
- errs().resetColor();
- } else {
- errs() << "error: ";
- }
- errs() << Msg << "\n";
- exitLld(1);
-}
-
-void fatal(std::error_code EC, const Twine &Msg) {
- fatal(Msg + ": " + EC.message());
-}
-
-void fatal(llvm::Error &Err, const Twine &Msg) {
- fatal(errorToErrorCode(std::move(Err)), Msg);
-}
-
-void warn(const Twine &Msg) {
- std::lock_guard<std::mutex> Lock(Mu);
- print("warning: ", raw_ostream::MAGENTA);
- *ErrorOS << Msg << "\n";
-}
-
-} // namespace coff
-} // namespace lld
diff --git a/COFF/Error.h b/COFF/Error.h
deleted file mode 100644
index e1e4c1e5216f..000000000000
--- a/COFF/Error.h
+++ /dev/null
@@ -1,62 +0,0 @@
-//===- Error.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_ERROR_H
-#define LLD_COFF_ERROR_H
-
-#include "lld/Core/LLVM.h"
-#include "llvm/Support/Error.h"
-
-namespace lld {
-namespace coff {
-
-extern uint64_t ErrorCount;
-extern llvm::raw_ostream *ErrorOS;
-
-void log(const Twine &Msg);
-void message(const Twine &Msg);
-void warn(const Twine &Msg);
-void error(const Twine &Msg);
-LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg);
-LLVM_ATTRIBUTE_NORETURN void fatal(std::error_code EC, const Twine &Prefix);
-LLVM_ATTRIBUTE_NORETURN void fatal(llvm::Error &Err, const Twine &Prefix);
-
-template <class T> T check(ErrorOr<T> V, const Twine &Prefix) {
- if (auto EC = V.getError())
- fatal(EC, Prefix);
- return std::move(*V);
-}
-
-template <class T> T check(Expected<T> E, const Twine &Prefix) {
- if (llvm::Error Err = E.takeError())
- fatal(Err, Prefix);
- return std::move(*E);
-}
-
-template <class T> T check(ErrorOr<T> EO) {
- if (!EO)
- fatal(EO.getError().message());
- return std::move(*EO);
-}
-
-template <class T> T check(Expected<T> E) {
- if (!E) {
- std::string Buf;
- llvm::raw_string_ostream OS(Buf);
- logAllUnhandledErrors(E.takeError(), OS, "");
- OS.flush();
- fatal(Buf);
- }
- return std::move(*E);
-}
-
-} // namespace coff
-} // namespace lld
-
-#endif
diff --git a/COFF/ICF.cpp b/COFF/ICF.cpp
index da8ca360542a..48895c34886c 100644
--- a/COFF/ICF.cpp
+++ b/COFF/ICF.cpp
@@ -19,8 +19,8 @@
//===----------------------------------------------------------------------===//
#include "Chunks.h"
-#include "Error.h"
#include "Symbols.h"
+#include "lld/Common/ErrorHandler.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Parallel.h"
@@ -36,7 +36,7 @@ namespace coff {
class ICF {
public:
- void run(const std::vector<Chunk *> &V);
+ void run(ArrayRef<Chunk *> V);
private:
void segregate(size_t Begin, size_t End, bool Constant);
@@ -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.
@@ -76,12 +73,21 @@ uint32_t ICF::getHash(SectionChunk *C) {
// 2017) says that /opt:icf folds both functions and read-only data.
// Despite that, the MSVC linker folds only functions. We found
// a few instances of programs that are not safe for data merging.
-// Therefore, we merge only functions just like the MSVC tool.
+// Therefore, we merge only functions just like the MSVC tool. However, we merge
+// identical .xdata sections, because the address of unwind information is
+// insignificant to the user program and the Visual C++ linker does this.
bool ICF::isEligible(SectionChunk *C) {
- bool Global = C->Sym && C->Sym->isExternal();
- bool Executable = C->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE;
+ // Non-comdat chunks, dead chunks, and writable chunks are not elegible.
bool Writable = C->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
- return C->isCOMDAT() && C->isLive() && Global && Executable && !Writable;
+ if (!C->isCOMDAT() || !C->isLive() || Writable)
+ return false;
+
+ // Code sections are eligible.
+ if (C->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE)
+ return true;
+
+ // .xdata unwind info sections are eligble.
+ return C->getSectionName().split('$').first == ".xdata";
}
// Split an equivalence class into smaller classes.
@@ -122,8 +128,8 @@ bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) {
R1.VirtualAddress != R2.VirtualAddress) {
return false;
}
- SymbolBody *B1 = A->File->getSymbolBody(R1.SymbolTableIndex);
- SymbolBody *B2 = B->File->getSymbolBody(R2.SymbolTableIndex);
+ Symbol *B1 = A->File->getSymbol(R1.SymbolTableIndex);
+ Symbol *B2 = B->File->getSymbol(R2.SymbolTableIndex);
if (B1 == B2)
return true;
if (auto *D1 = dyn_cast<DefinedRegular>(B1))
@@ -137,19 +143,17 @@ 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.
bool ICF::equalsVariable(const SectionChunk *A, const SectionChunk *B) {
// Compare relocations.
auto Eq = [&](const coff_relocation &R1, const coff_relocation &R2) {
- SymbolBody *B1 = A->File->getSymbolBody(R1.SymbolTableIndex);
- SymbolBody *B2 = B->File->getSymbolBody(R2.SymbolTableIndex);
+ Symbol *B1 = A->File->getSymbol(R1.SymbolTableIndex);
+ Symbol *B2 = B->File->getSymbol(R2.SymbolTableIndex);
if (B1 == B2)
return true;
if (auto *D1 = dyn_cast<DefinedRegular>(B1))
@@ -202,7 +206,7 @@ void ICF::forEachClass(std::function<void(size_t, size_t)> Fn) {
// Merge identical COMDAT sections.
// Two sections are considered the same if their section headers,
// contents and relocations are all the same.
-void ICF::run(const std::vector<Chunk *> &Vec) {
+void ICF::run(ArrayRef<Chunk *> Vec) {
// Collect only mergeable sections and group by hash value.
uint32_t NextId = 1;
for (Chunk *C : Vec) {
@@ -215,9 +219,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.
@@ -252,7 +257,7 @@ void ICF::run(const std::vector<Chunk *> &Vec) {
}
// Entry point to ICF.
-void doICF(const std::vector<Chunk *> &Chunks) { ICF().run(Chunks); }
+void doICF(ArrayRef<Chunk *> Chunks) { ICF().run(Chunks); }
} // namespace coff
} // namespace lld
diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp
index 7d41caebb4b9..a8f52e0391f7 100644
--- a/COFF/InputFiles.cpp
+++ b/COFF/InputFiles.cpp
@@ -11,10 +11,10 @@
#include "Chunks.h"
#include "Config.h"
#include "Driver.h"
-#include "Error.h"
-#include "Memory.h"
#include "SymbolTable.h"
#include "Symbols.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
#include "llvm-c/lto.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Triple.h"
@@ -43,14 +43,18 @@ 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.
static void checkAndSetWeakAlias(SymbolTable *Symtab, InputFile *F,
- SymbolBody *Source, SymbolBody *Target) {
+ Symbol *Source, Symbol *Target) {
if (auto *U = dyn_cast<Undefined>(Source)) {
if (U->WeakAlias && U->WeakAlias != Target)
- Symtab->reportDuplicate(Source->symbol(), F);
+ Symtab->reportDuplicate(Source, F);
U->WeakAlias = Target;
}
}
@@ -59,7 +63,7 @@ ArchiveFile::ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {}
void ArchiveFile::parse() {
// Parse a MemoryBufferRef as an archive file.
- File = check(Archive::create(MB), toString(this));
+ File = CHECK(Archive::create(MB), this);
// Read the symbol table to construct Lazy objects.
for (const Archive::Symbol &Sym : File->symbols())
@@ -69,7 +73,7 @@ void ArchiveFile::parse() {
// Returns a buffer pointing to a member file containing a given symbol.
void ArchiveFile::addMember(const Archive::Symbol *Sym) {
const Archive::Child &C =
- check(Sym->getMember(),
+ CHECK(Sym->getMember(),
"could not get the member for symbol " + Sym->getName());
// Return an empty buffer if we have already returned the same buffer.
@@ -79,9 +83,28 @@ 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));
+ std::unique_ptr<Binary> Bin = CHECK(createBinary(MB), this);
if (auto *Obj = dyn_cast<COFFObjectFile>(Bin.get())) {
Bin.release();
@@ -93,114 +116,184 @@ void ObjectFile::parse() {
// Read section and symbol tables.
initializeChunks();
initializeSymbols();
- initializeSEH();
}
-void ObjectFile::initializeChunks() {
+// We set SectionChunk pointers in the SparseChunks vector to this value
+// temporarily to mark comdat sections as having an unknown resolution. As we
+// walk the object file's symbol table, once we visit either a leader symbol or
+// an associative section definition together with the parent comdat's leader,
+// we set the pointer to either nullptr (to mark the section as discarded) or a
+// valid SectionChunk for that section.
+static SectionChunk *const PendingComdat = reinterpret_cast<SectionChunk *>(1);
+
+void ObjFile::initializeChunks() {
uint32_t NumSections = COFFObj->getNumberOfSections();
Chunks.reserve(NumSections);
SparseChunks.resize(NumSections + 1);
for (uint32_t I = 1; I < NumSections + 1; ++I) {
const coff_section *Sec;
- StringRef Name;
if (auto EC = COFFObj->getSection(I, Sec))
- fatal(EC, "getSection failed: #" + Twine(I));
- if (auto EC = COFFObj->getSectionName(Sec, Name))
- fatal(EC, "getSectionName failed: #" + Twine(I));
- if (Name == ".sxdata") {
- SXData = Sec;
- continue;
- }
- if (Name == ".drectve") {
- ArrayRef<uint8_t> Data;
- COFFObj->getSectionContents(Sec, Data);
- Directives = std::string((const char *)Data.data(), Data.size());
- continue;
- }
+ fatal("getSection failed: #" + Twine(I) + ": " + EC.message());
- // Object files may have DWARF debug info or MS CodeView debug info
- // (or both).
- //
- // DWARF sections don't need any special handling from the perspective
- // of the linker; they are just a data section containing relocations.
- // We can just link them to complete debug info.
- //
- // CodeView needs a linker support. We need to interpret and debug
- // info, and then write it to a separate .pdb file.
-
- // Ignore debug info unless /debug is given.
- if (!Config->Debug && Name.startswith(".debug"))
- continue;
-
- if (Sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
- continue;
- auto *C = make<SectionChunk>(this, Sec);
-
- // CodeView sections are stored to a different vector because they are not
- // linked in the regular manner.
- if (C->isCodeView())
- DebugChunks.push_back(C);
+ if (Sec->Characteristics & IMAGE_SCN_LNK_COMDAT)
+ SparseChunks[I] = PendingComdat;
else
- Chunks.push_back(C);
+ SparseChunks[I] = readSection(I, nullptr);
+ }
+}
- SparseChunks[I] = C;
+SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
+ const coff_aux_section_definition *Def) {
+ const coff_section *Sec;
+ StringRef Name;
+ if (auto EC = COFFObj->getSection(SectionNumber, Sec))
+ fatal("getSection failed: #" + Twine(SectionNumber) + ": " + EC.message());
+ if (auto EC = COFFObj->getSectionName(Sec, Name))
+ fatal("getSectionName failed: #" + Twine(SectionNumber) + ": " +
+ EC.message());
+ if (Name == ".sxdata") {
+ ArrayRef<uint8_t> Data;
+ COFFObj->getSectionContents(Sec, Data);
+ if (Data.size() % 4 != 0)
+ fatal(".sxdata must be an array of symbol table indices");
+ SXData = {reinterpret_cast<const ulittle32_t *>(Data.data()),
+ Data.size() / 4};
+ return nullptr;
+ }
+ if (Name == ".drectve") {
+ ArrayRef<uint8_t> Data;
+ COFFObj->getSectionContents(Sec, Data);
+ Directives = std::string((const char *)Data.data(), Data.size());
+ return nullptr;
}
+
+ // Object files may have DWARF debug info or MS CodeView debug info
+ // (or both).
+ //
+ // DWARF sections don't need any special handling from the perspective
+ // of the linker; they are just a data section containing relocations.
+ // We can just link them to complete debug info.
+ //
+ // CodeView needs a linker support. We need to interpret and debug
+ // info, and then write it to a separate .pdb file.
+
+ // Ignore debug info unless /debug is given.
+ if (!Config->Debug && Name.startswith(".debug"))
+ return nullptr;
+
+ if (Sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
+ return nullptr;
+ auto *C = make<SectionChunk>(this, Sec);
+ if (Def)
+ C->Checksum = Def->CheckSum;
+
+ // 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);
+
+ return C;
}
-void ObjectFile::initializeSymbols() {
+void ObjFile::readAssociativeDefinition(
+ COFFSymbolRef Sym, const coff_aux_section_definition *Def) {
+ SectionChunk *Parent = SparseChunks[Def->getNumber(Sym.isBigObj())];
+
+ // If the parent is pending, it probably means that its section definition
+ // appears after us in the symbol table. Leave the associated section as
+ // pending; we will handle it during the second pass in initializeSymbols().
+ if (Parent == PendingComdat)
+ return;
+
+ // Check whether the parent is prevailing. If it is, so are we, and we read
+ // the section; otherwise mark it as discarded.
+ int32_t SectionNumber = Sym.getSectionNumber();
+ if (Parent) {
+ SparseChunks[SectionNumber] = readSection(SectionNumber, Def);
+ if (SparseChunks[SectionNumber])
+ Parent->addAssociative(SparseChunks[SectionNumber]);
+ } else {
+ SparseChunks[SectionNumber] = nullptr;
+ }
+}
+
+Symbol *ObjFile::createRegular(COFFSymbolRef Sym) {
+ SectionChunk *SC = SparseChunks[Sym.getSectionNumber()];
+ if (Sym.isExternal()) {
+ StringRef Name;
+ COFFObj->getSymbolName(Sym, Name);
+ if (SC)
+ return Symtab->addRegular(this, Name, Sym.getGeneric(), SC);
+ return Symtab->addUndefined(Name, this, false);
+ }
+ if (SC)
+ return make<DefinedRegular>(this, /*Name*/ "", false,
+ /*IsExternal*/ false, Sym.getGeneric(), SC);
+ return nullptr;
+}
+
+void ObjFile::initializeSymbols() {
uint32_t NumSymbols = COFFObj->getNumberOfSymbols();
- SymbolBodies.reserve(NumSymbols);
- SparseSymbolBodies.resize(NumSymbols);
+ Symbols.resize(NumSymbols);
- SmallVector<std::pair<SymbolBody *, uint32_t>, 8> WeakAliases;
- int32_t LastSectionNumber = 0;
+ SmallVector<std::pair<Symbol *, uint32_t>, 8> WeakAliases;
+ std::vector<uint32_t> PendingIndexes;
+ PendingIndexes.reserve(NumSymbols);
+
+ std::vector<const coff_aux_section_definition *> ComdatDefs(
+ COFFObj->getNumberOfSections() + 1);
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;
-
- const void *AuxP = nullptr;
- if (Sym.getNumberOfAuxSymbols())
- AuxP = COFFObj->getSymbol(I + 1)->getRawPtr();
- bool IsFirst = (LastSectionNumber != Sym.getSectionNumber());
-
- SymbolBody *Body = nullptr;
- if (Sym.isUndefined()) {
- Body = createUndefined(Sym);
- } else if (Sym.isWeakExternal()) {
- Body = createUndefined(Sym);
- uint32_t TagIndex =
- static_cast<const coff_aux_weak_external *>(AuxP)->TagIndex;
- WeakAliases.emplace_back(Body, TagIndex);
+ COFFSymbolRef COFFSym = check(COFFObj->getSymbol(I));
+ if (COFFSym.isUndefined()) {
+ Symbols[I] = createUndefined(COFFSym);
+ } else if (COFFSym.isWeakExternal()) {
+ Symbols[I] = createUndefined(COFFSym);
+ uint32_t TagIndex = COFFSym.getAux<coff_aux_weak_external>()->TagIndex;
+ WeakAliases.emplace_back(Symbols[I], TagIndex);
+ } else if (Optional<Symbol *> OptSym = createDefined(COFFSym, ComdatDefs)) {
+ Symbols[I] = *OptSym;
} else {
- Body = createDefined(Sym, AuxP, IsFirst);
- }
- if (Body) {
- SymbolBodies.push_back(Body);
- SparseSymbolBodies[I] = Body;
+ // createDefined() returns None if a symbol belongs to a section that
+ // was pending at the point when the symbol was read. This can happen in
+ // two cases:
+ // 1) section definition symbol for a comdat leader;
+ // 2) symbol belongs to a comdat section associated with a section whose
+ // section definition symbol appears later in the symbol table.
+ // In both of these cases, we can expect the section to be resolved by
+ // the time we finish visiting the remaining symbols in the symbol
+ // table. So we postpone the handling of this symbol until that time.
+ PendingIndexes.push_back(I);
}
- I += Sym.getNumberOfAuxSymbols();
- LastSectionNumber = Sym.getSectionNumber();
+ I += COFFSym.getNumberOfAuxSymbols();
+ }
+
+ for (uint32_t I : PendingIndexes) {
+ COFFSymbolRef Sym = check(COFFObj->getSymbol(I));
+ if (auto *Def = Sym.getSectionDefinition())
+ if (Def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
+ readAssociativeDefinition(Sym, Def);
+ Symbols[I] = createRegular(Sym);
}
for (auto &KV : WeakAliases) {
- SymbolBody *Sym = KV.first;
+ Symbol *Sym = KV.first;
uint32_t Idx = KV.second;
- checkAndSetWeakAlias(Symtab, this, Sym, SparseSymbolBodies[Idx]);
+ checkAndSetWeakAlias(Symtab, this, Sym, Symbols[Idx]);
}
}
-SymbolBody *ObjectFile::createUndefined(COFFSymbolRef Sym) {
+Symbol *ObjFile::createUndefined(COFFSymbolRef Sym) {
StringRef Name;
COFFObj->getSymbolName(Sym, Name);
- return Symtab->addUndefined(Name, this, Sym.isWeakExternal())->body();
+ return Symtab->addUndefined(Name, this, Sym.isWeakExternal());
}
-SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
- bool IsFirst) {
+Optional<Symbol *> ObjFile::createDefined(
+ COFFSymbolRef Sym,
+ std::vector<const coff_aux_section_definition *> &ComdatDefs) {
StringRef Name;
if (Sym.isCommon()) {
auto *C = make<CommonChunk>(Sym);
@@ -208,7 +301,7 @@ SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
COFFObj->getSymbolName(Sym, Name);
Symbol *S =
Symtab->addCommon(this, Name, Sym.getValue(), Sym.getGeneric(), C);
- return S->body();
+ return S;
}
if (Sym.isAbsolute()) {
COFFObj->getSymbolName(Sym, Name);
@@ -222,7 +315,7 @@ SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
return nullptr;
}
if (Sym.isExternal())
- return Symtab->addAbsolute(Name, Sym)->body();
+ return Symtab->addAbsolute(Name, Sym);
else
return make<DefinedAbsolute>(Name, Sym);
}
@@ -239,54 +332,49 @@ SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
if ((uint32_t)SectionNumber >= SparseChunks.size())
fatal("broken object file: " + toString(this));
- // Nothing else to do without a section chunk.
- auto *SC = cast_or_null<SectionChunk>(SparseChunks[SectionNumber]);
- if (!SC)
- return nullptr;
-
- // Handle section definitions
- if (IsFirst && AuxP) {
- auto *Aux = reinterpret_cast<const coff_aux_section_definition *>(AuxP);
- if (Aux->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
- if (auto *ParentSC = cast_or_null<SectionChunk>(
- SparseChunks[Aux->getNumber(Sym.isBigObj())])) {
- ParentSC->addAssociative(SC);
- // If we already discarded the parent, discard the child.
- if (ParentSC->isDiscarded())
- SC->markDiscarded();
- }
- SC->Checksum = Aux->CheckSum;
+ // Handle comdat leader symbols.
+ if (const coff_aux_section_definition *Def = ComdatDefs[SectionNumber]) {
+ ComdatDefs[SectionNumber] = nullptr;
+ Symbol *Leader;
+ bool Prevailing;
+ if (Sym.isExternal()) {
+ COFFObj->getSymbolName(Sym, Name);
+ std::tie(Leader, Prevailing) =
+ Symtab->addComdat(this, Name, Sym.getGeneric());
+ } else {
+ Leader = make<DefinedRegular>(this, /*Name*/ "", false,
+ /*IsExternal*/ false, Sym.getGeneric());
+ Prevailing = true;
+ }
+ if (Prevailing) {
+ SectionChunk *C = readSection(SectionNumber, Def);
+ SparseChunks[SectionNumber] = C;
+ C->Sym = cast<DefinedRegular>(Leader);
+ cast<DefinedRegular>(Leader)->Data = &C->Repl;
+ } else {
+ SparseChunks[SectionNumber] = nullptr;
+ }
+ return Leader;
}
- DefinedRegular *B;
- if (Sym.isExternal()) {
- COFFObj->getSymbolName(Sym, Name);
- Symbol *S =
- Symtab->addRegular(this, Name, SC->isCOMDAT(), Sym.getGeneric(), SC);
- B = cast<DefinedRegular>(S->body());
- } else
- B = make<DefinedRegular>(this, /*Name*/ "", SC->isCOMDAT(),
- /*IsExternal*/ false, Sym.getGeneric(), SC);
- if (SC->isCOMDAT() && Sym.getValue() == 0 && !AuxP)
- SC->setSymbol(B);
-
- return B;
-}
+ // Read associative section definitions and prepare to handle the comdat
+ // leader symbol by setting the section's ComdatDefs pointer if we encounter a
+ // non-associative comdat.
+ if (SparseChunks[SectionNumber] == PendingComdat) {
+ if (auto *Def = Sym.getSectionDefinition()) {
+ if (Def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
+ readAssociativeDefinition(Sym, Def);
+ else
+ ComdatDefs[SectionNumber] = Def;
+ }
+ }
-void ObjectFile::initializeSEH() {
- if (!SEHCompat || !SXData)
- return;
- ArrayRef<uint8_t> A;
- COFFObj->getSectionContents(SXData, A);
- if (A.size() % 4 != 0)
- fatal(".sxdata must be an array of symbol table indices");
- auto *I = reinterpret_cast<const ulittle32_t *>(A.data());
- auto *E = reinterpret_cast<const ulittle32_t *>(A.data() + A.size());
- for (; I != E; ++I)
- SEHandlers.insert(SparseSymbolBodies[*I]);
+ if (SparseChunks[SectionNumber] == PendingComdat)
+ return None;
+ return createRegular(Sym);
}
-MachineTypes ObjectFile::getMachineType() {
+MachineTypes ObjFile::getMachineType() {
if (COFFObj)
return static_cast<MachineTypes>(COFFObj->getMachine());
return IMAGE_FILE_MACHINE_UNKNOWN;
@@ -332,26 +420,27 @@ 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() {
Obj = check(lto::InputFile::create(MemoryBufferRef(
MB.getBuffer(), Saver.save(ParentName + MB.getBufferIdentifier()))));
+ std::vector<std::pair<Symbol *, bool>> Comdat(Obj->getComdatTable().size());
+ for (size_t I = 0; I != Obj->getComdatTable().size(); ++I)
+ Comdat[I] = Symtab->addComdat(this, Saver.save(Obj->getComdatTable()[I]));
for (const lto::InputFile::Symbol &ObjSym : Obj->symbols()) {
StringRef SymName = Saver.save(ObjSym.getName());
+ int ComdatIndex = ObjSym.getComdatIndex();
Symbol *Sym;
if (ObjSym.isUndefined()) {
Sym = Symtab->addUndefined(SymName, this, false);
@@ -361,13 +450,19 @@ void BitcodeFile::parse() {
// Weak external.
Sym = Symtab->addUndefined(SymName, this, true);
std::string Fallback = ObjSym.getCOFFWeakExternalFallback();
- SymbolBody *Alias = Symtab->addUndefined(Saver.save(Fallback));
- checkAndSetWeakAlias(Symtab, this, Sym->body(), Alias);
+ Symbol *Alias = Symtab->addUndefined(Saver.save(Fallback));
+ checkAndSetWeakAlias(Symtab, this, Sym, Alias);
+ } else if (ComdatIndex != -1) {
+ if (SymName == Obj->getComdatTable()[ComdatIndex])
+ Sym = Comdat[ComdatIndex].first;
+ else if (Comdat[ComdatIndex].second)
+ Sym = Symtab->addRegular(this, SymName);
+ else
+ Sym = Symtab->addUndefined(SymName, this, false);
} else {
- bool IsCOMDAT = ObjSym.getComdatIndex() != -1;
- Sym = Symtab->addRegular(this, SymName, IsCOMDAT);
+ Sym = Symtab->addRegular(this, SymName);
}
- SymbolBodies.push_back(Sym->body());
+ SymbolBodies.push_back(Sym);
}
Directives = Obj->getCOFFLinkerOpts();
}
@@ -398,14 +493,13 @@ static StringRef getBasename(StringRef Path) {
}
// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)".
-std::string lld::toString(coff::InputFile *File) {
+std::string lld::toString(const coff::InputFile *File) {
if (!File)
- return "(internal)";
+ return "<internal>";
if (File->ParentName.empty())
- return File->getName().lower();
+ return File->getName();
- std::string Res =
- (getBasename(File->ParentName) + "(" + getBasename(File->getName()) + ")")
- .str();
- return StringRef(Res).lower();
+ return (getBasename(File->ParentName) + "(" + getBasename(File->getName()) +
+ ")")
+ .str();
}
diff --git a/COFF/InputFiles.h b/COFF/InputFiles.h
index 99868d9992c6..adedbc2ad7a8 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;
@@ -45,8 +47,7 @@ class DefinedImportData;
class DefinedImportThunk;
class Lazy;
class SectionChunk;
-struct Symbol;
-class SymbolBody;
+class Symbol;
class Undefined;
// The root class of input files.
@@ -57,7 +58,7 @@ public:
virtual ~InputFile() {}
// Returns the filename.
- StringRef getName() { return MB.getBufferIdentifier(); }
+ StringRef getName() const { return MB.getBufferIdentifier(); }
// Reads a file (the constructor doesn't do that).
virtual void parse() = 0;
@@ -101,32 +102,34 @@ 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;
- std::vector<Chunk *> &getChunks() { return Chunks; }
- std::vector<SectionChunk *> &getDebugChunks() { return DebugChunks; }
- std::vector<SymbolBody *> &getSymbols() { return SymbolBodies; }
+ ArrayRef<Chunk *> getChunks() { return Chunks; }
+ ArrayRef<SectionChunk *> getDebugChunks() { return DebugChunks; }
+ ArrayRef<Symbol *> getSymbols() { return Symbols; }
- // Returns a SymbolBody object for the SymbolIndex'th symbol in the
+ // Returns a Symbol object for the SymbolIndex'th symbol in the
// underlying object file.
- SymbolBody *getSymbolBody(uint32_t SymbolIndex) {
- return SparseSymbolBodies[SymbolIndex];
+ Symbol *getSymbol(uint32_t SymbolIndex) {
+ return Symbols[SymbolIndex];
}
// 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;
- // The list of safe exception handlers listed in .sxdata section.
+ // The symbol table indexes of the safe exception handlers.
// COFF-specific and x86-only.
- std::set<SymbolBody *> SEHandlers;
+ ArrayRef<llvm::support::ulittle32_t> SXData;
// Pointer to the PDB module descriptor builder. Various debug info records
// will reference object files by "module index", which is here. Things like
@@ -137,13 +140,23 @@ public:
private:
void initializeChunks();
void initializeSymbols();
- void initializeSEH();
- SymbolBody *createDefined(COFFSymbolRef Sym, const void *Aux, bool IsFirst);
- SymbolBody *createUndefined(COFFSymbolRef Sym);
+ SectionChunk *
+ readSection(uint32_t SectionNumber,
+ const llvm::object::coff_aux_section_definition *Def);
+
+ void readAssociativeDefinition(
+ COFFSymbolRef COFFSym,
+ const llvm::object::coff_aux_section_definition *Def);
+
+ llvm::Optional<Symbol *>
+ createDefined(COFFSymbolRef Sym,
+ std::vector<const llvm::object::coff_aux_section_definition *>
+ &ComdatDefs);
+ Symbol *createRegular(COFFSymbolRef Sym);
+ Symbol *createUndefined(COFFSymbolRef Sym);
std::unique_ptr<COFFObjectFile> COFFObj;
- const coff_section *SXData = nullptr;
// List of all chunks defined by this file. This includes both section
// chunks and non-section chunks for common symbols.
@@ -157,16 +170,13 @@ private:
// Nonexistent section indices are filled with null pointers.
// (Because section number is 1-based, the first slot is always a
// null pointer.)
- std::vector<Chunk *> SparseChunks;
-
- // List of all symbols referenced or defined by this file.
- std::vector<SymbolBody *> SymbolBodies;
+ std::vector<SectionChunk *> SparseChunks;
- // This vector contains the same symbols as SymbolBodies, but they
- // are indexed such that you can get a SymbolBody by symbol
+ // This vector contains a list of all symbols defined or referenced by this
+ // file. They are indexed such that you can get a Symbol by symbol
// index. Nonexistent indices (which are occupied by auxiliary
// symbols in the real symbol table) are filled with null pointers.
- std::vector<SymbolBody *> SparseSymbolBodies;
+ std::vector<Symbol *> Symbols;
};
// This type represents import library members that contain DLL names
@@ -179,8 +189,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;
@@ -206,18 +217,19 @@ class BitcodeFile : public InputFile {
public:
explicit BitcodeFile(MemoryBufferRef M) : InputFile(BitcodeKind, M) {}
static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }
- std::vector<SymbolBody *> &getSymbols() { return SymbolBodies; }
+ ArrayRef<Symbol *> getSymbols() { return SymbolBodies; }
MachineTypes getMachineType() override;
+ static std::vector<BitcodeFile *> Instances;
std::unique_ptr<llvm::lto::InputFile> Obj;
private:
void parse() override;
- std::vector<SymbolBody *> SymbolBodies;
+ std::vector<Symbol *> SymbolBodies;
};
} // namespace coff
-std::string toString(coff::InputFile *File);
+std::string toString(const coff::InputFile *File);
} // namespace lld
#endif
diff --git a/COFF/LTO.cpp b/COFF/LTO.cpp
index 6883b3b4c2c8..fa2a54b61841 100644
--- a/COFF/LTO.cpp
+++ b/COFF/LTO.cpp
@@ -9,15 +9,16 @@
#include "LTO.h"
#include "Config.h"
-#include "Error.h"
#include "InputFiles.h"
#include "Symbols.h"
-#include "lld/Core/TargetOptionsCommandFlags.h"
+#include "lld/Common/ErrorHandler.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;
@@ -83,20 +88,17 @@ BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) {}
BitcodeCompiler::~BitcodeCompiler() = default;
-static void undefine(Symbol *S) {
- replaceBody<Undefined>(S, S->body()->getName());
-}
+static void undefine(Symbol *S) { replaceSymbol<Undefined>(S, S->getName()); }
void BitcodeCompiler::add(BitcodeFile &F) {
lto::InputFile &Obj = *F.Obj;
unsigned SymNum = 0;
- std::vector<SymbolBody *> SymBodies = F.getSymbols();
+ std::vector<Symbol *> SymBodies = F.getSymbols();
std::vector<lto::SymbolResolution> Resols(SymBodies.size());
// Provide a resolution to the LTO API for each symbol.
for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) {
- SymbolBody *B = SymBodies[SymNum];
- Symbol *Sym = B->symbol();
+ Symbol *Sym = SymBodies[SymNum];
lto::SymbolResolution &R = Resols[SymNum];
++SymNum;
@@ -105,7 +107,7 @@ void BitcodeCompiler::add(BitcodeFile &F) {
// 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->getFile() == &F;
+ R.Prevailing = !ObjSym.isUndefined() && Sym->getFile() == &F;
R.VisibleToRegularObj = Sym->IsUsedInRegularObj;
if (R.Prevailing)
undefine(Sym);
@@ -118,11 +120,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 +154,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 194a4cce8ada..a444aa7ac4fe 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 b63d4672c7d5..717ed3419ea5 100644
--- a/COFF/MapFile.cpp
+++ b/COFF/MapFile.cpp
@@ -20,11 +20,11 @@
//===----------------------------------------------------------------------===//
#include "MapFile.h"
-#include "Error.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "Writer.h"
+#include "lld/Common/ErrorHandler.h"
#include "llvm/Support/Parallel.h"
#include "llvm/Support/raw_ostream.h"
@@ -48,9 +48,9 @@ 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 (SymbolBody *B : File->getSymbols())
- if (auto *Sym = dyn_cast<DefinedRegular>(B))
+ for (ObjFile *File : ObjFile::Instances)
+ for (Symbol *B : File->getSymbols())
+ if (auto *Sym = dyn_cast_or_null<DefinedRegular>(B))
if (Sym && !Sym->getCOFFSymbol().isSectionDefinition())
V.push_back(Sym);
return V;
@@ -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 a2756e5c89e0..01be60d12d82 100644
--- a/COFF/MarkLive.cpp
+++ b/COFF/MarkLive.cpp
@@ -18,7 +18,7 @@ namespace coff {
// Set live bit on for each reachable chunk. Unmarked (unreachable)
// COMDAT chunks will be ignored by Writer, so they will be excluded
// from the final output.
-void markLive(const std::vector<Chunk *> &Chunks) {
+void markLive(ArrayRef<Chunk *> Chunks) {
// We build up a worklist of sections which have been marked as live. We only
// push into the worklist when we discover an unmarked section, and we mark
// as we push, so sections never appear twice in the list.
@@ -37,7 +37,7 @@ void markLive(const std::vector<Chunk *> &Chunks) {
Worklist.push_back(C);
};
- auto AddSym = [&](SymbolBody *B) {
+ auto AddSym = [&](Symbol *B) {
if (auto *Sym = dyn_cast<DefinedRegular>(B))
Enqueue(Sym->getChunk());
else if (auto *Sym = dyn_cast<DefinedImportData>(B))
@@ -47,23 +47,17 @@ void markLive(const std::vector<Chunk *> &Chunks) {
};
// Add GC root chunks.
- for (SymbolBody *B : Config->GCRoot)
+ for (Symbol *B : Config->GCRoot)
AddSym(B);
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.
- for (SymbolBody *B : SC->symbols())
- AddSym(B);
+ for (Symbol *B : SC->symbols())
+ if (B)
+ AddSym(B);
// Mark associative sections if any.
for (SectionChunk *C : SC->children())
diff --git a/COFF/Memory.h b/COFF/Memory.h
deleted file mode 100644
index 526f11344a09..000000000000
--- a/COFF/Memory.h
+++ /dev/null
@@ -1,52 +0,0 @@
-//===- Memory.h -------------------------------------------------*- C++ -*-===//
-//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// See ELF/Memory.h
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_COFF_MEMORY_H
-#define LLD_COFF_MEMORY_H
-
-#include "llvm/Support/Allocator.h"
-#include "llvm/Support/StringSaver.h"
-#include <vector>
-
-namespace lld {
-namespace coff {
-
-extern llvm::BumpPtrAllocator BAlloc;
-extern llvm::StringSaver Saver;
-
-struct SpecificAllocBase {
- SpecificAllocBase() { Instances.push_back(this); }
- virtual ~SpecificAllocBase() = default;
- virtual void reset() = 0;
- static std::vector<SpecificAllocBase *> Instances;
-};
-
-template <class T> struct SpecificAlloc : public SpecificAllocBase {
- void reset() override { Alloc.DestroyAll(); }
- llvm::SpecificBumpPtrAllocator<T> Alloc;
-};
-
-template <typename T, typename... U> T *make(U &&... Args) {
- static SpecificAlloc<T> Alloc;
- return new (Alloc.Alloc.Allocate()) T(std::forward<U>(Args)...);
-}
-
-inline void freeArena() {
- for (SpecificAllocBase *Alloc : SpecificAllocBase::Instances)
- Alloc->reset();
- BAlloc.Reset();
-}
-}
-}
-
-#endif
diff --git a/COFF/MinGW.cpp b/COFF/MinGW.cpp
new file mode 100644
index 000000000000..b7a47165640d
--- /dev/null
+++ b/COFF/MinGW.cpp
@@ -0,0 +1,146 @@
+//===- 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 "SymbolTable.h"
+#include "lld/Common/ErrorHandler.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",
+ "libc++",
+ "libc++abi",
+ "libunwind",
+ "libmsvcrt",
+ "libucrtbase",
+ };
+ 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;
+
+ // Only allow the symbol kinds that make sense to export; in particular,
+ // disallow import symbols.
+ if (!isa<DefinedRegular>(Sym) && !isa<DefinedCommon>(Sym))
+ return false;
+ if (ExcludeSymbols.count(Sym->getName()))
+ return false;
+
+ // Don't export anything that looks like an import symbol (which also can be
+ // a manually defined data symbol with such a name).
+ if (Sym->getName().startswith("__imp_"))
+ return false;
+
+ // If a corresponding __imp_ symbol exists and is defined, don't export it.
+ if (Symtab->find(("__imp_" + Sym->getName()).str()))
+ return false;
+
+ // Check that file is non-null before dereferencing it, symbols not
+ // originating in regular object files probably shouldn't be exported.
+ if (!Sym->getFile())
+ return false;
+
+ StringRef LibName = sys::path::filename(Sym->getFile()->ParentName);
+
+ // Drop the file extension.
+ LibName = LibName.substr(0, LibName.rfind('.'));
+ if (!LibName.empty())
+ return !ExcludeLibs.count(LibName);
+
+ StringRef FileName = sys::path::filename(Sym->getFile()->getName());
+ return !ExcludeObjects.count(FileName);
+}
+
+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 000000000000..fe6cc5588ebc
--- /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 61523c4f2256..0e7a79730fa2 100644
--- a/COFF/Options.td
+++ b/COFF/Options.td
@@ -9,13 +9,15 @@ class F<string name> : Flag<["/", "-", "-?"], name>;
class P<string name, string help> :
Joined<["/", "-", "-?"], name#":">, HelpText<help>;
-// Boolean flag suffixed by ":no".
-multiclass B<string name, string help> {
- def "" : F<name>;
- def _no : F<name#":no">, HelpText<help>;
+// Boolean flag which can be suffixed by ":no". Using it unsuffixed turns the
+// flag on and using it suffixed by ":no" turns it off.
+multiclass B<string name, string help_on, string help_off> {
+ def "" : F<name>, HelpText<help_on>;
+ def _no : F<name#":no">, HelpText<help_off>;
}
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 +32,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 +48,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,31 +80,53 @@ 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">;
def force_unresolved : F<"force:unresolved">;
+defm WX : B<"WX", "Treat warnings as errors", "Don't treat warnings as errors">;
-defm allowbind: B<"allowbind", "Disable DLL binding">;
-defm allowisolation : B<"allowisolation", "Set NO_ISOLATION bit">;
+defm allowbind : B<"allowbind", "Enable DLL binding (default)",
+ "Disable DLL binding">;
+defm allowisolation : B<"allowisolation", "Enable DLL isolation (default)",
+ "Disable DLL isolation">;
defm appcontainer : B<"appcontainer",
- "Image can only be run in an app container">;
-defm dynamicbase : B<"dynamicbase",
- "Disable address space layout randomization">;
-defm fixed : B<"fixed", "Enable base relocations">;
-defm highentropyva : B<"highentropyva", "Set HIGH_ENTROPY_VA bit">;
-defm largeaddressaware : B<"largeaddressaware", "Disable large addresses">;
-defm nxcompat : B<"nxcompat", "Disable data execution provention">;
-defm safeseh : B<"safeseh", "Produce an image with Safe Exception Handler">;
-defm tsaware : B<"tsaware", "Create non-Terminal Server aware executable">;
+ "Image can only be run in an app container",
+ "Image can run outside an app container (default)">;
+defm dynamicbase : B<"dynamicbase", "Enable ASLR (default unless /fixed)",
+ "Disable ASLR (default when /fixed)">;
+defm fixed : B<"fixed", "Disable base relocations",
+ "Enable base relocations (default)">;
+defm highentropyva : B<"highentropyva",
+ "Enable 64-bit ASLR (default on 64-bit)",
+ "Disable 64-bit ASLR">;
+defm largeaddressaware : B<"largeaddressaware",
+ "Enable large addresses (default on 64-bit)",
+ "Disable large addresses (default on 32-bit)">;
+defm nxcompat : B<"nxcompat", "Enable data execution prevention (default)",
+ "Disable data execution provention">;
+defm safeseh : B<"safeseh",
+ "Produce an image with Safe Exception Handler (only for x86)",
+ "Don't produce an image with Safe Exception Handler">;
+defm tsaware : B<"tsaware",
+ "Create Terminal Server aware executable (default)",
+ "Create non-Terminal Server aware executable">;
def help : F<"help">;
def help_q : Flag<["/?", "-?"], "">, Alias<help>;
// LLD extensions
-def nopdb : F<"nopdb">, HelpText<"Disable PDB generation for DWARF users">;
-def nosymtab : F<"nosymtab">;
+def debug_ghash : F<"debug:ghash">;
+def debug_dwarf : F<"debug:dwarf">;
+def export_all_symbols : F<"export-all-symbols">;
+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'">;
+def dash_dash_version : Flag<["--"], "version">,
+ HelpText<"Print version information">;
// Flags for debugging
def lldmap : F<"lldmap">;
@@ -130,10 +157,9 @@ 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">;
def verbose_all : QF<"verbose">;
def guardsym : QF<"guardsym">;
-
-defm wx : QB<"wx">;
diff --git a/COFF/PDB.cpp b/COFF/PDB.cpp
index 89462da93454..91a9a01db569 100644
--- a/COFF/PDB.cpp
+++ b/COFF/PDB.cpp
@@ -10,37 +10,44 @@
#include "PDB.h"
#include "Chunks.h"
#include "Config.h"
-#include "Error.h"
+#include "Driver.h"
#include "SymbolTable.h"
#include "Symbols.h"
+#include "Writer.h"
+#include "lld/Common/ErrorHandler.h"
#include "llvm/DebugInfo/CodeView/CVDebugRecord.h"
#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h"
+#include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h"
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
+#include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.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/PDBStringTableBuilder.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>
@@ -67,16 +74,16 @@ class PDBLinker {
public:
PDBLinker(SymbolTable *Symtab)
: Alloc(), Symtab(Symtab), Builder(Alloc), TypeTable(Alloc),
- IDTable(Alloc) {}
+ IDTable(Alloc), GlobalTypeTable(Alloc), GlobalIDTable(Alloc) {}
/// Emit the basic PDB structure: initial streams, headers, etc.
- void initialize(const llvm::codeview::DebugInfo *DI);
+ 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 addObjectFile(ObjectFile *File);
+ void addObjFile(ObjFile *File);
/// Produce a mapping from the type and item indices used in the object
/// file to those in the destination PDB.
@@ -89,13 +96,17 @@ public:
/// 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(ObjectFile *File, CVIndexMap &ObjectIndexMap);
+ const CVIndexMap &mergeDebugT(ObjFile *File, CVIndexMap &ObjectIndexMap);
- const CVIndexMap &maybeMergeTypeServerPDB(ObjectFile *File,
+ const CVIndexMap &maybeMergeTypeServerPDB(ObjFile *File,
TypeServer2Record &TS);
/// Add the section map and section contributions to the PDB.
- void addSections(ArrayRef<uint8_t> SectionTable);
+ 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();
@@ -108,10 +119,16 @@ private:
pdb::PDBFileBuilder Builder;
/// Type records that will go into the PDB TPI stream.
- TypeTableBuilder TypeTable;
+ MergingTypeTableBuilder TypeTable;
/// Item records that will go into the PDB IPI stream.
- TypeTableBuilder IDTable;
+ MergingTypeTableBuilder IDTable;
+
+ /// Type records that will go into the PDB TPI stream (for /DEBUG:GHASH)
+ GlobalTypeTableBuilder GlobalTypeTable;
+
+ /// Item records that will go into the PDB IPI stream (for /DEBUG:GHASH)
+ GlobalTypeTableBuilder GlobalIDTable;
/// PDBs use a single global string table for filenames in the file checksum
/// table.
@@ -126,15 +143,7 @@ private:
};
}
-// 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);
-}
-
-static SectionChunk *findByName(std::vector<SectionChunk *> &Sections,
+static SectionChunk *findByName(ArrayRef<SectionChunk *> Sections,
StringRef Name) {
for (SectionChunk *C : Sections)
if (C->getSectionName() == Name)
@@ -152,21 +161,58 @@ static ArrayRef<uint8_t> consumeDebugMagic(ArrayRef<uint8_t> Data,
return Data.slice(4);
}
-static ArrayRef<uint8_t> getDebugSection(ObjectFile *File, StringRef SecName) {
+static ArrayRef<uint8_t> getDebugSection(ObjFile *File, StringRef SecName) {
if (SectionChunk *Sec = findByName(File->getDebugChunks(), SecName))
return consumeDebugMagic(Sec->getContents(), SecName);
return {};
}
+// A COFF .debug$H section is currently a clang extension. This function checks
+// if a .debug$H section is in a format that we expect / understand, so that we
+// can ignore any sections which are coincidentally also named .debug$H but do
+// not contain a format we recognize.
+static bool canUseDebugH(ArrayRef<uint8_t> DebugH) {
+ if (DebugH.size() < sizeof(object::debug_h_header))
+ return false;
+ auto *Header =
+ reinterpret_cast<const object::debug_h_header *>(DebugH.data());
+ DebugH = DebugH.drop_front(sizeof(object::debug_h_header));
+ return Header->Magic == COFF::DEBUG_HASHES_SECTION_MAGIC &&
+ Header->Version == 0 &&
+ Header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1) &&
+ (DebugH.size() % 20 == 0);
+}
+
+static Optional<ArrayRef<uint8_t>> getDebugH(ObjFile *File) {
+ SectionChunk *Sec = findByName(File->getDebugChunks(), ".debug$H");
+ if (!Sec)
+ return llvm::None;
+ ArrayRef<uint8_t> Contents = Sec->getContents();
+ if (!canUseDebugH(Contents))
+ return None;
+ return Contents;
+}
+
+static ArrayRef<GloballyHashedType>
+getHashesFromDebugH(ArrayRef<uint8_t> DebugH) {
+ assert(canUseDebugH(DebugH));
+
+ DebugH = DebugH.drop_front(sizeof(object::debug_h_header));
+ uint32_t Count = DebugH.size() / sizeof(GloballyHashedType);
+ return {reinterpret_cast<const GloballyHashedType *>(DebugH.data()), Count};
+}
+
static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder,
- TypeTableBuilder &TypeTable) {
+ TypeCollection &TypeTable) {
// Start the TPI or IPI stream header.
TpiBuilder.setVersionHeader(pdb::PdbTpiV80);
- // Flatten the in memory type table.
- TypeTable.ForEachRecord([&](TypeIndex TI, ArrayRef<uint8_t> Rec) {
- // FIXME: Hash types.
- TpiBuilder.addTypeRecord(Rec, None);
+ // Flatten the in memory type table and hash each type.
+ TypeTable.ForEachRecord([&](TypeIndex TI, const CVType &Type) {
+ auto Hash = pdb::hashTypeRecord(Type);
+ if (auto E = Hash.takeError())
+ fatal("type hashing error");
+ TpiBuilder.addTypeRecord(Type.RecordData, *Hash);
});
}
@@ -180,11 +226,11 @@ maybeReadTypeServerRecord(CVTypeArray &Types) {
return None;
TypeServer2Record TS;
if (auto EC = TypeDeserializer::deserializeAs(const_cast<CVType &>(Type), TS))
- fatal(EC, "error reading type server record");
+ fatal("error reading type server record: " + toString(std::move(EC)));
return std::move(TS);
}
-const CVIndexMap &PDBLinker::mergeDebugT(ObjectFile *File,
+const CVIndexMap &PDBLinker::mergeDebugT(ObjFile *File,
CVIndexMap &ObjectIndexMap) {
ArrayRef<uint8_t> Data = getDebugSection(File, ".debug$T");
if (Data.empty())
@@ -194,7 +240,7 @@ const CVIndexMap &PDBLinker::mergeDebugT(ObjectFile *File,
CVTypeArray Types;
BinaryStreamReader Reader(Stream);
if (auto EC = Reader.readArray(Types, Reader.getLength()))
- fatal(EC, "Reader::readArray failed");
+ fatal("Reader::readArray failed: " + toString(std::move(EC)));
// Look through type servers. If we've already seen this type server, don't
// merge any type information.
@@ -203,17 +249,41 @@ const CVIndexMap &PDBLinker::mergeDebugT(ObjectFile *File,
// 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");
+ if (Config->DebugGHashes) {
+ ArrayRef<GloballyHashedType> Hashes;
+ std::vector<GloballyHashedType> OwnedHashes;
+ if (Optional<ArrayRef<uint8_t>> DebugH = getDebugH(File))
+ Hashes = getHashesFromDebugH(*DebugH);
+ else {
+ OwnedHashes = GloballyHashedType::hashTypes(Types);
+ Hashes = OwnedHashes;
+ }
+
+ if (auto Err = mergeTypeAndIdRecords(GlobalIDTable, GlobalTypeTable,
+ ObjectIndexMap.TPIMap, Types, Hashes))
+ fatal("codeview::mergeTypeAndIdRecords failed: " +
+ toString(std::move(Err)));
+ } else {
+ if (auto Err = mergeTypeAndIdRecords(IDTable, TypeTable,
+ ObjectIndexMap.TPIMap, Types))
+ fatal("codeview::mergeTypeAndIdRecords failed: " +
+ toString(std::move(Err)));
+ }
return ObjectIndexMap;
}
static Expected<std::unique_ptr<pdb::NativeSession>>
tryToLoadPDB(const GUID &GuidFromObj, StringRef TSPath) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = MemoryBuffer::getFile(
+ TSPath, /*FileSize=*/-1, /*RequiresNullTerminator=*/false);
+ if (!MBOrErr)
+ return errorCodeToError(MBOrErr.getError());
+
std::unique_ptr<pdb::IPDBSession> ThisSession;
- if (auto EC =
- pdb::loadDataForPDB(pdb::PDB_ReaderType::Native, TSPath, ThisSession))
+ if (auto EC = pdb::NativeSession::createFromPdb(
+ MemoryBuffer::getMemBuffer(Driver->takeBuffer(std::move(*MBOrErr)),
+ /*RequiresNullTerminator=*/false),
+ ThisSession))
return std::move(EC);
std::unique_ptr<pdb::NativeSession> NS(
@@ -234,7 +304,7 @@ tryToLoadPDB(const GUID &GuidFromObj, StringRef TSPath) {
return std::move(NS);
}
-const CVIndexMap &PDBLinker::maybeMergeTypeServerPDB(ObjectFile *File,
+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.
@@ -260,23 +330,46 @@ const CVIndexMap &PDBLinker::maybeMergeTypeServerPDB(ObjectFile *File,
ExpectedSession = tryToLoadPDB(TS.getGuid(), Path);
}
if (auto E = ExpectedSession.takeError())
- fatal(E, "Type server PDB was not found");
+ fatal("Type server PDB was not found: " + toString(std::move(E)));
- // 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.
+ fatal("Type server does not have TPI stream: " + toString(std::move(E)));
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");
+ fatal("Type server does not have TPI stream: " + toString(std::move(E)));
+
+ if (Config->DebugGHashes) {
+ // PDBs do not actually store global hashes, so when merging a type server
+ // PDB we have to synthesize global hashes. To do this, we first synthesize
+ // global hashes for the TPI stream, since it is independent, then we
+ // synthesize hashes for the IPI stream, using the hashes for the TPI stream
+ // as inputs.
+ auto TpiHashes = GloballyHashedType::hashTypes(ExpectedTpi->typeArray());
+ auto IpiHashes =
+ GloballyHashedType::hashIds(ExpectedIpi->typeArray(), TpiHashes);
+
+ // Merge TPI first, because the IPI stream will reference type indices.
+ if (auto Err = mergeTypeRecords(GlobalTypeTable, IndexMap.TPIMap,
+ ExpectedTpi->typeArray(), TpiHashes))
+ fatal("codeview::mergeTypeRecords failed: " + toString(std::move(Err)));
+
+ // Merge IPI.
+ if (auto Err =
+ mergeIdRecords(GlobalIDTable, IndexMap.TPIMap, IndexMap.IPIMap,
+ ExpectedIpi->typeArray(), IpiHashes))
+ fatal("codeview::mergeIdRecords failed: " + toString(std::move(Err)));
+ } else {
+ // Merge TPI first, because the IPI stream will reference type indices.
+ if (auto Err = mergeTypeRecords(TypeTable, IndexMap.TPIMap,
+ ExpectedTpi->typeArray()))
+ fatal("codeview::mergeTypeRecords failed: " + toString(std::move(Err)));
+
+ // Merge IPI.
+ if (auto Err = mergeIdRecords(IDTable, IndexMap.TPIMap, IndexMap.IPIMap,
+ ExpectedIpi->typeArray()))
+ fatal("codeview::mergeIdRecords failed: " + toString(std::move(Err)));
+ }
return IndexMap;
}
@@ -290,7 +383,7 @@ static bool remapTypeIndex(TypeIndex &TI, ArrayRef<TypeIndex> TypeIndexMap) {
return true;
}
-static void remapTypesInSymbolRecord(ObjectFile *File,
+static void remapTypesInSymbolRecord(ObjFile *File, SymbolKind SymKind,
MutableArrayRef<uint8_t> Contents,
const CVIndexMap &IndexMap,
ArrayRef<TiReference> TypeRefs) {
@@ -301,27 +394,73 @@ static void remapTypesInSymbolRecord(ObjectFile *File,
// 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)
+ bool IsItemIndex = Ref.Kind == TiRefKind::IndexRef;
+ if (IsItemIndex && 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)) {
+ log("ignoring symbol record of kind 0x" + utohexstr(SymKind) + " in " +
+ File->getName() + " with bad " + (IsItemIndex ? "item" : "type") +
+ " index 0x" + utohexstr(TI.getIndex()));
TI = TypeIndex(SimpleTypeKind::NotTranslated);
- log("ignoring symbol record in " + File->getName() +
- " with bad type index 0x" + utohexstr(TI.getIndex()));
continue;
}
}
}
}
-/// MSVC translates S_PROC_ID_END to S_END.
-uint16_t canonicalizeSymbolKind(SymbolKind Kind) {
- if (Kind == SymbolKind::S_PROC_ID_END)
- return SymbolKind::S_END;
- return Kind;
+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,
+ TypeCollection &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()) {
+ CVType FuncIdData = IDTable.getType(*TI);
+ 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.
@@ -339,10 +478,8 @@ static MutableArrayRef<uint8_t> copySymbolForPdb(const CVSymbol &Sym,
memset(NewData.data() + Sym.length(), 0, Size - Sym.length());
// Update the record prefix length. It should point to the beginning of the
- // next record. MSVC does some canonicalization of the record kind, so we do
- // that as well.
+ // next record.
auto *Prefix = reinterpret_cast<RecordPrefix *>(Mem);
- Prefix->RecordKind = canonicalizeSymbolKind(Sym.kind());
Prefix->RecordLen = Size - 2;
return NewData;
}
@@ -402,7 +539,7 @@ static void scopeStackOpen(SmallVectorImpl<SymbolScope> &Stack,
}
static void scopeStackClose(SmallVectorImpl<SymbolScope> &Stack,
- uint32_t CurOffset, ObjectFile *File) {
+ uint32_t CurOffset, ObjFile *File) {
if (Stack.empty()) {
warn("symbol scopes are not balanced in " + File->getName());
return;
@@ -411,8 +548,86 @@ static void scopeStackClose(SmallVectorImpl<SymbolScope> &Stack,
S.OpeningRecord->PtrEnd = CurOffset;
}
-static void mergeSymbolRecords(BumpPtrAllocator &Alloc, ObjectFile *File,
+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,
+ TypeCollection &IDTable,
BinaryStreamRef SymData) {
// FIXME: Improve error recovery by warning and skipping records when
// possible.
@@ -420,11 +635,11 @@ static void mergeSymbolRecords(BumpPtrAllocator &Alloc, ObjectFile *File,
BinaryStreamReader Reader(SymData);
ExitOnErr(Reader.readArray(Syms, Reader.getLength()));
SmallVector<SymbolScope, 4> Scopes;
- for (const CVSymbol &Sym : Syms) {
+ 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 (!discoverTypeIndices(Sym, TypeRefs)) {
+ if (!discoverTypeIndicesInSymbol(Sym, TypeRefs)) {
log("ignoring unknown symbol record with kind 0x" + utohexstr(Sym.kind()));
continue;
}
@@ -435,17 +650,30 @@ static void mergeSymbolRecords(BumpPtrAllocator &Alloc, ObjectFile *File,
// Re-map all the type index references.
MutableArrayRef<uint8_t> Contents =
NewData.drop_front(sizeof(RecordPrefix));
- remapTypesInSymbolRecord(File, Contents, IndexMap, TypeRefs);
+ remapTypesInSymbolRecord(File, Sym.kind(), Contents, IndexMap, 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);
// Fill in "Parent" and "End" fields by maintaining a stack of scopes.
- CVSymbol NewSym(Sym.kind(), NewData);
- if (symbolOpensScope(Sym.kind()))
+ CVSymbol NewSym(NewKind, NewData);
+ if (symbolOpensScope(NewKind))
scopeStackOpen(Scopes, File->ModuleDBI->getNextSymbolOffset(), NewSym);
- else if (symbolEndsScope(Sym.kind()))
+ 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.
- File->ModuleDBI->addSymbol(NewSym);
+ if (symbolGoesInModuleStream(NewSym))
+ File->ModuleDBI->addSymbol(NewSym);
}
}
@@ -460,7 +688,7 @@ static ArrayRef<uint8_t> relocateDebugChunk(BumpPtrAllocator &Alloc,
".debug$S");
}
-void PDBLinker::addObjectFile(ObjectFile *File) {
+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
@@ -511,7 +739,13 @@ void PDBLinker::addObjectFile(ObjectFile *File) {
File->ModuleDBI->addDebugSubsection(SS);
break;
case DebugSubsectionKind::Symbols:
- mergeSymbolRecords(Alloc, File, IndexMap, SS.getRecordData());
+ if (Config->DebugGHashes) {
+ mergeSymbolRecords(Alloc, File, Builder.getGsiBuilder(), IndexMap,
+ GlobalIDTable, SS.getRecordData());
+ } else {
+ mergeSymbolRecords(Alloc, File, Builder.getGsiBuilder(), IndexMap,
+ IDTable, SS.getRecordData());
+ }
break;
default:
// FIXME: Process the rest of the subsections.
@@ -539,45 +773,88 @@ void PDBLinker::addObjectFile(ObjectFile *File) {
}
}
+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 (ObjectFile *File : Symtab->ObjectFiles)
- addObjectFile(File);
+ 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);
+ // Construct TPI and IPI stream contents.
+ if (Config->DebugGHashes) {
+ addTypeInfo(Builder.getTpiBuilder(), GlobalTypeTable);
+ addTypeInfo(Builder.getIpiBuilder(), GlobalIDTable);
+ } else {
+ addTypeInfo(Builder.getTpiBuilder(), TypeTable);
+ addTypeInfo(Builder.getIpiBuilder(), IDTable);
+ }
- // Add public and symbol records stream.
+ // 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);
+ if (Def && Def->isLive() && Def->getChunk())
+ Publics.push_back(createPublic(Def));
+ });
- // For now we don't actually write any thing useful to the publics stream, but
- // the act of "getting" it also creates it lazily so that we write an empty
- // stream.
- (void)Builder.getPublicsBuilder();
+ 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 addLinkerModuleSymbols(StringRef Path,
- pdb::DbiModuleDescriptorBuilder &Mod,
- BumpPtrAllocator &Allocator) {
- codeview::SymbolSerializer Serializer(Allocator, CodeViewContainer::Pdb);
- codeview::ObjNameSym ONS(SymbolRecordKind::ObjNameSym);
- codeview::Compile3Sym CS(SymbolRecordKind::Compile3Sym);
- codeview::EnvBlockSym EBS(SymbolRecordKind::EnvBlockSym);
+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 = 0;
- CS.VersionBackendMajor = 0;
- CS.VersionBackendMinor = 0;
+ 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;
@@ -592,7 +869,9 @@ static void addLinkerModuleSymbols(StringRef Path,
sys::fs::current_path(cwd);
EBS.Fields.push_back(cwd);
EBS.Fields.push_back("exe");
- EBS.Fields.push_back(Config->Argv[0]);
+ 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");
@@ -605,17 +884,33 @@ static void addLinkerModuleSymbols(StringRef Path,
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(SymbolTable *Symtab, ArrayRef<uint8_t> SectionTable,
- const llvm::codeview::DebugInfo *DI) {
+void coff::createPDB(SymbolTable *Symtab,
+ ArrayRef<OutputSection *> OutputSections,
+ ArrayRef<uint8_t> SectionTable,
+ const llvm::codeview::DebugInfo &BuildId) {
PDBLinker PDB(Symtab);
- PDB.initialize(DI);
+ PDB.initialize(BuildId);
PDB.addObjectsToPDB();
- PDB.addSections(SectionTable);
+ PDB.addSections(OutputSections, SectionTable);
PDB.commit();
}
-void PDBLinker::initialize(const llvm::codeview::DebugInfo *DI) {
+void PDBLinker::initialize(const llvm::codeview::DebugInfo &BuildId) {
ExitOnErr(Builder.initialize(4096)); // 4096 is blocksize
// Create streams in MSF for predefined streams, namely
@@ -625,41 +920,71 @@ void PDBLinker::initialize(const llvm::codeview::DebugInfo *DI) {
// Add an Info stream.
auto &InfoBuilder = Builder.getInfoBuilder();
- InfoBuilder.setAge(DI ? DI->PDB70.Age : 0);
+ InfoBuilder.setAge(BuildId.PDB70.Age);
- GUID uuid{};
- if (DI)
- memcpy(&uuid, &DI->PDB70.Signature, sizeof(uuid));
+ GUID uuid;
+ memcpy(&uuid, &BuildId.PDB70.Signature, sizeof(uuid));
InfoBuilder.setGuid(uuid);
InfoBuilder.setSignature(time(nullptr));
InfoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70);
// Add an empty DBI stream.
pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder();
+ DbiBuilder.setAge(BuildId.PDB70.Age);
DbiBuilder.setVersionHeader(pdb::PdbDbiV70);
ExitOnErr(DbiBuilder.addDbgStream(pdb::DbgHeaderType::NewFPO, {}));
}
-void PDBLinker::addSections(ArrayRef<uint8_t> SectionTable) {
- // Add Section Contributions.
- pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder();
- addSectionContribs(Symtab, DbiBuilder);
-
- // Add Section Map stream.
- ArrayRef<object::coff_section> Sections = {
- (const object::coff_section *)SectionTable.data(),
- SectionTable.size() / sizeof(object::coff_section)};
- SectionMap = pdb::DbiStreamBuilder::createSectionMap(Sections);
- DbiBuilder.setSectionMap(SectionMap);
+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);
+}
+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);
- addLinkerModuleSymbols(NativePath, LinkerModule, Alloc);
+ addCommonLinkerModuleSymbols(NativePath, LinkerModule, Alloc);
+
+ // 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)};
+ SectionMap = pdb::DbiStreamBuilder::createSectionMap(Sections);
+ DbiBuilder.setSectionMap(SectionMap);
// Add COFF section header stream.
ExitOnErr(
diff --git a/COFF/PDB.h b/COFF/PDB.h
index 9aaa3178df21..defd7d236790 100644
--- a/COFF/PDB.h
+++ b/COFF/PDB.h
@@ -21,10 +21,13 @@ union DebugInfo;
namespace lld {
namespace coff {
+class OutputSection;
class SymbolTable;
-void createPDB(SymbolTable *Symtab, llvm::ArrayRef<uint8_t> SectionTable,
- const llvm::codeview::DebugInfo *DI);
+void createPDB(SymbolTable *Symtab,
+ llvm::ArrayRef<OutputSection *> OutputSections,
+ llvm::ArrayRef<uint8_t> SectionTable,
+ const llvm::codeview::DebugInfo &BuildId);
}
}
diff --git a/COFF/Strings.cpp b/COFF/Strings.cpp
index 84f9b9a55a32..89b9c5186fd1 100644
--- a/COFF/Strings.cpp
+++ b/COFF/Strings.cpp
@@ -20,7 +20,7 @@ using namespace lld;
using namespace lld::coff;
using namespace llvm;
-Optional<std::string> coff::demangle(StringRef S) {
+Optional<std::string> coff::demangleMSVC(StringRef S) {
#if defined(_MSC_VER)
// UnDecorateSymbolName is not thread-safe, so we need a mutex.
static std::mutex Mu;
diff --git a/COFF/Strings.h b/COFF/Strings.h
index 1f85f3e2da5c..67fc1c773c66 100644
--- a/COFF/Strings.h
+++ b/COFF/Strings.h
@@ -16,7 +16,7 @@
namespace lld {
namespace coff {
-llvm::Optional<std::string> demangle(llvm::StringRef S);
+llvm::Optional<std::string> demangleMSVC(llvm::StringRef S);
}
}
diff --git a/COFF/SymbolTable.cpp b/COFF/SymbolTable.cpp
index c06e42bb114b..95b48e6d059f 100644
--- a/COFF/SymbolTable.cpp
+++ b/COFF/SymbolTable.cpp
@@ -10,10 +10,10 @@
#include "SymbolTable.h"
#include "Config.h"
#include "Driver.h"
-#include "Error.h"
#include "LTO.h"
-#include "Memory.h"
#include "Symbols.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
@@ -24,36 +24,6 @@ using namespace llvm;
namespace lld {
namespace coff {
-enum SymbolPreference {
- SP_EXISTING = -1,
- SP_CONFLICT = 0,
- SP_NEW = 1,
-};
-
-/// Checks if an existing symbol S should be kept or replaced by a new symbol.
-/// Returns SP_EXISTING when S should be kept, SP_NEW when the new symbol
-/// should be kept, and SP_CONFLICT if no valid resolution exists.
-static SymbolPreference compareDefined(Symbol *S, bool WasInserted,
- bool NewIsCOMDAT) {
- // If the symbol wasn't previously known, the new symbol wins by default.
- if (WasInserted || !isa<Defined>(S->body()))
- return SP_NEW;
-
- // If the existing symbol is a DefinedRegular, both it and the new symbol
- // must be comdats. In that case, we have no reason to prefer one symbol
- // over the other, and we keep the existing one. If one of the symbols
- // is not a comdat, we report a conflict.
- if (auto *R = dyn_cast<DefinedRegular>(S->body())) {
- if (NewIsCOMDAT && R->isCOMDAT())
- return SP_EXISTING;
- else
- return SP_CONFLICT;
- }
-
- // Existing symbol is not a DefinedRegular; new symbol wins.
- return SP_NEW;
-}
-
SymbolTable *Symtab;
void SymbolTable::addFile(InputFile *File) {
@@ -68,12 +38,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,70 +54,92 @@ 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) {
+ SmallPtrSet<Symbol *, 8> Undefs;
+ DenseMap<Symbol *, Symbol *> LocalImports;
+
+ for (auto &I : SymMap) {
Symbol *Sym = I.second;
- auto *Undef = dyn_cast<Undefined>(Sym->body());
+ auto *Undef = dyn_cast<Undefined>(Sym);
if (!Undef)
continue;
if (!Sym->IsUsedInRegularObj)
continue;
+
StringRef Name = Undef->getName();
+
// A weak alias may have been resolved, so check for that.
if (Defined *D = Undef->getWeakAlias()) {
- // We resolve weak aliases by replacing the alias's SymbolBody with the
- // target's SymbolBody. This causes all SymbolBody pointers referring to
- // the old symbol to instead refer to the new symbol. However, we can't
- // just blindly copy sizeof(Symbol::Body) bytes from D to Sym->Body
- // because D may be an internal symbol, and internal symbols are stored as
- // "unparented" SymbolBodies. For that reason we need to check which type
- // of symbol we are dealing with and copy the correct number of bytes.
+ // We want to replace Sym with D. However, we can't just blindly
+ // copy sizeof(SymbolUnion) bytes from D to Sym because D may be an
+ // internal symbol, and internal symbols are stored as "unparented"
+ // Symbols. For that reason we need to check which type of symbol we
+ // are dealing with and copy the correct number of bytes.
if (isa<DefinedRegular>(D))
- memcpy(Sym->Body.buffer, D, sizeof(DefinedRegular));
+ memcpy(Sym, D, sizeof(DefinedRegular));
else if (isa<DefinedAbsolute>(D))
- memcpy(Sym->Body.buffer, D, sizeof(DefinedAbsolute));
+ memcpy(Sym, D, sizeof(DefinedAbsolute));
else
- // No other internal symbols are possible.
- Sym->Body = D->symbol()->Body;
+ memcpy(Sym, D, sizeof(SymbolUnion));
continue;
}
+
// If we can resolve a symbol by removing __imp_ prefix, do that.
// This odd rule is for compatibility with MSVC linker.
if (Name.startswith("__imp_")) {
Symbol *Imp = find(Name.substr(strlen("__imp_")));
- if (Imp && isa<Defined>(Imp->body())) {
- auto *D = cast<Defined>(Imp->body());
- replaceBody<DefinedLocalImport>(Sym, Name, D);
- LocalImportChunks.push_back(
- cast<DefinedLocalImport>(Sym->body())->getChunk());
+ if (Imp && isa<Defined>(Imp)) {
+ auto *D = cast<Defined>(Imp);
+ replaceSymbol<DefinedLocalImport>(Sym, Name, D);
+ LocalImportChunks.push_back(cast<DefinedLocalImport>(Sym)->getChunk());
+ LocalImports[Sym] = D;
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());
+ replaceSymbol<DefinedAbsolute>(Sym, Name, 0);
+ Undefs.insert(Sym);
}
- if (Undefs.empty())
+
+ if (Undefs.empty() && LocalImports.empty())
return;
- for (SymbolBody *B : Config->GCRoot)
+
+ for (Symbol *B : Config->GCRoot) {
if (Undefs.count(B))
- warn("<root>: undefined symbol: " + B->getName());
- for (ObjectFile *File : ObjectFiles)
- for (SymbolBody *Sym : File->getSymbols())
+ errorOrWarn("<root>: undefined symbol: " + B->getName());
+ if (Symbol *Imp = LocalImports.lookup(B))
+ warn("<root>: locally defined symbol imported: " + Imp->getName() +
+ " (defined in " + toString(Imp->getFile()) + ")");
+ }
+
+ for (ObjFile *File : ObjFile::Instances) {
+ for (Symbol *Sym : File->getSymbols()) {
+ if (!Sym)
+ continue;
if (Undefs.count(Sym))
- warn(toString(File) + ": undefined symbol: " + Sym->getName());
- if (!Config->Force)
- fatal("link failed");
+ errorOrWarn(toString(File) + ": undefined symbol: " + Sym->getName());
+ if (Symbol *Imp = LocalImports.lookup(Sym))
+ warn(toString(File) + ": locally defined symbol imported: " +
+ Imp->getName() + " (defined in " + toString(Imp->getFile()) + ")");
+ }
+ }
}
std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) {
- Symbol *&Sym = Symtab[CachedHashStringRef(Name)];
+ Symbol *&Sym = SymMap[CachedHashStringRef(Name)];
if (Sym)
return {Sym, false};
- Sym = make<Symbol>();
+ Sym = (Symbol *)make<SymbolUnion>();
Sym->IsUsedInRegularObj = false;
Sym->PendingArchiveLoad = false;
return {Sym, true};
@@ -160,11 +152,11 @@ Symbol *SymbolTable::addUndefined(StringRef Name, InputFile *F,
std::tie(S, WasInserted) = insert(Name);
if (!F || !isa<BitcodeFile>(F))
S->IsUsedInRegularObj = true;
- if (WasInserted || (isa<Lazy>(S->body()) && IsWeakAlias)) {
- replaceBody<Undefined>(S, Name);
+ if (WasInserted || (isa<Lazy>(S) && IsWeakAlias)) {
+ replaceSymbol<Undefined>(S, Name);
return S;
}
- if (auto *L = dyn_cast<Lazy>(S->body())) {
+ if (auto *L = dyn_cast<Lazy>(S)) {
if (!S->PendingArchiveLoad) {
S->PendingArchiveLoad = true;
L->File->addMember(&L->Sym);
@@ -179,10 +171,10 @@ void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol Sym) {
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
if (WasInserted) {
- replaceBody<Lazy>(S, F, Sym);
+ replaceSymbol<Lazy>(S, F, Sym);
return;
}
- auto *U = dyn_cast<Undefined>(S->body());
+ auto *U = dyn_cast<Undefined>(S);
if (!U || U->WeakAlias || S->PendingArchiveLoad)
return;
S->PendingArchiveLoad = true;
@@ -190,9 +182,8 @@ void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol Sym) {
}
void SymbolTable::reportDuplicate(Symbol *Existing, InputFile *NewFile) {
- error("duplicate symbol: " + toString(*Existing->body()) + " in " +
- toString(Existing->body()->getFile()) + " and in " +
- (NewFile ? toString(NewFile) : "(internal)"));
+ error("duplicate symbol: " + toString(*Existing) + " in " +
+ toString(Existing->getFile()) + " and in " + toString(NewFile));
}
Symbol *SymbolTable::addAbsolute(StringRef N, COFFSymbolRef Sym) {
@@ -200,9 +191,9 @@ Symbol *SymbolTable::addAbsolute(StringRef N, COFFSymbolRef Sym) {
bool WasInserted;
std::tie(S, WasInserted) = insert(N);
S->IsUsedInRegularObj = true;
- if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
- replaceBody<DefinedAbsolute>(S, N, Sym);
- else if (!isa<DefinedCOFF>(S->body()))
+ if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S))
+ replaceSymbol<DefinedAbsolute>(S, N, Sym);
+ else if (!isa<DefinedCOFF>(S))
reportDuplicate(S, nullptr);
return S;
}
@@ -212,9 +203,9 @@ Symbol *SymbolTable::addAbsolute(StringRef N, uint64_t VA) {
bool WasInserted;
std::tie(S, WasInserted) = insert(N);
S->IsUsedInRegularObj = true;
- if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
- replaceBody<DefinedAbsolute>(S, N, VA);
- else if (!isa<DefinedCOFF>(S->body()))
+ if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S))
+ replaceSymbol<DefinedAbsolute>(S, N, VA);
+ else if (!isa<DefinedCOFF>(S))
reportDuplicate(S, nullptr);
return S;
}
@@ -224,14 +215,14 @@ Symbol *SymbolTable::addSynthetic(StringRef N, Chunk *C) {
bool WasInserted;
std::tie(S, WasInserted) = insert(N);
S->IsUsedInRegularObj = true;
- if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
- replaceBody<DefinedSynthetic>(S, N, C);
- else if (!isa<DefinedCOFF>(S->body()))
+ if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S))
+ replaceSymbol<DefinedSynthetic>(S, N, C);
+ else if (!isa<DefinedCOFF>(S))
reportDuplicate(S, nullptr);
return S;
}
-Symbol *SymbolTable::addRegular(InputFile *F, StringRef N, bool IsCOMDAT,
+Symbol *SymbolTable::addRegular(InputFile *F, StringRef N,
const coff_symbol_generic *Sym,
SectionChunk *C) {
Symbol *S;
@@ -239,21 +230,32 @@ Symbol *SymbolTable::addRegular(InputFile *F, StringRef N, bool IsCOMDAT,
std::tie(S, WasInserted) = insert(N);
if (!isa<BitcodeFile>(F))
S->IsUsedInRegularObj = true;
- SymbolPreference SP = compareDefined(S, WasInserted, IsCOMDAT);
- if (SP == SP_CONFLICT) {
+ if (WasInserted || !isa<DefinedRegular>(S))
+ replaceSymbol<DefinedRegular>(S, F, N, /*IsCOMDAT*/ false,
+ /*IsExternal*/ true, Sym, C);
+ else
reportDuplicate(S, F);
- } else if (SP == SP_NEW) {
- replaceBody<DefinedRegular>(S, F, N, IsCOMDAT, /*IsExternal*/ true, Sym, C);
- } else if (SP == SP_EXISTING && IsCOMDAT && C) {
- C->markDiscarded();
- // Discard associative chunks that we've parsed so far. No need to recurse
- // because an associative section cannot have children.
- for (SectionChunk *Child : C->children())
- Child->markDiscarded();
- }
return S;
}
+std::pair<Symbol *, bool>
+SymbolTable::addComdat(InputFile *F, StringRef N,
+ const coff_symbol_generic *Sym) {
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(N);
+ if (!isa<BitcodeFile>(F))
+ S->IsUsedInRegularObj = true;
+ if (WasInserted || !isa<DefinedRegular>(S)) {
+ replaceSymbol<DefinedRegular>(S, F, N, /*IsCOMDAT*/ true,
+ /*IsExternal*/ true, Sym, nullptr);
+ return {S, true};
+ }
+ if (!cast<DefinedRegular>(S)->isCOMDAT())
+ reportDuplicate(S, F);
+ return {S, false};
+}
+
Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size,
const coff_symbol_generic *Sym, CommonChunk *C) {
Symbol *S;
@@ -261,51 +263,56 @@ Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size,
std::tie(S, WasInserted) = insert(N);
if (!isa<BitcodeFile>(F))
S->IsUsedInRegularObj = true;
- if (WasInserted || !isa<DefinedCOFF>(S->body()))
- replaceBody<DefinedCommon>(S, F, N, Size, Sym, C);
- else if (auto *DC = dyn_cast<DefinedCommon>(S->body()))
+ if (WasInserted || !isa<DefinedCOFF>(S))
+ replaceSymbol<DefinedCommon>(S, F, N, Size, Sym, C);
+ else if (auto *DC = dyn_cast<DefinedCommon>(S))
if (Size > DC->getSize())
- replaceBody<DefinedCommon>(S, F, N, Size, Sym, C);
+ replaceSymbol<DefinedCommon>(S, F, N, Size, Sym, C);
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()))
- replaceBody<DefinedImportData>(S, N, F);
- else if (!isa<DefinedCOFF>(S->body()))
- reportDuplicate(S, nullptr);
- return S;
+ if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S)) {
+ replaceSymbol<DefinedImportData>(S, N, F);
+ return cast<DefinedImportData>(S);
+ }
+
+ 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()))
- replaceBody<DefinedImportThunk>(S, Name, ID, Machine);
- else if (!isa<DefinedCOFF>(S->body()))
- reportDuplicate(S, nullptr);
- return S;
+ if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S)) {
+ replaceSymbol<DefinedImportThunk>(S, Name, ID, Machine);
+ return cast<DefinedImportThunk>(S);
+ }
+
+ reportDuplicate(S, ID->File);
+ return nullptr;
}
std::vector<Chunk *> SymbolTable::getChunks() {
std::vector<Chunk *> Res;
- for (ObjectFile *File : ObjectFiles) {
- std::vector<Chunk *> &V = File->getChunks();
+ for (ObjFile *File : ObjFile::Instances) {
+ ArrayRef<Chunk *> V = File->getChunks();
Res.insert(Res.end(), V.begin(), V.end());
}
return Res;
}
Symbol *SymbolTable::find(StringRef Name) {
- auto It = Symtab.find(CachedHashStringRef(Name));
- if (It == Symtab.end())
+ auto It = SymMap.find(CachedHashStringRef(Name));
+ if (It == SymMap.end())
return nullptr;
return It->second;
}
@@ -317,7 +324,7 @@ Symbol *SymbolTable::findUnderscore(StringRef Name) {
}
StringRef SymbolTable::findByPrefix(StringRef Prefix) {
- for (auto Pair : Symtab) {
+ for (auto Pair : SymMap) {
StringRef Name = Pair.first.val();
if (Name.startswith(Prefix))
return Name;
@@ -327,47 +334,57 @@ StringRef SymbolTable::findByPrefix(StringRef Prefix) {
StringRef SymbolTable::findMangle(StringRef Name) {
if (Symbol *Sym = find(Name))
- if (!isa<Undefined>(Sym->body()))
+ if (!isa<Undefined>(Sym))
return Name;
if (Config->Machine != I386)
return findByPrefix(("?" + Name + "@@Y").str());
if (!Name.startswith("_"))
return "";
- // Search for x86 C function.
+ // Search for x86 stdcall function.
StringRef S = findByPrefix((Name + "@").str());
if (!S.empty())
return S;
+ // Search for x86 fastcall function.
+ S = findByPrefix(("@" + Name.substr(1) + "@").str());
+ if (!S.empty())
+ return S;
+ // Search for x86 vectorcall function.
+ S = findByPrefix((Name.substr(1) + "@@").str());
+ if (!S.empty())
+ return S;
// Search for x86 C++ non-member function.
return findByPrefix(("?" + Name.substr(1) + "@@Y").str());
}
-void SymbolTable::mangleMaybe(SymbolBody *B) {
+void SymbolTable::mangleMaybe(Symbol *B) {
auto *U = dyn_cast<Undefined>(B);
if (!U || U->WeakAlias)
return;
StringRef Alias = findMangle(U->getName());
- if (!Alias.empty())
+ if (!Alias.empty()) {
+ log(U->getName() + " aliased to " + Alias);
U->WeakAlias = addUndefined(Alias);
+ }
}
-SymbolBody *SymbolTable::addUndefined(StringRef Name) {
- return addUndefined(Name, nullptr, false)->body();
+Symbol *SymbolTable::addUndefined(StringRef Name) {
+ return addUndefined(Name, nullptr, false);
}
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 ea74678c28d8..55481e6475bb 100644
--- a/COFF/SymbolTable.h
+++ b/COFF/SymbolTable.h
@@ -31,8 +31,7 @@ class DefinedAbsolute;
class DefinedRelative;
class Lazy;
class SectionChunk;
-class SymbolBody;
-struct Symbol;
+class Symbol;
// SymbolTable is a bucket of all known symbols, including defined,
// undefined, or lazy symbols (the last one is symbols in archive
@@ -66,7 +65,7 @@ public:
// mangled symbol. This function tries to find a mangled name
// for U from the symbol table, and if found, set the symbol as
// a weak alias for U.
- void mangleMaybe(SymbolBody *B);
+ void mangleMaybe(Symbol *B);
StringRef findMangle(StringRef Name);
// Build a set of COFF objects representing the combined contents of
@@ -75,15 +74,8 @@ 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 *addUndefined(StringRef Name);
Symbol *addSynthetic(StringRef N, Chunk *C);
Symbol *addAbsolute(StringRef N, uint64_t VA);
@@ -91,28 +83,35 @@ public:
Symbol *addUndefined(StringRef Name, InputFile *F, bool IsWeakAlias);
void addLazy(ArchiveFile *F, const Archive::Symbol Sym);
Symbol *addAbsolute(StringRef N, COFFSymbolRef S);
- Symbol *addRegular(InputFile *F, StringRef N, bool IsCOMDAT,
+ Symbol *addRegular(InputFile *F, StringRef N,
const llvm::object::coff_symbol_generic *S = nullptr,
SectionChunk *C = nullptr);
+ std::pair<Symbol *, bool>
+ addComdat(InputFile *F, StringRef N,
+ const llvm::object::coff_symbol_generic *S = nullptr);
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 : SymMap)
+ 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;
+ llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> SymMap;
std::unique_ptr<BitcodeCompiler> LTO;
};
diff --git a/COFF/Symbols.cpp b/COFF/Symbols.cpp
index 9b59079072a8..4c5ab48c7565 100644
--- a/COFF/Symbols.cpp
+++ b/COFF/Symbols.cpp
@@ -8,10 +8,10 @@
//===----------------------------------------------------------------------===//
#include "Symbols.h"
-#include "Error.h"
#include "InputFiles.h"
-#include "Memory.h"
#include "Strings.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
@@ -20,8 +20,8 @@ using namespace llvm;
using namespace llvm::object;
// Returns a symbol name for an error message.
-std::string lld::toString(coff::SymbolBody &B) {
- if (Optional<std::string> S = coff::demangle(B.getName()))
+std::string lld::toString(coff::Symbol &B) {
+ if (Optional<std::string> S = coff::demangleMSVC(B.getName()))
return ("\"" + *S + "\" (" + B.getName() + ")").str();
return B.getName();
}
@@ -29,7 +29,7 @@ std::string lld::toString(coff::SymbolBody &B) {
namespace lld {
namespace coff {
-StringRef SymbolBody::getName() {
+StringRef Symbol::getName() {
// COFF symbol names are read lazily for a performance reason.
// Non-external symbol names are never used by the linker except for logging
// or debugging. Their internal references are resolved not by name but by
@@ -39,12 +39,12 @@ 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;
}
-InputFile *SymbolBody::getFile() {
+InputFile *Symbol::getFile() {
if (auto *Sym = dyn_cast<DefinedCOFF>(this))
return Sym->File;
if (auto *Sym = dyn_cast<Lazy>(this))
@@ -52,9 +52,19 @@ InputFile *SymbolBody::getFile() {
return nullptr;
}
+bool Symbol::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));
@@ -81,7 +91,7 @@ DefinedImportThunk::DefinedImportThunk(StringRef Name, DefinedImportData *S,
Defined *Undefined::getWeakAlias() {
// A weak alias may be a weak alias to another symbol, so check recursively.
- for (SymbolBody *A = WeakAlias; A; A = cast<Undefined>(A)->WeakAlias)
+ for (Symbol *A = WeakAlias; A; A = cast<Undefined>(A)->WeakAlias)
if (auto *D = dyn_cast<Defined>(A))
return D;
return nullptr;
diff --git a/COFF/Symbols.h b/COFF/Symbols.h
index a12ae1c01e07..d8a030705e27 100644
--- a/COFF/Symbols.h
+++ b/COFF/Symbols.h
@@ -12,8 +12,8 @@
#include "Chunks.h"
#include "Config.h"
-#include "Memory.h"
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
+#include "lld/Common/Memory.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/COFF.h"
@@ -31,12 +31,11 @@ using llvm::object::coff_symbol_generic;
class ArchiveFile;
class InputFile;
-class ObjectFile;
-struct Symbol;
+class ObjFile;
class SymbolTable;
// The base class for real symbol classes.
-class SymbolBody {
+class Symbol {
public:
enum Kind {
// The order of these is significant. We start with the regular defined
@@ -70,16 +69,16 @@ public:
// Returns the file from which this symbol was created.
InputFile *getFile();
- Symbol *symbol();
- const Symbol *symbol() const {
- return const_cast<SymbolBody *>(this)->symbol();
- }
+ // Indicates that this symbol will be included in the final image. Only valid
+ // after calling markLive.
+ bool isLive() const;
protected:
friend SymbolTable;
- explicit SymbolBody(Kind K, StringRef N = "")
+ explicit Symbol(Kind K, StringRef N = "")
: SymbolKind(K), IsExternal(true), IsCOMDAT(false),
- WrittenToSymtab(false), Name(N) {}
+ WrittenToSymtab(false), PendingArchiveLoad(false), IsGCRoot(false),
+ Name(N) {}
const unsigned SymbolKind : 8;
unsigned IsExternal : 1;
@@ -92,19 +91,28 @@ public:
// symbols from being written to the symbol table more than once.
unsigned WrittenToSymtab : 1;
+ // True if this symbol was referenced by a regular (non-bitcode) object.
+ unsigned IsUsedInRegularObj : 1;
+
+ // True if we've seen both a lazy and an undefined symbol with this symbol
+ // name, which means that we have enqueued an archive member load and should
+ // not load any more archive members to resolve the same symbol.
+ unsigned PendingArchiveLoad : 1;
+
+ /// True if we've already added this symbol to the list of GC roots.
+ unsigned IsGCRoot : 1;
+
protected:
StringRef Name;
};
// The base class for any defined symbols, including absolute symbols,
// etc.
-class Defined : public SymbolBody {
+class Defined : public Symbol {
public:
- Defined(Kind K, StringRef N) : SymbolBody(K, N) {}
+ Defined(Kind K, StringRef N) : Symbol(K, N) {}
- static bool classof(const SymbolBody *S) {
- return S->kind() <= LastDefinedKind;
- }
+ static bool classof(const Symbol *S) { return S->kind() <= LastDefinedKind; }
// Returns the RVA (relative virtual address) of this symbol. The
// writer sets and uses RVAs.
@@ -120,12 +128,13 @@ public:
// loaded through that. For bitcode files, Sym is nullptr and the name is stored
// as a StringRef.
class DefinedCOFF : public Defined {
- friend SymbolBody;
+ friend Symbol;
+
public:
DefinedCOFF(Kind K, InputFile *F, StringRef N, const coff_symbol_generic *S)
: Defined(K, N), File(F), Sym(S) {}
- static bool classof(const SymbolBody *S) {
+ static bool classof(const Symbol *S) {
return S->kind() <= LastDefinedCOFFKind;
}
@@ -151,16 +160,15 @@ public:
this->IsCOMDAT = IsCOMDAT;
}
- static bool classof(const SymbolBody *S) {
+ static bool classof(const Symbol *S) {
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;
};
@@ -173,12 +181,12 @@ public:
this->IsExternal = true;
}
- static bool classof(const SymbolBody *S) {
+ static bool classof(const Symbol *S) {
return S->kind() == DefinedCommonKind;
}
uint64_t getRVA() { return Data->getRVA(); }
- Chunk *getChunk() { return Data; }
+ CommonChunk *getChunk() { return Data; }
private:
friend SymbolTable;
@@ -198,7 +206,7 @@ public:
DefinedAbsolute(StringRef N, uint64_t V)
: Defined(DefinedAbsoluteKind, N), VA(V) {}
- static bool classof(const SymbolBody *S) {
+ static bool classof(const Symbol *S) {
return S->kind() == DefinedAbsoluteKind;
}
@@ -222,7 +230,7 @@ public:
explicit DefinedSynthetic(StringRef Name, Chunk *C)
: Defined(DefinedSyntheticKind, Name), C(C) {}
- static bool classof(const SymbolBody *S) {
+ static bool classof(const Symbol *S) {
return S->kind() == DefinedSyntheticKind;
}
@@ -240,12 +248,12 @@ private:
// object file from an archive to replace itself with a defined
// symbol. If the resolver finds both Undefined and Lazy for
// the same name, it will ask the Lazy to load a file.
-class Lazy : public SymbolBody {
+class Lazy : public Symbol {
public:
Lazy(ArchiveFile *F, const Archive::Symbol S)
- : SymbolBody(LazyKind, S.getName()), File(F), Sym(S) {}
+ : Symbol(LazyKind, S.getName()), File(F), Sym(S) {}
- static bool classof(const SymbolBody *S) { return S->kind() == LazyKind; }
+ static bool classof(const Symbol *S) { return S->kind() == LazyKind; }
ArchiveFile *File;
@@ -257,19 +265,17 @@ private:
};
// Undefined symbols.
-class Undefined : public SymbolBody {
+class Undefined : public Symbol {
public:
- explicit Undefined(StringRef N) : SymbolBody(UndefinedKind, N) {}
+ explicit Undefined(StringRef N) : Symbol(UndefinedKind, N) {}
- static bool classof(const SymbolBody *S) {
- return S->kind() == UndefinedKind;
- }
+ static bool classof(const Symbol *S) { return S->kind() == UndefinedKind; }
// An undefined symbol can have a fallback symbol which gives an
// undefined symbol a second chance if it would remain undefined.
// If it remains undefined, it'll be replaced with whatever the
// Alias pointer points to.
- SymbolBody *WeakAlias = nullptr;
+ Symbol *WeakAlias = nullptr;
// If this symbol is external weak, try to resolve it to a defined
// symbol by searching the chain of fallback symbols. Returns the symbol if
@@ -289,7 +295,7 @@ public:
: Defined(DefinedImportDataKind, N), File(F) {
}
- static bool classof(const SymbolBody *S) {
+ static bool classof(const Symbol *S) {
return S->kind() == DefinedImportDataKind;
}
@@ -313,7 +319,7 @@ class DefinedImportThunk : public Defined {
public:
DefinedImportThunk(StringRef Name, DefinedImportData *S, uint16_t Machine);
- static bool classof(const SymbolBody *S) {
+ static bool classof(const Symbol *S) {
return S->kind() == DefinedImportThunkKind;
}
@@ -336,7 +342,7 @@ public:
DefinedLocalImport(StringRef N, Defined *S)
: Defined(DefinedLocalImportKind, N), Data(make<LocalImportChunk>(S)) {}
- static bool classof(const SymbolBody *S) {
+ static bool classof(const Symbol *S) {
return S->kind() == DefinedLocalImportKind;
}
@@ -393,51 +399,33 @@ inline Chunk *Defined::getChunk() {
llvm_unreachable("unknown symbol kind");
}
-// A real symbol object, SymbolBody, is usually stored within a Symbol. There's
-// always one Symbol for each symbol name. The resolver updates the SymbolBody
-// stored in the Body field of this object as it resolves symbols. Symbol also
-// holds computed properties of symbol names.
-struct Symbol {
- // True if this symbol was referenced by a regular (non-bitcode) object.
- unsigned IsUsedInRegularObj : 1;
-
- // True if we've seen both a lazy and an undefined symbol with this symbol
- // name, which means that we have enqueued an archive member load and should
- // not load any more archive members to resolve the same symbol.
- unsigned PendingArchiveLoad : 1;
-
- // This field is used to store the Symbol's SymbolBody. This instantiation of
- // AlignedCharArrayUnion gives us a struct with a char array field that is
- // large and aligned enough to store any derived class of SymbolBody.
- llvm::AlignedCharArrayUnion<
- DefinedRegular, DefinedCommon, DefinedAbsolute, DefinedSynthetic, Lazy,
- Undefined, DefinedImportData, DefinedImportThunk, DefinedLocalImport>
- Body;
-
- SymbolBody *body() {
- return reinterpret_cast<SymbolBody *>(Body.buffer);
- }
- const SymbolBody *body() const { return const_cast<Symbol *>(this)->body(); }
+// A buffer class that is large enough to hold any Symbol-derived
+// object. We allocate memory using this class and instantiate a symbol
+// using the placement new.
+union SymbolUnion {
+ alignas(DefinedRegular) char A[sizeof(DefinedRegular)];
+ alignas(DefinedCommon) char B[sizeof(DefinedCommon)];
+ alignas(DefinedAbsolute) char C[sizeof(DefinedAbsolute)];
+ alignas(DefinedSynthetic) char D[sizeof(DefinedSynthetic)];
+ alignas(Lazy) char E[sizeof(Lazy)];
+ alignas(Undefined) char F[sizeof(Undefined)];
+ alignas(DefinedImportData) char G[sizeof(DefinedImportData)];
+ alignas(DefinedImportThunk) char H[sizeof(DefinedImportThunk)];
+ alignas(DefinedLocalImport) char I[sizeof(DefinedLocalImport)];
};
template <typename T, typename... ArgT>
-void replaceBody(Symbol *S, ArgT &&... Arg) {
- static_assert(sizeof(T) <= sizeof(S->Body), "Body too small");
- static_assert(alignof(T) <= alignof(decltype(S->Body)),
- "Body not aligned enough");
- assert(static_cast<SymbolBody *>(static_cast<T *>(nullptr)) == nullptr &&
- "Not a SymbolBody");
- new (S->Body.buffer) T(std::forward<ArgT>(Arg)...);
-}
-
-inline Symbol *SymbolBody::symbol() {
- assert(isExternal());
- return reinterpret_cast<Symbol *>(reinterpret_cast<char *>(this) -
- offsetof(Symbol, Body));
+void replaceSymbol(Symbol *S, ArgT &&... Arg) {
+ static_assert(sizeof(T) <= sizeof(SymbolUnion), "Symbol too small");
+ static_assert(alignof(T) <= alignof(SymbolUnion),
+ "SymbolUnion not aligned enough");
+ assert(static_cast<Symbol *>(static_cast<T *>(nullptr)) == nullptr &&
+ "Not a Symbol");
+ new (S) T(std::forward<ArgT>(Arg)...);
}
} // namespace coff
-std::string toString(coff::SymbolBody &B);
+std::string toString(coff::Symbol &B);
} // namespace lld
#endif
diff --git a/COFF/Writer.cpp b/COFF/Writer.cpp
index a6a5e278498a..584f0621bea3 100644
--- a/COFF/Writer.cpp
+++ b/COFF/Writer.cpp
@@ -10,22 +10,22 @@
#include "Writer.h"
#include "Config.h"
#include "DLL.h"
-#include "Error.h"
#include "InputFiles.h"
#include "MapFile.h"
-#include "Memory.h"
#include "PDB.h"
#include "SymbolTable.h"
#include "Symbols.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
#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,37 @@ 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) {}
+ Writer() : Buffer(errorHandler().OutputBuffer) {}
void run();
private:
@@ -115,11 +121,11 @@ private:
void createSymbolAndStringTable();
void openFile(StringRef OutputPath);
template <typename PEHeaderTy> void writeHeader();
- void fixSafeSEHSymbols();
+ void createSEHTable(OutputSection *RData);
void setSectionPermissions();
void writeSections();
- void sortExceptionTable();
void writeBuildId();
+ void sortExceptionTable();
llvm::Optional<coff_symbol16> createSymbol(Defined *D);
size_t addEntryToStringTable(StringRef Str);
@@ -132,8 +138,7 @@ private:
uint32_t getSizeOfInitializedData();
std::map<StringRef, std::vector<DefinedImportData *>> binImports();
- SymbolTable *Symtab;
- std::unique_ptr<FileOutputBuffer> Buffer;
+ std::unique_ptr<FileOutputBuffer> &Buffer;
std::vector<OutputSection *> OutputSections;
std::vector<char> Strtab;
std::vector<llvm::object::coff_symbol16> OutputSymtab;
@@ -145,6 +150,7 @@ private:
Chunk *DebugDirectory = nullptr;
std::vector<Chunk *> DebugRecords;
CVDebugRecordChunk *BuildId = nullptr;
+ Optional<codeview::DebugInfo> PreviousBuildId;
ArrayRef<uint8_t> SectionTable;
uint64_t FileSize;
@@ -157,7 +163,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;
@@ -178,10 +184,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->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);
@@ -203,7 +211,8 @@ void OutputSection::writeHeaderTo(uint8_t *Buf) {
// If name is too long, write offset into the string table as a name.
sprintf(Hdr->Name, "/%d", StringTableOff);
} else {
- assert(!Config->Debug || Name.size() <= COFF::NameSize);
+ assert(!Config->Debug || Name.size() <= COFF::NameSize ||
+ (Hdr->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0);
strncpy(Hdr->Name, Name.data(),
std::min(Name.size(), (size_t)COFF::NameSize));
}
@@ -212,6 +221,67 @@ void OutputSection::writeHeaderTo(uint8_t *Buf) {
} // namespace coff
} // namespace lld
+// 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;
+
+ auto ExpectedBinary = llvm::object::createBinary(Path);
+ if (!ExpectedBinary) {
+ consumeError(ExpectedBinary.takeError());
+ return None;
+ }
+
+ 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() {
createSections();
@@ -224,32 +294,39 @@ 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>();
} else {
writeHeader<pe32_header>();
}
- fixSafeSEHSymbols();
writeSections();
sortExceptionTable();
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(Symtab, SectionTable, DI);
+
+ assert(BuildId);
+ createPDB(Symtab, OutputSections, SectionTable, *BuildId->BuildId);
}
writeMapFile(OutputSections);
- if (auto EC = Buffer->commit())
- fatal(EC, "failed to write the output file");
+ if (auto E = Buffer->commit())
+ fatal("failed to write the output file: " + toString(std::move(E)));
}
static StringRef getOutputSection(StringRef Name) {
StringRef S = Name.split('$').first;
+
+ // Treat a later period as a separator for MinGW, for sections like
+ // ".ctors.01234".
+ S = S.substr(0, S.find('.', 1));
+
auto It = Config->Merge.find(S);
if (It == Config->Merge.end())
return S;
@@ -303,41 +380,20 @@ 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)
RData->addChunk(C);
}
- // Create SEH table. x86-only.
- if (Config->Machine != I386)
- return;
-
- std::set<Defined *> Handlers;
-
- for (lld::coff::ObjectFile *File : Symtab->ObjectFiles) {
- if (!File->SEHCompat)
- return;
- for (SymbolBody *B : File->SEHandlers) {
- // 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);
- }
- }
-
- if (!Handlers.empty()) {
- SEHTable = make<SEHTableChunk>(Handlers);
- RData->addChunk(SEHTable);
- }
+ createSEHTable(RData);
}
// Create .idata section for the DLL-imported symbol table.
@@ -345,13 +401,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;
@@ -361,7 +417,7 @@ void Writer::createImportTables() {
}
OutputSection *Text = createSection(".text");
- for (ImportFile *File : Symtab->ImportFiles) {
+ for (ImportFile *File : ImportFile::Instances) {
if (!File->Live)
continue;
@@ -432,19 +488,12 @@ Optional<coff_symbol16> Writer::createSymbol(Defined *Def) {
if (isa<DefinedSynthetic>(Def))
return None;
- if (auto *D = dyn_cast<DefinedRegular>(Def)) {
- // Don't write dead symbols or symbols in codeview sections to the symbol
- // table.
- if (!D->getChunk()->isLive() || D->getChunk()->isCodeView())
- 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)
+ // 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()->isCodeView())
return None;
coff_symbol16 Sym;
@@ -468,7 +517,7 @@ Optional<coff_symbol16> Writer::createSymbol(Defined *Def) {
Sym.NumberOfAuxSymbols = 0;
switch (Def->kind()) {
- case SymbolBody::DefinedAbsoluteKind:
+ case Symbol::DefinedAbsoluteKind:
Sym.Value = Def->getRVA();
Sym.SectionNumber = IMAGE_SYM_ABSOLUTE;
break;
@@ -489,40 +538,46 @@ Optional<coff_symbol16> Writer::createSymbol(Defined *Def) {
}
void Writer::createSymbolAndStringTable() {
- if (!Config->Debug || !Config->WriteSymtab)
- return;
-
// Name field in the section table is 8 byte long. Longer names need
// to be written to the string table. First, construct string table.
for (OutputSection *Sec : OutputSections) {
StringRef Name = Sec->getName();
if (Name.size() <= COFF::NameSize)
continue;
+ // If a section isn't discardable (i.e. will be mapped at runtime),
+ // prefer a truncated section name over a long section name in
+ // the string table that is unavailable at runtime. This is different from
+ // what link.exe does, but finding ".eh_fram" instead of "/4" is useful
+ // to libunwind.
+ if ((Sec->getPermissions() & IMAGE_SCN_MEM_DISCARDABLE) == 0)
+ continue;
Sec->setStringTableOff(addEntryToStringTable(Name));
}
- for (lld::coff::ObjectFile *File : Symtab->ObjectFiles) {
- for (SymbolBody *B : File->getSymbols()) {
- auto *D = dyn_cast<Defined>(B);
- if (!D || D->WrittenToSymtab)
- continue;
- D->WrittenToSymtab = true;
+ if (Config->DebugDwarf) {
+ for (ObjFile *File : ObjFile::Instances) {
+ for (Symbol *B : File->getSymbols()) {
+ auto *D = dyn_cast_or_null<Defined>(B);
+ if (!D || D->WrittenToSymtab)
+ continue;
+ D->WrittenToSymtab = true;
- if (Optional<coff_symbol16> Sym = createSymbol(D))
- OutputSymtab.push_back(*Sym);
+ if (Optional<coff_symbol16> Sym = createSymbol(D))
+ OutputSymtab.push_back(*Sym);
+ }
}
}
+ if (OutputSymtab.empty() && Strtab.empty())
+ return;
+
OutputSection *LastSection = OutputSections.back();
// We position the symbol table to be adjacent to the end of the last section.
uint64_t FileOff = LastSection->getFileOff() +
alignTo(LastSection->getRawSize(), SectorSize);
- if (!OutputSymtab.empty()) {
- PointerToSymbolTable = FileOff;
- FileOff += OutputSymtab.size() * sizeof(coff_symbol16);
- }
- if (!Strtab.empty())
- FileOff += Strtab.size() + 4;
+ PointerToSymbolTable = FileOff;
+ FileOff += OutputSymtab.size() * sizeof(coff_symbol16);
+ FileOff += 4 + Strtab.size();
FileSize = alignTo(FileOff, SectorSize);
}
@@ -551,7 +606,7 @@ void Writer::assignAddresses() {
RVA += alignTo(Sec->getVirtualSize(), PageSize);
FileSize += alignTo(Sec->getRawSize(), SectorSize);
}
- SizeOfImage = SizeOfHeaders + alignTo(RVA - 0x1000, PageSize);
+ SizeOfImage = alignTo(RVA, PageSize);
}
template <typename PEHeaderTy> void Writer::writeHeader() {
@@ -621,23 +676,21 @@ 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)
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION;
+ if (Config->Machine == I386 && !SEHTable &&
+ !Symtab->findUnderscore("_load_config_used"))
+ PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_SEH;
if (Config->TerminalServerAware)
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE;
PE->NumberOfRvaAndSize = NumberfOfDataDirectory;
@@ -673,7 +726,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
Dir[BASE_RELOCATION_TABLE].Size = Sec->getVirtualSize();
}
if (Symbol *Sym = Symtab->findUnderscore("_tls_used")) {
- if (Defined *B = dyn_cast<Defined>(Sym->body())) {
+ if (Defined *B = dyn_cast<Defined>(Sym)) {
Dir[TLS_TABLE].RelativeVirtualAddress = B->getRVA();
Dir[TLS_TABLE].Size = Config->is64()
? sizeof(object::coff_tls_directory64)
@@ -685,7 +738,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
Dir[DEBUG_DIRECTORY].Size = DebugDirectory->getSize();
}
if (Symbol *Sym = Symtab->findUnderscore("_load_config_used")) {
- if (auto *B = dyn_cast<DefinedRegular>(Sym->body())) {
+ if (auto *B = dyn_cast<DefinedRegular>(Sym)) {
SectionChunk *SC = B->getChunk();
assert(B->getRVA() >= SC->getRVA());
uint64_t OffsetInChunk = B->getRVA() - SC->getRVA();
@@ -715,7 +768,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
SectionTable = ArrayRef<uint8_t>(
Buf - OutputSections.size() * sizeof(coff_section), Buf);
- if (OutputSymtab.empty())
+ if (OutputSymtab.empty() && Strtab.empty())
return;
COFF->PointerToSymbolTable = PointerToSymbolTable;
@@ -734,21 +787,40 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
}
void Writer::openFile(StringRef Path) {
- Buffer = check(
+ Buffer = CHECK(
FileOutputBuffer::create(Path, FileSize, FileOutputBuffer::F_executable),
"failed to open " + Path);
}
-void Writer::fixSafeSEHSymbols() {
- if (!SEHTable)
+void Writer::createSEHTable(OutputSection *RData) {
+ // Create SEH table. x86-only.
+ if (Config->Machine != I386)
+ return;
+
+ std::set<Defined *> Handlers;
+
+ for (ObjFile *File : ObjFile::Instances) {
+ if (!File->SEHCompat)
+ return;
+ for (uint32_t I : File->SXData)
+ if (Symbol *B = File->getSymbol(I))
+ if (B->isLive())
+ Handlers.insert(cast<Defined>(B));
+ }
+
+ if (Handlers.empty())
return;
+
+ SEHTable = make<SEHTableChunk>(Handlers);
+ RData->addChunk(SEHTable);
+
// 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);
+ replaceSymbol<DefinedSynthetic>(T, T->getName(), SEHTable);
+ cast<DefinedAbsolute>(C)->setVA(SEHTable->getSize() / 4);
}
// Handles /section options to allow users to overwrite
@@ -781,6 +853,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");
@@ -795,7 +886,7 @@ void Writer::sortExceptionTable() {
[](const Entry &A, const Entry &B) { return A.Begin < B.Begin; });
return;
}
- if (Config->Machine == ARMNT) {
+ if (Config->Machine == ARMNT || Config->Machine == ARM64) {
struct Entry { ulittle32_t Begin, Unwind; };
sort(parallel::par, (Entry *)Begin, (Entry *)End,
[](const Entry &A, const Entry &B) { return A.Begin < B.Begin; });
@@ -804,26 +895,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 fef575423878..21be1be6e92a 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.
@@ -36,7 +34,7 @@ public:
void setFileOffset(uint64_t);
void addChunk(Chunk *C);
llvm::StringRef getName() { return Name; }
- std::vector<Chunk *> &getChunks() { return Chunks; }
+ ArrayRef<Chunk *> getChunks() { return Chunks; }
void addPermissions(uint32_t C);
void setPermissions(uint32_t C);
uint32_t getPermissions() { return Header.Characteristics & PermMask; }
diff --git a/Common/Args.cpp b/Common/Args.cpp
new file mode 100644
index 000000000000..680cf5bd0a6e
--- /dev/null
+++ b/Common/Args.cpp
@@ -0,0 +1,62 @@
+//===- Args.cpp -----------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lld/Common/Args.h"
+#include "lld/Common/ErrorHandler.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Option/ArgList.h"
+
+using namespace llvm;
+using namespace lld;
+
+int lld::args::getInteger(opt::InputArgList &Args, unsigned Key, int Default) {
+ int V = 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 + "'");
+ }
+ return V;
+}
+
+std::vector<StringRef> lld::args::getStrings(opt::InputArgList &Args, int Id) {
+ std::vector<StringRef> V;
+ for (auto *Arg : Args.filtered(Id))
+ V.push_back(Arg->getValue());
+ return V;
+}
+
+uint64_t lld::args::getZOptionValue(opt::InputArgList &Args, int Id,
+ StringRef Key, uint64_t Default) {
+ for (auto *Arg : Args.filtered(Id)) {
+ std::pair<StringRef, StringRef> KV = StringRef(Arg->getValue()).split('=');
+ if (KV.first == Key) {
+ uint64_t Result = Default;
+ if (!to_integer(KV.second, Result))
+ error("invalid " + Key + ": " + KV.second);
+ return Result;
+ }
+ }
+ return Default;
+}
+
+std::vector<StringRef> lld::args::getLines(MemoryBufferRef MB) {
+ SmallVector<StringRef, 0> Arr;
+ MB.getBuffer().split(Arr, '\n');
+
+ std::vector<StringRef> Ret;
+ for (StringRef S : Arr) {
+ S = S.trim();
+ if (!S.empty() && S[0] != '#')
+ Ret.push_back(S);
+ }
+ return Ret;
+}
diff --git a/Common/CMakeLists.txt b/Common/CMakeLists.txt
new file mode 100644
index 000000000000..b376893f35a4
--- /dev/null
+++ b/Common/CMakeLists.txt
@@ -0,0 +1,32 @@
+if(NOT LLD_BUILT_STANDALONE)
+ set(tablegen_deps intrinsics_gen)
+endif()
+
+add_lld_library(lldCommon
+ Args.cpp
+ ErrorHandler.cpp
+ Memory.cpp
+ Reproduce.cpp
+ Strings.cpp
+ TargetOptionsCommandFlags.cpp
+ Threads.cpp
+ Version.cpp
+
+ ADDITIONAL_HEADER_DIRS
+ ${LLD_INCLUDE_DIR}/lld/Common
+
+ LINK_COMPONENTS
+ Codegen
+ Core
+ Demangle
+ MC
+ Option
+ Support
+ Target
+
+ LINK_LIBS
+ ${LLVM_PTHREAD_LIB}
+
+ DEPENDS
+ ${tablegen_deps}
+ )
diff --git a/ELF/Error.cpp b/Common/ErrorHandler.cpp
index 224570ea7424..18affce4d5a6 100644
--- a/ELF/Error.cpp
+++ b/Common/ErrorHandler.cpp
@@ -1,4 +1,4 @@
-//===- Error.cpp ----------------------------------------------------------===//
+//===- ErrorHandler.cpp ---------------------------------------------------===//
//
// The LLVM Linker
//
@@ -7,8 +7,9 @@
//
//===----------------------------------------------------------------------===//
-#include "Error.h"
-#include "Config.h"
+#include "lld/Common/ErrorHandler.h"
+
+#include "lld/Common/Threads.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Error.h"
@@ -21,12 +22,7 @@
#endif
using namespace llvm;
-
using namespace lld;
-using namespace lld::elf;
-
-uint64_t elf::ErrorCount;
-raw_ostream *elf::ErrorOS;
// 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.
@@ -34,19 +30,38 @@ static std::mutex Mu;
// Prints "\n" or does nothing, depending on Msg contents of
// the previous call of this function.
-static void newline(const Twine &Msg) {
+static void newline(raw_ostream *ErrorOS, const Twine &Msg) {
// True if the previous error message contained "\n".
// We want to separate multi-line error messages with a newline.
static bool Flag;
if (Flag)
*ErrorOS << "\n";
- Flag = (StringRef(Msg.str()).find('\n') != StringRef::npos);
+ Flag = StringRef(Msg.str()).contains('\n');
+}
+
+ErrorHandler &lld::errorHandler() {
+ static ErrorHandler Handler;
+ return Handler;
+}
+
+void lld::exitLld(int Val) {
+ // Delete the output buffer so that any tempory file is deleted.
+ errorHandler().OutputBuffer.reset();
+
+ // Dealloc/destroy ManagedStatic variables before calling
+ // _exit(). In a non-LTO build, this is a nop. In an LTO
+ // build allows us to get the output of -time-passes.
+ llvm_shutdown();
+
+ outs().flush();
+ errs().flush();
+ _exit(Val);
}
-static void print(StringRef S, raw_ostream::Colors C) {
- *ErrorOS << Config->Argv[0] << ": ";
- if (Config->ColorDiagnostics) {
+void ErrorHandler::print(StringRef S, raw_ostream::Colors C) {
+ *ErrorOS << LogName << ": ";
+ if (ColorDiagnostics) {
ErrorOS->changeColor(C, true);
*ErrorOS << S;
ErrorOS->resetColor();
@@ -55,62 +70,49 @@ static void print(StringRef S, raw_ostream::Colors C) {
}
}
-void elf::log(const Twine &Msg) {
- if (Config->Verbose) {
+void ErrorHandler::log(const Twine &Msg) {
+ if (Verbose) {
std::lock_guard<std::mutex> Lock(Mu);
- outs() << Config->Argv[0] << ": " << Msg << "\n";
- outs().flush();
+ *ErrorOS << LogName << ": " << Msg << "\n";
}
}
-void elf::message(const Twine &Msg) {
+void ErrorHandler::message(const Twine &Msg) {
std::lock_guard<std::mutex> Lock(Mu);
outs() << Msg << "\n";
outs().flush();
}
-void elf::warn(const Twine &Msg) {
- if (Config->FatalWarnings) {
+void ErrorHandler::warn(const Twine &Msg) {
+ if (FatalWarnings) {
error(Msg);
return;
}
std::lock_guard<std::mutex> Lock(Mu);
- newline(Msg);
+ newline(ErrorOS, Msg);
print("warning: ", raw_ostream::MAGENTA);
*ErrorOS << Msg << "\n";
}
-void elf::error(const Twine &Msg) {
+void ErrorHandler::error(const Twine &Msg) {
std::lock_guard<std::mutex> Lock(Mu);
- newline(Msg);
+ newline(ErrorOS, Msg);
- if (Config->ErrorLimit == 0 || ErrorCount < Config->ErrorLimit) {
+ if (ErrorLimit == 0 || ErrorCount < ErrorLimit) {
print("error: ", raw_ostream::RED);
*ErrorOS << Msg << "\n";
- } else if (ErrorCount == Config->ErrorLimit) {
+ } else if (ErrorCount == ErrorLimit) {
print("error: ", raw_ostream::RED);
- *ErrorOS << "too many errors emitted, stopping now"
- << " (use -error-limit=0 to see all errors)\n";
- if (Config->ExitEarly)
+ *ErrorOS << ErrorLimitExceededMsg << "\n";
+ if (ExitEarly)
exitLld(1);
}
++ErrorCount;
}
-void elf::exitLld(int Val) {
- // Dealloc/destroy ManagedStatic variables before calling
- // _exit(). In a non-LTO build, this is a nop. In an LTO
- // build allows us to get the output of -time-passes.
- llvm_shutdown();
-
- outs().flush();
- errs().flush();
- _exit(Val);
-}
-
-void elf::fatal(const Twine &Msg) {
+void ErrorHandler::fatal(const Twine &Msg) {
error(Msg);
exitLld(1);
}
diff --git a/Common/Memory.cpp b/Common/Memory.cpp
new file mode 100644
index 000000000000..efc5bcc2218b
--- /dev/null
+++ b/Common/Memory.cpp
@@ -0,0 +1,23 @@
+//===- Memory.cpp ---------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lld/Common/Memory.h"
+
+using namespace llvm;
+using namespace lld;
+
+BumpPtrAllocator lld::BAlloc;
+StringSaver lld::Saver{BAlloc};
+std::vector<SpecificAllocBase *> lld::SpecificAllocBase::Instances;
+
+void lld::freeArena() {
+ for (SpecificAllocBase *Alloc : SpecificAllocBase::Instances)
+ Alloc->reset();
+ BAlloc.Reset();
+}
diff --git a/lib/Core/Reproduce.cpp b/Common/Reproduce.cpp
index e3629a93cbe3..7be4ea6bb98b 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) {
@@ -55,12 +55,12 @@ std::string lld::rewritePath(StringRef S) {
return S;
}
-std::string lld::toString(opt::Arg *Arg) {
- std::string K = Arg->getSpelling();
- if (Arg->getNumValues() == 0)
+std::string lld::toString(const opt::Arg &Arg) {
+ std::string K = Arg.getSpelling();
+ if (Arg.getNumValues() == 0)
return K;
- std::string V = quote(Arg->getValue());
- if (Arg->getOption().getRenderStyle() == opt::Option::RenderJoinedStyle)
+ std::string V = quote(Arg.getValue());
+ if (Arg.getOption().getRenderStyle() == opt::Option::RenderJoinedStyle)
return K + V;
return K + " " + V;
}
diff --git a/Common/Strings.cpp b/Common/Strings.cpp
new file mode 100644
index 000000000000..6cd4ad8d600a
--- /dev/null
+++ b/Common/Strings.cpp
@@ -0,0 +1,32 @@
+//===- Strings.cpp -------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lld/Common/Strings.h"
+#include "llvm/Demangle/Demangle.h"
+
+using namespace llvm;
+using namespace lld;
+
+// Returns the demangled C++ symbol name for Name.
+Optional<std::string> lld::demangleItanium(StringRef Name) {
+ // itaniumDemangle can be used to demangle strings other than symbol
+ // names which do not necessarily start with "_Z". Name can be
+ // either a C or C++ symbol. Don't call itaniumDemangle if the name
+ // does not look like a C++ symbol name to avoid getting unexpected
+ // result for a C symbol that happens to match a mangled type name.
+ if (!Name.startswith("_Z"))
+ return None;
+
+ char *Buf = itaniumDemangle(Name.str().c_str(), nullptr, nullptr, nullptr);
+ if (!Buf)
+ return None;
+ std::string S(Buf);
+ free(Buf);
+ return S;
+}
diff --git a/lib/Core/TargetOptionsCommandFlags.cpp b/Common/TargetOptionsCommandFlags.cpp
index e0f26761e705..e8e582f4c256 100644
--- a/lib/Core/TargetOptionsCommandFlags.cpp
+++ b/Common/TargetOptionsCommandFlags.cpp
@@ -8,25 +8,25 @@
//===----------------------------------------------------------------------===//
//
// This file exists as a place for global variables defined in LLVM's
-// CodeGen/CommandFlags.h. By putting the resulting object file in
+// CodeGen/CommandFlags.def. By putting the resulting object file in
// an archive and linking with it, the definitions will automatically be
// included when needed and skipped when already present.
//
//===----------------------------------------------------------------------===//
-#include "lld/Core/TargetOptionsCommandFlags.h"
+#include "lld/Common/TargetOptionsCommandFlags.h"
-#include "llvm/CodeGen/CommandFlags.h"
+#include "llvm/CodeGen/CommandFlags.def"
#include "llvm/Target/TargetOptions.h"
// Define an externally visible version of
// InitTargetOptionsFromCodeGenFlags, so that its functionality can be
-// used without having to include llvm/CodeGen/CommandFlags.h, which
+// used without having to include llvm/CodeGen/CommandFlags.def, which
// would lead to multiple definitions of the command line flags.
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 000000000000..c64b8c38b909
--- /dev/null
+++ b/Common/Threads.cpp
@@ -0,0 +1,12 @@
+//===- 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"
+
+bool lld::ThreadsEnabled = true;
diff --git a/lib/Config/Version.cpp b/Common/Version.cpp
index 25544756f8be..6226c9a2fac6 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/AArch64ErrataFix.cpp b/ELF/AArch64ErrataFix.cpp
new file mode 100644
index 000000000000..6cc68cc08e10
--- /dev/null
+++ b/ELF/AArch64ErrataFix.cpp
@@ -0,0 +1,648 @@
+//===- AArch64ErrataFix.cpp -----------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// This file implements Section Patching for the purpose of working around
+// errata in CPUs. The general principle is that an erratum sequence of one or
+// more instructions is detected in the instruction stream, one of the
+// instructions in the sequence is replaced with a branch to a patch sequence
+// of replacement instructions. At the end of the replacement sequence the
+// patch branches back to the instruction stream.
+
+// This technique is only suitable for fixing an erratum when:
+// - There is a set of necessary conditions required to trigger the erratum that
+// can be detected at static link time.
+// - There is a set of replacement instructions that can be used to remove at
+// least one of the necessary conditions that trigger the erratum.
+// - We can overwrite an instruction in the erratum sequence with a branch to
+// the replacement sequence.
+// - We can place the replacement sequence within range of the branch.
+
+// FIXME:
+// - The implementation here only supports one patch, the AArch64 Cortex-53
+// errata 843419 that affects r0p0, r0p1, r0p2 and r0p4 versions of the core.
+// To keep the initial version simple there is no support for multiple
+// architectures or selection of different patches.
+//===----------------------------------------------------------------------===//
+
+#include "AArch64ErrataFix.h"
+#include "Config.h"
+#include "LinkerScript.h"
+#include "OutputSections.h"
+#include "Relocations.h"
+#include "Strings.h"
+#include "Symbols.h"
+#include "SyntheticSections.h"
+#include "Target.h"
+#include "lld/Common/Memory.h"
+
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+
+using namespace llvm;
+using namespace llvm::ELF;
+using namespace llvm::object;
+using namespace llvm::support::endian;
+
+using namespace lld;
+using namespace lld::elf;
+
+// Helper functions to identify instructions and conditions needed to trigger
+// the Cortex-A53-843419 erratum.
+
+// ADRP
+// | 1 | immlo (2) | 1 | 0 0 0 0 | immhi (19) | Rd (5) |
+static bool isADRP(uint32_t Instr) {
+ return (Instr & 0x9f000000) == 0x90000000;
+}
+
+// Load and store bit patterns from ARMv8-A ARM ARM.
+// Instructions appear in order of appearance starting from table in
+// C4.1.3 Loads and Stores.
+
+// All loads and stores have 1 (at bit postion 27), (0 at bit position 25).
+// | op0 x op1 (2) | 1 op2 0 op3 (2) | x | op4 (5) | xxxx | op5 (2) | x (10) |
+static bool isLoadStoreClass(uint32_t Instr) {
+ return (Instr & 0x0a000000) == 0x08000000;
+}
+
+// LDN/STN multiple no offset
+// | 0 Q 00 | 1100 | 0 L 00 | 0000 | opcode (4) | size (2) | Rn (5) | Rt (5) |
+// LDN/STN multiple post-indexed
+// | 0 Q 00 | 1100 | 1 L 0 | Rm (5)| opcode (4) | size (2) | Rn (5) | Rt (5) |
+// L == 0 for stores.
+
+// Utility routine to decode opcode field of LDN/STN multiple structure
+// instructions to find the ST1 instructions.
+// opcode == 0010 ST1 4 registers.
+// opcode == 0110 ST1 3 registers.
+// opcode == 0111 ST1 1 register.
+// opcode == 1010 ST1 2 registers.
+static bool isST1MultipleOpcode(uint32_t Instr) {
+ return (Instr & 0x0000f000) == 0x00002000 ||
+ (Instr & 0x0000f000) == 0x00006000 ||
+ (Instr & 0x0000f000) == 0x00007000 ||
+ (Instr & 0x0000f000) == 0x0000a000;
+}
+
+static bool isST1Multiple(uint32_t Instr) {
+ return (Instr & 0xbfff0000) == 0x0c000000 && isST1MultipleOpcode(Instr);
+}
+
+// Writes to Rn (writeback).
+static bool isST1MultiplePost(uint32_t Instr) {
+ return (Instr & 0xbfe00000) == 0x0c800000 && isST1MultipleOpcode(Instr);
+}
+
+// LDN/STN single no offset
+// | 0 Q 00 | 1101 | 0 L R 0 | 0000 | opc (3) S | size (2) | Rn (5) | Rt (5)|
+// LDN/STN single post-indexed
+// | 0 Q 00 | 1101 | 1 L R | Rm (5) | opc (3) S | size (2) | Rn (5) | Rt (5)|
+// L == 0 for stores
+
+// Utility routine to decode opcode field of LDN/STN single structure
+// instructions to find the ST1 instructions.
+// R == 0 for ST1 and ST3, R == 1 for ST2 and ST4.
+// opcode == 000 ST1 8-bit.
+// opcode == 010 ST1 16-bit.
+// opcode == 100 ST1 32 or 64-bit (Size determines which).
+static bool isST1SingleOpcode(uint32_t Instr) {
+ return (Instr & 0x0040e000) == 0x00000000 ||
+ (Instr & 0x0040e000) == 0x00004000 ||
+ (Instr & 0x0040e000) == 0x00008000;
+}
+
+static bool isST1Single(uint32_t Instr) {
+ return (Instr & 0xbfff0000) == 0x0d000000 && isST1SingleOpcode(Instr);
+}
+
+// Writes to Rn (writeback).
+static bool isST1SinglePost(uint32_t Instr) {
+ return (Instr & 0xbfe00000) == 0x0d800000 && isST1SingleOpcode(Instr);
+}
+
+static bool isST1(uint32_t Instr) {
+ return isST1Multiple(Instr) || isST1MultiplePost(Instr) ||
+ isST1Single(Instr) || isST1SinglePost(Instr);
+}
+
+// Load/store exclusive
+// | size (2) 00 | 1000 | o2 L o1 | Rs (5) | o0 | Rt2 (5) | Rn (5) | Rt (5) |
+// L == 0 for Stores.
+static bool isLoadStoreExclusive(uint32_t Instr) {
+ return (Instr & 0x3f000000) == 0x08000000;
+}
+
+static bool isLoadExclusive(uint32_t Instr) {
+ return (Instr & 0x3f400000) == 0x08400000;
+}
+
+// Load register literal
+// | opc (2) 01 | 1 V 00 | imm19 | Rt (5) |
+static bool isLoadLiteral(uint32_t Instr) {
+ return (Instr & 0x3b000000) == 0x18000000;
+}
+
+// Load/store no-allocate pair
+// (offset)
+// | opc (2) 10 | 1 V 00 | 0 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) |
+// L == 0 for stores.
+// Never writes to register
+static bool isSTNP(uint32_t Instr) {
+ return (Instr & 0x3bc00000) == 0x28000000;
+}
+
+// Load/store register pair
+// (post-indexed)
+// | opc (2) 10 | 1 V 00 | 1 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) |
+// L == 0 for stores, V == 0 for Scalar, V == 1 for Simd/FP
+// Writes to Rn.
+static bool isSTPPost(uint32_t Instr) {
+ return (Instr & 0x3bc00000) == 0x28800000;
+}
+
+// (offset)
+// | opc (2) 10 | 1 V 01 | 0 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) |
+static bool isSTPOffset(uint32_t Instr) {
+ return (Instr & 0x3bc00000) == 0x29000000;
+}
+
+// (pre-index)
+// | opc (2) 10 | 1 V 01 | 1 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) |
+// Writes to Rn.
+static bool isSTPPre(uint32_t Instr) {
+ return (Instr & 0x3bc00000) == 0x29800000;
+}
+
+static bool isSTP(uint32_t Instr) {
+ return isSTPPost(Instr) || isSTPOffset(Instr) || isSTPPre(Instr);
+}
+
+// Load/store register (unscaled immediate)
+// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 00 | Rn (5) | Rt (5) |
+// V == 0 for Scalar, V == 1 for Simd/FP.
+static bool isLoadStoreUnscaled(uint32_t Instr) {
+ return (Instr & 0x3b000c00) == 0x38000000;
+}
+
+// Load/store register (immediate post-indexed)
+// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 01 | Rn (5) | Rt (5) |
+static bool isLoadStoreImmediatePost(uint32_t Instr) {
+ return (Instr & 0x3b200c00) == 0x38000400;
+}
+
+// Load/store register (unprivileged)
+// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 10 | Rn (5) | Rt (5) |
+static bool isLoadStoreUnpriv(uint32_t Instr) {
+ return (Instr & 0x3b200c00) == 0x38000800;
+}
+
+// Load/store register (immediate pre-indexed)
+// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 11 | Rn (5) | Rt (5) |
+static bool isLoadStoreImmediatePre(uint32_t Instr) {
+ return (Instr & 0x3b200c00) == 0x38000c00;
+}
+
+// Load/store register (register offset)
+// | size (2) 11 | 1 V 00 | opc (2) 1 | Rm (5) | option (3) S | 10 | Rn | Rt |
+static bool isLoadStoreRegisterOff(uint32_t Instr) {
+ return (Instr & 0x3b200c00) == 0x38200800;
+}
+
+// Load/store register (unsigned immediate)
+// | size (2) 11 | 1 V 01 | opc (2) | imm12 | Rn (5) | Rt (5) |
+static bool isLoadStoreRegisterUnsigned(uint32_t Instr) {
+ return (Instr & 0x3b000000) == 0x39000000;
+}
+
+// Rt is always in bit position 0 - 4.
+static uint32_t getRt(uint32_t Instr) { return (Instr & 0x1f); }
+
+// Rn is always in bit position 5 - 9.
+static uint32_t getRn(uint32_t Instr) { return (Instr >> 5) & 0x1f; }
+
+// C4.1.2 Branches, Exception Generating and System instructions
+// | op0 (3) 1 | 01 op1 (4) | x (22) |
+// op0 == 010 101 op1 == 0xxx Conditional Branch.
+// op0 == 110 101 op1 == 1xxx Unconditional Branch Register.
+// op0 == x00 101 op1 == xxxx Unconditional Branch immediate.
+// op0 == x01 101 op1 == 0xxx Compare and branch immediate.
+// op0 == x01 101 op1 == 1xxx Test and branch immediate.
+static bool isBranch(uint32_t Instr) {
+ return ((Instr & 0xfe000000) == 0xd6000000) || // Cond branch.
+ ((Instr & 0xfe000000) == 0x54000000) || // Uncond branch reg.
+ ((Instr & 0x7c000000) == 0x14000000) || // Uncond branch imm.
+ ((Instr & 0x7c000000) == 0x34000000); // Compare and test branch.
+}
+
+static bool isV8SingleRegisterNonStructureLoadStore(uint32_t Instr) {
+ return isLoadStoreUnscaled(Instr) || isLoadStoreImmediatePost(Instr) ||
+ isLoadStoreUnpriv(Instr) || isLoadStoreImmediatePre(Instr) ||
+ isLoadStoreRegisterOff(Instr) || isLoadStoreRegisterUnsigned(Instr);
+}
+
+// Note that this function refers to v8.0 only and does not include the
+// additional load and store instructions added for in later revisions of
+// the architecture such as the Atomic memory operations introduced
+// in v8.1.
+static bool isV8NonStructureLoad(uint32_t Instr) {
+ if (isLoadExclusive(Instr))
+ return true;
+ if (isLoadLiteral(Instr))
+ return true;
+ else if (isV8SingleRegisterNonStructureLoadStore(Instr)) {
+ // For Load and Store single register, Loads are derived from a
+ // combination of the Size, V and Opc fields.
+ uint32_t Size = (Instr >> 30) & 0xff;
+ uint32_t V = (Instr >> 26) & 0x1;
+ uint32_t Opc = (Instr >> 22) & 0x3;
+ // For the load and store instructions that we are decoding.
+ // Opc == 0 are all stores.
+ // Opc == 1 with a couple of exceptions are loads. The exceptions are:
+ // Size == 00 (0), V == 1, Opc == 10 (2) which is a store and
+ // Size == 11 (3), V == 0, Opc == 10 (2) which is a prefetch.
+ return Opc != 0 && !(Size == 0 && V == 1 && Opc == 2) &&
+ !(Size == 3 && V == 0 && Opc == 2);
+ }
+ return false;
+}
+
+// The following decode instructions are only complete up to the instructions
+// needed for errata 843419.
+
+// Instruction with writeback updates the index register after the load/store.
+static bool hasWriteback(uint32_t Instr) {
+ return isLoadStoreImmediatePre(Instr) || isLoadStoreImmediatePost(Instr) ||
+ isSTPPre(Instr) || isSTPPost(Instr) || isST1SinglePost(Instr) ||
+ isST1MultiplePost(Instr);
+}
+
+// For the load and store class of instructions, a load can write to the
+// destination register, a load and a store can write to the base register when
+// the instruction has writeback.
+static bool doesLoadStoreWriteToReg(uint32_t Instr, uint32_t Reg) {
+ return (isV8NonStructureLoad(Instr) && getRt(Instr) == Reg) ||
+ (hasWriteback(Instr) && getRn(Instr) == Reg);
+}
+
+// Scanner for Cortex-A53 errata 843419
+// Full details are available in the Cortex A53 MPCore revision 0 Software
+// Developers Errata Notice (ARM-EPM-048406).
+//
+// The instruction sequence that triggers the erratum is common in compiled
+// AArch64 code, however it is sensitive to the offset of the sequence within
+// a 4k page. This means that by scanning and fixing the patch after we have
+// assigned addresses we only need to disassemble and fix instances of the
+// sequence in the range of affected offsets.
+//
+// In summary the erratum conditions are a series of 4 instructions:
+// 1.) An ADRP instruction that writes to register Rn with low 12 bits of
+// address of instruction either 0xff8 or 0xffc.
+// 2.) A load or store instruction that can be:
+// - A single register load or store, of either integer or vector registers.
+// - An STP or STNP, of either integer or vector registers.
+// - An Advanced SIMD ST1 store instruction.
+// - Must not write to Rn, but may optionally read from it.
+// 3.) An optional instruction that is not a branch and does not write to Rn.
+// 4.) A load or store from the Load/store register (unsigned immediate) class
+// that uses Rn as the base address register.
+//
+// Note that we do not attempt to scan for Sequence 2 as described in the
+// Software Developers Errata Notice as this has been assessed to be extremely
+// unlikely to occur in compiled code. This matches gold and ld.bfd behavior.
+
+// Return true if the Instruction sequence Adrp, Instr2, and Instr4 match
+// the erratum sequence. The Adrp, Instr2 and Instr4 correspond to 1.), 2.),
+// and 4.) in the Scanner for Cortex-A53 errata comment above.
+static bool is843419ErratumSequence(uint32_t Instr1, uint32_t Instr2,
+ uint32_t Instr4) {
+ if (!isADRP(Instr1))
+ return false;
+
+ uint32_t Rn = getRt(Instr1);
+ return isLoadStoreClass(Instr2) &&
+ (isLoadStoreExclusive(Instr2) || isLoadLiteral(Instr2) ||
+ isV8SingleRegisterNonStructureLoadStore(Instr2) || isSTP(Instr2) ||
+ isSTNP(Instr2) || isST1(Instr2)) &&
+ !doesLoadStoreWriteToReg(Instr2, Rn) &&
+ isLoadStoreRegisterUnsigned(Instr4) && getRn(Instr4) == Rn;
+}
+
+// Scan the instruction sequence starting at Offset Off from the base of
+// InputSection IS. We update Off in this function rather than in the caller as
+// we can skip ahead much further into the section when we know how many
+// instructions we've scanned.
+// Return the offset of the load or store instruction in IS that we want to
+// patch or 0 if no patch required.
+static uint64_t scanCortexA53Errata843419(InputSection *IS, uint64_t &Off,
+ uint64_t Limit) {
+ uint64_t ISAddr = IS->getParent()->Addr + IS->OutSecOff;
+
+ // Advance Off so that (ISAddr + Off) modulo 0x1000 is at least 0xff8.
+ uint64_t InitialPageOff = (ISAddr + Off) & 0xfff;
+ if (InitialPageOff < 0xff8)
+ Off += 0xff8 - InitialPageOff;
+
+ bool OptionalAllowed = Limit - Off > 12;
+ if (Off >= Limit || Limit - Off < 12) {
+ // Need at least 3 4-byte sized instructions to trigger erratum.
+ Off = Limit;
+ return 0;
+ }
+
+ uint64_t PatchOff = 0;
+ const uint8_t *Buf = IS->Data.begin();
+ const uint32_t *InstBuf = reinterpret_cast<const uint32_t *>(Buf + Off);
+ uint32_t Instr1 = *InstBuf++;
+ uint32_t Instr2 = *InstBuf++;
+ uint32_t Instr3 = *InstBuf++;
+ if (is843419ErratumSequence(Instr1, Instr2, Instr3)) {
+ PatchOff = Off + 8;
+ } else if (OptionalAllowed && !isBranch(Instr3)) {
+ uint32_t Instr4 = *InstBuf++;
+ if (is843419ErratumSequence(Instr1, Instr2, Instr4))
+ PatchOff = Off + 12;
+ }
+ if (((ISAddr + Off) & 0xfff) == 0xff8)
+ Off += 4;
+ else
+ Off += 0xffc;
+ return PatchOff;
+}
+
+class lld::elf::Patch843419Section : public SyntheticSection {
+public:
+ Patch843419Section(InputSection *P, uint64_t Off);
+
+ void writeTo(uint8_t *Buf) override;
+
+ size_t getSize() const override { return 8; }
+
+ uint64_t getLDSTAddr() const;
+
+ // The Section we are patching.
+ const InputSection *Patchee;
+ // The offset of the instruction in the Patchee section we are patching.
+ uint64_t PatcheeOffset;
+ // A label for the start of the Patch that we can use as a relocation target.
+ Symbol *PatchSym;
+};
+
+lld::elf::Patch843419Section::Patch843419Section(InputSection *P, uint64_t Off)
+ : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 4,
+ ".text.patch"),
+ Patchee(P), PatcheeOffset(Off) {
+ this->Parent = P->getParent();
+ PatchSym = addSyntheticLocal(
+ Saver.save("__CortexA53843419_" + utohexstr(getLDSTAddr())), STT_FUNC, 0,
+ getSize(), this);
+ addSyntheticLocal(Saver.save("$x"), STT_NOTYPE, 0, 0, this);
+}
+
+uint64_t lld::elf::Patch843419Section::getLDSTAddr() const {
+ return Patchee->getParent()->Addr + Patchee->OutSecOff + PatcheeOffset;
+}
+
+void lld::elf::Patch843419Section::writeTo(uint8_t *Buf) {
+ // Copy the instruction that we will be replacing with a branch in the
+ // Patchee Section.
+ write32le(Buf, read32le(Patchee->Data.begin() + PatcheeOffset));
+
+ // Apply any relocation transferred from the original PatcheeSection.
+ // For a SyntheticSection Buf already has OutSecOff added, but relocateAlloc
+ // also adds OutSecOff so we need to subtract to avoid double counting.
+ this->relocateAlloc(Buf - OutSecOff, Buf - OutSecOff + getSize());
+
+ // Return address is the next instruction after the one we have just copied.
+ uint64_t S = getLDSTAddr() + 4;
+ uint64_t P = PatchSym->getVA() + 4;
+ Target->relocateOne(Buf + 4, R_AARCH64_JUMP26, S - P);
+}
+
+void AArch64Err843419Patcher::init() {
+ // The AArch64 ABI permits data in executable sections. We must avoid scanning
+ // this data as if it were instructions to avoid false matches. We use the
+ // mapping symbols in the InputObjects to identify this data, caching the
+ // results in SectionMap so we don't have to recalculate it each pass.
+
+ // The ABI Section 4.5.4 Mapping symbols; defines local symbols that describe
+ // half open intervals [Symbol Value, Next Symbol Value) of code and data
+ // within sections. If there is no next symbol then the half open interval is
+ // [Symbol Value, End of section). The type, code or data, is determined by
+ // the mapping symbol name, $x for code, $d for data.
+ auto IsCodeMapSymbol = [](const Symbol *B) {
+ return B->getName() == "$x" || B->getName().startswith("$x.");
+ };
+ auto IsDataMapSymbol = [](const Symbol *B) {
+ return B->getName() == "$d" || B->getName().startswith("$d.");
+ };
+
+ // Collect mapping symbols for every executable InputSection.
+ for (InputFile *File : ObjectFiles) {
+ auto *F = cast<ObjFile<ELF64LE>>(File);
+ for (Symbol *B : F->getLocalSymbols()) {
+ auto *Def = dyn_cast<Defined>(B);
+ if (!Def)
+ continue;
+ if (!IsCodeMapSymbol(Def) && !IsDataMapSymbol(Def))
+ continue;
+ if (auto *Sec = dyn_cast<InputSection>(Def->Section))
+ if (Sec->Flags & SHF_EXECINSTR)
+ SectionMap[Sec].push_back(Def);
+ }
+ }
+ // For each InputSection make sure the mapping symbols are in sorted in
+ // ascending order and free from consecutive runs of mapping symbols with
+ // the same type. For example we must remove the redundant $d.1 from $x.0
+ // $d.0 $d.1 $x.1.
+ for (auto &KV : SectionMap) {
+ std::vector<const Defined *> &MapSyms = KV.second;
+ if (MapSyms.size() <= 1)
+ continue;
+ std::stable_sort(
+ MapSyms.begin(), MapSyms.end(),
+ [](const Defined *A, const Defined *B) { return A->Value < B->Value; });
+ MapSyms.erase(
+ std::unique(MapSyms.begin(), MapSyms.end(),
+ [=](const Defined *A, const Defined *B) {
+ return (IsCodeMapSymbol(A) && IsCodeMapSymbol(B)) ||
+ (IsDataMapSymbol(A) && IsDataMapSymbol(B));
+ }),
+ MapSyms.end());
+ }
+ Initialized = true;
+}
+
+// Insert the PatchSections we have created back into the
+// InputSectionDescription. As inserting patches alters the addresses of
+// InputSections that follow them, we try and place the patches after all the
+// executable sections, although we may need to insert them earlier if the
+// InputSectionDescription is larger than the maximum branch range.
+void AArch64Err843419Patcher::insertPatches(
+ InputSectionDescription &ISD, std::vector<Patch843419Section *> &Patches) {
+ uint64_t ISLimit;
+ uint64_t PrevISLimit = ISD.Sections.front()->OutSecOff;
+ uint64_t PatchUpperBound = PrevISLimit + Target->ThunkSectionSpacing;
+
+ // Set the OutSecOff of patches to the place where we want to insert them.
+ // We use a similar strategy to Thunk placement. Place patches roughly
+ // every multiple of maximum branch range.
+ auto PatchIt = Patches.begin();
+ auto PatchEnd = Patches.end();
+ for (const InputSection *IS : ISD.Sections) {
+ ISLimit = IS->OutSecOff + IS->getSize();
+ if (ISLimit > PatchUpperBound) {
+ while (PatchIt != PatchEnd) {
+ if ((*PatchIt)->getLDSTAddr() >= PrevISLimit)
+ break;
+ (*PatchIt)->OutSecOff = PrevISLimit;
+ ++PatchIt;
+ }
+ PatchUpperBound = PrevISLimit + Target->ThunkSectionSpacing;
+ }
+ PrevISLimit = ISLimit;
+ }
+ for (; PatchIt != PatchEnd; ++PatchIt) {
+ (*PatchIt)->OutSecOff = ISLimit;
+ }
+
+ // merge all patch sections. We use the OutSecOff assigned above to
+ // determine the insertion point. This is ok as we only merge into an
+ // InputSectionDescription once per pass, and at the end of the pass
+ // assignAddresses() will recalculate all the OutSecOff values.
+ std::vector<InputSection *> Tmp;
+ Tmp.reserve(ISD.Sections.size() + Patches.size());
+ auto MergeCmp = [](const InputSection *A, const InputSection *B) {
+ if (A->OutSecOff < B->OutSecOff)
+ return true;
+ if (A->OutSecOff == B->OutSecOff && isa<Patch843419Section>(A) &&
+ !isa<Patch843419Section>(B))
+ return true;
+ return false;
+ };
+ std::merge(ISD.Sections.begin(), ISD.Sections.end(), Patches.begin(),
+ Patches.end(), std::back_inserter(Tmp), MergeCmp);
+ ISD.Sections = std::move(Tmp);
+}
+
+// Given an erratum sequence that starts at address AdrpAddr, with an
+// instruction that we need to patch at PatcheeOffset from the start of
+// InputSection IS, create a Patch843419 Section and add it to the
+// Patches that we need to insert.
+static void implementPatch(uint64_t AdrpAddr, uint64_t PatcheeOffset,
+ InputSection *IS,
+ std::vector<Patch843419Section *> &Patches) {
+ // There may be a relocation at the same offset that we are patching. There
+ // are three cases that we need to consider.
+ // Case 1: R_AARCH64_JUMP26 branch relocation. We have already patched this
+ // instance of the erratum on a previous patch and altered the relocation. We
+ // have nothing more to do.
+ // Case 2: A load/store register (unsigned immediate) class relocation. There
+ // are two of these R_AARCH_LD64_ABS_LO12_NC and R_AARCH_LD64_GOT_LO12_NC and
+ // they are both absolute. We need to add the same relocation to the patch,
+ // and replace the relocation with a R_AARCH_JUMP26 branch relocation.
+ // Case 3: No relocation. We must create a new R_AARCH64_JUMP26 branch
+ // relocation at the offset.
+ auto RelIt = std::find_if(
+ IS->Relocations.begin(), IS->Relocations.end(),
+ [=](const Relocation &R) { return R.Offset == PatcheeOffset; });
+ if (RelIt != IS->Relocations.end() && RelIt->Type == R_AARCH64_JUMP26)
+ return;
+
+ if (Config->Verbose)
+ message("detected cortex-a53-843419 erratum sequence starting at " +
+ utohexstr(AdrpAddr) + " in unpatched output.");
+
+ auto *PS = make<Patch843419Section>(IS, PatcheeOffset);
+ Patches.push_back(PS);
+
+ auto MakeRelToPatch = [](uint64_t Offset, Symbol *PatchSym) {
+ return Relocation{R_PC, R_AARCH64_JUMP26, Offset, 0, PatchSym};
+ };
+
+ if (RelIt != IS->Relocations.end()) {
+ PS->Relocations.push_back(
+ {RelIt->Expr, RelIt->Type, 0, RelIt->Addend, RelIt->Sym});
+ *RelIt = MakeRelToPatch(PatcheeOffset, PS->PatchSym);
+ } else
+ IS->Relocations.push_back(MakeRelToPatch(PatcheeOffset, PS->PatchSym));
+}
+
+// Scan all the instructions in InputSectionDescription, for each instance of
+// the erratum sequence create a Patch843419Section. We return the list of
+// Patch843419Sections that need to be applied to ISD.
+std::vector<Patch843419Section *>
+AArch64Err843419Patcher::patchInputSectionDescription(
+ InputSectionDescription &ISD) {
+ std::vector<Patch843419Section *> Patches;
+ for (InputSection *IS : ISD.Sections) {
+ // LLD doesn't use the erratum sequence in SyntheticSections.
+ if (isa<SyntheticSection>(IS))
+ continue;
+ // Use SectionMap to make sure we only scan code and not inline data.
+ // We have already sorted MapSyms in ascending order and removed consecutive
+ // mapping symbols of the same type. Our range of executable instructions to
+ // scan is therefore [CodeSym->Value, DataSym->Value) or [CodeSym->Value,
+ // section size).
+ std::vector<const Defined *> &MapSyms = SectionMap[IS];
+
+ auto CodeSym = llvm::find_if(MapSyms, [&](const Defined *MS) {
+ return MS->getName().startswith("$x");
+ });
+
+ while (CodeSym != MapSyms.end()) {
+ auto DataSym = std::next(CodeSym);
+ uint64_t Off = (*CodeSym)->Value;
+ uint64_t Limit =
+ (DataSym == MapSyms.end()) ? IS->Data.size() : (*DataSym)->Value;
+
+ while (Off < Limit) {
+ uint64_t StartAddr = IS->getParent()->Addr + IS->OutSecOff + Off;
+ if (uint64_t PatcheeOffset = scanCortexA53Errata843419(IS, Off, Limit))
+ implementPatch(StartAddr, PatcheeOffset, IS, Patches);
+ }
+ if (DataSym == MapSyms.end())
+ break;
+ CodeSym = std::next(DataSym);
+ }
+ }
+ return Patches;
+}
+
+// For each InputSectionDescription make one pass over the executable sections
+// looking for the erratum sequence; creating a synthetic Patch843419Section
+// for each instance found. We insert these synthetic patch sections after the
+// executable code in each InputSectionDescription.
+//
+// PreConditions:
+// The Output and Input Sections have had their final addresses assigned.
+//
+// PostConditions:
+// Returns true if at least one patch was added. The addresses of the
+// Ouptut and Input Sections may have been changed.
+// Returns false if no patches were required and no changes were made.
+bool AArch64Err843419Patcher::createFixes() {
+ if (Initialized == false)
+ init();
+
+ bool AddressesChanged = false;
+ for (OutputSection *OS : OutputSections) {
+ if (!(OS->Flags & SHF_ALLOC) || !(OS->Flags & SHF_EXECINSTR))
+ continue;
+ for (BaseCommand *BC : OS->SectionCommands)
+ if (auto *ISD = dyn_cast<InputSectionDescription>(BC)) {
+ std::vector<Patch843419Section *> Patches =
+ patchInputSectionDescription(*ISD);
+ if (!Patches.empty()) {
+ insertPatches(*ISD, Patches);
+ AddressesChanged = true;
+ }
+ }
+ }
+ return AddressesChanged;
+}
diff --git a/ELF/AArch64ErrataFix.h b/ELF/AArch64ErrataFix.h
new file mode 100644
index 000000000000..6c100f25d8af
--- /dev/null
+++ b/ELF/AArch64ErrataFix.h
@@ -0,0 +1,52 @@
+//===- AArch64ErrataFix.h ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_ELF_AARCH64ERRATAFIX_H
+#define LLD_ELF_AARCH64ERRATAFIX_H
+
+#include "lld/Common/LLVM.h"
+
+#include <map>
+#include <vector>
+
+namespace lld {
+namespace elf {
+
+class Defined;
+class InputSection;
+struct InputSectionDescription;
+class OutputSection;
+class Patch843419Section;
+
+class AArch64Err843419Patcher {
+public:
+ // return true if Patches have been added to the OutputSections.
+ bool createFixes();
+
+private:
+ std::vector<Patch843419Section *>
+ patchInputSectionDescription(InputSectionDescription &ISD);
+
+ void insertPatches(InputSectionDescription &ISD,
+ std::vector<Patch843419Section *> &Patches);
+
+ void init();
+
+ // A cache of the mapping symbols defined by the InputSecion sorted in order
+ // of ascending value with redundant symbols removed. These describe
+ // the ranges of code and data in an executable InputSection.
+ std::map<InputSection *, std::vector<const Defined *>> SectionMap;
+
+ bool Initialized = false;
+};
+
+} // namespace elf
+} // namespace lld
+
+#endif
diff --git a/ELF/Arch/AArch64.cpp b/ELF/Arch/AArch64.cpp
index b26cf0815109..99e9879a6989 100644
--- a/ELF/Arch/AArch64.cpp
+++ b/ELF/Arch/AArch64.cpp
@@ -7,11 +7,11 @@
//
//===----------------------------------------------------------------------===//
-#include "Error.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "Thunks.h"
+#include "lld/Common/ErrorHandler.h"
#include "llvm/Object/ELF.h"
#include "llvm/Support/Endian.h"
@@ -32,20 +32,23 @@ namespace {
class AArch64 final : public TargetInfo {
public:
AArch64();
- RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
+ RelExpr getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const override;
- bool isPicRel(uint32_t Type) const override;
- void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
+ bool isPicRel(RelType Type) const override;
+ void writeGotPlt(uint8_t *Buf, const Symbol &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 needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
+ uint64_t BranchAddr, const Symbol &S) const override;
+ bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override;
+ 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
@@ -66,13 +69,17 @@ AArch64::AArch64() {
// It doesn't seem to be documented anywhere, but tls on aarch64 uses variant
// 1 of the tls structures and the tcb size is 16.
TcbSize = 16;
+ NeedsThunks = true;
+
+ // See comment in Arch/ARM.cpp for a more detailed explanation of
+ // ThunkSectionSpacing. For AArch64 the only branches we are permitted to
+ // Thunk have a range of +/- 128 MiB
+ ThunkSectionSpacing = (128 * 1024 * 1024) - 0x30000;
}
-RelExpr AArch64::getRelExpr(uint32_t Type, const SymbolBody &S,
+RelExpr AArch64::getRelExpr(RelType Type, const Symbol &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 +99,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 +111,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 +126,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,11 +144,11 @@ 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;
}
-void AArch64::writeGotPlt(uint8_t *Buf, const SymbolBody &) const {
+void AArch64::writeGotPlt(uint8_t *Buf, const Symbol &) const {
write64le(Buf, InX::Plt->getVA());
}
@@ -180,6 +190,31 @@ void AArch64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
relocateOne(Buf + 8, R_AARCH64_ADD_ABS_LO12_NC, GotPltEntryAddr);
}
+bool AArch64::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
+ uint64_t BranchAddr, const Symbol &S) const {
+ // ELF for the ARM 64-bit architecture, section Call and Jump relocations
+ // only permits range extension thunks for R_AARCH64_CALL26 and
+ // R_AARCH64_JUMP26 relocation types.
+ if (Type != R_AARCH64_CALL26 && Type != R_AARCH64_JUMP26)
+ return false;
+ uint64_t Dst = (Expr == R_PLT_PC) ? S.getPltVA() : S.getVA();
+ return !inBranchRange(Type, BranchAddr, Dst);
+}
+
+bool AArch64::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
+ if (Type != R_AARCH64_CALL26 && Type != R_AARCH64_JUMP26)
+ return true;
+ // The AArch64 call and unconditional branch instructions have a range of
+ // +/- 128 MiB.
+ uint64_t Range = 128 * 1024 * 1024;
+ if (Dst > Src) {
+ // Immediate of branch is signed.
+ Range -= 4;
+ return Dst - Src <= Range;
+ }
+ return Src - Dst <= Range;
+}
+
static void write32AArch64Addr(uint8_t *L, uint64_t Imm) {
uint32_t ImmLo = (Imm & 0x3) << 29;
uint32_t ImmHi = (Imm & 0x1FFFFC) << 3;
@@ -201,7 +236,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 +267,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 +297,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 +341,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 +371,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 +402,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 de566c617ac0..505e0e6ad480 100644
--- a/ELF/Arch/AMDGPU.cpp
+++ b/ELF/Arch/AMDGPU.cpp
@@ -7,10 +7,10 @@
//
//===----------------------------------------------------------------------===//
-#include "Error.h"
#include "InputFiles.h"
#include "Symbols.h"
#include "Target.h"
+#include "lld/Common/ErrorHandler.h"
#include "llvm/Object/ELF.h"
#include "llvm/Support/Endian.h"
@@ -25,19 +25,38 @@ 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,
+ uint32_t calcEFlags() const override;
+ void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ RelExpr getRelExpr(RelType Type, const Symbol &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 {
+static uint32_t getEFlags(InputFile *File) {
+ return cast<ObjFile<ELF64LE>>(File)->getObj().getHeader()->e_flags;
+}
+
+uint32_t AMDGPU::calcEFlags() const {
+ assert(!ObjectFiles.empty());
+ uint32_t Ret = getEFlags(ObjectFiles[0]);
+
+ // Verify that all input files have the same e_flags.
+ for (InputFile *F : makeArrayRef(ObjectFiles).slice(1)) {
+ if (Ret == getEFlags(F))
+ continue;
+ error("incompatible e_flags: " + toString(F));
+ return 0;
+ }
+ return Ret;
+}
+
+void AMDGPU::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
switch (Type) {
case R_AMDGPU_ABS32:
case R_AMDGPU_GOTPCREL:
@@ -58,7 +77,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 Symbol &S,
const uint8_t *Loc) const {
switch (Type) {
case R_AMDGPU_ABS32:
@@ -73,8 +92,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 106021de7d32..94a98e7679bd 100644
--- a/ELF/Arch/ARM.cpp
+++ b/ELF/Arch/ARM.cpp
@@ -7,12 +7,12 @@
//
//===----------------------------------------------------------------------===//
-#include "Error.h"
#include "InputFiles.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "Thunks.h"
+#include "lld/Common/ErrorHandler.h"
#include "llvm/Object/ELF.h"
#include "llvm/Support/Endian.h"
@@ -26,23 +26,23 @@ namespace {
class ARM final : public TargetInfo {
public:
ARM();
- RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
+ uint32_t calcEFlags() const override;
+ RelExpr getRelExpr(RelType Type, const Symbol &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;
- void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
- void writeIgotPlt(uint8_t *Buf, const SymbolBody &S) 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 Symbol &S) const override;
+ void writeIgotPlt(uint8_t *Buf, const Symbol &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 addPltSymbols(InputSectionBase *IS, uint64_t Off) const override;
void addPltHeaderSymbols(InputSectionBase *ISD) const override;
- bool needsThunk(RelExpr Expr, uint32_t RelocType, const InputFile *File,
- const SymbolBody &S) const override;
- bool inBranchRange(uint32_t RelocType, uint64_t Src,
- uint64_t Dst) const override;
- void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
+ bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
+ uint64_t BranchAddr, const Symbol &S) 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
@@ -58,18 +58,54 @@ ARM::ARM() {
GotEntrySize = 4;
GotPltEntrySize = 4;
PltEntrySize = 16;
- PltHeaderSize = 20;
+ PltHeaderSize = 32;
TrapInstr = 0xd4d4d4d4;
// ARM uses Variant 1 TLS
TcbSize = 8;
NeedsThunks = true;
+
+ // The placing of pre-created ThunkSections is controlled by the
+ // ThunkSectionSpacing parameter. The aim is to place the
+ // ThunkSection such that all branches from the InputSections prior to the
+ // ThunkSection can reach a Thunk placed at the end of the ThunkSection.
+ // Graphically:
+ // | up to ThunkSectionSpacing .text input sections |
+ // | ThunkSection |
+ // | up to ThunkSectionSpacing .text input sections |
+ // | ThunkSection |
+
+ // Pre-created ThunkSections are spaced roughly 16MiB apart on ARM. This is to
+ // match the most common expected case of a Thumb 2 encoded BL, BLX or B.W
+ // ARM B, BL, BLX range +/- 32MiB
+ // Thumb B.W, BL, BLX range +/- 16MiB
+ // Thumb B<cc>.W range +/- 1MiB
+ // If a branch cannot reach a pre-created ThunkSection a new one will be
+ // created so we can handle the rare cases of a Thumb 2 conditional branch.
+ // We intentionally use a lower size for ThunkSectionSpacing than the maximum
+ // branch range so the end of the ThunkSection is more likely to be within
+ // range of the branch instruction that is furthest away. The value we shorten
+ // ThunkSectionSpacing by is set conservatively to allow us to create 16,384
+ // 12 byte Thunks at any offset in a ThunkSection without risk of a branch to
+ // one of the Thunks going out of range.
+
+ // FIXME: lld assumes that the Thumb BL and BLX encoding permits the J1 and
+ // J2 bits to be used to extend the branch range. On earlier Architectures
+ // such as ARMv4, ARMv5 and ARMv6 (except ARMv6T2) the range is +/- 4MiB. If
+ // support for the earlier encodings is added then when they are used the
+ // ThunkSectionSpacing will need lowering.
+ ThunkSectionSpacing = 0x1000000 - 0x30000;
+}
+
+uint32_t ARM::calcEFlags() const {
+ // We don't currently use any features incompatible with EF_ARM_EABI_VER5,
+ // but we don't have any firm guarantees of conformance. Linux AArch64
+ // kernels (as of 2016) require an EABI version to be set.
+ return EF_ARM_EABI_VER5;
}
-RelExpr ARM::getRelExpr(uint32_t Type, const SymbolBody &S,
+RelExpr ARM::getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const {
switch (Type) {
- default:
- return R_ABS;
case R_ARM_THM_JUMP11:
return R_PC;
case R_ARM_CALL:
@@ -120,15 +156,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)
@@ -137,41 +175,74 @@ uint32_t ARM::getDynRel(uint32_t Type) const {
return R_ARM_ABS32;
}
-void ARM::writeGotPlt(uint8_t *Buf, const SymbolBody &) const {
+void ARM::writeGotPlt(uint8_t *Buf, const Symbol &) const {
write32le(Buf, InX::Plt->getVA());
}
-void ARM::writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const {
+void ARM::writeIgotPlt(uint8_t *Buf, const Symbol &S) const {
// An ARM entry is the address of the ifunc resolver function.
write32le(Buf, S.getVA());
}
-void ARM::writePltHeader(uint8_t *Buf) const {
+// Long form PLT Heade that does not have any restrictions on the displacement
+// of the .plt from the .plt.got.
+static void writePltHeaderLong(uint8_t *Buf) {
const uint8_t PltData[] = {
0x04, 0xe0, 0x2d, 0xe5, // str lr, [sp,#-4]!
0x04, 0xe0, 0x9f, 0xe5, // ldr lr, L2
0x0e, 0xe0, 0x8f, 0xe0, // L1: add lr, pc, lr
0x08, 0xf0, 0xbe, 0xe5, // ldr pc, [lr, #8]
0x00, 0x00, 0x00, 0x00, // L2: .word &(.got.plt) - L1 - 8
- };
+ 0xd4, 0xd4, 0xd4, 0xd4, // Pad to 32-byte boundary
+ 0xd4, 0xd4, 0xd4, 0xd4, // Pad to 32-byte boundary
+ 0xd4, 0xd4, 0xd4, 0xd4};
memcpy(Buf, PltData, sizeof(PltData));
uint64_t GotPlt = InX::GotPlt->getVA();
uint64_t L1 = InX::Plt->getVA() + 8;
write32le(Buf + 16, GotPlt - L1 - 8);
}
+// The default PLT header requires the .plt.got to be within 128 Mb of the
+// .plt in the positive direction.
+void ARM::writePltHeader(uint8_t *Buf) const {
+ // Use a similar sequence to that in writePlt(), the difference is the calling
+ // conventions mean we use lr instead of ip. The PLT entry is responsible for
+ // saving lr on the stack, the dynamic loader is responsible for reloading
+ // it.
+ const uint32_t PltData[] = {
+ 0xe52de004, // L1: str lr, [sp,#-4]!
+ 0xe28fe600, // add lr, pc, #0x0NN00000 &(.got.plt - L1 - 4)
+ 0xe28eea00, // add lr, lr, #0x000NN000 &(.got.plt - L1 - 4)
+ 0xe5bef000, // ldr pc, [lr, #0x00000NNN] &(.got.plt -L1 - 4)
+ };
+
+ uint64_t Offset = InX::GotPlt->getVA() - InX::Plt->getVA() - 4;
+ if (!llvm::isUInt<27>(Offset)) {
+ // We cannot encode the Offset, use the long form.
+ writePltHeaderLong(Buf);
+ return;
+ }
+ write32le(Buf + 0, PltData[0]);
+ write32le(Buf + 4, PltData[1] | ((Offset >> 20) & 0xff));
+ write32le(Buf + 8, PltData[2] | ((Offset >> 12) & 0xff));
+ write32le(Buf + 12, PltData[3] | (Offset & 0xfff));
+ write32le(Buf + 16, TrapInstr); // Pad to 32-byte boundary
+ write32le(Buf + 20, TrapInstr);
+ write32le(Buf + 24, TrapInstr);
+ write32le(Buf + 28, TrapInstr);
+}
+
void ARM::addPltHeaderSymbols(InputSectionBase *ISD) const {
auto *IS = cast<InputSection>(ISD);
addSyntheticLocal("$a", STT_NOTYPE, 0, 0, IS);
addSyntheticLocal("$d", STT_NOTYPE, 16, 0, IS);
}
-void ARM::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
- uint64_t PltEntryAddr, int32_t Index,
- unsigned RelOff) const {
- // FIXME: Using simple code sequence with simple relocations.
- // There is a more optimal sequence but it requires support for the group
- // relocations. See ELF for the ARM Architecture Appendix A.3
+// Long form PLT entries that do not have any restrictions on the displacement
+// of the .plt from the .plt.got.
+static void writePltLong(uint8_t *Buf, uint64_t GotPltEntryAddr,
+ uint64_t PltEntryAddr, int32_t Index,
+ unsigned RelOff) {
const uint8_t PltData[] = {
0x04, 0xc0, 0x9f, 0xe5, // ldr ip, L2
0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc
@@ -183,24 +254,50 @@ void ARM::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
write32le(Buf + 12, GotPltEntryAddr - L1 - 8);
}
+// The default PLT entries require the .plt.got to be within 128 Mb of the
+// .plt in the positive direction.
+void ARM::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
+ uint64_t PltEntryAddr, int32_t Index,
+ unsigned RelOff) const {
+ // The PLT entry is similar to the example given in Appendix A of ELF for
+ // the Arm Architecture. Instead of using the Group Relocations to find the
+ // optimal rotation for the 8-bit immediate used in the add instructions we
+ // hard code the most compact rotations for simplicity. This saves a load
+ // instruction over the long plt sequences.
+ const uint32_t PltData[] = {
+ 0xe28fc600, // L1: add ip, pc, #0x0NN00000 Offset(&(.plt.got) - L1 - 8
+ 0xe28cca00, // add ip, ip, #0x000NN000 Offset(&(.plt.got) - L1 - 8
+ 0xe5bcf000, // ldr pc, [ip, #0x00000NNN] Offset(&(.plt.got) - L1 - 8
+ };
+
+ uint64_t Offset = GotPltEntryAddr - PltEntryAddr - 8;
+ if (!llvm::isUInt<27>(Offset)) {
+ // We cannot encode the Offset, use the long form.
+ writePltLong(Buf, GotPltEntryAddr, PltEntryAddr, Index, RelOff);
+ return;
+ }
+ write32le(Buf + 0, PltData[0] | ((Offset >> 20) & 0xff));
+ write32le(Buf + 4, PltData[1] | ((Offset >> 12) & 0xff));
+ write32le(Buf + 8, PltData[2] | (Offset & 0xfff));
+ write32le(Buf + 12, TrapInstr); // Pad to 16-byte boundary
+}
+
void ARM::addPltSymbols(InputSectionBase *ISD, uint64_t Off) const {
auto *IS = cast<InputSection>(ISD);
addSyntheticLocal("$a", STT_NOTYPE, Off, 0, IS);
addSyntheticLocal("$d", STT_NOTYPE, Off + 12, 0, IS);
}
-bool ARM::needsThunk(RelExpr Expr, uint32_t RelocType, 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)
+bool ARM::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
+ uint64_t BranchAddr, const Symbol &S) const {
+ // If S is an undefined weak symbol and does not have a PLT entry then it
+ // will be resolved as a branch to the next instruction.
+ if (S.isUndefWeak() && !S.isInPlt())
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:
@@ -208,23 +305,31 @@ bool ARM::needsThunk(RelExpr Expr, uint32_t RelocType, const InputFile *File,
// Otherwise we need to interwork if Symbol has bit 0 set (Thumb).
if (Expr == R_PC && ((S.getVA() & 1) == 1))
return true;
- break;
+ LLVM_FALLTHROUGH;
+ case R_ARM_CALL: {
+ uint64_t Dst = (Expr == R_PLT_PC) ? S.getPltVA() : S.getVA();
+ return !inBranchRange(Type, BranchAddr, Dst);
+ }
case R_ARM_THM_JUMP19:
case R_ARM_THM_JUMP24:
// Source is Thumb, all PLT entries are ARM so interworking is required.
// Otherwise we need to interwork if Symbol has bit 0 clear (ARM).
if (Expr == R_PLT_PC || ((S.getVA() & 1) == 0))
return true;
- break;
+ LLVM_FALLTHROUGH;
+ case R_ARM_THM_CALL: {
+ uint64_t Dst = (Expr == R_PLT_PC) ? S.getPltVA() : S.getVA();
+ return !inBranchRange(Type, BranchAddr, Dst);
+ }
}
return false;
}
-bool ARM::inBranchRange(uint32_t RelocType, uint64_t Src, uint64_t Dst) const {
+bool ARM::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
uint64_t Range;
uint64_t InstrSize;
- switch (RelocType) {
+ switch (Type) {
case R_ARM_PC24:
case R_ARM_PLT32:
case R_ARM_JUMP24:
@@ -263,7 +368,7 @@ bool ARM::inBranchRange(uint32_t RelocType, uint64_t Src, uint64_t Dst) const {
return Distance <= Range;
}
-void ARM::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
+void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
switch (Type) {
case R_ARM_ABS32:
case R_ARM_BASE_PREL:
@@ -400,7 +505,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 3853248f8fbd..02ac770127b9 100644
--- a/ELF/Arch/AVR.cpp
+++ b/ELF/Arch/AVR.cpp
@@ -26,10 +26,10 @@
//
//===----------------------------------------------------------------------===//
-#include "Error.h"
#include "InputFiles.h"
#include "Symbols.h"
#include "Target.h"
+#include "lld/Common/ErrorHandler.h"
#include "llvm/Object/ELF.h"
#include "llvm/Support/Endian.h"
@@ -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 Symbol &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 Symbol &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 b8d796f5897a..495e2567006f 100644
--- a/ELF/Arch/Mips.cpp
+++ b/ELF/Arch/Mips.cpp
@@ -7,13 +7,13 @@
//
//===----------------------------------------------------------------------===//
-#include "Error.h"
#include "InputFiles.h"
#include "OutputSections.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "Thunks.h"
+#include "lld/Common/ErrorHandler.h"
#include "llvm/Object/ELF.h"
#include "llvm/Support/Endian.h"
@@ -28,19 +28,20 @@ namespace {
template <class ELFT> class MIPS final : public TargetInfo {
public:
MIPS();
- RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
+ uint32_t calcEFlags() const override;
+ RelExpr getRelExpr(RelType Type, const Symbol &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;
- void writeGotPlt(uint8_t *Buf, const SymbolBody &S) 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 Symbol &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,
- 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;
+ bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
+ uint64_t BranchAddr, const Symbol &S) const override;
+ void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ bool usesOnlyLowPageBits(RelType Type) const override;
};
} // namespace
@@ -69,24 +70,39 @@ template <class ELFT> MIPS<ELFT>::MIPS() {
}
}
+template <class ELFT> uint32_t MIPS<ELFT>::calcEFlags() const {
+ return calcMipsEFlags<ELFT>();
+}
+
template <class ELFT>
-RelExpr MIPS<ELFT>::getRelExpr(uint32_t Type, const SymbolBody &S,
+RelExpr MIPS<ELFT>::getRelExpr(RelType Type, const Symbol &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
@@ -96,7 +112,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:
@@ -105,111 +138,171 @@ 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;
}
template <class ELFT>
-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);
+void MIPS<ELFT>::writeGotPlt(uint8_t *Buf, const Symbol &) const {
+ uint64_t VA = InX::Plt->getVA();
+ if (isMicroMips())
+ VA |= 1;
+ write32<ELFT::TargetEndianness>(Buf, VA);
}
-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);
- 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));
+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> static void writeMipsHi16(uint8_t *Loc, uint64_t V) {
+template <endianness E>
+static void writeRelocation(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
+ uint8_t Shift) {
uint32_t Instr = read32<E>(Loc);
- uint16_t Res = ((V + 0x8000) >> 16) & 0xffff;
- write32<E>(Loc, (Instr & 0xffff0000) | Res);
+ uint32_t Mask = 0xffffffff >> (32 - BitsSize);
+ uint32_t Data = (Instr & ~Mask) | ((V >> Shift) & Mask);
+ write32<E>(Loc, Data);
}
-template <endianness E> static void writeMipsHigher(uint8_t *Loc, uint64_t V) {
- uint32_t Instr = read32<E>(Loc);
- uint16_t Res = ((V + 0x80008000) >> 32) & 0xffff;
- write32<E>(Loc, (Instr & 0xffff0000) | Res);
-}
+template <endianness E>
+static void 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 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);
-}
+ writeRelocation<E>(Loc, V, BitsSize, Shift);
-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));
+ if (E == support::little)
+ std::swap(Words[0], Words[1]);
}
-template <class ELFT> static bool isMipsR6() {
- const auto &FirstObj = cast<ELFFileBase<ELFT>>(*Config->FirstElf);
- uint32_t Arch = FirstObj.getObj().getHeader()->e_flags & EF_MIPS_ARCH;
- return Arch == EF_MIPS_ARCH_32R6 || Arch == EF_MIPS_ARCH_64R6;
+template <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> void MIPS<ELFT>::writePltHeader(uint8_t *Buf) const {
const endianness E = ELFT::TargetEndianness;
+ if (isMicroMips()) {
+ uint64_t GotPlt = InX::GotPlt->getVA();
+ uint64_t Plt = InX::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)
write32<E>(Buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
write32<E>(Buf + 12, 0x030ec023); // subu $24, $24, $14
+ write32<E>(Buf + 16, 0x03e07825); // move $15, $31
+ write32<E>(Buf + 20, 0x0018c082); // srl $24, $24, 2
+ } else if (ELFT::Is64Bits) {
+ write32<E>(Buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
+ write32<E>(Buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14)
+ write32<E>(Buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
+ write32<E>(Buf + 12, 0x030ec023); // subu $24, $24, $14
+ write32<E>(Buf + 16, 0x03e07825); // move $15, $31
+ write32<E>(Buf + 20, 0x0018c0c2); // srl $24, $24, 3
} else {
write32<E>(Buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0])
write32<E>(Buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28)
write32<E>(Buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0])
write32<E>(Buf + 12, 0x031cc023); // subu $24, $24, $28
+ write32<E>(Buf + 16, 0x03e07825); // move $15, $31
+ write32<E>(Buf + 20, 0x0018c082); // srl $24, $24, 2
}
- write32<E>(Buf + 16, 0x03e07825); // move $15, $31
- write32<E>(Buf + 20, 0x0018c082); // srl $24, $24, 2
write32<E>(Buf + 24, 0x0320f809); // jalr $25
write32<E>(Buf + 28, 0x2718fffe); // subu $24, $24, 2
uint64_t 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>
@@ -217,25 +310,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,
- const SymbolBody &S) const {
+bool MIPS<ELFT>::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
+ uint64_t BranchAddr, const Symbol &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)
@@ -243,18 +356,16 @@ bool MIPS<ELFT>::needsThunk(RelExpr Expr, uint32_t Type, const InputFile *File,
// If current file has PIC code, LA25 stub is not required.
if (F->getObj().getHeader()->e_flags & EF_MIPS_PIC)
return false;
- auto *D = dyn_cast<DefinedRegular>(&S);
+ auto *D = dyn_cast<Defined>(&S);
// LA25 is required if target file has PIC code
// or target symbol is a PIC symbol.
- return D && D->isMipsPIC<ELFT>();
+ return D && isMipsPIC<ELFT>(D);
}
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:
@@ -264,7 +375,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:
@@ -273,21 +388,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,
@@ -300,32 +447,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:
@@ -339,36 +497,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);
- writeMipsLo16<E>(Loc, Val);
+ 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);
+ 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:
@@ -376,40 +563,107 @@ 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;
+}
+
+// Return true if the symbol is a PIC function.
+template <class ELFT> bool elf::isMipsPIC(const Defined *Sym) {
+ typedef typename ELFT::Ehdr Elf_Ehdr;
+ if (!Sym->Section || !Sym->isFunc())
+ return false;
+
+ auto *Sec = cast<InputSectionBase>(Sym->Section);
+ const Elf_Ehdr *Hdr = Sec->template getFile<ELFT>()->getObj().getHeader();
+ return (Sym->StOther & STO_MIPS_MIPS16) == STO_MIPS_PIC ||
+ (Hdr->e_flags & EF_MIPS_PIC);
}
template <class ELFT> TargetInfo *elf::getMipsTargetInfo() {
@@ -421,3 +675,8 @@ template TargetInfo *elf::getMipsTargetInfo<ELF32LE>();
template TargetInfo *elf::getMipsTargetInfo<ELF32BE>();
template TargetInfo *elf::getMipsTargetInfo<ELF64LE>();
template TargetInfo *elf::getMipsTargetInfo<ELF64BE>();
+
+template bool elf::isMipsPIC<ELF32LE>(const Defined *);
+template bool elf::isMipsPIC<ELF32BE>(const Defined *);
+template bool elf::isMipsPIC<ELF64LE>(const Defined *);
+template bool elf::isMipsPIC<ELF64BE>(const Defined *);
diff --git a/ELF/Arch/MipsArchTree.cpp b/ELF/Arch/MipsArchTree.cpp
index 3d1dc1daf0c1..754a47001579 100644
--- a/ELF/Arch/MipsArchTree.cpp
+++ b/ELF/Arch/MipsArchTree.cpp
@@ -11,11 +11,11 @@
//
//===---------------------------------------------------------------------===//
-#include "Error.h"
#include "InputFiles.h"
#include "SymbolTable.h"
#include "Writer.h"
+#include "lld/Common/ErrorHandler.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Object/ELF.h"
#include "llvm/Support/MipsABIFlags.h"
@@ -34,7 +34,7 @@ struct ArchTreeEdge {
};
struct FileFlags {
- StringRef Filename;
+ InputFile *File;
uint32_t Flags;
};
} // namespace
@@ -73,17 +73,17 @@ static void checkFlags(ArrayRef<FileFlags> Files) {
uint32_t ABI2 = F.Flags & (EF_MIPS_ABI | EF_MIPS_ABI2);
if (ABI != ABI2)
error("target ABI '" + getAbiName(ABI) + "' is incompatible with '" +
- getAbiName(ABI2) + "': " + F.Filename);
+ getAbiName(ABI2) + "': " + toString(F.File));
bool Nan2 = F.Flags & EF_MIPS_NAN2008;
if (Nan != Nan2)
error("target -mnan=" + getNanName(Nan) + " is incompatible with -mnan=" +
- getNanName(Nan2) + ": " + F.Filename);
+ getNanName(Nan2) + ": " + toString(F.File));
bool Fp2 = F.Flags & EF_MIPS_FP64;
if (Fp != Fp2)
error("target -mfp" + getFpName(Fp) + " is incompatible with -mfp" +
- getFpName(Fp2) + ": " + F.Filename);
+ getFpName(Fp2) + ": " + toString(F.File));
}
}
@@ -102,9 +102,11 @@ static uint32_t getPicFlags(ArrayRef<FileFlags> Files) {
for (const FileFlags &F : Files.slice(1)) {
bool IsPic2 = F.Flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
if (IsPic && !IsPic2)
- warn("linking abicalls code with non-abicalls file: " + F.Filename);
+ warn("linking abicalls code " + toString(Files[0].File) +
+ " with non-abicalls file: " + toString(F.File));
if (!IsPic && IsPic2)
- warn("linking non-abicalls code with abicalls file: " + F.Filename);
+ warn("linking non-abicalls code " + toString(Files[0].File) +
+ " with abicalls file: " + toString(F.File));
}
// Compute the result PIC/non-PIC flag.
@@ -221,10 +223,6 @@ static StringRef getMachName(uint32_t Flags) {
}
static StringRef getArchName(uint32_t Flags) {
- StringRef S = getMachName(Flags);
- if (!S.empty())
- return S;
-
switch (Flags & EF_MIPS_ARCH) {
case EF_MIPS_ARCH_1:
return "mips1";
@@ -253,6 +251,14 @@ static StringRef getArchName(uint32_t Flags) {
}
}
+static std::string getFullArchName(uint32_t Flags) {
+ StringRef Arch = getArchName(Flags);
+ StringRef Mach = getMachName(Flags);
+ if (Mach.empty())
+ return Arch.str();
+ return (Arch + " (" + Mach + ")").str();
+}
+
// There are (arguably too) many MIPS ISAs out there. Their relationships
// can be represented as a forest. If all input files have ISAs which
// reachable by repeated proceeding from the single child to the parent,
@@ -272,8 +278,9 @@ static uint32_t getArchFlags(ArrayRef<FileFlags> Files) {
if (isArchMatched(New, Ret))
continue;
if (!isArchMatched(Ret, New)) {
- error("target ISA '" + getArchName(Ret) + "' is incompatible with '" +
- getArchName(New) + "': " + F.Filename);
+ error("incompatible target ISA:\n>>> " + toString(Files[0].File) + ": " +
+ getFullArchName(Ret) + "\n>>> " + toString(F.File) + ": " +
+ getFullArchName(New));
return 0;
}
Ret = New;
@@ -281,10 +288,10 @@ 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, cast<ObjFile<ELFT>>(F)->getObj().getHeader()->e_flags});
if (V.empty())
return 0;
checkFlags(V);
@@ -363,7 +370,14 @@ 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::isMicroMips() { return Config->EFlags & EF_MIPS_MICROMIPS; }
+
+bool elf::isMipsR6() {
+ uint32_t Arch = Config->EFlags & 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 19e10729a00e..6af0df331df6 100644
--- a/ELF/Arch/PPC.cpp
+++ b/ELF/Arch/PPC.cpp
@@ -7,9 +7,9 @@
//
//===----------------------------------------------------------------------===//
-#include "Error.h"
#include "Symbols.h"
#include "Target.h"
+#include "lld/Common/ErrorHandler.h"
#include "llvm/Support/Endian.h"
using namespace llvm;
@@ -22,17 +22,33 @@ namespace {
class PPC final : public TargetInfo {
public:
PPC() { GotBaseSymOff = 0x8000; }
- 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 Symbol &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 Symbol &S,
+ const uint8_t *Loc) const {
+ switch (Type) {
+ case R_PPC_REL24:
+ case R_PPC_REL32:
+ return R_PC;
+ case R_PPC_PLTREL24:
+ return R_PLT_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);
break;
+ case R_PPC_ADDR16_HI:
+ write16be(Loc, Val >> 16);
+ break;
case R_PPC_ADDR16_LO:
write16be(Loc, Val);
break;
@@ -40,6 +56,7 @@ void PPC::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
case R_PPC_REL32:
write32be(Loc, Val);
break;
+ case R_PPC_PLTREL24:
case R_PPC_REL24:
write32be(Loc, read32be(Loc) | (Val & 0x3FFFFFC));
break;
@@ -48,17 +65,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 bf414d75bec7..ac4021b5918d 100644
--- a/ELF/Arch/PPC64.cpp
+++ b/ELF/Arch/PPC64.cpp
@@ -7,10 +7,10 @@
//
//===----------------------------------------------------------------------===//
-#include "Error.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
+#include "lld/Common/ErrorHandler.h"
#include "llvm/Support/Endian.h"
using namespace llvm;
@@ -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 Symbol &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 Symbol &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
index 1f977c1e9cf2..d9d6e1390407 100644
--- a/ELF/Arch/SPARCV9.cpp
+++ b/ELF/Arch/SPARCV9.cpp
@@ -7,11 +7,11 @@
//
//===----------------------------------------------------------------------===//
-#include "Error.h"
#include "InputFiles.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
+#include "lld/Common/ErrorHandler.h"
#include "llvm/Support/Endian.h"
using namespace llvm;
@@ -24,11 +24,11 @@ namespace {
class SPARCV9 final : public TargetInfo {
public:
SPARCV9();
- RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
+ RelExpr getRelExpr(RelType Type, const Symbol &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, uint32_t Type, uint64_t Val) const override;
+ void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
};
} // namespace
@@ -46,7 +46,7 @@ SPARCV9::SPARCV9() {
DefaultImageBase = 0x100000;
}
-RelExpr SPARCV9::getRelExpr(uint32_t Type, const SymbolBody &S,
+RelExpr SPARCV9::getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const {
switch (Type) {
case R_SPARC_32:
@@ -68,12 +68,11 @@ RelExpr SPARCV9::getRelExpr(uint32_t Type, const SymbolBody &S,
case R_SPARC_NONE:
return R_NONE;
default:
- error(toString(S.File) + ": unknown relocation type: " + toString(Type));
- return R_HINT;
+ return R_INVALID;
}
}
-void SPARCV9::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
+void SPARCV9::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
switch (Type) {
case R_SPARC_32:
case R_SPARC_UA32:
diff --git a/ELF/Arch/X86.cpp b/ELF/Arch/X86.cpp
index a1e9bcaf1b12..fc848917d4e9 100644
--- a/ELF/Arch/X86.cpp
+++ b/ELF/Arch/X86.cpp
@@ -7,11 +7,11 @@
//
//===----------------------------------------------------------------------===//
-#include "Error.h"
#include "InputFiles.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
+#include "lld/Common/ErrorHandler.h"
#include "llvm/Support/Endian.h"
using namespace llvm;
@@ -24,24 +24,24 @@ namespace {
class X86 final : public TargetInfo {
public:
X86();
- RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
+ RelExpr getRelExpr(RelType Type, const Symbol &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;
- void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
- void writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const override;
+ RelType getDynRel(RelType Type) const override;
+ void writeGotPlt(uint8_t *Buf, const Symbol &S) const override;
+ void writeIgotPlt(uint8_t *Buf, const Symbol &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
@@ -63,7 +63,9 @@ X86::X86() {
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 Symbol &S,
const uint8_t *Loc) const {
switch (Type) {
case R_386_8:
@@ -87,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:
@@ -116,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:
@@ -137,18 +156,18 @@ void X86::writeGotPltHeader(uint8_t *Buf) const {
write32le(Buf, InX::Dynamic->getVA());
}
-void X86::writeGotPlt(uint8_t *Buf, const SymbolBody &S) const {
+void X86::writeGotPlt(uint8_t *Buf, const Symbol &S) const {
// Entries in .got.plt initially points back to the corresponding
// PLT entries with a fixed offset to skip the first instruction.
write32le(Buf, S.getPltVA() + 6);
}
-void X86::writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const {
+void X86::writeIgotPlt(uint8_t *Buf, const Symbol &S) const {
// An x86 entry is the address of the ifunc resolver function.
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)
@@ -208,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);
@@ -228,15 +245,17 @@ 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 {
- // 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.
+void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
switch (Type) {
case R_386_8:
+ // 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.
checkUInt<8>(Loc, Val, Type);
*Loc = Val;
break;
@@ -262,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
@@ -283,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
@@ -300,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
@@ -337,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 10179f57ee93..14e354b9f4fb 100644
--- a/ELF/Arch/X86_64.cpp
+++ b/ELF/Arch/X86_64.cpp
@@ -7,11 +7,11 @@
//
//===----------------------------------------------------------------------===//
-#include "Error.h"
#include "InputFiles.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
+#include "lld/Common/ErrorHandler.h"
#include "llvm/Object/ELF.h"
#include "llvm/Support/Endian.h"
@@ -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 Symbol &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 writeGotPlt(uint8_t *Buf, const Symbol &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,
@@ -73,7 +73,7 @@ template <class ELFT> X86_64<ELFT>::X86_64() {
}
template <class ELFT>
-RelExpr X86_64<ELFT>::getRelExpr(uint32_t Type, const SymbolBody &S,
+RelExpr X86_64<ELFT>::getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const {
switch (Type) {
case R_X86_64_8:
@@ -109,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;
}
}
@@ -123,8 +122,8 @@ 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.
+void X86_64<ELFT>::writeGotPlt(uint8_t *Buf, const Symbol &S) const {
+ // See comments in X86::writeGotPlt.
write32le(Buf, S.getPltVA() + 6);
}
@@ -157,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
@@ -186,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
@@ -211,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;
@@ -254,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
@@ -283,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);
@@ -323,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/Bits.h b/ELF/Bits.h
new file mode 100644
index 000000000000..13d40322265e
--- /dev/null
+++ b/ELF/Bits.h
@@ -0,0 +1,35 @@
+//===- Bits.h ---------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_ELF_BITS_H
+#define LLD_ELF_BITS_H
+
+#include "Config.h"
+#include "llvm/Support/Endian.h"
+
+namespace lld {
+namespace elf {
+
+inline uint64_t readUint(uint8_t *Buf) {
+ if (Config->Is64)
+ return llvm::support::endian::read64(Buf, Config->Endianness);
+ return llvm::support::endian::read32(Buf, Config->Endianness);
+}
+
+inline void writeUint(uint8_t *Buf, uint64_t Val) {
+ if (Config->Is64)
+ llvm::support::endian::write64(Buf, Val, Config->Endianness);
+ else
+ llvm::support::endian::write32(Buf, Val, Config->Endianness);
+}
+
+} // namespace elf
+} // namespace lld
+
+#endif
diff --git a/ELF/CMakeLists.txt b/ELF/CMakeLists.txt
index 77243bd494d1..7ec837841315 100644
--- a/ELF/CMakeLists.txt
+++ b/ELF/CMakeLists.txt
@@ -7,6 +7,7 @@ if(NOT LLD_BUILT_STANDALONE)
endif()
add_lld_library(lldELF
+ AArch64ErrataFix.cpp
Arch/AArch64.cpp
Arch/AMDGPU.cpp
Arch/ARM.cpp
@@ -21,7 +22,6 @@ add_lld_library(lldELF
Driver.cpp
DriverUtils.cpp
EhFrame.cpp
- Error.cpp
Filesystem.cpp
GdbIndex.cpp
ICF.cpp
@@ -45,28 +45,17 @@ 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 23627dd812db..74c325cb7cb1 100644
--- a/ELF/Config.h
+++ b/ELF/Config.h
@@ -24,7 +24,6 @@ namespace lld {
namespace elf {
class InputFile;
-struct Symbol;
enum ELFKind {
ELFNoneKind,
@@ -44,7 +43,10 @@ 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 --orphan-handling.
+enum class OrphanHandlingPolicy { Place, Warn, Error };
// For --sort-section and linkerscript sorting rules.
enum class SortSectionPolicy { Default, None, Alignment, Name, Priority };
@@ -67,21 +69,15 @@ struct VersionDefinition {
size_t NameOff = 0; // Offset in the string table
};
-// Structure for mapping renamed symbols
-struct RenamedSymbol {
- Symbol *Target;
- uint8_t OriginalBinding;
-};
-
// 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;
@@ -103,15 +99,18 @@ struct Configuration {
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 AndroidPackDynRelocs = false;
+ bool ARMHasBlx = false;
+ bool ARMHasMovtMovw = false;
+ bool ARMJ1J2BranchEncoding = false;
bool AsNeeded = false;
bool Bsymbolic;
bool BsymbolicFunctions;
- bool ColorDiagnostics = false;
bool CompressDebugSections;
bool DefineCommon;
bool Demangle = true;
@@ -120,14 +119,19 @@ struct Configuration {
bool EmitRelocs;
bool EnableNewDtags;
bool ExportDynamic;
- bool FatalWarnings;
+ bool FixCortexA53Errata843419;
bool GcSections;
bool GdbIndex;
- bool GnuHash;
+ bool GnuHash = false;
+ bool HasDynamicList = false;
+ bool HasDynSymTab;
bool ICF;
+ bool ICFData;
+ bool MergeArmExidx;
bool MipsN32Abi = false;
bool NoGnuUnique;
bool NoUndefinedVersion;
+ bool NoinhibitExec;
bool Nostdlib;
bool OFormatBinary;
bool Omagic;
@@ -139,9 +143,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;
@@ -159,6 +162,7 @@ struct Configuration {
bool ExitEarly;
bool ZWxneeded;
DiscardPolicy Discard;
+ OrphanHandlingPolicy OrphanHandling;
SortSectionPolicy SortSection;
StripPolicy Strip;
UnresolvedPolicy UnresolvedSymbols;
@@ -167,8 +171,7 @@ struct Configuration {
ELFKind EKind = ELFNoneKind;
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;
@@ -206,6 +209,9 @@ struct Configuration {
// if that's true.)
bool IsMips64EL;
+ // Holds set of ELF header flags for the target.
+ uint32_t EFlags = 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 47a50bb725e7..2b6925031b07 100644
--- a/ELF/Driver.cpp
+++ b/ELF/Driver.cpp
@@ -25,23 +25,25 @@
#include "Driver.h"
#include "Config.h"
-#include "Error.h"
#include "Filesystem.h"
#include "ICF.h"
#include "InputFiles.h"
#include "InputSection.h"
#include "LinkerScript.h"
-#include "Memory.h"
#include "OutputSections.h"
#include "ScriptParser.h"
#include "Strings.h"
#include "SymbolTable.h"
+#include "Symbols.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/Args.h"
+#include "lld/Common/Driver.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.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"
@@ -64,27 +66,40 @@ using namespace lld::elf;
Configuration *elf::Config;
LinkerDriver *elf::Driver;
-BumpPtrAllocator elf::BAlloc;
-StringSaver elf::Saver{BAlloc};
-std::vector<SpecificAllocBase *> elf::SpecificAllocBase::Instances;
-
static void setConfigs();
bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly,
raw_ostream &Error) {
- ErrorCount = 0;
- ErrorOS = &Error;
+ errorHandler().LogName = Args[0];
+ errorHandler().ErrorLimitExceededMsg =
+ "too many errors emitted, stopping now (use "
+ "-error-limit=0 to see all errors)";
+ errorHandler().ErrorOS = &Error;
+ errorHandler().ColorDiagnostics = Error.has_colors();
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);
+
+ // 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;
+ return !errorCount();
}
// Parses a linker -m option.
@@ -112,12 +127,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);
}
@@ -126,19 +137,22 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
std::vector<std::pair<MemoryBufferRef, uint64_t>> static getArchiveMembers(
MemoryBufferRef MB) {
std::unique_ptr<Archive> File =
- check(Archive::create(MB),
+ CHECK(Archive::create(MB),
MB.getBufferIdentifier() + ": failed to parse archive");
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() +
+ CHECK(COrErr, MB.getBufferIdentifier() +
": could not get the child of the archive");
MemoryBufferRef MBRef =
- check(C.getMemoryBufferRef(),
+ 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)
@@ -179,7 +193,7 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) {
}
std::unique_ptr<Archive> File =
- check(Archive::create(MBRef), Path + ": failed to parse archive");
+ CHECK(Archive::create(MBRef), Path + ": failed to parse archive");
// If an archive file has no symbol table, it is likely that a user
// is attempting LTO and using a default ar command that doesn't
@@ -187,7 +201,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;
}
@@ -216,7 +230,7 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) {
return;
default:
if (InLib)
- Files.push_back(make<LazyObjectFile>(MBRef, "", 0));
+ Files.push_back(make<LazyObjFile>(MBRef, "", 0));
else
Files.push_back(createObjectFile(MBRef));
}
@@ -256,6 +270,9 @@ static void checkOptions(opt::InputArgList &Args) {
if (Config->EMachine == EM_MIPS && Config->GnuHash)
error("the .gnu.hash section is not compatible with the MIPS target.");
+ if (Config->FixCortexA53Errata843419 && Config->EMachine != EM_AARCH64)
+ error("--fix-cortex-a53-843419 is only supported on AArch64 targets.");
+
if (Config->Pie && Config->Shared)
error("-shared and -pie may not be used together");
@@ -265,6 +282,9 @@ static void checkOptions(opt::InputArgList &Args) {
if (!Config->Shared && !Config->AuxiliaryList.empty())
error("-f may not be used without -shared");
+ if (!Config->Relocatable && !Config->DefineCommon)
+ error("-no-define-common not supported in non relocatable output");
+
if (Config->Relocatable) {
if (Config->Shared)
error("-r and -shared may not be used together");
@@ -277,16 +297,6 @@ static void checkOptions(opt::InputArgList &Args) {
}
}
-static int getInteger(opt::InputArgList &Args, unsigned Key, int Default) {
- int V = 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);
- }
- return V;
-}
-
static const char *getReproduceOption(opt::InputArgList &Args) {
if (auto *Arg = Args.getLastArg(OPT_reproduce))
return Arg->getValue();
@@ -300,26 +310,12 @@ static bool hasZOption(opt::InputArgList &Args, StringRef Key) {
return false;
}
-static uint64_t getZOptionValue(opt::InputArgList &Args, StringRef Key,
- uint64_t Default) {
- for (auto *Arg : Args.filtered(OPT_z)) {
- std::pair<StringRef, StringRef> KV = StringRef(Arg->getValue()).split('=');
- if (KV.first == Key) {
- uint64_t Result = Default;
- if (!to_integer(KV.second, Result))
- error("invalid " + Key + ": " + KV.second);
- return Result;
- }
- }
- return Default;
-}
-
void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) {
ELFOptTable Parser;
opt::InputArgList Args = Parser.parse(ArgsArr.slice(1));
// Interpret this flag early because error() depends on them.
- Config->ErrorLimit = getInteger(Args, OPT_error_limit, 20);
+ errorHandler().ErrorLimit = args::getInteger(Args, OPT_error_limit, 20);
// Handle -help
if (Args.hasArg(OPT_help)) {
@@ -345,13 +341,15 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) {
if (Args.hasArg(OPT_v) || Args.hasArg(OPT_version))
message(getLLDVersion() + " (compatible with GNU linkers)");
- // ld.bfd always exits after printing out the version string.
- // ld.gold proceeds if a given option is -v. Because gold's behavior
- // is more permissive than ld.bfd, we chose what gold does here.
+ // The behavior of -v or --version is a bit strange, but this is
+ // needed for compatibility with GNU linkers.
+ if (Args.hasArg(OPT_v) && !Args.hasArg(OPT_INPUT))
+ return;
if (Args.hasArg(OPT_version))
return;
Config->ExitEarly = CanExitEarly && !Args.hasArg(OPT_full_shutdown);
+ errorHandler().ExitEarly = Config->ExitEarly;
if (const char *Path = getReproduceOption(Args)) {
// Note that --reproduce is a debug option so you can ignore it
@@ -375,7 +373,7 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) {
inferMachineType();
setConfigs();
checkOptions(Args);
- if (ErrorCount)
+ if (errorCount())
return;
switch (Config->EKind) {
@@ -396,36 +394,19 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) {
}
}
-static bool getArg(opt::InputArgList &Args, unsigned K1, unsigned K2,
- bool Default) {
- if (auto *Arg = Args.getLastArg(K1, K2))
- return Arg->getOption().getID() == K1;
- return Default;
-}
-
-static std::vector<StringRef> getArgs(opt::InputArgList &Args, int Id) {
- std::vector<StringRef> V;
- for (auto *Arg : Args.filtered(Id))
- V.push_back(Arg->getValue());
- return V;
-}
-
static std::string getRpath(opt::InputArgList &Args) {
- std::vector<StringRef> V = getArgs(Args, OPT_rpath);
+ std::vector<StringRef> V = args::getStrings(Args, OPT_rpath);
return llvm::join(V.begin(), V.end(), ":");
}
// 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;
- UnresolvedPolicy ErrorOrWarn = getArg(Args, OPT_error_unresolved_symbols,
- OPT_warn_unresolved_symbols, true)
+ UnresolvedPolicy ErrorOrWarn = Args.hasFlag(OPT_error_unresolved_symbols,
+ OPT_warn_unresolved_symbols, true)
? UnresolvedPolicy::ReportError
: UnresolvedPolicy::Warn;
@@ -513,7 +494,7 @@ static StripPolicy getStrip(opt::InputArgList &Args) {
return StripPolicy::Debug;
}
-static uint64_t parseSectionAddress(StringRef S, opt::Arg *Arg) {
+static uint64_t parseSectionAddress(StringRef S, const opt::Arg &Arg) {
uint64_t VA = 0;
if (S.startswith("0x"))
S = S.drop_front(2);
@@ -528,15 +509,15 @@ static StringMap<uint64_t> getSectionStartMap(opt::InputArgList &Args) {
StringRef Name;
StringRef Addr;
std::tie(Name, Addr) = StringRef(Arg->getValue()).split('=');
- Ret[Name] = parseSectionAddress(Addr, Arg);
+ Ret[Name] = parseSectionAddress(Addr, *Arg);
}
if (auto *Arg = Args.getLastArg(OPT_Ttext))
- Ret[".text"] = parseSectionAddress(Arg->getValue(), Arg);
+ Ret[".text"] = parseSectionAddress(Arg->getValue(), *Arg);
if (auto *Arg = Args.getLastArg(OPT_Tdata))
- Ret[".data"] = parseSectionAddress(Arg->getValue(), Arg);
+ Ret[".data"] = parseSectionAddress(Arg->getValue(), *Arg);
if (auto *Arg = Args.getLastArg(OPT_Tbss))
- Ret[".bss"] = parseSectionAddress(Arg->getValue(), Arg);
+ Ret[".bss"] = parseSectionAddress(Arg->getValue(), *Arg);
return Ret;
}
@@ -551,15 +532,15 @@ 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};
+static OrphanHandlingPolicy getOrphanHandling(opt::InputArgList &Args) {
+ StringRef S = Args.getLastArgValue(OPT_orphan_handling, "place");
+ if (S == "warn")
+ return OrphanHandlingPolicy::Warn;
+ if (S == "error")
+ return OrphanHandlingPolicy::Error;
+ if (S != "place")
+ error("unknown --orphan-handling mode: " + S);
+ return OrphanHandlingPolicy::Place;
}
// Parse --build-id or --build-id=<style>. We handle "tree" as a
@@ -589,19 +570,6 @@ getBuildId(opt::InputArgList &Args) {
return {BuildIdKind::None, {}};
}
-static std::vector<StringRef> getLines(MemoryBufferRef MB) {
- SmallVector<StringRef, 0> Arr;
- MB.getBuffer().split(Arr, '\n');
-
- std::vector<StringRef> Ret;
- for (StringRef S : Arr) {
- S = S.trim();
- if (!S.empty())
- Ret.push_back(S);
- }
- return Ret;
-}
-
static bool getCompressDebugSections(opt::InputArgList &Args) {
StringRef S = Args.getLastArgValue(OPT_compress_debug_sections, "none");
if (S == "none")
@@ -613,53 +581,70 @@ 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->AuxiliaryList = getArgs(Args, OPT_auxiliary);
+ Config->AllowMultipleDefinition =
+ Args.hasArg(OPT_allow_multiple_definition) || hasZOption(Args, "muldefs");
+ Config->AuxiliaryList = args::getStrings(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));
- Config->Demangle = getArg(Args, OPT_demangle, OPT_no_demangle, true);
+ Config->DefineCommon = Args.hasFlag(OPT_define_common, OPT_no_define_common,
+ !Args.hasArg(OPT_relocatable));
+ Config->Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, true);
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 =
+ Args.hasFlag(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);
Config->ExportDynamic =
- 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);
+ Args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false);
+ errorHandler().FatalWarnings =
+ Args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false);
+ Config->FilterList = args::getStrings(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 = getArg(Args, OPT_icf_all, OPT_icf_none, false);
+ Config->FixCortexA53Errata843419 = Args.hasArg(OPT_fix_cortex_a53_843419);
+ Config->GcSections = Args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false);
+ Config->GdbIndex = Args.hasFlag(OPT_gdb_index, OPT_no_gdb_index, false);
+ Config->ICF = Args.hasFlag(OPT_icf_all, OPT_icf_none, false);
+ Config->ICFData = Args.hasArg(OPT_icf_data);
Config->Init = Args.getLastArgValue(OPT_init, "_init");
Config->LTOAAPipeline = Args.getLastArgValue(OPT_lto_aa_pipeline);
Config->LTONewPmPasses = Args.getLastArgValue(OPT_lto_newpm_passes);
- Config->LTOO = getInteger(Args, OPT_lto_O, 2);
- Config->LTOPartitions = getInteger(Args, OPT_lto_partitions, 1);
+ Config->LTOO = args::getInteger(Args, OPT_lto_O, 2);
+ Config->LTOPartitions = args::getInteger(Args, OPT_lto_partitions, 1);
Config->MapFile = Args.getLastArgValue(OPT_Map);
Config->NoGnuUnique = Args.hasArg(OPT_no_gnu_unique);
+ Config->MergeArmExidx =
+ Args.hasFlag(OPT_merge_exidx_entries, OPT_no_merge_exidx_entries, true);
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);
+ Config->Omagic = Args.hasFlag(OPT_omagic, OPT_no_omagic, false);
Config->OptRemarksFilename = Args.getLastArgValue(OPT_opt_remarks_filename);
Config->OptRemarksWithHotness = Args.hasArg(OPT_opt_remarks_with_hotness);
- Config->Optimize = getInteger(Args, OPT_O, 1);
+ Config->Optimize = args::getInteger(Args, OPT_O, 1);
+ Config->OrphanHandling = getOrphanHandling(Args);
Config->OutputFile = Args.getLastArgValue(OPT_o);
- Config->Pie = getArg(Args, OPT_pie, OPT_nopie, false);
- Config->PrintGcSections = Args.hasArg(OPT_print_gc_sections);
+ Config->Pie = Args.hasFlag(OPT_pie, OPT_nopie, false);
+ Config->PrintGcSections =
+ Args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false);
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 = args::getStrings(Args, OPT_library_path);
Config->SectionStartMap = getSectionStartMap(Args);
Config->Shared = Args.hasArg(OPT_shared);
Config->SingleRoRx = Args.hasArg(OPT_no_rosegment);
@@ -667,18 +652,19 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
Config->SortSection = getSortSection(Args);
Config->Strip = getStrip(Args);
Config->Sysroot = Args.getLastArgValue(OPT_sysroot);
- Config->Target1Rel = getArg(Args, OPT_target1_rel, OPT_target1_abs, false);
+ Config->Target1Rel = Args.hasFlag(OPT_target1_rel, OPT_target1_abs, false);
Config->Target2 = getTarget2(Args);
Config->ThinLTOCacheDir = Args.getLastArgValue(OPT_thinlto_cache_dir);
- Config->ThinLTOCachePolicy = check(
+ Config->ThinLTOCachePolicy = CHECK(
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);
+ Config->ThinLTOJobs = args::getInteger(Args, OPT_thinlto_jobs, -1u);
+ ThreadsEnabled = Args.hasFlag(OPT_threads, OPT_no_threads, true);
Config->Trace = Args.hasArg(OPT_trace);
- Config->Undefined = getArgs(Args, OPT_undefined);
+ Config->Undefined = args::getStrings(Args, OPT_undefined);
Config->UnresolvedSymbols = getUnresolvedSymbolPolicy(Args);
Config->Verbose = Args.hasArg(OPT_verbose);
+ errorHandler().Verbose = Config->Verbose;
Config->WarnCommon = Args.hasArg(OPT_warn_common);
Config->ZCombreloc = !hasZOption(Args, "nocombreloc");
Config->ZExecstack = hasZOption(Args, "execstack");
@@ -689,20 +675,39 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
Config->ZOrigin = hasZOption(Args, "origin");
Config->ZRelro = !hasZOption(Args, "norelro");
Config->ZRodynamic = hasZOption(Args, "rodynamic");
- Config->ZStackSize = getZOptionValue(Args, "stack-size", 0);
+ Config->ZStackSize = args::getZOptionValue(Args, OPT_z, "stack-size", 0);
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);
@@ -710,6 +715,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 = "-";
@@ -720,25 +738,32 @@ 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_pack_dyn_relocs_eq)) {
+ StringRef S = Arg->getValue();
+ if (S == "android")
+ Config->AndroidPackDynRelocs = true;
+ else if (S != "none")
+ error("unknown -pack-dyn-relocs format: " + S);
+ }
+
if (auto *Arg = Args.getLastArg(OPT_symbol_ordering_file))
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
- Config->SymbolOrderingFile = getLines(*Buffer);
+ Config->SymbolOrderingFile = args::getLines(*Buffer);
// If --retain-symbol-file is used, we'll keep only the symbols listed in
// the file and discard all others.
if (auto *Arg = Args.getLastArg(OPT_retain_symbols_file)) {
Config->DefaultSymbolVersion = VER_NDX_LOCAL;
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
- for (StringRef S : getLines(*Buffer))
+ for (StringRef S : args::getLines(*Buffer))
Config->VersionScriptGlobals.push_back(
{S, /*IsExternCpp*/ false, /*HasWildcard*/ false});
}
bool HasExportDynamic =
- getArg(Args, OPT_export_dynamic, OPT_no_export_dynamic, false);
+ Args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false);
// Parses -dynamic-list and -export-dynamic-symbol. They make some
// symbols private. Note that -export-dynamic takes precedence over them
@@ -749,21 +774,11 @@ 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))
+ for (auto *Arg : Args.filtered(OPT_version_script))
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
readVersionScript(*Buffer);
}
@@ -804,17 +819,20 @@ 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);
+ if (Optional<std::string> Path = searchLinkerScript(Arg->getValue())) {
+ if (Optional<MemoryBufferRef> MB = readFile(*Path))
+ readLinkerScript(*MB);
+ break;
+ }
+ error(Twine("cannot find linker script ") + Arg->getValue());
break;
case OPT_as_needed:
Config->AsNeeded = true;
@@ -846,7 +864,7 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) {
}
}
- if (Files.empty() && ErrorCount == 0)
+ if (Files.empty() && errorCount() == 0)
error("no input files");
}
@@ -870,21 +888,20 @@ void LinkerDriver::inferMachineType() {
// Parse -z max-page-size=<value>. The default value is defined by
// each target.
static uint64_t getMaxPageSize(opt::InputArgList &Args) {
- uint64_t Val =
- getZOptionValue(Args, "max-page-size", Target->DefaultMaxPageSize);
+ uint64_t Val = args::getZOptionValue(Args, OPT_z, "max-page-size",
+ Target->DefaultMaxPageSize);
if (!isPowerOf2_64(Val))
error("max-page-size: value isn't a power of 2");
return Val;
}
// Parses -image-base option.
-static uint64_t getImageBase(opt::InputArgList &Args) {
- // Use default if no -image-base option is given.
- // Because we are using "Target" here, this function
- // has to be called after the variable is initialized.
+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;
@@ -897,21 +914,6 @@ static uint64_t getImageBase(opt::InputArgList &Args) {
return V;
}
-// Parses --defsym=alias option.
-static std::vector<std::pair<StringRef, StringRef>>
-getDefsym(opt::InputArgList &Args) {
- std::vector<std::pair<StringRef, StringRef>> Ret;
- for (auto *Arg : Args.filtered(OPT_defsym)) {
- StringRef From;
- StringRef To;
- std::tie(From, To) = StringRef(Arg->getValue()).split('=');
- if (!isValidCIdentifier(To))
- error("--defsym: symbol name expected, but got " + To);
- Ret.push_back({From, To});
- }
- return Ret;
-}
-
// Parses `--exclude-libs=lib,lib,...`.
// The library names may be delimited by commas or colons.
static DenseSet<StringRef> getExcludeLibs(opt::InputArgList &Args) {
@@ -930,33 +932,50 @@ static DenseSet<StringRef> getExcludeLibs(opt::InputArgList &Args) {
return Ret;
}
+static Optional<StringRef> getArchiveName(InputFile *File) {
+ if (isa<ArchiveFile>(File))
+ return File->getName();
+ if (!File->ArchiveName.empty())
+ return File->ArchiveName;
+ return None;
+}
+
// 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.
+template <class ELFT>
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 (Symbol *Sym : F->getSymbols())
- Sym->VersionId = VER_NDX_LOCAL;
+ if (Optional<StringRef> Archive = getArchiveName(File))
+ if (All || Libs.count(path::filename(*Archive)))
+ for (Symbol *Sym : File->getSymbols())
+ if (!Sym->isLocal())
+ Sym->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";
@@ -968,7 +987,7 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
error("cannot open output file " + Config->OutputFile + ": " + E.message());
if (auto E = tryCreateFile(Config->MapFile))
error("cannot open map file " + Config->MapFile + ": " + E.message());
- if (ErrorCount)
+ if (errorCount())
return;
// Use default entry point name if no name was given via the command
@@ -981,67 +1000,113 @@ 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);
+
+ // Process -defsym option.
+ for (auto *Arg : Args.filtered(OPT_defsym)) {
+ StringRef From;
+ StringRef To;
+ std::tie(From, To) = StringRef(Arg->getValue()).split('=');
+ readDefsym(From, MemoryBufferRef(To, "-defsym"));
+ }
+
+ // 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)
+ if (errorCount())
return;
- // Handle the `--undefined <sym>` options.
- Symtab.scanUndefinedFlags();
-
// Handle undefined symbols in DSOs.
- Symtab.scanShlibUndefined();
+ Symtab->scanShlibUndefined<ELFT>();
// Handle the -exclude-libs option.
if (Args.hasArg(OPT_exclude_libs))
- excludeLibs(Args, Files);
+ excludeLibs<ELFT>(Args, Files);
+
+ // Create ElfHeader early. We need a dummy section in
+ // addReservedSymbols to mark the created symbols as not absolute.
+ Out::ElfHeader = make<OutputSection>("", 0, SHF_ALLOC);
+ Out::ElfHeader->Size = sizeof(typename ELFT::Ehdr);
+
+ // We need to create some reserved symbols such as _end. Create them.
+ if (!Config->Relocatable)
+ addReservedSymbols<ELFT>();
// Apply version scripts.
- Symtab.scanVersionScript();
+ Symtab->scanVersionScript();
// Create wrapped symbols for -wrap option.
for (auto *Arg : Args.filtered(OPT_wrap))
- Symtab.addSymbolWrap(Arg->getValue());
-
- // Create alias symbols for -defsym option.
- for (std::pair<StringRef, StringRef> &Def : getDefsym(Args))
- Symtab.addSymbolAlias(Def.first, Def.second);
+ Symtab->addSymbolWrap<ELFT>(Arg->getValue());
- Symtab.addCombinedLTOObject();
- if (ErrorCount)
+ 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();
+ // Apply symbol renames for -wrap.
+ Symtab->applySymbolWrap();
// 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));
+ // We do not want to emit debug sections if --strip-all
+ // or -strip-debug are given.
+ if (Config->Strip != StripPolicy::None)
+ llvm::erase_if(InputSections, [](InputSectionBase *S) {
+ return S->Name.startswith(".debug") || S->Name.startswith(".zdebug");
+ });
+
+ Config->EFlags = Target->calcEFlags();
+
+ if (Config->EMachine == EM_ARM) {
+ // FIXME: These warnings can be removed when lld only uses these features
+ // when the input objects have been compiled with an architecture that
+ // supports them.
+ if (Config->ARMHasBlx == false)
+ warn("lld uses blx instruction, no object with architecture supporting "
+ "feature detected.");
+ if (Config->ARMJ1J2BranchEncoding == false)
+ warn("lld uses extended branch encoding, no object with architecture "
+ "supporting feature detected.");
+ if (Config->ARMHasMovtMovw == false)
+ warn("lld may use movt/movw, no object with architecture supporting "
+ "feature detected.");
+ }
+
// This adds a .comment section containing a version string. We have to add it
// before decompressAndMergeSections because the .comment section is a
// mergeable section.
@@ -1050,9 +1115,9 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
// 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 076dda7730ac..351d7926de71 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"
@@ -67,6 +67,7 @@ void printHelp(const char *Argv0);
std::string createResponseFile(const llvm::opt::InputArgList &Args);
llvm::Optional<std::string> findFromSearchPaths(StringRef Path);
+llvm::Optional<std::string> searchLinkerScript(StringRef Path);
llvm::Optional<std::string> searchLibrary(StringRef Path);
} // namespace elf
diff --git a/ELF/DriverUtils.cpp b/ELF/DriverUtils.cpp
index 5adb09176a3a..2f7c9228851a 100644
--- a/ELF/DriverUtils.cpp
+++ b/ELF/DriverUtils.cpp
@@ -14,10 +14,10 @@
//===----------------------------------------------------------------------===//
#include "Driver.h"
-#include "Error.h"
-#include "Memory.h"
-#include "lld/Config/Version.h"
-#include "lld/Core/Reproduce.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.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"
@@ -51,25 +51,26 @@ static const opt::OptTable::Info OptInfo[] = {
ELFOptTable::ELFOptTable() : OptTable(OptInfo) {}
-// Parse -color-diagnostics={auto,always,never} or -no-color-diagnostics.
-static bool getColorDiagnostics(opt::InputArgList &Args) {
+// Set color diagnostics according to -color-diagnostics={auto,always,never}
+// or -no-color-diagnostics flags.
+static void handleColorDiagnostics(opt::InputArgList &Args) {
auto *Arg = Args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq,
OPT_no_color_diagnostics);
if (!Arg)
- return ErrorOS->has_colors();
- if (Arg->getOption().getID() == OPT_color_diagnostics)
- return true;
- if (Arg->getOption().getID() == OPT_no_color_diagnostics)
- return false;
-
- StringRef S = Arg->getValue();
- if (S == "auto")
- return ErrorOS->has_colors();
- if (S == "always")
- return true;
- if (S != "never")
- error("unknown option: -color-diagnostics=" + S);
- return false;
+ return;
+ else if (Arg->getOption().getID() == OPT_color_diagnostics)
+ errorHandler().ColorDiagnostics = true;
+ else if (Arg->getOption().getID() == OPT_no_color_diagnostics)
+ errorHandler().ColorDiagnostics = false;
+ else {
+ StringRef S = Arg->getValue();
+ if (S == "always")
+ errorHandler().ColorDiagnostics = true;
+ else if (S == "never")
+ errorHandler().ColorDiagnostics = false;
+ else if (S != "auto")
+ error("unknown option: -color-diagnostics=" + S);
+ }
}
static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) {
@@ -103,9 +104,7 @@ opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> Argv) {
cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), Vec);
Args = this->ParseArgs(Vec, MissingIndex, MissingCount);
- // Interpret -color-diagnostics early so that error messages
- // for unknown flags are colored.
- Config->ColorDiagnostics = getColorDiagnostics(Args);
+ handleColorDiagnostics(Args);
if (MissingCount)
error(Twine(Args.getArgString(MissingIndex)) + ": missing argument");
@@ -115,8 +114,8 @@ opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> Argv) {
}
void elf::printHelp(const char *Argv0) {
- ELFOptTable Table;
- Table.PrintHelp(outs(), Argv0, "lld", false);
+ ELFOptTable().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 +137,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,17 +154,18 @@ 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_sysroot:
case OPT_version_script:
OS << Arg->getSpelling() << " " << quote(rewritePath(Arg->getValue()))
<< "\n";
break;
default:
- OS << toString(Arg) << "\n";
+ OS << toString(*Arg) << "\n";
}
}
return Data.str();
@@ -206,3 +207,12 @@ Optional<std::string> elf::searchLibrary(StringRef Name) {
}
return None;
}
+
+// If a linker script doesn't exist in the current directory, we also look for
+// the script in the '-L' search paths. This matches the behaviour of both '-T'
+// and linker script INPUT() directives in ld.bfd.
+Optional<std::string> elf::searchLinkerScript(StringRef Name) {
+ if (fs::exists(Name))
+ return Name.str();
+ return findFromSearchPaths(Name);
+}
diff --git a/ELF/EhFrame.cpp b/ELF/EhFrame.cpp
index c4e3f65c730e..62abc3973e7e 100644
--- a/ELF/EhFrame.cpp
+++ b/ELF/EhFrame.cpp
@@ -17,11 +17,12 @@
//===----------------------------------------------------------------------===//
#include "EhFrame.h"
-#include "Error.h"
+#include "Config.h"
#include "InputSection.h"
#include "Relocations.h"
#include "Strings.h"
+#include "lld/Common/ErrorHandler.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/Object/ELF.h"
#include "llvm/Support/Endian.h"
@@ -36,7 +37,7 @@ using namespace lld;
using namespace lld::elf;
namespace {
-template <class ELFT> class EhReader {
+class EhReader {
public:
EhReader(InputSectionBase *S, ArrayRef<uint8_t> D) : IS(S), D(D) {}
size_t readEhRecordSize();
@@ -45,7 +46,7 @@ public:
private:
template <class P> void failOn(const P *Loc, const Twine &Msg) {
fatal("corrupted .eh_frame: " + Msg + "\n>>> defined in " +
- IS->getObjMsg<ELFT>((const uint8_t *)Loc - IS->Data.data()));
+ IS->getObjMsg((const uint8_t *)Loc - IS->Data.data()));
}
uint8_t readByte();
@@ -59,22 +60,20 @@ private:
};
}
-template <class ELFT>
size_t elf::readEhRecordSize(InputSectionBase *S, size_t Off) {
- return EhReader<ELFT>(S, S->Data.slice(Off)).readEhRecordSize();
+ return EhReader(S, S->Data.slice(Off)).readEhRecordSize();
}
// .eh_frame section is a sequence of records. Each record starts with
// a 4 byte length field. This function reads the length.
-template <class ELFT> size_t EhReader<ELFT>::readEhRecordSize() {
- const endianness E = ELFT::TargetEndianness;
+size_t EhReader::readEhRecordSize() {
if (D.size() < 4)
failOn(D.data(), "CIE/FDE too small");
// First 4 bytes of CIE/FDE is the size of the record.
// If it is 0xFFFFFFFF, the next 8 bytes contain the size instead,
// but we do not support that format yet.
- uint64_t V = read32<E>(D.data());
+ uint64_t V = read32(D.data(), Config->Endianness);
if (V == UINT32_MAX)
failOn(D.data(), "CIE/FDE too large");
uint64_t Size = V + 4;
@@ -84,7 +83,7 @@ template <class ELFT> size_t EhReader<ELFT>::readEhRecordSize() {
}
// Read a byte and advance D by one byte.
-template <class ELFT> uint8_t EhReader<ELFT>::readByte() {
+uint8_t EhReader::readByte() {
if (D.empty())
failOn(D.data(), "unexpected end of CIE");
uint8_t B = D.front();
@@ -92,14 +91,14 @@ template <class ELFT> uint8_t EhReader<ELFT>::readByte() {
return B;
}
-template <class ELFT> void EhReader<ELFT>::skipBytes(size_t Count) {
+void EhReader::skipBytes(size_t Count) {
if (D.size() < Count)
failOn(D.data(), "CIE is too small");
D = D.slice(Count);
}
// Read a null-terminated string.
-template <class ELFT> StringRef EhReader<ELFT>::readString() {
+StringRef EhReader::readString() {
const uint8_t *End = std::find(D.begin(), D.end(), '\0');
if (End == D.end())
failOn(D.data(), "corrupted CIE (failed to read string)");
@@ -112,7 +111,7 @@ template <class ELFT> StringRef EhReader<ELFT>::readString() {
// Actual number is not of interest because only the runtime needs it.
// But we need to be at least able to skip it so that we can read
// the field that follows a LEB128 number.
-template <class ELFT> void EhReader<ELFT>::skipLeb128() {
+void EhReader::skipLeb128() {
const uint8_t *ErrPos = D.data();
while (!D.empty()) {
uint8_t Val = D.front();
@@ -141,7 +140,7 @@ static size_t getAugPSize(unsigned Enc) {
return 0;
}
-template <class ELFT> void EhReader<ELFT>::skipAugP() {
+void EhReader::skipAugP() {
uint8_t Enc = readByte();
if ((Enc & 0xf0) == DW_EH_PE_aligned)
failOn(D.data() - 1, "DW_EH_PE_aligned encoding is not supported");
@@ -153,12 +152,11 @@ template <class ELFT> void EhReader<ELFT>::skipAugP() {
D = D.slice(Size);
}
-template <class ELFT> uint8_t elf::getFdeEncoding(EhSectionPiece *P) {
- auto *IS = static_cast<InputSectionBase *>(P->ID);
- return EhReader<ELFT>(IS, P->data()).getFdeEncoding();
+uint8_t elf::getFdeEncoding(EhSectionPiece *P) {
+ return EhReader(P->Sec, P->data()).getFdeEncoding();
}
-template <class ELFT> uint8_t EhReader<ELFT>::getFdeEncoding() {
+uint8_t EhReader::getFdeEncoding() {
skipBytes(8);
int Version = readByte();
if (Version != 1 && Version != 3)
@@ -200,13 +198,3 @@ template <class ELFT> uint8_t EhReader<ELFT>::getFdeEncoding() {
}
return DW_EH_PE_absptr;
}
-
-template size_t elf::readEhRecordSize<ELF32LE>(InputSectionBase *S, size_t Off);
-template size_t elf::readEhRecordSize<ELF32BE>(InputSectionBase *S, size_t Off);
-template size_t elf::readEhRecordSize<ELF64LE>(InputSectionBase *S, size_t Off);
-template size_t elf::readEhRecordSize<ELF64BE>(InputSectionBase *S, size_t Off);
-
-template uint8_t elf::getFdeEncoding<ELF32LE>(EhSectionPiece *P);
-template uint8_t elf::getFdeEncoding<ELF32BE>(EhSectionPiece *P);
-template uint8_t elf::getFdeEncoding<ELF64LE>(EhSectionPiece *P);
-template uint8_t elf::getFdeEncoding<ELF64BE>(EhSectionPiece *P);
diff --git a/ELF/EhFrame.h b/ELF/EhFrame.h
index 07d1aaa3cbb3..5112891a911e 100644
--- a/ELF/EhFrame.h
+++ b/ELF/EhFrame.h
@@ -10,15 +10,15 @@
#ifndef LLD_ELF_EHFRAME_H
#define LLD_ELF_EHFRAME_H
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
namespace lld {
namespace elf {
class InputSectionBase;
struct EhSectionPiece;
-template <class ELFT> size_t readEhRecordSize(InputSectionBase *S, size_t Off);
-template <class ELFT> uint8_t getFdeEncoding(EhSectionPiece *P);
+size_t readEhRecordSize(InputSectionBase *S, size_t Off);
+uint8_t getFdeEncoding(EhSectionPiece *P);
} // namespace elf
} // namespace lld
diff --git a/ELF/Error.h b/ELF/Error.h
deleted file mode 100644
index 89bc2111b44e..000000000000
--- a/ELF/Error.h
+++ /dev/null
@@ -1,78 +0,0 @@
-//===- Error.h --------------------------------------------------*- C++ -*-===//
-//
-// The LLVM Linker
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// In LLD, we have three levels of errors: fatal, error or warn.
-//
-// Fatal makes the program exit immediately with an error message.
-// You shouldn't use it except for reporting a corrupted input file.
-//
-// Error prints out an error message and increment a global variable
-// ErrorCount to record the fact that we met an error condition. It does
-// not exit, so it is safe for a lld-as-a-library use case. It is generally
-// useful because it can report more than one error in a single run.
-//
-// Warn doesn't do anything but printing out a given message.
-//
-// It is not recommended to use llvm::outs() or llvm::errs() directly
-// in LLD because they are not thread-safe. The functions declared in
-// this file are mutually excluded, so you want to use them instead.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLD_ELF_ERROR_H
-#define LLD_ELF_ERROR_H
-
-#include "lld/Core/LLVM.h"
-
-#include "llvm/Support/Error.h"
-
-namespace lld {
-namespace elf {
-
-extern uint64_t ErrorCount;
-extern llvm::raw_ostream *ErrorOS;
-
-void log(const Twine &Msg);
-void message(const Twine &Msg);
-void warn(const Twine &Msg);
-void error(const Twine &Msg);
-LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg);
-
-LLVM_ATTRIBUTE_NORETURN void exitLld(int Val);
-
-// check() functions are convenient functions to strip errors
-// from error-or-value objects.
-template <class T> T check(ErrorOr<T> E) {
- if (auto EC = E.getError())
- fatal(EC.message());
- return std::move(*E);
-}
-
-template <class T> T check(Expected<T> E) {
- if (!E)
- fatal(llvm::toString(E.takeError()));
- return std::move(*E);
-}
-
-template <class T> T check(ErrorOr<T> E, const Twine &Prefix) {
- if (auto EC = E.getError())
- fatal(Prefix + ": " + EC.message());
- return std::move(*E);
-}
-
-template <class T> T check(Expected<T> E, const Twine &Prefix) {
- if (!E)
- fatal(Prefix + ": " + toString(E.takeError()));
- return std::move(*E);
-}
-
-} // namespace elf
-} // namespace lld
-
-#endif
diff --git a/ELF/Filesystem.cpp b/ELF/Filesystem.cpp
index d468ae0c618a..8d0b5d8a2f1c 100644
--- a/ELF/Filesystem.cpp
+++ b/ELF/Filesystem.cpp
@@ -13,8 +13,13 @@
#include "Filesystem.h"
#include "Config.h"
-#include "llvm/Support/FileSystem.h"
+#include "lld/Common/Threads.h"
+#include "llvm/Config/llvm-config.h"
#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/FileSystem.h"
+#if LLVM_ON_UNIX
+#include <unistd.h>
+#endif
#include <thread>
using namespace llvm;
@@ -35,27 +40,29 @@ using namespace lld::elf;
// Since LLD can link a 1 GB binary in about 5 seconds, that waste
// actually counts.
//
-// This function spawns a background thread to call unlink.
+// This function spawns a background thread to remove the file.
// The calling thread returns almost immediately.
void elf::unlinkAsync(StringRef Path) {
- if (!Config->Threads || !sys::fs::exists(Config->OutputFile) ||
- !sys::fs::is_regular_file(Config->OutputFile))
+// Removing a file is async on windows.
+#if defined(LLVM_ON_WIN32)
+ sys::fs::remove(Path);
+#else
+ if (!ThreadsEnabled || !sys::fs::exists(Path) ||
+ !sys::fs::is_regular_file(Path))
return;
- // First, rename Path to avoid race condition. We cannot remove
- // Path from a different thread because we are now going to create
- // Path as a new file. If we do that in a different thread, the new
- // thread can remove the new file.
- SmallString<128> TempPath;
- if (sys::fs::createUniqueFile(Path + "tmp%%%%%%%%", TempPath))
- return;
- if (sys::fs::rename(Path, TempPath)) {
- sys::fs::remove(TempPath);
- return;
- }
+ // We cannot just remove path from a different thread because we are now going
+ // to create path as a new file.
+ // Instead we open the file and unlink it on this thread. The unlink is fast
+ // since the open fd guarantees that it is not removing the last reference.
+ int FD;
+ std::error_code EC = sys::fs::openFileForRead(Path, FD);
+ sys::fs::remove(Path);
- // Remove TempPath in background.
- std::thread([=] { ::remove(TempPath.str().str().c_str()); }).detach();
+ // close and therefore remove TempPath in background.
+ if (!EC)
+ std::thread([=] { ::close(FD); }).detach();
+#endif
}
// Simulate file creation to see if Path is writable.
@@ -73,5 +80,7 @@ void elf::unlinkAsync(StringRef Path) {
std::error_code elf::tryCreateFile(StringRef Path) {
if (Path.empty())
return std::error_code();
- return FileOutputBuffer::create(Path, 1).getError();
+ if (Path == "-")
+ return std::error_code();
+ return errorToErrorCode(FileOutputBuffer::create(Path, 1).takeError());
}
diff --git a/ELF/Filesystem.h b/ELF/Filesystem.h
index dbeadac5a96b..987a74a6bcb6 100644
--- a/ELF/Filesystem.h
+++ b/ELF/Filesystem.h
@@ -10,7 +10,8 @@
#ifndef LLD_ELF_FILESYSTEM_H
#define LLD_ELF_FILESYSTEM_H
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
+#include <system_error>
namespace lld {
namespace elf {
diff --git a/ELF/GdbIndex.cpp b/ELF/GdbIndex.cpp
index 99e02d0025b0..d27b57f95938 100644
--- a/ELF/GdbIndex.cpp
+++ b/ELF/GdbIndex.cpp
@@ -15,7 +15,8 @@
//===----------------------------------------------------------------------===//
#include "GdbIndex.h"
-#include "Memory.h"
+#include "Symbols.h"
+#include "lld/Common/Memory.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h"
#include "llvm/Object/ELFObjectFile.h"
@@ -24,26 +25,77 @@ 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);
+ else if (Sec->Name == ".debug_str")
+ StrSection = 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);
- while (Table[I])
- I = (I + Step) & Mask;
- Table[I] = Sym;
+ // Broken debug info can point to a non-Defined symbol.
+ auto *DR = dyn_cast<Defined>(&File->getRelocTargetSym(Rel));
+ if (!DR) {
+ error("unsupported relocation target while parsing debug info");
+ return None;
}
+ uint64_t Val = DR->Value + getAddend<ELFT>(Rel);
+
+ // 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 bc024e6689ef..41ae9d793c11 100644
--- a/ELF/GdbIndex.h
+++ b/ELF/GdbIndex.h
@@ -19,61 +19,50 @@ 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 LLDDWARFSection final : public llvm::DWARFSection {
+ InputSectionBase *Sec = nullptr;
};
-// 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;
-};
-
-// 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 {
- InputSection *DebugInfoSec;
- 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;
+ StringRef StrSection;
-// 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 getFileName() const override { return ""; }
+ StringRef getCUIndexSection() const override { return ""; }
+ StringRef getAbbrevSection() const override { return AbbrevSection; }
+ StringRef getStringSection() const override { return StrSection; }
+ 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 09512a8b09d9..b1e12e0590d5 100644
--- a/ELF/ICF.cpp
+++ b/ELF/ICF.cpp
@@ -76,7 +76,8 @@
#include "ICF.h"
#include "Config.h"
#include "SymbolTable.h"
-#include "Threads.h"
+#include "Symbols.h"
+#include "lld/Common/Threads.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Object/ELF.h"
@@ -155,16 +156,20 @@ 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.
static bool isEligible(InputSection *S) {
+ // Don't merge read only data sections unless --icf-data was passed.
+ if (!(S->Flags & SHF_EXECINSTR) && !Config->ICFData)
+ return false;
+
// .init and .fini contains instructions that must be executed to
// initialize and finalize the process. They cannot and should not
// be merged.
- return S->Live && (S->Flags & SHF_ALLOC) && (S->Flags & SHF_EXECINSTR) &&
- !(S->Flags & SHF_WRITE) && S->Name != ".init" && S->Name != ".fini";
+ return S->Live && (S->Flags & SHF_ALLOC) && !(S->Flags & SHF_WRITE) &&
+ S->Name != ".init" && S->Name != ".fini";
}
// Split an equivalence class into smaller classes.
@@ -207,38 +212,49 @@ 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]);
- auto *DA = dyn_cast<DefinedRegular>(&SA);
- auto *DB = dyn_cast<DefinedRegular>(&SB);
+ Symbol &SA = SecA->template getFile<ELFT>()->getRelocTargetSym(RA[I]);
+ Symbol &SB = SecB->template getFile<ELFT>()->getRelocTargetSym(RB[I]);
+ if (&SA == &SB) {
+ if (AddA == AddB)
+ continue;
+ return false;
+ }
+
+ auto *DA = dyn_cast<Defined>(&SA);
+ auto *DB = dyn_cast<Defined>(&SB);
if (!DA || !DB)
return false;
// 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 +269,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,37 +294,39 @@ 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);
+ Symbol &SA = SecA->template getFile<ELFT>()->getRelocTargetSym(RA[I]);
+ Symbol &SB = SecB->template getFile<ELFT>()->getRelocTargetSym(RB[I]);
if (&SA == &SB)
- return true;
+ continue;
- auto *DA = cast<DefinedRegular>(&SA);
- auto *DB = cast<DefinedRegular>(&SB);
+ auto *DA = cast<Defined>(&SA);
+ auto *DB = cast<Defined>(&SB);
// We already dealt with absolute and non-InputSection symbols in
// 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 +371,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 +399,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/InputFiles.cpp b/ELF/InputFiles.cpp
index c609615fcc2e..1f68340c9428 100644
--- a/ELF/InputFiles.cpp
+++ b/ELF/InputFiles.cpp
@@ -8,13 +8,13 @@
//===----------------------------------------------------------------------===//
#include "InputFiles.h"
-#include "Error.h"
#include "InputSection.h"
#include "LinkerScript.h"
-#include "Memory.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "SyntheticSections.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/CodeGen/Analysis.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
@@ -23,6 +23,8 @@
#include "llvm/LTO/LTO.h"
#include "llvm/MC/StringTableBuilder.h"
#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Support/ARMAttributeParser.h"
+#include "llvm/Support/ARMBuildAttributes.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/TarWriter.h"
#include "llvm/Support/raw_ostream.h"
@@ -35,26 +37,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 LoadedObjectInfoHelper<ObjectInfo> {
-public:
- uint64_t getSectionLoadAddress(const object::SectionRef &Sec) const override {
- return static_cast<const ELFSectionRef &>(Sec).getOffset();
- }
-};
-}
-
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());
@@ -70,28 +69,88 @@ 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);
+template <class ELFT> void ObjFile<ELFT>::initializeDwarf() {
+ DWARFContext Dwarf(make_unique<LLDDwarfObj<ELFT>>(this));
+ const DWARFObject &Obj = Dwarf.getDWARFObj();
DwarfLine.reset(new DWARFDebugLine);
- DWARFDataExtractor LineData(Dwarf.getLineSection(), Config->IsLE,
+ 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
// CU (object file), so offset is always 0.
- DwarfLine->getOrParseLineTable(LineData, 0);
+ // FIXME: Provide the associated DWARFUnit if there is one. DWARF v5
+ // needs it in order to find indirect strings.
+ const DWARFDebugLine::LineTable *LT =
+ DwarfLine->getOrParseLineTable(LineData, 0, nullptr);
+
+ // Return if there is no debug information about CU available.
+ if (!Dwarf.getNumCompileUnits())
+ return;
+
+ // Loop over variable records and insert them to VariableLoc.
+ DWARFCompileUnit *CU = Dwarf.getCompileUnitAtIndex(0);
+ for (const auto &Entry : CU->dies()) {
+ DWARFDie Die(CU, &Entry);
+ // Skip all tags that are not variables.
+ if (Die.getTag() != dwarf::DW_TAG_variable)
+ continue;
+
+ // Skip if a local variable because we don't need them for generating error
+ // messages. In general, only non-local symbols can fail to be linked.
+ if (!dwarf::toUnsigned(Die.find(dwarf::DW_AT_external), 0))
+ continue;
+
+ // Get the source filename index for the variable.
+ unsigned File = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_file), 0);
+ if (!LT->hasFileAtIndex(File))
+ continue;
+
+ // Get the line number on which the variable is declared.
+ unsigned Line = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_line), 0);
+
+ // Get the name of the variable and add the collected information to
+ // VariableLoc. Usually Name is non-empty, but it can be empty if the input
+ // object file lacks some debug info.
+ StringRef Name = dwarf::toString(Die.find(dwarf::DW_AT_name), "");
+ if (!Name.empty())
+ VariableLoc.insert({Name, {File, Line}});
+ }
+}
+
+// Returns the pair of file name and line number describing location of data
+// object (variable, array, etc) definition.
+template <class ELFT>
+Optional<std::pair<std::string, unsigned>>
+ObjFile<ELFT>::getVariableLoc(StringRef Name) {
+ llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); });
+
+ // There is always only one CU so it's offset is 0.
+ const DWARFDebugLine::LineTable *LT = DwarfLine->getLineTable(0);
+ if (!LT)
+ return None;
+
+ // Return if we have no debug information about data object.
+ auto It = VariableLoc.find(Name);
+ if (It == VariableLoc.end())
+ return None;
+
+ // Take file name string from line table.
+ std::string FileName;
+ if (!LT->getFileNameByIndex(
+ It->second.first /* File */, nullptr,
+ DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, FileName))
+ return None;
+
+ return std::make_pair(FileName, It->second.second /*Line*/);
}
// 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) {
- llvm::call_once(InitDwarfLine, [this]() { initializeDwarfLine(); });
+Optional<DILineInfo> ObjFile<ELFT>::getDILineInfo(InputSectionBase *S,
+ uint64_t Offset) {
+ llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); });
// The offset to CU is 0.
const DWARFDebugLine::LineTable *Tbl = DwarfLine->getLineTable(0);
@@ -112,8 +171,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 "";
@@ -145,50 +203,41 @@ 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),
- toString(this));
+ return CHECK(getObj().getSectionIndex(&Sym, ELFSyms, SymtabSHNDX), this);
}
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), this);
+ if (FirstNonLocal == 0 || FirstNonLocal > ELFSyms.size())
fatal(toString(this) + ": invalid sh_info in symbol table");
- StringTable = check(getObj().getStringTableForSymtab(*Symtab, Sections),
- toString(this));
+ StringTable =
+ CHECK(getObj().getStringTableForSymtab(*Symtab, Sections), this);
}
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<Symbol *> 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();
@@ -198,19 +247,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)));
+ Sections, CHECK(object::getSection<ELFT>(Sections, Sec.sh_link), this));
- const Elf_Sym *Sym = check(
- object::getSymbol<ELFT>(this->Symbols, Sec.sh_info), toString(this));
- StringRef Signature = check(Sym->getName(this->StringTable), toString(this));
+ const Elf_Sym *Sym =
+ CHECK(object::getSymbol<ELFT>(this->ELFSyms, Sec.sh_info), this);
+ StringRef Signature = CHECK(Sym->getName(this->StringTable), this);
// As a special case, if a symbol is a section symbol and has no name,
// we use a section name as a signature.
@@ -225,32 +272,22 @@ 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));
+ ArrayRef<Elf_Word> Entries =
+ CHECK(Obj.template getSectionContentsAsArray<Elf_Word>(&Sec), this);
if (Entries.empty() || Entries[0] != GRP_COMDAT)
fatal(toString(this) + ": unsupported SHT_GROUP format");
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)
return false;
- // Do not merge sections if generating a relocatable object. It makes
- // the code simpler because we do not need to update relocation addends
- // to reflect changes introduced by merging. Instead of that we write
- // such "merge" sections into separate OutputSections and keep SHF_MERGE
- // / SHF_STRINGS flags and sh_entsize value to be able to perform merging
- // later during a final linking.
- if (Config->Relocatable)
- return false;
-
// A mergeable section with size 0 is useless because they don't have
// any data to merge. A mergeable string section with size 0 can be
// argued as invalid because it doesn't end with a null character.
@@ -276,29 +313,19 @@ bool elf::ObjectFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
if (Flags & SHF_WRITE)
fatal(toString(this) + ": writable SHF_MERGE section is not supported");
- // Don't try to merge if the alignment is larger than the sh_entsize and this
- // is not SHF_STRINGS.
- //
- // Since this is not a SHF_STRINGS, we would need to pad after every entity.
- // It would be equivalent for the producer of the .o to just set a larger
- // sh_entsize.
- if (Flags & SHF_STRINGS)
- return true;
-
- return Sec.sh_addralign <= EntSize;
+ return true;
}
template <class ELFT>
-void elf::ObjectFile<ELFT>::initializeSections(
+void ObjFile<ELFT>::initializeSections(
DenseSet<CachedHashStringRef> &ComdatGroups) {
const ELFFile<ELFT> &Obj = this->getObj();
- ArrayRef<Elf_Shdr> ObjSections =
- check(this->getObj().sections(), toString(this));
+ ArrayRef<Elf_Shdr> ObjSections = CHECK(this->getObj().sections(), this);
uint64_t Size = ObjSections.size();
this->Sections.resize(Size);
this->SectionStringTable =
- check(Obj.getSectionStringTable(ObjSections), toString(this));
+ CHECK(Obj.getSectionStringTable(ObjSections), this);
for (size_t I = 0, E = ObjSections.size(); I < E; I++) {
if (this->Sections[I] == &InputSection::Discarded)
@@ -344,8 +371,7 @@ void elf::ObjectFile<ELFT>::initializeSections(
this->initSymtab(ObjSections, &Sec);
break;
case SHT_SYMTAB_SHNDX:
- this->SymtabSHNDX =
- check(Obj.getSHNDXTable(Sec, ObjSections), toString(this));
+ this->SymtabSHNDX = CHECK(Obj.getSHNDXTable(Sec, ObjSections), this);
break;
case SHT_STRTAB:
case SHT_NULL:
@@ -361,13 +387,55 @@ 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]));
}
}
}
+// The ARM support in lld makes some use of instructions that are not available
+// on all ARM architectures. Namely:
+// - Use of BLX instruction for interworking between ARM and Thumb state.
+// - Use of the extended Thumb branch encoding in relocation.
+// - Use of the MOVT/MOVW instructions in Thumb Thunks.
+// The ARM Attributes section contains information about the architecture chosen
+// at compile time. We follow the convention that if at least one input object
+// is compiled with an architecture that supports these features then lld is
+// permitted to use them.
+static void updateSupportedARMFeatures(const ARMAttributeParser &Attributes) {
+ if (!Attributes.hasAttribute(ARMBuildAttrs::CPU_arch))
+ return;
+ auto Arch = Attributes.getAttributeValue(ARMBuildAttrs::CPU_arch);
+ switch (Arch) {
+ case ARMBuildAttrs::Pre_v4:
+ case ARMBuildAttrs::v4:
+ case ARMBuildAttrs::v4T:
+ // Architectures prior to v5 do not support BLX instruction
+ break;
+ case ARMBuildAttrs::v5T:
+ case ARMBuildAttrs::v5TE:
+ case ARMBuildAttrs::v5TEJ:
+ case ARMBuildAttrs::v6:
+ case ARMBuildAttrs::v6KZ:
+ case ARMBuildAttrs::v6K:
+ Config->ARMHasBlx = true;
+ // Architectures used in pre-Cortex processors do not support
+ // The J1 = 1 J2 = 1 Thumb branch range extension, with the exception
+ // of Architecture v6T2 (arm1156t2-s and arm1156t2f-s) that do.
+ break;
+ default:
+ // All other Architectures have BLX and extended branch encoding
+ Config->ARMHasBlx = true;
+ Config->ARMJ1J2BranchEncoding = true;
+ if (Arch != ARMBuildAttrs::v6_M && Arch != ARMBuildAttrs::v6S_M)
+ // All Architectures used in Cortex processors with the exception
+ // of v6-M and v6S-M have the MOVT and MOVW instructions.
+ Config->ARMHasMovtMovw = true;
+ break;
+ }
+}
+
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));
@@ -394,21 +462,26 @@ 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) {
- case SHT_ARM_ATTRIBUTES:
- // FIXME: ARM meta-data section. Retain the first attribute section
- // we see. The eglibc ARM dynamic loaders require the presence of an
- // attribute section for dlopen to work.
- // In a full implementation we would merge all attribute sections.
+ case SHT_ARM_ATTRIBUTES: {
+ if (Config->EMachine != EM_ARM)
+ break;
+ ARMAttributeParser Attributes;
+ ArrayRef<uint8_t> Contents = check(this->getObj().getSectionContents(&Sec));
+ Attributes.Parse(Contents, /*isLittle*/Config->EKind == ELF32LEKind);
+ updateSupportedARMFeatures(Attributes);
+ // FIXME: Retain the first attribute section we see. The eglibc ARM
+ // dynamic loaders require the presence of an attribute section for dlopen
+ // to work. In a full implementation we would merge all attribute sections.
if (InX::ARMAttributes == nullptr) {
InX::ARMAttributes = make<InputSection>(this, &Sec, Name);
return InX::ARMAttributes;
}
return &InputSection::Discarded;
+ }
case SHT_RELA:
case SHT_REL: {
// Find the relocation target section and associate this
@@ -443,13 +516,12 @@ elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
size_t NumRelocations;
if (Sec.sh_type == SHT_RELA) {
- ArrayRef<Elf_Rela> Rels =
- check(this->getObj().relas(&Sec), toString(this));
+ ArrayRef<Elf_Rela> Rels = CHECK(this->getObj().relas(&Sec), this);
Target->FirstRelocation = Rels.begin();
NumRelocations = Rels.size();
Target->AreRelocsRela = true;
} else {
- ArrayRef<Elf_Rel> Rels = check(this->getObj().rels(&Sec), toString(this));
+ ArrayRef<Elf_Rel> Rels = CHECK(this->getObj().rels(&Sec), this);
Target->FirstRelocation = Rels.begin();
NumRelocations = Rels.size();
Target->AreRelocsRela = false;
@@ -497,18 +569,6 @@ elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
return &InputSection::Discarded;
}
- 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.
@@ -529,46 +589,24 @@ elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
}
template <class ELFT>
-StringRef elf::ObjectFile<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));
+StringRef ObjFile<ELFT>::getSectionName(const Elf_Shdr &Sec) {
+ return CHECK(this->getObj().getSectionName(&Sec, SectionStringTable), this);
}
-template <class ELFT>
-InputSectionBase *elf::ObjectFile<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));
- InputSectionBase *S = this->Sections[Index];
-
- // We found that GNU assembler 2.17.50 [FreeBSD] 2007-07-03 could
- // generate broken objects. STT_SECTION/STT_NOTYPE symbols can be
- // associated with SHT_REL[A]/SHT_SYMTAB/SHT_STRTAB sections.
- // In this case it is fine for section to be null here as we do not
- // allocate sections of these types.
- if (!S) {
- if (Index == 0 || Sym.getType() == STT_SECTION ||
- Sym.getType() == STT_NOTYPE)
- return nullptr;
- fatal(toString(this) + ": invalid section index: " + Twine(Index));
- }
-
- if (S == &InputSection::Discarded)
- return S;
- return S->Repl;
+template <class ELFT> void ObjFile<ELFT>::initializeSymbols() {
+ this->Symbols.reserve(this->ELFSyms.size());
+ for (const Elf_Sym &Sym : this->ELFSyms)
+ this->Symbols.push_back(createSymbol(&Sym));
}
-template <class ELFT>
-SymbolBody *elf::ObjectFile<ELFT>::createSymbolBody(const Elf_Sym *Sym) {
+template <class ELFT> Symbol *ObjFile<ELFT>::createSymbol(const Elf_Sym *Sym) {
int Binding = Sym->getBinding();
- InputSectionBase *Sec = getSection(*Sym);
+ uint32_t SecIdx = this->getSectionIndex(*Sym);
+ if (SecIdx >= this->Sections.size())
+ fatal(toString(this) + ": invalid section index: " + Twine(SecIdx));
+
+ InputSectionBase *Sec = this->Sections[SecIdx];
uint8_t StOther = Sym->st_other;
uint8_t Type = Sym->getType();
uint64_t Value = Sym->st_value;
@@ -576,34 +614,29 @@ SymbolBody *elf::ObjectFile<ELFT>::createSymbolBody(const Elf_Sym *Sym) {
if (Binding == STB_LOCAL) {
if (Sym->getType() == STT_FILE)
- SourceFile = check(Sym->getName(this->StringTable), toString(this));
+ SourceFile = CHECK(Sym->getName(this->StringTable), this);
if (this->StringTable.size() <= Sym->st_name)
fatal(toString(this) + ": invalid symbol name offset");
StringRefZ Name = this->StringTable.data() + Sym->st_name;
if (Sym->st_shndx == SHN_UNDEF)
- return make<Undefined>(Name, /*IsLocal=*/true, StOther, Type, this);
+ return make<Undefined>(this, Name, Binding, StOther, Type);
- return make<DefinedRegular>(Name, /*IsLocal=*/true, StOther, Type, Value,
- Size, Sec, this);
+ return make<Defined>(this, Name, Binding, StOther, Type, Value, Size, Sec);
}
- StringRef Name = check(Sym->getName(this->StringTable), toString(this));
+ StringRef Name = CHECK(Sym->getName(this->StringTable), this);
switch (Sym->st_shndx) {
case SHN_UNDEF:
- return elf::Symtab<ELFT>::X
- ->addUndefined(Name, /*IsLocal=*/false, Binding, StOther, Type,
- /*CanOmitFromDynSym=*/false, this)
- ->body();
+ return Symtab->addUndefined<ELFT>(Name, Binding, StOther, Type,
+ /*CanOmitFromDynSym=*/false, this);
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)
- ->body();
+ return Symtab->addCommon(Name, Size, Value, Binding, StOther, Type, this);
}
switch (Binding) {
@@ -613,13 +646,10 @@ 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)
- ->body();
- return elf::Symtab<ELFT>::X
- ->addRegular(Name, StOther, Type, Value, Size, Binding, Sec, this)
- ->body();
+ return Symtab->addUndefined<ELFT>(Name, Binding, StOther, Type,
+ /*CanOmitFromDynSym=*/false, this);
+ return Symtab->addRegular<ELFT>(Name, StOther, Type, Value, Size, Binding,
+ Sec, this);
}
}
@@ -630,14 +660,14 @@ ArchiveFile::ArchiveFile(std::unique_ptr<Archive> &&File)
template <class ELFT> void ArchiveFile::parse() {
Symbols.reserve(File->getNumberOfSymbols());
for (const Archive::Symbol &Sym : File->symbols())
- Symbols.push_back(Symtab<ELFT>::X->addLazyArchive(this, Sym));
+ Symbols.push_back(Symtab->addLazyArchive<ELFT>(Sym.getName(), this, Sym));
}
// Returns a buffer pointing to a member file containing a given symbol.
std::pair<MemoryBufferRef, uint64_t>
ArchiveFile::getMember(const Archive::Symbol *Sym) {
Archive::Child C =
- check(Sym->getMember(), toString(this) +
+ CHECK(Sym->getMember(), toString(this) +
": could not get the member for symbol " +
Sym->getName());
@@ -645,14 +675,13 @@ ArchiveFile::getMember(const Archive::Symbol *Sym) {
return {MemoryBufferRef(), 0};
MemoryBufferRef Ret =
- check(C.getMemoryBufferRef(),
+ CHECK(C.getMemoryBufferRef(),
toString(this) +
": could not get the buffer for the member defining symbol " +
Sym->getName());
if (C.getParent()->isThin() && Tar)
- Tar->append(relativeToRoot(check(C.getFullName(), toString(this))),
- Ret.getBuffer());
+ Tar->append(relativeToRoot(CHECK(C.getFullName(), this)), Ret.getBuffer());
if (C.getParent()->isThin())
return {Ret, 0};
return {Ret, C.getChildOffset()};
@@ -661,22 +690,14 @@ ArchiveFile::getMember(const Archive::Symbol *Sym) {
template <class ELFT>
SharedFile<ELFT>::SharedFile(MemoryBufferRef M, StringRef DefaultSoName)
: ELFFileBase<ELFT>(Base::SharedKind, M), SoName(DefaultSoName),
- AsNeeded(Config->AsNeeded) {}
-
-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),
- toString(this));
-}
+ IsNeeded(!Config->AsNeeded) {}
// Partially parse the shared object file so that we can call
// getSoName on this object.
template <class ELFT> void SharedFile<ELFT>::parseSoName() {
const Elf_Shdr *DynamicSec = nullptr;
const ELFFile<ELFT> Obj = this->getObj();
- ArrayRef<Elf_Shdr> Sections = check(Obj.sections(), toString(this));
+ ArrayRef<Elf_Shdr> Sections = CHECK(Obj.sections(), this);
// Search for .dynsym, .dynamic, .symtab, .gnu.version and .gnu.version_d.
for (const Elf_Shdr &Sec : Sections) {
@@ -690,8 +711,7 @@ template <class ELFT> void SharedFile<ELFT>::parseSoName() {
DynamicSec = &Sec;
break;
case SHT_SYMTAB_SHNDX:
- this->SymtabSHNDX =
- check(Obj.getSHNDXTable(Sec, Sections), toString(this));
+ this->SymtabSHNDX = CHECK(Obj.getSHNDXTable(Sec, Sections), this);
break;
case SHT_GNU_versym:
this->VersymSec = &Sec;
@@ -702,15 +722,14 @@ 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.
if (!DynamicSec)
return;
ArrayRef<Elf_Dyn> Arr =
- check(Obj.template getSectionContentsAsArray<Elf_Dyn>(DynamicSec),
- toString(this));
+ CHECK(Obj.template getSectionContentsAsArray<Elf_Dyn>(DynamicSec), this);
for (const Elf_Dyn &Dyn : Arr) {
if (Dyn.d_tag == DT_SONAME) {
uint64_t Val = Dyn.getVal();
@@ -767,11 +786,14 @@ SharedFile<ELFT>::parseVerdefs(const Elf_Versym *&Versym) {
template <class ELFT> void SharedFile<ELFT>::parseRest() {
// Create mapping from version identifiers to Elf_Verdef entries.
const Elf_Versym *Versym = nullptr;
- std::vector<const Elf_Verdef *> Verdefs = parseVerdefs(Versym);
+ Verdefs = parseVerdefs(Versym);
- Elf_Sym_Range Syms = this->getGlobalSymbols();
+ ArrayRef<Elf_Shdr> Sections = CHECK(this->getObj().sections(), this);
+
+ // Add symbols to the symbol table.
+ Elf_Sym_Range Syms = this->getGlobalELFSyms();
for (const Elf_Sym &Sym : Syms) {
- unsigned VersymIndex = 0;
+ unsigned VersymIndex = VER_NDX_GLOBAL;
if (Versym) {
VersymIndex = Versym->vs_index;
++Versym;
@@ -779,28 +801,54 @@ template <class ELFT> void SharedFile<ELFT>::parseRest() {
bool Hidden = VersymIndex & VERSYM_HIDDEN;
VersymIndex = VersymIndex & ~VERSYM_HIDDEN;
- StringRef Name = check(Sym.getName(this->StringTable), toString(this));
+ StringRef Name = CHECK(Sym.getName(this->StringTable), this);
if (Sym.isUndefined()) {
Undefs.push_back(Name);
continue;
}
- // Ignore local symbols.
- if (Versym && VersymIndex == VER_NDX_LOCAL)
+ if (Sym.getBinding() == STB_LOCAL) {
+ warn("found local symbol '" + Name +
+ "' in global part of symbol table in file " + toString(this));
continue;
+ }
+
+ const Elf_Verdef *Ver = nullptr;
+ if (VersymIndex != VER_NDX_GLOBAL) {
+ if (VersymIndex >= Verdefs.size() || VersymIndex == VER_NDX_LOCAL) {
+ error("corrupt input file: version definition index " +
+ Twine(VersymIndex) + " for symbol " + Name +
+ " is out of bounds\n>>> defined in " + toString(this));
+ continue;
+ }
+ Ver = Verdefs[VersymIndex];
+ } else {
+ VersymIndex = 0;
+ }
- const Elf_Verdef *V =
- VersymIndex == VER_NDX_GLOBAL ? nullptr : Verdefs[VersymIndex];
+ // We do not usually care about alignments of data in shared object
+ // files because the loader takes care of it. However, if we promote a
+ // DSO symbol to point to .bss due to copy relocation, we need to keep
+ // the original alignment requirements. We infer it here.
+ uint64_t Alignment = 1;
+ if (Sym.st_value)
+ Alignment = 1ULL << countTrailingZeros((uint64_t)Sym.st_value);
+ if (0 < Sym.st_shndx && Sym.st_shndx < Sections.size()) {
+ uint64_t SecAlign = Sections[Sym.st_shndx].sh_addralign;
+ Alignment = std::min(Alignment, SecAlign);
+ }
+ if (Alignment > UINT32_MAX)
+ error(toString(this) + ": alignment too large: " + Name);
if (!Hidden)
- elf::Symtab<ELFT>::X->addShared(this, Name, Sym, V);
+ Symtab->addShared(Name, this, Sym, Alignment, VersymIndex);
// 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;
+ if (Ver) {
+ StringRef VerName = this->StringTable.data() + Ver->getAux()->vda_name;
Name = Saver.save(Name + "@" + VerName);
- elf::Symtab<ELFT>::X->addShared(this, Name, Sym, V);
+ Symtab->addShared(Name, this, Sym, Alignment, VersymIndex);
}
}
}
@@ -855,7 +903,7 @@ BitcodeFile::BitcodeFile(MemoryBufferRef MB, StringRef ArchiveName,
MemoryBufferRef MBRef(MB.getBuffer(),
Saver.save(ArchiveName + MB.getBufferIdentifier() +
utostr(OffsetInArchive)));
- Obj = check(lto::InputFile::create(MBRef), toString(this));
+ Obj = CHECK(lto::InputFile::create(MBRef), this);
Triple T(Obj->getTargetTriple());
EKind = getBitcodeELFKind(T);
@@ -887,22 +935,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, 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, 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>
@@ -947,18 +993,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) {
@@ -973,13 +1016,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");
}
@@ -1000,53 +1043,53 @@ 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());
- ArrayRef<Elf_Shdr> Sections = check(Obj.sections(), toString(this));
+ ELFFile<ELFT> Obj = check(ELFFile<ELFT>::create(this->MB.getBuffer()));
+ ArrayRef<Elf_Shdr> Sections = CHECK(Obj.sections(), this);
for (const Elf_Shdr &Sec : Sections) {
if (Sec.sh_type != SHT_SYMTAB)
continue;
- Elf_Sym_Range Syms = check(Obj.symbols(&Sec), toString(this));
+ Elf_Sym_Range Syms = CHECK(Obj.symbols(&Sec), this);
uint32_t FirstNonLocal = Sec.sh_info;
StringRef StringTable =
- check(Obj.getStringTableForSymtab(Sec, Sections), toString(this));
+ CHECK(Obj.getStringTableForSymtab(Sec, Sections), this);
std::vector<StringRef> V;
for (const Elf_Sym &Sym : Syms.slice(FirstNonLocal))
if (Sym.st_shndx != SHN_UNDEF)
- V.push_back(check(Sym.getName(StringTable), toString(this)));
+ V.push_back(CHECK(Sym.getName(StringTable), this));
return V;
}
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));
+ CHECK(lto::InputFile::create(this->MB), this);
std::vector<StringRef> V;
for (const lto::InputFile::Symbol &Sym : Obj->symbols())
if (!Sym.isUndefined())
@@ -1055,7 +1098,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();
@@ -1083,20 +1126,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 006218b45d9e..427f2fdea53e 100644
--- a/ELF/InputFiles.h
+++ b/ELF/InputFiles.h
@@ -11,12 +11,10 @@
#define LLD_ELF_INPUT_FILES_H
#include "Config.h"
-#include "Error.h"
-#include "InputSection.h"
-#include "Symbols.h"
+#include "lld/Common/ErrorHandler.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"
@@ -40,9 +38,10 @@ class InputFile;
namespace lld {
namespace elf {
class InputFile;
+class InputSectionBase;
}
-// Returns "(internal)", "foo.a(bar.o)" or "baz.o".
+// Returns "<internal>", "foo.a(bar.o)" or "baz.o".
std::string toString(const elf::InputFile *F);
namespace elf {
@@ -50,7 +49,7 @@ namespace elf {
using llvm::object::Archive;
class Lazy;
-class SymbolBody;
+class Symbol;
// If -reproduce option is given, all input files are written
// to this tar archive.
@@ -63,9 +62,9 @@ llvm::Optional<MemoryBufferRef> readFile(StringRef Path);
class InputFile {
public:
enum Kind {
- ObjectKind,
+ ObjKind,
SharedKind,
- LazyObjectKind,
+ LazyObjKind,
ArchiveKind,
BitcodeKind,
BinaryKind,
@@ -79,10 +78,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<Symbol *> 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.
@@ -100,6 +107,7 @@ public:
protected:
InputFile(Kind K, MemoryBufferRef M);
std::vector<InputSectionBase *> Sections;
+ std::vector<Symbol *> Symbols;
private:
const Kind FileKind;
@@ -115,21 +123,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;
@@ -137,7 +146,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;
@@ -150,34 +159,29 @@ 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();
+ ArrayRef<Symbol *> 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())
+ Symbol &getSymbol(uint32_t SymbolIndex) const {
+ if (SymbolIndex >= this->Symbols.size())
fatal(toString(this) + ": invalid symbol index");
- return *SymbolBodies[SymbolIndex];
+ return *this->Symbols[SymbolIndex];
}
- template <typename RelT>
- SymbolBody &getRelocTargetSym(const RelT &Rel) const {
+ template <typename RelT> Symbol &getRelocTargetSym(const RelT &Rel) const {
uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL);
- return getSymbolBody(SymIndex);
+ return getSymbol(SymIndex);
}
// Returns source line information for a given offset.
// If no information is available, returns "".
std::string getLineInfo(InputSectionBase *S, uint64_t Offset);
llvm::Optional<llvm::DILineInfo> getDILineInfo(InputSectionBase *, uint64_t);
+ llvm::Optional<std::pair<std::string, unsigned>> getVariableLoc(StringRef Name);
// MIPS GP0 value defined by this file. This value represents the gp value
// used to create the relocatable object and required to support
@@ -193,16 +197,13 @@ private:
void
initializeSections(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
void initializeSymbols();
- void initializeDwarfLine();
+ void initializeDwarf();
InputSectionBase *getRelocTarget(const Elf_Shdr &Sec);
InputSectionBase *createInputSection(const Elf_Shdr &Sec);
StringRef getSectionName(const Elf_Shdr &Sec);
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;
+ Symbol *createSymbol(const Elf_Sym *Sym);
// .shstrtab contents.
StringRef SectionStringTable;
@@ -212,34 +213,33 @@ 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::DenseMap<StringRef, std::pair<unsigned, unsigned>> VariableLoc;
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();
@@ -253,7 +253,6 @@ public:
explicit ArchiveFile(std::unique_ptr<Archive> &&File);
static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; }
template <class ELFT> void parse();
- ArrayRef<Symbol *> getSymbols() { return Symbols; }
// Returns a memory buffer for a given symbol and the offset in the archive
// for the member. An empty memory buffer and an offset of zero
@@ -264,7 +263,6 @@ public:
private:
std::unique_ptr<Archive> File;
llvm::DenseSet<uint64_t> Seen;
- std::vector<Symbol *> Symbols;
};
class BitcodeFile : public InputFile {
@@ -274,11 +272,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.
@@ -296,9 +290,9 @@ template <class ELFT> class SharedFile : public ELFFileBase<ELFT> {
const Elf_Shdr *VerdefSec = nullptr;
public:
+ std::vector<const Elf_Verdef *> Verdefs;
std::string SoName;
- const Elf_Shdr *getSection(const Elf_Sym &Sym) const;
llvm::ArrayRef<StringRef> getUndefinedSymbols() { return Undefs; }
static bool classof(const InputFile *F) {
@@ -324,9 +318,7 @@ public:
std::map<const Elf_Verdef *, NeededVer> VerdefMap;
// Used for --as-needed
- bool AsNeeded = false;
- bool IsUsed = false;
- bool isNeeded() const { return !AsNeeded || IsUsed; }
+ bool IsNeeded;
};
class BinaryFile : public InputFile {
@@ -340,6 +332,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 c6a539b8dfa5..02cad56ca508 100644
--- a/ELF/InputSection.cpp
+++ b/ELF/InputSection.cpp
@@ -10,21 +10,23 @@
#include "InputSection.h"
#include "Config.h"
#include "EhFrame.h"
-#include "Error.h"
#include "InputFiles.h"
#include "LinkerScript.h"
-#include "Memory.h"
#include "OutputSections.h"
#include "Relocations.h"
+#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "Thunks.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
#include "llvm/Object/Decompressor.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Compression.h"
#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 +46,34 @@ std::string lld::toString(const InputSectionBase *Sec) {
return (toString(Sec->File) + ":(" + Sec->Name + ")").str();
}
+DenseMap<SectionBase *, int> elf::buildSectionOrder() {
+ DenseMap<SectionBase *, int> SectionOrder;
+ if (Config->SymbolOrderingFile.empty())
+ return SectionOrder;
+
+ // 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.
+ for (InputFile *File : ObjectFiles) {
+ for (Symbol *Sym : File->getSymbols()) {
+ auto *D = dyn_cast<Defined>(Sym);
+ 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);
@@ -59,9 +87,7 @@ InputSectionBase::InputSectionBase(InputFile *File, uint64_t Flags,
StringRef Name, Kind SectionKind)
: SectionBase(SectionKind, Name, Flags, Entsize, Alignment, Type, Info,
Link),
- File(File), Data(Data), Repl(this) {
- Live = !Config->GcSections || !(Flags & SHF_ALLOC);
- Assigned = false;
+ File(File), Data(Data) {
NumRelocations = 0;
AreRelocsRela = false;
@@ -102,7 +128,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 +186,7 @@ uint64_t SectionBase::getOffset(uint64_t Offset) const {
OutputSection *SectionBase::getOutputSection() {
InputSection *Sec;
if (auto *IS = dyn_cast<InputSection>(this))
- Sec = IS;
+ return IS->getParent();
else if (auto *MS = dyn_cast<MergeInputSection>(this))
Sec = MS->getParent();
else if (auto *EH = dyn_cast<EhInputSection>(this))
@@ -170,29 +196,23 @@ 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->Flags &= ~(uint64_t)SHF_COMPRESSED;
-}
-uint64_t SectionBase::getOffset(const DefinedRegular &Sym) const {
- return getOffset(Sym.Value);
+ this->Data = makeArrayRef((uint8_t *)UncompressBuf.get(), Size);
+ this->Flags &= ~(uint64_t)SHF_COMPRESSED;
}
InputSection *InputSectionBase::getLinkOrderDep() const {
@@ -200,9 +220,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;
}
@@ -227,8 +247,8 @@ std::string InputSectionBase::getLocation(uint64_t Offset) {
SrcFile = toString(File);
// Find a function symbol that encloses a given location.
- for (SymbolBody *B : getFile<ELFT>()->getSymbols())
- if (auto *D = dyn_cast<DefinedRegular>(B))
+ for (Symbol *B : File->getSymbols())
+ if (auto *D = dyn_cast<Defined>(B))
if (D->Section == this && D->Type == STT_FUNC)
if (D->Value <= Offset && Offset < D->Value + D->Size)
return SrcFile + ":(function " + toString(*D) + ")";
@@ -237,31 +257,40 @@ std::string InputSectionBase::getLocation(uint64_t Offset) {
return (SrcFile + ":(" + Name + "+0x" + utohexstr(Offset) + ")").str();
}
-// Returns a source location string. This function is intended to be
-// used for constructing an error message. The returned message looks
-// like this:
+// Concatenates arguments to construct a string representing an error location.
+static std::string createFileLineMsg(StringRef Path, unsigned Line) {
+ std::string Filename = path::filename(Path);
+ std::string Lineno = ":" + std::to_string(Line);
+ if (Filename == Path)
+ return Filename + Lineno;
+ return Filename + Lineno + " (" + Path.str() + Lineno + ")";
+}
+
+// This function is intended to be used for constructing an error message.
+// The returned message looks like this:
//
// foo.c:42 (/home/alice/possibly/very/long/path/foo.c:42)
//
-// Returns an empty string if there's no way to get line info.
-template <class ELFT> std::string InputSectionBase::getSrcMsg(uint64_t Offset) {
+// Returns an empty string if there's no way to get line info.
+template <class ELFT>
+std::string InputSectionBase::getSrcMsg(const Symbol &Sym, uint64_t Offset) {
// Synthetic sections don't have input files.
- elf::ObjectFile<ELFT> *File = getFile<ELFT>();
+ ObjFile<ELFT> *File = getFile<ELFT>();
if (!File)
return "";
- Optional<DILineInfo> Info = File->getDILineInfo(this, Offset);
+ // In DWARF, functions and variables are stored to different places.
+ // First, lookup a function for a given offset.
+ if (Optional<DILineInfo> Info = File->getDILineInfo(this, Offset))
+ return createFileLineMsg(Info->FileName, Info->Line);
- // File->SourceFile contains STT_FILE symbol, and that is a last resort.
- if (!Info)
- return File->SourceFile;
+ // If it failed, lookup again as a variable.
+ if (Optional<std::pair<std::string, unsigned>> FileLine =
+ File->getVariableLoc(Sym.getName()))
+ return createFileLineMsg(FileLine->first, FileLine->second);
- std::string Path = Info->FileName;
- std::string Filename = path::filename(Path);
- std::string Lineno = ":" + std::to_string(Info->Line);
- if (Filename == Path)
- return Filename + Lineno;
- return Filename + Lineno + " (" + Path + Lineno + ")";
+ // File->SourceFile contains STT_FILE symbol, and that is a last resort.
+ return File->SourceFile;
}
// Returns a filename string along with an optional section name. This
@@ -273,11 +302,10 @@ template <class ELFT> std::string InputSectionBase::getSrcMsg(uint64_t Offset) {
// or
//
// path/to/foo.o:(function bar) in archive path/to/bar.a
-template <class ELFT> std::string InputSectionBase::getObjMsg(uint64_t Off) {
+std::string InputSectionBase::getObjMsg(uint64_t Off) {
// Synthetic sections don't have input files.
- elf::ObjectFile<ELFT> *File = getFile<ELFT>();
if (!File)
- return ("(internal):(" + Name + "+0x" + utohexstr(Off) + ")").str();
+ return ("<internal>:(" + Name + "+0x" + utohexstr(Off) + ")").str();
std::string Filename = File->getName();
std::string Archive;
@@ -285,8 +313,8 @@ template <class ELFT> std::string InputSectionBase::getObjMsg(uint64_t Off) {
Archive = (" in archive " + File->ArchiveName).str();
// Find a symbol that encloses a given location.
- for (SymbolBody *B : getFile<ELFT>()->getSymbols())
- if (auto *D = dyn_cast<DefinedRegular>(B))
+ for (Symbol *B : File->getSymbols())
+ if (auto *D = dyn_cast<Defined>(B))
if (D->Section == this && D->Value <= Off && Off < D->Value + D->Size)
return Filename + ":(" + toString(*D) + ")" + Archive;
@@ -295,7 +323,7 @@ template <class ELFT> std::string InputSectionBase::getObjMsg(uint64_t Off) {
.str();
}
-InputSectionBase InputSectionBase::Discarded;
+InputSection InputSection::Discarded(0, 0, 0, ArrayRef<uint8_t>(), "");
InputSection::InputSection(uint64_t Flags, uint32_t Type, uint32_t Alignment,
ArrayRef<uint8_t> Data, StringRef Name, Kind K)
@@ -304,8 +332,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) {
@@ -313,10 +341,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);
}
@@ -349,15 +373,11 @@ 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);
- SymbolBody &Body = this->getFile<ELFT>()->getRelocTargetSym(Rel);
+ RelType Type = Rel.getType(Config->IsMips64EL);
+ Symbol &Sym = this->getFile<ELFT>()->getRelocTargetSym(Rel);
auto *P = reinterpret_cast<typename ELFT::Rela *>(Buf);
Buf += sizeof(RelTy);
@@ -367,12 +387,11 @@ 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->setSymbolAndType(InX::SymTab->getSymbolIndex(&Body), Type,
+ P->r_offset = Sec->getOutputSection()->Addr + Sec->getOffset(Rel.r_offset);
+ P->setSymbolAndType(InX::SymTab->getSymbolIndex(&Sym), Type,
Config->IsMips64EL);
- if (Body.Type == STT_SECTION) {
+ if (Sym.Type == STT_SECTION) {
// We combine multiple section symbols into only one per
// section. This means we have to update the addend. That is
// trivial for Elf_Rela, but for Elf_Rel we have to write to the
@@ -382,19 +401,25 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
// avoid having to parse and recreate .eh_frame, we just replace any
// relocation in it pointing to discarded sections with R_*_NONE, which
// hopefully creates a frame that is ignored at runtime.
- SectionBase *Section = cast<DefinedRegular>(Body).Section;
+ auto *D = dyn_cast<Defined>(&Sym);
+ if (!D) {
+ error("STT_SECTION symbol should be defined");
+ continue;
+ }
+ SectionBase *Section = D->Section;
if (Section == &InputSection::Discarded) {
P->setSymbolAndType(0, 0, false);
continue;
}
if (Config->IsRela) {
- P->r_addend += Body.getVA() - Section->getOutputSection()->Addr;
+ P->r_addend =
+ Sym.getVA(getAddend<ELFT>(Rel)) - 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),
+ &Sym});
}
}
@@ -406,7 +431,7 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
// 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(uint32_t Type, uint32_t A,
+static uint32_t getARMUndefinedRelativeWeakVA(RelType Type, uint32_t A,
uint32_t P) {
switch (Type) {
// Unresolved branch relocations to weak references resolve to next
@@ -453,6 +478,7 @@ static uint64_t getAArch64UndefinedRelativeWeakVA(uint64_t Type, uint64_t A,
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");
@@ -461,53 +487,55 @@ static uint64_t getAArch64UndefinedRelativeWeakVA(uint64_t Type, uint64_t A,
// ARM SBREL relocations are of the form S + A - B where B is the static base
// The ARM ABI defines base to be "addressing origin of the output segment
// defining the symbol S". We defined the "addressing origin"/static base to be
-// the base of the PT_LOAD segment containing the Body.
+// the base of the PT_LOAD segment containing the Sym.
// The procedure call standard only defines a Read Write Position Independent
// RWPI variant so in practice we should expect the static base to be the base
// 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");
- return OS->FirstInPtLoad->Addr;
+static uint64_t getARMStaticBase(const Symbol &Sym) {
+ OutputSection *OS = Sym.getOutputSection();
+ if (!OS || !OS->PtLoad || !OS->PtLoad->FirstSec)
+ fatal("SBREL relocation to " + Sym.getName() + " without static base");
+ return OS->PtLoad->FirstSec->Addr;
}
-static uint64_t getRelocTargetVA(uint32_t Type, int64_t A, uint64_t P,
- const SymbolBody &Body, RelExpr Expr) {
+static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P,
+ const Symbol &Sym, RelExpr Expr) {
switch (Expr) {
+ case R_INVALID:
+ return 0;
case R_ABS:
case R_RELAX_GOT_PC_NOPIC:
- return Body.getVA(A);
+ return Sym.getVA(A);
case R_ARM_SBREL:
- return Body.getVA(A) - getARMStaticBase(Body);
+ return Sym.getVA(A) - getARMStaticBase(Sym);
case R_GOT:
case R_RELAX_TLS_GD_TO_IE_ABS:
- return Body.getGotVA() + A;
+ return Sym.getGotVA() + A;
case R_GOTONLY_PC:
return InX::Got->getVA() + A - P;
case R_GOTONLY_PC_FROM_END:
return InX::Got->getVA() + A - P + InX::Got->getSize();
case R_GOTREL:
- return Body.getVA(A) - InX::Got->getVA();
+ return Sym.getVA(A) - InX::Got->getVA();
case R_GOTREL_FROM_END:
- return Body.getVA(A) - InX::Got->getVA() - InX::Got->getSize();
+ return Sym.getVA(A) - InX::Got->getVA() - InX::Got->getSize();
case R_GOT_FROM_END:
case R_RELAX_TLS_GD_TO_IE_END:
- return Body.getGotOffset() + A - InX::Got->getSize();
+ return Sym.getGotOffset() + A - InX::Got->getSize();
case R_GOT_OFF:
- return Body.getGotOffset() + A;
+ return Sym.getGotOffset() + A;
case R_GOT_PAGE_PC:
case R_RELAX_TLS_GD_TO_IE_PAGE_PC:
- return getAArch64Page(Body.getGotVA() + A) - getAArch64Page(P);
+ return getAArch64Page(Sym.getGotVA() + A) - getAArch64Page(P);
case R_GOT_PC:
case R_RELAX_TLS_GD_TO_IE:
- return Body.getGotVA() + A - P;
+ return Sym.getGotVA() + A - P;
case R_HINT:
case R_NONE:
case R_TLSDESC_CALL:
llvm_unreachable("cannot relocate hint relocs");
case R_MIPS_GOTREL:
- return Body.getVA(A) - InX::MipsGot->getGp();
+ return Sym.getVA(A) - InX::MipsGot->getGp();
case R_MIPS_GOT_GP:
return InX::MipsGot->getGp() + A;
case R_MIPS_GOT_GP_PC: {
@@ -515,42 +543,47 @@ static uint64_t getRelocTargetVA(uint32_t Type, int64_t A, uint64_t P,
// is _gp_disp symbol. In that case we should use the following
// 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
+ // microMIPS variants of these relocations use slightly different
+ // expressions: AHL + GP - P + 3 for %lo() and AHL + GP - P - 1 for %hi()
+ // to correctly handle less-sugnificant bit of the microMIPS symbol.
uint64_t V = InX::MipsGot->getGp() + A - P;
- if (Type == R_MIPS_LO16)
+ if (Type == R_MIPS_LO16 || Type == R_MICROMIPS_LO16)
V += 4;
+ if (Type == R_MICROMIPS_LO16 || Type == R_MICROMIPS_HI16)
+ V -= 1;
return V;
}
case R_MIPS_GOT_LOCAL_PAGE:
// If relocation against MIPS local symbol requires GOT entry, this entry
// should be initialized by 'page address'. This address is high 16-bits
// of sum the symbol's value and the addend.
- return InX::MipsGot->getVA() + InX::MipsGot->getPageEntryOffset(Body, A) -
+ return InX::MipsGot->getVA() + InX::MipsGot->getPageEntryOffset(Sym, A) -
InX::MipsGot->getGp();
case R_MIPS_GOT_OFF:
case R_MIPS_GOT_OFF32:
// In case of MIPS if a GOT relocation has non-zero addend this addend
// should be applied to the GOT entry content not to the GOT entry offset.
// That is why we use separate expression type.
- return InX::MipsGot->getVA() + InX::MipsGot->getBodyEntryOffset(Body, A) -
+ return InX::MipsGot->getVA() + InX::MipsGot->getSymEntryOffset(Sym, A) -
InX::MipsGot->getGp();
case R_MIPS_TLSGD:
return InX::MipsGot->getVA() + InX::MipsGot->getTlsOffset() +
- InX::MipsGot->getGlobalDynOffset(Body) - InX::MipsGot->getGp();
+ InX::MipsGot->getGlobalDynOffset(Sym) - InX::MipsGot->getGp();
case R_MIPS_TLSLD:
return InX::MipsGot->getVA() + InX::MipsGot->getTlsOffset() +
InX::MipsGot->getTlsIndexOff() - InX::MipsGot->getGp();
case R_PAGE_PC:
case R_PLT_PAGE_PC: {
uint64_t Dest;
- if (Body.isUndefined() && !Body.isLocal() && Body.symbol()->isWeak())
+ if (Sym.isUndefWeak())
Dest = getAArch64Page(A);
else
- Dest = getAArch64Page(Body.getVA(A));
+ Dest = getAArch64Page(Sym.getVA(A));
return Dest - getAArch64Page(P);
}
case R_PC: {
uint64_t Dest;
- if (Body.isUndefined() && !Body.isLocal() && Body.symbol()->isWeak()) {
+ if (Sym.isUndefWeak()) {
// On ARM and AArch64 a branch to an undefined weak resolves to the
// next instruction, otherwise the place.
if (Config->EMachine == EM_ARM)
@@ -558,19 +591,19 @@ static uint64_t getRelocTargetVA(uint32_t Type, int64_t A, uint64_t P,
else if (Config->EMachine == EM_AARCH64)
Dest = getAArch64UndefinedRelativeWeakVA(Type, A, P);
else
- Dest = Body.getVA(A);
+ Dest = Sym.getVA(A);
} else {
- Dest = Body.getVA(A);
+ Dest = Sym.getVA(A);
}
return Dest - P;
}
case R_PLT:
- return Body.getPltVA() + A;
+ return Sym.getPltVA() + A;
case R_PLT_PC:
case R_PPC_PLT_OPD:
- return Body.getPltVA() + A - P;
+ return Sym.getPltVA() + A - P;
case R_PPC_OPD: {
- uint64_t SymVA = Body.getVA(A);
+ uint64_t SymVA = Sym.getVA(A);
// If we have an undefined weak symbol, we might get here with a symbol
// address of zero. That could overflow, but the code must be unreachable,
// so don't bother doing anything at all.
@@ -590,7 +623,7 @@ static uint64_t getRelocTargetVA(uint32_t Type, int64_t A, uint64_t P,
case R_PPC_TOC:
return getPPC64TocBase() + A;
case R_RELAX_GOT_PC:
- return Body.getVA(A) - P;
+ return Sym.getVA(A) - P;
case R_RELAX_TLS_GD_TO_LE:
case R_RELAX_TLS_IE_TO_LE:
case R_RELAX_TLS_LD_TO_LE:
@@ -600,26 +633,25 @@ 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 (Sym.isTls() && Sym.isUndefWeak())
return 0;
if (Target->TcbSize)
- return Body.getVA(A) + alignTo(Target->TcbSize, Out::TlsPhdr->p_align);
- return Body.getVA(A) - Out::TlsPhdr->p_memsz;
+ return Sym.getVA(A) + alignTo(Target->TcbSize, Out::TlsPhdr->p_align);
+ return Sym.getVA(A) - Out::TlsPhdr->p_memsz;
case R_RELAX_TLS_GD_TO_LE_NEG:
case R_NEG_TLS:
- return Out::TlsPhdr->p_memsz - Body.getVA(A);
+ return Out::TlsPhdr->p_memsz - Sym.getVA(A);
case R_SIZE:
- return A; // Body.getSize was already folded into the addend.
+ return A; // Sym.getSize was already folded into the addend.
case R_TLSDESC:
- return InX::Got->getGlobalDynAddr(Body) + A;
+ return InX::Got->getGlobalDynAddr(Sym) + A;
case R_TLSDESC_PAGE:
- return getAArch64Page(InX::Got->getGlobalDynAddr(Body) + A) -
+ return getAArch64Page(InX::Got->getGlobalDynAddr(Sym) + A) -
getAArch64Page(P);
case R_TLSGD:
- return InX::Got->getGlobalDynOffset(Body) + A - InX::Got->getSize();
+ return InX::Got->getGlobalDynOffset(Sym) + A - InX::Got->getSize();
case R_TLSGD_PC:
- return InX::Got->getGlobalDynAddr(Body) + A - P;
+ return InX::Got->getGlobalDynAddr(Sym) + A - P;
case R_TLSLD:
return InX::Got->getTlsIndexOff() + A - InX::Got->getSize();
case R_TLSLD_PC:
@@ -637,64 +669,62 @@ 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);
if (!RelTy::IsRela)
Addend += Target->getImplicitAddend(BufLoc, Type);
- SymbolBody &Sym = this->getFile<ELFT>()->getRelocTargetSym(Rel);
+ Symbol &Sym = this->getFile<ELFT>()->getRelocTargetSym(Rel);
RelExpr Expr = Target->getRelExpr(Type, Sym, BufLoc);
if (Expr == R_NONE)
continue;
if (Expr != R_ABS) {
- error(this->getLocation<ELFT>(Offset) + ": has non-ABS reloc");
+ // GCC 8.0 or earlier have a bug that it emits R_386_GOTPC relocations
+ // against _GLOBAL_OFFSET_TABLE for .debug_info. The bug seems to have
+ // been fixed in 2017: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82630,
+ // but we need to keep this bug-compatible code for a while.
+ if (Config->EMachine == EM_386 && Type == R_386_GOTPC)
+ continue;
+
+ 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;
@@ -776,24 +806,15 @@ 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) {
- // Mark .eh_frame sections as live by default because there are
- // usually no relocations that point to .eh_frames. Otherwise,
- // the garbage collector would drop all .eh_frame sections.
- this->Live = true;
-}
+ : InputSectionBase(F, Header, Name, InputSectionBase::EHFrame) {}
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>
@@ -820,14 +841,10 @@ template <class ELFT> void EhInputSection::split() {
if (!this->Pieces.empty())
return;
- if (this->NumRelocations) {
- if (this->AreRelocsRela)
- split<ELFT>(this->relas<ELFT>());
- else
- split<ELFT>(this->rels<ELFT>());
- return;
- }
- split<ELFT>(makeArrayRef<typename ELFT::Rela>(nullptr, nullptr));
+ if (this->AreRelocsRela)
+ split<ELFT>(this->relas<ELFT>());
+ else
+ split<ELFT>(this->rels<ELFT>());
}
template <class ELFT, class RelTy>
@@ -835,7 +852,7 @@ void EhInputSection::split(ArrayRef<RelTy> Rels) {
ArrayRef<uint8_t> Data = this->Data;
unsigned RelI = 0;
for (size_t Off = 0, End = Data.size(); Off != End;) {
- size_t Size = readEhRecordSize<ELFT>(this, Off);
+ size_t Size = readEhRecordSize(this, Off);
this->Pieces.emplace_back(Off, this, Size, getReloc(Off, Size, Rels, RelI));
// The empty record is the end marker.
if (Size == 4)
@@ -844,9 +861,8 @@ void EhInputSection::split(ArrayRef<RelTy> Rels) {
}
}
-static size_t findNull(ArrayRef<uint8_t> A, size_t EntSize) {
+static size_t findNull(StringRef S, size_t EntSize) {
// Optimize the common case.
- StringRef S((const char *)A.data(), A.size());
if (EntSize == 1)
return S.find(0);
@@ -867,14 +883,16 @@ SyntheticSection *MergeInputSection::getParent() const {
void MergeInputSection::splitStrings(ArrayRef<uint8_t> Data, size_t EntSize) {
size_t Off = 0;
bool IsAlloc = this->Flags & SHF_ALLOC;
- while (!Data.empty()) {
- size_t End = findNull(Data, EntSize);
+ StringRef S = toStringRef(Data);
+
+ while (!S.empty()) {
+ size_t End = findNull(S, EntSize);
if (End == StringRef::npos)
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))));
- Data = Data.slice(Size);
+
+ Pieces.emplace_back(Off, xxHash64(S.substr(0, Size)), !IsAlloc);
+ S = S.substr(Size);
Off += Size;
}
}
@@ -886,41 +904,43 @@ void MergeInputSection::splitNonStrings(ArrayRef<uint8_t> Data,
size_t Size = Data.size();
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))));
- Pieces.emplace_back(I, !IsAlloc);
- }
+
+ for (size_t I = 0; I != Size; I += EntSize)
+ Pieces.emplace_back(I, xxHash64(toStringRef(Data.slice(I, EntSize))),
+ !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) {}
+ : InputSectionBase(F, Header, Name, InputSectionBase::Merge) {
+ // In order to reduce memory allocation, we assume that mergeable
+ // sections are smaller than 4 GiB, which is not an unreasonable
+ // assumption as of 2017.
+ if (Data.size() > UINT32_MAX)
+ error(toString(this) + ": section too large");
+}
// This function is called after we obtain a complete list of input sections
// that need to be linked. This is responsible to split section contents
// into small chunks for further processing.
//
-// Note that this function is called from parallel_for_each. This must be
+// Note that this function is called from parallelForEach. This must be
// thread-safe (i.e. no memory allocation from the pools).
void MergeInputSection::splitIntoPieces() {
- ArrayRef<uint8_t> Data = this->Data;
- uint64_t EntSize = this->Entsize;
+ assert(Pieces.empty());
+
if (this->Flags & SHF_STRINGS)
- splitStrings(Data, EntSize);
+ splitStrings(Data, Entsize);
else
- splitNonStrings(Data, EntSize);
+ splitNonStrings(Data, Entsize);
if (Config->GcSections && (this->Flags & SHF_ALLOC))
for (uint64_t Off : LiveOffsets)
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);
@@ -941,8 +961,7 @@ static It fastUpperBound(It First, It Last, const T &Value, Compare Comp) {
}
const SectionPiece *MergeInputSection::getSectionPiece(uint64_t Offset) const {
- uint64_t Size = this->Data.size();
- if (Offset >= Size)
+ if (Data.size() <= Offset)
fatal(toString(this) + ": entry is past the end of the section");
// Find the element this offset points to.
@@ -957,20 +976,20 @@ const SectionPiece *MergeInputSection::getSectionPiece(uint64_t Offset) const {
// Because contents of a mergeable section is not contiguous in output,
// it is not just an addition to a base output offset.
uint64_t MergeInputSection::getOffset(uint64_t Offset) const {
+ if (!Live)
+ return 0;
+
// Initialize OffsetMap lazily.
llvm::call_once(InitOffsetMap, [&] {
OffsetMap.reserve(Pieces.size());
- for (const SectionPiece &Piece : Pieces)
- OffsetMap[Piece.InputOff] = Piece.OutputOff;
+ for (size_t I = 0; I < Pieces.size(); ++I)
+ OffsetMap[Pieces[I].InputOff] = I;
});
// Find a string starting at a given offset.
auto It = OffsetMap.find(Offset);
if (It != OffsetMap.end())
- return It->second;
-
- if (!this->Live)
- return 0;
+ return Pieces[It->second].OutputOff;
// If Offset is not at beginning of a section piece, it is not in the map.
// In that case we need to search from the original section piece vector.
@@ -982,56 +1001,50 @@ 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);
template std::string InputSectionBase::getLocation<ELF64LE>(uint64_t);
template std::string InputSectionBase::getLocation<ELF64BE>(uint64_t);
-template std::string InputSectionBase::getSrcMsg<ELF32LE>(uint64_t);
-template std::string InputSectionBase::getSrcMsg<ELF32BE>(uint64_t);
-template std::string InputSectionBase::getSrcMsg<ELF64LE>(uint64_t);
-template std::string InputSectionBase::getSrcMsg<ELF64BE>(uint64_t);
-
-template std::string InputSectionBase::getObjMsg<ELF32LE>(uint64_t);
-template std::string InputSectionBase::getObjMsg<ELF32BE>(uint64_t);
-template std::string InputSectionBase::getObjMsg<ELF64LE>(uint64_t);
-template std::string InputSectionBase::getObjMsg<ELF64BE>(uint64_t);
+template std::string InputSectionBase::getSrcMsg<ELF32LE>(const Symbol &,
+ uint64_t);
+template std::string InputSectionBase::getSrcMsg<ELF32BE>(const Symbol &,
+ uint64_t);
+template std::string InputSectionBase::getSrcMsg<ELF64LE>(const Symbol &,
+ uint64_t);
+template std::string InputSectionBase::getSrcMsg<ELF64BE>(const Symbol &,
+ uint64_t);
template void InputSection::writeTo<ELF32LE>(uint8_t *);
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 d262b589219a..dfd78a8fb458 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"
@@ -24,15 +24,13 @@
namespace lld {
namespace elf {
-class DefinedCommon;
-class SymbolBody;
+class Symbol;
struct SectionPiece;
-class DefinedRegular;
+class Defined;
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
@@ -47,6 +45,13 @@ public:
StringRef Name;
+ // This pointer points to the "real" instance of this instance.
+ // Usually Repl == this. However, if ICF merges two sections,
+ // Repl pointer of one section points to another section. So,
+ // if you need to get a pointer to this instance, do not use
+ // this but instead this->Repl.
+ SectionBase *Repl;
+
unsigned SectionKind : 3;
// The next two bit fields are only used by InputSectionBase, but we
@@ -54,12 +59,12 @@ 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
+ unsigned Live : 1;
- uint32_t Alignment;
+ unsigned Bss : 1;
// These corresponds to the fields in Elf_Shdr.
+ uint32_t Alignment;
uint64_t Flags;
uint64_t Entsize;
uint32_t Type;
@@ -75,45 +80,20 @@ public:
// section.
uint64_t getOffset(uint64_t Offset) const;
- uint64_t getOffset(const DefinedRegular &Sym) const;
-
protected:
SectionBase(Kind SectionKind, StringRef Name, uint64_t Flags,
uint64_t Entsize, uint64_t Alignment, uint32_t Type,
uint32_t Info, uint32_t Link)
- : Name(Name), SectionKind(SectionKind), Alignment(Alignment),
- Flags(Flags), Entsize(Entsize), Type(Type), Link(Link), Info(Info) {
- Live = false;
- Assigned = false;
- }
+ : Name(Name), Repl(this), SectionKind(SectionKind), Live(false),
+ Bss(false), Alignment(Alignment), Flags(Flags), Entsize(Entsize),
+ Type(Type), Link(Link), Info(Info) {}
};
// 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 +101,33 @@ 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;
+
+ // 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.
+ bool Assigned = false;
+
// 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 +138,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(
@@ -144,38 +153,34 @@ public:
NumRelocations);
}
- // This pointer points to the "real" instance of this instance.
- // Usually Repl == this. However, if ICF merges two sections,
- // Repl pointer of one section points to another section. So,
- // if you need to get a pointer to this instance, do not use
- // this but instead this->Repl.
- 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);
+ template <class ELFT>
+ std::string getSrcMsg(const Symbol &Sym, uint64_t Offset);
+ 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,36 +188,43 @@ 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.
// We allocate a lot of these and binary search on them. This means that they
// have to be as compact as possible, which is why we don't store the size (can
-// be found by looking at the next one) and put the hash in a side table.
+// be found by looking at the next one).
struct SectionPiece {
- SectionPiece(size_t Off, bool Live = false)
- : InputOff(Off), OutputOff(-1), Live(Live || !Config->GcSections) {}
-
- size_t InputOff;
- ssize_t OutputOff : 8 * sizeof(ssize_t) - 1;
- size_t Live : 1;
+ SectionPiece(size_t Off, uint32_t Hash, bool Live)
+ : InputOff(Off), Hash(Hash), OutputOff(-1),
+ Live(Live || !Config->GcSections) {}
+
+ uint32_t InputOff;
+ uint32_t Hash;
+ int64_t OutputOff : 63;
+ uint64_t Live : 1;
};
-static_assert(sizeof(SectionPiece) == 2 * sizeof(size_t),
- "SectionPiece is too big");
+
+static_assert(sizeof(SectionPiece) == 16, "SectionPiece is too big");
// This corresponds to a SHF_MERGE section of an input file.
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.
void markLiveAt(uint64_t Offset) {
- assert(this->Flags & llvm::ELF::SHF_ALLOC);
- LiveOffsets.insert(Offset);
+ if (this->Flags & llvm::ELF::SHF_ALLOC)
+ LiveOffsets.insert(Offset);
}
// Translate an offset in the input section to an offset
@@ -228,14 +240,9 @@ public:
LLVM_ATTRIBUTE_ALWAYS_INLINE
llvm::CachedHashStringRef getData(size_t I) const {
size_t Begin = Pieces[I].InputOff;
- size_t End;
- if (Pieces.size() - 1 == I)
- End = this->Data.size();
- else
- End = Pieces[I + 1].InputOff;
-
- StringRef S = {(const char *)(this->Data.data() + Begin), End - Begin};
- return {S, Hashes[I]};
+ size_t End =
+ (Pieces.size() - 1 == I) ? Data.size() : Pieces[I + 1].InputOff;
+ return {toStringRef(Data.slice(Begin, End - Begin)), Pieces[I].Hash};
}
// Returns the SectionPiece at a given input section offset.
@@ -248,24 +255,23 @@ private:
void splitStrings(ArrayRef<uint8_t> A, size_t Size);
void splitNonStrings(ArrayRef<uint8_t> A, size_t Size);
- std::vector<uint32_t> Hashes;
-
- mutable llvm::DenseMap<uint64_t, uint64_t> OffsetMap;
+ mutable llvm::DenseMap<uint32_t, uint32_t> OffsetMap;
mutable llvm::once_flag InitOffsetMap;
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 {ID->Data.data() + this->InputOff, Size}; }
+ ArrayRef<uint8_t> data() { return {Sec->Data.data() + this->InputOff, Size}; }
+
+ size_t InputOff;
+ ssize_t OutputOff = -1;
+ InputSectionBase *Sec;
+ uint32_t Size;
unsigned FirstRelocation;
};
@@ -273,9 +279,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 +301,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
@@ -304,8 +310,10 @@ public:
OutputSection *getParent() const;
- // The offset from beginning of the output sections this section was assigned
- // to. The writer sets a value.
+ // This variable has two usages. Initially, it represents an index in the
+ // OutputSection's InputSection list, and is used when ordering SHF_LINK_ORDER
+ // sections. After assignAddresses is called, it represents the offset from
+ // the beginning of the output section this section was assigned to.
uint64_t OutSecOff = 0;
static bool classof(const SectionBase *S);
@@ -321,6 +329,8 @@ public:
// Called by ICF to merge two input sections.
void replace(InputSection *Other);
+ static InputSection Discarded;
+
private:
template <class ELFT, class RelTy>
void copyRelocations(uint8_t *Buf, llvm::ArrayRef<RelTy> Rels);
@@ -331,6 +341,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 3a536271db4c..bfd85288d186 100644
--- a/ELF/LTO.cpp
+++ b/ELF/LTO.cpp
@@ -9,10 +9,12 @@
#include "LTO.h"
#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/ErrorHandler.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,13 +107,20 @@ 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->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);
+ replaceSymbol<Undefined>(S, nullptr, S->getName(), STB_GLOBAL, STV_DEFAULT,
+ S->Type);
}
void BitcodeCompiler::add(BitcodeFile &F) {
@@ -118,25 +129,42 @@ void BitcodeCompiler::add(BitcodeFile &F) {
std::vector<Symbol *> 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];
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() && Sym->File == &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(Sym->getName());
}
checkError(LTOObj->add(std::move(F.Obj), Resols));
}
@@ -156,9 +184,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 d19923c90a99..223af507a97d 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>
@@ -50,6 +51,7 @@ 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
diff --git a/ELF/LinkerScript.cpp b/ELF/LinkerScript.cpp
index 8bdbd8db20ad..91873e318f54 100644
--- a/ELF/LinkerScript.cpp
+++ b/ELF/LinkerScript.cpp
@@ -14,20 +14,19 @@
#include "LinkerScript.h"
#include "Config.h"
#include "InputSection.h"
-#include "Memory.h"
#include "OutputSections.h"
#include "Strings.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
-#include "Threads.h"
#include "Writer.h"
+#include "lld/Common/Memory.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,62 +49,56 @@ 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_NOBITS, 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;
}
@@ -113,16 +106,56 @@ void LinkerScript::setDot(Expr E, const Twine &Loc, bool InSec) {
uint64_t Val = E().getValue();
if (Val < Dot && InSec)
error(Loc + ": unable to move location counter backward for: " +
- CurAddressState->OutSec->Name);
+ Ctx->OutSec->Name);
Dot = Val;
+
// Update to location counter means update to section size.
if (InSec)
- CurAddressState->OutSec->Size = Dot - CurAddressState->OutSec->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.
+ Symbol *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);
+ 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();
+
+ replaceSymbol<Defined>(Sym, nullptr, Cmd->Name, STB_GLOBAL, Visibility,
+ STT_NOTYPE, SymValue, 0, Sec);
+ Cmd->Sym = cast<Defined>(Sym);
+}
+
+// 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);
@@ -132,116 +165,36 @@ 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);
- }
-}
-
-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");
+ Cmd->Sym->Section = V.Sec;
+ Cmd->Sym->Value = V.getSectionOffset();
}
}
-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)))
+ if (KeptSections.empty())
+ return false;
+ 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;
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.
-static int 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;
-}
-
// A helper function for the SORT() command.
static std::function<bool(InputSectionBase *, InputSectionBase *)>
getComparator(SortSectionPolicy K) {
@@ -267,28 +220,63 @@ 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) {
+LinkerScript::computeInputSections(const InputSectionDescription *Cmd,
+ const DenseMap<SectionBase *, int> &Order) {
std::vector<InputSection *> Ret;
// Collects all sections that satisfy constraints of Cmd.
@@ -296,13 +284,8 @@ LinkerScript::computeInputSections(const InputSectionDescription *Cmd) {
size_t SizeBefore = Ret.size();
for (InputSectionBase *Sec : InputSections) {
- if (Sec->Assigned)
- continue;
-
- if (!Sec->Live) {
- reportDiscarded(Sec);
+ if (!Sec->Live || Sec->Assigned)
continue;
- }
// For -emit-relocs we have to ignore entries like
// .rela.dyn : { *(.rela.data) }
@@ -310,67 +293,51 @@ 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) {
- S->Live = false;
+void LinkerScript::discard(ArrayRef<InputSection *> V) {
+ for (InputSection *S : V) {
if (S == InX::ShStrTab || S == InX::Dynamic || S == InX::DynSymTab ||
S == InX::DynStrTab)
error("discarding " + S->Name + " section is not allowed");
+
+ S->Assigned = false;
+ S->Live = false;
discard(S->DependentSections);
}
}
-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, const DenseMap<SectionBase *, int> &Order) {
+ 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, Order);
+ Ret.insert(Ret.end(), Cmd->Sections.begin(), Cmd->Sections.end());
+ }
}
-
return Ret;
}
-void LinkerScript::processCommands(OutputSectionFactory &Factory) {
+void LinkerScript::processSectionCommands() {
// 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:
@@ -382,28 +349,31 @@ void LinkerScript::processCommands(OutputSectionFactory &Factory) {
// which will map to whatever the first actual section is.
Aether = make<OutputSection>("", 0, SHF_ALLOC);
Aether->SectionIndex = 1;
- auto State = make_unique<AddressState>(Opt);
- // CurAddressState captures the local AddressState and makes it accessible
- // deliberately. This is needed as there are some cases where we cannot just
+
+ // 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.
- CurAddressState = State.get();
- CurAddressState->OutSec = Aether;
- Dot = 0;
+ auto Deleter = make_unique<AddressState>();
+ Ctx = Deleter.get();
+ Ctx->OutSec = Aether;
- for (size_t I = 0; I < Opt.Commands.size(); ++I) {
+ size_t I = 0;
+ DenseMap<SectionBase *, int> Order = buildSectionOrder();
+ // Add input sections to output sections.
+ for (BaseCommand *Base : SectionCommands) {
// Handle symbol assignments outside of any output section.
- if (auto *Cmd = dyn_cast<SymbolAssignment>(Opt.Commands[I])) {
+ if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
addSymbol(Cmd);
continue;
}
- if (auto *Cmd = dyn_cast<OutputSectionCommand>(Opt.Commands[I])) {
- std::vector<InputSectionBase *> V = createInputSectionList(*Cmd);
+ if (auto *Sec = dyn_cast<OutputSection>(Base)) {
+ std::vector<InputSection *> V = createInputSectionList(*Sec, Order);
// 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;
}
@@ -413,250 +383,265 @@ 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
- // way to "make it as if it wasn't present" is to just remove it.
- if (!matchConstraints(V, Cmd->Constraint)) {
+ // Because we'll iterate over SectionCommands many more times, the easy
+ // way to "make it as if it wasn't present" is to make it empty.
+ if (!matchConstraints(V, Sec->Constraint)) {
for (InputSectionBase *S : V)
S->Assigned = false;
- Opt.Commands.erase(Opt.Commands.begin() + I);
- --I;
+ Sec->SectionCommands.clear();
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);
+
+ Sec->SectionIndex = I++;
+ if (Sec->Noload)
+ Sec->Type = SHT_NOBITS;
}
}
- CurAddressState = nullptr;
+ Ctx = nullptr;
}
-void LinkerScript::fabricateDefaultCommands() {
- std::vector<BaseCommand *> Commands;
-
- // Define start address
- uint64_t StartAddr = -1;
-
- // 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)
- StartAddr = std::min(StartAddr, KV.second);
-
- Commands.push_back(make<SymbolAssignment>(
- ".",
- [=] {
- return std::min(StartAddr, Config->ImageBase + elf::getHeaderSize());
- },
- ""));
+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;
+}
- // 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;
-
- 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;
- }
+static OutputSection *createSection(InputSectionBase *IS,
+ StringRef OutsecName) {
+ OutputSection *Sec = Script->createOutputSection(OutsecName, "<internal>");
+ Sec->addSection(cast<InputSection>(IS));
+ return Sec;
+}
+
+static OutputSection *addInputSec(StringMap<OutputSection *> &Map,
+ InputSectionBase *IS, StringRef OutsecName) {
+ // 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
+ // should combine these relocation sections into single output.
+ // We skip synthetic sections because it can be .rela.dyn/.rela.plt or any
+ // other REL[A] sections created by linker itself.
+ if (!isa<SyntheticSection>(IS) &&
+ (IS->Type == SHT_REL || IS->Type == SHT_RELA)) {
+ auto *Sec = cast<InputSection>(IS);
+ OutputSection *Out = Sec->getRelocatedSection()->getOutputSection();
+
+ if (Out->RelocationSection) {
+ Out->RelocationSection->addSection(Sec);
+ return nullptr;
}
+
+ Out->RelocationSection = createSection(IS, OutsecName);
+ return Out->RelocationSection;
+ }
+
+ // When control reaches here, mergeable sections have already been merged into
+ // synthetic sections. For relocatable case we want to create one output
+ // section per syntetic section so that they have a valid sh_entsize.
+ if (Config->Relocatable && (IS->Flags & SHF_MERGE))
+ return createSection(IS, OutsecName);
+
+ // The ELF spec just says
+ // ----------------------------------------------------------------
+ // In the first phase, input sections that match in name, type and
+ // attribute flags should be concatenated into single sections.
+ // ----------------------------------------------------------------
+ //
+ // However, it is clear that at least some flags have to be ignored for
+ // section merging. At the very least SHF_GROUP and SHF_COMPRESSED have to be
+ // ignored. We should not have two output .text sections just because one was
+ // in a group and another was not for example.
+ //
+ // It also seems that that wording was a late addition and didn't get the
+ // necessary scrutiny.
+ //
+ // Merging sections with different flags is expected by some users. One
+ // reason is that if one file has
+ //
+ // int *const bar __attribute__((section(".foo"))) = (int *)0;
+ //
+ // gcc with -fPIC will produce a read only .foo section. But if another
+ // file has
+ //
+ // int zed;
+ // int *const bar __attribute__((section(".foo"))) = (int *)&zed;
+ //
+ // gcc with -fPIC will produce a read write section.
+ //
+ // Last but not least, when using linker script the merge rules are forced by
+ // the script. Unfortunately, linker scripts are name based. This means that
+ // expressions like *(.foo*) can refer to multiple input sections with
+ // different flags. We cannot put them in different output sections or we
+ // would produce wrong results for
+ //
+ // start = .; *(.foo.*) end = .; *(.bar)
+ //
+ // and a mapping of .foo1 and .bar1 to one section and .foo2 and .bar2 to
+ // another. The problem is that there is no way to layout those output
+ // sections such that the .foo sections are the only thing between the start
+ // and end symbols.
+ //
+ // Given the above issues, we instead merge sections by name and error on
+ // incompatible types and flags.
+ OutputSection *&Sec = Map[OutsecName];
+ if (Sec) {
+ Sec->addSection(cast<InputSection>(IS));
+ return nullptr;
}
- // SECTIONS commands run before other non SECTIONS commands
- Commands.insert(Commands.end(), Opt.Commands.begin(), Opt.Commands.end());
- Opt.Commands = std::move(Commands);
+
+ Sec = createSection(IS, OutsecName);
+ return Sec;
}
// Add sections that didn't match any sections command.
-void LinkerScript::addOrphanSections(OutputSectionFactory &Factory) {
- unsigned NumCommands = Opt.Commands.size();
+void LinkerScript::addOrphanSections() {
+ unsigned End = SectionCommands.size();
+ StringMap<OutputSection *> Map;
+
+ std::vector<OutputSection *> V;
for (InputSectionBase *S : InputSections) {
if (!S->Live || S->Parent)
continue;
- StringRef Name = getOutputSectionName(S->Name);
- auto End = Opt.Commands.begin() + NumCommands;
- auto I = std::find_if(Opt.Commands.begin(), End, [&](BaseCommand *Base) {
- if (auto *Cmd = dyn_cast<OutputSectionCommand>(Base))
- return Cmd->Name == Name;
- return false;
- });
- OutputSectionCommand *Cmd;
- if (I == End) {
- Factory.addInputSec(S, Name);
- OutputSection *Sec = S->getOutputSection();
- assert(Sec->SectionIndex == INT_MAX);
- OutputSectionCommand *&CmdRef = SecToCommand[Sec];
- if (!CmdRef) {
- CmdRef = createOutputSectionCommand(Sec->Name, "<internal>");
- CmdRef->Sec = Sec;
- Opt.Commands.push_back(CmdRef);
- }
- Cmd = CmdRef;
- } else {
- 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;
- }
+
+ StringRef Name = getOutputSectionName(S);
+
+ if (Config->OrphanHandling == OrphanHandlingPolicy::Error)
+ error(toString(S) + " is being placed in '" + Name + "'");
+ else if (Config->OrphanHandling == OrphanHandlingPolicy::Warn)
+ warn(toString(S) + " is being placed in '" + Name + "'");
+
+ if (OutputSection *Sec =
+ findByName(makeArrayRef(SectionCommands).slice(0, End), Name)) {
+ Sec->addSection(cast<InputSection>(S));
+ continue;
}
- auto *ISD = make<InputSectionDescription>("");
- ISD->Sections.push_back(cast<InputSection>(S));
- Cmd->Commands.push_back(ISD);
+
+ if (OutputSection *OS = addInputSec(Map, S, Name))
+ V.push_back(OS);
+ assert(S->getOutputSection()->SectionIndex == INT_MAX);
}
+
+ // If no SECTIONS command was given, we should insert sections commands
+ // before others, so that we can handle scripts which refers them,
+ // for example: "foo = ABSOLUTE(ADDR(.text)));".
+ // When SECTIONS command is present we just add all orphans to the end.
+ if (HasSectionsCommand)
+ SectionCommands.insert(SectionCommands.end(), V.begin(), V.end());
+ else
+ SectionCommands.insert(SectionCommands.begin(), V.begin(), V.end());
}
-uint64_t LinkerScript::advance(uint64_t Size, unsigned Align) {
- bool IsTbss = (CurAddressState->OutSec->Flags & SHF_TLS) &&
- CurAddressState->OutSec->Type == SHT_NOBITS;
- uint64_t Start = IsTbss ? Dot + CurAddressState->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)
- CurAddressState->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() - CurAddressState->OutSec->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) }
- CurAddressState->OutSec->Size = Pos - CurAddressState->OutSec->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 (CurAddressState->MemRegion) {
- uint64_t &CurOffset =
- CurAddressState->MemRegionOffset[CurAddressState->MemRegion];
- CurOffset += CurAddressState->OutSec->Size;
- uint64_t CurSize = CurOffset - CurAddressState->MemRegion->Origin;
- if (CurSize > CurAddressState->MemRegion->Length) {
- uint64_t OverflowAmt = CurSize - CurAddressState->MemRegion->Length;
- error("section '" + CurAddressState->OutSec->Name +
- "' will not fit in region '" + CurAddressState->MemRegion->Name +
- "': overflowed by " + Twine(OverflowAmt) + " bytes");
+ 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 (CurAddressState->OutSec == Sec)
+ if (Ctx->OutSec == Sec)
return;
- CurAddressState->OutSec = Sec;
- CurAddressState->OutSec->Addr =
- advance(0, CurAddressState->OutSec->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 (CurAddressState->LMAOffset)
- CurAddressState->OutSec->LMAOffset = CurAddressState->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 - CurAddressState->OutSec->Addr;
- Dot += Cmd->Size;
- CurAddressState->OutSec->Size = Dot - CurAddressState->OutSec->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(CurAddressState->OutSec == 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.
@@ -667,33 +652,76 @@ 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);
- if (Cmd->LMAExpr) {
+ Ctx->MemRegion = Sec->MemRegion;
+ if (Ctx->MemRegion)
+ Dot = Ctx->MemRegionOffset[Ctx->MemRegion];
+
+ if (Sec->LMAExpr) {
uint64_t D = Dot;
- CurAddressState->LMAOffset = [=] { return Cmd->LMAExpr().getValue() - D; };
+ Ctx->LMAOffset = [=] { return Sec->LMAExpr().getValue() - D; };
}
- CurAddressState->MemRegion = Cmd->MemRegion;
- if (CurAddressState->MemRegion)
- Dot = CurAddressState->MemRegionOffset[CurAddressState->MemRegion];
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 (CurAddressState->OutSec->Flags & SHF_COMPRESSED)
+ if (Ctx->OutSec->Flags & SHF_COMPRESSED)
return;
- for (BaseCommand *C : Cmd->Commands)
- process(*C);
+ // The Size previously denoted how many InputSections had been added to this
+ // section, and was used for sorting SHF_LINK_ORDER sections. Reset it to
+ // compute the actual size value.
+ Sec->Size = 0;
+
+ // 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() {
@@ -703,17 +731,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;
@@ -721,38 +747,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());
}
}
@@ -764,108 +807,112 @@ 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()) {
- OutputSection *Sec = Cmd->Sec;
+ if (Sec->Phdrs.empty()) {
// To match the bfd linker script behaviour, only propagate program
// headers to sections that are allocated.
- if (Sec && (Sec->Flags & SHF_ALLOC))
- Cmd->Phdrs = DefPhdrs;
+ if (Sec->Flags & SHF_ALLOC)
+ Sec->Phdrs = DefPhdrs;
} else {
- DefPhdrs = Cmd->Phdrs;
+ DefPhdrs = Sec->Phdrs;
}
}
-
- removeEmptyCommands();
}
-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();
- }
+static OutputSection *findFirstSection(PhdrEntry *Load) {
+ for (OutputSection *Sec : OutputSections)
+ if (Sec->PtLoad == Load)
+ return Sec;
+ return nullptr;
}
-void LinkerScript::allocateHeaders(std::vector<PhdrEntry> &Phdrs) {
+// 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 (OutputSectionCommand *Cmd : OutputSectionCommands) {
- OutputSection *Sec = Cmd->Sec;
+ for (OutputSection *Sec : OutputSections)
if (Sec->Flags & SHF_ALLOC)
Min = std::min<uint64_t>(Min, Sec->Addr);
- }
- auto FirstPTLoad = llvm::find_if(
- Phdrs, [](const PhdrEntry &E) { return E.p_type == PT_LOAD; });
- if (FirstPTLoad == Phdrs.end())
+ 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;
}
- 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 = llvm::find_if(
- Phdrs, [](const PhdrEntry &E) { return E.p_type == PT_PHDR; });
- if (PhdrI != Phdrs.end())
- Phdrs.erase(PhdrI);
+ llvm::erase_if(Phdrs,
+ [](const PhdrEntry *E) { return E->p_type == PT_PHDR; });
}
-LinkerScript::AddressState::AddressState(const ScriptConfiguration &Opt) {
- for (auto &MRI : Opt.MemoryRegions) {
- const MemoryRegion *MR = &MRI.second;
+LinkerScript::AddressState::AddressState() {
+ for (auto &MRI : Script->MemoryRegions) {
+ const MemoryRegion *MR = MRI.second;
MemRegionOffset[MR] = MR->Origin;
}
}
+static uint64_t getInitialDot() {
+ // By default linker scripts use an initial value of 0 for '.',
+ // but prefer -image-base if set.
+ if (Script->HasSectionsCommand)
+ return Config->ImageBase ? *Config->ImageBase : 0;
+
+ 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)
+ StartAddr = std::min(StartAddr, KV.second);
+ return std::min(StartAddr, Target->getImageBase() + elf::getHeaderSize());
+}
+
+// Here we assign addresses as instructed by linker script SECTIONS
+// sub-commands. Doing that allows us to use final VA values, so here
+// we also handle rest commands like symbol assignments and ASSERTs.
void LinkerScript::assignAddresses() {
- // Assign addresses as instructed by linker script SECTIONS sub-commands.
- Dot = 0;
- auto State = make_unique<AddressState>(Opt);
- // CurAddressState 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.
- CurAddressState = State.get();
+ Dot = getInitialDot();
+
+ auto Deleter = make_unique<AddressState>();
+ Ctx = Deleter.get();
ErrorOnMissingSection = true;
switchTo(Aether);
- for (BaseCommand *Base : Opt.Commands) {
+ for (BaseCommand *Base : SectionCommands) {
if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
assignSymbol(Cmd, false);
continue;
@@ -876,380 +923,98 @@ void LinkerScript::assignAddresses() {
continue;
}
- auto *Cmd = cast<OutputSectionCommand>(Base);
- assignOffsets(Cmd);
+ assignOffsets(cast<OutputSection>(Base));
}
- CurAddressState = nullptr;
+ 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) {
+ for (OutputSection *Sec : OutputSections) {
// Assign headers specified by linker script
- for (size_t Id : getPhdrIndices(Cmd)) {
- OutputSection *Sec = Cmd->Sec;
- Ret[Id].add(Sec);
- if (Opt.PhdrsCommands[Id].Flags == UINT_MAX)
- Ret[Id].p_flags |= Sec->getPhdrFlags();
+ for (size_t Id : getPhdrIndices(Sec)) {
+ 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)
- 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;
-}
-
-void OutputSectionCommand::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;
- assert(Commands.size() == 1);
- auto *ISD = cast<InputSectionDescription>(Commands[0]);
- for (InputSection *S : ISD->Sections)
- V.push_back({Order(S), S});
- std::stable_sort(V.begin(), V.end(), Comp);
- ISD->Sections.clear();
- for (Pair &P : V)
- ISD->Sections.push_back(P.second);
-}
-
-// 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".
+// Returns true if we should emit an .interp section.
//
-// 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 OutputSectionCommand::sortCtorsDtors() {
- assert(Commands.size() == 1);
- auto *ISD = cast<InputSectionDescription>(Commands[0]);
- std::stable_sort(ISD->Sections.begin(), ISD->Sections.end(), compCtors);
-}
-
-// 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 OutputSectionCommand::sortInitFini() {
- // Sort sections by priority.
- sort([](InputSectionBase *S) { return getPriority(S->Name); });
-}
-
-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) {
- 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.
- elf::ObjectFile<ELFT> *Obj = Sections[0]->getFile<ELFT>();
- ArrayRef<SymbolBody *> Symbols = Obj->getSymbols();
- OS->Info = InX::SymTab->getSymbolIndex(Symbols[Sections[0]->Info - 1]);
+// 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 true;
+ return false;
}
-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::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())
- Sec->Link = D->getParent()->SectionIndex;
+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;
}
- 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;
+ if (Symbol *Sym = Symtab->find(Name)) {
+ if (auto *DS = dyn_cast<Defined>(Sym))
+ return {DS->Section, false, DS->Value, Loc};
+ if (auto *SS = dyn_cast<SharedSymbol>(Sym))
+ if (!ErrorOnMissingSection || SS->CopyRelSec)
+ return {SS->CopyRelSec, false, 0, Loc};
}
- // 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);
-}
-
-ExprValue LinkerScript::getSymbolValue(const Twine &Loc, StringRef S) {
- if (S == ".")
- return {CurAddressState->OutSec, Dot - CurAddressState->OutSec->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};
- }
- error(Loc + ": symbol not found: " + S);
+ error(Loc + ": symbol not found: " + Name);
return 0;
}
-bool LinkerScript::isDefined(StringRef S) { return findSymbol(S) != nullptr; }
-
-static const size_t NoPhdr = -1;
+// 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(OutputSectionCommand *Cmd) {
+std::vector<size_t> LinkerScript::getPhdrIndices(OutputSection *Cmd) {
std::vector<size_t> Ret;
- for (StringRef PhdrName : Cmd->Phdrs) {
- size_t Index = getPhdrIndex(Cmd->Location, PhdrName);
- if (Index != NoPhdr)
- Ret.push_back(Index);
- }
- return Ret;
-}
-// Returns the index of the segment named PhdrName if found otherwise
-// NoPhdr. When not found, if PhdrName is not the special case value 'NONE'
-// (which can be used to explicitly specify that a section isn't assigned to a
-// segment) then error.
-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");
}
- if (PhdrName != "NONE")
- error(Loc + ": section header '" + PhdrName + "' is not listed in PHDRS");
- return NoPhdr;
+ 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 dd5a7d797f60..11131dda8e26 100644
--- a/ELF/LinkerScript.h
+++ b/ELF/LinkerScript.h
@@ -13,10 +13,11 @@
#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"
+#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/MemoryBuffer.h"
#include <cstddef>
@@ -28,31 +29,39 @@
namespace lld {
namespace elf {
-class DefinedCommon;
-class SymbolBody;
+class Defined;
+class Symbol;
class InputSectionBase;
class InputSection;
class OutputSection;
-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;
+ Defined *Sym = nullptr;
// The RHS of an expression.
Expr Expression;
@@ -114,37 +125,6 @@ struct MemoryRegion {
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();
-
- void sort(std::function<int(InputSectionBase *S)> Order);
- void sortInitFini();
- void sortCtorsDtors();
-};
-
// 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.
@@ -158,11 +138,14 @@ struct SectionPattern {
SortSectionPolicy SortInner;
};
+class ThunkSection;
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;
@@ -171,23 +154,28 @@ struct InputSectionDescription : BaseCommand {
std::vector<SectionPattern> SectionPatterns;
std::vector<InputSection *> Sections;
+
+ // Temporary record of synthetic ThunkSection instances and the pass that
+ // they were created in. This is used to insert newly created ThunkSections
+ // into Sections at the end of a createThunks() pass.
+ std::vector<std::pair<ThunkSection *, uint32_t>> ThunkSections;
};
// Represents an ASSERT().
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;
@@ -196,106 +184,103 @@ 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 {
- // Temporary state used in processCommands() and assignAddresses()
+ // 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;
- AddressState(const ScriptConfiguration &Opt);
};
- llvm::DenseMap<OutputSection *, OutputSectionCommand *> SecToCommand;
- llvm::DenseMap<StringRef, OutputSectionCommand *> NameToOutputSectionCommand;
+ 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 *);
+ computeInputSections(const InputSectionDescription *,
+ const llvm::DenseMap<SectionBase *, int> &Order);
- std::vector<InputSectionBase *>
- createInputSectionList(OutputSectionCommand &Cmd);
+ std::vector<InputSection *>
+ createInputSectionList(OutputSection &Cmd,
+ const llvm::DenseMap<SectionBase *, int> &Order);
- std::vector<size_t> getPhdrIndices(OutputSectionCommand *Cmd);
- size_t getPhdrIndex(const Twine &Loc, StringRef PhdrName);
+ std::vector<size_t> getPhdrIndices(OutputSection *Sec);
- 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);
- AddressState *CurAddressState = nullptr;
+ void assignOffsets(OutputSection *Sec);
+
+ // 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.
+ // This should remain a plain pointer as its lifetime is smaller than
+ // LinkerScript.
+ AddressState *Ctx = nullptr;
+
OutputSection *Aether;
uint64_t Dot;
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);
+ void addOrphanSections();
void removeEmptyCommands();
void adjustSectionsBeforeSorting();
void adjustSectionsAfterSorting();
- std::vector<PhdrEntry> createPhdrs();
- bool ignoreInterpSection();
+ std::vector<PhdrEntry *> createPhdrs();
+ bool needsInterpSection();
bool shouldKeep(InputSectionBase *S);
- void assignOffsets(OutputSectionCommand *Cmd);
- void processNonSectionCommands();
void assignAddresses();
- void allocateHeaders(std::vector<PhdrEntry> &Phdrs);
- void addSymbol(SymbolAssignment *Cmd);
- void processCommands(OutputSectionFactory &Factory);
+ void allocateHeaders(std::vector<PhdrEntry *> &Phdrs);
+ void processSectionCommands();
+
+ // SECTIONS command list.
+ std::vector<BaseCommand *> SectionCommands;
+
+ // PHDRS command list.
+ std::vector<PhdrsCommand> PhdrsCommands;
+
+ bool HasSectionsCommand = false;
+ bool ErrorOnMissingSection = false;
- // Parsed linker script configurations are set to this struct.
- ScriptConfiguration Opt;
+ // 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::MapVector<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 2b2a95c47cf9..dcc829315e64 100644
--- a/ELF/MapFile.cpp
+++ b/ELF/MapFile.cpp
@@ -25,8 +25,9 @@
#include "OutputSections.h"
#include "Strings.h"
#include "SymbolTable.h"
-#include "Threads.h"
-
+#include "Symbols.h"
+#include "SyntheticSections.h"
+#include "lld/Common/Threads.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
@@ -35,46 +36,57 @@ using namespace llvm::object;
using namespace lld;
using namespace lld::elf;
-typedef DenseMap<const SectionBase *, SmallVector<DefinedRegular *, 4>>
- SymbolMapTy;
+typedef DenseMap<const SectionBase *, SmallVector<Symbol *, 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 && Sym->Section->Live)
- V.push_back(Sym);
+static std::vector<Symbol *> getSymbols() {
+ std::vector<Symbol *> V;
+ for (InputFile *File : ObjectFiles) {
+ for (Symbol *B : File->getSymbols()) {
+ if (auto *SS = dyn_cast<SharedSymbol>(B))
+ if (SS->CopyRelSec || SS->NeedsPltAddr)
+ V.push_back(SS);
+ if (auto *DR = dyn_cast<Defined>(B))
+ if (DR->File == File && !DR->isSection() && DR->Section &&
+ DR->Section->Live)
+ V.push_back(DR);
+ }
+ }
return V;
}
// Returns a map from sections to their symbols.
-template <class ELFT>
-SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> Syms) {
+static SymbolMapTy getSectionSyms(ArrayRef<Symbol *> Syms) {
SymbolMapTy Ret;
- for (DefinedRegular *S : Syms)
- Ret[S->Section].push_back(S);
+ for (Symbol *S : Syms) {
+ if (auto *DR = dyn_cast<Defined>(S)) {
+ Ret[DR->Section].push_back(S);
+ continue;
+ }
+
+ SharedSymbol *SS = cast<SharedSymbol>(S);
+ if (SS->CopyRelSec)
+ Ret[SS->CopyRelSec].push_back(S);
+ else
+ Ret[InX::Plt].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<Symbol *> &V = It.second;
+ std::sort(V.begin(), V.end(),
+ [](Symbol *A, Symbol *B) { return A->getVA() < B->getVA(); });
}
return Ret;
}
@@ -82,25 +94,22 @@ SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> Syms) {
// Construct a map from symbols to their stringified representations.
// 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<Symbol *, std::string>
+getSymbolStrings(ArrayRef<Symbol *> 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]->getSize(), 0);
OS << indent(2) << toString(*Syms[I]);
});
- DenseMap<DefinedRegular *, std::string> Ret;
+ DenseMap<Symbol *, 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) {
+void elf::writeMapFile() {
if (Config->MapFile.empty())
return;
@@ -113,38 +122,32 @@ 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<Symbol *> Syms = getSymbols();
+ SymbolMapTy SectionSyms = getSectionSyms(Syms);
+ DenseMap<Symbol *, std::string> SymStr = getSymbolStrings(Syms);
// Print out the header line.
- int W = ELFT::Is64Bits ? 16 : 8;
+ int W = Config->Is64 ? 16 : 8;
OS << left_justify("Address", W) << ' ' << left_justify("Size", W)
<< " 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 (Symbol *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 *>);
diff --git a/ELF/MapFile.h b/ELF/MapFile.h
index 460848ff24d3..2d93e26d4cf8 100644
--- a/ELF/MapFile.h
+++ b/ELF/MapFile.h
@@ -10,13 +10,9 @@
#ifndef LLD_ELF_MAPFILE_H
#define LLD_ELF_MAPFILE_H
-#include <llvm/ADT/ArrayRef.h>
-
namespace lld {
namespace elf {
-struct OutputSectionCommand;
-template <class ELFT>
-void writeMapFile(llvm::ArrayRef<OutputSectionCommand *> Script);
+void writeMapFile();
} // namespace elf
} // namespace lld
diff --git a/ELF/MarkLive.cpp b/ELF/MarkLive.cpp
index bde3eefc6d5f..36f994f20490 100644
--- a/ELF/MarkLive.cpp
+++ b/ELF/MarkLive.cpp
@@ -22,13 +22,13 @@
#include "InputSection.h"
#include "LinkerScript.h"
-#include "Memory.h"
#include "OutputSections.h"
#include "Strings.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "Target.h"
#include "Writer.h"
+#include "lld/Common/Memory.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Object/ELF.h"
#include <functional>
@@ -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,36 @@ static DenseMap<StringRef, std::vector<InputSectionBase *>> CNamedSections;
template <class ELFT, class RelT>
static void resolveReloc(InputSectionBase &Sec, RelT &Rel,
- std::function<void(ResolvedReloc)> Fn) {
- SymbolBody &B = Sec.getFile<ELFT>()->getRelocTargetSym(Rel);
- if (auto *D = dyn_cast<DefinedRegular>(&B)) {
- if (!D->Section)
+ std::function<void(InputSectionBase *, uint64_t)> Fn) {
+ Symbol &B = Sec.getFile<ELFT>()->getRelocTargetSym(Rel);
+
+ // If a symbol is referenced in a live section, it is used.
+ B.Used = true;
+ if (auto *SS = dyn_cast<SharedSymbol>(&B))
+ if (!SS->isWeak())
+ SS->getFile<ELFT>()->IsNeeded = true;
+
+ if (auto *D = dyn_cast<Defined>(&B)) {
+ auto *RelSec = dyn_cast_or_null<InputSectionBase>(D->Section);
+ if (!RelSec)
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), Offset});
- } else if (auto *U = dyn_cast<Undefined>(&B)) {
- for (InputSectionBase *Sec : CNamedSections.lookup(U->getName()))
- Fn({Sec, 0});
+ Fn(RelSec, Offset);
+ return;
}
+
+ if (!B.isDefined())
+ 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 +98,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 +118,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 +131,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,14 +164,14 @@ 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:
-// 1) Sections used by the loader (.init, .fini, .ctors, .dtors or .jcr)
-// 2) Non-allocatable sections which typically contain debugging information
+// Some sections are used directly by the loader, so they should never be
+// garbage-collected. This function returns true if a given section is such
+// section.
template <class ELFT> static bool isReserved(InputSectionBase *Sec) {
switch (Sec->Type) {
case SHT_FINI_ARRAY:
@@ -175,9 +180,6 @@ template <class ELFT> static bool isReserved(InputSectionBase *Sec) {
case SHT_PREINIT_ARRAY:
return true;
default:
- if (!(Sec->Flags & SHF_ALLOC))
- return true;
-
StringRef S = Sec->Name;
return S.startswith(".ctors") || S.startswith(".dtors") ||
S.startswith(".init") || S.startswith(".fini") ||
@@ -188,72 +190,74 @@ 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))
- 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) {
- if (auto *D = dyn_cast_or_null<DefinedRegular>(Sym))
- if (auto *IS = cast_or_null<InputSectionBase>(D->Section))
- Enqueue({IS, D->Value});
+ auto MarkSymbol = [&](Symbol *Sym) {
+ if (auto *D = dyn_cast_or_null<Defined>(Sym))
+ if (auto *IS = dyn_cast_or_null<InputSectionBase>(D->Section))
+ 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());
+ MarkSymbol(S);
// Preserve special sections and those which are specified in linker
// script KEEP command.
for (InputSectionBase *Sec : InputSections) {
- // .eh_frame is always marked as live now, but also it can reference to
- // sections that contain personality. We preserve all non-text sections
- // referred by .eh_frame here.
- if (auto *EH = dyn_cast_or_null<EhInputSection>(Sec))
+ // Mark .eh_frame sections as live because there are usually no relocations
+ // that point to .eh_frames. Otherwise, the garbage collector would drop
+ // all of them. We also want to preserve personality routines and LSDA
+ // referenced by .eh_frame sections, so we scan them for that here.
+ if (auto *EH = dyn_cast_or_null<EhInputSection>(Sec)) {
+ EH->Live = true;
scanEhFrameSection<ELFT>(*EH, Enqueue);
+ }
+
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 +266,49 @@ 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>();
+
+ // Report garbage-collected sections.
+ if (Config->PrintGcSections)
+ for (InputSectionBase *Sec : InputSections)
+ if (!Sec->Live)
+ message("removing unused section from '" + Sec->Name + "' in file '" +
+ Sec->File->getName() + "'");
+}
+
template void elf::markLive<ELF32LE>();
template void elf::markLive<ELF32BE>();
template void elf::markLive<ELF64LE>();
diff --git a/ELF/Options.td b/ELF/Options.td
index 1400a206bdfc..20027e90aefd 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,37 +93,42 @@ 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">;
-def exclude_libs: S<"exclude-libs">,
+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 filter: J<"filter=">, HelpText<"Set DT_FILTER field to the specified name">;
+defm filter: Eq<"filter">,
+ HelpText<"Set DT_FILTER field to the specified name">;
-def fini: S<"fini">, MetaVarName<"<symbol>">,
- HelpText<"Specify a finalizer function">;
+defm fini: Eq<"fini">,
+ HelpText<"Specify a finalizer function">, MetaVarName<"<symbol>">;
+
+def fix_cortex_a53_843419: F<"fix-cortex-a53-843419">,
+ HelpText<"Apply fixes for AArch64 Cortex-A53 erratum 843419">;
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">;
@@ -121,29 +136,35 @@ 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_all: F<"icf=all">, HelpText<"Enable identical code folding">;
+def icf_data: F<"icf-data">,
+ HelpText<"Enable ICF to also fold identical read only data">;
+
def icf_none: F<"icf=none">, HelpText<"Disable identical code folding">;
-def image_base : J<"image-base=">, HelpText<"Set the base address">;
+defm image_base : Eq<"image-base">, HelpText<"Set the base address">;
-def init: S<"init">, MetaVarName<"<symbol>">,
- HelpText<"Specify an initializer function">;
+defm init: Eq<"init">, HelpText<"Specify an initializer function">,
+ MetaVarName<"<symbol>">;
-def l: JoinedOrSeparate<["-"], "l">, MetaVarName<"<libName>">,
- HelpText<"Root name of library to use">;
+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 merge_exidx_entries: F<"merge-exidx-entries">,
+ HelpText<"Enable merging .ARM.exidx entries">;
def nostdlib: F<"nostdlib">,
HelpText<"Only search directories specified on the command line">;
@@ -163,15 +184,24 @@ 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">;
+def no_merge_exidx_entries: F<"no-merge-exidx-entries">,
+ HelpText<"Disable merging .ARM.exidx entries">;
+
def no_threads: F<"no-threads">,
HelpText<"Do not run the linker multi-threaded">;
@@ -183,7 +213,14 @@ 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_omagic: Flag<["--"], "no-omagic">, MetaVarName<"<magic>">,
+ HelpText<"Do not set the text data sections to be writable">;
+
+def no_print_gc_sections: F<"no-print-gc-sections">,
+ HelpText<"Do not list removed unused sections">;
+
+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">;
@@ -200,6 +237,12 @@ def oformat: Separate<["--"], "oformat">, MetaVarName<"<format>">,
def omagic: Flag<["--"], "omagic">, MetaVarName<"<magic>">,
HelpText<"Set the text and data sections to be readable and writable">;
+defm orphan_handling: Eq<"orphan-handling">,
+ HelpText<"Control how orphan sections are handled when linker script used">;
+
+def pack_dyn_relocs_eq: J<"pack-dyn-relocs=">, MetaVarName<"<format>">,
+ HelpText<"Pack dynamic relocations in the given format (none or android)">;
+
def pie: F<"pie">, HelpText<"Create a position independent executable">;
def print_gc_sections: F<"print-gc-sections">,
@@ -208,26 +251,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">;
@@ -239,27 +284,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">;
@@ -268,8 +315,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">;
@@ -280,8 +326,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">;
@@ -293,60 +339,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_exclude_libs: J<"exclude-libs=">, Alias<exclude_libs>;
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_filter: Separate<["-"], "F">, Alias<filter>;
-def alias_fini_fini: J<"fini=">, Alias<fini>;
def alias_format_b: S<"b">, Alias<format>;
-def alias_hash_style_hash_style: J<"hash-style=">, Alias<hash_style>;
-def alias_init_init: J<"init=">, Alias<init>;
-def alias_l__library: J<"library=">, Alias<l>;
-def alias_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_script_eq: J<"version-script=">, Alias<version_script>;
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.
@@ -369,6 +391,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">;
@@ -385,18 +409,17 @@ 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 long_plt: F<"long-plt">;
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_ctors_in_init_array: F<"no-ctors-in-init-array">;
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">;
@@ -406,9 +429,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">;
-
diff --git a/ELF/OutputSections.cpp b/ELF/OutputSections.cpp
index abe548165866..f0677f7e1ca5 100644
--- a/ELF/OutputSections.cpp
+++ b/ELF/OutputSections.cpp
@@ -10,13 +10,14 @@
#include "OutputSections.h"
#include "Config.h"
#include "LinkerScript.h"
-#include "Memory.h"
#include "Strings.h"
#include "SymbolTable.h"
#include "SyntheticSections.h"
#include "Target.h"
-#include "Threads.h"
+#include "lld/Common/Memory.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,210 +68,376 @@ 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 (!Live) {
+ // If IS is the first section to be added to this section,
+ // initialize Type and Entsize from IS.
+ Live = true;
+ Type = IS->Type;
+ Entsize = IS->Entsize;
+ } 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;
+ }
+ }
- // 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->Parent = this;
+ Flags |= IS->Flags;
+ Alignment = std::max(Alignment, IS->Alignment);
+ IS->OutSecOff = Size++;
// 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.
- // 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);
+ // holds the element size. If it contains elements of different size we
+ // set sh_entsize to 0.
+ if (Entsize != IS->Entsize)
+ Entsize = 0;
+
+ 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);
+ }
}
-static SectionKey createKey(InputSectionBase *C, StringRef OutsecName) {
- // The ELF spec just says
- // ----------------------------------------------------------------
- // In the first phase, input sections that match in name, type and
- // attribute flags should be concatenated into single sections.
- // ----------------------------------------------------------------
- //
- // However, it is clear that at least some flags have to be ignored for
- // section merging. At the very least SHF_GROUP and SHF_COMPRESSED have to be
- // ignored. We should not have two output .text sections just because one was
- // in a group and another was not for example.
- //
- // It also seems that that wording was a late addition and didn't get the
- // necessary scrutiny.
- //
- // Merging sections with different flags is expected by some users. One
- // reason is that if one file has
- //
- // int *const bar __attribute__((section(".foo"))) = (int *)0;
- //
- // gcc with -fPIC will produce a read only .foo section. But if another
- // file has
- //
- // int zed;
- // int *const bar __attribute__((section(".foo"))) = (int *)&zed;
- //
- // gcc with -fPIC will produce a read write section.
- //
- // Last but not least, when using linker script the merge rules are forced by
- // the script. Unfortunately, linker scripts are name based. This means that
- // expressions like *(.foo*) can refer to multiple input sections with
- // different flags. We cannot put them in different output sections or we
- // would produce wrong results for
- //
- // start = .; *(.foo.*) end = .; *(.bar)
- //
- // and a mapping of .foo1 and .bar1 to one section and .foo2 and .bar2 to
- // another. The problem is that there is no way to layout those output
- // sections such that the .foo sections are the only thing between the start
- // and end symbols.
- //
- // 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);
- }
+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; };
+
+ std::vector<Pair> V;
+ for (InputSection *S : In)
+ V.push_back({Order(S), S});
+ std::stable_sort(V.begin(), V.end(), Comp);
- return SectionKey{OutsecName, Flags, Alignment};
+ for (size_t I = 0; I < V.size(); ++I)
+ In[I] = V[I].second;
}
-OutputSectionFactory::OutputSectionFactory() {}
+uint64_t elf::getHeaderSize() {
+ if (Config->OFormatBinary)
+ return 0;
+ return Out::ElfHeader->Size + Out::ProgramHeaders->Size;
+}
-static uint64_t getIncompatibleFlags(uint64_t Flags) {
- return Flags & (SHF_ALLOC | SHF_TLS);
+bool OutputSection::classof(const BaseCommand *C) {
+ return C->Kind == OutputSectionKind;
}
-// 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::sort(std::function<int(InputSectionBase *S)> Order) {
+ assert(Live);
+ assert(SectionCommands.size() == 1);
+ sortByOrder(cast<InputSectionDescription>(SectionCommands[0])->Sections,
+ Order);
}
-void elf::reportDiscarded(InputSectionBase *IS) {
- if (!Config->PrintGcSections)
- return;
- message("removing unused section from '" + IS->Name + "' in file '" +
- IS->File->getName() + "'");
+// 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);
}
-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;
- }
+// Compress section contents if this section contains debug info.
+template <class ELFT> void OutputSection::maybeCompress() {
+ typedef typename ELFT::Chdr Elf_Chdr;
- // 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
- // should combine these relocation sections into single output.
- // We skip synthetic sections because it can be .rela.dyn/.rela.plt or any
- // other REL[A] sections created by linker itself.
- if (!isa<SyntheticSection>(IS) &&
- (IS->Type == SHT_REL || IS->Type == SHT_RELA)) {
- auto *Sec = cast<InputSection>(IS);
- OutputSection *Out = Sec->getRelocatedSection()->getOutputSection();
- addInputSec(IS, OutsecName, Out->RelocationSection);
+ // Compress only DWARF debug sections.
+ if (!Config->CompressDebugSections || (Flags & SHF_ALLOC) ||
+ !Name.startswith(".debug_"))
return;
- }
- SectionKey Key = createKey(IS, OutsecName);
- OutputSection *&Sec = Map[Key];
- addInputSec(IS, OutsecName, Sec);
+ // Calculate the section offsets and size pre-compression.
+ Size = 0;
+ for (BaseCommand *Cmd : SectionCommands)
+ if (auto *ISD = dyn_cast<InputSectionDescription>(Cmd))
+ for (InputSection *IS : ISD->Sections) {
+ IS->OutSecOff = alignTo(Size, IS->Alignment);
+ this->Size = IS->OutSecOff + IS->getSize();
+ }
+
+ // Create a section header.
+ ZDebugHeader.resize(sizeof(Elf_Chdr));
+ auto *Hdr = reinterpret_cast<Elf_Chdr *>(ZDebugHeader.data());
+ 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;
}
-void OutputSectionFactory::addInputSec(InputSectionBase *IS,
- StringRef OutsecName,
- OutputSection *&Sec) {
- if (!IS->Live) {
- reportDiscarded(IS);
+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;
}
- 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;
+ // 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
- 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));
+ End = Buf + Sections[I + 1]->OutSecOff;
+ fill(Start, End - Start, Filler);
}
- Sec->Flags |= IS->Flags;
- } else {
- Sec = make<OutputSection>(OutsecName, IS->Type, IS->Flags);
- OutputSections.push_back(Sec);
+ });
+
+ // 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);
+}
+
+template <class ELFT>
+static void finalizeShtGroup(OutputSection *OS,
+ InputSection *Section) {
+ assert(Config->Relocatable);
+
+ // 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 = Section->getFile<ELFT>();
+ ArrayRef<Symbol *> Symbols = Obj->getSymbols();
+ OS->Info = InX::SymTab->getSymbolIndex(Symbols[Section->Info]);
+}
+
+template <class ELFT> void OutputSection::finalize() {
+ InputSection *First = nullptr;
+ for (BaseCommand *Base : SectionCommands) {
+ if (auto *ISD = dyn_cast<InputSectionDescription>(Base)) {
+ if (ISD->Sections.empty())
+ continue;
+ if (First == nullptr)
+ First = ISD->Sections.front();
+ }
+ if (isa<ByteCommand>(Base) && Type == SHT_NOBITS)
+ Type = SHT_PROGBITS;
+ }
+
+ if (Flags & SHF_LINK_ORDER) {
+ // 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 = First->getLinkOrderDep())
+ Link = D->getParent()->SectionIndex;
+ }
+
+ if (Type == SHT_GROUP) {
+ finalizeShtGroup<ELFT>(this, First);
+ return;
}
- Sec->addSection(cast<InputSection>(IS));
+ if (!Config->CopyRelocs || (Type != SHT_RELA && Type != SHT_REL))
+ return;
+
+ 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);
}
-OutputSectionFactory::~OutputSectionFactory() {}
+static bool isCrtbegin(StringRef S) { return isCrtBeginEnd(S, "crtbegin"); }
+static bool isCrtend(StringRef S) { return isCrtBeginEnd(S, "crtend"); }
-SectionKey DenseMapInfo<SectionKey>::getEmptyKey() {
- return SectionKey{DenseMapInfo<StringRef>::getEmptyKey(), 0, 0};
+// .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;
}
-SectionKey DenseMapInfo<SectionKey>::getTombstoneKey() {
- return SectionKey{DenseMapInfo<StringRef>::getTombstoneKey(), 0, 0};
+// 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);
}
-unsigned DenseMapInfo<SectionKey>::getHashValue(const SectionKey &Val) {
- return hash_combine(Val.Name, Val.Flags, Val.Alignment);
+// If an input string is in the form of "foo.N" where N is a number,
+// return N. Otherwise, returns 65536, which is one greater than the
+// lowest priority.
+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 DenseMapInfo<SectionKey>::isEqual(const SectionKey &LHS,
- const SectionKey &RHS) {
- return DenseMapInfo<StringRef>::isEqual(LHS.Name, RHS.Name) &&
- LHS.Flags == RHS.Flags && LHS.Alignment == RHS.Alignment;
+// 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); });
}
-uint64_t elf::getHeaderSize() {
- if (Config->OFormatBinary)
- return 0;
- return Out::ElfHeader->Size + Out::ProgramHeaders->Size;
+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 68b46ebf6a7b..b2845773e9af 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"
@@ -22,23 +23,23 @@ namespace lld {
namespace elf {
struct PhdrEntry;
-class SymbolBody;
+class Symbol;
struct EhSectionPiece;
class EhInputSection;
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;
+class Defined;
// This represents a section in an output file.
// 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,42 +57,68 @@ 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),
// it may have a non-null value.
OutputSection *RelocationSection = nullptr;
- // The following fields correspond to Elf_Shdr members.
+ // Initially this field is the number of InputSections that have been added to
+ // the OutputSection so far. Later on, after a call to assignAddresses, it
+ // corresponds to the Elf_Shdr member.
uint64_t Size = 0;
+
+ // The following fields correspond to Elf_Shdr members.
uint64_t Offset = 0;
uint64_t LMAOffset = 0;
uint64_t Addr = 0;
uint32_t ShName = 0;
- void addSection(InputSection *S);
- std::vector<InputSection *> Sections;
+ 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();
+
+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.
@@ -106,47 +135,17 @@ struct Out {
static OutputSection *FiniArray;
};
-struct SectionKey {
- StringRef Name;
- uint64_t Flags;
- uint32_t Alignment;
-};
} // namespace elf
} // namespace lld
-namespace llvm {
-template <> struct DenseMapInfo<lld::elf::SectionKey> {
- static lld::elf::SectionKey getEmptyKey();
- static lld::elf::SectionKey getTombstoneKey();
- static unsigned getHashValue(const lld::elf::SectionKey &Val);
- 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();
- ~OutputSectionFactory();
-
- void addInputSec(InputSectionBase *IS, StringRef OutsecName);
- void addInputSec(InputSectionBase *IS, StringRef OutsecName,
- OutputSection *&Sec);
-
-private:
- llvm::SmallDenseMap<SectionKey, OutputSection *> Map;
-};
-
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
diff --git a/ELF/Relocations.cpp b/ELF/Relocations.cpp
index e5fcb2dcc582..96e409578f5c 100644
--- a/ELF/Relocations.cpp
+++ b/ELF/Relocations.cpp
@@ -44,13 +44,14 @@
#include "Relocations.h"
#include "Config.h"
#include "LinkerScript.h"
-#include "Memory.h"
#include "OutputSections.h"
#include "Strings.h"
#include "SymbolTable.h"
+#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "Thunks.h"
+#include "lld/Common/Memory.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/raw_ostream.h"
@@ -70,30 +71,35 @@ using namespace lld::elf;
// >>> referenced by bar.c:12 (/home/alice/src/bar.c:12)
// >>> /home/alice/src/bar.o:(.text+0x1)
template <class ELFT>
-static std::string getLocation(InputSectionBase &S, const SymbolBody &Sym,
+static std::string getLocation(InputSectionBase &S, const Symbol &Sym,
uint64_t Off) {
std::string Msg =
"\n>>> defined in " + toString(Sym.File) + "\n>>> referenced by ";
- std::string Src = S.getSrcMsg<ELFT>(Off);
+ std::string Src = S.getSrcMsg<ELFT>(Sym, Off);
if (!Src.empty())
Msg += Src + "\n>>> ";
- return Msg + S.getObjMsg<ELFT>(Off);
+ return Msg + S.getObjMsg(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,28 +109,28 @@ 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, Symbol &Sym,
InputSectionBase &C, uint64_t Offset,
int64_t Addend, RelExpr Expr) {
if (Expr == R_MIPS_TLSLD) {
if (InX::MipsGot->addTlsIndex() && Config->Pic)
- In<ELFT>::RelaDyn->addReloc({Target->TlsModuleIndexRel, InX::MipsGot,
- InX::MipsGot->getTlsIndexOff(), false,
- nullptr, 0});
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
+ InX::RelaDyn->addReloc({Target->TlsModuleIndexRel, InX::MipsGot,
+ InX::MipsGot->getTlsIndexOff(), false, nullptr,
+ 0});
+ C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
return 1;
}
if (Expr == R_MIPS_TLSGD) {
- 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())
- In<ELFT>::RelaDyn->addReloc({Target->TlsOffsetRel, InX::MipsGot,
- Off + Config->Wordsize, false, &Body, 0});
+ if (InX::MipsGot->addDynTlsEntry(Sym) && Sym.IsPreemptible) {
+ uint64_t Off = InX::MipsGot->getGlobalDynOffset(Sym);
+ InX::RelaDyn->addReloc(
+ {Target->TlsModuleIndexRel, InX::MipsGot, Off, false, &Sym, 0});
+ if (Sym.IsPreemptible)
+ InX::RelaDyn->addReloc({Target->TlsOffsetRel, InX::MipsGot,
+ Off + Config->Wordsize, false, &Sym, 0});
}
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
+ C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
return 1;
}
return 0;
@@ -145,19 +151,18 @@ 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, Symbol &Sym,
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 = Sym.IsPreemptible || Config->Shared;
+ bool NeedDynOff = Sym.IsPreemptible;
- auto AddTlsReloc = [&](uint64_t Off, uint32_t Type, SymbolBody *Dest,
- bool Dyn) {
+ auto AddTlsReloc = [&](uint64_t Off, RelType Type, Symbol *Dest, bool Dyn) {
if (Dyn)
- In<ELFT>::RelaDyn->addReloc({Type, InX::Got, Off, false, Dest, 0});
+ InX::RelaDyn->addReloc({Type, InX::Got, Off, false, Dest, 0});
else
InX::Got->Relocations.push_back({R_ABS, Type, Off, 0, Dest});
};
@@ -168,8 +173,8 @@ static unsigned handleARMTlsRelocation(uint32_t Type, SymbolBody &Body,
// module. GOT[e1] is unused. There only needs to be one module index entry.
if (Expr == R_TLSLD_PC && InX::Got->addTlsIndex()) {
AddTlsReloc(InX::Got->getTlsIndexOff(), Target->TlsModuleIndexRel,
- NeedDynId ? nullptr : &Body, NeedDynId);
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
+ NeedDynId ? nullptr : &Sym, NeedDynId);
+ C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
return 1;
}
@@ -177,13 +182,13 @@ static unsigned handleARMTlsRelocation(uint32_t Type, SymbolBody &Body,
// the module index and offset of symbol in TLS block we can fill these in
// using static GOT relocations.
if (Expr == R_TLSGD_PC) {
- if (InX::Got->addDynTlsEntry(Body)) {
- uint64_t Off = InX::Got->getGlobalDynOffset(Body);
- AddTlsReloc(Off, Target->TlsModuleIndexRel, &Body, NeedDynId);
- AddTlsReloc(Off + Config->Wordsize, Target->TlsOffsetRel, &Body,
+ if (InX::Got->addDynTlsEntry(Sym)) {
+ uint64_t Off = InX::Got->getGlobalDynOffset(Sym);
+ AddTlsReloc(Off, Target->TlsModuleIndexRel, &Sym, NeedDynId);
+ AddTlsReloc(Off + Config->Wordsize, Target->TlsOffsetRel, &Sym,
NeedDynOff);
}
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
+ C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
return 1;
}
return 0;
@@ -192,29 +197,28 @@ 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, Symbol &Sym, InputSectionBase &C,
typename ELFT::uint Offset, int64_t Addend, RelExpr Expr) {
if (!(C.Flags & SHF_ALLOC))
return 0;
- if (!Body.isTls())
+ if (!Sym.isTls())
return 0;
if (Config->EMachine == EM_ARM)
- return handleARMTlsRelocation<ELFT>(Type, Body, C, Offset, Addend, Expr);
+ return handleARMTlsRelocation<ELFT>(Type, Sym, C, Offset, Addend, Expr);
if (Config->EMachine == EM_MIPS)
- return handleMipsTlsRelocation<ELFT>(Type, Body, C, Offset, Addend, Expr);
+ return handleMipsTlsRelocation<ELFT>(Type, Sym, 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});
+ if (InX::Got->addDynTlsEntry(Sym)) {
+ uint64_t Off = InX::Got->getGlobalDynOffset(Sym);
+ InX::RelaDyn->addReloc(
+ {Target->TlsDescRel, InX::Got, Off, !Sym.IsPreemptible, &Sym, 0});
}
if (Expr != R_TLSDESC_CALL)
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
+ C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
return 1;
}
@@ -222,61 +226,59 @@ handleTlsRelocation(uint32_t Type, SymbolBody &Body, InputSectionBase &C,
// Local-Dynamic relocs can be relaxed to Local-Exec.
if (!Config->Shared) {
C.Relocations.push_back(
- {R_RELAX_TLS_LD_TO_LE, Type, Offset, Addend, &Body});
+ {R_RELAX_TLS_LD_TO_LE, Type, Offset, Addend, &Sym});
return 2;
}
if (InX::Got->addTlsIndex())
- In<ELFT>::RelaDyn->addReloc({Target->TlsModuleIndexRel, InX::Got,
- InX::Got->getTlsIndexOff(), false, nullptr,
- 0});
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
+ InX::RelaDyn->addReloc({Target->TlsModuleIndexRel, InX::Got,
+ InX::Got->getTlsIndexOff(), false, nullptr, 0});
+ C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
return 1;
}
// Local-Dynamic relocs can be relaxed to Local-Exec.
if (isRelExprOneOf<R_ABS, R_TLSLD, R_TLSLD_PC>(Expr) && !Config->Shared) {
- C.Relocations.push_back(
- {R_RELAX_TLS_LD_TO_LE, Type, Offset, Addend, &Body});
+ C.Relocations.push_back({R_RELAX_TLS_LD_TO_LE, Type, Offset, Addend, &Sym});
return 1;
}
if (isRelExprOneOf<R_TLSDESC, R_TLSDESC_PAGE, R_TLSDESC_CALL, R_TLSGD,
R_TLSGD_PC>(Expr)) {
if (Config->Shared) {
- if (InX::Got->addDynTlsEntry(Body)) {
- uint64_t Off = InX::Got->getGlobalDynOffset(Body);
- In<ELFT>::RelaDyn->addReloc(
- {Target->TlsModuleIndexRel, InX::Got, Off, false, &Body, 0});
+ if (InX::Got->addDynTlsEntry(Sym)) {
+ uint64_t Off = InX::Got->getGlobalDynOffset(Sym);
+ InX::RelaDyn->addReloc(
+ {Target->TlsModuleIndexRel, InX::Got, Off, false, &Sym, 0});
// If the symbol is preemptible we need the dynamic linker to write
// the offset too.
uint64_t OffsetOff = Off + Config->Wordsize;
- if (IsPreemptible)
- In<ELFT>::RelaDyn->addReloc(
- {Target->TlsOffsetRel, InX::Got, OffsetOff, false, &Body, 0});
+ if (Sym.IsPreemptible)
+ InX::RelaDyn->addReloc(
+ {Target->TlsOffsetRel, InX::Got, OffsetOff, false, &Sym, 0});
else
InX::Got->Relocations.push_back(
- {R_ABS, Target->TlsOffsetRel, OffsetOff, 0, &Body});
+ {R_ABS, Target->TlsOffsetRel, OffsetOff, 0, &Sym});
}
- C.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
+ C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
return 1;
}
// Global-Dynamic relocs can be relaxed to Initial-Exec or Local-Exec
// depending on the symbol being locally defined or not.
- if (IsPreemptible) {
+ if (Sym.IsPreemptible) {
C.Relocations.push_back(
{Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_GD_TO_IE), Type,
- Offset, Addend, &Body});
- if (!Body.isInGot()) {
- InX::Got->addEntry(Body);
- In<ELFT>::RelaDyn->addReloc({Target->TlsGotRel, InX::Got,
- Body.getGotOffset(), false, &Body, 0});
+ Offset, Addend, &Sym});
+ if (!Sym.isInGot()) {
+ InX::Got->addEntry(Sym);
+ InX::RelaDyn->addReloc(
+ {Target->TlsGotRel, InX::Got, Sym.getGotOffset(), false, &Sym, 0});
}
} else {
C.Relocations.push_back(
{Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_GD_TO_LE), Type,
- Offset, Addend, &Body});
+ Offset, Addend, &Sym});
}
return Target->TlsGdRelaxSkip;
}
@@ -284,9 +286,8 @@ 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) {
- C.Relocations.push_back(
- {R_RELAX_TLS_IE_TO_LE, Type, Offset, Addend, &Body});
+ !Config->Shared && !Sym.IsPreemptible) {
+ C.Relocations.push_back({R_RELAX_TLS_IE_TO_LE, Type, Offset, Addend, &Sym});
return 1;
}
@@ -295,12 +296,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:
@@ -312,16 +323,16 @@ 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 (const auto *DR = dyn_cast<DefinedRegular>(&Body))
+static bool isAbsolute(const Symbol &Sym) {
+ if (Sym.isUndefWeak())
+ return true;
+ if (const auto *DR = dyn_cast<Defined>(&Sym))
return DR->Section == nullptr; // Absolute symbol.
return false;
}
-static bool isAbsoluteValue(const SymbolBody &Body) {
- return isAbsolute(Body) || Body.isTls();
+static bool isAbsoluteValue(const Symbol &Sym) {
+ return isAbsolute(Sym) || Sym.isTls();
}
// Returns true if Expr refers a PLT entry.
@@ -355,8 +366,7 @@ 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,
- const SymbolBody &Body,
+static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym,
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,
@@ -371,14 +381,14 @@ 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 (Sym.IsPreemptible)
return false;
if (!Config->Pic)
return true;
// For the target and the relocation, we want to know if they are
// absolute or relative.
- bool AbsVal = isAbsoluteValue(Body);
+ bool AbsVal = isAbsoluteValue(Sym);
bool RelE = isRelExpr(E);
if (AbsVal && !RelE)
return true;
@@ -396,11 +406,11 @@ 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 (Sym.isUndefWeak())
return true;
error("relocation " + toString(Type) + " cannot refer to absolute symbol: " +
- toString(Body) + getLocation<ELFT>(S, Body, RelOff));
+ toString(Sym) + getLocation<ELFT>(S, Sym, RelOff));
return true;
}
@@ -431,14 +441,13 @@ static RelExpr fromPlt(RelExpr Expr) {
// Returns true if a given shared symbol is in a read-only segment in a DSO.
template <class ELFT> static bool isReadOnly(SharedSymbol *SS) {
typedef typename ELFT::Phdr Elf_Phdr;
- 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 &&
- Value < Phdr.p_vaddr + Phdr.p_memsz)
+ !(Phdr.p_flags & ELF::PF_W) && SS->Value >= Phdr.p_vaddr &&
+ SS->Value < Phdr.p_vaddr + Phdr.p_memsz)
return true;
return false;
}
@@ -452,16 +461,15 @@ template <class ELFT>
static std::vector<SharedSymbol *> getSymbolsAt(SharedSymbol *SS) {
typedef typename ELFT::Sym Elf_Sym;
- auto *File = cast<SharedFile<ELFT>>(SS->File);
- uint64_t Shndx = SS->getShndx<ELFT>();
- uint64_t Value = SS->getValue<ELFT>();
+ SharedFile<ELFT> *File = SS->getFile<ELFT>();
std::vector<SharedSymbol *> Ret;
- for (const Elf_Sym &S : File->getGlobalSymbols()) {
- if (S.st_shndx != Shndx || S.st_value != Value)
+ for (const Elf_Sym &S : File->getGlobalELFSyms()) {
+ if (S.st_shndx == SHN_UNDEF || S.st_shndx == SHN_ABS ||
+ S.st_value != SS->Value)
continue;
StringRef Name = check(S.getName(File->getStringTable()));
- SymbolBody *Sym = Symtab<ELFT>::X->find(Name);
+ Symbol *Sym = Symtab->find(Name);
if (auto *Alias = dyn_cast_or_null<SharedSymbol>(Sym))
Ret.push_back(Alias);
}
@@ -512,79 +520,101 @@ static std::vector<SharedSymbol *> getSymbolsAt(SharedSymbol *SS) {
// define an accessor getV().
template <class ELFT> static void addCopyRelSymbol(SharedSymbol *SS) {
// Copy relocation against zero-sized symbol doesn't make sense.
- uint64_t SymSize = SS->template getSize<ELFT>();
+ uint64_t SymSize = SS->getSize();
if (SymSize == 0)
fatal("cannot create a copy relocation for symbol " + toString(*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->Alignment);
+ 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->symbol()->IsUsedInRegularObj = true;
+ Sym->IsPreemptible = false;
+ Sym->IsUsedInRegularObj = true;
+ Sym->Used = true;
}
- In<ELFT>::RelaDyn->addReloc({Target->CopyRel, Sec, Off, false, SS, 0});
+ InX::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(Symbol &Sym, 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;
- bool IsWrite = !Config->ZText || (S.Flags & SHF_WRITE);
- if (IsWrite || isStaticLinkTimeConstant<ELFT>(Expr, Type, Body, S, RelOff))
+ // 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;
- // 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
+ // 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, Sym, 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.
+
+ // If the relocation is to a weak undef, give up on it and produce a
+ // non preemptible 0.
+ if (Sym.isUndefWeak()) {
+ Sym.IsPreemptible = false;
+ return Expr;
+ }
+
+ // 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"
- : "symbol: " + toString(Body)) +
- " in readonly segment" + getLocation<ELFT>(S, Body, RelOff));
+ error(
+ "can't create dynamic relocation " + toString(Type) + " against " +
+ (Sym.getName().empty() ? "local symbol" : "symbol: " + toString(Sym)) +
+ " in readonly segment; recompile object files with -fPIC" +
+ getLocation<ELFT>(S, Sym, RelOff));
return Expr;
}
- if (Body.getVisibility() != STV_DEFAULT) {
- error("cannot preempt symbol: " + toString(Body) +
- getLocation<ELFT>(S, Body, RelOff));
+ if (Sym.getVisibility() != STV_DEFAULT) {
+ error("cannot preempt symbol: " + toString(Sym) +
+ getLocation<ELFT>(S, Sym, RelOff));
return Expr;
}
- if (Body.isObject()) {
+ if (Sym.isObject()) {
// Produce a copy relocation.
- auto *B = cast<SharedSymbol>(&Body);
- if (!B->NeedsCopy) {
+ auto *B = dyn_cast<SharedSymbol>(&Sym);
+ if (B && !B->CopyRelSec) {
if (Config->ZNocopyreloc)
error("unresolvable relocation " + toString(Type) +
" against symbol '" + toString(*B) +
"'; recompile with -fPIC or remove '-z nocopyreloc'" +
- getLocation<ELFT>(S, Body, RelOff));
+ getLocation<ELFT>(S, Sym, RelOff));
addCopyRelSymbol<ELFT>(B);
}
return Expr;
}
- if (Body.isFunc()) {
+ if (Sym.isFunc()) {
// This handles a non PIC program call to function in a shared library. In
// an ideal world, we could just report an error saying the relocation can
// overflow at runtime. In the real world with glibc, crt1.o has a
@@ -605,39 +635,25 @@ static RelExpr adjustExpr(SymbolBody &Body, RelExpr Expr, uint32_t Type,
// that points to the real function is a dedicated got entry used by the
// plt. That is identified by special relocation types (R_X86_64_JUMP_SLOT,
// R_386_JMP_SLOT, etc).
- Body.NeedsPltAddr = true;
+ Sym.NeedsPltAddr = true;
+ Sym.IsPreemptible = false;
return toPlt(Expr);
}
- error("symbol '" + toString(Body) + "' defined in " + toString(Body.File) +
- " has no type");
+ errorOrWarn("symbol '" + toString(Sym) + "' defined in " +
+ toString(Sym.File) + " 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 +661,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 +671,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(Symbol &Sym, InputSectionBase &Sec,
+ uint64_t Offset) {
if (Config->UnresolvedSymbols == UnresolvedPolicy::IgnoreAll)
- return;
+ return false;
+
+ if (Sym.isLocal() || !Sym.isUndefined() || Sym.isWeak())
+ return false;
- bool CanBeExternal = Sym.symbol()->computeBinding() != STB_LOCAL &&
- Sym.getVisibility() == STV_DEFAULT;
+ bool CanBeExternal =
+ Sym.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>(Sym, Offset);
if (!Src.empty())
Msg += Src + "\n>>> ";
- Msg += S.getObjMsg<ELFT>(Offset);
+ Msg += Sec.getObjMsg(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,73 +770,84 @@ 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,
- SymbolBody &Sym, bool UseSymVA) {
+ RelocationBaseSection *Rel, RelType Type, Symbol &Sym,
+ bool UseSymVA) {
Plt->addEntry<ELFT>(Sym);
GotPlt->addEntry(Sym);
Rel->addReloc({Type, GotPlt, Sym.getGotPltOffset(), UseSymVA, &Sym, 0});
}
-template <class ELFT>
-static void addGotEntry(SymbolBody &Sym, bool Preemptible) {
+template <class ELFT> static void addGotEntry(Symbol &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;
+ InX::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
@@ -816,41 +867,64 @@ template <class ELFT, class RelTy>
static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) {
OffsetGetter GetOffset(Sec);
+ // Not all relocations end up in Sec.Relocations, but a lot do.
+ Sec.Relocations.reserve(Rels.size());
+
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);
-
- if (Config->MipsN32Abi) {
- uint32_t Processed;
- std::tie(Type, Processed) =
- mergeMipsN32RelTypes(Type, Rel.r_offset, I + 1, End);
- I += Processed;
- }
+ Symbol &Sym = Sec.getFile<ELFT>()->getRelocTargetSym(Rel);
+ RelType Type = Rel.getType(Config->IsMips64EL);
+
+ // 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>(Sym, Sec, Rel.r_offset))
+ continue;
RelExpr Expr =
- Target->getRelExpr(Type, Body, Sec.Data.begin() + Rel.r_offset);
+ Target->getRelExpr(Type, Sym, Sec.Data.begin() + Rel.r_offset);
// Ignore "hint" relocations because they are only markers for relaxation.
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);
- if (ErrorCount)
+ // Handle yet another MIPS-ness.
+ if (isMipsGprel(Type)) {
+ int64_t Addend = computeAddend<ELFT>(Rel, End, Sec, Expr, Sym.isLocal());
+ Sec.Relocations.push_back({R_MIPS_GOTREL, Type, Offset, Addend, &Sym});
+ continue;
+ }
+
+ bool Preemptible = Sym.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 (Sym.isGnuIFunc())
+ Expr = toPlt(Expr);
+ else if (!Preemptible && Expr == R_GOT_PC && !isAbsoluteValue(Sym))
+ Expr =
+ Target->adjustRelaxExpr(Type, Sec.Data.data() + Rel.r_offset, Expr);
+ else if (!Preemptible)
+ Expr = fromPlt(Expr);
+
+ Expr = adjustExpr<ELFT>(Sym, Expr, Type, Sec, Rel.r_offset);
+ if (errorCount())
continue;
// This relocation does not require got entry, but it is relative to got and
@@ -860,26 +934,24 @@ 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, Sym.isLocal());
// Process some TLS relocations, including relaxing TLS relocations.
// Note that this function does not handle all TLS relocations.
if (unsigned Processed =
- handleTlsRelocation<ELFT>(Type, Body, Sec, Offset, Addend, Expr)) {
+ handleTlsRelocation<ELFT>(Type, Sym, Sec, Offset, Addend, Expr)) {
I += (Processed - 1);
continue;
}
// If a relocation needs PLT, we create PLT and GOTPLT slots for the symbol.
- if (needsPlt(Expr) && !Body.isInPlt()) {
- if (Body.isGnuIFunc() && !Preemptible)
- addPltEntry(InX::Iplt, InX::IgotPlt, In<ELFT>::RelaIplt,
- Target->IRelativeRel, Body, true);
+ if (needsPlt(Expr) && !Sym.isInPlt()) {
+ if (Sym.isGnuIFunc() && !Preemptible)
+ addPltEntry<ELFT>(InX::Iplt, InX::IgotPlt, InX::RelaIplt,
+ Target->IRelativeRel, Sym, true);
else
- addPltEntry(InX::Plt, InX::GotPlt, In<ELFT>::RelaPlt, Target->PltRel,
- Body, !Preemptible);
+ addPltEntry<ELFT>(InX::Plt, InX::GotPlt, InX::RelaPlt, Target->PltRel,
+ Sym, !Preemptible);
}
// Create a GOT slot if a relocation needs GOT.
@@ -892,25 +964,26 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) {
// See "Global Offset Table" in Chapter 5 in the following document
// for detailed description:
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
- InX::MipsGot->addEntry(Body, Addend, Expr);
- if (Body.isTls() && Body.isPreemptible())
- In<ELFT>::RelaDyn->addReloc({Target->TlsGotRel, InX::MipsGot,
- Body.getGotOffset(), false, &Body, 0});
- } else if (!Body.isInGot()) {
- addGotEntry<ELFT>(Body, Preemptible);
+ InX::MipsGot->addEntry(Sym, Addend, Expr);
+ if (Sym.isTls() && Sym.IsPreemptible)
+ InX::RelaDyn->addReloc({Target->TlsGotRel, InX::MipsGot,
+ Sym.getGotOffset(), false, &Sym, 0});
+ } else if (!Sym.isInGot()) {
+ addGotEntry<ELFT>(Sym, Preemptible);
}
}
- if (!needsPlt(Expr) && !needsGot(Expr) && isPreemptible(Body, Type)) {
+ if (!needsPlt(Expr) && !needsGot(Expr) && Sym.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, Sym, Offset));
- In<ELFT>::RelaDyn->addReloc(
- {Target->getDynRel(Type), &Sec, Offset, false, &Body, Addend});
+ InX::RelaDyn->addReloc(
+ {Target->getDynRel(Type), &Sec, Offset, false, &Sym, Addend});
// MIPS ABI turns using of GOT and dynamic relocations inside out.
// While regular ABI uses dynamic relocations to fill up GOT entries
@@ -928,32 +1001,40 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) {
// a dynamic relocation.
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf p.4-19
if (Config->EMachine == EM_MIPS)
- InX::MipsGot->addEntry(Body, Addend, Expr);
+ InX::MipsGot->addEntry(Sym, Addend, Expr);
continue;
}
// If the relocation points to something in the file, we can process it.
bool IsConstant =
- isStaticLinkTimeConstant<ELFT>(Expr, Type, Body, Sec, Rel.r_offset);
+ isStaticLinkTimeConstant<ELFT>(Expr, Type, Sym, Sec, Rel.r_offset);
// The size is not going to change, so we fold it in here.
if (Expr == R_SIZE)
- Addend += Body.getSize<ELFT>();
+ Addend += Sym.getSize();
+
+ // 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, &Sym});
+ 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)
- 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)
- Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Body});
+ if (Config->IsRela) {
+ InX::RelaDyn->addReloc(
+ {Target->RelativeRel, &Sec, Offset, true, &Sym, Addend});
+ } else {
+ // In REL, addends are stored to the target section.
+ InX::RelaDyn->addReloc(
+ {Target->RelativeRel, &Sec, Offset, true, &Sym, 0});
+ Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
+ }
}
}
@@ -964,178 +1045,365 @@ template <class ELFT> void elf::scanRelocations(InputSectionBase &S) {
scanRelocs<ELFT>(S, S.rels<ELFT>());
}
+// Thunk Implementation
+//
+// Thunks (sometimes called stubs, veneers or branch islands) are small pieces
+// of code that the linker inserts inbetween a caller and a callee. The thunks
+// are added at link time rather than compile time as the decision on whether
+// a thunk is needed, such as the caller and callee being out of range, can only
+// be made at link time.
+//
+// It is straightforward to tell given the current state of the program when a
+// thunk is needed for a particular call. The more difficult part is that
+// the thunk needs to be placed in the program such that the caller can reach
+// the thunk and the thunk can reach the callee; furthermore, adding thunks to
+// the program alters addresses, which can mean more thunks etc.
+//
+// In lld we have a synthetic ThunkSection that can hold many Thunks.
+// The decision to have a ThunkSection act as a container means that we can
+// more easily handle the most common case of a single block of contiguous
+// Thunks by inserting just a single ThunkSection.
+//
+// The implementation of Thunks in lld is split across these areas
+// Relocations.cpp : Framework for creating and placing thunks
+// Thunks.cpp : The code generated for each supported thunk
+// Target.cpp : Target specific hooks that the framework uses to decide when
+// a thunk is used
+// Synthetic.cpp : Implementation of ThunkSection
+// Writer.cpp : Iteratively call framework until no more Thunks added
+//
+// Thunk placement requirements:
+// Mips LA25 thunks. These must be placed immediately before the callee section
+// We can assume that the caller is in range of the Thunk. These are modelled
+// by Thunks that return the section they must precede with
+// getTargetInputSection().
+//
+// ARM interworking and range extension thunks. These thunks must be placed
+// within range of the caller. All implemented ARM thunks can always reach the
+// callee as they use an indirect jump via a register that has no range
+// restrictions.
+//
+// Thunk placement algorithm:
+// For Mips LA25 ThunkSections; the placement is explicit, it has to be before
+// getTargetInputSection().
+//
+// For thunks that must be placed within range of the caller there are many
+// possible choices given that the maximum range from the caller is usually
+// much larger than the average InputSection size. Desirable properties include:
+// - Maximize reuse of thunks by multiple callers
+// - Minimize number of ThunkSections to simplify insertion
+// - Handle impact of already added Thunks on addresses
+// - Simple to understand and implement
+//
+// In lld for the first pass, we pre-create one or more ThunkSections per
+// InputSectionDescription at Target specific intervals. A ThunkSection is
+// placed so that the estimated end of the ThunkSection is within range of the
+// start of the InputSectionDescription or the previous ThunkSection. For
+// example:
+// InputSectionDescription
+// Section 0
+// ...
+// Section N
+// ThunkSection 0
+// Section N + 1
+// ...
+// Section N + K
+// Thunk Section 1
+//
+// The intention is that we can add a Thunk to a ThunkSection that is well
+// spaced enough to service a number of callers without having to do a lot
+// of work. An important principle is that it is not an error if a Thunk cannot
+// be placed in a pre-created ThunkSection; when this happens we create a new
+// ThunkSection placed next to the caller. This allows us to handle the vast
+// majority of thunks simply, but also handle rare cases where the branch range
+// is smaller than the target specific spacing.
+//
+// The algorithm is expected to create all the thunks that are needed in a
+// single pass, with a small number of programs needing a second pass due to
+// the insertion of thunks in the first pass increasing the offset between
+// callers and callees that were only just in range.
+//
+// A consequence of allowing new ThunkSections to be created outside of the
+// pre-created ThunkSections is that in rare cases calls to Thunks that were in
+// range in pass K, are out of range in some pass > K due to the insertion of
+// more Thunks in between the caller and callee. When this happens we retarget
+// the relocation back to the original target and create another Thunk.
+
+// Remove ThunkSections that are empty, this should only be the initial set
+// precreated on pass 0.
+
// Insert the Thunks for OutputSection OS into their designated place
// in the Sections vector, and recalculate the InputSection output section
// offsets.
// This may invalidate any output section offsets stored outside of InputSection
-void ThunkCreator::mergeThunks() {
- for (auto &KV : ThunkSections) {
- std::vector<InputSection *> *ISR = KV.first;
- std::vector<ThunkSection *> &Thunks = KV.second;
-
- // Order Thunks in ascending OutSecOff
- auto ThunkCmp = [](const ThunkSection *A, const ThunkSection *B) {
- return A->OutSecOff < B->OutSecOff;
- };
- std::stable_sort(Thunks.begin(), Thunks.end(), ThunkCmp);
-
- // Merge sorted vectors of Thunks and InputSections by OutSecOff
- std::vector<InputSection *> Tmp;
- Tmp.reserve(ISR->size() + Thunks.size());
- auto MergeCmp = [](const InputSection *A, const InputSection *B) {
- // std::merge requires a strict weak ordering.
- if (A->OutSecOff < B->OutSecOff)
- return true;
- if (A->OutSecOff == B->OutSecOff)
- // Check if Thunk is immediately before any specific Target InputSection
- // for example Mips LA25 Thunks.
- if (auto *TA = dyn_cast<ThunkSection>(A))
- if (TA && TA->getTargetInputSection() == B)
+void ThunkCreator::mergeThunks(ArrayRef<OutputSection *> OutputSections) {
+ forEachInputSectionDescription(
+ OutputSections, [&](OutputSection *OS, InputSectionDescription *ISD) {
+ if (ISD->ThunkSections.empty())
+ return;
+
+ // Remove any zero sized precreated Thunks.
+ llvm::erase_if(ISD->ThunkSections,
+ [](const std::pair<ThunkSection *, uint32_t> &TS) {
+ return TS.first->getSize() == 0;
+ });
+ // ISD->ThunkSections contains all created ThunkSections, including
+ // those inserted in previous passes. Extract the Thunks created this
+ // pass and order them in ascending OutSecOff.
+ std::vector<ThunkSection *> NewThunks;
+ for (const std::pair<ThunkSection *, uint32_t> TS : ISD->ThunkSections)
+ if (TS.second == Pass)
+ NewThunks.push_back(TS.first);
+ std::stable_sort(NewThunks.begin(), NewThunks.end(),
+ [](const ThunkSection *A, const ThunkSection *B) {
+ return A->OutSecOff < B->OutSecOff;
+ });
+
+ // Merge sorted vectors of Thunks and InputSections by OutSecOff
+ std::vector<InputSection *> Tmp;
+ Tmp.reserve(ISD->Sections.size() + NewThunks.size());
+ auto MergeCmp = [](const InputSection *A, const InputSection *B) {
+ // std::merge requires a strict weak ordering.
+ if (A->OutSecOff < B->OutSecOff)
return true;
- return false;
- };
- std::merge(ISR->begin(), ISR->end(), Thunks.begin(), Thunks.end(),
- std::back_inserter(Tmp), MergeCmp);
- *ISR = std::move(Tmp);
- }
+ if (A->OutSecOff == B->OutSecOff) {
+ auto *TA = dyn_cast<ThunkSection>(A);
+ auto *TB = dyn_cast<ThunkSection>(B);
+ // Check if Thunk is immediately before any specific Target
+ // InputSection for example Mips LA25 Thunks.
+ if (TA && TA->getTargetInputSection() == B)
+ return true;
+ if (TA && !TB && !TA->getTargetInputSection())
+ // Place Thunk Sections without specific targets before
+ // non-Thunk Sections.
+ return true;
+ }
+ return false;
+ };
+ std::merge(ISD->Sections.begin(), ISD->Sections.end(),
+ NewThunks.begin(), NewThunks.end(), std::back_inserter(Tmp),
+ MergeCmp);
+ ISD->Sections = std::move(Tmp);
+ });
}
-static uint32_t findEndOfFirstNonExec(OutputSectionCommand &Cmd) {
- for (BaseCommand *Base : Cmd.Commands)
- 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;
-}
+// Find or create a ThunkSection within the InputSectionDescription (ISD) that
+// is in range of Src. An ISD maps to a range of InputSections described by a
+// linker script section pattern such as { .text .text.* }.
+ThunkSection *ThunkCreator::getISDThunkSec(OutputSection *OS, InputSection *IS,
+ InputSectionDescription *ISD,
+ uint32_t Type, uint64_t Src) {
+ for (std::pair<ThunkSection *, uint32_t> TP : ISD->ThunkSections) {
+ ThunkSection *TS = TP.first;
+ uint64_t TSBase = OS->Addr + TS->OutSecOff;
+ uint64_t TSLimit = TSBase + TS->getSize();
+ if (Target->inBranchRange(Type, Src, (Src > TSLimit) ? TSBase : TSLimit))
+ return TS;
+ }
-ThunkSection *ThunkCreator::getOSThunkSec(OutputSectionCommand *Cmd,
- std::vector<InputSection *> *ISR) {
- if (CurTS == nullptr) {
- uint32_t Off = findEndOfFirstNonExec(*Cmd);
- CurTS = addThunkSection(Cmd->Sec, ISR, Off);
+ // No suitable ThunkSection exists. This can happen when there is a branch
+ // with lower range than the ThunkSection spacing or when there are too
+ // many Thunks. Create a new ThunkSection as close to the InputSection as
+ // possible. Error if InputSection is so large we cannot place ThunkSection
+ // anywhere in Range.
+ uint64_t ThunkSecOff = IS->OutSecOff;
+ if (!Target->inBranchRange(Type, Src, OS->Addr + ThunkSecOff)) {
+ ThunkSecOff = IS->OutSecOff + IS->getSize();
+ if (!Target->inBranchRange(Type, Src, OS->Addr + ThunkSecOff))
+ fatal("InputSection too large for range extension thunk " +
+ IS->getObjMsg(Src - (OS->Addr + IS->OutSecOff)));
}
- return CurTS;
+ return addThunkSection(OS, ISD, ThunkSecOff);
}
-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);
- std::vector<InputSection *> *Range = nullptr;
- for (BaseCommand *BC : C->Commands)
+ // Find InputSectionRange within Target Output Section (TOS) that the
+ // InputSection (IS) that we need to precede is in.
+ OutputSection *TOS = IS->getParent();
+ for (BaseCommand *BC : TOS->SectionCommands)
if (auto *ISD = dyn_cast<InputSectionDescription>(BC)) {
+ if (ISD->Sections.empty())
+ continue;
InputSection *first = ISD->Sections.front();
InputSection *last = ISD->Sections.back();
if (IS->OutSecOff >= first->OutSecOff &&
IS->OutSecOff <= last->OutSecOff) {
- Range = &ISD->Sections;
+ TS = addThunkSection(TOS, ISD, IS->OutSecOff);
+ ThunkedSections[IS] = TS;
break;
}
}
- TS = addThunkSection(TOS, Range, IS->OutSecOff);
- ThunkedSections[IS] = TS;
return TS;
}
+// Create one or more ThunkSections per OS that can be used to place Thunks.
+// We attempt to place the ThunkSections using the following desirable
+// properties:
+// - Within range of the maximum number of callers
+// - Minimise the number of ThunkSections
+//
+// We follow a simple but conservative heuristic to place ThunkSections at
+// offsets that are multiples of a Target specific branch range.
+// For an InputSectionRange that is smaller than the range, a single
+// ThunkSection at the end of the range will do.
+void ThunkCreator::createInitialThunkSections(
+ ArrayRef<OutputSection *> OutputSections) {
+ forEachInputSectionDescription(
+ OutputSections, [&](OutputSection *OS, InputSectionDescription *ISD) {
+ if (ISD->Sections.empty())
+ return;
+ uint32_t ISLimit;
+ uint32_t PrevISLimit = ISD->Sections.front()->OutSecOff;
+ uint32_t ThunkUpperBound = PrevISLimit + Target->ThunkSectionSpacing;
+
+ for (const InputSection *IS : ISD->Sections) {
+ ISLimit = IS->OutSecOff + IS->getSize();
+ if (ISLimit > ThunkUpperBound) {
+ addThunkSection(OS, ISD, PrevISLimit);
+ ThunkUpperBound = PrevISLimit + Target->ThunkSectionSpacing;
+ }
+ PrevISLimit = ISLimit;
+ }
+ addThunkSection(OS, ISD, ISLimit);
+ });
+}
+
ThunkSection *ThunkCreator::addThunkSection(OutputSection *OS,
- std::vector<InputSection *> *ISR,
+ InputSectionDescription *ISD,
uint64_t Off) {
auto *TS = make<ThunkSection>(OS, Off);
- ThunkSections[ISR].push_back(TS);
+ ISD->ThunkSections.push_back(std::make_pair(TS, Pass));
return TS;
}
-std::pair<Thunk *, bool> ThunkCreator::getThunk(SymbolBody &Body,
- uint32_t Type) {
- auto Res = ThunkedSymbols.insert({&Body, std::vector<Thunk *>()});
+std::pair<Thunk *, bool> ThunkCreator::getThunk(Symbol &Sym, RelType Type,
+ uint64_t Src) {
+ auto Res = ThunkedSymbols.insert({&Sym, std::vector<Thunk *>()});
if (!Res.second) {
- // Check existing Thunks for Body to see if they can be reused
+ // Check existing Thunks for Sym to see if they can be reused
for (Thunk *ET : Res.first->second)
- if (ET->isCompatibleWith(Type))
+ if (ET->isCompatibleWith(Type) &&
+ Target->inBranchRange(Type, Src, ET->ThunkSym->getVA()))
return std::make_pair(ET, false);
}
// No existing compatible Thunk in range, create a new one
- Thunk *T = addThunk(Type, Body);
+ Thunk *T = addThunk(Type, Sym);
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,
- std::function<void(OutputSectionCommand *, std::vector<InputSection *> *,
- InputSection *)>
- Fn) {
- for (OutputSectionCommand *Cmd : OutputSections) {
- OutputSection *OS = Cmd->Sec;
+void ThunkCreator::forEachInputSectionDescription(
+ ArrayRef<OutputSection *> OutputSections,
+ std::function<void(OutputSection *, InputSectionDescription *)> Fn) {
+ for (OutputSection *OS : OutputSections) {
if (!(OS->Flags & SHF_ALLOC) || !(OS->Flags & SHF_EXECINSTR))
continue;
- for (BaseCommand *BC : Cmd->Commands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(BC)) {
- CurTS = nullptr;
- for (InputSection *IS : ISD->Sections)
- Fn(Cmd, &ISD->Sections, IS);
- }
+ for (BaseCommand *BC : OS->SectionCommands)
+ if (auto *ISD = dyn_cast<InputSectionDescription>(BC))
+ Fn(OS, ISD);
+ }
+}
+
+// Return true if the relocation target is an in range Thunk.
+// Return false if the relocation is not to a Thunk. If the relocation target
+// was originally to a Thunk, but is no longer in range we revert the
+// relocation back to its original non-Thunk target.
+bool ThunkCreator::normalizeExistingThunk(Relocation &Rel, uint64_t Src) {
+ if (Thunk *ET = Thunks.lookup(Rel.Sym)) {
+ if (Target->inBranchRange(Rel.Type, Src, Rel.Sym->getVA()))
+ return true;
+ Rel.Sym = &ET->Destination;
+ if (Rel.Sym->isInPlt())
+ Rel.Expr = toPlt(Rel.Expr);
}
+ return false;
}
// Process all relocations from the InputSections that have been assigned
-// to OutputSections and redirect through Thunks if needed.
+// to InputSectionDescriptions and redirect through Thunks if needed. The
+// function should be called iteratively until it returns false.
+//
+// PreConditions:
+// All InputSections that may need a Thunk are reachable from
+// OutputSectionCommands.
+//
+// All OutputSections have an address and all InputSections have an offset
+// within the OutputSection.
//
-// createThunks must be called after scanRelocs has created the Relocations for
-// each InputSection. It must be called before the static symbol table is
-// finalized. If any Thunks are added to an OutputSection the output section
-// offsets of the InputSections will change.
+// The offsets between caller (relocation place) and callee
+// (relocation target) will not be modified outside of createThunks().
//
-// 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) {
- if (Pass > 0)
- ThunkSections.clear();
+// PostConditions:
+// If return value is true then ThunkSections have been inserted into
+// OutputSections. All relocations that needed a Thunk based on the information
+// available to createThunks() on entry have been redirected to a Thunk. Note
+// that adding Thunks changes offsets between caller and callee so more Thunks
+// may be required.
+//
+// If return value is false then no more Thunks are needed, and createThunks has
+// made no changes. If the target requires range extension thunks, currently
+// ARM, then any future change in offset between caller and callee risks a
+// relocation out of range error.
+bool ThunkCreator::createThunks(ArrayRef<OutputSection *> OutputSections) {
+ bool AddressesChanged = false;
+ if (Pass == 0 && Target->ThunkSectionSpacing)
+ createInitialThunkSections(OutputSections);
+ else if (Pass == 10)
+ // With Thunk Size much smaller than branch range we expect to
+ // converge quickly; if we get to 10 something has gone wrong.
+ fatal("thunk creation not converged");
// Create all the Thunks and insert them into synthetic ThunkSections. The
- // ThunkSections are later inserted back into the OutputSection.
-
+ // ThunkSections are later inserted back into InputSectionDescriptions.
// 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, [&](OutputSectionCommand *Cmd,
- 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, Cmd->Sec);
- else
- TS = getOSThunkSec(Cmd, 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);
- }
- });
+ // ThunkSections as ThunkSections are not always inserted into the same
+ // InputSectionDescription as the caller.
+ forEachInputSectionDescription(
+ OutputSections, [&](OutputSection *OS, InputSectionDescription *ISD) {
+ for (InputSection *IS : ISD->Sections)
+ for (Relocation &Rel : IS->Relocations) {
+ uint64_t Src = OS->Addr + IS->OutSecOff + Rel.Offset;
+
+ // If we are a relocation to an existing Thunk, check if it is
+ // still in range. If not then Rel will be altered to point to its
+ // original target so another Thunk can be generated.
+ if (Pass > 0 && normalizeExistingThunk(Rel, Src))
+ continue;
+
+ if (!Target->needsThunk(Rel.Expr, Rel.Type, IS->File, Src,
+ *Rel.Sym))
+ continue;
+ Thunk *T;
+ bool IsNew;
+ std::tie(T, IsNew) = getThunk(*Rel.Sym, Rel.Type, Src);
+ if (IsNew) {
+ AddressesChanged = true;
+ // Find or create a ThunkSection for the new Thunk
+ ThunkSection *TS;
+ if (auto *TIS = T->getTargetInputSection())
+ TS = getISThunkSec(TIS);
+ else
+ TS = getISDThunkSec(OS, IS, ISD, Rel.Type, Src);
+ 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();
+ mergeThunks(OutputSections);
++Pass;
- return !ThunkSections.empty();
+ return AddressesChanged;
}
template void elf::scanRelocations<ELF32LE>(InputSectionBase &);
diff --git a/ELF/Relocations.h b/ELF/Relocations.h
index ea046d248474..2cc8adfa5985 100644
--- a/ELF/Relocations.h
+++ b/ELF/Relocations.h
@@ -10,23 +10,27 @@
#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>
namespace lld {
namespace elf {
-class SymbolBody;
+class Symbol;
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,
@@ -111,21 +115,22 @@ template <RelExpr... Exprs> bool isRelExprOneOf(RelExpr Expr) {
// Architecture-neutral representation of relocation.
struct Relocation {
RelExpr Expr;
- uint32_t Type;
+ RelType Type;
uint64_t Offset;
int64_t Addend;
- SymbolBody *Sym;
+ Symbol *Sym;
};
template <class ELFT> void scanRelocations(InputSectionBase &);
class ThunkSection;
class Thunk;
+struct InputSectionDescription;
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
@@ -133,40 +138,39 @@ public:
uint32_t Pass = 0;
private:
- void mergeThunks();
- ThunkSection *getOSThunkSec(OutputSectionCommand *Cmd,
- std::vector<InputSection *> *ISR);
- ThunkSection *getISThunkSec(InputSection *IS, OutputSection *OS);
- void forEachExecInputSection(
- ArrayRef<OutputSectionCommand *> OutputSections,
- std::function<void(OutputSectionCommand *, std::vector<InputSection *> *,
- InputSection *)>
- Fn);
- std::pair<Thunk *, bool> getThunk(SymbolBody &Body, uint32_t Type);
- ThunkSection *addThunkSection(OutputSection *OS,
- std::vector<InputSection *> *, uint64_t Off);
+ void mergeThunks(ArrayRef<OutputSection *> OutputSections);
+
+ ThunkSection *getISDThunkSec(OutputSection *OS, InputSection *IS,
+ InputSectionDescription *ISD, uint32_t Type,
+ uint64_t Src);
+
+ ThunkSection *getISThunkSec(InputSection *IS);
+
+ void createInitialThunkSections(ArrayRef<OutputSection *> OutputSections);
+
+ void forEachInputSectionDescription(
+ ArrayRef<OutputSection *> OutputSections,
+ std::function<void(OutputSection *, InputSectionDescription *)> Fn);
+
+ std::pair<Thunk *, bool> getThunk(Symbol &Sym, RelType Type, uint64_t Src);
+
+ ThunkSection *addThunkSection(OutputSection *OS, InputSectionDescription *,
+ uint64_t Off);
+
+ bool normalizeExistingThunk(Relocation &Rel, uint64_t Src);
+
// Record all the available Thunks for a Symbol
- llvm::DenseMap<SymbolBody *, std::vector<Thunk *>> ThunkedSymbols;
+ llvm::DenseMap<Symbol *, 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;
+ llvm::DenseMap<Symbol *, Thunk *> Thunks;
// 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
- // will contain a mix of ThunkSections that have been created this pass, and
- // ThunkSections that have been merged into the OutputSection on previous
- // passes
- std::map<std::vector<InputSection *> *, std::vector<ThunkSection *>>
- ThunkSections;
-
- // The ThunkSection for this vector of InputSections
- ThunkSection *CurTS;
};
// Return a int64_t to make sure we get the sign extension out of the way as
diff --git a/ELF/ScriptLexer.cpp b/ELF/ScriptLexer.cpp
index 86720de3527c..9f33c16f36b0 100644
--- a/ELF/ScriptLexer.cpp
+++ b/ELF/ScriptLexer.cpp
@@ -33,7 +33,7 @@
//===----------------------------------------------------------------------===//
#include "ScriptLexer.h"
-#include "Error.h"
+#include "lld/Common/ErrorHandler.h"
#include "llvm/ADT/Twine.h"
using namespace llvm;
@@ -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 64d6d9204864..e7c8b28e49fd 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 b3847081697c..d56500ae7dd8 100644
--- a/ELF/ScriptParser.cpp
+++ b/ELF/ScriptParser.cpp
@@ -17,13 +17,14 @@
#include "Driver.h"
#include "InputSection.h"
#include "LinkerScript.h"
-#include "Memory.h"
#include "OutputSections.h"
#include "ScriptLexer.h"
#include "Symbols.h"
#include "Target.h"
+#include "lld/Common/Memory.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"
@@ -52,10 +53,10 @@ public:
void readLinkerScript();
void readVersionScript();
void readDynamicList();
+ void readDefsym(StringRef Name);
private:
void addFile(StringRef Path);
- OutputSection *checkSection(OutputSectionCommand *Cmd, StringRef Loccation);
void readAsNeeded();
void readEntry();
@@ -67,17 +68,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 +92,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,7 +113,11 @@ 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
@@ -131,7 +139,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");
@@ -139,11 +147,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) {
@@ -170,10 +178,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() {
@@ -188,7 +210,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 "
@@ -213,7 +235,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") {
@@ -232,6 +254,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") {
@@ -239,13 +263,21 @@ 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);
}
}
}
+void ScriptParser::readDefsym(StringRef Name) {
+ Expr E = readExpr();
+ if (!atEOF())
+ setError("EOF expected, but got " + next());
+ SymbolAssignment *Cmd = make<SymbolAssignment>(Name, E, getCurrentLocation());
+ Script->SectionCommands.push_back(Cmd);
+}
+
void ScriptParser::addFile(StringRef S) {
if (IsUnderSysroot && S.startswith("/")) {
SmallString<128> PathData;
@@ -256,7 +288,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())
@@ -280,7 +312,7 @@ void ScriptParser::readAsNeeded() {
expect("(");
bool Orig = Config->AsNeeded;
Config->AsNeeded = true;
- while (!Error && !consume(")"))
+ while (!errorCount() && !consume(")"))
addFile(unquote(next()));
Config->AsNeeded = Orig;
}
@@ -296,13 +328,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
@@ -313,20 +345,17 @@ void ScriptParser::readGroup() {
void ScriptParser::readInclude() {
StringRef Tok = unquote(next());
- // https://sourceware.org/binutils/docs/ld/File-Commands.html:
- // The file will be searched for in the current directory, and in any
- // directory specified with the -L option.
- if (sys::fs::exists(Tok)) {
- if (Optional<MemoryBufferRef> MB = readFile(Tok))
- tokenize(*MB);
+ if (!Seen.insert(Tok).second) {
+ setError("there is a cycle in linker script INCLUDEs");
return;
}
- if (Optional<std::string> Path = findFromSearchPaths(Tok)) {
+
+ if (Optional<std::string> Path = searchLinkerScript(Tok)) {
if (Optional<MemoryBufferRef> MB = readFile(*Path))
tokenize(*MB);
return;
}
- setError("cannot open " + Tok);
+ setError("cannot find linker script " + Tok);
}
void ScriptParser::readOutput() {
@@ -341,7 +370,7 @@ void ScriptParser::readOutput() {
void ScriptParser::readOutputArch() {
// OUTPUT_ARCH is ignored for now.
expect("(");
- while (!Error && !consume(")"))
+ while (!errorCount() && !consume(")"))
skip();
}
@@ -360,28 +389,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.insert({Alias, Script->MemoryRegions[Name]});
+}
+
void ScriptParser::readSearchDir() {
expect("(");
StringRef Tok = next();
@@ -391,7 +435,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
@@ -399,7 +443,7 @@ void ScriptParser::readSections() {
Config->SingleRoRx = true;
expect("{");
- while (!Error && !consume("}")) {
+ while (!errorCount() && !consume("}")) {
StringRef Tok = next();
BaseCommand *Cmd = readProvideOrAssignment(Tok);
if (!Cmd) {
@@ -408,7 +452,7 @@ void ScriptParser::readSections() {
else
Cmd = readOutputSectionDescription(Tok);
}
- Script->Opt.Commands.push_back(Cmd);
+ Script->SectionCommands.push_back(Cmd);
}
}
@@ -424,7 +468,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);
}
@@ -456,7 +500,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("(");
@@ -464,7 +508,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())
@@ -491,7 +535,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;
@@ -529,7 +573,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);
@@ -579,7 +623,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(")");
@@ -599,21 +643,32 @@ void ScriptParser::readSectionAddressType(OutputSectionCommand *Cmd) {
}
}
-OutputSectionCommand *
-ScriptParser::readOutputSectionDescription(StringRef OutSec) {
- OutputSectionCommand *Cmd =
- Script->createOutputSectionCommand(OutSec, getCurrentLocation());
+static Expr checkAlignment(Expr E, std::string &Loc) {
+ return [=] {
+ uint64_t Alignment = std::max((uint64_t)1, E().getValue());
+ if (!isPowerOf2_64(Alignment)) {
+ error(Loc + ": alignment must be power of 2");
+ return (uint64_t)1; // Return a dummy value.
+ }
+ return Alignment;
+ };
+}
+
+OutputSection *ScriptParser::readOutputSectionDescription(StringRef OutSec) {
+ OutputSection *Cmd =
+ Script->createOutputSection(OutSec, getCurrentLocation());
if (peek() != ":")
readSectionAddressType(Cmd);
expect(":");
+ std::string Location = getCurrentLocation();
if (consume("AT"))
Cmd->LMAExpr = readParenExpr();
if (consume("ALIGN"))
- Cmd->AlignExpr = readParenExpr();
+ Cmd->AlignExpr = checkAlignment(readParenExpr(), Location);
if (consume("SUBALIGN"))
- Cmd->SubalignExpr = readParenExpr();
+ Cmd->SubalignExpr = checkAlignment(readParenExpr(), Location);
// Parse constraints.
if (consume("ONLY_IF_RO"))
@@ -622,16 +677,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
@@ -642,7 +697,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);
}
@@ -650,6 +705,8 @@ ScriptParser::readOutputSectionDescription(StringRef OutSec) {
if (consume(">"))
Cmd->MemoryRegionName = next();
+ else if (peek().startswith(">"))
+ Cmd->MemoryRegionName = next().drop_front();
Cmd->Phdrs = readOutputSectionPhdrs();
@@ -712,7 +769,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());
}
@@ -764,7 +821,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);
@@ -790,13 +847,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
@@ -812,10 +880,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")) {
@@ -833,7 +907,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)
@@ -842,8 +916,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() {
@@ -853,14 +926,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() {
@@ -871,6 +939,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(); };
@@ -891,18 +963,21 @@ 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(")"))
+ if (consume(")")) {
+ E = checkAlignment(E, Location);
return [=] { return alignTo(Script->getDot(), E().getValue()); };
+ }
expect(",");
- Expr E2 = readExpr();
+ Expr E2 = checkAlignment(readExpr(), Location);
expect(")");
return [=] {
ExprValue V = E();
@@ -912,22 +987,25 @@ Expr ScriptParser::readPrimary() {
}
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("(");
@@ -944,28 +1022,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("(");
@@ -977,18 +1059,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))
@@ -997,8 +1079,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) {
@@ -1017,7 +1099,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));
}
@@ -1120,7 +1202,7 @@ ScriptParser::readSymbols() {
std::vector<SymbolVersion> Globals;
std::vector<SymbolVersion> *V = &Globals;
- while (!Error) {
+ while (!errorCount()) {
if (consume("}"))
break;
if (consumeLabel("local")) {
@@ -1154,7 +1236,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});
@@ -1181,7 +1263,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;
@@ -1196,12 +1278,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, Flags, NegFlags};
+ MemoryRegion *MR = make<MemoryRegion>();
+ *MR = {Name, Origin, Length, Flags, NegFlags};
+ Script->MemoryRegions[Name] = MR;
}
}
@@ -1245,3 +1327,7 @@ void elf::readVersionScript(MemoryBufferRef MB) {
void elf::readDynamicList(MemoryBufferRef MB) {
ScriptParser(MB).readDynamicList();
}
+
+void elf::readDefsym(StringRef Name, MemoryBufferRef MB) {
+ ScriptParser(MB).readDefsym(Name);
+}
diff --git a/ELF/ScriptParser.h b/ELF/ScriptParser.h
index 02f3a2bd9d2c..d48d5aa2115e 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 {
@@ -25,6 +25,9 @@ void readVersionScript(MemoryBufferRef MB);
void readDynamicList(MemoryBufferRef MB);
+// Parses the defsym expression.
+void readDefsym(StringRef Name, MemoryBufferRef MB);
+
} // namespace elf
} // namespace lld
diff --git a/ELF/Strings.cpp b/ELF/Strings.cpp
index bca86384002d..0ef33a14bc3d 100644
--- a/ELF/Strings.cpp
+++ b/ELF/Strings.cpp
@@ -9,7 +9,7 @@
#include "Strings.h"
#include "Config.h"
-#include "Error.h"
+#include "lld/Common/ErrorHandler.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
@@ -54,32 +54,9 @@ 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);
-}
-
-// Returns the demangled C++ symbol name for Name.
-Optional<std::string> elf::demangle(StringRef Name) {
- // itaniumDemangle can be used to demangle strings other than symbol
- // names which do not necessarily start with "_Z". Name can be
- // either a C or C++ symbol. Don't call itaniumDemangle if the name
- // does not look like a C++ symbol name to avoid getting unexpected
- // result for a C symbol that happens to match a mangled type name.
- if (!Name.startswith("_Z"))
- return None;
-
- char *Buf = itaniumDemangle(Name.str().c_str(), nullptr, nullptr, nullptr);
- if (!Buf)
- return None;
- std::string S(Buf);
- free(Buf);
- return S;
+ return !S.empty() && (isAlpha(S[0]) || S[0] == '_') &&
+ std::all_of(S.begin() + 1, S.end(),
+ [](char C) { return C == '_' || isAlnum(C); });
}
diff --git a/ELF/Strings.h b/ELF/Strings.h
index 68ccafa2ff17..5009df65f4c1 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"
@@ -66,10 +66,6 @@ private:
std::vector<llvm::GlobPattern> Patterns;
};
-// Returns a demangled C++ symbol name. If Name is not a mangled
-// name, it returns Optional::None.
-llvm::Optional<std::string> demangle(StringRef Name);
-
inline ArrayRef<uint8_t> toArrayRef(StringRef S) {
return {(const uint8_t *)S.data(), S.size()};
}
diff --git a/ELF/SymbolTable.cpp b/ELF/SymbolTable.cpp
index 0c932400f0ad..12509c9a1757 100644
--- a/ELF/SymbolTable.cpp
+++ b/ELF/SymbolTable.cpp
@@ -16,10 +16,12 @@
#include "SymbolTable.h"
#include "Config.h"
-#include "Error.h"
#include "LinkerScript.h"
-#include "Memory.h"
#include "Symbols.h"
+#include "SyntheticSections.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+#include "lld/Common/Strings.h"
#include "llvm/ADT/STLExtras.h"
using namespace llvm;
@@ -29,6 +31,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 +58,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 +81,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;
}
@@ -84,7 +93,7 @@ template <class ELFT> void SymbolTable<ELFT>::addFile(InputFile *File) {
if (auto *F = dyn_cast<SharedFile<ELFT>>(File)) {
// DSOs are uniquified not by filename but by soname.
F->parseSoName();
- if (ErrorCount || !SoNames.insert(F->SoName).second)
+ if (errorCount() || !SoNames.insert(F->SoName).second)
return;
SharedFiles.push_back(F);
F->parseRest();
@@ -99,9 +108,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 +119,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,82 +129,77 @@ 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);
- 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);
+Defined *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<Defined>(Sym);
}
// Set a flag for --trace-symbol so that we can print out a log message
// if a new symbol with the same name is inserted into the symbol table.
-template <class ELFT> void SymbolTable<ELFT>::trace(StringRef Name) {
- Symtab.insert({CachedHashStringRef(Name), {-1, true}});
+void SymbolTable::trace(StringRef Name) {
+ SymMap.insert({CachedHashStringRef(Name), -1});
}
// 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) {
- SymbolBody *B = find(Name);
- if (!B)
+template <class ELFT> void SymbolTable::addSymbolWrap(StringRef Name) {
+ Symbol *Sym = find(Name);
+ if (!Sym)
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));
+ WrappedSymbols.push_back({Sym, Real, Wrap});
- // Tell LTO not to eliminate this symbol
- Wrap->IsUsedInRegularObj = true;
-
- Config->RenamedSymbols[Real] = {Sym, Real->Binding};
- Config->RenamedSymbols[Sym] = {Wrap, Sym->Binding};
-}
-
-// Creates alias for symbol. Used to implement --defsym=ALIAS=SYM.
-template <class ELFT>
-void SymbolTable<ELFT>::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);
+ // We want to tell LTO not to inline symbols to be overwritten
+ // because LTO doesn't know the final symbol contents after renaming.
+ Real->CanInline = false;
+ Sym->CanInline = false;
- // Tell LTO not to eliminate this symbol
+ // Tell LTO not to eliminate these symbols.
Sym->IsUsedInRegularObj = true;
- Config->RenamedSymbols[AliasSym] = {Sym, AliasSym->Binding};
+ Wrap->IsUsedInRegularObj = true;
}
-// 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
+// Apply symbol renames created by -wrap. The renames are created
+// before LTO in addSymbolWrap() 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 *Dst = KV.first;
- Symbol *Src = KV.second.Target;
- Dst->body()->copy(Src->body());
- Dst->Binding = KV.second.OriginalBinding;
+void SymbolTable::applySymbolWrap() {
+ // This function rotates 3 symbols:
+ //
+ // __real_sym becomes sym
+ // sym becomes __wrap_sym
+ // __wrap_sym becomes __real_sym
+ //
+ // The last part is special in that we don't want to change what references to
+ // __wrap_sym point to, we just want have __real_sym in the symbol table.
+
+ for (WrappedSymbol &W : WrappedSymbols) {
+ // First, make a copy of __real_sym.
+ Symbol *Real = nullptr;
+ if (W.Real->isDefined()) {
+ Real = (Symbol *)make<SymbolUnion>();
+ memcpy(Real, W.Real, sizeof(SymbolUnion));
+ }
+
+ // Replace __real_sym with sym and sym with __wrap_sym.
+ memcpy(W.Real, W.Sym, sizeof(SymbolUnion));
+ memcpy(W.Sym, W.Wrap, sizeof(SymbolUnion));
+
+ // We now have two copies of __wrap_sym. Drop one.
+ W.Wrap->IsUsedInRegularObj = false;
+
+ if (Real)
+ SymVector.push_back(Real);
}
}
@@ -209,48 +212,50 @@ 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>.
- size_t Pos = Name.find("@@");
- if (Pos != StringRef::npos)
+ //
+ // 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;
+ auto P = SymMap.insert({CachedHashStringRef(Name), (int)SymVector.size()});
+ int &SymIndex = P.first->second;
bool IsNew = P.second;
+ bool Traced = false;
- if (V.Idx == -1) {
- IsNew = true;
- V = SymIndex((int)SymVector.size(), true);
+ if (SymIndex == -1) {
+ SymIndex = SymVector.size();
+ IsNew = Traced = true;
}
Symbol *Sym;
if (IsNew) {
- Sym = make<Symbol>();
+ Sym = (Symbol *)make<SymbolUnion>();
Sym->InVersionScript = false;
- Sym->Binding = STB_WEAK;
Sym->Visibility = STV_DEFAULT;
Sym->IsUsedInRegularObj = false;
Sym->ExportDynamic = false;
- Sym->Traced = V.Traced;
+ Sym->CanInline = true;
+ Sym->Traced = Traced;
Sym->VersionId = Config->DefaultSymbolVersion;
SymVector.push_back(Sym);
} else {
- Sym = SymVector[V.Idx];
+ Sym = SymVector[SymIndex];
}
return {Sym, IsNew};
}
// 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) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
@@ -261,32 +266,30 @@ SymbolTable<ELFT>::insert(StringRef Name, uint8_t Type, uint8_t Visibility,
if (!CanOmitFromDynSym && (Config->Shared || Config->ExportDynamic))
S->ExportDynamic = true;
- if (IsUsedInRegularObj)
+ if (!File || File->kind() == InputFile::ObjKind)
S->IsUsedInRegularObj = true;
- 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));
+ if (!WasInserted && S->Type != Symbol::UnknownType &&
+ ((Type == STT_TLS) != S->isTls())) {
+ error("TLS attribute mismatch: " + toString(*S) + "\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, 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, uint8_t Binding,
+ uint8_t StOther, uint8_t Type,
+ bool CanOmitFromDynSym, InputFile *File) {
Symbol *S;
bool WasInserted;
uint8_t Visibility = getVisibility(StOther);
@@ -294,26 +297,24 @@ Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name, bool IsLocal,
insert(Name, Type, Visibility, CanOmitFromDynSym, File);
// An undefined symbol with non default visibility must be satisfied
// in the same DSO.
- if (WasInserted ||
- (isa<SharedSymbol>(S->body()) && Visibility != STV_DEFAULT)) {
- S->Binding = Binding;
- replaceBody<Undefined>(S, Name, IsLocal, StOther, Type, File);
+ if (WasInserted || (isa<SharedSymbol>(S) && Visibility != STV_DEFAULT)) {
+ replaceSymbol<Undefined>(S, File, Name, Binding, StOther, Type);
return S;
}
+ if (S->isShared() || S->isLazy() || (S->isUndefined() && Binding != STB_WEAK))
+ S->Binding = Binding;
if (Binding != STB_WEAK) {
- SymbolBody *B = S->body();
- if (B->isShared() || B->isLazy() || B->isUndefined())
- S->Binding = Binding;
- if (auto *SS = dyn_cast<SharedSymbol>(B))
- cast<SharedFile<ELFT>>(SS->File)->IsUsed = true;
+ if (auto *SS = dyn_cast<SharedSymbol>(S))
+ if (!Config->GcSections)
+ SS->getFile<ELFT>()->IsNeeded = true;
}
- if (auto *L = dyn_cast<Lazy>(S->body())) {
- // An undefined weak will not fetch archive members, but we have to remember
- // its type. See also comment in addLazyArchive.
- if (S->isWeak())
+ if (auto *L = dyn_cast<Lazy>(S)) {
+ // An undefined weak will not fetch archive members. See comment on Lazy in
+ // Symbols.h for the details.
+ if (Binding == STB_WEAK)
L->Type = Type;
else if (InputFile *F = L->fetch())
- addFile(F);
+ addFile<ELFT>(F);
}
return S;
}
@@ -325,11 +326,11 @@ Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name, bool IsLocal,
// .symver foo,foo@@@VER
// we can delete this hack.
static int compareVersion(Symbol *S, StringRef Name) {
- if (Name.find("@@") != StringRef::npos &&
- S->body()->getName().find("@@") == StringRef::npos)
+ bool A = Name.contains("@@");
+ bool B = S->getName().contains("@@");
+ if (A && !B)
return 1;
- if (Name.find("@@") == StringRef::npos &&
- S->body()->getName().find("@@") != StringRef::npos)
+ if (!A && B)
return -1;
return 0;
}
@@ -341,13 +342,10 @@ static int compareDefined(Symbol *S, bool WasInserted, uint8_t Binding,
StringRef Name) {
if (WasInserted)
return 1;
- SymbolBody *Body = S->body();
- if (!Body->isInCurrentDSO())
+ if (!S->isDefined())
return 1;
-
if (int R = compareVersion(S, Name))
return R;
-
if (Binding == STB_WEAK)
return -1;
if (S->isWeak())
@@ -358,22 +356,18 @@ 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,
+ bool IsAbsolute, uint64_t Value,
StringRef Name) {
- if (int Cmp = compareDefined(S, WasInserted, Binding, Name)) {
- if (Cmp > 0)
- S->Binding = Binding;
+ if (int Cmp = compareDefined(S, WasInserted, Binding, Name))
return Cmp;
- }
- SymbolBody *B = S->body();
- if (isa<DefinedCommon>(B)) {
- // Non-common symbols take precedence over common symbols.
- if (Config->WarnCommon)
- warn("common " + S->body()->getName() + " is overridden");
- return 1;
- } else if (auto *R = dyn_cast<DefinedRegular>(B)) {
+ if (auto *R = dyn_cast<Defined>(S)) {
+ if (R->Section && isa<BssSection>(R->Section)) {
+ // Non-common symbols take precedence over common symbols.
+ if (Config->WarnCommon)
+ warn("common " + S->getName() + " is overridden");
+ return 1;
+ }
if (R->Section == nullptr && Binding == STB_GLOBAL && IsAbsolute &&
R->Value == Value)
return -1;
@@ -381,34 +375,39 @@ 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, N);
if (Cmp > 0) {
- S->Binding = Binding;
- replaceBody<DefinedCommon>(S, N, Size, Alignment, StOther, Type, File);
+ auto *Bss = make<BssSection>("COMMON", Size, Alignment);
+ Bss->File = File;
+ Bss->Live = !Config->GcSections;
+ InputSections.push_back(Bss);
+
+ replaceSymbol<Defined>(S, File, N, Binding, StOther, Type, 0, Size, Bss);
} else if (Cmp == 0) {
- auto *C = dyn_cast<DefinedCommon>(S->body());
- if (!C) {
+ auto *D = cast<Defined>(S);
+ auto *Bss = dyn_cast_or_null<BssSection>(D->Section);
+ if (!Bss) {
// Non-common symbols take precedence over common symbols.
if (Config->WarnCommon)
- warn("common " + S->body()->getName() + " is overridden");
+ warn("common " + S->getName() + " is overridden");
return S;
}
if (Config->WarnCommon)
- warn("multiple common of " + S->body()->getName());
+ warn("multiple common of " + D->getName());
- Alignment = C->Alignment = std::max(C->Alignment, Alignment);
- if (Size > C->Size)
- replaceBody<DefinedCommon>(S, N, Size, Alignment, StOther, Type, File);
+ Bss->Alignment = std::max(Bss->Alignment, Alignment);
+ if (Size > Bss->Size) {
+ D->File = Bss->File = File;
+ D->Size = Bss->Size = Size;
+ }
}
return S;
}
@@ -420,17 +419,17 @@ static void warnOrError(const Twine &Msg) {
error(Msg);
}
-static void reportDuplicate(SymbolBody *Sym, InputFile *NewFile) {
+static void reportDuplicate(Symbol *Sym, InputFile *NewFile) {
warnOrError("duplicate symbol: " + toString(*Sym) + "\n>>> defined in " +
toString(Sym->File) + "\n>>> defined in " + toString(NewFile));
}
template <class ELFT>
-static void reportDuplicate(SymbolBody *Sym, InputSectionBase *ErrSec,
+static void reportDuplicate(Symbol *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);
+ Defined *D = cast<Defined>(Sym);
+ if (!D->Section || !ErrSec) {
+ reportDuplicate(Sym, ErrSec ? ErrSec->File : nullptr);
return;
}
@@ -442,10 +441,10 @@ static void reportDuplicate(SymbolBody *Sym, InputSectionBase *ErrSec,
// >>> defined at baz.c:563
// >>> baz.o in archive libbaz.a
auto *Sec1 = cast<InputSectionBase>(D->Section);
- std::string Src1 = Sec1->getSrcMsg<ELFT>(D->Value);
- std::string Obj1 = Sec1->getObjMsg<ELFT>(D->Value);
- std::string Src2 = ErrSec->getSrcMsg<ELFT>(ErrOffset);
- std::string Obj2 = ErrSec->getObjMsg<ELFT>(ErrOffset);
+ std::string Src1 = Sec1->getSrcMsg<ELFT>(*Sym, D->Value);
+ std::string Obj1 = Sec1->getObjMsg(D->Value);
+ std::string Src2 = ErrSec->getSrcMsg<ELFT>(*Sym, ErrOffset);
+ std::string Obj2 = ErrSec->getObjMsg(ErrOffset);
std::string Msg = "duplicate symbol: " + toString(*Sym) + "\n>>> defined at ";
if (!Src1.empty())
@@ -458,29 +457,28 @@ 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, Name);
+ 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);
+ replaceSymbol<Defined>(S, File, Name, Binding, StOther, Type, Value, Size,
+ Section);
else if (Cmp == 0)
- reportDuplicate<ELFT>(S->body(),
- dyn_cast_or_null<InputSectionBase>(Section), Value);
+ reportDuplicate<ELFT>(S, dyn_cast_or_null<InputSectionBase>(Section),
+ Value);
return S;
}
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, uint32_t Alignment,
+ uint32_t VerdefIndex) {
// 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.
@@ -492,110 +490,102 @@ void SymbolTable<ELFT>::addShared(SharedFile<ELFT> *File, StringRef Name,
if (Sym.getVisibility() == STV_DEFAULT)
S->ExportDynamic = true;
- 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)) {
- replaceBody<SharedSymbol>(S, File, Name, Sym.st_other, Sym.getType(), &Sym,
- Verdef);
- if (!S->isWeak())
- File->IsUsed = true;
+ if (WasInserted || ((S->isUndefined() || S->isLazy()) &&
+ S->getVisibility() == STV_DEFAULT)) {
+ uint8_t Binding = S->Binding;
+ replaceSymbol<SharedSymbol>(S, File, Name, Sym.getBinding(), Sym.st_other,
+ Sym.getType(), Sym.st_value, Sym.st_size,
+ Alignment, VerdefIndex);
+ if (!WasInserted) {
+ S->Binding = Binding;
+ if (!S->isWeak() && !Config->GcSections)
+ File->IsNeeded = true;
+ }
}
}
-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, Name);
+ 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);
+ replaceSymbol<Defined>(S, F, Name, Binding, StOther, Type, 0, 0, nullptr);
else if (Cmp == 0)
- reportDuplicate(S->body(), F);
+ reportDuplicate(S, F);
return S;
}
-template <class ELFT> SymbolBody *SymbolTable<ELFT>::find(StringRef Name) {
- auto It = Symtab.find(CachedHashStringRef(Name));
- if (It == Symtab.end())
+Symbol *SymbolTable::find(StringRef Name) {
+ auto It = SymMap.find(CachedHashStringRef(Name));
+ if (It == SymMap.end())
return nullptr;
- SymIndex V = It->second;
- if (V.Idx == -1)
+ if (It->second == -1)
return nullptr;
- 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;
+ return SymVector[It->second];
}
template <class ELFT>
-Symbol *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);
+ replaceSymbol<LazyArchive>(S, F, Sym, Symbol::UnknownType);
return S;
}
- if (!S->body()->isUndefined())
+ if (!S->isUndefined())
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
- // if a strong undefined symbol shows up afterwards in the link. If a strong
- // undefined symbol never shows up, this lazy symbol will get to the end of
- // the link and must be treated as the weak undefined one. We already marked
- // 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.
+ // An undefined weak will not fetch archive members. See comment on Lazy in
+ // Symbols.h for the details.
if (S->isWeak()) {
- replaceBody<LazyArchive>(S, *F, Sym, S->body()->Type);
+ replaceSymbol<LazyArchive>(S, F, Sym, S->Type);
+ S->Binding = STB_WEAK;
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);
+ replaceSymbol<LazyObject>(S, &Obj, Name, Symbol::UnknownType);
return;
}
- if (!S->body()->isUndefined())
+ if (!S->isUndefined())
return;
// See comment for addLazyArchive above.
if (S->isWeak())
- replaceBody<LazyObject>(S, Name, Obj, S->body()->Type);
+ replaceSymbol<LazyObject>(S, &Obj, Name, S->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 (Symbol *B = find(Name)) {
+ // Mark the symbol not to be eliminated by LTO
+ // even if it is a bitcode symbol.
+ B->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
@@ -605,19 +595,19 @@ 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()) {
- SymbolBody *Sym = find(U);
+template <class ELFT> void SymbolTable::scanShlibUndefined() {
+ for (InputFile *F : SharedFiles) {
+ for (StringRef U : cast<SharedFile<ELFT>>(F)->getUndefinedSymbols()) {
+ Symbol *Sym = find(U);
if (!Sym || !Sym->isDefined())
continue;
- Sym->symbol()->ExportDynamic = true;
+ Sym->ExportDynamic = true;
// If -dynamic-list is given, the default version is set to
// VER_NDX_LOCAL, which prevents a symbol to be exported via .dynsym.
// Set to VER_NDX_GLOBAL so the symbol will be handled as if it were
// specified by -dynamic-list.
- Sym->symbol()->VersionId = VER_NDX_GLOBAL;
+ Sym->VersionId = VER_NDX_GLOBAL;
}
}
}
@@ -635,37 +625,32 @@ 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<Symbol *>> &SymbolTable::getDemangledSyms() {
if (!DemangledSyms) {
DemangledSyms.emplace();
for (Symbol *Sym : SymVector) {
- SymbolBody *B = Sym->body();
- if (B->isUndefined())
+ if (!Sym->isDefined())
continue;
- if (Optional<std::string> S = demangle(B->getName()))
- (*DemangledSyms)[*S].push_back(B);
+ if (Optional<std::string> S = demangleItanium(Sym->getName()))
+ (*DemangledSyms)[*S].push_back(Sym);
else
- (*DemangledSyms)[B->getName()].push_back(B);
+ (*DemangledSyms)[Sym->getName()].push_back(Sym);
}
}
return *DemangledSyms;
}
-template <class ELFT>
-std::vector<SymbolBody *> SymbolTable<ELFT>::findByVersion(SymbolVersion Ver) {
+std::vector<Symbol *> SymbolTable::findByVersion(SymbolVersion Ver) {
if (Ver.IsExternCpp)
return getDemangledSyms().lookup(Ver.Name);
- if (SymbolBody *B = find(Ver.Name))
- if (!B->isUndefined())
+ if (Symbol *B = find(Ver.Name))
+ if (B->isDefined())
return {B};
return {};
}
-template <class ELFT>
-std::vector<SymbolBody *>
-SymbolTable<ELFT>::findAllByVersion(SymbolVersion Ver) {
- std::vector<SymbolBody *> Res;
+std::vector<Symbol *> SymbolTable::findAllByVersion(SymbolVersion Ver) {
+ std::vector<Symbol *> Res;
StringMatcher M(Ver.Name);
if (Ver.IsExternCpp) {
@@ -675,18 +660,16 @@ SymbolTable<ELFT>::findAllByVersion(SymbolVersion Ver) {
return Res;
}
- for (Symbol *Sym : SymVector) {
- SymbolBody *B = Sym->body();
- if (!B->isUndefined() && M.match(B->getName()))
- Res.push_back(B);
- }
+ for (Symbol *Sym : SymVector)
+ if (Sym->isDefined() && M.match(Sym->getName()))
+ Res.push_back(Sym);
return Res;
}
// 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)
@@ -697,17 +680,33 @@ 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<Symbol *> Syms;
+ if (Ver.HasWildcard)
+ Syms = findAllByVersion(Ver);
+ else
+ Syms = findByVersion(Ver);
+
+ for (Symbol *B : Syms) {
+ if (!Config->Shared)
+ B->ExportDynamic = true;
+ else if (B->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;
// Get a list of symbols which we need to assign the version to.
- std::vector<SymbolBody *> Syms = findByVersion(Ver);
+ std::vector<Symbol *> Syms = findByVersion(Ver);
if (Syms.empty()) {
if (Config->NoUndefinedVersion)
error("version script assignment of '" + VersionName + "' to symbol '" +
@@ -716,14 +715,13 @@ void SymbolTable<ELFT>::assignExactVersion(SymbolVersion Ver,
}
// Assign the version.
- for (SymbolBody *B : Syms) {
+ for (Symbol *Sym : Syms) {
// Skip symbols containing version info because symbol versions
// specified by symbol names take precedence over version scripts.
// See parseSymbolVersion().
- if (B->getName().find('@') != StringRef::npos)
+ if (Sym->getName().contains('@'))
continue;
- Symbol *Sym = B->symbol();
if (Sym->InVersionScript)
warn("duplicate symbol '" + Ver.Name + "' in version script");
Sym->VersionId = VersionId;
@@ -731,25 +729,24 @@ void SymbolTable<ELFT>::assignExactVersion(SymbolVersion Ver,
}
}
-template <class ELFT>
-void SymbolTable<ELFT>::assignWildcardVersion(SymbolVersion Ver,
- uint16_t VersionId) {
+void SymbolTable::assignWildcardVersion(SymbolVersion Ver, uint16_t VersionId) {
if (!Ver.HasWildcard)
return;
// 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 : findAllByVersion(Ver))
- if (B->symbol()->VersionId == Config->DefaultSymbolVersion)
- B->symbol()->VersionId = VersionId;
+ for (Symbol *B : findAllByVersion(Ver))
+ if (B->VersionId == Config->DefaultSymbolVersion)
+ B->VersionId = VersionId;
}
// This function processes version scripts by updating VersionId
// member of symbols.
-template <class ELFT> void SymbolTable<ELFT>::scanVersionScript() {
+void SymbolTable::scanVersionScript() {
// Handle edge cases first.
handleAnonymousVersion();
+ 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
@@ -773,10 +770,92 @@ template <class ELFT> void SymbolTable<ELFT>::scanVersionScript() {
// 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 class elf::SymbolTable<ELF32LE>;
-template class elf::SymbolTable<ELF32BE>;
-template class elf::SymbolTable<ELF64LE>;
-template class elf::SymbolTable<ELF64BE>;
+ Sym->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, uint8_t, uint8_t,
+ uint8_t, bool, InputFile *);
+template Symbol *SymbolTable::addUndefined<ELF32BE>(StringRef, uint8_t, uint8_t,
+ uint8_t, bool, InputFile *);
+template Symbol *SymbolTable::addUndefined<ELF64LE>(StringRef, uint8_t, uint8_t,
+ uint8_t, bool, InputFile *);
+template Symbol *SymbolTable::addUndefined<ELF64BE>(StringRef, uint8_t, uint8_t,
+ uint8_t, bool, InputFile *);
+
+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 Defined *SymbolTable::addAbsolute<ELF32LE>(StringRef, uint8_t,
+ uint8_t);
+template Defined *SymbolTable::addAbsolute<ELF32BE>(StringRef, uint8_t,
+ uint8_t);
+template Defined *SymbolTable::addAbsolute<ELF64LE>(StringRef, uint8_t,
+ uint8_t);
+template Defined *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 &,
+ uint32_t Alignment, uint32_t);
+template void SymbolTable::addShared<ELF32BE>(StringRef, SharedFile<ELF32BE> *,
+ const typename ELF32BE::Sym &,
+ uint32_t Alignment, uint32_t);
+template void SymbolTable::addShared<ELF64LE>(StringRef, SharedFile<ELF64LE> *,
+ const typename ELF64LE::Sym &,
+ uint32_t Alignment, uint32_t);
+template void SymbolTable::addShared<ELF64BE>(StringRef, SharedFile<ELF64BE> *,
+ const typename ELF64BE::Sym &,
+ uint32_t Alignment, uint32_t);
+
+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 4ba101fa5d50..738311c089db 100644
--- a/ELF/SymbolTable.h
+++ b/ELF/SymbolTable.h
@@ -18,8 +18,8 @@
namespace lld {
namespace elf {
-
-struct Symbol;
+class Defined;
+class SectionBase;
// SymbolTable is a bucket of all known symbols, including defined,
// undefined, or lazy symbols (the last one is symbols in archive
@@ -33,45 +33,44 @@ 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);
- void applySymbolRenames();
+ template <class ELFT> void addFile(InputFile *File);
+ template <class ELFT> void addCombinedLTOObject();
+ template <class ELFT> void addSymbolWrap(StringRef Name);
+ void applySymbolWrap();
ArrayRef<Symbol *> getSymbols() const { return SymVector; }
- ArrayRef<ObjectFile<ELFT> *> getObjectFiles() const { return ObjectFiles; }
- ArrayRef<BinaryFile *> getBinaryFiles() const { return BinaryFiles; }
- ArrayRef<SharedFile<ELFT> *> getSharedFiles() const { return SharedFiles; }
- DefinedRegular *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);
- Symbol *addUndefined(StringRef Name, bool IsLocal, uint8_t Binding,
- uint8_t StOther, uint8_t Type, bool CanOmitFromDynSym,
- InputFile *File);
+ template <class ELFT>
+ Defined *addAbsolute(StringRef Name,
+ uint8_t Visibility = llvm::ELF::STV_HIDDEN,
+ uint8_t Binding = llvm::ELF::STB_GLOBAL);
+ template <class ELFT> Symbol *addUndefined(StringRef Name);
+ template <class ELFT>
+ Symbol *addUndefined(StringRef Name, 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,
- const typename ELFT::Verdef *Verdef);
+ template <class ELFT>
+ void addShared(StringRef Name, SharedFile<ELFT> *F,
+ const typename ELFT::Sym &Sym, uint32_t Alignment,
+ uint32_t VerdefIndex);
+
+ 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 *addLazyArchive(ArchiveFile *F, const llvm::object::Archive::Symbol S);
- void addLazyObject(StringRef Name, LazyObjectFile &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,31 +79,27 @@ 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);
+ Symbol *find(StringRef Name);
void trace(StringRef Name);
+ void handleDynamicList();
+
private:
- std::vector<SymbolBody *> findByVersion(SymbolVersion Ver);
- std::vector<SymbolBody *> findAllByVersion(SymbolVersion Ver);
+ std::vector<Symbol *> findByVersion(SymbolVersion Ver);
+ std::vector<Symbol *> findAllByVersion(SymbolVersion Ver);
+ void defsym(Symbol *Dst, Symbol *Src);
- llvm::StringMap<std::vector<SymbolBody *>> &getDemangledSyms();
+ llvm::StringMap<std::vector<Symbol *>> &getDemangledSyms();
void handleAnonymousVersion();
void assignExactVersion(SymbolVersion Ver, uint16_t VersionId,
StringRef VersionName);
void assignWildcardVersion(SymbolVersion Ver, uint16_t VersionId);
- struct SymIndex {
- SymIndex(int Idx, bool Traced) : Idx(Idx), Traced(Traced) {}
- int Idx : 31;
- unsigned Traced : 1;
- };
-
// The order the global symbols are in is not defined. We can use an arbitrary
// order, but it has to be reproducible. That is true even when cross linking.
// The default hashing of StringRef produces different results on 32 and 64
@@ -112,7 +107,7 @@ private:
// but a bit inefficient.
// FIXME: Experiment with passing in a custom hashing or sorting the symbols
// once symbol resolution is finished.
- llvm::DenseMap<llvm::CachedHashStringRef, SymIndex> Symtab;
+ llvm::DenseMap<llvm::CachedHashStringRef, int> SymMap;
std::vector<Symbol *> SymVector;
// Comdat groups define "link once" sections. If two comdat groups have the
@@ -120,11 +115,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;
@@ -132,15 +122,22 @@ private:
// This mapping is 1:N because two symbols with different versions
// can have the same name. We use this map to handle "extern C++ {}"
// directive in version scripts.
- llvm::Optional<llvm::StringMap<std::vector<SymbolBody *>>> DemangledSyms;
+ llvm::Optional<llvm::StringMap<std::vector<Symbol *>>> DemangledSyms;
+
+ struct WrappedSymbol {
+ Symbol *Sym;
+ Symbol *Real;
+ Symbol *Wrap;
+ };
+
+ // For -wrap.
+ std::vector<WrappedSymbol> WrappedSymbols;
// 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 c69007e781a6..ab42fcd51a81 100644
--- a/ELF/Symbols.cpp
+++ b/ELF/Symbols.cpp
@@ -8,15 +8,15 @@
//===----------------------------------------------------------------------===//
#include "Symbols.h"
-#include "Error.h"
#include "InputFiles.h"
#include "InputSection.h"
#include "OutputSections.h"
-#include "Strings.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "Writer.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Strings.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Path.h"
#include <cstring>
@@ -28,25 +28,23 @@ using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
-DefinedRegular *ElfSym::Bss;
-DefinedRegular *ElfSym::Etext1;
-DefinedRegular *ElfSym::Etext2;
-DefinedRegular *ElfSym::Edata1;
-DefinedRegular *ElfSym::Edata2;
-DefinedRegular *ElfSym::End1;
-DefinedRegular *ElfSym::End2;
-DefinedRegular *ElfSym::GlobalOffsetTable;
-DefinedRegular *ElfSym::MipsGp;
-DefinedRegular *ElfSym::MipsGpDisp;
-DefinedRegular *ElfSym::MipsLocalGp;
-
-static uint64_t getSymVA(const SymbolBody &Body, int64_t &Addend) {
- switch (Body.kind()) {
- case SymbolBody::DefinedRegularKind: {
- auto &D = cast<DefinedRegular>(Body);
+Defined *ElfSym::Bss;
+Defined *ElfSym::Etext1;
+Defined *ElfSym::Etext2;
+Defined *ElfSym::Edata1;
+Defined *ElfSym::Edata2;
+Defined *ElfSym::End1;
+Defined *ElfSym::End2;
+Defined *ElfSym::GlobalOffsetTable;
+Defined *ElfSym::MipsGp;
+Defined *ElfSym::MipsGpDisp;
+Defined *ElfSym::MipsLocalGp;
+
+static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
+ switch (Sym.kind()) {
+ case Symbol::DefinedKind: {
+ auto &D = cast<Defined>(Sym);
SectionBase *IS = D.Section;
- if (auto *ISB = dyn_cast_or_null<InputSectionBase>(IS))
- IS = ISB->Repl;
// According to the ELF spec reference to a local symbol from outside
// the group are not allowed. Unfortunately .eh_frame breaks that rule
@@ -59,6 +57,7 @@ static uint64_t getSymVA(const SymbolBody &Body, int64_t &Addend) {
if (!IS)
return D.Value;
+ IS = IS->Repl;
uint64_t Offset = D.Value;
// An object in an SHF_MERGE section might be referenced via a
@@ -99,139 +98,85 @@ static uint64_t getSymVA(const SymbolBody &Body, int64_t &Addend) {
}
return VA;
}
- case SymbolBody::DefinedCommonKind:
- if (!Config->DefineCommon)
- return 0;
- return InX::Common->getParent()->Addr + InX::Common->OutSecOff +
- cast<DefinedCommon>(Body).Offset;
- case SymbolBody::SharedKind: {
- auto &SS = cast<SharedSymbol>(Body);
- if (SS.NeedsCopy)
- return SS.CopyRelSec->getParent()->Addr + SS.CopyRelSec->OutSecOff +
- SS.CopyRelSecOff;
+ case Symbol::SharedKind: {
+ auto &SS = cast<SharedSymbol>(Sym);
+ if (SS.CopyRelSec)
+ return SS.CopyRelSec->getParent()->Addr + SS.CopyRelSec->OutSecOff;
if (SS.NeedsPltAddr)
- return Body.getPltVA();
+ return Sym.getPltVA();
return 0;
}
- case SymbolBody::UndefinedKind:
+ case Symbol::UndefinedKind:
return 0;
- case SymbolBody::LazyArchiveKind:
- case SymbolBody::LazyObjectKind:
- assert(Body.symbol()->IsUsedInRegularObj && "lazy symbol reached writer");
+ case Symbol::LazyArchiveKind:
+ case Symbol::LazyObjectKind:
+ assert(Sym.IsUsedInRegularObj && "lazy symbol reached writer");
return 0;
}
llvm_unreachable("invalid symbol kind");
}
-SymbolBody::SymbolBody(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther,
- uint8_t Type)
- : SymbolKind(K), NeedsCopy(false), NeedsPltAddr(false), IsLocal(IsLocal),
- IsInGlobalMipsGot(false), Is32BitMipsGot(false), IsInIplt(false),
- IsInIgot(false), Type(Type), StOther(StOther), Name(Name) {}
-
-// Returns true if a symbol can be replaced at load-time by a symbol
-// with the same name defined in other ELF executable or DSO.
-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;
-
- // Only default visibility symbols can be preempted.
- if (symbol()->Visibility != STV_DEFAULT)
- return false;
-
- // -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::copy(SymbolBody *Other) {
- memcpy(symbol()->Body.buffer, Other->symbol()->Body.buffer,
- sizeof(Symbol::Body));
+// Returns true if this is a weak undefined symbol.
+bool Symbol::isUndefWeak() const {
+ // See comment on Lazy in Symbols.h for the details.
+ return isWeak() && (isUndefined() || isLazy());
}
-uint64_t SymbolBody::getVA(int64_t Addend) const {
+uint64_t Symbol::getVA(int64_t Addend) const {
uint64_t OutVA = getSymVA(*this, Addend);
return OutVA + Addend;
}
-uint64_t SymbolBody::getGotVA() const {
- return InX::Got->getVA() + getGotOffset();
-}
+uint64_t Symbol::getGotVA() const { return InX::Got->getVA() + getGotOffset(); }
-uint64_t SymbolBody::getGotOffset() const {
+uint64_t Symbol::getGotOffset() const {
return GotIndex * Target->GotEntrySize;
}
-uint64_t SymbolBody::getGotPltVA() const {
+uint64_t Symbol::getGotPltVA() const {
if (this->IsInIgot)
return InX::IgotPlt->getVA() + getGotPltOffset();
return InX::GotPlt->getVA() + getGotPltOffset();
}
-uint64_t SymbolBody::getGotPltOffset() const {
+uint64_t Symbol::getGotPltOffset() const {
return GotPltIndex * Target->GotPltEntrySize;
}
-uint64_t SymbolBody::getPltVA() const {
+uint64_t Symbol::getPltVA() const {
if (this->IsInIplt)
return InX::Iplt->getVA() + PltIndex * Target->PltEntrySize;
return InX::Plt->getVA() + Target->PltHeaderSize +
PltIndex * Target->PltEntrySize;
}
-template <class ELFT> typename ELFT::uint SymbolBody::getSize() const {
- if (const auto *C = dyn_cast<DefinedCommon>(this))
- return C->Size;
- if (const auto *DR = dyn_cast<DefinedRegular>(this))
+uint64_t Symbol::getSize() const {
+ if (const auto *DR = dyn_cast<Defined>(this))
return DR->Size;
if (const auto *S = dyn_cast<SharedSymbol>(this))
- return S->getSize<ELFT>();
+ return S->Size;
return 0;
}
-OutputSection *SymbolBody::getOutputSection() const {
- if (auto *S = dyn_cast<DefinedRegular>(this)) {
- if (S->Section)
- return S->Section->getOutputSection();
+OutputSection *Symbol::getOutputSection() const {
+ if (auto *S = dyn_cast<Defined>(this)) {
+ if (auto *Sec = S->Section)
+ return Sec->Repl->getOutputSection();
return nullptr;
}
if (auto *S = dyn_cast<SharedSymbol>(this)) {
- if (S->NeedsCopy)
+ if (S->CopyRelSec)
return S->CopyRelSec->getParent();
return nullptr;
}
- if (isa<DefinedCommon>(this)) {
- if (Config->DefineCommon)
- return InX::Common->getParent();
- return nullptr;
- }
-
return nullptr;
}
// If a symbol name contains '@', the characters after that is
// a symbol version name. This function parses that.
-void SymbolBody::parseSymbolVersion() {
+void Symbol::parseSymbolVersion() {
StringRef S = getName();
size_t Pos = S.find('@');
if (Pos == 0 || Pos == StringRef::npos)
@@ -244,7 +189,7 @@ void SymbolBody::parseSymbolVersion() {
Name = {S.data(), Pos};
// If this is not in this DSO, it is not a definition.
- if (!isInCurrentDSO())
+ if (!isDefined())
return;
// '@@' in a symbol name means the default version.
@@ -258,9 +203,9 @@ void SymbolBody::parseSymbolVersion() {
continue;
if (IsDefault)
- symbol()->VersionId = Ver.Id;
+ VersionId = Ver.Id;
else
- symbol()->VersionId = Ver.Id | VERSYM_HIDDEN;
+ VersionId = Ver.Id | VERSYM_HIDDEN;
return;
}
@@ -273,81 +218,34 @@ void SymbolBody::parseSymbolVersion() {
Verstr);
}
-Defined::Defined(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther,
- uint8_t Type)
- : SymbolBody(K, Name, IsLocal, StOther, Type) {}
-
-template <class ELFT> bool DefinedRegular::isMipsPIC() const {
- typedef typename ELFT::Ehdr Elf_Ehdr;
- if (!Section || !isFunc())
- return false;
-
- auto *Sec = cast<InputSectionBase>(Section);
- const Elf_Ehdr *Hdr = Sec->template getFile<ELFT>()->getObj().getHeader();
- return (this->StOther & STO_MIPS_MIPS16) == STO_MIPS_PIC ||
- (Hdr->e_flags & EF_MIPS_PIC);
-}
-
-Undefined::Undefined(StringRefZ Name, bool IsLocal, uint8_t StOther,
- uint8_t Type, InputFile *File)
- : SymbolBody(SymbolBody::UndefinedKind, Name, IsLocal, StOther, Type) {
- this->File = File;
-}
-
-DefinedCommon::DefinedCommon(StringRef Name, uint64_t Size, uint32_t Alignment,
- uint8_t StOther, uint8_t Type, InputFile *File)
- : Defined(SymbolBody::DefinedCommonKind, Name, /*IsLocal=*/false, StOther,
- Type),
- Alignment(Alignment), Size(Size) {
- this->File = File;
-}
-
-// 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);
- uint32_t SecAlign = File->getSection(getSym<ELFT>())->sh_addralign;
- uint64_t SymValue = getSym<ELFT>().st_value;
- uint32_t SymAlign = uint32_t(1) << countTrailingZeros(SymValue);
- return std::min(SecAlign, SymAlign);
-}
-
InputFile *Lazy::fetch() {
if (auto *S = dyn_cast<LazyArchive>(this))
return S->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;
-}
-
-LazyObject::LazyObject(StringRef Name, LazyObjectFile &File, uint8_t Type)
- : Lazy(LazyObjectKind, Name, Type) {
- this->File = &File;
-}
+ArchiveFile *LazyArchive::getFile() { return cast<ArchiveFile>(File); }
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);
}
-InputFile *LazyObject::fetch() { return file()->fetch(); }
+LazyObjFile *LazyObject::getFile() { return cast<LazyObjFile>(File); }
+
+InputFile *LazyObject::fetch() { return getFile()->fetch(); }
uint8_t Symbol::computeBinding() const {
if (Config->Relocatable)
return Binding;
if (Visibility != STV_DEFAULT && Visibility != STV_PROTECTED)
return STB_LOCAL;
- if (VersionId == VER_NDX_LOCAL && body()->isInCurrentDSO())
+ if (VersionId == VER_NDX_LOCAL && isDefined())
return STB_LOCAL;
if (Config->NoGnuUnique && Binding == STB_GNU_UNIQUE)
return STB_GLOBAL;
@@ -355,45 +253,36 @@ 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 (!isDefined())
+ return true;
+ return ExportDynamic;
}
// Print out a log message for --trace-symbol.
void elf::printTraceSymbol(Symbol *Sym) {
- SymbolBody *B = Sym->body();
std::string S;
- if (B->isUndefined())
+ if (Sym->isUndefined())
S = ": reference to ";
- else if (B->isCommon())
+ else if (Sym->isLazy())
+ S = ": lazy definition of ";
+ else if (Sym->isShared())
+ S = ": shared definition of ";
+ else if (dyn_cast_or_null<BssSection>(cast<Defined>(Sym)->Section))
S = ": common definition of ";
else
S = ": definition of ";
- message(toString(B->File) + S + B->getName());
+ message(toString(Sym->File) + S + Sym->getName());
}
// Returns a symbol for an error message.
-std::string lld::toString(const SymbolBody &B) {
+std::string lld::toString(const Symbol &B) {
if (Config->Demangle)
- if (Optional<std::string> S = demangle(B.getName()))
+ if (Optional<std::string> S = demangleItanium(B.getName()))
return *S;
return B.getName();
}
-
-template uint32_t SymbolBody::template getSize<ELF32LE>() const;
-template uint32_t SymbolBody::template getSize<ELF32BE>() const;
-template uint64_t SymbolBody::template getSize<ELF64LE>() const;
-template uint64_t SymbolBody::template getSize<ELF64BE>() const;
-
-template bool DefinedRegular::template isMipsPIC<ELF32LE>() const;
-template bool DefinedRegular::template isMipsPIC<ELF32BE>() const;
-template bool DefinedRegular::template isMipsPIC<ELF64LE>() const;
-template bool DefinedRegular::template isMipsPIC<ELF64BE>() const;
-
-template uint32_t SharedSymbol::template getAlignment<ELF32LE>() const;
-template uint32_t SharedSymbol::template getAlignment<ELF32BE>() const;
-template uint32_t SharedSymbol::template getAlignment<ELF64LE>() const;
-template uint32_t SharedSymbol::template getAlignment<ELF64BE>() const;
diff --git a/ELF/Symbols.h b/ELF/Symbols.h
index a1b3a6fba911..f4bb245f955d 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,53 +27,86 @@ 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;
-struct Symbol;
-
// The base class for real symbol classes.
-class SymbolBody {
+class Symbol {
public:
enum Kind {
- DefinedFirst,
- DefinedRegularKind = DefinedFirst,
+ DefinedKind,
SharedKind,
- DefinedCommonKind,
- DefinedLast = DefinedCommonKind,
UndefinedKind,
LazyArchiveKind,
LazyObjectKind,
};
- SymbolBody(Kind K) : SymbolKind(K) {}
+ Kind kind() const { return static_cast<Kind>(SymbolKind); }
- Symbol *symbol();
- const Symbol *symbol() const {
- return const_cast<SymbolBody *>(this)->symbol();
- }
+ // Symbol binding. This is not overwritten by replaceSymbol to track
+ // changes during resolution. In particular:
+ // - An undefined weak is still weak when it resolves to a shared library.
+ // - An undefined weak will not fetch archive members, but we have to
+ // remember it is weak.
+ uint8_t Binding;
- Kind kind() const { return static_cast<Kind>(SymbolKind); }
+ // Version definition index.
+ uint16_t VersionId;
+
+ // Symbol visibility. This is the computed minimum visibility of all
+ // observed non-DSO symbols.
+ unsigned Visibility : 2;
+
+ // True if the symbol was used for linking and thus need to be added to the
+ // output file's symbol table. This is true for all symbols except for
+ // unreferenced DSO symbols and bitcode symbols that are unreferenced except
+ // by other bitcode objects.
+ unsigned IsUsedInRegularObj : 1;
+
+ // If this flag is true and the symbol has protected or default visibility, it
+ // will appear in .dynsym. This flag is set by interposable DSO symbols in
+ // executables, by most symbols in DSOs and executables built with
+ // --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;
+
+ bool includeInDynsym() const;
+ uint8_t computeBinding() const;
+ bool isWeak() const { return Binding == llvm::ELF::STB_WEAK; }
bool isUndefined() const { return SymbolKind == UndefinedKind; }
- bool isDefined() const { return SymbolKind <= DefinedLast; }
- bool isCommon() const { return SymbolKind == DefinedCommonKind; }
+ bool isDefined() const { return SymbolKind == DefinedKind; }
+ bool isShared() const { return SymbolKind == SharedKind; }
+ bool isLocal() const { return Binding == llvm::ELF::STB_LOCAL; }
+
bool isLazy() const {
return SymbolKind == LazyArchiveKind || SymbolKind == LazyObjectKind;
}
- bool isShared() const { return SymbolKind == SharedKind; }
- bool isInCurrentDSO() const {
- return !isUndefined() && !isShared() && !isLazy();
- }
- bool isLocal() const { return IsLocal; }
- bool isPreemptible() const;
+
+ // True is this is an undefined weak symbol. This only works once
+ // all input files have been added.
+ bool isUndefWeak() const;
+
StringRef getName() const { return Name; }
uint8_t getVisibility() const { return StOther & 0x3; }
void parseSymbolVersion();
- void copy(SymbolBody *Other);
bool isInGot() const { return GotIndex != -1U; }
bool isInPlt() const { return PltIndex != -1U; }
@@ -85,12 +118,9 @@ public:
uint64_t getGotPltOffset() const;
uint64_t getGotPltVA() const;
uint64_t getPltVA() const;
- template <class ELFT> typename ELFT::uint getSize() const;
+ uint64_t 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;
@@ -98,23 +128,19 @@ public:
uint32_t GlobalDynIndex = -1;
protected:
- SymbolBody(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther,
- uint8_t Type);
+ Symbol(Kind K, InputFile *File, StringRefZ Name, uint8_t Binding,
+ uint8_t StOther, uint8_t Type)
+ : Binding(Binding), File(File), SymbolKind(K), NeedsPltAddr(false),
+ IsInGlobalMipsGot(false), Is32BitMipsGot(false), IsInIplt(false),
+ IsInIgot(false), IsPreemptible(false), Used(!Config->GcSections),
+ Type(Type), StOther(StOther), Name(Name) {}
const unsigned SymbolKind : 8;
public:
- // True if the linker has to generate a copy relocation.
- // For SharedSymbol only.
- unsigned NeedsCopy : 1;
-
// 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;
@@ -127,6 +153,11 @@ public:
// True if this symbol is in the Igot sub-section of the .got.plt or .got.
unsigned IsInIgot : 1;
+ unsigned IsPreemptible : 1;
+
+ // True if an undefined or shared symbol is used from a live section.
+ unsigned Used : 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
@@ -149,139 +180,111 @@ protected:
StringRefZ Name;
};
-// The base class for any defined symbols.
-class Defined : public SymbolBody {
-public:
- Defined(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther, uint8_t Type);
- static bool classof(const SymbolBody *S) { return S->isDefined(); }
-};
-
-class DefinedCommon : public Defined {
-public:
- DefinedCommon(StringRef N, uint64_t Size, uint32_t Alignment, uint8_t StOther,
- uint8_t Type, InputFile *File);
-
- 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;
-
- uint64_t Size;
-};
-
-// Regular defined symbols read from object file symbol tables.
-class DefinedRegular : public Defined {
+// Represents a symbol that is defined in the current output file.
+class Defined : public Symbol {
public:
- DefinedRegular(StringRefZ Name, bool IsLocal, uint8_t StOther, uint8_t Type,
- uint64_t Value, uint64_t Size, SectionBase *Section,
- InputFile *File)
- : Defined(SymbolBody::DefinedRegularKind, Name, IsLocal, StOther, Type),
- Value(Value), Size(Size), Section(Section) {
- this->File = File;
- }
-
- // Return true if the symbol is a PIC function.
- template <class ELFT> bool isMipsPIC() const;
+ Defined(InputFile *File, StringRefZ Name, uint8_t Binding, uint8_t StOther,
+ uint8_t Type, uint64_t Value, uint64_t Size, SectionBase *Section)
+ : Symbol(DefinedKind, File, Name, Binding, StOther, Type), Value(Value),
+ Size(Size), Section(Section) {}
- static bool classof(const SymbolBody *S) {
- return S->kind() == SymbolBody::DefinedRegularKind;
- }
+ static bool classof(const Symbol *S) { return S->isDefined(); }
uint64_t Value;
uint64_t Size;
SectionBase *Section;
};
-class Undefined : public SymbolBody {
+class Undefined : public Symbol {
public:
- Undefined(StringRefZ Name, bool IsLocal, uint8_t StOther, uint8_t Type,
- InputFile *F);
+ Undefined(InputFile *File, StringRefZ Name, uint8_t Binding, uint8_t StOther,
+ uint8_t Type)
+ : Symbol(UndefinedKind, File, Name, Binding, StOther, Type) {}
- static bool classof(const SymbolBody *S) {
- return S->kind() == UndefinedKind;
- }
+ static bool classof(const Symbol *S) { return S->kind() == UndefinedKind; }
};
-class SharedSymbol : public Defined {
+class SharedSymbol : public Symbol {
public:
- static bool classof(const SymbolBody *S) {
- return S->kind() == SymbolBody::SharedKind;
- }
-
- SharedSymbol(InputFile *File, 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())
+ static bool classof(const Symbol *S) { return S->kind() == SharedKind; }
+
+ SharedSymbol(InputFile *File, StringRef Name, uint8_t Binding,
+ uint8_t StOther, uint8_t Type, uint64_t Value, uint64_t Size,
+ uint32_t Alignment, uint32_t VerdefIndex)
+ : Symbol(SharedKind, File, Name, Binding, StOther, Type), Value(Value),
+ Size(Size), VerdefIndex(VerdefIndex), Alignment(Alignment) {
+ // 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;
- this->File = File;
}
- template <class ELFT> uint64_t getShndx() const {
- return getSym<ELFT>().st_shndx;
+ template <class ELFT> SharedFile<ELFT> *getFile() const {
+ return cast<SharedFile<ELFT>>(File);
}
- template <class ELFT> uint64_t getValue() const {
- return getSym<ELFT>().st_value;
- }
-
- template <class ELFT> uint64_t getSize() const {
- return getSym<ELFT>().st_size;
- }
-
- template <class ELFT> uint32_t getAlignment() const;
+ // If not null, there is a copy relocation to this section.
+ InputSection *CopyRelSec = nullptr;
- // This field is a pointer to the symbol's version definition.
- const void *Verdef;
+ uint64_t Value; // st_value
+ uint64_t Size; // st_size
- // CopyRelSec and CopyRelSecOff are significant only when NeedsCopy is true.
- InputSection *CopyRelSec;
- uint64_t CopyRelSecOff;
+ // This field is a index to the symbol's version definition.
+ uint32_t VerdefIndex;
-private:
- template <class ELFT> const typename ELFT::Sym &getSym() const {
- return *(const typename ELFT::Sym *)ElfSym;
- }
-
- const void *ElfSym;
+ uint32_t Alignment;
};
-// This class represents a symbol defined in an archive file. It is
-// created from an archive file header, and it knows how to load an
-// object file from an archive to replace itself with a defined
-// symbol. If the resolver finds both Undefined and Lazy for
-// the same name, it will ask the Lazy to load a file.
-class Lazy : public SymbolBody {
+// This represents a symbol that is not yet in the link, but we know where to
+// find it if needed. If the resolver finds both Undefined and Lazy for the same
+// name, it will ask the Lazy to load a file.
+//
+// A special complication is the handling of weak undefined symbols. They should
+// not load a file, but we have to remember we have seen both the weak undefined
+// and the lazy. We represent that with a lazy symbol with a weak binding. This
+// means that code looking for undefined symbols normally also has to take lazy
+// symbols into consideration.
+class Lazy : public Symbol {
public:
- static bool classof(const SymbolBody *S) { return S->isLazy(); }
+ static bool classof(const Symbol *S) { return S->isLazy(); }
// Returns an object file for this symbol, or a nullptr if the file
// was already returned.
InputFile *fetch();
protected:
- Lazy(SymbolBody::Kind K, StringRef Name, uint8_t Type)
- : SymbolBody(K, Name, /*IsLocal=*/false, llvm::ELF::STV_DEFAULT, Type) {}
+ Lazy(Kind K, InputFile *File, StringRef Name, uint8_t Type)
+ : Symbol(K, File, Name, llvm::ELF::STB_GLOBAL, llvm::ELF::STV_DEFAULT,
+ Type) {}
};
-// LazyArchive symbols represents symbols in archive files.
+// This class represents a symbol defined in an archive file. It is
+// created from an archive file header, and it knows how to load an
+// object file from an archive to replace itself with a defined
+// symbol.
class LazyArchive : public Lazy {
public:
- LazyArchive(ArchiveFile &File, const llvm::object::Archive::Symbol S,
- uint8_t Type);
+ LazyArchive(InputFile *File, const llvm::object::Archive::Symbol S,
+ uint8_t Type)
+ : Lazy(LazyArchiveKind, File, S.getName(), Type), Sym(S) {}
- static bool classof(const SymbolBody *S) {
- return S->kind() == LazyArchiveKind;
- }
+ static bool classof(const Symbol *S) { return S->kind() == LazyArchiveKind; }
- ArchiveFile *file() { return (ArchiveFile *)this->File; }
+ ArchiveFile *getFile();
InputFile *fetch();
private:
@@ -292,123 +295,85 @@ private:
// --start-lib and --end-lib options.
class LazyObject : public Lazy {
public:
- LazyObject(StringRef Name, LazyObjectFile &File, uint8_t Type);
+ LazyObject(InputFile *File, StringRef Name, uint8_t Type)
+ : Lazy(LazyObjectKind, File, Name, Type) {}
- static bool classof(const SymbolBody *S) {
- return S->kind() == LazyObjectKind;
- }
+ static bool classof(const Symbol *S) { return S->kind() == LazyObjectKind; }
- LazyObjectFile *file() { return (LazyObjectFile *)this->File; }
+ LazyObjFile *getFile();
InputFile *fetch();
};
// Some linker-generated symbols need to be created as
-// DefinedRegular symbols.
+// Defined symbols.
struct ElfSym {
// __bss_start
- static DefinedRegular *Bss;
+ static Defined *Bss;
// etext and _etext
- static DefinedRegular *Etext1;
- static DefinedRegular *Etext2;
+ static Defined *Etext1;
+ static Defined *Etext2;
// edata and _edata
- static DefinedRegular *Edata1;
- static DefinedRegular *Edata2;
+ static Defined *Edata1;
+ static Defined *Edata2;
// end and _end
- static DefinedRegular *End1;
- static DefinedRegular *End2;
+ static Defined *End1;
+ static Defined *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;
+ static Defined *GlobalOffsetTable;
// _gp, _gp_disp and __gnu_local_gp symbols. Only for MIPS.
- static DefinedRegular *MipsGp;
- static DefinedRegular *MipsGpDisp;
- static DefinedRegular *MipsLocalGp;
+ static Defined *MipsGp;
+ static Defined *MipsGpDisp;
+ static Defined *MipsLocalGp;
};
-// A real symbol object, SymbolBody, is usually stored within a Symbol. There's
-// always one Symbol for each symbol name. The resolver updates the SymbolBody
-// stored in the Body field of this object as it resolves symbols. Symbol also
-// holds computed properties of symbol names.
-struct Symbol {
- // Symbol binding. This is on the Symbol to track changes during resolution.
- // In particular:
- // An undefined weak is still weak when it resolves to a shared library.
- // An undefined weak will not fetch archive members, but we have to remember
- // it is weak.
- uint8_t Binding;
-
- // Version definition index.
- uint16_t VersionId;
-
- // Symbol visibility. This is the computed minimum visibility of all
- // observed non-DSO symbols.
- unsigned Visibility : 2;
-
- // True if the symbol was used for linking and thus need to be added to the
- // output file's symbol table. This is true for all symbols except for
- // unreferenced DSO symbols and bitcode symbols that are unreferenced except
- // by other bitcode objects.
- unsigned IsUsedInRegularObj : 1;
-
- // If this flag is true and the symbol has protected or default visibility, it
- // will appear in .dynsym. This flag is set by interposable DSO symbols in
- // executables, by most symbols in DSOs and executables built with
- // --export-dynamic, and by dynamic lists.
- unsigned ExportDynamic : 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;
-
- bool includeInDynsym() const;
- uint8_t computeBinding() const;
- bool isWeak() const { return Binding == llvm::ELF::STB_WEAK; }
-
- // This field is used to store the Symbol's SymbolBody. This instantiation of
- // AlignedCharArrayUnion gives us a struct with a char array field that is
- // large and aligned enough to store any derived class of SymbolBody.
- llvm::AlignedCharArrayUnion<DefinedCommon, DefinedRegular, Undefined,
- SharedSymbol, LazyArchive, LazyObject>
- Body;
-
- SymbolBody *body() { return reinterpret_cast<SymbolBody *>(Body.buffer); }
- const SymbolBody *body() const { return const_cast<Symbol *>(this)->body(); }
+// A buffer class that is large enough to hold any Symbol-derived
+// object. We allocate memory using this class and instantiate a symbol
+// using the placement new.
+union SymbolUnion {
+ alignas(Defined) char A[sizeof(Defined)];
+ alignas(Undefined) char C[sizeof(Undefined)];
+ alignas(SharedSymbol) char D[sizeof(SharedSymbol)];
+ alignas(LazyArchive) char E[sizeof(LazyArchive)];
+ alignas(LazyObject) char F[sizeof(LazyObject)];
};
void printTraceSymbol(Symbol *Sym);
template <typename T, typename... ArgT>
-void replaceBody(Symbol *S, ArgT &&... Arg) {
- static_assert(sizeof(T) <= sizeof(S->Body), "Body too small");
- static_assert(alignof(T) <= alignof(decltype(S->Body)),
- "Body not aligned enough");
- assert(static_cast<SymbolBody *>(static_cast<T *>(nullptr)) == nullptr &&
- "Not a SymbolBody");
+void replaceSymbol(Symbol *S, ArgT &&... Arg) {
+ static_assert(sizeof(T) <= sizeof(SymbolUnion), "SymbolUnion too small");
+ static_assert(alignof(T) <= alignof(SymbolUnion),
+ "SymbolUnion not aligned enough");
+ assert(static_cast<Symbol *>(static_cast<T *>(nullptr)) == nullptr &&
+ "Not a Symbol");
+
+ Symbol Sym = *S;
- new (S->Body.buffer) T(std::forward<ArgT>(Arg)...);
+ new (S) T(std::forward<ArgT>(Arg)...);
+
+ S->VersionId = Sym.VersionId;
+ S->Visibility = Sym.Visibility;
+ S->IsUsedInRegularObj = Sym.IsUsedInRegularObj;
+ S->ExportDynamic = Sym.ExportDynamic;
+ S->CanInline = Sym.CanInline;
+ S->Traced = Sym.Traced;
+ S->InVersionScript = Sym.InVersionScript;
// Print out a log message if --trace-symbol was specified.
// This is for debugging.
if (S->Traced)
printTraceSymbol(S);
}
-
-inline Symbol *SymbolBody::symbol() {
- assert(!isLocal());
- return reinterpret_cast<Symbol *>(reinterpret_cast<char *>(this) -
- offsetof(Symbol, Body));
-}
} // namespace elf
-std::string toString(const elf::SymbolBody &B);
+std::string toString(const elf::Symbol &B);
} // namespace lld
#endif
diff --git a/ELF/SyntheticSections.cpp b/ELF/SyntheticSections.cpp
index a67b039ddf21..b408e653dfa0 100644
--- a/ELF/SyntheticSections.cpp
+++ b/ELF/SyntheticSections.cpp
@@ -15,28 +15,32 @@
//===----------------------------------------------------------------------===//
#include "SyntheticSections.h"
+#include "Bits.h"
#include "Config.h"
-#include "Error.h"
#include "InputFiles.h"
#include "LinkerScript.h"
-#include "Memory.h"
#include "OutputSections.h"
#include "Strings.h"
#include "SymbolTable.h"
+#include "Symbols.h"
#include "Target.h"
-#include "Threads.h"
#include "Writer.h"
-#include "lld/Config/Version.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.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"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Support/Endian.h"
+#include "llvm/Support/LEB128.h"
#include "llvm/Support/MD5.h"
#include "llvm/Support/RandomNumberGenerator.h"
#include "llvm/Support/SHA1.h"
#include "llvm/Support/xxhash.h"
#include <cstdlib>
+#include <thread>
using namespace llvm;
using namespace llvm::dwarf;
@@ -48,41 +52,18 @@ using namespace llvm::support::endian;
using namespace lld;
using namespace lld::elf;
+constexpr size_t MergeNoTailSection::NumShards;
+
+static void write32(void *Buf, uint32_t Val) {
+ endian::write32(Buf, Val, Config->Endianness);
+}
+
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;
-
- std::stable_sort(Syms.begin(), Syms.end(),
- [](const DefinedCommon *A, const DefinedCommon *B) {
- return A->Alignment > B->Alignment;
- });
-
- BssSection *Sec = make<BssSection>("COMMON");
- for (DefinedCommon *Sym : Syms)
- Sym->Offset = Sec->reserveSpace(Sym->Size, Sym->Alignment);
- return Sec;
-}
-
// Returns an LLD version string.
static ArrayRef<uint8_t> getVersion() {
// Check LLD_VERSION first for ease of testing.
@@ -108,9 +89,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;
}
@@ -137,7 +117,7 @@ MipsAbiFlagsSection<ELFT> *MipsAbiFlagsSection<ELFT>::create() {
Sec->Live = false;
Create = true;
- std::string Filename = toString(Sec->getFile<ELFT>());
+ std::string Filename = toString(Sec->File);
const size_t Size = Sec->Data.size();
// Older version of BFD (such as the default FreeBSD linker) concatenate
// .MIPS.abiflags instead of merging. To allow for this case (or potential
@@ -154,7 +134,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);
@@ -197,16 +177,19 @@ MipsOptionsSection<ELFT> *MipsOptionsSection<ELFT>::create() {
if (!ELFT::Is64Bits)
return nullptr;
- Elf_Mips_RegInfo Reginfo = {};
- bool Create = false;
+ std::vector<InputSectionBase *> Sections;
+ for (InputSectionBase *Sec : InputSections)
+ if (Sec->Type == SHT_MIPS_OPTIONS)
+ Sections.push_back(Sec);
- for (InputSectionBase *Sec : InputSections) {
- if (Sec->Type != SHT_MIPS_OPTIONS)
- continue;
+ if (Sections.empty())
+ return nullptr;
+
+ Elf_Mips_RegInfo Reginfo = {};
+ for (InputSectionBase *Sec : Sections) {
Sec->Live = false;
- Create = true;
- std::string Filename = toString(Sec->getFile<ELFT>());
+ std::string Filename = toString(Sec->File);
ArrayRef<uint8_t> D = Sec->Data;
while (!D.empty()) {
@@ -230,9 +213,7 @@ MipsOptionsSection<ELFT> *MipsOptionsSection<ELFT>::create() {
}
};
- if (Create)
- return make<MipsOptionsSection<ELFT>>(Reginfo);
- return nullptr;
+ return make<MipsOptionsSection<ELFT>>(Reginfo);
}
// MIPS .reginfo section.
@@ -255,32 +236,31 @@ MipsReginfoSection<ELFT> *MipsReginfoSection<ELFT>::create() {
if (ELFT::Is64Bits)
return nullptr;
- Elf_Mips_RegInfo Reginfo = {};
- bool Create = false;
+ std::vector<InputSectionBase *> Sections;
+ for (InputSectionBase *Sec : InputSections)
+ if (Sec->Type == SHT_MIPS_REGINFO)
+ Sections.push_back(Sec);
- for (InputSectionBase *Sec : InputSections) {
- if (Sec->Type != SHT_MIPS_REGINFO)
- continue;
+ if (Sections.empty())
+ return nullptr;
+
+ Elf_Mips_RegInfo Reginfo = {};
+ for (InputSectionBase *Sec : Sections) {
Sec->Live = false;
- Create = true;
if (Sec->Data.size() != sizeof(Elf_Mips_RegInfo)) {
- error(toString(Sec->getFile<ELFT>()) +
- ": invalid size of .reginfo section");
+ error(toString(Sec->File) + ": invalid size of .reginfo section");
return nullptr;
}
auto *R = reinterpret_cast<const Elf_Mips_RegInfo *>(Sec->Data.data());
if (Config->Relocatable && R->ri_gp_value)
- error(toString(Sec->getFile<ELFT>()) +
- ": unsupported non-zero ri_gp_value");
+ error(toString(Sec->File) + ": unsupported non-zero ri_gp_value");
Reginfo.ri_gprmask |= R->ri_gprmask;
Sec->getFile<ELFT>()->MipsGp0 = R->ri_gp_value;
};
- if (Create)
- return make<MipsReginfoSection<ELFT>>(Reginfo);
- return nullptr;
+ return make<MipsReginfoSection<ELFT>>(Reginfo);
}
InputSection *elf::createInterpSection() {
@@ -294,10 +274,10 @@ InputSection *elf::createInterpSection() {
return Sec;
}
-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);
+Symbol *elf::addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value,
+ uint64_t Size, InputSectionBase *Section) {
+ auto *S = make<Defined>(Section->File, Name, STB_LOCAL, STV_DEFAULT, Type,
+ Value, Size, Section);
if (InX::SymTab)
InX::SymTab->addSymbol(S);
return S;
@@ -320,14 +300,13 @@ static size_t getHashSize() {
}
BuildIdSection::BuildIdSection()
- : SyntheticSection(SHF_ALLOC, SHT_NOTE, 1, ".note.gnu.build-id"),
+ : SyntheticSection(SHF_ALLOC, SHT_NOTE, 4, ".note.gnu.build-id"),
HashSize(getHashSize()) {}
void BuildIdSection::writeTo(uint8_t *Buf) {
- endianness E = Config->Endianness;
- write32(Buf, 4, E); // Name size
- write32(Buf + 4, HashSize, E); // Content size
- write32(Buf + 8, NT_GNU_BUILD_ID, E); // Type
+ write32(Buf, 4); // Name size
+ write32(Buf + 4, HashSize); // Content size
+ write32(Buf + 8, NT_GNU_BUILD_ID); // Type
memcpy(Buf + 12, "GNU", 4); // Name string
HashBuf = Buf + 16;
}
@@ -364,15 +343,12 @@ 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) {
+ this->Bss = true;
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) {
@@ -393,8 +369,8 @@ void BuildIdSection::writeBuildId(ArrayRef<uint8_t> Buf) {
});
break;
case BuildIdKind::Uuid:
- if (getRandomBytes(HashBuf, HashSize))
- error("entropy source failure");
+ if (auto EC = getRandomBytes(HashBuf, HashSize))
+ error("entropy source failure: " + EC.message());
break;
case BuildIdKind::Hexstring:
memcpy(HashBuf, Config->BuildIdVector.data(), Config->BuildIdVector.size());
@@ -404,101 +380,99 @@ void BuildIdSection::writeBuildId(ArrayRef<uint8_t> Buf) {
}
}
-template <class ELFT>
-EhFrameSection<ELFT>::EhFrameSection()
+EhFrameSection::EhFrameSection()
: SyntheticSection(SHF_ALLOC, SHT_PROGBITS, 1, ".eh_frame") {}
// Search for an existing CIE record or create a new one.
// CIE records from input object files are uniquified by their contents
// and where their relocations point to.
-template <class ELFT>
-template <class RelTy>
-CieRecord *EhFrameSection<ELFT>::addCie(EhSectionPiece &Piece,
- ArrayRef<RelTy> Rels) {
- auto *Sec = cast<EhInputSection>(Piece.ID);
- const endianness E = ELFT::TargetEndianness;
- if (read32<E>(Piece.data().data() + 4) != 0)
+template <class ELFT, class RelTy>
+CieRecord *EhFrameSection::addCie(EhSectionPiece &Cie, ArrayRef<RelTy> Rels) {
+ auto *Sec = cast<EhInputSection>(Cie.Sec);
+ if (read32(Cie.data().data() + 4, Config->Endianness) != 0)
fatal(toString(Sec) + ": CIE expected at beginning of .eh_frame");
- SymbolBody *Personality = nullptr;
- unsigned FirstRelI = Piece.FirstRelocation;
+ Symbol *Personality = nullptr;
+ 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) {
- Cie = make<CieRecord>();
- 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,
- ArrayRef<RelTy> Rels) {
- auto *Sec = cast<EhInputSection>(Piece.ID);
- unsigned FirstRelI = Piece.FirstRelocation;
+template <class ELFT, class RelTy>
+bool EhFrameSection::isFdeLive(EhSectionPiece &Fde, ArrayRef<RelTy> Rels) {
+ 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;
+ Symbol &B = Sec->template getFile<ELFT>()->getRelocTargetSym(Rel);
+
+ // FDEs for garbage-collected or merged-by-ICF sections are dead.
+ if (auto *D = dyn_cast<Defined>(&B))
+ if (SectionBase *Sec = D->Section)
+ return Sec->Live;
+ return false;
}
// .eh_frame is a sequence of CIE or FDE records. In general, there
// is one CIE record per input object file which is followed by
// a list of FDEs. This function searches an existing CIE or create a new
// one and associates FDEs to the CIE.
-template <class ELFT>
-template <class RelTy>
-void EhFrameSection<ELFT>::addSectionAux(EhInputSection *Sec,
- ArrayRef<RelTy> Rels) {
- const endianness E = ELFT::TargetEndianness;
-
+template <class ELFT, class RelTy>
+void EhFrameSection::addSectionAux(EhInputSection *Sec, ArrayRef<RelTy> Rels) {
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;
- uint32_t ID = read32<E>(Piece.data().data() + 4);
+ uint32_t ID = read32(Piece.data().data() + 4, Config->Endianness);
if (ID == 0) {
- OffsetToCie[Offset] = addCie(Piece, Rels);
+ OffsetToCie[Offset] = addCie<ELFT>(Piece, Rels);
continue;
}
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))
+ if (!isFdeLive<ELFT>(Piece, Rels))
continue;
- Cie->FdePieces.push_back(&Piece);
+ Rec->Fdes.push_back(&Piece);
NumFdes++;
}
}
-template <class ELFT>
-void EhFrameSection<ELFT>::addSection(InputSectionBase *C) {
+template <class ELFT> void EhFrameSection::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);
@@ -509,42 +483,36 @@ 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->AreRelocsRela)
+ addSectionAux<ELFT>(Sec, Sec->template relas<ELFT>());
+ else
+ addSectionAux<ELFT>(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));
+ size_t Aligned = alignTo(D.size(), Config->Wordsize);
// 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, Aligned - 4);
+ write32(Buf, Aligned - 4);
}
-template <class ELFT> void EhFrameSection<ELFT>::finalizeContents() {
+void EhFrameSection::finalizeContents() {
if (this->Size)
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);
}
}
@@ -557,32 +525,46 @@ template <class ELFT> void EhFrameSection<ELFT>::finalizeContents() {
this->Size = Off;
}
-template <class ELFT> static uint64_t readFdeAddr(uint8_t *Buf, int Size) {
- const endianness E = ELFT::TargetEndianness;
+// Returns data for .eh_frame_hdr. .eh_frame_hdr is a binary search table
+// to get an FDE from an address to which FDE is applied. This function
+// returns a list of such pairs.
+std::vector<EhFrameSection::FdeData> EhFrameSection::getFdeData() const {
+ uint8_t *Buf = getParent()->Loc + OutSecOff;
+ std::vector<FdeData> Ret;
+
+ for (CieRecord *Rec : CieRecords) {
+ uint8_t Enc = getFdeEncoding(Rec->Cie);
+ for (EhSectionPiece *Fde : Rec->Fdes) {
+ uint32_t Pc = getFdePc(Buf, Fde->OutputOff, Enc);
+ uint32_t FdeVA = getParent()->Addr + Fde->OutputOff;
+ Ret.push_back({Pc, FdeVA});
+ }
+ }
+ return Ret;
+}
+
+static uint64_t readFdeAddr(uint8_t *Buf, int Size) {
switch (Size) {
case DW_EH_PE_udata2:
- return read16<E>(Buf);
+ return read16(Buf, Config->Endianness);
case DW_EH_PE_udata4:
- return read32<E>(Buf);
+ return read32(Buf, Config->Endianness);
case DW_EH_PE_udata8:
- return read64<E>(Buf);
+ return read64(Buf, Config->Endianness);
case DW_EH_PE_absptr:
- if (ELFT::Is64Bits)
- return read64<E>(Buf);
- return read32<E>(Buf);
+ return readUint(Buf);
}
fatal("unknown FDE size encoding");
}
// Returns the VA to which a given FDE (on a mmap'ed buffer) is applied to.
// We need it to create .eh_frame_hdr section.
-template <class ELFT>
-uint64_t EhFrameSection<ELFT>::getFdePc(uint8_t *Buf, size_t FdeOff,
- uint8_t Enc) {
+uint64_t EhFrameSection::getFdePc(uint8_t *Buf, size_t FdeOff,
+ uint8_t Enc) const {
// The starting address to which this FDE applies is
// stored at FDE + 8 byte.
size_t Off = FdeOff + 8;
- uint64_t Addr = readFdeAddr<ELFT>(Buf + Off, Enc & 0x7);
+ uint64_t Addr = readFdeAddr(Buf + Off, Enc & 0x7);
if ((Enc & 0x70) == DW_EH_PE_absptr)
return Addr;
if ((Enc & 0x70) == DW_EH_PE_pcrel)
@@ -590,50 +572,39 @@ uint64_t EhFrameSection<ELFT>::getFdePc(uint8_t *Buf, size_t FdeOff,
fatal("unknown FDE size relative encoding");
}
-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());
+void EhFrameSection::writeTo(uint8_t *Buf) {
+ // Write CIE and FDE records.
+ for (CieRecord *Rec : CieRecords) {
+ size_t CieOffset = Rec->Cie->OutputOff;
+ writeCieFde(Buf + CieOffset, Rec->Cie->data());
- for (EhSectionPiece *Fde : Cie->FdePieces) {
+ for (EhSectionPiece *Fde : Rec->Fdes) {
size_t Off = Fde->OutputOff;
- writeCieFde<ELFT>(Buf + Off, Fde->data());
+ writeCieFde(Buf + Off, Fde->data());
// FDE's second word should have the offset to an associated CIE.
// Write it.
- write32<E>(Buf + Off + 4, Off + 4 - CieOffset);
+ write32(Buf + Off + 4, Off + 4 - CieOffset);
}
}
+ // 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);
-
- // Construct .eh_frame_hdr. .eh_frame_hdr is a binary search table
- // to get a FDE from an address to which FDE is applied. So here
- // we obtain two addresses and pass them to EhFrameHdr object.
- if (In<ELFT>::EhFrameHdr) {
- for (CieRecord *Cie : Cies) {
- uint8_t Enc = getFdeEncoding<ELFT>(Cie->Piece);
- for (SectionPiece *Fde : Cie->FdePieces) {
- uint64_t Pc = getFdePc(Buf, Fde->OutputOff, Enc);
- uint64_t FdeVA = getParent()->Addr + Fde->OutputOff;
- In<ELFT>::EhFrameHdr->addFde(Pc, FdeVA);
- }
- }
- }
}
GotSection::GotSection()
: SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS,
Target->GotEntrySize, ".got") {}
-void GotSection::addEntry(SymbolBody &Sym) {
+void GotSection::addEntry(Symbol &Sym) {
Sym.GotIndex = NumEntries;
++NumEntries;
}
-bool GotSection::addDynTlsEntry(SymbolBody &Sym) {
+bool GotSection::addDynTlsEntry(Symbol &Sym) {
if (Sym.GlobalDynIndex != -1U)
return false;
Sym.GlobalDynIndex = NumEntries;
@@ -652,20 +623,21 @@ bool GotSection::addTlsIndex() {
return true;
}
-uint64_t GotSection::getGlobalDynAddr(const SymbolBody &B) const {
+uint64_t GotSection::getGlobalDynAddr(const Symbol &B) const {
return this->getVA() + B.GlobalDynIndex * Config->Wordsize;
}
-uint64_t GotSection::getGlobalDynOffset(const SymbolBody &B) const {
+uint64_t GotSection::getGlobalDynOffset(const Symbol &B) const {
return B.GlobalDynIndex * Config->Wordsize;
}
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) {
@@ -679,7 +651,7 @@ MipsGotSection::MipsGotSection()
: SyntheticSection(SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL, SHT_PROGBITS, 16,
".got") {}
-void MipsGotSection::addEntry(SymbolBody &Sym, int64_t Addend, RelExpr Expr) {
+void MipsGotSection::addEntry(Symbol &Sym, int64_t Addend, RelExpr Expr) {
// For "true" local symbols which can be referenced from the same module
// only compiler creates two instructions for address loading:
//
@@ -721,7 +693,7 @@ void MipsGotSection::addEntry(SymbolBody &Sym, int64_t Addend, RelExpr Expr) {
TlsEntries.push_back(&Sym);
return;
}
- auto AddEntry = [&](SymbolBody &S, uint64_t A, GotEntries &Items) {
+ auto AddEntry = [&](Symbol &S, uint64_t A, GotEntries &Items) {
if (S.isInGot() && !A)
return;
size_t NewIndex = Items.size();
@@ -731,7 +703,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;
@@ -746,7 +718,7 @@ void MipsGotSection::addEntry(SymbolBody &Sym, int64_t Addend, RelExpr Expr) {
}
}
-bool MipsGotSection::addDynTlsEntry(SymbolBody &Sym) {
+bool MipsGotSection::addDynTlsEntry(Symbol &Sym) {
if (Sym.GlobalDynIndex != -1U)
return false;
Sym.GlobalDynIndex = TlsEntries.size();
@@ -775,7 +747,7 @@ static uint64_t getMipsPageCount(uint64_t Size) {
return (Size + 0xfffe) / 0xffff + 1;
}
-uint64_t MipsGotSection::getPageEntryOffset(const SymbolBody &B,
+uint64_t MipsGotSection::getPageEntryOffset(const Symbol &B,
int64_t Addend) const {
const OutputSection *OutSec = B.getOutputSection();
uint64_t SecAddr = getMipsPageAddr(OutSec->Addr);
@@ -785,8 +757,8 @@ uint64_t MipsGotSection::getPageEntryOffset(const SymbolBody &B,
return (HeaderEntriesNum + Index) * Config->Wordsize;
}
-uint64_t MipsGotSection::getBodyEntryOffset(const SymbolBody &B,
- int64_t Addend) const {
+uint64_t MipsGotSection::getSymEntryOffset(const Symbol &B,
+ int64_t Addend) const {
// Calculate offset of the GOT entries block: TLS, global, local.
uint64_t Index = HeaderEntriesNum + PageEntriesNum;
if (B.isTls())
@@ -810,11 +782,11 @@ uint64_t MipsGotSection::getTlsOffset() const {
return (getLocalEntriesNum() + GlobalEntries.size()) * Config->Wordsize;
}
-uint64_t MipsGotSection::getGlobalDynOffset(const SymbolBody &B) const {
+uint64_t MipsGotSection::getGlobalDynOffset(const Symbol &B) const {
return B.GlobalDynIndex * Config->Wordsize;
}
-const SymbolBody *MipsGotSection::getFirstGlobalEntry() const {
+const Symbol *MipsGotSection::getFirstGlobalEntry() const {
return GlobalEntries.empty() ? nullptr : GlobalEntries.front().first;
}
@@ -825,7 +797,7 @@ unsigned MipsGotSection::getLocalEntriesNum() const {
void MipsGotSection::finalizeContents() { updateAllocSize(); }
-void MipsGotSection::updateAllocSize() {
+bool MipsGotSection::updateAllocSize() {
PageEntriesNum = 0;
for (std::pair<const OutputSection *, size_t> &P : PageIndexMap) {
// For each output section referenced by GOT page relocations calculate
@@ -839,6 +811,7 @@ void MipsGotSection::updateAllocSize() {
}
Size = (getLocalEntriesNum() + GlobalEntries.size() + TlsEntries.size()) *
Config->Wordsize;
+ return false;
}
bool MipsGotSection::empty() const {
@@ -849,19 +822,6 @@ bool MipsGotSection::empty() const {
uint64_t MipsGotSection::getGp() const { return ElfSym::MipsGp->getVA(0); }
-static uint64_t readUint(uint8_t *Buf) {
- if (Config->Is64)
- return read64(Buf, Config->Endianness);
- return read32(Buf, Config->Endianness);
-}
-
-static void writeUint(uint8_t *Buf, uint64_t Val) {
- if (Config->Is64)
- write64(Buf, Val, Config->Endianness);
- else
- write32(Buf, Val, Config->Endianness);
-}
-
void MipsGotSection::writeTo(uint8_t *Buf) {
// Set the MSB of the second GOT slot. This is not required by any
// MIPS ABI documentation, though.
@@ -892,8 +852,10 @@ void MipsGotSection::writeTo(uint8_t *Buf) {
auto AddEntry = [&](const GotEntry &SA) {
uint8_t *Entry = Buf;
Buf += Config->Wordsize;
- const SymbolBody *Body = SA.first;
- uint64_t VA = Body->getVA(SA.second);
+ const Symbol *Sym = SA.first;
+ uint64_t VA = Sym->getVA(SA.second);
+ if (Sym->StOther & STO_MIPS_MICROMIPS)
+ VA |= 1;
writeUint(Entry, VA);
};
std::for_each(std::begin(LocalEntries), std::end(LocalEntries), AddEntry);
@@ -906,8 +868,8 @@ void MipsGotSection::writeTo(uint8_t *Buf) {
// https://www.linux-mips.org/wiki/NPTL
if (TlsIndexOff != -1U && !Config->Pic)
writeUint(Buf + TlsIndexOff, 1);
- for (const SymbolBody *B : TlsEntries) {
- if (!B || B->isPreemptible())
+ for (const Symbol *B : TlsEntries) {
+ if (!B || B->IsPreemptible)
continue;
uint64_t VA = B->getVA();
if (B->GotIndex != -1U) {
@@ -927,7 +889,7 @@ GotPltSection::GotPltSection()
: SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS,
Target->GotPltEntrySize, ".got.plt") {}
-void GotPltSection::addEntry(SymbolBody &Sym) {
+void GotPltSection::addEntry(Symbol &Sym) {
Sym.GotPltIndex = Target->GotPltHeaderEntriesNum + Entries.size();
Entries.push_back(&Sym);
}
@@ -940,7 +902,7 @@ size_t GotPltSection::getSize() const {
void GotPltSection::writeTo(uint8_t *Buf) {
Target->writeGotPltHeader(Buf);
Buf += Target->GotPltHeaderEntriesNum * Target->GotPltEntrySize;
- for (const SymbolBody *B : Entries) {
+ for (const Symbol *B : Entries) {
Target->writeGotPlt(Buf, *B);
Buf += Config->Wordsize;
}
@@ -953,7 +915,7 @@ IgotPltSection::IgotPltSection()
Target->GotPltEntrySize,
Config->EMachine == EM_ARM ? ".got" : ".got.plt") {}
-void IgotPltSection::addEntry(SymbolBody &Sym) {
+void IgotPltSection::addEntry(Symbol &Sym) {
Sym.IsInIgot = true;
Sym.GotPltIndex = Entries.size();
Entries.push_back(&Sym);
@@ -964,7 +926,7 @@ size_t IgotPltSection::getSize() const {
}
void IgotPltSection::writeTo(uint8_t *Buf) {
- for (const SymbolBody *B : Entries) {
+ for (const Symbol *B : Entries) {
Target->writeIgotPlt(Buf, *B);
Buf += Config->Wordsize;
}
@@ -996,6 +958,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;
}
}
@@ -1018,26 +981,61 @@ DynamicSection<ELFT>::DynamicSection()
if (Config->EMachine == EM_MIPS || Config->ZRodynamic)
this->Flags = SHF_ALLOC;
- addEntries();
-}
-
-// There are some dynamic entries that don't depend on other sections.
-// Such entries can be set early.
-template <class ELFT> void DynamicSection<ELFT>::addEntries() {
// Add strings to .dynstr early so that .dynstr's size will be
// fixed early.
for (StringRef S : Config->FilterList)
- add({DT_FILTER, InX::DynStrTab->addString(S)});
+ addInt(DT_FILTER, InX::DynStrTab->addString(S));
for (StringRef S : Config->AuxiliaryList)
- add({DT_AUXILIARY, InX::DynStrTab->addString(S)});
+ addInt(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())
- if (F->isNeeded())
- add({DT_NEEDED, InX::DynStrTab->addString(F->SoName)});
+ addInt(Config->EnableNewDtags ? DT_RUNPATH : DT_RPATH,
+ InX::DynStrTab->addString(Config->Rpath));
+
+ for (InputFile *File : SharedFiles) {
+ SharedFile<ELFT> *F = cast<SharedFile<ELFT>>(File);
+ if (F->IsNeeded)
+ addInt(DT_NEEDED, InX::DynStrTab->addString(F->SoName));
+ }
if (!Config->SoName.empty())
- add({DT_SONAME, InX::DynStrTab->addString(Config->SoName)});
+ addInt(DT_SONAME, InX::DynStrTab->addString(Config->SoName));
+}
+
+template <class ELFT>
+void DynamicSection<ELFT>::add(int32_t Tag, std::function<uint64_t()> Fn) {
+ Entries.push_back({Tag, Fn});
+}
+
+template <class ELFT>
+void DynamicSection<ELFT>::addInt(int32_t Tag, uint64_t Val) {
+ Entries.push_back({Tag, [=] { return Val; }});
+}
+
+template <class ELFT>
+void DynamicSection<ELFT>::addInSec(int32_t Tag, InputSection *Sec) {
+ Entries.push_back(
+ {Tag, [=] { return Sec->getParent()->Addr + Sec->OutSecOff; }});
+}
+
+template <class ELFT>
+void DynamicSection<ELFT>::addOutSec(int32_t Tag, OutputSection *Sec) {
+ Entries.push_back({Tag, [=] { return Sec->Addr; }});
+}
+
+template <class ELFT>
+void DynamicSection<ELFT>::addSize(int32_t Tag, OutputSection *Sec) {
+ Entries.push_back({Tag, [=] { return Sec->Size; }});
+}
+
+template <class ELFT>
+void DynamicSection<ELFT>::addSym(int32_t Tag, Symbol *Sym) {
+ Entries.push_back({Tag, [=] { return Sym->getVA(); }});
+}
+
+// Add remaining entries to complete .dynamic contents.
+template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
+ if (this->Size)
+ return; // Already finalized.
// Set DT_FLAGS and DT_FLAGS_1.
uint32_t DtFlags = 0;
@@ -1058,9 +1056,9 @@ template <class ELFT> void DynamicSection<ELFT>::addEntries() {
}
if (DtFlags)
- add({DT_FLAGS, DtFlags});
+ addInt(DT_FLAGS, DtFlags);
if (DtFlags1)
- add({DT_FLAGS_1, DtFlags1});
+ addInt(DT_FLAGS_1, DtFlags1);
// DT_DEBUG is a pointer to debug informaion used by debuggers at runtime. We
// need it for each process, so we don't write it for DSOs. The loader writes
@@ -1071,133 +1069,115 @@ template <class ELFT> void DynamicSection<ELFT>::addEntries() {
// debugger this information. Such systems may choose make .dynamic read-only.
// If the target is such a system (used -z rodynamic) don't write DT_DEBUG.
if (!Config->Shared && !Config->Relocatable && !Config->ZRodynamic)
- add({DT_DEBUG, (uint64_t)0});
-}
-
-// Add remaining entries to complete .dynamic contents.
-template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
- if (this->Size)
- return; // Already finalized.
+ addInt(DT_DEBUG, 0);
this->Link = InX::DynStrTab->getParent()->SectionIndex;
- if (In<ELFT>::RelaDyn->getParent() && !In<ELFT>::RelaDyn->empty()) {
+ if (InX::RelaDyn->getParent() && !InX::RelaDyn->empty()) {
+ addInSec(InX::RelaDyn->DynamicTag, InX::RelaDyn);
+ addSize(InX::RelaDyn->SizeDynamicTag, InX::RelaDyn->getParent());
+
bool IsRela = Config->IsRela;
- add({IsRela ? DT_RELA : DT_REL, In<ELFT>::RelaDyn});
- 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))});
+ addInt(IsRela ? DT_RELAENT : DT_RELENT,
+ IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel));
// MIPS dynamic loader does not support RELCOUNT tag.
// The problem is in the tight relation between dynamic
// relocations and GOT. So do not emit this tag on MIPS.
if (Config->EMachine != EM_MIPS) {
- size_t NumRelativeRels = In<ELFT>::RelaDyn->getRelativeRelocCount();
+ size_t NumRelativeRels = InX::RelaDyn->getRelativeRelocCount();
if (Config->ZCombreloc && NumRelativeRels)
- add({IsRela ? DT_RELACOUNT : DT_RELCOUNT, NumRelativeRels});
+ addInt(IsRela ? DT_RELACOUNT : DT_RELCOUNT, NumRelativeRels);
}
}
- if (In<ELFT>::RelaPlt->getParent() && !In<ELFT>::RelaPlt->empty()) {
- add({DT_JMPREL, In<ELFT>::RelaPlt});
- add({DT_PLTRELSZ, In<ELFT>::RelaPlt->getParent(), Entry::SecSize});
+ if (InX::RelaPlt->getParent() && !InX::RelaPlt->empty()) {
+ addInSec(DT_JMPREL, InX::RelaPlt);
+ addSize(DT_PLTRELSZ, InX::RelaPlt->getParent());
switch (Config->EMachine) {
case EM_MIPS:
- add({DT_MIPS_PLTGOT, In<ELFT>::GotPlt});
+ addInSec(DT_MIPS_PLTGOT, InX::GotPlt);
break;
case EM_SPARCV9:
- add({DT_PLTGOT, In<ELFT>::Plt});
+ addInSec(DT_PLTGOT, InX::Plt);
break;
default:
- add({DT_PLTGOT, In<ELFT>::GotPlt});
+ addInSec(DT_PLTGOT, InX::GotPlt);
break;
}
- add({DT_PLTREL, uint64_t(Config->IsRela ? DT_RELA : DT_REL)});
+ addInt(DT_PLTREL, Config->IsRela ? DT_RELA : DT_REL);
}
- add({DT_SYMTAB, InX::DynSymTab});
- add({DT_SYMENT, sizeof(Elf_Sym)});
- add({DT_STRTAB, InX::DynStrTab});
- add({DT_STRSZ, InX::DynStrTab->getSize()});
+ addInSec(DT_SYMTAB, InX::DynSymTab);
+ addInt(DT_SYMENT, sizeof(Elf_Sym));
+ addInSec(DT_STRTAB, InX::DynStrTab);
+ addInt(DT_STRSZ, InX::DynStrTab->getSize());
if (!Config->ZText)
- add({DT_TEXTREL, (uint64_t)0});
+ addInt(DT_TEXTREL, 0);
if (InX::GnuHashTab)
- add({DT_GNU_HASH, InX::GnuHashTab});
- if (In<ELFT>::HashTab)
- add({DT_HASH, In<ELFT>::HashTab});
+ addInSec(DT_GNU_HASH, InX::GnuHashTab);
+ if (InX::HashTab)
+ addInSec(DT_HASH, InX::HashTab);
if (Out::PreinitArray) {
- add({DT_PREINIT_ARRAY, Out::PreinitArray});
- add({DT_PREINIT_ARRAYSZ, Out::PreinitArray, Entry::SecSize});
+ addOutSec(DT_PREINIT_ARRAY, Out::PreinitArray);
+ addSize(DT_PREINIT_ARRAYSZ, Out::PreinitArray);
}
if (Out::InitArray) {
- add({DT_INIT_ARRAY, Out::InitArray});
- add({DT_INIT_ARRAYSZ, Out::InitArray, Entry::SecSize});
+ addOutSec(DT_INIT_ARRAY, Out::InitArray);
+ addSize(DT_INIT_ARRAYSZ, Out::InitArray);
}
if (Out::FiniArray) {
- add({DT_FINI_ARRAY, Out::FiniArray});
- add({DT_FINI_ARRAYSZ, Out::FiniArray, Entry::SecSize});
+ addOutSec(DT_FINI_ARRAY, Out::FiniArray);
+ addSize(DT_FINI_ARRAYSZ, Out::FiniArray);
}
- 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 (Symbol *B = Symtab->find(Config->Init))
+ if (B->isDefined())
+ addSym(DT_INIT, B);
+ if (Symbol *B = Symtab->find(Config->Fini))
+ if (B->isDefined())
+ addSym(DT_FINI, B);
bool HasVerNeed = In<ELFT>::VerNeed->getNeedNum() != 0;
if (HasVerNeed || In<ELFT>::VerDef)
- add({DT_VERSYM, In<ELFT>::VerSym});
+ addInSec(DT_VERSYM, In<ELFT>::VerSym);
if (In<ELFT>::VerDef) {
- add({DT_VERDEF, In<ELFT>::VerDef});
- add({DT_VERDEFNUM, getVerDefNum()});
+ addInSec(DT_VERDEF, In<ELFT>::VerDef);
+ addInt(DT_VERDEFNUM, getVerDefNum());
}
if (HasVerNeed) {
- add({DT_VERNEED, In<ELFT>::VerNeed});
- add({DT_VERNEEDNUM, In<ELFT>::VerNeed->getNeedNum()});
+ addInSec(DT_VERNEED, In<ELFT>::VerNeed);
+ addInt(DT_VERNEEDNUM, In<ELFT>::VerNeed->getNeedNum());
}
if (Config->EMachine == EM_MIPS) {
- add({DT_MIPS_RLD_VERSION, 1});
- add({DT_MIPS_FLAGS, RHF_NOTPOT});
- add({DT_MIPS_BASE_ADDRESS, Config->ImageBase});
- add({DT_MIPS_SYMTABNO, InX::DynSymTab->getNumSymbols()});
- add({DT_MIPS_LOCAL_GOTNO, InX::MipsGot->getLocalEntriesNum()});
- if (const SymbolBody *B = InX::MipsGot->getFirstGlobalEntry())
- add({DT_MIPS_GOTSYM, B->DynsymIndex});
+ addInt(DT_MIPS_RLD_VERSION, 1);
+ addInt(DT_MIPS_FLAGS, RHF_NOTPOT);
+ addInt(DT_MIPS_BASE_ADDRESS, Target->getImageBase());
+ addInt(DT_MIPS_SYMTABNO, InX::DynSymTab->getNumSymbols());
+
+ add(DT_MIPS_LOCAL_GOTNO, [] { return InX::MipsGot->getLocalEntriesNum(); });
+
+ if (const Symbol *B = InX::MipsGot->getFirstGlobalEntry())
+ addInt(DT_MIPS_GOTSYM, B->DynsymIndex);
else
- add({DT_MIPS_GOTSYM, InX::DynSymTab->getNumSymbols()});
- add({DT_PLTGOT, InX::MipsGot});
+ addInt(DT_MIPS_GOTSYM, InX::DynSymTab->getNumSymbols());
+ addInSec(DT_PLTGOT, InX::MipsGot);
if (InX::MipsRldMap)
- add({DT_MIPS_RLD_MAP, InX::MipsRldMap});
+ addInSec(DT_MIPS_RLD_MAP, InX::MipsRldMap);
}
- getParent()->Link = this->Link;
+ addInt(DT_NULL, 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) {
auto *P = reinterpret_cast<Elf_Dyn *>(Buf);
- for (const Entry &E : Entries) {
- P->d_tag = E.Tag;
- switch (E.Kind) {
- case Entry::SecAddr:
- P->d_un.d_ptr = E.OutSec->Addr;
- break;
- case Entry::InSecAddr:
- P->d_un.d_ptr = E.InSec->getParent()->Addr + E.InSec->OutSecOff;
- break;
- case Entry::SecSize:
- P->d_un.d_val = E.OutSec->Size;
- break;
- case Entry::SymAddr:
- P->d_un.d_ptr = E.Sym->getVA();
- break;
- case Entry::PlainInt:
- P->d_un.d_val = E.Val;
- break;
- }
+ for (std::pair<int32_t, std::function<uint64_t()>> &KV : Entries) {
+ P->d_tag = KV.first;
+ P->d_un.d_val = KV.second();
++P;
}
}
@@ -1218,21 +1198,57 @@ uint32_t DynamicReloc::getSymIndex() const {
return 0;
}
-template <class ELFT>
-RelocationSection<ELFT>::RelocationSection(StringRef Name, bool Sort)
- : SyntheticSection(SHF_ALLOC, Config->IsRela ? SHT_RELA : SHT_REL,
- Config->Wordsize, Name),
- Sort(Sort) {
- this->Entsize = Config->IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
-}
+RelocationBaseSection::RelocationBaseSection(StringRef Name, uint32_t Type,
+ int32_t DynamicTag,
+ int32_t SizeDynamicTag)
+ : SyntheticSection(SHF_ALLOC, Type, Config->Wordsize, Name),
+ DynamicTag(DynamicTag), SizeDynamicTag(SizeDynamicTag) {}
-template <class ELFT>
-void RelocationSection<ELFT>::addReloc(const DynamicReloc &Reloc) {
+void RelocationBaseSection::addReloc(const DynamicReloc &Reloc) {
if (Reloc.Type == Target->RelativeRel)
++NumRelativeRelocs;
Relocs.push_back(Reloc);
}
+void RelocationBaseSection::finalizeContents() {
+ // If all relocations are R_*_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.
+ Link = InX::DynSymTab ? InX::DynSymTab->getParent()->SectionIndex : 0;
+
+ // Set required output section properties.
+ getParent()->Link = Link;
+}
+
+template <class ELFT>
+static void encodeDynamicReloc(typename ELFT::Rela *P,
+ const DynamicReloc &Rel) {
+ if (Config->IsRela)
+ P->r_addend = Rel.getAddend();
+ P->r_offset = Rel.getOffset();
+ if (Config->EMachine == EM_MIPS && Rel.getInputSec() == InX::MipsGot)
+ // The MIPS GOT section contains dynamic relocations that correspond to TLS
+ // entries. These entries are placed after the global and local sections of
+ // the GOT. At the point when we create these relocations, the size of the
+ // global and local sections is unknown, so the offset that we store in the
+ // TLS entry's DynamicReloc is relative to the start of the TLS section of
+ // the GOT, rather than being relative to the start of the GOT. This line of
+ // code adds the size of the global and local sections to the virtual
+ // address computed by getOffset() in order to adjust it into the TLS
+ // section.
+ P->r_offset += InX::MipsGot->getTlsOffset();
+ P->setSymbolAndType(Rel.getSymIndex(), Rel.Type, Config->IsMips64EL);
+}
+
+template <class ELFT>
+RelocationSection<ELFT>::RelocationSection(StringRef Name, bool Sort)
+ : RelocationBaseSection(Name, Config->IsRela ? SHT_RELA : SHT_REL,
+ Config->IsRela ? DT_RELA : DT_REL,
+ Config->IsRela ? DT_RELASZ : DT_RELSZ),
+ Sort(Sort) {
+ this->Entsize = Config->IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
+}
+
template <class ELFT, class RelTy>
static bool compRelocations(const RelTy &A, const RelTy &B) {
bool AIsRel = A.getType(Config->IsMips64EL) == Target->RelativeRel;
@@ -1246,18 +1262,8 @@ static bool compRelocations(const RelTy &A, const RelTy &B) {
template <class ELFT> void RelocationSection<ELFT>::writeTo(uint8_t *Buf) {
uint8_t *BufBegin = Buf;
for (const DynamicReloc &Rel : Relocs) {
- auto *P = reinterpret_cast<Elf_Rela *>(Buf);
+ encodeDynamicReloc<ELFT>(reinterpret_cast<Elf_Rela *>(Buf), Rel);
Buf += Config->IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel);
-
- if (Config->IsRela)
- P->r_addend = Rel.getAddend();
- P->r_offset = Rel.getOffset();
- if (Config->EMachine == EM_MIPS && Rel.getInputSec() == InX::MipsGot)
- // Dynamic relocation against MIPS GOT section make deal TLS entries
- // allocated in the end of the GOT. We need to adjust the offset to take
- // in account 'local' and 'global' GOT entries.
- P->r_offset += InX::MipsGot->getTlsOffset();
- P->setSymbolAndType(Rel.getSymIndex(), Rel.Type, Config->IsMips64EL);
}
if (Sort) {
@@ -1275,12 +1281,192 @@ template <class ELFT> unsigned RelocationSection<ELFT>::getRelocOffset() {
return this->Entsize * Relocs.size();
}
-template <class ELFT> void RelocationSection<ELFT>::finalizeContents() {
- this->Link = InX::DynSymTab ? InX::DynSymTab->getParent()->SectionIndex
- : InX::SymTab->getParent()->SectionIndex;
+template <class ELFT>
+AndroidPackedRelocationSection<ELFT>::AndroidPackedRelocationSection(
+ StringRef Name)
+ : RelocationBaseSection(
+ Name, Config->IsRela ? SHT_ANDROID_RELA : SHT_ANDROID_REL,
+ Config->IsRela ? DT_ANDROID_RELA : DT_ANDROID_REL,
+ Config->IsRela ? DT_ANDROID_RELASZ : DT_ANDROID_RELSZ) {
+ this->Entsize = 1;
+}
- // Set required output section properties.
- getParent()->Link = this->Link;
+template <class ELFT>
+bool AndroidPackedRelocationSection<ELFT>::updateAllocSize() {
+ // This function computes the contents of an Android-format packed relocation
+ // section.
+ //
+ // This format compresses relocations by using relocation groups to factor out
+ // fields that are common between relocations and storing deltas from previous
+ // relocations in SLEB128 format (which has a short representation for small
+ // numbers). A good example of a relocation type with common fields is
+ // R_*_RELATIVE, which is normally used to represent function pointers in
+ // vtables. In the REL format, each relative relocation has the same r_info
+ // field, and is only different from other relative relocations in terms of
+ // the r_offset field. By sorting relocations by offset, grouping them by
+ // r_info and representing each relocation with only the delta from the
+ // previous offset, each 8-byte relocation can be compressed to as little as 1
+ // byte (or less with run-length encoding). This relocation packer was able to
+ // reduce the size of the relocation section in an Android Chromium DSO from
+ // 2,911,184 bytes to 174,693 bytes, or 6% of the original size.
+ //
+ // A relocation section consists of a header containing the literal bytes
+ // 'APS2' followed by a sequence of SLEB128-encoded integers. The first two
+ // elements are the total number of relocations in the section and an initial
+ // r_offset value. The remaining elements define a sequence of relocation
+ // groups. Each relocation group starts with a header consisting of the
+ // following elements:
+ //
+ // - the number of relocations in the relocation group
+ // - flags for the relocation group
+ // - (if RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG is set) the r_offset delta
+ // for each relocation in the group.
+ // - (if RELOCATION_GROUPED_BY_INFO_FLAG is set) the value of the r_info
+ // field for each relocation in the group.
+ // - (if RELOCATION_GROUP_HAS_ADDEND_FLAG and
+ // RELOCATION_GROUPED_BY_ADDEND_FLAG are set) the r_addend delta for
+ // each relocation in the group.
+ //
+ // Following the relocation group header are descriptions of each of the
+ // relocations in the group. They consist of the following elements:
+ //
+ // - (if RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG is not set) the r_offset
+ // delta for this relocation.
+ // - (if RELOCATION_GROUPED_BY_INFO_FLAG is not set) the value of the r_info
+ // field for this relocation.
+ // - (if RELOCATION_GROUP_HAS_ADDEND_FLAG is set and
+ // RELOCATION_GROUPED_BY_ADDEND_FLAG is not set) the r_addend delta for
+ // this relocation.
+
+ size_t OldSize = RelocData.size();
+
+ RelocData = {'A', 'P', 'S', '2'};
+ raw_svector_ostream OS(RelocData);
+ auto Add = [&](int64_t V) { encodeSLEB128(V, OS); };
+
+ // The format header includes the number of relocations and the initial
+ // offset (we set this to zero because the first relocation group will
+ // perform the initial adjustment).
+ Add(Relocs.size());
+ Add(0);
+
+ std::vector<Elf_Rela> Relatives, NonRelatives;
+
+ for (const DynamicReloc &Rel : Relocs) {
+ Elf_Rela R;
+ encodeDynamicReloc<ELFT>(&R, Rel);
+
+ if (R.getType(Config->IsMips64EL) == Target->RelativeRel)
+ Relatives.push_back(R);
+ else
+ NonRelatives.push_back(R);
+ }
+
+ std::sort(Relatives.begin(), Relatives.end(),
+ [](const Elf_Rel &A, const Elf_Rel &B) {
+ return A.r_offset < B.r_offset;
+ });
+
+ // Try to find groups of relative relocations which are spaced one word
+ // apart from one another. These generally correspond to vtable entries. The
+ // format allows these groups to be encoded using a sort of run-length
+ // encoding, but each group will cost 7 bytes in addition to the offset from
+ // the previous group, so it is only profitable to do this for groups of
+ // size 8 or larger.
+ std::vector<Elf_Rela> UngroupedRelatives;
+ std::vector<std::vector<Elf_Rela>> RelativeGroups;
+ for (auto I = Relatives.begin(), E = Relatives.end(); I != E;) {
+ std::vector<Elf_Rela> Group;
+ do {
+ Group.push_back(*I++);
+ } while (I != E && (I - 1)->r_offset + Config->Wordsize == I->r_offset);
+
+ if (Group.size() < 8)
+ UngroupedRelatives.insert(UngroupedRelatives.end(), Group.begin(),
+ Group.end());
+ else
+ RelativeGroups.emplace_back(std::move(Group));
+ }
+
+ unsigned HasAddendIfRela =
+ Config->IsRela ? RELOCATION_GROUP_HAS_ADDEND_FLAG : 0;
+
+ uint64_t Offset = 0;
+ uint64_t Addend = 0;
+
+ // Emit the run-length encoding for the groups of adjacent relative
+ // relocations. Each group is represented using two groups in the packed
+ // format. The first is used to set the current offset to the start of the
+ // group (and also encodes the first relocation), and the second encodes the
+ // remaining relocations.
+ for (std::vector<Elf_Rela> &G : RelativeGroups) {
+ // The first relocation in the group.
+ Add(1);
+ Add(RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG |
+ RELOCATION_GROUPED_BY_INFO_FLAG | HasAddendIfRela);
+ Add(G[0].r_offset - Offset);
+ Add(Target->RelativeRel);
+ if (Config->IsRela) {
+ Add(G[0].r_addend - Addend);
+ Addend = G[0].r_addend;
+ }
+
+ // The remaining relocations.
+ Add(G.size() - 1);
+ Add(RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG |
+ RELOCATION_GROUPED_BY_INFO_FLAG | HasAddendIfRela);
+ Add(Config->Wordsize);
+ Add(Target->RelativeRel);
+ if (Config->IsRela) {
+ for (auto I = G.begin() + 1, E = G.end(); I != E; ++I) {
+ Add(I->r_addend - Addend);
+ Addend = I->r_addend;
+ }
+ }
+
+ Offset = G.back().r_offset;
+ }
+
+ // Now the ungrouped relatives.
+ if (!UngroupedRelatives.empty()) {
+ Add(UngroupedRelatives.size());
+ Add(RELOCATION_GROUPED_BY_INFO_FLAG | HasAddendIfRela);
+ Add(Target->RelativeRel);
+ for (Elf_Rela &R : UngroupedRelatives) {
+ Add(R.r_offset - Offset);
+ Offset = R.r_offset;
+ if (Config->IsRela) {
+ Add(R.r_addend - Addend);
+ Addend = R.r_addend;
+ }
+ }
+ }
+
+ // Finally the non-relative relocations.
+ std::sort(NonRelatives.begin(), NonRelatives.end(),
+ [](const Elf_Rela &A, const Elf_Rela &B) {
+ return A.r_offset < B.r_offset;
+ });
+ if (!NonRelatives.empty()) {
+ Add(NonRelatives.size());
+ Add(HasAddendIfRela);
+ for (Elf_Rela &R : NonRelatives) {
+ Add(R.r_offset - Offset);
+ Offset = R.r_offset;
+ Add(R.r_info);
+ if (Config->IsRela) {
+ Add(R.r_addend - Addend);
+ Addend = R.r_addend;
+ }
+ }
+ }
+
+ // Returns whether the section size changed. We need to keep recomputing both
+ // section layout and the contents of this section until the size converges
+ // because changing this section's size can affect section layout, which in
+ // turn can affect the sizes of the LEB-encoded integers stored in this
+ // section.
+ return RelocData.size() != OldSize;
}
SymbolTableBaseSection::SymbolTableBaseSection(StringTableSection &StrTabSec)
@@ -1299,17 +1485,13 @@ static bool sortMipsSymbols(const SymbolTableEntry &L,
const SymbolTableEntry &R) {
// Sort entries related to non-local preemptible symbols by GOT indexes.
// All other entries go to the first part of GOT in arbitrary order.
- bool LIsInLocalGot = !L.Symbol->IsInGlobalMipsGot;
- bool RIsInLocalGot = !R.Symbol->IsInGlobalMipsGot;
+ bool LIsInLocalGot = !L.Sym->IsInGlobalMipsGot;
+ bool RIsInLocalGot = !R.Sym->IsInGlobalMipsGot;
if (LIsInLocalGot || RIsInLocalGot)
return !RIsInLocalGot;
- return L.Symbol->GotIndex < R.Symbol->GotIndex;
+ return L.Sym->GotIndex < R.Sym->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;
@@ -1328,26 +1510,27 @@ void SymbolTableBaseSection::finalizeContents() {
}
size_t I = 0;
- for (const SymbolTableEntry &S : Symbols)
- S.Symbol->DynsymIndex = ++I;
+ for (const SymbolTableEntry &S : Symbols) S.Sym->DynsymIndex = ++I;
return;
}
}
+// 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;
// move all local symbols before global symbols.
auto It = std::stable_partition(
Symbols.begin(), Symbols.end(), [](const SymbolTableEntry &S) {
- return S.Symbol->isLocal() ||
- S.Symbol->symbol()->computeBinding() == STB_LOCAL;
+ return S.Sym->isLocal() || S.Sym->computeBinding() == STB_LOCAL;
});
size_t NumLocals = It - Symbols.begin();
getParent()->Info = NumLocals + 1;
}
-void SymbolTableBaseSection::addSymbol(SymbolBody *B) {
+void SymbolTableBaseSection::addSymbol(Symbol *B) {
// Adding a local symbol to a .dynsym is a bug.
assert(this->Type != SHT_DYNSYM || !B->isLocal());
@@ -1355,19 +1538,25 @@ void SymbolTableBaseSection::addSymbol(SymbolBody *B) {
Symbols.push_back({B, StrTabSec.addString(B->getName(), HashIt)});
}
-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;
+size_t SymbolTableBaseSection::getSymbolIndex(Symbol *Sym) {
+ // 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.Sym->Type == STT_SECTION)
+ SectionIndexMap[E.Sym->getOutputSection()] = ++I;
+ else
+ SymbolIndexMap[E.Sym] = ++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 (Sym->Type == STT_SECTION)
+ return SectionIndexMap.lookup(Sym->getOutputSection());
+ return SymbolIndexMap.lookup(Sym);
}
template <class ELFT>
@@ -1379,46 +1568,56 @@ 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);
for (SymbolTableEntry &Ent : Symbols) {
- SymbolBody *Body = Ent.Symbol;
+ Symbol *Sym = Ent.Sym;
// Set st_info and st_other.
- if (Body->isLocal()) {
- ESym->setBindingAndType(STB_LOCAL, Body->Type);
+ ESym->st_other = 0;
+ if (Sym->isLocal()) {
+ ESym->setBindingAndType(STB_LOCAL, Sym->Type);
} else {
- ESym->setBindingAndType(Body->symbol()->computeBinding(), Body->Type);
- ESym->setVisibility(Body->symbol()->Visibility);
+ ESym->setBindingAndType(Sym->computeBinding(), Sym->Type);
+ ESym->setVisibility(Sym->Visibility);
}
ESym->st_name = Ent.StrTabOffset;
// Set a section index.
- if (const OutputSection *OutSec = Body->getOutputSection())
+ BssSection *CommonSec = nullptr;
+ if (!Config->DefineCommon)
+ if (auto *D = dyn_cast<Defined>(Sym))
+ CommonSec = dyn_cast_or_null<BssSection>(D->Section);
+ if (CommonSec)
+ ESym->st_shndx = SHN_COMMON;
+ else if (const OutputSection *OutSec = Sym->getOutputSection())
ESym->st_shndx = OutSec->SectionIndex;
- else if (isa<DefinedRegular>(Body))
+ else if (isa<Defined>(Sym))
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 = Body->getSize<ELFT>();
+ if (ESym->st_shndx == SHN_UNDEF)
+ ESym->st_size = 0;
+ else
+ ESym->st_size = Sym->getSize();
// st_value is usually an address of a symbol, but that has a
// special meaining for uninstantiated common symbols (this can
// occur if -r is given).
- if (!Config->DefineCommon && isa<DefinedCommon>(Body))
- ESym->st_value = cast<DefinedCommon>(Body)->Alignment;
+ if (CommonSec)
+ ESym->st_value = CommonSec->Alignment;
else
- ESym->st_value = Body->getVA();
+ ESym->st_value = Sym->getVA();
++ESym;
}
@@ -1431,13 +1630,22 @@ template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *Buf) {
auto *ESym = reinterpret_cast<Elf_Sym *>(Buf);
for (SymbolTableEntry &Ent : Symbols) {
- SymbolBody *Body = Ent.Symbol;
- if (Body->isInPlt() && Body->NeedsPltAddr)
+ Symbol *Sym = Ent.Sym;
+ if (Sym->isInPlt() && Sym->NeedsPltAddr)
ESym->st_other |= STO_MIPS_PLT;
-
+ if (isMicroMips()) {
+ // Set STO_MIPS_MICROMIPS flag and less-significant bit for
+ // defined microMIPS symbols and shared symbols with PLT record.
+ if ((Sym->isDefined() && (Sym->StOther & STO_MIPS_MICROMIPS)) ||
+ (Sym->isShared() && Sym->NeedsPltAddr)) {
+ if (StrTabSec.isDynamic())
+ ESym->st_value |= 1;
+ ESym->st_other |= STO_MIPS_MICROMIPS;
+ }
+ }
if (Config->Relocatable)
- if (auto *D = dyn_cast<DefinedRegular>(Body))
- if (D->isMipsPIC<ELFT>())
+ if (auto *D = dyn_cast<Defined>(Sym))
+ if (isMipsPIC<ELFT>(D))
ESym->st_other |= STO_MIPS_PIC;
++ESym;
}
@@ -1496,12 +1704,16 @@ void GnuHashTableSection::finalizeContents() {
}
void GnuHashTableSection::writeTo(uint8_t *Buf) {
+ // The output buffer is not guaranteed to be zero-cleared because we pre-
+ // fill executable sections with trap instructions. This is a precaution
+ // for that case, which happens only when -no-rosegment is given.
+ memset(Buf, 0, Size);
+
// Write a header.
- write32(Buf, NBuckets, Config->Endianness);
- write32(Buf + 4, InX::DynSymTab->getNumSymbols() - Symbols.size(),
- Config->Endianness);
- write32(Buf + 8, MaskWords, Config->Endianness);
- write32(Buf + 12, getShift2(), Config->Endianness);
+ write32(Buf, NBuckets);
+ write32(Buf + 4, InX::DynSymTab->getNumSymbols() - Symbols.size());
+ write32(Buf + 8, MaskWords);
+ write32(Buf + 12, getShift2());
Buf += 16;
// Write a bloom filter and a hash table.
@@ -1529,29 +1741,24 @@ void GnuHashTableSection::writeBloomFilter(uint8_t *Buf) {
}
void GnuHashTableSection::writeHashTable(uint8_t *Buf) {
- // Group symbols by hash value.
- std::vector<std::vector<Entry>> Syms(NBuckets);
- for (const Entry &Ent : Symbols)
- Syms[Ent.Hash % NBuckets].push_back(Ent);
-
- // Write hash buckets. Hash buckets contain indices in the following
- // hash value table.
uint32_t *Buckets = reinterpret_cast<uint32_t *>(Buf);
- for (size_t I = 0; I < NBuckets; ++I)
- if (!Syms[I].empty())
- write32(Buckets + I, Syms[I][0].Body->DynsymIndex, Config->Endianness);
-
- // Write a hash value table. It represents a sequence of chains that
- // share the same hash modulo value. The last element of each chain
- // is terminated by LSB 1.
+ uint32_t OldBucket = -1;
uint32_t *Values = Buckets + NBuckets;
- size_t I = 0;
- for (std::vector<Entry> &Vec : Syms) {
- if (Vec.empty())
+ for (auto I = Symbols.begin(), E = Symbols.end(); I != E; ++I) {
+ // Write a hash value. It represents a sequence of chains that share the
+ // same hash modulo value. The last element of each chain is terminated by
+ // LSB 1.
+ uint32_t Hash = I->Hash;
+ bool IsLastInChain = (I + 1) == E || I->BucketIdx != (I + 1)->BucketIdx;
+ Hash = IsLastInChain ? Hash | 1 : Hash & ~1;
+ write32(Values++, Hash);
+
+ if (I->BucketIdx == OldBucket)
continue;
- for (const Entry &Ent : makeArrayRef(Vec).drop_back())
- write32(Values + I++, Ent.Hash & ~1, Config->Endianness);
- write32(Values + I++, Vec.back().Hash | 1, Config->Endianness);
+ // Write a hash bucket. Hash buckets contain indices in the following hash
+ // value table.
+ write32(Buckets + I->BucketIdx, I->Sym->DynsymIndex);
+ OldBucket = I->BucketIdx;
}
}
@@ -1562,22 +1769,6 @@ static uint32_t hashGnu(StringRef Name) {
return H;
}
-// Returns a number of hash buckets to accomodate given number of elements.
-// We want to choose a moderate number that is not too small (which
-// causes too many hash collisions) and not too large (which wastes
-// disk space.)
-//
-// We return a prime number because it (is believed to) achieve good
-// hash distribution.
-static size_t getBucketSize(size_t NumSymbols) {
- // List of largest prime numbers that are not greater than 2^n + 1.
- for (size_t N : {131071, 65521, 32749, 16381, 8191, 4093, 2039, 1021, 509,
- 251, 127, 61, 31, 13, 7, 3, 1})
- if (N <= NumSymbols)
- return N;
- return 0;
-}
-
// Add symbols to this symbol hash table. Note that this function
// destructively sort a given vector -- which is needed because
// GNU-style hash table places some sorting requirements.
@@ -1586,66 +1777,70 @@ 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.Sym))
+ return SS->CopyRelSec == nullptr && !SS->NeedsPltAddr;
+ return !S.Sym->isDefined();
});
if (Mid == V.end())
return;
+ // We chose load factor 4 for the on-disk hash table. For each hash
+ // collision, the dynamic linker will compare a uint32_t hash value.
+ // Since the integer comparison is quite fast, we believe we can make
+ // the load factor even larger. 4 is just a conservative choice.
+ NBuckets = std::max<size_t>((V.end() - Mid) / 4, 1);
+
for (SymbolTableEntry &Ent : llvm::make_range(Mid, V.end())) {
- SymbolBody *B = Ent.Symbol;
- Symbols.push_back({B, Ent.StrTabOffset, hashGnu(B->getName())});
+ Symbol *B = Ent.Sym;
+ uint32_t Hash = hashGnu(B->getName());
+ uint32_t BucketIdx = Hash % NBuckets;
+ Symbols.push_back({B, Ent.StrTabOffset, Hash, BucketIdx});
}
- NBuckets = getBucketSize(Symbols.size());
- std::stable_sort(Symbols.begin(), Symbols.end(),
- [&](const Entry &L, const Entry &R) {
- return L.Hash % NBuckets < R.Hash % NBuckets;
- });
+ std::stable_sort(
+ Symbols.begin(), Symbols.end(),
+ [](const Entry &L, const Entry &R) { return L.BucketIdx < R.BucketIdx; });
V.erase(Mid, V.end());
for (const Entry &Ent : Symbols)
- V.push_back({Ent.Body, Ent.StrTabOffset});
+ V.push_back({Ent.Sym, 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.
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); // nbucket
+ write32(P++, NumSymbols); // 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;
- StringRef Name = Body->getName();
- unsigned I = Body->DynsymIndex;
+ Symbol *Sym = S.Sym;
+ StringRef Name = Sym->getName();
+ unsigned I = Sym->DynsymIndex;
uint32_t Hash = hashSysV(Name) % NumSymbols;
Chains[I] = Buckets[Hash];
- Buckets[Hash] = I;
+ write32(Buckets + Hash, I);
}
}
@@ -1668,7 +1863,7 @@ void PltSection::writeTo(uint8_t *Buf) {
unsigned PltOff = getPltRelocOff();
for (auto &I : Entries) {
- const SymbolBody *B = I.first;
+ const Symbol *B = I.first;
unsigned RelOff = I.second + PltOff;
uint64_t Got = B->getGotPltVA();
uint64_t Plt = this->getVA() + Off;
@@ -1677,14 +1872,15 @@ void PltSection::writeTo(uint8_t *Buf) {
}
}
-template <class ELFT> void PltSection::addEntry(SymbolBody &Sym) {
+template <class ELFT> void PltSection::addEntry(Symbol &Sym) {
Sym.PltIndex = Entries.size();
- RelocationSection<ELFT> *PltRelocSection = In<ELFT>::RelaPlt;
+ RelocationBaseSection *PltRelocSection = InX::RelaPlt;
if (HeaderSize == 0) {
- PltRelocSection = In<ELFT>::RelaIplt;
+ PltRelocSection = InX::RelaIplt;
Sym.IsInIplt = true;
}
- unsigned RelOff = PltRelocSection->getRelocOffset();
+ unsigned RelOff =
+ static_cast<RelocationSection<ELFT> *>(PltRelocSection)->getRelocOffset();
Entries.push_back(std::make_pair(&Sym, RelOff));
}
@@ -1709,35 +1905,29 @@ unsigned PltSection::getPltRelocOff() const {
return (HeaderSize == 0) ? InX::Plt->getSize() : 0;
}
-GdbIndexSection::GdbIndexSection(std::vector<GdbIndexChunk> &&Chunks)
- : SyntheticSection(0, SHT_PROGBITS, 1, ".gdb_index"),
- StringPool(llvm::StringTableBuilder::ELF), Chunks(std::move(Chunks)) {}
-
-// 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) {
- std::vector<CompilationUnitEntry> Ret;
- for (std::unique_ptr<DWARFCompileUnit> &CU : Dwarf.compile_units())
- Ret.push_back({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) {
@@ -1747,24 +1937,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;
}
@@ -1778,119 +1973,140 @@ static std::vector<InputSection *> getDebugInfoSections() {
return Ret;
}
-void GdbIndexSection::buildIndex() {
- if (Chunks.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();
+ }
+}
- 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);
+std::vector<std::vector<uint32_t>> GdbIndexSection::createCuVectors() {
+ std::vector<std::vector<uint32_t>> Ret;
+ uint32_t Idx = 0;
+ uint32_t Off = 0;
+
+ 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();
}
-}
-static GdbIndexChunk readDwarf(DWARFContextInMemory &Dwarf, InputSection *Sec) {
- GdbIndexChunk Ret;
- Ret.DebugInfoSec = Sec;
- Ret.CompilationUnits = readCuList(Dwarf);
- Ret.AddressArea = readAddressArea(Dwarf, Sec);
- Ret.NamesAndTypes = readPubNamesAndTypes(Dwarf, Config->IsLE);
+ StringPoolSize = Off;
return Ret;
}
template <class ELFT> GdbIndexSection *elf::createGdbIndex() {
- std::vector<GdbIndexChunk> Chunks;
- for (InputSection *Sec : getDebugInfoSections()) {
- InputFile *F = Sec->File;
- std::error_code EC;
- ELFObjectFile<ELFT> Obj(F->MB, EC);
- if (EC)
- fatal(EC.message());
- DWARFContextInMemory Dwarf(Obj, nullptr, [&](Error E) {
- error(toString(F) + ": error parsing DWARF data:\n>>> " +
- toString(std::move(E)));
- return ErrorPolicy::Continue;
- });
- Chunks.push_back(readDwarf(Dwarf, Sec));
- }
+ // 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));
+
+ Chunks[I].DebugInfoSec = Sections[I];
+ Chunks[I].CompilationUnits = readCuList(Dwarf);
+ Chunks[I].AddressAreas = readAddressAreas(Dwarf, Sections[I]);
+ Chunks[I].NamesAndTypes = readPubNamesAndTypes(Dwarf);
+ });
+
+ // .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) {
+ for (GdbIndexChunk::CuEntry &Cu : D.CompilationUnits) {
write64le(Buf, D.DebugInfoSec->OutSecOff + Cu.CuOffset);
write64le(Buf + 8, Cu.CuLength);
Buf += 16;
@@ -1899,7 +2115,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);
@@ -1910,43 +2126,47 @@ 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; }
-template <class ELFT>
-EhFrameHeader<ELFT>::EhFrameHeader()
+EhFrameHeader::EhFrameHeader()
: SyntheticSection(SHF_ALLOC, SHT_PROGBITS, 1, ".eh_frame_hdr") {}
// .eh_frame_hdr contains a binary search table of pointers to FDEs.
// Each entry of the search table consists of two values,
// the starting PC from where FDEs covers, and the FDE's address.
// It is sorted by PC.
-template <class ELFT> void EhFrameHeader<ELFT>::writeTo(uint8_t *Buf) {
- const endianness E = ELFT::TargetEndianness;
+void EhFrameHeader::writeTo(uint8_t *Buf) {
+ typedef EhFrameSection::FdeData FdeData;
+
+ std::vector<FdeData> Fdes = InX::EhFrame->getFdeData();
// Sort the FDE list by their PC and uniqueify. Usually there is only
// one FDE for a PC (i.e. function), but if ICF merges two functions
@@ -1960,31 +2180,24 @@ template <class ELFT> void EhFrameHeader<ELFT>::writeTo(uint8_t *Buf) {
Buf[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4;
Buf[2] = DW_EH_PE_udata4;
Buf[3] = DW_EH_PE_datarel | DW_EH_PE_sdata4;
- write32<E>(Buf + 4, In<ELFT>::EhFrame->getParent()->Addr - this->getVA() - 4);
- write32<E>(Buf + 8, Fdes.size());
+ write32(Buf + 4, InX::EhFrame->getParent()->Addr - this->getVA() - 4);
+ write32(Buf + 8, Fdes.size());
Buf += 12;
uint64_t VA = this->getVA();
for (FdeData &Fde : Fdes) {
- write32<E>(Buf, Fde.Pc - VA);
- write32<E>(Buf + 4, Fde.FdeVA - VA);
+ write32(Buf, Fde.Pc - VA);
+ write32(Buf + 4, Fde.FdeVA - VA);
Buf += 8;
}
}
-template <class ELFT> size_t EhFrameHeader<ELFT>::getSize() const {
+size_t EhFrameHeader::getSize() const {
// .eh_frame_hdr has a 12 bytes header followed by an array of FDEs.
- return 12 + In<ELFT>::EhFrame->NumFdes * 8;
-}
-
-template <class ELFT>
-void EhFrameHeader<ELFT>::addFde(uint32_t Pc, uint32_t FdeVA) {
- Fdes.push_back({Pc, FdeVA});
+ return 12 + InX::EhFrame->NumFdes * 8;
}
-template <class ELFT> bool EhFrameHeader<ELFT>::empty() const {
- return In<ELFT>::EhFrame->empty();
-}
+bool EhFrameHeader::empty() const { return InX::EhFrame->empty(); }
template <class ELFT>
VersionDefinitionSection<ELFT>::VersionDefinitionSection()
@@ -2065,7 +2278,7 @@ template <class ELFT> size_t VersionTableSection<ELFT>::getSize() const {
template <class ELFT> void VersionTableSection<ELFT>::writeTo(uint8_t *Buf) {
auto *OutVersym = reinterpret_cast<Elf_Versym *>(Buf) + 1;
for (const SymbolTableEntry &S : InX::DynSymTab->getSymbols()) {
- OutVersym->vs_index = S.Symbol->symbol()->VersionId;
+ OutVersym->vs_index = S.Sym->VersionId;
++OutVersym;
}
}
@@ -2086,14 +2299,13 @@ VersionNeedSection<ELFT>::VersionNeedSection()
template <class ELFT>
void VersionNeedSection<ELFT>::addSymbol(SharedSymbol *SS) {
- auto *Ver = reinterpret_cast<const typename ELFT::Verdef *>(SS->Verdef);
+ SharedFile<ELFT> *File = SS->getFile<ELFT>();
+ const typename ELFT::Verdef *Ver = File->Verdefs[SS->VerdefIndex];
if (!Ver) {
- SS->symbol()->VersionId = VER_NDX_GLOBAL;
+ SS->VersionId = VER_NDX_GLOBAL;
return;
}
- auto *File = cast<SharedFile<ELFT>>(SS->File);
-
// If we don't already know that we need an Elf_Verneed for this DSO, prepare
// to create one by adding it to our needed list and creating a dynstr entry
// for the soname.
@@ -2108,7 +2320,7 @@ void VersionNeedSection<ELFT>::addSymbol(SharedSymbol *SS) {
Ver->getAux()->vda_name);
NV.Index = NextIndex++;
}
- SS->symbol()->VersionId = NV.Index;
+ SS->VersionId = NV.Index;
}
template <class ELFT> void VersionNeedSection<ELFT>::writeTo(uint8_t *Buf) {
@@ -2162,23 +2374,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 MergeTailSection::writeTo(uint8_t *Buf) { Builder.write(Buf); }
-void MergeSyntheticSection::finalizeTailMerge() {
+void MergeTailSection::finalizeContents() {
// Add all string pieces to the string table builder to create section
// contents.
for (MergeInputSection *Sec : Sections)
@@ -2198,46 +2408,98 @@ 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;
+ size_t ShardId = getShardId(Sec->Pieces[I].Hash);
+ if ((ShardId & (Concurrency - 1)) == ThreadId)
+ Sec->Pieces[I].OutputOff = Shards[ShardId].add(Sec->getData(I));
+ }
+ }
+ });
+
+ // 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->Pieces[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) {
@@ -2250,20 +2512,28 @@ void elf::decompressAndMergeSections() {
if (!MS->Live)
continue;
- StringRef OutsecName = getOutputSectionName(MS->Name);
- uint64_t Flags = MS->Flags & ~(uint64_t)SHF_GROUP;
+ StringRef OutsecName = getOutputSectionName(MS);
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 &&
- Sec->Alignment == Alignment;
+ // While we could create a single synthetic section for two different
+ // values of Entsize, it is better to take Entsize into consideration.
+ //
+ // With a single synthetic section no two pieces with different Entsize
+ // could be equal, so we may as well have two sections.
+ //
+ // Using Entsize in here also allows us to propagate it to the synthetic
+ // section.
+ return Sec->Name == OutsecName && Sec->Flags == MS->Flags &&
+ Sec->Entsize == MS->Entsize && 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;
+ Syn->Entsize = MS->Entsize;
} else {
S = nullptr;
}
@@ -2293,20 +2563,27 @@ ARMExidxSentinelSection::ARMExidxSentinelSection()
void ARMExidxSentinelSection::writeTo(uint8_t *Buf) {
// The Sections are sorted in order of ascending PREL31 address with the
// 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);
- });
- auto L = cast<InputSectionDescription>(*ISD);
- InputSection *Highest = L->Sections[L->Sections.size() - 2];
+ // sentinel.
+ OutputSection *C = getParent();
+ InputSection *Highest = nullptr;
+ unsigned Skip = 1;
+ for (const BaseCommand *Base : llvm::reverse(C->SectionCommands)) {
+ if (!isa<InputSectionDescription>(Base))
+ continue;
+ auto L = cast<InputSectionDescription>(Base);
+ if (Skip >= L->Sections.size()) {
+ Skip -= L->Sections.size();
+ continue;
+ }
+ Highest = L->Sections[L->Sections.size() - Skip - 1];
+ break;
+ }
+ assert(Highest);
InputSection *LS = Highest->getLinkOrderDep();
uint64_t S = LS->getParent()->Addr + LS->getOffset(LS->getSize());
uint64_t P = getVA();
Target->relocateOne(Buf, R_ARM_PREL31, S - P);
- write32le(Buf + 4, 0x1);
+ write32le(Buf + 4, 1);
}
ThunkSection::ThunkSection(OutputSection *OS, uint64_t Off)
@@ -2330,6 +2607,8 @@ void ThunkSection::writeTo(uint8_t *Buf) {
}
InputSection *ThunkSection::getTargetInputSection() const {
+ if (Thunks.empty())
+ return nullptr;
const Thunk *T = Thunks.front();
return T->getTargetInputSection();
}
@@ -2338,7 +2617,8 @@ InputSection *InX::ARMAttributes;
BssSection *InX::Bss;
BssSection *InX::BssRelRo;
BuildIdSection *InX::BuildId;
-InputSection *InX::Common;
+EhFrameHeader *InX::EhFrameHdr;
+EhFrameSection *InX::EhFrame;
SyntheticSection *InX::Dynamic;
StringTableSection *InX::DynStrTab;
SymbolTableBaseSection *InX::DynSymTab;
@@ -2347,11 +2627,15 @@ GdbIndexSection *InX::GdbIndex;
GotSection *InX::Got;
GotPltSection *InX::GotPlt;
GnuHashTableSection *InX::GnuHashTab;
+HashTableSection *InX::HashTab;
IgotPltSection *InX::IgotPlt;
MipsGotSection *InX::MipsGot;
MipsRldMapSection *InX::MipsRldMap;
PltSection *InX::Plt;
PltSection *InX::Iplt;
+RelocationBaseSection *InX::RelaDyn;
+RelocationBaseSection *InX::RelaPlt;
+RelocationBaseSection *InX::RelaIplt;
StringTableSection *InX::ShStrTab;
StringTableSection *InX::StrTab;
SymbolTableBaseSection *InX::SymTab;
@@ -2361,15 +2645,15 @@ 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 void EhFrameSection::addSection<ELF32LE>(InputSectionBase *);
+template void EhFrameSection::addSection<ELF32BE>(InputSectionBase *);
+template void EhFrameSection::addSection<ELF64LE>(InputSectionBase *);
+template void EhFrameSection::addSection<ELF64BE>(InputSectionBase *);
-template InputSection *elf::createCommonSection<ELF32LE>();
-template InputSection *elf::createCommonSection<ELF32BE>();
-template InputSection *elf::createCommonSection<ELF64LE>();
-template InputSection *elf::createCommonSection<ELF64BE>();
+template void PltSection::addEntry<ELF32LE>(Symbol &Sym);
+template void PltSection::addEntry<ELF32BE>(Symbol &Sym);
+template void PltSection::addEntry<ELF64LE>(Symbol &Sym);
+template void PltSection::addEntry<ELF64BE>(Symbol &Sym);
template MergeInputSection *elf::createCommentSection<ELF32LE>();
template MergeInputSection *elf::createCommentSection<ELF32BE>();
@@ -2401,21 +2685,16 @@ template class elf::RelocationSection<ELF32BE>;
template class elf::RelocationSection<ELF64LE>;
template class elf::RelocationSection<ELF64BE>;
+template class elf::AndroidPackedRelocationSection<ELF32LE>;
+template class elf::AndroidPackedRelocationSection<ELF32BE>;
+template class elf::AndroidPackedRelocationSection<ELF64LE>;
+template class elf::AndroidPackedRelocationSection<ELF64BE>;
+
template class elf::SymbolTableSection<ELF32LE>;
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>;
-template class elf::EhFrameHeader<ELF64BE>;
-
template class elf::VersionTableSection<ELF32LE>;
template class elf::VersionTableSection<ELF32BE>;
template class elf::VersionTableSection<ELF64LE>;
@@ -2430,8 +2709,3 @@ template class elf::VersionDefinitionSection<ELF32LE>;
template class elf::VersionDefinitionSection<ELF32BE>;
template class elf::VersionDefinitionSection<ELF64LE>;
template class elf::VersionDefinitionSection<ELF64BE>;
-
-template class elf::EhFrameSection<ELF32LE>;
-template class elf::EhFrameSection<ELF32BE>;
-template class elf::EhFrameSection<ELF64LE>;
-template class elf::EhFrameSection<ELF64BE>;
diff --git a/ELF/SyntheticSections.h b/ELF/SyntheticSections.h
index ccf021ec9597..5aaf479f3e35 100644
--- a/ELF/SyntheticSections.h
+++ b/ELF/SyntheticSections.h
@@ -26,11 +26,11 @@
#include "InputSection.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/MC/StringTableBuilder.h"
-
-#include <set>
+#include <functional>
namespace lld {
namespace elf {
+class SharedSymbol;
class SyntheticSection : public InputSection {
public:
@@ -47,7 +47,7 @@ public:
virtual void finalizeContents() {}
// If the section has the SHF_ALLOC flag and the size may be changed if
// thunks are added, update the section size.
- virtual void updateAllocSize() {}
+ virtual bool updateAllocSize() { return false; }
// If any additional finalization of contents are needed post thunk creation.
virtual void postThunkContents() {}
virtual bool empty() const { return false; }
@@ -59,21 +59,12 @@ public:
};
struct CieRecord {
- EhSectionPiece *Piece = nullptr;
- std::vector<EhSectionPiece *> FdePieces;
+ EhSectionPiece *Cie = nullptr;
+ std::vector<EhSectionPiece *> Fdes;
};
// Section for .eh_frame.
-template <class ELFT> class EhFrameSection final : public SyntheticSection {
- typedef typename ELFT::Shdr Elf_Shdr;
- typedef typename ELFT::Rel Elf_Rel;
- typedef typename ELFT::Rela Elf_Rela;
-
- void updateAlignment(uint64_t Val) {
- if (Val > this->Alignment)
- this->Alignment = Val;
- }
-
+class EhFrameSection final : public SyntheticSection {
public:
EhFrameSection();
void writeTo(uint8_t *Buf) override;
@@ -81,30 +72,36 @@ public:
bool empty() const override { return Sections.empty(); }
size_t getSize() const override { return Size; }
- void addSection(InputSectionBase *S);
+ template <class ELFT> void addSection(InputSectionBase *S);
+ std::vector<EhInputSection *> Sections;
size_t NumFdes = 0;
- std::vector<EhInputSection *> Sections;
+ struct FdeData {
+ uint32_t Pc;
+ uint32_t FdeVA;
+ };
+
+ std::vector<FdeData> getFdeData() const;
private:
uint64_t Size = 0;
- template <class RelTy>
+
+ template <class ELFT, class RelTy>
void addSectionAux(EhInputSection *S, llvm::ArrayRef<RelTy> Rels);
- template <class RelTy>
+ template <class ELFT, class RelTy>
CieRecord *addCie(EhSectionPiece &Piece, ArrayRef<RelTy> Rels);
- template <class RelTy>
+ template <class ELFT, class RelTy>
bool isFdeLive(EhSectionPiece &Piece, ArrayRef<RelTy> Rels);
- uint64_t getFdePc(uint8_t *Buf, size_t Off, uint8_t Enc);
+ uint64_t getFdePc(uint8_t *Buf, size_t Off, uint8_t Enc) const;
- 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>, Symbol *>, CieRecord *> CieMap;
};
class GotSection : public SyntheticSection {
@@ -115,11 +112,11 @@ public:
bool empty() const override;
void writeTo(uint8_t *Buf) override;
- void addEntry(SymbolBody &Sym);
- bool addDynTlsEntry(SymbolBody &Sym);
+ void addEntry(Symbol &Sym);
+ bool addDynTlsEntry(Symbol &Sym);
bool addTlsIndex();
- uint64_t getGlobalDynAddr(const SymbolBody &B) const;
- uint64_t getGlobalDynOffset(const SymbolBody &B) const;
+ uint64_t getGlobalDynAddr(const Symbol &B) const;
+ uint64_t getGlobalDynOffset(const Symbol &B) const;
uint64_t getTlsIndexVA() { return this->getVA() + TlsIndexOff; }
uint32_t getTlsIndexOff() const { return TlsIndexOff; }
@@ -159,14 +156,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;
+ static bool classof(const SectionBase *S) { return S->Bss; }
+ uint64_t Size;
};
class MipsGotSection final : public SyntheticSection {
@@ -174,21 +170,21 @@ public:
MipsGotSection();
void writeTo(uint8_t *Buf) override;
size_t getSize() const override { return Size; }
- void updateAllocSize() override;
+ bool updateAllocSize() override;
void finalizeContents() override;
bool empty() const override;
- void addEntry(SymbolBody &Sym, int64_t Addend, RelExpr Expr);
- bool addDynTlsEntry(SymbolBody &Sym);
+ void addEntry(Symbol &Sym, int64_t Addend, RelExpr Expr);
+ bool addDynTlsEntry(Symbol &Sym);
bool addTlsIndex();
- uint64_t getPageEntryOffset(const SymbolBody &B, int64_t Addend) const;
- uint64_t getBodyEntryOffset(const SymbolBody &B, int64_t Addend) const;
- uint64_t getGlobalDynOffset(const SymbolBody &B) const;
+ uint64_t getPageEntryOffset(const Symbol &B, int64_t Addend) const;
+ uint64_t getSymEntryOffset(const Symbol &B, int64_t Addend) const;
+ uint64_t getGlobalDynOffset(const Symbol &B) const;
// Returns the symbol which corresponds to the first entry of the global part
// of GOT on MIPS platform. It is required to fill up MIPS-specific dynamic
// table properties.
// Returns nullptr if the global part is empty.
- const SymbolBody *getFirstGlobalEntry() const;
+ const Symbol *getFirstGlobalEntry() const;
// Returns the number of entries in the local part of GOT including
// the number of reserved entries.
@@ -248,7 +244,7 @@ private:
// to the first index of "Page" entries allocated for this section.
llvm::SmallMapVector<const OutputSection *, size_t, 16> PageIndexMap;
- typedef std::pair<const SymbolBody *, uint64_t> GotEntry;
+ typedef std::pair<const Symbol *, uint64_t> GotEntry;
typedef std::vector<GotEntry> GotEntries;
// Map from Symbol-Addend pair to the GOT index.
llvm::DenseMap<GotEntry, size_t> EntryIndexMap;
@@ -261,7 +257,7 @@ private:
GotEntries GlobalEntries;
// TLS entries.
- std::vector<const SymbolBody *> TlsEntries;
+ std::vector<const Symbol *> TlsEntries;
uint32_t TlsIndexOff = -1;
uint64_t Size = 0;
@@ -270,13 +266,13 @@ private:
class GotPltSection final : public SyntheticSection {
public:
GotPltSection();
- void addEntry(SymbolBody &Sym);
+ void addEntry(Symbol &Sym);
size_t getSize() const override;
void writeTo(uint8_t *Buf) override;
bool empty() const override { return Entries.empty(); }
private:
- std::vector<const SymbolBody *> Entries;
+ std::vector<const Symbol *> Entries;
};
// The IgotPltSection is a Got associated with the PltSection for GNU Ifunc
@@ -286,13 +282,13 @@ private:
class IgotPltSection final : public SyntheticSection {
public:
IgotPltSection();
- void addEntry(SymbolBody &Sym);
+ void addEntry(Symbol &Sym);
size_t getSize() const override;
void writeTo(uint8_t *Buf) override;
bool empty() const override { return Entries.empty(); }
private:
- std::vector<const SymbolBody *> Entries;
+ std::vector<const Symbol *> Entries;
};
class StringTableSection final : public SyntheticSection {
@@ -315,8 +311,7 @@ private:
class DynamicReloc {
public:
DynamicReloc(uint32_t Type, const InputSectionBase *InputSec,
- uint64_t OffsetInSec, bool UseSymVA, SymbolBody *Sym,
- int64_t Addend)
+ uint64_t OffsetInSec, bool UseSymVA, Symbol *Sym, int64_t Addend)
: Type(Type), Sym(Sym), InputSec(InputSec), OffsetInSec(OffsetInSec),
UseSymVA(UseSymVA), Addend(Addend) {}
@@ -328,7 +323,7 @@ public:
uint32_t Type;
private:
- SymbolBody *Sym;
+ Symbol *Sym;
const InputSectionBase *InputSec = nullptr;
uint64_t OffsetInSec;
bool UseSymVA;
@@ -342,30 +337,8 @@ template <class ELFT> class DynamicSection final : public SyntheticSection {
typedef typename ELFT::Shdr Elf_Shdr;
typedef typename ELFT::Sym Elf_Sym;
- // The .dynamic section contains information for the dynamic linker.
- // The section consists of fixed size entries, which consist of
- // type and value fields. Value are one of plain integers, symbol
- // addresses, or section addresses. This struct represents the entry.
- struct Entry {
- int32_t Tag;
- union {
- OutputSection *OutSec;
- InputSection *InSec;
- uint64_t Val;
- const SymbolBody *Sym;
- };
- enum KindT { SecAddr, SecSize, SymAddr, PlainInt, InSecAddr } Kind;
- Entry(int32_t Tag, OutputSection *OutSec, KindT Kind = SecAddr)
- : Tag(Tag), OutSec(OutSec), Kind(Kind) {}
- Entry(int32_t Tag, InputSection *Sec)
- : Tag(Tag), InSec(Sec), Kind(InSecAddr) {}
- Entry(int32_t Tag, uint64_t Val) : Tag(Tag), Val(Val), Kind(PlainInt) {}
- Entry(int32_t Tag, const SymbolBody *Sym)
- : Tag(Tag), Sym(Sym), Kind(SymAddr) {}
- };
-
// finalizeContents() fills this vector with the section contents.
- std::vector<Entry> Entries;
+ std::vector<std::pair<int32_t, std::function<uint64_t()>>> Entries;
public:
DynamicSection();
@@ -374,33 +347,66 @@ public:
size_t getSize() const override { return Size; }
private:
- void addEntries();
- void add(Entry E) { Entries.push_back(E); }
+ void add(int32_t Tag, std::function<uint64_t()> Fn);
+ void addInt(int32_t Tag, uint64_t Val);
+ void addInSec(int32_t Tag, InputSection *Sec);
+ void addOutSec(int32_t Tag, OutputSection *Sec);
+ void addSize(int32_t Tag, OutputSection *Sec);
+ void addSym(int32_t Tag, Symbol *Sym);
+
uint64_t Size = 0;
};
-template <class ELFT> class RelocationSection final : public SyntheticSection {
+class RelocationBaseSection : public SyntheticSection {
+public:
+ RelocationBaseSection(StringRef Name, uint32_t Type, int32_t DynamicTag,
+ int32_t SizeDynamicTag);
+ void addReloc(const DynamicReloc &Reloc);
+ bool empty() const override { return Relocs.empty(); }
+ size_t getSize() const override { return Relocs.size() * this->Entsize; }
+ size_t getRelativeRelocCount() const { return NumRelativeRelocs; }
+ void finalizeContents() override;
+ int32_t DynamicTag, SizeDynamicTag;
+
+protected:
+ std::vector<DynamicReloc> Relocs;
+ size_t NumRelativeRelocs = 0;
+};
+
+template <class ELFT>
+class RelocationSection final : public RelocationBaseSection {
typedef typename ELFT::Rel Elf_Rel;
typedef typename ELFT::Rela Elf_Rela;
public:
RelocationSection(StringRef Name, bool Sort);
- void addReloc(const DynamicReloc &Reloc);
unsigned getRelocOffset();
- void finalizeContents() override;
void writeTo(uint8_t *Buf) override;
- bool empty() const override { return Relocs.empty(); }
- size_t getSize() const override { return Relocs.size() * this->Entsize; }
- size_t getRelativeRelocCount() const { return NumRelativeRelocs; }
private:
bool Sort;
- size_t NumRelativeRelocs = 0;
- std::vector<DynamicReloc> Relocs;
+};
+
+template <class ELFT>
+class AndroidPackedRelocationSection final : public RelocationBaseSection {
+ typedef typename ELFT::Rel Elf_Rel;
+ typedef typename ELFT::Rela Elf_Rela;
+
+public:
+ AndroidPackedRelocationSection(StringRef Name);
+
+ bool updateAllocSize() override;
+ size_t getSize() const override { return RelocData.size(); }
+ void writeTo(uint8_t *Buf) override {
+ memcpy(Buf, RelocData.data(), RelocData.size());
+ }
+
+private:
+ SmallVector<char, 0> RelocData;
};
struct SymbolTableEntry {
- SymbolBody *Symbol;
+ Symbol *Sym;
size_t StrTabOffset;
};
@@ -410,9 +416,9 @@ public:
void finalizeContents() override;
void postThunkContents() override;
size_t getSize() const override { return getNumSymbols() * Entsize; }
- void addSymbol(SymbolBody *Body);
+ void addSymbol(Symbol *Sym);
unsigned getNumSymbols() const { return Symbols.size() + 1; }
- size_t getSymbolIndex(SymbolBody *Body);
+ size_t getSymbolIndex(Symbol *Sym);
ArrayRef<SymbolTableEntry> getSymbols() const { return Symbols; }
protected:
@@ -420,6 +426,10 @@ protected:
std::vector<SymbolTableEntry> Symbols;
StringTableSection &StrTabSec;
+
+ llvm::once_flag OnceFlag;
+ llvm::DenseMap<Symbol *, size_t> SymbolIndexMap;
+ llvm::DenseMap<OutputSection *, size_t> SectionIndexMap;
};
template <class ELFT>
@@ -451,9 +461,10 @@ private:
void writeHashTable(uint8_t *Buf);
struct Entry {
- SymbolBody *Body;
+ Symbol *Sym;
size_t StrTabOffset;
uint32_t Hash;
+ uint32_t BucketIdx;
};
std::vector<Entry> Symbols;
@@ -462,7 +473,7 @@ private:
size_t Size = 0;
};
-template <class ELFT> class HashTableSection final : public SyntheticSection {
+class HashTableSection final : public SyntheticSection {
public:
HashTableSection();
void finalizeContents() override;
@@ -485,57 +496,81 @@ public:
bool empty() const override { return Entries.empty(); }
void addSymbols();
- template <class ELFT> void addEntry(SymbolBody &Sym);
+ template <class ELFT> void addEntry(Symbol &Sym);
private:
- void writeHeader(uint8_t *Buf){};
- void addHeaderSymbols(){};
unsigned getPltRelocOff() const;
- std::vector<std::pair<const SymbolBody *, unsigned>> Entries;
+ std::vector<std::pair<const Symbol *, 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(std::vector<GdbIndexChunk> &&Chunks);
- void finalizeContents() override;
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:
- 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();
@@ -549,21 +584,12 @@ template <class ELFT> GdbIndexSection *createGdbIndex();
// Detailed info about internals can be found in Ian Lance Taylor's blog:
// http://www.airs.com/blog/archives/460 (".eh_frame")
// http://www.airs.com/blog/archives/462 (".eh_frame_hdr")
-template <class ELFT> class EhFrameHeader final : public SyntheticSection {
+class EhFrameHeader final : public SyntheticSection {
public:
EhFrameHeader();
void writeTo(uint8_t *Buf) override;
size_t getSize() const override;
- void addFde(uint32_t Pc, uint32_t FdeVA);
bool empty() const override;
-
-private:
- struct FdeData {
- uint32_t Pc;
- uint32_t FdeVA;
- };
-
- std::vector<FdeData> Fdes;
};
// For more information about .gnu.version and .gnu.version_r see:
@@ -639,22 +665,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.
@@ -746,13 +808,13 @@ private:
size_t Size = 0;
};
-template <class ELFT> InputSection *createCommonSection();
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);
+Symbol *addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value,
+ uint64_t Size, InputSectionBase *Section);
// Linker generated sections which can be used as inputs.
struct InX {
@@ -760,11 +822,13 @@ struct InX {
static BssSection *Bss;
static BssSection *BssRelRo;
static BuildIdSection *BuildId;
- static InputSection *Common;
+ static EhFrameHeader *EhFrameHdr;
+ static EhFrameSection *EhFrame;
static SyntheticSection *Dynamic;
static StringTableSection *DynStrTab;
static SymbolTableBaseSection *DynSymTab;
static GnuHashTableSection *GnuHashTab;
+ static HashTableSection *HashTab;
static InputSection *Interp;
static GdbIndexSection *GdbIndex;
static GotSection *Got;
@@ -774,29 +838,20 @@ struct InX {
static MipsRldMapSection *MipsRldMap;
static PltSection *Plt;
static PltSection *Iplt;
+ static RelocationBaseSection *RelaDyn;
+ static RelocationBaseSection *RelaPlt;
+ static RelocationBaseSection *RelaIplt;
static StringTableSection *ShStrTab;
static StringTableSection *StrTab;
static SymbolTableBaseSection *SymTab;
};
-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;
+template <class ELFT> struct In {
static VersionDefinitionSection<ELFT> *VerDef;
static VersionTableSection<ELFT> *VerSym;
static VersionNeedSection<ELFT> *VerNeed;
};
-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;
template <class ELFT> VersionDefinitionSection<ELFT> *In<ELFT>::VerDef;
template <class ELFT> VersionTableSection<ELFT> *In<ELFT>::VerSym;
template <class ELFT> VersionNeedSection<ELFT> *In<ELFT>::VerNeed;
diff --git a/ELF/Target.cpp b/ELF/Target.cpp
index 11986efc746f..ddd408906d14 100644
--- a/ELF/Target.cpp
+++ b/ELF/Target.cpp
@@ -25,11 +25,11 @@
//===----------------------------------------------------------------------===//
#include "Target.h"
-#include "Error.h"
#include "InputFiles.h"
#include "OutputSections.h"
#include "SymbolTable.h"
#include "Symbols.h"
+#include "lld/Common/ErrorHandler.h"
#include "llvm/Object/ELF.h"
using namespace llvm;
@@ -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();
@@ -117,27 +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,
+ uint64_t BranchAddr, const Symbol &S) const {
return false;
}
-bool TargetInfo::inBranchRange(uint32_t RelocType, uint64_t Src,
- uint64_t Dst) const {
+bool TargetInfo::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
return true;
}
-void TargetInfo::writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const {
+void TargetInfo::writeIgotPlt(uint8_t *Buf, const Symbol &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;
}
@@ -146,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 1658a81c9b71..2902dbc99149 100644
--- a/ELF/Target.h
+++ b/ELF/Target.h
@@ -10,25 +10,27 @@
#ifndef LLD_ELF_TARGET_H
#define LLD_ELF_TARGET_H
-#include "Error.h"
#include "InputSection.h"
+#include "lld/Common/ErrorHandler.h"
#include "llvm/Object/ELF.h"
namespace lld {
-std::string toString(uint32_t RelType);
+std::string toString(elf::RelType Type);
namespace elf {
+class Defined;
class InputFile;
-class SymbolBody;
+class Symbol;
class TargetInfo {
public:
- virtual bool isPicRel(uint32_t Type) const { return true; }
- virtual uint32_t getDynRel(uint32_t Type) const { return Type; }
+ virtual uint32_t calcEFlags() const { return 0; }
+ 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 void writeGotPlt(uint8_t *Buf, const Symbol &S) const {};
+ virtual void writeIgotPlt(uint8_t *Buf, const Symbol &S) 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,48 +42,52 @@ 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 bool needsThunk(RelExpr Expr, RelType RelocType,
+ const InputFile *File, uint64_t BranchAddr,
+ const Symbol &S) const;
// Return true if we can reach Dst from Src with Relocation RelocType
- virtual bool inBranchRange(uint32_t RelocType, uint64_t Src,
+ virtual bool inBranchRange(RelType Type, uint64_t Src,
uint64_t Dst) const;
- virtual RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
+ virtual RelExpr getRelExpr(RelType Type, const Symbol &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;
+ uint64_t getImageBase();
// Offset of _GLOBAL_OFFSET_TABLE_ from base of .got section. Use -1 for
// end of .got
uint64_t GotBaseSymOff = 0;
- 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;
+ // On systems with range extensions we place collections of Thunks at
+ // regular spacings that enable the majority of branches reach the Thunks.
+ uint32_t ThunkSectionSpacing = 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;
@@ -100,13 +106,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();
@@ -129,32 +142,42 @@ uint64_t getAArch64Page(uint64_t Expr);
extern TargetInfo *Target;
TargetInfo *getTarget();
+template <class ELFT> bool isMipsPIC(const Defined *Sym);
+
+static inline void reportRangeError(uint8_t *Loc, RelType Type, const Twine &V,
+ int64_t Min, uint64_t Max) {
+ error(getErrorLocation(Loc) + "relocation " + lld::toString(Type) +
+ " out of range: " + V + " is not in [" + Twine(Min) + ", " +
+ Twine(Max) + "]");
+}
+
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");
+ reportRangeError(Loc, Type, Twine(V), llvm::minIntN(N), llvm::maxIntN(N));
}
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");
+ reportRangeError(Loc, Type, Twine(V), 0, llvm::maxUIntN(N));
}
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");
+ // For the error message we should cast V to a signed integer so that error
+ // messages show a small negative value rather than an extremely large one
+ reportRangeError(Loc, Type, Twine((int64_t)V), llvm::minIntN(N),
+ llvm::maxUIntN(N));
}
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));
+ lld::toString(Type) + ": 0x" + llvm::utohexstr(V) +
+ " is not aligned to " + Twine(N) + " bytes");
}
} // namespace elf
} // namespace lld
diff --git a/ELF/Thunks.cpp b/ELF/Thunks.cpp
index 07289d0efdf1..91ca3b9b5bc2 100644
--- a/ELF/Thunks.cpp
+++ b/ELF/Thunks.cpp
@@ -23,13 +23,13 @@
#include "Thunks.h"
#include "Config.h"
-#include "Error.h"
#include "InputSection.h"
-#include "Memory.h"
#include "OutputSections.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Endian.h"
@@ -48,52 +48,69 @@ namespace elf {
namespace {
+// AArch64 long range Thunks
+class AArch64ABSLongThunk final : public Thunk {
+public:
+ AArch64ABSLongThunk(Symbol &Dest) : Thunk(Dest) {}
+ uint32_t size() const override { return 16; }
+ void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
+ void addSymbols(ThunkSection &IS) override;
+};
+
+class AArch64ADRPThunk final : public Thunk {
+public:
+ AArch64ADRPThunk(Symbol &Dest) : Thunk(Dest) {}
+ uint32_t size() const override { return 12; }
+ void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
+ void addSymbols(ThunkSection &IS) override;
+};
+
// Specific ARM Thunk implementations. The naming convention is:
// Source State, TargetState, Target Requirement, ABS or PI, Range
class ARMV7ABSLongThunk final : public Thunk {
public:
- ARMV7ABSLongThunk(const SymbolBody &Dest) : Thunk(Dest) {}
+ ARMV7ABSLongThunk(Symbol &Dest) : Thunk(Dest) {}
uint32_t size() const override { return 12; }
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
void addSymbols(ThunkSection &IS) override;
- bool isCompatibleWith(uint32_t RelocType) const override;
+ bool isCompatibleWith(RelType Type) const override;
};
class ARMV7PILongThunk final : public Thunk {
public:
- ARMV7PILongThunk(const SymbolBody &Dest) : Thunk(Dest) {}
+ ARMV7PILongThunk(Symbol &Dest) : Thunk(Dest) {}
uint32_t size() const override { return 16; }
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
void addSymbols(ThunkSection &IS) override;
- bool isCompatibleWith(uint32_t RelocType) const override;
+ bool isCompatibleWith(RelType Type) const override;
};
class ThumbV7ABSLongThunk final : public Thunk {
public:
- ThumbV7ABSLongThunk(const SymbolBody &Dest) : Thunk(Dest) { Alignment = 2; }
+ ThumbV7ABSLongThunk(Symbol &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(uint32_t RelocType) const override;
+ bool isCompatibleWith(RelType Type) const override;
};
class ThumbV7PILongThunk final : public Thunk {
public:
- ThumbV7PILongThunk(const SymbolBody &Dest) : Thunk(Dest) { Alignment = 2; }
+ ThumbV7PILongThunk(Symbol &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(uint32_t RelocType) const override;
+ bool isCompatibleWith(RelType Type) const override;
};
// MIPS LA25 thunk
class MipsThunk final : public Thunk {
public:
- MipsThunk(const SymbolBody &Dest) : Thunk(Dest) {}
+ MipsThunk(Symbol &Dest) : Thunk(Dest) {}
uint32_t size() const override { return 16; }
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
@@ -101,10 +118,86 @@ public:
InputSection *getTargetInputSection() const override;
};
+// microMIPS R2-R5 LA25 thunk
+class MicroMipsThunk final : public Thunk {
+public:
+ MicroMipsThunk(Symbol &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(Symbol &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
+// AArch64 long range Thunks
+
+static uint64_t getAArch64ThunkDestVA(const Symbol &S) {
+ uint64_t V = S.isInPlt() ? S.getPltVA() : S.getVA();
+ return V;
+}
+
+void AArch64ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
+ const uint8_t Data[] = {
+ 0x50, 0x00, 0x00, 0x58, // ldr x16, L0
+ 0x00, 0x02, 0x1f, 0xd6, // br x16
+ 0x00, 0x00, 0x00, 0x00, // L0: .xword S
+ 0x00, 0x00, 0x00, 0x00,
+ };
+ uint64_t S = getAArch64ThunkDestVA(Destination);
+ memcpy(Buf, Data, sizeof(Data));
+ Target->relocateOne(Buf + 8, R_AARCH64_ABS64, S);
+}
+
+void AArch64ABSLongThunk::addSymbols(ThunkSection &IS) {
+ ThunkSym = addSyntheticLocal(
+ Saver.save("__AArch64AbsLongThunk_" + Destination.getName()), STT_FUNC,
+ Offset, size(), &IS);
+ addSyntheticLocal("$x", STT_NOTYPE, Offset, 0, &IS);
+ addSyntheticLocal("$d", STT_NOTYPE, Offset + 8, 0, &IS);
+}
+
+// This Thunk has a maximum range of 4Gb, this is sufficient for all programs
+// using the small code model, including pc-relative ones. At time of writing
+// clang and gcc do not support the large code model for position independent
+// code so it is safe to use this for position independent thunks without
+// worrying about the destination being more than 4Gb away.
+void AArch64ADRPThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
+ const uint8_t Data[] = {
+ 0x10, 0x00, 0x00, 0x90, // adrp x16, Dest R_AARCH64_ADR_PREL_PG_HI21(Dest)
+ 0x10, 0x02, 0x00, 0x91, // add x16, x16, R_AARCH64_ADD_ABS_LO12_NC(Dest)
+ 0x00, 0x02, 0x1f, 0xd6, // br x16
+ };
+ uint64_t S = getAArch64ThunkDestVA(Destination);
+ uint64_t P = ThunkSym->getVA();
+ memcpy(Buf, Data, sizeof(Data));
+ Target->relocateOne(Buf, R_AARCH64_ADR_PREL_PG_HI21,
+ getAArch64Page(S) - getAArch64Page(P));
+ Target->relocateOne(Buf + 4, R_AARCH64_ADD_ABS_LO12_NC, S);
+}
+
+void AArch64ADRPThunk::addSymbols(ThunkSection &IS)
+{
+ ThunkSym = addSyntheticLocal(
+ Saver.save("__AArch64ADRPThunk_" + Destination.getName()), STT_FUNC,
+ Offset, size(), &IS);
+ addSyntheticLocal("$x", STT_NOTYPE, Offset, 0, &IS);
+}
+
// ARM Target Thunks
-static uint64_t getARMThunkDestVA(const SymbolBody &S) {
+static uint64_t getARMThunkDestVA(const Symbol &S) {
uint64_t V = S.isInPlt() ? S.getPltVA() : S.getVA();
return SignExtend64<32>(V);
}
@@ -128,9 +221,9 @@ void ARMV7ABSLongThunk::addSymbols(ThunkSection &IS) {
addSyntheticLocal("$a", STT_NOTYPE, Offset, 0, &IS);
}
-bool ARMV7ABSLongThunk::isCompatibleWith(uint32_t RelocType) const {
+bool ARMV7ABSLongThunk::isCompatibleWith(RelType Type) const {
// Thumb branch relocations can't use BLX
- return RelocType != R_ARM_THM_JUMP19 && RelocType != R_ARM_THM_JUMP24;
+ return Type != R_ARM_THM_JUMP19 && Type != R_ARM_THM_JUMP24;
}
void ThumbV7ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
@@ -152,24 +245,24 @@ void ThumbV7ABSLongThunk::addSymbols(ThunkSection &IS) {
addSyntheticLocal("$t", STT_NOTYPE, Offset, 0, &IS);
}
-bool ThumbV7ABSLongThunk::isCompatibleWith(uint32_t RelocType) const {
+bool ThumbV7ABSLongThunk::isCompatibleWith(RelType Type) const {
// ARM branch relocations can't use BLX
- return RelocType != R_ARM_JUMP24 && RelocType != R_ARM_PC24 &&
- RelocType != R_ARM_PLT32;
+ 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) {
@@ -179,23 +272,24 @@ void ARMV7PILongThunk::addSymbols(ThunkSection &IS) {
addSyntheticLocal("$a", STT_NOTYPE, Offset, 0, &IS);
}
-bool ARMV7PILongThunk::isCompatibleWith(uint32_t RelocType) const {
+bool ARMV7PILongThunk::isCompatibleWith(RelType Type) const {
// Thumb branch relocations can't use BLX
- return RelocType != R_ARM_THM_JUMP19 && RelocType != R_ARM_THM_JUMP24;
+ 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) {
@@ -205,10 +299,9 @@ void ThumbV7PILongThunk::addSymbols(ThunkSection &IS) {
addSyntheticLocal("$t", STT_NOTYPE, Offset, 0, &IS);
}
-bool ThumbV7PILongThunk::isCompatibleWith(uint32_t RelocType) const {
+bool ThumbV7PILongThunk::isCompatibleWith(RelType Type) const {
// ARM branch relocations can't use BLX
- return RelocType != R_ARM_JUMP24 && RelocType != R_ARM_PC24 &&
- RelocType != R_ARM_PLT32;
+ 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.
@@ -229,16 +322,74 @@ void MipsThunk::addSymbols(ThunkSection &IS) {
}
InputSection *MipsThunk::getTargetInputSection() const {
- auto *DR = dyn_cast<DefinedRegular>(&Destination);
- return dyn_cast<InputSection>(DR->Section);
+ auto &DR = cast<Defined>(Destination);
+ return dyn_cast<InputSection>(DR.Section);
}
-Thunk::Thunk(const SymbolBody &D) : Destination(D), Offset(0) {}
+// 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() | 1;
+ 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 = cast<Defined>(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() | 1;
+ 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 = cast<Defined>(Destination);
+ return dyn_cast<InputSection>(DR.Section);
+}
+
+Thunk::Thunk(Symbol &D) : Destination(D), Offset(0) {}
Thunk::~Thunk() = default;
+static Thunk *addThunkAArch64(RelType Type, Symbol &S) {
+ if (Type != R_AARCH64_CALL26 && Type != R_AARCH64_JUMP26)
+ fatal("unrecognized relocation type");
+ if (Config->Pic)
+ return make<AArch64ADRPThunk>(S);
+ return make<AArch64ABSLongThunk>(S);
+}
+
// Creates a thunk for Thumb-ARM interworking.
-static Thunk *addThunkArm(uint32_t Reloc, SymbolBody &S) {
+static Thunk *addThunkArm(RelType Reloc, Symbol &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.
@@ -246,11 +397,13 @@ static Thunk *addThunkArm(uint32_t Reloc, SymbolBody &S) {
case R_ARM_PC24:
case R_ARM_PLT32:
case R_ARM_JUMP24:
+ case R_ARM_CALL:
if (Config->Pic)
return make<ARMV7PILongThunk>(S);
return make<ARMV7ABSLongThunk>(S);
case R_ARM_THM_JUMP19:
case R_ARM_THM_JUMP24:
+ case R_ARM_THM_CALL:
if (Config->Pic)
return make<ThumbV7PILongThunk>(S);
return make<ThumbV7ABSLongThunk>(S);
@@ -258,13 +411,21 @@ static Thunk *addThunkArm(uint32_t Reloc, SymbolBody &S) {
fatal("unrecognized relocation type");
}
-static Thunk *addThunkMips(SymbolBody &S) { return make<MipsThunk>(S); }
+static Thunk *addThunkMips(RelType Type, Symbol &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) {
- if (Config->EMachine == EM_ARM)
- return addThunkArm(RelocType, S);
+Thunk *addThunk(RelType Type, Symbol &S) {
+ if (Config->EMachine == EM_AARCH64)
+ return addThunkAArch64(Type, S);
+ else if (Config->EMachine == EM_ARM)
+ 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 21eba699fe4f..828fac0bf53b 100644
--- a/ELF/Thunks.h
+++ b/ELF/Thunks.h
@@ -14,7 +14,7 @@
namespace lld {
namespace elf {
-class SymbolBody;
+class Symbol;
class ThunkSection;
// Class to describe an instance of a Thunk.
// A Thunk is a code-sequence inserted by the linker in between a caller and
@@ -23,11 +23,11 @@ class ThunkSection;
// include transferring control from non-pi to pi and changing state on
// targets like ARM.
//
-// Thunks can be created for DefinedRegular, Shared and Undefined Symbols.
+// Thunks can be created for Defined, Shared and Undefined Symbols.
// Thunks are assigned to synthetic ThunkSections
class Thunk {
public:
- Thunk(const SymbolBody &Destination);
+ Thunk(Symbol &Destination);
virtual ~Thunk();
virtual uint32_t size() const { return 0; }
@@ -41,21 +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 RelocType must be
+ // To reuse a Thunk the caller as identified by the Type must be
// compatible with it.
- virtual bool isCompatibleWith(uint32_t RelocType) const { return true; }
+ 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;
+ Symbol &Destination;
+ Symbol *ThunkSym;
+ uint64_t Offset = 0;
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, Symbol &S);
} // namespace elf
} // namespace lld
diff --git a/ELF/Writer.cpp b/ELF/Writer.cpp
index 1853f99bc600..c7a3cae49ae6 100644
--- a/ELF/Writer.cpp
+++ b/ELF/Writer.cpp
@@ -8,22 +8,22 @@
//===----------------------------------------------------------------------===//
#include "Writer.h"
+#include "AArch64ErrataFix.h"
#include "Config.h"
#include "Filesystem.h"
#include "LinkerScript.h"
#include "MapFile.h"
-#include "Memory.h"
#include "OutputSections.h"
#include "Relocations.h"
#include "Strings.h"
#include "SymbolTable.h"
+#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
-#include "Threads.h"
+#include "lld/Common/Memory.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;
@@ -39,6 +39,7 @@ namespace {
// The writer writes a SymbolTable result to a file.
template <class ELFT> class Writer {
public:
+ Writer() : Buffer(errorHandler().OutputBuffer) {}
typedef typename ELFT::Shdr Elf_Shdr;
typedef typename ELFT::Ehdr Elf_Ehdr;
typedef typename ELFT::Phdr Elf_Phdr;
@@ -46,43 +47,38 @@ public:
void run();
private:
- void clearOutputSections();
- void createSyntheticSections();
void copyLocalSymbols();
void addSectionSymbols();
- void addReservedSymbols();
- void createSections();
void forEachRelSec(std::function<void(InputSectionBase &)> Fn);
void sortSections();
+ void resolveShfLinkOrder();
+ void sortInputSections();
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();
void writeBuildId();
- std::unique_ptr<FileOutputBuffer> Buffer;
-
- OutputSectionFactory Factory;
+ std::unique_ptr<FileOutputBuffer> &Buffer;
void addRelIpltSymbols();
void addStartEndSymbols();
void addStartStopSymbols(OutputSection *Sec);
uint64_t getEntryAddr();
- OutputSection *findSectionInScript(StringRef Name);
- OutputSectionCommand *findSectionCommand(StringRef Name);
- std::vector<PhdrEntry> Phdrs;
+ std::vector<PhdrEntry *> Phdrs;
uint64_t FileSize;
uint64_t SectionHeaderOff;
@@ -91,49 +87,60 @@ private:
};
} // anonymous namespace
-StringRef elf::getOutputSectionName(StringRef Name) {
+StringRef elf::getOutputSectionName(InputSectionBase *S) {
// ".zdebug_" is a prefix for ZLIB-compressed sections.
// Because we decompressed input sections, we want to remove 'z'.
- if (Name.startswith(".zdebug_"))
- return Saver.save("." + Name.substr(2));
+ if (S->Name.startswith(".zdebug_"))
+ return Saver.save("." + S->Name.substr(2));
if (Config->Relocatable)
- return Name;
+ return S->Name;
+
+ // This is for --emit-relocs. If .text.foo is emitted as .text.bar, we want
+ // to emit .rela.text.foo as .rela.text.bar for consistency (this is not
+ // technically required, but not doing it is odd). This code guarantees that.
+ if ((S->Type == SHT_REL || S->Type == SHT_RELA) &&
+ !isa<SyntheticSection>(S)) {
+ OutputSection *Out =
+ cast<InputSection>(S)->getRelocatedSection()->getOutputSection();
+ if (S->Type == SHT_RELA)
+ return Saver.save(".rela" + Out->Name);
+ return Saver.save(".rel" + Out->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.", ".ARM.extab."}) {
StringRef Prefix = V.drop_back();
- if (Name.startswith(V) || Name == Prefix)
+ if (S->Name.startswith(V) || S->Name == Prefix)
return Prefix;
}
// CommonSection is identified as "COMMON" in linker scripts.
// By default, it should go to .bss section.
- if (Name == "COMMON")
+ if (S->Name == "COMMON")
return ".bss";
- return Name;
+ return S->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() {
@@ -142,7 +149,7 @@ template <class ELFT> static void combineEhFrameSections() {
if (!ES || !ES->Live)
continue;
- In<ELFT>::EhFrame->addSection(ES);
+ InX::EhFrame->addSection<ELFT>(ES);
S = nullptr;
}
@@ -150,130 +157,88 @@ template <class ELFT> static void combineEhFrameSections() {
V.erase(std::remove(V.begin(), V.end(), nullptr), V.end());
}
-template <class ELFT> void Writer<ELFT>::clearOutputSections() {
- // 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();
+template <class ELFT>
+static Defined *addOptionalRegular(StringRef Name, SectionBase *Sec,
+ uint64_t Val, uint8_t StOther = STV_HIDDEN,
+ uint8_t Binding = STB_GLOBAL) {
+ Symbol *S = Symtab->find(Name);
+ if (!S || S->isDefined())
+ return nullptr;
+ Symbol *Sym = Symtab->addRegular<ELFT>(Name, StOther, STT_NOTYPE, Val,
+ /*Size=*/0, Binding, Sec,
+ /*File=*/nullptr);
+ return cast<Defined>(Sym);
}
-// The main function of the writer.
-template <class ELFT> void Writer<ELFT>::run() {
- // Create linker-synthesized sections such as .got or .plt.
- // Such sections are of type input section.
- createSyntheticSections();
-
- if (!Config->Relocatable)
- combineEhFrameSections<ELFT>();
-
- // We need to create some reserved symbols such as _end. Create them.
- if (!Config->Relocatable)
- addReservedSymbols();
-
- // Create output sections.
- if (Script->Opt.HasSections) {
- // If linker script contains SECTIONS commands, let it create sections.
- Script->processCommands(Factory);
-
- // Linker scripts may have left some input sections unassigned.
- // Assign such sections using the default rule.
- Script->addOrphanSections(Factory);
- } else {
- // If linker script does not contain SECTIONS commands, create
- // output sections by default rules. We still need to give the
- // linker script a chance to run, because it might contain
- // non-SECTIONS commands such as ASSERT.
- Script->processCommands(Factory);
- createSections();
- }
- clearOutputSections();
-
- if (Config->Discard != DiscardPolicy::All)
- copyLocalSymbols();
-
- if (Config->CopyRelocs)
- addSectionSymbols();
-
- // Now that we have a complete set of output sections. This function
- // completes section contents. For example, we need to add strings
- // to the string table, and add entries to .got and .plt.
- // finalizeSections does that.
- finalizeSections();
- if (ErrorCount)
- return;
-
- if (!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>(); });
-
- 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
- // we know the size of the sections.
- removeEmptyPTLoad();
-
- if (!Config->OFormatBinary)
- assignFileOffsets();
- else
- assignFileOffsetsBinary();
+// The linker is expected to define some symbols depending on
+// the linking result. This function defines such symbols.
+template <class ELFT> void elf::addReservedSymbols() {
+ if (Config->EMachine == EM_MIPS) {
+ // Define _gp for MIPS. st_value of _gp symbol will be updated by Writer
+ // so that it points to an absolute address which by default is relative
+ // to GOT. Default offset is 0x7ff0.
+ // See "Global Data Symbols" in Chapter 6 in the following document:
+ // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
+ ElfSym::MipsGp = Symtab->addAbsolute<ELFT>("_gp", STV_HIDDEN, STB_GLOBAL);
- setPhdrs();
+ // On MIPS O32 ABI, _gp_disp is a magic symbol designates offset between
+ // start of function and 'gp' pointer into GOT.
+ if (Symtab->find("_gp_disp"))
+ ElfSym::MipsGpDisp =
+ Symtab->addAbsolute<ELFT>("_gp_disp", STV_HIDDEN, STB_GLOBAL);
- if (Config->Relocatable) {
- for (OutputSectionCommand *Cmd : OutputSectionCommands)
- Cmd->Sec->Addr = 0;
- } else {
- fixPredefinedSymbols();
+ // 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->find("__gnu_local_gp"))
+ ElfSym::MipsLocalGp =
+ Symtab->addAbsolute<ELFT>("__gnu_local_gp", STV_HIDDEN, STB_GLOBAL);
}
- // It does not make sense try to open the file if we have error already.
- if (ErrorCount)
- return;
- // Write the result down to a file.
- openFile();
- if (ErrorCount)
- return;
+ ElfSym::GlobalOffsetTable = addOptionalRegular<ELFT>(
+ "_GLOBAL_OFFSET_TABLE_", Out::ElfHeader, Target->GotBaseSymOff);
- if (!Config->OFormatBinary) {
- writeHeader();
- writeSections();
- } else {
- writeSectionsBinary();
- }
+ // __ehdr_start is the location of ELF file headers. Note that we define
+ // this symbol unconditionally even when using a linker script, which
+ // differs from the behavior implemented by GNU linker which only define
+ // this symbol if ELF headers are in the memory mapped segment.
+ // __executable_start is not documented, but the expectation of at
+ // least the android libc is that it points to the elf header too.
+ // __dso_handle symbol is passed to cxa_finalize as a marker to identify
+ // each DSO. The address of the symbol doesn't matter as long as they are
+ // different in different DSOs, so we chose the start address of the DSO.
+ for (const char *Name :
+ {"__ehdr_start", "__executable_start", "__dso_handle"})
+ addOptionalRegular<ELFT>(Name, Out::ElfHeader, 0, STV_HIDDEN);
- // Backfill .note.gnu.build-id section content. This is done at last
- // because the content is usually a hash value of the entire output file.
- writeBuildId();
- if (ErrorCount)
+ // If linker script do layout we do not need to create any standart symbols.
+ if (Script->HasSectionsCommand)
return;
- // Handle -Map option.
- writeMapFile<ELFT>(OutputSectionCommands);
- if (ErrorCount)
- return;
+ auto Add = [](StringRef S, int64_t Pos) {
+ return addOptionalRegular<ELFT>(S, Out::ElfHeader, Pos, STV_DEFAULT);
+ };
- if (auto EC = Buffer->commit())
- error("failed to write to the output file: " + EC.message());
+ 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);
+}
- // 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);
+static OutputSection *findSection(StringRef Name) {
+ for (BaseCommand *Base : Script->SectionCommands)
+ if (auto *Sec = dyn_cast<OutputSection>(Base))
+ if (Sec->Name == Name)
+ return Sec;
+ return nullptr;
}
// Initialize Out members.
-template <class ELFT> void Writer<ELFT>::createSyntheticSections() {
+template <class ELFT> static void createSyntheticSections() {
// Initialize all pointers with NULL. This is needed because
// you can call lld::elf::main more than once as a library.
memset(&Out::First, 0, sizeof(Out));
@@ -282,16 +247,19 @@ template <class ELFT> void Writer<ELFT>::createSyntheticSections() {
InX::DynStrTab = make<StringTableSection>(".dynstr", true);
InX::Dynamic = make<DynamicSection<ELFT>>();
- In<ELFT>::RelaDyn = make<RelocationSection<ELFT>>(
- Config->IsRela ? ".rela.dyn" : ".rel.dyn", Config->ZCombreloc);
+ if (Config->AndroidPackDynRelocs) {
+ InX::RelaDyn = make<AndroidPackedRelocationSection<ELFT>>(
+ Config->IsRela ? ".rela.dyn" : ".rel.dyn");
+ } else {
+ InX::RelaDyn = make<RelocationSection<ELFT>>(
+ Config->IsRela ? ".rela.dyn" : ".rel.dyn", Config->ZCombreloc);
+ }
InX::ShStrTab = make<StringTableSection>(".shstrtab", false);
- 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 {
@@ -308,20 +276,21 @@ 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");
+
+ // If there is a SECTIONS command and a .data.rel.ro section name use name
+ // .data.rel.ro.bss so that we match in the .data.rel.ro output section.
+ // This makes sure our relro is contiguous.
+ bool HasDataRelRo =
+ Script->HasSectionsCommand && findSection(".data.rel.ro");
+ InX::BssRelRo = make<BssSection>(
+ HasDataRelRo ? ".data.rel.ro.bss" : ".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);
}
@@ -333,7 +302,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);
@@ -354,13 +323,13 @@ 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);
Add(InX::DynStrTab);
- Add(In<ELFT>::RelaDyn);
+ Add(InX::RelaDyn);
}
// Add .got. MIPS' .got is so different from the other archs,
@@ -385,16 +354,22 @@ template <class ELFT> void Writer<ELFT>::createSyntheticSections() {
// We always need to add rel[a].plt to output if it has entries.
// Even for static linking it can contain R_[*]_IRELATIVE relocations.
- In<ELFT>::RelaPlt = make<RelocationSection<ELFT>>(
+ InX::RelaPlt = make<RelocationSection<ELFT>>(
Config->IsRela ? ".rela.plt" : ".rel.plt", false /*Sort*/);
- Add(In<ELFT>::RelaPlt);
+ Add(InX::RelaPlt);
// The RelaIplt immediately follows .rel.plt (.rel.dyn for ARM) to ensure
- // that the IRelative relocations are processed last by the dynamic loader
- In<ELFT>::RelaIplt = make<RelocationSection<ELFT>>(
- (Config->EMachine == EM_ARM) ? ".rel.dyn" : In<ELFT>::RelaPlt->Name,
+ // that the IRelative relocations are processed last by the dynamic loader.
+ // We cannot place the iplt section in .rel.dyn when Android relocation
+ // packing is enabled because that would cause a section type mismatch.
+ // However, because the Android dynamic loader reads .rel.plt after .rel.dyn,
+ // we can get the desired behaviour by placing the iplt section in .rel.plt.
+ InX::RelaIplt = make<RelocationSection<ELFT>>(
+ (Config->EMachine == EM_ARM && !Config->AndroidPackDynRelocs)
+ ? ".rel.dyn"
+ : InX::RelaPlt->Name,
false /*Sort*/);
- Add(In<ELFT>::RelaIplt);
+ Add(InX::RelaIplt);
InX::Plt = make<PltSection>(Target->PltHeaderSize);
Add(InX::Plt);
@@ -403,11 +378,11 @@ template <class ELFT> void Writer<ELFT>::createSyntheticSections() {
if (!Config->Relocatable) {
if (Config->EhFrameHdr) {
- In<ELFT>::EhFrameHdr = make<EhFrameHeader<ELFT>>();
- Add(In<ELFT>::EhFrameHdr);
+ InX::EhFrameHdr = make<EhFrameHeader>();
+ Add(InX::EhFrameHdr);
}
- In<ELFT>::EhFrame = make<EhFrameSection<ELFT>>();
- Add(In<ELFT>::EhFrame);
+ InX::EhFrame = make<EhFrameSection>();
+ Add(InX::EhFrame);
}
if (InX::SymTab)
@@ -417,8 +392,97 @@ template <class ELFT> void Writer<ELFT>::createSyntheticSections() {
Add(InX::StrTab);
}
+// The main function of the writer.
+template <class ELFT> void Writer<ELFT>::run() {
+ // Create linker-synthesized sections such as .got or .plt.
+ // Such sections are of type input section.
+ createSyntheticSections<ELFT>();
+
+ if (!Config->Relocatable)
+ combineEhFrameSections<ELFT>();
+
+ // We want to process linker script commands. When SECTIONS command
+ // is given we let it create sections.
+ Script->processSectionCommands();
+
+ // Linker scripts controls how input sections are assigned to output sections.
+ // Input sections that were not handled by scripts are called "orphans", and
+ // they are assigned to output sections by the default rule. Process that.
+ Script->addOrphanSections();
+
+ if (Config->Discard != DiscardPolicy::All)
+ copyLocalSymbols();
+
+ if (Config->CopyRelocs)
+ addSectionSymbols();
+
+ // Now that we have a complete set of output sections. This function
+ // completes section contents. For example, we need to add strings
+ // to the string table, and add entries to .got and .plt.
+ // finalizeSections does that.
+ finalizeSections();
+ if (errorCount())
+ return;
+
+ // If -compressed-debug-sections is specified, we need to compress
+ // .debug_* sections. Do it right now because it changes the size of
+ // output sections.
+ parallelForEach(OutputSections,
+ [](OutputSection *Sec) { Sec->maybeCompress<ELFT>(); });
+
+ 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
+ // we know the size of the sections.
+ removeEmptyPTLoad();
+
+ if (!Config->OFormatBinary)
+ assignFileOffsets();
+ else
+ assignFileOffsetsBinary();
+
+ setPhdrs();
+
+ if (Config->Relocatable) {
+ for (OutputSection *Sec : OutputSections)
+ Sec->Addr = 0;
+ }
+
+ // It does not make sense try to open the file if we have error already.
+ if (errorCount())
+ return;
+ // Write the result down to a file.
+ openFile();
+ if (errorCount())
+ return;
+
+ if (!Config->OFormatBinary) {
+ writeTrapInstr();
+ writeHeader();
+ writeSections();
+ } else {
+ writeSectionsBinary();
+ }
+
+ // Backfill .note.gnu.build-id section content. This is done at last
+ // because the content is usually a hash value of the entire output file.
+ writeBuildId();
+ if (errorCount())
+ return;
+
+ // Handle -Map option.
+ writeMapFile();
+ if (errorCount())
+ return;
+
+ if (auto E = Buffer->commit())
+ error("failed to write to the output file: " + toString(std::move(E)));
+}
+
static bool shouldKeepInSymtab(SectionBase *Sec, StringRef SymName,
- const SymbolBody &B) {
+ const Symbol &B) {
if (B.isFile() || B.isSection())
return false;
@@ -443,27 +507,25 @@ static bool shouldKeepInSymtab(SectionBase *Sec, StringRef SymName,
return !Sec || !(Sec->Flags & SHF_MERGE);
}
-static bool includeInSymtab(const SymbolBody &B) {
- if (!B.isLocal() && !B.symbol()->IsUsedInRegularObj)
+static bool includeInSymtab(const Symbol &B) {
+ if (!B.isLocal() && !B.IsUsedInRegularObj)
return false;
- if (auto *D = dyn_cast<DefinedRegular>(&B)) {
+ if (auto *D = dyn_cast<Defined>(&B)) {
// Always include absolute symbols.
SectionBase *Sec = D->Section;
if (!Sec)
return true;
- if (auto *IS = dyn_cast<InputSectionBase>(Sec)) {
- Sec = IS->Repl;
- IS = cast<InputSectionBase>(Sec);
- // Exclude symbols pointing to garbage-collected sections.
- if (!IS->Live)
- return false;
- }
+ Sec = Sec->Repl;
+ // Exclude symbols pointing to garbage-collected sections.
+ if (isa<InputSectionBase>(Sec) && !Sec->Live)
+ return false;
if (auto *S = dyn_cast<MergeInputSection>(Sec))
if (!S->getSectionPiece(D->Value)->Live)
return false;
+ return true;
}
- return true;
+ return B.Used;
}
// Local symbols are not in the linker's symbol table. This function scans
@@ -471,12 +533,13 @@ 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 (SymbolBody *B : F->getLocalSymbols()) {
- if (!B->IsLocal)
+ for (InputFile *File : ObjectFiles) {
+ ObjFile<ELFT> *F = cast<ObjFile<ELFT>>(File);
+ for (Symbol *B : F->getLocalSymbols()) {
+ if (!B->isLocal())
fatal(toString(F) +
": broken object: getLocalSymbols returns a non-local symbol");
- auto *DR = dyn_cast<DefinedRegular>(B);
+ auto *DR = dyn_cast<Defined>(B);
// No reason to keep local undefined symbol in symtab.
if (!DR)
@@ -493,27 +556,36 @@ 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 (BaseCommand *Base : Script->Opt.Commands) {
- auto *Cmd = dyn_cast<OutputSectionCommand>(Base);
- if (!Cmd)
+ // Create a section symbol for each output section so that we can represent
+ // relocations that point to the section. If we know that no relocation is
+ // referring to a section (that happens if the section is a synthetic one), we
+ // don't create a section symbol for that section.
+ for (BaseCommand *Base : Script->SectionCommands) {
+ auto *Sec = dyn_cast<OutputSection>(Base);
+ if (!Sec)
continue;
- auto I = llvm::find_if(Cmd->Commands, [](BaseCommand *Base) {
+ auto I = llvm::find_if(Sec->SectionCommands, [](BaseCommand *Base) {
if (auto *ISD = dyn_cast<InputSectionDescription>(Base))
return !ISD->Sections.empty();
return false;
});
- if (I == Cmd->Commands.end())
+ 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)
+
+ // Relocations are not using REL[A] section symbols.
+ if (IS->Type == SHT_REL || IS->Type == SHT_RELA)
+ continue;
+
+ // Unlike other synthetic sections, mergeable output sections contain data
+ // copied from input sections, and there may be a relocation pointing to its
+ // contents if -r or -emit-reloc are given.
+ if (isa<SyntheticSection>(IS) && !(IS->Flags & SHF_MERGE))
continue;
auto *Sym =
- make<DefinedRegular>("", /*IsLocal=*/true, /*StOther=*/0, STT_SECTION,
- /*Value=*/0, /*Size=*/0, IS, nullptr);
+ make<Defined>(IS->File, "", STB_LOCAL, /*StOther=*/0, STT_SECTION,
+ /*Value=*/0, /*Size=*/0, IS);
InX::SymTab->addSymbol(Sym);
}
}
@@ -524,7 +596,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;
@@ -575,20 +647,14 @@ bool elf::isRelroSection(const OutputSection *Sec) {
if (Sec == InX::Dynamic->getParent())
return true;
- // .bss.rel.ro is used for copy relocations for read-only symbols.
- // Since the dynamic linker needs to process copy relocations, the
- // section cannot be read-only, but once initialized, they shouldn't
- // change.
- if (Sec == InX::BssRelRo->getParent())
- return true;
-
// Sections with some special names are put into RELRO. This is a
// bit unfortunate because section names shouldn't be significant in
// ELF in spirit. But in reality many linker features depend on
// magic section names.
StringRef S = Sec->Name;
- return S == ".data.rel.ro" || S == ".ctors" || S == ".dtors" || S == ".jcr" ||
- S == ".eh_frame" || S == ".openbsd.randomdata";
+ return S == ".data.rel.ro" || S == ".bss.rel.ro" || S == ".ctors" ||
+ S == ".dtors" || S == ".jcr" || S == ".eh_frame" ||
+ S == ".openbsd.randomdata";
}
// We compute a rank for each section. The rank indicates where the
@@ -696,8 +762,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
@@ -735,8 +801,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))
@@ -746,36 +812,12 @@ 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);
-}
-
-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)
- return nullptr;
- if (S->isInCurrentDSO())
- return nullptr;
- return cast<DefinedRegular>(
- addRegular<ELFT>(Name, Sec, Val, StOther, Binding)->body());
+ Sec->PtLoad = this;
}
// The beginning and the ending of .rel[a].plt section are marked
@@ -785,177 +827,115 @@ 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);
+ addOptionalRegular<ELFT>(S, InX::RelaIplt, 0, STV_HIDDEN, STB_WEAK);
S = Config->IsRela ? "__rela_iplt_end" : "__rel_iplt_end";
- addOptionalRegular<ELFT>(S, In<ELFT>::RelaIplt, -1, STV_HIDDEN, STB_WEAK);
+ addOptionalRegular<ELFT>(S, InX::RelaIplt, -1, STV_HIDDEN, STB_WEAK);
}
-// The linker is expected to define some symbols depending on
-// the linking result. This function defines such symbols.
-template <class ELFT> void Writer<ELFT>::addReservedSymbols() {
- if (Config->EMachine == EM_MIPS) {
- // Define _gp for MIPS. st_value of _gp symbol will be updated by Writer
- // so that it points to an absolute address which by default is relative
- // to GOT. Default offset is 0x7ff0.
- // See "Global Data Symbols" in Chapter 6 in the following document:
- // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
- ElfSym::MipsGp = Symtab<ELFT>::X->addAbsolute("_gp", STV_HIDDEN, STB_LOCAL);
-
- // On MIPS O32 ABI, _gp_disp is a magic symbol designates offset between
- // start of function and 'gp' pointer into GOT.
- if (Symtab<ELFT>::X->find("_gp_disp"))
- ElfSym::MipsGpDisp =
- Symtab<ELFT>::X->addAbsolute("_gp_disp", STV_HIDDEN, STB_LOCAL);
+template <class ELFT>
+void Writer<ELFT>::forEachRelSec(std::function<void(InputSectionBase &)> Fn) {
+ // 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);
+ for (EhInputSection *ES : InX::EhFrame->Sections)
+ Fn(*ES);
+}
- // 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"))
- ElfSym::MipsLocalGp =
- Symtab<ELFT>::X->addAbsolute("__gnu_local_gp", STV_HIDDEN, STB_LOCAL);
+// 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() {
+ if (ElfSym::GlobalOffsetTable) {
+ // 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->Section = GotSection;
}
- // 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);
-
- // __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");
-
- // __ehdr_start is the location of ELF file headers. Note that we define
- // this symbol unconditionally even when using a linker script, which
- // differs from the behavior implemented by GNU linker which only define
- // this symbol if ELF headers are in the memory mapped segment.
- // __executable_start is not documented, but the expectation of at
- // least the android libc is that it points to the elf header too.
- // __dso_handle symbol is passed to cxa_finalize as a marker to identify
- // each DSO. The address of the symbol doesn't matter as long as they are
- // different in different DSOs, so we chose the start address of the DSO.
- for (const char *Name :
- {"__ehdr_start", "__executable_start", "__dso_handle"})
- 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)
- return;
-
- auto Add = [](StringRef S) {
- return addOptionalRegular<ELFT>(S, Out::ElfHeader, 0, 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");
-}
-
-// Sort input sections by section name suffixes for
-// __attribute__((init_priority(N))).
-static void sortInitFini(OutputSectionCommand *Cmd) {
- if (Cmd)
- Cmd->sortInitFini();
-}
+ PhdrEntry *Last = nullptr;
+ PhdrEntry *LastRO = nullptr;
-// Sort input sections by the special rule for .ctors and .dtors.
-static void sortCtorsDtors(OutputSectionCommand *Cmd) {
- if (Cmd)
- Cmd->sortCtorsDtors();
-}
+ for (PhdrEntry *P : Phdrs) {
+ if (P->p_type != PT_LOAD)
+ continue;
+ Last = P;
+ if (!(P->p_flags & PF_W))
+ LastRO = P;
+ }
-// Sort input sections using the list provided by --symbol-ordering-file.
-template <class ELFT> static void sortBySymbolsOrder() {
- if (Config->SymbolOrderingFile.empty())
- return;
+ if (LastRO) {
+ // _etext is the first location after the last read-only loadable segment.
+ if (ElfSym::Etext1)
+ ElfSym::Etext1->Section = LastRO->LastSec;
+ if (ElfSym::Etext2)
+ ElfSym::Etext2->Section = LastRO->LastSec;
+ }
- // 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()));
+ if (Last) {
+ // _edata points to the end of the last mapped initialized section.
+ OutputSection *Edata = nullptr;
+ for (OutputSection *OS : OutputSections) {
+ if (OS->Type != SHT_NOBITS)
+ Edata = OS;
+ if (OS == Last->LastSec)
+ break;
}
- }
- // Sort sections by priority.
- for (BaseCommand *Base : Script->Opt.Commands)
- if (auto *Cmd = dyn_cast<OutputSectionCommand>(Base))
- Cmd->sort([&](InputSectionBase *S) { return SectionOrder.lookup(S); });
-}
+ if (ElfSym::Edata1)
+ ElfSym::Edata1->Section = Edata;
+ if (ElfSym::Edata2)
+ ElfSym::Edata2->Section = Edata;
-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))
- Fn(*IS);
+ // _end is the first location after the uninitialized data region.
+ if (ElfSym::End1)
+ ElfSym::End1->Section = Last->LastSec;
+ if (ElfSym::End2)
+ ElfSym::End2->Section = Last->LastSec;
}
- if (!Config->Relocatable) {
- for (EhInputSection *ES : In<ELFT>::EhFrame->Sections)
- Fn(*ES);
- }
-}
+ if (ElfSym::Bss)
+ ElfSym::Bss->Section = findSection(".bss");
-template <class ELFT> void Writer<ELFT>::createSections() {
- for (InputSectionBase *IS : InputSections)
- if (IS)
- Factory.addInputSec(IS, getOutputSectionName(IS->Name));
-
- Script->fabricateDefaultCommands();
- sortBySymbolsOrder<ELFT>();
- sortInitFini(findSectionCommand(".init_array"));
- sortInitFini(findSectionCommand(".fini_array"));
- sortCtorsDtors(findSectionCommand(".ctors"));
- sortCtorsDtors(findSectionCommand(".dtors"));
+ // 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;
}
@@ -974,7 +954,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 != ".";
@@ -988,7 +968,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) {
@@ -1000,44 +980,84 @@ 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 IsLiveSection = [](BaseCommand *Cmd) {
+ auto *OS = dyn_cast<OutputSection>(Cmd);
+ return OS && OS->Live;
+ };
+
+ auto J = std::find_if(llvm::make_reverse_iterator(I),
+ llvm::make_reverse_iterator(B), IsLiveSection);
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, IsLiveSection);
+ if (NextSec == E)
+ return E;
+
while (I != E && shouldSkip(*I))
++I;
return I;
}
+// If no layout was provided by linker script, we want to apply default
+// sorting for special input sections and handle --symbol-ordering-file.
+template <class ELFT> void Writer<ELFT>::sortInputSections() {
+ assert(!Script->HasSectionsCommand);
+
+ // Sort input sections by priority using the list provided
+ // by --symbol-ordering-file.
+ DenseMap<SectionBase *, int> Order = buildSectionOrder();
+ if (!Order.empty())
+ for (BaseCommand *Base : Script->SectionCommands)
+ if (auto *Sec = dyn_cast<OutputSection>(Base))
+ if (Sec->Live)
+ Sec->sort([&](InputSectionBase *S) { return Order.lookup(S); });
+
+ // Sort input sections by section name suffixes for
+ // __attribute__((init_priority(N))).
+ if (OutputSection *Sec = findSection(".init_array"))
+ Sec->sortInitFini();
+ if (OutputSection *Sec = findSection(".fini_array"))
+ Sec->sortInitFini();
+
+ // Sort input sections by the special rule for .ctors and .dtors.
+ if (OutputSection *Sec = findSection(".ctors"))
+ Sec->sortCtorsDtors();
+ if (OutputSection *Sec = findSection(".dtors"))
+ Sec->sortCtorsDtors();
+}
+
template <class ELFT> void Writer<ELFT>::sortSections() {
- if (Script->Opt.HasSections)
- Script->adjustSectionsBeforeSorting();
+ 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;
- for (BaseCommand *Base : Script->Opt.Commands)
- if (auto *Cmd = dyn_cast<OutputSectionCommand>(Base))
- if (OutputSection *Sec = Cmd->Sec)
- Sec->SortRank = getSectionRank(Sec);
-
- if (!Script->Opt.HasSections) {
- // We know that all the OutputSectionCommands 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);
- };
+ for (BaseCommand *Base : Script->SectionCommands)
+ if (auto *Sec = dyn_cast<OutputSection>(Base))
+ Sec->SortRank = getSectionRank(Sec);
+
+ if (!Script->HasSectionsCommand) {
+ sortInputSections();
+
+ // We know that all the OutputSections are contiguous in this case.
+ 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)
@@ -1077,7 +1097,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
@@ -1085,11 +1105,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;
});
@@ -1109,13 +1129,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;
@@ -1124,6 +1144,125 @@ template <class ELFT> void Writer<ELFT>::sortSections() {
Script->adjustSectionsAfterSorting();
}
+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;
+}
+
+// This function is used by the --merge-exidx-entries to detect duplicate
+// .ARM.exidx sections. It is Arm only.
+//
+// The .ARM.exidx section is of the form:
+// | PREL31 offset to function | Unwind instructions for function |
+// where the unwind instructions are either a small number of unwind
+// instructions inlined into the table entry, the special CANT_UNWIND value of
+// 0x1 or a PREL31 offset into a .ARM.extab Section that contains unwind
+// instructions.
+//
+// We return true if all the unwind instructions in the .ARM.exidx entries of
+// Cur can be merged into the last entry of Prev.
+static bool isDuplicateArmExidxSec(InputSection *Prev, InputSection *Cur) {
+
+ // References to .ARM.Extab Sections have bit 31 clear and are not the
+ // special EXIDX_CANTUNWIND bit-pattern.
+ auto IsExtabRef = [](uint32_t Unwind) {
+ return (Unwind & 0x80000000) == 0 && Unwind != 0x1;
+ };
+
+ struct ExidxEntry {
+ ulittle32_t Fn;
+ ulittle32_t Unwind;
+ };
+
+ // Get the last table Entry from the previous .ARM.exidx section.
+ const ExidxEntry &PrevEntry = *reinterpret_cast<const ExidxEntry *>(
+ Prev->Data.data() + Prev->getSize() - sizeof(ExidxEntry));
+ if (IsExtabRef(PrevEntry.Unwind))
+ return false;
+
+ // We consider the unwind instructions of an .ARM.exidx table entry
+ // a duplicate if the previous unwind instructions if:
+ // - Both are the special EXIDX_CANTUNWIND.
+ // - Both are the same inline unwind instructions.
+ // We do not attempt to follow and check links into .ARM.extab tables as
+ // consecutive identical entries are rare and the effort to check that they
+ // are identical is high.
+
+ if (isa<SyntheticSection>(Cur))
+ // Exidx sentinel section has implicit EXIDX_CANTUNWIND;
+ return PrevEntry.Unwind == 0x1;
+
+ ArrayRef<const ExidxEntry> Entries(
+ reinterpret_cast<const ExidxEntry *>(Cur->Data.data()),
+ Cur->getSize() / sizeof(ExidxEntry));
+ for (const ExidxEntry &Entry : Entries)
+ if (IsExtabRef(Entry.Unwind) || Entry.Unwind != PrevEntry.Unwind)
+ return false;
+ // All table entries in this .ARM.exidx Section can be merged into the
+ // previous Section.
+ return true;
+}
+
+template <class ELFT> void Writer<ELFT>::resolveShfLinkOrder() {
+ for (OutputSection *Sec : OutputSections) {
+ if (!(Sec->Flags & SHF_LINK_ORDER))
+ continue;
+
+ // 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 : Sec->SectionCommands) {
+ if (auto *ISD = dyn_cast<InputSectionDescription>(Base)) {
+ for (InputSection *&IS : ISD->Sections) {
+ ScriptSections.push_back(&IS);
+ Sections.push_back(IS);
+ }
+ }
+ }
+ std::stable_sort(Sections.begin(), Sections.end(), compareByFilePosition);
+
+ if (Config->MergeArmExidx && !Config->Relocatable &&
+ Config->EMachine == EM_ARM && Sec->Type == SHT_ARM_EXIDX) {
+ // The EHABI for the Arm Architecture permits consecutive identical
+ // table entries to be merged. We use a simple implementation that
+ // removes a .ARM.exidx Input Section if it can be merged into the
+ // previous one. This does not require any rewriting of InputSection
+ // contents but misses opportunities for fine grained deduplication where
+ // only a subset of the InputSection contents can be merged.
+ int Cur = 1;
+ int Prev = 0;
+ int N = Sections.size();
+ while (Cur < N) {
+ if (isDuplicateArmExidxSec(Sections[Prev], Sections[Cur]))
+ Sections[Cur] = nullptr;
+ else
+ Prev = Cur;
+ ++Cur;
+ }
+ }
+
+ for (int I = 0, N = Sections.size(); I < N; ++I)
+ *ScriptSections[I] = Sections[I];
+
+ // Remove the Sections we marked as duplicate earlier.
+ for (BaseCommand *Base : Sec->SectionCommands)
+ if (auto *ISD = dyn_cast<InputSectionDescription>(Base))
+ ISD->Sections.erase(
+ std::remove(ISD->Sections.begin(), ISD->Sections.end(), nullptr),
+ ISD->Sections.end());
+ }
+}
+
static void applySynthetic(const std::vector<SyntheticSection *> &Sections,
std::function<void(SyntheticSection *)> Fn) {
for (SyntheticSection *SS : Sections)
@@ -1131,10 +1270,18 @@ 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.
+// 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
@@ -1146,55 +1293,73 @@ static void removeUnusedSyntheticSections() {
OutputSection *OS = SS->getParent();
if (!SS->empty() || !OS)
continue;
- if ((SS == InX::Got || SS == InX::MipsGot) && ElfSym::GlobalOffsetTable)
- continue;
- OutputSectionCommand *Cmd = Script->getCmd(OS);
- std::vector<BaseCommand *>::iterator Empty = Cmd->Commands.end();
- for (auto I = Cmd->Commands.begin(), E = Cmd->Commands.end(); I != E; ++I) {
+ 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)) {
- auto P = std::find(ISD->Sections.begin(), ISD->Sections.end(), SS);
- if (P != ISD->Sections.end())
- ISD->Sections.erase(P);
+ llvm::erase_if(ISD->Sections,
+ [=](InputSection *IS) { return IS == SS; });
if (ISD->Sections.empty())
Empty = I;
}
}
- if (Empty != Cmd->Commands.end())
- Cmd->Commands.erase(Empty);
+ 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 (Cmd->Commands.empty()) {
- // Also remove script commands matching the output section.
- auto &Cmds = Script->Opt.Commands;
- auto I = std::remove_if(Cmds.begin(), Cmds.end(), [&](BaseCommand *Cmd) {
- if (auto *OSCmd = dyn_cast<OutputSectionCommand>(Cmd))
- return OSCmd->Sec == OS;
- return false;
- });
- Cmds.erase(I, Cmds.end());
- }
+ 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 Symbol &B) {
+ assert(!B.isLocal());
+ // Only symbols that appear in dynsym can be preempted.
+ if (!B.includeInDynsym())
+ return false;
+
+ // Only default visibility symbols can be preempted.
+ if (B.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.isDefined())
+ 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 = findSectionInScript(".debug_info");
- Out::PreinitArray = findSectionInScript(".preinit_array");
- Out::InitArray = findSectionInScript(".init_array");
- Out::FiniArray = findSectionInScript(".fini_array");
+ Out::DebugInfo = findSection(".debug_info");
+ Out::PreinitArray = findSection(".preinit_array");
+ Out::InitArray = findSection(".init_array");
+ Out::FiniArray = findSection(".fini_array");
// The linker needs to define SECNAME_start, SECNAME_end and SECNAME_stop
// symbols for sections, so that the runtime can get the start and end
// addresses of each section by section name. Add such symbols.
if (!Config->Relocatable) {
addStartEndSymbols();
- for (BaseCommand *Base : Script->Opt.Commands)
- if (auto *Cmd = dyn_cast<OutputSectionCommand>(Base))
- if (Cmd->Sec)
- addStartStopSymbols(Cmd->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.
@@ -1202,7 +1367,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();
@@ -1210,12 +1377,16 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
// This responsible for splitting up .eh_frame section into
// pieces. The relocation scan uses those pieces, so this has to be
// earlier.
- applySynthetic({In<ELFT>::EhFrame},
+ applySynthetic({InX::EhFrame},
[](SyntheticSection *SS) { SS->finalizeContents(); });
+ for (Symbol *S : Symtab->getSymbols())
+ S->IsPreemptible |= computeIsPreemptible(*S);
+
// 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();
@@ -1224,42 +1395,41 @@ 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()) {
- SymbolBody *Body = S->body();
-
- if (!includeInSymtab(*Body))
+ for (Symbol *Sym : Symtab->getSymbols()) {
+ if (!includeInSymtab(*Sym))
continue;
if (InX::SymTab)
- InX::SymTab->addSymbol(Body);
+ InX::SymTab->addSymbol(Sym);
- if (InX::DynSymTab && S->includeInDynsym()) {
- InX::DynSymTab->addSymbol(Body);
- if (auto *SS = dyn_cast<SharedSymbol>(Body))
- if (cast<SharedFile<ELFT>>(SS->File)->isNeeded())
+ if (InX::DynSymTab && Sym->includeInDynsym()) {
+ InX::DynSymTab->addSymbol(Sym);
+ if (auto *SS = dyn_cast<SharedSymbol>(Sym))
+ if (cast<SharedFile<ELFT>>(Sym->File)->IsNeeded)
In<ELFT>::VerNeed->addSymbol(SS);
}
}
// Do not proceed if there was an undefined symbol.
- if (ErrorCount)
+ if (errorCount())
return;
addPredefinedSections();
removeUnusedSyntheticSections();
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 (OutputSectionCommand *Cmd : OutputSectionCommands) {
- auto I = Config->SectionStartMap.find(Cmd->Name);
+ for (OutputSection *Sec : OutputSections) {
+ auto I = Config->SectionStartMap.find(Sec->Name);
if (I != Config->SectionStartMap.end())
- Cmd->AddrExpr = [=] { return I->second; };
+ Sec->AddrExpr = [=] { return I->second; };
}
// This is a bit of a hack. A value of 0 means undef, so we set it
@@ -1268,8 +1438,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);
}
@@ -1283,64 +1452,73 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
Out::ProgramHeaders->Size = sizeof(Elf_Phdr) * Phdrs.size();
}
+ // 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},
- [](SyntheticSection *SS) { SS->finalizeContents(); });
+ 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, InX::RelaDyn, InX::RelaIplt,
+ InX::RelaPlt, InX::Plt, InX::Iplt, InX::EhFrameHdr,
+ In<ELFT>::VerSym, In<ELFT>::VerNeed, InX::Dynamic},
+ [](SyntheticSection *SS) { SS->finalizeContents(); });
+
+ if (!Script->HasSectionsCommand && !Config->Relocatable)
+ fixSectionAlignments();
+
+ // After link order processing .ARM.exidx sections can be deduplicated, which
+ // needs to be resolved before any other address dependent operation.
+ resolveShfLinkOrder();
- // Some architectures use small displacements for jump instructions.
- // It is linker's responsibility to create thunks containing long
- // jump instructions if jump targets are too far. Create thunks.
- if (Target->NeedsThunks) {
- // FIXME: only ARM Interworking and Mips LA25 Thunks are implemented,
- // these
- // do not require address information. To support range extension Thunks
- // we need to assign addresses so that we can tell if jump instructions
- // are out of range. This will need to turn into a loop that converges
- // when no more Thunks are added
+ // Some architectures need to generate content that depends on the address
+ // of InputSections. For example some architectures use small displacements
+ // for jump instructions that is is the linker's responsibility for creating
+ // range extension thunks for. As the generation of the content may also
+ // alter InputSection addresses we must converge to a fixed point.
+ if (Target->NeedsThunks || Config->AndroidPackDynRelocs) {
ThunkCreator TC;
- Script->assignAddresses();
- if (TC.createThunks(OutputSectionCommands)) {
- applySynthetic({InX::MipsGot},
- [](SyntheticSection *SS) { SS->updateAllocSize(); });
- if (TC.createThunks(OutputSectionCommands))
- fatal("All non-range thunks should be created in first call");
- }
+ AArch64Err843419Patcher A64P;
+ bool Changed;
+ do {
+ Script->assignAddresses();
+ Changed = false;
+ if (Target->NeedsThunks)
+ Changed |= TC.createThunks(OutputSections);
+ if (Config->FixCortexA53Errata843419) {
+ if (Changed)
+ Script->assignAddresses();
+ Changed |= A64P.createFixes();
+ }
+ if (InX::MipsGot)
+ InX::MipsGot->updateAllocSize();
+ Changed |= InX::RelaDyn->updateAllocSize();
+ } while (Changed);
}
// 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},
+ applySynthetic({InX::SymTab},
[](SyntheticSection *SS) { SS->postThunkContents(); });
}
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.
- OutputSectionCommand *Cmd = findSectionCommand(".ARM.exidx");
- if (!Cmd || !Cmd->Sec || Config->Relocatable)
+ OutputSection *Cmd = findSection(".ARM.exidx");
+ if (!Cmd || !Cmd->Live || Config->Relocatable)
return;
auto *Sentinel = make<ARMExidxSentinelSection>();
- Cmd->Sec->addSection(Sentinel);
- // Add the sentinel to the last of these too.
- auto ISD = std::find_if(Cmd->Commands.rbegin(), Cmd->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
@@ -1364,7 +1542,7 @@ template <class ELFT> void Writer<ELFT>::addStartEndSymbols() {
Define("__init_array_start", "__init_array_end", Out::InitArray);
Define("__fini_array_start", "__fini_array_end", Out::FiniArray);
- if (OutputSection *Sec = findSectionInScript(".ARM.exidx"))
+ if (OutputSection *Sec = findSection(".ARM.exidx"))
Define("__exidx_start", "__exidx_end", Sec);
}
@@ -1382,22 +1560,6 @@ 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 (BaseCommand *Base : Script->Opt.Commands)
- if (auto *Cmd = dyn_cast<OutputSectionCommand>(Base))
- 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;
-}
-
static bool needsPtLoad(OutputSection *Sec) {
if (!(Sec->Flags & SHF_ALLOC))
return false;
@@ -1424,19 +1586,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);
@@ -1446,8 +1608,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))
@@ -1459,7 +1620,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 (Cmd->LMAExpr || Flags != NewFlags) {
+ if (Sec->LMAExpr || Flags != NewFlags) {
Load = AddHdr(PT_LOAD, NewFlags);
Flags = NewFlags;
}
@@ -1468,14 +1629,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)
@@ -1484,25 +1643,39 @@ 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;
- if (needsPtLoad(Sec) && isRelroSection(Sec))
- RelRo.add(Sec);
+ // Current dynamic loaders only support one PT_GNU_RELRO PHDR, give
+ // an error message if more than one PT_GNU_RELRO PHDR is required.
+ PhdrEntry *RelRo = make<PhdrEntry>(PT_GNU_RELRO, PF_R);
+ bool InRelroPhdr = false;
+ bool IsRelroFinished = false;
+ for (OutputSection *Sec : OutputSections) {
+ if (!needsPtLoad(Sec))
+ continue;
+ if (isRelroSection(Sec)) {
+ InRelroPhdr = true;
+ if (!IsRelroFinished)
+ RelRo->add(Sec);
+ else
+ error("section: " + Sec->Name + " is not contiguous with other relro" +
+ " sections");
+ } else if (InRelroPhdr) {
+ InRelroPhdr = false;
+ IsRelroFinished = true;
+ }
}
- if (RelRo.First)
- Ret.push_back(std::move(RelRo));
+ 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 &&
- In<ELFT>::EhFrame->getParent() && In<ELFT>::EhFrameHdr->getParent())
- AddHdr(PT_GNU_EH_FRAME, In<ELFT>::EhFrameHdr->getParent()->getPhdrFlags())
- ->add(In<ELFT>::EhFrameHdr->getParent());
+ if (!InX::EhFrame->empty() && InX::EhFrameHdr && InX::EhFrame->getParent() &&
+ InX::EhFrameHdr->getParent())
+ AddHdr(PT_GNU_EH_FRAME, InX::EhFrameHdr->getParent()->getPhdrFlags())
+ ->add(InX::EhFrameHdr->getParent());
// 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
@@ -1524,10 +1697,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 || Cmd->LMAExpr)
+ if (!Note || Sec->LMAExpr)
Note = AddHdr(PT_NOTE, PF_R);
Note->add(Sec);
} else {
@@ -1538,18 +1710,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 = llvm::find_if(OutputSectionCommands, [](OutputSectionCommand *Cmd) {
- return Cmd->Sec->Type == SHT_ARM_EXIDX;
+ auto I = llvm::find_if(OutputSections, [](OutputSection *Cmd) {
+ return Cmd->Type == SHT_ARM_EXIDX;
});
- if (I == OutputSectionCommands.end())
+ 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);
}
@@ -1557,33 +1729,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);
}
}
@@ -1591,40 +1761,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);
}
@@ -1634,46 +1803,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);
}
}
}
@@ -1682,27 +1863,29 @@ template <class ELFT> void Writer<ELFT>::setPhdrs() {
//
// 1. the '-e' entry command-line option;
// 2. the ENTRY(symbol) command in a linker control script;
-// 3. the value of the symbol start, if present;
-// 4. the address of the first byte of the .text section, if present;
-// 5. the address 0.
+// 3. the value of the symbol _start, if present;
+// 4. the number represented by the entry symbol, if it is a number;
+// 5. the address of the first byte of the .text section, if present;
+// 6. the address 0.
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))
+ // Case 1, 2 or 3
+ if (Symbol *B = Symtab->find(Config->Entry))
return B->getVA();
+
+ // Case 4
uint64_t Addr;
if (to_integer(Config->Entry, Addr))
return Addr;
- // Case 4
- if (OutputSection *Sec = findSectionInScript(".text")) {
+ // Case 5
+ if (OutputSection *Sec = findSection(".text")) {
if (Config->WarnMissingEntry)
warn("cannot find entry symbol " + Config->Entry + "; defaulting to 0x" +
utohexstr(Sec->Addr));
return Sec->Addr;
}
- // Case 5
+ // Case 6
if (Config->WarnMissingEntry)
warn("cannot find entry symbol " + Config->Entry +
"; not setting start address");
@@ -1717,64 +1900,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);
@@ -1790,20 +1915,13 @@ template <class ELFT> void Writer<ELFT>::writeHeader() {
EHdr->e_version = EV_CURRENT;
EHdr->e_entry = getEntryAddr();
EHdr->e_shoff = SectionHeaderOff;
+ EHdr->e_flags = Config->EFlags;
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)
- // We don't currently use any features incompatible with EF_ARM_EABI_VER5,
- // but we don't have any firm guarantees of conformance. Linux AArch64
- // kernels (as of 2016) require an EABI version to be set.
- EHdr->e_flags = EF_ARM_EABI_VER5;
- else if (Config->EMachine == EM_MIPS)
- EHdr->e_flags = getMipsEFlags<ELFT>();
-
if (!Config->Relocatable) {
EHdr->e_phoff = sizeof(Elf_Ehdr);
EHdr->e_phentsize = sizeof(Elf_Phdr);
@@ -1811,22 +1929,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.
@@ -1837,23 +1955,58 @@ template <class ELFT> void Writer<ELFT>::openFile() {
}
unlinkAsync(Config->OutputFile);
- ErrorOr<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
- FileOutputBuffer::create(Config->OutputFile, FileSize,
- FileOutputBuffer::F_executable);
+ unsigned Flags = 0;
+ if (!Config->Relocatable)
+ Flags = FileOutputBuffer::F_executable;
+ Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
+ FileOutputBuffer::create(Config->OutputFile, FileSize, Flags);
- if (auto EC = BufferOrErr.getError())
- error("failed to open " + Config->OutputFile + ": " + EC.message());
+ if (!BufferOrErr)
+ error("failed to open " + Config->OutputFile + ": " +
+ llvm::toString(BufferOrErr.takeError()));
else
Buffer = std::move(*BufferOrErr);
}
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 tools don't accidentally
+ // trim the instruction padding (e.g. when stripping the file).
+ PhdrEntry *Last = nullptr;
+ for (PhdrEntry *P : Phdrs)
+ if (P->p_type == PT_LOAD)
+ Last = P;
+
+ if (Last && (Last->p_flags & PF_X))
+ Last->p_memsz = Last->p_filesz = alignTo(Last->p_filesz, Target->PageSize);
}
// Write section contents to a mmap'ed file.
@@ -1862,39 +2015,32 @@ 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);
}
- OutputSection *EhFrameHdr =
- (In<ELFT>::EhFrameHdr && !In<ELFT>::EhFrameHdr->empty())
- ? In<ELFT>::EhFrameHdr->getParent()
- : nullptr;
+ OutputSection *EhFrameHdr = nullptr;
+ if (InX::EhFrameHdr && !InX::EhFrameHdr->empty())
+ EhFrameHdr = InX::EhFrameHdr->getParent();
// 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() {
@@ -1911,3 +2057,8 @@ template void elf::writeResult<ELF32LE>();
template void elf::writeResult<ELF32BE>();
template void elf::writeResult<ELF64LE>();
template void elf::writeResult<ELF64BE>();
+
+template void elf::addReservedSymbols<ELF32LE>();
+template void elf::addReservedSymbols<ELF32BE>();
+template void elf::addReservedSymbols<ELF64LE>();
+template void elf::addReservedSymbols<ELF64BE>();
diff --git a/ELF/Writer.h b/ELF/Writer.h
index 7fa56bea1c35..32d3c23047dd 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,19 +41,22 @@ 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> void addReservedSymbols();
+llvm::StringRef getOutputSectionName(InputSectionBase *S);
-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 isMicroMips();
+bool isMipsR6();
} // namespace elf
} // namespace lld
diff --git a/MinGW/CMakeLists.txt b/MinGW/CMakeLists.txt
new file mode 100644
index 000000000000..bb0fe4a3887d
--- /dev/null
+++ b/MinGW/CMakeLists.txt
@@ -0,0 +1,23 @@
+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
+ lldCommon
+
+ DEPENDS
+ MinGWOptionsTableGen
+ ${tablegen_deps}
+)
diff --git a/MinGW/Driver.cpp b/MinGW/Driver.cpp
new file mode 100644
index 000000000000..6d3bea5d9040
--- /dev/null
+++ b/MinGW/Driver.cpp
@@ -0,0 +1,247 @@
+//===- 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 "lld/Common/ErrorHandler.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;
+
+// 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)
+ fatal(StringRef(Args.getArgString(MissingIndex)) + ": missing argument");
+ for (auto *Arg : Args.filtered(OPT_UNKNOWN))
+ fatal("unknown argument: " + Arg->getSpelling());
+ if (!Args.hasArg(OPT_INPUT) && !Args.hasArg(OPT_l))
+ fatal("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;
+ fatal("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;
+ }
+ fatal("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_image_base))
+ Add("-base:" + 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 (!Args.hasArg(OPT_strip_all))
+ Add("-debug:dwarf");
+ if (Args.hasArg(OPT_large_address_aware))
+ Add("-largeaddressaware");
+
+ if (Args.getLastArgValue(OPT_m) != "thumb2pe" &&
+ Args.getLastArgValue(OPT_m) != "arm64pe" && !Args.hasArg(OPT_dynamicbase))
+ Add("-dynamicbase:no");
+
+ if (Args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false))
+ Add("-opt:ref");
+ else
+ Add("-opt:noref");
+
+ if (auto *A = Args.getLastArg(OPT_icf)) {
+ StringRef S = A->getValue();
+ if (S == "all")
+ Add("-opt:icf");
+ else if (S == "safe" || S == "none")
+ Add("-opt:noicf");
+ else
+ fatal("unknown parameter: --icf=" + S);
+ } else {
+ Add("-opt:noicf");
+ }
+
+ 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
+ fatal("unknown parameter: -m" + S);
+ }
+
+ for (auto *A : Args.filtered(OPT_mllvm))
+ Add("-mllvm:" + StringRef(A->getValue()));
+
+ for (auto *A : Args.filtered(OPT_Xlink))
+ Add(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, true);
+}
diff --git a/MinGW/Options.td b/MinGW/Options.td
new file mode 100644
index 000000000000..f3bf25a2258b
--- /dev/null
+++ b/MinGW/Options.td
@@ -0,0 +1,68 @@
+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 dynamicbase: F<"dynamicbase">, HelpText<"Enable ASLR">;
+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 gc_sections: F<"gc-sections">, HelpText<"Remove unused sections">;
+def icf: J<"icf=">, HelpText<"Identical code folding">;
+def image_base: S<"image-base">, HelpText<"Base address of the program">;
+def l: JoinedOrSeparate<["-"], "l">, MetaVarName<"<libName>">,
+ HelpText<"Root name of library to use">;
+def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">;
+def no_whole_archive: F<"no-whole-archive">,
+ HelpText<"No longer include all object files for following archives">;
+def large_address_aware: Flag<["--"], "large-address-aware">,
+ HelpText<"Enable large addresses">;
+def no_gc_sections: F<"no-gc-sections">, HelpText<"Don't remove unused sections">;
+def o: JoinedOrSeparate<["-"], "o">, MetaVarName<"<path>">,
+ HelpText<"Path to file to write output">;
+def out_implib: Separate<["--"], "out-implib">, HelpText<"Import library name">;
+def out_implib_eq: Joined<["--"], "out-implib=">, Alias<out_implib>;
+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 strip_all: F<"strip-all">,
+ HelpText<"Omit all symbol information from the output binary">;
+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">;
+def mllvm: S<"mllvm">;
+def Xlink : J<"Xlink=">, MetaVarName<"<arg>">,
+ HelpText<"Pass <arg> to the COFF linker">;
+
+// 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 build_id: F<"build-id">;
+def disable_auto_image_base: F<"disable-auto-image-base">;
+def enable_auto_image_base: F<"enable-auto-image-base">;
+def enable_auto_import: F<"enable-auto-import">;
+def full_shutdown: Flag<["--"], "full-shutdown">;
+def high_entropy_va: F<"high-entropy-va">, HelpText<"Enable 64-bit ASLR">;
+def major_image_version: S<"major-image-version">;
+def minor_image_version: S<"minor-image-version">;
+def no_seh: F<"no-seh">;
+def nxcompat: F<"nxcompat">, HelpText<"Enable data execution prevention">;
+def pic_executable: F<"pic-executable">;
+def sysroot: J<"sysroot">, HelpText<"Sysroot">;
+def tsaware: F<"tsaware">, HelpText<"Create Terminal Server aware executable">;
+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>;
+def alias_strip_s: Flag<["-"], "s">, Alias<strip_all>;
diff --git a/README.md b/README.md
index 38a6b48b1841..34372a4c3ad5 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,5 @@
-
LLVM Linker (lld)
-==============================
+=================
This directory and its subdirectories contain source code for the LLVM Linker, a
modular cross platform linker which is built as part of the LLVM compiler
@@ -9,3 +8,12 @@ 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 69cff27cf63304a3f766e72dc9010407eced5c60635254a3c31496e1183ef9e1.
diff --git a/cmake/modules/AddLLD.cmake b/cmake/modules/AddLLD.cmake
index fd1d44199ca6..0d951799cdfe 100644
--- a/cmake/modules/AddLLD.cmake
+++ b/cmake/modules/AddLLD.cmake
@@ -25,11 +25,9 @@ macro(add_lld_library name)
RUNTIME DESTINATION bin)
if (${ARG_SHARED} AND NOT CMAKE_CONFIGURATION_TYPES)
- add_custom_target(install-${name}
+ add_llvm_install_targets(install-${name}
DEPENDS ${name}
- COMMAND "${CMAKE_COMMAND}"
- -DCMAKE_INSTALL_COMPONENT=${name}
- -P "${CMAKE_BINARY_DIR}/cmake_install.cmake")
+ COMPONENT ${name})
endif()
set_property(GLOBAL APPEND PROPERTY LLD_EXPORTS ${name})
endif()
@@ -60,11 +58,9 @@ macro(add_lld_tool name)
COMPONENT ${name})
if(NOT CMAKE_CONFIGURATION_TYPES)
- add_custom_target(install-${name}
+ add_llvm_install_targets(install-${name}
DEPENDS ${name}
- COMMAND "${CMAKE_COMMAND}"
- -DCMAKE_INSTALL_COMPONENT=${name}
- -P "${CMAKE_BINARY_DIR}/cmake_install.cmake")
+ COMPONENT ${name})
endif()
set_property(GLOBAL APPEND PROPERTY LLD_EXPORTS ${name})
endif()
diff --git a/docs/Driver.rst b/docs/Driver.rst
index 27b378712a6c..4ee6ce0c985f 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 67f6b368b0e4..afdb41e0a145 100644
--- a/docs/NewLLD.rst
+++ b/docs/NewLLD.rst
@@ -1,5 +1,5 @@
-The ELF and COFF Linkers
-========================
+The ELF, COFF and Wasm Linkers
+==============================
The ELF Linker as a Library
---------------------------
@@ -33,11 +33,12 @@ between speed, simplicity and extensibility.
We implemented the linkers as native linkers for each file format.
- The two linkers share the same design but do not share code.
+ The linkers share the same design but share very little code.
Sharing code makes sense if the benefit is worth its cost.
- In our case, ELF and COFF are different enough that we thought the layer to
- abstract the differences wouldn't worth its complexity and run-time cost.
- Elimination of the abstract layer has greatly simplified the implementation.
+ In our case, the object formats are different enough that we thought the layer
+ to abstract the differences wouldn't be worth its complexity and run-time
+ cost. Elimination of the abstract layer has greatly simplified the
+ implementation.
* Speed by design
@@ -57,59 +58,61 @@ between speed, simplicity and extensibility.
* Efficient archive file handling
- LLD's handling of archive files (the files with ".a" file extension) is different
- from the traditional Unix linkers and similar to Windows linkers.
- We'll describe how the traditional Unix linker handles archive files,
- what the problem is, and how LLD approached the problem.
+ LLD's handling of archive files (the files with ".a" file extension) is
+ different from the traditional Unix linkers and similar to Windows linkers.
+ We'll describe how the traditional Unix linker handles archive files, what the
+ problem is, and how LLD approached the problem.
- The traditional Unix linker maintains a set of undefined symbols during linking.
- The linker visits each file in the order as they appeared in the command line
- until the set becomes empty. What the linker would do depends on file type.
+ The traditional Unix linker maintains a set of undefined symbols during
+ linking. The linker visits each file in the order as they appeared in the
+ command line until the set becomes empty. What the linker would do depends on
+ file type.
- - If the linker visits an object file, the linker links object files to the result,
- and undefined symbols in the object file are added to the set.
+ - If the linker visits an object file, the linker links object files to the
+ result, and undefined symbols in the object file are added to the set.
- - If the linker visits an archive file, it checks for the archive file's symbol table
- and extracts all object files that have definitions for any symbols in the set.
+ - If the linker visits an archive file, it checks for the archive file's
+ symbol table and extracts all object files that have definitions for any
+ symbols in the set.
- This algorithm sometimes leads to a counter-intuitive behavior.
- If you give archive files before object files, nothing will happen
- because when the linker visits archives, there is no undefined symbols in the set.
- As a result, no files are extracted from the first archive file,
- and the link is done at that point because the set is empty after it visits one file.
+ This algorithm sometimes leads to a counter-intuitive behavior. If you give
+ archive files before object files, nothing will happen because when the linker
+ visits archives, there is no undefined symbols in the set. As a result, no
+ files are extracted from the first archive file, and the link is done at that
+ point because the set is empty after it visits one file.
You can fix the problem by reordering the files,
but that cannot fix the issue of mutually-dependent archive files.
- Linking mutually-dependent archive files is tricky.
- You may specify the same archive file multiple times to
- let the linker visit it more than once.
- Or, you may use the special command line options, `--start-group` and `--end-group`,
- to let the linker loop over the files between the options until
+ Linking mutually-dependent archive files is tricky. You may specify the same
+ archive file multiple times to let the linker visit it more than once. Or,
+ you may use the special command line options, `--start-group` and
+ `--end-group`, to let the linker loop over the files between the options until
no new symbols are added to the set.
Visiting the same archive files multiple makes the linker slower.
- Here is how LLD approaches the problem. Instead of memorizing only undefined symbols,
- we program LLD so that it memorizes all symbols.
- When it sees an undefined symbol that can be resolved by extracting an object file
- from an archive file it previously visited, it immediately extracts the file and link it.
- It is doable because LLD does not forget symbols it have seen in archive files.
+ Here is how LLD approaches the problem. Instead of memorizing only undefined
+ symbols, we program LLD so that it memorizes all symbols. When it sees an
+ undefined symbol that can be resolved by extracting an object file from an
+ archive file it previously visited, it immediately extracts the file and link
+ it. It is doable because LLD does not forget symbols it have seen in archive
+ files.
We believe that the LLD's way is efficient and easy to justify.
- The semantics of LLD's archive handling is different from the traditional Unix's.
- You can observe it if you carefully craft archive files to exploit it.
- However, in reality, we don't know any program that cannot link
- with our algorithm so far, so it's not going to cause trouble.
+ The semantics of LLD's archive handling is different from the traditional
+ Unix's. You can observe it if you carefully craft archive files to exploit
+ it. However, in reality, we don't know any program that cannot link with our
+ algorithm so far, so it's not going to cause trouble.
Numbers You Want to Know
------------------------
To give you intuition about what kinds of data the linker is mainly working on,
I'll give you the list of objects and their numbers LLD has to read and process
-in order to link a very large executable. In order to link Chrome with debug info,
-which is roughly 2 GB in output size, LLD reads
+in order to link a very large executable. In order to link Chrome with debug
+info, which is roughly 2 GB in output size, LLD reads
- 17,000 files,
- 1,800,000 sections,
@@ -136,17 +139,17 @@ when handling files.
Important Data Structures
-------------------------
-We will describe the key data structures in LLD in this section.
-The linker can be understood as the interactions between them.
-Once you understand their functions, the code of the linker should look obvious to you.
+We will describe the key data structures in LLD in this section. The linker can
+be understood as the interactions between them. Once you understand their
+functions, the code of the linker should look obvious to you.
-* SymbolBody
+* Symbol
- SymbolBody is a class to represent symbols.
+ This class represents a symbol.
They are created for symbols in object files or archive files.
The linker creates linker-defined symbols as well.
- There are basically three types of SymbolBodies: Defined, Undefined, or Lazy.
+ There are basically three types of Symbols: Defined, Undefined, or Lazy.
- Defined symbols are for all symbols that are considered as "resolved",
including real defined symbols, COMDAT symbols, common symbols,
@@ -156,33 +159,25 @@ Once you understand their functions, the code of the linker should look obvious
- Lazy symbols represent symbols we found in archive file headers
which can turn into Defined if we read archieve members.
-* Symbol
+ There's only one Symbol instance for each unique symbol name. This uniqueness
+ is guaranteed by the symbol table. As the resolver reads symbols from input
+ files, it replaces an existing Symbol with the "best" Symbol for its symbol
+ name using the placement new.
- A Symbol is a container for a SymbolBody. There's only one Symbol for each
- unique symbol name (this uniqueness is guaranteed by the symbol table).
- Each global symbol has only one SymbolBody at any one time, which is
- the SymbolBody stored within a memory region of the Symbol large enough
- to store any SymbolBody.
-
- As the resolver reads symbols from input files, it replaces the Symbol's
- SymbolBody with the "best" SymbolBody for its symbol name by constructing
- the new SymbolBody in place on top of the existing SymbolBody. For example,
- if the resolver is given a defined symbol, and the SymbolBody with its name
- is undefined, it will construct a Defined SymbolBody over the Undefined
- SymbolBody.
-
- This means that each SymbolBody pointer always points to the best SymbolBody,
- and it is possible to get from a SymbolBody to a Symbol, or vice versa,
- by adding or subtracting a fixed offset. This memory layout helps reduce
- the cache miss rate through high locality and a small number of required
- pointer indirections.
+ The above mechanism allows you to use pointers to Symbols as a very cheap way
+ to access name resolution results. Assume for example that you have a pointer
+ to an undefined symbol before name resolution. If the symbol is resolved to a
+ defined symbol by the resolver, the pointer will "automatically" point to the
+ defined symbol, because the undefined symbol the pointer pointed to will have
+ been replaced by the defined symbol in-place.
* SymbolTable
SymbolTable is basically a hash table from strings to Symbols
with logic to resolve symbol conflicts. It resolves conflicts by symbol type.
- - If we add Defined and Undefined symbols, the symbol table will keep the former.
+ - If we add Defined and Undefined symbols, the symbol table will keep the
+ former.
- If we add Defined and Lazy symbols, it will keep the former.
- If we add Lazy and Undefined, it will keep the former,
but it will also trigger the Lazy symbol to load the archive member
@@ -221,15 +216,14 @@ There are mainly three actors in this linker.
InputFile is a superclass of file readers.
We have a different subclass for each input file type,
such as regular object file, archive file, etc.
- They are responsible for creating and owning SymbolBodies and
- InputSections/Chunks.
+ They are responsible for creating and owning Symbols and InputSections/Chunks.
* Writer
- The writer is responsible for writing file headers and InputSections/Chunks to a file.
- It creates OutputSections, put all InputSections/Chunks into them,
- assign unique, non-overlapping addresses and file offsets to them,
- and then write them down to a file.
+ The writer is responsible for writing file headers and InputSections/Chunks to
+ a file. It creates OutputSections, put all InputSections/Chunks into them,
+ assign unique, non-overlapping addresses and file offsets to them, and then
+ write them down to a file.
* Driver
@@ -237,7 +231,8 @@ There are mainly three actors in this linker.
- processes command line options,
- creates a symbol table,
- - creates an InputFile for each input file and puts all symbols within into the symbol table,
+ - creates an InputFile for each input file and puts all symbols within into
+ the symbol table,
- checks if there's no remaining undefined symbols,
- creates a writer,
- and passes the symbol table to the writer to write the result to a file.
@@ -301,7 +296,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 01a984148b7e..dcd6ac0602d3 100644
--- a/docs/ReleaseNotes.rst
+++ b/docs/ReleaseNotes.rst
@@ -1,23 +1,22 @@
=======================
-lld 5.0.0 Release Notes
+LLD 6.0.0 Release Notes
=======================
.. contents::
:local:
+.. warning::
+ 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
============
-lld is a linker from the LLVM project. It supports ELF (Unix), COFF (Windows)
-and Mach-O (macOS), and it is generally faster than the GNU bfd or gold linkers
-or the MSVC linker.
-
-lld is designed to be a drop-in replacement for the system linkers, so that
-users don't need to change their build systems other than swapping the linker
-command.
-
-All lld releases may be downloaded from the `LLVM releases web site
-<http://llvm.org/releases/>`_.
+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/>`_.
Non-comprehensive list of changes in this release
=================================================
@@ -25,148 +24,14 @@ Non-comprehensive list of changes in this release
ELF Improvements
----------------
-* First and foremost, a lot of compatibility issues and bugs have been fixed.
- Linker script support has significantly improved. As a result, we believe you
- are very likely to be able to link your programs with lld without experiencing
- any problem now.
-
-* Error message format has changed in order to improve readability.
- Traditionally, linker's error messages are concise and arguably too terse.
- This is an example of lld 4.0.0's error message (they are actually in one line)::
-
- /ssd/clang/bin/ld.lld: error: /ssd/llvm-project/lld/ELF/Writer.cpp:207:
- undefined symbol 'lld::elf::EhFrameSection::addSection()'
-
- It is not easy to read because too much information is packed into a single line
- and the embedded text, particularly a symbol name, is sometimes too long.
- In lld 5.0.0, we use more vertical space to print out error messages in a more
- structured manner like this::
-
- bin/ld.lld: error: undefined symbol: lld::elf::EhFrameSection::addSection()
- >>> Referenced by Writer.cpp:207 (/ssd/llvm-project/lld/ELF/Writer.cpp:207)
- >>> Writer.cpp.o in archive lib/liblldELF.a
-
- As a bonus, the new error message contains source code location of the error
- if it is available from debug info.
-
-* ``./configure`` scripts generated by GNU autoconf determines whether a linker
- supports modern GNU-compatible features or not by searching for "GNU" in the
- ``--help`` message. To be compatible with the scripts, we decided to add a
- string "(compatible with GNU linkers)" to our ``--help`` message. This is a
- hack, but just like the web browser's User-Agent string (which everyone still
- claim they are "Mozilla/5.0"), we had no choice other than doing this to claim
- that we accept GNU-compatible options.
+* Item 1.
-* The ``-Map`` option is added. The option is to make the linker to print out how
- input files are mapped to the output file. Here is an example::
+COFF Improvements
+-----------------
- Address Size Align Out In Symbol
- 00000000016d84d8 00000000008f8f50 8 .eh_frame
- 00000000016d84d8 00000000008f8f50 8 <internal>:(.eh_frame)
- 0000000001fd2000 00000000034b3bd0 16 .text
- 0000000001fd2000 000000000000002a 1 /usr/lib/x86_64-linux-gnu/crt1.o:(.text)
- 0000000001fd2000 0000000000000000 0 _start
- 0000000001fd202a 0000000000000000 1 /usr/lib/x86_64-linux-gnu/crti.o:(.text)
- 0000000001fd2030 00000000000000bd 16 /usr/lib/gcc/x86_64-linux-gnu/4.8/crtbegin.o:(.text)
- 0000000001fd2030 0000000000000000 0 deregister_tm_clones
- 0000000001fd2060 0000000000000000 0 register_tm_clones
-
- This format is not the same as GNU linkers as our linker internal data
- structure is different from them but contains the same amount of information
- and should be more readable than their outputs.
-
- As with other lld features, the ``-Map`` option is designed with speed in mind.
- The option would generate a hundred megabyte text file if you link a large
- program with it. lld can usually do that in a few seconds, and it is generally
- a few times faster than the GNU gold's ``-Map`` option.
-
-* lld's ``--gdb-index`` option used to be slow, but we sped it up so that it is
- at least as fast as the GNU gold.
-
-* Some nonstandard relocations, such as R_X86_64_8 or R_X86_64_16, are supported.
- They are not used for 32/64-bit applications, but some 16-bit bootloaders need
- them.
-
-* Paddings in executable text sections are now filled with trap instructions
- (such as INT3) instead of being left as null bytes. This change improves
- disassembler outputs because it now prints out trap instructions instead of
- trying to decode 0x00 as an instruction. It also makes debugging of some type
- of program easier because when the control reaches a padding, the program
- immediately raises an error.
-
-* The following options are added: ``-M``, ``-Map``,
- ``-compress-debug-sections``, ``-emit-relocs``,
- ``-error-unresolved-symbols``, ``-exclude-libs``, ``-filter``,
- ``-no-dynamic-linker``, ``-no-export-dynamic``, ``-no-fatal-warnings``,
- ``-print-map``, ``-warn-unresolved-symbols``, ``-z nocopyreloc``,
- ``-z notext``, ``-z rodynamic``
-
-
-Contributors to lld 5.0
-=======================
+* Item 1.
-We had 63 individuals contribute to lld 5.0. Thank you so much!
+MachO Improvements
+------------------
-- Adrian McCarthy
-- Alberto Magni
-- Alexander Richardson
-- Andre Vieira
-- Andrew Ng
-- Anton Korobeynikov
-- Bob Haarman
-- David Blaikie
-- Davide Italiano
-- David L. Jones
-- Dmitry Mikulin
-- Ed Maste
-- Ed Schouten
-- Eric Beckmann
-- Eric Fiselier
-- Eugene Leviant
-- Evgeniy Stepanov
-- Galina Kistanova
-- George Rimar
-- Hans Wennborg
-- Igor Kudrin
-- Ismail Donmez
-- Jake Ehrlich
-- James Henderson
-- Joel Jones
-- Jon Chesterfield
-- Kamil Rytarowski
-- Kevin Enderby
-- Konstantin Zhuravlyov
-- Kyungwoo Lee
-- Leslie Zhai
-- Mark Kettenis
-- Martell Malone
-- Martin Storsjo
-- Meador Inge
-- Mehdi Amini
-- Michal Gorny
-- NAKAMURA Takumi
-- Paul Robinson
-- Pavel Labath
-- Petar Jovanovic
-- Peter Collingbourne
-- Peter Smith
-- Petr Hosek
-- Rafael Espindola
-- Reid Kleckner
-- Richard Smith
-- Robert Clarke
-- Rui Ueyama
-- Saleem Abdulrasool
-- Sam Clegg
-- Sean Eveson
-- Sean Silva
-- Shankar Easwaran
-- Shoaib Meenai
-- Simon Atanasyan
-- Simon Dardis
-- Simon Tatham
-- Sylvestre Ledru
-- Tom Stellard
-- Vitaly Buka
-- Yuka Takahashi
-- Zachary Turner
+* Item 1.
diff --git a/docs/WebAssembly.rst b/docs/WebAssembly.rst
new file mode 100644
index 000000000000..1df8fa3a82b0
--- /dev/null
+++ b/docs/WebAssembly.rst
@@ -0,0 +1,36 @@
+WebAssembly lld port
+====================
+
+Note: The WebAssembly port is still a work in progress and is be lacking
+certain features.
+
+The WebAssembly version of lld takes WebAssembly binaries as inputs and produces
+a WebAssembly binary as its output. For the most part this port tried to mimic
+the behaviour of traditional ELF linkers and specifically the ELF lld port.
+Where possible that command line flags and the semantics should be the same.
+
+
+Object file format
+------------------
+
+The format the input object files that lld expects is specified as part of the
+the WebAssembly tool conventions
+https://github.com/WebAssembly/tool-conventions/blob/master/Linking.md.
+
+This is object format that the llvm will produce when run with the
+``wasm32-unknown-unknown-wasm`` target. To build llvm with WebAssembly support
+currently requires enabling the experimental backed using
+``-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly``.
+
+
+Missing features
+----------------
+
+There are several key features that are not yet implement in the WebAssembly
+ports:
+
+- COMDAT support. This means that support for C++ is still very limited.
+- Function stripping. Currently there is no support for ``--gc-sections`` so
+ functions and data from a given object will linked as a unit.
+- Section start/end symbols. The synthetic symbols that mark the start and
+ of data regions are not yet created in the output file.
diff --git a/docs/_templates/indexsidebar.html b/docs/_templates/indexsidebar.html
index 61968f22d5c5..588be9309bde 100644
--- a/docs/_templates/indexsidebar.html
+++ b/docs/_templates/indexsidebar.html
@@ -1,4 +1,4 @@
<h3>Bugs</h3>
<p>lld bugs should be reported at the
- LLVM <a href="http://llvm.org/bugs">Bugzilla</a>.</p>
+ LLVM <a href="https://bugs.llvm.org/">Bugzilla</a>.</p>
diff --git a/docs/conf.py b/docs/conf.py
index 410b33e98ace..28e16f5b8856 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 fbdade7daf27..f4bf3be9bf29 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -5,13 +5,14 @@ LLD is a linker from the LLVM project. That is a drop-in replacement
for system linkers and runs much faster than them. It also provides
features that are useful for toolchain developers.
-The linker supports ELF (Unix), PE/COFF (Windows) and Mach-O (macOS)
-in descending order of completeness. Internally, LLD consists of three
-different linkers. The ELF port is the one that will be described in
-this document. The PE/COFF port is almost complete except the lack of
-the Windows debug info (PDB) support. The Mach-O port is built based
-on a different architecture than the ELF or COFF ports. For the
-details about Mach-O, please read :doc:`AtomLLD`.
+The linker supports ELF (Unix), PE/COFF (Windows), Mach-O (macOS) and
+WebAssembly in descending order of completeness. Internally, LLD consists of
+several different linkers. The ELF port is the one that will be described in
+this document. The PE/COFF port is almost complete except the lack of the
+Windows debug info (PDB) support. The WebAssembly port is still a work in
+progress (See :doc:`WebAssembly`). The Mach-O port is built based on a
+different architecture than the others. For the details about Mach-O, please
+read :doc:`AtomLLD`.
Features
--------
@@ -71,30 +72,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 +108,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
@@ -175,5 +173,6 @@ document soon.
NewLLD
AtomLLD
+ WebAssembly
windows_support
ReleaseNotes
diff --git a/docs/sphinx_intro.rst b/docs/sphinx_intro.rst
index 6845bc812e78..6bb9816b5ab4 100644
--- a/docs/sphinx_intro.rst
+++ b/docs/sphinx_intro.rst
@@ -57,41 +57,21 @@ to install it using:
Building the documentation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-In order to build the documentation, all you should need to do is change to the
-``docs`` directory and invoke make as follows::
-
- $ cd path/to/project/docs
- $ make html
-
-Note that on Windows there is a ``make.bat`` command in the docs directory which
-supplies the same interface as the ``Makefile``.
-
-That command will invoke ``sphinx-build`` with the appropriate options for the
-project, and generate the HTML documentation in a ``_build`` subdirectory. You
-can browse it starting from the index page by visiting
-``_build/html/index.html``.
-
-Sphinx supports a wide variety of generation formats (including LaTeX, man
-pages, and plain text). The ``Makefile`` includes a number of convenience
-targets for invoking ``sphinx-build`` appropriately, the common ones are:
-
- make html
- Generate the HTML output.
-
- make latexpdf
- Generate LaTeX documentation and convert to a PDF.
-
- make man
- Generate man pages.
+In order to build the documentation need to add ``-DLLVM_ENABLE_SPHINX=ON`` to
+your ``cmake`` command. Once you do this you can build the docs using
+``docs-lld-html`` build (``ninja`` or ``make``) target.
+That build target will invoke ``sphinx-build`` with the appropriate options for
+the project, and generate the HTML documentation in a ``tools/lld/docs/html``
+subdirectory.
.. _writing_documentation:
Writing documentation
~~~~~~~~~~~~~~~~~~~~~
-The documentation itself is written in the reStructuredText (ReST) format, and Sphinx
-defines additional tags to support features like cross-referencing.
+The documentation itself is written in the reStructuredText (ReST) format, and
+Sphinx defines additional tags to support features like cross-referencing.
The ReST format itself is organized around documents mostly being readable
plaintext documents. You should generally be able to write new documentation
diff --git a/include/lld/Common/Args.h b/include/lld/Common/Args.h
new file mode 100644
index 000000000000..c49a6a7e17e7
--- /dev/null
+++ b/include/lld/Common/Args.h
@@ -0,0 +1,35 @@
+//===- Args.h ---------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_ARGS_H
+#define LLD_ARGS_H
+
+#include "lld/Common/LLVM.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include <vector>
+
+namespace llvm {
+namespace opt {
+class InputArgList;
+}
+} // namespace llvm
+
+namespace lld {
+namespace args {
+int getInteger(llvm::opt::InputArgList &Args, unsigned Key, int Default);
+std::vector<StringRef> getStrings(llvm::opt::InputArgList &Args, int Id);
+
+uint64_t getZOptionValue(llvm::opt::InputArgList &Args, int Id, StringRef Key,
+ uint64_t Default);
+
+std::vector<StringRef> getLines(MemoryBufferRef MB);
+} // namespace args
+} // namespace lld
+
+#endif
diff --git a/include/lld/Driver/Driver.h b/include/lld/Common/Driver.h
index 4ba0994e88b9..15ec3cd44cb5 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,14 +7,19 @@
//
//===----------------------------------------------------------------------===//
-#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"
namespace lld {
namespace coff {
+bool link(llvm::ArrayRef<const char *> Args, bool CanExitEarly,
+ llvm::raw_ostream &Diag = llvm::errs());
+}
+
+namespace mingw {
bool link(llvm::ArrayRef<const char *> Args,
llvm::raw_ostream &Diag = llvm::errs());
}
@@ -28,6 +33,11 @@ namespace mach_o {
bool link(llvm::ArrayRef<const char *> Args,
llvm::raw_ostream &Diag = llvm::errs());
}
+
+namespace wasm {
+bool link(llvm::ArrayRef<const char *> Args, bool CanExitEarly,
+ llvm::raw_ostream &Diag = llvm::errs());
+}
}
#endif
diff --git a/include/lld/Common/ErrorHandler.h b/include/lld/Common/ErrorHandler.h
new file mode 100644
index 000000000000..8ae6f46ac59e
--- /dev/null
+++ b/include/lld/Common/ErrorHandler.h
@@ -0,0 +1,112 @@
+//===- ErrorHandler.h -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// In LLD, we have three levels of errors: fatal, error or warn.
+//
+// Fatal makes the program exit immediately with an error message.
+// You shouldn't use it except for reporting a corrupted input file.
+//
+// Error prints out an error message and increment a global variable
+// ErrorCount to record the fact that we met an error condition. It does
+// not exit, so it is safe for a lld-as-a-library use case. It is generally
+// useful because it can report more than one error in a single run.
+//
+// Warn doesn't do anything but printing out a given message.
+//
+// It is not recommended to use llvm::outs() or llvm::errs() directly
+// in LLD because they are not thread-safe. The functions declared in
+// this file are mutually excluded, so you want to use them instead.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_COMMON_ERRORHANDLER_H
+#define LLD_COMMON_ERRORHANDLER_H
+
+#include "lld/Common/LLVM.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileOutputBuffer.h"
+
+namespace lld {
+
+class ErrorHandler {
+public:
+ uint64_t ErrorCount = 0;
+ uint64_t ErrorLimit = 20;
+ StringRef ErrorLimitExceededMsg = "too many errors emitted, stopping now";
+ StringRef LogName = "lld";
+ llvm::raw_ostream *ErrorOS = &llvm::errs();
+ bool ColorDiagnostics = llvm::errs().has_colors();
+ bool ExitEarly = true;
+ bool FatalWarnings = false;
+ bool Verbose = false;
+
+ void error(const Twine &Msg);
+ LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg);
+ void log(const Twine &Msg);
+ void message(const Twine &Msg);
+ void warn(const Twine &Msg);
+
+ std::unique_ptr<llvm::FileOutputBuffer> OutputBuffer;
+
+private:
+ void print(StringRef S, raw_ostream::Colors C);
+};
+
+/// Returns the default error handler.
+ErrorHandler &errorHandler();
+
+inline void error(const Twine &Msg) { errorHandler().error(Msg); }
+inline LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg) {
+ errorHandler().fatal(Msg);
+}
+inline void log(const Twine &Msg) { errorHandler().log(Msg); }
+inline void message(const Twine &Msg) { errorHandler().message(Msg); }
+inline void warn(const Twine &Msg) { errorHandler().warn(Msg); }
+inline uint64_t errorCount() { return errorHandler().ErrorCount; }
+
+LLVM_ATTRIBUTE_NORETURN void exitLld(int Val);
+
+// check functions are convenient functions to strip errors
+// from error-or-value objects.
+template <class T> T check(ErrorOr<T> E) {
+ if (auto EC = E.getError())
+ fatal(EC.message());
+ return std::move(*E);
+}
+
+template <class T> T check(Expected<T> E) {
+ if (!E)
+ fatal(llvm::toString(E.takeError()));
+ return std::move(*E);
+}
+
+template <class T>
+T check2(ErrorOr<T> E, llvm::function_ref<std::string()> Prefix) {
+ if (auto EC = E.getError())
+ fatal(Prefix() + ": " + EC.message());
+ return std::move(*E);
+}
+
+template <class T>
+T check2(Expected<T> E, llvm::function_ref<std::string()> Prefix) {
+ if (!E)
+ fatal(Prefix() + ": " + toString(E.takeError()));
+ return std::move(*E);
+}
+
+inline std::string toString(const Twine &S) { return S.str(); }
+
+// To evaluate the second argument lazily, we use C macro.
+#define CHECK(E, S) check2(E, [&] { return toString(S); })
+
+} // namespace lld
+
+#endif
diff --git a/include/lld/Core/LLVM.h b/include/lld/Common/LLVM.h
index ccf08859f4ae..b5d0e2bffb03 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/ELF/Memory.h b/include/lld/Common/Memory.h
index 4000f2f9f1c9..699f7c1654cd 100644
--- a/ELF/Memory.h
+++ b/include/lld/Common/Memory.h
@@ -17,24 +17,23 @@
// Arena allocators are efficient and easy to understand.
// Most objects are allocated using the arena allocators defined by this file.
//
-// If you edit this file, please edit COFF/Memory.h too.
-//
//===----------------------------------------------------------------------===//
-#ifndef LLD_ELF_MEMORY_H
-#define LLD_ELF_MEMORY_H
+#ifndef LLD_COMMON_MEMORY_H
+#define LLD_COMMON_MEMORY_H
#include "llvm/Support/Allocator.h"
#include "llvm/Support/StringSaver.h"
#include <vector>
namespace lld {
-namespace elf {
// Use this arena if your object doesn't have a destructor.
extern llvm::BumpPtrAllocator BAlloc;
extern llvm::StringSaver Saver;
+void freeArena();
+
// These two classes are hack to keep track of all
// SpecificBumpPtrAllocator instances.
struct SpecificAllocBase {
@@ -56,12 +55,6 @@ template <typename T, typename... U> T *make(U &&... Args) {
return new (Alloc.Alloc.Allocate()) T(std::forward<U>(Args)...);
}
-inline void freeArena() {
- for (SpecificAllocBase *Alloc : SpecificAllocBase::Instances)
- Alloc->reset();
- BAlloc.Reset();
-}
-} // namespace elf
} // namespace lld
#endif
diff --git a/include/lld/Core/Reproduce.h b/include/lld/Common/Reproduce.h
index 6e1d36a54916..0f425de269c7 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"
@@ -33,7 +33,7 @@ std::string quote(StringRef S);
std::string rewritePath(StringRef S);
// Returns the string form of the given argument.
-std::string toString(llvm::opt::Arg *Arg);
+std::string toString(const llvm::opt::Arg &Arg);
}
#endif
diff --git a/include/lld/Common/Strings.h b/include/lld/Common/Strings.h
new file mode 100644
index 000000000000..1a63f75f9ecf
--- /dev/null
+++ b/include/lld/Common/Strings.h
@@ -0,0 +1,23 @@
+//===- Strings.h ------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_STRINGS_H
+#define LLD_STRINGS_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+
+namespace lld {
+// Returns a demangled C++ symbol name. If Name is not a mangled
+// name, it returns Optional::None.
+llvm::Optional<std::string> demangleItanium(llvm::StringRef Name);
+}
+
+#endif
diff --git a/include/lld/Core/TargetOptionsCommandFlags.h b/include/lld/Common/TargetOptionsCommandFlags.h
index 9ba99d94b957..9c4ff7cea3fb 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 9feb8683976c..854590753143 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,31 @@
//
//===----------------------------------------------------------------------===//
-#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);
}
-} // namespace elf
+
} // namespace lld
#endif
diff --git a/include/lld/Config/Version.h b/include/lld/Common/Version.h
index 1cec3cc7678c..93de77df5804 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 2789a5c46089..2789a5c46089 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 156a5d4a736f..149c3d5ee2c5 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 7f623d2ea5e6..6229d67e25a5 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 b0bf73b1cb7b..36a36724987a 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/LinkingContext.h b/include/lld/Core/LinkingContext.h
index b3a999b00fbd..eb9510cbd215 100644
--- a/include/lld/Core/LinkingContext.h
+++ b/include/lld/Core/LinkingContext.h
@@ -62,7 +62,7 @@ public:
/// of DefinedAtoms that should be marked live (along with all Atoms they
/// reference). Only Atoms with scope scopeLinkageUnit or scopeGlobal can
/// be kept live using this method.
- const std::vector<StringRef> &deadStripRoots() const {
+ ArrayRef<StringRef> deadStripRoots() const {
return _deadStripRoots;
}
@@ -106,7 +106,7 @@ public:
/// options which are used to configure LLVM's command line settings.
/// For instance the -debug-only XXX option can be used to dynamically
/// trace different parts of LLVM and lld.
- const std::vector<const char *> &llvmOptions() const { return _llvmOptions; }
+ ArrayRef<const char *> llvmOptions() const { return _llvmOptions; }
/// \name Methods used by Drivers to configure TargetInfo
/// @{
diff --git a/include/lld/Core/PassManager.h b/include/lld/Core/PassManager.h
index 09b417a2985d..2ea65ae13ace 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 32d04249f378..c7baf86af61f 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 ba4951e5bd13..9c39a6ed507c 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 216f934916bc..1f0ca4cda41f 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 b26161a15431..b97d21f68e55 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>
@@ -18,6 +18,7 @@
namespace lld {
class File;
class LinkingContext;
+class Registry;
namespace mach_o {
namespace normalized {
struct NormalizedFile;
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 699f5e93f8af..8884efcfe9ba 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 3e142b66f578..000000000000
--- 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 85046b93f34f..2d4d9ded0886 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 e7cfaaac7835..9c51c6cdb19c 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 583c65acb5d3..51ae8d17181d 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 be75872869e6..097a8177ea1f 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 f564d8cb8d7e..a019e9c32800 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"
@@ -74,7 +74,7 @@ enum {
#undef PREFIX
// Create table mapping all options defined in DarwinLdOptions.td
-static const llvm::opt::OptTable::Info infoTable[] = {
+static const llvm::opt::OptTable::Info InfoTable[] = {
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR, VALUES) \
{PREFIX, NAME, HELPTEXT, \
@@ -88,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 8751d569b754..bedb836d2c1e 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 762d0871db06..04c0bee76989 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 6028006ca9d9..80840b561701 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"
diff --git a/lib/ReaderWriter/MachO/CMakeLists.txt b/lib/ReaderWriter/MachO/CMakeLists.txt
index 5a96d87f1f7a..f2fc34772496 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 49d518456a45..1e210409237f 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/FlatNamespaceFile.h b/lib/ReaderWriter/MachO/FlatNamespaceFile.h
index 76d295841c9d..7ccd4f19f834 100644
--- a/lib/ReaderWriter/MachO/FlatNamespaceFile.h
+++ b/lib/ReaderWriter/MachO/FlatNamespaceFile.h
@@ -10,7 +10,9 @@
#ifndef LLD_READER_WRITER_MACHO_FLAT_NAMESPACE_FILE_H
#define LLD_READER_WRITER_MACHO_FLAT_NAMESPACE_FILE_H
+#include "Atoms.h"
#include "lld/Core/SharedLibraryFile.h"
+#include "lld/ReaderWriter/MachOLinkingContext.h"
#include "llvm/Support/Debug.h"
namespace lld {
diff --git a/lib/ReaderWriter/MachO/GOTPass.cpp b/lib/ReaderWriter/MachO/GOTPass.cpp
index 8458a1c79282..49e6f88d4aa4 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 7e7b559b150a..4ef7a62a8297 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 31b24dfd1025..7eeb8adbd84f 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"
@@ -124,15 +124,6 @@ struct Section {
ArrayRef<uint8_t> content;
Relocations relocations;
IndirectSymbols indirectSymbols;
-
-#ifndef NDEBUG
- raw_ostream& operator<<(raw_ostream &OS) const {
- dump(OS);
- return OS;
- }
-
- void dump(raw_ostream &OS = llvm::dbgs()) const;
-#endif
};
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp
index b54054726dfe..7c2e833c090f 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"
@@ -511,7 +511,8 @@ readBinary(std::unique_ptr<MemoryBuffer> &mb,
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));
- for (const ExportEntry &trieExport : MachOObjectFile::exports(trie)) {
+ 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 b38f7059228f..407bd9b97020 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 bac41d2a52bf..7ef0237e8c36 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"
@@ -1523,10 +1523,10 @@ llvm::Error MachOFileLayout::writeBinary(StringRef path) {
unsigned flags = 0;
if (_file.fileType != llvm::MachO::MH_OBJECT)
flags = llvm::FileOutputBuffer::F_executable;
- ErrorOr<std::unique_ptr<llvm::FileOutputBuffer>> fobOrErr =
+ Expected<std::unique_ptr<llvm::FileOutputBuffer>> fobOrErr =
llvm::FileOutputBuffer::create(path, size(), flags);
- if (std::error_code ec = fobOrErr.getError())
- return llvm::errorCodeToError(ec);
+ if (Error E = fobOrErr.takeError())
+ return E;
std::unique_ptr<llvm::FileOutputBuffer> &fob = *fobOrErr;
// Write content.
_buffer = fob->getBufferStart();
@@ -1535,7 +1535,8 @@ llvm::Error MachOFileLayout::writeBinary(StringRef path) {
return ec;
writeSectionContent();
writeLinkEditContent();
- fob->commit();
+ if (Error E = fob->commit())
+ return E;
return llvm::Error::success();
}
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp
index f2e5ed781678..e93ca86c3164 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"
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp
index 226a25d3c1ed..3b07a40f9bf2 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"
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp
index 5233e42e5fc5..92a646dab5e0 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"
@@ -257,7 +257,7 @@ template <> struct ScalarTraits<SectionAlignment> {
return StringRef(); // returning empty string means success
}
- static bool mustQuote(StringRef) { return false; }
+ static QuotingType mustQuote(StringRef) { return QuotingType::None; }
};
template <>
@@ -522,7 +522,7 @@ struct ScalarTraits<VMProtect> {
// Return the empty string on success,
return StringRef();
}
- static bool mustQuote(StringRef) { return false; }
+ static QuotingType mustQuote(StringRef) { return QuotingType::None; }
};
@@ -706,7 +706,7 @@ struct ScalarTraits<PackedVersion> {
// Return the empty string on success,
return StringRef();
}
- static bool mustQuote(StringRef) { return false; }
+ static QuotingType mustQuote(StringRef) { return QuotingType::None; }
};
template <>
diff --git a/lib/ReaderWriter/MachO/ObjCPass.cpp b/lib/ReaderWriter/MachO/ObjCPass.cpp
index ccea081f053d..23c71e0f5ecd 100644
--- a/lib/ReaderWriter/MachO/ObjCPass.cpp
+++ b/lib/ReaderWriter/MachO/ObjCPass.cpp
@@ -13,9 +13,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 "lld/ReaderWriter/MachOLinkingContext.h"
diff --git a/lib/ReaderWriter/MachO/ShimPass.cpp b/lib/ReaderWriter/MachO/ShimPass.cpp
index ff559d70eabe..8a2d2e910cad 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 19e2bc592f5c..04c586df336c 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/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp b/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp
index 4c88eb1d1476..59548684e677 100644
--- a/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp
+++ b/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp
@@ -280,7 +280,7 @@ template <> struct ScalarTraits<RefKind> {
return StringRef("unknown reference kind");
}
- static bool mustQuote(StringRef) { return false; }
+ static QuotingType mustQuote(StringRef) { return QuotingType::None; }
};
template <> struct ScalarEnumerationTraits<lld::File::Kind> {
@@ -495,7 +495,7 @@ template <> struct ScalarTraits<lld::DefinedAtom::Alignment> {
return StringRef(); // returning empty string means success
}
- static bool mustQuote(StringRef) { return false; }
+ static QuotingType mustQuote(StringRef) { return QuotingType::None; }
};
template <> struct ScalarEnumerationTraits<FileKinds> {
@@ -552,7 +552,7 @@ template <> struct ScalarTraits<ImplicitHex8> {
return StringRef(); // returning empty string means success
}
- static bool mustQuote(StringRef) { return false; }
+ static QuotingType mustQuote(StringRef) { return QuotingType::None; }
};
// YAML conversion for std::vector<const lld::File*>
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 1dbd1b7699ea..eb8475881de8 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 000000000000..53fc36beee68
--- /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 000000000000..ff98618d884f
--- /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/except_handler3.lib b/test/COFF/Inputs/except_handler3.lib
new file mode 100644
index 000000000000..fdc51ed73285
--- /dev/null
+++ b/test/COFF/Inputs/except_handler3.lib
Binary files differ
diff --git a/test/COFF/Inputs/far-arm-thumb-abs20.s b/test/COFF/Inputs/far-arm-thumb-abs20.s
new file mode 100644
index 000000000000..8483e32dc7f0
--- /dev/null
+++ b/test/COFF/Inputs/far-arm-thumb-abs20.s
@@ -0,0 +1,2 @@
+.global too_far20
+too_far20 = 0x501004
diff --git a/test/COFF/Inputs/gamma.ll b/test/COFF/Inputs/gamma.ll
new file mode 100644
index 000000000000..1c78fff2909d
--- /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/library2-arm64.lib b/test/COFF/Inputs/library2-arm64.lib
new file mode 100644
index 000000000000..6fedf06f7be5
--- /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 000000000000..d1906010cd30
--- /dev/null
+++ b/test/COFF/Inputs/library2.def
@@ -0,0 +1,3 @@
+LIBRARY library2
+EXPORTS
+ function2
diff --git a/test/COFF/Inputs/locally-imported-def.s b/test/COFF/Inputs/locally-imported-def.s
new file mode 100644
index 000000000000..60a0970e9702
--- /dev/null
+++ b/test/COFF/Inputs/locally-imported-def.s
@@ -0,0 +1,4 @@
+.text
+.globl f
+f:
+ ret
diff --git a/test/COFF/Inputs/locally-imported-imp.s b/test/COFF/Inputs/locally-imported-imp.s
new file mode 100644
index 000000000000..33f2d54f617f
--- /dev/null
+++ b/test/COFF/Inputs/locally-imported-imp.s
@@ -0,0 +1,2 @@
+.text
+ call __imp_f
diff --git a/test/COFF/Inputs/lto-cache.ll b/test/COFF/Inputs/lto-cache.ll
new file mode 100644
index 000000000000..acbee7be0144
--- /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/pdb-globals.yaml b/test/COFF/Inputs/pdb-globals.yaml
new file mode 100644
index 000000000000..98ef914d9041
--- /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-hashes-1.yaml b/test/COFF/Inputs/pdb-hashes-1.yaml
new file mode 100644
index 000000000000..ad9e69188f51
--- /dev/null
+++ b/test/COFF/Inputs/pdb-hashes-1.yaml
@@ -0,0 +1,540 @@
+--- !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: 5589E55683EC188B450C8B4D088D55F4C745F8000000008B7508894DF089D18934248945ECE80000000083EC048D4DF4890C248945E8E80000000083C4185E5DC3
+ Relocations:
+ - VirtualAddress: 38
+ SymbolName: '??0Foo@NS@@QAE@H@Z'
+ Type: IMAGE_REL_I386_REL32
+ - VirtualAddress: 55
+ SymbolName: '?func@NS@@YAHABUFoo@1@@Z'
+ Type: IMAGE_REL_I386_REL32
+ - 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: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 5589E583EC088B4508894DFC8B4DFC8B550889118945F889C883C4085DC20400
+ - 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: 04000000F10000002F0000002D003C1101000000070006000000000000007017000000000000636C616E672076657273696F6E20362E302E30200000F50000008400000000000000000000004100000000000000080000000000000052000000070000000400000001000000400000000000000008000000000000007F0000000600040000000000030000003E000000000000000800000000000000BD0000000400040000000000040000003D000000000000000800000000000000FA0000000300080000000000F1000000960000002A00471100000000000000000000000041000000000000000000000003100000000000000000006D61696E000D003E1174000000010061726763001200451116000000080000001700000000002A000D003E11001000000100617267760012004511160000000C0000001700000000002A000A003E1109100000000066001200451116000000F4FFFFFF1700000000002A0002004F110000F200000030000000000000000000000041000000000000000300000024000000000000000300000017000000040000003000000005000000F1000000100000000E000811091000004E533A3A466F6F00F40000003000000001000000100165C9E387F88362A8EB2B49539DD5A65500002B00000010019303CF100D518DAF59C31DA01FEF4AFC0000F30000004801000000443A5C7372635C6C6C766D6275696C645C636C616E675C44656275675C7838365C6F626A312E63707000443A5C7372635C6C6C766D6275696C645C636C616E675C44656275675C7838365C6F626A2E6800245430202E7261536561726368203D202465697020245430205E203D2024657370202454302034202B203D2000245430202E7261536561726368203D202465697020245430205E203D2024657370202454302034202B203D2024656270202454302034202D205E203D200024543020246562702034202B203D202465697020245430205E203D2024657370202454302034202B203D2024656270202454302034202D205E203D200024543020246562702034202B203D202465697020245430205E203D2024657370202454302034202B203D2024656270202454302034202D205E203D2024657369202454302038202D205E203D2000
+ 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 '
+ - !FrameData
+ Frames:
+ - CodeSize: 65
+ FrameFunc: '$T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = '
+ LocalSize: 0
+ MaxStackSize: 0
+ ParamsSize: 8
+ PrologSize: 7
+ RvaStart: 0
+ SavedRegsSize: 0
+ - CodeSize: 64
+ FrameFunc: '$T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = '
+ LocalSize: 0
+ MaxStackSize: 0
+ ParamsSize: 8
+ PrologSize: 6
+ RvaStart: 1
+ SavedRegsSize: 4
+ - CodeSize: 62
+ FrameFunc: '$T0 $ebp 4 + = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = '
+ LocalSize: 0
+ MaxStackSize: 0
+ ParamsSize: 8
+ PrologSize: 4
+ RvaStart: 3
+ SavedRegsSize: 4
+ - CodeSize: 61
+ FrameFunc: '$T0 $ebp 4 + = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = $esi $T0 8 - ^ = '
+ LocalSize: 0
+ MaxStackSize: 0
+ ParamsSize: 8
+ PrologSize: 3
+ RvaStart: 4
+ SavedRegsSize: 8
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ CodeSize: 65
+ DbgStart: 0
+ DbgEnd: 0
+ FunctionType: 4099
+ Flags: [ ]
+ DisplayName: main
+ - Kind: S_LOCAL
+ LocalSym:
+ Type: 116
+ Flags: [ IsParameter ]
+ VarName: argc
+ - Kind: S_DEFRANGE_REGISTER_REL
+ DefRangeRegisterRelSym:
+ - Kind: S_LOCAL
+ LocalSym:
+ Type: 4096
+ Flags: [ IsParameter ]
+ VarName: argv
+ - Kind: S_DEFRANGE_REGISTER_REL
+ DefRangeRegisterRelSym:
+ - Kind: S_LOCAL
+ LocalSym:
+ Type: 4105
+ Flags: [ ]
+ VarName: f
+ - Kind: S_DEFRANGE_REGISTER_REL
+ DefRangeRegisterRelSym:
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 65
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'D:\src\llvmbuild\clang\Debug\x86\obj1.cpp'
+ Lines:
+ - Offset: 0
+ LineStart: 3
+ IsStatement: false
+ EndDelta: 0
+ - Offset: 23
+ LineStart: 4
+ IsStatement: false
+ EndDelta: 0
+ - Offset: 48
+ LineStart: 5
+ IsStatement: false
+ EndDelta: 0
+ Columns:
+ - !Symbols
+ Records:
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4105
+ UDTName: 'NS::Foo'
+ - !FileChecksums
+ Checksums:
+ - FileName: 'D:\src\llvmbuild\clang\Debug\x86\obj1.cpp'
+ Kind: MD5
+ Checksum: 65C9E387F88362A8EB2B49539DD5A655
+ - FileName: 'D:\src\llvmbuild\clang\Debug\x86\obj.h'
+ Kind: MD5
+ Checksum: 9303CF100D518DAF59C31DA01FEF4AFC
+ - !StringTable
+ Strings:
+ - 'D:\src\llvmbuild\clang\Debug\x86\obj1.cpp'
+ - 'D:\src\llvmbuild\clang\Debug\x86\obj.h'
+ - '$T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = '
+ - '$T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = '
+ - '$T0 $ebp 4 + = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = '
+ - '$T0 $ebp 4 + = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = $esi $T0 8 - ^ = '
+ Relocations:
+ - VirtualAddress: 68
+ SymbolName: _main
+ Type: IMAGE_REL_I386_DIR32NB
+ - VirtualAddress: 240
+ SymbolName: _main
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 244
+ SymbolName: _main
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 279
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 283
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 314
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 318
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 346
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 350
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 368
+ SymbolName: _main
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 372
+ SymbolName: _main
+ 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: 040000000A000210700400000A8000000E0001120200000074000000001000000E0008107400000000000200011000001200011600000000021000006D61696E00F3F2F12A0005150000800200000000000000000000000000004E533A3A466F6F002E3F4155466F6F404E53404000F10A000210041000000A8000000A00011201000000740000001A0009100300000004100000051000000B00010006100000000000001A0003120D15030074000000000058001115030007100000466F6F002A0005150200000208100000000000000000000004004E533A3A466F6F002E3F4155466F6F404E53404000F12E00051600000000443A5C7372635C6C6C766D6275696C645C636C616E675C44656275675C7838365C6F626A2E6800F10E000616091000000A100000020000000E0002160410000007100000466F6F00
+ Types:
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 1136
+ Attrs: 32778
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 116, 4096 ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 116
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 2
+ ArgumentList: 4097
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4098
+ Name: main
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: 'NS::Foo'
+ UniqueName: '.?AUFoo@NS@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4100
+ Attrs: 32778
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 116 ]
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4100
+ ThisType: 4101
+ CallConv: ThisCall
+ Options: [ None ]
+ ParameterCount: 1
+ ArgumentList: 4102
+ ThisPointerAdjustment: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 116
+ FieldOffset: 0
+ Name: X
+ - Kind: LF_ONEMETHOD
+ OneMethod:
+ Type: 4103
+ Attrs: 3
+ VFTableOffset: -1
+ Name: Foo
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 2
+ Options: [ None, HasUniqueName ]
+ FieldList: 4104
+ Name: 'NS::Foo'
+ UniqueName: '.?AUFoo@NS@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 4
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'D:\src\llvmbuild\clang\Debug\x86\obj.h'
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4105
+ SourceFile: 4106
+ LineNumber: 2
+ - Kind: LF_MFUNC_ID
+ MemberFuncId:
+ ClassType: 4100
+ FunctionType: 4103
+ Name: Foo
+ - Name: '.debug$H'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: C5C93301000000009E56666824DC4B12E25261D4E09E6E9DA0F4EE31FDEC3D2D96287486127C66070B248ED52E421F55074AE5CC2D68AF9F0A3BEF23993968F7FD82CA84BF0439C1A64C9070C6A6ADB0A34D21DAD0FFC3E99E616EF06A14EA74A2420F9062A1FB04917E5975E3A50EABE5E8FE3945468547C19DC681D0BFB3B797DD91CA4D7F1953C314442D5549419E78044E38A0BF16BFFAA5EE9C0103E7DBFE9941E63379C0B0C0A9021B711ACC4F67008974EBF441031BDD653F6935DFF3112C6A5346EF2AC94B9B7EB56EF55CFA0AF6C1846743F43D846BB19517E12E8873BBA90CC41DD1BEAC89CBA8897AC1BA46762E2557A82D894CEAE81AEF8680D723D403D9A4481F0E28683A98
+ GlobalHashes:
+ Version: 0
+ HashAlgorithm: 0
+ HashValues:
+ - 9E56666824DC4B12E25261D4E09E6E9DA0F4EE31
+ - FDEC3D2D96287486127C66070B248ED52E421F55
+ - 074AE5CC2D68AF9F0A3BEF23993968F7FD82CA84
+ - BF0439C1A64C9070C6A6ADB0A34D21DAD0FFC3E9
+ - 9E616EF06A14EA74A2420F9062A1FB04917E5975
+ - E3A50EABE5E8FE3945468547C19DC681D0BFB3B7
+ - 97DD91CA4D7F1953C314442D5549419E78044E38
+ - A0BF16BFFAA5EE9C0103E7DBFE9941E63379C0B0
+ - C0A9021B711ACC4F67008974EBF441031BDD653F
+ - 6935DFF3112C6A5346EF2AC94B9B7EB56EF55CFA
+ - 0AF6C1846743F43D846BB19517E12E8873BBA90C
+ - C41DD1BEAC89CBA8897AC1BA46762E2557A82D89
+ - 4CEAE81AEF8680D723D403D9A4481F0E28683A98
+ - Name: '.debug$S'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: 04000000F500000064000000000000000000000020000000000000000400000000000000520000000600000004000000010000001F0000000000000004000000000000007F0000000500040000000000030000001D000000000000000400000000000000BD0000000300040000000000F10000007B000000320047110000000000000000000000002000000000000000000000000C100000000000000000004E533A3A466F6F3A3A466F6F000D003E1105100000010074686973001200451116000000FCFFFFFF0F000000000011000A003E1174000000010078001200451116000000080000000F0000000000110002004F1100F2000000200000000000000000000000200000001800000001000000140000000000000003000000
+ Subsections:
+ - !FrameData
+ Frames:
+ - CodeSize: 32
+ FrameFunc: '$T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = '
+ LocalSize: 0
+ MaxStackSize: 0
+ ParamsSize: 4
+ PrologSize: 6
+ RvaStart: 0
+ SavedRegsSize: 0
+ - CodeSize: 31
+ FrameFunc: '$T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = '
+ LocalSize: 0
+ MaxStackSize: 0
+ ParamsSize: 4
+ PrologSize: 5
+ RvaStart: 1
+ SavedRegsSize: 4
+ - CodeSize: 29
+ FrameFunc: '$T0 $ebp 4 + = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = '
+ LocalSize: 0
+ MaxStackSize: 0
+ ParamsSize: 4
+ PrologSize: 3
+ RvaStart: 3
+ SavedRegsSize: 4
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ CodeSize: 32
+ DbgStart: 0
+ DbgEnd: 0
+ FunctionType: 4108
+ Flags: [ ]
+ DisplayName: 'NS::Foo::Foo'
+ - Kind: S_LOCAL
+ LocalSym:
+ Type: 4101
+ Flags: [ IsParameter ]
+ VarName: this
+ - Kind: S_DEFRANGE_REGISTER_REL
+ DefRangeRegisterRelSym:
+ - Kind: S_LOCAL
+ LocalSym:
+ Type: 116
+ Flags: [ IsParameter ]
+ VarName: x
+ - Kind: S_DEFRANGE_REGISTER_REL
+ DefRangeRegisterRelSym:
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 32
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'D:\src\llvmbuild\clang\Debug\x86\obj.h'
+ Lines:
+ - Offset: 0
+ LineStart: 3
+ IsStatement: false
+ EndDelta: 0
+ Columns:
+ Relocations:
+ - VirtualAddress: 12
+ SymbolName: '??0Foo@NS@@QAE@H@Z'
+ Type: IMAGE_REL_I386_DIR32NB
+ - VirtualAddress: 152
+ SymbolName: '??0Foo@NS@@QAE@H@Z'
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 156
+ SymbolName: '??0Foo@NS@@QAE@H@Z'
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 199
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 203
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 231
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 235
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 252
+ SymbolName: '??0Foo@NS@@QAE@H@Z'
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 256
+ SymbolName: '??0Foo@NS@@QAE@H@Z'
+ 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: 65
+ NumberOfRelocations: 2
+ NumberOfLinenumbers: 0
+ CheckSum: 4176946275
+ Number: 1
+ - Name: .data
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 0
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 2
+ - Name: .bss
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 0
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 3
+ - Name: .text
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 32
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 1438182552
+ Number: 4
+ Selection: IMAGE_COMDAT_SELECT_ANY
+ - Name: '??0Foo@NS@@QAE@H@Z'
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: .drectve
+ Value: 0
+ SectionNumber: 5
+ 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: 5
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 6
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 832
+ NumberOfRelocations: 11
+ NumberOfLinenumbers: 0
+ CheckSum: 4106171226
+ Number: 6
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 9
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 284
+ NumberOfRelocations: 9
+ NumberOfLinenumbers: 0
+ CheckSum: 1378739251
+ Number: 4
+ Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE
+ - Name: '.debug$T'
+ Value: 0
+ SectionNumber: 7
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 316
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 3343977630
+ Number: 7
+ - Name: '.debug$H'
+ Value: 0
+ SectionNumber: 8
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 268
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 3965031229
+ 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: _main
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: '?func@NS@@YAHABUFoo@1@@Z'
+ 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-hashes-2-missing.yaml b/test/COFF/Inputs/pdb-hashes-2-missing.yaml
new file mode 100644
index 000000000000..74f3a62fcc23
--- /dev/null
+++ b/test/COFF/Inputs/pdb-hashes-2-missing.yaml
@@ -0,0 +1,321 @@
+--- !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: 5589E5508B45088B4D088B09C1E1018945FC89C883C4045DC3
+ - 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: .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: 04000000F10000002F0000002D003C1101000000070006000000000000007017000000000000636C616E672076657273696F6E20362E302E30200000F5000000640000000000000000000000190000000000000004000000000000002B000000040000000400000001000000180000000000000004000000000000005800000003000400000000000300000016000000000000000400000000000000960000000100040000000000F1000000540000002E0047110000000000000000000000001900000000000000000000000D100000000000000000004E533A3A66756E63000A003E110310000001006600120045111600000008000000070000000000120002004F11F20000002800000000000000000000001900000000000000020000001C00000000000000030000000700000004000000F1000000100000000E0008110A1000004E533A3A466F6F00F40000001800000001000000100159DFAC75D18675AED1AD169FE316317E0000F3000000D400000000443A5C7372635C6C6C766D6275696C645C636C616E675C44656275675C7838365C6F626A322E63707000245430202E7261536561726368203D202465697020245430205E203D2024657370202454302034202B203D2000245430202E7261536561726368203D202465697020245430205E203D2024657370202454302034202B203D2024656270202454302034202D205E203D200024543020246562702034202B203D202465697020245430205E203D2024657370202454302034202B203D2024656270202454302034202D205E203D200000
+ 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 '
+ - !FrameData
+ Frames:
+ - CodeSize: 25
+ FrameFunc: '$T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = '
+ LocalSize: 0
+ MaxStackSize: 0
+ ParamsSize: 4
+ PrologSize: 4
+ RvaStart: 0
+ SavedRegsSize: 0
+ - CodeSize: 24
+ FrameFunc: '$T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = '
+ LocalSize: 0
+ MaxStackSize: 0
+ ParamsSize: 4
+ PrologSize: 3
+ RvaStart: 1
+ SavedRegsSize: 4
+ - CodeSize: 22
+ FrameFunc: '$T0 $ebp 4 + = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = '
+ LocalSize: 0
+ MaxStackSize: 0
+ ParamsSize: 4
+ PrologSize: 1
+ RvaStart: 3
+ SavedRegsSize: 4
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ CodeSize: 25
+ DbgStart: 0
+ DbgEnd: 0
+ FunctionType: 4109
+ Flags: [ ]
+ DisplayName: 'NS::func'
+ - Kind: S_LOCAL
+ LocalSym:
+ Type: 4099
+ Flags: [ IsParameter ]
+ VarName: f
+ - Kind: S_DEFRANGE_REGISTER_REL
+ DefRangeRegisterRelSym:
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 25
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'D:\src\llvmbuild\clang\Debug\x86\obj2.cpp'
+ Lines:
+ - Offset: 0
+ LineStart: 3
+ IsStatement: false
+ EndDelta: 0
+ - Offset: 7
+ LineStart: 4
+ IsStatement: false
+ EndDelta: 0
+ Columns:
+ - !Symbols
+ Records:
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4106
+ UDTName: 'NS::Foo'
+ - !FileChecksums
+ Checksums:
+ - FileName: 'D:\src\llvmbuild\clang\Debug\x86\obj2.cpp'
+ Kind: MD5
+ Checksum: 59DFAC75D18675AED1AD169FE316317E
+ - !StringTable
+ Strings:
+ - 'D:\src\llvmbuild\clang\Debug\x86\obj2.cpp'
+ - '$T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = '
+ - '$T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = '
+ - '$T0 $ebp 4 + = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = '
+ - ''
+ Relocations:
+ - VirtualAddress: 68
+ SymbolName: '?func@NS@@YAHABUFoo@1@@Z'
+ Type: IMAGE_REL_I386_DIR32NB
+ - VirtualAddress: 208
+ SymbolName: '?func@NS@@YAHABUFoo@1@@Z'
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 212
+ SymbolName: '?func@NS@@YAHABUFoo@1@@Z'
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 248
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 252
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 268
+ SymbolName: '?func@NS@@YAHABUFoo@1@@Z'
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 272
+ SymbolName: '?func@NS@@YAHABUFoo@1@@Z'
+ 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: 040000000A000516000000004E5300F12A0005150000800200000000000000000000000000004E533A3A466F6F002E3F4155466F6F404E53404000F10A000110011000000100F2F10A000210021000002A8000000A00011201000000031000000E0008107400000000000100041000000A000210011000000A8000000A00011201000000740000001A0009100300000001100000061000000B00010007100000000000001A0003120D15030074000000000058001115030008100000466F6F002A0005150200000209100000000000000000000004004E533A3A466F6F002E3F4155466F6F404E53404000F12E00051600000000443A5C7372635C6C6C766D6275696C645C636C616E675C44656275675C7838365C6F626A2E6800F10E0006160A1000000B1000000200000012000116001000000510000066756E6300F3F2F1
+ Types:
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: NS
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: 'NS::Foo'
+ UniqueName: '.?AUFoo@NS@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_MODIFIER
+ Modifier:
+ ModifiedType: 4097
+ Modifiers: [ None, Const ]
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4098
+ Attrs: 32810
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4099 ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 116
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 1
+ ArgumentList: 4100
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4097
+ Attrs: 32778
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 116 ]
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4097
+ ThisType: 4102
+ CallConv: ThisCall
+ Options: [ None ]
+ ParameterCount: 1
+ ArgumentList: 4103
+ ThisPointerAdjustment: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 116
+ FieldOffset: 0
+ Name: X
+ - Kind: LF_ONEMETHOD
+ OneMethod:
+ Type: 4104
+ Attrs: 3
+ VFTableOffset: -1
+ Name: Foo
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 2
+ Options: [ None, HasUniqueName ]
+ FieldList: 4105
+ Name: 'NS::Foo'
+ UniqueName: '.?AUFoo@NS@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 4
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'D:\src\llvmbuild\clang\Debug\x86\obj.h'
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4106
+ SourceFile: 4107
+ LineNumber: 2
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 4096
+ FunctionType: 4101
+ Name: func
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 25
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 1820185021
+ Number: 1
+ - Name: .data
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 0
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 2
+ - Name: .bss
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 0
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 3
+ - Name: .drectve
+ Value: 0
+ SectionNumber: 4
+ 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: 4
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 5
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 584
+ NumberOfRelocations: 7
+ NumberOfLinenumbers: 0
+ CheckSum: 2847177244
+ Number: 5
+ - Name: '.debug$T'
+ Value: 0
+ SectionNumber: 6
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 320
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 2684556216
+ Number: 6
+ - Name: '@feat.00'
+ Value: 1
+ SectionNumber: -1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: '?func@NS@@YAHABUFoo@1@@Z'
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/Inputs/pdb-hashes-2.yaml b/test/COFF/Inputs/pdb-hashes-2.yaml
new file mode 100644
index 000000000000..63e9fc02ad5a
--- /dev/null
+++ b/test/COFF/Inputs/pdb-hashes-2.yaml
@@ -0,0 +1,355 @@
+--- !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: 5589E5508B45088B4D088B09C1E1018945FC89C883C4045DC3
+ - 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: .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: 04000000F10000002F0000002D003C1101000000070006000000000000007017000000000000636C616E672076657273696F6E20362E302E30200000F5000000640000000000000000000000190000000000000004000000000000002B000000040000000400000001000000180000000000000004000000000000005800000003000400000000000300000016000000000000000400000000000000960000000100040000000000F1000000540000002E0047110000000000000000000000001900000000000000000000000D100000000000000000004E533A3A66756E63000A003E110310000001006600120045111600000008000000070000000000120002004F11F20000002800000000000000000000001900000000000000020000001C00000000000000030000000700000004000000F1000000100000000E0008110A1000004E533A3A466F6F00F40000001800000001000000100159DFAC75D18675AED1AD169FE316317E0000F3000000D400000000443A5C7372635C6C6C766D6275696C645C636C616E675C44656275675C7838365C6F626A322E63707000245430202E7261536561726368203D202465697020245430205E203D2024657370202454302034202B203D2000245430202E7261536561726368203D202465697020245430205E203D2024657370202454302034202B203D2024656270202454302034202D205E203D200024543020246562702034202B203D202465697020245430205E203D2024657370202454302034202B203D2024656270202454302034202D205E203D200000
+ 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 '
+ - !FrameData
+ Frames:
+ - CodeSize: 25
+ FrameFunc: '$T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = '
+ LocalSize: 0
+ MaxStackSize: 0
+ ParamsSize: 4
+ PrologSize: 4
+ RvaStart: 0
+ SavedRegsSize: 0
+ - CodeSize: 24
+ FrameFunc: '$T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = '
+ LocalSize: 0
+ MaxStackSize: 0
+ ParamsSize: 4
+ PrologSize: 3
+ RvaStart: 1
+ SavedRegsSize: 4
+ - CodeSize: 22
+ FrameFunc: '$T0 $ebp 4 + = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = '
+ LocalSize: 0
+ MaxStackSize: 0
+ ParamsSize: 4
+ PrologSize: 1
+ RvaStart: 3
+ SavedRegsSize: 4
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ CodeSize: 25
+ DbgStart: 0
+ DbgEnd: 0
+ FunctionType: 4109
+ Flags: [ ]
+ DisplayName: 'NS::func'
+ - Kind: S_LOCAL
+ LocalSym:
+ Type: 4099
+ Flags: [ IsParameter ]
+ VarName: f
+ - Kind: S_DEFRANGE_REGISTER_REL
+ DefRangeRegisterRelSym:
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 25
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'D:\src\llvmbuild\clang\Debug\x86\obj2.cpp'
+ Lines:
+ - Offset: 0
+ LineStart: 3
+ IsStatement: false
+ EndDelta: 0
+ - Offset: 7
+ LineStart: 4
+ IsStatement: false
+ EndDelta: 0
+ Columns:
+ - !Symbols
+ Records:
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4106
+ UDTName: 'NS::Foo'
+ - !FileChecksums
+ Checksums:
+ - FileName: 'D:\src\llvmbuild\clang\Debug\x86\obj2.cpp'
+ Kind: MD5
+ Checksum: 59DFAC75D18675AED1AD169FE316317E
+ - !StringTable
+ Strings:
+ - 'D:\src\llvmbuild\clang\Debug\x86\obj2.cpp'
+ - '$T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = '
+ - '$T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = '
+ - '$T0 $ebp 4 + = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = '
+ - ''
+ Relocations:
+ - VirtualAddress: 68
+ SymbolName: '?func@NS@@YAHABUFoo@1@@Z'
+ Type: IMAGE_REL_I386_DIR32NB
+ - VirtualAddress: 208
+ SymbolName: '?func@NS@@YAHABUFoo@1@@Z'
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 212
+ SymbolName: '?func@NS@@YAHABUFoo@1@@Z'
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 248
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 252
+ SymbolName: .text
+ Type: IMAGE_REL_I386_SECTION
+ - VirtualAddress: 268
+ SymbolName: '?func@NS@@YAHABUFoo@1@@Z'
+ Type: IMAGE_REL_I386_SECREL
+ - VirtualAddress: 272
+ SymbolName: '?func@NS@@YAHABUFoo@1@@Z'
+ 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: 040000000A000516000000004E5300F12A0005150000800200000000000000000000000000004E533A3A466F6F002E3F4155466F6F404E53404000F10A000110011000000100F2F10A000210021000002A8000000A00011201000000031000000E0008107400000000000100041000000A000210011000000A8000000A00011201000000740000001A0009100300000001100000061000000B00010007100000000000001A0003120D15030074000000000058001115030008100000466F6F002A0005150200000209100000000000000000000004004E533A3A466F6F002E3F4155466F6F404E53404000F12E00051600000000443A5C7372635C6C6C766D6275696C645C636C616E675C44656275675C7838365C6F626A2E6800F10E0006160A1000000B1000000200000012000116001000000510000066756E6300F3F2F1
+ Types:
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: NS
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: 'NS::Foo'
+ UniqueName: '.?AUFoo@NS@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_MODIFIER
+ Modifier:
+ ModifiedType: 4097
+ Modifiers: [ None, Const ]
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4098
+ Attrs: 32810
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4099 ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 116
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 1
+ ArgumentList: 4100
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4097
+ Attrs: 32778
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 116 ]
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4097
+ ThisType: 4102
+ CallConv: ThisCall
+ Options: [ None ]
+ ParameterCount: 1
+ ArgumentList: 4103
+ ThisPointerAdjustment: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 116
+ FieldOffset: 0
+ Name: X
+ - Kind: LF_ONEMETHOD
+ OneMethod:
+ Type: 4104
+ Attrs: 3
+ VFTableOffset: -1
+ Name: Foo
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 2
+ Options: [ None, HasUniqueName ]
+ FieldList: 4105
+ Name: 'NS::Foo'
+ UniqueName: '.?AUFoo@NS@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 4
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'D:\src\llvmbuild\clang\Debug\x86\obj.h'
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4106
+ SourceFile: 4107
+ LineNumber: 2
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 4096
+ FunctionType: 4101
+ Name: func
+ - Name: '.debug$H'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: C5C9330100000000EC145CD76AEFE74E78880D531132B3BB8FFACEF79E616EF06A14EA74A2420F9062A1FB04917E59759949E334BA18509ED692F3C65CE242D8450EBC78B81B63AF8316DC324562EB9F0D4A0D708E8A25C263DB05943C19B84A36719E1E414DDA3EDBDF005322238D70F9058EEDC5C50EF11BC849618B51FD89E3A50EABE5E8FE3945468547C19DC681D0BFB3B797DD91CA4D7F1953C314442D5549419E78044E38A0BF16BFFAA5EE9C0103E7DBFE9941E63379C0B0C0A9021B711ACC4F67008974EBF441031BDD653F6935DFF3112C6A5346EF2AC94B9B7EB56EF55CFA0AF6C1846743F43D846BB19517E12E8873BBA90CC41DD1BEAC89CBA8897AC1BA46762E2557A82D89DCBC783AF285D9DBB672F67A81E36906B2038B57
+ GlobalHashes:
+ Version: 0
+ HashAlgorithm: 0
+ HashValues:
+ - EC145CD76AEFE74E78880D531132B3BB8FFACEF7
+ - 9E616EF06A14EA74A2420F9062A1FB04917E5975
+ - 9949E334BA18509ED692F3C65CE242D8450EBC78
+ - B81B63AF8316DC324562EB9F0D4A0D708E8A25C2
+ - 63DB05943C19B84A36719E1E414DDA3EDBDF0053
+ - 22238D70F9058EEDC5C50EF11BC849618B51FD89
+ - E3A50EABE5E8FE3945468547C19DC681D0BFB3B7
+ - 97DD91CA4D7F1953C314442D5549419E78044E38
+ - A0BF16BFFAA5EE9C0103E7DBFE9941E63379C0B0
+ - C0A9021B711ACC4F67008974EBF441031BDD653F
+ - 6935DFF3112C6A5346EF2AC94B9B7EB56EF55CFA
+ - 0AF6C1846743F43D846BB19517E12E8873BBA90C
+ - C41DD1BEAC89CBA8897AC1BA46762E2557A82D89
+ - DCBC783AF285D9DBB672F67A81E36906B2038B57
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 25
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 1820185021
+ Number: 1
+ - Name: .data
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 0
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 2
+ - Name: .bss
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 0
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 3
+ - Name: .drectve
+ Value: 0
+ SectionNumber: 4
+ 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: 4
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 5
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 584
+ NumberOfRelocations: 7
+ NumberOfLinenumbers: 0
+ CheckSum: 2847177244
+ Number: 5
+ - Name: '.debug$T'
+ Value: 0
+ SectionNumber: 6
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 320
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 2684556216
+ Number: 6
+ - Name: '.debug$H'
+ Value: 0
+ SectionNumber: 7
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 288
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 2348181452
+ Number: 7
+ - Name: '@feat.00'
+ Value: 1
+ SectionNumber: -1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: '?func@NS@@YAHABUFoo@1@@Z'
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/Inputs/pdb-scopes-a.yaml b/test/COFF/Inputs/pdb-scopes-a.yaml
index e9f4484c7060..e422a6241f27 100644
--- a/test/COFF/Inputs/pdb-scopes-a.yaml
+++ b/test/COFF/Inputs/pdb-scopes-a.yaml
@@ -1,8 +1,8 @@
--- !COFF
-header:
+header:
Machine: IMAGE_FILE_MACHINE_AMD64
Characteristics: [ ]
-sections:
+sections:
- Name: .drectve
Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ]
Alignment: 1
@@ -10,15 +10,15 @@ sections:
- Name: '.debug$S'
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
Alignment: 1
- Subsections:
+ Subsections:
- !Symbols
- Records:
+ Records:
- Kind: S_OBJNAME
- ObjNameSym:
+ ObjNameSym:
Signature: 0
ObjectName: 'C:\src\llvm-project\build\a.obj'
- Kind: S_COMPILE3
- Compile3Sym:
+ Compile3Sym:
Flags: [ SecurityChecks, HotPatch ]
Machine: X64
FrontendMajor: 19
@@ -31,9 +31,9 @@ sections:
BackendQFE: 1
Version: 'Microsoft (R) Optimizing Compiler'
- !Symbols
- Records:
+ Records:
- Kind: S_GPROC32_ID
- ProcSym:
+ ProcSym:
CodeSize: 5
DbgStart: 4
DbgEnd: 4
@@ -41,7 +41,7 @@ sections:
Flags: [ ]
DisplayName: g
- Kind: S_FRAMEPROC
- FrameProcSym:
+ FrameProcSym:
TotalFrameBytes: 0
PaddingFrameBytes: 0
OffsetToPadding: 0
@@ -50,30 +50,30 @@ sections:
SectionIdOfExceptionHandler: 0
Flags: [ AsynchronousExceptionHandling, OptimizedForSpeed ]
- Kind: S_REGREL32
- RegRelativeSym:
+ RegRelativeSym:
Offset: 8
Type: 116
Register: RSP
VarName: x
- Kind: S_PROC_ID_END
- ScopeEndSym:
+ ScopeEndSym:
- !Lines
CodeSize: 5
Flags: [ ]
RelocOffset: 0
RelocSegment: 0
- Blocks:
+ Blocks:
- FileName: 'c:\src\llvm-project\build\a.c'
- Lines:
+ Lines:
- Offset: 0
LineStart: 1
IsStatement: true
EndDelta: 0
- Columns:
+ Columns:
- !Symbols
- Records:
+ Records:
- Kind: S_GPROC32_ID
- ProcSym:
+ ProcSym:
CodeSize: 58
DbgStart: 8
DbgEnd: 53
@@ -81,7 +81,7 @@ sections:
Flags: [ ]
DisplayName: main
- Kind: S_FRAMEPROC
- FrameProcSym:
+ FrameProcSym:
TotalFrameBytes: 56
PaddingFrameBytes: 0
OffsetToPadding: 0
@@ -90,47 +90,47 @@ sections:
SectionIdOfExceptionHandler: 0
Flags: [ AsynchronousExceptionHandling, OptimizedForSpeed ]
- Kind: S_REGREL32
- RegRelativeSym:
+ RegRelativeSym:
Offset: 64
Type: 116
Register: RSP
VarName: argc
- Kind: S_BLOCK32
- BlockSym:
+ BlockSym:
CodeSize: 17
Offset: 15
BlockName: ''
- Kind: S_REGREL32
- RegRelativeSym:
+ RegRelativeSym:
Offset: 32
Type: 116
Register: RSP
VarName: x
- Kind: S_END
- ScopeEndSym:
+ ScopeEndSym:
- Kind: S_BLOCK32
- BlockSym:
+ BlockSym:
CodeSize: 17
Offset: 34
BlockName: ''
- Kind: S_REGREL32
- RegRelativeSym:
+ RegRelativeSym:
Offset: 36
Type: 116
Register: RSP
VarName: y
- Kind: S_END
- ScopeEndSym:
+ ScopeEndSym:
- Kind: S_PROC_ID_END
- ScopeEndSym:
+ ScopeEndSym:
- !Lines
CodeSize: 58
Flags: [ ]
RelocOffset: 0
RelocSegment: 0
- Blocks:
+ Blocks:
- FileName: 'c:\src\llvm-project\build\a.c'
- Lines:
+ Lines:
- Offset: 0
LineStart: 3
IsStatement: true
@@ -163,21 +163,21 @@ sections:
LineStart: 11
IsStatement: true
EndDelta: 0
- Columns:
+ Columns:
- !FileChecksums
- Checksums:
+ Checksums:
- FileName: 'c:\src\llvm-project\build\a.c'
Kind: MD5
Checksum: 7FA72225C3F5630316383BD8BCC3EF72
- !StringTable
- Strings:
+ Strings:
- 'c:\src\llvm-project\build\a.c'
- !Symbols
- Records:
+ Records:
- Kind: S_BUILDINFO
- BuildInfoSym:
+ BuildInfoSym:
BuildId: 4110
- Relocations:
+ Relocations:
- VirtualAddress: 152
SymbolName: g
Type: IMAGE_REL_AMD64_SECREL
@@ -217,78 +217,78 @@ sections:
- Name: '.debug$T'
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
Alignment: 1
- Types:
+ Types:
- Kind: LF_ARGLIST
- ArgList:
+ ArgList:
ArgIndices: [ 116 ]
- Kind: LF_PROCEDURE
- Procedure:
+ Procedure:
ReturnType: 3
CallConv: NearC
Options: [ None ]
ParameterCount: 1
ArgumentList: 4096
- Kind: LF_POINTER
- Pointer:
+ Pointer:
ReferentType: 4097
Attrs: 65548
- Kind: LF_FUNC_ID
- FuncId:
+ FuncId:
ParentScope: 0
FunctionType: 4097
Name: g
- Kind: LF_PROCEDURE
- Procedure:
+ Procedure:
ReturnType: 116
CallConv: NearC
Options: [ None ]
ParameterCount: 1
ArgumentList: 4096
- Kind: LF_FUNC_ID
- FuncId:
+ FuncId:
ParentScope: 0
FunctionType: 4100
Name: main
- Kind: LF_FUNC_ID
- FuncId:
+ FuncId:
ParentScope: 0
FunctionType: 4097
Name: f
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: 'C:\src\llvm-project\build'
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: 'C:\PROGRA~2\MICROS~1.0\VC\Bin\amd64\cl.exe'
- Kind: LF_STRING_ID
- StringId:
+ 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:
+ StringList:
StringIndices: [ 4105 ]
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 4106
String: ' -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\winrt -TC -X'
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: a.c
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: 'C:\src\llvm-project\build\vc140.pdb'
- Kind: LF_BUILDINFO
- 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:
+ Relocations:
- VirtualAddress: 44
SymbolName: f
Type: IMAGE_REL_AMD64_REL32
@@ -303,7 +303,7 @@ sections:
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
Alignment: 4
SectionData: 000000003A00000000000000
- Relocations:
+ Relocations:
- VirtualAddress: 0
SymbolName: '$LN5'
Type: IMAGE_REL_AMD64_ADDR32NB
@@ -313,14 +313,14 @@ sections:
- VirtualAddress: 8
SymbolName: '$unwind$main'
Type: IMAGE_REL_AMD64_ADDR32NB
-symbols:
+symbols:
- Name: .drectve
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 47
NumberOfRelocations: 0
NumberOfLinenumbers: 0
@@ -332,7 +332,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 628
NumberOfRelocations: 12
NumberOfLinenumbers: 0
@@ -344,7 +344,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 624
NumberOfRelocations: 0
NumberOfLinenumbers: 0
@@ -356,7 +356,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 74
NumberOfRelocations: 2
NumberOfLinenumbers: 0
@@ -392,7 +392,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 8
NumberOfRelocations: 0
NumberOfLinenumbers: 0
@@ -410,7 +410,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 12
NumberOfRelocations: 3
NumberOfLinenumbers: 0
diff --git a/test/COFF/Inputs/pdb-scopes-b.yaml b/test/COFF/Inputs/pdb-scopes-b.yaml
index 2839bf7e3538..b1c602143c3f 100644
--- a/test/COFF/Inputs/pdb-scopes-b.yaml
+++ b/test/COFF/Inputs/pdb-scopes-b.yaml
@@ -1,8 +1,8 @@
--- !COFF
-header:
+header:
Machine: IMAGE_FILE_MACHINE_AMD64
Characteristics: [ ]
-sections:
+sections:
- Name: .drectve
Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ]
Alignment: 1
@@ -10,15 +10,15 @@ sections:
- Name: '.debug$S'
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
Alignment: 1
- Subsections:
+ Subsections:
- !Symbols
- Records:
+ Records:
- Kind: S_OBJNAME
- ObjNameSym:
+ ObjNameSym:
Signature: 0
ObjectName: 'C:\src\llvm-project\build\b.obj'
- Kind: S_COMPILE3
- Compile3Sym:
+ Compile3Sym:
Flags: [ SecurityChecks, HotPatch ]
Machine: X64
FrontendMajor: 19
@@ -31,9 +31,9 @@ sections:
BackendQFE: 1
Version: 'Microsoft (R) Optimizing Compiler'
- !Symbols
- Records:
+ Records:
- Kind: S_GPROC32_ID
- ProcSym:
+ ProcSym:
CodeSize: 62
DbgStart: 8
DbgEnd: 57
@@ -41,7 +41,7 @@ sections:
Flags: [ ]
DisplayName: f
- Kind: S_FRAMEPROC
- FrameProcSym:
+ FrameProcSym:
TotalFrameBytes: 56
PaddingFrameBytes: 0
OffsetToPadding: 0
@@ -50,47 +50,47 @@ sections:
SectionIdOfExceptionHandler: 0
Flags: [ AsynchronousExceptionHandling, OptimizedForSpeed ]
- Kind: S_REGREL32
- RegRelativeSym:
+ RegRelativeSym:
Offset: 64
Type: 116
Register: RSP
VarName: x
- Kind: S_BLOCK32
- BlockSym:
+ BlockSym:
CodeSize: 20
Offset: 15
BlockName: ''
- Kind: S_REGREL32
- RegRelativeSym:
+ RegRelativeSym:
Offset: 32
Type: 116
Register: RSP
VarName: y
- Kind: S_END
- ScopeEndSym:
+ ScopeEndSym:
- Kind: S_BLOCK32
- BlockSym:
+ BlockSym:
CodeSize: 20
Offset: 37
BlockName: ''
- Kind: S_REGREL32
- RegRelativeSym:
+ RegRelativeSym:
Offset: 36
Type: 116
Register: RSP
VarName: w
- Kind: S_END
- ScopeEndSym:
+ ScopeEndSym:
- Kind: S_PROC_ID_END
- ScopeEndSym:
+ ScopeEndSym:
- !Lines
CodeSize: 62
Flags: [ ]
RelocOffset: 0
RelocSegment: 0
- Blocks:
+ Blocks:
- FileName: 'c:\src\llvm-project\build\b.c'
- Lines:
+ Lines:
- Offset: 0
LineStart: 2
IsStatement: true
@@ -123,21 +123,21 @@ sections:
LineStart: 10
IsStatement: true
EndDelta: 0
- Columns:
+ Columns:
- !FileChecksums
- Checksums:
+ Checksums:
- FileName: 'c:\src\llvm-project\build\b.c'
Kind: MD5
Checksum: 8E8C92DB46478902EBEAEBFCFF15A6E0
- !StringTable
- Strings:
+ Strings:
- 'c:\src\llvm-project\build\b.c'
- !Symbols
- Records:
+ Records:
- Kind: S_BUILDINFO
- BuildInfoSym:
+ BuildInfoSym:
BuildId: 4110
- Relocations:
+ Relocations:
- VirtualAddress: 152
SymbolName: f
Type: IMAGE_REL_AMD64_SECREL
@@ -165,76 +165,76 @@ sections:
- Name: '.debug$T'
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
Alignment: 1
- Types:
+ Types:
- Kind: LF_ARGLIST
- ArgList:
+ ArgList:
ArgIndices: [ 0 ]
- Kind: LF_PROCEDURE
- Procedure:
+ Procedure:
ReturnType: 3
CallConv: NearC
Options: [ None ]
ParameterCount: 0
ArgumentList: 4096
- Kind: LF_POINTER
- Pointer:
+ Pointer:
ReferentType: 4097
Attrs: 65548
- Kind: LF_ARGLIST
- ArgList:
+ ArgList:
ArgIndices: [ 116 ]
- Kind: LF_PROCEDURE
- Procedure:
+ Procedure:
ReturnType: 3
CallConv: NearC
Options: [ None ]
ParameterCount: 1
ArgumentList: 4099
- Kind: LF_FUNC_ID
- FuncId:
+ FuncId:
ParentScope: 0
FunctionType: 4100
Name: f
- Kind: LF_FUNC_ID
- FuncId:
+ FuncId:
ParentScope: 0
FunctionType: 4097
Name: g
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: 'C:\src\llvm-project\build'
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: 'C:\PROGRA~2\MICROS~1.0\VC\Bin\amd64\cl.exe'
- Kind: LF_STRING_ID
- StringId:
+ 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:
+ StringList:
StringIndices: [ 4105 ]
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 4106
String: ' -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\winrt -TC -X'
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: b.c
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: 'C:\src\llvm-project\build\vc140.pdb'
- Kind: LF_BUILDINFO
- 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:
+ Relocations:
- VirtualAddress: 31
SymbolName: g
Type: IMAGE_REL_AMD64_REL32
@@ -249,7 +249,7 @@ sections:
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
Alignment: 4
SectionData: '000000003E00000000000000'
- Relocations:
+ Relocations:
- VirtualAddress: 0
SymbolName: '$LN5'
Type: IMAGE_REL_AMD64_ADDR32NB
@@ -259,14 +259,14 @@ sections:
- VirtualAddress: 8
SymbolName: '$unwind$f'
Type: IMAGE_REL_AMD64_ADDR32NB
-symbols:
+symbols:
- Name: .drectve
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 47
NumberOfRelocations: 0
NumberOfLinenumbers: 0
@@ -278,7 +278,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 484
NumberOfRelocations: 8
NumberOfLinenumbers: 0
@@ -290,7 +290,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 616
NumberOfRelocations: 0
NumberOfLinenumbers: 0
@@ -302,7 +302,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 62
NumberOfRelocations: 2
NumberOfLinenumbers: 0
@@ -332,7 +332,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 8
NumberOfRelocations: 0
NumberOfLinenumbers: 0
@@ -350,7 +350,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 12
NumberOfRelocations: 3
NumberOfLinenumbers: 0
diff --git a/test/COFF/Inputs/pdb_comdat_bar.yaml b/test/COFF/Inputs/pdb_comdat_bar.yaml
index 71a9535c50b7..796e71379c47 100644
--- a/test/COFF/Inputs/pdb_comdat_bar.yaml
+++ b/test/COFF/Inputs/pdb_comdat_bar.yaml
@@ -1,8 +1,8 @@
--- !COFF
-header:
+header:
Machine: IMAGE_FILE_MACHINE_AMD64
Characteristics: [ ]
-sections:
+sections:
- Name: .drectve
Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ]
Alignment: 1
@@ -10,15 +10,15 @@ sections:
- Name: '.debug$S'
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
Alignment: 1
- Subsections:
+ Subsections:
- !Symbols
- Records:
+ Records:
- Kind: S_OBJNAME
- ObjNameSym:
+ ObjNameSym:
Signature: 0
ObjectName: 'C:\src\llvm-project\build\pdb_comdat_bar.obj'
- Kind: S_COMPILE3
- Compile3Sym:
+ Compile3Sym:
Flags: [ SecurityChecks, HotPatch ]
Machine: X64
FrontendMajor: 19
@@ -31,9 +31,9 @@ sections:
BackendQFE: 1
Version: 'Microsoft (R) Optimizing Compiler'
- !Symbols
- Records:
+ Records:
- Kind: S_GPROC32_ID
- ProcSym:
+ ProcSym:
PtrParent: 0
PtrEnd: 0
PtrNext: 0
@@ -45,7 +45,7 @@ sections:
Flags: [ ]
DisplayName: bar
- Kind: S_FRAMEPROC
- FrameProcSym:
+ FrameProcSym:
TotalFrameBytes: 40
PaddingFrameBytes: 0
OffsetToPadding: 0
@@ -54,15 +54,15 @@ sections:
SectionIdOfExceptionHandler: 0
Flags: [ AsynchronousExceptionHandling, OptimizedForSpeed ]
- Kind: S_PROC_ID_END
- ScopeEndSym:
+ ScopeEndSym:
- !Lines
CodeSize: 14
Flags: [ ]
RelocOffset: 0
RelocSegment: 0
- Blocks:
+ Blocks:
- FileName: 'c:\src\llvm-project\build\pdb_comdat_bar.c'
- Lines:
+ Lines:
- Offset: 0
LineStart: 3
IsStatement: true
@@ -75,15 +75,15 @@ sections:
LineStart: 5
IsStatement: true
EndDelta: 0
- Columns:
+ Columns:
- !Symbols
- Records:
+ Records:
- Kind: S_GDATA32
- DataSym:
+ DataSym:
Type: 116
DisplayName: global
- !FileChecksums
- Checksums:
+ Checksums:
- FileName: 'c:\src\llvm-project\build\pdb_comdat_bar.c'
Kind: MD5
Checksum: 365279DB4FCBEDD721BBFC3B14A953C2
@@ -91,15 +91,15 @@ sections:
Kind: MD5
Checksum: D74D834EFAC3AE2B45E606A8320B1D5C
- !StringTable
- Strings:
+ Strings:
- 'c:\src\llvm-project\build\pdb_comdat_bar.c'
- 'c:\src\llvm-project\build\foo.h'
- !Symbols
- Records:
+ Records:
- Kind: S_BUILDINFO
- BuildInfoSym:
+ BuildInfoSym:
BuildId: 4110
- Relocations:
+ Relocations:
- VirtualAddress: 168
SymbolName: bar
Type: IMAGE_REL_AMD64_SECREL
@@ -121,70 +121,70 @@ sections:
- Name: '.debug$T'
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
Alignment: 1
- Types:
+ Types:
- Kind: LF_ARGLIST
- ArgList:
+ ArgList:
ArgIndices: [ 0 ]
- Kind: LF_PROCEDURE
- Procedure:
+ Procedure:
ReturnType: 3
CallConv: NearC
Options: [ None ]
ParameterCount: 0
ArgumentList: 4096
- Kind: LF_POINTER
- Pointer:
+ Pointer:
ReferentType: 4097
Attrs: 65548
- Kind: LF_FUNC_ID
- FuncId:
+ FuncId:
ParentScope: 0
FunctionType: 4097
Name: foo
- Kind: LF_ARGLIST
- ArgList:
+ ArgList:
ArgIndices: [ ]
- Kind: LF_PROCEDURE
- Procedure:
+ Procedure:
ReturnType: 3
CallConv: NearC
Options: [ None ]
ParameterCount: 0
ArgumentList: 4100
- Kind: LF_FUNC_ID
- FuncId:
+ FuncId:
ParentScope: 0
FunctionType: 4101
Name: bar
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: 'C:\src\llvm-project\build'
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: 'C:\PROGRA~2\MICROS~1.0\VC\Bin\amd64\cl.exe'
- Kind: LF_STRING_ID
- StringId:
+ 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:
+ StringList:
StringIndices: [ 4105 ]
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 4106
String: ' -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\winrt -TC -X'
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: pdb_comdat_bar.c
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: 'C:\src\llvm-project\build\vc140.pdb'
- Kind: LF_BUILDINFO
- BuildInfo:
+ BuildInfo:
ArgIndices: [ 4103, 4104, 4108, 4109, 4107 ]
- Name: .bss
Characteristics: [ IMAGE_SCN_CNT_UNINITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
@@ -194,7 +194,7 @@ sections:
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 16
SectionData: 4883EC28E8000000004883C428C3
- Relocations:
+ Relocations:
- VirtualAddress: 5
SymbolName: foo
Type: IMAGE_REL_AMD64_REL32
@@ -202,7 +202,7 @@ sections:
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 16
SectionData: 8B0500000000FFC0890500000000C3
- Relocations:
+ Relocations:
- VirtualAddress: 2
SymbolName: global
Type: IMAGE_REL_AMD64_REL32
@@ -212,11 +212,11 @@ sections:
- Name: '.debug$S'
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
Alignment: 1
- Subsections:
+ Subsections:
- !Symbols
- Records:
+ Records:
- Kind: S_GPROC32_ID
- ProcSym:
+ ProcSym:
PtrParent: 0
PtrEnd: 0
PtrNext: 0
@@ -228,7 +228,7 @@ sections:
Flags: [ ]
DisplayName: foo
- Kind: S_FRAMEPROC
- FrameProcSym:
+ FrameProcSym:
TotalFrameBytes: 0
PaddingFrameBytes: 0
OffsetToPadding: 0
@@ -237,15 +237,15 @@ sections:
SectionIdOfExceptionHandler: 0
Flags: [ MarkedInline, AsynchronousExceptionHandling, OptimizedForSpeed ]
- Kind: S_PROC_ID_END
- ScopeEndSym:
+ ScopeEndSym:
- !Lines
CodeSize: 15
Flags: [ ]
RelocOffset: 0
RelocSegment: 0
- Blocks:
+ Blocks:
- FileName: 'c:\src\llvm-project\build\foo.h'
- Lines:
+ Lines:
- Offset: 0
LineStart: 2
IsStatement: true
@@ -258,8 +258,8 @@ sections:
LineStart: 4
IsStatement: true
EndDelta: 0
- Columns:
- Relocations:
+ Columns:
+ Relocations:
- VirtualAddress: 44
SymbolName: foo
Type: IMAGE_REL_AMD64_SECREL
@@ -280,7 +280,7 @@ sections:
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
Alignment: 4
SectionData: '000000000E00000000000000'
- Relocations:
+ Relocations:
- VirtualAddress: 0
SymbolName: '$LN3'
Type: IMAGE_REL_AMD64_ADDR32NB
@@ -290,14 +290,14 @@ sections:
- VirtualAddress: 8
SymbolName: '$unwind$bar'
Type: IMAGE_REL_AMD64_ADDR32NB
-symbols:
+symbols:
- Name: .drectve
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 47
NumberOfRelocations: 0
NumberOfLinenumbers: 0
@@ -309,7 +309,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 460
NumberOfRelocations: 6
NumberOfLinenumbers: 0
@@ -321,7 +321,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 628
NumberOfRelocations: 0
NumberOfLinenumbers: 0
@@ -333,7 +333,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 4
NumberOfRelocations: 0
NumberOfLinenumbers: 0
@@ -351,7 +351,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 14
NumberOfRelocations: 1
NumberOfLinenumbers: 0
@@ -363,7 +363,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 15
NumberOfRelocations: 2
NumberOfLinenumbers: 0
@@ -376,7 +376,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 148
NumberOfRelocations: 4
NumberOfLinenumbers: 0
@@ -407,7 +407,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 8
NumberOfRelocations: 0
NumberOfLinenumbers: 0
@@ -425,7 +425,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 12
NumberOfRelocations: 3
NumberOfLinenumbers: 0
diff --git a/test/COFF/Inputs/pdb_comdat_main.yaml b/test/COFF/Inputs/pdb_comdat_main.yaml
index d9019d633415..637ac21abc5c 100644
--- a/test/COFF/Inputs/pdb_comdat_main.yaml
+++ b/test/COFF/Inputs/pdb_comdat_main.yaml
@@ -1,8 +1,8 @@
--- !COFF
-header:
+header:
Machine: IMAGE_FILE_MACHINE_AMD64
Characteristics: [ ]
-sections:
+sections:
- Name: .drectve
Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ]
Alignment: 1
@@ -10,15 +10,15 @@ sections:
- Name: '.debug$S'
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
Alignment: 1
- Subsections:
+ Subsections:
- !Symbols
- Records:
+ Records:
- Kind: S_OBJNAME
- ObjNameSym:
+ ObjNameSym:
Signature: 0
ObjectName: 'C:\src\llvm-project\build\pdb_comdat_main.obj'
- Kind: S_COMPILE3
- Compile3Sym:
+ Compile3Sym:
Flags: [ SecurityChecks, HotPatch ]
Machine: X64
FrontendMajor: 19
@@ -31,9 +31,9 @@ sections:
BackendQFE: 1
Version: 'Microsoft (R) Optimizing Compiler'
- !Symbols
- Records:
+ Records:
- Kind: S_GPROC32_ID
- ProcSym:
+ ProcSym:
PtrParent: 0
PtrEnd: 0
PtrNext: 0
@@ -45,7 +45,7 @@ sections:
Flags: [ ]
DisplayName: main
- Kind: S_FRAMEPROC
- FrameProcSym:
+ FrameProcSym:
TotalFrameBytes: 40
PaddingFrameBytes: 0
OffsetToPadding: 0
@@ -54,15 +54,15 @@ sections:
SectionIdOfExceptionHandler: 0
Flags: [ AsynchronousExceptionHandling, OptimizedForSpeed ]
- Kind: S_PROC_ID_END
- ScopeEndSym:
+ ScopeEndSym:
- !Lines
CodeSize: 24
Flags: [ ]
RelocOffset: 0
RelocSegment: 0
- Blocks:
+ Blocks:
- FileName: 'c:\src\llvm-project\build\pdb_comdat_main.c'
- Lines:
+ Lines:
- Offset: 0
LineStart: 2
IsStatement: true
@@ -83,15 +83,15 @@ sections:
LineStart: 6
IsStatement: true
EndDelta: 0
- Columns:
+ Columns:
- !Symbols
- Records:
+ Records:
- Kind: S_GDATA32
- DataSym:
+ DataSym:
Type: 116
DisplayName: global
- !FileChecksums
- Checksums:
+ Checksums:
- FileName: 'c:\src\llvm-project\build\pdb_comdat_main.c'
Kind: MD5
Checksum: F969E51BBE373436D81492EB61387F36
@@ -99,15 +99,15 @@ sections:
Kind: MD5
Checksum: D74D834EFAC3AE2B45E606A8320B1D5C
- !StringTable
- Strings:
+ Strings:
- 'c:\src\llvm-project\build\pdb_comdat_main.c'
- 'c:\src\llvm-project\build\foo.h'
- !Symbols
- Records:
+ Records:
- Kind: S_BUILDINFO
- BuildInfoSym:
+ BuildInfoSym:
BuildId: 4111
- Relocations:
+ Relocations:
- VirtualAddress: 168
SymbolName: main
Type: IMAGE_REL_AMD64_SECREL
@@ -129,81 +129,81 @@ sections:
- Name: '.debug$T'
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
Alignment: 1
- Types:
+ Types:
- Kind: LF_ARGLIST
- ArgList:
+ ArgList:
ArgIndices: [ 0 ]
- Kind: LF_PROCEDURE
- Procedure:
+ Procedure:
ReturnType: 3
CallConv: NearC
Options: [ None ]
ParameterCount: 0
ArgumentList: 4096
- Kind: LF_POINTER
- Pointer:
+ Pointer:
ReferentType: 4097
Attrs: 65548
- Kind: LF_FUNC_ID
- FuncId:
+ FuncId:
ParentScope: 0
FunctionType: 4097
Name: foo
- Kind: LF_ARGLIST
- ArgList:
+ ArgList:
ArgIndices: [ ]
- Kind: LF_PROCEDURE
- Procedure:
+ Procedure:
ReturnType: 116
CallConv: NearC
Options: [ None ]
ParameterCount: 0
ArgumentList: 4100
- Kind: LF_FUNC_ID
- FuncId:
+ FuncId:
ParentScope: 0
FunctionType: 4101
Name: main
- Kind: LF_FUNC_ID
- FuncId:
+ FuncId:
ParentScope: 0
FunctionType: 4097
Name: bar
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: 'C:\src\llvm-project\build'
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: 'C:\PROGRA~2\MICROS~1.0\VC\Bin\amd64\cl.exe'
- Kind: LF_STRING_ID
- StringId:
+ 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:
+ StringList:
StringIndices: [ 4106 ]
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 4107
String: ' -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\winrt -TC -X'
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: pdb_comdat_main.c
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: 'C:\src\llvm-project\build\vc140.pdb'
- Kind: LF_BUILDINFO
- 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:
+ Relocations:
- VirtualAddress: 5
SymbolName: foo
Type: IMAGE_REL_AMD64_REL32
@@ -214,7 +214,7 @@ sections:
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 16
SectionData: 8B0500000000FFC0890500000000C3
- Relocations:
+ Relocations:
- VirtualAddress: 2
SymbolName: global
Type: IMAGE_REL_AMD64_REL32
@@ -224,11 +224,11 @@ sections:
- Name: '.debug$S'
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
Alignment: 1
- Subsections:
+ Subsections:
- !Symbols
- Records:
+ Records:
- Kind: S_GPROC32_ID
- ProcSym:
+ ProcSym:
PtrParent: 0
PtrEnd: 0
PtrNext: 0
@@ -240,7 +240,7 @@ sections:
Flags: [ ]
DisplayName: foo
- Kind: S_FRAMEPROC
- FrameProcSym:
+ FrameProcSym:
TotalFrameBytes: 0
PaddingFrameBytes: 0
OffsetToPadding: 0
@@ -249,15 +249,15 @@ sections:
SectionIdOfExceptionHandler: 0
Flags: [ MarkedInline, AsynchronousExceptionHandling, OptimizedForSpeed ]
- Kind: S_PROC_ID_END
- ScopeEndSym:
+ ScopeEndSym:
- !Lines
CodeSize: 15
Flags: [ ]
RelocOffset: 0
RelocSegment: 0
- Blocks:
+ Blocks:
- FileName: 'c:\src\llvm-project\build\foo.h'
- Lines:
+ Lines:
- Offset: 0
LineStart: 2
IsStatement: true
@@ -270,8 +270,8 @@ sections:
LineStart: 4
IsStatement: true
EndDelta: 0
- Columns:
- Relocations:
+ Columns:
+ Relocations:
- VirtualAddress: 44
SymbolName: foo
Type: IMAGE_REL_AMD64_SECREL
@@ -292,7 +292,7 @@ sections:
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
Alignment: 4
SectionData: '000000001800000000000000'
- Relocations:
+ Relocations:
- VirtualAddress: 0
SymbolName: '$LN3'
Type: IMAGE_REL_AMD64_ADDR32NB
@@ -302,14 +302,14 @@ sections:
- VirtualAddress: 8
SymbolName: '$unwind$main'
Type: IMAGE_REL_AMD64_ADDR32NB
-symbols:
+symbols:
- Name: .drectve
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 47
NumberOfRelocations: 0
NumberOfLinenumbers: 0
@@ -321,7 +321,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 480
NumberOfRelocations: 6
NumberOfLinenumbers: 0
@@ -333,7 +333,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 648
NumberOfRelocations: 0
NumberOfLinenumbers: 0
@@ -345,7 +345,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 24
NumberOfRelocations: 2
NumberOfLinenumbers: 0
@@ -357,7 +357,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 15
NumberOfRelocations: 2
NumberOfLinenumbers: 0
@@ -370,7 +370,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 148
NumberOfRelocations: 4
NumberOfLinenumbers: 0
@@ -407,7 +407,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 8
NumberOfRelocations: 0
NumberOfLinenumbers: 0
@@ -425,7 +425,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 12
NumberOfRelocations: 3
NumberOfLinenumbers: 0
diff --git a/test/COFF/Inputs/pdb_lines_1.yaml b/test/COFF/Inputs/pdb_lines_1.yaml
index 3fbb2a94d45f..b52ed97c93c7 100644
--- a/test/COFF/Inputs/pdb_lines_1.yaml
+++ b/test/COFF/Inputs/pdb_lines_1.yaml
@@ -1,8 +1,8 @@
--- !COFF
-header:
+header:
Machine: IMAGE_FILE_MACHINE_AMD64
Characteristics: [ ]
-sections:
+sections:
- Name: .drectve
Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ]
Alignment: 1
@@ -10,15 +10,15 @@ sections:
- Name: '.debug$S'
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
Alignment: 1
- Subsections:
+ Subsections:
- !Symbols
- Records:
+ Records:
- Kind: S_OBJNAME
- ObjNameSym:
+ ObjNameSym:
Signature: 0
ObjectName: 'C:\src\llvm-project\build\pdb_lines_1.obj'
- Kind: S_COMPILE3
- Compile3Sym:
+ Compile3Sym:
Flags: [ SecurityChecks, HotPatch ]
Machine: X64
FrontendMajor: 19
@@ -31,9 +31,9 @@ sections:
BackendQFE: 1
Version: 'Microsoft (R) Optimizing Compiler'
- !Symbols
- Records:
+ Records:
- Kind: S_GPROC32_ID
- ProcSym:
+ ProcSym:
PtrParent: 0
PtrEnd: 0
PtrNext: 0
@@ -45,7 +45,7 @@ sections:
Flags: [ ]
DisplayName: main
- Kind: S_FRAMEPROC
- FrameProcSym:
+ FrameProcSym:
TotalFrameBytes: 40
PaddingFrameBytes: 0
OffsetToPadding: 0
@@ -54,15 +54,15 @@ sections:
SectionIdOfExceptionHandler: 0
Flags: [ AsynchronousExceptionHandling, OptimizedForSpeed ]
- Kind: S_PROC_ID_END
- ScopeEndSym:
+ ScopeEndSym:
- !Lines
CodeSize: 19
Flags: [ ]
RelocOffset: 0
RelocSegment: 0
- Blocks:
+ Blocks:
- FileName: 'c:\src\llvm-project\build\pdb_lines_1.c'
- Lines:
+ Lines:
- Offset: 0
LineStart: 2
IsStatement: true
@@ -79,9 +79,9 @@ sections:
LineStart: 5
IsStatement: true
EndDelta: 0
- Columns:
+ Columns:
- !FileChecksums
- Checksums:
+ Checksums:
- FileName: 'c:\src\llvm-project\build\pdb_lines_1.c'
Kind: MD5
Checksum: 4EB19DCD86C3BA2238A255C718572E7B
@@ -89,15 +89,15 @@ sections:
Kind: MD5
Checksum: 061EB73ABB642532857A4F1D9CBAC323
- !StringTable
- Strings:
+ Strings:
- 'c:\src\llvm-project\build\pdb_lines_1.c'
- 'c:\src\llvm-project\build\foo.h'
- !Symbols
- Records:
+ Records:
- Kind: S_BUILDINFO
- BuildInfoSym:
+ BuildInfoSym:
BuildId: 4111
- Relocations:
+ Relocations:
- VirtualAddress: 164
SymbolName: main
Type: IMAGE_REL_AMD64_SECREL
@@ -113,81 +113,81 @@ sections:
- Name: '.debug$T'
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
Alignment: 1
- Types:
+ Types:
- Kind: LF_ARGLIST
- ArgList:
+ ArgList:
ArgIndices: [ ]
- Kind: LF_PROCEDURE
- Procedure:
+ Procedure:
ReturnType: 3
CallConv: NearC
Options: [ None ]
ParameterCount: 0
ArgumentList: 4096
- Kind: LF_POINTER
- Pointer:
+ Pointer:
ReferentType: 4097
Attrs: 65548
- Kind: LF_FUNC_ID
- FuncId:
+ FuncId:
ParentScope: 0
FunctionType: 4097
Name: foo
- Kind: LF_ARGLIST
- ArgList:
+ ArgList:
ArgIndices: [ 0 ]
- Kind: LF_PROCEDURE
- Procedure:
+ Procedure:
ReturnType: 116
CallConv: NearC
Options: [ None ]
ParameterCount: 0
ArgumentList: 4100
- Kind: LF_FUNC_ID
- FuncId:
+ FuncId:
ParentScope: 0
FunctionType: 4101
Name: main
- Kind: LF_FUNC_ID
- FuncId:
+ FuncId:
ParentScope: 0
FunctionType: 4097
Name: bar
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: 'C:\src\llvm-project\build'
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: 'C:\PROGRA~2\MICROS~1.0\VC\Bin\amd64\cl.exe'
- Kind: LF_STRING_ID
- StringId:
+ 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:
+ StringList:
StringIndices: [ 4106 ]
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 4107
String: ' -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\winrt -TC -X'
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: pdb_lines_1.c
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: 'C:\src\llvm-project\build\vc140.pdb'
- Kind: LF_BUILDINFO
- 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:
+ Relocations:
- VirtualAddress: 5
SymbolName: foo
Type: IMAGE_REL_AMD64_REL32
@@ -195,18 +195,18 @@ sections:
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 16
SectionData: 4883EC28E8000000004883C428C3
- Relocations:
+ 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:
+ Subsections:
- !Symbols
- Records:
+ Records:
- Kind: S_GPROC32_ID
- ProcSym:
+ ProcSym:
PtrParent: 0
PtrEnd: 0
PtrNext: 0
@@ -218,7 +218,7 @@ sections:
Flags: [ ]
DisplayName: foo
- Kind: S_FRAMEPROC
- FrameProcSym:
+ FrameProcSym:
TotalFrameBytes: 40
PaddingFrameBytes: 0
OffsetToPadding: 0
@@ -227,15 +227,15 @@ sections:
SectionIdOfExceptionHandler: 0
Flags: [ MarkedInline, AsynchronousExceptionHandling, OptimizedForSpeed ]
- Kind: S_PROC_ID_END
- ScopeEndSym:
+ ScopeEndSym:
- !Lines
CodeSize: 14
Flags: [ ]
RelocOffset: 0
RelocSegment: 0
- Blocks:
+ Blocks:
- FileName: 'c:\src\llvm-project\build\foo.h'
- Lines:
+ Lines:
- Offset: 0
LineStart: 2
IsStatement: true
@@ -248,8 +248,8 @@ sections:
LineStart: 4
IsStatement: true
EndDelta: 0
- Columns:
- Relocations:
+ Columns:
+ Relocations:
- VirtualAddress: 44
SymbolName: foo
Type: IMAGE_REL_AMD64_SECREL
@@ -270,7 +270,7 @@ sections:
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_READ ]
Alignment: 4
SectionData: '000000000E00000000000000'
- Relocations:
+ Relocations:
- VirtualAddress: 0
SymbolName: '$LN3'
Type: IMAGE_REL_AMD64_ADDR32NB
@@ -288,7 +288,7 @@ sections:
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
Alignment: 4
SectionData: '000000001300000000000000'
- Relocations:
+ Relocations:
- VirtualAddress: 0
SymbolName: '$LN3'
Type: IMAGE_REL_AMD64_ADDR32NB
@@ -298,14 +298,14 @@ sections:
- VirtualAddress: 8
SymbolName: '$unwind$main'
Type: IMAGE_REL_AMD64_ADDR32NB
-symbols:
+symbols:
- Name: .drectve
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 47
NumberOfRelocations: 0
NumberOfLinenumbers: 0
@@ -317,7 +317,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 432
NumberOfRelocations: 4
NumberOfLinenumbers: 0
@@ -329,7 +329,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 644
NumberOfRelocations: 0
NumberOfLinenumbers: 0
@@ -341,7 +341,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 19
NumberOfRelocations: 1
NumberOfLinenumbers: 0
@@ -353,7 +353,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 14
NumberOfRelocations: 1
NumberOfLinenumbers: 0
@@ -366,7 +366,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 148
NumberOfRelocations: 4
NumberOfLinenumbers: 0
@@ -409,7 +409,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 8
NumberOfRelocations: 0
NumberOfLinenumbers: 0
@@ -428,7 +428,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 12
NumberOfRelocations: 3
NumberOfLinenumbers: 0
@@ -447,7 +447,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 8
NumberOfRelocations: 0
NumberOfLinenumbers: 0
@@ -465,7 +465,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 12
NumberOfRelocations: 3
NumberOfLinenumbers: 0
diff --git a/test/COFF/Inputs/pdb_lines_2.yaml b/test/COFF/Inputs/pdb_lines_2.yaml
index 8ad8d062db58..8026c19c91d4 100644
--- a/test/COFF/Inputs/pdb_lines_2.yaml
+++ b/test/COFF/Inputs/pdb_lines_2.yaml
@@ -1,8 +1,8 @@
--- !COFF
-header:
+header:
Machine: IMAGE_FILE_MACHINE_AMD64
Characteristics: [ ]
-sections:
+sections:
- Name: .drectve
Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ]
Alignment: 1
@@ -10,15 +10,15 @@ sections:
- Name: '.debug$S'
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
Alignment: 1
- Subsections:
+ Subsections:
- !Symbols
- Records:
+ Records:
- Kind: S_OBJNAME
- ObjNameSym:
+ ObjNameSym:
Signature: 0
ObjectName: 'C:\src\llvm-project\build\pdb_lines_2.obj'
- Kind: S_COMPILE3
- Compile3Sym:
+ Compile3Sym:
Flags: [ SecurityChecks, HotPatch ]
Machine: X64
FrontendMajor: 19
@@ -31,9 +31,9 @@ sections:
BackendQFE: 1
Version: 'Microsoft (R) Optimizing Compiler'
- !Symbols
- Records:
+ Records:
- Kind: S_GPROC32_ID
- ProcSym:
+ ProcSym:
PtrParent: 0
PtrEnd: 0
PtrNext: 0
@@ -45,7 +45,7 @@ sections:
Flags: [ ]
DisplayName: bar
- Kind: S_FRAMEPROC
- FrameProcSym:
+ FrameProcSym:
TotalFrameBytes: 0
PaddingFrameBytes: 0
OffsetToPadding: 0
@@ -54,15 +54,15 @@ sections:
SectionIdOfExceptionHandler: 0
Flags: [ AsynchronousExceptionHandling, OptimizedForSpeed ]
- Kind: S_PROC_ID_END
- ScopeEndSym:
+ ScopeEndSym:
- !Lines
CodeSize: 1
Flags: [ ]
RelocOffset: 0
RelocSegment: 0
- Blocks:
+ Blocks:
- FileName: 'c:\src\llvm-project\build\pdb_lines_2.c'
- Lines:
+ Lines:
- Offset: 0
LineStart: 1
IsStatement: true
@@ -71,21 +71,21 @@ sections:
LineStart: 2
IsStatement: true
EndDelta: 0
- Columns:
+ Columns:
- !FileChecksums
- Checksums:
+ Checksums:
- FileName: 'c:\src\llvm-project\build\pdb_lines_2.c'
Kind: MD5
Checksum: DF91CB3A2B8D917486574BB50CAC4CC7
- !StringTable
- Strings:
+ Strings:
- 'c:\src\llvm-project\build\pdb_lines_2.c'
- !Symbols
- Records:
+ Records:
- Kind: S_BUILDINFO
- BuildInfoSym:
+ BuildInfoSym:
BuildId: 4106
- Relocations:
+ Relocations:
- VirtualAddress: 164
SymbolName: bar
Type: IMAGE_REL_AMD64_SECREL
@@ -101,64 +101,64 @@ sections:
- Name: '.debug$T'
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
Alignment: 1
- Types:
+ Types:
- Kind: LF_ARGLIST
- ArgList:
+ ArgList:
ArgIndices: [ ]
- Kind: LF_PROCEDURE
- Procedure:
+ Procedure:
ReturnType: 3
CallConv: NearC
Options: [ None ]
ParameterCount: 0
ArgumentList: 4096
- Kind: LF_FUNC_ID
- FuncId:
+ FuncId:
ParentScope: 0
FunctionType: 4097
Name: bar
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: 'C:\src\llvm-project\build'
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: 'C:\PROGRA~2\MICROS~1.0\VC\Bin\amd64\cl.exe'
- Kind: LF_STRING_ID
- StringId:
+ 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:
+ StringList:
StringIndices: [ 4101 ]
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 4102
String: ' -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.14393.0\winrt -TC -X'
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: pdb_lines_2.c
- Kind: LF_STRING_ID
- StringId:
+ StringId:
Id: 0
String: 'C:\src\llvm-project\build\vc140.pdb'
- Kind: LF_BUILDINFO
- 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:
+symbols:
- Name: .drectve
Value: 0
SectionNumber: 1
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 47
NumberOfRelocations: 0
NumberOfLinenumbers: 0
@@ -170,7 +170,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 360
NumberOfRelocations: 4
NumberOfLinenumbers: 0
@@ -182,7 +182,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 568
NumberOfRelocations: 0
NumberOfLinenumbers: 0
@@ -194,7 +194,7 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
- SectionDefinition:
+ SectionDefinition:
Length: 1
NumberOfRelocations: 0
NumberOfLinenumbers: 0
diff --git a/test/COFF/arm-thumb-branch20-error.s b/test/COFF/arm-thumb-branch20-error.s
new file mode 100644
index 000000000000..ec7d23b05329
--- /dev/null
+++ b/test/COFF/arm-thumb-branch20-error.s
@@ -0,0 +1,10 @@
+// REQUIRES: arm
+// RUN: llvm-mc -filetype=obj -triple=thumbv7a-windows-gnu %s -o %t.obj
+// RUN: llvm-mc -filetype=obj -triple=thumbv7a-windows-gnu %S/Inputs/far-arm-thumb-abs20.s -o %t.far.obj
+// RUN: not lld-link -entry:_start -subsystem:console %t.obj %t.far.obj -out:%t.exe 2>&1 | FileCheck %s
+ .syntax unified
+ .globl _start
+_start:
+ bne too_far20
+
+// CHECK: relocation out of range
diff --git a/test/COFF/arm64-dynamicbase.s b/test/COFF/arm64-dynamicbase.s
new file mode 100644
index 000000000000..d4cb12c1c8ff
--- /dev/null
+++ b/test/COFF/arm64-dynamicbase.s
@@ -0,0 +1,8 @@
+// REQUIRES: aarch64
+// RUN: llvm-mc -filetype=obj -triple=aarch64-windows %s -o %t.obj
+// RUN: not lld-link -entry:_start -subsystem:console %t.obj -out:%t.exe -dynamicbase:no 2>&1 | FileCheck %s
+ .globl _start
+_start:
+ ret
+
+# CHECK: dynamicbase:no is not compatible with arm64
diff --git a/test/COFF/arm64-import2.test b/test/COFF/arm64-import2.test
new file mode 100644
index 000000000000..3119e73e1d54
--- /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: IMAGE_REL_ARM64_BRANCH26
+ - VirtualAddress: 4
+ SymbolName: function2
+ Type: IMAGE_REL_ARM64_BRANCH26
+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-relocs-imports.test b/test/COFF/arm64-relocs-imports.test
index 3d252aaa2801..57590dcf733a 100644
--- a/test/COFF/arm64-relocs-imports.test
+++ b/test/COFF/arm64-relocs-imports.test
@@ -14,29 +14,67 @@
# 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: e0 03 1f 2a mov w0, wzr
-# BEFORE: 24: fe 07 41 f8 ldr x30, [sp], #16
-# BEFORE: 28: c0 03 5f d6 ret
-# BEFORE: 2c: 08 00 00 00 <unknown>
-# BEFORE: 30: 00 00 00 00 <unknown>
+# 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>
+# BEFORE: 7c: 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: 0a 00 00 94 bl #40
+# AFTER: 14000200c: 1d 00 00 94 bl #116
# 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: e0 03 1f 2a mov w0, wzr
-# AFTER: 140002024: fe 07 41 f8 ldr x30, [sp], #16
-# AFTER: 140002028: c0 03 5f d6 ret
-# AFTER: 14000202c: 10 10 00 40 <unknown>
-# AFTER: 140002030: 01 00 00 00 <unknown>
-# AFTER: 140002034: 10 00 00 b0 adrp x16, #4096
-# AFTER: 140002038: 10 1e 40 f9 ldr x16, [x16, #56]
-# AFTER: 14000203c: 00 02 1f d6 br x16
+# 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: 09 00 00 00 <unknown>
+# AFTER: 140002080: 10 00 00 b0 adrp x16, #4096
+# AFTER: 140002084: 10 1e 40 f9 ldr x16, [x16, #56]
+# AFTER: 140002088: 00 02 1f d6 br x16
--- !COFF
header:
@@ -46,32 +84,89 @@ sections:
- Name: .text
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 4
- SectionData: FE0F1FF80000009000080091000000940001403900014079000140B9000140F9E0031F2AFE0741F8C0035FD60800000000000000
+ SectionData: FE0F1FF80000009000080091000000940001403900014079000140B9000140F90001003900010079000100B9000100F90001403D0001407D000140BD000140FD0001C03D0001003D0001007D000100BD000100FD0001803D000540F9201A01B000FC4FF9E0031F2AFE0741F8C0035FD608000000000000000100000001000000
Relocations:
- VirtualAddress: 4
SymbolName: .Lstr
- Type: 4
+ Type: IMAGE_REL_ARM64_PAGEBASE_REL21
- VirtualAddress: 8
SymbolName: .Lstr
- Type: 6
+ Type: IMAGE_REL_ARM64_PAGEOFFSET_12A
- VirtualAddress: 12
SymbolName: function
- Type: 3
+ Type: IMAGE_REL_ARM64_BRANCH26
- VirtualAddress: 16
SymbolName: .Lglobal
- Type: 7
+ Type: IMAGE_REL_ARM64_PAGEOFFSET_12L
- VirtualAddress: 20
SymbolName: .Lglobal
- Type: 7
+ Type: IMAGE_REL_ARM64_PAGEOFFSET_12L
- VirtualAddress: 24
SymbolName: .Lglobal
- Type: 7
+ Type: IMAGE_REL_ARM64_PAGEOFFSET_12L
- VirtualAddress: 28
SymbolName: .Lglobal
- Type: 7
+ Type: IMAGE_REL_ARM64_PAGEOFFSET_12L
+ - VirtualAddress: 32
+ SymbolName: .Lglobal
+ Type: IMAGE_REL_ARM64_PAGEOFFSET_12L
+ - VirtualAddress: 36
+ SymbolName: .Lglobal
+ Type: IMAGE_REL_ARM64_PAGEOFFSET_12L
+ - VirtualAddress: 40
+ SymbolName: .Lglobal
+ Type: IMAGE_REL_ARM64_PAGEOFFSET_12L
- VirtualAddress: 44
SymbolName: .Lglobal
- Type: 14
+ Type: IMAGE_REL_ARM64_PAGEOFFSET_12L
+ - VirtualAddress: 48
+ SymbolName: .Lglobal16
+ Type: IMAGE_REL_ARM64_PAGEOFFSET_12L
+ - VirtualAddress: 52
+ SymbolName: .Lglobal16
+ Type: IMAGE_REL_ARM64_PAGEOFFSET_12L
+ - VirtualAddress: 56
+ SymbolName: .Lglobal16
+ Type: IMAGE_REL_ARM64_PAGEOFFSET_12L
+ - VirtualAddress: 60
+ SymbolName: .Lglobal16
+ Type: IMAGE_REL_ARM64_PAGEOFFSET_12L
+ - VirtualAddress: 64
+ SymbolName: .Lglobal16
+ Type: IMAGE_REL_ARM64_PAGEOFFSET_12L
+ - VirtualAddress: 68
+ SymbolName: .Lglobal16
+ Type: IMAGE_REL_ARM64_PAGEOFFSET_12L
+ - VirtualAddress: 72
+ SymbolName: .Lglobal16
+ Type: IMAGE_REL_ARM64_PAGEOFFSET_12L
+ - VirtualAddress: 76
+ SymbolName: .Lglobal16
+ Type: IMAGE_REL_ARM64_PAGEOFFSET_12L
+ - VirtualAddress: 80
+ SymbolName: .Lglobal16
+ Type: IMAGE_REL_ARM64_PAGEOFFSET_12L
+ - VirtualAddress: 84
+ SymbolName: .Lglobal16
+ Type: IMAGE_REL_ARM64_PAGEOFFSET_12L
+ - VirtualAddress: 88
+ SymbolName: .Lglobal
+ Type: IMAGE_REL_ARM64_PAGEOFFSET_12L
+ - VirtualAddress: 92
+ SymbolName: .Lglobal16
+ Type: IMAGE_REL_ARM64_PAGEBASE_REL21
+ - VirtualAddress: 96
+ SymbolName: .Lglobal0
+ Type: IMAGE_REL_ARM64_PAGEOFFSET_12L
+ - VirtualAddress: 112
+ SymbolName: .Lglobal
+ Type: IMAGE_REL_ARM64_ADDR64
+ - VirtualAddress: 120
+ SymbolName: .Lglobal
+ Type: IMAGE_REL_ARM64_ADDR32NB
+ - VirtualAddress: 124
+ SymbolName: .Lglobal
+ Type: IMAGE_REL_ARM64_SECREL
- Name: .data
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
Alignment: 4
@@ -127,6 +222,18 @@ symbols:
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
diff --git a/test/COFF/armnt-blx23t.test b/test/COFF/armnt-blx23t.test
index 5caf6ebaa127..ec36225e4332 100644
--- a/test/COFF/armnt-blx23t.test
+++ b/test/COFF/armnt-blx23t.test
@@ -37,7 +37,7 @@ sections:
Relocations:
- VirtualAddress: 12
SymbolName: identity
- Type: 21
+ Type: IMAGE_REL_ARM_BLX23T
symbols:
- Name: .text
Value: 0
diff --git a/test/COFF/armnt-branch24t.test b/test/COFF/armnt-branch24t.test
index 6e1114c94a18..88d17e445b1e 100644
--- a/test/COFF/armnt-branch24t.test
+++ b/test/COFF/armnt-branch24t.test
@@ -30,7 +30,7 @@ sections:
Relocations:
- VirtualAddress: 6
SymbolName: identity
- Type: 20
+ Type: IMAGE_REL_ARM_BRANCH24T
symbols:
- Name: .text
Value: 0
diff --git a/test/COFF/armnt-dynamicbase.test b/test/COFF/armnt-dynamicbase.test
new file mode 100644
index 000000000000..50d3bea54fd3
--- /dev/null
+++ b/test/COFF/armnt-dynamicbase.test
@@ -0,0 +1,3 @@
+# RUN: yaml2obj < %p/Inputs/armnt-executable.obj.yaml > %t.obj
+# RUN: not lld-link /out:%t.exe /entry:mainCRTStartup /subsystem:console %t.obj /dynamicbase:no 2>&1 | FileCheck %s
+# CHECK: dynamicbase:no is not compatible with arm
diff --git a/test/COFF/armnt-imports.test b/test/COFF/armnt-imports.test
index 519886eb0c06..7d3612a0df7e 100644
--- a/test/COFF/armnt-imports.test
+++ b/test/COFF/armnt-imports.test
@@ -22,7 +22,7 @@ sections:
Relocations:
- VirtualAddress: 0
SymbolName: __imp_function
- Type: 17
+ Type: IMAGE_REL_ARM_MOV32T
symbols:
- Name: .text
Value: 0
diff --git a/test/COFF/armnt-mov32t-exec.test b/test/COFF/armnt-mov32t-exec.test
index 629f0620730d..abc78c7fcc5b 100644
--- a/test/COFF/armnt-mov32t-exec.test
+++ b/test/COFF/armnt-mov32t-exec.test
@@ -31,7 +31,7 @@ sections:
Relocations:
- VirtualAddress: 4
SymbolName: function
- Type: 17
+ Type: IMAGE_REL_ARM_MOV32T
symbols:
- Name: .text
Value: 0
diff --git a/test/COFF/armnt-movt32t.test b/test/COFF/armnt-movt32t.test
index 6a9bf256e335..7c6965efed6a 100644
--- a/test/COFF/armnt-movt32t.test
+++ b/test/COFF/armnt-movt32t.test
@@ -27,7 +27,7 @@ sections:
Relocations:
- VirtualAddress: 0
SymbolName: buffer
- Type: 17
+ Type: IMAGE_REL_ARM_MOV32T
- Name: .rdata
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
Alignment: 1
diff --git a/test/COFF/common-alignment.test b/test/COFF/common-alignment.test
new file mode 100644
index 000000000000..a4ee15729107
--- /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/ctors_dtors_priority.s b/test/COFF/ctors_dtors_priority.s
new file mode 100644
index 000000000000..60562ba57a52
--- /dev/null
+++ b/test/COFF/ctors_dtors_priority.s
@@ -0,0 +1,30 @@
+# REQUIRES: x86
+# RUN: llvm-mc -triple=x86_64-windows-gnu -filetype=obj -o %t.obj %s
+# RUN: lld-link -entry:main %t.obj -out:%t.exe
+# RUN: llvm-objdump -s %t.exe | FileCheck %s
+
+.globl main
+main:
+ nop
+
+.section .ctors.00005, "w"
+ .quad 2
+.section .ctors, "w"
+ .quad 1
+.section .ctors.00100, "w"
+ .quad 3
+
+.section .dtors, "w"
+ .quad 1
+.section .dtors.00100, "w"
+ .quad 3
+.section .dtors.00005, "w"
+ .quad 2
+
+# CHECK: Contents of section .ctors:
+# CHECK-NEXT: 140001000 01000000 00000000 02000000 00000000
+# CHECK-NEXT: 140001010 03000000 00000000
+
+# CHECK: Contents of section .dtors:
+# CHECK-NEXT: 140002000 01000000 00000000 02000000 00000000
+# CHECK-NEXT: 140002010 03000000 00000000
diff --git a/test/COFF/debug-dwarf.test b/test/COFF/debug-dwarf.test
new file mode 100644
index 000000000000..156b2f58f64e
--- /dev/null
+++ b/test/COFF/debug-dwarf.test
@@ -0,0 +1,19 @@
+# Check that /debug creates %t.pdb.
+# RUN: rm -f %t.pdb
+# RUN: lld-link /debug /entry:main /out:%t.exe %p/Inputs/ret42.obj
+# RUN: ls %t.pdb
+
+# Check that /debug:dwarf does not create %t.pdb.
+# RUN: rm -f %t.pdb
+# RUN: lld-link /debug:dwarf /entry:main /out:%t.exe %p/Inputs/ret42.obj
+# RUN: not ls %t.pdb
+
+# Check that /debug:dwarf /debug creates %t.pdb.
+# RUN: rm -f %t.pdb
+# RUN: lld-link /debug:dwarf /debug /entry:main /out:%t.exe %p/Inputs/ret42.obj
+# RUN: ls %t.pdb
+
+# Check that /debug:dwarf /pdb:%t.pdb does not create %t.pdb.
+# RUN: rm -f %t.pdb
+# RUN: lld-link /debug:dwarf /pdb:%t.pdb /entry:main /out:%t.exe %p/Inputs/ret42.obj
+# RUN: not ls %t.pdb
diff --git a/test/COFF/def-export-stdcall.s b/test/COFF/def-export-stdcall.s
index 17473f7f9761..851ac6d975b9 100644
--- a/test/COFF/def-export-stdcall.s
+++ b/test/COFF/def-export-stdcall.s
@@ -1,21 +1,89 @@
# 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: echo -e "LIBRARY foo\nEXPORTS\n stdcall\n fastcall\n vectorcall\n _underscored" > %t.def
# RUN: lld-link -entry:dllmain -dll -def:%t.def %t.obj -out:%t.dll -implib:%t.lib
-# RUN: llvm-readobj %t.lib | FileCheck %s
-# CHECK: Name type: undecorate
-# CHECK: __imp__stdcall@8
-# CHECK: _stdcall@8
+# RUN: llvm-readobj %t.lib | FileCheck -check-prefix UNDECORATED-IMPLIB %s
+# RUN: llvm-readobj -coff-exports %t.dll | FileCheck -check-prefix UNDECORATED-EXPORTS %s
+
+# UNDECORATED-IMPLIB: Name type: noprefix
+# UNDECORATED-IMPLIB-NEXT: __imp___underscored
+# UNDECORATED-IMPLIB-NEXT: __underscored
+# UNDECORATED-IMPLIB: Name type: undecorate
+# UNDECORATED-IMPLIB-NEXT: __imp_@fastcall@8
+# UNDECORATED-IMPLIB-NEXT: fastcall@8
+# UNDECORATED-IMPLIB: Name type: undecorate
+# UNDECORATED-IMPLIB-NEXT: __imp__stdcall@8
+# UNDECORATED-IMPLIB-NEXT: _stdcall@8
+# UNDECORATED-IMPLIB: Name type: undecorate
+# UNDECORATED-IMPLIB-NEXT: __imp_vectorcall@@8
+# UNDECORATED-IMPLIB-NEXT: vectorcall@@8
+
+# UNDECORATED-EXPORTS: Name: _underscored
+# UNDECORATED-EXPORTS: Name: fastcall
+# UNDECORATED-EXPORTS: Name: stdcall
+# UNDECORATED-EXPORTS: Name: vectorcall
+
+
+# RUN: echo -e "LIBRARY foo\nEXPORTS\n _stdcall@8\n @fastcall@8\n vectorcall@@8" > %t.def
+# RUN: lld-link -entry:dllmain -dll -def:%t.def %t.obj -out:%t.dll -implib:%t.lib
+# RUN: llvm-readobj %t.lib | FileCheck -check-prefix DECORATED-IMPLIB %s
+# RUN: llvm-readobj -coff-exports %t.dll | FileCheck -check-prefix DECORATED-EXPORTS %s
+
+# DECORATED-IMPLIB: Name type: name
+# DECORATED-IMPLIB-NEXT: __imp_@fastcall@8
+# DECORATED-IMPLIB-NEXT: @fastcall@8
+# TODO: To match link.exe, this one should also be Name type: name.
+# DECORATED-IMPLIB: Name type: noprefix
+# DECORATED-IMPLIB-NEXT: __imp__stdcall@8
+# DECORATED-IMPLIB-NEXT: _stdcall@8
+# DECORATED-IMPLIB: Name type: name
+# DECORATED-IMPLIB-NEXT: __imp_vectorcall@@8
+# DECORATED-IMPLIB-NEXT: vectorcall@@8
+
+# DECORATED-EXPORTS: Name: @fastcall@8
+# TODO: To match link.exe, this one should actually be _stdcall@8
+# DECORATED-EXPORTS: Name: stdcall@8
+# DECORATED-EXPORTS: Name: vectorcall@@8
+
+
+# RUN: echo -e "LIBRARY foo\nEXPORTS\n stdcall@8\n @fastcall@8" > %t.def
+# RUN: lld-link -lldmingw -entry:dllmain -dll -def:%t.def %t.obj -out:%t.dll -implib:%t.lib
+# RUN: llvm-readobj %t.lib | FileCheck -check-prefix DECORATED-MINGW-IMPLIB %s
+# RUN: llvm-readobj -coff-exports %t.dll | FileCheck -check-prefix DECORATED-MINGW-EXPORTS %s
+
+# DECORATED-MINGW-IMPLIB: Name type: name
+# DECORATED-MINGW-IMPLIB-NEXT: __imp_@fastcall@8
+# DECORATED-MINGW-IMPLIB-NEXT: fastcall@8
+# DECORATED-MINGW-IMPLIB: Name type: noprefix
+# DECORATED-MINGW-IMPLIB-NEXT: __imp__stdcall@8
+# DECORATED-MINGW-IMPLIB-NEXT: _stdcall@8
+
+# DECORATED-MINGW-EXPORTS: Name: @fastcall@8
+# DECORATED-MINGW-EXPORTS: Name: stdcall@8
+
.def _stdcall@8;
.scl 2;
.type 32;
.endef
.globl _stdcall@8
+ .globl @fastcall@8
+ .globl vectorcall@@8
+ .globl __underscored
_stdcall@8:
movl 8(%esp), %eax
addl 4(%esp), %eax
retl $8
+@fastcall@8:
+ movl 8(%esp), %eax
+ addl 4(%esp), %eax
+ retl $8
+vectorcall@@8:
+ movl 8(%esp), %eax
+ addl 4(%esp), %eax
+ retl $8
+__underscored:
+ ret
.def _dllmain;
.scl 2;
diff --git a/test/COFF/delayimports-armnt.yaml b/test/COFF/delayimports-armnt.yaml
new file mode 100644
index 000000000000..231b4bc5c1f1
--- /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: IMAGE_REL_ARM_MOV32T
+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 53aadbb6a185..006eecff4d95 100644
--- a/test/COFF/delayimports32.test
+++ b/test/COFF/delayimports32.test
@@ -73,7 +73,7 @@ BASEREL-NEXT: ]
DISASM: 202b: 68 20 10 40 00 pushl $4198432
DISASM-NEXT: 2030: 68 00 40 40 00 pushl $4210688
-DISASM-NEXT: 2035: e8 c6 ff ff ff calll -58 <_main@0>
+DISASM-NEXT: 2035: e8 c6 ff ff ff calll -58 <.text>
DISASM-NEXT: 203a: 5a popl %edx
DISASM-NEXT: 203b: 59 popl %ecx
DISASM-NEXT: 203c: ff e0 jmpl *%eax
@@ -81,7 +81,7 @@ DISASM-NEXT: 203e: 51 pushl %ecx
DISASM-NEXT: 203f: 52 pushl %edx
DISASM-NEXT: 2040: 68 24 10 40 00 pushl $4198436
DISASM-NEXT: 2045: 68 00 40 40 00 pushl $4210688
-DISASM-NEXT: 204a: e8 b1 ff ff ff calll -79 <_main@0>
+DISASM-NEXT: 204a: e8 b1 ff ff ff calll -79 <.text>
DISASM-NEXT: 204f: 5a popl %edx
DISASM-NEXT: 2050: 59 popl %ecx
DISASM-NEXT: 2051: ff e0 jmpl *%eax
diff --git a/test/COFF/dllexport-mingw.s b/test/COFF/dllexport-mingw.s
new file mode 100644
index 000000000000..8bf035b36dcf
--- /dev/null
+++ b/test/COFF/dllexport-mingw.s
@@ -0,0 +1,24 @@
+# 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___underscoredFunc
+# CHECK: Symbol: __underscoredFunc
+# CHECK: Symbol: __imp__func
+# CHECK: Symbol: _func
+
+.global _main
+.global _func
+.global __underscoredFunc
+.text
+_main:
+ ret
+_func:
+ ret
+__underscoredFunc:
+ ret
+.section .drectve
+.ascii "-export:func -export:_underscoredFunc"
diff --git a/test/COFF/driver.test b/test/COFF/driver.test
index 0832350a4f30..36de6c200cb1 100644
--- a/test/COFF/driver.test
+++ b/test/COFF/driver.test
@@ -1,3 +1,6 @@
# RUN: not lld-link nosuchfile.obj >& %t.log
# RUN: FileCheck -check-prefix=MISSING %s < %t.log
MISSING: nosuchfile.obj: {{[Nn]}}o such file or directory
+
+# RUN: lld-link --version | FileCheck -check-prefix=VERSION %s
+VERSION: {{LLD [0-9]+\.[0-9]+}}
diff --git a/test/COFF/duplicate.test b/test/COFF/duplicate.test
new file mode 100644
index 000000000000..c2f743ebc28f
--- /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-drectve.test b/test/COFF/entry-drectve.test
new file mode 100644
index 000000000000..e51e7cb201f3
--- /dev/null
+++ b/test/COFF/entry-drectve.test
@@ -0,0 +1,24 @@
+# RUN: yaml2obj < %s > %t.obj
+# RUN: lld-link /subsystem:console /out:%t.exe %t.obj
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_I386
+ Characteristics: []
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: B800000000506800000000680000000050E80000000050E800000000
+ - Name: .drectve
+ Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ]
+ Alignment: 1
+ SectionData: 2f656e7472793a437573746f6d456e74727900 # /entry:CustomEntry
+symbols:
+ - Name: _CustomEntry
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/entry-inference.test b/test/COFF/entry-inference.test
index 2eb9a6863983..294870bf4185 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/export-all.s b/test/COFF/export-all.s
new file mode 100644
index 000000000000..96c7dca5df29
--- /dev/null
+++ b/test/COFF/export-all.s
@@ -0,0 +1,86 @@
+# 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
+# RUN: llvm-readobj %t.lib | FileCheck -check-prefix=IMPLIB %s
+
+# CHECK-NOT: Name: DllMainCRTStartup
+# CHECK-NOT: Name: _imp__unexported
+# CHECK: Name: dataSym
+# CHECK: Name: foobar
+# CHECK-NOT: Name: unexported
+
+# IMPLIB: Symbol: __imp__dataSym
+# IMPLIB-NOT: Symbol: _dataSym
+# IMPLIB: Symbol: __imp__foobar
+# IMPLIB: Symbol: _foobar
+
+.global _foobar
+.global _DllMainCRTStartup@12
+.global _dataSym
+.global _unexported
+.global __imp__unexported
+.text
+_DllMainCRTStartup@12:
+ ret
+_foobar:
+ ret
+_unexported:
+ ret
+.data
+_dataSym:
+ .int 4
+__imp__unexported:
+ .int _unexported
+
+# 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
+
+# Test that we handle import libraries together with -opt:noref.
+
+# RUN: yaml2obj < %p/Inputs/hello32.yaml > %t.obj
+# RUN: lld-link -lldmingw -dll -out:%t.dll -entry:main@0 %t.obj -implib:%t.lib -opt:noref %p/Inputs/std32.lib -output-def:%t.def
+# RUN: echo "EOF" >> %t.def
+# RUN: cat %t.def | FileCheck -check-prefix=CHECK-IMPLIB %s
+
+# CHECK-IMPLIB: EXPORTS
+# CHECK-IMPLIB-NEXT: main@0 @1
+# CHECK-IMPLIB-NEXT: EOF
diff --git a/test/COFF/export-arm64.yaml b/test/COFF/export-arm64.yaml
new file mode 100644
index 000000000000..ad9a96db4100
--- /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 000000000000..461d5a033fe2
--- /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/export32.test b/test/COFF/export32.test
index 83de18b11624..34cd1a73319e 100644
--- a/test/COFF/export32.test
+++ b/test/COFF/export32.test
@@ -57,6 +57,11 @@
# RUN: lld-link /out:%t.dll /dll %t.obj /def:%t.def
# RUN: llvm-objdump -p %t.dll | FileCheck -check-prefix=CHECK5 %s
+# RUN: echo "EXPORTS exportfn1 @ 3" > %t.def
+# RUN: echo "fn2=exportfn2 @ 2" >> %t.def
+# RUN: lld-link /out:%t.dll /dll %t.obj /def:%t.def
+# RUN: llvm-objdump -p %t.dll | FileCheck -check-prefix=CHECK5 %s
+
# CHECK5: Export Table:
# CHECK5: DLL name: export32.test.tmp.dll
# CHECK5: Ordinal RVA Name
diff --git a/test/COFF/filename-casing.s b/test/COFF/filename-casing.s
new file mode 100644
index 000000000000..e210aea9358a
--- /dev/null
+++ b/test/COFF/filename-casing.s
@@ -0,0 +1,14 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc -o %T/MixedCase.obj %s
+# RUN: not lld-link /entry:main %T/MixedCase.obj 2>&1 | FileCheck -check-prefix=OBJECT %s
+
+# RUN: llvm-lib /out:%T/MixedCase.lib %T/MixedCase.obj
+# RUN: not lld-link /machine:x64 /entry:main %T/MixedCase.lib 2>&1 | FileCheck -check-prefix=ARCHIVE %s
+
+# OBJECT: MixedCase.obj: undefined symbol: f
+# ARCHIVE: MixedCase.lib(MixedCase.obj): undefined symbol: f
+
+.globl main
+main:
+ callq f
diff --git a/test/COFF/force.test b/test/COFF/force.test
index 80bd275558f6..b96c1f84cd44 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
index 4f99d705301c..57dca5870d0f 100644
--- a/test/COFF/guardcf.test
+++ b/test/COFF/guardcf.test
@@ -71,4 +71,10 @@ symbols:
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: __enclave_config
+ 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 e987bb953890..2399193da6a1 100644
--- a/test/COFF/hello32.test
+++ b/test/COFF/hello32.test
@@ -39,13 +39,13 @@ HEADER-NEXT: MajorImageVersion: 0
HEADER-NEXT: MinorImageVersion: 0
HEADER-NEXT: MajorSubsystemVersion: 6
HEADER-NEXT: MinorSubsystemVersion: 0
-HEADER-NEXT: SizeOfImage: 16896
+HEADER-NEXT: SizeOfImage: 20480
HEADER-NEXT: SizeOfHeaders: 512
HEADER-NEXT: Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI (0x3)
-HEADER-NEXT: Characteristics [ (0x9940)
+HEADER-NEXT: Characteristics [ (0x9540)
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_NO_SEH (0x400)
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 bfaeabb4d41a..3b2aa5de41b1 100644
--- a/test/COFF/icf-associative.test
+++ b/test/COFF/icf-associative.test
@@ -1,5 +1,5 @@
# RUN: yaml2obj < %s > %t.obj
-# RUN: lld-link /entry:foo /out:%t.exe /subsystem:console /include:bar \
+# RUN: lld-link /opt:icf /entry:foo /out:%t.exe /subsystem:console /include:bar \
# RUN: /debug /verbose %t.obj > %t.log 2>&1
# RUN: FileCheck %s < %t.log
diff --git a/test/COFF/icf-executable.s b/test/COFF/icf-executable.s
new file mode 100644
index 000000000000..7f923d25ca45
--- /dev/null
+++ b/test/COFF/icf-executable.s
@@ -0,0 +1,18 @@
+# RUN: llvm-mc -triple=x86_64-windows-msvc %s -filetype=obj -o %t.obj
+# RUN: lld-link -entry:main %t.obj -out:%t.exe -verbose 2>&1 | FileCheck %s
+
+# CHECK: Selected internal
+# CHECK: Removed f2
+
+.section .text,"xr",one_only,internal
+internal:
+.globl main
+main:
+call f2
+ret
+
+.section .text,"xr",one_only,f2
+.globl f2
+f2:
+call main
+ret
diff --git a/test/COFF/icf-simple.test b/test/COFF/icf-simple.test
index c302c8796a95..ead7e7679ce6 100644
--- a/test/COFF/icf-simple.test
+++ b/test/COFF/icf-simple.test
@@ -1,5 +1,5 @@
# RUN: yaml2obj < %s > %t.obj
-# RUN: lld-link /entry:foo /out:%t.exe /subsystem:console /include:bar \
+# RUN: lld-link /opt:icf /entry:foo /out:%t.exe /subsystem:console /include:bar \
# RUN: /verbose %t.obj > %t.log 2>&1
# RUN: FileCheck -check-prefix=ICF %s < %t.log
@@ -13,6 +13,31 @@
# RUN: /verbose /opt:noref,noicf %t.obj > %t.log 2>&1
# RUN: FileCheck -check-prefix=NOICF %s < %t.log
+# ICF is on by default (no /opt: flags).
+# RUN: lld-link /entry:foo /out:%t.exe /subsystem:console \
+# RUN: /include:bar /verbose %t.obj > %t.log 2>&1
+# RUN: FileCheck -check-prefix=ICF %s < %t.log
+
+# /debug disables ICF.
+# RUN: lld-link /debug /entry:foo /out:%t.exe /subsystem:console \
+# RUN: /include:bar /verbose %t.obj > %t.log 2>&1
+# RUN: FileCheck -check-prefix=NOICF %s < %t.log
+
+# /opt:noref disables ICF.
+# RUN: lld-link /opt:noref /entry:foo /out:%t.exe /subsystem:console \
+# RUN: /include:bar /verbose %t.obj > %t.log 2>&1
+# RUN: FileCheck -check-prefix=NOICF %s < %t.log
+
+# /debug /opt:ref enables ICF.
+# RUN: lld-link /debug /opt:ref /entry:foo /out:%t.exe /subsystem:console \
+# RUN: /include:bar /verbose %t.obj > %t.log 2>&1
+# RUN: FileCheck -check-prefix=ICF %s < %t.log
+
+# /debug /opt:noicf,ref disables ICF.
+# RUN: lld-link /debug /opt:noicf,ref /entry:foo /out:%t.exe /subsystem:console \
+# RUN: /include:bar /verbose %t.obj > %t.log 2>&1
+# RUN: FileCheck -check-prefix=NOICF %s < %t.log
+
# NOICF-NOT: Removed foo
# NOICF-NOT: Removed bar
diff --git a/test/COFF/icf-xdata.s b/test/COFF/icf-xdata.s
new file mode 100644
index 000000000000..8fb4bad057bd
--- /dev/null
+++ b/test/COFF/icf-xdata.s
@@ -0,0 +1,86 @@
+# RUN: llvm-mc %s -triple x86_64-windows-msvc -filetype=obj -o %t.obj
+# RUN: lld-link %t.obj -dll -noentry -out:%t.dll
+# RUN: llvm-readobj -sections %t.dll | FileCheck %s
+
+# There shouldn't be much xdata, because all three .pdata entries (12 bytes
+# each) should use the same .xdata unwind info.
+# CHECK: Name: .pdata
+# CHECK-NEXT: VirtualSize: 0x24
+# CHECK: Name: .xdata
+# CHECK-NEXT: VirtualSize: 0x8
+
+ .text
+callee:
+ ret
+
+ .def xdata1;
+ .scl 2;
+ .type 32;
+ .endef
+ .section .text,"xr",one_only,xdata1
+ .globl xdata1 # -- Begin function xdata1
+ .p2align 4, 0x90
+xdata1: # @xdata1
+.seh_proc xdata1
+# BB#0: # %entry
+ subq $40, %rsp
+ .seh_stackalloc 40
+ .seh_endprologue
+ callq callee
+ nop
+ addq $40, %rsp
+ jmp callee # TAILCALL
+ .seh_handlerdata
+ .section .text,"xr",one_only,xdata1
+ .seh_endproc
+ # -- End function
+ .def xdata2;
+ .scl 2;
+ .type 32;
+ .endef
+ .section .text,"xr",one_only,xdata2
+ .globl xdata2 # -- Begin function xdata2
+ .p2align 4, 0x90
+xdata2: # @xdata2
+.seh_proc xdata2
+# BB#0: # %entry
+ subq $40, %rsp
+ .seh_stackalloc 40
+ .seh_endprologue
+ callq callee
+ callq callee
+ nop
+ addq $40, %rsp
+ jmp callee # TAILCALL
+ .seh_handlerdata
+ .section .text,"xr",one_only,xdata2
+ .seh_endproc
+ # -- End function
+ .def xdata3;
+ .scl 2;
+ .type 32;
+ .endef
+ .section .text,"xr",one_only,xdata3
+ .globl xdata3 # -- Begin function xdata3
+ .p2align 4, 0x90
+xdata3: # @xdata3
+.seh_proc xdata3
+# BB#0: # %entry
+ subq $40, %rsp
+ .seh_stackalloc 40
+ .seh_endprologue
+ callq callee
+ callq callee
+ callq callee
+ nop
+ addq $40, %rsp
+ jmp callee # TAILCALL
+ .seh_handlerdata
+ .section .text,"xr",one_only,xdata3
+ .seh_endproc
+ # -- End function
+ .section .drectve,"yn"
+ .ascii " -export:xdata1"
+ .ascii " -export:xdata2"
+ .ascii " -export:xdata3"
+
diff --git a/test/COFF/include.test b/test/COFF/include.test
index e7b0c58d5a19..e7e9095cff8b 100644
--- a/test/COFF/include.test
+++ b/test/COFF/include.test
@@ -62,6 +62,19 @@ symbols:
CheckSum: 0
Number: 0
Selection: IMAGE_COMDAT_SELECT_ANY
+ - 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: 0
+ Number: 0
+ Selection: IMAGE_COMDAT_SELECT_ANY
- Name: main
Value: 0
SectionNumber: 1
diff --git a/test/COFF/libpath.test b/test/COFF/libpath.test
index da465bc556bc..77b4c546c992 100644
--- a/test/COFF/libpath.test
+++ b/test/COFF/libpath.test
@@ -5,14 +5,22 @@
# RUN: env LIB=%t/a lld-link /out:%t.exe /entry:main /verbose \
# RUN: std64.lib /subsystem:console %p/Inputs/hello64.obj \
-# RUN: /libpath:%t/b /libpath:%t/c > %t.log
+# RUN: /libpath:%t/b /libpath:%t/c 2> %t.log
# RUN: FileCheck -check-prefix=CHECK1 %s < %t.log
CHECK1: b{{[/\\]}}std64.lib
# RUN: lld-link /out:%t.exe /entry:main /verbose \
# RUN: std64.lib /subsystem:console %p/Inputs/hello64.obj \
-# RUN: /libpath:%t/a /libpath:%t/b /libpath:%t/c > %t.log
+# RUN: /libpath:%t/a /libpath:%t/b /libpath:%t/c 2> %t.log
# RUN: FileCheck -check-prefix=CHECK2 %s < %t.log
CHECK2: a{{[/\\]}}std64.lib
+
+# RUN: lld-link /out:%t.exe /entry:main /verbose \
+# RUN: %t/a/std64.lib /subsystem:console %p/Inputs/hello64.obj \
+# RUN: /libpath:%t/b /verbose > %t.log 2>&1
+# RUN: FileCheck -check-prefix=CHECK3 %s < %t.log
+
+CHECK3: Reading {{.*}}a/std64.lib
+CHECK3-NOT: Reading {{.*}}b/std64.lib
diff --git a/test/COFF/linkrepro-manifest.test b/test/COFF/linkrepro-manifest.test
new file mode 100644
index 000000000000..a938ab590b2e
--- /dev/null
+++ b/test/COFF/linkrepro-manifest.test
@@ -0,0 +1,12 @@
+REQUIRES: x86, gnutar, manifest_tool
+
+RUN: rm -rf %t && mkdir %t && cd %t
+RUN: lld-link -entry:__ImageBase -nodefaultlib -linkrepro:%t -manifest:embed %p/Inputs/std32.lib -subsystem:console
+RUN: tar tf repro.tar | FileCheck --check-prefix=LIST %s
+RUN: tar xOf repro.tar repro/response.txt | FileCheck %s
+
+LIST: manifest.res
+
+CHECK-NOT: -manifest:
+CHECK: .manifest.res
+CHECK-NOT: -manifest:
diff --git a/test/COFF/linkrepro-pdb.test b/test/COFF/linkrepro-pdb.test
new file mode 100644
index 000000000000..33aa0bc4a90e
--- /dev/null
+++ b/test/COFF/linkrepro-pdb.test
@@ -0,0 +1,9 @@
+REQUIRES: x86, gnutar
+
+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 -linkrepro:.
+RUN: tar xOf repro.tar repro/%:t/ts.pdb > repro-ts.pdb
+RUN: diff ts.pdb repro-ts.pdb
diff --git a/test/COFF/linkrepro-res.test b/test/COFF/linkrepro-res.test
new file mode 100644
index 000000000000..cf0aa1636ce2
--- /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/loadcfg.test b/test/COFF/loadcfg.test
index b74917f15707..072ee6b1edb8 100644
--- a/test/COFF/loadcfg.test
+++ b/test/COFF/loadcfg.test
@@ -14,10 +14,6 @@ sections:
Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
Alignment: 4
SectionData: B82A000000C3
- - Name: .text
- Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
- Alignment: 4
- SectionData: B82A000000C3
- Name: .rdata
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
Alignment: 16
@@ -35,19 +31,6 @@ symbols:
NumberOfLinenumbers: 0
CheckSum: 0
Number: 0
- - Name: .text
- Value: 0
- SectionNumber: 2
- 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
- Selection: IMAGE_COMDAT_SELECT_ANY
- Name: main
Value: 0
SectionNumber: 1
@@ -56,7 +39,7 @@ symbols:
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
- Name: .rdata
Value: 0
- SectionNumber: 3
+ SectionNumber: 2
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_STATIC
@@ -68,7 +51,7 @@ symbols:
Number: 3
- Name: _load_config_used
Value: 0
- SectionNumber: 3
+ SectionNumber: 2
SimpleType: IMAGE_SYM_TYPE_NULL
ComplexType: IMAGE_SYM_DTYPE_NULL
StorageClass: IMAGE_SYM_CLASS_EXTERNAL
diff --git a/test/COFF/locally-imported-arm64.test b/test/COFF/locally-imported-arm64.test
new file mode 100644
index 000000000000..35200ffc1e8d
--- /dev/null
+++ b/test/COFF/locally-imported-arm64.test
@@ -0,0 +1,61 @@
+# RUN: yaml2obj < %s > %t.obj
+# RUN: lld-link /out:%t.exe /entry:main %t.obj
+# RUN: llvm-objdump -s %t.exe | FileCheck %s
+# RUN: llvm-readobj -coff-basereloc %t.exe | FileCheck -check-prefix=BASEREL %s
+
+# CHECK: Contents of section .text:
+# CHECK-NEXT: 1000 00200000
+# CHECK: Contents of section .rdata:
+# CHECK-NEXT: 2000 04100040 01000000
+
+# BASEREL: BaseReloc [
+# BASEREL-NEXT: Entry {
+# BASEREL-NEXT: Type: DIR64
+# BASEREL-NEXT: Address: 0x2000
+# BASEREL-NEXT: }
+# BASEREL-NEXT: Entry {
+# BASEREL-NEXT: Type: ABSOLUTE
+# BASEREL-NEXT: Address: 0x2000
+# BASEREL-NEXT: }
+# BASEREL-NEXT: ]
+
+--- !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: 00000000
+ Relocations:
+ - VirtualAddress: 0
+ SymbolName: __imp_main
+ Type: IMAGE_REL_ARM64_ADDR32NB
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ 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: 0
+ Selection: IMAGE_COMDAT_SELECT_ANY
+ - Name: main
+ Value: 4
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: __imp_main
+ Value: 0
+ SectionNumber: 0
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/locally-imported-warn-multiple.s b/test/COFF/locally-imported-warn-multiple.s
new file mode 100644
index 000000000000..247ec58b0fdb
--- /dev/null
+++ b/test/COFF/locally-imported-warn-multiple.s
@@ -0,0 +1,14 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc -o %T/locally-imported-def.obj %S/Inputs/locally-imported-def.s
+# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc -o %T/locally-imported-imp1.obj %S/Inputs/locally-imported-imp.s
+# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc -o %T/locally-imported-imp2.obj %S/Inputs/locally-imported-imp.s
+# RUN: llvm-mc -filetype=obj -triple=x86_64-windows-msvc -o %t.obj %s
+# RUN: lld-link /entry:main %T/locally-imported-def.obj %T/locally-imported-imp1.obj %T/locally-imported-imp2.obj %t.obj 2>&1 | FileCheck %s
+
+# CHECK: warning: [[TESTDIR:.+]]locally-imported-imp1.obj: locally defined symbol imported: f (defined in [[TESTDIR]]locally-imported-def.obj)
+# CHECK-NEXT: warning: [[TESTDIR:.+]]locally-imported-imp2.obj: locally defined symbol imported: f (defined in [[TESTDIR]]locally-imported-def.obj)
+
+.globl main
+main:
+ ret
diff --git a/test/COFF/locally-imported.test b/test/COFF/locally-imported.test
index a10da4b11bcb..b269cecf00b7 100644
--- a/test/COFF/locally-imported.test
+++ b/test/COFF/locally-imported.test
@@ -1,8 +1,10 @@
# RUN: yaml2obj < %s > %t.obj
-# RUN: lld-link /out:%t.exe /entry:main %t.obj
+# RUN: lld-link /out:%t.exe /entry:main %t.obj 2>&1 | FileCheck -check-prefix=WARN %s
# RUN: llvm-objdump -s %t.exe | FileCheck %s
# RUN: llvm-readobj -coff-basereloc %t.exe | FileCheck -check-prefix=BASEREL %s
+# WARN: warning: [[INPUT:.+]]: locally defined symbol imported: main (defined in [[INPUT]])
+
# CHECK: Contents of section .text:
# CHECK-NEXT: 1000 00200000
# CHECK: Contents of section .rdata:
diff --git a/test/COFF/long-section-name.test b/test/COFF/long-section-name.test
index 1de329db0296..bad7876c888f 100644
--- a/test/COFF/long-section-name.test
+++ b/test/COFF/long-section-name.test
@@ -1,7 +1,10 @@
# RUN: yaml2obj < %s > %t.obj
-# RUN: lld-link /debug /out:%t.exe /entry:main %t.obj
+# RUN: lld-link /out:%t.exe /entry:main %t.obj
# RUN: llvm-readobj -sections %t.exe | FileCheck %s
+# RUN: lld-link /debug /out:%t2.exe /entry:main %t.obj
+# RUN: llvm-readobj -sections %t2.exe | FileCheck %s
+# CHECK: Name: .eh_fram (
# CHECK: Name: .data_long_section_name
# CHECK: Name: .text_long_section_name
@@ -11,10 +14,14 @@ header:
Characteristics: [ ]
sections:
- Name: .text_long_section_name
- Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_DISCARDABLE ]
Alignment: 4
SectionData: B82A000000C3
- Name: .data_long_section_name
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE, IMAGE_SCN_MEM_DISCARDABLE ]
+ Alignment: 4
+ SectionData: "00"
+ - Name: .eh_frame
Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
Alignment: 4
SectionData: "00"
@@ -49,6 +56,18 @@ symbols:
NumberOfLinenumbers: 0
CheckSum: 0
Number: 0
+ - Name: .eh_frame
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 0
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
- Name: main
Value: 0
SectionNumber: 1
diff --git a/test/COFF/lto-cache.ll b/test/COFF/lto-cache.ll
new file mode 100644
index 000000000000..ad1a3b71f19a
--- /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-opt-level.ll b/test/COFF/lto-opt-level.ll
index 674b6cc0f934..cacd0637731a 100644
--- a/test/COFF/lto-opt-level.ll
+++ b/test/COFF/lto-opt-level.ll
@@ -1,10 +1,10 @@
; RUN: llvm-as -o %t.obj %s
-; RUN: lld-link /out:%t0.exe /entry:main /subsystem:console /opt:lldlto=0 /debug %t.obj
-; RUN: llvm-nm %t0.exe | FileCheck --check-prefix=CHECK-O0 %s
-; RUN: lld-link /out:%t2.exe /entry:main /subsystem:console /opt:lldlto=2 /debug %t.obj
-; RUN: llvm-nm %t2.exe | FileCheck --check-prefix=CHECK-O2 %s
-; RUN: lld-link /out:%t2a.exe /entry:main /subsystem:console /debug %t.obj
-; RUN: llvm-nm %t2a.exe | FileCheck --check-prefix=CHECK-O2 %s
+; RUN: lld-link /out:%t0.exe /entry:main /subsystem:console /opt:lldlto=0 /lldmap:%t0.map %t.obj
+; RUN: FileCheck --check-prefix=CHECK-O0 %s < %t0.map
+; RUN: lld-link /out:%t2.exe /entry:main /subsystem:console /opt:lldlto=2 /lldmap:%t2.map %t.obj
+; RUN: FileCheck --check-prefix=CHECK-O2 %s < %t2.map
+; RUN: lld-link /out:%t2a.exe /entry:main /subsystem:console /lldmap:%t2a.map %t.obj
+; RUN: FileCheck --check-prefix=CHECK-O2 %s < %t2a.map
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-windows-msvc"
diff --git a/test/COFF/lto-reloc-model.ll b/test/COFF/lto-reloc-model.ll
new file mode 100644
index 000000000000..bea19e9ce3e9
--- /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 accec48d6866..31587b43e2e9 100644
--- a/test/COFF/manifest.test
+++ b/test/COFF/manifest.test
@@ -57,10 +57,24 @@ DEPENDENCY: </dependentAssembly>
DEPENDENCY: </dependency>
DEPENDENCY: </assembly>
-# RUN: lld-link /manifest /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 000000000000..eca7d0d03927
--- /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 000000000000..d8dd864d533f
--- /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 95ebc22f7410..51c189098261 100644
--- a/test/COFF/manifestinput.test
+++ b/test/COFF/manifestinput.test
@@ -1,4 +1,4 @@
-# REQUIRES: win_mt
+# REQUIRES: manifest_tool
# RUN: yaml2obj %p/Inputs/ret42.yaml > %t.obj
# RUN: lld-link /out:%t.exe /entry:main \
@@ -11,7 +11,7 @@
TEST_EMBED: ResourceTableRVA: 0x1000
TEST_EMBED-NEXT: ResourceTableSize: 0x298
TEST_EMBED-DAG: Resources [
-TEST_EMBED-NEXT: Total Number of Resources: 1
+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) [
diff --git a/test/COFF/msvclto-archive.ll b/test/COFF/msvclto-archive.ll
index 334565a1bef7..f09532721024 100644
--- a/test/COFF/msvclto-archive.ll
+++ b/test/COFF/msvclto-archive.ll
@@ -9,14 +9,14 @@
; 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
; RUN: lld-link %t-main1.a %t.dir/bitcode.obj /msvclto /out:%t.exe /opt:lldlto=1 /opt:icf \
-; RUN: /entry:main /verbose > %t.log || true
+; RUN: /entry:main /verbose 2> %t.log || true
; RUN: FileCheck -check-prefix=BC %s < %t.log
; BC-NOT: Creating a temporary archive for
; RUN: rm -f %t-main2.a
; RUN: llvm-ar cru %t-main2.a %t.dir/bitcode.obj
; RUN: lld-link %t.obj %t-main2.a /msvclto /out:%t.exe /opt:lldlto=1 /opt:icf \
-; RUN: /entry:main /verbose > %t.log || true
+; RUN: /entry:main /verbose 2> %t.log || true
; RUN: FileCheck -check-prefix=OBJ %s < %t.log
; OBJ-NOT: Creating a temporary archive
@@ -25,7 +25,7 @@
; RUN: rm -f %t-main3.a
; RUN: llvm-ar cruT %t-main3.a %t.dir/bitcode.obj
; RUN: lld-link %t.obj %t-main3.a /msvclto /out:%t.exe /opt:lldlto=1 /opt:icf \
-; RUN: /entry:main /verbose > %t.log || true
+; RUN: /entry:main /verbose 2> %t.log || true
; RUN: FileCheck -check-prefix=THIN %s < %t.log
; THIN: Creating a temporary archive
diff --git a/test/COFF/msvclto-order.ll b/test/COFF/msvclto-order.ll
index 6f569af4af0c..1758077fe748 100644
--- a/test/COFF/msvclto-order.ll
+++ b/test/COFF/msvclto-order.ll
@@ -5,7 +5,7 @@
; RUN: llc -filetype=obj %S/Inputs/msvclto-order-b.ll -o %T/msvclto-order-b.obj
; RUN: llvm-ar crs %T/msvclto-order-b.lib %T/msvclto-order-b.obj
; RUN: lld-link /verbose /msvclto /out:%t.exe /entry:main %t.obj \
-; RUN: %T/msvclto-order-a.lib %T/msvclto-order-b.lib > %t.log || true
+; RUN: %T/msvclto-order-a.lib %T/msvclto-order-b.lib 2> %t.log || true
; RUN: FileCheck %s < %t.log
; CHECK: : link.exe
diff --git a/test/COFF/msvclto.ll b/test/COFF/msvclto.ll
index 66fabeb80c74..b29982737f14 100644
--- a/test/COFF/msvclto.ll
+++ b/test/COFF/msvclto.ll
@@ -3,7 +3,7 @@
; 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
; RUN: lld-link %t.obj %t.dir/bitcode.obj /msvclto /out:%t.exe /opt:lldlto=1 /opt:icf \
-; RUN: /entry:main /verbose > %t.log || true
+; RUN: /entry:main /verbose 2> %t.log || true
; RUN: FileCheck %s < %t.log
; CHECK: /opt:icf /entry:main
diff --git a/test/COFF/nodefaultlib.test b/test/COFF/nodefaultlib.test
index 867dc8f18e78..c0f8d50fc7ed 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/nopdb.test b/test/COFF/nopdb.test
deleted file mode 100644
index 29797bbf9310..000000000000
--- a/test/COFF/nopdb.test
+++ /dev/null
@@ -1,14 +0,0 @@
-# Check that /debug creates %t.pdb.
-# RUN: rm -f %t.pdb
-# RUN: lld-link /debug /entry:main /out:%t.exe %p/Inputs/ret42.obj
-# RUN: ls %t.pdb
-
-# Check that /debug /nopdb does not create %t.pdb.
-# RUN: rm -f %t.pdb
-# RUN: lld-link /debug /nopdb /entry:main /out:%t.exe %p/Inputs/ret42.obj
-# RUN: not ls %t.pdb
-
-# Check that /debug /nopdb /pdb:%t.pdb does not create %t.pdb.
-# RUN: rm -f %t.pdb
-# RUN: lld-link /debug /nopdb /pdb:%t.pdb /entry:main /out:%t.exe %p/Inputs/ret42.obj
-# RUN: not ls %t.pdb
diff --git a/test/COFF/options.test b/test/COFF/options.test
index a23da1971d15..39f944beddbc 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/pdata-arm64.yaml b/test/COFF/pdata-arm64.yaml
new file mode 100644
index 000000000000..f21749b9253f
--- /dev/null
+++ b/test/COFF/pdata-arm64.yaml
@@ -0,0 +1,87 @@
+# RUN: yaml2obj < %s > %t.obj
+#
+# RUN: lld-link /out:%t.exe /entry:func1 /subsystem:console %t.obj
+# RUN: llvm-objdump -s -section=.pdata %t.exe | FileCheck -check-prefix=PDATA %s
+
+# PDATA: 00200000 2500a100 24200000 31002201
+
+--- !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: ff4300d1f37b00a9f303012a00000094e003132a00000094f37b40a9ff430091c0035fd6f353bea9fe0b00f9f303012af403022a00000094e003132a00000094e003142a00000094fe0b40f9f353c2a8c0035fd6c0035fd6
+ Relocations:
+ - VirtualAddress: 12
+ SymbolName: func3
+ Type: IMAGE_REL_ARM64_BRANCH26
+ - VirtualAddress: 20
+ SymbolName: func3
+ Type: IMAGE_REL_ARM64_BRANCH26
+ - VirtualAddress: 52
+ SymbolName: func3
+ Type: IMAGE_REL_ARM64_BRANCH26
+ - VirtualAddress: 60
+ SymbolName: func3
+ Type: IMAGE_REL_ARM64_BRANCH26
+ - VirtualAddress: 68
+ SymbolName: func3
+ Type: IMAGE_REL_ARM64_BRANCH26
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: 0000000031002201000000002500a100
+ Relocations:
+ - VirtualAddress: 0
+ SymbolName: func2
+ Type: IMAGE_REL_ARM64_ADDR32NB
+ - VirtualAddress: 8
+ SymbolName: func1
+ Type: IMAGE_REL_ARM64_ADDR32NB
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 57
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 1
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 16
+ NumberOfRelocations: 2
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 2
+ - Name: func1
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: func2
+ Value: 36
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: func3
+ Value: 84
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/pdb-comdat.test b/test/COFF/pdb-comdat.test
index f85dacdbf4bb..655f215e0199 100644
--- a/test/COFF/pdb-comdat.test
+++ b/test/COFF/pdb-comdat.test
@@ -25,8 +25,8 @@ 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 t.pdb | FileCheck %s
+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: ============================================================
@@ -38,6 +38,20 @@ CHECK: c:\src\llvm-project\build\pdb_comdat_bar.c (MD5: 365279DB4FCBEDD721
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`:
@@ -46,7 +60,7 @@ 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_ID [size = 44] `main`
+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]
@@ -54,24 +68,22 @@ 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_GDATA32 [size = 24] `global`
-CHECK: type = 0x0074 (int), addr = 0000:0000
-CHECK: 224 | S_BUILDINFO [size = 8] BuildId = `0x100A`
-CHECK: 232 | S_GPROC32_ID [size = 44] `foo`
-CHECK: parent = 0, end = 308, addr = 0002:0032, code size = 15
+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: 276 | S_FRAMEPROC [size = 32]
+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: 308 | S_END [size = 4]
+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_ID [size = 44] `bar`
+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]
@@ -79,10 +91,8 @@ 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_GDATA32 [size = 24] `global`
-CHECK: type = 0x0074 (int), addr = 0000:0000
-CHECK: 224 | S_BUILDINFO [size = 8] BuildId = `0x100D`
-CHECK-NOT: S_GPROC32_ID {{.*}} `foo`
+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.
@@ -97,3 +107,6 @@ 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 *`:
+
+Make sure that we don't crash on non-prevailing debug sections if -debug is not enabled.
+RUN: lld-link pdb_comdat_main.obj pdb_comdat_bar.obj -out:%t.exe -nodefaultlib -entry:main
diff --git a/test/COFF/pdb-diff.test b/test/COFF/pdb-diff.test
index 79b23a5c026d..17d26b60353e 100644
--- a/test/COFF/pdb-diff.test
+++ b/test/COFF/pdb-diff.test
@@ -4,8 +4,11 @@ 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: lld-link /debug /pdb:%T/pdb-diff-lld.pdb /nodefaultlib /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/ %T/pdb-diff-lld.pdb %S/Inputs/pdb-diff-cl.pdb | FileCheck %s
+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 |
@@ -25,7 +28,7 @@ CHECK-NEXT: | Stream Directory |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | File | |
CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | Stream Count | D |
+CHECK-NEXT: | Stream Count | I |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | Old MSF Directory | I |
CHECK-NEXT: |------------------------------+---|
@@ -55,9 +58,9 @@ CHECK-NEXT: | IPI Hash | {{[EI]}} |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: | Public Symbol Hash | {{[EI]}} |
CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | Public Symbol Records | {{[EI]}} |
+CHECK-NEXT: | Global Symbol Hash | {{[EI]}} |
CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | Global Symbol Hash | D |
+CHECK-NEXT: | Symbol Records | {{[EI]}} |
CHECK-NEXT: |------------------------------+---|
CHECK-NEXT: ------------------------------------
CHECK-NEXT: | String Table |
@@ -152,7 +155,7 @@ CHECK-NEXT: | DBG (NewFPO) | {{[EI]}} |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | DBG (SectionHdrOrig) | I |
CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | Globals Stream | D |
+CHECK-NEXT: | Globals Stream | {{[EI]}} |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | Publics Stream | {{[EI]}} |
CHECK-NEXT: |----------------------------------------+---|
@@ -186,7 +189,7 @@ CHECK-NEXT: | - Pdb File Path Index | I |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | - Source File Name Index | I |
CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | - Symbol Byte Size | D |
+CHECK-NEXT: | - Symbol Byte Size |
CHECK-NEXT: |----------------------------------------+---|
CHECK-NEXT: | Module "* Linker *" |
CHECK-NEXT: |----------------------------------------+---|
diff --git a/test/COFF/pdb-global-gc.yaml b/test/COFF/pdb-global-gc.yaml
index b66b3f2ca7b8..f2c4450809b0 100644
--- a/test/COFF/pdb-global-gc.yaml
+++ b/test/COFF/pdb-global-gc.yaml
@@ -1,8 +1,8 @@
# 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 %t.pdb | FileCheck %s
+# RUN: -nodefaultlib -opt:ref -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.
@@ -12,13 +12,17 @@
# int discarded() { return __wc_mb_cur; }
# int main() { return g2; }
-# CHECK: Symbols
-# CHECK: ============================================================
-# CHECK: Mod 0000 | `{{.*}}pdb-global-gc.yaml.tmp.obj`:
-# CHECK: 4 | S_GDATA32 [size = 28] `__wc_mb_cur`
-# CHECK-NEXT: type = 0x0070 (char), addr = 0000:0000
-# CHECK: Mod 0001 | `{{.*}}pdb-global-gc.yaml.tmp2.obj`:
-# CHECK: Mod 0002 | `* Linker *`:
+# 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:
diff --git a/test/COFF/pdb-global-hashes.test b/test/COFF/pdb-global-hashes.test
new file mode 100644
index 000000000000..b47e43826537
--- /dev/null
+++ b/test/COFF/pdb-global-hashes.test
@@ -0,0 +1,93 @@
+RUN: yaml2obj %p/Inputs/pdb-hashes-1.yaml > %t.1.obj
+RUN: yaml2obj %p/Inputs/pdb-hashes-2.yaml > %t.2.obj
+RUN: yaml2obj %p/Inputs/pdb-hashes-2-missing.yaml > %t.2.missing.obj
+RUN: lld-link /debug %t.1.obj %t.2.obj /entry:main /nodefaultlib /PDB:%t.nohash.pdb
+RUN: lld-link /debug:ghash %t.1.obj %t.2.obj /entry:main /nodefaultlib /PDB:%t.hash.pdb
+RUN: lld-link /debug:ghash %t.1.obj %t.2.missing.obj /entry:main /nodefaultlib /PDB:%t.mixed.pdb
+RUN: llvm-pdbutil dump -types -ids %t.nohash.pdb | FileCheck %s
+RUN: llvm-pdbutil dump -types -ids %t.hash.pdb | FileCheck %s
+RUN: llvm-pdbutil dump -types -ids %t.mixed.pdb | FileCheck %s
+
+; These object files were generated via the following inputs and commands:
+; ----------------------------------------------
+; // obj.h
+; namespace NS {
+; struct Foo {
+; explicit Foo(int x) : X(x) {}
+; int X;
+; };
+;
+; int func(const Foo &f);
+; }
+; ----------------------------------------------
+; // obj1.cpp
+; #include "obj.h"
+;
+; int main(int argc, char **argv) {
+; NS::Foo f(argc);
+; return NS::func(f);
+; }
+; ----------------------------------------------
+; // obj2.cpp
+; #include "obj.h"
+;
+; int NS::func(const Foo &f) {
+; return 2 * f.X;
+; }
+; ----------------------------------------------
+; $ clang-cl /Z7 /GS- obj1.cpp /c /o obj1.obj
+; $ clang-cl /Z7 /GS- obj2.cpp /c /o obj2.obj
+
+CHECK: Types (TPI Stream)
+CHECK-NEXT: ============================================================
+CHECK-NEXT: Showing 13 records
+CHECK-NEXT: 0x1000 | LF_POINTER [size = 12]
+CHECK-NEXT: referent = 0x0470 (char*), mode = pointer, opts = None, kind = ptr32
+CHECK-NEXT: 0x1001 | LF_ARGLIST [size = 16]
+CHECK-NEXT: 0x0074 (int): `int`
+CHECK-NEXT: 0x1000: `char**`
+CHECK-NEXT: 0x1002 | LF_PROCEDURE [size = 16]
+CHECK-NEXT: return type = 0x0074 (int), # args = 2, param list = 0x1001
+CHECK-NEXT: calling conv = cdecl, options = None
+CHECK-NEXT: 0x1003 | LF_STRUCTURE [size = 44] `NS::Foo`
+CHECK-NEXT: unique name: `.?AUFoo@NS@@`
+CHECK-NEXT: vtable: <no type>, base list: <no type>, field list: <no type>
+CHECK-NEXT: options: forward ref | has unique name
+CHECK-NEXT: 0x1004 | LF_POINTER [size = 12]
+CHECK-NEXT: referent = 0x1003, mode = pointer, opts = None, kind = ptr32
+CHECK-NEXT: 0x1005 | LF_ARGLIST [size = 12]
+CHECK-NEXT: 0x0074 (int): `int`
+CHECK-NEXT: 0x1006 | LF_MFUNCTION [size = 28]
+CHECK-NEXT: return type = 0x0003 (void), # args = 1, param list = 0x1005
+CHECK-NEXT: class type = 0x1003, this type = 0x1004, this adjust = 0
+CHECK-NEXT: calling conv = thiscall, options = None
+CHECK-NEXT: 0x1007 | LF_FIELDLIST [size = 28]
+CHECK-NEXT: - LF_MEMBER [name = `X`, Type = 0x0074 (int), offset = 0, attrs = public]
+CHECK-NEXT: - LF_ONEMETHOD [name = `Foo`]
+CHECK-NEXT: type = 0x1006, vftable offset = -1, attrs = public
+CHECK-NEXT: 0x1008 | LF_STRUCTURE [size = 44] `NS::Foo`
+CHECK-NEXT: unique name: `.?AUFoo@NS@@`
+CHECK-NEXT: vtable: <no type>, base list: <no type>, field list: 0x1007
+CHECK-NEXT: options: has unique name
+CHECK-NEXT: 0x1009 | LF_MODIFIER [size = 12]
+CHECK-NEXT: referent = 0x1003, modifiers = const
+CHECK-NEXT: 0x100A | LF_POINTER [size = 12]
+CHECK-NEXT: referent = 0x1009, mode = ref, opts = None, kind = ptr32
+CHECK-NEXT: 0x100B | LF_ARGLIST [size = 12]
+CHECK-NEXT: 0x100A: `const NS::Foo&`
+CHECK-NEXT: 0x100C | LF_PROCEDURE [size = 16]
+CHECK-NEXT: return type = 0x0074 (int), # args = 1, param list = 0x100B
+CHECK-NEXT: calling conv = cdecl, options = None
+CHECK: Types (IPI Stream)
+CHECK-NEXT: ============================================================
+CHECK-NEXT: Showing 6 records
+CHECK-NEXT: 0x1000 | LF_FUNC_ID [size = 20]
+CHECK-NEXT: name = main, type = 0x1002, parent scope = <no type>
+CHECK-NEXT: 0x1001 | LF_STRING_ID [size = 48] ID: <no type>, String: D:\src\llvmbuild\clang\Debug\x86\obj.h
+CHECK-NEXT: 0x1002 | LF_UDT_SRC_LINE [size = 16]
+CHECK-NEXT: udt = 0x1008, file = 4097, line = 2
+CHECK-NEXT: 0x1003 | LF_MFUNC_ID [size = 16]
+CHECK-NEXT: name = Foo, type = 0x1006, class type = 0x1003
+CHECK-NEXT: 0x1004 | LF_STRING_ID [size = 12] ID: <no type>, String: NS
+CHECK-NEXT: 0x1005 | LF_FUNC_ID [size = 20]
+CHECK-NEXT: name = func, type = 0x100C, parent scope = 0x1004
diff --git a/test/COFF/pdb-globals.test b/test/COFF/pdb-globals.test
new file mode 100644
index 000000000000..b5e4f49cb458
--- /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-heapsite.yaml b/test/COFF/pdb-heapsite.yaml
new file mode 100644
index 000000000000..966ae4284890
--- /dev/null
+++ b/test/COFF/pdb-heapsite.yaml
@@ -0,0 +1,1561 @@
+# RUN: yaml2obj %s -o %t.obj
+# RUN: lld-link %t.obj -dll -debug -noentry -nodefaultlib -debug -out:%t.exe -pdb:%t.pdb
+# RUN: llvm-pdbutil dump -symbols %t.pdb | FileCheck %s
+
+# This object generated from this C++ source:
+# // t.cpp
+# void *operator new(size_t) { return nullptr; }
+# struct Foo { int x; };
+# extern "C" __declspec(dllexport) Foo *f() { return new Foo; }
+
+# Compile as:
+# $ cl -c -Z7 t.cpp
+
+# CHECK: S_HEAPALLOCSITE [size = 16]
+# CHECK-NEXT: type = 0x1000 (Foo), addr = {{.*}} call size = 5
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ ]
+sections:
+ - Name: .drectve
+ Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ]
+ Alignment: 1
+ SectionData: 2020202F44454641554C544C49423A224C4942434D5422202F44454641554C544C49423A224F4C444E414D455322202F4558504F52543A6620
+ - 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\t.obj'
+ - Kind: S_COMPILE3
+ Compile3Sym:
+ Flags: [ SecurityChecks, HotPatch ]
+ Machine: X64
+ FrontendMajor: 19
+ FrontendMinor: 11
+ FrontendBuild: 25508
+ FrontendQFE: 2
+ BackendMajor: 19
+ BackendMinor: 11
+ BackendBuild: 25508
+ BackendQFE: 2
+ Version: 'Microsoft (R) Optimizing Compiler'
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ CodeSize: 8
+ DbgStart: 5
+ DbgEnd: 7
+ FunctionType: 4104
+ Flags: [ ]
+ DisplayName: operator new
+ - 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: 35
+ Register: RSP
+ VarName: __formal
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 8
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'c:\src\llvm-project\build\t.cpp'
+ Lines:
+ - Offset: 0
+ LineStart: 1
+ IsStatement: true
+ EndDelta: 0
+ Columns:
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ CodeSize: 29
+ DbgStart: 4
+ DbgEnd: 24
+ FunctionType: 4107
+ Flags: [ ]
+ DisplayName: f
+ - Kind: S_FRAMEPROC
+ FrameProcSym:
+ TotalFrameBytes: 56
+ PaddingFrameBytes: 0
+ OffsetToPadding: 0
+ BytesOfCalleeSavedRegisters: 0
+ OffsetOfExceptionHandler: 0
+ SectionIdOfExceptionHandler: 0
+ Flags: [ AsynchronousExceptionHandling, OptimizedForSpeed ]
+ - Kind: S_HEAPALLOCSITE
+ HeapAllocationSiteSym:
+ Offset: 9
+ CallInstructionSize: 5
+ Type: 4096
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 29
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'c:\src\llvm-project\build\t.cpp'
+ Lines:
+ - Offset: 0
+ LineStart: 3
+ IsStatement: true
+ EndDelta: 0
+ Columns:
+ - !Symbols
+ Records:
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4098
+ UDTName: Foo
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4196
+ UDTName: '__vc_attributes::event_sourceAttribute'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4188
+ UDTName: '__vc_attributes::event_sourceAttribute::optimize_e'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4185
+ UDTName: '__vc_attributes::event_sourceAttribute::type_e'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4181
+ UDTName: '__vc_attributes::helper_attributes::v1_alttypeAttribute'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4175
+ UDTName: '__vc_attributes::helper_attributes::v1_alttypeAttribute::type_e'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4171
+ UDTName: '__vc_attributes::helper_attributes::usageAttribute'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4165
+ UDTName: '__vc_attributes::helper_attributes::usageAttribute::usage_e'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4161
+ UDTName: '__vc_attributes::threadingAttribute'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4153
+ UDTName: '__vc_attributes::threadingAttribute::threading_e'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4149
+ UDTName: '__vc_attributes::aggregatableAttribute'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4141
+ UDTName: '__vc_attributes::aggregatableAttribute::type_e'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4137
+ UDTName: '__vc_attributes::event_receiverAttribute'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4127
+ UDTName: '__vc_attributes::event_receiverAttribute::type_e'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4123
+ UDTName: '__vc_attributes::moduleAttribute'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4110
+ UDTName: '__vc_attributes::moduleAttribute::type_e'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 35
+ UDTName: size_t
+ - !FileChecksums
+ Checksums:
+ - FileName: 'c:\src\llvm-project\build\t.cpp'
+ Kind: MD5
+ Checksum: 3C4D132707FA572FA0869E4E4DAA7F85
+ - !StringTable
+ Strings:
+ - 'c:\src\llvm-project\build\t.cpp'
+ - !Symbols
+ Records:
+ - Kind: S_BUILDINFO
+ BuildInfoSym:
+ BuildId: 4205
+ Relocations:
+ - VirtualAddress: 152
+ SymbolName: '??2@YAPEAX_K@Z'
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 156
+ SymbolName: '??2@YAPEAX_K@Z'
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 240
+ SymbolName: '??2@YAPEAX_K@Z'
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 244
+ SymbolName: '??2@YAPEAX_K@Z'
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 312
+ SymbolName: f
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 316
+ SymbolName: f
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 355
+ SymbolName: f
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 359
+ SymbolName: f
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 380
+ SymbolName: f
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 384
+ 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_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: 4097
+ Name: Foo
+ UniqueName: '.?AUFoo@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 4
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'c:\src\llvm-project\build\t.cpp'
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4098
+ SourceFile: 4099
+ LineNumber: 2
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4096
+ Attrs: 65548
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 35 ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 1539
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 1
+ ArgumentList: 4102
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4103
+ Name: operator new
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 4101
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 0
+ ArgumentList: 4105
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4106
+ Name: f
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: '__vc_attributes::moduleAttribute'
+ UniqueName: '.?AUmoduleAttribute@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 1
+ Name: dll
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 2
+ Name: exe
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 3
+ Name: service
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 4
+ Name: unspecified
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 2
+ Name: EXE
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 3
+ Name: SERVICE
+ - Kind: LF_ENUM
+ Enum:
+ NumEnumerators: 6
+ Options: [ None, Nested, HasUniqueName ]
+ FieldList: 4109
+ Name: '__vc_attributes::moduleAttribute::type_e'
+ UniqueName: '.?AW4type_e@moduleAttribute@__vc_attributes@@'
+ UnderlyingType: 116
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'c:\src\llvm-project\build\predefined c++ attributes (compiler internal)'
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4110
+ SourceFile: 4111
+ LineNumber: 482
+ - Kind: LF_MODIFIER
+ Modifier:
+ ModifiedType: 112
+ Modifiers: [ None, Const ]
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4113
+ Attrs: 65548
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4110, 4114, 4114, 4114, 116, 48, 4114, 116,
+ 4114, 4114, 116, 48, 48, 4114, 4114 ]
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4108
+ Attrs: 66572
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4108
+ ThisType: 4116
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 15
+ ArgumentList: 4115
+ ThisPointerAdjustment: 0
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4110 ]
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4108
+ ThisType: 4116
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 1
+ ArgumentList: 4118
+ ThisPointerAdjustment: 0
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4108
+ ThisType: 4116
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 0
+ ArgumentList: 4105
+ ThisPointerAdjustment: 0
+ - Kind: LF_METHODLIST
+ MethodOverloadList:
+ Methods:
+ - Type: 4117
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Type: 4119
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Type: 4120
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_NESTTYPE
+ NestedType:
+ Type: 4110
+ Name: type_e
+ - Kind: LF_METHOD
+ OverloadedMethod:
+ NumOverloads: 3
+ MethodList: 4121
+ Name: moduleAttribute
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4110
+ FieldOffset: 0
+ Name: type
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4114
+ FieldOffset: 8
+ Name: name
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4114
+ FieldOffset: 16
+ Name: version
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4114
+ FieldOffset: 24
+ Name: uuid
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 116
+ FieldOffset: 32
+ Name: lcid
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 48
+ FieldOffset: 36
+ Name: control
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4114
+ FieldOffset: 40
+ Name: helpstring
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 116
+ FieldOffset: 48
+ Name: helpstringcontext
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4114
+ FieldOffset: 56
+ Name: helpstringdll
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4114
+ FieldOffset: 64
+ Name: helpfile
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 116
+ FieldOffset: 72
+ Name: helpcontext
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 48
+ FieldOffset: 76
+ Name: hidden
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 48
+ FieldOffset: 77
+ Name: restricted
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4114
+ FieldOffset: 80
+ Name: custom
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4114
+ FieldOffset: 88
+ Name: resource_name
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 19
+ Options: [ None, HasConstructorOrDestructor, ContainsNestedClass, HasUniqueName ]
+ FieldList: 4122
+ Name: '__vc_attributes::moduleAttribute'
+ UniqueName: '.?AUmoduleAttribute@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 96
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4123
+ SourceFile: 4111
+ LineNumber: 481
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: '__vc_attributes::event_receiverAttribute'
+ UniqueName: '.?AUevent_receiverAttribute@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 0
+ Name: native
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 1
+ Name: com
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 2
+ Name: managed
+ - Kind: LF_ENUM
+ Enum:
+ NumEnumerators: 3
+ Options: [ None, Nested, HasUniqueName ]
+ FieldList: 4126
+ Name: '__vc_attributes::event_receiverAttribute::type_e'
+ UniqueName: '.?AW4type_e@event_receiverAttribute@__vc_attributes@@'
+ UnderlyingType: 116
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4127
+ SourceFile: 4111
+ LineNumber: 136
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4127, 48 ]
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4125
+ Attrs: 66572
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4125
+ ThisType: 4130
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 2
+ ArgumentList: 4129
+ ThisPointerAdjustment: 0
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4127 ]
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4125
+ ThisType: 4130
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 1
+ ArgumentList: 4132
+ ThisPointerAdjustment: 0
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4125
+ ThisType: 4130
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 0
+ ArgumentList: 4105
+ ThisPointerAdjustment: 0
+ - Kind: LF_METHODLIST
+ MethodOverloadList:
+ Methods:
+ - Type: 4131
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Type: 4133
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Type: 4134
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_NESTTYPE
+ NestedType:
+ Type: 4127
+ Name: type_e
+ - Kind: LF_METHOD
+ OverloadedMethod:
+ NumOverloads: 3
+ MethodList: 4135
+ Name: event_receiverAttribute
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4127
+ FieldOffset: 0
+ Name: type
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 48
+ FieldOffset: 4
+ Name: layout_dependent
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 6
+ Options: [ None, HasConstructorOrDestructor, ContainsNestedClass, HasUniqueName ]
+ FieldList: 4136
+ Name: '__vc_attributes::event_receiverAttribute'
+ UniqueName: '.?AUevent_receiverAttribute@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 8
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4137
+ SourceFile: 4111
+ LineNumber: 135
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: '__vc_attributes::aggregatableAttribute'
+ UniqueName: '.?AUaggregatableAttribute@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 0
+ Name: never
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 1
+ Name: allowed
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 2
+ Name: always
+ - Kind: LF_ENUM
+ Enum:
+ NumEnumerators: 3
+ Options: [ None, Nested, HasUniqueName ]
+ FieldList: 4140
+ Name: '__vc_attributes::aggregatableAttribute::type_e'
+ UniqueName: '.?AW4type_e@aggregatableAttribute@__vc_attributes@@'
+ UnderlyingType: 116
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4141
+ SourceFile: 4111
+ LineNumber: 545
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4141 ]
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4139
+ Attrs: 66572
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4139
+ ThisType: 4144
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 1
+ ArgumentList: 4143
+ ThisPointerAdjustment: 0
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4139
+ ThisType: 4144
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 0
+ ArgumentList: 4105
+ ThisPointerAdjustment: 0
+ - Kind: LF_METHODLIST
+ MethodOverloadList:
+ Methods:
+ - Type: 4145
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Type: 4146
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_NESTTYPE
+ NestedType:
+ Type: 4141
+ Name: type_e
+ - Kind: LF_METHOD
+ OverloadedMethod:
+ NumOverloads: 2
+ MethodList: 4147
+ Name: aggregatableAttribute
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4141
+ FieldOffset: 0
+ Name: type
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 4
+ Options: [ None, HasConstructorOrDestructor, ContainsNestedClass, HasUniqueName ]
+ FieldList: 4148
+ Name: '__vc_attributes::aggregatableAttribute'
+ UniqueName: '.?AUaggregatableAttribute@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 4
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4149
+ SourceFile: 4111
+ LineNumber: 544
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: '__vc_attributes::threadingAttribute'
+ UniqueName: '.?AUthreadingAttribute@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 1
+ Name: apartment
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 2
+ Name: single
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 3
+ Name: free
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 4
+ Name: neutral
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 5
+ Name: both
+ - Kind: LF_ENUM
+ Enum:
+ NumEnumerators: 5
+ Options: [ None, Nested, HasUniqueName ]
+ FieldList: 4152
+ Name: '__vc_attributes::threadingAttribute::threading_e'
+ UniqueName: '.?AW4threading_e@threadingAttribute@__vc_attributes@@'
+ UnderlyingType: 116
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4153
+ SourceFile: 4111
+ LineNumber: 423
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4153 ]
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4151
+ Attrs: 66572
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4151
+ ThisType: 4156
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 1
+ ArgumentList: 4155
+ ThisPointerAdjustment: 0
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4151
+ ThisType: 4156
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 0
+ ArgumentList: 4105
+ ThisPointerAdjustment: 0
+ - Kind: LF_METHODLIST
+ MethodOverloadList:
+ Methods:
+ - Type: 4157
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Type: 4158
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_NESTTYPE
+ NestedType:
+ Type: 4153
+ Name: threading_e
+ - Kind: LF_METHOD
+ OverloadedMethod:
+ NumOverloads: 2
+ MethodList: 4159
+ Name: threadingAttribute
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4153
+ FieldOffset: 0
+ Name: value
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 4
+ Options: [ None, HasConstructorOrDestructor, ContainsNestedClass, HasUniqueName ]
+ FieldList: 4160
+ Name: '__vc_attributes::threadingAttribute'
+ UniqueName: '.?AUthreadingAttribute@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 4
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4161
+ SourceFile: 4111
+ LineNumber: 422
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: '__vc_attributes::helper_attributes::usageAttribute'
+ UniqueName: '.?AUusageAttribute@helper_attributes@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 0
+ Name: eAnyUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 1
+ Name: eCoClassUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 2
+ Name: eCOMInterfaceUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 6
+ Name: eInterfaceUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 8
+ Name: eMemberUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 16
+ Name: eMethodUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 32
+ Name: eInterfaceMethodUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 64
+ Name: eInterfaceMemberUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 128
+ Name: eCoClassMemberUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 256
+ Name: eCoClassMethodUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 768
+ Name: eGlobalMethodUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 1024
+ Name: eGlobalDataUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 2048
+ Name: eClassUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 4096
+ Name: eInterfaceParameterUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 12288
+ Name: eMethodParameterUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 16384
+ Name: eIDLModuleUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 32768
+ Name: eAnonymousUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 65536
+ Name: eTypedefUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 131072
+ Name: eUnionUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 262144
+ Name: eEnumUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 524288
+ Name: eDefineTagUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 1048576
+ Name: eStructUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 2097152
+ Name: eLocalUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 4194304
+ Name: ePropertyUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 8388608
+ Name: eEventUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 16777216
+ Name: eTemplateUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 16777216
+ Name: eModuleUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 33554432
+ Name: eIllegalUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 67108864
+ Name: eAsynchronousUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 4161535
+ Name: eAnyIDLUsage
+ - Kind: LF_ENUM
+ Enum:
+ NumEnumerators: 30
+ Options: [ None, Nested, HasUniqueName ]
+ FieldList: 4164
+ Name: '__vc_attributes::helper_attributes::usageAttribute::usage_e'
+ UniqueName: '.?AW4usage_e@usageAttribute@helper_attributes@__vc_attributes@@'
+ UnderlyingType: 116
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4165
+ SourceFile: 4111
+ LineNumber: 51
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 117 ]
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4163
+ Attrs: 66572
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4163
+ ThisType: 4168
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 1
+ ArgumentList: 4167
+ ThisPointerAdjustment: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_NESTTYPE
+ NestedType:
+ Type: 4165
+ Name: usage_e
+ - Kind: LF_ONEMETHOD
+ OneMethod:
+ Type: 4169
+ Attrs: 3
+ VFTableOffset: -1
+ Name: usageAttribute
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 117
+ FieldOffset: 0
+ Name: value
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 3
+ Options: [ None, HasConstructorOrDestructor, ContainsNestedClass, HasUniqueName ]
+ FieldList: 4170
+ Name: '__vc_attributes::helper_attributes::usageAttribute'
+ UniqueName: '.?AUusageAttribute@helper_attributes@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 4
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4171
+ SourceFile: 4111
+ LineNumber: 49
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: '__vc_attributes::helper_attributes::v1_alttypeAttribute'
+ UniqueName: '.?AUv1_alttypeAttribute@helper_attributes@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 0
+ Name: eBoolean
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 1
+ Name: eInteger
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 2
+ Name: eFloat
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 3
+ Name: eDouble
+ - Kind: LF_ENUM
+ Enum:
+ NumEnumerators: 4
+ Options: [ None, Nested, HasUniqueName ]
+ FieldList: 4174
+ Name: '__vc_attributes::helper_attributes::v1_alttypeAttribute::type_e'
+ UniqueName: '.?AW4type_e@v1_alttypeAttribute@helper_attributes@__vc_attributes@@'
+ UnderlyingType: 116
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4175
+ SourceFile: 4111
+ LineNumber: 38
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4175 ]
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4173
+ Attrs: 66572
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4173
+ ThisType: 4178
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 1
+ ArgumentList: 4177
+ ThisPointerAdjustment: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_NESTTYPE
+ NestedType:
+ Type: 4175
+ Name: type_e
+ - Kind: LF_ONEMETHOD
+ OneMethod:
+ Type: 4179
+ Attrs: 3
+ VFTableOffset: -1
+ Name: v1_alttypeAttribute
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4175
+ FieldOffset: 0
+ Name: type
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 3
+ Options: [ None, HasConstructorOrDestructor, ContainsNestedClass, HasUniqueName ]
+ FieldList: 4180
+ Name: '__vc_attributes::helper_attributes::v1_alttypeAttribute'
+ UniqueName: '.?AUv1_alttypeAttribute@helper_attributes@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 4
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4181
+ SourceFile: 4111
+ LineNumber: 37
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: '__vc_attributes::event_sourceAttribute'
+ UniqueName: '.?AUevent_sourceAttribute@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 0
+ Name: native
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 1
+ Name: com
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 2
+ Name: managed
+ - Kind: LF_ENUM
+ Enum:
+ NumEnumerators: 3
+ Options: [ None, Nested, HasUniqueName ]
+ FieldList: 4184
+ Name: '__vc_attributes::event_sourceAttribute::type_e'
+ UniqueName: '.?AW4type_e@event_sourceAttribute@__vc_attributes@@'
+ UnderlyingType: 116
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4185
+ SourceFile: 4111
+ LineNumber: 1142
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 0
+ Name: speed
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 1
+ Name: size
+ - Kind: LF_ENUM
+ Enum:
+ NumEnumerators: 2
+ Options: [ None, Nested, HasUniqueName ]
+ FieldList: 4187
+ Name: '__vc_attributes::event_sourceAttribute::optimize_e'
+ UniqueName: '.?AW4optimize_e@event_sourceAttribute@__vc_attributes@@'
+ UnderlyingType: 116
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4188
+ SourceFile: 4111
+ LineNumber: 1145
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4185 ]
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4183
+ Attrs: 66572
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4183
+ ThisType: 4191
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 1
+ ArgumentList: 4190
+ ThisPointerAdjustment: 0
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4183
+ ThisType: 4191
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 0
+ ArgumentList: 4105
+ ThisPointerAdjustment: 0
+ - Kind: LF_METHODLIST
+ MethodOverloadList:
+ Methods:
+ - Type: 4192
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Type: 4193
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_NESTTYPE
+ NestedType:
+ Type: 4185
+ Name: type_e
+ - Kind: LF_NESTTYPE
+ NestedType:
+ Type: 4188
+ Name: optimize_e
+ - Kind: LF_METHOD
+ OverloadedMethod:
+ NumOverloads: 2
+ MethodList: 4194
+ Name: event_sourceAttribute
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4185
+ FieldOffset: 0
+ Name: type
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4188
+ FieldOffset: 4
+ Name: optimize
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 48
+ FieldOffset: 8
+ Name: decorate
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 7
+ Options: [ None, HasConstructorOrDestructor, ContainsNestedClass, HasUniqueName ]
+ FieldList: 4195
+ Name: '__vc_attributes::event_sourceAttribute'
+ UniqueName: '.?AUevent_sourceAttribute@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 12
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4196
+ SourceFile: 4111
+ LineNumber: 1141
+ - 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\2017\PROFES~1\VC\Tools\MSVC\14.11.25503\bin\HostX64\x64\cl.exe'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: '-c -Z7 -MT -IC:\PROGRA~2\MICROS~1\2017\PROFES~1\VC\Tools\MSVC\14.11.25503\ATLMFC\include -IC:\PROGRA~2\MICROS~1\2017\PROFES~1\VC\Tools\MSVC\14.11.25503\include -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.16299.0\ucrt -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.16299.0\'
+ - Kind: LF_SUBSTR_LIST
+ StringList:
+ StringIndices: [ 4200 ]
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 4201
+ String: 'shared -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.16299.0\um -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.16299.0\winrt -TP -X'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: t.cpp
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\src\llvm-project\build\vc140.pdb'
+ - Kind: LF_BUILDINFO
+ BuildInfo:
+ ArgIndices: [ 4198, 4199, 4203, 4204, 4202 ]
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 48894C240833C0C3CCCCCCCCCCCCCCCC4883EC38B904000000E8000000004889442420488B4424204883C438C3
+ Relocations:
+ - VirtualAddress: 26
+ SymbolName: '??2@YAPEAX_K@Z'
+ 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: 000000001D00000000000000
+ Relocations:
+ - VirtualAddress: 0
+ SymbolName: '$LN3'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 4
+ SymbolName: '$LN3'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 8
+ SymbolName: '$unwind$f'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+symbols:
+ - Name: '@comp.id'
+ Value: 17130404
+ 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: 57
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 1455566745
+ 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: 1364
+ NumberOfRelocations: 10
+ 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: 6560
+ 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: 45
+ NumberOfRelocations: 1
+ NumberOfLinenumbers: 0
+ CheckSum: 1509507051
+ Number: 0
+ - Name: '??2@YAPEAX_K@Z'
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: f
+ Value: 16
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: '$LN3'
+ 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: 931692337
+ 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: 3887998202
+ 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/pdb-import-gc.yaml b/test/COFF/pdb-import-gc.yaml
index 80484cb75f4f..8f50a111411c 100644
--- a/test/COFF/pdb-import-gc.yaml
+++ b/test/COFF/pdb-import-gc.yaml
@@ -1,7 +1,7 @@
# 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 -symbols %t.pdb | FileCheck %s
+# RUN: -nodefaultlib -opt:ref -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.
@@ -11,12 +11,16 @@
# int discarded() { return __wc_mb_cur; }
# int main() { return g2; }
-# CHECK: Symbols
-# CHECK: ============================================================
-# CHECK: Mod 0000 | `{{.*}}pdb-import-gc.yaml.tmp.obj`:
-# CHECK: 4 | S_GDATA32 [size = 32] `__imp___wc_mb_cur`
-# CHECK-NEXT: type = 0x0070 (char), addr = 0000:0000
-# CHECK: Mod 0001 | `* Linker *`:
+# 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:
diff --git a/test/COFF/pdb-invalid-func-type.yaml b/test/COFF/pdb-invalid-func-type.yaml
index 686079e7d8e4..219dd424c718 100644
--- a/test/COFF/pdb-invalid-func-type.yaml
+++ b/test/COFF/pdb-invalid-func-type.yaml
@@ -7,7 +7,7 @@
# RUN: llvm-pdbutil dump -symbols %t.pdb | FileCheck %s
# CHECK: Mod 0000 | `{{.*}}pdb-invalid-func-type.yaml.tmp.obj`:
-# CHECK: 4 | S_GPROC32_ID [size = 44] `main`
+# 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]
diff --git a/test/COFF/pdb-lib.s b/test/COFF/pdb-lib.s
index 74d987eac814..319d4bc0fb80 100644
--- a/test/COFF/pdb-lib.s
+++ b/test/COFF/pdb-lib.s
@@ -11,15 +11,15 @@
# 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: pdb file ni: 0 ``, src file ni: 0 ``
-# CHECK-NEXT: Mod 0001 | Name: `bar.obj`:
+# 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: pdb file ni: 0 ``, src file ni: 0 ``
-# CHECK-NEXT: Mod 0002 | Name: `* Linker *`:
+# 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 ``
diff --git a/test/COFF/pdb-linker-module.test b/test/COFF/pdb-linker-module.test
index ce366b6d6482..1bb57298f96e 100644
--- a/test/COFF/pdb-linker-module.test
+++ b/test/COFF/pdb-linker-module.test
@@ -1,18 +1,24 @@
RUN: lld-link /debug /pdb:%t.pdb /nodefaultlib /entry:main %S/Inputs/pdb-diff.obj
-RUN: llvm-pdbutil dump -modules -symbols %t.pdb | FileCheck %s
+RUN: llvm-pdbutil dump -modules %t.pdb | FileCheck --check-prefix=MODS %s
+RUN: llvm-pdbutil dump -symbols %t.pdb | FileCheck --check-prefix=SYMS %s
-CHECK: Mod 0001 | `* Linker *`:
-CHECK-NEXT: 4 | S_OBJNAME [size = 20] sig=0, `* Linker *`
-CHECK-NEXT: 24 | S_COMPILE3 [size = 40]
-CHECK-NEXT: machine = intel 80386, Ver = LLVM Linker, language = link
-CHECK-NEXT: frontend = 0.0.0.0, backend = 0.0.0.0
-CHECK-NEXT: flags = none
-CHECK-NEXT: 64 | S_ENVBLOCK
-CHECK-NEXT: - cwd
-CHECK-NEXT: -
-CHECK-NEXT: - exe
-CHECK-NEXT: - {{.*}}lld-link
-CHECK-NEXT: - pdb
-CHECK-NEXT: - {{.*}}pdb-linker-module{{.*}}pdb
-CHECK-NEXT: - cmd
-CHECK-NEXT: - /debug /pdb:{{.*}}pdb-linker-module{{.*}}pdb /nodefaultlib /entry:main {{.*}}pdb-diff.obj
+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 c1becbad7a3b..4f5ba0ebb4df 100644
--- a/test/COFF/pdb-none.test
+++ b/test/COFF/pdb-none.test
@@ -1,12 +1,13 @@
# 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: Age: 1
# CHECK-NEXT: Guid:
# CHECK-NEXT: Signature:
# CHECK-NEXT: Features: [ VC140 ]
diff --git a/test/COFF/pdb-options.test b/test/COFF/pdb-options.test
index 1468c4f1cc65..2bd1d920ee48 100644
--- a/test/COFF/pdb-options.test
+++ b/test/COFF/pdb-options.test
@@ -18,4 +18,4 @@
# RUN: lld-link /DEBUG /entry:main /nodefaultlib %t1.obj %t2.obj
# RUN: ls %t1.pdb
# RUN: rm %t*
-# RUN: cd %T/..
+# RUN: cd %T/..
diff --git a/test/COFF/pdb-procid-remapping.test b/test/COFF/pdb-procid-remapping.test
new file mode 100644
index 000000000000..cb612400a650
--- /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 000000000000..1c75e905ed46
--- /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
index 24215bd11dbe..27948e38d3d3 100644
--- a/test/COFF/pdb-safeseh.yaml
+++ b/test/COFF/pdb-safeseh.yaml
@@ -1,16 +1,17 @@
# RUN: yaml2obj %s -o %t.obj
# RUN: lld-link -debug -entry:main -out:%t.exe -pdb:%t.pdb %t.obj
-# RUN: llvm-pdbutil dump -symbols %t.pdb | FileCheck %s
+# 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: Mod 0000 |
-# CHECK-NEXT: 4 | S_GDATA32 [size = 40] `___safe_se_handler_table`
-# CHECK-NEXT: type = 0x0022 (unsigned long), addr = 0003:0000
-# CHECK-NEXT: Mod 0001 | `* Linker *`:
+# 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:
diff --git a/test/COFF/pdb-same-name.test b/test/COFF/pdb-same-name.test
new file mode 100644
index 000000000000..76db69fabbd6
--- /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
index 7beb59766cc5..2649167e900f 100644
--- a/test/COFF/pdb-scopes.test
+++ b/test/COFF/pdb-scopes.test
@@ -34,12 +34,12 @@ RUN: lld-link %t-a.obj %t-b.obj -debug -entry:main -nodefaultlib -out:%t.exe -pd
RUN: llvm-pdbutil dump -symbols %t.pdb | FileCheck %s
CHECK-LABEL: Mod 0000 | `{{.*}}pdb-scopes.test.tmp-a.obj`:
-CHECK: 104 | S_GPROC32_ID [size = 44] `g`
+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_ID [size = 44] `main`
+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`
@@ -56,7 +56,7 @@ 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_ID [size = 44] `f`
+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`
diff --git a/test/COFF/pdb-secrel-absolute.yaml b/test/COFF/pdb-secrel-absolute.yaml
index c514e54e99f1..330106b3bbed 100644
--- a/test/COFF/pdb-secrel-absolute.yaml
+++ b/test/COFF/pdb-secrel-absolute.yaml
@@ -1,16 +1,17 @@
# RUN: yaml2obj %s -o %t.obj
# RUN: lld-link -debug -entry:main -out:%t.exe -pdb:%t.pdb %t.obj
-# RUN: llvm-pdbutil dump -symbols %t.pdb | FileCheck %s
+# 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: Mod 0000 |
-# CHECK-NEXT: 4 | S_GDATA32 [size = 36] `__guard_fids_table`
-# CHECK-NEXT: type = 0x0022 (unsigned long), addr = 0003:0000
-# CHECK-NEXT: Mod 0001 | `* Linker *`:
+# 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:
diff --git a/test/COFF/pdb-source-lines.test b/test/COFF/pdb-source-lines.test
index f9e0e5c7487f..389d91f20efb 100644
--- a/test/COFF/pdb-source-lines.test
+++ b/test/COFF/pdb-source-lines.test
@@ -19,10 +19,11 @@ $ 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-LABEL: DbiStream:
CHECK-NEXT: VerHeader: V70
CHECK-NEXT: Age: 1
CHECK-NEXT: BuildNumber: 0
@@ -30,22 +31,22 @@ CHECK-NEXT: PdbDllVersion: 0
CHECK-NEXT: PdbDllRbld: 0
CHECK-NEXT: Flags: 0
CHECK-NEXT: MachineType: x86
-CHECK-NEXT: Modules:
+CHECK-NEXT: Modules:
CHECK-LABEL: - Module: {{.*}}pdb_lines_1.obj
CHECK-NEXT: ObjFile: {{.*}}pdb_lines_1.obj
-CHECK-NEXT: SourceFiles:
+CHECK-NEXT: SourceFiles:
CHECK-NEXT: - '{{.*}}pdb_lines_1.c'
CHECK-NEXT: - '{{.*}}foo.h'
-CHECK-NEXT: Subsections:
+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: Blocks:
CHECK-NEXT: - FileName: '{{.*}}pdb_lines_1.c'
-CHECK-NEXT: Lines:
+CHECK-NEXT: Lines:
CHECK-NEXT: - Offset: 0
CHECK-NEXT: LineStart: 2
CHECK-NEXT: IsStatement: true
@@ -62,9 +63,9 @@ CHECK-NEXT: - Offset: 14
CHECK-NEXT: LineStart: 5
CHECK-NEXT: IsStatement: true
CHECK-NEXT: EndDelta: 0
-CHECK-NEXT: Columns:
+CHECK-NEXT: Columns:
CHECK-NEXT: - !FileChecksums
-CHECK-NEXT: Checksums:
+CHECK-NEXT: Checksums:
CHECK-NEXT: - FileName: '{{.*}}pdb_lines_1.c'
CHECK-NEXT: Kind: MD5
CHECK-NEXT: Checksum: 4EB19DCD86C3BA2238A255C718572E7B
@@ -76,9 +77,9 @@ CHECK-NEXT: CodeSize: 14
CHECK-NEXT: Flags: [ ]
CHECK-NEXT: RelocOffset: 32
CHECK-NEXT: RelocSegment: 2
-CHECK-NEXT: Blocks:
+CHECK-NEXT: Blocks:
CHECK-NEXT: - FileName: '{{.*}}foo.h'
-CHECK-NEXT: Lines:
+CHECK-NEXT: Lines:
CHECK-NEXT: - Offset: 0
CHECK-NEXT: LineStart: 2
CHECK-NEXT: IsStatement: true
@@ -91,21 +92,21 @@ CHECK-NEXT: - Offset: 9
CHECK-NEXT: LineStart: 4
CHECK-NEXT: IsStatement: true
CHECK-NEXT: EndDelta: 0
-CHECK-NEXT: Columns:
+CHECK-NEXT: Columns:
CHECK-LABEL: - Module: {{.*}}pdb_lines_2.obj
CHECK-NEXT: ObjFile: {{.*}}pdb_lines_2.obj
-CHECK-NEXT: SourceFiles:
+CHECK-NEXT: SourceFiles:
CHECK-NEXT: - '{{.*}}pdb_lines_2.c'
-CHECK-NEXT: Subsections:
+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: Blocks:
CHECK-NEXT: - FileName: '{{.*}}pdb_lines_2.c'
-CHECK-NEXT: Lines:
+CHECK-NEXT: Lines:
CHECK-NEXT: - Offset: 0
CHECK-NEXT: LineStart: 1
CHECK-NEXT: IsStatement: true
@@ -114,9 +115,9 @@ CHECK-NEXT: - Offset: 0
CHECK-NEXT: LineStart: 2
CHECK-NEXT: IsStatement: true
CHECK-NEXT: EndDelta: 0
-CHECK-NEXT: Columns:
+CHECK-NEXT: Columns:
CHECK-NEXT: - !FileChecksums
-CHECK-NEXT: Checksums:
+CHECK-NEXT: Checksums:
CHECK-NEXT: - FileName: '{{.*}}pdb_lines_2.c'
CHECK-NEXT: Kind: MD5
CHECK-NEXT: Checksum: DF91CB3A2B8D917486574BB50CAC4CC7
diff --git a/test/COFF/pdb-symbol-types.yaml b/test/COFF/pdb-symbol-types.yaml
index 2ad6f5b07bf4..9dad72d3cc2f 100644
--- a/test/COFF/pdb-symbol-types.yaml
+++ b/test/COFF/pdb-symbol-types.yaml
@@ -1,6 +1,6 @@
# 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 %t.pdb | FileCheck %s
+# RUN: llvm-pdbutil dump -symbols -globals %t.pdb | FileCheck %s
# To regenerate the object file:
# $ cat symbol-types.c
@@ -13,6 +13,14 @@
# 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`:
@@ -21,7 +29,7 @@
# 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_ID [size = 44] `main`
+# 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]
@@ -29,13 +37,7 @@
# 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_GDATA32 [size = 28] `global_foo`
-# CHECK: type = 0x1004 (Foo), addr = 0001:0000
-# CHECK: 224 | S_UDT [size = 16] `UDT_Foo`
-# CHECK: original type = 0x1004
-# CHECK: 240 | S_UDT [size = 12] `Foo`
-# CHECK: original type = 0x1004
-# CHECK: 252 | S_BUILDINFO [size = 8] BuildId = `0x100A`
+# CHECK: 196 | S_BUILDINFO [size = 8] BuildId = `0x100A`
# CHECK-LABEL: Mod 0001 | `* Linker *`:
--- !COFF
diff --git a/test/COFF/pdb-thunk.yaml b/test/COFF/pdb-thunk.yaml
new file mode 100644
index 000000000000..6435a17e8f62
--- /dev/null
+++ b/test/COFF/pdb-thunk.yaml
@@ -0,0 +1,2747 @@
+# RUN: yaml2obj %s -o %t.obj
+# RUN: lld-link %t.obj -dll -debug -noentry -nodefaultlib -debug -out:%t.exe -pdb:%t.pdb
+# RUN: llvm-pdbutil dump -symbols %t.pdb | FileCheck %s
+
+# We used to skip vtable thunk symbol records, leading to symbol scope stack
+# imbalances (PR35014). Test that they survive the object file into the PDB.
+
+# This object generated from this C++ source:
+# // t.cpp
+# struct A {
+# virtual int f();
+# };
+# struct B {
+# virtual int f();
+# };
+# struct C : A, B {
+# __declspec(dllexport) C();
+# int f() override;
+# int c;
+# };
+# int A::f() { return 0; }
+# int B::f() { return 0; }
+# int C::f() { return c; }
+# C::C() : c(42) {}
+
+# Compile as:
+# $ cl -GR- -Z7 -c t.cpp
+
+# CHECK: S_THUNK32
+# CHECK: S_END
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ ]
+sections:
+ - Name: .drectve
+ Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ]
+ Alignment: 1
+ SectionData: 2020202F44454641554C544C49423A224C4942434D5422202F44454641554C544C49423A224F4C444E414D455322202F4558504F52543A3F3F304340405145414140585A20
+ - 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\t.obj'
+ - Kind: S_COMPILE3
+ Compile3Sym:
+ Flags: [ SecurityChecks, HotPatch ]
+ Machine: X64
+ FrontendMajor: 19
+ FrontendMinor: 11
+ FrontendBuild: 25508
+ FrontendQFE: 2
+ BackendMajor: 19
+ BackendMinor: 11
+ BackendBuild: 25508
+ BackendQFE: 2
+ Version: 'Microsoft (R) Optimizing Compiler'
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ CodeSize: 8
+ DbgStart: 5
+ DbgEnd: 7
+ FunctionType: 4172
+ Flags: [ ]
+ DisplayName: 'A::f'
+ - 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: this
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 8
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'c:\src\llvm-project\build\t.cpp'
+ Lines:
+ - Offset: 0
+ LineStart: 12
+ IsStatement: true
+ EndDelta: 0
+ Columns:
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ CodeSize: 8
+ DbgStart: 5
+ DbgEnd: 7
+ FunctionType: 4173
+ Flags: [ ]
+ DisplayName: 'B::f'
+ - 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: 4121
+ Register: RSP
+ VarName: this
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 8
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'c:\src\llvm-project\build\t.cpp'
+ Lines:
+ - Offset: 0
+ LineStart: 13
+ IsStatement: true
+ EndDelta: 0
+ Columns:
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ CodeSize: 89
+ DbgStart: 9
+ DbgEnd: 84
+ FunctionType: 4175
+ Flags: [ ]
+ DisplayName: 'C::C'
+ - Kind: S_FRAMEPROC
+ FrameProcSym:
+ TotalFrameBytes: 40
+ PaddingFrameBytes: 0
+ OffsetToPadding: 0
+ BytesOfCalleeSavedRegisters: 0
+ OffsetOfExceptionHandler: 0
+ SectionIdOfExceptionHandler: 0
+ Flags: [ AsynchronousExceptionHandling, OptimizedForSpeed ]
+ - Kind: S_REGREL32
+ RegRelativeSym:
+ Offset: 48
+ Type: 4143
+ Register: RSP
+ VarName: this
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 89
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'c:\src\llvm-project\build\t.cpp'
+ Lines:
+ - Offset: 0
+ LineStart: 15
+ IsStatement: true
+ EndDelta: 0
+ - Offset: 67
+ LineStart: 15
+ IsStatement: true
+ EndDelta: 0
+ Columns:
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ CodeSize: 14
+ DbgStart: 5
+ DbgEnd: 13
+ FunctionType: 4174
+ Flags: [ ]
+ DisplayName: 'C::f'
+ - 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: 4143
+ Register: RSP
+ VarName: this
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 14
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'c:\src\llvm-project\build\t.cpp'
+ Lines:
+ - Offset: 0
+ LineStart: 14
+ IsStatement: true
+ EndDelta: 0
+ Columns:
+ - !Symbols
+ Records:
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4117
+ UDTName: A
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4268
+ UDTName: '__vc_attributes::event_sourceAttribute'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4260
+ UDTName: '__vc_attributes::event_sourceAttribute::optimize_e'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4257
+ UDTName: '__vc_attributes::event_sourceAttribute::type_e'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4253
+ UDTName: '__vc_attributes::helper_attributes::v1_alttypeAttribute'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4247
+ UDTName: '__vc_attributes::helper_attributes::v1_alttypeAttribute::type_e'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4243
+ UDTName: '__vc_attributes::helper_attributes::usageAttribute'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4237
+ UDTName: '__vc_attributes::helper_attributes::usageAttribute::usage_e'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4233
+ UDTName: '__vc_attributes::threadingAttribute'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4225
+ UDTName: '__vc_attributes::threadingAttribute::threading_e'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4221
+ UDTName: '__vc_attributes::aggregatableAttribute'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4213
+ UDTName: '__vc_attributes::aggregatableAttribute::type_e'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4209
+ UDTName: '__vc_attributes::event_receiverAttribute'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4199
+ UDTName: '__vc_attributes::event_receiverAttribute::type_e'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4195
+ UDTName: '__vc_attributes::moduleAttribute'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4182
+ UDTName: '__vc_attributes::moduleAttribute::type_e'
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4160
+ UDTName: C
+ - Kind: S_UDT
+ UDTSym:
+ Type: 4140
+ UDTName: B
+ - !FileChecksums
+ Checksums:
+ - FileName: 'c:\src\llvm-project\build\t.cpp'
+ Kind: MD5
+ Checksum: 1E487A6B7D4DB0A502F8E5945CCB70D1
+ - !StringTable
+ Strings:
+ - 'c:\src\llvm-project\build\t.cpp'
+ - !Symbols
+ Records:
+ - Kind: S_BUILDINFO
+ BuildInfoSym:
+ BuildId: 4281
+ Relocations:
+ - VirtualAddress: 152
+ SymbolName: '?f@A@@UEAAHXZ'
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 156
+ SymbolName: '?f@A@@UEAAHXZ'
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 228
+ SymbolName: '?f@A@@UEAAHXZ'
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 232
+ SymbolName: '?f@A@@UEAAHXZ'
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 300
+ SymbolName: '?f@B@@UEAAHXZ'
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 304
+ SymbolName: '?f@B@@UEAAHXZ'
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 376
+ SymbolName: '?f@B@@UEAAHXZ'
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 380
+ SymbolName: '?f@B@@UEAAHXZ'
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 448
+ SymbolName: '??0C@@QEAA@XZ'
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 452
+ SymbolName: '??0C@@QEAA@XZ'
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 524
+ SymbolName: '??0C@@QEAA@XZ'
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 528
+ SymbolName: '??0C@@QEAA@XZ'
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 604
+ SymbolName: '?f@C@@UEAAHXZ'
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 608
+ SymbolName: '?f@C@@UEAAHXZ'
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 680
+ SymbolName: '?f@C@@UEAAHXZ'
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 684
+ SymbolName: '?f@C@@UEAAHXZ'
+ 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_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: A
+ UniqueName: '.?AUA@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4096
+ Attrs: 66572
+ - Kind: LF_VTSHAPE
+ VFTableShape:
+ Slots:
+ - Near16
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4098
+ Attrs: 65548
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ ]
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 116
+ ClassType: 4096
+ ThisType: 4097
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 0
+ ArgumentList: 4100
+ ThisPointerAdjustment: 0
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4096
+ Attrs: 65676
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4102 ]
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4096
+ ThisType: 4097
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 1
+ ArgumentList: 4103
+ ThisPointerAdjustment: 0
+ - Kind: LF_MODIFIER
+ Modifier:
+ ModifiedType: 4096
+ Modifiers: [ None, Const ]
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4105
+ Attrs: 65580
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4106 ]
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4096
+ ThisType: 4097
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 1
+ ArgumentList: 4107
+ ThisPointerAdjustment: 0
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4096
+ ThisType: 4097
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 0
+ ArgumentList: 4100
+ ThisPointerAdjustment: 0
+ - Kind: LF_METHODLIST
+ MethodOverloadList:
+ Methods:
+ - Type: 4104
+ Attrs: 259
+ VFTableOffset: -1
+ Name: ''
+ - Type: 4108
+ Attrs: 259
+ VFTableOffset: -1
+ Name: ''
+ - Type: 4109
+ Attrs: 259
+ VFTableOffset: -1
+ Name: ''
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4096
+ Attrs: 65580
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 4111
+ ClassType: 4096
+ ThisType: 4097
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 1
+ ArgumentList: 4103
+ ThisPointerAdjustment: 0
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 4111
+ ClassType: 4096
+ ThisType: 4097
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 1
+ ArgumentList: 4107
+ ThisPointerAdjustment: 0
+ - Kind: LF_METHODLIST
+ MethodOverloadList:
+ Methods:
+ - Type: 4112
+ Attrs: 259
+ VFTableOffset: -1
+ Name: ''
+ - Type: 4113
+ Attrs: 259
+ VFTableOffset: -1
+ Name: ''
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_VFUNCTAB
+ VFPtr:
+ Type: 4099
+ - Kind: LF_ONEMETHOD
+ OneMethod:
+ Type: 4101
+ Attrs: 19
+ VFTableOffset: 0
+ Name: f
+ - Kind: LF_METHOD
+ OverloadedMethod:
+ NumOverloads: 3
+ MethodList: 4110
+ Name: A
+ - Kind: LF_METHOD
+ OverloadedMethod:
+ NumOverloads: 2
+ MethodList: 4114
+ Name: 'operator='
+ - Kind: LF_VTSHAPE
+ VFTableShape:
+ Slots:
+ - Near16
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 7
+ Options: [ None, HasConstructorOrDestructor, HasOverloadedOperator, HasOverloadedAssignmentOperator, HasUniqueName ]
+ FieldList: 4115
+ Name: A
+ UniqueName: '.?AUA@@'
+ DerivationList: 0
+ VTableShape: 4116
+ Size: 8
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'c:\src\llvm-project\build\t.cpp'
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4117
+ SourceFile: 4118
+ LineNumber: 1
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: B
+ UniqueName: '.?AUB@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4120
+ Attrs: 66572
+ - Kind: LF_VTSHAPE
+ VFTableShape:
+ Slots:
+ - Near16
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4122
+ Attrs: 65548
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 116
+ ClassType: 4120
+ ThisType: 4121
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 0
+ ArgumentList: 4100
+ ThisPointerAdjustment: 0
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4120
+ Attrs: 65676
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4125 ]
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4120
+ ThisType: 4121
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 1
+ ArgumentList: 4126
+ ThisPointerAdjustment: 0
+ - Kind: LF_MODIFIER
+ Modifier:
+ ModifiedType: 4120
+ Modifiers: [ None, Const ]
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4128
+ Attrs: 65580
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4129 ]
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4120
+ ThisType: 4121
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 1
+ ArgumentList: 4130
+ ThisPointerAdjustment: 0
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4120
+ ThisType: 4121
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 0
+ ArgumentList: 4100
+ ThisPointerAdjustment: 0
+ - Kind: LF_METHODLIST
+ MethodOverloadList:
+ Methods:
+ - Type: 4127
+ Attrs: 259
+ VFTableOffset: -1
+ Name: ''
+ - Type: 4131
+ Attrs: 259
+ VFTableOffset: -1
+ Name: ''
+ - Type: 4132
+ Attrs: 259
+ VFTableOffset: -1
+ Name: ''
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4120
+ Attrs: 65580
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 4134
+ ClassType: 4120
+ ThisType: 4121
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 1
+ ArgumentList: 4126
+ ThisPointerAdjustment: 0
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 4134
+ ClassType: 4120
+ ThisType: 4121
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 1
+ ArgumentList: 4130
+ ThisPointerAdjustment: 0
+ - Kind: LF_METHODLIST
+ MethodOverloadList:
+ Methods:
+ - Type: 4135
+ Attrs: 259
+ VFTableOffset: -1
+ Name: ''
+ - Type: 4136
+ Attrs: 259
+ VFTableOffset: -1
+ Name: ''
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_VFUNCTAB
+ VFPtr:
+ Type: 4123
+ - Kind: LF_ONEMETHOD
+ OneMethod:
+ Type: 4124
+ Attrs: 19
+ VFTableOffset: 0
+ Name: f
+ - Kind: LF_METHOD
+ OverloadedMethod:
+ NumOverloads: 3
+ MethodList: 4133
+ Name: B
+ - Kind: LF_METHOD
+ OverloadedMethod:
+ NumOverloads: 2
+ MethodList: 4137
+ Name: 'operator='
+ - Kind: LF_VTSHAPE
+ VFTableShape:
+ Slots:
+ - Near16
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 7
+ Options: [ None, HasConstructorOrDestructor, HasOverloadedOperator, HasOverloadedAssignmentOperator, HasUniqueName ]
+ FieldList: 4138
+ Name: B
+ UniqueName: '.?AUB@@'
+ DerivationList: 0
+ VTableShape: 4139
+ Size: 8
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4140
+ SourceFile: 4118
+ LineNumber: 4
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: C
+ UniqueName: '.?AUC@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4142
+ Attrs: 66572
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4142
+ Attrs: 65676
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4144 ]
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4142
+ ThisType: 4143
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 1
+ ArgumentList: 4145
+ ThisPointerAdjustment: 0
+ - Kind: LF_MODIFIER
+ Modifier:
+ ModifiedType: 4142
+ Modifiers: [ None, Const ]
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4147
+ Attrs: 65580
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4148 ]
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4142
+ ThisType: 4143
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 1
+ ArgumentList: 4149
+ ThisPointerAdjustment: 0
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4142
+ ThisType: 4143
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 0
+ ArgumentList: 4100
+ ThisPointerAdjustment: 0
+ - Kind: LF_METHODLIST
+ MethodOverloadList:
+ Methods:
+ - Type: 4146
+ Attrs: 259
+ VFTableOffset: -1
+ Name: ''
+ - Type: 4150
+ Attrs: 259
+ VFTableOffset: -1
+ Name: ''
+ - Type: 4151
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 116
+ ClassType: 4142
+ ThisType: 4143
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 0
+ ArgumentList: 4100
+ ThisPointerAdjustment: 0
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4142
+ Attrs: 65580
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 4154
+ ClassType: 4142
+ ThisType: 4143
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 1
+ ArgumentList: 4145
+ ThisPointerAdjustment: 0
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 4154
+ ClassType: 4142
+ ThisType: 4143
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 1
+ ArgumentList: 4149
+ ThisPointerAdjustment: 0
+ - Kind: LF_METHODLIST
+ MethodOverloadList:
+ Methods:
+ - Type: 4155
+ Attrs: 259
+ VFTableOffset: -1
+ Name: ''
+ - Type: 4156
+ Attrs: 259
+ VFTableOffset: -1
+ Name: ''
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_BCLASS
+ BaseClass:
+ Attrs: 3
+ Type: 4096
+ Offset: 0
+ - Kind: LF_BCLASS
+ BaseClass:
+ Attrs: 3
+ Type: 4120
+ Offset: 8
+ - Kind: LF_METHOD
+ OverloadedMethod:
+ NumOverloads: 3
+ MethodList: 4152
+ Name: C
+ - Kind: LF_ONEMETHOD
+ OneMethod:
+ Type: 4153
+ Attrs: 7
+ VFTableOffset: -1
+ Name: f
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 116
+ FieldOffset: 16
+ Name: c
+ - Kind: LF_METHOD
+ OverloadedMethod:
+ NumOverloads: 2
+ MethodList: 4157
+ Name: 'operator='
+ - Kind: LF_VTSHAPE
+ VFTableShape:
+ Slots:
+ - Near16
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 9
+ Options: [ None, HasConstructorOrDestructor, HasOverloadedOperator, HasOverloadedAssignmentOperator, HasUniqueName ]
+ FieldList: 4158
+ Name: C
+ UniqueName: '.?AUC@@'
+ DerivationList: 0
+ VTableShape: 4159
+ Size: 24
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4160
+ SourceFile: 4118
+ LineNumber: 7
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4142
+ Attrs: 65548
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4142
+ ThisType: 4143
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 0
+ ArgumentList: 4100
+ ThisPointerAdjustment: 0
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4096
+ Attrs: 65548
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4096
+ ThisType: 4097
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 0
+ ArgumentList: 4100
+ ThisPointerAdjustment: 0
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4120
+ Attrs: 65548
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4120
+ ThisType: 4121
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 0
+ ArgumentList: 4100
+ ThisPointerAdjustment: 0
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 3
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 0
+ ArgumentList: 4100
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4168
+ Attrs: 66572
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4169
+ Attrs: 65548
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4170
+ Attrs: 65548
+ - Kind: LF_MFUNC_ID
+ MemberFuncId:
+ ClassType: 4096
+ FunctionType: 4101
+ Name: f
+ - Kind: LF_MFUNC_ID
+ MemberFuncId:
+ ClassType: 4120
+ FunctionType: 4124
+ Name: f
+ - Kind: LF_MFUNC_ID
+ MemberFuncId:
+ ClassType: 4142
+ FunctionType: 4153
+ Name: f
+ - Kind: LF_MFUNC_ID
+ MemberFuncId:
+ ClassType: 4142
+ FunctionType: 4151
+ Name: '{ctor}'
+ - Kind: LF_MFUNC_ID
+ MemberFuncId:
+ ClassType: 4096
+ FunctionType: 4109
+ Name: '{ctor}'
+ - Kind: LF_MFUNC_ID
+ MemberFuncId:
+ ClassType: 4120
+ FunctionType: 4132
+ Name: '{ctor}'
+ - Kind: LF_MFUNC_ID
+ MemberFuncId:
+ ClassType: 4142
+ FunctionType: 4153
+ Name: f
+ - Kind: LF_ARRAY
+ Array:
+ ElementType: 4169
+ IndexType: 35
+ Size: 8
+ Name: ''
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: '__vc_attributes::moduleAttribute'
+ UniqueName: '.?AUmoduleAttribute@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 1
+ Name: dll
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 2
+ Name: exe
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 3
+ Name: service
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 4
+ Name: unspecified
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 2
+ Name: EXE
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 3
+ Name: SERVICE
+ - Kind: LF_ENUM
+ Enum:
+ NumEnumerators: 6
+ Options: [ None, Nested, HasUniqueName ]
+ FieldList: 4181
+ Name: '__vc_attributes::moduleAttribute::type_e'
+ UniqueName: '.?AW4type_e@moduleAttribute@__vc_attributes@@'
+ UnderlyingType: 116
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'c:\src\llvm-project\build\predefined c++ attributes (compiler internal)'
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4182
+ SourceFile: 4183
+ LineNumber: 482
+ - Kind: LF_MODIFIER
+ Modifier:
+ ModifiedType: 112
+ Modifiers: [ None, Const ]
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4185
+ Attrs: 65548
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4182, 4186, 4186, 4186, 116, 48, 4186, 116,
+ 4186, 4186, 116, 48, 48, 4186, 4186 ]
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4180
+ Attrs: 66572
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4180
+ ThisType: 4188
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 15
+ ArgumentList: 4187
+ ThisPointerAdjustment: 0
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4182 ]
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4180
+ ThisType: 4188
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 1
+ ArgumentList: 4190
+ ThisPointerAdjustment: 0
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4180
+ ThisType: 4188
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 0
+ ArgumentList: 4100
+ ThisPointerAdjustment: 0
+ - Kind: LF_METHODLIST
+ MethodOverloadList:
+ Methods:
+ - Type: 4189
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Type: 4191
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Type: 4192
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_NESTTYPE
+ NestedType:
+ Type: 4182
+ Name: type_e
+ - Kind: LF_METHOD
+ OverloadedMethod:
+ NumOverloads: 3
+ MethodList: 4193
+ Name: moduleAttribute
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4182
+ FieldOffset: 0
+ Name: type
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4186
+ FieldOffset: 8
+ Name: name
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4186
+ FieldOffset: 16
+ Name: version
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4186
+ FieldOffset: 24
+ Name: uuid
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 116
+ FieldOffset: 32
+ Name: lcid
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 48
+ FieldOffset: 36
+ Name: control
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4186
+ FieldOffset: 40
+ Name: helpstring
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 116
+ FieldOffset: 48
+ Name: helpstringcontext
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4186
+ FieldOffset: 56
+ Name: helpstringdll
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4186
+ FieldOffset: 64
+ Name: helpfile
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 116
+ FieldOffset: 72
+ Name: helpcontext
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 48
+ FieldOffset: 76
+ Name: hidden
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 48
+ FieldOffset: 77
+ Name: restricted
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4186
+ FieldOffset: 80
+ Name: custom
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4186
+ FieldOffset: 88
+ Name: resource_name
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 19
+ Options: [ None, HasConstructorOrDestructor, ContainsNestedClass, HasUniqueName ]
+ FieldList: 4194
+ Name: '__vc_attributes::moduleAttribute'
+ UniqueName: '.?AUmoduleAttribute@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 96
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4195
+ SourceFile: 4183
+ LineNumber: 481
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: '__vc_attributes::event_receiverAttribute'
+ UniqueName: '.?AUevent_receiverAttribute@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 0
+ Name: native
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 1
+ Name: com
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 2
+ Name: managed
+ - Kind: LF_ENUM
+ Enum:
+ NumEnumerators: 3
+ Options: [ None, Nested, HasUniqueName ]
+ FieldList: 4198
+ Name: '__vc_attributes::event_receiverAttribute::type_e'
+ UniqueName: '.?AW4type_e@event_receiverAttribute@__vc_attributes@@'
+ UnderlyingType: 116
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4199
+ SourceFile: 4183
+ LineNumber: 136
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4199, 48 ]
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4197
+ Attrs: 66572
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4197
+ ThisType: 4202
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 2
+ ArgumentList: 4201
+ ThisPointerAdjustment: 0
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4199 ]
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4197
+ ThisType: 4202
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 1
+ ArgumentList: 4204
+ ThisPointerAdjustment: 0
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4197
+ ThisType: 4202
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 0
+ ArgumentList: 4100
+ ThisPointerAdjustment: 0
+ - Kind: LF_METHODLIST
+ MethodOverloadList:
+ Methods:
+ - Type: 4203
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Type: 4205
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Type: 4206
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_NESTTYPE
+ NestedType:
+ Type: 4199
+ Name: type_e
+ - Kind: LF_METHOD
+ OverloadedMethod:
+ NumOverloads: 3
+ MethodList: 4207
+ Name: event_receiverAttribute
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4199
+ FieldOffset: 0
+ Name: type
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 48
+ FieldOffset: 4
+ Name: layout_dependent
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 6
+ Options: [ None, HasConstructorOrDestructor, ContainsNestedClass, HasUniqueName ]
+ FieldList: 4208
+ Name: '__vc_attributes::event_receiverAttribute'
+ UniqueName: '.?AUevent_receiverAttribute@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 8
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4209
+ SourceFile: 4183
+ LineNumber: 135
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: '__vc_attributes::aggregatableAttribute'
+ UniqueName: '.?AUaggregatableAttribute@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 0
+ Name: never
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 1
+ Name: allowed
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 2
+ Name: always
+ - Kind: LF_ENUM
+ Enum:
+ NumEnumerators: 3
+ Options: [ None, Nested, HasUniqueName ]
+ FieldList: 4212
+ Name: '__vc_attributes::aggregatableAttribute::type_e'
+ UniqueName: '.?AW4type_e@aggregatableAttribute@__vc_attributes@@'
+ UnderlyingType: 116
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4213
+ SourceFile: 4183
+ LineNumber: 545
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4213 ]
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4211
+ Attrs: 66572
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4211
+ ThisType: 4216
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 1
+ ArgumentList: 4215
+ ThisPointerAdjustment: 0
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4211
+ ThisType: 4216
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 0
+ ArgumentList: 4100
+ ThisPointerAdjustment: 0
+ - Kind: LF_METHODLIST
+ MethodOverloadList:
+ Methods:
+ - Type: 4217
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Type: 4218
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_NESTTYPE
+ NestedType:
+ Type: 4213
+ Name: type_e
+ - Kind: LF_METHOD
+ OverloadedMethod:
+ NumOverloads: 2
+ MethodList: 4219
+ Name: aggregatableAttribute
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4213
+ FieldOffset: 0
+ Name: type
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 4
+ Options: [ None, HasConstructorOrDestructor, ContainsNestedClass, HasUniqueName ]
+ FieldList: 4220
+ Name: '__vc_attributes::aggregatableAttribute'
+ UniqueName: '.?AUaggregatableAttribute@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 4
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4221
+ SourceFile: 4183
+ LineNumber: 544
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: '__vc_attributes::threadingAttribute'
+ UniqueName: '.?AUthreadingAttribute@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 1
+ Name: apartment
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 2
+ Name: single
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 3
+ Name: free
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 4
+ Name: neutral
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 5
+ Name: both
+ - Kind: LF_ENUM
+ Enum:
+ NumEnumerators: 5
+ Options: [ None, Nested, HasUniqueName ]
+ FieldList: 4224
+ Name: '__vc_attributes::threadingAttribute::threading_e'
+ UniqueName: '.?AW4threading_e@threadingAttribute@__vc_attributes@@'
+ UnderlyingType: 116
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4225
+ SourceFile: 4183
+ LineNumber: 423
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4225 ]
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4223
+ Attrs: 66572
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4223
+ ThisType: 4228
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 1
+ ArgumentList: 4227
+ ThisPointerAdjustment: 0
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4223
+ ThisType: 4228
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 0
+ ArgumentList: 4100
+ ThisPointerAdjustment: 0
+ - Kind: LF_METHODLIST
+ MethodOverloadList:
+ Methods:
+ - Type: 4229
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Type: 4230
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_NESTTYPE
+ NestedType:
+ Type: 4225
+ Name: threading_e
+ - Kind: LF_METHOD
+ OverloadedMethod:
+ NumOverloads: 2
+ MethodList: 4231
+ Name: threadingAttribute
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4225
+ FieldOffset: 0
+ Name: value
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 4
+ Options: [ None, HasConstructorOrDestructor, ContainsNestedClass, HasUniqueName ]
+ FieldList: 4232
+ Name: '__vc_attributes::threadingAttribute'
+ UniqueName: '.?AUthreadingAttribute@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 4
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4233
+ SourceFile: 4183
+ LineNumber: 422
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: '__vc_attributes::helper_attributes::usageAttribute'
+ UniqueName: '.?AUusageAttribute@helper_attributes@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 0
+ Name: eAnyUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 1
+ Name: eCoClassUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 2
+ Name: eCOMInterfaceUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 6
+ Name: eInterfaceUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 8
+ Name: eMemberUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 16
+ Name: eMethodUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 32
+ Name: eInterfaceMethodUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 64
+ Name: eInterfaceMemberUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 128
+ Name: eCoClassMemberUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 256
+ Name: eCoClassMethodUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 768
+ Name: eGlobalMethodUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 1024
+ Name: eGlobalDataUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 2048
+ Name: eClassUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 4096
+ Name: eInterfaceParameterUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 12288
+ Name: eMethodParameterUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 16384
+ Name: eIDLModuleUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 32768
+ Name: eAnonymousUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 65536
+ Name: eTypedefUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 131072
+ Name: eUnionUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 262144
+ Name: eEnumUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 524288
+ Name: eDefineTagUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 1048576
+ Name: eStructUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 2097152
+ Name: eLocalUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 4194304
+ Name: ePropertyUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 8388608
+ Name: eEventUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 16777216
+ Name: eTemplateUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 16777216
+ Name: eModuleUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 33554432
+ Name: eIllegalUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 67108864
+ Name: eAsynchronousUsage
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 4161535
+ Name: eAnyIDLUsage
+ - Kind: LF_ENUM
+ Enum:
+ NumEnumerators: 30
+ Options: [ None, Nested, HasUniqueName ]
+ FieldList: 4236
+ Name: '__vc_attributes::helper_attributes::usageAttribute::usage_e'
+ UniqueName: '.?AW4usage_e@usageAttribute@helper_attributes@__vc_attributes@@'
+ UnderlyingType: 116
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4237
+ SourceFile: 4183
+ LineNumber: 51
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 117 ]
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4235
+ Attrs: 66572
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4235
+ ThisType: 4240
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 1
+ ArgumentList: 4239
+ ThisPointerAdjustment: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_NESTTYPE
+ NestedType:
+ Type: 4237
+ Name: usage_e
+ - Kind: LF_ONEMETHOD
+ OneMethod:
+ Type: 4241
+ Attrs: 3
+ VFTableOffset: -1
+ Name: usageAttribute
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 117
+ FieldOffset: 0
+ Name: value
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 3
+ Options: [ None, HasConstructorOrDestructor, ContainsNestedClass, HasUniqueName ]
+ FieldList: 4242
+ Name: '__vc_attributes::helper_attributes::usageAttribute'
+ UniqueName: '.?AUusageAttribute@helper_attributes@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 4
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4243
+ SourceFile: 4183
+ LineNumber: 49
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: '__vc_attributes::helper_attributes::v1_alttypeAttribute'
+ UniqueName: '.?AUv1_alttypeAttribute@helper_attributes@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 0
+ Name: eBoolean
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 1
+ Name: eInteger
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 2
+ Name: eFloat
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 3
+ Name: eDouble
+ - Kind: LF_ENUM
+ Enum:
+ NumEnumerators: 4
+ Options: [ None, Nested, HasUniqueName ]
+ FieldList: 4246
+ Name: '__vc_attributes::helper_attributes::v1_alttypeAttribute::type_e'
+ UniqueName: '.?AW4type_e@v1_alttypeAttribute@helper_attributes@__vc_attributes@@'
+ UnderlyingType: 116
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4247
+ SourceFile: 4183
+ LineNumber: 38
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4247 ]
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4245
+ Attrs: 66572
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4245
+ ThisType: 4250
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 1
+ ArgumentList: 4249
+ ThisPointerAdjustment: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_NESTTYPE
+ NestedType:
+ Type: 4247
+ Name: type_e
+ - Kind: LF_ONEMETHOD
+ OneMethod:
+ Type: 4251
+ Attrs: 3
+ VFTableOffset: -1
+ Name: v1_alttypeAttribute
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4247
+ FieldOffset: 0
+ Name: type
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 3
+ Options: [ None, HasConstructorOrDestructor, ContainsNestedClass, HasUniqueName ]
+ FieldList: 4252
+ Name: '__vc_attributes::helper_attributes::v1_alttypeAttribute'
+ UniqueName: '.?AUv1_alttypeAttribute@helper_attributes@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 4
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4253
+ SourceFile: 4183
+ LineNumber: 37
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 0
+ Options: [ None, ForwardReference, HasUniqueName ]
+ FieldList: 0
+ Name: '__vc_attributes::event_sourceAttribute'
+ UniqueName: '.?AUevent_sourceAttribute@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 0
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 0
+ Name: native
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 1
+ Name: com
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 2
+ Name: managed
+ - Kind: LF_ENUM
+ Enum:
+ NumEnumerators: 3
+ Options: [ None, Nested, HasUniqueName ]
+ FieldList: 4256
+ Name: '__vc_attributes::event_sourceAttribute::type_e'
+ UniqueName: '.?AW4type_e@event_sourceAttribute@__vc_attributes@@'
+ UnderlyingType: 116
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4257
+ SourceFile: 4183
+ LineNumber: 1142
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 0
+ Name: speed
+ - Kind: LF_ENUMERATE
+ Enumerator:
+ Attrs: 3
+ Value: 1
+ Name: size
+ - Kind: LF_ENUM
+ Enum:
+ NumEnumerators: 2
+ Options: [ None, Nested, HasUniqueName ]
+ FieldList: 4259
+ Name: '__vc_attributes::event_sourceAttribute::optimize_e'
+ UniqueName: '.?AW4optimize_e@event_sourceAttribute@__vc_attributes@@'
+ UnderlyingType: 116
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4260
+ SourceFile: 4183
+ LineNumber: 1145
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 4257 ]
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 4255
+ Attrs: 66572
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4255
+ ThisType: 4263
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 1
+ ArgumentList: 4262
+ ThisPointerAdjustment: 0
+ - Kind: LF_MFUNCTION
+ MemberFunction:
+ ReturnType: 3
+ ClassType: 4255
+ ThisType: 4263
+ CallConv: NearC
+ Options: [ None, Constructor ]
+ ParameterCount: 0
+ ArgumentList: 4100
+ ThisPointerAdjustment: 0
+ - Kind: LF_METHODLIST
+ MethodOverloadList:
+ Methods:
+ - Type: 4264
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Type: 4265
+ Attrs: 3
+ VFTableOffset: -1
+ Name: ''
+ - Kind: LF_FIELDLIST
+ FieldList:
+ - Kind: LF_NESTTYPE
+ NestedType:
+ Type: 4257
+ Name: type_e
+ - Kind: LF_NESTTYPE
+ NestedType:
+ Type: 4260
+ Name: optimize_e
+ - Kind: LF_METHOD
+ OverloadedMethod:
+ NumOverloads: 2
+ MethodList: 4266
+ Name: event_sourceAttribute
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4257
+ FieldOffset: 0
+ Name: type
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 4260
+ FieldOffset: 4
+ Name: optimize
+ - Kind: LF_MEMBER
+ DataMember:
+ Attrs: 3
+ Type: 48
+ FieldOffset: 8
+ Name: decorate
+ - Kind: LF_STRUCTURE
+ Class:
+ MemberCount: 7
+ Options: [ None, HasConstructorOrDestructor, ContainsNestedClass, HasUniqueName ]
+ FieldList: 4267
+ Name: '__vc_attributes::event_sourceAttribute'
+ UniqueName: '.?AUevent_sourceAttribute@__vc_attributes@@'
+ DerivationList: 0
+ VTableShape: 0
+ Size: 12
+ - Kind: LF_UDT_SRC_LINE
+ UdtSourceLine:
+ UDT: 4268
+ SourceFile: 4183
+ LineNumber: 1141
+ - Kind: LF_VFTABLE
+ VFTable:
+ CompleteClass: 4117
+ OverriddenVFTable: 0
+ VFPtrOffset: 0
+ MethodNames:
+ - '??_7A@@6B@'
+ - '?f@A@@UEAAHXZ'
+ - Kind: LF_VFTABLE
+ VFTable:
+ CompleteClass: 4140
+ OverriddenVFTable: 0
+ VFPtrOffset: 0
+ MethodNames:
+ - '??_7B@@6B@'
+ - '?f@B@@UEAAHXZ'
+ - Kind: LF_VFTABLE
+ VFTable:
+ CompleteClass: 4160
+ OverriddenVFTable: 4270
+ VFPtrOffset: 0
+ MethodNames:
+ - '??_7C@@6BA@@@'
+ - '?f@C@@UEAAHXZ'
+ - Kind: LF_VFTABLE
+ VFTable:
+ CompleteClass: 4160
+ OverriddenVFTable: 4271
+ VFPtrOffset: 8
+ MethodNames:
+ - '??_7C@@6BB@@@'
+ - '?f@C@@W7EAAHXZ'
+ - 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\2017\PROFES~1\VC\Tools\MSVC\14.11.25503\bin\HostX64\x64\cl.exe'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: '-GR- -Z7 -c -MT -IC:\PROGRA~2\MICROS~1\2017\PROFES~1\VC\Tools\MSVC\14.11.25503\ATLMFC\include -IC:\PROGRA~2\MICROS~1\2017\PROFES~1\VC\Tools\MSVC\14.11.25503\include -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.16299.0\ucrt -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.162'
+ - Kind: LF_SUBSTR_LIST
+ StringList:
+ StringIndices: [ 4276 ]
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 4277
+ String: '99.0\shared -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.16299.0\um -IC:\PROGRA~2\WI3CF2~1\10\include\10.0.16299.0\winrt -TP -X'
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: t.cpp
+ - Kind: LF_STRING_ID
+ StringId:
+ Id: 0
+ String: 'C:\src\llvm-project\build\vc140.pdb'
+ - Kind: LF_BUILDINFO
+ BuildInfo:
+ ArgIndices: [ 4274, 4275, 4279, 4280, 4278 ]
+ - Name: '.text$mn'
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 48894C240833C0C3CCCCCCCCCCCCCCCC48894C240833C0C3CCCCCCCCCCCCCCCC48894C24084883EC28488B4C2430E800000000488B4424304883C008488BC8E800000000488B442430488D0D00000000488908488B442430488D0D0000000048894808488B442430C740102A000000488B4424304883C428C3CCCCCCCCCCCCCC48894C2408488B4424088B4010C3
+ Relocations:
+ - VirtualAddress: 47
+ SymbolName: '??0A@@QEAA@XZ'
+ Type: IMAGE_REL_AMD64_REL32
+ - VirtualAddress: 64
+ SymbolName: '??0B@@QEAA@XZ'
+ Type: IMAGE_REL_AMD64_REL32
+ - VirtualAddress: 76
+ SymbolName: '??_7C@@6BA@@@'
+ Type: IMAGE_REL_AMD64_REL32
+ - VirtualAddress: 91
+ SymbolName: '??_7C@@6BB@@@'
+ 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: 48894C2408488B442408488D0D00000000488908488B442408C3
+ Relocations:
+ - VirtualAddress: 13
+ SymbolName: '??_7A@@6B@'
+ 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:
+ CodeSize: 26
+ DbgStart: 5
+ DbgEnd: 25
+ FunctionType: 4176
+ Flags: [ ]
+ DisplayName: 'A::A'
+ - Kind: S_FRAMEPROC
+ FrameProcSym:
+ TotalFrameBytes: 0
+ PaddingFrameBytes: 0
+ OffsetToPadding: 0
+ BytesOfCalleeSavedRegisters: 0
+ OffsetOfExceptionHandler: 0
+ SectionIdOfExceptionHandler: 0
+ Flags: [ MarkedInline, AsynchronousExceptionHandling, OptimizedForSpeed ]
+ - Kind: S_REGREL32
+ RegRelativeSym:
+ Offset: 8
+ Type: 4097
+ Register: RSP
+ VarName: this
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ Relocations:
+ - VirtualAddress: 44
+ SymbolName: '??0A@@QEAA@XZ'
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 48
+ SymbolName: '??0A@@QEAA@XZ'
+ 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: 48894C2408488B442408488D0D00000000488908488B442408C3
+ Relocations:
+ - VirtualAddress: 13
+ SymbolName: '??_7B@@6B@'
+ 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:
+ CodeSize: 26
+ DbgStart: 5
+ DbgEnd: 25
+ FunctionType: 4177
+ Flags: [ ]
+ DisplayName: 'B::B'
+ - Kind: S_FRAMEPROC
+ FrameProcSym:
+ TotalFrameBytes: 0
+ PaddingFrameBytes: 0
+ OffsetToPadding: 0
+ BytesOfCalleeSavedRegisters: 0
+ OffsetOfExceptionHandler: 0
+ SectionIdOfExceptionHandler: 0
+ Flags: [ MarkedInline, AsynchronousExceptionHandling, OptimizedForSpeed ]
+ - Kind: S_REGREL32
+ RegRelativeSym:
+ Offset: 8
+ Type: 4121
+ Register: RSP
+ VarName: this
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ Relocations:
+ - VirtualAddress: 44
+ SymbolName: '??0B@@QEAA@XZ'
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 48
+ SymbolName: '??0B@@QEAA@XZ'
+ 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: 4
+ SectionData: 4883E908E900000000
+ Relocations:
+ - VirtualAddress: 5
+ SymbolName: '?f@C@@UEAAHXZ'
+ 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_THUNK32
+ Thunk32Sym:
+ Parent: 0
+ End: 0
+ Next: 0
+ Off: 0
+ Seg: 0
+ Len: 9
+ Ordinal: Pcode
+ - Kind: S_FRAMEPROC
+ FrameProcSym:
+ TotalFrameBytes: 0
+ PaddingFrameBytes: 0
+ OffsetToPadding: 0
+ BytesOfCalleeSavedRegisters: 0
+ OffsetOfExceptionHandler: 0
+ SectionIdOfExceptionHandler: 0
+ Flags: [ MarkedInline, Naked, AsynchronousExceptionHandling ]
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ Relocations:
+ - VirtualAddress: 28
+ SymbolName: '?f@C@@W7EAAHXZ'
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 32
+ SymbolName: '?f@C@@W7EAAHXZ'
+ Type: IMAGE_REL_AMD64_SECTION
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: '0109010009420000'
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: '000000005900000000000000'
+ Relocations:
+ - VirtualAddress: 0
+ SymbolName: '$LN3'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 4
+ SymbolName: '$LN3'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 8
+ SymbolName: '$unwind$??0C@@QEAA@XZ'
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - Name: .rdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_READ ]
+ Alignment: 8
+ SectionData: '0000000000000000'
+ Relocations:
+ - VirtualAddress: 0
+ SymbolName: '?f@A@@UEAAHXZ'
+ Type: IMAGE_REL_AMD64_ADDR64
+ - Name: .rdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_READ ]
+ Alignment: 8
+ SectionData: '0000000000000000'
+ Relocations:
+ - VirtualAddress: 0
+ SymbolName: '?f@B@@UEAAHXZ'
+ Type: IMAGE_REL_AMD64_ADDR64
+ - Name: .rdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_READ ]
+ Alignment: 8
+ SectionData: '0000000000000000'
+ Relocations:
+ - VirtualAddress: 0
+ SymbolName: '?f@C@@UEAAHXZ'
+ Type: IMAGE_REL_AMD64_ADDR64
+ - Name: .rdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_READ ]
+ Alignment: 8
+ SectionData: '0000000000000000'
+ Relocations:
+ - VirtualAddress: 0
+ SymbolName: '?f@C@@W7EAAHXZ'
+ Type: IMAGE_REL_AMD64_ADDR64
+ - 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_GDATA32
+ DataSym:
+ Type: 4179
+ DisplayName: 'A::`vftable'''
+ Relocations:
+ - VirtualAddress: 20
+ SymbolName: '??_7A@@6B@'
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 24
+ SymbolName: '??_7A@@6B@'
+ Type: IMAGE_REL_AMD64_SECTION
+ - 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_GDATA32
+ DataSym:
+ Type: 4179
+ DisplayName: 'B::`vftable'''
+ Relocations:
+ - VirtualAddress: 20
+ SymbolName: '??_7B@@6B@'
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 24
+ SymbolName: '??_7B@@6B@'
+ Type: IMAGE_REL_AMD64_SECTION
+ - 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_GDATA32
+ DataSym:
+ Type: 4179
+ DisplayName: 'C::`vftable'''
+ Relocations:
+ - VirtualAddress: 20
+ SymbolName: '??_7C@@6BA@@@'
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 24
+ SymbolName: '??_7C@@6BA@@@'
+ Type: IMAGE_REL_AMD64_SECTION
+ - 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_GDATA32
+ DataSym:
+ Type: 4179
+ DisplayName: 'C::`vftable'''
+ Relocations:
+ - VirtualAddress: 20
+ SymbolName: '??_7C@@6BB@@@'
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 24
+ SymbolName: '??_7C@@6BB@@@'
+ Type: IMAGE_REL_AMD64_SECTION
+symbols:
+ - Name: '@comp.id'
+ Value: 17130404
+ 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: 69
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 876125206
+ 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: 1668
+ NumberOfRelocations: 16
+ 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: 8276
+ 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: 142
+ NumberOfRelocations: 4
+ NumberOfLinenumbers: 0
+ CheckSum: 1906691115
+ 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: 26
+ NumberOfRelocations: 1
+ NumberOfLinenumbers: 0
+ CheckSum: 2299407997
+ 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: 112
+ NumberOfRelocations: 2
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 5
+ Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE
+ - Name: '.text$mn'
+ Value: 0
+ SectionNumber: 7
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 26
+ NumberOfRelocations: 1
+ NumberOfLinenumbers: 0
+ CheckSum: 2299407997
+ Number: 0
+ Selection: IMAGE_COMDAT_SELECT_ANY
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 8
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 112
+ NumberOfRelocations: 2
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 7
+ Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE
+ - Name: '.text$mn'
+ Value: 0
+ SectionNumber: 9
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 9
+ NumberOfRelocations: 1
+ NumberOfLinenumbers: 0
+ CheckSum: 3880904481
+ Number: 0
+ Selection: IMAGE_COMDAT_SELECT_ANY
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 10
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 100
+ NumberOfRelocations: 2
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 9
+ Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE
+ - Name: '?f@A@@UEAAHXZ'
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: '??0A@@QEAA@XZ'
+ Value: 0
+ SectionNumber: 5
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: '?f@B@@UEAAHXZ'
+ Value: 16
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: '??0B@@QEAA@XZ'
+ Value: 0
+ SectionNumber: 7
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: '??0C@@QEAA@XZ'
+ Value: 32
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: '?f@C@@UEAAHXZ'
+ Value: 128
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: '?f@C@@W7EAAHXZ'
+ Value: 0
+ SectionNumber: 9
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: '$LN3'
+ Value: 32
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_LABEL
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 11
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 8
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 2625142988
+ Number: 0
+ - Name: '$unwind$??0C@@QEAA@XZ'
+ Value: 0
+ SectionNumber: 11
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 12
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 12
+ NumberOfRelocations: 3
+ NumberOfLinenumbers: 0
+ CheckSum: 1248454893
+ Number: 0
+ - Name: '$pdata$??0C@@QEAA@XZ'
+ Value: 0
+ SectionNumber: 12
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .rdata
+ Value: 0
+ SectionNumber: 13
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 8
+ NumberOfRelocations: 1
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ Selection: IMAGE_COMDAT_SELECT_ANY
+ - Name: '??_7A@@6B@'
+ Value: 0
+ SectionNumber: 13
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: .rdata
+ Value: 0
+ SectionNumber: 14
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 8
+ NumberOfRelocations: 1
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ Selection: IMAGE_COMDAT_SELECT_ANY
+ - Name: '??_7B@@6B@'
+ Value: 0
+ SectionNumber: 14
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: .rdata
+ Value: 0
+ SectionNumber: 15
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 8
+ NumberOfRelocations: 1
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ Selection: IMAGE_COMDAT_SELECT_ANY
+ - Name: '??_7C@@6BA@@@'
+ Value: 0
+ SectionNumber: 15
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: .rdata
+ Value: 0
+ SectionNumber: 16
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 8
+ NumberOfRelocations: 1
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 0
+ Selection: IMAGE_COMDAT_SELECT_ANY
+ - Name: '??_7C@@6BB@@@'
+ Value: 0
+ SectionNumber: 16
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 17
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 40
+ NumberOfRelocations: 2
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 13
+ Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 18
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 40
+ NumberOfRelocations: 2
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 14
+ Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 19
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 40
+ NumberOfRelocations: 2
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 15
+ Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 20
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 40
+ NumberOfRelocations: 2
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 16
+ Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE
+...
+
diff --git a/test/COFF/pdb-type-server-simple.test b/test/COFF/pdb-type-server-simple.test
index c0de4e390914..898c5d4aa130 100644
--- a/test/COFF/pdb-type-server-simple.test
+++ b/test/COFF/pdb-type-server-simple.test
@@ -21,7 +21,7 @@ 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 %t/t.pdb | FileCheck %s
+RUN: llvm-pdbutil dump -symbols -types -ids -globals %t/t.pdb | FileCheck %s
CHECK-LABEL: Types (TPI Stream)
@@ -59,23 +59,29 @@ 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_ID [size = 44] `main`
+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_UDT [size = 12] `Foo`
-CHECK: original type = [[FOO_COMPLETE]]
-CHECK: 212 | S_BUILDINFO [size = 8] BuildId = `[[A_BUILD]]`
+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_ID [size = 44] `g`
+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]
@@ -83,9 +89,7 @@ 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: type = [[FOO_PTR]] (Foo*), register = RSP, offset = 8
CHECK: 196 | S_END [size = 4]
-CHECK: 200 | S_UDT [size = 12] `Foo`
-CHECK: original type = [[FOO_COMPLETE]]
-CHECK: 212 | S_BUILDINFO [size = 8] BuildId = `[[B_BUILD]]`
+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 a4cd4f7c35b3..dad6de25db58 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 dump -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:
@@ -117,86 +119,170 @@
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: 1, has ec info: false
RAW-NEXT: pdb file ni: 0 ``, src file ni: 0 ``
-RAW-NEXT: Mod 0001 | Name: `{{.*}}pdb.test.tmp2.obj`:
+RAW-NEXT: Mod 0001 | `{{.*}}pdb.test.tmp2.obj`:
RAW-NEXT: Obj: `{{.*}}pdb.test.tmp2.obj`:
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 | Name: `* Linker *`:
+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-NEXT: SC[???] | mod = 2, 0004:0000, size = {{[0-9]+}}, data crc = 0, reloc crc = 0
+RAW-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+RAW-NEXT: SC[???] | mod = 2, 0004:0028, size = {{[0-9]+}}, data crc = 0, reloc crc = 0
+RAW-NEXT: IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ
+RAW-NOT: SC[
RAW: Section Map
RAW-NEXT: ============================================================
-RAW-NEXT: Section 0000 | ovl = 0, group = 0, frame = 0, name = 1
+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: 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: 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: 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: 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-arm.test b/test/COFF/reloc-arm.test
index 1f3de619c44b..872e6d53c8aa 100644
--- a/test/COFF/reloc-arm.test
+++ b/test/COFF/reloc-arm.test
@@ -28,28 +28,28 @@ sections:
Relocations:
- VirtualAddress: 0
SymbolName: foo
- Type: 1 # IMAGE_REL_ARM_ADDR32
+ Type: IMAGE_REL_ARM_ADDR32
- VirtualAddress: 16
SymbolName: foo
- Type: 2 # IMAGE_REL_ARM_ADDR32NB
+ Type: IMAGE_REL_ARM_ADDR32NB
- VirtualAddress: 32
SymbolName: foo
- Type: 17 # IMAGE_REL_ARM_MOV32T
+ Type: IMAGE_REL_ARM_MOV32T
- VirtualAddress: 48
SymbolName: foo
- Type: 20 # IMAGE_REL_ARM_BRANCH24T
+ Type: IMAGE_REL_ARM_BRANCH24T
- VirtualAddress: 64
SymbolName: foo
- Type: 18 # IMAGE_REL_ARM_BRANCH20T
+ Type: IMAGE_REL_ARM_BRANCH20T
- VirtualAddress: 80
SymbolName: foo
- Type: 21 # IMAGE_REL_ARM_BLX23T
+ Type: IMAGE_REL_ARM_BLX23T
- VirtualAddress: 96
SymbolName: bar
- Type: 20 # IMAGE_REL_ARM_BRANCH24T
+ Type: IMAGE_REL_ARM_BRANCH24T
- VirtualAddress: 112
SymbolName: bar
- Type: 15 # IMAGE_REL_ARM_SECREL
+ Type: IMAGE_REL_ARM_SECREL
symbols:
- Name: .aaa
Value: 0
diff --git a/test/COFF/reloc-discarded-dwarf.s b/test/COFF/reloc-discarded-dwarf.s
index d779d2f5b8fc..14dc5948b32a 100644
--- a/test/COFF/reloc-discarded-dwarf.s
+++ b/test/COFF/reloc-discarded-dwarf.s
@@ -13,3 +13,5 @@ f:
.section .debug_info,"dr"
.quad f
+ .section .eh_frame,"dr"
+ .quad f
diff --git a/test/COFF/reloc-discarded-early.s b/test/COFF/reloc-discarded-early.s
new file mode 100644
index 000000000000..6d1043dbfa74
--- /dev/null
+++ b/test/COFF/reloc-discarded-early.s
@@ -0,0 +1,8 @@
+# RUN: llvm-mc -triple=x86_64-windows-msvc -filetype=obj -o %t.obj %s
+# RUN: lld-link -entry:__ImageBase -subsystem:console -debug %t.obj
+
+.section .debug_info,"dr"
+.quad .Ldrectve
+
+.section .drectve
+.Ldrectve:
diff --git a/test/COFF/reloc-discarded-early2.s b/test/COFF/reloc-discarded-early2.s
new file mode 100644
index 000000000000..18e200008721
--- /dev/null
+++ b/test/COFF/reloc-discarded-early2.s
@@ -0,0 +1,9 @@
+# RUN: llvm-mc -triple=x86_64-windows-msvc -filetype=obj -o %t.obj %s
+# RUN: not lld-link -entry:__ImageBase -subsystem:console %t.obj 2>&1 | FileCheck %s
+
+.text
+# CHECK: error: relocation against symbol in discarded section: .drectve
+.quad .Ldrectve
+
+.section .drectve
+.Ldrectve:
diff --git a/test/COFF/reloc-discarded.s b/test/COFF/reloc-discarded.s
index 94eaba998330..0be4e110b4db 100644
--- a/test/COFF/reloc-discarded.s
+++ b/test/COFF/reloc-discarded.s
@@ -18,7 +18,6 @@ main_global:
.section .CRT$XCU,"dr",associative,main_global
.p2align 3
- .globl assoc_global
assoc_global:
.quad main_global
diff --git a/test/COFF/responsefile.test b/test/COFF/responsefile.test
index fd4d221c20dc..742962423cc5 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 82b0f220b6c1..176597786848 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-md.s b/test/COFF/safeseh-md.s
new file mode 100644
index 000000000000..ae731b5211df
--- /dev/null
+++ b/test/COFF/safeseh-md.s
@@ -0,0 +1,34 @@
+# RUN: llvm-mc -triple i686-windows-msvc %s -filetype=obj -o %t.obj
+# RUN: lld-link %t.obj %S/Inputs/except_handler3.lib -safeseh -out:%t.exe -opt:noref -entry:main
+# RUN: llvm-readobj -coff-load-config %t.exe | FileCheck %s
+
+# CHECK: SEHTable [
+# CHECK-NEXT: 0x
+# CHECK-NEXT: ]
+
+ .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
+
+.safeseh __except_handler3
+
+ .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/safeseh.s b/test/COFF/safeseh.s
index 83c15afbf938..35f54c590d13 100644
--- a/test/COFF/safeseh.s
+++ b/test/COFF/safeseh.s
@@ -1,9 +1,15 @@
# 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: llvm-readobj -coff-basereloc -coff-load-config -file-headers %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
+# RUN: llvm-readobj -coff-basereloc -coff-load-config -file-headers %t.exe | FileCheck %s --check-prefix=CHECK-GC
+# __safe_se_handler_table needs to be relocated against ImageBase.
+# check that the relocation is present.
+# CHECK-NOGC-NOT: IMAGE_DLL_CHARACTERISTICS_NO_SEH
+# CHECK-NOGC: BaseReloc [
+# CHECK-NOGC: Entry {
+# CHECK-NOGC: Type: HIGHLOW
# CHECK-NOGC: LoadConfig [
# CHECK-NOGC: Size: 0x48
# CHECK-NOGC: SEHandlerTable: 0x401048
@@ -13,6 +19,11 @@
# CHECK-NOGC-NEXT: 0x402006
# CHECK-NOGC-NEXT: ]
+# Without the SEH table, the address is absolute, so check that we do
+# not have a relocation for it.
+# CHECK-GC-NOT: IMAGE_DLL_CHARACTERISTICS_NO_SEH
+# CHECK-GC: BaseReloc [
+# CHECK-GC-NEXT: ]
# CHECK-GC: LoadConfig [
# CHECK-GC: Size: 0x48
# CHECK-GC: SEHandlerTable: 0x0
diff --git a/test/COFF/section-size.s b/test/COFF/section-size.s
new file mode 100644
index 000000000000..28f3f4acbc9d
--- /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/seh-comdat.test b/test/COFF/seh-comdat.test
new file mode 100644
index 000000000000..d9133289ed06
--- /dev/null
+++ b/test/COFF/seh-comdat.test
@@ -0,0 +1,66 @@
+# RUN: yaml2obj < %s > %t1.obj
+# RUN: yaml2obj < %s > %t2.obj
+# RUN: lld-link /out:%t.exe /subsystem:console /entry:main %t1.obj %t2.obj
+# RUN: llvm-objdump -s %t.exe | FileCheck %s
+
+# CHECK: Contents of section .rdata:
+# CHECK: 1000 00200000
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_I386
+ Characteristics: [ ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ, IMAGE_SCN_LNK_COMDAT ]
+ Alignment: 1
+ SectionData: 0000000000000000
+ - Name: .sxdata
+ Characteristics: [ IMAGE_SCN_LNK_INFO ]
+ Alignment: 4
+ SectionData: 02000000
+symbols:
+ - Name: '@comp.id'
+ Value: 14766605
+ SectionNumber: 65535
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: '@feat.00'
+ Value: 2147484049
+ SectionNumber: 65535
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ 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
+ Selection: IMAGE_COMDAT_SELECT_ANY
+ - Name: .sxdata
+ Value: 0
+ SectionNumber: 2
+ 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: 0
+ - Name: _main
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/strtab-size.s b/test/COFF/strtab-size.s
new file mode 100644
index 000000000000..6b29049f548c
--- /dev/null
+++ b/test/COFF/strtab-size.s
@@ -0,0 +1,216 @@
+# REQUIRES: x86
+
+# Test that the strtab size is included in the allocation even if the
+# strtab itself is empty. To achieve this, we need a number of symbols N
+# where alignTo(N*18, 512) < alignTo(N*18 + 4, 512), where the first
+# positive N fulfilling that is 199.
+
+# RUN: llvm-mc -triple=x86_64-windows-msvc %s -filetype=obj -o %t.obj
+# RUN: lld-link -out:%t.exe -entry:main %t.obj -debug:dwarf
+
+# If the size of the strtab isn't allocated for, llvm-readobj would
+# output SymbolCount: 0 (and dumpbin.exe would error out with "invalid file
+# or disk full, cannot seek to 0x1602").
+
+# RUN: llvm-readobj -file-headers %t.exe | FileCheck %s
+# CHECK: SymbolCount: 199
+
+.global main
+.text
+main:
+sym0:
+sym1:
+sym2:
+sym3:
+sym4:
+sym5:
+sym6:
+sym7:
+sym8:
+sym9:
+sym10:
+sym11:
+sym12:
+sym13:
+sym14:
+sym15:
+sym16:
+sym17:
+sym18:
+sym19:
+sym20:
+sym21:
+sym22:
+sym23:
+sym24:
+sym25:
+sym26:
+sym27:
+sym28:
+sym29:
+sym30:
+sym31:
+sym32:
+sym33:
+sym34:
+sym35:
+sym36:
+sym37:
+sym38:
+sym39:
+sym40:
+sym41:
+sym42:
+sym43:
+sym44:
+sym45:
+sym46:
+sym47:
+sym48:
+sym49:
+sym50:
+sym51:
+sym52:
+sym53:
+sym54:
+sym55:
+sym56:
+sym57:
+sym58:
+sym59:
+sym60:
+sym61:
+sym62:
+sym63:
+sym64:
+sym65:
+sym66:
+sym67:
+sym68:
+sym69:
+sym70:
+sym71:
+sym72:
+sym73:
+sym74:
+sym75:
+sym76:
+sym77:
+sym78:
+sym79:
+sym80:
+sym81:
+sym82:
+sym83:
+sym84:
+sym85:
+sym86:
+sym87:
+sym88:
+sym89:
+sym90:
+sym91:
+sym92:
+sym93:
+sym94:
+sym95:
+sym96:
+sym97:
+sym98:
+sym99:
+sym100:
+sym101:
+sym102:
+sym103:
+sym104:
+sym105:
+sym106:
+sym107:
+sym108:
+sym109:
+sym110:
+sym111:
+sym112:
+sym113:
+sym114:
+sym115:
+sym116:
+sym117:
+sym118:
+sym119:
+sym120:
+sym121:
+sym122:
+sym123:
+sym124:
+sym125:
+sym126:
+sym127:
+sym128:
+sym129:
+sym130:
+sym131:
+sym132:
+sym133:
+sym134:
+sym135:
+sym136:
+sym137:
+sym138:
+sym139:
+sym140:
+sym141:
+sym142:
+sym143:
+sym144:
+sym145:
+sym146:
+sym147:
+sym148:
+sym149:
+sym150:
+sym151:
+sym152:
+sym153:
+sym154:
+sym155:
+sym156:
+sym157:
+sym158:
+sym159:
+sym160:
+sym161:
+sym162:
+sym163:
+sym164:
+sym165:
+sym166:
+sym167:
+sym168:
+sym169:
+sym170:
+sym171:
+sym172:
+sym173:
+sym174:
+sym175:
+sym176:
+sym177:
+sym178:
+sym179:
+sym180:
+sym181:
+sym182:
+sym183:
+sym184:
+sym185:
+sym186:
+sym187:
+sym188:
+sym189:
+sym190:
+sym191:
+sym192:
+sym193:
+sym194:
+ ret
diff --git a/test/COFF/subsystem-drectve.test b/test/COFF/subsystem-drectve.test
new file mode 100644
index 000000000000..fa567bc7a9af
--- /dev/null
+++ b/test/COFF/subsystem-drectve.test
@@ -0,0 +1,21 @@
+# RUN: yaml2obj < %s > %t.obj
+# RUN: lld-link /dll /noentry /out:%t.dll %t.obj
+# RUN: llvm-readobj -file-headers %t.dll | FileCheck %s
+
+# CHECK: MajorOperatingSystemVersion: 42
+# CHECK: MinorOperatingSystemVersion: 43
+# CHECK: MajorSubsystemVersion: 42
+# CHECK: MinorSubsystemVersion: 43
+# CHECK: Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_I386
+ Characteristics: []
+sections:
+ - Name: .drectve
+ Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ]
+ Alignment: 1
+ SectionData: 2f73756273797374656d3a636f6e736f6c652c34322e343300 # /subsystem:console,42.43
+symbols:
+...
diff --git a/test/COFF/symtab.test b/test/COFF/symtab.test
index ffaca285d6f0..4e46e3383a4a 100644
--- a/test/COFF/symtab.test
+++ b/test/COFF/symtab.test
@@ -1,10 +1,10 @@
# RUN: yaml2obj < %s > %t.obj
-# RUN: lld-link /debug /out:%t.exe /entry:main %t.obj %p/Inputs/std64.lib
+# RUN: lld-link /debug:dwarf /out:%t.exe /entry:main %t.obj %p/Inputs/std64.lib
# RUN: llvm-readobj -symbols %t.exe | FileCheck %s
-# RUN: lld-link /debug /opt:noref /out:%t.exe /entry:main %t.obj %p/Inputs/std64.lib
+# RUN: lld-link /debug:dwarf /opt:noref /out:%t.exe /entry:main %t.obj %p/Inputs/std64.lib
# RUN: llvm-readobj -symbols %t.exe | FileCheck %s
-# RUN: lld-link /debug /nosymtab /out:%t.exe /entry:main %t.obj %p/Inputs/std64.lib
+# RUN: lld-link /debug /out:%t.exe /entry:main %t.obj %p/Inputs/std64.lib
# RUN: llvm-readobj -symbols %t.exe | FileCheck -check-prefix=NO %s
# CHECK: Symbols [
diff --git a/test/COFF/thinlto.ll b/test/COFF/thinlto.ll
index f01d0d802289..77ba9b91de70 100644
--- a/test/COFF/thinlto.ll
+++ b/test/COFF/thinlto.ll
@@ -4,7 +4,7 @@
; RUN: opt -thinlto-bc -o %T/thinlto/main.obj %s
; RUN: opt -thinlto-bc -o %T/thinlto/foo.obj %S/Inputs/lto-dep.ll
; RUN: lld-link /lldsavetemps /out:%T/thinlto/main.exe /entry:main /subsystem:console %T/thinlto/main.obj %T/thinlto/foo.obj
-; RUN: llvm-nm %T/thinlto/main.exe.lto.obj | FileCheck %s
+; RUN: llvm-nm %T/thinlto/main.exe1.lto.obj | FileCheck %s
; CHECK-NOT: U foo
diff --git a/test/COFF/wholearchive.s b/test/COFF/wholearchive.s
new file mode 100644
index 000000000000..da9976382b0d
--- /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/COFF/wx.s b/test/COFF/wx.s
new file mode 100644
index 000000000000..3a470edc261c
--- /dev/null
+++ b/test/COFF/wx.s
@@ -0,0 +1,17 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -triple=x86_64-windows-msvc -filetype=obj -o %t.obj %s
+# RUN: not lld-link /out:%t.exe /entry:main -notarealoption /WX %t.obj 2>&1 | \
+# RUN: FileCheck -check-prefix=ERROR %s
+# RUN: not lld-link /out:%t.exe /entry:main -notarealoption /WX:NO /WX %t.obj 2>&1 | \
+# RUN: FileCheck -check-prefix=ERROR %s
+# RUN: lld-link /out:%t.exe /entry:main -notarealoption /WX /WX:NO %t.obj 2>&1 | \
+# RUN: FileCheck -check-prefix=WARNING %s
+
+# ERROR: error: ignoring unknown argument: -notarealoption
+# WARNING: warning: ignoring unknown argument: -notarealoption
+
+.text
+.global main
+main:
+ ret
diff --git a/test/ELF/Inputs/amdgpu-kernel-0.s b/test/ELF/Inputs/amdgpu-kernel-0.s
new file mode 100644
index 000000000000..1567c3fda10f
--- /dev/null
+++ b/test/ELF/Inputs/amdgpu-kernel-0.s
@@ -0,0 +1,6 @@
+.text
+.globl kernel_0
+.align 64
+.amdgpu_hsa_kernel kernel_0
+kernel_0:
+ s_endpgm
diff --git a/test/ELF/Inputs/amdgpu-kernel-1.s b/test/ELF/Inputs/amdgpu-kernel-1.s
new file mode 100644
index 000000000000..7fb520bfbe32
--- /dev/null
+++ b/test/ELF/Inputs/amdgpu-kernel-1.s
@@ -0,0 +1,6 @@
+.text
+.globl kernel_1
+.align 64
+.amdgpu_hsa_kernel kernel_1
+kernel_1:
+ s_endpgm
diff --git a/test/ELF/Inputs/amdgpu-kernel-2.o b/test/ELF/Inputs/amdgpu-kernel-2.o
new file mode 100644
index 000000000000..fa76151f8976
--- /dev/null
+++ b/test/ELF/Inputs/amdgpu-kernel-2.o
Binary files differ
diff --git a/test/ELF/Inputs/copy-rel-abs.s b/test/ELF/Inputs/copy-rel-abs.s
new file mode 100644
index 000000000000..66d08807b9c9
--- /dev/null
+++ b/test/ELF/Inputs/copy-rel-abs.s
@@ -0,0 +1,13 @@
+ .global foo
+ .type foo, @object
+ .size foo, 4
+foo:
+ .weak bar
+ .type bar, @object
+ .size bar, 4
+bar:
+ .long 42
+
+ .weak zed
+ .type zed, @object
+ zed = 0x1000
diff --git a/test/ELF/Inputs/copy-rel-large.s b/test/ELF/Inputs/copy-rel-large.s
new file mode 100644
index 000000000000..8786a2fa1a3f
--- /dev/null
+++ b/test/ELF/Inputs/copy-rel-large.s
@@ -0,0 +1,4 @@
+ .global foo
+ .type foo, @object
+foo:
+ .size foo, 0x100000001
diff --git a/test/ELF/Inputs/copy-rel-pie.s b/test/ELF/Inputs/copy-rel-pie.s
index 6dac01ddec62..b3345ceea672 100644
--- a/test/ELF/Inputs/copy-rel-pie.s
+++ b/test/ELF/Inputs/copy-rel-pie.s
@@ -9,3 +9,4 @@ foo:
.global bar
.type bar, @function
bar:
+retq
diff --git a/test/ELF/Inputs/corrupt-version-reference.so b/test/ELF/Inputs/corrupt-version-reference.so
new file mode 100644
index 000000000000..ef6adc6a026c
--- /dev/null
+++ b/test/ELF/Inputs/corrupt-version-reference.so
Binary files differ
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 000000000000..dd28fcbd5255
--- /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 000000000000..0aa4008b7b2c
--- /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/gc-sections-shared.s b/test/ELF/Inputs/gc-sections-shared.s
new file mode 100644
index 000000000000..e7017f566411
--- /dev/null
+++ b/test/ELF/Inputs/gc-sections-shared.s
@@ -0,0 +1,3 @@
+.global baz
+.type baz, @function
+baz:
diff --git a/test/ELF/Inputs/gc-sections-shared2.s b/test/ELF/Inputs/gc-sections-shared2.s
new file mode 100644
index 000000000000..fb69c0e218fb
--- /dev/null
+++ b/test/ELF/Inputs/gc-sections-shared2.s
@@ -0,0 +1,3 @@
+.global qux
+.type qux, @function
+qux:
diff --git a/test/ELF/Inputs/local-symbol-in-dso.so b/test/ELF/Inputs/local-symbol-in-dso.so
new file mode 100755
index 000000000000..5062a7aeead2
--- /dev/null
+++ b/test/ELF/Inputs/local-symbol-in-dso.so
Binary files differ
diff --git a/test/ELF/Inputs/map-file5.s b/test/ELF/Inputs/map-file5.s
new file mode 100644
index 000000000000..2a89b4c7f6f5
--- /dev/null
+++ b/test/ELF/Inputs/map-file5.s
@@ -0,0 +1,23 @@
+.bss
+.type sharedFoo,@object
+.globl sharedFoo
+sharedFoo:
+.long 0
+.size sharedFoo, 4
+
+.type sharedBar,@object
+.globl sharedBar
+sharedBar:
+.quad 0
+.size sharedBar, 8
+
+.text
+.globl sharedFunc1
+.type sharedFunc1,@function
+sharedFunc1:
+ nop
+
+.globl sharedFunc2
+.type sharedFunc2,@function
+sharedFunc2:
+ nop
diff --git a/test/ELF/Inputs/mips-micro.s b/test/ELF/Inputs/mips-micro.s
new file mode 100644
index 000000000000..0d0b11f6fdb2
--- /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/shared3.s b/test/ELF/Inputs/shared3.s
index d1f6ffea1332..e7017f566411 100644
--- a/test/ELF/Inputs/shared3.s
+++ b/test/ELF/Inputs/shared3.s
@@ -1,3 +1,3 @@
.global baz
-.type barz, @function
+.type baz, @function
baz:
diff --git a/test/ELF/Inputs/undefined-error.s b/test/ELF/Inputs/undefined-error.s
new file mode 100644
index 000000000000..84ac4f121361
--- /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 6664d62c90f5..d9e7f3829f32 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 3423f678e47a..000000000000
--- 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 000000000000..a342d7dd797f
--- /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 744852b9660a..000000000000
--- 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 000000000000..1b46de6880b1
--- /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 ba6c05ee6b1c..000000000000
--- a/test/ELF/Inputs/verneed2.so
+++ /dev/null
Binary files differ
diff --git a/test/ELF/Inputs/weak-undef-lazy.s b/test/ELF/Inputs/weak-undef-lazy.s
new file mode 100644
index 000000000000..c77477315156
--- /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-no-real.s b/test/ELF/Inputs/wrap-no-real.s
new file mode 100644
index 000000000000..2fd1bcc6bc29
--- /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 000000000000..dbcb0889b965
--- /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/Inputs/wrap.s b/test/ELF/Inputs/wrap.s
index 584e27033d5c..e0765196606b 100644
--- a/test/ELF/Inputs/wrap.s
+++ b/test/ELF/Inputs/wrap.s
@@ -1,4 +1,7 @@
-.globl foo, __wrap_foo, __real_foo
+.global foo
+.weak __wrap_foo
+.protected __wrap_foo
+.global __real_foo
foo = 0x11000
__wrap_foo = 0x11010
__real_foo = 0x11020
diff --git a/test/ELF/aarch64-abs16.s b/test/ELF/aarch64-abs16.s
index c4f5b3e44b58..20a65b1cf4a8 100644
--- a/test/ELF/aarch64-abs16.s
+++ b/test/ELF/aarch64-abs16.s
@@ -24,4 +24,4 @@ _start:
// | FileCheck %s --check-prefix=OVERFLOW
// RUN: not ld.lld %t.o %t257.o -o %t2
// | FileCheck %s --check-prefix=OVERFLOW
-// OVERFLOW: Relocation R_AARCH64_ABS16 out of range
+// OVERFLOW: Relocation R_AARCH64_ABS16 out of range: 65536 is not in [-32768, 65535]
diff --git a/test/ELF/aarch64-abs32.s b/test/ELF/aarch64-abs32.s
index b051692374b1..b93f27a0bc4b 100644
--- a/test/ELF/aarch64-abs32.s
+++ b/test/ELF/aarch64-abs32.s
@@ -24,4 +24,4 @@ _start:
// | FileCheck %s --check-prefix=OVERFLOW
// RUN: not ld.lld %t.o %t257.o -o %t2
// | FileCheck %s --check-prefix=OVERFLOW
-// OVERFLOW: Relocation R_AARCH64_ABS32 out of range
+// OVERFLOW: Relocation R_AARCH64_ABS32 out of range: 4294967296 is not in [-2147483648, 4294967295]
diff --git a/test/ELF/aarch64-call26-error.s b/test/ELF/aarch64-call26-error.s
deleted file mode 100644
index 4b666c69011a..000000000000
--- a/test/ELF/aarch64-call26-error.s
+++ /dev/null
@@ -1,11 +0,0 @@
-// RUN: llvm-mc -filetype=obj -triple=aarch64-pc-freebsd %S/Inputs/abs.s -o %tabs
-// RUN: llvm-mc -filetype=obj -triple=aarch64-pc-freebsd %s -o %t
-// RUN: not ld.lld %t %tabs -o %t2 2>&1 | FileCheck %s
-// REQUIRES: aarch64
-
-.text
-.globl _start
-_start:
- bl big
-
-// CHECK: R_AARCH64_CALL26 out of range
diff --git a/test/ELF/aarch64-call26-thunk.s b/test/ELF/aarch64-call26-thunk.s
new file mode 100644
index 000000000000..0fe99cec974d
--- /dev/null
+++ b/test/ELF/aarch64-call26-thunk.s
@@ -0,0 +1,21 @@
+// RUN: llvm-mc -filetype=obj -triple=aarch64-pc-freebsd %S/Inputs/abs.s -o %tabs
+// RUN: llvm-mc -filetype=obj -triple=aarch64-pc-freebsd %s -o %t
+// RUN: ld.lld %t %tabs -o %t2 2>&1
+// RUN: llvm-objdump -d -triple=aarch64-pc-freebsd %t2 | FileCheck %s
+// REQUIRES: aarch64
+
+.text
+.globl _start
+_start:
+ bl big
+
+// CHECK: Disassembly of section .text:
+// CHECK-NEXT: _start:
+// CHECK-NEXT: 20000: 02 00 00 94 bl #8
+// CHECK: __AArch64AbsLongThunk_big:
+// CHECK-NEXT: 20008: 50 00 00 58 ldr x16, #8
+// CHECK-NEXT: 2000c: 00 02 1f d6 br x16
+// CHECK: $d:
+// CHECK-NEXT: 20010: 00 00 00 00 .word 0x00000000
+// CHECK-NEXT: 20014: 10 00 00 00 .word 0x00000010
+
diff --git a/test/ELF/aarch64-cortex-a53-843419-address.s b/test/ELF/aarch64-cortex-a53-843419-address.s
new file mode 100644
index 000000000000..e9f6ff4c38db
--- /dev/null
+++ b/test/ELF/aarch64-cortex-a53-843419-address.s
@@ -0,0 +1,180 @@
+// REQUIRES: aarch64
+// RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux %s -o %t.o
+// RUN: echo "SECTIONS { \
+// RUN: .text : { *(.text) *(.text.*) *(.newisd) } \
+// RUN: .text2 : { *.(newos) } \
+// RUN: .data : { *(.data) } }" > %t.script
+// RUN: ld.lld --script %t.script -fix-cortex-a53-843419 -verbose %t.o -o %t2 | FileCheck -check-prefix=CHECK-PRINT %s
+// RUN: llvm-objdump -triple=aarch64-linux-gnu -d %t2 | FileCheck %s
+
+// Test cases for Cortex-A53 Erratum 843419 that involve interactions
+// between the generated patches and the address of sections.
+
+// See ARM-EPM-048406 Cortex_A53_MPCore_Software_Developers_Errata_Notice.pdf
+// for full erratum details.
+// In Summary
+// 1.)
+// ADRP (0xff8 or 0xffc).
+// 2.)
+// - load or store single register or either integer or vector registers.
+// - STP or STNP of either vector or vector registers.
+// - Advanced SIMD ST1 store instruction.
+// - Must not write Rn.
+// 3.) optional instruction, can't be a branch, must not write Rn, may read Rn.
+// 4.) A load or store instruction from the Load/Store register unsigned
+// immediate class using Rn as the base register.
+
+// An aarch64 section can contain ranges of literal data embedded within the
+// code, these ranges are encoded with mapping symbols. This tests that we
+// can match the erratum sequence in code, but not data.
+// - We can handle more than one patch per code range (denoted by mapping
+// symbols).
+// - We can handle a patch in more than range of code, with literal data
+// inbetween.
+// - We can handle redundant mapping symbols (two or more consecutive mapping
+// symbols with the same type).
+// - We can ignore erratum sequences in multiple literal data ranges.
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at FF8 in unpatched output.
+// CHECK: t3_ff8_ldr:
+// CHECK-NEXT: ff8: 20 00 00 d0 adrp x0, #24576
+// CHECK-NEXT: ffc: 21 00 40 f9 ldr x1, [x1]
+// CHECK-NEXT: 1000: f9 0f 00 14 b #16356
+// CHECK-NEXT: 1004: c0 03 5f d6 ret
+ .section .text.01, "ax", %progbits
+ .balign 4096
+ .space 4096 - 8
+ .globl t3_ff8_ldr
+ .type t3_ff8_ldr, %function
+t3_ff8_ldr:
+ adrp x0, dat
+ ldr x1, [x1, #0]
+ ldr x0, [x0, :got_lo12:dat]
+ ret
+
+ // create a redundant mapping symbol as we are already in a $x range
+ // some object producers unconditionally generate a mapping symbol on
+ // every symbol so we need to handle the case of $x $x.
+ .local $x.999
+$x.999:
+// CHECK-PRINT-NEXT: detected cortex-a53-843419 erratum sequence starting at 1FFC in unpatched output.
+// CHECK: t3_ffc_ldrsimd:
+// CHECK-NEXT: 1ffc: 20 00 00 b0 adrp x0, #20480
+// CHECK-NEXT: 2000: 21 00 40 bd ldr s1, [x1]
+// CHECK-NEXT: 2004: fa 0b 00 14 b #12264
+// CHECK-NEXT: 2008: c0 03 5f d6 ret
+ .globl t3_ffc_ldrsimd
+ .type t3_ffc_ldrsimd, %function
+ .space 4096 - 12
+t3_ffc_ldrsimd:
+ adrp x0, dat
+ ldr s1, [x1, #0]
+ ldr x2, [x0, :got_lo12:dat]
+ ret
+
+// Inline data containing bit pattern of erratum sequence, expect no patch.
+ .globl t3_ffc_ldralldata
+ .type t3_ff8_ldralldata, %function
+ .space 4096 - 20
+t3_ff8_ldralldata:
+ // 0x90000000 = adrp x0, #0
+ .byte 0x00
+ .byte 0x00
+ .byte 0x00
+ .byte 0x90
+ // 0xf9400021 = ldr x1, [x1]
+ .byte 0x21
+ .byte 0x00
+ .byte 0x40
+ .byte 0xf9
+ // 0xf9400000 = ldr x0, [x0]
+ .byte 0x00
+ .byte 0x00
+ .byte 0x40
+ .byte 0xf9
+ // Check that we can recognise the erratum sequence post literal data.
+
+// CHECK-PRINT-NEXT: detected cortex-a53-843419 erratum sequence starting at 3FF8 in unpatched output.
+// CHECK: t3_ffc_ldr:
+// CHECK-NEXT: 3ff8: 00 00 00 f0 adrp x0, #12288
+// CHECK-NEXT: 3ffc: 21 00 40 f9 ldr x1, [x1]
+// CHECK-NEXT: 4000: fd 03 00 14 b #4084
+// CHECK-NEXT: 4004: c0 03 5f d6 ret
+ .space 4096 - 12
+ .globl t3_ffc_ldr
+ .type t3_ffc_ldr, %function
+ t3_ffc_ldr:
+ adrp x0, dat
+ ldr x1, [x1, #0]
+ ldr x0, [x0, :got_lo12:dat]
+ ret
+
+// CHECK: __CortexA53843419_1000:
+// CHECK-NEXT: 4fe4: 00 0c 40 f9 ldr x0, [x0, #24]
+// CHECK-NEXT: 4fe8: 07 f0 ff 17 b #-16356
+// CHECK: __CortexA53843419_2004:
+// CHECK-NEXT: 4fec: 02 0c 40 f9 ldr x2, [x0, #24]
+// CHECK-NEXT: 4ff0: 06 f4 ff 17 b #-12264
+// CHECK: __CortexA53843419_4000:
+// CHECK-NEXT: 4ff4: 00 0c 40 f9 ldr x0, [x0, #24]
+// CHECK-NEXT: 4ff8: 03 fc ff 17 b #-4084
+
+ .section .text.02, "ax", %progbits
+ .space 4096 - 36
+
+ // Start a new InputSectionDescription (see Linker Script) so the
+ // start address will be affected by any patches added to previous
+ // InputSectionDescription.
+
+// CHECK-PRINT-NEXT: detected cortex-a53-843419 erratum sequence starting at 4FFC in unpatched output
+// CHECK: t3_ffc_str:
+// CHECK-NEXT: 4ffc: 00 00 00 d0 adrp x0, #8192
+// CHECK-NEXT: 5000: 21 00 00 f9 str x1, [x1]
+// CHECK-NEXT: 5004: fb 03 00 14 b #4076
+// CHECK-NEXT: 5008: c0 03 5f d6 ret
+
+ .section .newisd, "ax", %progbits
+ .globl t3_ffc_str
+ .type t3_ffc_str, %function
+t3_ffc_str:
+ adrp x0, dat
+ str x1, [x1, #0]
+ ldr x0, [x0, :got_lo12:dat]
+ ret
+ .space 4096 - 28
+
+// CHECK: __CortexA53843419_5004:
+// CHECK-NEXT: 5ff0: 00 0c 40 f9 ldr x0, [x0, #24]
+// CHECK-NEXT: 5ff4: 05 fc ff 17 b #-4076
+
+ // Start a new OutputSection (see Linker Script) so the
+ // start address will be affected by any patches added to previous
+ // InputSectionDescription.
+
+//CHECK-PRINT-NEXT: detected cortex-a53-843419 erratum sequence starting at 5FF8 in unpatched output
+// CHECK: t3_ff8_str:
+// CHECK-NEXT: 5ff8: 00 00 00 b0 adrp x0, #4096
+// CHECK-NEXT: 5ffc: 21 00 00 f9 str x1, [x1]
+// CHECK-NEXT: 6000: 03 00 00 14 b #12
+// CHECK-NEXT: 6004: c0 03 5f d6 ret
+
+ .section .newos, "ax", %progbits
+ .globl t3_ff8_str
+ .type t3_ff8_str, %function
+t3_ff8_str:
+ adrp x0, dat
+ str x1, [x1, #0]
+ ldr x0, [x0, :got_lo12:dat]
+ ret
+ .globl _start
+ .type _start, %function
+_start:
+ ret
+
+// CHECK: __CortexA53843419_6000:
+// CHECK-NEXT: 600c: 00 0c 40 f9 ldr x0, [x0, #24]
+// CHECK-NEXT: 6010: fd ff ff 17 b #-12
+
+ .data
+ .globl dat
+dat: .word 0
diff --git a/test/ELF/aarch64-cortex-a53-843419-cli.s b/test/ELF/aarch64-cortex-a53-843419-cli.s
new file mode 100644
index 000000000000..30abc8f06d20
--- /dev/null
+++ b/test/ELF/aarch64-cortex-a53-843419-cli.s
@@ -0,0 +1,10 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+// RUN: not ld.lld %t -fix-cortex-a53-843419 -o %t2 2>&1 | FileCheck %s
+
+// CHECK: --fix-cortex-a53-843419 is only supported on AArch64 targets.
+.globl entry
+.text
+ .quad 0
+entry:
+ ret
diff --git a/test/ELF/aarch64-cortex-a53-843419-large.s b/test/ELF/aarch64-cortex-a53-843419-large.s
new file mode 100644
index 000000000000..00c92ebeb182
--- /dev/null
+++ b/test/ELF/aarch64-cortex-a53-843419-large.s
@@ -0,0 +1,115 @@
+// REQUIRES: aarch64
+// RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux %s -o %t.o
+// RUN: ld.lld --fix-cortex-a53-843419 %t.o -o %t2
+// RUN: llvm-objdump -triple=aarch64-linux-gnu -d %t2 -start-address=131072 -stop-address=131084 | FileCheck --check-prefix=CHECK1 %s
+// RUN: llvm-objdump -triple=aarch64-linux-gnu -d %t2 -start-address=135168 -stop-address=135172 | FileCheck --check-prefix=CHECK2 %s
+// RUN: llvm-objdump -triple=aarch64-linux-gnu -d %t2 -start-address=139256 -stop-address=139272 | FileCheck --check-prefix=CHECK3 %s
+// RUN: llvm-objdump -triple=aarch64-linux-gnu -d %t2 -start-address=67256312 -stop-address=67256328 | FileCheck --check-prefix=CHECK4 %s
+// RUN: llvm-objdump -triple=aarch64-linux-gnu -d %t2 -start-address=100810760 -stop-address=100810776 | FileCheck --check-prefix=CHECK5 %s
+// RUN: llvm-objdump -triple=aarch64-linux-gnu -d %t2 -start-address=134352908 -stop-address=134352912 | FileCheck --check-prefix=CHECK6 %s
+// RUN: llvm-objdump -triple=aarch64-linux-gnu -d %t2 -start-address=134356988 -stop-address=134357012 | FileCheck --check-prefix=CHECK7 %s
+// Test case for Cortex-A53 Erratum 843419 in an OutputSection exceeding
+// the maximum branch range. Both range extension thunks and patches are
+// required.
+
+// CHECK1: __AArch64AbsLongThunk_need_thunk_after_patch:
+// CHECK1-NEXT: 20000: 50 00 00 58 ldr x16, #8
+// CHECK1-NEXT: 20004: 00 02 1f d6 br x16
+// CHECK1: $d:
+// CHECK1-NEXT: 20008: 0c 10 02 08 .word 0x0802100c
+
+ .section .text.01, "ax", %progbits
+ .balign 4096
+ .globl _start
+ .type _start, %function
+_start:
+ // Expect thunk on pass 2
+ bl need_thunk_after_patch
+ .section .text.02, "ax", %progbits
+ .space 4096 - 12
+
+// CHECK2: _start:
+// CHECK2-NEXT: 21000: 00 fc ff 97 bl #-4096
+
+ // Expect patch on pass 1
+ .section .text.03, "ax", %progbits
+ .globl t3_ff8_ldr
+ .type t3_ff8_ldr, %function
+t3_ff8_ldr:
+ adrp x0, dat
+ ldr x1, [x1, #0]
+ ldr x0, [x0, :got_lo12:dat]
+ ret
+
+// CHECK3: t3_ff8_ldr:
+// CHECK3-NEXT: 21ff8: 60 00 04 f0 adrp x0, #134279168
+// CHECK3-NEXT: 21ffc: 21 00 40 f9 ldr x1, [x1]
+// CHECK3-NEXT: 22000: 02 08 80 15 b #100671496
+// CHECK3-NEXT: 22004: c0 03 5f d6 ret
+
+ .section .text.04, "ax", %progbits
+ .space 64 * 1024 * 1024
+
+ // Expect patch on pass 1
+ .section .text.05, "ax", %progbits
+ .balign 4096
+ .space 4096 - 8
+ .globl t3_ff8_str
+ .type t3_ff8_str, %function
+t3_ff8_str:
+ adrp x0, dat
+ ldr x1, [x1, #0]
+ str x0, [x0, :got_lo12:dat]
+ ret
+
+// CHECK4: t3_ff8_str:
+// CHECK4-NEXT: 4023ff8: 60 00 02 b0 adrp x0, #67162112
+// CHECK4-NEXT: 4023ffc: 21 00 40 f9 ldr x1, [x1]
+// CHECK4-NEXT: 4024000: 04 00 80 14 b #33554448
+// CHECK4-NEXT: 4024004: c0 03 5f d6 ret
+
+ .section .text.06, "ax", %progbits
+ .space 32 * 1024 * 1024
+
+// CHECK5: __CortexA53843419_21000:
+// CHECK5-NEXT: 6024008: 00 00 40 f9 ldr x0, [x0]
+// CHECK5-NEXT: 602400c: fe f7 7f 16 b #-100671496
+// CHECK5: __CortexA53843419_4023000:
+// CHECK5-NEXT: 6024010: 00 00 00 f9 str x0, [x0]
+// CHECK5-NEXT: 6024014: fc ff 7f 17 b #-33554448
+
+ .section .text.07, "ax", %progbits
+ .space (32 * 1024 * 1024) - 12300
+
+ .section .text.08, "ax", %progbits
+ .globl need_thunk_after_patch
+ .type need_thunk_after_patch, %function
+need_thunk_after_patch:
+ ret
+
+// CHECK6: need_thunk_after_patch:
+// CHECK6-NEXT: 802100c: c0 03 5f d6 ret
+
+ // Will need a patch on pass 2
+ .section .text.09, "ax", %progbits
+ .space 4096 - 20
+ .globl t3_ffc_ldr
+ .type t3_ffc_ldr, %function
+t3_ffc_ldr:
+ adrp x0, dat
+ ldr x1, [x1, #0]
+ ldr x0, [x0, :got_lo12:dat]
+ ret
+
+// CHECK7: t3_ffc_ldr:
+// CHECK7-NEXT: 8021ffc: 60 00 00 f0 adrp x0, #61440
+// CHECK7-NEXT: 8022000: 21 00 40 f9 ldr x1, [x1]
+// CHECK7-NEXT: 8022004: 02 00 00 14 b #8
+// CHECK7-NEXT: 8022008: c0 03 5f d6 ret
+// CHECK7: __CortexA53843419_8022004:
+// CHECK7-NEXT: 802200c: 00 00 40 f9 ldr x0, [x0]
+// CHECK7-NEXT: 8022010: fe ff ff 17 b #-8
+
+ .section .data
+ .globl dat
+dat: .quad 0
diff --git a/test/ELF/aarch64-cortex-a53-843419-nopatch.s b/test/ELF/aarch64-cortex-a53-843419-nopatch.s
new file mode 100644
index 000000000000..389bf4505735
--- /dev/null
+++ b/test/ELF/aarch64-cortex-a53-843419-nopatch.s
@@ -0,0 +1,338 @@
+// REQUIRES: aarch64
+// RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux %s -o %t.o
+// RUN: ld.lld -fix-cortex-a53-843419 -verbose -t %t.o -o %t2 | FileCheck %s
+// Test cases for Cortex-A53 Erratum 843419 that we don't expect to recognize
+// as needing a patch as one or more of the conditions isn't satisfied.
+// See ARM-EPM-048406 Cortex_A53_MPCore_Software_Developers_Errata_Notice.pdf
+// for full erratum details.
+// In Summary
+// 1.)
+// ADRP (0xff8 or 0xffc)
+// 2.)
+// - load or store single register or either integer or vector registers
+// - STP or STNP of either vector or vector registers
+// - Advanced SIMD ST1 store instruction
+// Must not write Rn
+// 3.) optional instruction, can't be a branch, must not write Rn, may read Rn
+// 4.) A load or store instruction from the Load/Store register unsigned
+// immediate class using Rn as the base register
+
+// Expect no patches detected.
+// CHECK-NOT: detected cortex-a53-843419 erratum sequence
+
+// erratum sequence but adrp (address & 0xfff) is not 0xff8 or 0xffc
+ .section .text.01, "ax", %progbits
+ .balign 4096
+ .globl t3_0_ldr
+ .type t3_ff8_ldr, %function
+t3_0_ldr:
+ adrp x0, dat
+ ldr x1, [x1, #0]
+ ldr x0, [x0, :got_lo12:dat]
+ ret
+
+ .section .text.02, "ax", %progbits
+ .balign 4096
+ .globl t3_ff4_ldr
+ .space 4096 - 12
+ .type t3_ff4_ldr, %function
+t3_ff4_ldr:
+ adrp x0, dat
+ ldr x1, [x1, #0]
+ ldr x0, [x0, :got_lo12:dat]
+ ret
+
+// Close matches for erratum sequence, with adrp at correct address but
+// instruction 2 is a load or store but not one that matches the erratum
+// conditions, but with a similar encoding to an instruction that does.
+
+ // ldp is not part of sequence, although stp is.
+ .section .text.03, "ax", %progbits
+ .balign 4096
+ .globl t3_ff8_ldp
+ .type t3_ff8_ldp, %function
+ .space 4096 - 8
+t3_ff8_ldp:
+ adrp x16, dat
+ ldp x1,x2, [x3, #0]
+ ldr x13, [x16, :got_lo12:dat]
+ ret
+
+ // st2 is not part of sequence although st1 is.
+ .section .text.04, "ax", %progbits
+ .balign 4096
+ .globl t3_ffc_st2
+ .type t3_ffc_st2, %function
+ .space 4096 - 4
+t3_ffc_st2:
+ adrp x16, dat
+ st2 { v0.16b, v1.16b }, [x1]
+ ldr x13, [x16, :got_lo12:dat]
+ ret
+
+ // st3 is not part of sequence although st1 is.
+ .section .text.05, "ax", %progbits
+ .balign 4096
+ .globl t3_ffc_st3
+ .type t3_ffc_st3, %function
+ .space 4096 - 4
+t3_ffc_st3:
+ adrp x16, dat
+ st3 { v0.16b, v1.16b, v2.16b }, [x1], x2
+ ldr x13, [x16, :got_lo12:dat]
+ ret
+
+ // ld1 is not part of sequence although st1 is.
+ .section .text.06, "ax", %progbits
+ .balign 4096
+ .globl t3_ffc_ld2
+ .type t3_ffc_st3, %function
+ .space 4096 - 4
+t3_ffc_ld1:
+ adrp x16, dat
+ ld1 { v0.16b }, [x2], x3
+ ldr x13, [x16, :got_lo12:dat]
+ ret
+
+ // ldnp is not part of sequence although stnp is.
+ .section .text.07, "ax", %progbits
+ .balign 4096
+ .globl t4_ff8_ldnp
+ .type t4_ff8_ldnp, %function
+ .space 4096 - 8
+t4_ff8_ldnp:
+ adrp x7, dat
+ ldnp x1,x2, [x3, #0]
+ nop
+ ldr x10, [x7, :got_lo12:dat]
+ ret
+
+// Close match for erratum sequence, with adrp at correct address but
+// instruction 2 writes to Rn, with Rn as either destination or as the
+// transfer register but with writeback.
+
+ // ldr instruction writes to Rn
+ .section .text.08, "ax", %progbits
+ .balign 4096
+ .globl t3_ff8_ldr
+ .type t3_ff8_ldr, %function
+ .space 4096 - 8
+t3_ff8_ldr:
+ adrp x0, dat
+ ldr x0, [x1, #0]
+ ldr x0, [x0, :got_lo12:dat]
+ ret
+
+ // str instruction writes to Rn via writeback (pre index)
+ .section .text.09, "ax", %progbits
+ .balign 4096
+ .globl t3_ff8_str
+ .type t3_ff8_str, %function
+ .space 4096 - 8
+t3_ff8_str:
+ adrp x0, dat
+ str x1, [x0, #4]!
+ ldr x0, [x0, :got_lo12:dat]
+ ret
+
+ // ldr instruction writes to Rn via writeback (post index)
+ .section .text.09, "ax", %progbits
+ .balign 4096
+ .globl t3_ffc_ldr
+ .type t3_ffc_ldr, %function
+ .space 4096 - 8
+t3_ffc_ldr:
+ adrp x0, dat
+ ldr x1, [x0], 0x8
+ ldr x0, [x0, :got_lo12:dat]
+ ret
+
+ // stp writes to Rn via writeback (pre index)
+ .section .text.10, "ax", %progbits
+ .balign 4096
+ .globl t4_ffc_stppre
+ .type t4_ffc_stppre, %function
+ .space 4096 - 4
+t4_ffc_stppre:
+ adrp x16, dat
+ stp x1,x2, [x16, #16]!
+ mul x3, x16, x16
+ ldr x14, [x16, #8]
+ ret
+
+ // stp writes to Rn via writeback (post index)
+ .section .text.11, "ax", %progbits
+ .balign 4096
+ .globl t4_ff8_stppost
+ .type t4_ff8_stppost, %function
+ .space 4096 - 8
+t4_ff8_stppost:
+ adrp x16, dat
+ stp x1,x2, [x16], #16
+ mul x3, x16, x16
+ ldr x14, [x16, #8]
+ ret
+
+ // st1 writes to Rn via writeback
+ .section .text.12, "ax", %progbits
+ .balign 4096
+ .globl t3_ff8_st1
+ .type t3_ff8_st1, %function
+ .space 4096 - 8
+t3_ff8_st1:
+ adrp x16, dat
+ st1 { v0.16b}, [x16], x2
+ ldr x13, [x16, :got_lo12:dat]
+ ret
+
+// Close match for erratum sequence, but with optional instruction 3 a branch
+
+ // function call via immediate
+ .section .text.13, "ax", %progbits
+ .balign 4096
+ .globl t4_ffc_blimm
+ .type t4_ffc_blimm, %function
+ .space 4096 - 4
+t4_ffc_blimm:
+ adrp x7, dat
+ stnp x1,x2, [x3, #0]
+ bl t4_ffc_blimm
+ ldr x10, [x7, :got_lo12:dat]
+ ret
+
+ // function call via register
+ .section .text.14, "ax", %progbits
+ .balign 4096
+ .globl t4_ffc_blreg
+ .type t4_ffc_blreg, %function
+ .space 4096 - 4
+t4_ffc_blreg:
+ adrp x7, dat
+ stnp x1,x2, [x3, #0]
+ blr x4
+ ldr x10, [x7, :got_lo12:dat]
+ ret
+
+ // Unconditional branch immediate
+ .section .text.15, "ax", %progbits
+ .balign 4096
+ .globl t4_ffc_branchimm
+ .type t4_ffc_branchimm, %function
+ .space 4096 - 4
+t4_ffc_branchimm:
+ adrp x7, dat
+ stnp x1,x2, [x3, #0]
+ b t4_ffc_branchimm
+ ldr x10, [x7, :got_lo12:dat]
+ ret
+
+ // Unconditional branch register
+ .section .text.16, "ax", %progbits
+ .balign 4096
+ .globl t4_ffc_branchreg
+ .type t4_ffc_branchreg, %function
+ .space 4096 - 4
+t4_ffc_branchreg:
+ adrp x7, dat
+ stnp x1,x2, [x3, #0]
+ br x4
+ ldr x10, [x7, :got_lo12:dat]
+ ret
+
+ // Conditional branch
+ .section .text.17, "ax", %progbits
+ .balign 4096
+ .globl t4_ffc_branchcond
+ .type t4_ffc_branchcond, %function
+ .space 4096 - 4
+t4_ffc_branchcond:
+ adrp x7, dat
+ stnp x1,x2, [x3, #0]
+ cbz x5, t4_ffc_branchcond
+ ldr x10, [x7, :got_lo12:dat]
+ ret
+
+ // Conditional branch immediate
+ .section .text.18, "ax", %progbits
+ .balign 4096
+ .globl t4_ffc_branchcondimm
+ .type t4_ffc_branchcondimm, %function
+ .space 4096 - 4
+t4_ffc_branchcondimm:
+ adrp x7, dat
+ stnp x1,x2, [x3, #0]
+ beq t4_ffc_branchcondimm
+ ldr x10, [x7, :got_lo12:dat]
+ ret
+
+// Bitpattern matches erratum sequence but either all or part of the sequence
+// is in inline literal data
+ .section .text.19, "ax", %progbits
+ .balign 4096
+ .globl t3_ffc_ldrtraildata
+ .type t3_ff8_ldrtraildata, %function
+ .space 4096 - 8
+t3_ff8_ldrtraildata:
+ adrp x0, dat
+ ldr x1, [x1, #0]
+ // 0xf9400000 = ldr x0, [x0]
+ .byte 0x00
+ .byte 0x00
+ .byte 0x40
+ .byte 0xf9
+ ldr x0, [x0, :got_lo12:dat]
+ ret
+
+ .section .text.20, "ax", %progbits
+ .balign 4096
+ .globl t3_ffc_ldrpredata
+ .type t3_ff8_ldrpredata, %function
+ .space 4096 - 8
+t3_ff8_ldrpredata:
+ // 0x90000000 = adrp x0, #0
+ .byte 0x00
+ .byte 0x00
+ .byte 0x00
+ .byte 0x90
+ ldr x1, [x1, #0]
+ ldr x0, [x0, :got_lo12:dat]
+ ret
+
+ .section .text.21, "ax", %progbits
+ .balign 4096
+ .globl t3_ffc_ldralldata
+ .type t3_ff8_ldralldata, %function
+ .space 4096 - 8
+t3_ff8_ldralldata:
+ // 0x90000000 = adrp x0, #0
+ .byte 0x00
+ .byte 0x00
+ .byte 0x00
+ .byte 0x90
+ // 0xf9400021 = ldr x1, [x1]
+ .byte 0x21
+ .byte 0x00
+ .byte 0x40
+ .byte 0xf9
+ // 0xf9400000 = ldr x0, [x0]
+ .byte 0x00
+ .byte 0x00
+ .byte 0x40
+ .byte 0xf9
+
+ ret
+
+ .text
+ .globl _start
+ .type _start, %function
+_start:
+ ret
+
+
+
+
+
+// Bitpattern matches erratum sequence but section is not executable
+ .data
+ .globl dat
+dat: .word 0
diff --git a/test/ELF/aarch64-cortex-a53-843419-recognize.s b/test/ELF/aarch64-cortex-a53-843419-recognize.s
new file mode 100644
index 000000000000..3674dd2744da
--- /dev/null
+++ b/test/ELF/aarch64-cortex-a53-843419-recognize.s
@@ -0,0 +1,563 @@
+// REQUIRES: aarch64
+// RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux %s -o %t.o
+// RUN: ld.lld -fix-cortex-a53-843419 -verbose %t.o -o %t2 | FileCheck -check-prefix CHECK-PRINT %s
+// RUN: llvm-objdump -triple=aarch64-linux-gnu -d %t2 | FileCheck %s -check-prefixes=CHECK,CHECK-FIX
+// RUN: ld.lld -verbose %t.o -o %t3
+// RUN: llvm-objdump -triple=aarch64-linux-gnu -d %t3 | FileCheck %s -check-prefixes=CHECK,CHECK-NOFIX
+// Test cases for Cortex-A53 Erratum 843419
+// See ARM-EPM-048406 Cortex_A53_MPCore_Software_Developers_Errata_Notice.pdf
+// for full erratum details.
+// In Summary
+// 1.)
+// ADRP (0xff8 or 0xffc).
+// 2.)
+// - load or store single register or either integer or vector registers.
+// - STP or STNP of either vector or vector registers.
+// - Advanced SIMD ST1 store instruction.
+// - Must not write Rn.
+// 3.) optional instruction, can't be a branch, must not write Rn, may read Rn.
+// 4.) A load or store instruction from the Load/Store register unsigned
+// immediate class using Rn as the base register.
+
+// Each section contains a sequence of instructions that should be recognized
+// as erratum 843419. The test cases cover the major variations such as:
+// - adrp starts at 0xfff8 or 0xfffc.
+// - Variations in instruction class for instruction 2.
+// - Optional instruction 3 present or not.
+// - Load or store for instruction 4.
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 21FF8 in unpatched output.
+// CHECK: t3_ff8_ldr:
+// CHECK-NEXT: 21ff8: e0 01 00 f0 adrp x0, #258048
+// CHECK-NEXT: 21ffc: 21 00 40 f9 ldr x1, [x1]
+// CHECK-FIX: 22000: 03 b8 00 14 b #188428
+// CHECK-NOFIX: 22000: 00 00 40 f9 ldr x0, [x0]
+// CHECK-NEXT: 22004: c0 03 5f d6 ret
+ .section .text.01, "ax", %progbits
+ .balign 4096
+ .globl t3_ff8_ldr
+ .type t3_ff8_ldr, %function
+ .space 4096 - 8
+t3_ff8_ldr:
+ adrp x0, dat1
+ ldr x1, [x1, #0]
+ ldr x0, [x0, :got_lo12:dat1]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 23FF8 in unpatched output.
+// CHECK: t3_ff8_ldrsimd:
+// CHECK-NEXT: 23ff8: e0 01 00 b0 adrp x0, #249856
+// CHECK-NEXT: 23ffc: 21 00 40 bd ldr s1, [x1]
+// CHECK-FIX: 24000: 05 b0 00 14 b #180244
+// CHECK-NOFIX: 24000: 02 04 40 f9 ldr x2, [x0, #8]
+// CHECK-NEXT: 24004: c0 03 5f d6 ret
+ .section .text.02, "ax", %progbits
+ .balign 4096
+ .globl t3_ff8_ldrsimd
+ .type t3_ff8_ldrsimd, %function
+ .space 4096 - 8
+t3_ff8_ldrsimd:
+ adrp x0, dat2
+ ldr s1, [x1, #0]
+ ldr x2, [x0, :got_lo12:dat2]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 25FFC in unpatched output.
+// CHECK: t3_ffc_ldrpost:
+// CHECK-NEXT: 25ffc: c0 01 00 f0 adrp x0, #241664
+// CHECK-NEXT: 26000: 21 84 40 bc ldr s1, [x1], #8
+// CHECK-FIX: 26004: 06 a8 00 14 b #172056
+// CHECK-NOFIX: 26004: 03 08 40 f9 ldr x3, [x0, #16]
+// CHECK-NEXT: 26008: c0 03 5f d6 ret
+ .section .text.03, "ax", %progbits
+ .balign 4096
+ .globl t3_ffc_ldrpost
+ .type t3_ffc_ldrpost, %function
+ .space 4096 - 4
+t3_ffc_ldrpost:
+ adrp x0, dat3
+ ldr s1, [x1], #8
+ ldr x3, [x0, :got_lo12:dat3]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 27FF8 in unpatched output.
+// CHECK: t3_ff8_strpre:
+// CHECK-NEXT: 27ff8: c0 01 00 b0 adrp x0, #233472
+// CHECK-NEXT: 27ffc: 21 8c 00 bc str s1, [x1, #8]!
+// CHECK-FIX: 28000: 09 a0 00 14 b #163876
+// CHECK-NOFIX: 28000: 02 00 40 f9 ldr x2, [x0]
+// CHECK-NEXT: 28004: c0 03 5f d6 ret
+ .section .text.04, "ax", %progbits
+ .balign 4096
+ .globl t3_ff8_strpre
+ .type t3_ff8_strpre, %function
+ .space 4096 - 8
+t3_ff8_strpre:
+ adrp x0, dat1
+ str s1, [x1, #8]!
+ ldr x2, [x0, :lo12:dat1]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 29FFC in unpatched output.
+// CHECK: t3_ffc_str:
+// CHECK-NEXT: 29ffc: bc 01 00 f0 adrp x28, #225280
+// CHECK-NEXT: 2a000: 42 00 00 f9 str x2, [x2]
+// CHECK-FIX: 2a004: 0a 98 00 14 b #155688
+// CHECK-NOFIX: 2a004: 9c 07 00 f9 str x28, [x28, #8]
+// CHECK-NEXT: 2a008: c0 03 5f d6 ret
+ .section .text.05, "ax", %progbits
+ .balign 4096
+ .globl t3_ffc_str
+ .type t3_ffc_str, %function
+ .space 4096 - 4
+t3_ffc_str:
+ adrp x28, dat2
+ str x2, [x2, #0]
+ str x28, [x28, :lo12:dat2]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 2BFFC in unpatched output.
+// CHECK: t3_ffc_strsimd:
+// CHECK-NEXT: 2bffc: bc 01 00 b0 adrp x28, #217088
+// CHECK-NEXT: 2c000: 44 00 00 b9 str w4, [x2]
+// CHECK-FIX: 2c004: 0c 90 00 14 b #147504
+// CHECK-NOFIX: 2c004: 84 0b 00 f9 str x4, [x28, #16]
+// CHECK-NEXT: 2c008: c0 03 5f d6 ret
+ .section .text.06, "ax", %progbits
+ .balign 4096
+ .globl t3_ffc_strsimd
+ .type t3_ffc_strsimd, %function
+ .space 4096 - 4
+t3_ffc_strsimd:
+ adrp x28, dat3
+ str w4, [x2, #0]
+ str x4, [x28, :lo12:dat3]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 2DFF8 in unpatched output.
+// CHECK: t3_ff8_ldrunpriv:
+// CHECK-NEXT: 2dff8: 9d 01 00 f0 adrp x29, #208896
+// CHECK-NEXT: 2dffc: 41 08 40 38 ldtrb w1, [x2]
+// CHECK-FIX: 2e000: 0f 88 00 14 b #139324
+// CHECK-NOFIX: 2e000: bd 03 40 f9 ldr x29, [x29]
+// CHECK-NEXT: 2e004: c0 03 5f d6 ret
+ .section .text.07, "ax", %progbits
+ .balign 4096
+ .globl t3_ff8_ldrunpriv
+ .type t3_ff8_ldrunpriv, %function
+ .space 4096 - 8
+t3_ff8_ldrunpriv:
+ adrp x29, dat1
+ ldtrb w1, [x2, #0]
+ ldr x29, [x29, :got_lo12:dat1]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 2FFFC in unpatched output.
+// CHECK: t3_ffc_ldur:
+// CHECK-NEXT: 2fffc: 9d 01 00 b0 adrp x29, #200704
+// CHECK-NEXT: 30000: 42 40 40 b8 ldur w2, [x2, #4]
+// CHECK-FIX: 30004: 10 80 00 14 b #131136
+// CHECK-NOFIX: 30004: bd 07 40 f9 ldr x29, [x29, #8]
+// CHECK-NEXT: 30008: c0 03 5f d6 ret
+ .balign 4096
+ .globl t3_ffc_ldur
+ .type t3_ffc_ldur, %function
+ .space 4096 - 4
+t3_ffc_ldur:
+ adrp x29, dat2
+ ldur w2, [x2, #4]
+ ldr x29, [x29, :got_lo12:dat2]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 31FFC in unpatched output.
+// CHECK: t3_ffc_sturh:
+// CHECK-NEXT: 31ffc: 72 01 00 f0 adrp x18, #192512
+// CHECK-NEXT: 32000: 43 40 00 78 sturh w3, [x2, #4]
+// CHECK-FIX: 32004: 12 78 00 14 b #122952
+// CHECK-NOFIX: 32004: 41 0a 40 f9 ldr x1, [x18, #16]
+// CHECK-NEXT: 32008: c0 03 5f d6 ret
+ .section .text.09, "ax", %progbits
+ .balign 4096
+ .globl t3_ffc_sturh
+ .type t3_ffc_sturh, %function
+ .space 4096 - 4
+t3_ffc_sturh:
+ adrp x18, dat3
+ sturh w3, [x2, #4]
+ ldr x1, [x18, :got_lo12:dat3]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 33FF8 in unpatched output.
+// CHECK: t3_ff8_literal:
+// CHECK-NEXT: 33ff8: 72 01 00 b0 adrp x18, #184320
+// CHECK-NEXT: 33ffc: e3 ff ff 58 ldr x3, #-4
+// CHECK-FIX: 34000: 15 70 00 14 b #114772
+// CHECK-NOFIX: 34000: 52 02 40 f9 ldr x18, [x18]
+// CHECK-NEXT: 34004: c0 03 5f d6 ret
+ .section .text.10, "ax", %progbits
+ .balign 4096
+ .globl t3_ff8_literal
+ .type t3_ff8_literal, %function
+ .space 4096 - 8
+t3_ff8_literal:
+ adrp x18, dat1
+ ldr x3, t3_ff8_literal
+ ldr x18, [x18, :lo12:dat1]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 35FFC in unpatched output.
+// CHECK: t3_ffc_register:
+// CHECK-NEXT: 35ffc: 4f 01 00 f0 adrp x15, #176128
+// CHECK-NEXT: 36000: 43 68 61 f8 ldr x3, [x2, x1]
+// CHECK-FIX: 36004: 16 68 00 14 b #106584
+// CHECK-NOFIX: 36004: ea 05 40 f9 ldr x10, [x15, #8]
+// CHECK-NEXT: 36008: c0 03 5f d6 ret
+ .section .text.11, "ax", %progbits
+ .balign 4096
+ .globl t3_ffc_register
+ .type t3_ffc_register, %function
+ .space 4096 - 4
+t3_ffc_register:
+ adrp x15, dat2
+ ldr x3, [x2, x1]
+ ldr x10, [x15, :lo12:dat2]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 37FF8 in unpatched output.
+// CHECK: t3_ff8_stp:
+// CHECK-NEXT: 37ff8: 50 01 00 b0 adrp x16, #167936
+// CHECK-NEXT: 37ffc: 61 08 00 a9 stp x1, x2, [x3]
+// CHECK-FIX: 38000: 19 60 00 14 b #98404
+// CHECK-NOFIX: 38000: 0d 0a 40 f9 ldr x13, [x16, #16]
+// CHECK-NEXT: 38004: c0 03 5f d6 ret
+ .section .text.12, "ax", %progbits
+ .balign 4096
+ .globl t3_ff8_stp
+ .type t3_ff8_stp, %function
+ .space 4096 - 8
+t3_ff8_stp:
+ adrp x16, dat3
+ stp x1,x2, [x3, #0]
+ ldr x13, [x16, :lo12:dat3]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 39FFC in unpatched output.
+// CHECK: t3_ffc_stnp:
+// CHECK-NEXT: 39ffc: 27 01 00 f0 adrp x7, #159744
+// CHECK-NEXT: 3a000: 61 08 00 a8 stnp x1, x2, [x3]
+// CHECK-FIX: 3a004: 1a 58 00 14 b #90216
+// CHECK-NOFIX: 3a004: e9 00 40 f9 ldr x9, [x7]
+// CHECK-NEXT: 3a008: c0 03 5f d6 ret
+ .section .text.13, "ax", %progbits
+ .balign 4096
+ .globl t3_ffc_stnp
+ .type t3_ffc_stnp, %function
+ .space 4096 - 4
+t3_ffc_stnp:
+ adrp x7, dat1
+ stnp x1,x2, [x3, #0]
+ ldr x9, [x7, :lo12:dat1]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 3BFFC in unpatched output.
+// CHECK: t3_ffc_st1singlepost:
+// CHECK-NEXT: 3bffc: 37 01 00 b0 adrp x23, #151552
+// CHECK-NEXT: 3c000: 20 70 82 4c st1 { v0.16b }, [x1], x2
+// CHECK-FIX: 3c004: 1c 50 00 14 b #82032
+// CHECK-NOFIX: 3c004: f6 06 40 f9 ldr x22, [x23, #8]
+// CHECK-NEXT: 3c008: c0 03 5f d6 ret
+ .section .text.14, "ax", %progbits
+ .balign 4096
+ .globl t3_ffc_st1singlepost
+ .type t3_ffc_st1singlepost, %function
+ .space 4096 - 4
+t3_ffc_st1singlepost:
+ adrp x23, dat2
+ st1 { v0.16b }, [x1], x2
+ ldr x22, [x23, :lo12:dat2]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 3DFF8 in unpatched output.
+// CHECK: t3_ff8_st1multiple:
+// CHECK-NEXT: 3dff8: 17 01 00 f0 adrp x23, #143360
+// CHECK-NEXT: 3dffc: 20 a0 00 4c st1 { v0.16b, v1.16b }, [x1]
+// CHECK-FIX: 3e000: 1f 48 00 14 b #73852
+// CHECK-NOFIX: 3e000: f8 0a 40 f9 ldr x24, [x23, #16]
+// CHECK-NEXT: 3e004: c0 03 5f d6 ret
+ .section .text.15, "ax", %progbits
+ .balign 4096
+ .globl t3_ff8_st1multiple
+ .type t3_ff8_st1muliple, %function
+ .space 4096 - 8
+t3_ff8_st1multiple:
+ adrp x23, dat3
+ st1 { v0.16b, v1.16b }, [x1]
+ ldr x24, [x23, :lo12:dat3]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 3FFF8 in unpatched output.
+// CHECK: t4_ff8_ldr:
+// CHECK-NEXT: 3fff8: 00 01 00 b0 adrp x0, #135168
+// CHECK-NEXT: 3fffc: 21 00 40 f9 ldr x1, [x1]
+// CHECK-NEXT: 40000: 42 00 00 8b add x2, x2, x0
+// CHECK-FIX: 40004: 20 40 00 14 b #65664
+// CHECK-NOFIX: 40004: 02 00 40 f9 ldr x2, [x0]
+// CHECK-NEXT: 40008: c0 03 5f d6 ret
+ .section .text.16, "ax", %progbits
+ .balign 4096
+ .globl t4_ff8_ldr
+ .type t4_ff8_ldr, %function
+ .space 4096 - 8
+t4_ff8_ldr:
+ adrp x0, dat1
+ ldr x1, [x1, #0]
+ add x2, x2, x0
+ ldr x2, [x0, :got_lo12:dat1]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 41FFC in unpatched output.
+// CHECK: t4_ffc_str:
+// CHECK-NEXT: 41ffc: fc 00 00 f0 adrp x28, #126976
+// CHECK-NEXT: 42000: 42 00 00 f9 str x2, [x2]
+// CHECK-NEXT: 42004: 20 00 02 cb sub x0, x1, x2
+// CHECK-FIX: 42008: 21 38 00 14 b #57476
+// CHECK-NOFIX: 42008: 9b 07 00 f9 str x27, [x28, #8]
+// CHECK-NEXT: 4200c: c0 03 5f d6 ret
+ .section .text.17, "ax", %progbits
+ .balign 4096
+ .globl t4_ffc_str
+ .type t4_ffc_str, %function
+ .space 4096 - 4
+t4_ffc_str:
+ adrp x28, dat2
+ str x2, [x2, #0]
+ sub x0, x1, x2
+ str x27, [x28, :got_lo12:dat2]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 43FF8 in unpatched output.
+// CHECK: t4_ff8_stp:
+// CHECK-NEXT: 43ff8: f0 00 00 b0 adrp x16, #118784
+// CHECK-NEXT: 43ffc: 61 08 00 a9 stp x1, x2, [x3]
+// CHECK-NEXT: 44000: 03 7e 10 9b mul x3, x16, x16
+// CHECK-FIX: 44004: 24 30 00 14 b #49296
+// CHECK-NOFIX: 44004: 0e 0a 40 f9 ldr x14, [x16, #16]
+// CHECK-NEXT: 44008: c0 03 5f d6 ret
+ .section .text.18, "ax", %progbits
+ .balign 4096
+ .globl t4_ff8_stp
+ .type t4_ff8_stp, %function
+ .space 4096 - 8
+t4_ff8_stp:
+ adrp x16, dat3
+ stp x1,x2, [x3, #0]
+ mul x3, x16, x16
+ ldr x14, [x16, :got_lo12:dat3]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 45FF8 in unpatched output.
+// CHECK: t4_ff8_stppre:
+// CHECK-NEXT: 45ff8: d0 00 00 f0 adrp x16, #110592
+// CHECK-NEXT: 45ffc: 61 08 81 a9 stp x1, x2, [x3, #16]!
+// CHECK-NEXT: 46000: 03 7e 10 9b mul x3, x16, x16
+// CHECK-FIX: 46004: 26 28 00 14 b #41112
+// CHECK-NOFIX: 46004: 0e 06 40 f9 ldr x14, [x16, #8]
+// CHECK-NEXT: 46008: c0 03 5f d6 ret
+ .section .text.19, "ax", %progbits
+ .balign 4096
+ .globl t4_ff8_stppre
+ .type t4_ff8_stppre, %function
+ .space 4096 - 8
+t4_ff8_stppre:
+ adrp x16, dat1
+ stp x1,x2, [x3, #16]!
+ mul x3, x16, x16
+ ldr x14, [x16, #8]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 47FF8 in unpatched output.
+// CHECK: t4_ff8_stppost:
+// CHECK-NEXT: 47ff8: d0 00 00 b0 adrp x16, #102400
+// CHECK-NEXT: 47ffc: 61 08 81 a8 stp x1, x2, [x3], #16
+// CHECK-NEXT: 48000: 03 7e 10 9b mul x3, x16, x16
+// CHECK-FIX: 48004: 28 20 00 14 b #32928
+// CHECK-NOFIX: 48004: 0e 06 40 f9 ldr x14, [x16, #8]
+// CHECK-NEXT: 48008: c0 03 5f d6 ret
+ .section .text.20, "ax", %progbits
+ .balign 4096
+ .globl t4_ff8_stppost
+ .type t4_ff8_stppost, %function
+ .space 4096 - 8
+t4_ff8_stppost:
+ adrp x16, dat2
+ stp x1,x2, [x3], #16
+ mul x3, x16, x16
+ ldr x14, [x16, #8]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 49FFC in unpatched output.
+// CHECK: t4_ffc_stpsimd:
+// CHECK-NEXT: 49ffc: b0 00 00 f0 adrp x16, #94208
+// CHECK-NEXT: 4a000: 61 08 00 ad stp q1, q2, [x3]
+// CHECK-NEXT: 4a004: 03 7e 10 9b mul x3, x16, x16
+// CHECK-FIX: 4a008: 29 18 00 14 b #24740
+// CHECK-NOFIX: 4a008: 0e 06 40 f9 ldr x14, [x16, #8]
+// CHECK-NEXT: 4a00c: c0 03 5f d6 ret
+ .section .text.21, "ax", %progbits
+ .balign 4096
+ .globl t4_ffc_stpsimd
+ .type t4_ffc_stpsimd, %function
+ .space 4096 - 4
+t4_ffc_stpsimd:
+ adrp x16, dat3
+ stp q1,q2, [x3, #0]
+ mul x3, x16, x16
+ ldr x14, [x16, #8]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 4BFFC in unpatched output.
+// CHECK: t4_ffc_stnp:
+// CHECK-NEXT: 4bffc: a7 00 00 b0 adrp x7, #86016
+// CHECK-NEXT: 4c000: 61 08 00 a8 stnp x1, x2, [x3]
+// CHECK-NEXT: 4c004: 1f 20 03 d5 nop
+// CHECK-FIX: 4c008: 2b 10 00 14 b #16556
+// CHECK-NOFIX: 4c008: ea 00 40 f9 ldr x10, [x7]
+// CHECK-NEXT: 4c00c: c0 03 5f d6 ret
+ .section .text.22, "ax", %progbits
+ .balign 4096
+ .globl t4_ffc_stnp
+ .type t4_ffc_stnp, %function
+ .space 4096 - 4
+t4_ffc_stnp:
+ adrp x7, dat1
+ stnp x1,x2, [x3, #0]
+ nop
+ ldr x10, [x7, :got_lo12:dat1]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 4DFFC in unpatched output.
+// CHECK: t4_ffc_st1:
+// CHECK-NEXT: 4dffc: 98 00 00 f0 adrp x24, #77824
+// CHECK-NEXT: 4e000: 20 70 00 4c st1 { v0.16b }, [x1]
+// CHECK-NEXT: 4e004: f6 06 40 f9 ldr x22, [x23, #8]
+// CHECK-FIX: 4e008: 2d 08 00 14 b #8372
+// CHECK-NOFIX: 4e008: 18 ff 3f f9 str x24, [x24, #32760]
+// CHECK-NEXT: 4e00c: c0 03 5f d6 ret
+ .section .text.23, "ax", %progbits
+ .balign 4096
+ .globl t4_ffc_st1
+ .type t4_ffc_st1, %function
+ .space 4096 - 4
+t4_ffc_st1:
+ adrp x24, dat2
+ st1 { v0.16b }, [x1]
+ ldr x22, [x23, :got_lo12:dat2]
+ str x24, [x24, #32760]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 4FFF8 in unpatched output.
+// CHECK: t3_ff8_ldr_once:
+// CHECK-NEXT: 4fff8: 80 00 00 b0 adrp x0, #69632
+// CHECK-NEXT: 4fffc: 20 70 82 4c st1 { v0.16b }, [x1], x2
+// CHECK-FIX: 50000: 31 00 00 14 b #196
+// CHECK-NOFIX: 50000: 01 08 40 f9 ldr x1, [x0, #16]
+// CHECK-NEXT: 50004: 02 08 40 f9 ldr x2, [x0, #16]
+// CHECK-NEXT: 50008: c0 03 5f d6 ret
+ .section .text.24, "ax", %progbits
+ .balign 4096
+ .globl t3_ff8_ldr_once
+ .type t3_ff8_ldr_once, %function
+ .space 4096 - 8
+t3_ff8_ldr_once:
+ adrp x0, dat3
+ st1 { v0.16b }, [x1], x2
+ ldr x1, [x0, #16]
+ ldr x2, [x0, #16]
+ ret
+
+ .text
+ .globl _start
+ .type _start, %function
+_start:
+ ret
+
+// CHECK-FIX: __CortexA53843419_22000:
+// CHECK-FIX-NEXT: 5000c: 00 00 40 f9 ldr x0, [x0]
+// CHECK-FIX-NEXT: 50010: fd 47 ff 17 b #-188428
+// CHECK-FIX: __CortexA53843419_24000:
+// CHECK-FIX-NEXT: 50014: 02 04 40 f9 ldr x2, [x0, #8]
+// CHECK-FIX-NEXT: 50018: fb 4f ff 17 b #-180244
+// CHECK-FIX: __CortexA53843419_26004:
+// CHECK-FIX-NEXT: 5001c: 03 08 40 f9 ldr x3, [x0, #16]
+// CHECK-FIX-NEXT: 50020: fa 57 ff 17 b #-172056
+// CHECK-FIX: __CortexA53843419_28000:
+// CHECK-FIX-NEXT: 50024: 02 00 40 f9 ldr x2, [x0]
+// CHECK-FIX-NEXT: 50028: f7 5f ff 17 b #-163876
+// CHECK-FIX: __CortexA53843419_2A004:
+// CHECK-FIX-NEXT: 5002c: 9c 07 00 f9 str x28, [x28, #8]
+// CHECK-FIX-NEXT: 50030: f6 67 ff 17 b #-155688
+// CHECK-FIX: __CortexA53843419_2C004:
+// CHECK-FIX-NEXT: 50034: 84 0b 00 f9 str x4, [x28, #16]
+// CHECK-FIX-NEXT: 50038: f4 6f ff 17 b #-147504
+// CHECK-FIX: __CortexA53843419_2E000:
+// CHECK-FIX-NEXT: 5003c: bd 03 40 f9 ldr x29, [x29]
+// CHECK-FIX-NEXT: 50040: f1 77 ff 17 b #-139324
+// CHECK-FIX: __CortexA53843419_30004:
+// CHECK-FIX-NEXT: 50044: bd 07 40 f9 ldr x29, [x29, #8]
+// CHECK-FIX-NEXT: 50048: f0 7f ff 17 b #-131136
+// CHECK-FIX: __CortexA53843419_32004:
+// CHECK-FIX-NEXT: 5004c: 41 0a 40 f9 ldr x1, [x18, #16]
+// CHECK-FIX-NEXT: 50050: ee 87 ff 17 b #-122952
+// CHECK-FIX: __CortexA53843419_34000:
+// CHECK-FIX-NEXT: 50054: 52 02 40 f9 ldr x18, [x18]
+// CHECK-FIX-NEXT: 50058: eb 8f ff 17 b #-114772
+// CHECK-FIX: __CortexA53843419_36004:
+// CHECK-FIX-NEXT: 5005c: ea 05 40 f9 ldr x10, [x15, #8]
+// CHECK-FIX-NEXT: 50060: ea 97 ff 17 b #-106584
+// CHECK-FIX: __CortexA53843419_38000:
+// CHECK-FIX-NEXT: 50064: 0d 0a 40 f9 ldr x13, [x16, #16]
+// CHECK-FIX-NEXT: 50068: e7 9f ff 17 b #-98404
+// CHECK-FIX: __CortexA53843419_3A004:
+// CHECK-FIX-NEXT: 5006c: e9 00 40 f9 ldr x9, [x7]
+// CHECK-FIX-NEXT: 50070: e6 a7 ff 17 b #-90216
+// CHECK-FIX: __CortexA53843419_3C004:
+// CHECK-FIX-NEXT: 50074: f6 06 40 f9 ldr x22, [x23, #8]
+// CHECK-FIX-NEXT: 50078: e4 af ff 17 b #-82032
+// CHECK-FIX: __CortexA53843419_3E000:
+// CHECK-FIX-NEXT: 5007c: f8 0a 40 f9 ldr x24, [x23, #16]
+// CHECK-FIX-NEXT: 50080: e1 b7 ff 17 b #-73852
+// CHECK-FIX: __CortexA53843419_40004:
+// CHECK-FIX-NEXT: 50084: 02 00 40 f9 ldr x2, [x0]
+// CHECK-FIX-NEXT: 50088: e0 bf ff 17 b #-65664
+// CHECK-FIX: __CortexA53843419_42008:
+// CHECK-FIX-NEXT: 5008c: 9b 07 00 f9 str x27, [x28, #8]
+// CHECK-FIX-NEXT: 50090: df c7 ff 17 b #-57476
+// CHECK-FIX: __CortexA53843419_44004:
+// CHECK-FIX-NEXT: 50094: 0e 0a 40 f9 ldr x14, [x16, #16]
+// CHECK-FIX-NEXT: 50098: dc cf ff 17 b #-49296
+// CHECK-FIX: __CortexA53843419_46004:
+// CHECK-FIX-NEXT: 5009c: 0e 06 40 f9 ldr x14, [x16, #8]
+// CHECK-FIX-NEXT: 500a0: da d7 ff 17 b #-41112
+// CHECK-FIX: __CortexA53843419_48004:
+// CHECK-FIX-NEXT: 500a4: 0e 06 40 f9 ldr x14, [x16, #8]
+// CHECK-FIX-NEXT: 500a8: d8 df ff 17 b #-32928
+// CHECK-FIX: __CortexA53843419_4A008:
+// CHECK-FIX-NEXT: 500ac: 0e 06 40 f9 ldr x14, [x16, #8]
+// CHECK-FIX-NEXT: 500b0: d7 e7 ff 17 b #-24740
+// CHECK-FIX: __CortexA53843419_4C008:
+// CHECK-FIX-NEXT: 500b4: ea 00 40 f9 ldr x10, [x7]
+// CHECK-FIX-NEXT: 500b8: d5 ef ff 17 b #-16556
+// CHECK-FIX: __CortexA53843419_4E008:
+// CHECK-FIX-NEXT: 500bc: 18 ff 3f f9 str x24, [x24, #32760]
+// CHECK-FIX-NEXT: 500c0: d3 f7 ff 17 b #-8372
+// CHECK-FIX: __CortexA53843419_50000:
+// CHECK-FIX-NEXT: 500c4: 01 08 40 f9 ldr x1, [x0, #16]
+// CHECK-FIX-NEXT: 500c8: cf ff ff 17 b #-196
+
+ .data
+ .globl dat
+ .globl dat2
+ .globl dat3
+dat1: .quad 1
+dat2: .quad 2
+dat3: .quad 3
diff --git a/test/ELF/aarch64-cortex-a53-843419-thunk.s b/test/ELF/aarch64-cortex-a53-843419-thunk.s
new file mode 100644
index 000000000000..4568095a2fa7
--- /dev/null
+++ b/test/ELF/aarch64-cortex-a53-843419-thunk.s
@@ -0,0 +1,57 @@
+// REQUIRES: aarch64
+// RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux %s -o %t.o
+// RUN: echo "SECTIONS { \
+// RUN: .text1 0x10000 : { *(.text.01) *(.text.02) *(.text.03) } \
+// RUN: .text2 0x8010000 : { *(.text.04) } } " > %t.script
+// RUN: ld.lld --script %t.script -fix-cortex-a53-843419 -verbose %t.o -o %t2 | FileCheck -check-prefix=CHECK-PRINT %s
+// RUN: llvm-objdump -triple=aarch64-linux-gnu -d %t2 | FileCheck %s
+
+// %t2 is 128 Megabytes, so delete it early.
+// RUN: rm %t2
+
+// Test cases for Cortex-A53 Erratum 843419 that involve interactions with
+// range extension thunks. Both erratum fixes and range extension thunks need
+// precise address information and after creation alter address information.
+
+
+ .section .text.01, "ax", %progbits
+ .balign 4096
+ .globl _start
+ .type _start, %function
+_start:
+ bl far_away
+ // Thunk to far_away, size 16-bytes goes here.
+
+ .section .text.02, "ax", %progbits
+ .space 4096 - 28
+
+ // Erratum sequence will only line up at address 0 modulo 0xffc when
+ // Thunk is inserted.
+ .section .text.03, "ax", %progbits
+ .globl t3_ff8_ldr
+ .type t3_ff8_ldr, %function
+t3_ff8_ldr:
+ adrp x0, dat
+ ldr x1, [x1, #0]
+ ldr x0, [x0, :got_lo12:dat]
+ ret
+
+// CHECK-PRINT: detected cortex-a53-843419 erratum sequence starting at 10FFC in unpatched output.
+// CHECK: t3_ff8_ldr:
+// CHECK-NEXT: 10ffc: 00 00 04 90 adrp x0, #134217728
+// CHECK-NEXT: 11000: 21 00 40 f9 ldr x1, [x1]
+// CHECK-NEXT: 11004: 02 00 00 14 b #8
+// CHECK-NEXT: 11008: c0 03 5f d6 ret
+// CHECK: __CortexA53843419_11004:
+// CHECK-NEXT: 1100c: 00 08 40 f9 ldr x0, [x0, #16]
+// CHECK-NEXT: 11010: fe ff ff 17 b #-8
+
+ .section .text.04, "ax", %progbits
+ .globl far_away
+ .type far_away, function
+far_away:
+ ret
+
+ .section .data
+ .globl dat
+dat: .quad 0
diff --git a/test/ELF/aarch64-gnu-ifunc-plt.s b/test/ELF/aarch64-gnu-ifunc-plt.s
index be9a8a7e674a..5138675676d3 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 46f4a292d2ea..4e0dc328025d 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 fec1ad6a1d22..1882dcd43a48 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 13ee09a892e9..a7745b05904d 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-jump26-error.s b/test/ELF/aarch64-jump26-error.s
deleted file mode 100644
index 81fbba1f4551..000000000000
--- a/test/ELF/aarch64-jump26-error.s
+++ /dev/null
@@ -1,11 +0,0 @@
-// RUN: llvm-mc -filetype=obj -triple=aarch64-pc-freebsd %S/Inputs/abs.s -o %tabs
-// RUN: llvm-mc -filetype=obj -triple=aarch64-pc-freebsd %s -o %t
-// RUN: not ld.lld %t %tabs -o %t2 2>&1 | FileCheck %s
-// REQUIRES: aarch64
-
-.text
-.globl _start
-_start:
- b big
-
-// CHECK: R_AARCH64_JUMP26 out of range
diff --git a/test/ELF/aarch64-jump26-thunk.s b/test/ELF/aarch64-jump26-thunk.s
new file mode 100644
index 000000000000..088ab3a9e1a5
--- /dev/null
+++ b/test/ELF/aarch64-jump26-thunk.s
@@ -0,0 +1,20 @@
+// RUN: llvm-mc -filetype=obj -triple=aarch64-pc-freebsd %S/Inputs/abs.s -o %tabs
+// RUN: llvm-mc -filetype=obj -triple=aarch64-pc-freebsd %s -o %t
+// RUN: ld.lld %t %tabs -o %t2 2>&1
+// RUN: llvm-objdump -d -triple=aarch64-pc-freebsd %t2 | FileCheck %s
+// REQUIRES: aarch64
+
+.text
+.globl _start
+_start:
+ b big
+
+// CHECK: Disassembly of section .text:
+// CHECK-NEXT: _start:
+// CHECK-NEXT: 20000: 02 00 00 14 b #8
+// CHECK: __AArch64AbsLongThunk_big:
+// CHECK-NEXT: 20008: 50 00 00 58 ldr x16, #8
+// CHECK-NEXT: 2000c: 00 02 1f d6 br x16
+// CHECK: $d:
+// CHECK-NEXT: 20010: 00 00 00 00 .word 0x00000000
+// CHECK-NEXT: 20014: 10 00 00 00 .word 0x00000010
diff --git a/test/ELF/aarch64-ldprel-lo19-invalid.s b/test/ELF/aarch64-ldprel-lo19-invalid.s
new file mode 100644
index 000000000000..04df32e05904
--- /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: 2065536 is not in [-1048576, 1048575]
+
+ 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 000000000000..2b30022658e6
--- /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: 0x30001 is not aligned to 2 bytes
+// CHECK-NEXT: improper alignment for relocation R_AARCH64_LDST32_ABS_LO12_NC: 0x30002 is not aligned to 4 bytes
+// CHECK-NEXT: improper alignment for relocation R_AARCH64_LDST64_ABS_LO12_NC: 0x30004 is not aligned to 8 bytes
+// CHECK-NEXT: improper alignment for relocation R_AARCH64_LDST128_ABS_LO12_NC: 0x30008 is not aligned to 16 bytes
diff --git a/test/ELF/aarch64-load-alignment.s b/test/ELF/aarch64-load-alignment.s
new file mode 100644
index 000000000000..7b1129b7afa5
--- /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: 0x10005 is not aligned to 4 bytes
+
+ ldr x8, patatino
+ .data
+ .zero 5
+patatino:
diff --git a/test/ELF/aarch64-prel16.s b/test/ELF/aarch64-prel16.s
index 4ae1f87e2081..fc34f010853c 100644
--- a/test/ELF/aarch64-prel16.s
+++ b/test/ELF/aarch64-prel16.s
@@ -28,4 +28,4 @@ _start:
// | FileCheck %s --check-prefix=OVERFLOW
// RUN: not ld.lld %t.o %t257.o -o %t2
// | FileCheck %s --check-prefix=OVERFLOW
-// OVERFLOW: Relocation R_AARCH64_PREL16 out of range
+// OVERFLOW: Relocation R_AARCH64_PREL16 out of range: -94209 is not in [-32768, 65535]
diff --git a/test/ELF/aarch64-prel32.s b/test/ELF/aarch64-prel32.s
index 302f4521f46b..7aa290382c53 100644
--- a/test/ELF/aarch64-prel32.s
+++ b/test/ELF/aarch64-prel32.s
@@ -28,4 +28,4 @@ _start:
// | FileCheck %s --check-prefix=OVERFLOW
// RUN: not ld.lld %t.o %t257.o -o %t2
// | FileCheck %s --check-prefix=OVERFLOW
-// OVERFLOW: Relocation R_AARCH64_PREL32 out of range
+// OVERFLOW: Relocation R_AARCH64_PREL32 out of range: 18446744071562006527 is not in [-2147483648, 4294967295]
diff --git a/test/ELF/aarch64-thunk-pi.s b/test/ELF/aarch64-thunk-pi.s
new file mode 100644
index 000000000000..91e2b7f0f3cd
--- /dev/null
+++ b/test/ELF/aarch64-thunk-pi.s
@@ -0,0 +1,91 @@
+// RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %s -o %t
+// RUN: echo "SECTIONS { \
+// RUN: .text_low : { *(.text_low) } \
+// RUN: .text_high 0x10000000 : { *(.text_high) } \
+// RUN: } " > %t.script
+// RUN: ld.lld --script %t.script --shared %t -o %t2 2>&1
+// RUN: llvm-objdump -d -triple=aarch64-linux-gnu %t2 | FileCheck %s
+// REQUIRES: aarch64
+
+// Check that Position Independent thunks are generated for shared libraries.
+ .section .text_low, "ax", %progbits
+ .globl low_target
+ .type low_target, %function
+low_target:
+ // Need thunk to high_target@plt
+ bl high_target
+ ret
+// CHECK: low_target:
+// CHECK-NEXT: 0: 04 00 00 94 bl #16
+// CHECK-NEXT: 4: c0 03 5f d6 ret
+
+ .hidden low_target2
+ .globl low_target2
+ .type low_target2, %function
+low_target2:
+ // Need thunk to high_target
+ bl high_target2
+ ret
+// CHECK: low_target2:
+// CHECK-NEXT: 8: 05 00 00 94 bl #20
+// CHECK-NEXT: c: c0 03 5f d6 ret
+
+// Expect range extension thunks for .text_low
+// adrp calculation is (PC + signed immediate) & (!0xfff)
+// CHECK: __AArch64ADRPThunk_high_target:
+// CHECK-NEXT: 10: 10 00 08 90 adrp x16, #268435456
+// CHECK-NEXT: 14: 10 82 04 91 add x16, x16, #288
+// CHECK-NEXT: 18: 00 02 1f d6 br x16
+// CHECK: __AArch64ADRPThunk_high_target2:
+// CHECK-NEXT: 1c: 10 00 08 90 adrp x16, #268435456
+// CHECK-NEXT: 20: 10 22 00 91 add x16, x16, #8
+// CHECK-NEXT: 24: 00 02 1f d6 br x16
+
+
+ .section .text_high, "ax", %progbits
+ .globl high_target
+ .type high_target, %function
+high_target:
+ // No thunk needed as we can reach low_target@plt
+ bl low_target
+ ret
+// CHECK: high_target:
+// CHECK-NEXT: 10000000: 4c 00 00 94 bl #304
+// CHECK-NEXT: 10000004: c0 03 5f d6 ret
+
+ .hidden high_target2
+ .globl high_target2
+ .type high_target2, %function
+high_target2:
+ // Need thunk to low_target
+ bl low_target2
+ ret
+// CHECK: high_target2:
+// CHECK-NEXT: 10000008: 02 00 00 94 bl #8
+// CHECK-NEXT: 1000000c: c0 03 5f d6 ret
+
+// Expect Thunk for .text.high
+
+// CHECK: __AArch64ADRPThunk_low_target2:
+// CHECK-NEXT: 10000010: 10 00 f8 90 adrp x16, #-268435456
+// CHECK-NEXT: 10000014: 10 22 00 91 add x16, x16, #8
+// CHECK-NEXT: 10000018: 00 02 1f d6 br x16
+
+// CHECK: Disassembly of section .plt:
+// CHECK-NEXT: .plt:
+// CHECK-NEXT: 10000100: f0 7b bf a9 stp x16, x30, [sp, #-16]!
+// CHECK-NEXT: 10000104: 10 00 00 90 adrp x16, #0
+// CHECK-NEXT: 10000108: 11 aa 40 f9 ldr x17, [x16, #336]
+// CHECK-NEXT: 1000010c: 10 42 05 91 add x16, x16, #336
+// CHECK-NEXT: 10000110: 20 02 1f d6 br x17
+// CHECK-NEXT: 10000114: 1f 20 03 d5 nop
+// CHECK-NEXT: 10000118: 1f 20 03 d5 nop
+// CHECK-NEXT: 1000011c: 1f 20 03 d5 nop
+// CHECK-NEXT: 10000120: 10 00 00 90 adrp x16, #0
+// CHECK-NEXT: 10000124: 11 ae 40 f9 ldr x17, [x16, #344]
+// CHECK-NEXT: 10000128: 10 62 05 91 add x16, x16, #344
+// CHECK-NEXT: 1000012c: 20 02 1f d6 br x17
+// CHECK-NEXT: 10000130: 10 00 00 90 adrp x16, #0
+// CHECK-NEXT: 10000134: 11 b2 40 f9 ldr x17, [x16, #352]
+// CHECK-NEXT: 10000138: 10 82 05 91 add x16, x16, #352
+// CHECK-NEXT: 1000013c: 20 02 1f d6 br x17
diff --git a/test/ELF/aarch64-thunk-script.s b/test/ELF/aarch64-thunk-script.s
new file mode 100644
index 000000000000..ebfaf72de5f4
--- /dev/null
+++ b/test/ELF/aarch64-thunk-script.s
@@ -0,0 +1,41 @@
+// RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %s -o %t
+// RUN: echo "SECTIONS { \
+// RUN: .text_low 0x2000: { *(.text_low) } \
+// RUN: .text_high 0x8002000 : { *(.text_high) } \
+// RUN: } " > %t.script
+// RUN: ld.lld --script %t.script %t -o %t2 2>&1
+// RUN: llvm-objdump -d -triple=aarch64-linux-gnu %t2 | FileCheck %s
+// REQUIRES: aarch64
+
+// Check that we have the out of branch range calculation right. The immediate
+// field is signed so we have a slightly higher negative displacement.
+ .section .text_low, "ax", %progbits
+ .globl _start
+ .type _start, %function
+_start:
+ // Need thunk to high_target@plt
+ bl high_target
+ ret
+
+ .section .text_high, "ax", %progbits
+ .globl high_target
+ .type high_target, %function
+high_target:
+ // No Thunk needed as we are within signed immediate range
+ bl _start
+ ret
+
+// CHECK: Disassembly of section .text_low:
+// CHECK-NEXT: _start:
+// CHECK-NEXT: 2000: 02 00 00 94 bl #8
+// CHECK-NEXT: 2004: c0 03 5f d6 ret
+// CHECK: __AArch64AbsLongThunk_high_target:
+// CHECK-NEXT: 2008: 50 00 00 58 ldr x16, #8
+// CHECK-NEXT: 200c: 00 02 1f d6 br x16
+// CHECK: $d:
+// CHECK-NEXT: 2010: 00 20 00 08 .word 0x08002000
+// CHECK-NEXT: 2014: 00 00 00 00 .word 0x00000000
+// CHECK: Disassembly of section .text_high:
+// CHECK-NEXT: high_target:
+// CHECK-NEXT: 8002000: 00 00 00 96 bl #-134217728
+// CHECK-NEXT: 8002004: c0 03 5f d6 ret
diff --git a/test/ELF/aarch64-thunk-section-location.s b/test/ELF/aarch64-thunk-section-location.s
new file mode 100644
index 000000000000..bf70b7c365ba
--- /dev/null
+++ b/test/ELF/aarch64-thunk-section-location.s
@@ -0,0 +1,41 @@
+// RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %s -o %t
+// RUN: ld.lld %t -o %t2 2>&1
+// RUN: llvm-objdump -d -start-address=134086664 -stop-address=134086676 -triple=aarch64-linux-gnu %t2 | FileCheck %s
+// REQUIRES: aarch64
+// Check that the range extension thunks are dumped close to the aarch64 branch
+// range of 128 MiB
+ .section .text.1, "ax", %progbits
+ .balign 0x1000
+ .globl _start
+_start:
+ bl high_target
+ ret
+
+ .section .text.2, "ax", %progbits
+ .space 0x2000000
+
+ .section .text.2, "ax", %progbits
+ .space 0x2000000
+
+ .section .text.3, "ax", %progbits
+ .space 0x2000000
+
+ .section .text.4, "ax", %progbits
+ .space 0x2000000 - 0x40000
+
+ .section .text.5, "ax", %progbits
+ .space 0x40000
+
+ .section .text.6, "ax", %progbits
+ .balign 0x1000
+
+ .globl high_target
+ .type high_target, %function
+high_target:
+ ret
+
+// CHECK: __AArch64AbsLongThunk_high_target:
+// CHECK-NEXT: 7fe0008: 50 00 00 58 ldr x16, #8
+// CHECK-NEXT: 7fe000c: 00 02 1f d6 br x16
+// CHECK: $d:
+// CHECK-NEXT: 7fe0010: 00 10 02 08 .word 0x08021000
diff --git a/test/ELF/aarch64-tls-gdie.s b/test/ELF/aarch64-tls-gdie.s
index c66ea6cfcadb..ab7461ac2973 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 81ca326aff54..8b7431093a26 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 24306d5d7ee3..309a2e9f56ca 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 09dfd04d8ac9..b7c2e65a164f 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 1c21213643ac..35f50417497e 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 5fad4cf6c40d..82d19cdc015a 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 e4637e1a5367..c54438d9f1e0 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/amdgpu-elf-flags-err.s b/test/ELF/amdgpu-elf-flags-err.s
new file mode 100644
index 000000000000..4c295b5b92e4
--- /dev/null
+++ b/test/ELF/amdgpu-elf-flags-err.s
@@ -0,0 +1,7 @@
+# RUN: llvm-mc -triple amdgcn-amd-amdhsa -mcpu=gfx803 -filetype=obj %S/Inputs/amdgpu-kernel-0.s -o %t-0.o
+# RUN: llvm-mc -triple amdgcn-amd-amdhsa -mcpu=gfx803 -filetype=obj %S/Inputs/amdgpu-kernel-1.s -o %t-1.o
+# RUN: not ld.lld -shared %t-0.o %t-1.o %S/Inputs/amdgpu-kernel-2.o -o %t.so 2>&1 | FileCheck %s
+
+# REQUIRES: amdgpu
+
+# CHECK: error: incompatible e_flags: {{.*}}amdgpu-kernel-2.o
diff --git a/test/ELF/amdgpu-elf-flags.s b/test/ELF/amdgpu-elf-flags.s
new file mode 100644
index 000000000000..85f891a98364
--- /dev/null
+++ b/test/ELF/amdgpu-elf-flags.s
@@ -0,0 +1,10 @@
+# RUN: llvm-mc -triple amdgcn-amd-amdhsa -mcpu=gfx803 -filetype=obj %S/Inputs/amdgpu-kernel-0.s -o %t-0.o
+# RUN: llvm-mc -triple amdgcn-amd-amdhsa -mcpu=gfx803 -filetype=obj %S/Inputs/amdgpu-kernel-1.s -o %t-1.o
+# RUN: ld.lld -shared %t-0.o %t-1.o -o %t.so
+# RUN: llvm-readobj -file-headers %t.so | FileCheck %s
+
+# REQUIRES: amdgpu
+
+# CHECK: Flags [ (0x2)
+# CHECK: EF_AMDGPU_ARCH_GCN (0x2)
+# CHECK: ]
diff --git a/test/ELF/amdgpu-relocs.s b/test/ELF/amdgpu-relocs.s
index 1adb1faf20fd..8b5a61ed21f4 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-bl-v6.s b/test/ELF/arm-bl-v6.s
new file mode 100644
index 000000000000..6317aa433d6c
--- /dev/null
+++ b/test/ELF/arm-bl-v6.s
@@ -0,0 +1,51 @@
+// RUN: llvm-mc -filetype=obj -triple=arm-none-linux-gnueabi %s -o %t
+// RUN: ld.lld %t -o %t2 2>&1 | FileCheck %s
+// REQUIRES: arm
+
+// On Arm v6 the range of a Thumb BL instruction is only 4 megabytes as the
+// extended range encoding is not supported. The following example has a Thumb
+// BL that is out of range on ARM v6 and requires a range extension thunk.
+// As v6 does not support MOVT or MOVW instructions the Thunk must not
+// use these instructions either. At present we don't support v6 so we give a
+// warning for unsupported features.
+
+// CHECK: warning: lld uses extended branch encoding, no object with architecture supporting feature detected.
+// CHECK-NEXT: warning: lld may use movt/movw, no object with architecture supporting feature detected.
+// ARM v6 supports blx so we shouldn't see the blx not supported warning.
+// CHECK-NOT: warning: lld uses blx instruction, no object with architecture supporting feature detected.
+ .text
+ .syntax unified
+ .cpu arm1176jzf-s
+ .eabi_attribute 6, 6 @ Tag_CPU_arch
+ .globl _start
+ .type _start,%function
+ .balign 0x1000
+_start:
+ bl thumbfunc
+ bx lr
+
+ .thumb
+ .section .text.2, "ax", %progbits
+ .globl thumbfunc
+ .type thumbfunc,%function
+thumbfunc:
+ bl farthumbfunc
+
+// 6 Megabytes, enough to make farthumbfunc out of range of caller on a v6
+// Arm, but not on a v7 Arm.
+ .section .text.3, "ax", %progbits
+ .space 0x200000
+
+ .section .text.4, "ax", %progbits
+ .space 0x200000
+
+ .section .text.5, "ax", %progbits
+ .space 0x200000
+
+ .thumb
+ .section .text.6, "ax", %progbits
+ .balign 0x1000
+ .globl farthumbfunc
+ .type farthumbfunc,%function
+farthumbfunc:
+ bx lr
diff --git a/test/ELF/arm-blx-v4t.s b/test/ELF/arm-blx-v4t.s
new file mode 100644
index 000000000000..858b93fd5891
--- /dev/null
+++ b/test/ELF/arm-blx-v4t.s
@@ -0,0 +1,30 @@
+// RUN: llvm-mc -filetype=obj -triple=arm-none-linux-gnueabi %s -o %t
+// RUN: ld.lld %t -o %t2 2>&1 | FileCheck %s
+// REQUIRES: arm
+
+// On Arm v4t there is no blx instruction so all interworking must go via
+// a thunk. At present we don't support v4t so we give a warning for unsupported
+// features.
+
+// CHECK: warning: lld uses blx instruction, no object with architecture supporting feature detected.
+// CHECK-NEXT: warning: lld uses extended branch encoding, no object with architecture supporting feature detected.
+// CHECK-NEXT: warning: lld may use movt/movw, no object with architecture supporting feature detected.
+
+ .text
+ .syntax unified
+ .cpu arm7tdmi
+ .eabi_attribute 6, 2 @ Tag_CPU_arch
+ .arm
+ .globl _start
+ .type _start,%function
+ .p2align 2
+_start:
+ bl thumbfunc
+ bx lr
+
+ .thumb
+ .section .text.2, "ax", %progbits
+ .globl thumbfunc
+ .type thumbfunc,%function
+thumbfunc:
+ bx lr
diff --git a/test/ELF/arm-branch-error.s b/test/ELF/arm-branch-error.s
deleted file mode 100644
index f1a855d7373f..000000000000
--- a/test/ELF/arm-branch-error.s
+++ /dev/null
@@ -1,19 +0,0 @@
-// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
-// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %S/Inputs/far-arm-abs.s -o %tfar
-// RUN: not ld.lld %t %tfar -o %t2 2>&1 | FileCheck %s
-// REQUIRES: arm
- .syntax unified
- .section .text, "ax",%progbits
- .globl _start
- .balign 0x10000
- .type _start,%function
-_start:
- // address of too_far symbols are just out of range of ARM branch with
- // 26-bit immediate field and an addend of -8
- bl too_far1
- b too_far2
- beq too_far3
-
-// CHECK: R_ARM_CALL out of range
-// CHECK-NEXT: R_ARM_JUMP24 out of range
-// CHECK-NEXT: R_ARM_JUMP24 out of range
diff --git a/test/ELF/arm-branch-rangethunk.s b/test/ELF/arm-branch-rangethunk.s
new file mode 100644
index 000000000000..c61ec899adae
--- /dev/null
+++ b/test/ELF/arm-branch-rangethunk.s
@@ -0,0 +1,34 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %S/Inputs/far-arm-abs.s -o %tfar
+// RUN: ld.lld %t %tfar -o %t2 2>&1
+// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t2 | FileCheck %s
+// REQUIRES: arm
+ .syntax unified
+ .section .text, "ax",%progbits
+ .globl _start
+ .balign 0x10000
+ .type _start,%function
+_start:
+ // address of too_far symbols are just out of range of ARM branch with
+ // 26-bit immediate field and an addend of -8
+ bl too_far1
+ b too_far2
+ beq too_far3
+
+// CHECK: Disassembly of section .text:
+// CHECK-NEXT: _start:
+// CHECK-NEXT: 20000: 01 00 00 eb bl #4 <__ARMv7ABSLongThunk_too_far1>
+// CHECK-NEXT: 20004: 03 00 00 ea b #12 <__ARMv7ABSLongThunk_too_far2>
+// CHECK-NEXT: 20008: 05 00 00 0a beq #20 <__ARMv7ABSLongThunk_too_far3>
+// CHECK: __ARMv7ABSLongThunk_too_far1:
+// CHECK-NEXT: 2000c: 08 c0 00 e3 movw r12, #8
+// CHECK-NEXT: 20010: 02 c2 40 e3 movt r12, #514
+// CHECK-NEXT: 20014: 1c ff 2f e1 bx r12
+// CHECK: __ARMv7ABSLongThunk_too_far2:
+// CHECK-NEXT: 20018: 0c c0 00 e3 movw r12, #12
+// CHECK-NEXT: 2001c: 02 c2 40 e3 movt r12, #514
+// CHECK-NEXT: 20020: 1c ff 2f e1 bx r12
+// CHECK: __ARMv7ABSLongThunk_too_far3:
+// CHECK-NEXT: 20024: 10 c0 00 e3 movw r12, #16
+// CHECK-NEXT: 20028: 02 c2 40 e3 movt r12, #514
+// CHECK-NEXT: 2002c: 1c ff 2f e1 bx r12
diff --git a/test/ELF/arm-branch-undef-weak-plt-thunk.s b/test/ELF/arm-branch-undef-weak-plt-thunk.s
new file mode 100644
index 000000000000..f95da0dcec21
--- /dev/null
+++ b/test/ELF/arm-branch-undef-weak-plt-thunk.s
@@ -0,0 +1,35 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %S/Inputs/arm-shared.s -o %t
+// RUN: ld.lld %t --shared -o %t.so
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t2
+// RUN: ld.lld %t2 %t.so -o %t3
+// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi -start-address=69632 -stop-address=69664 %t3 | FileCheck %s
+// REQUIRES: arm
+
+// When we are dynamic linking, undefined weak references have a PLT entry so
+// we must create a thunk for the branch to the PLT entry.
+
+ .text
+ .globl bar2
+ .weak undefined_weak_we_expect_a_plt_entry_for
+_start:
+ .globl _start
+ .type _start, %function
+ b undefined_weak_we_expect_a_plt_entry_for
+ bl bar2
+// Create 32 Mb gap between the call to the weak reference and the PLT so that
+// the b and bl need a range-extension thunk.
+ .section .text.1, "ax", %progbits
+ .space 32 * 1024 * 1024
+
+// CHECK: Disassembly of section .text:
+// CHECK-NEXT: _start:
+// CHECK-NEXT: 11000: 00 00 00 ea b #0 <__ARMv7ABSLongThunk_undefined_weak_we_expect_a_plt_entry_for>
+// CHECK-NEXT: 11004: 02 00 00 eb bl #8 <__ARMv7ABSLongThunk_bar2>
+// CHECK: __ARMv7ABSLongThunk_undefined_weak_we_expect_a_plt_entry_for:
+// CHECK-NEXT: 11008: 40 c0 01 e3 movw r12, #4160
+// CHECK-NEXT: 1100c: 01 c2 40 e3 movt r12, #513
+// CHECK-NEXT: 11010: 1c ff 2f e1 bx r12
+// CHECK: __ARMv7ABSLongThunk_bar2:
+// CHECK-NEXT: 11014: 50 c0 01 e3 movw r12, #4176
+// CHECK-NEXT: 11018: 01 c2 40 e3 movt r12, #513
+// CHECK-NEXT: 1101c: 1c ff 2f e1 bx r12
diff --git a/test/ELF/arm-copy.s b/test/ELF/arm-copy.s
index e5ce1577babd..dc9e3628de4f 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-dedup.s b/test/ELF/arm-exidx-dedup.s
new file mode 100644
index 000000000000..1648f77152e9
--- /dev/null
+++ b/test/ELF/arm-exidx-dedup.s
@@ -0,0 +1,126 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
+// RUN: ld.lld %t --no-merge-exidx-entries -o %t2
+// RUN: llvm-objdump -s %t2 | FileCheck --check-prefix CHECK-DUPS %s
+// RUN: ld.lld %t -o %t3
+// RUN: llvm-objdump -s %t3 | FileCheck %s
+// REQUIRES: arm
+// Test that lld can at least remove duplicate .ARM.exidx sections. A more
+// fine grained implementation will be able to remove duplicate entries within
+// a .ARM.exidx section.
+
+// With duplicate entries
+// CHECK-DUPS: Contents of section .ARM.exidx:
+// CHECK-DUPS-NEXT: 100d4 2c0f0000 01000000 280f0000 01000000
+// CHECK-DUPS-NEXT: 100e4 240f0000 01000000 200f0000 01000000
+// CHECK-DUPS-NEXT: 100f4 1c0f0000 08849780 180f0000 08849780
+// CHECK-DUPS-NEXT: 10104 140f0000 08849780 100f0000 14000000
+// CHECK-DUPS-NEXT: 10114 0c0f0000 18000000 080f0000 01000000
+// CHECK-DUPS-NEXT: Contents of section .ARM.extab:
+
+// After duplicate entry removal
+// CHECK: Contents of section .ARM.exidx:
+// CHECK-NEXT: 100d4 2c0f0000 01000000 340f0000 08849780
+// CHECK-NEXT: 100e4 380f0000 14000000 340f0000 18000000
+// CHECK-NEXT: 100f4 300f0000 01000000
+// CHECK-NEXT: Contents of section .ARM.extab:
+ .syntax unified
+
+ // Expect 1 EXIDX_CANTUNWIND entry.
+ .section .text.00, "ax", %progbits
+ .globl _start
+_start:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+
+ // Expect .ARM.exidx.text.01 to be identical to .ARM.exidx.text.00
+ .section .text.01, "ax", %progbits
+ .globl f1
+f1:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+
+ // Expect 2 EXIDX_CANTUNWIND entries, these can be duplicated into
+ // .ARM.exid.text.00
+ .section .text.02, "ax", %progbits
+ .globl f2
+f2:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+
+ .globl f3
+f3:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+
+ // Expect inline unwind instructions, not a duplicate of previous entry.
+ .section .text.03, "ax", %progbits
+ .global f4
+f4:
+ .fnstart
+ bx lr
+ .save {r7, lr}
+ .setfp r7, sp, #0
+ .fnend
+
+ // Expect 2 inline unwind entries that are a duplicate of
+ // .ARM.exidx.text.03
+ .section .text.04, "ax", %progbits
+ .global f5
+f5:
+ .fnstart
+ bx lr
+ .save {r7, lr}
+ .setfp r7, sp, #0
+ .fnend
+
+ .global f6
+f6:
+ .fnstart
+ bx lr
+ .save {r7, lr}
+ .setfp r7, sp, #0
+ .fnend
+
+ // Expect a section with a reference to an .ARM.extab. Not a duplicate
+ // of previous inline table entry.
+ .section .text.05, "ax",%progbits
+ .global f7
+f7:
+ .fnstart
+ bx lr
+ .personality __gxx_personality_v0
+ .handlerdata
+ .long 0
+ .fnend
+
+ // Expect a reference to an identical .ARM.extab. We do not try to
+ // deduplicate references to .ARM.extab sections.
+ .section .text.06, "ax",%progbits
+ .global f8
+f8:
+ .fnstart
+ bx lr
+ .personality __gxx_personality_v0
+ .handlerdata
+ .long 0
+ .fnend
+
+ // Dummy implementation of personality routines to satisfy reference from
+ // exception tables
+ .section .text.__gcc_personality_v0, "ax", %progbits
+ .global __gxx_personality_v0
+__gxx_personality_v0:
+ bx lr
+
+ .section .text.__aeabi_unwind_cpp_pr0, "ax", %progbits
+ .global __aeabi_unwind_cpp_pr0
+__aeabi_unwind_cpp_pr0:
+ bx lr
diff --git a/test/ELF/arm-exidx-gc.s b/test/ELF/arm-exidx-gc.s
index 1336c256f7c1..34bd9dbe37b2 100644
--- a/test/ELF/arm-exidx-gc.s
+++ b/test/ELF/arm-exidx-gc.s
@@ -1,5 +1,5 @@
// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
-// RUN: ld.lld %t -o %t2 --gc-sections 2>&1
+// RUN: ld.lld %t --no-merge-exidx-entries -o %t2 --gc-sections 2>&1
// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t2 | FileCheck %s
// RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi %t2 | FileCheck -check-prefix=CHECK-EXIDX %s
// REQUIRES: arm
diff --git a/test/ELF/arm-exidx-order.s b/test/ELF/arm-exidx-order.s
index 951c71a4c33e..c988ad8a2cfe 100644
--- a/test/ELF/arm-exidx-order.s
+++ b/test/ELF/arm-exidx-order.s
@@ -1,6 +1,6 @@
// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %S/Inputs/arm-exidx-cantunwind.s -o %tcantunwind
-// RUN: ld.lld %t %tcantunwind -o %t2 2>&1
+// RUN: ld.lld --no-merge-exidx-entries %t %tcantunwind -o %t2 2>&1
// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t2 | FileCheck %s
// RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi %t2 | FileCheck -check-prefix=CHECK-EXIDX %s
// RUN: llvm-readobj --program-headers --sections %t2 | FileCheck -check-prefix=CHECK-PT %s
@@ -8,7 +8,7 @@
// RUN: echo "SECTIONS { \
// RUN: .text 0x11000 : { *(.text*) } \
// RUN: .ARM.exidx : { *(.ARM.exidx) } } " > %t.script
-// RUN: ld.lld --script %t.script %tcantunwind %t -o %t3 2>&1
+// RUN: ld.lld --no-merge-exidx-entries --script %t.script %tcantunwind %t -o %t3 2>&1
// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t3 | FileCheck -check-prefix=CHECK-SCRIPT %s
// RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi %t3 | FileCheck -check-prefix=CHECK-SCRIPT-EXIDX %s
// REQUIRES: arm
diff --git a/test/ELF/arm-exidx-sentinel-orphan.s b/test/ELF/arm-exidx-sentinel-orphan.s
index c054fe940db6..0e68c245dd10 100644
--- a/test/ELF/arm-exidx-sentinel-orphan.s
+++ b/test/ELF/arm-exidx-sentinel-orphan.s
@@ -4,7 +4,7 @@
// RUN: echo "SECTIONS { \
// RUN: .text 0x11000 : { *(.text*) } \
// RUN: } " > %t.script
-// RUN: ld.lld --script %t.script %t -o %t2
+// RUN: ld.lld --no-merge-exidx-entries --script %t.script %t -o %t2
// RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi %t2 | FileCheck %s
// REQUIRES: arm
diff --git a/test/ELF/arm-exidx-shared.s b/test/ELF/arm-exidx-shared.s
index e06733352a37..bf7c2dc383e7 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
@@ -41,5 +41,5 @@ __aeabi_unwind_cpp_pr0:
// CHECK-NEXT: 0x200C R_ARM_JUMP_SLOT __gxx_personality_v0
// CHECK-EXTAB: Contents of section .ARM.extab:
-// 014c + 0ed8 = 0x1024 = __gxx_personality_v0(PLT)
-// CHECK-EXTAB-NEXT: 014c d80e0000 b0b0b000 00000000
+// 014c + 0ee4 = 0x1030 = __gxx_personality_v0(PLT)
+// CHECK-EXTAB-NEXT: 014c e40e0000 b0b0b000 00000000
diff --git a/test/ELF/arm-gnu-ifunc-plt.s b/test/ELF/arm-gnu-ifunc-plt.s
index efcaee1e9889..2ff2ec0a143d 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
@@ -33,49 +33,49 @@
// DISASM-NEXT: 11000: 1e ff 2f e1 bx lr
// DISASM: bar:
// DISASM-NEXT: 11004: 1e ff 2f e1 bx lr
-// DISASM: _start:
+// DISASM: _start:
// DISASM-NEXT: 11008: 14 00 00 eb bl #80
// DISASM-NEXT: 1100c: 17 00 00 eb bl #92
-// DISASM: 11010: 00 00 00 00 .word 0x00000000
+// DISASM: $d.1:
+// DISASM-NEXT: 11010: 00 00 00 00 .word 0x00000000
// DISASM-NEXT: 11014: 04 00 00 00 .word 0x00000004
-// DISASM: 11018: 05 00 00 eb bl #20
-// DISASM-NEXT: 1101c: 08 00 00 eb bl #32
+// DISASM: 11018: 08 00 00 eb bl #32
+// DISASM-NEXT: 1101c: 0b 00 00 eb bl #44
// DISASM-NEXT: Disassembly of section .plt:
// DISASM-NEXT: $a:
// DISASM-NEXT: 11020: 04 e0 2d e5 str lr, [sp, #-4]!
-// DISASM-NEXT: 11024: 04 e0 9f e5 ldr lr, [pc, #4]
-// DISASM-NEXT: 11028: 0e e0 8f e0 add lr, pc, lr
-// DISASM-NEXT: 1102c: 08 f0 be e5 ldr pc, [lr, #8]!
+// DISASM-NEXT: 11024: 00 e6 8f e2 add lr, pc, #0, #12
+// DISASM-NEXT: 11028: 00 ea 8e e2 add lr, lr, #0, #20
+// DISASM-NEXT: 1102c: dc ff be e5 ldr pc, [lr, #4060]!
// DISASM: $d:
-// DISASM-NEXT: 11030: d0 0f 00 00 .word 0x00000fd0
+// DISASM-NEXT: 11030: d4 d4 d4 d4 .word 0xd4d4d4d4
+// DISASM-NEXT: 11034: d4 d4 d4 d4 .word 0xd4d4d4d4
+// DISASM-NEXT: 11038: d4 d4 d4 d4 .word 0xd4d4d4d4
+// DISASM-NEXT: 1103c: d4 d4 d4 d4 .word 0xd4d4d4d4
// DISASM: $a:
-// DISASM-NEXT: 11034: 04 c0 9f e5 ldr r12, [pc, #4]
-// DISASM-NEXT: 11038: 0f c0 8c e0 add r12, r12, pc
-// DISASM-NEXT: 1103c: 00 f0 9c e5 ldr pc, [r12]
+// DISASM-NEXT: 11040: 00 c6 8f e2 add r12, pc, #0, #12
+// DISASM-NEXT: 11044: 00 ca 8c e2 add r12, r12, #0, #20
+// DISASM-NEXT: 11048: c4 ff bc e5 ldr pc, [r12, #4036]!
// DISASM: $d:
-// DISASM-NEXT: 11040: cc 0f 00 00 .word 0x00000fcc
+// DISASM-NEXT: 1104c: d4 d4 d4 d4 .word 0xd4d4d4d4
// DISASM: $a:
-// DISASM-NEXT: 11044: 04 c0 9f e5 ldr r12, [pc, #4]
-// DISASM-NEXT: 11048: 0f c0 8c e0 add r12, r12, pc
-// DISASM-NEXT: 1104c: 00 f0 9c e5 ldr pc, [r12]
+// DISASM-NEXT: 11050: 00 c6 8f e2 add r12, pc, #0, #12
+// DISASM-NEXT: 11054: 00 ca 8c e2 add r12, r12, #0, #20
+// DISASM-NEXT: 11058: b8 ff bc e5 ldr pc, [r12, #4024]!
// 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: 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
-// DISASM-NEXT: 11068: 00 f0 9c e5 ldr pc, [r12]
+// DISASM-NEXT: 11060: 00 c6 8f e2 add r12, pc, #0, #12
+// DISASM-NEXT: 11064: 02 ca 8c e2 add r12, r12, #8192
+// DISASM-NEXT: 11068: 18 f0 bc e5 ldr pc, [r12, #24]!
// DISASM: $d:
-// DISASM-NEXT: 1106c: 14 20 00 00 .word 0x00002014
+// DISASM-NEXT: 1106c: d4 d4 d4 d4 .word 0xd4d4d4d4
// DISASM: $a:
-// DISASM-NEXT: 11070: 04 c0 9f e5 ldr r12, [pc, #4]
-// DISASM-NEXT: 11074: 0f c0 8c e0 add r12, r12, pc
-// DISASM-NEXT: 11078: 00 f0 9c e5 ldr pc, [r12]
+// DISASM-NEXT: 11070: 00 c6 8f e2 add r12, pc, #0, #12
+// DISASM-NEXT: 11074: 02 ca 8c e2 add r12, r12, #8192
+// DISASM-NEXT: 11078: 0c f0 bc e5 ldr pc, [r12, #12]!
// DISASM: $d:
-// DISASM-NEXT: 1107c: 08 20 00 00 .word 0x00002008
+// DISASM-NEXT: 1107c: d4 d4 d4 d4 .word 0xd4d4d4d4
.syntax unified
.text
diff --git a/test/ELF/arm-gnu-ifunc.s b/test/ELF/arm-gnu-ifunc.s
index 23c540310b3c..799b8b17f62b 100644
--- a/test/ELF/arm-gnu-ifunc.s
+++ b/test/ELF/arm-gnu-ifunc.s
@@ -111,30 +111,29 @@ _start:
// DISASM: Disassembly of section .text:
// DISASM-NEXT: foo:
-// DISASM-NEXT: 11000: 1e ff 2f e1 bx lr
-// DISASM: bar:
-// DISASM-NEXT: 11004: 1e ff 2f e1 bx lr
-// DISASM: _start:
-// DISASM-NEXT: 11008: 04 00 00 eb bl #16
-// DISASM-NEXT: 1100c: 07 00 00 eb bl #28
+// DISASM-NEXT: 11000: 1e ff 2f e1 bx lr
+// DISASM: bar:
+// DISASM-NEXT: 11004: 1e ff 2f e1 bx lr
+// DISASM: _start:
+// DISASM-NEXT: 11008: 04 00 00 eb bl #16
+// DISASM-NEXT: 1100c: 07 00 00 eb bl #28
// 1 * 65536 + 244 = 0x100f4 __rel_iplt_start
-// DISASM-NEXT: 11010: f4 00 00 e3 movw r0, #244
-// DISASM-NEXT: 11014: 01 00 40 e3 movt r0, #1
+// DISASM-NEXT: 11010: f4 00 00 e3 movw r0, #244
+// DISASM-NEXT: 11014: 01 00 40 e3 movt r0, #1
// 1 * 65536 + 260 = 0x10104 __rel_iplt_end
-// DISASM-NEXT: 11018: 04 01 00 e3 movw r0, #260
-// DISASM-NEXT: 1101c: 01 00 40 e3 movt r0, #1
+// DISASM-NEXT: 11018: 04 01 00 e3 movw r0, #260
+// DISASM-NEXT: 1101c: 01 00 40 e3 movt r0, #1
// DISASM-NEXT: Disassembly of section .plt:
-// DISASM: $a:
-// DISASM-NEXT: 11020: 04 c0 9f e5 ldr r12, [pc, #4]
-// DISASM-NEXT: 11024: 0f c0 8c e0 add r12, r12, pc
-// 11024 + 8 + fd4 = 0x12000
-// DISASM-NEXT: 11028: 00 f0 9c e5 ldr pc, [r12]
+// DISASM-NEXT: $a:
+// DISASM-NEXT: 11020: 00 c6 8f e2 add r12, pc, #0, #12
+// DISASM-NEXT: 11024: 00 ca 8c e2 add r12, r12, #0, #20
+// DISASM-NEXT: 11028: d8 ff bc e5 ldr pc, [r12, #4056]!
// DISASM: $d:
-// DISASM-NEXT: 1102c: d4 0f 00 00 .word 0x00000fd4
+// DISASM-NEXT: 1102c: d4 d4 d4 d4 .word 0xd4d4d4d4
// DISASM: $a:
-// DISASM-NEXT: 11030: 04 c0 9f e5 ldr r12, [pc, #4]
-// DISASM-NEXT: 11034: 0f c0 8c e0 add r12, r12, pc
-// 11034 + 8 + fc8 = 0x12004
-// DISASM-NEXT: 11038: 00 f0 9c e5 ldr pc, [r12]
+// DISASM-NEXT: 11030: 00 c6 8f e2 add r12, pc, #0, #12
+// DISASM-NEXT: 11034: 00 ca 8c e2 add r12, r12, #0, #20
+// DISASM-NEXT: 11038: cc ff bc e5 ldr pc, [r12, #4044]!
// DISASM: $d:
-// DISASM-NEXT: 1103c: c8 0f 00 00 .word 0x00000fc8
+// DISASM-NEXT: 1103c: d4 d4 d4 d4 .word 0xd4d4d4d4
+
diff --git a/test/ELF/arm-got-relative.s b/test/ELF/arm-got-relative.s
index 46a3ca97d080..cf8b0a64a966 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
diff --git a/test/ELF/arm-icf-exidx.s b/test/ELF/arm-icf-exidx.s
index 6af30285db67..629505cb7ffa 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-pie-relative.s b/test/ELF/arm-pie-relative.s
index f965c24f4fa6..f225015eb5f3 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 1588f745f22c..f8166d60ffcf 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
@@ -32,51 +32,55 @@ _start:
// CHECK-NEXT: 11014: fb ff ff 0a beq #-20 <func3>
// Expect PLT entries as symbols can be preempted
+// The .got.plt and .plt displacement is small so we can use small PLT entries.
// DSO: Disassembly of section .text:
// DSO-NEXT: func1:
-// DSO-NEXT: 1000: 1e ff 2f e1 bx lr
+// DSO-NEXT: 1000: 1e ff 2f e1 bx lr
// DSO: func2:
-// DSO-NEXT: 1004: 1e ff 2f e1 bx lr
+// DSO-NEXT: 1004: 1e ff 2f e1 bx lr
// DSO: func3:
-// DSO-NEXT: 1008: 1e ff 2f e1 bx lr
+// DSO-NEXT: 1008: 1e ff 2f e1 bx lr
// DSO: _start:
-// S(0x1034) - P(0x100c) + A(-8) = 0x20 = 32
-// DSO-NEXT: 100c: 08 00 00 ea b #32
-// S(0x1044) - P(0x1010) + A(-8) = 0x2c = 44
-// DSO-NEXT: 1010: 0b 00 00 eb bl #44
-// S(0x1054) - P(0x1014) + A(-8) = 0x38 = 56
-// DSO-NEXT: 1014: 0e 00 00 0a beq #56
-
-// DSO: Disassembly of section .plt:
+// S(0x1040) - P(0x100c) + A(-8) = 0x2c = 32
+// DSO-NEXT: 100c: 0b 00 00 ea b #44
+// S(0x1050) - P(0x1010) + A(-8) = 0x38 = 56
+// DSO-NEXT: 1010: 0e 00 00 eb bl #56
+// S(0x10160) - P(0x1014) + A(-8) = 0x44 = 68
+// DSO-NEXT: 1014: 11 00 00 0a beq #68
+// DSO-NEXT: Disassembly of section .plt:
// DSO-NEXT: $a:
// DSO-NEXT: 1020: 04 e0 2d e5 str lr, [sp, #-4]!
-// DSO-NEXT: 1024: 04 e0 9f e5 ldr lr, [pc, #4]
-// DSO-NEXT: 1028: 0e e0 8f e0 add lr, pc, lr
-// DSO-NEXT: 102c: 08 f0 be e5 ldr pc, [lr, #8]!
-// 0x1028 + 8 + 0fd0 = 0x2000
+// (0x1024 + 8) + (0 RoR 12) + (0 RoR 20) + (0xfdc) = 0x2008 = .got.plt[3]
+// DSO-NEXT: 1024: 00 e6 8f e2 add lr, pc, #0, #12
+// DSO-NEXT: 1028: 00 ea 8e e2 add lr, lr, #0, #20
+// DSO-NEXT: 102c: dc ff be e5 ldr pc, [lr, #4060]!
// DSO: $d:
-// DSO-NEXT: 1030: d0 0f 00 00 .word 0x00000fd0
+// DSO-NEXT: 1030: d4 d4 d4 d4 .word 0xd4d4d4d4
+// DSO-NEXT: 1034: d4 d4 d4 d4 .word 0xd4d4d4d4
+// DSO-NEXT: 1038: d4 d4 d4 d4 .word 0xd4d4d4d4
+// DSO-NEXT: 103c: d4 d4 d4 d4 .word 0xd4d4d4d4
// DSO: $a:
-// DSO-NEXT: 1034: 04 c0 9f e5 ldr r12, [pc, #4]
-// DSO-NEXT: 1038: 0f c0 8c e0 add r12, r12, pc
-// DSO-NEXT: 103c: 00 f0 9c e5 ldr pc, [r12]
-// 0x1038 + 8 + 0fcc = 0x200c
+// (0x1040 + 8) + (0 RoR 12) + (0 RoR 20) + (0xfc4) = 0x200c
+// DSO-NEXT: 1040: 00 c6 8f e2 add r12, pc, #0, #12
+// DSO-NEXT: 1044: 00 ca 8c e2 add r12, r12, #0, #20
+// DSO-NEXT: 1048: c4 ff bc e5 ldr pc, [r12, #4036]!
// DSO: $d:
-// DSO-NEXT: 1040: cc 0f 00 00 .word 0x00000fcc
+// DSO-NEXT: 104c: d4 d4 d4 d4 .word 0xd4d4d4d4
// DSO: $a:
-// DSO-NEXT: 1044: 04 c0 9f e5 ldr r12, [pc, #4]
-// DSO-NEXT: 1048: 0f c0 8c e0 add r12, r12, pc
-// DSO-NEXT: 104c: 00 f0 9c e5 ldr pc, [r12]
-// 0x1048 + 8 + 0fc0 = 0x2010
+// (0x1050 + 8) + (0 RoR 12) + (0 RoR 20) + (0xfb8) = 0x2010
+// DSO-NEXT: 1050: 00 c6 8f e2 add r12, pc, #0, #12
+// DSO-NEXT: 1054: 00 ca 8c e2 add r12, r12, #0, #20
+// DSO-NEXT: 1058: b8 ff bc e5 ldr pc, [r12, #4024]!
// DSO: $d:
-// DSO-NEXT: 1050: c0 0f 00 00 .word 0x00000fc0
+// DSO-NEXT: 105c: d4 d4 d4 d4 .word 0xd4d4d4d4
// DSO: $a:
-// DSO-NEXT: 1054: 04 c0 9f e5 ldr r12, [pc, #4]
-// DSO-NEXT: 1058: 0f c0 8c e0 add r12, r12, pc
-// DSO-NEXT: 105c: 00 f0 9c e5 ldr pc, [r12]
-// 0x1058 + 8 + 0fb4 = 0x2014
+// (0x1060 + 8) + (0 RoR 12) + (0 RoR 20) + (0xfac) = 0x2014
+// DSO-NEXT: 1060: 00 c6 8f e2 add r12, pc, #0, #12
+// DSO-NEXT: 1064: 00 ca 8c e2 add r12, r12, #0, #20
+// DSO-NEXT: 1068: ac ff bc e5 ldr pc, [r12, #4012]!
// DSO: $d:
-// DSO-NEXT: 1060: b4 0f 00 00 .word 0x00000fb4
+// DSO-NEXT: 106c: d4 d4 d4 d4 .word 0xd4d4d4d4
+
// DSOREL: Name: .got.plt
// DSOREL-NEXT: Type: SHT_PROGBITS
@@ -96,3 +100,199 @@ _start:
// DSOREL-NEXT: 0x200C R_ARM_JUMP_SLOT func1 0x0
// DSOREL-NEXT: 0x2010 R_ARM_JUMP_SLOT func2 0x0
// DSOREL-NEXT: 0x2014 R_ARM_JUMP_SLOT func3 0x0
+
+// Test a large separation between the .plt and .got.plt
+// The .got.plt and .plt displacement is large but still within the range
+// of the short plt sequence.
+// RUN: echo "SECTIONS { \
+// RUN: .text 0x1000 : { *(.text) } \
+// RUN: .plt 0x2000 : { *(.plt) *(.plt.*) } \
+// RUN: .got.plt 0x1100000 : { *(.got.plt) } \
+// RUN: }" > %t.script
+// RUN: ld.lld --hash-style=sysv --script %t.script -shared %t1 %t2 -o %t4
+// RUN: llvm-objdump -triple=armv7a-none-linux-gnueabi -d %t4 | FileCheck --check-prefix=CHECKHIGH %s
+// RUN: llvm-readobj -s -r %t4 | FileCheck --check-prefix=DSORELHIGH %s
+
+// CHECKHIGH: Disassembly of section .text:
+// CHECKHIGH-NEXT: func1:
+// CHECKHIGH-NEXT: 1000: 1e ff 2f e1 bx lr
+// CHECKHIGH: func2:
+// CHECKHIGH-NEXT: 1004: 1e ff 2f e1 bx lr
+// CHECKHIGH: func3:
+// CHECKHIGH-NEXT: 1008: 1e ff 2f e1 bx lr
+// CHECKHIGH: _start:
+// CHECKHIGH-NEXT: 100c: 03 04 00 ea b #4108 <$a>
+// CHECKHIGH-NEXT: 1010: 06 04 00 eb bl #4120 <$a>
+// CHECKHIGH-NEXT: 1014: 09 04 00 0a beq #4132 <$a>
+// CHECKHIGH-NEXT: Disassembly of section .plt:
+// CHECKHIGH-NEXT: $a:
+// CHECKHIGH-NEXT: 2000: 04 e0 2d e5 str lr, [sp, #-4]!
+// CHECKHIGH-NEXT: 2004: 10 e6 8f e2 add lr, pc, #16, #12
+// CHECKHIGH-NEXT: 2008: fd ea 8e e2 add lr, lr, #1036288
+// CHECKHIGH-NEXT: 200c: fc ff be e5 ldr pc, [lr, #4092]!
+// CHECKHIGH: $d:
+// CHECKHIGH-NEXT: 2010: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECKHIGH-NEXT: 2014: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECKHIGH-NEXT: 2018: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECKHIGH-NEXT: 201c: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECKHIGH: $a:
+// CHECKHIGH-NEXT: 2020: 10 c6 8f e2 add r12, pc, #16, #12
+// CHECKHIGH-NEXT: 2024: fd ca 8c e2 add r12, r12, #1036288
+// CHECKHIGH-NEXT: 2028: e4 ff bc e5 ldr pc, [r12, #4068]!
+// CHECKHIGH: $d:
+// CHECKHIGH-NEXT: 202c: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECKHIGH: $a:
+// CHECKHIGH-NEXT: 2030: 10 c6 8f e2 add r12, pc, #16, #12
+// CHECKHIGH-NEXT: 2034: fd ca 8c e2 add r12, r12, #1036288
+// CHECKHIGH-NEXT: 2038: d8 ff bc e5 ldr pc, [r12, #4056]!
+// CHECKHIGH: $d:
+// CHECKHIGH-NEXT: 203c: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECKHIGH: $a:
+// CHECKHIGH-NEXT: 2040: 10 c6 8f e2 add r12, pc, #16, #12
+// CHECKHIGH-NEXT: 2044: fd ca 8c e2 add r12, r12, #1036288
+// CHECKHIGH-NEXT: 2048: cc ff bc e5 ldr pc, [r12, #4044]!
+// CHECKHIGH: $d:
+// CHECKHIGH-NEXT: 204c: d4 d4 d4 d4 .word 0xd4d4d4d4
+
+// DSORELHIGH: Name: .got.plt
+// DSORELHIGH-NEXT: Type: SHT_PROGBITS
+// DSORELHIGH-NEXT: Flags [
+// DSORELHIGH-NEXT: SHF_ALLOC
+// DSORELHIGH-NEXT: SHF_WRITE
+// DSORELHIGH-NEXT: ]
+// DSORELHIGH-NEXT: Address: 0x1100000
+// DSORELHIGH: Relocations [
+// DSORELHIGH-NEXT: Section (6) .rel.plt {
+// DSORELHIGH-NEXT: 0x110000C R_ARM_JUMP_SLOT func1 0x0
+// DSORELHIGH-NEXT: 0x1100010 R_ARM_JUMP_SLOT func2 0x0
+// DSORELHIGH-NEXT: 0x1100014 R_ARM_JUMP_SLOT func3 0x0
+
+// Test a very large separation between the .plt and .got.plt so we must use
+// large plt entries that do not have any range restriction.
+// RUN: echo "SECTIONS { \
+// RUN: .text 0x1000 : { *(.text) } \
+// RUN: .plt 0x2000 : { *(.plt) *(.plt.*) } \
+// RUN: .got.plt 0x11111100 : { *(.got.plt) } \
+// RUN: }" > %t2.script
+// RUN: ld.lld --hash-style=sysv --script %t2.script -shared %t1 %t2 -o %t5
+// RUN: llvm-objdump -triple=armv7a-none-linux-gnueabi -d %t5 | FileCheck --check-prefix=CHECKLONG %s
+// RUN: llvm-readobj -s -r %t5 | FileCheck --check-prefix=DSORELLONG %s
+
+// CHECKLONG: Disassembly of section .text:
+// CHECKLONG-NEXT: func1:
+// CHECKLONG-NEXT: 1000: 1e ff 2f e1 bx lr
+// CHECKLONG: func2:
+// CHECKLONG-NEXT: 1004: 1e ff 2f e1 bx lr
+// CHECKLONG: func3:
+// CHECKLONG-NEXT: 1008: 1e ff 2f e1 bx lr
+// CHECKLONG: _start:
+// CHECKLONG-NEXT: 100c: 03 04 00 ea b #4108 <$a>
+// CHECKLONG-NEXT: 1010: 06 04 00 eb bl #4120 <$a>
+// CHECKLONG-NEXT: 1014: 09 04 00 0a beq #4132 <$a>
+// CHECKLONG-NEXT: Disassembly of section .plt:
+// CHECKLONG-NEXT: $a:
+// CHECKLONG-NEXT: 2000: 04 e0 2d e5 str lr, [sp, #-4]!
+// CHECKLONG-NEXT: 2004: 04 e0 9f e5 ldr lr, [pc, #4]
+// CHECKLONG-NEXT: 2008: 0e e0 8f e0 add lr, pc, lr
+// CHECKLONG-NEXT: 200c: 08 f0 be e5 ldr pc, [lr, #8]!
+// CHECKLONG: $d:
+// CHECKLONG-NEXT: 2010: f0 f0 10 11 .word 0x1110f0f0
+// CHECKLONG-NEXT: 2014: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECKLONG-NEXT: 2018: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECKLONG-NEXT: 201c: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECKLONG: $a:
+// CHECKLONG-NEXT: 2020: 04 c0 9f e5 ldr r12, [pc, #4]
+// CHECKLONG-NEXT: 2024: 0f c0 8c e0 add r12, r12, pc
+// CHECKLONG-NEXT: 2028: 00 f0 9c e5 ldr pc, [r12]
+// CHECKLONG: $d:
+// CHECKLONG-NEXT: 202c: e0 f0 10 11 .word 0x1110f0e0
+// CHECKLONG: $a:
+// CHECKLONG-NEXT: 2030: 04 c0 9f e5 ldr r12, [pc, #4]
+// CHECKLONG-NEXT: 2034: 0f c0 8c e0 add r12, r12, pc
+// CHECKLONG-NEXT: 2038: 00 f0 9c e5 ldr pc, [r12]
+// CHECKLONG: $d:
+// CHECKLONG-NEXT: 203c: d4 f0 10 11 .word 0x1110f0d4
+// CHECKLONG: $a:
+// CHECKLONG-NEXT: 2040: 04 c0 9f e5 ldr r12, [pc, #4]
+// CHECKLONG-NEXT: 2044: 0f c0 8c e0 add r12, r12, pc
+// CHECKLONG-NEXT: 2048: 00 f0 9c e5 ldr pc, [r12]
+// CHECKLONG: $d:
+// CHECKLONG-NEXT: 204c: c8 f0 10 11 .word 0x1110f0c8
+
+// DSORELLONG: Name: .got.plt
+// DSORELLONG-NEXT: Type: SHT_PROGBITS
+// DSORELLONG-NEXT: Flags [
+// DSORELLONG-NEXT: SHF_ALLOC
+// DSORELLONG-NEXT: SHF_WRITE
+// DSORELLONG-NEXT: ]
+// DSORELLONG-NEXT: Address: 0x11111100
+// DSORELLONG: Relocations [
+// DSORELLONG-NEXT: Section (6) .rel.plt {
+// DSORELLONG-NEXT: 0x1111110C R_ARM_JUMP_SLOT func1 0x0
+// DSORELLONG-NEXT: 0x11111110 R_ARM_JUMP_SLOT func2 0x0
+// DSORELLONG-NEXT: 0x11111114 R_ARM_JUMP_SLOT func3 0x0
+
+// Test a separation between the .plt and .got.plt that is part in range of
+// short table entries and part needing long entries. We use the long entries
+// only when we need to.
+// RUN: echo "SECTIONS { \
+// RUN: .text 0x1000 : { *(.text) } \
+// RUN: .plt 0x2000 : { *(.plt) *(.plt.*) } \
+// RUN: .got.plt 0x8002020 : { *(.got.plt) } \
+// RUN: }" > %t3.script
+// RUN: ld.lld --hash-style=sysv --script %t3.script -shared %t1 %t2 -o %t6
+// RUN: llvm-objdump -triple=armv7a-none-linux-gnueabi -d %t6 | FileCheck --check-prefix=CHECKMIX %s
+// RUN: llvm-readobj -s -r %t6 | FileCheck --check-prefix=DSORELMIX %s
+
+// CHECKMIX: Disassembly of section .text:
+// CHECKMIX-NEXT: func1:
+// CHECKMIX-NEXT: 1000: 1e ff 2f e1 bx lr
+// CHECKMIX: func2:
+// CHECKMIX-NEXT: 1004: 1e ff 2f e1 bx lr
+// CHECKMIX: func3:
+// CHECKMIX-NEXT: 1008: 1e ff 2f e1 bx lr
+// CHECKMIX: _start:
+// CHECKMIX-NEXT: 100c: 03 04 00 ea b #4108 <$a>
+// CHECKMIX-NEXT: 1010: 06 04 00 eb bl #4120 <$a>
+// CHECKMIX-NEXT: 1014: 09 04 00 0a beq #4132 <$a>
+// CHECKMIX-NEXT: Disassembly of section .plt:
+// CHECKMIX-NEXT: $a:
+// CHECKMIX-NEXT: 2000: 04 e0 2d e5 str lr, [sp, #-4]!
+// CHECKMIX-NEXT: 2004: 04 e0 9f e5 ldr lr, [pc, #4]
+// CHECKMIX-NEXT: 2008: 0e e0 8f e0 add lr, pc, lr
+// CHECKMIX-NEXT: 200c: 08 f0 be e5 ldr pc, [lr, #8]!
+// CHECKMIX: $d:
+// CHECKMIX-NEXT: 2010: 10 00 00 08 .word 0x08000010
+// CHECKMIX-NEXT: 2014: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECKMIX-NEXT: 2018: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECKMIX-NEXT: 201c: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECKMIX: $a:
+// CHECKMIX-NEXT: 2020: 04 c0 9f e5 ldr r12, [pc, #4]
+// CHECKMIX-NEXT: 2024: 0f c0 8c e0 add r12, r12, pc
+// CHECKMIX-NEXT: 2028: 00 f0 9c e5 ldr pc, [r12]
+// CHECKMIX: $d:
+// CHECKMIX-NEXT: 202c: 00 00 00 08 .word 0x08000000
+// CHECKMIX: $a:
+// CHECKMIX-NEXT: 2030: 7f c6 8f e2 add r12, pc, #133169152
+// CHECKMIX-NEXT: 2034: ff ca 8c e2 add r12, r12, #1044480
+// CHECKMIX-NEXT: 2038: f8 ff bc e5 ldr pc, [r12, #4088]!
+// CHECKMIX: $d:
+// CHECKMIX-NEXT: 203c: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECKMIX: $a:
+// CHECKMIX-NEXT: 2040: 7f c6 8f e2 add r12, pc, #133169152
+// CHECKMIX-NEXT: 2044: ff ca 8c e2 add r12, r12, #1044480
+// CHECKMIX-NEXT: 2048: ec ff bc e5 ldr pc, [r12, #4076]!
+// CHECKMIX: $d:
+// CHECKMIX-NEXT: 204c: d4 d4 d4 d4 .word 0xd4d4d4d4
+
+// DSORELMIX: Name: .got.plt
+// DSORELMIX-NEXT: Type: SHT_PROGBITS
+// DSORELMIX-NEXT: Flags [
+// DSORELMIX-NEXT: SHF_ALLOC
+// DSORELMIX-NEXT: SHF_WRITE
+// DSORELMIX-NEXT: ]
+// DSORELMIX-NEXT: Address: 0x8002020
+// DSORELMIX: Section (6) .rel.plt {
+// DSORELMIX-NEXT: 0x800202C R_ARM_JUMP_SLOT func1 0x0
+// DSORELMIX-NEXT: 0x8002030 R_ARM_JUMP_SLOT func2 0x0
+// DSORELMIX-NEXT: 0x8002034 R_ARM_JUMP_SLOT func3 0x0
diff --git a/test/ELF/arm-static-defines.s b/test/ELF/arm-static-defines.s
index 0012841fb32c..815c20ca9451 100644
--- a/test/ELF/arm-static-defines.s
+++ b/test/ELF/arm-static-defines.s
@@ -1,5 +1,5 @@
// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
-// RUN: ld.lld %t --static -o %t2 2>&1
+// RUN: ld.lld --no-merge-exidx-entries %t --static -o %t2 2>&1
// RUN: llvm-readobj --symbols %t2 | FileCheck %s
// REQUIRES: arm
diff --git a/test/ELF/arm-thumb-branch-error.s b/test/ELF/arm-thumb-branch-error.s
deleted file mode 100644
index de6c1bc16c96..000000000000
--- a/test/ELF/arm-thumb-branch-error.s
+++ /dev/null
@@ -1,19 +0,0 @@
-// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %s -o %t
-// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %S/Inputs/far-arm-thumb-abs.s -o %tfar
-// RUN: not ld.lld %t %tfar -o %t2 2>&1 | FileCheck %s
-// REQUIRES: arm
- .syntax unified
- .section .text, "ax",%progbits
- .globl _start
- .balign 0x10000
- .type _start,%function
-_start:
- // address of too_far symbols are just out of range of ARM branch with
- // 26-bit immediate field and an addend of -8
- bl too_far1
- b too_far2
- beq.w too_far3
-
-// CHECK: R_ARM_THM_CALL out of range
-// CHECK-NEXT: R_ARM_THM_JUMP24 out of range
-// CHECK-NEXT: R_ARM_THM_JUMP19 out of range
diff --git a/test/ELF/arm-thumb-branch-rangethunk.s b/test/ELF/arm-thumb-branch-rangethunk.s
new file mode 100644
index 000000000000..f83e64144d70
--- /dev/null
+++ b/test/ELF/arm-thumb-branch-rangethunk.s
@@ -0,0 +1,36 @@
+// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %s -o %t
+// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %S/Inputs/far-arm-thumb-abs.s -o %tfar
+// RUN: ld.lld %t %tfar -o %t2 2>&1
+// RUN: llvm-objdump -d -triple=thumbv7a-none-linux-gnueabi %t2
+// REQUIRES: arm
+ .syntax unified
+ .thumb
+ .section .text, "ax",%progbits
+ .globl _start
+ .balign 0x10000
+ .type _start,%function
+_start:
+ // address of too_far symbols are just out of range of ARM branch with
+ // 26-bit immediate field and an addend of -8
+ bl too_far1
+ b too_far2
+ beq.w too_far3
+
+// CHECK: Disassembly of section .text:
+// CHECK-NEXT: _start:
+// CHECK-NEXT: 20000: 00 f0 04 f8 bl #8
+// CHECK-NEXT: 20004: 00 f0 07 b8 b.w #14 <__Thumbv7ABSLongThunk_too_far2>
+// CHECK-NEXT: 20008: 00 f0 0a 80 beq.w #20 <__Thumbv7ABSLongThunk_too_far3>
+// CHECK: __Thumbv7ABSLongThunk_too_far1:
+// CHECK-NEXT: 2000c: 40 f2 05 0c movw r12, #5
+// CHECK-NEXT: 20010: c0 f2 02 1c movt r12, #258
+// CHECK-NEXT: 20014: 60 47 bx r12
+// CHECK: __Thumbv7ABSLongThunk_too_far2:
+// CHECK-NEXT: 20016: 40 f2 09 0c movw r12, #9
+// CHECK-NEXT: 2001a: c0 f2 02 1c movt r12, #258
+// CHECK-NEXT: 2001e: 60 47 bx r12
+// CHECK: __Thumbv7ABSLongThunk_too_far3:
+// CHECK-NEXT: 20020: 40 f2 0d 0c movw r12, #13
+// CHECK-NEXT: 20024: c0 f2 12 0c movt r12, #18
+// CHECK-NEXT: 20028: 60 47 bx r12
+// CHECK-NEXT: 2002a: 00 00 movs r0, r0
diff --git a/test/ELF/arm-thumb-condbranch-thunk.s b/test/ELF/arm-thumb-condbranch-thunk.s
new file mode 100644
index 000000000000..c527e5df297c
--- /dev/null
+++ b/test/ELF/arm-thumb-condbranch-thunk.s
@@ -0,0 +1,117 @@
+// REQUIRES: arm
+// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %s -o %t
+// RUN: ld.lld %t -o %t2 2>&1
+// The output file is large, most of it zeroes. We dissassemble only the
+// parts we need to speed up the test and avoid a large output file
+// RUN: llvm-objdump -d %t2 -start-address=524288 -stop-address=524316 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK1 %s
+// RUN: llvm-objdump -d %t2 -start-address=1048576 -stop-address=1048584 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK2 %s
+// RUN: llvm-objdump -d %t2 -start-address=1572864 -stop-address=1572872 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK3 %s
+// RUN: llvm-objdump -d %t2 -start-address=5242884 -stop-address=5242894 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK4 %s
+// RUN: llvm-objdump -d %t2 -start-address=5767168 -stop-address=5767174 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK5 %s
+// RUN: llvm-objdump -d %t2 -start-address=16777220 -stop-address=16777240 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK6 %s
+// RUN: llvm-objdump -d %t2 -start-address=17825792 -stop-address=17825798 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK7 %s
+// Test Range extension Thunks for the Thumb conditional branch instruction.
+// This instruction only has a range of 1Mb whereas all the other Thumb wide
+// Branch instructions have 16Mb range. We still place our pre-created Thunk
+// Sections at 16Mb intervals as conditional branches to a target defined
+// in a different section are rare.
+ .syntax unified
+// Define a function aligned on a half megabyte boundary
+ .macro FUNCTION suff
+ .section .text.\suff\(), "ax", %progbits
+ .thumb
+ .balign 0x80000
+ .globl tfunc\suff\()
+ .type tfunc\suff\(), %function
+tfunc\suff\():
+ bx lr
+ .endm
+
+ .globl _start
+_start:
+ FUNCTION 00
+// Long Range Thunk needed for 16Mb range branch, can reach pre-created Thunk
+// Section
+ bl tfunc33
+// CHECK1: Disassembly of section .text:
+// CHECK1-NEXT: tfunc00:
+// CHECK1-NEXT: 80000: 70 47 bx lr
+// CHECK1-NEXT: 80002: 7f f3 ff d7 bl #16252926
+// CHECK1: __Thumbv7ABSLongThunk_tfunc05:
+// CHECK1-NEXT: 80008: 40 f2 01 0c movw r12, #1
+// CHECK1-NEXT: 8000c: c0 f2 30 0c movt r12, #48
+// CHECK1-NEXT: 80010: 60 47 bx r12
+// CHECK1: __Thumbv7ABSLongThunk_tfunc00:
+// CHECK1-NEXT: 80012: 40 f2 01 0c movw r12, #1
+// CHECK1-NEXT: 80016: c0 f2 08 0c movt r12, #8
+// CHECK1-NEXT: 8001a: 60 47 bx r12
+ FUNCTION 01
+// tfunc02 is within range of tfunc02
+ beq.w tfunc02
+// tfunc05 is out of range, and we can't reach the pre-created Thunk Section
+// create a new one.
+ bne.w tfunc05
+// CHECK2: tfunc01:
+// CHECK2-NEXT: 100000: 70 47 bx lr
+// CHECK2-NEXT: 100002: 3f f0 fd a7 beq.w #524282 <tfunc02>
+// CHECK2-NEXT: 100006: 7f f4 ff a7 bne.w #-524290 <__Thumbv7ABSLongThunk_tfunc05>
+ FUNCTION 02
+// We can reach the Thunk Section created for bne.w tfunc05
+ bne.w tfunc05
+ beq.w tfunc00
+// CHECK3: 180000: 70 47 bx lr
+// CHECK3-NEXT: 180002: 40 f4 01 80 bne.w #-1048574 <__Thumbv7ABSLongThunk_tfunc05>
+// CHECK3-NEXT: 180006: 00 f4 04 80 beq.w #-1048568 <__Thumbv7ABSLongThunk_tfunc00>
+ FUNCTION 03
+ FUNCTION 04
+ FUNCTION 05
+ FUNCTION 06
+ FUNCTION 07
+ FUNCTION 08
+ FUNCTION 09
+// CHECK4: __Thumbv7ABSLongThunk_tfunc03:
+// CHECK4-NEXT: 500004: 40 f2 01 0c movw r12, #1
+// CHECK4-NEXT: 500008: c0 f2 20 0c movt r12, #32
+// CHECK4-NEXT: 50000c: 60 47 bx r12
+ FUNCTION 10
+// We can't reach any Thunk Section, create a new one
+ beq.w tfunc03
+// CHECK5: tfunc10:
+// CHECK5-NEXT: 580000: 70 47 bx lr
+// CHECK5-NEXT: 580002: 3f f4 ff a7 beq.w #-524290 <__Thumbv7ABSLongThunk_tfunc03>
+ FUNCTION 11
+ FUNCTION 12
+ FUNCTION 13
+ FUNCTION 14
+ FUNCTION 15
+ FUNCTION 16
+ FUNCTION 17
+ FUNCTION 18
+ FUNCTION 19
+ FUNCTION 20
+ FUNCTION 21
+ FUNCTION 22
+ FUNCTION 23
+ FUNCTION 24
+ FUNCTION 25
+ FUNCTION 26
+ FUNCTION 27
+ FUNCTION 28
+ FUNCTION 29
+ FUNCTION 30
+ FUNCTION 31
+// CHECK6: __Thumbv7ABSLongThunk_tfunc33:
+// CHECK6-NEXT: 1000004: 40 f2 01 0c movw r12, #1
+// CHECK6-NEXT: 1000008: c0 f2 10 1c movt r12, #272
+// CHECK6-NEXT: 100000c: 60 47 bx r12
+// CHECK6: __Thumbv7ABSLongThunk_tfunc00:
+// CHECK6-NEXT: 100000e: 40 f2 01 0c movw r12, #1
+// CHECK6-NEXT: 1000012: c0 f2 08 0c movt r12, #8
+// CHECK6-NEXT: 1000016: 60 47 bx r12
+ FUNCTION 32
+ FUNCTION 33
+ // We should be able to reach an existing ThunkSection.
+ b.w tfunc00
+// CHECK7: tfunc33:
+// CHECK7-NEXT: 1100000: 70 47 bx lr
+// CHECK7-NEXT: 1100002: 00 f7 04 b8 b.w #-1048568 <__Thumbv7ABSLongThunk_tfunc00>
diff --git a/test/ELF/arm-thumb-interwork-shared.s b/test/ELF/arm-thumb-interwork-shared.s
index 8362ae26aed4..cadcd451ad67 100644
--- a/test/ELF/arm-thumb-interwork-shared.s
+++ b/test/ELF/arm-thumb-interwork-shared.s
@@ -19,34 +19,37 @@ sym1:
// CHECK-NEXT: 1000: 00 f0 02 b8 b.w #4 <__ThumbV7PILongThunk_elsewhere>
// CHECK-NEXT: 1004: 00 f0 06 b8 b.w #12 <__ThumbV7PILongThunk_weakref>
// CHECK: __ThumbV7PILongThunk_elsewhere:
-// CHECK-NEXT: 1008: 40 f2 20 0c movw r12, #32
+// CHECK-NEXT: 1008: 40 f2 2c 0c movw r12, #44
// CHECK-NEXT: 100c: c0 f2 00 0c movt r12, #0
// CHECK-NEXT: 1010: fc 44 add r12, pc
// CHECK-NEXT: 1012: 60 47 bx r12
-
// CHECK: __ThumbV7PILongThunk_weakref:
-// CHECK-NEXT: 1014: 40 f2 24 0c movw r12, #36
+// CHECK-NEXT: 1014: 40 f2 30 0c movw r12, #48
// CHECK-NEXT: 1018: c0 f2 00 0c movt r12, #0
// CHECK-NEXT: 101c: fc 44 add r12, pc
// CHECK-NEXT: 101e: 60 47 bx r12
// PLT: Disassembly of section .plt:
-// PLT: $a:
-// PLT-NEXT: 1020: 04 e0 2d e5 str lr, [sp, #-4]!
-// PLT-NEXT: 1024: 04 e0 9f e5 ldr lr, [pc, #4]
-// PLT-NEXT: 1028: 0e e0 8f e0 add lr, pc, lr
-// PLT-NEXT: 102c: 08 f0 be e5 ldr pc, [lr, #8]!
+// PLT-NEXT: $a:
+// PLT-NEXT: 1020: 04 e0 2d e5 str lr, [sp, #-4]!
+// PLT-NEXT: 1024: 00 e6 8f e2 add lr, pc, #0, #12
+// PLT-NEXT: 1028: 00 ea 8e e2 add lr, lr, #0, #20
+// PLT-NEXT: 102c: dc ff be e5 ldr pc, [lr, #4060]!
// PLT: $d:
-// PLT-NEXT: 1030: d0 0f 00 00 .word 0x00000fd0
+// PLT-NEXT: 1030: d4 d4 d4 d4 .word 0xd4d4d4d4
+// PLT-NEXT: 1034: d4 d4 d4 d4 .word 0xd4d4d4d4
+// PLT-NEXT: 1038: d4 d4 d4 d4 .word 0xd4d4d4d4
+// PLT-NEXT: 103c: d4 d4 d4 d4 .word 0xd4d4d4d4
// PLT: $a:
-// PLT-NEXT: 1034: 04 c0 9f e5 ldr r12, [pc, #4]
-// PLT-NEXT: 1038: 0f c0 8c e0 add r12, r12, pc
-// PLT-NEXT: 103c: 00 f0 9c e5 ldr pc, [r12]
+// PLT-NEXT: 1040: 00 c6 8f e2 add r12, pc, #0, #12
+// PLT-NEXT: 1044: 00 ca 8c e2 add r12, r12, #0, #20
+// PLT-NEXT: 1048: c4 ff bc e5 ldr pc, [r12, #4036]!
// PLT: $d:
-// PLT-NEXT: 1040: cc 0f 00 00 .word 0x00000fcc
+// PLT-NEXT: 104c: d4 d4 d4 d4 .word 0xd4d4d4d4
// PLT: $a:
-// PLT-NEXT: 1044: 04 c0 9f e5 ldr r12, [pc, #4]
-// PLT-NEXT: 1048: 0f c0 8c e0 add r12, r12, pc
-// PLT-NEXT: 104c: 00 f0 9c e5 ldr pc, [r12]
+// PLT-NEXT: 1050: 00 c6 8f e2 add r12, pc, #0, #12
+// PLT-NEXT: 1054: 00 ca 8c e2 add r12, r12, #0, #20
+// PLT-NEXT: 1058: b8 ff bc e5 ldr pc, [r12, #4024]!
// PLT: $d:
-// PLT-NEXT: 1050: c0 0f 00 00 .word 0x00000fc0
+// PLT-NEXT: 105c: d4 d4 d4 d4 .word 0xd4d4d4d4
+
diff --git a/test/ELF/arm-thumb-mix-range-thunk-os.s b/test/ELF/arm-thumb-mix-range-thunk-os.s
new file mode 100644
index 000000000000..beff4148b6ff
--- /dev/null
+++ b/test/ELF/arm-thumb-mix-range-thunk-os.s
@@ -0,0 +1,195 @@
+// REQUIRES: arm
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
+// RUN: ld.lld %t -o %t2 2>&1
+// The output file is large, most of it zeroes. We dissassemble only the
+// parts we need to speed up the test and avoid a large output file
+// RUN: llvm-objdump -d %t2 -start-address=1048576 -stop-address=1048604 -triple=armv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK1 %s
+// RUN: llvm-objdump -d %t2 -start-address=2097152 -stop-address=2097162 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK2 %s
+// RUN: llvm-objdump -d %t2 -start-address=16777220 -stop-address=16777232 -triple=armv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK3 %s
+// RUN: llvm-objdump -d %t2 -start-address=16777232 -stop-address=16777242 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK4 %s
+// RUN: llvm-objdump -d %t2 -start-address=32505860 -stop-address=32505870 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK5 %s
+// RUN: llvm-objdump -d %t2 -start-address=35651584 -stop-address=35651590 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK6 %s
+// RUN: llvm-objdump -d %t2 -start-address=36700160 -stop-address=36700168 -triple=armv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK7 %s
+// RUN: llvm-objdump -d %t2 -start-address=48234500 -stop-address=48234512 -triple=armv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK8 %s
+// RUN: llvm-objdump -d %t2 -start-address=63963140 -stop-address=63963160 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK9 %s
+// RUN: llvm-objdump -d %t2 -start-address=68157440 -stop-address=68157452 -triple=armv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK10 %s
+// RUN: llvm-objdump -d %t2 -start-address=69206016 -stop-address=69206024 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK11 %s
+
+// Test the Range extension Thunks for ARM and Thumb when all the code is in a
+// single OutputSection. The ARM branches and branch and link instructions
+// have a range of 32Mb, the Thumb unconditional branch and
+// branch and link instructions have . We create a series of Functions a
+// megabyte apart. We expect range extension thunks to be created when a
+// branch is out of range. Thunks will be reused whenever they are in range
+ .syntax unified
+
+// Define a function aligned on a megabyte boundary
+ .macro ARMFUNCTION suff
+ .section .text.\suff\(), "ax", %progbits
+ .arm
+ .balign 0x100000
+ .globl afunc\suff\()
+ .type afunc\suff\(), %function
+afunc\suff\():
+ bx lr
+ .endm
+
+// Define a function aligned on a megabyte boundary
+ .macro THUMBFUNCTION suff
+ .section .text.\suff\(), "ax", %progbits
+ .thumb
+ .balign 0x100000
+ .globl tfunc\suff\()
+ .type tfunc\suff\(), %function
+tfunc\suff\():
+ bx lr
+ .endm
+
+ .section .text, "ax", %progbits
+ .thumb
+ .globl _start
+_start:
+
+ ARMFUNCTION 00
+// Expect ARM bl to be in range (can use blx to change state)
+ bl tfunc31
+// ARM b and beq are in range but need Thunk to change state to Thumb
+ b tfunc31
+ beq tfunc31
+// afunc32 is out of range of ARM branch and branch and link
+ bl afunc32
+ b afunc32
+ bne afunc32
+// CHECK1: afunc00:
+// CHECK1-NEXT: 100000: 1e ff 2f e1 bx lr
+// CHECK1-NEXT: 100004: fd ff 7b fa blx #32505844
+// CHECK1-NEXT: 100008: fd ff 3b ea b #15728628
+// CHECK1-NEXT: 10000c: fc ff 3b 0a beq #15728624
+// CHECK1-NEXT: 100010: fa ff 7f eb bl #33554408
+// CHECK1-NEXT: 100014: f9 ff 7f ea b #33554404
+// CHECK1-NEXT: 100018: f8 ff 7f 1a bne #33554400
+ THUMBFUNCTION 01
+// Expect Thumb bl to be in range (can use blx to change state)
+ bl afunc14
+// In range but need thunk to change state to Thumb
+ b.w afunc14
+// CHECK2: tfunc01:
+// CHECK2-NEXT: 200000: 70 47 bx lr
+// CHECK2-NEXT: 200002: ff f0 fe c7 blx #13631484
+// CHECK2-NEXT: 200006: 00 f2 03 90 b.w #14680070 <__Thumbv7ABSLongThunk_afunc14>
+
+ ARMFUNCTION 02
+ THUMBFUNCTION 03
+ ARMFUNCTION 04
+ THUMBFUNCTION 05
+ ARMFUNCTION 06
+ THUMBFUNCTION 07
+ ARMFUNCTION 08
+ THUMBFUNCTION 09
+ ARMFUNCTION 10
+ THUMBFUNCTION 11
+ ARMFUNCTION 12
+ THUMBFUNCTION 13
+ ARMFUNCTION 14
+// CHECK3: __ARMv7ABSLongThunk_tfunc31:
+// CHECK3-NEXT: 1000004: 01 c0 00 e3 movw r12, #1
+// CHECK3-NEXT: 1000008: 00 c2 40 e3 movt r12, #512
+// CHECK3-NEXT: 100000c: 1c ff 2f e1 bx r12
+// CHECK4: __Thumbv7ABSLongThunk_afunc14:
+// CHECK4-NEXT: 1000010: 40 f2 00 0c movw r12, #0
+// CHECK4-NEXT: 1000014: c0 f2 f0 0c movt r12, #240
+// CHECK4-NEXT: 1000018: 60 47 bx r12
+ THUMBFUNCTION 15
+ ARMFUNCTION 16
+ THUMBFUNCTION 17
+ ARMFUNCTION 18
+ THUMBFUNCTION 19
+ ARMFUNCTION 20
+ THUMBFUNCTION 21
+ ARMFUNCTION 22
+ THUMBFUNCTION 23
+ ARMFUNCTION 24
+ THUMBFUNCTION 25
+ ARMFUNCTION 26
+ THUMBFUNCTION 27
+ ARMFUNCTION 28
+ THUMBFUNCTION 29
+ ARMFUNCTION 30
+// Expect precreated Thunk Section here
+// CHECK5: __Thumbv7ABSLongThunk_afunc00:
+// CHECK5-NEXT: 1f00004: 40 f2 00 0c movw r12, #0
+// CHECK5-NEXT: 1f00008: c0 f2 10 0c movt r12, #16
+// CHECK5-NEXT: 1f0000c: 60 47 bx r12
+ THUMBFUNCTION 31
+ ARMFUNCTION 32
+ THUMBFUNCTION 33
+// Out of range, can only reach closest Thunk Section
+ bl afunc00
+// CHECK6: tfunc33:
+// CHECK6-NEXT: 2200000: 70 47 bx lr
+// CHECK6-NEXT: 2200002: ff f4 ff ff bl #-3145730
+ ARMFUNCTION 34
+// Out of range, can reach earlier Thunk Section
+// CHECK7: afunc34:
+// CHECK7-NEXT: 2300000: 1e ff 2f e1 bx lr
+// CHECK7-NEXT: 2300004: fe ff ef fa blx #-4194312 <__Thumbv7ABSLongThunk_afunc00
+ bl afunc00
+ THUMBFUNCTION 35
+ ARMFUNCTION 36
+ THUMBFUNCTION 37
+ ARMFUNCTION 38
+ THUMBFUNCTION 39
+ ARMFUNCTION 40
+ THUMBFUNCTION 41
+ ARMFUNCTION 42
+ THUMBFUNCTION 43
+ ARMFUNCTION 44
+ THUMBFUNCTION 45
+// Expect precreated Thunk Section here
+// CHECK8: __ARMv7ABSLongThunk_tfunc35:
+// CHECK8-NEXT: 2e00004: 01 c0 00 e3 movw r12, #1
+// CHECK8-NEXT: 2e00008: 40 c2 40 e3 movt r12, #576
+// CHECK8-NEXT: 2e0000c: 1c ff 2f e1 bx r12
+ ARMFUNCTION 46
+ THUMBFUNCTION 47
+ ARMFUNCTION 48
+ THUMBFUNCTION 49
+ ARMFUNCTION 50
+ THUMBFUNCTION 51
+ ARMFUNCTION 52
+ THUMBFUNCTION 53
+ ARMFUNCTION 54
+ THUMBFUNCTION 55
+ ARMFUNCTION 56
+ THUMBFUNCTION 57
+ ARMFUNCTION 58
+ THUMBFUNCTION 59
+ ARMFUNCTION 60
+// Expect precreated Thunk Section here
+// CHECK9: __Thumbv7ABSLongThunk_afunc34:
+// CHECK9-NEXT: 3d00004: 40 f2 00 0c movw r12, #0
+// CHECK9-NEXT: 3d00008: c0 f2 30 2c movt r12, #560
+// CHECK9-NEXT: 3d0000c: 60 47 bx r12
+// CHECK9: __Thumbv7ABSLongThunk_tfunc35:
+// CHECK9-NEXT: 3d0000e: 40 f2 01 0c movw r12, #1
+// CHECK9-NEXT: 3d00012: c0 f2 40 2c movt r12, #576
+// CHECK9-NEXT: 3d00016: 60 47 bx r12
+ THUMBFUNCTION 61
+ ARMFUNCTION 62
+ THUMBFUNCTION 63
+ ARMFUNCTION 64
+// afunc34 is in range, as is tfunc35 but a branch needs a state change Thunk
+ bl afunc34
+ b tfunc35
+// CHECK10: afunc64:
+// CHECK10-NEXT: 4100000: 1e ff 2f e1 bx lr
+// CHECK10-NEXT: 4100004: fd ff 87 eb bl #-31457292 <afunc34>
+// CHECK10-NEXT: 4100008: fd ff b3 ea b #-19922956 <__ARMv7ABSLongThunk_tfunc35>
+ THUMBFUNCTION 65
+// afunc34 and tfunc35 are both out of range
+ bl afunc34
+ bl tfunc35
+// CHECK11: tfunc65:
+// CHECK11: 4200000: 70 47 bx lr
+// CHECK11-NEXT: 4200002: ff f6 ff f7 bl #-5242882
+// CHECK11-NEXT: 4200006: 00 f7 02 f0 bl #-5242876
diff --git a/test/ELF/arm-thumb-plt-range-thunk-os.s b/test/ELF/arm-thumb-plt-range-thunk-os.s
new file mode 100644
index 000000000000..f412faa98eca
--- /dev/null
+++ b/test/ELF/arm-thumb-plt-range-thunk-os.s
@@ -0,0 +1,92 @@
+// REQUIRES: arm
+// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %s -o %t
+// RUN: ld.lld %t --shared -o %t.so
+// The output file is large, most of it zeroes. We dissassemble only the
+// parts we need to speed up the test and avoid a large output file
+// RUN: llvm-objdump -d %t.so -start-address=8388608 -stop-address=8388624 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK1 %s
+// RUN: llvm-objdump -d %t.so -start-address=16777216 -stop-address=16777256 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK2 %s
+// RUN: llvm-objdump -d %t.so -start-address=25165824 -stop-address=25165828 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK3 %s
+// RUN: llvm-objdump -d %t.so -start-address=25165828 -stop-address=25165924 -triple=armv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK4 %s
+ .syntax unified
+ .thumb
+
+// Make sure that we generate a range extension thunk to a PLT entry
+ .section ".text.1", "ax", %progbits
+ .global sym1
+ .global elsewhere
+ .type elsewhere, %function
+ .global preemptible
+ .type preemptible, %function
+ .global far_preemptible
+ .type far_preemptible, %function
+sym1:
+ bl elsewhere
+ bl preemptible
+ bx lr
+preemptible:
+ bl far_preemptible
+ bx lr
+// CHECK1: Disassembly of section .text:
+// CHECK1-NEXT: sym1:
+// CHECK1-NEXT: 800000: 00 f0 00 d8 bl #8388608
+// CHECK1-NEXT: 800004: 00 f0 04 d8 bl #8388616
+// CHECK1-NEXT: 800008: 70 47 bx lr
+// CHECK1: preemptible:
+// CHECK1-NEXT: 80000a: 00 f0 07 d8 bl #8388622
+// CHECK1-NEXT: 80000e: 70 47 bx lr
+
+ .section .text.2, "ax", %progbits
+ .balign 0x0800000
+ bx lr
+// CHECK2: __ThumbV7PILongThunk_elsewhere:
+// CHECK2-NEXT: 1000004: 40 f2 20 0c movw r12, #32
+// CHECK2-NEXT: 1000008: c0 f2 80 0c movt r12, #128
+// CHECK2-NEXT: 100000c: fc 44 add r12, pc
+// CHECK2-NEXT: 100000e: 60 47 bx r12
+// CHECK2: __ThumbV7PILongThunk_preemptible:
+// CHECK2-NEXT: 1000010: 40 f2 24 0c movw r12, #36
+// CHECK2-NEXT: 1000014: c0 f2 80 0c movt r12, #128
+// CHECK2-NEXT: 1000018: fc 44 add r12, pc
+// CHECK2-NEXT: 100001a: 60 47 bx r12
+// CHECK2: __ThumbV7PILongThunk_far_preemptible:
+// CHECK2-NEXT: 100001c: 40 f2 28 0c movw r12, #40
+// CHECK2-NEXT: 1000020: c0 f2 80 0c movt r12, #128
+// CHECK2-NEXT: 1000024: fc 44 add r12, pc
+// CHECK2-NEXT: 1000026: 60 47 bx r12
+
+ .section .text.3, "ax", %progbits
+.balign 0x0800000
+far_preemptible:
+ bl elsewhere
+// CHECK3: far_preemptible:
+// CHECK3: 1800000: 00 f0 16 e8 blx #44
+
+// CHECK4: Disassembly of section .plt:
+// CHECK4-NEXT: $a:
+// CHECK4-NEXT: 1800010: 04 e0 2d e5 str lr, [sp, #-4]!
+// CHECK4-NEXT: 1800014: 00 e6 8f e2 add lr, pc, #0, #12
+// CHECK4-NEXT: 1800018: 00 ea 8e e2 add lr, lr, #0, #20
+// CHECK4-NEXT: 180001c: ec ff be e5 ldr pc, [lr, #4076]!
+// CHECK4: $d:
+// CHECK4-NEXT: 1800020: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECK4-NEXT: 1800024: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECK4-NEXT: 1800028: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECK4-NEXT: 180002c: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECK4: $a:
+// CHECK4-NEXT: 1800030: 00 c6 8f e2 add r12, pc, #0, #12
+// CHECK4-NEXT: 1800034: 00 ca 8c e2 add r12, r12, #0, #20
+// CHECK4-NEXT: 1800038: d4 ff bc e5 ldr pc, [r12, #4052]!
+// CHECK4: $d:
+// CHECK4-NEXT: 180003c: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECK4: $a:
+// CHECK4-NEXT: 1800040: 00 c6 8f e2 add r12, pc, #0, #12
+// CHECK4-NEXT: 1800044: 00 ca 8c e2 add r12, r12, #0, #20
+// CHECK4-NEXT: 1800048: c8 ff bc e5 ldr pc, [r12, #4040]!
+// CHECK4: $d:
+// CHECK4-NEXT: 180004c: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECK4: $a:
+// CHECK4-NEXT: 1800050: 00 c6 8f e2 add r12, pc, #0, #12
+// CHECK4-NEXT: 1800054: 00 ca 8c e2 add r12, r12, #0, #20
+// CHECK4-NEXT: 1800058: bc ff bc e5 ldr pc, [r12, #4028]!
+// CHECK4: $d:
+// CHECK4-NEXT: 180005c: d4 d4 d4 d4 .word 0xd4d4d4d4
diff --git a/test/ELF/arm-thumb-plt-reloc.s b/test/ELF/arm-thumb-plt-reloc.s
index f9afbb9c0ce1..dd8770edc3c1 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
@@ -43,50 +43,54 @@ _start:
// .text is Thumb and .plt is ARM, llvm-objdump can currently only disassemble
// as ARM or Thumb. Work around by disassembling twice.
// DSOTHUMB: Disassembly of section .text:
-// DSOTHUMB: func1:
-// DSOTHUMB-NEXT: 1000: 70 47 bx lr
+// DSOTHUMB-NEXT: func1:
+// DSOTHUMB-NEXT: 1000: 70 47 bx lr
// DSOTHUMB: func2:
-// DSOTHUMB-NEXT: 1002: 70 47 bx lr
+// DSOTHUMB-NEXT: 1002: 70 47 bx lr
// DSOTHUMB: func3:
-// DSOTHUMB-NEXT: 1004: 70 47 bx lr
-// DSOTHUMB-NEXT: 1006: d4 d4
+// DSOTHUMB-NEXT: 1004: 70 47 bx lr
+// DSOTHUMB-NEXT: 1006: d4 d4 bmi #-88
// DSOTHUMB: _start:
-// 0x1008 + 0x28 + 4 = 0x1034 = PLT func1
-// DSOTHUMB-NEXT: 1008: 00 f0 14 e8 blx #40
-// 0x100c + 0x34 + 4 = 0x1044 = PLT func2
-// DSOTHUMB-NEXT: 100c: 00 f0 1a e8 blx #52
-// 0x1010 + 0x40 + 4 = 0x1054 = PLT func3
-// DSOTHUMB-NEXT: 1010: 00 f0 20 e8 blx #64
+// 0x1008 + 0x34 + 4 = 0x1040 = PLT func1
+// DSOTHUMB-NEXT: 1008: 00 f0 1a e8 blx #52
+// 0x100c + 0x40 + 4 = 0x1050 = PLT func2
+// DSOTHUMB-NEXT: 100c: 00 f0 20 e8 blx #64
+// 0x1010 + 0x4C + 4 = 0x1060 = PLT func3
+// DSOTHUMB-NEXT: 1010: 00 f0 26 e8 blx #76
// DSOARM: Disassembly of section .plt:
// DSOARM-NEXT: $a:
-// DSOARM-NEXT: 1020: 04 e0 2d e5 str lr, [sp, #-4]!
-// DSOARM-NEXT: 1024: 04 e0 9f e5 ldr lr, [pc, #4]
-// DSOARM-NEXT: 1028: 0e e0 8f e0 add lr, pc, lr
-// DSOARM-NEXT: 102c: 08 f0 be e5 ldr pc, [lr, #8]!
+// DSOARM-NEXT: 1020: 04 e0 2d e5 str lr, [sp, #-4]!
+// (0x1024 + 8) + (0 RoR 12) + (0 RoR 20) + (0xfdc) = 0x2008 = .got.plt[3]
+// DSOARM-NEXT: 1024: 00 e6 8f e2 add lr, pc, #0, #12
+// DSOARM-NEXT: 1028: 00 ea 8e e2 add lr, lr, #0, #20
+// DSOARM-NEXT: 102c: dc ff be e5 ldr pc, [lr, #4060]!
// DSOARM: $d:
-// DSOARM-NEXT: 1030: d0 0f 00 00 .word 0x00000fd0
-// 0x1028 + 8 + 0fd0 = 0x2000
+
+// DSOARM-NEXT: 1030: d4 d4 d4 d4 .word 0xd4d4d4d4
+// DSOARM-NEXT: 1034: d4 d4 d4 d4 .word 0xd4d4d4d4
+// DSOARM-NEXT: 1038: d4 d4 d4 d4 .word 0xd4d4d4d4
+// DSOARM-NEXT: 103c: d4 d4 d4 d4 .word 0xd4d4d4d4
// DSOARM: $a:
-// DSOARM-NEXT: 1034: 04 c0 9f e5 ldr r12, [pc, #4]
-// DSOARM-NEXT: 1038: 0f c0 8c e0 add r12, r12, pc
-// DSOARM-NEXT: 103c: 00 f0 9c e5 ldr pc, [r12]
+// (0x1040 + 8) + (0 RoR 12) + (0 RoR 20) + (0xfc4) = 0x200c
+// DSOARM-NEXT: 1040: 00 c6 8f e2 add r12, pc, #0, #12
+// DSOARM-NEXT: 1044: 00 ca 8c e2 add r12, r12, #0, #20
+// DSOARM-NEXT: 1048: c4 ff bc e5 ldr pc, [r12, #4036]!
// DSOARM: $d:
-// DSOARM-NEXT: 1040: cc 0f 00 00 .word 0x00000fcc
-// 0x1038 + 8 + 0fcc = 0x200c
+// DSOARM-NEXT: 104c: d4 d4 d4 d4 .word 0xd4d4d4d4
// DSOARM: $a:
-// DSOARM-NEXT: 1044: 04 c0 9f e5 ldr r12, [pc, #4]
-// DSOARM-NEXT: 1048: 0f c0 8c e0 add r12, r12, pc
-// DSOARM-NEXT: 104c: 00 f0 9c e5 ldr pc, [r12]
+// (0x1050 + 8) + (0 RoR 12) + (0 RoR 20) + (0xfb8) = 0x2010
+// DSOARM-NEXT: 1050: 00 c6 8f e2 add r12, pc, #0, #12
+// DSOARM-NEXT: 1054: 00 ca 8c e2 add r12, r12, #0, #20
+// DSOARM-NEXT: 1058: b8 ff bc e5 ldr pc, [r12, #4024]!
// DSOARM: $d:
-// DSOARM-NEXT: 1050: c0 0f 00 00 .word 0x00000fc0
-// 0x1048 + 8 + 0fc0 = 0x2010
+// DSOARM-NEXT: 105c: d4 d4 d4 d4 .word 0xd4d4d4d4
// DSOARM: $a:
-// DSOARM-NEXT: 1054: 04 c0 9f e5 ldr r12, [pc, #4]
-// DSOARM-NEXT: 1058: 0f c0 8c e0 add r12, r12, pc
-// DSOARM-NEXT: 105c: 00 f0 9c e5 ldr pc, [r12]
+// (0x1060 + 8) + (0 RoR 12) + (0 RoR 20) + (0xfac) = 0x2014
+// DSOARM-NEXT: 1060: 00 c6 8f e2 add r12, pc, #0, #12
+// DSOARM-NEXT: 1064: 00 ca 8c e2 add r12, r12, #0, #20
+// DSOARM-NEXT: 1068: ac ff bc e5 ldr pc, [r12, #4012]!
// DSOARM: $d:
-// DSOARM-NEXT: 1060: b4 0f 00 00 .word 0x00000fb4
-// 0x1058 + 8 + 0fb4 = 0x2014
+// DSOARM-NEXT: 106c: d4 d4 d4 d4 .word 0xd4d4d4d4
// DSOREL: Name: .got.plt
// DSOREL-NEXT: Type: SHT_PROGBITS
diff --git a/test/ELF/arm-thumb-range-thunk-os.s b/test/ELF/arm-thumb-range-thunk-os.s
new file mode 100644
index 000000000000..588539ddab8c
--- /dev/null
+++ b/test/ELF/arm-thumb-range-thunk-os.s
@@ -0,0 +1,159 @@
+// REQUIRES: arm
+// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %s -o %t
+// RUN: ld.lld %t -o %t2 2>&1
+// The output file is large, most of it zeroes. We dissassemble only the
+// parts we need to speed up the test and avoid a large output file
+// RUN: llvm-objdump -d %t2 -start-address=1048576 -stop-address=1048588 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK1 %s
+// RUN: llvm-objdump -d %t2 -start-address=2097152 -stop-address=2097154 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK2 %s
+// RUN: llvm-objdump -d %t2 -start-address=3145728 -stop-address=3145730 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK3 %s
+// RUN: llvm-objdump -d %t2 -start-address=4194304 -stop-address=4194310 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK4 %s
+// RUN: llvm-objdump -d %t2 -start-address=16777216 -stop-address=16777270 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK5 %s
+// RUN: llvm-objdump -d %t2 -start-address=17825792 -stop-address=17825808 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK6 %s
+// RUN: llvm-objdump -d %t2 -start-address=31457280 -stop-address=31457286 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK7 %s
+// RUN: llvm-objdump -d %t2 -start-address=32505860 -stop-address=32505880 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK8 %s
+// RUN: llvm-objdump -d %t2 -start-address=35651584 -stop-address=35651594 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK9 %s
+// RUN: llvm-objdump -d %t2 -start-address=36700160 -stop-address=36700170 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK10 %s
+
+// Test the Range extension Thunks for Thumb when all the code is in a single
+// OutputSection. The Thumb unconditional branch b.w and branch and link bl
+// instructions have a range of 16Mb. We create a series of Functions a
+// megabyte apart. We expect range extension thunks to be created when a
+// branch is out of range. Thunks will be reused whenever they are in range
+ .syntax unified
+
+// Define a function aligned on a megabyte boundary
+ .macro FUNCTION suff
+ .section .text.\suff\(), "ax", %progbits
+ .thumb
+ .balign 0x100000
+ .globl tfunc\suff\()
+ .type tfunc\suff\(), %function
+tfunc\suff\():
+ bx lr
+ .endm
+
+ .section .text, "ax", %progbits
+ .thumb
+ .globl _start
+_start:
+// tfunc00 and tfunc15 are within 16Mb no Range Thunks expected
+ bl tfunc00
+ bl tfunc15
+// tfunc16 is > 16Mb away, expect a Range Thunk to be generated, to go into
+// the first of the pre-created ThunkSections.
+ bl tfunc16
+// CHECK1: Disassembly of section .text:
+// CHECK1-NEXT: _start:
+// CHECK1-NEXT: 100000: ff f0 fe ff bl #1048572
+// CHECK1-NEXT: 100004: ff f3 fc d7 bl #16777208
+// CHECK1-NEXT: 100008: ff f2 fc d7 bl #15728632
+
+ FUNCTION 00
+// CHECK2: tfunc00:
+// CHECK2-NEXT: 200000: 70 47 bx lr
+ FUNCTION 01
+// CHECK3: tfunc01:
+// CHECK3-NEXT: 300000: 70 47 bx lr
+ FUNCTION 02
+// tfunc28 is > 16Mb away, expect a Range Thunk to be generated, to go into
+// the first of the pre-created ThunkSections.
+ b.w tfunc28
+// CHECK4: tfunc02:
+// CHECK4-NEXT: 400000: 70 47 bx lr
+// CHECK4-NEXT: 400002: 00 f0 04 90 b.w #12582920 <__Thumbv7ABSLongThunk_tfunc28>
+ FUNCTION 03
+ FUNCTION 04
+ FUNCTION 05
+ FUNCTION 06
+ FUNCTION 07
+ FUNCTION 08
+ FUNCTION 09
+ FUNCTION 10
+ FUNCTION 11
+ FUNCTION 12
+ FUNCTION 13
+ FUNCTION 14
+// Expect precreated ThunkSection here
+// CHECK5: __Thumbv7ABSLongThunk_tfunc16:
+// CHECK5-NEXT: 1000004: 40 f2 01 0c movw r12, #1
+// CHECK5-NEXT: 1000008: c0 f2 20 1c movt r12, #288
+// CHECK5-NEXT: 100000c: 60 47 bx r12
+// CHECK5: __Thumbv7ABSLongThunk_tfunc28:
+// CHECK5-NEXT: 100000e: 40 f2 01 0c movw r12, #1
+// CHECK5-NEXT: 1000012: c0 f2 e0 1c movt r12, #480
+// CHECK5-NEXT: 1000016: 60 47 bx r12
+// CHECK5: __Thumbv7ABSLongThunk_tfunc32:
+// CHECK5-NEXT: 1000018: 40 f2 01 0c movw r12, #1
+// CHECK5-NEXT: 100001c: c0 f2 20 2c movt r12, #544
+// CHECK5-NEXT: 1000020: 60 47 bx r12
+// CHECK5: __Thumbv7ABSLongThunk_tfunc33:
+// CHECK5-NEXT: 1000022: 40 f2 01 0c movw r12, #1
+// CHECK5-NEXT: 1000026: c0 f2 30 2c movt r12, #560
+// CHECK5-NEXT: 100002a: 60 47 bx r12
+// CHECK5: __Thumbv7ABSLongThunk_tfunc02:
+// CHECK5-NEXT: 100002c: 40 f2 01 0c movw r12, #1
+// CHECK5-NEXT: 1000030: c0 f2 40 0c movt r12, #64
+// CHECK5-NEXT: 1000034: 60 47 bx r12
+ FUNCTION 15
+// tfunc00 and tfunc01 are < 16Mb away, expect no range extension thunks
+ bl tfunc00
+ bl tfunc01
+// tfunc32 and tfunc33 are > 16Mb away, expect range extension thunks in the
+// precreated thunk section
+ bl tfunc32
+ bl tfunc33
+// CHECK6: tfunc15:
+// CHECK6-NEXT: 1100000: 70 47 bx lr
+// CHECK6-NEXT: 1100002: ff f4 fd d7 bl #-15728646
+// CHECK6-NEXT: 1100006: ff f5 fb d7 bl #-14680074
+// CHECK6-NEXT: 110000a: 00 f7 05 f8 bl #-1048566
+// CHECK6-NEXT: 110000e: 00 f7 08 f8 bl #-1048560
+ FUNCTION 16
+ FUNCTION 17
+ FUNCTION 18
+ FUNCTION 19
+ FUNCTION 20
+ FUNCTION 21
+ FUNCTION 22
+ FUNCTION 23
+ FUNCTION 24
+ FUNCTION 25
+ FUNCTION 26
+ FUNCTION 27
+ FUNCTION 28
+// tfunc02 is > 16Mb away, expect range extension thunks in precreated thunk
+// section
+// CHECK7: tfunc28:
+// CHECK7-NEXT: 1e00000: 70 47 bx lr
+// CHECK7-NEXT: 1e00002: 00 f6 13 90 b.w #-14680026 <__Thumbv7ABSLongThunk_tfunc02>
+
+ b.w tfunc02
+ FUNCTION 29
+// Expect another precreated thunk section here
+// CHECK8: __Thumbv7ABSLongThunk_tfunc15:
+// CHECK8-NEXT: 1f00004: 40 f2 01 0c movw r12, #1
+// CHECK8-NEXT: 1f00008: c0 f2 10 1c movt r12, #272
+// CHECK8-NEXT: 1f0000c: 60 47 bx r12
+// CHECK8: __Thumbv7ABSLongThunk_tfunc16:
+// CHECK8-NEXT: 1f0000e: 40 f2 01 0c movw r12, #1
+// CHECK8-NEXT: 1f00012: c0 f2 20 1c movt r12, #288
+// CHECK8-NEXT: 1f00016: 60 47 bx r12
+ FUNCTION 30
+ FUNCTION 31
+ FUNCTION 32
+ // tfunc15 and tfunc16 are > 16 Mb away expect Thunks in the nearest
+ // precreated thunk section.
+ bl tfunc15
+ bl tfunc16
+// CHECK9: tfunc32:
+// CHECK9: 2200000: 70 47 bx lr
+// CHECK9-NEXT: 2200002: ff f4 ff ff bl #-3145730
+// CHECK9-NEXT: 2200006: 00 f5 02 f8 bl #-3145724
+
+ FUNCTION 33
+ bl tfunc15
+ bl tfunc16
+// CHECK10: tfunc33:
+// CHECK10: 2300000: 70 47 bx lr
+// CHECK10-NEXT: 2300002: ff f7 ff f7 bl #-4194306
+// CHECK10-NEXT: 2300006: 00 f4 02 f8 bl #-4194300
diff --git a/test/ELF/arm-thumb-thunk-empty-pass.s b/test/ELF/arm-thumb-thunk-empty-pass.s
new file mode 100644
index 000000000000..9ff6ed6a7807
--- /dev/null
+++ b/test/ELF/arm-thumb-thunk-empty-pass.s
@@ -0,0 +1,32 @@
+// REQUIRES: arm
+// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %s -o %t
+// RUN: ld.lld %t -o %t2 2>&1
+// RUN: llvm-objdump -d %t2 -start-address=69632 -stop-address=69646 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK1 %s
+// RUN: llvm-objdump -d %t2 -start-address=16846860 -stop-address=16846874 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK2 %s
+ .syntax unified
+ .global _start, foo
+ .type _start, %function
+ .section .text.start,"ax",%progbits
+_start:
+ bl _start
+ .section .text.dummy1,"ax",%progbits
+ .space 0xfffffe
+ .section .text.foo,"ax",%progbits
+ .type foo, %function
+foo:
+ bl _start
+
+// CHECK1: Disassembly of section .text:
+// CHECK1-NEXT: _start:
+// CHECK1-NEXT: 11000: ff f7 fe ff bl #-4
+// CHECK1: __Thumbv7ABSLongThunk__start:
+// CHECK1-NEXT: 11004: 41 f2 01 0c movw r12, #4097
+// CHECK1-NEXT: 11008: c0 f2 01 0c movt r12, #1
+// CHECK1-NEXT: 1100c: 60 47 bx r12
+
+// CHECK2: __Thumbv7ABSLongThunk__start:
+// CHECK2: 101100c: 41 f2 01 0c movw r12, #4097
+// CHECK2-NEXT: 1011010: c0 f2 01 0c movt r12, #1
+// CHECK2-NEXT: 1011014: 60 47 bx r12
+// CHECK2: foo:
+// CHECK2-NEXT: 1011016: ff f7 f9 ff bl #-14
diff --git a/test/ELF/arm-thumb-thunk-symbols.s b/test/ELF/arm-thumb-thunk-symbols.s
index 42046f802f96..faa39fec0218 100644
--- a/test/ELF/arm-thumb-thunk-symbols.s
+++ b/test/ELF/arm-thumb-thunk-symbols.s
@@ -25,18 +25,18 @@ arm_fn:
b thumb_fn
// CHECK: Name: __Thumbv7ABSLongThunk_arm_fn
-// CHECK-NEXT: Value: 0x11005
+// CHECK-NEXT: Value: 0x12005
// CHECK-NEXT: Size: 10
// CHECK-NEXT: Binding: Local (0x0)
// CHECK-NEXT: Type: Function (0x2)
// CHECK: Name: __ARMv7ABSLongThunk_thumb_fn
-// CHECK-NEXT: Value: 0x11010
+// CHECK-NEXT: Value: 0x12010
// CHECK-NEXT: Size: 12
// CHECK-NEXT: Binding: Local (0x0)
// CHECK-NEXT: Type: Function (0x2)
// CHECK-PI: Name: __ThumbV7PILongThunk_arm_fn
-// CHECK-PI-NEXT: Value: 0x1005
+// CHECK-PI-NEXT: Value: 0x2005
// CHECK-PI-NEXT: Size: 12
// CHECK-PI-NEXT: Binding: Local (0x0)
// CHECK-PI-NEXT: Type: Function (0x2)
diff --git a/test/ELF/arm-thunk-edgecase.s b/test/ELF/arm-thunk-edgecase.s
new file mode 100644
index 000000000000..81837c7b3284
--- /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-thunk-largesection.s b/test/ELF/arm-thunk-largesection.s
new file mode 100644
index 000000000000..950f789764a6
--- /dev/null
+++ b/test/ELF/arm-thunk-largesection.s
@@ -0,0 +1,42 @@
+// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %s -o %t
+// RUN: ld.lld %t -o %t2 2>&1
+// RUN: llvm-objdump -d -triple=thumbv7a-none-linux-gnueabi -start-address=69632 -stop-address=69636 %t2 | FileCheck -check-prefix=CHECK1 %s
+// RUN: llvm-objdump -d -triple=thumbv7a-none-linux-gnueabi -start-address=73732 -stop-address=73742 %t2 | FileCheck -check-prefix=CHECK2 %s
+// RUN: llvm-objdump -d -triple=thumbv7a-none-linux-gnueabi -start-address=16850944 -stop-address=16850948 %t2 | FileCheck -check-prefix=CHECK3 %s
+// RUN: llvm-objdump -d -triple=thumbv7a-none-linux-gnueabi -start-address=33628160 -stop-address=33628164 %t2 | FileCheck -check-prefix=CHECK4 %s
+// RUN: llvm-objdump -d -triple=thumbv7a-none-linux-gnueabi -start-address=50405364 -stop-address=50405376 %t2 | FileCheck -check-prefix=CHECK5 %s
+// REQUIRES: arm
+ .syntax unified
+ .balign 0x1000
+ .thumb
+ .text
+ .globl _start
+ .type _start, %function
+_start:
+ bx lr
+ .space 0x1000
+// CHECK1: Disassembly of section .text:
+// CHECK1-NEXT: _start:
+// CHECK1-NEXT: 11000: 70 47 bx lr
+// CHECK1-NEXT: 11002: 00 00 movs r0, r0
+
+// CHECK2: __Thumbv7ABSLongThunk__start:
+// CHECK2-NEXT: 12004: 41 f2 01 0c movw r12, #4097
+// CHECK2-NEXT: 12008: c0 f2 01 0c movt r12, #1
+// CHECK2-NEXT: 1200c: 60 47 bx r12
+
+// Gigantic section where we need a ThunkSection either side of it
+ .section .text.large1, "ax", %progbits
+ .balign 4
+ .space (16 * 1024 * 1024) - 16
+ bl _start
+ .space (16 * 1024 * 1024) - 4
+ bl _start
+ .space (16 * 1024 * 1024) - 16
+// CHECK3: 1012000: 00 f4 00 d0 bl #-16777216
+// CHECK4: 2012000: ff f3 f8 d7 bl #16777200
+
+// CHECK5: __Thumbv7ABSLongThunk__start:
+// CHECK5-NEXT: 3011ff4: 41 f2 01 0c movw r12, #4097
+// CHECK5-NEXT: 3011ff8: c0 f2 01 0c movt r12, #1
+// CHECK5-NEXT: 3011ffc: 60 47 bx r12
diff --git a/test/ELF/arm-thunk-linkerscript-dotexpr.s b/test/ELF/arm-thunk-linkerscript-dotexpr.s
new file mode 100644
index 000000000000..bd0e9a293102
--- /dev/null
+++ b/test/ELF/arm-thunk-linkerscript-dotexpr.s
@@ -0,0 +1,77 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
+// RUN: echo "SECTIONS { \
+// RUN: . = SIZEOF_HEADERS; \
+// RUN: .text_low : { *(.text_low) *(.text_low2) . = . + 0x2000000 ; *(.text_high) *(.text_high2) } \
+// RUN: } " > %t.script
+// RUN: ld.lld --script %t.script %t -o %t2 2>&1
+// RUN: llvm-objdump -d %t2 -start-address=148 -stop-address=188 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK1 %s
+// RUN: llvm-objdump -d %t2 -start-address=33554620 -stop-address=33554654 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK2 %s
+// REQUIRES: arm
+// Test that range extension thunks can handle location expressions within
+// a Section Description
+ .syntax unified
+ .section .text_low, "ax", %progbits
+ .thumb
+ .globl _start
+_start: bx lr
+ .globl low_target
+ .type low_target, %function
+low_target:
+ bl high_target
+ bl high_target2
+
+ .section .text_low2, "ax", %progbits
+ .thumb
+ .globl low_target2
+ .type low_target2, %function
+low_target2:
+ bl high_target
+ bl high_target2
+// CHECK1: Disassembly of section .text_low:
+// CHECK1-NEXT: _start:
+// CHECK1-NEXT: 94: 70 47 bx lr
+// CHECK1: low_target:
+// CHECK1-NEXT: 96: 00 f0 03 f8 bl #6
+// CHECK1-NEXT: 9a: 00 f0 06 f8 bl #12
+// CHECK1: __Thumbv7ABSLongThunk_high_target:
+// CHECK1-NEXT: a0: 40 f2 bd 0c movw r12, #189
+// CHECK1-NEXT: a4: c0 f2 00 2c movt r12, #512
+// CHECK1-NEXT: a8: 60 47 bx r12
+// CHECK1: __Thumbv7ABSLongThunk_high_target2:
+// CHECK1-NEXT: aa: 40 f2 d9 0c movw r12, #217
+// CHECK1-NEXT: ae: c0 f2 00 2c movt r12, #512
+// CHECK1-NEXT: b2: 60 47 bx r12
+// CHECK1: low_target2:
+// CHECK1-NEXT: b4: ff f7 f4 ff bl #-24
+// CHECK1-NEXT: b8: ff f7 f7 ff bl #-18
+
+ .section .text_high, "ax", %progbits
+ .thumb
+ .globl high_target
+ .type high_target, %function
+high_target:
+ bl low_target
+ bl low_target2
+
+ .section .text_high2, "ax", %progbits
+ .thumb
+ .globl high_target2
+ .type high_target2, %function
+high_target2:
+ bl low_target
+ bl low_target2
+
+// CHECK2: high_target:
+// CHECK2-NEXT: 20000bc: 00 f0 02 f8 bl #4
+// CHECK2-NEXT: 20000c0: 00 f0 05 f8 bl #10
+// CHECK2: __Thumbv7ABSLongThunk_low_target:
+// CHECK2-NEXT: 20000c4: 40 f2 97 0c movw r12, #151
+// CHECK2-NEXT: 20000c8: c0 f2 00 0c movt r12, #0
+// CHECK2-NEXT: 20000cc: 60 47 bx r12
+// CHECK2: __Thumbv7ABSLongThunk_low_target2:
+// CHECK2-NEXT: 20000ce: 40 f2 b5 0c movw r12, #181
+// CHECK2-NEXT: 20000d2: c0 f2 00 0c movt r12, #0
+// CHECK2-NEXT: 20000d6: 60 47 bx r12
+// CHECK2: high_target2:
+// CHECK2-NEXT: 20000d8: ff f7 f4 ff bl #-24
+// CHECK2-NEXT: 20000dc: ff f7 f7 ff bl #-18
diff --git a/test/ELF/arm-thunk-linkerscript-large.s b/test/ELF/arm-thunk-linkerscript-large.s
new file mode 100644
index 000000000000..07cd1dd87976
--- /dev/null
+++ b/test/ELF/arm-thunk-linkerscript-large.s
@@ -0,0 +1,176 @@
+// REQUIRES: arm
+// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %s -o %t
+// RUN: echo "SECTIONS { \
+// RUN: .text 0x100000 : { *(.text) } \
+// RUN: .textl : { *(.text_l0*) *(.text_l1*) *(.text_l2*) *(.text_l3*) } \
+// RUN: .texth : { *(.text_h0*) *(.text_h1*) *(.text_h2*) *(.text_h3*) } \
+// RUN: }" > %t.script
+// RUN: ld.lld --script %t.script %t -o %t2 2>&1
+// The output file is large, most of it zeroes. We dissassemble only the
+// parts we need to speed up the test and avoid a large output file
+// RUN: llvm-objdump -d %t2 -start-address=1048576 -stop-address=1048594 -triple=thumbv7a-linux-gnueabihf | FileCheck --check-prefix=CHECK1 %s
+// RUN: llvm-objdump -d %t2 -start-address=2097152 -stop-address=2097160 -triple=thumbv7a-linux-gnueabihf | FileCheck --check-prefix=CHECK2 %s
+// RUN: llvm-objdump -d %t2 -start-address=11534340 -stop-address=11534350 -triple=thumbv7a-linux-gnueabihf | FileCheck --check-prefix=CHECK3 %s
+// RUN: llvm-objdump -d %t2 -start-address=34603008 -stop-address=34603034 -triple=thumbv7a-linux-gnueabihf | FileCheck --check-prefix=CHECK4 %s
+// RUN: llvm-objdump -d %t2 -start-address=35651584 -stop-address=35651598 -triple=thumbv7a-linux-gnueabihf | FileCheck --check-prefix=CHECK5 %s
+// RUN: llvm-objdump -d %t2 -start-address=68157440 -stop-address=68157472 -triple=thumbv7a-linux-gnueabihf | FileCheck --check-prefix=CHECK6 %s
+
+// Test the range extensions in a linker script where there are several
+// OutputSections requiring range extension Thunks. We should be able to reuse
+// Thunks between OutputSections but our placement of new Thunks is done on a
+// per OutputSection basis
+ .syntax unified
+
+// Define a function that we can match with .text_l* aligned on a megabyte // boundary
+ .macro FUNCTIONL suff
+ .section .text_l\suff\(), "ax", %progbits
+ .thumb
+ .balign 0x100000
+ .globl tfuncl\suff\()
+ .type tfuncl\suff\(), %function
+tfuncl\suff\():
+ bx lr
+ .endm
+
+// Define a function that we can match with .text_h* aligned on a megabyte
+// boundary
+ .macro FUNCTIONH suff
+ .section .text_h\suff\(), "ax", %progbits
+ .thumb
+ .balign 0x100000
+ .globl tfunch\suff\()
+ .type tfunch\suff\(), %function
+tfunch\suff\():
+ bx lr
+ .endm
+
+ .section .text, "ax", %progbits
+ .thumb
+ .globl _start
+_start:
+ bl tfuncl00
+ // Expect a range extension thunk in .text OutputSection
+ bl tfunch31
+// CHECK1: Disassembly of section .text:
+// CHECK1-NEXT: _start:
+// CHECK1-NEXT: 100000: ff f0 fe ff bl #1048572
+// CHECK1-NEXT: 100004: 00 f0 00 f8 bl #0
+// CHECK1: __Thumbv7ABSLongThunk_tfunch31:
+// CHECK1-NEXT: 100008: 40 f2 01 0c movw r12, #1
+// CHECK1-NEXT: 10000c: c0 f2 10 4c movt r12, #1040
+// CHECK1-NEXT: 100010: 60 47 bx r12
+ FUNCTIONL 00
+ // Create a range extension thunk in .textl
+ bl tfuncl24
+ // We can reuse existing thunk in .text
+ bl tfunch31
+// CHECK2: Disassembly of section .textl:
+// CHECK2-NEXT: tfuncl00:
+// CHECK2-NEXT: 200000: 70 47 bx lr
+// CHECK2-NEXT: 200002: ff f0 ff df bl #9437182
+// CHECK2-NEXT: 200006: ff f6 ff ff bl #-1048578
+ FUNCTIONL 01
+ FUNCTIONL 02
+ FUNCTIONL 03
+ FUNCTIONL 04
+ FUNCTIONL 05
+ FUNCTIONL 06
+ FUNCTIONL 07
+ FUNCTIONL 08
+ FUNCTIONL 09
+// CHECK3: __Thumbv7ABSLongThunk_tfuncl24:
+// CHECK3-NEXT: b00004: 40 f2 01 0c movw r12, #1
+// CHECK3-NEXT: b00008: c0 f2 a0 1c movt r12, #416
+// CHECK3-NEXT: b0000c: 60 47 bx r12
+ FUNCTIONL 10
+ FUNCTIONL 11
+ FUNCTIONL 12
+ FUNCTIONL 13
+ FUNCTIONL 14
+ FUNCTIONL 15
+ FUNCTIONL 16
+ FUNCTIONL 17
+ FUNCTIONL 18
+ FUNCTIONL 19
+ FUNCTIONL 20
+ FUNCTIONL 21
+ FUNCTIONL 22
+ FUNCTIONL 23
+ FUNCTIONL 24
+ FUNCTIONL 25
+ FUNCTIONL 26
+ FUNCTIONL 27
+ FUNCTIONL 28
+ FUNCTIONL 29
+ FUNCTIONL 30
+ FUNCTIONL 31
+ // Create range extension thunks in .textl
+ bl tfuncl00
+ bl tfuncl24
+ // Shouldn't need a thunk
+ bl tfunch00
+// CHECK4: 2100002: 00 f0 05 f8 bl #10
+// CHECK4-NEXT: 2100006: ff f4 fb f7 bl #-7340042
+// CHECK4-NEXT: 210000a: ff f0 f9 ff bl #1048562
+// CHECK4: __Thumbv7ABSLongThunk_tfuncl00:
+// CHECK4-NEXT: 2100010: 40 f2 01 0c movw r12, #1
+// CHECK4-NEXT: 2100014: c0 f2 20 0c movt r12, #32
+// CHECK4-NEXT: 2100018: 60 47 bx r12
+ FUNCTIONH 00
+ // Can reuse existing thunks in .textl
+ bl tfuncl00
+ bl tfuncl24
+ // Shouldn't need a thunk
+ bl tfuncl31
+// CHECK5: Disassembly of section .texth:
+// CHECK5-NEXT: tfunch00:
+// CHECK5-NEXT: 2200000: 70 47 bx lr
+// CHECK5-NEXT: 2200002: 00 f7 05 f8 bl #-1048566
+// CHECK5-NEXT: 2200006: ff f7 fb df bl #-8388618
+// CHECK5-NEXT: 220000a: ff f6 f9 ff bl #-1048590
+ FUNCTIONH 01
+ FUNCTIONH 02
+ FUNCTIONH 03
+ FUNCTIONH 04
+ FUNCTIONH 05
+ FUNCTIONH 06
+ FUNCTIONH 07
+ FUNCTIONH 08
+ FUNCTIONH 09
+ FUNCTIONH 10
+ FUNCTIONH 11
+ FUNCTIONH 12
+ FUNCTIONH 13
+ FUNCTIONH 14
+ FUNCTIONH 15
+ FUNCTIONH 16
+ FUNCTIONH 17
+ FUNCTIONH 18
+ FUNCTIONH 19
+ FUNCTIONH 20
+ FUNCTIONH 21
+ FUNCTIONH 22
+ FUNCTIONH 23
+ FUNCTIONH 24
+ FUNCTIONH 25
+ FUNCTIONH 26
+ FUNCTIONH 27
+ FUNCTIONH 28
+ FUNCTIONH 29
+ FUNCTIONH 30
+ FUNCTIONH 31
+// expect Thunks in .texth
+ bl tfuncl00
+ bl tfunch00
+// CHECK6: tfunch31:
+// CHECK6-NEXT: 4100000: 70 47 bx lr
+// CHECK6-NEXT: 4100002: 00 f0 03 f8 bl #6
+// CHECK6-NEXT: 4100006: 00 f0 06 f8 bl #12
+// CHECK6: __Thumbv7ABSLongThunk_tfuncl00:
+// CHECK6-NEXT: 410000c: 40 f2 01 0c movw r12, #1
+// CHECK6-NEXT: 4100010: c0 f2 20 0c movt r12, #32
+// CHECK6-NEXT: 4100014: 60 47 bx r12
+// CHECK6: __Thumbv7ABSLongThunk_tfunch00:
+// CHECK6-NEXT: 4100016: 40 f2 01 0c movw r12, #1
+// CHECK6-NEXT: 410001a: c0 f2 20 2c movt r12, #544
+// CHECK6-NEXT: 410001e: 60 47 bx r12
diff --git a/test/ELF/arm-thunk-linkerscript-orphan.s b/test/ELF/arm-thunk-linkerscript-orphan.s
new file mode 100644
index 000000000000..f05cc0de9069
--- /dev/null
+++ b/test/ELF/arm-thunk-linkerscript-orphan.s
@@ -0,0 +1,63 @@
+// REQUIRES: arm
+// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %s -o %t
+// RUN: echo "SECTIONS { \
+// RUN: .text_low 0x100000 : { *(.text_low) } \
+// RUN: .text_high 0x2000000 : { *(.text_high) } \
+// RUN: .data : { *(.data) } \
+// RUN: }" > %t.script
+// RUN: ld.lld --script %t.script %t -o %t2 2>&1
+// RUN: llvm-objdump -d -triple=thumbv7a-none-linux-gnueabi %t2 | FileCheck %s
+ .syntax unified
+ .section .text_low, "ax", %progbits
+ .thumb
+ .globl _start
+_start: bx lr
+ .globl low_target
+ .type low_target, %function
+low_target:
+ bl high_target
+ bl orphan_target
+// CHECK: Disassembly of section .text_low:
+// CHECK-NEXT: _start:
+// CHECK-NEXT: 100000: 70 47 bx lr
+// CHECK: low_target:
+// CHECK-NEXT: 100002: 00 f0 03 f8 bl #6
+// CHECK-NEXT: 100006: 00 f0 06 f8 bl #12
+// CHECK: __Thumbv7ABSLongThunk_high_target:
+// CHECK-NEXT: 10000c: 40 f2 01 0c movw r12, #1
+// CHECK-NEXT: 100010: c0 f2 00 2c movt r12, #512
+// CHECK-NEXT: 100014: 60 47 bx r12
+// CHECK: __Thumbv7ABSLongThunk_orphan_target:
+// CHECK-NEXT: 100016: 40 f2 15 0c movw r12, #21
+// CHECK-NEXT: 10001a: c0 f2 00 2c movt r12, #512
+// CHECK-NEXT: 10001e: 60 47 bx r12
+ .section .text_high, "ax", %progbits
+ .thumb
+ .globl high_target
+ .type high_target, %function
+high_target:
+ bl low_target
+ bl orphan_target
+// CHECK: Disassembly of section .text_high:
+// CHECK-NEXT: high_target:
+// CHECK-NEXT: 2000000: 00 f0 02 f8 bl #4
+// CHECK-NEXT: 2000004: 00 f0 06 f8 bl #12
+// CHECK: __Thumbv7ABSLongThunk_low_target:
+// CHECK-NEXT: 2000008: 40 f2 03 0c movw r12, #3
+// CHECK-NEXT: 200000c: c0 f2 10 0c movt r12, #16
+// CHECK-NEXT: 2000010: 60 47 bx r12
+
+ .section orphan, "ax", %progbits
+ .thumb
+ .globl orphan_target
+ .type orphan_target, %function
+orphan_target:
+ bl low_target
+ bl high_target
+// CHECK: Disassembly of section orphan:
+// CHECK-NEXT: orphan_target:
+// CHECK-NEXT: 2000014: ff f7 f8 ff bl #-16
+// CHECK-NEXT: 2000018: ff f7 f2 ff bl #-28
+
+ .data
+ .word 10
diff --git a/test/ELF/arm-thunk-linkerscript-sort.s b/test/ELF/arm-thunk-linkerscript-sort.s
new file mode 100644
index 000000000000..69d176765780
--- /dev/null
+++ b/test/ELF/arm-thunk-linkerscript-sort.s
@@ -0,0 +1,71 @@
+// REQUIRES: arm
+// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %s -o %t
+// RUN: echo "SECTIONS { \
+// RUN: .text 0x100000 : { *(SORT_BY_NAME(.text.*)) } \
+// RUN: }" > %t.script
+// RUN: ld.lld --script %t.script %t -o %t2 2>&1
+// RUN: llvm-objdump -d %t2 -start-address=1048576 -stop-address=1048584 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK1 %s
+// RUN: llvm-objdump -d %t2 -start-address=16777220 -stop-address=16777230 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK2 %s
+
+ .syntax unified
+
+// Test that linkerscript sorting does not apply to Thunks, we expect that the
+// sort will reverse the order of sections presented here.
+
+// Define a function aligned on a megabyte boundary
+ .macro FUNCTION suff
+ .section .text.\suff\(), "ax", %progbits
+ .thumb
+ .balign 0x100000
+ .globl tfunc\suff\()
+ .type tfunc\suff\(), %function
+tfunc\suff\():
+ bx lr
+ .endm
+
+ FUNCTION 31
+ FUNCTION 30
+ FUNCTION 29
+ FUNCTION 28
+ FUNCTION 27
+ FUNCTION 26
+ FUNCTION 25
+ FUNCTION 24
+ FUNCTION 23
+ FUNCTION 22
+ FUNCTION 21
+ FUNCTION 20
+ FUNCTION 19
+ FUNCTION 18
+ FUNCTION 17
+ FUNCTION 16
+ FUNCTION 15
+// CHECK2: __Thumbv7ABSLongThunk_tfunc31:
+// CHECK2-NEXT: 1000004: 40 f2 01 0c movw r12, #1
+// CHECK2-NEXT: 1000008: c0 f2 00 2c movt r12, #512
+// CHECK2-NEXT: 100000c: 60 47 bx r12
+ FUNCTION 14
+ FUNCTION 13
+ FUNCTION 12
+ FUNCTION 11
+ FUNCTION 10
+ FUNCTION 09
+ FUNCTION 08
+ FUNCTION 07
+ FUNCTION 06
+ FUNCTION 05
+ FUNCTION 04
+ FUNCTION 03
+ FUNCTION 02
+ FUNCTION 01
+ .section .text.00, "ax", %progbits
+ .thumb
+ .globl _start
+_start:
+// Expect no range extension needed for tfunc01 and an extension needed for
+// tfunc31
+ bl tfunc01
+ bl tfunc31
+// CHECK1: _start:
+// CHECK1-NEXT: 100000: ff f0 fe ff bl #1048572
+// CHECK1-NEXT: 100004: ff f2 fe d7 bl #15728636
diff --git a/test/ELF/arm-thunk-linkerscript.s b/test/ELF/arm-thunk-linkerscript.s
new file mode 100644
index 000000000000..9aaa29237cbe
--- /dev/null
+++ b/test/ELF/arm-thunk-linkerscript.s
@@ -0,0 +1,78 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
+// RUN: echo "SECTIONS { \
+// RUN: . = SIZEOF_HEADERS; \
+// RUN: .text_low : { *(.text_low) *(.text_low2) } \
+// RUN: .text_high 0x2000000 : { *(.text_high) *(.text_high2) } \
+// RUN: } " > %t.script
+// RUN: ld.lld --script %t.script %t -o %t2 2>&1
+// RUN: llvm-objdump -d -triple=thumbv7a-none-linux-gnueabi %t2 | FileCheck %s
+// REQUIRES: arm
+// Simple test that we can support range extension thunks with linker scripts
+ .syntax unified
+ .section .text_low, "ax", %progbits
+ .thumb
+ .globl _start
+_start: bx lr
+ .globl low_target
+ .type low_target, %function
+low_target:
+ bl high_target
+ bl high_target2
+
+ .section .text_low2, "ax", %progbits
+ .thumb
+ .globl low_target2
+ .type low_target2, %function
+low_target2:
+ bl high_target
+ bl high_target2
+
+// CHECK: Disassembly of section .text_low:
+// CHECK-NEXT: _start:
+// CHECK-NEXT: 94: 70 47 bx lr
+// CHECK: low_target:
+// CHECK-NEXT: 96: 00 f0 03 f8 bl #6
+// CHECK-NEXT: 9a: 00 f0 06 f8 bl #12
+// CHECK: __Thumbv7ABSLongThunk_high_target:
+// CHECK-NEXT: a0: 40 f2 01 0c movw r12, #1
+// CHECK-NEXT: a4: c0 f2 00 2c movt r12, #512
+// CHECK-NEXT: a8: 60 47 bx r12
+// CHECK: __Thumbv7ABSLongThunk_high_target2:
+// CHECK-NEXT: aa: 40 f2 1d 0c movw r12, #29
+// CHECK-NEXT: ae: c0 f2 00 2c movt r12, #512
+// CHECK-NEXT: b2: 60 47 bx r12
+// CHECK: low_target2:
+// CHECK-NEXT: b4: ff f7 f4 ff bl #-24
+// CHECK-NEXT: b8: ff f7 f7 ff bl #-18
+
+ .section .text_high, "ax", %progbits
+ .thumb
+ .globl high_target
+ .type high_target, %function
+high_target:
+ bl low_target
+ bl low_target2
+
+ .section .text_high2, "ax", %progbits
+ .thumb
+ .globl high_target2
+ .type high_target2, %function
+high_target2:
+ bl low_target
+ bl low_target2
+
+// CHECK: Disassembly of section .text_high:
+// CHECK-NEXT: high_target:
+// CHECK-NEXT: 2000000: 00 f0 02 f8 bl #4
+// CHECK-NEXT: 2000004: 00 f0 05 f8 bl #10
+// CHECK: __Thumbv7ABSLongThunk_low_target:
+// CHECK-NEXT: 2000008: 40 f2 97 0c movw r12, #151
+// CHECK-NEXT: 200000c: c0 f2 00 0c movt r12, #0
+// CHECK-NEXT: 2000010: 60 47 bx r12
+// CHECK: __Thumbv7ABSLongThunk_low_target2:
+// CHECK-NEXT: 2000012: 40 f2 b5 0c movw r12, #181
+// CHECK-NEXT: 2000016: c0 f2 00 0c movt r12, #0
+// CHECK-NEXT: 200001a: 60 47 bx r12
+// CHECK: high_target2:
+// CHECK-NEXT: 200001c: ff f7 f4 ff bl #-24
+// CHECK-NEXT: 2000020: ff f7 f7 ff bl #-18
diff --git a/test/ELF/arm-thunk-multipass.s b/test/ELF/arm-thunk-multipass.s
new file mode 100644
index 000000000000..25bf5235f755
--- /dev/null
+++ b/test/ELF/arm-thunk-multipass.s
@@ -0,0 +1,96 @@
+// REQUIRES: arm
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
+// RUN: ld.lld %t -o %t2 2>&1
+// The output file is large, most of it zeroes. We dissassemble only the
+// parts we need to speed up the test and avoid a large output file
+// RUN: llvm-objdump -d %t2 -start-address=1048578 -stop-address=1048586 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK1 %s
+// RUN: llvm-objdump -d %t2 -start-address=16777224 -stop-address=16777254 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK2 %s
+// RUN: llvm-objdump -d %t2 -start-address=17825818 -stop-address=17825828 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK3 %s
+// In this test case a branch that is in range and does not need its range
+// extended can be pushed out of range by another Thunk, necessitating another
+// pass
+
+ .macro FUNCTION suff
+ .section .text.\suff\(), "ax", %progbits
+ .thumb
+ .balign 0x100000
+ .globl tfunc\suff\()
+ .type tfunc\suff\(), %function
+tfunc\suff\():
+ bx lr
+ .endm
+
+ FUNCTION 00
+ .globl _start
+_start:
+ bl target
+ b.w arm_target
+// arm_target is in range but needs an interworking thunk
+// CHECK1: _start:
+// CHECK1-NEXT: 100002: 00 f3 06 d0 bl #15728652
+// CHECK1-NEXT: 100006: ff f2 ff 97 b.w #15728638 <__Thumbv7ABSLongThunk_arm_target>
+ nop
+ nop
+ nop
+ .globl target2
+ .type target2, %function
+ nop
+
+target2:
+ FUNCTION 01
+ FUNCTION 02
+ FUNCTION 03
+ FUNCTION 04
+ FUNCTION 05
+ FUNCTION 06
+ FUNCTION 07
+ FUNCTION 08
+ FUNCTION 09
+ FUNCTION 10
+ FUNCTION 11
+ FUNCTION 12
+ FUNCTION 13
+ FUNCTION 14
+ FUNCTION 15
+
+ .section .text.16, "ax", %progbits
+ .arm
+ .globl arm_target
+ .type arm_target, %function
+arm_target:
+ bx lr
+// CHECK2: __Thumbv7ABSLongThunk_arm_target:
+// CHECK2-NEXT: 1000008: 40 f2 02 0c movw r12, #2
+// CHECK2-NEXT: 100000c: c0 f2 00 1c movt r12, #256
+// CHECK2-NEXT: 1000010: 60 47 bx r12
+// CHECK2: __Thumbv7ABSLongThunk_target:
+// CHECK2-NEXT: 1000012: 40 f2 1b 0c movw r12, #27
+// CHECK2-NEXT: 1000016: c0 f2 10 1c movt r12, #272
+// CHECK2-NEXT: 100001a: 60 47 bx r12
+// CHECK2: __Thumbv7ABSLongThunk_target2:
+// CHECK2-NEXT: 100001c: 40 f2 13 0c movw r12, #19
+// CHECK2-NEXT: 1000020: c0 f2 10 0c movt r12, #16
+// CHECK2-NEXT: 1000024: 60 47 bx r12
+
+ .section .text.17, "ax", %progbits
+// Just enough space so that bl target is in range if no extension thunks are
+// generated.
+
+ .space 0x100000 - 12
+
+ .section .text.18, "ax", %progbits
+ .thumb
+ .globl target
+ .type target, %function
+// target is at maximum ARM branch range away from caller.
+target:
+// Similar case in the backwards direction
+ bl target2
+ nop
+ nop
+ bx lr
+// CHECK3: target:
+// CHECK3-NEXT: 110001a: ff f6 ff ff bl #-1048578
+// CHECK3-NEXT: 110001e: 00 bf nop
+// CHECK3-NEXT: 1100020: 00 bf nop
+// CHECK3-NEXT: 1100022: 70 47 bx lr
diff --git a/test/ELF/arm-thunk-re-add.s b/test/ELF/arm-thunk-re-add.s
new file mode 100644
index 000000000000..760e18f57313
--- /dev/null
+++ b/test/ELF/arm-thunk-re-add.s
@@ -0,0 +1,123 @@
+// REQUIRES: arm
+// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %s -o %t
+// RUN: ld.lld %t --shared -o %t.so
+// The output file is large, most of it zeroes. We dissassemble only the
+// parts we need to speed up the test and avoid a large output file
+// RUN: llvm-objdump -d %t.so -start-address=16777220 -stop-address=16777244 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK1 %s
+// RUN: llvm-objdump -d %t.so -start-address=17825800 -stop-address=17825826 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK2 %s
+// RUN: llvm-objdump -d %t.so -start-address=17825824 -stop-address=17825892 -triple=armv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK3 %s
+
+// A branch to a Thunk that we create on pass N, can drift out of range if
+// other Thunks are added in between. In this case we must create a new Thunk
+// for the branch that is in range. We also need to make sure that if the
+// destination of the Thunk is in the PLT the new Thunk also targets the PLT
+ .syntax unified
+ .thumb
+
+ .macro FUNCTION suff
+ .section .text.\suff\(), "ax", %progbits
+ .thumb
+ .balign 0x80000
+ .globl tfunc\suff\()
+ .type tfunc\suff\(), %function
+tfunc\suff\():
+ bx lr
+ .endm
+
+ .globl imported
+ .type imported, %function
+ .globl imported2
+ .type imported2, %function
+ .globl imported3
+ .type imported3, %function
+.globl imported4
+ .type imported4, %function
+ FUNCTION 00
+ FUNCTION 01
+ FUNCTION 02
+ FUNCTION 03
+ FUNCTION 04
+ FUNCTION 05
+ FUNCTION 06
+ FUNCTION 07
+ FUNCTION 08
+ FUNCTION 09
+ FUNCTION 10
+ FUNCTION 11
+ FUNCTION 12
+ FUNCTION 13
+ FUNCTION 14
+ FUNCTION 15
+ FUNCTION 16
+ FUNCTION 17
+ FUNCTION 18
+ FUNCTION 19
+ FUNCTION 20
+ FUNCTION 21
+ FUNCTION 22
+ FUNCTION 23
+ FUNCTION 24
+ FUNCTION 25
+ FUNCTION 26
+ FUNCTION 27
+ FUNCTION 28
+ FUNCTION 29
+ FUNCTION 30
+ FUNCTION 31
+// Precreated Thunk Pool goes here
+// CHECK1: __ThumbV7PILongThunk_imported:
+// CHECK1-NEXT: 1000004: 40 f2 30 0c movw r12, #48
+// CHECK1-NEXT: 1000008: c0 f2 10 0c movt r12, #16
+// CHECK1-NEXT: 100000c: fc 44 add r12, pc
+// CHECK1-NEXT: 100000e: 60 47 bx r12
+// CHECK1: __ThumbV7PILongThunk_imported2:
+// CHECK1-NEXT: 1000010: 40 f2 34 0c movw r12, #52
+// CHECK1-NEXT: 1000014: c0 f2 10 0c movt r12, #16
+// CHECK1-NEXT: 1000018: fc 44 add r12, pc
+// CHECK1-NEXT: 100001a: 60 47 bx r12
+
+ .section .text.32, "ax", %progbits
+ .space 0x80000
+ .section .text.33, "ax", %progbits
+ .space 0x80000 - 0x14
+ .section .text.34, "ax", %progbits
+ // Need a Thunk to the PLT entry, can use precreated ThunkSection
+ .globl callers
+ .type callers, %function
+callers:
+ b.w imported
+ beq.w imported
+ b.w imported2
+// CHECK2: __ThumbV7PILongThunk_imported:
+// CHECK2-NEXT: 1100008: 40 f2 2c 0c movw r12, #44
+// CHECK2-NEXT: 110000c: c0 f2 00 0c movt r12, #0
+// CHECK2-NEXT: 1100010: fc 44 add r12, pc
+// CHECK2-NEXT: 1100012: 60 47 bx r12
+// CHECK2: callers:
+// CHECK2-NEXT: 1100014: ff f6 f6 bf b.w #-1048596 <__ThumbV7PILongThunk_imported>
+// CHECK2-NEXT: 1100018: 3f f4 f6 af beq.w #-20 <__ThumbV7PILongThunk_imported>
+// CHECK2-NEXT: 110001c: ff f6 f8 bf b.w #-1048592 <__ThumbV7PILongThunk_imported2>
+
+// CHECK3: Disassembly of section .plt:
+// CHECK3-NEXT: $a:
+// CHECK3-NEXT: 1100020: 04 e0 2d e5 str lr, [sp, #-4]!
+// CHECK3-NEXT: 1100024: 00 e6 8f e2 add lr, pc, #0, #12
+// CHECK3-NEXT: 1100028: 00 ea 8e e2 add lr, lr, #0, #20
+// CHECK3-NEXT: 110002c: dc ff be e5 ldr pc, [lr, #4060]!
+// CHECK3: $d:
+// CHECK3-NEXT: 1100030: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECK3-NEXT: 1100034: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECK3-NEXT: 1100038: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECK3-NEXT: 110003c: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECK3: $a:
+// CHECK3-NEXT: 1100040: 00 c6 8f e2 add r12, pc, #0, #12
+// CHECK3-NEXT: 1100044: 00 ca 8c e2 add r12, r12, #0, #20
+// CHECK3-NEXT: 1100048: c4 ff bc e5 ldr pc, [r12, #4036]!
+// CHECK3: $d:
+// CHECK3-NEXT: 110004c: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECK3: $a:
+// CHECK3-NEXT: 1100050: 00 c6 8f e2 add r12, pc, #0, #12
+// CHECK3-NEXT: 1100054: 00 ca 8c e2 add r12, r12, #0, #20
+// CHECK3-NEXT: 1100058: b8 ff bc e5 ldr pc, [r12, #4024]!
+// CHECK3: $d:
+// CHECK3-NEXT: 110005c: d4 d4 d4 d4 .word 0xd4d4d4d4
diff --git a/test/ELF/arm-thunk-toolargesection.s b/test/ELF/arm-thunk-toolargesection.s
new file mode 100644
index 000000000000..28fb94a8ccfd
--- /dev/null
+++ b/test/ELF/arm-thunk-toolargesection.s
@@ -0,0 +1,19 @@
+// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %s -o %t
+// RUN: not ld.lld %t -o %t2 2>&1 | FileCheck %s
+// REQUIRES: arm
+ .syntax unified
+ .balign 0x1000
+ .thumb
+ .text
+ .globl _start
+ .type _start, %function
+_start:
+ bx lr
+
+ .section .text.large1, "ax", %progbits
+ .balign 4
+.space (17 * 1024 * 1024)
+ bl _start
+.space (17 * 1024 * 1024)
+
+// CHECK: error: InputSection too large for range extension thunk {{.*}}.text.large1
diff --git a/test/ELF/arm-tls-gd-nonpreemptible.s b/test/ELF/arm-tls-gd-nonpreemptible.s
index 650c00800269..ebaad4788c76 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 206b65d05af9..a32e26f2aeb9 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 48120fa682da..26e1265568c8 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 47e879102707..629dcd038899 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 2617089b4080..bcee56165958 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 41df72494f8b..788c845b3f8b 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 e8c528b401ca..eb96aa0fad57 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 9fd822aefef7..fc5b72b80f9c 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.s b/test/ELF/as-needed.s
index 37c6103b0ed0..bcfa32d01f66 100644
--- a/test/ELF/as-needed.s
+++ b/test/ELF/as-needed.s
@@ -15,6 +15,10 @@
// RUN: ld.lld --as-needed %t.o %t2.so %t3.so %t4.so -o %t2
// RUN: llvm-readobj -dynamic-table %t2 | FileCheck -check-prefix=CHECK2 %s
+// Test with the .o last
+// RUN: ld.lld --as-needed %t2.so %t3.so %t4.so %t.o -o %t2
+// RUN: llvm-readobj -dynamic-table %t2 | FileCheck -check-prefix=CHECK2 %s
+
// RUN: ld.lld --as-needed %t.o %t2.so --no-as-needed %t3.so %t4.so -o %t2
// RUN: llvm-readobj -dynamic-table %t2 | FileCheck %s
diff --git a/test/ELF/assignment-archive.s b/test/ELF/assignment-archive.s
new file mode 100644
index 000000000000..11753a692130
--- /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/avoid-empty-program-headers.s b/test/ELF/avoid-empty-program-headers.s
index 271d1a303850..731ecce67cbc 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 144fe6aee41b..6527d3dc0def 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,12 +76,12 @@ _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
# CHECK-NEXT: AddressAlignment: 1
-# CHECK-NEXT: EntrySize: 0
+# CHECK-NEXT: EntrySize: 1
# CHECK-NEXT: }
# CHECK-NEXT: Section {
# CHECK-NEXT: Index: 3
@@ -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 4c7a66cf0050..a193529b3487 100644
--- a/test/ELF/basic-mips.s
+++ b/test/ELF/basic-mips.s
@@ -164,7 +164,7 @@ __start:
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
# CHECK-NEXT: AddressAlignment: 1
-# CHECK-NEXT: EntrySize: 0
+# CHECK-NEXT: EntrySize: 1
# CHECK-NEXT: }
# CHECK-NEXT: Section {
# CHECK-NEXT: Index: 8
@@ -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 4a36af99ed72..cda32245fd2b 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
@@ -163,7 +163,7 @@
// CHECK-NEXT: Link: 0
// CHECK-NEXT: Info: 0
// CHECK-NEXT: AddressAlignment: 1
-// CHECK-NEXT: EntrySize: 0
+// CHECK-NEXT: EntrySize: 1
// CHECK-NEXT: SectionData (
// CHECK-NEXT: 0000: 4C4C4420 312E3000 |LLD 1.0.|
// CHECK-NEXT: )
diff --git a/test/ELF/basic-sparcv9.s b/test/ELF/basic-sparcv9.s
index 983224c52913..75c20476a43b 100644
--- a/test/ELF/basic-sparcv9.s
+++ b/test/ELF/basic-sparcv9.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: 0x100080
+# CHECK-NEXT: SectionHeaderOffset: 0x102070
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: HeaderSize: 64
@@ -76,12 +76,12 @@ _start:
# CHECK-NEXT: SHF_STRINGS (0x20)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x10000C
+# 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: EntrySize: 1
# CHECK-NEXT: }
# CHECK-NEXT: Section {
# CHECK-NEXT: Index: 3
@@ -90,7 +90,7 @@ _start:
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x100018
+# CHECK-NEXT: Offset: 0x102008
# CHECK-NEXT: Size: 48
# CHECK-NEXT: Link: 5
# CHECK-NEXT: Info: 1
@@ -104,7 +104,7 @@ _start:
# CHECK-NEXT: Flags [ (0x0)
# CHECK-NEXT: ]
# CHECK-NEXT: Address: 0x0
-# CHECK-NEXT: Offset: 0x100048
+# CHECK-NEXT: Offset: 0x102038
# 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: 0x100072
+# CHECK-NEXT: Offset: 0x102062
# CHECK-NEXT: Size: 8
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
@@ -176,8 +176,8 @@ _start:
# CHECK-NEXT: Offset: 0x100000
# CHECK-NEXT: VirtualAddress: 0x200000
# CHECK-NEXT: PhysicalAddress: 0x200000
-# CHECK-NEXT: FileSize: 12
-# CHECK-NEXT: MemSize: 12
+# CHECK-NEXT: FileSize: 8192
+# CHECK-NEXT: MemSize: 8192
# CHECK-NEXT: Flags [ (0x5)
# CHECK-NEXT: PF_R (0x4)
# CHECK-NEXT: PF_X (0x1)
diff --git a/test/ELF/basic.s b/test/ELF/basic.s
index c62a516c1bf6..a62f3dc0e853 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,12 +78,12 @@ _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
# CHECK-NEXT: AddressAlignment: 1
-# CHECK-NEXT: EntrySize: 0
+# CHECK-NEXT: EntrySize: 1
# CHECK-NEXT: }
# CHECK-NEXT: Section {
# CHECK-NEXT: Index: 3
@@ -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 cbf67eec8b7d..071a06332f0a 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,12 +75,12 @@ _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
# CHECK-NEXT: AddressAlignment: 1
-# CHECK-NEXT: EntrySize: 0
+# CHECK-NEXT: EntrySize: 1
# CHECK-NEXT: }
# CHECK-NEXT: Section {
# CHECK-NEXT: Index: 3
@@ -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/basic64be.s b/test/ELF/basic64be.s
index c39669bfd547..d16f4a074175 100644
--- a/test/ELF/basic64be.s
+++ b/test/ELF/basic64be.s
@@ -175,7 +175,7 @@ _start:
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
# CHECK-NEXT: AddressAlignment: 1
-# CHECK-NEXT: EntrySize: 0
+# CHECK-NEXT: EntrySize: 1
# CHECK-NEXT: SectionData (
# CHECK-NEXT: 0000: 4C4C4420 312E3000 |LLD 1.0.|
# CHECK-NEXT: )
diff --git a/test/ELF/build-id.s b/test/ELF/build-id.s
index 2d193478df71..9447a14d4e8a 100644
--- a/test/ELF/build-id.s
+++ b/test/ELF/build-id.s
@@ -2,6 +2,9 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: ld.lld --build-id %t -o %t2
+# RUN: llvm-readobj -s %t2 | FileCheck -check-prefix=ALIGN %s
+
# RUN: ld.lld --build-id %t -o %t2 -threads
# RUN: llvm-objdump -s %t2 | FileCheck -check-prefix=DEFAULT %s
# RUN: ld.lld --build-id %t -o %t2 -no-threads
@@ -45,18 +48,30 @@ _start:
.section .note.test, "a", @note
.quad 42
+# ALIGN: Name: .note.gnu.build-id
+# ALIGN-NEXT: Type: SHT_NOTE
+# ALIGN-NEXT: Flags [
+# ALIGN-NEXT: SHF_ALLOC
+# ALIGN-NEXT: ]
+# ALIGN-NEXT: Address:
+# ALIGN-NEXT: Offset: [[_:0x[0-9A-Z]*(0|4|8|C)$]]
+# ALIGN-NEXT: Size:
+# ALIGN-NEXT: Link:
+# ALIGN-NEXT: Info:
+# ALIGN-NEXT: AddressAlignment: 4
+
# 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: 894c04e8 fbf5556b
# MD5: Contents of section .note.gnu.build-id:
# MD5-NEXT: 04000000 10000000 03000000 474e5500 ............GNU.
-# MD5-NEXT: fc
+# MD5-NEXT: 6a51bbd7 9e8ee3f9 2e02d213 711cfec9
# SHA1: Contents of section .note.gnu.build-id:
# SHA1-NEXT: 04000000 14000000 03000000 474e5500 ............GNU.
-# SHA1-NEXT: 55b1eedb 03b588e1 09987d1d e9a79be7
+# SHA1-NEXT: 9a8618b1 d6fd0e5c eda73dd8 76de5596
# 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 000000000000..8c97e99af4dc
--- /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 8018ff89bbf9..44c08fe9cef9 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 000000000000..99fde9ea3c39
--- /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 000000000000..165bf625394e
--- /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 000000000000..e161a635c077
--- /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 c8011a0a5088..7f241ee4d65b 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 5fb7ee515dcb..a7933e130b77 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 000000000000..c67bc9201803
--- /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 d96ebdcb30b4..aad8cd02b3b1 100644
--- a/test/ELF/compressed-debug-input.s
+++ b/test/ELF/compressed-debug-input.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 {
@@ -59,13 +59,13 @@
# DATA-NEXT: Link: 0
# DATA-NEXT: Info: 0
# DATA-NEXT: AddressAlignment: 1
-# DATA-NEXT: EntrySize: 0
+# DATA-NEXT: EntrySize: 1
# DATA-NEXT: SectionData (
-# DATA-NEXT: 0000: 73686F72 7420756E 7369676E 65642069 |short unsigned i|
-# DATA-NEXT: 0010: 6E740075 6E736967 6E656420 696E7400 |nt.unsigned int.|
-# DATA-NEXT: 0020: 6C6F6E67 20756E73 69676E65 6420696E |long unsigned in|
-# DATA-NEXT: 0030: 74006368 61720075 6E736967 6E656420 |t.char.unsigned |
-# DATA-NEXT: 0040: 63686172 00 |char.|
+# DATA-NEXT: 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/conflict-debug-variable.s b/test/ELF/conflict-debug-variable.s
new file mode 100644
index 000000000000..297ed4bbe1ea
--- /dev/null
+++ b/test/ELF/conflict-debug-variable.s
@@ -0,0 +1,144 @@
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: llvm-dwarfdump %t.o | FileCheck -check-prefix=INPUT %s
+# RUN: not ld.lld %t.o %t.o -o %t 2>&1 | FileCheck %s
+
+# INPUT: .debug_info contents:
+# INPUT: DW_TAG_variable
+# INPUT-NEXT: DW_AT_name ("foo")
+# INPUT-NEXT: DW_AT_decl_file ("1.c")
+# INPUT-NEXT: DW_AT_decl_line (1)
+# INPUT-NEXT: DW_AT_type (cu + 0x0032 "int")
+# INPUT-NEXT: DW_AT_external (true)
+# INPUT-NEXT: DW_AT_location (DW_OP_addr 0x0)
+# INPUT: DW_TAG_variable
+# INPUT-NEXT: DW_AT_name ("bar")
+# INPUT-NEXT: DW_AT_decl_file ("1.c")
+# INPUT-NEXT: DW_AT_decl_line (2)
+# INPUT-NEXT: DW_AT_type (cu + 0x0032 "int")
+# INPUT-NEXT: DW_AT_external (true)
+# INPUT-NEXT: DW_AT_location (DW_OP_addr 0x0)
+
+## Check we use information from .debug_info in messages.
+# CHECK: duplicate symbol: bar
+# CHECK-NEXT: >>> defined at 1.c:2
+# CHECK-NEXT: >>> {{.*}}:(bar)
+# CHECK-NEXT: >>> defined at 1.c:2
+# CHECK-NEXT: >>> {{.*}}:(.data+0x0)
+# CHECK: duplicate symbol: foo
+# CHECK-NEXT: >>> defined at 1.c:1
+# CHECK-NEXT: >>> {{.*}}:(foo)
+# CHECK-NEXT: >>> defined at 1.c:1
+# CHECK-NEXT: >>> {{.*}}:(.bss+0x0)
+
+## Check that stripping debug sections does not break error reporting.
+# RUN: not ld.lld --strip-debug %t.o %t.o -o %t 2>&1 | FileCheck %s
+
+# Used reduced output from following code and gcc 7.1.0
+# to produce this input file:
+# Source (1.c):
+# int foo = 0;
+# int bar = 1;
+# Invocation: g++ -g -S 1.c
+
+.bss
+.globl foo
+.type foo, @object
+.size foo, 4
+foo:
+
+.data
+.globl bar
+.type bar, @object
+.size bar, 4
+bar:
+
+.text
+.file 1 "1.c"
+
+.section .debug_info,"",@progbits
+ .long 0x4b # Compile Unit: length = 0x0000004b)
+ .value 0x4 # version = 0x0004
+ .long 0 # abbr_offset = 0x0
+ .byte 0x8 # addr_size = 0x08
+
+ .uleb128 0x1 # DW_TAG_compile_unit [1] *
+ .long 0 # DW_AT_producer [DW_FORM_strp] ( .debug_str[0x00000000] = )
+ .byte 0x4 # DW_AT_language [DW_FORM_data1] (DW_LANG_C_plus_plus)
+ .string "1.c" # DW_AT_name [DW_FORM_string] ("1.c")
+ .long 0 # DW_AT_comp_dir [DW_FORM_strp] ( .debug_str[0x00000000] = )
+ .long 0 # DW_AT_stmt_list [DW_FORM_sec_offset] (0x00000000)
+
+ .uleb128 0x2 # DW_TAG_variable [2]
+ .string "foo" # DW_AT_name [DW_FORM_string] ("foo")
+ .byte 0x1 # DW_AT_decl_file [DW_FORM_data1] ("1.c")
+ .byte 0x1 # DW_AT_decl_line [DW_FORM_data1] (1)
+ .long 0x32 # DW_AT_type [DW_FORM_ref4] (cu + 0x0032 => {0x00000032})
+ .uleb128 0x9 # DW_AT_external [DW_FORM_flag_present] (true)
+ .byte 0x3
+ .quad foo # DW_AT_location [DW_FORM_exprloc] (DW_OP_addr 0x0)
+
+ .uleb128 0x3 # DW_TAG_base_type [3]
+ .byte 0x4 # DW_AT_byte_size [DW_FORM_data1] (0x04)
+ .byte 0x5 # DW_AT_encoding [DW_FORM_data1] (DW_ATE_signed)
+ .string "int" # DW_AT_name [DW_FORM_string] ("int")
+
+ .uleb128 0x2 # DW_TAG_variable [2]
+ .string "bar" # DW_AT_name [DW_FORM_string] ("bar")
+ .byte 0x1 # DW_AT_decl_file [DW_FORM_data1] ("1.c")
+ .byte 0x2 # DW_AT_decl_line [DW_FORM_data1] (2)
+ .long 0x32 # DW_AT_type [DW_FORM_ref4] (cu + 0x0032 => {0x00000032})
+ .uleb128 0x9 # DW_AT_external [DW_FORM_flag_present] (true)
+ .byte 0x3
+ .quad bar # DW_AT_location [DW_FORM_exprloc] (DW_OP_addr 0x0)
+ .byte 0 # END
+
+
+.section .debug_abbrev,"",@progbits
+ .uleb128 0x1 # Abbreviation code.
+ .uleb128 0x11 # DW_TAG_compile_unit
+
+ .byte 0x1 # ID
+ .uleb128 0x25 # DW_AT_producer, DW_FORM_strp
+ .uleb128 0xe
+ .uleb128 0x13 # DW_AT_language, DW_FORM_data1
+ .uleb128 0xb
+ .uleb128 0x3 # DW_AT_name, DW_FORM_string
+ .uleb128 0x8
+ .uleb128 0x1b # DW_AT_comp_dir, DW_FORM_strp
+ .uleb128 0xe
+ .uleb128 0x10 # DW_AT_stmt_list, DW_FORM_sec_offset
+ .uleb128 0x17
+ .byte 0
+ .byte 0
+
+ .uleb128 0x2 # ID
+ .uleb128 0x34 # DW_TAG_variable, DW_CHILDREN_no
+ .byte 0
+ .uleb128 0x3 # DW_AT_name, DW_FORM_string
+ .uleb128 0x8
+ .uleb128 0x3a # DW_AT_decl_file, DW_FORM_data1
+ .uleb128 0xb
+ .uleb128 0x3b # DW_AT_decl_line, DW_FORM_data1
+ .uleb128 0xb
+ .uleb128 0x49 # DW_AT_type, DW_FORM_ref4
+ .uleb128 0x13
+ .uleb128 0x3f # DW_AT_external, DW_FORM_flag_present
+ .uleb128 0x19
+ .uleb128 0x2 # DW_AT_location, DW_FORM_exprloc
+ .uleb128 0x18
+ .byte 0
+ .byte 0
+
+ .uleb128 0x3 # ID
+ .uleb128 0x24 # DW_TAG_base_type, DW_CHILDREN_no
+ .byte 0
+ .uleb128 0xb # DW_AT_byte_size, DW_FORM_data1
+ .uleb128 0xb
+ .uleb128 0x3e # DW_AT_encoding, DW_FORM_data1
+ .uleb128 0xb
+ .uleb128 0x3 # DW_AT_name, DW_FORM_string
+ .uleb128 0x8
+ .byte 0
+ .byte 0
+ .byte 0
+
diff --git a/test/ELF/conflict-debug-variable2.s b/test/ELF/conflict-debug-variable2.s
new file mode 100644
index 000000000000..1fb9b09443b4
--- /dev/null
+++ b/test/ELF/conflict-debug-variable2.s
@@ -0,0 +1,160 @@
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+
+# RUN: llvm-dwarfdump -v %t.o | FileCheck -check-prefix=INPUT %s
+# INPUT: .debug_info contents:
+# INPUT: DW_TAG_variable
+# INPUT-NEXT: DW_AT_name [DW_FORM_strp] ( .debug_str[0x00000027] = "foo")
+# INPUT-NEXT: DW_AT_type [DW_FORM_ref4] (cu + 0x0033 => {0x00000033} "int")
+# INPUT-NEXT: DW_AT_external [DW_FORM_flag_present] (true)
+# INPUT-NEXT: DW_AT_decl_file [DW_FORM_data1] ("/home/path/test.c")
+# INPUT-NEXT: DW_AT_decl_line [DW_FORM_data1] (1)
+# INPUT-NEXT: DW_AT_location [DW_FORM_exprloc] (DW_OP_addr 0x0)
+# INPUT: DW_TAG_variable
+# INPUT-NEXT: DW_AT_name [DW_FORM_strp] ( .debug_str[0x0000002f] = "bar")
+# INPUT-NEXT: DW_AT_type [DW_FORM_ref4] (cu + 0x0033 => {0x00000033} "int")
+# INPUT-NEXT: DW_AT_external [DW_FORM_flag_present] (true)
+# INPUT-NEXT: DW_AT_decl_file [DW_FORM_data1] ("/home/path/test.c")
+# INPUT-NEXT: DW_AT_decl_line [DW_FORM_data1] (2)
+# INPUT-NEXT: DW_AT_location [DW_FORM_exprloc] (DW_OP_addr 0x0)
+
+## Check we use information from .debug_info in messages.
+# RUN: not ld.lld %t.o %t.o -o %t 2>&1 | FileCheck %s
+# CHECK: duplicate symbol: bar
+# CHECK-NEXT: >>> defined at test.c:2
+# CHECK-NEXT: >>> {{.*}}:(bar)
+# CHECK-NEXT: >>> defined at test.c:2
+# CHECK-NEXT: >>> {{.*}}:(.data+0x0)
+# CHECK: duplicate symbol: foo
+# CHECK-NEXT: >>> defined at test.c:1
+# CHECK-NEXT: >>> {{.*}}:(foo)
+# CHECK-NEXT: >>> defined at test.c:1
+# CHECK-NEXT: >>> {{.*}}:(.bss+0x0)
+
+# Used reduced output from following code and clang
+# version 6.0.0 (trunk 316661) to produce this input file:
+# Source (test.c):
+# int foo = 0;
+# int bar = 1;
+# Invocation: clang -g -S test.c
+
+.text
+.file "test.c"
+.file 1 "test.c"
+
+.type foo,@object
+.bss
+.globl foo
+.p2align 2
+foo:
+ .long 0
+ .size foo, 4
+
+.type bar,@object
+.data
+.globl bar
+.p2align 2
+bar:
+ .long 1
+ .size bar, 4
+
+.section .debug_str,"MS",@progbits,1
+.Linfo_string0:
+ .asciz "clang version 6.0.0"
+.Linfo_string1:
+ .asciz "test.c"
+.Linfo_string2:
+ .asciz "/home/path/"
+.Linfo_string3:
+ .asciz "foo"
+.Linfo_string4:
+ .asciz "int"
+.Linfo_string5:
+ .asciz "bar"
+
+.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 76 # 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:0x45 DW_TAG_compile_unit
+ .long .Linfo_string0 # DW_AT_producer
+ .short 12 # DW_AT_language
+ .long .Linfo_string1 # DW_AT_name
+ .long 0 # DW_AT_stmt_list
+ .long .Linfo_string2 # DW_AT_comp_dir
+
+ .byte 2 # Abbrev [2] 0x1e:0x15 DW_TAG_variable
+ .long .Linfo_string3 # DW_AT_name
+ .long 51 # DW_AT_type
+ .byte 1 # DW_AT_decl_file
+ .byte 1 # DW_AT_decl_line
+ .byte 9 # DW_AT_location
+ .byte 3
+ .quad foo
+
+ .byte 3 # Abbrev [3] 0x33:0x7 DW_TAG_base_type
+ .long .Linfo_string4 # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+
+ .byte 2 # Abbrev [2] 0x3a:0x15 DW_TAG_variable
+ .long .Linfo_string5 # DW_AT_name
+ .long 51 # DW_AT_type
+ .byte 1 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .byte 9 # DW_AT_location
+ .byte 3
+ .quad bar
+ .byte 0 # End Of Children Mark
diff --git a/test/ELF/copy-errors.s b/test/ELF/copy-errors.s
index d0d6abdf68da..0af4638120d1 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-rel-abs.s b/test/ELF/copy-rel-abs.s
new file mode 100644
index 000000000000..37a2c4323819
--- /dev/null
+++ b/test/ELF/copy-rel-abs.s
@@ -0,0 +1,47 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/copy-rel-abs.s -o %t1.o
+// RUN: ld.lld --hash-style=gnu -shared %t1.o -o %t1.so
+// RUN: llvm-readelf --dyn-symbols %t1.so | FileCheck --check-prefix=SYMS %s
+
+// The symbols have the same st_value, but one is ABS.
+// SYMS: 0000000000001000 {{.*}} 4 bar
+// SYMS: 0000000000001000 {{.*}} 4 foo
+// SYMS: 0000000000001000 {{.*}} ABS zed
+
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t2.o
+// RUN: ld.lld %t2.o %t1.so -o %t2
+// RUN: llvm-readobj --dyn-symbols %t2 | FileCheck %s
+
+// CHECK: DynamicSymbols [
+// CHECK-NEXT: Symbol {
+// CHECK-NEXT: Name:
+// CHECK-NEXT: Value:
+// CHECK-NEXT: Size:
+// CHECK-NEXT: Binding:
+// CHECK-NEXT: Type:
+// CHECK-NEXT: Other:
+// CHECK-NEXT: Section:
+// CHECK-NEXT: }
+// CHECK-NEXT: Symbol {
+// CHECK-NEXT: Name: foo
+// CHECK-NEXT: Value:
+// CHECK-NEXT: Size:
+// CHECK-NEXT: Binding:
+// CHECK-NEXT: Type:
+// CHECK-NEXT: Other:
+// CHECK-NEXT: Section: .bss.rel.ro
+// CHECK-NEXT: }
+// CHECK-NEXT: Symbol {
+// CHECK-NEXT: Name: bar
+// CHECK-NEXT: Value:
+// CHECK-NEXT: Size:
+// CHECK-NEXT: Binding:
+// CHECK-NEXT: Type:
+// CHECK-NEXT: Other:
+// CHECK-NEXT: Section: .bss.rel.ro
+// CHECK-NEXT: }
+// CHECK-NEXT: ]
+
+.global _start
+_start:
+.quad foo
diff --git a/test/ELF/copy-rel-large.s b/test/ELF/copy-rel-large.s
new file mode 100644
index 000000000000..035eca3bf10d
--- /dev/null
+++ b/test/ELF/copy-rel-large.s
@@ -0,0 +1,20 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/copy-rel-large.s -o %t1.o
+// RUN: ld.lld -shared %t1.o -o %t1.so
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t2.o
+// RUN: ld.lld %t2.o %t1.so -o %t2
+// RUN: llvm-readobj --dyn-symbols %t2 | FileCheck %s
+
+ .global _start
+_start:
+ .quad foo
+
+// CHECK: Symbol {
+// CHECK: Name: foo
+// CHECK-NEXT: Value:
+// CHECK-NEXT: Size: 4294967297
+// CHECK-NEXT: Binding:
+// CHECK-NEXT: Type:
+// CHECK-NEXT: Other:
+// CHECK-NEXT: Section: .bss.rel.ro
+// CHECK-NEXT: }
diff --git a/test/ELF/copy-rel-pie.s b/test/ELF/copy-rel-pie.s
index 769a2431d850..dcccf8e30b1d 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 000000000000..d37f272f445d
--- /dev/null
+++ b/test/ELF/corrupted-version-reference.s
@@ -0,0 +1,14 @@
+# 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
+
+# CHECK: error: corrupt input file: version definition index 0 for symbol _Jv_RegisterClasses is out of bounds
+# CHECK-NEXT: >>> defined in {{.*}}/corrupt-version-reference.so
+
+.globl __start
+__start:
+ dla $a0, __cxa_finalize
+ nop
diff --git a/test/ELF/debug-gc.s b/test/ELF/debug-gc.s
index 8bcfde16a7b7..5ec43f67829e 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/defsym-dynamic.s b/test/ELF/defsym-dynamic.s
new file mode 100644
index 000000000000..f69e835af34c
--- /dev/null
+++ b/test/ELF/defsym-dynamic.s
@@ -0,0 +1,10 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld -o %t.so -shared %t.o --defsym=foo2=foo1
+# RUN: llvm-readobj --dyn-symbols %t.so | FileCheck %s
+
+# CHECK: Name: foo1
+# CHECK: Name: foo2
+
+.globl foo1
+ foo1 = 0x123
diff --git a/test/ELF/defsym.s b/test/ELF/defsym.s
index b821484261b2..2abc08f99dfc 100644
--- a/test/ELF/defsym.s
+++ b/test/ELF/defsym.s
@@ -19,7 +19,7 @@
# CHECK-NEXT: Section: Absolute
# CHECK-NEXT: }
# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: foo1
+# CHECK-NEXT: Name: foo2
# CHECK-NEXT: Value: 0x123
# CHECK-NEXT: Size:
# CHECK-NEXT: Binding: Global
@@ -33,11 +33,43 @@
# USE-NEXT: _start:
# USE-NEXT: movl $0x123, %edx
-# RUN: not ld.lld -o %t %t.o --defsym=foo2=1 2>&1 | FileCheck %s -check-prefix=ERR1
-# ERR1: error: --defsym: symbol name expected, but got 1
+# RUN: ld.lld -o %t %t.o --defsym=foo2=1
+# RUN: llvm-readobj -t -s %t | FileCheck %s --check-prefix=ABS
-# RUN: not ld.lld -o %t %t.o --defsym=foo2=und 2>&1 | FileCheck %s -check-prefix=ERR2
-# ERR2: error: -defsym: undefined symbol: und
+# ABS: Symbol {
+# ABS: Name: foo2
+# ABS-NEXT: Value: 0x1
+# ABS-NEXT: Size:
+# ABS-NEXT: Binding: Global
+# ABS-NEXT: Type:
+# ABS-NEXT: Other:
+# ABS-NEXT: Section: Absolute
+# ABS-NEXT: }
+
+# RUN: ld.lld -o %t %t.o --defsym=foo2=foo1+5
+# RUN: llvm-readobj -t -s %t | FileCheck %s --check-prefix=EXPR
+
+# EXPR: Symbol {
+# EXPR: Name: foo1
+# EXPR-NEXT: Value: 0x123
+# EXPR-NEXT: Size:
+# EXPR-NEXT: Binding: Global
+# EXPR-NEXT: Type:
+# EXPR-NEXT: Other:
+# EXPR-NEXT: Section: Absolute
+# EXPR-NEXT: }
+# EXPR-NEXT: Symbol {
+# EXPR-NEXT: Name: foo2
+# EXPR-NEXT: Value: 0x128
+# EXPR-NEXT: Size:
+# EXPR-NEXT: Binding: Global
+# EXPR-NEXT: Type:
+# EXPR-NEXT: Other:
+# EXPR-NEXT: Section: Absolute
+# EXPR-NEXT: }
+
+# RUN: not ld.lld -o %t %t.o --defsym=foo2=und 2>&1 | FileCheck %s -check-prefix=ERR
+# ERR: error: -defsym:1: symbol not found: und
.globl foo1
foo1 = 0x123
diff --git a/test/ELF/driver-access.test b/test/ELF/driver-access.test
index 5209483ae480..46b87c1f7e5d 100644
--- a/test/ELF/driver-access.test
+++ b/test/ELF/driver-access.test
@@ -7,7 +7,7 @@
# RUN: mkdir -p %t.dir
# RUN: chmod 100 %t.dir
# RUN: cd %t.dir
-# RUN: ld.lld %t.o -o %t.exe
+# RUN: ld.lld %t.o -o %t.exe -M
# RUN: chmod 755 %t.dir
.globl _start
diff --git a/test/ELF/driver.test b/test/ELF/driver.test
index d876218130c3..ac324cbaac45 100644
--- a/test/ELF/driver.test
+++ b/test/ELF/driver.test
@@ -18,10 +18,10 @@
# HELP: : supported targets:{{.*}} elf
# RUN: ld.lld --version 2>&1 | FileCheck -check-prefix=VERSION %s
+# RUN: ld.lld -v 2>&1 | FileCheck -check-prefix=VERSION %s
+# RUN: not ld.lld -v xyz 2>&1 | FileCheck -check-prefix=VERSION %s
# VERSION: LLD {{.*}} (compatible with GNU linkers)
-# RUN: not ld.lld -v 2>&1 | FileCheck -check-prefix=VERSION %s
-
## Attempt to link DSO with -r
# RUN: ld.lld -shared %t -o %t.so
# RUN: not ld.lld -r %t.so %t -o %tfail 2>&1 | FileCheck -check-prefix=ERR %s
diff --git a/test/ELF/duplicated-synthetic-sym.s b/test/ELF/duplicated-synthetic-sym.s
index cfd8642d2d17..92de33ec6278 100644
--- a/test/ELF/duplicated-synthetic-sym.s
+++ b/test/ELF/duplicated-synthetic-sym.s
@@ -1,9 +1,10 @@
// 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)
+// CHECK: defined at <internal>:(.data+0x0)
.globl _binary_duplicated_synthetic_sym_s_start
_binary_duplicated_synthetic_sym_s_start:
diff --git a/test/ELF/dynamic-got.s b/test/ELF/dynamic-got.s
index c50c9022329a..385394b9d342 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 000000000000..5686ce0c431b
--- /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 000000000000..2bb10a3ed0a6
--- /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 000000000000..f7f72afd0c3b
--- /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-wildcard.s b/test/ELF/dynamic-list-wildcard.s
new file mode 100644
index 000000000000..cd7ed71771ae
--- /dev/null
+++ b/test/ELF/dynamic-list-wildcard.s
@@ -0,0 +1,53 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+# RUN: echo "{ foo1*; };" > %t.list
+# RUN: ld.lld --hash-style=sysv -pie --dynamic-list %t.list %t -o %t.exe
+# RUN: llvm-readobj -dyn-symbols %t.exe | 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: foo1@ (1)
+# CHECK-NEXT: Value: 0x1000
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Global (0x1)
+# CHECK-NEXT: Type: None (0x0)
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .text (0x4)
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: foo11@ (6)
+# CHECK-NEXT: Value: 0x1001
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Global (0x1)
+# CHECK-NEXT: Type: None (0x0)
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .text (0x4)
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+.globl foo1
+foo1:
+ ret
+
+.globl foo11
+foo11:
+ ret
+
+.globl foo2
+foo2:
+ ret
+
+.globl _start
+_start:
+ retq
diff --git a/test/ELF/dynamic-list.s b/test/ELF/dynamic-list.s
index 7cd587380e59..888508e270a7 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 000000000000..e5ad26e3fa44
--- /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 23b068ff839f..ecdbfeb6658e 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 939093c17b41..4d95e41fb803 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
diff --git a/test/ELF/dynstr-no-rosegment.s b/test/ELF/dynstr-no-rosegment.s
new file mode 100644
index 000000000000..0e12721dac44
--- /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 000000000000..947f526e0575
--- /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 9d3a9ffe304f..b162d27335ac 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 3b0ba49ad1af..2358399857de 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/edata-no-bss.s b/test/ELF/edata-no-bss.s
new file mode 100644
index 000000000000..5127c4710fa9
--- /dev/null
+++ b/test/ELF/edata-no-bss.s
@@ -0,0 +1,18 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld %t.o -o %t --gc-sections
+# RUN: llvm-objdump -t -section-headers %t | FileCheck %s
+
+# CHECK: .data 00000008 0000000000202000 DATA
+
+# CHECK: 0000000000202008 .data 00000000 _edata
+
+.text
+.globl _start
+_start:
+.long .data - .
+
+.data
+.quad 0
+
+.globl _edata
diff --git a/test/ELF/eh-align-cie.s b/test/ELF/eh-align-cie.s
index 343dea5004f0..9e6ac2dc17be 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 618f5e1a9d7d..135f8119662a 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-icf-fde.s b/test/ELF/eh-frame-hdr-icf-fde.s
new file mode 100644
index 000000000000..54c2cd8bf8c9
--- /dev/null
+++ b/test/ELF/eh-frame-hdr-icf-fde.s
@@ -0,0 +1,95 @@
+# REQUIRES: x86
+
+## Testcase checks that we correctly deduplicate FDEs when ICF
+## merges two sections.
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: ld.lld %t -o %t2 --icf=all --eh-frame-hdr
+# RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=OBJ
+# RUN: llvm-readobj -s -section-data %t2 | FileCheck %s
+
+# OBJ: Relocations [
+# OBJ-NEXT: Section {{.*}} .rela.eh_frame {
+# OBJ-NEXT: 0x20 R_X86_64_PC32 .text.f1 0x0
+# OBJ-NEXT: 0x34 R_X86_64_PC32 .text.f1 0x2
+# OBJ-NEXT: 0x48 R_X86_64_PC32 .text.f2 0x0
+# OBJ-NEXT: 0x5C R_X86_64_PC32 .text.f2 0x2
+# OBJ-NEXT: }
+# OBJ-NEXT: ]
+
+# CHECK: Section {
+# CHECK: Index: 1
+# CHECK: Name: .eh_frame_hdr
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x200158
+# CHECK-NEXT: Offset: 0x158
+# CHECK-NEXT: Size: 28
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment:
+# CHECK-NEXT: EntrySize: 0
+# CHECK-NEXT: SectionData (
+# CHECK-NEXT: 0000: 011B033B 1C000000 02000000 A80E0000
+## ^ ^-- FDE(1) PC
+## ^-- Number of FDEs
+# CHECK-NEXT: 0010: 38000000 AA0E0000 50000000
+## ^-- FDE(2) PC
+# CHECK-NEXT: )
+# CHECK-NEXT: }
+## FDE(1) == 0x201000 - .eh_frame_hdr(0x200158) = 0xEA8
+## FDE(2) == 0x201000 - .eh_frame_hdr(0x200158) + 2(relocation addend) = 0xEAA
+
+## Check .eh_frame contains CIE and two FDEs remaining after ICF.
+# CHECK-NEXT: Section {
+# CHECK-NEXT: Index: 2
+# CHECK-NEXT: Name: .eh_frame
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x200178
+# CHECK-NEXT: Offset: 0x178
+# CHECK-NEXT: Size: 72
+# CHECK-NEXT: Link: 0
+# CHECK-NEXT: Info: 0
+# CHECK-NEXT: AddressAlignment: 8
+# CHECK-NEXT: EntrySize: 0
+# CHECK-NEXT: SectionData (
+# CHECK-NEXT: 0000: 14000000 00000000 017A5200 01781001
+# CHECK-NEXT: 0010: 1B0C0708 90010000 14000000 1C000000
+# CHECK-NEXT: 0020: 680E0000 01000000 00000000 00000000
+# CHECK-NEXT: 0030: 14000000 34000000 520E0000 01000000
+# CHECK-NEXT: 0040: 00000000 00000000
+# CHECK-NEXT: )
+# CHECK-NEXT: }
+
+# CHECK: Section {
+# CHECK: Index:
+# CHECK: Name: .text
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_EXECINSTR
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x201000
+
+.section .text.f1, "ax"
+.cfi_startproc
+nop
+.cfi_endproc
+nop
+.cfi_startproc
+ret
+.cfi_endproc
+
+.section .text.f2, "ax"
+.cfi_startproc
+nop
+.cfi_endproc
+nop
+.cfi_startproc
+ret
+.cfi_endproc
diff --git a/test/ELF/eh-frame-hdr-icf.s b/test/ELF/eh-frame-hdr-icf.s
index 1b42285f5d32..0ef3f97de8d3 100644
--- a/test/ELF/eh-frame-hdr-icf.s
+++ b/test/ELF/eh-frame-hdr-icf.s
@@ -2,17 +2,18 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
# RUN: ld.lld %t -o %t2 --icf=all --eh-frame-hdr
-# RUN: llvm-objdump -s %t2 | FileCheck %s
+# RUN: llvm-objdump -s -section-headers %t2 | FileCheck %s
+
+## Check .eh_frame_hdr contains single FDE and no garbage data at tail.
+# CHECK: Sections:
+# CHECK: Idx Name Size
+# CHECK: .eh_frame_hdr 00000014
# CHECK: Contents of section .eh_frame_hdr:
-# CHECK-NEXT: 200158 011b033b 1c000000 01000000 a80e0000
+# CHECK-NEXT: 200158 011b033b 14000000 01000000
# ^ FDE count
-# CHECK-NEXT: 200168 38000000 00000000 00000000
-# ^ FDE for f2
-.globl _start, f1, f2
-_start:
- ret
+.globl f1, f2
.section .text.f1, "ax"
f1:
diff --git a/test/ELF/eh-frame-hdr.s b/test/ELF/eh-frame-hdr.s
index 35c14a4b65dd..4498d7d30eaa 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 addbb3f857fe..4b54c173c699 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
index 951fed0a56e9..e106f2989d4b 100644
--- a/test/ELF/eh-frame-padding-no-rosegment.s
+++ b/test/ELF/eh-frame-padding-no-rosegment.s
@@ -31,7 +31,7 @@ bar:
// OBJ-NEXT: 0020: 20000000 00000000 00000000 00000000
// OBJ-NEXT: )
-// RUN: ld.lld %t.o -no-rosegment -o %t -shared
+// 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
diff --git a/test/ELF/eh-frame.s b/test/ELF/eh-frame.s
new file mode 100644
index 000000000000..a07d242add15
--- /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 000000000000..0741e78ab955
--- /dev/null
+++ b/test/ELF/emit-relocs-gc.s
@@ -0,0 +1,30 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+
+## Show that we emit .rela.bar and .rela.text 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.text
+# NOGC: .rela.bar
+
+## GC collects .bar section and we exclude .rela.bar from output. We keep
+## .rela.text because we keep .text.
+# 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
+# GC: rela.text
+# GC-NOT: rela.bar
+
+.section .bar,"a"
+.quad .bar
+
+.text
+relocs:
+.quad _start
+
+.global _start
+_start:
+ nop
diff --git a/test/ELF/emit-relocs-merge.s b/test/ELF/emit-relocs-merge.s
index c700c337e334..3fecca23b735 100644
--- a/test/ELF/emit-relocs-merge.s
+++ b/test/ELF/emit-relocs-merge.s
@@ -8,7 +8,7 @@
# CHECK-NEXT: 0x1000 R_X86_64_64 zed 0x0
# CHECK-NEXT: 0x1008 R_X86_64_64 zed 0x0
# CHECK-NEXT: }
-# CHECK-NEXT: Section ({{.*}}) .rela.data.foo {
+# CHECK-NEXT: Section ({{.*}}) .rela.data {
# CHECK-NEXT: 0x1000 R_X86_64_64 zed 0x0
# CHECK-NEXT: 0x1008 R_X86_64_64 zed 0x0
# CHECK-NEXT: }
diff --git a/test/ELF/emit-relocs-mergeable-i386.s b/test/ELF/emit-relocs-mergeable-i386.s
new file mode 100644
index 000000000000..1ddee8c1faf1
--- /dev/null
+++ b/test/ELF/emit-relocs-mergeable-i386.s
@@ -0,0 +1,66 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %s -o %t1
+# RUN: ld.lld --emit-relocs %t1 -o %t2
+# RUN: llvm-readobj -sections -section-data %t2 | FileCheck %s
+
+## Check lf we produce proper relocations when doing merging of SHF_MERGE sections.
+
+## Check addends of relocations are: 0x0, 0x8, 0x8, 0x4
+# CHECK: Section {
+# CHECK: Index:
+# CHECK: Name: .foo
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_EXECINSTR
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Link:
+# CHECK-NEXT: Info:
+# CHECK-NEXT: AddressAlignment:
+# CHECK-NEXT: EntrySize:
+# CHECK-NEXT: SectionData (
+# CHECK-NEXT: 0000: 00000000 08000000 08000000 04000000
+# CHECK-NEXT: )
+# CHECK-NEXT: }
+
+## Check that offsets for AAA is 0x0, for BBB is 0x8 and CCC has offset 0x4.
+# CHECK: Section {
+# CHECK: Index:
+# CHECK: Name: .strings
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_MERGE
+# CHECK-NEXT: SHF_STRINGS
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Link:
+# CHECK-NEXT: Info:
+# CHECK-NEXT: AddressAlignment:
+# CHECK-NEXT: EntrySize:
+# CHECK-NEXT: SectionData (
+# CHECK-NEXT: |AAA.CCC.BBB.|
+# CHECK-NEXT: )
+# CHECK-NEXT: }
+
+.section .strings,"MS",@progbits,1,unique,10
+.Linfo_string0:
+ .asciz "AAA"
+.Linfo_string1:
+ .asciz "BBB"
+
+.section .strings,"MS",@progbits,1,unique,20
+.Linfo_string2:
+ .asciz "BBB"
+.Linfo_string3:
+ .asciz "CCC"
+
+.section .foo,"ax",@progbits
+.long .Linfo_string0
+.long .Linfo_string1
+.long .Linfo_string2
+.long .Linfo_string3
diff --git a/test/ELF/emit-relocs-mergeable.s b/test/ELF/emit-relocs-mergeable.s
new file mode 100644
index 000000000000..1553cb616012
--- /dev/null
+++ b/test/ELF/emit-relocs-mergeable.s
@@ -0,0 +1,53 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1
+# RUN: ld.lld --emit-relocs %t1 -o %t2
+# RUN: llvm-readobj -sections -section-data -r %t2 | FileCheck %s
+
+## Check if we produce proper relocations when doing merging of SHF_MERGE sections.
+
+# CHECK: Section {
+# CHECK: Index:
+# CHECK: Name: .strings
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_MERGE
+# CHECK-NEXT: SHF_STRINGS
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size: 12
+# CHECK-NEXT: Link:
+# CHECK-NEXT: Info:
+# CHECK-NEXT: AddressAlignment:
+# CHECK-NEXT: EntrySize:
+# CHECK-NEXT: SectionData (
+# CHECK-NEXT: 0000: 41414100 43434300 42424200 |AAA.CCC.BBB.|
+# CHECK-NEXT: )
+# CHECK-NEXT: }
+
+# CHECK: Relocations [
+# CHECK-NEXT: Section {{.*}} .rela.foo {
+# CHECK-NEXT: 0x201000 R_X86_64_64 .strings 0x0
+# CHECK-NEXT: 0x201008 R_X86_64_64 .strings 0x8
+# CHECK-NEXT: 0x201010 R_X86_64_64 .strings 0x8
+# CHECK-NEXT: 0x201018 R_X86_64_64 .strings 0x4
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+.section .strings,"MS",@progbits,1,unique,10
+.Linfo_string0:
+ .asciz "AAA"
+.Linfo_string1:
+ .asciz "BBB"
+
+.section .strings,"MS",@progbits,1,unique,20
+.Linfo_string2:
+ .asciz "BBB"
+.Linfo_string3:
+ .asciz "CCC"
+
+.section .foo,"ax",@progbits
+.quad .Linfo_string0
+.quad .Linfo_string1
+.quad .Linfo_string2
+.quad .Linfo_string3
diff --git a/test/ELF/emit-relocs-shared.s b/test/ELF/emit-relocs-shared.s
index 7a0d7911831b..65a12c15ee2e 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/emit-relocs.s b/test/ELF/emit-relocs.s
index fd9722f13ab0..95e70d80acf5 100644
--- a/test/ELF/emit-relocs.s
+++ b/test/ELF/emit-relocs.s
@@ -64,6 +64,15 @@
# 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: .comment
+# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
# CHECK-NEXT: Name: fn
# CHECK-NEXT: Value: 0x201000
# CHECK-NEXT: Size: 0
@@ -83,7 +92,7 @@
# CHECK-NEXT: }
# CHECK-NEXT: ]
-.section .text,"ax",@progbits,unique,0
+.section .text.fn,"ax",@progbits,unique,0
.globl fn
.type fn,@function
fn:
@@ -94,7 +103,7 @@ bar:
callq fn@PLT
nop
-.section .text,"ax",@progbits,unique,1
+.section .text.fn2,"ax",@progbits,unique,1
.globl fn2
.type fn2,@function
fn2:
diff --git a/test/ELF/exclude-libs.s b/test/ELF/exclude-libs.s
index c36081f40e54..dc7530068586 100644
--- a/test/ELF/exclude-libs.s
+++ b/test/ELF/exclude-libs.s
@@ -22,6 +22,12 @@
// 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
+// RUN: ld.lld -shared --whole-archive %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 --whole-archive %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
diff --git a/test/ELF/executable-undefined-ignoreall.s b/test/ELF/executable-undefined-ignoreall.s
new file mode 100644
index 000000000000..44f83a687ee2
--- /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 000000000000..37911791e124
--- /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/file-access.s b/test/ELF/file-access.s
new file mode 100644
index 000000000000..5a9e53b11140
--- /dev/null
+++ b/test/ELF/file-access.s
@@ -0,0 +1,13 @@
+# REQUIRES: x86, shell
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: ld.lld -r %t.o -o %t1.o
+# RUN: [ ! -x %t1.o ]
+# RUN: ld.lld -shared %t.o -o %t2.so
+# RUN: [ -x %t2.so ]
+# RUN: ld.lld %t.o -o %t3
+# RUN: [ -x %t3 ]
+
+.global _start
+_start:
+ nop
diff --git a/test/ELF/fill-trap.s b/test/ELF/fill-trap.s
new file mode 100644
index 000000000000..001b904a7790
--- /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
index fa8e5267b18b..4c9104a32510 100644
--- a/test/ELF/filter.s
+++ b/test/ELF/filter.s
@@ -2,10 +2,14 @@
# RUN: ld.lld %t.o -shared -F foo.so -F boo.so -o %t1
# RUN: llvm-readobj --dynamic-table %t1 | FileCheck %s
-# Test alias.
+# 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]
diff --git a/test/ELF/format-binary-non-ascii.s b/test/ELF/format-binary-non-ascii.s
new file mode 100644
index 000000000000..5a3ad960c30e
--- /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-collect-undefined.s b/test/ELF/gc-collect-undefined.s
new file mode 100644
index 000000000000..7ade554aef13
--- /dev/null
+++ b/test/ELF/gc-collect-undefined.s
@@ -0,0 +1,19 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: ld.lld %t -o %tout --gc-sections -shared
+# RUN: llvm-nm -D %tout | FileCheck %s
+
+# CHECK-NOT: qux
+# CHECK: bar
+# CHECK-NOT: qux
+
+ .global foo,bar,qux
+ .local baz
+
+ .section .data.foo,"aw",%progbits
+foo:
+ .dc.a bar
+
+ .section .bata.baz,"aw",%progbits
+baz:
+ .dc.a qux
diff --git a/test/ELF/gc-merge-local-sym.s b/test/ELF/gc-merge-local-sym.s
index a4540af4d9cc..b02a3a4e4762 100644
--- a/test/ELF/gc-merge-local-sym.s
+++ b/test/ELF/gc-merge-local-sym.s
@@ -15,7 +15,7 @@
// CHECK-NEXT: Link: 0
// CHECK-NEXT: Info: 0
// CHECK-NEXT: AddressAlignment: 1
-// CHECK-NEXT: EntrySize: 0
+// CHECK-NEXT: EntrySize: 1
// CHECK-NEXT: SectionData (
// CHECK-NEXT: 0000: 61626300 |abc.|
// CHECK-NEXT: )
diff --git a/test/ELF/gc-sections-linker-defined-symbol.s b/test/ELF/gc-sections-linker-defined-symbol.s
new file mode 100644
index 000000000000..796f7b363559
--- /dev/null
+++ b/test/ELF/gc-sections-linker-defined-symbol.s
@@ -0,0 +1,18 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld %t.o -o %t.so --gc-sections -shared
+# RUN: llvm-readobj --dyn-symbols %t.so | FileCheck %s
+
+# CHECK: Name: _end@
+# CHECK-NEXT: Value:
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: None
+# CHECK-NEXT: Other:
+# CHECK-NEXT: Section: .dynamic
+
+ .data
+ .globl g
+ g:
+ .quad _end
diff --git a/test/ELF/gc-sections-merge-addend.s b/test/ELF/gc-sections-merge-addend.s
index 1c1f6ee2389b..8595f5802be5 100644
--- a/test/ELF/gc-sections-merge-addend.s
+++ b/test/ELF/gc-sections-merge-addend.s
@@ -16,7 +16,7 @@
// CHECK-NEXT: Link: 0
// CHECK-NEXT: Info: 0
// CHECK-NEXT: AddressAlignment: 1
-// CHECK-NEXT: EntrySize: 0
+// CHECK-NEXT: EntrySize: 1
// CHECK-NEXT: SectionData (
// CHECK-NEXT: 0000: 62617200 |bar.|
// CHECK-NEXT: )
diff --git a/test/ELF/gc-sections-merge-implicit-addend.s b/test/ELF/gc-sections-merge-implicit-addend.s
index c725e294a187..8a7c804a830a 100644
--- a/test/ELF/gc-sections-merge-implicit-addend.s
+++ b/test/ELF/gc-sections-merge-implicit-addend.s
@@ -16,7 +16,7 @@
// CHECK-NEXT: Link: 0
// CHECK-NEXT: Info: 0
// CHECK-NEXT: AddressAlignment: 1
-// CHECK-NEXT: EntrySize: 0
+// CHECK-NEXT: EntrySize: 1
// CHECK-NEXT: SectionData (
// CHECK-NEXT: 0000: 62617200 |bar.|
// CHECK-NEXT: )
diff --git a/test/ELF/gc-sections-merge.s b/test/ELF/gc-sections-merge.s
index d7b3eaeca751..ef2688659871 100644
--- a/test/ELF/gc-sections-merge.s
+++ b/test/ELF/gc-sections-merge.s
@@ -18,7 +18,7 @@
// CHECK-NEXT: Link: 0
// CHECK-NEXT: Info: 0
// CHECK-NEXT: AddressAlignment: 1
-// CHECK-NEXT: EntrySize: 0
+// CHECK-NEXT: EntrySize: 1
// CHECK-NEXT: SectionData (
// CHECK-NEXT: 0000: 666F6F00 62617200 |foo.bar.|
// CHECK-NEXT: )
@@ -36,7 +36,7 @@
// GC-NEXT: Link: 0
// GC-NEXT: Info: 0
// GC-NEXT: AddressAlignment: 1
-// GC-NEXT: EntrySize: 0
+// GC-NEXT: EntrySize: 1
// GC-NEXT: SectionData (
// GC-NEXT: 0000: 666F6F00 |foo.|
// GC-NEXT: )
diff --git a/test/ELF/gc-sections-print.s b/test/ELF/gc-sections-print.s
index 59177a65367e..e05824177c1f 100644
--- a/test/ELF/gc-sections-print.s
+++ b/test/ELF/gc-sections-print.s
@@ -5,6 +5,12 @@
# PRINT: removing unused section from '.text.x' in file
# PRINT-NEXT: removing unused section from '.text.y' in file
+# RUN: ld.lld %t --gc-sections --print-gc-sections --no-print-gc-sections -o %t2 >& %t.log
+# RUN: echo >> %t.log
+# RUN: FileCheck -check-prefix=NOPRINT %s < %t.log
+
+# NOPRINT-NOT: removing
+
.globl _start
.protected a, x, y
_start:
diff --git a/test/ELF/gc-sections-shared.s b/test/ELF/gc-sections-shared.s
index efb21faee6bd..2976213e910a 100644
--- a/test/ELF/gc-sections-shared.s
+++ b/test/ELF/gc-sections-shared.s
@@ -1,12 +1,18 @@
# REQUIRES: x86
# 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/gc-sections-shared.s -o %t3.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/gc-sections-shared2.s -o %t4.o
# RUN: ld.lld -shared %t2.o -o %t2.so
+# RUN: ld.lld -shared %t3.o -o %t3.so
+# RUN: ld.lld -shared %t4.o -o %t4.so
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
-# RUN: ld.lld --gc-sections --export-dynamic-symbol foo -o %t %t.o --as-needed %t2.so
+# RUN: ld.lld --gc-sections --export-dynamic-symbol foo -o %t %t.o --as-needed %t2.so %t3.so %t4.so
# RUN: llvm-readobj --dynamic-table --dyn-symbols %t | FileCheck %s
# This test the property that we have a needed line for every undefined.
-# It would also be OK to drop bar2 and the need for the .so
+# It would also be OK to keep bar2 and the need for %t2.so
+# At the same time, weak symbols should not cause adding DT_NEEDED;
+# this case is checked with symbol qux and %t4.so.
# CHECK: DynamicSymbols [
# CHECK-NEXT: Symbol {
@@ -19,7 +25,16 @@
# CHECK-NEXT: Section: Undefined (0x0)
# CHECK-NEXT: }
# CHECK-NEXT: Symbol {
-# CHECK-NEXT: Name: bar2
+# 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: baz
# CHECK-NEXT: Value:
# CHECK-NEXT: Size:
# CHECK-NEXT: Binding: Global
@@ -36,9 +51,76 @@
# CHECK-NEXT: Other:
# CHECK-NEXT: Section: .text
# CHECK-NEXT: }
+# CHECK-NEXT: Symbol {
+# CHECK-NEXT: Name: qux
+# CHECK-NEXT: Value:
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Binding: Weak
+# CHECK-NEXT: Type:
+# CHECK-NEXT: Other:
+# CHECK-NEXT: Section: Undefined
+# CHECK-NEXT: }
# CHECK-NEXT: ]
-# CHECK: NEEDED Shared library: [{{.*}}.so]
+# CHECK-NOT: NEEDED
+# CHECK: NEEDED Shared library: [{{.*}}3.so]
+# CHECK-NOT: NEEDED
+
+# Test with %t.o at the end too.
+# RUN: ld.lld --gc-sections --export-dynamic-symbol foo -o %t --as-needed %t2.so %t3.so %t4.so %t.o
+# RUN: llvm-readobj --dynamic-table --dyn-symbols %t | FileCheck --check-prefix=CHECK2 %s
+
+# CHECK2: DynamicSymbols [
+# CHECK2-NEXT: Symbol {
+# CHECK2-NEXT: Name:
+# CHECK2-NEXT: Value:
+# CHECK2-NEXT: Size:
+# CHECK2-NEXT: Binding: Local
+# CHECK2-NEXT: Type:
+# CHECK2-NEXT: Other:
+# CHECK2-NEXT: Section: Undefined (0x0)
+# CHECK2-NEXT: }
+# CHECK2-NEXT: Symbol {
+# CHECK2-NEXT: Name: bar
+# CHECK2-NEXT: Value:
+# CHECK2-NEXT: Size:
+# CHECK2-NEXT: Binding: Global
+# CHECK2-NEXT: Type:
+# CHECK2-NEXT: Other:
+# CHECK2-NEXT: Section: .text
+# CHECK2-NEXT: }
+# CHECK2-NEXT: Symbol {
+# CHECK2-NEXT: Name: baz
+# CHECK2-NEXT: Value:
+# CHECK2-NEXT: Size:
+# CHECK2-NEXT: Binding: Global
+# CHECK2-NEXT: Type:
+# CHECK2-NEXT: Other:
+# CHECK2-NEXT: Section: Undefined
+# CHECK2-NEXT: }
+# CHECK2-NEXT: Symbol {
+# CHECK2-NEXT: Name: qux
+# CHECK2-NEXT: Value:
+# CHECK2-NEXT: Size:
+# CHECK2-NEXT: Binding: Weak
+# CHECK2-NEXT: Type:
+# CHECK2-NEXT: Other:
+# CHECK2-NEXT: Section: Undefined
+# CHECK2-NEXT: }
+# CHECK2-NEXT: Symbol {
+# CHECK2-NEXT: Name: foo
+# CHECK2-NEXT: Value:
+# CHECK2-NEXT: Size:
+# CHECK2-NEXT: Binding: Global
+# CHECK2-NEXT: Type:
+# CHECK2-NEXT: Other:
+# CHECK2-NEXT: Section: .text
+# CHECK2-NEXT: }
+# CHECK2-NEXT: ]
+
+# CHECK2-NOT: NEEDED
+# CHECK2: NEEDED Shared library: [{{.*}}3.so]
+# CHECK2-NOT: NEEDED
.section .text.foo, "ax"
.globl foo
@@ -52,7 +134,10 @@ ret
.section .text._start, "ax"
.globl _start
+.weak qux
_start:
+call baz
+call qux
ret
.section .text.unused, "ax"
diff --git a/test/ELF/gc-sections-undefined.s b/test/ELF/gc-sections-undefined.s
new file mode 100644
index 000000000000..e1ce9c739738
--- /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 000000000000..3871f8fc92e9
--- /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 e0bed33eed4d..f5df00c453cc 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 0158357c9bfd..b5b403636290 100644
--- a/test/ELF/gdb-index-empty.s
+++ b/test/ELF/gdb-index-empty.s
@@ -1,7 +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:
@@ -74,7 +74,7 @@ _start:
.quad .Lfunc_begin0 # DW_AT_low_pc
.long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
.byte 1 # DW_AT_frame_base
- .byte 87
+ .byte 87
.long 0 # DW_AT_name
.byte 1 # DW_AT_decl_file
.byte 1 # DW_AT_decl_line
diff --git a/test/ELF/gdb-index-gc-sections.s b/test/ELF/gdb-index-gc-sections.s
index 58c47ae5e987..6016e9ccdd07 100644
--- a/test/ELF/gdb-index-gc-sections.s
+++ b/test/ELF/gdb-index-gc-sections.s
@@ -1,7 +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-noranges.s b/test/ELF/gdb-index-noranges.s
new file mode 100644
index 000000000000..29ba91eb5a75
--- /dev/null
+++ b/test/ELF/gdb-index-noranges.s
@@ -0,0 +1,49 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t1.o
+
+## Input is reduced from following code and invocation:
+## clang++ -gsplit-dwarf -c test.ii -o test.s -S
+## clang version: 6.0.0 (trunk 318293)
+##
+## test.ii:
+## int a;
+##
+## Debug information does not contain any address ranges.
+## We crashed in that case. Check we don't.
+# RUN: ld.lld --gdb-index %t1.o -o %t
+
+.section .debug_str,"MS",@progbits,1
+.Lskel_string0:
+ .asciz "t.dwo"
+.Lskel_string1:
+ .asciz "path"
+
+.section .debug_abbrev,"",@progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 0 # DW_CHILDREN_no
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .ascii "\260B" # DW_AT_GNU_dwo_name
+ .byte 14 # DW_FORM_strp
+ .byte 27 # DW_AT_comp_dir
+ .byte 14 # DW_FORM_strp
+ .ascii "\261B" # DW_AT_GNU_dwo_id
+ .byte 7 # DW_FORM_data8
+ .ascii "\263B" # DW_AT_GNU_addr_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+
+.section .debug_info,"",@progbits
+ .long 32 # 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:0x19 DW_TAG_compile_unit
+ .long 0 # DW_AT_stmt_list
+ .long .Lskel_string0 # DW_AT_GNU_dwo_name
+ .long .Lskel_string1 # DW_AT_comp_dir
+ .quad -3824446529333676116 # DW_AT_GNU_dwo_id
+ .long 0 # DW_AT_GNU_addr_base
diff --git a/test/ELF/gdb-index-ranges.s b/test/ELF/gdb-index-ranges.s
index 2dd158ee4a0a..c41be114f005 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 000000000000..0fd7b6115676
--- /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 e04845e022c3..8fea83d145fa 100644
--- a/test/ELF/gdb-index.s
+++ b/test/ELF/gdb-index.s
@@ -1,12 +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 %p/Inputs/gdb-index.s -o %t2.o
-# RUN: ld.lld --gdb-index -e main %t1.o %t2.o -o %t
-# RUN: llvm-dwarfdump -debug-dump=gdb_index %t | FileCheck %s
+# 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: entrypoint:
# DISASM-CHECK: 201000: 90 nop
# DISASM-CHECK: 201001: cc int3
# DISASM-CHECK: 201002: cc int3
@@ -24,25 +24,29 @@
# 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 main() { return 0; }' | gcc -gsplit-dwarf -xc++ -S -o- -
+## echo 'int entrypoint() { return 0; }' | gcc -gsplit-dwarf -xc++ -S -o- -
.text
.Ltext0:
-.globl main
-.type main, @function
-main:
+.globl entrypoint
+.type entrypoint, @function
+entrypoint:
nop
.Letext0:
@@ -98,7 +102,7 @@ main:
.long 0x33
.long 0x18
.byte 0x30
-.string "main"
+.string "entrypoint"
.long 0
.section .debug_gnu_pubtypes,"",@progbits
diff --git a/test/ELF/global-offset-table-position-aarch64.s b/test/ELF/global-offset-table-position-aarch64.s
index 624e9b516fed..68bc4a4254ed 100644
--- a/test/ELF/global-offset-table-position-aarch64.s
+++ b/test/ELF/global-offset-table-position-aarch64.s
@@ -1,5 +1,5 @@
// RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %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
// REQUIRES: aarch64
.globl a
diff --git a/test/ELF/global-offset-table-position-arm.s b/test/ELF/global-offset-table-position-arm.s
index 781d8ce5222f..19619b2cc9e8 100644
--- a/test/ELF/global-offset-table-position-arm.s
+++ b/test/ELF/global-offset-table-position-arm.s
@@ -1,5 +1,5 @@
// RUN: llvm-mc -filetype=obj -triple=armv7a-linux-gnueabihf %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
// REQUIRES: arm
diff --git a/test/ELF/global-offset-table-position-i386.s b/test/ELF/global-offset-table-position-i386.s
index 907105edcff0..9f778e1efb50 100644
--- a/test/ELF/global-offset-table-position-i386.s
+++ b/test/ELF/global-offset-table-position-i386.s
@@ -1,5 +1,5 @@
// RUN: llvm-mc -filetype=obj -triple=i386-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
// REQUIRES: x86
diff --git a/test/ELF/global-offset-table-position.s b/test/ELF/global-offset-table-position.s
index b3317c7edd8b..f1195b2cf674 100644
--- a/test/ELF/global-offset-table-position.s
+++ b/test/ELF/global-offset-table-position.s
@@ -1,5 +1,5 @@
// 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
// REQUIRES: x86
diff --git a/test/ELF/global_offset_table_shared.s b/test/ELF/global_offset_table_shared.s
index 1ebc0110d4d7..03af02e5805e 100644
--- a/test/ELF/global_offset_table_shared.s
+++ b/test/ELF/global_offset_table_shared.s
@@ -1,5 +1,5 @@
// 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_ - .
diff --git a/test/ELF/gnu-hash-table-copy.s b/test/ELF/gnu-hash-table-copy.s
new file mode 100644
index 000000000000..9d91163258ea
--- /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-many.s b/test/ELF/gnu-hash-table-many.s
new file mode 100644
index 000000000000..ab35a07981e7
--- /dev/null
+++ b/test/ELF/gnu-hash-table-many.s
@@ -0,0 +1,55 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld -hash-style=gnu %t.o -o %t.so -shared
+# RUN: llvm-readelf --gnu-hash-table %t.so | FileCheck %s
+
+# CHECK: Num Buckets: 4
+
+.global sym1
+sym1:
+
+.global sym2
+sym2:
+
+.global sym3
+sym3:
+
+.global sym4
+sym4:
+
+.global sym5
+sym5:
+
+.global sym6
+sym6:
+
+.global sym7
+sym7:
+
+.global sym8
+sym8:
+
+.global sym9
+sym9:
+
+.global sym10
+sym10:
+
+.global sym11
+sym11:
+
+.global sym12
+sym12:
+
+.global sym13
+sym13:
+
+.global sym14
+sym14:
+
+.global sym15
+sym15:
+
+.global sym16
+sym16:
diff --git a/test/ELF/gnu-hash-table-rwsegment.s b/test/ELF/gnu-hash-table-rwsegment.s
new file mode 100644
index 000000000000..b1a50fbde3a1
--- /dev/null
+++ b/test/ELF/gnu-hash-table-rwsegment.s
@@ -0,0 +1,20 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld -shared -hash-style=gnu --no-rosegment -o %t.so %t.o
+# RUN: llvm-readobj -gnu-hash-table %t.so | FileCheck %s
+
+# CHECK: GnuHashTable {
+# CHECK-NEXT: Num Buckets: 1
+# CHECK-NEXT: First Hashed Symbol Index: 1
+# CHECK-NEXT: Num Mask Words: 1
+# CHECK-NEXT: Shift Count: 6
+# CHECK-NEXT: Bloom Filter: [0x400000000004204]
+# CHECK-NEXT: Buckets: [1]
+# CHECK-NEXT: Values: [0xB8860BA, 0xB887389]
+# CHECK-NEXT: }
+
+.globl foo, bar
+foo:
+bar:
+ ret
diff --git a/test/ELF/gnu-hash-table.s b/test/ELF/gnu-hash-table.s
index 0e37574aaa7c..fa68ba250131 100644
--- a/test/ELF/gnu-hash-table.s
+++ b/test/ELF/gnu-hash-table.s
@@ -1,15 +1,34 @@
# REQUIRES: x86,ppc
-# RUN: echo ".globl foo" > %te.s
+# RUN: echo ".globl foo; .data; .dc.a foo" > %te.s
# RUN: llvm-mc -filetype=obj -triple=i386-pc-linux %te.s -o %te-i386.o
# RUN: llvm-mc -filetype=obj -triple=i386-pc-linux %s -o %t-i386.o
# 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,18 @@
# 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
+.data
+ .dc.a baz
diff --git a/test/ELF/gnu-ifunc-dynsym.s b/test/ELF/gnu-ifunc-dynsym.s
new file mode 100644
index 000000000000..fca15462dcb1
--- /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 2a5814a98bc2..b506ffa8c390 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 21f1313a9b05..559e98a3e625 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 50f10c5fe6ca..243eff62fcbf 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 cf46380d3894..88a09931853c 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 aee870c28e11..bde6807e4b43 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 f86f0300baaf..17883a3209f5 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 ef6943881edc..f46946c9f4aa 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 57c1baddfd0c..f67ea13d3f4e 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-pie-rw.s b/test/ELF/got32-i386-pie-rw.s
new file mode 100644
index 000000000000..18b019c2cc9d
--- /dev/null
+++ b/test/ELF/got32-i386-pie-rw.s
@@ -0,0 +1,17 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %s -o %t.o
+# RUN: ld.lld %t.o -o %t -pie
+# RUN: llvm-readelf -r -s %t | FileCheck %s
+
+# Unlike bfd and gold we accept this.
+
+# CHECK: .foobar PROGBITS 00001000
+# CHECK: .got PROGBITS [[GOT:[0-9a-z]*]]
+# CHECK: [[GOT]] 00000008 R_386_RELATIVE
+# CHECK: 00001002 00000008 R_386_RELATIVE
+foo:
+
+.section .foobar, "awx"
+.global _start
+_start:
+ movl foo@GOT, %ebx
diff --git a/test/ELF/got32-i386.s b/test/ELF/got32-i386.s
index 468aaf1f0d53..00c7c0d6d553 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 9a67d1997f2b..1311472cc061 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 dc7dcf2e91e7..e51b8bd9f4e9 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 ed6c4bc9bb15..3c5c8ba8c49b 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 95dbf663ffe8..3ccbc56aba94 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 000000000000..2554531532b3
--- /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-debug-noabs.test b/test/ELF/i386-debug-noabs.test
new file mode 100644
index 000000000000..712d0a59cecc
--- /dev/null
+++ b/test/ELF/i386-debug-noabs.test
@@ -0,0 +1,33 @@
+# REQUIRES: x86
+
+# RUN: yaml2obj %s -o %t.o
+# RUN: ld.lld %t.o -o %t.exe
+
+## This is for https://bugs.llvm.org//show_bug.cgi?id=34852. GCC 8.0 or
+## earlier have a bug which creates non-absolute R_386_GOTPC relocations
+## in non-allocated sections. It is illegal, but we want to make sure that
+## lld skips them instead of reporting errors.
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS32
+ Data: ELFDATA2LSB
+ Type: ET_REL
+ Machine: EM_386
+Sections:
+ - Name: .debug_info
+ Type: SHT_PROGBITS
+ AddressAlign: 0x0000000000000001
+ Content: 0000000000000000
+ - Name: .rel.debug_info
+ Type: SHT_REL
+ Link: .symtab
+ AddressAlign: 0x0000000000000004
+ Info: .debug_info
+ Relocations:
+ - Offset: 0x000000000000041F
+ Symbol: _GLOBAL_OFFSET_TABLE_
+ Type: R_386_GOTPC
+Symbols:
+ Global:
+ - Name: _GLOBAL_OFFSET_TABLE_
diff --git a/test/ELF/i386-got-and-copy.s b/test/ELF/i386-got-and-copy.s
index f5b0b8ec5bb8..81bac22fd66a 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-got-value.s b/test/ELF/i386-got-value.s
new file mode 100644
index 000000000000..f42555b79272
--- /dev/null
+++ b/test/ELF/i386-got-value.s
@@ -0,0 +1,36 @@
+# RUN: llvm-mc %s -o %t.o -filetype=obj -triple=i386-pc-linux
+# RUN: ld.lld %t.o -o %t.so -shared
+# RUN: llvm-readobj --relocations --symbols --sections --section-data %t.so | FileCheck %s
+
+# Check that the value of a preemptible symbol is written to the got
+# entry when using Elf_Rel. It is not clear why that is required, but
+# freebsd i386 seems to depend on it.
+
+# CHECK: Name: .got
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_WRITE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size: 4
+# CHECK-NEXT: Link:
+# CHECK-NEXT: Info:
+# CHECK-NEXT: AddressAlignment:
+# CHECK-NEXT: EntrySize:
+# CHECK-NEXT: SectionData (
+# CHECK-NEXT: 0000: 00200000
+# CHECK-NEXT: )
+
+# CHECK: R_386_GLOB_DAT bar 0x0
+
+# CHECK: Name: bar
+# CHECK-NEXT: Value: 0x2000
+
+ movl bar@GOT(%eax), %eax
+
+ .data
+ .globl bar
+bar:
+ .long 42
diff --git a/test/ELF/i386-gotoff-shared.s b/test/ELF/i386-gotoff-shared.s
index 01242ad01c35..c22bd6dd5d78 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 da8a607d5753..0ec737e701a6 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 8222effd6655..d2c5ef3d469c 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-pc8-pc16-addend.s b/test/ELF/i386-pc8-pc16-addend.s
index 9d6424df3600..fc648b035509 100644
--- a/test/ELF/i386-pc8-pc16-addend.s
+++ b/test/ELF/i386-pc8-pc16-addend.s
@@ -4,7 +4,7 @@
# RUN: ld.lld %t1.o -o %t.out
# RUN: llvm-objdump -s -t %t.out | FileCheck %s
# CHECK: Contents of section .text:
-# CHECK-NEXT: 11000 020000
+# CHECK-NEXT: 11000 020000
## 0x11003 - 0x11000 + addend(-1) = 0x02
## 0x11003 - 0x11001 + addend(-2) = 0x0000
# CHECK: SYMBOL TABLE:
diff --git a/test/ELF/i386-reloc-16.s b/test/ELF/i386-reloc-16.s
index db9dc0b36908..d69e6fbc49a7 100644
--- a/test/ELF/i386-reloc-16.s
+++ b/test/ELF/i386-reloc-16.s
@@ -9,6 +9,6 @@
// CHECK-NEXT: 200000 42
// RUN: not ld.lld -shared %t %t2 -o %t4 2>&1 | FileCheck --check-prefix=ERROR %s
-// ERROR: relocation R_386_16 out of range
+// ERROR: relocation R_386_16 out of range: 65536 is not in [0, 65535]
.short foo
diff --git a/test/ELF/i386-reloc-8.s b/test/ELF/i386-reloc-8.s
index b2e4426910e6..c6ae67120e22 100644
--- a/test/ELF/i386-reloc-8.s
+++ b/test/ELF/i386-reloc-8.s
@@ -9,6 +9,6 @@
// CHECK-NEXT: 200000 42
// RUN: not ld.lld -shared %t %t2 -o %t4 2>&1 | FileCheck --check-prefix=ERROR %s
-// ERROR: relocation R_386_8 out of range
+// ERROR: relocation R_386_8 out of range: 256 is not in [0, 255]
.byte foo
diff --git a/test/ELF/i386-reloc-range.s b/test/ELF/i386-reloc-range.s
index 4fb5325e5434..6f72f7af73c7 100644
--- a/test/ELF/i386-reloc-range.s
+++ b/test/ELF/i386-reloc-range.s
@@ -16,7 +16,7 @@
// RUN: not ld.lld -Ttext 0x200 %t.o %t2.o -o %t2 2>&1 | FileCheck --check-prefix=ERR %s
-// ERR: {{.*}}:(.text+0x1): relocation R_386_PC16 out of range
+// ERR: {{.*}}:(.text+0x1): relocation R_386_PC16 out of range: 65536 is not in [-65536, 65535]
.global _start
_start:
diff --git a/test/ELF/i386-reloc8-reloc16-addend.s b/test/ELF/i386-reloc8-reloc16-addend.s
index 42a57cedbca3..16b953e87ca9 100644
--- a/test/ELF/i386-reloc8-reloc16-addend.s
+++ b/test/ELF/i386-reloc8-reloc16-addend.s
@@ -4,7 +4,7 @@
# RUN: ld.lld -Ttext=0x0 %t1.o -o %t.out
# RUN: llvm-objdump -s -t %t.out | FileCheck %s
# CHECK: Contents of section .text:
-# CHECK-NEXT: 0000 020100
+# CHECK-NEXT: 0000 020100
## 0x3 + addend(-1) = 0x02
## 0x3 + addend(-2) = 0x0100
# CHECK: SYMBOL TABLE:
diff --git a/test/ELF/i386-tls-ie-shared.s b/test/ELF/i386-tls-ie-shared.s
index f419eb45dfb9..2b842a86eb0f 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-absolute.s b/test/ELF/icf-absolute.s
index 601322477dae..09f6790907a1 100644
--- a/test/ELF/icf-absolute.s
+++ b/test/ELF/icf-absolute.s
@@ -2,7 +2,7 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %S/Inputs/icf-absolute.s -o %t2
-# RUN: ld.lld %t %t2 -o %t3 --icf=all --verbose | FileCheck %s
+# RUN: ld.lld %t %t2 -o %t3 --icf=all --verbose 2>&1 | FileCheck %s
# CHECK: selected .text.f1
# CHECK: removed .text.f2
diff --git a/test/ELF/icf-comdat.s b/test/ELF/icf-comdat.s
index 28c0a586bf03..aab6a00f484d 100644
--- a/test/ELF/icf-comdat.s
+++ b/test/ELF/icf-comdat.s
@@ -1,7 +1,7 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-# RUN: ld.lld %t -o %t2 --icf=all --verbose | FileCheck %s
+# RUN: ld.lld %t -o %t2 --icf=all --verbose 2>&1 | FileCheck %s
# CHECK: selected .text.f1
# CHECK: removed .text.f2
diff --git a/test/ELF/icf-i386.s b/test/ELF/icf-i386.s
index 292883e16fe5..b01e0503d405 100644
--- a/test/ELF/icf-i386.s
+++ b/test/ELF/icf-i386.s
@@ -2,7 +2,7 @@
# This test is to make sure that we can handle implicit addends properly.
# RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %s -o %t
-# RUN: ld.lld %t -o %t2 --icf=all --verbose | FileCheck %s
+# RUN: ld.lld %t -o %t2 --icf=all --verbose 2>&1 | FileCheck %s
# CHECK: selected .text.f1
# CHECK: removed .text.f2
diff --git a/test/ELF/icf-merge-sec.s b/test/ELF/icf-merge-sec.s
index 39f6a884fa93..1e866a0caa49 100644
--- a/test/ELF/icf-merge-sec.s
+++ b/test/ELF/icf-merge-sec.s
@@ -2,7 +2,7 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %S/Inputs/icf-merge-sec.s -o %t2
-# RUN: ld.lld %t %t2 -o %t3 --icf=all --verbose | FileCheck %s
+# RUN: ld.lld %t %t2 -o %t3 --icf=all --verbose 2>&1 | FileCheck %s
# CHECK: selected .text.f1
# CHECK: removed .text.f2
diff --git a/test/ELF/icf-merge.s b/test/ELF/icf-merge.s
index 938b749da6b8..06e852fe9dd5 100644
--- a/test/ELF/icf-merge.s
+++ b/test/ELF/icf-merge.s
@@ -2,13 +2,13 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %S/Inputs/icf-merge.s -o %t1
-# RUN: ld.lld %t %t1 -o %t1.out --icf=all --verbose | FileCheck %s
+# RUN: ld.lld %t %t1 -o %t1.out --icf=all --verbose 2>&1 | FileCheck %s
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %S/Inputs/icf-merge2.s -o %t2
-# RUN: ld.lld %t %t2 -o %t3.out --icf=all --verbose | FileCheck --check-prefix=NOMERGE %s
+# RUN: ld.lld %t %t2 -o %t3.out --icf=all --verbose 2>&1 | FileCheck --check-prefix=NOMERGE %s
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %S/Inputs/icf-merge3.s -o %t3
-# RUN: ld.lld %t %t3 -o %t3.out --icf=all --verbose | FileCheck --check-prefix=NOMERGE %s
+# RUN: ld.lld %t %t3 -o %t3.out --icf=all --verbose 2>&1 | FileCheck --check-prefix=NOMERGE %s
# CHECK: selected .text.f1
# CHECK: removed .text.f2
diff --git a/test/ELF/icf-non-mergeable.s b/test/ELF/icf-non-mergeable.s
index 378c7b33696d..48ba2008cacc 100644
--- a/test/ELF/icf-non-mergeable.s
+++ b/test/ELF/icf-non-mergeable.s
@@ -1,14 +1,14 @@
// 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
// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \
// RUN: %p/Inputs/icf-non-mergeable.s -o %t2
-// RUN: ld.lld %t1 %t2 -o %t3 --icf=all --verbose | FileCheck %s
+// RUN: ld.lld %t1 %t2 -o %t3 --icf=all --verbose 2>&1 | FileCheck %s
// CHECK-NOT: selected .text.f1
// CHECK-NOT: removed .text.f2
diff --git a/test/ELF/icf-none.s b/test/ELF/icf-none.s
index 671f2085f669..9ec1406de8a4 100644
--- a/test/ELF/icf-none.s
+++ b/test/ELF/icf-none.s
@@ -1,7 +1,7 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-# RUN: ld.lld %t -o %t2 --icf=all --icf=none --verbose | FileCheck %s
+# RUN: ld.lld %t -o %t2 --icf=all --icf=none --verbose 2>&1 | FileCheck %s
# CHECK-NOT: selected .text.f1
diff --git a/test/ELF/icf-symbol-type.s b/test/ELF/icf-symbol-type.s
new file mode 100644
index 000000000000..9cc1c509689a
--- /dev/null
+++ b/test/ELF/icf-symbol-type.s
@@ -0,0 +1,26 @@
+# 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 --sections %t | FileCheck %s
+
+# We used to mark bar as absolute.
+
+# CHECK: .text PROGBITS 0000000000001000
+# CHECK: 0000000000001001 0 NOTYPE GLOBAL DEFAULT 4 foo
+# CHECK: 0000000000001001 0 NOTYPE GLOBAL DEFAULT 4 bar
+
+# The nop makes the test more interesting by making the offset of
+# text.f non zero.
+
+nop
+
+ .section .text.f,"ax",@progbits
+ .globl foo
+foo:
+ retq
+
+ .section .text.g,"ax",@progbits
+ .globl bar
+bar:
+ retq
diff --git a/test/ELF/icf1.s b/test/ELF/icf1.s
index bb060078476e..e2562b5a83e7 100644
--- a/test/ELF/icf1.s
+++ b/test/ELF/icf1.s
@@ -1,7 +1,7 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-# RUN: ld.lld %t -o %t2 --icf=all --verbose | FileCheck %s
+# RUN: ld.lld %t -o %t2 --icf=all --verbose 2>&1 | FileCheck %s
# CHECK: selected .text.f1
# CHECK: removed .text.f2
diff --git a/test/ELF/icf2.s b/test/ELF/icf2.s
index be595112b7e7..fd0a311cbd1d 100644
--- a/test/ELF/icf2.s
+++ b/test/ELF/icf2.s
@@ -2,7 +2,7 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/icf2.s -o %t2
-# RUN: ld.lld %t1 %t2 -o %t --icf=all --verbose | FileCheck %s
+# RUN: ld.lld %t1 %t2 -o %t --icf=all --verbose 2>&1 | FileCheck %s
# CHECK: selected .text.f1
# CHECK: removed .text.f2
diff --git a/test/ELF/icf3.s b/test/ELF/icf3.s
index 9f39ff6c7477..40067cefb200 100644
--- a/test/ELF/icf3.s
+++ b/test/ELF/icf3.s
@@ -2,7 +2,7 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/icf2.s -o %t2
-# RUN: ld.lld %t1 %t2 -o %t --icf=all --verbose | FileCheck %s
+# RUN: ld.lld %t1 %t2 -o %t --icf=all --verbose 2>&1 | FileCheck %s
# CHECK-NOT: Selected .text.f1
# CHECK-NOT: Selected .text.f2
diff --git a/test/ELF/icf4.s b/test/ELF/icf4.s
index 08830c8e503c..b7f40e805733 100644
--- a/test/ELF/icf4.s
+++ b/test/ELF/icf4.s
@@ -1,7 +1,7 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-# RUN: ld.lld %t -o %t2 --icf=all --verbose | FileCheck %s
+# RUN: ld.lld %t -o %t2 --icf=all --verbose 2>&1 | FileCheck %s
# CHECK-NOT: Selected .text.f1
# CHECK-NOT: Selected .text.f2
diff --git a/test/ELF/icf5.s b/test/ELF/icf5.s
index 952fe3601a4d..749cc5e923a0 100644
--- a/test/ELF/icf5.s
+++ b/test/ELF/icf5.s
@@ -1,7 +1,7 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-# RUN: ld.lld %t -o %t2 --icf=all --verbose | FileCheck %s
+# RUN: ld.lld %t -o %t2 --icf=all --verbose 2>&1 | FileCheck %s
# CHECK-NOT: Selected .text.f1
# CHECK-NOT: Selected .text.f2
diff --git a/test/ELF/icf6.s b/test/ELF/icf6.s
index ecb62fee2a0c..6420868523bf 100644
--- a/test/ELF/icf6.s
+++ b/test/ELF/icf6.s
@@ -1,7 +1,7 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-# RUN: ld.lld %t -o %t2 --icf=all --verbose | FileCheck %s
+# RUN: ld.lld %t -o %t2 --icf=all --verbose 2>&1 | FileCheck %s
# CHECK-NOT: Selected .text.f1
# CHECK-NOT: Selected .text.f2
diff --git a/test/ELF/icf7.s b/test/ELF/icf7.s
index 8504ca2ac610..00fca793aeea 100644
--- a/test/ELF/icf7.s
+++ b/test/ELF/icf7.s
@@ -1,7 +1,7 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-# RUN: ld.lld %t -o %t2 --icf=all --verbose | FileCheck %s
+# RUN: ld.lld %t -o %t2 --icf=all --verbose 2>&1 | FileCheck %s
# RUN: llvm-objdump -t %t2 | FileCheck -check-prefix=ALIGN %s
# CHECK: selected .text.f1
diff --git a/test/ELF/icf9.s b/test/ELF/icf9.s
index c5a532915cc7..de6db60f9684 100644
--- a/test/ELF/icf9.s
+++ b/test/ELF/icf9.s
@@ -2,19 +2,31 @@
### Make sure that we do not merge data.
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-# RUN: ld.lld %t -o %t2 --icf=all --verbose | FileCheck %s
+# RUN: ld.lld %t -o %t2 --icf=all --verbose 2>&1 | FileCheck %s
+# RUN: llvm-readelf -S -W %t2 | FileCheck --check-prefix=SEC %s
-# CHECK-NOT: selected .data.d1
-# CHECK-NOT: selected .data.d2
+# SEC: .rodata PROGBITS 0000000000200120 000120 000002 00 A 0 0 1
+
+# CHECK-NOT: selected .rodata.d1
+# CHECK-NOT: selected .rodata.d2
+
+# We do merge rodata if passed --icf-data
+# RUN: ld.lld %t -o %t2 --icf=all --verbose --icf-data 2>&1 | FileCheck --check-prefix=DATA %s
+# RUN: llvm-readelf -S -W %t2 | FileCheck --check-prefix=DATA-SEC %s
+
+# DATA: selected .rodata.d1
+# DATA: removed .rodata.d2
+
+# DATA-SEC: .rodata PROGBITS 0000000000200120 000120 000001 00 A 0 0 1
.globl _start, d1, d2
_start:
ret
-.section .data.f1, "a"
+.section .rodata.d1, "a"
d1:
.byte 1
-.section .data.f2, "a"
+.section .rodata.d2, "a"
d2:
.byte 1
diff --git a/test/ELF/image-base.s b/test/ELF/image-base.s
index a6d160744b70..eb79acdced81 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 84e5dc35e9d2..b10b925063e5 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-linkerscript.test b/test/ELF/invalid-linkerscript.test
index 280686eff41b..f9fb8478251f 100644
--- a/test/ELF/invalid-linkerscript.test
+++ b/test/ELF/invalid-linkerscript.test
@@ -45,7 +45,7 @@
# RUN: echo "INCLUDE /no/such/file" > %t7
# RUN: not ld.lld %t7 no-such-file 2>&1 | FileCheck -check-prefix=ERR7 %s
-# ERR7: cannot open /no/such/file
+# ERR7: cannot find linker script /no/such/file
# ERR7: cannot open no-such-file:
# RUN: echo "OUTPUT_FORMAT(x y z)" > %t8
diff --git a/test/ELF/invalid-local-symbol-in-dso.s b/test/ELF/invalid-local-symbol-in-dso.s
new file mode 100644
index 000000000000..dc6bff7bd792
--- /dev/null
+++ b/test/ELF/invalid-local-symbol-in-dso.s
@@ -0,0 +1,13 @@
+# REQUIRES: x86
+
+# We used to crash on this
+# RUN: ld.lld %p/Inputs/local-symbol-in-dso.so -o %t 2>&1 | \
+# RUN: FileCheck -check-prefix=WARN %s
+# WARN: found local symbol 'foo' in global part of symbol table in file {{.*}}local-symbol-in-dso.so
+
+# RUN: llvm-mc %s -o %t.o -filetype=obj -triple x86_64-pc-linux
+# RUN: not ld.lld %t.o %p/Inputs/local-symbol-in-dso.so -o %t
+
+.globl main
+main:
+ movq foo@GOTTPOFF(%rip), %rax
diff --git a/test/ELF/invalid-undef-section-symbol.test b/test/ELF/invalid-undef-section-symbol.test
new file mode 100644
index 000000000000..f634d6ad8c63
--- /dev/null
+++ b/test/ELF/invalid-undef-section-symbol.test
@@ -0,0 +1,27 @@
+# RUN: yaml2obj %s -o %t.o
+# RUN: not ld.lld -r %t.o -o %2.o 2>&1 | FileCheck %s
+
+# We used to crash at this.
+# CHECK: STT_SECTION symbol should be defined
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_REL
+ Machine: EM_X86_64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ - Name: .rela.text
+ Type: SHT_RELA
+ AddressAlign: 0x0000000000000008
+ Info: .text
+ Relocations:
+ - Offset: 0x0000000000000000
+ Symbol: .text
+ Type: R_X86_64_NONE
+Symbols:
+ Local:
+ - Name: .text
+ Type: STT_SECTION
diff --git a/test/ELF/invalid/Inputs/section-index2.elf b/test/ELF/invalid/Inputs/section-index2.elf
deleted file mode 100644
index 5d842880b375..000000000000
--- a/test/ELF/invalid/Inputs/section-index2.elf
+++ /dev/null
Binary files differ
diff --git a/test/ELF/invalid/invalid-debug-relocations.test b/test/ELF/invalid/invalid-debug-relocations.test
index 75e41d18514f..9c86b6743093 100644
--- a/test/ELF/invalid/invalid-debug-relocations.test
+++ b/test/ELF/invalid/invalid-debug-relocations.test
@@ -2,8 +2,7 @@
# RUN: yaml2obj %s -o %t.o
# RUN: not ld.lld -gdb-index %t.o -o %t.exe 2>&1 | FileCheck %s
-# CHECK: error: {{.*}}.o: error parsing DWARF data:
-# CHECK-NEXT: >>> failed to compute relocation: Unknown, Invalid data was encountered while parsing the file
+# CHECK: error: {{.*}}invalid-debug-relocations.test.tmp.o:(.debug_info+0x0): has non-ABS relocation Unknown (255) against symbol '_start'
!ELF
FileHeader:
diff --git a/test/ELF/invalid/invalid-elf.test b/test/ELF/invalid/invalid-elf.test
index e03450ed289c..8be0437c0680 100644
--- a/test/ELF/invalid/invalid-elf.test
+++ b/test/ELF/invalid/invalid-elf.test
@@ -20,10 +20,6 @@
# RUN: FileCheck --check-prefix=INVALID-SECTION-INDEX-LLD %s
# INVALID-SECTION-INDEX-LLD: invalid section index
-## section-index2.elf has local symbol with incorrect section index.
-# RUN: not ld.lld %p/Inputs/section-index2.elf -o %t2 2>&1 | \
-# RUN: FileCheck --check-prefix=INVALID-SECTION-INDEX-LLD %s
-
# RUN: not ld.lld %p/Inputs/multiple-eh-relocs.elf -o %t2 2>&1 | \
# RUN: FileCheck --check-prefix=INVALID-EH-RELOCS %s
# INVALID-EH-RELOCS: multiple relocation sections to one section are not supported
diff --git a/test/ELF/invalid/invalid-relocation-x64.test b/test/ELF/invalid/invalid-relocation-x64.test
index 9b8ebb59e474..a9b316415697 100644
--- a/test/ELF/invalid/invalid-relocation-x64.test
+++ b/test/ELF/invalid/invalid-relocation-x64.test
@@ -1,7 +1,10 @@
-# RUN: yaml2obj %s -o %t.o
-# RUN: not ld.lld %t.o -o /dev/null 2>&1 | FileCheck %s
-# CHECK: {{.*}}.o: unknown relocation type: Unknown (152)
-# CHECK: {{.*}}.o: unknown relocation type: Unknown (153)
+# 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:
@@ -20,8 +23,11 @@ Sections:
Info: .text
Relocations:
- Offset: 0x0000000000000000
- Symbol: ''
+ Symbol: foo
Type: 0x98
- Offset: 0x0000000000000000
- Symbol: ''
+ Symbol: foo
Type: 0x99
+Symbols:
+ Global:
+ - Name: foo
diff --git a/test/ELF/libsearch.s b/test/ELF/libsearch.s
index 32be866a66a9..d21baf9dd95f 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 000000000000..9e25d1291354
--- /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 000000000000..ceac01793599
--- /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/copy-rel-symbol-value.s b/test/ELF/linkerscript/Inputs/copy-rel-symbol-value.s
new file mode 100644
index 000000000000..b10a698eac18
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/copy-rel-symbol-value.s
@@ -0,0 +1,5 @@
+ .global bar
+ .type bar, @object
+ .size bar, 8
+bar:
+ .quad 0
diff --git a/test/ELF/linkerscript/Inputs/provide-shared.s b/test/ELF/linkerscript/Inputs/provide-shared.s
new file mode 100644
index 000000000000..5ea4e952a766
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/provide-shared.s
@@ -0,0 +1,5 @@
+ .global foo
+ .size foo, 8
+ .type foo, @object
+foo:
+ .quad 42
diff --git a/test/ELF/linkerscript/Inputs/symbol-reserved.script b/test/ELF/linkerscript/Inputs/symbol-reserved.script
new file mode 100644
index 000000000000..269bb120de95
--- /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 000000000000..513468d25814
--- /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 000000000000..9c1603a19853
--- /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 000000000000..d26f15c87329
--- /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 357f54ce1ca5..99e7382daa59 100644
--- a/test/ELF/linkerscript/align.s
+++ b/test/ELF/linkerscript/align.s
@@ -66,6 +66,51 @@
# 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
+
+## Test we fail gracefuly when alignment value is not a power of 2 (#1).
+# RUN: echo "SECTIONS { . = 0x123; . = ALIGN(0x123, 3); .aaa : { *(.aaa) } }" > %t.script
+# RUN: not ld.lld -o %t6 --script %t.script %t 2>&1 | FileCheck -check-prefix=ERR %s
+# ERR: {{.*}}.script:1: alignment must be power of 2
+
+## Test we fail gracefuly when alignment value is not a power of 2 (#2).
+# RUN: echo "SECTIONS { . = 0x123; . = ALIGN(3); .aaa : { *(.aaa) } }" > %t.script
+# RUN: not ld.lld -o %t7 --script %t.script %t 2>&1 | FileCheck -check-prefix=ERR %s
+
+# 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 000000000000..1ff1711e60be
--- /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_NOBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: ]
+
+.fnstart
+.cantunwind
+.fnend
diff --git a/test/ELF/linkerscript/arm-exidx-sentinel-and-assignment.s b/test/ELF/linkerscript/arm-exidx-sentinel-and-assignment.s
new file mode 100644
index 000000000000..8cee22f7fed2
--- /dev/null
+++ b/test/ELF/linkerscript/arm-exidx-sentinel-and-assignment.s
@@ -0,0 +1,24 @@
+# REQUIRES: arm
+# RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t.o
+# RUN: echo "SECTIONS { \
+# RUN: .ARM.exidx 0x1000 : { *(.ARM.exidx*) foo = .; } \
+# RUN: .text 0x2000 : { *(.text*) } \
+# RUN: }" > %t.script
+## We used to crash if the last output section command for .ARM.exidx
+## was anything but an input section description.
+# RUN: ld.lld --no-merge-exidx-entries -T %t.script %t.o -shared -o %t.so
+# RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi %t.so | FileCheck %s
+
+ .syntax unified
+ .text
+ .global _start
+_start:
+ .fnstart
+ .cantunwind
+ bx lr
+ .fnend
+
+// CHECK: Contents of section .ARM.exidx:
+// 1000 + 1000 = 0x2000 = _start
+// 1008 + 0ffc = 0x2004 = _start + sizeof(_start)
+// CHECK-NEXT: 1000 00100000 01000000 fc0f0000 01000000
diff --git a/test/ELF/linkerscript/at-addr.s b/test/ELF/linkerscript/at-addr.s
index 0eddf3d9e3fb..f0ab989c52c7 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 26441f1ffd9e..430e68a53d98 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 000000000000..ef2d51b1b229
--- /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 000000000000..25bb486ed445
--- /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 2e5972d52379..52f0371526c4 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 6798a217b5ac..5e8cd004068d 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/copy-rel-symbol-value-err.s b/test/ELF/linkerscript/copy-rel-symbol-value-err.s
new file mode 100644
index 000000000000..f134edbb1d0c
--- /dev/null
+++ b/test/ELF/linkerscript/copy-rel-symbol-value-err.s
@@ -0,0 +1,12 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/copy-rel-symbol-value.s -o %t2.o
+# RUN: ld.lld %t2.o -o %t2.so -shared
+# RUN: echo "SECTIONS { . = . + SIZEOF_HEADERS; foo = bar; }" > %t.script
+# RUN: not ld.lld %t.o %t2.so --script %t.script -o %t 2>&1 | FileCheck %s
+
+# CHECK: symbol not found: bar
+
+.global _start
+_start:
+.quad bar@got
diff --git a/test/ELF/linkerscript/copy-rel-symbol-value.s b/test/ELF/linkerscript/copy-rel-symbol-value.s
new file mode 100644
index 000000000000..64627b42f885
--- /dev/null
+++ b/test/ELF/linkerscript/copy-rel-symbol-value.s
@@ -0,0 +1,27 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/copy-rel-symbol-value.s -o %t2.o
+# RUN: ld.lld %t2.o -o %t2.so -shared
+# RUN: echo "SECTIONS { . = . + SIZEOF_HEADERS; foo = bar; }" > %t.script
+# RUN: ld.lld %t.o %t2.so --script %t.script -o %t
+# RUN: llvm-readobj -t %t | FileCheck %s
+
+# CHECK: Name: bar
+# CHECK-NEXT: Value: 0x[[VAL:.*]]
+# CHECK-NEXT: Size: 8
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: Object
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .bss.rel.ro
+
+# CHECK: Name: foo
+# CHECK-NEXT: Value: 0x[[VAL]]
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type:
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .bss.rel.ro
+
+.global _start
+_start:
+.quad bar
diff --git a/test/ELF/linkerscript/data-segment-relro.s b/test/ELF/linkerscript/data-segment-relro.s
index 7f69319dde7a..e835f42e22c6 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 bd1ebd01b5d2..af185729c430 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 5d9955545d92..8ad5b486cb39 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 0626e66e6fb6..5f6117863666 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/eh-frame-reloc-out-of-range.s b/test/ELF/linkerscript/eh-frame-reloc-out-of-range.s
index 54c0cc74d394..817e458fa5ef 100644
--- a/test/ELF/linkerscript/eh-frame-reloc-out-of-range.s
+++ b/test/ELF/linkerscript/eh-frame-reloc-out-of-range.s
@@ -12,7 +12,7 @@
# RUN: }" > %t.script
# RUN: not ld.lld %t.o -T %t.script -o %t 2>&1 | FileCheck %s
-# CHECK: error: {{.*}}:(.eh_frame+0x20): relocation R_X86_64_PC32 out of range
+# CHECK: error: {{.*}}:(.eh_frame+0x20): relocation R_X86_64_PC32 out of range: 64424443872 is not in [-2147483648, 2147483647]
.text
.globl _start
diff --git a/test/ELF/linkerscript/emit-reloc-section-names.s b/test/ELF/linkerscript/emit-reloc-section-names.s
new file mode 100644
index 000000000000..8661ff060a79
--- /dev/null
+++ b/test/ELF/linkerscript/emit-reloc-section-names.s
@@ -0,0 +1,22 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "SECTIONS { .text.zed : { *(.text.foo) } \
+# RUN: .text.qux : { *(.text.bar) } }" > %t.script
+# RUN: ld.lld -T %t.script --emit-relocs %t.o -o %t
+# RUN: llvm-objdump -section-headers %t | FileCheck %s
+
+## Check we name relocation sections in according to
+## their target sections names.
+
+# CHECK: .text.zed
+# CHECK: .text.qux
+# CHECK: .rela.text.zed
+# CHECK: .rela.text.qux
+
+.section .text.foo,"ax"
+foo:
+ mov $bar, %rax
+
+.section .text.bar,"ax"
+bar:
+ mov $foo, %rax
diff --git a/test/ELF/linkerscript/emit-reloc.s b/test/ELF/linkerscript/emit-reloc.s
index 725f314a9772..2fe127af8590 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/emit-relocs-multiple.s b/test/ELF/linkerscript/emit-relocs-multiple.s
index b04ca1be7a15..dcf40b3d9312 100644
--- a/test/ELF/linkerscript/emit-relocs-multiple.s
+++ b/test/ELF/linkerscript/emit-relocs-multiple.s
@@ -5,7 +5,7 @@
# RUN: llvm-readobj -r %t1 | FileCheck %s
# CHECK: Relocations [
-# CHECK-NEXT: Section {{.*}} .rela.foo {
+# CHECK-NEXT: Section {{.*}} .rela.zed {
# CHECK-NEXT: 0x1 R_X86_64_32 .zed 0x0
# CHECK-NEXT: 0x6 R_X86_64_32 .zed 0x5
# CHECK-NEXT: }
diff --git a/test/ELF/linkerscript/extend-pt-load.s b/test/ELF/linkerscript/extend-pt-load.s
index f9a77c8c12bf..72740f1092ee 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
@@ -33,18 +33,17 @@
# RUN: .hash : { } \
# RUN: .dynstr : { } \
# RUN: .text : { *(.text) } \
-# RUN: . = ALIGN(0x1000); \
-# RUN: bar : { HIDDEN(bar_sym = .); } \
+# RUN: bar : { . = ALIGN(0x1000); } \
# 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
-# CHECK2-NEXT: bar PROGBITS 0000000000001000 001000 000000 00 AX
+# CHECK2-NEXT: bar NOBITS 00000000000001bd 0001bd 000e43 00 AX
# CHECK2-NEXT: .data.rel.ro PROGBITS 0000000000001000 001000 000001 00 WA
-# CHECK2: LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x001000 0x001000 R E
+# CHECK2: LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x0001bd 0x001000 R E
# CHECK2-NEXT: LOAD 0x001000 0x0000000000001000 0x0000000000001000 0x000068 0x000068 RW
# If the current behavior becomes a problem we should consider just moving the commands out
@@ -60,7 +59,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 d4bc495efb81..5f075a8e5005 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/header-addr.s b/test/ELF/linkerscript/header-addr.s
index f37d319aaabe..70e6f674bafb 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 000000000000..8c9097d8dfa5
--- /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 000000000000..0ae463fce8a9
--- /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 9598a6abebb0..95cdf142fe42 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 000000000000..e93ed904f05a
--- /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/linker-script-in-search-path.s b/test/ELF/linkerscript/linker-script-in-search-path.s
new file mode 100644
index 000000000000..be83b55b8995
--- /dev/null
+++ b/test/ELF/linkerscript/linker-script-in-search-path.s
@@ -0,0 +1,19 @@
+# REQUIRES: x86
+# Check that we fall back to search paths if a linker script was not found
+# This behaviour matches ld.bfd and various projects appear to rely on this
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: mkdir -p %T/searchpath
+# RUN: echo "OUTPUT(\"%t.out\")" > %T/searchpath/foo.script
+# RUN: ld.lld -T%T/searchpath/foo.script %t.o
+# RUN: llvm-readobj %t.out | FileCheck %s
+# CHECK: Format: ELF64-x86-64
+
+# If the linker script specified with -T is missing we should emit an error
+# RUN: not ld.lld -Tfoo.script %t.o 2>&1 | FileCheck %s -check-prefix ERROR
+# ERROR: error: cannot find linker script foo.script
+
+# But if it exists in the search path we should fall back to that instead:
+# RUN: rm %t.out
+# RUN: ld.lld -L %T/searchpath -Tfoo.script %t.o
+# RUN: llvm-readobj %t.out | FileCheck %s
diff --git a/test/ELF/linkerscript/linkerscript.s b/test/ELF/linkerscript/linkerscript.s
index cac902af4270..6a239ea57c8d 100644
--- a/test/ELF/linkerscript/linkerscript.s
+++ b/test/ELF/linkerscript/linkerscript.s
@@ -37,8 +37,8 @@
# RUN: echo "OUTPUT(\"%t.out\")" > %T/foo.script
# RUN: not ld.lld %t.script > %t.log 2>&1
# RUN: FileCheck -check-prefix=INCLUDE_ERR %s < %t.log
-# INCLUDE_ERR: error: {{.+}}.script:1: cannot open foo.script
-# INCLUDE_ERR-NEXT: error: {{.+}}.script:1: INCLUDE "foo.script"
+# INCLUDE_ERR: error: {{.+}}.script:1: cannot find linker script 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/memory-at.s b/test/ELF/linkerscript/memory-at.s
new file mode 100644
index 000000000000..9e56dbdbd657
--- /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 000000000000..19517687b78d
--- /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 774a6f92ab17..172768394d30 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 000000000000..2e7381fb8914
--- /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/memory3.s b/test/ELF/linkerscript/memory3.s
new file mode 100644
index 000000000000..6a24313f0621
--- /dev/null
+++ b/test/ELF/linkerscript/memory3.s
@@ -0,0 +1,23 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: echo "MEMORY { ram2 (ax) : ORIGIN = 0x1000, LENGTH = 1K \
+# RUN: ram1 (ax) : ORIGIN = 0x4000, LENGTH = 1K } \
+# RUN: SECTIONS {}" > %t1.script
+# RUN: ld.lld -o %t1 --script %t1.script %t
+# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
+
+# RUN: echo "MEMORY { ram1 (ax) : ORIGIN = 0x1000, LENGTH = 1K \
+# RUN: ram2 (ax) : ORIGIN = 0x4000, LENGTH = 1K } \
+# RUN: SECTIONS {}" > %t2.script
+# RUN: ld.lld -o %t2 --script %t2.script %t
+# RUN: llvm-objdump -section-headers %t2 | FileCheck %s
+
+## Check we place .text into first defined memory region with appropriate flags.
+# CHECK: Sections:
+# CHECK: Idx Name Size Address
+# CHECK: 0 00000000 0000000000000000
+# CHECK: 1 .text 00000001 0000000000001000
+
+.section .text.foo,"ax",%progbits
+foo:
+ nop
diff --git a/test/ELF/linkerscript/merge-sections.s b/test/ELF/linkerscript/merge-sections.s
index 950d822ec403..2709bdaee444 100644
--- a/test/ELF/linkerscript/merge-sections.s
+++ b/test/ELF/linkerscript/merge-sections.s
@@ -36,7 +36,7 @@
# RUN: llvm-readobj -s -t %t2 | FileCheck %s --check-prefix=GC
# GC: Name: .foo
-# GC-NEXT: Type: SHT_PROGBITS
+# GC-NEXT: Type: SHT_NOBITS
# GC-NEXT: Flags [
# GC-NEXT: SHF_ALLOC
# GC-NEXT: ]
diff --git a/test/ELF/linkerscript/no-space.s b/test/ELF/linkerscript/no-space.s
index fc9e5b13325f..21a38e42b2a3 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/noload.s b/test/ELF/linkerscript/noload.s
index 9d0e085a0504..28be55df1f12 100644
--- a/test/ELF/linkerscript/noload.s
+++ b/test/ELF/linkerscript/noload.s
@@ -4,10 +4,10 @@
# RUN: .data_noload_a (NOLOAD) : { *(.data_noload_a) } \
# RUN: .data_noload_b (0x10000) (NOLOAD) : { *(.data_noload_b) } };" > %t.script
# RUN: ld.lld -o %t --script %t.script %t.o
-# RUN: llvm-readobj --symbols -sections %t
+# RUN: llvm-readobj --symbols -sections %t | FileCheck %s
# CHECK: Section {
-# CHECK-NEXT: Index: 2
+# CHECK: Index: 2
# CHECK-NEXT: Name: .data_noload_a
# CHECK-NEXT: Type: SHT_NOBITS
# CHECK-NEXT: Flags [
diff --git a/test/ELF/linkerscript/non-alloc.s b/test/ELF/linkerscript/non-alloc.s
index 861c74996b85..3257cb965565 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 470558d29df1..868805b34c2e 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-discard.s b/test/ELF/linkerscript/orphan-discard.s
new file mode 100644
index 000000000000..6fd6fafcd7f4
--- /dev/null
+++ b/test/ELF/linkerscript/orphan-discard.s
@@ -0,0 +1,25 @@
+# REQUIRES: x86
+# RUN: llvm-mc -position-independent -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: echo "SECTIONS { \
+# RUN: . = 0xffffffff80000000; \
+# RUN: .text : ALIGN(4096) { *(.text) } \
+# RUN: .data : ALIGN(4096) { *(.data) } \
+# RUN: .bss : ALIGN(4096) { *(.bss); } \
+# RUN: . = ALIGN(4096); \
+# RUN: _end = .; \
+# RUN: /DISCARD/ : { *(.comment) } \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t --script %t.script %t.o
+# RUN: llvm-readelf -s -symbols %t | FileCheck %s
+
+# CHECK: .bss NOBITS ffffffff80002000 002008 000002 00 WA 0 0 4096
+# CHECK: ffffffff80003000 0 NOTYPE GLOBAL DEFAULT 3 _end
+
+.section .text, "ax"
+ ret
+
+.section .data, "aw"
+ .quad 0
+
+.section .bss, "", @nobits
+ .short 0
diff --git a/test/ELF/linkerscript/orphan-end.s b/test/ELF/linkerscript/orphan-end.s
new file mode 100644
index 000000000000..5f56d8f487a2
--- /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 000000000000..648911162e97
--- /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 000000000000..241857b239c4
--- /dev/null
+++ b/test/ELF/linkerscript/orphan-report.s
@@ -0,0 +1,54 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: echo "SECTIONS { .text : { *(.text.1) } }" > %t.script
+
+## Check we do not report orphans by default even with -verbose.
+# RUN: ld.lld -shared -o %t.out --script %t.script %t.o 2>&1 -verbose \
+# RUN: | FileCheck %s --check-prefix=DEFAULT
+# DEFAULT-NOT: placed
+
+## Check --orphan-handling=place has the same behavior as default.
+# RUN: ld.lld -shared --orphan-handling=place -o %t.out --script %t.script \
+# RUN: %t.o 2>&1 -verbose -error-limit=0 | FileCheck %s --check-prefix=DEFAULT
+
+## Check --orphan-handling=error reports errors about orphans.
+# RUN: not ld.lld -shared --orphan-handling=error -o %t.out --script %t.script \
+# RUN: %t.o 2>&1 -verbose -error-limit=0 | FileCheck %s --check-prefix=REPORT
+# REPORT: {{.*}}.o:(.text) is being placed in '.text'
+# REPORT-NEXT: {{.*}}.o:(.text.2) is being placed in '.text'
+# REPORT-NEXT: <internal>:(.comment) is being placed in '.comment'
+# REPORT-NEXT: <internal>:(.bss) is being placed in '.bss'
+# REPORT-NEXT: <internal>:(.bss.rel.ro) is being placed in '.bss.rel.ro'
+# REPORT-NEXT: <internal>:(.dynsym) is being placed in '.dynsym'
+# REPORT-NEXT: <internal>:(.gnu.version) is being placed in '.gnu.version'
+# REPORT-NEXT: <internal>:(.gnu.version_r) is being placed in '.gnu.version_r'
+# REPORT-NEXT: <internal>:(.gnu.hash) is being placed in '.gnu.hash'
+# REPORT-NEXT: <internal>:(.hash) is being placed in '.hash'
+# REPORT-NEXT: <internal>:(.dynamic) is being placed in '.dynamic'
+# REPORT-NEXT: <internal>:(.dynstr) is being placed in '.dynstr'
+# REPORT-NEXT: <internal>:(.rela.dyn) is being placed in '.rela.dyn'
+# REPORT-NEXT: <internal>:(.got) is being placed in '.got'
+# REPORT-NEXT: <internal>:(.got.plt) is being placed in '.got.plt'
+# REPORT-NEXT: <internal>:(.got.plt) is being placed in '.got.plt'
+# REPORT-NEXT: <internal>:(.rela.plt) is being placed in '.rela.plt'
+# REPORT-NEXT: <internal>:(.rela.plt) is being placed in '.rela.plt'
+# REPORT-NEXT: <internal>:(.plt) is being placed in '.plt'
+# REPORT-NEXT: <internal>:(.plt) is being placed in '.plt'
+# REPORT-NEXT: <internal>:(.eh_frame) is being placed in '.eh_frame'
+# REPORT-NEXT: <internal>:(.symtab) is being placed in '.symtab'
+# REPORT-NEXT: <internal>:(.shstrtab) is being placed in '.shstrtab'
+# REPORT-NEXT: <internal>:(.strtab) is being placed in '.strtab'
+
+## Check --orphan-handling=warn reports warnings about orphans.
+# RUN: ld.lld -shared --orphan-handling=warn -o %t.out --script %t.script \
+# RUN: %t.o 2>&1 -verbose | FileCheck %s --check-prefix=REPORT
+
+# RUN: not ld.lld --orphan-handling=foo -o %t.out --script %t.script %t.o 2>&1 \
+# RUN: | FileCheck %s --check-prefix=UNKNOWN
+# UNKNOWN: unknown --orphan-handling mode: foo
+
+.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 6cfd533c4e14..c43df43e5002 100644
--- a/test/ELF/linkerscript/out-of-order.s
+++ b/test/ELF/linkerscript/out-of-order.s
@@ -1,7 +1,7 @@
# 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: 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-objdump -section-headers %t.so | FileCheck %s
# CHECK: Sections:
diff --git a/test/ELF/linkerscript/phdr-check.s b/test/ELF/linkerscript/phdr-check.s
index c7229ed3312c..b35256be6b71 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/phdrs.s b/test/ELF/linkerscript/phdrs.s
index 025ced95b30a..b65015994533 100644
--- a/test/ELF/linkerscript/phdrs.s
+++ b/test/ELF/linkerscript/phdrs.s
@@ -39,9 +39,9 @@
# RUN: ld.lld -o %t1 --script %t.script %t
# RUN: llvm-readobj -program-headers %t1 | FileCheck --check-prefix=DEFHDR %s
-## Check that error is reported when trying to use phdr which is not listed
+## Check that error is reported when trying to use phdr which is not listed
## inside PHDRS {} block
-## TODO: If script doesn't contain PHDRS {} block then default phdr is always
+## TODO: If script doesn't contain PHDRS {} block then default phdr is always
## created and error is not reported.
# RUN: echo "PHDRS { all PT_LOAD; } \
# RUN: SECTIONS { .baz : {*(.foo.*)} :bar }" > %t.script
diff --git a/test/ELF/linkerscript/provide-shared.s b/test/ELF/linkerscript/provide-shared.s
new file mode 100644
index 000000000000..e3f1bdb2ea3d
--- /dev/null
+++ b/test/ELF/linkerscript/provide-shared.s
@@ -0,0 +1,13 @@
+# 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/provide-shared.s -o %t2.o
+# RUN: ld.lld %t2.o -o %t2.so -shared
+# RUN: echo "SECTIONS { . = . + SIZEOF_HEADERS; PROVIDE(foo = 42);}" > %t.script
+# RUN: ld.lld -o %t --script %t.script %t.o %t2.so
+# RUN: llvm-objdump -t %t | FileCheck %s
+
+# CHECK: 000000000000002a *ABS* 00000000 foo
+
+.global _start
+_start:
+.quad foo
diff --git a/test/ELF/linkerscript/region-alias.s b/test/ELF/linkerscript/region-alias.s
new file mode 100644
index 000000000000..8a88f6f5ad9f
--- /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 d2d8c9dd56ef..195380be9ff8 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 0e99851910f4..99bbbead925c 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 000000000000..b3bdad9ea08b
--- /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-start.s b/test/ELF/linkerscript/segment-start.s
index e46c398f63f9..69897d604b3b 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 b0517608d51b..4611b18d55ef 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/subalign.s b/test/ELF/linkerscript/subalign.s
index 8b441d440b0e..1396798c82f6 100644
--- a/test/ELF/linkerscript/subalign.s
+++ b/test/ELF/linkerscript/subalign.s
@@ -22,6 +22,23 @@
# SUBALIGN: 01000000 00000000 02000000 00000000
# SUBALIGN: 03000000 00000000 04000000 00000000
+## Test we do not assert or crash when dot(.) is used inside SUBALIGN.
+## ld.bfd does not allow to use dot in such expressions, our behavior is
+## different for simplicity of implementation. Value of dot is undefined.
+# RUN: echo "SECTIONS { . = 0x32; .aaa : SUBALIGN(.) { *(.aaa*) } }" > %t3.script
+# RUN: ld.lld %t1.o --script %t3.script -o %t3
+# RUN: llvm-objdump -s %t3 > /dev/null
+
+## Test we are able to link with zero alignment, this is consistent with bfd 2.26.1.
+# RUN: echo "SECTIONS { .aaa : SUBALIGN(0) { *(.aaa*) } }" > %t4.script
+# RUN: ld.lld %t1.o --script %t4.script -o %t4
+# RUN: llvm-objdump -s %t4 | FileCheck -check-prefix=SUBALIGN %s
+
+## Test we fail gracefuly when alignment value is not a power of 2.
+# RUN: echo "SECTIONS { .aaa : SUBALIGN(3) { *(.aaa*) } }" > %t5.script
+# RUN: not ld.lld %t1.o --script %t5.script -o %t5 2>&1 | FileCheck -check-prefix=ERR %s
+# ERR: {{.*}}.script:1: alignment must be power of 2
+
.global _start
_start:
nop
diff --git a/test/ELF/linkerscript/symbol-assignexpr.s b/test/ELF/linkerscript/symbol-assignexpr.s
index 14a1f0890ca6..9ab03a173f1c 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 000000000000..300d8d88da97
--- /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_NOBITS
+# 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-only.s b/test/ELF/linkerscript/symbol-only.s
index 2fb57260b333..76d54f01cdc7 100644
--- a/test/ELF/linkerscript/symbol-only.s
+++ b/test/ELF/linkerscript/symbol-only.s
@@ -12,7 +12,7 @@
# CHECK: Sections:
# CHECK-NEXT: Idx Name Size Address
# CHECK-NEXT: 0 00000000 0000000000000000
-# CHECK: abc 00000000 [[ADDR:[0-9a-f]*]] DATA
+# CHECK: abc 00000000 [[ADDR:[0-9a-f]*]] BSS
# CHECK-NEXT: bar 00000000 0000000000001000 DATA
# CHECK: SYMBOL TABLE:
diff --git a/test/ELF/linkerscript/symbol-ordering-file.s b/test/ELF/linkerscript/symbol-ordering-file.s
new file mode 100644
index 000000000000..be686c420887
--- /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 e0b259597381..8ae9d0cd661a 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 4656635171c8..a6d797584417 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/thunk-gen-mips.s b/test/ELF/linkerscript/thunk-gen-mips.s
new file mode 100644
index 000000000000..97d06d0ee654
--- /dev/null
+++ b/test/ELF/linkerscript/thunk-gen-mips.s
@@ -0,0 +1,40 @@
+# REQUIRES: mips
+# RUN: llvm-mc -filetype=obj -defsym=MAIN=1 -triple=mips-unknown-freebsd %s -o %t
+# RUN: llvm-mc -filetype=obj -defsym=TARGET=1 -triple=mips-unknown-freebsd %s -o %t1
+
+# SECTIONS command with the first pattern that does not match.
+# Linking a PIC and non-PIC object files triggers the LA25 thunk generation.
+# RUN: echo "SECTIONS { \
+# RUN: .text : { \
+# RUN: *(.nomatch) \
+# RUN: %t(.text) \
+# RUN: . = . + 0x100000 ; \
+# RUN: %t1(.text) \
+# RUN: } \
+# RUN: }" > %t.script
+# RUN: ld.lld -o %t.exe --script %t.script %t %t1
+# RUN: llvm-objdump -t %t.exe | FileCheck %s
+# CHECK: SYMBOL TABLE:
+# CHECK-ANY: 00000000 .text 00000000 _start
+# CHECK-ANY: 0010000c l F .text 00000010 __LA25Thunk_too_far
+# CHECK-ANY: 00100020 g F .text 00000024 too_far
+
+.ifdef MAIN
+.global _start
+_start:
+ j too_far
+ nop
+.endif
+
+.ifdef TARGET
+ .text
+ .abicalls
+ .set noreorder
+ .globl too_far
+ .ent too_far
+too_far:
+ nop
+ jr $ra
+ nop
+ .end too_far
+.endif
diff --git a/test/ELF/linkerscript/unused-synthetic.s b/test/ELF/linkerscript/unused-synthetic.s
index c9295fff7b59..b7cedbc8e09c 100644
--- a/test/ELF/linkerscript/unused-synthetic.s
+++ b/test/ELF/linkerscript/unused-synthetic.s
@@ -13,6 +13,16 @@
# CHECK: .text
# CHECK-NEXT: .dynsym
+# Test that the size of a removed unused synthetic input section is not added
+# to the output section size. Adding a symbol assignment prevents removal of
+# the output section, but does not cause the section size to be recomputed.
+# RUN: echo "SECTIONS { \
+# RUN: .got.plt : { a_sym = .; *(.got.plt) } \
+# RUN: }" > %t2.script
+# RUN: ld.lld -shared -o %t2.so --script %t2.script %t.o
+# RUN: llvm-objdump -section-headers %t2.so | FileCheck %s --check-prefix=CHECK2
+# CHECK2: .got.plt 00000000
+
.global _start
_start:
nop
diff --git a/test/ELF/linkerscript/version-linker-symbol.s b/test/ELF/linkerscript/version-linker-symbol.s
new file mode 100644
index 000000000000..de30cf5c2ed5
--- /dev/null
+++ b/test/ELF/linkerscript/version-linker-symbol.s
@@ -0,0 +1,28 @@
+# REQUIRES: x86
+
+# RUN: echo "VER1 { global: _end; foo ; local: * ; } ;" > %t.script
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so
+# RUN: llvm-readobj -dyn-symbols %t.so | FileCheck %s
+
+# CHECK: Name: _end@@VER1
+# CHECK-NEXT: Value: 0
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: None
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .dynamic
+
+# CHECK: Name: foo@@VER1
+# CHECK-NEXT: Value: 0
+# CHECK-NEXT: Size: 0
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: None
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: .text
+
+.global foo
+foo:
+ .data
+ .quad _end
+ .quad foo
diff --git a/test/ELF/lit.local.cfg b/test/ELF/lit.local.cfg
index b93a36d2b50b..284077d687fa 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 417f9d002034..b1b213af6659 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 e4fd46997517..c858424cfd96 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 7e6ef9e0be7e..17517f6a70ea 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 000000000000..2f45a43b2428
--- /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 000000000000..a95fa6d5a58a
--- /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 000000000000..c872f9e1dd52
--- /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 000000000000..164659ce27ba
--- /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/cache.ll b/test/ELF/lto/cache.ll
index 6731f522606f..5ab74f5c5457 100644
--- a/test/ELF/lto/cache.ll
+++ b/test/ELF/lto/cache.ll
@@ -7,7 +7,7 @@
; 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: ld.lld --thinlto-cache-dir=%t.cache --thinlto-cache-policy prune_after=1h -o %t3 %t2.o %t.o
+; RUN: ld.lld --thinlto-cache-dir=%t.cache --thinlto-cache-policy prune_after=1h:prune_interval=0s -o %t3 %t2.o %t.o
; Two cached objects, plus a timestamp file and "foo", minus the file we removed.
; RUN: ls %t.cache | count 4
@@ -16,13 +16,21 @@
; 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: ld.lld --thinlto-cache-dir=%t.cache --thinlto-cache-policy cache_size_bytes=128k:prune_interval=0s -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: ld.lld --thinlto-cache-dir=%t.cache --thinlto-cache-policy cache_size_bytes=32k:prune_interval=0s -o %t3 %t2.o %t.o
; RUN: ls %t.cache | count 4
+; Setting max number of files to 0 should disable the limit, not delete everything.
+; RUN: ld.lld --thinlto-cache-dir=%t.cache --thinlto-cache-policy prune_after=0s:cache_size=0%:cache_size_files=0:prune_interval=0s -o %t3 %t2.o %t.o
+; RUN: ls %t.cache | count 4
+
+; Delete everything except for the timestamp, "foo" and one cache file.
+; RUN: ld.lld --thinlto-cache-dir=%t.cache --thinlto-cache-policy prune_after=0s:cache_size=0%:cache_size_files=1:prune_interval=0s -o %t3 %t2.o %t.o
+; RUN: ls %t.cache | count 3
+
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/data-ordering-lto.s b/test/ELF/lto/data-ordering-lto.s
new file mode 100644
index 000000000000..0364e587b908
--- /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/keep-undefined.ll b/test/ELF/lto/keep-undefined.ll
new file mode 100644
index 000000000000..cb0f4ce491f9
--- /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 000000000000..2ffdc823a484
--- /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 000000000000..6ac1a83e1ec0
--- /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 000000000000..c2a58b6e841d
--- /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 1065ca775751..57fa3041ac65 100644
--- a/test/ELF/lto/opt-level.ll
+++ b/test/ELF/lto/opt-level.ll
@@ -2,18 +2,31 @@
; 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 e29cc72bb3cc..19b141feb258 100644
--- a/test/ELF/lto/opt-remarks.ll
+++ b/test/ELF/lto/opt-remarks.ll
@@ -15,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'
@@ -29,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
@@ -49,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/relocatable.ll b/test/ELF/lto/relocatable.ll
new file mode 100644
index 000000000000..ef21f84a621a
--- /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 f7af99ed40af..c8e52ff4b4ec 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 000000000000..483184716a07
--- /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/shlib-undefined.ll b/test/ELF/lto/shlib-undefined.ll
index 0250ed761927..6d37bfa6b304 100644
--- a/test/ELF/lto/shlib-undefined.ll
+++ b/test/ELF/lto/shlib-undefined.ll
@@ -1,6 +1,6 @@
; REQUIRES: x86
; RUN: llvm-as %s -o %t.o
-; RUN: echo .global __progname > %t2.s
+; RUN: echo ".global __progname; .data; .dc.a __progname" > %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 -o %t %t.o %t2.so
diff --git a/test/ELF/lto/symbol-ordering-lto.s b/test/ELF/lto/symbol-ordering-lto.s
new file mode 100644
index 000000000000..4c29e54c476e
--- /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/thinlto.ll b/test/ELF/lto/thinlto.ll
index 99dd19130d28..37d2b88131f6 100644
--- a/test/ELF/lto/thinlto.ll
+++ b/test/ELF/lto/thinlto.ll
@@ -6,14 +6,14 @@
; First force single-threaded mode
; RUN: rm -f %t.lto.o %t1.lto.o
; RUN: ld.lld -save-temps --thinlto-jobs=1 -shared %t.o %t2.o -o %t
-; RUN: llvm-nm %t.lto.o | FileCheck %s --check-prefix=NM1
-; RUN: llvm-nm %t1.lto.o | FileCheck %s --check-prefix=NM2
+; RUN: llvm-nm %t1.lto.o | FileCheck %s --check-prefix=NM1
+; RUN: llvm-nm %t2.lto.o | FileCheck %s --check-prefix=NM2
; Next force multi-threaded mode
; RUN: rm -f %t2.lto.o %t21.lto.o
; RUN: ld.lld -save-temps --thinlto-jobs=2 -shared %t.o %t2.o -o %t2
-; RUN: llvm-nm %t2.lto.o | FileCheck %s --check-prefix=NM1
-; RUN: llvm-nm %t21.lto.o | FileCheck %s --check-prefix=NM2
+; RUN: llvm-nm %t21.lto.o | FileCheck %s --check-prefix=NM1
+; RUN: llvm-nm %t22.lto.o | FileCheck %s --check-prefix=NM2
; NM1: T f
; NM1-NOT: U g
diff --git a/test/ELF/lto/verify-invalid.ll b/test/ELF/lto/verify-invalid.ll
index 16d6a3e54f12..e6138a3cca62 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 1dd9139808b6..83e09493fb5d 100644
--- a/test/ELF/lto/wrap-1.ll
+++ b/test/ELF/lto/wrap-1.ll
@@ -19,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 06ef4064e4d1..4e82d4a0e8b0 100644
--- a/test/ELF/lto/wrap-2.ll
+++ b/test/ELF/lto/wrap-2.ll
@@ -28,11 +28,11 @@
; 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 648ab8250286..a022b9275d9f 100644
--- a/test/ELF/many-alloc-sections.s
+++ b/test/ELF/many-alloc-sections.s
@@ -1,8 +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 7ef0f7ceaac4..e3b845f305a8 100644
--- a/test/ELF/many-sections.s
+++ b/test/ELF/many-sections.s
@@ -11,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 9dbbda0ff0e9..d1acabe28126 100644
--- a/test/ELF/map-file.s
+++ b/test/ELF/map-file.s
@@ -4,15 +4,21 @@
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/map-file2.s -o %t2.o
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/map-file3.s -o %t3.o
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/map-file4.s -o %t4.o
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/map-file5.s -o %t5.o
+// RUN: ld.lld -shared %t5.o -o %t5.so -soname dso
// RUN: rm -f %t4.a
// RUN: llvm-ar rc %t4.a %t4.o
-// RUN: ld.lld %t1.o %t2.o %t3.o %t4.a -o %t -M | FileCheck -strict-whitespace %s
-// RUN: ld.lld %t1.o %t2.o %t3.o %t4.a -o %t -print-map | FileCheck -strict-whitespace %s
-// RUN: ld.lld %t1.o %t2.o %t3.o %t4.a -o %t -Map=%t.map
+// RUN: ld.lld %t1.o %t2.o %t3.o %t4.a %t5.so -o %t -M | FileCheck -strict-whitespace %s
+// RUN: ld.lld %t1.o %t2.o %t3.o %t4.a %t5.so -o %t -print-map | FileCheck -strict-whitespace %s
+// RUN: ld.lld %t1.o %t2.o %t3.o %t4.a %t5.so -o %t -Map=%t.map
// RUN: FileCheck -strict-whitespace %s < %t.map
.global _start
_start:
+ .quad sharedFoo
+ .quad sharedBar
+ callq sharedFunc1
+ callq sharedFunc2
call baz
.global _Z1fi
_Z1fi:
@@ -25,34 +31,62 @@ 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
-// CHECK-NEXT: 0000000000200158 0000000000000030 8 <internal>:(.eh_frame)
-// CHECK-NEXT: 0000000000201000 0000000000000015 4 .text
-// CHECK-NEXT: 0000000000201000 000000000000000e 4 {{.*}}{{/|\\}}map-file.s.tmp1.o:(.text)
+// CHECK-NEXT: 00000000002001c8 0000000000000078 8 .dynsym
+// CHECK-NEXT: 00000000002001c8 0000000000000078 8 <internal>:(.dynsym)
+// CHECK-NEXT: 0000000000200240 000000000000002c 8 .gnu.hash
+// CHECK-NEXT: 0000000000200240 000000000000002c 8 <internal>:(.gnu.hash)
+// CHECK-NEXT: 000000000020026c 0000000000000030 4 .hash
+// CHECK-NEXT: 000000000020026c 0000000000000030 4 <internal>:(.hash)
+// CHECK-NEXT: 000000000020029c 0000000000000031 1 .dynstr
+// CHECK-NEXT: 000000000020029c 0000000000000031 1 <internal>:(.dynstr)
+// CHECK-NEXT: 00000000002002d0 0000000000000030 8 .rela.dyn
+// CHECK-NEXT: 00000000002002d0 0000000000000030 8 <internal>:(.rela.dyn)
+// CHECK-NEXT: 0000000000200300 0000000000000030 8 .rela.plt
+// CHECK-NEXT: 0000000000200300 0000000000000030 8 <internal>:(.rela.plt)
+// CHECK-NEXT: 0000000000200330 0000000000000030 8 .eh_frame
+// CHECK-NEXT: 0000000000200330 0000000000000030 8 <internal>:(.eh_frame)
+// CHECK-NEXT: 0000000000201000 000000000000002d 4 .text
+// CHECK-NEXT: 0000000000201000 0000000000000028 4 {{.*}}{{/|\\}}map-file.s.tmp1.o:(.text)
// CHECK-NEXT: 0000000000201000 0000000000000000 0 _start
-// CHECK-NEXT: 0000000000201005 0000000000000000 0 f(int)
-// CHECK-NEXT: 000000000020100e 0000000000000000 0 local
-// CHECK-NEXT: 0000000000201010 0000000000000002 4 {{.*}}{{/|\\}}map-file.s.tmp2.o:(.text)
-// CHECK-NEXT: 0000000000201010 0000000000000000 0 foo
-// CHECK-NEXT: 0000000000201011 0000000000000000 0 bar
-// CHECK-NEXT: 0000000000201012 0000000000000000 1 {{.*}}{{/|\\}}map-file.s.tmp2.o:(.text.zed)
-// CHECK-NEXT: 0000000000201012 0000000000000000 0 zed
-// CHECK-NEXT: 0000000000201014 0000000000000000 4 {{.*}}{{/|\\}}map-file.s.tmp3.o:(.text)
-// CHECK-NEXT: 0000000000201014 0000000000000000 0 bah
-// 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: 000000000020101f 0000000000000000 0 f(int)
+// CHECK-NEXT: 0000000000201028 0000000000000000 0 local
+// CHECK-NEXT: 0000000000201028 0000000000000002 4 {{.*}}{{/|\\}}map-file.s.tmp2.o:(.text)
+// CHECK-NEXT: 0000000000201028 0000000000000000 0 foo
+// CHECK-NEXT: 0000000000201029 0000000000000000 0 bar
+// CHECK-NEXT: 000000000020102a 0000000000000000 1 {{.*}}{{/|\\}}map-file.s.tmp2.o:(.text.zed)
+// CHECK-NEXT: 000000000020102a 0000000000000000 0 zed
+// CHECK-NEXT: 000000000020102c 0000000000000000 4 {{.*}}{{/|\\}}map-file.s.tmp3.o:(.text)
+// CHECK-NEXT: 000000000020102c 0000000000000000 0 bah
+// CHECK-NEXT: 000000000020102c 0000000000000001 4 {{.*}}{{/|\\}}map-file.s.tmp4.a(map-file.s.tmp4.o):(.text)
+// CHECK-NEXT: 000000000020102c 0000000000000000 0 baz
+// CHECK-NEXT: 0000000000201030 0000000000000030 16 .plt
+// CHECK-NEXT: 0000000000201030 0000000000000030 16 <internal>:(.plt)
+// CHECK-NEXT: 0000000000201040 0000000000000000 0 sharedFunc1
+// CHECK-NEXT: 0000000000201050 0000000000000000 0 sharedFunc2
+// CHECK-NEXT: 0000000000202000 0000000000000028 8 .got.plt
+// CHECK-NEXT: 0000000000202000 0000000000000028 8 <internal>:(.got.plt)
+// CHECK-NEXT: 0000000000203000 0000000000000100 8 .dynamic
+// CHECK-NEXT: 0000000000203000 0000000000000100 8 <internal>:(.dynamic)
+// CHECK-NEXT: 0000000000204000 0000000000000010 16 .bss
+// CHECK-NEXT: 0000000000204000 0000000000000004 16 {{.*}}{{/|\\}}map-file.s.tmp1.o:(COMMON)
+// CHECK-NEXT: 0000000000204000 0000000000000004 0 common
+// CHECK-NEXT: 0000000000204004 0000000000000004 1 <internal>:(.bss)
+// CHECK-NEXT: 0000000000204004 0000000000000004 0 sharedFoo
+// CHECK-NEXT: 0000000000204008 0000000000000008 1 <internal>:(.bss)
+// CHECK-NEXT: 0000000000204008 0000000000000008 0 sharedBar
// 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 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 0000000000000198 8 .symtab
+// CHECK-NEXT: 0000000000000000 0000000000000198 8 <internal>:(.symtab)
+// CHECK-NEXT: 0000000000000000 0000000000000084 1 .shstrtab
+// CHECK-NEXT: 0000000000000000 0000000000000084 1 <internal>:(.shstrtab)
+// CHECK-NEXT: 0000000000000000 000000000000006d 1 .strtab
+// CHECK-NEXT: 0000000000000000 000000000000006d 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/merge-align.s b/test/ELF/merge-align.s
new file mode 100644
index 000000000000..dea2fc2086e4
--- /dev/null
+++ b/test/ELF/merge-align.s
@@ -0,0 +1,34 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+// RUN: ld.lld %t.o -o %t.so -shared
+// RUN: llvm-readobj -s -section-data %t.so | FileCheck %s
+
+ .section .rodata.foo,"aM",@progbits,1
+ .align 16
+ .byte 0x42
+
+ .section .rodata.bar,"aM",@progbits,1
+ .align 16
+ .byte 0x42
+
+ .section .rodata.zed,"aM",@progbits,1
+ .align 16
+ .byte 0x41
+
+// CHECK: Name: .rodata (
+// CHECK-NEXT: Type: SHT_PROGBITS
+// CHECK-NEXT: Flags [
+// CHECK-NEXT: SHF_ALLOC
+// CHECK-NEXT: SHF_MERGE
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address:
+// CHECK-NEXT: Offset:
+// CHECK-NEXT: Size: 17
+// CHECK-NEXT: Link: 0
+// CHECK-NEXT: Info: 0
+// CHECK-NEXT: AddressAlignment: 16
+// CHECK-NEXT: EntrySize: 1
+// CHECK-NEXT: SectionData (
+// CHECK-NEXT: 0000: 42000000 00000000 00000000 00000000 |
+// CHECK-NEXT: 0010: 41 |
+// CHECK-NEXT: )
diff --git a/test/ELF/merge-entsize.s b/test/ELF/merge-entsize.s
new file mode 100644
index 000000000000..c2e41ccf185a
--- /dev/null
+++ b/test/ELF/merge-entsize.s
@@ -0,0 +1,27 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+// RUN: ld.lld %t.o -o %t
+// RUN: llvm-readobj -s %t | FileCheck %s
+
+ .section .rodata.1,"aM",@progbits,1
+ .byte 0x42
+
+ .section .rodata.2,"aM",@progbits,2
+ .short 0x42
+
+// Since the output section has both .rodata.1 and .rodata.2, it
+// contains elements of different sizes and we use an entsize of 0.
+
+// CHECK: Name: .rodata (
+// CHECK-NEXT: Type: SHT_PROGBITS
+// CHECK-NEXT: Flags [
+// CHECK-NEXT: SHF_ALLOC
+// CHECK-NEXT: SHF_MERGE
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address:
+// CHECK-NEXT: Offset:
+// CHECK-NEXT: Size:
+// CHECK-NEXT: Link:
+// CHECK-NEXT: Info:
+// CHECK-NEXT: AddressAlignment:
+// CHECK-NEXT: EntrySize: 0
diff --git a/test/ELF/merge-reloc.s b/test/ELF/merge-reloc.s
index 934ac3b9384e..3dde6aa35297 100644
--- a/test/ELF/merge-reloc.s
+++ b/test/ELF/merge-reloc.s
@@ -3,14 +3,13 @@
# RUN: ld.lld %t.o -r -o %t-rel
# RUN: llvm-readobj -s -section-data %t-rel | FileCheck %s
-# When linker generates a relocatable object it should keep "merge"
-# sections as-is: do not merge content, do not join regular and
-# "merge" sections, do not joint "merge" sections with different
-# entry size.
+# When linker generates a relocatable object it does string merging in the same
+# way as for regular link. It should keep SHF_MERGE flag and set proper sh_entsize
+# value so that final link can perform the final merging optimization.
# CHECK: Section {
# CHECK: Index:
-# CHECK: Name: .rodata
+# CHECK: Name: .rodata.1 (
# CHECK-NEXT: Type: SHT_PROGBITS
# CHECK-NEXT: Flags [
# CHECK-NEXT: SHF_ALLOC
@@ -18,18 +17,18 @@
# CHECK-NEXT: ]
# CHECK-NEXT: Address:
# CHECK-NEXT: Offset:
-# CHECK-NEXT: Size: 12
+# CHECK-NEXT: Size: 4
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
# CHECK-NEXT: AddressAlignment: 4
# CHECK-NEXT: EntrySize: 4
# CHECK-NEXT: SectionData (
-# CHECK-NEXT: 0000: 42000000 42000000 42000000
+# CHECK-NEXT: 0000: 42000000
# CHECK-NEXT: )
# CHECK-NEXT: }
# CHECK: Section {
# CHECK: Index:
-# CHECK: Name: .rodata
+# CHECK: Name: .rodata.2 (
# CHECK-NEXT: Type: SHT_PROGBITS
# CHECK-NEXT: Flags [
# CHECK-NEXT: SHF_ALLOC
@@ -37,13 +36,13 @@
# CHECK-NEXT: ]
# CHECK-NEXT: Address:
# CHECK-NEXT: Offset:
-# CHECK-NEXT: Size: 16
+# CHECK-NEXT: Size: 8
# CHECK-NEXT: Link: 0
# CHECK-NEXT: Info: 0
# CHECK-NEXT: AddressAlignment: 8
# CHECK-NEXT: EntrySize: 8
# CHECK-NEXT: SectionData (
-# CHECK-NEXT: 0000: 42000000 42000000 42000000 42000000
+# CHECK-NEXT: 0000: 42000000 42000000
# CHECK-NEXT: )
# CHECK-NEXT: }
# CHECK: Section {
diff --git a/test/ELF/merge-string.s b/test/ELF/merge-string.s
index 13c89f029711..d284d0ab523c 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
@@ -34,7 +34,7 @@ zed:
// CHECK-NEXT: Link: 0
// CHECK-NEXT: Info: 0
// CHECK-NEXT: AddressAlignment: 1
-// CHECK-NEXT: EntrySize: 0
+// CHECK-NEXT: EntrySize: 1
// CHECK-NEXT: SectionData (
// CHECK-NEXT: 0000: 61626300 |abc.|
// CHECK-NEXT: )
@@ -52,9 +52,9 @@ zed:
// NOTAIL-NEXT: Link: 0
// NOTAIL-NEXT: Info: 0
// NOTAIL-NEXT: AddressAlignment: 1
-// NOTAIL-NEXT: EntrySize: 0
+// NOTAIL-NEXT: EntrySize: 1
// NOTAIL-NEXT: SectionData (
-// NOTAIL-NEXT: 0000: 61626300 626300 |abc.bc.|
+// NOTAIL-NEXT: 0000: 62630061 626300 |bc.abc.|
// NOTAIL-NEXT: )
// NOMERGE: Name: .rodata1
@@ -88,7 +88,7 @@ zed:
// CHECK-NEXT: Link: 0
// CHECK-NEXT: Info: 0
// CHECK-NEXT: AddressAlignment: 2
-// CHECK-NEXT: EntrySize: 0
+// CHECK-NEXT: EntrySize: 2
// CHECK-NEXT: SectionData (
// CHECK-NEXT: 0000: 14000000 |....|
// CHECK-NEXT: )
diff --git a/test/ELF/merge.s b/test/ELF/merge.s
index fba41346c536..b84d33a3411e 100644
--- a/test/ELF/merge.s
+++ b/test/ELF/merge.s
@@ -29,7 +29,7 @@ zed:
// CHECK-NEXT: Link: 0
// CHECK-NEXT: Info: 0
// CHECK-NEXT: AddressAlignment: 4
-// CHECK-NEXT: EntrySize: 0
+// CHECK-NEXT: EntrySize: 4
// CHECK-NEXT: SectionData (
// CHECK-NEXT: 0000: 10000000 42000000
// CHECK-NEXT: )
diff --git a/test/ELF/mips-26-n32-n64.s b/test/ELF/mips-26-n32-n64.s
new file mode 100644
index 000000000000..2e24873332a7
--- /dev/null
+++ b/test/ELF/mips-26-n32-n64.s
@@ -0,0 +1,35 @@
+# Check R_MIPS_26 relocation handling in case of N64 ABIs.
+
+# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux \
+# RUN: %S/Inputs/mips-dynamic.s -o %t-so.o
+# RUN: ld.lld %t-so.o -shared -o %t.so
+# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %s -o %t.o
+# RUN: ld.lld %t.o %t.so -o %t.exe
+# RUN: llvm-objdump -d %t.exe | FileCheck %s
+
+# REQUIRES: mips
+
+# CHECK: Disassembly of section .text:
+# CHECK-NEXT: __start:
+# CHECK-NEXT: 20000: 0c 00 80 0c jal 131120
+# CHECK-NEXT: 20004: 00 00 00 00 nop
+# CHECK-NEXT: Disassembly of section .plt:
+# CHECK-NEXT: .plt:
+# CHECK-NEXT: 20010: 3c 0e 00 03 lui $14, 3
+# CHECK-NEXT: 20014: dd d9 00 08 ld $25, 8($14)
+# CHECK-NEXT: 20018: 25 ce 00 08 addiu $14, $14, 8
+# CHECK-NEXT: 2001c: 03 0e c0 23 subu $24, $24, $14
+# CHECK-NEXT: 20020: 03 e0 78 25 move $15, $ra
+# CHECK-NEXT: 20024: 00 18 c0 c2 srl $24, $24, 3
+# 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 lui $15, 3
+# CHECK-NEXT: 20034: 8d f9 00 18 lw $25, 24($15)
+# CHECK-NEXT: 20038: 03 20 00 08 jr $25
+# CHECK-NEXT: 2003c: 25 f8 00 18 addiu $24, $15, 24
+
+ .text
+ .option pic0
+ .global __start
+__start:
+ jal foo0
diff --git a/test/ELF/mips-64-gprel-so.s b/test/ELF/mips-64-gprel-so.s
index a390ec082b1b..437238ef5f26 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 93d893aacbde..78671554b1cb 100644
--- a/test/ELF/mips-64-rels.s
+++ b/test/ELF/mips-64-rels.s
@@ -20,11 +20,11 @@
# ^-- %lo(0x17ff0)
# CHECK: Contents of section .rodata:
-# CHECK-NEXT: 10158 ffffffff fffe8014
-# ^-- 0x20004 - 0x37ff0 = 0xfffffffffffe8014
+# CHECK-NEXT: {{[0-9a-f]+}} ffffffff fffe8014
+# ^-- 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-align-err.s b/test/ELF/mips-align-err.s
index 0c71ffba8cb9..a3bf134e4386 100644
--- a/test/ELF/mips-align-err.s
+++ b/test/ELF/mips-align-err.s
@@ -4,7 +4,7 @@
# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
# RUN: -mcpu=mips32r6 %S/Inputs/mips-align-err.s -o %t2.o
# RUN: not ld.lld %t.o %t2.o -o %t.exe 2>&1 | FileCheck %s
-# CHECK: {{.*}}:(.text+0x1): improper alignment for relocation R_MIPS_PC16
+# CHECK: {{.*}}:(.text+0x1): improper alignment for relocation R_MIPS_PC16: 0xB is not aligned to 4 bytes
.globl __start
__start:
diff --git a/test/ELF/mips-elf-flags-err.s b/test/ELF/mips-elf-flags-err.s
index 28d93eb6bea7..e1ac8c5e015e 100644
--- a/test/ELF/mips-elf-flags-err.s
+++ b/test/ELF/mips-elf-flags-err.s
@@ -71,8 +71,14 @@ __start:
# R1R2-NEXT: EF_MIPS_CPIC
# R1R2-NEXT: ]
-# R3R32: target ISA 'mips3' is incompatible with 'mips32': {{.*}}mips-elf-flags-err.s.tmp2.o
-# R6OCTEON: target ISA 'mips64r6' is incompatible with 'octeon': {{.*}}mips-elf-flags-err.s.tmp2.o
+# R3R32: error: incompatible target ISA:
+# R3R32-NEXT: >>> {{.+}}mips-elf-flags-err.s.tmp1.o: mips3
+# R3R32-NEXT: >>> {{.+}}mips-elf-flags-err.s.tmp2.o: mips32
+
+# R6OCTEON: error: incompatible target ISA:
+# R6OCTEON-NEXT: >>> {{.+}}mips-elf-flags-err.s.tmp1.o: mips64r6
+# R6OCTEON-NEXT: >>> {{.+}}mips-elf-flags-err.s.tmp2.o: mips64r2 (octeon)
+
# FPABI: target floating point ABI '-mdouble-float' is incompatible with '-mgp32 -mfp64': {{.*}}mips-elf-flags-err.s.tmp2.o
# OCTEON: Flags [
diff --git a/test/ELF/mips-elf-flags.s b/test/ELF/mips-elf-flags.s
index f8f916c9353d..d2b3d929e2f5 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-page-script.s b/test/ELF/mips-got-page-script.s
new file mode 100644
index 000000000000..056e4fda77c2
--- /dev/null
+++ b/test/ELF/mips-got-page-script.s
@@ -0,0 +1,65 @@
+# Check calculation of MIPS GOT page address entries number
+# when a linker script is provided.
+
+# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux -o %t.o %s
+# RUN: echo "SECTIONS { \
+# RUN: .text : { *(.text) } \
+# RUN: .data 0x10000 : { *(.data) } }" > %t.script
+# RUN: ld.lld -shared --script %t.script -o %t.so %t.o
+# RUN: llvm-readobj -t -mips-plt-got %t.so | FileCheck %s
+
+# REQUIRES: mips
+
+# CHECK: Name: foo1
+# CHECK-NEXT: Value: 0x10000
+# CHECK: Name: foo2
+# CHECK-NEXT: Value: 0x20000
+# CHECK: Name: foo3
+# CHECK-NEXT: Value: 0x30000
+# CHECK: Name: foo4
+# CHECK-NEXT: Value: 0x40000
+
+# CHECK: Local entries [
+# CHECK-BEXT: Entry {
+# CHECK-BEXT: Address:
+# CHECK-BEXT: Access:
+# CHECK-BEXT: Initial: 0x10000
+# CHECK-BEXT: }
+# CHECK-BEXT: Entry {
+# CHECK-BEXT: Address:
+# CHECK-BEXT: Access:
+# CHECK-BEXT: Initial: 0x20000
+# CHECK-BEXT: }
+# CHECK-BEXT: Entry {
+# CHECK-BEXT: Address:
+# CHECK-BEXT: Access:
+# CHECK-BEXT: Initial: 0x30000
+# CHECK-BEXT: }
+# CHECK-BEXT: Entry {
+# CHECK-BEXT: Address:
+# CHECK-BEXT: Access:
+# CHECK-BEXT: Initial: 0x40000
+# CHECK-BEXT: }
+# CHECK-BEXT: Entry {
+# CHECK-BEXT: Address:
+# CHECK-BEXT: Access:
+# CHECK-BEXT: Initial: 0x50000
+# CHECK-BEXT: }
+# CHECK-BEXT: ]
+
+ .option pic2
+ .text
+ ld $v0,%got_page(foo1)($gp)
+ ld $v0,%got_page(foo2)($gp)
+ ld $v0,%got_page(foo3)($gp)
+ ld $v0,%got_page(foo4)($gp)
+
+ .data
+foo1:
+ .space 0x10000
+foo2:
+ .space 0x10000
+foo3:
+ .space 0x10000
+foo4:
+ .word 0
diff --git a/test/ELF/mips-got-relocs.s b/test/ELF/mips-got-relocs.s
index 4471bc210e2d..5b443e51938a 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-got-script.s b/test/ELF/mips-got-script.s
new file mode 100644
index 000000000000..da1858469863
--- /dev/null
+++ b/test/ELF/mips-got-script.s
@@ -0,0 +1,47 @@
+# Check number of got entries is adjusted for linker script-added space.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o
+# RUN: echo "SECTIONS { .data : { *(.data.1); . += 0x10000; *(.data.2) } }" > %t.script
+# RUN: ld.lld %t.o -shared -o %t.so -T %t.script
+# RUN: llvm-readobj -mips-plt-got -dynamic-table %t.so | FileCheck %s
+
+# REQUIRES: mips
+
+# CHECK: 0x7000000A MIPS_LOCAL_GOTNO 5
+# ^-- 2 * header + 3 local entries
+# CHECK: Local entries [
+# CHECK-NEXT: Entry {
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Access: -32744
+# CHECK-NEXT: Initial: 0x0
+# ^-- loc1
+# CHECK-NEXT: }
+# CHECK-NEXT: Entry {
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Access: -32740
+# CHECK-NEXT: Initial: 0x10000
+# ^-- loc2
+# CHECK-NEXT: }
+# CHECK-NEXT: Entry {
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Access: -32736
+# CHECK-NEXT: Initial: 0x20000
+# ^-- redundant
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+ .text
+ .globl foo
+foo:
+ lw $t0, %got(loc1)($gp)
+ addi $t0, $t0, %lo(loc1)
+ lw $t0, %got(loc2)($gp)
+ addi $t0, $t0, %lo(loc2)
+
+ .section .data.1,"aw",%progbits
+loc1:
+ .word 0
+
+ .section .data.2,"aw",%progbits
+loc2:
+ .word 0
diff --git a/test/ELF/mips-gp-disp.s b/test/ELF/mips-gp-disp.s
index 62a2b1084530..7a0fd6409d18 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 98b9cbc06d28..2fd21b7a9818 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 b77dbb8367ae..8bb3c236edf0 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 4f1962bd683b..507224e05d15 100644
--- a/test/ELF/mips-gprel32-relocs-gp0.s
+++ b/test/ELF/mips-gprel32-relocs-gp0.s
@@ -22,14 +22,14 @@
# DSO: GP: 0x27FF0
# DUMP: Contents of section .rodata:
-# DUMP: 00f4 ffff0004 ffff0008
-# ^ 0x10004 + 0x7ff0 - 0x27ff0
-# ^ 0x10008 + 0x7ff0 - 0x27ff0
+# DUMP: {{[0-9a-f]+}} ffff0004 ffff0008
+# ^ 0x10004 + 0x7ff0 - 0x27ff0
+# ^ 0x10008 + 0x7ff0 - 0x27ff0
# DUMP: SYMBOL TABLE:
# DUMP: 00010008 .text 00000000 bar
# DUMP: 00010004 .text 00000000 foo
-# DUMP: 00027ff0 *ABS* 00000000 .hidden _gp
+# 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 1c877b12b297..047165f19ae4 100644
--- a/test/ELF/mips-gprel32-relocs.s
+++ b/test/ELF/mips-gprel32-relocs.s
@@ -21,11 +21,11 @@ v1:
.gpword bar
# CHECK: Contents of section .rodata:
-# CHECK: 00f4 fffe8014 fffe8018
-# ^ 0x10004 - 0x27ff0
-# ^ 0x10008 - 0x27ff0
+# CHECK: {{[0-9a-f]+}} fffe8014 fffe8018
+# ^ 0x10004 - 0x27ff0
+# ^ 0x10008 - 0x27ff0
# 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 62e03c7b6760..c7229ee0da25 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 97808b515da5..0858e3f6cd17 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 000000000000..8d077f2800f1
--- /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 000000000000..653bfbfbe8ce
--- /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 000000000000..83826126f766
--- /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-plt.s b/test/ELF/mips-micro-plt.s
new file mode 100644
index 000000000000..5671dc420c55
--- /dev/null
+++ b/test/ELF/mips-micro-plt.s
@@ -0,0 +1,91 @@
+# Check less-significant bit setup for microMIPS PLT.
+
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mattr=micromips %S/Inputs/mips-dynamic.s -o %t-dso.o
+# RUN: ld.lld %t-dso.o -shared -o %t.so
+# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \
+# RUN: -mattr=micromips %s -o %t-exe.o
+# RUN: ld.lld %t-exe.o %t.so -o %t.exe
+# RUN: llvm-readobj -t -dt -mips-plt-got %t.exe | FileCheck %s
+
+# REQUIRES: mips
+
+# CHECK: Symbols [
+# CHECK: Symbol {
+# CHECK: Name: foo
+# CHECK-NEXT: Value: 0x20008
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Binding: Local
+# CHECK-NEXT: Type: None
+# CHECK-NEXT: Other [
+# CHECK-NEXT: STO_MIPS_MICROMIPS
+# CHECK-NEXT: STV_HIDDEN
+# CHECK-NEXT: ]
+# CHECK-NEXT: Section: .text
+# CHECK-NEXT: }
+# CHECK: Symbol {
+# CHECK: Name: __start
+# CHECK-NEXT: Value: 0x20000
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: None
+# CHECK-NEXT: Other [
+# CHECK-NEXT: STO_MIPS_MICROMIPS
+# CHECK-NEXT: ]
+# CHECK-NEXT: Section: .text
+# CHECK-NEXT: }
+# CHECK: Symbol {
+# CHECK: Name: foo0
+# CHECK-NEXT: Value: 0x0
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: Function
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: Undefined
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+# CHECK: DynamicSymbols [
+# CHECK: Symbol {
+# CHECK: Name: foo0
+# CHECK-NEXT: Value: 0x0
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Binding: Global
+# CHECK-NEXT: Type: Function
+# CHECK-NEXT: Other: 0
+# CHECK-NEXT: Section: Undefined
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+# CHECK: Primary GOT {
+# CHECK: Local entries [
+# CHECK-NEXT: Entry {
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Access:
+# CHECK-NEXT: Initial: 0x20009
+# CHECK-NEXT: }
+# CHECK: ]
+# CHECK: }
+
+# CHECK: PLT GOT {
+# CHECK: Entries [
+# CHECK-NEXT: Entry {
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Initial: 0x20011
+# CHECK-NEXT: Value: 0x0
+# CHECK-NEXT: Type: Function
+# CHECK-NEXT: Section: Undefined
+# CHECK-NEXT: Name: foo0@
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+# CHECK-NEXT: }
+
+ .text
+ .set micromips
+ .global foo
+ .hidden foo
+ .global __start
+__start:
+ lw $t0,%got(foo)($gp)
+ addi $t0,$t0,%lo(foo)
+foo:
+ jal foo0
diff --git a/test/ELF/mips-micro-relocs.s b/test/ELF/mips-micro-relocs.s
new file mode 100644
index 000000000000..3986711cc7f7
--- /dev/null
+++ b/test/ELF/mips-micro-relocs.s
@@ -0,0 +1,59 @@
+# 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
+
+# EB: __start:
+# EB-NEXT: 20010: 41 a3 00 01 lui $3, 1
+# EB-NEXT: 20014: 30 63 7f df addiu $3, $3, 32735
+# 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 df 7f addiu $3, $3, 32735
+# 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 000000000000..18a8fc33f53c
--- /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 21 addiu $25, $25, 33
+# 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 21 00 addiu $25, $25, 33
+# 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 7706e2591a33..954d4c30a157 100644
--- a/test/ELF/mips-n32-rels.s
+++ b/test/ELF/mips-n32-rels.s
@@ -38,11 +38,11 @@
# ^-- %lo(0x17ff0)
# CHECK: Contents of section .rodata:
-# CHECK-NEXT: 100d4 00020004
-# ^-- loc
+# CHECK-NEXT: {{[0-9a-f]+}} 00020004
+# ^-- 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-out-of-bounds-call16-reloc.s b/test/ELF/mips-out-of-bounds-call16-reloc.s
new file mode 100644
index 000000000000..64e9ab3aa7e2
--- /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: 32768 is not in [-32768, 32767]
+
+.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/no-inhibit-exec.s b/test/ELF/no-inhibit-exec.s
index d0970d93f21e..afb7aed94c12 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/non-abs-reloc.s b/test/ELF/non-abs-reloc.s
index ef9ba4466133..454104cca076 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 81e4410ac2c2..7f6ccf23d5cc 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/pack-dyn-relocs.s b/test/ELF/pack-dyn-relocs.s
new file mode 100644
index 000000000000..cb8674318ec6
--- /dev/null
+++ b/test/ELF/pack-dyn-relocs.s
@@ -0,0 +1,210 @@
+// REQUIRES: arm, aarch64
+
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %p/Inputs/arm-shared.s -o %t.a32.so.o
+// RUN: ld.lld -shared %t.a32.so.o -o %t.a32.so
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t.a32
+// RUN: ld.lld -pie --pack-dyn-relocs=none %t.a32 %t.a32.so -o %t2.a32
+// RUN: llvm-readobj -relocations %t2.a32 | FileCheck --check-prefix=UNPACKED32 %s
+// RUN: ld.lld -pie --pack-dyn-relocs=android %t.a32 %t.a32.so -o %t3.a32
+// RUN: llvm-readobj -s -dynamic-table %t3.a32 | FileCheck --check-prefix=PACKED32-HEADERS %s
+// RUN: llvm-readobj -relocations %t3.a32 | FileCheck --check-prefix=PACKED32 %s
+
+// Unpacked should have the relative relocations in their natural order.
+// UNPACKED32: 0x1000 R_ARM_RELATIVE - 0x0
+// UNPACKED32-NEXT: 0x1004 R_ARM_RELATIVE - 0x0
+// UNPACKED32-NEXT: 0x1008 R_ARM_RELATIVE - 0x0
+// UNPACKED32-NEXT: 0x100C R_ARM_RELATIVE - 0x0
+// UNPACKED32-NEXT: 0x1010 R_ARM_RELATIVE - 0x0
+// UNPACKED32-NEXT: 0x1014 R_ARM_RELATIVE - 0x0
+// UNPACKED32-NEXT: 0x1018 R_ARM_RELATIVE - 0x0
+// UNPACKED32-NEXT: 0x101C R_ARM_RELATIVE - 0x0
+
+// UNPACKED32-NEXT: 0x1024 R_ARM_RELATIVE - 0x0
+// UNPACKED32-NEXT: 0x1028 R_ARM_RELATIVE - 0x0
+// UNPACKED32-NEXT: 0x102C R_ARM_RELATIVE - 0x0
+// UNPACKED32-NEXT: 0x1030 R_ARM_RELATIVE - 0x0
+// UNPACKED32-NEXT: 0x1034 R_ARM_RELATIVE - 0x0
+// UNPACKED32-NEXT: 0x1038 R_ARM_RELATIVE - 0x0
+// UNPACKED32-NEXT: 0x103C R_ARM_RELATIVE - 0x0
+
+// UNPACKED32-NEXT: 0x1044 R_ARM_RELATIVE - 0x0
+// UNPACKED32-NEXT: 0x1048 R_ARM_RELATIVE - 0x0
+// UNPACKED32-NEXT: 0x104C R_ARM_RELATIVE - 0x0
+// UNPACKED32-NEXT: 0x1050 R_ARM_RELATIVE - 0x0
+// UNPACKED32-NEXT: 0x1054 R_ARM_RELATIVE - 0x0
+// UNPACKED32-NEXT: 0x1058 R_ARM_RELATIVE - 0x0
+// UNPACKED32-NEXT: 0x105C R_ARM_RELATIVE - 0x0
+// UNPACKED32-NEXT: 0x1060 R_ARM_RELATIVE - 0x0
+// UNPACKED32-NEXT: 0x1064 R_ARM_RELATIVE - 0x0
+
+// UNPACKED32-NEXT: 0x1020 R_ARM_ABS32 bar2 0x0
+// UNPACKED32-NEXT: 0x1040 R_ARM_ABS32 zed2 0x0
+
+// PACKED32-HEADERS: Index: 1
+// PACKED32-HEADERS-NEXT: Name: .dynsym
+
+// PACKED32-HEADERS: Name: .rel.dyn
+// PACKED32-HEADERS-NEXT: Type: SHT_ANDROID_REL
+// PACKED32-HEADERS-NEXT: Flags [ (0x2)
+// PACKED32-HEADERS-NEXT: SHF_ALLOC (0x2)
+// PACKED32-HEADERS-NEXT: ]
+// PACKED32-HEADERS-NEXT: Address: [[ADDR:.*]]
+// PACKED32-HEADERS-NEXT: Offset: [[ADDR]]
+// PACKED32-HEADERS-NEXT: Size: [[SIZE:.*]]
+// PACKED32-HEADERS-NEXT: Link: 1
+// PACKED32-HEADERS-NEXT: Info: 0
+// PACKED32-HEADERS-NEXT: AddressAlignment: 4
+// PACKED32-HEADERS-NEXT: EntrySize: 1
+
+// PACKED32-HEADERS: 0x6000000F ANDROID_REL [[ADDR]]
+// PACKED32-HEADERS: 0x60000010 ANDROID_RELSZ [[SIZE]]
+
+// Packed should have the larger groups of relative relocations first,
+// i.e. the 8 and 9 followed by the 7.
+// PACKED32: 0x1000 R_ARM_RELATIVE - 0x0
+// PACKED32-NEXT: 0x1004 R_ARM_RELATIVE - 0x0
+// PACKED32-NEXT: 0x1008 R_ARM_RELATIVE - 0x0
+// PACKED32-NEXT: 0x100C R_ARM_RELATIVE - 0x0
+// PACKED32-NEXT: 0x1010 R_ARM_RELATIVE - 0x0
+// PACKED32-NEXT: 0x1014 R_ARM_RELATIVE - 0x0
+// PACKED32-NEXT: 0x1018 R_ARM_RELATIVE - 0x0
+// PACKED32-NEXT: 0x101C R_ARM_RELATIVE - 0x0
+
+// PACKED32-NEXT: 0x1044 R_ARM_RELATIVE - 0x0
+// PACKED32-NEXT: 0x1048 R_ARM_RELATIVE - 0x0
+// PACKED32-NEXT: 0x104C R_ARM_RELATIVE - 0x0
+// PACKED32-NEXT: 0x1050 R_ARM_RELATIVE - 0x0
+// PACKED32-NEXT: 0x1054 R_ARM_RELATIVE - 0x0
+// PACKED32-NEXT: 0x1058 R_ARM_RELATIVE - 0x0
+// PACKED32-NEXT: 0x105C R_ARM_RELATIVE - 0x0
+// PACKED32-NEXT: 0x1060 R_ARM_RELATIVE - 0x0
+// PACKED32-NEXT: 0x1064 R_ARM_RELATIVE - 0x0
+
+// PACKED32-NEXT: 0x1024 R_ARM_RELATIVE - 0x0
+// PACKED32-NEXT: 0x1028 R_ARM_RELATIVE - 0x0
+// PACKED32-NEXT: 0x102C R_ARM_RELATIVE - 0x0
+// PACKED32-NEXT: 0x1030 R_ARM_RELATIVE - 0x0
+// PACKED32-NEXT: 0x1034 R_ARM_RELATIVE - 0x0
+// PACKED32-NEXT: 0x1038 R_ARM_RELATIVE - 0x0
+// PACKED32-NEXT: 0x103C R_ARM_RELATIVE - 0x0
+
+// PACKED32-NEXT: 0x1020 R_ARM_ABS32 bar2 0x0
+// PACKED32-NEXT: 0x1040 R_ARM_ABS32 zed2 0x0
+
+// RUN: llvm-mc -filetype=obj -triple=aarch64-unknown-linux %p/Inputs/shared2.s -o %t.a64.so.o
+// RUN: ld.lld -shared %t.a64.so.o -o %t.a64.so
+// RUN: llvm-mc -filetype=obj -triple=aarch64-unknown-linux %s -o %t.a64
+// RUN: ld.lld -pie --pack-dyn-relocs=none %t.a64 %t.a64.so -o %t2.a64
+// RUN: llvm-readobj -relocations %t2.a64 | FileCheck --check-prefix=UNPACKED64 %s
+// RUN: ld.lld -pie --pack-dyn-relocs=android %t.a64 %t.a64.so -o %t3.a64
+// RUN: llvm-readobj -s -dynamic-table %t3.a64 | FileCheck --check-prefix=PACKED64-HEADERS %s
+// RUN: llvm-readobj -relocations %t3.a64 | FileCheck --check-prefix=PACKED64 %s
+
+// UNPACKED64: 0x10000 R_AARCH64_RELATIVE - 0x1
+// UNPACKED64-NEXT: 0x10008 R_AARCH64_RELATIVE - 0x2
+// UNPACKED64-NEXT: 0x10010 R_AARCH64_RELATIVE - 0x3
+// UNPACKED64-NEXT: 0x10018 R_AARCH64_RELATIVE - 0x4
+// UNPACKED64-NEXT: 0x10020 R_AARCH64_RELATIVE - 0x5
+// UNPACKED64-NEXT: 0x10028 R_AARCH64_RELATIVE - 0x6
+// UNPACKED64-NEXT: 0x10030 R_AARCH64_RELATIVE - 0x7
+// UNPACKED64-NEXT: 0x10038 R_AARCH64_RELATIVE - 0x8
+
+// UNPACKED64-NEXT: 0x10048 R_AARCH64_RELATIVE - 0x1
+// UNPACKED64-NEXT: 0x10050 R_AARCH64_RELATIVE - 0x2
+// UNPACKED64-NEXT: 0x10058 R_AARCH64_RELATIVE - 0x3
+// UNPACKED64-NEXT: 0x10060 R_AARCH64_RELATIVE - 0x4
+// UNPACKED64-NEXT: 0x10068 R_AARCH64_RELATIVE - 0x5
+// UNPACKED64-NEXT: 0x10070 R_AARCH64_RELATIVE - 0x6
+// UNPACKED64-NEXT: 0x10078 R_AARCH64_RELATIVE - 0x7
+
+// UNPACKED64-NEXT: 0x10088 R_AARCH64_RELATIVE - 0x1
+// UNPACKED64-NEXT: 0x10090 R_AARCH64_RELATIVE - 0x2
+// UNPACKED64-NEXT: 0x10098 R_AARCH64_RELATIVE - 0x3
+// UNPACKED64-NEXT: 0x100A0 R_AARCH64_RELATIVE - 0x4
+// UNPACKED64-NEXT: 0x100A8 R_AARCH64_RELATIVE - 0x5
+// UNPACKED64-NEXT: 0x100B0 R_AARCH64_RELATIVE - 0x6
+// UNPACKED64-NEXT: 0x100B8 R_AARCH64_RELATIVE - 0x7
+// UNPACKED64-NEXT: 0x100C0 R_AARCH64_RELATIVE - 0x8
+// UNPACKED64-NEXT: 0x100C8 R_AARCH64_RELATIVE - 0x9
+
+// UNPACKED64-NEXT: 0x10040 R_AARCH64_ABS64 bar2 0x1
+// UNPACKED64-NEXT: 0x10080 R_AARCH64_ABS64 zed2 0x0
+
+// PACKED64: 0x10000 R_AARCH64_RELATIVE - 0x1
+// PACKED64-NEXT: 0x10008 R_AARCH64_RELATIVE - 0x2
+// PACKED64-NEXT: 0x10010 R_AARCH64_RELATIVE - 0x3
+// PACKED64-NEXT: 0x10018 R_AARCH64_RELATIVE - 0x4
+// PACKED64-NEXT: 0x10020 R_AARCH64_RELATIVE - 0x5
+// PACKED64-NEXT: 0x10028 R_AARCH64_RELATIVE - 0x6
+// PACKED64-NEXT: 0x10030 R_AARCH64_RELATIVE - 0x7
+// PACKED64-NEXT: 0x10038 R_AARCH64_RELATIVE - 0x8
+
+// PACKED64-NEXT: 0x10088 R_AARCH64_RELATIVE - 0x1
+// PACKED64-NEXT: 0x10090 R_AARCH64_RELATIVE - 0x2
+// PACKED64-NEXT: 0x10098 R_AARCH64_RELATIVE - 0x3
+// PACKED64-NEXT: 0x100A0 R_AARCH64_RELATIVE - 0x4
+// PACKED64-NEXT: 0x100A8 R_AARCH64_RELATIVE - 0x5
+// PACKED64-NEXT: 0x100B0 R_AARCH64_RELATIVE - 0x6
+// PACKED64-NEXT: 0x100B8 R_AARCH64_RELATIVE - 0x7
+// PACKED64-NEXT: 0x100C0 R_AARCH64_RELATIVE - 0x8
+// PACKED64-NEXT: 0x100C8 R_AARCH64_RELATIVE - 0x9
+
+// PACKED64-NEXT: 0x10048 R_AARCH64_RELATIVE - 0x1
+// PACKED64-NEXT: 0x10050 R_AARCH64_RELATIVE - 0x2
+// PACKED64-NEXT: 0x10058 R_AARCH64_RELATIVE - 0x3
+// PACKED64-NEXT: 0x10060 R_AARCH64_RELATIVE - 0x4
+// PACKED64-NEXT: 0x10068 R_AARCH64_RELATIVE - 0x5
+// PACKED64-NEXT: 0x10070 R_AARCH64_RELATIVE - 0x6
+// PACKED64-NEXT: 0x10078 R_AARCH64_RELATIVE - 0x7
+
+// PACKED64-NEXT: 0x10040 R_AARCH64_ABS64 bar2 0x1
+// PACKED64-NEXT: 0x10080 R_AARCH64_ABS64 zed2 0x0
+
+// PACKED64-HEADERS: Index: 1
+// PACKED64-HEADERS-NEXT: Name: .dynsym
+
+// PACKED64-HEADERS: Name: .rela.dyn
+// PACKED64-HEADERS-NEXT: Type: SHT_ANDROID_RELA
+// PACKED64-HEADERS-NEXT: Flags [ (0x2)
+// PACKED64-HEADERS-NEXT: SHF_ALLOC (0x2)
+// PACKED64-HEADERS-NEXT: ]
+// PACKED64-HEADERS-NEXT: Address: [[ADDR:.*]]
+// PACKED64-HEADERS-NEXT: Offset: [[ADDR]]
+// PACKED64-HEADERS-NEXT: Size: [[SIZE:.*]]
+// PACKED64-HEADERS-NEXT: Link: 1
+// PACKED64-HEADERS-NEXT: Info: 0
+// PACKED64-HEADERS-NEXT: AddressAlignment: 8
+// PACKED64-HEADERS-NEXT: EntrySize: 1
+
+// PACKED64-HEADERS: 0x0000000060000011 ANDROID_RELA [[ADDR]]
+// PACKED64-HEADERS: 0x0000000060000012 ANDROID_RELASZ [[SIZE]]
+
+.data
+.dc.a __ehdr_start + 1
+.dc.a __ehdr_start + 2
+.dc.a __ehdr_start + 3
+.dc.a __ehdr_start + 4
+.dc.a __ehdr_start + 5
+.dc.a __ehdr_start + 6
+.dc.a __ehdr_start + 7
+.dc.a __ehdr_start + 8
+.dc.a bar2 + 1
+
+.dc.a __ehdr_start + 1
+.dc.a __ehdr_start + 2
+.dc.a __ehdr_start + 3
+.dc.a __ehdr_start + 4
+.dc.a __ehdr_start + 5
+.dc.a __ehdr_start + 6
+.dc.a __ehdr_start + 7
+.dc.a zed2
+
+.dc.a __ehdr_start + 1
+.dc.a __ehdr_start + 2
+.dc.a __ehdr_start + 3
+.dc.a __ehdr_start + 4
+.dc.a __ehdr_start + 5
+.dc.a __ehdr_start + 6
+.dc.a __ehdr_start + 7
+.dc.a __ehdr_start + 8
+.dc.a __ehdr_start + 9
diff --git a/test/ELF/pie-weak.s b/test/ELF/pie-weak.s
index 99dbd47488fc..c4d64e3ff287 100644
--- a/test/ELF/pie-weak.s
+++ b/test/ELF/pie-weak.s
@@ -1,10 +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
@@ -12,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/ppc-relocs.s b/test/ELF/ppc-relocs.s
index 78542dd64a4c..5aa3474e6339 100644
--- a/test/ELF/ppc-relocs.s
+++ b/test/ELF/ppc-relocs.s
@@ -17,6 +17,20 @@ msg:
# CHECK: msg:
# CHECK: 11004: 66 6f 6f 00 oris 15, 19, 28416
+.section .R_PPC_ADDR16_HI,"ax",@progbits
+.globl _starti
+_starti:
+ lis 4,msgi@h
+msgi:
+ .string "foo"
+ leni = . - msgi
+
+# CHECK: Disassembly of section .R_PPC_ADDR16_HI:
+# CHECK: _starti:
+# CHECK: 11008: 3c 80 00 01 lis 4, 1
+# CHECK: msgi:
+# CHECK: 1100c: 66 6f 6f 00 oris 15, 19, 28416
+
.section .R_PPC_ADDR16_LO,"ax",@progbits
addi 4, 4, msg@l
mystr:
@@ -25,9 +39,9 @@ mystr:
# CHECK: Disassembly of section .R_PPC_ADDR16_LO:
# CHECK: .R_PPC_ADDR16_LO:
-# CHECK: 11008: 38 84 10 04 addi 4, 4, 4100
+# CHECK: 11010: 38 84 10 04 addi 4, 4, 4100
# CHECK: mystr:
-# CHECK: 1100c: 62 6c 61 68 ori 12, 19, 24936
+# CHECK: 11014: 62 6c 61 68 ori 12, 19, 24936
.align 2
.section .R_PPC_REL24,"ax",@progbits
@@ -39,7 +53,7 @@ mystr:
# CHECK: Disassembly of section .R_PPC_REL24:
# CHECK: .FR_PPC_REL24:
-# CHECK: 11014: 48 00 00 04 b .+4
+# CHECK: 1101c: 48 00 00 04 b .+4
.section .R_PPC_REL32,"ax",@progbits
.globl .FR_PPC_REL32
@@ -50,7 +64,7 @@ mystr:
# CHECK: Disassembly of section .R_PPC_REL32:
# CHECK: .FR_PPC_REL32:
-# CHECK: 11018: 00 00 00 04
+# CHECK: 11020: 00 00 00 04
.section .R_PPC_ADDR32,"ax",@progbits
.globl .FR_PPC_ADDR32
@@ -61,4 +75,16 @@ mystr:
# CHECK: Disassembly of section .R_PPC_ADDR32:
# CHECK: .FR_PPC_ADDR32:
-# CHECK: 1101c: 00 01 10 20
+# CHECK: 11024: 00 01 10 28
+
+.align 2
+.section .R_PPC_PLTREL24,"ax",@progbits
+.globl .R_PPC_PLTREL24
+.FR_PPC_PLTREL24:
+ b .Lfoox4@PLT
+.section .R_PPC_PLTREL24_2,"ax",@progbits
+.Lfoox4:
+
+# CHECK: Disassembly of section .R_PPC_PLTREL24:
+# CHECK: .R_PPC_PLTREL24:
+# CHECK: 11028: 48 00 00 04 b .+4
diff --git a/test/ELF/ppc64-addr16-error.s b/test/ELF/ppc64-addr16-error.s
index 2bc8ef2ae4d7..f16ca69957a3 100644
--- a/test/ELF/ppc64-addr16-error.s
+++ b/test/ELF/ppc64-addr16-error.s
@@ -5,4 +5,4 @@
.short sym+65539
-// CHECK: relocation R_PPC64_ADDR16 out of range
+// CHECK: relocation R_PPC64_ADDR16 out of range: 65539 is not in [-32768, 32767]
diff --git a/test/ELF/pr34660.s b/test/ELF/pr34660.s
new file mode 100644
index 000000000000..7c78bbc11c7b
--- /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 000000000000..c656be2a9279
--- /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 be8ab9e31c4f..ecd0fd872347 100644
--- a/test/ELF/progname.s
+++ b/test/ELF/progname.s
@@ -1,6 +1,6 @@
// REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
-// RUN: echo .global __progname > %t2.s
+// RUN: echo ".global __progname; .data; .dc.a __progname" > %t2.s
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %t2.s -o %t2.o
// RUN: ld.lld -shared %t2.o -o %t2.so
// RUN: ld.lld -o %t %t.o %t2.so
diff --git a/test/ELF/relocatable-comdat2.s b/test/ELF/relocatable-comdat2.s
new file mode 100644
index 000000000000..2643e645fda9
--- /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-common.s b/test/ELF/relocatable-common.s
index 3ead775492fc..698380b650fc 100644
--- a/test/ELF/relocatable-common.s
+++ b/test/ELF/relocatable-common.s
@@ -30,7 +30,10 @@
# DEFCOMM-NEXT: Binding: Global
# DEFCOMM-NEXT: Type: Object
# DEFCOMM-NEXT: Other: 0
-# DEFCOMM-NEXT: Section: COMMON (0x2)
+# DEFCOMM-NEXT: Section: COMMON
# DEFCOMM-NEXT: }
+# RUN: not ld.lld -shared --no-define-common %t1.o -o %t 2>&1 | FileCheck --check-prefix=ERROR %s
+# ERROR: error: -no-define-common not supported in non relocatable output
+
.comm common,4,4
diff --git a/test/ELF/relocatable-compressed-input.s b/test/ELF/relocatable-compressed-input.s
index 3c0199c33f7d..47d8c111452d 100644
--- a/test/ELF/relocatable-compressed-input.s
+++ b/test/ELF/relocatable-compressed-input.s
@@ -24,11 +24,11 @@
# CHECK-NEXT: AddressAlignment: 1
# CHECK-NEXT: EntrySize: 1
# CHECK-NEXT: SectionData (
-# CHECK-NEXT: 0000: {{.*}} |short unsigned i|
-# CHECK-NEXT: 0010: {{.*}} |nt.unsigned int.|
-# CHECK-NEXT: 0020: {{.*}} |long unsigned in|
-# CHECK-NEXT: 0030: {{.*}} |t.char.unsigned |
-# CHECK-NEXT: 0040: {{.*}} |char.|
+# CHECK-NEXT: 0000: {{.*}} |long unsigned in|
+# CHECK-NEXT: 0010: {{.*}} |t.unsigned char.|
+# CHECK-NEXT: 0020: {{.*}} |unsigned int.cha|
+# CHECK-NEXT: 0030: {{.*}} |r.short unsigned|
+# CHECK-NEXT: 0040: {{.*}} | int.|
# CHECK-NEXT: )
# CHECK-NEXT: }
diff --git a/test/ELF/relocatable.s b/test/ELF/relocatable.s
index 00572d07cbb9..7cb2a084c935 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 000000000000..24bf4b74ef92
--- /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 15712e39bc93..f2251bbeefc2 100644
--- a/test/ELF/relocation-copy-alias.s
+++ b/test/ELF/relocation-copy-alias.s
@@ -1,8 +1,10 @@
// 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
+// RUN: ld.lld --hash-style=sysv --gc-sections %t.o %t.so -o %t3
// RUN: llvm-readobj --dyn-symbols -r --expand-relocs %t3 | FileCheck %s
.global _start
@@ -61,7 +63,7 @@ movl $5, b2
// CHECK: Name: b3
// CHECK-NEXT: Value: [[B]]
// CHECK-NEXT: Size: 1
-// CHECK-NEXT: Binding: Weak
+// CHECK-NEXT: Binding: Global
// CHECK-NEXT: Type: Object (0x1)
// CHECK-NEXT: Other: 0
// CHECK-NEXT: Section: .bss
diff --git a/test/ELF/relocation-copy-align-common.s b/test/ELF/relocation-copy-align-common.s
index a94c208a8b22..56eab76cbf81 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 4d97e3d95d24..3f74bade3347 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 1684c409e349..947c6c73f674 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 4bb55d9684b0..3986357d66f2 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 c525012acf67..7f28fced7116 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.s b/test/ELF/relocation.s
index 77c766960f46..3359a8badda6 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-copyrel-bss-script.s b/test/ELF/relro-copyrel-bss-script.s
new file mode 100644
index 000000000000..5f3b981cae98
--- /dev/null
+++ b/test/ELF/relro-copyrel-bss-script.s
@@ -0,0 +1,40 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t.o
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/copy-in-shared.s -o %t2.o
+// RUN: ld.lld -shared %t.o %t2.o -o %t.so
+
+// A linker script that will map .bss.rel.ro into .bss.
+// RUN: echo "SECTIONS { \
+// RUN: .bss : { *(.bss) *(.bss.*) } \
+// RUN: } " > %t.script
+
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t3.o
+// RUN: ld.lld %t3.o %t.so -z relro -o %t --script=%t.script 2>&1
+// RUN: llvm-readobj --program-headers %t | FileCheck %s
+ .section .text, "ax", @progbits
+ .global bar
+ .global foo
+ .global _start
+_start:
+ callq bar
+ // Will produce .bss.rel.ro that will match in .bss, this will lose
+ // the relro property of the copy relocation.
+ .quad foo
+
+ // Non relro bss
+ .bss
+ // make large enough to affect PT_GNU_RELRO MemSize if this was marked
+ // as relro.
+ .space 0x2000
+
+// CHECK: Type: PT_GNU_RELRO (0x6474E552)
+// CHECK-NEXT: Offset:
+// CHECK-NEXT: VirtualAddress:
+// CHECK-NEXT: PhysicalAddress:
+// CHECK-NEXT: FileSize:
+// CHECK-NEXT: MemSize: 4096
+// CHECK-NEXT: Flags [ (0x4)
+// CHECK-NEXT: PF_R (0x4)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Alignment: 1
+// CHECK-NEXT: }
diff --git a/test/ELF/relro-non-contiguous-script-data.s b/test/ELF/relro-non-contiguous-script-data.s
new file mode 100644
index 000000000000..c2332d14a982
--- /dev/null
+++ b/test/ELF/relro-non-contiguous-script-data.s
@@ -0,0 +1,25 @@
+// REQUIRES: x86
+
+// RUN: echo "SECTIONS { \
+// RUN: .dynamic : { *(.dynamic) } \
+// RUN: .non_ro : { . += 1; } \
+// RUN: .jcr : { *(.jcr) } \
+// RUN: } " > %t.script
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+// RUN: not ld.lld --export-dynamic %t.o -o %t --script=%t.script 2>&1 | FileCheck %s
+
+// RUN: echo "SECTIONS { \
+// RUN: .dynamic : { *(.dynamic) } \
+// RUN: .non_ro : { BYTE(1); } \
+// RUN: .jcr : { *(.jcr) } \
+// RUN: } " > %t2.script
+// RUN: not ld.lld --export-dynamic %t.o -o %t --script=%t2.script 2>&1 | FileCheck %s
+
+// CHECK: error: section: .jcr is not contiguous with other relro sections
+
+.global _start
+_start:
+
+ // non-empty relro section
+ .section .jcr, "aw", @progbits
+ .quad 0
diff --git a/test/ELF/relro-non-contiguous.s b/test/ELF/relro-non-contiguous.s
new file mode 100644
index 000000000000..ce6680860edf
--- /dev/null
+++ b/test/ELF/relro-non-contiguous.s
@@ -0,0 +1,28 @@
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t.o
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/copy-in-shared.s -o %t2.o
+// RUN: ld.lld -shared %t.o %t2.o -o %t.so
+
+// Place the .got.plt (non relro) immediately after .dynamic. This is the
+// reverse order of the non-linker script case. The linker created .bss.rel.ro
+// section will be placed after .got.plt causing the relro to be non-contiguous.
+// RUN: echo "SECTIONS { \
+// RUN: .dynamic : { *(.dynamic) } \
+// RUN: .got.plt : { *(.got.plt) } \
+// RUN: } " > %t.script
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t3.o
+
+// Expect error for non-contiguous relro
+// RUN: not ld.lld %t3.o %t.so -z relro -o %t --script=%t.script 2>&1 | FileCheck %s
+// No error when we do not request relro.
+// RUN: ld.lld %t3.o %t.so -z norelro -o %t --script=%t.script
+// REQUIRES: x86
+
+// CHECK: error: section: .bss.rel.ro is not contiguous with other relro sections
+ .section .text, "ax", @progbits
+ .global _start
+ .global bar
+ .global foo
+_start:
+ .quad bar
+ .quad foo
+
diff --git a/test/ELF/relro-omagic.s b/test/ELF/relro-omagic.s
index 6e9d59a35279..97c3a812406d 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/relro-script.s b/test/ELF/relro-script.s
new file mode 100644
index 000000000000..f0dca67a3422
--- /dev/null
+++ b/test/ELF/relro-script.s
@@ -0,0 +1,29 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t.o
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/copy-in-shared.s -o %t2.o
+// RUN: ld.lld -shared %t.o %t2.o -o %t.so
+
+// ld.bfd and gold use .data.rel.ro rather than .bss.rel.ro. When a linker
+// script, such as ld.bfd's internal linker script has a .data.rel.ro
+// OutputSection we rename .bss.rel.ro to .data.rel.ro.bss in order to match in
+// .data.rel.ro. This keeps the relro sections contiguous.
+
+// Use the same sections and ordering as the ld.bfd internal linker script.
+// RUN: echo "SECTIONS { \
+// RUN: .data.rel.ro : { *(.data.rel.ro .data.rel.ro.*) } \
+// RUN: .dynamic : { *(.dynamic) } \
+// RUN: .got : { *(.got) } \
+// RUN: .got.plt : { *(.got.plt) } \
+// RUN: } " > %t.script
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t3.o
+// RUN: ld.lld %t3.o %t.so -o %t --script=%t.script --print-map | FileCheck %s
+
+// CHECK: .data.rel.ro
+// CHECK-NEXT: <internal>:(.bss.rel.ro)
+ .section .text, "ax", @progbits
+ .global _start
+ .global bar
+ .global foo
+_start:
+ .quad bar
+ .quad foo
diff --git a/test/ELF/reproduce-thin-archive.s b/test/ELF/reproduce-thin-archive.s
index 2de88d77f51b..c3e6e88757eb 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 0a93be08d040..69671a088473 100644
--- a/test/ELF/reproduce.s
+++ b/test/ELF/reproduce.s
@@ -33,27 +33,36 @@
# RUN: echo "{};" > dyn
# RUN: echo > file
# RUN: echo > file2
+# RUN: echo "_start" > order
+# RUN: mkdir "sysroot with spaces"
# 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: --sysroot "sysroot with spaces" --sysroot="sysroot with spaces" \
+# 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: --script {{.+}}file2
-# RSP2-NEXT: --dynamic-list {{.+}}dyn
-# RSP2-NEXT: -rpath {{.+}}file
-# RSP2-NEXT: --script {{.+}}file
-# RSP2-NEXT: --version-script [[PATH:.*]]ver
+# RSP2-NEXT: --library-path "[[BASEDIR:.+]]/foo bar"
+# RSP2-NEXT: --library-path [[BASEDIR]]/file
+# RSP2-NEXT: --script [[BASEDIR]]/file2
+# RSP2-NEXT: --dynamic-list [[BASEDIR]]/dyn
+# RSP2-NEXT: -rpath [[BASEDIR]]/file
+# RSP2-NEXT: --script [[BASEDIR]]/file
+# RSP2-NEXT: --symbol-ordering-file [[BASEDIR]]/order
+# RSP2-NEXT: --sysroot "[[BASEDIR]]/sysroot with spaces"
+# RSP2-NEXT: --sysroot "[[BASEDIR]]/sysroot with spaces"
+# RSP2-NEXT: --version-script [[BASEDIR]]/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 aa1c999fb79a..26858372ce09 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 aa7d35d914eb..79d569d69cdb 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 f3b5842945cb..1bcbedfbab71 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/segments.s b/test/ELF/segments.s
index 9307ba39fe46..4648ba97a854 100644
--- a/test/ELF/segments.s
+++ b/test/ELF/segments.s
@@ -1,7 +1,10 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
# RUN: ld.lld %t -o %t1
# RUN: llvm-readobj --program-headers %t1 | FileCheck --check-prefix=ROSEGMENT %s
+# RUN: ld.lld --omagic --no-omagic %t -o %t1
+# RUN: llvm-readobj --program-headers %t1 | FileCheck --check-prefix=ROSEGMENT %s
# ROSEGMENT: ProgramHeader {
# ROSEGMENT: Type: PT_LOAD
@@ -76,6 +79,8 @@
# RUN: ld.lld -N %t -o %t3
# RUN: llvm-readobj --program-headers %t3 | FileCheck --check-prefix=OMAGIC %s
+# RUN: ld.lld --omagic %t -o %t3
+# RUN: llvm-readobj --program-headers %t3 | FileCheck --check-prefix=OMAGIC %s
# OMAGIC: ProgramHeader {
# OMAGIC: Type: PT_LOAD
diff --git a/test/ELF/shared-lazy.s b/test/ELF/shared-lazy.s
new file mode 100644
index 000000000000..bc1e61c3c949
--- /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 d4c79d914ffb..65ad2c61359b 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
diff --git a/test/ELF/silent-ignore.test b/test/ELF/silent-ignore.test
new file mode 100644
index 000000000000..6655754ace58
--- /dev/null
+++ b/test/ELF/silent-ignore.test
@@ -0,0 +1,20 @@
+RUN: ld.lld --version \
+RUN: -allow-shlib-undefined \
+RUN: -cref \
+RUN: -g \
+RUN: -no-add-needed \
+RUN: -no-allow-shlib-undefined \
+RUN: -no-copy-dt-needed-entries \
+RUN: -no-ctors-in-init-array \
+RUN: -no-keep-memory \
+RUN: -no-warn-common \
+RUN: -no-warn-mismatch \
+RUN: -sort-common \
+RUN: -stats \
+RUN: -warn-execstack \
+RUN: -warn-once \
+RUN: -warn-shared-textrel \
+RUN: -EB \
+RUN: -EL \
+RUN: -Qy
+RUN: not ld.lld --version --not-an-ignored-argument
diff --git a/test/ELF/sort-norosegment.s b/test/ELF/sort-norosegment.s
index c5a759a08a77..bd74f2eb330a 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 daff08187cdf..cb0009c54bec 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 250fb2cb3f5c..1e75042a0c8c 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 3157a79a65cd..96c75e9236f4 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/strip-debug.s b/test/ELF/strip-debug.s
index 81f7572aa7c5..8005cfacee6c 100644
--- a/test/ELF/strip-debug.s
+++ b/test/ELF/strip-debug.s
@@ -1,25 +1,14 @@
# REQUIRES: x86
-
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux -g %s -o %t
-# RUN: ld.lld %t -o %t2
-# RUN: llvm-readobj -sections -symbols %t2 | FileCheck -check-prefix=DEFAULT %s
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
# RUN: ld.lld %t -o %t2 --strip-debug
-# RUN: llvm-readobj -sections -symbols %t2 | FileCheck -check-prefix=STRIP %s
+# RUN: llvm-readobj -sections %t2 | FileCheck %s
# RUN: ld.lld %t -o %t2 -S
-# RUN: llvm-readobj -sections -symbols %t2 | FileCheck -check-prefix=STRIP %s
+# RUN: llvm-readobj -sections %t2 | FileCheck %s
# RUN: ld.lld %t -o %t2 --strip-all
-# RUN: llvm-readobj -sections -symbols %t2 | FileCheck -check-prefix=STRIP %s
-
-# DEFAULT: Name: .debug_info
-# DEFAULT: Name: .debug_abbrev
-# DEFAULT: Name: .debug_aranges
-# DEFAULT: Name: .debug_line
+# RUN: llvm-readobj -sections %t2 | FileCheck %s
-# STRIP-NOT: Name: .debug_info
-# STRIP-NOT: Name: .debug_abbrev
-# STRIP-NOT: Name: .debug_aranges
-# STRIP-NOT: Name: .debug_line
+# CHECK-NOT: Foo
+# CHECK-NOT: Bar
-.globl _start
-_start:
- ret
+.section .debug_Foo,"",@progbits
+.section .zdebug_Bar,"",@progbits
diff --git a/test/ELF/symbol-ordering-file2.s b/test/ELF/symbol-ordering-file2.s
new file mode 100644
index 000000000000..723eef1dfb72
--- /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/synthetic-got.s b/test/ELF/synthetic-got.s
index 8d82f3177f80..375a4387f1c8 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 358b4d9dfa5b..6b40c7d3952a 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 ac88e6eaed31..1b13f26cc134 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 05473d4b5b18..167f7687451e 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 450dd634d7a9..b1686cd6d5f4 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 7f2dd605cacd..c411fc74cd68 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 0aef3c8237b6..e65fb294bc17 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 c423a3e0f246..6e8531257714 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 1432a9607da9..2dc3731eba57 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 b6608c16551c..02148b5dbff9 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 81ecc82758d7..338d95c817ee 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 7c5d6abf77f4..d5687e9bf862 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/trace-symbols.s b/test/ELF/trace-symbols.s
index eb8e17c709a5..c8ba9b21bcdb 100644
--- a/test/ELF/trace-symbols.s
+++ b/test/ELF/trace-symbols.s
@@ -31,19 +31,23 @@
# RUN: %t %t2 %t1 -o %t4 2>&1 | FileCheck -check-prefix=OBJECTD2FOO %s
# RUN: ld.lld -y foo -y common %t %t1.so %t2 -o %t3 2>&1 | \
# RUN: FileCheck -check-prefix=OBJECTD2FOO %s
-# RUN: ld.lld -y foo -y common %t %t2 %t1.a -o %t3 2>&1 | \
-# RUN: FileCheck -check-prefix=OBJECTD2FOO %s
# OBJECTD2FOO: trace-symbols.s.tmp2: definition of foo
+# RUN: ld.lld -y foo -y common %t %t2 %t1.a -o %t3 2>&1 | \
+# RUN: FileCheck -check-prefix=FOO_AND_COMMON %s
+# FOO_AND_COMMON: trace-symbols.s.tmp: reference to foo
+# FOO_AND_COMMON: trace-symbols.s.tmp2: definition of foo
+# FOO_AND_COMMON: trace-symbols.s.tmp1.a: lazy definition of common
+
# RUN: ld.lld -y foo -y common %t %t1.so %t2 -o %t3 2>&1 | \
# RUN: FileCheck -check-prefix=SHLIBDCOMMON %s
-# SHLIBDCOMMON: trace-symbols.s.tmp1.so: definition of common
+# SHLIBDCOMMON: trace-symbols.s.tmp1.so: shared definition of common
# RUN: ld.lld -y foo -y common %t %t2.so %t1.so -o %t3 2>&1 | \
# RUN: FileCheck -check-prefix=SHLIBD2FOO %s
# RUN: ld.lld -y foo %t %t1.a %t2.so -o %t3 | \
# RUN: FileCheck -check-prefix=NO-SHLIBD2FOO %s
-# SHLIBD2FOO: trace-symbols.s.tmp2.so: definition of foo
+# SHLIBD2FOO: trace-symbols.s.tmp2.so: shared definition of foo
# NO-SHLIBD2FOO-NOT: trace-symbols.s.tmp2.so: definition of foo
# RUN: ld.lld -y foo -y common %t %t2 %t1.a -o %t3 2>&1 | \
@@ -61,7 +65,7 @@
# RUN: ld.lld -y bar %t %t1.so %t2.so -o %t3 | \
# RUN: FileCheck -check-prefix=SHLIBDBAR %s
-# SHLIBDBAR: trace-symbols.s.tmp2.so: definition of bar
+# SHLIBDBAR: trace-symbols.s.tmp2.so: shared definition of bar
# RUN: ld.lld -y foo -y bar %t %t1.so %t2.so -o %t3 | \
# RUN: FileCheck -check-prefix=SHLIBRBAR %s
diff --git a/test/ELF/typed-undef.s b/test/ELF/typed-undef.s
new file mode 100644
index 000000000000..d00e07f82518
--- /dev/null
+++ b/test/ELF/typed-undef.s
@@ -0,0 +1,11 @@
+# REQUIRES: x86
+
+# We used to crash on this, check that we don't
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld %t.o -o %t -pie --unresolved-symbols=ignore-all
+
+ .global _start
+_start:
+ .quad foo - .
+ .type foo, @object
diff --git a/test/ELF/undef-broken-debug.test b/test/ELF/undef-broken-debug.test
new file mode 100644
index 000000000000..1238ebe70aca
--- /dev/null
+++ b/test/ELF/undef-broken-debug.test
@@ -0,0 +1,47 @@
+# REQUIRES: x86
+# RUN: yaml2obj %s -o %t.o
+# RUN: not ld.lld %t.o -o %t.exe 2>&1 | FileCheck %s
+
+# The debug info has a broken relocation. Check that we don't crash
+# and still report the undefined symbol.
+
+# CHECK: error: unsupported relocation target while parsing debug info
+# CHECK: error: undefined symbol: bar
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_REL
+ Machine: EM_X86_64
+Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Content: '0000000000000000'
+ - Name: .rela.text
+ Type: SHT_RELA
+ AddressAlign: 8
+ Link: .symtab
+ Info: .text
+ Relocations:
+ - Offset: 0x0000000000000000
+ Symbol: bar
+ Type: R_X86_64_64
+ - Name: .debug_line
+ Type: SHT_PROGBITS
+ Content: 3300000002001C0000000101FB0E0D000101010100000001000001006162632E7300000000000009020000000000000000140208000101
+ - Name: .rela.debug_line
+ AddressAlign: 8
+ Type: SHT_RELA
+ Link: .symtab
+ Info: .debug_line
+ Relocations:
+ - Offset: 0x0000000000000029
+ Symbol: bar
+ Type: R_X86_64_64
+Symbols:
+ Global:
+ - Name: _start
+ Section: .text
+ - Name: bar
diff --git a/test/ELF/undef-version-script.s b/test/ELF/undef-version-script.s
index 529728328747..024ac1dc0727 100644
--- a/test/ELF/undef-version-script.s
+++ b/test/ELF/undef-version-script.s
@@ -3,9 +3,6 @@
# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so
# RUN: llvm-readobj -dyn-symbols %t.so | FileCheck %s
-# This does not match gold's behavior because gold does not create undefined
-# symbols in dynsym without an appropriate (e.g. PLT) relocation in the input.
-
# CHECK: DynamicSymbols [
# CHECK-NEXT: Symbol {
# CHECK-NEXT: Name: @
@@ -38,3 +35,6 @@
.global foo
.weak bar
+.data
+ .dc.a foo
+ .dc.a bar
diff --git a/test/ELF/unresolved-symbols.s b/test/ELF/unresolved-symbols.s
index 18656dcceca3..97ecd5014b12 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 c37c90a66d92..496a29e3db5a 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 7fd60a95d1df..b5d12ee3884f 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 a8efdc42d6d4..df15bc78ca6c 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 9ab6cd791484..208d8ecf8f62 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 d0d0067250aa..27ab047e8222 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 ea3f664ea6ce..bd786d913369 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 2b89839c369a..c63ff817fb40 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-twice.s b/test/ELF/version-script-twice.s
index 3aeedd5b5ddc..1227dcd50f1d 100644
--- a/test/ELF/version-script-twice.s
+++ b/test/ELF/version-script-twice.s
@@ -7,7 +7,11 @@
.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
diff --git a/test/ELF/version-script.s b/test/ELF/version-script.s
index 72f9eeb944d1..abc716250eba 100644
--- a/test/ELF/version-script.s
+++ b/test/ELF/version-script.s
@@ -42,6 +42,12 @@
# RUN: ld.lld --version-script %t.script --dynamic-list %t.list %t.o %t2.so -o %t2
# RUN: llvm-readobj %t2 > /dev/null
+## Check that we can handle multiple "--version-script" options.
+# RUN: echo "VERSION_1.0 { global : foo1; local : *; };" > %t7a.script
+# RUN: echo "VERSION_2.0 { global: foo3; local: *; };" > %t7b.script
+# RUN: ld.lld --version-script %t7a.script --version-script %t7b.script -shared %t.o %t2.so -o %t7.so
+# RUN: llvm-readobj -dyn-symbols %t7.so | FileCheck --check-prefix=VERDSO %s
+
# DSO: DynamicSymbols [
# DSO-NEXT: Symbol {
# DSO-NEXT: Name: @
@@ -142,12 +148,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 000000000000..427230ab82c6
--- /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 000000000000..164bc1730832
--- /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 000000000000..113013ea2e0f
--- /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 000000000000..acd42f4845de
--- /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 b6340339ebac..09c2a4c4440f 100644
--- a/test/ELF/weak-undef.s
+++ b/test/ELF/weak-undef.s
@@ -13,9 +13,21 @@
# 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
.globl _start
_start:
+
+.data
+ .dc.a foo
diff --git a/test/ELF/wrap-no-real.s b/test/ELF/wrap-no-real.s
new file mode 100644
index 000000000000..100efa6bbc28
--- /dev/null
+++ b/test/ELF/wrap-no-real.s
@@ -0,0 +1,77 @@
+// 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 %t | FileCheck -check-prefix=SYM %s
+
+// Test the full symbol table. It is verbose, but lld at times
+// produced duplicated symbols which are hard to test otherwise.
+
+// SYM: Symbols [
+// SYM-NEXT: Symbol {
+// SYM-NEXT: Name: (0)
+// SYM-NEXT: Value:
+// SYM-NEXT: Size:
+// SYM-NEXT: Binding:
+// SYM-NEXT: Type
+// SYM-NEXT: Other:
+// SYM-NEXT: Section:
+// SYM-NEXT: }
+// SYM-NEXT: Symbol {
+// SYM-NEXT: Name: _DYNAMIC
+// SYM-NEXT: Value:
+// SYM-NEXT: Size:
+// SYM-NEXT: Binding:
+// SYM-NEXT: Type:
+// SYM-NEXT: Other [
+// SYM-NEXT: STV_HIDDEN
+// SYM-NEXT: ]
+// SYM-NEXT: Section: .dynamic
+// SYM-NEXT: }
+// SYM-NEXT: Symbol {
+// SYM-NEXT: Name: foo
+// SYM-NEXT: Value: 0x11000
+// SYM-NEXT: Size:
+// SYM-NEXT: Binding:
+// SYM-NEXT: Type:
+// SYM-NEXT: Other:
+// SYM-NEXT: Section:
+// SYM-NEXT: }
+// SYM-NEXT: Symbol {
+// SYM-NEXT: Name: _start
+// SYM-NEXT: Value:
+// SYM-NEXT: Size:
+// SYM-NEXT: Binding:
+// SYM-NEXT: Type
+// SYM-NEXT: Other:
+// SYM-NEXT: Section:
+// SYM-NEXT: }
+// SYM-NEXT: Symbol {
+// SYM-NEXT: Name: __wrap_foo
+// SYM-NEXT: Value: 0x11010
+// SYM-NEXT: Size:
+// SYM-NEXT: Binding:
+// SYM-NEXT: Type:
+// SYM-NEXT: Other:
+// SYM-NEXT: Section:
+// SYM-NEXT: }
+// SYM-NEXT: ]
+
+.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 3e75fdbad811..b96917b7be49 100644
--- a/test/ELF/wrap.s
+++ b/test/ELF/wrap.s
@@ -12,17 +12,31 @@
// CHECK-NEXT: movl $0x11010, %edx
// CHECK-NEXT: movl $0x11000, %edx
-// This shows an oddity of our implementation. The symbol foo gets
-// mapped to __wrap_foo, but stays in the symbol table. This results
-// in it showing up twice in the output.
+// RUN: llvm-readobj -t %t3 > %t4.dump
+// RUN: FileCheck --check-prefix=SYM1 %s < %t4.dump
+// RUN: FileCheck --check-prefix=SYM2 %s < %t4.dump
+// RUN: FileCheck --check-prefix=SYM3 %s < %t4.dump
-// 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: __wrap_foo
-// SYM-NEXT: Value: 0x11010
+// SYM1: Name: foo
+// SYM1-NEXT: Value: 0x11000
+// SYM1-NEXT: Size:
+// SYM1-NEXT: Binding: Global
+// SYM1-NEXT: Type: None
+// SYM1-NEXT: Other: 0
+// SYM2: Name: __wrap_foo
+// SYM2-NEXT: Value: 0x11010
+// SYM2-NEXT: Size:
+// SYM2-NEXT: Binding: Weak
+// SYM2-NEXT: Type: None
+// SYM2-NEXT: Other [
+// SYM2-NEXT: STV_PROTECTED
+// SYM2-NEXT: ]
+// SYM3: Name: __real_foo
+// SYM3-NEXT: Value: 0x11020
+// SYM3-NEXT: Size:
+// SYM3-NEXT: Binding: Global
+// SYM3-NEXT: Type: None
+// SYM3-NEXT: Other: 0
.global _start
_start:
diff --git a/test/ELF/x86-64-dyn-rel-error.s b/test/ELF/x86-64-dyn-rel-error.s
index ee39e2cb83fd..f479eca2e778 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 c4291202f035..0caf6dcec1fa 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-reloc-16.s b/test/ELF/x86-64-reloc-16.s
index 2954d9900cfa..4822ec71757b 100644
--- a/test/ELF/x86-64-reloc-16.s
+++ b/test/ELF/x86-64-reloc-16.s
@@ -9,6 +9,6 @@
// CHECK-NEXT: 200000 42
// RUN: not ld.lld -shared %t %t2 -o %t4 2>&1 | FileCheck --check-prefix=ERROR %s
-// ERROR: relocation R_X86_64_16 out of range
+// ERROR: relocation R_X86_64_16 out of range: 65536 is not in [0, 65535]
.short foo
diff --git a/test/ELF/x86-64-reloc-8.s b/test/ELF/x86-64-reloc-8.s
index 1c3831fafa20..8f6ba5aa14bb 100644
--- a/test/ELF/x86-64-reloc-8.s
+++ b/test/ELF/x86-64-reloc-8.s
@@ -9,6 +9,6 @@
// CHECK-NEXT: 200000 42
// RUN: not ld.lld -shared %t %t2 -o %t4 2>&1 | FileCheck --check-prefix=ERROR %s
-// ERROR: relocation R_X86_64_8 out of range
+// ERROR: relocation R_X86_64_8 out of range: 256 is not in [0, 255]
.byte foo
diff --git a/test/ELF/x86-64-reloc-error.s b/test/ELF/x86-64-reloc-error.s
index ece1bd45aa4f..cb600d9bf1e3 100644
--- a/test/ELF/x86-64-reloc-error.s
+++ b/test/ELF/x86-64-reloc-error.s
@@ -6,5 +6,5 @@
movl $big, %edx
movq $foo - 0x1000000000000, %rdx
-# CHECK: {{.*}}:(.text+0x1): relocation R_X86_64_32 out of range
-# CHECK: {{.*}}:(.text+0x8): relocation R_X86_64_32S out of range
+# CHECK: {{.*}}:(.text+0x1): relocation R_X86_64_32 out of range: 68719476736 is not in [0, 4294967295]
+# CHECK: {{.*}}:(.text+0x8): relocation R_X86_64_32S out of range: -281474976710656 is not in [-2147483648, 2147483647]
diff --git a/test/ELF/x86-64-reloc-range.s b/test/ELF/x86-64-reloc-range.s
index 08f604ee6a30..2913458ab5cb 100644
--- a/test/ELF/x86-64-reloc-range.s
+++ b/test/ELF/x86-64-reloc-range.s
@@ -1,7 +1,7 @@
// RUN: llvm-mc %s -o %t.o -triple x86_64-pc-linux -filetype=obj
// RUN: not ld.lld %t.o -o %t.so -shared 2>&1 | FileCheck %s
-// CHECK: {{.*}}:(.text+0x3): relocation R_X86_64_PC32 out of range
+// CHECK: {{.*}}:(.text+0x3): relocation R_X86_64_PC32 out of range: 2147483648 is not in [-2147483648, 2147483647]
// CHECK-NOT: relocation
lea foo(%rip), %rax
diff --git a/test/ELF/x86-64-tls-gd-local.s b/test/ELF/x86-64-tls-gd-local.s
index ec6115dc2f75..8879737ad3fb 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 000000000000..8bf70a9b24fa
--- /dev/null
+++ b/test/MinGW/driver.test
@@ -0,0 +1,126 @@
+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
+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
+
+RUN: ld.lld -### -m i386pep foo.o -Xlink=-lldmap | FileCheck -check-prefix XLINK %s
+XLINK: -lldmap
+
+RUN: ld.lld -### -m i386pep foo.o | FileCheck -check-prefix DEBUG %s
+DEBUG: -debug:dwarf
+
+RUN: ld.lld -### -m i386pep foo.o -s | FileCheck -check-prefix STRIP %s
+RUN: ld.lld -### -m i386pep foo.o --strip-all | FileCheck -check-prefix STRIP %s
+STRIP-NOT: -debug:dwarf
+
+RUN: ld.lld -### -m i386pep foo.o --large-address-aware | FileCheck -check-prefix LARGE-ADDRESS-AWARE %s
+LARGE-ADDRESS-AWARE: -largeaddressaware
+
+RUN: ld.lld -### -m i386pe foo.o | FileCheck -check-prefix DEFAULT-DISABLE-FLAGS %s
+RUN: ld.lld -### -m i386pep foo.o | FileCheck -check-prefix DEFAULT-DISABLE-FLAGS %s
+DEFAULT-DISABLE-FLAGS: -dynamicbase:no
+RUN: ld.lld -### -m i386pe --dynamicbase foo.o | FileCheck -check-prefix NO-DEFAULT-DISABLE-FLAGS %s
+RUN: ld.lld -### -m i386pep -dynamicbase foo.o | FileCheck -check-prefix NO-DEFAULT-DISABLE-FLAGS %s
+RUN: ld.lld -### -m thumb2pe foo.o | FileCheck -check-prefix NO-DEFAULT-DISABLE-FLAGS %s
+RUN: ld.lld -### -m arm64pe foo.o | FileCheck -check-prefix NO-DEFAULT-DISABLE-FLAGS %s
+NO-DEFAULT-DISABLE-FLAGS-NOT: -dynamicbase:no
+
+RUN: ld.lld -### -m i386pep foo.o --image-base 0x1230000 | FileCheck -check-prefix IMAGE-BASE %s
+RUN: ld.lld -### -m i386pep foo.o -image-base 0x1230000 | FileCheck -check-prefix IMAGE-BASE %s
+IMAGE-BASE: -base:0x1230000
+
+RUN: ld.lld -### -m i386pep foo.o | FileCheck -check-prefix NO-GC-SECTIONS %s
+RUN: ld.lld -### -m i386pep foo.o --gc-sections --no-gc-sections | FileCheck -check-prefix NO-GC-SECTIONS %s
+NO-GC-SECTIONS: -opt:noref
+
+RUN: ld.lld -### -m i386pep foo.o --gc-sections | FileCheck -check-prefix GC-SECTIONS %s
+RUN: ld.lld -### -m i386pep foo.o -gc-sections | FileCheck -check-prefix GC-SECTIONS %s
+GC-SECTIONS: -opt:ref
+
+RUN: ld.lld -### -m i386pep foo.o | FileCheck -check-prefix ICF-NONE %s
+RUN: ld.lld -### -m i386pep foo.o --icf=none | FileCheck -check-prefix ICF-NONE %s
+RUN: ld.lld -### -m i386pep foo.o -icf=none | FileCheck -check-prefix ICF-NONE %s
+RUN: ld.lld -### -m i386pep foo.o --icf=safe | FileCheck -check-prefix ICF-NONE %s
+RUN: ld.lld -### -m i386pep foo.o -icf=safe | FileCheck -check-prefix ICF-NONE %s
+ICF-NONE: -opt:noicf
+
+RUN: ld.lld -### -m i386pep foo.o --icf=all | FileCheck -check-prefix ICF %s
+RUN: ld.lld -### -m i386pep foo.o -icf=all | FileCheck -check-prefix ICF %s
+ICF: -opt:icf
diff --git a/test/MinGW/lib.test b/test/MinGW/lib.test
new file mode 100644
index 000000000000..56104d6d907a
--- /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 4bc973a58edd..dac9f5dc6c33 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 c2f3054a2d59..cc12117ad0bf 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 95bf3c0dc434..000000000000
--- a/test/lit.cfg
+++ /dev/null
@@ -1,270 +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', '.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__)
-
-# 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 = subprocess.check_output(['llvm-config', '--src-root']).strip()
- llvm_obj_root = subprocess.check_output(['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'AMDGPU', archs):
- config.available_features.add('amdgpu')
-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'PowerPC', archs):
- config.available_features.add('ppc')
-if re.search(r'Sparc', archs):
- config.available_features.add('sparc')
-if re.search(r'X86', archs):
- config.available_features.add('x86')
-llvm_config_cmd.wait()
-
-# 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.
-if lit.util.which('cvtres', 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 000000000000..ae0dd187c607
--- /dev/null
+++ b/test/lit.cfg.py
@@ -0,0 +1,93 @@
+# -*- 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 = [
+ 'llc', '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',
+ 'WebAssembly': 'wasm',
+ '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')
+
+tar_executable = lit.util.which('tar', config.environment['PATH'])
+if tar_executable:
+ tar_version = subprocess.Popen(
+ [tar_executable, '--version'], stdout=subprocess.PIPE, env={'LANG': 'C'})
+ if 'GNU tar' in tar_version.stdout.read().decode():
+ config.available_features.add('gnutar')
+ tar_version.wait()
diff --git a/test/lit.site.cfg.in b/test/lit.site.cfg.py.in
index 1fb8d3690708..50593f7d01bd 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/wasm/Inputs/archive1.ll b/test/wasm/Inputs/archive1.ll
new file mode 100644
index 000000000000..c942fa2c1b55
--- /dev/null
+++ b/test/wasm/Inputs/archive1.ll
@@ -0,0 +1,7 @@
+declare i32 @bar() local_unnamed_addr #1
+
+define i32 @foo() local_unnamed_addr #0 {
+entry:
+ %call = tail call i32 @bar() #2
+ ret i32 %call
+}
diff --git a/test/wasm/Inputs/archive2.ll b/test/wasm/Inputs/archive2.ll
new file mode 100644
index 000000000000..35534dc9e076
--- /dev/null
+++ b/test/wasm/Inputs/archive2.ll
@@ -0,0 +1,7 @@
+declare i32 @foo() local_unnamed_addr #1
+
+define i32 @bar() local_unnamed_addr #0 {
+entry:
+ %call = tail call i32 @foo() #2
+ ret i32 %call
+}
diff --git a/test/wasm/Inputs/call-indirect.ll b/test/wasm/Inputs/call-indirect.ll
new file mode 100644
index 000000000000..388ea60c3a07
--- /dev/null
+++ b/test/wasm/Inputs/call-indirect.ll
@@ -0,0 +1,17 @@
+@indirect_bar = internal local_unnamed_addr global i32 ()* @bar, align 4
+@indirect_foo = internal local_unnamed_addr global i32 ()* @foo, align 4
+
+declare i32 @foo() local_unnamed_addr
+
+define i32 @bar() {
+entry:
+ ret i32 1
+}
+
+define void @call_bar_indirect() local_unnamed_addr #1 {
+entry:
+ %0 = load i32 ()*, i32 ()** @indirect_bar, align 4
+ %1 = load i32 ()*, i32 ()** @indirect_foo, align 4
+ %call = tail call i32 %0() #2
+ ret void
+}
diff --git a/test/wasm/Inputs/hello.ll b/test/wasm/Inputs/hello.ll
new file mode 100644
index 000000000000..93df0f559809
--- /dev/null
+++ b/test/wasm/Inputs/hello.ll
@@ -0,0 +1,15 @@
+; Wasm module generated from the following C code:
+; void puts(const char*);
+; void hello() { puts("hello\n"); }
+
+@hello_str = unnamed_addr constant [7 x i8] c"hello\0A\00", align 1
+
+; Function Attrs: nounwind
+define hidden void @hello() local_unnamed_addr #0 {
+entry:
+ tail call void @puts(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @hello_str, i32 0, i32 0))
+ ret void
+}
+
+; Function Attrs: nounwind
+declare void @puts(i8* nocapture readonly) local_unnamed_addr #1
diff --git a/test/wasm/Inputs/hidden.ll b/test/wasm/Inputs/hidden.ll
new file mode 100644
index 000000000000..25890e9f03f2
--- /dev/null
+++ b/test/wasm/Inputs/hidden.ll
@@ -0,0 +1,11 @@
+; Function Attrs: norecurse nounwind readnone
+define hidden i32 @archiveHidden() #0 {
+entry:
+ ret i32 0
+}
+
+; Function Attrs: norecurse nounwind readnone
+define i32 @archiveDefault() #1 {
+entry:
+ ret i32 0
+}
diff --git a/test/wasm/Inputs/many-funcs.ll b/test/wasm/Inputs/many-funcs.ll
new file mode 100644
index 000000000000..b8daab23638d
--- /dev/null
+++ b/test/wasm/Inputs/many-funcs.ll
@@ -0,0 +1,776 @@
+@g0 = global i32 1, align 4
+@foo = global i32 1, align 4
+
+define i32 @f1() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f2() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f3() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f4() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f5() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f6() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f7() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f8() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f9() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f10() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f11() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f12() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f13() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f14() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f15() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f16() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f17() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f18() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f19() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f20() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f21() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f22() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f23() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f24() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f25() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f26() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f27() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f28() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f29() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f30() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f31() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f32() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f33() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f34() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f35() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f36() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f37() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f38() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f39() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f40() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f41() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f42() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f43() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f44() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f45() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f46() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f47() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f48() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f49() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f50() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f51() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f52() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f53() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f54() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f55() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f56() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f57() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f58() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f59() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f60() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f61() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f62() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f63() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f64() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f65() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f66() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f67() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f68() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f69() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f70() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f71() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f72() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f73() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f74() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f75() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f76() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f77() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f78() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f79() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f80() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f81() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f82() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f83() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f84() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f85() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f86() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f87() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f88() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f89() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f90() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f91() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f92() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f93() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f94() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f95() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f96() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f97() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f98() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f99() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f100() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f101() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f102() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f103() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f104() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f105() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f106() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f107() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f108() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f109() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f110() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f111() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f112() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f113() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f114() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f115() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f116() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f117() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f118() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f119() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f120() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f121() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f122() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f123() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f124() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f125() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f126() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f127() {
+entry:
+ %0 = load i32, i32* @foo, align 4
+ ret i32 %0
+}
+
+define i32 @f128() {
+entry:
+ %0 = load i32, i32* @g0, align 4
+ ret i32 %0
+}
+
+define i32 @f129() {
+entry:
+ %0 = load i32, i32* @g0, align 4
+ ret i32 %0
+}
diff --git a/test/wasm/Inputs/ret32.ll b/test/wasm/Inputs/ret32.ll
new file mode 100644
index 000000000000..a4565288f08b
--- /dev/null
+++ b/test/wasm/Inputs/ret32.ll
@@ -0,0 +1,6 @@
+; Function Attrs: norecurse nounwind readnone
+define i32 @ret32(float %arg) #0 {
+entry:
+ ret i32 0
+ ; ptrtoint (i32 (float)* @ret32 to i32)
+}
diff --git a/test/wasm/Inputs/ret64.ll b/test/wasm/Inputs/ret64.ll
new file mode 100644
index 000000000000..6a9de0dace1d
--- /dev/null
+++ b/test/wasm/Inputs/ret64.ll
@@ -0,0 +1,4 @@
+define i64 @ret64(double %arg) local_unnamed_addr #0 {
+entry:
+ ret i64 1
+}
diff --git a/test/wasm/Inputs/weak-alias.ll b/test/wasm/Inputs/weak-alias.ll
new file mode 100644
index 000000000000..079e68e3ce7a
--- /dev/null
+++ b/test/wasm/Inputs/weak-alias.ll
@@ -0,0 +1,13 @@
+; Function Attrs: norecurse nounwind readnone
+define i32 @foo() #0 {
+entry:
+ ret i32 0
+}
+
+@bar = weak alias i32 (), i32 ()* @foo
+
+define i32 @call_bar() #0 {
+entry:
+ %call = call i32 @bar()
+ ret i32 %call
+}
diff --git a/test/wasm/Inputs/weak-symbol1.ll b/test/wasm/Inputs/weak-symbol1.ll
new file mode 100644
index 000000000000..2d561716f825
--- /dev/null
+++ b/test/wasm/Inputs/weak-symbol1.ll
@@ -0,0 +1,9 @@
+define weak i32 @weakFn() #0 {
+entry:
+ ret i32 1
+}
+
+define i32 @exportWeak1() {
+entry:
+ ret i32 ptrtoint (i32 ()* @weakFn to i32)
+}
diff --git a/test/wasm/Inputs/weak-symbol2.ll b/test/wasm/Inputs/weak-symbol2.ll
new file mode 100644
index 000000000000..f43ea96673b9
--- /dev/null
+++ b/test/wasm/Inputs/weak-symbol2.ll
@@ -0,0 +1,9 @@
+define weak i32 @weakFn() #0 {
+entry:
+ ret i32 2
+}
+
+define i32 @exportWeak2() {
+entry:
+ ret i32 ptrtoint (i32 ()* @weakFn to i32)
+}
diff --git a/test/wasm/archive.ll b/test/wasm/archive.ll
new file mode 100644
index 000000000000..18f35330d1e9
--- /dev/null
+++ b/test/wasm/archive.ll
@@ -0,0 +1,31 @@
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %s -o %t.o
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %S/Inputs/archive1.ll -o %t.a1.o
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %S/Inputs/archive2.ll -o %t.a2.o
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %S/Inputs/hello.ll -o %t.a3.o
+; RUN: llvm-ar rcs %t.a %t.a1.o %t.a2.o %t.a3.o
+; RUN: lld -flavor wasm %t.a %t.o -o %t.wasm
+; RUN: llvm-nm -a %t.wasm | FileCheck %s
+
+declare i32 @foo() local_unnamed_addr #1
+
+define i32 @_start() local_unnamed_addr #0 {
+entry:
+ %call = tail call i32 @foo() #2
+ ret i32 %call
+}
+
+; Verify that multually dependant object files in an archive is handled
+; correctly.
+
+; CHECK: 00000002 T _start
+; CHECK-NEXT: 00000002 T _start
+; CHECK-NEXT: 00000000 T bar
+; CHECK-NEXT: 00000000 T bar
+; CHECK-NEXT: 00000001 T foo
+; CHECK-NEXT: 00000001 T foo
+
+; Verify that symbols from unused objects don't appear in the symbol table
+; CHECK-NOT: hello
+
+; Specifying the same archive twice is allowed.
+; RUN: lld -flavor wasm %t.a %t.a %t.o -o %t.wasm
diff --git a/test/wasm/call-indirect.ll b/test/wasm/call-indirect.ll
new file mode 100644
index 000000000000..14845eb50f83
--- /dev/null
+++ b/test/wasm/call-indirect.ll
@@ -0,0 +1,112 @@
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %p/Inputs/call-indirect.ll -o %t2.o
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %s -o %t.o
+; RUN: lld -flavor wasm -o %t.wasm %t2.o %t.o
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+; bitcode generated from the following C code:
+; int foo(void) { return 1; }
+; int (*indirect_func)(void) = &foo;
+; void _start(void) { indirect_func(); }
+
+@indirect_func = local_unnamed_addr global i32 ()* @foo, align 4
+
+; Function Attrs: norecurse nounwind readnone
+define i32 @foo() #0 {
+entry:
+ ret i32 2
+}
+
+; Function Attrs: nounwind
+define i32 @_start() local_unnamed_addr #1 {
+entry:
+ %0 = load i32 ()*, i32 ()** @indirect_func, align 4
+ %call = tail call i32 %0() #2
+ ret i32 0
+}
+
+; CHECK: !WASM
+; CHECK-NEXT: FileHeader:
+; CHECK-NEXT: Version: 0x00000001
+; CHECK-NEXT: Sections:
+; CHECK-NEXT: - Type: TYPE
+; CHECK-NEXT: Signatures:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: ReturnType: NORESULT
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Type: FUNCTION
+; CHECK-NEXT: FunctionTypes: [ 0, 1, 0, 0 ]
+; CHECK-NEXT: - Type: TABLE
+; CHECK-NEXT: Tables:
+; CHECK-NEXT: - ElemType: ANYFUNC
+; CHECK-NEXT: Limits:
+; CHECK-NEXT: Flags: [ HAS_MAX ]
+; CHECK-NEXT: Initial: 0x00000003
+; CHECK-NEXT: Maximum: 0x00000003
+; CHECK-NEXT: - Type: MEMORY
+; CHECK-NEXT: Memories:
+; CHECK-NEXT: - Initial: 0x00000002
+; CHECK-NEXT: - Type: GLOBAL
+; CHECK-NEXT: Globals:
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: true
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 66576
+; CHECK-NEXT: - Type: EXPORT
+; CHECK-NEXT: Exports:
+; CHECK-NEXT: - Name: memory
+; CHECK-NEXT: Kind: MEMORY
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: _start
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 3
+; CHECK-NEXT: - Name: foo
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 2
+; CHECK-NEXT: - Name: bar
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: call_bar_indirect
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: - Type: ELEM
+; CHECK-NEXT: Segments:
+; CHECK-NEXT: - Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1
+; CHECK-NEXT: Functions: [ 0, 2 ]
+; CHECK-NEXT: - Type: CODE
+; CHECK-NEXT: Functions:
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 41010B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 410028028088808000118080808000001A0B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 41020B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 410028028888808000118080808000001A41000B
+; CHECK-NEXT: - Type: DATA
+; CHECK-NEXT: Segments:
+; CHECK-NEXT: - SectionOffset: 7
+; CHECK-NEXT: MemoryIndex: 0
+; CHECK-NEXT: Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1024
+; CHECK-NEXT: Content: '010000000200000002000000'
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: linking
+; CHECK-NEXT: DataSize: 12
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: name
+; CHECK-NEXT: FunctionNames:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Name: bar
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: call_bar_indirect
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Name: foo
+; CHECK-NEXT: - Index: 3
+; CHECK-NEXT: Name: _start
diff --git a/test/wasm/conflict.test b/test/wasm/conflict.test
new file mode 100644
index 000000000000..a0a2eb4e3b3a
--- /dev/null
+++ b/test/wasm/conflict.test
@@ -0,0 +1,6 @@
+# RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %p/Inputs/ret32.ll -o %t.ret32.o
+# RUN: not lld -flavor wasm -o %t.wasm %t.ret32.o %t.ret32.o 2>&1 | FileCheck %s
+
+# CHECK: duplicate symbol: ret32
+# CHECK-NEXT: >>> defined in {{.*}}conflict.test.tmp.ret32.o
+# CHECK-NEXT: >>> defined in {{.*}}conflict.test.tmp.ret32.o
diff --git a/test/wasm/data-layout.ll b/test/wasm/data-layout.ll
new file mode 100644
index 000000000000..0b2c61da5547
--- /dev/null
+++ b/test/wasm/data-layout.ll
@@ -0,0 +1,61 @@
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %p/Inputs/hello.ll -o %t.hello.o
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %s -o %t.o
+; RUN: lld -flavor wasm --emit-relocs --allow-undefined --no-entry -o %t.wasm %t.o %t.hello.o
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+@foo = hidden global i32 1, align 4
+@aligned_bar = hidden global i32 3, align 16
+
+@hello_str = external global i8*
+@external_ref = global i8** @hello_str, align 8
+
+; CHECK: - Type: GLOBAL
+; CHECK-NEXT: Globals:
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: true
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 66608
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: false
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1024
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: false
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1040
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: false
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1048
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: false
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1052
+
+; CHECK: - Type: DATA
+; CHECK-NEXT: Relocations:
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_I32
+; CHECK-NEXT: Index: 4
+; CHECK-NEXT: Offset: 0x0000001F
+; CHECK-NEXT: Segments:
+; CHECK-NEXT: - SectionOffset: 7
+; CHECK-NEXT: MemoryIndex: 0
+; CHECK-NEXT: Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1024
+; CHECK-NEXT: Content: 0100000000000000000000000000000003000000000000001C040000
+; CHECK-NEXT: - SectionOffset: 41
+; CHECK-NEXT: MemoryIndex: 0
+; CHECK-NEXT: Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1052
+; CHECK-NEXT: Content: 68656C6C6F0A00
+
+; CHECK: - Type: CUSTOM
+; CHECK-NEXT: Name: linking
+; CHECK-NEXT: DataSize: 35
diff --git a/test/wasm/entry.ll b/test/wasm/entry.ll
new file mode 100644
index 000000000000..e51c7055b80e
--- /dev/null
+++ b/test/wasm/entry.ll
@@ -0,0 +1,19 @@
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %s -o %t.o
+; RUN: lld -flavor wasm -e entry -o %t.wasm %t.o
+; RUN: obj2yaml %t.wasm | FileCheck %s
+; RUN: lld -flavor wasm --entry=entry -o %t.wasm %t.o
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+define void @entry() local_unnamed_addr #0 {
+entry:
+ ret void
+}
+
+; CHECK: - Type: EXPORT
+; CHECK: Exports:
+; CHECK: - Name: memory
+; CHECK: Kind: MEMORY
+; CHECK: Index: 0
+; CHECK: - Name: entry
+; CHECK: Kind: FUNCTION
+; CHECK: Index: 0
diff --git a/test/wasm/function-imports-first.ll b/test/wasm/function-imports-first.ll
new file mode 100644
index 000000000000..085345ad7ffc
--- /dev/null
+++ b/test/wasm/function-imports-first.ll
@@ -0,0 +1,42 @@
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %p/Inputs/ret32.ll -o %t.ret32.o
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %s -o %t.o
+; RUN: lld -flavor wasm -o %t.wasm %t.o %t.ret32.o
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+; Function Attrs: nounwind
+define hidden void @_start() local_unnamed_addr #0 {
+entry:
+ %call = tail call i32 @ret32(float 0.000000e+00) #2
+ ret void
+}
+
+declare i32 @ret32(float) local_unnamed_addr #1
+
+; CHECK: - Type: TYPE
+; CHECK: Signatures:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: ReturnType: NORESULT
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - F32
+; CHECK: - Type: FUNCTION
+; CHECK-NEXT: FunctionTypes: [ 0, 1 ]
+; CHECK: - Type: CODE
+; CHECK-NEXT: Functions:
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 43000000001081808080001A0B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 41000B
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: linking
+; CHECK-NEXT: DataSize: 0
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: name
+; CHECK-NEXT: FunctionNames:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Name: _start
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: ret32
+; CHECK-NEXT: ...
diff --git a/test/wasm/function-imports.ll b/test/wasm/function-imports.ll
new file mode 100644
index 000000000000..e0988ff95f96
--- /dev/null
+++ b/test/wasm/function-imports.ll
@@ -0,0 +1,37 @@
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %p/Inputs/ret32.ll -o %t.ret32.o
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %s -o %t.o
+; RUN: lld -flavor wasm -o %t.wasm %t.ret32.o %t.o
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+; Function Attrs: nounwind
+define hidden void @_start() local_unnamed_addr #0 {
+entry:
+ %call = tail call i32 @ret32(float 0.000000e+00) #2
+ ret void
+}
+
+declare i32 @ret32(float) local_unnamed_addr #1
+
+; CHECK: Sections:
+; CHECK: - Type: TYPE
+; CHECK-NEXT: Signatures:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - F32
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: ReturnType: NORESULT
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Type: FUNCTION
+; CHECK-NEXT: FunctionTypes: [ 0, 1 ]
+; CHECK: - Type: CODE
+; CHECK-NEXT: Functions:
+; CHECK: - Locals:
+; CHECK: - Locals:
+; CHECK: Name: name
+; CHECK-NEXT: FunctionNames:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Name: ret32
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: _start
+; CHECK-NEXT: ...
diff --git a/test/wasm/function-index.test b/test/wasm/function-index.test
new file mode 100644
index 000000000000..03bd879b5176
--- /dev/null
+++ b/test/wasm/function-index.test
@@ -0,0 +1,18 @@
+# RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %p/Inputs/ret32.ll -o %t.ret32.o
+# RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %p/Inputs/ret64.ll -o %t.ret64.o
+# RUN: lld -flavor wasm -r -o %t.wasm %t.ret32.o %t.ret64.o
+# RUN: obj2yaml %t.wasm | FileCheck %s
+
+CHECK: Sections:
+CHECK: - Type: TYPE
+CHECK: Signatures:
+CHECK: - Index: 0
+CHECK: ReturnType: I32
+CHECK: ParamTypes:
+CHECK: - F32
+CHECK: - Index: 1
+CHECK: ReturnType: I64
+CHECK: ParamTypes:
+CHECK: - F64
+CHECK: - Type: FUNCTION
+CHECK: FunctionTypes: [ 0, 1 ]
diff --git a/test/wasm/import-memory.test b/test/wasm/import-memory.test
new file mode 100644
index 000000000000..af0ff910449c
--- /dev/null
+++ b/test/wasm/import-memory.test
@@ -0,0 +1,13 @@
+# RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %p/Inputs/ret32.ll -o %t.ret32.o
+# RUN: lld -flavor wasm -entry ret32 --import-memory -o %t.wasm %t.ret32.o
+# RUN: obj2yaml %t.wasm | FileCheck %s
+
+# Verify the --import-memory flag creates a memory import
+
+# CHECK: - Type: IMPORT
+# CHECK-NEXT: Imports:
+# CHECK-NEXT: - Module: env
+# CHECK-NEXT: Field: memory
+# CHECK-NEXT: Kind: MEMORY
+# CHECK-NEXT: Memory:
+# CHECK-NEXT: Initial: 0x00000002
diff --git a/test/wasm/invalid-stack-size.test b/test/wasm/invalid-stack-size.test
new file mode 100644
index 000000000000..6597c548499a
--- /dev/null
+++ b/test/wasm/invalid-stack-size.test
@@ -0,0 +1,9 @@
+; RUN: llc -mtriple wasm32-unknown-unknown-wasm -filetype=obj %s -o %t.o
+; RUN: not lld -flavor wasm -o %t.wasm -z stack-size=1 %t.o 2>&1 | FileCheck %s
+
+define i32 @_start() local_unnamed_addr #1 {
+entry:
+ ret i32 0
+}
+
+; CHECK: error: stack size must be 16-byte aligned
diff --git a/test/wasm/lit.local.cfg b/test/wasm/lit.local.cfg
new file mode 100644
index 000000000000..bc76e57c8e87
--- /dev/null
+++ b/test/wasm/lit.local.cfg
@@ -0,0 +1,4 @@
+if 'wasm' not in config.available_features:
+ config.unsupported = True
+
+config.suffixes = ['.test', '.yaml', '.ll']
diff --git a/test/wasm/load-undefined.ll b/test/wasm/load-undefined.ll
new file mode 100644
index 000000000000..f979c9acb517
--- /dev/null
+++ b/test/wasm/load-undefined.ll
@@ -0,0 +1,38 @@
+; Verify that the -u / --undefined option is able to pull in symbols from
+; an archive, and doesn't error when uses to pull in a symbol already loaded.
+;
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-unknown-wasm %S/Inputs/ret64.ll -o %t.o
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-unknown-wasm %S/Inputs/ret32.ll -o %t2.o
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-unknown-wasm %s -o %t3.o
+; RUN: llvm-ar rcs %t2.a %t2.o
+; RUN: lld -flavor wasm %t3.o %t2.a %t.o -o %t.wasm -u ret32 --undefined ret64
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+define i32 @_start() local_unnamed_addr {
+entry:
+ ret i32 1
+}
+
+; CHECK: - Type: EXPORT
+; CHECK-NEXT: Exports:
+; CHECK-NEXT: - Name: memory
+; CHECK-NEXT: Kind: MEMORY
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: _start
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: ret32
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: - Name: ret64
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 2
+; CHECK-NEXT: - Type:
+
+
+; Verify that referencing a symbol that doesn't exist won't work
+; RUN: not lld -flavor wasm %t3.o -o %t.wasm -u symboldoesnotexist 2>&1 | FileCheck -check-prefix=CHECK-UNDEFINED1 %s
+; CHECK-UNDEFINED1: error: undefined symbol: symboldoesnotexist
+
+; RUN: not lld -flavor wasm %t3.o -o %t.wasm --undefined symboldoesnotexist --allow-undefined 2>&1 | FileCheck -check-prefix=CHECK-UNDEFINED2 %s
+; CHECK-UNDEFINED2: function forced with --undefined not found: symboldoesnotexist
diff --git a/test/wasm/local-symbols.ll b/test/wasm/local-symbols.ll
new file mode 100644
index 000000000000..3e6722b124ec
--- /dev/null
+++ b/test/wasm/local-symbols.ll
@@ -0,0 +1,78 @@
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %s -o %t.o
+; RUN: lld -flavor wasm -o %t.wasm %t.o
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+@foo = default global i32 1, align 4
+@bar = internal default global i32 3, align 4
+
+define internal i32 @baz() local_unnamed_addr {
+entry:
+ ret i32 2
+}
+
+define i32 @_start() local_unnamed_addr {
+entry:
+ ret i32 1
+}
+
+; CHECK: --- !WASM
+; CHECK-NEXT: FileHeader:
+; CHECK-NEXT: Version: 0x00000001
+; CHECK-NEXT: Sections:
+; CHECK-NEXT: - Type: TYPE
+; CHECK-NEXT: Signatures:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Type: FUNCTION
+; CHECK-NEXT: FunctionTypes: [ 0, 0 ]
+; CHECK-NEXT: - Type: TABLE
+; CHECK-NEXT: Tables:
+; CHECK-NEXT: - ElemType: ANYFUNC
+; CHECK-NEXT: Limits:
+; CHECK-NEXT: Flags: [ HAS_MAX ]
+; CHECK-NEXT: Initial: 0x00000001
+; CHECK-NEXT: Maximum: 0x00000001
+; CHECK-NEXT: - Type: MEMORY
+; CHECK-NEXT: Memories:
+; CHECK-NEXT: - Initial: 0x00000002
+; CHECK-NEXT: - Type: GLOBAL
+; CHECK-NEXT: Globals:
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: true
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 66576
+; CHECK-NEXT: - Type: EXPORT
+; CHECK-NEXT: Exports:
+; CHECK-NEXT: - Name: memory
+; CHECK-NEXT: Kind: MEMORY
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: _start
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: - Type: CODE
+; CHECK-NEXT: Functions:
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 41020B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 41010B
+; CHECK-NEXT: - Type: DATA
+; CHECK-NEXT: Segments:
+; CHECK-NEXT: - SectionOffset: 7
+; CHECK-NEXT: MemoryIndex: 0
+; CHECK-NEXT: Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1024
+; CHECK-NEXT: Content: '0100000003000000'
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: linking
+; CHECK-NEXT: DataSize: 8
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: name
+; CHECK-NEXT: FunctionNames:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Name: baz
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: _start
+; CHECK-NEXT: ...
diff --git a/test/wasm/many-functions.ll b/test/wasm/many-functions.ll
new file mode 100644
index 000000000000..77326d739a8b
--- /dev/null
+++ b/test/wasm/many-functions.ll
@@ -0,0 +1,695 @@
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %p/Inputs/many-funcs.ll -o %t.many.o
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %s -o %t.o
+; RUN: lld -flavor wasm -r -o %t.wasm %t.many.o %t.o
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+; Test that relocations within the CODE section correctly handle
+; linking object with different header sizes. many-funcs.ll has
+; 128 function and so the final output requires a 2-byte LEB in
+; the CODE section header to store the function count.
+
+define i32 @func() {
+entry:
+ %call = tail call i32 @func()
+ ret i32 %call
+}
+
+; CHECK: - Type: CODE
+; CHECK-NEXT: Relocations:
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000008
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000014
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000020
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000002C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000038
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000044
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000050
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000005C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000068
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000074
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000080
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000008C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000098
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000000A4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000000B0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000000BC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000000C8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000000D4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000000E0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000000EC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000000F8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000104
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000110
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000011C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000128
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000134
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000140
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000014C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000158
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000164
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000170
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000017C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000188
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000194
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000001A0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000001AC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000001B8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000001C4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000001D0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000001DC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000001E8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000001F4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000200
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000020C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000218
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000224
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000230
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000023C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000248
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000254
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000260
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000026C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000278
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000284
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000290
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000029C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000002A8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000002B4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000002C0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000002CC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000002D8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000002E4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000002F0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000002FC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000308
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000314
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000320
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000032C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000338
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000344
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000350
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000035C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000368
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000374
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000380
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000038C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000398
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000003A4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000003B0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000003BC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000003C8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000003D4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000003E0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000003EC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000003F8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000404
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000410
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000041C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000428
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000434
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000440
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000044C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000458
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000464
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000470
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000047C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000488
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000494
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000004A0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000004AC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000004B8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000004C4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000004D0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000004DC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000004E8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000004F4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000500
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000050C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000518
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000524
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000530
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000053C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000548
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000554
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000560
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000056C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000578
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000584
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000590
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000059C
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000005A8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000005B4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000005C0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000005CC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000005D8
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000005E4
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x000005F0
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: Offset: 0x000005FC
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_LEB
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: Offset: 0x00000608
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_FUNCTION_INDEX_LEB
+; CHECK-NEXT: Index: 129
+; CHECK-NEXT: Offset: 0x00000611
+; CHECK-NEXT: Functions:
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280284808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280280808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4100280280808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 1081818080000B
+; CHECK-NEXT: - Type: DATA
+; CHECK-NEXT: Segments:
+; CHECK-NEXT: - SectionOffset: 6
+; CHECK-NEXT: MemoryIndex: 0
+; CHECK-NEXT: Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 0
+; CHECK-NEXT: Content: '01000000'
+; CHECK-NEXT: - SectionOffset: 15
+; CHECK-NEXT: MemoryIndex: 0
+; CHECK-NEXT: Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 4
+; CHECK-NEXT: Content: '01000000'
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: linking
+; CHECK-NEXT: DataSize: 8
+; CHECK-NEXT: SegmentInfo:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Name: .data.g0
+; CHECK-NEXT: Alignment: 4
+; CHECK-NEXT: Flags: [ ]
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: .data.foo
+; CHECK-NEXT: Alignment: 4
+; CHECK-NEXT: Flags: [ ]
diff --git a/test/wasm/relocatable.ll b/test/wasm/relocatable.ll
new file mode 100644
index 000000000000..cb86aa20cab2
--- /dev/null
+++ b/test/wasm/relocatable.ll
@@ -0,0 +1,194 @@
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %p/Inputs/hello.ll -o %t.hello.o
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %s -o %t.o
+; RUN: lld -flavor wasm -r -o %t.wasm %t.hello.o %t.o
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+; Function Attrs: nounwind
+define hidden i32 @my_func() local_unnamed_addr {
+entry:
+ %call = tail call i32 @foo_import()
+ ret i32 1
+}
+
+declare i32 @foo_import() local_unnamed_addr
+@data_import = external global i64
+
+@func_addr1 = hidden global i32()* @my_func, align 4
+@func_addr2 = hidden global i32()* @foo_import, align 4
+@data_addr1 = hidden global i64* @data_import, align 8
+
+; CHECK: --- !WASM
+; CHECK-NEXT: FileHeader:
+; CHECK-NEXT: Version: 0x00000001
+; CHECK-NEXT: Sections:
+; CHECK-NEXT: - Type: TYPE
+; CHECK-NEXT: Signatures:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: ReturnType: NORESULT
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: ReturnType: NORESULT
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - I32
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Type: IMPORT
+; CHECK-NEXT: Imports:
+; CHECK-NEXT: - Module: env
+; CHECK-NEXT: Field: puts
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: SigIndex: 1
+; CHECK-NEXT: - Module: env
+; CHECK-NEXT: Field: foo_import
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: SigIndex: 2
+; CHECK-NEXT: - Module: env
+; CHECK-NEXT: Field: data_import
+; CHECK-NEXT: Kind: GLOBAL
+; CHECK-NEXT: GlobalType: I32
+; CHECK-NEXT: GlobalMutable: false
+; CHECK-NEXT: - Type: FUNCTION
+; CHECK-NEXT: FunctionTypes: [ 0, 2 ]
+; CHECK-NEXT: - Type: TABLE
+; CHECK-NEXT: Tables:
+; CHECK-NEXT: - ElemType: ANYFUNC
+; CHECK-NEXT: Limits:
+; CHECK-NEXT: Flags: [ HAS_MAX ]
+; CHECK-NEXT: Initial: 0x00000002
+; CHECK-NEXT: Maximum: 0x00000002
+; CHECK-NEXT: - Type: MEMORY
+; CHECK-NEXT: Memories:
+; CHECK-NEXT: - Initial: 0x00000001
+; CHECK-NEXT: - Type: GLOBAL
+; CHECK-NEXT: Globals:
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: false
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 0
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: false
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 8
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: false
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 12
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: false
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 16
+; CHECK-NEXT: - Type: EXPORT
+; CHECK-NEXT: Exports:
+; CHECK-NEXT: - Name: hello
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 2
+; CHECK-NEXT: - Name: my_func
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 3
+; CHECK-NEXT: - Name: hello_str
+; CHECK-NEXT: Kind: GLOBAL
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: - Name: func_addr1
+; CHECK-NEXT: Kind: GLOBAL
+; CHECK-NEXT: Index: 2
+; CHECK-NEXT: - Name: func_addr2
+; CHECK-NEXT: Kind: GLOBAL
+; CHECK-NEXT: Index: 3
+; CHECK-NEXT: - Name: data_addr1
+; CHECK-NEXT: Kind: GLOBAL
+; CHECK-NEXT: Index: 4
+; CHECK-NEXT: - Type: ELEM
+; CHECK-NEXT: Segments:
+; CHECK-NEXT: - Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 0
+; CHECK-NEXT: Functions: [ 3, 1 ]
+; CHECK-NEXT: - Type: CODE
+; CHECK-NEXT: Relocations:
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_SLEB
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: Offset: 0x00000004
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_FUNCTION_INDEX_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x0000000A
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_FUNCTION_INDEX_LEB
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: Offset: 0x00000013
+; CHECK-NEXT: Functions:
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4180808080001080808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 1081808080001A41010B
+; CHECK-NEXT: - Type: DATA
+; CHECK-NEXT: Relocations:
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_TABLE_INDEX_I32
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000012
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_TABLE_INDEX_I32
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: Offset: 0x0000001B
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_I32
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000024
+; CHECK-NEXT: Segments:
+; CHECK-NEXT: - SectionOffset: 6
+; CHECK-NEXT: MemoryIndex: 0
+; CHECK-NEXT: Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 0
+; CHECK-NEXT: Content: 68656C6C6F0A00
+; CHECK-NEXT: - SectionOffset: 18
+; CHECK-NEXT: MemoryIndex: 0
+; CHECK-NEXT: Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 8
+; CHECK-NEXT: Content: '00000000'
+; CHECK-NEXT: - SectionOffset: 27
+; CHECK-NEXT: MemoryIndex: 0
+; CHECK-NEXT: Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 12
+; CHECK-NEXT: Content: '01000000'
+; CHECK-NEXT: - SectionOffset: 36
+; CHECK-NEXT: MemoryIndex: 0
+; CHECK-NEXT: Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 16
+; CHECK-NEXT: Content: FFFFFFFF
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: linking
+; CHECK-NEXT: DataSize: 20
+; CHECK-NEXT: SegmentInfo:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Name: .rodata.hello_str
+; CHECK-NEXT: Alignment: 1
+; CHECK-NEXT: Flags: [ ]
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: .data.func_addr1
+; CHECK-NEXT: Alignment: 4
+; CHECK-NEXT: Flags: [ ]
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Name: .data.func_addr2
+; CHECK-NEXT: Alignment: 4
+; CHECK-NEXT: Flags: [ ]
+; CHECK-NEXT: - Index: 3
+; CHECK-NEXT: Name: .data.data_addr1
+; CHECK-NEXT: Alignment: 8
+; CHECK-NEXT: Flags: [ ]
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: name
+; CHECK-NEXT: FunctionNames:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Name: puts
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: foo_import
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Name: hello
+; CHECK-NEXT: - Index: 3
+; CHECK-NEXT: Name: my_func
+; CHECK-NEXT: ...
diff --git a/test/wasm/signature-mismatch.ll b/test/wasm/signature-mismatch.ll
new file mode 100644
index 000000000000..88f70fea5123
--- /dev/null
+++ b/test/wasm/signature-mismatch.ll
@@ -0,0 +1,16 @@
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %p/Inputs/ret32.ll -o %t.ret32.o
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %s -o %t.main.o
+; RUN: not lld -flavor wasm --check-signatures -o %t.wasm %t.main.o %t.ret32.o 2>&1 | FileCheck %s
+
+; Function Attrs: nounwind
+define hidden void @_start() local_unnamed_addr #0 {
+entry:
+ %call = tail call i32 @ret32(i32 1, i64 2, i32 3) #2
+ ret void
+}
+
+declare i32 @ret32(i32, i64, i32) local_unnamed_addr #1
+
+; CHECK: error: function signature mismatch: ret32
+; CHECK-NEXT: >>> defined as (I32, I64, I32) -> I32 in {{.*}}.main.o
+; CHECK-NEXT: >>> defined as (F32) -> I32 in {{.*}}.ret32.o
diff --git a/test/wasm/stack-pointer.ll b/test/wasm/stack-pointer.ll
new file mode 100644
index 000000000000..738f0dce8e81
--- /dev/null
+++ b/test/wasm/stack-pointer.ll
@@ -0,0 +1,64 @@
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %s -o %t.o
+; RUN: lld -flavor wasm --emit-relocs -o %t.wasm %t.o
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+; Function Attrs: nounwind
+define hidden i32 @_start() local_unnamed_addr {
+entry:
+ %retval = alloca i32, align 4
+ ret i32 0
+}
+
+; CHECK: --- !WASM
+; CHECK-NEXT: FileHeader:
+; CHECK-NEXT: Version: 0x00000001
+; CHECK-NEXT: Sections:
+; CHECK-NEXT: - Type: TYPE
+; CHECK-NEXT: Signatures:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Type: FUNCTION
+; CHECK-NEXT: FunctionTypes: [ 0 ]
+; CHECK-NEXT: - Type: TABLE
+; CHECK-NEXT: Tables:
+; CHECK-NEXT: - ElemType: ANYFUNC
+; CHECK-NEXT: Limits:
+; CHECK-NEXT: Flags: [ HAS_MAX ]
+; CHECK-NEXT: Initial: 0x00000001
+; CHECK-NEXT: Maximum: 0x00000001
+; CHECK-NEXT: - Type: MEMORY
+; CHECK-NEXT: Memories:
+; CHECK-NEXT: - Initial: 0x00000002
+; CHECK-NEXT: - Type: GLOBAL
+; CHECK-NEXT: Globals:
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: true
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 66560
+; CHECK-NEXT: - Type: EXPORT
+; CHECK-NEXT: Exports:
+; CHECK-NEXT: - Name: memory
+; CHECK-NEXT: Kind: MEMORY
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: _start
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Type: CODE
+; CHECK-NEXT: Relocations:
+; CHECK-NEXT: - Type: R_WEBASSEMBLY_GLOBAL_INDEX_LEB
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Offset: 0x00000004
+; CHECK-NEXT: Functions:
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 23808080800041106B1A41000B
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: linking
+; CHECK-NEXT: DataSize: 0
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: name
+; CHECK-NEXT: FunctionNames:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Name: _start
+; CHECK-NEXT: ...
diff --git a/test/wasm/strip-debug.test b/test/wasm/strip-debug.test
new file mode 100644
index 000000000000..57667a9f4406
--- /dev/null
+++ b/test/wasm/strip-debug.test
@@ -0,0 +1,6 @@
+RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %p/Inputs/ret32.ll -o %t.ret32.o
+RUN: lld -flavor wasm --strip-debug --entry=ret32 -o %t.wasm %t.ret32.o
+RUN: obj2yaml %t.wasm | FileCheck %s
+
+# Check that there is no name section
+CHECK-NOT: Name: name
diff --git a/test/wasm/symbol-type-mismatch.ll b/test/wasm/symbol-type-mismatch.ll
new file mode 100644
index 000000000000..706a361dd767
--- /dev/null
+++ b/test/wasm/symbol-type-mismatch.ll
@@ -0,0 +1,9 @@
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %s -o %t.o
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %p/Inputs/ret32.ll -o %t.ret32.o
+; RUN: not lld -flavor wasm -o %t.wasm %t.o %t.ret32.o 2>&1 | FileCheck %s
+
+@ret32 = extern_weak global i32, align 4
+
+; CHECK: error: symbol type mismatch: ret32
+; CHECK: >>> defined as Global in {{.*}}symbol-type-mismatch.ll.tmp.o
+; CHECK: >>> defined as Function in {{.*}}.ret32.o
diff --git a/test/wasm/undefined-entry.test b/test/wasm/undefined-entry.test
new file mode 100644
index 000000000000..00a0761f4b6c
--- /dev/null
+++ b/test/wasm/undefined-entry.test
@@ -0,0 +1,4 @@
+RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %p/Inputs/ret32.ll -o %t.ret32.o
+RUN: not lld -flavor wasm -o %t.wasm %t.ret32.o 2>&1 | FileCheck %s
+
+CHECK: error: undefined symbol: _start
diff --git a/test/wasm/undefined.ll b/test/wasm/undefined.ll
new file mode 100644
index 000000000000..c5f266431702
--- /dev/null
+++ b/test/wasm/undefined.ll
@@ -0,0 +1,20 @@
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %s -o %t.o
+; RUN: lld -flavor wasm --allow-undefined -o %t.wasm %t.o
+
+; Fails due to undefined 'foo'
+; RUN: not lld -flavor wasm -o %t.wasm %t.o 2>&1 | FileCheck %s
+; CHECK: error: {{.*}}.o: undefined symbol: foo
+
+; But succeeds if we pass a file containing 'foo' as --allow-undefined-file.
+; RUN: echo 'foo' > %t.txt
+; RUN: lld -flavor wasm --allow-undefined-file=%t.txt -o %t.wasm %t.o
+
+; Takes the address of the external foo() resulting in undefined external
+@bar = hidden local_unnamed_addr global i8* bitcast (i32 ()* @foo to i8*), align 4
+
+declare i32 @foo() #0
+
+define hidden void @_start() local_unnamed_addr #0 {
+entry:
+ ret void
+}
diff --git a/test/wasm/version.ll b/test/wasm/version.ll
new file mode 100644
index 000000000000..aed7b39ff3b9
--- /dev/null
+++ b/test/wasm/version.ll
@@ -0,0 +1,13 @@
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %s -o %t.o
+; RUN: lld -flavor wasm -o %t.wasm %t.o
+; RUN: llvm-readobj -file-headers %t.wasm | FileCheck %s
+
+define hidden void @_start() local_unnamed_addr #0 {
+entry:
+ ret void
+}
+
+; CHECK: Format: WASM
+; CHECK: Arch: wasm32
+; CHECK: AddressSize: 32bit
+; CHECK: Version: 0x1
diff --git a/test/wasm/visibility-hidden.ll b/test/wasm/visibility-hidden.ll
new file mode 100644
index 000000000000..9960b952492b
--- /dev/null
+++ b/test/wasm/visibility-hidden.ll
@@ -0,0 +1,46 @@
+; RUN: llc -mtriple=wasm32-unknown-unknown-wasm -filetype=obj -o %t.o %s
+; RUN: llc -mtriple=wasm32-unknown-unknown-wasm -filetype=obj %S/Inputs/hidden.ll -o %t2.o
+; RUN: llvm-ar rcs %t2.a %t2.o
+; RUN: lld -flavor wasm %t.o %t2.a -o %t.wasm
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+; Test that hidden symbols are not exported, whether pulled in from an archive
+; or directly.
+
+define hidden i32 @objectHidden() {
+entry:
+ ret i32 0
+}
+
+define i32 @objectDefault() {
+entry:
+ ret i32 0
+}
+
+declare i32 @archiveHidden()
+declare i32 @archiveDefault()
+
+define i32 @_start() {
+entry:
+ %call1 = call i32 @objectHidden()
+ %call2 = call i32 @objectDefault()
+ %call3 = call i32 @archiveHidden()
+ %call4 = call i32 @archiveDefault()
+ ret i32 0
+}
+
+; CHECK: - Type: EXPORT
+; CHECK-NEXT: Exports:
+; CHECK-NEXT: - Name: memory
+; CHECK-NEXT: Kind: MEMORY
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: _start
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 2
+; CHECK-NEXT: - Name: archiveDefault
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 4
+; CHECK-NEXT: - Name: objectDefault
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: - Type:
diff --git a/test/wasm/weak-alias-overide.ll b/test/wasm/weak-alias-overide.ll
new file mode 100644
index 000000000000..5f4021e18b0f
--- /dev/null
+++ b/test/wasm/weak-alias-overide.ll
@@ -0,0 +1,92 @@
+; RUN: llc -mtriple wasm32-unknown-unknown-wasm -filetype=obj -o %t.o %s
+; RUN: llc -mtriple=wasm32-unknown-unknown-wasm -filetype=obj %S/Inputs/weak-alias.ll -o %t2.o
+; RUN: lld -flavor wasm %t.o %t2.o -o %t.wasm
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+; Test that the strongly defined bar is used correctly despite the existence
+; of the weak alias
+
+define i32 @bar() local_unnamed_addr #1 {
+ ret i32 1
+}
+
+; Function Attrs: nounwind uwtable
+define void @_start() local_unnamed_addr #1 {
+entry:
+ %call = tail call i32 @bar() #2
+ ret void
+}
+
+; CHECK: --- !WASM
+; CHECK-NEXT: FileHeader:
+; CHECK-NEXT: Version: 0x00000001
+; CHECK-NEXT: Sections:
+; CHECK-NEXT: - Type: TYPE
+; CHECK-NEXT: Signatures:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: ReturnType: NORESULT
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Type: FUNCTION
+; CHECK-NEXT: FunctionTypes: [ 0, 1, 0, 0 ]
+; CHECK-NEXT: - Type: TABLE
+; CHECK-NEXT: Tables:
+; CHECK-NEXT: - ElemType: ANYFUNC
+; CHECK-NEXT: Limits:
+; CHECK-NEXT: Flags: [ HAS_MAX ]
+; CHECK-NEXT: Initial: 0x00000001
+; CHECK-NEXT: Maximum: 0x00000001
+; CHECK-NEXT: - Type: MEMORY
+; CHECK-NEXT: Memories:
+; CHECK-NEXT: - Initial: 0x00000002
+; CHECK-NEXT: - Type: GLOBAL
+; CHECK-NEXT: Globals:
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: true
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 66560
+; CHECK-NEXT: - Type: EXPORT
+; CHECK-NEXT: Exports:
+; CHECK-NEXT: - Name: memory
+; CHECK-NEXT: Kind: MEMORY
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: _start
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: - Name: bar
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: foo
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 2
+; CHECK-NEXT: - Name: call_bar
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 3
+; CHECK-NEXT: - Type: CODE
+; CHECK-NEXT: Functions:
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 41010B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 1080808080001A0B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 41000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 1080808080000B
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: linking
+; CHECK-NEXT: DataSize: 0
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: name
+; CHECK-NEXT: FunctionNames:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Name: bar
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: _start
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Name: foo
+; CHECK-NEXT: - Index: 3
+; CHECK-NEXT: Name: call_bar
+; CHECK-NEXT: ...
diff --git a/test/wasm/weak-alias.ll b/test/wasm/weak-alias.ll
new file mode 100644
index 000000000000..6f96d4d17643
--- /dev/null
+++ b/test/wasm/weak-alias.ll
@@ -0,0 +1,82 @@
+; RUN: llc -mtriple wasm32-unknown-unknown-wasm -filetype=obj -o %t.o %s
+; RUN: llc -mtriple=wasm32-unknown-unknown-wasm -filetype=obj %S/Inputs/weak-alias.ll -o %t2.o
+; RUN: lld -flavor wasm %t.o %t2.o -o %t.wasm
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+; Test that weak aliases (bar is a weak alias of foo) are linked correctly
+
+declare i32 @bar() local_unnamed_addr #1
+
+; Function Attrs: nounwind uwtable
+define i32 @_start() local_unnamed_addr #1 {
+entry:
+ %call = tail call i32 @bar() #2
+ ret i32 %call
+}
+
+; CHECK: --- !WASM
+; CHECK-NEXT: FileHeader:
+; CHECK-NEXT: Version: 0x00000001
+; CHECK-NEXT: Sections:
+; CHECK-NEXT: - Type: TYPE
+; CHECK-NEXT: Signatures:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Type: FUNCTION
+; CHECK-NEXT: FunctionTypes: [ 0, 0, 0 ]
+; CHECK-NEXT: - Type: TABLE
+; CHECK-NEXT: Tables:
+; CHECK-NEXT: - ElemType: ANYFUNC
+; CHECK-NEXT: Limits:
+; CHECK-NEXT: Flags: [ HAS_MAX ]
+; CHECK-NEXT: Initial: 0x00000001
+; CHECK-NEXT: Maximum: 0x00000001
+; CHECK-NEXT: - Type: MEMORY
+; CHECK-NEXT: Memories:
+; CHECK-NEXT: - Initial: 0x00000002
+; CHECK-NEXT: - Type: GLOBAL
+; CHECK-NEXT: Globals:
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: true
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 66560
+; CHECK-NEXT: - Type: EXPORT
+; CHECK-NEXT: Exports:
+; CHECK-NEXT: - Name: memory
+; CHECK-NEXT: Kind: MEMORY
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: _start
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: bar
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: - Name: foo
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: - Name: call_bar
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 2
+; CHECK-NEXT: - Type: CODE
+; CHECK-NEXT: Functions:
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 1081808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 41000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 1081808080000B
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: linking
+; CHECK-NEXT: DataSize: 0
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: name
+; CHECK-NEXT: FunctionNames:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Name: _start
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: foo
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Name: call_bar
+; CHECK-NEXT: ...
diff --git a/test/wasm/weak-external.ll b/test/wasm/weak-external.ll
new file mode 100644
index 000000000000..891f7666ec76
--- /dev/null
+++ b/test/wasm/weak-external.ll
@@ -0,0 +1,86 @@
+; RUN: llc -mtriple wasm32-unknown-unknown-wasm -filetype=obj -o %t.o %s
+; RUN: lld -flavor wasm -strip-debug %t.o -o %t.wasm
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+; Test that undefined weak externals (global_var) and (foo) don't cause
+; link failures and resolve to zero.
+
+@global_var = extern_weak global i32, align 4
+
+declare extern_weak i32 @foo()
+
+define i8* @get_address_of_foo() #0 {
+entry:
+ ret i8* bitcast (i32 ()* @foo to i8*)
+}
+
+define i32* @get_address_of_global_var() #0 {
+ ret i32* @global_var
+}
+
+define i32 @_start() #0 {
+entry:
+ %0 = load i32, i32* @global_var, align 4
+ ret i32 %0
+}
+
+; CHECK: --- !WASM
+; CHECK-NEXT: FileHeader:
+; CHECK-NEXT: Version: 0x00000001
+; CHECK-NEXT: Sections:
+; CHECK-NEXT: - Type: TYPE
+; CHECK-NEXT: Signatures:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Type: FUNCTION
+; CHECK-NEXT: FunctionTypes: [ 0, 0, 0 ]
+; CHECK-NEXT: - Type: TABLE
+; CHECK-NEXT: Tables:
+; CHECK-NEXT: - ElemType: ANYFUNC
+; CHECK-NEXT: Limits:
+; CHECK-NEXT: Flags: [ HAS_MAX ]
+; CHECK-NEXT: Initial: 0x00000002
+; CHECK-NEXT: Maximum: 0x00000002
+; CHECK-NEXT: - Type: MEMORY
+; CHECK-NEXT: Memories:
+; CHECK-NEXT: - Initial: 0x00000002
+; CHECK-NEXT: - Type: GLOBAL
+; CHECK-NEXT: Globals:
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: true
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 66560
+; CHECK-NEXT: - Type: EXPORT
+; CHECK-NEXT: Exports:
+; CHECK-NEXT: - Name: memory
+; CHECK-NEXT: Kind: MEMORY
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: _start
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 2
+; CHECK-NEXT: - Name: get_address_of_foo
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: get_address_of_global_var
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: - Type: ELEM
+; CHECK-NEXT: Segments:
+; CHECK-NEXT: - Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1
+; CHECK-NEXT: Functions: [ 0 ]
+; CHECK-NEXT: - Type: CODE
+; CHECK-NEXT: Functions:
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4181808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 41FFFFFFFF7F0B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 41002802FFFFFFFF0F0B
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: linking
+; CHECK-NEXT: DataSize: 0
+; CHECK-NEXT: ...
diff --git a/test/wasm/weak-symbols.ll b/test/wasm/weak-symbols.ll
new file mode 100644
index 000000000000..c282d005623f
--- /dev/null
+++ b/test/wasm/weak-symbols.ll
@@ -0,0 +1,93 @@
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %p/Inputs/weak-symbol1.ll -o %t1.o
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %p/Inputs/weak-symbol2.ll -o %t2.o
+; RUN: llc -filetype=obj -mtriple=wasm32-unknown-uknown-wasm %s -o %t.o
+; RUN: lld -flavor wasm -o %t.wasm %t.o %t1.o %t2.o
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+declare i32 @weakFn() local_unnamed_addr
+
+define void @_start() local_unnamed_addr {
+entry:
+ %call = call i32 @weakFn()
+ ret void
+}
+
+; CHECK: --- !WASM
+; CHECK-NEXT: FileHeader:
+; CHECK-NEXT: Version: 0x00000001
+; CHECK-NEXT: Sections:
+; CHECK-NEXT: - Type: TYPE
+; CHECK-NEXT: Signatures:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: ReturnType: NORESULT
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Type: FUNCTION
+; CHECK-NEXT: FunctionTypes: [ 0, 1, 1, 1, 1 ]
+; CHECK-NEXT: - Type: TABLE
+; CHECK-NEXT: Tables:
+; CHECK-NEXT: - ElemType: ANYFUNC
+; CHECK-NEXT: Limits:
+; CHECK-NEXT: Flags: [ HAS_MAX ]
+; CHECK-NEXT: Initial: 0x00000002
+; CHECK-NEXT: Maximum: 0x00000002
+; CHECK-NEXT: - Type: MEMORY
+; CHECK-NEXT: Memories:
+; CHECK-NEXT: - Initial: 0x00000002
+; CHECK-NEXT: - Type: GLOBAL
+; CHECK-NEXT: Globals:
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Mutable: true
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 66560
+; CHECK-NEXT: - Type: EXPORT
+; CHECK-NEXT: Exports:
+; CHECK-NEXT: - Name: memory
+; CHECK-NEXT: Kind: MEMORY
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: _start
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: weakFn
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: - Name: exportWeak1
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 2
+; CHECK-NEXT: - Name: exportWeak2
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 4
+; CHECK-NEXT: - Type: ELEM
+; CHECK-NEXT: Segments:
+; CHECK-NEXT: - Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1
+; CHECK-NEXT: Functions: [ 1 ]
+; CHECK-NEXT: - Type: CODE
+; CHECK-NEXT: Functions:
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 1081808080001A0B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 41010B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4181808080000B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 41020B
+; CHECK-NEXT: - Locals:
+; CHECK-NEXT: Body: 4181808080000B
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: linking
+; CHECK-NEXT: DataSize: 0
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: name
+; CHECK-NEXT: FunctionNames:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Name: _start
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Name: exportWeak1
+; CHECK-NEXT: - Index: 4
+; CHECK-NEXT: Name: exportWeak2
+; CHECK-NEXT: ...
diff --git a/tools/lld/CMakeLists.txt b/tools/lld/CMakeLists.txt
index 2df10697ff66..d8829493fc22 100644
--- a/tools/lld/CMakeLists.txt
+++ b/tools/lld/CMakeLists.txt
@@ -7,16 +7,19 @@ add_lld_tool(lld
)
target_link_libraries(lld
- lldDriver
+ PRIVATE
lldCOFF
+ lldDriver
lldELF
+ lldMinGW
+ lldWasm
)
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 wasm-ld)
endif()
foreach(link ${LLD_SYMLINKS_TO_CREATE})
diff --git a/tools/lld/lld.cpp b/tools/lld/lld.cpp
index 09f8079010d4..64e9aea25e39 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"
@@ -34,6 +34,7 @@ enum Flavor {
Gnu, // -flavor gnu
WinLink, // -flavor link
Darwin, // -flavor darwin
+ Wasm, // -flavor wasm
};
LLVM_ATTRIBUTE_NORETURN static void die(const Twine &S) {
@@ -44,11 +45,22 @@ LLVM_ATTRIBUTE_NORETURN static void die(const Twine &S) {
static Flavor getFlavor(StringRef S) {
return StringSwitch<Flavor>(S)
.CasesLower("ld", "ld.lld", "gnu", Gnu)
+ .CasesLower("wasm", "ld-wasm", Wasm)
.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,11 +113,15 @@ 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);
+ return !coff::link(Args, true);
case Darwin:
return !mach_o::link(Args);
+ case Wasm:
+ return !wasm::link(Args, true);
default:
die("lld is a generic driver.\n"
"Invoke ld.lld (Unix), ld (macOS) or lld-link (Windows) instead.");
diff --git a/unittests/DriverTests/CMakeLists.txt b/unittests/DriverTests/CMakeLists.txt
index 91d22ca19d72..7137d69e1b60 100644
--- a/unittests/DriverTests/CMakeLists.txt
+++ b/unittests/DriverTests/CMakeLists.txt
@@ -3,6 +3,7 @@ add_lld_unittest(DriverTests
)
target_link_libraries(DriverTests
+ PRIVATE
lldDriver
lldMachO
)
diff --git a/unittests/DriverTests/DarwinLdDriverTest.cpp b/unittests/DriverTests/DarwinLdDriverTest.cpp
index d81f1543f453..696be69bc269 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"
diff --git a/unittests/MachOTests/CMakeLists.txt b/unittests/MachOTests/CMakeLists.txt
index 1a683484b5f9..b2b22fb089c3 100644
--- a/unittests/MachOTests/CMakeLists.txt
+++ b/unittests/MachOTests/CMakeLists.txt
@@ -7,6 +7,7 @@ add_lld_unittest(lldMachOTests
)
target_link_libraries(lldMachOTests
+ PRIVATE
lldDriver
lldMachO
lldYAML
diff --git a/utils/benchmark.py b/utils/benchmark.py
new file mode 100755
index 000000000000..f1deb0647a15
--- /dev/null
+++ b/utils/benchmark.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+# ==------------------------------------------------------------------------==#
+
+import os
+import glob
+import re
+import subprocess
+import json
+import datetime
+import argparse
+import urllib
+import urllib2
+
+parser = argparse.ArgumentParser()
+parser.add_argument('benchmark_directory')
+parser.add_argument('--runs', type=int, default=10)
+parser.add_argument('--wrapper', default='')
+parser.add_argument('--machine', required=True)
+parser.add_argument('--revision', required=True)
+parser.add_argument('--threads', action='store_true')
+parser.add_argument('--url', help='The lnt server url to send the results to',
+ default='http://localhost:8000/db_default/v4/link/submitRun')
+args = parser.parse_args()
+
+class Bench:
+ def __init__(self, directory, variant):
+ self.directory = directory
+ self.variant = variant
+ def __str__(self):
+ if not self.variant:
+ return self.directory
+ return '%s-%s' % (self.directory, self.variant)
+
+def getBenchmarks():
+ ret = []
+ for i in glob.glob('*/response*.txt'):
+ m = re.match('response-(.*)\.txt', os.path.basename(i))
+ variant = m.groups()[0] if m else None
+ ret.append(Bench(os.path.dirname(i), variant))
+ return ret
+
+def parsePerfNum(num):
+ num = num.replace(b',',b'')
+ try:
+ return int(num)
+ except ValueError:
+ return float(num)
+
+def parsePerfLine(line):
+ ret = {}
+ line = line.split(b'#')[0].strip()
+ if len(line) != 0:
+ p = line.split()
+ ret[p[1].strip().decode('ascii')] = parsePerfNum(p[0])
+ return ret
+
+def parsePerf(output):
+ ret = {}
+ lines = [x.strip() for x in output.split(b'\n')]
+
+ seconds = [x for x in lines if b'seconds time elapsed' in x][0]
+ seconds = seconds.strip().split()[0].strip()
+ ret['seconds-elapsed'] = parsePerfNum(seconds)
+
+ measurement_lines = [x for x in lines if b'#' in x]
+ for l in measurement_lines:
+ ret.update(parsePerfLine(l))
+ return ret
+
+def run(cmd):
+ try:
+ return subprocess.check_output(cmd, stderr=subprocess.STDOUT)
+ except subprocess.CalledProcessError as e:
+ print(e.output)
+ raise e
+
+def combinePerfRun(acc, d):
+ for k,v in d.items():
+ a = acc.get(k, [])
+ a.append(v)
+ acc[k] = a
+
+def perf(cmd):
+ # Discard the first run to warm up any system cache.
+ run(cmd)
+
+ ret = {}
+ wrapper_args = [x for x in args.wrapper.split(',') if x]
+ for i in range(args.runs):
+ os.unlink('t')
+ out = run(wrapper_args + ['perf', 'stat'] + cmd)
+ r = parsePerf(out)
+ combinePerfRun(ret, r)
+ os.unlink('t')
+ return ret
+
+def runBench(bench):
+ thread_arg = [] if args.threads else ['--no-threads']
+ os.chdir(bench.directory)
+ suffix = '-%s' % bench.variant if bench.variant else ''
+ response = 'response' + suffix + '.txt'
+ ret = perf(['../ld.lld', '@' + response, '-o', 't'] + thread_arg)
+ ret['name'] = str(bench)
+ os.chdir('..')
+ return ret
+
+def buildLntJson(benchmarks):
+ start = datetime.datetime.utcnow().isoformat()
+ tests = [runBench(b) for b in benchmarks]
+ end = datetime.datetime.utcnow().isoformat()
+ ret = {
+ 'format_version' : 2,
+ 'machine' : { 'name' : args.machine },
+ 'run' : {
+ 'end_time' : start,
+ 'start_time' : end,
+ 'llvm_project_revision': args.revision
+ },
+ 'tests' : tests
+ }
+ return json.dumps(ret, sort_keys=True, indent=4)
+
+def submitToServer(data):
+ data2 = urllib.urlencode({ 'input_data' : data }).encode('ascii')
+ urllib2.urlopen(urllib2.Request(args.url, data2))
+
+os.chdir(args.benchmark_directory)
+data = buildLntJson(getBenchmarks())
+submitToServer(data)
diff --git a/utils/link.yaml b/utils/link.yaml
new file mode 100644
index 000000000000..498e945587d8
--- /dev/null
+++ b/utils/link.yaml
@@ -0,0 +1,39 @@
+format_version: '2'
+name: link
+run_fields:
+ - name: llvm_project_revision
+ order: true
+machine_fields:
+ - name: hardware
+ - name: os
+metrics:
+ - name: branch-misses
+ bigger_is_better: false
+ type: Real
+ - name: stalled-cycles-frontend
+ bigger_is_better: false
+ type: Real
+ - name: branches
+ bigger_is_better: false
+ type: Real
+ - name: context-switches
+ bigger_is_better: false
+ type: Real
+ - name: cpu-migrations
+ bigger_is_better: false
+ type: Real
+ - name: cycles
+ bigger_is_better: false
+ type: Real
+ - name: instructions
+ bigger_is_better: false
+ type: Real
+ - name: seconds-elapsed
+ bigger_is_better: false
+ type: Real
+ - name: page-faults
+ bigger_is_better: false
+ type: Real
+ - name: task-clock
+ bigger_is_better: false
+ type: Real
diff --git a/wasm/CMakeLists.txt b/wasm/CMakeLists.txt
new file mode 100644
index 000000000000..19b0d168437c
--- /dev/null
+++ b/wasm/CMakeLists.txt
@@ -0,0 +1,26 @@
+set(LLVM_TARGET_DEFINITIONS Options.td)
+tablegen(LLVM Options.inc -gen-opt-parser-defs)
+add_public_tablegen_target(WasmOptionsTableGen)
+
+add_lld_library(lldWasm
+ Driver.cpp
+ InputFiles.cpp
+ InputSegment.cpp
+ OutputSections.cpp
+ SymbolTable.cpp
+ Symbols.cpp
+ Writer.cpp
+ WriterUtils.cpp
+
+ LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ BinaryFormat
+ Core
+ Demangle
+ Object
+ Option
+ Support
+
+ LINK_LIBS
+ lldCommon
+ )
diff --git a/wasm/Config.h b/wasm/Config.h
new file mode 100644
index 000000000000..82f49ce175bb
--- /dev/null
+++ b/wasm/Config.h
@@ -0,0 +1,51 @@
+//===- Config.h -------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_WASM_CONFIG_H
+#define LLD_WASM_CONFIG_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/BinaryFormat/Wasm.h"
+
+#include "Symbols.h"
+
+using llvm::wasm::WasmGlobal;
+
+namespace lld {
+namespace wasm {
+
+struct Configuration {
+ bool AllowUndefined;
+ bool CheckSignatures;
+ bool Demangle;
+ bool EmitRelocs;
+ bool ImportMemory;
+ bool Relocatable;
+ bool StripAll;
+ bool StripDebug;
+ uint32_t GlobalBase;
+ uint32_t InitialMemory;
+ uint32_t MaxMemory;
+ uint32_t ZStackSize;
+ llvm::StringRef Entry;
+ llvm::StringRef OutputFile;
+
+ llvm::StringSet<> AllowUndefinedSymbols;
+ std::vector<llvm::StringRef> SearchPaths;
+ Symbol *StackPointerSymbol = nullptr;
+};
+
+// The only instance of Configuration struct.
+extern Configuration *Config;
+
+} // namespace wasm
+} // namespace lld
+
+#endif
diff --git a/wasm/Driver.cpp b/wasm/Driver.cpp
new file mode 100644
index 000000000000..97ec262be308
--- /dev/null
+++ b/wasm/Driver.cpp
@@ -0,0 +1,321 @@
+//===- Driver.cpp ---------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lld/Common/Driver.h"
+#include "Config.h"
+#include "SymbolTable.h"
+#include "Writer.h"
+#include "lld/Common/Args.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+#include "lld/Common/Threads.h"
+#include "lld/Common/Version.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Object/Wasm.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+
+using namespace llvm;
+using namespace llvm::sys;
+using namespace llvm::wasm;
+
+using namespace lld;
+using namespace lld::wasm;
+
+namespace {
+
+// Parses command line options.
+class WasmOptTable : public llvm::opt::OptTable {
+public:
+ WasmOptTable();
+ llvm::opt::InputArgList parse(ArrayRef<const char *> Argv);
+};
+
+// 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, _12) OPT_##ID,
+#include "Options.inc"
+#undef OPTION
+};
+
+class LinkerDriver {
+public:
+ void link(ArrayRef<const char *> ArgsArr);
+
+private:
+ void createFiles(llvm::opt::InputArgList &Args);
+ void addFile(StringRef Path);
+ void addLibrary(StringRef Name);
+ std::vector<InputFile *> Files;
+};
+
+} // anonymous namespace
+
+Configuration *lld::wasm::Config;
+
+bool lld::wasm::link(ArrayRef<const char *> Args, bool CanExitEarly,
+ raw_ostream &Error) {
+ errorHandler().LogName = Args[0];
+ errorHandler().ErrorOS = &Error;
+ errorHandler().ColorDiagnostics = Error.has_colors();
+ errorHandler().ErrorLimitExceededMsg =
+ "too many errors emitted, stopping now (use "
+ "-error-limit=0 to see all errors)";
+
+ Config = make<Configuration>();
+ Symtab = make<SymbolTable>();
+
+ LinkerDriver().link(Args);
+
+ // 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 (CanExitEarly)
+ exitLld(errorCount() ? 1 : 0);
+
+ freeArena();
+ return !errorCount();
+}
+
+// Create OptTable
+
+// Create prefix string literals used in Options.td
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "Options.inc"
+#undef PREFIX
+
+// Create table mapping all options defined in Options.td
+static const opt::OptTable::Info OptInfo[] = {
+#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
+};
+
+// Set color diagnostics according to -color-diagnostics={auto,always,never}
+// or -no-color-diagnostics flags.
+static void handleColorDiagnostics(opt::InputArgList &Args) {
+ auto *Arg = Args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq,
+ OPT_no_color_diagnostics);
+ if (!Arg)
+ return;
+
+ if (Arg->getOption().getID() == OPT_color_diagnostics)
+ errorHandler().ColorDiagnostics = true;
+ else if (Arg->getOption().getID() == OPT_no_color_diagnostics)
+ errorHandler().ColorDiagnostics = false;
+ else {
+ StringRef S = Arg->getValue();
+ if (S == "always")
+ errorHandler().ColorDiagnostics = true;
+ if (S == "never")
+ errorHandler().ColorDiagnostics = false;
+ if (S != "auto")
+ error("unknown option: -color-diagnostics=" + S);
+ }
+}
+
+// Find a file by concatenating given paths.
+static Optional<std::string> findFile(StringRef Path1, const Twine &Path2) {
+ SmallString<128> S;
+ path::append(S, Path1, Path2);
+ if (fs::exists(S))
+ return S.str().str();
+ return None;
+}
+
+// Inject a new undefined symbol into the link. This will cause the link to
+// fail unless this symbol can be found.
+static void addSyntheticUndefinedFunction(StringRef Name,
+ const WasmSignature *Type) {
+ log("injecting undefined func: " + Name);
+ Symtab->addUndefinedFunction(Name, Type);
+}
+
+static void printHelp(const char *Argv0) {
+ WasmOptTable().PrintHelp(outs(), Argv0, "LLVM Linker", false);
+}
+
+WasmOptTable::WasmOptTable() : OptTable(OptInfo) {}
+
+opt::InputArgList WasmOptTable::parse(ArrayRef<const char *> Argv) {
+ SmallVector<const char *, 256> Vec(Argv.data(), Argv.data() + Argv.size());
+
+ unsigned MissingIndex;
+ unsigned MissingCount;
+ opt::InputArgList Args = this->ParseArgs(Vec, MissingIndex, MissingCount);
+
+ handleColorDiagnostics(Args);
+ for (auto *Arg : Args.filtered(OPT_UNKNOWN))
+ error("unknown argument: " + Arg->getSpelling());
+ return Args;
+}
+
+void LinkerDriver::addFile(StringRef Path) {
+ Optional<MemoryBufferRef> Buffer = readFile(Path);
+ if (!Buffer.hasValue())
+ return;
+ MemoryBufferRef MBRef = *Buffer;
+
+ if (identify_magic(MBRef.getBuffer()) == file_magic::archive)
+ Files.push_back(make<ArchiveFile>(MBRef));
+ else
+ Files.push_back(make<ObjFile>(MBRef));
+}
+
+// Add a given library by searching it from input search paths.
+void LinkerDriver::addLibrary(StringRef Name) {
+ for (StringRef Dir : Config->SearchPaths) {
+ if (Optional<std::string> S = findFile(Dir, "lib" + Name + ".a")) {
+ addFile(*S);
+ return;
+ }
+ }
+
+ error("unable to find library -l" + Name);
+}
+
+void LinkerDriver::createFiles(opt::InputArgList &Args) {
+ for (auto *Arg : Args) {
+ switch (Arg->getOption().getUnaliasedOption().getID()) {
+ case OPT_l:
+ addLibrary(Arg->getValue());
+ break;
+ case OPT_INPUT:
+ addFile(Arg->getValue());
+ break;
+ }
+ }
+
+ if (Files.empty())
+ error("no input files");
+}
+
+static StringRef getEntry(opt::InputArgList &Args, StringRef Default) {
+ auto *Arg = Args.getLastArg(OPT_entry, OPT_no_entry);
+ if (!Arg)
+ return Default;
+ if (Arg->getOption().getID() == OPT_no_entry)
+ return "";
+ return Arg->getValue();
+}
+
+void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
+ WasmOptTable Parser;
+ opt::InputArgList Args = Parser.parse(ArgsArr.slice(1));
+
+ // Handle --help
+ if (Args.hasArg(OPT_help)) {
+ printHelp(ArgsArr[0]);
+ return;
+ }
+
+ // Parse and evaluate -mllvm options.
+ std::vector<const char *> V;
+ V.push_back("wasm-ld (LLVM option parsing)");
+ for (auto *Arg : Args.filtered(OPT_mllvm))
+ V.push_back(Arg->getValue());
+ cl::ParseCommandLineOptions(V.size(), V.data());
+
+ errorHandler().ErrorLimit = args::getInteger(Args, OPT_error_limit, 20);
+
+ if (Args.hasArg(OPT_version) || Args.hasArg(OPT_v)) {
+ outs() << getLLDVersion() << "\n";
+ return;
+ }
+
+ Config->AllowUndefined = Args.hasArg(OPT_allow_undefined);
+ Config->CheckSignatures =
+ Args.hasFlag(OPT_check_signatures, OPT_no_check_signatures, false);
+ Config->EmitRelocs = Args.hasArg(OPT_emit_relocs);
+ Config->Entry = getEntry(Args, Args.hasArg(OPT_relocatable) ? "" : "_start");
+ Config->ImportMemory = Args.hasArg(OPT_import_memory);
+ Config->OutputFile = Args.getLastArgValue(OPT_o);
+ Config->Relocatable = Args.hasArg(OPT_relocatable);
+ Config->SearchPaths = args::getStrings(Args, OPT_L);
+ Config->StripAll = Args.hasArg(OPT_strip_all);
+ Config->StripDebug = Args.hasArg(OPT_strip_debug);
+ errorHandler().Verbose = Args.hasArg(OPT_verbose);
+ ThreadsEnabled = Args.hasFlag(OPT_threads, OPT_no_threads, true);
+ if (Config->Relocatable)
+ Config->EmitRelocs = true;
+
+ Config->InitialMemory = args::getInteger(Args, OPT_initial_memory, 0);
+ Config->GlobalBase = args::getInteger(Args, OPT_global_base, 1024);
+ Config->MaxMemory = args::getInteger(Args, OPT_max_memory, 0);
+ Config->ZStackSize =
+ args::getZOptionValue(Args, OPT_z, "stack-size", WasmPageSize);
+
+ if (auto *Arg = Args.getLastArg(OPT_allow_undefined_file))
+ if (Optional<MemoryBufferRef> Buf = readFile(Arg->getValue()))
+ for (StringRef Sym : args::getLines(*Buf))
+ Config->AllowUndefinedSymbols.insert(Sym);
+
+ if (Config->OutputFile.empty())
+ error("no output file specified");
+
+ if (!Args.hasArg(OPT_INPUT))
+ error("no input files");
+
+ if (Config->Relocatable && !Config->Entry.empty())
+ error("entry point specified for relocatable output file");
+ if (Config->Relocatable && Args.hasArg(OPT_undefined))
+ error("undefined symbols specified for relocatable output file");
+
+ if (!Config->Relocatable) {
+ if (!Config->Entry.empty()) {
+ static WasmSignature Signature = {{}, WASM_TYPE_NORESULT};
+ addSyntheticUndefinedFunction(Config->Entry, &Signature);
+ }
+
+ // Handle the `--undefined <sym>` options.
+ for (StringRef S : args::getStrings(Args, OPT_undefined))
+ addSyntheticUndefinedFunction(S, nullptr);
+
+ Config->StackPointerSymbol = Symtab->addDefinedGlobal("__stack_pointer");
+ }
+
+ createFiles(Args);
+ if (errorCount())
+ return;
+
+ // 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);
+
+ // Make sure we have resolved all symbols.
+ if (!Config->Relocatable && !Config->AllowUndefined) {
+ Symtab->reportRemainingUndefines();
+ } else {
+ // When we allow undefined symbols we cannot include those defined in
+ // -u/--undefined since these undefined symbols have only names and no
+ // function signature, which means they cannot be written to the final
+ // output.
+ for (StringRef S : args::getStrings(Args, OPT_undefined)) {
+ Symbol *Sym = Symtab->find(S);
+ if (!Sym->isDefined())
+ error("function forced with --undefined not found: " + Sym->getName());
+ }
+ }
+ if (errorCount())
+ return;
+
+ if (!Config->Entry.empty() && !Symtab->find(Config->Entry)->isDefined())
+ error("entry point not found: " + Config->Entry);
+ if (errorCount())
+ return;
+
+ // Write the result to the file.
+ writeResult();
+}
diff --git a/wasm/InputFiles.cpp b/wasm/InputFiles.cpp
new file mode 100644
index 000000000000..e7463da39db9
--- /dev/null
+++ b/wasm/InputFiles.cpp
@@ -0,0 +1,303 @@
+//===- InputFiles.cpp -----------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InputFiles.h"
+
+#include "Config.h"
+#include "InputSegment.h"
+#include "SymbolTable.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/Wasm.h"
+#include "llvm/Support/raw_ostream.h"
+
+#define DEBUG_TYPE "lld"
+
+using namespace lld;
+using namespace lld::wasm;
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace llvm::wasm;
+
+Optional<MemoryBufferRef> lld::wasm::readFile(StringRef Path) {
+ log("Loading: " + Path);
+
+ auto MBOrErr = MemoryBuffer::getFile(Path);
+ if (auto EC = MBOrErr.getError()) {
+ error("cannot open " + Path + ": " + EC.message());
+ return None;
+ }
+ std::unique_ptr<MemoryBuffer> &MB = *MBOrErr;
+ MemoryBufferRef MBRef = MB->getMemBufferRef();
+ make<std::unique_ptr<MemoryBuffer>>(std::move(MB)); // take MB ownership
+
+ return MBRef;
+}
+
+void ObjFile::dumpInfo() const {
+ log("reloc info for: " + getName() + "\n" +
+ " FunctionIndexOffset : " + Twine(FunctionIndexOffset) + "\n" +
+ " NumFunctionImports : " + Twine(NumFunctionImports()) + "\n" +
+ " NumGlobalImports : " + Twine(NumGlobalImports()) + "\n");
+}
+
+bool ObjFile::isImportedFunction(uint32_t Index) const {
+ return Index < NumFunctionImports();
+}
+
+Symbol *ObjFile::getFunctionSymbol(uint32_t Index) const {
+ return FunctionSymbols[Index];
+}
+
+Symbol *ObjFile::getTableSymbol(uint32_t Index) const {
+ return TableSymbols[Index];
+}
+
+Symbol *ObjFile::getGlobalSymbol(uint32_t Index) const {
+ return GlobalSymbols[Index];
+}
+
+uint32_t ObjFile::getRelocatedAddress(uint32_t Index) const {
+ return getGlobalSymbol(Index)->getVirtualAddress();
+}
+
+uint32_t ObjFile::relocateFunctionIndex(uint32_t Original) const {
+ Symbol *Sym = getFunctionSymbol(Original);
+ uint32_t Index = Sym->getOutputIndex();
+ DEBUG(dbgs() << "relocateFunctionIndex: " << toString(*Sym) << ": "
+ << Original << " -> " << Index << "\n");
+ return Index;
+}
+
+uint32_t ObjFile::relocateTypeIndex(uint32_t Original) const {
+ return TypeMap[Original];
+}
+
+uint32_t ObjFile::relocateTableIndex(uint32_t Original) const {
+ Symbol *Sym = getTableSymbol(Original);
+ uint32_t Index = Sym->getTableIndex();
+ DEBUG(dbgs() << "relocateTableIndex: " << toString(*Sym) << ": " << Original
+ << " -> " << Index << "\n");
+ return Index;
+}
+
+uint32_t ObjFile::relocateGlobalIndex(uint32_t Original) const {
+ Symbol *Sym = getGlobalSymbol(Original);
+ uint32_t Index = Sym->getOutputIndex();
+ DEBUG(dbgs() << "relocateGlobalIndex: " << toString(*Sym) << ": " << Original
+ << " -> " << Index << "\n");
+ return Index;
+}
+
+void ObjFile::parse() {
+ // Parse a memory buffer as a wasm file.
+ DEBUG(dbgs() << "Parsing object: " << toString(this) << "\n");
+ std::unique_ptr<Binary> Bin = CHECK(createBinary(MB), toString(this));
+
+ auto *Obj = dyn_cast<WasmObjectFile>(Bin.get());
+ if (!Obj)
+ fatal(toString(this) + ": not a wasm file");
+ if (!Obj->isRelocatableObject())
+ fatal(toString(this) + ": not a relocatable wasm file");
+
+ Bin.release();
+ WasmObj.reset(Obj);
+
+ // Find the code and data sections. Wasm objects can have at most one code
+ // and one data section.
+ for (const SectionRef &Sec : WasmObj->sections()) {
+ const WasmSection &Section = WasmObj->getWasmSection(Sec);
+ if (Section.Type == WASM_SEC_CODE)
+ CodeSection = &Section;
+ else if (Section.Type == WASM_SEC_DATA)
+ DataSection = &Section;
+ }
+
+ initializeSymbols();
+}
+
+// Return the InputSegment in which a given symbol is defined.
+InputSegment *ObjFile::getSegment(const WasmSymbol &WasmSym) {
+ uint32_t Address = WasmObj->getWasmSymbolValue(WasmSym);
+ for (InputSegment *Segment : Segments) {
+ if (Address >= Segment->startVA() && Address < Segment->endVA()) {
+ DEBUG(dbgs() << "Found symbol in segment: " << WasmSym.Name << " -> "
+ << Segment->getName() << "\n");
+
+ return Segment;
+ }
+ }
+ error("symbol not found in any segment: " + WasmSym.Name);
+ return nullptr;
+}
+
+static void copyRelocationsRange(std::vector<WasmRelocation> &To,
+ ArrayRef<WasmRelocation> From, size_t Start,
+ size_t End) {
+ for (const WasmRelocation &R : From)
+ if (R.Offset >= Start && R.Offset < End)
+ To.push_back(R);
+}
+
+void ObjFile::initializeSymbols() {
+ Symbols.reserve(WasmObj->getNumberOfSymbols());
+
+ for (const WasmImport &Import : WasmObj->imports()) {
+ switch (Import.Kind) {
+ case WASM_EXTERNAL_FUNCTION:
+ ++FunctionImports;
+ break;
+ case WASM_EXTERNAL_GLOBAL:
+ ++GlobalImports;
+ break;
+ }
+ }
+
+ FunctionSymbols.resize(FunctionImports + WasmObj->functions().size());
+ GlobalSymbols.resize(GlobalImports + WasmObj->globals().size());
+
+ for (const WasmSegment &S : WasmObj->dataSegments()) {
+ InputSegment *Seg = make<InputSegment>(&S, this);
+ copyRelocationsRange(Seg->Relocations, DataSection->Relocations,
+ Seg->getInputSectionOffset(),
+ Seg->getInputSectionOffset() + Seg->getSize());
+ Segments.emplace_back(Seg);
+ }
+
+ // Populate `FunctionSymbols` and `GlobalSymbols` based on the WasmSymbols
+ // in the object
+ for (const SymbolRef &Sym : WasmObj->symbols()) {
+ const WasmSymbol &WasmSym = WasmObj->getWasmSymbol(Sym.getRawDataRefImpl());
+ Symbol *S;
+ switch (WasmSym.Type) {
+ case WasmSymbol::SymbolType::FUNCTION_IMPORT:
+ case WasmSymbol::SymbolType::GLOBAL_IMPORT:
+ S = createUndefined(WasmSym);
+ break;
+ case WasmSymbol::SymbolType::GLOBAL_EXPORT:
+ S = createDefined(WasmSym, getSegment(WasmSym));
+ break;
+ case WasmSymbol::SymbolType::FUNCTION_EXPORT:
+ S = createDefined(WasmSym);
+ break;
+ case WasmSymbol::SymbolType::DEBUG_FUNCTION_NAME:
+ // These are for debugging only, no need to create linker symbols for them
+ continue;
+ }
+
+ Symbols.push_back(S);
+ if (WasmSym.isFunction()) {
+ DEBUG(dbgs() << "Function: " << WasmSym.ElementIndex << " -> "
+ << toString(*S) << "\n");
+ FunctionSymbols[WasmSym.ElementIndex] = S;
+ } else {
+ DEBUG(dbgs() << "Global: " << WasmSym.ElementIndex << " -> "
+ << toString(*S) << "\n");
+ GlobalSymbols[WasmSym.ElementIndex] = S;
+ }
+ }
+
+ // Populate `TableSymbols` with all symbols that are called indirectly
+ uint32_t SegmentCount = WasmObj->elements().size();
+ if (SegmentCount) {
+ if (SegmentCount > 1)
+ fatal(getName() + ": contains more than one element segment");
+ const WasmElemSegment &Segment = WasmObj->elements()[0];
+ if (Segment.Offset.Opcode != WASM_OPCODE_I32_CONST)
+ fatal(getName() + ": unsupported element segment");
+ if (Segment.TableIndex != 0)
+ fatal(getName() + ": unsupported table index in elem segment");
+ if (Segment.Offset.Value.Int32 != 0)
+ fatal(getName() + ": unsupported element segment offset");
+ TableSymbols.reserve(Segment.Functions.size());
+ for (uint64_t FunctionIndex : Segment.Functions)
+ TableSymbols.push_back(getFunctionSymbol(FunctionIndex));
+ }
+
+ DEBUG(dbgs() << "TableSymbols: " << TableSymbols.size() << "\n");
+ DEBUG(dbgs() << "Functions : " << FunctionSymbols.size() << "\n");
+ DEBUG(dbgs() << "Globals : " << GlobalSymbols.size() << "\n");
+}
+
+Symbol *ObjFile::createUndefined(const WasmSymbol &Sym) {
+ return Symtab->addUndefined(this, &Sym);
+}
+
+Symbol *ObjFile::createDefined(const WasmSymbol &Sym,
+ const InputSegment *Segment) {
+ Symbol *S;
+ if (Sym.isLocal()) {
+ S = make<Symbol>(Sym.Name, true);
+ Symbol::Kind Kind;
+ if (Sym.Type == WasmSymbol::SymbolType::FUNCTION_EXPORT)
+ Kind = Symbol::Kind::DefinedFunctionKind;
+ else if (Sym.Type == WasmSymbol::SymbolType::GLOBAL_EXPORT)
+ Kind = Symbol::Kind::DefinedGlobalKind;
+ else
+ llvm_unreachable("invalid local symbol type");
+ S->update(Kind, this, &Sym, Segment);
+ return S;
+ }
+ return Symtab->addDefined(this, &Sym, Segment);
+}
+
+void ArchiveFile::parse() {
+ // Parse a MemoryBufferRef as an archive file.
+ DEBUG(dbgs() << "Parsing library: " << toString(this) << "\n");
+ File = CHECK(Archive::create(MB), toString(this));
+
+ // Read the symbol table to construct Lazy symbols.
+ int Count = 0;
+ for (const Archive::Symbol &Sym : File->symbols()) {
+ Symtab->addLazy(this, &Sym);
+ ++Count;
+ }
+ DEBUG(dbgs() << "Read " << Count << " symbols\n");
+}
+
+void ArchiveFile::addMember(const Archive::Symbol *Sym) {
+ const Archive::Child &C =
+ CHECK(Sym->getMember(),
+ "could not get the member for symbol " + Sym->getName());
+
+ // Don't try to load the same member twice (this can happen when members
+ // mutually reference each other).
+ if (!Seen.insert(C.getChildOffset()).second)
+ return;
+
+ DEBUG(dbgs() << "loading lazy: " << Sym->getName() << "\n");
+ DEBUG(dbgs() << "from archive: " << toString(this) << "\n");
+
+ MemoryBufferRef MB =
+ CHECK(C.getMemoryBufferRef(),
+ "could not get the buffer for the member defining symbol " +
+ Sym->getName());
+
+ if (identify_magic(MB.getBuffer()) != file_magic::wasm_object) {
+ error("unknown file type: " + MB.getBufferIdentifier());
+ return;
+ }
+
+ InputFile *Obj = make<ObjFile>(MB);
+ Obj->ParentName = ParentName;
+ Symtab->addFile(Obj);
+}
+
+// Returns a string in the format of "foo.o" or "foo.a(bar.o)".
+std::string lld::toString(const wasm::InputFile *File) {
+ if (!File)
+ return "<internal>";
+
+ if (File->ParentName.empty())
+ return File->getName();
+
+ return (File->ParentName + "(" + File->getName() + ")").str();
+}
diff --git a/wasm/InputFiles.h b/wasm/InputFiles.h
new file mode 100644
index 000000000000..158cc53cafb1
--- /dev/null
+++ b/wasm/InputFiles.h
@@ -0,0 +1,153 @@
+//===- InputFiles.h ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_WASM_INPUT_FILES_H
+#define LLD_WASM_INPUT_FILES_H
+
+#include "lld/Common/LLVM.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/Wasm.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+#include "WriterUtils.h"
+
+#include <vector>
+
+using llvm::object::Archive;
+using llvm::object::WasmObjectFile;
+using llvm::object::WasmSection;
+using llvm::object::WasmSymbol;
+using llvm::wasm::WasmImport;
+
+namespace lld {
+namespace wasm {
+
+class Symbol;
+class InputSegment;
+
+class InputFile {
+public:
+ enum Kind {
+ ObjectKind,
+ ArchiveKind,
+ };
+
+ virtual ~InputFile() {}
+
+ // Returns the filename.
+ StringRef getName() const { return MB.getBufferIdentifier(); }
+
+ // Reads a file (the constructor doesn't do that).
+ virtual void parse() = 0;
+
+ Kind kind() const { return FileKind; }
+
+ // An archive file name if this file is created from an archive.
+ StringRef ParentName;
+
+protected:
+ InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {}
+ MemoryBufferRef MB;
+
+private:
+ const Kind FileKind;
+};
+
+// .a file (ar archive)
+class ArchiveFile : public InputFile {
+public:
+ explicit ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {}
+ static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; }
+
+ void addMember(const Archive::Symbol *Sym);
+
+ void parse() override;
+
+private:
+ std::unique_ptr<Archive> File;
+ llvm::DenseSet<uint64_t> Seen;
+};
+
+// .o file (wasm object file)
+class ObjFile : public InputFile {
+public:
+ explicit ObjFile(MemoryBufferRef M) : InputFile(ObjectKind, M) {}
+ static bool classof(const InputFile *F) { return F->kind() == ObjectKind; }
+
+ void parse() override;
+
+ // Returns the underlying wasm file.
+ const WasmObjectFile *getWasmObj() const { return WasmObj.get(); }
+
+ void dumpInfo() const;
+
+ uint32_t relocateTypeIndex(uint32_t Original) const;
+ uint32_t relocateFunctionIndex(uint32_t Original) const;
+ uint32_t relocateGlobalIndex(uint32_t Original) const;
+ uint32_t relocateTableIndex(uint32_t Original) const;
+ uint32_t getRelocatedAddress(uint32_t Index) const;
+
+ // Returns true if the given function index is an imported function,
+ // as opposed to the locally defined function.
+ bool isImportedFunction(uint32_t Index) const;
+
+ size_t NumFunctionImports() const { return FunctionImports; }
+ size_t NumGlobalImports() const { return GlobalImports; }
+
+ int32_t FunctionIndexOffset = 0;
+ const WasmSection *CodeSection = nullptr;
+ std::vector<OutputRelocation> CodeRelocations;
+ int32_t CodeOffset = 0;
+ const WasmSection *DataSection = nullptr;
+
+ std::vector<uint32_t> TypeMap;
+ std::vector<InputSegment *> Segments;
+
+ ArrayRef<Symbol *> getSymbols() { return Symbols; }
+ ArrayRef<Symbol *> getTableSymbols() { return TableSymbols; }
+
+private:
+ Symbol *createDefined(const WasmSymbol &Sym,
+ const InputSegment *Segment = nullptr);
+ Symbol *createUndefined(const WasmSymbol &Sym);
+ void initializeSymbols();
+ InputSegment *getSegment(const WasmSymbol &WasmSym);
+ Symbol *getFunctionSymbol(uint32_t FunctionIndex) const;
+ Symbol *getTableSymbol(uint32_t TableIndex) const;
+ Symbol *getGlobalSymbol(uint32_t GlobalIndex) const;
+
+ // List of all symbols referenced or defined by this file.
+ std::vector<Symbol *> Symbols;
+
+ // List of all function symbols indexed by the function index space
+ std::vector<Symbol *> FunctionSymbols;
+
+ // List of all global symbols indexed by the global index space
+ std::vector<Symbol *> GlobalSymbols;
+
+ // List of all indirect symbols indexed by table index space.
+ std::vector<Symbol *> TableSymbols;
+
+ uint32_t GlobalImports = 0;
+ uint32_t FunctionImports = 0;
+ std::unique_ptr<WasmObjectFile> WasmObj;
+};
+
+// Opens a given file.
+llvm::Optional<MemoryBufferRef> readFile(StringRef Path);
+
+} // namespace wasm
+
+std::string toString(const wasm::InputFile *File);
+
+} // namespace lld
+
+#endif
diff --git a/wasm/InputSegment.cpp b/wasm/InputSegment.cpp
new file mode 100644
index 000000000000..650914386259
--- /dev/null
+++ b/wasm/InputSegment.cpp
@@ -0,0 +1,25 @@
+//===- InputSegment.cpp ---------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InputSegment.h"
+#include "OutputSegment.h"
+#include "lld/Common/LLVM.h"
+
+#define DEBUG_TYPE "lld"
+
+using namespace llvm;
+using namespace lld::wasm;
+
+uint32_t InputSegment::translateVA(uint32_t Address) const {
+ assert(Address >= startVA() && Address < endVA());
+ int32_t Delta = OutputSeg->StartVA + OutputSegmentOffset - startVA();
+ DEBUG(dbgs() << "translateVA: " << getName() << " Delta=" << Delta
+ << " Address=" << Address << "\n");
+ return Address + Delta;
+}
diff --git a/wasm/InputSegment.h b/wasm/InputSegment.h
new file mode 100644
index 000000000000..64124b1ebc2c
--- /dev/null
+++ b/wasm/InputSegment.h
@@ -0,0 +1,74 @@
+//===- InputSegment.h -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Represents a WebAssembly data segment which can be included as part of
+// an output data segments. Note that in WebAssembly, unlike ELF and other
+// formats, used the term "data segment" to refer to the continous regions of
+// memory that make on the data section. See:
+// https://webassembly.github.io/spec/syntax/modules.html#syntax-data
+//
+// For example, by default, clang will produce a separate data section for
+// each global variable.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_WASM_INPUT_SEGMENT_H
+#define LLD_WASM_INPUT_SEGMENT_H
+
+#include "lld/Common/ErrorHandler.h"
+#include "llvm/Object/Wasm.h"
+
+using llvm::object::WasmSegment;
+using llvm::wasm::WasmRelocation;
+
+namespace lld {
+namespace wasm {
+
+class ObjFile;
+class OutputSegment;
+
+class InputSegment {
+public:
+ InputSegment(const WasmSegment *Seg, const ObjFile *F)
+ : Segment(Seg), File(F) {}
+
+ // Translate an offset in the input segment to an offset in the output
+ // segment.
+ uint32_t translateVA(uint32_t Address) const;
+
+ const OutputSegment *getOutputSegment() const { return OutputSeg; }
+
+ uint32_t getOutputSegmentOffset() const { return OutputSegmentOffset; }
+
+ uint32_t getInputSectionOffset() const { return Segment->SectionOffset; }
+
+ void setOutputSegment(const OutputSegment *Segment, uint32_t Offset) {
+ OutputSeg = Segment;
+ OutputSegmentOffset = Offset;
+ }
+
+ uint32_t getSize() const { return Segment->Data.Content.size(); }
+ uint32_t getAlignment() const { return Segment->Data.Alignment; }
+ uint32_t startVA() const { return Segment->Data.Offset.Value.Int32; }
+ uint32_t endVA() const { return startVA() + getSize(); }
+ StringRef getName() const { return Segment->Data.Name; }
+
+ const WasmSegment *Segment;
+ const ObjFile *File;
+ std::vector<WasmRelocation> Relocations;
+
+protected:
+ const OutputSegment *OutputSeg = nullptr;
+ uint32_t OutputSegmentOffset = 0;
+};
+
+} // namespace wasm
+} // namespace lld
+
+#endif // LLD_WASM_INPUT_SEGMENT_H
diff --git a/wasm/Options.td b/wasm/Options.td
new file mode 100644
index 000000000000..df0c6d708072
--- /dev/null
+++ b/wasm/Options.td
@@ -0,0 +1,103 @@
+include "llvm/Option/OptParser.td"
+
+// For options whose names are multiple letters, either one dash or
+// two can precede the option name except those that start with 'o'.
+class F<string name>: Flag<["--", "-"], name>;
+class J<string name>: Joined<["--", "-"], name>;
+class S<string name>: Separate<["--", "-"], name>;
+
+multiclass Eq<string name> {
+ def "": Separate<["--", "-"], name>;
+ def _eq: Joined<["--", "-"], name # "=">, Alias<!cast<Separate>(NAME)>;
+}
+
+def L: JoinedOrSeparate<["-"], "L">, MetaVarName<"<dir>">,
+ HelpText<"Add a directory to the library search path">;
+
+def color_diagnostics: F<"color-diagnostics">,
+ HelpText<"Use colors in diagnostics">;
+
+def color_diagnostics_eq: J<"color-diagnostics=">,
+ HelpText<"Use colors in diagnostics">;
+
+// The follow flags are shared with the ELF linker
+def help: F<"help">, HelpText<"Print option help">;
+
+def l: JoinedOrSeparate<["-"], "l">, MetaVarName<"<libName>">,
+ HelpText<"Root name of library to use">;
+
+def mllvm: S<"mllvm">, HelpText<"Options to pass to LLVM">;
+
+def no_threads: F<"no-threads">,
+ HelpText<"Do not run the linker multi-threaded">;
+
+def no_color_diagnostics: F<"no-color-diagnostics">,
+ HelpText<"Do not use colors in diagnostics">;
+
+def no_check_signatures: F<"no-check-signatures">, HelpText<"Don't check function signatures">;
+
+def o: JoinedOrSeparate<["-"], "o">, MetaVarName<"<path>">,
+ HelpText<"Path to file to write output">;
+
+def threads: F<"threads">, HelpText<"Run the linker multi-threaded">;
+
+def check_signatures: F<"check-signatures">, HelpText<"Check function signatures">;
+
+def v: Flag<["-"], "v">, HelpText<"Display the version number">;
+
+def version: F<"version">, HelpText<"Display the version number and exit">;
+
+def verbose: F<"verbose">, HelpText<"Verbose mode">;
+
+def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">;
+
+def emit_relocs: F<"emit-relocs">, HelpText<"Generate relocations in output">;
+
+def strip_all: F<"strip-all">, HelpText<"Strip all symbols">;
+
+def strip_debug: F<"strip-debug">, HelpText<"Strip debugging information">;
+
+defm undefined: Eq<"undefined">,
+ HelpText<"Force undefined symbol during linking">;
+
+def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"<option>">,
+ HelpText<"Linker option extensions">;
+
+def entry: S<"entry">, MetaVarName<"<entry>">,
+ HelpText<"Name of entry point symbol">;
+
+def no_entry: F<"no-entry">,
+ HelpText<"Do not output any entry point">;
+
+def error_limit: J<"error-limit=">,
+ HelpText<"Maximum number of errors to emit before stopping (0 = no limit)">;
+
+// The follow flags are unique to wasm
+
+def global_base: J<"global-base=">,
+ HelpText<"Where to start to place global data">;
+
+def initial_memory: J<"initial-memory=">,
+ HelpText<"Initial size of the linear memory">;
+
+def max_memory: J<"max-memory=">,
+ HelpText<"Maximum size of the linear memory">;
+
+def import_memory: F<"import-memory">,
+ HelpText<"Import memory from the environment">;
+
+def allow_undefined: F<"allow-undefined">,
+ HelpText<"Allow undefined symbols in linked binary">;
+
+def allow_undefined_file: J<"allow-undefined-file=">,
+ HelpText<"Allow symbols listed in <file> to be undefined in linked binary">;
+
+def allow_undefined_file_s: Separate<["-"], "allow-undefined-file">, Alias<allow_undefined_file>;
+
+// Aliases
+def alias_initial_memory_i: Flag<["-"], "i">, Alias<initial_memory>;
+def alias_max_memory_m: Flag<["-"], "m">, Alias<max_memory>;
+def alias_relocatable_r: Flag<["-"], "r">, Alias<relocatable>;
+def alias_entry_e: JoinedOrSeparate<["-"], "e">, Alias<entry>;
+def alias_entry_entry: J<"entry=">, Alias<entry>;
+def alias_undefined_u: JoinedOrSeparate<["-"], "u">, Alias<undefined>;
diff --git a/wasm/OutputSections.cpp b/wasm/OutputSections.cpp
new file mode 100644
index 000000000000..e5253640b9db
--- /dev/null
+++ b/wasm/OutputSections.cpp
@@ -0,0 +1,333 @@
+//===- OutputSections.cpp -------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "OutputSections.h"
+
+#include "Config.h"
+#include "InputFiles.h"
+#include "OutputSegment.h"
+#include "SymbolTable.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+#include "lld/Common/Threads.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/LEB128.h"
+
+#define DEBUG_TYPE "lld"
+
+using namespace llvm;
+using namespace llvm::wasm;
+using namespace lld;
+using namespace lld::wasm;
+
+enum class RelocEncoding {
+ Uleb128,
+ Sleb128,
+ I32,
+};
+
+static StringRef sectionTypeToString(uint32_t SectionType) {
+ switch (SectionType) {
+ case WASM_SEC_CUSTOM:
+ return "CUSTOM";
+ case WASM_SEC_TYPE:
+ return "TYPE";
+ case WASM_SEC_IMPORT:
+ return "IMPORT";
+ case WASM_SEC_FUNCTION:
+ return "FUNCTION";
+ case WASM_SEC_TABLE:
+ return "TABLE";
+ case WASM_SEC_MEMORY:
+ return "MEMORY";
+ case WASM_SEC_GLOBAL:
+ return "GLOBAL";
+ case WASM_SEC_EXPORT:
+ return "EXPORT";
+ case WASM_SEC_START:
+ return "START";
+ case WASM_SEC_ELEM:
+ return "ELEM";
+ case WASM_SEC_CODE:
+ return "CODE";
+ case WASM_SEC_DATA:
+ return "DATA";
+ default:
+ fatal("invalid section type");
+ }
+}
+
+std::string lld::toString(OutputSection *Section) {
+ std::string rtn = sectionTypeToString(Section->Type);
+ if (!Section->Name.empty())
+ rtn += "(" + Section->Name + ")";
+ return rtn;
+}
+
+static void applyRelocation(uint8_t *Buf, const OutputRelocation &Reloc) {
+ DEBUG(dbgs() << "write reloc: type=" << Reloc.Reloc.Type
+ << " index=" << Reloc.Reloc.Index << " value=" << Reloc.Value
+ << " offset=" << Reloc.Reloc.Offset << "\n");
+ Buf += Reloc.Reloc.Offset;
+ int64_t ExistingValue;
+ switch (Reloc.Reloc.Type) {
+ case R_WEBASSEMBLY_TYPE_INDEX_LEB:
+ case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
+ ExistingValue = decodeULEB128(Buf);
+ if (ExistingValue != Reloc.Reloc.Index) {
+ DEBUG(dbgs() << "existing value: " << decodeULEB128(Buf) << "\n");
+ assert(decodeULEB128(Buf) == Reloc.Reloc.Index);
+ }
+ LLVM_FALLTHROUGH;
+ case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
+ case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
+ encodeULEB128(Reloc.Value, Buf, 5);
+ break;
+ case R_WEBASSEMBLY_TABLE_INDEX_SLEB:
+ ExistingValue = decodeSLEB128(Buf);
+ if (ExistingValue != Reloc.Reloc.Index) {
+ DEBUG(dbgs() << "existing value: " << decodeSLEB128(Buf) << "\n");
+ assert(decodeSLEB128(Buf) == Reloc.Reloc.Index);
+ }
+ LLVM_FALLTHROUGH;
+ case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
+ encodeSLEB128(static_cast<int32_t>(Reloc.Value), Buf, 5);
+ break;
+ case R_WEBASSEMBLY_TABLE_INDEX_I32:
+ case R_WEBASSEMBLY_MEMORY_ADDR_I32:
+ support::endian::write32<support::little>(Buf, Reloc.Value);
+ break;
+ default:
+ llvm_unreachable("unknown relocation type");
+ }
+}
+
+static void applyRelocations(uint8_t *Buf,
+ ArrayRef<OutputRelocation> Relocs) {
+ log("applyRelocations: count=" + Twine(Relocs.size()));
+ for (const OutputRelocation &Reloc : Relocs) {
+ applyRelocation(Buf, Reloc);
+ }
+}
+
+// Relocations contain an index into the function, global or table index
+// space of the input file. This function takes a relocation and returns the
+// relocated index (i.e. translates from the input index space to the output
+// index space).
+static uint32_t calcNewIndex(const ObjFile &File, const WasmRelocation &Reloc) {
+ switch (Reloc.Type) {
+ case R_WEBASSEMBLY_TYPE_INDEX_LEB:
+ return File.relocateTypeIndex(Reloc.Index);
+ case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
+ return File.relocateFunctionIndex(Reloc.Index);
+ case R_WEBASSEMBLY_TABLE_INDEX_I32:
+ case R_WEBASSEMBLY_TABLE_INDEX_SLEB:
+ return File.relocateTableIndex(Reloc.Index);
+ case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
+ case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
+ case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
+ case R_WEBASSEMBLY_MEMORY_ADDR_I32:
+ return File.relocateGlobalIndex(Reloc.Index);
+ default:
+ llvm_unreachable("unknown relocation type");
+ }
+}
+
+// Take a vector of relocations from an input file and create output
+// relocations based on them. Calculates the updated index and offset for
+// each relocation as well as the value to write out in the final binary.
+static void calcRelocations(const ObjFile &File,
+ ArrayRef<WasmRelocation> Relocs,
+ std::vector<OutputRelocation> &OutputRelocs,
+ int32_t OutputOffset) {
+ log("calcRelocations: " + File.getName() + " offset=" + Twine(OutputOffset));
+ for (const WasmRelocation &Reloc : Relocs) {
+ OutputRelocation NewReloc;
+ NewReloc.Reloc = Reloc;
+ NewReloc.Reloc.Offset += OutputOffset;
+ DEBUG(dbgs() << "reloc: type=" << Reloc.Type << " index=" << Reloc.Index
+ << " offset=" << Reloc.Offset
+ << " newOffset=" << NewReloc.Reloc.Offset << "\n");
+
+ if (Config->EmitRelocs)
+ NewReloc.NewIndex = calcNewIndex(File, Reloc);
+ else
+ NewReloc.NewIndex = UINT32_MAX;
+
+ switch (Reloc.Type) {
+ case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
+ case R_WEBASSEMBLY_MEMORY_ADDR_I32:
+ case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
+ NewReloc.Value = File.getRelocatedAddress(Reloc.Index);
+ if (NewReloc.Value != UINT32_MAX)
+ NewReloc.Value += Reloc.Addend;
+ break;
+ default:
+ NewReloc.Value = calcNewIndex(File, Reloc);
+ break;
+ }
+
+ OutputRelocs.emplace_back(NewReloc);
+ }
+}
+
+void OutputSection::createHeader(size_t BodySize) {
+ raw_string_ostream OS(Header);
+ debugWrite(OS.tell(),
+ "section type [" + Twine(sectionTypeToString(Type)) + "]");
+ writeUleb128(OS, Type, nullptr);
+ writeUleb128(OS, BodySize, "section size");
+ OS.flush();
+ log("createHeader: " + toString(this) + " body=" + Twine(BodySize) +
+ " total=" + Twine(getSize()));
+}
+
+CodeSection::CodeSection(uint32_t NumFunctions, ArrayRef<ObjFile *> Objs)
+ : OutputSection(WASM_SEC_CODE), InputObjects(Objs) {
+ raw_string_ostream OS(CodeSectionHeader);
+ writeUleb128(OS, NumFunctions, "function count");
+ OS.flush();
+ BodySize = CodeSectionHeader.size();
+
+ for (ObjFile *File : InputObjects) {
+ if (!File->CodeSection)
+ continue;
+
+ File->CodeOffset = BodySize;
+ ArrayRef<uint8_t> Content = File->CodeSection->Content;
+ unsigned HeaderSize = 0;
+ decodeULEB128(Content.data(), &HeaderSize);
+
+ calcRelocations(*File, File->CodeSection->Relocations,
+ File->CodeRelocations, BodySize - HeaderSize);
+
+ size_t PayloadSize = Content.size() - HeaderSize;
+ BodySize += PayloadSize;
+ }
+
+ createHeader(BodySize);
+}
+
+void CodeSection::writeTo(uint8_t *Buf) {
+ log("writing " + toString(this));
+ log(" size=" + Twine(getSize()));
+ Buf += Offset;
+
+ // Write section header
+ memcpy(Buf, Header.data(), Header.size());
+ Buf += Header.size();
+
+ uint8_t *ContentsStart = Buf;
+
+ // Write code section headers
+ memcpy(Buf, CodeSectionHeader.data(), CodeSectionHeader.size());
+ Buf += CodeSectionHeader.size();
+
+ // Write code section bodies
+ parallelForEach(InputObjects, [ContentsStart](ObjFile *File) {
+ if (!File->CodeSection)
+ return;
+
+ ArrayRef<uint8_t> Content(File->CodeSection->Content);
+
+ // Payload doesn't include the initial header (function count)
+ unsigned HeaderSize = 0;
+ decodeULEB128(Content.data(), &HeaderSize);
+
+ size_t PayloadSize = Content.size() - HeaderSize;
+ memcpy(ContentsStart + File->CodeOffset, Content.data() + HeaderSize,
+ PayloadSize);
+
+ log("applying relocations for: " + File->getName());
+ if (File->CodeRelocations.size())
+ applyRelocations(ContentsStart, File->CodeRelocations);
+ });
+}
+
+uint32_t CodeSection::numRelocations() const {
+ uint32_t Count = 0;
+ for (ObjFile *File : InputObjects)
+ Count += File->CodeRelocations.size();
+ return Count;
+}
+
+void CodeSection::writeRelocations(raw_ostream &OS) const {
+ for (ObjFile *File : InputObjects)
+ for (const OutputRelocation &Reloc : File->CodeRelocations)
+ writeReloc(OS, Reloc);
+}
+
+DataSection::DataSection(ArrayRef<OutputSegment *> Segments)
+ : OutputSection(WASM_SEC_DATA), Segments(Segments) {
+ raw_string_ostream OS(DataSectionHeader);
+
+ writeUleb128(OS, Segments.size(), "data segment count");
+ OS.flush();
+ BodySize = DataSectionHeader.size();
+
+ for (OutputSegment *Segment : Segments) {
+ raw_string_ostream OS(Segment->Header);
+ writeUleb128(OS, 0, "memory index");
+ writeUleb128(OS, WASM_OPCODE_I32_CONST, "opcode:i32const");
+ writeSleb128(OS, Segment->StartVA, "memory offset");
+ writeUleb128(OS, WASM_OPCODE_END, "opcode:end");
+ writeUleb128(OS, Segment->Size, "segment size");
+ OS.flush();
+ Segment->setSectionOffset(BodySize);
+ BodySize += Segment->Header.size();
+ log("Data segment: size=" + Twine(Segment->Size));
+ for (const InputSegment *InputSeg : Segment->InputSegments) {
+ uint32_t InputOffset = InputSeg->getInputSectionOffset();
+ uint32_t OutputOffset = Segment->getSectionOffset() +
+ Segment->Header.size() +
+ InputSeg->getOutputSegmentOffset();
+ calcRelocations(*InputSeg->File, InputSeg->Relocations, Relocations,
+ OutputOffset - InputOffset);
+ }
+ BodySize += Segment->Size;
+ }
+
+ createHeader(BodySize);
+}
+
+void DataSection::writeTo(uint8_t *Buf) {
+ log("writing " + toString(this) + " size=" + Twine(getSize()) +
+ " body=" + Twine(BodySize));
+ Buf += Offset;
+
+ // Write section header
+ memcpy(Buf, Header.data(), Header.size());
+ Buf += Header.size();
+
+ uint8_t *ContentsStart = Buf;
+
+ // Write data section headers
+ memcpy(Buf, DataSectionHeader.data(), DataSectionHeader.size());
+
+ parallelForEach(Segments, [ContentsStart](const OutputSegment *Segment) {
+ // Write data segment header
+ uint8_t *SegStart = ContentsStart + Segment->getSectionOffset();
+ memcpy(SegStart, Segment->Header.data(), Segment->Header.size());
+
+ // Write segment data payload
+ for (const InputSegment *Input : Segment->InputSegments) {
+ ArrayRef<uint8_t> Content(Input->Segment->Data.Content);
+ memcpy(SegStart + Segment->Header.size() +
+ Input->getOutputSegmentOffset(),
+ Content.data(), Content.size());
+ }
+ });
+
+ applyRelocations(ContentsStart, Relocations);
+}
+
+void DataSection::writeRelocations(raw_ostream &OS) const {
+ for (const OutputRelocation &Reloc : Relocations)
+ writeReloc(OS, Reloc);
+}
diff --git a/wasm/OutputSections.h b/wasm/OutputSections.h
new file mode 100644
index 000000000000..926101710cdf
--- /dev/null
+++ b/wasm/OutputSections.h
@@ -0,0 +1,138 @@
+//===- OutputSections.h -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_WASM_OUTPUT_SECTIONS_H
+#define LLD_WASM_OUTPUT_SECTIONS_H
+
+#include "InputSegment.h"
+#include "WriterUtils.h"
+#include "lld/Common/ErrorHandler.h"
+#include "llvm/ADT/DenseMap.h"
+
+using llvm::raw_ostream;
+using llvm::raw_string_ostream;
+
+namespace lld {
+
+namespace wasm {
+class OutputSection;
+}
+std::string toString(wasm::OutputSection *Section);
+
+namespace wasm {
+
+class OutputSegment;
+class ObjFile;
+
+class OutputSection {
+public:
+ OutputSection(uint32_t Type, std::string Name = "")
+ : Type(Type), Name(Name) {}
+
+ virtual ~OutputSection() = default;
+
+ void setOffset(size_t NewOffset) {
+ log("setOffset: " + toString(this) + " -> " + Twine(NewOffset));
+ Offset = NewOffset;
+ }
+
+ void createHeader(size_t BodySize);
+ virtual size_t getSize() const = 0;
+ virtual void writeTo(uint8_t *Buf) = 0;
+ virtual void finalizeContents() {}
+
+ std::string Header;
+ uint32_t Type;
+ std::string Name;
+
+ virtual uint32_t numRelocations() const { return 0; }
+ virtual void writeRelocations(raw_ostream &OS) const {}
+
+protected:
+ size_t Offset = 0;
+};
+
+class SyntheticSection : public OutputSection {
+public:
+ SyntheticSection(uint32_t Type, std::string Name = "")
+ : OutputSection(Type, Name), BodyOutputStream(Body) {
+ if (!Name.empty())
+ writeStr(BodyOutputStream, Name);
+ }
+
+ void writeTo(uint8_t *Buf) override {
+ assert(Offset);
+ log("writing " + toString(this));
+ memcpy(Buf + Offset, Header.data(), Header.size());
+ memcpy(Buf + Offset + Header.size(), Body.data(), Body.size());
+ }
+
+ size_t getSize() const override { return Header.size() + Body.size(); }
+
+ void finalizeContents() override {
+ BodyOutputStream.flush();
+ createHeader(Body.size());
+ }
+
+ raw_ostream &getStream() { return BodyOutputStream; }
+
+ std::string Body;
+
+protected:
+ raw_string_ostream BodyOutputStream;
+};
+
+// Some synthetic sections (e.g. "name" and "linking") have subsections.
+// Just like the synthetic sections themselves these need to be created before
+// they can be written out (since they are preceded by their length). This
+// class is used to create subsections and then write them into the stream
+// of the parent section.
+class SubSection : public SyntheticSection {
+public:
+ explicit SubSection(uint32_t Type) : SyntheticSection(Type) {}
+
+ void writeToStream(raw_ostream &OS) {
+ writeBytes(OS, Header.data(), Header.size());
+ writeBytes(OS, Body.data(), Body.size());
+ }
+};
+
+class CodeSection : public OutputSection {
+public:
+ explicit CodeSection(uint32_t NumFunctions, ArrayRef<ObjFile *> Objs);
+ size_t getSize() const override { return Header.size() + BodySize; }
+ void writeTo(uint8_t *Buf) override;
+ uint32_t numRelocations() const override;
+ void writeRelocations(raw_ostream &OS) const override;
+
+protected:
+ ArrayRef<ObjFile *> InputObjects;
+ std::string CodeSectionHeader;
+ size_t BodySize = 0;
+};
+
+class DataSection : public OutputSection {
+public:
+ explicit DataSection(ArrayRef<OutputSegment *> Segments);
+ size_t getSize() const override { return Header.size() + BodySize; }
+ void writeTo(uint8_t *Buf) override;
+ uint32_t numRelocations() const override { return Relocations.size(); }
+ void writeRelocations(raw_ostream &OS) const override;
+
+protected:
+ std::vector<OutputRelocation> Relocations;
+ ArrayRef<OutputSegment *> Segments;
+ std::string DataSectionHeader;
+ size_t BodySize = 0;
+};
+
+} // namespace wasm
+} // namespace lld
+
+#endif // LLD_WASM_OUTPUT_SECTIONS_H
diff --git a/wasm/OutputSegment.h b/wasm/OutputSegment.h
new file mode 100644
index 000000000000..1375aefae92f
--- /dev/null
+++ b/wasm/OutputSegment.h
@@ -0,0 +1,56 @@
+//===- OutputSegment.h ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_WASM_OUTPUT_SEGMENT_H
+#define LLD_WASM_OUTPUT_SEGMENT_H
+
+#include "InputSegment.h"
+#include "lld/Common/ErrorHandler.h"
+#include "llvm/Object/Wasm.h"
+
+namespace lld {
+namespace wasm {
+
+class InputSegment;
+
+class OutputSegment {
+public:
+ OutputSegment(StringRef N) : Name(N) {}
+
+ void addInputSegment(InputSegment *Segment) {
+ Alignment = std::max(Alignment, Segment->getAlignment());
+ InputSegments.push_back(Segment);
+ Size = llvm::alignTo(Size, Segment->getAlignment());
+ Segment->setOutputSegment(this, Size);
+ Size += Segment->getSize();
+ }
+
+ uint32_t getSectionOffset() const { return SectionOffset; }
+
+ void setSectionOffset(uint32_t Offset) { SectionOffset = Offset; }
+
+ StringRef Name;
+ uint32_t Alignment = 0;
+ uint32_t StartVA = 0;
+ std::vector<const InputSegment *> InputSegments;
+
+ // Sum of the size of the all the input segments
+ uint32_t Size = 0;
+
+ // Segment header
+ std::string Header;
+
+private:
+ uint32_t SectionOffset = 0;
+};
+
+} // namespace wasm
+} // namespace lld
+
+#endif // LLD_WASM_OUTPUT_SEGMENT_H
diff --git a/wasm/SymbolTable.cpp b/wasm/SymbolTable.cpp
new file mode 100644
index 000000000000..d9a6fa1f04f5
--- /dev/null
+++ b/wasm/SymbolTable.cpp
@@ -0,0 +1,245 @@
+//===- SymbolTable.cpp ----------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolTable.h"
+
+#include "Config.h"
+#include "WriterUtils.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+
+#include <unordered_set>
+
+#define DEBUG_TYPE "lld"
+
+using namespace llvm;
+using namespace lld;
+using namespace lld::wasm;
+
+SymbolTable *lld::wasm::Symtab;
+
+void SymbolTable::addFile(InputFile *File) {
+ log("Processing: " + toString(File));
+ File->parse();
+
+ if (auto *F = dyn_cast<ObjFile>(File))
+ ObjectFiles.push_back(F);
+}
+
+void SymbolTable::reportRemainingUndefines() {
+ std::unordered_set<Symbol *> Undefs;
+ for (Symbol *Sym : SymVector) {
+ if (Sym->isUndefined() && !Sym->isWeak() &&
+ Config->AllowUndefinedSymbols.count(Sym->getName()) == 0) {
+ Undefs.insert(Sym);
+ }
+ }
+
+ if (Undefs.empty())
+ return;
+
+ for (ObjFile *File : ObjectFiles)
+ for (Symbol *Sym : File->getSymbols())
+ if (Undefs.count(Sym))
+ error(toString(File) + ": undefined symbol: " + toString(*Sym));
+
+ for (Symbol *Sym : Undefs)
+ if (!Sym->getFile())
+ error("undefined symbol: " + toString(*Sym));
+}
+
+Symbol *SymbolTable::find(StringRef Name) {
+ auto It = SymMap.find(CachedHashStringRef(Name));
+ if (It == SymMap.end())
+ return nullptr;
+ return It->second;
+}
+
+std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) {
+ Symbol *&Sym = SymMap[CachedHashStringRef(Name)];
+ if (Sym)
+ return {Sym, false};
+ Sym = make<Symbol>(Name, false);
+ SymVector.emplace_back(Sym);
+ return {Sym, true};
+}
+
+void SymbolTable::reportDuplicate(Symbol *Existing, InputFile *NewFile) {
+ error("duplicate symbol: " + toString(*Existing) + "\n>>> defined in " +
+ toString(Existing->getFile()) + "\n>>> defined in " +
+ toString(NewFile));
+}
+
+// Get the signature for a given function symbol, either by looking
+// it up in function sections (for defined functions), of the imports section
+// (for imported functions).
+static const WasmSignature *getFunctionSig(const ObjFile &Obj,
+ const WasmSymbol &Sym) {
+ DEBUG(dbgs() << "getFunctionSig: " << Sym.Name << "\n");
+ const WasmObjectFile *WasmObj = Obj.getWasmObj();
+ uint32_t FunctionType;
+ if (Obj.isImportedFunction(Sym.ElementIndex)) {
+ const WasmImport &Import = WasmObj->imports()[Sym.ImportIndex];
+ FunctionType = Import.SigIndex;
+ } else {
+ uint32_t FuntionIndex = Sym.ElementIndex - Obj.NumFunctionImports();
+ FunctionType = WasmObj->functionTypes()[FuntionIndex];
+ }
+ return &WasmObj->types()[FunctionType];
+}
+
+// Check the type of new symbol matches that of the symbol is replacing.
+// For functions this can also involve verifying that the signatures match.
+static void checkSymbolTypes(const Symbol &Existing, const InputFile &F,
+ const WasmSymbol &New,
+ const WasmSignature *NewSig) {
+ if (Existing.isLazy())
+ return;
+
+ bool NewIsFunction = New.Type == WasmSymbol::SymbolType::FUNCTION_EXPORT ||
+ New.Type == WasmSymbol::SymbolType::FUNCTION_IMPORT;
+
+ // First check the symbol types match (i.e. either both are function
+ // symbols or both are data symbols).
+ if (Existing.isFunction() != NewIsFunction) {
+ error("symbol type mismatch: " + New.Name + "\n>>> defined as " +
+ (Existing.isFunction() ? "Function" : "Global") + " in " +
+ toString(Existing.getFile()) + "\n>>> defined as " +
+ (NewIsFunction ? "Function" : "Global") + " in " + F.getName());
+ return;
+ }
+
+ // For function symbols, optionally check the function signature matches too.
+ if (!NewIsFunction || !Config->CheckSignatures)
+ return;
+ // Skip the signature check if the existing function has no signature (e.g.
+ // if it is an undefined symbol generated by --undefined command line flag).
+ if (!Existing.hasFunctionType())
+ return;
+
+ DEBUG(dbgs() << "checkSymbolTypes: " << New.Name << "\n");
+ assert(NewSig);
+
+ const WasmSignature &OldSig = Existing.getFunctionType();
+ if (*NewSig == OldSig)
+ return;
+
+ error("function signature mismatch: " + New.Name + "\n>>> defined as " +
+ toString(OldSig) + " in " + toString(Existing.getFile()) +
+ "\n>>> defined as " + toString(*NewSig) + " in " + F.getName());
+}
+
+Symbol *SymbolTable::addDefinedGlobal(StringRef Name) {
+ DEBUG(dbgs() << "addDefinedGlobal: " << Name << "\n");
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(Name);
+ if (WasInserted)
+ S->update(Symbol::DefinedGlobalKind);
+ else if (!S->isGlobal())
+ error("symbol type mismatch: " + Name);
+ return S;
+}
+
+Symbol *SymbolTable::addDefined(InputFile *F, const WasmSymbol *Sym,
+ const InputSegment *Segment) {
+ DEBUG(dbgs() << "addDefined: " << Sym->Name << "\n");
+ Symbol *S;
+ bool WasInserted;
+ Symbol::Kind Kind = Symbol::DefinedFunctionKind;
+ const WasmSignature *NewSig = nullptr;
+ if (Sym->Type == WasmSymbol::SymbolType::GLOBAL_EXPORT)
+ Kind = Symbol::DefinedGlobalKind;
+ else
+ NewSig = getFunctionSig(*cast<ObjFile>(F), *Sym);
+
+ std::tie(S, WasInserted) = insert(Sym->Name);
+ if (WasInserted) {
+ S->update(Kind, F, Sym, Segment, NewSig);
+ } else if (S->isLazy()) {
+ // The existing symbol is lazy. Replace it without checking types since
+ // lazy symbols don't have any type information.
+ DEBUG(dbgs() << "replacing existing lazy symbol: " << Sym->Name << "\n");
+ S->update(Kind, F, Sym, Segment, NewSig);
+ } else if (!S->isDefined()) {
+ // The existing symbol table entry is undefined. The new symbol replaces
+ // it, after checking the type matches
+ DEBUG(dbgs() << "resolving existing undefined symbol: " << Sym->Name
+ << "\n");
+ checkSymbolTypes(*S, *F, *Sym, NewSig);
+ S->update(Kind, F, Sym, Segment, NewSig);
+ } else if (Sym->isWeak()) {
+ // the new symbol is weak we can ignore it
+ DEBUG(dbgs() << "existing symbol takes precedence\n");
+ } else if (S->isWeak()) {
+ // the new symbol is not weak and the existing symbol is, so we replace
+ // it
+ DEBUG(dbgs() << "replacing existing weak symbol\n");
+ checkSymbolTypes(*S, *F, *Sym, NewSig);
+ S->update(Kind, F, Sym, Segment, NewSig);
+ } else {
+ // neither symbol is week. They conflict.
+ reportDuplicate(S, F);
+ }
+ return S;
+}
+
+Symbol *SymbolTable::addUndefinedFunction(StringRef Name,
+ const WasmSignature *Type) {
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(Name);
+ if (WasInserted) {
+ S->update(Symbol::UndefinedFunctionKind, nullptr, nullptr, nullptr, Type);
+ } else if (!S->isFunction()) {
+ error("symbol type mismatch: " + Name);
+ }
+ return S;
+}
+
+Symbol *SymbolTable::addUndefined(InputFile *F, const WasmSymbol *Sym) {
+ DEBUG(dbgs() << "addUndefined: " << Sym->Name << "\n");
+ Symbol *S;
+ bool WasInserted;
+ Symbol::Kind Kind = Symbol::UndefinedFunctionKind;
+ const WasmSignature *NewSig = nullptr;
+ if (Sym->Type == WasmSymbol::SymbolType::GLOBAL_IMPORT)
+ Kind = Symbol::UndefinedGlobalKind;
+ else
+ NewSig = getFunctionSig(*cast<ObjFile>(F), *Sym);
+ std::tie(S, WasInserted) = insert(Sym->Name);
+ if (WasInserted) {
+ S->update(Kind, F, Sym, nullptr, NewSig);
+ } else if (S->isLazy()) {
+ DEBUG(dbgs() << "resolved by existing lazy\n");
+ auto *AF = cast<ArchiveFile>(S->getFile());
+ AF->addMember(&S->getArchiveSymbol());
+ } else if (S->isDefined()) {
+ DEBUG(dbgs() << "resolved by existing\n");
+ checkSymbolTypes(*S, *F, *Sym, NewSig);
+ }
+ return S;
+}
+
+void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol *Sym) {
+ DEBUG(dbgs() << "addLazy: " << Sym->getName() << "\n");
+ StringRef Name = Sym->getName();
+ Symbol *S;
+ bool WasInserted;
+ std::tie(S, WasInserted) = insert(Name);
+ if (WasInserted) {
+ S->update(Symbol::LazyKind, F);
+ S->setArchiveSymbol(*Sym);
+ } else if (S->isUndefined()) {
+ // There is an existing undefined symbol. The can load from the
+ // archive.
+ DEBUG(dbgs() << "replacing existing undefined\n");
+ F->addMember(Sym);
+ }
+}
diff --git a/wasm/SymbolTable.h b/wasm/SymbolTable.h
new file mode 100644
index 000000000000..e1e7da120b93
--- /dev/null
+++ b/wasm/SymbolTable.h
@@ -0,0 +1,72 @@
+//===- SymbolTable.h --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_WASM_SYMBOL_TABLE_H
+#define LLD_WASM_SYMBOL_TABLE_H
+
+#include "InputFiles.h"
+#include "Symbols.h"
+
+#include "llvm/ADT/CachedHashString.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/Support/raw_ostream.h"
+
+using llvm::object::WasmSymbol;
+using llvm::wasm::WasmSignature;
+
+namespace lld {
+namespace wasm {
+
+class InputSegment;
+
+// SymbolTable is a bucket of all known symbols, including defined,
+// undefined, or lazy symbols (the last one is symbols in archive
+// files whose archive members are not yet loaded).
+//
+// We put all symbols of all files to a SymbolTable, and the
+// SymbolTable selects the "best" symbols if there are name
+// conflicts. For example, obviously, a defined symbol is better than
+// an undefined symbol. Or, if there's a conflict between a lazy and a
+// undefined, it'll read an archive member to read a real definition
+// to replace the lazy symbol. The logic is implemented in the
+// add*() functions, which are called by input files as they are parsed.
+// There is one add* function per symbol type.
+class SymbolTable {
+public:
+ void addFile(InputFile *File);
+
+ std::vector<ObjFile *> ObjectFiles;
+ std::vector<Symbol *> SyntheticSymbols;
+
+ void reportDuplicate(Symbol *Existing, InputFile *NewFile);
+ void reportRemainingUndefines();
+
+ ArrayRef<Symbol *> getSymbols() const { return SymVector; }
+ Symbol *find(StringRef Name);
+
+ Symbol *addDefined(InputFile *F, const WasmSymbol *Sym,
+ const InputSegment *Segment = nullptr);
+ Symbol *addUndefined(InputFile *F, const WasmSymbol *Sym);
+ Symbol *addUndefinedFunction(StringRef Name, const WasmSignature *Type);
+ Symbol *addDefinedGlobal(StringRef Name);
+ void addLazy(ArchiveFile *F, const Archive::Symbol *Sym);
+
+private:
+ std::pair<Symbol *, bool> insert(StringRef Name);
+
+ llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> SymMap;
+ std::vector<Symbol *> SymVector;
+};
+
+extern SymbolTable *Symtab;
+
+} // namespace wasm
+} // namespace lld
+
+#endif
diff --git a/wasm/Symbols.cpp b/wasm/Symbols.cpp
new file mode 100644
index 000000000000..6bf5459c2663
--- /dev/null
+++ b/wasm/Symbols.cpp
@@ -0,0 +1,114 @@
+//===- Symbols.cpp --------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Symbols.h"
+
+#include "Config.h"
+#include "InputFiles.h"
+#include "InputSegment.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Strings.h"
+
+#define DEBUG_TYPE "lld"
+
+using namespace llvm;
+using namespace lld;
+using namespace lld::wasm;
+
+uint32_t Symbol::getGlobalIndex() const {
+ assert(!Sym->isFunction());
+ return Sym->ElementIndex;
+}
+
+uint32_t Symbol::getFunctionIndex() const {
+ assert(Sym->isFunction());
+ return Sym->ElementIndex;
+}
+
+const WasmSignature &Symbol::getFunctionType() const {
+ assert(FunctionType != nullptr);
+ return *FunctionType;
+}
+
+uint32_t Symbol::getVirtualAddress() const {
+ assert(isGlobal());
+ DEBUG(dbgs() << "getVirtualAddress: " << getName() << "\n");
+ if (isUndefined())
+ return UINT32_MAX;
+ if (VirtualAddress.hasValue())
+ return VirtualAddress.getValue();
+
+ assert(Sym != nullptr);
+ ObjFile *Obj = cast<ObjFile>(File);
+ const WasmGlobal &Global =
+ Obj->getWasmObj()->globals()[getGlobalIndex() - Obj->NumGlobalImports()];
+ assert(Global.Type == llvm::wasm::WASM_TYPE_I32);
+ assert(Segment);
+ return Segment->translateVA(Global.InitExpr.Value.Int32);
+}
+
+uint32_t Symbol::getOutputIndex() const {
+ if (isUndefined() && isWeak())
+ return 0;
+ return OutputIndex.getValue();
+}
+
+void Symbol::setVirtualAddress(uint32_t Value) {
+ DEBUG(dbgs() << "setVirtualAddress " << Name << " -> " << Value << "\n");
+ assert(!VirtualAddress.hasValue());
+ VirtualAddress = Value;
+}
+
+void Symbol::setOutputIndex(uint32_t Index) {
+ DEBUG(dbgs() << "setOutputIndex " << Name << " -> " << Index << "\n");
+ assert(!OutputIndex.hasValue());
+ OutputIndex = Index;
+}
+
+void Symbol::setTableIndex(uint32_t Index) {
+ DEBUG(dbgs() << "setTableIndex " << Name << " -> " << Index << "\n");
+ assert(!TableIndex.hasValue());
+ TableIndex = Index;
+}
+
+void Symbol::update(Kind K, InputFile *F, const WasmSymbol *WasmSym,
+ const InputSegment *Seg, const WasmSignature *Sig) {
+ SymbolKind = K;
+ File = F;
+ Sym = WasmSym;
+ Segment = Seg;
+ FunctionType = Sig;
+}
+
+bool Symbol::isWeak() const { return Sym && Sym->isWeak(); }
+
+bool Symbol::isHidden() const { return Sym && Sym->isHidden(); }
+
+std::string lld::toString(const wasm::Symbol &Sym) {
+ if (Config->Demangle)
+ if (Optional<std::string> S = demangleItanium(Sym.getName()))
+ return "`" + *S + "'";
+ return Sym.getName();
+}
+
+std::string lld::toString(wasm::Symbol::Kind Kind) {
+ switch (Kind) {
+ case wasm::Symbol::DefinedFunctionKind:
+ return "DefinedFunction";
+ case wasm::Symbol::DefinedGlobalKind:
+ return "DefinedGlobal";
+ case wasm::Symbol::UndefinedFunctionKind:
+ return "UndefinedFunction";
+ case wasm::Symbol::UndefinedGlobalKind:
+ return "UndefinedGlobal";
+ case wasm::Symbol::LazyKind:
+ return "LazyKind";
+ }
+ llvm_unreachable("Invalid symbol kind!");
+}
diff --git a/wasm/Symbols.h b/wasm/Symbols.h
new file mode 100644
index 000000000000..8194bcaca383
--- /dev/null
+++ b/wasm/Symbols.h
@@ -0,0 +1,128 @@
+//===- Symbols.h ------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_WASM_SYMBOLS_H
+#define LLD_WASM_SYMBOLS_H
+
+#include "lld/Common/LLVM.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/Wasm.h"
+
+using llvm::object::Archive;
+using llvm::object::WasmSymbol;
+using llvm::wasm::WasmExport;
+using llvm::wasm::WasmImport;
+using llvm::wasm::WasmSignature;
+
+namespace lld {
+namespace wasm {
+
+class InputFile;
+class InputSegment;
+
+class Symbol {
+public:
+ enum Kind {
+ DefinedFunctionKind,
+ DefinedGlobalKind,
+
+ LazyKind,
+ UndefinedFunctionKind,
+ UndefinedGlobalKind,
+
+ LastDefinedKind = DefinedGlobalKind,
+ InvalidKind,
+ };
+
+ Symbol(StringRef Name, bool IsLocal)
+ : WrittenToSymtab(0), WrittenToNameSec(0), IsLocal(IsLocal), Name(Name) {}
+
+ Kind getKind() const { return SymbolKind; }
+
+ bool isLazy() const { return SymbolKind == LazyKind; }
+ bool isDefined() const { return SymbolKind <= LastDefinedKind; }
+ bool isUndefined() const {
+ return SymbolKind == UndefinedGlobalKind ||
+ SymbolKind == UndefinedFunctionKind;
+ }
+ bool isFunction() const {
+ return SymbolKind == DefinedFunctionKind ||
+ SymbolKind == UndefinedFunctionKind;
+ }
+ bool isGlobal() const { return !isFunction(); }
+ bool isLocal() const { return IsLocal; }
+ bool isWeak() const;
+ bool isHidden() const;
+
+ // Returns the symbol name.
+ StringRef getName() const { return Name; }
+
+ // Returns the file from which this symbol was created.
+ InputFile *getFile() const { return File; }
+
+ uint32_t getGlobalIndex() const;
+ uint32_t getFunctionIndex() const;
+
+ bool hasFunctionType() const { return FunctionType != nullptr; }
+ const WasmSignature &getFunctionType() const;
+ uint32_t getOutputIndex() const;
+ uint32_t getTableIndex() const { return TableIndex.getValue(); }
+
+ // Returns the virtual address of a defined global.
+ // Only works for globals, not functions.
+ uint32_t getVirtualAddress() const;
+
+ // Set the output index of the symbol (in the function or global index
+ // space of the output object.
+ void setOutputIndex(uint32_t Index);
+
+ // Returns true if a table index has been set for this symbol
+ bool hasTableIndex() const { return TableIndex.hasValue(); }
+
+ // Set the table index of the symbol
+ void setTableIndex(uint32_t Index);
+
+ void setVirtualAddress(uint32_t VA);
+
+ void update(Kind K, InputFile *F = nullptr, const WasmSymbol *Sym = nullptr,
+ const InputSegment *Segment = nullptr,
+ const WasmSignature *Sig = nullptr);
+
+ void setArchiveSymbol(const Archive::Symbol &Sym) { ArchiveSymbol = Sym; }
+ const Archive::Symbol &getArchiveSymbol() { return ArchiveSymbol; }
+
+ // This bit is used by Writer::writeNameSection() to prevent
+ // symbols from being written to the symbol table more than once.
+ unsigned WrittenToSymtab : 1;
+ unsigned WrittenToNameSec : 1;
+
+protected:
+ unsigned IsLocal : 1;
+
+ StringRef Name;
+ Archive::Symbol ArchiveSymbol = {nullptr, 0, 0};
+ Kind SymbolKind = InvalidKind;
+ InputFile *File = nullptr;
+ const WasmSymbol *Sym = nullptr;
+ const InputSegment *Segment = nullptr;
+ llvm::Optional<uint32_t> OutputIndex;
+ llvm::Optional<uint32_t> TableIndex;
+ llvm::Optional<uint32_t> VirtualAddress;
+ const WasmSignature *FunctionType;
+};
+
+} // namespace wasm
+
+// Returns a symbol name for an error message.
+std::string toString(const wasm::Symbol &Sym);
+std::string toString(wasm::Symbol::Kind Kind);
+
+} // namespace lld
+
+#endif
diff --git a/wasm/Writer.cpp b/wasm/Writer.cpp
new file mode 100644
index 000000000000..61ac54a3e4b3
--- /dev/null
+++ b/wasm/Writer.cpp
@@ -0,0 +1,724 @@
+//===- Writer.cpp ---------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Writer.h"
+
+#include "Config.h"
+#include "OutputSections.h"
+#include "OutputSegment.h"
+#include "SymbolTable.h"
+#include "WriterUtils.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+#include "lld/Common/Threads.h"
+#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/LEB128.h"
+
+#include <cstdarg>
+
+#define DEBUG_TYPE "lld"
+
+using namespace llvm;
+using namespace llvm::wasm;
+using namespace lld;
+using namespace lld::wasm;
+
+static constexpr int kStackAlignment = 16;
+
+namespace {
+
+// Traits for using WasmSignature in a DenseMap.
+struct WasmSignatureDenseMapInfo {
+ static WasmSignature getEmptyKey() {
+ WasmSignature Sig;
+ Sig.ReturnType = 1;
+ return Sig;
+ }
+ static WasmSignature getTombstoneKey() {
+ WasmSignature Sig;
+ Sig.ReturnType = 2;
+ return Sig;
+ }
+ static unsigned getHashValue(const WasmSignature &Sig) {
+ uintptr_t Value = 0;
+ Value += DenseMapInfo<int32_t>::getHashValue(Sig.ReturnType);
+ for (int32_t Param : Sig.ParamTypes)
+ Value += DenseMapInfo<int32_t>::getHashValue(Param);
+ return Value;
+ }
+ static bool isEqual(const WasmSignature &LHS, const WasmSignature &RHS) {
+ return LHS == RHS;
+ }
+};
+
+// The writer writes a SymbolTable result to a file.
+class Writer {
+public:
+ void run();
+
+private:
+ void openFile();
+
+ uint32_t getTypeIndex(const WasmSignature &Sig);
+ void assignSymbolIndexes();
+ void calculateImports();
+ void calculateOffsets();
+ void calculateTypes();
+ void createOutputSegments();
+ void layoutMemory();
+ void createHeader();
+ void createSections();
+ SyntheticSection *createSyntheticSection(uint32_t Type,
+ std::string Name = "");
+
+ // Builtin sections
+ void createTypeSection();
+ void createFunctionSection();
+ void createTableSection();
+ void createGlobalSection();
+ void createExportSection();
+ void createImportSection();
+ void createMemorySection();
+ void createElemSection();
+ void createStartSection();
+ void createCodeSection();
+ void createDataSection();
+
+ // Custom sections
+ void createRelocSections();
+ void createLinkingSection();
+ void createNameSection();
+
+ void writeHeader();
+ void writeSections();
+
+ uint64_t FileSize = 0;
+ uint32_t DataSize = 0;
+ uint32_t NumFunctions = 0;
+ uint32_t NumMemoryPages = 0;
+ uint32_t InitialTableOffset = 0;
+
+ std::vector<const WasmSignature *> Types;
+ DenseMap<WasmSignature, int32_t, WasmSignatureDenseMapInfo> TypeIndices;
+ std::vector<const Symbol *> FunctionImports;
+ std::vector<const Symbol *> GlobalImports;
+ std::vector<const Symbol *> DefinedGlobals;
+ std::vector<const Symbol *> IndirectFunctions;
+
+ // Elements that are used to construct the final output
+ std::string Header;
+ std::vector<OutputSection *> OutputSections;
+
+ std::unique_ptr<FileOutputBuffer> Buffer;
+
+ std::vector<OutputSegment *> Segments;
+ llvm::SmallDenseMap<StringRef, OutputSegment *> SegmentMap;
+};
+
+} // anonymous namespace
+
+static void debugPrint(const char *fmt, ...) {
+ if (!errorHandler().Verbose)
+ return;
+ fprintf(stderr, "lld: ");
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+void Writer::createImportSection() {
+ uint32_t NumImports = FunctionImports.size() + GlobalImports.size();
+ if (Config->ImportMemory)
+ ++NumImports;
+
+ if (NumImports == 0)
+ return;
+
+ SyntheticSection *Section = createSyntheticSection(WASM_SEC_IMPORT);
+ raw_ostream &OS = Section->getStream();
+
+ writeUleb128(OS, NumImports, "import count");
+
+ for (const Symbol *Sym : FunctionImports) {
+ WasmImport Import;
+ Import.Module = "env";
+ Import.Field = Sym->getName();
+ Import.Kind = WASM_EXTERNAL_FUNCTION;
+ assert(TypeIndices.count(Sym->getFunctionType()) > 0);
+ Import.SigIndex = TypeIndices.lookup(Sym->getFunctionType());
+ writeImport(OS, Import);
+ }
+
+ if (Config->ImportMemory) {
+ WasmImport Import;
+ Import.Module = "env";
+ Import.Field = "memory";
+ Import.Kind = WASM_EXTERNAL_MEMORY;
+ Import.Memory.Flags = 0;
+ Import.Memory.Initial = NumMemoryPages;
+ writeImport(OS, Import);
+ }
+
+ for (const Symbol *Sym : GlobalImports) {
+ WasmImport Import;
+ Import.Module = "env";
+ Import.Field = Sym->getName();
+ Import.Kind = WASM_EXTERNAL_GLOBAL;
+ Import.Global.Mutable = false;
+ Import.Global.Type = WASM_TYPE_I32; // Sym->getGlobalType();
+ writeImport(OS, Import);
+ }
+}
+
+void Writer::createTypeSection() {
+ SyntheticSection *Section = createSyntheticSection(WASM_SEC_TYPE);
+ raw_ostream &OS = Section->getStream();
+ writeUleb128(OS, Types.size(), "type count");
+ for (const WasmSignature *Sig : Types) {
+ writeSig(OS, *Sig);
+ }
+}
+
+void Writer::createFunctionSection() {
+ if (!NumFunctions)
+ return;
+
+ SyntheticSection *Section = createSyntheticSection(WASM_SEC_FUNCTION);
+ raw_ostream &OS = Section->getStream();
+
+ writeUleb128(OS, NumFunctions, "function count");
+ for (ObjFile *File : Symtab->ObjectFiles) {
+ for (uint32_t Sig : File->getWasmObj()->functionTypes()) {
+ writeUleb128(OS, File->relocateTypeIndex(Sig), "sig index");
+ }
+ }
+}
+
+void Writer::createMemorySection() {
+ if (Config->ImportMemory)
+ return;
+
+ SyntheticSection *Section = createSyntheticSection(WASM_SEC_MEMORY);
+ raw_ostream &OS = Section->getStream();
+
+ writeUleb128(OS, 1, "memory count");
+ writeUleb128(OS, 0, "memory limits flags");
+ writeUleb128(OS, NumMemoryPages, "initial pages");
+}
+
+void Writer::createGlobalSection() {
+ if (DefinedGlobals.empty())
+ return;
+
+ SyntheticSection *Section = createSyntheticSection(WASM_SEC_GLOBAL);
+ raw_ostream &OS = Section->getStream();
+
+ writeUleb128(OS, DefinedGlobals.size(), "global count");
+ for (const Symbol *Sym : DefinedGlobals) {
+ WasmGlobal Global;
+ Global.Type = WASM_TYPE_I32;
+ Global.Mutable = Sym == Config->StackPointerSymbol;
+ Global.InitExpr.Opcode = WASM_OPCODE_I32_CONST;
+ Global.InitExpr.Value.Int32 = Sym->getVirtualAddress();
+ writeGlobal(OS, Global);
+ }
+}
+
+void Writer::createTableSection() {
+ // Always output a table section, even if there are no indirect calls.
+ // There are two reasons for this:
+ // 1. For executables it is useful to have an empty table slot at 0
+ // which can be filled with a null function call handler.
+ // 2. If we don't do this, any program that contains a call_indirect but
+ // no address-taken function will fail at validation time since it is
+ // a validation error to include a call_indirect instruction if there
+ // is not table.
+ uint32_t TableSize = InitialTableOffset + IndirectFunctions.size();
+
+ SyntheticSection *Section = createSyntheticSection(WASM_SEC_TABLE);
+ raw_ostream &OS = Section->getStream();
+
+ writeUleb128(OS, 1, "table count");
+ writeSleb128(OS, WASM_TYPE_ANYFUNC, "table type");
+ writeUleb128(OS, WASM_LIMITS_FLAG_HAS_MAX, "table flags");
+ writeUleb128(OS, TableSize, "table initial size");
+ writeUleb128(OS, TableSize, "table max size");
+}
+
+void Writer::createExportSection() {
+ bool ExportMemory = !Config->Relocatable && !Config->ImportMemory;
+ Symbol *EntrySym = Symtab->find(Config->Entry);
+ bool ExportEntry = !Config->Relocatable && EntrySym && EntrySym->isDefined();
+ bool ExportHidden = Config->EmitRelocs;
+
+ uint32_t NumExports = ExportMemory ? 1 : 0;
+
+ std::vector<const Symbol *> SymbolExports;
+ if (ExportEntry)
+ SymbolExports.emplace_back(EntrySym);
+
+ for (const Symbol *Sym : Symtab->getSymbols()) {
+ if (Sym->isUndefined() || Sym->isGlobal())
+ continue;
+ if (Sym->isHidden() && !ExportHidden)
+ continue;
+ if (ExportEntry && Sym == EntrySym)
+ continue;
+ SymbolExports.emplace_back(Sym);
+ }
+
+ for (const Symbol *Sym : DefinedGlobals) {
+ // Can't export the SP right now because it mutable and mutable globals
+ // connot be exported.
+ if (Sym == Config->StackPointerSymbol)
+ continue;
+ SymbolExports.emplace_back(Sym);
+ }
+
+ NumExports += SymbolExports.size();
+ if (!NumExports)
+ return;
+
+ SyntheticSection *Section = createSyntheticSection(WASM_SEC_EXPORT);
+ raw_ostream &OS = Section->getStream();
+
+ writeUleb128(OS, NumExports, "export count");
+
+ if (ExportMemory) {
+ WasmExport MemoryExport;
+ MemoryExport.Name = "memory";
+ MemoryExport.Kind = WASM_EXTERNAL_MEMORY;
+ MemoryExport.Index = 0;
+ writeExport(OS, MemoryExport);
+ }
+
+ for (const Symbol *Sym : SymbolExports) {
+ log("Export: " + Sym->getName());
+ WasmExport Export;
+ Export.Name = Sym->getName();
+ Export.Index = Sym->getOutputIndex();
+ if (Sym->isFunction())
+ Export.Kind = WASM_EXTERNAL_FUNCTION;
+ else
+ Export.Kind = WASM_EXTERNAL_GLOBAL;
+ writeExport(OS, Export);
+ }
+}
+
+void Writer::createStartSection() {}
+
+void Writer::createElemSection() {
+ if (IndirectFunctions.empty())
+ return;
+
+ SyntheticSection *Section = createSyntheticSection(WASM_SEC_ELEM);
+ raw_ostream &OS = Section->getStream();
+
+ writeUleb128(OS, 1, "segment count");
+ writeUleb128(OS, 0, "table index");
+ WasmInitExpr InitExpr;
+ InitExpr.Opcode = WASM_OPCODE_I32_CONST;
+ InitExpr.Value.Int32 = InitialTableOffset;
+ writeInitExpr(OS, InitExpr);
+ writeUleb128(OS, IndirectFunctions.size(), "elem count");
+
+ uint32_t TableIndex = InitialTableOffset;
+ for (const Symbol *Sym : IndirectFunctions) {
+ assert(Sym->getTableIndex() == TableIndex);
+ writeUleb128(OS, Sym->getOutputIndex(), "function index");
+ ++TableIndex;
+ }
+}
+
+void Writer::createCodeSection() {
+ if (!NumFunctions)
+ return;
+
+ log("createCodeSection");
+
+ auto Section = make<CodeSection>(NumFunctions, Symtab->ObjectFiles);
+ OutputSections.push_back(Section);
+}
+
+void Writer::createDataSection() {
+ if (!Segments.size())
+ return;
+
+ log("createDataSection");
+ auto Section = make<DataSection>(Segments);
+ OutputSections.push_back(Section);
+}
+
+// Create reloctions sections in the final output.
+// These are only created when relocatable output is requested.
+void Writer::createRelocSections() {
+ log("createRelocSections");
+ // Don't use iterator here since we are adding to OutputSection
+ size_t OrigSize = OutputSections.size();
+ for (size_t i = 0; i < OrigSize; i++) {
+ OutputSection *S = OutputSections[i];
+ const char *name;
+ uint32_t Count = S->numRelocations();
+ if (!Count)
+ continue;
+
+ if (S->Type == WASM_SEC_DATA)
+ name = "reloc.DATA";
+ else if (S->Type == WASM_SEC_CODE)
+ name = "reloc.CODE";
+ else
+ llvm_unreachable("relocations only support for code and data");
+
+ SyntheticSection *Section = createSyntheticSection(WASM_SEC_CUSTOM, name);
+ raw_ostream &OS = Section->getStream();
+ writeUleb128(OS, S->Type, "reloc section");
+ writeUleb128(OS, Count, "reloc count");
+ S->writeRelocations(OS);
+ }
+}
+
+// Create the custom "linking" section containing linker metadata.
+// This is only created when relocatable output is requested.
+void Writer::createLinkingSection() {
+ SyntheticSection *Section =
+ createSyntheticSection(WASM_SEC_CUSTOM, "linking");
+ raw_ostream &OS = Section->getStream();
+
+ SubSection DataSizeSubSection(WASM_DATA_SIZE);
+ writeUleb128(DataSizeSubSection.getStream(), DataSize, "data size");
+ DataSizeSubSection.finalizeContents();
+ DataSizeSubSection.writeToStream(OS);
+
+ if (Segments.size() && Config->Relocatable) {
+ SubSection SubSection(WASM_SEGMENT_INFO);
+ writeUleb128(SubSection.getStream(), Segments.size(), "num data segments");
+ for (const OutputSegment *S : Segments) {
+ writeStr(SubSection.getStream(), S->Name, "segment name");
+ writeUleb128(SubSection.getStream(), S->Alignment, "alignment");
+ writeUleb128(SubSection.getStream(), 0, "flags");
+ }
+ SubSection.finalizeContents();
+ SubSection.writeToStream(OS);
+ }
+}
+
+// Create the custom "name" section containing debug symbol names.
+void Writer::createNameSection() {
+ // Create an array of all function sorted by function index space
+ std::vector<const Symbol *> Names;
+
+ for (ObjFile *File : Symtab->ObjectFiles) {
+ Names.reserve(Names.size() + File->getSymbols().size());
+ for (Symbol *S : File->getSymbols()) {
+ if (!S->isFunction() || S->isWeak() || S->WrittenToNameSec)
+ continue;
+ S->WrittenToNameSec = true;
+ Names.emplace_back(S);
+ }
+ }
+
+ SyntheticSection *Section = createSyntheticSection(WASM_SEC_CUSTOM, "name");
+
+ std::sort(Names.begin(), Names.end(), [](const Symbol *A, const Symbol *B) {
+ return A->getOutputIndex() < B->getOutputIndex();
+ });
+
+ SubSection FunctionSubsection(WASM_NAMES_FUNCTION);
+ raw_ostream &OS = FunctionSubsection.getStream();
+ writeUleb128(OS, Names.size(), "name count");
+
+ // We have to iterate through the inputs twice so that all the imports
+ // appear first before any of the local function names.
+ for (const Symbol *S : Names) {
+ writeUleb128(OS, S->getOutputIndex(), "func index");
+ writeStr(OS, S->getName(), "symbol name");
+ }
+
+ FunctionSubsection.finalizeContents();
+ FunctionSubsection.writeToStream(Section->getStream());
+}
+
+void Writer::writeHeader() {
+ memcpy(Buffer->getBufferStart(), Header.data(), Header.size());
+}
+
+void Writer::writeSections() {
+ uint8_t *Buf = Buffer->getBufferStart();
+ parallelForEach(OutputSections, [Buf](OutputSection *S) { S->writeTo(Buf); });
+}
+
+// Fix the memory layout of the output binary. This assigns memory offsets
+// to each of the input data sections as well as the explicit stack region.
+void Writer::layoutMemory() {
+ uint32_t MemoryPtr = 0;
+ if (!Config->Relocatable) {
+ MemoryPtr = Config->GlobalBase;
+ debugPrint("mem: global base = %d\n", Config->GlobalBase);
+ }
+
+ createOutputSegments();
+
+ // Static data comes first
+ for (OutputSegment *Seg : Segments) {
+ MemoryPtr = alignTo(MemoryPtr, Seg->Alignment);
+ Seg->StartVA = MemoryPtr;
+ debugPrint("mem: %-10s offset=%-8d size=%-4d align=%d\n",
+ Seg->Name.str().c_str(), MemoryPtr, Seg->Size, Seg->Alignment);
+ MemoryPtr += Seg->Size;
+ }
+
+ DataSize = MemoryPtr;
+ if (!Config->Relocatable)
+ DataSize -= Config->GlobalBase;
+ debugPrint("mem: static data = %d\n", DataSize);
+
+ // Stack comes after static data
+ if (!Config->Relocatable) {
+ MemoryPtr = alignTo(MemoryPtr, kStackAlignment);
+ if (Config->ZStackSize != alignTo(Config->ZStackSize, kStackAlignment))
+ error("stack size must be " + Twine(kStackAlignment) + "-byte aligned");
+ debugPrint("mem: stack size = %d\n", Config->ZStackSize);
+ debugPrint("mem: stack base = %d\n", MemoryPtr);
+ MemoryPtr += Config->ZStackSize;
+ Config->StackPointerSymbol->setVirtualAddress(MemoryPtr);
+ debugPrint("mem: stack top = %d\n", MemoryPtr);
+ }
+
+ uint32_t MemSize = alignTo(MemoryPtr, WasmPageSize);
+ NumMemoryPages = MemSize / WasmPageSize;
+ debugPrint("mem: total pages = %d\n", NumMemoryPages);
+}
+
+SyntheticSection *Writer::createSyntheticSection(uint32_t Type,
+ std::string Name) {
+ auto Sec = make<SyntheticSection>(Type, Name);
+ log("createSection: " + toString(Sec));
+ OutputSections.push_back(Sec);
+ return Sec;
+}
+
+void Writer::createSections() {
+ // Known sections
+ createTypeSection();
+ createImportSection();
+ createFunctionSection();
+ createTableSection();
+ createMemorySection();
+ createGlobalSection();
+ createExportSection();
+ createStartSection();
+ createElemSection();
+ createCodeSection();
+ createDataSection();
+
+ // Custom sections
+ if (Config->EmitRelocs)
+ createRelocSections();
+ createLinkingSection();
+ if (!Config->StripDebug && !Config->StripAll)
+ createNameSection();
+
+ for (OutputSection *S : OutputSections) {
+ S->setOffset(FileSize);
+ S->finalizeContents();
+ FileSize += S->getSize();
+ }
+}
+
+void Writer::calculateOffsets() {
+ for (ObjFile *File : Symtab->ObjectFiles) {
+ const WasmObjectFile *WasmFile = File->getWasmObj();
+
+ // Function Index
+ File->FunctionIndexOffset =
+ FunctionImports.size() - File->NumFunctionImports() + NumFunctions;
+ NumFunctions += WasmFile->functions().size();
+
+ // Memory
+ if (WasmFile->memories().size() > 1)
+ fatal(File->getName() + ": contains more than one memory");
+ }
+}
+
+void Writer::calculateImports() {
+ for (Symbol *Sym : Symtab->getSymbols()) {
+ if (!Sym->isUndefined() || Sym->isWeak())
+ continue;
+
+ if (Sym->isFunction()) {
+ Sym->setOutputIndex(FunctionImports.size());
+ FunctionImports.push_back(Sym);
+ } else {
+ Sym->setOutputIndex(GlobalImports.size());
+ GlobalImports.push_back(Sym);
+ }
+ }
+}
+
+uint32_t Writer::getTypeIndex(const WasmSignature &Sig) {
+ auto Pair = TypeIndices.insert(std::make_pair(Sig, Types.size()));
+ if (Pair.second)
+ Types.push_back(&Sig);
+ return Pair.first->second;
+}
+
+void Writer::calculateTypes() {
+ for (ObjFile *File : Symtab->ObjectFiles) {
+ File->TypeMap.reserve(File->getWasmObj()->types().size());
+ for (const WasmSignature &Sig : File->getWasmObj()->types())
+ File->TypeMap.push_back(getTypeIndex(Sig));
+ }
+}
+
+void Writer::assignSymbolIndexes() {
+ uint32_t GlobalIndex = GlobalImports.size();
+
+ if (Config->StackPointerSymbol) {
+ DefinedGlobals.emplace_back(Config->StackPointerSymbol);
+ Config->StackPointerSymbol->setOutputIndex(GlobalIndex++);
+ }
+
+ if (Config->EmitRelocs)
+ DefinedGlobals.reserve(Symtab->getSymbols().size());
+
+ uint32_t TableIndex = InitialTableOffset;
+
+ for (ObjFile *File : Symtab->ObjectFiles) {
+ DEBUG(dbgs() << "assignSymbolIndexes: " << File->getName() << "\n");
+
+ for (Symbol *Sym : File->getSymbols()) {
+ // Assign indexes for symbols defined with this file.
+ if (!Sym->isDefined() || File != Sym->getFile())
+ continue;
+ if (Sym->isFunction()) {
+ auto *Obj = cast<ObjFile>(Sym->getFile());
+ Sym->setOutputIndex(Obj->FunctionIndexOffset +
+ Sym->getFunctionIndex());
+ } else if (Config->EmitRelocs) {
+ DefinedGlobals.emplace_back(Sym);
+ Sym->setOutputIndex(GlobalIndex++);
+ }
+ }
+
+ for (Symbol *Sym : File->getTableSymbols()) {
+ if (!Sym->hasTableIndex()) {
+ Sym->setTableIndex(TableIndex++);
+ IndirectFunctions.emplace_back(Sym);
+ }
+ }
+ }
+}
+
+static StringRef getOutputDataSegmentName(StringRef Name) {
+ if (Config->Relocatable)
+ return 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.", ".ARM.extab."}) {
+ StringRef Prefix = V.drop_back();
+ if (Name.startswith(V) || Name == Prefix)
+ return Prefix;
+ }
+
+ return Name;
+}
+
+void Writer::createOutputSegments() {
+ for (ObjFile *File : Symtab->ObjectFiles) {
+ for (InputSegment *Segment : File->Segments) {
+ StringRef Name = getOutputDataSegmentName(Segment->getName());
+ OutputSegment *&S = SegmentMap[Name];
+ if (S == nullptr) {
+ DEBUG(dbgs() << "new segment: " << Name << "\n");
+ S = make<OutputSegment>(Name);
+ Segments.push_back(S);
+ }
+ S->addInputSegment(Segment);
+ DEBUG(dbgs() << "added data: " << Name << ": " << S->Size << "\n");
+ }
+ }
+}
+
+void Writer::run() {
+ if (!Config->Relocatable)
+ InitialTableOffset = 1;
+
+ log("-- calculateTypes");
+ calculateTypes();
+ log("-- calculateImports");
+ calculateImports();
+ log("-- calculateOffsets");
+ calculateOffsets();
+
+ if (errorHandler().Verbose) {
+ log("Defined Functions: " + Twine(NumFunctions));
+ log("Defined Globals : " + Twine(DefinedGlobals.size()));
+ log("Function Imports : " + Twine(FunctionImports.size()));
+ log("Global Imports : " + Twine(GlobalImports.size()));
+ log("Total Imports : " +
+ Twine(FunctionImports.size() + GlobalImports.size()));
+ for (ObjFile *File : Symtab->ObjectFiles)
+ File->dumpInfo();
+ }
+
+ log("-- assignSymbolIndexes");
+ assignSymbolIndexes();
+ log("-- layoutMemory");
+ layoutMemory();
+
+ createHeader();
+ log("-- createSections");
+ createSections();
+
+ log("-- openFile");
+ openFile();
+ if (errorCount())
+ return;
+
+ writeHeader();
+
+ log("-- writeSections");
+ writeSections();
+ if (errorCount())
+ return;
+
+ if (Error E = Buffer->commit())
+ fatal("failed to write the output file: " + toString(std::move(E)));
+}
+
+// Open a result file.
+void Writer::openFile() {
+ log("writing: " + Config->OutputFile);
+ ::remove(Config->OutputFile.str().c_str());
+
+ Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
+ FileOutputBuffer::create(Config->OutputFile, FileSize,
+ FileOutputBuffer::F_executable);
+
+ if (!BufferOrErr)
+ error("failed to open " + Config->OutputFile + ": " +
+ toString(BufferOrErr.takeError()));
+ else
+ Buffer = std::move(*BufferOrErr);
+}
+
+void Writer::createHeader() {
+ raw_string_ostream OS(Header);
+ writeBytes(OS, WasmMagic, sizeof(WasmMagic), "wasm magic");
+ writeU32(OS, WasmVersion, "wasm version");
+ OS.flush();
+ FileSize += Header.size();
+}
+
+void lld::wasm::writeResult() { Writer().run(); }
diff --git a/wasm/Writer.h b/wasm/Writer.h
new file mode 100644
index 000000000000..a931ba9c29a8
--- /dev/null
+++ b/wasm/Writer.h
@@ -0,0 +1,21 @@
+//===- Writer.h -------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_WASM_WRITER_H
+#define LLD_WASM_WRITER_H
+
+namespace lld {
+namespace wasm {
+
+void writeResult();
+
+} // namespace wasm
+} // namespace lld
+
+#endif
diff --git a/wasm/WriterUtils.cpp b/wasm/WriterUtils.cpp
new file mode 100644
index 000000000000..5bdf0d2e3f65
--- /dev/null
+++ b/wasm/WriterUtils.cpp
@@ -0,0 +1,215 @@
+//===- WriterUtils.cpp ----------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "WriterUtils.h"
+
+#include "lld/Common/ErrorHandler.h"
+
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/EndianStream.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/LEB128.h"
+
+#define DEBUG_TYPE "lld"
+
+using namespace llvm;
+using namespace llvm::wasm;
+using namespace lld::wasm;
+
+static const char *valueTypeToString(int32_t Type) {
+ switch (Type) {
+ case WASM_TYPE_I32:
+ return "i32";
+ case WASM_TYPE_I64:
+ return "i64";
+ case WASM_TYPE_F32:
+ return "f32";
+ case WASM_TYPE_F64:
+ return "f64";
+ default:
+ llvm_unreachable("invalid value type");
+ }
+}
+
+namespace lld {
+
+void wasm::debugWrite(uint64_t offset, Twine msg) {
+ DEBUG(dbgs() << format(" | %08" PRIx64 ": ", offset) << msg << "\n");
+}
+
+void wasm::writeUleb128(raw_ostream &OS, uint32_t Number, const char *msg) {
+ if (msg)
+ debugWrite(OS.tell(), msg + formatv(" [{0:x}]", Number));
+ encodeULEB128(Number, OS);
+}
+
+void wasm::writeSleb128(raw_ostream &OS, int32_t Number, const char *msg) {
+ if (msg)
+ debugWrite(OS.tell(), msg + formatv(" [{0:x}]", Number));
+ encodeSLEB128(Number, OS);
+}
+
+void wasm::writeBytes(raw_ostream &OS, const char *bytes, size_t count,
+ const char *msg) {
+ if (msg)
+ debugWrite(OS.tell(), msg + formatv(" [data[{0}]]", count));
+ OS.write(bytes, count);
+}
+
+void wasm::writeStr(raw_ostream &OS, const StringRef String, const char *msg) {
+ if (msg)
+ debugWrite(OS.tell(),
+ msg + formatv(" [str[{0}]: {1}]", String.size(), String));
+ writeUleb128(OS, String.size(), nullptr);
+ writeBytes(OS, String.data(), String.size());
+}
+
+void wasm::writeU8(raw_ostream &OS, uint8_t byte, const char *msg) {
+ OS << byte;
+}
+
+void wasm::writeU32(raw_ostream &OS, uint32_t Number, const char *msg) {
+ debugWrite(OS.tell(), msg + formatv("[{0:x}]", Number));
+ support::endian::Writer<support::little>(OS).write(Number);
+}
+
+void wasm::writeValueType(raw_ostream &OS, int32_t Type, const char *msg) {
+ debugWrite(OS.tell(), msg + formatv("[type: {0}]", valueTypeToString(Type)));
+ writeSleb128(OS, Type, nullptr);
+}
+
+void wasm::writeSig(raw_ostream &OS, const WasmSignature &Sig) {
+ writeSleb128(OS, WASM_TYPE_FUNC, "signature type");
+ writeUleb128(OS, Sig.ParamTypes.size(), "param count");
+ for (int32_t ParamType : Sig.ParamTypes) {
+ writeValueType(OS, ParamType, "param type");
+ }
+ if (Sig.ReturnType == WASM_TYPE_NORESULT) {
+ writeUleb128(OS, 0, "result count");
+ } else {
+ writeUleb128(OS, 1, "result count");
+ writeValueType(OS, Sig.ReturnType, "result type");
+ }
+}
+
+void wasm::writeInitExpr(raw_ostream &OS, const WasmInitExpr &InitExpr) {
+ writeU8(OS, InitExpr.Opcode, "opcode");
+ switch (InitExpr.Opcode) {
+ case WASM_OPCODE_I32_CONST:
+ writeSleb128(OS, InitExpr.Value.Int32, "literal (i32)");
+ break;
+ case WASM_OPCODE_I64_CONST:
+ writeSleb128(OS, InitExpr.Value.Int64, "literal (i64)");
+ break;
+ case WASM_OPCODE_GET_GLOBAL:
+ writeUleb128(OS, InitExpr.Value.Global, "literal (global index)");
+ break;
+ default:
+ fatal("unknown opcode in init expr: " + Twine(InitExpr.Opcode));
+ }
+ writeU8(OS, WASM_OPCODE_END, "opcode:end");
+}
+
+void wasm::writeLimits(raw_ostream &OS, const WasmLimits &Limits) {
+ writeUleb128(OS, Limits.Flags, "limits flags");
+ writeUleb128(OS, Limits.Initial, "limits initial");
+ if (Limits.Flags & WASM_LIMITS_FLAG_HAS_MAX)
+ writeUleb128(OS, Limits.Maximum, "limits max");
+}
+
+void wasm::writeGlobal(raw_ostream &OS, const WasmGlobal &Global) {
+ writeValueType(OS, Global.Type, "global type");
+ writeUleb128(OS, Global.Mutable, "global mutable");
+ writeInitExpr(OS, Global.InitExpr);
+}
+
+void wasm::writeImport(raw_ostream &OS, const WasmImport &Import) {
+ writeStr(OS, Import.Module, "import module name");
+ writeStr(OS, Import.Field, "import field name");
+ writeU8(OS, Import.Kind, "import kind");
+ switch (Import.Kind) {
+ case WASM_EXTERNAL_FUNCTION:
+ writeUleb128(OS, Import.SigIndex, "import sig index");
+ break;
+ case WASM_EXTERNAL_GLOBAL:
+ writeValueType(OS, Import.Global.Type, "import global type");
+ writeUleb128(OS, Import.Global.Mutable, "import global mutable");
+ break;
+ case WASM_EXTERNAL_MEMORY:
+ writeLimits(OS, Import.Memory);
+ break;
+ default:
+ fatal("unsupported import type: " + Twine(Import.Kind));
+ }
+}
+
+void wasm::writeExport(raw_ostream &OS, const WasmExport &Export) {
+ writeStr(OS, Export.Name, "export name");
+ writeU8(OS, Export.Kind, "export kind");
+ switch (Export.Kind) {
+ case WASM_EXTERNAL_FUNCTION:
+ writeUleb128(OS, Export.Index, "function index");
+ break;
+ case WASM_EXTERNAL_GLOBAL:
+ writeUleb128(OS, Export.Index, "global index");
+ break;
+ case WASM_EXTERNAL_MEMORY:
+ writeUleb128(OS, Export.Index, "memory index");
+ break;
+ default:
+ fatal("unsupported export type: " + Twine(Export.Kind));
+ }
+}
+
+void wasm::writeReloc(raw_ostream &OS, const OutputRelocation &Reloc) {
+ writeUleb128(OS, Reloc.Reloc.Type, "reloc type");
+ writeUleb128(OS, Reloc.Reloc.Offset, "reloc offset");
+ writeUleb128(OS, Reloc.NewIndex, "reloc index");
+
+ switch (Reloc.Reloc.Type) {
+ case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
+ case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
+ case R_WEBASSEMBLY_MEMORY_ADDR_I32:
+ writeUleb128(OS, Reloc.Reloc.Addend, "reloc addend");
+ break;
+ default:
+ break;
+ }
+}
+
+} // namespace lld
+
+std::string lld::toString(ValType Type) {
+ switch (Type) {
+ case ValType::I32:
+ return "I32";
+ case ValType::I64:
+ return "I64";
+ case ValType::F32:
+ return "F32";
+ case ValType::F64:
+ return "F64";
+ }
+ llvm_unreachable("Invalid wasm::ValType");
+}
+
+std::string lld::toString(const WasmSignature &Sig) {
+ SmallString<128> S("(");
+ for (uint32_t Type : Sig.ParamTypes) {
+ if (S.size() != 1)
+ S += ", ";
+ S += toString(static_cast<ValType>(Type));
+ }
+ S += ") -> ";
+ if (Sig.ReturnType == WASM_TYPE_NORESULT)
+ S += "void";
+ else
+ S += toString(static_cast<ValType>(Sig.ReturnType));
+ return S.str();
+}
diff --git a/wasm/WriterUtils.h b/wasm/WriterUtils.h
new file mode 100644
index 000000000000..c1ed90793f78
--- /dev/null
+++ b/wasm/WriterUtils.h
@@ -0,0 +1,78 @@
+//===- WriterUtils.h --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_WASM_WRITERUTILS_H
+#define LLD_WASM_WRITERUTILS_H
+
+#include "llvm/ADT/Twine.h"
+#include "llvm/Object/Wasm.h"
+#include "llvm/Support/raw_ostream.h"
+
+using llvm::raw_ostream;
+
+// Needed for WasmSignatureDenseMapInfo
+inline bool operator==(const llvm::wasm::WasmSignature &LHS,
+ const llvm::wasm::WasmSignature &RHS) {
+ return LHS.ReturnType == RHS.ReturnType && LHS.ParamTypes == RHS.ParamTypes;
+}
+
+inline bool operator!=(const llvm::wasm::WasmSignature &LHS,
+ const llvm::wasm::WasmSignature &RHS) {
+ return !(LHS == RHS);
+}
+
+namespace lld {
+namespace wasm {
+
+struct OutputRelocation {
+ llvm::wasm::WasmRelocation Reloc;
+ uint32_t NewIndex;
+ uint32_t Value;
+};
+
+void debugWrite(uint64_t offset, llvm::Twine msg);
+
+void writeUleb128(raw_ostream &OS, uint32_t Number, const char *msg);
+
+void writeSleb128(raw_ostream &OS, int32_t Number, const char *msg);
+
+void writeBytes(raw_ostream &OS, const char *bytes, size_t count,
+ const char *msg = nullptr);
+
+void writeStr(raw_ostream &OS, const llvm::StringRef String,
+ const char *msg = nullptr);
+
+void writeU8(raw_ostream &OS, uint8_t byte, const char *msg);
+
+void writeU32(raw_ostream &OS, uint32_t Number, const char *msg);
+
+void writeValueType(raw_ostream &OS, int32_t Type, const char *msg);
+
+void writeSig(raw_ostream &OS, const llvm::wasm::WasmSignature &Sig);
+
+void writeInitExpr(raw_ostream &OS, const llvm::wasm::WasmInitExpr &InitExpr);
+
+void writeLimits(raw_ostream &OS, const llvm::wasm::WasmLimits &Limits);
+
+void writeGlobal(raw_ostream &OS, const llvm::wasm::WasmGlobal &Global);
+
+void writeImport(raw_ostream &OS, const llvm::wasm::WasmImport &Import);
+
+void writeExport(raw_ostream &OS, const llvm::wasm::WasmExport &Export);
+
+void writeReloc(raw_ostream &OS, const OutputRelocation &Reloc);
+
+} // namespace wasm
+
+std::string toString(const llvm::wasm::ValType Type);
+std::string toString(const llvm::wasm::WasmSignature &Sig);
+
+} // namespace lld
+
+#endif // LLD_WASM_WRITERUTILS_H