aboutsummaryrefslogtreecommitdiffstats
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";