From d2d3ebb81992e107edf95c1c136d7a342d9b1418 Mon Sep 17 00:00:00 2001 From: Dimitry Andric Date: Sun, 16 Apr 2017 16:03:39 +0000 Subject: Vendor import of lld trunk r300422: https://llvm.org/svn/llvm-project/lld/trunk@300422 --- CMakeLists.txt | 68 +- COFF/CMakeLists.txt | 5 +- COFF/Chunks.cpp | 9 +- COFF/Chunks.h | 4 +- COFF/Config.h | 14 +- COFF/Driver.cpp | 287 ++- COFF/Driver.h | 4 + COFF/DriverUtils.cpp | 71 +- COFF/Error.cpp | 74 +- COFF/Error.h | 10 +- COFF/ICF.cpp | 9 +- COFF/InputFiles.cpp | 94 +- COFF/InputFiles.h | 11 +- COFF/LTO.cpp | 140 ++ COFF/LTO.h | 56 + COFF/Librarian.cpp | 13 +- COFF/MapFile.cpp | 114 ++ COFF/MapFile.h | 22 + COFF/ModuleDef.cpp | 22 +- COFF/Options.td | 8 + COFF/PDB.cpp | 109 +- COFF/SymbolTable.cpp | 173 +- COFF/SymbolTable.h | 17 +- COFF/Symbols.cpp | 9 +- COFF/Symbols.h | 78 +- COFF/Writer.cpp | 61 +- COFF/Writer.h | 51 + ELF/CMakeLists.txt | 5 +- ELF/Config.h | 90 +- ELF/Driver.cpp | 419 +++-- ELF/Driver.h | 2 +- ELF/DriverUtils.cpp | 28 +- ELF/EhFrame.cpp | 30 +- ELF/EhFrame.h | 5 +- ELF/Error.cpp | 48 +- ELF/Error.h | 15 +- ELF/Filesystem.cpp | 79 + ELF/Filesystem.h | 22 + ELF/GdbIndex.cpp | 208 +-- ELF/GdbIndex.h | 51 +- ELF/ICF.cpp | 100 +- ELF/InputFiles.cpp | 373 ++-- ELF/InputFiles.h | 55 +- ELF/InputSection.cpp | 755 ++++---- ELF/InputSection.h | 275 +-- ELF/LTO.cpp | 63 +- ELF/LTO.h | 3 +- ELF/LinkerScript.cpp | 1919 +++++--------------- ELF/LinkerScript.h | 217 ++- ELF/MapFile.cpp | 131 ++ ELF/MapFile.h | 22 + ELF/MarkLive.cpp | 122 +- ELF/Options.td | 43 +- ELF/OutputSections.cpp | 626 ++----- ELF/OutputSections.h | 231 +-- ELF/Relocations.cpp | 1036 +++++++---- ELF/Relocations.h | 58 +- ELF/ScriptLexer.cpp | 285 +++ ELF/ScriptLexer.h | 56 + ELF/ScriptParser.cpp | 1235 +++++++++++-- ELF/ScriptParser.h | 38 +- ELF/Strings.cpp | 4 +- ELF/SymbolTable.cpp | 147 +- ELF/SymbolTable.h | 33 +- ELF/Symbols.cpp | 260 ++- ELF/Symbols.h | 212 +-- ELF/SyntheticSections.cpp | 1613 +++++++++------- ELF/SyntheticSections.h | 464 ++--- ELF/Target.cpp | 437 +++-- ELF/Target.h | 24 +- ELF/Threads.h | 9 +- ELF/Thunks.cpp | 208 ++- ELF/Thunks.h | 39 +- ELF/Writer.cpp | 1194 ++++++------ ELF/Writer.h | 20 +- docs/AtomLLD.rst | 5 +- docs/C++11.rst | 9 - docs/Driver.rst | 3 + docs/NewLLD.rst | 46 +- docs/Readers.rst | 3 + docs/ReleaseNotes.rst | 96 +- docs/conf.py | 6 +- docs/design.rst | 3 + docs/development.rst | 9 +- docs/index.rst | 184 +- include/lld/Core/Parallel.h | 3 +- include/lld/Core/TargetOptionsCommandFlags.h | 20 + include/lld/Driver/Driver.h | 3 +- lib/Core/CMakeLists.txt | 9 + lib/Core/TargetOptionsCommandFlags.cpp | 32 + lib/ReaderWriter/MachO/ArchHandler_arm64.cpp | 4 - lib/ReaderWriter/MachO/ArchHandler_x86.cpp | 9 +- lib/ReaderWriter/MachO/CMakeLists.txt | 2 +- test/CMakeLists.txt | 35 +- test/COFF/Inputs/bar.ll | 6 + test/COFF/Inputs/msvclto.s | 3 + test/COFF/Inputs/thinlto-mangled-qux.ll | 28 + test/COFF/def-name.test | 28 + test/COFF/driver-windows.test | 3 + test/COFF/error-limit.test | 29 + test/COFF/export32.test | 10 + test/COFF/hello32.test | 5 +- test/COFF/include-lto.ll | 24 + test/COFF/invalid-debug-type.test | 5 + test/COFF/lldmap.test | 9 +- test/COFF/lto-comdat.ll | 69 +- test/COFF/lto-debug-pass-arguments.ll | 16 + test/COFF/lto-parallel.ll | 8 +- test/COFF/msvclto-archive.ll | 39 + test/COFF/msvclto.ll | 18 + test/COFF/nopdb.test | 14 + test/COFF/pdb-none.test | 13 + test/COFF/pdb.test | 222 +-- test/COFF/rsds.test | 25 +- test/COFF/savetemps.ll | 28 + test/COFF/thinlto-archives.ll | 22 + test/COFF/thinlto-mangled.ll | 16 + test/COFF/thinlto.ll | 18 + test/COFF/weak-external.test | 5 +- test/COFF/weak-external3.test | 10 +- test/ELF/Inputs/i386-got32x-baseless.elf | Bin 0 -> 628 bytes test/ELF/Inputs/i386-reloc-16-error.s | 3 + test/ELF/Inputs/i386-reloc-16.s | 3 + test/ELF/Inputs/i386-reloc-8-error.s | 3 + test/ELF/Inputs/i386-reloc-8.s | 3 + test/ELF/Inputs/icf-absolute.s | 3 + test/ELF/Inputs/map-file2.s | 8 + test/ELF/Inputs/map-file3.s | 2 + test/ELF/Inputs/map-file4.s | 3 + test/ELF/Inputs/relocatable-non-alloc.s | 6 + test/ELF/Inputs/relocation-copy-align-common.s | 7 + test/ELF/Inputs/resolution-end.s | 1 + test/ELF/Inputs/x86-64-reloc-16-error.s | 3 + test/ELF/Inputs/x86-64-reloc-16.s | 3 + test/ELF/Inputs/x86-64-reloc-8-error.s | 3 + test/ELF/Inputs/x86-64-reloc-8.s | 3 + test/ELF/Inputs/ztext-text-notext.s | 10 + test/ELF/aarch64-fpic-abs16.s | 4 +- test/ELF/aarch64-fpic-add_abs_lo12_nc.s | 4 +- test/ELF/aarch64-fpic-adr_prel_lo21.s | 4 +- test/ELF/aarch64-fpic-adr_prel_pg_hi21.s | 4 +- test/ELF/aarch64-fpic-ldst32_abs_lo12_nc.s | 4 +- test/ELF/aarch64-fpic-ldst64_abs_lo12_nc.s | 4 +- test/ELF/aarch64-fpic-ldst8_abs_lo12_nc.s | 4 +- test/ELF/aarch64-fpic-prel16.s | 4 +- test/ELF/aarch64-fpic-prel32.s | 4 +- test/ELF/aarch64-fpic-prel64.s | 4 +- test/ELF/abs-conflict.s | 4 +- test/ELF/arm-gnu-ifunc-plt.s | 28 +- test/ELF/arm-gnu-ifunc.s | 25 +- test/ELF/arm-icf-exidx.s | 31 + test/ELF/arm-plt-reloc.s | 46 +- test/ELF/arm-target1.s | 8 +- test/ELF/arm-thumb-interwork-shared.s | 22 +- test/ELF/arm-thumb-interwork-thunk.s | 185 +- test/ELF/arm-thumb-plt-reloc.s | 17 +- test/ELF/arm-tls-gd-nonpreemptible.s | 72 + test/ELF/as-needed-no-reloc.s | 2 +- test/ELF/basic-aarch64.s | 2 +- test/ELF/basic-mips.s | 2 +- test/ELF/basic-ppc.s | 2 +- test/ELF/basic.s | 7 +- test/ELF/basic32.s | 2 +- test/ELF/basic64be.s | 2 +- test/ELF/bss-start-common.s | 15 + test/ELF/build-id.s | 6 +- test/ELF/compatible-section-types.s | 20 + test/ELF/compressed-debug-input.s | 2 +- test/ELF/conflict.s | 32 +- test/ELF/copy-errors.s | 10 +- test/ELF/copy-in-shared.s | 5 +- test/ELF/copy-rel-pie-error.s | 9 +- test/ELF/default-fill.s | 38 + test/ELF/driver.test | 9 +- test/ELF/dt_flags.s | 6 +- test/ELF/dynamic-list.s | 63 + test/ELF/dynamic-reloc-in-ro.s | 6 +- test/ELF/early-exit-for-bad-paths.s | 35 + test/ELF/edata-etext.s | 6 +- test/ELF/eh-frame-dyn-rel.s | 12 +- test/ELF/ehdr_start.s | 22 +- test/ELF/emit-relocs-merge.s | 20 + test/ELF/emit-relocs-shared.s | 16 + test/ELF/emit-relocs.s | 99 + test/ELF/emulation.s | 6 +- test/ELF/end-abs.s | 11 + test/ELF/gc-debuginfo-tls.s | 18 +- test/ELF/gc-merge-local-sym.s | 2 +- test/ELF/gc-sections-merge-addend.s | 2 +- test/ELF/gc-sections-merge-implicit-addend.s | 2 +- test/ELF/gc-sections-merge.s | 4 +- test/ELF/gc-sections-metadata-startstop.s | 33 + test/ELF/gc-sections-metadata.s | 38 + test/ELF/gc-sections-metadata2.s | 19 + test/ELF/gnu-hash-table.s | 2 +- test/ELF/gnustack.s | 40 +- test/ELF/got32-i386.s | 23 + test/ELF/got32x-i386.s | 47 + test/ELF/hidden-vis-shared.s | 18 + test/ELF/i386-pc16.test | 6 +- test/ELF/i386-pc8-pc16-addend.s | 17 + test/ELF/i386-pc8.s | 15 + test/ELF/i386-reloc-16.s | 14 + test/ELF/i386-reloc-8.s | 14 + test/ELF/i386-reloc8-reloc16-addend.s | 17 + test/ELF/icf-absolute.s | 20 + test/ELF/icf8.s | 14 + test/ELF/icf9.s | 20 + test/ELF/incompatible-section-types.s | 10 - test/ELF/init-fini-progbits.s | 19 + test/ELF/invalid-cie-length.s | 7 +- test/ELF/invalid-cie-length2.s | 7 +- test/ELF/invalid-cie-length3.s | 8 +- test/ELF/invalid-cie-length4.s | 9 +- test/ELF/invalid-fde-rel.s | 8 +- test/ELF/invalid-linkerscript.test | 2 +- test/ELF/invalid/broken-relaxation-x64.s | 46 - test/ELF/invalid/broken-relaxation-x64.test | 46 + test/ELF/invalid/dynamic-section-size.s | 2 +- test/ELF/invalid/invalid-relocation-x64.s | 30 - test/ELF/invalid/invalid-relocation-x64.test | 30 + test/ELF/invalid/mips-invalid-options-descriptor.s | 2 +- test/ELF/libsearch.s | 3 +- test/ELF/linkerscript/Inputs/lazy-symbols.s | 2 + test/ELF/linkerscript/absolute.s | 17 + test/ELF/linkerscript/addr-zero.s | 18 + test/ELF/linkerscript/align.s | 87 +- test/ELF/linkerscript/alternate-sections.s | 40 +- test/ELF/linkerscript/assert.s | 4 - test/ELF/linkerscript/at-addr.s | 39 + test/ELF/linkerscript/at.s | 37 +- test/ELF/linkerscript/constructor.s | 13 + test/ELF/linkerscript/discard-print-gc.s | 19 + test/ELF/linkerscript/discard-section-metadata.s | 32 + test/ELF/linkerscript/edata-etext.s | 9 +- .../ELF/linkerscript/eh-frame-reloc-out-of-range.s | 27 + test/ELF/linkerscript/eh-frame.s | 19 + test/ELF/linkerscript/ehdr_start.s | 3 +- test/ELF/linkerscript/emit-reloc.s | 17 + test/ELF/linkerscript/emit-relocs-discard.s | 14 + .../ELF/linkerscript/emit-relocs-ehframe-discard.s | 11 + test/ELF/linkerscript/empty-load.s | 3 +- test/ELF/linkerscript/excludefile.s | 9 +- test/ELF/linkerscript/expr-invalid-sec.s | 6 + test/ELF/linkerscript/expr-sections.s | 22 + test/ELF/linkerscript/fill-exec-sections.s | 40 + test/ELF/linkerscript/fill.s | 5 +- test/ELF/linkerscript/huge-temporary-file.s | 12 + test/ELF/linkerscript/lazy-symbols.s | 13 + test/ELF/linkerscript/locationcounter.s | 189 -- test/ELF/linkerscript/locationcountererr.s | 6 +- test/ELF/linkerscript/locationcountererr2.s | 9 + test/ELF/linkerscript/memory.s | 114 ++ test/ELF/linkerscript/merge-sections-syms.s | 49 + test/ELF/linkerscript/merge-sections.s | 50 +- test/ELF/linkerscript/no-space.s | 6 +- test/ELF/linkerscript/non-absolute.s | 30 + test/ELF/linkerscript/non-absolute2.s | 12 + test/ELF/linkerscript/non-alloc.s | 6 +- test/ELF/linkerscript/numbers.s | 26 +- test/ELF/linkerscript/obj-symbol-value.s | 19 + test/ELF/linkerscript/operators.s | 93 + test/ELF/linkerscript/orphan-first-cmd.s | 4 +- test/ELF/linkerscript/out-of-order.s | 10 + test/ELF/linkerscript/output-too-large.s | 8 + test/ELF/linkerscript/outputarch.s | 8 +- test/ELF/linkerscript/outsections-addr.s | 22 +- test/ELF/linkerscript/page-size.s | 4 +- test/ELF/linkerscript/pt_gnu_eh_frame.s | 13 + test/ELF/linkerscript/section-align.s | 62 + test/ELF/linkerscript/sections-gc.s | 19 + test/ELF/linkerscript/sections-gc2.s | 31 + test/ELF/linkerscript/sections-padding.s | 11 +- test/ELF/linkerscript/sections-sort.s | 5 +- test/ELF/linkerscript/sections.s | 24 +- test/ELF/linkerscript/symbol-assignexpr.s | 4 +- test/ELF/linkerscript/symbol-only.s | 2 +- test/ELF/linkerscript/symbol-reserved.s | 16 + test/ELF/linkerscript/symbols-non-alloc.s | 16 + test/ELF/lto/Inputs/cache.ll | 10 + test/ELF/lto/archive-no-index.ll | 39 + test/ELF/lto/cache.ll | 21 + test/ELF/lto/codemodel.ll | 20 + test/ELF/lto/combined-lto-object-name.ll | 4 +- test/ELF/lto/duplicated.ll | 6 +- test/ELF/lto/opt-remarks.ll | 68 + test/ELF/lto/parallel-internalize.ll | 3 +- test/ELF/lto/parallel.ll | 3 +- test/ELF/lto/thinlto.ll | 13 +- test/ELF/map-file.s | 62 + test/ELF/merge-reloc.s | 8 +- test/ELF/merge-string-align.s | 4 +- test/ELF/merge-string.s | 28 +- test/ELF/merge.s | 2 +- test/ELF/mips-elf-flags-err.s | 9 +- test/ELF/mips-elf-flags.s | 4 +- test/ELF/mips-got-string.s | 28 + test/ELF/mips-got16-relocatable.s | 40 + test/ELF/mips-gp-ext.s | 4 +- test/ELF/mips-gprel-sec.s | 37 + test/ELF/mips-npic-call-pic-os.s | 138 ++ test/ELF/mips-npic-call-pic.s | 169 +- test/ELF/mips-options.s | 2 +- test/ELF/mips-reginfo.s | 2 +- test/ELF/mips-sto-pic-flag.s | 4 +- test/ELF/mips-tls-64.s | 3 +- test/ELF/mips-tls-hilo.s | 3 +- test/ELF/mips-tls.s | 3 +- test/ELF/no-dynamic-linker.s | 12 + test/ELF/no-merge.s | 22 +- test/ELF/no-soname.s | 31 + test/ELF/note-contiguous.s | 24 + test/ELF/note-loadaddr.c | 35 + test/ELF/note-multiple.s | 43 + test/ELF/plt-i686.s | 12 +- test/ELF/pre_init_fini_array_missing.s | 13 + test/ELF/relocatable-bss.s | 2 +- test/ELF/relocatable-common.s | 12 +- test/ELF/relocatable-eh-frame-hdr.s | 11 + test/ELF/relocatable-eh-frame.s | 19 + test/ELF/relocatable-ehframe.s | 4 +- test/ELF/relocatable-non-alloc.s | 10 + test/ELF/relocatable-section-symbol.s | 49 + test/ELF/relocatable-symbol-name.s | 28 + test/ELF/relocatable.s | 2 +- test/ELF/relocation-copy-align-common.s | 40 + test/ELF/relocation-group.test | 43 + test/ELF/relocation-nocopy.s | 19 + test/ELF/relocation-none-aarch64.test | 24 + test/ELF/relocation-relative-absolute.s | 4 +- test/ELF/relro-omagic.s | 2 +- test/ELF/reproduce.s | 8 + test/ELF/resolution-end.s | 13 +- test/ELF/retain-symbols-file.s | 44 +- test/ELF/retain-und.s | 18 + test/ELF/section-name.s | 4 +- test/ELF/splitstacks.s | 2 +- test/ELF/startstop-gccollect.s | 16 +- test/ELF/static-with-export-dynamic.s | 32 + test/ELF/sysroot.s | 3 +- test/ELF/tls-mismatch.s | 5 +- test/ELF/tls-static.s | 3 +- test/ELF/trace-symbols.s | 2 +- test/ELF/ttext-tdata-tbss.s | 11 +- test/ELF/undef-shared.s | 9 +- test/ELF/undef-with-plt-addr.s | 3 + test/ELF/undef.s | 36 +- test/ELF/unknown-reloc.s | 14 - test/ELF/unresolved-symbols.s | 10 +- test/ELF/verneed-local.s | 3 +- test/ELF/version-script-copy-rel.s | 24 + test/ELF/version-script-extern-exact.s | 18 +- test/ELF/version-script-extern-wildcards-anon.s | 12 + test/ELF/version-script-glob.s | 27 + test/ELF/version-script.s | 3 +- test/ELF/warn-unresolved-symbols-hidden.s | 14 + test/ELF/warn-unresolved-symbols.s | 51 + test/ELF/x86-64-dyn-rel-error.s | 2 +- test/ELF/x86-64-dyn-rel-error2.s | 6 +- test/ELF/x86-64-reloc-16.s | 14 + test/ELF/x86-64-reloc-32-fpic.s | 5 +- test/ELF/x86-64-reloc-8.s | 14 + test/ELF/x86-64-reloc-pc32-fpic.s | 5 +- test/ELF/zdefs.s | 3 +- test/ELF/zstack-size.s | 37 +- test/ELF/ztext-text-notext.s | 36 + test/lit.cfg | 16 +- test/lit.site.cfg.in | 4 +- tools/lld/lld.cpp | 8 +- unittests/CoreTests/CMakeLists.txt | 2 +- unittests/CoreTests/ParallelTest.cpp | 15 + 371 files changed, 12806 insertions(+), 7949 deletions(-) create mode 100644 COFF/LTO.cpp create mode 100644 COFF/LTO.h create mode 100644 COFF/MapFile.cpp create mode 100644 COFF/MapFile.h create mode 100644 ELF/Filesystem.cpp create mode 100644 ELF/Filesystem.h create mode 100644 ELF/MapFile.cpp create mode 100644 ELF/MapFile.h create mode 100644 ELF/ScriptLexer.cpp create mode 100644 ELF/ScriptLexer.h delete mode 100644 docs/C++11.rst create mode 100644 include/lld/Core/TargetOptionsCommandFlags.h create mode 100644 lib/Core/TargetOptionsCommandFlags.cpp create mode 100644 test/COFF/Inputs/bar.ll create mode 100644 test/COFF/Inputs/msvclto.s create mode 100644 test/COFF/Inputs/thinlto-mangled-qux.ll create mode 100644 test/COFF/def-name.test create mode 100644 test/COFF/driver-windows.test create mode 100644 test/COFF/error-limit.test create mode 100644 test/COFF/include-lto.ll create mode 100644 test/COFF/invalid-debug-type.test create mode 100644 test/COFF/lto-debug-pass-arguments.ll create mode 100644 test/COFF/msvclto-archive.ll create mode 100644 test/COFF/msvclto.ll create mode 100644 test/COFF/nopdb.test create mode 100644 test/COFF/pdb-none.test create mode 100644 test/COFF/savetemps.ll create mode 100644 test/COFF/thinlto-archives.ll create mode 100644 test/COFF/thinlto-mangled.ll create mode 100644 test/COFF/thinlto.ll create mode 100644 test/ELF/Inputs/i386-got32x-baseless.elf create mode 100644 test/ELF/Inputs/i386-reloc-16-error.s create mode 100644 test/ELF/Inputs/i386-reloc-16.s create mode 100644 test/ELF/Inputs/i386-reloc-8-error.s create mode 100644 test/ELF/Inputs/i386-reloc-8.s create mode 100644 test/ELF/Inputs/icf-absolute.s create mode 100644 test/ELF/Inputs/map-file2.s create mode 100644 test/ELF/Inputs/map-file3.s create mode 100644 test/ELF/Inputs/map-file4.s create mode 100644 test/ELF/Inputs/relocatable-non-alloc.s create mode 100644 test/ELF/Inputs/relocation-copy-align-common.s create mode 100644 test/ELF/Inputs/x86-64-reloc-16-error.s create mode 100644 test/ELF/Inputs/x86-64-reloc-16.s create mode 100644 test/ELF/Inputs/x86-64-reloc-8-error.s create mode 100644 test/ELF/Inputs/x86-64-reloc-8.s create mode 100644 test/ELF/Inputs/ztext-text-notext.s create mode 100644 test/ELF/arm-icf-exidx.s create mode 100644 test/ELF/arm-tls-gd-nonpreemptible.s create mode 100644 test/ELF/bss-start-common.s create mode 100644 test/ELF/compatible-section-types.s create mode 100644 test/ELF/default-fill.s create mode 100644 test/ELF/early-exit-for-bad-paths.s create mode 100644 test/ELF/emit-relocs-merge.s create mode 100644 test/ELF/emit-relocs-shared.s create mode 100644 test/ELF/emit-relocs.s create mode 100644 test/ELF/end-abs.s create mode 100644 test/ELF/gc-sections-metadata-startstop.s create mode 100644 test/ELF/gc-sections-metadata.s create mode 100644 test/ELF/gc-sections-metadata2.s create mode 100644 test/ELF/got32-i386.s create mode 100644 test/ELF/got32x-i386.s create mode 100644 test/ELF/hidden-vis-shared.s create mode 100644 test/ELF/i386-pc8-pc16-addend.s create mode 100644 test/ELF/i386-pc8.s create mode 100644 test/ELF/i386-reloc-16.s create mode 100644 test/ELF/i386-reloc-8.s create mode 100644 test/ELF/i386-reloc8-reloc16-addend.s create mode 100644 test/ELF/icf-absolute.s create mode 100644 test/ELF/icf8.s create mode 100644 test/ELF/icf9.s delete mode 100644 test/ELF/incompatible-section-types.s create mode 100644 test/ELF/init-fini-progbits.s delete mode 100644 test/ELF/invalid/broken-relaxation-x64.s create mode 100644 test/ELF/invalid/broken-relaxation-x64.test delete mode 100644 test/ELF/invalid/invalid-relocation-x64.s create mode 100644 test/ELF/invalid/invalid-relocation-x64.test create mode 100644 test/ELF/linkerscript/Inputs/lazy-symbols.s create mode 100644 test/ELF/linkerscript/addr-zero.s create mode 100644 test/ELF/linkerscript/at-addr.s create mode 100644 test/ELF/linkerscript/constructor.s create mode 100644 test/ELF/linkerscript/discard-print-gc.s create mode 100644 test/ELF/linkerscript/discard-section-metadata.s create mode 100644 test/ELF/linkerscript/eh-frame-reloc-out-of-range.s create mode 100644 test/ELF/linkerscript/eh-frame.s create mode 100644 test/ELF/linkerscript/emit-reloc.s create mode 100644 test/ELF/linkerscript/emit-relocs-discard.s create mode 100644 test/ELF/linkerscript/emit-relocs-ehframe-discard.s create mode 100644 test/ELF/linkerscript/expr-invalid-sec.s create mode 100644 test/ELF/linkerscript/expr-sections.s create mode 100644 test/ELF/linkerscript/fill-exec-sections.s create mode 100644 test/ELF/linkerscript/huge-temporary-file.s create mode 100644 test/ELF/linkerscript/lazy-symbols.s delete mode 100644 test/ELF/linkerscript/locationcounter.s create mode 100644 test/ELF/linkerscript/locationcountererr2.s create mode 100644 test/ELF/linkerscript/memory.s create mode 100644 test/ELF/linkerscript/merge-sections-syms.s create mode 100644 test/ELF/linkerscript/non-absolute.s create mode 100644 test/ELF/linkerscript/non-absolute2.s create mode 100644 test/ELF/linkerscript/obj-symbol-value.s create mode 100644 test/ELF/linkerscript/operators.s create mode 100644 test/ELF/linkerscript/out-of-order.s create mode 100644 test/ELF/linkerscript/output-too-large.s create mode 100644 test/ELF/linkerscript/pt_gnu_eh_frame.s create mode 100644 test/ELF/linkerscript/section-align.s create mode 100644 test/ELF/linkerscript/sections-gc.s create mode 100644 test/ELF/linkerscript/sections-gc2.s create mode 100644 test/ELF/linkerscript/symbol-reserved.s create mode 100644 test/ELF/linkerscript/symbols-non-alloc.s create mode 100644 test/ELF/lto/Inputs/cache.ll create mode 100644 test/ELF/lto/archive-no-index.ll create mode 100644 test/ELF/lto/cache.ll create mode 100644 test/ELF/lto/codemodel.ll create mode 100644 test/ELF/lto/opt-remarks.ll create mode 100644 test/ELF/map-file.s create mode 100644 test/ELF/mips-got-string.s create mode 100644 test/ELF/mips-got16-relocatable.s create mode 100644 test/ELF/mips-gprel-sec.s create mode 100644 test/ELF/mips-npic-call-pic-os.s create mode 100644 test/ELF/no-dynamic-linker.s create mode 100644 test/ELF/no-soname.s create mode 100644 test/ELF/note-contiguous.s create mode 100644 test/ELF/note-loadaddr.c create mode 100644 test/ELF/note-multiple.s create mode 100644 test/ELF/relocatable-eh-frame-hdr.s create mode 100644 test/ELF/relocatable-eh-frame.s create mode 100644 test/ELF/relocatable-non-alloc.s create mode 100644 test/ELF/relocatable-section-symbol.s create mode 100644 test/ELF/relocatable-symbol-name.s create mode 100644 test/ELF/relocation-copy-align-common.s create mode 100644 test/ELF/relocation-group.test create mode 100644 test/ELF/relocation-nocopy.s create mode 100644 test/ELF/relocation-none-aarch64.test create mode 100644 test/ELF/retain-und.s create mode 100644 test/ELF/static-with-export-dynamic.s delete mode 100644 test/ELF/unknown-reloc.s create mode 100644 test/ELF/version-script-copy-rel.s create mode 100644 test/ELF/warn-unresolved-symbols-hidden.s create mode 100644 test/ELF/warn-unresolved-symbols.s create mode 100644 test/ELF/x86-64-reloc-16.s create mode 100644 test/ELF/x86-64-reloc-8.s create mode 100644 test/ELF/ztext-text-notext.s diff --git a/CMakeLists.txt b/CMakeLists.txt index be424efbbd878..7fcb1a748ffc1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,8 +11,11 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) message(FATAL_ERROR "llvm-config not found: specify LLVM_CONFIG_PATH") endif() - execute_process(COMMAND "${LLVM_CONFIG_PATH}" "--obj-root" "--includedir" + execute_process(COMMAND "${LLVM_CONFIG_PATH}" + "--obj-root" + "--includedir" "--cmakedir" + "--src-root" RESULT_VARIABLE HAD_ERROR OUTPUT_VARIABLE LLVM_CONFIG_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE) @@ -25,9 +28,11 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) list(GET LLVM_CONFIG_OUTPUT 0 OBJ_ROOT) list(GET LLVM_CONFIG_OUTPUT 1 MAIN_INCLUDE_DIR) list(GET LLVM_CONFIG_OUTPUT 2 LLVM_CMAKE_PATH) + list(GET LLVM_CONFIG_OUTPUT 3 MAIN_SRC_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") + set(LLVM_MAIN_SRC_DIR ${MAIN_SRC_DIR} CACHE PATH "Path to LLVM source tree") file(TO_CMAKE_PATH ${LLVM_OBJ_ROOT} LLVM_BINARY_DIR) @@ -49,6 +54,67 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) include(AddLLVM) include(TableGen) include(HandleLLVMOptions) + + if(LLVM_INCLUDE_TESTS) + set(Python_ADDITIONAL_VERSIONS 2.7) + include(FindPythonInterp) + if(NOT PYTHONINTERP_FOUND) + message(FATAL_ERROR +"Unable to find Python interpreter, required for testing. + +Please install Python or specify the PYTHON_EXECUTABLE CMake variable.") + endif() + + if(${PYTHON_VERSION_STRING} VERSION_LESS 2.7) + message(FATAL_ERROR "Python 2.7 or newer is required") + endif() + + # Check prebuilt llvm/utils. + if(EXISTS ${LLVM_TOOLS_BINARY_DIR}/FileCheck${CMAKE_EXECUTABLE_SUFFIX} + AND EXISTS ${LLVM_TOOLS_BINARY_DIR}/not${CMAKE_EXECUTABLE_SUFFIX}) + set(LLVM_UTILS_PROVIDED ON) + endif() + + if(EXISTS ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py) + # Note: path not really used, except for checking if lit was found + set(LLVM_LIT ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py) + if(NOT LLVM_UTILS_PROVIDED) + add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/FileCheck utils/FileCheck) + add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/not utils/not) + set(LLVM_UTILS_PROVIDED ON) + set(LLD_TEST_DEPS FileCheck not) + endif() + set(UNITTEST_DIR ${LLVM_MAIN_SRC_DIR}/utils/unittest) + if(EXISTS ${UNITTEST_DIR}/googletest/include/gtest/gtest.h + AND NOT EXISTS ${LLVM_LIBRARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX} + AND EXISTS ${UNITTEST_DIR}/CMakeLists.txt) + add_subdirectory(${UNITTEST_DIR} utils/unittest) + endif() + else() + # Seek installed Lit. + find_program(LLVM_LIT + NAMES llvm-lit lit.py lit + PATHS "${LLVM_MAIN_SRC_DIR}/utils/lit" + DOC "Path to lit.py") + endif() + + if(LLVM_LIT) + # Define the default arguments to use with 'lit', and an option for the user + # to override. + set(LIT_ARGS_DEFAULT "-sv") + if (MSVC OR XCODE) + set(LIT_ARGS_DEFAULT "${LIT_ARGS_DEFAULT} --no-progress-bar") + endif() + set(LLVM_LIT_ARGS "${LIT_ARGS_DEFAULT}" CACHE STRING "Default options for lit") + + # On Win32 hosts, provide an option to specify the path to the GnuWin32 tools. + if(WIN32 AND NOT CYGWIN) + set(LLVM_LIT_TOOLS_DIR "" CACHE PATH "Path to GnuWin32 tools") + endif() + else() + set(LLVM_INCLUDE_TESTS OFF) + endif() + endif() endif() set(LLD_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/COFF/CMakeLists.txt b/COFF/CMakeLists.txt index 70a33b9fdd818..8f24e36c0ecad 100644 --- a/COFF/CMakeLists.txt +++ b/COFF/CMakeLists.txt @@ -15,6 +15,8 @@ add_lld_library(lldCOFF ICF.cpp InputFiles.cpp Librarian.cpp + LTO.cpp + MapFile.cpp MarkLive.cpp ModuleDef.cpp PDB.cpp @@ -25,6 +27,7 @@ add_lld_library(lldCOFF LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} + BitReader Core DebugInfoCodeView DebugInfoMSF @@ -40,7 +43,7 @@ add_lld_library(lldCOFF LINK_LIBS lldCore - ${PTHREAD_LIB} + ${LLVM_PTHREAD_LIB} DEPENDS COFFOptionsTableGen diff --git a/COFF/Chunks.cpp b/COFF/Chunks.cpp index 7f0dfa92ec10f..10eeedd88e551 100644 --- a/COFF/Chunks.cpp +++ b/COFF/Chunks.cpp @@ -11,6 +11,7 @@ #include "Error.h" #include "InputFiles.h" #include "Symbols.h" +#include "llvm/ADT/Twine.h" #include "llvm/Object/COFF.h" #include "llvm/Support/COFF.h" #include "llvm/Support/Debug.h" @@ -61,7 +62,7 @@ void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, Defined *Sym, case IMAGE_REL_AMD64_SECTION: add16(Off, Sym->getSectionIndex()); break; case IMAGE_REL_AMD64_SECREL: add32(Off, Sym->getSecrel()); break; default: - fatal("unsupported relocation type"); + fatal("unsupported relocation type 0x" + Twine::utohexstr(Type)); } } @@ -76,7 +77,7 @@ void SectionChunk::applyRelX86(uint8_t *Off, uint16_t Type, Defined *Sym, case IMAGE_REL_I386_SECTION: add16(Off, Sym->getSectionIndex()); break; case IMAGE_REL_I386_SECREL: add32(Off, Sym->getSecrel()); break; default: - fatal("unsupported relocation type"); + fatal("unsupported relocation type 0x" + Twine::utohexstr(Type)); } } @@ -136,7 +137,7 @@ void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, Defined *Sym, 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"); + fatal("unsupported relocation type 0x" + Twine::utohexstr(Type)); } } @@ -226,7 +227,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) - outs() << "Discarded " << Sym->getName() << "\n"; + message("Discarded " + Sym->getName()); } StringRef SectionChunk::getDebugName() { diff --git a/COFF/Chunks.h b/COFF/Chunks.h index 59e36b84c9b0b..44d7f31afc678 100644 --- a/COFF/Chunks.h +++ b/COFF/Chunks.h @@ -187,10 +187,10 @@ public: const coff_section *Header; -private: - // A file this chunk was created from. + // The file that this chunk was created from. ObjectFile *File; +private: StringRef SectionName; std::vector AssocChildren; llvm::iterator_range Relocs; diff --git a/COFF/Config.h b/COFF/Config.h index 0fa3338aa28ce..31534aeb39719 100644 --- a/COFF/Config.h +++ b/COFF/Config.h @@ -80,14 +80,16 @@ struct Configuration { SymbolBody *Entry = nullptr; bool NoEntry = false; std::string OutputFile; + bool ColorDiagnostics; bool DoGC = true; bool DoICF = true; + uint64_t ErrorLimit = 20; bool Relocatable = true; bool Force = false; bool Debug = false; bool WriteSymtab = true; unsigned DebugTypes = static_cast(DebugType::None); - StringRef PDBPath; + llvm::SmallString<128> PDBPath; // Symbols in this set are considered as live by the garbage collector. std::set GCRoot; @@ -103,6 +105,8 @@ struct Configuration { std::map DLLOrder; SymbolBody *DelayLoadHelper = nullptr; + bool SaveTemps = false; + // Used for SafeSEH. Symbol *SEHTable = nullptr; Symbol *SEHCount = nullptr; @@ -111,7 +115,9 @@ struct Configuration { unsigned LTOOptLevel = 2; // Used for /opt:lldltojobs=N - unsigned LTOJobs = 1; + unsigned LTOJobs = 0; + // Used for /opt:lldltopartitions=N + unsigned LTOPartitions = 1; // Used for /merge:from=to (e.g. /merge:.rdata=.text) std::map Merge; @@ -135,6 +141,9 @@ struct Configuration { // Used for /alternatename. std::map AlternateNames; + // Used for /lldmap. + std::string MapFile; + uint64_t ImageBase = -1; uint64_t StackReserve = 1024 * 1024; uint64_t StackCommit = 4096; @@ -151,6 +160,7 @@ struct Configuration { bool TerminalServerAware = true; bool LargeAddressAware = false; bool HighEntropyVA = false; + bool AppContainer = false; // This is for debugging. bool DebugPdb = false; diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp index 4dabd9ebcc6d3..3e7f10bf8d11e 100644 --- a/COFF/Driver.cpp +++ b/COFF/Driver.cpp @@ -19,6 +19,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/LibDriver/LibDriver.h" +#include "llvm/Object/ArchiveWriter.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" @@ -31,17 +32,11 @@ #include #include -#ifdef _MSC_VER -// depends on for __uncaught_exception. -#include -#endif - #include using namespace llvm; using namespace llvm::COFF; using llvm::sys::Process; -using llvm::sys::fs::OpenFlags; using llvm::sys::fs::file_magic; using llvm::sys::fs::identify_magic; @@ -55,11 +50,16 @@ BumpPtrAllocator BAlloc; StringSaver Saver{BAlloc}; std::vector SpecificAllocBase::Instances; -bool link(ArrayRef Args) { +bool link(ArrayRef Args, raw_ostream &Diag) { + ErrorCount = 0; + ErrorOS = &Diag; + Argv0 = Args[0]; Config = make(); + Config->ColorDiagnostics = + (ErrorOS == &llvm::errs() && Process::StandardErrHasColors()); Driver = make(); Driver->link(Args); - return true; + return !ErrorCount; } // Drop directory components and replace extension with ".exe" or ".dll". @@ -121,10 +121,12 @@ void LinkerDriver::addBuffer(std::unique_ptr MB) { return Symtab.addFile(make(MBRef)); if (Magic == file_magic::bitcode) return Symtab.addFile(make(MBRef)); + if (Magic == file_magic::coff_cl_gl_object) - fatal(MBRef.getBufferIdentifier() + ": is not a native COFF file. " + error(MBRef.getBufferIdentifier() + ": is not a native COFF file. " "Recompile without /GL"); - Symtab.addFile(make(MBRef)); + else + Symtab.addFile(make(MBRef)); } void LinkerDriver::enqueuePath(StringRef Path) { @@ -134,12 +136,10 @@ void LinkerDriver::enqueuePath(StringRef Path) { enqueueTask([=]() { auto MBOrErr = Future->get(); if (MBOrErr.second) - fatal(MBOrErr.second, "could not open " + PathStr); - Driver->addBuffer(std::move(MBOrErr.first)); + error("could not open " + PathStr + ": " + MBOrErr.second.message()); + else + Driver->addBuffer(std::move(MBOrErr.first)); }); - - if (Config->OutputFile == "") - Config->OutputFile = getOutputPath(Path); } void LinkerDriver::addArchiveBuffer(MemoryBufferRef MB, StringRef SymName, @@ -151,17 +151,18 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef MB, StringRef SymName, } InputFile *Obj; - if (Magic == file_magic::coff_object) + if (Magic == file_magic::coff_object) { Obj = make(MB); - else if (Magic == file_magic::bitcode) + } else if (Magic == file_magic::bitcode) { Obj = make(MB); - else - fatal("unknown file type: " + MB.getBufferIdentifier()); + } else { + error("unknown file type: " + MB.getBufferIdentifier()); + return; + } Obj->ParentName = ParentName; Symtab.addFile(Obj); - if (Config->Verbose) - outs() << "Loaded " << toString(Obj) << " for " << SymName << "\n"; + log("Loaded " + toString(Obj) + " for " + SymName); } void LinkerDriver::enqueueArchiveMember(const Archive::Child &C, @@ -234,7 +235,7 @@ void LinkerDriver::parseDirectives(StringRef S) { case OPT_throwingnew: break; default: - fatal(Arg->getSpelling() + " is not allowed in .drectve"); + error(Arg->getSpelling() + " is not allowed in .drectve"); } } } @@ -402,7 +403,8 @@ static unsigned parseDebugType(StringRef Arg) { DebugTypes |= StringSwitch(Type.lower()) .Case("cv", static_cast(DebugType::CV)) .Case("pdata", static_cast(DebugType::PData)) - .Case("fixup", static_cast(DebugType::Fixup)); + .Case("fixup", static_cast(DebugType::Fixup)) + .Default(0); return DebugTypes; } @@ -418,6 +420,132 @@ static std::string getMapFile(const opt::InputArgList &Args) { return (OutFile.substr(0, OutFile.rfind('.')) + ".map").str(); } +std::vector getArchiveMembers(Archive *File) { + std::vector V; + Error Err = Error::success(); + for (const ErrorOr &COrErr : File->children(Err)) { + Archive::Child C = + check(COrErr, + File->getFileName() + ": could not get the child of the archive"); + MemoryBufferRef MBRef = + check(C.getMemoryBufferRef(), + File->getFileName() + + ": could not get the buffer for a child of the archive"); + V.push_back(MBRef); + } + if (Err) + fatal(File->getFileName() + + ": Archive::children failed: " + toString(std::move(Err))); + return V; +} + +// A helper function for filterBitcodeFiles. +static bool needsRebuilding(MemoryBufferRef MB) { + // The MSVC linker doesn't support thin archives, so if it's a thin + // archive, we always need to rebuild it. + std::unique_ptr File = + check(Archive::create(MB), "Failed to read " + MB.getBufferIdentifier()); + if (File->isThin()) + return true; + + // Returns true if the archive contains at least one bitcode file. + for (MemoryBufferRef Member : getArchiveMembers(File.get())) + if (identify_magic(Member.getBuffer()) == file_magic::bitcode) + return true; + return false; +} + +// Opens a given path as an archive file and removes bitcode files +// from them if exists. This function is to appease the MSVC linker as +// their linker doesn't like archive files containing non-native +// object files. +// +// If a given archive doesn't contain bitcode files, the archive path +// is returned as-is. Otherwise, a new temporary file is created and +// its path is returned. +static Optional +filterBitcodeFiles(StringRef Path, std::vector &TemporaryFiles) { + std::unique_ptr MB = check( + MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path); + MemoryBufferRef MBRef = MB->getMemBufferRef(); + file_magic Magic = identify_magic(MBRef.getBuffer()); + + if (Magic == file_magic::bitcode) + return None; + if (Magic != file_magic::archive) + return Path.str(); + if (!needsRebuilding(MBRef)) + return Path.str(); + + std::unique_ptr File = + check(Archive::create(MBRef), + MBRef.getBufferIdentifier() + ": failed to parse archive"); + + std::vector New; + for (MemoryBufferRef Member : getArchiveMembers(File.get())) + if (identify_magic(Member.getBuffer()) != file_magic::bitcode) + New.emplace_back(Member); + + if (New.empty()) + return None; + + log("Creating a temporary archive for " + Path + " to remove bitcode files"); + + SmallString<128> S; + if (auto EC = sys::fs::createTemporaryFile("lld-" + sys::path::stem(Path), + ".lib", S)) + fatal(EC, "cannot create a temporary file"); + std::string Temp = S.str(); + TemporaryFiles.push_back(Temp); + + std::pair Ret = + llvm::writeArchive(Temp, New, /*WriteSymtab=*/true, Archive::Kind::K_GNU, + /*Deterministics=*/true, + /*Thin=*/false); + if (Ret.second) + error("failed to create a new archive " + S.str() + ": " + Ret.first); + return Temp; +} + +// Create response file contents and invoke the MSVC linker. +void LinkerDriver::invokeMSVC(opt::InputArgList &Args) { + std::string Rsp = "/nologo "; + std::vector Temps; + + for (auto *Arg : Args) { + switch (Arg->getOption().getID()) { + case OPT_linkrepro: + case OPT_lldmap: + case OPT_lldmap_file: + case OPT_lldsavetemps: + case OPT_msvclto: + // LLD-specific options are stripped. + break; + case OPT_opt: + if (!StringRef(Arg->getValue()).startswith("lld")) + Rsp += toString(Arg) + " "; + break; + case OPT_INPUT: { + if (Optional Path = doFindFile(Arg->getValue())) { + if (Optional S = filterBitcodeFiles(*Path, Temps)) + Rsp += quote(*S) + " "; + continue; + } + Rsp += quote(Arg->getValue()) + " "; + break; + } + default: + Rsp += toString(Arg) + " "; + } + } + + std::vector ObjectFiles = Symtab.compileBitcodeFiles(); + runMSVCLinker(Rsp, ObjectFiles); + + for (StringRef Path : Temps) + sys::fs::remove(Path); +} + void LinkerDriver::enqueueTask(std::function Task) { TaskQueue.push_back(std::move(Task)); } @@ -451,6 +579,22 @@ void LinkerDriver::link(ArrayRef ArgsArr) { // Parse command line options. opt::InputArgList Args = Parser.parseLINK(ArgsArr.slice(1)); + // Parse and evaluate -mllvm options. + std::vector V; + V.push_back("lld-link (LLVM option parsing)"); + for (auto *Arg : Args.filtered(OPT_mllvm)) + V.push_back(Arg->getValue()); + cl::ParseCommandLineOptions(V.size(), V.data()); + + // Handle /errorlimit early, because error() depends on it. + if (auto *Arg = Args.getLastArg(OPT_errorlimit)) { + int N = 20; + StringRef S = Arg->getValue(); + if (S.getAsInteger(10, N)) + error(Arg->getSpelling() + " number expected, but got " + S); + Config->ErrorLimit = N; + } + // Handle /help if (Args.hasArg(OPT_help)) { printHelp(ArgsArr[0]); @@ -467,12 +611,12 @@ void LinkerDriver::link(ArrayRef ArgsArr) { if (ErrOrWriter) { Tar = std::move(*ErrOrWriter); } else { - errs() << "/linkrepro: failed to open " << Path << ": " - << toString(ErrOrWriter.takeError()) << '\n'; + error("/linkrepro: failed to open " + Path + ": " + + toString(ErrOrWriter.takeError())); } } - if (Args.filtered_begin(OPT_INPUT) == Args.filtered_end()) + if (!Args.hasArgNoClaim(OPT_INPUT)) fatal("no input files"); // Construct search path list. @@ -508,9 +652,10 @@ void LinkerDriver::link(ArrayRef ArgsArr) { // Handle /noentry if (Args.hasArg(OPT_noentry)) { - if (!Args.hasArg(OPT_dll)) - fatal("/noentry must be specified with /dll"); - Config->NoEntry = true; + if (Args.hasArg(OPT_dll)) + Config->NoEntry = true; + else + error("/noentry must be specified with /dll"); } // Handle /dll @@ -521,12 +666,17 @@ void LinkerDriver::link(ArrayRef ArgsArr) { // Handle /fixed if (Args.hasArg(OPT_fixed)) { - if (Args.hasArg(OPT_dynamicbase)) - fatal("/fixed must not be specified with /dynamicbase"); - Config->Relocatable = false; - Config->DynamicBase = false; + if (Args.hasArg(OPT_dynamicbase)) { + error("/fixed must not be specified with /dynamicbase"); + } else { + Config->Relocatable = false; + Config->DynamicBase = false; + } } + if (Args.hasArg(OPT_appcontainer)) + Config->AppContainer = true; + // Handle /machine if (auto *Arg = Args.getLastArg(OPT_machine)) Config->Machine = getMachineType(Arg->getValue()); @@ -596,20 +746,31 @@ void LinkerDriver::link(ArrayRef ArgsArr) { StringRef OptLevel = StringRef(S).substr(7); if (OptLevel.getAsInteger(10, Config->LTOOptLevel) || Config->LTOOptLevel > 3) - fatal("/opt:lldlto: invalid optimization level: " + OptLevel); + error("/opt:lldlto: invalid optimization level: " + OptLevel); continue; } if (StringRef(S).startswith("lldltojobs=")) { StringRef Jobs = StringRef(S).substr(11); if (Jobs.getAsInteger(10, Config->LTOJobs) || Config->LTOJobs == 0) - fatal("/opt:lldltojobs: invalid job count: " + Jobs); + error("/opt:lldltojobs: invalid job count: " + Jobs); + continue; + } + if (StringRef(S).startswith("lldltopartitions=")) { + StringRef N = StringRef(S).substr(17); + if (N.getAsInteger(10, Config->LTOPartitions) || + Config->LTOPartitions == 0) + error("/opt:lldltopartitions: invalid partition count: " + N); continue; } if (S != "ref" && S != "lbr" && S != "nolbr") - fatal("/opt: unknown option: " + S); + error("/opt: unknown option: " + S); } } + // Handle /lldsavetemps + if (Args.hasArg(OPT_lldsavetemps)) + Config->SaveTemps = true; + // Handle /failifmismatch for (auto *Arg : Args.filtered(OPT_failifmismatch)) checkFailIfMismatch(Arg->getValue()); @@ -658,6 +819,11 @@ void LinkerDriver::link(ArrayRef ArgsArr) { Config->DumpPdb = Args.hasArg(OPT_dumppdb); Config->DebugPdb = Args.hasArg(OPT_debugpdb); + Config->MapFile = getMapFile(Args); + + if (ErrorCount) + return; + // Create a list of input files. Files can be given as arguments // for /defaultlib option. std::vector MBs; @@ -678,7 +844,7 @@ void LinkerDriver::link(ArrayRef ArgsArr) { // 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) { - errs() << "warning: /machine is not specified. x64 is assumed.\n"; + warn("/machine is not specified. x64 is assumed"); Config->Machine = AMD64; } @@ -715,8 +881,7 @@ void LinkerDriver::link(ArrayRef ArgsArr) { if (S.empty()) fatal("entry point must be defined"); Config->Entry = addUndefined(S); - if (Config->Verbose) - outs() << "Entry name inferred: " << S << "\n"; + log("Entry name inferred: " + S); } // Handle /export @@ -749,6 +914,22 @@ void LinkerDriver::link(ArrayRef ArgsArr) { } } + // Set default image name if neither /out or /def set it. + if (Config->OutputFile.empty()) { + Config->OutputFile = + getOutputPath((*Args.filtered(OPT_INPUT).begin())->getValue()); + } + + // Put the PDB next to the image if no /pdb flag was passed. + if (Config->Debug && Config->PDBPath.empty()) { + Config->PDBPath = Config->OutputFile; + sys::path::replace_extension(Config->PDBPath, ".pdb"); + } + + // Disable PDB generation if the user requested it. + if (Args.hasArg(OPT_nopdb)) + Config->PDBPath = ""; + // Set default image base if /base is not given. if (Config->ImageBase == uint64_t(-1)) Config->ImageBase = getDefaultImageBase(); @@ -801,6 +982,16 @@ void LinkerDriver::link(ArrayRef ArgsArr) { addUndefined(mangle("_load_config_used")); } while (run()); + if (ErrorCount) + return; + + // If /msvclto is given, we use the MSVC linker to link LTO output files. + // This is useful because MSVC link.exe can generate complete PDBs. + if (Args.hasArg(OPT_msvclto)) { + invokeMSVC(Args); + exit(0); + } + // Do LTO by compiling bitcode input files to a set of native COFF files then // link those files. Symtab.addCombinedLTOObjects(); @@ -818,10 +1009,13 @@ void LinkerDriver::link(ArrayRef ArgsArr) { } // Handle /safeseh. - if (Args.hasArg(OPT_safeseh)) + if (Args.hasArg(OPT_safeseh)) { for (ObjectFile *File : Symtab.ObjectFiles) if (!File->SEHCompat) - fatal("/safeseh: " + File->getName() + " is not compatible with SEH"); + error("/safeseh: " + File->getName() + " is not compatible with SEH"); + if (ErrorCount) + return; + } // Windows specific -- when we are creating a .dll file, we also // need to create a .lib file. @@ -846,17 +1040,6 @@ void LinkerDriver::link(ArrayRef ArgsArr) { // Write the result. writeResult(&Symtab); - // Create a symbol map file containing symbol VAs and their names - // to help debugging. - std::string MapFile = getMapFile(Args); - if (!MapFile.empty()) { - std::error_code EC; - raw_fd_ostream Out(MapFile, EC, OpenFlags::F_Text); - if (EC) - 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 44894269fcbe7..4566f73eef318 100644 --- a/COFF/Driver.h +++ b/COFF/Driver.h @@ -107,6 +107,8 @@ private: StringRef findDefaultEntry(); WindowsSubsystem inferSubsystem(); + void invokeMSVC(llvm::opt::InputArgList &Args); + MemoryBufferRef takeBuffer(std::unique_ptr MB); void addBuffer(std::unique_ptr MB); void addArchiveBuffer(MemoryBufferRef MBRef, StringRef SymName, @@ -178,6 +180,8 @@ void checkFailIfMismatch(StringRef Arg); std::unique_ptr convertResToCOFF(const std::vector &MBs); +void runMSVCLinker(std::string Rsp, ArrayRef Objects); + // 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 14dd004f1c049..a9c1c9d5593e0 100644 --- a/COFF/DriverUtils.cpp +++ b/COFF/DriverUtils.cpp @@ -44,31 +44,33 @@ namespace { class Executor { public: explicit Executor(StringRef S) : Saver(Alloc), Prog(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 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 run() { ErrorOr ExeOrErr = sys::findProgramByName(Prog); if (auto EC = ExeOrErr.getError()) fatal(EC, "unable to find " + Prog + " in PATH: "); - const char *Exe = Saver.save(*ExeOrErr).data(); + StringRef Exe = Saver.save(*ExeOrErr); Args.insert(Args.begin(), Exe); - Args.push_back(nullptr); - if (sys::ExecuteAndWait(Args[0], Args.data()) != 0) { - for (const char *S : Args) - if (S) - errs() << S << " "; - fatal("ExecuteAndWait failed"); - } + + std::vector Vec; + for (StringRef S : Args) + Vec.push_back(S.data()); + Vec.push_back(nullptr); + + if (sys::ExecuteAndWait(Args[0], Vec.data()) != 0) + fatal("ExecuteAndWait failed: " + + llvm::join(Args.begin(), Args.end(), " ")); } private: BumpPtrAllocator Alloc; StringSaver Saver; StringRef Prog; - std::vector Args; + std::vector Args; }; } // anonymous namespace @@ -167,8 +169,7 @@ void parseMerge(StringRef S) { if (!Inserted) { StringRef Existing = Pair.first->second; if (Existing != To) - errs() << "warning: " << S << ": already merged into " << Existing - << "\n"; + warn(S + ": already merged into " + Existing); } } @@ -282,11 +283,19 @@ static void quoteAndPrint(raw_ostream &Out, StringRef S) { namespace { class TemporaryFile { public: - TemporaryFile(StringRef Prefix, StringRef Extn) { + TemporaryFile(StringRef Prefix, StringRef Extn, StringRef Contents = "") { SmallString<128> S; if (auto EC = sys::fs::createTemporaryFile("lld-" + Prefix, Extn, S)) fatal(EC, "cannot create a temporary file"); Path = S.str(); + + if (!Contents.empty()) { + std::error_code EC; + raw_fd_ostream OS(Path, EC, sys::fs::F_None); + if (EC) + fatal(EC, "failed to open " + Path); + OS << Contents; + } } TemporaryFile(TemporaryFile &&Obj) { @@ -542,7 +551,7 @@ void fixupExports() { Export *Existing = Pair.first->second; if (E == *Existing || E.Name != Existing->Name) continue; - errs() << "warning: duplicate /export option: " << E.Name << "\n"; + warn("duplicate /export option: " + E.Name); } Config->Exports = std::move(V); @@ -617,6 +626,26 @@ convertResToCOFF(const std::vector &MBs) { return File.getMemoryBuffer(); } +// Run MSVC link.exe for given in-memory object files. +// Command line options are copied from those given to LLD. +// This is for the /msvclto option. +void runMSVCLinker(std::string Rsp, ArrayRef Objects) { + // Write the in-memory object files to disk. + std::vector Temps; + for (StringRef S : Objects) { + Temps.emplace_back("lto", "obj", S); + Rsp += quote(Temps.back().Path) + " "; + } + + log("link.exe " + Rsp); + + // Run MSVC link.exe. + Temps.emplace_back("lto", "rsp", Rsp); + Executor E("link.exe"); + E.add(Twine("@" + Temps.back().Path)); + E.run(); +} + // Create OptTable // Create prefix string literals used in Options.td @@ -653,16 +682,16 @@ opt::InputArgList ArgParser::parse(ArrayRef ArgsArr) { // Print the real command line if response files are expanded. if (Args.hasArg(OPT_verbose) && ArgsArr.size() != Argv.size()) { - outs() << "Command line:"; + std::string Msg = "Command line:"; for (const char *S : Argv) - outs() << " " << S; - outs() << "\n"; + Msg += " " + std::string(S); + message(Msg); } if (MissingCount) fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument"); for (auto *Arg : Args.filtered(OPT_UNKNOWN)) - errs() << "ignoring unknown argument: " << Arg->getSpelling() << "\n"; + warn("ignoring unknown argument: " + Arg->getSpelling()); return Args; } diff --git a/COFF/Error.cpp b/COFF/Error.cpp index b2bd557413df0..b2c7c89bd36c5 100644 --- a/COFF/Error.cpp +++ b/COFF/Error.cpp @@ -8,11 +8,14 @@ //===----------------------------------------------------------------------===// #include "Error.h" +#include "Config.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/Error.h" +#include "llvm/Support/ManagedStatic.h" #include "llvm/Support/Process.h" #include "llvm/Support/raw_ostream.h" +#include #if !defined(_MSC_VER) && !defined(__MINGW32__) #include @@ -21,10 +24,68 @@ using namespace llvm; namespace lld { +// The functions defined in this file can be called from multiple threads, +// but outs() or errs() are not thread-safe. We protect them using a mutex. +static std::mutex Mu; + namespace coff { +StringRef Argv0; +uint64_t ErrorCount; +raw_ostream *ErrorOS; + +static LLVM_ATTRIBUTE_NORETURN void exitLld(int Val) { + // Dealloc/destroy ManagedStatic variables before calling + // _exit(). In a non-LTO build, this is a nop. In an LTO + // build allows us to get the output of -time-passes. + llvm_shutdown(); + + outs().flush(); + errs().flush(); + _exit(Val); +} + +static void print(StringRef S, raw_ostream::Colors C) { + *ErrorOS << Argv0 + ": "; + if (Config->ColorDiagnostics) { + ErrorOS->changeColor(C, true); + *ErrorOS << S; + ErrorOS->resetColor(); + } else { + *ErrorOS << S; + } +} + +void log(const Twine &Msg) { + if (Config->Verbose) { + std::lock_guard Lock(Mu); + outs() << Argv0 << ": " << Msg << "\n"; + } +} + +void message(const Twine &Msg) { + std::lock_guard Lock(Mu); + outs() << Msg << "\n"; + outs().flush(); +} + +void error(const Twine &Msg) { + std::lock_guard Lock(Mu); + + if (Config->ErrorLimit == 0 || ErrorCount < Config->ErrorLimit) { + print("error: ", raw_ostream::RED); + *ErrorOS << Msg << "\n"; + } else if (ErrorCount == Config->ErrorLimit) { + print("error: ", raw_ostream::RED); + *ErrorOS << "too many errors emitted, stopping now" + << " (use /ERRORLIMIT:0 to see all errors)\n"; + exitLld(1); + } + + ++ErrorCount; +} void fatal(const Twine &Msg) { - if (sys::Process::StandardErrHasColors()) { + if (Config->ColorDiagnostics) { errs().changeColor(raw_ostream::RED, /*bold=*/true); errs() << "error: "; errs().resetColor(); @@ -32,10 +93,7 @@ void fatal(const Twine &Msg) { errs() << "error: "; } errs() << Msg << "\n"; - - outs().flush(); - errs().flush(); - _exit(1); + exitLld(1); } void fatal(std::error_code EC, const Twine &Msg) { @@ -46,5 +104,11 @@ void fatal(llvm::Error &Err, const Twine &Msg) { fatal(errorToErrorCode(std::move(Err)), Msg); } +void warn(const Twine &Msg) { + std::lock_guard Lock(Mu); + print("warning: ", raw_ostream::MAGENTA); + *ErrorOS << Msg << "\n"; +} + } // namespace coff } // namespace lld diff --git a/COFF/Error.h b/COFF/Error.h index 47549327db2be..a4f44fb1e36c7 100644 --- a/COFF/Error.h +++ b/COFF/Error.h @@ -16,11 +16,19 @@ namespace lld { namespace coff { +extern uint64_t ErrorCount; +extern llvm::raw_ostream *ErrorOS; +extern llvm::StringRef Argv0; + +void log(const Twine &Msg); +void message(const Twine &Msg); +void warn(const Twine &Msg); +void error(const Twine &Msg); LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg); LLVM_ATTRIBUTE_NORETURN void fatal(std::error_code EC, const Twine &Prefix); LLVM_ATTRIBUTE_NORETURN void fatal(llvm::Error &Err, const Twine &Prefix); -template T check(ErrorOr &&V, const Twine &Prefix) { +template T check(ErrorOr V, const Twine &Prefix) { if (auto EC = V.getError()) fatal(EC, Prefix); return std::move(*V); diff --git a/COFF/ICF.cpp b/COFF/ICF.cpp index 196fbe2610ea7..19468c0fac5e9 100644 --- a/COFF/ICF.cpp +++ b/COFF/ICF.cpp @@ -231,19 +231,16 @@ void ICF::run(const std::vector &Vec) { ++Cnt; } while (Repeat); - if (Config->Verbose) - outs() << "\nICF needed " << Cnt << " iterations\n"; + log("ICF needed " + Twine(Cnt) + " iterations"); // Merge sections in the same colors. forEachColor([&](size_t Begin, size_t End) { if (End - Begin == 1) return; - if (Config->Verbose) - outs() << "Selected " << Chunks[Begin]->getDebugName() << "\n"; + log("Selected " + Chunks[Begin]->getDebugName()); for (size_t I = Begin + 1; I < End; ++I) { - if (Config->Verbose) - outs() << " Removed " << Chunks[I]->getDebugName() << "\n"; + log(" Removed " + Chunks[I]->getDebugName()); Chunks[Begin]->replace(Chunks[I]); } }); diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp index cde355cd3f343..cb56e13014db9 100644 --- a/COFF/InputFiles.cpp +++ b/COFF/InputFiles.cpp @@ -19,8 +19,6 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Triple.h" #include "llvm/ADT/Twine.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/LTO/legacy/LTOModule.h" #include "llvm/Object/Binary.h" #include "llvm/Object/COFF.h" #include "llvm/Support/COFF.h" @@ -41,13 +39,23 @@ 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 { -LLVMContext BitcodeFile::Context; +/// Checks that Source is compatible with being a weak alias to Target. +/// If Source is Undefined and has no weak alias set, makes it a weak +/// alias to Target. +static void checkAndSetWeakAlias(SymbolTable *Symtab, InputFile *F, + SymbolBody *Source, SymbolBody *Target) { + auto *U = dyn_cast(Source); + if (!U) + return; + else if (!U->WeakAlias) + U->WeakAlias = Target; + else if (U->WeakAlias != Target) + Symtab->reportDuplicate(Source->symbol(), F); +} ArchiveFile::ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {} @@ -177,15 +185,9 @@ void ObjectFile::initializeSymbols() { I += Sym.getNumberOfAuxSymbols(); LastSectionNumber = Sym.getSectionNumber(); } - 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]; - } + for (auto WeakAlias : WeakAliases) + checkAndSetWeakAlias(Symtab, this, WeakAlias.first, + SparseSymbolBodies[WeakAlias.second]); } SymbolBody *ObjectFile::createUndefined(COFFSymbolRef Sym) { @@ -200,7 +202,10 @@ SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP, if (Sym.isCommon()) { auto *C = new (Alloc) CommonChunk(Sym); Chunks.push_back(C); - return Symtab->addCommon(this, Sym, C)->body(); + COFFObj->getSymbolName(Sym, Name); + Symbol *S = + Symtab->addCommon(this, Name, Sym.getValue(), Sym.getGeneric(), C); + return S->body(); } if (Sym.isAbsolute()) { COFFObj->getSymbolName(Sym, Name); @@ -247,10 +252,14 @@ SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP, } DefinedRegular *B; - if (Sym.isExternal()) - B = cast(Symtab->addRegular(this, Sym, SC)->body()); - else - B = new (Alloc) DefinedRegular(this, Sym, SC); + if (Sym.isExternal()) { + COFFObj->getSymbolName(Sym, Name); + Symbol *S = + Symtab->addRegular(this, Name, SC->isCOMDAT(), Sym.getGeneric(), SC); + B = cast(S->body()); + } else + B = new (Alloc) DefinedRegular(this, /*Name*/ "", SC->isCOMDAT(), + /*IsExternal*/ false, Sym.getGeneric(), SC); if (SC->isCOMDAT() && Sym.getValue() == 0 && !AuxP) SC->setSymbol(B); @@ -329,39 +338,32 @@ void ImportFile::parse() { } void BitcodeFile::parse() { - Context.enableDebugTypeODRUniquing(); - ErrorOr> ModOrErr = LTOModule::createFromBuffer( - Context, MB.getBufferStart(), MB.getBufferSize(), llvm::TargetOptions()); - M = check(std::move(ModOrErr), "could not create LTO module"); - - 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) - continue; - - StringRef SymName = Saver.save(M->getSymbolName(I)); - int SymbolDef = Attrs & LTO_SYMBOL_DEFINITION_MASK; - if (SymbolDef == LTO_SYMBOL_DEFINITION_UNDEFINED) { - SymbolBodies.push_back(Symtab->addUndefined(SymName, this, false)->body()); + Obj = check(lto::InputFile::create(MemoryBufferRef( + MB.getBuffer(), Saver.save(ParentName + MB.getBufferIdentifier())))); + for (const lto::InputFile::Symbol &ObjSym : Obj->symbols()) { + StringRef SymName = Saver.save(ObjSym.getName()); + Symbol *Sym; + if (ObjSym.isUndefined()) { + Sym = Symtab->addUndefined(SymName, this, false); + } else if (ObjSym.isCommon()) { + Sym = Symtab->addCommon(this, SymName, ObjSym.getCommonSize()); + } else if (ObjSym.isWeak() && ObjSym.isIndirect()) { + // Weak external. + Sym = Symtab->addUndefined(SymName, this, true); + std::string Fallback = ObjSym.getCOFFWeakExternalFallback(); + SymbolBody *Alias = Symtab->addUndefined(Saver.save(Fallback)); + checkAndSetWeakAlias(Symtab, this, Sym->body(), Alias); } 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( - Symtab->addBitcode(this, SymName, Replaceable)->body()); + bool IsCOMDAT = ObjSym.getComdatIndex() != -1; + Sym = Symtab->addRegular(this, SymName, IsCOMDAT); } + SymbolBodies.push_back(Sym->body()); } - - Directives = M->getLinkerOpts(); + Directives = Obj->getCOFFLinkerOpts(); } MachineTypes BitcodeFile::getMachineType() { - if (!M) - return IMAGE_FILE_MACHINE_UNKNOWN; - switch (Triple(M->getTargetTriple()).getArch()) { + switch (Triple(Obj->getTargetTriple()).getArch()) { case Triple::x86_64: return AMD64; case Triple::x86: diff --git a/COFF/InputFiles.h b/COFF/InputFiles.h index 1b5d42939ccab..9e02b2fc68bb8 100644 --- a/COFF/InputFiles.h +++ b/COFF/InputFiles.h @@ -13,8 +13,7 @@ #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/LTO/LTO.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFF.h" #include "llvm/Support/StringSaver.h" @@ -25,7 +24,6 @@ namespace lld { namespace coff { -using llvm::LTOModule; using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN; using llvm::COFF::MachineTypes; using llvm::object::Archive; @@ -174,7 +172,6 @@ public: private: void parse() override; - llvm::BumpPtrAllocator Alloc; llvm::BumpPtrAllocator StringAllocAux; llvm::StringSaver StringAlloc; @@ -191,16 +188,12 @@ public: static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; } std::vector &getSymbols() { return SymbolBodies; } MachineTypes getMachineType() override; - std::unique_ptr takeModule() { return std::move(M); } - - static llvm::LLVMContext Context; + std::unique_ptr Obj; private: void parse() override; std::vector SymbolBodies; - llvm::BumpPtrAllocator Alloc; - std::unique_ptr M; }; } // namespace coff diff --git a/COFF/LTO.cpp b/COFF/LTO.cpp new file mode 100644 index 0000000000000..6883b3b4c2c83 --- /dev/null +++ b/COFF/LTO.cpp @@ -0,0 +1,140 @@ +//===- LTO.cpp ------------------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LTO.h" +#include "Config.h" +#include "Error.h" +#include "InputFiles.h" +#include "Symbols.h" +#include "lld/Core/TargetOptionsCommandFlags.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/IR/DiagnosticPrinter.h" +#include "llvm/LTO/Config.h" +#include "llvm/LTO/LTO.h" +#include "llvm/Object/SymbolicFile.h" +#include "llvm/Support/CodeGen.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include +#include +#include + +using namespace llvm; +using namespace llvm::object; + +using namespace lld; +using namespace lld::coff; + +static void diagnosticHandler(const DiagnosticInfo &DI) { + SmallString<128> ErrStorage; + raw_svector_ostream OS(ErrStorage); + DiagnosticPrinterRawOStream DP(OS); + DI.print(DP); + warn(ErrStorage); +} + +static void checkError(Error E) { + handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) -> Error { + error(EIB.message()); + return Error::success(); + }); +} + +static void saveBuffer(StringRef Buffer, const Twine &Path) { + std::error_code EC; + raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None); + if (EC) + error("cannot create " + Path + ": " + EC.message()); + OS << Buffer; +} + +static std::unique_ptr createLTO() { + lto::Config Conf; + Conf.Options = InitTargetOptionsFromCodeGenFlags(); + Conf.RelocModel = Reloc::PIC_; + Conf.DisableVerify = true; + Conf.DiagHandler = diagnosticHandler; + Conf.OptLevel = Config->LTOOptLevel; + if (Config->SaveTemps) + checkError(Conf.addSaveTemps(std::string(Config->OutputFile) + ".", + /*UseInputModulePath*/ true)); + lto::ThinBackend Backend; + if (Config->LTOJobs != 0) + Backend = lto::createInProcessThinBackend(Config->LTOJobs); + return llvm::make_unique(std::move(Conf), Backend, + Config->LTOPartitions); +} + +BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) {} + +BitcodeCompiler::~BitcodeCompiler() = default; + +static void undefine(Symbol *S) { + replaceBody(S, S->body()->getName()); +} + +void BitcodeCompiler::add(BitcodeFile &F) { + lto::InputFile &Obj = *F.Obj; + unsigned SymNum = 0; + std::vector SymBodies = F.getSymbols(); + std::vector Resols(SymBodies.size()); + + // Provide a resolution to the LTO API for each symbol. + for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) { + SymbolBody *B = SymBodies[SymNum]; + Symbol *Sym = B->symbol(); + lto::SymbolResolution &R = Resols[SymNum]; + ++SymNum; + + // Ideally we shouldn't check for SF_Undefined but currently IRObjectFile + // reports two symbols for module ASM defined. Without this check, lld + // flags an undefined in IR with a definition in ASM as prevailing. + // Once IRObjectFile is fixed to report only one symbol this hack can + // be removed. + R.Prevailing = !ObjSym.isUndefined() && B->getFile() == &F; + R.VisibleToRegularObj = Sym->IsUsedInRegularObj; + if (R.Prevailing) + undefine(Sym); + } + checkError(LTOObj->add(std::move(F.Obj), Resols)); +} + +// Merge all the bitcode files we have seen, codegen the result +// and return the resulting objects. +std::vector BitcodeCompiler::compile() { + unsigned MaxTasks = LTOObj->getMaxTasks(); + Buff.resize(MaxTasks); + + checkError(LTOObj->run([&](size_t Task) { + return llvm::make_unique( + llvm::make_unique(Buff[Task])); + })); + + std::vector Ret; + for (unsigned I = 0; I != MaxTasks; ++I) { + if (Buff[I].empty()) + continue; + if (Config->SaveTemps) { + if (I == 0) + saveBuffer(Buff[I], Config->OutputFile + ".lto.obj"); + else + saveBuffer(Buff[I], Config->OutputFile + Twine(I) + ".lto.obj"); + } + Ret.emplace_back(Buff[I].data(), Buff[I].size()); + } + return Ret; +} diff --git a/COFF/LTO.h b/COFF/LTO.h new file mode 100644 index 0000000000000..194a4cce8adaf --- /dev/null +++ b/COFF/LTO.h @@ -0,0 +1,56 @@ +//===- LTO.h ----------------------------------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides a way to combine bitcode files into one COFF +// file by compiling them using LLVM. +// +// If LTO is in use, your input files are not in regular COFF files +// but instead LLVM bitcode files. In that case, the linker has to +// convert bitcode files into the native format so that we can create +// a COFF file that contains native code. This file provides that +// functionality. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_COFF_LTO_H +#define LLD_COFF_LTO_H + +#include "lld/Core/LLVM.h" +#include "llvm/ADT/SmallString.h" +#include +#include + +namespace llvm { +namespace lto { +class LTO; +} +} + +namespace lld { +namespace coff { + +class BitcodeFile; +class InputFile; + +class BitcodeCompiler { +public: + BitcodeCompiler(); + ~BitcodeCompiler(); + + void add(BitcodeFile &F); + std::vector compile(); + +private: + std::unique_ptr LTOObj; + std::vector> Buff; +}; +} +} + +#endif diff --git a/COFF/Librarian.cpp b/COFF/Librarian.cpp index 4c597fad73455..3ce72822180b0 100644 --- a/COFF/Librarian.cpp +++ b/COFF/Librarian.cpp @@ -104,7 +104,18 @@ static ImportNameType getNameType(StringRef Sym, StringRef ExtName) { static std::string replace(StringRef S, StringRef From, StringRef To) { size_t Pos = S.find(From); - assert(Pos != StringRef::npos); + + // From and To may be mangled, but substrings in S may not. + if (Pos == StringRef::npos && From.startswith("_") && To.startswith("_")) { + From = From.substr(1); + To = To.substr(1); + Pos = S.find(From); + } + + if (Pos == StringRef::npos) { + error(S + ": replacing '" + From + "' with '" + To + "' failed"); + return ""; + } return (Twine(S.substr(0, Pos)) + To + S.substr(Pos + From.size())).str(); } diff --git a/COFF/MapFile.cpp b/COFF/MapFile.cpp new file mode 100644 index 0000000000000..43dd8dc358107 --- /dev/null +++ b/COFF/MapFile.cpp @@ -0,0 +1,114 @@ +//===- MapFile.cpp --------------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the /lldmap option. It shows lists in order and +// hierarchically the output sections, input sections, input files and +// symbol: +// +// Address Size Align Out In File Symbol +// ================================================================= +// 00201000 00000015 4 .text +// 00201000 0000000e 4 .text +// 00201000 0000000e 4 test.o +// 0020100e 00000000 0 local +// 00201005 00000000 0 f(int) +// +//===----------------------------------------------------------------------===// + +#include "MapFile.h" +#include "Error.h" +#include "Symbols.h" +#include "Writer.h" + +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace llvm::object; + +using namespace lld; +using namespace lld::coff; + +static void writeOutSecLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size, + uint64_t Align, StringRef Name) { + OS << format("%08llx %08llx %5lld ", Address, Size, Align) + << left_justify(Name, 7); +} + +static void writeInSecLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size, + uint64_t Align, StringRef Name) { + // Pass an empty name to align the text to the correct column. + writeOutSecLine(OS, Address, Size, Align, ""); + OS << ' ' << left_justify(Name, 7); +} + +static void writeFileLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size, + uint64_t Align, StringRef Name) { + // Pass an empty name to align the text to the correct column. + writeInSecLine(OS, Address, Size, Align, ""); + OS << ' ' << left_justify(Name, 7); +} + +static void writeSymbolLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size, + StringRef Name) { + // Pass an empty name to align the text to the correct column. + writeFileLine(OS, Address, Size, 0, ""); + OS << ' ' << left_justify(Name, 7); +} + +static void writeSectionChunk(raw_fd_ostream &OS, const SectionChunk *SC, + StringRef &PrevName) { + StringRef Name = SC->getSectionName(); + if (Name != PrevName) { + writeInSecLine(OS, SC->getRVA(), SC->getSize(), SC->getAlign(), Name); + OS << '\n'; + PrevName = Name; + } + coff::ObjectFile *File = SC->File; + if (!File) + return; + writeFileLine(OS, SC->getRVA(), SC->getSize(), SC->getAlign(), + toString(File)); + OS << '\n'; + ArrayRef Syms = File->getSymbols(); + for (SymbolBody *Sym : Syms) { + auto *DR = dyn_cast(Sym); + if (!DR || DR->getChunk() != SC || + DR->getCOFFSymbol().isSectionDefinition()) + continue; + writeSymbolLine(OS, DR->getRVA(), SC->getSize(), toString(*Sym)); + OS << '\n'; + } +} + +static void writeMapFile2(raw_fd_ostream &OS, + ArrayRef OutputSections) { + OS << "Address Size Align Out In File Symbol\n"; + + for (OutputSection *Sec : OutputSections) { + uint32_t VA = Sec->getRVA(); + writeOutSecLine(OS, VA, Sec->getVirtualSize(), /*Align=*/PageSize, + Sec->getName()); + OS << '\n'; + StringRef PrevName = ""; + for (Chunk *C : Sec->getChunks()) + if (const auto *SC = dyn_cast(C)) + writeSectionChunk(OS, SC, PrevName); + } +} + +void coff::writeMapFile(ArrayRef OutputSections) { + if (Config->MapFile.empty()) + return; + + std::error_code EC; + raw_fd_ostream OS(Config->MapFile, EC, sys::fs::F_None); + if (EC) + fatal("cannot open " + Config->MapFile + ": " + EC.message()); + writeMapFile2(OS, OutputSections); +} diff --git a/COFF/MapFile.h b/COFF/MapFile.h new file mode 100644 index 0000000000000..0d0d68ce3ead0 --- /dev/null +++ b/COFF/MapFile.h @@ -0,0 +1,22 @@ +//===- MapFile.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_MAPFILE_H +#define LLD_COFF_MAPFILE_H + +#include "llvm/ADT/ArrayRef.h" + +namespace lld { +namespace coff { +class OutputSection; +void writeMapFile(llvm::ArrayRef OutputSections); +} +} + +#endif diff --git a/COFF/ModuleDef.cpp b/COFF/ModuleDef.cpp index a273b6f535db6..c9a40ac5ab8c6 100644 --- a/COFF/ModuleDef.cpp +++ b/COFF/ModuleDef.cpp @@ -163,17 +163,25 @@ private: case KwHeapsize: parseNumbers(&Config->HeapReserve, &Config->HeapCommit); return; - case KwLibrary: - parseName(&Config->OutputFile, &Config->ImageBase); - if (!StringRef(Config->OutputFile).endswith_lower(".dll")) - Config->OutputFile += ".dll"; - return; case KwStacksize: parseNumbers(&Config->StackReserve, &Config->StackCommit); return; - case KwName: - parseName(&Config->OutputFile, &Config->ImageBase); + case KwLibrary: + case KwName: { + bool IsDll = Tok.K == KwLibrary; // Check before parseName. + std::string Name; + parseName(&Name, &Config->ImageBase); + + // Append the appropriate file extension if not already present. + StringRef Ext = IsDll ? ".dll" : ".exe"; + if (!StringRef(Name).endswith_lower(Ext)) + Name += Ext; + + // Set the output file, but don't override /out if it was already passed. + if (Config->OutputFile.empty()) + Config->OutputFile = Name; return; + } case KwVersion: parseVersion(&Config->MajorImageVersion, &Config->MinorImageVersion); return; diff --git a/COFF/Options.td b/COFF/Options.td index 9dfbcc8e188cc..7b5573b31cd33 100644 --- a/COFF/Options.td +++ b/COFF/Options.td @@ -21,6 +21,8 @@ def base : P<"base", "Base address of the program">; def defaultlib : P<"defaultlib", "Add the library to the list of input files">; def delayload : P<"delayload", "Delay loaded DLL name">; def entry : P<"entry", "Name of entry point symbol">; +def errorlimit : P<"errorlimit", + "Maximum number of errors to emit before stopping (0 = no limit)">; def export : P<"export", "Export a function">; // No help text because /failifmismatch is not intended to be used by the user. def failifmismatch : P<"failifmismatch", "">; @@ -28,6 +30,8 @@ def heap : P<"heap", "Size of the heap">; def implib : P<"implib", "Import library name">; def libpath : P<"libpath", "Additional library search path">; def linkrepro : P<"linkrepro", "Dump linker invocation and input files for debugging">; +def lldsavetemps : F<"lldsavetemps">, + HelpText<"Save temporary files instead of deleting them">; def machine : P<"machine", "Specify target platform">; def merge : P<"merge", "Combine sections">; def mllvm : P<"mllvm", "Options to pass to LLVM">; @@ -78,6 +82,8 @@ def force_unresolved : F<"force:unresolved">; defm allowbind: B<"allowbind", "Disable DLL binding">; defm allowisolation : B<"allowisolation", "Set NO_ISOLATION bit">; +defm appcontainer : B<"appcontainer", + "Image can only be run in an app container">; defm dynamicbase : B<"dynamicbase", "Disable address space layout randomization">; defm fixed : B<"fixed", "Enable base relocations">; @@ -91,7 +97,9 @@ def help : F<"help">; def help_q : Flag<["/?", "-?"], "">, Alias; // LLD extensions +def nopdb : F<"nopdb">, HelpText<"Disable PDB generation for DWARF users">; def nosymtab : F<"nosymtab">; +def msvclto : F<"msvclto">; // Flags for debugging def debugpdb : F<"debugpdb">; diff --git a/COFF/PDB.cpp b/COFF/PDB.cpp index 923fc64c77341..e32bcd20a5416 100644 --- a/COFF/PDB.cpp +++ b/COFF/PDB.cpp @@ -20,20 +20,23 @@ #include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h" #include "llvm/DebugInfo/CodeView/TypeStreamMerger.h" #include "llvm/DebugInfo/CodeView/TypeTableBuilder.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/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/InfoStream.h" +#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h" +#include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h" +#include "llvm/DebugInfo/PDB/Native/PDBTypeServerHandler.h" +#include "llvm/DebugInfo/PDB/Native/StringTableBuilder.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" +#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h" #include "llvm/Object/COFF.h" +#include "llvm/Support/BinaryByteStream.h" #include "llvm/Support/Endian.h" #include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" #include @@ -79,32 +82,49 @@ static ArrayRef getDebugSection(ObjectFile *File, StringRef SecName) { return Data.slice(4); } -// Merge .debug$T sections and returns it. -static std::vector mergeDebugT(SymbolTable *Symtab) { - ScopedPrinter W(outs()); +static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder, + codeview::TypeTableBuilder &TypeTable) { + // Start the TPI or IPI stream header. + TpiBuilder.setVersionHeader(pdb::PdbTpiV80); + + // Flatten the in memory type table. + TypeTable.ForEachRecord([&](TypeIndex TI, ArrayRef Rec) { + // FIXME: Hash types. + TpiBuilder.addTypeRecord(Rec, None); + }); +} +// Merge .debug$T sections into IpiData and TpiData. +static void mergeDebugT(SymbolTable *Symtab, pdb::PDBFileBuilder &Builder, + codeview::TypeTableBuilder &TypeTable, + codeview::TypeTableBuilder &IDTable) { // Visit all .debug$T sections to add them to Builder. - codeview::TypeTableBuilder Builder(BAlloc); for (ObjectFile *File : Symtab->ObjectFiles) { ArrayRef Data = getDebugSection(File, ".debug$T"); if (Data.empty()) continue; - msf::ByteStream Stream(Data); + BinaryByteStream Stream(Data, support::little); codeview::CVTypeArray Types; - msf::StreamReader Reader(Stream); + BinaryStreamReader Reader(Stream); + // Follow type servers. If the same type server is encountered more than + // once for this instance of `PDBTypeServerHandler` (for example if many + // object files reference the same TypeServer), the types from the + // TypeServer will only be visited once. + pdb::PDBTypeServerHandler Handler; + Handler.addSearchPath(llvm::sys::path::parent_path(File->getName())); if (auto EC = Reader.readArray(Types, Reader.getLength())) fatal(EC, "Reader::readArray failed"); - if (!codeview::mergeTypeStreams(Builder, Types)) - fatal("codeview::mergeTypeStreams failed"); + if (auto Err = + codeview::mergeTypeStreams(IDTable, TypeTable, &Handler, Types)) + fatal(Err, "codeview::mergeTypeStreams failed"); } - // Construct section contents. - std::vector V; - Builder.ForEachRecord([&](TypeIndex TI, ArrayRef Rec) { - V.insert(V.end(), Rec.begin(), Rec.end()); - }); - return V; + // Construct TPI stream contents. + addTypeInfo(Builder.getTpiBuilder(), TypeTable); + + // Construct IPI stream contents. + addTypeInfo(Builder.getIpiBuilder(), IDTable); } static void dumpDebugT(ScopedPrinter &W, ObjectFile *File) { @@ -115,6 +135,8 @@ static void dumpDebugT(ScopedPrinter &W, ObjectFile *File) { TypeDatabase TDB; TypeDumpVisitor TDV(TDB, &W, false); + // Use a default implementation that does not follow type servers and instead + // just dumps the contents of the TypeServer2 record. CVTypeDumper TypeDumper(TDB); if (auto EC = TypeDumper.dump(Data, TDV)) fatal(EC, "CVTypeDumper::dump failed"); @@ -126,9 +148,9 @@ static void dumpDebugS(ScopedPrinter &W, ObjectFile *File) { if (Data.empty()) return; - msf::ByteStream Stream(Data); + BinaryByteStream Stream(Data, llvm::support::little); CVSymbolArray Symbols; - msf::StreamReader Reader(Stream); + BinaryStreamReader Reader(Stream); if (auto EC = Reader.readArray(Symbols, Reader.getLength())) fatal(EC, "StreamReader.readArray failed"); @@ -148,17 +170,6 @@ static void dumpCodeView(SymbolTable *Symtab) { } } -static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder, - ArrayRef Data) { - 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, @@ -177,9 +188,12 @@ void coff::createPDB(StringRef Path, SymbolTable *Symtab, // Add an Info stream. auto &InfoBuilder = Builder.getInfoBuilder(); - InfoBuilder.setAge(DI->PDB70.Age); - InfoBuilder.setGuid( - *reinterpret_cast(&DI->PDB70.Signature)); + InfoBuilder.setAge(DI ? DI->PDB70.Age : 0); + + pdb::PDB_UniqueId uuid{}; + if (DI) + memcpy(&uuid, &DI->PDB70.Signature, sizeof(uuid)); + InfoBuilder.setGuid(uuid); // Should be the current time, but set 0 for reproducibilty. InfoBuilder.setSignature(0); InfoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70); @@ -188,18 +202,9 @@ void coff::createPDB(StringRef Path, SymbolTable *Symtab, auto &DbiBuilder = Builder.getDbiBuilder(); DbiBuilder.setVersionHeader(pdb::PdbDbiV110); - // Add an empty TPI stream. - auto &TpiBuilder = Builder.getTpiBuilder(); - TpiBuilder.setVersionHeader(pdb::PdbTpiV80); - std::vector TpiData; - if (Config->DebugPdb) { - TpiData = mergeDebugT(Symtab); - addTypeInfo(TpiBuilder, TpiData); - } - - // Add an empty IPI stream. - auto &IpiBuilder = Builder.getIpiBuilder(); - IpiBuilder.setVersionHeader(pdb::PdbTpiV80); + codeview::TypeTableBuilder TypeTable(BAlloc); + codeview::TypeTableBuilder IDTable(BAlloc); + mergeDebugT(Symtab, Builder, TypeTable, IDTable); // Add Section Contributions. std::vector Contribs = @@ -214,7 +219,7 @@ void coff::createPDB(StringRef Path, SymbolTable *Symtab, pdb::DbiStreamBuilder::createSectionMap(Sections); DbiBuilder.setSectionMap(SectionMap); - ExitOnErr(DbiBuilder.addModuleInfo("", "* Linker *")); + ExitOnErr(DbiBuilder.addModuleInfo("* Linker *")); // Add COFF section header stream. ExitOnErr( diff --git a/COFF/SymbolTable.cpp b/COFF/SymbolTable.cpp index 9cc0b75c1510a..310eab2745265 100644 --- a/COFF/SymbolTable.cpp +++ b/COFF/SymbolTable.cpp @@ -11,10 +11,10 @@ #include "Config.h" #include "Driver.h" #include "Error.h" +#include "LTO.h" #include "Memory.h" #include "Symbols.h" #include "llvm/IR/LLVMContext.h" -#include "llvm/LTO/legacy/LTOCodeGenerator.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include @@ -24,11 +24,40 @@ using namespace llvm; namespace lld { namespace coff { +enum SymbolPreference { + SP_EXISTING = -1, + SP_CONFLICT = 0, + SP_NEW = 1, +}; + +/// Checks if an existing symbol S should be kept or replaced by a new symbol. +/// Returns SP_EXISTING when S should be kept, SP_NEW when the new symbol +/// should be kept, and SP_CONFLICT if no valid resolution exists. +static SymbolPreference compareDefined(Symbol *S, bool WasInserted, + bool NewIsCOMDAT) { + // If the symbol wasn't previously known, the new symbol wins by default. + if (WasInserted || !isa(S->body())) + return SP_NEW; + + // If the existing symbol is a DefinedRegular, both it and the new symbol + // must be comdats. In that case, we have no reason to prefer one symbol + // over the other, and we keep the existing one. If one of the symbols + // is not a comdat, we report a conflict. + if (auto *R = dyn_cast(S->body())) { + if (NewIsCOMDAT && R->isCOMDAT()) + return SP_EXISTING; + else + return SP_CONFLICT; + } + + // Existing symbol is not a DefinedRegular; new symbol wins. + return SP_NEW; +} + SymbolTable *Symtab; void SymbolTable::addFile(InputFile *File) { - if (Config->Verbose) - outs() << "Reading " << toString(File) << "\n"; + log("Reading " + toString(File)); File->parse(); MachineTypes MT = File->getMachineType(); @@ -51,8 +80,7 @@ void SymbolTable::addFile(InputFile *File) { if (S.empty()) return; - if (Config->Verbose) - outs() << "Directives: " << toString(File) << ": " << S << "\n"; + log("Directives: " + toString(File) + ": " + S); Driver->parseDirectives(S); } @@ -106,12 +134,11 @@ void SymbolTable::reportRemainingUndefines() { return; for (SymbolBody *B : Config->GCRoot) if (Undefs.count(B)) - errs() << ": undefined symbol: " << B->getName() << "\n"; + warn(": undefined symbol: " + B->getName()); for (ObjectFile *File : ObjectFiles) for (SymbolBody *Sym : File->getSymbols()) if (Undefs.count(Sym)) - errs() << toString(File) << ": undefined symbol: " << Sym->getName() - << "\n"; + warn(toString(File) + ": undefined symbol: " + Sym->getName()); if (!Config->Force) fatal("link failed"); } @@ -163,7 +190,7 @@ void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol Sym) { } void SymbolTable::reportDuplicate(Symbol *Existing, InputFile *NewFile) { - fatal("duplicate symbol: " + toString(*Existing->body()) + " in " + + error("duplicate symbol: " + toString(*Existing->body()) + " in " + toString(Existing->body()->getFile()) + " and in " + (NewFile ? toString(NewFile) : "(internal)")); } @@ -204,59 +231,35 @@ Symbol *SymbolTable::addRelative(StringRef N, uint64_t VA) { return S; } -Symbol *SymbolTable::addRegular(ObjectFile *F, COFFSymbolRef Sym, +Symbol *SymbolTable::addRegular(InputFile *F, StringRef N, bool IsCOMDAT, + const coff_symbol_generic *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; + if (!isa(F)) + S->IsUsedInRegularObj = true; + SymbolPreference SP = compareDefined(S, WasInserted, IsCOMDAT); + if (SP == SP_CONFLICT) { + reportDuplicate(S, F); + } else if (SP == SP_NEW) { + replaceBody(S, F, N, IsCOMDAT, /*IsExternal*/ true, Sym, C); } - if (isa(S->body())) - return S; - if (IsReplaceable) - if (isa(S->body()) || isa(S->body())) - return S; - reportDuplicate(S, F); return S; } -Symbol *SymbolTable::addCommon(ObjectFile *F, COFFSymbolRef Sym, - CommonChunk *C) { - StringRef Name; - F->getCOFFObj()->getSymbolName(Sym, Name); +Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size, + const coff_symbol_generic *Sym, CommonChunk *C) { Symbol *S; bool WasInserted; - std::tie(S, WasInserted) = insert(Name); - S->IsUsedInRegularObj = true; + std::tie(S, WasInserted) = insert(N); + if (!isa(F)) + S->IsUsedInRegularObj = true; if (WasInserted || !isa(S->body())) - replaceBody(S, F, Sym, C); + replaceBody(S, F, N, Size, Sym, C); else if (auto *DC = dyn_cast(S->body())) - if (Sym.getValue() > DC->getSize()) - replaceBody(S, F, Sym, C); + if (Size > DC->getSize()) + replaceBody(S, F, N, Size, Sym, C); return S; } @@ -345,75 +348,21 @@ SymbolBody *SymbolTable::addUndefined(StringRef Name) { return addUndefined(Name, nullptr, false)->body(); } -void SymbolTable::printMap(llvm::raw_ostream &OS) { - for (ObjectFile *File : ObjectFiles) { - OS << toString(File) << ":\n"; - for (SymbolBody *Body : File->getSymbols()) - if (auto *R = dyn_cast(Body)) - if (R->getChunk()->isLive()) - OS << Twine::utohexstr(Config->ImageBase + R->getRVA()) - << " " << R->getName() << "\n"; - } +std::vector SymbolTable::compileBitcodeFiles() { + LTO.reset(new BitcodeCompiler); + for (BitcodeFile *F : BitcodeFiles) + LTO->add(*F); + return LTO->compile(); } void SymbolTable::addCombinedLTOObjects() { if (BitcodeFiles.empty()) return; - - // 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); - for (ObjectFile *Obj : createLTOObjects(&CG)) + for (StringRef Object : compileBitcodeFiles()) { + auto *Obj = make(MemoryBufferRef(Object, "lto.tmp")); 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, 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)) - continue; - if (Body->symbol()->IsUsedInRegularObj) - CG->addMustPreserveSymbol(Body->getName()); - replaceBody(Body->symbol(), Body->getName()); - } - - CG->setModule(BitcodeFiles[0]->takeModule()); - for (unsigned I = 1, E = BitcodeFiles.size(); I != E; ++I) - CG->addModule(BitcodeFiles[I]->takeModule().get()); - - bool DisableVerify = true; -#ifdef NDEBUG - DisableVerify = false; -#endif - if (!CG->optimize(DisableVerify, false, false, false)) - fatal(""); // optimize() should have emitted any error message. - - Objs.resize(Config->LTOJobs); - // Use std::list to avoid invalidation of pointers in OSPtrs. - std::list OSs; - std::vector OSPtrs; - for (SmallString<0> &Obj : Objs) { - OSs.emplace_back(Obj); - OSPtrs.push_back(&OSs.back()); + ObjectFiles.push_back(Obj); } - - if (!CG->compileOptimized(OSPtrs)) - fatal(""); // compileOptimized() should have emitted any error message. - - std::vector ObjFiles; - for (SmallString<0> &Obj : Objs) { - auto *ObjFile = make(MemoryBufferRef(Obj, "")); - ObjectFiles.push_back(ObjFile); - ObjFiles.push_back(ObjFile); - } - - return ObjFiles; } } // namespace coff diff --git a/COFF/SymbolTable.h b/COFF/SymbolTable.h index 703821f2e1245..764dd53187750 100644 --- a/COFF/SymbolTable.h +++ b/COFF/SymbolTable.h @@ -11,6 +11,7 @@ #define LLD_COFF_SYMBOL_TABLE_H #include "InputFiles.h" +#include "LTO.h" #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseMapInfo.h" @@ -69,13 +70,11 @@ public: void mangleMaybe(SymbolBody *B); StringRef findMangle(StringRef Name); - // Print a layout map to OS. - void printMap(llvm::raw_ostream &OS); - // Build a set of COFF objects representing the combined contents of // BitcodeFiles and add them to the symbol table. Called after all files are // added and before the writer writes results to a file. void addCombinedLTOObjects(); + std::vector compileBitcodeFiles(); // The writer needs to handle DLL import libraries specially in // order to create the import descriptor table. @@ -93,9 +92,12 @@ public: Symbol *addUndefined(StringRef Name, InputFile *F, bool IsWeakAlias); void addLazy(ArchiveFile *F, const Archive::Symbol Sym); Symbol *addAbsolute(StringRef N, COFFSymbolRef S); - Symbol *addRegular(ObjectFile *F, COFFSymbolRef S, SectionChunk *C); - Symbol *addBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable); - Symbol *addCommon(ObjectFile *F, COFFSymbolRef S, CommonChunk *C); + Symbol *addRegular(InputFile *F, StringRef N, bool IsCOMDAT, + const llvm::object::coff_symbol_generic *S = nullptr, + SectionChunk *C = nullptr); + Symbol *addCommon(InputFile *F, StringRef N, uint64_t Size, + const llvm::object::coff_symbol_generic *S = nullptr, + CommonChunk *C = nullptr); Symbol *addImportData(StringRef N, ImportFile *F); Symbol *addImportThunk(StringRef Name, DefinedImportData *S, uint16_t Machine); @@ -113,12 +115,11 @@ private: StringRef findByPrefix(StringRef Prefix); void addCombinedLTOObject(ObjectFile *Obj); - std::vector createLTOObjects(llvm::LTOCodeGenerator *CG); llvm::DenseMap Symtab; std::vector BitcodeFiles; - std::vector> Objs; + std::unique_ptr LTO; }; extern SymbolTable *Symtab; diff --git a/COFF/Symbols.cpp b/COFF/Symbols.cpp index c44537d371350..993e920ce7f76 100644 --- a/COFF/Symbols.cpp +++ b/COFF/Symbols.cpp @@ -30,7 +30,7 @@ namespace lld { namespace coff { StringRef SymbolBody::getName() { - // DefinedCOFF names are read lazily for a performance reason. + // COFF symbol names are read lazily for a performance reason. // Non-external symbol names are never used by the linker except for logging // or debugging. Their internal references are resolved not by name but by // symbol index. And because they are not external, no one can refer them by @@ -39,7 +39,7 @@ StringRef SymbolBody::getName() { // is a waste of time. if (Name.empty()) { auto *D = cast(this); - D->File->getCOFFObj()->getSymbolName(D->Sym, Name); + cast(D->File)->getCOFFObj()->getSymbolName(D->Sym, Name); } return Name; } @@ -47,15 +47,14 @@ StringRef SymbolBody::getName() { 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() { - size_t SymSize = File->getCOFFObj()->getSymbolTableEntrySize(); + size_t SymSize = + cast(File)->getCOFFObj()->getSymbolTableEntrySize(); if (SymSize == sizeof(coff_symbol16)) return COFFSymbolRef(reinterpret_cast(Sym)); assert(SymSize == sizeof(coff_symbol32)); diff --git a/COFF/Symbols.h b/COFF/Symbols.h index 1ca7366364d79..1b83f73ff20c8 100644 --- a/COFF/Symbols.h +++ b/COFF/Symbols.h @@ -30,7 +30,6 @@ using llvm::object::coff_import_header; using llvm::object::coff_symbol_generic; class ArchiveFile; -class BitcodeFile; class InputFile; class ObjectFile; struct Symbol; @@ -52,13 +51,12 @@ public: DefinedImportDataKind, DefinedAbsoluteKind, DefinedRelativeKind, - DefinedBitcodeKind, UndefinedKind, LazyKind, LastDefinedCOFFKind = DefinedCommonKind, - LastDefinedKind = DefinedBitcodeKind, + LastDefinedKind = DefinedRelativeKind, }; Kind kind() const { return static_cast(SymbolKind); } @@ -81,7 +79,7 @@ protected: friend SymbolTable; explicit SymbolBody(Kind K, StringRef N = "") : SymbolKind(K), IsExternal(true), IsCOMDAT(false), - IsReplaceable(false), WrittenToSymtab(false), Name(N) {} + WrittenToSymtab(false), Name(N) {} const unsigned SymbolKind : 8; unsigned IsExternal : 1; @@ -89,11 +87,9 @@ protected: // This bit is used by the \c DefinedRegular subclass. unsigned IsCOMDAT : 1; - // This bit is used by the \c DefinedBitcode subclass. - unsigned IsReplaceable : 1; - public: - // This bit is used by Writer::createSymbolAndStringTable(). + // This bit is used by Writer::createSymbolAndStringTable() to prevent + // symbols from being written to the symbol table more than once. unsigned WrittenToSymtab : 1; protected: @@ -104,7 +100,7 @@ protected: // etc. class Defined : public SymbolBody { public: - Defined(Kind K, StringRef N = "") : SymbolBody(K, N) {} + Defined(Kind K, StringRef N) : SymbolBody(K, N) {} static bool classof(const SymbolBody *S) { return S->kind() <= LastDefinedKind; @@ -127,22 +123,25 @@ public: bool isExecutable(); }; -// Symbols defined via a COFF object file. +// Symbols defined via a COFF object file or bitcode file. For COFF files, this +// stores a coff_symbol_generic*, and names of internal symbols are lazily +// loaded through that. For bitcode files, Sym is nullptr and the name is stored +// as a StringRef. class DefinedCOFF : public Defined { friend SymbolBody; public: - DefinedCOFF(Kind K, ObjectFile *F, COFFSymbolRef S) - : Defined(K), File(F), Sym(S.getGeneric()) {} + DefinedCOFF(Kind K, InputFile *F, StringRef N, const coff_symbol_generic *S) + : Defined(K, N), File(F), Sym(S) {} static bool classof(const SymbolBody *S) { return S->kind() <= LastDefinedCOFFKind; } - ObjectFile *getFile() { return File; } + InputFile *getFile() { return File; } COFFSymbolRef getCOFFSymbol(); - ObjectFile *File; + InputFile *File; protected: const coff_symbol_generic *Sym; @@ -151,10 +150,13 @@ protected: // Regular defined symbols read from object file symbol tables. class DefinedRegular : public DefinedCOFF { public: - DefinedRegular(ObjectFile *F, COFFSymbolRef S, SectionChunk *C) - : DefinedCOFF(DefinedRegularKind, F, S), Data(&C->Repl) { - IsExternal = S.isExternal(); - IsCOMDAT = C->isCOMDAT(); + DefinedRegular(InputFile *F, StringRef N, bool IsCOMDAT, + bool IsExternal = false, + const coff_symbol_generic *S = nullptr, + SectionChunk *C = nullptr) + : DefinedCOFF(DefinedRegularKind, F, N, S), Data(C ? &C->Repl : nullptr) { + this->IsExternal = IsExternal; + this->IsCOMDAT = IsCOMDAT; } static bool classof(const SymbolBody *S) { @@ -172,9 +174,11 @@ private: class DefinedCommon : public DefinedCOFF { public: - DefinedCommon(ObjectFile *F, COFFSymbolRef S, CommonChunk *C) - : DefinedCOFF(DefinedCommonKind, F, S), Data(C) { - IsExternal = S.isExternal(); + DefinedCommon(InputFile *F, StringRef N, uint64_t Size, + const coff_symbol_generic *S = nullptr, + CommonChunk *C = nullptr) + : DefinedCOFF(DefinedCommonKind, F, N, S), Data(C), Size(Size) { + this->IsExternal = true; } static bool classof(const SymbolBody *S) { @@ -185,8 +189,9 @@ public: private: friend SymbolTable; - uint64_t getSize() { return Sym->Value; } + uint64_t getSize() const { return Size; } CommonChunk *Data; + uint64_t Size; }; // Absolute symbols. @@ -340,26 +345,6 @@ private: LocalImportChunk *Data; }; -class DefinedBitcode : public Defined { - friend SymbolBody; -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; - } - - static bool classof(const SymbolBody *S) { - return S->kind() == DefinedBitcodeKind; - } - - BitcodeFile *File; -}; - inline uint64_t Defined::getRVA() { switch (kind()) { case DefinedAbsoluteKind: @@ -376,8 +361,6 @@ inline uint64_t Defined::getRVA() { return cast(this)->getRVA(); case DefinedRegularKind: return cast(this)->getRVA(); - case DefinedBitcodeKind: - llvm_unreachable("There is no address for a bitcode symbol."); case LazyKind: case UndefinedKind: llvm_unreachable("Cannot get the address for an undefined symbol."); @@ -401,10 +384,9 @@ struct Symbol { // 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 + llvm::AlignedCharArrayUnion< + DefinedRegular, DefinedCommon, DefinedAbsolute, DefinedRelative, Lazy, + Undefined, DefinedImportData, DefinedImportThunk, DefinedLocalImport> Body; SymbolBody *body() { diff --git a/COFF/Writer.cpp b/COFF/Writer.cpp index 71217ebeb60ac..8762b88c4d6ba 100644 --- a/COFF/Writer.cpp +++ b/COFF/Writer.cpp @@ -12,6 +12,7 @@ #include "DLL.h" #include "Error.h" #include "InputFiles.h" +#include "MapFile.h" #include "Memory.h" #include "PDB.h" #include "SymbolTable.h" @@ -39,7 +40,6 @@ using namespace llvm::support::endian; using namespace lld; using namespace lld::coff; -static const int PageSize = 4096; static const int SectorSize = 512; static const int DOSStubSize = 64; static const int NumberfOfDataDirectory = 16; @@ -163,51 +163,6 @@ namespace coff { void writeResult(SymbolTable *T) { Writer(T).run(); } -// OutputSection represents a section in an output file. It's a -// container of chunks. OutputSection and Chunk are 1:N relationship. -// Chunks cannot belong to more than one OutputSections. The writer -// creates multiple OutputSections and assign them unique, -// non-overlapping file offsets and RVAs. -class OutputSection { -public: - OutputSection(StringRef N) : Name(N), Header({}) {} - void setRVA(uint64_t); - void setFileOffset(uint64_t); - void addChunk(Chunk *C); - StringRef getName() { return Name; } - std::vector &getChunks() { return Chunks; } - void addPermissions(uint32_t C); - void setPermissions(uint32_t C); - uint32_t getPermissions() { return Header.Characteristics & PermMask; } - uint32_t getCharacteristics() { return Header.Characteristics; } - uint64_t getRVA() { return Header.VirtualAddress; } - uint64_t getFileOff() { return Header.PointerToRawData; } - void writeHeaderTo(uint8_t *Buf); - - // Returns the size of this section in an executable memory image. - // This may be smaller than the raw size (the raw size is multiple - // of disk sector size, so there may be padding at end), or may be - // larger (if that's the case, the loader reserves spaces after end - // of raw data). - uint64_t getVirtualSize() { return Header.VirtualSize; } - - // Returns the size of the section in the output file. - uint64_t getRawSize() { return Header.SizeOfRawData; } - - // Set offset into the string table storing this section name. - // Used only when the name is longer than 8 bytes. - void setStringTableOff(uint32_t V) { StringTableOff = V; } - - // N.B. The section index is one based. - uint32_t SectionIndex = 0; - -private: - StringRef Name; - coff_section Header; - uint32_t StringTableOff = 0; - std::vector Chunks; -}; - void OutputSection::setRVA(uint64_t RVA) { Header.VirtualAddress = RVA; for (Chunk *C : Chunks) @@ -303,8 +258,14 @@ void Writer::run() { sortExceptionTable(); writeBuildId(); - if (!Config->PDBPath.empty()) - createPDB(Config->PDBPath, Symtab, SectionTable, BuildId->DI); + if (!Config->PDBPath.empty()) { + const llvm::codeview::DebugInfo *DI = nullptr; + if (Config->DebugTypes & static_cast(coff::DebugType::CV)) + DI = BuildId->DI; + createPDB(Config->PDBPath, Symtab, SectionTable, DI); + } + + writeMapFile(OutputSections); if (auto EC = Buffer->commit()) fatal(EC, "failed to write the output file"); @@ -641,6 +602,8 @@ template void Writer::writeHeader() { PE->SizeOfStackCommit = Config->StackCommit; PE->SizeOfHeapReserve = Config->HeapReserve; PE->SizeOfHeapCommit = Config->HeapCommit; + if (Config->AppContainer) + PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_APPCONTAINER; if (Config->DynamicBase) PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE; if (Config->HighEntropyVA) @@ -830,7 +793,7 @@ void Writer::writeBuildId() { "only PDB 7.0 is supported"); assert(sizeof(Res) == sizeof(BuildId->DI->PDB70.Signature) && "signature size mismatch"); - memcpy(BuildId->DI->PDB70.Signature, Res, + memcpy(BuildId->DI->PDB70.Signature, Res.Bytes.data(), sizeof(codeview::PDB70DebugInfo::Signature)); // TODO(compnerd) track the Age BuildId->DI->PDB70.Age = 1; diff --git a/COFF/Writer.h b/COFF/Writer.h index 0d26090177d8c..fef5754238785 100644 --- a/COFF/Writer.h +++ b/COFF/Writer.h @@ -10,14 +10,65 @@ #ifndef LLD_COFF_WRITER_H #define LLD_COFF_WRITER_H +#include "Chunks.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/COFF.h" +#include #include namespace lld { namespace coff { class SymbolTable; +static const int PageSize = 4096; + void writeResult(SymbolTable *T); +// OutputSection represents a section in an output file. It's a +// container of chunks. OutputSection and Chunk are 1:N relationship. +// Chunks cannot belong to more than one OutputSections. The writer +// creates multiple OutputSections and assign them unique, +// non-overlapping file offsets and RVAs. +class OutputSection { +public: + OutputSection(llvm::StringRef N) : Name(N), Header({}) {} + void setRVA(uint64_t); + void setFileOffset(uint64_t); + void addChunk(Chunk *C); + llvm::StringRef getName() { return Name; } + std::vector &getChunks() { return Chunks; } + void addPermissions(uint32_t C); + void setPermissions(uint32_t C); + uint32_t getPermissions() { return Header.Characteristics & PermMask; } + uint32_t getCharacteristics() { return Header.Characteristics; } + uint64_t getRVA() { return Header.VirtualAddress; } + uint64_t getFileOff() { return Header.PointerToRawData; } + void writeHeaderTo(uint8_t *Buf); + + // Returns the size of this section in an executable memory image. + // This may be smaller than the raw size (the raw size is multiple + // of disk sector size, so there may be padding at end), or may be + // larger (if that's the case, the loader reserves spaces after end + // of raw data). + uint64_t getVirtualSize() { return Header.VirtualSize; } + + // Returns the size of the section in the output file. + uint64_t getRawSize() { return Header.SizeOfRawData; } + + // Set offset into the string table storing this section name. + // Used only when the name is longer than 8 bytes. + void setStringTableOff(uint32_t V) { StringTableOff = V; } + + // N.B. The section index is one based. + uint32_t SectionIndex = 0; + +private: + llvm::StringRef Name; + llvm::object::coff_section Header; + uint32_t StringTableOff = 0; + std::vector Chunks; +}; + } } diff --git a/ELF/CMakeLists.txt b/ELF/CMakeLists.txt index 2e9d2b941fd9b..41da497abe267 100644 --- a/ELF/CMakeLists.txt +++ b/ELF/CMakeLists.txt @@ -11,16 +11,19 @@ add_lld_library(lldELF DriverUtils.cpp EhFrame.cpp Error.cpp + Filesystem.cpp GdbIndex.cpp ICF.cpp InputFiles.cpp InputSection.cpp LTO.cpp LinkerScript.cpp + MapFile.cpp MarkLive.cpp Mips.cpp OutputSections.cpp Relocations.cpp + ScriptLexer.cpp ScriptParser.cpp Strings.cpp SymbolTable.cpp @@ -53,7 +56,7 @@ add_lld_library(lldELF LINK_LIBS lldConfig lldCore - ${PTHREAD_LIB} + ${LLVM_PTHREAD_LIB} DEPENDS ELFOptionsTableGen diff --git a/ELF/Config.h b/ELF/Config.h index b7706205a5b6f..c8eecec7439c6 100644 --- a/ELF/Config.h +++ b/ELF/Config.h @@ -13,7 +13,10 @@ #include "llvm/ADT/MapVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" +#include "llvm/Support/CachePruning.h" +#include "llvm/Support/CodeGen.h" #include "llvm/Support/ELF.h" +#include "llvm/Support/Endian.h" #include @@ -34,14 +37,14 @@ enum ELFKind { // For --build-id. enum class BuildIdKind { None, Fast, Md5, Sha1, Hexstring, Uuid }; -// For --discard-{all,locals,none} and --retain-symbols-file. -enum class DiscardPolicy { Default, All, Locals, RetainFile, None }; +// For --discard-{all,locals,none}. +enum class DiscardPolicy { Default, All, Locals, None }; // For --strip-{all,debug}. enum class StripPolicy { None, All, Debug }; // For --unresolved-symbols. -enum class UnresolvedPolicy { NoUndef, ReportError, Warn, Ignore }; +enum class UnresolvedPolicy { ReportError, Warn, WarnAll, Ignore, IgnoreAll }; // For --sort-section and linkerscript sorting rules. enum class SortSectionPolicy { Default, None, Alignment, Name, Priority }; @@ -58,11 +61,10 @@ struct SymbolVersion { // 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, uint16_t Id) : Name(Name), Id(Id) {} llvm::StringRef Name; - uint16_t Id; + uint16_t Id = 0; std::vector Globals; - size_t NameOff; // Offset in string table. + size_t NameOff = 0; // Offset in the string table }; // This struct contains the global configuration for the linker. @@ -72,6 +74,7 @@ struct VersionDefinition { struct Configuration { InputFile *FirstElf = nullptr; uint8_t OSABI = 0; + llvm::CachePruningPolicy ThinLTOCachePolicy; llvm::StringMap SectionStartMap; llvm::StringRef DynamicLinker; llvm::StringRef Entry; @@ -80,10 +83,12 @@ struct Configuration { llvm::StringRef Init; llvm::StringRef LTOAAPipeline; llvm::StringRef LTONewPmPasses; + llvm::StringRef MapFile; llvm::StringRef OutputFile; + llvm::StringRef OptRemarksFilename; llvm::StringRef SoName; llvm::StringRef Sysroot; - llvm::StringSet<> RetainSymbolsFile; + llvm::StringRef ThinLTOCacheDir; std::string RPath; std::vector VersionDefinitions; std::vector AuxiliaryList; @@ -94,6 +99,7 @@ struct Configuration { std::vector VersionScriptLocals; std::vector BuildIdVector; bool AllowMultipleDefinition; + bool ArchiveWithoutSymbolsSeen = false; bool AsNeeded = false; bool Bsymbolic; bool BsymbolicFunctions; @@ -102,30 +108,29 @@ struct Configuration { bool Demangle = true; bool DisableVerify; bool EhFrameHdr; + bool EmitRelocs; bool EnableNewDtags; bool ExportDynamic; bool FatalWarnings; bool GcSections; bool GdbIndex; - bool GnuHash = false; + bool GnuHash; bool ICF; - bool Mips64EL = false; bool MipsN32Abi = false; bool NoGnuUnique; bool NoUndefinedVersion; bool Nostdlib; bool OFormatBinary; - bool OMagic; - bool Pic; + bool Omagic; + bool OptRemarksWithHotness; bool Pie; bool PrintGcSections; - bool Rela; bool Relocatable; bool SaveTemps; bool SingleRoRx; bool Shared; bool Static = false; - bool SysvHash = true; + bool SysvHash; bool Target1Rel; bool Threads; bool Trace; @@ -134,17 +139,20 @@ struct Configuration { bool WarnMissingEntry; bool ZCombreloc; bool ZExecstack; + bool ZNocopyreloc; bool ZNodelete; + bool ZNodlopen; bool ZNow; bool ZOrigin; bool ZRelro; + bool ZText; bool ExitEarly; bool ZWxneeded; DiscardPolicy Discard; SortSectionPolicy SortSection; - StripPolicy Strip = StripPolicy::None; + StripPolicy Strip; UnresolvedPolicy UnresolvedSymbols; - Target2Policy Target2 = Target2Policy::GotRel; + Target2Policy Target2; BuildIdKind BuildId = BuildIdKind::None; ELFKind EKind = ELFNoneKind; uint16_t DefaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL; @@ -157,6 +165,58 @@ struct Configuration { unsigned LTOO; unsigned Optimize; unsigned ThinLTOJobs; + + // The following config options do not directly correspond to any + // particualr command line options. + + // True if we need to pass through relocations in input files to the + // output file. Usually false because we consume relocations. + bool CopyRelocs; + + // True if the target is ELF64. False if ELF32. + bool Is64; + + // True if the target is little-endian. False if big-endian. + bool IsLE; + + // endianness::little if IsLE is true. endianness::big otherwise. + llvm::support::endianness Endianness; + + // True if the target is the little-endian MIPS64. + // + // The reason why we have this variable only for the MIPS is because + // we use this often. Some ELF headers for MIPS64EL are in a + // mixed-endian (which is horrible and I'd say that's a serious spec + // bug), and we need to know whether we are reading MIPS ELF files or + // not in various places. + // + // (Note that MIPS64EL is not a typo for MIPS64LE. This is the official + // name whatever that means. A fun hypothesis is that "EL" is short for + // little-endian written in the little-endian order, but I don't know + // if that's true.) + bool IsMips64EL; + + // The ELF spec defines two types of relocation table entries, RELA and + // REL. RELA is a triplet of (offset, info, addend) while REL is a + // tuple of (offset, info). Addends for REL are implicit and read from + // the location where the relocations are applied. So, REL is more + // compact than RELA but requires a bit of more work to process. + // + // (From the linker writer's view, this distinction is not necessary. + // If the ELF had chosen whichever and sticked with it, it would have + // been easier to write code to process relocations, but it's too late + // to change the spec.) + // + // Each ABI defines its relocation type. IsRela is true if target + // uses RELA. As far as we know, all 64-bit ABIs are using RELA. A + // few 32-bit ABIs are using RELA too. + bool IsRela; + + // True if we are creating position-independent code. + bool Pic; + + // 4 for ELF32, 8 for ELF64. + int Wordsize; }; // The only instance of Configuration struct. diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp index 50b701175d3e3..47ecd607a48f1 100644 --- a/ELF/Driver.cpp +++ b/ELF/Driver.cpp @@ -6,15 +6,34 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// +// +// The driver drives the entire linking process. It is responsible for +// parsing command line options and doing whatever it is instructed to do. +// +// One notable thing in the LLD's driver when compared to other linkers is +// that the LLD's driver is agnostic on the host operating system. +// Other linkers usually have implicit default values (such as a dynamic +// linker path or library paths) for each host OS. +// +// I don't think implicit default values are useful because they are +// usually explicitly specified by the compiler driver. They can even +// be harmful when you are doing cross-linking. Therefore, in LLD, we +// simply trust the compiler driver to pass all required options and +// don't try to make effort on our side. +// +//===----------------------------------------------------------------------===// #include "Driver.h" #include "Config.h" #include "Error.h" +#include "Filesystem.h" #include "ICF.h" #include "InputFiles.h" #include "InputSection.h" #include "LinkerScript.h" #include "Memory.h" +#include "OutputSections.h" +#include "ScriptParser.h" #include "Strings.h" #include "SymbolTable.h" #include "Target.h" @@ -48,16 +67,19 @@ BumpPtrAllocator elf::BAlloc; StringSaver elf::Saver{BAlloc}; std::vector elf::SpecificAllocBase::Instances; +static void setConfigs(); + bool elf::link(ArrayRef Args, bool CanExitEarly, raw_ostream &Error) { ErrorCount = 0; ErrorOS = &Error; Argv0 = Args[0]; + InputSections.clear(); Tar = nullptr; Config = make(); Driver = make(); - ScriptConfig = make(); + Script = make(); Driver->main(Args, CanExitEarly); freeArena(); @@ -78,10 +100,8 @@ static std::tuple parseEmulation(StringRef Emul) { .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}) + .Cases("elf32btsmip", "elf32btsmipn32", {ELF32BEKind, EM_MIPS}) + .Cases("elf32ltsmip", "elf32ltsmipn32", {ELF32LEKind, EM_MIPS}) .Case("elf32ppc", {ELF32BEKind, EM_PPC}) .Case("elf64btsmip", {ELF64BEKind, EM_MIPS}) .Case("elf64ltsmip", {ELF64LEKind, EM_MIPS}) @@ -133,7 +153,7 @@ LinkerDriver::getArchiveMembers(MemoryBufferRef MB) { // Opens and parses a file. Path has to be resolved already. // Newly created memory buffers are owned by this driver. -void LinkerDriver::addFile(StringRef Path) { +void LinkerDriver::addFile(StringRef Path, bool WithLOption) { using namespace sys::fs; Optional Buffer = readFile(Path); @@ -164,6 +184,19 @@ void LinkerDriver::addFile(StringRef Path) { return; } Files.push_back(createSharedFile(MBRef)); + + // DSOs usually have DT_SONAME tags in their ELF headers, and the + // sonames are used to identify DSOs. But if they are missing, + // they are identified by filenames. We don't know whether the new + // file has a DT_SONAME or not because we haven't parsed it yet. + // Here, we set the default soname for the file because we might + // need it later. + // + // If a file was specified by -lfoo, the directory part is not + // significant, as a user did not specify it. This behavior is + // compatible with GNU. + Files.back()->DefaultSoName = + WithLOption ? sys::path::filename(Path) : Path; return; default: if (InLib) @@ -176,7 +209,7 @@ void LinkerDriver::addFile(StringRef Path) { // Add a given library by searching it from input search paths. void LinkerDriver::addLibrary(StringRef Name) { if (Optional Path = searchLibrary(Name)) - addFile(*Path); + addFile(*Path, /*WithLOption=*/true); else error("unable to find library -l" + Name); } @@ -281,11 +314,27 @@ void LinkerDriver::main(ArrayRef ArgsArr, bool CanExitEarly) { return; } - // 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"; + // Handle -v or -version. + // + // A note about "compatible with GNU linkers" message: this is a hack for + // scripts generated by GNU Libtool 2.4.6 (released in February 2014 and + // still the newest version in March 2017) or earlier to recognize LLD as + // a GNU compatible linker. As long as an output for the -v option + // contains "GNU" or "with BFD", they recognize us as GNU-compatible. + // + // This is somewhat ugly hack, but in reality, we had no choice other + // than doing this. Considering the very long release cycle of Libtool, + // it is not easy to improve it to recognize LLD as a GNU compatible + // linker in a timely manner. Even if we can make it, there are still a + // lot of "configure" scripts out there that are generated by old version + // of Libtool. We cannot convince every software developer to migrate to + // the latest version and re-generate scripts. So we have this hack. + if (Args.hasArg(OPT_v) || Args.hasArg(OPT_version)) + message(getLLDVersion() + " (compatible with GNU linkers)"); + + // ld.bfd always exits after printing out the version string. + // ld.gold proceeds if a given option is -v. Because gold's behavior + // is more permissive than ld.bfd, we chose what gold does here. if (Args.hasArg(OPT_version)) return; @@ -311,6 +360,7 @@ void LinkerDriver::main(ArrayRef ArgsArr, bool CanExitEarly) { initLLVM(Args); createFiles(Args); inferMachineType(); + setConfigs(); checkOptions(Args); if (ErrorCount) return; @@ -333,26 +383,68 @@ void LinkerDriver::main(ArrayRef ArgsArr, bool CanExitEarly) { } } -static UnresolvedPolicy getUnresolvedSymbolOption(opt::InputArgList &Args) { +static bool getArg(opt::InputArgList &Args, unsigned K1, unsigned K2, + bool Default) { + if (auto *Arg = Args.getLastArg(K1, K2)) + return Arg->getOption().getID() == K1; + return Default; +} + +static std::vector getArgs(opt::InputArgList &Args, int Id) { + std::vector V; + for (auto *Arg : Args.filtered(Id)) + V.push_back(Arg->getValue()); + return V; +} + +static std::string getRPath(opt::InputArgList &Args) { + std::vector V = getArgs(Args, OPT_rpath); + return llvm::join(V.begin(), V.end(), ":"); +} + +// Determines what we should do if there are remaining unresolved +// symbols after the name resolution. +static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &Args) { + // -noinhibit-exec or -r imply some default values. if (Args.hasArg(OPT_noinhibit_exec)) - return UnresolvedPolicy::Warn; - if (Args.hasArg(OPT_no_undefined) || hasZOption(Args, "defs")) - return UnresolvedPolicy::NoUndef; - if (Config->Relocatable) - return UnresolvedPolicy::Ignore; + return UnresolvedPolicy::WarnAll; + if (Args.hasArg(OPT_relocatable)) + return UnresolvedPolicy::IgnoreAll; - if (auto *Arg = Args.getLastArg(OPT_unresolved_symbols)) { - StringRef S = Arg->getValue(); - if (S == "ignore-all" || S == "ignore-in-object-files") - return UnresolvedPolicy::Ignore; - if (S == "ignore-in-shared-libs" || S == "report-all") - return UnresolvedPolicy::ReportError; - error("unknown --unresolved-symbols value: " + S); + UnresolvedPolicy ErrorOrWarn = getArg(Args, OPT_error_unresolved_symbols, + OPT_warn_unresolved_symbols, true) + ? UnresolvedPolicy::ReportError + : UnresolvedPolicy::Warn; + + // Process the last of -unresolved-symbols, -no-undefined or -z defs. + for (auto *Arg : llvm::reverse(Args)) { + switch (Arg->getOption().getID()) { + case OPT_unresolved_symbols: { + StringRef S = Arg->getValue(); + if (S == "ignore-all" || S == "ignore-in-object-files") + return UnresolvedPolicy::Ignore; + if (S == "ignore-in-shared-libs" || S == "report-all") + return ErrorOrWarn; + error("unknown --unresolved-symbols value: " + S); + continue; + } + case OPT_no_undefined: + return ErrorOrWarn; + case OPT_z: + if (StringRef(Arg->getValue()) == "defs") + return ErrorOrWarn; + continue; + } } - return UnresolvedPolicy::ReportError; + + // -shared implies -unresolved-symbols=ignore-all because missing + // symbols are likely to be resolved at runtime using other DSOs. + if (Config->Shared) + return UnresolvedPolicy::Ignore; + return ErrorOrWarn; } -static Target2Policy getTarget2Option(opt::InputArgList &Args) { +static Target2Policy getTarget2(opt::InputArgList &Args) { if (auto *Arg = Args.getLastArg(OPT_target2)) { StringRef S = Arg->getValue(); if (S == "rel") @@ -376,16 +468,10 @@ static bool isOutputFormatBinary(opt::InputArgList &Args) { 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) +static DiscardPolicy getDiscard(opt::InputArgList &Args) { + if (Args.hasArg(OPT_relocatable)) return DiscardPolicy::None; + auto *Arg = Args.getLastArg(OPT_discard_all, OPT_discard_locals, OPT_discard_none); if (!Arg) @@ -397,13 +483,23 @@ static DiscardPolicy getDiscardOption(opt::InputArgList &Args) { 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 StringRef getDynamicLinker(opt::InputArgList &Args) { + auto *Arg = Args.getLastArg(OPT_dynamic_linker, OPT_no_dynamic_linker); + if (!Arg || Arg->getOption().getID() == OPT_no_dynamic_linker) + return ""; + return Arg->getValue(); +} + +static StripPolicy getStrip(opt::InputArgList &Args) { + if (Args.hasArg(OPT_relocatable)) + return StripPolicy::None; + + auto *Arg = Args.getLastArg(OPT_strip_all, OPT_strip_debug); + if (!Arg) + return StripPolicy::None; + if (Arg->getOption().getID() == OPT_strip_all) + return StripPolicy::All; + return StripPolicy::Debug; } static uint64_t parseSectionAddress(StringRef S, opt::Arg *Arg) { @@ -433,7 +529,7 @@ static StringMap getSectionStartMap(opt::InputArgList &Args) { return Ret; } -static SortSectionPolicy getSortKind(opt::InputArgList &Args) { +static SortSectionPolicy getSortSection(opt::InputArgList &Args) { StringRef S = getString(Args, OPT_sort_section); if (S == "alignment") return SortSectionPolicy::Alignment; @@ -444,6 +540,17 @@ static SortSectionPolicy getSortKind(opt::InputArgList &Args) { return SortSectionPolicy::Default; } +static std::pair getHashStyle(opt::InputArgList &Args) { + StringRef S = getString(Args, OPT_hash_style, "sysv"); + if (S == "sysv") + return {true, false}; + if (S == "gnu") + return {false, true}; + if (S != "both") + error("unknown -hash-style: " + S); + return {true, true}; +} + static std::vector getLines(MemoryBufferRef MB) { SmallVector Arr; MB.getBuffer().split(Arr, '\n'); @@ -459,116 +566,112 @@ static std::vector getLines(MemoryBufferRef MB) { // Initializes Config members by the command line options. void LinkerDriver::readConfigs(opt::InputArgList &Args) { - for (auto *Arg : Args.filtered(OPT_L)) - Config->SearchPaths.push_back(Arg->getValue()); - - std::vector RPaths; - for (auto *Arg : Args.filtered(OPT_rpath)) - RPaths.push_back(Arg->getValue()); - if (!RPaths.empty()) - Config->RPath = llvm::join(RPaths.begin(), RPaths.end(), ":"); - - if (auto *Arg = Args.getLastArg(OPT_m)) { - // Parse ELF{32,64}{LE,BE} and CPU type. - StringRef S = Arg->getValue(); - std::tie(Config->EKind, Config->EMachine, Config->OSABI) = - parseEmulation(S); - Config->MipsN32Abi = (S == "elf32btsmipn32" || S == "elf32ltsmipn32"); - Config->Emulation = S; - } - Config->AllowMultipleDefinition = Args.hasArg(OPT_allow_multiple_definition); + Config->AuxiliaryList = getArgs(Args, OPT_auxiliary); Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic); Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions); + Config->DefineCommon = getArg(Args, OPT_define_common, OPT_no_define_common, + !Args.hasArg(OPT_relocatable)); Config->Demangle = getArg(Args, OPT_demangle, OPT_no_demangle, true); Config->DisableVerify = Args.hasArg(OPT_disable_verify); + Config->Discard = getDiscard(Args); + Config->DynamicLinker = getDynamicLinker(Args); Config->EhFrameHdr = Args.hasArg(OPT_eh_frame_hdr); + Config->EmitRelocs = Args.hasArg(OPT_emit_relocs); Config->EnableNewDtags = !Args.hasArg(OPT_disable_new_dtags); - Config->ExportDynamic = Args.hasArg(OPT_export_dynamic); - Config->FatalWarnings = Args.hasArg(OPT_fatal_warnings); + Config->Entry = getString(Args, OPT_entry); + Config->ExportDynamic = + getArg(Args, OPT_export_dynamic, OPT_no_export_dynamic, false); + Config->FatalWarnings = + getArg(Args, OPT_fatal_warnings, OPT_no_fatal_warnings, false); + Config->Fini = getString(Args, OPT_fini, "_fini"); Config->GcSections = getArg(Args, OPT_gc_sections, OPT_no_gc_sections, false); Config->GdbIndex = Args.hasArg(OPT_gdb_index); Config->ICF = Args.hasArg(OPT_icf); + Config->Init = getString(Args, OPT_init, "_init"); + Config->LTOAAPipeline = getString(Args, OPT_lto_aa_pipeline); + Config->LTONewPmPasses = getString(Args, OPT_lto_newpm_passes); + Config->LTOO = getInteger(Args, OPT_lto_O, 2); + Config->LTOPartitions = getInteger(Args, OPT_lto_partitions, 1); + Config->MapFile = getString(Args, OPT_Map); Config->NoGnuUnique = Args.hasArg(OPT_no_gnu_unique); Config->NoUndefinedVersion = Args.hasArg(OPT_no_undefined_version); Config->Nostdlib = Args.hasArg(OPT_nostdlib); - Config->OMagic = Args.hasArg(OPT_omagic); + Config->OFormatBinary = isOutputFormatBinary(Args); + Config->Omagic = Args.hasArg(OPT_omagic); + Config->OptRemarksFilename = getString(Args, OPT_opt_remarks_filename); + Config->OptRemarksWithHotness = Args.hasArg(OPT_opt_remarks_with_hotness); + Config->Optimize = getInteger(Args, OPT_O, 1); + Config->OutputFile = getString(Args, OPT_o); Config->Pie = getArg(Args, OPT_pie, OPT_nopie, false); Config->PrintGcSections = Args.hasArg(OPT_print_gc_sections); + Config->RPath = getRPath(Args); Config->Relocatable = Args.hasArg(OPT_relocatable); - Config->DefineCommon = getArg(Args, OPT_define_common, OPT_no_define_common, - !Config->Relocatable); - Config->Discard = getDiscardOption(Args); Config->SaveTemps = Args.hasArg(OPT_save_temps); - Config->SingleRoRx = Args.hasArg(OPT_no_rosegment); + Config->SearchPaths = getArgs(Args, OPT_L); + Config->SectionStartMap = getSectionStartMap(Args); Config->Shared = Args.hasArg(OPT_shared); + Config->SingleRoRx = Args.hasArg(OPT_no_rosegment); + Config->SoName = getString(Args, OPT_soname); + Config->SortSection = getSortSection(Args); + Config->Strip = getStrip(Args); + Config->Sysroot = getString(Args, OPT_sysroot); Config->Target1Rel = getArg(Args, OPT_target1_rel, OPT_target1_abs, false); + Config->Target2 = getTarget2(Args); + Config->ThinLTOCacheDir = getString(Args, OPT_thinlto_cache_dir); + Config->ThinLTOCachePolicy = + check(parseCachePruningPolicy(getString(Args, OPT_thinlto_cache_policy)), + "--thinlto-cache-policy: invalid cache policy"); + Config->ThinLTOJobs = getInteger(Args, OPT_thinlto_jobs, -1u); Config->Threads = getArg(Args, OPT_threads, OPT_no_threads, true); Config->Trace = Args.hasArg(OPT_trace); + Config->Undefined = getArgs(Args, OPT_undefined); + Config->UnresolvedSymbols = getUnresolvedSymbolPolicy(Args); Config->Verbose = Args.hasArg(OPT_verbose); Config->WarnCommon = Args.hasArg(OPT_warn_common); + Config->ZCombreloc = !hasZOption(Args, "nocombreloc"); + Config->ZExecstack = hasZOption(Args, "execstack"); + Config->ZNocopyreloc = hasZOption(Args, "nocopyreloc"); + Config->ZNodelete = hasZOption(Args, "nodelete"); + Config->ZNodlopen = hasZOption(Args, "nodlopen"); + Config->ZNow = hasZOption(Args, "now"); + Config->ZOrigin = hasZOption(Args, "origin"); + Config->ZRelro = !hasZOption(Args, "norelro"); + Config->ZStackSize = getZOptionValue(Args, "stack-size", 0); + Config->ZText = !hasZOption(Args, "notext"); + Config->ZWxneeded = hasZOption(Args, "wxneeded"); - Config->DynamicLinker = getString(Args, OPT_dynamic_linker); - 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->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) error("invalid optimization level for LTO: " + getString(Args, OPT_lto_O)); - 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->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 (auto *Arg = Args.getLastArg(OPT_m)) { + // Parse ELF{32,64}{LE,BE} and CPU type. + StringRef S = Arg->getValue(); + std::tie(Config->EKind, Config->EMachine, Config->OSABI) = + parseEmulation(S); + Config->MipsN32Abi = (S == "elf32btsmipn32" || S == "elf32ltsmipn32"); + Config->Emulation = S; + } - Config->OFormatBinary = isOutputFormatBinary(Args); - Config->SectionStartMap = getSectionStartMap(Args); - Config->SortSection = getSortKind(Args); - Config->Target2 = getTarget2Option(Args); - Config->UnresolvedSymbols = getUnresolvedSymbolOption(Args); + if (Args.hasArg(OPT_print_map)) + Config->MapFile = "-"; // --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) + if (Config->Omagic) Config->ZRelro = false; - if (!Config->Relocatable) - Config->Strip = getStripOption(Args); - - // Config->Pic is true if we are generating position-independent code. - Config->Pic = Config->Pie || Config->Shared; - - if (auto *Arg = Args.getLastArg(OPT_hash_style)) { - StringRef S = Arg->getValue(); - if (S == "gnu") { - Config->GnuHash = true; - Config->SysvHash = false; - } else if (S == "both") { - Config->GnuHash = true; - } else if (S != "sysv") - error("unknown hash style: " + S); - } + std::tie(Config->SysvHash, Config->GnuHash) = getHashStyle(Args); - // Parse --build-id or --build-id=