From d93e1dfac8711cfed1a9d9cd1876a788b83945cd Mon Sep 17 00:00:00 2001 From: Dimitry Andric Date: Mon, 2 Jan 2017 19:19:15 +0000 Subject: Vendor import of lld trunk r290819: https://llvm.org/svn/llvm-project/lld/trunk@290819 --- CMakeLists.txt | 61 +- COFF/CMakeLists.txt | 18 +- COFF/Chunks.cpp | 32 +- COFF/Chunks.h | 7 +- COFF/Config.h | 28 +- COFF/DLL.cpp | 2 +- COFF/Driver.cpp | 428 ++-- COFF/Driver.h | 38 +- COFF/DriverUtils.cpp | 194 +- COFF/Error.cpp | 21 +- COFF/Error.h | 17 + COFF/ICF.cpp | 278 +-- COFF/InputFiles.cpp | 179 +- COFF/InputFiles.h | 68 +- COFF/Librarian.cpp | 39 +- COFF/MarkLive.cpp | 6 +- COFF/Memory.h | 52 + COFF/ModuleDef.cpp | 12 +- COFF/Options.td | 8 +- COFF/PDB.cpp | 214 +- COFF/PDB.h | 25 + COFF/Strings.cpp | 30 + COFF/Strings.h | 23 + COFF/SymbolTable.cpp | 494 +++-- COFF/SymbolTable.h | 66 +- COFF/Symbols.cpp | 178 +- COFF/Symbols.h | 142 +- COFF/Writer.cpp | 198 +- COFF/Writer.h | 4 +- ELF/CMakeLists.txt | 17 +- ELF/Config.h | 67 +- ELF/Driver.cpp | 553 ++++-- ELF/Driver.h | 51 +- ELF/DriverUtils.cpp | 222 +-- ELF/EhFrame.cpp | 147 +- ELF/EhFrame.h | 8 +- ELF/Error.cpp | 91 +- ELF/Error.h | 44 +- ELF/GdbIndex.cpp | 205 ++ ELF/GdbIndex.h | 99 + ELF/ICF.cpp | 508 ++--- ELF/InputFiles.cpp | 727 ++++--- ELF/InputFiles.h | 123 +- ELF/InputSection.cpp | 677 ++++--- ELF/InputSection.h | 295 +-- ELF/LTO.cpp | 376 +--- ELF/LTO.h | 24 +- ELF/LinkerScript.cpp | 2060 ++++++++++++++++---- ELF/LinkerScript.h | 275 ++- ELF/MarkLive.cpp | 150 +- ELF/Memory.h | 67 + ELF/Mips.cpp | 369 ++++ ELF/Options.td | 111 +- ELF/OutputSections.cpp | 1768 +++-------------- ELF/OutputSections.h | 659 +------ ELF/Relocations.cpp | 501 +++-- ELF/Relocations.h | 49 +- ELF/ScriptParser.cpp | 133 +- ELF/ScriptParser.h | 24 +- ELF/Strings.cpp | 80 +- ELF/Strings.h | 65 +- ELF/SymbolListFile.cpp | 168 -- ELF/SymbolListFile.h | 27 - ELF/SymbolTable.cpp | 581 +++--- ELF/SymbolTable.h | 91 +- ELF/Symbols.cpp | 176 +- ELF/Symbols.h | 153 +- ELF/SyntheticSections.cpp | 1990 +++++++++++++++++++ ELF/SyntheticSections.h | 747 +++++++ ELF/Target.cpp | 608 ++++-- ELF/Target.h | 20 +- ELF/Threads.h | 90 + ELF/Thunks.cpp | 37 +- ELF/Writer.cpp | 1742 ++++++++++------- ELF/Writer.h | 48 +- cmake/modules/AddLLD.cmake | 45 + docs/Driver.rst | 2 +- docs/NewLLD.rst | 6 +- docs/ReleaseNotes.rst | 101 +- docs/conf.py | 4 +- docs/windows_support.rst | 2 +- include/lld/Config/Version.h | 34 +- include/lld/Config/Version.inc.in | 1 + include/lld/Core/Atom.h | 16 +- include/lld/Core/DefinedAtom.h | 4 - include/lld/Core/LinkingContext.h | 21 +- include/lld/Core/Node.h | 11 +- include/lld/Core/Parallel.h | 69 +- include/lld/Core/Pass.h | 13 +- include/lld/Core/PassManager.h | 2 +- include/lld/Core/Reader.h | 17 +- include/lld/Core/Reference.h | 10 +- include/lld/Core/Reproduce.h | 73 + include/lld/Core/Simple.h | 83 +- include/lld/Core/SymbolTable.h | 10 - include/lld/Driver/Driver.h | 2 +- include/lld/ReaderWriter/MachOLinkingContext.h | 6 +- include/lld/Support/Memory.h | 63 + lib/Config/Version.cpp | 52 +- lib/Core/CMakeLists.txt | 1 + lib/Core/DefinedAtom.cpp | 12 - lib/Core/Error.cpp | 6 +- lib/Core/File.cpp | 5 +- lib/Core/LinkingContext.cpp | 11 +- lib/Core/Reader.cpp | 10 +- lib/Core/Reproduce.cpp | 128 ++ lib/Core/Resolver.cpp | 2 +- lib/Core/SymbolTable.cpp | 32 +- lib/Core/Writer.cpp | 9 +- lib/Driver/DarwinLdDriver.cpp | 68 +- lib/ReaderWriter/FileArchive.cpp | 23 +- lib/ReaderWriter/MachO/ArchHandler_arm.cpp | 20 +- lib/ReaderWriter/MachO/ArchHandler_arm64.cpp | 34 +- lib/ReaderWriter/MachO/ArchHandler_x86.cpp | 4 +- lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp | 21 +- lib/ReaderWriter/MachO/CMakeLists.txt | 2 + lib/ReaderWriter/MachO/CompactUnwindPass.cpp | 4 +- lib/ReaderWriter/MachO/DebugInfo.h | 106 + lib/ReaderWriter/MachO/File.h | 18 +- lib/ReaderWriter/MachO/GOTPass.cpp | 2 +- lib/ReaderWriter/MachO/LayoutPass.cpp | 2 +- lib/ReaderWriter/MachO/MachOLinkingContext.cpp | 63 +- lib/ReaderWriter/MachO/MachONormalizedFile.h | 3 +- .../MachO/MachONormalizedFileBinaryReader.cpp | 14 +- .../MachO/MachONormalizedFileBinaryUtils.h | 9 +- .../MachO/MachONormalizedFileBinaryWriter.cpp | 163 +- .../MachO/MachONormalizedFileFromAtoms.cpp | 220 ++- .../MachO/MachONormalizedFileToAtoms.cpp | 372 +++- lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp | 2 + lib/ReaderWriter/MachO/ObjCPass.cpp | 2 +- lib/ReaderWriter/MachO/ShimPass.cpp | 4 +- lib/ReaderWriter/MachO/StubsPass.cpp | 6 +- lib/ReaderWriter/MachO/TLVPass.cpp | 2 +- lib/ReaderWriter/YAML/ReaderWriterYAML.cpp | 31 +- test/CMakeLists.txt | 6 +- test/COFF/Inputs/cl-gl.obj | Bin 0 -> 3734 bytes test/COFF/Inputs/far-arm-thumb-abs.s | 2 + test/COFF/Inputs/lto-lazy-reference-dummy.ll | 6 + test/COFF/Inputs/lto-lazy-reference-quadruple.ll | 16 + test/COFF/arm-thumb-branch-error.s | 10 + test/COFF/cl-gl.test | 4 + test/COFF/common.test | 2 +- test/COFF/conflict-mangled.test | 37 + test/COFF/conflict.test | 2 +- test/COFF/delayimports32.test | 6 +- test/COFF/dll.test | 1 - test/COFF/dumppdb.test | 181 ++ test/COFF/export-exe.test | 1 - test/COFF/export.test | 1 - test/COFF/export32.test | 1 - test/COFF/icf-associative.test | 104 + test/COFF/include2.test | 2 +- test/COFF/linkrepro.test | 38 + test/COFF/lldmap.test | 6 +- test/COFF/lto-lazy-reference.ll | 21 + test/COFF/noentry.test | 1 - test/COFF/order.test | 2 +- test/COFF/pdb.test | 421 ++++ test/COFF/reloc-arm.test | 25 +- test/COFF/rsds.test | 113 ++ test/COFF/symtab.test | 52 +- test/ELF/Inputs/arm-attributes1.s | 29 + test/ELF/Inputs/arm-exidx-cantunwind.s | 40 + test/ELF/Inputs/arm-shared.s | 8 + test/ELF/Inputs/arm-tls-get-addr.s | 13 + test/ELF/Inputs/bad-archive.a | 2 + test/ELF/Inputs/comment-gc.s | 1 + test/ELF/Inputs/conflict-debug.s | 5 + test/ELF/Inputs/gdb-index-a.elf | Bin 0 -> 3040 bytes test/ELF/Inputs/gdb-index-b.elf | Bin 0 -> 3048 bytes test/ELF/Inputs/i386-tls-got.s | 5 + test/ELF/Inputs/icf-non-mergeable.s | 8 + test/ELF/Inputs/invalid-binding.elf | Bin 536 -> 0 bytes test/ELF/Inputs/invalid-cie-version2.elf | Bin 1128 -> 0 bytes test/ELF/Inputs/invalid-data-encoding.a | Bin 156 -> 0 bytes test/ELF/Inputs/invalid-file-class.a | Bin 156 -> 0 bytes test/ELF/Inputs/invalid-multiple-eh-relocs.elf | Bin 784 -> 0 bytes test/ELF/Inputs/invalid-section-index.elf | Bin 544 -> 0 bytes test/ELF/Inputs/invalid-shentsize-zero.elf | Bin 512 -> 0 bytes test/ELF/Inputs/invalid-shstrndx.so | Bin 13032 -> 0 bytes test/ELF/Inputs/invalid-symtab-sh_info.elf | Bin 512 -> 0 bytes test/ELF/Inputs/mips-concatenated-abiflags.o | Bin 0 -> 1084 bytes test/ELF/Inputs/mips-fnpic.s | 6 + test/ELF/Inputs/mips-fpic.s | 6 + test/ELF/Inputs/mips-gp0-non-zero.o | Bin 0 -> 848 bytes test/ELF/Inputs/mips-n32-rels.o | Bin 0 -> 1092 bytes test/ELF/Inputs/relocatable-tls.s | 1 + test/ELF/Inputs/relocation-relative-absolute.s | 2 + test/ELF/Inputs/shared2-x86-64.s | 9 + test/ELF/Inputs/shf-info-link.test | 21 + test/ELF/Inputs/startstop-shared2.s | 2 + test/ELF/Inputs/symbol-override.s | 2 +- test/ELF/Inputs/uabs_label.s | 4 + test/ELF/Inputs/undef-debug.s | 11 + test/ELF/Inputs/use-bar.s | 2 + test/ELF/Inputs/verdef-defaultver.s | 4 +- test/ELF/aarch64-abs16.s | 2 +- test/ELF/aarch64-abs32.s | 6 +- test/ELF/aarch64-condb-reloc.s | 98 +- test/ELF/aarch64-copy.s | 34 +- test/ELF/aarch64-copy2.s | 2 +- test/ELF/aarch64-data-relocs.s | 8 +- test/ELF/aarch64-fpic-abs16.s | 2 +- test/ELF/aarch64-fpic-add_abs_lo12_nc.s | 2 +- test/ELF/aarch64-fpic-adr_prel_lo21.s | 2 +- test/ELF/aarch64-fpic-adr_prel_pg_hi21.s | 2 +- test/ELF/aarch64-fpic-ldst32_abs_lo12_nc.s | 2 +- test/ELF/aarch64-fpic-ldst64_abs_lo12_nc.s | 2 +- test/ELF/aarch64-fpic-ldst8_abs_lo12_nc.s | 2 +- test/ELF/aarch64-fpic-prel16.s | 2 +- test/ELF/aarch64-fpic-prel32.s | 2 +- test/ELF/aarch64-fpic-prel64.s | 2 +- test/ELF/aarch64-gnu-ifunc-plt.s | 85 + test/ELF/aarch64-gnu-ifunc.s | 55 +- test/ELF/aarch64-got-reloc.s | 30 + test/ELF/aarch64-prel16.s | 2 +- test/ELF/aarch64-prel32.s | 2 +- test/ELF/aarch64-relocs.s | 68 +- test/ELF/aarch64-tls-gdie.s | 14 +- test/ELF/aarch64-tls-gdle.s | 8 +- test/ELF/aarch64-tls-ie.s | 24 +- test/ELF/aarch64-tls-iele.s | 8 +- test/ELF/aarch64-tls-le.s | 6 +- test/ELF/aarch64-tls-static.s | 14 +- test/ELF/aarch64-tlsdesc.s | 58 +- test/ELF/aarch64-tstbr14-reloc.s | 100 +- test/ELF/aarch64-undefined-weak.s | 45 + test/ELF/abs-conflict.s | 16 + test/ELF/abs-hidden.s | 2 +- test/ELF/allow-multiple-definition.s | 4 +- test/ELF/amdgpu-entry.s | 16 - test/ELF/amdgpu-globals.s | 2 +- test/ELF/amdgpu-relocs.s | 96 +- test/ELF/arm-attributes-remove.s | 45 - test/ELF/arm-attributes.s | 183 ++ test/ELF/arm-blx.s | 1 + test/ELF/arm-branch.s | 1 + test/ELF/arm-data-prel.s | 8 +- test/ELF/arm-eabi-version.s | 14 + test/ELF/arm-exidx-canunwind.s | 99 + test/ELF/arm-exidx-gc.s | 125 ++ test/ELF/arm-exidx-link.s | 25 + test/ELF/arm-exidx-order.s | 169 ++ test/ELF/arm-exidx-output.s | 44 + test/ELF/arm-exidx-relocatable.s | 132 ++ test/ELF/arm-exidx-sentinel-norelocatable.s | 17 + test/ELF/arm-exidx-sentinel-orphan.s | 23 + test/ELF/arm-exidx-shared.s | 45 + test/ELF/arm-gnu-ifunc-plt.s | 93 + test/ELF/arm-gnu-ifunc.s | 156 +- test/ELF/arm-got-relative.s | 2 +- test/ELF/arm-pie-relative.s | 25 + test/ELF/arm-static-defines.s | 44 + test/ELF/arm-target1.s | 32 + test/ELF/arm-target2.s | 60 + test/ELF/arm-thumb-blx.s | 2 +- test/ELF/arm-thumb-branch.s | 1 + test/ELF/arm-thumb-interwork-thunk-range.s | 15 + test/ELF/arm-thumb-interwork-thunk.s | 4 +- test/ELF/arm-thumb-narrow-branch-check.s | 1 + test/ELF/arm-thumb-no-undefined-thunk.s | 24 + test/ELF/arm-thumb-undefined-weak.s | 38 + test/ELF/arm-tls-gd32.s | 106 + test/ELF/arm-tls-ie32.s | 96 + test/ELF/arm-tls-ldm32.s | 73 + test/ELF/arm-tls-le32.s | 77 + test/ELF/arm-tls-norelax-gd-ie.s | 30 + test/ELF/arm-tls-norelax-gd-le.s | 37 + test/ELF/arm-tls-norelax-ie-le.s | 41 + test/ELF/arm-tls-norelax-ld-le.s | 35 + test/ELF/arm-undefined-weak.s | 39 + test/ELF/arm-use-r-output.s | 13 + test/ELF/auxiliary.s | 12 + test/ELF/avoid-empty-program-headers.s | 16 +- test/ELF/bad-archive.s | 11 + test/ELF/basic-aarch64.s | 52 +- test/ELF/basic-mips.s | 62 +- test/ELF/basic-ppc.s | 62 +- test/ELF/basic.s | 70 +- test/ELF/basic32.s | 38 +- test/ELF/basic64be.s | 41 +- test/ELF/bss.s | 14 +- test/ELF/bsymbolic-undef.s | 4 +- test/ELF/bsymbolic.s | 2 +- test/ELF/build-id.s | 34 +- test/ELF/color-diagnostics.test | 18 + test/ELF/comdat.s | 9 + test/ELF/comment-gc.s | 15 + test/ELF/common.s | 10 +- test/ELF/compressed-debug-input.s | 105 +- test/ELF/conflict.s | 28 +- test/ELF/copy-errors.s | 4 +- test/ELF/copy-in-shared.s | 2 +- test/ELF/copy-rel-corrupted.s | 2 +- test/ELF/copy-rel-pie-error.s | 4 +- test/ELF/debug-gc.s | 30 + test/ELF/defined-tls_get_addr.s | 10 + test/ELF/discard-locals.s | 2 +- test/ELF/discard-none.s | 4 +- test/ELF/dont-export-hidden.s | 2 +- test/ELF/driver.test | 10 + test/ELF/duplicate-internal.s | 11 - test/ELF/dynamic-got-rela.s | 34 + test/ELF/dynamic-got.s | 39 + test/ELF/dynamic-list-extern.s | 15 + test/ELF/dynamic-list.s | 26 +- test/ELF/dynamic-reloc-in-ro.s | 2 +- test/ELF/dynamic-reloc-index.s | 2 +- test/ELF/dynamic-reloc.s | 6 +- test/ELF/dynamic.s | 4 +- test/ELF/edata-etext.s | 115 +- test/ELF/eh-align-cie.s | 4 +- test/ELF/eh-frame-dyn-rel.s | 2 +- test/ELF/eh-frame-gc2.s | 15 + test/ELF/eh-frame-hdr-abs-fde.s | 8 +- test/ELF/eh-frame-hdr-augmentation.s | 2 +- test/ELF/eh-frame-hdr-icf.s | 8 +- test/ELF/eh-frame-hdr-no-out.s | 6 - test/ELF/eh-frame-hdr.s | 111 +- test/ELF/eh-frame-marker.s | 13 + test/ELF/eh-frame-merge.s | 6 +- test/ELF/ehdr_start.s | 16 + test/ELF/ehframe-relocation.s | 10 +- test/ELF/empty-pt-load.s | 11 + test/ELF/empty-ver.s | 22 +- test/ELF/emulation.s | 73 +- test/ELF/end-update.s | 4 +- test/ELF/end.s | 52 +- test/ELF/entry.s | 46 +- test/ELF/error-limit.test | 26 + test/ELF/exclude.s | 19 + test/ELF/format-binary.test | 56 + test/ELF/gc-debuginfo-tls.s | 23 + test/ELF/gc-sections-alloc.s | 31 + test/ELF/gc-sections-implicit-addend.s | 26 + test/ELF/gc-sections-keep-shared-start.s | 30 + test/ELF/gc-sections-lsda.s | 2 +- test/ELF/gc-sections-non-alloc-to-merge.s | 27 + test/ELF/gc-sections-shared.s | 42 +- test/ELF/gc-sections-synthetic.s | 16 + test/ELF/gc-sections.s | 18 +- test/ELF/gdb-index.s | 49 + test/ELF/gnu-ifunc-gotpcrel.s | 2 +- test/ELF/gnu-ifunc-i386.s | 36 +- test/ELF/gnu-ifunc-plt-i386.s | 76 + test/ELF/gnu-ifunc-plt.s | 74 + test/ELF/gnu-ifunc-shared.s | 66 + test/ELF/gnu-ifunc.s | 45 +- test/ELF/got-aarch64.s | 12 +- test/ELF/got.s | 18 +- test/ELF/gotpc-relax-nopic.s | 56 +- test/ELF/gotpc-relax.s | 14 +- test/ELF/i386-gotoff-shared.s | 23 + test/ELF/i386-gotpc.s | 2 +- test/ELF/i386-pc16.test | 40 + test/ELF/i386-tls-got.s | 7 + test/ELF/i386-tls-ie-shared.s | 47 +- test/ELF/icf-non-mergeable.s | 28 + test/ELF/icf4.s | 2 +- test/ELF/icf5.s | 2 +- test/ELF/icf7.s | 4 +- test/ELF/image-base.s | 4 + test/ELF/incompatible-ar-first.s | 2 +- test/ELF/incompatible.s | 2 +- test/ELF/invalid-cie-length.s | 2 +- test/ELF/invalid-cie-length2.s | 2 +- test/ELF/invalid-cie-length3.s | 2 +- test/ELF/invalid-cie-length4.s | 2 +- test/ELF/invalid-dynamic-list.test | 12 +- test/ELF/invalid-elf.test | 35 - test/ELF/invalid-linkerscript.test | 2 +- test/ELF/invalid/Inputs/binding.elf | Bin 0 -> 536 bytes test/ELF/invalid/Inputs/broken-relaxation-x64.elf | Bin 0 -> 688 bytes test/ELF/invalid/Inputs/cie-version2.elf | Bin 0 -> 1128 bytes .../ELF/invalid/Inputs/common-symbol-alignment.elf | Bin 0 -> 456 bytes .../invalid/Inputs/common-symbol-alignment2.elf | Bin 0 -> 456 bytes test/ELF/invalid/Inputs/data-encoding.a | Bin 0 -> 156 bytes .../ELF/invalid/Inputs/dynamic-section-sh_size.elf | Bin 0 -> 482 bytes test/ELF/invalid/Inputs/file-class.a | Bin 0 -> 156 bytes test/ELF/invalid/Inputs/invalid-e_shnum.elf | Bin 0 -> 64 bytes test/ELF/invalid/Inputs/invalid-relocation-x64.elf | Bin 0 -> 559 bytes .../Inputs/mips-invalid-options-descriptor.elf | Bin 0 -> 480 bytes test/ELF/invalid/Inputs/multiple-eh-relocs.elf | Bin 0 -> 784 bytes .../invalid/Inputs/section-alignment-notpow2.elf | Bin 0 -> 960 bytes test/ELF/invalid/Inputs/section-index.elf | Bin 0 -> 544 bytes test/ELF/invalid/Inputs/section-index2.elf | Bin 0 -> 474 bytes test/ELF/invalid/Inputs/shentsize-zero.elf | Bin 0 -> 512 bytes test/ELF/invalid/Inputs/sht-group.elf | Bin 0 -> 480 bytes test/ELF/invalid/Inputs/symbol-index.elf | Bin 0 -> 480 bytes test/ELF/invalid/Inputs/symbol-name-offset.elf | Bin 0 -> 480 bytes test/ELF/invalid/Inputs/symtab-sh_info.elf | Bin 0 -> 512 bytes test/ELF/invalid/Inputs/symtab-sh_info2.elf | Bin 0 -> 470 bytes test/ELF/invalid/Inputs/symtab-sh_info3.elf | Bin 0 -> 470 bytes test/ELF/invalid/Inputs/tls-symbol.elf | Bin 0 -> 456 bytes test/ELF/invalid/Inputs/too-short.elf | Bin 0 -> 44 bytes test/ELF/invalid/broken-relaxation-x64.s | 46 + test/ELF/invalid/common-symbol-alignment.s | 12 + test/ELF/invalid/dynamic-section-size.s | 4 + test/ELF/invalid/eh-frame-hdr-no-out.s | 6 + test/ELF/invalid/invalid-e_shnum.s | 3 + test/ELF/invalid/invalid-elf.test | 31 + test/ELF/invalid/invalid-relocation-x64.s | 30 + test/ELF/invalid/merge-invalid-size.s | 10 + test/ELF/invalid/mips-invalid-options-descriptor.s | 5 + test/ELF/invalid/section-alignment.test | 19 + test/ELF/invalid/section-alignment2.s | 5 + test/ELF/invalid/sht-group.s | 3 + test/ELF/invalid/symbol-index.s | 10 + test/ELF/invalid/symbol-name.s | 7 + test/ELF/invalid/symtab-sh-info.s | 9 + test/ELF/invalid/symtab-symbols.test | 25 + test/ELF/invalid/tls-symbol.s | 5 + test/ELF/invalid/too-short.s | 5 + test/ELF/invalid/verdef-no-symtab.test | 26 + test/ELF/libsearch.s | 17 +- test/ELF/linkerscript-align.s | 41 - test/ELF/linkerscript-diagnostic.s | 66 - test/ELF/linkerscript-locationcounter.s | 340 ---- test/ELF/linkerscript-orphans.s | 31 - test/ELF/linkerscript-ouputformat.s | 10 - test/ELF/linkerscript-outputarch.s | 10 - test/ELF/linkerscript-phdr-check.s | 15 - test/ELF/linkerscript-repsection-va.s | 24 - test/ELF/linkerscript-sections-keep.s | 80 - test/ELF/linkerscript-sections-padding.s | 44 - test/ELF/linkerscript-sections.s | 119 -- test/ELF/linkerscript-symbol-conflict.s | 11 - test/ELF/linkerscript-symbols.s | 11 - test/ELF/linkerscript-va.s | 24 - test/ELF/linkerscript.s | 120 -- test/ELF/linkerscript/Inputs/comdat-gc.s | 5 + test/ELF/linkerscript/Inputs/exclude-multiple1.s | 8 + test/ELF/linkerscript/Inputs/exclude-multiple2.s | 8 + test/ELF/linkerscript/Inputs/filename-spec.s | 2 + .../Inputs/implicit-program-header.script | 12 + test/ELF/linkerscript/Inputs/include.s | 5 + test/ELF/linkerscript/Inputs/keep.s | 2 + test/ELF/linkerscript/Inputs/libsearch-dyn.s | 3 + test/ELF/linkerscript/Inputs/libsearch-st.s | 3 + .../ELF/linkerscript/Inputs/merge-sections-reloc.s | 3 + test/ELF/linkerscript/Inputs/notinclude.s | 4 + test/ELF/linkerscript/Inputs/segment-start.script | 7 + test/ELF/linkerscript/Inputs/shared.s | 10 + test/ELF/linkerscript/Inputs/sort-nested.s | 7 + test/ELF/linkerscript/Inputs/sort.s | 19 + test/ELF/linkerscript/absolute-expr.s | 82 + test/ELF/linkerscript/absolute.s | 18 + test/ELF/linkerscript/addr.s | 32 + test/ELF/linkerscript/align-empty.s | 18 + test/ELF/linkerscript/align.s | 83 + test/ELF/linkerscript/alignof.s | 41 + test/ELF/linkerscript/alternate-sections.s | 34 + test/ELF/linkerscript/arm-exidx-phdrs.s | 16 + test/ELF/linkerscript/assert.s | 43 + test/ELF/linkerscript/at.s | 119 ++ test/ELF/linkerscript/comdat-gc.s | 14 + test/ELF/linkerscript/common.s | 49 + test/ELF/linkerscript/data-commands.s | 81 + test/ELF/linkerscript/data-segment-relro.s | 70 + test/ELF/linkerscript/define.s | 25 + test/ELF/linkerscript/diagnostic.s | 106 + test/ELF/linkerscript/discard-interp.s | 12 + test/ELF/linkerscript/discard-section.s | 14 + test/ELF/linkerscript/dot-is-not-abs.s | 53 + test/ELF/linkerscript/double-bss.s | 21 + test/ELF/linkerscript/dynamic-sym.s | 17 + test/ELF/linkerscript/dynamic.s | 28 + test/ELF/linkerscript/edata-etext.s | 20 + test/ELF/linkerscript/eh-frame-hdr.s | 20 + test/ELF/linkerscript/ehdr_start.s | 11 + test/ELF/linkerscript/empty-load.s | 22 + test/ELF/linkerscript/empty-tls.s | 14 + test/ELF/linkerscript/entry.s | 42 + test/ELF/linkerscript/exclude-multiple.s | 37 + test/ELF/linkerscript/excludefile.s | 46 + test/ELF/linkerscript/extend-pt-load.s | 69 + test/ELF/linkerscript/filename-spec.s | 59 + test/ELF/linkerscript/fill.s | 30 + test/ELF/linkerscript/group.s | 56 + test/ELF/linkerscript/header-addr.s | 47 + test/ELF/linkerscript/implicit-program-header.s | 13 + test/ELF/linkerscript/input-order.s | 38 + test/ELF/linkerscript/input-sec-dup.s | 18 + test/ELF/linkerscript/linkerscript.s | 54 + test/ELF/linkerscript/loadaddr.s | 42 + test/ELF/linkerscript/locationcounter.s | 189 ++ test/ELF/linkerscript/locationcountererr.s | 9 + test/ELF/linkerscript/merge-sections-reloc.s | 16 + test/ELF/linkerscript/merge-sections.s | 94 + test/ELF/linkerscript/multi-sections-constraint.s | 34 + test/ELF/linkerscript/multiple-tbss.s | 45 + test/ELF/linkerscript/no-pt-load.s | 5 + test/ELF/linkerscript/no-space.s | 26 + test/ELF/linkerscript/non-alloc.s | 25 + test/ELF/linkerscript/numbers.s | 72 + test/ELF/linkerscript/openbsd-bootdata.s | 7 + test/ELF/linkerscript/openbsd-randomize.s | 23 + test/ELF/linkerscript/openbsd-wxneeded.s | 17 + test/ELF/linkerscript/orphan-align.s | 28 + test/ELF/linkerscript/orphan-first-cmd.s | 18 + test/ELF/linkerscript/orphan.s | 36 + test/ELF/linkerscript/orphans.s | 31 + test/ELF/linkerscript/ouputformat.s | 9 + test/ELF/linkerscript/outputarch.s | 10 + test/ELF/linkerscript/outsections-addr.s | 122 ++ test/ELF/linkerscript/page-size-align.s | 22 + test/ELF/linkerscript/page-size.s | 68 + test/ELF/linkerscript/phdr-check.s | 15 + test/ELF/linkerscript/phdrs-flags.s | 58 + test/ELF/linkerscript/phdrs.s | 143 ++ test/ELF/linkerscript/repsection-symbol.s | 28 + test/ELF/linkerscript/repsection-va.s | 24 + test/ELF/linkerscript/rosegment.s | 24 + test/ELF/linkerscript/searchdir.s | 12 + test/ELF/linkerscript/sections-constraint.s | 46 + test/ELF/linkerscript/sections-constraint2.s | 14 + test/ELF/linkerscript/sections-constraint3.s | 11 + test/ELF/linkerscript/sections-constraint4.s | 20 + test/ELF/linkerscript/sections-constraint5.s | 32 + test/ELF/linkerscript/sections-keep.s | 95 + test/ELF/linkerscript/sections-padding.s | 49 + test/ELF/linkerscript/sections-sort.s | 30 + test/ELF/linkerscript/sections.s | 124 ++ test/ELF/linkerscript/segment-start.s | 27 + test/ELF/linkerscript/sizeof.s | 53 + test/ELF/linkerscript/sizeofheaders.s | 18 + test/ELF/linkerscript/sort-constructors.s | 5 + test/ELF/linkerscript/sort-init.s | 24 + test/ELF/linkerscript/sort-nested.s | 50 + test/ELF/linkerscript/sort-non-script.s | 16 + test/ELF/linkerscript/sort.s | 120 ++ test/ELF/linkerscript/sort2.s | 39 + test/ELF/linkerscript/start-end.s | 16 + test/ELF/linkerscript/subalign.s | 43 + test/ELF/linkerscript/symbol-assignexpr.s | 48 + test/ELF/linkerscript/symbol-conflict.s | 11 + test/ELF/linkerscript/symbol-only.s | 21 + test/ELF/linkerscript/symbolreferenced.s | 22 + test/ELF/linkerscript/symbols-synthetic.s | 98 + test/ELF/linkerscript/symbols.s | 84 + test/ELF/linkerscript/tbss.s | 42 + test/ELF/linkerscript/undef.s | 11 + test/ELF/linkerscript/va.s | 24 + test/ELF/linkerscript/visibility.s | 22 + test/ELF/linkerscript/wildcards.s | 83 + test/ELF/linkerscript/wildcards2.s | 25 + test/ELF/linkerscript2.s | 17 - test/ELF/local-dynamic.s | 12 +- test/ELF/local-got-pie.s | 11 +- test/ELF/local-got-shared.s | 11 +- test/ELF/local-got.s | 18 +- test/ELF/local.s | 24 +- test/ELF/lto/Inputs/common3.ll | 3 + test/ELF/lto/Inputs/thin1.ll | 12 + test/ELF/lto/Inputs/thin2.ll | 11 + test/ELF/lto/Inputs/thinlto.ll | 7 + test/ELF/lto/Inputs/unnamed-addr-drop.ll | 4 + test/ELF/lto/archive-3.ll | 4 +- test/ELF/lto/archive.ll | 1 - test/ELF/lto/asmundef.ll | 2 +- test/ELF/lto/available-externally.ll | 2 +- test/ELF/lto/bitcode-nodatalayout.ll | 13 + test/ELF/lto/combined-lto-object-name.ll | 2 +- test/ELF/lto/common2.ll | 15 +- test/ELF/lto/common3.ll | 14 + test/ELF/lto/discard-value-names.ll | 2 +- test/ELF/lto/drop-debug-info.ll | 2 +- test/ELF/lto/drop-linkage.ll | 2 +- test/ELF/lto/duplicated.ll | 2 +- test/ELF/lto/dynsym.ll | 5 + test/ELF/lto/internalize-basic.ll | 2 +- test/ELF/lto/internalize-exportdyn.ll | 2 +- test/ELF/lto/internalize-llvmused.ll | 2 +- test/ELF/lto/internalize-undef.ll | 2 +- test/ELF/lto/internalize-version-script.ll | 2 +- test/ELF/lto/invalid-bitcode.ll | 12 - test/ELF/lto/irmover-error.ll | 2 +- test/ELF/lto/linkonce-odr.ll | 2 +- test/ELF/lto/linkonce.ll | 2 +- test/ELF/lto/ltopasses-basic.ll | 3 +- test/ELF/lto/ltopasses-custom.ll | 11 +- test/ELF/lto/metadata.ll | 4 +- test/ELF/lto/parallel-internalize.ll | 11 +- test/ELF/lto/parallel.ll | 2 +- test/ELF/lto/save-temps.ll | 4 +- test/ELF/lto/shlib-undefined.ll | 2 +- test/ELF/lto/thin-archivecollision.ll | 29 + test/ELF/lto/thinlto.ll | 35 + test/ELF/lto/timepasses.ll | 15 + test/ELF/lto/type-merge.ll | 2 +- test/ELF/lto/type-merge2.ll | 2 +- test/ELF/lto/undefined-puts.ll | 2 +- test/ELF/lto/unnamed-addr-comdat.ll | 2 +- test/ELF/lto/unnamed-addr-drop.ll | 12 + test/ELF/lto/unnamed-addr-lib.ll | 2 +- test/ELF/lto/unnamed-addr.ll | 2 +- test/ELF/lto/version-script.ll | 2 +- test/ELF/merge-invalid-size.s | 7 - test/ELF/merge-reloc.s | 92 + test/ELF/merge-string-empty.s | 12 + test/ELF/merge-string-error.s | 2 +- test/ELF/merge-string.s | 2 - test/ELF/merge.s | 48 +- test/ELF/mips-26-mask.s | 16 + test/ELF/mips-32.s | 9 +- test/ELF/mips-64-disp.s | 2 +- test/ELF/mips-64-got.s | 19 +- test/ELF/mips-64-gprel-so.s | 2 +- test/ELF/mips-64-rels.s | 2 +- test/ELF/mips-align-err.s | 2 +- test/ELF/mips-call-hilo.s | 62 + test/ELF/mips-elf-flags-err.s | 87 + test/ELF/mips-elf-flags.s | 155 +- test/ELF/mips-got-and-copy.s | 2 +- test/ELF/mips-got-hilo.s | 64 + test/ELF/mips-got-page.s | 40 + test/ELF/mips-got-redundant.s | 6 + test/ELF/mips-got-relocs.s | 4 +- test/ELF/mips-got16.s | 40 +- test/ELF/mips-gp-disp.s | 2 +- test/ELF/mips-gp-ext.s | 73 + test/ELF/mips-gp-local.s | 2 +- test/ELF/mips-gp-lowest.s | 44 + test/ELF/mips-gprel32-relocs-gp0.s | 48 + test/ELF/mips-gprel32-relocs-gp0.test | 31 - test/ELF/mips-gprel32-relocs.s | 2 +- test/ELF/mips-higher-highest.s | 21 + test/ELF/mips-hilo-gp-disp.s | 4 +- test/ELF/mips-merge-abiflags.s | 63 + test/ELF/mips-n32-emul.s | 14 + test/ELF/mips-n32-rels.s | 71 + test/ELF/mips-no-objects.s | 5 + test/ELF/mips-npic-call-pic.s | 50 +- test/ELF/mips-options-r.test | 7 +- test/ELF/mips-options.s | 13 +- test/ELF/mips-plt-r6.s | 38 + test/ELF/mips-relocatable.s | 21 + test/ELF/mips-sto-pic-flag.s | 58 + test/ELF/mips-tls-64.s | 116 +- test/ELF/mips-tls-static-64.s | 37 + test/ELF/mips-tls-static.s | 42 + test/ELF/mips-tls.s | 99 +- test/ELF/mips-xgot-order.s | 49 + test/ELF/no-inhibit-exec.s | 2 +- test/ELF/no-merge.s | 25 + test/ELF/no-obj.s | 2 +- test/ELF/non-abs-reloc.s | 11 + test/ELF/noplt-pie.s | 4 +- test/ELF/oformat-binary-ttext.s | 18 + test/ELF/oformat-binary.s | 32 + test/ELF/openbsd-randomize.s | 20 + test/ELF/openbsd-wxneeded.s | 17 + test/ELF/phdr-align.s | 1 + test/ELF/pie.s | 78 +- test/ELF/plt-aarch64.s | 158 +- test/ELF/plt.s | 46 +- test/ELF/ppc-relocs.s | 34 + test/ELF/pre_init_fini_array.s | 30 +- test/ELF/pre_init_fini_array_missing.s | 24 +- test/ELF/program-header-layout.s | 17 +- test/ELF/relative-dynamic-reloc-ppc64.s | 2 +- test/ELF/relative-dynamic-reloc.s | 2 +- test/ELF/relocatable-comment.s | 27 + test/ELF/relocatable-local-sym.s | 16 + test/ELF/relocatable-sections.s | 30 + test/ELF/relocatable-symbols.s | 42 +- test/ELF/relocatable-tls.s | 15 + test/ELF/relocatable-visibility.s | 19 + test/ELF/relocatable.s | 10 +- test/ELF/relocation-common.s | 4 +- test/ELF/relocation-copy-flags.s | 10 +- test/ELF/relocation-copy.s | 20 +- test/ELF/relocation-dtrace.test | 24 + test/ELF/relocation-i686.s | 6 +- test/ELF/relocation-local.s | 6 +- test/ELF/relocation-past-merge-end.s | 9 +- test/ELF/relocation-relative-absolute.s | 12 +- test/ELF/relocation-size-shared.s | 72 +- test/ELF/relocation-size.s | 60 +- test/ELF/relocation-undefined-weak.s | 6 +- test/ELF/relocation.s | 40 +- test/ELF/relro-omagic.s | 34 + test/ELF/relro.s | 239 +-- test/ELF/reproduce-backslash.s | 9 + test/ELF/reproduce-windows.s | 6 +- test/ELF/reproduce-windows2.s | 11 + test/ELF/reproduce.s | 8 +- test/ELF/resolution.s | 18 +- test/ELF/retain-symbols-file.s | 44 + test/ELF/section-name.s | 50 +- test/ELF/section-symbols.test | 35 + test/ELF/sectionstart.s | 67 + test/ELF/segments.s | 108 + test/ELF/shared-be.s | 4 +- test/ELF/shared.s | 8 +- test/ELF/shf-info-link.test | 32 + test/ELF/sort-norosegment.s | 15 + test/ELF/startstop-gccollect.s | 6 +- test/ELF/startstop-shared.s | 16 +- test/ELF/startstop-shared2.s | 14 + test/ELF/startstop.s | 24 +- test/ELF/string-gc.s | 6 +- test/ELF/string-table.s | 7 +- test/ELF/symbol-ordering-file.s | 44 + test/ELF/symbol-override.s | 4 +- test/ELF/symbols.s | 24 +- test/ELF/synthetic-got.s | 32 + test/ELF/sysroot.s | 3 +- test/ELF/tls-got.s | 22 +- test/ELF/tls-mismatch.s | 2 +- test/ELF/tls-offset.s | 6 +- test/ELF/tls-opt-gdie.s | 18 +- test/ELF/tls-opt-local.s | 24 +- test/ELF/tls-opt.s | 46 +- test/ELF/tls-relocatable.s | 21 + test/ELF/tls-static.s | 2 +- test/ELF/tls-weak-undef.s | 16 + test/ELF/tls.s | 12 +- test/ELF/ttext-tdata-tbss.s | 63 + test/ELF/undef-shared.s | 11 +- test/ELF/undef-start.s | 3 +- test/ELF/undef-version-script.s | 6 +- test/ELF/undef-with-plt-addr.s | 8 +- test/ELF/undef.s | 22 +- test/ELF/undefined-opt.s | 2 + test/ELF/unresolved-symbols.s | 4 +- test/ELF/verdef-defaultver.s | 60 +- test/ELF/verdef-dependency.s | 12 +- test/ELF/verdef.s | 22 +- test/ELF/verneed-local.s | 2 +- test/ELF/verneed.s | 34 +- test/ELF/version-script-complex-wildcards.s | 62 + test/ELF/version-script-err.s | 3 +- test/ELF/version-script-extern-exact.s | 22 + test/ELF/version-script-extern-wildcards-anon.s | 62 + test/ELF/version-script-extern-wildcards.s | 29 + test/ELF/version-script-extern.s | 61 +- test/ELF/version-script-glob.s | 45 + test/ELF/version-script-locals-extern.s | 45 + test/ELF/version-script-locals.s | 45 + test/ELF/version-script-missing.s | 7 + test/ELF/version-script-no-warn.s | 12 + test/ELF/version-script-noundef.s | 17 +- test/ELF/version-script.s | 89 +- test/ELF/version-symbol-error.s | 12 + test/ELF/version-wildcard.test | 12 +- test/ELF/visibility.s | 2 +- test/ELF/weak-undef-shared.s | 2 +- test/ELF/weak-undef.s | 2 +- test/ELF/wildcards.s | 80 - test/ELF/writable-merge.s | 1 + test/ELF/x86-64-dyn-rel-error.s | 2 +- test/ELF/x86-64-dyn-rel-error2.s | 2 +- test/ELF/x86-64-relax-got-abs.s | 16 + test/ELF/x86-64-reloc-32-fpic.s | 2 +- test/ELF/x86-64-reloc-error.s | 4 +- test/ELF/x86-64-reloc-pc32-fpic.s | 2 +- test/ELF/x86-64-reloc-range.s | 2 +- test/ELF/zdefs.s | 2 +- test/ELF/zstack-size.s | 20 + test/lit.cfg | 17 +- test/mach-o/Inputs/arm64/libSystem.yaml | 13 + test/mach-o/Inputs/armv7/libSystem.yaml | 13 + test/mach-o/Inputs/libSystem.yaml | 13 - test/mach-o/Inputs/x86/libSystem.yaml | 13 + test/mach-o/Inputs/x86_64/libSystem.yaml | 13 + test/mach-o/arm-interworking-movw.yaml | 2 +- test/mach-o/arm-interworking.yaml | 2 +- test/mach-o/arm-shims.yaml | 2 +- test/mach-o/arm64-reloc-negDelta32-fixup.yaml | 38 +- .../mach-o/arm64-relocs-errors-delta64-offset.yaml | 12 +- test/mach-o/bind-opcodes.yaml | 143 ++ test/mach-o/data-in-code-load-command.yaml | 16 +- test/mach-o/data-only-dylib.yaml | 2 +- test/mach-o/dead-strip-globals.yaml | 10 +- test/mach-o/debug-syms.yaml | 249 +++ test/mach-o/demangle.yaml | 4 +- test/mach-o/dso_handle.yaml | 8 +- test/mach-o/dylib-install-names.yaml | 12 +- test/mach-o/eh-frame-relocs-arm64.yaml | 92 +- test/mach-o/exe-offsets.yaml | 2 +- test/mach-o/exe-segment-overlap.yaml | 2 +- test/mach-o/executable-exports.yaml | 2 +- test/mach-o/export-trie-order.yaml | 62 + test/mach-o/exported_symbols_list-dylib.yaml | 8 +- test/mach-o/exported_symbols_list-undef.yaml | 2 +- test/mach-o/fat-archive.yaml | 2 +- test/mach-o/flat_namespace_undef_error.yaml | 2 +- test/mach-o/flat_namespace_undef_suppress.yaml | 2 +- test/mach-o/force_load-dylib.yaml | 4 +- test/mach-o/force_load-x86_64.yaml | 4 +- test/mach-o/function-starts-load-command.yaml | 10 +- test/mach-o/gcc_except_tab-got-arm64.yaml | 30 +- test/mach-o/got-order.yaml | 2 +- test/mach-o/hello-world-arm64.yaml | 8 + test/mach-o/image-base.yaml | 2 +- test/mach-o/interposing-section.yaml | 2 +- test/mach-o/lazy-bind-x86_64.yaml | 2 +- test/mach-o/lc_segment_filesize.yaml | 31 + test/mach-o/library-order.yaml | 2 +- test/mach-o/library-rescan.yaml | 2 +- test/mach-o/linker-as-ld.yaml | 4 +- test/mach-o/lit.local.cfg | 2 +- test/mach-o/mh_bundle_header.yaml | 4 +- test/mach-o/mh_dylib_header.yaml | 2 +- test/mach-o/objc-category-list-atom.yaml | 14 +- test/mach-o/objc_export_list.yaml | 2 +- test/mach-o/order_file-basic.yaml | 2 +- test/mach-o/parse-data-in-code-armv7.yaml | 2 +- test/mach-o/parse-data-relocs-x86_64.yaml | 6 +- test/mach-o/rpath.yaml | 2 +- test/mach-o/run-tlv-pass-x86-64.yaml | 18 +- test/mach-o/sectalign.yaml | 2 +- test/mach-o/sectattrs.yaml | 2 +- test/mach-o/source-version.yaml | 2 +- test/mach-o/stack-size.yaml | 4 +- test/mach-o/string-table.yaml | 66 + .../twolevel_namespace_undef_dynamic_lookup.yaml | 2 +- .../twolevel_namespace_undef_warning_suppress.yaml | 4 +- test/mach-o/unwind-info-simple-arm64.yaml | 2 +- test/mach-o/unwind-info-simple-x86_64.yaml | 2 +- test/mach-o/upward-dylib-load-command.yaml | 4 +- test/mach-o/use-dylib.yaml | 39 + test/mach-o/version-min-load-command.yaml | 14 +- tools/lld/CMakeLists.txt | 7 +- tools/lld/lld.cpp | 6 +- unittests/CMakeLists.txt | 2 + .../MachONormalizedFileBinaryWriterTests.cpp | 4 + 828 files changed, 28282 insertions(+), 11419 deletions(-) create mode 100644 COFF/Memory.h create mode 100644 COFF/PDB.h create mode 100644 COFF/Strings.cpp create mode 100644 COFF/Strings.h create mode 100644 ELF/GdbIndex.cpp create mode 100644 ELF/GdbIndex.h create mode 100644 ELF/Memory.h create mode 100644 ELF/Mips.cpp delete mode 100644 ELF/SymbolListFile.cpp delete mode 100644 ELF/SymbolListFile.h create mode 100644 ELF/SyntheticSections.cpp create mode 100644 ELF/SyntheticSections.h create mode 100644 ELF/Threads.h create mode 100644 cmake/modules/AddLLD.cmake create mode 100644 include/lld/Core/Reproduce.h create mode 100644 include/lld/Support/Memory.h create mode 100644 lib/Core/Reproduce.cpp create mode 100644 lib/ReaderWriter/MachO/DebugInfo.h create mode 100755 test/COFF/Inputs/cl-gl.obj create mode 100644 test/COFF/Inputs/far-arm-thumb-abs.s create mode 100644 test/COFF/Inputs/lto-lazy-reference-dummy.ll create mode 100644 test/COFF/Inputs/lto-lazy-reference-quadruple.ll create mode 100644 test/COFF/arm-thumb-branch-error.s create mode 100644 test/COFF/cl-gl.test create mode 100644 test/COFF/conflict-mangled.test create mode 100644 test/COFF/dumppdb.test create mode 100644 test/COFF/icf-associative.test create mode 100644 test/COFF/linkrepro.test create mode 100644 test/COFF/lto-lazy-reference.ll create mode 100644 test/COFF/pdb.test create mode 100644 test/COFF/rsds.test create mode 100644 test/ELF/Inputs/arm-attributes1.s create mode 100644 test/ELF/Inputs/arm-exidx-cantunwind.s create mode 100644 test/ELF/Inputs/arm-shared.s create mode 100644 test/ELF/Inputs/arm-tls-get-addr.s create mode 100644 test/ELF/Inputs/bad-archive.a create mode 100644 test/ELF/Inputs/comment-gc.s create mode 100644 test/ELF/Inputs/conflict-debug.s create mode 100644 test/ELF/Inputs/gdb-index-a.elf create mode 100644 test/ELF/Inputs/gdb-index-b.elf create mode 100644 test/ELF/Inputs/i386-tls-got.s create mode 100644 test/ELF/Inputs/icf-non-mergeable.s delete mode 100644 test/ELF/Inputs/invalid-binding.elf delete mode 100644 test/ELF/Inputs/invalid-cie-version2.elf delete mode 100644 test/ELF/Inputs/invalid-data-encoding.a delete mode 100644 test/ELF/Inputs/invalid-file-class.a delete mode 100644 test/ELF/Inputs/invalid-multiple-eh-relocs.elf delete mode 100644 test/ELF/Inputs/invalid-section-index.elf delete mode 100644 test/ELF/Inputs/invalid-shentsize-zero.elf delete mode 100755 test/ELF/Inputs/invalid-shstrndx.so delete mode 100644 test/ELF/Inputs/invalid-symtab-sh_info.elf create mode 100644 test/ELF/Inputs/mips-concatenated-abiflags.o create mode 100644 test/ELF/Inputs/mips-fnpic.s create mode 100644 test/ELF/Inputs/mips-fpic.s create mode 100755 test/ELF/Inputs/mips-gp0-non-zero.o create mode 100644 test/ELF/Inputs/mips-n32-rels.o create mode 100644 test/ELF/Inputs/relocatable-tls.s create mode 100644 test/ELF/Inputs/relocation-relative-absolute.s create mode 100644 test/ELF/Inputs/shared2-x86-64.s create mode 100644 test/ELF/Inputs/shf-info-link.test create mode 100644 test/ELF/Inputs/startstop-shared2.s create mode 100644 test/ELF/Inputs/uabs_label.s create mode 100644 test/ELF/Inputs/undef-debug.s create mode 100644 test/ELF/Inputs/use-bar.s create mode 100644 test/ELF/aarch64-gnu-ifunc-plt.s create mode 100644 test/ELF/aarch64-got-reloc.s create mode 100644 test/ELF/aarch64-undefined-weak.s create mode 100644 test/ELF/abs-conflict.s delete mode 100644 test/ELF/amdgpu-entry.s delete mode 100644 test/ELF/arm-attributes-remove.s create mode 100644 test/ELF/arm-attributes.s create mode 100644 test/ELF/arm-eabi-version.s create mode 100644 test/ELF/arm-exidx-canunwind.s create mode 100644 test/ELF/arm-exidx-gc.s create mode 100644 test/ELF/arm-exidx-link.s create mode 100644 test/ELF/arm-exidx-order.s create mode 100644 test/ELF/arm-exidx-output.s create mode 100644 test/ELF/arm-exidx-relocatable.s create mode 100644 test/ELF/arm-exidx-sentinel-norelocatable.s create mode 100644 test/ELF/arm-exidx-sentinel-orphan.s create mode 100644 test/ELF/arm-exidx-shared.s create mode 100644 test/ELF/arm-gnu-ifunc-plt.s create mode 100644 test/ELF/arm-pie-relative.s create mode 100644 test/ELF/arm-static-defines.s create mode 100644 test/ELF/arm-target1.s create mode 100644 test/ELF/arm-target2.s create mode 100644 test/ELF/arm-thumb-interwork-thunk-range.s create mode 100644 test/ELF/arm-thumb-no-undefined-thunk.s create mode 100644 test/ELF/arm-thumb-undefined-weak.s create mode 100644 test/ELF/arm-tls-gd32.s create mode 100644 test/ELF/arm-tls-ie32.s create mode 100644 test/ELF/arm-tls-ldm32.s create mode 100644 test/ELF/arm-tls-le32.s create mode 100644 test/ELF/arm-tls-norelax-gd-ie.s create mode 100644 test/ELF/arm-tls-norelax-gd-le.s create mode 100644 test/ELF/arm-tls-norelax-ie-le.s create mode 100644 test/ELF/arm-tls-norelax-ld-le.s create mode 100644 test/ELF/arm-undefined-weak.s create mode 100644 test/ELF/arm-use-r-output.s create mode 100644 test/ELF/auxiliary.s create mode 100644 test/ELF/bad-archive.s create mode 100644 test/ELF/color-diagnostics.test create mode 100644 test/ELF/comment-gc.s create mode 100644 test/ELF/debug-gc.s create mode 100644 test/ELF/defined-tls_get_addr.s delete mode 100644 test/ELF/duplicate-internal.s create mode 100644 test/ELF/dynamic-got-rela.s create mode 100644 test/ELF/dynamic-got.s create mode 100644 test/ELF/dynamic-list-extern.s create mode 100644 test/ELF/eh-frame-gc2.s delete mode 100644 test/ELF/eh-frame-hdr-no-out.s create mode 100644 test/ELF/ehdr_start.s create mode 100644 test/ELF/empty-pt-load.s create mode 100644 test/ELF/error-limit.test create mode 100644 test/ELF/exclude.s create mode 100644 test/ELF/format-binary.test create mode 100644 test/ELF/gc-debuginfo-tls.s create mode 100644 test/ELF/gc-sections-alloc.s create mode 100644 test/ELF/gc-sections-implicit-addend.s create mode 100644 test/ELF/gc-sections-keep-shared-start.s create mode 100644 test/ELF/gc-sections-non-alloc-to-merge.s create mode 100644 test/ELF/gc-sections-synthetic.s create mode 100644 test/ELF/gdb-index.s create mode 100644 test/ELF/gnu-ifunc-plt-i386.s create mode 100644 test/ELF/gnu-ifunc-plt.s create mode 100644 test/ELF/gnu-ifunc-shared.s create mode 100644 test/ELF/i386-gotoff-shared.s create mode 100644 test/ELF/i386-pc16.test create mode 100644 test/ELF/i386-tls-got.s create mode 100644 test/ELF/icf-non-mergeable.s delete mode 100644 test/ELF/invalid-elf.test create mode 100644 test/ELF/invalid/Inputs/binding.elf create mode 100644 test/ELF/invalid/Inputs/broken-relaxation-x64.elf create mode 100644 test/ELF/invalid/Inputs/cie-version2.elf create mode 100644 test/ELF/invalid/Inputs/common-symbol-alignment.elf create mode 100644 test/ELF/invalid/Inputs/common-symbol-alignment2.elf create mode 100644 test/ELF/invalid/Inputs/data-encoding.a create mode 100644 test/ELF/invalid/Inputs/dynamic-section-sh_size.elf create mode 100644 test/ELF/invalid/Inputs/file-class.a create mode 100644 test/ELF/invalid/Inputs/invalid-e_shnum.elf create mode 100644 test/ELF/invalid/Inputs/invalid-relocation-x64.elf create mode 100644 test/ELF/invalid/Inputs/mips-invalid-options-descriptor.elf create mode 100644 test/ELF/invalid/Inputs/multiple-eh-relocs.elf create mode 100644 test/ELF/invalid/Inputs/section-alignment-notpow2.elf create mode 100644 test/ELF/invalid/Inputs/section-index.elf create mode 100644 test/ELF/invalid/Inputs/section-index2.elf create mode 100644 test/ELF/invalid/Inputs/shentsize-zero.elf create mode 100644 test/ELF/invalid/Inputs/sht-group.elf create mode 100644 test/ELF/invalid/Inputs/symbol-index.elf create mode 100644 test/ELF/invalid/Inputs/symbol-name-offset.elf create mode 100644 test/ELF/invalid/Inputs/symtab-sh_info.elf create mode 100644 test/ELF/invalid/Inputs/symtab-sh_info2.elf create mode 100644 test/ELF/invalid/Inputs/symtab-sh_info3.elf create mode 100644 test/ELF/invalid/Inputs/tls-symbol.elf create mode 100644 test/ELF/invalid/Inputs/too-short.elf create mode 100644 test/ELF/invalid/broken-relaxation-x64.s create mode 100644 test/ELF/invalid/common-symbol-alignment.s create mode 100644 test/ELF/invalid/dynamic-section-size.s create mode 100644 test/ELF/invalid/eh-frame-hdr-no-out.s create mode 100644 test/ELF/invalid/invalid-e_shnum.s create mode 100644 test/ELF/invalid/invalid-elf.test create mode 100644 test/ELF/invalid/invalid-relocation-x64.s create mode 100644 test/ELF/invalid/merge-invalid-size.s create mode 100644 test/ELF/invalid/mips-invalid-options-descriptor.s create mode 100644 test/ELF/invalid/section-alignment.test create mode 100644 test/ELF/invalid/section-alignment2.s create mode 100644 test/ELF/invalid/sht-group.s create mode 100644 test/ELF/invalid/symbol-index.s create mode 100644 test/ELF/invalid/symbol-name.s create mode 100644 test/ELF/invalid/symtab-sh-info.s create mode 100644 test/ELF/invalid/symtab-symbols.test create mode 100644 test/ELF/invalid/tls-symbol.s create mode 100644 test/ELF/invalid/too-short.s create mode 100644 test/ELF/invalid/verdef-no-symtab.test delete mode 100644 test/ELF/linkerscript-align.s delete mode 100644 test/ELF/linkerscript-diagnostic.s delete mode 100644 test/ELF/linkerscript-locationcounter.s delete mode 100644 test/ELF/linkerscript-orphans.s delete mode 100644 test/ELF/linkerscript-ouputformat.s delete mode 100644 test/ELF/linkerscript-outputarch.s delete mode 100644 test/ELF/linkerscript-phdr-check.s delete mode 100644 test/ELF/linkerscript-repsection-va.s delete mode 100644 test/ELF/linkerscript-sections-keep.s delete mode 100644 test/ELF/linkerscript-sections-padding.s delete mode 100644 test/ELF/linkerscript-sections.s delete mode 100644 test/ELF/linkerscript-symbol-conflict.s delete mode 100644 test/ELF/linkerscript-symbols.s delete mode 100644 test/ELF/linkerscript-va.s delete mode 100644 test/ELF/linkerscript.s create mode 100644 test/ELF/linkerscript/Inputs/comdat-gc.s create mode 100644 test/ELF/linkerscript/Inputs/exclude-multiple1.s create mode 100644 test/ELF/linkerscript/Inputs/exclude-multiple2.s create mode 100644 test/ELF/linkerscript/Inputs/filename-spec.s create mode 100644 test/ELF/linkerscript/Inputs/implicit-program-header.script create mode 100644 test/ELF/linkerscript/Inputs/include.s create mode 100644 test/ELF/linkerscript/Inputs/keep.s create mode 100644 test/ELF/linkerscript/Inputs/libsearch-dyn.s create mode 100644 test/ELF/linkerscript/Inputs/libsearch-st.s create mode 100644 test/ELF/linkerscript/Inputs/merge-sections-reloc.s create mode 100644 test/ELF/linkerscript/Inputs/notinclude.s create mode 100644 test/ELF/linkerscript/Inputs/segment-start.script create mode 100644 test/ELF/linkerscript/Inputs/shared.s create mode 100644 test/ELF/linkerscript/Inputs/sort-nested.s create mode 100644 test/ELF/linkerscript/Inputs/sort.s create mode 100644 test/ELF/linkerscript/absolute-expr.s create mode 100644 test/ELF/linkerscript/absolute.s create mode 100644 test/ELF/linkerscript/addr.s create mode 100644 test/ELF/linkerscript/align-empty.s create mode 100644 test/ELF/linkerscript/align.s create mode 100644 test/ELF/linkerscript/alignof.s create mode 100644 test/ELF/linkerscript/alternate-sections.s create mode 100644 test/ELF/linkerscript/arm-exidx-phdrs.s create mode 100644 test/ELF/linkerscript/assert.s create mode 100644 test/ELF/linkerscript/at.s create mode 100644 test/ELF/linkerscript/comdat-gc.s create mode 100644 test/ELF/linkerscript/common.s create mode 100644 test/ELF/linkerscript/data-commands.s create mode 100644 test/ELF/linkerscript/data-segment-relro.s create mode 100644 test/ELF/linkerscript/define.s create mode 100644 test/ELF/linkerscript/diagnostic.s create mode 100644 test/ELF/linkerscript/discard-interp.s create mode 100644 test/ELF/linkerscript/discard-section.s create mode 100644 test/ELF/linkerscript/dot-is-not-abs.s create mode 100644 test/ELF/linkerscript/double-bss.s create mode 100644 test/ELF/linkerscript/dynamic-sym.s create mode 100644 test/ELF/linkerscript/dynamic.s create mode 100644 test/ELF/linkerscript/edata-etext.s create mode 100644 test/ELF/linkerscript/eh-frame-hdr.s create mode 100644 test/ELF/linkerscript/ehdr_start.s create mode 100644 test/ELF/linkerscript/empty-load.s create mode 100644 test/ELF/linkerscript/empty-tls.s create mode 100644 test/ELF/linkerscript/entry.s create mode 100644 test/ELF/linkerscript/exclude-multiple.s create mode 100644 test/ELF/linkerscript/excludefile.s create mode 100644 test/ELF/linkerscript/extend-pt-load.s create mode 100644 test/ELF/linkerscript/filename-spec.s create mode 100644 test/ELF/linkerscript/fill.s create mode 100644 test/ELF/linkerscript/group.s create mode 100644 test/ELF/linkerscript/header-addr.s create mode 100644 test/ELF/linkerscript/implicit-program-header.s create mode 100644 test/ELF/linkerscript/input-order.s create mode 100644 test/ELF/linkerscript/input-sec-dup.s create mode 100644 test/ELF/linkerscript/linkerscript.s create mode 100644 test/ELF/linkerscript/loadaddr.s create mode 100644 test/ELF/linkerscript/locationcounter.s create mode 100644 test/ELF/linkerscript/locationcountererr.s create mode 100644 test/ELF/linkerscript/merge-sections-reloc.s create mode 100644 test/ELF/linkerscript/merge-sections.s create mode 100644 test/ELF/linkerscript/multi-sections-constraint.s create mode 100644 test/ELF/linkerscript/multiple-tbss.s create mode 100644 test/ELF/linkerscript/no-pt-load.s create mode 100644 test/ELF/linkerscript/no-space.s create mode 100644 test/ELF/linkerscript/non-alloc.s create mode 100644 test/ELF/linkerscript/numbers.s create mode 100644 test/ELF/linkerscript/openbsd-bootdata.s create mode 100644 test/ELF/linkerscript/openbsd-randomize.s create mode 100644 test/ELF/linkerscript/openbsd-wxneeded.s create mode 100644 test/ELF/linkerscript/orphan-align.s create mode 100644 test/ELF/linkerscript/orphan-first-cmd.s create mode 100644 test/ELF/linkerscript/orphan.s create mode 100644 test/ELF/linkerscript/orphans.s create mode 100644 test/ELF/linkerscript/ouputformat.s create mode 100644 test/ELF/linkerscript/outputarch.s create mode 100644 test/ELF/linkerscript/outsections-addr.s create mode 100644 test/ELF/linkerscript/page-size-align.s create mode 100644 test/ELF/linkerscript/page-size.s create mode 100644 test/ELF/linkerscript/phdr-check.s create mode 100644 test/ELF/linkerscript/phdrs-flags.s create mode 100644 test/ELF/linkerscript/phdrs.s create mode 100644 test/ELF/linkerscript/repsection-symbol.s create mode 100644 test/ELF/linkerscript/repsection-va.s create mode 100644 test/ELF/linkerscript/rosegment.s create mode 100644 test/ELF/linkerscript/searchdir.s create mode 100644 test/ELF/linkerscript/sections-constraint.s create mode 100644 test/ELF/linkerscript/sections-constraint2.s create mode 100644 test/ELF/linkerscript/sections-constraint3.s create mode 100644 test/ELF/linkerscript/sections-constraint4.s create mode 100644 test/ELF/linkerscript/sections-constraint5.s create mode 100644 test/ELF/linkerscript/sections-keep.s create mode 100644 test/ELF/linkerscript/sections-padding.s create mode 100644 test/ELF/linkerscript/sections-sort.s create mode 100644 test/ELF/linkerscript/sections.s create mode 100644 test/ELF/linkerscript/segment-start.s create mode 100644 test/ELF/linkerscript/sizeof.s create mode 100644 test/ELF/linkerscript/sizeofheaders.s create mode 100644 test/ELF/linkerscript/sort-constructors.s create mode 100644 test/ELF/linkerscript/sort-init.s create mode 100644 test/ELF/linkerscript/sort-nested.s create mode 100644 test/ELF/linkerscript/sort-non-script.s create mode 100644 test/ELF/linkerscript/sort.s create mode 100644 test/ELF/linkerscript/sort2.s create mode 100644 test/ELF/linkerscript/start-end.s create mode 100644 test/ELF/linkerscript/subalign.s create mode 100644 test/ELF/linkerscript/symbol-assignexpr.s create mode 100644 test/ELF/linkerscript/symbol-conflict.s create mode 100644 test/ELF/linkerscript/symbol-only.s create mode 100644 test/ELF/linkerscript/symbolreferenced.s create mode 100644 test/ELF/linkerscript/symbols-synthetic.s create mode 100644 test/ELF/linkerscript/symbols.s create mode 100644 test/ELF/linkerscript/tbss.s create mode 100644 test/ELF/linkerscript/undef.s create mode 100644 test/ELF/linkerscript/va.s create mode 100644 test/ELF/linkerscript/visibility.s create mode 100644 test/ELF/linkerscript/wildcards.s create mode 100644 test/ELF/linkerscript/wildcards2.s delete mode 100644 test/ELF/linkerscript2.s create mode 100644 test/ELF/lto/Inputs/common3.ll create mode 100644 test/ELF/lto/Inputs/thin1.ll create mode 100644 test/ELF/lto/Inputs/thin2.ll create mode 100644 test/ELF/lto/Inputs/thinlto.ll create mode 100644 test/ELF/lto/Inputs/unnamed-addr-drop.ll create mode 100644 test/ELF/lto/bitcode-nodatalayout.ll create mode 100644 test/ELF/lto/common3.ll delete mode 100644 test/ELF/lto/invalid-bitcode.ll create mode 100644 test/ELF/lto/thin-archivecollision.ll create mode 100644 test/ELF/lto/thinlto.ll create mode 100644 test/ELF/lto/timepasses.ll create mode 100644 test/ELF/lto/unnamed-addr-drop.ll delete mode 100644 test/ELF/merge-invalid-size.s create mode 100644 test/ELF/merge-reloc.s create mode 100644 test/ELF/merge-string-empty.s create mode 100644 test/ELF/mips-26-mask.s create mode 100644 test/ELF/mips-call-hilo.s create mode 100644 test/ELF/mips-elf-flags-err.s create mode 100644 test/ELF/mips-got-hilo.s create mode 100644 test/ELF/mips-got-page.s create mode 100644 test/ELF/mips-gp-ext.s create mode 100644 test/ELF/mips-gp-lowest.s create mode 100644 test/ELF/mips-gprel32-relocs-gp0.s delete mode 100644 test/ELF/mips-gprel32-relocs-gp0.test create mode 100644 test/ELF/mips-higher-highest.s create mode 100644 test/ELF/mips-merge-abiflags.s create mode 100644 test/ELF/mips-n32-emul.s create mode 100644 test/ELF/mips-n32-rels.s create mode 100644 test/ELF/mips-no-objects.s create mode 100644 test/ELF/mips-plt-r6.s create mode 100644 test/ELF/mips-relocatable.s create mode 100644 test/ELF/mips-sto-pic-flag.s create mode 100644 test/ELF/mips-tls-static-64.s create mode 100644 test/ELF/mips-tls-static.s create mode 100644 test/ELF/mips-xgot-order.s create mode 100644 test/ELF/no-merge.s create mode 100644 test/ELF/non-abs-reloc.s create mode 100644 test/ELF/oformat-binary-ttext.s create mode 100644 test/ELF/oformat-binary.s create mode 100644 test/ELF/openbsd-randomize.s create mode 100644 test/ELF/openbsd-wxneeded.s create mode 100644 test/ELF/relocatable-comment.s create mode 100644 test/ELF/relocatable-local-sym.s create mode 100644 test/ELF/relocatable-sections.s create mode 100644 test/ELF/relocatable-tls.s create mode 100644 test/ELF/relocatable-visibility.s create mode 100644 test/ELF/relocation-dtrace.test create mode 100644 test/ELF/relro-omagic.s create mode 100644 test/ELF/reproduce-backslash.s create mode 100644 test/ELF/reproduce-windows2.s create mode 100644 test/ELF/retain-symbols-file.s create mode 100644 test/ELF/section-symbols.test create mode 100644 test/ELF/sectionstart.s create mode 100644 test/ELF/segments.s create mode 100644 test/ELF/shf-info-link.test create mode 100644 test/ELF/sort-norosegment.s create mode 100644 test/ELF/startstop-shared2.s create mode 100644 test/ELF/symbol-ordering-file.s create mode 100644 test/ELF/synthetic-got.s create mode 100644 test/ELF/tls-relocatable.s create mode 100644 test/ELF/tls-weak-undef.s create mode 100644 test/ELF/ttext-tdata-tbss.s create mode 100644 test/ELF/version-script-complex-wildcards.s create mode 100644 test/ELF/version-script-extern-exact.s create mode 100644 test/ELF/version-script-extern-wildcards-anon.s create mode 100644 test/ELF/version-script-extern-wildcards.s create mode 100644 test/ELF/version-script-glob.s create mode 100644 test/ELF/version-script-locals-extern.s create mode 100644 test/ELF/version-script-locals.s create mode 100644 test/ELF/version-script-missing.s create mode 100644 test/ELF/version-script-no-warn.s create mode 100644 test/ELF/version-symbol-error.s delete mode 100644 test/ELF/wildcards.s create mode 100644 test/ELF/x86-64-relax-got-abs.s create mode 100644 test/ELF/zstack-size.s create mode 100644 test/mach-o/Inputs/arm64/libSystem.yaml create mode 100644 test/mach-o/Inputs/armv7/libSystem.yaml delete mode 100644 test/mach-o/Inputs/libSystem.yaml create mode 100644 test/mach-o/Inputs/x86/libSystem.yaml create mode 100644 test/mach-o/Inputs/x86_64/libSystem.yaml create mode 100644 test/mach-o/bind-opcodes.yaml create mode 100644 test/mach-o/debug-syms.yaml create mode 100644 test/mach-o/export-trie-order.yaml create mode 100644 test/mach-o/lc_segment_filesize.yaml create mode 100644 test/mach-o/string-table.yaml create mode 100644 test/mach-o/use-dylib.yaml diff --git a/CMakeLists.txt b/CMakeLists.txt index 46ca748f8facd..23cef2e9fc67c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,54 @@ +# Check if lld is built as a standalone project. +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + project(lld) + cmake_minimum_required(VERSION 3.4.3) + + set(CMAKE_INCLUDE_CURRENT_DIR ON) + set(LLD_BUILT_STANDALONE TRUE) + + find_program(LLVM_CONFIG_PATH "llvm-config" DOC "Path to llvm-config binary") + if(NOT LLVM_CONFIG_PATH) + message(FATAL_ERROR "llvm-config not found: specify LLVM_CONFIG_PATH") + endif() + + execute_process(COMMAND "${LLVM_CONFIG_PATH}" "--obj-root" "--includedir" + RESULT_VARIABLE HAD_ERROR + OUTPUT_VARIABLE LLVM_CONFIG_OUTPUT + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(HAD_ERROR) + message(FATAL_ERROR "llvm-config failed with status ${HAD_ERROR}") + endif() + + string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" LLVM_CONFIG_OUTPUT "${LLVM_CONFIG_OUTPUT}") + + list(GET LLVM_CONFIG_OUTPUT 0 OBJ_ROOT) + list(GET LLVM_CONFIG_OUTPUT 1 MAIN_INCLUDE_DIR) + + set(LLVM_OBJ_ROOT ${OBJ_ROOT} CACHE PATH "path to LLVM build tree") + set(LLVM_MAIN_INCLUDE_DIR ${MAIN_INCLUDE_DIR} CACHE PATH "path to llvm/include") + + file(TO_CMAKE_PATH ${LLVM_OBJ_ROOT} LLVM_BINARY_DIR) + set(LLVM_CMAKE_PATH "${LLVM_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm") + + if(NOT EXISTS "${LLVM_CMAKE_PATH}/LLVMConfig.cmake") + message(FATAL_ERROR "LLVMConfig.cmake not found") + endif() + include("${LLVM_CMAKE_PATH}/LLVMConfig.cmake") + + list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_PATH}") + + set(PACKAGE_VERSION "${LLVM_PACKAGE_VERSION}") + include_directories("${LLVM_BINARY_DIR}/include" ${LLVM_INCLUDE_DIRS}) + link_directories(${LLVM_LIBRARY_DIRS}) + + set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin) + find_program(LLVM_TABLEGEN_EXE "llvm-tblgen" ${LLVM_TOOLS_BINARY_DIR} NO_DEFAULT_PATH) + + include(AddLLVM) + include(TableGen) + include(HandleLLVMOptions) +endif() + set(LLD_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(LLD_INCLUDE_DIR ${LLD_SOURCE_DIR}/include ) set(LLD_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) @@ -55,6 +106,8 @@ endif() list (APPEND CMAKE_MODULE_PATH "${LLD_SOURCE_DIR}/cmake/modules") +include(AddLLD) + option(LLD_USE_VTUNE "Enable VTune user task tracking." OFF) @@ -67,6 +120,8 @@ if (LLD_USE_VTUNE) endif() endif() +option(LLD_BUILD_TOOLS + "Build the lld tools. If OFF, just generate build targets." ON) if (MSVC) add_definitions(-wd4530) # Suppress 'warning C4530: C++ exception handler used, but unwind semantics are not enabled.' @@ -87,12 +142,6 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) ) endif() -macro(add_lld_library name) - add_llvm_library(${name} ${ARGN}) - set_target_properties(${name} PROPERTIES FOLDER "lld libraries") -endmacro(add_lld_library) - - add_subdirectory(lib) add_subdirectory(tools/lld) diff --git a/COFF/CMakeLists.txt b/COFF/CMakeLists.txt index ad5b6fda1693a..70a33b9fdd818 100644 --- a/COFF/CMakeLists.txt +++ b/COFF/CMakeLists.txt @@ -2,6 +2,10 @@ set(LLVM_TARGET_DEFINITIONS Options.td) tablegen(LLVM Options.inc -gen-opt-parser-defs) add_public_tablegen_target(COFFOptionsTableGen) +if(NOT LLD_BUILT_STANDALONE) + set(tablegen_deps intrinsics_gen) +endif() + add_lld_library(lldCOFF Chunks.cpp DLL.cpp @@ -14,6 +18,7 @@ add_lld_library(lldCOFF MarkLive.cpp ModuleDef.cpp PDB.cpp + Strings.cpp SymbolTable.cpp Symbols.cpp Writer.cpp @@ -21,6 +26,9 @@ add_lld_library(lldCOFF LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} Core + DebugInfoCodeView + DebugInfoMSF + DebugInfoPDB LTO LibDriver Object @@ -30,7 +38,11 @@ add_lld_library(lldCOFF Option Support - LINK_LIBS ${PTHREAD_LIB} - ) + LINK_LIBS + lldCore + ${PTHREAD_LIB} -add_dependencies(lldCOFF COFFOptionsTableGen intrinsics_gen) + DEPENDS + COFFOptionsTableGen + ${tablegen_deps} + ) diff --git a/COFF/Chunks.cpp b/COFF/Chunks.cpp index 1c1b18176aa2a..7f0dfa92ec10f 100644 --- a/COFF/Chunks.cpp +++ b/COFF/Chunks.cpp @@ -28,7 +28,7 @@ namespace lld { namespace coff { SectionChunk::SectionChunk(ObjectFile *F, const coff_section *H) - : Chunk(SectionKind), Repl(this), File(F), Header(H), + : Chunk(SectionKind), Repl(this), Header(H), File(F), Relocs(File->getCOFFObj()->getRelocations(Header)), NumRelocs(std::distance(Relocs.begin(), Relocs.end())) { // Initialize SectionName. @@ -81,11 +81,23 @@ void SectionChunk::applyRelX86(uint8_t *Off, uint16_t Type, Defined *Sym, } static void applyMOV(uint8_t *Off, uint16_t V) { - or16(Off, ((V & 0x800) >> 1) | ((V >> 12) & 0xf)); - or16(Off + 2, ((V & 0x700) << 4) | (V & 0xff)); + write16le(Off, (read16le(Off) & 0xfbf0) | ((V & 0x800) >> 1) | ((V >> 12) & 0xf)); + write16le(Off + 2, (read16le(Off + 2) & 0x8f00) | ((V & 0x700) << 4) | (V & 0xff)); +} + +static uint16_t readMOV(uint8_t *Off) { + uint16_t Opcode1 = read16le(Off); + uint16_t Opcode2 = read16le(Off + 2); + uint16_t Imm = (Opcode2 & 0x00ff) | ((Opcode2 >> 4) & 0x0700); + Imm |= ((Opcode1 << 1) & 0x0800) | ((Opcode1 & 0x000f) << 12); + return Imm; } static void applyMOV32T(uint8_t *Off, uint32_t V) { + uint16_t ImmW = readMOV(Off); // read MOVW operand + uint16_t ImmT = readMOV(Off + 4); // read MOVT operand + uint32_t Imm = ImmW | (ImmT << 16); + V += Imm; // add the immediate offset applyMOV(Off, V); // set MOVW operand applyMOV(Off + 4, V >> 16); // set MOVT operand } @@ -99,11 +111,14 @@ static void applyBranch20T(uint8_t *Off, int32_t V) { } static void applyBranch24T(uint8_t *Off, int32_t V) { + if (!isInt<25>(V)) + fatal("relocation out of range"); uint32_t S = V < 0 ? 1 : 0; uint32_t J1 = ((~V >> 23) & 1) ^ S; uint32_t J2 = ((~V >> 22) & 1) ^ S; or16(Off, (S << 10) | ((V >> 12) & 0x3ff)); - or16(Off + 2, (J1 << 13) | (J2 << 11) | ((V >> 1) & 0x7ff)); + // Clear out the J1 and J2 bits which may be set. + write16le(Off + 2, (read16le(Off + 2) & 0xd000) | (J1 << 13) | (J2 << 11) | ((V >> 1) & 0x7ff)); } void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, Defined *Sym, @@ -119,6 +134,7 @@ void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, Defined *Sym, case IMAGE_REL_ARM_BRANCH20T: applyBranch20T(Off, S - P - 4); break; case IMAGE_REL_ARM_BRANCH24T: applyBranch24T(Off, S - P - 4); break; case IMAGE_REL_ARM_BLX23T: applyBranch24T(Off, S - P - 4); break; + case IMAGE_REL_ARM_SECREL: add32(Off, Sym->getSecrel()); break; default: fatal("unsupported relocation type"); } @@ -134,7 +150,7 @@ void SectionChunk::writeTo(uint8_t *Buf) const { // Apply relocations. for (const coff_relocation &Rel : Relocs) { uint8_t *Off = Buf + OutputSectionOff + Rel.VirtualAddress; - SymbolBody *Body = File->getSymbolBody(Rel.SymbolTableIndex)->repl(); + SymbolBody *Body = File->getSymbolBody(Rel.SymbolTableIndex); Defined *Sym = cast(Body); uint64_t P = RVA + Rel.VirtualAddress; switch (Config->Machine) { @@ -187,7 +203,7 @@ void SectionChunk::getBaserels(std::vector *Res) { uint8_t Ty = getBaserelType(Rel); if (Ty == IMAGE_REL_BASED_ABSOLUTE) continue; - SymbolBody *Body = File->getSymbolBody(Rel.SymbolTableIndex)->repl(); + SymbolBody *Body = File->getSymbolBody(Rel.SymbolTableIndex); if (isa(Body)) continue; Res->emplace_back(RVA + Rel.VirtualAddress, Ty); @@ -210,7 +226,7 @@ void SectionChunk::printDiscardedMessage() const { // Removed by dead-stripping. If it's removed by ICF, ICF already // printed out the name, so don't repeat that here. if (Sym && this == Repl) - llvm::outs() << "Discarded " << Sym->getName() << "\n"; + outs() << "Discarded " << Sym->getName() << "\n"; } StringRef SectionChunk::getDebugName() { @@ -233,7 +249,7 @@ void SectionChunk::replace(SectionChunk *Other) { CommonChunk::CommonChunk(const COFFSymbolRef S) : Sym(S) { // Common symbols are aligned on natural boundaries up to 32 bytes. // This is what MSVC link.exe does. - Align = std::min(uint64_t(32), NextPowerOf2(Sym.getValue())); + Align = std::min(uint64_t(32), PowerOf2Ceil(Sym.getValue())); } uint32_t CommonChunk::getPermissions() const { diff --git a/COFF/Chunks.h b/COFF/Chunks.h index cd0e2e69ef5d8..59e36b84c9b0b 100644 --- a/COFF/Chunks.h +++ b/COFF/Chunks.h @@ -17,7 +17,6 @@ #include "llvm/ADT/iterator.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Object/COFF.h" -#include #include #include @@ -29,7 +28,6 @@ using llvm::object::COFFSymbolRef; using llvm::object::SectionRef; using llvm::object::coff_relocation; using llvm::object::coff_section; -using llvm::sys::fs::file_magic; class Baserel; class Defined; @@ -187,11 +185,12 @@ public: // Auxiliary Format 5: Section Definitions. Used for ICF. uint32_t Checksum = 0; + const coff_section *Header; + private: // A file this chunk was created from. ObjectFile *File; - const coff_section *Header; StringRef SectionName; std::vector AssocChildren; llvm::iterator_range Relocs; @@ -202,7 +201,7 @@ private: // Used for ICF (Identical COMDAT Folding) void replace(SectionChunk *Other); - std::atomic GroupID = { 0 }; + uint32_t Color[2] = {0, 0}; // Sym points to a section symbol if this is a COMDAT chunk. DefinedRegular *Sym = nullptr; diff --git a/COFF/Config.h b/COFF/Config.h index a5472e937fa1a..0fa3338aa28ce 100644 --- a/COFF/Config.h +++ b/COFF/Config.h @@ -26,7 +26,8 @@ using llvm::StringRef; class DefinedAbsolute; class DefinedRelative; class StringChunk; -class Undefined; +struct Symbol; +class SymbolBody; // Short aliases. static const auto AMD64 = llvm::COFF::IMAGE_FILE_MACHINE_AMD64; @@ -37,7 +38,7 @@ static const auto I386 = llvm::COFF::IMAGE_FILE_MACHINE_I386; struct Export { StringRef Name; // N in /export:N or /export:E=N StringRef ExtName; // E in /export:E=N - Undefined *Sym = nullptr; + SymbolBody *Sym = nullptr; uint16_t Ordinal = 0; bool Noname = false; bool Data = false; @@ -61,6 +62,13 @@ struct Export { } }; +enum class DebugType { + None = 0x0, + CV = 0x1, /// CodeView + PData = 0x2, /// Procedure Data + Fixup = 0x4, /// Relocation Table +}; + // Global configuration. struct Configuration { enum ManifestKind { SideBySide, Embed, No }; @@ -69,7 +77,7 @@ struct Configuration { llvm::COFF::MachineTypes Machine = IMAGE_FILE_MACHINE_UNKNOWN; bool Verbose = false; WindowsSubsystem Subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN; - Undefined *Entry = nullptr; + SymbolBody *Entry = nullptr; bool NoEntry = false; std::string OutputFile; bool DoGC = true; @@ -78,9 +86,11 @@ struct Configuration { bool Force = false; bool Debug = false; bool WriteSymtab = true; + unsigned DebugTypes = static_cast(DebugType::None); + StringRef PDBPath; // Symbols in this set are considered as live by the garbage collector. - std::set GCRoot; + std::set GCRoot; std::set NoDefaultLibs; bool NoDefaultLibAll = false; @@ -91,11 +101,11 @@ struct Configuration { std::vector Exports; std::set DelayLoads; std::map DLLOrder; - Undefined *DelayLoadHelper = nullptr; + SymbolBody *DelayLoadHelper = nullptr; // Used for SafeSEH. - DefinedRelative *SEHTable = nullptr; - DefinedAbsolute *SEHCount = nullptr; + Symbol *SEHTable = nullptr; + Symbol *SEHCount = nullptr; // Used for /opt:lldlto=N unsigned LTOOptLevel = 2; @@ -141,6 +151,10 @@ struct Configuration { bool TerminalServerAware = true; bool LargeAddressAware = false; bool HighEntropyVA = false; + + // This is for debugging. + bool DebugPdb = false; + bool DumpPdb = false; }; extern Configuration *Config; diff --git a/COFF/DLL.cpp b/COFF/DLL.cpp index 9ac370c11d592..f93dc5cde44ca 100644 --- a/COFF/DLL.cpp +++ b/COFF/DLL.cpp @@ -324,7 +324,7 @@ public: if (E.ForwardChunk) { write32le(P, E.ForwardChunk->getRVA()); } else { - write32le(P, cast(E.Sym->repl())->getRVA()); + write32le(P, cast(E.Sym)->getRVA()); } } } diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp index bb6a60e4fc4c1..dc3a00ba55ed0 100644 --- a/COFF/Driver.cpp +++ b/COFF/Driver.cpp @@ -7,15 +7,17 @@ // //===----------------------------------------------------------------------===// -#include "Config.h" #include "Driver.h" +#include "Config.h" #include "Error.h" #include "InputFiles.h" +#include "Memory.h" #include "SymbolTable.h" #include "Symbols.h" #include "Writer.h" #include "lld/Driver/Driver.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/LibDriver/LibDriver.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" @@ -28,6 +30,13 @@ #include #include +#ifdef _MSC_VER +// depends on for __uncaught_exception. +#include +#endif + +#include + using namespace llvm; using namespace llvm::COFF; using llvm::sys::Process; @@ -41,11 +50,13 @@ namespace coff { Configuration *Config; LinkerDriver *Driver; -bool link(llvm::ArrayRef Args) { - Configuration C; - LinkerDriver D; - Config = &C; - Driver = &D; +BumpPtrAllocator BAlloc; +StringSaver Saver{BAlloc}; +std::vector SpecificAllocBase::Instances; + +bool link(ArrayRef Args) { + Config = make(); + Driver = make(); Driver->link(Args); return true; } @@ -58,26 +69,123 @@ static std::string getOutputPath(StringRef Path) { return (S.substr(0, S.rfind('.')) + E).str(); } -// Opens a file. Path has to be resolved already. -// Newly created memory buffers are owned by this driver. -MemoryBufferRef LinkerDriver::openFile(StringRef Path) { - std::unique_ptr MB = - check(MemoryBuffer::getFile(Path), "could not open " + Path); - MemoryBufferRef MBRef = MB->getMemBufferRef(); - OwningMBs.push_back(std::move(MB)); // take ownership +// ErrorOr is not default constructible, so it cannot be used as the type +// parameter of a future. +// FIXME: We could open the file in createFutureForFile and avoid needing to +// return an error here, but for the moment that would cost us a file descriptor +// (a limited resource on Windows) for the duration that the future is pending. +typedef std::pair, std::error_code> MBErrPair; + +// Create a std::future that opens and maps a file using the best strategy for +// the host platform. +static std::future createFutureForFile(std::string Path) { +#if LLVM_ON_WIN32 + // On Windows, file I/O is relatively slow so it is best to do this + // asynchronously. + auto Strategy = std::launch::async; +#else + auto Strategy = std::launch::deferred; +#endif + return std::async(Strategy, [=]() { + auto MBOrErr = MemoryBuffer::getFile(Path); + if (!MBOrErr) + return MBErrPair{nullptr, MBOrErr.getError()}; + return MBErrPair{std::move(*MBOrErr), std::error_code()}; + }); +} + +MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr MB) { + MemoryBufferRef MBRef = *MB; + OwningMBs.push_back(std::move(MB)); + + if (Driver->Cpio) + Driver->Cpio->append(relativeToRoot(MBRef.getBufferIdentifier()), + MBRef.getBuffer()); + return MBRef; } -static std::unique_ptr createFile(MemoryBufferRef MB) { +void LinkerDriver::addBuffer(std::unique_ptr MB) { + MemoryBufferRef MBRef = takeBuffer(std::move(MB)); + // File type is detected by contents, not by file extension. - file_magic Magic = identify_magic(MB.getBuffer()); + file_magic Magic = identify_magic(MBRef.getBuffer()); + if (Magic == file_magic::windows_resource) { + Resources.push_back(MBRef); + return; + } + + FilePaths.push_back(MBRef.getBufferIdentifier()); if (Magic == file_magic::archive) - return std::unique_ptr(new ArchiveFile(MB)); + return Symtab.addFile(make(MBRef)); if (Magic == file_magic::bitcode) - return std::unique_ptr(new BitcodeFile(MB)); + return Symtab.addFile(make(MBRef)); + if (Magic == file_magic::coff_cl_gl_object) + fatal(MBRef.getBufferIdentifier() + ": is not a native COFF file. " + "Recompile without /GL"); + Symtab.addFile(make(MBRef)); +} + +void LinkerDriver::enqueuePath(StringRef Path) { + auto Future = + std::make_shared>(createFutureForFile(Path)); + std::string PathStr = Path; + enqueueTask([=]() { + auto MBOrErr = Future->get(); + if (MBOrErr.second) + fatal(MBOrErr.second, "could not open " + PathStr); + Driver->addBuffer(std::move(MBOrErr.first)); + }); + if (Config->OutputFile == "") - Config->OutputFile = getOutputPath(MB.getBufferIdentifier()); - return std::unique_ptr(new ObjectFile(MB)); + Config->OutputFile = getOutputPath(Path); +} + +void LinkerDriver::addArchiveBuffer(MemoryBufferRef MB, StringRef SymName, + StringRef ParentName) { + file_magic Magic = identify_magic(MB.getBuffer()); + if (Magic == file_magic::coff_import_library) { + Symtab.addFile(make(MB)); + return; + } + + InputFile *Obj; + if (Magic == file_magic::coff_object) + Obj = make(MB); + else if (Magic == file_magic::bitcode) + Obj = make(MB); + else + fatal("unknown file type: " + MB.getBufferIdentifier()); + + Obj->ParentName = ParentName; + Symtab.addFile(Obj); + if (Config->Verbose) + outs() << "Loaded " << toString(Obj) << " for " << SymName << "\n"; +} + +void LinkerDriver::enqueueArchiveMember(const Archive::Child &C, + StringRef SymName, + StringRef ParentName) { + if (!C.getParent()->isThin()) { + MemoryBufferRef MB = check( + C.getMemoryBufferRef(), + "could not get the buffer for the member defining symbol " + SymName); + enqueueTask([=]() { Driver->addArchiveBuffer(MB, SymName, ParentName); }); + return; + } + + auto Future = std::make_shared>(createFutureForFile( + check(C.getFullName(), + "could not get the filename for the member defining symbol " + + SymName))); + enqueueTask([=]() { + auto MBOrErr = Future->get(); + if (MBOrErr.second) + fatal(MBOrErr.second, + "could not get the buffer for the member defining " + SymName); + Driver->addArchiveBuffer(takeBuffer(std::move(MBOrErr.first)), SymName, + ParentName); + }); } static bool isDecorated(StringRef Sym) { @@ -87,7 +195,7 @@ static bool isDecorated(StringRef Sym) { // Parses .drectve section contents and returns a list of files // specified by /defaultlib. void LinkerDriver::parseDirectives(StringRef S) { - llvm::opt::InputArgList Args = Parser.parse(S); + opt::InputArgList Args = Parser.parse(S); for (auto *Arg : Args) { switch (Arg->getOption().getID()) { @@ -95,10 +203,8 @@ void LinkerDriver::parseDirectives(StringRef S) { parseAlternateName(Arg->getValue()); break; case OPT_defaultlib: - if (Optional Path = findLib(Arg->getValue())) { - MemoryBufferRef MB = openFile(*Path); - Symtab.addFile(createFile(MB)); - } + if (Optional Path = findLib(Arg->getValue())) + enqueuePath(*Path); break; case OPT_export: { Export E = parseExport(Arg->getValue()); @@ -135,19 +241,19 @@ void LinkerDriver::parseDirectives(StringRef S) { // Find file from search paths. You can omit ".obj", this function takes // care of that. Note that the returned path is not guaranteed to exist. StringRef LinkerDriver::doFindFile(StringRef Filename) { - bool hasPathSep = (Filename.find_first_of("/\\") != StringRef::npos); - if (hasPathSep) + bool HasPathSep = (Filename.find_first_of("/\\") != StringRef::npos); + if (HasPathSep) return Filename; - bool hasExt = (Filename.find('.') != StringRef::npos); + bool HasExt = (Filename.find('.') != StringRef::npos); for (StringRef Dir : SearchPaths) { SmallString<128> Path = Dir; - llvm::sys::path::append(Path, Filename); - if (llvm::sys::fs::exists(Path.str())) - return Alloc.save(Path.str()); - if (!hasExt) { + sys::path::append(Path, Filename); + if (sys::fs::exists(Path.str())) + return Saver.save(Path.str()); + if (!HasExt) { Path.append(".obj"); - if (llvm::sys::fs::exists(Path.str())) - return Alloc.save(Path.str()); + if (sys::fs::exists(Path.str())) + return Saver.save(Path.str()); } } return Filename; @@ -166,9 +272,9 @@ Optional LinkerDriver::findFile(StringRef Filename) { // Find library file from search path. StringRef LinkerDriver::doFindLib(StringRef Filename) { // Add ".lib" to Filename if that has no file extension. - bool hasExt = (Filename.find('.') != StringRef::npos); - if (!hasExt) - Filename = Alloc.save(Filename + ".lib"); + bool HasExt = (Filename.find('.') != StringRef::npos); + if (!HasExt) + Filename = Saver.save(Filename + ".lib"); return doFindFile(Filename); } @@ -178,11 +284,12 @@ StringRef LinkerDriver::doFindLib(StringRef Filename) { Optional LinkerDriver::findLib(StringRef Filename) { if (Config->NoDefaultLibAll) return None; + if (!VisitedLibs.insert(Filename.lower()).second) + return None; StringRef Path = doFindLib(Filename); if (Config->NoDefaultLibs.count(Path)) return None; - bool Seen = !VisitedFiles.insert(Path.lower()).second; - if (Seen) + if (!VisitedFiles.insert(Path.lower()).second) return None; return Path; } @@ -192,7 +299,7 @@ void LinkerDriver::addLibSearchPaths() { Optional EnvOpt = Process::GetEnv("LIB"); if (!EnvOpt.hasValue()) return; - StringRef Env = Alloc.save(*EnvOpt); + StringRef Env = Saver.save(*EnvOpt); while (!Env.empty()) { StringRef Path; std::tie(Path, Env) = Env.split(';'); @@ -200,17 +307,17 @@ void LinkerDriver::addLibSearchPaths() { } } -Undefined *LinkerDriver::addUndefined(StringRef Name) { - Undefined *U = Symtab.addUndefined(Name); - Config->GCRoot.insert(U); - return U; +SymbolBody *LinkerDriver::addUndefined(StringRef Name) { + SymbolBody *B = Symtab.addUndefined(Name); + Config->GCRoot.insert(B); + return B; } // Symbol names are mangled by appending "_" prefix on x86. StringRef LinkerDriver::mangle(StringRef Sym) { assert(Config->Machine != IMAGE_FILE_MACHINE_UNKNOWN); if (Config->Machine == I386) - return Alloc.save("_" + Sym); + return Saver.save("_" + Sym); return Sym; } @@ -225,7 +332,7 @@ StringRef LinkerDriver::findDefaultEntry() { }; for (auto E : Entries) { StringRef Entry = Symtab.findMangle(mangle(E[0])); - if (!Entry.empty() && !isa(Symtab.find(Entry)->Body)) + if (!Entry.empty() && !isa(Symtab.find(Entry)->body())) return mangle(E[1]); } return ""; @@ -247,7 +354,83 @@ static uint64_t getDefaultImageBase() { return Config->DLL ? 0x10000000 : 0x400000; } -void LinkerDriver::link(llvm::ArrayRef ArgsArr) { +static std::string createResponseFile(const opt::InputArgList &Args, + ArrayRef FilePaths, + ArrayRef SearchPaths) { + SmallString<0> Data; + raw_svector_ostream OS(Data); + + for (auto *Arg : Args) { + switch (Arg->getOption().getID()) { + case OPT_linkrepro: + case OPT_INPUT: + case OPT_defaultlib: + case OPT_libpath: + break; + default: + OS << stringize(Arg) << "\n"; + } + } + + for (StringRef Path : SearchPaths) { + std::string RelPath = relativeToRoot(Path); + OS << "/libpath:" << quote(RelPath) << "\n"; + } + + for (StringRef Path : FilePaths) + OS << quote(relativeToRoot(Path)) << "\n"; + + return Data.str(); +} + +static unsigned getDefaultDebugType(const opt::InputArgList &Args) { + unsigned DebugTypes = static_cast(DebugType::CV); + if (Args.hasArg(OPT_driver)) + DebugTypes |= static_cast(DebugType::PData); + if (Args.hasArg(OPT_profile)) + DebugTypes |= static_cast(DebugType::Fixup); + return DebugTypes; +} + +static unsigned parseDebugType(StringRef Arg) { + SmallVector Types; + Arg.split(Types, ',', /*KeepEmpty=*/false); + + unsigned DebugTypes = static_cast(DebugType::None); + for (StringRef Type : Types) + DebugTypes |= StringSwitch(Type.lower()) + .Case("cv", static_cast(DebugType::CV)) + .Case("pdata", static_cast(DebugType::PData)) + .Case("fixup", static_cast(DebugType::Fixup)); + return DebugTypes; +} + +static std::string getMapFile(const opt::InputArgList &Args) { + auto *Arg = Args.getLastArg(OPT_lldmap, OPT_lldmap_file); + if (!Arg) + return ""; + if (Arg->getOption().getID() == OPT_lldmap_file) + return Arg->getValue(); + + assert(Arg->getOption().getID() == OPT_lldmap); + StringRef OutFile = Config->OutputFile; + return (OutFile.substr(0, OutFile.rfind('.')) + ".map").str(); +} + +void LinkerDriver::enqueueTask(std::function Task) { + TaskQueue.push_back(std::move(Task)); +} + +bool LinkerDriver::run() { + bool DidWork = !TaskQueue.empty(); + while (!TaskQueue.empty()) { + TaskQueue.front()(); + TaskQueue.pop_front(); + } + return DidWork; +} + +void LinkerDriver::link(ArrayRef ArgsArr) { // If the first command line argument is "/lib", link.exe acts like lib.exe. // We call our own implementation of lib.exe that understands bitcode files. if (ArgsArr.size() > 1 && StringRef(ArgsArr[1]).equals_lower("/lib")) { @@ -257,15 +440,15 @@ void LinkerDriver::link(llvm::ArrayRef ArgsArr) { } // Needed for LTO. - llvm::InitializeAllTargetInfos(); - llvm::InitializeAllTargets(); - llvm::InitializeAllTargetMCs(); - llvm::InitializeAllAsmParsers(); - llvm::InitializeAllAsmPrinters(); - llvm::InitializeAllDisassemblers(); + InitializeAllTargetInfos(); + InitializeAllTargets(); + InitializeAllTargetMCs(); + InitializeAllAsmParsers(); + InitializeAllAsmPrinters(); + InitializeAllDisassemblers(); // Parse command line options. - llvm::opt::InputArgList Args = Parser.parseLINK(ArgsArr.slice(1)); + opt::InputArgList Args = Parser.parseLINK(ArgsArr.slice(1)); // Handle /help if (Args.hasArg(OPT_help)) { @@ -273,6 +456,17 @@ void LinkerDriver::link(llvm::ArrayRef ArgsArr) { return; } + if (auto *Arg = Args.getLastArg(OPT_linkrepro)) { + SmallString<64> Path = StringRef(Arg->getValue()); + sys::path::append(Path, "repro"); + ErrorOr F = CpioFile::create(Path); + if (F) + Cpio.reset(*F); + else + errs() << "/linkrepro: failed to open " << Path + << ".cpio: " << F.getError().message() << '\n'; + } + if (Args.filtered_begin(OPT_INPUT) == Args.filtered_end()) fatal("no input files"); @@ -295,8 +489,17 @@ void LinkerDriver::link(llvm::ArrayRef ArgsArr) { Config->Force = true; // Handle /debug - if (Args.hasArg(OPT_debug)) + if (Args.hasArg(OPT_debug)) { Config->Debug = true; + Config->DebugTypes = + Args.hasArg(OPT_debugtype) + ? parseDebugType(Args.getLastArg(OPT_debugtype)->getValue()) + : getDefaultDebugType(Args); + } + + // Create a dummy PDB file to satisfy build sytem rules. + if (auto *Arg = Args.getLastArg(OPT_pdb)) + Config->PDBPath = Arg->getValue(); // Handle /noentry if (Args.hasArg(OPT_noentry)) { @@ -447,72 +650,43 @@ void LinkerDriver::link(llvm::ArrayRef ArgsArr) { Config->TerminalServerAware = false; if (Args.hasArg(OPT_nosymtab)) Config->WriteSymtab = false; + Config->DumpPdb = Args.hasArg(OPT_dumppdb); + Config->DebugPdb = Args.hasArg(OPT_debugpdb); // Create a list of input files. Files can be given as arguments // for /defaultlib option. - std::vector Paths; std::vector MBs; for (auto *Arg : Args.filtered(OPT_INPUT)) if (Optional Path = findFile(Arg->getValue())) - Paths.push_back(*Path); + enqueuePath(*Path); for (auto *Arg : Args.filtered(OPT_defaultlib)) if (Optional Path = findLib(Arg->getValue())) - Paths.push_back(*Path); - for (StringRef Path : Paths) - MBs.push_back(openFile(Path)); + enqueuePath(*Path); // Windows specific -- Create a resource file containing a manifest file. - if (Config->Manifest == Configuration::Embed) { - std::unique_ptr MB = createManifestRes(); - MBs.push_back(MB->getMemBufferRef()); - OwningMBs.push_back(std::move(MB)); // take ownership - } + if (Config->Manifest == Configuration::Embed) + addBuffer(createManifestRes()); - // Windows specific -- Input files can be Windows resource files (.res files). - // We invoke cvtres.exe to convert resource files to a regular COFF file - // then link the result file normally. - std::vector Resources; - auto NotResource = [](MemoryBufferRef MB) { - return identify_magic(MB.getBuffer()) != file_magic::windows_resource; - }; - auto It = std::stable_partition(MBs.begin(), MBs.end(), NotResource); - if (It != MBs.end()) { - Resources.insert(Resources.end(), It, MBs.end()); - MBs.erase(It, MBs.end()); - } + // Read all input files given via the command line. + run(); - // Read all input files given via the command line. Note that step() - // doesn't read files that are specified by directive sections. - for (MemoryBufferRef MB : MBs) - Symtab.addFile(createFile(MB)); - Symtab.step(); - - // Determine machine type and check if all object files are - // for the same CPU type. Note that this needs to be done before - // any call to mangle(). - for (std::unique_ptr &File : Symtab.getFiles()) { - MachineTypes MT = File->getMachineType(); - if (MT == IMAGE_FILE_MACHINE_UNKNOWN) - continue; - if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) { - Config->Machine = MT; - continue; - } - if (Config->Machine != MT) - fatal(File->getShortName() + ": machine type " + machineToStr(MT) + - " conflicts with " + machineToStr(Config->Machine)); - } + // We should have inferred a machine type by now from the input files, but if + // not we assume x64. if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) { - llvm::errs() << "warning: /machine is not specified. x64 is assumed.\n"; + errs() << "warning: /machine is not specified. x64 is assumed.\n"; Config->Machine = AMD64; } - // Windows specific -- Convert Windows resource files to a COFF file. - if (!Resources.empty()) { - std::unique_ptr MB = convertResToCOFF(Resources); - Symtab.addFile(createFile(MB->getMemBufferRef())); - OwningMBs.push_back(std::move(MB)); // take ownership - } + // Windows specific -- Input files can be Windows resource files (.res files). + // We invoke cvtres.exe to convert resource files to a regular COFF file + // then link the result file normally. + if (!Resources.empty()) + addBuffer(convertResToCOFF(Resources)); + + if (Cpio) + Cpio->append("response.txt", + createResponseFile(Args, FilePaths, + ArrayRef(SearchPaths).slice(1))); // Handle /largeaddressaware if (Config->is64() || Args.hasArg(OPT_largeaddressaware)) @@ -537,7 +711,7 @@ void LinkerDriver::link(llvm::ArrayRef ArgsArr) { fatal("entry point must be defined"); Config->Entry = addUndefined(S); if (Config->Verbose) - llvm::outs() << "Entry name inferred: " << S << "\n"; + outs() << "Entry name inferred: " << S << "\n"; } // Handle /export @@ -545,18 +719,19 @@ void LinkerDriver::link(llvm::ArrayRef ArgsArr) { Export E = parseExport(Arg->getValue()); if (Config->Machine == I386) { if (!isDecorated(E.Name)) - E.Name = Alloc.save("_" + E.Name); + E.Name = Saver.save("_" + E.Name); if (!E.ExtName.empty() && !isDecorated(E.ExtName)) - E.ExtName = Alloc.save("_" + E.ExtName); + E.ExtName = Saver.save("_" + E.ExtName); } Config->Exports.push_back(E); } // Handle /def if (auto *Arg = Args.getLastArg(OPT_deffile)) { - MemoryBufferRef MB = openFile(Arg->getValue()); // parseModuleDefs mutates Config object. - parseModuleDefs(MB, &Alloc); + parseModuleDefs( + takeBuffer(check(MemoryBuffer::getFile(Arg->getValue()), + Twine("could not open ") + Arg->getValue()))); } // Handle /delayload @@ -585,14 +760,10 @@ void LinkerDriver::link(llvm::ArrayRef ArgsArr) { Symtab.addAbsolute(mangle("__guard_fids_count"), 0); Symtab.addAbsolute(mangle("__guard_flags"), 0x100); - // Read as much files as we can from directives sections. - Symtab.run(); - - // Resolve auxiliary symbols until we get a convergence. - // (Trying to resolve a symbol may trigger a Lazy symbol to load a new file. - // A new file may contain a directive section to add new command line options. - // That's why we have to repeat until converge.) - for (;;) { + // This code may add new undefined symbols to the link, which may enqueue more + // symbol resolution tasks, so we need to continue executing tasks until we + // converge. + do { // Windows specific -- if entry point is not found, // search for its mangled names. if (Config->Entry) @@ -615,7 +786,7 @@ void LinkerDriver::link(llvm::ArrayRef ArgsArr) { Symbol *Sym = Symtab.find(From); if (!Sym) continue; - if (auto *U = dyn_cast(Sym->Body)) + if (auto *U = dyn_cast(Sym->body())) if (!U->WeakAlias) U->WeakAlias = Symtab.addUndefined(To); } @@ -623,18 +794,15 @@ void LinkerDriver::link(llvm::ArrayRef ArgsArr) { // Windows specific -- if __load_config_used can be resolved, resolve it. if (Symtab.findUnderscore("_load_config_used")) addUndefined(mangle("_load_config_used")); - - if (Symtab.queueEmpty()) - break; - Symtab.run(); - } + } while (run()); // Do LTO by compiling bitcode input files to a set of native COFF files then // link those files. Symtab.addCombinedLTOObjects(); + run(); // Make sure we have resolved all symbols. - Symtab.reportRemainingUndefines(/*Resolve=*/true); + Symtab.reportRemainingUndefines(); // Windows specific -- if no /subsystem is given, we need to infer // that from entry point name. @@ -662,10 +830,6 @@ void LinkerDriver::link(llvm::ArrayRef ArgsArr) { if (Config->Manifest == Configuration::SideBySide) createSideBySideManifest(); - // Create a dummy PDB file to satisfy build sytem rules. - if (auto *Arg = Args.getLastArg(OPT_pdb)) - createPDB(Arg->getValue()); - // Identify unreferenced COMDAT sections. if (Config->DoGC) markLive(Symtab.getChunks()); @@ -679,13 +843,15 @@ void LinkerDriver::link(llvm::ArrayRef ArgsArr) { // Create a symbol map file containing symbol VAs and their names // to help debugging. - if (auto *Arg = Args.getLastArg(OPT_lldmap)) { + std::string MapFile = getMapFile(Args); + if (!MapFile.empty()) { std::error_code EC; - llvm::raw_fd_ostream Out(Arg->getValue(), EC, OpenFlags::F_Text); + raw_fd_ostream Out(MapFile, EC, OpenFlags::F_Text); if (EC) - fatal(EC, "could not create the symbol map"); + fatal(EC, "could not create the symbol map " + MapFile); Symtab.printMap(Out); } + // Call exit to avoid calling destructors. exit(0); } diff --git a/COFF/Driver.h b/COFF/Driver.h index 23969ee802fb4..e8114640edecc 100644 --- a/COFF/Driver.h +++ b/COFF/Driver.h @@ -13,12 +13,13 @@ #include "Config.h" #include "SymbolTable.h" #include "lld/Core/LLVM.h" +#include "lld/Core/Reproduce.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Object/Archive.h" #include "llvm/Object/COFF.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" -#include "llvm/Support/StringSaver.h" #include #include #include @@ -42,7 +43,6 @@ void doICF(const std::vector &Chunks); class ArgParser { public: - ArgParser() : Alloc(AllocAux) {} // Parses command line options. llvm::opt::InputArgList parse(llvm::ArrayRef Args); @@ -56,25 +56,26 @@ private: std::vector tokenize(StringRef S); std::vector replaceResponseFiles(std::vector); - - llvm::BumpPtrAllocator AllocAux; - llvm::StringSaver Alloc; }; class LinkerDriver { public: - LinkerDriver() : Alloc(AllocAux) {} + LinkerDriver() { coff::Symtab = &Symtab; } void link(llvm::ArrayRef Args); // Used by the resolver to parse .drectve section contents. void parseDirectives(StringRef S); + // Used by ArchiveFile to enqueue members. + void enqueueArchiveMember(const Archive::Child &C, StringRef SymName, + StringRef ParentName); + private: - llvm::BumpPtrAllocator AllocAux; - llvm::StringSaver Alloc; ArgParser Parser; SymbolTable Symtab; + std::unique_ptr Cpio; // for /linkrepro + // Opens a file. Path has to be resolved already. MemoryBufferRef openFile(StringRef Path); @@ -90,8 +91,9 @@ private: // Library search path. The first element is always "" (current directory). std::vector SearchPaths; std::set VisitedFiles; + std::set VisitedLibs; - Undefined *addUndefined(StringRef Sym); + SymbolBody *addUndefined(StringRef Sym); StringRef mangle(StringRef Sym); // Windows specific -- "main" is not the only main function in Windows. @@ -104,12 +106,26 @@ private: StringRef findDefaultEntry(); WindowsSubsystem inferSubsystem(); + MemoryBufferRef takeBuffer(std::unique_ptr MB); + void addBuffer(std::unique_ptr MB); + void addArchiveBuffer(MemoryBufferRef MBRef, StringRef SymName, + StringRef ParentName); + + void enqueuePath(StringRef Path); + + void enqueueTask(std::function Task); + bool run(); + // Driver is the owner of all opened files. // InputFiles have MemoryBufferRefs to them. std::vector> OwningMBs; + + std::list> TaskQueue; + std::vector FilePaths; + std::vector Resources; }; -void parseModuleDefs(MemoryBufferRef MB, llvm::StringSaver *Alloc); +void parseModuleDefs(MemoryBufferRef MB); void writeImportLibrary(); // Functions below this line are defined in DriverUtils.cpp. @@ -161,8 +177,6 @@ void checkFailIfMismatch(StringRef Arg); std::unique_ptr convertResToCOFF(const std::vector &MBs); -void createPDB(StringRef Path); - // Create enum with OPT_xxx values for each option in Options.td enum { OPT_INVALID = 0, diff --git a/COFF/DriverUtils.cpp b/COFF/DriverUtils.cpp index 5d7dc2bc65aff..14dd004f1c049 100644 --- a/COFF/DriverUtils.cpp +++ b/COFF/DriverUtils.cpp @@ -16,6 +16,7 @@ #include "Config.h" #include "Driver.h" #include "Error.h" +#include "Memory.h" #include "Symbols.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSwitch.h" @@ -43,29 +44,29 @@ namespace { class Executor { public: explicit Executor(StringRef S) : Saver(Alloc), Prog(Saver.save(S)) {} - void add(StringRef S) { Args.push_back(Saver.save(S)); } - void add(std::string &S) { Args.push_back(Saver.save(S)); } - void add(Twine S) { Args.push_back(Saver.save(S)); } - void add(const char *S) { Args.push_back(Saver.save(S)); } + void add(StringRef S) { Args.push_back(Saver.save(S).data()); } + void add(std::string &S) { Args.push_back(Saver.save(S).data()); } + void add(Twine S) { Args.push_back(Saver.save(S).data()); } + void add(const char *S) { Args.push_back(Saver.save(S).data()); } void run() { - ErrorOr ExeOrErr = llvm::sys::findProgramByName(Prog); + ErrorOr ExeOrErr = sys::findProgramByName(Prog); if (auto EC = ExeOrErr.getError()) fatal(EC, "unable to find " + Prog + " in PATH: "); - const char *Exe = Saver.save(*ExeOrErr); + const char *Exe = Saver.save(*ExeOrErr).data(); Args.insert(Args.begin(), Exe); Args.push_back(nullptr); - if (llvm::sys::ExecuteAndWait(Args[0], Args.data()) != 0) { + if (sys::ExecuteAndWait(Args[0], Args.data()) != 0) { for (const char *S : Args) if (S) - llvm::errs() << S << " "; + errs() << S << " "; fatal("ExecuteAndWait failed"); } } private: - llvm::BumpPtrAllocator Alloc; - llvm::StringSaver Saver; + BumpPtrAllocator Alloc; + StringSaver Saver; StringRef Prog; std::vector Args; }; @@ -75,10 +76,8 @@ private: // Returns /machine's value. MachineTypes getMachineType(StringRef S) { MachineTypes MT = StringSwitch(S.lower()) - .Case("x64", AMD64) - .Case("amd64", AMD64) - .Case("x86", I386) - .Case("i386", I386) + .Cases("x64", "amd64", AMD64) + .Cases("x86", "i386", I386) .Case("arm", ARMNT) .Default(IMAGE_FILE_MACHINE_UNKNOWN); if (MT != IMAGE_FILE_MACHINE_UNKNOWN) @@ -168,8 +167,8 @@ void parseMerge(StringRef S) { if (!Inserted) { StringRef Existing = Pair.first->second; if (Existing != To) - llvm::errs() << "warning: " << S << ": already merged into " - << Existing << "\n"; + errs() << "warning: " << S << ": already merged into " << Existing + << "\n"; } } @@ -279,18 +278,54 @@ static void quoteAndPrint(raw_ostream &Out, StringRef S) { } } +// An RAII temporary file class that automatically removes a temporary file. +namespace { +class TemporaryFile { +public: + TemporaryFile(StringRef Prefix, StringRef Extn) { + SmallString<128> S; + if (auto EC = sys::fs::createTemporaryFile("lld-" + Prefix, Extn, S)) + fatal(EC, "cannot create a temporary file"); + Path = S.str(); + } + + TemporaryFile(TemporaryFile &&Obj) { + std::swap(Path, Obj.Path); + } + + ~TemporaryFile() { + if (Path.empty()) + return; + if (sys::fs::remove(Path)) + fatal("failed to remove " + Path); + } + + // Returns a memory buffer of this temporary file. + // Note that this function does not leave the file open, + // so it is safe to remove the file immediately after this function + // is called (you cannot remove an opened file on Windows.) + std::unique_ptr getMemoryBuffer() { + // IsVolatileSize=true forces MemoryBuffer to not use mmap(). + return check(MemoryBuffer::getFile(Path, /*FileSize=*/-1, + /*RequiresNullTerminator=*/false, + /*IsVolatileSize=*/true), + "could not open " + Path); + } + + std::string Path; +}; +} + // Create the default manifest file as a temporary file. -static std::string createDefaultXml() { +TemporaryFile createDefaultXml() { // Create a temporary file. - SmallString<128> Path; - if (auto EC = sys::fs::createTemporaryFile("tmp", "manifest", Path)) - fatal(EC, "cannot create a temporary file"); + TemporaryFile File("defaultxml", "manifest"); // Open the temporary file for writing. std::error_code EC; - llvm::raw_fd_ostream OS(Path, EC, sys::fs::F_Text); + raw_fd_ostream OS(File.Path, EC, sys::fs::F_Text); if (EC) - fatal(EC, "failed to open " + Path); + fatal(EC, "failed to open " + File.Path); // Emit the XML. Note that we do *not* verify that the XML attributes are // syntactically correct. This is intentional for link.exe compatibility. @@ -316,56 +351,48 @@ static std::string createDefaultXml() { } OS << "\n"; OS.close(); - return StringRef(Path); + return File; } static std::string readFile(StringRef Path) { std::unique_ptr MB = check(MemoryBuffer::getFile(Path), "could not open " + Path); - std::unique_ptr Buf(std::move(MB)); - return Buf->getBuffer(); + return MB->getBuffer(); } static std::string createManifestXml() { // Create the default manifest file. - std::string Path1 = createDefaultXml(); + TemporaryFile File1 = createDefaultXml(); if (Config->ManifestInput.empty()) - return readFile(Path1); + return readFile(File1.Path); // If manifest files are supplied by the user using /MANIFESTINPUT // option, we need to merge them with the default manifest. - SmallString<128> Path2; - if (auto EC = sys::fs::createTemporaryFile("tmp", "manifest", Path2)) - fatal(EC, "cannot create a temporary file"); - FileRemover Remover1(Path1); - FileRemover Remover2(Path2); + TemporaryFile File2("user", "manifest"); Executor E("mt.exe"); E.add("/manifest"); - E.add(Path1); + E.add(File1.Path); for (StringRef Filename : Config->ManifestInput) { E.add("/manifest"); E.add(Filename); } E.add("/nologo"); - E.add("/out:" + StringRef(Path2)); + E.add("/out:" + StringRef(File2.Path)); E.run(); - return readFile(Path2); + return readFile(File2.Path); } // Create a resource file containing a manifest XML. std::unique_ptr createManifestRes() { // Create a temporary file for the resource script file. - SmallString<128> RCPath; - if (auto EC = sys::fs::createTemporaryFile("tmp", "rc", RCPath)) - fatal(EC, "cannot create a temporary file"); - FileRemover RCRemover(RCPath); + TemporaryFile RCFile("manifest", "rc"); // Open the temporary file for writing. std::error_code EC; - llvm::raw_fd_ostream Out(RCPath, EC, sys::fs::F_Text); + raw_fd_ostream Out(RCFile.Path, EC, sys::fs::F_Text); if (EC) - fatal(EC, "failed to open " + RCPath); + fatal(EC, "failed to open " + RCFile.Path); // Write resource script to the RC file. Out << "#define LANG_ENGLISH 9\n" @@ -379,17 +406,15 @@ std::unique_ptr createManifestRes() { Out.close(); // Create output resource file. - SmallString<128> ResPath; - if (auto EC = sys::fs::createTemporaryFile("tmp", "res", ResPath)) - fatal(EC, "cannot create a temporary file"); + TemporaryFile ResFile("output-resource", "res"); Executor E("rc.exe"); E.add("/fo"); - E.add(ResPath.str()); + E.add(ResFile.Path); E.add("/nologo"); - E.add(RCPath.str()); + E.add(RCFile.Path); E.run(); - return check(MemoryBuffer::getFile(ResPath), "could not open " + ResPath); + return ResFile.getMemoryBuffer(); } void createSideBySideManifest() { @@ -397,7 +422,7 @@ void createSideBySideManifest() { if (Path == "") Path = Config->OutputFile + ".manifest"; std::error_code EC; - llvm::raw_fd_ostream Out(Path, EC, llvm::sys::fs::F_Text); + raw_fd_ostream Out(Path, EC, sys::fs::F_Text); if (EC) fatal(EC, "failed to create manifest"); Out << createManifestXml(); @@ -485,12 +510,14 @@ void fixupExports() { } for (Export &E : Config->Exports) { + SymbolBody *Sym = E.Sym; if (!E.ForwardTo.empty()) { E.SymbolName = E.Name; - } else if (Undefined *U = cast_or_null(E.Sym->WeakAlias)) { - E.SymbolName = U->getName(); } else { - E.SymbolName = E.Sym->getName(); + if (auto *U = dyn_cast(Sym)) + if (U->WeakAlias) + Sym = U->WeakAlias; + E.SymbolName = Sym->getName(); } } @@ -515,7 +542,7 @@ void fixupExports() { Export *Existing = Pair.first->second; if (E == *Existing || E.Name != Existing->Name) continue; - llvm::errs() << "warning: duplicate /export option: " << E.Name << "\n"; + errs() << "warning: duplicate /export option: " << E.Name << "\n"; } Config->Exports = std::move(V); @@ -555,20 +582,39 @@ void checkFailIfMismatch(StringRef Arg) { std::unique_ptr convertResToCOFF(const std::vector &MBs) { // Create an output file path. - SmallString<128> Path; - if (auto EC = llvm::sys::fs::createTemporaryFile("resource", "obj", Path)) - fatal(EC, "could not create temporary file"); + TemporaryFile File("resource-file", "obj"); // Execute cvtres.exe. Executor E("cvtres.exe"); E.add("/machine:" + machineToStr(Config->Machine)); E.add("/readonly"); E.add("/nologo"); - E.add("/out:" + Path); - for (MemoryBufferRef MB : MBs) - E.add(MB.getBufferIdentifier()); + E.add("/out:" + Twine(File.Path)); + + // We must create new files because the memory buffers we have may have no + // underlying file still existing on the disk. + // It happens if it was created from a TemporaryFile, which usually delete + // the file just after creating the MemoryBuffer. + std::vector ResFiles; + ResFiles.reserve(MBs.size()); + for (MemoryBufferRef MB : MBs) { + // We store the temporary file in a vector to avoid deletion + // before running cvtres + ResFiles.emplace_back("resource-file", "res"); + TemporaryFile& ResFile = ResFiles.back(); + // Write the content of the resource in a temporary file + std::error_code EC; + raw_fd_ostream OS(ResFile.Path, EC, sys::fs::F_None); + if (EC) + fatal(EC, "failed to open " + ResFile.Path); + OS << MB.getBuffer(); + OS.close(); + + E.add(ResFile.Path); + } + E.run(); - return check(MemoryBuffer::getFile(Path), "could not open " + Path); + return File.getMemoryBuffer(); } // Create OptTable @@ -595,7 +641,7 @@ public: }; // Parses a given list of options. -llvm::opt::InputArgList ArgParser::parse(ArrayRef ArgsArr) { +opt::InputArgList ArgParser::parse(ArrayRef ArgsArr) { // First, replace respnose files (@-style options). std::vector Argv = replaceResponseFiles(ArgsArr); @@ -603,28 +649,28 @@ llvm::opt::InputArgList ArgParser::parse(ArrayRef ArgsArr) { COFFOptTable Table; unsigned MissingIndex; unsigned MissingCount; - llvm::opt::InputArgList Args = - Table.ParseArgs(Argv, MissingIndex, MissingCount); + opt::InputArgList Args = Table.ParseArgs(Argv, MissingIndex, MissingCount); // Print the real command line if response files are expanded. if (Args.hasArg(OPT_verbose) && ArgsArr.size() != Argv.size()) { - llvm::outs() << "Command line:"; + outs() << "Command line:"; for (const char *S : Argv) - llvm::outs() << " " << S; - llvm::outs() << "\n"; + outs() << " " << S; + outs() << "\n"; } if (MissingCount) - fatal("missing arg value for \"" + Twine(Args.getArgString(MissingIndex)) + - "\", expected " + Twine(MissingCount) + - (MissingCount == 1 ? " argument." : " arguments.")); + fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument"); for (auto *Arg : Args.filtered(OPT_UNKNOWN)) - llvm::errs() << "ignoring unknown argument: " << Arg->getSpelling() << "\n"; + errs() << "ignoring unknown argument: " << Arg->getSpelling() << "\n"; return Args; } -llvm::opt::InputArgList ArgParser::parseLINK(ArrayRef Args) { - // Concatenate LINK env and given arguments and parse them. +// link.exe has an interesting feature. If LINK environment exists, +// its contents are handled as a command line string. So you can pass +// extra arguments using the environment variable. +opt::InputArgList ArgParser::parseLINK(ArrayRef Args) { + // Concatenate LINK env and command line arguments, and then parse them. Optional Env = Process::GetEnv("LINK"); if (!Env) return parse(Args); @@ -635,8 +681,7 @@ llvm::opt::InputArgList ArgParser::parseLINK(ArrayRef Args) { std::vector ArgParser::tokenize(StringRef S) { SmallVector Tokens; - StringSaver Saver(AllocAux); - llvm::cl::TokenizeWindowsCommandLine(S, Saver, Tokens); + cl::TokenizeWindowsCommandLine(S, Saver, Tokens); return std::vector(Tokens.begin(), Tokens.end()); } @@ -645,14 +690,13 @@ std::vector ArgParser::tokenize(StringRef S) { std::vector ArgParser::replaceResponseFiles(std::vector Argv) { SmallVector Tokens(Argv.data(), Argv.data() + Argv.size()); - StringSaver Saver(AllocAux); ExpandResponseFiles(Saver, TokenizeWindowsCommandLine, Tokens); return std::vector(Tokens.begin(), Tokens.end()); } void printHelp(const char *Argv0) { COFFOptTable Table; - Table.PrintHelp(llvm::outs(), Argv0, "LLVM Linker", false); + Table.PrintHelp(outs(), Argv0, "LLVM Linker", false); } } // namespace coff diff --git a/COFF/Error.cpp b/COFF/Error.cpp index 602a8544ce2b3..b2bd557413df0 100644 --- a/COFF/Error.cpp +++ b/COFF/Error.cpp @@ -11,14 +11,31 @@ #include "llvm/ADT/Twine.h" #include "llvm/Support/Error.h" +#include "llvm/Support/Process.h" #include "llvm/Support/raw_ostream.h" +#if !defined(_MSC_VER) && !defined(__MINGW32__) +#include +#endif + +using namespace llvm; + namespace lld { namespace coff { void fatal(const Twine &Msg) { - llvm::errs() << Msg << "\n"; - exit(1); + if (sys::Process::StandardErrHasColors()) { + errs().changeColor(raw_ostream::RED, /*bold=*/true); + errs() << "error: "; + errs().resetColor(); + } else { + errs() << "error: "; + } + errs() << Msg << "\n"; + + outs().flush(); + errs().flush(); + _exit(1); } void fatal(std::error_code EC, const Twine &Msg) { diff --git a/COFF/Error.h b/COFF/Error.h index c9f64c662580a..47549327db2be 100644 --- a/COFF/Error.h +++ b/COFF/Error.h @@ -32,6 +32,23 @@ template T check(Expected E, const Twine &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 diff --git a/COFF/ICF.cpp b/COFF/ICF.cpp index a2c5a90334d03..196fbe2610ea7 100644 --- a/COFF/ICF.cpp +++ b/COFF/ICF.cpp @@ -7,43 +7,19 @@ // //===----------------------------------------------------------------------===// // -// Identical COMDAT Folding is a feature to merge COMDAT sections not by -// name (which is regular COMDAT handling) but by contents. If two COMDAT -// sections have the same data, relocations, attributes, etc., then the two -// are considered identical and merged by the linker. This optimization -// makes outputs smaller. +// ICF is short for Identical Code Folding. That is a size optimization to +// identify and merge two or more read-only sections (typically functions) +// that happened to have the same contents. It usually reduces output size +// by a few percent. // -// ICF is theoretically a problem of reducing graphs by merging as many -// identical subgraphs as possible, if we consider sections as vertices and -// relocations as edges. This may be a bit more complicated problem than you -// might think. The order of processing sections matters since merging two -// sections can make other sections, whose relocations now point to the same -// section, mergeable. Graphs may contain cycles, which is common in COFF. -// We need a sophisticated algorithm to do this properly and efficiently. +// On Windows, ICF is enabled by default. // -// What we do in this file is this. We split sections into groups. Sections -// in the same group are considered identical. -// -// First, all sections are grouped by their "constant" values. Constant -// values are values that are never changed by ICF, such as section contents, -// section name, number of relocations, type and offset of each relocation, -// etc. Because we do not care about some relocation targets in this step, -// two sections in the same group may not be identical, but at least two -// sections in different groups can never be identical. -// -// Then, we try to split each group by relocation targets. Relocations are -// considered identical if and only if the relocation targets are in the -// same group. Splitting a group may make more groups to be splittable, -// because two relocations that were previously considered identical might -// now point to different groups. We repeat this step until the convergence -// is obtained. -// -// This algorithm is so-called "optimistic" algorithm described in -// http://research.google.com/pubs/pub36912.html. +// See ELF/ICF.cpp for the details about the algortihm. // //===----------------------------------------------------------------------===// #include "Chunks.h" +#include "Error.h" #include "Symbols.h" #include "lld/Core/Parallel.h" #include "llvm/ADT/Hashing.h" @@ -58,29 +34,34 @@ using namespace llvm; namespace lld { namespace coff { -typedef std::vector::iterator ChunkIterator; -typedef bool (*Comparator)(const SectionChunk *, const SectionChunk *); - class ICF { public: void run(const std::vector &V); private: - static uint64_t getHash(SectionChunk *C); - static bool equalsConstant(const SectionChunk *A, const SectionChunk *B); - static bool equalsVariable(const SectionChunk *A, const SectionChunk *B); - bool forEachGroup(std::vector &Chunks, Comparator Eq); - bool segregate(ChunkIterator Begin, ChunkIterator End, Comparator Eq); + void segregate(size_t Begin, size_t End, bool Constant); - std::atomic NextID = { 1 }; -}; + bool equalsConstant(const SectionChunk *A, const SectionChunk *B); + bool equalsVariable(const SectionChunk *A, const SectionChunk *B); -// Entry point to ICF. -void doICF(const std::vector &Chunks) { - ICF().run(Chunks); -} + uint32_t getHash(SectionChunk *C); + bool isEligible(SectionChunk *C); + + size_t findBoundary(size_t Begin, size_t End); + + void forEachColorRange(size_t Begin, size_t End, + std::function Fn); + + void forEachColor(std::function Fn); + + std::vector Chunks; + int Cnt = 0; + std::atomic NextId = {1}; + std::atomic Repeat = {false}; +}; -uint64_t ICF::getHash(SectionChunk *C) { +// Returns a hash value for S. +uint32_t ICF::getHash(SectionChunk *C) { return hash_combine(C->getPermissions(), hash_value(C->SectionName), C->NumRelocs, @@ -89,16 +70,44 @@ uint64_t ICF::getHash(SectionChunk *C) { C->Checksum); } -bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) { - if (A->AssocChildren.size() != B->AssocChildren.size() || - A->NumRelocs != B->NumRelocs) { - return false; +// Returns true if section S is subject of ICF. +bool ICF::isEligible(SectionChunk *C) { + bool Global = C->Sym && C->Sym->isExternal(); + bool Writable = C->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_WRITE; + return C->isCOMDAT() && C->isLive() && Global && !Writable; +} + +// Split a range into smaller ranges by recoloring sections +void ICF::segregate(size_t Begin, size_t End, bool Constant) { + while (Begin < End) { + // Divide [Begin, End) into two. Let Mid be the start index of the + // second group. + auto Bound = std::stable_partition( + Chunks.begin() + Begin + 1, Chunks.begin() + End, [&](SectionChunk *S) { + if (Constant) + return equalsConstant(Chunks[Begin], S); + return equalsVariable(Chunks[Begin], S); + }); + size_t Mid = Bound - Chunks.begin(); + + // Split [Begin, End) into [Begin, Mid) and [Mid, End). + uint32_t Id = NextId++; + for (size_t I = Begin; I < Mid; ++I) + Chunks[I]->Color[(Cnt + 1) % 2] = Id; + + // If we created a group, we need to iterate the main loop again. + if (Mid != End) + Repeat = true; + + Begin = Mid; } +} - // Compare associative sections. - for (size_t I = 0, E = A->AssocChildren.size(); I != E; ++I) - if (A->AssocChildren[I]->GroupID != B->AssocChildren[I]->GroupID) - return false; +// Compare "non-moving" part of two sections, namely everything +// except relocation targets. +bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) { + if (A->NumRelocs != B->NumRelocs) + return false; // Compare relocations. auto Eq = [&](const coff_relocation &R1, const coff_relocation &R2) { @@ -106,14 +115,14 @@ bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) { R1.VirtualAddress != R2.VirtualAddress) { return false; } - SymbolBody *B1 = A->File->getSymbolBody(R1.SymbolTableIndex)->repl(); - SymbolBody *B2 = B->File->getSymbolBody(R2.SymbolTableIndex)->repl(); + SymbolBody *B1 = A->File->getSymbolBody(R1.SymbolTableIndex); + SymbolBody *B2 = B->File->getSymbolBody(R2.SymbolTableIndex); if (B1 == B2) return true; if (auto *D1 = dyn_cast(B1)) if (auto *D2 = dyn_cast(B2)) return D1->getValue() == D2->getValue() && - D1->getChunk()->GroupID == D2->getChunk()->GroupID; + D1->getChunk()->Color[Cnt % 2] == D2->getChunk()->Color[Cnt % 2]; return false; }; if (!std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq)) @@ -128,54 +137,57 @@ bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) { A->getContents() == B->getContents(); } +// Compare "moving" part of two sections, namely relocation targets. bool ICF::equalsVariable(const SectionChunk *A, const SectionChunk *B) { - // Compare associative sections. - for (size_t I = 0, E = A->AssocChildren.size(); I != E; ++I) - if (A->AssocChildren[I]->GroupID != B->AssocChildren[I]->GroupID) - return false; - // Compare relocations. auto Eq = [&](const coff_relocation &R1, const coff_relocation &R2) { - SymbolBody *B1 = A->File->getSymbolBody(R1.SymbolTableIndex)->repl(); - SymbolBody *B2 = B->File->getSymbolBody(R2.SymbolTableIndex)->repl(); + SymbolBody *B1 = A->File->getSymbolBody(R1.SymbolTableIndex); + SymbolBody *B2 = B->File->getSymbolBody(R2.SymbolTableIndex); if (B1 == B2) return true; if (auto *D1 = dyn_cast(B1)) if (auto *D2 = dyn_cast(B2)) - return D1->getChunk()->GroupID == D2->getChunk()->GroupID; + return D1->getChunk()->Color[Cnt % 2] == D2->getChunk()->Color[Cnt % 2]; return false; }; return std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq); } -bool ICF::segregate(ChunkIterator Begin, ChunkIterator End, Comparator Eq) { - bool R = false; - for (auto It = Begin;;) { - SectionChunk *Head = *It; - auto Bound = std::partition(It + 1, End, [&](SectionChunk *SC) { - return Eq(Head, SC); - }); - if (Bound == End) - return R; - uint64_t ID = NextID++; - std::for_each(It, Bound, [&](SectionChunk *SC) { SC->GroupID = ID; }); - It = Bound; - R = true; +size_t ICF::findBoundary(size_t Begin, size_t End) { + for (size_t I = Begin + 1; I < End; ++I) + if (Chunks[Begin]->Color[Cnt % 2] != Chunks[I]->Color[Cnt % 2]) + return I; + return End; +} + +void ICF::forEachColorRange(size_t Begin, size_t End, + std::function Fn) { + if (Begin > 0) + Begin = findBoundary(Begin - 1, End); + + while (Begin < End) { + size_t Mid = findBoundary(Begin, Chunks.size()); + Fn(Begin, Mid); + Begin = Mid; } } -bool ICF::forEachGroup(std::vector &Chunks, Comparator Eq) { - bool R = false; - for (auto It = Chunks.begin(), End = Chunks.end(); It != End;) { - SectionChunk *Head = *It; - auto Bound = std::find_if(It + 1, End, [&](SectionChunk *SC) { - return SC->GroupID != Head->GroupID; - }); - if (segregate(It, Bound, Eq)) - R = true; - It = Bound; +// Call Fn on each color group. +void ICF::forEachColor(std::function Fn) { + // If the number of sections are too small to use threading, + // call Fn sequentially. + if (Chunks.size() < 1024) { + forEachColorRange(0, Chunks.size(), Fn); + return; } - return R; + + // Split sections into 256 shards and call Fn in parallel. + size_t NumShards = 256; + size_t Step = Chunks.size() / NumShards; + parallel_for(size_t(0), NumShards, [&](size_t I) { + forEachColorRange(I * Step, (I + 1) * Step, Fn); + }); + forEachColorRange(Step * NumShards, Chunks.size(), Fn); } // Merge identical COMDAT sections. @@ -183,62 +195,62 @@ bool ICF::forEachGroup(std::vector &Chunks, Comparator Eq) { // contents and relocations are all the same. void ICF::run(const std::vector &Vec) { // Collect only mergeable sections and group by hash value. - parallel_for_each(Vec.begin(), Vec.end(), [&](Chunk *C) { - if (auto *SC = dyn_cast(C)) { - bool Global = SC->Sym && SC->Sym->isExternal(); - bool Writable = SC->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_WRITE; - if (SC->isCOMDAT() && SC->isLive() && Global && !Writable) - SC->GroupID = getHash(SC) | (uint64_t(1) << 63); - } - }); - std::vector Chunks; for (Chunk *C : Vec) { - if (auto *SC = dyn_cast(C)) { - if (SC->GroupID) { - Chunks.push_back(SC); - } else { - SC->GroupID = NextID++; - } + auto *SC = dyn_cast(C); + if (!SC) + continue; + + if (isEligible(SC)) { + // Set MSB to 1 to avoid collisions with non-hash colors. + SC->Color[0] = getHash(SC) | (1 << 31); + Chunks.push_back(SC); + } else { + SC->Color[0] = NextId++; } } + if (Chunks.empty()) + return; + // From now on, sections in Chunks are ordered so that sections in // the same group are consecutive in the vector. - std::sort(Chunks.begin(), Chunks.end(), - [](SectionChunk *A, SectionChunk *B) { - return A->GroupID < B->GroupID; - }); - - // Split groups until we get a convergence. - int Cnt = 1; - forEachGroup(Chunks, equalsConstant); - - for (;;) { - if (!forEachGroup(Chunks, equalsVariable)) - break; + std::stable_sort(Chunks.begin(), Chunks.end(), + [](SectionChunk *A, SectionChunk *B) { + return A->Color[0] < B->Color[0]; + }); + + // Compare static contents and assign unique IDs for each static content. + forEachColor([&](size_t Begin, size_t End) { segregate(Begin, End, true); }); + ++Cnt; + + // Split groups by comparing relocations until convergence is obtained. + do { + Repeat = false; + forEachColor( + [&](size_t Begin, size_t End) { segregate(Begin, End, false); }); ++Cnt; - } + } while (Repeat); + if (Config->Verbose) - llvm::outs() << "\nICF needed " << Cnt << " iterations.\n"; - - // Merge sections in the same group. - for (auto It = Chunks.begin(), End = Chunks.end(); It != End;) { - SectionChunk *Head = *It++; - auto Bound = std::find_if(It, End, [&](SectionChunk *SC) { - return Head->GroupID != SC->GroupID; - }); - if (It == Bound) - continue; + outs() << "\nICF needed " << Cnt << " iterations\n"; + + // Merge sections in the same colors. + forEachColor([&](size_t Begin, size_t End) { + if (End - Begin == 1) + return; + if (Config->Verbose) - llvm::outs() << "Selected " << Head->getDebugName() << "\n"; - while (It != Bound) { - SectionChunk *SC = *It++; + outs() << "Selected " << Chunks[Begin]->getDebugName() << "\n"; + for (size_t I = Begin + 1; I < End; ++I) { if (Config->Verbose) - llvm::outs() << " Removed " << SC->getDebugName() << "\n"; - Head->replace(SC); + outs() << " Removed " << Chunks[I]->getDebugName() << "\n"; + Chunks[Begin]->replace(Chunks[I]); } - } + }); } +// Entry point to ICF. +void doICF(const std::vector &Chunks) { ICF().run(Chunks); } + } // namespace coff } // namespace lld diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp index ff26826371fa7..0a97c2185f894 100644 --- a/COFF/InputFiles.cpp +++ b/COFF/InputFiles.cpp @@ -7,11 +7,15 @@ // //===----------------------------------------------------------------------===// +#include "InputFiles.h" #include "Chunks.h" #include "Config.h" +#include "Driver.h" #include "Error.h" -#include "InputFiles.h" +#include "Memory.h" +#include "SymbolTable.h" #include "Symbols.h" +#include "llvm-c/lto.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Triple.h" #include "llvm/ADT/Twine.h" @@ -26,88 +30,58 @@ #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" #include "llvm/Target/TargetOptions.h" -#include "llvm-c/lto.h" #include #include #include +using namespace llvm; using namespace llvm::COFF; using namespace llvm::object; using namespace llvm::support::endian; using llvm::Triple; using llvm::support::ulittle32_t; +using llvm::sys::fs::file_magic; +using llvm::sys::fs::identify_magic; namespace lld { namespace coff { -int InputFile::NextIndex = 0; -llvm::LLVMContext BitcodeFile::Context; - -// Returns the last element of a path, which is supposed to be a filename. -static StringRef getBasename(StringRef Path) { - size_t Pos = Path.find_last_of("\\/"); - if (Pos == StringRef::npos) - return Path; - return Path.substr(Pos + 1); -} +LLVMContext BitcodeFile::Context; -// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)". -std::string InputFile::getShortName() { - if (ParentName == "") - return getName().lower(); - std::string Res = (getBasename(ParentName) + "(" + - getBasename(getName()) + ")").str(); - return StringRef(Res).lower(); -} +ArchiveFile::ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {} void ArchiveFile::parse() { // Parse a MemoryBufferRef as an archive file. - File = check(Archive::create(MB), "failed to parse static library"); - - // Allocate a buffer for Lazy objects. - size_t NumSyms = File->getNumberOfSymbols(); - LazySymbols.reserve(NumSyms); + File = check(Archive::create(MB), toString(this)); // Read the symbol table to construct Lazy objects. for (const Archive::Symbol &Sym : File->symbols()) - LazySymbols.emplace_back(this, Sym); - - // Seen is a map from member files to boolean values. Initially - // all members are mapped to false, which indicates all these files - // are not read yet. - Error Err; - for (auto &Child : File->children(Err)) - Seen[Child.getChildOffset()].clear(); - if (Err) - fatal(Err, "failed to parse static library"); + Symtab->addLazy(this, Sym); } // Returns a buffer pointing to a member file containing a given symbol. -// This function is thread-safe. -MemoryBufferRef ArchiveFile::getMember(const Archive::Symbol *Sym) { +void ArchiveFile::addMember(const Archive::Symbol *Sym) { const Archive::Child &C = check(Sym->getMember(), "could not get the member for symbol " + Sym->getName()); // Return an empty buffer if we have already returned the same buffer. - if (Seen[C.getChildOffset()].test_and_set()) - return MemoryBufferRef(); - return check(C.getMemoryBufferRef(), - "could not get the buffer for the member defining symbol " + - Sym->getName()); + if (!Seen.insert(C.getChildOffset()).second) + return; + + Driver->enqueueArchiveMember(C, Sym->getName(), getName()); } void ObjectFile::parse() { // Parse a memory buffer as a COFF file. - std::unique_ptr Bin = - check(createBinary(MB), "failed to parse object file"); + std::unique_ptr Bin = check(createBinary(MB), toString(this)); if (auto *Obj = dyn_cast(Bin.get())) { Bin.release(); COFFObj.reset(Obj); } else { - fatal(getName() + " is not a COFF file"); + fatal(toString(this) + " is not a COFF file"); } // Read section and symbol tables. @@ -137,13 +111,28 @@ void ObjectFile::initializeChunks() { Directives = std::string((const char *)Data.data(), Data.size()); continue; } - // Skip non-DWARF debug info. MSVC linker converts the sections into - // a PDB file, but we don't support that. - if (Name == ".debug" || Name.startswith(".debug$")) - continue; - // We want to preserve DWARF debug sections only when /debug is on. + + // Object files may have DWARF debug info or MS CodeView debug info + // (or both). + // + // DWARF sections don't need any special handling from the perspective + // of the linker; they are just a data section containing relocations. + // We can just link them to complete debug info. + // + // CodeView needs a linker support. We need to interpret and debug + // info, and then write it to a separate .pdb file. + + // Ignore debug info unless /debug is given. if (!Config->Debug && Name.startswith(".debug")) continue; + + // CodeView sections are stored to a different vector because they are + // not linked in the regular manner. + if (Name == ".debug" || Name.startswith(".debug$")) { + DebugChunks.push_back(new (Alloc) SectionChunk(this, Sec)); + continue; + } + if (Sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE) continue; auto *C = new (Alloc) SectionChunk(this, Sec); @@ -156,12 +145,14 @@ void ObjectFile::initializeSymbols() { uint32_t NumSymbols = COFFObj->getNumberOfSymbols(); SymbolBodies.reserve(NumSymbols); SparseSymbolBodies.resize(NumSymbols); - llvm::SmallVector, 8> WeakAliases; + SmallVector, 8> WeakAliases; int32_t LastSectionNumber = 0; for (uint32_t I = 0; I < NumSymbols; ++I) { // Get a COFFSymbolRef object. - COFFSymbolRef Sym = - check(COFFObj->getSymbol(I), "broken object file: " + getName()); + ErrorOr SymOrErr = COFFObj->getSymbol(I); + if (!SymOrErr) + fatal(SymOrErr.getError(), "broken object file: " + toString(this)); + COFFSymbolRef Sym = *SymOrErr; const void *AuxP = nullptr; if (Sym.getNumberOfAuxSymbols()) @@ -175,7 +166,7 @@ void ObjectFile::initializeSymbols() { Body = createUndefined(Sym); uint32_t TagIndex = static_cast(AuxP)->TagIndex; - WeakAliases.emplace_back((Undefined *)Body, TagIndex); + WeakAliases.emplace_back(Body, TagIndex); } else { Body = createDefined(Sym, AuxP, IsFirst); } @@ -186,23 +177,30 @@ void ObjectFile::initializeSymbols() { I += Sym.getNumberOfAuxSymbols(); LastSectionNumber = Sym.getSectionNumber(); } - for (auto WeakAlias : WeakAliases) - WeakAlias.first->WeakAlias = SparseSymbolBodies[WeakAlias.second]; + for (auto WeakAlias : WeakAliases) { + auto *U = dyn_cast(WeakAlias.first); + if (!U) + continue; + // Report an error if two undefined symbols have different weak aliases. + if (U->WeakAlias && U->WeakAlias != SparseSymbolBodies[WeakAlias.second]) + Symtab->reportDuplicate(U->symbol(), this); + U->WeakAlias = SparseSymbolBodies[WeakAlias.second]; + } } -Undefined *ObjectFile::createUndefined(COFFSymbolRef Sym) { +SymbolBody *ObjectFile::createUndefined(COFFSymbolRef Sym) { StringRef Name; COFFObj->getSymbolName(Sym, Name); - return new (Alloc) Undefined(Name); + return Symtab->addUndefined(Name, this, Sym.isWeakExternal())->body(); } -Defined *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP, - bool IsFirst) { +SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP, + bool IsFirst) { StringRef Name; if (Sym.isCommon()) { auto *C = new (Alloc) CommonChunk(Sym); Chunks.push_back(C); - return new (Alloc) DefinedCommon(this, Sym, C); + return Symtab->addCommon(this, Sym, C)->body(); } if (Sym.isAbsolute()) { COFFObj->getSymbolName(Sym, Name); @@ -215,7 +213,10 @@ Defined *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP, SEHCompat = true; return nullptr; } - return new (Alloc) DefinedAbsolute(Name, Sym); + if (Sym.isExternal()) + return Symtab->addAbsolute(Name, Sym)->body(); + else + return new (Alloc) DefinedAbsolute(Name, Sym); } int32_t SectionNumber = Sym.getSectionNumber(); if (SectionNumber == llvm::COFF::IMAGE_SYM_DEBUG) @@ -223,12 +224,12 @@ Defined *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP, // Reserved sections numbers don't have contents. if (llvm::COFF::isReservedSectionNumber(SectionNumber)) - fatal("broken object file: " + getName()); + fatal("broken object file: " + toString(this)); // This symbol references a section which is not present in the section // header. if ((uint32_t)SectionNumber >= SparseChunks.size()) - fatal("broken object file: " + getName()); + fatal("broken object file: " + toString(this)); // Nothing else to do without a section chunk. auto *SC = cast_or_null(SparseChunks[SectionNumber]); @@ -245,7 +246,11 @@ Defined *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP, SC->Checksum = Aux->CheckSum; } - auto *B = new (Alloc) DefinedRegular(this, Sym, SC); + DefinedRegular *B; + if (Sym.isExternal()) + B = cast(Symtab->addRegular(this, Sym, SC)->body()); + else + B = new (Alloc) DefinedRegular(this, Sym, SC); if (SC->isCOMDAT() && Sym.getValue() == 0 && !AuxP) SC->setSymbol(B); @@ -307,28 +312,29 @@ void ImportFile::parse() { ExtName = ExtName.substr(0, ExtName.find('@')); break; } - ImpSym = new (Alloc) DefinedImportData(DLLName, ImpName, ExtName, Hdr); - SymbolBodies.push_back(ImpSym); + + this->Hdr = Hdr; + ExternalName = ExtName; + + ImpSym = cast( + Symtab->addImportData(ImpName, this)->body()); // If type is function, we need to create a thunk which jump to an // address pointed by the __imp_ symbol. (This allows you to call // DLL functions just like regular non-DLL functions.) if (Hdr->getType() != llvm::COFF::IMPORT_CODE) return; - ThunkSym = new (Alloc) DefinedImportThunk(Name, ImpSym, Hdr->Machine); - SymbolBodies.push_back(ThunkSym); + ThunkSym = cast( + Symtab->addImportThunk(Name, ImpSym, Hdr->Machine)->body()); } void BitcodeFile::parse() { - // Usually parse() is thread-safe, but bitcode file is an exception. - std::lock_guard Lock(Mu); - Context.enableDebugTypeODRUniquing(); ErrorOr> ModOrErr = LTOModule::createFromBuffer( Context, MB.getBufferStart(), MB.getBufferSize(), llvm::TargetOptions()); M = check(std::move(ModOrErr), "could not create LTO module"); - llvm::StringSaver Saver(Alloc); + StringSaver Saver(Alloc); for (unsigned I = 0, E = M->getSymbolCount(); I != E; ++I) { lto_symbol_attributes Attrs = M->getSymbolAttributes(I); if ((Attrs & LTO_SYMBOL_SCOPE_MASK) == LTO_SYMBOL_SCOPE_INTERNAL) @@ -337,15 +343,15 @@ void BitcodeFile::parse() { StringRef SymName = Saver.save(M->getSymbolName(I)); int SymbolDef = Attrs & LTO_SYMBOL_DEFINITION_MASK; if (SymbolDef == LTO_SYMBOL_DEFINITION_UNDEFINED) { - SymbolBodies.push_back(new (Alloc) Undefined(SymName)); + SymbolBodies.push_back(Symtab->addUndefined(SymName, this, false)->body()); } else { bool Replaceable = (SymbolDef == LTO_SYMBOL_DEFINITION_TENTATIVE || // common (Attrs & LTO_SYMBOL_COMDAT) || // comdat (SymbolDef == LTO_SYMBOL_DEFINITION_WEAK && // weak external (Attrs & LTO_SYMBOL_ALIAS))); - SymbolBodies.push_back(new (Alloc) DefinedBitcode(this, SymName, - Replaceable)); + SymbolBodies.push_back( + Symtab->addBitcode(this, SymName, Replaceable)->body()); } } @@ -367,7 +373,26 @@ MachineTypes BitcodeFile::getMachineType() { } } -std::mutex BitcodeFile::Mu; +// Returns the last element of a path, which is supposed to be a filename. +static StringRef getBasename(StringRef Path) { + size_t Pos = Path.find_last_of("\\/"); + if (Pos == StringRef::npos) + return Path; + return Path.substr(Pos + 1); +} + +// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)". +std::string toString(InputFile *File) { + if (!File) + return "(internal)"; + if (File->ParentName.empty()) + return File->getName().lower(); + + std::string Res = + (getBasename(File->ParentName) + "(" + getBasename(File->getName()) + ")") + .str(); + return StringRef(Res).lower(); +} } // namespace coff } // namespace lld diff --git a/COFF/InputFiles.h b/COFF/InputFiles.h index 0ec01b5075f93..498a1743e9853 100644 --- a/COFF/InputFiles.h +++ b/COFF/InputFiles.h @@ -12,13 +12,13 @@ #include "lld/Core/LLVM.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/IR/LLVMContext.h" #include "llvm/LTO/legacy/LTOModule.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFF.h" #include "llvm/Support/StringSaver.h" #include -#include #include #include @@ -31,6 +31,7 @@ using llvm::COFF::MachineTypes; using llvm::object::Archive; using llvm::object::COFFObjectFile; using llvm::object::COFFSymbolRef; +using llvm::object::coff_import_header; using llvm::object::coff_section; class Chunk; @@ -38,6 +39,8 @@ class Defined; class DefinedImportData; class DefinedImportThunk; class Lazy; +class SectionChunk; +struct Symbol; class SymbolBody; class Undefined; @@ -51,67 +54,44 @@ public: // Returns the filename. StringRef getName() { return MB.getBufferIdentifier(); } - // Returns symbols defined by this file. - virtual std::vector &getSymbols() = 0; - // Reads a file (the constructor doesn't do that). virtual void parse() = 0; // Returns the CPU type this file was compiled to. virtual MachineTypes getMachineType() { return IMAGE_FILE_MACHINE_UNKNOWN; } - // Returns a short, human-friendly filename. If this is a member of - // an archive file, a returned value includes parent's filename. - // Used for logging or debugging. - std::string getShortName(); - - // Sets a parent filename if this file is created from an archive. - void setParentName(StringRef N) { ParentName = N; } + // An archive file name if this file is created from an archive. + StringRef ParentName; // Returns .drectve section contents if exist. StringRef getDirectives() { return StringRef(Directives).trim(); } - // Each file has a unique index. The index number is used to - // resolve ties in symbol resolution. - int Index; - static int NextIndex; - protected: - InputFile(Kind K, MemoryBufferRef M) - : Index(NextIndex++), MB(M), FileKind(K) {} + InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {} MemoryBufferRef MB; std::string Directives; private: const Kind FileKind; - StringRef ParentName; }; // .lib or .a file. class ArchiveFile : public InputFile { public: - explicit ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {} + explicit ArchiveFile(MemoryBufferRef M); static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; } void parse() override; - // Returns a memory buffer for a given symbol. An empty memory buffer - // is returned if we have already returned the same memory buffer. - // (So that we don't instantiate same members more than once.) - MemoryBufferRef getMember(const Archive::Symbol *Sym); - - llvm::MutableArrayRef getLazySymbols() { return LazySymbols; } - - // All symbols returned by ArchiveFiles are of Lazy type. - std::vector &getSymbols() override { - llvm_unreachable("internal fatal"); - } + // Enqueues an archive member load for the given symbol. If we've already + // enqueued a load for the same archive member, this function does nothing, + // which ensures that we don't load the same member more than once. + void addMember(const Archive::Symbol *Sym); private: std::unique_ptr File; std::string Filename; - std::vector LazySymbols; - std::map Seen; + llvm::DenseSet Seen; }; // .obj or .o file. This may be a member of an archive file. @@ -122,7 +102,8 @@ public: void parse() override; MachineTypes getMachineType() override; std::vector &getChunks() { return Chunks; } - std::vector &getSymbols() override { return SymbolBodies; } + std::vector &getDebugChunks() { return DebugChunks; } + std::vector &getSymbols() { return SymbolBodies; } // Returns a SymbolBody object for the SymbolIndex'th symbol in the // underlying object file. @@ -146,8 +127,8 @@ private: void initializeSymbols(); void initializeSEH(); - Defined *createDefined(COFFSymbolRef Sym, const void *Aux, bool IsFirst); - Undefined *createUndefined(COFFSymbolRef Sym); + SymbolBody *createDefined(COFFSymbolRef Sym, const void *Aux, bool IsFirst); + SymbolBody *createUndefined(COFFSymbolRef Sym); std::unique_ptr COFFObj; llvm::BumpPtrAllocator Alloc; @@ -157,6 +138,9 @@ private: // chunks and non-section chunks for common symbols. std::vector Chunks; + // CodeView debug info sections. + std::vector DebugChunks; + // This vector contains the same chunks as Chunks, but they are // indexed such that you can get a SectionChunk by section index. // Nonexistent section indices are filled with null pointers. @@ -182,7 +166,6 @@ public: explicit ImportFile(MemoryBufferRef M) : InputFile(ImportKind, M), StringAlloc(StringAllocAux) {} static bool classof(const InputFile *F) { return F->kind() == ImportKind; } - std::vector &getSymbols() override { return SymbolBodies; } DefinedImportData *ImpSym = nullptr; DefinedImportThunk *ThunkSym = nullptr; @@ -191,10 +174,14 @@ public: private: void parse() override; - std::vector SymbolBodies; llvm::BumpPtrAllocator Alloc; llvm::BumpPtrAllocator StringAllocAux; llvm::StringSaver StringAlloc; + +public: + StringRef ExternalName; + const coff_import_header *Hdr; + Chunk *Location = nullptr; }; // Used for LTO. @@ -202,7 +189,7 @@ class BitcodeFile : public InputFile { public: explicit BitcodeFile(MemoryBufferRef M) : InputFile(BitcodeKind, M) {} static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; } - std::vector &getSymbols() override { return SymbolBodies; } + std::vector &getSymbols() { return SymbolBodies; } MachineTypes getMachineType() override; std::unique_ptr takeModule() { return std::move(M); } @@ -214,9 +201,10 @@ private: std::vector SymbolBodies; llvm::BumpPtrAllocator Alloc; std::unique_ptr M; - static std::mutex Mu; }; +std::string toString(InputFile *File); + } // namespace coff } // namespace lld diff --git a/COFF/Librarian.cpp b/COFF/Librarian.cpp index 25fb4a87b3eb1..4c597fad73455 100644 --- a/COFF/Librarian.cpp +++ b/COFF/Librarian.cpp @@ -54,7 +54,7 @@ static uint16_t getImgRelRelocation() { } } -template void append(std::vector &B, const T &Data) { +template static void append(std::vector &B, const T &Data) { size_t S = B.size(); B.resize(S + sizeof(T)); memcpy(&B[S], &Data, sizeof(T)); @@ -352,15 +352,16 @@ ObjectFactory::createNullImportDescriptor(std::vector &Buffer) { NewArchiveMember ObjectFactory::createNullThunk(std::vector &Buffer) { static const uint32_t NumberOfSections = 2; static const uint32_t NumberOfSymbols = 1; + uint32_t VASize = is32bit() ? 4 : 8; // COFF Header coff_file_header Header{ u16(Config->Machine), u16(NumberOfSections), u32(0), u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + // .idata$5 - sizeof(export_address_table_entry) + + VASize + // .idata$4 - sizeof(export_address_table_entry)), + VASize), u32(NumberOfSymbols), u16(0), u16(is32bit() ? IMAGE_FILE_32BIT_MACHINE : 0), }; @@ -371,36 +372,40 @@ NewArchiveMember ObjectFactory::createNullThunk(std::vector &Buffer) { {{'.', 'i', 'd', 'a', 't', 'a', '$', '5'}, u32(0), u32(0), - u32(sizeof(export_address_table_entry)), + u32(VASize), u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)), u32(0), u32(0), u16(0), u16(0), - u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | - IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, + u32((is32bit() ? IMAGE_SCN_ALIGN_4BYTES : IMAGE_SCN_ALIGN_8BYTES) | + IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | + IMAGE_SCN_MEM_WRITE)}, {{'.', 'i', 'd', 'a', 't', 'a', '$', '4'}, u32(0), u32(0), - u32(sizeof(export_address_table_entry)), + u32(VASize), u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) + - sizeof(export_address_table_entry)), + VASize), u32(0), u32(0), u16(0), u16(0), - u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | - IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, + u32((is32bit() ? IMAGE_SCN_ALIGN_4BYTES : IMAGE_SCN_ALIGN_8BYTES) | + IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | + IMAGE_SCN_MEM_WRITE)}, }; append(Buffer, SectionTable); - // .idata$5 - static const export_address_table_entry ILT{u32(0)}; - append(Buffer, ILT); + // .idata$5, ILT + append(Buffer, u32(0)); + if (!is32bit()) + append(Buffer, u32(0)); - // .idata$4 - static const export_address_table_entry IAT{u32(0)}; - append(Buffer, IAT); + // .idata$4, IAT + append(Buffer, u32(0)); + if (!is32bit()) + append(Buffer, u32(0)); // Symbol Table coff_symbol16 SymbolTable[NumberOfSymbols] = { @@ -458,7 +463,7 @@ void lld::coff::writeImportLibrary() { std::vector Members; std::string Path = getImplibPath(); - std::string DLLName = llvm::sys::path::filename(Config->OutputFile); + std::string DLLName = sys::path::filename(Config->OutputFile); ObjectFactory OF(DLLName); std::vector ImportDescriptor; diff --git a/COFF/MarkLive.cpp b/COFF/MarkLive.cpp index 0870986ad81a6..0156d238b6726 100644 --- a/COFF/MarkLive.cpp +++ b/COFF/MarkLive.cpp @@ -38,8 +38,8 @@ void markLive(const std::vector &Chunks) { }; // Add GC root chunks. - for (Undefined *U : Config->GCRoot) - if (auto *D = dyn_cast(U->repl())) + for (SymbolBody *B : Config->GCRoot) + if (auto *D = dyn_cast(B)) Enqueue(D->getChunk()); while (!Worklist.empty()) { @@ -48,7 +48,7 @@ void markLive(const std::vector &Chunks) { // Mark all symbols listed in the relocation table for this section. for (SymbolBody *S : SC->symbols()) - if (auto *D = dyn_cast(S->repl())) + if (auto *D = dyn_cast(S)) Enqueue(D->getChunk()); // Mark associative sections if any. diff --git a/COFF/Memory.h b/COFF/Memory.h new file mode 100644 index 0000000000000..526f11344a09b --- /dev/null +++ b/COFF/Memory.h @@ -0,0 +1,52 @@ +//===- Memory.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// See ELF/Memory.h +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_COFF_MEMORY_H +#define LLD_COFF_MEMORY_H + +#include "llvm/Support/Allocator.h" +#include "llvm/Support/StringSaver.h" +#include + +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/ModuleDef.cpp b/COFF/ModuleDef.cpp index 5e393f45d1841..a273b6f535db6 100644 --- a/COFF/ModuleDef.cpp +++ b/COFF/ModuleDef.cpp @@ -18,6 +18,7 @@ #include "Config.h" #include "Error.h" +#include "Memory.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/StringSaver.h" @@ -113,7 +114,7 @@ private: class Parser { public: - explicit Parser(StringRef S, StringSaver *A) : Lex(S), Alloc(A) {} + explicit Parser(StringRef S) : Lex(S) {} void parse() { do { @@ -197,9 +198,9 @@ private: if (Config->Machine == I386) { if (!isDecorated(E.Name)) - E.Name = Alloc->save("_" + E.Name); + E.Name = Saver.save("_" + E.Name); if (!E.ExtName.empty() && !isDecorated(E.ExtName)) - E.ExtName = Alloc->save("_" + E.ExtName); + E.ExtName = Saver.save("_" + E.ExtName); } for (;;) { @@ -278,14 +279,11 @@ private: Lexer Lex; Token Tok; std::vector Stack; - StringSaver *Alloc; }; } // anonymous namespace -void parseModuleDefs(MemoryBufferRef MB, StringSaver *Alloc) { - Parser(MB.getBuffer(), Alloc).parse(); -} +void parseModuleDefs(MemoryBufferRef MB) { Parser(MB.getBuffer()).parse(); } } // namespace coff } // namespace lld diff --git a/COFF/Options.td b/COFF/Options.td index e5c9c5b4635ba..9dfbcc8e188cc 100644 --- a/COFF/Options.td +++ b/COFF/Options.td @@ -27,6 +27,7 @@ def failifmismatch : P<"failifmismatch", "">; def heap : P<"heap", "Size of the heap">; def implib : P<"implib", "Import library name">; def libpath : P<"libpath", "Additional library search path">; +def linkrepro : P<"linkrepro", "Dump linker invocation and input files for debugging">; def machine : P<"machine", "Specify target platform">; def merge : P<"merge", "Combine sections">; def mllvm : P<"mllvm", "Options to pass to LLVM">; @@ -61,7 +62,9 @@ def deffile : Joined<["/", "-"], "def:">, HelpText<"Use module-definition file">; def debug : F<"debug">, HelpText<"Embed a symbol table in the image">; +def debugtype : P<"debugtype", "Debug Info Options">; def dll : F<"dll">, HelpText<"Create a DLL">; +def driver : P<"driver", "Generate a Windows NT Kernel Mode Driver">; def nodefaultlib_all : F<"nodefaultlib">; def noentry : F<"noentry">; def profile : F<"profile">; @@ -91,7 +94,10 @@ def help_q : Flag<["/?", "-?"], "">, Alias; def nosymtab : F<"nosymtab">; // Flags for debugging -def lldmap : Joined<["/", "-"], "lldmap:">; +def debugpdb : F<"debugpdb">; +def dumppdb : Joined<["/", "-"], "dumppdb">; +def lldmap : F<"lldmap">; +def lldmap_file : Joined<["/", "-"], "lldmap:">; //============================================================================== // The flags below do nothing. They are defined only for link.exe compatibility. diff --git a/COFF/PDB.cpp b/COFF/PDB.cpp index 7606ccc680d38..56d5a36511439 100644 --- a/COFF/PDB.cpp +++ b/COFF/PDB.cpp @@ -7,55 +7,187 @@ // //===----------------------------------------------------------------------===// -#include "Driver.h" +#include "PDB.h" +#include "Chunks.h" +#include "Config.h" #include "Error.h" +#include "SymbolTable.h" #include "Symbols.h" +#include "llvm/DebugInfo/CodeView/SymbolDumper.h" +#include "llvm/DebugInfo/CodeView/TypeDumper.h" +#include "llvm/DebugInfo/MSF/ByteStream.h" +#include "llvm/DebugInfo/MSF/MSFBuilder.h" +#include "llvm/DebugInfo/MSF/MSFCommon.h" +#include "llvm/DebugInfo/PDB/Raw/DbiStream.h" +#include "llvm/DebugInfo/PDB/Raw/DbiStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Raw/InfoStream.h" +#include "llvm/DebugInfo/PDB/Raw/InfoStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Raw/PDBFile.h" +#include "llvm/DebugInfo/PDB/Raw/PDBFileBuilder.h" +#include "llvm/DebugInfo/PDB/Raw/TpiStream.h" +#include "llvm/DebugInfo/PDB/Raw/TpiStreamBuilder.h" +#include "llvm/Object/COFF.h" #include "llvm/Support/Endian.h" #include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/ScopedPrinter.h" #include +using namespace lld; +using namespace lld::coff; using namespace llvm; +using namespace llvm::codeview; using namespace llvm::support; using namespace llvm::support::endian; -const int PageSize = 4096; -const uint8_t Magic[32] = "Microsoft C/C++ MSF 7.00\r\n\032DS\0\0"; - -namespace { -struct PDBHeader { - uint8_t Magic[32]; - ulittle32_t PageSize; - ulittle32_t FpmPage; - ulittle32_t PageCount; - ulittle32_t RootSize; - ulittle32_t Reserved; - ulittle32_t RootPointer; -}; -} - -void lld::coff::createPDB(StringRef Path) { - // Create a file. - size_t FileSize = PageSize * 3; - ErrorOr> BufferOrErr = - FileOutputBuffer::create(Path, FileSize); - if (auto EC = BufferOrErr.getError()) - fatal(EC, "failed to open " + Path); - std::unique_ptr Buffer = std::move(*BufferOrErr); - - // Write the file header. - uint8_t *Buf = Buffer->getBufferStart(); - auto *Hdr = reinterpret_cast(Buf); - memcpy(Hdr->Magic, Magic, sizeof(Magic)); - Hdr->PageSize = PageSize; - // I don't know what FpmPage field means, but it must not be 0. - Hdr->FpmPage = 1; - Hdr->PageCount = FileSize / PageSize; - // Root directory is empty, containing only the length field. - Hdr->RootSize = 4; - // Root directory is on page 1. - Hdr->RootPointer = 1; - - // Write the root directory. Root stream is on page 2. - write32le(Buf + PageSize, 2); - Buffer->commit(); +using llvm::object::coff_section; + +static ExitOnError ExitOnErr; + +// Returns a list of all SectionChunks. +static std::vector getInputSections(SymbolTable *Symtab) { + std::vector V; + for (Chunk *C : Symtab->getChunks()) + if (auto *SC = dyn_cast(C)) + V.push_back(*SC->Header); + return V; +} + +static SectionChunk *findByName(std::vector &Sections, + StringRef Name) { + for (SectionChunk *C : Sections) + if (C->getSectionName() == Name) + return C; + return nullptr; +} + +static ArrayRef getDebugT(ObjectFile *File) { + SectionChunk *Sec = findByName(File->getDebugChunks(), ".debug$T"); + if (!Sec) + return {}; + + // First 4 bytes are section magic. + ArrayRef Data = Sec->getContents(); + if (Data.size() < 4) + fatal(".debug$T too short"); + if (read32le(Data.data()) != COFF::DEBUG_SECTION_MAGIC) + fatal(".debug$T has an invalid magic"); + return Data.slice(4); +} + +static void dumpDebugT(ScopedPrinter &W, ObjectFile *File) { + ArrayRef Data = getDebugT(File); + if (Data.empty()) + return; + + msf::ByteStream Stream(Data); + CVTypeDumper TypeDumper(&W, false); + if (auto EC = TypeDumper.dump(Data)) + fatal(EC, "CVTypeDumper::dump failed"); +} + +static void dumpDebugS(ScopedPrinter &W, ObjectFile *File) { + SectionChunk *Sec = findByName(File->getDebugChunks(), ".debug$S"); + if (!Sec) + return; + + msf::ByteStream Stream(Sec->getContents()); + CVSymbolArray Symbols; + msf::StreamReader Reader(Stream); + if (auto EC = Reader.readArray(Symbols, Reader.getLength())) + fatal(EC, "StreamReader.readArray failed"); + + CVTypeDumper TypeDumper(&W, false); + CVSymbolDumper SymbolDumper(W, TypeDumper, nullptr, false); + if (auto EC = SymbolDumper.dump(Symbols)) + fatal(EC, "CVSymbolDumper::dump failed"); +} + +// Dump CodeView debug info. This is for debugging. +static void dumpCodeView(SymbolTable *Symtab) { + ScopedPrinter W(outs()); + + for (ObjectFile *File : Symtab->ObjectFiles) { + dumpDebugT(W, File); + dumpDebugS(W, File); + } +} + +static void addTypeInfo(SymbolTable *Symtab, + pdb::TpiStreamBuilder &TpiBuilder) { + for (ObjectFile *File : Symtab->ObjectFiles) { + ArrayRef Data = getDebugT(File); + if (Data.empty()) + continue; + + msf::ByteStream Stream(Data); + codeview::CVTypeArray Records; + msf::StreamReader Reader(Stream); + if (auto EC = Reader.readArray(Records, Reader.getLength())) + fatal(EC, "Reader.readArray failed"); + for (const codeview::CVType &Rec : Records) + TpiBuilder.addTypeRecord(Rec); + } +} + +// Creates a PDB file. +void coff::createPDB(StringRef Path, SymbolTable *Symtab, + ArrayRef SectionTable) { + if (Config->DumpPdb) + dumpCodeView(Symtab); + + BumpPtrAllocator Alloc; + pdb::PDBFileBuilder Builder(Alloc); + ExitOnErr(Builder.initialize(4096)); // 4096 is blocksize + + // Create streams in MSF for predefined streams, namely + // PDB, TPI, DBI and IPI. + for (int I = 0; I < (int)pdb::kSpecialStreamCount; ++I) + ExitOnErr(Builder.getMsfBuilder().addStream(0)); + + // Add an Info stream. + auto &InfoBuilder = Builder.getInfoBuilder(); + InfoBuilder.setAge(1); + + // Should be a random number, 0 for now. + InfoBuilder.setGuid({}); + + // Should be the current time, but set 0 for reproducibilty. + InfoBuilder.setSignature(0); + InfoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70); + + // Add an empty DPI stream. + auto &DbiBuilder = Builder.getDbiBuilder(); + DbiBuilder.setVersionHeader(pdb::PdbDbiV110); + + // Add an empty TPI stream. + auto &TpiBuilder = Builder.getTpiBuilder(); + TpiBuilder.setVersionHeader(pdb::PdbTpiV80); + if (Config->DebugPdb) + addTypeInfo(Symtab, TpiBuilder); + + // Add an empty IPI stream. + auto &IpiBuilder = Builder.getIpiBuilder(); + IpiBuilder.setVersionHeader(pdb::PdbTpiV80); + + // Add Section Contributions. + std::vector Contribs = + pdb::DbiStreamBuilder::createSectionContribs(getInputSections(Symtab)); + DbiBuilder.setSectionContribs(Contribs); + + // Add Section Map stream. + ArrayRef Sections = { + (const object::coff_section *)SectionTable.data(), + SectionTable.size() / sizeof(object::coff_section)}; + std::vector SectionMap = + pdb::DbiStreamBuilder::createSectionMap(Sections); + DbiBuilder.setSectionMap(SectionMap); + + ExitOnErr(DbiBuilder.addModuleInfo("", "* Linker *")); + + // Add COFF section header stream. + ExitOnErr( + DbiBuilder.addDbgStream(pdb::DbgHeaderType::SectionHdr, SectionTable)); + + // Write to a file. + ExitOnErr(Builder.commit(Path)); } diff --git a/COFF/PDB.h b/COFF/PDB.h new file mode 100644 index 0000000000000..091e90fa1ef10 --- /dev/null +++ b/COFF/PDB.h @@ -0,0 +1,25 @@ +//===- PDB.h ----------------------------------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_COFF_PDB_H +#define LLD_COFF_PDB_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" + +namespace lld { +namespace coff { +class SymbolTable; + +void createPDB(llvm::StringRef Path, SymbolTable *Symtab, + llvm::ArrayRef SectionTable); +} +} + +#endif diff --git a/COFF/Strings.cpp b/COFF/Strings.cpp new file mode 100644 index 0000000000000..d0558413f6733 --- /dev/null +++ b/COFF/Strings.cpp @@ -0,0 +1,30 @@ +//===- Strings.cpp -------------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Strings.h" + +#if defined(_MSC_VER) +#include +#include +#pragma comment(lib, "dbghelp.lib") +#endif + +using namespace lld; +using namespace lld::coff; +using namespace llvm; + +Optional coff::demangle(StringRef S) { +#if defined(_MSC_VER) + char Buf[4096]; + if (S.startswith("?")) + if (size_t Len = UnDecorateSymbolName(S.str().c_str(), Buf, sizeof(Buf), 0)) + return std::string(Buf, Len); +#endif + return None; +} diff --git a/COFF/Strings.h b/COFF/Strings.h new file mode 100644 index 0000000000000..1f85f3e2da5cd --- /dev/null +++ b/COFF/Strings.h @@ -0,0 +1,23 @@ +//===- Strings.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_COFF_STRINGS_H +#define LLD_COFF_STRINGS_H + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include + +namespace lld { +namespace coff { +llvm::Optional demangle(llvm::StringRef S); +} +} + +#endif diff --git a/COFF/SymbolTable.cpp b/COFF/SymbolTable.cpp index df9da4c36650d..9cc0b75c1510a 100644 --- a/COFF/SymbolTable.cpp +++ b/COFF/SymbolTable.cpp @@ -7,12 +7,12 @@ // //===----------------------------------------------------------------------===// +#include "SymbolTable.h" #include "Config.h" #include "Driver.h" #include "Error.h" -#include "SymbolTable.h" +#include "Memory.h" #include "Symbols.h" -#include "lld/Core/Parallel.h" #include "llvm/IR/LLVMContext.h" #include "llvm/LTO/legacy/LTOCodeGenerator.h" #include "llvm/Support/Debug.h" @@ -24,222 +24,265 @@ using namespace llvm; namespace lld { namespace coff { -void SymbolTable::addFile(std::unique_ptr FileP) { -#if LLVM_ENABLE_THREADS - std::launch Policy = std::launch::async; -#else - std::launch Policy = std::launch::deferred; -#endif +SymbolTable *Symtab; - InputFile *File = FileP.get(); - Files.push_back(std::move(FileP)); - if (auto *F = dyn_cast(File)) { - ArchiveQueue.push_back( - std::async(Policy, [=]() { F->parse(); return F; })); - return; +void SymbolTable::addFile(InputFile *File) { + if (Config->Verbose) + outs() << "Reading " << toString(File) << "\n"; + File->parse(); + + MachineTypes MT = File->getMachineType(); + if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) { + Config->Machine = MT; + } else if (MT != IMAGE_FILE_MACHINE_UNKNOWN && Config->Machine != MT) { + fatal(toString(File) + ": machine type " + machineToStr(MT) + + " conflicts with " + machineToStr(Config->Machine)); } - ObjectQueue.push_back( - std::async(Policy, [=]() { File->parse(); return File; })); + if (auto *F = dyn_cast(File)) { ObjectFiles.push_back(F); } else if (auto *F = dyn_cast(File)) { BitcodeFiles.push_back(F); - } else { - ImportFiles.push_back(cast(File)); + } else if (auto *F = dyn_cast(File)) { + ImportFiles.push_back(F); } -} -void SymbolTable::step() { - if (queueEmpty()) + StringRef S = File->getDirectives(); + if (S.empty()) return; - readObjects(); - readArchives(); -} -void SymbolTable::run() { - while (!queueEmpty()) - step(); -} - -void SymbolTable::readArchives() { - if (ArchiveQueue.empty()) - return; - - // Add lazy symbols to the symbol table. Lazy symbols that conflict - // with existing undefined symbols are accumulated in LazySyms. - std::vector LazySyms; - for (std::future &Future : ArchiveQueue) { - ArchiveFile *File = Future.get(); - if (Config->Verbose) - llvm::outs() << "Reading " << File->getShortName() << "\n"; - for (Lazy &Sym : File->getLazySymbols()) - addLazy(&Sym, &LazySyms); - } - ArchiveQueue.clear(); - - // Add archive member files to ObjectQueue that should resolve - // existing undefined symbols. - for (Symbol *Sym : LazySyms) - addMemberFile(cast(Sym->Body)); -} - -void SymbolTable::readObjects() { - if (ObjectQueue.empty()) - return; - - // Add defined and undefined symbols to the symbol table. - std::vector Directives; - for (size_t I = 0; I < ObjectQueue.size(); ++I) { - InputFile *File = ObjectQueue[I].get(); - if (Config->Verbose) - llvm::outs() << "Reading " << File->getShortName() << "\n"; - // Adding symbols may add more files to ObjectQueue - // (but not to ArchiveQueue). - for (SymbolBody *Sym : File->getSymbols()) - if (Sym->isExternal()) - addSymbol(Sym); - StringRef S = File->getDirectives(); - if (!S.empty()) { - Directives.push_back(S); - if (Config->Verbose) - llvm::outs() << "Directives: " << File->getShortName() - << ": " << S << "\n"; - } - } - ObjectQueue.clear(); - - // Parse directive sections. This may add files to - // ArchiveQueue and ObjectQueue. - for (StringRef S : Directives) - Driver->parseDirectives(S); -} - -bool SymbolTable::queueEmpty() { - return ArchiveQueue.empty() && ObjectQueue.empty(); + if (Config->Verbose) + outs() << "Directives: " << toString(File) << ": " << S << "\n"; + Driver->parseDirectives(S); } -void SymbolTable::reportRemainingUndefines(bool Resolve) { - llvm::SmallPtrSet Undefs; +void SymbolTable::reportRemainingUndefines() { + SmallPtrSet Undefs; for (auto &I : Symtab) { Symbol *Sym = I.second; - auto *Undef = dyn_cast(Sym->Body); + auto *Undef = dyn_cast(Sym->body()); if (!Undef) continue; + if (!Sym->IsUsedInRegularObj) + continue; StringRef Name = Undef->getName(); // A weak alias may have been resolved, so check for that. if (Defined *D = Undef->getWeakAlias()) { - if (Resolve) - Sym->Body = D; + // We resolve weak aliases by replacing the alias's SymbolBody with the + // target's SymbolBody. This causes all SymbolBody pointers referring to + // the old symbol to instead refer to the new symbol. However, we can't + // just blindly copy sizeof(Symbol::Body) bytes from D to Sym->Body + // because D may be an internal symbol, and internal symbols are stored as + // "unparented" SymbolBodies. For that reason we need to check which type + // of symbol we are dealing with and copy the correct number of bytes. + if (isa(D)) + memcpy(Sym->Body.buffer, D, sizeof(DefinedRegular)); + else if (isa(D)) + memcpy(Sym->Body.buffer, D, sizeof(DefinedAbsolute)); + else + // No other internal symbols are possible. + Sym->Body = D->symbol()->Body; continue; } // If we can resolve a symbol by removing __imp_ prefix, do that. // This odd rule is for compatibility with MSVC linker. if (Name.startswith("__imp_")) { Symbol *Imp = find(Name.substr(strlen("__imp_"))); - if (Imp && isa(Imp->Body)) { - if (!Resolve) - continue; - auto *D = cast(Imp->Body); - auto *S = new (Alloc) DefinedLocalImport(Name, D); - LocalImportChunks.push_back(S->getChunk()); - Sym->Body = S; + if (Imp && isa(Imp->body())) { + auto *D = cast(Imp->body()); + replaceBody(Sym, Name, D); + LocalImportChunks.push_back( + cast(Sym->body())->getChunk()); continue; } } // Remaining undefined symbols are not fatal if /force is specified. // They are replaced with dummy defined symbols. - if (Config->Force && Resolve) - Sym->Body = new (Alloc) DefinedAbsolute(Name, 0); - Undefs.insert(Sym->Body); + if (Config->Force) + replaceBody(Sym, Name, 0); + Undefs.insert(Sym->body()); } if (Undefs.empty()) return; - for (Undefined *U : Config->GCRoot) - if (Undefs.count(U->repl())) - llvm::errs() << ": undefined symbol: " << U->getName() << "\n"; - for (std::unique_ptr &File : Files) - if (!isa(File.get())) - for (SymbolBody *Sym : File->getSymbols()) - if (Undefs.count(Sym->repl())) - llvm::errs() << File->getShortName() << ": undefined symbol: " - << Sym->getName() << "\n"; + for (SymbolBody *B : Config->GCRoot) + if (Undefs.count(B)) + errs() << ": undefined symbol: " << B->getName() << "\n"; + for (ObjectFile *File : ObjectFiles) + for (SymbolBody *Sym : File->getSymbols()) + if (Undefs.count(Sym)) + errs() << toString(File) << ": undefined symbol: " << Sym->getName() + << "\n"; if (!Config->Force) fatal("link failed"); } -void SymbolTable::addLazy(Lazy *New, std::vector *Accum) { - Symbol *Sym = insert(New); - if (Sym->Body == New) - return; - SymbolBody *Existing = Sym->Body; - if (isa(Existing)) - return; - if (Lazy *L = dyn_cast(Existing)) - if (L->getFileIndex() < New->getFileIndex()) - return; - Sym->Body = New; - New->setBackref(Sym); - if (isa(Existing)) - Accum->push_back(Sym); +std::pair SymbolTable::insert(StringRef Name) { + Symbol *&Sym = Symtab[CachedHashStringRef(Name)]; + if (Sym) + return {Sym, false}; + Sym = make(); + Sym->IsUsedInRegularObj = false; + Sym->PendingArchiveLoad = false; + return {Sym, true}; } -void SymbolTable::addSymbol(SymbolBody *New) { - // Find an existing symbol or create and insert a new one. - assert(isa(New) || isa(New)); - Symbol *Sym = insert(New); - if (Sym->Body == New) - return; - SymbolBody *Existing = Sym->Body; - - // If we have an undefined symbol and a lazy symbol, - // let the lazy symbol to read a member file. - if (auto *L = dyn_cast(Existing)) { - // Undefined symbols with weak aliases need not to be resolved, - // since they would be replaced with weak aliases if they remain - // undefined. - if (auto *U = dyn_cast(New)) { - if (!U->WeakAlias) { - addMemberFile(L); - return; - } +Symbol *SymbolTable::addUndefined(StringRef Name, InputFile *F, + bool IsWeakAlias) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(Name); + if (!F || !isa(F)) + S->IsUsedInRegularObj = true; + if (WasInserted || (isa(S->body()) && IsWeakAlias)) { + replaceBody(S, Name); + return S; + } + if (auto *L = dyn_cast(S->body())) { + if (!S->PendingArchiveLoad) { + S->PendingArchiveLoad = true; + L->File->addMember(&L->Sym); } - Sym->Body = New; + } + return S; +} + +void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol Sym) { + StringRef Name = Sym.getName(); + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(Name); + if (WasInserted) { + replaceBody(S, F, Sym); return; } + auto *U = dyn_cast(S->body()); + if (!U || U->WeakAlias || S->PendingArchiveLoad) + return; + S->PendingArchiveLoad = true; + F->addMember(&Sym); +} + +void SymbolTable::reportDuplicate(Symbol *Existing, InputFile *NewFile) { + fatal("duplicate symbol: " + toString(*Existing->body()) + " in " + + toString(Existing->body()->getFile()) + " and in " + + (NewFile ? toString(NewFile) : "(internal)")); +} - // compare() returns -1, 0, or 1 if the lhs symbol is less preferable, - // equivalent (conflicting), or more preferable, respectively. - int Comp = Existing->compare(New); - if (Comp == 0) - fatal("duplicate symbol: " + Existing->getDebugName() + " and " + - New->getDebugName()); - if (Comp < 0) - Sym->Body = New; +Symbol *SymbolTable::addAbsolute(StringRef N, COFFSymbolRef Sym) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(N); + S->IsUsedInRegularObj = true; + if (WasInserted || isa(S->body()) || isa(S->body())) + replaceBody(S, N, Sym); + else if (!isa(S->body())) + reportDuplicate(S, nullptr); + return S; } -Symbol *SymbolTable::insert(SymbolBody *New) { - Symbol *&Sym = Symtab[New->getName()]; - if (Sym) { - New->setBackref(Sym); - return Sym; +Symbol *SymbolTable::addAbsolute(StringRef N, uint64_t VA) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(N); + S->IsUsedInRegularObj = true; + if (WasInserted || isa(S->body()) || isa(S->body())) + replaceBody(S, N, VA); + else if (!isa(S->body())) + reportDuplicate(S, nullptr); + return S; +} + +Symbol *SymbolTable::addRelative(StringRef N, uint64_t VA) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(N); + S->IsUsedInRegularObj = true; + if (WasInserted || isa(S->body()) || isa(S->body())) + replaceBody(S, N, VA); + else if (!isa(S->body())) + reportDuplicate(S, nullptr); + return S; +} + +Symbol *SymbolTable::addRegular(ObjectFile *F, COFFSymbolRef Sym, + SectionChunk *C) { + StringRef Name; + F->getCOFFObj()->getSymbolName(Sym, Name); + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(Name); + S->IsUsedInRegularObj = true; + if (WasInserted || isa(S->body()) || isa(S->body())) + replaceBody(S, F, Sym, C); + else if (auto *R = dyn_cast(S->body())) { + if (!C->isCOMDAT() || !R->isCOMDAT()) + reportDuplicate(S, F); + } else if (auto *B = dyn_cast(S->body())) { + if (B->IsReplaceable) + replaceBody(S, F, Sym, C); + else if (!C->isCOMDAT()) + reportDuplicate(S, F); + } else + replaceBody(S, F, Sym, C); + return S; +} + +Symbol *SymbolTable::addBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(N); + if (WasInserted || isa(S->body()) || isa(S->body())) { + replaceBody(S, F, N, IsReplaceable); + return S; } - Sym = new (Alloc) Symbol(New); - New->setBackref(Sym); - return Sym; + if (isa(S->body())) + return S; + if (IsReplaceable) + if (isa(S->body()) || isa(S->body())) + return S; + reportDuplicate(S, F); + return S; } -// Reads an archive member file pointed by a given symbol. -void SymbolTable::addMemberFile(Lazy *Body) { - std::unique_ptr File = Body->getMember(); +Symbol *SymbolTable::addCommon(ObjectFile *F, COFFSymbolRef Sym, + CommonChunk *C) { + StringRef Name; + F->getCOFFObj()->getSymbolName(Sym, Name); + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(Name); + S->IsUsedInRegularObj = true; + if (WasInserted || !isa(S->body())) + replaceBody(S, F, Sym, C); + else if (auto *DC = dyn_cast(S->body())) + if (Sym.getValue() > DC->getSize()) + replaceBody(S, F, Sym, C); + return S; +} - // getMember returns an empty buffer if the member was already - // read from the library. - if (!File) - return; - if (Config->Verbose) - llvm::outs() << "Loaded " << File->getShortName() << " for " - << Body->getName() << "\n"; - addFile(std::move(File)); +Symbol *SymbolTable::addImportData(StringRef N, ImportFile *F) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(N); + S->IsUsedInRegularObj = true; + if (WasInserted || isa(S->body()) || isa(S->body())) + replaceBody(S, N, F); + else if (!isa(S->body())) + reportDuplicate(S, nullptr); + return S; +} + +Symbol *SymbolTable::addImportThunk(StringRef Name, DefinedImportData *ID, + uint16_t Machine) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(Name); + S->IsUsedInRegularObj = true; + if (WasInserted || isa(S->body()) || isa(S->body())) + replaceBody(S, Name, ID, Machine); + else if (!isa(S->body())) + reportDuplicate(S, nullptr); + return S; } std::vector SymbolTable::getChunks() { @@ -252,7 +295,7 @@ std::vector SymbolTable::getChunks() { } Symbol *SymbolTable::find(StringRef Name) { - auto It = Symtab.find(Name); + auto It = Symtab.find(CachedHashStringRef(Name)); if (It == Symtab.end()) return nullptr; return It->second; @@ -266,7 +309,7 @@ Symbol *SymbolTable::findUnderscore(StringRef Name) { StringRef SymbolTable::findByPrefix(StringRef Prefix) { for (auto Pair : Symtab) { - StringRef Name = Pair.first; + StringRef Name = Pair.first.val(); if (Name.startswith(Prefix)) return Name; } @@ -275,7 +318,7 @@ StringRef SymbolTable::findByPrefix(StringRef Prefix) { StringRef SymbolTable::findMangle(StringRef Name) { if (Symbol *Sym = find(Name)) - if (!isa(Sym->Body)) + if (!isa(Sym->body())) return Name; if (Config->Machine != I386) return findByPrefix(("?" + Name + "@@Y").str()); @@ -289,39 +332,22 @@ StringRef SymbolTable::findMangle(StringRef Name) { return findByPrefix(("?" + Name.substr(1) + "@@Y").str()); } -void SymbolTable::mangleMaybe(Undefined *U) { - if (U->WeakAlias) - return; - if (!isa(U->repl())) +void SymbolTable::mangleMaybe(SymbolBody *B) { + auto *U = dyn_cast(B); + if (!U || U->WeakAlias) return; StringRef Alias = findMangle(U->getName()); if (!Alias.empty()) U->WeakAlias = addUndefined(Alias); } -Undefined *SymbolTable::addUndefined(StringRef Name) { - auto *New = new (Alloc) Undefined(Name); - addSymbol(New); - if (auto *U = dyn_cast(New->repl())) - return U; - return New; -} - -DefinedRelative *SymbolTable::addRelative(StringRef Name, uint64_t VA) { - auto *New = new (Alloc) DefinedRelative(Name, VA); - addSymbol(New); - return New; -} - -DefinedAbsolute *SymbolTable::addAbsolute(StringRef Name, uint64_t VA) { - auto *New = new (Alloc) DefinedAbsolute(Name, VA); - addSymbol(New); - return New; +SymbolBody *SymbolTable::addUndefined(StringRef Name) { + return addUndefined(Name, nullptr, false)->body(); } void SymbolTable::printMap(llvm::raw_ostream &OS) { for (ObjectFile *File : ObjectFiles) { - OS << File->getShortName() << ":\n"; + OS << toString(File) << ":\n"; for (SymbolBody *Body : File->getSymbols()) if (auto *R = dyn_cast(Body)) if (R->getChunk()->isLive()) @@ -330,84 +356,32 @@ void SymbolTable::printMap(llvm::raw_ostream &OS) { } } -void SymbolTable::addCombinedLTOObject(ObjectFile *Obj) { - for (SymbolBody *Body : Obj->getSymbols()) { - if (!Body->isExternal()) - continue; - // We should not see any new undefined symbols at this point, but we'll - // diagnose them later in reportRemainingUndefines(). - StringRef Name = Body->getName(); - Symbol *Sym = insert(Body); - SymbolBody *Existing = Sym->Body; - - if (Existing == Body) - continue; - - if (isa(Existing)) { - Sym->Body = Body; - continue; - } - if (auto *L = dyn_cast(Existing)) { - // We may see new references to runtime library symbols such as __chkstk - // here. These symbols must be wholly defined in non-bitcode files. - addMemberFile(L); - continue; - } - - int Comp = Existing->compare(Body); - if (Comp == 0) - fatal("LTO: unexpected duplicate symbol: " + Name); - if (Comp < 0) - Sym->Body = Body; - } -} - void SymbolTable::addCombinedLTOObjects() { if (BitcodeFiles.empty()) return; - // Diagnose any undefined symbols early, but do not resolve weak externals, - // as resolution breaks the invariant that each Symbol points to a unique - // SymbolBody, which we rely on to replace DefinedBitcode symbols correctly. - reportRemainingUndefines(/*Resolve=*/false); - // Create an object file and add it to the symbol table by replacing any // DefinedBitcode symbols with the definitions in the object file. LTOCodeGenerator CG(BitcodeFile::Context); CG.setOptLevel(Config->LTOOptLevel); - std::vector Objs = createLTOObjects(&CG); - - for (ObjectFile *Obj : Objs) - addCombinedLTOObject(Obj); - - size_t NumBitcodeFiles = BitcodeFiles.size(); - run(); - if (BitcodeFiles.size() != NumBitcodeFiles) - fatal("LTO: late loaded symbol created new bitcode reference"); + for (ObjectFile *Obj : createLTOObjects(&CG)) + Obj->parse(); } // Combine and compile bitcode files and then return the result // as a vector of regular COFF object files. std::vector SymbolTable::createLTOObjects(LTOCodeGenerator *CG) { - // All symbols referenced by non-bitcode objects must be preserved. - for (ObjectFile *File : ObjectFiles) - for (SymbolBody *Body : File->getSymbols()) - if (auto *S = dyn_cast(Body->repl())) - CG->addMustPreserveSymbol(S->getName()); - - // Likewise for bitcode symbols which we initially resolved to non-bitcode. + // All symbols referenced by non-bitcode objects, including GC roots, must be + // preserved. We must also replace bitcode symbols with undefined symbols so + // that they may be replaced with real definitions without conflicting. for (BitcodeFile *File : BitcodeFiles) - for (SymbolBody *Body : File->getSymbols()) - if (isa(Body) && !isa(Body->repl())) + for (SymbolBody *Body : File->getSymbols()) { + if (!isa(Body)) + continue; + if (Body->symbol()->IsUsedInRegularObj) CG->addMustPreserveSymbol(Body->getName()); - - // Likewise for other symbols that must be preserved. - for (Undefined *U : Config->GCRoot) { - if (auto *S = dyn_cast(U->repl())) - CG->addMustPreserveSymbol(S->getName()); - else if (auto *S = dyn_cast_or_null(U->getWeakAlias())) - CG->addMustPreserveSymbol(S->getName()); - } + replaceBody(Body->symbol(), Body->getName()); + } CG->setModule(BitcodeFiles[0]->takeModule()); for (unsigned I = 1, E = BitcodeFiles.size(); I != E; ++I) @@ -434,10 +408,8 @@ std::vector SymbolTable::createLTOObjects(LTOCodeGenerator *CG) { std::vector ObjFiles; for (SmallString<0> &Obj : Objs) { - auto *ObjFile = new ObjectFile(MemoryBufferRef(Obj, "")); - Files.emplace_back(ObjFile); + auto *ObjFile = make(MemoryBufferRef(Obj, "")); ObjectFiles.push_back(ObjFile); - ObjFile->parse(); ObjFiles.push_back(ObjFile); } diff --git a/COFF/SymbolTable.h b/COFF/SymbolTable.h index 8bf4387cdfff0..703821f2e1245 100644 --- a/COFF/SymbolTable.h +++ b/COFF/SymbolTable.h @@ -11,18 +11,12 @@ #define LLD_COFF_SYMBOL_TABLE_H #include "InputFiles.h" +#include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseMapInfo.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/raw_ostream.h" -#ifdef _MSC_VER -// depends on for __uncaught_exception. -#include -#endif - -#include - namespace llvm { struct LTOCodeGenerator; } @@ -31,8 +25,12 @@ namespace lld { namespace coff { class Chunk; +class CommonChunk; class Defined; +class DefinedAbsolute; +class DefinedRelative; class Lazy; +class SectionChunk; class SymbolBody; struct Symbol; @@ -45,18 +43,17 @@ struct Symbol; // conflicts. For example, obviously, a defined symbol is better than // an undefined symbol. Or, if there's a conflict between a lazy and a // undefined, it'll read an archive member to read a real definition -// to replace the lazy symbol. The logic is implemented in resolve(). +// to replace the lazy symbol. The logic is implemented in the +// add*() functions, which are called by input files as they are parsed. +// There is one add* function per symbol type. class SymbolTable { public: - void addFile(std::unique_ptr File); - std::vector> &getFiles() { return Files; } - void step(); - void run(); - bool queueEmpty(); + void addFile(InputFile *File); - // Print an error message on undefined symbols. If Resolve is true, try to - // resolve any undefined symbols and update the symbol table accordingly. - void reportRemainingUndefines(bool Resolve); + // Try to resolve any undefined symbols and update the symbol table + // accordingly, then print an error message for any remaining undefined + // symbols. + void reportRemainingUndefines(); // Returns a list of chunks of selected symbols. std::vector getChunks(); @@ -69,7 +66,7 @@ public: // mangled symbol. This function tries to find a mangled name // for U from the symbol table, and if found, set the symbol as // a weak alias for U. - void mangleMaybe(Undefined *U); + void mangleMaybe(SymbolBody *B); StringRef findMangle(StringRef Name); // Print a layout map to OS. @@ -88,37 +85,44 @@ public: std::vector ObjectFiles; // Creates an Undefined symbol for a given name. - Undefined *addUndefined(StringRef Name); - DefinedRelative *addRelative(StringRef Name, uint64_t VA); - DefinedAbsolute *addAbsolute(StringRef Name, uint64_t VA); + SymbolBody *addUndefined(StringRef Name); + + Symbol *addRelative(StringRef N, uint64_t VA); + Symbol *addAbsolute(StringRef N, uint64_t VA); + + Symbol *addUndefined(StringRef Name, InputFile *F, bool IsWeakAlias); + void addLazy(ArchiveFile *F, const Archive::Symbol Sym); + Symbol *addAbsolute(StringRef N, COFFSymbolRef S); + Symbol *addRegular(ObjectFile *F, COFFSymbolRef S, SectionChunk *C); + Symbol *addBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable); + Symbol *addCommon(ObjectFile *F, COFFSymbolRef S, CommonChunk *C); + Symbol *addImportData(StringRef N, ImportFile *F); + Symbol *addImportThunk(StringRef Name, DefinedImportData *S, + uint16_t Machine); + + void reportDuplicate(Symbol *Existing, InputFile *NewFile); // A list of chunks which to be added to .rdata. std::vector LocalImportChunks; private: - void readArchives(); + void readArchive(); void readObjects(); - void addSymbol(SymbolBody *New); - void addLazy(Lazy *New, std::vector *Accum); - Symbol *insert(SymbolBody *New); + std::pair insert(StringRef Name); StringRef findByPrefix(StringRef Prefix); - void addMemberFile(Lazy *Body); void addCombinedLTOObject(ObjectFile *Obj); std::vector createLTOObjects(llvm::LTOCodeGenerator *CG); - llvm::DenseMap Symtab; - - std::vector> Files; - std::vector> ArchiveQueue; - std::vector> ObjectQueue; + llvm::DenseMap Symtab; std::vector BitcodeFiles; std::vector> Objs; - llvm::BumpPtrAllocator Alloc; }; +extern SymbolTable *Symtab; + } // namespace coff } // namespace lld diff --git a/COFF/Symbols.cpp b/COFF/Symbols.cpp index 6e2db6631ce79..6de85d581f494 100644 --- a/COFF/Symbols.cpp +++ b/COFF/Symbols.cpp @@ -7,16 +7,17 @@ // //===----------------------------------------------------------------------===// +#include "Symbols.h" #include "Error.h" #include "InputFiles.h" -#include "Symbols.h" +#include "Memory.h" +#include "Strings.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" +using namespace llvm; using namespace llvm::object; -using llvm::sys::fs::identify_magic; -using llvm::sys::fs::file_magic; namespace lld { namespace coff { @@ -36,130 +37,14 @@ StringRef SymbolBody::getName() { return Name; } -// Returns 1, 0 or -1 if this symbol should take precedence -// over the Other, tie or lose, respectively. -int SymbolBody::compare(SymbolBody *Other) { - Kind LK = kind(), RK = Other->kind(); - - // Normalize so that the smaller kind is on the left. - if (LK > RK) - return -Other->compare(this); - - // First handle comparisons between two different kinds. - if (LK != RK) { - if (RK > LastDefinedKind) { - if (LK == LazyKind && cast(Other)->WeakAlias) - return -1; - - // The LHS is either defined or lazy and so it wins. - assert((LK <= LastDefinedKind || LK == LazyKind) && "Bad kind!"); - return 1; - } - - // Bitcode has special complexities. - if (RK == DefinedBitcodeKind) { - auto *RHS = cast(Other); - - switch (LK) { - case DefinedCommonKind: - return 1; - - case DefinedRegularKind: - // As an approximation, regular symbols win over bitcode symbols, - // but we definitely have a conflict if the regular symbol is not - // replaceable and neither is the bitcode symbol. We do not - // replicate the rest of the symbol resolution logic here; symbol - // resolution will be done accurately after lowering bitcode symbols - // to regular symbols in addCombinedLTOObject(). - if (cast(this)->isCOMDAT() || RHS->IsReplaceable) - return 1; - - // Fallthrough to the default of a tie otherwise. - default: - return 0; - } - } - - // Either of the object file kind will trump a higher kind. - if (LK <= LastDefinedCOFFKind) - return 1; - - // The remaining kind pairs are ties amongst defined symbols. - return 0; - } - - // Now handle the case where the kinds are the same. - switch (LK) { - case DefinedRegularKind: { - auto *LHS = cast(this); - auto *RHS = cast(Other); - if (LHS->isCOMDAT() && RHS->isCOMDAT()) - return LHS->getFileIndex() < RHS->getFileIndex() ? 1 : -1; - return 0; - } - - case DefinedCommonKind: { - auto *LHS = cast(this); - auto *RHS = cast(Other); - if (LHS->getSize() == RHS->getSize()) - return LHS->getFileIndex() < RHS->getFileIndex() ? 1 : -1; - return LHS->getSize() > RHS->getSize() ? 1 : -1; - } - - case DefinedBitcodeKind: { - auto *LHS = cast(this); - auto *RHS = cast(Other); - // If both are non-replaceable, we have a tie. - if (!LHS->IsReplaceable && !RHS->IsReplaceable) - return 0; - - // Non-replaceable symbols win, but even two replaceable symboles don't - // tie. If both symbols are replaceable, choice is arbitrary. - if (RHS->IsReplaceable && LHS->IsReplaceable) - return uintptr_t(LHS) < uintptr_t(RHS) ? 1 : -1; - return LHS->IsReplaceable ? -1 : 1; - } - - case LazyKind: { - // Don't tie, pick the earliest. - auto *LHS = cast(this); - auto *RHS = cast(Other); - return LHS->getFileIndex() < RHS->getFileIndex() ? 1 : -1; - } - - case UndefinedKind: { - auto *LHS = cast(this); - auto *RHS = cast(Other); - // Tie if both undefined symbols have different weak aliases. - if (LHS->WeakAlias && RHS->WeakAlias) { - if (LHS->WeakAlias->getName() != RHS->WeakAlias->getName()) - return 0; - return uintptr_t(LHS) < uintptr_t(RHS) ? 1 : -1; - } - return LHS->WeakAlias ? 1 : -1; - } - - case DefinedLocalImportKind: - case DefinedImportThunkKind: - case DefinedImportDataKind: - case DefinedAbsoluteKind: - case DefinedRelativeKind: - // These all simply tie. - return 0; - } - llvm_unreachable("unknown symbol kind"); -} - -std::string SymbolBody::getDebugName() { - std::string N = getName().str(); - if (auto *D = dyn_cast(this)) { - N += " "; - N += D->File->getShortName(); - } else if (auto *D = dyn_cast(this)) { - N += " "; - N += D->File->getShortName(); - } - return N; +InputFile *SymbolBody::getFile() { + if (auto *Sym = dyn_cast(this)) + return Sym->File; + if (auto *Sym = dyn_cast(this)) + return Sym->File; + if (auto *Sym = dyn_cast(this)) + return Sym->File; + return nullptr; } COFFSymbolRef DefinedCOFF::getCOFFSymbol() { @@ -174,44 +59,27 @@ DefinedImportThunk::DefinedImportThunk(StringRef Name, DefinedImportData *S, uint16_t Machine) : Defined(DefinedImportThunkKind, Name) { switch (Machine) { - case AMD64: Data.reset(new ImportThunkChunkX64(S)); return; - case I386: Data.reset(new ImportThunkChunkX86(S)); return; - case ARMNT: Data.reset(new ImportThunkChunkARM(S)); return; + case AMD64: Data = make(S); return; + case I386: Data = make(S); return; + case ARMNT: Data = make(S); return; default: llvm_unreachable("unknown machine type"); } } -std::unique_ptr Lazy::getMember() { - MemoryBufferRef MBRef = File->getMember(&Sym); - - // getMember returns an empty buffer if the member was already - // read from the library. - if (MBRef.getBuffer().empty()) - return std::unique_ptr(nullptr); - - file_magic Magic = identify_magic(MBRef.getBuffer()); - if (Magic == file_magic::coff_import_library) - return std::unique_ptr(new ImportFile(MBRef)); - - std::unique_ptr Obj; - if (Magic == file_magic::coff_object) - Obj.reset(new ObjectFile(MBRef)); - else if (Magic == file_magic::bitcode) - Obj.reset(new BitcodeFile(MBRef)); - else - fatal("unknown file type: " + File->getName()); - - Obj->setParentName(File->getName()); - return Obj; -} - Defined *Undefined::getWeakAlias() { // A weak alias may be a weak alias to another symbol, so check recursively. for (SymbolBody *A = WeakAlias; A; A = cast(A)->WeakAlias) - if (auto *D = dyn_cast(A->repl())) + if (auto *D = dyn_cast(A)) return D; return nullptr; } +// Returns a symbol name for an error message. +std::string toString(SymbolBody &B) { + if (Optional S = demangle(B.getName())) + return ("\"" + *S + "\" (" + B.getName() + ")").str(); + return B.getName(); +} + } // namespace coff } // namespace lld diff --git a/COFF/Symbols.h b/COFF/Symbols.h index f96c1fb3cc1d3..bc9ad4aa8affa 100644 --- a/COFF/Symbols.h +++ b/COFF/Symbols.h @@ -12,6 +12,7 @@ #include "Chunks.h" #include "Config.h" +#include "Memory.h" #include "lld/Core/LLVM.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Object/Archive.h" @@ -32,15 +33,8 @@ class ArchiveFile; class BitcodeFile; class InputFile; class ObjectFile; -class SymbolBody; - -// A real symbol object, SymbolBody, is usually accessed indirectly -// through a Symbol. There's always one Symbol for each symbol name. -// The resolver updates SymbolBody pointers as it resolves symbols. -struct Symbol { - explicit Symbol(SymbolBody *P) : Body(P) {} - SymbolBody *Body; -}; +struct Symbol; +class SymbolTable; // The base class for real symbol classes. class SymbolBody { @@ -75,28 +69,19 @@ public: // Returns the symbol name. StringRef getName(); - // A SymbolBody has a backreference to a Symbol. Originally they are - // doubly-linked. A backreference will never change. But the pointer - // in the Symbol may be mutated by the resolver. If you have a - // pointer P to a SymbolBody and are not sure whether the resolver - // has chosen the object among other objects having the same name, - // you can access P->Backref->Body to get the resolver's result. - void setBackref(Symbol *P) { Backref = P; } - SymbolBody *repl() { return Backref ? Backref->Body : this; } - - // Decides which symbol should "win" in the symbol table, this or - // the Other. Returns 1 if this wins, -1 if the Other wins, or 0 if - // they are duplicate (conflicting) symbols. - int compare(SymbolBody *Other); + // Returns the file from which this symbol was created. + InputFile *getFile(); - // Returns a name of this symbol including source file name. - // Used only for debugging and logging. - std::string getDebugName(); + Symbol *symbol(); + const Symbol *symbol() const { + return const_cast(this)->symbol(); + } protected: + friend SymbolTable; explicit SymbolBody(Kind K, StringRef N = "") : SymbolKind(K), IsExternal(true), IsCOMDAT(false), - IsReplaceable(false), Name(N) {} + IsReplaceable(false), WrittenToSymtab(false), Name(N) {} const unsigned SymbolKind : 8; unsigned IsExternal : 1; @@ -107,8 +92,12 @@ protected: // This bit is used by the \c DefinedBitcode subclass. unsigned IsReplaceable : 1; +public: + // This bit is used by Writer::createSymbolAndStringTable(). + unsigned WrittenToSymtab : 1; + +protected: StringRef Name; - Symbol *Backref = nullptr; }; // The base class for any defined symbols, including absolute symbols, @@ -149,12 +138,13 @@ public: return S->kind() <= LastDefinedCOFFKind; } - int getFileIndex() { return File->Index; } + ObjectFile *getFile() { return File; } COFFSymbolRef getCOFFSymbol(); -protected: ObjectFile *File; + +protected: const coff_symbol_generic *Sym; }; @@ -194,7 +184,7 @@ public: uint64_t getRVA() { return Data->getRVA(); } private: - friend SymbolBody; + friend SymbolTable; uint64_t getSize() { return Sym->Value; } CommonChunk *Data; }; @@ -253,14 +243,12 @@ public: static bool classof(const SymbolBody *S) { return S->kind() == LazyKind; } - // Returns an object file for this symbol, or a nullptr if the file - // was already returned. - std::unique_ptr getMember(); + ArchiveFile *File; - int getFileIndex() { return File->Index; } +private: + friend SymbolTable; private: - ArchiveFile *File; const Archive::Symbol Sym; }; @@ -293,26 +281,22 @@ public: // table in an output. The former has "__imp_" prefix. class DefinedImportData : public Defined { public: - DefinedImportData(StringRef D, StringRef N, StringRef E, - const coff_import_header *H) - : Defined(DefinedImportDataKind, N), DLLName(D), ExternalName(E), Hdr(H) { + DefinedImportData(StringRef N, ImportFile *F) + : Defined(DefinedImportDataKind, N), File(F) { } static bool classof(const SymbolBody *S) { return S->kind() == DefinedImportDataKind; } - uint64_t getRVA() { return Location->getRVA(); } - StringRef getDLLName() { return DLLName; } - StringRef getExternalName() { return ExternalName; } - void setLocation(Chunk *AddressTable) { Location = AddressTable; } - uint16_t getOrdinal() { return Hdr->OrdinalHint; } + uint64_t getRVA() { return File->Location->getRVA(); } + StringRef getDLLName() { return File->DLLName; } + StringRef getExternalName() { return File->ExternalName; } + void setLocation(Chunk *AddressTable) { File->Location = AddressTable; } + uint16_t getOrdinal() { return File->Hdr->OrdinalHint; } private: - StringRef DLLName; - StringRef ExternalName; - const coff_import_header *Hdr; - Chunk *Location = nullptr; + ImportFile *File; }; // This class represents a symbol for a jump table entry which jumps @@ -329,10 +313,10 @@ public: } uint64_t getRVA() { return Data->getRVA(); } - Chunk *getChunk() { return Data.get(); } + Chunk *getChunk() { return Data; } private: - std::unique_ptr Data; + Chunk *Data; }; // If you have a symbol "__imp_foo" in your object file, a symbol name @@ -343,17 +327,17 @@ private: class DefinedLocalImport : public Defined { public: DefinedLocalImport(StringRef N, Defined *S) - : Defined(DefinedLocalImportKind, N), Data(S) {} + : Defined(DefinedLocalImportKind, N), Data(make(S)) {} static bool classof(const SymbolBody *S) { return S->kind() == DefinedLocalImportKind; } - uint64_t getRVA() { return Data.getRVA(); } - Chunk *getChunk() { return &Data; } + uint64_t getRVA() { return Data->getRVA(); } + Chunk *getChunk() { return Data; } private: - LocalImportChunk Data; + LocalImportChunk *Data; }; class DefinedBitcode : public Defined { @@ -361,6 +345,11 @@ class DefinedBitcode : public Defined { public: DefinedBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable) : Defined(DefinedBitcodeKind, N), File(F) { + // IsReplaceable tracks whether the bitcode symbol may be replaced with some + // other (defined, common or bitcode) symbol. This is the case for common, + // comdat and weak external symbols. We try to replace bitcode symbols with + // "real" symbols (see SymbolTable::add{Regular,Bitcode}), and resolve the + // result against the real symbol from the combined LTO object. this->IsReplaceable = IsReplaceable; } @@ -368,7 +357,6 @@ public: return S->kind() == DefinedBitcodeKind; } -private: BitcodeFile *File; }; @@ -397,6 +385,52 @@ inline uint64_t Defined::getRVA() { llvm_unreachable("unknown symbol kind"); } +// A real symbol object, SymbolBody, is usually stored within a Symbol. There's +// always one Symbol for each symbol name. The resolver updates the SymbolBody +// stored in the Body field of this object as it resolves symbols. Symbol also +// holds computed properties of symbol names. +struct Symbol { + // True if this symbol was referenced by a regular (non-bitcode) object. + unsigned IsUsedInRegularObj : 1; + + // True if we've seen both a lazy and an undefined symbol with this symbol + // name, which means that we have enqueued an archive member load and should + // not load any more archive members to resolve the same symbol. + unsigned PendingArchiveLoad : 1; + + // This field is used to store the Symbol's SymbolBody. This instantiation of + // AlignedCharArrayUnion gives us a struct with a char array field that is + // large and aligned enough to store any derived class of SymbolBody. + llvm::AlignedCharArrayUnion + Body; + + SymbolBody *body() { + return reinterpret_cast(Body.buffer); + } + const SymbolBody *body() const { return const_cast(this)->body(); } +}; + +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)); +} + +std::string toString(SymbolBody &B); + } // namespace coff } // namespace lld diff --git a/COFF/Writer.cpp b/COFF/Writer.cpp index d8077df957011..3e69aebbb424d 100644 --- a/COFF/Writer.cpp +++ b/COFF/Writer.cpp @@ -7,13 +7,15 @@ // //===----------------------------------------------------------------------===// +#include "Writer.h" #include "Config.h" #include "DLL.h" #include "Error.h" #include "InputFiles.h" +#include "Memory.h" +#include "PDB.h" #include "SymbolTable.h" #include "Symbols.h" -#include "Writer.h" #include "lld/Core/Parallel.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" @@ -21,6 +23,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/Endian.h" #include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/RandomNumberGenerator.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -42,6 +45,61 @@ static const int DOSStubSize = 64; static const int NumberfOfDataDirectory = 16; namespace { + +class DebugDirectoryChunk : public Chunk { +public: + DebugDirectoryChunk(const std::vector> &R) + : Records(R) {} + + size_t getSize() const override { + return Records.size() * sizeof(debug_directory); + } + + void writeTo(uint8_t *B) const override { + auto *D = reinterpret_cast(B + OutputSectionOff); + + for (const std::unique_ptr &Record : Records) { + D->Characteristics = 0; + D->TimeDateStamp = 0; + D->MajorVersion = 0; + D->MinorVersion = 0; + D->Type = COFF::IMAGE_DEBUG_TYPE_CODEVIEW; + D->SizeOfData = Record->getSize(); + D->AddressOfRawData = Record->getRVA(); + // TODO(compnerd) get the file offset + D->PointerToRawData = 0; + + ++D; + } + } + +private: + const std::vector> &Records; +}; + +class CVDebugRecordChunk : public Chunk { + size_t getSize() const override { + return sizeof(codeview::DebugInfo) + Config->PDBPath.size() + 1; + } + + void writeTo(uint8_t *B) const override { + // Save off the DebugInfo entry to backfill the file signature (build id) + // in Writer::writeBuildId + DI = reinterpret_cast(B + OutputSectionOff); + + DI->Signature.CVSignature = OMF::Signature::PDB70; + + // 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'; + } + +public: + mutable codeview::DebugInfo *DI = nullptr; +}; + // The writer writes a SymbolTable result to a file. class Writer { public: @@ -62,6 +120,7 @@ private: void setSectionPermissions(); void writeSections(); void sortExceptionTable(); + void writeBuildId(); void applyRelocations(); llvm::Optional createSymbol(Defined *D); @@ -76,9 +135,7 @@ private: std::map> binImports(); SymbolTable *Symtab; - std::unique_ptr Buffer; - llvm::SpecificBumpPtrAllocator CAlloc; - llvm::SpecificBumpPtrAllocator BAlloc; + std::unique_ptr Buffer; std::vector OutputSections; std::vector Strtab; std::vector OutputSymtab; @@ -87,6 +144,11 @@ private: EdataContents Edata; std::unique_ptr SEHTable; + std::unique_ptr DebugDirectory; + std::vector> DebugRecords; + CVDebugRecordChunk *BuildId = nullptr; + ArrayRef SectionTable; + uint64_t FileSize; uint32_t PointerToSymbolTable = 0; uint64_t SizeOfImage; @@ -239,6 +301,11 @@ void Writer::run() { fixSafeSEHSymbols(); writeSections(); sortExceptionTable(); + writeBuildId(); + + if (!Config->PDBPath.empty()) + createPDB(Config->PDBPath, Symtab, SectionTable); + if (auto EC = Buffer->commit()) fatal(EC, "failed to write the output file"); } @@ -274,7 +341,7 @@ void Writer::createSections() { StringRef Name = getOutputSection(Pair.first); OutputSection *&Sec = Sections[Name]; if (!Sec) { - Sec = new (CAlloc.Allocate()) OutputSection(Name); + Sec = make(Name); OutputSections.push_back(Sec); } std::vector &Chunks = Pair.second; @@ -286,25 +353,46 @@ void Writer::createSections() { } void Writer::createMiscChunks() { + OutputSection *RData = createSection(".rdata"); + // Create thunks for locally-dllimported symbols. if (!Symtab->LocalImportChunks.empty()) { - OutputSection *Sec = createSection(".rdata"); for (Chunk *C : Symtab->LocalImportChunks) - Sec->addChunk(C); + RData->addChunk(C); + } + + // Create Debug Information Chunks + if (Config->Debug) { + DebugDirectory = llvm::make_unique(DebugRecords); + + // TODO(compnerd) create a coffgrp entry if DebugType::CV is not enabled + if (Config->DebugTypes & static_cast(coff::DebugType::CV)) { + auto Chunk = llvm::make_unique(); + + BuildId = Chunk.get(); + DebugRecords.push_back(std::move(Chunk)); + } + + RData->addChunk(DebugDirectory.get()); + for (const std::unique_ptr &C : DebugRecords) + RData->addChunk(C.get()); } // 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) - Handlers.insert(cast(B->repl())); + Handlers.insert(cast(B)); } + SEHTable.reset(new SEHTableChunk(Handlers)); - createSection(".rdata")->addChunk(SEHTable.get()); + RData->addChunk(SEHTable.get()); } // Create .idata section for the DLL-imported symbol table. @@ -340,7 +428,7 @@ void Writer::createImportTables() { Sec->addChunk(C); } if (!DelayIdata.empty()) { - Defined *Helper = cast(Config->DelayLoadHelper->repl()); + Defined *Helper = cast(Config->DelayLoadHelper); DelayIdata.create(Helper); OutputSection *Sec = createSection(".didat"); for (Chunk *C : DelayIdata.getChunks()) @@ -383,6 +471,10 @@ size_t Writer::addEntryToStringTable(StringRef Str) { } Optional Writer::createSymbol(Defined *Def) { + // Relative symbols are unrepresentable in a COFF symbol table. + if (isa(Def)) + return None; + if (auto *D = dyn_cast(Def)) if (!D->getChunk()->isLive()) return None; @@ -409,7 +501,6 @@ Optional Writer::createSymbol(Defined *Def) { switch (Def->kind()) { case SymbolBody::DefinedAbsoluteKind: - case SymbolBody::DefinedRelativeKind: Sym.Value = Def->getRVA(); Sym.SectionNumber = IMAGE_SYM_ABSOLUTE; break; @@ -445,13 +536,11 @@ void Writer::createSymbolAndStringTable() { for (lld::coff::ObjectFile *File : Symtab->ObjectFiles) for (SymbolBody *B : File->getSymbols()) if (auto *D = dyn_cast(B)) - if (Optional Sym = createSymbol(D)) - OutputSymtab.push_back(*Sym); - - for (ImportFile *File : Symtab->ImportFiles) - for (SymbolBody *B : File->getSymbols()) - if (Optional Sym = createSymbol(cast(B))) - OutputSymtab.push_back(*Sym); + if (!D->WrittenToSymtab) { + D->WrittenToSymtab = true; + if (Optional Sym = createSymbol(D)) + OutputSymtab.push_back(*Sym); + } OutputSection *LastSection = OutputSections.back(); // We position the symbol table to be adjacent to the end of the last section. @@ -542,7 +631,7 @@ template void Writer::writeHeader() { PE->SizeOfImage = SizeOfImage; PE->SizeOfHeaders = SizeOfHeaders; if (!Config->NoEntry) { - Defined *Entry = cast(Config->Entry->repl()); + Defined *Entry = cast(Config->Entry); PE->AddressOfEntryPoint = Entry->getRVA(); // Pointer to thumb code must have the LSB set, so adjust it. if (Config->Machine == ARMNT) @@ -584,33 +673,32 @@ template void Writer::writeHeader() { Dir[IAT].RelativeVirtualAddress = Idata.getIATRVA(); Dir[IAT].Size = Idata.getIATSize(); } - if (!DelayIdata.empty()) { - Dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress = - DelayIdata.getDirRVA(); - Dir[DELAY_IMPORT_DESCRIPTOR].Size = DelayIdata.getDirSize(); - } if (OutputSection *Sec = findSection(".rsrc")) { Dir[RESOURCE_TABLE].RelativeVirtualAddress = Sec->getRVA(); Dir[RESOURCE_TABLE].Size = Sec->getVirtualSize(); } - if (OutputSection *Sec = findSection(".reloc")) { - Dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = Sec->getRVA(); - Dir[BASE_RELOCATION_TABLE].Size = Sec->getVirtualSize(); - } if (OutputSection *Sec = findSection(".pdata")) { Dir[EXCEPTION_TABLE].RelativeVirtualAddress = Sec->getRVA(); Dir[EXCEPTION_TABLE].Size = Sec->getVirtualSize(); } + if (OutputSection *Sec = findSection(".reloc")) { + Dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = Sec->getRVA(); + Dir[BASE_RELOCATION_TABLE].Size = Sec->getVirtualSize(); + } if (Symbol *Sym = Symtab->findUnderscore("_tls_used")) { - if (Defined *B = dyn_cast(Sym->Body)) { + if (Defined *B = dyn_cast(Sym->body())) { Dir[TLS_TABLE].RelativeVirtualAddress = B->getRVA(); Dir[TLS_TABLE].Size = Config->is64() ? sizeof(object::coff_tls_directory64) : sizeof(object::coff_tls_directory32); } } + if (Config->Debug) { + Dir[DEBUG_DIRECTORY].RelativeVirtualAddress = DebugDirectory->getRVA(); + Dir[DEBUG_DIRECTORY].Size = DebugDirectory->getSize(); + } if (Symbol *Sym = Symtab->findUnderscore("_load_config_used")) { - if (auto *B = dyn_cast(Sym->Body)) { + if (auto *B = dyn_cast(Sym->body())) { SectionChunk *SC = B->getChunk(); assert(B->getRVA() >= SC->getRVA()); uint64_t OffsetInChunk = B->getRVA() - SC->getRVA(); @@ -626,12 +714,19 @@ template void Writer::writeHeader() { Dir[LOAD_CONFIG_TABLE].Size = LoadConfigSize; } } + if (!DelayIdata.empty()) { + Dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress = + DelayIdata.getDirRVA(); + Dir[DELAY_IMPORT_DESCRIPTOR].Size = DelayIdata.getDirSize(); + } // Write section table for (OutputSection *Sec : OutputSections) { Sec->writeHeaderTo(Buf); Buf += sizeof(coff_section); } + SectionTable = ArrayRef( + Buf - OutputSections.size() * sizeof(coff_section), Buf); if (OutputSymtab.empty()) return; @@ -660,8 +755,10 @@ void Writer::openFile(StringRef Path) { void Writer::fixSafeSEHSymbols() { if (!SEHTable) return; - Config->SEHTable->setRVA(SEHTable->getRVA()); - Config->SEHCount->setVA(SEHTable->getSize() / 4); + if (auto *T = dyn_cast(Config->SEHTable->body())) + T->setRVA(SEHTable->getRVA()); + if (auto *C = dyn_cast(Config->SEHCount->body())) + C->setVA(SEHTable->getSize() / 4); } // Handles /section options to allow users to overwrite @@ -715,6 +812,30 @@ void Writer::sortExceptionTable() { errs() << "warning: don't know how to handle .pdata.\n"; } +// Backfill the CVSignature in a PDB70 Debug Record. This backfilling allows us +// to get reproducible builds. +void Writer::writeBuildId() { + // There is nothing to backfill if BuildId was not setup. + if (BuildId == nullptr) + return; + + MD5 Hash; + MD5::MD5Result Res; + + Hash.update(ArrayRef{Buffer->getBufferStart(), + Buffer->getBufferEnd()}); + Hash.final(Res); + + assert(BuildId->DI->Signature.CVSignature == OMF::Signature::PDB70 && + "only PDB 7.0 is supported"); + assert(sizeof(Res) == sizeof(BuildId->DI->PDB70.Signature) && + "signature size mismatch"); + memcpy(BuildId->DI->PDB70.Signature, Res, + sizeof(codeview::PDB70DebugInfo::Signature)); + // TODO(compnerd) track the Age + BuildId->DI->PDB70.Age = 1; +} + OutputSection *Writer::findSection(StringRef Name) { for (OutputSection *Sec : OutputSections) if (Sec->getName() == Name) @@ -744,16 +865,13 @@ OutputSection *Writer::createSection(StringRef Name) { uint32_t Perms = StringSwitch(Name) .Case(".bss", BSS | R | W) .Case(".data", DATA | R | W) - .Case(".didat", DATA | R) - .Case(".edata", DATA | R) - .Case(".idata", DATA | R) - .Case(".rdata", DATA | R) + .Cases(".didat", ".edata", ".idata", ".rdata", DATA | R) .Case(".reloc", DATA | DISCARDABLE | R) .Case(".text", CODE | R | X) .Default(0); if (!Perms) llvm_unreachable("unknown section name"); - auto Sec = new (CAlloc.Allocate()) OutputSection(Name); + auto Sec = make(Name); Sec->addPermissions(Perms); OutputSections.push_back(Sec); return Sec; @@ -784,13 +902,11 @@ void Writer::addBaserelBlocks(OutputSection *Dest, std::vector &V) { uint32_t P = V[J].RVA & Mask; if (P == Page) continue; - BaserelChunk *Buf = BAlloc.Allocate(); - Dest->addChunk(new (Buf) BaserelChunk(Page, &V[I], &V[0] + J)); + Dest->addChunk(make(Page, &V[I], &V[0] + J)); I = J; Page = P; } if (I == J) return; - BaserelChunk *Buf = BAlloc.Allocate(); - Dest->addChunk(new (Buf) BaserelChunk(Page, &V[I], &V[0] + J)); + Dest->addChunk(make(Page, &V[I], &V[0] + J)); } diff --git a/COFF/Writer.h b/COFF/Writer.h index 0473315ae50ab..0d26090177d8c 100644 --- a/COFF/Writer.h +++ b/COFF/Writer.h @@ -14,9 +14,7 @@ namespace lld { namespace coff { - -class Chunk; -class OutputSection; +class SymbolTable; void writeResult(SymbolTable *T); diff --git a/ELF/CMakeLists.txt b/ELF/CMakeLists.txt index a1b65adc7400b..2e9d2b941fd9b 100644 --- a/ELF/CMakeLists.txt +++ b/ELF/CMakeLists.txt @@ -2,24 +2,30 @@ set(LLVM_TARGET_DEFINITIONS Options.td) tablegen(LLVM Options.inc -gen-opt-parser-defs) add_public_tablegen_target(ELFOptionsTableGen) +if(NOT LLD_BUILT_STANDALONE) + set(tablegen_deps intrinsics_gen) +endif() + add_lld_library(lldELF Driver.cpp DriverUtils.cpp EhFrame.cpp Error.cpp + GdbIndex.cpp ICF.cpp InputFiles.cpp InputSection.cpp LTO.cpp LinkerScript.cpp MarkLive.cpp + Mips.cpp OutputSections.cpp Relocations.cpp ScriptParser.cpp Strings.cpp - SymbolListFile.cpp SymbolTable.cpp Symbols.cpp + SyntheticSections.cpp Target.cpp Thunks.cpp Writer.cpp @@ -31,6 +37,8 @@ add_lld_library(lldELF BitWriter Codegen Core + DebugInfoDWARF + Demangle IPO Linker LTO @@ -44,7 +52,10 @@ add_lld_library(lldELF LINK_LIBS lldConfig + lldCore ${PTHREAD_LIB} - ) -add_dependencies(lldELF intrinsics_gen ELFOptionsTableGen) + DEPENDS + ELFOptionsTableGen + ${tablegen_deps} + ) diff --git a/ELF/Config.h b/ELF/Config.h index 2ccd95e88775d..b828cdb250479 100644 --- a/ELF/Config.h +++ b/ELF/Config.h @@ -12,6 +12,7 @@ #include "llvm/ADT/MapVector.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" #include "llvm/Support/ELF.h" #include @@ -30,21 +31,36 @@ enum ELFKind { ELF64BEKind }; -enum class BuildIdKind { None, Fnv1, Md5, Sha1, Hexstring }; +// For --build-id. +enum class BuildIdKind { None, Fast, Md5, Sha1, Hexstring, Uuid }; -enum class UnresolvedPolicy { NoUndef, Error, Warn, Ignore }; +// For --discard-{all,locals,none} and --retain-symbols-file. +enum class DiscardPolicy { Default, All, Locals, RetainFile, None }; + +// For --strip-{all,debug}. +enum class StripPolicy { None, All, Debug }; + +// For --unresolved-symbols. +enum class UnresolvedPolicy { NoUndef, ReportError, Warn, Ignore }; + +// For --sort-section and linkerscript sorting rules. +enum class SortSectionPolicy { Default, None, Alignment, Name, Priority }; + +// For --target2 +enum class Target2Policy { Abs, Rel, GotRel }; struct SymbolVersion { llvm::StringRef Name; bool IsExternCpp; + bool HasWildcard; }; // This struct contains symbols version definition that // can be found in version script if it is used for link. struct VersionDefinition { - VersionDefinition(llvm::StringRef Name, size_t Id) : Name(Name), Id(Id) {} + VersionDefinition(llvm::StringRef Name, uint16_t Id) : Name(Name), Id(Id) {} llvm::StringRef Name; - size_t Id; + uint16_t Id; std::vector Globals; size_t NameOff; // Offset in string table. }; @@ -54,75 +70,92 @@ struct VersionDefinition { // and such fields have the same name as the corresponding options. // Most fields are initialized by the driver. struct Configuration { - Symbol *EntrySym = nullptr; InputFile *FirstElf = nullptr; + uint8_t OSABI = 0; + llvm::StringMap SectionStartMap; llvm::StringRef DynamicLinker; llvm::StringRef Entry; llvm::StringRef Emulation; llvm::StringRef Fini; llvm::StringRef Init; - llvm::StringRef LtoAAPipeline; - llvm::StringRef LtoNewPmPasses; + llvm::StringRef LTOAAPipeline; + llvm::StringRef LTONewPmPasses; llvm::StringRef OutputFile; llvm::StringRef SoName; llvm::StringRef Sysroot; + llvm::StringSet<> RetainSymbolsFile; std::string RPath; std::vector VersionDefinitions; - std::vector DynamicList; + std::vector AuxiliaryList; std::vector SearchPaths; + std::vector SymbolOrderingFile; std::vector Undefined; std::vector VersionScriptGlobals; + std::vector VersionScriptLocals; std::vector BuildIdVector; bool AllowMultipleDefinition; bool AsNeeded = false; bool Bsymbolic; bool BsymbolicFunctions; + bool ColorDiagnostics = false; bool Demangle = true; bool DisableVerify; - bool DiscardAll; - bool DiscardLocals; - bool DiscardNone; bool EhFrameHdr; bool EnableNewDtags; bool ExportDynamic; bool FatalWarnings; bool GcSections; + bool GdbIndex; bool GnuHash = false; bool ICF; bool Mips64EL = false; + bool MipsN32Abi = false; bool NoGnuUnique; bool NoUndefinedVersion; + bool Nostdlib; + bool OFormatBinary; + bool OMagic; bool Pic; bool Pie; bool PrintGcSections; bool Rela; bool Relocatable; bool SaveTemps; + bool SingleRoRx; bool Shared; bool Static = false; - bool StripAll; - bool StripDebug; bool SysvHash = true; + bool Target1Rel; bool Threads; bool Trace; bool Verbose; bool WarnCommon; + bool WarnMissingEntry; bool ZCombreloc; - bool ZExecStack; + bool ZExecstack; bool ZNodelete; bool ZNow; bool ZOrigin; bool ZRelro; + bool ExitEarly; + bool ZWxneeded; + DiscardPolicy Discard; + SortSectionPolicy SortSection; + StripPolicy Strip = StripPolicy::None; UnresolvedPolicy UnresolvedSymbols; + Target2Policy Target2 = Target2Policy::GotRel; BuildIdKind BuildId = BuildIdKind::None; ELFKind EKind = ELFNoneKind; uint16_t DefaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL; uint16_t EMachine = llvm::ELF::EM_NONE; - uint64_t EntryAddr = -1; + uint64_t ErrorLimit = 20; uint64_t ImageBase; - unsigned LtoJobs; - unsigned LtoO; + uint64_t MaxPageSize; + uint64_t ZStackSize; + unsigned LTOPartitions; + unsigned LTOO; unsigned Optimize; + unsigned ThinLTOJobs; }; // The only instance of Configuration struct. diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp index c6ca2639236ff..a11dbc7cc47fd 100644 --- a/ELF/Driver.cpp +++ b/ELF/Driver.cpp @@ -14,14 +14,17 @@ #include "InputFiles.h" #include "InputSection.h" #include "LinkerScript.h" +#include "Memory.h" #include "Strings.h" -#include "SymbolListFile.h" #include "SymbolTable.h" #include "Target.h" +#include "Threads.h" #include "Writer.h" +#include "lld/Config/Version.h" #include "lld/Driver/Driver.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/raw_ostream.h" #include @@ -38,48 +41,59 @@ using namespace lld::elf; Configuration *elf::Config; LinkerDriver *elf::Driver; -bool elf::link(ArrayRef Args, raw_ostream &Error) { - HasError = false; +BumpPtrAllocator elf::BAlloc; +StringSaver elf::Saver{BAlloc}; +std::vector elf::SpecificAllocBase::Instances; + +bool elf::link(ArrayRef Args, bool CanExitEarly, + raw_ostream &Error) { + ErrorCount = 0; ErrorOS = &Error; + Argv0 = Args[0]; - Configuration C; - LinkerDriver D; - ScriptConfiguration SC; - Config = &C; - Driver = &D; - ScriptConfig = &SC; + Config = make(); + Driver = make(); + ScriptConfig = make(); - Driver->main(Args); - return !HasError; + Driver->main(Args, CanExitEarly); + freeArena(); + return !ErrorCount; } // Parses a linker -m option. -static std::pair parseEmulation(StringRef S) { - if (S.endswith("_fbsd")) +static std::tuple parseEmulation(StringRef Emul) { + uint8_t OSABI = 0; + StringRef S = Emul; + if (S.endswith("_fbsd")) { S = S.drop_back(5); + OSABI = ELFOSABI_FREEBSD; + } std::pair Ret = StringSwitch>(S) - .Case("aarch64linux", {ELF64LEKind, EM_AARCH64}) + .Cases("aarch64elf", "aarch64linux", {ELF64LEKind, EM_AARCH64}) .Case("armelf_linux_eabi", {ELF32LEKind, EM_ARM}) .Case("elf32_x86_64", {ELF32LEKind, EM_X86_64}) .Case("elf32btsmip", {ELF32BEKind, EM_MIPS}) .Case("elf32ltsmip", {ELF32LEKind, EM_MIPS}) + .Case("elf32btsmipn32", {ELF32BEKind, EM_MIPS}) + .Case("elf32ltsmipn32", {ELF32LEKind, EM_MIPS}) .Case("elf32ppc", {ELF32BEKind, EM_PPC}) .Case("elf64btsmip", {ELF64BEKind, EM_MIPS}) .Case("elf64ltsmip", {ELF64LEKind, EM_MIPS}) .Case("elf64ppc", {ELF64BEKind, EM_PPC64}) + .Cases("elf_amd64", "elf_x86_64", {ELF64LEKind, EM_X86_64}) .Case("elf_i386", {ELF32LEKind, EM_386}) - .Case("elf_x86_64", {ELF64LEKind, EM_X86_64}) + .Case("elf_iamcu", {ELF32LEKind, EM_IAMCU}) .Default({ELFNoneKind, EM_NONE}); if (Ret.first == ELFNoneKind) { if (S == "i386pe" || S == "i386pep" || S == "thumb2pe") - error("Windows targets are not supported on the ELF frontend: " + S); + error("Windows targets are not supported on the ELF frontend: " + Emul); else - error("unknown emulation: " + S); + error("unknown emulation: " + Emul); } - return Ret; + return std::make_tuple(Ret.first, Ret.second, OSABI); } // Returns slices of MB by parsing MB as an archive file. @@ -87,25 +101,28 @@ static std::pair parseEmulation(StringRef S) { std::vector LinkerDriver::getArchiveMembers(MemoryBufferRef MB) { std::unique_ptr File = - check(Archive::create(MB), "failed to parse archive"); + check(Archive::create(MB), + MB.getBufferIdentifier() + ": failed to parse archive"); std::vector V; - Error Err; + Error Err = Error::success(); for (const ErrorOr &COrErr : File->children(Err)) { - Archive::Child C = check(COrErr, "could not get the child of the archive " + - File->getFileName()); + Archive::Child C = + check(COrErr, MB.getBufferIdentifier() + + ": could not get the child of the archive"); MemoryBufferRef MBRef = check(C.getMemoryBufferRef(), - "could not get the buffer for a child of the archive " + - File->getFileName()); + MB.getBufferIdentifier() + + ": could not get the buffer for a child of the archive"); V.push_back(MBRef); } if (Err) - Error(Err); + fatal(MB.getBufferIdentifier() + ": Archive::children failed: " + + toString(std::move(Err))); // Take ownership of memory buffers created for members of thin archives. for (std::unique_ptr &MB : File->takeThinBuffers()) - OwningMBs.push_back(std::move(MB)); + make>(std::move(MB)); return V; } @@ -114,25 +131,28 @@ LinkerDriver::getArchiveMembers(MemoryBufferRef MB) { // Newly created memory buffers are owned by this driver. void LinkerDriver::addFile(StringRef Path) { using namespace sys::fs; - if (Config->Verbose) - outs() << Path << "\n"; Optional Buffer = readFile(Path); if (!Buffer.hasValue()) return; MemoryBufferRef MBRef = *Buffer; + if (InBinary) { + Files.push_back(make(MBRef)); + return; + } + switch (identify_magic(MBRef.getBuffer())) { case file_magic::unknown: readLinkerScript(MBRef); return; case file_magic::archive: - if (WholeArchive) { + if (InWholeArchive) { for (MemoryBufferRef MB : getArchiveMembers(MBRef)) Files.push_back(createObjectFile(MB, Path)); return; } - Files.push_back(make_unique(MBRef)); + Files.push_back(make(MBRef)); return; case file_magic::elf_shared_object: if (Config->Relocatable) { @@ -143,13 +163,16 @@ void LinkerDriver::addFile(StringRef Path) { return; default: if (InLib) - Files.push_back(make_unique(MBRef)); + Files.push_back(make(MBRef)); else Files.push_back(createObjectFile(MBRef)); } } Optional LinkerDriver::readFile(StringRef Path) { + if (Config->Verbose) + outs() << Path << "\n"; + auto MBOrErr = MemoryBuffer::getFile(Path); if (auto EC = MBOrErr.getError()) { error(EC, "cannot open " + Path); @@ -157,7 +180,7 @@ Optional LinkerDriver::readFile(StringRef Path) { } std::unique_ptr &MB = *MBOrErr; MemoryBufferRef MBRef = MB->getMemBufferRef(); - OwningMBs.push_back(std::move(MB)); // take MB ownership + make>(std::move(MB)); // take MB ownership if (Cpio) Cpio->append(relativeToRoot(Path), MBRef.getBuffer()); @@ -167,11 +190,10 @@ Optional LinkerDriver::readFile(StringRef Path) { // Add a given library by searching it from input search paths. void LinkerDriver::addLibrary(StringRef Name) { - std::string Path = searchLibrary(Name); - if (Path.empty()) - error("unable to find library -l" + Name); + if (Optional Path = searchLibrary(Name)) + addFile(*Path); else - addFile(Path); + error("unable to find library -l" + Name); } // This function is called on startup. We need this for LTO since @@ -184,12 +206,6 @@ static void initLLVM(opt::InputArgList &Args) { InitializeAllAsmPrinters(); InitializeAllAsmParsers(); - // This is a flag to discard all but GlobalValue names. - // We want to enable it by default because it saves memory. - // Disable it only when a developer option (-save-temps) is given. - Driver->Context.setDiscardValueNames(!Config->SaveTemps); - Driver->Context.enableDebugTypeODRUniquing(); - // Parse and evaluate -mllvm options. std::vector V; V.push_back("lld (LLVM option parsing)"); @@ -206,9 +222,6 @@ static void checkOptions(opt::InputArgList &Args) { if (Config->EMachine == EM_MIPS && Config->GnuHash) error("the .gnu.hash section is not compatible with the MIPS target."); - if (Config->EMachine == EM_AMDGPU && !Config->Entry.empty()) - error("-e option is not valid for AMDGPU."); - if (Config->Pie && Config->Shared) error("-shared and -pie may not be used together"); @@ -224,8 +237,8 @@ static void checkOptions(opt::InputArgList &Args) { } } -static StringRef -getString(opt::InputArgList &Args, unsigned Key, StringRef Default = "") { +static StringRef getString(opt::InputArgList &Args, unsigned Key, + StringRef Default = "") { if (auto *Arg = Args.getLastArg(Key)) return Arg->getValue(); return Default; @@ -254,33 +267,64 @@ static bool hasZOption(opt::InputArgList &Args, StringRef Key) { return false; } -void LinkerDriver::main(ArrayRef ArgsArr) { +static uint64_t getZOptionValue(opt::InputArgList &Args, StringRef Key, + uint64_t Default) { + for (auto *Arg : Args.filtered(OPT_z)) { + StringRef Value = Arg->getValue(); + size_t Pos = Value.find("="); + if (Pos != StringRef::npos && Key == Value.substr(0, Pos)) { + Value = Value.substr(Pos + 1); + uint64_t Result; + if (Value.getAsInteger(0, Result)) + error("invalid " + Key + ": " + Value); + return Result; + } + } + return Default; +} + +void LinkerDriver::main(ArrayRef ArgsArr, bool CanExitEarly) { ELFOptTable Parser; opt::InputArgList Args = Parser.parse(ArgsArr.slice(1)); + + // Interpret this flag early because error() depends on them. + Config->ErrorLimit = getInteger(Args, OPT_error_limit, 20); + + // Handle -help if (Args.hasArg(OPT_help)) { printHelp(ArgsArr[0]); return; } - if (Args.hasArg(OPT_version)) { - outs() << getVersionString(); + + // GNU linkers disagree here. Though both -version and -v are mentioned + // in help to print the version information, GNU ld just normally exits, + // while gold can continue linking. We are compatible with ld.bfd here. + if (Args.hasArg(OPT_version) || Args.hasArg(OPT_v)) + outs() << getLLDVersion() << "\n"; + if (Args.hasArg(OPT_version)) return; - } + + Config->ExitEarly = CanExitEarly && !Args.hasArg(OPT_full_shutdown); if (const char *Path = getReproduceOption(Args)) { // Note that --reproduce is a debug option so you can ignore it // if you are trying to understand the whole picture of the code. - Cpio.reset(CpioFile::create(Path)); - if (Cpio) { + ErrorOr F = CpioFile::create(Path); + if (F) { + Cpio.reset(*F); Cpio->append("response.txt", createResponseFile(Args)); - Cpio->append("version.txt", getVersionString()); - } + Cpio->append("version.txt", getLLDVersion() + "\n"); + } else + error(F.getError(), + Twine("--reproduce: failed to open ") + Path + ".cpio"); } readConfigs(Args); initLLVM(Args); createFiles(Args); + inferMachineType(); checkOptions(Args); - if (HasError) + if (ErrorCount) return; switch (Config->EKind) { @@ -297,7 +341,7 @@ void LinkerDriver::main(ArrayRef ArgsArr) { link(Args); return; default: - error("-m or at least a .o file required"); + llvm_unreachable("unknown Config->EKind"); } } @@ -314,10 +358,115 @@ static UnresolvedPolicy getUnresolvedSymbolOption(opt::InputArgList &Args) { if (S == "ignore-all" || S == "ignore-in-object-files") return UnresolvedPolicy::Ignore; if (S == "ignore-in-shared-libs" || S == "report-all") - return UnresolvedPolicy::Error; + return UnresolvedPolicy::ReportError; error("unknown --unresolved-symbols value: " + S); } - return UnresolvedPolicy::Error; + return UnresolvedPolicy::ReportError; +} + +static Target2Policy getTarget2Option(opt::InputArgList &Args) { + if (auto *Arg = Args.getLastArg(OPT_target2)) { + StringRef S = Arg->getValue(); + if (S == "rel") + return Target2Policy::Rel; + if (S == "abs") + return Target2Policy::Abs; + if (S == "got-rel") + return Target2Policy::GotRel; + error("unknown --target2 option: " + S); + } + return Target2Policy::GotRel; +} + +static bool isOutputFormatBinary(opt::InputArgList &Args) { + if (auto *Arg = Args.getLastArg(OPT_oformat)) { + StringRef S = Arg->getValue(); + if (S == "binary") + return true; + error("unknown --oformat value: " + S); + } + return false; +} + +static bool getArg(opt::InputArgList &Args, unsigned K1, unsigned K2, + bool Default) { + if (auto *Arg = Args.getLastArg(K1, K2)) + return Arg->getOption().getID() == K1; + return Default; +} + +static DiscardPolicy getDiscardOption(opt::InputArgList &Args) { + if (Config->Relocatable) + return DiscardPolicy::None; + auto *Arg = + Args.getLastArg(OPT_discard_all, OPT_discard_locals, OPT_discard_none); + if (!Arg) + return DiscardPolicy::Default; + if (Arg->getOption().getID() == OPT_discard_all) + return DiscardPolicy::All; + if (Arg->getOption().getID() == OPT_discard_locals) + return DiscardPolicy::Locals; + return DiscardPolicy::None; +} + +static StripPolicy getStripOption(opt::InputArgList &Args) { + if (auto *Arg = Args.getLastArg(OPT_strip_all, OPT_strip_debug)) { + if (Arg->getOption().getID() == OPT_strip_all) + return StripPolicy::All; + return StripPolicy::Debug; + } + return StripPolicy::None; +} + +static uint64_t parseSectionAddress(StringRef S, opt::Arg *Arg) { + uint64_t VA = 0; + if (S.startswith("0x")) + S = S.drop_front(2); + if (S.getAsInteger(16, VA)) + error("invalid argument: " + stringize(Arg)); + return VA; +} + +static StringMap getSectionStartMap(opt::InputArgList &Args) { + StringMap Ret; + for (auto *Arg : Args.filtered(OPT_section_start)) { + StringRef Name; + StringRef Addr; + std::tie(Name, Addr) = StringRef(Arg->getValue()).split('='); + Ret[Name] = parseSectionAddress(Addr, Arg); + } + + if (auto *Arg = Args.getLastArg(OPT_Ttext)) + Ret[".text"] = parseSectionAddress(Arg->getValue(), Arg); + if (auto *Arg = Args.getLastArg(OPT_Tdata)) + Ret[".data"] = parseSectionAddress(Arg->getValue(), Arg); + if (auto *Arg = Args.getLastArg(OPT_Tbss)) + Ret[".bss"] = parseSectionAddress(Arg->getValue(), Arg); + return Ret; +} + +static SortSectionPolicy getSortKind(opt::InputArgList &Args) { + StringRef S = getString(Args, OPT_sort_section); + if (S == "alignment") + return SortSectionPolicy::Alignment; + if (S == "name") + return SortSectionPolicy::Name; + if (!S.empty()) + error("unknown --sort-section rule: " + S); + return SortSectionPolicy::Default; +} + +static std::vector getLines(MemoryBufferRef MB) { + SmallVector Arr; + MB.getBuffer().split(Arr, '\n'); + + std::vector Ret; + for (StringRef S : Arr) { + S = S.trim(); + if (!S.empty()) + Ret.push_back(S); + } + return Ret; } // Initializes Config members by the command line options. @@ -334,34 +483,37 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) { if (auto *Arg = Args.getLastArg(OPT_m)) { // Parse ELF{32,64}{LE,BE} and CPU type. StringRef S = Arg->getValue(); - std::tie(Config->EKind, Config->EMachine) = parseEmulation(S); + std::tie(Config->EKind, Config->EMachine, Config->OSABI) = + parseEmulation(S); + Config->MipsN32Abi = (S == "elf32btsmipn32" || S == "elf32ltsmipn32"); Config->Emulation = S; } Config->AllowMultipleDefinition = Args.hasArg(OPT_allow_multiple_definition); Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic); Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions); - Config->Demangle = !Args.hasArg(OPT_no_demangle); + Config->Demangle = getArg(Args, OPT_demangle, OPT_no_demangle, true); Config->DisableVerify = Args.hasArg(OPT_disable_verify); - Config->DiscardAll = Args.hasArg(OPT_discard_all); - Config->DiscardLocals = Args.hasArg(OPT_discard_locals); - Config->DiscardNone = Args.hasArg(OPT_discard_none); Config->EhFrameHdr = Args.hasArg(OPT_eh_frame_hdr); Config->EnableNewDtags = !Args.hasArg(OPT_disable_new_dtags); Config->ExportDynamic = Args.hasArg(OPT_export_dynamic); Config->FatalWarnings = Args.hasArg(OPT_fatal_warnings); - Config->GcSections = Args.hasArg(OPT_gc_sections); + Config->GcSections = getArg(Args, OPT_gc_sections, OPT_no_gc_sections, false); + Config->GdbIndex = Args.hasArg(OPT_gdb_index); Config->ICF = Args.hasArg(OPT_icf); Config->NoGnuUnique = Args.hasArg(OPT_no_gnu_unique); Config->NoUndefinedVersion = Args.hasArg(OPT_no_undefined_version); - Config->Pie = Args.hasArg(OPT_pie); + Config->Nostdlib = Args.hasArg(OPT_nostdlib); + Config->OMagic = Args.hasArg(OPT_omagic); + Config->Pie = getArg(Args, OPT_pie, OPT_nopie, false); Config->PrintGcSections = Args.hasArg(OPT_print_gc_sections); Config->Relocatable = Args.hasArg(OPT_relocatable); + Config->Discard = getDiscardOption(Args); Config->SaveTemps = Args.hasArg(OPT_save_temps); + Config->SingleRoRx = Args.hasArg(OPT_no_rosegment); Config->Shared = Args.hasArg(OPT_shared); - Config->StripAll = Args.hasArg(OPT_strip_all); - Config->StripDebug = Args.hasArg(OPT_strip_debug); - Config->Threads = Args.hasArg(OPT_threads); + Config->Target1Rel = getArg(Args, OPT_target1_rel, OPT_target1_abs, false); + Config->Threads = getArg(Args, OPT_threads, OPT_no_threads, true); Config->Trace = Args.hasArg(OPT_trace); Config->Verbose = Args.hasArg(OPT_verbose); Config->WarnCommon = Args.hasArg(OPT_warn_common); @@ -370,33 +522,47 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) { Config->Entry = getString(Args, OPT_entry); Config->Fini = getString(Args, OPT_fini, "_fini"); Config->Init = getString(Args, OPT_init, "_init"); - Config->LtoAAPipeline = getString(Args, OPT_lto_aa_pipeline); - Config->LtoNewPmPasses = getString(Args, OPT_lto_newpm_passes); + Config->LTOAAPipeline = getString(Args, OPT_lto_aa_pipeline); + Config->LTONewPmPasses = getString(Args, OPT_lto_newpm_passes); Config->OutputFile = getString(Args, OPT_o); Config->SoName = getString(Args, OPT_soname); Config->Sysroot = getString(Args, OPT_sysroot); Config->Optimize = getInteger(Args, OPT_O, 1); - Config->LtoO = getInteger(Args, OPT_lto_O, 2); - if (Config->LtoO > 3) + Config->LTOO = getInteger(Args, OPT_lto_O, 2); + if (Config->LTOO > 3) error("invalid optimization level for LTO: " + getString(Args, OPT_lto_O)); - Config->LtoJobs = getInteger(Args, OPT_lto_jobs, 1); - if (Config->LtoJobs == 0) - error("number of threads must be > 0"); + Config->LTOPartitions = getInteger(Args, OPT_lto_partitions, 1); + if (Config->LTOPartitions == 0) + error("--lto-partitions: number of threads must be > 0"); + Config->ThinLTOJobs = getInteger(Args, OPT_thinlto_jobs, -1u); + if (Config->ThinLTOJobs == 0) + error("--thinlto-jobs: number of threads must be > 0"); Config->ZCombreloc = !hasZOption(Args, "nocombreloc"); - Config->ZExecStack = hasZOption(Args, "execstack"); + Config->ZExecstack = hasZOption(Args, "execstack"); Config->ZNodelete = hasZOption(Args, "nodelete"); Config->ZNow = hasZOption(Args, "now"); Config->ZOrigin = hasZOption(Args, "origin"); Config->ZRelro = !hasZOption(Args, "norelro"); + Config->ZStackSize = getZOptionValue(Args, "stack-size", -1); + Config->ZWxneeded = hasZOption(Args, "wxneeded"); - if (Config->Relocatable) - Config->StripAll = false; + Config->OFormatBinary = isOutputFormatBinary(Args); + Config->SectionStartMap = getSectionStartMap(Args); + Config->SortSection = getSortKind(Args); + Config->Target2 = getTarget2Option(Args); + Config->UnresolvedSymbols = getUnresolvedSymbolOption(Args); + + // --omagic is an option to create old-fashioned executables in which + // .text segments are writable. Today, the option is still in use to + // create special-purpose programs such as boot loaders. It doesn't + // make sense to create PT_GNU_RELRO for such executables. + if (Config->OMagic) + Config->ZRelro = false; - // --strip-all implies --strip-debug. - if (Config->StripAll) - Config->StripDebug = true; + if (!Config->Relocatable) + Config->Strip = getStripOption(Args); // Config->Pic is true if we are generating position-independent code. Config->Pic = Config->Pie || Config->Shared; @@ -414,13 +580,15 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) { // Parse --build-id or --build-id=