aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2019-01-19 10:05:49 +0000
committerDimitry Andric <dim@FreeBSD.org>2019-01-19 10:05:49 +0000
commite2fd426bdafe9f5c10066d3926ece6e342184a67 (patch)
treebfbbb5fd38554e6b8988b7a217e9fd0623728d7d
parent84c4061b34e048f47e5eb4fbabc1558495e8157c (diff)
downloadsrc-e2fd426bdafe9f5c10066d3926ece6e342184a67.tar.gz
src-e2fd426bdafe9f5c10066d3926ece6e342184a67.zip
Vendor import of lld trunk r351319 (just before the release_80 branchvendor/lld/lld-trunk-r351319
Notes
Notes: svn path=/vendor/lld/dist/; revision=343179 svn path=/vendor/lld/lld-trunk-r351319/; revision=343180; tag=vendor/lld/lld-trunk-r351319
-rw-r--r--COFF/Chunks.cpp327
-rw-r--r--COFF/Chunks.h121
-rw-r--r--COFF/Config.h5
-rw-r--r--COFF/DLL.cpp102
-rw-r--r--COFF/DLL.h9
-rw-r--r--COFF/Driver.cpp553
-rw-r--r--COFF/Driver.h4
-rw-r--r--COFF/DriverUtils.cpp24
-rw-r--r--COFF/ICF.cpp27
-rw-r--r--COFF/InputFiles.cpp95
-rw-r--r--COFF/InputFiles.h30
-rw-r--r--COFF/LTO.cpp3
-rw-r--r--COFF/MapFile.cpp2
-rw-r--r--COFF/MarkLive.cpp8
-rw-r--r--COFF/MinGW.cpp42
-rw-r--r--COFF/MinGW.h6
-rw-r--r--COFF/Options.td37
-rw-r--r--COFF/PDB.cpp997
-rw-r--r--COFF/PDB.h2
-rw-r--r--COFF/SymbolTable.cpp185
-rw-r--r--COFF/SymbolTable.h8
-rw-r--r--COFF/Symbols.cpp9
-rw-r--r--COFF/Symbols.h14
-rw-r--r--COFF/Writer.cpp723
-rw-r--r--COFF/Writer.h6
-rw-r--r--Common/Args.cpp9
-rw-r--r--Common/ErrorHandler.cpp5
-rw-r--r--Common/Strings.cpp33
-rw-r--r--Common/TargetOptionsCommandFlags.cpp1
-rw-r--r--ELF/AArch64ErrataFix.cpp27
-rw-r--r--ELF/Arch/AArch64.cpp37
-rw-r--r--ELF/Arch/AMDGPU.cpp1
-rw-r--r--ELF/Arch/ARM.cpp124
-rw-r--r--ELF/Arch/AVR.cpp3
-rw-r--r--ELF/Arch/Hexagon.cpp195
-rw-r--r--ELF/Arch/MSP430.cpp94
-rw-r--r--ELF/Arch/Mips.cpp13
-rw-r--r--ELF/Arch/PPC.cpp5
-rw-r--r--ELF/Arch/PPC64.cpp473
-rw-r--r--ELF/Arch/RISCV.cpp279
-rw-r--r--ELF/Arch/SPARCV9.cpp1
-rw-r--r--ELF/Arch/X86.cpp25
-rw-r--r--ELF/Arch/X86_64.cpp50
-rw-r--r--ELF/CMakeLists.txt4
-rw-r--r--ELF/CallGraphSort.cpp51
-rw-r--r--ELF/Config.h11
-rw-r--r--ELF/DWARF.cpp (renamed from ELF/GdbIndex.cpp)40
-rw-r--r--ELF/DWARF.h (renamed from ELF/GdbIndex.h)69
-rw-r--r--ELF/Driver.cpp378
-rw-r--r--ELF/Driver.h3
-rw-r--r--ELF/DriverUtils.cpp5
-rw-r--r--ELF/EhFrame.cpp4
-rw-r--r--ELF/ICF.cpp32
-rw-r--r--ELF/InputFiles.cpp111
-rw-r--r--ELF/InputFiles.h12
-rw-r--r--ELF/InputSection.cpp459
-rw-r--r--ELF/InputSection.h38
-rw-r--r--ELF/LTO.cpp48
-rw-r--r--ELF/LTO.h2
-rw-r--r--ELF/LinkerScript.cpp55
-rw-r--r--ELF/LinkerScript.h12
-rw-r--r--ELF/MapFile.cpp11
-rw-r--r--ELF/MarkLive.cpp15
-rw-r--r--ELF/Options.td21
-rw-r--r--ELF/OutputSections.cpp28
-rw-r--r--ELF/OutputSections.h6
-rw-r--r--ELF/Relocations.cpp363
-rw-r--r--ELF/Relocations.h39
-rw-r--r--ELF/ScriptLexer.cpp9
-rw-r--r--ELF/ScriptLexer.h1
-rw-r--r--ELF/ScriptParser.cpp153
-rw-r--r--ELF/SymbolTable.cpp250
-rw-r--r--ELF/SymbolTable.h34
-rw-r--r--ELF/Symbols.cpp60
-rw-r--r--ELF/Symbols.h66
-rw-r--r--ELF/SyntheticSections.cpp506
-rw-r--r--ELF/SyntheticSections.h137
-rw-r--r--ELF/Target.cpp13
-rw-r--r--ELF/Target.h64
-rw-r--r--ELF/Thunks.cpp298
-rw-r--r--ELF/Writer.cpp697
-rw-r--r--LICENSE.TXT2
-rw-r--r--MinGW/Driver.cpp31
-rw-r--r--MinGW/Options.td3
-rw-r--r--docs/NewLLD.rst14
-rw-r--r--docs/README.txt5
-rw-r--r--docs/Readers.rst2
-rw-r--r--docs/ReleaseNotes.rst57
-rw-r--r--docs/WebAssembly.rst106
-rw-r--r--docs/conf.py4
-rw-r--r--docs/index.rst20
-rw-r--r--docs/ld.lld.155
-rw-r--r--docs/missingkeyfunction.rst84
-rw-r--r--docs/open_projects.rst2
-rw-r--r--docs/windows_support.rst4
-rw-r--r--include/lld/Common/Args.h3
-rw-r--r--include/lld/Common/ErrorHandler.h2
-rw-r--r--include/lld/Common/LLVM.h100
-rw-r--r--include/lld/Common/Strings.h3
-rw-r--r--include/lld/Common/TargetOptionsCommandFlags.h1
-rw-r--r--include/lld/Common/Threads.h2
-rw-r--r--include/lld/Core/TODO.txt14
-rw-r--r--lib/Driver/DarwinLdDriver.cpp11
-rw-r--r--lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp1
-rw-r--r--lib/ReaderWriter/MachO/MachOLinkingContext.cpp1
-rw-r--r--lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h9
-rw-r--r--test/CMakeLists.txt3
-rw-r--r--test/COFF/Inputs/associative-comdat-mingw-2.s34
-rw-r--r--test/COFF/Inputs/bad-block-size.pdb2
-rw-r--r--test/COFF/Inputs/comdat-jumptable2.s35
-rw-r--r--test/COFF/Inputs/common-replacement.s5
-rw-r--r--test/COFF/Inputs/crt-dyn-initializer-order_1.yaml15
-rw-r--r--test/COFF/Inputs/crt-dyn-initializer-order_2.yaml19
-rw-r--r--test/COFF/Inputs/empty.yaml67
-rw-r--r--test/COFF/Inputs/far-arm-thumb-abs.s2
-rw-r--r--test/COFF/Inputs/far-arm-thumb-abs20.s2
-rw-r--r--test/COFF/Inputs/gnu-implib-data.s23
-rw-r--r--test/COFF/Inputs/gnu-implib-func.s27
-rw-r--r--test/COFF/Inputs/gnu-implib-head.s13
-rw-r--r--test/COFF/Inputs/gnu-implib-tail.s11
-rw-r--r--test/COFF/Inputs/gnu-weak.obin0 -> 1468 bytes
-rw-r--r--test/COFF/Inputs/gnu-weak2.obin0 -> 874 bytes
-rw-r--r--test/COFF/Inputs/icf-safe.s9
-rw-r--r--test/COFF/Inputs/inline-weak.obin0 -> 1625 bytes
-rw-r--r--test/COFF/Inputs/inline-weak2.obin0 -> 1389 bytes
-rw-r--r--test/COFF/Inputs/pdb-file-statics-a.yaml6
-rw-r--r--test/COFF/Inputs/pdb-file-statics-b.yaml2
-rw-r--r--test/COFF/Inputs/pdb-scopes-a.yaml8
-rw-r--r--test/COFF/Inputs/pdb-scopes-b.yaml6
-rw-r--r--test/COFF/Inputs/pdb-type-server-missing-2.yaml32
-rw-r--r--test/COFF/Inputs/pdb-type-server-simple-a.yaml2
-rw-r--r--test/COFF/Inputs/pdb-type-server-simple-b.yaml2
-rw-r--r--test/COFF/Inputs/pdb-type-server-valid-signature.yaml121
-rw-r--r--test/COFF/Inputs/precomp-a.objbin0 -> 2598 bytes
-rw-r--r--test/COFF/Inputs/precomp-b.objbin0 -> 2257 bytes
-rw-r--r--test/COFF/Inputs/precomp-invalid.objbin0 -> 2257 bytes
-rw-r--r--test/COFF/Inputs/precomp.objbin0 -> 62392 bytes
-rw-r--r--test/COFF/arm-thumb-branch-error.s10
-rw-r--r--test/COFF/arm-thumb-branch20-error.s12
-rw-r--r--test/COFF/arm-thumb-thunks-multipass.s70
-rw-r--r--test/COFF/arm-thumb-thunks.s75
-rw-r--r--test/COFF/arm64-delayimport.yaml91
-rw-r--r--test/COFF/arm64-localimport-align.s24
-rw-r--r--test/COFF/arm64-relocs-imports.test18
-rw-r--r--test/COFF/associative-comdat-mingw.s74
-rw-r--r--test/COFF/autoimport-arm-code.s19
-rw-r--r--test/COFF/autoimport-arm-data.s42
-rw-r--r--test/COFF/autoimport-arm64-code.s18
-rw-r--r--test/COFF/autoimport-arm64-data.s42
-rw-r--r--test/COFF/autoimport-gnu-implib.s26
-rw-r--r--test/COFF/autoimport-list-ptrs.s20
-rw-r--r--test/COFF/autoimport-refptr.s65
-rw-r--r--test/COFF/autoimport-x86.s53
-rw-r--r--test/COFF/baserel.test6
-rw-r--r--test/COFF/broken-arm-reloc.yaml92
-rw-r--r--test/COFF/comdat-jumptable.s70
-rw-r--r--test/COFF/comdat-weak.test82
-rw-r--r--test/COFF/common-replacement.s35
-rw-r--r--test/COFF/could-not-open.test5
-rw-r--r--test/COFF/crt-dyn-initializer-order.test100
-rw-r--r--test/COFF/ctors_dtors_priority.s35
-rw-r--r--test/COFF/debug-fastlink.test12
-rw-r--r--test/COFF/debug-reloc.s18
-rw-r--r--test/COFF/directives.s46
-rw-r--r--test/COFF/duplicate-imp-func.s43
-rw-r--r--test/COFF/entry-inference-mingw.s44
-rw-r--r--test/COFF/entry-inference.test8
-rw-r--r--test/COFF/entry-inference3.test10
-rw-r--r--test/COFF/entry-inference332.test39
-rw-r--r--test/COFF/entry-inference4.test68
-rw-r--r--test/COFF/export-all.s27
-rw-r--r--test/COFF/export.test9
-rw-r--r--test/COFF/force-multiple.test45
-rw-r--r--test/COFF/gfids-export.s44
-rw-r--r--test/COFF/gfids-relocations32.s87
-rw-r--r--test/COFF/gfids-relocations64.s76
-rw-r--r--test/COFF/gnu-weak.test52
-rw-r--r--test/COFF/guardcf-align.s1
-rw-r--r--test/COFF/header-size.s12
-rw-r--r--test/COFF/icf-safe.s37
-rw-r--r--test/COFF/ignore-many.test16
-rw-r--r--test/COFF/imports-gnu-autoexport.s25
-rw-r--r--test/COFF/imports-gnu-only.s28
-rw-r--r--test/COFF/imports-gnu.test29
-rw-r--r--test/COFF/invalid-debug-type.test3
-rw-r--r--test/COFF/invalid-debug.test6
-rw-r--r--test/COFF/libname-mingw.test8
-rw-r--r--test/COFF/line-error.yaml160
-rw-r--r--test/COFF/lto-cache.ll2
-rw-r--r--test/COFF/lto-chkstk.ll1
-rw-r--r--test/COFF/lto-cpu-string.ll21
-rw-r--r--test/COFF/msvclto-archive.ll40
-rw-r--r--test/COFF/msvclto-order.ll25
-rw-r--r--test/COFF/msvclto.ll20
-rw-r--r--test/COFF/no-idata.s20
-rw-r--r--test/COFF/pdb-debug-f.s28
-rw-r--r--test/COFF/pdb-framedata.yaml462
-rw-r--r--test/COFF/pdb-global-hashes.test6
-rw-r--r--test/COFF/pdb-globals.test6
-rw-r--r--test/COFF/pdb-heapsite.yaml2
-rw-r--r--test/COFF/pdb-linker-module.test6
-rw-r--r--test/COFF/pdb-options.test5
-rw-r--r--test/COFF/pdb-procid-remapping.test2
-rw-r--r--test/COFF/pdb-relative-source-lines.test98
-rw-r--r--test/COFF/pdb-same-name.test4
-rw-r--r--test/COFF/pdb-symbol-types.yaml8
-rw-r--r--test/COFF/pdb-thunk.yaml12
-rw-r--r--test/COFF/pdb-type-server-invalid-signature.yaml140
-rw-r--r--test/COFF/pdb-type-server-missing.yaml21
-rw-r--r--test/COFF/pdb-type-server-native-errors.yaml130
-rw-r--r--test/COFF/pdb-type-server-simple.test12
-rw-r--r--test/COFF/pdb.test19
-rw-r--r--test/COFF/pdbaltpath.test39
-rw-r--r--test/COFF/precomp-link.test42
-rw-r--r--test/COFF/reloc-discarded.s1
-rw-r--r--test/COFF/rsds.test126
-rw-r--r--test/COFF/s_udt.s476
-rw-r--r--test/COFF/subsystem-inference-mingw.s17
-rw-r--r--test/COFF/subsystem-inference2.test54
-rw-r--r--test/COFF/subsystem-inference32.test74
-rw-r--r--test/COFF/thinlto-archives.ll12
-rw-r--r--test/COFF/undefined-symbol-cv.s10
-rw-r--r--test/COFF/undefined-symbol.s12
-rw-r--r--test/COFF/wholearchive.s1
-rw-r--r--test/ELF/Inputs/bad-reloc-target.test21
-rw-r--r--test/ELF/Inputs/copy-rel-tls.s12
-rw-r--r--test/ELF/Inputs/gdb-index-invalid-ranges.obj.s2
-rw-r--r--test/ELF/Inputs/gdb-index-multiple-cu-2.s45
-rw-r--r--test/ELF/Inputs/hexagon-shared.s3
-rw-r--r--test/ELF/Inputs/i386-linkonce.s11
-rw-r--r--test/ELF/Inputs/ppc64-bsymbolic-local-def.s14
-rw-r--r--test/ELF/Inputs/ppc64-no-split-stack.s8
-rw-r--r--test/ELF/Inputs/ppc64-tls-ie-le.s29
-rw-r--r--test/ELF/Inputs/ppc64le-quadword-ldst.obin0 -> 1032 bytes
-rw-r--r--test/ELF/Inputs/wrap-with-archive.s5
-rw-r--r--test/ELF/Inputs/x86-64-split-stack-extra.s10
-rw-r--r--test/ELF/aarch64-abs16.s20
-rw-r--r--test/ELF/aarch64-abs32.s20
-rw-r--r--test/ELF/aarch64-call26-thunk.s10
-rw-r--r--test/ELF/aarch64-combined-dynrel-ifunc.s51
-rw-r--r--test/ELF/aarch64-combined-dynrel.s41
-rw-r--r--test/ELF/aarch64-condb-reloc.s30
-rw-r--r--test/ELF/aarch64-copy.s32
-rw-r--r--test/ELF/aarch64-copy2.s2
-rw-r--r--test/ELF/aarch64-cortex-a53-843419-abs-mapsyms.s22
-rw-r--r--test/ELF/aarch64-cortex-a53-843419-cli.s2
-rw-r--r--test/ELF/aarch64-cortex-a53-843419-large.s74
-rw-r--r--test/ELF/aarch64-cortex-a53-843419-large2.s19
-rw-r--r--test/ELF/aarch64-cortex-a53-843419-recognize.s490
-rw-r--r--test/ELF/aarch64-cortex-a53-843419-tlsrelax.s38
-rw-r--r--test/ELF/aarch64-data-relocs.s4
-rw-r--r--test/ELF/aarch64-gnu-ifunc-address.s40
-rw-r--r--test/ELF/aarch64-gnu-ifunc-plt.s80
-rw-r--r--test/ELF/aarch64-gnu-ifunc.s46
-rw-r--r--test/ELF/aarch64-gnu-ifunc2.s52
-rw-r--r--test/ELF/aarch64-gnu-ifunc3.s49
-rw-r--r--test/ELF/aarch64-jump26-thunk.s10
-rw-r--r--test/ELF/aarch64-lo12-alignment.s8
-rw-r--r--test/ELF/aarch64-prel16.s24
-rw-r--r--test/ELF/aarch64-prel32.s24
-rw-r--r--test/ELF/aarch64-relocs.s50
-rw-r--r--test/ELF/aarch64-thunk-pi.s4
-rw-r--r--test/ELF/aarch64-thunk-section-location.s8
-rw-r--r--test/ELF/aarch64-tls-gdie.s14
-rw-r--r--test/ELF/aarch64-tls-gdle.s12
-rw-r--r--test/ELF/aarch64-tls-ie.s22
-rw-r--r--test/ELF/aarch64-tls-iele.s10
-rw-r--r--test/ELF/aarch64-tls-le.s29
-rw-r--r--test/ELF/aarch64-tlsld-ldst.s52
-rw-r--r--test/ELF/aarch64-tstbr14-reloc.s26
-rw-r--r--test/ELF/aarch64-undefined-weak.s24
-rw-r--r--test/ELF/amdgpu-elf-flags-err.s4
-rw-r--r--test/ELF/amdgpu-elf-flags.s4
-rw-r--r--test/ELF/amdgpu-kernels.s2
-rw-r--r--test/ELF/archive-fetch.s15
-rw-r--r--test/ELF/arm-bl-v6-inrange.s47
-rw-r--r--test/ELF/arm-bl-v6.s33
-rw-r--r--test/ELF/arm-blx-v4t.s7
-rw-r--r--test/ELF/arm-branch-rangethunk.s6
-rw-r--r--test/ELF/arm-branch-undef-weak-plt-thunk.s4
-rw-r--r--test/ELF/arm-combined-dynrel-ifunc.s49
-rw-r--r--test/ELF/arm-extreme-range-pi-thunk.s82
-rw-r--r--test/ELF/arm-long-thunk-converge.s2
-rw-r--r--test/ELF/arm-thumb-branch-rangethunk.s4
-rw-r--r--test/ELF/arm-thumb-branch.s4
-rw-r--r--test/ELF/arm-thumb-condbranch-thunk.s2
-rw-r--r--test/ELF/arm-thumb-interwork-shared.s2
-rw-r--r--test/ELF/arm-thumb-interwork-thunk-range.s2
-rw-r--r--test/ELF/arm-thumb-interwork-thunk-v5.s66
-rw-r--r--test/ELF/arm-thumb-interwork-thunk.s2
-rw-r--r--test/ELF/arm-thumb-mix-range-thunk-os.s2
-rw-r--r--test/ELF/arm-thumb-plt-range-thunk-os.s2
-rw-r--r--test/ELF/arm-thumb-range-thunk-os.s2
-rw-r--r--test/ELF/arm-thumb-thunk-empty-pass.s2
-rw-r--r--test/ELF/arm-thumb-thunk-symbols.s2
-rw-r--r--test/ELF/arm-thumb-thunk-v6m.s61
-rw-r--r--test/ELF/arm-thumb-undefined-weak-narrow.test50
-rw-r--r--test/ELF/arm-thunk-edgecase.s2
-rw-r--r--test/ELF/arm-thunk-largesection.s11
-rw-r--r--test/ELF/arm-thunk-linkerscript-dotexpr.s2
-rw-r--r--test/ELF/arm-thunk-linkerscript-large.s5
-rw-r--r--test/ELF/arm-thunk-linkerscript-orphan.s2
-rw-r--r--test/ELF/arm-thunk-linkerscript-sort.s2
-rw-r--r--test/ELF/arm-thunk-linkerscript.s2
-rw-r--r--test/ELF/arm-thunk-multipass.s2
-rw-r--r--test/ELF/arm-thunk-nosuitable.s5
-rw-r--r--test/ELF/arm-thunk-re-add.s2
-rw-r--r--test/ELF/arm-thunk-section-too-large.s2
-rw-r--r--test/ELF/arm-tls-le32.s12
-rw-r--r--test/ELF/arm-tls-norelax-ie-le.s4
-rw-r--r--test/ELF/arm-v4bx.test40
-rw-r--r--test/ELF/arm-v5-reloc-error.s31
-rw-r--r--test/ELF/as-needed-in-regular.s24
-rw-r--r--test/ELF/as-needed-not-in-regular.s32
-rw-r--r--test/ELF/as-needed-weak.s2
-rw-r--r--test/ELF/bad-reloc-target.test29
-rw-r--r--test/ELF/basic-aarch64.s16
-rw-r--r--test/ELF/basic-ppc64.s51
-rw-r--r--test/ELF/basic32.s16
-rw-r--r--test/ELF/basic64be.s65
-rw-r--r--test/ELF/bsymbolic-undef.s4
-rw-r--r--test/ELF/cgprofile-obj.s44
-rw-r--r--test/ELF/cgprofile-shared-warn.s21
-rw-r--r--test/ELF/cgprofile-txt2.s38
-rw-r--r--test/ELF/common-gc2.s4
-rw-r--r--test/ELF/compressed-debug-input-err.s4
-rw-r--r--test/ELF/conflict.s1
-rw-r--r--test/ELF/copy-rel-tls.s15
-rw-r--r--test/ELF/debug-line-str.s136
-rw-r--r--test/ELF/debug-relocation-none.test58
-rw-r--r--test/ELF/defsym.s19
-rw-r--r--test/ELF/dont-export-hidden.s2
-rw-r--r--test/ELF/driver-access.test2
-rw-r--r--test/ELF/driver.test3
-rw-r--r--test/ELF/dt_flags.s5
-rw-r--r--test/ELF/dynamic-list-locals.s7
-rw-r--r--test/ELF/dynamic-list-preempt.s8
-rw-r--r--test/ELF/dynamic-list-unexpected-end.s7
-rw-r--r--test/ELF/dynamic-list-wildcard.s6
-rw-r--r--test/ELF/dynamic-list.s22
-rw-r--r--test/ELF/dynamic-reloc.s2
-rw-r--r--test/ELF/dynsym-no-rosegment.s4
-rw-r--r--test/ELF/dynsym-pie.s2
-rw-r--r--test/ELF/edata-etext.s1
-rw-r--r--test/ELF/emit-relocs-icf1.s32
-rw-r--r--test/ELF/emit-relocs-icf2.s36
-rw-r--r--test/ELF/empty-pack-dyn-relocs.s11
-rw-r--r--test/ELF/empty-ver.s2
-rw-r--r--test/ELF/empty-ver2.s4
-rw-r--r--test/ELF/emulation-aarch64.s34
-rw-r--r--test/ELF/emulation-arm.s27
-rw-r--r--test/ELF/emulation-mips.s189
-rw-r--r--test/ELF/emulation-ppc.s75
-rw-r--r--test/ELF/emulation-x86.s205
-rw-r--r--test/ELF/emulation.s396
-rw-r--r--test/ELF/execute-only-mixed-data.s2
-rw-r--r--test/ELF/format-binary.test12
-rw-r--r--test/ELF/gc-sections-implicit-addend.s6
-rw-r--r--test/ELF/gc-sections-linker-defined-symbol.s2
-rw-r--r--test/ELF/gdb-index-base-addr.s2
-rw-r--r--test/ELF/gdb-index-dwarf5-low-high.s49
-rw-r--r--test/ELF/gdb-index-invalid-ranges.s42
-rw-r--r--test/ELF/gdb-index-multiple-cu-2.s36
-rw-r--r--test/ELF/gdb-index-multiple-cu.s88
-rw-r--r--test/ELF/gdb-index-no-debug.s8
-rw-r--r--test/ELF/gdb-index-ranges.s2
-rw-r--r--test/ELF/gdb-index-rng-lists.s202
-rw-r--r--test/ELF/gdb-index.s2
-rw-r--r--test/ELF/global-offset-table-position-redef-err.s14
-rw-r--r--test/ELF/gnu-hash-table-copy.s8
-rw-r--r--test/ELF/gnu-hash-table-rwsegment.s4
-rw-r--r--test/ELF/gnu-hash-table.s54
-rw-r--r--test/ELF/gnu-ifunc-empty.s16
-rw-r--r--test/ELF/gnu-ifunc-i386.s38
-rw-r--r--test/ELF/gnu-ifunc-plt-i386.s64
-rw-r--r--test/ELF/gnu-ifunc-plt.s8
-rw-r--r--test/ELF/gnu-ifunc-relative.s16
-rw-r--r--test/ELF/gnu-ifunc-shared.s6
-rw-r--r--test/ELF/gnu-ifunc.s4
-rw-r--r--test/ELF/gnu-unique.s4
-rw-r--r--test/ELF/got-i386.s18
-rw-r--r--test/ELF/got32-i386-pie-rw.s2
-rw-r--r--test/ELF/got32-i386.s4
-rw-r--r--test/ELF/got32x-i386.s10
-rw-r--r--test/ELF/hexagon-eflag.s7
-rw-r--r--test/ELF/hexagon-shared.s46
-rw-r--r--test/ELF/hexagon.s206
-rw-r--r--test/ELF/i386-linkonce.s9
-rw-r--r--test/ELF/i386-pc8-pc16-addend.s8
-rw-r--r--test/ELF/i386-retpoline-nopic.s96
-rw-r--r--test/ELF/icf13.s1
-rw-r--r--test/ELF/icf15.s1
-rw-r--r--test/ELF/icf16.s1
-rw-r--r--test/ELF/icf17.s1
-rw-r--r--test/ELF/image-base.s3
-rw-r--r--test/ELF/incompatible-ar-first.s1
-rw-r--r--test/ELF/incompatible.s1
-rw-r--r--test/ELF/invalid-linkerscript.test8
-rw-r--r--test/ELF/invalid/Inputs/shentsize-zero.elfbin512 -> 0 bytes
-rw-r--r--test/ELF/invalid/Inputs/sht-group.elfbin480 -> 0 bytes
-rwxr-xr-xtest/ELF/invalid/Inputs/undefined-local-symbol-in-dso.sobin0 -> 5080 bytes
-rw-r--r--test/ELF/invalid/comdat-broken.test25
-rw-r--r--test/ELF/invalid/ehframe-broken-relocation.test31
-rw-r--r--test/ELF/invalid/invalid-soname.test18
-rw-r--r--test/ELF/invalid/linkorder-invalid-sec.test16
-rw-r--r--test/ELF/invalid/linkorder-invalid-sec2.test16
-rw-r--r--test/ELF/invalid/merge-invalid-size.s3
-rw-r--r--test/ELF/invalid/merge-zero-size.test21
-rw-r--r--test/ELF/invalid/non-terminated-string.test19
-rw-r--r--test/ELF/invalid/sht-group-wrong-section.test22
-rw-r--r--test/ELF/invalid/sht-group.s3
-rw-r--r--test/ELF/invalid/sht-group.test18
-rw-r--r--test/ELF/invalid/undefined-local-symbol-in-dso.test66
-rw-r--r--test/ELF/lazy-arch-conflict.s7
-rw-r--r--test/ELF/linkerscript/Inputs/at6.s11
-rw-r--r--test/ELF/linkerscript/Inputs/at7.s7
-rw-r--r--test/ELF/linkerscript/Inputs/at8.s8
-rw-r--r--test/ELF/linkerscript/align-r.test2
-rw-r--r--test/ELF/linkerscript/align4.test1
-rw-r--r--test/ELF/linkerscript/at6.test30
-rw-r--r--test/ELF/linkerscript/at7.test28
-rw-r--r--test/ELF/linkerscript/at8.test31
-rw-r--r--test/ELF/linkerscript/discard-section-err.s20
-rw-r--r--test/ELF/linkerscript/filename-spec.s1
-rw-r--r--test/ELF/linkerscript/icf.s11
-rw-r--r--test/ELF/linkerscript/info-section-type.s9
-rw-r--r--test/ELF/linkerscript/lazy-symbols.test1
-rw-r--r--test/ELF/linkerscript/map-file.test8
-rw-r--r--test/ELF/linkerscript/map-file2.test22
-rw-r--r--test/ELF/linkerscript/memory-include.test23
-rw-r--r--test/ELF/linkerscript/merge-nonalloc.s14
-rw-r--r--test/ELF/linkerscript/no-filename-spec.s17
-rw-r--r--test/ELF/linkerscript/non-alloc-segment.s2
-rw-r--r--test/ELF/linkerscript/non-alloc.s2
-rw-r--r--test/ELF/linkerscript/orphan-discard.s2
-rw-r--r--test/ELF/linkerscript/orphan-phdrs.s2
-rw-r--r--test/ELF/linkerscript/ouputformat.s9
-rw-r--r--test/ELF/linkerscript/output-section-include.test30
-rw-r--r--test/ELF/linkerscript/output-too-large.s6
-rw-r--r--test/ELF/linkerscript/phdrs.s8
-rw-r--r--test/ELF/linkerscript/provide-shared2.s2
-rw-r--r--test/ELF/linkerscript/relocatable-discard.s21
-rw-r--r--test/ELF/linkerscript/section-include.test32
-rw-r--r--test/ELF/linkerscript/sections-va-overflow.test4
-rw-r--r--test/ELF/linkerscript/segment-none.s4
-rw-r--r--test/ELF/linkerscript/sizeof.s1
-rw-r--r--test/ELF/linkerscript/sizeofheaders.s1
-rw-r--r--test/ELF/linkerscript/sort-init.s6
-rw-r--r--test/ELF/linkerscript/sort-non-script.s2
-rw-r--r--test/ELF/linkerscript/symbol-assignexpr.s1
-rw-r--r--test/ELF/linkerscript/symbol-location.s15
-rw-r--r--test/ELF/linkerscript/symbol-memoryexpr.s1
-rw-r--r--test/ELF/linkerscript/target.s18
-rw-r--r--test/ELF/linkerscript/unused-synthetic.s2
-rw-r--r--test/ELF/linkerscript/version-script.s6
-rw-r--r--test/ELF/local-dynamic.s4
-rw-r--r--test/ELF/local-ver-preemptible.s22
-rw-r--r--test/ELF/lto-plugin-ignore.s4
-rw-r--r--test/ELF/lto/Inputs/libcall-archive.s2
-rw-r--r--test/ELF/lto/amdgcn.ll12
-rw-r--r--test/ELF/lto/cache.ll22
-rw-r--r--test/ELF/lto/data-ordering-lto.s2
-rw-r--r--test/ELF/lto/defsym.ll4
-rw-r--r--test/ELF/lto/dynamic-list.ll2
-rw-r--r--test/ELF/lto/emit-llvm.ll14
-rw-r--r--test/ELF/lto/libcall-archive.ll7
-rw-r--r--test/ELF/lto/ltopasses-custom.ll4
-rw-r--r--test/ELF/lto/opt-remarks.ll10
-rw-r--r--test/ELF/lto/ppc64le.ll12
-rw-r--r--test/ELF/lto/r600.ll12
-rw-r--r--test/ELF/lto/relocatable.ll9
-rw-r--r--test/ELF/lto/section-name.ll4
-rw-r--r--test/ELF/lto/shlib-undefined.ll2
-rw-r--r--test/ELF/lto/symbol-ordering-lto.s2
-rw-r--r--test/ELF/lto/thinlto-obj-path.ll3
-rw-r--r--test/ELF/lto/thinlto-object-suffix-replace.ll12
-rw-r--r--test/ELF/lto/undefined-puts.ll2
-rw-r--r--test/ELF/lto/version-script.ll2
-rw-r--r--test/ELF/lto/wrap-2.ll8
-rw-r--r--test/ELF/map-file-i686.s24
-rw-r--r--test/ELF/merge-string-error.s2
-rw-r--r--test/ELF/mergeable-errors.s8
-rw-r--r--test/ELF/mips-32.s4
-rw-r--r--test/ELF/mips-64.s4
-rw-r--r--test/ELF/mips-dynamic.s6
-rw-r--r--test/ELF/mips-dynsym-sort.s6
-rw-r--r--test/ELF/mips-gnu-hash.s2
-rw-r--r--test/ELF/mips-got-and-copy.s4
-rw-r--r--test/ELF/mips-got-extsym.s2
-rw-r--r--test/ELF/mips-got-relocs.s4
-rw-r--r--test/ELF/mips-got16-relocatable.s8
-rw-r--r--test/ELF/mips-got16.s2
-rw-r--r--test/ELF/mips-hilo.s6
-rw-r--r--test/ELF/mips-mgot.s14
-rw-r--r--test/ELF/mips-micro-jal.s12
-rw-r--r--test/ELF/mips-micro-plt.s2
-rw-r--r--test/ELF/mips-npic-call-pic-os.s35
-rw-r--r--test/ELF/mips-npic-call-pic-script.s114
-rw-r--r--test/ELF/mips-npic-call-pic.s25
-rw-r--r--test/ELF/mips-sto-plt.s4
-rw-r--r--test/ELF/mips-tls-64.s6
-rw-r--r--test/ELF/mips-tls-hilo.s2
-rw-r--r--test/ELF/mips-tls-static-64.s4
-rw-r--r--test/ELF/mips-tls-static.s4
-rw-r--r--test/ELF/mips-tls.s6
-rw-r--r--test/ELF/mips-traps.s22
-rw-r--r--test/ELF/msp430.s43
-rw-r--r--test/ELF/no-obj.s1
-rw-r--r--test/ELF/note-first-page.s12
-rw-r--r--test/ELF/oformat-binary.s1
-rw-r--r--test/ELF/pack-dyn-relocs-loop.s66
-rw-r--r--test/ELF/pack-dyn-relocs-tls-aarch64.s34
-rw-r--r--test/ELF/pack-dyn-relocs-tls-x86-64.s23
-rw-r--r--test/ELF/plt-aarch64.s82
-rw-r--r--test/ELF/plt-i686.s68
-rw-r--r--test/ELF/plt.s10
-rw-r--r--test/ELF/ppc-relocs.s17
-rw-r--r--test/ELF/ppc64-bsymbolic-toc-restore.s68
-rw-r--r--test/ELF/ppc64-call-reach.s94
-rw-r--r--test/ELF/ppc64-dq.s32
-rw-r--r--test/ELF/ppc64-dtprel.s8
-rw-r--r--test/ELF/ppc64-entry-point.s (renamed from test/ELF/ppc64_entry_point.s)15
-rw-r--r--test/ELF/ppc64-error-missaligned-dq.s26
-rw-r--r--test/ELF/ppc64-error-missaligned-ds.s26
-rw-r--r--test/ELF/ppc64-func-entry-points.s2
-rw-r--r--test/ELF/ppc64-gd-to-ie.s8
-rw-r--r--test/ELF/ppc64-general-dynamic-tls.s12
-rw-r--r--test/ELF/ppc64-got-indirect.s4
-rw-r--r--test/ELF/ppc64-got-off.s67
-rw-r--r--test/ELF/ppc64-initial-exec-tls.s12
-rw-r--r--test/ELF/ppc64-local-dynamic.s12
-rw-r--r--test/ELF/ppc64-local-exec-tls.s4
-rw-r--r--test/ELF/ppc64-long-branch.s121
-rw-r--r--test/ELF/ppc64-rel-so-local-calls.s4
-rw-r--r--test/ELF/ppc64-relocs.s49
-rw-r--r--test/ELF/ppc64-shared-long_branch.s114
-rw-r--r--test/ELF/ppc64-split-stack-adjust-fail.s53
-rw-r--r--test/ELF/ppc64-split-stack-adjust-overflow.s64
-rw-r--r--test/ELF/ppc64-split-stack-adjust-size-success.s108
-rw-r--r--test/ELF/ppc64-split-stack-prologue-adjust-success.s224
-rw-r--r--test/ELF/ppc64-tls-gd-le-small.s61
-rw-r--r--test/ELF/ppc64-tls-gd-le.s12
-rw-r--r--test/ELF/ppc64-tls-ie-le.s140
-rw-r--r--test/ELF/ppc64-tls-ld-le.s12
-rw-r--r--test/ELF/ppc64-toc-addis-nop-lqsq.s73
-rw-r--r--test/ELF/ppc64-toc-addis-nop.s272
-rw-r--r--test/ELF/ppc64-toc-rel.s30
-rw-r--r--test/ELF/ppc64-toc-restore-recursive-call.s52
-rw-r--r--test/ELF/ppc64-toc-restore.s2
-rw-r--r--test/ELF/ppc64-tocopt-option.s14
-rw-r--r--test/ELF/pr34660.s2
-rw-r--r--test/ELF/progname.s2
-rw-r--r--test/ELF/protected-shared.s4
-rw-r--r--test/ELF/push-state.s3
-rw-r--r--test/ELF/relative-dynamic-reloc-ppc64.s4
-rw-r--r--test/ELF/relative-dynamic-reloc.s4
-rw-r--r--test/ELF/relocatable-bss.s2
-rw-r--r--test/ELF/relocatable-comdat-multiple.s4
-rw-r--r--test/ELF/relocatable-comdat.s2
-rw-r--r--test/ELF/relocatable-comdat2.s4
-rw-r--r--test/ELF/relocatable-compressed-input.s2
-rw-r--r--test/ELF/relocatable-many-sections.s18
-rw-r--r--test/ELF/relocatable-rel-iplt.s56
-rw-r--r--test/ELF/relocatable.s16
-rw-r--r--test/ELF/relocation-b-aarch64.test4
-rw-r--r--test/ELF/relocation-before-merge-start.s9
-rw-r--r--test/ELF/relocation-common.s12
-rw-r--r--test/ELF/relocation-copy-i686.s14
-rw-r--r--test/ELF/relocation-i686.s30
-rw-r--r--test/ELF/relocation-past-merge-end.s2
-rw-r--r--test/ELF/relocation-size-shared.s15
-rw-r--r--test/ELF/relocation-size.s13
-rw-r--r--test/ELF/reproduce-backslash.s2
-rw-r--r--test/ELF/retain-symbols-file.s2
-rw-r--r--test/ELF/riscv-branch.test119
-rw-r--r--test/ELF/riscv-call.test95
-rw-r--r--test/ELF/riscv-hi20-lo12.test86
-rw-r--r--test/ELF/riscv-jal-error.test93
-rw-r--r--test/ELF/riscv-jal.test161
-rw-r--r--test/ELF/riscv-pcrel-hilo.test103
-rw-r--r--test/ELF/shared.s20
-rw-r--r--test/ELF/sort-norosegment.s2
-rw-r--r--test/ELF/static-error.s13
-rw-r--r--test/ELF/static-with-export-dynamic.s2
-rw-r--r--test/ELF/strip-debug.s4
-rw-r--r--test/ELF/symbol-ordering-file-warnings.s13
-rw-r--r--test/ELF/textrel.s40
-rw-r--r--test/ELF/tls-i686.s20
-rw-r--r--test/ELF/tls-in-archive.s1
-rw-r--r--test/ELF/tls-opt-gdiele-i686.s20
-rw-r--r--test/ELF/tls-opt-i686.s32
-rw-r--r--test/ELF/tls-opt-iele-i686-nopic.s42
-rw-r--r--test/ELF/tls-static.s1
-rw-r--r--test/ELF/tls-weak-undef.s1
-rw-r--r--test/ELF/trace-ar.s1
-rw-r--r--test/ELF/trace-symbols.s2
-rw-r--r--test/ELF/undef-broken-debug.test2
-rw-r--r--test/ELF/undef-version-script.s6
-rw-r--r--test/ELF/undef-with-plt-addr-i686.s4
-rw-r--r--test/ELF/undef.s5
-rw-r--r--test/ELF/verdef-defaultver.s8
-rw-r--r--test/ELF/verdef.s4
-rw-r--r--test/ELF/verneed.s4
-rw-r--r--test/ELF/version-exclude-libs.s1
-rw-r--r--test/ELF/version-script-complex-wildcards.s8
-rw-r--r--test/ELF/version-script-extern-undefined.s4
-rw-r--r--test/ELF/version-script-extern-wildcards.s2
-rw-r--r--test/ELF/version-script-extern.s4
-rw-r--r--test/ELF/version-script-extern2.s2
-rw-r--r--test/ELF/version-script-hide-so-symbol.s2
-rw-r--r--test/ELF/version-script-locals-extern.s8
-rw-r--r--test/ELF/version-script-symver2.s2
-rw-r--r--test/ELF/version-script-weak.s2
-rw-r--r--test/ELF/version-script.s28
-rw-r--r--test/ELF/version-wildcard.test4
-rw-r--r--test/ELF/visibility.s2
-rw-r--r--test/ELF/weak-undef-export.s4
-rw-r--r--test/ELF/weak-undef.s4
-rw-r--r--test/ELF/wrap-entry.s13
-rw-r--r--test/ELF/wrap-no-real.s58
-rw-r--r--test/ELF/wrap-plt.s45
-rw-r--r--test/ELF/wrap-with-archive.s13
-rw-r--r--test/ELF/wrap.s2
-rw-r--r--test/ELF/x86-64-combined-dynrel.s40
-rw-r--r--test/ELF/x86-64-reloc-error2.s12
-rw-r--r--test/ELF/x86-64-reloc-gotoff64.s2
-rw-r--r--test/ELF/x86-64-reloc-gotpc64.s2
-rw-r--r--test/ELF/x86-64-reloc-range-debug-loc.s2
-rw-r--r--test/ELF/x86-64-reloc-range.s2
-rw-r--r--test/ELF/x86-64-retpoline-znow-static-iplt.s26
-rw-r--r--test/ELF/x86-64-split-stack-prologue-adjust-fail.s12
-rw-r--r--test/ELF/x86-64-split-stack-prologue-adjust-shared.s31
-rw-r--r--test/ELF/x86-64-split-stack-prologue-adjust-silent.s2
-rw-r--r--test/ELF/x86-64-split-stack-prologue-adjust-success.s35
-rw-r--r--test/ELF/znotext-plt-relocations-protected.s7
-rw-r--r--test/ELF/zstack-size.s3
-rw-r--r--test/MinGW/driver.test6
-rw-r--r--test/lit.cfg.py18
-rw-r--r--test/lit.site.cfg.py.in3
-rw-r--r--test/mach-o/cstring-sections.yaml8
-rw-r--r--test/mach-o/dependency_info.yaml2
-rw-r--r--test/mach-o/parse-data-relocs-x86_64.yaml2
-rw-r--r--test/mach-o/parse-data.yaml2
-rw-r--r--test/mach-o/sectcreate.yaml2
-rw-r--r--test/wasm/Inputs/event-section1.ll9
-rw-r--r--test/wasm/Inputs/event-section2.ll9
-rw-r--r--test/wasm/Inputs/globals.yaml2
-rw-r--r--test/wasm/Inputs/undefined-globals.yaml2
-rw-r--r--test/wasm/alias.ll4
-rw-r--r--test/wasm/archive-export.ll50
-rw-r--r--test/wasm/archive.ll1
-rw-r--r--test/wasm/call-indirect.ll10
-rw-r--r--test/wasm/comdats.ll2
-rw-r--r--test/wasm/compress-relocs.ll10
-rw-r--r--test/wasm/cxx-mangling.ll20
-rw-r--r--test/wasm/data-layout.ll10
-rw-r--r--test/wasm/data-segment-merging.ll51
-rw-r--r--test/wasm/debug-removed-fn.ll44
-rw-r--r--test/wasm/debuginfo.test4
-rw-r--r--test/wasm/demangle.ll14
-rw-r--r--test/wasm/event-section.ll34
-rw-r--r--test/wasm/export-all.ll2
-rw-r--r--test/wasm/export-table.test2
-rw-r--r--test/wasm/export.ll6
-rw-r--r--test/wasm/import-memory.test17
-rw-r--r--test/wasm/import-table.test4
-rw-r--r--test/wasm/load-undefined.test14
-rw-r--r--test/wasm/local-symbols.ll5
-rw-r--r--test/wasm/locals-duplicate.test14
-rw-r--r--test/wasm/lto/archive.ll2
-rw-r--r--test/wasm/lto/cache.ll8
-rw-r--r--test/wasm/lto/export.ll6
-rw-r--r--test/wasm/lto/signature-mismatch.ll19
-rw-r--r--test/wasm/many-functions.ll6
-rw-r--r--test/wasm/relocatable.ll16
-rw-r--r--test/wasm/shared.ll82
-rw-r--r--test/wasm/signature-mismatch-weak.ll4
-rw-r--r--test/wasm/signature-mismatch.ll8
-rw-r--r--test/wasm/stack-pointer.ll4
-rw-r--r--test/wasm/strip-all.test10
-rw-r--r--test/wasm/strip-debug.test4
-rw-r--r--test/wasm/undefined-entry.test8
-rw-r--r--test/wasm/undefined-weak-call.ll4
-rw-r--r--test/wasm/undefined.ll4
-rw-r--r--test/wasm/visibility-hidden.ll36
-rw-r--r--test/wasm/weak-alias-overide.ll10
-rw-r--r--test/wasm/weak-alias.ll8
-rw-r--r--test/wasm/weak-symbols.ll17
-rw-r--r--test/wasm/weak-undefined.ll13
-rw-r--r--tools/lld/lld.cpp2
-rw-r--r--wasm/Config.h9
-rw-r--r--wasm/Driver.cpp288
-rw-r--r--wasm/InputChunks.cpp27
-rw-r--r--wasm/InputChunks.h40
-rw-r--r--wasm/InputEvent.h63
-rw-r--r--wasm/InputFiles.cpp93
-rw-r--r--wasm/InputFiles.h22
-rw-r--r--wasm/InputGlobal.h3
-rw-r--r--wasm/LTO.cpp17
-rw-r--r--wasm/MarkLive.cpp6
-rw-r--r--wasm/Options.td49
-rw-r--r--wasm/OutputSections.cpp16
-rw-r--r--wasm/OutputSections.h8
-rw-r--r--wasm/OutputSegment.h2
-rw-r--r--wasm/SymbolTable.cpp147
-rw-r--r--wasm/SymbolTable.h11
-rw-r--r--wasm/Symbols.cpp52
-rw-r--r--wasm/Symbols.h93
-rw-r--r--wasm/Writer.cpp190
-rw-r--r--wasm/WriterUtils.cpp82
-rw-r--r--wasm/WriterUtils.h13
711 files changed, 19884 insertions, 5608 deletions
diff --git a/COFF/Chunks.cpp b/COFF/Chunks.cpp
index 412ff783222b..29131d7eb8db 100644
--- a/COFF/Chunks.cpp
+++ b/COFF/Chunks.cpp
@@ -11,6 +11,7 @@
#include "InputFiles.h"
#include "Symbols.h"
#include "Writer.h"
+#include "SymbolTable.h"
#include "lld/Common/ErrorHandler.h"
#include "llvm/ADT/Twine.h"
#include "llvm/BinaryFormat/COFF.h"
@@ -44,6 +45,22 @@ SectionChunk::SectionChunk(ObjFile *F, const coff_section *H)
Live = !Config->DoGC || !isCOMDAT();
}
+// Initialize the RelocTargets vector, to allow redirecting certain relocations
+// to a thunk instead of the actual symbol the relocation's symbol table index
+// indicates.
+void SectionChunk::readRelocTargets() {
+ assert(RelocTargets.empty());
+ RelocTargets.reserve(Relocs.size());
+ for (const coff_relocation &Rel : Relocs)
+ RelocTargets.push_back(File->getSymbol(Rel.SymbolTableIndex));
+}
+
+// Reset RelocTargets to their original targets before thunks were added.
+void SectionChunk::resetRelocTargets() {
+ for (size_t I = 0, E = Relocs.size(); I < E; ++I)
+ RelocTargets[I] = File->getSymbol(Relocs[I].SymbolTableIndex);
+}
+
static void add16(uint8_t *P, int16_t V) { write16le(P, read16le(P) + V); }
static void add32(uint8_t *P, int32_t V) { write32le(P, read32le(P) + V); }
static void add64(uint8_t *P, int64_t V) { write64le(P, read64le(P) + V); }
@@ -58,7 +75,8 @@ static bool checkSecRel(const SectionChunk *Sec, OutputSection *OS) {
return true;
if (Sec->isCodeView())
return false;
- fatal("SECREL relocation cannot be applied to absolute symbols");
+ error("SECREL relocation cannot be applied to absolute symbols");
+ return false;
}
static void applySecRel(const SectionChunk *Sec, uint8_t *Off,
@@ -98,7 +116,7 @@ void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, OutputSection *OS,
case IMAGE_REL_AMD64_SECTION: applySecIdx(Off, OS); break;
case IMAGE_REL_AMD64_SECREL: applySecRel(this, Off, OS, S); break;
default:
- fatal("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
+ error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
toString(File));
}
}
@@ -113,7 +131,7 @@ void SectionChunk::applyRelX86(uint8_t *Off, uint16_t Type, OutputSection *OS,
case IMAGE_REL_I386_SECTION: applySecIdx(Off, OS); break;
case IMAGE_REL_I386_SECREL: applySecRel(this, Off, OS, S); break;
default:
- fatal("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
+ error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
toString(File));
}
}
@@ -123,16 +141,22 @@ static void applyMOV(uint8_t *Off, uint16_t V) {
write16le(Off + 2, (read16le(Off + 2) & 0x8f00) | ((V & 0x700) << 4) | (V & 0xff));
}
-static uint16_t readMOV(uint8_t *Off) {
+static uint16_t readMOV(uint8_t *Off, bool MOVT) {
uint16_t Op1 = read16le(Off);
+ if ((Op1 & 0xfbf0) != (MOVT ? 0xf2c0 : 0xf240))
+ error("unexpected instruction in " + Twine(MOVT ? "MOVT" : "MOVW") +
+ " instruction in MOV32T relocation");
uint16_t Op2 = read16le(Off + 2);
+ if ((Op2 & 0x8000) != 0)
+ error("unexpected instruction in " + Twine(MOVT ? "MOVT" : "MOVW") +
+ " instruction in MOV32T relocation");
return (Op2 & 0x00ff) | ((Op2 >> 4) & 0x0700) | ((Op1 << 1) & 0x0800) |
((Op1 & 0x000f) << 12);
}
void applyMOV32T(uint8_t *Off, uint32_t V) {
- uint16_t ImmW = readMOV(Off); // read MOVW operand
- uint16_t ImmT = readMOV(Off + 4); // read MOVT operand
+ uint16_t ImmW = readMOV(Off, false); // read MOVW operand
+ uint16_t ImmT = readMOV(Off + 4, true); // read MOVT operand
uint32_t Imm = ImmW | (ImmT << 16);
V += Imm; // add the immediate offset
applyMOV(Off, V); // set MOVW operand
@@ -141,7 +165,7 @@ void applyMOV32T(uint8_t *Off, uint32_t V) {
static void applyBranch20T(uint8_t *Off, int32_t V) {
if (!isInt<21>(V))
- fatal("relocation out of range");
+ error("relocation out of range");
uint32_t S = V < 0 ? 1 : 0;
uint32_t J1 = (V >> 19) & 1;
uint32_t J2 = (V >> 18) & 1;
@@ -151,7 +175,7 @@ static void applyBranch20T(uint8_t *Off, int32_t V) {
void applyBranch24T(uint8_t *Off, int32_t V) {
if (!isInt<25>(V))
- fatal("relocation out of range");
+ error("relocation out of range");
uint32_t S = V < 0 ? 1 : 0;
uint32_t J1 = ((~V >> 23) & 1) ^ S;
uint32_t J2 = ((~V >> 22) & 1) ^ S;
@@ -176,7 +200,7 @@ void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, OutputSection *OS,
case IMAGE_REL_ARM_SECTION: applySecIdx(Off, OS); break;
case IMAGE_REL_ARM_SECREL: applySecRel(this, Off, OS, S); break;
default:
- fatal("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
+ error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
toString(File));
}
}
@@ -184,7 +208,7 @@ void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, OutputSection *OS,
// Interpret the existing immediate value as a byte offset to the
// target symbol, then update the instruction with the immediate as
// the page offset from the current instruction to the target.
-static void applyArm64Addr(uint8_t *Off, uint64_t S, uint64_t P, int Shift) {
+void applyArm64Addr(uint8_t *Off, uint64_t S, uint64_t P, int Shift) {
uint32_t Orig = read32le(Off);
uint64_t Imm = ((Orig >> 29) & 0x3) | ((Orig >> 3) & 0x1FFFFC);
S += Imm;
@@ -198,7 +222,7 @@ static void applyArm64Addr(uint8_t *Off, uint64_t S, uint64_t P, int Shift) {
// Update the immediate field in a AARCH64 ldr, str, and add instruction.
// Optionally limit the range of the written immediate by one or more bits
// (RangeLimit).
-static void applyArm64Imm(uint8_t *Off, uint64_t Imm, uint32_t RangeLimit) {
+void applyArm64Imm(uint8_t *Off, uint64_t Imm, uint32_t RangeLimit) {
uint32_t Orig = read32le(Off);
Imm += (Orig >> 10) & 0xFFF;
Orig &= ~(0xFFF << 10);
@@ -221,7 +245,7 @@ static void applyArm64Ldr(uint8_t *Off, uint64_t Imm) {
if ((Orig & 0x4800000) == 0x4800000)
Size += 4;
if ((Imm & ((1 << Size) - 1)) != 0)
- fatal("misaligned ldr/str offset");
+ error("misaligned ldr/str offset");
applyArm64Imm(Off, Imm >> Size, Size);
}
@@ -250,21 +274,21 @@ static void applySecRelLdr(const SectionChunk *Sec, uint8_t *Off,
applyArm64Ldr(Off, (S - OS->getRVA()) & 0xfff);
}
-static void applyArm64Branch26(uint8_t *Off, int64_t V) {
+void applyArm64Branch26(uint8_t *Off, int64_t V) {
if (!isInt<28>(V))
- fatal("relocation out of range");
+ error("relocation out of range");
or32(Off, (V & 0x0FFFFFFC) >> 2);
}
static void applyArm64Branch19(uint8_t *Off, int64_t V) {
if (!isInt<21>(V))
- fatal("relocation out of range");
+ error("relocation out of range");
or32(Off, (V & 0x001FFFFC) << 3);
}
static void applyArm64Branch14(uint8_t *Off, int64_t V) {
if (!isInt<16>(V))
- fatal("relocation out of range");
+ error("relocation out of range");
or32(Off, (V & 0x0000FFFC) << 3);
}
@@ -287,11 +311,37 @@ void SectionChunk::applyRelARM64(uint8_t *Off, uint16_t Type, OutputSection *OS,
case IMAGE_REL_ARM64_SECREL_LOW12L: applySecRelLdr(this, Off, OS, S); break;
case IMAGE_REL_ARM64_SECTION: applySecIdx(Off, OS); break;
default:
- fatal("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
+ error("unsupported relocation type 0x" + Twine::utohexstr(Type) + " in " +
toString(File));
}
}
+static void maybeReportRelocationToDiscarded(const SectionChunk *FromChunk,
+ Defined *Sym,
+ const coff_relocation &Rel) {
+ // Don't report these errors when the relocation comes from a debug info
+ // section or in mingw mode. MinGW mode object files (built by GCC) can
+ // have leftover sections with relocations against discarded comdat
+ // sections. Such sections are left as is, with relocations untouched.
+ if (FromChunk->isCodeView() || FromChunk->isDWARF() || Config->MinGW)
+ return;
+
+ // Get the name of the symbol. If it's null, it was discarded early, so we
+ // have to go back to the object file.
+ ObjFile *File = FromChunk->File;
+ StringRef Name;
+ if (Sym) {
+ Name = Sym->getName();
+ } else {
+ COFFSymbolRef COFFSym =
+ check(File->getCOFFObj()->getSymbol(Rel.SymbolTableIndex));
+ File->getCOFFObj()->getSymbolName(COFFSym, Name);
+ }
+
+ error("relocation against symbol in discarded section: " + Name +
+ getSymbolLocations(File, Rel.SymbolTableIndex));
+}
+
void SectionChunk::writeTo(uint8_t *Buf) const {
if (!hasData())
return;
@@ -302,46 +352,40 @@ void SectionChunk::writeTo(uint8_t *Buf) const {
// Apply relocations.
size_t InputSize = getSize();
- for (const coff_relocation &Rel : Relocs) {
+ for (size_t I = 0, E = Relocs.size(); I < E; I++) {
+ const coff_relocation &Rel = Relocs[I];
+
// Check for an invalid relocation offset. This check isn't perfect, because
// we don't have the relocation size, which is only known after checking the
// machine and relocation type. As a result, a relocation may overwrite the
// beginning of the following input section.
- if (Rel.VirtualAddress >= InputSize)
- fatal("relocation points beyond the end of its parent section");
+ if (Rel.VirtualAddress >= InputSize) {
+ error("relocation points beyond the end of its parent section");
+ continue;
+ }
uint8_t *Off = Buf + OutputSectionOff + Rel.VirtualAddress;
+ // Use the potentially remapped Symbol instead of the one that the
+ // relocation points to.
+ auto *Sym = dyn_cast_or_null<Defined>(RelocTargets[I]);
+
// Get the output section of the symbol for this relocation. The output
// section is needed to compute SECREL and SECTION relocations used in debug
// info.
- auto *Sym =
- dyn_cast_or_null<Defined>(File->getSymbol(Rel.SymbolTableIndex));
- if (!Sym) {
- if (isCodeView() || isDWARF())
- continue;
- // Symbols in early discarded sections are represented using null pointers,
- // so we need to retrieve the name from the object file.
- COFFSymbolRef Sym =
- check(File->getCOFFObj()->getSymbol(Rel.SymbolTableIndex));
- StringRef Name;
- File->getCOFFObj()->getSymbolName(Sym, Name);
- fatal("relocation against symbol in discarded section: " + Name);
- }
- Chunk *C = Sym->getChunk();
+ Chunk *C = Sym ? Sym->getChunk() : nullptr;
OutputSection *OS = C ? C->getOutputSection() : nullptr;
- // Only absolute and __ImageBase symbols lack an output section. For any
- // other symbol, this indicates that the chunk was discarded. Normally
- // relocations against discarded sections are an error. However, debug info
- // sections are not GC roots and can end up with these kinds of relocations.
- // Skip these relocations.
- if (!OS && !isa<DefinedAbsolute>(Sym) && !isa<DefinedSynthetic>(Sym)) {
- if (isCodeView() || isDWARF())
- continue;
- fatal("relocation against symbol in discarded section: " +
- Sym->getName());
+ // Skip the relocation if it refers to a discarded section, and diagnose it
+ // as an error if appropriate. If a symbol was discarded early, it may be
+ // null. If it was discarded late, the output section will be null, unless
+ // it was an absolute or synthetic symbol.
+ if (!Sym ||
+ (!OS && !isa<DefinedAbsolute>(Sym) && !isa<DefinedSynthetic>(Sym))) {
+ maybeReportRelocationToDiscarded(this, Sym, Rel);
+ continue;
}
+
uint64_t S = Sym->getRVA();
// Compute the RVA of the relocation for relative relocations.
@@ -399,17 +443,125 @@ static uint8_t getBaserelType(const coff_relocation &Rel) {
// fixed by the loader if load-time relocation is needed.
// Only called when base relocation is enabled.
void SectionChunk::getBaserels(std::vector<Baserel> *Res) {
- for (const coff_relocation &Rel : Relocs) {
+ for (size_t I = 0, E = Relocs.size(); I < E; I++) {
+ const coff_relocation &Rel = Relocs[I];
uint8_t Ty = getBaserelType(Rel);
if (Ty == IMAGE_REL_BASED_ABSOLUTE)
continue;
- Symbol *Target = File->getSymbol(Rel.SymbolTableIndex);
+ // Use the potentially remapped Symbol instead of the one that the
+ // relocation points to.
+ Symbol *Target = RelocTargets[I];
if (!Target || isa<DefinedAbsolute>(Target))
continue;
Res->emplace_back(RVA + Rel.VirtualAddress, Ty);
}
}
+// MinGW specific.
+// Check whether a static relocation of type Type can be deferred and
+// handled at runtime as a pseudo relocation (for references to a module
+// local variable, which turned out to actually need to be imported from
+// another DLL) This returns the size the relocation is supposed to update,
+// in bits, or 0 if the relocation cannot be handled as a runtime pseudo
+// relocation.
+static int getRuntimePseudoRelocSize(uint16_t Type) {
+ // Relocations that either contain an absolute address, or a plain
+ // relative offset, since the runtime pseudo reloc implementation
+ // adds 8/16/32/64 bit values to a memory address.
+ //
+ // Given a pseudo relocation entry,
+ //
+ // typedef struct {
+ // DWORD sym;
+ // DWORD target;
+ // DWORD flags;
+ // } runtime_pseudo_reloc_item_v2;
+ //
+ // the runtime relocation performs this adjustment:
+ // *(base + .target) += *(base + .sym) - (base + .sym)
+ //
+ // This works for both absolute addresses (IMAGE_REL_*_ADDR32/64,
+ // IMAGE_REL_I386_DIR32, where the memory location initially contains
+ // the address of the IAT slot, and for relative addresses (IMAGE_REL*_REL32),
+ // where the memory location originally contains the relative offset to the
+ // IAT slot.
+ //
+ // This requires the target address to be writable, either directly out of
+ // the image, or temporarily changed at runtime with VirtualProtect.
+ // Since this only operates on direct address values, it doesn't work for
+ // ARM/ARM64 relocations, other than the plain ADDR32/ADDR64 relocations.
+ switch (Config->Machine) {
+ case AMD64:
+ switch (Type) {
+ case IMAGE_REL_AMD64_ADDR64:
+ return 64;
+ case IMAGE_REL_AMD64_ADDR32:
+ case IMAGE_REL_AMD64_REL32:
+ case IMAGE_REL_AMD64_REL32_1:
+ case IMAGE_REL_AMD64_REL32_2:
+ case IMAGE_REL_AMD64_REL32_3:
+ case IMAGE_REL_AMD64_REL32_4:
+ case IMAGE_REL_AMD64_REL32_5:
+ return 32;
+ default:
+ return 0;
+ }
+ case I386:
+ switch (Type) {
+ case IMAGE_REL_I386_DIR32:
+ case IMAGE_REL_I386_REL32:
+ return 32;
+ default:
+ return 0;
+ }
+ case ARMNT:
+ switch (Type) {
+ case IMAGE_REL_ARM_ADDR32:
+ return 32;
+ default:
+ return 0;
+ }
+ case ARM64:
+ switch (Type) {
+ case IMAGE_REL_ARM64_ADDR64:
+ return 64;
+ case IMAGE_REL_ARM64_ADDR32:
+ return 32;
+ default:
+ return 0;
+ }
+ default:
+ llvm_unreachable("unknown machine type");
+ }
+}
+
+// MinGW specific.
+// Append information to the provided vector about all relocations that
+// need to be handled at runtime as runtime pseudo relocations (references
+// to a module local variable, which turned out to actually need to be
+// imported from another DLL).
+void SectionChunk::getRuntimePseudoRelocs(
+ std::vector<RuntimePseudoReloc> &Res) {
+ for (const coff_relocation &Rel : Relocs) {
+ auto *Target =
+ dyn_cast_or_null<Defined>(File->getSymbol(Rel.SymbolTableIndex));
+ if (!Target || !Target->IsRuntimePseudoReloc)
+ continue;
+ int SizeInBits = getRuntimePseudoRelocSize(Rel.Type);
+ if (SizeInBits == 0) {
+ error("unable to automatically import from " + Target->getName() +
+ " with relocation type " +
+ File->getCOFFObj()->getRelocationTypeName(Rel.Type) + " in " +
+ toString(File));
+ continue;
+ }
+ // SizeInBits is used to initialize the Flags field; currently no
+ // other flags are defined.
+ Res.emplace_back(
+ RuntimePseudoReloc(Target, this, Rel.VirtualAddress, SizeInBits));
+ }
+}
+
bool SectionChunk::hasData() const {
return !(Header->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA);
}
@@ -447,6 +599,13 @@ void SectionChunk::replace(SectionChunk *Other) {
Other->Live = false;
}
+uint32_t SectionChunk::getSectionNumber() const {
+ DataRefImpl R;
+ R.p = reinterpret_cast<uintptr_t>(Header);
+ SectionRef S(R, File->getCOFFObj());
+ return S.getIndex() + 1;
+}
+
CommonChunk::CommonChunk(const COFFSymbolRef S) : Sym(S) {
// Common symbols are aligned on natural boundaries up to 32 bytes.
// This is what MSVC link.exe does.
@@ -460,6 +619,7 @@ uint32_t CommonChunk::getOutputCharacteristics() const {
void StringChunk::writeTo(uint8_t *Buf) const {
memcpy(Buf + OutputSectionOff, Str.data(), Str.size());
+ Buf[OutputSectionOff + Str.size()] = '\0';
}
ImportThunkChunkX64::ImportThunkChunkX64(Defined *S) : ImpSymbol(S) {
@@ -502,13 +662,30 @@ void ImportThunkChunkARM64::writeTo(uint8_t *Buf) const {
applyArm64Ldr(Buf + OutputSectionOff + 4, Off);
}
+// A Thumb2, PIC, non-interworking range extension thunk.
+const uint8_t ArmThunk[] = {
+ 0x40, 0xf2, 0x00, 0x0c, // P: movw ip,:lower16:S - (P + (L1-P) + 4)
+ 0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P) + 4)
+ 0xe7, 0x44, // L1: add pc, ip
+};
+
+size_t RangeExtensionThunk::getSize() const {
+ assert(Config->Machine == ARMNT);
+ return sizeof(ArmThunk);
+}
+
+void RangeExtensionThunk::writeTo(uint8_t *Buf) const {
+ assert(Config->Machine == ARMNT);
+ uint64_t Offset = Target->getRVA() - RVA - 12;
+ memcpy(Buf + OutputSectionOff, ArmThunk, sizeof(ArmThunk));
+ applyMOV32T(Buf + OutputSectionOff, uint32_t(Offset));
+}
+
void LocalImportChunk::getBaserels(std::vector<Baserel> *Res) {
Res->emplace_back(getRVA());
}
-size_t LocalImportChunk::getSize() const {
- return Config->is64() ? 8 : 4;
-}
+size_t LocalImportChunk::getSize() const { return Config->Wordsize; }
void LocalImportChunk::writeTo(uint8_t *Buf) const {
if (Config->is64()) {
@@ -528,6 +705,34 @@ void RVATableChunk::writeTo(uint8_t *Buf) const {
"RVA tables should be de-duplicated");
}
+// MinGW specific, for the "automatic import of variables from DLLs" feature.
+size_t PseudoRelocTableChunk::getSize() const {
+ if (Relocs.empty())
+ return 0;
+ return 12 + 12 * Relocs.size();
+}
+
+// MinGW specific.
+void PseudoRelocTableChunk::writeTo(uint8_t *Buf) const {
+ if (Relocs.empty())
+ return;
+
+ ulittle32_t *Table = reinterpret_cast<ulittle32_t *>(Buf + OutputSectionOff);
+ // This is the list header, to signal the runtime pseudo relocation v2
+ // format.
+ Table[0] = 0;
+ Table[1] = 0;
+ Table[2] = 1;
+
+ size_t Idx = 3;
+ for (const RuntimePseudoReloc &RPR : Relocs) {
+ Table[Idx + 0] = RPR.Sym->getRVA();
+ Table[Idx + 1] = RPR.Target->getRVA() + RPR.TargetOffset;
+ Table[Idx + 2] = RPR.Flags;
+ Idx += 3;
+ }
+}
+
// Windows-specific. This class represents a block in .reloc section.
// The format is described here.
//
@@ -613,13 +818,16 @@ void MergeChunk::addSection(SectionChunk *C) {
}
void MergeChunk::finalizeContents() {
- for (SectionChunk *C : Sections)
- if (C->isLive())
- Builder.add(toStringRef(C->getContents()));
- Builder.finalize();
+ if (!Finalized) {
+ for (SectionChunk *C : Sections)
+ if (C->Live)
+ Builder.add(toStringRef(C->getContents()));
+ Builder.finalize();
+ Finalized = true;
+ }
for (SectionChunk *C : Sections) {
- if (!C->isLive())
+ if (!C->Live)
continue;
size_t Off = Builder.getOffset(toStringRef(C->getContents()));
C->setOutputSection(Out);
@@ -640,5 +848,16 @@ void MergeChunk::writeTo(uint8_t *Buf) const {
Builder.write(Buf + OutputSectionOff);
}
+// MinGW specific.
+size_t AbsolutePointerChunk::getSize() const { return Config->Wordsize; }
+
+void AbsolutePointerChunk::writeTo(uint8_t *Buf) const {
+ if (Config->is64()) {
+ write64le(Buf + OutputSectionOff, Value);
+ } else {
+ write32le(Buf + OutputSectionOff, Value);
+ }
+}
+
} // namespace coff
} // namespace lld
diff --git a/COFF/Chunks.h b/COFF/Chunks.h
index 9e896531bd9a..f8a0ddd8ef3b 100644
--- a/COFF/Chunks.h
+++ b/COFF/Chunks.h
@@ -36,6 +36,7 @@ class DefinedImportData;
class DefinedRegular;
class ObjFile;
class OutputSection;
+class RuntimePseudoReloc;
class Symbol;
// Mask for permissions (discardable, writable, readable, executable, etc).
@@ -63,6 +64,13 @@ public:
// before calling this function.
virtual void writeTo(uint8_t *Buf) const {}
+ // Called by the writer once before assigning addresses and writing
+ // the output.
+ virtual void readRelocTargets() {}
+
+ // Called if restarting thunk addition.
+ virtual void resetRelocTargets() {}
+
// Called by the writer after an RVA is assigned, but before calling
// getSize().
virtual void finalizeContents() {}
@@ -114,6 +122,10 @@ protected:
public:
// The offset from beginning of the output section. The writer sets a value.
uint64_t OutputSectionOff = 0;
+
+ // Whether this section needs to be kept distinct from other sections during
+ // ICF. This is set by the driver using address-significance tables.
+ bool KeepUnique = false;
};
// A chunk corresponding a section of an input file.
@@ -140,6 +152,8 @@ public:
SectionChunk(ObjFile *File, const coff_section *Header);
static bool classof(const Chunk *C) { return C->kind() == SectionKind; }
+ void readRelocTargets() override;
+ void resetRelocTargets() override;
size_t getSize() const override { return Header->SizeOfRawData; }
ArrayRef<uint8_t> getContents() const;
void writeTo(uint8_t *Buf) const override;
@@ -157,6 +171,8 @@ public:
void applyRelARM64(uint8_t *Off, uint16_t Type, OutputSection *OS, uint64_t S,
uint64_t P) const;
+ void getRuntimePseudoRelocs(std::vector<RuntimePseudoReloc> &Res);
+
// Called if the garbage collector decides to not include this chunk
// in a final output. It's supposed to print out a log message to stdout.
void printDiscardedMessage() const;
@@ -167,16 +183,6 @@ public:
StringRef getDebugName() override;
- // Returns true if the chunk was not dropped by GC.
- bool isLive() { return Live; }
-
- // Used by the garbage collector.
- void markLive() {
- assert(Config->DoGC && "should only mark things live from GC");
- assert(!isLive() && "Cannot mark an already live section!");
- Live = true;
- }
-
// True if this is a codeview debug info chunk. These will not be laid out in
// the image. Instead they will end up in the PDB, if one is requested.
bool isCodeView() const {
@@ -197,10 +203,13 @@ public:
// Allow iteration over the associated child chunks for this section.
ArrayRef<SectionChunk *> children() const { return AssocChildren; }
+ // The section ID this chunk belongs to in its Obj.
+ uint32_t getSectionNumber() const;
+
// A pointer pointing to a replacement for this chunk.
// Initially it points to "this" object. If this chunk is merged
// with other chunk by ICF, it points to another chunk,
- // and this chunk is considrered as dead.
+ // and this chunk is considered as dead.
SectionChunk *Repl;
// The CRC of the contents as described in the COFF spec 4.5.5.
@@ -217,13 +226,17 @@ public:
ArrayRef<coff_relocation> Relocs;
+ // Used by the garbage collector.
+ bool Live;
+
+ // When inserting a thunk, we need to adjust a relocation to point to
+ // the thunk instead of the actual original target Symbol.
+ std::vector<Symbol *> RelocTargets;
+
private:
StringRef SectionName;
std::vector<SectionChunk *> AssocChildren;
- // Used by the garbage collector.
- bool Live;
-
// Used for ICF (Identical COMDAT Folding)
void replace(SectionChunk *Other);
uint32_t Class[2] = {0, 0};
@@ -254,6 +267,7 @@ public:
private:
llvm::StringTableBuilder Builder;
+ bool Finalized = false;
};
// A chunk for common symbols. Common chunks don't have actual data.
@@ -297,7 +311,7 @@ static const uint8_t ImportThunkARM64[] = {
};
// Windows-specific.
-// A chunk for DLL import jump table entry. In a final output, it's
+// A chunk for DLL import jump table entry. In a final output, its
// contents will be a JMP instruction to some __imp_ symbol.
class ImportThunkChunkX64 : public Chunk {
public:
@@ -341,11 +355,22 @@ private:
Defined *ImpSymbol;
};
+class RangeExtensionThunk : public Chunk {
+public:
+ explicit RangeExtensionThunk(Defined *T) : Target(T) {}
+ size_t getSize() const override;
+ void writeTo(uint8_t *Buf) const override;
+
+ Defined *Target;
+};
+
// Windows-specific.
// See comments for DefinedLocalImport class.
class LocalImportChunk : public Chunk {
public:
- explicit LocalImportChunk(Defined *S) : Sym(S) {}
+ explicit LocalImportChunk(Defined *S) : Sym(S) {
+ Alignment = Config->Wordsize;
+ }
size_t getSize() const override;
void getBaserels(std::vector<Baserel> *Res) override;
void writeTo(uint8_t *Buf) const override;
@@ -414,9 +439,73 @@ public:
uint8_t Type;
};
+// This is a placeholder Chunk, to allow attaching a DefinedSynthetic to a
+// specific place in a section, without any data. This is used for the MinGW
+// specific symbol __RUNTIME_PSEUDO_RELOC_LIST_END__, even though the concept
+// of an empty chunk isn't MinGW specific.
+class EmptyChunk : public Chunk {
+public:
+ EmptyChunk() {}
+ size_t getSize() const override { return 0; }
+ void writeTo(uint8_t *Buf) const override {}
+};
+
+// MinGW specific, for the "automatic import of variables from DLLs" feature.
+// This provides the table of runtime pseudo relocations, for variable
+// references that turned out to need to be imported from a DLL even though
+// the reference didn't use the dllimport attribute. The MinGW runtime will
+// process this table after loading, before handling control over to user
+// code.
+class PseudoRelocTableChunk : public Chunk {
+public:
+ PseudoRelocTableChunk(std::vector<RuntimePseudoReloc> &Relocs)
+ : Relocs(std::move(Relocs)) {
+ Alignment = 4;
+ }
+ size_t getSize() const override;
+ void writeTo(uint8_t *Buf) const override;
+
+private:
+ std::vector<RuntimePseudoReloc> Relocs;
+};
+
+// MinGW specific; information about one individual location in the image
+// that needs to be fixed up at runtime after loading. This represents
+// one individual element in the PseudoRelocTableChunk table.
+class RuntimePseudoReloc {
+public:
+ RuntimePseudoReloc(Defined *Sym, SectionChunk *Target, uint32_t TargetOffset,
+ int Flags)
+ : Sym(Sym), Target(Target), TargetOffset(TargetOffset), Flags(Flags) {}
+
+ Defined *Sym;
+ SectionChunk *Target;
+ uint32_t TargetOffset;
+ // The Flags field contains the size of the relocation, in bits. No other
+ // flags are currently defined.
+ int Flags;
+};
+
+// MinGW specific. A Chunk that contains one pointer-sized absolute value.
+class AbsolutePointerChunk : public Chunk {
+public:
+ AbsolutePointerChunk(uint64_t Value) : Value(Value) {
+ Alignment = getSize();
+ }
+ size_t getSize() const override;
+ void writeTo(uint8_t *Buf) const override;
+
+private:
+ uint64_t Value;
+};
+
void applyMOV32T(uint8_t *Off, uint32_t V);
void applyBranch24T(uint8_t *Off, int32_t V);
+void applyArm64Addr(uint8_t *Off, uint64_t S, uint64_t P, int Shift);
+void applyArm64Imm(uint8_t *Off, uint64_t Imm, uint32_t RangeLimit);
+void applyArm64Branch26(uint8_t *Off, int64_t V);
+
} // namespace coff
} // namespace lld
diff --git a/COFF/Config.h b/COFF/Config.h
index 3ae50b868333..8915b6a3bdd8 100644
--- a/COFF/Config.h
+++ b/COFF/Config.h
@@ -84,6 +84,7 @@ struct Configuration {
bool is64() { return Machine == AMD64 || Machine == ARM64; }
llvm::COFF::MachineTypes Machine = IMAGE_FILE_MACHINE_UNKNOWN;
+ size_t Wordsize;
bool Verbose = false;
WindowsSubsystem Subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN;
Symbol *Entry = nullptr;
@@ -94,7 +95,8 @@ struct Configuration {
bool DoICF = true;
bool TailMerge;
bool Relocatable = true;
- bool Force = false;
+ bool ForceMultiple = false;
+ bool ForceUnresolved = false;
bool Debug = false;
bool DebugDwarf = false;
bool DebugGHashes = false;
@@ -195,6 +197,7 @@ struct Configuration {
bool MinGW = false;
bool WarnMissingOrderSymbol = true;
bool WarnLocallyDefinedImported = true;
+ bool WarnDebugInfoUnusable = true;
bool Incremental = true;
bool IntegrityCheck = false;
bool KillAt = false;
diff --git a/COFF/DLL.cpp b/COFF/DLL.cpp
index 464abe8e0894..599cc5892a16 100644
--- a/COFF/DLL.cpp
+++ b/COFF/DLL.cpp
@@ -35,8 +35,6 @@ namespace {
// Import table
-static int ptrSize() { return Config->is64() ? 8 : 4; }
-
// A chunk for the import descriptor table.
class HintNameChunk : public Chunk {
public:
@@ -61,8 +59,8 @@ private:
// A chunk for the import descriptor table.
class LookupChunk : public Chunk {
public:
- explicit LookupChunk(Chunk *C) : HintName(C) { Alignment = ptrSize(); }
- size_t getSize() const override { return ptrSize(); }
+ explicit LookupChunk(Chunk *C) : HintName(C) { Alignment = Config->Wordsize; }
+ size_t getSize() const override { return Config->Wordsize; }
void writeTo(uint8_t *Buf) const override {
write32le(Buf + OutputSectionOff, HintName->getRVA());
@@ -76,8 +74,10 @@ public:
// See Microsoft PE/COFF spec 7.1. Import Header for details.
class OrdinalOnlyChunk : public Chunk {
public:
- explicit OrdinalOnlyChunk(uint16_t V) : Ordinal(V) { Alignment = ptrSize(); }
- size_t getSize() const override { return ptrSize(); }
+ explicit OrdinalOnlyChunk(uint16_t V) : Ordinal(V) {
+ Alignment = Config->Wordsize;
+ }
+ size_t getSize() const override { return Config->Wordsize; }
void writeTo(uint8_t *Buf) const override {
// An import-by-ordinal slot has MSB 1 to indicate that
@@ -230,6 +230,36 @@ static const uint8_t ThunkARM[] = {
0x60, 0x47, // bx ip
};
+static const uint8_t ThunkARM64[] = {
+ 0x11, 0x00, 0x00, 0x90, // adrp x17, #0 __imp_<FUNCNAME>
+ 0x31, 0x02, 0x00, 0x91, // add x17, x17, #0 :lo12:__imp_<FUNCNAME>
+ 0xfd, 0x7b, 0xb3, 0xa9, // stp x29, x30, [sp, #-208]!
+ 0xfd, 0x03, 0x00, 0x91, // mov x29, sp
+ 0xe0, 0x07, 0x01, 0xa9, // stp x0, x1, [sp, #16]
+ 0xe2, 0x0f, 0x02, 0xa9, // stp x2, x3, [sp, #32]
+ 0xe4, 0x17, 0x03, 0xa9, // stp x4, x5, [sp, #48]
+ 0xe6, 0x1f, 0x04, 0xa9, // stp x6, x7, [sp, #64]
+ 0xe0, 0x87, 0x02, 0xad, // stp q0, q1, [sp, #80]
+ 0xe2, 0x8f, 0x03, 0xad, // stp q2, q3, [sp, #112]
+ 0xe4, 0x97, 0x04, 0xad, // stp q4, q5, [sp, #144]
+ 0xe6, 0x9f, 0x05, 0xad, // stp q6, q7, [sp, #176]
+ 0xe1, 0x03, 0x11, 0xaa, // mov x1, x17
+ 0x00, 0x00, 0x00, 0x90, // adrp x0, #0 DELAY_IMPORT_DESCRIPTOR
+ 0x00, 0x00, 0x00, 0x91, // add x0, x0, #0 :lo12:DELAY_IMPORT_DESCRIPTOR
+ 0x00, 0x00, 0x00, 0x94, // bl #0 __delayLoadHelper2
+ 0xf0, 0x03, 0x00, 0xaa, // mov x16, x0
+ 0xe6, 0x9f, 0x45, 0xad, // ldp q6, q7, [sp, #176]
+ 0xe4, 0x97, 0x44, 0xad, // ldp q4, q5, [sp, #144]
+ 0xe2, 0x8f, 0x43, 0xad, // ldp q2, q3, [sp, #112]
+ 0xe0, 0x87, 0x42, 0xad, // ldp q0, q1, [sp, #80]
+ 0xe6, 0x1f, 0x44, 0xa9, // ldp x6, x7, [sp, #64]
+ 0xe4, 0x17, 0x43, 0xa9, // ldp x4, x5, [sp, #48]
+ 0xe2, 0x0f, 0x42, 0xa9, // ldp x2, x3, [sp, #32]
+ 0xe0, 0x07, 0x41, 0xa9, // ldp x0, x1, [sp, #16]
+ 0xfd, 0x7b, 0xcd, 0xa8, // ldp x29, x30, [sp], #208
+ 0x00, 0x02, 0x1f, 0xd6, // br x16
+};
+
// A chunk for the delay import thunk.
class ThunkChunkX64 : public Chunk {
public:
@@ -298,11 +328,35 @@ public:
Defined *Helper = nullptr;
};
+class ThunkChunkARM64 : public Chunk {
+public:
+ ThunkChunkARM64(Defined *I, Chunk *D, Defined *H)
+ : Imp(I), Desc(D), Helper(H) {}
+
+ size_t getSize() const override { return sizeof(ThunkARM64); }
+
+ void writeTo(uint8_t *Buf) const override {
+ memcpy(Buf + OutputSectionOff, ThunkARM64, sizeof(ThunkARM64));
+ applyArm64Addr(Buf + OutputSectionOff + 0, Imp->getRVA(), RVA + 0, 12);
+ applyArm64Imm(Buf + OutputSectionOff + 4, Imp->getRVA() & 0xfff, 0);
+ applyArm64Addr(Buf + OutputSectionOff + 52, Desc->getRVA(), RVA + 52, 12);
+ applyArm64Imm(Buf + OutputSectionOff + 56, Desc->getRVA() & 0xfff, 0);
+ applyArm64Branch26(Buf + OutputSectionOff + 60,
+ Helper->getRVA() - RVA - 60);
+ }
+
+ Defined *Imp = nullptr;
+ Chunk *Desc = nullptr;
+ Defined *Helper = nullptr;
+};
+
// A chunk for the import descriptor table.
class DelayAddressChunk : public Chunk {
public:
- explicit DelayAddressChunk(Chunk *C) : Thunk(C) { Alignment = ptrSize(); }
- size_t getSize() const override { return ptrSize(); }
+ explicit DelayAddressChunk(Chunk *C) : Thunk(C) {
+ Alignment = Config->Wordsize;
+ }
+ size_t getSize() const override { return Config->Wordsize; }
void writeTo(uint8_t *Buf) const override {
if (Config->is64()) {
@@ -362,6 +416,8 @@ public:
size_t getSize() const override { return Size * 4; }
void writeTo(uint8_t *Buf) const override {
+ memset(Buf + OutputSectionOff, 0, getSize());
+
for (const Export &E : Config->Exports) {
uint8_t *P = Buf + OutputSectionOff + E.Ordinal * 4;
uint32_t Bit = 0;
@@ -418,30 +474,6 @@ private:
} // anonymous namespace
-uint64_t IdataContents::getDirSize() {
- return Dirs.size() * sizeof(ImportDirectoryTableEntry);
-}
-
-uint64_t IdataContents::getIATSize() {
- return Addresses.size() * ptrSize();
-}
-
-// Returns a list of .idata contents.
-// See Microsoft PE/COFF spec 5.4 for details.
-std::vector<Chunk *> IdataContents::getChunks() {
- create();
-
- // The loader assumes a specific order of data.
- // Add each type in the correct order.
- std::vector<Chunk *> V;
- V.insert(V.end(), Dirs.begin(), Dirs.end());
- V.insert(V.end(), Lookups.begin(), Lookups.end());
- V.insert(V.end(), Addresses.begin(), Addresses.end());
- V.insert(V.end(), Hints.begin(), Hints.end());
- V.insert(V.end(), DLLNames.begin(), DLLNames.end());
- return V;
-}
-
void IdataContents::create() {
std::vector<std::vector<DefinedImportData *>> V = binImports(Imports);
@@ -465,8 +497,8 @@ void IdataContents::create() {
Hints.push_back(C);
}
// Terminate with null values.
- Lookups.push_back(make<NullChunk>(ptrSize()));
- Addresses.push_back(make<NullChunk>(ptrSize()));
+ Lookups.push_back(make<NullChunk>(Config->Wordsize));
+ Addresses.push_back(make<NullChunk>(Config->Wordsize));
for (int I = 0, E = Syms.size(); I < E; ++I)
Syms[I]->setLocation(Addresses[Base + I]);
@@ -555,6 +587,8 @@ Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *S, Chunk *Dir) {
return make<ThunkChunkX86>(S, Dir, Helper);
case ARMNT:
return make<ThunkChunkARM>(S, Dir, Helper);
+ case ARM64:
+ return make<ThunkChunkARM64>(S, Dir, Helper);
default:
llvm_unreachable("unsupported machine type");
}
diff --git a/COFF/DLL.h b/COFF/DLL.h
index c5d6e7c93abf..a298271e2c0d 100644
--- a/COFF/DLL.h
+++ b/COFF/DLL.h
@@ -19,19 +19,12 @@ namespace coff {
// Windows-specific.
// IdataContents creates all chunks for the DLL import table.
// You are supposed to call add() to add symbols and then
-// call getChunks() to get a list of chunks.
+// call create() to populate the chunk vectors.
class IdataContents {
public:
void add(DefinedImportData *Sym) { Imports.push_back(Sym); }
bool empty() { return Imports.empty(); }
- std::vector<Chunk *> getChunks();
- uint64_t getDirRVA() { return Dirs[0]->getRVA(); }
- uint64_t getDirSize();
- uint64_t getIATRVA() { return Addresses[0]->getRVA(); }
- uint64_t getIATSize();
-
-private:
void create();
std::vector<DefinedImportData *> Imports;
diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp
index eefdb48beadd..2e4b1e6d3147 100644
--- a/COFF/Driver.cpp
+++ b/COFF/Driver.cpp
@@ -32,6 +32,7 @@
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/Debug.h"
+#include "llvm/Support/LEB128.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/TarWriter.h"
@@ -56,7 +57,7 @@ Configuration *Config;
LinkerDriver *Driver;
bool link(ArrayRef<const char *> Args, bool CanExitEarly, raw_ostream &Diag) {
- errorHandler().LogName = sys::path::filename(Args[0]);
+ errorHandler().LogName = args::getFilenameWithoutExe(Args[0]);
errorHandler().ErrorOS = &Diag;
errorHandler().ColorDiagnostics = Diag.has_colors();
errorHandler().ErrorLimitExceededMsg =
@@ -116,6 +117,19 @@ static std::future<MBErrPair> createFutureForFile(std::string Path) {
});
}
+// Symbol names are mangled by prepending "_" on x86.
+static StringRef mangle(StringRef Sym) {
+ assert(Config->Machine != IMAGE_FILE_MACHINE_UNKNOWN);
+ if (Config->Machine == I386)
+ return Saver.save("_" + Sym);
+ return Sym;
+}
+
+static bool findUnderscoreMangle(StringRef Sym) {
+ StringRef Entry = Symtab->findMangle(mangle(Sym));
+ return !Entry.empty() && !isa<Undefined>(Symtab->find(Entry));
+}
+
MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> MB) {
MemoryBufferRef MBRef = *MB;
make<std::unique_ptr<MemoryBuffer>>(std::move(MB)); // take ownership
@@ -357,13 +371,30 @@ Optional<StringRef> LinkerDriver::findFile(StringRef Filename) {
return Path;
}
+// MinGW specific. If an embedded directive specified to link to
+// foo.lib, but it isn't found, try libfoo.a instead.
+StringRef LinkerDriver::doFindLibMinGW(StringRef Filename) {
+ if (Filename.contains('/') || Filename.contains('\\'))
+ return Filename;
+
+ SmallString<128> S = Filename;
+ sys::path::replace_extension(S, ".a");
+ StringRef LibName = Saver.save("lib" + S.str());
+ return doFindFile(LibName);
+}
+
// Find library file from search path.
StringRef LinkerDriver::doFindLib(StringRef Filename) {
// Add ".lib" to Filename if that has no file extension.
bool HasExt = Filename.contains('.');
if (!HasExt)
Filename = Saver.save(Filename + ".lib");
- return doFindFile(Filename);
+ StringRef Ret = doFindFile(Filename);
+ // For MinGW, if the find above didn't turn up anything, try
+ // looking for a MinGW formatted library name.
+ if (Config->MinGW && Ret == Filename)
+ return doFindLibMinGW(Filename);
+ return Ret;
}
// Resolves a library path. /nodefaultlib options are taken into
@@ -407,54 +438,57 @@ Symbol *LinkerDriver::addUndefined(StringRef Name) {
return B;
}
-// Symbol names are mangled by appending "_" prefix on x86.
-StringRef LinkerDriver::mangle(StringRef Sym) {
- assert(Config->Machine != IMAGE_FILE_MACHINE_UNKNOWN);
- if (Config->Machine == I386)
- return Saver.save("_" + Sym);
- return Sym;
-}
-
// Windows specific -- find default entry point name.
//
// There are four different entry point functions for Windows executables,
// each of which corresponds to a user-defined "main" function. This function
// infers an entry point from a user-defined "main" function.
StringRef LinkerDriver::findDefaultEntry() {
- // As a special case, if /nodefaultlib is given, we directly look for an
- // entry point. This is because, if no default library is linked, users
- // need to define an entry point instead of a "main".
- if (Config->NoDefaultLibAll) {
- for (StringRef S : {"mainCRTStartup", "wmainCRTStartup",
- "WinMainCRTStartup", "wWinMainCRTStartup"}) {
- StringRef Entry = Symtab->findMangle(S);
- if (!Entry.empty() && !isa<Undefined>(Symtab->find(Entry)))
- return mangle(S);
+ assert(Config->Subsystem != IMAGE_SUBSYSTEM_UNKNOWN &&
+ "must handle /subsystem before calling this");
+
+ if (Config->MinGW)
+ return mangle(Config->Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI
+ ? "WinMainCRTStartup"
+ : "mainCRTStartup");
+
+ if (Config->Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) {
+ if (findUnderscoreMangle("wWinMain")) {
+ if (!findUnderscoreMangle("WinMain"))
+ return mangle("wWinMainCRTStartup");
+ warn("found both wWinMain and WinMain; using latter");
}
- return "";
+ return mangle("WinMainCRTStartup");
}
-
- // User-defined main functions and their corresponding entry points.
- static const char *Entries[][2] = {
- {"main", "mainCRTStartup"},
- {"wmain", "wmainCRTStartup"},
- {"WinMain", "WinMainCRTStartup"},
- {"wWinMain", "wWinMainCRTStartup"},
- };
- for (auto E : Entries) {
- StringRef Entry = Symtab->findMangle(mangle(E[0]));
- if (!Entry.empty() && !isa<Undefined>(Symtab->find(Entry)))
- return mangle(E[1]);
+ if (findUnderscoreMangle("wmain")) {
+ if (!findUnderscoreMangle("main"))
+ return mangle("wmainCRTStartup");
+ warn("found both wmain and main; using latter");
}
- return "";
+ return mangle("mainCRTStartup");
}
WindowsSubsystem LinkerDriver::inferSubsystem() {
if (Config->DLL)
return IMAGE_SUBSYSTEM_WINDOWS_GUI;
- if (Symtab->findUnderscore("main") || Symtab->findUnderscore("wmain"))
+ if (Config->MinGW)
return IMAGE_SUBSYSTEM_WINDOWS_CUI;
- if (Symtab->findUnderscore("WinMain") || Symtab->findUnderscore("wWinMain"))
+ // Note that link.exe infers the subsystem from the presence of these
+ // functions even if /entry: or /nodefaultlib are passed which causes them
+ // to not be called.
+ bool HaveMain = findUnderscoreMangle("main");
+ bool HaveWMain = findUnderscoreMangle("wmain");
+ bool HaveWinMain = findUnderscoreMangle("WinMain");
+ bool HaveWWinMain = findUnderscoreMangle("wWinMain");
+ if (HaveMain || HaveWMain) {
+ if (HaveWinMain || HaveWWinMain) {
+ warn(std::string("found ") + (HaveMain ? "main" : "wmain") + " and " +
+ (HaveWinMain ? "WinMain" : "wWinMain") +
+ "; defaulting to /subsystem:console");
+ }
+ return IMAGE_SUBSYSTEM_WINDOWS_CUI;
+ }
+ if (HaveWinMain || HaveWWinMain)
return IMAGE_SUBSYSTEM_WINDOWS_GUI;
return IMAGE_SUBSYSTEM_UNKNOWN;
}
@@ -500,26 +534,65 @@ static std::string createResponseFile(const opt::InputArgList &Args,
return Data.str();
}
-static unsigned getDefaultDebugType(const opt::InputArgList &Args) {
- unsigned DebugTypes = static_cast<unsigned>(DebugType::CV);
+enum class DebugKind { Unknown, None, Full, FastLink, GHash, Dwarf, Symtab };
+
+static DebugKind parseDebugKind(const opt::InputArgList &Args) {
+ auto *A = Args.getLastArg(OPT_debug, OPT_debug_opt);
+ if (!A)
+ return DebugKind::None;
+ if (A->getNumValues() == 0)
+ return DebugKind::Full;
+
+ DebugKind Debug = StringSwitch<DebugKind>(A->getValue())
+ .CaseLower("none", DebugKind::None)
+ .CaseLower("full", DebugKind::Full)
+ .CaseLower("fastlink", DebugKind::FastLink)
+ // LLD extensions
+ .CaseLower("ghash", DebugKind::GHash)
+ .CaseLower("dwarf", DebugKind::Dwarf)
+ .CaseLower("symtab", DebugKind::Symtab)
+ .Default(DebugKind::Unknown);
+
+ if (Debug == DebugKind::FastLink) {
+ warn("/debug:fastlink unsupported; using /debug:full");
+ return DebugKind::Full;
+ }
+ if (Debug == DebugKind::Unknown) {
+ error("/debug: unknown option: " + Twine(A->getValue()));
+ return DebugKind::None;
+ }
+ return Debug;
+}
+
+static unsigned parseDebugTypes(const opt::InputArgList &Args) {
+ unsigned DebugTypes = static_cast<unsigned>(DebugType::None);
+
+ if (auto *A = Args.getLastArg(OPT_debugtype)) {
+ SmallVector<StringRef, 3> Types;
+ A->getSpelling().split(Types, ',', /*KeepEmpty=*/false);
+
+ for (StringRef Type : Types) {
+ unsigned V = StringSwitch<unsigned>(Type.lower())
+ .Case("cv", static_cast<unsigned>(DebugType::CV))
+ .Case("pdata", static_cast<unsigned>(DebugType::PData))
+ .Case("fixup", static_cast<unsigned>(DebugType::Fixup))
+ .Default(0);
+ if (V == 0) {
+ warn("/debugtype: unknown option: " + Twine(A->getValue()));
+ continue;
+ }
+ DebugTypes |= V;
+ }
+ return DebugTypes;
+ }
+
+ // Default debug types
+ DebugTypes = static_cast<unsigned>(DebugType::CV);
if (Args.hasArg(OPT_driver))
DebugTypes |= static_cast<unsigned>(DebugType::PData);
if (Args.hasArg(OPT_profile))
DebugTypes |= static_cast<unsigned>(DebugType::Fixup);
- return DebugTypes;
-}
-static unsigned parseDebugType(StringRef Arg) {
- SmallVector<StringRef, 3> Types;
- Arg.split(Types, ',', /*KeepEmpty=*/false);
-
- unsigned DebugTypes = static_cast<unsigned>(DebugType::None);
- for (StringRef Type : Types)
- DebugTypes |= StringSwitch<unsigned>(Type.lower())
- .Case("cv", static_cast<unsigned>(DebugType::CV))
- .Case("pdata", static_cast<unsigned>(DebugType::PData))
- .Case("fixup", static_cast<unsigned>(DebugType::Fixup))
- .Default(0);
return DebugTypes;
}
@@ -679,131 +752,6 @@ static void parseModuleDefs(StringRef Path) {
}
}
-// A helper function for filterBitcodeFiles.
-static bool needsRebuilding(MemoryBufferRef MB) {
- // The MSVC linker doesn't support thin archives, so if it's a thin
- // archive, we always need to rebuild it.
- std::unique_ptr<Archive> File =
- CHECK(Archive::create(MB), "Failed to read " + MB.getBufferIdentifier());
- 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<std::string>
-filterBitcodeFiles(StringRef Path, std::vector<std::string> &TemporaryFiles) {
- std::unique_ptr<MemoryBuffer> MB = CHECK(
- MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path);
- MemoryBufferRef MBRef = MB->getMemBufferRef();
- file_magic Magic = identify_magic(MBRef.getBuffer());
-
- if (Magic == file_magic::bitcode)
- return None;
- if (Magic != file_magic::archive)
- return Path.str();
- if (!needsRebuilding(MBRef))
- return Path.str();
-
- std::unique_ptr<Archive> File =
- CHECK(Archive::create(MBRef),
- MBRef.getBufferIdentifier() + ": failed to parse archive");
-
- std::vector<NewArchiveMember> 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 (std::error_code EC = sys::fs::createTemporaryFile(
- "lld-" + sys::path::stem(Path), ".lib", S))
- fatal("cannot create a temporary file: " + EC.message());
- std::string Temp = S.str();
- TemporaryFiles.push_back(Temp);
-
- Error E =
- llvm::writeArchive(Temp, New, /*WriteSymtab=*/true, Archive::Kind::K_GNU,
- /*Deterministics=*/true,
- /*Thin=*/false);
- handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) {
- error("failed to create a new archive " + S.str() + ": " + EI.message());
- });
- return Temp;
-}
-
-// Create response file contents and invoke the MSVC linker.
-void LinkerDriver::invokeMSVC(opt::InputArgList &Args) {
- std::string Rsp = "/nologo\n";
- std::vector<std::string> Temps;
-
- // Write out archive members that we used in symbol resolution and pass these
- // to MSVC before any archives, so that MSVC uses the same objects to satisfy
- // references.
- for (ObjFile *Obj : ObjFile::Instances) {
- if (Obj->ParentName.empty())
- continue;
- SmallString<128> S;
- int Fd;
- if (auto EC = sys::fs::createTemporaryFile(
- "lld-" + sys::path::filename(Obj->ParentName), ".obj", Fd, S))
- fatal("cannot create a temporary file: " + EC.message());
- raw_fd_ostream OS(Fd, /*shouldClose*/ true);
- OS << Obj->MB.getBuffer();
- Temps.push_back(S.str());
- Rsp += quote(S) + "\n";
- }
-
- 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<StringRef> Path = doFindFile(Arg->getValue())) {
- if (Optional<std::string> S = filterBitcodeFiles(*Path, Temps))
- Rsp += quote(*S) + "\n";
- continue;
- }
- Rsp += quote(Arg->getValue()) + "\n";
- break;
- }
- default:
- Rsp += toString(*Arg) + "\n";
- }
- }
-
- std::vector<StringRef> ObjFiles = Symtab->compileBitcodeFiles();
- runMSVCLinker(Rsp, ObjFiles);
-
- for (StringRef Path : Temps)
- sys::fs::remove(Path);
-}
-
void LinkerDriver::enqueueTask(std::function<void()> Task) {
TaskQueue.push_back(std::move(Task));
}
@@ -859,6 +807,97 @@ static void parseOrderFile(StringRef Arg) {
}
}
+static void markAddrsig(Symbol *S) {
+ if (auto *D = dyn_cast_or_null<Defined>(S))
+ if (Chunk *C = D->getChunk())
+ C->KeepUnique = true;
+}
+
+static void findKeepUniqueSections() {
+ // Exported symbols could be address-significant in other executables or DSOs,
+ // so we conservatively mark them as address-significant.
+ for (Export &R : Config->Exports)
+ markAddrsig(R.Sym);
+
+ // Visit the address-significance table in each object file and mark each
+ // referenced symbol as address-significant.
+ for (ObjFile *Obj : ObjFile::Instances) {
+ ArrayRef<Symbol *> Syms = Obj->getSymbols();
+ if (Obj->AddrsigSec) {
+ ArrayRef<uint8_t> Contents;
+ Obj->getCOFFObj()->getSectionContents(Obj->AddrsigSec, Contents);
+ const uint8_t *Cur = Contents.begin();
+ while (Cur != Contents.end()) {
+ unsigned Size;
+ const char *Err;
+ uint64_t SymIndex = decodeULEB128(Cur, &Size, Contents.end(), &Err);
+ if (Err)
+ fatal(toString(Obj) + ": could not decode addrsig section: " + Err);
+ if (SymIndex >= Syms.size())
+ fatal(toString(Obj) + ": invalid symbol index in addrsig section");
+ markAddrsig(Syms[SymIndex]);
+ Cur += Size;
+ }
+ } else {
+ // If an object file does not have an address-significance table,
+ // conservatively mark all of its symbols as address-significant.
+ for (Symbol *S : Syms)
+ markAddrsig(S);
+ }
+ }
+}
+
+// link.exe replaces each %foo% in AltPath with the contents of environment
+// variable foo, and adds the two magic env vars _PDB (expands to the basename
+// of pdb's output path) and _EXT (expands to the extension of the output
+// binary).
+// lld only supports %_PDB% and %_EXT% and warns on references to all other env
+// vars.
+static void parsePDBAltPath(StringRef AltPath) {
+ SmallString<128> Buf;
+ StringRef PDBBasename =
+ sys::path::filename(Config->PDBPath, sys::path::Style::windows);
+ StringRef BinaryExtension =
+ sys::path::extension(Config->OutputFile, sys::path::Style::windows);
+ if (!BinaryExtension.empty())
+ BinaryExtension = BinaryExtension.substr(1); // %_EXT% does not include '.'.
+
+ // Invariant:
+ // +--------- Cursor ('a...' might be the empty string).
+ // | +----- FirstMark
+ // | | +- SecondMark
+ // v v v
+ // a...%...%...
+ size_t Cursor = 0;
+ while (Cursor < AltPath.size()) {
+ size_t FirstMark, SecondMark;
+ if ((FirstMark = AltPath.find('%', Cursor)) == StringRef::npos ||
+ (SecondMark = AltPath.find('%', FirstMark + 1)) == StringRef::npos) {
+ // Didn't find another full fragment, treat rest of string as literal.
+ Buf.append(AltPath.substr(Cursor));
+ break;
+ }
+
+ // Found a full fragment. Append text in front of first %, and interpret
+ // text between first and second % as variable name.
+ Buf.append(AltPath.substr(Cursor, FirstMark - Cursor));
+ StringRef Var = AltPath.substr(FirstMark, SecondMark - FirstMark + 1);
+ if (Var.equals_lower("%_pdb%"))
+ Buf.append(PDBBasename);
+ else if (Var.equals_lower("%_ext%"))
+ Buf.append(BinaryExtension);
+ else {
+ warn("only %_PDB% and %_EXT% supported in /pdbaltpath:, keeping " +
+ Var + " as literal");
+ Buf.append(Var);
+ }
+
+ Cursor = SecondMark + 1;
+ }
+
+ Config->PDBAltPath = Buf;
+}
+
void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// If the first command line argument is "/lib", link.exe acts like lib.exe.
// We call our own implementation of lib.exe that understands bitcode files.
@@ -947,11 +986,17 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Handle /ignore
for (auto *Arg : Args.filtered(OPT_ignore)) {
- if (StringRef(Arg->getValue()) == "4037")
- Config->WarnMissingOrderSymbol = false;
- else if (StringRef(Arg->getValue()) == "4217")
- Config->WarnLocallyDefinedImported = false;
- // Other warning numbers are ignored.
+ SmallVector<StringRef, 8> Vec;
+ StringRef(Arg->getValue()).split(Vec, ',');
+ for (StringRef S : Vec) {
+ if (S == "4037")
+ Config->WarnMissingOrderSymbol = false;
+ else if (S == "4099")
+ Config->WarnDebugInfoUnusable = false;
+ else if (S == "4217")
+ Config->WarnLocallyDefinedImported = false;
+ // Other warning numbers are ignored.
+ }
}
// Handle /out
@@ -965,20 +1010,26 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Handle /force or /force:unresolved
if (Args.hasArg(OPT_force, OPT_force_unresolved))
- Config->Force = true;
+ Config->ForceUnresolved = true;
+
+ // Handle /force or /force:multiple
+ if (Args.hasArg(OPT_force, OPT_force_multiple))
+ Config->ForceMultiple = true;
// Handle /debug
- if (Args.hasArg(OPT_debug, OPT_debug_dwarf, OPT_debug_ghash)) {
+ DebugKind Debug = parseDebugKind(Args);
+ if (Debug == DebugKind::Full || Debug == DebugKind::Dwarf ||
+ Debug == DebugKind::GHash) {
Config->Debug = true;
Config->Incremental = true;
- if (auto *Arg = Args.getLastArg(OPT_debugtype))
- Config->DebugTypes = parseDebugType(Arg->getValue());
- else
- Config->DebugTypes = getDefaultDebugType(Args);
}
+ // Handle /debugtype
+ Config->DebugTypes = parseDebugTypes(Args);
+
// Handle /pdb
- bool ShouldCreatePDB = Args.hasArg(OPT_debug, OPT_debug_ghash);
+ bool ShouldCreatePDB =
+ (Debug == DebugKind::Full || Debug == DebugKind::GHash);
if (ShouldCreatePDB) {
if (auto *Arg = Args.getLastArg(OPT_pdb))
Config->PDBPath = Arg->getValue();
@@ -1099,7 +1150,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
Config->Implib = Arg->getValue();
// Handle /opt.
- bool DoGC = !Args.hasArg(OPT_debug) || Args.hasArg(OPT_profile);
+ bool DoGC = Debug == DebugKind::None || Args.hasArg(OPT_profile);
unsigned ICFLevel =
Args.hasArg(OPT_profile) ? 0 : 1; // 0: off, 1: limited, 2: on
unsigned TailMerge = 1;
@@ -1184,6 +1235,12 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
parseMerge(".xdata=.rdata");
parseMerge(".bss=.data");
+ if (Config->MinGW) {
+ parseMerge(".ctors=.rdata");
+ parseMerge(".dtors=.rdata");
+ parseMerge(".CRT=.rdata");
+ }
+
// Handle /section
for (auto *Arg : Args.filtered(OPT_section))
parseSection(Arg->getValue());
@@ -1237,9 +1294,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
Config->NxCompat = Args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true);
Config->TerminalServerAware =
!Config->DLL && Args.hasFlag(OPT_tsaware, OPT_tsaware_no, true);
- Config->DebugDwarf = Args.hasArg(OPT_debug_dwarf);
- Config->DebugGHashes = Args.hasArg(OPT_debug_ghash);
- Config->DebugSymtab = Args.hasArg(OPT_debug_symtab);
+ Config->DebugDwarf = Debug == DebugKind::Dwarf;
+ Config->DebugGHashes = Debug == DebugKind::GHash;
+ Config->DebugSymtab = Debug == DebugKind::Symtab;
Config->MapFile = getMapFile(Args);
@@ -1269,10 +1326,14 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
return;
std::set<sys::fs::UniqueID> WholeArchives;
- for (auto *Arg : Args.filtered(OPT_wholearchive_file))
- if (Optional<StringRef> Path = doFindFile(Arg->getValue()))
+ AutoExporter Exporter;
+ for (auto *Arg : Args.filtered(OPT_wholearchive_file)) {
+ if (Optional<StringRef> Path = doFindFile(Arg->getValue())) {
if (Optional<sys::fs::UniqueID> ID = getUniqueID(*Path))
WholeArchives.insert(*ID);
+ Exporter.addWholeArchive(*Path);
+ }
+ }
// A predicate returning true if a given path is an argument for
// /wholearchive:, or /wholearchive is enabled globally.
@@ -1303,12 +1364,16 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Read all input files given via the command line.
run();
+ if (errorCount())
+ return;
+
// 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) {
warn("/machine is not specified. x64 is assumed");
Config->Machine = AMD64;
}
+ Config->Wordsize = Config->is64() ? 8 : 4;
// Input files can be Windows resource files (.res files). We use
// WindowsResource to convert resource files to a regular COFF file,
@@ -1335,25 +1400,6 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
error("/dynamicbase:no is not compatible with " +
machineToStr(Config->Machine));
- // Handle /entry and /dll
- if (auto *Arg = Args.getLastArg(OPT_entry)) {
- Config->Entry = addUndefined(mangle(Arg->getValue()));
- } else if (!Config->Entry && !Config->NoEntry) {
- if (Args.hasArg(OPT_dll)) {
- StringRef S = (Config->Machine == I386) ? "__DllMainCRTStartup@12"
- : "_DllMainCRTStartup";
- Config->Entry = addUndefined(S);
- } else {
- // Windows specific -- If entry point name is not given, we need to
- // infer that from user-defined entry name.
- StringRef S = findDefaultEntry();
- if (S.empty())
- fatal("entry point must be defined");
- Config->Entry = addUndefined(S);
- log("Entry name inferred: " + S);
- }
- }
-
// Handle /export
for (auto *Arg : Args.filtered(OPT_export)) {
Export E = parseExport(Arg->getValue());
@@ -1379,6 +1425,34 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
return;
}
+ // Windows specific -- if no /subsystem is given, we need to infer
+ // that from entry point name. Must happen before /entry handling,
+ // and after the early return when just writing an import library.
+ if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) {
+ Config->Subsystem = inferSubsystem();
+ if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN)
+ fatal("subsystem must be defined");
+ }
+
+ // Handle /entry and /dll
+ if (auto *Arg = Args.getLastArg(OPT_entry)) {
+ Config->Entry = addUndefined(mangle(Arg->getValue()));
+ } else if (!Config->Entry && !Config->NoEntry) {
+ if (Args.hasArg(OPT_dll)) {
+ StringRef S = (Config->Machine == I386) ? "__DllMainCRTStartup@12"
+ : "_DllMainCRTStartup";
+ Config->Entry = addUndefined(S);
+ } else {
+ // Windows specific -- If entry point name is not given, we need to
+ // infer that from user-defined entry name.
+ StringRef S = findDefaultEntry();
+ if (S.empty())
+ fatal("entry point must be defined");
+ Config->Entry = addUndefined(S);
+ log("Entry name inferred: " + S);
+ }
+ }
+
// Handle /delayload
for (auto *Arg : Args.filtered(OPT_delayload)) {
Config->DelayLoads.insert(StringRef(Arg->getValue()).lower());
@@ -1412,6 +1486,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// tools won't work correctly if these assumptions are not held.
sys::fs::make_absolute(Config->PDBAltPath);
sys::path::remove_dots(Config->PDBAltPath);
+ } else {
+ // Don't do this earlier, so that Config->OutputFile is ready.
+ parsePDBAltPath(Config->PDBAltPath);
}
}
@@ -1435,6 +1512,13 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Needed for MSVC 2017 15.5 CRT.
Symtab->addAbsolute(mangle("__enclave_config"), 0);
+ if (Config->MinGW) {
+ Symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0);
+ Symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0);
+ Symtab->addAbsolute(mangle("__CTOR_LIST__"), 0);
+ Symtab->addAbsolute(mangle("__DTOR_LIST__"), 0);
+ }
+
// This code may add new undefined symbols to the link, which may enqueue more
// symbol resolution tasks, so we need to continue executing tasks until we
// converge.
@@ -1474,31 +1558,34 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
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);
- return;
- }
-
// Do LTO by compiling bitcode input files to a set of native COFF files then
// link those files.
Symtab->addCombinedLTOObjects();
run();
+ if (Config->MinGW) {
+ // Load any further object files that might be needed for doing automatic
+ // imports.
+ //
+ // For cases with no automatically imported symbols, this iterates once
+ // over the symbol table and doesn't do anything.
+ //
+ // For the normal case with a few automatically imported symbols, this
+ // should only need to be run once, since each new object file imported
+ // is an import library and wouldn't add any new undefined references,
+ // but there's nothing stopping the __imp_ symbols from coming from a
+ // normal object file as well (although that won't be used for the
+ // actual autoimport later on). If this pass adds new undefined references,
+ // we won't iterate further to resolve them.
+ Symtab->loadMinGWAutomaticImports();
+ run();
+ }
+
// Make sure we have resolved all symbols.
Symtab->reportRemainingUndefines();
if (errorCount())
return;
- // Windows specific -- if no /subsystem is given, we need to infer
- // that from entry point name.
- if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) {
- Config->Subsystem = inferSubsystem();
- if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN)
- fatal("subsystem must be defined");
- }
-
// Handle /safeseh.
if (Args.hasFlag(OPT_safeseh, OPT_safeseh_no, false)) {
for (ObjFile *File : ObjFile::Instances)
@@ -1512,7 +1599,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// are chosen to be exported.
if (Config->DLL && ((Config->MinGW && Config->Exports.empty()) ||
Args.hasArg(OPT_export_all_symbols))) {
- AutoExporter Exporter;
+ Exporter.initSymbolExcludes();
Symtab->forEachSymbol([=](Symbol *S) {
auto *Def = dyn_cast<Defined>(S);
@@ -1551,11 +1638,11 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
continue;
}
+ // If the symbol isn't common, it must have been replaced with a regular
+ // symbol, which will carry its own alignment.
auto *DC = dyn_cast<DefinedCommon>(Sym);
- if (!DC) {
- warn("/aligncomm symbol " + Name + " of wrong kind");
+ if (!DC)
continue;
- }
CommonChunk *C = DC->getChunk();
C->Alignment = std::max(C->Alignment, Alignment);
@@ -1576,8 +1663,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
markLive(Symtab->getChunks());
// Identify identical COMDAT sections to merge them.
- if (Config->DoICF)
+ if (Config->DoICF) {
+ findKeepUniqueSections();
doICF(Symtab->getChunks());
+ }
// Write the result.
writeResult();
diff --git a/COFF/Driver.h b/COFF/Driver.h
index 627e991a9028..e779721ab75d 100644
--- a/COFF/Driver.h
+++ b/COFF/Driver.h
@@ -89,6 +89,7 @@ private:
Optional<StringRef> findLib(StringRef Filename);
StringRef doFindFile(StringRef Filename);
StringRef doFindLib(StringRef Filename);
+ StringRef doFindLibMinGW(StringRef Filename);
// Parses LIB environment which contains a list of search paths.
void addLibSearchPaths();
@@ -103,7 +104,6 @@ private:
std::set<std::string> VisitedLibs;
Symbol *addUndefined(StringRef Sym);
- StringRef mangle(StringRef Sym);
// Windows specific -- "main" is not the only main function in Windows.
// You can choose one from these four -- {w,}{WinMain,main}.
@@ -115,8 +115,6 @@ private:
StringRef findDefaultEntry();
WindowsSubsystem inferSubsystem();
- void invokeMSVC(llvm::opt::InputArgList &Args);
-
void addBuffer(std::unique_ptr<MemoryBuffer> MB, bool WholeArchive);
void addArchiveBuffer(MemoryBufferRef MBRef, StringRef SymName,
StringRef ParentName);
diff --git a/COFF/DriverUtils.cpp b/COFF/DriverUtils.cpp
index c12e791f9507..3a11895497a4 100644
--- a/COFF/DriverUtils.cpp
+++ b/COFF/DriverUtils.cpp
@@ -713,26 +713,6 @@ MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> MBs) {
return MBRef;
}
-// 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<StringRef> Objects) {
- // Write the in-memory object files to disk.
- std::vector<TemporaryFile> Temps;
- for (StringRef S : Objects) {
- Temps.emplace_back("lto", "obj", S);
- Rsp += quote(Temps.back().Path) + "\n";
- }
-
- 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
@@ -883,7 +863,9 @@ std::vector<const char *> ArgParser::tokenize(StringRef S) {
}
void printHelp(const char *Argv0) {
- COFFOptTable().PrintHelp(outs(), Argv0, "LLVM Linker", false);
+ COFFOptTable().PrintHelp(outs(),
+ (std::string(Argv0) + " [options] file...").c_str(),
+ "LLVM Linker", false);
}
} // namespace coff
diff --git a/COFF/ICF.cpp b/COFF/ICF.cpp
index 7feb3c4e0b0c..34ea360fa925 100644
--- a/COFF/ICF.cpp
+++ b/COFF/ICF.cpp
@@ -22,6 +22,7 @@
#include "Chunks.h"
#include "Symbols.h"
#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Threads.h"
#include "lld/Common/Timer.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/Support/Debug.h"
@@ -80,7 +81,7 @@ private:
bool ICF::isEligible(SectionChunk *C) {
// Non-comdat chunks, dead chunks, and writable chunks are not elegible.
bool Writable = C->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
- if (!C->isCOMDAT() || !C->isLive() || Writable)
+ if (!C->isCOMDAT() || !C->Live || Writable)
return false;
// Code sections are eligible.
@@ -93,7 +94,11 @@ bool ICF::isEligible(SectionChunk *C) {
return true;
// So are vtables.
- return C->Sym && C->Sym->getName().startswith("??_7");
+ if (C->Sym && C->Sym->getName().startswith("??_7"))
+ return true;
+
+ // Anything else not in an address-significance table is eligible.
+ return !C->KeepUnique;
}
// Split an equivalence class into smaller classes.
@@ -222,10 +227,10 @@ void ICF::forEachClass(std::function<void(size_t, size_t)> Fn) {
size_t Boundaries[NumShards + 1];
Boundaries[0] = 0;
Boundaries[NumShards] = Chunks.size();
- for_each_n(parallel::par, size_t(1), NumShards, [&](size_t I) {
+ parallelForEachN(1, NumShards, [&](size_t I) {
Boundaries[I] = findBoundary((I - 1) * Step, Chunks.size());
});
- for_each_n(parallel::par, size_t(1), NumShards + 1, [&](size_t I) {
+ parallelForEachN(1, NumShards + 1, [&](size_t I) {
if (Boundaries[I - 1] < Boundaries[I]) {
forEachClassRange(Boundaries[I - 1], Boundaries[I], Fn);
}
@@ -257,9 +262,19 @@ void ICF::run(ArrayRef<Chunk *> Vec) {
SC->Class[0] = NextId++;
// Initially, we use hash values to partition sections.
- for_each(parallel::par, Chunks.begin(), Chunks.end(), [&](SectionChunk *SC) {
+ parallelForEach(Chunks, [&](SectionChunk *SC) {
+ SC->Class[1] = xxHash64(SC->getContents());
+ });
+
+ // Combine the hashes of the sections referenced by each section into its
+ // hash.
+ parallelForEach(Chunks, [&](SectionChunk *SC) {
+ uint32_t Hash = SC->Class[1];
+ for (Symbol *B : SC->symbols())
+ if (auto *Sym = dyn_cast_or_null<DefinedRegular>(B))
+ Hash ^= Sym->getChunk()->Class[1];
// Set MSB to 1 to avoid collisions with non-hash classs.
- SC->Class[0] = xxHash64(SC->getContents()) | (1 << 31);
+ SC->Class[0] = Hash | (1U << 31);
});
// From now on, sections in Chunks are ordered so that sections in
diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp
index 2b3e65fae04b..236c90ef0388 100644
--- a/COFF/InputFiles.cpp
+++ b/COFF/InputFiles.cpp
@@ -54,8 +54,16 @@ std::vector<BitcodeFile *> BitcodeFile::Instances;
static void checkAndSetWeakAlias(SymbolTable *Symtab, InputFile *F,
Symbol *Source, Symbol *Target) {
if (auto *U = dyn_cast<Undefined>(Source)) {
- if (U->WeakAlias && U->WeakAlias != Target)
+ if (U->WeakAlias && U->WeakAlias != Target) {
+ // Weak aliases as produced by GCC are named in the form
+ // .weak.<weaksymbol>.<othersymbol>, where <othersymbol> is the name
+ // of another symbol emitted near the weak symbol.
+ // Just use the definition from the first object file that defined
+ // this weak symbol.
+ if (Config->MinGW)
+ return;
Symtab->reportDuplicate(Source, F);
+ }
U->WeakAlias = Target;
}
}
@@ -147,9 +155,10 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
const coff_aux_section_definition *Def,
StringRef LeaderName) {
const coff_section *Sec;
- StringRef Name;
if (auto EC = COFFObj->getSection(SectionNumber, Sec))
fatal("getSection failed: #" + Twine(SectionNumber) + ": " + EC.message());
+
+ StringRef Name;
if (auto EC = COFFObj->getSectionName(Sec, Name))
fatal("getSectionName failed: #" + Twine(SectionNumber) + ": " +
EC.message());
@@ -161,6 +170,11 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
return nullptr;
}
+ if (Name == ".llvm_addrsig") {
+ AddrsigSec = Sec;
+ return nullptr;
+ }
+
// Object files may have DWARF debug info or MS CodeView debug info
// (or both).
//
@@ -168,8 +182,8 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
// of the linker; they are just a data section containing relocations.
// We can just link them to complete debug info.
//
- // CodeView needs a linker support. We need to interpret and debug
- // info, and then write it to a separate .pdb file.
+ // CodeView needs linker support. We need to interpret debug info,
+ // and then write it to a separate .pdb file.
// Ignore DWARF debug info unless /debug is given.
if (!Config->Debug && Name.startswith(".debug_"))
@@ -205,7 +219,13 @@ SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
void ObjFile::readAssociativeDefinition(
COFFSymbolRef Sym, const coff_aux_section_definition *Def) {
- SectionChunk *Parent = SparseChunks[Def->getNumber(Sym.isBigObj())];
+ readAssociativeDefinition(Sym, Def, Def->getNumber(Sym.isBigObj()));
+}
+
+void ObjFile::readAssociativeDefinition(COFFSymbolRef Sym,
+ const coff_aux_section_definition *Def,
+ uint32_t ParentSection) {
+ SectionChunk *Parent = SparseChunks[ParentSection];
// If the parent is pending, it probably means that its section definition
// appears after us in the symbol table. Leave the associated section as
@@ -225,6 +245,35 @@ void ObjFile::readAssociativeDefinition(
}
}
+void ObjFile::recordPrevailingSymbolForMingw(
+ COFFSymbolRef Sym, DenseMap<StringRef, uint32_t> &PrevailingSectionMap) {
+ // For comdat symbols in executable sections, where this is the copy
+ // of the section chunk we actually include instead of discarding it,
+ // add the symbol to a map to allow using it for implicitly
+ // associating .[px]data$<func> sections to it.
+ int32_t SectionNumber = Sym.getSectionNumber();
+ SectionChunk *SC = SparseChunks[SectionNumber];
+ if (SC && SC->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) {
+ StringRef Name;
+ COFFObj->getSymbolName(Sym, Name);
+ PrevailingSectionMap[Name] = SectionNumber;
+ }
+}
+
+void ObjFile::maybeAssociateSEHForMingw(
+ COFFSymbolRef Sym, const coff_aux_section_definition *Def,
+ const DenseMap<StringRef, uint32_t> &PrevailingSectionMap) {
+ StringRef Name;
+ COFFObj->getSymbolName(Sym, Name);
+ if (Name.consume_front(".pdata$") || Name.consume_front(".xdata$")) {
+ // For MinGW, treat .[px]data$<func> as implicitly associative to
+ // the symbol <func>.
+ auto ParentSym = PrevailingSectionMap.find(Name);
+ if (ParentSym != PrevailingSectionMap.end())
+ readAssociativeDefinition(Sym, Def, ParentSym->second);
+ }
+}
+
Symbol *ObjFile::createRegular(COFFSymbolRef Sym) {
SectionChunk *SC = SparseChunks[Sym.getSectionNumber()];
if (Sym.isExternal()) {
@@ -232,10 +281,17 @@ Symbol *ObjFile::createRegular(COFFSymbolRef Sym) {
COFFObj->getSymbolName(Sym, Name);
if (SC)
return Symtab->addRegular(this, Name, Sym.getGeneric(), SC);
+ // For MinGW symbols named .weak.* that point to a discarded section,
+ // don't create an Undefined symbol. If nothing ever refers to the symbol,
+ // everything should be fine. If something actually refers to the symbol
+ // (e.g. the undefined weak alias), linking will fail due to undefined
+ // references at the end.
+ if (Config->MinGW && Name.startswith(".weak."))
+ return nullptr;
return Symtab->addUndefined(Name, this, false);
}
if (SC)
- return make<DefinedRegular>(this, /*Name*/ "", false,
+ return make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false,
/*IsExternal*/ false, Sym.getGeneric(), SC);
return nullptr;
}
@@ -248,19 +304,24 @@ void ObjFile::initializeSymbols() {
std::vector<uint32_t> PendingIndexes;
PendingIndexes.reserve(NumSymbols);
+ DenseMap<StringRef, uint32_t> PrevailingSectionMap;
std::vector<const coff_aux_section_definition *> ComdatDefs(
COFFObj->getNumberOfSections() + 1);
for (uint32_t I = 0; I < NumSymbols; ++I) {
COFFSymbolRef COFFSym = check(COFFObj->getSymbol(I));
+ bool PrevailingComdat;
if (COFFSym.isUndefined()) {
Symbols[I] = createUndefined(COFFSym);
} else if (COFFSym.isWeakExternal()) {
Symbols[I] = createUndefined(COFFSym);
uint32_t TagIndex = COFFSym.getAux<coff_aux_weak_external>()->TagIndex;
WeakAliases.emplace_back(Symbols[I], TagIndex);
- } else if (Optional<Symbol *> OptSym = createDefined(COFFSym, ComdatDefs)) {
+ } else if (Optional<Symbol *> OptSym =
+ createDefined(COFFSym, ComdatDefs, PrevailingComdat)) {
Symbols[I] = *OptSym;
+ if (Config->MinGW && PrevailingComdat)
+ recordPrevailingSymbolForMingw(COFFSym, PrevailingSectionMap);
} else {
// createDefined() returns None if a symbol belongs to a section that
// was pending at the point when the symbol was read. This can happen in
@@ -278,9 +339,12 @@ void ObjFile::initializeSymbols() {
for (uint32_t I : PendingIndexes) {
COFFSymbolRef Sym = check(COFFObj->getSymbol(I));
- if (auto *Def = Sym.getSectionDefinition())
+ if (const coff_aux_section_definition *Def = Sym.getSectionDefinition()) {
if (Def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
readAssociativeDefinition(Sym, Def);
+ else if (Config->MinGW)
+ maybeAssociateSEHForMingw(Sym, Def, PrevailingSectionMap);
+ }
if (SparseChunks[Sym.getSectionNumber()] == PendingComdat) {
StringRef Name;
COFFObj->getSymbolName(Sym, Name);
@@ -306,7 +370,9 @@ Symbol *ObjFile::createUndefined(COFFSymbolRef Sym) {
Optional<Symbol *> ObjFile::createDefined(
COFFSymbolRef Sym,
- std::vector<const coff_aux_section_definition *> &ComdatDefs) {
+ std::vector<const coff_aux_section_definition *> &ComdatDefs,
+ bool &Prevailing) {
+ Prevailing = false;
auto GetName = [&]() {
StringRef S;
COFFObj->getSymbolName(Sym, S);
@@ -352,12 +418,11 @@ Optional<Symbol *> ObjFile::createDefined(
if (const coff_aux_section_definition *Def = ComdatDefs[SectionNumber]) {
ComdatDefs[SectionNumber] = nullptr;
Symbol *Leader;
- bool Prevailing;
if (Sym.isExternal()) {
std::tie(Leader, Prevailing) =
Symtab->addComdat(this, GetName(), Sym.getGeneric());
} else {
- Leader = make<DefinedRegular>(this, /*Name*/ "", false,
+ Leader = make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false,
/*IsExternal*/ false, Sym.getGeneric());
Prevailing = true;
}
@@ -377,7 +442,7 @@ Optional<Symbol *> ObjFile::createDefined(
// leader symbol by setting the section's ComdatDefs pointer if we encounter a
// non-associative comdat.
if (SparseChunks[SectionNumber] == PendingComdat) {
- if (auto *Def = Sym.getSectionDefinition()) {
+ if (const coff_aux_section_definition *Def = Sym.getSectionDefinition()) {
if (Def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
readAssociativeDefinition(Sym, Def);
else
@@ -385,8 +450,10 @@ Optional<Symbol *> ObjFile::createDefined(
}
}
+ // readAssociativeDefinition() writes to SparseChunks, so need to check again.
if (SparseChunks[SectionNumber] == PendingComdat)
return None;
+
return createRegular(Sym);
}
@@ -437,6 +504,10 @@ void ImportFile::parse() {
ExternalName = ExtName;
ImpSym = Symtab->addImportData(ImpName, this);
+ // If this was a duplicate, we logged an error but may continue;
+ // in this case, ImpSym is nullptr.
+ if (!ImpSym)
+ return;
if (Hdr->getType() == llvm::COFF::IMPORT_CONST)
static_cast<void>(Symtab->addImportData(Name, this));
diff --git a/COFF/InputFiles.h b/COFF/InputFiles.h
index 4ee4b363886f..ec802f2d0300 100644
--- a/COFF/InputFiles.h
+++ b/COFF/InputFiles.h
@@ -13,7 +13,9 @@
#include "Config.h"
#include "lld/Common/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
+#include "llvm/DebugInfo/CodeView/TypeRecord.h"
#include "llvm/LTO/LTO.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/COFF.h"
@@ -121,9 +123,12 @@ public:
return Symbols[SymbolIndex];
}
- // Returns the underying COFF file.
+ // Returns the underlying COFF file.
COFFObjectFile *getCOFFObj() { return COFFObj.get(); }
+ // Whether the object was already merged into the final PDB or not
+ bool wasProcessedForPDB() const { return !!ModuleDBI; }
+
static std::vector<ObjFile *> Instances;
// Flags in the absolute @feat.00 symbol if it is present. These usually
@@ -144,6 +149,13 @@ public:
// if we are not producing a PDB.
llvm::pdb::DbiModuleDescriptorBuilder *ModuleDBI = nullptr;
+ const coff_section *AddrsigSec = nullptr;
+
+ // When using Microsoft precompiled headers, this is the PCH's key.
+ // The same key is used by both the precompiled object, and objects using the
+ // precompiled object. Any difference indicates out-of-date objects.
+ llvm::Optional<uint32_t> PCHSignature;
+
private:
void initializeChunks();
void initializeSymbols();
@@ -157,10 +169,24 @@ private:
COFFSymbolRef COFFSym,
const llvm::object::coff_aux_section_definition *Def);
+ void readAssociativeDefinition(
+ COFFSymbolRef COFFSym,
+ const llvm::object::coff_aux_section_definition *Def,
+ uint32_t ParentSection);
+
+ void recordPrevailingSymbolForMingw(
+ COFFSymbolRef COFFSym,
+ llvm::DenseMap<StringRef, uint32_t> &PrevailingSectionMap);
+
+ void maybeAssociateSEHForMingw(
+ COFFSymbolRef Sym, const llvm::object::coff_aux_section_definition *Def,
+ const llvm::DenseMap<StringRef, uint32_t> &PrevailingSectionMap);
+
llvm::Optional<Symbol *>
createDefined(COFFSymbolRef Sym,
std::vector<const llvm::object::coff_aux_section_definition *>
- &ComdatDefs);
+ &ComdatDefs,
+ bool &PrevailingComdat);
Symbol *createRegular(COFFSymbolRef Sym);
Symbol *createUndefined(COFFSymbolRef Sym);
diff --git a/COFF/LTO.cpp b/COFF/LTO.cpp
index 93f7ba3f9e4c..92d9ff0937c0 100644
--- a/COFF/LTO.cpp
+++ b/COFF/LTO.cpp
@@ -60,6 +60,9 @@ static std::unique_ptr<lto::LTO> createLTO() {
C.DisableVerify = true;
C.DiagHandler = diagnosticHandler;
C.OptLevel = Config->LTOO;
+ C.CPU = GetCPUStr();
+ C.MAttrs = GetMAttrs();
+
if (Config->SaveTemps)
checkError(C.addSaveTemps(std::string(Config->OutputFile) + ".",
/*UseInputModulePath*/ true));
diff --git a/COFF/MapFile.cpp b/COFF/MapFile.cpp
index 6ca1b6647bd7..fd4894250223 100644
--- a/COFF/MapFile.cpp
+++ b/COFF/MapFile.cpp
@@ -110,7 +110,7 @@ void coff::writeMapFile(ArrayRef<OutputSection *> OutputSections) {
writeHeader(OS, Sec->getRVA(), Sec->getVirtualSize(), /*Align=*/PageSize);
OS << Sec->Name << '\n';
- for (Chunk *C : Sec->getChunks()) {
+ for (Chunk *C : Sec->Chunks) {
auto *SC = dyn_cast<SectionChunk>(C);
if (!SC)
continue;
diff --git a/COFF/MarkLive.cpp b/COFF/MarkLive.cpp
index 57ae450a9138..18b1c9c2529f 100644
--- a/COFF/MarkLive.cpp
+++ b/COFF/MarkLive.cpp
@@ -32,13 +32,13 @@ void markLive(ArrayRef<Chunk *> Chunks) {
// COMDAT section chunks are dead by default. Add non-COMDAT chunks.
for (Chunk *C : Chunks)
if (auto *SC = dyn_cast<SectionChunk>(C))
- if (SC->isLive())
+ if (SC->Live)
Worklist.push_back(SC);
auto Enqueue = [&](SectionChunk *C) {
- if (C->isLive())
+ if (C->Live)
return;
- C->markLive();
+ C->Live = true;
Worklist.push_back(C);
};
@@ -57,7 +57,7 @@ void markLive(ArrayRef<Chunk *> Chunks) {
while (!Worklist.empty()) {
SectionChunk *SC = Worklist.pop_back_val();
- assert(SC->isLive() && "We mark as live when pushing onto the worklist!");
+ assert(SC->Live && "We mark as live when pushing onto the worklist!");
// Mark all symbols listed in the relocation table for this section.
for (Symbol *B : SC->symbols())
diff --git a/COFF/MinGW.cpp b/COFF/MinGW.cpp
index 2ca00587331f..b2c8c4eadca4 100644
--- a/COFF/MinGW.cpp
+++ b/COFF/MinGW.cpp
@@ -19,7 +19,23 @@ using namespace lld::coff;
using namespace llvm;
using namespace llvm::COFF;
-AutoExporter::AutoExporter() {
+void AutoExporter::initSymbolExcludes() {
+ ExcludeSymbolPrefixes = {
+ // Import symbols
+ "__imp_",
+ "__IMPORT_DESCRIPTOR_",
+ // Extra import symbols from GNU import libraries
+ "__nm_",
+ // C++ symbols
+ "__rtti_",
+ "__builtin_",
+ // Artifical symbols such as .refptr
+ ".",
+ };
+ ExcludeSymbolSuffixes = {
+ "_iname",
+ "_NULL_THUNK_DATA",
+ };
if (Config->Machine == I386) {
ExcludeSymbols = {
"__NULL_IMPORT_DESCRIPTOR",
@@ -36,9 +52,10 @@ AutoExporter::AutoExporter() {
"_DllEntryPoint@12",
"_DllMainCRTStartup@12",
};
+ ExcludeSymbolPrefixes.insert("__head_");
} else {
ExcludeSymbols = {
- "_NULL_IMPORT_DESCRIPTOR",
+ "__NULL_IMPORT_DESCRIPTOR",
"_pei386_runtime_relocator",
"do_pseudo_reloc",
"impure_ptr",
@@ -52,8 +69,11 @@ AutoExporter::AutoExporter() {
"DllEntryPoint",
"DllMainCRTStartup",
};
+ ExcludeSymbolPrefixes.insert("_head_");
}
+}
+AutoExporter::AutoExporter() {
ExcludeLibs = {
"libgcc",
"libgcc_s",
@@ -64,6 +84,7 @@ AutoExporter::AutoExporter() {
"libsupc++",
"libobjc",
"libgcj",
+ "libclang_rt.builtins",
"libclang_rt.builtins-aarch64",
"libclang_rt.builtins-arm",
"libclang_rt.builtins-i386",
@@ -90,6 +111,13 @@ AutoExporter::AutoExporter() {
};
}
+void AutoExporter::addWholeArchive(StringRef Path) {
+ StringRef LibName = sys::path::filename(Path);
+ // Drop the file extension, to match the processing below.
+ LibName = LibName.substr(0, LibName.rfind('.'));
+ ExcludeLibs.erase(LibName);
+}
+
bool AutoExporter::shouldExport(Defined *Sym) const {
if (!Sym || !Sym->isLive() || !Sym->getChunk())
return false;
@@ -101,10 +129,12 @@ bool AutoExporter::shouldExport(Defined *Sym) const {
if (ExcludeSymbols.count(Sym->getName()))
return false;
- // Don't export anything that looks like an import symbol (which also can be
- // a manually defined data symbol with such a name).
- if (Sym->getName().startswith("__imp_"))
- return false;
+ for (StringRef Prefix : ExcludeSymbolPrefixes.keys())
+ if (Sym->getName().startswith(Prefix))
+ return false;
+ for (StringRef Suffix : ExcludeSymbolSuffixes.keys())
+ if (Sym->getName().endswith(Suffix))
+ return false;
// If a corresponding __imp_ symbol exists and is defined, don't export it.
if (Symtab->find(("__imp_" + Sym->getName()).str()))
diff --git a/COFF/MinGW.h b/COFF/MinGW.h
index fe6cc5588ebc..f9c5e3e5c2cc 100644
--- a/COFF/MinGW.h
+++ b/COFF/MinGW.h
@@ -23,7 +23,13 @@ class AutoExporter {
public:
AutoExporter();
+ void initSymbolExcludes();
+
+ void addWholeArchive(StringRef Path);
+
llvm::StringSet<> ExcludeSymbols;
+ llvm::StringSet<> ExcludeSymbolPrefixes;
+ llvm::StringSet<> ExcludeSymbolSuffixes;
llvm::StringSet<> ExcludeLibs;
llvm::StringSet<> ExcludeObjects;
diff --git a/COFF/Options.td b/COFF/Options.td
index 871bad8bd655..acf1bc5c8b1d 100644
--- a/COFF/Options.td
+++ b/COFF/Options.td
@@ -66,13 +66,18 @@ def wholearchive_file : P<"wholearchive", "Include all object files from this ar
def disallowlib : Joined<["/", "-", "-?"], "disallowlib:">, Alias<nodefaultlib>;
-def manifest : F<"manifest">;
-def manifest_colon : P<"manifest", "Create manifest file">;
+def manifest : F<"manifest">, HelpText<"Create .manifest file">;
+def manifest_colon : P<
+ "manifest",
+ "NO disables manifest output; EMBED[,ID=#] embeds manifest as resource in the image">;
def manifestuac : P<"manifestuac", "User access control">;
-def manifestfile : P<"manifestfile", "Manifest file path">;
-def manifestdependency : P<"manifestdependency",
- "Attributes for <dependency> in manifest file">;
-def manifestinput : P<"manifestinput", "Specify manifest file">;
+def manifestfile : P<"manifestfile", "Manifest output path, with /manifest">;
+def manifestdependency : P<
+ "manifestdependency",
+ "Attributes for <dependency> element in manifest file; implies /manifest">;
+def manifestinput : P<
+ "manifestinput",
+ "Additional manifest inputs; only valid with /manifest:embed">;
// We cannot use multiclass P because class name "incl" is different
// from its command line option name. We do this because "include" is
@@ -85,22 +90,28 @@ def deffile : Joined<["/", "-"], "def:">,
HelpText<"Use module-definition file">;
def debug : F<"debug">, HelpText<"Embed a symbol table in the image">;
-def debug_full : F<"debug:full">, Alias<debug>;
+def debug_opt : P<"debug", "Embed a symbol table in the image with option">;
def debugtype : P<"debugtype", "Debug Info Options">;
def dll : F<"dll">, HelpText<"Create a DLL">;
def driver : P<"driver", "Generate a Windows NT Kernel Mode Driver">;
-def nodefaultlib_all : F<"nodefaultlib">;
-def noentry : F<"noentry">;
+def nodefaultlib_all : F<"nodefaultlib">,
+ HelpText<"Remove all default libraries">;
+def noentry : F<"noentry">,
+ HelpText<"Don't add reference to DllMainCRTStartup; only valid with /dll">;
def profile : F<"profile">;
-def repro : F<"Brepro">, HelpText<"Use a hash of the executable as the PE header timestamp">;
+def repro : F<"Brepro">,
+ HelpText<"Use a hash of the executable as the PE header timestamp">;
def swaprun_cd : F<"swaprun:cd">;
def swaprun_net : F<"swaprun:net">;
def verbose : F<"verbose">;
def wholearchive_flag : F<"wholearchive">;
def force : F<"force">,
+ HelpText<"Allow undefined and multiply defined symbols when creating executables">;
+def force_unresolved : F<"force:unresolved">,
HelpText<"Allow undefined symbols when creating executables">;
-def force_unresolved : F<"force:unresolved">;
+def force_multiple : F<"force:multiple">,
+ HelpText<"Allow multiply defined symbols when creating executables">;
defm WX : B<"WX", "Treat warnings as errors", "Don't treat warnings as errors">;
defm allowbind : B<"allowbind", "Enable DLL binding (default)",
@@ -139,13 +150,9 @@ def help : F<"help">;
def help_q : Flag<["/?", "-?"], "">, Alias<help>;
// LLD extensions
-def debug_ghash : F<"debug:ghash">;
-def debug_dwarf : F<"debug:dwarf">;
-def debug_symtab : F<"debug:symtab">;
def export_all_symbols : F<"export-all-symbols">;
def kill_at : F<"kill-at">;
def lldmingw : F<"lldmingw">;
-def msvclto : F<"msvclto">;
def output_def : Joined<["/", "-"], "output-def:">;
def pdb_source_path : P<"pdbsourcepath",
"Base path used to make relative source file path absolute in PDB">;
diff --git a/COFF/PDB.cpp b/COFF/PDB.cpp
index 766bf3f6b456..7862b6ce4cc5 100644
--- a/COFF/PDB.cpp
+++ b/COFF/PDB.cpp
@@ -16,12 +16,14 @@
#include "Writer.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Timer.h"
+#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h"
#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h"
#include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h"
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
#include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h"
#include "llvm/DebugInfo/CodeView/RecordName.h"
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
+#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h"
#include "llvm/DebugInfo/CodeView/SymbolSerializer.h"
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h"
@@ -48,8 +50,10 @@
#include "llvm/Object/CVDebugRecord.h"
#include "llvm/Support/BinaryByteStream.h"
#include "llvm/Support/Endian.h"
+#include "llvm/Support/Errc.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/JamCRC.h"
+#include "llvm/Support/Parallel.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/ScopedPrinter.h"
#include <memory>
@@ -79,9 +83,14 @@ struct CVIndexMap {
SmallVector<TypeIndex, 0> TPIMap;
SmallVector<TypeIndex, 0> IPIMap;
bool IsTypeServerMap = false;
+ bool IsPrecompiledTypeMap = false;
};
+class DebugSHandler;
+
class PDBLinker {
+ friend DebugSHandler;
+
public:
PDBLinker(SymbolTable *Symtab)
: Alloc(), Symtab(Symtab), Builder(Alloc), TypeTable(Alloc),
@@ -93,7 +102,7 @@ public:
}
/// Emit the basic PDB structure: initial streams, headers, etc.
- void initialize(const llvm::codeview::DebugInfo &BuildId);
+ void initialize(llvm::codeview::DebugInfo *BuildId);
/// Add natvis files specified on the command line.
void addNatvisFiles();
@@ -101,8 +110,10 @@ public:
/// Link CodeView from each object file in the symbol table into the PDB.
void addObjectsToPDB();
- /// Link CodeView from a single object file into the PDB.
- void addObjFile(ObjFile *File);
+ /// Link CodeView from a single object file into the target (output) PDB.
+ /// When a precompiled headers object is linked, its TPI map might be provided
+ /// externally.
+ void addObjFile(ObjFile *File, CVIndexMap *ExternIndexMap = nullptr);
/// Produce a mapping from the type and item indices used in the object
/// file to those in the destination PDB.
@@ -115,18 +126,60 @@ public:
/// If the object does not use a type server PDB (compiled with /Z7), we merge
/// all the type and item records from the .debug$S stream and fill in the
/// caller-provided ObjectIndexMap.
- Expected<const CVIndexMap&> mergeDebugT(ObjFile *File,
- CVIndexMap &ObjectIndexMap);
+ Expected<const CVIndexMap &> mergeDebugT(ObjFile *File,
+ CVIndexMap *ObjectIndexMap);
+
+ /// Reads and makes available a PDB.
+ Expected<const CVIndexMap &> maybeMergeTypeServerPDB(ObjFile *File,
+ const CVType &FirstType);
+
+ /// Merges a precompiled headers TPI map into the current TPI map. The
+ /// precompiled headers object will also be loaded and remapped in the
+ /// process.
+ Expected<const CVIndexMap &>
+ mergeInPrecompHeaderObj(ObjFile *File, const CVType &FirstType,
+ CVIndexMap *ObjectIndexMap);
+
+ /// Reads and makes available a precompiled headers object.
+ ///
+ /// This is a requirement for objects compiled with cl.exe /Yu. In that
+ /// case, the referenced object (which was compiled with /Yc) has to be loaded
+ /// first. This is mainly because the current object's TPI stream has external
+ /// references to the precompiled headers object.
+ ///
+ /// If the precompiled headers object was already loaded, this function will
+ /// simply return its (remapped) TPI map.
+ Expected<const CVIndexMap &> aquirePrecompObj(ObjFile *File,
+ PrecompRecord Precomp);
+
+ /// Adds a precompiled headers object signature -> TPI mapping.
+ std::pair<CVIndexMap &, bool /*already there*/>
+ registerPrecompiledHeaders(uint32_t Signature);
- Expected<const CVIndexMap&> maybeMergeTypeServerPDB(ObjFile *File,
- TypeServer2Record &TS);
+ void mergeSymbolRecords(ObjFile *File, const CVIndexMap &IndexMap,
+ std::vector<ulittle32_t *> &StringTableRefs,
+ BinaryStreamRef SymData);
/// Add the section map and section contributions to the PDB.
void addSections(ArrayRef<OutputSection *> OutputSections,
ArrayRef<uint8_t> SectionTable);
- /// Write the PDB to disk.
- void commit();
+ /// Get the type table or the global type table if /DEBUG:GHASH is enabled.
+ TypeCollection &getTypeTable() {
+ if (Config->DebugGHashes)
+ return GlobalTypeTable;
+ return TypeTable;
+ }
+
+ /// Get the ID table or the global ID table if /DEBUG:GHASH is enabled.
+ TypeCollection &getIDTable() {
+ if (Config->DebugGHashes)
+ return GlobalIDTable;
+ return IDTable;
+ }
+
+ /// Write the PDB to disk and store the Guid generated for it in *Guid.
+ void commit(codeview::GUID *Guid);
private:
BumpPtrAllocator Alloc;
@@ -161,14 +214,95 @@ private:
std::vector<pdb::SecMapEntry> SectionMap;
/// Type index mappings of type server PDBs that we've loaded so far.
- std::map<GUID, CVIndexMap> TypeServerIndexMappings;
+ std::map<codeview::GUID, CVIndexMap> TypeServerIndexMappings;
+
+ /// Type index mappings of precompiled objects type map that we've loaded so
+ /// far.
+ std::map<uint32_t, CVIndexMap> PrecompTypeIndexMappings;
/// List of TypeServer PDBs which cannot be loaded.
/// Cached to prevent repeated load attempts.
- std::set<GUID> MissingTypeServerPDBs;
+ std::map<codeview::GUID, std::string> MissingTypeServerPDBs;
+};
+
+class DebugSHandler {
+ PDBLinker &Linker;
+
+ /// The object file whose .debug$S sections we're processing.
+ ObjFile &File;
+
+ /// The result of merging type indices.
+ const CVIndexMap &IndexMap;
+
+ /// The DEBUG_S_STRINGTABLE subsection. These strings are referred to by
+ /// index from other records in the .debug$S section. All of these strings
+ /// need to be added to the global PDB string table, and all references to
+ /// these strings need to have their indices re-written to refer to the
+ /// global PDB string table.
+ DebugStringTableSubsectionRef CVStrTab;
+
+ /// The DEBUG_S_FILECHKSMS subsection. As above, these are referred to
+ /// by other records in the .debug$S section and need to be merged into the
+ /// PDB.
+ DebugChecksumsSubsectionRef Checksums;
+
+ /// The DEBUG_S_FRAMEDATA subsection(s). There can be more than one of
+ /// these and they need not appear in any specific order. However, they
+ /// contain string table references which need to be re-written, so we
+ /// collect them all here and re-write them after all subsections have been
+ /// discovered and processed.
+ std::vector<DebugFrameDataSubsectionRef> NewFpoFrames;
+
+ /// Pointers to raw memory that we determine have string table references
+ /// that need to be re-written. We first process all .debug$S subsections
+ /// to ensure that we can handle subsections written in any order, building
+ /// up this list as we go. At the end, we use the string table (which must
+ /// have been discovered by now else it is an error) to re-write these
+ /// references.
+ std::vector<ulittle32_t *> StringTableReferences;
+
+public:
+ DebugSHandler(PDBLinker &Linker, ObjFile &File, const CVIndexMap &IndexMap)
+ : Linker(Linker), File(File), IndexMap(IndexMap) {}
+
+ void handleDebugS(lld::coff::SectionChunk &DebugS);
+ void finish();
};
}
+// Visual Studio's debugger requires absolute paths in various places in the
+// PDB to work without additional configuration:
+// https://docs.microsoft.com/en-us/visualstudio/debugger/debug-source-files-common-properties-solution-property-pages-dialog-box
+static void pdbMakeAbsolute(SmallVectorImpl<char> &FileName) {
+ // The default behavior is to produce paths that are valid within the context
+ // of the machine that you perform the link on. If the linker is running on
+ // a POSIX system, we will output absolute POSIX paths. If the linker is
+ // running on a Windows system, we will output absolute Windows paths. If the
+ // user desires any other kind of behavior, they should explicitly pass
+ // /pdbsourcepath, in which case we will treat the exact string the user
+ // passed in as the gospel and not normalize, canonicalize it.
+ if (sys::path::is_absolute(FileName, sys::path::Style::windows) ||
+ sys::path::is_absolute(FileName, sys::path::Style::posix))
+ return;
+
+ // It's not absolute in any path syntax. Relative paths necessarily refer to
+ // the local file system, so we can make it native without ending up with a
+ // nonsensical path.
+ sys::path::native(FileName);
+ if (Config->PDBSourcePath.empty()) {
+ sys::fs::make_absolute(FileName);
+ return;
+ }
+ // Only apply native and dot removal to the relative file path. We want to
+ // leave the path the user specified untouched since we assume they specified
+ // it for a reason.
+ sys::path::remove_dots(FileName, /*remove_dot_dots=*/true);
+
+ SmallString<128> AbsoluteFileName = Config->PDBSourcePath;
+ sys::path::append(AbsoluteFileName, FileName);
+ FileName = std::move(AbsoluteFileName);
+}
+
static SectionChunk *findByName(ArrayRef<SectionChunk *> Sections,
StringRef Name) {
for (SectionChunk *C : Sections)
@@ -242,27 +376,79 @@ static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder,
});
}
-static Optional<TypeServer2Record>
-maybeReadTypeServerRecord(CVTypeArray &Types) {
- auto I = Types.begin();
- if (I == Types.end())
- return None;
- const CVType &Type = *I;
- if (Type.kind() != LF_TYPESERVER2)
- return None;
- TypeServer2Record TS;
- if (auto EC = TypeDeserializer::deserializeAs(const_cast<CVType &>(Type), TS))
- fatal("error reading type server record: " + toString(std::move(EC)));
- return std::move(TS);
+// OBJs usually start their symbol stream with a S_OBJNAME record. This record
+// also contains the signature/key of the current PCH session. The signature
+// must be same for all objects which depend on the precompiled object.
+// Recompiling the precompiled headers will generate a new PCH key and thus
+// invalidate all the dependent objects.
+static uint32_t extractPCHSignature(ObjFile *File) {
+ auto DbgIt = find_if(File->getDebugChunks(), [](SectionChunk *C) {
+ return C->getSectionName() == ".debug$S";
+ });
+ if (!DbgIt)
+ return 0;
+
+ ArrayRef<uint8_t> Contents =
+ consumeDebugMagic((*DbgIt)->getContents(), ".debug$S");
+ DebugSubsectionArray Subsections;
+ BinaryStreamReader Reader(Contents, support::little);
+ ExitOnErr(Reader.readArray(Subsections, Contents.size()));
+
+ for (const DebugSubsectionRecord &SS : Subsections) {
+ if (SS.kind() != DebugSubsectionKind::Symbols)
+ continue;
+
+ // If it's there, the S_OBJNAME record shall come first in the stream.
+ Expected<CVSymbol> Sym = readSymbolFromStream(SS.getRecordData(), 0);
+ if (!Sym) {
+ consumeError(Sym.takeError());
+ continue;
+ }
+ if (auto ObjName = SymbolDeserializer::deserializeAs<ObjNameSym>(Sym.get()))
+ return ObjName->Signature;
+ }
+ return 0;
}
-Expected<const CVIndexMap&> PDBLinker::mergeDebugT(ObjFile *File,
- CVIndexMap &ObjectIndexMap) {
+Expected<const CVIndexMap &>
+PDBLinker::mergeDebugT(ObjFile *File, CVIndexMap *ObjectIndexMap) {
ScopedTimer T(TypeMergingTimer);
+ bool IsPrecompiledHeader = false;
+
ArrayRef<uint8_t> Data = getDebugSection(File, ".debug$T");
+ if (Data.empty()) {
+ // Try again, Microsoft precompiled headers use .debug$P instead of
+ // .debug$T
+ Data = getDebugSection(File, ".debug$P");
+ IsPrecompiledHeader = true;
+ }
if (Data.empty())
- return ObjectIndexMap;
+ return *ObjectIndexMap; // no debug info
+
+ // Precompiled headers objects need to save the index map for further
+ // reference by other objects which use the precompiled headers.
+ if (IsPrecompiledHeader) {
+ uint32_t PCHSignature = extractPCHSignature(File);
+ if (PCHSignature == 0)
+ fatal("No signature found for the precompiled headers OBJ (" +
+ File->getName() + ")");
+
+ // When a precompiled headers object comes first on the command-line, we
+ // update the mapping here. Otherwise, if an object referencing the
+ // precompiled headers object comes first, the mapping is created in
+ // aquirePrecompObj(), thus we would skip this block.
+ if (!ObjectIndexMap->IsPrecompiledTypeMap) {
+ auto R = registerPrecompiledHeaders(PCHSignature);
+ if (R.second)
+ fatal(
+ "A precompiled headers OBJ with the same signature was already "
+ "provided! (" +
+ File->getName() + ")");
+
+ ObjectIndexMap = &R.first;
+ }
+ }
BinaryByteStream Stream(Data, support::little);
CVTypeArray Types;
@@ -270,13 +456,32 @@ Expected<const CVIndexMap&> PDBLinker::mergeDebugT(ObjFile *File,
if (auto EC = Reader.readArray(Types, Reader.getLength()))
fatal("Reader::readArray failed: " + toString(std::move(EC)));
- // Look through type servers. If we've already seen this type server, don't
- // merge any type information.
- if (Optional<TypeServer2Record> TS = maybeReadTypeServerRecord(Types))
- return maybeMergeTypeServerPDB(File, *TS);
+ auto FirstType = Types.begin();
+ if (FirstType == Types.end())
+ return *ObjectIndexMap;
+
+ if (FirstType->kind() == LF_TYPESERVER2) {
+ // Look through type servers. If we've already seen this type server,
+ // don't merge any type information.
+ return maybeMergeTypeServerPDB(File, *FirstType);
+ } else if (FirstType->kind() == LF_PRECOMP) {
+ // This object was compiled with /Yu, so process the corresponding
+ // precompiled headers object (/Yc) first. Some type indices in the current
+ // object are referencing data in the precompiled headers object, so we need
+ // both to be loaded.
+ auto E = mergeInPrecompHeaderObj(File, *FirstType, ObjectIndexMap);
+ if (!E)
+ return E.takeError();
+
+ // Drop LF_PRECOMP record from the input stream, as it needs to be replaced
+ // with the precompiled headers object type stream.
+ // Note that we can't just call Types.drop_front(), as we explicitly want to
+ // rebase the stream.
+ Types.setUnderlyingStream(
+ Types.getUnderlyingStream().drop_front(FirstType->RecordData.size()));
+ }
- // This is a /Z7 object. Fill in the temporary, caller-provided
- // ObjectIndexMap.
+ // Fill in the temporary, caller-provided ObjectIndexMap.
if (Config->DebugGHashes) {
ArrayRef<GloballyHashedType> Hashes;
std::vector<GloballyHashedType> OwnedHashes;
@@ -288,20 +493,28 @@ Expected<const CVIndexMap&> PDBLinker::mergeDebugT(ObjFile *File,
}
if (auto Err = mergeTypeAndIdRecords(GlobalIDTable, GlobalTypeTable,
- ObjectIndexMap.TPIMap, Types, Hashes))
+ ObjectIndexMap->TPIMap, Types, Hashes,
+ File->PCHSignature))
fatal("codeview::mergeTypeAndIdRecords failed: " +
toString(std::move(Err)));
} else {
- if (auto Err = mergeTypeAndIdRecords(IDTable, TypeTable,
- ObjectIndexMap.TPIMap, Types))
+ if (auto Err =
+ mergeTypeAndIdRecords(IDTable, TypeTable, ObjectIndexMap->TPIMap,
+ Types, File->PCHSignature))
fatal("codeview::mergeTypeAndIdRecords failed: " +
toString(std::move(Err)));
}
- return ObjectIndexMap;
+ return *ObjectIndexMap;
}
static Expected<std::unique_ptr<pdb::NativeSession>>
-tryToLoadPDB(const GUID &GuidFromObj, StringRef TSPath) {
+tryToLoadPDB(const codeview::GUID &GuidFromObj, StringRef TSPath) {
+ // Ensure the file exists before anything else. We want to return ENOENT,
+ // "file not found", even if the path points to a removable device (in which
+ // case the return message would be EAGAIN, "resource unavailable try again")
+ if (!llvm::sys::fs::exists(TSPath))
+ return errorCodeToError(std::error_code(ENOENT, std::generic_category()));
+
ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr = MemoryBuffer::getFile(
TSPath, /*FileSize=*/-1, /*RequiresNullTerminator=*/false);
if (!MBOrErr)
@@ -326,21 +539,27 @@ tryToLoadPDB(const GUID &GuidFromObj, StringRef TSPath) {
// PDB file doesn't mean it matches. For it to match the InfoStream's GUID
// must match the GUID specified in the TypeServer2 record.
if (ExpectedInfo->getGuid() != GuidFromObj)
- return make_error<pdb::GenericError>(
- pdb::generic_error_code::type_server_not_found, TSPath);
+ return make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date);
return std::move(NS);
}
-Expected<const CVIndexMap&> PDBLinker::maybeMergeTypeServerPDB(ObjFile *File,
- TypeServer2Record &TS) {
- const GUID& TSId = TS.getGuid();
+Expected<const CVIndexMap &>
+PDBLinker::maybeMergeTypeServerPDB(ObjFile *File, const CVType &FirstType) {
+ TypeServer2Record TS;
+ if (auto EC =
+ TypeDeserializer::deserializeAs(const_cast<CVType &>(FirstType), TS))
+ fatal("error reading record: " + toString(std::move(EC)));
+
+ const codeview::GUID &TSId = TS.getGuid();
StringRef TSPath = TS.getName();
// First, check if the PDB has previously failed to load.
- if (MissingTypeServerPDBs.count(TSId))
- return make_error<pdb::GenericError>(
- pdb::generic_error_code::type_server_not_found, TSPath);
+ auto PrevErr = MissingTypeServerPDBs.find(TSId);
+ if (PrevErr != MissingTypeServerPDBs.end())
+ return createFileError(
+ TSPath,
+ make_error<StringError>(PrevErr->second, inconvertibleErrorCode()));
// Second, check if we already loaded a PDB with this GUID. Return the type
// index mapping if we have it.
@@ -355,20 +574,39 @@ Expected<const CVIndexMap&> PDBLinker::maybeMergeTypeServerPDB(ObjFile *File,
// Check for a PDB at:
// 1. The given file path
// 2. Next to the object file or archive file
- auto ExpectedSession = tryToLoadPDB(TSId, TSPath);
- if (!ExpectedSession) {
- consumeError(ExpectedSession.takeError());
- StringRef LocalPath =
- !File->ParentName.empty() ? File->ParentName : File->getName();
- SmallString<128> Path = sys::path::parent_path(LocalPath);
- sys::path::append(
- Path, sys::path::filename(TSPath, sys::path::Style::windows));
- ExpectedSession = tryToLoadPDB(TSId, Path);
- }
+ auto ExpectedSession = handleExpected(
+ tryToLoadPDB(TSId, TSPath),
+ [&]() {
+ StringRef LocalPath =
+ !File->ParentName.empty() ? File->ParentName : File->getName();
+ SmallString<128> Path = sys::path::parent_path(LocalPath);
+ // Currently, type server PDBs are only created by cl, which only runs
+ // on Windows, so we can assume type server paths are Windows style.
+ sys::path::append(
+ Path, sys::path::filename(TSPath, sys::path::Style::windows));
+ return tryToLoadPDB(TSId, Path);
+ },
+ [&](std::unique_ptr<ECError> EC) -> Error {
+ auto SysErr = EC->convertToErrorCode();
+ // Only re-try loading if the previous error was "No such file or
+ // directory"
+ if (SysErr.category() == std::generic_category() &&
+ SysErr.value() == ENOENT)
+ return Error::success();
+ return Error(std::move(EC));
+ });
+
if (auto E = ExpectedSession.takeError()) {
TypeServerIndexMappings.erase(TSId);
- MissingTypeServerPDBs.emplace(TSId);
- return std::move(E);
+
+ // Flatten the error to a string, for later display, if the error occurs
+ // again on the same PDB.
+ std::string ErrMsg;
+ raw_string_ostream S(ErrMsg);
+ S << E;
+ MissingTypeServerPDBs.emplace(TSId, S.str());
+
+ return createFileError(TSPath, std::move(E));
}
pdb::NativeSession *Session = ExpectedSession->get();
@@ -394,9 +632,10 @@ Expected<const CVIndexMap&> PDBLinker::maybeMergeTypeServerPDB(ObjFile *File,
auto IpiHashes =
GloballyHashedType::hashIds(ExpectedIpi->typeArray(), TpiHashes);
+ Optional<uint32_t> EndPrecomp;
// Merge TPI first, because the IPI stream will reference type indices.
if (auto Err = mergeTypeRecords(GlobalTypeTable, IndexMap.TPIMap,
- ExpectedTpi->typeArray(), TpiHashes))
+ ExpectedTpi->typeArray(), TpiHashes, EndPrecomp))
fatal("codeview::mergeTypeRecords failed: " + toString(std::move(Err)));
// Merge IPI.
@@ -419,6 +658,103 @@ Expected<const CVIndexMap&> PDBLinker::maybeMergeTypeServerPDB(ObjFile *File,
return IndexMap;
}
+Expected<const CVIndexMap &>
+PDBLinker::mergeInPrecompHeaderObj(ObjFile *File, const CVType &FirstType,
+ CVIndexMap *ObjectIndexMap) {
+ PrecompRecord Precomp;
+ if (auto EC = TypeDeserializer::deserializeAs(const_cast<CVType &>(FirstType),
+ Precomp))
+ fatal("error reading record: " + toString(std::move(EC)));
+
+ auto E = aquirePrecompObj(File, Precomp);
+ if (!E)
+ return E.takeError();
+
+ const CVIndexMap &PrecompIndexMap = *E;
+ assert(PrecompIndexMap.IsPrecompiledTypeMap);
+
+ if (PrecompIndexMap.TPIMap.empty())
+ return PrecompIndexMap;
+
+ assert(Precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex);
+ assert(Precomp.getTypesCount() <= PrecompIndexMap.TPIMap.size());
+ // Use the previously remapped index map from the precompiled headers.
+ ObjectIndexMap->TPIMap.append(PrecompIndexMap.TPIMap.begin(),
+ PrecompIndexMap.TPIMap.begin() +
+ Precomp.getTypesCount());
+ return *ObjectIndexMap;
+}
+
+static bool equals_path(StringRef path1, StringRef path2) {
+#if defined(_WIN32)
+ return path1.equals_lower(path2);
+#else
+ return path1.equals(path2);
+#endif
+}
+
+// Find by name an OBJ provided on the command line
+static ObjFile *findObjByName(StringRef FileNameOnly) {
+ SmallString<128> CurrentPath;
+
+ for (ObjFile *F : ObjFile::Instances) {
+ StringRef CurrentFileName = sys::path::filename(F->getName());
+
+ // Compare based solely on the file name (link.exe behavior)
+ if (equals_path(CurrentFileName, FileNameOnly))
+ return F;
+ }
+ return nullptr;
+}
+
+std::pair<CVIndexMap &, bool /*already there*/>
+PDBLinker::registerPrecompiledHeaders(uint32_t Signature) {
+ auto Insertion = PrecompTypeIndexMappings.insert({Signature, CVIndexMap()});
+ CVIndexMap &IndexMap = Insertion.first->second;
+ if (!Insertion.second)
+ return {IndexMap, true};
+ // Mark this map as a precompiled types map.
+ IndexMap.IsPrecompiledTypeMap = true;
+ return {IndexMap, false};
+}
+
+Expected<const CVIndexMap &>
+PDBLinker::aquirePrecompObj(ObjFile *File, PrecompRecord Precomp) {
+ // First, check if we already loaded the precompiled headers object with this
+ // signature. Return the type index mapping if we've already seen it.
+ auto R = registerPrecompiledHeaders(Precomp.getSignature());
+ if (R.second)
+ return R.first;
+
+ CVIndexMap &IndexMap = R.first;
+
+ // Cross-compile warning: given that Clang doesn't generate LF_PRECOMP
+ // records, we assume the OBJ comes from a Windows build of cl.exe. Thusly,
+ // the paths embedded in the OBJs are in the Windows format.
+ SmallString<128> PrecompFileName = sys::path::filename(
+ Precomp.getPrecompFilePath(), sys::path::Style::windows);
+
+ // link.exe requires that a precompiled headers object must always be provided
+ // on the command-line, even if that's not necessary.
+ auto PrecompFile = findObjByName(PrecompFileName);
+ if (!PrecompFile)
+ return createFileError(
+ PrecompFileName.str(),
+ make_error<pdb::PDBError>(pdb::pdb_error_code::external_cmdline_ref));
+
+ addObjFile(PrecompFile, &IndexMap);
+
+ if (!PrecompFile->PCHSignature)
+ fatal(PrecompFile->getName() + " is not a precompiled headers object");
+
+ if (Precomp.getSignature() != PrecompFile->PCHSignature.getValueOr(0))
+ return createFileError(
+ Precomp.getPrecompFilePath().str(),
+ make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date));
+
+ return IndexMap;
+}
+
static bool remapTypeIndex(TypeIndex &TI, ArrayRef<TypeIndex> TypeIndexMap) {
if (TI.isSimple())
return true;
@@ -429,9 +765,11 @@ static bool remapTypeIndex(TypeIndex &TI, ArrayRef<TypeIndex> TypeIndexMap) {
}
static void remapTypesInSymbolRecord(ObjFile *File, SymbolKind SymKind,
- MutableArrayRef<uint8_t> Contents,
+ MutableArrayRef<uint8_t> RecordBytes,
const CVIndexMap &IndexMap,
ArrayRef<TiReference> TypeRefs) {
+ MutableArrayRef<uint8_t> Contents =
+ RecordBytes.drop_front(sizeof(RecordPrefix));
for (const TiReference &Ref : TypeRefs) {
unsigned ByteSize = Ref.Count * sizeof(TypeIndex);
if (Contents.size() < Ref.Offset + ByteSize)
@@ -477,7 +815,7 @@ recordStringTableReferences(SymbolKind Kind, MutableArrayRef<uint8_t> Contents,
switch (Kind) {
case SymbolKind::S_FILESTATIC:
// FileStaticSym::ModFileOffset
- recordStringTableReferenceAtOffset(Contents, 4, StrTableRefs);
+ recordStringTableReferenceAtOffset(Contents, 8, StrTableRefs);
break;
case SymbolKind::S_DEFRANGE:
case SymbolKind::S_DEFRANGE_SUBFIELD:
@@ -542,58 +880,26 @@ static void translateIdSymbols(MutableArrayRef<uint8_t> &RecordData,
/// Copy the symbol record. In a PDB, symbol records must be 4 byte aligned.
/// The object file may not be aligned.
-static MutableArrayRef<uint8_t> copySymbolForPdb(const CVSymbol &Sym,
- BumpPtrAllocator &Alloc) {
+static MutableArrayRef<uint8_t>
+copyAndAlignSymbol(const CVSymbol &Sym, MutableArrayRef<uint8_t> &AlignedMem) {
size_t Size = alignTo(Sym.length(), alignOf(CodeViewContainer::Pdb));
assert(Size >= 4 && "record too short");
assert(Size <= MaxRecordLength && "record too long");
- void *Mem = Alloc.Allocate(Size, 4);
+ assert(AlignedMem.size() >= Size && "didn't preallocate enough");
// Copy the symbol record and zero out any padding bytes.
- MutableArrayRef<uint8_t> NewData(reinterpret_cast<uint8_t *>(Mem), Size);
+ MutableArrayRef<uint8_t> NewData = AlignedMem.take_front(Size);
+ AlignedMem = AlignedMem.drop_front(Size);
memcpy(NewData.data(), Sym.data().data(), Sym.length());
memset(NewData.data() + Sym.length(), 0, Size - Sym.length());
// Update the record prefix length. It should point to the beginning of the
// next record.
- auto *Prefix = reinterpret_cast<RecordPrefix *>(Mem);
+ auto *Prefix = reinterpret_cast<RecordPrefix *>(NewData.data());
Prefix->RecordLen = Size - 2;
return NewData;
}
-/// Return true if this symbol opens a scope. This implies that the symbol has
-/// "parent" and "end" fields, which contain the offset of the S_END or
-/// S_INLINESITE_END record.
-static bool symbolOpensScope(SymbolKind Kind) {
- switch (Kind) {
- case SymbolKind::S_GPROC32:
- case SymbolKind::S_LPROC32:
- case SymbolKind::S_LPROC32_ID:
- case SymbolKind::S_GPROC32_ID:
- case SymbolKind::S_BLOCK32:
- case SymbolKind::S_SEPCODE:
- case SymbolKind::S_THUNK32:
- case SymbolKind::S_INLINESITE:
- case SymbolKind::S_INLINESITE2:
- return true;
- default:
- break;
- }
- return false;
-}
-
-static bool symbolEndsScope(SymbolKind Kind) {
- switch (Kind) {
- case SymbolKind::S_END:
- case SymbolKind::S_PROC_ID_END:
- case SymbolKind::S_INLINESITE_END:
- return true;
- default:
- break;
- }
- return false;
-}
-
struct ScopeRecord {
ulittle32_t PtrParent;
ulittle32_t PtrEnd;
@@ -625,11 +931,10 @@ static void scopeStackClose(SmallVectorImpl<SymbolScope> &Stack,
S.OpeningRecord->PtrEnd = CurOffset;
}
-static bool symbolGoesInModuleStream(const CVSymbol &Sym) {
+static bool symbolGoesInModuleStream(const CVSymbol &Sym, bool IsGlobalScope) {
switch (Sym.kind()) {
case SymbolKind::S_GDATA32:
case SymbolKind::S_CONSTANT:
- case SymbolKind::S_UDT:
// We really should not be seeing S_PROCREF and S_LPROCREF in the first place
// since they are synthesized by the linker in response to S_GPROC32 and
// S_LPROC32, but if we do see them, don't put them in the module stream I
@@ -637,6 +942,9 @@ static bool symbolGoesInModuleStream(const CVSymbol &Sym) {
case SymbolKind::S_PROCREF:
case SymbolKind::S_LPROCREF:
return false;
+ // S_UDT records go in the module stream if it is not a global S_UDT.
+ case SymbolKind::S_UDT:
+ return !IsGlobalScope;
// S_GDATA32 does not go in the module stream, but S_LDATA32 does.
case SymbolKind::S_LDATA32:
default:
@@ -644,7 +952,7 @@ static bool symbolGoesInModuleStream(const CVSymbol &Sym) {
}
}
-static bool symbolGoesInGlobalsStream(const CVSymbol &Sym) {
+static bool symbolGoesInGlobalsStream(const CVSymbol &Sym, bool IsGlobalScope) {
switch (Sym.kind()) {
case SymbolKind::S_CONSTANT:
case SymbolKind::S_GDATA32:
@@ -658,20 +966,16 @@ static bool symbolGoesInGlobalsStream(const CVSymbol &Sym) {
case SymbolKind::S_PROCREF:
case SymbolKind::S_LPROCREF:
return true;
- // FIXME: For now, we drop all S_UDT symbols (i.e. they don't go in the
- // globals stream or the modules stream). These have special handling which
- // needs more investigation before we can get right, but by putting them all
- // into the globals stream WinDbg fails to display local variables of class
- // types saying that it cannot find the type Foo *. So as a stopgap just to
- // keep things working, we drop them.
+ // S_UDT records go in the globals stream if it is a global S_UDT.
case SymbolKind::S_UDT:
+ return IsGlobalScope;
default:
return false;
}
}
-static void addGlobalSymbol(pdb::GSIStreamBuilder &Builder, ObjFile &File,
- const CVSymbol &Sym) {
+static void addGlobalSymbol(pdb::GSIStreamBuilder &Builder, uint16_t ModIndex,
+ unsigned SymOffset, const CVSymbol &Sym) {
switch (Sym.kind()) {
case SymbolKind::S_CONSTANT:
case SymbolKind::S_UDT:
@@ -687,12 +991,12 @@ static void addGlobalSymbol(pdb::GSIStreamBuilder &Builder, ObjFile &File,
if (Sym.kind() == SymbolKind::S_LPROC32)
K = SymbolRecordKind::LocalProcRef;
ProcRefSym PS(K);
- PS.Module = static_cast<uint16_t>(File.ModuleDBI->getModuleIndex());
+ PS.Module = ModIndex;
// For some reason, MSVC seems to add one to this value.
++PS.Module;
PS.Name = getSymbolName(Sym);
PS.SumName = 0;
- PS.SymOffset = File.ModuleDBI->getNextSymbolOffset();
+ PS.SymOffset = SymOffset;
Builder.addGlobalSymbol(PS);
break;
}
@@ -701,20 +1005,62 @@ static void addGlobalSymbol(pdb::GSIStreamBuilder &Builder, ObjFile &File,
}
}
-static void mergeSymbolRecords(BumpPtrAllocator &Alloc, ObjFile *File,
- pdb::GSIStreamBuilder &GsiBuilder,
- const CVIndexMap &IndexMap,
- TypeCollection &IDTable,
- std::vector<ulittle32_t *> &StringTableRefs,
- BinaryStreamRef SymData) {
- // FIXME: Improve error recovery by warning and skipping records when
- // possible.
+void PDBLinker::mergeSymbolRecords(ObjFile *File, const CVIndexMap &IndexMap,
+ std::vector<ulittle32_t *> &StringTableRefs,
+ BinaryStreamRef SymData) {
ArrayRef<uint8_t> SymsBuffer;
cantFail(SymData.readBytes(0, SymData.getLength(), SymsBuffer));
SmallVector<SymbolScope, 4> Scopes;
+ // Iterate every symbol to check if any need to be realigned, and if so, how
+ // much space we need to allocate for them.
+ bool NeedsRealignment = false;
+ unsigned TotalRealignedSize = 0;
auto EC = forEachCodeViewRecord<CVSymbol>(
- SymsBuffer, [&](const CVSymbol &Sym) -> llvm::Error {
+ SymsBuffer, [&](CVSymbol Sym) -> llvm::Error {
+ unsigned RealignedSize =
+ alignTo(Sym.length(), alignOf(CodeViewContainer::Pdb));
+ NeedsRealignment |= RealignedSize != Sym.length();
+ TotalRealignedSize += RealignedSize;
+ return Error::success();
+ });
+
+ // If any of the symbol record lengths was corrupt, ignore them all, warn
+ // about it, and move on.
+ if (EC) {
+ warn("corrupt symbol records in " + File->getName());
+ consumeError(std::move(EC));
+ return;
+ }
+
+ // If any symbol needed realignment, allocate enough contiguous memory for
+ // them all. Typically symbol subsections are small enough that this will not
+ // cause fragmentation.
+ MutableArrayRef<uint8_t> AlignedSymbolMem;
+ if (NeedsRealignment) {
+ void *AlignedData =
+ Alloc.Allocate(TotalRealignedSize, alignOf(CodeViewContainer::Pdb));
+ AlignedSymbolMem = makeMutableArrayRef(
+ reinterpret_cast<uint8_t *>(AlignedData), TotalRealignedSize);
+ }
+
+ // Iterate again, this time doing the real work.
+ unsigned CurSymOffset = File->ModuleDBI->getNextSymbolOffset();
+ ArrayRef<uint8_t> BulkSymbols;
+ cantFail(forEachCodeViewRecord<CVSymbol>(
+ SymsBuffer, [&](CVSymbol Sym) -> llvm::Error {
+ // Align the record if required.
+ MutableArrayRef<uint8_t> RecordBytes;
+ if (NeedsRealignment) {
+ RecordBytes = copyAndAlignSymbol(Sym, AlignedSymbolMem);
+ Sym = CVSymbol(Sym.kind(), RecordBytes);
+ } else {
+ // Otherwise, we can actually mutate the symbol directly, since we
+ // copied it to apply relocations.
+ RecordBytes = makeMutableArrayRef(
+ const_cast<uint8_t *>(Sym.data().data()), Sym.length());
+ }
+
// Discover type index references in the record. Skip it if we don't
// know where they are.
SmallVector<TiReference, 32> TypeRefs;
@@ -724,57 +1070,62 @@ static void mergeSymbolRecords(BumpPtrAllocator &Alloc, ObjFile *File,
return Error::success();
}
- // Copy the symbol record so we can mutate it.
- MutableArrayRef<uint8_t> NewData = copySymbolForPdb(Sym, Alloc);
-
// Re-map all the type index references.
- MutableArrayRef<uint8_t> Contents =
- NewData.drop_front(sizeof(RecordPrefix));
- remapTypesInSymbolRecord(File, Sym.kind(), Contents, IndexMap,
+ remapTypesInSymbolRecord(File, Sym.kind(), RecordBytes, IndexMap,
TypeRefs);
// An object file may have S_xxx_ID symbols, but these get converted to
// "real" symbols in a PDB.
- translateIdSymbols(NewData, IDTable);
+ translateIdSymbols(RecordBytes, getIDTable());
+ Sym = CVSymbol(symbolKind(RecordBytes), RecordBytes);
// If this record refers to an offset in the object file's string table,
// add that item to the global PDB string table and re-write the index.
- recordStringTableReferences(Sym.kind(), Contents, StringTableRefs);
-
- SymbolKind NewKind = symbolKind(NewData);
+ recordStringTableReferences(Sym.kind(), RecordBytes, StringTableRefs);
// Fill in "Parent" and "End" fields by maintaining a stack of scopes.
- CVSymbol NewSym(NewKind, NewData);
- if (symbolOpensScope(NewKind))
- scopeStackOpen(Scopes, File->ModuleDBI->getNextSymbolOffset(),
- NewSym);
- else if (symbolEndsScope(NewKind))
- scopeStackClose(Scopes, File->ModuleDBI->getNextSymbolOffset(), File);
+ if (symbolOpensScope(Sym.kind()))
+ scopeStackOpen(Scopes, CurSymOffset, Sym);
+ else if (symbolEndsScope(Sym.kind()))
+ scopeStackClose(Scopes, CurSymOffset, File);
// Add the symbol to the globals stream if necessary. Do this before
// adding the symbol to the module since we may need to get the next
// symbol offset, and writing to the module's symbol stream will update
// that offset.
- if (symbolGoesInGlobalsStream(NewSym))
- addGlobalSymbol(GsiBuilder, *File, NewSym);
-
- // Add the symbol to the module.
- if (symbolGoesInModuleStream(NewSym))
- File->ModuleDBI->addSymbol(NewSym);
+ if (symbolGoesInGlobalsStream(Sym, Scopes.empty()))
+ addGlobalSymbol(Builder.getGsiBuilder(),
+ File->ModuleDBI->getModuleIndex(), CurSymOffset, Sym);
+
+ if (symbolGoesInModuleStream(Sym, Scopes.empty())) {
+ // Add symbols to the module in bulk. If this symbol is contiguous
+ // with the previous run of symbols to add, combine the ranges. If
+ // not, close the previous range of symbols and start a new one.
+ if (Sym.data().data() == BulkSymbols.end()) {
+ BulkSymbols = makeArrayRef(BulkSymbols.data(),
+ BulkSymbols.size() + Sym.length());
+ } else {
+ File->ModuleDBI->addSymbolsInBulk(BulkSymbols);
+ BulkSymbols = RecordBytes;
+ }
+ CurSymOffset += Sym.length();
+ }
return Error::success();
- });
- cantFail(std::move(EC));
+ }));
+
+ // Add any remaining symbols we've accumulated.
+ File->ModuleDBI->addSymbolsInBulk(BulkSymbols);
}
-// Allocate memory for a .debug$S section and relocate it.
+// Allocate memory for a .debug$S / .debug$F section and relocate it.
static ArrayRef<uint8_t> relocateDebugChunk(BumpPtrAllocator &Alloc,
- SectionChunk *DebugChunk) {
- uint8_t *Buffer = Alloc.Allocate<uint8_t>(DebugChunk->getSize());
- assert(DebugChunk->OutputSectionOff == 0 &&
+ SectionChunk &DebugChunk) {
+ uint8_t *Buffer = Alloc.Allocate<uint8_t>(DebugChunk.getSize());
+ assert(DebugChunk.OutputSectionOff == 0 &&
"debug sections should not be in output sections");
- DebugChunk->writeTo(Buffer);
- return consumeDebugMagic(makeArrayRef(Buffer, DebugChunk->getSize()),
- ".debug$S");
+ DebugChunk.readRelocTargets();
+ DebugChunk.writeTo(Buffer);
+ return makeArrayRef(Buffer, DebugChunk.getSize());
}
static pdb::SectionContrib createSectionContrib(const Chunk *C, uint32_t Modi) {
@@ -803,25 +1154,137 @@ static pdb::SectionContrib createSectionContrib(const Chunk *C, uint32_t Modi) {
return SC;
}
-void PDBLinker::addObjFile(ObjFile *File) {
+static uint32_t
+translateStringTableIndex(uint32_t ObjIndex,
+ const DebugStringTableSubsectionRef &ObjStrTable,
+ DebugStringTableSubsection &PdbStrTable) {
+ auto ExpectedString = ObjStrTable.getString(ObjIndex);
+ if (!ExpectedString) {
+ warn("Invalid string table reference");
+ consumeError(ExpectedString.takeError());
+ return 0;
+ }
+
+ return PdbStrTable.insert(*ExpectedString);
+}
+
+void DebugSHandler::handleDebugS(lld::coff::SectionChunk &DebugS) {
+ DebugSubsectionArray Subsections;
+
+ ArrayRef<uint8_t> RelocatedDebugContents = consumeDebugMagic(
+ relocateDebugChunk(Linker.Alloc, DebugS), DebugS.getSectionName());
+
+ BinaryStreamReader Reader(RelocatedDebugContents, support::little);
+ ExitOnErr(Reader.readArray(Subsections, RelocatedDebugContents.size()));
+
+ for (const DebugSubsectionRecord &SS : Subsections) {
+ switch (SS.kind()) {
+ case DebugSubsectionKind::StringTable: {
+ assert(!CVStrTab.valid() &&
+ "Encountered multiple string table subsections!");
+ ExitOnErr(CVStrTab.initialize(SS.getRecordData()));
+ break;
+ }
+ case DebugSubsectionKind::FileChecksums:
+ assert(!Checksums.valid() &&
+ "Encountered multiple checksum subsections!");
+ ExitOnErr(Checksums.initialize(SS.getRecordData()));
+ break;
+ case DebugSubsectionKind::Lines:
+ // We can add the relocated line table directly to the PDB without
+ // modification because the file checksum offsets will stay the same.
+ File.ModuleDBI->addDebugSubsection(SS);
+ break;
+ case DebugSubsectionKind::FrameData: {
+ // We need to re-write string table indices here, so save off all
+ // frame data subsections until we've processed the entire list of
+ // subsections so that we can be sure we have the string table.
+ DebugFrameDataSubsectionRef FDS;
+ ExitOnErr(FDS.initialize(SS.getRecordData()));
+ NewFpoFrames.push_back(std::move(FDS));
+ break;
+ }
+ case DebugSubsectionKind::Symbols: {
+ Linker.mergeSymbolRecords(&File, IndexMap, StringTableReferences,
+ SS.getRecordData());
+ break;
+ }
+ default:
+ // FIXME: Process the rest of the subsections.
+ break;
+ }
+ }
+}
+
+void DebugSHandler::finish() {
+ pdb::DbiStreamBuilder &DbiBuilder = Linker.Builder.getDbiBuilder();
+
+ // We should have seen all debug subsections across the entire object file now
+ // which means that if a StringTable subsection and Checksums subsection were
+ // present, now is the time to handle them.
+ if (!CVStrTab.valid()) {
+ if (Checksums.valid())
+ fatal(".debug$S sections with a checksums subsection must also contain a "
+ "string table subsection");
+
+ if (!StringTableReferences.empty())
+ warn("No StringTable subsection was encountered, but there are string "
+ "table references");
+ return;
+ }
+
+ // Rewrite string table indices in the Fpo Data and symbol records to refer to
+ // the global PDB string table instead of the object file string table.
+ for (DebugFrameDataSubsectionRef &FDS : NewFpoFrames) {
+ const ulittle32_t *Reloc = FDS.getRelocPtr();
+ for (codeview::FrameData FD : FDS) {
+ FD.RvaStart += *Reloc;
+ FD.FrameFunc =
+ translateStringTableIndex(FD.FrameFunc, CVStrTab, Linker.PDBStrTab);
+ DbiBuilder.addNewFpoData(FD);
+ }
+ }
+
+ for (ulittle32_t *Ref : StringTableReferences)
+ *Ref = translateStringTableIndex(*Ref, CVStrTab, Linker.PDBStrTab);
+
+ // Make a new file checksum table that refers to offsets in the PDB-wide
+ // string table. Generally the string table subsection appears after the
+ // checksum table, so we have to do this after looping over all the
+ // subsections.
+ auto NewChecksums = make_unique<DebugChecksumsSubsection>(Linker.PDBStrTab);
+ for (FileChecksumEntry &FC : Checksums) {
+ SmallString<128> FileName =
+ ExitOnErr(CVStrTab.getString(FC.FileNameOffset));
+ pdbMakeAbsolute(FileName);
+ ExitOnErr(Linker.Builder.getDbiBuilder().addModuleSourceFile(
+ *File.ModuleDBI, FileName));
+ NewChecksums->addChecksum(FileName, FC.Kind, FC.Checksum);
+ }
+ File.ModuleDBI->addDebugSubsection(std::move(NewChecksums));
+}
+
+void PDBLinker::addObjFile(ObjFile *File, CVIndexMap *ExternIndexMap) {
+ if (File->wasProcessedForPDB())
+ return;
// Add a module descriptor for every object file. We need to put an absolute
// path to the object into the PDB. If this is a plain object, we make its
// path absolute. If it's an object in an archive, we make the archive path
// absolute.
bool InArchive = !File->ParentName.empty();
SmallString<128> Path = InArchive ? File->ParentName : File->getName();
- sys::fs::make_absolute(Path);
- sys::path::native(Path, sys::path::Style::windows);
+ pdbMakeAbsolute(Path);
StringRef Name = InArchive ? File->getName() : StringRef(Path);
- File->ModuleDBI = &ExitOnErr(Builder.getDbiBuilder().addModuleInfo(Name));
+ pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder();
+ File->ModuleDBI = &ExitOnErr(DbiBuilder.addModuleInfo(Name));
File->ModuleDBI->setObjFileName(Path);
auto Chunks = File->getChunks();
uint32_t Modi = File->ModuleDBI->getModuleIndex();
for (Chunk *C : Chunks) {
auto *SecChunk = dyn_cast<SectionChunk>(C);
- if (!SecChunk || !SecChunk->isLive())
+ if (!SecChunk || !SecChunk->Live)
continue;
pdb::SectionContrib SC = createSectionContrib(SecChunk, Modi);
File->ModuleDBI->setFirstSectionContrib(SC);
@@ -833,119 +1296,54 @@ void PDBLinker::addObjFile(ObjFile *File) {
// the PDB first, so that we can get the map from object file type and item
// indices to PDB type and item indices.
CVIndexMap ObjectIndexMap;
- auto IndexMapResult = mergeDebugT(File, ObjectIndexMap);
+ auto IndexMapResult =
+ mergeDebugT(File, ExternIndexMap ? ExternIndexMap : &ObjectIndexMap);
// If the .debug$T sections fail to merge, assume there is no debug info.
if (!IndexMapResult) {
- warn("Type server PDB for " + Name + " is invalid, ignoring debug info. " +
- toString(IndexMapResult.takeError()));
+ if (!Config->WarnDebugInfoUnusable) {
+ consumeError(IndexMapResult.takeError());
+ return;
+ }
+ StringRef FileName = sys::path::filename(Path);
+ warn("Cannot use debug info for '" + FileName + "' [LNK4099]\n" +
+ ">>> failed to load reference " +
+ StringRef(toString(IndexMapResult.takeError())));
return;
}
- const CVIndexMap &IndexMap = *IndexMapResult;
-
ScopedTimer T(SymbolMergingTimer);
- // Now do all live .debug$S sections.
- DebugStringTableSubsectionRef CVStrTab;
- DebugChecksumsSubsectionRef Checksums;
- std::vector<ulittle32_t *> StringTableReferences;
+ DebugSHandler DSH(*this, *File, *IndexMapResult);
+ // Now do all live .debug$S and .debug$F sections.
for (SectionChunk *DebugChunk : File->getDebugChunks()) {
- if (!DebugChunk->isLive() || DebugChunk->getSectionName() != ".debug$S")
+ if (!DebugChunk->Live || DebugChunk->getSize() == 0)
continue;
- ArrayRef<uint8_t> RelocatedDebugContents =
- relocateDebugChunk(Alloc, DebugChunk);
- if (RelocatedDebugContents.empty())
+ if (DebugChunk->getSectionName() == ".debug$S") {
+ DSH.handleDebugS(*DebugChunk);
continue;
-
- DebugSubsectionArray Subsections;
- BinaryStreamReader Reader(RelocatedDebugContents, support::little);
- ExitOnErr(Reader.readArray(Subsections, RelocatedDebugContents.size()));
-
- for (const DebugSubsectionRecord &SS : Subsections) {
- switch (SS.kind()) {
- case DebugSubsectionKind::StringTable: {
- assert(!CVStrTab.valid() &&
- "Encountered multiple string table subsections!");
- ExitOnErr(CVStrTab.initialize(SS.getRecordData()));
- break;
- }
- case DebugSubsectionKind::FileChecksums:
- assert(!Checksums.valid() &&
- "Encountered multiple checksum subsections!");
- ExitOnErr(Checksums.initialize(SS.getRecordData()));
- break;
- case DebugSubsectionKind::Lines:
- // We can add the relocated line table directly to the PDB without
- // modification because the file checksum offsets will stay the same.
- File->ModuleDBI->addDebugSubsection(SS);
- break;
- case DebugSubsectionKind::Symbols:
- if (Config->DebugGHashes) {
- mergeSymbolRecords(Alloc, File, Builder.getGsiBuilder(), IndexMap,
- GlobalIDTable, StringTableReferences,
- SS.getRecordData());
- } else {
- mergeSymbolRecords(Alloc, File, Builder.getGsiBuilder(), IndexMap,
- IDTable, StringTableReferences,
- SS.getRecordData());
- }
- break;
- default:
- // FIXME: Process the rest of the subsections.
- break;
- }
}
- }
- // We should have seen all debug subsections across the entire object file now
- // which means that if a StringTable subsection and Checksums subsection were
- // present, now is the time to handle them.
- if (!CVStrTab.valid()) {
- if (Checksums.valid())
- fatal(".debug$S sections with a checksums subsection must also contain a "
- "string table subsection");
+ if (DebugChunk->getSectionName() == ".debug$F") {
+ ArrayRef<uint8_t> RelocatedDebugContents =
+ relocateDebugChunk(Alloc, *DebugChunk);
- if (!StringTableReferences.empty())
- warn("No StringTable subsection was encountered, but there are string "
- "table references");
- return;
- }
+ FixedStreamArray<object::FpoData> FpoRecords;
+ BinaryStreamReader Reader(RelocatedDebugContents, support::little);
+ uint32_t Count = RelocatedDebugContents.size() / sizeof(object::FpoData);
+ ExitOnErr(Reader.readArray(FpoRecords, Count));
- // Rewrite each string table reference based on the value that the string
- // assumes in the final PDB.
- for (ulittle32_t *Ref : StringTableReferences) {
- auto ExpectedString = CVStrTab.getString(*Ref);
- if (!ExpectedString) {
- warn("Invalid string table reference");
- consumeError(ExpectedString.takeError());
+ // These are already relocated and don't refer to the string table, so we
+ // can just copy it.
+ for (const object::FpoData &FD : FpoRecords)
+ DbiBuilder.addOldFpoData(FD);
continue;
}
-
- *Ref = PDBStrTab.insert(*ExpectedString);
}
- // Make a new file checksum table that refers to offsets in the PDB-wide
- // string table. Generally the string table subsection appears after the
- // checksum table, so we have to do this after looping over all the
- // subsections.
- auto NewChecksums = make_unique<DebugChecksumsSubsection>(PDBStrTab);
- for (FileChecksumEntry &FC : Checksums) {
- SmallString<128> FileName = ExitOnErr(CVStrTab.getString(FC.FileNameOffset));
- if (!sys::path::is_absolute(FileName) &&
- !Config->PDBSourcePath.empty()) {
- SmallString<128> AbsoluteFileName = Config->PDBSourcePath;
- sys::path::append(AbsoluteFileName, FileName);
- sys::path::native(AbsoluteFileName);
- sys::path::remove_dots(AbsoluteFileName, /*remove_dot_dots=*/true);
- FileName = std::move(AbsoluteFileName);
- }
- ExitOnErr(Builder.getDbiBuilder().addModuleSourceFile(*File->ModuleDBI,
- FileName));
- NewChecksums->addChecksum(FileName, FC.Kind, FC.Checksum);
- }
- File->ModuleDBI->addDebugSubsection(std::move(NewChecksums));
+ // Do any post-processing now that all .debug$S sections have been processed.
+ DSH.finish();
}
static PublicSym32 createPublic(Defined *Def) {
@@ -977,13 +1375,8 @@ void PDBLinker::addObjectsToPDB() {
// Construct TPI and IPI stream contents.
ScopedTimer T2(TpiStreamLayoutTimer);
- if (Config->DebugGHashes) {
- addTypeInfo(Builder.getTpiBuilder(), GlobalTypeTable);
- addTypeInfo(Builder.getIpiBuilder(), GlobalIDTable);
- } else {
- addTypeInfo(Builder.getTpiBuilder(), TypeTable);
- addTypeInfo(Builder.getIpiBuilder(), IDTable);
- }
+ addTypeInfo(Builder.getTpiBuilder(), getTypeTable());
+ addTypeInfo(Builder.getIpiBuilder(), getIDTable());
T2.stop();
ScopedTimer T3(GlobalsLayoutTimer);
@@ -999,10 +1392,10 @@ void PDBLinker::addObjectsToPDB() {
if (!Publics.empty()) {
// Sort the public symbols and add them to the stream.
- std::sort(Publics.begin(), Publics.end(),
- [](const PublicSym32 &L, const PublicSym32 &R) {
- return L.Name < R.Name;
- });
+ sort(parallel::par, Publics.begin(), Publics.end(),
+ [](const PublicSym32 &L, const PublicSym32 &R) {
+ return L.Name < R.Name;
+ });
for (const PublicSym32 &Pub : Publics)
GsiBuilder.addPublicSymbol(Pub);
}
@@ -1037,6 +1430,32 @@ static codeview::CPUType toCodeViewMachine(COFF::MachineTypes Machine) {
}
}
+// Mimic MSVC which surrounds arguments containing whitespace with quotes.
+// Double double-quotes are handled, so that the resulting string can be
+// executed again on the cmd-line.
+static std::string quote(ArrayRef<StringRef> Args) {
+ std::string R;
+ R.reserve(256);
+ for (StringRef A : Args) {
+ if (!R.empty())
+ R.push_back(' ');
+ bool HasWS = A.find(' ') != StringRef::npos;
+ bool HasQ = A.find('"') != StringRef::npos;
+ if (HasWS || HasQ)
+ R.push_back('"');
+ if (HasQ) {
+ SmallVector<StringRef, 4> S;
+ A.split(S, '"');
+ R.append(join(S, "\"\""));
+ } else {
+ R.append(A);
+ }
+ if (HasWS || HasQ)
+ R.push_back('"');
+ }
+ return R;
+}
+
static void addCommonLinkerModuleSymbols(StringRef Path,
pdb::DbiModuleDescriptorBuilder &Mod,
BumpPtrAllocator &Allocator) {
@@ -1072,14 +1491,17 @@ static void addCommonLinkerModuleSymbols(StringRef Path,
CS.setLanguage(SourceLanguage::Link);
ArrayRef<StringRef> Args = makeArrayRef(Config->Argv).drop_front();
- std::string ArgStr = llvm::join(Args, " ");
+ std::string ArgStr = quote(Args);
EBS.Fields.push_back("cwd");
SmallString<64> cwd;
- sys::fs::current_path(cwd);
+ if (Config->PDBSourcePath.empty())
+ sys::fs::current_path(cwd);
+ else
+ cwd = Config->PDBSourcePath;
EBS.Fields.push_back(cwd);
EBS.Fields.push_back("exe");
SmallString<64> exe = Config->Argv[0];
- llvm::sys::fs::make_absolute(exe);
+ pdbMakeAbsolute(exe);
EBS.Fields.push_back(exe);
EBS.Fields.push_back("pdb");
EBS.Fields.push_back(Path);
@@ -1111,7 +1533,7 @@ static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &Mod,
void coff::createPDB(SymbolTable *Symtab,
ArrayRef<OutputSection *> OutputSections,
ArrayRef<uint8_t> SectionTable,
- const llvm::codeview::DebugInfo &BuildId) {
+ llvm::codeview::DebugInfo *BuildId) {
ScopedTimer T1(TotalPdbLinkTimer);
PDBLinker PDB(Symtab);
@@ -1121,12 +1543,19 @@ void coff::createPDB(SymbolTable *Symtab,
PDB.addNatvisFiles();
ScopedTimer T2(DiskCommitTimer);
- PDB.commit();
+ codeview::GUID Guid;
+ PDB.commit(&Guid);
+ memcpy(&BuildId->PDB70.Signature, &Guid, 16);
}
-void PDBLinker::initialize(const llvm::codeview::DebugInfo &BuildId) {
+void PDBLinker::initialize(llvm::codeview::DebugInfo *BuildId) {
ExitOnErr(Builder.initialize(4096)); // 4096 is blocksize
+ BuildId->Signature.CVSignature = OMF::Signature::PDB70;
+ // Signature is set to a hash of the PDB contents when the PDB is done.
+ memset(BuildId->PDB70.Signature, 0, 16);
+ BuildId->PDB70.Age = 1;
+
// Create streams in MSF for predefined streams, namely
// PDB, TPI, DBI and IPI.
for (int I = 0; I < (int)pdb::kSpecialStreamCount; ++I)
@@ -1134,15 +1563,12 @@ void PDBLinker::initialize(const llvm::codeview::DebugInfo &BuildId) {
// Add an Info stream.
auto &InfoBuilder = Builder.getInfoBuilder();
- GUID uuid;
- memcpy(&uuid, &BuildId.PDB70.Signature, sizeof(uuid));
- InfoBuilder.setAge(BuildId.PDB70.Age);
- InfoBuilder.setGuid(uuid);
InfoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70);
+ InfoBuilder.setHashPDBContentsToGUID(true);
// Add an empty DBI stream.
pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder();
- DbiBuilder.setAge(BuildId.PDB70.Age);
+ DbiBuilder.setAge(BuildId->PDB70.Age);
DbiBuilder.setVersionHeader(pdb::PdbDbiV70);
DbiBuilder.setMachineType(Config->Machine);
// Technically we are not link.exe 14.11, but there are known cases where
@@ -1157,8 +1583,7 @@ void PDBLinker::addSections(ArrayRef<OutputSection *> OutputSections,
// It's not entirely clear what this is, but the * Linker * module uses it.
pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder();
NativePath = Config->PDBPath;
- sys::fs::make_absolute(NativePath);
- sys::path::native(NativePath, sys::path::Style::windows);
+ pdbMakeAbsolute(NativePath);
uint32_t PdbFilePathNI = DbiBuilder.addECName(NativePath);
auto &LinkerModule = ExitOnErr(DbiBuilder.addModuleInfo("* Linker *"));
LinkerModule.setPdbFilePathNI(PdbFilePathNI);
@@ -1167,7 +1592,7 @@ void PDBLinker::addSections(ArrayRef<OutputSection *> OutputSections,
// Add section contributions. They must be ordered by ascending RVA.
for (OutputSection *OS : OutputSections) {
addLinkerModuleSectionSymbol(LinkerModule, *OS, Alloc);
- for (Chunk *C : OS->getChunks()) {
+ for (Chunk *C : OS->Chunks) {
pdb::SectionContrib SC =
createSectionContrib(C, LinkerModule.getModuleIndex());
Builder.getDbiBuilder().addSectionContrib(SC);
@@ -1186,9 +1611,9 @@ void PDBLinker::addSections(ArrayRef<OutputSection *> OutputSections,
DbiBuilder.addDbgStream(pdb::DbgHeaderType::SectionHdr, SectionTable));
}
-void PDBLinker::commit() {
+void PDBLinker::commit(codeview::GUID *Guid) {
// Write to a file.
- ExitOnErr(Builder.commit(Config->PDBPath));
+ ExitOnErr(Builder.commit(Config->PDBPath, Guid));
}
static Expected<StringRef>
@@ -1315,20 +1740,26 @@ std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *C,
if (!findLineTable(C, Addr, CVStrTab, Checksums, Lines, OffsetInLinetable))
return {"", 0};
- uint32_t NameIndex;
- uint32_t LineNumber;
+ Optional<uint32_t> NameIndex;
+ Optional<uint32_t> LineNumber;
for (LineColumnEntry &Entry : Lines) {
for (const LineNumberEntry &LN : Entry.LineNumbers) {
+ LineInfo LI(LN.Flags);
if (LN.Offset > OffsetInLinetable) {
+ if (!NameIndex) {
+ NameIndex = Entry.NameIndex;
+ LineNumber = LI.getStartLine();
+ }
StringRef Filename =
- ExitOnErr(getFileName(CVStrTab, Checksums, NameIndex));
- return {Filename, LineNumber};
+ ExitOnErr(getFileName(CVStrTab, Checksums, *NameIndex));
+ return {Filename, *LineNumber};
}
- LineInfo LI(LN.Flags);
NameIndex = Entry.NameIndex;
LineNumber = LI.getStartLine();
}
}
- StringRef Filename = ExitOnErr(getFileName(CVStrTab, Checksums, NameIndex));
- return {Filename, LineNumber};
+ if (!NameIndex)
+ return {"", 0};
+ StringRef Filename = ExitOnErr(getFileName(CVStrTab, Checksums, *NameIndex));
+ return {Filename, *LineNumber};
}
diff --git a/COFF/PDB.h b/COFF/PDB.h
index a98d129a633b..ea7a9996f415 100644
--- a/COFF/PDB.h
+++ b/COFF/PDB.h
@@ -28,7 +28,7 @@ class SymbolTable;
void createPDB(SymbolTable *Symtab,
llvm::ArrayRef<OutputSection *> OutputSections,
llvm::ArrayRef<uint8_t> SectionTable,
- const llvm::codeview::DebugInfo &BuildId);
+ llvm::codeview::DebugInfo *BuildId);
std::pair<llvm::StringRef, uint32_t> getFileLine(const SectionChunk *C,
uint32_t Addr);
diff --git a/COFF/SymbolTable.cpp b/COFF/SymbolTable.cpp
index b286d865caaf..1a9e0455dc1d 100644
--- a/COFF/SymbolTable.cpp
+++ b/COFF/SymbolTable.cpp
@@ -60,16 +60,16 @@ void SymbolTable::addFile(InputFile *File) {
}
static void errorOrWarn(const Twine &S) {
- if (Config->Force)
+ if (Config->ForceUnresolved)
warn(S);
else
error(S);
}
-// Returns the name of the symbol in SC whose value is <= Addr that is closest
-// to Addr. This is generally the name of the global variable or function whose
-// definition contains Addr.
-static StringRef getSymbolName(SectionChunk *SC, uint32_t Addr) {
+// Returns the symbol in SC whose value is <= Addr that is closest to Addr.
+// This is generally the global variable or function whose definition contains
+// Addr.
+static Symbol *getSymbol(SectionChunk *SC, uint32_t Addr) {
DefinedRegular *Candidate = nullptr;
for (Symbol *S : SC->File->getSymbols()) {
@@ -81,14 +81,12 @@ static StringRef getSymbolName(SectionChunk *SC, uint32_t Addr) {
Candidate = D;
}
- if (!Candidate)
- return "";
- return Candidate->getName();
+ return Candidate;
}
-static std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex) {
+std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex) {
struct Location {
- StringRef SymName;
+ Symbol *Sym;
std::pair<StringRef, uint32_t> FileLine;
};
std::vector<Location> Locations;
@@ -102,14 +100,14 @@ static std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex) {
continue;
std::pair<StringRef, uint32_t> FileLine =
getFileLine(SC, R.VirtualAddress);
- StringRef SymName = getSymbolName(SC, R.VirtualAddress);
- if (!FileLine.first.empty() || !SymName.empty())
- Locations.push_back({SymName, FileLine});
+ Symbol *Sym = getSymbol(SC, R.VirtualAddress);
+ if (!FileLine.first.empty() || Sym)
+ Locations.push_back({Sym, FileLine});
}
}
if (Locations.empty())
- return "\n>>> referenced by " + toString(File) + "\n";
+ return "\n>>> referenced by " + toString(File);
std::string Out;
llvm::raw_string_ostream OS(Out);
@@ -119,13 +117,87 @@ static std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex) {
OS << Loc.FileLine.first << ":" << Loc.FileLine.second
<< "\n>>> ";
OS << toString(File);
- if (!Loc.SymName.empty())
- OS << ":(" << Loc.SymName << ')';
+ if (Loc.Sym)
+ OS << ":(" << toString(*Loc.Sym) << ')';
}
- OS << '\n';
return OS.str();
}
+void SymbolTable::loadMinGWAutomaticImports() {
+ for (auto &I : SymMap) {
+ Symbol *Sym = I.second;
+ auto *Undef = dyn_cast<Undefined>(Sym);
+ if (!Undef)
+ continue;
+ if (!Sym->IsUsedInRegularObj)
+ continue;
+
+ StringRef Name = Undef->getName();
+
+ if (Name.startswith("__imp_"))
+ continue;
+ // If we have an undefined symbol, but we have a Lazy representing a
+ // symbol we could load from file, make sure to load that.
+ Lazy *L = dyn_cast_or_null<Lazy>(find(("__imp_" + Name).str()));
+ if (!L || L->PendingArchiveLoad)
+ continue;
+
+ log("Loading lazy " + L->getName() + " from " + L->File->getName() +
+ " for automatic import");
+ L->PendingArchiveLoad = true;
+ L->File->addMember(&L->Sym);
+ }
+}
+
+bool SymbolTable::handleMinGWAutomaticImport(Symbol *Sym, StringRef Name) {
+ if (Name.startswith("__imp_"))
+ return false;
+ Defined *Imp = dyn_cast_or_null<Defined>(find(("__imp_" + Name).str()));
+ if (!Imp)
+ return false;
+
+ // Replace the reference directly to a variable with a reference
+ // to the import address table instead. This obviously isn't right,
+ // but we mark the symbol as IsRuntimePseudoReloc, and a later pass
+ // will add runtime pseudo relocations for every relocation against
+ // this Symbol. The runtime pseudo relocation framework expects the
+ // reference itself to point at the IAT entry.
+ size_t ImpSize = 0;
+ if (isa<DefinedImportData>(Imp)) {
+ log("Automatically importing " + Name + " from " +
+ cast<DefinedImportData>(Imp)->getDLLName());
+ ImpSize = sizeof(DefinedImportData);
+ } else if (isa<DefinedRegular>(Imp)) {
+ log("Automatically importing " + Name + " from " +
+ toString(cast<DefinedRegular>(Imp)->File));
+ ImpSize = sizeof(DefinedRegular);
+ } else {
+ warn("unable to automatically import " + Name + " from " + Imp->getName() +
+ " from " + toString(cast<DefinedRegular>(Imp)->File) +
+ "; unexpected symbol type");
+ return false;
+ }
+ Sym->replaceKeepingName(Imp, ImpSize);
+ Sym->IsRuntimePseudoReloc = true;
+
+ // There may exist symbols named .refptr.<name> which only consist
+ // of a single pointer to <name>. If it turns out <name> is
+ // automatically imported, we don't need to keep the .refptr.<name>
+ // pointer at all, but redirect all accesses to it to the IAT entry
+ // for __imp_<name> instead, and drop the whole .refptr.<name> chunk.
+ DefinedRegular *Refptr =
+ dyn_cast_or_null<DefinedRegular>(find((".refptr." + Name).str()));
+ if (Refptr && Refptr->getChunk()->getSize() == Config->Wordsize) {
+ SectionChunk *SC = dyn_cast_or_null<SectionChunk>(Refptr->getChunk());
+ if (SC && SC->Relocs.size() == 1 && *SC->symbols().begin() == Sym) {
+ log("Replacing .refptr." + Name + " with " + Imp->getName());
+ Refptr->getChunk()->Live = false;
+ Refptr->replaceKeepingName(Imp, ImpSize);
+ }
+ }
+ return true;
+}
+
void SymbolTable::reportRemainingUndefines() {
SmallPtrSet<Symbol *, 8> Undefs;
DenseMap<Symbol *, Symbol *> LocalImports;
@@ -169,9 +241,17 @@ void SymbolTable::reportRemainingUndefines() {
}
}
+ // We don't want to report missing Microsoft precompiled headers symbols.
+ // A proper message will be emitted instead in PDBLinker::aquirePrecompObj
+ if (Name.contains("_PchSym_"))
+ continue;
+
+ if (Config->MinGW && handleMinGWAutomaticImport(Sym, Name))
+ continue;
+
// Remaining undefined symbols are not fatal if /force is specified.
// They are replaced with dummy defined symbols.
- if (Config->Force)
+ if (Config->ForceUnresolved)
replaceSymbol<DefinedAbsolute>(Sym, Name, 0);
Undefs.insert(Sym);
}
@@ -181,10 +261,10 @@ void SymbolTable::reportRemainingUndefines() {
for (Symbol *B : Config->GCRoot) {
if (Undefs.count(B))
- errorOrWarn("<root>: undefined symbol: " + B->getName());
+ errorOrWarn("<root>: undefined symbol: " + toString(*B));
if (Config->WarnLocallyDefinedImported)
if (Symbol *Imp = LocalImports.lookup(B))
- warn("<root>: locally defined symbol imported: " + Imp->getName() +
+ warn("<root>: locally defined symbol imported: " + toString(*Imp) +
" (defined in " + toString(Imp->getFile()) + ") [LNK4217]");
}
@@ -195,34 +275,41 @@ void SymbolTable::reportRemainingUndefines() {
if (!Sym)
continue;
if (Undefs.count(Sym))
- errorOrWarn("undefined symbol: " + Sym->getName() +
+ errorOrWarn("undefined symbol: " + toString(*Sym) +
getSymbolLocations(File, SymIndex));
if (Config->WarnLocallyDefinedImported)
if (Symbol *Imp = LocalImports.lookup(Sym))
- warn(toString(File) + ": locally defined symbol imported: " +
- Imp->getName() + " (defined in " + toString(Imp->getFile()) +
- ") [LNK4217]");
+ warn(toString(File) +
+ ": locally defined symbol imported: " + toString(*Imp) +
+ " (defined in " + toString(Imp->getFile()) + ") [LNK4217]");
}
}
}
std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) {
+ bool Inserted = false;
Symbol *&Sym = SymMap[CachedHashStringRef(Name)];
- if (Sym)
- return {Sym, false};
- Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
- Sym->IsUsedInRegularObj = false;
- Sym->PendingArchiveLoad = false;
- return {Sym, true};
+ if (!Sym) {
+ Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
+ Sym->IsUsedInRegularObj = false;
+ Sym->PendingArchiveLoad = false;
+ Inserted = true;
+ }
+ return {Sym, Inserted};
+}
+
+std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name, InputFile *File) {
+ std::pair<Symbol *, bool> Result = insert(Name);
+ if (!File || !isa<BitcodeFile>(File))
+ Result.first->IsUsedInRegularObj = true;
+ return Result;
}
Symbol *SymbolTable::addUndefined(StringRef Name, InputFile *F,
bool IsWeakAlias) {
Symbol *S;
bool WasInserted;
- std::tie(S, WasInserted) = insert(Name);
- if (!F || !isa<BitcodeFile>(F))
- S->IsUsedInRegularObj = true;
+ std::tie(S, WasInserted) = insert(Name, F);
if (WasInserted || (isa<Lazy>(S) && IsWeakAlias)) {
replaceSymbol<Undefined>(S, Name);
return S;
@@ -253,14 +340,20 @@ void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol Sym) {
}
void SymbolTable::reportDuplicate(Symbol *Existing, InputFile *NewFile) {
- error("duplicate symbol: " + toString(*Existing) + " in " +
- toString(Existing->getFile()) + " and in " + toString(NewFile));
+ std::string Msg = "duplicate symbol: " + toString(*Existing) + " in " +
+ toString(Existing->getFile()) + " and in " +
+ toString(NewFile);
+
+ if (Config->ForceMultiple)
+ warn(Msg);
+ else
+ error(Msg);
}
Symbol *SymbolTable::addAbsolute(StringRef N, COFFSymbolRef Sym) {
Symbol *S;
bool WasInserted;
- std::tie(S, WasInserted) = insert(N);
+ std::tie(S, WasInserted) = insert(N, nullptr);
S->IsUsedInRegularObj = true;
if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S))
replaceSymbol<DefinedAbsolute>(S, N, Sym);
@@ -272,7 +365,7 @@ Symbol *SymbolTable::addAbsolute(StringRef N, COFFSymbolRef Sym) {
Symbol *SymbolTable::addAbsolute(StringRef N, uint64_t VA) {
Symbol *S;
bool WasInserted;
- std::tie(S, WasInserted) = insert(N);
+ std::tie(S, WasInserted) = insert(N, nullptr);
S->IsUsedInRegularObj = true;
if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S))
replaceSymbol<DefinedAbsolute>(S, N, VA);
@@ -284,7 +377,7 @@ Symbol *SymbolTable::addAbsolute(StringRef N, uint64_t VA) {
Symbol *SymbolTable::addSynthetic(StringRef N, Chunk *C) {
Symbol *S;
bool WasInserted;
- std::tie(S, WasInserted) = insert(N);
+ std::tie(S, WasInserted) = insert(N, nullptr);
S->IsUsedInRegularObj = true;
if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S))
replaceSymbol<DefinedSynthetic>(S, N, C);
@@ -298,9 +391,7 @@ Symbol *SymbolTable::addRegular(InputFile *F, StringRef N,
SectionChunk *C) {
Symbol *S;
bool WasInserted;
- std::tie(S, WasInserted) = insert(N);
- if (!isa<BitcodeFile>(F))
- S->IsUsedInRegularObj = true;
+ std::tie(S, WasInserted) = insert(N, F);
if (WasInserted || !isa<DefinedRegular>(S))
replaceSymbol<DefinedRegular>(S, F, N, /*IsCOMDAT*/ false,
/*IsExternal*/ true, Sym, C);
@@ -314,9 +405,7 @@ SymbolTable::addComdat(InputFile *F, StringRef N,
const coff_symbol_generic *Sym) {
Symbol *S;
bool WasInserted;
- std::tie(S, WasInserted) = insert(N);
- if (!isa<BitcodeFile>(F))
- S->IsUsedInRegularObj = true;
+ std::tie(S, WasInserted) = insert(N, F);
if (WasInserted || !isa<DefinedRegular>(S)) {
replaceSymbol<DefinedRegular>(S, F, N, /*IsCOMDAT*/ true,
/*IsExternal*/ true, Sym, nullptr);
@@ -331,9 +420,7 @@ 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(N);
- if (!isa<BitcodeFile>(F))
- S->IsUsedInRegularObj = true;
+ std::tie(S, WasInserted) = insert(N, F);
if (WasInserted || !isa<DefinedCOFF>(S))
replaceSymbol<DefinedCommon>(S, F, N, Size, Sym, C);
else if (auto *DC = dyn_cast<DefinedCommon>(S))
@@ -345,7 +432,7 @@ Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size,
Symbol *SymbolTable::addImportData(StringRef N, ImportFile *F) {
Symbol *S;
bool WasInserted;
- std::tie(S, WasInserted) = insert(N);
+ std::tie(S, WasInserted) = insert(N, nullptr);
S->IsUsedInRegularObj = true;
if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S)) {
replaceSymbol<DefinedImportData>(S, N, F);
@@ -360,7 +447,7 @@ Symbol *SymbolTable::addImportThunk(StringRef Name, DefinedImportData *ID,
uint16_t Machine) {
Symbol *S;
bool WasInserted;
- std::tie(S, WasInserted) = insert(Name);
+ std::tie(S, WasInserted) = insert(Name, nullptr);
S->IsUsedInRegularObj = true;
if (WasInserted || isa<Undefined>(S) || isa<Lazy>(S)) {
replaceSymbol<DefinedImportThunk>(S, Name, ID, Machine);
diff --git a/COFF/SymbolTable.h b/COFF/SymbolTable.h
index 30cb1a5410c3..00e55dbb7a02 100644
--- a/COFF/SymbolTable.h
+++ b/COFF/SymbolTable.h
@@ -54,6 +54,9 @@ public:
// symbols.
void reportRemainingUndefines();
+ void loadMinGWAutomaticImports();
+ bool handleMinGWAutomaticImport(Symbol *Sym, StringRef Name);
+
// Returns a list of chunks of selected symbols.
std::vector<Chunk *> getChunks();
@@ -108,7 +111,10 @@ public:
}
private:
+ /// Inserts symbol if not already present.
std::pair<Symbol *, bool> insert(StringRef Name);
+ /// Same as insert(Name), but also sets IsUsedInRegularObj.
+ std::pair<Symbol *, bool> insert(StringRef Name, InputFile *F);
StringRef findByPrefix(StringRef Prefix);
llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> SymMap;
@@ -117,6 +123,8 @@ private:
extern SymbolTable *Symtab;
+std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex);
+
} // namespace coff
} // namespace lld
diff --git a/COFF/Symbols.cpp b/COFF/Symbols.cpp
index 7c8b7d5e8fc5..ccaf86417f10 100644
--- a/COFF/Symbols.cpp
+++ b/COFF/Symbols.cpp
@@ -54,7 +54,7 @@ InputFile *Symbol::getFile() {
bool Symbol::isLive() const {
if (auto *R = dyn_cast<DefinedRegular>(this))
- return R->getChunk()->isLive();
+ return R->getChunk()->Live;
if (auto *Imp = dyn_cast<DefinedImportData>(this))
return Imp->File->Live;
if (auto *Imp = dyn_cast<DefinedImportThunk>(this))
@@ -63,6 +63,13 @@ bool Symbol::isLive() const {
return true;
}
+// MinGW specific.
+void Symbol::replaceKeepingName(Symbol *Other, size_t Size) {
+ StringRef OrigName = Name;
+ memcpy(this, Other, Size);
+ Name = OrigName;
+}
+
COFFSymbolRef DefinedCOFF::getCOFFSymbol() {
size_t SymSize = cast<ObjFile>(File)->getCOFFObj()->getSymbolTableEntrySize();
if (SymSize == sizeof(coff_symbol16))
diff --git a/COFF/Symbols.h b/COFF/Symbols.h
index 783965adbd9a..4a8693e22e3c 100644
--- a/COFF/Symbols.h
+++ b/COFF/Symbols.h
@@ -39,9 +39,9 @@ class Symbol {
public:
enum Kind {
// The order of these is significant. We start with the regular defined
- // symbols as those are the most prevelant and the zero tag is the cheapest
+ // symbols as those are the most prevalent and the zero tag is the cheapest
// to set. Among the defined kinds, the lower the kind is preferred over
- // the higher kind when testing wether one symbol should take precedence
+ // the higher kind when testing whether one symbol should take precedence
// over another.
DefinedRegularKind = 0,
DefinedCommonKind,
@@ -66,6 +66,8 @@ public:
// Returns the symbol name.
StringRef getName();
+ void replaceKeepingName(Symbol *Other, size_t Size);
+
// Returns the file from which this symbol was created.
InputFile *getFile();
@@ -78,7 +80,7 @@ protected:
explicit Symbol(Kind K, StringRef N = "")
: SymbolKind(K), IsExternal(true), IsCOMDAT(false),
WrittenToSymtab(false), PendingArchiveLoad(false), IsGCRoot(false),
- Name(N) {}
+ IsRuntimePseudoReloc(false), Name(N) {}
const unsigned SymbolKind : 8;
unsigned IsExternal : 1;
@@ -102,6 +104,8 @@ public:
/// True if we've already added this symbol to the list of GC roots.
unsigned IsGCRoot : 1;
+ unsigned IsRuntimePseudoReloc : 1;
+
protected:
StringRef Name;
};
@@ -331,8 +335,8 @@ private:
Chunk *Data;
};
-// If you have a symbol "__imp_foo" in your object file, a symbol name
-// "foo" becomes automatically available as a pointer to "__imp_foo".
+// If you have a symbol "foo" in your object file, a symbol name
+// "__imp_foo" becomes automatically available as a pointer to "foo".
// This class is for such automatically-created symbols.
// Yes, this is an odd feature. We didn't intend to implement that.
// This is here just for compatibility with MSVC.
diff --git a/COFF/Writer.cpp b/COFF/Writer.cpp
index d17405ec26ab..258796ea6057 100644
--- a/COFF/Writer.cpp
+++ b/COFF/Writer.cpp
@@ -77,36 +77,38 @@ static const int SectorSize = 512;
static const int DOSStubSize = sizeof(dos_header) + sizeof(DOSProgram);
static_assert(DOSStubSize % 8 == 0, "DOSStub size must be multiple of 8");
-static const int NumberfOfDataDirectory = 16;
+static const int NumberOfDataDirectory = 16;
namespace {
class DebugDirectoryChunk : public Chunk {
public:
- DebugDirectoryChunk(const std::vector<Chunk *> &R) : Records(R) {}
+ DebugDirectoryChunk(const std::vector<Chunk *> &R, bool WriteRepro)
+ : Records(R), WriteRepro(WriteRepro) {}
size_t getSize() const override {
- return Records.size() * sizeof(debug_directory);
+ return (Records.size() + int(WriteRepro)) * sizeof(debug_directory);
}
void writeTo(uint8_t *B) const override {
auto *D = reinterpret_cast<debug_directory *>(B + OutputSectionOff);
for (const Chunk *Record : Records) {
- D->Characteristics = 0;
- D->TimeDateStamp = 0;
- D->MajorVersion = 0;
- D->MinorVersion = 0;
- D->Type = COFF::IMAGE_DEBUG_TYPE_CODEVIEW;
- D->SizeOfData = Record->getSize();
- D->AddressOfRawData = Record->getRVA();
OutputSection *OS = Record->getOutputSection();
uint64_t Offs = OS->getFileOff() + (Record->getRVA() - OS->getRVA());
- D->PointerToRawData = Offs;
-
- TimeDateStamps.push_back(&D->TimeDateStamp);
+ fillEntry(D, COFF::IMAGE_DEBUG_TYPE_CODEVIEW, Record->getSize(),
+ Record->getRVA(), Offs);
++D;
}
+
+ if (WriteRepro) {
+ // FIXME: The COFF spec allows either a 0-sized entry to just say
+ // "the timestamp field is really a hash", or a 4-byte size field
+ // followed by that many bytes containing a longer hash (with the
+ // lowest 4 bytes usually being the timestamp in little-endian order).
+ // Consider storing the full 8 bytes computed by xxHash64 here.
+ fillEntry(D, COFF::IMAGE_DEBUG_TYPE_REPRO, 0, 0, 0);
+ }
}
void setTimeDateStamp(uint32_t TimeDateStamp) {
@@ -115,8 +117,23 @@ public:
}
private:
+ void fillEntry(debug_directory *D, COFF::DebugType DebugType, size_t Size,
+ uint64_t RVA, uint64_t Offs) const {
+ D->Characteristics = 0;
+ D->TimeDateStamp = 0;
+ D->MajorVersion = 0;
+ D->MinorVersion = 0;
+ D->Type = DebugType;
+ D->SizeOfData = Size;
+ D->AddressOfRawData = RVA;
+ D->PointerToRawData = Offs;
+
+ TimeDateStamps.push_back(&D->TimeDateStamp);
+ }
+
mutable std::vector<support::ulittle32_t *> TimeDateStamps;
const std::vector<Chunk *> &Records;
+ bool WriteRepro;
};
class CVDebugRecordChunk : public Chunk {
@@ -150,14 +167,22 @@ private:
void createSections();
void createMiscChunks();
void createImportTables();
+ void appendImportThunks();
+ void locateImportTables(
+ std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> &Map);
void createExportTable();
void mergeSections();
+ void readRelocTargets();
+ void removeUnusedSections();
void assignAddresses();
+ void finalizeAddresses();
void removeEmptySections();
void createSymbolAndStringTable();
void openFile(StringRef OutputPath);
template <typename PEHeaderTy> void writeHeader();
void createSEHTable();
+ void createRuntimePseudoRelocs();
+ void insertCtorDtorSymbols();
void createGuardCFTables();
void markSymbolsForRVATable(ObjFile *File,
ArrayRef<SectionChunk *> SymIdxChunks,
@@ -168,6 +193,7 @@ private:
void writeSections();
void writeBuildId();
void sortExceptionTable();
+ void sortCRTSectionChunks(std::vector<Chunk *> &Chunks);
llvm::Optional<coff_symbol16> createSymbol(Defined *D);
size_t addEntryToStringTable(StringRef Str);
@@ -184,6 +210,10 @@ private:
std::vector<char> Strtab;
std::vector<llvm::object::coff_symbol16> OutputSymtab;
IdataContents Idata;
+ Chunk *ImportTableStart = nullptr;
+ uint64_t ImportTableSize = 0;
+ Chunk *IATStart = nullptr;
+ uint64_t IATSize = 0;
DelayLoadContents DelayIdata;
EdataContents Edata;
bool SetNoSEHCharacteristic = false;
@@ -191,7 +221,6 @@ private:
DebugDirectoryChunk *DebugDirectory = nullptr;
std::vector<Chunk *> DebugRecords;
CVDebugRecordChunk *BuildId = nullptr;
- Optional<codeview::DebugInfo> PreviousBuildId;
ArrayRef<uint8_t> SectionTable;
uint64_t FileSize;
@@ -209,6 +238,8 @@ private:
OutputSection *DidatSec;
OutputSection *RsrcSec;
OutputSection *RelocSec;
+ OutputSection *CtorsSec;
+ OutputSection *DtorsSec;
// The first and last .pdata sections in the output file.
//
@@ -237,6 +268,11 @@ void OutputSection::addChunk(Chunk *C) {
C->setOutputSection(this);
}
+void OutputSection::insertChunkAtStart(Chunk *C) {
+ Chunks.insert(Chunks.begin(), C);
+ C->setOutputSection(this);
+}
+
void OutputSection::setPermissions(uint32_t C) {
Header.Characteristics &= ~PermMask;
Header.Characteristics |= C;
@@ -267,77 +303,206 @@ void OutputSection::writeHeaderTo(uint8_t *Buf) {
} // namespace coff
} // namespace lld
-// PDBs are matched against executables using a build id which consists of three
-// components:
-// 1. A 16-bit GUID
-// 2. An age
-// 3. A time stamp.
+// Check whether the target address S is in range from a relocation
+// of type RelType at address P.
+static bool isInRange(uint16_t RelType, uint64_t S, uint64_t P, int Margin) {
+ assert(Config->Machine == ARMNT);
+ int64_t Diff = AbsoluteDifference(S, P + 4) + Margin;
+ switch (RelType) {
+ case IMAGE_REL_ARM_BRANCH20T:
+ return isInt<21>(Diff);
+ case IMAGE_REL_ARM_BRANCH24T:
+ case IMAGE_REL_ARM_BLX23T:
+ return isInt<25>(Diff);
+ default:
+ return true;
+ }
+}
+
+// Return the last thunk for the given target if it is in range,
+// or create a new one.
+static std::pair<Defined *, bool>
+getThunk(DenseMap<uint64_t, Defined *> &LastThunks, Defined *Target, uint64_t P,
+ uint16_t Type, int Margin) {
+ Defined *&LastThunk = LastThunks[Target->getRVA()];
+ if (LastThunk && isInRange(Type, LastThunk->getRVA(), P, Margin))
+ return {LastThunk, false};
+ RangeExtensionThunk *C = make<RangeExtensionThunk>(Target);
+ Defined *D = make<DefinedSynthetic>("", C);
+ LastThunk = D;
+ return {D, true};
+}
+
+// This checks all relocations, and for any relocation which isn't in range
+// it adds a thunk after the section chunk that contains the relocation.
+// If the latest thunk for the specific target is in range, that is used
+// instead of creating a new thunk. All range checks are done with the
+// specified margin, to make sure that relocations that originally are in
+// range, but only barely, also get thunks - in case other added thunks makes
+// the target go out of range.
//
-// Debuggers and symbol servers match executables against debug info by checking
-// each of these components of the EXE/DLL against the corresponding value in
-// the PDB and failing a match if any of the components differ. In the case of
-// symbol servers, symbols are cached in a folder that is a function of the
-// GUID. As a result, in order to avoid symbol cache pollution where every
-// incremental build copies a new PDB to the symbol cache, we must try to re-use
-// the existing GUID if one exists, but bump the age. This way the match will
-// fail, so the symbol cache knows to use the new PDB, but the GUID matches, so
-// it overwrites the existing item in the symbol cache rather than making a new
-// one.
-static Optional<codeview::DebugInfo> loadExistingBuildId(StringRef Path) {
- // We don't need to incrementally update a previous build id if we're not
- // writing codeview debug info.
- if (!Config->Debug)
- return None;
+// After adding thunks, we verify that all relocations are in range (with
+// no extra margin requirements). If this failed, we restart (throwing away
+// the previously created thunks) and retry with a wider margin.
+static bool createThunks(std::vector<Chunk *> &Chunks, int Margin) {
+ bool AddressesChanged = false;
+ DenseMap<uint64_t, Defined *> LastThunks;
+ size_t ThunksSize = 0;
+ // Recheck Chunks.size() each iteration, since we can insert more
+ // elements into it.
+ for (size_t I = 0; I != Chunks.size(); ++I) {
+ SectionChunk *SC = dyn_cast_or_null<SectionChunk>(Chunks[I]);
+ if (!SC)
+ continue;
+ size_t ThunkInsertionSpot = I + 1;
+
+ // Try to get a good enough estimate of where new thunks will be placed.
+ // Offset this by the size of the new thunks added so far, to make the
+ // estimate slightly better.
+ size_t ThunkInsertionRVA = SC->getRVA() + SC->getSize() + ThunksSize;
+ for (size_t J = 0, E = SC->Relocs.size(); J < E; ++J) {
+ const coff_relocation &Rel = SC->Relocs[J];
+ Symbol *&RelocTarget = SC->RelocTargets[J];
+
+ // The estimate of the source address P should be pretty accurate,
+ // but we don't know whether the target Symbol address should be
+ // offset by ThunkSize or not (or by some of ThunksSize but not all of
+ // it), giving us some uncertainty once we have added one thunk.
+ uint64_t P = SC->getRVA() + Rel.VirtualAddress + ThunksSize;
+
+ Defined *Sym = dyn_cast_or_null<Defined>(RelocTarget);
+ if (!Sym)
+ continue;
- auto ExpectedBinary = llvm::object::createBinary(Path);
- if (!ExpectedBinary) {
- consumeError(ExpectedBinary.takeError());
- return None;
+ uint64_t S = Sym->getRVA();
+
+ if (isInRange(Rel.Type, S, P, Margin))
+ continue;
+
+ // If the target isn't in range, hook it up to an existing or new
+ // thunk.
+ Defined *Thunk;
+ bool WasNew;
+ std::tie(Thunk, WasNew) = getThunk(LastThunks, Sym, P, Rel.Type, Margin);
+ if (WasNew) {
+ Chunk *ThunkChunk = Thunk->getChunk();
+ ThunkChunk->setRVA(
+ ThunkInsertionRVA); // Estimate of where it will be located.
+ Chunks.insert(Chunks.begin() + ThunkInsertionSpot, ThunkChunk);
+ ThunkInsertionSpot++;
+ ThunksSize += ThunkChunk->getSize();
+ ThunkInsertionRVA += ThunkChunk->getSize();
+ AddressesChanged = true;
+ }
+ RelocTarget = Thunk;
+ }
}
+ return AddressesChanged;
+}
- auto Binary = std::move(*ExpectedBinary);
- if (!Binary.getBinary()->isCOFF())
- return None;
+// Verify that all relocations are in range, with no extra margin requirements.
+static bool verifyRanges(const std::vector<Chunk *> Chunks) {
+ for (Chunk *C : Chunks) {
+ SectionChunk *SC = dyn_cast_or_null<SectionChunk>(C);
+ if (!SC)
+ continue;
- std::error_code EC;
- COFFObjectFile File(Binary.getBinary()->getMemoryBufferRef(), EC);
- if (EC)
- return None;
+ for (size_t J = 0, E = SC->Relocs.size(); J < E; ++J) {
+ const coff_relocation &Rel = SC->Relocs[J];
+ Symbol *RelocTarget = SC->RelocTargets[J];
- // If the machine of the binary we're outputting doesn't match the machine
- // of the existing binary, don't try to re-use the build id.
- if (File.is64() != Config->is64() || File.getMachine() != Config->Machine)
- return None;
+ Defined *Sym = dyn_cast_or_null<Defined>(RelocTarget);
+ if (!Sym)
+ continue;
- for (const auto &DebugDir : File.debug_directories()) {
- if (DebugDir.Type != IMAGE_DEBUG_TYPE_CODEVIEW)
- continue;
+ uint64_t P = SC->getRVA() + Rel.VirtualAddress;
+ uint64_t S = Sym->getRVA();
- const codeview::DebugInfo *ExistingDI = nullptr;
- StringRef PDBFileName;
- if (auto EC = File.getDebugPDBInfo(ExistingDI, PDBFileName)) {
- (void)EC;
- return None;
+ if (!isInRange(Rel.Type, S, P, 0))
+ return false;
}
- // We only support writing PDBs in v70 format. So if this is not a build
- // id that we recognize / support, ignore it.
- if (ExistingDI->Signature.CVSignature != OMF::Signature::PDB70)
- return None;
- return *ExistingDI;
}
- return None;
+ return true;
+}
+
+// Assign addresses and add thunks if necessary.
+void Writer::finalizeAddresses() {
+ assignAddresses();
+ if (Config->Machine != ARMNT)
+ return;
+
+ size_t OrigNumChunks = 0;
+ for (OutputSection *Sec : OutputSections) {
+ Sec->OrigChunks = Sec->Chunks;
+ OrigNumChunks += Sec->Chunks.size();
+ }
+
+ int Pass = 0;
+ int Margin = 1024 * 100;
+ while (true) {
+ // First check whether we need thunks at all, or if the previous pass of
+ // adding them turned out ok.
+ bool RangesOk = true;
+ size_t NumChunks = 0;
+ for (OutputSection *Sec : OutputSections) {
+ if (!verifyRanges(Sec->Chunks)) {
+ RangesOk = false;
+ break;
+ }
+ NumChunks += Sec->Chunks.size();
+ }
+ if (RangesOk) {
+ if (Pass > 0)
+ log("Added " + Twine(NumChunks - OrigNumChunks) + " thunks with " +
+ "margin " + Twine(Margin) + " in " + Twine(Pass) + " passes");
+ return;
+ }
+
+ if (Pass >= 10)
+ fatal("adding thunks hasn't converged after " + Twine(Pass) + " passes");
+
+ if (Pass > 0) {
+ // If the previous pass didn't work out, reset everything back to the
+ // original conditions before retrying with a wider margin. This should
+ // ideally never happen under real circumstances.
+ for (OutputSection *Sec : OutputSections) {
+ Sec->Chunks = Sec->OrigChunks;
+ for (Chunk *C : Sec->Chunks)
+ C->resetRelocTargets();
+ }
+ Margin *= 2;
+ }
+
+ // Try adding thunks everywhere where it is needed, with a margin
+ // to avoid things going out of range due to the added thunks.
+ bool AddressesChanged = false;
+ for (OutputSection *Sec : OutputSections)
+ AddressesChanged |= createThunks(Sec->Chunks, Margin);
+ // If the verification above thought we needed thunks, we should have
+ // added some.
+ assert(AddressesChanged);
+
+ // Recalculate the layout for the whole image (and verify the ranges at
+ // the start of the next round).
+ assignAddresses();
+
+ Pass++;
+ }
}
// The main function of the writer.
void Writer::run() {
ScopedTimer T1(CodeLayoutTimer);
+ createImportTables();
createSections();
createMiscChunks();
- createImportTables();
+ appendImportThunks();
createExportTable();
mergeSections();
- assignAddresses();
+ readRelocTargets();
+ removeUnusedSections();
+ finalizeAddresses();
removeEmptySections();
setSectionPermissions();
createSymbolAndStringTable();
@@ -346,9 +511,6 @@ void Writer::run() {
fatal("image size (" + Twine(FileSize) + ") " +
"exceeds maximum allowable size (" + Twine(UINT32_MAX) + ")");
- // We must do this before opening the output file, as it depends on being able
- // to read the contents of the existing output file.
- PreviousBuildId = loadExistingBuildId(Config->OutputFile);
openFile(Config->OutputFile);
if (Config->is64()) {
writeHeader<pe32plus_header>();
@@ -357,14 +519,14 @@ void Writer::run() {
}
writeSections();
sortExceptionTable();
- writeBuildId();
T1.stop();
if (!Config->PDBPath.empty() && Config->Debug) {
assert(BuildId);
- createPDB(Symtab, OutputSections, SectionTable, *BuildId->BuildId);
+ createPDB(Symtab, OutputSections, SectionTable, BuildId->BuildId);
}
+ writeBuildId();
writeMapFile(OutputSections);
@@ -396,6 +558,110 @@ static void sortBySectionOrder(std::vector<Chunk *> &Chunks) {
});
}
+// Sort concrete section chunks from GNU import libraries.
+//
+// GNU binutils doesn't use short import files, but instead produces import
+// libraries that consist of object files, with section chunks for the .idata$*
+// sections. These are linked just as regular static libraries. Each import
+// library consists of one header object, one object file for every imported
+// symbol, and one trailer object. In order for the .idata tables/lists to
+// be formed correctly, the section chunks within each .idata$* section need
+// to be grouped by library, and sorted alphabetically within each library
+// (which makes sure the header comes first and the trailer last).
+static bool fixGnuImportChunks(
+ std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> &Map) {
+ uint32_t RDATA = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
+
+ // Make sure all .idata$* section chunks are mapped as RDATA in order to
+ // be sorted into the same sections as our own synthesized .idata chunks.
+ for (auto &Pair : Map) {
+ StringRef SectionName = Pair.first.first;
+ uint32_t OutChars = Pair.first.second;
+ if (!SectionName.startswith(".idata"))
+ continue;
+ if (OutChars == RDATA)
+ continue;
+ std::vector<Chunk *> &SrcVect = Pair.second;
+ std::vector<Chunk *> &DestVect = Map[{SectionName, RDATA}];
+ DestVect.insert(DestVect.end(), SrcVect.begin(), SrcVect.end());
+ SrcVect.clear();
+ }
+
+ bool HasIdata = false;
+ // Sort all .idata$* chunks, grouping chunks from the same library,
+ // with alphabetical ordering of the object fils within a library.
+ for (auto &Pair : Map) {
+ StringRef SectionName = Pair.first.first;
+ if (!SectionName.startswith(".idata"))
+ continue;
+
+ std::vector<Chunk *> &Chunks = Pair.second;
+ if (!Chunks.empty())
+ HasIdata = true;
+ std::stable_sort(Chunks.begin(), Chunks.end(), [&](Chunk *S, Chunk *T) {
+ SectionChunk *SC1 = dyn_cast_or_null<SectionChunk>(S);
+ SectionChunk *SC2 = dyn_cast_or_null<SectionChunk>(T);
+ if (!SC1 || !SC2) {
+ // if SC1, order them ascending. If SC2 or both null,
+ // S is not less than T.
+ return SC1 != nullptr;
+ }
+ // Make a string with "libraryname/objectfile" for sorting, achieving
+ // both grouping by library and sorting of objects within a library,
+ // at once.
+ std::string Key1 =
+ (SC1->File->ParentName + "/" + SC1->File->getName()).str();
+ std::string Key2 =
+ (SC2->File->ParentName + "/" + SC2->File->getName()).str();
+ return Key1 < Key2;
+ });
+ }
+ return HasIdata;
+}
+
+// Add generated idata chunks, for imported symbols and DLLs, and a
+// terminator in .idata$2.
+static void addSyntheticIdata(
+ IdataContents &Idata,
+ std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> &Map) {
+ uint32_t RDATA = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
+ Idata.create();
+
+ // Add the .idata content in the right section groups, to allow
+ // chunks from other linked in object files to be grouped together.
+ // See Microsoft PE/COFF spec 5.4 for details.
+ auto Add = [&](StringRef N, std::vector<Chunk *> &V) {
+ std::vector<Chunk *> &DestVect = Map[{N, RDATA}];
+ DestVect.insert(DestVect.end(), V.begin(), V.end());
+ };
+
+ // The loader assumes a specific order of data.
+ // Add each type in the correct order.
+ Add(".idata$2", Idata.Dirs);
+ Add(".idata$4", Idata.Lookups);
+ Add(".idata$5", Idata.Addresses);
+ Add(".idata$6", Idata.Hints);
+ Add(".idata$7", Idata.DLLNames);
+}
+
+// Locate the first Chunk and size of the import directory list and the
+// IAT.
+void Writer::locateImportTables(
+ std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> &Map) {
+ uint32_t RDATA = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
+ std::vector<Chunk *> &ImportTables = Map[{".idata$2", RDATA}];
+ if (!ImportTables.empty())
+ ImportTableStart = ImportTables.front();
+ for (Chunk *C : ImportTables)
+ ImportTableSize += C->getSize();
+
+ std::vector<Chunk *> &IAT = Map[{".idata$5", RDATA}];
+ if (!IAT.empty())
+ IATStart = IAT.front();
+ for (Chunk *C : IAT)
+ IATSize += C->getSize();
+}
+
// Create output section objects and add them to OutputSections.
void Writer::createSections() {
// First, create the builtin sections.
@@ -429,12 +695,14 @@ void Writer::createSections() {
DidatSec = CreateSection(".didat", DATA | R);
RsrcSec = CreateSection(".rsrc", DATA | R);
RelocSec = CreateSection(".reloc", DATA | DISCARDABLE | R);
+ CtorsSec = CreateSection(".ctors", DATA | R | W);
+ DtorsSec = CreateSection(".dtors", DATA | R | W);
// Then bin chunks by name and output characteristics.
std::map<std::pair<StringRef, uint32_t>, std::vector<Chunk *>> Map;
for (Chunk *C : Symtab->getChunks()) {
auto *SC = dyn_cast<SectionChunk>(C);
- if (SC && !SC->isLive()) {
+ if (SC && !SC->Live) {
if (Config->Verbose)
SC->printDiscardedMessage();
continue;
@@ -442,26 +710,43 @@ void Writer::createSections() {
Map[{C->getSectionName(), C->getOutputCharacteristics()}].push_back(C);
}
+ // Even in non MinGW cases, we might need to link against GNU import
+ // libraries.
+ bool HasIdata = fixGnuImportChunks(Map);
+ if (!Idata.empty())
+ HasIdata = true;
+
+ if (HasIdata)
+ addSyntheticIdata(Idata, Map);
+
// Process an /order option.
if (!Config->Order.empty())
for (auto &Pair : Map)
sortBySectionOrder(Pair.second);
+ if (HasIdata)
+ locateImportTables(Map);
+
// Then create an OutputSection for each section.
// '$' and all following characters in input section names are
// discarded when determining output section. So, .text$foo
// contributes to .text, for example. See PE/COFF spec 3.2.
- for (auto Pair : Map) {
+ for (auto &Pair : Map) {
StringRef Name = getOutputSectionName(Pair.first.first);
uint32_t OutChars = Pair.first.second;
- // In link.exe, there is a special case for the I386 target where .CRT
- // sections are treated as if they have output characteristics DATA | R if
- // their characteristics are DATA | R | W. This implements the same special
- // case for all architectures.
- if (Name == ".CRT")
+ if (Name == ".CRT") {
+ // In link.exe, there is a special case for the I386 target where .CRT
+ // sections are treated as if they have output characteristics DATA | R if
+ // their characteristics are DATA | R | W. This implements the same
+ // special case for all architectures.
OutChars = DATA | R;
+ log("Processing section " + Pair.first.first + " -> " + Name);
+
+ sortCRTSectionChunks(Pair.second);
+ }
+
OutputSection *Sec = CreateSection(Name, OutChars);
std::vector<Chunk *> &Chunks = Pair.second;
for (Chunk *C : Chunks)
@@ -499,20 +784,20 @@ void Writer::createMiscChunks() {
}
// Create Debug Information Chunks
- if (Config->Debug) {
- DebugDirectory = make<DebugDirectoryChunk>(DebugRecords);
-
- OutputSection *DebugInfoSec = Config->MinGW ? BuildidSec : RdataSec;
+ OutputSection *DebugInfoSec = Config->MinGW ? BuildidSec : RdataSec;
+ if (Config->Debug || Config->Repro) {
+ DebugDirectory = make<DebugDirectoryChunk>(DebugRecords, Config->Repro);
+ DebugInfoSec->addChunk(DebugDirectory);
+ }
+ if (Config->Debug) {
// Make a CVDebugRecordChunk even when /DEBUG:CV is not specified. We
// output a PDB no matter what, and this chunk provides the only means of
// allowing a debugger to match a PDB and an executable. So we need it even
// if we're ultimately not going to write CodeView data to the PDB.
- auto *CVChunk = make<CVDebugRecordChunk>();
- BuildId = CVChunk;
- DebugRecords.push_back(CVChunk);
+ BuildId = make<CVDebugRecordChunk>();
+ DebugRecords.push_back(BuildId);
- DebugInfoSec->addChunk(DebugDirectory);
for (Chunk *C : DebugRecords)
DebugInfoSec->addChunk(C);
}
@@ -524,6 +809,12 @@ void Writer::createMiscChunks() {
// Create /guard:cf tables if requested.
if (Config->GuardCF != GuardCFLevel::Off)
createGuardCFTables();
+
+ if (Config->MinGW) {
+ createRuntimePseudoRelocs();
+
+ insertCtorDtorSymbols();
+ }
}
// Create .idata section for the DLL-imported symbol table.
@@ -531,9 +822,6 @@ void Writer::createMiscChunks() {
// IdataContents class abstracted away the details for us,
// so we just let it create chunks and add them to the section.
void Writer::createImportTables() {
- if (ImportFile::Instances.empty())
- return;
-
// Initialize DLLOrder so that import entries are ordered in
// the same order as in the command line. (That affects DLL
// initialization order, and this ordering is MSVC-compatible.)
@@ -545,14 +833,6 @@ void Writer::createImportTables() {
if (Config->DLLOrder.count(DLL) == 0)
Config->DLLOrder[DLL] = Config->DLLOrder.size();
- if (File->ThunkSym) {
- if (!isa<DefinedImportThunk>(File->ThunkSym))
- fatal(toString(*File->ThunkSym) + " was replaced");
- DefinedImportThunk *Thunk = cast<DefinedImportThunk>(File->ThunkSym);
- if (File->ThunkLive)
- TextSec->addChunk(Thunk->getChunk());
- }
-
if (File->ImpSym && !isa<DefinedImportData>(File->ImpSym))
fatal(toString(*File->ImpSym) + " was replaced");
DefinedImportData *ImpSym = cast_or_null<DefinedImportData>(File->ImpSym);
@@ -565,10 +845,25 @@ void Writer::createImportTables() {
Idata.add(ImpSym);
}
}
+}
- if (!Idata.empty())
- for (Chunk *C : Idata.getChunks())
- IdataSec->addChunk(C);
+void Writer::appendImportThunks() {
+ if (ImportFile::Instances.empty())
+ return;
+
+ for (ImportFile *File : ImportFile::Instances) {
+ if (!File->Live)
+ continue;
+
+ if (!File->ThunkSym)
+ continue;
+
+ if (!isa<DefinedImportThunk>(File->ThunkSym))
+ fatal(toString(*File->ThunkSym) + " was replaced");
+ DefinedImportThunk *Thunk = cast<DefinedImportThunk>(File->ThunkSym);
+ if (File->ThunkLive)
+ TextSec->addChunk(Thunk->getChunk());
+ }
if (!DelayIdata.empty()) {
Defined *Helper = cast<Defined>(Config->DelayLoadHelper);
@@ -589,6 +884,21 @@ void Writer::createExportTable() {
EdataSec->addChunk(C);
}
+void Writer::removeUnusedSections() {
+ // Remove sections that we can be sure won't get content, to avoid
+ // allocating space for their section headers.
+ auto IsUnused = [this](OutputSection *S) {
+ if (S == RelocSec)
+ return false; // This section is populated later.
+ // MergeChunks have zero size at this point, as their size is finalized
+ // later. Only remove sections that have no Chunks at all.
+ return S->Chunks.empty();
+ };
+ OutputSections.erase(
+ std::remove_if(OutputSections.begin(), OutputSections.end(), IsUnused),
+ OutputSections.end());
+}
+
// The Windows loader doesn't seem to like empty sections,
// so we remove them if any.
void Writer::removeEmptySections() {
@@ -699,9 +1009,9 @@ void Writer::createSymbolAndStringTable() {
}
void Writer::mergeSections() {
- if (!PdataSec->getChunks().empty()) {
- FirstPdata = PdataSec->getChunks().front();
- LastPdata = PdataSec->getChunks().back();
+ if (!PdataSec->Chunks.empty()) {
+ FirstPdata = PdataSec->Chunks.front();
+ LastPdata = PdataSec->Chunks.back();
}
for (auto &P : Config->Merge) {
@@ -729,11 +1039,18 @@ void Writer::mergeSections() {
}
}
+// Visits all sections to initialize their relocation targets.
+void Writer::readRelocTargets() {
+ for (OutputSection *Sec : OutputSections)
+ for_each(parallel::par, Sec->Chunks.begin(), Sec->Chunks.end(),
+ [&](Chunk *C) { C->readRelocTargets(); });
+}
+
// Visits all sections to assign incremental, non-overlapping RVAs and
// file offsets.
void Writer::assignAddresses() {
SizeOfHeaders = DOSStubSize + sizeof(PEMagic) + sizeof(coff_file_header) +
- sizeof(data_directory) * NumberfOfDataDirectory +
+ sizeof(data_directory) * NumberOfDataDirectory +
sizeof(coff_section) * OutputSections.size();
SizeOfHeaders +=
Config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header);
@@ -746,7 +1063,7 @@ void Writer::assignAddresses() {
addBaserels();
uint64_t RawSize = 0, VirtualSize = 0;
Sec->Header.VirtualAddress = RVA;
- for (Chunk *C : Sec->getChunks()) {
+ for (Chunk *C : Sec->Chunks) {
VirtualSize = alignTo(VirtualSize, C->Alignment);
C->setRVA(RVA + VirtualSize);
C->OutputSectionOff = VirtualSize;
@@ -808,7 +1125,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
if (!Config->Relocatable)
COFF->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED;
COFF->SizeOfOptionalHeader =
- sizeof(PEHeaderTy) + sizeof(data_directory) * NumberfOfDataDirectory;
+ sizeof(PEHeaderTy) + sizeof(data_directory) * NumberOfDataDirectory;
// Write PE header
auto *PE = reinterpret_cast<PEHeaderTy *>(Buf);
@@ -866,7 +1183,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_SEH;
if (Config->TerminalServerAware)
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE;
- PE->NumberOfRvaAndSize = NumberfOfDataDirectory;
+ PE->NumberOfRvaAndSize = NumberOfDataDirectory;
if (TextSec->getVirtualSize()) {
PE->BaseOfCode = TextSec->getRVA();
PE->SizeOfCode = TextSec->getRawSize();
@@ -875,16 +1192,18 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
// Write data directory
auto *Dir = reinterpret_cast<data_directory *>(Buf);
- Buf += sizeof(*Dir) * NumberfOfDataDirectory;
+ Buf += sizeof(*Dir) * NumberOfDataDirectory;
if (!Config->Exports.empty()) {
Dir[EXPORT_TABLE].RelativeVirtualAddress = Edata.getRVA();
Dir[EXPORT_TABLE].Size = Edata.getSize();
}
- if (!Idata.empty()) {
- Dir[IMPORT_TABLE].RelativeVirtualAddress = Idata.getDirRVA();
- Dir[IMPORT_TABLE].Size = Idata.getDirSize();
- Dir[IAT].RelativeVirtualAddress = Idata.getIATRVA();
- Dir[IAT].Size = Idata.getIATSize();
+ if (ImportTableStart) {
+ Dir[IMPORT_TABLE].RelativeVirtualAddress = ImportTableStart->getRVA();
+ Dir[IMPORT_TABLE].Size = ImportTableSize;
+ }
+ if (IATStart) {
+ Dir[IAT].RelativeVirtualAddress = IATStart->getRVA();
+ Dir[IAT].Size = IATSize;
}
if (RsrcSec->getVirtualSize()) {
Dir[RESOURCE_TABLE].RelativeVirtualAddress = RsrcSec->getRVA();
@@ -907,7 +1226,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
: sizeof(object::coff_tls_directory32);
}
}
- if (Config->Debug) {
+ if (DebugDirectory) {
Dir[DEBUG_DIRECTORY].RelativeVirtualAddress = DebugDirectory->getRVA();
Dir[DEBUG_DIRECTORY].Size = DebugDirectory->getSize();
}
@@ -1002,6 +1321,25 @@ static void addSymbolToRVASet(SymbolRVASet &RVASet, Defined *S) {
RVASet.insert({C, Off});
}
+// Given a symbol, add it to the GFIDs table if it is a live, defined, function
+// symbol in an executable section.
+static void maybeAddAddressTakenFunction(SymbolRVASet &AddressTakenSyms,
+ Symbol *S) {
+ auto *D = dyn_cast_or_null<DefinedCOFF>(S);
+
+ // Ignore undefined symbols and references to non-functions (e.g. globals and
+ // labels).
+ if (!D ||
+ D->getCOFFSymbol().getComplexType() != COFF::IMAGE_SYM_DTYPE_FUNCTION)
+ return;
+
+ // Mark the symbol as address taken if it's in an executable section.
+ Chunk *RefChunk = D->getChunk();
+ OutputSection *OS = RefChunk ? RefChunk->getOutputSection() : nullptr;
+ if (OS && OS->Header.Characteristics & IMAGE_SCN_MEM_EXECUTE)
+ addSymbolToRVASet(AddressTakenSyms, D);
+}
+
// Visit all relocations from all section contributions of this object file and
// mark the relocation target as address-taken.
static void markSymbolsWithRelocations(ObjFile *File,
@@ -1010,21 +1348,17 @@ static void markSymbolsWithRelocations(ObjFile *File,
// We only care about live section chunks. Common chunks and other chunks
// don't generally contain relocations.
SectionChunk *SC = dyn_cast<SectionChunk>(C);
- if (!SC || !SC->isLive())
+ if (!SC || !SC->Live)
continue;
- // Look for relocations in this section against symbols in executable output
- // sections.
- for (Symbol *Ref : SC->symbols()) {
- // FIXME: Do further testing to see if the relocation type matters,
- // especially for 32-bit where taking the address of something usually
- // uses an absolute relocation instead of a relative one.
- if (auto *D = dyn_cast_or_null<Defined>(Ref)) {
- Chunk *RefChunk = D->getChunk();
- OutputSection *OS = RefChunk ? RefChunk->getOutputSection() : nullptr;
- if (OS && OS->Header.Characteristics & IMAGE_SCN_MEM_EXECUTE)
- addSymbolToRVASet(UsedSymbols, D);
- }
+ for (const coff_relocation &Reloc : SC->Relocs) {
+ if (Config->Machine == I386 && Reloc.Type == COFF::IMAGE_REL_I386_REL32)
+ // Ignore relative relocations on x86. On x86_64 they can't be ignored
+ // since they're also used to compute absolute addresses.
+ continue;
+
+ Symbol *Ref = SC->File->getSymbol(Reloc.SymbolTableIndex);
+ maybeAddAddressTakenFunction(UsedSymbols, Ref);
}
}
}
@@ -1051,7 +1385,11 @@ void Writer::createGuardCFTables() {
// Mark the image entry as address-taken.
if (Config->Entry)
- addSymbolToRVASet(AddressTakenSyms, cast<Defined>(Config->Entry));
+ maybeAddAddressTakenFunction(AddressTakenSyms, Config->Entry);
+
+ // Mark exported symbols in executable sections as address-taken.
+ for (Export &E : Config->Exports)
+ maybeAddAddressTakenFunction(AddressTakenSyms, E.Sym);
// Ensure sections referenced in the gfid table are 16-byte aligned.
for (const ChunkAndOffset &C : AddressTakenSyms)
@@ -1087,7 +1425,7 @@ void Writer::markSymbolsForRVATable(ObjFile *File,
// is associated with something like a vtable and the vtable is discarded.
// In this case, the associated gfids section is discarded, and we don't
// mark the virtual member functions as address-taken by the vtable.
- if (!C->isLive())
+ if (!C->Live)
continue;
// Validate that the contents look like symbol table indices.
@@ -1134,6 +1472,56 @@ void Writer::maybeAddRVATable(SymbolRVASet TableSymbols, StringRef TableSym,
cast<DefinedAbsolute>(C)->setVA(TableChunk->getSize() / 4);
}
+// MinGW specific. Gather all relocations that are imported from a DLL even
+// though the code didn't expect it to, produce the table that the runtime
+// uses for fixing them up, and provide the synthetic symbols that the
+// runtime uses for finding the table.
+void Writer::createRuntimePseudoRelocs() {
+ std::vector<RuntimePseudoReloc> Rels;
+
+ for (Chunk *C : Symtab->getChunks()) {
+ auto *SC = dyn_cast<SectionChunk>(C);
+ if (!SC || !SC->Live)
+ continue;
+ SC->getRuntimePseudoRelocs(Rels);
+ }
+
+ if (!Rels.empty())
+ log("Writing " + Twine(Rels.size()) + " runtime pseudo relocations");
+ PseudoRelocTableChunk *Table = make<PseudoRelocTableChunk>(Rels);
+ RdataSec->addChunk(Table);
+ EmptyChunk *EndOfList = make<EmptyChunk>();
+ RdataSec->addChunk(EndOfList);
+
+ Symbol *HeadSym = Symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST__");
+ Symbol *EndSym = Symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST_END__");
+ replaceSymbol<DefinedSynthetic>(HeadSym, HeadSym->getName(), Table);
+ replaceSymbol<DefinedSynthetic>(EndSym, EndSym->getName(), EndOfList);
+}
+
+// MinGW specific.
+// The MinGW .ctors and .dtors lists have sentinels at each end;
+// a (uintptr_t)-1 at the start and a (uintptr_t)0 at the end.
+// There's a symbol pointing to the start sentinel pointer, __CTOR_LIST__
+// and __DTOR_LIST__ respectively.
+void Writer::insertCtorDtorSymbols() {
+ AbsolutePointerChunk *CtorListHead = make<AbsolutePointerChunk>(-1);
+ AbsolutePointerChunk *CtorListEnd = make<AbsolutePointerChunk>(0);
+ AbsolutePointerChunk *DtorListHead = make<AbsolutePointerChunk>(-1);
+ AbsolutePointerChunk *DtorListEnd = make<AbsolutePointerChunk>(0);
+ CtorsSec->insertChunkAtStart(CtorListHead);
+ CtorsSec->addChunk(CtorListEnd);
+ DtorsSec->insertChunkAtStart(DtorListHead);
+ DtorsSec->addChunk(DtorListEnd);
+
+ Symbol *CtorListSym = Symtab->findUnderscore("__CTOR_LIST__");
+ Symbol *DtorListSym = Symtab->findUnderscore("__DTOR_LIST__");
+ replaceSymbol<DefinedSynthetic>(CtorListSym, CtorListSym->getName(),
+ CtorListHead);
+ replaceSymbol<DefinedSynthetic>(DtorListSym, DtorListSym->getName(),
+ DtorListHead);
+}
+
// Handles /section options to allow users to overwrite
// section attributes.
void Writer::setSectionPermissions() {
@@ -1160,7 +1548,7 @@ void Writer::writeSections() {
// ADD instructions).
if (Sec->Header.Characteristics & IMAGE_SCN_CNT_CODE)
memset(SecBuf, 0xCC, Sec->getRawSize());
- for_each(parallel::par, Sec->getChunks().begin(), Sec->getChunks().end(),
+ for_each(parallel::par, Sec->Chunks.begin(), Sec->Chunks.end(),
[&](Chunk *C) { C->writeTo(SecBuf); });
}
}
@@ -1171,25 +1559,10 @@ void Writer::writeBuildId() {
// timestamp as well as a Guid and Age of the PDB.
// 2) In all cases, the PE COFF file header also contains a timestamp.
// For reproducibility, instead of a timestamp we want to use a hash of the
- // binary, however when building with debug info the hash needs to take into
- // account the debug info, since it's possible to add blank lines to a file
- // which causes the debug info to change but not the generated code.
- //
- // To handle this, we first set the Guid and Age in the debug directory (but
- // only if we're doing a debug build). Then, we hash the binary (thus causing
- // the hash to change if only the debug info changes, since the Age will be
- // different). Finally, we write that hash into the debug directory (if
- // present) as well as the COFF file header (always).
+ // PE contents.
if (Config->Debug) {
assert(BuildId && "BuildId is not set!");
- if (PreviousBuildId.hasValue()) {
- *BuildId->BuildId = *PreviousBuildId;
- BuildId->BuildId->PDB70.Age = BuildId->BuildId->PDB70.Age + 1;
- } else {
- BuildId->BuildId->Signature.CVSignature = OMF::Signature::PDB70;
- BuildId->BuildId->PDB70.Age = 1;
- llvm::getRandomBytes(BuildId->BuildId->PDB70.Signature, 16);
- }
+ // BuildId->BuildId was filled in when the PDB was written.
}
// At this point the only fields in the COFF file which remain unset are the
@@ -1201,8 +1574,25 @@ void Writer::writeBuildId() {
Buffer->getBufferSize());
uint32_t Timestamp = Config->Timestamp;
+ uint64_t Hash = 0;
+ bool GenerateSyntheticBuildId =
+ Config->MinGW && Config->Debug && Config->PDBPath.empty();
+
+ if (Config->Repro || GenerateSyntheticBuildId)
+ Hash = xxHash64(OutputFileData);
+
if (Config->Repro)
- Timestamp = static_cast<uint32_t>(xxHash64(OutputFileData));
+ Timestamp = static_cast<uint32_t>(Hash);
+
+ if (GenerateSyntheticBuildId) {
+ // For MinGW builds without a PDB file, we still generate a build id
+ // to allow associating a crash dump to the executable.
+ BuildId->BuildId->PDB70.CVSignature = OMF::Signature::PDB70;
+ BuildId->BuildId->PDB70.Age = 1;
+ memcpy(BuildId->BuildId->PDB70.Signature, &Hash, 8);
+ // xxhash only gives us 8 bytes, so put some fixed data in the other half.
+ memcpy(&BuildId->BuildId->PDB70.Signature[8], "LLD PDB.", 8);
+ }
if (DebugDirectory)
DebugDirectory->setTimeDateStamp(Timestamp);
@@ -1240,6 +1630,42 @@ void Writer::sortExceptionTable() {
errs() << "warning: don't know how to handle .pdata.\n";
}
+// The CRT section contains, among other things, the array of function
+// pointers that initialize every global variable that is not trivially
+// constructed. The CRT calls them one after the other prior to invoking
+// main().
+//
+// As per C++ spec, 3.6.2/2.3,
+// "Variables with ordered initialization defined within a single
+// translation unit shall be initialized in the order of their definitions
+// in the translation unit"
+//
+// It is therefore critical to sort the chunks containing the function
+// pointers in the order that they are listed in the object file (top to
+// bottom), otherwise global objects might not be initialized in the
+// correct order.
+void Writer::sortCRTSectionChunks(std::vector<Chunk *> &Chunks) {
+ auto SectionChunkOrder = [](const Chunk *A, const Chunk *B) {
+ auto SA = dyn_cast<SectionChunk>(A);
+ auto SB = dyn_cast<SectionChunk>(B);
+ assert(SA && SB && "Non-section chunks in CRT section!");
+
+ StringRef SAObj = SA->File->MB.getBufferIdentifier();
+ StringRef SBObj = SB->File->MB.getBufferIdentifier();
+
+ return SAObj == SBObj && SA->getSectionNumber() < SB->getSectionNumber();
+ };
+ std::stable_sort(Chunks.begin(), Chunks.end(), SectionChunkOrder);
+
+ if (Config->Verbose) {
+ for (auto &C : Chunks) {
+ auto SC = dyn_cast<SectionChunk>(C);
+ log(" " + SC->File->MB.getBufferIdentifier().str() +
+ ", SectionID: " + Twine(SC->getSectionNumber()));
+ }
+ }
+}
+
OutputSection *Writer::findSection(StringRef Name) {
for (OutputSection *Sec : OutputSections)
if (Sec->Name == Name)
@@ -1259,12 +1685,13 @@ uint32_t Writer::getSizeOfInitializedData() {
void Writer::addBaserels() {
if (!Config->Relocatable)
return;
+ RelocSec->Chunks.clear();
std::vector<Baserel> V;
for (OutputSection *Sec : OutputSections) {
if (Sec->Header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
continue;
// Collect all locations for base relocations.
- for (Chunk *C : Sec->getChunks())
+ for (Chunk *C : Sec->Chunks)
C->getBaserels(&V);
// Add the addresses to .reloc section.
if (!V.empty())
diff --git a/COFF/Writer.h b/COFF/Writer.h
index d37276cb6d91..727582480c91 100644
--- a/COFF/Writer.h
+++ b/COFF/Writer.h
@@ -34,8 +34,8 @@ public:
Header.Characteristics = Chars;
}
void addChunk(Chunk *C);
+ void insertChunkAtStart(Chunk *C);
void merge(OutputSection *Other);
- ArrayRef<Chunk *> getChunks() { return Chunks; }
void addPermissions(uint32_t C);
void setPermissions(uint32_t C);
uint64_t getRVA() { return Header.VirtualAddress; }
@@ -62,9 +62,11 @@ public:
llvm::StringRef Name;
llvm::object::coff_section Header = {};
+ std::vector<Chunk *> Chunks;
+ std::vector<Chunk *> OrigChunks;
+
private:
uint32_t StringTableOff = 0;
- std::vector<Chunk *> Chunks;
};
}
diff --git a/Common/Args.cpp b/Common/Args.cpp
index ff77bfcc3b76..3f0671d72a66 100644
--- a/Common/Args.cpp
+++ b/Common/Args.cpp
@@ -13,6 +13,7 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Option/ArgList.h"
+#include "llvm/Support/Path.h"
using namespace llvm;
using namespace lld;
@@ -40,7 +41,7 @@ std::vector<StringRef> lld::args::getStrings(opt::InputArgList &Args, int Id) {
uint64_t lld::args::getZOptionValue(opt::InputArgList &Args, int Id,
StringRef Key, uint64_t Default) {
- for (auto *Arg : Args.filtered(Id)) {
+ for (auto *Arg : Args.filtered_reverse(Id)) {
std::pair<StringRef, StringRef> KV = StringRef(Arg->getValue()).split('=');
if (KV.first == Key) {
uint64_t Result = Default;
@@ -64,3 +65,9 @@ std::vector<StringRef> lld::args::getLines(MemoryBufferRef MB) {
}
return Ret;
}
+
+StringRef lld::args::getFilenameWithoutExe(StringRef Path) {
+ if (Path.endswith_lower(".exe"))
+ return sys::path::stem(Path);
+ return sys::path::filename(Path);
+}
diff --git a/Common/ErrorHandler.cpp b/Common/ErrorHandler.cpp
index d1cb3dbbe03c..c059516daf94 100644
--- a/Common/ErrorHandler.cpp
+++ b/Common/ErrorHandler.cpp
@@ -47,8 +47,9 @@ ErrorHandler &lld::errorHandler() {
}
void lld::exitLld(int Val) {
- // Delete the output buffer so that any tempory file is deleted.
- errorHandler().OutputBuffer.reset();
+ // Delete any temporary file, while keeping the memory mapping open.
+ if (errorHandler().OutputBuffer)
+ errorHandler().OutputBuffer->discard();
// Dealloc/destroy ManagedStatic variables before calling
// _exit(). In a non-LTO build, this is a nop. In an LTO
diff --git a/Common/Strings.cpp b/Common/Strings.cpp
index 36f4f77d8476..6f74865b7f42 100644
--- a/Common/Strings.cpp
+++ b/Common/Strings.cpp
@@ -16,14 +16,6 @@
#include <mutex>
#include <vector>
-#if defined(_MSC_VER)
-#include <Windows.h>
-
-// DbgHelp.h must be included after Windows.h.
-#include <DbgHelp.h>
-#pragma comment(lib, "dbghelp.lib")
-#endif
-
using namespace llvm;
using namespace lld;
@@ -45,18 +37,21 @@ Optional<std::string> lld::demangleItanium(StringRef Name) {
return S;
}
-Optional<std::string> lld::demangleMSVC(StringRef S) {
-#if defined(_MSC_VER)
- // UnDecorateSymbolName is not thread-safe, so we need a mutex.
- static std::mutex Mu;
- std::lock_guard<std::mutex> Lock(Mu);
+Optional<std::string> lld::demangleMSVC(StringRef Name) {
+ std::string Prefix;
+ if (Name.consume_front("__imp_"))
+ Prefix = "__declspec(dllimport) ";
+
+ // Demangle only C++ names.
+ if (!Name.startswith("?"))
+ return None;
- char Buf[4096];
- if (S.startswith("?"))
- if (size_t Len = UnDecorateSymbolName(S.str().c_str(), Buf, sizeof(Buf), 0))
- return std::string(Buf, Len);
-#endif
- return None;
+ char *Buf = microsoftDemangle(Name.str().c_str(), nullptr, nullptr, nullptr);
+ if (!Buf)
+ return None;
+ std::string S(Buf);
+ free(Buf);
+ return Prefix + S;
}
StringMatcher::StringMatcher(ArrayRef<StringRef> Pat) {
diff --git a/Common/TargetOptionsCommandFlags.cpp b/Common/TargetOptionsCommandFlags.cpp
index b46df363c361..7a3fc510704f 100644
--- a/Common/TargetOptionsCommandFlags.cpp
+++ b/Common/TargetOptionsCommandFlags.cpp
@@ -32,3 +32,4 @@ llvm::Optional<llvm::CodeModel::Model> lld::GetCodeModelFromCMModel() {
}
std::string lld::GetCPUStr() { return ::getCPUStr(); }
+std::vector<std::string> lld::GetMAttrs() { return ::MAttrs; }
diff --git a/ELF/AArch64ErrataFix.cpp b/ELF/AArch64ErrataFix.cpp
index 7551919cf86f..ac753cb58265 100644
--- a/ELF/AArch64ErrataFix.cpp
+++ b/ELF/AArch64ErrataFix.cpp
@@ -356,7 +356,7 @@ static uint64_t scanCortexA53Errata843419(InputSection *IS, uint64_t &Off,
}
uint64_t PatchOff = 0;
- const uint8_t *Buf = IS->Data.begin();
+ const uint8_t *Buf = IS->data().begin();
const ulittle32_t *InstBuf = reinterpret_cast<const ulittle32_t *>(Buf + Off);
uint32_t Instr1 = *InstBuf++;
uint32_t Instr2 = *InstBuf++;
@@ -411,7 +411,7 @@ uint64_t lld::elf::Patch843419Section::getLDSTAddr() const {
void lld::elf::Patch843419Section::writeTo(uint8_t *Buf) {
// Copy the instruction that we will be replacing with a branch in the
// Patchee Section.
- write32le(Buf, read32le(Patchee->Data.begin() + PatcheeOffset));
+ write32le(Buf, read32le(Patchee->data().begin() + PatcheeOffset));
// Apply any relocation transferred from the original PatcheeSection.
// For a SyntheticSection Buf already has OutSecOff added, but relocateAlloc
@@ -451,7 +451,7 @@ void AArch64Err843419Patcher::init() {
continue;
if (!IsCodeMapSymbol(Def) && !IsDataMapSymbol(Def))
continue;
- if (auto *Sec = dyn_cast<InputSection>(Def->Section))
+ if (auto *Sec = dyn_cast_or_null<InputSection>(Def->Section))
if (Sec->Flags & SHF_EXECINSTR)
SectionMap[Sec].push_back(Def);
}
@@ -487,7 +487,8 @@ void AArch64Err843419Patcher::insertPatches(
InputSectionDescription &ISD, std::vector<Patch843419Section *> &Patches) {
uint64_t ISLimit;
uint64_t PrevISLimit = ISD.Sections.front()->OutSecOff;
- uint64_t PatchUpperBound = PrevISLimit + Target->ThunkSectionSpacing;
+ uint64_t PatchUpperBound = PrevISLimit + Target->getThunkSectionSpacing();
+ uint64_t OutSecAddr = ISD.Sections.front()->getParent()->Addr;
// Set the OutSecOff of patches to the place where we want to insert them.
// We use a similar strategy to Thunk placement. Place patches roughly
@@ -498,12 +499,12 @@ void AArch64Err843419Patcher::insertPatches(
ISLimit = IS->OutSecOff + IS->getSize();
if (ISLimit > PatchUpperBound) {
while (PatchIt != PatchEnd) {
- if ((*PatchIt)->getLDSTAddr() >= PrevISLimit)
+ if ((*PatchIt)->getLDSTAddr() - OutSecAddr >= PrevISLimit)
break;
(*PatchIt)->OutSecOff = PrevISLimit;
++PatchIt;
}
- PatchUpperBound = PrevISLimit + Target->ThunkSectionSpacing;
+ PatchUpperBound = PrevISLimit + Target->getThunkSectionSpacing();
}
PrevISLimit = ISLimit;
}
@@ -538,20 +539,24 @@ static void implementPatch(uint64_t AdrpAddr, uint64_t PatcheeOffset,
InputSection *IS,
std::vector<Patch843419Section *> &Patches) {
// There may be a relocation at the same offset that we are patching. There
- // are three cases that we need to consider.
+ // are four cases that we need to consider.
// Case 1: R_AARCH64_JUMP26 branch relocation. We have already patched this
// instance of the erratum on a previous patch and altered the relocation. We
// have nothing more to do.
- // Case 2: A load/store register (unsigned immediate) class relocation. There
+ // Case 2: A TLS Relaxation R_RELAX_TLS_IE_TO_LE. In this case the ADRP that
+ // we read will be transformed into a MOVZ later so we actually don't match
+ // the sequence and have nothing more to do.
+ // Case 3: A load/store register (unsigned immediate) class relocation. There
// are two of these R_AARCH_LD64_ABS_LO12_NC and R_AARCH_LD64_GOT_LO12_NC and
// they are both absolute. We need to add the same relocation to the patch,
// and replace the relocation with a R_AARCH_JUMP26 branch relocation.
- // Case 3: No relocation. We must create a new R_AARCH64_JUMP26 branch
+ // Case 4: No relocation. We must create a new R_AARCH64_JUMP26 branch
// relocation at the offset.
auto RelIt = std::find_if(
IS->Relocations.begin(), IS->Relocations.end(),
[=](const Relocation &R) { return R.Offset == PatcheeOffset; });
- if (RelIt != IS->Relocations.end() && RelIt->Type == R_AARCH64_JUMP26)
+ if (RelIt != IS->Relocations.end() &&
+ (RelIt->Type == R_AARCH64_JUMP26 || RelIt->Expr == R_RELAX_TLS_IE_TO_LE))
return;
log("detected cortex-a53-843419 erratum sequence starting at " +
@@ -598,7 +603,7 @@ AArch64Err843419Patcher::patchInputSectionDescription(
auto DataSym = std::next(CodeSym);
uint64_t Off = (*CodeSym)->Value;
uint64_t Limit =
- (DataSym == MapSyms.end()) ? IS->Data.size() : (*DataSym)->Value;
+ (DataSym == MapSyms.end()) ? IS->data().size() : (*DataSym)->Value;
while (Off < Limit) {
uint64_t StartAddr = IS->getVA(Off);
diff --git a/ELF/Arch/AArch64.cpp b/ELF/Arch/AArch64.cpp
index c7b3c0801de2..08ffe2a08c0f 100644
--- a/ELF/Arch/AArch64.cpp
+++ b/ELF/Arch/AArch64.cpp
@@ -41,6 +41,7 @@ public:
int32_t Index, unsigned RelOff) const override;
bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
uint64_t BranchAddr, const Symbol &S) const override;
+ uint32_t getThunkSectionSpacing() const override;
bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override;
bool usesOnlyLowPageBits(RelType Type) const override;
void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
@@ -57,6 +58,7 @@ AArch64::AArch64() {
RelativeRel = R_AARCH64_RELATIVE;
IRelativeRel = R_AARCH64_IRELATIVE;
GotRel = R_AARCH64_GLOB_DAT;
+ NoneRel = R_AARCH64_NONE;
PltRel = R_AARCH64_JUMP_SLOT;
TlsDescRel = R_AARCH64_TLSDESC;
TlsGotRel = R_AARCH64_TLS_TPREL64;
@@ -66,22 +68,18 @@ AArch64::AArch64() {
PltHeaderSize = 32;
DefaultMaxPageSize = 65536;
- // It doesn't seem to be documented anywhere, but tls on aarch64 uses variant
- // 1 of the tls structures and the tcb size is 16.
- TcbSize = 16;
- NeedsThunks = true;
+ // Align to the 2 MiB page size (known as a superpage or huge page).
+ // FreeBSD automatically promotes 2 MiB-aligned allocations.
+ DefaultImageBase = 0x200000;
- // See comment in Arch/ARM.cpp for a more detailed explanation of
- // ThunkSectionSpacing. For AArch64 the only branches we are permitted to
- // Thunk have a range of +/- 128 MiB
- ThunkSectionSpacing = (128 * 1024 * 1024) - 0x30000;
+ NeedsThunks = true;
}
RelExpr AArch64::getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const {
switch (Type) {
case R_AARCH64_TLSDESC_ADR_PAGE21:
- return R_TLSDESC_PAGE;
+ return R_AARCH64_TLSDESC_PAGE;
case R_AARCH64_TLSDESC_LD64_LO12:
case R_AARCH64_TLSDESC_ADD_LO12:
return R_TLSDESC;
@@ -107,13 +105,13 @@ RelExpr AArch64::getRelExpr(RelType Type, const Symbol &S,
case R_AARCH64_LD_PREL_LO19:
return R_PC;
case R_AARCH64_ADR_PREL_PG_HI21:
- return R_PAGE_PC;
+ return R_AARCH64_PAGE_PC;
case R_AARCH64_LD64_GOT_LO12_NC:
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
return R_GOT;
case R_AARCH64_ADR_GOT_PAGE:
case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
- return R_GOT_PAGE_PC;
+ return R_AARCH64_GOT_PAGE_PC;
case R_AARCH64_NONE:
return R_NONE;
default:
@@ -125,7 +123,7 @@ RelExpr AArch64::adjustRelaxExpr(RelType Type, const uint8_t *Data,
RelExpr Expr) const {
if (Expr == R_RELAX_TLS_GD_TO_IE) {
if (Type == R_AARCH64_TLSDESC_ADR_PAGE21)
- return R_RELAX_TLS_GD_TO_IE_PAGE_PC;
+ return R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC;
return R_RELAX_TLS_GD_TO_IE_ABS;
}
return Expr;
@@ -156,7 +154,7 @@ RelType AArch64::getDynRel(RelType Type) const {
}
void AArch64::writeGotPlt(uint8_t *Buf, const Symbol &) const {
- write64le(Buf, InX::Plt->getVA());
+ write64le(Buf, In.Plt->getVA());
}
void AArch64::writePltHeader(uint8_t *Buf) const {
@@ -172,8 +170,8 @@ void AArch64::writePltHeader(uint8_t *Buf) const {
};
memcpy(Buf, PltData, sizeof(PltData));
- uint64_t Got = InX::GotPlt->getVA();
- uint64_t Plt = InX::Plt->getVA();
+ uint64_t Got = In.GotPlt->getVA();
+ uint64_t Plt = In.Plt->getVA();
relocateOne(Buf + 4, R_AARCH64_ADR_PREL_PG_HI21,
getAArch64Page(Got + 16) - getAArch64Page(Plt + 4));
relocateOne(Buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, Got + 16);
@@ -208,6 +206,13 @@ bool AArch64::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
return !inBranchRange(Type, BranchAddr, Dst);
}
+uint32_t AArch64::getThunkSectionSpacing() const {
+ // See comment in Arch/ARM.cpp for a more detailed explanation of
+ // getThunkSectionSpacing(). For AArch64 the only branches we are permitted to
+ // Thunk have a range of +/- 128 MiB
+ return (128 * 1024 * 1024) - 0x30000;
+}
+
bool AArch64::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
if (Type != R_AARCH64_CALL26 && Type != R_AARCH64_JUMP26)
return true;
@@ -338,7 +343,7 @@ void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
or32le(Loc, (Val & 0xFFFC) << 3);
break;
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
- checkInt(Loc, Val, 24, Type);
+ checkUInt(Loc, Val, 24, Type);
or32AArch64Imm(Loc, Val >> 12);
break;
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
diff --git a/ELF/Arch/AMDGPU.cpp b/ELF/Arch/AMDGPU.cpp
index 48b27f23510c..a7c6c84ceecd 100644
--- a/ELF/Arch/AMDGPU.cpp
+++ b/ELF/Arch/AMDGPU.cpp
@@ -35,6 +35,7 @@ public:
AMDGPU::AMDGPU() {
RelativeRel = R_AMDGPU_RELATIVE64;
GotRel = R_AMDGPU_ABS64;
+ NoneRel = R_AMDGPU_NONE;
GotEntrySize = 8;
}
diff --git a/ELF/Arch/ARM.cpp b/ELF/Arch/ARM.cpp
index acf9a615f20b..120caca671af 100644
--- a/ELF/Arch/ARM.cpp
+++ b/ELF/Arch/ARM.cpp
@@ -40,6 +40,7 @@ public:
void addPltHeaderSymbols(InputSection &ISD) const override;
bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
uint64_t BranchAddr, const Symbol &S) const override;
+ uint32_t getThunkSectionSpacing() const override;
bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override;
void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
};
@@ -50,6 +51,7 @@ ARM::ARM() {
RelativeRel = R_ARM_RELATIVE;
IRelativeRel = R_ARM_IRELATIVE;
GotRel = R_ARM_GLOB_DAT;
+ NoneRel = R_ARM_NONE;
PltRel = R_ARM_JUMP_SLOT;
TlsGotRel = R_ARM_TLS_TPOFF32;
TlsModuleIndexRel = R_ARM_TLS_DTPMOD32;
@@ -59,41 +61,8 @@ ARM::ARM() {
GotPltEntrySize = 4;
PltEntrySize = 16;
PltHeaderSize = 32;
- TrapInstr = 0xd4d4d4d4;
- // ARM uses Variant 1 TLS
- TcbSize = 8;
+ TrapInstr = {0xd4, 0xd4, 0xd4, 0xd4};
NeedsThunks = true;
-
- // The placing of pre-created ThunkSections is controlled by the
- // ThunkSectionSpacing parameter. The aim is to place the
- // ThunkSection such that all branches from the InputSections prior to the
- // ThunkSection can reach a Thunk placed at the end of the ThunkSection.
- // Graphically:
- // | up to ThunkSectionSpacing .text input sections |
- // | ThunkSection |
- // | up to ThunkSectionSpacing .text input sections |
- // | ThunkSection |
-
- // Pre-created ThunkSections are spaced roughly 16MiB apart on ARM. This is to
- // match the most common expected case of a Thumb 2 encoded BL, BLX or B.W
- // ARM B, BL, BLX range +/- 32MiB
- // Thumb B.W, BL, BLX range +/- 16MiB
- // Thumb B<cc>.W range +/- 1MiB
- // If a branch cannot reach a pre-created ThunkSection a new one will be
- // created so we can handle the rare cases of a Thumb 2 conditional branch.
- // We intentionally use a lower size for ThunkSectionSpacing than the maximum
- // branch range so the end of the ThunkSection is more likely to be within
- // range of the branch instruction that is furthest away. The value we shorten
- // ThunkSectionSpacing by is set conservatively to allow us to create 16,384
- // 12 byte Thunks at any offset in a ThunkSection without risk of a branch to
- // one of the Thunks going out of range.
-
- // FIXME: lld assumes that the Thumb BL and BLX encoding permits the J1 and
- // J2 bits to be used to extend the branch range. On earlier Architectures
- // such as ARMv4, ARMv5 and ARMv6 (except ARMv6T2) the range is +/- 4MiB. If
- // support for the earlier encodings is added then when they are used the
- // ThunkSectionSpacing will need lowering.
- ThunkSectionSpacing = 0x1000000 - 0x30000;
}
uint32_t ARM::calcEFlags() const {
@@ -165,6 +134,12 @@ RelExpr ARM::getRelExpr(RelType Type, const Symbol &S,
return R_NONE;
case R_ARM_TLS_LE32:
return R_TLS;
+ case R_ARM_V4BX:
+ // V4BX is just a marker to indicate there's a "bx rN" instruction at the
+ // given address. It can be used to implement a special linker mode which
+ // rewrites ARMv4T inputs to ARMv4. Since we support only ARMv4 input and
+ // not ARMv4 output, we can just ignore it.
+ return R_HINT;
default:
return R_ABS;
}
@@ -177,7 +152,7 @@ RelType ARM::getDynRel(RelType Type) const {
}
void ARM::writeGotPlt(uint8_t *Buf, const Symbol &) const {
- write32le(Buf, InX::Plt->getVA());
+ write32le(Buf, In.Plt->getVA());
}
void ARM::writeIgotPlt(uint8_t *Buf, const Symbol &S) const {
@@ -198,8 +173,8 @@ static void writePltHeaderLong(uint8_t *Buf) {
0xd4, 0xd4, 0xd4, 0xd4, // Pad to 32-byte boundary
0xd4, 0xd4, 0xd4, 0xd4};
memcpy(Buf, PltData, sizeof(PltData));
- uint64_t GotPlt = InX::GotPlt->getVA();
- uint64_t L1 = InX::Plt->getVA() + 8;
+ uint64_t GotPlt = In.GotPlt->getVA();
+ uint64_t L1 = In.Plt->getVA() + 8;
write32le(Buf + 16, GotPlt - L1 - 8);
}
@@ -217,7 +192,7 @@ void ARM::writePltHeader(uint8_t *Buf) const {
0xe5bef000, // ldr pc, [lr, #0x00000NNN] &(.got.plt -L1 - 4)
};
- uint64_t Offset = InX::GotPlt->getVA() - InX::Plt->getVA() - 4;
+ uint64_t Offset = In.GotPlt->getVA() - In.Plt->getVA() - 4;
if (!llvm::isUInt<27>(Offset)) {
// We cannot encode the Offset, use the long form.
writePltHeaderLong(Buf);
@@ -227,10 +202,10 @@ void ARM::writePltHeader(uint8_t *Buf) const {
write32le(Buf + 4, PltData[1] | ((Offset >> 20) & 0xff));
write32le(Buf + 8, PltData[2] | ((Offset >> 12) & 0xff));
write32le(Buf + 12, PltData[3] | (Offset & 0xfff));
- write32le(Buf + 16, TrapInstr); // Pad to 32-byte boundary
- write32le(Buf + 20, TrapInstr);
- write32le(Buf + 24, TrapInstr);
- write32le(Buf + 28, TrapInstr);
+ memcpy(Buf + 16, TrapInstr.data(), 4); // Pad to 32-byte boundary
+ memcpy(Buf + 20, TrapInstr.data(), 4);
+ memcpy(Buf + 24, TrapInstr.data(), 4);
+ memcpy(Buf + 28, TrapInstr.data(), 4);
}
void ARM::addPltHeaderSymbols(InputSection &IS) const {
@@ -279,7 +254,7 @@ void ARM::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
write32le(Buf + 0, PltData[0] | ((Offset >> 20) & 0xff));
write32le(Buf + 4, PltData[1] | ((Offset >> 12) & 0xff));
write32le(Buf + 8, PltData[2] | (Offset & 0xfff));
- write32le(Buf + 12, TrapInstr); // Pad to 16-byte boundary
+ memcpy(Buf + 12, TrapInstr.data(), 4); // Pad to 16-byte boundary
}
void ARM::addPltSymbols(InputSection &IS, uint64_t Off) const {
@@ -324,6 +299,40 @@ bool ARM::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
return false;
}
+uint32_t ARM::getThunkSectionSpacing() const {
+ // The placing of pre-created ThunkSections is controlled by the value
+ // ThunkSectionSpacing returned by getThunkSectionSpacing(). The aim is to
+ // place the ThunkSection such that all branches from the InputSections
+ // prior to the ThunkSection can reach a Thunk placed at the end of the
+ // ThunkSection. Graphically:
+ // | up to ThunkSectionSpacing .text input sections |
+ // | ThunkSection |
+ // | up to ThunkSectionSpacing .text input sections |
+ // | ThunkSection |
+
+ // Pre-created ThunkSections are spaced roughly 16MiB apart on ARMv7. This
+ // is to match the most common expected case of a Thumb 2 encoded BL, BLX or
+ // B.W:
+ // ARM B, BL, BLX range +/- 32MiB
+ // Thumb B.W, BL, BLX range +/- 16MiB
+ // Thumb B<cc>.W range +/- 1MiB
+ // If a branch cannot reach a pre-created ThunkSection a new one will be
+ // created so we can handle the rare cases of a Thumb 2 conditional branch.
+ // We intentionally use a lower size for ThunkSectionSpacing than the maximum
+ // branch range so the end of the ThunkSection is more likely to be within
+ // range of the branch instruction that is furthest away. The value we shorten
+ // ThunkSectionSpacing by is set conservatively to allow us to create 16,384
+ // 12 byte Thunks at any offset in a ThunkSection without risk of a branch to
+ // one of the Thunks going out of range.
+
+ // On Arm the ThunkSectionSpacing depends on the range of the Thumb Branch
+ // range. On earlier Architectures such as ARMv4, ARMv5 and ARMv6 (except
+ // ARMv6T2) the range is +/- 4MiB.
+
+ return (Config->ARMJ1J2BranchEncoding) ? 0x1000000 - 0x30000
+ : 0x400000 - 0x7500;
+}
+
bool ARM::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
uint64_t Range;
uint64_t InstrSize;
@@ -342,7 +351,7 @@ bool ARM::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
break;
case R_ARM_THM_JUMP24:
case R_ARM_THM_CALL:
- Range = 0x1000000;
+ Range = Config->ARMJ1J2BranchEncoding ? 0x1000000 : 0x400000;
InstrSize = 2;
break;
default:
@@ -447,11 +456,23 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
}
// Bit 12 is 0 for BLX, 1 for BL
write16le(Loc + 2, (read16le(Loc + 2) & ~0x1000) | (Val & 1) << 12);
+ if (!Config->ARMJ1J2BranchEncoding) {
+ // Older Arm architectures do not support R_ARM_THM_JUMP24 and have
+ // different encoding rules and range due to J1 and J2 always being 1.
+ checkInt(Loc, Val, 23, Type);
+ write16le(Loc,
+ 0xf000 | // opcode
+ ((Val >> 12) & 0x07ff)); // imm11
+ write16le(Loc + 2,
+ (read16le(Loc + 2) & 0xd000) | // opcode
+ 0x2800 | // J1 == J2 == 1
+ ((Val >> 1) & 0x07ff)); // imm11
+ break;
+ }
// Fall through as rest of encoding is the same as B.W
LLVM_FALLTHROUGH;
case R_ARM_THM_JUMP24:
// Encoding B T4, BL T1, BLX T2: Val = S:I1:I2:imm10:imm11:0
- // FIXME: Use of I1 and I2 require v6T2ops
checkInt(Loc, Val, 25, Type);
write16le(Loc,
0xf000 | // opcode
@@ -470,14 +491,12 @@ void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
break;
case R_ARM_MOVT_ABS:
case R_ARM_MOVT_PREL:
- checkInt(Loc, Val, 32, Type);
write32le(Loc, (read32le(Loc) & ~0x000f0fff) |
(((Val >> 16) & 0xf000) << 4) | ((Val >> 16) & 0xfff));
break;
case R_ARM_THM_MOVT_ABS:
case R_ARM_THM_MOVT_PREL:
// Encoding T1: A = imm4:i:imm3:imm8
- checkInt(Loc, Val, 32, Type);
write16le(Loc,
0xf2c0 | // opcode
((Val >> 17) & 0x0400) | // i
@@ -542,10 +561,19 @@ int64_t ARM::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
((Lo & 0x07ff) << 1)); // imm11:0
}
case R_ARM_THM_CALL:
+ if (!Config->ARMJ1J2BranchEncoding) {
+ // Older Arm architectures do not support R_ARM_THM_JUMP24 and have
+ // different encoding rules and range due to J1 and J2 always being 1.
+ uint16_t Hi = read16le(Buf);
+ uint16_t Lo = read16le(Buf + 2);
+ return SignExtend64<22>(((Hi & 0x7ff) << 12) | // imm11
+ ((Lo & 0x7ff) << 1)); // imm11:0
+ break;
+ }
+ LLVM_FALLTHROUGH;
case R_ARM_THM_JUMP24: {
// Encoding B T4, BL T1, BLX T2: A = S:I1:I2:imm10:imm11:0
// I1 = NOT(J1 EOR S), I2 = NOT(J2 EOR S)
- // FIXME: I1 and I2 require v6T2ops
uint16_t Hi = read16le(Buf);
uint16_t Lo = read16le(Buf + 2);
return SignExtend64<24>(((Hi & 0x0400) << 14) | // S
diff --git a/ELF/Arch/AVR.cpp b/ELF/Arch/AVR.cpp
index 02ac770127b9..637da3778bd2 100644
--- a/ELF/Arch/AVR.cpp
+++ b/ELF/Arch/AVR.cpp
@@ -43,12 +43,15 @@ using namespace lld::elf;
namespace {
class AVR final : public TargetInfo {
public:
+ AVR();
RelExpr getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const override;
void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
};
} // namespace
+AVR::AVR() { NoneRel = R_AVR_NONE; }
+
RelExpr AVR::getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const {
return R_ABS;
diff --git a/ELF/Arch/Hexagon.cpp b/ELF/Arch/Hexagon.cpp
index ff5e862bafa2..b4d33be2ad39 100644
--- a/ELF/Arch/Hexagon.cpp
+++ b/ELF/Arch/Hexagon.cpp
@@ -9,6 +9,7 @@
#include "InputFiles.h"
#include "Symbols.h"
+#include "SyntheticSections.h"
#include "Target.h"
#include "lld/Common/ErrorHandler.h"
#include "llvm/BinaryFormat/ELF.h"
@@ -25,15 +26,48 @@ using namespace lld::elf;
namespace {
class Hexagon final : public TargetInfo {
public:
+ Hexagon();
uint32_t calcEFlags() const override;
RelExpr getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const override;
void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ void writePltHeader(uint8_t *Buf) const override;
+ void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
+ int32_t Index, unsigned RelOff) const override;
};
} // namespace
-// Support V60 only at the moment.
-uint32_t Hexagon::calcEFlags() const { return 0x60; }
+Hexagon::Hexagon() {
+ PltRel = R_HEX_JMP_SLOT;
+ RelativeRel = R_HEX_RELATIVE;
+ GotRel = R_HEX_GLOB_DAT;
+ GotEntrySize = 4;
+ // The zero'th GOT entry is reserved for the address of _DYNAMIC. The
+ // next 3 are reserved for the dynamic loader.
+ GotPltHeaderEntriesNum = 4;
+ GotPltEntrySize = 4;
+
+ PltEntrySize = 16;
+ PltHeaderSize = 32;
+
+ // Hexagon Linux uses 64K pages by default.
+ DefaultMaxPageSize = 0x10000;
+ NoneRel = R_HEX_NONE;
+}
+
+uint32_t Hexagon::calcEFlags() const {
+ assert(!ObjectFiles.empty());
+
+ // The architecture revision must always be equal to or greater than
+ // greatest revision in the list of inputs.
+ uint32_t Ret = 0;
+ for (InputFile *F : ObjectFiles) {
+ uint32_t EFlags = cast<ObjFile<ELF32LE>>(F)->getObj().getHeader()->e_flags;
+ if (EFlags > Ret)
+ Ret = EFlags;
+ }
+ return Ret;
+}
static uint32_t applyMask(uint32_t Mask, uint32_t Data) {
uint32_t Result = 0;
@@ -53,29 +87,143 @@ static uint32_t applyMask(uint32_t Mask, uint32_t Data) {
RelExpr Hexagon::getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const {
switch (Type) {
+ case R_HEX_B9_PCREL:
+ case R_HEX_B9_PCREL_X:
+ case R_HEX_B13_PCREL:
case R_HEX_B15_PCREL:
case R_HEX_B15_PCREL_X:
+ case R_HEX_6_PCREL_X:
+ case R_HEX_32_PCREL:
+ return R_PC;
case R_HEX_B22_PCREL:
+ case R_HEX_PLT_B22_PCREL:
case R_HEX_B22_PCREL_X:
case R_HEX_B32_PCREL_X:
- return R_PC;
+ return R_PLT_PC;
+ case R_HEX_GOT_11_X:
+ case R_HEX_GOT_16_X:
+ case R_HEX_GOT_32_6_X:
+ return R_HEXAGON_GOT;
default:
return R_ABS;
}
}
+static uint32_t findMaskR6(uint32_t Insn) {
+ // There are (arguably too) many relocation masks for the DSP's
+ // R_HEX_6_X type. The table below is used to select the correct mask
+ // for the given instruction.
+ struct InstructionMask {
+ uint32_t CmpMask;
+ uint32_t RelocMask;
+ };
+
+ static const InstructionMask R6[] = {
+ {0x38000000, 0x0000201f}, {0x39000000, 0x0000201f},
+ {0x3e000000, 0x00001f80}, {0x3f000000, 0x00001f80},
+ {0x40000000, 0x000020f8}, {0x41000000, 0x000007e0},
+ {0x42000000, 0x000020f8}, {0x43000000, 0x000007e0},
+ {0x44000000, 0x000020f8}, {0x45000000, 0x000007e0},
+ {0x46000000, 0x000020f8}, {0x47000000, 0x000007e0},
+ {0x6a000000, 0x00001f80}, {0x7c000000, 0x001f2000},
+ {0x9a000000, 0x00000f60}, {0x9b000000, 0x00000f60},
+ {0x9c000000, 0x00000f60}, {0x9d000000, 0x00000f60},
+ {0x9f000000, 0x001f0100}, {0xab000000, 0x0000003f},
+ {0xad000000, 0x0000003f}, {0xaf000000, 0x00030078},
+ {0xd7000000, 0x006020e0}, {0xd8000000, 0x006020e0},
+ {0xdb000000, 0x006020e0}, {0xdf000000, 0x006020e0}};
+
+ // Duplex forms have a fixed mask and parse bits 15:14 are always
+ // zero. Non-duplex insns will always have at least one bit set in the
+ // parse field.
+ if ((0xC000 & Insn) == 0x0)
+ return 0x03f00000;
+
+ for (InstructionMask I : R6)
+ if ((0xff000000 & Insn) == I.CmpMask)
+ return I.RelocMask;
+
+ error("unrecognized instruction for R_HEX_6 relocation: 0x" +
+ utohexstr(Insn));
+ return 0;
+}
+
+static uint32_t findMaskR8(uint32_t Insn) {
+ if ((0xff000000 & Insn) == 0xde000000)
+ return 0x00e020e8;
+ if ((0xff000000 & Insn) == 0x3c000000)
+ return 0x0000207f;
+ return 0x00001fe0;
+}
+
+static uint32_t findMaskR11(uint32_t Insn) {
+ if ((0xff000000 & Insn) == 0xa1000000)
+ return 0x060020ff;
+ return 0x06003fe0;
+}
+
+static uint32_t findMaskR16(uint32_t Insn) {
+ if ((0xff000000 & Insn) == 0x48000000)
+ return 0x061f20ff;
+ if ((0xff000000 & Insn) == 0x49000000)
+ return 0x061f3fe0;
+ if ((0xff000000 & Insn) == 0x78000000)
+ return 0x00df3fe0;
+ if ((0xff000000 & Insn) == 0xb0000000)
+ return 0x0fe03fe0;
+
+ error("unrecognized instruction for R_HEX_16_X relocation: 0x" +
+ utohexstr(Insn));
+ return 0;
+}
+
static void or32le(uint8_t *P, int32_t V) { write32le(P, read32le(P) | V); }
void Hexagon::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
switch (Type) {
case R_HEX_NONE:
break;
+ case R_HEX_6_PCREL_X:
+ case R_HEX_6_X:
+ or32le(Loc, applyMask(findMaskR6(read32le(Loc)), Val));
+ break;
+ case R_HEX_8_X:
+ or32le(Loc, applyMask(findMaskR8(read32le(Loc)), Val));
+ break;
+ case R_HEX_9_X:
+ or32le(Loc, applyMask(0x00003fe0, Val & 0x3f));
+ break;
+ case R_HEX_10_X:
+ or32le(Loc, applyMask(0x00203fe0, Val & 0x3f));
+ break;
+ case R_HEX_11_X:
+ case R_HEX_GOT_11_X:
+ or32le(Loc, applyMask(findMaskR11(read32le(Loc)), Val & 0x3f));
+ break;
case R_HEX_12_X:
or32le(Loc, applyMask(0x000007e0, Val));
break;
+ case R_HEX_16_X: // These relocs only have 6 effective bits.
+ case R_HEX_GOT_16_X:
+ or32le(Loc, applyMask(findMaskR16(read32le(Loc)), Val & 0x3f));
+ break;
+ case R_HEX_32:
+ case R_HEX_32_PCREL:
+ or32le(Loc, Val);
+ break;
case R_HEX_32_6_X:
+ case R_HEX_GOT_32_6_X:
or32le(Loc, applyMask(0x0fff3fff, Val >> 6));
break;
+ case R_HEX_B9_PCREL:
+ or32le(Loc, applyMask(0x003000fe, Val >> 2));
+ break;
+ case R_HEX_B9_PCREL_X:
+ or32le(Loc, applyMask(0x003000fe, Val & 0x3f));
+ break;
+ case R_HEX_B13_PCREL:
+ or32le(Loc, applyMask(0x00202ffe, Val >> 2));
+ break;
case R_HEX_B15_PCREL:
or32le(Loc, applyMask(0x00df20fe, Val >> 2));
break;
@@ -83,6 +231,7 @@ void Hexagon::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
or32le(Loc, applyMask(0x00df20fe, Val & 0x3f));
break;
case R_HEX_B22_PCREL:
+ case R_HEX_PLT_B22_PCREL:
or32le(Loc, applyMask(0x1ff3ffe, Val >> 2));
break;
case R_HEX_B22_PCREL_X:
@@ -91,12 +240,52 @@ void Hexagon::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_HEX_B32_PCREL_X:
or32le(Loc, applyMask(0x0fff3fff, Val >> 6));
break;
+ case R_HEX_HI16:
+ or32le(Loc, applyMask(0x00c03fff, Val >> 16));
+ break;
+ case R_HEX_LO16:
+ or32le(Loc, applyMask(0x00c03fff, Val));
+ break;
default:
error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type));
break;
}
}
+void Hexagon::writePltHeader(uint8_t *Buf) const {
+ const uint8_t PltData[] = {
+ 0x00, 0x40, 0x00, 0x00, // { immext (#0)
+ 0x1c, 0xc0, 0x49, 0x6a, // r28 = add (pc, ##GOT0@PCREL) } # @GOT0
+ 0x0e, 0x42, 0x9c, 0xe2, // { r14 -= add (r28, #16) # offset of GOTn
+ 0x4f, 0x40, 0x9c, 0x91, // r15 = memw (r28 + #8) # object ID at GOT2
+ 0x3c, 0xc0, 0x9c, 0x91, // r28 = memw (r28 + #4) }# dynamic link at GOT1
+ 0x0e, 0x42, 0x0e, 0x8c, // { r14 = asr (r14, #2) # index of PLTn
+ 0x00, 0xc0, 0x9c, 0x52, // jumpr r28 } # call dynamic linker
+ 0x0c, 0xdb, 0x00, 0x54, // trap0(#0xdb) # bring plt0 into 16byte alignment
+ };
+ memcpy(Buf, PltData, sizeof(PltData));
+
+ // Offset from PLT0 to the GOT.
+ uint64_t Off = In.GotPlt->getVA() - In.Plt->getVA();
+ relocateOne(Buf, R_HEX_B32_PCREL_X, Off);
+ relocateOne(Buf + 4, R_HEX_6_PCREL_X, Off);
+}
+
+void Hexagon::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
+ uint64_t PltEntryAddr, int32_t Index,
+ unsigned RelOff) const {
+ const uint8_t Inst[] = {
+ 0x00, 0x40, 0x00, 0x00, // { immext (#0)
+ 0x0e, 0xc0, 0x49, 0x6a, // r14 = add (pc, ##GOTn@PCREL) }
+ 0x1c, 0xc0, 0x8e, 0x91, // r28 = memw (r14)
+ 0x00, 0xc0, 0x9c, 0x52, // jumpr r28
+ };
+ memcpy(Buf, Inst, sizeof(Inst));
+
+ relocateOne(Buf, R_HEX_B32_PCREL_X, GotPltEntryAddr - PltEntryAddr);
+ relocateOne(Buf + 4, R_HEX_6_PCREL_X, GotPltEntryAddr - PltEntryAddr);
+}
+
TargetInfo *elf::getHexagonTargetInfo() {
static Hexagon Target;
return &Target;
diff --git a/ELF/Arch/MSP430.cpp b/ELF/Arch/MSP430.cpp
new file mode 100644
index 000000000000..fe0c0fe64daf
--- /dev/null
+++ b/ELF/Arch/MSP430.cpp
@@ -0,0 +1,94 @@
+//===- MSP430.cpp ---------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// The MSP430 is a 16-bit microcontroller RISC architecture. The instruction set
+// has only 27 core instructions orthogonally augmented with a variety
+// of addressing modes for source and destination operands. Entire address space
+// of MSP430 is 64KB (the extended MSP430X architecture is not considered here).
+// A typical MSP430 MCU has several kilobytes of RAM and ROM, plenty
+// of peripherals and is generally optimized for a low power consumption.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InputFiles.h"
+#include "Symbols.h"
+#include "Target.h"
+#include "lld/Common/ErrorHandler.h"
+#include "llvm/Object/ELF.h"
+#include "llvm/Support/Endian.h"
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace llvm::support::endian;
+using namespace llvm::ELF;
+using namespace lld;
+using namespace lld::elf;
+
+namespace {
+class MSP430 final : public TargetInfo {
+public:
+ MSP430();
+ RelExpr getRelExpr(RelType Type, const Symbol &S,
+ const uint8_t *Loc) const override;
+ void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+};
+} // namespace
+
+MSP430::MSP430() {
+ // mov.b #0, r3
+ TrapInstr = {0x43, 0x43, 0x43, 0x43};
+}
+
+RelExpr MSP430::getRelExpr(RelType Type, const Symbol &S,
+ const uint8_t *Loc) const {
+ switch (Type) {
+ case R_MSP430_10_PCREL:
+ case R_MSP430_16_PCREL:
+ case R_MSP430_16_PCREL_BYTE:
+ case R_MSP430_2X_PCREL:
+ case R_MSP430_RL_PCREL:
+ case R_MSP430_SYM_DIFF:
+ return R_PC;
+ default:
+ return R_ABS;
+ }
+}
+
+void MSP430::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
+ switch (Type) {
+ case R_MSP430_8:
+ checkIntUInt(Loc, Val, 8, Type);
+ *Loc = Val;
+ break;
+ case R_MSP430_16:
+ case R_MSP430_16_PCREL:
+ case R_MSP430_16_BYTE:
+ case R_MSP430_16_PCREL_BYTE:
+ checkIntUInt(Loc, Val, 16, Type);
+ write16le(Loc, Val);
+ break;
+ case R_MSP430_32:
+ checkIntUInt(Loc, Val, 32, Type);
+ write32le(Loc, Val);
+ break;
+ case R_MSP430_10_PCREL: {
+ int16_t Offset = ((int16_t)Val >> 1) - 1;
+ checkInt(Loc, Offset, 10, Type);
+ write16le(Loc, (read16le(Loc) & 0xFC00) | (Offset & 0x3FF));
+ break;
+ }
+ default:
+ error(getErrorLocation(Loc) + "unrecognized reloc " + toString(Type));
+ }
+}
+
+TargetInfo *elf::getMSP430TargetInfo() {
+ static MSP430 Target;
+ return &Target;
+}
diff --git a/ELF/Arch/Mips.cpp b/ELF/Arch/Mips.cpp
index dc70401c0b0e..23b0c1dd8a2d 100644
--- a/ELF/Arch/Mips.cpp
+++ b/ELF/Arch/Mips.cpp
@@ -53,9 +53,12 @@ template <class ELFT> MIPS<ELFT>::MIPS() {
PltEntrySize = 16;
PltHeaderSize = 32;
CopyRel = R_MIPS_COPY;
+ NoneRel = R_MIPS_NONE;
PltRel = R_MIPS_JUMP_SLOT;
NeedsThunks = true;
- TrapInstr = 0xefefefef;
+
+ // Set `sigrie 1` as a trap instruction.
+ write32(TrapInstr.data(), 0x04170001);
if (ELFT::Is64Bits) {
RelativeRel = (R_MIPS_64 << 8) | R_MIPS_REL32;
@@ -185,7 +188,7 @@ template <class ELFT> RelType MIPS<ELFT>::getDynRel(RelType Type) const {
template <class ELFT>
void MIPS<ELFT>::writeGotPlt(uint8_t *Buf, const Symbol &) const {
- uint64_t VA = InX::Plt->getVA();
+ uint64_t VA = In.Plt->getVA();
if (isMicroMips())
VA |= 1;
write32<ELFT::TargetEndianness>(Buf, VA);
@@ -239,8 +242,8 @@ static void writeMicroRelocation16(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *Buf) const {
const endianness E = ELFT::TargetEndianness;
if (isMicroMips()) {
- uint64_t GotPlt = InX::GotPlt->getVA();
- uint64_t Plt = InX::Plt->getVA();
+ uint64_t GotPlt = In.GotPlt->getVA();
+ uint64_t Plt = In.Plt->getVA();
// Overwrite trap instructions written by Writer::writeTrapInstr.
memset(Buf, 0, PltHeaderSize);
@@ -292,7 +295,7 @@ template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *Buf) const {
write32<E>(Buf + 24, JalrInst); // jalr.hb $25 or jalr $25
write32<E>(Buf + 28, 0x2718fffe); // subu $24, $24, 2
- uint64_t GotPlt = InX::GotPlt->getVA();
+ uint64_t GotPlt = In.GotPlt->getVA();
writeValue<E>(Buf, GotPlt + 0x8000, 16, 16);
writeValue<E>(Buf + 4, GotPlt, 16, 0);
writeValue<E>(Buf + 8, GotPlt, 16, 0);
diff --git a/ELF/Arch/PPC.cpp b/ELF/Arch/PPC.cpp
index 20cae0e59cf4..767378067341 100644
--- a/ELF/Arch/PPC.cpp
+++ b/ELF/Arch/PPC.cpp
@@ -29,6 +29,7 @@ public:
} // namespace
PPC::PPC() {
+ NoneRel = R_PPC_NONE;
GotBaseSymOff = 0x8000;
GotBaseSymInGotPlt = false;
}
@@ -36,6 +37,7 @@ PPC::PPC() {
RelExpr PPC::getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const {
switch (Type) {
+ case R_PPC_REL14:
case R_PPC_REL24:
case R_PPC_REL32:
return R_PC;
@@ -61,6 +63,9 @@ void PPC::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_PPC_REL32:
write32be(Loc, Val);
break;
+ case R_PPC_REL14:
+ write32be(Loc, read32be(Loc) | (Val & 0xFFFC));
+ break;
case R_PPC_PLTREL24:
case R_PPC_REL24:
write32be(Loc, read32be(Loc) | (Val & 0x3FFFFFC));
diff --git a/ELF/Arch/PPC64.cpp b/ELF/Arch/PPC64.cpp
index fa3bf6c62a0d..8a320c9a4e9e 100644
--- a/ELF/Arch/PPC64.cpp
+++ b/ELF/Arch/PPC64.cpp
@@ -23,12 +23,49 @@ using namespace lld::elf;
static uint64_t PPC64TocOffset = 0x8000;
static uint64_t DynamicThreadPointerOffset = 0x8000;
+// The instruction encoding of bits 21-30 from the ISA for the Xform and Dform
+// instructions that can be used as part of the initial exec TLS sequence.
+enum XFormOpcd {
+ LBZX = 87,
+ LHZX = 279,
+ LWZX = 23,
+ LDX = 21,
+ STBX = 215,
+ STHX = 407,
+ STWX = 151,
+ STDX = 149,
+ ADD = 266,
+};
+
+enum DFormOpcd {
+ LBZ = 34,
+ LBZU = 35,
+ LHZ = 40,
+ LHZU = 41,
+ LHAU = 43,
+ LWZ = 32,
+ LWZU = 33,
+ LFSU = 49,
+ LD = 58,
+ LFDU = 51,
+ STB = 38,
+ STBU = 39,
+ STH = 44,
+ STHU = 45,
+ STW = 36,
+ STWU = 37,
+ STFSU = 53,
+ STFDU = 55,
+ STD = 62,
+ ADDI = 14
+};
+
uint64_t elf::getPPC64TocBase() {
// The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The
// TOC starts where the first of these sections starts. We always create a
// .got when we see a relocation that uses it, so for us the start is always
// the .got.
- uint64_t TocVA = InX::Got->getVA();
+ uint64_t TocVA = In.Got->getVA();
// Per the ppc64-elf-linux ABI, The TOC base is TOC value plus 0x8000
// thus permitting a full 64 Kbytes segment. Note that the glibc startup
@@ -37,6 +74,31 @@ uint64_t elf::getPPC64TocBase() {
return TocVA + PPC64TocOffset;
}
+unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t StOther) {
+ // The offset is encoded into the 3 most significant bits of the st_other
+ // field, with some special values described in section 3.4.1 of the ABI:
+ // 0 --> Zero offset between the GEP and LEP, and the function does NOT use
+ // the TOC pointer (r2). r2 will hold the same value on returning from
+ // the function as it did on entering the function.
+ // 1 --> Zero offset between the GEP and LEP, and r2 should be treated as a
+ // caller-saved register for all callers.
+ // 2-6 --> The binary logarithm of the offset eg:
+ // 2 --> 2^2 = 4 bytes --> 1 instruction.
+ // 6 --> 2^6 = 64 bytes --> 16 instructions.
+ // 7 --> Reserved.
+ uint8_t GepToLep = (StOther >> 5) & 7;
+ if (GepToLep < 2)
+ return 0;
+
+ // The value encoded in the st_other bits is the
+ // log-base-2(offset).
+ if (GepToLep < 7)
+ return 1 << GepToLep;
+
+ error("reserved value of 7 in the 3 most-significant-bits of st_other");
+ return 0;
+}
+
namespace {
class PPC64 final : public TargetInfo {
public:
@@ -51,11 +113,16 @@ public:
void writeGotHeader(uint8_t *Buf) const override;
bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
uint64_t BranchAddr, const Symbol &S) const override;
+ bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override;
RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data,
RelExpr Expr) const override;
void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+
+ bool adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End,
+ uint8_t StOther) const override;
};
} // namespace
@@ -71,8 +138,64 @@ static uint16_t highera(uint64_t V) { return (V + 0x8000) >> 32; }
static uint16_t highest(uint64_t V) { return V >> 48; }
static uint16_t highesta(uint64_t V) { return (V + 0x8000) >> 48; }
+// Extracts the 'PO' field of an instruction encoding.
+static uint8_t getPrimaryOpCode(uint32_t Encoding) { return (Encoding >> 26); }
+
+static bool isDQFormInstruction(uint32_t Encoding) {
+ switch (getPrimaryOpCode(Encoding)) {
+ default:
+ return false;
+ case 56:
+ // The only instruction with a primary opcode of 56 is `lq`.
+ return true;
+ case 61:
+ // There are both DS and DQ instruction forms with this primary opcode.
+ // Namely `lxv` and `stxv` are the DQ-forms that use it.
+ // The DS 'XO' bits being set to 01 is restricted to DQ form.
+ return (Encoding & 3) == 0x1;
+ }
+}
+
+static bool isInstructionUpdateForm(uint32_t Encoding) {
+ switch (getPrimaryOpCode(Encoding)) {
+ default:
+ return false;
+ case LBZU:
+ case LHAU:
+ case LHZU:
+ case LWZU:
+ case LFSU:
+ case LFDU:
+ case STBU:
+ case STHU:
+ case STWU:
+ case STFSU:
+ case STFDU:
+ return true;
+ // LWA has the same opcode as LD, and the DS bits is what differentiates
+ // between LD/LDU/LWA
+ case LD:
+ case STD:
+ return (Encoding & 3) == 1;
+ }
+}
+
+// There are a number of places when we either want to read or write an
+// instruction when handling a half16 relocation type. On big-endian the buffer
+// pointer is pointing into the middle of the word we want to extract, and on
+// little-endian it is pointing to the start of the word. These 2 helpers are to
+// simplify reading and writing in that context.
+static void writeInstrFromHalf16(uint8_t *Loc, uint32_t Instr) {
+ write32(Loc - (Config->EKind == ELF64BEKind ? 2 : 0), Instr);
+}
+
+static uint32_t readInstrFromHalf16(const uint8_t *Loc) {
+ return read32(Loc - (Config->EKind == ELF64BEKind ? 2 : 0));
+}
+
PPC64::PPC64() {
GotRel = R_PPC64_GLOB_DAT;
+ NoneRel = R_PPC64_NONE;
PltRel = R_PPC64_JMP_SLOT;
RelativeRel = R_PPC64_RELATIVE;
IRelativeRel = R_PPC64_IRELATIVE;
@@ -85,14 +208,14 @@ PPC64::PPC64() {
GotPltHeaderEntriesNum = 2;
PltHeaderSize = 60;
NeedsThunks = true;
- TcbSize = 8;
- TlsTpOffset = 0x7000;
TlsModuleIndexRel = R_PPC64_DTPMOD64;
TlsOffsetRel = R_PPC64_DTPREL64;
TlsGotRel = R_PPC64_TPREL64;
+ NeedsMoreStackNonSplit = false;
+
// We need 64K pages (at least under glibc/Linux, the loader won't
// set different permissions on a finer granularity than that).
DefaultMaxPageSize = 65536;
@@ -107,8 +230,7 @@ PPC64::PPC64() {
// use 0x10000000 as the starting address.
DefaultImageBase = 0x10000000;
- TrapInstr =
- (Config->IsLE == sys::IsLittleEndianHost) ? 0x7fe00008 : 0x0800e07f;
+ write32(TrapInstr.data(), 0x7fe00008);
}
static uint32_t getEFlags(InputFile *File) {
@@ -146,27 +268,29 @@ void PPC64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// bl __tls_get_addr(x@tlsgd) into nop
// nop into addi r3, r3, x@tprel@l
- uint32_t EndianOffset = Config->EKind == ELF64BEKind ? 2U : 0U;
-
switch (Type) {
case R_PPC64_GOT_TLSGD16_HA:
- write32(Loc - EndianOffset, 0x60000000); // nop
+ writeInstrFromHalf16(Loc, 0x60000000); // nop
break;
+ case R_PPC64_GOT_TLSGD16:
case R_PPC64_GOT_TLSGD16_LO:
- write32(Loc - EndianOffset, 0x3c6d0000); // addis r3, r13
+ writeInstrFromHalf16(Loc, 0x3c6d0000); // addis r3, r13
relocateOne(Loc, R_PPC64_TPREL16_HA, Val);
break;
case R_PPC64_TLSGD:
write32(Loc, 0x60000000); // nop
write32(Loc + 4, 0x38630000); // addi r3, r3
- relocateOne(Loc + 4 + EndianOffset, R_PPC64_TPREL16_LO, Val);
+ // Since we are relocating a half16 type relocation and Loc + 4 points to
+ // the start of an instruction we need to advance the buffer by an extra
+ // 2 bytes on BE.
+ relocateOne(Loc + 4 + (Config->EKind == ELF64BEKind ? 2 : 0),
+ R_PPC64_TPREL16_LO, Val);
break;
default:
llvm_unreachable("unsupported relocation for TLS GD to LE relaxation");
}
}
-
void PPC64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// Reference: 3.7.4.3 of the 64-bit ELF V2 abi supplement.
// The local dynamic code sequence for a global `x` will look like:
@@ -183,13 +307,12 @@ void PPC64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
// bl __tls_get_addr(x@tlsgd) into nop
// nop into addi r3, r3, 4096
- uint32_t EndianOffset = Config->EKind == ELF64BEKind ? 2U : 0U;
switch (Type) {
case R_PPC64_GOT_TLSLD16_HA:
- write32(Loc - EndianOffset, 0x60000000); // nop
+ writeInstrFromHalf16(Loc, 0x60000000); // nop
break;
case R_PPC64_GOT_TLSLD16_LO:
- write32(Loc - EndianOffset, 0x3c6d0000); // addis r3, r13, 0
+ writeInstrFromHalf16(Loc, 0x3c6d0000); // addis r3, r13, 0
break;
case R_PPC64_TLSLD:
write32(Loc, 0x60000000); // nop
@@ -212,9 +335,90 @@ void PPC64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
}
}
+static unsigned getDFormOp(unsigned SecondaryOp) {
+ switch (SecondaryOp) {
+ case LBZX:
+ return LBZ;
+ case LHZX:
+ return LHZ;
+ case LWZX:
+ return LWZ;
+ case LDX:
+ return LD;
+ case STBX:
+ return STB;
+ case STHX:
+ return STH;
+ case STWX:
+ return STW;
+ case STDX:
+ return STD;
+ case ADD:
+ return ADDI;
+ default:
+ error("unrecognized instruction for IE to LE R_PPC64_TLS");
+ return 0;
+ }
+}
+
+void PPC64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
+ // The initial exec code sequence for a global `x` will look like:
+ // Instruction Relocation Symbol
+ // addis r9, r2, x@got@tprel@ha R_PPC64_GOT_TPREL16_HA x
+ // ld r9, x@got@tprel@l(r9) R_PPC64_GOT_TPREL16_LO_DS x
+ // add r9, r9, x@tls R_PPC64_TLS x
+
+ // Relaxing to local exec entails converting:
+ // addis r9, r2, x@got@tprel@ha into nop
+ // ld r9, x@got@tprel@l(r9) into addis r9, r13, x@tprel@ha
+ // add r9, r9, x@tls into addi r9, r9, x@tprel@l
+
+ // x@tls R_PPC64_TLS is a relocation which does not compute anything,
+ // it is replaced with r13 (thread pointer).
+
+ // The add instruction in the initial exec sequence has multiple variations
+ // that need to be handled. If we are building an address it will use an add
+ // instruction, if we are accessing memory it will use any of the X-form
+ // indexed load or store instructions.
+
+ unsigned Offset = (Config->EKind == ELF64BEKind) ? 2 : 0;
+ switch (Type) {
+ case R_PPC64_GOT_TPREL16_HA:
+ write32(Loc - Offset, 0x60000000); // nop
+ break;
+ case R_PPC64_GOT_TPREL16_LO_DS:
+ case R_PPC64_GOT_TPREL16_DS: {
+ uint32_t RegNo = read32(Loc - Offset) & 0x03E00000; // bits 6-10
+ write32(Loc - Offset, 0x3C0D0000 | RegNo); // addis RegNo, r13
+ relocateOne(Loc, R_PPC64_TPREL16_HA, Val);
+ break;
+ }
+ case R_PPC64_TLS: {
+ uint32_t PrimaryOp = getPrimaryOpCode(read32(Loc));
+ if (PrimaryOp != 31)
+ error("unrecognized instruction for IE to LE R_PPC64_TLS");
+ uint32_t SecondaryOp = (read32(Loc) & 0x000007FE) >> 1; // bits 21-30
+ uint32_t DFormOp = getDFormOp(SecondaryOp);
+ write32(Loc, ((DFormOp << 26) | (read32(Loc) & 0x03FFFFFF)));
+ relocateOne(Loc + Offset, R_PPC64_TPREL16_LO, Val);
+ break;
+ }
+ default:
+ llvm_unreachable("unknown relocation for IE to LE");
+ break;
+ }
+}
+
RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const {
switch (Type) {
+ case R_PPC64_GOT16:
+ case R_PPC64_GOT16_DS:
+ case R_PPC64_GOT16_HA:
+ case R_PPC64_GOT16_HI:
+ case R_PPC64_GOT16_LO:
+ case R_PPC64_GOT16_LO_DS:
+ return R_GOT_OFF;
case R_PPC64_TOC16:
case R_PPC64_TOC16_DS:
case R_PPC64_TOC16_HA:
@@ -224,6 +428,7 @@ RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S,
return R_GOTREL;
case R_PPC64_TOC:
return R_PPC_TOC;
+ case R_PPC64_REL14:
case R_PPC64_REL24:
return R_PPC_CALL_PLT;
case R_PPC64_REL16_LO:
@@ -279,7 +484,7 @@ RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S,
case R_PPC64_TLSLD:
return R_TLSLD_HINT;
case R_PPC64_TLS:
- return R_HINT;
+ return R_TLSIE_HINT;
default:
return R_ABS;
}
@@ -308,16 +513,16 @@ void PPC64::writePltHeader(uint8_t *Buf) const {
// The 'bcl' instruction will set the link register to the address of the
// following instruction ('mflr r11'). Here we store the offset from that
// instruction to the first entry in the GotPlt section.
- int64_t GotPltOffset = InX::GotPlt->getVA() - (InX::Plt->getVA() + 8);
+ int64_t GotPltOffset = In.GotPlt->getVA() - (In.Plt->getVA() + 8);
write64(Buf + 52, GotPltOffset);
}
void PPC64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
uint64_t PltEntryAddr, int32_t Index,
unsigned RelOff) const {
- int32_t Offset = PltHeaderSize + Index * PltEntrySize;
- // bl __glink_PLTresolve
- write32(Buf, 0x48000000 | ((-Offset) & 0x03FFFFFc));
+ int32_t Offset = PltHeaderSize + Index * PltEntrySize;
+ // bl __glink_PLTresolve
+ write32(Buf, 0x48000000 | ((-Offset) & 0x03FFFFFc));
}
static std::pair<RelType, uint64_t> toAddr16Rel(RelType Type, uint64_t Val) {
@@ -328,30 +533,36 @@ static std::pair<RelType, uint64_t> toAddr16Rel(RelType Type, uint64_t Val) {
switch (Type) {
// TOC biased relocation.
+ case R_PPC64_GOT16:
case R_PPC64_GOT_TLSGD16:
case R_PPC64_GOT_TLSLD16:
case R_PPC64_TOC16:
return {R_PPC64_ADDR16, TocBiasedVal};
+ case R_PPC64_GOT16_DS:
case R_PPC64_TOC16_DS:
case R_PPC64_GOT_TPREL16_DS:
case R_PPC64_GOT_DTPREL16_DS:
return {R_PPC64_ADDR16_DS, TocBiasedVal};
+ case R_PPC64_GOT16_HA:
case R_PPC64_GOT_TLSGD16_HA:
case R_PPC64_GOT_TLSLD16_HA:
case R_PPC64_GOT_TPREL16_HA:
case R_PPC64_GOT_DTPREL16_HA:
case R_PPC64_TOC16_HA:
return {R_PPC64_ADDR16_HA, TocBiasedVal};
+ case R_PPC64_GOT16_HI:
case R_PPC64_GOT_TLSGD16_HI:
case R_PPC64_GOT_TLSLD16_HI:
case R_PPC64_GOT_TPREL16_HI:
case R_PPC64_GOT_DTPREL16_HI:
case R_PPC64_TOC16_HI:
return {R_PPC64_ADDR16_HI, TocBiasedVal};
+ case R_PPC64_GOT16_LO:
case R_PPC64_GOT_TLSGD16_LO:
case R_PPC64_GOT_TLSLD16_LO:
case R_PPC64_TOC16_LO:
return {R_PPC64_ADDR16_LO, TocBiasedVal};
+ case R_PPC64_GOT16_LO_DS:
case R_PPC64_TOC16_LO_DS:
case R_PPC64_GOT_TPREL16_LO_DS:
case R_PPC64_GOT_DTPREL16_LO_DS:
@@ -386,9 +597,27 @@ static std::pair<RelType, uint64_t> toAddr16Rel(RelType Type, uint64_t Val) {
}
}
+static bool isTocOptType(RelType Type) {
+ switch (Type) {
+ case R_PPC64_GOT16_HA:
+ case R_PPC64_GOT16_LO_DS:
+ case R_PPC64_TOC16_HA:
+ case R_PPC64_TOC16_LO_DS:
+ case R_PPC64_TOC16_LO:
+ return true;
+ default:
+ return false;
+ }
+}
+
void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
- // For a TOC-relative relocation, proceed in terms of the corresponding
- // ADDR16 relocation type.
+ // We need to save the original relocation type to use in diagnostics, and
+ // use the original type to determine if we should toc-optimize the
+ // instructions being relocated.
+ RelType OriginalType = Type;
+ bool ShouldTocOptimize = isTocOptType(Type);
+ // For dynamic thread pointer relative, toc-relative, and got-indirect
+ // relocations, proceed in terms of the corresponding ADDR16 relocation type.
std::tie(Type, Val) = toAddr16Rel(Type, Val);
switch (Type) {
@@ -401,18 +630,25 @@ void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
}
case R_PPC64_ADDR16:
case R_PPC64_TPREL16:
- checkInt(Loc, Val, 16, Type);
+ checkInt(Loc, Val, 16, OriginalType);
write16(Loc, Val);
break;
case R_PPC64_ADDR16_DS:
- case R_PPC64_TPREL16_DS:
- checkInt(Loc, Val, 16, Type);
- write16(Loc, (read16(Loc) & 3) | (Val & ~3));
- break;
+ case R_PPC64_TPREL16_DS: {
+ checkInt(Loc, Val, 16, OriginalType);
+ // DQ-form instructions use bits 28-31 as part of the instruction encoding
+ // DS-form instructions only use bits 30-31.
+ uint16_t Mask = isDQFormInstruction(readInstrFromHalf16(Loc)) ? 0xF : 0x3;
+ checkAlignment(Loc, lo(Val), Mask + 1, OriginalType);
+ write16(Loc, (read16(Loc) & Mask) | lo(Val));
+ } break;
case R_PPC64_ADDR16_HA:
case R_PPC64_REL16_HA:
case R_PPC64_TPREL16_HA:
- write16(Loc, ha(Val));
+ if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0)
+ writeInstrFromHalf16(Loc, 0x60000000);
+ else
+ write16(Loc, ha(Val));
break;
case R_PPC64_ADDR16_HI:
case R_PPC64_REL16_HI:
@@ -438,12 +674,40 @@ void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_PPC64_ADDR16_LO:
case R_PPC64_REL16_LO:
case R_PPC64_TPREL16_LO:
+ // When the high-adjusted part of a toc relocation evalutes to 0, it is
+ // changed into a nop. The lo part then needs to be updated to use the
+ // toc-pointer register r2, as the base register.
+ if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0) {
+ uint32_t Instr = readInstrFromHalf16(Loc);
+ if (isInstructionUpdateForm(Instr))
+ error(getErrorLocation(Loc) +
+ "can't toc-optimize an update instruction: 0x" +
+ utohexstr(Instr));
+ Instr = (Instr & 0xFFE00000) | 0x00020000;
+ writeInstrFromHalf16(Loc, Instr);
+ }
write16(Loc, lo(Val));
break;
case R_PPC64_ADDR16_LO_DS:
- case R_PPC64_TPREL16_LO_DS:
- write16(Loc, (read16(Loc) & 3) | (lo(Val) & ~3));
- break;
+ case R_PPC64_TPREL16_LO_DS: {
+ // DQ-form instructions use bits 28-31 as part of the instruction encoding
+ // DS-form instructions only use bits 30-31.
+ uint32_t Inst = readInstrFromHalf16(Loc);
+ uint16_t Mask = isDQFormInstruction(Inst) ? 0xF : 0x3;
+ checkAlignment(Loc, lo(Val), Mask + 1, OriginalType);
+ if (Config->TocOptimize && ShouldTocOptimize && ha(Val) == 0) {
+ // When the high-adjusted part of a toc relocation evalutes to 0, it is
+ // changed into a nop. The lo part then needs to be updated to use the toc
+ // pointer register r2, as the base register.
+ if (isInstructionUpdateForm(Inst))
+ error(getErrorLocation(Loc) +
+ "Can't toc-optimize an update instruction: 0x" +
+ Twine::utohexstr(Inst));
+ Inst = (Inst & 0xFFE0000F) | 0x00020000;
+ writeInstrFromHalf16(Loc, Inst);
+ }
+ write16(Loc, (read16(Loc) & Mask) | lo(Val));
+ } break;
case R_PPC64_ADDR32:
case R_PPC64_REL32:
checkInt(Loc, Val, 32, Type);
@@ -454,9 +718,17 @@ void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_PPC64_TOC:
write64(Loc, Val);
break;
+ case R_PPC64_REL14: {
+ uint32_t Mask = 0x0000FFFC;
+ checkInt(Loc, Val, 16, Type);
+ checkAlignment(Loc, Val, 4, Type);
+ write32(Loc, (read32(Loc) & ~Mask) | (Val & Mask));
+ break;
+ }
case R_PPC64_REL24: {
uint32_t Mask = 0x03FFFFFC;
- checkInt(Loc, Val, 24, Type);
+ checkInt(Loc, Val, 26, Type);
+ checkAlignment(Loc, Val, 4, Type);
write32(Loc, (read32(Loc) & ~Mask) | (Val & Mask));
break;
}
@@ -470,9 +742,30 @@ void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
bool PPC64::needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
uint64_t BranchAddr, const Symbol &S) const {
- // If a function is in the plt it needs to be called through
- // a call stub.
- return Type == R_PPC64_REL24 && S.isInPlt();
+ if (Type != R_PPC64_REL14 && Type != R_PPC64_REL24)
+ return false;
+
+ // If a function is in the Plt it needs to be called with a call-stub.
+ if (S.isInPlt())
+ return true;
+
+ // If a symbol is a weak undefined and we are compiling an executable
+ // it doesn't need a range-extending thunk since it can't be called.
+ if (S.isUndefWeak() && !Config->Shared)
+ return false;
+
+ // If the offset exceeds the range of the branch type then it will need
+ // a range-extending thunk.
+ return !inBranchRange(Type, BranchAddr, S.getVA());
+}
+
+bool PPC64::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const {
+ int64_t Offset = Dst - Src;
+ if (Type == R_PPC64_REL14)
+ return isInt<16>(Offset);
+ if (Type == R_PPC64_REL24)
+ return isInt<26>(Offset);
+ llvm_unreachable("unsupported relocation type used in branch");
}
RelExpr PPC64::adjustRelaxExpr(RelType Type, const uint8_t *Data,
@@ -511,9 +804,8 @@ void PPC64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const {
case R_PPC64_GOT_TLSGD16_LO: {
// Relax from addi r3, rA, sym@got@tlsgd@l to
// ld r3, sym@got@tprel@l(rA)
- uint32_t EndianOffset = Config->EKind == ELF64BEKind ? 2U : 0U;
- uint32_t InputRegister = (read32(Loc - EndianOffset) & (0x1f << 16));
- write32(Loc - EndianOffset, 0xE8600000 | InputRegister);
+ uint32_t InputRegister = (readInstrFromHalf16(Loc) & (0x1f << 16));
+ writeInstrFromHalf16(Loc, 0xE8600000 | InputRegister);
relocateOne(Loc, R_PPC64_GOT_TPREL16_LO_DS, Val);
return;
}
@@ -526,6 +818,113 @@ void PPC64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const {
}
}
+// The prologue for a split-stack function is expected to look roughly
+// like this:
+// .Lglobal_entry_point:
+// # TOC pointer initalization.
+// ...
+// .Llocal_entry_point:
+// # load the __private_ss member of the threads tcbhead.
+// ld r0,-0x7000-64(r13)
+// # subtract the functions stack size from the stack pointer.
+// addis r12, r1, ha(-stack-frame size)
+// addi r12, r12, l(-stack-frame size)
+// # compare needed to actual and branch to allocate_more_stack if more
+// # space is needed, otherwise fallthrough to 'normal' function body.
+// cmpld cr7,r12,r0
+// blt- cr7, .Lallocate_more_stack
+//
+// -) The allocate_more_stack block might be placed after the split-stack
+// prologue and the `blt-` replaced with a `bge+ .Lnormal_func_body`
+// instead.
+// -) If either the addis or addi is not needed due to the stack size being
+// smaller then 32K or a multiple of 64K they will be replaced with a nop,
+// but there will always be 2 instructions the linker can overwrite for the
+// adjusted stack size.
+//
+// The linkers job here is to increase the stack size used in the addis/addi
+// pair by split-stack-size-adjust.
+// addis r12, r1, ha(-stack-frame size - split-stack-adjust-size)
+// addi r12, r12, l(-stack-frame size - split-stack-adjust-size)
+bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End,
+ uint8_t StOther) const {
+ // If the caller has a global entry point adjust the buffer past it. The start
+ // of the split-stack prologue will be at the local entry point.
+ Loc += getPPC64GlobalEntryToLocalEntryOffset(StOther);
+
+ // At the very least we expect to see a load of some split-stack data from the
+ // tcb, and 2 instructions that calculate the ending stack address this
+ // function will require. If there is not enough room for at least 3
+ // instructions it can't be a split-stack prologue.
+ if (Loc + 12 >= End)
+ return false;
+
+ // First instruction must be `ld r0, -0x7000-64(r13)`
+ if (read32(Loc) != 0xe80d8fc0)
+ return false;
+
+ int16_t HiImm = 0;
+ int16_t LoImm = 0;
+ // First instruction can be either an addis if the frame size is larger then
+ // 32K, or an addi if the size is less then 32K.
+ int32_t FirstInstr = read32(Loc + 4);
+ if (getPrimaryOpCode(FirstInstr) == 15) {
+ HiImm = FirstInstr & 0xFFFF;
+ } else if (getPrimaryOpCode(FirstInstr) == 14) {
+ LoImm = FirstInstr & 0xFFFF;
+ } else {
+ return false;
+ }
+
+ // Second instruction is either an addi or a nop. If the first instruction was
+ // an addi then LoImm is set and the second instruction must be a nop.
+ uint32_t SecondInstr = read32(Loc + 8);
+ if (!LoImm && getPrimaryOpCode(SecondInstr) == 14) {
+ LoImm = SecondInstr & 0xFFFF;
+ } else if (SecondInstr != 0x60000000) {
+ return false;
+ }
+
+ // The register operands of the first instruction should be the stack-pointer
+ // (r1) as the input (RA) and r12 as the output (RT). If the second
+ // instruction is not a nop, then it should use r12 as both input and output.
+ auto CheckRegOperands = [](uint32_t Instr, uint8_t ExpectedRT,
+ uint8_t ExpectedRA) {
+ return ((Instr & 0x3E00000) >> 21 == ExpectedRT) &&
+ ((Instr & 0x1F0000) >> 16 == ExpectedRA);
+ };
+ if (!CheckRegOperands(FirstInstr, 12, 1))
+ return false;
+ if (SecondInstr != 0x60000000 && !CheckRegOperands(SecondInstr, 12, 12))
+ return false;
+
+ int32_t StackFrameSize = (HiImm * 65536) + LoImm;
+ // Check that the adjusted size doesn't overflow what we can represent with 2
+ // instructions.
+ if (StackFrameSize < Config->SplitStackAdjustSize + INT32_MIN) {
+ error(getErrorLocation(Loc) + "split-stack prologue adjustment overflows");
+ return false;
+ }
+
+ int32_t AdjustedStackFrameSize =
+ StackFrameSize - Config->SplitStackAdjustSize;
+
+ LoImm = AdjustedStackFrameSize & 0xFFFF;
+ HiImm = (AdjustedStackFrameSize + 0x8000) >> 16;
+ if (HiImm) {
+ write32(Loc + 4, 0x3D810000 | (uint16_t)HiImm);
+ // If the low immediate is zero the second instruction will be a nop.
+ SecondInstr = LoImm ? 0x398C0000 | (uint16_t)LoImm : 0x60000000;
+ write32(Loc + 8, SecondInstr);
+ } else {
+ // addi r12, r1, imm
+ write32(Loc + 4, (0x39810000) | (uint16_t)LoImm);
+ write32(Loc + 8, 0x60000000);
+ }
+
+ return true;
+}
+
TargetInfo *elf::getPPC64TargetInfo() {
static PPC64 Target;
return &Target;
diff --git a/ELF/Arch/RISCV.cpp b/ELF/Arch/RISCV.cpp
new file mode 100644
index 000000000000..461e8d35c3e6
--- /dev/null
+++ b/ELF/Arch/RISCV.cpp
@@ -0,0 +1,279 @@
+//===- RISCV.cpp ----------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InputFiles.h"
+#include "Target.h"
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace llvm::support::endian;
+using namespace llvm::ELF;
+using namespace lld;
+using namespace lld::elf;
+
+namespace {
+
+class RISCV final : public TargetInfo {
+public:
+ RISCV();
+ uint32_t calcEFlags() const override;
+ RelExpr getRelExpr(RelType Type, const Symbol &S,
+ const uint8_t *Loc) const override;
+ void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+};
+
+} // end anonymous namespace
+
+RISCV::RISCV() { NoneRel = R_RISCV_NONE; }
+
+static uint32_t getEFlags(InputFile *F) {
+ if (Config->Is64)
+ return cast<ObjFile<ELF64LE>>(F)->getObj().getHeader()->e_flags;
+ return cast<ObjFile<ELF32LE>>(F)->getObj().getHeader()->e_flags;
+}
+
+uint32_t RISCV::calcEFlags() const {
+ assert(!ObjectFiles.empty());
+
+ uint32_t Target = getEFlags(ObjectFiles.front());
+
+ for (InputFile *F : ObjectFiles) {
+ uint32_t EFlags = getEFlags(F);
+ if (EFlags & EF_RISCV_RVC)
+ Target |= EF_RISCV_RVC;
+
+ if ((EFlags & EF_RISCV_FLOAT_ABI) != (Target & EF_RISCV_FLOAT_ABI))
+ error(toString(F) +
+ ": cannot link object files with different floating-point ABI");
+
+ if ((EFlags & EF_RISCV_RVE) != (Target & EF_RISCV_RVE))
+ error(toString(F) +
+ ": cannot link object files with different EF_RISCV_RVE");
+ }
+
+ return Target;
+}
+
+RelExpr RISCV::getRelExpr(const RelType Type, const Symbol &S,
+ const uint8_t *Loc) const {
+ switch (Type) {
+ case R_RISCV_JAL:
+ case R_RISCV_BRANCH:
+ case R_RISCV_CALL:
+ case R_RISCV_PCREL_HI20:
+ case R_RISCV_RVC_BRANCH:
+ case R_RISCV_RVC_JUMP:
+ case R_RISCV_32_PCREL:
+ return R_PC;
+ case R_RISCV_PCREL_LO12_I:
+ case R_RISCV_PCREL_LO12_S:
+ return R_RISCV_PC_INDIRECT;
+ case R_RISCV_RELAX:
+ case R_RISCV_ALIGN:
+ return R_HINT;
+ default:
+ return R_ABS;
+ }
+}
+
+// Extract bits V[Begin:End], where range is inclusive, and Begin must be < 63.
+static uint32_t extractBits(uint64_t V, uint32_t Begin, uint32_t End) {
+ return (V & ((1ULL << (Begin + 1)) - 1)) >> End;
+}
+
+void RISCV::relocateOne(uint8_t *Loc, const RelType Type,
+ const uint64_t Val) const {
+ switch (Type) {
+ case R_RISCV_32:
+ write32le(Loc, Val);
+ return;
+ case R_RISCV_64:
+ write64le(Loc, Val);
+ return;
+
+ case R_RISCV_RVC_BRANCH: {
+ checkInt(Loc, static_cast<int64_t>(Val) >> 1, 8, Type);
+ checkAlignment(Loc, Val, 2, Type);
+ uint16_t Insn = read16le(Loc) & 0xE383;
+ uint16_t Imm8 = extractBits(Val, 8, 8) << 12;
+ uint16_t Imm4_3 = extractBits(Val, 4, 3) << 10;
+ uint16_t Imm7_6 = extractBits(Val, 7, 6) << 5;
+ uint16_t Imm2_1 = extractBits(Val, 2, 1) << 3;
+ uint16_t Imm5 = extractBits(Val, 5, 5) << 2;
+ Insn |= Imm8 | Imm4_3 | Imm7_6 | Imm2_1 | Imm5;
+
+ write16le(Loc, Insn);
+ return;
+ }
+
+ case R_RISCV_RVC_JUMP: {
+ checkInt(Loc, static_cast<int64_t>(Val) >> 1, 11, Type);
+ checkAlignment(Loc, Val, 2, Type);
+ uint16_t Insn = read16le(Loc) & 0xE003;
+ uint16_t Imm11 = extractBits(Val, 11, 11) << 12;
+ uint16_t Imm4 = extractBits(Val, 4, 4) << 11;
+ uint16_t Imm9_8 = extractBits(Val, 9, 8) << 9;
+ uint16_t Imm10 = extractBits(Val, 10, 10) << 8;
+ uint16_t Imm6 = extractBits(Val, 6, 6) << 7;
+ uint16_t Imm7 = extractBits(Val, 7, 7) << 6;
+ uint16_t Imm3_1 = extractBits(Val, 3, 1) << 3;
+ uint16_t Imm5 = extractBits(Val, 5, 5) << 2;
+ Insn |= Imm11 | Imm4 | Imm9_8 | Imm10 | Imm6 | Imm7 | Imm3_1 | Imm5;
+
+ write16le(Loc, Insn);
+ return;
+ }
+
+ case R_RISCV_RVC_LUI: {
+ int32_t Imm = ((Val + 0x800) >> 12);
+ checkUInt(Loc, Imm, 6, Type);
+ if (Imm == 0) { // `c.lui rd, 0` is illegal, convert to `c.li rd, 0`
+ write16le(Loc, (read16le(Loc) & 0x0F83) | 0x4000);
+ } else {
+ uint16_t Imm17 = extractBits(Val + 0x800, 17, 17) << 12;
+ uint16_t Imm16_12 = extractBits(Val + 0x800, 16, 12) << 2;
+ write16le(Loc, (read16le(Loc) & 0xEF83) | Imm17 | Imm16_12);
+ }
+ return;
+ }
+
+ case R_RISCV_JAL: {
+ checkInt(Loc, static_cast<int64_t>(Val) >> 1, 20, Type);
+ checkAlignment(Loc, Val, 2, Type);
+
+ uint32_t Insn = read32le(Loc) & 0xFFF;
+ uint32_t Imm20 = extractBits(Val, 20, 20) << 31;
+ uint32_t Imm10_1 = extractBits(Val, 10, 1) << 21;
+ uint32_t Imm11 = extractBits(Val, 11, 11) << 20;
+ uint32_t Imm19_12 = extractBits(Val, 19, 12) << 12;
+ Insn |= Imm20 | Imm10_1 | Imm11 | Imm19_12;
+
+ write32le(Loc, Insn);
+ return;
+ }
+
+ case R_RISCV_BRANCH: {
+ checkInt(Loc, static_cast<int64_t>(Val) >> 1, 12, Type);
+ checkAlignment(Loc, Val, 2, Type);
+
+ uint32_t Insn = read32le(Loc) & 0x1FFF07F;
+ uint32_t Imm12 = extractBits(Val, 12, 12) << 31;
+ uint32_t Imm10_5 = extractBits(Val, 10, 5) << 25;
+ uint32_t Imm4_1 = extractBits(Val, 4, 1) << 8;
+ uint32_t Imm11 = extractBits(Val, 11, 11) << 7;
+ Insn |= Imm12 | Imm10_5 | Imm4_1 | Imm11;
+
+ write32le(Loc, Insn);
+ return;
+ }
+
+ // auipc + jalr pair
+ case R_RISCV_CALL: {
+ checkInt(Loc, Val, 32, Type);
+ if (isInt<32>(Val)) {
+ relocateOne(Loc, R_RISCV_PCREL_HI20, Val);
+ relocateOne(Loc + 4, R_RISCV_PCREL_LO12_I, Val);
+ }
+ return;
+ }
+
+ case R_RISCV_PCREL_HI20:
+ case R_RISCV_HI20: {
+ checkInt(Loc, Val, 32, Type);
+ uint32_t Hi = Val + 0x800;
+ write32le(Loc, (read32le(Loc) & 0xFFF) | (Hi & 0xFFFFF000));
+ return;
+ }
+
+ case R_RISCV_PCREL_LO12_I:
+ case R_RISCV_LO12_I: {
+ checkInt(Loc, Val, 32, Type);
+ uint32_t Hi = Val + 0x800;
+ uint32_t Lo = Val - (Hi & 0xFFFFF000);
+ write32le(Loc, (read32le(Loc) & 0xFFFFF) | ((Lo & 0xFFF) << 20));
+ return;
+ }
+
+ case R_RISCV_PCREL_LO12_S:
+ case R_RISCV_LO12_S: {
+ checkInt(Loc, Val, 32, Type);
+ uint32_t Hi = Val + 0x800;
+ uint32_t Lo = Val - (Hi & 0xFFFFF000);
+ uint32_t Imm11_5 = extractBits(Lo, 11, 5) << 25;
+ uint32_t Imm4_0 = extractBits(Lo, 4, 0) << 7;
+ write32le(Loc, (read32le(Loc) & 0x1FFF07F) | Imm11_5 | Imm4_0);
+ return;
+ }
+
+ case R_RISCV_ADD8:
+ *Loc += Val;
+ return;
+ case R_RISCV_ADD16:
+ write16le(Loc, read16le(Loc) + Val);
+ return;
+ case R_RISCV_ADD32:
+ write32le(Loc, read32le(Loc) + Val);
+ return;
+ case R_RISCV_ADD64:
+ write64le(Loc, read64le(Loc) + Val);
+ return;
+ case R_RISCV_SUB6:
+ *Loc = (*Loc & 0xc0) | (((*Loc & 0x3f) - Val) & 0x3f);
+ return;
+ case R_RISCV_SUB8:
+ *Loc -= Val;
+ return;
+ case R_RISCV_SUB16:
+ write16le(Loc, read16le(Loc) - Val);
+ return;
+ case R_RISCV_SUB32:
+ write32le(Loc, read32le(Loc) - Val);
+ return;
+ case R_RISCV_SUB64:
+ write64le(Loc, read64le(Loc) - Val);
+ return;
+ case R_RISCV_SET6:
+ *Loc = (*Loc & 0xc0) | (Val & 0x3f);
+ return;
+ case R_RISCV_SET8:
+ *Loc = Val;
+ return;
+ case R_RISCV_SET16:
+ write16le(Loc, Val);
+ return;
+ case R_RISCV_SET32:
+ case R_RISCV_32_PCREL:
+ write32le(Loc, Val);
+ return;
+
+ case R_RISCV_ALIGN:
+ case R_RISCV_RELAX:
+ return; // Ignored (for now)
+ case R_RISCV_NONE:
+ return; // Do nothing
+
+ // These are handled by the dynamic linker
+ case R_RISCV_RELATIVE:
+ case R_RISCV_COPY:
+ case R_RISCV_JUMP_SLOT:
+ // GP-relative relocations are only produced after relaxation, which
+ // we don't support for now
+ case R_RISCV_GPREL_I:
+ case R_RISCV_GPREL_S:
+ default:
+ error(getErrorLocation(Loc) +
+ "unimplemented relocation: " + toString(Type));
+ return;
+ }
+}
+
+TargetInfo *elf::getRISCVTargetInfo() {
+ static RISCV Target;
+ return &Target;
+}
diff --git a/ELF/Arch/SPARCV9.cpp b/ELF/Arch/SPARCV9.cpp
index 36f5c836930e..831aa2028e7f 100644
--- a/ELF/Arch/SPARCV9.cpp
+++ b/ELF/Arch/SPARCV9.cpp
@@ -35,6 +35,7 @@ public:
SPARCV9::SPARCV9() {
CopyRel = R_SPARC_COPY;
GotRel = R_SPARC_GLOB_DAT;
+ NoneRel = R_SPARC_NONE;
PltRel = R_SPARC_JMP_SLOT;
RelativeRel = R_SPARC_RELATIVE;
GotEntrySize = 8;
diff --git a/ELF/Arch/X86.cpp b/ELF/Arch/X86.cpp
index 19a0b6017f1a..e910375d2fc7 100644
--- a/ELF/Arch/X86.cpp
+++ b/ELF/Arch/X86.cpp
@@ -48,6 +48,7 @@ public:
X86::X86() {
CopyRel = R_386_COPY;
GotRel = R_386_GLOB_DAT;
+ NoneRel = R_386_NONE;
PltRel = R_386_JUMP_SLOT;
IRelativeRel = R_386_IRELATIVE;
RelativeRel = R_386_RELATIVE;
@@ -59,7 +60,11 @@ X86::X86() {
PltEntrySize = 16;
PltHeaderSize = 16;
TlsGdRelaxSkip = 2;
- TrapInstr = 0xcccccccc; // 0xcc = INT3
+ TrapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3
+
+ // Align to the non-PAE large page size (known as a superpage or huge page).
+ // FreeBSD automatically promotes large, superpage-aligned allocations.
+ DefaultImageBase = 0x400000;
}
static bool hasBaseReg(uint8_t ModRM) { return (ModRM & 0xc7) != 0x5; }
@@ -152,7 +157,7 @@ RelExpr X86::adjustRelaxExpr(RelType Type, const uint8_t *Data,
}
void X86::writeGotPltHeader(uint8_t *Buf) const {
- write32le(Buf, InX::Dynamic->getVA());
+ write32le(Buf, In.Dynamic->getVA());
}
void X86::writeGotPlt(uint8_t *Buf, const Symbol &S) const {
@@ -183,8 +188,8 @@ void X86::writePltHeader(uint8_t *Buf) const {
};
memcpy(Buf, V, sizeof(V));
- uint32_t Ebx = InX::Got->getVA() + InX::Got->getSize();
- uint32_t GotPlt = InX::GotPlt->getVA() - Ebx;
+ uint32_t Ebx = In.Got->getVA() + In.Got->getSize();
+ uint32_t GotPlt = In.GotPlt->getVA() - Ebx;
write32le(Buf + 2, GotPlt + 4);
write32le(Buf + 8, GotPlt + 8);
return;
@@ -196,7 +201,7 @@ void X86::writePltHeader(uint8_t *Buf) const {
0x90, 0x90, 0x90, 0x90, // nop
};
memcpy(Buf, PltData, sizeof(PltData));
- uint32_t GotPlt = InX::GotPlt->getVA();
+ uint32_t GotPlt = In.GotPlt->getVA();
write32le(Buf + 2, GotPlt + 4);
write32le(Buf + 8, GotPlt + 8);
}
@@ -213,7 +218,7 @@ void X86::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
if (Config->Pic) {
// jmp *foo@GOT(%ebx)
- uint32_t Ebx = InX::Got->getVA() + InX::Got->getSize();
+ uint32_t Ebx = In.Got->getVA() + In.Got->getSize();
Buf[1] = 0xa3;
write32le(Buf + 2, GotPltEntryAddr - Ebx);
} else {
@@ -447,8 +452,8 @@ void RetpolinePic::writePltHeader(uint8_t *Buf) const {
};
memcpy(Buf, Insn, sizeof(Insn));
- uint32_t Ebx = InX::Got->getVA() + InX::Got->getSize();
- uint32_t GotPlt = InX::GotPlt->getVA() - Ebx;
+ uint32_t Ebx = In.Got->getVA() + In.Got->getSize();
+ uint32_t GotPlt = In.GotPlt->getVA() - Ebx;
write32le(Buf + 2, GotPlt + 4);
write32le(Buf + 9, GotPlt + 8);
}
@@ -467,7 +472,7 @@ void RetpolinePic::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
};
memcpy(Buf, Insn, sizeof(Insn));
- uint32_t Ebx = InX::Got->getVA() + InX::Got->getSize();
+ uint32_t Ebx = In.Got->getVA() + In.Got->getSize();
unsigned Off = getPltEntryOffset(Index);
write32le(Buf + 3, GotPltEntryAddr - Ebx);
write32le(Buf + 8, -Off - 12 + 32);
@@ -506,7 +511,7 @@ void RetpolineNoPic::writePltHeader(uint8_t *Buf) const {
};
memcpy(Buf, Insn, sizeof(Insn));
- uint32_t GotPlt = InX::GotPlt->getVA();
+ uint32_t GotPlt = In.GotPlt->getVA();
write32le(Buf + 2, GotPlt + 4);
write32le(Buf + 8, GotPlt + 8);
}
diff --git a/ELF/Arch/X86_64.cpp b/ELF/Arch/X86_64.cpp
index d4bdb3730c58..06314155dcc9 100644
--- a/ELF/Arch/X86_64.cpp
+++ b/ELF/Arch/X86_64.cpp
@@ -43,8 +43,8 @@ public:
void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override;
- bool adjustPrologueForCrossSplitStack(uint8_t *Loc,
- uint8_t *End) const override;
+ bool adjustPrologueForCrossSplitStack(uint8_t *Loc, uint8_t *End,
+ uint8_t StOther) const override;
private:
void relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op,
@@ -55,6 +55,7 @@ private:
template <class ELFT> X86_64<ELFT>::X86_64() {
CopyRel = R_X86_64_COPY;
GotRel = R_X86_64_GLOB_DAT;
+ NoneRel = R_X86_64_NONE;
PltRel = R_X86_64_JUMP_SLOT;
RelativeRel = R_X86_64_RELATIVE;
IRelativeRel = R_X86_64_IRELATIVE;
@@ -66,7 +67,7 @@ template <class ELFT> X86_64<ELFT>::X86_64() {
PltEntrySize = 16;
PltHeaderSize = 16;
TlsGdRelaxSkip = 2;
- TrapInstr = 0xcccccccc; // 0xcc = INT3
+ TrapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3
// Align to the large page size (known as a superpage or huge page).
// FreeBSD automatically promotes large, superpage-aligned allocations.
@@ -124,7 +125,7 @@ template <class ELFT> void X86_64<ELFT>::writeGotPltHeader(uint8_t *Buf) const {
// required, but it is documented in the psabi and the glibc dynamic linker
// seems to use it (note that this is relevant for linking ld.so, not any
// other program).
- write64le(Buf, InX::Dynamic->getVA());
+ write64le(Buf, In.Dynamic->getVA());
}
template <class ELFT>
@@ -140,8 +141,8 @@ template <class ELFT> void X86_64<ELFT>::writePltHeader(uint8_t *Buf) const {
0x0f, 0x1f, 0x40, 0x00, // nop
};
memcpy(Buf, PltData, sizeof(PltData));
- uint64_t GotPlt = InX::GotPlt->getVA();
- uint64_t Plt = InX::Plt->getVA();
+ uint64_t GotPlt = In.GotPlt->getVA();
+ uint64_t Plt = In.Plt->getVA();
write32le(Buf + 2, GotPlt - Plt + 2); // GOTPLT+8
write32le(Buf + 8, GotPlt - Plt + 4); // GOTPLT+16
}
@@ -481,23 +482,27 @@ namespace {
// B) Or a load of a stack pointer offset with an lea to r10 or r11.
template <>
bool X86_64<ELF64LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc,
- uint8_t *End) const {
+ uint8_t *End,
+ uint8_t StOther) const {
+ if (Loc + 8 >= End)
+ return false;
+
// Replace "cmp %fs:0x70,%rsp" and subsequent branch
// with "stc, nopl 0x0(%rax,%rax,1)"
- if (Loc + 8 < End && memcmp(Loc, "\x64\x48\x3b\x24\x25", 4) == 0) {
+ if (memcmp(Loc, "\x64\x48\x3b\x24\x25", 5) == 0) {
memcpy(Loc, "\xf9\x0f\x1f\x84\x00\x00\x00\x00", 8);
return true;
}
- // Adjust "lea -0x200(%rsp),%r10" to lea "-0x4200(%rsp),%r10"
- if (Loc + 7 < End && memcmp(Loc, "\x4c\x8d\x94\x24\x00\xfe\xff", 7) == 0) {
- memcpy(Loc, "\x4c\x8d\x94\x24\x00\xbe\xff", 7);
- return true;
- }
-
- // Adjust "lea -0x200(%rsp),%r11" to lea "-0x4200(%rsp),%r11"
- if (Loc + 7 < End && memcmp(Loc, "\x4c\x8d\x9c\x24\x00\xfe\xff", 7) == 0) {
- memcpy(Loc, "\x4c\x8d\x9c\x24\x00\xbe\xff", 7);
+ // Adjust "lea X(%rsp),%rYY" to lea "(X - 0x4000)(%rsp),%rYY" where rYY could
+ // be r10 or r11. The lea instruction feeds a subsequent compare which checks
+ // if there is X available stack space. Making X larger effectively reserves
+ // that much additional space. The stack grows downward so subtract the value.
+ if (memcmp(Loc, "\x4c\x8d\x94\x24", 4) == 0 ||
+ memcmp(Loc, "\x4c\x8d\x9c\x24", 4) == 0) {
+ // The offset bytes are encoded four bytes after the start of the
+ // instruction.
+ write32le(Loc + 4, read32le(Loc + 4) - 0x4000);
return true;
}
return false;
@@ -505,7 +510,8 @@ bool X86_64<ELF64LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc,
template <>
bool X86_64<ELF32LE>::adjustPrologueForCrossSplitStack(uint8_t *Loc,
- uint8_t *End) const {
+ uint8_t *End,
+ uint8_t StOther) const {
llvm_unreachable("Target doesn't support split stacks.");
}
@@ -566,8 +572,8 @@ template <class ELFT> void Retpoline<ELFT>::writePltHeader(uint8_t *Buf) const {
};
memcpy(Buf, Insn, sizeof(Insn));
- uint64_t GotPlt = InX::GotPlt->getVA();
- uint64_t Plt = InX::Plt->getVA();
+ uint64_t GotPlt = In.GotPlt->getVA();
+ uint64_t Plt = In.Plt->getVA();
write32le(Buf + 2, GotPlt - Plt - 6 + 8);
write32le(Buf + 9, GotPlt - Plt - 13 + 16);
}
@@ -586,7 +592,7 @@ void Retpoline<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
};
memcpy(Buf, Insn, sizeof(Insn));
- uint64_t Off = TargetInfo::getPltEntryOffset(Index);
+ uint64_t Off = getPltEntryOffset(Index);
write32le(Buf + 3, GotPltEntryAddr - PltEntryAddr - 7);
write32le(Buf + 8, -Off - 12 + 32);
@@ -629,7 +635,7 @@ void RetpolineZNow<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
memcpy(Buf, Insn, sizeof(Insn));
write32le(Buf + 3, GotPltEntryAddr - PltEntryAddr - 7);
- write32le(Buf + 8, -TargetInfo::getPltEntryOffset(Index) - 12);
+ write32le(Buf + 8, -getPltEntryOffset(Index) - 12);
}
template <class ELFT> static TargetInfo *getTargetInfo() {
diff --git a/ELF/CMakeLists.txt b/ELF/CMakeLists.txt
index fb2f53a72025..a1c23b0d49ac 100644
--- a/ELF/CMakeLists.txt
+++ b/ELF/CMakeLists.txt
@@ -15,17 +15,19 @@ add_lld_library(lldELF
Arch/Hexagon.cpp
Arch/Mips.cpp
Arch/MipsArchTree.cpp
+ Arch/MSP430.cpp
Arch/PPC.cpp
Arch/PPC64.cpp
+ Arch/RISCV.cpp
Arch/SPARCV9.cpp
Arch/X86.cpp
Arch/X86_64.cpp
CallGraphSort.cpp
+ DWARF.cpp
Driver.cpp
DriverUtils.cpp
EhFrame.cpp
Filesystem.cpp
- GdbIndex.cpp
ICF.cpp
InputFiles.cpp
InputSection.cpp
diff --git a/ELF/CallGraphSort.cpp b/ELF/CallGraphSort.cpp
index 33ac159a6e26..2a7d78664b8e 100644
--- a/ELF/CallGraphSort.cpp
+++ b/ELF/CallGraphSort.cpp
@@ -57,10 +57,7 @@ struct Edge {
};
struct Cluster {
- Cluster(int Sec, size_t S) {
- Sections.push_back(Sec);
- Size = S;
- }
+ Cluster(int Sec, size_t S) : Sections{Sec}, Size(S) {}
double getDensity() const {
if (Size == 0)
@@ -72,7 +69,7 @@ struct Cluster {
size_t Size = 0;
uint64_t Weight = 0;
uint64_t InitialWeight = 0;
- std::vector<Edge> Preds;
+ Edge BestPred = {-1, 0};
};
class CallGraphSort {
@@ -96,12 +93,14 @@ constexpr int MAX_DENSITY_DEGRADATION = 8;
constexpr uint64_t MAX_CLUSTER_SIZE = 1024 * 1024;
} // end anonymous namespace
+typedef std::pair<const InputSectionBase *, const InputSectionBase *>
+ SectionPair;
+
// Take the edge list in Config->CallGraphProfile, resolve symbol names to
// Symbols, and generate a graph between InputSections with the provided
// weights.
CallGraphSort::CallGraphSort() {
- llvm::MapVector<std::pair<const InputSectionBase *, const InputSectionBase *>,
- uint64_t> &Profile = Config->CallGraphProfile;
+ MapVector<SectionPair, uint64_t> &Profile = Config->CallGraphProfile;
DenseMap<const InputSectionBase *, int> SecToCluster;
auto GetOrCreateNode = [&](const InputSectionBase *IS) -> int {
@@ -114,7 +113,7 @@ CallGraphSort::CallGraphSort() {
};
// Create the graph.
- for (const auto &C : Profile) {
+ for (std::pair<SectionPair, uint64_t> &C : Profile) {
const auto *FromSB = cast<InputSectionBase>(C.first.first->Repl);
const auto *ToSB = cast<InputSectionBase>(C.first.second->Repl);
uint64_t Weight = C.second;
@@ -136,8 +135,12 @@ CallGraphSort::CallGraphSort() {
if (From == To)
continue;
- // Add an edge
- Clusters[To].Preds.push_back({From, Weight});
+ // Remember the best edge.
+ Cluster &ToC = Clusters[To];
+ if (ToC.BestPred.From == -1 || ToC.BestPred.Weight < Weight) {
+ ToC.BestPred.From = From;
+ ToC.BestPred.Weight = Weight;
+ }
}
for (Cluster &C : Clusters)
C.InitialWeight = C.Weight;
@@ -146,9 +149,7 @@ CallGraphSort::CallGraphSort() {
// It's bad to merge clusters which would degrade the density too much.
static bool isNewDensityBad(Cluster &A, Cluster &B) {
double NewDensity = double(A.Weight + B.Weight) / double(A.Size + B.Size);
- if (NewDensity < A.getDensity() / MAX_DENSITY_DEGRADATION)
- return true;
- return false;
+ return NewDensity < A.getDensity() / MAX_DENSITY_DEGRADATION;
}
static void mergeClusters(Cluster &Into, Cluster &From) {
@@ -167,9 +168,9 @@ void CallGraphSort::groupClusters() {
std::vector<int> SortedSecs(Clusters.size());
std::vector<Cluster *> SecToCluster(Clusters.size());
- for (int SI = 0, SE = Clusters.size(); SI != SE; ++SI) {
- SortedSecs[SI] = SI;
- SecToCluster[SI] = &Clusters[SI];
+ for (size_t I = 0; I < Clusters.size(); ++I) {
+ SortedSecs[I] = I;
+ SecToCluster[I] = &Clusters[I];
}
std::stable_sort(SortedSecs.begin(), SortedSecs.end(), [&](int A, int B) {
@@ -181,21 +182,11 @@ void CallGraphSort::groupClusters() {
// been merged into another cluster yet.
Cluster &C = Clusters[SI];
- int BestPred = -1;
- uint64_t BestWeight = 0;
-
- for (Edge &E : C.Preds) {
- if (BestPred == -1 || E.Weight > BestWeight) {
- BestPred = E.From;
- BestWeight = E.Weight;
- }
- }
-
- // don't consider merging if the edge is unlikely.
- if (BestWeight * 10 <= C.InitialWeight)
+ // Don't consider merging if the edge is unlikely.
+ if (C.BestPred.From == -1 || C.BestPred.Weight * 10 <= C.InitialWeight)
continue;
- Cluster *PredC = SecToCluster[BestPred];
+ Cluster *PredC = SecToCluster[C.BestPred.From];
if (PredC == &C)
continue;
@@ -229,7 +220,7 @@ DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
groupClusters();
// Generate order.
- llvm::DenseMap<const InputSectionBase *, int> OrderMap;
+ DenseMap<const InputSectionBase *, int> OrderMap;
ssize_t CurOrder = 1;
for (const Cluster &C : Clusters)
diff --git a/ELF/Config.h b/ELF/Config.h
index 622324c13e2d..8fb760e592eb 100644
--- a/ELF/Config.h
+++ b/ELF/Config.h
@@ -47,7 +47,7 @@ enum class ICFLevel { None, Safe, All };
enum class StripPolicy { None, All, Debug };
// For --unresolved-symbols.
-enum class UnresolvedPolicy { ReportError, Warn, Ignore, IgnoreAll };
+enum class UnresolvedPolicy { ReportError, Warn, Ignore };
// For --orphan-handling.
enum class OrphanHandlingPolicy { Place, Warn, Error };
@@ -127,6 +127,7 @@ struct Configuration {
bool AsNeeded = false;
bool Bsymbolic;
bool BsymbolicFunctions;
+ bool CallGraphProfileSort;
bool CheckSections;
bool CompressDebugSections;
bool Cref;
@@ -134,11 +135,13 @@ struct Configuration {
bool Demangle = true;
bool DisableVerify;
bool EhFrameHdr;
+ bool EmitLLVM;
bool EmitRelocs;
bool EnableNewDtags;
bool ExecuteOnly;
bool ExportDynamic;
bool FixCortexA53Errata843419;
+ bool FormatBinary = false;
bool GcSections;
bool GdbIndex;
bool GnuHash = false;
@@ -170,19 +173,24 @@ struct Configuration {
bool Trace;
bool ThinLTOEmitImportsFiles;
bool ThinLTOIndexOnly;
+ bool TocOptimize;
bool UndefinedVersion;
bool UseAndroidRelrTags = false;
bool WarnBackrefs;
bool WarnCommon;
+ bool WarnIfuncTextrel;
bool WarnMissingEntry;
bool WarnSymbolOrdering;
bool WriteAddends;
bool ZCombreloc;
bool ZCopyreloc;
bool ZExecstack;
+ bool ZGlobal;
bool ZHazardplt;
bool ZInitfirst;
+ bool ZInterpose;
bool ZKeepTextSectionPrefix;
+ bool ZNodefaultlib;
bool ZNodelete;
bool ZNodlopen;
bool ZNow;
@@ -212,6 +220,7 @@ struct Configuration {
unsigned LTOO;
unsigned Optimize;
unsigned ThinLTOJobs;
+ int32_t SplitStackAdjustSize;
// The following config options do not directly correspond to any
// particualr command line options.
diff --git a/ELF/GdbIndex.cpp b/ELF/DWARF.cpp
index 85449a200647..17e1a4d600eb 100644
--- a/ELF/GdbIndex.cpp
+++ b/ELF/DWARF.cpp
@@ -1,4 +1,4 @@
-//===- GdbIndex.cpp -------------------------------------------------------===//
+//===- DWARF.cpp ----------------------------------------------------------===//
//
// The LLVM Linker
//
@@ -14,8 +14,9 @@
//
//===----------------------------------------------------------------------===//
-#include "GdbIndex.h"
+#include "DWARF.h"
#include "Symbols.h"
+#include "Target.h"
#include "lld/Common/Memory.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h"
#include "llvm/Object/ELFObjectFile.h"
@@ -29,24 +30,28 @@ template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *Obj) {
for (InputSectionBase *Sec : Obj->getSections()) {
if (!Sec)
continue;
- if (LLDDWARFSection *M = StringSwitch<LLDDWARFSection *>(Sec->Name)
- .Case(".debug_info", &InfoSection)
- .Case(".debug_ranges", &RangeSection)
- .Case(".debug_line", &LineSection)
- .Default(nullptr)) {
- Sec->maybeDecompress();
- M->Data = toStringRef(Sec->Data);
+
+ if (LLDDWARFSection *M =
+ StringSwitch<LLDDWARFSection *>(Sec->Name)
+ .Case(".debug_addr", &AddrSection)
+ .Case(".debug_gnu_pubnames", &GnuPubNamesSection)
+ .Case(".debug_gnu_pubtypes", &GnuPubTypesSection)
+ .Case(".debug_info", &InfoSection)
+ .Case(".debug_ranges", &RangeSection)
+ .Case(".debug_rnglists", &RngListsSection)
+ .Case(".debug_line", &LineSection)
+ .Default(nullptr)) {
+ M->Data = toStringRef(Sec->data());
M->Sec = Sec;
continue;
}
+
if (Sec->Name == ".debug_abbrev")
- AbbrevSection = toStringRef(Sec->Data);
- else if (Sec->Name == ".debug_gnu_pubnames")
- GnuPubNamesSection = toStringRef(Sec->Data);
- else if (Sec->Name == ".debug_gnu_pubtypes")
- GnuPubTypesSection = toStringRef(Sec->Data);
+ AbbrevSection = toStringRef(Sec->data());
else if (Sec->Name == ".debug_str")
- StrSection = toStringRef(Sec->Data);
+ StrSection = toStringRef(Sec->data());
+ else if (Sec->Name == ".debug_line_str")
+ LineStringSection = toStringRef(Sec->data());
}
}
@@ -73,7 +78,10 @@ LLDDwarfObj<ELFT>::findAux(const InputSectionBase &Sec, uint64_t Pos,
// Broken debug info can point to a non-Defined symbol.
auto *DR = dyn_cast<Defined>(&File->getRelocTargetSym(Rel));
if (!DR) {
- error("unsupported relocation target while parsing debug info");
+ RelType Type = Rel.getType(Config->IsMips64EL);
+ if (Type != Target->NoneRel)
+ error(toString(File) + ": relocation " + lld::toString(Type) + " at 0x" +
+ llvm::utohexstr(Rel.r_offset) + " has unsupported target");
return None;
}
uint64_t Val = DR->Value + getAddend<ELFT>(Rel);
diff --git a/ELF/GdbIndex.h b/ELF/DWARF.h
index eba1ba22f879..8ecf02c77fb4 100644
--- a/ELF/GdbIndex.h
+++ b/ELF/DWARF.h
@@ -1,4 +1,4 @@
-//===- GdbIndex.h --------------------------------------------*- C++ -*-===//
+//===- DWARF.h -----------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
@@ -7,10 +7,11 @@
//
//===-------------------------------------------------------------------===//
-#ifndef LLD_ELF_GDB_INDEX_H
-#define LLD_ELF_GDB_INDEX_H
+#ifndef LLD_ELF_DWARF_H
+#define LLD_ELF_DWARF_H
#include "InputFiles.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/Object/ELF.h"
@@ -24,44 +25,66 @@ struct LLDDWARFSection final : public llvm::DWARFSection {
};
template <class ELFT> class LLDDwarfObj final : public llvm::DWARFObject {
- LLDDWARFSection InfoSection;
- LLDDWARFSection RangeSection;
- LLDDWARFSection LineSection;
- StringRef AbbrevSection;
- StringRef GnuPubNamesSection;
- StringRef GnuPubTypesSection;
- StringRef StrSection;
-
- template <class RelTy>
- llvm::Optional<llvm::RelocAddrEntry> findAux(const InputSectionBase &Sec,
- uint64_t Pos,
- ArrayRef<RelTy> Rels) const;
-
public:
explicit LLDDwarfObj(ObjFile<ELFT> *Obj);
- const llvm::DWARFSection &getInfoSection() const override {
- return InfoSection;
+
+ void forEachInfoSections(
+ llvm::function_ref<void(const llvm::DWARFSection &)> F) const override {
+ F(InfoSection);
}
+
const llvm::DWARFSection &getRangeSection() const override {
return RangeSection;
}
+
+ const llvm::DWARFSection &getRnglistsSection() const override {
+ return RngListsSection;
+ }
+
const llvm::DWARFSection &getLineSection() const override {
return LineSection;
}
- StringRef getFileName() const override { return ""; }
- StringRef getAbbrevSection() const override { return AbbrevSection; }
- StringRef getStringSection() const override { return StrSection; }
- StringRef getGnuPubNamesSection() const override {
+
+ const llvm::DWARFSection &getAddrSection() const override {
+ return AddrSection;
+ }
+
+ const llvm::DWARFSection &getGnuPubNamesSection() const override {
return GnuPubNamesSection;
}
- StringRef getGnuPubTypesSection() const override {
+
+ const llvm::DWARFSection &getGnuPubTypesSection() const override {
return GnuPubTypesSection;
}
+
+ StringRef getFileName() const override { return ""; }
+ StringRef getAbbrevSection() const override { return AbbrevSection; }
+ StringRef getStringSection() const override { return StrSection; }
+ StringRef getLineStringSection() const override { return LineStringSection; }
+
bool isLittleEndian() const override {
return ELFT::TargetEndianness == llvm::support::little;
}
+
llvm::Optional<llvm::RelocAddrEntry> find(const llvm::DWARFSection &Sec,
uint64_t Pos) const override;
+
+private:
+ template <class RelTy>
+ llvm::Optional<llvm::RelocAddrEntry> findAux(const InputSectionBase &Sec,
+ uint64_t Pos,
+ ArrayRef<RelTy> Rels) const;
+
+ LLDDWARFSection GnuPubNamesSection;
+ LLDDWARFSection GnuPubTypesSection;
+ LLDDWARFSection InfoSection;
+ LLDDWARFSection RangeSection;
+ LLDDWARFSection RngListsSection;
+ LLDDWARFSection LineSection;
+ LLDDWARFSection AddrSection;
+ StringRef AbbrevSection;
+ StringRef StrSection;
+ StringRef LineStringSection;
};
} // namespace elf
diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp
index 693dba64ab5a..13b6119e2dc9 100644
--- a/ELF/Driver.cpp
+++ b/ELF/Driver.cpp
@@ -63,6 +63,7 @@ using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
using namespace llvm::sys;
+using namespace llvm::support;
using namespace lld;
using namespace lld::elf;
@@ -74,7 +75,7 @@ static void setConfigs(opt::InputArgList &Args);
bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly,
raw_ostream &Error) {
- errorHandler().LogName = sys::path::filename(Args[0]);
+ errorHandler().LogName = args::getFilenameWithoutExe(Args[0]);
errorHandler().ErrorLimitExceededMsg =
"too many errors emitted, stopping now (use "
"-error-limit=0 to see all errors)";
@@ -84,7 +85,6 @@ bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly,
InputSections.clear();
OutputSections.clear();
- Tar = nullptr;
BinaryFiles.clear();
BitcodeFiles.clear();
ObjectFiles.clear();
@@ -94,6 +94,10 @@ bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly,
Driver = make<LinkerDriver>();
Script = make<LinkerScript>();
Symtab = make<SymbolTable>();
+
+ Tar = nullptr;
+ memset(&In, 0, sizeof(In));
+
Config->ProgName = Args[0];
Driver->main(Args);
@@ -125,9 +129,11 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
.Case("elf32_x86_64", {ELF32LEKind, EM_X86_64})
.Cases("elf32btsmip", "elf32btsmipn32", {ELF32BEKind, EM_MIPS})
.Cases("elf32ltsmip", "elf32ltsmipn32", {ELF32LEKind, EM_MIPS})
+ .Case("elf32lriscv", {ELF32LEKind, EM_RISCV})
.Case("elf32ppc", {ELF32BEKind, EM_PPC})
.Case("elf64btsmip", {ELF64BEKind, EM_MIPS})
.Case("elf64ltsmip", {ELF64LEKind, EM_MIPS})
+ .Case("elf64lriscv", {ELF64LEKind, EM_RISCV})
.Case("elf64ppc", {ELF64BEKind, EM_PPC64})
.Case("elf64lppc", {ELF64LEKind, EM_PPC64})
.Cases("elf_amd64", "elf_x86_64", {ELF64LEKind, EM_X86_64})
@@ -183,7 +189,7 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) {
return;
MemoryBufferRef MBRef = *Buffer;
- if (InBinary) {
+ if (Config->FormatBinary) {
Files.push_back(make<BinaryFile>(MBRef));
return;
}
@@ -218,7 +224,7 @@ void LinkerDriver::addFile(StringRef Path, bool WithLOption) {
return;
}
case file_magic::elf_shared_object:
- if (Config->Relocatable) {
+ if (Config->Static || Config->Relocatable) {
error("attempted static link of dynamic object " + Path);
return;
}
@@ -269,14 +275,17 @@ static void initLLVM() {
// Some command line options or some combinations of them are not allowed.
// This function checks for such errors.
-static void checkOptions(opt::InputArgList &Args) {
+static void checkOptions() {
// The MIPS ABI as of 2016 does not support the GNU-style symbol lookup
// table which is a relatively new feature.
if (Config->EMachine == EM_MIPS && Config->GnuHash)
- error("the .gnu.hash section is not compatible with the MIPS target.");
+ error("the .gnu.hash section is not compatible with the MIPS target");
if (Config->FixCortexA53Errata843419 && Config->EMachine != EM_AARCH64)
- error("--fix-cortex-a53-843419 is only supported on AArch64 targets.");
+ error("--fix-cortex-a53-843419 is only supported on AArch64 targets");
+
+ if (Config->TocOptimize && Config->EMachine != EM_PPC64)
+ error("--toc-optimize is only supported on the PowerPC64 target");
if (Config->Pie && Config->Shared)
error("-shared and -pie may not be used together");
@@ -336,12 +345,13 @@ static bool getZFlag(opt::InputArgList &Args, StringRef K1, StringRef K2,
return Default;
}
-static bool isKnown(StringRef S) {
+static bool isKnownZFlag(StringRef S) {
return S == "combreloc" || S == "copyreloc" || S == "defs" ||
- S == "execstack" || S == "hazardplt" || S == "initfirst" ||
+ S == "execstack" || S == "global" || S == "hazardplt" ||
+ S == "initfirst" || S == "interpose" ||
S == "keep-text-section-prefix" || S == "lazy" || S == "muldefs" ||
- S == "nocombreloc" || S == "nocopyreloc" || S == "nodelete" ||
- S == "nodlopen" || S == "noexecstack" ||
+ S == "nocombreloc" || S == "nocopyreloc" || S == "nodefaultlib" ||
+ S == "nodelete" || S == "nodlopen" || S == "noexecstack" ||
S == "nokeep-text-section-prefix" || S == "norelro" || S == "notext" ||
S == "now" || S == "origin" || S == "relro" || S == "retpolineplt" ||
S == "rodynamic" || S == "text" || S == "wxneeded" ||
@@ -351,7 +361,7 @@ static bool isKnown(StringRef S) {
// Report an error for an unknown -z option.
static void checkZOptions(opt::InputArgList &Args) {
for (auto *Arg : Args.filtered(OPT_z))
- if (!isKnown(Arg->getValue()))
+ if (!isKnownZFlag(Arg->getValue()))
error("unknown -z value: " + StringRef(Arg->getValue()));
}
@@ -386,31 +396,30 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr) {
if (Args.hasArg(OPT_v) || Args.hasArg(OPT_version))
message(getLLDVersion() + " (compatible with GNU linkers)");
- // The behavior of -v or --version is a bit strange, but this is
- // needed for compatibility with GNU linkers.
- if (Args.hasArg(OPT_v) && !Args.hasArg(OPT_INPUT))
- return;
- if (Args.hasArg(OPT_version))
- return;
-
if (const char *Path = getReproduceOption(Args)) {
// Note that --reproduce is a debug option so you can ignore it
// if you are trying to understand the whole picture of the code.
Expected<std::unique_ptr<TarWriter>> ErrOrWriter =
TarWriter::create(Path, path::stem(Path));
if (ErrOrWriter) {
- Tar = ErrOrWriter->get();
+ Tar = std::move(*ErrOrWriter);
Tar->append("response.txt", createResponseFile(Args));
Tar->append("version.txt", getLLDVersion() + "\n");
- make<std::unique_ptr<TarWriter>>(std::move(*ErrOrWriter));
} else {
- error(Twine("--reproduce: failed to open ") + Path + ": " +
- toString(ErrOrWriter.takeError()));
+ error("--reproduce: " + toString(ErrOrWriter.takeError()));
}
}
readConfigs(Args);
checkZOptions(Args);
+
+ // The behavior of -v or --version is a bit strange, but this is
+ // needed for compatibility with GNU linkers.
+ if (Args.hasArg(OPT_v) && !Args.hasArg(OPT_INPUT))
+ return;
+ if (Args.hasArg(OPT_version))
+ return;
+
initLLVM();
createFiles(Args);
if (errorCount())
@@ -418,7 +427,7 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr) {
inferMachineType();
setConfigs(Args);
- checkOptions(Args);
+ checkOptions();
if (errorCount())
return;
@@ -448,9 +457,6 @@ static std::string getRpath(opt::InputArgList &Args) {
// Determines what we should do if there are remaining unresolved
// symbols after the name resolution.
static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &Args) {
- if (Args.hasArg(OPT_relocatable))
- return UnresolvedPolicy::IgnoreAll;
-
UnresolvedPolicy ErrorOrWarn = Args.hasFlag(OPT_error_unresolved_symbols,
OPT_warn_unresolved_symbols, true)
? UnresolvedPolicy::ReportError
@@ -497,14 +503,11 @@ static Target2Policy getTarget2(opt::InputArgList &Args) {
}
static bool isOutputFormatBinary(opt::InputArgList &Args) {
- if (auto *Arg = Args.getLastArg(OPT_oformat)) {
- StringRef S = Arg->getValue();
- if (S == "binary")
- return true;
- if (S.startswith("elf"))
- return false;
+ StringRef S = Args.getLastArgValue(OPT_oformat, "elf");
+ if (S == "binary")
+ return true;
+ if (!S.startswith("elf"))
error("unknown --oformat value: " + S);
- }
return false;
}
@@ -645,38 +648,56 @@ static std::pair<bool, bool> getPackDynRelocs(opt::InputArgList &Args) {
static void readCallGraph(MemoryBufferRef MB) {
// Build a map from symbol name to section
- DenseMap<StringRef, const Symbol *> SymbolNameToSymbol;
+ DenseMap<StringRef, Symbol *> Map;
for (InputFile *File : ObjectFiles)
for (Symbol *Sym : File->getSymbols())
- SymbolNameToSymbol[Sym->getName()] = Sym;
+ Map[Sym->getName()] = Sym;
+
+ auto FindSection = [&](StringRef Name) -> InputSectionBase * {
+ Symbol *Sym = Map.lookup(Name);
+ if (!Sym) {
+ if (Config->WarnSymbolOrdering)
+ warn(MB.getBufferIdentifier() + ": no such symbol: " + Name);
+ return nullptr;
+ }
+ maybeWarnUnorderableSymbol(Sym);
+
+ if (Defined *DR = dyn_cast_or_null<Defined>(Sym))
+ return dyn_cast_or_null<InputSectionBase>(DR->Section);
+ return nullptr;
+ };
- for (StringRef L : args::getLines(MB)) {
+ for (StringRef Line : args::getLines(MB)) {
SmallVector<StringRef, 3> Fields;
- L.split(Fields, ' ');
+ Line.split(Fields, ' ');
uint64_t Count;
- if (Fields.size() != 3 || !to_integer(Fields[2], Count))
- fatal(MB.getBufferIdentifier() + ": parse error");
- const Symbol *FromSym = SymbolNameToSymbol.lookup(Fields[0]);
- const Symbol *ToSym = SymbolNameToSymbol.lookup(Fields[1]);
- if (Config->WarnSymbolOrdering) {
- if (!FromSym)
- warn(MB.getBufferIdentifier() + ": no such symbol: " + Fields[0]);
- if (!ToSym)
- warn(MB.getBufferIdentifier() + ": no such symbol: " + Fields[1]);
+
+ if (Fields.size() != 3 || !to_integer(Fields[2], Count)) {
+ error(MB.getBufferIdentifier() + ": parse error");
+ return;
+ }
+
+ if (InputSectionBase *From = FindSection(Fields[0]))
+ if (InputSectionBase *To = FindSection(Fields[1]))
+ Config->CallGraphProfile[std::make_pair(From, To)] += Count;
+ }
+}
+
+template <class ELFT> static void readCallGraphsFromObjectFiles() {
+ for (auto File : ObjectFiles) {
+ auto *Obj = cast<ObjFile<ELFT>>(File);
+
+ for (const Elf_CGProfile_Impl<ELFT> &CGPE : Obj->CGProfile) {
+ auto *FromSym = dyn_cast<Defined>(&Obj->getSymbol(CGPE.cgp_from));
+ auto *ToSym = dyn_cast<Defined>(&Obj->getSymbol(CGPE.cgp_to));
+ if (!FromSym || !ToSym)
+ continue;
+
+ auto *From = dyn_cast_or_null<InputSectionBase>(FromSym->Section);
+ auto *To = dyn_cast_or_null<InputSectionBase>(ToSym->Section);
+ if (From && To)
+ Config->CallGraphProfile[{From, To}] += CGPE.cgp_weight;
}
- if (!FromSym || !ToSym || Count == 0)
- continue;
- warnUnorderableSymbol(FromSym);
- warnUnorderableSymbol(ToSym);
- const Defined *FromSymD = dyn_cast<Defined>(FromSym);
- const Defined *ToSymD = dyn_cast<Defined>(ToSym);
- if (!FromSymD || !ToSymD)
- continue;
- const auto *FromSB = dyn_cast_or_null<InputSectionBase>(FromSymD->Section);
- const auto *ToSB = dyn_cast_or_null<InputSectionBase>(ToSymD->Section);
- if (!FromSB || !ToSB)
- continue;
- Config->CallGraphProfile[std::make_pair(FromSB, ToSB)] += Count;
}
}
@@ -753,7 +774,10 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
Config->DynamicLinker = getDynamicLinker(Args);
Config->EhFrameHdr =
Args.hasFlag(OPT_eh_frame_hdr, OPT_no_eh_frame_hdr, false);
+ Config->EmitLLVM = Args.hasArg(OPT_plugin_opt_emit_llvm, false);
Config->EmitRelocs = Args.hasArg(OPT_emit_relocs);
+ Config->CallGraphProfileSort = Args.hasFlag(
+ OPT_call_graph_profile_sort, OPT_no_call_graph_profile_sort, true);
Config->EnableNewDtags =
Args.hasFlag(OPT_enable_new_dtags, OPT_disable_new_dtags, true);
Config->Entry = Args.getLastArgValue(OPT_entry);
@@ -808,6 +832,7 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
Config->SingleRoRx = Args.hasArg(OPT_no_rosegment);
Config->SoName = Args.getLastArgValue(OPT_soname);
Config->SortSection = getSortSection(Args);
+ Config->SplitStackAdjustSize = args::getInteger(Args, OPT_split_stack_adjust_size, 16384);
Config->Strip = getStrip(Args);
Config->Sysroot = Args.getLastArgValue(OPT_sysroot);
Config->Target1Rel = Args.hasFlag(OPT_target1_rel, OPT_target1_abs, false);
@@ -837,15 +862,20 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
Config->WarnBackrefs =
Args.hasFlag(OPT_warn_backrefs, OPT_no_warn_backrefs, false);
Config->WarnCommon = Args.hasFlag(OPT_warn_common, OPT_no_warn_common, false);
+ Config->WarnIfuncTextrel =
+ Args.hasFlag(OPT_warn_ifunc_textrel, OPT_no_warn_ifunc_textrel, false);
Config->WarnSymbolOrdering =
Args.hasFlag(OPT_warn_symbol_ordering, OPT_no_warn_symbol_ordering, true);
Config->ZCombreloc = getZFlag(Args, "combreloc", "nocombreloc", true);
Config->ZCopyreloc = getZFlag(Args, "copyreloc", "nocopyreloc", true);
Config->ZExecstack = getZFlag(Args, "execstack", "noexecstack", false);
+ Config->ZGlobal = hasZOption(Args, "global");
Config->ZHazardplt = hasZOption(Args, "hazardplt");
Config->ZInitfirst = hasZOption(Args, "initfirst");
+ Config->ZInterpose = hasZOption(Args, "interpose");
Config->ZKeepTextSectionPrefix = getZFlag(
Args, "keep-text-section-prefix", "nokeep-text-section-prefix", false);
+ Config->ZNodefaultlib = hasZOption(Args, "nodefaultlib");
Config->ZNodelete = hasZOption(Args, "nodelete");
Config->ZNodlopen = hasZOption(Args, "nodlopen");
Config->ZNow = getZFlag(Args, "now", "lazy", false);
@@ -876,6 +906,9 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
if (Config->ThinLTOJobs == 0)
error("--thinlto-jobs: number of threads must be > 0");
+ if (Config->SplitStackAdjustSize < 0)
+ error("--split-stack-adjust-size: size must be >= 0");
+
// Parse ELF{32,64}{LE,BE} and CPU type.
if (auto *Arg = Args.getLastArg(OPT_m)) {
StringRef S = Arg->getValue();
@@ -964,22 +997,17 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
// This function initialize such members. See Config.h for the details
// of these values.
static void setConfigs(opt::InputArgList &Args) {
- ELFKind Kind = Config->EKind;
- uint16_t Machine = Config->EMachine;
+ ELFKind K = Config->EKind;
+ uint16_t M = Config->EMachine;
Config->CopyRelocs = (Config->Relocatable || Config->EmitRelocs);
- Config->Is64 = (Kind == ELF64LEKind || Kind == ELF64BEKind);
- Config->IsLE = (Kind == ELF32LEKind || Kind == ELF64LEKind);
- Config->Endianness =
- Config->IsLE ? support::endianness::little : support::endianness::big;
- Config->IsMips64EL = (Kind == ELF64LEKind && Machine == EM_MIPS);
+ Config->Is64 = (K == ELF64LEKind || K == ELF64BEKind);
+ Config->IsLE = (K == ELF32LEKind || K == ELF64LEKind);
+ Config->Endianness = Config->IsLE ? endianness::little : endianness::big;
+ Config->IsMips64EL = (K == ELF64LEKind && M == EM_MIPS);
Config->Pic = Config->Pie || Config->Shared;
Config->Wordsize = Config->Is64 ? 8 : 4;
- // There is an ILP32 ABI for x86-64, although it's not very popular.
- // It is called the x32 ABI.
- bool IsX32 = (Kind == ELF32LEKind && Machine == EM_X86_64);
-
// ELF defines two different ways to store relocation addends as shown below:
//
// Rel: Addends are stored to the location where relocations are applied.
@@ -993,8 +1021,9 @@ static void setConfigs(opt::InputArgList &Args) {
// You cannot choose which one, Rel or Rela, you want to use. Instead each
// ABI defines which one you need to use. The following expression expresses
// that.
- Config->IsRela =
- (Config->Is64 || IsX32 || Machine == EM_PPC) && Machine != EM_MIPS;
+ Config->IsRela = M == EM_AARCH64 || M == EM_AMDGPU || M == EM_HEXAGON ||
+ M == EM_PPC || M == EM_PPC64 || M == EM_RISCV ||
+ M == EM_X86_64;
// If the output uses REL relocations we must store the dynamic relocation
// addends to the output sections. We also store addends for RELA relocations
@@ -1004,10 +1033,13 @@ static void setConfigs(opt::InputArgList &Args) {
Config->WriteAddends = Args.hasFlag(OPT_apply_dynamic_relocs,
OPT_no_apply_dynamic_relocs, false) ||
!Config->IsRela;
+
+ Config->TocOptimize =
+ Args.hasFlag(OPT_toc_optimize, OPT_no_toc_optimize, M == EM_PPC64);
}
// Returns a value of "-format" option.
-static bool getBinaryOption(StringRef S) {
+static bool isFormatBinary(StringRef S) {
if (S == "binary")
return true;
if (S == "elf" || S == "default")
@@ -1034,7 +1066,10 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) {
StringRef From;
StringRef To;
std::tie(From, To) = StringRef(Arg->getValue()).split('=');
- readDefsym(From, MemoryBufferRef(To, "-defsym"));
+ if (From.empty() || To.empty())
+ error("-defsym: syntax error: " + StringRef(Arg->getValue()));
+ else
+ readDefsym(From, MemoryBufferRef(To, "-defsym"));
break;
}
case OPT_script:
@@ -1049,7 +1084,7 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) {
Config->AsNeeded = true;
break;
case OPT_format:
- InBinary = getBinaryOption(Arg->getValue());
+ Config->FormatBinary = isFormatBinary(Arg->getValue());
break;
case OPT_no_as_needed:
Config->AsNeeded = false;
@@ -1220,33 +1255,34 @@ template <class ELFT> static void handleUndefined(StringRef Name) {
Symtab->fetchLazy<ELFT>(Sym);
}
-template <class ELFT> static bool shouldDemote(Symbol &Sym) {
- // If all references to a DSO happen to be weak, the DSO is not added to
- // DT_NEEDED. If that happens, we need to eliminate shared symbols created
- // from the DSO. Otherwise, they become dangling references that point to a
- // non-existent DSO.
- if (auto *S = dyn_cast<SharedSymbol>(&Sym))
- return !S->getFile<ELFT>().IsNeeded;
-
- // We are done processing archives, so lazy symbols that were used but not
- // found can be converted to undefined. We could also just delete the other
- // lazy symbols, but that seems to be more work than it is worth.
- return Sym.isLazy() && Sym.IsUsedInRegularObj;
+template <class ELFT> static void handleLibcall(StringRef Name) {
+ Symbol *Sym = Symtab->find(Name);
+ if (!Sym || !Sym->isLazy())
+ return;
+
+ MemoryBufferRef MB;
+ if (auto *LO = dyn_cast<LazyObject>(Sym))
+ MB = LO->File->MB;
+ else
+ MB = cast<LazyArchive>(Sym)->getMemberBuffer();
+
+ if (isBitcode(MB))
+ Symtab->fetchLazy<ELFT>(Sym);
}
-// Some files, such as .so or files between -{start,end}-lib may be removed
-// after their symbols are added to the symbol table. If that happens, we
-// need to remove symbols that refer files that no longer exist, so that
-// they won't appear in the symbol table of the output file.
-//
-// We remove symbols by demoting them to undefined symbol.
-template <class ELFT> static void demoteSymbols() {
+// If all references to a DSO happen to be weak, the DSO is not added
+// to DT_NEEDED. If that happens, we need to eliminate shared symbols
+// created from the DSO. Otherwise, they become dangling references
+// that point to a non-existent DSO.
+template <class ELFT> static void demoteSharedSymbols() {
for (Symbol *Sym : Symtab->getSymbols()) {
- if (shouldDemote<ELFT>(*Sym)) {
- bool Used = Sym->Used;
- replaceSymbol<Undefined>(Sym, nullptr, Sym->getName(), Sym->Binding,
- Sym->StOther, Sym->Type);
- Sym->Used = Used;
+ if (auto *S = dyn_cast<SharedSymbol>(Sym)) {
+ if (!S->getFile<ELFT>().IsNeeded) {
+ bool Used = S->Used;
+ replaceSymbol<Undefined>(S, nullptr, S->getName(), STB_WEAK, S->StOther,
+ S->Type);
+ S->Used = Used;
+ }
}
}
}
@@ -1315,6 +1351,85 @@ static void findKeepUniqueSections(opt::InputArgList &Args) {
}
}
+template <class ELFT> static Symbol *addUndefined(StringRef Name) {
+ return Symtab->addUndefined<ELFT>(Name, STB_GLOBAL, STV_DEFAULT, 0, false,
+ nullptr);
+}
+
+// The --wrap option is a feature to rename symbols so that you can write
+// wrappers for existing functions. If you pass `-wrap=foo`, all
+// occurrences of symbol `foo` are resolved to `wrap_foo` (so, you are
+// expected to write `wrap_foo` function as a wrapper). The original
+// symbol becomes accessible as `real_foo`, so you can call that from your
+// wrapper.
+//
+// This data structure is instantiated for each -wrap option.
+struct WrappedSymbol {
+ Symbol *Sym;
+ Symbol *Real;
+ Symbol *Wrap;
+};
+
+// Handles -wrap option.
+//
+// This function instantiates wrapper symbols. At this point, they seem
+// like they are not being used at all, so we explicitly set some flags so
+// that LTO won't eliminate them.
+template <class ELFT>
+static std::vector<WrappedSymbol> addWrappedSymbols(opt::InputArgList &Args) {
+ std::vector<WrappedSymbol> V;
+ DenseSet<StringRef> Seen;
+
+ for (auto *Arg : Args.filtered(OPT_wrap)) {
+ StringRef Name = Arg->getValue();
+ if (!Seen.insert(Name).second)
+ continue;
+
+ Symbol *Sym = Symtab->find(Name);
+ if (!Sym)
+ continue;
+
+ Symbol *Real = addUndefined<ELFT>(Saver.save("__real_" + Name));
+ Symbol *Wrap = addUndefined<ELFT>(Saver.save("__wrap_" + Name));
+ V.push_back({Sym, Real, Wrap});
+
+ // We want to tell LTO not to inline symbols to be overwritten
+ // because LTO doesn't know the final symbol contents after renaming.
+ Real->CanInline = false;
+ Sym->CanInline = false;
+
+ // Tell LTO not to eliminate these symbols.
+ Sym->IsUsedInRegularObj = true;
+ Wrap->IsUsedInRegularObj = true;
+ }
+ return V;
+}
+
+// Do renaming for -wrap by updating pointers to symbols.
+//
+// When this function is executed, only InputFiles and symbol table
+// contain pointers to symbol objects. We visit them to replace pointers,
+// so that wrapped symbols are swapped as instructed by the command line.
+template <class ELFT> static void wrapSymbols(ArrayRef<WrappedSymbol> Wrapped) {
+ DenseMap<Symbol *, Symbol *> Map;
+ for (const WrappedSymbol &W : Wrapped) {
+ Map[W.Sym] = W.Wrap;
+ Map[W.Real] = W.Sym;
+ }
+
+ // Update pointers in input files.
+ parallelForEach(ObjectFiles, [&](InputFile *File) {
+ std::vector<Symbol *> &Syms = File->getMutableSymbols();
+ for (size_t I = 0, E = Syms.size(); I != E; ++I)
+ if (Symbol *S = Map.lookup(Syms[I]))
+ Syms[I] = S;
+ });
+
+ // Update pointers in the symbol table.
+ for (const WrappedSymbol &W : Wrapped)
+ Symtab->wrap(W.Sym, W.Real, W.Wrap);
+}
+
static const char *LibcallRoutineNames[] = {
#define HANDLE_LIBCALL(code, name) name,
#include "llvm/IR/RuntimeLibcalls.def"
@@ -1325,6 +1440,8 @@ static const char *LibcallRoutineNames[] = {
// all linker scripts have already been parsed.
template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
Target = getTarget();
+ InX<ELFT>::VerSym = nullptr;
+ InX<ELFT>::VerNeed = nullptr;
Config->MaxPageSize = getMaxPageSize(Args);
Config->ImageBase = getImageBase(Args);
@@ -1380,8 +1497,8 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
// Some symbols (such as __ehdr_start) are defined lazily only when there
// are undefined symbols for them, so we add these to trigger that logic.
- for (StringRef Sym : Script->ReferencedSymbols)
- Symtab->addUndefined<ELFT>(Sym);
+ for (StringRef Name : Script->ReferencedSymbols)
+ addUndefined<ELFT>(Name);
// Handle the `--undefined <sym>` options.
for (StringRef S : Config->Undefined)
@@ -1396,11 +1513,20 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
// in a bitcode file in an archive member, we need to arrange to use LTO to
// compile those archive members by adding them to the link beforehand.
//
- // With this the symbol table should be complete. After this, no new names
- // except a few linker-synthesized ones will be added to the symbol table.
+ // However, adding all libcall symbols to the link can have undesired
+ // consequences. For example, the libgcc implementation of
+ // __sync_val_compare_and_swap_8 on 32-bit ARM pulls in an .init_array entry
+ // that aborts the program if the Linux kernel does not support 64-bit
+ // atomics, which would prevent the program from running even if it does not
+ // use 64-bit atomics.
+ //
+ // Therefore, we only add libcall symbols to the link before LTO if we have
+ // to, i.e. if the symbol's definition is in bitcode. Any other required
+ // libcall symbols will be added to the link after LTO when we add the LTO
+ // object file to the link.
if (!BitcodeFiles.empty())
for (const char *S : LibcallRoutineNames)
- handleUndefined<ELFT>(S);
+ handleLibcall<ELFT>(S);
// Return if there were name resolution errors.
if (errorCount())
@@ -1424,6 +1550,9 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
Out::ElfHeader = make<OutputSection>("", 0, SHF_ALLOC);
Out::ElfHeader->Size = sizeof(typename ELFT::Ehdr);
+ // Create wrapped symbols for -wrap option.
+ std::vector<WrappedSymbol> Wrapped = addWrappedSymbols<ELFT>(Args);
+
// We need to create some reserved symbols such as _end. Create them.
if (!Config->Relocatable)
addReservedSymbols();
@@ -1436,12 +1565,11 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
if (!Config->Relocatable)
Symtab->scanVersionScript();
- // Create wrapped symbols for -wrap option.
- for (auto *Arg : Args.filtered(OPT_wrap))
- Symtab->addSymbolWrap<ELFT>(Arg->getValue());
-
// Do link-time optimization if given files are LLVM bitcode files.
// This compiles bitcode files into real object files.
+ //
+ // With this the symbol table should be complete. After this, no new names
+ // except a few linker-synthesized ones will be added to the symbol table.
Symtab->addCombinedLTOObject<ELFT>();
if (errorCount())
return;
@@ -1452,8 +1580,15 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
if (Config->ThinLTOIndexOnly)
return;
+ // Likewise, --plugin-opt=emit-llvm is an option to make LTO create
+ // an output file in bitcode and exit, so that you can just get a
+ // combined bitcode file.
+ if (Config->EmitLLVM)
+ return;
+
// Apply symbol renames for -wrap.
- Symtab->applySymbolWrap();
+ if (!Wrapped.empty())
+ wrapSymbols<ELFT>(Wrapped);
// Now that we have a complete list of input files.
// Beyond this point, no new files are added.
@@ -1481,27 +1616,19 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
// supports them.
if (Config->ARMHasBlx == false)
warn("lld uses blx instruction, no object with architecture supporting "
- "feature detected.");
- if (Config->ARMJ1J2BranchEncoding == false)
- warn("lld uses extended branch encoding, no object with architecture "
- "supporting feature detected.");
- if (Config->ARMHasMovtMovw == false)
- warn("lld may use movt/movw, no object with architecture supporting "
- "feature detected.");
+ "feature detected");
}
// This adds a .comment section containing a version string. We have to add it
- // before decompressAndMergeSections because the .comment section is a
- // mergeable section.
+ // before mergeSections because the .comment section is a mergeable section.
if (!Config->Relocatable)
InputSections.push_back(createCommentSection());
// Do size optimizations: garbage collection, merging of SHF_MERGE sections
// and identical code folding.
- decompressSections();
splitSections<ELFT>();
markLive<ELFT>();
- demoteSymbols<ELFT>();
+ demoteSharedSymbols<ELFT>();
mergeSections();
if (Config->ICF != ICFLevel::None) {
findKeepUniqueSections<ELFT>(Args);
@@ -1509,9 +1636,12 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
}
// Read the callgraph now that we know what was gced or icfed
- if (auto *Arg = Args.getLastArg(OPT_call_graph_ordering_file))
- if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
- readCallGraph(*Buffer);
+ if (Config->CallGraphProfileSort) {
+ if (auto *Arg = Args.getLastArg(OPT_call_graph_ordering_file))
+ if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
+ readCallGraph(*Buffer);
+ readCallGraphsFromObjectFiles<ELFT>();
+ }
// Write the result to the file.
writeResult<ELFT>();
diff --git a/ELF/Driver.h b/ELF/Driver.h
index 99e194d9b66c..81d7f608e588 100644
--- a/ELF/Driver.h
+++ b/ELF/Driver.h
@@ -42,9 +42,6 @@ private:
// True if we are in --start-lib and --end-lib.
bool InLib = false;
- // True if we are in -format=binary and -format=elf.
- bool InBinary = false;
-
std::vector<InputFile *> Files;
};
diff --git a/ELF/DriverUtils.cpp b/ELF/DriverUtils.cpp
index 698e06edfe63..e51d02e38da1 100644
--- a/ELF/DriverUtils.cpp
+++ b/ELF/DriverUtils.cpp
@@ -139,8 +139,9 @@ opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> Argv) {
}
void elf::printHelp() {
- ELFOptTable().PrintHelp(outs(), Config->ProgName.data(), "lld",
- false /*ShowHidden*/, true /*ShowAllAliases*/);
+ ELFOptTable().PrintHelp(
+ outs(), (Config->ProgName + " [options] file...").str().c_str(), "lld",
+ false /*ShowHidden*/, true /*ShowAllAliases*/);
outs() << "\n";
// Scripts generated by Libtool versions up to at least 2.4.6 (the most
diff --git a/ELF/EhFrame.cpp b/ELF/EhFrame.cpp
index 20b32c0a96e6..95d444bdc2a1 100644
--- a/ELF/EhFrame.cpp
+++ b/ELF/EhFrame.cpp
@@ -44,7 +44,7 @@ public:
private:
template <class P> void failOn(const P *Loc, const Twine &Msg) {
fatal("corrupted .eh_frame: " + Msg + "\n>>> defined in " +
- IS->getObjMsg((const uint8_t *)Loc - IS->Data.data()));
+ IS->getObjMsg((const uint8_t *)Loc - IS->data().data()));
}
uint8_t readByte();
@@ -59,7 +59,7 @@ private:
}
size_t elf::readEhRecordSize(InputSectionBase *S, size_t Off) {
- return EhReader(S, S->Data.slice(Off)).readEhRecordSize();
+ return EhReader(S, S->data().slice(Off)).readEhRecordSize();
}
// .eh_frame section is a sequence of records. Each record starts with
diff --git a/ELF/ICF.cpp b/ELF/ICF.cpp
index 075938bd16b9..e917ae76a689 100644
--- a/ELF/ICF.cpp
+++ b/ELF/ICF.cpp
@@ -252,7 +252,10 @@ bool ICF<ELFT>::constantEq(const InputSection *SecA, ArrayRef<RelTy> RA,
auto *DA = dyn_cast<Defined>(&SA);
auto *DB = dyn_cast<Defined>(&SB);
- if (!DA || !DB)
+
+ // Placeholder symbols generated by linker scripts look the same now but
+ // may have different values later.
+ if (!DA || !DB || DA->ScriptDefined || DB->ScriptDefined)
return false;
// Relocations referring to absolute symbols are constant-equal if their
@@ -298,7 +301,7 @@ bool ICF<ELFT>::constantEq(const InputSection *SecA, ArrayRef<RelTy> RA,
template <class ELFT>
bool ICF<ELFT>::equalsConstant(const InputSection *A, const InputSection *B) {
if (A->NumRelocations != B->NumRelocations || A->Flags != B->Flags ||
- A->getSize() != B->getSize() || A->Data != B->Data)
+ A->getSize() != B->getSize() || A->data() != B->data())
return false;
// If two sections have different output sections, we cannot merge them.
@@ -420,6 +423,21 @@ void ICF<ELFT>::forEachClass(llvm::function_ref<void(size_t, size_t)> Fn) {
++Cnt;
}
+// Combine the hashes of the sections referenced by the given section into its
+// hash.
+template <class ELFT, class RelTy>
+static void combineRelocHashes(InputSection *IS, ArrayRef<RelTy> Rels) {
+ uint32_t Hash = IS->Class[1];
+ for (RelTy Rel : Rels) {
+ Symbol &S = IS->template getFile<ELFT>()->getRelocTargetSym(Rel);
+ if (auto *D = dyn_cast<Defined>(&S))
+ if (auto *RelSec = dyn_cast_or_null<InputSection>(D->Section))
+ Hash ^= RelSec->Class[1];
+ }
+ // Set MSB to 1 to avoid collisions with non-hash IDs.
+ IS->Class[0] = Hash | (1U << 31);
+}
+
static void print(const Twine &S) {
if (Config->PrintIcfSections)
message(S);
@@ -435,8 +453,14 @@ template <class ELFT> void ICF<ELFT>::run() {
// Initially, we use hash values to partition sections.
parallelForEach(Sections, [&](InputSection *S) {
- // Set MSB to 1 to avoid collisions with non-hash IDs.
- S->Class[0] = xxHash64(S->Data) | (1U << 31);
+ S->Class[1] = xxHash64(S->data());
+ });
+
+ parallelForEach(Sections, [&](InputSection *S) {
+ if (S->AreRelocsRela)
+ combineRelocHashes<ELFT>(S, S->template relas<ELFT>());
+ else
+ combineRelocHashes<ELFT>(S, S->template rels<ELFT>());
});
// From now on, sections in Sections vector are ordered so that sections
diff --git a/ELF/InputFiles.cpp b/ELF/InputFiles.cpp
index 0eb605a556ae..e4d1dec7cbcb 100644
--- a/ELF/InputFiles.cpp
+++ b/ELF/InputFiles.cpp
@@ -46,7 +46,7 @@ std::vector<LazyObjFile *> elf::LazyObjFiles;
std::vector<InputFile *> elf::ObjectFiles;
std::vector<InputFile *> elf::SharedFiles;
-TarWriter *elf::Tar;
+std::unique_ptr<TarWriter> elf::Tar;
InputFile::InputFile(Kind K, MemoryBufferRef M)
: MB(M), GroupId(NextGroupId), FileKind(K) {
@@ -125,11 +125,7 @@ std::string InputFile::getSrcMsg(const Symbol &Sym, InputSectionBase &Sec,
template <class ELFT> void ObjFile<ELFT>::initializeDwarf() {
Dwarf = llvm::make_unique<DWARFContext>(make_unique<LLDDwarfObj<ELFT>>(this));
- const DWARFObject &Obj = Dwarf->getDWARFObj();
- DWARFDataExtractor LineData(Obj, Obj.getLineSection(), Config->IsLE,
- Config->Wordsize);
-
- for (std::unique_ptr<DWARFCompileUnit> &CU : Dwarf->compile_units()) {
+ for (std::unique_ptr<DWARFUnit> &CU : Dwarf->compile_units()) {
auto Report = [](Error Err) {
handleAllErrors(std::move(Err),
[](ErrorInfoBase &Info) { warn(Info.message()); });
@@ -416,6 +412,11 @@ void ObjFile<ELFT>::initializeSections(
continue;
const Elf_Shdr &Sec = ObjSections[I];
+ if (Sec.sh_type == ELF::SHT_LLVM_CALL_GRAPH_PROFILE)
+ CGProfile = check(
+ this->getObj().template getSectionContentsAsArray<Elf_CGProfile>(
+ &Sec));
+
// SHF_EXCLUDE'ed sections are discarded by the linker. However,
// if -r is given, we'll let the final link discard such sections.
// This is compatible with GNU.
@@ -442,6 +443,10 @@ void ObjFile<ELFT>::initializeSections(
bool IsNew = ComdatGroups.insert(CachedHashStringRef(Signature)).second;
this->Sections[I] = &InputSection::Discarded;
+ // We only support GRP_COMDAT type of group. Get the all entries of the
+ // section here to let getShtGroupEntries to check the type early for us.
+ ArrayRef<Elf_Word> Entries = getShtGroupEntries(Sec);
+
// If it is a new section group, we want to keep group members.
// Group leader sections, which contain indices of group members, are
// discarded because they are useless beyond this point. The only
@@ -454,7 +459,7 @@ void ObjFile<ELFT>::initializeSections(
}
// Otherwise, discard group members.
- for (uint32_t SecIndex : getShtGroupEntries(Sec)) {
+ for (uint32_t SecIndex : Entries) {
if (SecIndex >= Size)
fatal(toString(this) +
": invalid section index in group: " + Twine(SecIndex));
@@ -478,11 +483,13 @@ void ObjFile<ELFT>::initializeSections(
// .ARM.exidx sections have a reverse dependency on the InputSection they
// have a SHF_LINK_ORDER dependency, this is identified by the sh_link.
if (Sec.sh_flags & SHF_LINK_ORDER) {
- if (Sec.sh_link >= this->Sections.size())
+ InputSectionBase *LinkSec = nullptr;
+ if (Sec.sh_link < this->Sections.size())
+ LinkSec = this->Sections[Sec.sh_link];
+ if (!LinkSec)
fatal(toString(this) +
": invalid sh_link index: " + Twine(Sec.sh_link));
- InputSectionBase *LinkSec = this->Sections[Sec.sh_link];
InputSection *IS = cast<InputSection>(this->Sections[I]);
LinkSec->DependentSections.push_back(IS);
if (!isa<InputSection>(LinkSec))
@@ -598,7 +605,7 @@ InputSectionBase *ObjFile<ELFT>::getRelocTarget(const Elf_Shdr &Sec) {
// as a given section.
static InputSection *toRegularSection(MergeInputSection *Sec) {
return make<InputSection>(Sec->File, Sec->Flags, Sec->Type, Sec->Alignment,
- Sec->Data, Sec->Name);
+ Sec->data(), Sec->Name);
}
template <class ELFT>
@@ -618,9 +625,9 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
// FIXME: Retain the first attribute section we see. The eglibc ARM
// dynamic loaders require the presence of an attribute section for dlopen
// to work. In a full implementation we would merge all attribute sections.
- if (InX::ARMAttributes == nullptr) {
- InX::ARMAttributes = make<InputSection>(*this, Sec, Name);
- return InX::ARMAttributes;
+ if (In.ARMAttributes == nullptr) {
+ In.ARMAttributes = make<InputSection>(*this, Sec, Name);
+ return In.ARMAttributes;
}
return &InputSection::Discarded;
}
@@ -638,8 +645,16 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
// This section contains relocation information.
// If -r is given, we do not interpret or apply relocation
// but just copy relocation sections to output.
- if (Config->Relocatable)
- return make<InputSection>(*this, Sec, Name);
+ if (Config->Relocatable) {
+ InputSection *RelocSec = make<InputSection>(*this, Sec, Name);
+ // We want to add a dependency to target, similar like we do for
+ // -emit-relocs below. This is useful for the case when linker script
+ // contains the "/DISCARD/". It is perhaps uncommon to use a script with
+ // -r, but we faced it in the Linux kernel and have to handle such case
+ // and not to crash.
+ Target->DependentSections.push_back(RelocSec);
+ return RelocSec;
+ }
if (Target->FirstRelocation)
fatal(toString(this) +
@@ -704,7 +719,7 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
// for split stack will include a .note.GNU-split-stack section.
if (Name == ".note.GNU-split-stack") {
if (Config->Relocatable) {
- error("Cannot mix split-stack and non-split-stack in a relocatable link");
+ error("cannot mix split-stack and non-split-stack in a relocatable link");
return &InputSection::Discarded;
}
this->SplitStack = true;
@@ -806,7 +821,7 @@ template <class ELFT> Symbol *ObjFile<ELFT>::createSymbol(const Elf_Sym *Sym) {
if (Sec == &InputSection::Discarded)
return Symtab->addUndefined<ELFT>(Name, Binding, StOther, Type,
/*CanOmitFromDynSym=*/false, this);
- return Symtab->addRegular(Name, StOther, Type, Value, Size, Binding, Sec,
+ return Symtab->addDefined(Name, StOther, Type, Value, Size, Binding, Sec,
this);
}
}
@@ -940,8 +955,7 @@ std::vector<const typename ELFT::Verdef *> SharedFile<ELFT>::parseVerdefs() {
auto *CurVerdef = reinterpret_cast<const Elf_Verdef *>(Verdef);
Verdef += CurVerdef->vd_next;
unsigned VerdefIndex = CurVerdef->vd_ndx;
- if (Verdefs.size() <= VerdefIndex)
- Verdefs.resize(VerdefIndex + 1);
+ Verdefs.resize(VerdefIndex + 1);
Verdefs[VerdefIndex] = CurVerdef;
}
@@ -993,25 +1007,25 @@ template <class ELFT> void SharedFile<ELFT>::parseRest() {
for (size_t I = 0; I < Syms.size(); ++I) {
const Elf_Sym &Sym = Syms[I];
- StringRef Name = CHECK(Sym.getName(this->StringTable), this);
- if (Sym.isUndefined()) {
- Symbol *S = Symtab->addUndefined<ELFT>(Name, Sym.getBinding(),
- Sym.st_other, Sym.getType(),
- /*CanOmitFromDynSym=*/false, this);
- S->ExportDynamic = true;
- continue;
- }
-
// ELF spec requires that all local symbols precede weak or global
// symbols in each symbol table, and the index of first non-local symbol
// is stored to sh_info. If a local symbol appears after some non-local
// symbol, that's a violation of the spec.
+ StringRef Name = CHECK(Sym.getName(this->StringTable), this);
if (Sym.getBinding() == STB_LOCAL) {
warn("found local symbol '" + Name +
"' in global part of symbol table in file " + toString(this));
continue;
}
+ if (Sym.isUndefined()) {
+ Symbol *S = Symtab->addUndefined<ELFT>(Name, Sym.getBinding(),
+ Sym.st_other, Sym.getType(),
+ /*CanOmitFromDynSym=*/false, this);
+ S->ExportDynamic = true;
+ continue;
+ }
+
// MIPS BFD linker puts _gp_disp symbol into DSO files and incorrectly
// assigns VER_NDX_LOCAL to this section global symbol. Here is a
// workaround for this bug.
@@ -1054,6 +1068,9 @@ static uint8_t getBitcodeMachineKind(StringRef Path, const Triple &T) {
switch (T.getArch()) {
case Triple::aarch64:
return EM_AARCH64;
+ case Triple::amdgcn:
+ case Triple::r600:
+ return EM_AMDGPU;
case Triple::arm:
case Triple::thumb:
return EM_ARM;
@@ -1064,9 +1081,12 @@ static uint8_t getBitcodeMachineKind(StringRef Path, const Triple &T) {
case Triple::mips64:
case Triple::mips64el:
return EM_MIPS;
+ case Triple::msp430:
+ return EM_MSP430;
case Triple::ppc:
return EM_PPC;
case Triple::ppc64:
+ case Triple::ppc64le:
return EM_PPC64;
case Triple::x86:
return T.isOSIAMCU() ? EM_IAMCU : EM_386;
@@ -1178,7 +1198,7 @@ static ELFKind getELFKind(MemoryBufferRef MB) {
}
void BinaryFile::parse() {
- ArrayRef<uint8_t> Data = toArrayRef(MB.getBuffer());
+ ArrayRef<uint8_t> Data = arrayRefFromStringRef(MB.getBuffer());
auto *Section = make<InputSection>(this, SHF_ALLOC | SHF_WRITE, SHT_PROGBITS,
8, Data, ".data");
Sections.push_back(Section);
@@ -1192,11 +1212,11 @@ void BinaryFile::parse() {
if (!isAlnum(S[I]))
S[I] = '_';
- Symtab->addRegular(Saver.save(S + "_start"), STV_DEFAULT, STT_OBJECT, 0, 0,
+ Symtab->addDefined(Saver.save(S + "_start"), STV_DEFAULT, STT_OBJECT, 0, 0,
STB_GLOBAL, Section, nullptr);
- Symtab->addRegular(Saver.save(S + "_end"), STV_DEFAULT, STT_OBJECT,
+ Symtab->addDefined(Saver.save(S + "_end"), STV_DEFAULT, STT_OBJECT,
Data.size(), 0, STB_GLOBAL, Section, nullptr);
- Symtab->addRegular(Saver.save(S + "_size"), STV_DEFAULT, STT_OBJECT,
+ Symtab->addDefined(Saver.save(S + "_size"), STV_DEFAULT, STT_OBJECT,
Data.size(), 0, STB_GLOBAL, nullptr, nullptr);
}
@@ -1262,25 +1282,11 @@ template <class ELFT> void LazyObjFile::parse() {
return;
}
- switch (getELFKind(this->MB)) {
- case ELF32LEKind:
- addElfSymbols<ELF32LE>();
- return;
- case ELF32BEKind:
- addElfSymbols<ELF32BE>();
+ if (getELFKind(this->MB) != Config->EKind) {
+ error("incompatible file: " + this->MB.getBufferIdentifier());
return;
- case ELF64LEKind:
- addElfSymbols<ELF64LE>();
- return;
- case ELF64BEKind:
- addElfSymbols<ELF64BE>();
- return;
- default:
- llvm_unreachable("getELFKind");
}
-}
-template <class ELFT> void LazyObjFile::addElfSymbols() {
ELFFile<ELFT> Obj = check(ELFFile<ELFT>::create(MB.getBuffer()));
ArrayRef<typename ELFT::Shdr> Sections = CHECK(Obj.sections(), this);
@@ -1305,12 +1311,9 @@ std::string elf::replaceThinLTOSuffix(StringRef Path) {
StringRef Suffix = Config->ThinLTOObjectSuffixReplace.first;
StringRef Repl = Config->ThinLTOObjectSuffixReplace.second;
- if (!Path.endswith(Suffix)) {
- error("-thinlto-object-suffix-replace=" + Suffix + ";" + Repl +
- " was given, but " + Path + " does not end with the suffix");
- return "";
- }
- return (Path.drop_back(Suffix.size()) + Repl).str();
+ if (Path.consume_back(Suffix))
+ return (Path + Repl).str();
+ return Path;
}
template void ArchiveFile::parse<ELF32LE>();
diff --git a/ELF/InputFiles.h b/ELF/InputFiles.h
index 0db3203b0ba2..5094ddd804a5 100644
--- a/ELF/InputFiles.h
+++ b/ELF/InputFiles.h
@@ -50,7 +50,7 @@ class Symbol;
// If -reproduce option is given, all input files are written
// to this tar archive.
-extern llvm::TarWriter *Tar;
+extern std::unique_ptr<llvm::TarWriter> Tar;
// Opens a given file.
llvm::Optional<MemoryBufferRef> readFile(StringRef Path);
@@ -86,7 +86,9 @@ public:
// Returns object file symbols. It is a runtime error to call this
// function on files of other types.
- ArrayRef<Symbol *> getSymbols() {
+ ArrayRef<Symbol *> getSymbols() { return getMutableSymbols(); }
+
+ std::vector<Symbol *> &getMutableSymbols() {
assert(FileKind == BinaryKind || FileKind == ObjKind ||
FileKind == BitcodeKind);
return Symbols;
@@ -169,6 +171,7 @@ template <class ELFT> class ObjFile : public ELFFileBase<ELFT> {
typedef typename ELFT::Sym Elf_Sym;
typedef typename ELFT::Shdr Elf_Shdr;
typedef typename ELFT::Word Elf_Word;
+ typedef typename ELFT::CGProfile Elf_CGProfile;
StringRef getShtGroupSignature(ArrayRef<Elf_Shdr> Sections,
const Elf_Shdr &Sec);
@@ -218,6 +221,9 @@ public:
// Pointer to this input file's .llvm_addrsig section, if it has one.
const Elf_Shdr *AddrsigSec = nullptr;
+ // SHT_LLVM_CALL_GRAPH_PROFILE table
+ ArrayRef<Elf_CGProfile> CGProfile;
+
private:
void
initializeSections(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
@@ -272,8 +278,6 @@ public:
bool AddedToLink = false;
private:
- template <class ELFT> void addElfSymbols();
-
uint64_t OffsetInArchive;
};
diff --git a/ELF/InputSection.cpp b/ELF/InputSection.cpp
index 54fb57cf9888..839bff7011eb 100644
--- a/ELF/InputSection.cpp
+++ b/ELF/InputSection.cpp
@@ -21,7 +21,6 @@
#include "Thunks.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
-#include "llvm/Object/Decompressor.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Compression.h"
#include "llvm/Support/Endian.h"
@@ -64,11 +63,11 @@ InputSectionBase::InputSectionBase(InputFile *File, uint64_t Flags,
StringRef Name, Kind SectionKind)
: SectionBase(SectionKind, Name, Flags, Entsize, Alignment, Type, Info,
Link),
- File(File), Data(Data) {
+ File(File), RawData(Data) {
// In order to reduce memory allocation, we assume that mergeable
// sections are smaller than 4 GiB, which is not an unreasonable
// assumption as of 2017.
- if (SectionKind == SectionBase::Merge && Data.size() > UINT32_MAX)
+ if (SectionKind == SectionBase::Merge && RawData.size() > UINT32_MAX)
error(toString(this) + ": section too large");
NumRelocations = 0;
@@ -80,6 +79,17 @@ InputSectionBase::InputSectionBase(InputFile *File, uint64_t Flags,
if (!isPowerOf2_64(V))
fatal(toString(File) + ": section sh_addralign is not a power of 2");
this->Alignment = V;
+
+ // In ELF, each section can be compressed by zlib, and if compressed,
+ // section name may be mangled by appending "z" (e.g. ".zdebug_info").
+ // If that's the case, demangle section name so that we can handle a
+ // section as if it weren't compressed.
+ if ((Flags & SHF_COMPRESSED) || Name.startswith(".zdebug")) {
+ if (!zlib::isAvailable())
+ error(toString(File) + ": contains a compressed section, " +
+ "but zlib is not available");
+ parseCompressedHeader();
+ }
}
// Drop SHF_GROUP bit unless we are producing a re-linkable object file.
@@ -128,13 +138,25 @@ InputSectionBase::InputSectionBase(ObjFile<ELFT> &File,
size_t InputSectionBase::getSize() const {
if (auto *S = dyn_cast<SyntheticSection>(this))
return S->getSize();
+ if (UncompressedSize >= 0)
+ return UncompressedSize;
+ return RawData.size();
+}
+
+void InputSectionBase::uncompress() const {
+ size_t Size = UncompressedSize;
+ UncompressedBuf.reset(new char[Size]);
- return Data.size();
+ if (Error E =
+ zlib::uncompress(toStringRef(RawData), UncompressedBuf.get(), Size))
+ fatal(toString(this) +
+ ": uncompress failed: " + llvm::toString(std::move(E)));
+ RawData = makeArrayRef((uint8_t *)UncompressedBuf.get(), Size);
}
uint64_t InputSectionBase::getOffsetInFile() const {
const uint8_t *FileStart = (const uint8_t *)File->MB.getBufferStart();
- const uint8_t *SecStart = Data.begin();
+ const uint8_t *SecStart = data().begin();
return SecStart - FileStart;
}
@@ -180,34 +202,70 @@ OutputSection *SectionBase::getOutputSection() {
return Sec ? Sec->getParent() : nullptr;
}
-// Decompress section contents if required. Note that this function
-// is called from parallelForEach, so it must be thread-safe.
-void InputSectionBase::maybeDecompress() {
- if (DecompressBuf)
- return;
- if (!(Flags & SHF_COMPRESSED) && !Name.startswith(".zdebug"))
- return;
+// When a section is compressed, `RawData` consists with a header followed
+// by zlib-compressed data. This function parses a header to initialize
+// `UncompressedSize` member and remove the header from `RawData`.
+void InputSectionBase::parseCompressedHeader() {
+ typedef typename ELF64LE::Chdr Chdr64;
+ typedef typename ELF32LE::Chdr Chdr32;
- // Decompress a section.
- Decompressor Dec = check(Decompressor::create(Name, toStringRef(Data),
- Config->IsLE, Config->Is64));
+ // Old-style header
+ if (Name.startswith(".zdebug")) {
+ if (!toStringRef(RawData).startswith("ZLIB")) {
+ error(toString(this) + ": corrupted compressed section header");
+ return;
+ }
+ RawData = RawData.slice(4);
- size_t Size = Dec.getDecompressedSize();
- DecompressBuf.reset(new char[Size + Name.size()]());
- if (Error E = Dec.decompress({DecompressBuf.get(), Size}))
- fatal(toString(this) +
- ": decompress failed: " + llvm::toString(std::move(E)));
+ if (RawData.size() < 8) {
+ error(toString(this) + ": corrupted compressed section header");
+ return;
+ }
+
+ UncompressedSize = read64be(RawData.data());
+ RawData = RawData.slice(8);
+
+ // Restore the original section name.
+ // (e.g. ".zdebug_info" -> ".debug_info")
+ Name = Saver.save("." + Name.substr(2));
+ return;
+ }
- Data = makeArrayRef((uint8_t *)DecompressBuf.get(), Size);
+ assert(Flags & SHF_COMPRESSED);
Flags &= ~(uint64_t)SHF_COMPRESSED;
- // A section name may have been altered if compressed. If that's
- // the case, restore the original name. (i.e. ".zdebug_" -> ".debug_")
- if (Name.startswith(".zdebug")) {
- DecompressBuf[Size] = '.';
- memcpy(&DecompressBuf[Size + 1], Name.data() + 2, Name.size() - 2);
- Name = StringRef(&DecompressBuf[Size], Name.size() - 1);
+ // New-style 64-bit header
+ if (Config->Is64) {
+ if (RawData.size() < sizeof(Chdr64)) {
+ error(toString(this) + ": corrupted compressed section");
+ return;
+ }
+
+ auto *Hdr = reinterpret_cast<const Chdr64 *>(RawData.data());
+ if (Hdr->ch_type != ELFCOMPRESS_ZLIB) {
+ error(toString(this) + ": unsupported compression type");
+ return;
+ }
+
+ UncompressedSize = Hdr->ch_size;
+ RawData = RawData.slice(sizeof(*Hdr));
+ return;
}
+
+ // New-style 32-bit header
+ if (RawData.size() < sizeof(Chdr32)) {
+ error(toString(this) + ": corrupted compressed section");
+ return;
+ }
+
+ auto *Hdr = reinterpret_cast<const Chdr32 *>(RawData.data());
+ if (Hdr->ch_type != ELFCOMPRESS_ZLIB) {
+ error(toString(this) + ": unsupported compression type");
+ return;
+ }
+
+ UncompressedSize = Hdr->ch_size;
+ RawData = RawData.slice(sizeof(*Hdr));
}
InputSection *InputSectionBase::getLinkOrderDep() const {
@@ -230,14 +288,17 @@ Defined *InputSectionBase::getEnclosingFunction(uint64_t Offset) {
// Returns a source location string. Used to construct an error message.
template <class ELFT>
std::string InputSectionBase::getLocation(uint64_t Offset) {
+ std::string SecAndOffset = (Name + "+0x" + utohexstr(Offset)).str();
+
// We don't have file for synthetic sections.
if (getFile<ELFT>() == nullptr)
- return (Config->OutputFile + ":(" + Name + "+0x" + utohexstr(Offset) + ")")
+ return (Config->OutputFile + ":(" + SecAndOffset + ")")
.str();
// First check if we can get desired values from debugging information.
if (Optional<DILineInfo> Info = getFile<ELFT>()->getDILineInfo(this, Offset))
- return Info->FileName + ":" + std::to_string(Info->Line);
+ return Info->FileName + ":" + std::to_string(Info->Line) + ":(" +
+ SecAndOffset + ")";
// File->SourceFile contains STT_FILE symbol that contains a
// source file name. If it's missing, we use an object file name.
@@ -246,10 +307,10 @@ std::string InputSectionBase::getLocation(uint64_t Offset) {
SrcFile = toString(File);
if (Defined *D = getEnclosingFunction<ELFT>(Offset))
- return SrcFile + ":(function " + toString(*D) + ")";
+ return SrcFile + ":(function " + toString(*D) + ": " + SecAndOffset + ")";
// If there's no symbol, print out the offset in the section.
- return (SrcFile + ":(" + Name + "+0x" + utohexstr(Offset) + ")").str();
+ return (SrcFile + ":(" + SecAndOffset + ")");
}
// This function is intended to be used for constructing an error message.
@@ -259,9 +320,6 @@ std::string InputSectionBase::getLocation(uint64_t Offset) {
//
// Returns an empty string if there's no way to get line info.
std::string InputSectionBase::getSrcMsg(const Symbol &Sym, uint64_t Offset) {
- // Synthetic sections don't have input files.
- if (!File)
- return "";
return File->getSrcMsg(Sym, *this, Offset);
}
@@ -275,9 +333,6 @@ std::string InputSectionBase::getSrcMsg(const Symbol &Sym, uint64_t Offset) {
//
// path/to/foo.o:(function bar) in archive path/to/bar.a
std::string InputSectionBase::getObjMsg(uint64_t Off) {
- // Synthetic sections don't have input files.
- if (!File)
- return ("<internal>:(" + Name + "+0x" + utohexstr(Off) + ")").str();
std::string Filename = File->getName();
std::string Archive;
@@ -362,7 +417,7 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
// Output section VA is zero for -r, so r_offset is an offset within the
// section, but for --emit-relocs it is an virtual address.
P->r_offset = Sec->getVA(Rel.r_offset);
- P->setSymbolAndType(InX::SymTab->getSymbolIndex(&Sym), Type,
+ P->setSymbolAndType(In.SymTab->getSymbolIndex(&Sym), Type,
Config->IsMips64EL);
if (Sym.Type == STT_SECTION) {
@@ -380,14 +435,14 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef<RelTy> Rels) {
error("STT_SECTION symbol should be defined");
continue;
}
- SectionBase *Section = D->Section;
- if (Section == &InputSection::Discarded) {
+ SectionBase *Section = D->Section->Repl;
+ if (!Section->Live) {
P->setSymbolAndType(0, 0, false);
continue;
}
int64_t Addend = getAddend<ELFT>(Rel);
- const uint8_t *BufLoc = Sec->Data.begin() + Rel.r_offset;
+ const uint8_t *BufLoc = Sec->data().begin() + Rel.r_offset;
if (!RelTy::IsRela)
Addend = Target->getImplicitAddend(BufLoc, Type);
@@ -487,6 +542,62 @@ static uint64_t getARMStaticBase(const Symbol &Sym) {
return OS->PtLoad->FirstSec->Addr;
}
+// For R_RISCV_PC_INDIRECT (R_RISCV_PCREL_LO12_{I,S}), the symbol actually
+// points the corresponding R_RISCV_PCREL_HI20 relocation, and the target VA
+// is calculated using PCREL_HI20's symbol.
+//
+// This function returns the R_RISCV_PCREL_HI20 relocation from
+// R_RISCV_PCREL_LO12's symbol and addend.
+static Relocation *getRISCVPCRelHi20(const Symbol *Sym, uint64_t Addend) {
+ const Defined *D = cast<Defined>(Sym);
+ InputSection *IS = cast<InputSection>(D->Section);
+
+ if (Addend != 0)
+ warn("Non-zero addend in R_RISCV_PCREL_LO12 relocation to " +
+ IS->getObjMsg(D->Value) + " is ignored");
+
+ // Relocations are sorted by offset, so we can use std::equal_range to do
+ // binary search.
+ auto Range = std::equal_range(IS->Relocations.begin(), IS->Relocations.end(),
+ D->Value, RelocationOffsetComparator{});
+ for (auto It = std::get<0>(Range); It != std::get<1>(Range); ++It)
+ if (isRelExprOneOf<R_PC>(It->Expr))
+ return &*It;
+
+ error("R_RISCV_PCREL_LO12 relocation points to " + IS->getObjMsg(D->Value) +
+ " without an associated R_RISCV_PCREL_HI20 relocation");
+ return nullptr;
+}
+
+// A TLS symbol's virtual address is relative to the TLS segment. Add a
+// target-specific adjustment to produce a thread-pointer-relative offset.
+static int64_t getTlsTpOffset() {
+ switch (Config->EMachine) {
+ case EM_ARM:
+ case EM_AARCH64:
+ // Variant 1. The thread pointer points to a TCB with a fixed 2-word size,
+ // followed by a variable amount of alignment padding, followed by the TLS
+ // segment.
+ //
+ // NB: While the ARM/AArch64 ABI formally has a 2-word TCB size, lld
+ // effectively increases the TCB size to 8 words for Android compatibility.
+ // It accomplishes this by increasing the segment's alignment.
+ return alignTo(Config->Wordsize * 2, Out::TlsPhdr->p_align);
+ case EM_386:
+ case EM_X86_64:
+ // Variant 2. The TLS segment is located just before the thread pointer.
+ return -Out::TlsPhdr->p_memsz;
+ case EM_PPC64:
+ // The thread pointer points to a fixed offset from the start of the
+ // executable's TLS segment. An offset of 0x7000 allows a signed 16-bit
+ // offset to reach 0x1000 of TCB/thread-library data and 0xf000 of the
+ // program's TLS segment.
+ return -0x7000;
+ default:
+ llvm_unreachable("unhandled Config->EMachine");
+ }
+}
+
static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
uint64_t P, const Symbol &Sym, RelExpr Expr) {
switch (Expr) {
@@ -501,38 +612,37 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
case R_ARM_SBREL:
return Sym.getVA(A) - getARMStaticBase(Sym);
case R_GOT:
+ case R_GOT_PLT:
case R_RELAX_TLS_GD_TO_IE_ABS:
return Sym.getGotVA() + A;
case R_GOTONLY_PC:
- return InX::Got->getVA() + A - P;
+ return In.Got->getVA() + A - P;
case R_GOTONLY_PC_FROM_END:
- return InX::Got->getVA() + A - P + InX::Got->getSize();
+ return In.Got->getVA() + A - P + In.Got->getSize();
case R_GOTREL:
- return Sym.getVA(A) - InX::Got->getVA();
+ return Sym.getVA(A) - In.Got->getVA();
case R_GOTREL_FROM_END:
- return Sym.getVA(A) - InX::Got->getVA() - InX::Got->getSize();
+ return Sym.getVA(A) - In.Got->getVA() - In.Got->getSize();
case R_GOT_FROM_END:
case R_RELAX_TLS_GD_TO_IE_END:
- return Sym.getGotOffset() + A - InX::Got->getSize();
+ return Sym.getGotOffset() + A - In.Got->getSize();
case R_TLSLD_GOT_OFF:
case R_GOT_OFF:
case R_RELAX_TLS_GD_TO_IE_GOT_OFF:
return Sym.getGotOffset() + A;
- case R_GOT_PAGE_PC:
- case R_RELAX_TLS_GD_TO_IE_PAGE_PC:
+ case R_AARCH64_GOT_PAGE_PC:
+ case R_AARCH64_GOT_PAGE_PC_PLT:
+ case R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC:
return getAArch64Page(Sym.getGotVA() + A) - getAArch64Page(P);
case R_GOT_PC:
case R_RELAX_TLS_GD_TO_IE:
return Sym.getGotVA() + A - P;
- case R_HINT:
- case R_NONE:
- case R_TLSDESC_CALL:
- case R_TLSLD_HINT:
- llvm_unreachable("cannot relocate hint relocs");
+ case R_HEXAGON_GOT:
+ return Sym.getGotVA() - In.GotPlt->getVA();
case R_MIPS_GOTREL:
- return Sym.getVA(A) - InX::MipsGot->getGp(File);
+ return Sym.getVA(A) - In.MipsGot->getGp(File);
case R_MIPS_GOT_GP:
- return InX::MipsGot->getGp(File) + A;
+ return In.MipsGot->getGp(File) + A;
case R_MIPS_GOT_GP_PC: {
// R_MIPS_LO16 expression has R_MIPS_GOT_GP_PC type iif the target
// is _gp_disp symbol. In that case we should use the following
@@ -541,7 +651,7 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
// microMIPS variants of these relocations use slightly different
// expressions: AHL + GP - P + 3 for %lo() and AHL + GP - P - 1 for %hi()
// to correctly handle less-sugnificant bit of the microMIPS symbol.
- uint64_t V = InX::MipsGot->getGp(File) + A - P;
+ uint64_t V = In.MipsGot->getGp(File) + A - P;
if (Type == R_MIPS_LO16 || Type == R_MICROMIPS_LO16)
V += 4;
if (Type == R_MICROMIPS_LO16 || Type == R_MICROMIPS_HI16)
@@ -552,31 +662,34 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
// If relocation against MIPS local symbol requires GOT entry, this entry
// should be initialized by 'page address'. This address is high 16-bits
// of sum the symbol's value and the addend.
- return InX::MipsGot->getVA() +
- InX::MipsGot->getPageEntryOffset(File, Sym, A) -
- InX::MipsGot->getGp(File);
+ return In.MipsGot->getVA() + In.MipsGot->getPageEntryOffset(File, Sym, A) -
+ In.MipsGot->getGp(File);
case R_MIPS_GOT_OFF:
case R_MIPS_GOT_OFF32:
// In case of MIPS if a GOT relocation has non-zero addend this addend
// should be applied to the GOT entry content not to the GOT entry offset.
// That is why we use separate expression type.
- return InX::MipsGot->getVA() +
- InX::MipsGot->getSymEntryOffset(File, Sym, A) -
- InX::MipsGot->getGp(File);
+ return In.MipsGot->getVA() + In.MipsGot->getSymEntryOffset(File, Sym, A) -
+ In.MipsGot->getGp(File);
case R_MIPS_TLSGD:
- return InX::MipsGot->getVA() + InX::MipsGot->getGlobalDynOffset(File, Sym) -
- InX::MipsGot->getGp(File);
+ return In.MipsGot->getVA() + In.MipsGot->getGlobalDynOffset(File, Sym) -
+ In.MipsGot->getGp(File);
case R_MIPS_TLSLD:
- return InX::MipsGot->getVA() + InX::MipsGot->getTlsIndexOffset(File) -
- InX::MipsGot->getGp(File);
- case R_PAGE_PC:
- case R_PLT_PAGE_PC: {
- uint64_t Dest;
- if (Sym.isUndefWeak())
- Dest = getAArch64Page(A);
- else
- Dest = getAArch64Page(Sym.getVA(A));
- return Dest - getAArch64Page(P);
+ return In.MipsGot->getVA() + In.MipsGot->getTlsIndexOffset(File) -
+ In.MipsGot->getGp(File);
+ case R_AARCH64_PAGE_PC: {
+ uint64_t Val = Sym.isUndefWeak() ? P + A : Sym.getVA(A);
+ return getAArch64Page(Val) - getAArch64Page(P);
+ }
+ case R_AARCH64_PLT_PAGE_PC: {
+ uint64_t Val = Sym.isUndefWeak() ? P + A : Sym.getPltVA() + A;
+ return getAArch64Page(Val) - getAArch64Page(P);
+ }
+ case R_RISCV_PC_INDIRECT: {
+ if (const Relocation *HiRel = getRISCVPCRelHi20(&Sym, A))
+ return getRelocTargetVA(File, HiRel->Type, HiRel->Addend, Sym.getVA(),
+ *HiRel->Sym, HiRel->Expr);
+ return 0;
}
case R_PC: {
uint64_t Dest;
@@ -608,16 +721,12 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
return 0;
// PPC64 V2 ABI describes two entry points to a function. The global entry
- // point sets up the TOC base pointer. When calling a local function, the
- // call should branch to the local entry point rather than the global entry
- // point. Section 3.4.1 describes using the 3 most significant bits of the
- // st_other field to find out how many instructions there are between the
- // local and global entry point.
- uint8_t StOther = (Sym.StOther >> 5) & 7;
- if (StOther == 0 || StOther == 1)
- return SymVA - P;
-
- return SymVA - P + (1LL << StOther);
+ // point is used for calls where the caller and callee (may) have different
+ // TOC base pointers and r2 needs to be modified to hold the TOC base for
+ // the callee. For local calls the caller and callee share the same
+ // TOC base and so the TOC pointer initialization code should be skipped by
+ // branching to the local entry point.
+ return SymVA - P + getPPC64GlobalEntryToLocalEntryOffset(Sym.StOther);
}
case R_PPC_TOC:
return getPPC64TocBase() + A;
@@ -634,48 +743,32 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
// statically to zero.
if (Sym.isTls() && Sym.isUndefWeak())
return 0;
-
- // For TLS variant 1 the TCB is a fixed size, whereas for TLS variant 2 the
- // TCB is on unspecified size and content. Targets that implement variant 1
- // should set TcbSize.
- if (Target->TcbSize) {
- // PPC64 V2 ABI has the thread pointer offset into the middle of the TLS
- // storage area by TlsTpOffset for efficient addressing TCB and up to
- // 4KB – 8 B of other thread library information (placed before the TCB).
- // Subtracting this offset will get the address of the first TLS block.
- if (Target->TlsTpOffset)
- return Sym.getVA(A) - Target->TlsTpOffset;
-
- // If thread pointer is not offset into the middle, the first thing in the
- // TLS storage area is the TCB. Add the TcbSize to get the address of the
- // first TLS block.
- return Sym.getVA(A) + alignTo(Target->TcbSize, Out::TlsPhdr->p_align);
- }
- return Sym.getVA(A) - Out::TlsPhdr->p_memsz;
+ return Sym.getVA(A) + getTlsTpOffset();
case R_RELAX_TLS_GD_TO_LE_NEG:
case R_NEG_TLS:
return Out::TlsPhdr->p_memsz - Sym.getVA(A);
case R_SIZE:
return Sym.getSize() + A;
case R_TLSDESC:
- return InX::Got->getGlobalDynAddr(Sym) + A;
- case R_TLSDESC_PAGE:
- return getAArch64Page(InX::Got->getGlobalDynAddr(Sym) + A) -
+ return In.Got->getGlobalDynAddr(Sym) + A;
+ case R_AARCH64_TLSDESC_PAGE:
+ return getAArch64Page(In.Got->getGlobalDynAddr(Sym) + A) -
getAArch64Page(P);
case R_TLSGD_GOT:
- return InX::Got->getGlobalDynOffset(Sym) + A;
+ return In.Got->getGlobalDynOffset(Sym) + A;
case R_TLSGD_GOT_FROM_END:
- return InX::Got->getGlobalDynOffset(Sym) + A - InX::Got->getSize();
+ return In.Got->getGlobalDynOffset(Sym) + A - In.Got->getSize();
case R_TLSGD_PC:
- return InX::Got->getGlobalDynAddr(Sym) + A - P;
+ return In.Got->getGlobalDynAddr(Sym) + A - P;
case R_TLSLD_GOT_FROM_END:
- return InX::Got->getTlsIndexOff() + A - InX::Got->getSize();
+ return In.Got->getTlsIndexOff() + A - In.Got->getSize();
case R_TLSLD_GOT:
- return InX::Got->getTlsIndexOff() + A;
+ return In.Got->getTlsIndexOff() + A;
case R_TLSLD_PC:
- return InX::Got->getTlsIndexVA() + A - P;
+ return In.Got->getTlsIndexVA() + A - P;
+ default:
+ llvm_unreachable("invalid expression");
}
- llvm_unreachable("Invalid expression");
}
// This function applies relocations to sections without SHF_ALLOC bit.
@@ -808,10 +901,10 @@ void InputSectionBase::relocateAlloc(uint8_t *Buf, uint8_t *BufEnd) {
case R_RELAX_TLS_GD_TO_LE_NEG:
Target->relaxTlsGdToLe(BufLoc, Type, TargetVA);
break;
+ case R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC:
case R_RELAX_TLS_GD_TO_IE:
case R_RELAX_TLS_GD_TO_IE_ABS:
case R_RELAX_TLS_GD_TO_IE_GOT_OFF:
- case R_RELAX_TLS_GD_TO_IE_PAGE_PC:
case R_RELAX_TLS_GD_TO_IE_END:
Target->relaxTlsGdToIe(BufLoc, Type, TargetVA);
break;
@@ -848,16 +941,20 @@ static void switchMorestackCallsToMorestackNonSplit(
// __morestack inside that function should be switched to
// __morestack_non_split.
Symbol *MoreStackNonSplit = Symtab->find("__morestack_non_split");
+ if (!MoreStackNonSplit) {
+ error("Mixing split-stack objects requires a definition of "
+ "__morestack_non_split");
+ return;
+ }
// Sort both collections to compare addresses efficiently.
- llvm::sort(MorestackCalls.begin(), MorestackCalls.end(),
- [](const Relocation *L, const Relocation *R) {
- return L->Offset < R->Offset;
- });
+ llvm::sort(MorestackCalls, [](const Relocation *L, const Relocation *R) {
+ return L->Offset < R->Offset;
+ });
std::vector<Defined *> Functions(Prologues.begin(), Prologues.end());
- llvm::sort(
- Functions.begin(), Functions.end(),
- [](const Defined *L, const Defined *R) { return L->Value < R->Value; });
+ llvm::sort(Functions, [](const Defined *L, const Defined *R) {
+ return L->Value < R->Value;
+ });
auto It = MorestackCalls.begin();
for (Defined *F : Functions) {
@@ -872,8 +969,8 @@ static void switchMorestackCallsToMorestackNonSplit(
}
}
-static bool enclosingPrologueAdjusted(uint64_t Offset,
- const DenseSet<Defined *> &Prologues) {
+static bool enclosingPrologueAttempted(uint64_t Offset,
+ const DenseSet<Defined *> &Prologues) {
for (Defined *F : Prologues)
if (F->Value <= Offset && Offset < F->Value + F->Size)
return true;
@@ -889,7 +986,7 @@ void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *Buf,
uint8_t *End) {
if (!getFile<ELFT>()->SplitStack)
return;
- DenseSet<Defined *> AdjustedPrologues;
+ DenseSet<Defined *> Prologues;
std::vector<Relocation *> MorestackCalls;
for (Relocation &Rel : Relocations) {
@@ -898,15 +995,9 @@ void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *Buf,
if (Rel.Sym->isLocal())
continue;
- Defined *D = dyn_cast<Defined>(Rel.Sym);
- // A reference to an undefined symbol was an error, and should not
- // have gotten to this point.
- if (!D)
- continue;
-
// Ignore calls into the split-stack api.
- if (D->getName().startswith("__morestack")) {
- if (D->getName().equals("__morestack"))
+ if (Rel.Sym->getName().startswith("__morestack")) {
+ if (Rel.Sym->getName().equals("__morestack"))
MorestackCalls.push_back(&Rel);
continue;
}
@@ -914,24 +1005,36 @@ void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *Buf,
// A relocation to non-function isn't relevant. Sometimes
// __morestack is not marked as a function, so this check comes
// after the name check.
- if (D->Type != STT_FUNC)
+ if (Rel.Sym->Type != STT_FUNC)
continue;
- if (enclosingPrologueAdjusted(Rel.Offset, AdjustedPrologues))
+ // If the callee's-file was compiled with split stack, nothing to do. In
+ // this context, a "Defined" symbol is one "defined by the binary currently
+ // being produced". So an "undefined" symbol might be provided by a shared
+ // library. It is not possible to tell how such symbols were compiled, so be
+ // conservative.
+ if (Defined *D = dyn_cast<Defined>(Rel.Sym))
+ if (InputSection *IS = cast_or_null<InputSection>(D->Section))
+ if (!IS || !IS->getFile<ELFT>() || IS->getFile<ELFT>()->SplitStack)
+ continue;
+
+ if (enclosingPrologueAttempted(Rel.Offset, Prologues))
continue;
if (Defined *F = getEnclosingFunction<ELFT>(Rel.Offset)) {
- if (Target->adjustPrologueForCrossSplitStack(Buf + F->Value, End)) {
- AdjustedPrologues.insert(F);
+ Prologues.insert(F);
+ if (Target->adjustPrologueForCrossSplitStack(Buf + getOffset(F->Value),
+ End, F->StOther))
continue;
- }
+ if (!getFile<ELFT>()->SomeNoSplitStack)
+ error(lld::toString(this) + ": " + F->getName() +
+ " (with -fsplit-stack) calls " + Rel.Sym->getName() +
+ " (without -fsplit-stack), but couldn't adjust its prologue");
}
- if (!getFile<ELFT>()->SomeNoSplitStack)
- error("function call at " + getErrorLocation(Buf + Rel.Offset) +
- "crosses a split-stack boundary, but unable " +
- "to adjust the enclosing function's prologue");
}
- switchMorestackCallsToMorestackNonSplit(AdjustedPrologues, MorestackCalls);
+
+ if (Target->NeedsMoreStackNonSplit)
+ switchMorestackCallsToMorestackNonSplit(Prologues, MorestackCalls);
}
template <class ELFT> void InputSection::writeTo(uint8_t *Buf) {
@@ -960,10 +1063,23 @@ template <class ELFT> void InputSection::writeTo(uint8_t *Buf) {
return;
}
+ // If this is a compressed section, uncompress section contents directly
+ // to the buffer.
+ if (UncompressedSize >= 0 && !UncompressedBuf) {
+ size_t Size = UncompressedSize;
+ if (Error E = zlib::uncompress(toStringRef(RawData),
+ (char *)(Buf + OutSecOff), Size))
+ fatal(toString(this) +
+ ": uncompress failed: " + llvm::toString(std::move(E)));
+ uint8_t *BufEnd = Buf + OutSecOff + Size;
+ relocate<ELFT>(Buf, BufEnd);
+ return;
+ }
+
// Copy section contents from source object file to output file
// and then apply relocations.
- memcpy(Buf + OutSecOff, Data.data(), Data.size());
- uint8_t *BufEnd = Buf + OutSecOff + Data.size();
+ memcpy(Buf + OutSecOff, data().data(), data().size());
+ uint8_t *BufEnd = Buf + OutSecOff + data().size();
relocate<ELFT>(Buf, BufEnd);
}
@@ -1014,7 +1130,7 @@ template <class ELFT> void EhInputSection::split() {
template <class ELFT, class RelTy>
void EhInputSection::split(ArrayRef<RelTy> Rels) {
unsigned RelI = 0;
- for (size_t Off = 0, End = Data.size(); Off != End;) {
+ for (size_t Off = 0, End = data().size(); Off != End;) {
size_t Size = readEhRecordSize(this, Off);
Pieces.emplace_back(Off, this, Size, getReloc(Off, Size, Rels, RelI));
// The empty record is the end marker.
@@ -1094,65 +1210,32 @@ void MergeInputSection::splitIntoPieces() {
assert(Pieces.empty());
if (Flags & SHF_STRINGS)
- splitStrings(Data, Entsize);
+ splitStrings(data(), Entsize);
else
- splitNonStrings(Data, Entsize);
-
- OffsetMap.reserve(Pieces.size());
- for (size_t I = 0, E = Pieces.size(); I != E; ++I)
- OffsetMap[Pieces[I].InputOff] = I;
-}
-
-template <class It, class T, class Compare>
-static It fastUpperBound(It First, It Last, const T &Value, Compare Comp) {
- size_t Size = std::distance(First, Last);
- assert(Size != 0);
- while (Size != 1) {
- size_t H = Size / 2;
- const It MI = First + H;
- Size -= H;
- First = Comp(Value, *MI) ? First : First + H;
- }
- return Comp(Value, *First) ? First : First + 1;
-}
-
-// Do binary search to get a section piece at a given input offset.
-static SectionPiece *findSectionPiece(MergeInputSection *Sec, uint64_t Offset) {
- if (Sec->Data.size() <= Offset)
- fatal(toString(Sec) + ": entry is past the end of the section");
-
- // Find the element this offset points to.
- auto I = fastUpperBound(
- Sec->Pieces.begin(), Sec->Pieces.end(), Offset,
- [](const uint64_t &A, const SectionPiece &B) { return A < B.InputOff; });
- --I;
- return &*I;
+ splitNonStrings(data(), Entsize);
}
SectionPiece *MergeInputSection::getSectionPiece(uint64_t Offset) {
- // Find a piece starting at a given offset.
- auto It = OffsetMap.find(Offset);
- if (It != OffsetMap.end())
- return &Pieces[It->second];
+ if (this->data().size() <= Offset)
+ fatal(toString(this) + ": offset is outside the section");
// If Offset is not at beginning of a section piece, it is not in the map.
- // In that case we need to search from the original section piece vector.
- return findSectionPiece(this, Offset);
+ // In that case we need to do a binary search of the original section piece vector.
+ auto It2 =
+ llvm::upper_bound(Pieces, Offset, [](uint64_t Offset, SectionPiece P) {
+ return Offset < P.InputOff;
+ });
+ return &It2[-1];
}
// Returns the offset in an output section for a given input offset.
// Because contents of a mergeable section is not contiguous in output,
// it is not just an addition to a base output offset.
uint64_t MergeInputSection::getParentOffset(uint64_t Offset) const {
- // Find a string starting at a given offset.
- auto It = OffsetMap.find(Offset);
- if (It != OffsetMap.end())
- return Pieces[It->second].OutputOff;
-
// If Offset is not at beginning of a section piece, it is not in the map.
// In that case we need to search from the original section piece vector.
const SectionPiece &Piece =
- *findSectionPiece(const_cast<MergeInputSection *>(this), Offset);
+ *(const_cast<MergeInputSection *>(this)->getSectionPiece (Offset));
uint64_t Addend = Offset - Piece.InputOff;
return Piece.OutputOff + Addend;
}
diff --git a/ELF/InputSection.h b/ELF/InputSection.h
index 4db01e035e32..34f411e87200 100644
--- a/ELF/InputSection.h
+++ b/ELF/InputSection.h
@@ -115,7 +115,12 @@ public:
return cast_or_null<ObjFile<ELFT>>(File);
}
- ArrayRef<uint8_t> Data;
+ ArrayRef<uint8_t> data() const {
+ if (UncompressedSize >= 0 && !UncompressedBuf)
+ uncompress();
+ return RawData;
+ }
+
uint64_t getOffsetInFile() const;
// True if this section has already been placed to a linker script
@@ -169,11 +174,6 @@ public:
template <class ELFT>
Defined *getEnclosingFunction(uint64_t Offset);
- // Compilers emit zlib-compressed debug sections if the -gz option
- // is given. This function checks if this section is compressed, and
- // if so, decompress in memory.
- void maybeDecompress();
-
// Returns a source location string. Used to construct an error message.
template <class ELFT> std::string getLocation(uint64_t Offset);
std::string getSrcMsg(const Symbol &Sym, uint64_t Offset);
@@ -200,15 +200,21 @@ public:
template <typename T> llvm::ArrayRef<T> getDataAs() const {
- size_t S = Data.size();
+ size_t S = data().size();
assert(S % sizeof(T) == 0);
- return llvm::makeArrayRef<T>((const T *)Data.data(), S / sizeof(T));
+ return llvm::makeArrayRef<T>((const T *)data().data(), S / sizeof(T));
}
-private:
- // A pointer that owns decompressed data if a section is compressed by zlib.
+protected:
+ void parseCompressedHeader();
+ void uncompress() const;
+
+ mutable ArrayRef<uint8_t> RawData;
+
+ // A pointer that owns uncompressed data if a section is compressed by zlib.
// Since the feature is not used often, this is usually a nullptr.
- std::unique_ptr<char[]> DecompressBuf;
+ mutable std::unique_ptr<char[]> UncompressedBuf;
+ int64_t UncompressedSize = -1;
};
// SectionPiece represents a piece of splittable section contents.
@@ -247,7 +253,6 @@ public:
// Splittable sections are handled as a sequence of data
// rather than a single large blob of data.
std::vector<SectionPiece> Pieces;
- llvm::DenseMap<uint32_t, uint32_t> OffsetMap;
// Returns I'th piece's data. This function is very hot when
// string merging is enabled, so we want to inline.
@@ -255,8 +260,8 @@ public:
llvm::CachedHashStringRef getData(size_t I) const {
size_t Begin = Pieces[I].InputOff;
size_t End =
- (Pieces.size() - 1 == I) ? Data.size() : Pieces[I + 1].InputOff;
- return {toStringRef(Data.slice(Begin, End - Begin)), Pieces[I].Hash};
+ (Pieces.size() - 1 == I) ? data().size() : Pieces[I + 1].InputOff;
+ return {toStringRef(data().slice(Begin, End - Begin)), Pieces[I].Hash};
}
// Returns the SectionPiece at a given input section offset.
@@ -277,7 +282,9 @@ struct EhSectionPiece {
unsigned FirstRelocation)
: InputOff(Off), Sec(Sec), Size(Size), FirstRelocation(FirstRelocation) {}
- ArrayRef<uint8_t> data() { return {Sec->Data.data() + this->InputOff, Size}; }
+ ArrayRef<uint8_t> data() {
+ return {Sec->data().data() + this->InputOff, Size};
+ }
size_t InputOff;
ssize_t OutputOff = -1;
@@ -353,6 +360,7 @@ private:
// The list of all input sections.
extern std::vector<InputSectionBase *> InputSections;
+
} // namespace elf
std::string toString(const elf::InputSectionBase *);
diff --git a/ELF/LTO.cpp b/ELF/LTO.cpp
index ef58932e86cc..ca44581780e4 100644
--- a/ELF/LTO.cpp
+++ b/ELF/LTO.cpp
@@ -67,9 +67,10 @@ static std::string getThinLTOOutputFile(StringRef ModulePath) {
static lto::Config createConfig() {
lto::Config C;
- // LLD supports the new relocations.
+ // LLD supports the new relocations and address-significance tables.
C.Options = InitTargetOptionsFromCodeGenFlags();
C.Options.RelaxELFRelocations = true;
+ C.Options.EmitAddrsig = true;
// Always emit a section per function/datum with LTO.
C.Options.FunctionSections = true;
@@ -87,6 +88,7 @@ static lto::Config createConfig() {
C.DiagHandler = diagnosticHandler;
C.OptLevel = Config->LTOO;
C.CPU = GetCPUStr();
+ C.MAttrs = GetMAttrs();
// Set up a custom pipeline if we've been asked to.
C.OptPipeline = Config->LTONewPmPasses;
@@ -101,6 +103,14 @@ static lto::Config createConfig() {
C.DebugPassManager = Config->LTODebugPassManager;
C.DwoDir = Config->DwoDir;
+ if (Config->EmitLLVM) {
+ C.PostInternalizeModuleHook = [](size_t Task, const Module &M) {
+ if (std::unique_ptr<raw_fd_ostream> OS = openFile(Config->OutputFile))
+ WriteBitcodeToFile(M, *OS, false);
+ return false;
+ };
+ }
+
if (Config->SaveTemps)
checkError(C.addSaveTemps(Config->OutputFile.str() + ".",
/*UseInputModulePath*/ true));
@@ -108,18 +118,14 @@ static lto::Config createConfig() {
}
BitcodeCompiler::BitcodeCompiler() {
+ // Initialize IndexFile.
+ if (!Config->ThinLTOIndexOnlyArg.empty())
+ IndexFile = openFile(Config->ThinLTOIndexOnlyArg);
+
// Initialize LTOObj.
lto::ThinBackend Backend;
-
if (Config->ThinLTOIndexOnly) {
- StringRef Path = Config->ThinLTOIndexOnlyArg;
- if (!Path.empty())
- IndexFile = openFile(Path);
-
- auto OnIndexWrite = [&](const std::string &Identifier) {
- ObjectToIndexFileState[Identifier] = true;
- };
-
+ auto OnIndexWrite = [&](StringRef S) { ThinIndices.erase(S); };
Backend = lto::createWriteIndexesThinBackend(
Config->ThinLTOPrefixReplace.first, Config->ThinLTOPrefixReplace.second,
Config->ThinLTOEmitImportsFiles, IndexFile.get(), OnIndexWrite);
@@ -132,10 +138,10 @@ BitcodeCompiler::BitcodeCompiler() {
// Initialize UsedStartStop.
for (Symbol *Sym : Symtab->getSymbols()) {
- StringRef Name = Sym->getName();
+ StringRef S = Sym->getName();
for (StringRef Prefix : {"__start_", "__stop_"})
- if (Name.startswith(Prefix))
- UsedStartStop.insert(Name.substr(Prefix.size()));
+ if (S.startswith(Prefix))
+ UsedStartStop.insert(S.substr(Prefix.size()));
}
}
@@ -151,7 +157,7 @@ void BitcodeCompiler::add(BitcodeFile &F) {
bool IsExec = !Config->Shared && !Config->Relocatable;
if (Config->ThinLTOIndexOnly)
- ObjectToIndexFileState.insert({Obj.getName(), false});
+ ThinIndices.insert(Obj.getName());
ArrayRef<Symbol *> Syms = F.getSymbols();
ArrayRef<lto::InputFile::Symbol> ObjSyms = Obj.symbols();
@@ -240,15 +246,11 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
Cache));
// Emit empty index files for non-indexed files
- if (Config->ThinLTOIndexOnly) {
- for (auto &Identifier : ObjectToIndexFileState)
- if (!Identifier.getValue()) {
- std::string Path = getThinLTOOutputFile(Identifier.getKey());
- openFile(Path + ".thinlto.bc");
-
- if (Config->ThinLTOEmitImportsFiles)
- openFile(Path + ".imports");
- }
+ for (StringRef S : ThinIndices) {
+ std::string Path = getThinLTOOutputFile(S);
+ openFile(Path + ".thinlto.bc");
+ if (Config->ThinLTOEmitImportsFiles)
+ openFile(Path + ".imports");
}
// If LazyObjFile has not been added to link, emit empty index files.
diff --git a/ELF/LTO.h b/ELF/LTO.h
index 8803078eb1df..a190da3e5996 100644
--- a/ELF/LTO.h
+++ b/ELF/LTO.h
@@ -55,7 +55,7 @@ private:
std::vector<std::unique_ptr<MemoryBuffer>> Files;
llvm::DenseSet<StringRef> UsedStartStop;
std::unique_ptr<llvm::raw_fd_ostream> IndexFile;
- llvm::StringMap<bool> ObjectToIndexFileState;
+ llvm::DenseSet<StringRef> ThinIndices;
};
} // namespace elf
} // namespace lld
diff --git a/ELF/LinkerScript.cpp b/ELF/LinkerScript.cpp
index abdd899da487..fbc025416205 100644
--- a/ELF/LinkerScript.cpp
+++ b/ELF/LinkerScript.cpp
@@ -116,7 +116,8 @@ void LinkerScript::expandMemoryRegions(uint64_t Size) {
if (Ctx->MemRegion)
expandMemoryRegion(Ctx->MemRegion, Size, Ctx->MemRegion->Name,
Ctx->OutSec->Name);
- if (Ctx->LMARegion)
+ // Only expand the LMARegion if it is different from MemRegion.
+ if (Ctx->LMARegion && Ctx->MemRegion != Ctx->LMARegion)
expandMemoryRegion(Ctx->LMARegion, Size, Ctx->LMARegion->Name,
Ctx->OutSec->Name);
}
@@ -168,7 +169,7 @@ void LinkerScript::addSymbol(SymbolAssignment *Cmd) {
// Define a symbol.
Symbol *Sym;
uint8_t Visibility = Cmd->Hidden ? STV_HIDDEN : STV_DEFAULT;
- std::tie(Sym, std::ignore) = Symtab->insert(Cmd->Name, /*Type*/ 0, Visibility,
+ std::tie(Sym, std::ignore) = Symtab->insert(Cmd->Name, Visibility,
/*CanOmitFromDynSym*/ false,
/*File*/ nullptr);
ExprValue Value = Cmd->Expression();
@@ -201,13 +202,14 @@ static void declareSymbol(SymbolAssignment *Cmd) {
// We can't calculate final value right now.
Symbol *Sym;
uint8_t Visibility = Cmd->Hidden ? STV_HIDDEN : STV_DEFAULT;
- std::tie(Sym, std::ignore) = Symtab->insert(Cmd->Name, /*Type*/ 0, Visibility,
+ std::tie(Sym, std::ignore) = Symtab->insert(Cmd->Name, Visibility,
/*CanOmitFromDynSym*/ false,
/*File*/ nullptr);
replaceSymbol<Defined>(Sym, nullptr, Cmd->Name, STB_GLOBAL, Visibility,
STT_NOTYPE, 0, 0, nullptr);
Cmd->Sym = cast<Defined>(Sym);
Cmd->Provide = false;
+ Sym->ScriptDefined = true;
}
// This method is used to handle INSERT AFTER statement. Here we rebuild
@@ -413,18 +415,16 @@ LinkerScript::computeInputSections(const InputSectionDescription *Cmd) {
void LinkerScript::discard(ArrayRef<InputSection *> V) {
for (InputSection *S : V) {
- if (S == InX::ShStrTab || S == InX::Dynamic || S == InX::DynSymTab ||
- S == InX::DynStrTab || S == InX::RelaPlt || S == InX::RelaDyn ||
- S == InX::RelrDyn)
+ if (S == In.ShStrTab || S == In.RelaDyn || S == In.RelrDyn)
error("discarding " + S->Name + " section is not allowed");
// You can discard .hash and .gnu.hash sections by linker scripts. Since
// they are synthesized sections, we need to handle them differently than
// other regular sections.
- if (S == InX::GnuHashTab)
- InX::GnuHashTab = nullptr;
- if (S == InX::HashTab)
- InX::HashTab = nullptr;
+ if (S == In.GnuHashTab)
+ In.GnuHashTab = nullptr;
+ if (S == In.HashTab)
+ In.HashTab = nullptr;
S->Assigned = false;
S->Live = false;
@@ -700,6 +700,7 @@ uint64_t LinkerScript::advance(uint64_t Size, unsigned Alignment) {
}
void LinkerScript::output(InputSection *S) {
+ assert(Ctx->OutSec == S->getParent());
uint64_t Before = advance(0, 1);
uint64_t Pos = advance(S->getSize(), S->Alignment);
S->OutSecOff = Pos - S->getSize() - Ctx->OutSec->Addr;
@@ -750,6 +751,13 @@ MemoryRegion *LinkerScript::findMemoryRegion(OutputSection *Sec) {
return nullptr;
}
+static OutputSection *findFirstSection(PhdrEntry *Load) {
+ for (OutputSection *Sec : OutputSections)
+ if (Sec->PtLoad == Load)
+ return Sec;
+ return nullptr;
+}
+
// This function assigns offsets to input sections and an output section
// for a single sections command (e.g. ".text { *(.text); }").
void LinkerScript::assignOffsets(OutputSection *Sec) {
@@ -775,8 +783,11 @@ void LinkerScript::assignOffsets(OutputSection *Sec) {
// will set the LMA such that the difference between VMA and LMA for the
// section is the same as the preceding output section in the same region
// https://sourceware.org/binutils/docs-2.20/ld/Output-Section-LMA.html
+ // This, however, should only be done by the first "non-header" section
+ // in the segment.
if (PhdrEntry *L = Ctx->OutSec->PtLoad)
- L->LMAOffset = Ctx->LMAOffset;
+ if (Sec == findFirstSection(L))
+ L->LMAOffset = Ctx->LMAOffset;
// We can call this method multiple times during the creation of
// thunks and want to start over calculation each time.
@@ -805,21 +816,8 @@ void LinkerScript::assignOffsets(OutputSection *Sec) {
// Handle a single input section description command.
// It calculates and assigns the offsets for each section and also
// updates the output section size.
- auto *Cmd = cast<InputSectionDescription>(Base);
- for (InputSection *Sec : Cmd->Sections) {
- // We tentatively added all synthetic sections at the beginning and
- // removed empty ones afterwards (because there is no way to know
- // whether they were going be empty or not other than actually running
- // linker scripts.) We need to ignore remains of empty sections.
- if (auto *S = dyn_cast<SyntheticSection>(Sec))
- if (S->empty())
- continue;
-
- if (!Sec->Live)
- continue;
- assert(Ctx->OutSec == Sec->getParent());
+ for (InputSection *Sec : cast<InputSectionDescription>(Base)->Sections)
output(Sec);
- }
}
}
@@ -953,13 +951,6 @@ void LinkerScript::adjustSectionsAfterSorting() {
}
}
-static OutputSection *findFirstSection(PhdrEntry *Load) {
- for (OutputSection *Sec : OutputSections)
- if (Sec->PtLoad == Load)
- return Sec;
- return nullptr;
-}
-
static uint64_t computeBase(uint64_t Min, bool AllocateHeaders) {
// If there is no SECTIONS or if the linkerscript is explicit about program
// headers, do our best to allocate them.
diff --git a/ELF/LinkerScript.h b/ELF/LinkerScript.h
index 3b790dd4669f..51161981efc8 100644
--- a/ELF/LinkerScript.h
+++ b/ELF/LinkerScript.h
@@ -30,12 +30,13 @@ namespace lld {
namespace elf {
class Defined;
-class Symbol;
-class InputSectionBase;
class InputSection;
-class OutputSection;
class InputSectionBase;
+class InputSectionBase;
+class OutputSection;
class SectionBase;
+class Symbol;
+class ThunkSection;
// This represents an r-value in the linker script.
struct ExprValue {
@@ -145,7 +146,9 @@ struct MemoryRegion {
// Also it may be surrounded with SORT() command, so contains sorting rules.
struct SectionPattern {
SectionPattern(StringMatcher &&Pat1, StringMatcher &&Pat2)
- : ExcludedFilePat(Pat1), SectionPat(Pat2) {}
+ : ExcludedFilePat(Pat1), SectionPat(Pat2),
+ SortOuter(SortSectionPolicy::Default),
+ SortInner(SortSectionPolicy::Default) {}
StringMatcher ExcludedFilePat;
StringMatcher SectionPat;
@@ -153,7 +156,6 @@ struct SectionPattern {
SortSectionPolicy SortInner;
};
-class ThunkSection;
struct InputSectionDescription : BaseCommand {
InputSectionDescription(StringRef FilePattern)
: BaseCommand(InputSectionKind), FilePat(FilePattern) {}
diff --git a/ELF/MapFile.cpp b/ELF/MapFile.cpp
index 54fddfb7b299..b0dc6203008d 100644
--- a/ELF/MapFile.cpp
+++ b/ELF/MapFile.cpp
@@ -126,7 +126,7 @@ static void printEhFrame(raw_ostream &OS, OutputSection *OSec) {
};
// Gather section pieces.
- for (const CieRecord *Rec : InX::EhFrame->getCieRecords()) {
+ for (const CieRecord *Rec : In.EhFrame->getCieRecords()) {
Add(*Rec->Cie);
for (const EhSectionPiece *Fde : Rec->Fdes)
Add(*Fde);
@@ -163,17 +163,18 @@ void elf::writeMapFile() {
OS << right_justify("VMA", W) << ' ' << right_justify("LMA", W)
<< " Size Align Out In Symbol\n";
+ OutputSection* OSec = nullptr;
for (BaseCommand *Base : Script->SectionCommands) {
if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
if (Cmd->Provide && !Cmd->Sym)
continue;
- //FIXME: calculate and print LMA.
- writeHeader(OS, Cmd->Addr, 0, Cmd->Size, 1);
+ uint64_t LMA = OSec ? OSec->getLMA() + Cmd->Addr - OSec->getVA(0) : 0;
+ writeHeader(OS, Cmd->Addr, LMA, Cmd->Size, 1);
OS << Cmd->CommandString << '\n';
continue;
}
- auto *OSec = cast<OutputSection>(Base);
+ OSec = cast<OutputSection>(Base);
writeHeader(OS, OSec->Addr, OSec->getLMA(), OSec->Size, OSec->Alignment);
OS << OSec->Name << '\n';
@@ -181,7 +182,7 @@ void elf::writeMapFile() {
for (BaseCommand *Base : OSec->SectionCommands) {
if (auto *ISD = dyn_cast<InputSectionDescription>(Base)) {
for (InputSection *IS : ISD->Sections) {
- if (IS == InX::EhFrame) {
+ if (IS == In.EhFrame) {
printEhFrame(OS, OSec);
continue;
}
diff --git a/ELF/MarkLive.cpp b/ELF/MarkLive.cpp
index a8371e212c3e..8d0ec091c327 100644
--- a/ELF/MarkLive.cpp
+++ b/ELF/MarkLive.cpp
@@ -45,7 +45,7 @@ using namespace lld::elf;
template <class ELFT>
static typename ELFT::uint getAddend(InputSectionBase &Sec,
const typename ELFT::Rel &Rel) {
- return Target->getImplicitAddend(Sec.Data.begin() + Rel.r_offset,
+ return Target->getImplicitAddend(Sec.data().begin() + Rel.r_offset,
Rel.getType(Config->IsMips64EL));
}
@@ -250,9 +250,10 @@ template <class ELFT> static void doGcSections() {
if (Sec->Flags & SHF_LINK_ORDER)
continue;
- if (isReserved<ELFT>(Sec) || Script->shouldKeep(Sec))
+
+ if (isReserved<ELFT>(Sec) || Script->shouldKeep(Sec)) {
Enqueue(Sec, 0);
- else if (isValidCIdentifier(Sec->Name)) {
+ } else if (isValidCIdentifier(Sec->Name)) {
CNamedSections[Saver.save("__start_" + Sec->Name)].push_back(Sec);
CNamedSections[Saver.save("__stop_" + Sec->Name)].push_back(Sec);
}
@@ -267,10 +268,16 @@ template <class ELFT> static void doGcSections() {
// input sections. This function make some or all of them on
// so that they are emitted to the output file.
template <class ELFT> void elf::markLive() {
- // If -gc-sections is missing, no sections are removed.
if (!Config->GcSections) {
+ // If -gc-sections is missing, no sections are removed.
for (InputSectionBase *Sec : InputSections)
Sec->Live = true;
+
+ // If a DSO defines a symbol referenced in a regular object, it is needed.
+ for (Symbol *Sym : Symtab->getSymbols())
+ if (auto *S = dyn_cast<SharedSymbol>(Sym))
+ if (S->IsUsedInRegularObj && !S->isWeak())
+ S->getFile<ELFT>().IsNeeded = true;
return;
}
diff --git a/ELF/Options.td b/ELF/Options.td
index 04a4f8ffbe28..e43a21b923d3 100644
--- a/ELF/Options.td
+++ b/ELF/Options.td
@@ -42,6 +42,12 @@ defm compress_debug_sections:
defm defsym: Eq<"defsym", "Define a symbol alias">, MetaVarName<"<symbol>=<value>">;
+defm split_stack_adjust_size
+ : Eq<"split-stack-adjust-size",
+ "Specify adjustment to stack size when a split-stack function calls a "
+ "non-split-stack function">,
+ MetaVarName<"<value>">;
+
defm library_path:
Eq<"library-path", "Add a directory to the library search path">, MetaVarName<"<dir>">;
@@ -68,6 +74,10 @@ defm as_needed: B<"as-needed",
defm call_graph_ordering_file:
Eq<"call-graph-ordering-file", "Layout sections to optimize the given callgraph">;
+defm call_graph_profile_sort: B<"call-graph-profile-sort",
+ "Reorder sections with call graph profile (default)",
+ "Do not reorder sections with call graph profile">;
+
// -chroot doesn't have a help text because it is an internal option.
def chroot: Separate<["--", "-"], "chroot">;
@@ -132,7 +142,7 @@ def error_unresolved_symbols: F<"error-unresolved-symbols">,
defm exclude_libs: Eq<"exclude-libs", "Exclude static libraries from automatic export">;
defm execute_only: B<"execute-only",
- "Do not mark executable sections readable",
+ "Mark executable sections unreadable",
"Mark executable sections readable (default)">;
defm export_dynamic: B<"export-dynamic",
@@ -315,6 +325,10 @@ defm threads: B<"threads",
"Run the linker multi-threaded (default)",
"Do not run the linker multi-threaded">;
+defm toc_optimize : B<"toc-optimize",
+ "(PowerPC64) Enable TOC related optimizations (default)",
+ "(PowerPC64) Disable TOC related optimizations">;
+
def trace: F<"trace">, HelpText<"Print the names of the input files">;
defm trace_symbol: Eq<"trace-symbol", "Trace references to symbols">;
@@ -348,6 +362,10 @@ defm warn_common: B<"warn-common",
"Warn about duplicate common symbols",
"Do not warn about duplicate common symbols (default)">;
+defm warn_ifunc_textrel: B<"warn-ifunc-textrel",
+ "Warn about using ifunc symbols with text relocations",
+ "Do not warn about using ifunc symbols with text relocations (default)">;
+
defm warn_symbol_ordering: B<"warn-symbol-ordering",
"Warn about problems with the symbol ordering file (default)",
"Do not warn about problems with the symbol ordering file">;
@@ -440,6 +458,7 @@ def: F<"plugin-opt=debug-pass-manager">,
def: F<"plugin-opt=disable-verify">, Alias<disable_verify>, HelpText<"Alias for -disable-verify">;
def plugin_opt_dwo_dir_eq: J<"plugin-opt=dwo_dir=">,
HelpText<"Directory to store .dwo files when LTO and debug fission are used">;
+def plugin_opt_emit_llvm: F<"plugin-opt=emit-llvm">;
def: J<"plugin-opt=jobs=">, Alias<thinlto_jobs>, HelpText<"Alias for -thinlto-jobs">;
def: J<"plugin-opt=lto-partitions=">, Alias<lto_partitions>, HelpText<"Alias for -lto-partitions">;
def plugin_opt_mcpu_eq: J<"plugin-opt=mcpu=">;
diff --git a/ELF/OutputSections.cpp b/ELF/OutputSections.cpp
index 8253b18b486c..c1442c078736 100644
--- a/ELF/OutputSections.cpp
+++ b/ELF/OutputSections.cpp
@@ -25,6 +25,7 @@
using namespace llvm;
using namespace llvm::dwarf;
using namespace llvm::object;
+using namespace llvm::support::endian;
using namespace llvm::ELF;
using namespace lld;
@@ -32,7 +33,6 @@ using namespace lld::elf;
uint8_t Out::First;
PhdrEntry *Out::TlsPhdr;
-OutputSection *Out::DebugInfo;
OutputSection *Out::ElfHeader;
OutputSection *Out::ProgramHeaders;
OutputSection *Out::PreinitArray;
@@ -95,7 +95,7 @@ void OutputSection::addSection(InputSection *IS) {
Flags = IS->Flags;
} else {
// Otherwise, check if new type or flags are compatible with existing ones.
- unsigned Mask = SHF_ALLOC | SHF_TLS | SHF_LINK_ORDER;
+ unsigned Mask = SHF_TLS | SHF_LINK_ORDER;
if ((Flags & Mask) != (IS->Flags & Mask))
error("incompatible section flags for " + Name + "\n>>> " + toString(IS) +
": 0x" + utohexstr(IS->Flags) + "\n>>> output section " + Name +
@@ -171,11 +171,12 @@ void OutputSection::sort(llvm::function_ref<int(InputSectionBase *S)> Order) {
// Fill [Buf, Buf + Size) with Filler.
// This is used for linker script "=fillexp" command.
-static void fill(uint8_t *Buf, size_t Size, uint32_t Filler) {
+static void fill(uint8_t *Buf, size_t Size,
+ const std::array<uint8_t, 4> &Filler) {
size_t I = 0;
for (; I + 4 < Size; I += 4)
- memcpy(Buf + I, &Filler, 4);
- memcpy(Buf + I, &Filler, Size - I);
+ memcpy(Buf + I, Filler.data(), 4);
+ memcpy(Buf + I, Filler.data(), Size - I);
}
// Compress section contents if this section contains debug info.
@@ -236,8 +237,9 @@ template <class ELFT> void OutputSection::writeTo(uint8_t *Buf) {
// Write leading padding.
std::vector<InputSection *> Sections = getInputSections(this);
- uint32_t Filler = getFiller();
- if (Filler)
+ std::array<uint8_t, 4> Filler = getFiller();
+ bool NonZeroFiller = read32(Filler.data()) != 0;
+ if (NonZeroFiller)
fill(Buf, Sections.empty() ? Size : Sections[0]->OutSecOff, Filler);
parallelForEachN(0, Sections.size(), [&](size_t I) {
@@ -245,7 +247,7 @@ template <class ELFT> void OutputSection::writeTo(uint8_t *Buf) {
IS->writeTo<ELFT>(Buf);
// Fill gaps between sections.
- if (Filler) {
+ if (NonZeroFiller) {
uint8_t *Start = Buf + IS->OutSecOff + IS->getSize();
uint8_t *End;
if (I + 1 == Sections.size())
@@ -270,13 +272,13 @@ static void finalizeShtGroup(OutputSection *OS,
// sh_link field for SHT_GROUP sections should contain the section index of
// the symbol table.
- OS->Link = InX::SymTab->getParent()->SectionIndex;
+ OS->Link = In.SymTab->getParent()->SectionIndex;
// sh_info then contain index of an entry in symbol table section which
// provides signature of the section group.
ObjFile<ELFT> *Obj = Section->getFile<ELFT>();
ArrayRef<Symbol *> Symbols = Obj->getSymbols();
- OS->Info = InX::SymTab->getSymbolIndex(Symbols[Section->Info]);
+ OS->Info = In.SymTab->getSymbolIndex(Symbols[Section->Info]);
}
template <class ELFT> void OutputSection::finalize() {
@@ -308,7 +310,7 @@ template <class ELFT> void OutputSection::finalize() {
if (isa<SyntheticSection>(First))
return;
- Link = InX::SymTab->getParent()->SectionIndex;
+ Link = In.SymTab->getParent()->SectionIndex;
// sh_info for SHT_REL[A] sections should contain the section header index of
// the section to which the relocation applies.
InputSectionBase *S = First->getRelocatedSection();
@@ -406,12 +408,12 @@ void OutputSection::sortInitFini() {
sort([](InputSectionBase *S) { return getPriority(S->Name); });
}
-uint32_t OutputSection::getFiller() {
+std::array<uint8_t, 4> OutputSection::getFiller() {
if (Filler)
return *Filler;
if (Flags & SHF_EXECINSTR)
return Target->TrapInstr;
- return 0;
+ return {0, 0, 0, 0};
}
template void OutputSection::writeHeaderTo<ELF32LE>(ELF32LE::Shdr *Shdr);
diff --git a/ELF/OutputSections.h b/ELF/OutputSections.h
index efb6aabe9743..113bf6836926 100644
--- a/ELF/OutputSections.h
+++ b/ELF/OutputSections.h
@@ -17,6 +17,7 @@
#include "lld/Common/LLVM.h"
#include "llvm/MC/StringTableBuilder.h"
#include "llvm/Object/ELF.h"
+#include <array>
namespace lld {
namespace elf {
@@ -94,7 +95,7 @@ public:
Expr SubalignExpr;
std::vector<BaseCommand *> SectionCommands;
std::vector<StringRef> Phdrs;
- llvm::Optional<uint32_t> Filler;
+ llvm::Optional<std::array<uint8_t, 4>> Filler;
ConstraintKind Constraint = ConstraintKind::NoConstraint;
std::string Location;
std::string MemoryRegionName;
@@ -117,7 +118,7 @@ private:
std::vector<uint8_t> ZDebugHeader;
llvm::SmallVector<char, 1> CompressedData;
- uint32_t getFiller();
+ std::array<uint8_t, 4> getFiller();
};
int getPriority(StringRef S);
@@ -130,7 +131,6 @@ std::vector<InputSection *> getInputSections(OutputSection* OS);
struct Out {
static uint8_t First;
static PhdrEntry *TlsPhdr;
- static OutputSection *DebugInfo;
static OutputSection *ElfHeader;
static OutputSection *ProgramHeaders;
static OutputSection *PreinitArray;
diff --git a/ELF/Relocations.cpp b/ELF/Relocations.cpp
index 467219ad0542..812468896f0d 100644
--- a/ELF/Relocations.cpp
+++ b/ELF/Relocations.cpp
@@ -50,6 +50,7 @@
#include "SyntheticSections.h"
#include "Target.h"
#include "Thunks.h"
+#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
#include "llvm/ADT/SmallSet.h"
@@ -65,6 +66,14 @@ using namespace llvm::support::endian;
using namespace lld;
using namespace lld::elf;
+static Optional<std::string> getLinkerScriptLocation(const Symbol &Sym) {
+ for (BaseCommand *Base : Script->SectionCommands)
+ if (auto *Cmd = dyn_cast<SymbolAssignment>(Base))
+ if (Cmd->Sym == &Sym)
+ return Cmd->Location;
+ return None;
+}
+
// Construct a message in the following format.
//
// >>> defined in /home/alice/src/foo.o
@@ -72,8 +81,13 @@ using namespace lld::elf;
// >>> /home/alice/src/bar.o:(.text+0x1)
static std::string getLocation(InputSectionBase &S, const Symbol &Sym,
uint64_t Off) {
- std::string Msg =
- "\n>>> defined in " + toString(Sym.File) + "\n>>> referenced by ";
+ std::string Msg = "\n>>> defined in ";
+ if (Sym.File)
+ Msg += toString(Sym.File);
+ else if (Optional<std::string> Loc = getLinkerScriptLocation(Sym))
+ Msg += *Loc;
+
+ Msg += "\n>>> referenced by ";
std::string Src = S.getSrcMsg(Sym, Off);
if (!Src.empty())
Msg += Src + "\n>>> ";
@@ -90,12 +104,12 @@ static unsigned handleMipsTlsRelocation(RelType Type, Symbol &Sym,
InputSectionBase &C, uint64_t Offset,
int64_t Addend, RelExpr Expr) {
if (Expr == R_MIPS_TLSLD) {
- InX::MipsGot->addTlsIndex(*C.File);
+ In.MipsGot->addTlsIndex(*C.File);
C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
return 1;
}
if (Expr == R_MIPS_TLSGD) {
- InX::MipsGot->addDynTlsEntry(*C.File, Sym);
+ In.MipsGot->addDynTlsEntry(*C.File, Sym);
C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
return 1;
}
@@ -128,17 +142,17 @@ static unsigned handleARMTlsRelocation(RelType Type, Symbol &Sym,
auto AddTlsReloc = [&](uint64_t Off, RelType Type, Symbol *Dest, bool Dyn) {
if (Dyn)
- InX::RelaDyn->addReloc(Type, InX::Got, Off, Dest);
+ In.RelaDyn->addReloc(Type, In.Got, Off, Dest);
else
- InX::Got->Relocations.push_back({R_ABS, Type, Off, 0, Dest});
+ In.Got->Relocations.push_back({R_ABS, Type, Off, 0, Dest});
};
// Local Dynamic is for access to module local TLS variables, while still
// being suitable for being dynamically loaded via dlopen.
// GOT[e0] is the module index, with a special value of 0 for the current
// module. GOT[e1] is unused. There only needs to be one module index entry.
- if (Expr == R_TLSLD_PC && InX::Got->addTlsIndex()) {
- AddTlsReloc(InX::Got->getTlsIndexOff(), Target->TlsModuleIndexRel,
+ if (Expr == R_TLSLD_PC && In.Got->addTlsIndex()) {
+ AddTlsReloc(In.Got->getTlsIndexOff(), Target->TlsModuleIndexRel,
NeedDynId ? nullptr : &Sym, NeedDynId);
C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
return 1;
@@ -148,8 +162,8 @@ static unsigned handleARMTlsRelocation(RelType Type, Symbol &Sym,
// the module index and offset of symbol in TLS block we can fill these in
// using static GOT relocations.
if (Expr == R_TLSGD_PC) {
- if (InX::Got->addDynTlsEntry(Sym)) {
- uint64_t Off = InX::Got->getGlobalDynOffset(Sym);
+ if (In.Got->addDynTlsEntry(Sym)) {
+ uint64_t Off = In.Got->getGlobalDynOffset(Sym);
AddTlsReloc(Off, Target->TlsModuleIndexRel, &Sym, NeedDynId);
AddTlsReloc(Off + Config->Wordsize, Target->TlsOffsetRel, &Sym,
NeedDynOff);
@@ -165,9 +179,6 @@ template <class ELFT>
static unsigned
handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C,
typename ELFT::uint Offset, int64_t Addend, RelExpr Expr) {
- if (!(C.Flags & SHF_ALLOC))
- return 0;
-
if (!Sym.isTls())
return 0;
@@ -176,12 +187,12 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C,
if (Config->EMachine == EM_MIPS)
return handleMipsTlsRelocation(Type, Sym, C, Offset, Addend, Expr);
- if (isRelExprOneOf<R_TLSDESC, R_TLSDESC_PAGE, R_TLSDESC_CALL>(Expr) &&
+ if (isRelExprOneOf<R_TLSDESC, R_AARCH64_TLSDESC_PAGE, R_TLSDESC_CALL>(Expr) &&
Config->Shared) {
- if (InX::Got->addDynTlsEntry(Sym)) {
- uint64_t Off = InX::Got->getGlobalDynOffset(Sym);
- InX::RelaDyn->addReloc(
- {Target->TlsDescRel, InX::Got, Off, !Sym.IsPreemptible, &Sym, 0});
+ if (In.Got->addDynTlsEntry(Sym)) {
+ uint64_t Off = In.Got->getGlobalDynOffset(Sym);
+ In.RelaDyn->addReloc(
+ {Target->TlsDescRel, In.Got, Off, !Sym.IsPreemptible, &Sym, 0});
}
if (Expr != R_TLSDESC_CALL)
C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
@@ -199,9 +210,9 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C,
}
if (Expr == R_TLSLD_HINT)
return 1;
- if (InX::Got->addTlsIndex())
- InX::RelaDyn->addReloc(Target->TlsModuleIndexRel, InX::Got,
- InX::Got->getTlsIndexOff(), nullptr);
+ if (In.Got->addTlsIndex())
+ In.RelaDyn->addReloc(Target->TlsModuleIndexRel, In.Got,
+ In.Got->getTlsIndexOff(), nullptr);
C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
return 1;
}
@@ -223,29 +234,29 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C,
return 1;
}
if (!Sym.isInGot()) {
- InX::Got->addEntry(Sym);
+ In.Got->addEntry(Sym);
uint64_t Off = Sym.getGotOffset();
- InX::Got->Relocations.push_back({R_ABS, Target->TlsOffsetRel, Off, 0, &Sym});
+ In.Got->Relocations.push_back(
+ {R_ABS, Target->TlsOffsetRel, Off, 0, &Sym});
}
C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
return 1;
}
- if (isRelExprOneOf<R_TLSDESC, R_TLSDESC_PAGE, R_TLSDESC_CALL, R_TLSGD_GOT,
- R_TLSGD_GOT_FROM_END, R_TLSGD_PC>(Expr)) {
+ if (isRelExprOneOf<R_TLSDESC, R_AARCH64_TLSDESC_PAGE, R_TLSDESC_CALL,
+ R_TLSGD_GOT, R_TLSGD_GOT_FROM_END, R_TLSGD_PC>(Expr)) {
if (Config->Shared) {
- if (InX::Got->addDynTlsEntry(Sym)) {
- uint64_t Off = InX::Got->getGlobalDynOffset(Sym);
- InX::RelaDyn->addReloc(Target->TlsModuleIndexRel, InX::Got, Off, &Sym);
+ if (In.Got->addDynTlsEntry(Sym)) {
+ uint64_t Off = In.Got->getGlobalDynOffset(Sym);
+ In.RelaDyn->addReloc(Target->TlsModuleIndexRel, In.Got, Off, &Sym);
// If the symbol is preemptible we need the dynamic linker to write
// the offset too.
uint64_t OffsetOff = Off + Config->Wordsize;
if (Sym.IsPreemptible)
- InX::RelaDyn->addReloc(Target->TlsOffsetRel, InX::Got, OffsetOff,
- &Sym);
+ In.RelaDyn->addReloc(Target->TlsOffsetRel, In.Got, OffsetOff, &Sym);
else
- InX::Got->Relocations.push_back(
+ In.Got->Relocations.push_back(
{R_ABS, Target->TlsOffsetRel, OffsetOff, 0, &Sym});
}
C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
@@ -259,9 +270,9 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C,
{Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_GD_TO_IE), Type,
Offset, Addend, &Sym});
if (!Sym.isInGot()) {
- InX::Got->addEntry(Sym);
- InX::RelaDyn->addReloc(Target->TlsGotRel, InX::Got, Sym.getGotOffset(),
- &Sym);
+ In.Got->addEntry(Sym);
+ In.RelaDyn->addReloc(Target->TlsGotRel, In.Got, Sym.getGotOffset(),
+ &Sym);
}
} else {
C.Relocations.push_back(
@@ -273,13 +284,14 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C,
// Initial-Exec relocs can be relaxed to Local-Exec if the symbol is locally
// defined.
- if (isRelExprOneOf<R_GOT, R_GOT_FROM_END, R_GOT_PC, R_GOT_PAGE_PC>(Expr) &&
+ if (isRelExprOneOf<R_GOT, R_GOT_FROM_END, R_GOT_PC, R_AARCH64_GOT_PAGE_PC,
+ R_GOT_OFF, R_TLSIE_HINT>(Expr) &&
!Config->Shared && !Sym.IsPreemptible) {
C.Relocations.push_back({R_RELAX_TLS_IE_TO_LE, Type, Offset, Addend, &Sym});
return 1;
}
- if (Expr == R_TLSDESC_CALL)
+ if (Expr == R_TLSIE_HINT)
return 1;
return 0;
}
@@ -325,23 +337,25 @@ static bool isAbsoluteValue(const Symbol &Sym) {
// Returns true if Expr refers a PLT entry.
static bool needsPlt(RelExpr Expr) {
- return isRelExprOneOf<R_PLT_PC, R_PPC_CALL_PLT, R_PLT, R_PLT_PAGE_PC>(Expr);
+ return isRelExprOneOf<R_PLT_PC, R_PPC_CALL_PLT, R_PLT, R_AARCH64_PLT_PAGE_PC,
+ R_GOT_PLT, R_AARCH64_GOT_PAGE_PC_PLT>(Expr);
}
// Returns true if Expr refers a GOT entry. Note that this function
// returns false for TLS variables even though they need GOT, because
// TLS variables uses GOT differently than the regular variables.
static bool needsGot(RelExpr Expr) {
- return isRelExprOneOf<R_GOT, R_GOT_OFF, R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOT_OFF,
- R_MIPS_GOT_OFF32, R_GOT_PAGE_PC, R_GOT_PC,
- R_GOT_FROM_END>(Expr);
+ return isRelExprOneOf<R_GOT, R_GOT_OFF, R_HEXAGON_GOT, R_MIPS_GOT_LOCAL_PAGE,
+ R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_AARCH64_GOT_PAGE_PC,
+ R_AARCH64_GOT_PAGE_PC_PLT, R_GOT_PC, R_GOT_FROM_END,
+ R_GOT_PLT>(Expr);
}
// True if this expression is of the form Sym - X, where X is a position in the
// file (PC, or GOT for example).
static bool isRelExpr(RelExpr Expr) {
return isRelExprOneOf<R_PC, R_GOTREL, R_GOTREL_FROM_END, R_MIPS_GOTREL,
- R_PPC_CALL, R_PPC_CALL_PLT, R_PAGE_PC,
+ R_PPC_CALL, R_PPC_CALL_PLT, R_AARCH64_PAGE_PC,
R_RELAX_GOT_PC>(Expr);
}
@@ -357,18 +371,19 @@ static bool isRelExpr(RelExpr Expr) {
static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym,
InputSectionBase &S, uint64_t RelOff) {
// These expressions always compute a constant
- if (isRelExprOneOf<
- R_GOT_FROM_END, R_GOT_OFF, R_TLSLD_GOT_OFF, R_MIPS_GOT_LOCAL_PAGE,
- R_MIPS_GOTREL, R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC,
- R_MIPS_TLSGD, R_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC,
- R_GOTONLY_PC_FROM_END, R_PLT_PC, R_TLSGD_GOT, R_TLSGD_GOT_FROM_END,
- R_TLSGD_PC, R_PPC_CALL_PLT, R_TLSDESC_CALL, R_TLSDESC_PAGE, R_HINT,
- R_TLSLD_HINT>(E))
+ if (isRelExprOneOf<R_GOT_FROM_END, R_GOT_OFF, R_HEXAGON_GOT, R_TLSLD_GOT_OFF,
+ R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOTREL, R_MIPS_GOT_OFF,
+ R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC, R_MIPS_TLSGD,
+ R_AARCH64_GOT_PAGE_PC, R_AARCH64_GOT_PAGE_PC_PLT, R_GOT_PC,
+ R_GOTONLY_PC, R_GOTONLY_PC_FROM_END, R_PLT_PC, R_TLSGD_GOT,
+ R_TLSGD_GOT_FROM_END, R_TLSGD_PC, R_PPC_CALL_PLT,
+ R_TLSDESC_CALL, R_AARCH64_TLSDESC_PAGE, R_HINT,
+ R_TLSLD_HINT, R_TLSIE_HINT>(E))
return true;
// These never do, except if the entire file is position dependent or if
// only the low bits are used.
- if (E == R_GOT || E == R_PLT || E == R_TLSDESC)
+ if (E == R_GOT || E == R_GOT_PLT || E == R_PLT || E == R_TLSDESC)
return Target->usesOnlyLowPageBits(Type) || !Config->Pic;
if (Sym.IsPreemptible)
@@ -414,10 +429,14 @@ static RelExpr toPlt(RelExpr Expr) {
return R_PPC_CALL_PLT;
case R_PC:
return R_PLT_PC;
- case R_PAGE_PC:
- return R_PLT_PAGE_PC;
+ case R_AARCH64_PAGE_PC:
+ return R_AARCH64_PLT_PAGE_PC;
+ case R_AARCH64_GOT_PAGE_PC:
+ return R_AARCH64_GOT_PAGE_PC_PLT;
case R_ABS:
return R_PLT;
+ case R_GOT:
+ return R_GOT_PLT;
default:
return Expr;
}
@@ -466,7 +485,7 @@ static SmallSet<SharedSymbol *, 4> getSymbolsAt(SharedSymbol &SS) {
SmallSet<SharedSymbol *, 4> Ret;
for (const Elf_Sym &S : File.getGlobalELFSyms()) {
if (S.st_shndx == SHN_UNDEF || S.st_shndx == SHN_ABS ||
- S.st_value != SS.Value)
+ S.getType() == STT_TLS || S.st_value != SS.Value)
continue;
StringRef Name = check(S.getName(File.getStringTable()));
Symbol *Sym = Symtab->find(Name);
@@ -489,6 +508,7 @@ static void replaceWithDefined(Symbol &Sym, SectionBase *Sec, uint64_t Value,
Sym.PltIndex = Old.PltIndex;
Sym.GotIndex = Old.GotIndex;
Sym.VerdefIndex = Old.VerdefIndex;
+ Sym.PPC64BranchltIndex = Old.PPC64BranchltIndex;
Sym.IsPreemptible = true;
Sym.ExportDynamic = true;
Sym.IsUsedInRegularObj = true;
@@ -549,9 +569,9 @@ template <class ELFT> static void addCopyRelSymbol(SharedSymbol &SS) {
BssSection *Sec = make<BssSection>(IsReadOnly ? ".bss.rel.ro" : ".bss",
SymSize, SS.Alignment);
if (IsReadOnly)
- InX::BssRelRo->getParent()->addSection(Sec);
+ In.BssRelRo->getParent()->addSection(Sec);
else
- InX::Bss->getParent()->addSection(Sec);
+ In.Bss->getParent()->addSection(Sec);
// Look through the DSO's dynamic symbol table for aliases and create a
// dynamic symbol for each one. This causes the copy relocation to correctly
@@ -559,7 +579,7 @@ template <class ELFT> static void addCopyRelSymbol(SharedSymbol &SS) {
for (SharedSymbol *Sym : getSymbolsAt<ELFT>(SS))
replaceWithDefined(*Sym, Sec, 0, Sym->Size);
- InX::RelaDyn->addReloc(Target->CopyRel, Sec, 0, &SS);
+ In.RelaDyn->addReloc(Target->CopyRel, Sec, 0, &SS);
}
// MIPS has an odd notion of "paired" relocations to calculate addends.
@@ -583,7 +603,7 @@ static int64_t computeMipsAddend(const RelTy &Rel, const RelTy *End,
if (PairTy == R_MIPS_NONE)
return 0;
- const uint8_t *Buf = Sec.Data.data();
+ const uint8_t *Buf = Sec.data().data();
uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL);
// To make things worse, paired relocations might not be contiguous in
@@ -611,7 +631,7 @@ static int64_t computeAddend(const RelTy &Rel, const RelTy *End,
if (RelTy::IsRela) {
Addend = getAddend<ELFT>(Rel);
} else {
- const uint8_t *Buf = Sec.Data.data();
+ const uint8_t *Buf = Sec.data().data();
Addend = Target->getImplicitAddend(Buf + Rel.r_offset, Type);
}
@@ -627,9 +647,6 @@ static int64_t computeAddend(const RelTy &Rel, const RelTy *End,
// Returns true if this function printed out an error message.
static bool maybeReportUndefined(Symbol &Sym, InputSectionBase &Sec,
uint64_t Offset) {
- if (Config->UnresolvedSymbols == UnresolvedPolicy::IgnoreAll)
- return false;
-
if (Sym.isLocal() || !Sym.isUndefined() || Sym.isWeak())
return false;
@@ -646,6 +663,10 @@ static bool maybeReportUndefined(Symbol &Sym, InputSectionBase &Sec,
Msg += Src + "\n>>> ";
Msg += Sec.getObjMsg(Offset);
+ if (Sym.getName().startswith("_ZTV"))
+ Msg += "\nthe vtable symbol may be undefined because the class is missing "
+ "its key function (see https://lld.llvm.org/missingkeyfunction)";
+
if ((Config->UnresolvedSymbols == UnresolvedPolicy::Warn && CanBeExternal) ||
Config->NoinhibitExec) {
warn(Msg);
@@ -700,7 +721,7 @@ public:
while (I != Pieces.size() && Pieces[I].InputOff + Pieces[I].Size <= Off)
++I;
if (I == Pieces.size())
- return Off;
+ fatal(".eh_frame: relocation is not in any piece");
// Pieces must be contiguous, so there must be no holes in between.
assert(Pieces[I].InputOff <= Off && "Relocation not in any piece");
@@ -726,13 +747,13 @@ static void addRelativeReloc(InputSectionBase *IS, uint64_t OffsetInSec,
// RelrDyn sections don't support odd offsets. Also, RelrDyn sections
// don't store the addend values, so we must write it to the relocated
// address.
- if (InX::RelrDyn && IS->Alignment >= 2 && OffsetInSec % 2 == 0) {
+ if (In.RelrDyn && IS->Alignment >= 2 && OffsetInSec % 2 == 0) {
IS->Relocations.push_back({Expr, Type, OffsetInSec, Addend, Sym});
- InX::RelrDyn->Relocs.push_back({IS, OffsetInSec});
+ In.RelrDyn->Relocs.push_back({IS, OffsetInSec});
return;
}
- InX::RelaDyn->addReloc(Target->RelativeRel, IS, OffsetInSec, Sym, Addend,
- Expr, Type);
+ In.RelaDyn->addReloc(Target->RelativeRel, IS, OffsetInSec, Sym, Addend, Expr,
+ Type);
}
template <class ELFT, class GotPltSection>
@@ -745,9 +766,16 @@ static void addPltEntry(PltSection *Plt, GotPltSection *GotPlt,
}
template <class ELFT> static void addGotEntry(Symbol &Sym) {
- InX::Got->addEntry(Sym);
+ In.Got->addEntry(Sym);
+
+ RelExpr Expr;
+ if (Sym.isTls())
+ Expr = R_TLS;
+ else if (Sym.isGnuIFunc())
+ Expr = R_PLT;
+ else
+ Expr = R_ABS;
- RelExpr Expr = Sym.isTls() ? R_TLS : R_ABS;
uint64_t Off = Sym.getGotOffset();
// If a GOT slot value can be calculated at link-time, which is now,
@@ -760,19 +788,19 @@ template <class ELFT> static void addGotEntry(Symbol &Sym) {
bool IsLinkTimeConstant =
!Sym.IsPreemptible && (!Config->Pic || isAbsolute(Sym));
if (IsLinkTimeConstant) {
- InX::Got->Relocations.push_back({Expr, Target->GotRel, Off, 0, &Sym});
+ In.Got->Relocations.push_back({Expr, Target->GotRel, Off, 0, &Sym});
return;
}
// Otherwise, we emit a dynamic relocation to .rel[a].dyn so that
// the GOT slot will be fixed at load-time.
if (!Sym.isTls() && !Sym.IsPreemptible && Config->Pic && !isAbsolute(Sym)) {
- addRelativeReloc(InX::Got, Off, &Sym, 0, R_ABS, Target->GotRel);
+ addRelativeReloc(In.Got, Off, &Sym, 0, R_ABS, Target->GotRel);
return;
}
- InX::RelaDyn->addReloc(Sym.isTls() ? Target->TlsGotRel : Target->GotRel,
- InX::Got, Off, &Sym, 0,
- Sym.IsPreemptible ? R_ADDEND : R_ABS, Target->GotRel);
+ In.RelaDyn->addReloc(Sym.isTls() ? Target->TlsGotRel : Target->GotRel, In.Got,
+ Off, &Sym, 0, Sym.IsPreemptible ? R_ADDEND : R_ABS,
+ Target->GotRel);
}
// Return true if we can define a symbol in the executable that
@@ -825,7 +853,7 @@ static void processRelocAux(InputSectionBase &Sec, RelExpr Expr, RelType Type,
addRelativeReloc(&Sec, Offset, &Sym, Addend, Expr, Type);
return;
} else if (RelType Rel = Target->getDynRel(Type)) {
- InX::RelaDyn->addReloc(Rel, &Sec, Offset, &Sym, Addend, R_ADDEND, Type);
+ In.RelaDyn->addReloc(Rel, &Sec, Offset, &Sym, Addend, R_ADDEND, Type);
// MIPS ABI turns using of GOT and dynamic relocations inside out.
// While regular ABI uses dynamic relocations to fill up GOT entries
@@ -843,7 +871,7 @@ static void processRelocAux(InputSectionBase &Sec, RelExpr Expr, RelType Type,
// a dynamic relocation.
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf p.4-19
if (Config->EMachine == EM_MIPS)
- InX::MipsGot->addEntry(*Sec.File, Sym, Addend, Expr);
+ In.MipsGot->addEntry(*Sec.File, Sym, Addend, Expr);
return;
}
}
@@ -930,10 +958,9 @@ static void processRelocAux(InputSectionBase &Sec, RelExpr Expr, RelType Type,
"' cannot be preempted; recompile with -fPIE" +
getLocation(Sec, Sym, Offset));
if (!Sym.isInPlt())
- addPltEntry<ELFT>(InX::Plt, InX::GotPlt, InX::RelaPlt, Target->PltRel,
- Sym);
+ addPltEntry<ELFT>(In.Plt, In.GotPlt, In.RelaPlt, Target->PltRel, Sym);
if (!Sym.isDefined())
- replaceWithDefined(Sym, InX::Plt, Sym.getPltOffset(), 0);
+ replaceWithDefined(Sym, In.Plt, getPltEntryOffset(Sym.PltIndex), 0);
Sym.NeedsPltAddr = true;
Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
return;
@@ -967,7 +994,7 @@ static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I,
if (maybeReportUndefined(Sym, Sec, Rel.r_offset))
return;
- const uint8_t *RelocatedAddr = Sec.Data.begin() + Rel.r_offset;
+ const uint8_t *RelocatedAddr = Sec.data().begin() + Rel.r_offset;
RelExpr Expr = Target->getRelExpr(Type, Sym, RelocatedAddr);
// Ignore "hint" relocations because they are only markers for relaxation.
@@ -985,18 +1012,28 @@ static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I,
// all dynamic symbols that can be resolved within the executable will
// actually be resolved that way at runtime, because the main exectuable
// is always at the beginning of a search list. We can leverage that fact.
- if (Sym.isGnuIFunc())
+ if (Sym.isGnuIFunc()) {
+ if (!Config->ZText && Config->WarnIfuncTextrel) {
+ warn("using ifunc symbols when text relocations are allowed may produce "
+ "a binary that will segfault, if the object file is linked with "
+ "old version of glibc (glibc 2.28 and earlier). If this applies to "
+ "you, consider recompiling the object files without -fPIC and "
+ "without -Wl,-z,notext option. Use -no-warn-ifunc-textrel to "
+ "turn off this warning." +
+ getLocation(Sec, Sym, Offset));
+ }
Expr = toPlt(Expr);
- else if (!Sym.IsPreemptible && Expr == R_GOT_PC && !isAbsoluteValue(Sym))
+ } else if (!Sym.IsPreemptible && Expr == R_GOT_PC && !isAbsoluteValue(Sym)) {
Expr = Target->adjustRelaxExpr(Type, RelocatedAddr, Expr);
- else if (!Sym.IsPreemptible)
+ } else if (!Sym.IsPreemptible) {
Expr = fromPlt(Expr);
+ }
// This relocation does not require got entry, but it is relative to got and
// needs it to be created. Here we request for that.
if (isRelExprOneOf<R_GOTONLY_PC, R_GOTONLY_PC_FROM_END, R_GOTREL,
R_GOTREL_FROM_END, R_PPC_TOC>(Expr))
- InX::Got->HasGotOffRel = true;
+ In.Got->HasGotOffRel = true;
// Read an addend.
int64_t Addend = computeAddend<ELFT>(Rel, End, Sec, Expr, Sym.isLocal());
@@ -1012,11 +1049,10 @@ static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I,
// If a relocation needs PLT, we create PLT and GOTPLT slots for the symbol.
if (needsPlt(Expr) && !Sym.isInPlt()) {
if (Sym.isGnuIFunc() && !Sym.IsPreemptible)
- addPltEntry<ELFT>(InX::Iplt, InX::IgotPlt, InX::RelaIplt,
- Target->IRelativeRel, Sym);
- else
- addPltEntry<ELFT>(InX::Plt, InX::GotPlt, InX::RelaPlt, Target->PltRel,
+ addPltEntry<ELFT>(In.Iplt, In.IgotPlt, In.RelaIplt, Target->IRelativeRel,
Sym);
+ else
+ addPltEntry<ELFT>(In.Plt, In.GotPlt, In.RelaPlt, Target->PltRel, Sym);
}
// Create a GOT slot if a relocation needs GOT.
@@ -1029,7 +1065,7 @@ static void scanReloc(InputSectionBase &Sec, OffsetGetter &GetOffset, RelTy *&I,
// See "Global Offset Table" in Chapter 5 in the following document
// for detailed description:
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
- InX::MipsGot->addEntry(*Sec.File, Sym, Addend, Expr);
+ In.MipsGot->addEntry(*Sec.File, Sym, Addend, Expr);
} else if (!Sym.isInGot()) {
addGotEntry<ELFT>(Sym);
}
@@ -1047,6 +1083,11 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef<RelTy> Rels) {
for (auto I = Rels.begin(), End = Rels.end(); I != End;)
scanReloc<ELFT>(Sec, GetOffset, I, End);
+
+ // Sort relocations by offset to binary search for R_RISCV_PCREL_HI20
+ if (Config->EMachine == EM_RISCV)
+ std::stable_sort(Sec.Relocations.begin(), Sec.Relocations.end(),
+ RelocationOffsetComparator{});
}
template <class ELFT> void elf::scanRelocations(InputSectionBase &S) {
@@ -1056,6 +1097,43 @@ template <class ELFT> void elf::scanRelocations(InputSectionBase &S) {
scanRelocs<ELFT>(S, S.rels<ELFT>());
}
+static bool mergeCmp(const InputSection *A, const InputSection *B) {
+ // std::merge requires a strict weak ordering.
+ if (A->OutSecOff < B->OutSecOff)
+ return true;
+
+ if (A->OutSecOff == B->OutSecOff) {
+ auto *TA = dyn_cast<ThunkSection>(A);
+ auto *TB = dyn_cast<ThunkSection>(B);
+
+ // Check if Thunk is immediately before any specific Target
+ // InputSection for example Mips LA25 Thunks.
+ if (TA && TA->getTargetInputSection() == B)
+ return true;
+
+ // Place Thunk Sections without specific targets before
+ // non-Thunk Sections.
+ if (TA && !TB && !TA->getTargetInputSection())
+ return true;
+ }
+
+ return false;
+}
+
+// Call Fn on every executable InputSection accessed via the linker script
+// InputSectionDescription::Sections.
+static void forEachInputSectionDescription(
+ ArrayRef<OutputSection *> OutputSections,
+ llvm::function_ref<void(OutputSection *, InputSectionDescription *)> Fn) {
+ for (OutputSection *OS : OutputSections) {
+ if (!(OS->Flags & SHF_ALLOC) || !(OS->Flags & SHF_EXECINSTR))
+ continue;
+ for (BaseCommand *BC : OS->SectionCommands)
+ if (auto *ISD = dyn_cast<InputSectionDescription>(BC))
+ Fn(OS, ISD);
+ }
+}
+
// Thunk Implementation
//
// Thunks (sometimes called stubs, veneers or branch islands) are small pieces
@@ -1158,6 +1236,7 @@ void ThunkCreator::mergeThunks(ArrayRef<OutputSection *> OutputSections) {
[](const std::pair<ThunkSection *, uint32_t> &TS) {
return TS.first->getSize() == 0;
});
+
// ISD->ThunkSections contains all created ThunkSections, including
// those inserted in previous passes. Extract the Thunks created this
// pass and order them in ascending OutSecOff.
@@ -1173,27 +1252,11 @@ void ThunkCreator::mergeThunks(ArrayRef<OutputSection *> OutputSections) {
// Merge sorted vectors of Thunks and InputSections by OutSecOff
std::vector<InputSection *> Tmp;
Tmp.reserve(ISD->Sections.size() + NewThunks.size());
- auto MergeCmp = [](const InputSection *A, const InputSection *B) {
- // std::merge requires a strict weak ordering.
- if (A->OutSecOff < B->OutSecOff)
- return true;
- if (A->OutSecOff == B->OutSecOff) {
- auto *TA = dyn_cast<ThunkSection>(A);
- auto *TB = dyn_cast<ThunkSection>(B);
- // Check if Thunk is immediately before any specific Target
- // InputSection for example Mips LA25 Thunks.
- if (TA && TA->getTargetInputSection() == B)
- return true;
- if (TA && !TB && !TA->getTargetInputSection())
- // Place Thunk Sections without specific targets before
- // non-Thunk Sections.
- return true;
- }
- return false;
- };
+
std::merge(ISD->Sections.begin(), ISD->Sections.end(),
NewThunks.begin(), NewThunks.end(), std::back_inserter(Tmp),
- MergeCmp);
+ mergeCmp);
+
ISD->Sections = std::move(Tmp);
});
}
@@ -1237,20 +1300,23 @@ ThunkSection *ThunkCreator::getISThunkSec(InputSection *IS) {
// Find InputSectionRange within Target Output Section (TOS) that the
// InputSection (IS) that we need to precede is in.
OutputSection *TOS = IS->getParent();
- for (BaseCommand *BC : TOS->SectionCommands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(BC)) {
- if (ISD->Sections.empty())
- continue;
- InputSection *first = ISD->Sections.front();
- InputSection *last = ISD->Sections.back();
- if (IS->OutSecOff >= first->OutSecOff &&
- IS->OutSecOff <= last->OutSecOff) {
- TS = addThunkSection(TOS, ISD, IS->OutSecOff);
- ThunkedSections[IS] = TS;
- break;
- }
- }
- return TS;
+ for (BaseCommand *BC : TOS->SectionCommands) {
+ auto *ISD = dyn_cast<InputSectionDescription>(BC);
+ if (!ISD || ISD->Sections.empty())
+ continue;
+
+ InputSection *First = ISD->Sections.front();
+ InputSection *Last = ISD->Sections.back();
+
+ if (IS->OutSecOff < First->OutSecOff || Last->OutSecOff < IS->OutSecOff)
+ continue;
+
+ TS = addThunkSection(TOS, ISD, IS->OutSecOff);
+ ThunkedSections[IS] = TS;
+ return TS;
+ }
+
+ return nullptr;
}
// Create one or more ThunkSections per OS that can be used to place Thunks.
@@ -1271,26 +1337,29 @@ ThunkSection *ThunkCreator::getISThunkSec(InputSection *IS) {
// allow for the creation of a short thunk.
void ThunkCreator::createInitialThunkSections(
ArrayRef<OutputSection *> OutputSections) {
+ uint32_t ThunkSectionSpacing = Target->getThunkSectionSpacing();
+
forEachInputSectionDescription(
OutputSections, [&](OutputSection *OS, InputSectionDescription *ISD) {
if (ISD->Sections.empty())
return;
+
uint32_t ISDBegin = ISD->Sections.front()->OutSecOff;
uint32_t ISDEnd =
ISD->Sections.back()->OutSecOff + ISD->Sections.back()->getSize();
uint32_t LastThunkLowerBound = -1;
- if (ISDEnd - ISDBegin > Target->ThunkSectionSpacing * 2)
- LastThunkLowerBound = ISDEnd - Target->ThunkSectionSpacing;
+ if (ISDEnd - ISDBegin > ThunkSectionSpacing * 2)
+ LastThunkLowerBound = ISDEnd - ThunkSectionSpacing;
uint32_t ISLimit;
uint32_t PrevISLimit = ISDBegin;
- uint32_t ThunkUpperBound = ISDBegin + Target->ThunkSectionSpacing;
+ uint32_t ThunkUpperBound = ISDBegin + ThunkSectionSpacing;
for (const InputSection *IS : ISD->Sections) {
ISLimit = IS->OutSecOff + IS->getSize();
if (ISLimit > ThunkUpperBound) {
addThunkSection(OS, ISD, PrevISLimit);
- ThunkUpperBound = PrevISLimit + Target->ThunkSectionSpacing;
+ ThunkUpperBound = PrevISLimit + ThunkSectionSpacing;
}
if (ISLimit > LastThunkLowerBound)
break;
@@ -1304,13 +1373,14 @@ ThunkSection *ThunkCreator::addThunkSection(OutputSection *OS,
InputSectionDescription *ISD,
uint64_t Off) {
auto *TS = make<ThunkSection>(OS, Off);
- ISD->ThunkSections.push_back(std::make_pair(TS, Pass));
+ ISD->ThunkSections.push_back({TS, Pass});
return TS;
}
std::pair<Thunk *, bool> ThunkCreator::getThunk(Symbol &Sym, RelType Type,
uint64_t Src) {
std::vector<Thunk *> *ThunkVec = nullptr;
+
// We use (section, offset) pair to find the thunk position if possible so
// that we create only one thunk for aliased symbols or ICFed sections.
if (auto *D = dyn_cast<Defined>(&Sym))
@@ -1318,40 +1388,28 @@ std::pair<Thunk *, bool> ThunkCreator::getThunk(Symbol &Sym, RelType Type,
ThunkVec = &ThunkedSymbolsBySection[{D->Section->Repl, D->Value}];
if (!ThunkVec)
ThunkVec = &ThunkedSymbols[&Sym];
+
// Check existing Thunks for Sym to see if they can be reused
- for (Thunk *ET : *ThunkVec)
- if (ET->isCompatibleWith(Type) &&
- Target->inBranchRange(Type, Src, ET->getThunkTargetSym()->getVA()))
- return std::make_pair(ET, false);
+ for (Thunk *T : *ThunkVec)
+ if (T->isCompatibleWith(Type) &&
+ Target->inBranchRange(Type, Src, T->getThunkTargetSym()->getVA()))
+ return std::make_pair(T, false);
+
// No existing compatible Thunk in range, create a new one
Thunk *T = addThunk(Type, Sym);
ThunkVec->push_back(T);
return std::make_pair(T, true);
}
-// Call Fn on every executable InputSection accessed via the linker script
-// InputSectionDescription::Sections.
-void ThunkCreator::forEachInputSectionDescription(
- ArrayRef<OutputSection *> OutputSections,
- llvm::function_ref<void(OutputSection *, InputSectionDescription *)> Fn) {
- for (OutputSection *OS : OutputSections) {
- if (!(OS->Flags & SHF_ALLOC) || !(OS->Flags & SHF_EXECINSTR))
- continue;
- for (BaseCommand *BC : OS->SectionCommands)
- if (auto *ISD = dyn_cast<InputSectionDescription>(BC))
- Fn(OS, ISD);
- }
-}
-
// Return true if the relocation target is an in range Thunk.
// Return false if the relocation is not to a Thunk. If the relocation target
// was originally to a Thunk, but is no longer in range we revert the
// relocation back to its original non-Thunk target.
bool ThunkCreator::normalizeExistingThunk(Relocation &Rel, uint64_t Src) {
- if (Thunk *ET = Thunks.lookup(Rel.Sym)) {
+ if (Thunk *T = Thunks.lookup(Rel.Sym)) {
if (Target->inBranchRange(Rel.Type, Src, Rel.Sym->getVA()))
return true;
- Rel.Sym = &ET->Destination;
+ Rel.Sym = &T->Destination;
if (Rel.Sym->isInPlt())
Rel.E