From eb1ff93d02b5f17b6b409e83c6d9be585f4a04b3 Mon Sep 17 00:00:00 2001 From: Dimitry Andric Date: Mon, 18 Dec 2017 20:12:21 +0000 Subject: Vendor import of lld trunk r321017: https://llvm.org/svn/llvm-project/lld/trunk@321017 --- .arcconfig | 2 +- CMakeLists.txt | 8 +- CODE_OWNERS.TXT | 3 + COFF/CMakeLists.txt | 12 +- COFF/Chunks.cpp | 99 +- COFF/Chunks.h | 66 +- COFF/Config.h | 28 +- COFF/DLL.cpp | 65 +- COFF/Driver.cpp | 543 ++-- COFF/Driver.h | 43 +- COFF/DriverUtils.cpp | 216 +- COFF/Error.cpp | 114 - COFF/Error.h | 62 - COFF/ICF.cpp | 51 +- COFF/InputFiles.cpp | 394 +-- COFF/InputFiles.h | 70 +- COFF/LTO.cpp | 61 +- COFF/LTO.h | 3 +- COFF/MapFile.cpp | 10 +- COFF/MarkLive.cpp | 18 +- COFF/Memory.h | 52 - COFF/MinGW.cpp | 146 ++ COFF/MinGW.h | 38 + COFF/Options.td | 64 +- COFF/PDB.cpp | 553 +++- COFF/PDB.h | 7 +- COFF/Strings.cpp | 2 +- COFF/Strings.h | 2 +- COFF/SymbolTable.cpp | 273 +- COFF/SymbolTable.h | 35 +- COFF/Symbols.cpp | 30 +- COFF/Symbols.h | 146 +- COFF/Writer.cpp | 331 ++- COFF/Writer.h | 6 +- Common/Args.cpp | 62 + Common/CMakeLists.txt | 32 + Common/ErrorHandler.cpp | 118 + Common/Memory.cpp | 23 + Common/Reproduce.cpp | 66 + Common/Strings.cpp | 32 + Common/TargetOptionsCommandFlags.cpp | 32 + Common/Threads.cpp | 12 + Common/Version.cpp | 43 + ELF/AArch64ErrataFix.cpp | 648 +++++ ELF/AArch64ErrataFix.h | 52 + ELF/Arch/AArch64.cpp | 94 +- ELF/Arch/AMDGPU.cpp | 34 +- ELF/Arch/ARM.cpp | 189 +- ELF/Arch/AVR.cpp | 18 +- ELF/Arch/Mips.cpp | 463 +++- ELF/Arch/MipsArchTree.cpp | 54 +- ELF/Arch/PPC.cpp | 36 +- ELF/Arch/PPC64.cpp | 16 +- ELF/Arch/SPARCV9.cpp | 13 +- ELF/Arch/X86.cpp | 137 +- ELF/Arch/X86_64.cpp | 46 +- ELF/Bits.h | 35 + ELF/CMakeLists.txt | 17 +- ELF/Config.h | 40 +- ELF/Driver.cpp | 485 ++-- ELF/Driver.h | 5 +- ELF/DriverUtils.cpp | 68 +- ELF/EhFrame.cpp | 42 +- ELF/EhFrame.h | 6 +- ELF/Error.cpp | 116 - ELF/Error.h | 78 - ELF/Filesystem.cpp | 45 +- ELF/Filesystem.h | 3 +- ELF/GdbIndex.cpp | 88 +- ELF/GdbIndex.h | 89 +- ELF/ICF.cpp | 99 +- ELF/InputFiles.cpp | 541 ++-- ELF/InputFiles.h | 105 +- ELF/InputSection.cpp | 461 ++-- ELF/InputSection.h | 199 +- ELF/LTO.cpp | 63 +- ELF/LTO.h | 4 +- ELF/LinkerScript.cpp | 1381 ++++------ ELF/LinkerScript.h | 211 +- ELF/MapFile.cpp | 95 +- ELF/MapFile.h | 6 +- ELF/MarkLive.cpp | 191 +- ELF/Memory.h | 67 - ELF/Options.td | 177 +- ELF/OutputSections.cpp | 488 ++-- ELF/OutputSections.h | 109 +- ELF/Relocations.cpp | 1078 +++++--- ELF/Relocations.h | 64 +- ELF/ScriptLexer.cpp | 44 +- ELF/ScriptLexer.h | 3 +- ELF/ScriptParser.cpp | 302 ++- ELF/ScriptParser.h | 5 +- ELF/Strings.cpp | 31 +- ELF/Strings.h | 6 +- ELF/SymbolTable.cpp | 631 +++-- ELF/SymbolTable.h | 99 +- ELF/Symbols.cpp | 253 +- ELF/Symbols.h | 399 ++- ELF/SyntheticSections.cpp | 1632 +++++++----- ELF/SyntheticSections.h | 351 +-- ELF/Target.cpp | 34 +- ELF/Target.h | 111 +- ELF/Threads.h | 88 - ELF/Thunks.cpp | 237 +- ELF/Thunks.h | 18 +- ELF/Writer.cpp | 1609 ++++++------ ELF/Writer.h | 16 +- MinGW/CMakeLists.txt | 23 + MinGW/Driver.cpp | 247 ++ MinGW/Options.td | 68 + README.md | 12 +- cmake/modules/AddLLD.cmake | 12 +- docs/Driver.rst | 2 +- docs/NewLLD.rst | 139 +- docs/ReleaseNotes.rst | 169 +- docs/WebAssembly.rst | 36 + docs/_templates/indexsidebar.html | 2 +- docs/conf.py | 4 +- docs/index.rst | 55 +- docs/sphinx_intro.rst | 36 +- include/lld/Common/Args.h | 35 + include/lld/Common/Driver.h | 43 + include/lld/Common/ErrorHandler.h | 112 + include/lld/Common/LLVM.h | 83 + include/lld/Common/Memory.h | 60 + include/lld/Common/Reproduce.h | 39 + include/lld/Common/Strings.h | 23 + include/lld/Common/TargetOptionsCommandFlags.h | 21 + include/lld/Common/Threads.h | 86 + include/lld/Common/Version.h | 25 + include/lld/Common/Version.inc.in | 6 + include/lld/Config/Version.h | 25 - include/lld/Config/Version.inc.in | 6 - include/lld/Core/Atom.h | 2 +- include/lld/Core/DefinedAtom.h | 2 +- include/lld/Core/Error.h | 2 +- include/lld/Core/LLVM.h | 83 - include/lld/Core/LinkingContext.h | 4 +- include/lld/Core/PassManager.h | 2 +- include/lld/Core/Reader.h | 2 +- include/lld/Core/Reproduce.h | 39 - include/lld/Core/SymbolTable.h | 2 +- include/lld/Core/TargetOptionsCommandFlags.h | 20 - include/lld/Core/Writer.h | 2 +- include/lld/Driver/Driver.h | 33 - include/lld/ReaderWriter/YamlContext.h | 3 +- lib/CMakeLists.txt | 1 - lib/Config/CMakeLists.txt | 9 - lib/Config/Version.cpp | 43 - lib/Core/CMakeLists.txt | 2 - lib/Core/Reproduce.cpp | 66 - lib/Core/Resolver.cpp | 6 +- lib/Core/SymbolTable.cpp | 2 +- lib/Core/TargetOptionsCommandFlags.cpp | 32 - lib/Driver/CMakeLists.txt | 4 +- lib/Driver/DarwinLdDriver.cpp | 6 +- lib/ReaderWriter/CMakeLists.txt | 1 - lib/ReaderWriter/FileArchive.cpp | 2 +- lib/ReaderWriter/MachO/ArchHandler.h | 2 +- lib/ReaderWriter/MachO/CMakeLists.txt | 2 +- lib/ReaderWriter/MachO/CompactUnwindPass.cpp | 2 +- lib/ReaderWriter/MachO/FlatNamespaceFile.h | 2 + lib/ReaderWriter/MachO/GOTPass.cpp | 2 +- lib/ReaderWriter/MachO/MachOLinkingContext.cpp | 2 +- lib/ReaderWriter/MachO/MachONormalizedFile.h | 11 +- .../MachO/MachONormalizedFileBinaryReader.cpp | 7 +- .../MachO/MachONormalizedFileBinaryUtils.h | 2 +- .../MachO/MachONormalizedFileBinaryWriter.cpp | 11 +- .../MachO/MachONormalizedFileFromAtoms.cpp | 2 +- .../MachO/MachONormalizedFileToAtoms.cpp | 2 +- lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp | 8 +- lib/ReaderWriter/MachO/ObjCPass.cpp | 2 +- lib/ReaderWriter/MachO/ShimPass.cpp | 2 +- lib/ReaderWriter/MachO/StubsPass.cpp | 2 +- lib/ReaderWriter/YAML/ReaderWriterYAML.cpp | 6 +- test/CMakeLists.txt | 15 +- test/COFF/Inputs/alpha.ll | 9 + test/COFF/Inputs/beta.ll | 7 + test/COFF/Inputs/except_handler3.lib | Bin 0 -> 1364 bytes test/COFF/Inputs/far-arm-thumb-abs20.s | 2 + test/COFF/Inputs/gamma.ll | 14 + test/COFF/Inputs/library2-arm64.lib | Bin 0 -> 1720 bytes test/COFF/Inputs/library2.def | 3 + test/COFF/Inputs/locally-imported-def.s | 4 + test/COFF/Inputs/locally-imported-imp.s | 2 + test/COFF/Inputs/lto-cache.ll | 10 + test/COFF/Inputs/pdb-globals.yaml | 593 +++++ test/COFF/Inputs/pdb-hashes-1.yaml | 540 ++++ test/COFF/Inputs/pdb-hashes-2-missing.yaml | 321 +++ test/COFF/Inputs/pdb-hashes-2.yaml | 355 +++ test/COFF/Inputs/pdb-scopes-a.yaml | 116 +- test/COFF/Inputs/pdb-scopes-b.yaml | 100 +- test/COFF/Inputs/pdb_comdat_bar.yaml | 116 +- test/COFF/Inputs/pdb_comdat_main.yaml | 116 +- test/COFF/Inputs/pdb_lines_1.yaml | 118 +- test/COFF/Inputs/pdb_lines_2.yaml | 70 +- test/COFF/arm-thumb-branch20-error.s | 10 + test/COFF/arm64-dynamicbase.s | 8 + test/COFF/arm64-import2.test | 85 + test/COFF/arm64-relocs-imports.test | 153 +- test/COFF/armnt-blx23t.test | 2 +- test/COFF/armnt-branch24t.test | 2 +- test/COFF/armnt-dynamicbase.test | 3 + test/COFF/armnt-imports.test | 2 +- test/COFF/armnt-mov32t-exec.test | 2 +- test/COFF/armnt-movt32t.test | 2 +- test/COFF/common-alignment.test | 78 + test/COFF/ctors_dtors_priority.s | 30 + test/COFF/debug-dwarf.test | 19 + test/COFF/def-export-stdcall.s | 78 +- test/COFF/delayimports-armnt.yaml | 106 + test/COFF/delayimports32.test | 4 +- test/COFF/dllexport-mingw.s | 24 + test/COFF/driver.test | 3 + test/COFF/duplicate.test | 12 + test/COFF/entry-drectve.test | 24 + test/COFF/entry-inference.test | 8 +- test/COFF/export-all.s | 86 + test/COFF/export-arm64.yaml | 70 + test/COFF/export-armnt.yaml | 72 + test/COFF/export32.test | 5 + test/COFF/filename-casing.s | 14 + test/COFF/force.test | 7 +- test/COFF/guardcf.test | 6 + test/COFF/hello32.test | 6 +- test/COFF/icf-associative.test | 2 +- test/COFF/icf-executable.s | 18 + test/COFF/icf-simple.test | 27 +- test/COFF/icf-xdata.s | 86 + test/COFF/include.test | 13 + test/COFF/libpath.test | 12 +- test/COFF/linkrepro-manifest.test | 12 + test/COFF/linkrepro-pdb.test | 9 + test/COFF/linkrepro-res.test | 12 + test/COFF/loadcfg.test | 21 +- test/COFF/locally-imported-arm64.test | 61 + test/COFF/locally-imported-warn-multiple.s | 14 + test/COFF/locally-imported.test | 4 +- test/COFF/long-section-name.test | 23 +- test/COFF/lto-cache.ll | 21 + test/COFF/lto-opt-level.ll | 12 +- test/COFF/lto-reloc-model.ll | 19 + test/COFF/manifest.test | 16 +- test/COFF/manifestinput-error.test | 10 + test/COFF/manifestinput-nowarning.test | 11 + test/COFF/manifestinput.test | 4 +- test/COFF/msvclto-archive.ll | 6 +- test/COFF/msvclto-order.ll | 2 +- test/COFF/msvclto.ll | 2 +- test/COFF/nodefaultlib.test | 6 +- test/COFF/nopdb.test | 14 - test/COFF/options.test | 8 +- test/COFF/pdata-arm64.yaml | 87 + test/COFF/pdb-comdat.test | 43 +- test/COFF/pdb-diff.test | 17 +- test/COFF/pdb-global-gc.yaml | 22 +- test/COFF/pdb-global-hashes.test | 93 + test/COFF/pdb-globals.test | 42 + test/COFF/pdb-heapsite.yaml | 1561 +++++++++++ test/COFF/pdb-import-gc.yaml | 20 +- test/COFF/pdb-invalid-func-type.yaml | 2 +- test/COFF/pdb-lib.s | 6 +- test/COFF/pdb-linker-module.test | 38 +- test/COFF/pdb-none.test | 3 +- test/COFF/pdb-options.test | 2 +- test/COFF/pdb-procid-remapping.test | 29 + test/COFF/pdb-publics-import.test | 42 + test/COFF/pdb-safeseh.yaml | 11 +- test/COFF/pdb-same-name.test | 23 + test/COFF/pdb-scopes.test | 6 +- test/COFF/pdb-secrel-absolute.yaml | 11 +- test/COFF/pdb-source-lines.test | 35 +- test/COFF/pdb-symbol-types.yaml | 20 +- test/COFF/pdb-thunk.yaml | 2747 ++++++++++++++++++++ test/COFF/pdb-type-server-simple.test | 24 +- test/COFF/pdb.test | 160 +- test/COFF/reloc-arm.test | 16 +- test/COFF/reloc-discarded-dwarf.s | 2 + test/COFF/reloc-discarded-early.s | 8 + test/COFF/reloc-discarded-early2.s | 9 + test/COFF/reloc-discarded.s | 1 - test/COFF/responsefile.test | 20 +- test/COFF/rsds.test | 40 +- test/COFF/safeseh-md.s | 34 + test/COFF/safeseh.s | 15 +- test/COFF/section-size.s | 14 + test/COFF/seh-comdat.test | 66 + test/COFF/strtab-size.s | 216 ++ test/COFF/subsystem-drectve.test | 21 + test/COFF/symtab.test | 6 +- test/COFF/thinlto.ll | 2 +- test/COFF/wholearchive.s | 19 + test/COFF/wx.s | 17 + test/ELF/Inputs/amdgpu-kernel-0.s | 6 + test/ELF/Inputs/amdgpu-kernel-1.s | 6 + test/ELF/Inputs/amdgpu-kernel-2.o | Bin 0 -> 408 bytes test/ELF/Inputs/copy-rel-abs.s | 13 + test/ELF/Inputs/copy-rel-large.s | 4 + test/ELF/Inputs/copy-rel-pie.s | 1 + test/ELF/Inputs/corrupt-version-reference.so | Bin 0 -> 134272 bytes test/ELF/Inputs/dynamic-list-weak-archive.s | 2 + test/ELF/Inputs/eh-frame.s | 3 + test/ELF/Inputs/gc-sections-shared.s | 3 + test/ELF/Inputs/gc-sections-shared2.s | 3 + test/ELF/Inputs/local-symbol-in-dso.so | Bin 0 -> 5128 bytes test/ELF/Inputs/map-file5.s | 23 + test/ELF/Inputs/mips-micro.s | 12 + test/ELF/Inputs/shared3.s | 2 +- test/ELF/Inputs/undefined-error.s | 1 + test/ELF/Inputs/verdef-defaultver.s | 3 + test/ELF/Inputs/verneed.so.sh | 58 - test/ELF/Inputs/verneed1.s | 32 + test/ELF/Inputs/verneed1.so | Bin 2632 -> 0 bytes test/ELF/Inputs/verneed2.s | 5 + test/ELF/Inputs/verneed2.so | Bin 2200 -> 0 bytes test/ELF/Inputs/weak-undef-lazy.s | 3 + test/ELF/Inputs/wrap-no-real.s | 3 + test/ELF/Inputs/wrap-no-real2.s | 2 + test/ELF/Inputs/wrap.s | 5 +- test/ELF/aarch64-abs16.s | 2 +- test/ELF/aarch64-abs32.s | 2 +- test/ELF/aarch64-call26-error.s | 11 - test/ELF/aarch64-call26-thunk.s | 21 + test/ELF/aarch64-cortex-a53-843419-address.s | 180 ++ test/ELF/aarch64-cortex-a53-843419-cli.s | 10 + test/ELF/aarch64-cortex-a53-843419-large.s | 115 + test/ELF/aarch64-cortex-a53-843419-nopatch.s | 338 +++ test/ELF/aarch64-cortex-a53-843419-recognize.s | 563 ++++ test/ELF/aarch64-cortex-a53-843419-thunk.s | 57 + test/ELF/aarch64-gnu-ifunc-plt.s | 2 +- test/ELF/aarch64-gnu-ifunc.s | 2 +- test/ELF/aarch64-got-reloc.s | 4 +- test/ELF/aarch64-got-relocations.s | 2 +- test/ELF/aarch64-jump26-error.s | 11 - test/ELF/aarch64-jump26-thunk.s | 20 + test/ELF/aarch64-ldprel-lo19-invalid.s | 11 + test/ELF/aarch64-lo12-alignment.s | 45 + test/ELF/aarch64-load-alignment.s | 11 + test/ELF/aarch64-prel16.s | 2 +- test/ELF/aarch64-prel32.s | 2 +- test/ELF/aarch64-thunk-pi.s | 91 + test/ELF/aarch64-thunk-script.s | 41 + test/ELF/aarch64-thunk-section-location.s | 41 + test/ELF/aarch64-tls-gdie.s | 2 +- test/ELF/aarch64-tls-ie.s | 2 +- test/ELF/aarch64-tls-static.s | 2 +- test/ELF/aarch64-tlsdesc.s | 2 +- test/ELF/aarch64-undefined-weak.s | 6 +- test/ELF/abs-hidden.s | 2 +- test/ELF/allow-multiple-definition.s | 5 + test/ELF/amdgpu-elf-flags-err.s | 7 + test/ELF/amdgpu-elf-flags.s | 10 + test/ELF/amdgpu-relocs.s | 15 +- test/ELF/arm-bl-v6.s | 51 + test/ELF/arm-blx-v4t.s | 30 + test/ELF/arm-branch-error.s | 19 - test/ELF/arm-branch-rangethunk.s | 34 + test/ELF/arm-branch-undef-weak-plt-thunk.s | 35 + test/ELF/arm-copy.s | 2 +- test/ELF/arm-exidx-dedup.s | 126 + test/ELF/arm-exidx-gc.s | 2 +- test/ELF/arm-exidx-order.s | 4 +- test/ELF/arm-exidx-sentinel-orphan.s | 2 +- test/ELF/arm-exidx-shared.s | 6 +- test/ELF/arm-gnu-ifunc-plt.s | 56 +- test/ELF/arm-gnu-ifunc.s | 41 +- test/ELF/arm-got-relative.s | 2 +- test/ELF/arm-icf-exidx.s | 2 +- test/ELF/arm-pie-relative.s | 2 +- test/ELF/arm-plt-reloc.s | 264 +- test/ELF/arm-static-defines.s | 2 +- test/ELF/arm-thumb-branch-error.s | 19 - test/ELF/arm-thumb-branch-rangethunk.s | 36 + test/ELF/arm-thumb-condbranch-thunk.s | 117 + test/ELF/arm-thumb-interwork-shared.s | 37 +- test/ELF/arm-thumb-mix-range-thunk-os.s | 195 ++ test/ELF/arm-thumb-plt-range-thunk-os.s | 92 + test/ELF/arm-thumb-plt-reloc.s | 70 +- test/ELF/arm-thumb-range-thunk-os.s | 159 ++ test/ELF/arm-thumb-thunk-empty-pass.s | 32 + test/ELF/arm-thumb-thunk-symbols.s | 6 +- test/ELF/arm-thunk-edgecase.s | 37 + test/ELF/arm-thunk-largesection.s | 42 + test/ELF/arm-thunk-linkerscript-dotexpr.s | 77 + test/ELF/arm-thunk-linkerscript-large.s | 176 ++ test/ELF/arm-thunk-linkerscript-orphan.s | 63 + test/ELF/arm-thunk-linkerscript-sort.s | 71 + test/ELF/arm-thunk-linkerscript.s | 78 + test/ELF/arm-thunk-multipass.s | 96 + test/ELF/arm-thunk-re-add.s | 123 + test/ELF/arm-thunk-toolargesection.s | 19 + test/ELF/arm-tls-gd-nonpreemptible.s | 2 +- test/ELF/arm-tls-gd32.s | 2 +- test/ELF/arm-tls-ie32.s | 2 +- test/ELF/arm-tls-ldm32.s | 2 +- test/ELF/arm-tls-norelax-gd-ie.s | 2 +- test/ELF/arm-tls-norelax-gd-le.s | 6 +- test/ELF/arm-tls-norelax-ie-le.s | 2 +- test/ELF/arm-tls-norelax-ld-le.s | 2 +- test/ELF/as-needed.s | 4 + test/ELF/assignment-archive.s | 27 + test/ELF/avoid-empty-program-headers.s | 6 +- test/ELF/basic-aarch64.s | 16 +- test/ELF/basic-mips.s | 4 +- test/ELF/basic-ppc.s | 4 +- test/ELF/basic-sparcv9.s | 16 +- test/ELF/basic.s | 18 +- test/ELF/basic32.s | 16 +- test/ELF/basic64be.s | 2 +- test/ELF/build-id.s | 21 +- test/ELF/chroot.s | 12 + test/ELF/comment-gc.s | 3 +- test/ELF/common-gc.s | 41 + test/ELF/common-gc2.s | 15 + test/ELF/common-gc3.s | 18 + test/ELF/common.s | 10 +- test/ELF/compress-debug-sections.s | 7 +- test/ELF/compressed-debug-conflict.s | 29 + test/ELF/compressed-debug-input.s | 16 +- test/ELF/conflict-debug-variable.s | 144 + test/ELF/conflict-debug-variable2.s | 160 ++ test/ELF/copy-errors.s | 3 + test/ELF/copy-rel-abs.s | 47 + test/ELF/copy-rel-large.s | 20 + test/ELF/copy-rel-pie.s | 2 +- test/ELF/corrupted-version-reference.s | 14 + test/ELF/debug-gc.s | 4 +- test/ELF/defsym-dynamic.s | 10 + test/ELF/defsym.s | 42 +- test/ELF/driver-access.test | 2 +- test/ELF/driver.test | 4 +- test/ELF/duplicated-synthetic-sym.s | 3 +- test/ELF/dynamic-got.s | 2 +- test/ELF/dynamic-list-empty.s | 18 + test/ELF/dynamic-list-preempt.s | 76 + test/ELF/dynamic-list-weak-archive.s | 18 + test/ELF/dynamic-list-wildcard.s | 53 + test/ELF/dynamic-list.s | 22 +- test/ELF/dynamic-no-rosegment.s | 15 + test/ELF/dynamic-reloc-in-ro.s | 6 +- test/ELF/dynamic-reloc.s | 2 +- test/ELF/dynstr-no-rosegment.s | 12 + test/ELF/dynsym-no-rosegment.s | 27 + test/ELF/dynsym-pie.s | 52 +- test/ELF/edata-etext.s | 2 +- test/ELF/edata-no-bss.s | 18 + test/ELF/eh-align-cie.s | 2 +- test/ELF/eh-frame-hdr-augmentation.s | 2 +- test/ELF/eh-frame-hdr-icf-fde.s | 95 + test/ELF/eh-frame-hdr-icf.s | 15 +- test/ELF/eh-frame-hdr.s | 12 +- test/ELF/eh-frame-merge.s | 2 +- test/ELF/eh-frame-padding-no-rosegment.s | 2 +- test/ELF/eh-frame.s | 12 + test/ELF/emit-relocs-gc.s | 30 + test/ELF/emit-relocs-merge.s | 2 +- test/ELF/emit-relocs-mergeable-i386.s | 66 + test/ELF/emit-relocs-mergeable.s | 53 + test/ELF/emit-relocs-shared.s | 2 +- test/ELF/emit-relocs.s | 13 +- test/ELF/exclude-libs.s | 6 + test/ELF/executable-undefined-ignoreall.s | 13 + .../ELF/executable-undefined-protected-ignoreall.s | 8 + test/ELF/file-access.s | 13 + test/ELF/fill-trap.s | 25 + test/ELF/filter.s | 6 +- test/ELF/format-binary-non-ascii.s | 15 + test/ELF/gc-collect-undefined.s | 19 + test/ELF/gc-merge-local-sym.s | 2 +- test/ELF/gc-sections-linker-defined-symbol.s | 18 + test/ELF/gc-sections-merge-addend.s | 2 +- test/ELF/gc-sections-merge-implicit-addend.s | 2 +- test/ELF/gc-sections-merge.s | 4 +- test/ELF/gc-sections-print.s | 6 + test/ELF/gc-sections-shared.s | 93 +- test/ELF/gc-sections-undefined.s | 10 + test/ELF/gdb-index-base-addr.s | 70 + test/ELF/gdb-index-dup-types.s | 2 +- test/ELF/gdb-index-empty.s | 4 +- test/ELF/gdb-index-gc-sections.s | 2 +- test/ELF/gdb-index-noranges.s | 49 + test/ELF/gdb-index-ranges.s | 2 +- test/ELF/gdb-index-tls.s | 91 + test/ELF/gdb-index.s | 30 +- test/ELF/global-offset-table-position-aarch64.s | 2 +- test/ELF/global-offset-table-position-arm.s | 2 +- test/ELF/global-offset-table-position-i386.s | 2 +- test/ELF/global-offset-table-position.s | 2 +- test/ELF/global_offset_table_shared.s | 2 +- test/ELF/gnu-hash-table-copy.s | 30 + test/ELF/gnu-hash-table-many.s | 55 + test/ELF/gnu-hash-table-rwsegment.s | 20 + test/ELF/gnu-hash-table.s | 79 +- test/ELF/gnu-ifunc-dynsym.s | 19 + test/ELF/gnu-ifunc-gotpcrel.s | 2 +- test/ELF/gnu-ifunc-i386.s | 2 +- test/ELF/gnu-ifunc-plt-i386.s | 2 +- test/ELF/gnu-ifunc-plt.s | 2 +- test/ELF/gnu-ifunc-shared.s | 2 +- test/ELF/gnu-ifunc.s | 2 +- test/ELF/got-aarch64.s | 2 +- test/ELF/got.s | 2 +- test/ELF/got32-i386-pie-rw.s | 17 + test/ELF/got32-i386.s | 2 +- test/ELF/got32x-i386.s | 4 +- test/ELF/gotpc-relax-nopic.s | 6 +- test/ELF/gotpc-relax-und-dso.s | 2 +- test/ELF/gotpcrelx.s | 2 +- test/ELF/help.s | 5 + test/ELF/i386-debug-noabs.test | 33 + test/ELF/i386-got-and-copy.s | 2 +- test/ELF/i386-got-value.s | 36 + test/ELF/i386-gotoff-shared.s | 2 +- test/ELF/i386-gotpc-dynamic.s | 2 +- test/ELF/i386-gotpc.s | 2 +- test/ELF/i386-pc8-pc16-addend.s | 2 +- test/ELF/i386-reloc-16.s | 2 +- test/ELF/i386-reloc-8.s | 2 +- test/ELF/i386-reloc-range.s | 2 +- test/ELF/i386-reloc8-reloc16-addend.s | 2 +- test/ELF/i386-tls-ie-shared.s | 2 +- test/ELF/icf-absolute.s | 2 +- test/ELF/icf-comdat.s | 2 +- test/ELF/icf-i386.s | 2 +- test/ELF/icf-merge-sec.s | 2 +- test/ELF/icf-merge.s | 6 +- test/ELF/icf-non-mergeable.s | 4 +- test/ELF/icf-none.s | 2 +- test/ELF/icf-symbol-type.s | 26 + test/ELF/icf1.s | 2 +- test/ELF/icf2.s | 2 +- test/ELF/icf3.s | 2 +- test/ELF/icf4.s | 2 +- test/ELF/icf5.s | 2 +- test/ELF/icf6.s | 2 +- test/ELF/icf7.s | 2 +- test/ELF/icf9.s | 22 +- test/ELF/image-base.s | 7 +- test/ELF/init_fini_priority.s | 24 +- test/ELF/invalid-linkerscript.test | 2 +- test/ELF/invalid-local-symbol-in-dso.s | 13 + test/ELF/invalid-undef-section-symbol.test | 27 + test/ELF/invalid/Inputs/section-index2.elf | Bin 474 -> 0 bytes test/ELF/invalid/invalid-debug-relocations.test | 3 +- test/ELF/invalid/invalid-elf.test | 4 - test/ELF/invalid/invalid-relocation-x64.test | 18 +- test/ELF/libsearch.s | 1 + test/ELF/linkerscript/Inputs/common-filespec1.s | 2 + test/ELF/linkerscript/Inputs/common-filespec2.s | 2 + .../linkerscript/Inputs/copy-rel-symbol-value.s | 5 + test/ELF/linkerscript/Inputs/provide-shared.s | 5 + .../ELF/linkerscript/Inputs/symbol-reserved.script | 5 + test/ELF/linkerscript/absolute2.s | 17 + test/ELF/linkerscript/align-section-offset.s | 11 + test/ELF/linkerscript/align-section.s | 6 + test/ELF/linkerscript/align.s | 45 + test/ELF/linkerscript/arm-exidx-order.s | 19 + .../arm-exidx-sentinel-and-assignment.s | 24 + test/ELF/linkerscript/at-addr.s | 4 - test/ELF/linkerscript/at.s | 25 - test/ELF/linkerscript/common-exclude.s | 86 + test/ELF/linkerscript/common-filespec.s | 105 + test/ELF/linkerscript/common.s | 8 +- test/ELF/linkerscript/compress-debug-sections.s | 4 +- test/ELF/linkerscript/copy-rel-symbol-value-err.s | 12 + test/ELF/linkerscript/copy-rel-symbol-value.s | 27 + test/ELF/linkerscript/data-segment-relro.s | 4 +- test/ELF/linkerscript/diagnostic.s | 14 +- test/ELF/linkerscript/discard-section-err.s | 2 + test/ELF/linkerscript/early-assign-symbol.s | 24 +- .../ELF/linkerscript/eh-frame-reloc-out-of-range.s | 2 +- test/ELF/linkerscript/emit-reloc-section-names.s | 22 + test/ELF/linkerscript/emit-reloc.s | 2 +- test/ELF/linkerscript/emit-relocs-multiple.s | 2 +- test/ELF/linkerscript/extend-pt-load.s | 13 +- test/ELF/linkerscript/filename-spec.s | 47 +- test/ELF/linkerscript/header-addr.s | 22 +- test/ELF/linkerscript/header-phdr.s | 13 + test/ELF/linkerscript/image-base.s | 18 + test/ELF/linkerscript/implicit-program-header.s | 2 +- test/ELF/linkerscript/include-cycle.s | 15 + .../linkerscript/linker-script-in-search-path.s | 19 + test/ELF/linkerscript/linkerscript.s | 4 +- test/ELF/linkerscript/memory-at.s | 46 + test/ELF/linkerscript/memory-err.s | 16 + test/ELF/linkerscript/memory.s | 4 +- test/ELF/linkerscript/memory2.s | 14 + test/ELF/linkerscript/memory3.s | 23 + test/ELF/linkerscript/merge-sections.s | 2 +- test/ELF/linkerscript/no-space.s | 4 +- test/ELF/linkerscript/noload.s | 4 +- test/ELF/linkerscript/non-alloc.s | 2 +- test/ELF/linkerscript/operators.s | 2 + test/ELF/linkerscript/orphan-discard.s | 25 + test/ELF/linkerscript/orphan-end.s | 57 + test/ELF/linkerscript/orphan-phdrs.s | 34 + test/ELF/linkerscript/orphan-report.s | 54 + test/ELF/linkerscript/out-of-order.s | 2 +- test/ELF/linkerscript/phdr-check.s | 4 +- test/ELF/linkerscript/phdrs.s | 4 +- test/ELF/linkerscript/provide-shared.s | 13 + test/ELF/linkerscript/region-alias.s | 54 + test/ELF/linkerscript/repsection-symbol.s | 2 +- test/ELF/linkerscript/sections-sort.s | 2 +- test/ELF/linkerscript/segment-headers.s | 26 + test/ELF/linkerscript/segment-start.s | 2 +- test/ELF/linkerscript/sort-non-script.s | 2 +- test/ELF/linkerscript/subalign.s | 17 + test/ELF/linkerscript/symbol-assignexpr.s | 8 +- test/ELF/linkerscript/symbol-only-flags.s | 20 + test/ELF/linkerscript/symbol-only.s | 2 +- test/ELF/linkerscript/symbol-ordering-file.s | 23 + test/ELF/linkerscript/symbol-reserved.s | 28 + test/ELF/linkerscript/symbols.s | 13 +- test/ELF/linkerscript/thunk-gen-mips.s | 40 + test/ELF/linkerscript/unused-synthetic.s | 10 + test/ELF/linkerscript/version-linker-symbol.s | 28 + test/ELF/lit.local.cfg | 1 - test/ELF/local-got-pie.s | 2 +- test/ELF/local-got-shared.s | 2 +- test/ELF/local-got.s | 2 +- test/ELF/lto-plugin-ignore.s | 11 + test/ELF/lto/Inputs/data-ordering-lto.ll | 6 + test/ELF/lto/Inputs/linker-script-symbols-ipo.ll | 9 + test/ELF/lto/Inputs/symbol-ordering-lto.ll | 10 + test/ELF/lto/cache.ll | 14 +- test/ELF/lto/data-ordering-lto.s | 27 + test/ELF/lto/keep-undefined.ll | 20 + test/ELF/lto/linker-script-symbols-assign.ll | 48 + test/ELF/lto/linker-script-symbols-ipo.ll | 32 + test/ELF/lto/linker-script-symbols.ll | 29 + test/ELF/lto/opt-level.ll | 21 +- test/ELF/lto/opt-remarks.ll | 25 +- test/ELF/lto/relocatable.ll | 55 + test/ELF/lto/save-temps.ll | 7 + test/ELF/lto/section-name.ll | 35 + test/ELF/lto/shlib-undefined.ll | 2 +- test/ELF/lto/symbol-ordering-lto.s | 25 + test/ELF/lto/thinlto.ll | 8 +- test/ELF/lto/verify-invalid.ll | 2 + test/ELF/lto/wrap-1.ll | 2 +- test/ELF/lto/wrap-2.ll | 4 +- test/ELF/many-alloc-sections.s | 3 +- test/ELF/many-sections.s | 7 +- test/ELF/map-file.s | 86 +- test/ELF/merge-align.s | 34 + test/ELF/merge-entsize.s | 27 + test/ELF/merge-reloc.s | 19 +- test/ELF/merge-string.s | 14 +- test/ELF/merge.s | 2 +- test/ELF/mips-26-n32-n64.s | 35 + test/ELF/mips-64-gprel-so.s | 2 +- test/ELF/mips-64-rels.s | 6 +- test/ELF/mips-align-err.s | 2 +- test/ELF/mips-elf-flags-err.s | 10 +- test/ELF/mips-elf-flags.s | 29 + test/ELF/mips-got-page-script.s | 65 + test/ELF/mips-got-relocs.s | 4 +- test/ELF/mips-got-script.s | 47 + test/ELF/mips-gp-disp.s | 2 +- test/ELF/mips-gp-ext.s | 7 + test/ELF/mips-gp-local.s | 2 +- test/ELF/mips-gprel32-relocs-gp0.s | 8 +- test/ELF/mips-gprel32-relocs.s | 8 +- test/ELF/mips-hilo-gp-disp.s | 4 +- test/ELF/mips-hilo-hi-only.s | 2 +- test/ELF/mips-micro-got.s | 46 + test/ELF/mips-micro-got64.s | 48 + test/ELF/mips-micro-jal.s | 155 ++ test/ELF/mips-micro-plt.s | 91 + test/ELF/mips-micro-relocs.s | 59 + test/ELF/mips-micro-thunks.s | 47 + test/ELF/mips-n32-rels.s | 6 +- test/ELF/mips-out-of-bounds-call16-reloc.s | 29 + test/ELF/no-inhibit-exec.s | 4 + test/ELF/non-abs-reloc.s | 2 +- test/ELF/noplt-pie.s | 2 +- test/ELF/pack-dyn-relocs.s | 210 ++ test/ELF/pie-weak.s | 7 +- test/ELF/ppc-relocs.s | 36 +- test/ELF/ppc64-addr16-error.s | 2 +- test/ELF/pr34660.s | 25 + test/ELF/pr34872.s | 14 + test/ELF/progname.s | 2 +- test/ELF/relocatable-comdat2.s | 35 + test/ELF/relocatable-common.s | 5 +- test/ELF/relocatable-compressed-input.s | 10 +- test/ELF/relocatable.s | 2 +- test/ELF/relocation-b-aarch64.test | 48 + test/ELF/relocation-copy-alias.s | 8 +- test/ELF/relocation-copy-align-common.s | 2 +- test/ELF/relocation-copy-flags.s | 2 +- test/ELF/relocation-copy-relro.s | 2 +- test/ELF/relocation-i686.s | 2 +- test/ELF/relocation-relative-weak.s | 1 + test/ELF/relocation.s | 2 +- test/ELF/relro-copyrel-bss-script.s | 40 + test/ELF/relro-non-contiguous-script-data.s | 25 + test/ELF/relro-non-contiguous.s | 28 + test/ELF/relro-omagic.s | 2 +- test/ELF/relro-script.s | 29 + test/ELF/reproduce-thin-archive.s | 6 + test/ELF/reproduce.s | 31 +- test/ELF/resolution-end.s | 2 +- test/ELF/retain-symbols-file.s | 4 +- test/ELF/section-metadata-err.s | 2 +- test/ELF/segments.s | 5 + test/ELF/shared-lazy.s | 16 + test/ELF/shared.s | 6 +- test/ELF/silent-ignore.test | 20 + test/ELF/sort-norosegment.s | 2 +- test/ELF/startstop-gccollect.s | 12 +- test/ELF/startstop.s | 2 +- test/ELF/string-gc.s | 4 +- test/ELF/strip-debug.s | 27 +- test/ELF/symbol-ordering-file2.s | 21 + test/ELF/synthetic-got.s | 4 +- test/ELF/sysroot.s | 2 + test/ELF/tls-dynamic-i686.s | 2 +- test/ELF/tls-dynamic.s | 2 +- test/ELF/tls-got.s | 2 +- test/ELF/tls-i686.s | 2 +- test/ELF/tls-initial-exec-local.s | 2 +- test/ELF/tls-opt-gdie.s | 2 +- test/ELF/tls-opt-gdiele-i686.s | 2 +- test/ELF/tls-opt-iele-i686-nopic.s | 2 +- test/ELF/tls-static.s | 16 +- test/ELF/tls-two-relocs.s | 2 +- test/ELF/trace-symbols.s | 14 +- test/ELF/typed-undef.s | 11 + test/ELF/undef-broken-debug.test | 47 + test/ELF/undef-version-script.s | 6 +- test/ELF/unresolved-symbols.s | 3 + test/ELF/verdef-defaultver.s | 4 +- test/ELF/verdef.s | 6 +- test/ELF/verneed-as-needed-weak.s | 6 +- test/ELF/verneed-local.s | 6 +- test/ELF/verneed.s | 8 +- test/ELF/version-script-err.s | 1 - test/ELF/version-script-extern.s | 2 +- test/ELF/version-script-twice.s | 4 + test/ELF/version-script.s | 10 +- test/ELF/weak-entry.s | 13 + test/ELF/weak-undef-export.s | 31 + test/ELF/weak-undef-lazy.s | 11 + test/ELF/weak-undef-val.s | 26 + test/ELF/weak-undef.s | 12 + test/ELF/wrap-no-real.s | 77 + test/ELF/wrap.s | 34 +- test/ELF/x86-64-dyn-rel-error.s | 2 + test/ELF/x86-64-relax-got-abs.s | 2 +- test/ELF/x86-64-reloc-16.s | 2 +- test/ELF/x86-64-reloc-8.s | 2 +- test/ELF/x86-64-reloc-error.s | 4 +- test/ELF/x86-64-reloc-range.s | 2 +- test/ELF/x86-64-tls-gd-local.s | 2 +- test/MinGW/driver.test | 126 + test/MinGW/lib.test | 21 + test/Unit/lit.cfg | 23 - test/Unit/lit.cfg.py | 37 + test/Unit/lit.site.cfg.in | 25 - test/Unit/lit.site.cfg.py.in | 27 + test/lit.cfg | 270 -- test/lit.cfg.py | 93 + test/lit.site.cfg.in | 25 - test/lit.site.cfg.py.in | 28 + test/wasm/Inputs/archive1.ll | 7 + test/wasm/Inputs/archive2.ll | 7 + test/wasm/Inputs/call-indirect.ll | 17 + test/wasm/Inputs/hello.ll | 15 + test/wasm/Inputs/hidden.ll | 11 + test/wasm/Inputs/many-funcs.ll | 776 ++++++ test/wasm/Inputs/ret32.ll | 6 + test/wasm/Inputs/ret64.ll | 4 + test/wasm/Inputs/weak-alias.ll | 13 + test/wasm/Inputs/weak-symbol1.ll | 9 + test/wasm/Inputs/weak-symbol2.ll | 9 + test/wasm/archive.ll | 31 + test/wasm/call-indirect.ll | 112 + test/wasm/conflict.test | 6 + test/wasm/data-layout.ll | 61 + test/wasm/entry.ll | 19 + test/wasm/function-imports-first.ll | 42 + test/wasm/function-imports.ll | 37 + test/wasm/function-index.test | 18 + test/wasm/import-memory.test | 13 + test/wasm/invalid-stack-size.test | 9 + test/wasm/lit.local.cfg | 4 + test/wasm/load-undefined.ll | 38 + test/wasm/local-symbols.ll | 78 + test/wasm/many-functions.ll | 695 +++++ test/wasm/relocatable.ll | 194 ++ test/wasm/signature-mismatch.ll | 16 + test/wasm/stack-pointer.ll | 64 + test/wasm/strip-debug.test | 6 + test/wasm/symbol-type-mismatch.ll | 9 + test/wasm/undefined-entry.test | 4 + test/wasm/undefined.ll | 20 + test/wasm/version.ll | 13 + test/wasm/visibility-hidden.ll | 46 + test/wasm/weak-alias-overide.ll | 92 + test/wasm/weak-alias.ll | 82 + test/wasm/weak-external.ll | 86 + test/wasm/weak-symbols.ll | 93 + tools/lld/CMakeLists.txt | 7 +- tools/lld/lld.cpp | 22 +- unittests/DriverTests/CMakeLists.txt | 1 + unittests/DriverTests/DarwinLdDriverTest.cpp | 2 +- unittests/MachOTests/CMakeLists.txt | 1 + utils/benchmark.py | 135 + utils/link.yaml | 39 + wasm/CMakeLists.txt | 26 + wasm/Config.h | 51 + wasm/Driver.cpp | 321 +++ wasm/InputFiles.cpp | 303 +++ wasm/InputFiles.h | 153 ++ wasm/InputSegment.cpp | 25 + wasm/InputSegment.h | 74 + wasm/Options.td | 103 + wasm/OutputSections.cpp | 333 +++ wasm/OutputSections.h | 138 + wasm/OutputSegment.h | 56 + wasm/SymbolTable.cpp | 245 ++ wasm/SymbolTable.h | 72 + wasm/Symbols.cpp | 114 + wasm/Symbols.h | 128 + wasm/Writer.cpp | 724 ++++++ wasm/Writer.h | 21 + wasm/WriterUtils.cpp | 215 ++ wasm/WriterUtils.h | 78 + 830 files changed, 36310 insertions(+), 9941 deletions(-) delete mode 100644 COFF/Error.cpp delete mode 100644 COFF/Error.h delete mode 100644 COFF/Memory.h create mode 100644 COFF/MinGW.cpp create mode 100644 COFF/MinGW.h create mode 100644 Common/Args.cpp create mode 100644 Common/CMakeLists.txt create mode 100644 Common/ErrorHandler.cpp create mode 100644 Common/Memory.cpp create mode 100644 Common/Reproduce.cpp create mode 100644 Common/Strings.cpp create mode 100644 Common/TargetOptionsCommandFlags.cpp create mode 100644 Common/Threads.cpp create mode 100644 Common/Version.cpp create mode 100644 ELF/AArch64ErrataFix.cpp create mode 100644 ELF/AArch64ErrataFix.h create mode 100644 ELF/Bits.h delete mode 100644 ELF/Error.cpp delete mode 100644 ELF/Error.h delete mode 100644 ELF/Memory.h delete mode 100644 ELF/Threads.h create mode 100644 MinGW/CMakeLists.txt create mode 100644 MinGW/Driver.cpp create mode 100644 MinGW/Options.td create mode 100644 docs/WebAssembly.rst create mode 100644 include/lld/Common/Args.h create mode 100644 include/lld/Common/Driver.h create mode 100644 include/lld/Common/ErrorHandler.h create mode 100644 include/lld/Common/LLVM.h create mode 100644 include/lld/Common/Memory.h create mode 100644 include/lld/Common/Reproduce.h create mode 100644 include/lld/Common/Strings.h create mode 100644 include/lld/Common/TargetOptionsCommandFlags.h create mode 100644 include/lld/Common/Threads.h create mode 100644 include/lld/Common/Version.h create mode 100644 include/lld/Common/Version.inc.in delete mode 100644 include/lld/Config/Version.h delete mode 100644 include/lld/Config/Version.inc.in delete mode 100644 include/lld/Core/LLVM.h delete mode 100644 include/lld/Core/Reproduce.h delete mode 100644 include/lld/Core/TargetOptionsCommandFlags.h delete mode 100644 include/lld/Driver/Driver.h delete mode 100644 lib/Config/CMakeLists.txt delete mode 100644 lib/Config/Version.cpp delete mode 100644 lib/Core/Reproduce.cpp delete mode 100644 lib/Core/TargetOptionsCommandFlags.cpp create mode 100644 test/COFF/Inputs/alpha.ll create mode 100644 test/COFF/Inputs/beta.ll create mode 100644 test/COFF/Inputs/except_handler3.lib create mode 100644 test/COFF/Inputs/far-arm-thumb-abs20.s create mode 100644 test/COFF/Inputs/gamma.ll create mode 100644 test/COFF/Inputs/library2-arm64.lib create mode 100644 test/COFF/Inputs/library2.def create mode 100644 test/COFF/Inputs/locally-imported-def.s create mode 100644 test/COFF/Inputs/locally-imported-imp.s create mode 100644 test/COFF/Inputs/lto-cache.ll create mode 100644 test/COFF/Inputs/pdb-globals.yaml create mode 100644 test/COFF/Inputs/pdb-hashes-1.yaml create mode 100644 test/COFF/Inputs/pdb-hashes-2-missing.yaml create mode 100644 test/COFF/Inputs/pdb-hashes-2.yaml create mode 100644 test/COFF/arm-thumb-branch20-error.s create mode 100644 test/COFF/arm64-dynamicbase.s create mode 100644 test/COFF/arm64-import2.test create mode 100644 test/COFF/armnt-dynamicbase.test create mode 100644 test/COFF/common-alignment.test create mode 100644 test/COFF/ctors_dtors_priority.s create mode 100644 test/COFF/debug-dwarf.test create mode 100644 test/COFF/delayimports-armnt.yaml create mode 100644 test/COFF/dllexport-mingw.s create mode 100644 test/COFF/duplicate.test create mode 100644 test/COFF/entry-drectve.test create mode 100644 test/COFF/export-all.s create mode 100644 test/COFF/export-arm64.yaml create mode 100644 test/COFF/export-armnt.yaml create mode 100644 test/COFF/filename-casing.s create mode 100644 test/COFF/icf-executable.s create mode 100644 test/COFF/icf-xdata.s create mode 100644 test/COFF/linkrepro-manifest.test create mode 100644 test/COFF/linkrepro-pdb.test create mode 100644 test/COFF/linkrepro-res.test create mode 100644 test/COFF/locally-imported-arm64.test create mode 100644 test/COFF/locally-imported-warn-multiple.s create mode 100644 test/COFF/lto-cache.ll create mode 100644 test/COFF/lto-reloc-model.ll create mode 100644 test/COFF/manifestinput-error.test create mode 100644 test/COFF/manifestinput-nowarning.test delete mode 100644 test/COFF/nopdb.test create mode 100644 test/COFF/pdata-arm64.yaml create mode 100644 test/COFF/pdb-global-hashes.test create mode 100644 test/COFF/pdb-globals.test create mode 100644 test/COFF/pdb-heapsite.yaml create mode 100644 test/COFF/pdb-procid-remapping.test create mode 100644 test/COFF/pdb-publics-import.test create mode 100644 test/COFF/pdb-same-name.test create mode 100644 test/COFF/pdb-thunk.yaml create mode 100644 test/COFF/reloc-discarded-early.s create mode 100644 test/COFF/reloc-discarded-early2.s create mode 100644 test/COFF/safeseh-md.s create mode 100644 test/COFF/section-size.s create mode 100644 test/COFF/seh-comdat.test create mode 100644 test/COFF/strtab-size.s create mode 100644 test/COFF/subsystem-drectve.test create mode 100644 test/COFF/wholearchive.s create mode 100644 test/COFF/wx.s create mode 100644 test/ELF/Inputs/amdgpu-kernel-0.s create mode 100644 test/ELF/Inputs/amdgpu-kernel-1.s create mode 100644 test/ELF/Inputs/amdgpu-kernel-2.o create mode 100644 test/ELF/Inputs/copy-rel-abs.s create mode 100644 test/ELF/Inputs/copy-rel-large.s create mode 100644 test/ELF/Inputs/corrupt-version-reference.so create mode 100644 test/ELF/Inputs/dynamic-list-weak-archive.s create mode 100644 test/ELF/Inputs/eh-frame.s create mode 100644 test/ELF/Inputs/gc-sections-shared.s create mode 100644 test/ELF/Inputs/gc-sections-shared2.s create mode 100755 test/ELF/Inputs/local-symbol-in-dso.so create mode 100644 test/ELF/Inputs/map-file5.s create mode 100644 test/ELF/Inputs/mips-micro.s create mode 100644 test/ELF/Inputs/undefined-error.s delete mode 100755 test/ELF/Inputs/verneed.so.sh create mode 100644 test/ELF/Inputs/verneed1.s delete mode 100755 test/ELF/Inputs/verneed1.so create mode 100644 test/ELF/Inputs/verneed2.s delete mode 100755 test/ELF/Inputs/verneed2.so create mode 100644 test/ELF/Inputs/weak-undef-lazy.s create mode 100644 test/ELF/Inputs/wrap-no-real.s create mode 100644 test/ELF/Inputs/wrap-no-real2.s delete mode 100644 test/ELF/aarch64-call26-error.s create mode 100644 test/ELF/aarch64-call26-thunk.s create mode 100644 test/ELF/aarch64-cortex-a53-843419-address.s create mode 100644 test/ELF/aarch64-cortex-a53-843419-cli.s create mode 100644 test/ELF/aarch64-cortex-a53-843419-large.s create mode 100644 test/ELF/aarch64-cortex-a53-843419-nopatch.s create mode 100644 test/ELF/aarch64-cortex-a53-843419-recognize.s create mode 100644 test/ELF/aarch64-cortex-a53-843419-thunk.s delete mode 100644 test/ELF/aarch64-jump26-error.s create mode 100644 test/ELF/aarch64-jump26-thunk.s create mode 100644 test/ELF/aarch64-ldprel-lo19-invalid.s create mode 100644 test/ELF/aarch64-lo12-alignment.s create mode 100644 test/ELF/aarch64-load-alignment.s create mode 100644 test/ELF/aarch64-thunk-pi.s create mode 100644 test/ELF/aarch64-thunk-script.s create mode 100644 test/ELF/aarch64-thunk-section-location.s create mode 100644 test/ELF/amdgpu-elf-flags-err.s create mode 100644 test/ELF/amdgpu-elf-flags.s create mode 100644 test/ELF/arm-bl-v6.s create mode 100644 test/ELF/arm-blx-v4t.s delete mode 100644 test/ELF/arm-branch-error.s create mode 100644 test/ELF/arm-branch-rangethunk.s create mode 100644 test/ELF/arm-branch-undef-weak-plt-thunk.s create mode 100644 test/ELF/arm-exidx-dedup.s delete mode 100644 test/ELF/arm-thumb-branch-error.s create mode 100644 test/ELF/arm-thumb-branch-rangethunk.s create mode 100644 test/ELF/arm-thumb-condbranch-thunk.s create mode 100644 test/ELF/arm-thumb-mix-range-thunk-os.s create mode 100644 test/ELF/arm-thumb-plt-range-thunk-os.s create mode 100644 test/ELF/arm-thumb-range-thunk-os.s create mode 100644 test/ELF/arm-thumb-thunk-empty-pass.s create mode 100644 test/ELF/arm-thunk-edgecase.s create mode 100644 test/ELF/arm-thunk-largesection.s create mode 100644 test/ELF/arm-thunk-linkerscript-dotexpr.s create mode 100644 test/ELF/arm-thunk-linkerscript-large.s create mode 100644 test/ELF/arm-thunk-linkerscript-orphan.s create mode 100644 test/ELF/arm-thunk-linkerscript-sort.s create mode 100644 test/ELF/arm-thunk-linkerscript.s create mode 100644 test/ELF/arm-thunk-multipass.s create mode 100644 test/ELF/arm-thunk-re-add.s create mode 100644 test/ELF/arm-thunk-toolargesection.s create mode 100644 test/ELF/assignment-archive.s create mode 100644 test/ELF/chroot.s create mode 100644 test/ELF/common-gc.s create mode 100644 test/ELF/common-gc2.s create mode 100644 test/ELF/common-gc3.s create mode 100644 test/ELF/compressed-debug-conflict.s create mode 100644 test/ELF/conflict-debug-variable.s create mode 100644 test/ELF/conflict-debug-variable2.s create mode 100644 test/ELF/copy-rel-abs.s create mode 100644 test/ELF/copy-rel-large.s create mode 100644 test/ELF/corrupted-version-reference.s create mode 100644 test/ELF/defsym-dynamic.s create mode 100644 test/ELF/dynamic-list-empty.s create mode 100644 test/ELF/dynamic-list-preempt.s create mode 100644 test/ELF/dynamic-list-weak-archive.s create mode 100644 test/ELF/dynamic-list-wildcard.s create mode 100644 test/ELF/dynamic-no-rosegment.s create mode 100644 test/ELF/dynstr-no-rosegment.s create mode 100644 test/ELF/dynsym-no-rosegment.s create mode 100644 test/ELF/edata-no-bss.s create mode 100644 test/ELF/eh-frame-hdr-icf-fde.s create mode 100644 test/ELF/eh-frame.s create mode 100644 test/ELF/emit-relocs-gc.s create mode 100644 test/ELF/emit-relocs-mergeable-i386.s create mode 100644 test/ELF/emit-relocs-mergeable.s create mode 100644 test/ELF/executable-undefined-ignoreall.s create mode 100644 test/ELF/executable-undefined-protected-ignoreall.s create mode 100644 test/ELF/file-access.s create mode 100644 test/ELF/fill-trap.s create mode 100644 test/ELF/format-binary-non-ascii.s create mode 100644 test/ELF/gc-collect-undefined.s create mode 100644 test/ELF/gc-sections-linker-defined-symbol.s create mode 100644 test/ELF/gc-sections-undefined.s create mode 100644 test/ELF/gdb-index-base-addr.s create mode 100644 test/ELF/gdb-index-noranges.s create mode 100644 test/ELF/gdb-index-tls.s create mode 100644 test/ELF/gnu-hash-table-copy.s create mode 100644 test/ELF/gnu-hash-table-many.s create mode 100644 test/ELF/gnu-hash-table-rwsegment.s create mode 100644 test/ELF/gnu-ifunc-dynsym.s create mode 100644 test/ELF/got32-i386-pie-rw.s create mode 100644 test/ELF/help.s create mode 100644 test/ELF/i386-debug-noabs.test create mode 100644 test/ELF/i386-got-value.s create mode 100644 test/ELF/icf-symbol-type.s create mode 100644 test/ELF/invalid-local-symbol-in-dso.s create mode 100644 test/ELF/invalid-undef-section-symbol.test delete mode 100644 test/ELF/invalid/Inputs/section-index2.elf create mode 100644 test/ELF/linkerscript/Inputs/common-filespec1.s create mode 100644 test/ELF/linkerscript/Inputs/common-filespec2.s create mode 100644 test/ELF/linkerscript/Inputs/copy-rel-symbol-value.s create mode 100644 test/ELF/linkerscript/Inputs/provide-shared.s create mode 100644 test/ELF/linkerscript/Inputs/symbol-reserved.script create mode 100644 test/ELF/linkerscript/absolute2.s create mode 100644 test/ELF/linkerscript/align-section-offset.s create mode 100644 test/ELF/linkerscript/align-section.s create mode 100644 test/ELF/linkerscript/arm-exidx-order.s create mode 100644 test/ELF/linkerscript/arm-exidx-sentinel-and-assignment.s create mode 100644 test/ELF/linkerscript/common-exclude.s create mode 100644 test/ELF/linkerscript/common-filespec.s create mode 100644 test/ELF/linkerscript/copy-rel-symbol-value-err.s create mode 100644 test/ELF/linkerscript/copy-rel-symbol-value.s create mode 100644 test/ELF/linkerscript/emit-reloc-section-names.s create mode 100644 test/ELF/linkerscript/header-phdr.s create mode 100644 test/ELF/linkerscript/image-base.s create mode 100644 test/ELF/linkerscript/include-cycle.s create mode 100644 test/ELF/linkerscript/linker-script-in-search-path.s create mode 100644 test/ELF/linkerscript/memory-at.s create mode 100644 test/ELF/linkerscript/memory-err.s create mode 100644 test/ELF/linkerscript/memory2.s create mode 100644 test/ELF/linkerscript/memory3.s create mode 100644 test/ELF/linkerscript/orphan-discard.s create mode 100644 test/ELF/linkerscript/orphan-end.s create mode 100644 test/ELF/linkerscript/orphan-phdrs.s create mode 100644 test/ELF/linkerscript/orphan-report.s create mode 100644 test/ELF/linkerscript/provide-shared.s create mode 100644 test/ELF/linkerscript/region-alias.s create mode 100644 test/ELF/linkerscript/segment-headers.s create mode 100644 test/ELF/linkerscript/symbol-only-flags.s create mode 100644 test/ELF/linkerscript/symbol-ordering-file.s create mode 100644 test/ELF/linkerscript/thunk-gen-mips.s create mode 100644 test/ELF/linkerscript/version-linker-symbol.s create mode 100644 test/ELF/lto-plugin-ignore.s create mode 100644 test/ELF/lto/Inputs/data-ordering-lto.ll create mode 100644 test/ELF/lto/Inputs/linker-script-symbols-ipo.ll create mode 100644 test/ELF/lto/Inputs/symbol-ordering-lto.ll create mode 100644 test/ELF/lto/data-ordering-lto.s create mode 100644 test/ELF/lto/keep-undefined.ll create mode 100644 test/ELF/lto/linker-script-symbols-assign.ll create mode 100644 test/ELF/lto/linker-script-symbols-ipo.ll create mode 100644 test/ELF/lto/linker-script-symbols.ll create mode 100644 test/ELF/lto/relocatable.ll create mode 100644 test/ELF/lto/section-name.ll create mode 100644 test/ELF/lto/symbol-ordering-lto.s create mode 100644 test/ELF/merge-align.s create mode 100644 test/ELF/merge-entsize.s create mode 100644 test/ELF/mips-26-n32-n64.s create mode 100644 test/ELF/mips-got-page-script.s create mode 100644 test/ELF/mips-got-script.s create mode 100644 test/ELF/mips-micro-got.s create mode 100644 test/ELF/mips-micro-got64.s create mode 100644 test/ELF/mips-micro-jal.s create mode 100644 test/ELF/mips-micro-plt.s create mode 100644 test/ELF/mips-micro-relocs.s create mode 100644 test/ELF/mips-micro-thunks.s create mode 100644 test/ELF/mips-out-of-bounds-call16-reloc.s create mode 100644 test/ELF/pack-dyn-relocs.s create mode 100644 test/ELF/pr34660.s create mode 100644 test/ELF/pr34872.s create mode 100644 test/ELF/relocatable-comdat2.s create mode 100644 test/ELF/relocation-b-aarch64.test create mode 100644 test/ELF/relro-copyrel-bss-script.s create mode 100644 test/ELF/relro-non-contiguous-script-data.s create mode 100644 test/ELF/relro-non-contiguous.s create mode 100644 test/ELF/relro-script.s create mode 100644 test/ELF/shared-lazy.s create mode 100644 test/ELF/silent-ignore.test create mode 100644 test/ELF/symbol-ordering-file2.s create mode 100644 test/ELF/typed-undef.s create mode 100644 test/ELF/undef-broken-debug.test create mode 100644 test/ELF/weak-entry.s create mode 100644 test/ELF/weak-undef-export.s create mode 100644 test/ELF/weak-undef-lazy.s create mode 100644 test/ELF/weak-undef-val.s create mode 100644 test/ELF/wrap-no-real.s create mode 100644 test/MinGW/driver.test create mode 100644 test/MinGW/lib.test delete mode 100644 test/Unit/lit.cfg create mode 100644 test/Unit/lit.cfg.py delete mode 100644 test/Unit/lit.site.cfg.in create mode 100644 test/Unit/lit.site.cfg.py.in delete mode 100644 test/lit.cfg create mode 100644 test/lit.cfg.py delete mode 100644 test/lit.site.cfg.in create mode 100644 test/lit.site.cfg.py.in create mode 100644 test/wasm/Inputs/archive1.ll create mode 100644 test/wasm/Inputs/archive2.ll create mode 100644 test/wasm/Inputs/call-indirect.ll create mode 100644 test/wasm/Inputs/hello.ll create mode 100644 test/wasm/Inputs/hidden.ll create mode 100644 test/wasm/Inputs/many-funcs.ll create mode 100644 test/wasm/Inputs/ret32.ll create mode 100644 test/wasm/Inputs/ret64.ll create mode 100644 test/wasm/Inputs/weak-alias.ll create mode 100644 test/wasm/Inputs/weak-symbol1.ll create mode 100644 test/wasm/Inputs/weak-symbol2.ll create mode 100644 test/wasm/archive.ll create mode 100644 test/wasm/call-indirect.ll create mode 100644 test/wasm/conflict.test create mode 100644 test/wasm/data-layout.ll create mode 100644 test/wasm/entry.ll create mode 100644 test/wasm/function-imports-first.ll create mode 100644 test/wasm/function-imports.ll create mode 100644 test/wasm/function-index.test create mode 100644 test/wasm/import-memory.test create mode 100644 test/wasm/invalid-stack-size.test create mode 100644 test/wasm/lit.local.cfg create mode 100644 test/wasm/load-undefined.ll create mode 100644 test/wasm/local-symbols.ll create mode 100644 test/wasm/many-functions.ll create mode 100644 test/wasm/relocatable.ll create mode 100644 test/wasm/signature-mismatch.ll create mode 100644 test/wasm/stack-pointer.ll create mode 100644 test/wasm/strip-debug.test create mode 100644 test/wasm/symbol-type-mismatch.ll create mode 100644 test/wasm/undefined-entry.test create mode 100644 test/wasm/undefined.ll create mode 100644 test/wasm/version.ll create mode 100644 test/wasm/visibility-hidden.ll create mode 100644 test/wasm/weak-alias-overide.ll create mode 100644 test/wasm/weak-alias.ll create mode 100644 test/wasm/weak-external.ll create mode 100644 test/wasm/weak-symbols.ll create mode 100755 utils/benchmark.py create mode 100644 utils/link.yaml create mode 100644 wasm/CMakeLists.txt create mode 100644 wasm/Config.h create mode 100644 wasm/Driver.cpp create mode 100644 wasm/InputFiles.cpp create mode 100644 wasm/InputFiles.h create mode 100644 wasm/InputSegment.cpp create mode 100644 wasm/InputSegment.h create mode 100644 wasm/Options.td create mode 100644 wasm/OutputSections.cpp create mode 100644 wasm/OutputSections.h create mode 100644 wasm/OutputSegment.h create mode 100644 wasm/SymbolTable.cpp create mode 100644 wasm/SymbolTable.h create mode 100644 wasm/Symbols.cpp create mode 100644 wasm/Symbols.h create mode 100644 wasm/Writer.cpp create mode 100644 wasm/Writer.h create mode 100644 wasm/WriterUtils.cpp create mode 100644 wasm/WriterUtils.h 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(Body); + auto *Sym = + dyn_cast_or_null(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 *Res) { uint8_t Ty = getBaserelType(Rel); if (Ty == IMAGE_REL_BASED_ABSOLUTE) continue; - SymbolBody *Body = File->getSymbolBody(Rel.SymbolTableIndex); - if (isa(Body)) + Symbol *Target = File->getSymbol(Rel.SymbolTableIndex); + if (!Target || isa(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 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 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 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 #include #include @@ -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(DebugType::None); llvm::SmallString<128> PDBPath; std::vector Argv; // Symbols in this set are considered as live by the garbage collector. - std::set GCRoot; + std::vector GCRoot; std::set NoDefaultLibs; bool NoDefaultLibAll = false; @@ -107,7 +106,7 @@ struct Configuration { std::vector Exports; std::set DelayLoads; std::map 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 Merge; @@ -139,6 +143,9 @@ struct Configuration { StringRef ManifestUIAccess = "'false'"; StringRef ManifestFile; + // Used for /aligncomm. + std::map AlignComm; + // Used for /failifmismatch. std::map 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_ + 0xc0, 0xf2, 0x00, 0x0c, // mov.t ip, #0 __imp_ + 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 *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(E.Sym)->getRVA()); + write32le(P, cast(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(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(S, Dir, Helper); case I386: return make(S, Dir, Helper); + case ARMNT: + return make(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::Instances; - -bool link(ArrayRef Args, raw_ostream &Diag) { - ErrorCount = 0; - ErrorOS = &Diag; +bool link(ArrayRef 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(); Config->Argv = {Args.begin(), Args.end()}; - Config->ColorDiagnostics = - (ErrorOS == &llvm::errs() && Process::StandardErrHasColors()); + Config->CanExitEarly = CanExitEarly; + + Symtab = make(); + Driver = make(); 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 MB) { return MBRef; } -void LinkerDriver::addBuffer(std::unique_ptr MB) { +void LinkerDriver::addBuffer(std::unique_ptr 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(MBRef)); - if (Magic == file_magic::bitcode) - return Symtab.addFile(make(MBRef)); + case file_magic::archive: + if (WholeArchive) { + std::unique_ptr File = + CHECK(Archive::create(MBRef), + MBRef.getBufferIdentifier() + ": failed to parse archive"); + + for (MemoryBufferRef M : getArchiveMembers(File.get())) + addArchiveBuffer(M, "", MBRef.getBufferIdentifier()); + return; + } + Symtab->addFile(make(MBRef)); + break; + + case file_magic::bitcode: + Symtab->addFile(make(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(MBRef)); + break; + + default: + Symtab->addFile(make(MBRef)); + break; + } } -void LinkerDriver::enqueuePath(StringRef Path) { +void LinkerDriver::enqueuePath(StringRef Path, bool WholeArchive) { auto Future = std::make_shared>(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(MB)); + Symtab->addFile(make(MB)); return; } InputFile *Obj; if (Magic == file_magic::coff_object) { - Obj = make(MB); + Obj = make(MB); } else if (Magic == file_magic::bitcode) { Obj = make(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>(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 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 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(Symtab.find(Entry)->body())) + StringRef Entry = Symtab->findMangle(mangle(E[0])); + if (!Entry.empty() && !isa(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 MB = check( - MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path); - COFFModuleDefinition M = - check(parseCOFFModuleDefinition(MB->getMemBufferRef(), Config->Machine)); + std::unique_ptr 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 getArchiveMembers(Archive *File) { - std::vector V; - Error Err = Error::success(); - for (const ErrorOr &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 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 filterBitcodeFiles(StringRef Path, std::vector &TemporaryFiles) { - std::unique_ptr MB = check( + std::unique_ptr 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 &TemporaryFiles) { return Path.str(); std::unique_ptr File = - check(Archive::create(MBRef), + CHECK(Archive::create(MBRef), MBRef.getBufferIdentifier() + ": failed to parse archive"); std::vector New; @@ -589,16 +629,17 @@ filterBitcodeFiles(StringRef Path, std::vector &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 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 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 ObjectFiles = Symtab.compileBitcodeFiles(); - runMSVCLinker(Rsp, ObjectFiles); + std::vector ObjFiles = Symtab->compileBitcodeFiles(); + runMSVCLinker(Rsp, ObjFiles); for (StringRef Path : Temps) sys::fs::remove(Path); @@ -689,6 +730,7 @@ void LinkerDriver::link(ArrayRef 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 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 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 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 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 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 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 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 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 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 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 MBs; - for (auto *Arg : Args.filtered(OPT_INPUT)) - if (Optional 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 Path = findFile(Arg->getValue())) + enqueuePath(*Path, WholeArchiveFlag); + break; + case OPT_wholearchive_file: + if (Optional Path = findFile(Arg->getValue())) + enqueuePath(*Path, true); + break; + } + } for (auto *Arg : Args.filtered(OPT_defaultlib)) if (Optional 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 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(convertResToCOFF(Resources))); if (Tar) Tar->append("response.txt", @@ -984,28 +1073,36 @@ void LinkerDriver::link(ArrayRef ArgsArr) { ArrayRef(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 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 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 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 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 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(Sym->body())) + if (auto *U = dyn_cast(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 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(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 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(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 &Chunks); +void markLive(ArrayRef Chunks); // Implemented in ICF.cpp. -void doICF(const std::vector &Chunks); +void doICF(ArrayRef Chunks); -class ArgParser { +class COFFOptTable : public llvm::opt::OptTable { public: - // Parses command line options. - llvm::opt::InputArgList parse(llvm::ArrayRef 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 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 Args); + std::vector tokenize(StringRef S); - std::vector replaceResponseFiles(std::vector); + COFFOptTable Table; }; class LinkerDriver { public: - LinkerDriver() { coff::Symtab = &Symtab; } void link(llvm::ArrayRef 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 MB); +private: std::unique_ptr Tar; // for /linkrepro // Opens a file. Path has to be resolved already. @@ -93,7 +96,7 @@ private: std::set VisitedFiles; std::set 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 MB); - void addBuffer(std::unique_ptr MB); + void addBuffer(std::unique_ptr 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 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[,=]|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 -convertResToCOFF(const std::vector &MBs); +// Convert Windows resource files (.res files) to a .obj file. +MemoryBufferRef convertResToCOFF(ArrayRef MBs); void runMSVCLinker(std::string Rsp, ArrayRef 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 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 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[,=]|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 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() { << " \n" << " \n" << " \n"; - if (!Config->ManifestDependency.empty()) { - OS << " \n" - << " \n" - << " ManifestDependency << " />\n" - << " \n" - << " \n"; - } + } + if (!Config->ManifestDependency.empty()) { + OS << " \n" + << " \n" + << " ManifestDependency << " />\n" + << " \n" + << " \n"; } OS << "\n"; - OS.close(); - return File; + return OS.str(); } -static std::string readFile(StringRef Path) { - std::unique_ptr MB = - check(MemoryBuffer::getFile(Path), "could not open " + Path); - return MB->getBuffer(); +static std::string createManifestXmlWithInternalMt(StringRef DefaultXml) { + std::unique_ptr 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 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 @@ -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 "=.". - 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 Map; + DenseMap Map(Config->Exports.size()); std::vector 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 -convertResToCOFF(const std::vector &MBs) { +// Convert Windows resource files (.res files) to a .obj file. +MemoryBufferRef convertResToCOFF(ArrayRef MBs) { object::WindowsResourceParser Parser; for (MemoryBufferRef MB : MBs) { @@ -613,14 +651,17 @@ convertResToCOFF(const std::vector &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> 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::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 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 ArgsArr) { - // First, replace respnose files (@-style options). - std::vector 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 Argv) { // Make InputArgList from string vectors. - COFFOptTable Table; unsigned MissingIndex; unsigned MissingCount; - opt::InputArgList Args = Table.ParseArgs(Argv, MissingIndex, MissingCount); + SmallVector 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 @) + // 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 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 Args) { +opt::InputArgList ArgParser::parseLINK(std::vector Argv) { // Concatenate LINK env and command line arguments, and then parse them. if (Optional S = Process::GetEnv("LINK")) { std::vector V = tokenize(*S); - Args.insert(Args.begin(), V.begin(), V.end()); + Argv.insert(Argv.begin(), V.begin(), V.end()); } if (Optional S = Process::GetEnv("_LINK_")) { std::vector 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 ArgParser::tokenize(StringRef S) { @@ -712,18 +772,8 @@ std::vector ArgParser::tokenize(StringRef S) { return std::vector(Tokens.begin(), Tokens.end()); } -// Creates a new command line by replacing options starting with '@' -// character. '@' is replaced by the file's contents. -std::vector -ArgParser::replaceResponseFiles(std::vector Argv) { - SmallVector Tokens(Argv.data(), Argv.data() + Argv.size()); - ExpandResponseFiles(Saver, TokenizeWindowsCommandLine, Tokens); - return std::vector(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 - -#if !defined(_MSC_VER) && !defined(__MINGW32__) -#include -#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 Lock(Mu); - outs() << Config->Argv[0] << ": " << Msg << "\n"; - outs().flush(); - } -} - -void message(const Twine &Msg) { - std::lock_guard Lock(Mu); - outs() << Msg << "\n"; - outs().flush(); -} - -void error(const Twine &Msg) { - std::lock_guard 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 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 T check(ErrorOr V, const Twine &Prefix) { - if (auto EC = V.getError()) - fatal(EC, Prefix); - return std::move(*V); -} - -template T check(Expected E, const Twine &Prefix) { - if (llvm::Error Err = E.takeError()) - fatal(Err, Prefix); - return std::move(*E); -} - -template T check(ErrorOr EO) { - if (!EO) - fatal(EO.getError().message()); - return std::move(*EO); -} - -template T check(Expected 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 &V); + void run(ArrayRef 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(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(B1)) @@ -202,7 +206,7 @@ void ICF::forEachClass(std::function 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 &Vec) { +void ICF::run(ArrayRef 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 &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 &Vec) { } // Entry point to ICF. -void doICF(const std::vector &Chunks) { ICF().run(Chunks); } +void doICF(ArrayRef 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::Instances; +std::vector ImportFile::Instances; +std::vector 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(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 getArchiveMembers(Archive *File) { + std::vector V; + Error Err = Error::success(); + for (const ErrorOr &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 Bin = check(createBinary(MB), toString(this)); + std::unique_ptr Bin = CHECK(createBinary(MB), this); if (auto *Obj = dyn_cast(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(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 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(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 Data; + COFFObj->getSectionContents(Sec, Data); + if (Data.size() % 4 != 0) + fatal(".sxdata must be an array of symbol table indices"); + SXData = {reinterpret_cast(Data.data()), + Data.size() / 4}; + return nullptr; + } + if (Name == ".drectve") { + ArrayRef 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(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(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, 8> WeakAliases; - int32_t LastSectionNumber = 0; + SmallVector, 8> WeakAliases; + std::vector PendingIndexes; + PendingIndexes.reserve(NumSymbols); + + std::vector ComdatDefs( + COFFObj->getNumberOfSections() + 1); for (uint32_t I = 0; I < NumSymbols; ++I) { - // Get a COFFSymbolRef object. - ErrorOr 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(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()->TagIndex; + WeakAliases.emplace_back(Symbols[I], TagIndex); + } else if (Optional 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 ObjFile::createDefined( + COFFSymbolRef Sym, + std::vector &ComdatDefs) { StringRef Name; if (Sym.isCommon()) { auto *C = make(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(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(SparseChunks[SectionNumber]); - if (!SC) - return nullptr; - - // Handle section definitions - if (IsFirst && AuxP) { - auto *Aux = reinterpret_cast(AuxP); - if (Aux->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE) - if (auto *ParentSC = cast_or_null( - 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(this, /*Name*/ "", false, + /*IsExternal*/ false, Sym.getGeneric()); + Prevailing = true; + } + if (Prevailing) { + SectionChunk *C = readSection(SectionNumber, Def); + SparseChunks[SectionNumber] = C; + C->Sym = cast(Leader); + cast(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(S->body()); - } else - B = make(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 A; - COFFObj->getSectionContents(SXData, A); - if (A.size() % 4 != 0) - fatal(".sxdata must be an array of symbol table indices"); - auto *I = reinterpret_cast(A.data()); - auto *E = reinterpret_cast(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(COFFObj->getMachine()); return IMAGE_FILE_MACHINE_UNKNOWN; @@ -332,26 +420,27 @@ void ImportFile::parse() { this->Hdr = Hdr; ExternalName = ExtName; - ImpSym = cast( - Symtab->addImportData(ImpName, this)->body()); + ImpSym = Symtab->addImportData(ImpName, this); + if (Hdr->getType() == llvm::COFF::IMPORT_CONST) - ConstSym = - cast(Symtab->addImportData(Name, this)->body()); + static_cast(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( - 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> 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 ""; 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 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 &getChunks() { return Chunks; } - std::vector &getDebugChunks() { return DebugChunks; } - std::vector &getSymbols() { return SymbolBodies; } + ArrayRef getChunks() { return Chunks; } + ArrayRef getDebugChunks() { return DebugChunks; } + ArrayRef 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 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 SEHandlers; + ArrayRef 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 + createDefined(COFFSymbolRef Sym, + std::vector + &ComdatDefs); + Symbol *createRegular(COFFSymbolRef Sym); + Symbol *createUndefined(COFFSymbolRef Sym); std::unique_ptr 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 SparseChunks; - - // List of all symbols referenced or defined by this file. - std::vector SymbolBodies; + std::vector 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 SparseSymbolBodies; + std::vector 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 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 &getSymbols() { return SymbolBodies; } + ArrayRef getSymbols() { return SymbolBodies; } MachineTypes getMachineType() override; + static std::vector Instances; std::unique_ptr Obj; private: void parse() override; - std::vector SymbolBodies; + std::vector 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 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(S, S->body()->getName()); -} +static void undefine(Symbol *S) { replaceSymbol(S, S->getName()); } void BitcodeCompiler::add(BitcodeFile &F) { lto::InputFile &Obj = *F.Obj; unsigned SymNum = 0; - std::vector SymBodies = F.getSymbols(); + std::vector SymBodies = F.getSymbols(); std::vector 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 BitcodeCompiler::compile() { unsigned MaxTasks = LTOObj->getMaxTasks(); Buff.resize(MaxTasks); - - checkError(LTOObj->run([&](size_t Task) { - return llvm::make_unique( - llvm::make_unique(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 MB, + StringRef Path) { Files[Task] = std::move(MB); })); + + checkError(LTOObj->run( + [&](size_t Task) { + return llvm::make_unique( + llvm::make_unique(Buff[Task])); + }, + Cache)); + + if (!Config->LTOCache.empty()) + pruneCache(Config->LTOCache, Config->LTOCachePolicy); std::vector Ret; for (unsigned I = 0; I != MaxTasks; ++I) { @@ -136,5 +154,10 @@ std::vector BitcodeCompiler::compile() { } Ret.emplace_back(Buff[I].data(), Buff[I].size()); } + + for (std::unique_ptr &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 #include @@ -49,6 +49,7 @@ public: private: std::unique_ptr LTOObj; std::vector> Buff; + std::vector> 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 getSymbols() { std::vector V; - for (coff::ObjectFile *File : Symtab->ObjectFiles) - for (SymbolBody *B : File->getSymbols()) - if (auto *Sym = dyn_cast(B)) + for (ObjFile *File : ObjFile::Instances) + for (Symbol *B : File->getSymbols()) + if (auto *Sym = dyn_cast_or_null(B)) if (Sym && !Sym->getCOFFSymbol().isSectionDefinition()) V.push_back(Sym); return V; @@ -115,7 +115,7 @@ void coff::writeMapFile(ArrayRef 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 &Chunks) { +void markLive(ArrayRef 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 &Chunks) { Worklist.push_back(C); }; - auto AddSym = [&](SymbolBody *B) { + auto AddSym = [&](Symbol *B) { if (auto *Sym = dyn_cast(B)) Enqueue(Sym->getChunk()); else if (auto *Sym = dyn_cast(B)) @@ -47,23 +47,17 @@ void markLive(const std::vector &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 - -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 Instances; -}; - -template struct SpecificAlloc : public SpecificAllocBase { - void reset() override { Alloc.DestroyAll(); } - llvm::SpecificBumpPtrAllocator Alloc; -}; - -template T *make(U &&... Args) { - static SpecificAlloc Alloc; - return new (Alloc.Alloc.Allocate()) T(std::forward(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(Sym) && !isa(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(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 : Flag<["/", "-", "-?"], name>; class P : Joined<["/", "-", "-?"], name#":">, HelpText; -// Boolean flag suffixed by ":no". -multiclass B { - def "" : F; - def _no : F, HelpText; +// 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 { + def "" : F, HelpText; + def _no : F, HelpText; } 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; @@ -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; // 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 @@ -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 SectionTable); + void addSections(ArrayRef OutputSections, + ArrayRef 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(C)) - DbiBuilder.addSectionContrib(SC->File->ModuleDBI, SC->Header); -} - -static SectionChunk *findByName(std::vector &Sections, +static SectionChunk *findByName(ArrayRef Sections, StringRef Name) { for (SectionChunk *C : Sections) if (C->getSectionName() == Name) @@ -152,21 +161,58 @@ static ArrayRef consumeDebugMagic(ArrayRef Data, return Data.slice(4); } -static ArrayRef getDebugSection(ObjectFile *File, StringRef SecName) { +static ArrayRef 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 DebugH) { + if (DebugH.size() < sizeof(object::debug_h_header)) + return false; + auto *Header = + reinterpret_cast(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> getDebugH(ObjFile *File) { + SectionChunk *Sec = findByName(File->getDebugChunks(), ".debug$H"); + if (!Sec) + return llvm::None; + ArrayRef Contents = Sec->getContents(); + if (!canUseDebugH(Contents)) + return None; + return Contents; +} + +static ArrayRef +getHashesFromDebugH(ArrayRef DebugH) { + assert(canUseDebugH(DebugH)); + + DebugH = DebugH.drop_front(sizeof(object::debug_h_header)); + uint32_t Count = DebugH.size() / sizeof(GloballyHashedType); + return {reinterpret_cast(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 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(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 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 Hashes; + std::vector OwnedHashes; + if (Optional> 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> tryToLoadPDB(const GUID &GuidFromObj, StringRef TSPath) { + ErrorOr> MBOrErr = MemoryBuffer::getFile( + TSPath, /*FileSize=*/-1, /*RequiresNullTerminator=*/false); + if (!MBOrErr) + return errorCodeToError(MBOrErr.getError()); + std::unique_ptr 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 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 TypeIndexMap) { return true; } -static void remapTypesInSymbolRecord(ObjectFile *File, +static void remapTypesInSymbolRecord(ObjFile *File, SymbolKind SymKind, MutableArrayRef Contents, const CVIndexMap &IndexMap, ArrayRef 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 TypeOrItemMap = IndexMap.TPIMap; - if (Ref.Kind == TiRefKind::IndexRef && IndexMap.IsTypeServerMap) + bool IsItemIndex = Ref.Kind == TiRefKind::IndexRef; + if (IsItemIndex && IndexMap.IsTypeServerMap) TypeOrItemMap = IndexMap.IPIMap; MutableArrayRef TIs( reinterpret_cast(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 RecordData) { + const RecordPrefix *Prefix = + reinterpret_cast(RecordData.data()); + return static_cast(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 &RecordData, + TypeCollection &IDTable) { + RecordPrefix *Prefix = reinterpret_cast(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 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(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 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 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(Mem); - Prefix->RecordKind = canonicalizeSymbolKind(Sym.kind()); Prefix->RecordLen = Size - 2; return NewData; } @@ -402,7 +539,7 @@ static void scopeStackOpen(SmallVectorImpl &Stack, } static void scopeStackClose(SmallVectorImpl &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 &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(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 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 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 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 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(Def)) { + if (D->getCOFFSymbol().isFunctionDefinition()) + Pub.Flags = PublicSymFlags::Function; + } else if (isa(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 Publics; + Symtab->forEachSymbol([&Publics](Symbol *S) { + // Only emit defined, live symbols that have a chunk. + auto *Def = dyn_cast(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 SectionTable, - const llvm::codeview::DebugInfo *DI) { +void coff::createPDB(SymbolTable *Symtab, + ArrayRef OutputSections, + ArrayRef 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 SectionTable) { - // Add Section Contributions. - pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder(); - addSectionContribs(Symtab, DbiBuilder); - - // Add Section Map stream. - ArrayRef 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(C)) { + SC.Characteristics = SecChunk->Header->Characteristics; + SC.Imod = SecChunk->File->ModuleDBI->getModuleIndex(); + ArrayRef Contents = SecChunk->getContents(); + JamCRC CRC(0); + ArrayRef CharContents = makeArrayRef( + reinterpret_cast(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 OutputSections, + ArrayRef 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 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 SectionTable, - const llvm::codeview::DebugInfo *DI); +void createPDB(SymbolTable *Symtab, + llvm::ArrayRef OutputSections, + llvm::ArrayRef 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 coff::demangle(StringRef S) { +Optional 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 demangle(llvm::StringRef S); +llvm::Optional 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(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(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(File)) { - ObjectFiles.push_back(F); + if (auto *F = dyn_cast(File)) { + ObjFile::Instances.push_back(F); } else if (auto *F = dyn_cast(File)) { - BitcodeFiles.push_back(F); + BitcodeFile::Instances.push_back(F); } else if (auto *F = dyn_cast(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 Undefs; - for (auto &I : Symtab) { + SmallPtrSet Undefs; + DenseMap LocalImports; + + for (auto &I : SymMap) { Symbol *Sym = I.second; - auto *Undef = dyn_cast(Sym->body()); + auto *Undef = dyn_cast(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(D)) - memcpy(Sym->Body.buffer, D, sizeof(DefinedRegular)); + memcpy(Sym, D, sizeof(DefinedRegular)); else if (isa(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(Imp->body())) { - auto *D = cast(Imp->body()); - replaceBody(Sym, Name, D); - LocalImportChunks.push_back( - cast(Sym->body())->getChunk()); + if (Imp && isa(Imp)) { + auto *D = cast(Imp); + replaceSymbol(Sym, Name, D); + LocalImportChunks.push_back(cast(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(Sym, Name, 0); - Undefs.insert(Sym->body()); + replaceSymbol(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(": undefined symbol: " + B->getName()); - for (ObjectFile *File : ObjectFiles) - for (SymbolBody *Sym : File->getSymbols()) + errorOrWarn(": undefined symbol: " + B->getName()); + if (Symbol *Imp = LocalImports.lookup(B)) + warn(": 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 SymbolTable::insert(StringRef Name) { - Symbol *&Sym = Symtab[CachedHashStringRef(Name)]; + Symbol *&Sym = SymMap[CachedHashStringRef(Name)]; if (Sym) return {Sym, false}; - Sym = make(); + Sym = (Symbol *)make(); 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(F)) S->IsUsedInRegularObj = true; - if (WasInserted || (isa(S->body()) && IsWeakAlias)) { - replaceBody(S, Name); + if (WasInserted || (isa(S) && IsWeakAlias)) { + replaceSymbol(S, Name); return S; } - if (auto *L = dyn_cast(S->body())) { + if (auto *L = dyn_cast(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(S, F, Sym); + replaceSymbol(S, F, Sym); return; } - auto *U = dyn_cast(S->body()); + auto *U = dyn_cast(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(S->body()) || isa(S->body())) - replaceBody(S, N, Sym); - else if (!isa(S->body())) + if (WasInserted || isa(S) || isa(S)) + replaceSymbol(S, N, Sym); + else if (!isa(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(S->body()) || isa(S->body())) - replaceBody(S, N, VA); - else if (!isa(S->body())) + if (WasInserted || isa(S) || isa(S)) + replaceSymbol(S, N, VA); + else if (!isa(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(S->body()) || isa(S->body())) - replaceBody(S, N, C); - else if (!isa(S->body())) + if (WasInserted || isa(S) || isa(S)) + replaceSymbol(S, N, C); + else if (!isa(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(F)) S->IsUsedInRegularObj = true; - SymbolPreference SP = compareDefined(S, WasInserted, IsCOMDAT); - if (SP == SP_CONFLICT) { + if (WasInserted || !isa(S)) + replaceSymbol(S, F, N, /*IsCOMDAT*/ false, + /*IsExternal*/ true, Sym, C); + else reportDuplicate(S, F); - } else if (SP == SP_NEW) { - replaceBody(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 +SymbolTable::addComdat(InputFile *F, StringRef N, + const coff_symbol_generic *Sym) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(N); + if (!isa(F)) + S->IsUsedInRegularObj = true; + if (WasInserted || !isa(S)) { + replaceSymbol(S, F, N, /*IsCOMDAT*/ true, + /*IsExternal*/ true, Sym, nullptr); + return {S, true}; + } + if (!cast(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(F)) S->IsUsedInRegularObj = true; - if (WasInserted || !isa(S->body())) - replaceBody(S, F, N, Size, Sym, C); - else if (auto *DC = dyn_cast(S->body())) + if (WasInserted || !isa(S)) + replaceSymbol(S, F, N, Size, Sym, C); + else if (auto *DC = dyn_cast(S)) if (Size > DC->getSize()) - replaceBody(S, F, N, Size, Sym, C); + replaceSymbol(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(S->body()) || isa(S->body())) - replaceBody(S, N, F); - else if (!isa(S->body())) - reportDuplicate(S, nullptr); - return S; + if (WasInserted || isa(S) || isa(S)) { + replaceSymbol(S, N, F); + return cast(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(S->body()) || isa(S->body())) - replaceBody(S, Name, ID, Machine); - else if (!isa(S->body())) - reportDuplicate(S, nullptr); - return S; + if (WasInserted || isa(S) || isa(S)) { + replaceSymbol(S, Name, ID, Machine); + return cast(S); + } + + reportDuplicate(S, ID->File); + return nullptr; } std::vector SymbolTable::getChunks() { std::vector Res; - for (ObjectFile *File : ObjectFiles) { - std::vector &V = File->getChunks(); + for (ObjFile *File : ObjFile::Instances) { + ArrayRef 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(Sym->body())) + if (!isa(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(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 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(MemoryBufferRef(Object, "lto.tmp")); + auto *Obj = make(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 compileBitcodeFiles(); - // The writer needs to handle DLL import libraries specially in - // order to create the import descriptor table. - std::vector ImportFiles; - - // The writer needs to infer the machine type from the object files. - std::vector 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 + 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 LocalImportChunks; + // Iterates symbols in non-determinstic hash table order. + template void forEachSymbol(T Callback) { + for (auto &Pair : SymMap) + Callback(Pair.second); + } + private: std::pair insert(StringRef Name); StringRef findByPrefix(StringRef Prefix); - llvm::DenseMap Symtab; - - std::vector BitcodeFiles; + llvm::DenseMap SymMap; std::unique_ptr 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 S = coff::demangle(B.getName())) +std::string lld::toString(coff::Symbol &B) { + if (Optional 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(this); - cast(D->File)->getCOFFObj()->getSymbolName(D->Sym, Name); + cast(D->File)->getCOFFObj()->getSymbolName(D->Sym, Name); } return Name; } -InputFile *SymbolBody::getFile() { +InputFile *Symbol::getFile() { if (auto *Sym = dyn_cast(this)) return Sym->File; if (auto *Sym = dyn_cast(this)) @@ -52,9 +52,19 @@ InputFile *SymbolBody::getFile() { return nullptr; } +bool Symbol::isLive() const { + if (auto *R = dyn_cast(this)) + return R->getChunk()->isLive(); + if (auto *Imp = dyn_cast(this)) + return Imp->File->Live; + if (auto *Imp = dyn_cast(this)) + return Imp->WrappedSym->File->Live; + // Assume any other kind of symbol is live. + return true; +} + COFFSymbolRef DefinedCOFF::getCOFFSymbol() { - size_t SymSize = - cast(File)->getCOFFObj()->getSymbolTableEntrySize(); + size_t SymSize = cast(File)->getCOFFObj()->getSymbolTableEntrySize(); if (SymSize == sizeof(coff_symbol16)) return COFFSymbolRef(reinterpret_cast(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(A)->WeakAlias) + for (Symbol *A = WeakAlias; A; A = cast(A)->WeakAlias) if (auto *D = dyn_cast(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(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(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(Body.buffer); - } - const SymbolBody *body() const { return const_cast(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 -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(static_cast(nullptr)) == nullptr && - "Not a SymbolBody"); - new (S->Body.buffer) T(std::forward(Arg)...); -} - -inline Symbol *SymbolBody::symbol() { - assert(isExternal()); - return reinterpret_cast(reinterpret_cast(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(static_cast(nullptr)) == nullptr && + "Not a Symbol"); + new (S) T(std::forward(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 #include #include @@ -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(B + OutputSectionOff); - - DI->Signature.CVSignature = OMF::Signature::PDB70; + BuildId = reinterpret_cast(B + OutputSectionOff); // variable sized field (PDB Path) - auto *P = reinterpret_cast(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(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 void writeHeader(); - void fixSafeSEHSymbols(); + void createSEHTable(OutputSection *RData); void setSectionPermissions(); void writeSections(); - void sortExceptionTable(); void writeBuildId(); + void sortExceptionTable(); llvm::Optional createSymbol(Defined *D); size_t addEntryToStringTable(StringRef Str); @@ -132,8 +138,7 @@ private: uint32_t getSizeOfInitializedData(); std::map> binImports(); - SymbolTable *Symtab; - std::unique_ptr Buffer; + std::unique_ptr &Buffer; std::vector OutputSections; std::vector Strtab; std::vector OutputSymtab; @@ -145,6 +150,7 @@ private: Chunk *DebugDirectory = nullptr; std::vector DebugRecords; CVDebugRecordChunk *BuildId = nullptr; + Optional PreviousBuildId; ArrayRef 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 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(); } else { writeHeader(); } - fixSafeSEHSymbols(); writeSections(); sortExceptionTable(); writeBuildId(); if (!Config->PDBPath.empty() && Config->Debug) { - const llvm::codeview::DebugInfo *DI = nullptr; - if (Config->DebugTypes & static_cast(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(DebugRecords); - // TODO(compnerd) create a coffgrp entry if DebugType::CV is not enabled - if (Config->DebugTypes & static_cast(coff::DebugType::CV)) { - auto *Chunk = make(); - - 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(); + 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 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(B); - if (D && D->getChunk()->isLive()) - Handlers.insert(D); - } - } - - if (!Handlers.empty()) { - SEHTable = make(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 Writer::createSymbol(Defined *Def) { if (isa(Def)) return None; - if (auto *D = dyn_cast(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(Def)) - if (!Sym->File->Live) - return None; - - if (auto *Sym = dyn_cast(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(Def)) + if (D->getChunk()->isCodeView()) return None; coff_symbol16 Sym; @@ -468,7 +517,7 @@ Optional 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 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(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(B); + if (!D || D->WrittenToSymtab) + continue; + D->WrittenToSymtab = true; - if (Optional Sym = createSymbol(D)) - OutputSymtab.push_back(*Sym); + if (Optional 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 void Writer::writeHeader() { @@ -621,23 +676,21 @@ template 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 void Writer::writeHeader() { Dir[BASE_RELOCATION_TABLE].Size = Sec->getVirtualSize(); } if (Symbol *Sym = Symtab->findUnderscore("_tls_used")) { - if (Defined *B = dyn_cast(Sym->body())) { + if (Defined *B = dyn_cast(Sym)) { Dir[TLS_TABLE].RelativeVirtualAddress = B->getRVA(); Dir[TLS_TABLE].Size = Config->is64() ? sizeof(object::coff_tls_directory64) @@ -685,7 +738,7 @@ template void Writer::writeHeader() { Dir[DEBUG_DIRECTORY].Size = DebugDirectory->getSize(); } if (Symbol *Sym = Symtab->findUnderscore("_load_config_used")) { - if (auto *B = dyn_cast(Sym->body())) { + if (auto *B = dyn_cast(Sym)) { SectionChunk *SC = B->getChunk(); assert(B->getRVA() >= SC->getRVA()); uint64_t OffsetInChunk = B->getRVA() - SC->getRVA(); @@ -715,7 +768,7 @@ template void Writer::writeHeader() { SectionTable = ArrayRef( Buf - OutputSections.size() * sizeof(coff_section), Buf); - if (OutputSymtab.empty()) + if (OutputSymtab.empty() && Strtab.empty()) return; COFF->PointerToSymbolTable = PointerToSymbolTable; @@ -734,21 +787,40 @@ template 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 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(B)); + } + + if (Handlers.empty()) return; + + SEHTable = make(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(T, T->body()->getName(), SEHTable); - cast(C->body())->setVA(SEHTable->getSize() / 4); + replaceSymbol(T, T->getName(), SEHTable); + cast(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 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 &getChunks() { return Chunks; } + ArrayRef 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 lld::args::getStrings(opt::InputArgList &Args, int Id) { + std::vector 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 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 lld::args::getLines(MemoryBufferRef MB) { + SmallVector Arr; + MB.getBuffer().split(Arr, '\n'); + + std::vector 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/Common/ErrorHandler.cpp b/Common/ErrorHandler.cpp new file mode 100644 index 000000000000..18affce4d5a6 --- /dev/null +++ b/Common/ErrorHandler.cpp @@ -0,0 +1,118 @@ +//===- ErrorHandler.cpp ---------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lld/Common/ErrorHandler.h" + +#include "lld/Common/Threads.h" + +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/raw_ostream.h" +#include + +#if !defined(_MSC_VER) && !defined(__MINGW32__) +#include +#endif + +using namespace llvm; +using 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; + +// Prints "\n" or does nothing, depending on Msg contents of +// the previous call of this function. +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()).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); +} + +void ErrorHandler::print(StringRef S, raw_ostream::Colors C) { + *ErrorOS << LogName << ": "; + if (ColorDiagnostics) { + ErrorOS->changeColor(C, true); + *ErrorOS << S; + ErrorOS->resetColor(); + } else { + *ErrorOS << S; + } +} + +void ErrorHandler::log(const Twine &Msg) { + if (Verbose) { + std::lock_guard Lock(Mu); + *ErrorOS << LogName << ": " << Msg << "\n"; + } +} + +void ErrorHandler::message(const Twine &Msg) { + std::lock_guard Lock(Mu); + outs() << Msg << "\n"; + outs().flush(); +} + +void ErrorHandler::warn(const Twine &Msg) { + if (FatalWarnings) { + error(Msg); + return; + } + + std::lock_guard Lock(Mu); + newline(ErrorOS, Msg); + print("warning: ", raw_ostream::MAGENTA); + *ErrorOS << Msg << "\n"; +} + +void ErrorHandler::error(const Twine &Msg) { + std::lock_guard Lock(Mu); + newline(ErrorOS, Msg); + + if (ErrorLimit == 0 || ErrorCount < ErrorLimit) { + print("error: ", raw_ostream::RED); + *ErrorOS << Msg << "\n"; + } else if (ErrorCount == ErrorLimit) { + print("error: ", raw_ostream::RED); + *ErrorOS << ErrorLimitExceededMsg << "\n"; + if (ExitEarly) + exitLld(1); + } + + ++ErrorCount; +} + +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 lld::SpecificAllocBase::Instances; + +void lld::freeArena() { + for (SpecificAllocBase *Alloc : SpecificAllocBase::Instances) + Alloc->reset(); + BAlloc.Reset(); +} diff --git a/Common/Reproduce.cpp b/Common/Reproduce.cpp new file mode 100644 index 000000000000..7be4ea6bb98b --- /dev/null +++ b/Common/Reproduce.cpp @@ -0,0 +1,66 @@ +//===- Reproduce.cpp - Utilities for creating reproducers -----------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lld/Common/Reproduce.h" +#include "llvm/Option/Arg.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" + +using namespace lld; +using namespace llvm; +using namespace llvm::sys; + +// Makes a given pathname an absolute path first, and then remove +// beginning /. For example, "../foo.o" is converted to "home/john/foo.o", +// assuming that the current directory is "/home/john/bar". +// Returned string is a forward slash separated path even on Windows to avoid +// a mess with backslash-as-escape and backslash-as-path-separator. +std::string lld::relativeToRoot(StringRef Path) { + SmallString<128> Abs = Path; + if (fs::make_absolute(Abs)) + return Path; + path::remove_dots(Abs, /*remove_dot_dot=*/true); + + // This is Windows specific. root_name() returns a drive letter + // (e.g. "c:") or a UNC name (//net). We want to keep it as part + // of the result. + SmallString<128> Res; + StringRef Root = path::root_name(Abs); + if (Root.endswith(":")) + Res = Root.drop_back(); + else if (Root.startswith("//")) + Res = Root.substr(2); + + path::append(Res, path::relative_path(Abs)); + return path::convert_to_slash(Res); +} + +// Quote a given string if it contains a space character. +std::string lld::quote(StringRef S) { + if (S.contains(' ')) + return ("\"" + S + "\"").str(); + return S; +} + +std::string lld::rewritePath(StringRef S) { + if (fs::exists(S)) + return relativeToRoot(S); + return S; +} + +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) + 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 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/Common/TargetOptionsCommandFlags.cpp b/Common/TargetOptionsCommandFlags.cpp new file mode 100644 index 000000000000..e8e582f4c256 --- /dev/null +++ b/Common/TargetOptionsCommandFlags.cpp @@ -0,0 +1,32 @@ +//===-- TargetOptionsCommandFlags.cpp ---------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file exists as a place for global variables defined in LLVM's +// 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/Common/TargetOptionsCommandFlags.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.def, which +// would lead to multiple definitions of the command line flags. +llvm::TargetOptions lld::InitTargetOptionsFromCodeGenFlags() { + return ::InitTargetOptionsFromCodeGenFlags(); +} + +llvm::Optional 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/Common/Version.cpp b/Common/Version.cpp new file mode 100644 index 000000000000..6226c9a2fac6 --- /dev/null +++ b/Common/Version.cpp @@ -0,0 +1,43 @@ +//===- lib/Common/Version.cpp - LLD Version Number ---------------*- C++-=====// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines several version-related utility functions for LLD. +// +//===----------------------------------------------------------------------===// + +#include "lld/Common/Version.h" + +using namespace llvm; + +// Returns an SVN repository path, which is usually "trunk". +static std::string getRepositoryPath() { + StringRef S = LLD_REPOSITORY_STRING; + size_t Pos = S.find("lld/"); + if (Pos != StringRef::npos) + return S.substr(Pos + 4); + return S; +} + +// Returns an SVN repository name, e.g., " (trunk 284614)" +// or an empty string if no repository info is available. +static std::string getRepository() { + std::string Repo = getRepositoryPath(); + std::string Rev = LLD_REVISION_STRING; + + if (Repo.empty() && Rev.empty()) + return ""; + if (!Repo.empty() && !Rev.empty()) + return " (" + Repo + " " + Rev + ")"; + return " (" + Repo + Rev + ")"; +} + +// Returns a version string, e.g., "LLD 4.0 (lld/trunk 284614)". +std::string lld::getLLDVersion() { + return "LLD " + std::string(LLD_VERSION_STRING) + getRepository(); +} 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 + +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(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>(File); + for (Symbol *B : F->getLocalSymbols()) { + auto *Def = dyn_cast(B); + if (!Def) + continue; + if (!IsCodeMapSymbol(Def) && !IsDataMapSymbol(Def)) + continue; + if (auto *Sec = dyn_cast(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 &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 &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 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(A) && + !isa(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 &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(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 +AArch64Err843419Patcher::patchInputSectionDescription( + InputSectionDescription &ISD) { + std::vector Patches; + for (InputSection *IS : ISD.Sections) { + // LLD doesn't use the erratum sequence in SyntheticSections. + if (isa(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 &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(BC)) { + std::vector 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 +#include + +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 + patchInputSectionDescription(InputSectionDescription &ISD); + + void insertPatches(InputSectionDescription &ISD, + std::vector &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> 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>(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.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(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(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 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 MIPS::MIPS() { } } +template uint32_t MIPS::calcEFlags() const { + return calcMipsEFlags(); +} + template -RelExpr MIPS::getRelExpr(uint32_t Type, const SymbolBody &S, +RelExpr MIPS::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::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::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 bool MIPS::isPicRel(uint32_t Type) const { +template bool MIPS::isPicRel(RelType Type) const { return Type == R_MIPS_32 || Type == R_MIPS_64; } -template uint32_t MIPS::getDynRel(uint32_t Type) const { +template RelType MIPS::getDynRel(RelType Type) const { return RelativeRel; } template -void MIPS::writeGotPlt(uint8_t *Buf, const SymbolBody &) const { - write32(Buf, InX::Plt->getVA()); -} - -template -static int64_t getPcRelocAddend(const uint8_t *Loc) { - uint32_t Instr = read32(Loc); - uint32_t Mask = 0xffffffff >> (32 - BSIZE); - return SignExtend64((Instr & Mask) << SHIFT); +void MIPS::writeGotPlt(uint8_t *Buf, const Symbol &) const { + uint64_t VA = InX::Plt->getVA(); + if (isMicroMips()) + VA |= 1; + write32(Buf, VA); } -template -static void applyMipsPcReloc(uint8_t *Loc, uint32_t Type, uint64_t V) { - uint32_t Mask = 0xffffffff >> (32 - BSIZE); - uint32_t Instr = read32(Loc); - if (SHIFT > 0) - checkAlignment<(1 << SHIFT)>(Loc, V, Type); - checkInt(Loc, V, Type); - write32(Loc, (Instr & ~Mask) | ((V >> SHIFT) & Mask)); +template 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(Loc); + if (E == support::little) + return (V << 16) | (V >> 16); + return V; } -template static void writeMipsHi16(uint8_t *Loc, uint64_t V) { +template +static void writeRelocation(uint8_t *Loc, uint64_t V, uint8_t BitsSize, + uint8_t Shift) { uint32_t Instr = read32(Loc); - uint16_t Res = ((V + 0x8000) >> 16) & 0xffff; - write32(Loc, (Instr & 0xffff0000) | Res); + uint32_t Mask = 0xffffffff >> (32 - BitsSize); + uint32_t Data = (Instr & ~Mask) | ((V >> Shift) & Mask); + write32(Loc, Data); } -template static void writeMipsHigher(uint8_t *Loc, uint64_t V) { - uint32_t Instr = read32(Loc); - uint16_t Res = ((V + 0x80008000) >> 32) & 0xffff; - write32(Loc, (Instr & 0xffff0000) | Res); -} +template +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 static void writeMipsHighest(uint8_t *Loc, uint64_t V) { - uint32_t Instr = read32(Loc); - uint16_t Res = ((V + 0x800080008000) >> 48) & 0xffff; - write32(Loc, (Instr & 0xffff0000) | Res); -} + writeRelocation(Loc, V, BitsSize, Shift); -template static void writeMipsLo16(uint8_t *Loc, uint64_t V) { - uint32_t Instr = read32(Loc); - write32(Loc, (Instr & 0xffff0000) | (V & 0xffff)); + if (E == support::little) + std::swap(Words[0], Words[1]); } -template static bool isMipsR6() { - const auto &FirstObj = cast>(*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 +static void writeMicroRelocation16(uint8_t *Loc, uint64_t V, uint8_t BitsSize, + uint8_t Shift) { + uint16_t Instr = read16(Loc); + uint16_t Mask = 0xffff >> (16 - BitsSize); + uint16_t Data = (Instr & ~Mask) | ((V >> Shift) & Mask); + write16(Loc, Data); } template void MIPS::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(Buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - . + write16(Buf + 4, 0xff23); // lw $25, 0($3) + write16(Buf + 8, 0x0535); // subu16 $2, $2, $3 + write16(Buf + 10, 0x2525); // srl16 $2, $2, 2 + write16(Buf + 12, 0x3302); // addiu $24, $2, -2 + write16(Buf + 14, 0xfffe); + write16(Buf + 16, 0x0dff); // move $15, $31 + if (isMipsR6()) { + write16(Buf + 18, 0x0f83); // move $28, $3 + write16(Buf + 20, 0x472b); // jalrc $25 + write16(Buf + 22, 0x0c00); // nop + relocateOne(Buf, R_MICROMIPS_PC19_S2, GotPlt - Plt); + } else { + write16(Buf + 18, 0x45f9); // jalrc $25 + write16(Buf + 20, 0x0f83); // move $28, $3 + write16(Buf + 22, 0x0c00); // nop + relocateOne(Buf, R_MICROMIPS_PC23_S2, GotPlt - Plt); + } + return; + } + if (Config->MipsN32Abi) { write32(Buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) write32(Buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14) write32(Buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) write32(Buf + 12, 0x030ec023); // subu $24, $24, $14 + write32(Buf + 16, 0x03e07825); // move $15, $31 + write32(Buf + 20, 0x0018c082); // srl $24, $24, 2 + } else if (ELFT::Is64Bits) { + write32(Buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) + write32(Buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14) + write32(Buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) + write32(Buf + 12, 0x030ec023); // subu $24, $24, $14 + write32(Buf + 16, 0x03e07825); // move $15, $31 + write32(Buf + 20, 0x0018c0c2); // srl $24, $24, 3 } else { write32(Buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0]) write32(Buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28) write32(Buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0]) write32(Buf + 12, 0x031cc023); // subu $24, $24, $28 + write32(Buf + 16, 0x03e07825); // move $15, $31 + write32(Buf + 20, 0x0018c082); // srl $24, $24, 2 } - write32(Buf + 16, 0x03e07825); // move $15, $31 - write32(Buf + 20, 0x0018c082); // srl $24, $24, 2 write32(Buf + 24, 0x0320f809); // jalr $25 write32(Buf + 28, 0x2718fffe); // subu $24, $24, 2 uint64_t GotPlt = InX::GotPlt->getVA(); - writeMipsHi16(Buf, GotPlt); - writeMipsLo16(Buf + 4, GotPlt); - writeMipsLo16(Buf + 8, GotPlt); + writeRelocation(Buf, GotPlt + 0x8000, 16, 16); + writeRelocation(Buf + 4, GotPlt, 16, 0); + writeRelocation(Buf + 8, GotPlt, 16, 0); } template @@ -217,25 +310,45 @@ void MIPS::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(Buf, 0x7840); // addiupc $2, (GOTPLT) - . + write16(Buf + 4, 0xff22); // lw $25, 0($2) + write16(Buf + 8, 0x0f02); // move $24, $2 + write16(Buf + 10, 0x4723); // jrc $25 / jr16 $25 + relocateOne(Buf, R_MICROMIPS_PC19_S2, GotPltEntryAddr - PltEntryAddr); + } else { + write16(Buf, 0x7900); // addiupc $2, (GOTPLT) - . + write16(Buf + 4, 0xff22); // lw $25, 0($2) + write16(Buf + 8, 0x4599); // jrc $25 / jr16 $25 + write16(Buf + 10, 0x0f02); // move $24, $2 + relocateOne(Buf, R_MICROMIPS_PC23_S2, GotPltEntryAddr - PltEntryAddr); + } + return; + } + write32(Buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry) write32(Buf + 4, 0x8df90000); // l[wd] $25, %lo(.got.plt entry)($15) - // jr $25 - write32(Buf + 8, isMipsR6() ? 0x03200009 : 0x03200008); + write32(Buf + 8, isMipsR6() ? 0x03200009 : 0x03200008); // jr $25 write32(Buf + 12, 0x25f80000); // addiu $24, $15, %lo(.got.plt entry) - writeMipsHi16(Buf, GotPltEntryAddr); - writeMipsLo16(Buf + 4, GotPltEntryAddr); - writeMipsLo16(Buf + 12, GotPltEntryAddr); + writeRelocation(Buf, GotPltEntryAddr + 0x8000, 16, 16); + writeRelocation(Buf + 4, GotPltEntryAddr, 16, 0); + writeRelocation(Buf + 12, GotPltEntryAddr, 16, 0); } template -bool MIPS::needsThunk(RelExpr Expr, uint32_t Type, const InputFile *File, - const SymbolBody &S) const { +bool MIPS::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>(File); if (!F) @@ -243,18 +356,16 @@ bool MIPS::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(&S); + auto *D = dyn_cast(&S); // LA25 is required if target file has PIC code // or target symbol is a PIC symbol. - return D && D->isMipsPIC(); + return D && isMipsPIC(D); } template -int64_t MIPS::getImplicitAddend(const uint8_t *Buf, uint32_t Type) const { +int64_t MIPS::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::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(Buf) & 0x3ffffff) << 2); + return SignExtend64<28>(read32(Buf) << 2); + case R_MIPS_GOT16: + case R_MIPS_HI16: + case R_MIPS_PCHI16: + return SignExtend64<16>(read32(Buf)) << 16; case R_MIPS_GPREL16: case R_MIPS_LO16: case R_MIPS_PCLO16: @@ -273,21 +388,53 @@ int64_t MIPS::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(Buf)); + case R_MICROMIPS_GOT16: + case R_MICROMIPS_HI16: + return SignExtend64<16>(readShuffle(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(Buf)); + case R_MICROMIPS_GPREL7_S2: + return SignExtend64<9>(readShuffle(Buf) << 2); case R_MIPS_PC16: - return getPcRelocAddend(Buf); + return SignExtend64<18>(read32(Buf) << 2); case R_MIPS_PC19_S2: - return getPcRelocAddend(Buf); + return SignExtend64<21>(read32(Buf) << 2); case R_MIPS_PC21_S2: - return getPcRelocAddend(Buf); + return SignExtend64<23>(read32(Buf) << 2); case R_MIPS_PC26_S2: - return getPcRelocAddend(Buf); + return SignExtend64<28>(read32(Buf) << 2); case R_MIPS_PC32: - return getPcRelocAddend(Buf); + return SignExtend64<32>(read32(Buf)); + case R_MICROMIPS_26_S1: + return SignExtend64<27>(readShuffle(Buf) << 1); + case R_MICROMIPS_PC7_S1: + return SignExtend64<8>(read16(Buf) << 1); + case R_MICROMIPS_PC10_S1: + return SignExtend64<11>(read16(Buf) << 1); + case R_MICROMIPS_PC16_S1: + return SignExtend64<17>(readShuffle(Buf) << 1); + case R_MICROMIPS_PC18_S3: + return SignExtend64<21>(readShuffle(Buf) << 3); + case R_MICROMIPS_PC19_S2: + return SignExtend64<21>(readShuffle(Buf) << 2); + case R_MICROMIPS_PC21_S1: + return SignExtend64<22>(readShuffle(Buf) << 1); + case R_MICROMIPS_PC23_S2: + return SignExtend64<25>(readShuffle(Buf) << 2); + case R_MICROMIPS_PC26_S1: + return SignExtend64<27>(readShuffle(Buf) << 1); + default: + return 0; } } static std::pair -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 -void MIPS::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const { +void MIPS::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::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const { write64(Loc, Val); break; case R_MIPS_26: - write32(Loc, (read32(Loc) & ~0x3ffffff) | ((Val >> 2) & 0x3ffffff)); + writeRelocation(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(Loc, Val); - else { + if (Config->Relocatable) { + writeRelocation(Loc, Val + 0x8000, 16, 16); + } else { checkInt<16>(Loc, Val, Type); - writeMipsLo16(Loc, Val); + writeRelocation(Loc, Val, 16, 0); } break; + case R_MICROMIPS_GOT16: + if (Config->Relocatable) { + writeMicroRelocation32(Loc, Val + 0x8000, 16, 16); + } else { + checkInt<16>(Loc, Val, Type); + writeMicroRelocation32(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(Loc, Val); + writeRelocation(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(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(Loc, Val, 16, 0); + break; + case R_MICROMIPS_GPREL7_S2: + checkInt<7>(Loc, Val, Type); + writeMicroRelocation32(Loc, Val, 7, 2); break; case R_MIPS_CALL_HI16: case R_MIPS_GOT_HI16: @@ -376,40 +563,107 @@ void MIPS::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(Loc, Val); + writeRelocation(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(Loc, Val + 0x8000, 16, 16); break; case R_MIPS_HIGHER: - writeMipsHigher(Loc, Val); + writeRelocation(Loc, Val + 0x80008000, 16, 32); break; case R_MIPS_HIGHEST: - writeMipsHighest(Loc, Val); + writeRelocation(Loc, Val + 0x800080008000, 16, 48); + break; + case R_MICROMIPS_HIGHER: + writeMicroRelocation32(Loc, Val + 0x80008000, 16, 32); + break; + case R_MICROMIPS_HIGHEST: + writeMicroRelocation32(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(Loc, Type, Val); + checkAlignment<4>(Loc, Val, Type); + checkInt<18>(Loc, Val, Type); + writeRelocation(Loc, Val, 16, 2); break; case R_MIPS_PC19_S2: - applyMipsPcReloc(Loc, Type, Val); + checkAlignment<4>(Loc, Val, Type); + checkInt<21>(Loc, Val, Type); + writeRelocation(Loc, Val, 19, 2); break; case R_MIPS_PC21_S2: - applyMipsPcReloc(Loc, Type, Val); + checkAlignment<4>(Loc, Val, Type); + checkInt<23>(Loc, Val, Type); + writeRelocation(Loc, Val, 21, 2); break; case R_MIPS_PC26_S2: - applyMipsPcReloc(Loc, Type, Val); + checkAlignment<4>(Loc, Val, Type); + checkInt<28>(Loc, Val, Type); + writeRelocation(Loc, Val, 26, 2); break; case R_MIPS_PC32: - applyMipsPcReloc(Loc, Type, Val); + writeRelocation(Loc, Val, 32, 0); + break; + case R_MICROMIPS_26_S1: + case R_MICROMIPS_PC26_S1: + checkInt<27>(Loc, Val, Type); + writeMicroRelocation32(Loc, Val, 26, 1); + break; + case R_MICROMIPS_PC7_S1: + checkInt<8>(Loc, Val, Type); + writeMicroRelocation16(Loc, Val, 7, 1); + break; + case R_MICROMIPS_PC10_S1: + checkInt<11>(Loc, Val, Type); + writeMicroRelocation16(Loc, Val, 10, 1); + break; + case R_MICROMIPS_PC16_S1: + checkInt<17>(Loc, Val, Type); + writeMicroRelocation32(Loc, Val, 16, 1); + break; + case R_MICROMIPS_PC18_S3: + checkInt<21>(Loc, Val, Type); + writeMicroRelocation32(Loc, Val, 18, 3); + break; + case R_MICROMIPS_PC19_S2: + checkInt<21>(Loc, Val, Type); + writeMicroRelocation32(Loc, Val, 19, 2); + break; + case R_MICROMIPS_PC21_S1: + checkInt<22>(Loc, Val, Type); + writeMicroRelocation32(Loc, Val, 21, 1); + break; + case R_MICROMIPS_PC23_S2: + checkInt<25>(Loc, Val, Type); + writeMicroRelocation32(Loc, Val, 23, 2); break; default: error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); } } -template -bool MIPS::usesOnlyLowPageBits(uint32_t Type) const { - return Type == R_MIPS_LO16 || Type == R_MIPS_GOT_OFST; +template bool MIPS::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 bool elf::isMipsPIC(const Defined *Sym) { + typedef typename ELFT::Ehdr Elf_Ehdr; + if (!Sym->Section || !Sym->isFunc()) + return false; + + auto *Sec = cast(Sym->Section); + const Elf_Ehdr *Hdr = Sec->template getFile()->getObj().getHeader(); + return (Sym->StOther & STO_MIPS_MIPS16) == STO_MIPS_PIC || + (Hdr->e_flags & EF_MIPS_PIC); } template TargetInfo *elf::getMipsTargetInfo() { @@ -421,3 +675,8 @@ template TargetInfo *elf::getMipsTargetInfo(); template TargetInfo *elf::getMipsTargetInfo(); template TargetInfo *elf::getMipsTargetInfo(); template TargetInfo *elf::getMipsTargetInfo(); + +template bool elf::isMipsPIC(const Defined *); +template bool elf::isMipsPIC(const Defined *); +template bool elf::isMipsPIC(const Defined *); +template bool elf::isMipsPIC(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 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 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 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 Files) { return Ret; } -template uint32_t elf::getMipsEFlags() { +template uint32_t elf::calcMipsEFlags() { std::vector V; - for (elf::ObjectFile *F : Symtab::X->getObjectFiles()) - V.push_back({F->getName(), F->getObj().getHeader()->e_flags}); + for (InputFile *F : ObjectFiles) + V.push_back({F, cast>(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(); -template uint32_t elf::getMipsEFlags(); -template uint32_t elf::getMipsEFlags(); -template uint32_t elf::getMipsEFlags(); +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(); +template uint32_t elf::calcMipsEFlags(); +template uint32_t elf::calcMipsEFlags(); +template uint32_t elf::calcMipsEFlags(); 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 toAddr16Rel(uint32_t Type, uint64_t Val) { +static std::pair toAddr16Rel(RelType Type, uint64_t Val) { uint64_t V = Val - PPC64TocOffset; switch (Type) { case R_PPC64_TOC16: @@ -142,7 +142,7 @@ static std::pair 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 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 X86_64::X86_64() { } template -RelExpr X86_64::getRelExpr(uint32_t Type, const SymbolBody &S, +RelExpr X86_64::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::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 void X86_64::writeGotPltHeader(uint8_t *Buf) const { } template -void X86_64::writeGotPlt(uint8_t *Buf, const SymbolBody &S) const { - // See comments in X86TargetInfo::writeGotPlt. +void X86_64::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::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16); } -template bool X86_64::isPicRel(uint32_t Type) const { +template bool X86_64::isPicRel(RelType Type) const { return Type != R_X86_64_PC32 && Type != R_X86_64_32 && Type != R_X86_64_TPOFF32; } template -void X86_64::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, +void X86_64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { // Convert // .byte 0x66 @@ -186,7 +185,7 @@ void X86_64::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, } template -void X86_64::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, +void X86_64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const { // Convert // .byte 0x66 @@ -211,7 +210,7 @@ void X86_64::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 -void X86_64::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, +void X86_64::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::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, } template -void X86_64::relaxTlsLdToLe(uint8_t *Loc, uint32_t Type, +void X86_64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { // Convert // leaq bar@tlsld(%rip), %rdi @@ -283,8 +282,7 @@ void X86_64::relaxTlsLdToLe(uint8_t *Loc, uint32_t Type, } template -void X86_64::relocateOne(uint8_t *Loc, uint32_t Type, - uint64_t Val) const { +void X86_64::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::relocateOne(uint8_t *Loc, uint32_t Type, write64le(Loc, Val); break; default: - llvm_unreachable("unexpected relocation"); + error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); } } template -RelExpr X86_64::adjustRelaxExpr(uint32_t Type, const uint8_t *Data, +RelExpr X86_64::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 SectionStartMap; + llvm::StringRef Chroot; llvm::StringRef DynamicLinker; llvm::StringRef Entry; llvm::StringRef Emulation; @@ -103,15 +99,18 @@ struct Configuration { std::vector SearchPaths; std::vector SymbolOrderingFile; std::vector Undefined; + std::vector DynamicList; std::vector VersionScriptGlobals; std::vector VersionScriptLocals; std::vector BuildIdVector; - llvm::MapVector 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 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 elf::SpecificAllocBase::Instances; - static void setConfigs(); bool elf::link(ArrayRef 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(); Driver = make(); Script = make(); + Symtab = make(); 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 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 parseEmulation(StringRef Emul) { std::vector> static getArchiveMembers( MemoryBufferRef MB) { std::unique_ptr File = - check(Archive::create(MB), + CHECK(Archive::create(MB), MB.getBufferIdentifier() + ": failed to parse archive"); std::vector> V; Error Err = Error::success(); + bool AddToTar = File->isThin() && Tar; for (const ErrorOr &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 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(P.first, Path, P.second)); + Files.push_back(make(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(MBRef, "", 0)); + Files.push_back(make(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 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 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 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 ArgsArr, bool CanExitEarly) { inferMachineType(); setConfigs(); checkOptions(Args); - if (ErrorCount) + if (errorCount()) return; switch (Config->EKind) { @@ -396,36 +394,19 @@ void LinkerDriver::main(ArrayRef 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 getArgs(opt::InputArgList &Args, int Id) { - std::vector V; - for (auto *Arg : Args.filtered(Id)) - V.push_back(Arg->getValue()); - return V; -} - static std::string getRpath(opt::InputArgList &Args) { - std::vector V = getArgs(Args, OPT_rpath); + std::vector 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 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 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=