import of lld from LLVM-16.0.6
authorrobert <robert@openbsd.org>
Sat, 11 Nov 2023 18:21:06 +0000 (18:21 +0000)
committerrobert <robert@openbsd.org>
Sat, 11 Nov 2023 18:21:06 +0000 (18:21 +0000)
199 files changed:
gnu/llvm/lld/CMakeLists.txt
gnu/llvm/lld/COFF/CMakeLists.txt
gnu/llvm/lld/COFF/COFFLinkerContext.cpp [new file with mode: 0644]
gnu/llvm/lld/COFF/COFFLinkerContext.h [new file with mode: 0644]
gnu/llvm/lld/COFF/CallGraphSort.cpp
gnu/llvm/lld/COFF/CallGraphSort.h
gnu/llvm/lld/COFF/Chunks.cpp
gnu/llvm/lld/COFF/Chunks.h
gnu/llvm/lld/COFF/Config.h
gnu/llvm/lld/COFF/DLL.cpp
gnu/llvm/lld/COFF/DLL.h
gnu/llvm/lld/COFF/DebugTypes.cpp
gnu/llvm/lld/COFF/DebugTypes.h
gnu/llvm/lld/COFF/Driver.cpp
gnu/llvm/lld/COFF/Driver.h
gnu/llvm/lld/COFF/DriverUtils.cpp
gnu/llvm/lld/COFF/ICF.cpp
gnu/llvm/lld/COFF/ICF.h
gnu/llvm/lld/COFF/InputFiles.cpp
gnu/llvm/lld/COFF/InputFiles.h
gnu/llvm/lld/COFF/LLDMapFile.cpp
gnu/llvm/lld/COFF/LLDMapFile.h
gnu/llvm/lld/COFF/LTO.cpp
gnu/llvm/lld/COFF/LTO.h
gnu/llvm/lld/COFF/MapFile.cpp
gnu/llvm/lld/COFF/MapFile.h
gnu/llvm/lld/COFF/MarkLive.cpp
gnu/llvm/lld/COFF/MarkLive.h
gnu/llvm/lld/COFF/MinGW.cpp
gnu/llvm/lld/COFF/MinGW.h
gnu/llvm/lld/COFF/Options.td
gnu/llvm/lld/COFF/PDB.cpp
gnu/llvm/lld/COFF/PDB.h
gnu/llvm/lld/COFF/SymbolTable.cpp
gnu/llvm/lld/COFF/SymbolTable.h
gnu/llvm/lld/COFF/Symbols.cpp
gnu/llvm/lld/COFF/Symbols.h
gnu/llvm/lld/COFF/TypeMerger.h
gnu/llvm/lld/COFF/Writer.cpp
gnu/llvm/lld/COFF/Writer.h
gnu/llvm/lld/Common/Args.cpp
gnu/llvm/lld/Common/CMakeLists.txt
gnu/llvm/lld/Common/CommonLinkerContext.cpp [new file with mode: 0644]
gnu/llvm/lld/Common/DWARF.cpp
gnu/llvm/lld/Common/ErrorHandler.cpp
gnu/llvm/lld/Common/Memory.cpp
gnu/llvm/lld/Common/Strings.cpp
gnu/llvm/lld/Common/TargetOptionsCommandFlags.cpp
gnu/llvm/lld/Common/Timer.cpp
gnu/llvm/lld/Common/Version.cpp
gnu/llvm/lld/ELF/AArch64ErrataFix.cpp
gnu/llvm/lld/ELF/AArch64ErrataFix.h
gnu/llvm/lld/ELF/ARMErrataFix.cpp
gnu/llvm/lld/ELF/ARMErrataFix.h
gnu/llvm/lld/ELF/Arch/AMDGPU.cpp
gnu/llvm/lld/ELF/Arch/ARM.cpp
gnu/llvm/lld/ELF/Arch/AVR.cpp
gnu/llvm/lld/ELF/Arch/Hexagon.cpp
gnu/llvm/lld/ELF/Arch/MSP430.cpp
gnu/llvm/lld/ELF/Arch/Mips.cpp
gnu/llvm/lld/ELF/Arch/MipsArchTree.cpp
gnu/llvm/lld/ELF/Arch/SPARCV9.cpp
gnu/llvm/lld/ELF/Arch/X86.cpp
gnu/llvm/lld/ELF/CMakeLists.txt
gnu/llvm/lld/ELF/CallGraphSort.cpp
gnu/llvm/lld/ELF/CallGraphSort.h
gnu/llvm/lld/ELF/DWARF.cpp
gnu/llvm/lld/ELF/DWARF.h
gnu/llvm/lld/ELF/Driver.h
gnu/llvm/lld/ELF/EhFrame.cpp
gnu/llvm/lld/ELF/EhFrame.h
gnu/llvm/lld/ELF/ICF.cpp
gnu/llvm/lld/ELF/ICF.h
gnu/llvm/lld/ELF/InputSection.h
gnu/llvm/lld/ELF/LTO.cpp
gnu/llvm/lld/ELF/LTO.h
gnu/llvm/lld/ELF/LinkerScript.h
gnu/llvm/lld/ELF/MapFile.cpp
gnu/llvm/lld/ELF/MapFile.h
gnu/llvm/lld/ELF/MarkLive.cpp
gnu/llvm/lld/ELF/MarkLive.h
gnu/llvm/lld/ELF/OutputSections.cpp
gnu/llvm/lld/ELF/OutputSections.h
gnu/llvm/lld/ELF/Relocations.h
gnu/llvm/lld/ELF/ScriptLexer.cpp
gnu/llvm/lld/ELF/ScriptLexer.h
gnu/llvm/lld/ELF/ScriptParser.h
gnu/llvm/lld/ELF/SymbolTable.h
gnu/llvm/lld/ELF/Target.cpp
gnu/llvm/lld/ELF/Target.h
gnu/llvm/lld/ELF/Thunks.cpp
gnu/llvm/lld/ELF/Thunks.h
gnu/llvm/lld/MachO/Arch/ARM.cpp
gnu/llvm/lld/MachO/Arch/ARM64.cpp
gnu/llvm/lld/MachO/Arch/ARM64Common.cpp
gnu/llvm/lld/MachO/Arch/ARM64Common.h
gnu/llvm/lld/MachO/Arch/ARM64_32.cpp
gnu/llvm/lld/MachO/Arch/X86_64.cpp
gnu/llvm/lld/MachO/CMakeLists.txt
gnu/llvm/lld/MachO/ConcatOutputSection.cpp
gnu/llvm/lld/MachO/ConcatOutputSection.h
gnu/llvm/lld/MachO/Config.h
gnu/llvm/lld/MachO/Driver.cpp
gnu/llvm/lld/MachO/Driver.h
gnu/llvm/lld/MachO/DriverUtils.cpp
gnu/llvm/lld/MachO/Dwarf.cpp
gnu/llvm/lld/MachO/Dwarf.h
gnu/llvm/lld/MachO/EhFrame.cpp [new file with mode: 0644]
gnu/llvm/lld/MachO/EhFrame.h [new file with mode: 0644]
gnu/llvm/lld/MachO/ExportTrie.cpp
gnu/llvm/lld/MachO/ExportTrie.h
gnu/llvm/lld/MachO/ICF.cpp
gnu/llvm/lld/MachO/ICF.h
gnu/llvm/lld/MachO/InputFiles.cpp
gnu/llvm/lld/MachO/InputFiles.h
gnu/llvm/lld/MachO/InputSection.cpp
gnu/llvm/lld/MachO/InputSection.h
gnu/llvm/lld/MachO/LTO.cpp
gnu/llvm/lld/MachO/LTO.h
gnu/llvm/lld/MachO/MachOStructs.h
gnu/llvm/lld/MachO/MapFile.cpp
gnu/llvm/lld/MachO/MapFile.h
gnu/llvm/lld/MachO/MarkLive.cpp
gnu/llvm/lld/MachO/MarkLive.h
gnu/llvm/lld/MachO/ObjC.cpp
gnu/llvm/lld/MachO/ObjC.h
gnu/llvm/lld/MachO/Options.td
gnu/llvm/lld/MachO/OutputSection.cpp
gnu/llvm/lld/MachO/OutputSection.h
gnu/llvm/lld/MachO/OutputSegment.cpp
gnu/llvm/lld/MachO/OutputSegment.h
gnu/llvm/lld/MachO/Relocations.cpp
gnu/llvm/lld/MachO/Relocations.h
gnu/llvm/lld/MachO/SectionPriorities.cpp [new file with mode: 0644]
gnu/llvm/lld/MachO/SectionPriorities.h [new file with mode: 0644]
gnu/llvm/lld/MachO/SymbolTable.cpp
gnu/llvm/lld/MachO/SymbolTable.h
gnu/llvm/lld/MachO/Symbols.cpp
gnu/llvm/lld/MachO/Symbols.h
gnu/llvm/lld/MachO/SyntheticSections.cpp
gnu/llvm/lld/MachO/SyntheticSections.h
gnu/llvm/lld/MachO/Target.h
gnu/llvm/lld/MachO/UnwindInfoSection.cpp
gnu/llvm/lld/MachO/UnwindInfoSection.h
gnu/llvm/lld/MachO/Writer.cpp
gnu/llvm/lld/MachO/Writer.h
gnu/llvm/lld/MinGW/CMakeLists.txt
gnu/llvm/lld/MinGW/Driver.cpp
gnu/llvm/lld/MinGW/Options.td
gnu/llvm/lld/cmake/modules/AddLLD.cmake
gnu/llvm/lld/cmake/modules/CMakeLists.txt
gnu/llvm/lld/cmake/modules/LLDConfig.cmake.in
gnu/llvm/lld/cmake/modules/LLDConfigVersion.cmake.in [new file with mode: 0644]
gnu/llvm/lld/docs/ELF/linker_script.rst
gnu/llvm/lld/docs/ELF/start-stop-gc.rst [new file with mode: 0644]
gnu/llvm/lld/docs/MachO/index.rst [new file with mode: 0644]
gnu/llvm/lld/docs/MachO/ld64-vs-lld.rst [new file with mode: 0644]
gnu/llvm/lld/docs/ReleaseNotes.rst
gnu/llvm/lld/docs/WebAssembly.rst
gnu/llvm/lld/docs/_templates/indexsidebar.html
gnu/llvm/lld/docs/conf.py
gnu/llvm/lld/docs/index.rst
gnu/llvm/lld/include/lld/Common/Args.h
gnu/llvm/lld/include/lld/Common/CommonLinkerContext.h [new file with mode: 0644]
gnu/llvm/lld/include/lld/Common/DWARF.h
gnu/llvm/lld/include/lld/Common/Driver.h
gnu/llvm/lld/include/lld/Common/ErrorHandler.h
gnu/llvm/lld/include/lld/Common/LLVM.h
gnu/llvm/lld/include/lld/Common/Memory.h
gnu/llvm/lld/include/lld/Common/Strings.h
gnu/llvm/lld/include/lld/Common/TargetOptionsCommandFlags.h
gnu/llvm/lld/include/lld/Common/Timer.h
gnu/llvm/lld/tools/lld/CMakeLists.txt
gnu/llvm/lld/wasm/CMakeLists.txt
gnu/llvm/lld/wasm/Config.h
gnu/llvm/lld/wasm/Driver.cpp
gnu/llvm/lld/wasm/InputChunks.cpp
gnu/llvm/lld/wasm/InputChunks.h
gnu/llvm/lld/wasm/InputElement.h
gnu/llvm/lld/wasm/InputFiles.cpp
gnu/llvm/lld/wasm/InputFiles.h
gnu/llvm/lld/wasm/LTO.cpp
gnu/llvm/lld/wasm/MapFile.cpp
gnu/llvm/lld/wasm/MarkLive.cpp
gnu/llvm/lld/wasm/Options.td
gnu/llvm/lld/wasm/OutputSections.cpp
gnu/llvm/lld/wasm/OutputSections.h
gnu/llvm/lld/wasm/OutputSegment.cpp
gnu/llvm/lld/wasm/OutputSegment.h
gnu/llvm/lld/wasm/Relocations.cpp
gnu/llvm/lld/wasm/SymbolTable.cpp
gnu/llvm/lld/wasm/SymbolTable.h
gnu/llvm/lld/wasm/Symbols.cpp
gnu/llvm/lld/wasm/Symbols.h
gnu/llvm/lld/wasm/SyntheticSections.cpp
gnu/llvm/lld/wasm/SyntheticSections.h
gnu/llvm/lld/wasm/Writer.cpp
gnu/llvm/lld/wasm/WriterUtils.cpp
gnu/llvm/lld/wasm/WriterUtils.h

index 2e99564..3d62256 100644 (file)
@@ -1,56 +1,49 @@
-# Check if lld is built as a standalone project.
+cmake_minimum_required(VERSION 3.13.4)
+
+if(NOT DEFINED LLVM_COMMON_CMAKE_UTILS)
+  set(LLVM_COMMON_CMAKE_UTILS ${CMAKE_CURRENT_SOURCE_DIR}/../cmake)
+endif()
+include(${LLVM_COMMON_CMAKE_UTILS}/Modules/CMakePolicy.cmake
+  NO_POLICY_SCOPE)
+
+# If we are not building as a part of LLVM, build LLD as an
+# standalone project, using LLVM as an external library:
 if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
   project(lld)
-  cmake_minimum_required(VERSION 3.13.4)
-
-  set(CMAKE_INCLUDE_CURRENT_DIR ON)
   set(LLD_BUILT_STANDALONE TRUE)
-
-  find_program(LLVM_CONFIG_PATH "llvm-config" DOC "Path to llvm-config binary")
-  if(NOT LLVM_CONFIG_PATH)
-    message(FATAL_ERROR "llvm-config not found: specify LLVM_CONFIG_PATH")
-  endif()
-
-  execute_process(COMMAND "${LLVM_CONFIG_PATH}"
-                          "--obj-root"
-                          "--includedir"
-                          "--cmakedir"
-                          "--src-root"
-                  RESULT_VARIABLE HAD_ERROR
-                  OUTPUT_VARIABLE LLVM_CONFIG_OUTPUT
-                  OUTPUT_STRIP_TRAILING_WHITESPACE)
-  if(HAD_ERROR)
-    message(FATAL_ERROR "llvm-config failed with status ${HAD_ERROR}")
+  if ("${CMAKE_VERSION}" VERSION_LESS "3.20.0")
+    message(WARNING
+      "Your CMake version is ${CMAKE_VERSION}. Starting with LLVM 17.0.0, the "
+      "minimum version of CMake required to build LLVM will become 3.20.0, and "
+      "using an older CMake will become an error. Please upgrade your CMake to "
+      "at least 3.20.0 now to avoid issues in the future!")
   endif()
+endif()
 
-  string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" LLVM_CONFIG_OUTPUT "${LLVM_CONFIG_OUTPUT}")
+# Must go below project(..)
+include(GNUInstallDirs)
 
-  list(GET LLVM_CONFIG_OUTPUT 0 OBJ_ROOT)
-  list(GET LLVM_CONFIG_OUTPUT 1 MAIN_INCLUDE_DIR)
-  list(GET LLVM_CONFIG_OUTPUT 2 LLVM_CMAKE_PATH)
-  list(GET LLVM_CONFIG_OUTPUT 3 MAIN_SRC_DIR)
+if(LLD_BUILT_STANDALONE)
+  set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard to conform to")
+  set(CMAKE_CXX_STANDARD_REQUIRED YES)
+  set(CMAKE_CXX_EXTENSIONS NO)
 
-  set(LLVM_OBJ_ROOT ${OBJ_ROOT} CACHE PATH "path to LLVM build tree")
-  set(LLVM_MAIN_INCLUDE_DIR ${MAIN_INCLUDE_DIR} CACHE PATH "path to llvm/include")
-  set(LLVM_MAIN_SRC_DIR ${MAIN_SRC_DIR} CACHE PATH "Path to LLVM source tree")
+  set(CMAKE_INCLUDE_CURRENT_DIR ON)
 
-  file(TO_CMAKE_PATH ${LLVM_OBJ_ROOT} LLVM_BINARY_DIR)
-  file(TO_CMAKE_PATH ${LLVM_CMAKE_PATH} LLVM_CMAKE_PATH)
+  find_package(LLVM REQUIRED HINTS "${LLVM_CMAKE_DIR}")
+  list(APPEND CMAKE_MODULE_PATH "${LLVM_DIR}")
 
-  if(NOT EXISTS "${LLVM_CMAKE_PATH}/LLVMConfig.cmake")
-    message(FATAL_ERROR "LLVMConfig.cmake not found")
-  endif()
-  include("${LLVM_CMAKE_PATH}/LLVMConfig.cmake")
+  # Turn into CACHE PATHs for overwriting
+  set(LLVM_INCLUDE_DIRS ${LLVM_INCLUDE_DIRS} CACHE PATH "Path to llvm/include and any other header dirs needed")
+  set(LLVM_BINARY_DIR "${LLVM_BINARY_DIR}" CACHE PATH "Path to LLVM build tree")
+  set(LLVM_MAIN_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../llvm" CACHE PATH "Path to LLVM source tree")
 
-  list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_PATH}")
+  find_program(LLVM_TABLEGEN_EXE "llvm-tblgen" ${LLVM_TOOLS_BINARY_DIR}
+    NO_DEFAULT_PATH)
 
-  set(PACKAGE_VERSION "${LLVM_PACKAGE_VERSION}")
-  include_directories("${LLVM_BINARY_DIR}/include" ${LLVM_INCLUDE_DIRS})
-  link_directories(${LLVM_LIBRARY_DIRS})
-
-  set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX})
+  # They are used as destination of target generators.
   set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin)
-  find_program(LLVM_TABLEGEN_EXE "llvm-tblgen" ${LLVM_TOOLS_BINARY_DIR} NO_DEFAULT_PATH)
+  set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX})
 
   include(AddLLVM)
   include(TableGen)
@@ -58,6 +51,11 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
   include(GetErrcMessages)
   include(CheckAtomic)
 
+  set(PACKAGE_VERSION "${LLVM_PACKAGE_VERSION}")
+
+  include_directories(${LLVM_INCLUDE_DIRS})
+  link_directories(${LLVM_LIBRARY_DIRS})
+
   if(LLVM_INCLUDE_TESTS)
     find_package(Python3 ${LLVM_MINIMUM_PYTHON_VERSION} REQUIRED
       COMPONENTS Interpreter)
@@ -77,11 +75,11 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
         set(LLVM_UTILS_PROVIDED ON)
         set(LLD_TEST_DEPS FileCheck not)
       endif()
-      set(UNITTEST_DIR ${LLVM_MAIN_SRC_DIR}/utils/unittest)
+      set(UNITTEST_DIR ${LLVM_THIRD_PARTY_DIR}/unittest)
       if(EXISTS ${UNITTEST_DIR}/googletest/include/gtest/gtest.h
           AND NOT EXISTS ${LLVM_LIBRARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}
           AND EXISTS ${UNITTEST_DIR}/CMakeLists.txt)
-        add_subdirectory(${UNITTEST_DIR} utils/unittest)
+        add_subdirectory(${UNITTEST_DIR} third-party/unittest)
       endif()
     else()
       # Seek installed Lit.
@@ -114,7 +112,11 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
   if(LLVM_HAVE_LIBXAR)
     set(XAR_LIB xar)
   endif()
-endif()
+endif() # standalone
+
+set(LLD_TOOLS_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}" CACHE PATH
+    "Path for binary subdirectory (defaults to '${CMAKE_INSTALL_BINDIR}')")
+mark_as_advanced(LLD_TOOLS_INSTALL_DIR)
 
 set(LLD_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
 set(LLD_INCLUDE_DIR ${LLD_SOURCE_DIR}/include )
@@ -151,7 +153,11 @@ if (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
 "`CMakeFiles'. Please delete them.")
 endif()
 
-list (APPEND CMAKE_MODULE_PATH "${LLD_SOURCE_DIR}/cmake/modules")
+# Add path for custom modules.
+list(INSERT CMAKE_MODULE_PATH 0
+  "${LLD_SOURCE_DIR}/cmake/modules"
+  "${LLVM_COMMON_CMAKE_UTILS}/Modules"
+  )
 
 include(AddLLD)
 
@@ -188,19 +194,17 @@ include_directories(BEFORE
 
 if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
   install(DIRECTORY include/
-    DESTINATION include
+    DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
     FILES_MATCHING
     PATTERN "*.h"
     )
 endif()
 
 add_subdirectory(Common)
-add_subdirectory(lib)
 add_subdirectory(tools/lld)
 
 if (LLVM_INCLUDE_TESTS)
   add_subdirectory(test)
-  add_subdirectory(unittests)
 endif()
 
 add_subdirectory(docs)
index bbcd337..acbd2e5 100644 (file)
@@ -5,6 +5,7 @@ add_public_tablegen_target(COFFOptionsTableGen)
 add_lld_library(lldCOFF
   CallGraphSort.cpp
   Chunks.cpp
+  COFFLinkerContext.cpp
   DebugTypes.cpp
   DLL.cpp
   Driver.cpp
@@ -37,11 +38,14 @@ add_lld_library(lldCOFF
   Option
   Passes
   Support
+  TargetParser
+  WindowsDriver
   WindowsManifest
 
   LINK_LIBS
   lldCommon
   ${LLVM_PTHREAD_LIB}
+  ${LLVM_ATOMIC_LIB}
 
   DEPENDS
   COFFOptionsTableGen
diff --git a/gnu/llvm/lld/COFF/COFFLinkerContext.cpp b/gnu/llvm/lld/COFF/COFFLinkerContext.cpp
new file mode 100644 (file)
index 0000000..2aba87d
--- /dev/null
@@ -0,0 +1,44 @@
+//===- COFFContext.cpp ----------------------------------------------------===//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Description
+//
+//===----------------------------------------------------------------------===//
+
+#include "COFFLinkerContext.h"
+#include "Symbols.h"
+#include "lld/Common/Memory.h"
+#include "llvm/BinaryFormat/COFF.h"
+#include "llvm/DebugInfo/CodeView/TypeHashing.h"
+#include "llvm/Demangle/Demangle.h"
+
+namespace lld::coff {
+COFFLinkerContext::COFFLinkerContext()
+    : driver(*this), symtab(*this),
+      ltoTextSection(llvm::COFF::IMAGE_SCN_MEM_EXECUTE),
+      ltoDataSection(llvm::COFF::IMAGE_SCN_CNT_INITIALIZED_DATA),
+      ltoTextSectionChunk(&ltoTextSection.section),
+      ltoDataSectionChunk(&ltoDataSection.section),
+      rootTimer("Total Linking Time"),
+      inputFileTimer("Input File Reading", rootTimer),
+      ltoTimer("LTO", rootTimer), gcTimer("GC", rootTimer),
+      icfTimer("ICF", rootTimer), codeLayoutTimer("Code Layout", rootTimer),
+      outputCommitTimer("Commit Output File", rootTimer),
+      totalMapTimer("MAP Emission (Cumulative)", rootTimer),
+      symbolGatherTimer("Gather Symbols", totalMapTimer),
+      symbolStringsTimer("Build Symbol Strings", totalMapTimer),
+      writeTimer("Write to File", totalMapTimer),
+      totalPdbLinkTimer("PDB Emission (Cumulative)", rootTimer),
+      addObjectsTimer("Add Objects", totalPdbLinkTimer),
+      typeMergingTimer("Type Merging", addObjectsTimer),
+      loadGHashTimer("Global Type Hashing", addObjectsTimer),
+      mergeGHashTimer("GHash Type Merging", addObjectsTimer),
+      symbolMergingTimer("Symbol Merging", addObjectsTimer),
+      publicsLayoutTimer("Publics Stream Layout", totalPdbLinkTimer),
+      tpiStreamLayoutTimer("TPI Stream Layout", totalPdbLinkTimer),
+      diskCommitTimer("Commit to Disk", totalPdbLinkTimer) {}
+} // namespace lld::coff
diff --git a/gnu/llvm/lld/COFF/COFFLinkerContext.h b/gnu/llvm/lld/COFF/COFFLinkerContext.h
new file mode 100644 (file)
index 0000000..059d4ae
--- /dev/null
@@ -0,0 +1,95 @@
+//===- COFFLinkerContext.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_COFF_COFFLINKERCONTEXT_H
+#define LLD_COFF_COFFLINKERCONTEXT_H
+
+#include "Chunks.h"
+#include "Config.h"
+#include "DebugTypes.h"
+#include "Driver.h"
+#include "InputFiles.h"
+#include "SymbolTable.h"
+#include "Writer.h"
+#include "lld/Common/CommonLinkerContext.h"
+#include "lld/Common/Timer.h"
+
+namespace lld::coff {
+
+class COFFLinkerContext : public CommonLinkerContext {
+public:
+  COFFLinkerContext();
+  COFFLinkerContext(const COFFLinkerContext &) = delete;
+  COFFLinkerContext &operator=(const COFFLinkerContext &) = delete;
+  ~COFFLinkerContext() = default;
+
+  LinkerDriver driver;
+  SymbolTable symtab;
+  COFFOptTable optTable;
+
+  std::vector<ObjFile *> objFileInstances;
+  std::map<std::string, PDBInputFile *> pdbInputFileInstances;
+  std::vector<ImportFile *> importFileInstances;
+  std::vector<BitcodeFile *> bitcodeFileInstances;
+
+  MergeChunk *mergeChunkInstances[Log2MaxSectionAlignment + 1] = {};
+
+  /// All sources of type information in the program.
+  std::vector<TpiSource *> tpiSourceList;
+
+  void addTpiSource(TpiSource *tpi) { tpiSourceList.push_back(tpi); }
+
+  std::map<llvm::codeview::GUID, TpiSource *> typeServerSourceMappings;
+  std::map<uint32_t, TpiSource *> precompSourceMappings;
+
+  /// List of all output sections. After output sections are finalized, this
+  /// can be indexed by getOutputSection.
+  std::vector<OutputSection *> outputSections;
+
+  OutputSection *getOutputSection(const Chunk *c) const {
+    return c->osidx == 0 ? nullptr : outputSections[c->osidx - 1];
+  }
+
+  // Fake sections for parsing bitcode files.
+  FakeSection ltoTextSection;
+  FakeSection ltoDataSection;
+  FakeSectionChunk ltoTextSectionChunk;
+  FakeSectionChunk ltoDataSectionChunk;
+
+  // All timers used in the COFF linker.
+  Timer rootTimer;
+  Timer inputFileTimer;
+  Timer ltoTimer;
+  Timer gcTimer;
+  Timer icfTimer;
+
+  // Writer timers.
+  Timer codeLayoutTimer;
+  Timer outputCommitTimer;
+  Timer totalMapTimer;
+  Timer symbolGatherTimer;
+  Timer symbolStringsTimer;
+  Timer writeTimer;
+
+  // PDB timers.
+  Timer totalPdbLinkTimer;
+  Timer addObjectsTimer;
+  Timer typeMergingTimer;
+  Timer loadGHashTimer;
+  Timer mergeGHashTimer;
+  Timer symbolMergingTimer;
+  Timer publicsLayoutTimer;
+  Timer tpiStreamLayoutTimer;
+  Timer diskCommitTimer;
+
+  Configuration config;
+};
+
+} // namespace lld::coff
+
+#endif
index d3e5312..e83996b 100644 (file)
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "CallGraphSort.h"
+#include "COFFLinkerContext.h"
 #include "InputFiles.h"
 #include "SymbolTable.h"
 #include "Symbols.h"
@@ -48,13 +49,15 @@ struct Cluster {
 
 class CallGraphSort {
 public:
-  CallGraphSort();
+  CallGraphSort(const COFFLinkerContext &ctx);
 
   DenseMap<const SectionChunk *, int> run();
 
 private:
   std::vector<Cluster> clusters;
   std::vector<const SectionChunk *> sections;
+
+  const COFFLinkerContext &ctx;
 };
 
 // Maximum amount the combined cluster density can be worse than the original
@@ -70,8 +73,8 @@ using SectionPair = std::pair<const SectionChunk *, const SectionChunk *>;
 // Take the edge list in Config->CallGraphProfile, resolve symbol names to
 // Symbols, and generate a graph between InputSections with the provided
 // weights.
-CallGraphSort::CallGraphSort() {
-  MapVector<SectionPair, uint64_t> &profile = config->callGraphProfile;
+CallGraphSort::CallGraphSort(const COFFLinkerContext &ctx) : ctx(ctx) {
+  const MapVector<SectionPair, uint64_t> &profile = ctx.config.callGraphProfile;
   DenseMap<const SectionChunk *, int> secToCluster;
 
   auto getOrCreateNode = [&](const SectionChunk *isec) -> int {
@@ -84,7 +87,7 @@ CallGraphSort::CallGraphSort() {
   };
 
   // Create the graph.
-  for (std::pair<SectionPair, uint64_t> &c : profile) {
+  for (const std::pair<SectionPair, uint64_t> &c : profile) {
     const auto *fromSec = cast<SectionChunk>(c.first.first->repl);
     const auto *toSec = cast<SectionChunk>(c.first.second->repl);
     uint64_t weight = c.second;
@@ -95,7 +98,7 @@ CallGraphSort::CallGraphSort() {
     // output.  This messes with the cluster size and density calculations.  We
     // would also end up moving input sections in other output sections without
     // moving them closer to what calls them.
-    if (fromSec->getOutputSection() != toSec->getOutputSection())
+    if (ctx.getOutputSection(fromSec) != ctx.getOutputSection(toSec))
       continue;
 
     int from = getOrCreateNode(fromSec);
@@ -204,11 +207,11 @@ DenseMap<const SectionChunk *, int> CallGraphSort::run() {
         break;
     }
   }
-  if (!config->printSymbolOrder.empty()) {
+  if (!ctx.config.printSymbolOrder.empty()) {
     std::error_code ec;
-    raw_fd_ostream os(config->printSymbolOrder, ec, sys::fs::OF_None);
+    raw_fd_ostream os(ctx.config.printSymbolOrder, ec, sys::fs::OF_None);
     if (ec) {
-      error("cannot open " + config->printSymbolOrder + ": " + ec.message());
+      error("cannot open " + ctx.config.printSymbolOrder + ": " + ec.message());
       return orderMap;
     }
     // Print the symbols ordered by C3, in the order of increasing curOrder
@@ -240,6 +243,7 @@ DenseMap<const SectionChunk *, int> CallGraphSort::run() {
 // This first builds a call graph based on the profile data then merges sections
 // according to the C³ heuristic. All clusters are then sorted by a density
 // metric to further improve locality.
-DenseMap<const SectionChunk *, int> coff::computeCallGraphProfileOrder() {
-  return CallGraphSort().run();
+DenseMap<const SectionChunk *, int>
+coff::computeCallGraphProfileOrder(const COFFLinkerContext &ctx) {
+  return CallGraphSort(ctx).run();
 }
index e4f3721..60f2941 100644 (file)
 
 #include "llvm/ADT/DenseMap.h"
 
-namespace lld {
-namespace coff {
+namespace lld::coff {
 class SectionChunk;
+class COFFLinkerContext;
 
-llvm::DenseMap<const SectionChunk *, int> computeCallGraphProfileOrder();
-} // namespace coff
-} // namespace lld
+llvm::DenseMap<const SectionChunk *, int>
+computeCallGraphProfileOrder(const COFFLinkerContext &ctx);
+} // namespace lld::coff
 
 #endif
index 36d5f37..65de868 100644 (file)
@@ -7,11 +7,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "Chunks.h"
+#include "COFFLinkerContext.h"
 #include "InputFiles.h"
+#include "SymbolTable.h"
 #include "Symbols.h"
 #include "Writer.h"
-#include "SymbolTable.h"
-#include "lld/Common/ErrorHandler.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/BinaryFormat/COFF.h"
 #include "llvm/Object/COFF.h"
@@ -19,6 +20,7 @@
 #include "llvm/Support/Endian.h"
 #include "llvm/Support/raw_ostream.h"
 #include <algorithm>
+#include <iterator>
 
 using namespace llvm;
 using namespace llvm::object;
@@ -26,8 +28,7 @@ using namespace llvm::support::endian;
 using namespace llvm::COFF;
 using llvm::support::ulittle32_t;
 
-namespace lld {
-namespace coff {
+namespace lld::coff {
 
 SectionChunk::SectionChunk(ObjFile *f, const coff_section *h)
     : Chunk(SectionKind), file(f), header(h), repl(this) {
@@ -52,8 +53,8 @@ SectionChunk::SectionChunk(ObjFile *f, const coff_section *h)
   // enabled, treat non-comdat sections as roots. Generally optimized object
   // files will be built with -ffunction-sections or /Gy, so most things worth
   // stripping will be in a comdat.
-  if (config)
-    live = !config->doGC || !isCOMDAT();
+  if (file)
+    live = !file->ctx.config.doGC || !isCOMDAT();
   else
     live = true;
 }
@@ -93,21 +94,31 @@ static void applySecRel(const SectionChunk *sec, uint8_t *off,
   add32(off, secRel);
 }
 
-static void applySecIdx(uint8_t *off, OutputSection *os) {
+static void applySecIdx(uint8_t *off, OutputSection *os,
+                        unsigned numOutputSections) {
+  // numOutputSections is the largest valid section index. Make sure that
+  // it fits in 16 bits.
+  assert(numOutputSections <= 0xffff && "size of outputSections is too big");
+
   // Absolute symbol doesn't have section index, but section index relocation
   // against absolute symbol should be resolved to one plus the last output
   // section index. This is required for compatibility with MSVC.
   if (os)
     add16(off, os->sectionIndex);
   else
-    add16(off, DefinedAbsolute::numOutputSections + 1);
+    add16(off, numOutputSections + 1);
 }
 
 void SectionChunk::applyRelX64(uint8_t *off, uint16_t type, OutputSection *os,
-                               uint64_t s, uint64_t p) const {
+                               uint64_t s, uint64_t p,
+                               uint64_t imageBase) const {
   switch (type) {
-  case IMAGE_REL_AMD64_ADDR32:   add32(off, s + config->imageBase); break;
-  case IMAGE_REL_AMD64_ADDR64:   add64(off, s + config->imageBase); break;
+  case IMAGE_REL_AMD64_ADDR32:
+    add32(off, s + imageBase);
+    break;
+  case IMAGE_REL_AMD64_ADDR64:
+    add64(off, s + imageBase);
+    break;
   case IMAGE_REL_AMD64_ADDR32NB: add32(off, s); break;
   case IMAGE_REL_AMD64_REL32:    add32(off, s - p - 4); break;
   case IMAGE_REL_AMD64_REL32_1:  add32(off, s - p - 5); break;
@@ -115,7 +126,9 @@ void SectionChunk::applyRelX64(uint8_t *off, uint16_t type, OutputSection *os,
   case IMAGE_REL_AMD64_REL32_3:  add32(off, s - p - 7); break;
   case IMAGE_REL_AMD64_REL32_4:  add32(off, s - p - 8); break;
   case IMAGE_REL_AMD64_REL32_5:  add32(off, s - p - 9); break;
-  case IMAGE_REL_AMD64_SECTION:  applySecIdx(off, os); break;
+  case IMAGE_REL_AMD64_SECTION:
+    applySecIdx(off, os, file->ctx.outputSections.size());
+    break;
   case IMAGE_REL_AMD64_SECREL:   applySecRel(this, off, os, s); break;
   default:
     error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " +
@@ -124,13 +137,18 @@ void SectionChunk::applyRelX64(uint8_t *off, uint16_t type, OutputSection *os,
 }
 
 void SectionChunk::applyRelX86(uint8_t *off, uint16_t type, OutputSection *os,
-                               uint64_t s, uint64_t p) const {
+                               uint64_t s, uint64_t p,
+                               uint64_t imageBase) const {
   switch (type) {
   case IMAGE_REL_I386_ABSOLUTE: break;
-  case IMAGE_REL_I386_DIR32:    add32(off, s + config->imageBase); break;
+  case IMAGE_REL_I386_DIR32:
+    add32(off, s + imageBase);
+    break;
   case IMAGE_REL_I386_DIR32NB:  add32(off, s); break;
   case IMAGE_REL_I386_REL32:    add32(off, s - p - 4); break;
-  case IMAGE_REL_I386_SECTION:  applySecIdx(off, os); break;
+  case IMAGE_REL_I386_SECTION:
+    applySecIdx(off, os, file->ctx.outputSections.size());
+    break;
   case IMAGE_REL_I386_SECREL:   applySecRel(this, off, os, s); break;
   default:
     error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " +
@@ -187,19 +205,26 @@ void applyBranch24T(uint8_t *off, int32_t v) {
 }
 
 void SectionChunk::applyRelARM(uint8_t *off, uint16_t type, OutputSection *os,
-                               uint64_t s, uint64_t p) const {
+                               uint64_t s, uint64_t p,
+                               uint64_t imageBase) const {
   // Pointer to thumb code must have the LSB set.
   uint64_t sx = s;
   if (os && (os->header.Characteristics & IMAGE_SCN_MEM_EXECUTE))
     sx |= 1;
   switch (type) {
-  case IMAGE_REL_ARM_ADDR32:    add32(off, sx + config->imageBase); break;
+  case IMAGE_REL_ARM_ADDR32:
+    add32(off, sx + imageBase);
+    break;
   case IMAGE_REL_ARM_ADDR32NB:  add32(off, sx); break;
-  case IMAGE_REL_ARM_MOV32T:    applyMOV32T(off, sx + config->imageBase); break;
+  case IMAGE_REL_ARM_MOV32T:
+    applyMOV32T(off, sx + imageBase);
+    break;
   case IMAGE_REL_ARM_BRANCH20T: applyBranch20T(off, sx - p - 4); break;
   case IMAGE_REL_ARM_BRANCH24T: applyBranch24T(off, sx - p - 4); break;
   case IMAGE_REL_ARM_BLX23T:    applyBranch24T(off, sx - p - 4); break;
-  case IMAGE_REL_ARM_SECTION:   applySecIdx(off, os); break;
+  case IMAGE_REL_ARM_SECTION:
+    applySecIdx(off, os, file->ctx.outputSections.size());
+    break;
   case IMAGE_REL_ARM_SECREL:    applySecRel(this, off, os, s); break;
   case IMAGE_REL_ARM_REL32:     add32(off, sx - p - 4); break;
   default:
@@ -213,7 +238,8 @@ void SectionChunk::applyRelARM(uint8_t *off, uint16_t type, OutputSection *os,
 // the page offset from the current instruction to the target.
 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);
+  int64_t imm =
+      SignExtend64<21>(((orig >> 29) & 0x3) | ((orig >> 3) & 0x1FFFFC));
   s += imm;
   imm = (s >> shift) - (p >> shift);
   uint32_t immLo = (imm & 0x3) << 29;
@@ -296,7 +322,8 @@ static void applyArm64Branch14(uint8_t *off, int64_t v) {
 }
 
 void SectionChunk::applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os,
-                                 uint64_t s, uint64_t p) const {
+                                 uint64_t s, uint64_t p,
+                                 uint64_t imageBase) const {
   switch (type) {
   case IMAGE_REL_ARM64_PAGEBASE_REL21: applyArm64Addr(off, s, p, 12); break;
   case IMAGE_REL_ARM64_REL21:          applyArm64Addr(off, s, p, 0); break;
@@ -305,14 +332,20 @@ void SectionChunk::applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os,
   case IMAGE_REL_ARM64_BRANCH26:       applyArm64Branch26(off, s - p); break;
   case IMAGE_REL_ARM64_BRANCH19:       applyArm64Branch19(off, s - p); break;
   case IMAGE_REL_ARM64_BRANCH14:       applyArm64Branch14(off, s - p); break;
-  case IMAGE_REL_ARM64_ADDR32:         add32(off, s + config->imageBase); break;
+  case IMAGE_REL_ARM64_ADDR32:
+    add32(off, s + imageBase);
+    break;
   case IMAGE_REL_ARM64_ADDR32NB:       add32(off, s); break;
-  case IMAGE_REL_ARM64_ADDR64:         add64(off, s + config->imageBase); break;
+  case IMAGE_REL_ARM64_ADDR64:
+    add64(off, s + imageBase);
+    break;
   case IMAGE_REL_ARM64_SECREL:         applySecRel(this, off, os, s); break;
   case IMAGE_REL_ARM64_SECREL_LOW12A:  applySecRelLow12A(this, off, os, s); break;
   case IMAGE_REL_ARM64_SECREL_HIGH12A: applySecRelHigh12A(this, off, os, s); break;
   case IMAGE_REL_ARM64_SECREL_LOW12L:  applySecRelLdr(this, off, os, s); break;
-  case IMAGE_REL_ARM64_SECTION:        applySecIdx(off, os); break;
+  case IMAGE_REL_ARM64_SECTION:
+    applySecIdx(off, os, file->ctx.outputSections.size());
+    break;
   case IMAGE_REL_ARM64_REL32:          add32(off, s - p - 4); break;
   default:
     error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " +
@@ -322,12 +355,13 @@ void SectionChunk::applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os,
 
 static void maybeReportRelocationToDiscarded(const SectionChunk *fromChunk,
                                              Defined *sym,
-                                             const coff_relocation &rel) {
+                                             const coff_relocation &rel,
+                                             bool isMinGW) {
   // 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)
+  if (fromChunk->isCodeView() || fromChunk->isDWARF() || isMinGW)
     return;
 
   // Get the name of the symbol. If it's null, it was discarded early, so we
@@ -385,7 +419,7 @@ void SectionChunk::applyRelocation(uint8_t *off,
   // section is needed to compute SECREL and SECTION relocations used in debug
   // info.
   Chunk *c = sym ? sym->getChunk() : nullptr;
-  OutputSection *os = c ? c->getOutputSection() : nullptr;
+  OutputSection *os = c ? file->ctx.getOutputSection(c) : nullptr;
 
   // 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
@@ -393,7 +427,7 @@ void SectionChunk::applyRelocation(uint8_t *off,
   // it was an absolute or synthetic symbol.
   if (!sym ||
       (!os && !isa<DefinedAbsolute>(sym) && !isa<DefinedSynthetic>(sym))) {
-    maybeReportRelocationToDiscarded(this, sym, rel);
+    maybeReportRelocationToDiscarded(this, sym, rel, file->ctx.config.mingw);
     return;
   }
 
@@ -401,18 +435,19 @@ void SectionChunk::applyRelocation(uint8_t *off,
 
   // Compute the RVA of the relocation for relative relocations.
   uint64_t p = rva + rel.VirtualAddress;
-  switch (config->machine) {
+  uint64_t imageBase = file->ctx.config.imageBase;
+  switch (file->ctx.config.machine) {
   case AMD64:
-    applyRelX64(off, rel.Type, os, s, p);
+    applyRelX64(off, rel.Type, os, s, p, imageBase);
     break;
   case I386:
-    applyRelX86(off, rel.Type, os, s, p);
+    applyRelX86(off, rel.Type, os, s, p, imageBase);
     break;
   case ARMNT:
-    applyRelARM(off, rel.Type, os, s, p);
+    applyRelARM(off, rel.Type, os, s, p, imageBase);
     break;
   case ARM64:
-    applyRelARM64(off, rel.Type, os, s, p);
+    applyRelARM64(off, rel.Type, os, s, p, imageBase);
     break;
   default:
     llvm_unreachable("unknown machine type");
@@ -428,7 +463,7 @@ void SectionChunk::sortRelocations() {
     return;
   warn("some relocations in " + file->getName() + " are not sorted");
   MutableArrayRef<coff_relocation> newRelocs(
-      bAlloc.Allocate<coff_relocation>(relocsSize), relocsSize);
+      bAlloc().Allocate<coff_relocation>(relocsSize), relocsSize);
   memcpy(newRelocs.data(), relocsData, relocsSize * sizeof(coff_relocation));
   llvm::sort(newRelocs, cmpByVa);
   setRelocs(newRelocs);
@@ -477,8 +512,9 @@ void SectionChunk::addAssociative(SectionChunk *child) {
   child->assocChildren = next;
 }
 
-static uint8_t getBaserelType(const coff_relocation &rel) {
-  switch (config->machine) {
+static uint8_t getBaserelType(const coff_relocation &rel,
+                              llvm::COFF::MachineTypes machine) {
+  switch (machine) {
   case AMD64:
     if (rel.Type == IMAGE_REL_AMD64_ADDR64)
       return IMAGE_REL_BASED_DIR64;
@@ -510,7 +546,7 @@ static uint8_t getBaserelType(const coff_relocation &rel) {
 // Only called when base relocation is enabled.
 void SectionChunk::getBaserels(std::vector<Baserel> *res) {
   for (const coff_relocation &rel : getRelocs()) {
-    uint8_t ty = getBaserelType(rel);
+    uint8_t ty = getBaserelType(rel, file->ctx.config.machine);
     if (ty == IMAGE_REL_BASED_ABSOLUTE)
       continue;
     Symbol *target = file->getSymbol(rel.SymbolTableIndex);
@@ -527,7 +563,8 @@ void SectionChunk::getBaserels(std::vector<Baserel> *res) {
 // 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) {
+static int getRuntimePseudoRelocSize(uint16_t type,
+                                     llvm::COFF::MachineTypes machine) {
   // 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.
@@ -553,7 +590,7 @@ static int getRuntimePseudoRelocSize(uint16_t type) {
   // 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) {
+  switch (machine) {
   case AMD64:
     switch (type) {
     case IMAGE_REL_AMD64_ADDR64:
@@ -610,7 +647,8 @@ void SectionChunk::getRuntimePseudoRelocs(
         dyn_cast_or_null<Defined>(file->getSymbol(rel.SymbolTableIndex));
     if (!target || !target->isRuntimePseudoReloc)
       continue;
-    int sizeInBits = getRuntimePseudoRelocSize(rel.Type);
+    int sizeInBits =
+        getRuntimePseudoRelocSize(rel.Type, file->ctx.config.machine);
     if (sizeInBits == 0) {
       error("unable to automatically import from " + target->getName() +
             " with relocation type " +
@@ -633,7 +671,7 @@ void SectionChunk::printDiscardedMessage() const {
   // Removed by dead-stripping. If it's removed by ICF, ICF already
   // printed out the name, so don't repeat that here.
   if (sym && this == repl)
-    message("Discarded " + sym->getName());
+    log("Discarded " + sym->getName());
 }
 
 StringRef SectionChunk::getDebugName() const {
@@ -716,7 +754,8 @@ void StringChunk::writeTo(uint8_t *buf) const {
   buf[str.size()] = '\0';
 }
 
-ImportThunkChunkX64::ImportThunkChunkX64(Defined *s) : ImportThunkChunk(s) {
+ImportThunkChunkX64::ImportThunkChunkX64(COFFLinkerContext &ctx, Defined *s)
+    : ImportThunkChunk(ctx, s) {
   // Intel Optimization Manual says that all branch targets
   // should be 16-byte aligned. MSVC linker does this too.
   setAlignment(16);
@@ -729,14 +768,13 @@ void ImportThunkChunkX64::writeTo(uint8_t *buf) const {
 }
 
 void ImportThunkChunkX86::getBaserels(std::vector<Baserel> *res) {
-  res->emplace_back(getRVA() + 2);
+  res->emplace_back(getRVA() + 2, ctx.config.machine);
 }
 
 void ImportThunkChunkX86::writeTo(uint8_t *buf) const {
   memcpy(buf, importThunkX86, sizeof(importThunkX86));
   // The first two bytes is a JMP instruction. Fill its operand.
-  write32le(buf + 2,
-            impSymbol->getRVA() + config->imageBase);
+  write32le(buf + 2, impSymbol->getRVA() + ctx.config.imageBase);
 }
 
 void ImportThunkChunkARM::getBaserels(std::vector<Baserel> *res) {
@@ -746,7 +784,7 @@ void ImportThunkChunkARM::getBaserels(std::vector<Baserel> *res) {
 void ImportThunkChunkARM::writeTo(uint8_t *buf) const {
   memcpy(buf, importThunkARM, sizeof(importThunkARM));
   // Fix mov.w and mov.t operands.
-  applyMOV32T(buf, impSymbol->getRVA() + config->imageBase);
+  applyMOV32T(buf, impSymbol->getRVA() + ctx.config.imageBase);
 }
 
 void ImportThunkChunkARM64::writeTo(uint8_t *buf) const {
@@ -764,12 +802,13 @@ const uint8_t armThunk[] = {
 };
 
 size_t RangeExtensionThunkARM::getSize() const {
-  assert(config->machine == ARMNT);
+  assert(ctx.config.machine == ARMNT);
+  (void)&ctx;
   return sizeof(armThunk);
 }
 
 void RangeExtensionThunkARM::writeTo(uint8_t *buf) const {
-  assert(config->machine == ARMNT);
+  assert(ctx.config.machine == ARMNT);
   uint64_t offset = target->getRVA() - rva - 12;
   memcpy(buf, armThunk, sizeof(armThunk));
   applyMOV32T(buf, uint32_t(offset));
@@ -784,28 +823,34 @@ const uint8_t arm64Thunk[] = {
 };
 
 size_t RangeExtensionThunkARM64::getSize() const {
-  assert(config->machine == ARM64);
+  assert(ctx.config.machine == ARM64);
+  (void)&ctx;
   return sizeof(arm64Thunk);
 }
 
 void RangeExtensionThunkARM64::writeTo(uint8_t *buf) const {
-  assert(config->machine == ARM64);
+  assert(ctx.config.machine == ARM64);
   memcpy(buf, arm64Thunk, sizeof(arm64Thunk));
   applyArm64Addr(buf + 0, target->getRVA(), rva, 12);
   applyArm64Imm(buf + 4, target->getRVA() & 0xfff, 0);
 }
 
+LocalImportChunk::LocalImportChunk(COFFLinkerContext &c, Defined *s)
+    : sym(s), ctx(c) {
+  setAlignment(ctx.config.wordsize);
+}
+
 void LocalImportChunk::getBaserels(std::vector<Baserel> *res) {
-  res->emplace_back(getRVA());
+  res->emplace_back(getRVA(), ctx.config.machine);
 }
 
-size_t LocalImportChunk::getSize() const { return config->wordsize; }
+size_t LocalImportChunk::getSize() const { return ctx.config.wordsize; }
 
 void LocalImportChunk::writeTo(uint8_t *buf) const {
-  if (config->is64()) {
-    write64le(buf, sym->getRVA() + config->imageBase);
+  if (ctx.config.is64()) {
+    write64le(buf, sym->getRVA() + ctx.config.imageBase);
   } else {
-    write32le(buf, sym->getRVA() + config->imageBase);
+    write32le(buf, sym->getRVA() + ctx.config.imageBase);
   }
 }
 
@@ -814,7 +859,7 @@ void RVATableChunk::writeTo(uint8_t *buf) const {
   size_t cnt = 0;
   for (const ChunkAndOffset &co : syms)
     begin[cnt++] = co.inputChunk->getRVA() + co.offset;
-  std::sort(begin, begin + cnt);
+  llvm::sort(begin, begin + cnt);
   assert(std::unique(begin, begin + cnt) == begin + cnt &&
          "RVA tables should be de-duplicated");
 }
@@ -825,7 +870,7 @@ void RVAFlagTableChunk::writeTo(uint8_t *buf) const {
     uint8_t flag;
   };
   auto flags =
-      makeMutableArrayRef(reinterpret_cast<RVAFlag *>(buf), syms.size());
+      MutableArrayRef(reinterpret_cast<RVAFlag *>(buf), syms.size());
   for (auto t : zip(syms, flags)) {
     const auto &sym = std::get<0>(t);
     auto &flag = std::get<1>(t);
@@ -925,8 +970,8 @@ void BaserelChunk::writeTo(uint8_t *buf) const {
   memcpy(buf, data.data(), data.size());
 }
 
-uint8_t Baserel::getDefaultType() {
-  switch (config->machine) {
+uint8_t Baserel::getDefaultType(llvm::COFF::MachineTypes machine) {
+  switch (machine) {
   case AMD64:
   case ARM64:
     return IMAGE_REL_BASED_DIR64;
@@ -938,18 +983,16 @@ uint8_t Baserel::getDefaultType() {
   }
 }
 
-MergeChunk *MergeChunk::instances[Log2MaxSectionAlignment + 1] = {};
-
 MergeChunk::MergeChunk(uint32_t alignment)
-    : builder(StringTableBuilder::RAW, alignment) {
+    : builder(StringTableBuilder::RAW, llvm::Align(alignment)) {
   setAlignment(alignment);
 }
 
-void MergeChunk::addSection(SectionChunk *c) {
+void MergeChunk::addSection(COFFLinkerContext &ctx, SectionChunk *c) {
   assert(isPowerOf2_32(c->getAlignment()));
   uint8_t p2Align = llvm::Log2_32(c->getAlignment());
-  assert(p2Align < array_lengthof(instances));
-  auto *&mc = instances[p2Align];
+  assert(p2Align < std::size(ctx.mergeChunkInstances));
+  auto *&mc = ctx.mergeChunkInstances[p2Align];
   if (!mc)
     mc = make<MergeChunk>(c->getAlignment());
   mc->sections.push_back(c);
@@ -986,15 +1029,14 @@ void MergeChunk::writeTo(uint8_t *buf) const {
 }
 
 // MinGW specific.
-size_t AbsolutePointerChunk::getSize() const { return config->wordsize; }
+size_t AbsolutePointerChunk::getSize() const { return ctx.config.wordsize; }
 
 void AbsolutePointerChunk::writeTo(uint8_t *buf) const {
-  if (config->is64()) {
+  if (ctx.config.is64()) {
     write64le(buf, value);
   } else {
     write32le(buf, value);
   }
 }
 
-} // namespace coff
-} // namespace lld
+} // namespace lld::coff
index bdd3faa..9d9e738 100644 (file)
@@ -21,8 +21,7 @@
 #include <utility>
 #include <vector>
 
-namespace lld {
-namespace coff {
+namespace lld::coff {
 
 using llvm::COFF::ImportDirectoryTableEntry;
 using llvm::object::COFFSymbolRef;
@@ -101,7 +100,6 @@ public:
   // chunk has a back pointer to an output section.
   void setOutputSectionIdx(uint16_t o) { osidx = o; }
   uint16_t getOutputSectionIdx() const { return osidx; }
-  OutputSection *getOutputSection() const;
 
   // Windows-specific.
   // Collect all locations that contain absolute addresses for base relocations.
@@ -176,6 +174,23 @@ protected:
   NonSectionChunk(Kind k = OtherKind) : Chunk(k) {}
 };
 
+// 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;
+};
+
 // A chunk corresponding a section of an input file.
 class SectionChunk final : public Chunk {
   // Identical COMDAT Folding feature accesses section internal data.
@@ -223,13 +238,13 @@ public:
   bool isCOMDAT() const;
   void applyRelocation(uint8_t *off, const coff_relocation &rel) const;
   void applyRelX64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
-                   uint64_t p) const;
+                   uint64_t p, uint64_t imageBase) const;
   void applyRelX86(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
-                   uint64_t p) const;
+                   uint64_t p, uint64_t imageBase) const;
   void applyRelARM(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
-                   uint64_t p) const;
+                   uint64_t p, uint64_t imageBase) const;
   void applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
-                     uint64_t p) const;
+                     uint64_t p, uint64_t imageBase) const;
 
   void getRuntimePseudoRelocs(std::vector<RuntimePseudoReloc> &res);
 
@@ -261,7 +276,7 @@ public:
   }
 
   ArrayRef<coff_relocation> getRelocs() const {
-    return llvm::makeArrayRef(relocsData, relocsSize);
+    return llvm::ArrayRef(relocsData, relocsSize);
   }
 
   // Reloc setter used by ARM range extension thunk insertion.
@@ -415,7 +430,7 @@ inline StringRef Chunk::getDebugName() const {
 class MergeChunk : public NonSectionChunk {
 public:
   MergeChunk(uint32_t alignment);
-  static void addSection(SectionChunk *c);
+  static void addSection(COFFLinkerContext &ctx, SectionChunk *c);
   void finalizeContents();
   void assignSubsectionRVAs();
 
@@ -424,7 +439,6 @@ public:
   size_t getSize() const override;
   void writeTo(uint8_t *buf) const override;
 
-  static MergeChunk *instances[Log2MaxSectionAlignment + 1];
   std::vector<SectionChunk *> sections;
 
 private:
@@ -476,24 +490,26 @@ static const uint8_t importThunkARM64[] = {
 // contents will be a JMP instruction to some __imp_ symbol.
 class ImportThunkChunk : public NonSectionChunk {
 public:
-  ImportThunkChunk(Defined *s)
-      : NonSectionChunk(ImportThunkKind), impSymbol(s) {}
+  ImportThunkChunk(COFFLinkerContext &ctx, Defined *s)
+      : NonSectionChunk(ImportThunkKind), impSymbol(s), ctx(ctx) {}
   static bool classof(const Chunk *c) { return c->kind() == ImportThunkKind; }
 
 protected:
   Defined *impSymbol;
+  COFFLinkerContext &ctx;
 };
 
 class ImportThunkChunkX64 : public ImportThunkChunk {
 public:
-  explicit ImportThunkChunkX64(Defined *s);
+  explicit ImportThunkChunkX64(COFFLinkerContext &ctx, Defined *s);
   size_t getSize() const override { return sizeof(importThunkX86); }
   void writeTo(uint8_t *buf) const override;
 };
 
 class ImportThunkChunkX86 : public ImportThunkChunk {
 public:
-  explicit ImportThunkChunkX86(Defined *s) : ImportThunkChunk(s) {}
+  explicit ImportThunkChunkX86(COFFLinkerContext &ctx, Defined *s)
+      : ImportThunkChunk(ctx, s) {}
   size_t getSize() const override { return sizeof(importThunkX86); }
   void getBaserels(std::vector<Baserel> *res) override;
   void writeTo(uint8_t *buf) const override;
@@ -501,7 +517,8 @@ public:
 
 class ImportThunkChunkARM : public ImportThunkChunk {
 public:
-  explicit ImportThunkChunkARM(Defined *s) : ImportThunkChunk(s) {
+  explicit ImportThunkChunkARM(COFFLinkerContext &ctx, Defined *s)
+      : ImportThunkChunk(ctx, s) {
     setAlignment(2);
   }
   size_t getSize() const override { return sizeof(importThunkARM); }
@@ -511,7 +528,8 @@ public:
 
 class ImportThunkChunkARM64 : public ImportThunkChunk {
 public:
-  explicit ImportThunkChunkARM64(Defined *s) : ImportThunkChunk(s) {
+  explicit ImportThunkChunkARM64(COFFLinkerContext &ctx, Defined *s)
+      : ImportThunkChunk(ctx, s) {
     setAlignment(4);
   }
   size_t getSize() const override { return sizeof(importThunkARM64); }
@@ -520,35 +538,46 @@ public:
 
 class RangeExtensionThunkARM : public NonSectionChunk {
 public:
-  explicit RangeExtensionThunkARM(Defined *t) : target(t) { setAlignment(2); }
+  explicit RangeExtensionThunkARM(COFFLinkerContext &ctx, Defined *t)
+      : target(t), ctx(ctx) {
+    setAlignment(2);
+  }
   size_t getSize() const override;
   void writeTo(uint8_t *buf) const override;
 
   Defined *target;
+
+private:
+  COFFLinkerContext &ctx;
 };
 
 class RangeExtensionThunkARM64 : public NonSectionChunk {
 public:
-  explicit RangeExtensionThunkARM64(Defined *t) : target(t) { setAlignment(4); }
+  explicit RangeExtensionThunkARM64(COFFLinkerContext &ctx, Defined *t)
+      : target(t), ctx(ctx) {
+    setAlignment(4);
+  }
   size_t getSize() const override;
   void writeTo(uint8_t *buf) const override;
 
   Defined *target;
+
+private:
+  COFFLinkerContext &ctx;
 };
 
 // Windows-specific.
 // See comments for DefinedLocalImport class.
 class LocalImportChunk : public NonSectionChunk {
 public:
-  explicit LocalImportChunk(Defined *s) : sym(s) {
-    setAlignment(config->wordsize);
-  }
+  explicit LocalImportChunk(COFFLinkerContext &ctx, Defined *s);
   size_t getSize() const override;
   void getBaserels(std::vector<Baserel> *res) override;
   void writeTo(uint8_t *buf) const override;
 
 private:
   Defined *sym;
+  COFFLinkerContext &ctx;
 };
 
 // Duplicate RVAs are not allowed in RVA tables, so unique symbols by chunk and
@@ -615,8 +644,9 @@ private:
 class Baserel {
 public:
   Baserel(uint32_t v, uint8_t ty) : rva(v), type(ty) {}
-  explicit Baserel(uint32_t v) : Baserel(v, getDefaultType()) {}
-  uint8_t getDefaultType();
+  explicit Baserel(uint32_t v, llvm::COFF::MachineTypes machine)
+      : Baserel(v, getDefaultType(machine)) {}
+  uint8_t getDefaultType(llvm::COFF::MachineTypes machine);
 
   uint32_t rva;
   uint8_t type;
@@ -652,27 +682,11 @@ 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 NonSectionChunk {
 public:
-  AbsolutePointerChunk(uint64_t value) : value(value) {
+  AbsolutePointerChunk(COFFLinkerContext &ctx, uint64_t value)
+      : value(value), ctx(ctx) {
     setAlignment(getSize());
   }
   size_t getSize() const override;
@@ -680,6 +694,7 @@ public:
 
 private:
   uint64_t value;
+  COFFLinkerContext &ctx;
 };
 
 // Return true if this file has the hotpatch flag set to true in the S_COMPILE3
@@ -700,8 +715,28 @@ 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
+// Convenience class for initializing a coff_section with specific flags.
+class FakeSection {
+public:
+  FakeSection(int c) { section.Characteristics = c; }
+
+  coff_section section;
+};
+
+// Convenience class for initializing a SectionChunk with specific flags.
+class FakeSectionChunk {
+public:
+  FakeSectionChunk(const coff_section *section) : chunk(nullptr, section) {
+    // Comdats from LTO files can't be fully treated as regular comdats
+    // at this point; we don't know what size or contents they are going to
+    // have, so we can't do proper checking of such aspects of them.
+    chunk.selection = llvm::COFF::IMAGE_COMDAT_SELECT_ANY;
+  }
+
+  SectionChunk chunk;
+};
+
+} // namespace lld::coff
 
 namespace llvm {
 template <>
index df883b7..4711573 100644 (file)
 #define LLD_COFF_CONFIG_H
 
 #include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Object/COFF.h"
 #include "llvm/Support/CachePruning.h"
+#include "llvm/Support/VirtualFileSystem.h"
 #include <cstdint>
 #include <map>
 #include <set>
 #include <string>
 
-namespace lld {
-namespace coff {
+namespace lld::coff {
 
 using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN;
 using llvm::COFF::WindowsSubsystem;
@@ -42,6 +44,7 @@ static const auto I386 = llvm::COFF::IMAGE_FILE_MACHINE_I386;
 struct Export {
   StringRef name;       // N in /export:N or /export:E=N
   StringRef extName;    // E in /export:E=N
+  StringRef aliasTarget; // GNU specific: N in "alias == N"
   Symbol *sym = nullptr;
   uint16_t ordinal = 0;
   bool noname = false;
@@ -62,6 +65,7 @@ struct Export {
 
   bool operator==(const Export &e) {
     return (name == e.name && extName == e.extName &&
+            aliasTarget == e.aliasTarget &&
             ordinal == e.ordinal && noname == e.noname &&
             data == e.data && isPrivate == e.isPrivate);
   }
@@ -91,8 +95,8 @@ enum class ICFLevel {
 
 // Global configuration.
 struct Configuration {
-  enum ManifestKind { SideBySide, Embed, No };
-  bool is64() { return machine == AMD64 || machine == ARM64; }
+  enum ManifestKind { Default, SideBySide, Embed, No };
+  bool is64() const { return machine == AMD64 || machine == ARM64; }
 
   llvm::COFF::MachineTypes machine = IMAGE_FILE_MACHINE_UNKNOWN;
   size_t wordsize;
@@ -120,9 +124,11 @@ struct Configuration {
   bool showTiming = false;
   bool showSummary = false;
   unsigned debugTypes = static_cast<unsigned>(DebugType::None);
+  llvm::SmallVector<llvm::StringRef, 0> mllvmOpts;
   std::vector<std::string> natvisFiles;
   llvm::StringMap<std::string> namedStreams;
   llvm::SmallString<128> pdbAltPath;
+  int pdbPageSize = 4096;
   llvm::SmallString<128> pdbPath;
   llvm::SmallString<128> pdbSourcePath;
   std::vector<llvm::StringRef> argv;
@@ -136,6 +142,7 @@ struct Configuration {
   // True if we are creating a DLL.
   bool dll = false;
   StringRef implib;
+  bool noimplib = false;
   std::vector<Export> exports;
   bool hadExplicitExports;
   std::set<std::string> delayLoads;
@@ -166,8 +173,6 @@ struct Configuration {
   // Used for /opt:lldltocachepolicy=policy
   llvm::CachePruningPolicy ltoCachePolicy;
 
-  // Used for /opt:[no]ltonewpassmanager
-  bool ltoNewPassManager = false;
   // Used for /opt:[no]ltodebugpassmanager
   bool ltoDebugPassManager = false;
 
@@ -178,9 +183,9 @@ struct Configuration {
   std::map<StringRef, uint32_t> section;
 
   // Options for manifest files.
-  ManifestKind manifest = No;
+  ManifestKind manifest = Default;
   int manifestID = 1;
-  StringRef manifestDependency;
+  llvm::SetVector<StringRef> manifestDependencies;
   bool manifestUAC = true;
   std::vector<std::string> manifestInput;
   StringRef manifestLevel = "'asInvoker'";
@@ -205,6 +210,9 @@ struct Configuration {
   // Used for /map.
   std::string mapFile;
 
+  // Used for /mapinfo.
+  bool mapInfo = false;
+
   // Used for /thinlto-index-only:
   llvm::StringRef thinLTOIndexOnlyArg;
 
@@ -223,6 +231,9 @@ struct Configuration {
   // Used for /lto-cs-profile-path
   llvm::StringRef ltoCSProfileFile;
 
+  // Used for /lto-pgo-warn-mismatch:
+  bool ltoPGOWarnMismatch = true;
+
   // Used for /call-graph-ordering-file:
   llvm::MapVector<std::pair<const SectionChunk *, const SectionChunk *>,
                   uint64_t>
@@ -232,6 +243,9 @@ struct Configuration {
   // Used for /print-symbol-order:
   StringRef printSymbolOrder;
 
+  // Used for /vfsoverlay:
+  std::unique_ptr<llvm::vfs::FileSystem> vfs;
+
   uint64_t align = 4096;
   uint64_t imageBase = -1;
   uint64_t fileAlign = 512;
@@ -275,11 +289,9 @@ struct Configuration {
   bool autoImport = false;
   bool pseudoRelocs = false;
   bool stdcallFixup = false;
+  bool writeCheckSum = false;
 };
 
-extern Configuration *config;
-
-} // namespace coff
-} // namespace lld
+} // namespace lld::coff
 
 #endif
index b9e12ef..417b704 100644 (file)
 //===----------------------------------------------------------------------===//
 
 #include "DLL.h"
+#include "COFFLinkerContext.h"
 #include "Chunks.h"
 #include "SymbolTable.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/Object/COFF.h"
 #include "llvm/Support/Endian.h"
 #include "llvm/Support/Path.h"
@@ -29,8 +31,7 @@ using namespace llvm::object;
 using namespace llvm::support::endian;
 using namespace llvm::COFF;
 
-namespace lld {
-namespace coff {
+namespace lld::coff {
 namespace {
 
 // Import table
@@ -60,19 +61,23 @@ private:
 // A chunk for the import descriptor table.
 class LookupChunk : public NonSectionChunk {
 public:
-  explicit LookupChunk(Chunk *c) : hintName(c) {
-    setAlignment(config->wordsize);
+  explicit LookupChunk(COFFLinkerContext &ctx, Chunk *c)
+      : hintName(c), ctx(ctx) {
+    setAlignment(ctx.config.wordsize);
   }
-  size_t getSize() const override { return config->wordsize; }
+  size_t getSize() const override { return ctx.config.wordsize; }
 
   void writeTo(uint8_t *buf) const override {
-    if (config->is64())
+    if (ctx.config.is64())
       write64le(buf, hintName->getRVA());
     else
       write32le(buf, hintName->getRVA());
   }
 
   Chunk *hintName;
+
+private:
+  COFFLinkerContext &ctx;
 };
 
 // A chunk for the import descriptor table.
@@ -80,15 +85,16 @@ public:
 // See Microsoft PE/COFF spec 7.1. Import Header for details.
 class OrdinalOnlyChunk : public NonSectionChunk {
 public:
-  explicit OrdinalOnlyChunk(uint16_t v) : ordinal(v) {
-    setAlignment(config->wordsize);
+  explicit OrdinalOnlyChunk(COFFLinkerContext &c, uint16_t v)
+      : ordinal(v), ctx(c) {
+    setAlignment(ctx.config.wordsize);
   }
-  size_t getSize() const override { return config->wordsize; }
+  size_t getSize() const override { return ctx.config.wordsize; }
 
   void writeTo(uint8_t *buf) const override {
     // An import-by-ordinal slot has MSB 1 to indicate that
     // this is import-by-ordinal (and not import-by-name).
-    if (config->is64()) {
+    if (ctx.config.is64()) {
       write64le(buf, (1ULL << 63) | ordinal);
     } else {
       write32le(buf, (1ULL << 31) | ordinal);
@@ -96,6 +102,9 @@ public:
   }
 
   uint16_t ordinal;
+
+private:
+  COFFLinkerContext &ctx;
 };
 
 // A chunk for the import descriptor table.
@@ -134,14 +143,15 @@ private:
 };
 
 static std::vector<std::vector<DefinedImportData *>>
-binImports(const std::vector<DefinedImportData *> &imports) {
+binImports(COFFLinkerContext &ctx,
+           const std::vector<DefinedImportData *> &imports) {
   // Group DLL-imported symbols by DLL name because that's how
   // symbols are laid out in the import descriptor table.
-  auto less = [](const std::string &a, const std::string &b) {
-    return config->dllOrder[a] < config->dllOrder[b];
+  auto less = [&ctx](const std::string &a, const std::string &b) {
+    return ctx.config.dllOrder[a] < ctx.config.dllOrder[b];
   };
-  std::map<std::string, std::vector<DefinedImportData *>,
-           bool(*)(const std::string &, const std::string &)> m(less);
+  std::map<std::string, std::vector<DefinedImportData *>, decltype(less)> m(
+      less);
   for (DefinedImportData *sym : imports)
     m[sym->getDLLName().lower()].push_back(sym);
 
@@ -149,10 +159,9 @@ binImports(const std::vector<DefinedImportData *> &imports) {
   for (auto &kv : m) {
     // Sort symbols by name for each group.
     std::vector<DefinedImportData *> &syms = kv.second;
-    std::sort(syms.begin(), syms.end(),
-              [](DefinedImportData *a, DefinedImportData *b) {
-                return a->getName() < b->getName();
-              });
+    llvm::sort(syms, [](DefinedImportData *a, DefinedImportData *b) {
+      return a->getName() < b->getName();
+    });
     v.push_back(std::move(syms));
   }
   return v;
@@ -220,6 +229,19 @@ static const uint8_t tailMergeX64[] = {
     0xFF, 0xE0,                         // jmp     rax
 };
 
+static const uint8_t tailMergeUnwindInfoX64[] = {
+    0x01,       // Version=1, Flags=UNW_FLAG_NHANDLER
+    0x0a,       // Size of prolog
+    0x05,       // Count of unwind codes
+    0x00,       // No frame register
+    0x0a, 0x82, // Offset 0xa: UWOP_ALLOC_SMALL(0x48)
+    0x06, 0x02, // Offset 6: UWOP_ALLOC_SMALL(8)
+    0x04, 0x02, // Offset 4: UWOP_ALLOC_SMALL(8)
+    0x02, 0x02, // Offset 2: UWOP_ALLOC_SMALL(8)
+    0x01, 0x02, // Offset 1: UWOP_ALLOC_SMALL(8)
+    0x00, 0x00  // Padding to align on 32-bits
+};
+
 static const uint8_t thunkX86[] = {
     0xB8, 0, 0, 0, 0,  // mov   eax, offset ___imp__<FUNCNAME>
     0xE9, 0, 0, 0, 0,  // jmp   __tailMerge_<lib>
@@ -323,49 +345,93 @@ public:
   Defined *helper = nullptr;
 };
 
+class TailMergePDataChunkX64 : public NonSectionChunk {
+public:
+  TailMergePDataChunkX64(Chunk *tm, Chunk *unwind) : tm(tm), unwind(unwind) {
+    // See
+    // https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-runtime_function
+    setAlignment(4);
+  }
+
+  size_t getSize() const override { return 3 * sizeof(uint32_t); }
+
+  void writeTo(uint8_t *buf) const override {
+    write32le(buf + 0, tm->getRVA()); // TailMergeChunk start RVA
+    write32le(buf + 4, tm->getRVA() + tm->getSize()); // TailMergeChunk stop RVA
+    write32le(buf + 8, unwind->getRVA());             // UnwindInfo RVA
+  }
+
+  Chunk *tm = nullptr;
+  Chunk *unwind = nullptr;
+};
+
+class TailMergeUnwindInfoX64 : public NonSectionChunk {
+public:
+  TailMergeUnwindInfoX64() {
+    // See
+    // https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64#struct-unwind_info
+    setAlignment(4);
+  }
+
+  size_t getSize() const override { return sizeof(tailMergeUnwindInfoX64); }
+
+  void writeTo(uint8_t *buf) const override {
+    memcpy(buf, tailMergeUnwindInfoX64, sizeof(tailMergeUnwindInfoX64));
+  }
+};
+
 class ThunkChunkX86 : public NonSectionChunk {
 public:
-  ThunkChunkX86(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {}
+  ThunkChunkX86(COFFLinkerContext &ctx, Defined *i, Chunk *tm)
+      : imp(i), tailMerge(tm), ctx(ctx) {}
 
   size_t getSize() const override { return sizeof(thunkX86); }
 
   void writeTo(uint8_t *buf) const override {
     memcpy(buf, thunkX86, sizeof(thunkX86));
-    write32le(buf + 1, imp->getRVA() + config->imageBase);
+    write32le(buf + 1, imp->getRVA() + ctx.config.imageBase);
     write32le(buf + 6, tailMerge->getRVA() - rva - 10);
   }
 
   void getBaserels(std::vector<Baserel> *res) override {
-    res->emplace_back(rva + 1);
+    res->emplace_back(rva + 1, ctx.config.machine);
   }
 
   Defined *imp = nullptr;
   Chunk *tailMerge = nullptr;
+
+private:
+  const COFFLinkerContext &ctx;
 };
 
 class TailMergeChunkX86 : public NonSectionChunk {
 public:
-  TailMergeChunkX86(Chunk *d, Defined *h) : desc(d), helper(h) {}
+  TailMergeChunkX86(COFFLinkerContext &ctx, Chunk *d, Defined *h)
+      : desc(d), helper(h), ctx(ctx) {}
 
   size_t getSize() const override { return sizeof(tailMergeX86); }
 
   void writeTo(uint8_t *buf) const override {
     memcpy(buf, tailMergeX86, sizeof(tailMergeX86));
-    write32le(buf + 4, desc->getRVA() + config->imageBase);
+    write32le(buf + 4, desc->getRVA() + ctx.config.imageBase);
     write32le(buf + 9, helper->getRVA() - rva - 13);
   }
 
   void getBaserels(std::vector<Baserel> *res) override {
-    res->emplace_back(rva + 4);
+    res->emplace_back(rva + 4, ctx.config.machine);
   }
 
   Chunk *desc = nullptr;
   Defined *helper = nullptr;
+
+private:
+  const COFFLinkerContext &ctx;
 };
 
 class ThunkChunkARM : public NonSectionChunk {
 public:
-  ThunkChunkARM(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {
+  ThunkChunkARM(COFFLinkerContext &ctx, Defined *i, Chunk *tm)
+      : imp(i), tailMerge(tm), ctx(ctx) {
     setAlignment(2);
   }
 
@@ -373,7 +439,7 @@ public:
 
   void writeTo(uint8_t *buf) const override {
     memcpy(buf, thunkARM, sizeof(thunkARM));
-    applyMOV32T(buf + 0, imp->getRVA() + config->imageBase);
+    applyMOV32T(buf + 0, imp->getRVA() + ctx.config.imageBase);
     applyBranch24T(buf + 8, tailMerge->getRVA() - rva - 12);
   }
 
@@ -383,11 +449,15 @@ public:
 
   Defined *imp = nullptr;
   Chunk *tailMerge = nullptr;
+
+private:
+  const COFFLinkerContext &ctx;
 };
 
 class TailMergeChunkARM : public NonSectionChunk {
 public:
-  TailMergeChunkARM(Chunk *d, Defined *h) : desc(d), helper(h) {
+  TailMergeChunkARM(COFFLinkerContext &ctx, Chunk *d, Defined *h)
+      : desc(d), helper(h), ctx(ctx) {
     setAlignment(2);
   }
 
@@ -395,7 +465,7 @@ public:
 
   void writeTo(uint8_t *buf) const override {
     memcpy(buf, tailMergeARM, sizeof(tailMergeARM));
-    applyMOV32T(buf + 14, desc->getRVA() + config->imageBase);
+    applyMOV32T(buf + 14, desc->getRVA() + ctx.config.imageBase);
     applyBranch24T(buf + 22, helper->getRVA() - rva - 26);
   }
 
@@ -405,6 +475,9 @@ public:
 
   Chunk *desc = nullptr;
   Defined *helper = nullptr;
+
+private:
+  const COFFLinkerContext &ctx;
 };
 
 class ThunkChunkARM64 : public NonSectionChunk {
@@ -448,28 +521,32 @@ public:
 // A chunk for the import descriptor table.
 class DelayAddressChunk : public NonSectionChunk {
 public:
-  explicit DelayAddressChunk(Chunk *c) : thunk(c) {
-    setAlignment(config->wordsize);
+  explicit DelayAddressChunk(COFFLinkerContext &ctx, Chunk *c)
+      : thunk(c), ctx(ctx) {
+    setAlignment(ctx.config.wordsize);
   }
-  size_t getSize() const override { return config->wordsize; }
+  size_t getSize() const override { return ctx.config.wordsize; }
 
   void writeTo(uint8_t *buf) const override {
-    if (config->is64()) {
-      write64le(buf, thunk->getRVA() + config->imageBase);
+    if (ctx.config.is64()) {
+      write64le(buf, thunk->getRVA() + ctx.config.imageBase);
     } else {
       uint32_t bit = 0;
       // Pointer to thumb code must have the LSB set, so adjust it.
-      if (config->machine == ARMNT)
+      if (ctx.config.machine == ARMNT)
         bit = 1;
-      write32le(buf, (thunk->getRVA() + config->imageBase) | bit);
+      write32le(buf, (thunk->getRVA() + ctx.config.imageBase) | bit);
     }
   }
 
   void getBaserels(std::vector<Baserel> *res) override {
-    res->emplace_back(rva);
+    res->emplace_back(rva, ctx.config.machine);
   }
 
   Chunk *thunk;
+
+private:
+  const COFFLinkerContext &ctx;
 };
 
 // Export table
@@ -491,8 +568,8 @@ public:
 
     auto *e = (export_directory_table_entry *)(buf);
     e->NameRVA = dllName->getRVA();
-    e->OrdinalBase = 0;
-    e->AddressTableEntries = maxOrdinal + 1;
+    e->OrdinalBase = 1;
+    e->AddressTableEntries = maxOrdinal;
     e->NumberOfNamePointers = nameTabSize;
     e->ExportAddressTableRVA = addressTab->getRVA();
     e->NamePointerRVA = nameTab->getRVA();
@@ -509,17 +586,20 @@ public:
 
 class AddressTableChunk : public NonSectionChunk {
 public:
-  explicit AddressTableChunk(size_t maxOrdinal) : size(maxOrdinal + 1) {}
+  explicit AddressTableChunk(COFFLinkerContext &ctx, size_t maxOrdinal)
+      : size(maxOrdinal), ctx(ctx) {}
   size_t getSize() const override { return size * 4; }
 
   void writeTo(uint8_t *buf) const override {
     memset(buf, 0, getSize());
 
-    for (const Export &e : config->exports) {
-      uint8_t *p = buf + e.ordinal * 4;
+    for (const Export &e : ctx.config.exports) {
+      assert(e.ordinal != 0 && "Export symbol has invalid ordinal");
+      // OrdinalBase is 1, so subtract 1 to get the index.
+      uint8_t *p = buf + (e.ordinal - 1) * 4;
       uint32_t bit = 0;
       // Pointer to thumb code must have the LSB set, so adjust it.
-      if (config->machine == ARMNT && !e.data)
+      if (ctx.config.machine == ARMNT && !e.data)
         bit = 1;
       if (e.forwardChunk) {
         write32le(p, e.forwardChunk->getRVA() | bit);
@@ -533,6 +613,7 @@ public:
 
 private:
   size_t size;
+  const COFFLinkerContext &ctx;
 };
 
 class NamePointersChunk : public NonSectionChunk {
@@ -553,26 +634,30 @@ private:
 
 class ExportOrdinalChunk : public NonSectionChunk {
 public:
-  explicit ExportOrdinalChunk(size_t i) : size(i) {}
+  explicit ExportOrdinalChunk(const COFFLinkerContext &ctx, size_t i)
+      : size(i), ctx(ctx) {}
   size_t getSize() const override { return size * 2; }
 
   void writeTo(uint8_t *buf) const override {
-    for (Export &e : config->exports) {
+    for (const Export &e : ctx.config.exports) {
       if (e.noname)
         continue;
-      write16le(buf, e.ordinal);
+      assert(e.ordinal != 0 && "Export symbol has invalid ordinal");
+      // This table stores unbiased indices, so subtract 1 (OrdinalBase).
+      write16le(buf, e.ordinal - 1);
       buf += 2;
     }
   }
 
 private:
   size_t size;
+  const COFFLinkerContext &ctx;
 };
 
 } // anonymous namespace
 
-void IdataContents::create() {
-  std::vector<std::vector<DefinedImportData *>> v = binImports(imports);
+void IdataContents::create(COFFLinkerContext &ctx) {
+  std::vector<std::vector<DefinedImportData *>> v = binImports(ctx, imports);
 
   // Create .idata contents for each DLL.
   for (std::vector<DefinedImportData *> &syms : v) {
@@ -584,18 +669,18 @@ void IdataContents::create() {
     for (DefinedImportData *s : syms) {
       uint16_t ord = s->getOrdinal();
       if (s->getExternalName().empty()) {
-        lookups.push_back(make<OrdinalOnlyChunk>(ord));
-        addresses.push_back(make<OrdinalOnlyChunk>(ord));
+        lookups.push_back(make<OrdinalOnlyChunk>(ctx, ord));
+        addresses.push_back(make<OrdinalOnlyChunk>(ctx, ord));
         continue;
       }
       auto *c = make<HintNameChunk>(s->getExternalName(), ord);
-      lookups.push_back(make<LookupChunk>(c));
-      addresses.push_back(make<LookupChunk>(c));
+      lookups.push_back(make<LookupChunk>(ctx, c));
+      addresses.push_back(make<LookupChunk>(ctx, c));
       hints.push_back(c);
     }
     // Terminate with null values.
-    lookups.push_back(make<NullChunk>(config->wordsize));
-    addresses.push_back(make<NullChunk>(config->wordsize));
+    lookups.push_back(make<NullChunk>(ctx.config.wordsize));
+    addresses.push_back(make<NullChunk>(ctx.config.wordsize));
 
     for (int i = 0, e = syms.size(); i < e; ++i)
       syms[i]->setLocation(addresses[base + i]);
@@ -633,7 +718,9 @@ uint64_t DelayLoadContents::getDirSize() {
 
 void DelayLoadContents::create(Defined *h) {
   helper = h;
-  std::vector<std::vector<DefinedImportData *>> v = binImports(imports);
+  std::vector<std::vector<DefinedImportData *>> v = binImports(ctx, imports);
+
+  Chunk *unwind = newTailMergeUnwindInfoChunk();
 
   // Create .didat contents for each DLL.
   for (std::vector<DefinedImportData *> &syms : v) {
@@ -643,30 +730,33 @@ void DelayLoadContents::create(Defined *h) {
 
     size_t base = addresses.size();
     Chunk *tm = newTailMergeChunk(dir);
+    Chunk *pdataChunk = unwind ? newTailMergePDataChunk(tm, unwind) : nullptr;
     for (DefinedImportData *s : syms) {
       Chunk *t = newThunkChunk(s, tm);
-      auto *a = make<DelayAddressChunk>(t);
+      auto *a = make<DelayAddressChunk>(ctx, t);
       addresses.push_back(a);
       thunks.push_back(t);
       StringRef extName = s->getExternalName();
       if (extName.empty()) {
-        names.push_back(make<OrdinalOnlyChunk>(s->getOrdinal()));
+        names.push_back(make<OrdinalOnlyChunk>(ctx, s->getOrdinal()));
       } else {
         auto *c = make<HintNameChunk>(extName, 0);
-        names.push_back(make<LookupChunk>(c));
+        names.push_back(make<LookupChunk>(ctx, c));
         hintNames.push_back(c);
-        // Add a syntentic symbol for this load thunk, using the "__imp_load"
+        // Add a synthetic symbol for this load thunk, using the "__imp___load"
         // prefix, in case this thunk needs to be added to the list of valid
         // call targets for Control Flow Guard.
-        StringRef symName = saver.save("__imp_load_" + extName);
+        StringRef symName = saver().save("__imp___load_" + extName);
         s->loadThunkSym =
-            cast<DefinedSynthetic>(symtab->addSynthetic(symName, t));
+            cast<DefinedSynthetic>(ctx.symtab.addSynthetic(symName, t));
       }
     }
     thunks.push_back(tm);
+    if (pdataChunk)
+      pdata.push_back(pdataChunk);
     StringRef tmName =
-        saver.save("__tailMerge_" + syms[0]->getDLLName().lower());
-    symtab->addSynthetic(tmName, tm);
+        saver().save("__tailMerge_" + syms[0]->getDLLName().lower());
+    ctx.symtab.addSynthetic(tmName, tm);
     // Terminate with null values.
     addresses.push_back(make<NullChunk>(8));
     names.push_back(make<NullChunk>(8));
@@ -683,18 +773,21 @@ void DelayLoadContents::create(Defined *h) {
     dir->nameTab = names[base];
     dirs.push_back(dir);
   }
+
+  if (unwind)
+    unwindinfo.push_back(unwind);
   // Add null terminator.
   dirs.push_back(make<NullChunk>(sizeof(delay_import_directory_table_entry)));
 }
 
 Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) {
-  switch (config->machine) {
+  switch (ctx.config.machine) {
   case AMD64:
     return make<TailMergeChunkX64>(dir, helper);
   case I386:
-    return make<TailMergeChunkX86>(dir, helper);
+    return make<TailMergeChunkX86>(ctx, dir, helper);
   case ARMNT:
-    return make<TailMergeChunkARM>(dir, helper);
+    return make<TailMergeChunkARM>(ctx, dir, helper);
   case ARM64:
     return make<TailMergeChunkARM64>(dir, helper);
   default:
@@ -702,15 +795,34 @@ Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) {
   }
 }
 
+Chunk *DelayLoadContents::newTailMergeUnwindInfoChunk() {
+  switch (ctx.config.machine) {
+  case AMD64:
+    return make<TailMergeUnwindInfoX64>();
+    // FIXME: Add support for other architectures.
+  default:
+    return nullptr; // Just don't generate unwind info.
+  }
+}
+Chunk *DelayLoadContents::newTailMergePDataChunk(Chunk *tm, Chunk *unwind) {
+  switch (ctx.config.machine) {
+  case AMD64:
+    return make<TailMergePDataChunkX64>(tm, unwind);
+    // FIXME: Add support for other architectures.
+  default:
+    return nullptr; // Just don't generate unwind info.
+  }
+}
+
 Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s,
                                         Chunk *tailMerge) {
-  switch (config->machine) {
+  switch (ctx.config.machine) {
   case AMD64:
     return make<ThunkChunkX64>(s, tailMerge);
   case I386:
-    return make<ThunkChunkX86>(s, tailMerge);
+    return make<ThunkChunkX86>(ctx, s, tailMerge);
   case ARMNT:
-    return make<ThunkChunkARM>(s, tailMerge);
+    return make<ThunkChunkARM>(ctx, s, tailMerge);
   case ARM64:
     return make<ThunkChunkARM64>(s, tailMerge);
   default:
@@ -718,20 +830,20 @@ Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s,
   }
 }
 
-EdataContents::EdataContents() {
+EdataContents::EdataContents(COFFLinkerContext &ctx) : ctx(ctx) {
   uint16_t maxOrdinal = 0;
-  for (Export &e : config->exports)
+  for (Export &e : ctx.config.exports)
     maxOrdinal = std::max(maxOrdinal, e.ordinal);
 
-  auto *dllName = make<StringChunk>(sys::path::filename(config->outputFile));
-  auto *addressTab = make<AddressTableChunk>(maxOrdinal);
+  auto *dllName = make<StringChunk>(sys::path::filename(ctx.config.outputFile));
+  auto *addressTab = make<AddressTableChunk>(ctx, maxOrdinal);
   std::vector<Chunk *> names;
-  for (Export &e : config->exports)
+  for (Export &e : ctx.config.exports)
     if (!e.noname)
       names.push_back(make<StringChunk>(e.exportName));
 
   std::vector<Chunk *> forwards;
-  for (Export &e : config->exports) {
+  for (Export &e : ctx.config.exports) {
     if (e.forwardTo.empty())
       continue;
     e.forwardChunk = make<StringChunk>(e.forwardTo);
@@ -739,7 +851,7 @@ EdataContents::EdataContents() {
   }
 
   auto *nameTab = make<NamePointersChunk>(names);
-  auto *ordinalTab = make<ExportOrdinalChunk>(names.size());
+  auto *ordinalTab = make<ExportOrdinalChunk>(ctx, names.size());
   auto *dir = make<ExportDirectoryChunk>(maxOrdinal, names.size(), dllName,
                                          addressTab, nameTab, ordinalTab);
   chunks.push_back(dir);
@@ -751,5 +863,4 @@ EdataContents::EdataContents() {
   chunks.insert(chunks.end(), forwards.begin(), forwards.end());
 }
 
-} // namespace coff
-} // namespace lld
+} // namespace lld::coff
index ce0ee01..7cf71f5 100644 (file)
@@ -12,8 +12,7 @@
 #include "Chunks.h"
 #include "Symbols.h"
 
-namespace lld {
-namespace coff {
+namespace lld::coff {
 
 // Windows-specific.
 // IdataContents creates all chunks for the DLL import table.
@@ -24,7 +23,7 @@ public:
   void add(DefinedImportData *sym) { imports.push_back(sym); }
   bool empty() { return imports.empty(); }
 
-  void create();
+  void create(COFFLinkerContext &ctx);
 
   std::vector<DefinedImportData *> imports;
   std::vector<Chunk *> dirs;
@@ -38,12 +37,15 @@ public:
 // DelayLoadContents creates all chunks for the delay-load DLL import table.
 class DelayLoadContents {
 public:
+  DelayLoadContents(COFFLinkerContext &ctx) : ctx(ctx) {}
   void add(DefinedImportData *sym) { imports.push_back(sym); }
   bool empty() { return imports.empty(); }
   void create(Defined *helper);
   std::vector<Chunk *> getChunks();
   std::vector<Chunk *> getDataChunks();
   ArrayRef<Chunk *> getCodeChunks() { return thunks; }
+  ArrayRef<Chunk *> getCodePData() { return pdata; }
+  ArrayRef<Chunk *> getCodeUnwindInfo() { return unwindinfo; }
 
   uint64_t getDirRVA() { return dirs[0]->getRVA(); }
   uint64_t getDirSize();
@@ -51,6 +53,8 @@ public:
 private:
   Chunk *newThunkChunk(DefinedImportData *s, Chunk *tailMerge);
   Chunk *newTailMergeChunk(Chunk *dir);
+  Chunk *newTailMergePDataChunk(Chunk *tm, Chunk *unwind);
+  Chunk *newTailMergeUnwindInfoChunk();
 
   Defined *helper;
   std::vector<DefinedImportData *> imports;
@@ -60,23 +64,28 @@ private:
   std::vector<Chunk *> names;
   std::vector<Chunk *> hintNames;
   std::vector<Chunk *> thunks;
+  std::vector<Chunk *> pdata;
+  std::vector<Chunk *> unwindinfo;
   std::vector<Chunk *> dllNames;
+
+  COFFLinkerContext &ctx;
 };
 
 // Windows-specific.
 // EdataContents creates all chunks for the DLL export table.
 class EdataContents {
 public:
-  EdataContents();
+  EdataContents(COFFLinkerContext &ctx);
   std::vector<Chunk *> chunks;
 
   uint64_t getRVA() { return chunks[0]->getRVA(); }
   uint64_t getSize() {
     return chunks.back()->getRVA() + chunks.back()->getSize() - getRVA();
   }
+
+  COFFLinkerContext &ctx;
 };
 
-} // namespace coff
-} // namespace lld
+} // namespace lld::coff
 
 #endif
index 97be5bc..7bbce84 100644 (file)
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "DebugTypes.h"
+#include "COFFLinkerContext.h"
 #include "Chunks.h"
 #include "Driver.h"
 #include "InputFiles.h"
@@ -14,7 +15,6 @@
 #include "TypeMerger.h"
 #include "lld/Common/ErrorHandler.h"
 #include "lld/Common/Memory.h"
-#include "lld/Common/Timer.h"
 #include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
 #include "llvm/DebugInfo/CodeView/TypeRecord.h"
 #include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h"
@@ -46,18 +46,22 @@ class TypeServerIpiSource;
 // before any dependent OBJ.
 class TypeServerSource : public TpiSource {
 public:
-  explicit TypeServerSource(PDBInputFile *f)
-      : TpiSource(PDB, nullptr), pdbInputFile(f) {
-    if (f->loadErr && *f->loadErr)
+  explicit TypeServerSource(COFFLinkerContext &ctx, PDBInputFile *f)
+      : TpiSource(ctx, PDB, nullptr), pdbInputFile(f) {
+    if (f->loadErrorStr)
       return;
     pdb::PDBFile &file = f->session->getPDBFile();
     auto expectedInfo = file.getPDBInfoStream();
     if (!expectedInfo)
       return;
     Guid = expectedInfo->getGuid();
-    auto it = mappings.emplace(Guid, this);
-    assert(it.second);
-    (void)it;
+    auto it = ctx.typeServerSourceMappings.emplace(Guid, this);
+    if (!it.second) {
+      // If we hit here we have collision on Guid's in two PDB files.
+      // This can happen if the PDB Guid is invalid or if we are really
+      // unlucky. This should fall back on stright file-system lookup.
+      it.first->second = nullptr;
+    }
   }
 
   Error mergeDebugT(TypeMerger *m) override;
@@ -74,8 +78,6 @@ public:
 
   // The PDB signature GUID.
   codeview::GUID Guid;
-
-  static std::map<codeview::GUID, TypeServerSource *> mappings;
 };
 
 // Companion to TypeServerSource. Stores the index map for the IPI stream in the
@@ -83,7 +85,8 @@ public:
 // invariant of one type index space per source.
 class TypeServerIpiSource : public TpiSource {
 public:
-  explicit TypeServerIpiSource() : TpiSource(PDBIpi, nullptr) {}
+  explicit TypeServerIpiSource(COFFLinkerContext &ctx)
+      : TpiSource(ctx, PDBIpi, nullptr) {}
 
   friend class TypeServerSource;
 
@@ -101,8 +104,8 @@ class UseTypeServerSource : public TpiSource {
   Expected<TypeServerSource *> getTypeServerSource();
 
 public:
-  UseTypeServerSource(ObjFile *f, TypeServer2Record ts)
-      : TpiSource(UsingPDB, f), typeServerDependency(ts) {}
+  UseTypeServerSource(COFFLinkerContext &ctx, ObjFile *f, TypeServer2Record ts)
+      : TpiSource(ctx, UsingPDB, f), typeServerDependency(ts) {}
 
   Error mergeDebugT(TypeMerger *m) override;
 
@@ -121,29 +124,32 @@ public:
 // such files, clang does not.
 class PrecompSource : public TpiSource {
 public:
-  PrecompSource(ObjFile *f) : TpiSource(PCH, f) {
-    if (!f->pchSignature || !*f->pchSignature)
-      fatal(toString(f) +
-            " claims to be a PCH object, but does not have a valid signature");
-    auto it = mappings.emplace(*f->pchSignature, this);
-    if (!it.second)
-      fatal("a PCH object with the same signature has already been provided (" +
-            toString(it.first->second->file) + " and " + toString(file) + ")");
+  PrecompSource(COFFLinkerContext &ctx, ObjFile *f) : TpiSource(ctx, PCH, f) {
+    // If the S_OBJNAME record contains the PCH signature, we'll register this
+    // source file right away.
+    registerMapping();
   }
 
+  Error mergeDebugT(TypeMerger *m) override;
+
   void loadGHashes() override;
 
   bool isDependency() const override { return true; }
 
-  static std::map<uint32_t, PrecompSource *> mappings;
+private:
+  void registerMapping();
+
+  // Whether this precomp OBJ was recorded in the precompSourceMappings map.
+  // Only happens if the file->pchSignature is valid.
+  bool registered = false;
 };
 
 // This class represents the debug type stream of an OBJ file that depends on a
 // Microsoft precompiled headers OBJ (see PrecompSource).
 class UsePrecompSource : public TpiSource {
 public:
-  UsePrecompSource(ObjFile *f, PrecompRecord precomp)
-      : TpiSource(UsingPCH, f), precompDependency(precomp) {}
+  UsePrecompSource(COFFLinkerContext &ctx, ObjFile *f, PrecompRecord precomp)
+      : TpiSource(ctx, UsingPCH, f), precompDependency(precomp) {}
 
   Error mergeDebugT(TypeMerger *m) override;
 
@@ -153,6 +159,10 @@ public:
 private:
   Error mergeInPrecompHeaderObj();
 
+  PrecompSource *findObjByName(StringRef fileNameOnly);
+  PrecompSource *findPrecompSource(ObjFile *file, PrecompRecord &pr);
+  Expected<PrecompSource *> findPrecompMap(ObjFile *file, PrecompRecord &pr);
+
 public:
   // Information about the Precomp OBJ dependency, that needs to be loaded in
   // before merging this OBJ.
@@ -160,13 +170,9 @@ public:
 };
 } // namespace
 
-std::vector<TpiSource *> TpiSource::instances;
-ArrayRef<TpiSource *> TpiSource::dependencySources;
-ArrayRef<TpiSource *> TpiSource::objectSources;
-
-TpiSource::TpiSource(TpiKind k, ObjFile *f)
-    : kind(k), tpiSrcIdx(instances.size()), file(f) {
-  instances.push_back(this);
+TpiSource::TpiSource(COFFLinkerContext &ctx, TpiKind k, ObjFile *f)
+    : ctx(ctx), kind(k), tpiSrcIdx(ctx.tpiSourceList.size()), file(f) {
+  ctx.addTpiSource(this);
 }
 
 // Vtable key method.
@@ -175,52 +181,35 @@ TpiSource::~TpiSource() {
   consumeError(std::move(typeMergingError));
 }
 
-void TpiSource::sortDependencies() {
-  // Order dependencies first, but preserve the existing order.
-  std::vector<TpiSource *> deps;
-  std::vector<TpiSource *> objs;
-  for (TpiSource *s : instances)
-    (s->isDependency() ? deps : objs).push_back(s);
-  uint32_t numDeps = deps.size();
-  uint32_t numObjs = objs.size();
-  instances = std::move(deps);
-  instances.insert(instances.end(), objs.begin(), objs.end());
-  for (uint32_t i = 0, e = instances.size(); i < e; ++i)
-    instances[i]->tpiSrcIdx = i;
-  dependencySources = makeArrayRef(instances.data(), numDeps);
-  objectSources = makeArrayRef(instances.data() + numDeps, numObjs);
-}
-
-TpiSource *lld::coff::makeTpiSource(ObjFile *file) {
-  return make<TpiSource>(TpiSource::Regular, file);
+TpiSource *lld::coff::makeTpiSource(COFFLinkerContext &ctx, ObjFile *file) {
+  return make<TpiSource>(ctx, TpiSource::Regular, file);
 }
 
-TpiSource *lld::coff::makeTypeServerSource(PDBInputFile *pdbInputFile) {
+TpiSource *lld::coff::makeTypeServerSource(COFFLinkerContext &ctx,
+                                           PDBInputFile *pdbInputFile) {
   // Type server sources come in pairs: the TPI stream, and the IPI stream.
-  auto *tpiSource = make<TypeServerSource>(pdbInputFile);
+  auto *tpiSource = make<TypeServerSource>(ctx, pdbInputFile);
   if (pdbInputFile->session->getPDBFile().hasPDBIpiStream())
-    tpiSource->ipiSrc = make<TypeServerIpiSource>();
+    tpiSource->ipiSrc = make<TypeServerIpiSource>(ctx);
   return tpiSource;
 }
 
-TpiSource *lld::coff::makeUseTypeServerSource(ObjFile *file,
+TpiSource *lld::coff::makeUseTypeServerSource(COFFLinkerContext &ctx,
+                                              ObjFile *file,
                                               TypeServer2Record ts) {
-  return make<UseTypeServerSource>(file, ts);
+  return make<UseTypeServerSource>(ctx, file, ts);
 }
 
-TpiSource *lld::coff::makePrecompSource(ObjFile *file) {
-  return make<PrecompSource>(file);
+TpiSource *lld::coff::makePrecompSource(COFFLinkerContext &ctx, ObjFile *file) {
+  return make<PrecompSource>(ctx, file);
 }
 
-TpiSource *lld::coff::makeUsePrecompSource(ObjFile *file,
+TpiSource *lld::coff::makeUsePrecompSource(COFFLinkerContext &ctx,
+                                           ObjFile *file,
                                            PrecompRecord precomp) {
-  return make<UsePrecompSource>(file, precomp);
+  return make<UsePrecompSource>(ctx, file, precomp);
 }
 
-std::map<codeview::GUID, TypeServerSource *> TypeServerSource::mappings;
-
-std::map<uint32_t, PrecompSource *> PrecompSource::mappings;
-
 bool TpiSource::remapTypeIndex(TypeIndex &ti, TiRefKind refKind) const {
   if (ti.isSimple())
     return true;
@@ -246,7 +235,7 @@ void TpiSource::remapRecord(MutableArrayRef<uint8_t> rec,
         reinterpret_cast<TypeIndex *>(contents.data() + ref.Offset), ref.Count);
     for (TypeIndex &ti : indices) {
       if (!remapTypeIndex(ti, ref.Kind)) {
-        if (config->verbose) {
+        if (ctx.config.verbose) {
           uint16_t kind =
               reinterpret_cast<const RecordPrefix *>(rec.data())->RecordKind;
           StringRef fname = file ? file->getName() : "<unknown PDB>";
@@ -291,18 +280,18 @@ static bool canUseDebugH(ArrayRef<uint8_t> debugH) {
   debugH = debugH.drop_front(sizeof(object::debug_h_header));
   return header->Magic == COFF::DEBUG_HASHES_SECTION_MAGIC &&
          header->Version == 0 &&
-         header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1_8) &&
+         header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::BLAKE3) &&
          (debugH.size() % 8 == 0);
 }
 
-static Optional<ArrayRef<uint8_t>> getDebugH(ObjFile *file) {
+static std::optional<ArrayRef<uint8_t>> getDebugH(ObjFile *file) {
   SectionChunk *sec =
       SectionChunk::findByName(file->getDebugChunks(), ".debug$H");
   if (!sec)
-    return llvm::None;
+    return std::nullopt;
   ArrayRef<uint8_t> contents = sec->getContents();
   if (!canUseDebugH(contents))
-    return None;
+    return std::nullopt;
   return contents;
 }
 
@@ -316,7 +305,7 @@ getHashesFromDebugH(ArrayRef<uint8_t> debugH) {
 
 // Merge .debug$T for a generic object file.
 Error TpiSource::mergeDebugT(TypeMerger *m) {
-  assert(!config->debugGHashes &&
+  assert(!ctx.config.debugGHashes &&
          "use remapTpiWithGHashes when ghash is enabled");
 
   CVTypeArray types;
@@ -326,16 +315,21 @@ Error TpiSource::mergeDebugT(TypeMerger *m) {
   // When dealing with PCH.OBJ, some indices were already merged.
   unsigned nbHeadIndices = indexMapStorage.size();
 
-  if (auto err = mergeTypeAndIdRecords(
-          m->idTable, m->typeTable, indexMapStorage, types, file->pchSignature))
+  std::optional<PCHMergerInfo> pchInfo;
+  if (auto err = mergeTypeAndIdRecords(m->idTable, m->typeTable,
+                                       indexMapStorage, types, pchInfo))
     fatal("codeview::mergeTypeAndIdRecords failed: " +
           toString(std::move(err)));
+  if (pchInfo) {
+    file->pchSignature = pchInfo->PCHSignature;
+    endPrecompIdx = pchInfo->EndPrecompIndex;
+  }
 
   // In an object, there is only one mapping for both types and items.
   tpiMap = indexMapStorage;
   ipiMap = indexMapStorage;
 
-  if (config->showSummary) {
+  if (ctx.config.showSummary) {
     nbTypeRecords = indexMapStorage.size() - nbHeadIndices;
     nbTypeRecordsBytes = reader.getLength();
     // Count how many times we saw each type record in our input. This
@@ -345,7 +339,7 @@ Error TpiSource::mergeDebugT(TypeMerger *m) {
     m->tpiCounts.resize(m->getTypeTable().size());
     m->ipiCounts.resize(m->getIDTable().size());
     uint32_t srcIdx = nbHeadIndices;
-    for (CVType &ty : types) {
+    for (const CVType &ty : types) {
       TypeIndex dstIdx = tpiMap[srcIdx++];
       // Type merging may fail, so a complex source type may become the simple
       // NotTranslated type, which cannot be used as an array index.
@@ -362,7 +356,7 @@ Error TpiSource::mergeDebugT(TypeMerger *m) {
 
 // Merge types from a type server PDB.
 Error TypeServerSource::mergeDebugT(TypeMerger *m) {
-  assert(!config->debugGHashes &&
+  assert(!ctx.config.debugGHashes &&
          "use remapTpiWithGHashes when ghash is enabled");
 
   pdb::PDBFile &pdbFile = pdbInputFile->session->getPDBFile();
@@ -391,7 +385,7 @@ Error TypeServerSource::mergeDebugT(TypeMerger *m) {
     ipiMap = ipiSrc->indexMapStorage;
   }
 
-  if (config->showSummary) {
+  if (ctx.config.showSummary) {
     nbTypeRecords = tpiMap.size() + ipiMap.size();
     nbTypeRecordsBytes =
         expectedTpi->typeArray().getUnderlyingStream().getLength() +
@@ -418,19 +412,22 @@ Expected<TypeServerSource *> UseTypeServerSource::getTypeServerSource() {
   const codeview::GUID &tsId = typeServerDependency.getGuid();
   StringRef tsPath = typeServerDependency.getName();
 
-  TypeServerSource *tsSrc;
-  auto it = TypeServerSource::mappings.find(tsId);
-  if (it != TypeServerSource::mappings.end()) {
-    tsSrc = it->second;
-  } else {
+  TypeServerSource *tsSrc = nullptr;
+  auto it = ctx.typeServerSourceMappings.find(tsId);
+  if (it != ctx.typeServerSourceMappings.end()) {
+    tsSrc = (TypeServerSource *)it->second;
+  }
+  if (tsSrc == nullptr) {
     // The file failed to load, lookup by name
-    PDBInputFile *pdb = PDBInputFile::findFromRecordPath(tsPath, file);
+    PDBInputFile *pdb = PDBInputFile::findFromRecordPath(ctx, tsPath, file);
     if (!pdb)
       return createFileError(tsPath, errorCodeToError(std::error_code(
                                          ENOENT, std::generic_category())));
     // If an error occurred during loading, throw it now
-    if (pdb->loadErr && *pdb->loadErr)
-      return createFileError(tsPath, std::move(*pdb->loadErr));
+    if (pdb->loadErrorStr)
+      return createFileError(
+          tsPath, make_error<StringError>(*pdb->loadErrorStr,
+                                          llvm::inconvertibleErrorCode()));
 
     tsSrc = (TypeServerSource *)pdb->debugTypesObj;
 
@@ -471,36 +468,37 @@ static bool equalsPath(StringRef path1, StringRef path2) {
 }
 
 // Find by name an OBJ provided on the command line
-static PrecompSource *findObjByName(StringRef fileNameOnly) {
+PrecompSource *UsePrecompSource::findObjByName(StringRef fileNameOnly) {
   SmallString<128> currentPath;
-  for (auto kv : PrecompSource::mappings) {
+  for (auto kv : ctx.precompSourceMappings) {
     StringRef currentFileName = sys::path::filename(kv.second->file->getName(),
                                                     sys::path::Style::windows);
 
     // Compare based solely on the file name (link.exe behavior)
     if (equalsPath(currentFileName, fileNameOnly))
-      return kv.second;
+      return (PrecompSource *)kv.second;
   }
   return nullptr;
 }
 
-static PrecompSource *findPrecompSource(ObjFile *file, PrecompRecord &pr) {
+PrecompSource *UsePrecompSource::findPrecompSource(ObjFile *file,
+                                                   PrecompRecord &pr) {
   // 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> prFileName =
       sys::path::filename(pr.getPrecompFilePath(), sys::path::Style::windows);
 
-  auto it = PrecompSource::mappings.find(pr.getSignature());
-  if (it != PrecompSource::mappings.end()) {
-    return it->second;
+  auto it = ctx.precompSourceMappings.find(pr.getSignature());
+  if (it != ctx.precompSourceMappings.end()) {
+    return (PrecompSource *)it->second;
   }
   // Lookup by name
   return findObjByName(prFileName);
 }
 
-static Expected<PrecompSource *> findPrecompMap(ObjFile *file,
-                                                PrecompRecord &pr) {
+Expected<PrecompSource *> UsePrecompSource::findPrecompMap(ObjFile *file,
+                                                           PrecompRecord &pr) {
   PrecompSource *precomp = findPrecompSource(file, pr);
 
   if (!precomp)
@@ -508,16 +506,15 @@ static Expected<PrecompSource *> findPrecompMap(ObjFile *file,
         pr.getPrecompFilePath(),
         make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch));
 
-  if (pr.getSignature() != file->pchSignature)
+  // Don't rely on the PCH signature to validate the concordance between the PCH
+  // and the OBJ that uses it. However we do validate here that the
+  // LF_ENDPRECOMP record index lines up with the number of type records
+  // LF_PRECOMP is expecting.
+  if (precomp->endPrecompIdx != pr.getTypesCount())
     return createFileError(
         toString(file),
         make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch));
 
-  if (pr.getSignature() != *precomp->file->pchSignature)
-    return createFileError(
-        toString(precomp->file),
-        make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch));
-
   return precomp;
 }
 
@@ -555,20 +552,28 @@ Error UsePrecompSource::mergeDebugT(TypeMerger *m) {
   return TpiSource::mergeDebugT(m);
 }
 
-uint32_t TpiSource::countTypeServerPDBs() {
-  return TypeServerSource::mappings.size();
-}
+Error PrecompSource::mergeDebugT(TypeMerger *m) {
+  // In some cases, the S_OBJNAME record doesn't contain the PCH signature.
+  // The signature comes later with the LF_ENDPRECOMP record, so we first need
+  // to merge in all the .PCH.OBJ file type records, before registering below.
+  if (Error e = TpiSource::mergeDebugT(m))
+    return e;
+
+  registerMapping();
 
-uint32_t TpiSource::countPrecompObjs() {
-  return PrecompSource::mappings.size();
+  return Error::success();
 }
 
-void TpiSource::clear() {
-  // Clean up any owned ghash allocations.
-  clearGHashes();
-  TpiSource::instances.clear();
-  TypeServerSource::mappings.clear();
-  PrecompSource::mappings.clear();
+void PrecompSource::registerMapping() {
+  if (registered)
+    return;
+  if (file->pchSignature && *file->pchSignature) {
+    auto it = ctx.precompSourceMappings.emplace(*file->pchSignature, this);
+    if (!it.second)
+      fatal("a PCH object with the same signature has already been provided (" +
+            toString(it.first->second->file) + " and " + toString(file) + ")");
+    registered = true;
+  }
 }
 
 //===----------------------------------------------------------------------===//
@@ -576,7 +581,7 @@ void TpiSource::clear() {
 //===----------------------------------------------------------------------===//
 
 void TpiSource::loadGHashes() {
-  if (Optional<ArrayRef<uint8_t>> debugH = getDebugH(file)) {
+  if (std::optional<ArrayRef<uint8_t>> debugH = getDebugH(file)) {
     ghashes = getHashesFromDebugH(*debugH);
     ownedGHashes = false;
   } else {
@@ -598,7 +603,7 @@ void TpiSource::assignGHashesFromVector(
     return;
   GloballyHashedType *hashes = new GloballyHashedType[hashVec.size()];
   memcpy(hashes, hashVec.data(), hashVec.size() * sizeof(GloballyHashedType));
-  ghashes = makeArrayRef(hashes, hashVec.size());
+  ghashes = ArrayRef(hashes, hashVec.size());
   ownedGHashes = true;
 }
 
@@ -637,7 +642,7 @@ void TpiSource::mergeTypeRecord(TypeIndex curIndex, CVType ty) {
   size_t offset = merged.recs.size();
   size_t newSize = alignTo(ty.length(), 4);
   merged.recs.resize(offset + newSize);
-  auto newRec = makeMutableArrayRef(&merged.recs[offset], newSize);
+  auto newRec = MutableArrayRef(&merged.recs[offset], newSize);
   memcpy(newRec.data(), ty.data().data(), newSize);
 
   // Fix up the record prefix and padding bytes if it required resizing.
@@ -677,7 +682,7 @@ void TpiSource::mergeUniqueTypeRecords(ArrayRef<uint8_t> typeRecords,
                                        TypeIndex beginIndex) {
   // Re-sort the list of unique types by index.
   if (kind == PDB)
-    assert(std::is_sorted(uniqueTypes.begin(), uniqueTypes.end()));
+    assert(llvm::is_sorted(uniqueTypes));
   else
     llvm::sort(uniqueTypes);
 
@@ -722,14 +727,14 @@ void TpiSource::mergeUniqueTypeRecords(ArrayRef<uint8_t> typeRecords,
 }
 
 void TpiSource::remapTpiWithGHashes(GHashState *g) {
-  assert(config->debugGHashes && "ghashes must be enabled");
+  assert(ctx.config.debugGHashes && "ghashes must be enabled");
   fillMapFromGHashes(g);
   tpiMap = indexMapStorage;
   ipiMap = indexMapStorage;
   mergeUniqueTypeRecords(file->debugTypes);
   // TODO: Free all unneeded ghash resources now that we have a full index map.
 
-  if (config->showSummary) {
+  if (ctx.config.showSummary) {
     nbTypeRecords = ghashes.size();
     nbTypeRecordsBytes = file->debugTypes.size();
   }
@@ -782,7 +787,7 @@ static ArrayRef<uint8_t> typeArrayToBytes(const CVTypeArray &types) {
 
 // Merge types from a type server PDB.
 void TypeServerSource::remapTpiWithGHashes(GHashState *g) {
-  assert(config->debugGHashes && "ghashes must be enabled");
+  assert(ctx.config.debugGHashes && "ghashes must be enabled");
 
   // IPI merging depends on TPI, so do TPI first, then do IPI.  No need to
   // propagate errors, those should've been handled during ghash loading.
@@ -800,13 +805,13 @@ void TypeServerSource::remapTpiWithGHashes(GHashState *g) {
     ipiSrc->ipiMap = ipiMap;
     ipiSrc->mergeUniqueTypeRecords(typeArrayToBytes(ipi.typeArray()));
 
-    if (config->showSummary) {
+    if (ctx.config.showSummary) {
       nbTypeRecords = ipiSrc->ghashes.size();
       nbTypeRecordsBytes = ipi.typeArray().getUnderlyingStream().getLength();
     }
   }
 
-  if (config->showSummary) {
+  if (ctx.config.showSummary) {
     nbTypeRecords += ghashes.size();
     nbTypeRecordsBytes += tpi.typeArray().getUnderlyingStream().getLength();
   }
@@ -838,8 +843,14 @@ void PrecompSource::loadGHashes() {
     // Remember the index of the LF_ENDPRECOMP record so it can be excluded from
     // the PDB. There must be an entry in the list of ghashes so that the type
     // indexes of the following records in the /Yc PCH object line up.
-    if (ty.kind() == LF_ENDPRECOMP)
-      endPrecompGHashIdx = ghashIdx;
+    if (ty.kind() == LF_ENDPRECOMP) {
+      EndPrecompRecord endPrecomp;
+      cantFail(TypeDeserializer::deserializeAs<EndPrecompRecord>(
+          const_cast<CVType &>(ty), endPrecomp));
+      file->pchSignature = endPrecomp.getSignature();
+      registerMapping();
+      endPrecompIdx = ghashIdx;
+    }
 
     hashVec.push_back(GloballyHashedType::hashType(ty, hashVec, hashVec));
     isItemIndex.push_back(isIdRecord(ty.kind()));
@@ -849,14 +860,17 @@ void PrecompSource::loadGHashes() {
 }
 
 void UsePrecompSource::loadGHashes() {
-  PrecompSource *pchSrc = findPrecompSource(file, precompDependency);
-  if (!pchSrc)
+  auto e = findPrecompMap(file, precompDependency);
+  if (!e) {
+    warn(toString(e.takeError()));
     return;
+  }
+
+  PrecompSource *pchSrc = *e;
 
-  // To compute ghashes of a /Yu object file, we need to build on the the
-  // ghashes of the /Yc PCH object. After we are done hashing, discard the
-  // ghashes from the PCH source so we don't unnecessarily try to deduplicate
-  // them.
+  // To compute ghashes of a /Yu object file, we need to build on the ghashes of
+  // the /Yc PCH object. After we are done hashing, discard the ghashes from the
+  // PCH source so we don't unnecessarily try to deduplicate them.
   std::vector<GloballyHashedType> hashVec =
       pchSrc->ghashes.take_front(precompDependency.getTypesCount());
   forEachTypeChecked(file->debugTypes, [&](const CVType &ty) {
@@ -884,7 +898,7 @@ void UsePrecompSource::remapTpiWithGHashes(GHashState *g) {
   mergeUniqueTypeRecords(file->debugTypes,
                          TypeIndex(precompDependency.getStartTypeIndex() +
                                    precompDependency.getTypesCount()));
-  if (config->showSummary) {
+  if (ctx.config.showSummary) {
     nbTypeRecords = ghashes.size();
     nbTypeRecordsBytes = file->debugTypes.size();
   }
@@ -926,12 +940,17 @@ struct GHashTable {
   /// Insert the cell with the given ghash into the table. Return the insertion
   /// position in the table. It is safe for the caller to store the insertion
   /// position because the table cannot be resized.
-  uint32_t insert(GloballyHashedType ghash, GHashCell newCell);
+  uint32_t insert(COFFLinkerContext &ctx, GloballyHashedType ghash,
+                  GHashCell newCell);
 };
 
 /// A ghash table cell for deduplicating types from TpiSources.
 class GHashCell {
-  uint64_t data = 0;
+  // Force "data" to be 64-bit aligned; otherwise, some versions of clang
+  // will generate calls to libatomic when using some versions of libstdc++
+  // on 32-bit targets.  (Also, in theory, there could be a target where
+  // new[] doesn't always return an 8-byte-aligned allocation.)
+  alignas(sizeof(uint64_t)) uint64_t data = 0;
 
 public:
   GHashCell() = default;
@@ -965,8 +984,8 @@ public:
   bool isItem() const { return data & (1ULL << 63U); }
 
   /// Get the ghash key for this cell.
-  GloballyHashedType getGHash() const {
-    return TpiSource::instances[getTpiSrcIdx()]->ghashes[getGHashIdx()];
+  GloballyHashedType getGHash(const COFFLinkerContext &ctx) const {
+    return ctx.tpiSourceList[getTpiSrcIdx()]->ghashes[getGHashIdx()];
   }
 
   /// The priority function for the cell. The data is stored such that lower
@@ -978,15 +997,13 @@ public:
 };
 } // namespace
 
-namespace lld {
-namespace coff {
+namespace lld::coff {
 /// This type is just a wrapper around GHashTable with external linkage so it
 /// can be used from a header.
 struct GHashState {
   GHashTable table;
 };
-} // namespace coff
-} // namespace lld
+} // namespace lld::coff
 
 GHashTable::~GHashTable() { delete[] table; }
 
@@ -996,7 +1013,8 @@ void GHashTable::init(uint32_t newTableSize) {
   tableSize = newTableSize;
 }
 
-uint32_t GHashTable::insert(GloballyHashedType ghash, GHashCell newCell) {
+uint32_t GHashTable::insert(COFFLinkerContext &ctx, GloballyHashedType ghash,
+                            GHashCell newCell) {
   assert(!newCell.isEmpty() && "cannot insert empty cell value");
 
   // FIXME: The low bytes of SHA1 have low entropy for short records, which
@@ -1015,7 +1033,7 @@ uint32_t GHashTable::insert(GloballyHashedType ghash, GHashCell newCell) {
     // - cell has non-matching key: hash collision, probe next cell
     auto *cellPtr = reinterpret_cast<std::atomic<GHashCell> *>(&table[idx]);
     GHashCell oldCell(cellPtr->load());
-    while (oldCell.isEmpty() || oldCell.getGHash() == ghash) {
+    while (oldCell.isEmpty() || oldCell.getGHash(ctx) == ghash) {
       // Check if there is an existing ghash entry with a higher priority
       // (earlier ordering). If so, this is a duplicate, we are done.
       if (!oldCell.isEmpty() && oldCell < newCell)
@@ -1040,22 +1058,22 @@ uint32_t GHashTable::insert(GloballyHashedType ghash, GHashCell newCell) {
   llvm_unreachable("left infloop");
 }
 
-TypeMerger::TypeMerger(llvm::BumpPtrAllocator &alloc)
-    : typeTable(alloc), idTable(alloc) {}
+TypeMerger::TypeMerger(COFFLinkerContext &c, llvm::BumpPtrAllocator &alloc)
+    : typeTable(alloc), idTable(alloc), ctx(c) {}
 
 TypeMerger::~TypeMerger() = default;
 
 void TypeMerger::mergeTypesWithGHash() {
   // Load ghashes. Do type servers and PCH objects first.
   {
-    ScopedTimer t1(loadGHashTimer);
-    parallelForEach(TpiSource::dependencySources,
+    ScopedTimer t1(ctx.loadGHashTimer);
+    parallelForEach(dependencySources,
                     [&](TpiSource *source) { source->loadGHashes(); });
-    parallelForEach(TpiSource::objectSources,
+    parallelForEach(objectSources,
                     [&](TpiSource *source) { source->loadGHashes(); });
   }
 
-  ScopedTimer t2(mergeGHashTimer);
+  ScopedTimer t2(ctx.mergeGHashTimer);
   GHashState ghashState;
 
   // Estimate the size of hash table needed to deduplicate ghashes. This *must*
@@ -1066,7 +1084,7 @@ void TypeMerger::mergeTypesWithGHash() {
   // small compared to total memory usage, at eight bytes per input type record,
   // and most input type records are larger than eight bytes.
   size_t tableSize = 0;
-  for (TpiSource *source : TpiSource::instances)
+  for (TpiSource *source : ctx.tpiSourceList)
     tableSize += source->ghashes.size();
 
   // Cap the table size so that we can use 32-bit cell indices. Type indices are
@@ -1080,8 +1098,8 @@ void TypeMerger::mergeTypesWithGHash() {
   // position. Because the table does not rehash, the position will not change
   // under insertion. After insertion is done, the value of the cell can be read
   // to retrieve the final PDB type index.
-  parallelForEachN(0, TpiSource::instances.size(), [&](size_t tpiSrcIdx) {
-    TpiSource *source = TpiSource::instances[tpiSrcIdx];
+  parallelFor(0, ctx.tpiSourceList.size(), [&](size_t tpiSrcIdx) {
+    TpiSource *source = ctx.tpiSourceList[tpiSrcIdx];
     source->indexMapStorage.resize(source->ghashes.size());
     for (uint32_t i = 0, e = source->ghashes.size(); i < e; i++) {
       if (source->shouldOmitFromPdb(i)) {
@@ -1091,7 +1109,7 @@ void TypeMerger::mergeTypesWithGHash() {
       GloballyHashedType ghash = source->ghashes[i];
       bool isItem = source->isItemIndex.test(i);
       uint32_t cellIdx =
-          ghashState.table.insert(ghash, GHashCell(isItem, tpiSrcIdx, i));
+          ghashState.table.insert(ctx, ghash, GHashCell(isItem, tpiSrcIdx, i));
 
       // Store the ghash cell index as a type index in indexMapStorage. Later
       // we will replace it with the PDB type index.
@@ -1109,8 +1127,7 @@ void TypeMerger::mergeTypesWithGHash() {
   //   - source 0, type 1...
   //   - source 1, type 0...
   std::vector<GHashCell> entries;
-  for (const GHashCell &cell :
-       makeArrayRef(ghashState.table.table, tableSize)) {
+  for (const GHashCell &cell : ArrayRef(ghashState.table.table, tableSize)) {
     if (!cell.isEmpty())
       entries.push_back(cell);
   }
@@ -1120,8 +1137,7 @@ void TypeMerger::mergeTypesWithGHash() {
               entries.size(), tableSize));
 
   // Find out how many type and item indices there are.
-  auto mid =
-      std::lower_bound(entries.begin(), entries.end(), GHashCell(true, 0, 0));
+  auto mid = llvm::lower_bound(entries, GHashCell(true, 0, 0));
   assert((mid == entries.end() || mid->isItem()) &&
          (mid == entries.begin() || !std::prev(mid)->isItem()) &&
          "midpoint is not midpoint");
@@ -1137,7 +1153,7 @@ void TypeMerger::mergeTypesWithGHash() {
   for (uint32_t i = 0, e = entries.size(); i < e; ++i) {
     auto &cell = entries[i];
     uint32_t tpiSrcIdx = cell.getTpiSrcIdx();
-    TpiSource *source = TpiSource::instances[tpiSrcIdx];
+    TpiSource *source = ctx.tpiSourceList[tpiSrcIdx];
     source->uniqueTypes.push_back(cell.getGHashIdx());
 
     // Update the ghash table to store the destination PDB type index in the
@@ -1150,21 +1166,36 @@ void TypeMerger::mergeTypesWithGHash() {
   }
 
   // In parallel, remap all types.
-  for_each(TpiSource::dependencySources, [&](TpiSource *source) {
+  for (TpiSource *source : dependencySources)
     source->remapTpiWithGHashes(&ghashState);
-  });
-  parallelForEach(TpiSource::objectSources, [&](TpiSource *source) {
+  parallelForEach(objectSources, [&](TpiSource *source) {
     source->remapTpiWithGHashes(&ghashState);
   });
 
   // Build a global map of from function ID to function type.
-  for (TpiSource *source : TpiSource::instances) {
+  for (TpiSource *source : ctx.tpiSourceList) {
     for (auto idToType : source->funcIdToType)
       funcIdToType.insert(idToType);
     source->funcIdToType.clear();
   }
 
-  TpiSource::clearGHashes();
+  clearGHashes();
+}
+
+void TypeMerger::sortDependencies() {
+  // Order dependencies first, but preserve the existing order.
+  std::vector<TpiSource *> deps;
+  std::vector<TpiSource *> objs;
+  for (TpiSource *s : ctx.tpiSourceList)
+    (s->isDependency() ? deps : objs).push_back(s);
+  uint32_t numDeps = deps.size();
+  uint32_t numObjs = objs.size();
+  ctx.tpiSourceList = std::move(deps);
+  ctx.tpiSourceList.insert(ctx.tpiSourceList.end(), objs.begin(), objs.end());
+  for (uint32_t i = 0, e = ctx.tpiSourceList.size(); i < e; ++i)
+    ctx.tpiSourceList[i]->tpiSrcIdx = i;
+  dependencySources = ArrayRef(ctx.tpiSourceList.data(), numDeps);
+  objectSources = ArrayRef(ctx.tpiSourceList.data() + numDeps, numObjs);
 }
 
 /// Given the index into the ghash table for a particular type, return the type
@@ -1175,6 +1206,17 @@ static TypeIndex loadPdbTypeIndexFromCell(GHashState *g,
   return TypeIndex::fromArrayIndex(cell.getGHashIdx());
 }
 
+/// Free heap allocated ghashes.
+void TypeMerger::clearGHashes() {
+  for (TpiSource *src : ctx.tpiSourceList) {
+    if (src->ownedGHashes)
+      delete[] src->ghashes.data();
+    src->ghashes = {};
+    src->isItemIndex.clear();
+    src->uniqueTypes.clear();
+  }
+}
+
 // Fill in a TPI or IPI index map using ghashes. For each source type, use its
 // ghash to lookup its final type index in the PDB, and store that in the map.
 void TpiSource::fillMapFromGHashes(GHashState *g) {
@@ -1187,13 +1229,3 @@ void TpiSource::fillMapFromGHashes(GHashState *g) {
           loadPdbTypeIndexFromCell(g, fakeCellIndex.toArrayIndex());
   }
 }
-
-void TpiSource::clearGHashes() {
-  for (TpiSource *src : TpiSource::instances) {
-    if (src->ownedGHashes)
-      delete[] src->ghashes.data();
-    src->ghashes = {};
-    src->isItemIndex.clear();
-    src->uniqueTypes.clear();
-  }
-}
index faad30b..a7d4776 100644 (file)
 #include "llvm/Support/Error.h"
 #include "llvm/Support/MemoryBuffer.h"
 
-namespace llvm {
-namespace codeview {
+namespace llvm::codeview {
 struct GloballyHashedType;
-} // namespace codeview
-namespace pdb {
+}
+namespace llvm::pdb {
 class NativeSession;
 class TpiStream;
 }
-} // namespace llvm
 
-namespace lld {
-namespace coff {
+namespace lld::coff {
 
 using llvm::codeview::GloballyHashedType;
 using llvm::codeview::TypeIndex;
@@ -37,12 +34,13 @@ class ObjFile;
 class PDBInputFile;
 class TypeMerger;
 struct GHashState;
+class COFFLinkerContext;
 
 class TpiSource {
 public:
   enum TpiKind : uint8_t { Regular, PCH, UsingPCH, PDB, PDBIpi, UsingPDB };
 
-  TpiSource(TpiKind k, ObjFile *f);
+  TpiSource(COFFLinkerContext &ctx, TpiKind k, ObjFile *f);
   virtual ~TpiSource();
 
   /// Produce a mapping from the type and item indices used in the object
@@ -93,6 +91,8 @@ protected:
   // Walk over file->debugTypes and fill in the isItemIndex bit vector.
   void fillIsItemIndexFromDebugT();
 
+  COFFLinkerContext &ctx;
+
 public:
   bool remapTypesInSymbolRecord(MutableArrayRef<uint8_t> rec);
 
@@ -106,41 +106,18 @@ public:
   /// it is unique. This prevents a record from being added to the input ghash
   /// table.
   bool shouldOmitFromPdb(uint32_t ghashIdx) {
-    return ghashIdx == endPrecompGHashIdx;
+    return ghashIdx == endPrecompIdx;
   }
 
-  /// All sources of type information in the program.
-  static std::vector<TpiSource *> instances;
-
-  /// Dependency type sources, such as type servers or PCH object files. These
-  /// must be processed before objects that rely on them. Set by
-  /// TpiSources::sortDependencies.
-  static ArrayRef<TpiSource *> dependencySources;
-
-  /// Object file sources. These must be processed after dependencySources.
-  static ArrayRef<TpiSource *> objectSources;
-
-  /// Sorts the dependencies and reassigns TpiSource indices.
-  static void sortDependencies();
-
-  static uint32_t countTypeServerPDBs();
-  static uint32_t countPrecompObjs();
-
-  /// Free heap allocated ghashes.
-  static void clearGHashes();
-
-  /// Clear global data structures for TpiSources.
-  static void clear();
-
   const TpiKind kind;
   bool ownedGHashes = true;
   uint32_t tpiSrcIdx = 0;
 
-protected:
-  /// The ghash index (zero based, not 0x1000-based) of the LF_ENDPRECOMP record
-  /// in this object, if one exists. This is the all ones value otherwise. It is
-  /// recorded here so that it can be omitted from the final ghash table.
-  uint32_t endPrecompGHashIdx = ~0U;
+  /// The index (zero based, not 0x1000-based) of the LF_ENDPRECOMP record in
+  /// this object, if one exists. This is the all ones value otherwise. It is
+  /// recorded here for validation, and so that it can be omitted from the final
+  /// ghash table.
+  uint32_t endPrecompIdx = ~0U;
 
 public:
   ObjFile *file;
@@ -186,15 +163,15 @@ public:
   uint64_t nbTypeRecordsBytes = 0;
 };
 
-TpiSource *makeTpiSource(ObjFile *file);
-TpiSource *makeTypeServerSource(PDBInputFile *pdbInputFile);
-TpiSource *makeUseTypeServerSource(ObjFile *file,
+TpiSource *makeTpiSource(COFFLinkerContext &ctx, ObjFile *f);
+TpiSource *makeTypeServerSource(COFFLinkerContext &ctx,
+                                PDBInputFile *pdbInputFile);
+TpiSource *makeUseTypeServerSource(COFFLinkerContext &ctx, ObjFile *file,
                                    llvm::codeview::TypeServer2Record ts);
-TpiSource *makePrecompSource(ObjFile *file);
-TpiSource *makeUsePrecompSource(ObjFile *file,
+TpiSource *makePrecompSource(COFFLinkerContext &ctx, ObjFile *file);
+TpiSource *makeUsePrecompSource(COFFLinkerContext &ctx, ObjFile *file,
                                 llvm::codeview::PrecompRecord ts);
 
-} // namespace coff
-} // namespace lld
+} // namespace lld::coff
 
 #endif
index 9ba0db3..d6b71ea 100644 (file)
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "Driver.h"
+#include "COFFLinkerContext.h"
 #include "Config.h"
 #include "DebugTypes.h"
 #include "ICF.h"
 #include "Symbols.h"
 #include "Writer.h"
 #include "lld/Common/Args.h"
+#include "lld/Common/CommonLinkerContext.h"
 #include "lld/Common/Driver.h"
-#include "lld/Common/ErrorHandler.h"
 #include "lld/Common/Filesystem.h"
-#include "lld/Common/Memory.h"
 #include "lld/Common/Timer.h"
 #include "lld/Common/Version.h"
-#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
 #include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/Triple.h"
 #include "llvm/BinaryFormat/Magic.h"
 #include "llvm/Config/llvm-config.h"
 #include "llvm/LTO/LTO.h"
 #include "llvm/Support/Process.h"
 #include "llvm/Support/TarWriter.h"
 #include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/VirtualFileSystem.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/ToolDrivers/llvm-lib/LibDriver.h"
 #include <algorithm>
 #include <future>
 #include <memory>
+#include <optional>
 
 using namespace llvm;
 using namespace llvm::object;
 using namespace llvm::COFF;
 using namespace llvm::sys;
 
-namespace lld {
-namespace coff {
+namespace lld::coff {
 
-static Timer inputFileTimer("Input File Reading", Timer::root());
+bool link(ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,
+          llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput) {
+  // This driver-specific context will be freed later by lldMain().
+  auto *ctx = new COFFLinkerContext;
 
-Configuration *config;
-LinkerDriver *driver;
+  ctx->e.initialize(stdoutOS, stderrOS, exitEarly, disableOutput);
+  ctx->e.logName = args::getFilenameWithoutExe(args[0]);
+  ctx->e.errorLimitExceededMsg = "too many errors emitted, stopping now"
+                                 " (use /errorlimit:0 to see all errors)";
 
-bool link(ArrayRef<const char *> args, bool canExitEarly, raw_ostream &stdoutOS,
-          raw_ostream &stderrOS) {
-  lld::stdoutOS = &stdoutOS;
-  lld::stderrOS = &stderrOS;
+  ctx->driver.linkerMain(args);
 
-  errorHandler().cleanupCallback = []() {
-    TpiSource::clear();
-    freeArena();
-    ObjFile::instances.clear();
-    PDBInputFile::instances.clear();
-    ImportFile::instances.clear();
-    BitcodeFile::instances.clear();
-    memset(MergeChunk::instances, 0, sizeof(MergeChunk::instances));
-    OutputSection::clear();
-  };
-
-  errorHandler().logName = args::getFilenameWithoutExe(args[0]);
-  errorHandler().errorLimitExceededMsg =
-      "too many errors emitted, stopping now"
-      " (use /errorlimit:0 to see all errors)";
-  errorHandler().exitEarly = canExitEarly;
-  stderrOS.enable_colors(stderrOS.has_colors());
-
-  config = make<Configuration>();
-  symtab = make<SymbolTable>();
-  driver = make<LinkerDriver>();
-
-  driver->linkerMain(args);
-
-  // Call exit() if we can to avoid calling destructors.
-  if (canExitEarly)
-    exitLld(errorCount() ? 1 : 0);
-
-  bool ret = errorCount() == 0;
-  if (!canExitEarly)
-    errorHandler().reset();
-  return ret;
+  return errorCount() == 0;
 }
 
 // Parse options of the form "old;new".
@@ -119,11 +92,11 @@ static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args,
 
 // Drop directory components and replace extension with
 // ".exe", ".dll" or ".sys".
-static std::string getOutputPath(StringRef path) {
+static std::string getOutputPath(StringRef path, bool isDll, bool isDriver) {
   StringRef ext = ".exe";
-  if (config->dll)
+  if (isDll)
     ext = ".dll";
-  else if (config->driver)
+  else if (isDriver)
     ext = ".sys";
 
   return (sys::path::stem(path) + ext).str();
@@ -167,15 +140,30 @@ 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);
+StringRef LinkerDriver::mangle(StringRef sym) {
+  assert(ctx.config.machine != IMAGE_FILE_MACHINE_UNKNOWN);
+  if (ctx.config.machine == I386)
+    return saver().save("_" + sym);
   return sym;
 }
 
-static bool findUnderscoreMangle(StringRef sym) {
-  Symbol *s = symtab->findMangle(mangle(sym));
+llvm::Triple::ArchType LinkerDriver::getArch() {
+  switch (ctx.config.machine) {
+  case I386:
+    return llvm::Triple::ArchType::x86;
+  case AMD64:
+    return llvm::Triple::ArchType::x86_64;
+  case ARMNT:
+    return llvm::Triple::ArchType::arm;
+  case ARM64:
+    return llvm::Triple::ArchType::aarch64;
+  default:
+    return llvm::Triple::ArchType::UnknownArch;
+  }
+}
+
+bool LinkerDriver::findUnderscoreMangle(StringRef sym) {
+  Symbol *s = ctx.symtab.findMangle(mangle(sym));
   return s && !isa<Undefined>(s);
 }
 
@@ -183,9 +171,9 @@ MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> mb) {
   MemoryBufferRef mbref = *mb;
   make<std::unique_ptr<MemoryBuffer>>(std::move(mb)); // take ownership
 
-  if (driver->tar)
-    driver->tar->append(relativeToRoot(mbref.getBufferIdentifier()),
-                        mbref.getBuffer());
+  if (ctx.driver.tar)
+    ctx.driver.tar->append(relativeToRoot(mbref.getBufferIdentifier()),
+                           mbref.getBuffer());
   return mbref;
 }
 
@@ -213,30 +201,24 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
         addArchiveBuffer(m, "<whole-archive>", filename, memberIndex++);
       return;
     }
-    symtab->addFile(make<ArchiveFile>(mbref));
+    ctx.symtab.addFile(make<ArchiveFile>(ctx, mbref));
     break;
   case file_magic::bitcode:
-    if (lazy)
-      symtab->addFile(make<LazyObjFile>(mbref));
-    else
-      symtab->addFile(make<BitcodeFile>(mbref, "", 0));
+    ctx.symtab.addFile(make<BitcodeFile>(ctx, mbref, "", 0, lazy));
     break;
   case file_magic::coff_object:
   case file_magic::coff_import_library:
-    if (lazy)
-      symtab->addFile(make<LazyObjFile>(mbref));
-    else
-      symtab->addFile(make<ObjFile>(mbref));
+    ctx.symtab.addFile(make<ObjFile>(ctx, mbref, lazy));
     break;
   case file_magic::pdb:
-    symtab->addFile(make<PDBInputFile>(mbref));
+    ctx.symtab.addFile(make<PDBInputFile>(ctx, mbref));
     break;
   case file_magic::coff_cl_gl_object:
     error(filename + ": is not a native COFF file. Recompile without /GL");
     break;
   case file_magic::pecoff_executable:
-    if (config->mingw) {
-      symtab->addFile(make<DLLFile>(mbref));
+    if (ctx.config.mingw) {
+      ctx.symtab.addFile(make<DLLFile>(ctx, mbref));
       break;
     }
     if (filename.endswith_insensitive(".dll")) {
@@ -244,7 +226,7 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
                        "import library?");
       break;
     }
-    LLVM_FALLTHROUGH;
+    [[fallthrough]];
   default:
     error(mbref.getBufferIdentifier() + ": unknown file type");
     break;
@@ -266,12 +248,12 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) {
       // the option `/nodefaultlib` than a reference to a file in the root
       // directory.
       std::string nearest;
-      if (optTable.findNearest(pathStr, nearest) > 1)
+      if (ctx.optTable.findNearest(pathStr, nearest) > 1)
         error(msg);
       else
         error(msg + "; did you mean '" + nearest + "'");
     } else
-      driver->addBuffer(std::move(mbOrErr.first), wholeArchive, lazy);
+      ctx.driver.addBuffer(std::move(mbOrErr.first), wholeArchive, lazy);
   });
 }
 
@@ -280,24 +262,29 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName,
                                     uint64_t offsetInArchive) {
   file_magic magic = identify_magic(mb.getBuffer());
   if (magic == file_magic::coff_import_library) {
-    InputFile *imp = make<ImportFile>(mb);
+    InputFile *imp = make<ImportFile>(ctx, mb);
     imp->parentName = parentName;
-    symtab->addFile(imp);
+    ctx.symtab.addFile(imp);
     return;
   }
 
   InputFile *obj;
   if (magic == file_magic::coff_object) {
-    obj = make<ObjFile>(mb);
+    obj = make<ObjFile>(ctx, mb);
   } else if (magic == file_magic::bitcode) {
-    obj = make<BitcodeFile>(mb, parentName, offsetInArchive);
+    obj =
+        make<BitcodeFile>(ctx, mb, parentName, offsetInArchive, /*lazy=*/false);
+  } else if (magic == file_magic::coff_cl_gl_object) {
+    error(mb.getBufferIdentifier() +
+          ": is not a native COFF file. Recompile without /GL?");
+    return;
   } else {
     error("unknown file type: " + mb.getBufferIdentifier());
     return;
   }
 
   obj->parentName = parentName;
-  symtab->addFile(obj);
+  ctx.symtab.addFile(obj);
   log("Loaded " + toString(obj) + " for " + symName);
 }
 
@@ -307,8 +294,8 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
 
   auto reportBufferError = [=](Error &&e, StringRef childName) {
     fatal("could not get the buffer for the member defining symbol " +
-          toCOFFString(sym) + ": " + parentName + "(" + childName + "): " +
-          toString(std::move(e)));
+          toCOFFString(ctx, sym) + ": " + parentName + "(" + childName +
+          "): " + toString(std::move(e)));
   };
 
   if (!c.getParent()->isThin()) {
@@ -318,16 +305,16 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
       reportBufferError(mbOrErr.takeError(), check(c.getFullName()));
     MemoryBufferRef mb = mbOrErr.get();
     enqueueTask([=]() {
-      driver->addArchiveBuffer(mb, toCOFFString(sym), parentName,
-                               offsetInArchive);
+      ctx.driver.addArchiveBuffer(mb, toCOFFString(ctx, sym), parentName,
+                                  offsetInArchive);
     });
     return;
   }
 
-  std::string childName = CHECK(
-      c.getFullName(),
-      "could not get the filename for the member defining symbol " +
-      toCOFFString(sym));
+  std::string childName =
+      CHECK(c.getFullName(),
+            "could not get the filename for the member defining symbol " +
+                toCOFFString(ctx, sym));
   auto future = std::make_shared<std::future<MBErrPair>>(
       createFutureForFile(childName));
   enqueueTask([=]() {
@@ -336,14 +323,15 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
       reportBufferError(errorCodeToError(mbOrErr.second), childName);
     // Pass empty string as archive name so that the original filename is
     // used as the buffer identifier.
-    driver->addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)),
-                             toCOFFString(sym), "", /*OffsetInArchive=*/0);
+    ctx.driver.addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)),
+                                toCOFFString(ctx, sym), "",
+                                /*OffsetInArchive=*/0);
   });
 }
 
-static bool isDecorated(StringRef sym) {
+bool LinkerDriver::isDecorated(StringRef sym) {
   return sym.startswith("@") || sym.contains("@@") || sym.startswith("?") ||
-         (!config->mingw && sym.contains('@'));
+         (!ctx.config.mingw && sym.contains('@'));
 }
 
 // Parses .drectve section contents and returns a list of files
@@ -355,7 +343,7 @@ void LinkerDriver::parseDirectives(InputFile *file) {
 
   log("Directives: " + toString(file) + ": " + s);
 
-  ArgParser parser;
+  ArgParser parser(ctx);
   // .drectve is always tokenized using Windows shell rules.
   // /EXPORT: option can appear too many times, processing in fastpath.
   ParsedDirectives directives = parser.parseDirectives(s);
@@ -369,20 +357,29 @@ void LinkerDriver::parseDirectives(InputFile *file) {
       continue;
 
     Export exp = parseExport(e);
-    if (config->machine == I386 && config->mingw) {
+    if (ctx.config.machine == I386 && ctx.config.mingw) {
       if (!isDecorated(exp.name))
-        exp.name = saver.save("_" + exp.name);
+        exp.name = saver().save("_" + exp.name);
       if (!exp.extName.empty() && !isDecorated(exp.extName))
-        exp.extName = saver.save("_" + exp.extName);
+        exp.extName = saver().save("_" + exp.extName);
     }
     exp.directives = true;
-    config->exports.push_back(exp);
+    ctx.config.exports.push_back(exp);
   }
 
   // Handle /include: in bulk.
   for (StringRef inc : directives.includes)
     addUndefined(inc);
 
+  // Handle /exclude-symbols: in bulk.
+  for (StringRef e : directives.excludes) {
+    SmallVector<StringRef, 2> vec;
+    e.split(vec, ',');
+    for (StringRef sym : vec)
+      excludedSymbols.insert(mangle(sym));
+  }
+
+  // https://docs.microsoft.com/en-us/cpp/preprocessor/comment-c-cpp?view=msvc-160
   for (auto *arg : directives.args) {
     switch (arg->getOption().getID()) {
     case OPT_aligncomm:
@@ -392,11 +389,11 @@ void LinkerDriver::parseDirectives(InputFile *file) {
       parseAlternateName(arg->getValue());
       break;
     case OPT_defaultlib:
-      if (Optional<StringRef> path = findLib(arg->getValue()))
+      if (std::optional<StringRef> path = findLib(arg->getValue()))
         enqueuePath(*path, false, false);
       break;
     case OPT_entry:
-      config->entry = addUndefined(mangle(arg->getValue()));
+      ctx.config.entry = addUndefined(mangle(arg->getValue()));
       break;
     case OPT_failifmismatch:
       checkFailIfMismatch(arg->getValue(), file);
@@ -404,27 +401,33 @@ void LinkerDriver::parseDirectives(InputFile *file) {
     case OPT_incl:
       addUndefined(arg->getValue());
       break;
+    case OPT_manifestdependency:
+      ctx.config.manifestDependencies.insert(arg->getValue());
+      break;
     case OPT_merge:
       parseMerge(arg->getValue());
       break;
     case OPT_nodefaultlib:
-      config->noDefaultLibs.insert(doFindLib(arg->getValue()).lower());
+      ctx.config.noDefaultLibs.insert(doFindLib(arg->getValue()).lower());
+      break;
+    case OPT_release:
+      ctx.config.writeCheckSum = true;
       break;
     case OPT_section:
       parseSection(arg->getValue());
       break;
     case OPT_stack:
-      parseNumbers(arg->getValue(), &config->stackReserve,
-                   &config->stackCommit);
+      parseNumbers(arg->getValue(), &ctx.config.stackReserve,
+                   &ctx.config.stackCommit);
       break;
     case OPT_subsystem: {
       bool gotVersion = false;
-      parseSubsystem(arg->getValue(), &config->subsystem,
-                     &config->majorSubsystemVersion,
-                     &config->minorSubsystemVersion, &gotVersion);
+      parseSubsystem(arg->getValue(), &ctx.config.subsystem,
+                     &ctx.config.majorSubsystemVersion,
+                     &ctx.config.minorSubsystemVersion, &gotVersion);
       if (gotVersion) {
-        config->majorOSVersion = config->majorSubsystemVersion;
-        config->minorOSVersion = config->minorSubsystemVersion;
+        ctx.config.majorOSVersion = ctx.config.majorSubsystemVersion;
+        ctx.config.minorOSVersion = ctx.config.minorSubsystemVersion;
       }
       break;
     }
@@ -433,9 +436,12 @@ void LinkerDriver::parseDirectives(InputFile *file) {
     case OPT_editandcontinue:
     case OPT_guardsym:
     case OPT_throwingnew:
+    case OPT_inferasanlibs:
+    case OPT_inferasanlibs_no:
       break;
     default:
-      error(arg->getSpelling() + " is not allowed in .drectve");
+      error(arg->getSpelling() + " is not allowed in .drectve (" +
+            toString(file) + ")");
     }
   }
 }
@@ -443,44 +449,53 @@ void LinkerDriver::parseDirectives(InputFile *file) {
 // Find file from search paths. You can omit ".obj", this function takes
 // care of that. Note that the returned path is not guaranteed to exist.
 StringRef LinkerDriver::doFindFile(StringRef filename) {
+  auto getFilename = [this](StringRef filename) -> StringRef {
+    if (ctx.config.vfs)
+      if (auto statOrErr = ctx.config.vfs->status(filename))
+        return saver().save(statOrErr->getName());
+    return filename;
+  };
+
   bool hasPathSep = (filename.find_first_of("/\\") != StringRef::npos);
   if (hasPathSep)
-    return filename;
+    return getFilename(filename);
   bool hasExt = filename.contains('.');
   for (StringRef dir : searchPaths) {
     SmallString<128> path = dir;
     sys::path::append(path, filename);
+    path = SmallString<128>{getFilename(path.str())};
     if (sys::fs::exists(path.str()))
-      return saver.save(path.str());
+      return saver().save(path.str());
     if (!hasExt) {
       path.append(".obj");
+      path = SmallString<128>{getFilename(path.str())};
       if (sys::fs::exists(path.str()))
-        return saver.save(path.str());
+        return saver().save(path.str());
     }
   }
   return filename;
 }
 
-static Optional<sys::fs::UniqueID> getUniqueID(StringRef path) {
+static std::optional<sys::fs::UniqueID> getUniqueID(StringRef path) {
   sys::fs::UniqueID ret;
   if (sys::fs::getUniqueID(path, ret))
-    return None;
+    return std::nullopt;
   return ret;
 }
 
 // Resolves a file path. This never returns the same path
-// (in that case, it returns None).
-Optional<StringRef> LinkerDriver::findFile(StringRef filename) {
+// (in that case, it returns std::nullopt).
+std::optional<StringRef> LinkerDriver::findFile(StringRef filename) {
   StringRef path = doFindFile(filename);
 
-  if (Optional<sys::fs::UniqueID> id = getUniqueID(path)) {
+  if (std::optional<sys::fs::UniqueID> id = getUniqueID(path)) {
     bool seen = !visitedFiles.insert(*id).second;
     if (seen)
-      return None;
+      return std::nullopt;
   }
 
   if (path.endswith_insensitive(".lib"))
-    visitedLibs.insert(std::string(sys::path::filename(path)));
+    visitedLibs.insert(std::string(sys::path::filename(path).lower()));
   return path;
 }
 
@@ -492,7 +507,7 @@ StringRef LinkerDriver::doFindLibMinGW(StringRef filename) {
 
   SmallString<128> s = filename;
   sys::path::replace_extension(s, ".a");
-  StringRef libName = saver.save("lib" + s.str());
+  StringRef libName = saver().save("lib" + s.str());
   return doFindFile(libName);
 }
 
@@ -501,40 +516,135 @@ 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");
+    filename = saver().save(filename + ".lib");
   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)
+  if (ctx.config.mingw && ret == filename)
     return doFindLibMinGW(filename);
   return ret;
 }
 
 // Resolves a library path. /nodefaultlib options are taken into
 // consideration. This never returns the same path (in that case,
-// it returns None).
-Optional<StringRef> LinkerDriver::findLib(StringRef filename) {
-  if (config->noDefaultLibAll)
-    return None;
+// it returns std::nullopt).
+std::optional<StringRef> LinkerDriver::findLib(StringRef filename) {
+  if (ctx.config.noDefaultLibAll)
+    return std::nullopt;
   if (!visitedLibs.insert(filename.lower()).second)
-    return None;
+    return std::nullopt;
 
   StringRef path = doFindLib(filename);
-  if (config->noDefaultLibs.count(path.lower()))
-    return None;
+  if (ctx.config.noDefaultLibs.count(path.lower()))
+    return std::nullopt;
 
-  if (Optional<sys::fs::UniqueID> id = getUniqueID(path))
+  if (std::optional<sys::fs::UniqueID> id = getUniqueID(path))
     if (!visitedFiles.insert(*id).second)
-      return None;
+      return std::nullopt;
   return path;
 }
 
+void LinkerDriver::detectWinSysRoot(const opt::InputArgList &Args) {
+  IntrusiveRefCntPtr<vfs::FileSystem> VFS = vfs::getRealFileSystem();
+
+  // Check the command line first, that's the user explicitly telling us what to
+  // use. Check the environment next, in case we're being invoked from a VS
+  // command prompt. Failing that, just try to find the newest Visual Studio
+  // version we can and use its default VC toolchain.
+  std::optional<StringRef> VCToolsDir, VCToolsVersion, WinSysRoot;
+  if (auto *A = Args.getLastArg(OPT_vctoolsdir))
+    VCToolsDir = A->getValue();
+  if (auto *A = Args.getLastArg(OPT_vctoolsversion))
+    VCToolsVersion = A->getValue();
+  if (auto *A = Args.getLastArg(OPT_winsysroot))
+    WinSysRoot = A->getValue();
+  if (!findVCToolChainViaCommandLine(*VFS, VCToolsDir, VCToolsVersion,
+                                     WinSysRoot, vcToolChainPath, vsLayout) &&
+      (Args.hasArg(OPT_lldignoreenv) ||
+       !findVCToolChainViaEnvironment(*VFS, vcToolChainPath, vsLayout)) &&
+      !findVCToolChainViaSetupConfig(*VFS, vcToolChainPath, vsLayout) &&
+      !findVCToolChainViaRegistry(vcToolChainPath, vsLayout))
+    return;
+
+  // If the VC environment hasn't been configured (perhaps because the user did
+  // not run vcvarsall), try to build a consistent link environment.  If the
+  // environment variable is set however, assume the user knows what they're
+  // doing. If the user passes /vctoolsdir or /winsdkdir, trust that over env
+  // vars.
+  if (const auto *A = Args.getLastArg(OPT_diasdkdir, OPT_winsysroot)) {
+    diaPath = A->getValue();
+    if (A->getOption().getID() == OPT_winsysroot)
+      path::append(diaPath, "DIA SDK");
+  }
+  useWinSysRootLibPath = Args.hasArg(OPT_lldignoreenv) ||
+                         !Process::GetEnv("LIB") ||
+                         Args.getLastArg(OPT_vctoolsdir, OPT_winsysroot);
+  if (Args.hasArg(OPT_lldignoreenv) || !Process::GetEnv("LIB") ||
+      Args.getLastArg(OPT_winsdkdir, OPT_winsysroot)) {
+    std::optional<StringRef> WinSdkDir, WinSdkVersion;
+    if (auto *A = Args.getLastArg(OPT_winsdkdir))
+      WinSdkDir = A->getValue();
+    if (auto *A = Args.getLastArg(OPT_winsdkversion))
+      WinSdkVersion = A->getValue();
+
+    if (useUniversalCRT(vsLayout, vcToolChainPath, getArch(), *VFS)) {
+      std::string UniversalCRTSdkPath;
+      std::string UCRTVersion;
+      if (getUniversalCRTSdkDir(*VFS, WinSdkDir, WinSdkVersion, WinSysRoot,
+                                UniversalCRTSdkPath, UCRTVersion)) {
+        universalCRTLibPath = UniversalCRTSdkPath;
+        path::append(universalCRTLibPath, "Lib", UCRTVersion, "ucrt");
+      }
+    }
+
+    std::string sdkPath;
+    std::string windowsSDKIncludeVersion;
+    std::string windowsSDKLibVersion;
+    if (getWindowsSDKDir(*VFS, WinSdkDir, WinSdkVersion, WinSysRoot, sdkPath,
+                         sdkMajor, windowsSDKIncludeVersion,
+                         windowsSDKLibVersion)) {
+      windowsSdkLibPath = sdkPath;
+      path::append(windowsSdkLibPath, "Lib");
+      if (sdkMajor >= 8)
+        path::append(windowsSdkLibPath, windowsSDKLibVersion, "um");
+    }
+  }
+}
+
+void LinkerDriver::addWinSysRootLibSearchPaths() {
+  if (!diaPath.empty()) {
+    // The DIA SDK always uses the legacy vc arch, even in new MSVC versions.
+    path::append(diaPath, "lib", archToLegacyVCArch(getArch()));
+    searchPaths.push_back(saver().save(diaPath.str()));
+  }
+  if (useWinSysRootLibPath) {
+    searchPaths.push_back(saver().save(getSubDirectoryPath(
+        SubDirectoryType::Lib, vsLayout, vcToolChainPath, getArch())));
+    searchPaths.push_back(saver().save(
+        getSubDirectoryPath(SubDirectoryType::Lib, vsLayout, vcToolChainPath,
+                            getArch(), "atlmfc")));
+  }
+  if (!universalCRTLibPath.empty()) {
+    StringRef ArchName = archToWindowsSDKArch(getArch());
+    if (!ArchName.empty()) {
+      path::append(universalCRTLibPath, ArchName);
+      searchPaths.push_back(saver().save(universalCRTLibPath.str()));
+    }
+  }
+  if (!windowsSdkLibPath.empty()) {
+    std::string path;
+    if (appendArchToWindowsSDKLibPath(sdkMajor, windowsSdkLibPath, getArch(),
+                                      path))
+      searchPaths.push_back(saver().save(path));
+  }
+}
+
 // Parses LIB environment which contains a list of search paths.
 void LinkerDriver::addLibSearchPaths() {
-  Optional<std::string> envOpt = Process::GetEnv("LIB");
-  if (!envOpt.hasValue())
+  std::optional<std::string> envOpt = Process::GetEnv("LIB");
+  if (!envOpt)
     return;
-  StringRef env = saver.save(*envOpt);
+  StringRef env = saver().save(*envOpt);
   while (!env.empty()) {
     StringRef path;
     std::tie(path, env) = env.split(';');
@@ -543,10 +653,10 @@ void LinkerDriver::addLibSearchPaths() {
 }
 
 Symbol *LinkerDriver::addUndefined(StringRef name) {
-  Symbol *b = symtab->addUndefined(name);
+  Symbol *b = ctx.symtab.addUndefined(name);
   if (!b->isGCRoot) {
     b->isGCRoot = true;
-    config->gcroot.push_back(b);
+    ctx.config.gcroot.push_back(b);
   }
   return b;
 }
@@ -558,14 +668,14 @@ StringRef LinkerDriver::mangleMaybe(Symbol *s) {
     return "";
 
   // Otherwise, see if a similar, mangled symbol exists in the symbol table.
-  Symbol *mangled = symtab->findMangle(unmangled->getName());
+  Symbol *mangled = ctx.symtab.findMangle(unmangled->getName());
   if (!mangled)
     return "";
 
   // If we find a similar mangled symbol, make this an alias to it and return
   // its name.
   log(unmangled->getName() + " aliased to " + mangled->getName());
-  unmangled->weakAlias = symtab->addUndefined(mangled->getName());
+  unmangled->weakAlias = ctx.symtab.addUndefined(mangled->getName());
   return mangled->getName();
 }
 
@@ -575,15 +685,15 @@ StringRef LinkerDriver::mangleMaybe(Symbol *s) {
 // 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() {
-  assert(config->subsystem != IMAGE_SUBSYSTEM_UNKNOWN &&
+  assert(ctx.config.subsystem != IMAGE_SUBSYSTEM_UNKNOWN &&
          "must handle /subsystem before calling this");
 
-  if (config->mingw)
-    return mangle(config->subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI
+  if (ctx.config.mingw)
+    return mangle(ctx.config.subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI
                       ? "WinMainCRTStartup"
                       : "mainCRTStartup");
 
-  if (config->subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) {
+  if (ctx.config.subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) {
     if (findUnderscoreMangle("wWinMain")) {
       if (!findUnderscoreMangle("WinMain"))
         return mangle("wWinMainCRTStartup");
@@ -600,9 +710,9 @@ StringRef LinkerDriver::findDefaultEntry() {
 }
 
 WindowsSubsystem LinkerDriver::inferSubsystem() {
-  if (config->dll)
+  if (ctx.config.dll)
     return IMAGE_SUBSYSTEM_WINDOWS_GUI;
-  if (config->mingw)
+  if (ctx.config.mingw)
     return IMAGE_SUBSYSTEM_WINDOWS_CUI;
   // Note that link.exe infers the subsystem from the presence of these
   // functions even if /entry: or /nodefaultlib are passed which causes them
@@ -624,10 +734,10 @@ WindowsSubsystem LinkerDriver::inferSubsystem() {
   return IMAGE_SUBSYSTEM_UNKNOWN;
 }
 
-static uint64_t getDefaultImageBase() {
-  if (config->is64())
-    return config->dll ? 0x180000000 : 0x140000000;
-  return config->dll ? 0x10000000 : 0x400000;
+uint64_t LinkerDriver::getDefaultImageBase() {
+  if (ctx.config.is64())
+    return ctx.config.dll ? 0x180000000 : 0x140000000;
+  return ctx.config.dll ? 0x10000000 : 0x400000;
 }
 
 static std::string rewritePath(StringRef s) {
@@ -651,15 +761,11 @@ static std::string createResponseFile(const opt::InputArgList &args,
     case OPT_INPUT:
     case OPT_defaultlib:
     case OPT_libpath:
-    case OPT_manifest:
-    case OPT_manifest_colon:
-    case OPT_manifestdependency:
-    case OPT_manifestfile:
-    case OPT_manifestinput:
-    case OPT_manifestuac:
+    case OPT_winsysroot:
       break;
     case OPT_call_graph_ordering_file:
     case OPT_deffile:
+    case OPT_manifestinput:
     case OPT_natvis:
       os << arg->getSpelling() << quote(rewritePath(arg->getValue())) << '\n';
       break;
@@ -677,6 +783,7 @@ static std::string createResponseFile(const opt::InputArgList &args,
       break;
     }
     case OPT_implib:
+    case OPT_manifestfile:
     case OPT_pdb:
     case OPT_pdbstripped:
     case OPT_out:
@@ -771,8 +878,9 @@ static unsigned parseDebugTypes(const opt::InputArgList &args) {
   return debugTypes;
 }
 
-static std::string getMapFile(const opt::InputArgList &args,
-                              opt::OptSpecifier os, opt::OptSpecifier osFile) {
+std::string LinkerDriver::getMapFile(const opt::InputArgList &args,
+                                     opt::OptSpecifier os,
+                                     opt::OptSpecifier osFile) {
   auto *arg = args.getLastArg(os, osFile);
   if (!arg)
     return "";
@@ -780,14 +888,14 @@ static std::string getMapFile(const opt::InputArgList &args,
     return arg->getValue();
 
   assert(arg->getOption().getID() == os.getID());
-  StringRef outFile = config->outputFile;
+  StringRef outFile = ctx.config.outputFile;
   return (outFile.substr(0, outFile.rfind('.')) + ".map").str();
 }
 
-static std::string getImplibPath() {
-  if (!config->implib.empty())
-    return std::string(config->implib);
-  SmallString<128> out = StringRef(config->outputFile);
+std::string LinkerDriver::getImplibPath() {
+  if (!ctx.config.implib.empty())
+    return std::string(ctx.config.implib);
+  SmallString<128> out = StringRef(ctx.config.outputFile);
   sys::path::replace_extension(out, ".lib");
   return std::string(out.str());
 }
@@ -799,30 +907,31 @@ static std::string getImplibPath() {
 //   LINK | {value}        | {value}.{.dll/.exe} | {output name}
 //    LIB | {value}        | {value}.dll         | {output name}.dll
 //
-static std::string getImportName(bool asLib) {
+std::string LinkerDriver::getImportName(bool asLib) {
   SmallString<128> out;
 
-  if (config->importName.empty()) {
-    out.assign(sys::path::filename(config->outputFile));
+  if (ctx.config.importName.empty()) {
+    out.assign(sys::path::filename(ctx.config.outputFile));
     if (asLib)
       sys::path::replace_extension(out, ".dll");
   } else {
-    out.assign(config->importName);
+    out.assign(ctx.config.importName);
     if (!sys::path::has_extension(out))
       sys::path::replace_extension(out,
-                                   (config->dll || asLib) ? ".dll" : ".exe");
+                                   (ctx.config.dll || asLib) ? ".dll" : ".exe");
   }
 
   return std::string(out.str());
 }
 
-static void createImportLibrary(bool asLib) {
+void LinkerDriver::createImportLibrary(bool asLib) {
   std::vector<COFFShortExport> exports;
-  for (Export &e1 : config->exports) {
+  for (Export &e1 : ctx.config.exports) {
     COFFShortExport e2;
     e2.Name = std::string(e1.name);
     e2.SymbolName = std::string(e1.symbolName);
     e2.ExtName = std::string(e1.extName);
+    e2.AliasTarget = std::string(e1.aliasTarget);
     e2.Ordinal = e1.ordinal;
     e2.Noname = e1.noname;
     e2.Data = e1.data;
@@ -831,16 +940,12 @@ static void createImportLibrary(bool asLib) {
     exports.push_back(e2);
   }
 
-  auto handleError = [](Error &&e) {
-    handleAllErrors(std::move(e),
-                    [](ErrorInfoBase &eib) { error(eib.message()); });
-  };
   std::string libName = getImportName(asLib);
   std::string path = getImplibPath();
 
-  if (!config->incremental) {
-    handleError(writeImportLibrary(libName, path, exports, config->machine,
-                                   config->mingw));
+  if (!ctx.config.incremental) {
+    checkError(writeImportLibrary(libName, path, exports, ctx.config.machine,
+                                  ctx.config.mingw));
     return;
   }
 
@@ -849,8 +954,8 @@ static void createImportLibrary(bool asLib) {
   ErrorOr<std::unique_ptr<MemoryBuffer>> oldBuf = MemoryBuffer::getFile(
       path, /*IsText=*/false, /*RequiresNullTerminator=*/false);
   if (!oldBuf) {
-    handleError(writeImportLibrary(libName, path, exports, config->machine,
-                                   config->mingw));
+    checkError(writeImportLibrary(libName, path, exports, ctx.config.machine,
+                                  ctx.config.mingw));
     return;
   }
 
@@ -860,9 +965,9 @@ static void createImportLibrary(bool asLib) {
     fatal("cannot create temporary file for import library " + path + ": " +
           ec.message());
 
-  if (Error e = writeImportLibrary(libName, tmpName, exports, config->machine,
-                                   config->mingw)) {
-    handleError(std::move(e));
+  if (Error e = writeImportLibrary(libName, tmpName, exports,
+                                   ctx.config.machine, ctx.config.mingw)) {
+    checkError(std::move(e));
     return;
   }
 
@@ -870,45 +975,45 @@ static void createImportLibrary(bool asLib) {
       tmpName, /*IsText=*/false, /*RequiresNullTerminator=*/false));
   if ((*oldBuf)->getBuffer() != newBuf->getBuffer()) {
     oldBuf->reset();
-    handleError(errorCodeToError(sys::fs::rename(tmpName, path)));
+    checkError(errorCodeToError(sys::fs::rename(tmpName, path)));
   } else {
     sys::fs::remove(tmpName);
   }
 }
 
-static void parseModuleDefs(StringRef path) {
+void LinkerDriver::parseModuleDefs(StringRef path) {
   std::unique_ptr<MemoryBuffer> mb =
       CHECK(MemoryBuffer::getFile(path, /*IsText=*/false,
                                   /*RequiresNullTerminator=*/false,
                                   /*IsVolatile=*/true),
             "could not open " + path);
   COFFModuleDefinition m = check(parseCOFFModuleDefinition(
-      mb->getMemBufferRef(), config->machine, config->mingw));
+      mb->getMemBufferRef(), ctx.config.machine, ctx.config.mingw));
 
   // Include in /reproduce: output if applicable.
-  driver->takeBuffer(std::move(mb));
+  ctx.driver.takeBuffer(std::move(mb));
 
-  if (config->outputFile.empty())
-    config->outputFile = std::string(saver.save(m.OutputFile));
-  config->importName = std::string(saver.save(m.ImportName));
+  if (ctx.config.outputFile.empty())
+    ctx.config.outputFile = std::string(saver().save(m.OutputFile));
+  ctx.config.importName = std::string(saver().save(m.ImportName));
   if (m.ImageBase)
-    config->imageBase = m.ImageBase;
+    ctx.config.imageBase = m.ImageBase;
   if (m.StackReserve)
-    config->stackReserve = m.StackReserve;
+    ctx.config.stackReserve = m.StackReserve;
   if (m.StackCommit)
-    config->stackCommit = m.StackCommit;
+    ctx.config.stackCommit = m.StackCommit;
   if (m.HeapReserve)
-    config->heapReserve = m.HeapReserve;
+    ctx.config.heapReserve = m.HeapReserve;
   if (m.HeapCommit)
-    config->heapCommit = m.HeapCommit;
+    ctx.config.heapCommit = m.HeapCommit;
   if (m.MajorImageVersion)
-    config->majorImageVersion = m.MajorImageVersion;
+    ctx.config.majorImageVersion = m.MajorImageVersion;
   if (m.MinorImageVersion)
-    config->minorImageVersion = m.MinorImageVersion;
+    ctx.config.minorImageVersion = m.MinorImageVersion;
   if (m.MajorOSVersion)
-    config->majorOSVersion = m.MajorOSVersion;
+    ctx.config.majorOSVersion = m.MajorOSVersion;
   if (m.MinorOSVersion)
-    config->minorOSVersion = m.MinorOSVersion;
+    ctx.config.minorOSVersion = m.MinorOSVersion;
 
   for (COFFShortExport e1 : m.Exports) {
     Export e2;
@@ -918,19 +1023,20 @@ static void parseModuleDefs(StringRef path) {
     // DLL instead. This is supported by both MS and GNU linkers.
     if (!e1.ExtName.empty() && e1.ExtName != e1.Name &&
         StringRef(e1.Name).contains('.')) {
-      e2.name = saver.save(e1.ExtName);
-      e2.forwardTo = saver.save(e1.Name);
-      config->exports.push_back(e2);
+      e2.name = saver().save(e1.ExtName);
+      e2.forwardTo = saver().save(e1.Name);
+      ctx.config.exports.push_back(e2);
       continue;
     }
-    e2.name = saver.save(e1.Name);
-    e2.extName = saver.save(e1.ExtName);
+    e2.name = saver().save(e1.Name);
+    e2.extName = saver().save(e1.ExtName);
+    e2.aliasTarget = saver().save(e1.AliasTarget);
     e2.ordinal = e1.Ordinal;
     e2.noname = e1.Noname;
     e2.data = e1.Data;
     e2.isPrivate = e1.Private;
     e2.constant = e1.Constant;
-    config->exports.push_back(e2);
+    ctx.config.exports.push_back(e2);
   }
 }
 
@@ -939,7 +1045,7 @@ void LinkerDriver::enqueueTask(std::function<void()> task) {
 }
 
 bool LinkerDriver::run() {
-  ScopedTimer t(inputFileTimer);
+  ScopedTimer t(ctx.inputFileTimer);
 
   bool didWork = !taskQueue.empty();
   while (!taskQueue.empty()) {
@@ -952,7 +1058,7 @@ bool LinkerDriver::run() {
 // Parse an /order file. If an option is given, the linker places
 // COMDAT sections in the same order as their names appear in the
 // given file.
-static void parseOrderFile(StringRef arg) {
+void LinkerDriver::parseOrderFile(StringRef arg) {
   // For some reason, the MSVC linker requires a filename to be
   // preceded by "@".
   if (!arg.startswith("@")) {
@@ -962,7 +1068,7 @@ static void parseOrderFile(StringRef arg) {
 
   // Get a list of all comdat sections for error checking.
   DenseSet<StringRef> set;
-  for (Chunk *c : symtab->getChunks())
+  for (Chunk *c : ctx.symtab.getChunks())
     if (auto *sec = dyn_cast<SectionChunk>(c))
       if (sec->sym)
         set.insert(sec->sym->getName());
@@ -981,22 +1087,22 @@ static void parseOrderFile(StringRef arg) {
   // end of an output section.
   for (StringRef arg : args::getLines(mb->getMemBufferRef())) {
     std::string s(arg);
-    if (config->machine == I386 && !isDecorated(s))
+    if (ctx.config.machine == I386 && !isDecorated(s))
       s = "_" + s;
 
     if (set.count(s) == 0) {
-      if (config->warnMissingOrderSymbol)
+      if (ctx.config.warnMissingOrderSymbol)
         warn("/order:" + arg + ": missing symbol: " + s + " [LNK4037]");
     }
     else
-      config->order[s] = INT_MIN + config->order.size();
+      ctx.config.order[s] = INT_MIN + ctx.config.order.size();
   }
 
   // Include in /reproduce: output if applicable.
-  driver->takeBuffer(std::move(mb));
+  ctx.driver.takeBuffer(std::move(mb));
 }
 
-static void parseCallGraphFile(StringRef path) {
+void LinkerDriver::parseCallGraphFile(StringRef path) {
   std::unique_ptr<MemoryBuffer> mb =
       CHECK(MemoryBuffer::getFile(path, /*IsText=*/false,
                                   /*RequiresNullTerminator=*/false,
@@ -1005,7 +1111,7 @@ static void parseCallGraphFile(StringRef path) {
 
   // Build a map from symbol name to section.
   DenseMap<StringRef, Symbol *> map;
-  for (ObjFile *file : ObjFile::instances)
+  for (ObjFile *file : ctx.objFileInstances)
     for (Symbol *sym : file->getSymbols())
       if (sym)
         map[sym->getName()] = sym;
@@ -1013,7 +1119,7 @@ static void parseCallGraphFile(StringRef path) {
   auto findSection = [&](StringRef name) -> SectionChunk * {
     Symbol *sym = map.lookup(name);
     if (!sym) {
-      if (config->warnMissingOrderSymbol)
+      if (ctx.config.warnMissingOrderSymbol)
         warn(path + ": no such symbol: " + name);
       return nullptr;
     }
@@ -1035,15 +1141,15 @@ static void parseCallGraphFile(StringRef path) {
 
     if (SectionChunk *from = findSection(fields[0]))
       if (SectionChunk *to = findSection(fields[1]))
-        config->callGraphProfile[{from, to}] += count;
+        ctx.config.callGraphProfile[{from, to}] += count;
   }
 
   // Include in /reproduce: output if applicable.
-  driver->takeBuffer(std::move(mb));
+  ctx.driver.takeBuffer(std::move(mb));
 }
 
-static void readCallGraphsFromObjectFiles() {
-  for (ObjFile *obj : ObjFile::instances) {
+static void readCallGraphsFromObjectFiles(COFFLinkerContext &ctx) {
+  for (ObjFile *obj : ctx.objFileInstances) {
     if (obj->callgraphSec) {
       ArrayRef<uint8_t> contents;
       cantFail(
@@ -1065,7 +1171,7 @@ static void readCallGraphsFromObjectFiles() {
         auto *from = dyn_cast_or_null<SectionChunk>(fromSym->getChunk());
         auto *to = dyn_cast_or_null<SectionChunk>(toSym->getChunk());
         if (from && to)
-          config->callGraphProfile[{from, to}] += count;
+          ctx.config.callGraphProfile[{from, to}] += count;
       }
     }
   }
@@ -1077,15 +1183,15 @@ static void markAddrsig(Symbol *s) {
       c->keepUnique = true;
 }
 
-static void findKeepUniqueSections() {
+static void findKeepUniqueSections(COFFLinkerContext &ctx) {
   // Exported symbols could be address-significant in other executables or DSOs,
   // so we conservatively mark them as address-significant.
-  for (Export &r : config->exports)
+  for (Export &r : ctx.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) {
+  for (ObjFile *obj : ctx.objFileInstances) {
     ArrayRef<Symbol *> syms = obj->getSymbols();
     if (obj->addrsigSec) {
       ArrayRef<uint8_t> contents;
@@ -1118,12 +1224,12 @@ static void findKeepUniqueSections() {
 // binary).
 // lld only supports %_PDB% and %_EXT% and warns on references to all other env
 // vars.
-static void parsePDBAltPath(StringRef altPath) {
+void LinkerDriver::parsePDBAltPath() {
   SmallString<128> buf;
   StringRef pdbBasename =
-      sys::path::filename(config->pdbPath, sys::path::Style::windows);
+      sys::path::filename(ctx.config.pdbPath, sys::path::Style::windows);
   StringRef binaryExtension =
-      sys::path::extension(config->outputFile, sys::path::Style::windows);
+      sys::path::extension(ctx.config.outputFile, sys::path::Style::windows);
   if (!binaryExtension.empty())
     binaryExtension = binaryExtension.substr(1); // %_EXT% does not include '.'.
 
@@ -1134,19 +1240,22 @@ static void parsePDBAltPath(StringRef altPath) {
   //   v   v   v
   //   a...%...%...
   size_t cursor = 0;
-  while (cursor < altPath.size()) {
+  while (cursor < ctx.config.pdbAltPath.size()) {
     size_t firstMark, secondMark;
-    if ((firstMark = altPath.find('%', cursor)) == StringRef::npos ||
-        (secondMark = altPath.find('%', firstMark + 1)) == StringRef::npos) {
+    if ((firstMark = ctx.config.pdbAltPath.find('%', cursor)) ==
+            StringRef::npos ||
+        (secondMark = ctx.config.pdbAltPath.find('%', firstMark + 1)) ==
+            StringRef::npos) {
       // Didn't find another full fragment, treat rest of string as literal.
-      buf.append(altPath.substr(cursor));
+      buf.append(ctx.config.pdbAltPath.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);
+    buf.append(ctx.config.pdbAltPath.substr(cursor, firstMark - cursor));
+    StringRef var =
+        ctx.config.pdbAltPath.substr(firstMark, secondMark - firstMark + 1);
     if (var.equals_insensitive("%_pdb%"))
       buf.append(pdbBasename);
     else if (var.equals_insensitive("%_ext%"))
@@ -1160,7 +1269,7 @@ static void parsePDBAltPath(StringRef altPath) {
     cursor = secondMark + 1;
   }
 
-  config->pdbAltPath = buf;
+  ctx.config.pdbAltPath = buf;
 }
 
 /// Convert resource files and potentially merge input resource object
@@ -1169,12 +1278,12 @@ static void parsePDBAltPath(StringRef altPath) {
 void LinkerDriver::convertResources() {
   std::vector<ObjFile *> resourceObjFiles;
 
-  for (ObjFile *f : ObjFile::instances) {
+  for (ObjFile *f : ctx.objFileInstances) {
     if (f->isResourceObjFile())
       resourceObjFiles.push_back(f);
   }
 
-  if (!config->mingw &&
+  if (!ctx.config.mingw &&
       (resourceObjFiles.size() > 1 ||
        (resourceObjFiles.size() == 1 && !resources.empty()))) {
     error((!resources.empty() ? "internal .obj file created from .res files"
@@ -1191,8 +1300,9 @@ void LinkerDriver::convertResources() {
       f->includeResourceChunks();
     return;
   }
-  ObjFile *f = make<ObjFile>(convertResToCOFF(resources, resourceObjFiles));
-  symtab->addFile(f);
+  ObjFile *f =
+      make<ObjFile>(ctx, convertResToCOFF(resources, resourceObjFiles));
+  ctx.symtab.addFile(f);
   f->includeResourceChunks();
 }
 
@@ -1204,29 +1314,36 @@ void LinkerDriver::convertResources() {
 // than MinGW in the case that nothing is explicitly exported.
 void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) {
   if (!args.hasArg(OPT_export_all_symbols)) {
-    if (!config->dll)
+    if (!ctx.config.dll)
       return;
 
-    if (!config->exports.empty())
+    if (!ctx.config.exports.empty())
       return;
     if (args.hasArg(OPT_exclude_all_symbols))
       return;
   }
 
-  AutoExporter exporter;
+  AutoExporter exporter(ctx, excludedSymbols);
 
   for (auto *arg : args.filtered(OPT_wholearchive_file))
-    if (Optional<StringRef> path = doFindFile(arg->getValue()))
+    if (std::optional<StringRef> path = doFindFile(arg->getValue()))
       exporter.addWholeArchive(*path);
 
-  symtab->forEachSymbol([&](Symbol *s) {
+  for (auto *arg : args.filtered(OPT_exclude_symbols)) {
+    SmallVector<StringRef, 2> vec;
+    StringRef(arg->getValue()).split(vec, ',');
+    for (StringRef sym : vec)
+      exporter.addExcludedSymbol(mangle(sym));
+  }
+
+  ctx.symtab.forEachSymbol([&](Symbol *s) {
     auto *def = dyn_cast<Defined>(s);
     if (!exporter.shouldExport(def))
       return;
 
     if (!def->isGCRoot) {
       def->isGCRoot = true;
-      config->gcroot.push_back(def);
+      ctx.config.gcroot.push_back(def);
     }
 
     Export e;
@@ -1236,7 +1353,7 @@ void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) {
       if (!(c->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE))
         e.data = true;
     s->isUsedInRegularObj = true;
-    config->exports.push_back(e);
+    ctx.config.exports.push_back(e);
   });
 }
 
@@ -1247,7 +1364,7 @@ void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) {
 // /linkrepro and /reproduce are very similar, but /linkrepro takes a directory
 // name while /reproduce takes a full path. We have /linkrepro for compatibility
 // with Microsoft link.exe.
-Optional<std::string> getReproduceFile(const opt::InputArgList &args) {
+std::optional<std::string> getReproduceFile(const opt::InputArgList &args) {
   if (auto *arg = args.getLastArg(OPT_reproduce))
     return std::string(arg->getValue());
 
@@ -1262,11 +1379,34 @@ Optional<std::string> getReproduceFile(const opt::InputArgList &args) {
   if (auto *path = getenv("LLD_REPRODUCE"))
     return std::string(path);
 
-  return None;
+  return std::nullopt;
+}
+
+static std::unique_ptr<llvm::vfs::FileSystem>
+getVFS(const opt::InputArgList &args) {
+  using namespace llvm::vfs;
+
+  const opt::Arg *arg = args.getLastArg(OPT_vfsoverlay);
+  if (!arg)
+    return nullptr;
+
+  auto bufOrErr = llvm::MemoryBuffer::getFile(arg->getValue());
+  if (!bufOrErr) {
+    checkError(errorCodeToError(bufOrErr.getError()));
+    return nullptr;
+  }
+
+  if (auto ret = vfs::getVFSFromYAML(std::move(*bufOrErr), /*DiagHandler*/ nullptr,
+                             arg->getValue()))
+    return ret;
+
+  error("Invalid vfs overlay");
+  return nullptr;
 }
 
 void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
-  ScopedTimer rootTimer(Timer::root());
+  ScopedTimer rootTimer(ctx.rootTimer);
+  Configuration *config = &ctx.config;
 
   // Needed for LTO.
   InitializeAllTargetInfos();
@@ -1286,14 +1426,16 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   }
 
   // Parse command line options.
-  ArgParser parser;
+  ArgParser parser(ctx);
   opt::InputArgList args = parser.parse(argsArr);
 
   // Parse and evaluate -mllvm options.
   std::vector<const char *> v;
   v.push_back("lld-link (LLVM option parsing)");
-  for (auto *arg : args.filtered(OPT_mllvm))
+  for (const auto *arg : args.filtered(OPT_mllvm)) {
     v.push_back(arg->getValue());
+    config->mllvmOpts.emplace_back(arg->getValue());
+  }
   cl::ResetAllOptionOccurrences();
   cl::ParseCommandLineOptions(v.size(), v.data());
 
@@ -1306,6 +1448,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     errorHandler().errorLimit = n;
   }
 
+  config->vfs = getVFS(args);
+
   // Handle /help
   if (args.hasArg(OPT_help)) {
     printHelp(argsArr[0]);
@@ -1340,9 +1484,12 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   // Handle /lldmingw early, since it can potentially affect how other
   // options are handled.
   config->mingw = args.hasArg(OPT_lldmingw);
+  if (config->mingw)
+    ctx.e.errorLimitExceededMsg = "too many errors emitted, stopping now"
+                                  " (use --error-limit=0 to see all errors)";
 
   // Handle /linkrepro and /reproduce.
-  if (Optional<std::string> path = getReproduceFile(args)) {
+  if (std::optional<std::string> path = getReproduceFile(args)) {
     Expected<std::unique_ptr<TarWriter>> errOrWriter =
         TarWriter::create(*path, sys::path::stem(*path));
 
@@ -1365,7 +1512,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   searchPaths.push_back("");
   for (auto *arg : args.filtered(OPT_libpath))
     searchPaths.push_back(arg->getValue());
-  if (!args.hasArg(OPT_lldignoreenv))
+  detectWinSysRoot(args);
+  if (!args.hasArg(OPT_lldignoreenv) && !args.hasArg(OPT_winsysroot))
     addLibSearchPaths();
 
   // Handle /ignore
@@ -1415,7 +1563,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   }
 
   // Handle /demangle
-  config->demangle = args.hasFlag(OPT_demangle, OPT_demangle_no);
+  config->demangle = args.hasFlag(OPT_demangle, OPT_demangle_no, true);
 
   // Handle /debugtype
   config->debugTypes = parseDebugTypes(args);
@@ -1439,6 +1587,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
       config->pdbPath = arg->getValue();
     if (auto *arg = args.getLastArg(OPT_pdbaltpath))
       config->pdbAltPath = arg->getValue();
+    if (auto *arg = args.getLastArg(OPT_pdbpagesize))
+      parsePDBPageSize(arg->getValue());
     if (args.hasArg(OPT_natvis))
       config->natvisFiles = args.getAllArgValues(OPT_natvis);
     if (args.hasArg(OPT_pdbstream)) {
@@ -1503,6 +1653,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     config->machine = getMachineType(arg->getValue());
     if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN)
       fatal(Twine("unknown /machine argument: ") + arg->getValue());
+    addWinSysRootLibSearchPaths();
   }
 
   // Handle /nodefaultlib:<filename>
@@ -1585,13 +1736,14 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   if (auto *arg = args.getLastArg(OPT_implib))
     config->implib = arg->getValue();
 
+  config->noimplib = args.hasArg(OPT_noimplib);
+
   // Handle /opt.
   bool doGC = debug == DebugKind::None || args.hasArg(OPT_profile);
-  Optional<ICFLevel> icfLevel = None;
+  std::optional<ICFLevel> icfLevel;
   if (args.hasArg(OPT_profile))
     icfLevel = ICFLevel::None;
   unsigned tailMerge = 1;
-  bool ltoNewPM = LLVM_ENABLE_NEW_PASS_MANAGER;
   bool ltoDebugPM = false;
   for (auto *arg : args.filtered(OPT_opt)) {
     std::string str = StringRef(arg->getValue()).lower();
@@ -1613,9 +1765,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
       } else if (s == "nolldtailmerge") {
         tailMerge = 0;
       } else if (s == "ltonewpassmanager") {
-        ltoNewPM = true;
-      } else if (s == "noltonewpassmanager") {
-        ltoNewPM = false;
+        /* We always use the new PM. */
       } else if (s == "ltodebugpassmanager") {
         ltoDebugPM = true;
       } else if (s == "noltodebugpassmanager") {
@@ -1642,10 +1792,9 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   if (!icfLevel)
     icfLevel = doGC ? ICFLevel::All : ICFLevel::None;
   config->doGC = doGC;
-  config->doICF = icfLevel.getValue();
+  config->doICF = *icfLevel;
   config->tailMerge =
       (tailMerge == 1 && config->doICF != ICFLevel::None) || tailMerge == 2;
-  config->ltoNewPassManager = ltoNewPM;
   config->ltoDebugPassManager = ltoDebugPM;
 
   // Handle /lldsavetemps
@@ -1705,12 +1854,9 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   for (auto *arg : args.filtered(OPT_aligncomm))
     parseAligncomm(arg->getValue());
 
-  // Handle /manifestdependency. This enables /manifest unless /manifest:no is
-  // also passed.
-  if (auto *arg = args.getLastArg(OPT_manifestdependency)) {
-    config->manifestDependency = arg->getValue();
-    config->manifest = Configuration::SideBySide;
-  }
+  // Handle /manifestdependency.
+  for (auto *arg : args.filtered(OPT_manifestdependency))
+    config->manifestDependencies.insert(arg->getValue());
 
   // Handle /manifest and /manifest:
   if (auto *arg = args.getLastArg(OPT_manifest, OPT_manifest_colon)) {
@@ -1750,6 +1896,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   config->ltoCSProfileGenerate = args.hasArg(OPT_lto_cs_profile_generate);
   config->ltoCSProfileFile = args.getLastArgValue(OPT_lto_cs_profile_file);
   // Handle miscellaneous boolean flags.
+  config->ltoPGOWarnMismatch = args.hasFlag(OPT_lto_pgo_warn_mismatch,
+                                            OPT_lto_pgo_warn_mismatch_no, true);
   config->allowBind = args.hasFlag(OPT_allowbind, OPT_allowbind_no, true);
   config->allowIsolation =
       args.hasFlag(OPT_allowisolation, OPT_allowisolation_no, true);
@@ -1778,20 +1926,14 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
       args.hasFlag(OPT_stdcall_fixup, OPT_stdcall_fixup_no, config->mingw);
   config->warnStdcallFixup = !args.hasArg(OPT_stdcall_fixup);
 
+  if (args.hasFlag(OPT_inferasanlibs, OPT_inferasanlibs_no, false))
+    warn("ignoring '/inferasanlibs', this flag is not supported");
+
   // Don't warn about long section names, such as .debug_info, for mingw or
   // when -debug:dwarf is requested.
   if (config->mingw || config->debugDwarf)
     config->warnLongSectionNames = false;
 
-  config->lldmapFile = getMapFile(args, OPT_lldmap, OPT_lldmap_file);
-  config->mapFile = getMapFile(args, OPT_map, OPT_map_file);
-
-  if (config->lldmapFile != "" && config->lldmapFile == config->mapFile) {
-    warn("/lldmap and /map have the same output file '" + config->mapFile +
-         "'.\n>>> ignoring /lldmap");
-    config->lldmapFile.clear();
-  }
-
   if (config->incremental && args.hasArg(OPT_profile)) {
     warn("ignoring '/incremental' due to '/profile' specification");
     config->incremental = false;
@@ -1819,8 +1961,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
 
   std::set<sys::fs::UniqueID> wholeArchives;
   for (auto *arg : args.filtered(OPT_wholearchive_file))
-    if (Optional<StringRef> path = doFindFile(arg->getValue()))
-      if (Optional<sys::fs::UniqueID> id = getUniqueID(*path))
+    if (std::optional<StringRef> path = doFindFile(arg->getValue()))
+      if (std::optional<sys::fs::UniqueID> id = getUniqueID(*path))
         wholeArchives.insert(*id);
 
   // A predicate returning true if a given path is an argument for
@@ -1830,7 +1972,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   auto isWholeArchive = [&](StringRef path) -> bool {
     if (args.hasArg(OPT_wholearchive_flag))
       return true;
-    if (Optional<sys::fs::UniqueID> id = getUniqueID(path))
+    if (std::optional<sys::fs::UniqueID> id = getUniqueID(path))
       return wholeArchives.count(*id);
     return false;
   };
@@ -1852,11 +1994,11 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
       inLib = true;
       break;
     case OPT_wholearchive_file:
-      if (Optional<StringRef> path = findFile(arg->getValue()))
+      if (std::optional<StringRef> path = findFile(arg->getValue()))
         enqueuePath(*path, true, inLib);
       break;
     case OPT_INPUT:
-      if (Optional<StringRef> path = findFile(arg->getValue()))
+      if (std::optional<StringRef> path = findFile(arg->getValue()))
         enqueuePath(*path, isWholeArchive(*path), inLib);
       break;
     default:
@@ -1865,19 +2007,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     }
   }
 
-  // Process files specified as /defaultlib. These should be enequeued after
-  // other files, which is why they are in a separate loop.
-  for (auto *arg : args.filtered(OPT_defaultlib))
-    if (Optional<StringRef> path = findLib(arg->getValue()))
-      enqueuePath(*path, false, false);
-
-  // Windows specific -- Create a resource file containing a manifest file.
-  if (config->manifest == Configuration::Embed)
-    addBuffer(createManifestRes(), false, false);
-
   // Read all input files given via the command line.
   run();
-
   if (errorCount())
     return;
 
@@ -1886,9 +2017,23 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) {
     warn("/machine is not specified. x64 is assumed");
     config->machine = AMD64;
+    addWinSysRootLibSearchPaths();
   }
   config->wordsize = config->is64() ? 8 : 4;
 
+  // Process files specified as /defaultlib. These must be processed after
+  // addWinSysRootLibSearchPaths(), which is why they are in a separate loop.
+  for (auto *arg : args.filtered(OPT_defaultlib))
+    if (std::optional<StringRef> path = findLib(arg->getValue()))
+      enqueuePath(*path, false, false);
+  run();
+  if (errorCount())
+    return;
+
+  // Handle /RELEASE
+  if (args.hasArg(OPT_release))
+    config->writeCheckSum = true;
+  
   // Handle /safeseh, x86 only, on by default, except for mingw.
   if (config->machine == I386) {
     config->safeSEH = args.hasFlag(OPT_safeseh, OPT_safeseh_no, !config->mingw);
@@ -1897,12 +2042,13 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
 
   // Handle /functionpadmin
   for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt))
-    parseFunctionPadMin(arg, config->machine);
+    parseFunctionPadMin(arg);
 
-  if (tar)
+  if (tar) {
     tar->append("response.txt",
                 createResponseFile(args, filePaths,
                                    ArrayRef<StringRef>(searchPaths).slice(1)));
+  }
 
   // Handle /largeaddressaware
   config->largeAddressAware = args.hasFlag(
@@ -1923,9 +2069,9 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     Export e = parseExport(arg->getValue());
     if (config->machine == I386) {
       if (!isDecorated(e.name))
-        e.name = saver.save("_" + e.name);
+        e.name = saver().save("_" + e.name);
       if (!e.extName.empty() && !isDecorated(e.extName))
-        e.extName = saver.save("_" + e.extName);
+        e.extName = saver().save("_" + e.extName);
     }
     config->exports.push_back(e);
   }
@@ -1939,7 +2085,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   // Handle generation of import library from a def file.
   if (!args.hasArg(OPT_INPUT, OPT_wholearchive_file)) {
     fixupExports();
-    createImportLibrary(/*asLib=*/true);
+    if (!config->noimplib)
+      createImportLibrary(/*asLib=*/true);
     return;
   }
 
@@ -1987,7 +2134,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   // Set default image name if neither /out or /def set it.
   if (config->outputFile.empty()) {
     config->outputFile = getOutputPath(
-        (*args.filtered(OPT_INPUT, OPT_wholearchive_file).begin())->getValue());
+        (*args.filtered(OPT_INPUT, OPT_wholearchive_file).begin())->getValue(),
+        config->dll, config->driver);
   }
 
   // Fail early if an output file is not writable.
@@ -1996,6 +2144,25 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     return;
   }
 
+  config->lldmapFile = getMapFile(args, OPT_lldmap, OPT_lldmap_file);
+  config->mapFile = getMapFile(args, OPT_map, OPT_map_file);
+
+  if (config->mapFile != "" && args.hasArg(OPT_map_info)) {
+    for (auto *arg : args.filtered(OPT_map_info)) {
+      std::string s = StringRef(arg->getValue()).lower();
+      if (s == "exports")
+        config->mapInfo = true;
+      else
+        error("unknown option: /mapinfo:" + s);
+    }
+  }
+
+  if (config->lldmapFile != "" && config->lldmapFile == config->mapFile) {
+    warn("/lldmap and /map have the same output file '" + config->mapFile +
+         "'.\n>>> ignoring /lldmap");
+    config->lldmapFile.clear();
+  }
+
   if (shouldCreatePDB) {
     // Put the PDB next to the image if no /pdb flag was passed.
     if (config->pdbPath.empty()) {
@@ -2014,8 +2181,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
       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);
+      // Don't do this earlier, so that ctx.OutputFile is ready.
+      parsePDBAltPath();
     }
   }
 
@@ -2023,32 +2190,32 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   if (config->imageBase == uint64_t(-1))
     config->imageBase = getDefaultImageBase();
 
-  symtab->addSynthetic(mangle("__ImageBase"), nullptr);
+  ctx.symtab.addSynthetic(mangle("__ImageBase"), nullptr);
   if (config->machine == I386) {
-    symtab->addAbsolute("___safe_se_handler_table", 0);
-    symtab->addAbsolute("___safe_se_handler_count", 0);
+    ctx.symtab.addAbsolute("___safe_se_handler_table", 0);
+    ctx.symtab.addAbsolute("___safe_se_handler_count", 0);
   }
 
-  symtab->addAbsolute(mangle("__guard_fids_count"), 0);
-  symtab->addAbsolute(mangle("__guard_fids_table"), 0);
-  symtab->addAbsolute(mangle("__guard_flags"), 0);
-  symtab->addAbsolute(mangle("__guard_iat_count"), 0);
-  symtab->addAbsolute(mangle("__guard_iat_table"), 0);
-  symtab->addAbsolute(mangle("__guard_longjmp_count"), 0);
-  symtab->addAbsolute(mangle("__guard_longjmp_table"), 0);
+  ctx.symtab.addAbsolute(mangle("__guard_fids_count"), 0);
+  ctx.symtab.addAbsolute(mangle("__guard_fids_table"), 0);
+  ctx.symtab.addAbsolute(mangle("__guard_flags"), 0);
+  ctx.symtab.addAbsolute(mangle("__guard_iat_count"), 0);
+  ctx.symtab.addAbsolute(mangle("__guard_iat_table"), 0);
+  ctx.symtab.addAbsolute(mangle("__guard_longjmp_count"), 0);
+  ctx.symtab.addAbsolute(mangle("__guard_longjmp_table"), 0);
   // Needed for MSVC 2017 15.5 CRT.
-  symtab->addAbsolute(mangle("__enclave_config"), 0);
+  ctx.symtab.addAbsolute(mangle("__enclave_config"), 0);
   // Needed for MSVC 2019 16.8 CRT.
-  symtab->addAbsolute(mangle("__guard_eh_cont_count"), 0);
-  symtab->addAbsolute(mangle("__guard_eh_cont_table"), 0);
+  ctx.symtab.addAbsolute(mangle("__guard_eh_cont_count"), 0);
+  ctx.symtab.addAbsolute(mangle("__guard_eh_cont_table"), 0);
 
   if (config->pseudoRelocs) {
-    symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0);
-    symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0);
+    ctx.symtab.addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0);
+    ctx.symtab.addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0);
   }
   if (config->mingw) {
-    symtab->addAbsolute(mangle("__CTOR_LIST__"), 0);
-    symtab->addAbsolute(mangle("__DTOR_LIST__"), 0);
+    ctx.symtab.addAbsolute(mangle("__CTOR_LIST__"), 0);
+    ctx.symtab.addAbsolute(mangle("__DTOR_LIST__"), 0);
   }
 
   // This code may add new undefined symbols to the link, which may enqueue more
@@ -2074,12 +2241,12 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     for (auto pair : config->alternateNames) {
       StringRef from = pair.first;
       StringRef to = pair.second;
-      Symbol *sym = symtab->find(from);
+      Symbol *sym = ctx.symtab.find(from);
       if (!sym)
         continue;
       if (auto *u = dyn_cast<Undefined>(sym))
         if (!u->weakAlias)
-          u->weakAlias = symtab->addUndefined(to);
+          u->weakAlias = ctx.symtab.addUndefined(to);
     }
 
     // If any inputs are bitcode files, the LTO code generator may create
@@ -2087,25 +2254,24 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     // file's symbol table. If any of those library functions are defined 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.
-    if (!BitcodeFile::instances.empty())
+    if (!ctx.bitcodeFileInstances.empty())
       for (auto *s : lto::LTO::getRuntimeLibcallSymbols())
-        symtab->addLibcall(s);
+        ctx.symtab.addLibcall(s);
 
     // Windows specific -- if __load_config_used can be resolved, resolve it.
-    if (symtab->findUnderscore("_load_config_used"))
+    if (ctx.symtab.findUnderscore("_load_config_used"))
       addUndefined(mangle("_load_config_used"));
-  } while (run());
 
-  if (args.hasArg(OPT_include_optional)) {
-    // Handle /includeoptional
-    for (auto *arg : args.filtered(OPT_include_optional))
-      if (dyn_cast_or_null<LazyArchive>(symtab->find(arg->getValue())))
-        addUndefined(arg->getValue());
-    while (run());
-  }
+    if (args.hasArg(OPT_include_optional)) {
+      // Handle /includeoptional
+      for (auto *arg : args.filtered(OPT_include_optional))
+        if (isa_and_nonnull<LazyArchive>(ctx.symtab.find(arg->getValue())))
+          addUndefined(arg->getValue());
+    }
+  } while (run());
 
   // Create wrapped symbols for -wrap option.
-  std::vector<WrappedSymbol> wrapped = addWrappedSymbols(args);
+  std::vector<WrappedSymbol> wrapped = addWrappedSymbols(ctx, args);
   // Load more object files that might be needed for wrapped symbols.
   if (!wrapped.empty())
     while (run());
@@ -2131,7 +2297,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     // If it ends up pulling in more object files from static libraries,
     // (and maybe doing more stdcall fixups along the way), this would need
     // to loop these two calls.
-    symtab->loadMinGWSymbols();
+    ctx.symtab.loadMinGWSymbols();
     run();
   }
 
@@ -2139,8 +2305,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   // If we are going to do codegen for link-time optimization, check for
   // unresolvable symbols first, so we don't spend time generating code that
   // will fail to link anyway.
-  if (!BitcodeFile::instances.empty() && !config->forceUnresolved)
-    symtab->reportUnresolvable();
+  if (!ctx.bitcodeFileInstances.empty() && !config->forceUnresolved)
+    ctx.symtab.reportUnresolvable();
   if (errorCount())
     return;
 
@@ -2154,7 +2320,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   // Do LTO by compiling bitcode input files to a set of native COFF files then
   // link those files (unless -thinlto-index-only was given, in which case we
   // resolve symbols and write indices, but don't generate native code or link).
-  symtab->addCombinedLTOObjects();
+  ctx.symtab.compileBitcodeFiles();
 
   // If -thinlto-index-only is given, we should create only "index
   // files" and not object files. Index file creation is already done
@@ -2168,10 +2334,10 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
 
   // Apply symbol renames for -wrap.
   if (!wrapped.empty())
-    wrapSymbols(wrapped);
+    wrapSymbols(ctx, wrapped);
 
   // Resolve remaining undefined symbols and warn about imported locals.
-  symtab->resolveRemainingUndefines();
+  ctx.symtab.resolveRemainingUndefines();
   if (errorCount())
     return;
 
@@ -2182,12 +2348,12 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     // order provided on the command line, while lld will pull in needed
     // files from static libraries only after the last object file on the
     // command line.
-    for (auto i = ObjFile::instances.begin(), e = ObjFile::instances.end();
+    for (auto i = ctx.objFileInstances.begin(), e = ctx.objFileInstances.end();
          i != e; i++) {
       ObjFile *file = *i;
       if (isCrtend(file->getName())) {
-        ObjFile::instances.erase(i);
-        ObjFile::instances.push_back(file);
+        ctx.objFileInstances.erase(i);
+        ctx.objFileInstances.push_back(file);
         break;
       }
     }
@@ -2198,21 +2364,21 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   // -implib option is given explicitly, for compatibility with GNU ld.
   if (!config->exports.empty() || config->dll) {
     fixupExports();
-    if (!config->mingw || !config->implib.empty())
+    if (!config->noimplib && (!config->mingw || !config->implib.empty()))
       createImportLibrary(/*asLib=*/false);
     assignExportOrdinals();
   }
 
   // Handle /output-def (MinGW specific).
   if (auto *arg = args.getLastArg(OPT_output_def))
-    writeDefFile(arg->getValue());
+    writeDefFile(arg->getValue(), config->exports);
 
   // Set extra alignment for .comm symbols
   for (auto pair : config->alignComm) {
     StringRef name = pair.first;
     uint32_t alignment = pair.second;
 
-    Symbol *sym = symtab->find(name);
+    Symbol *sym = ctx.symtab.find(name);
     if (!sym) {
       warn("/aligncomm symbol " + name + " not found");
       continue;
@@ -2228,8 +2394,14 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     c->setAlignment(std::max(c->getAlignment(), alignment));
   }
 
-  // Windows specific -- Create a side-by-side manifest file.
-  if (config->manifest == Configuration::SideBySide)
+  // Windows specific -- Create an embedded or side-by-side manifest.
+  // /manifestdependency: enables /manifest unless an explicit /manifest:no is
+  // also passed.
+  if (config->manifest == Configuration::Embed)
+    addBuffer(createManifestRes(), false, false);
+  else if (config->manifest == Configuration::SideBySide ||
+           (config->manifest == Configuration::Default &&
+            !config->manifestDependencies.empty()))
     createSideBySideManifest();
 
   // Handle /order. We want to do this at this moment because we
@@ -2247,7 +2419,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     if (auto *arg = args.getLastArg(OPT_call_graph_ordering_file)) {
       parseCallGraphFile(arg->getValue());
     }
-    readCallGraphsFromObjectFiles();
+    readCallGraphsFromObjectFiles(ctx);
   }
 
   // Handle /print-symbol-order.
@@ -2263,8 +2435,9 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
       // For now, just manually try to retain the known possible personality
       // functions. This doesn't bring in more object files, but only marks
       // functions that already have been included to be retained.
-      for (const char *n : {"__gxx_personality_v0", "__gcc_personality_v0"}) {
-        Defined *d = dyn_cast_or_null<Defined>(symtab->findUnderscore(n));
+      for (const char *n : {"__gxx_personality_v0", "__gcc_personality_v0",
+                            "rust_eh_personality"}) {
+        Defined *d = dyn_cast_or_null<Defined>(ctx.symtab.findUnderscore(n));
         if (d && !d->isGCRoot) {
           d->isGCRoot = true;
           config->gcroot.push_back(d);
@@ -2272,7 +2445,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
       }
     }
 
-    markLive(symtab->getChunks());
+    markLive(ctx);
   }
 
   // Needs to happen after the last call to addFile().
@@ -2280,18 +2453,17 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
 
   // Identify identical COMDAT sections to merge them.
   if (config->doICF != ICFLevel::None) {
-    findKeepUniqueSections();
-    doICF(symtab->getChunks(), config->doICF);
+    findKeepUniqueSections(ctx);
+    doICF(ctx);
   }
 
   // Write the result.
-  writeResult();
+  writeResult(ctx);
 
   // Stop early so we can print the results.
   rootTimer.stop();
   if (config->showTiming)
-    Timer::root().print();
+    ctx.rootTimer.print();
 }
 
-} // namespace coff
-} // namespace lld
+} // namespace lld::coff
index 5729bed..8ec3bb4 100644 (file)
@@ -13,7 +13,6 @@
 #include "SymbolTable.h"
 #include "lld/Common/LLVM.h"
 #include "lld/Common/Reproduce.h"
-#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSet.h"
 #include "llvm/Object/Archive.h"
 #include "llvm/Option/ArgList.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/TarWriter.h"
+#include "llvm/WindowsDriver/MSVCPaths.h"
 #include <memory>
+#include <optional>
 #include <set>
 #include <vector>
 
-namespace lld {
-namespace coff {
-
-class LinkerDriver;
-extern LinkerDriver *driver;
+namespace lld::coff {
 
 using llvm::COFF::MachineTypes;
 using llvm::COFF::WindowsSubsystem;
-using llvm::Optional;
+using std::optional;
 
-class COFFOptTable : public llvm::opt::OptTable {
+class COFFOptTable : public llvm::opt::GenericOptTable {
 public:
   COFFOptTable();
 };
 
-// Constructing the option table is expensive. Use a global table to avoid doing
-// it more than once.
-extern COFFOptTable optTable;
-
 // The result of parsing the .drective section. The /export: and /include:
 // options are handled separately because they reference symbols, and the number
 // of symbols can be quite large. The LLVM Option library will perform at least
@@ -53,11 +46,14 @@ extern COFFOptTable optTable;
 struct ParsedDirectives {
   std::vector<StringRef> exports;
   std::vector<StringRef> includes;
+  std::vector<StringRef> excludes;
   llvm::opt::InputArgList args;
 };
 
 class ArgParser {
 public:
+  ArgParser(COFFLinkerContext &ctx);
+
   // Parses command line options.
   llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> args);
 
@@ -74,12 +70,20 @@ private:
   void addLINK(SmallVector<const char *, 256> &argv);
 
   std::vector<const char *> tokenize(StringRef s);
+
+  COFFLinkerContext &ctx;
 };
 
 class LinkerDriver {
 public:
+  LinkerDriver(COFFLinkerContext &ctx) : ctx(ctx) {}
+
   void linkerMain(llvm::ArrayRef<const char *> args);
 
+  // Adds various search paths based on the sysroot.  Must only be called once
+  // config->machine has been set.
+  void addWinSysRootLibSearchPaths();
+
   // Used by the resolver to parse .drectve section contents.
   void parseDirectives(InputFile *file);
 
@@ -97,12 +101,53 @@ public:
 
 private:
   // Searches a file from search paths.
-  Optional<StringRef> findFile(StringRef filename);
-  Optional<StringRef> findLib(StringRef filename);
+  std::optional<StringRef> findFile(StringRef filename);
+  std::optional<StringRef> findLib(StringRef filename);
   StringRef doFindFile(StringRef filename);
   StringRef doFindLib(StringRef filename);
   StringRef doFindLibMinGW(StringRef filename);
 
+  bool findUnderscoreMangle(StringRef sym);
+
+  // Determines the location of the sysroot based on `args`, environment, etc.
+  void detectWinSysRoot(const llvm::opt::InputArgList &args);
+
+  // Symbol names are mangled by prepending "_" on x86.
+  StringRef mangle(StringRef sym);
+
+  llvm::Triple::ArchType getArch();
+
+  uint64_t getDefaultImageBase();
+
+  bool isDecorated(StringRef sym);
+
+  std::string getMapFile(const llvm::opt::InputArgList &args,
+                         llvm::opt::OptSpecifier os,
+                         llvm::opt::OptSpecifier osFile);
+
+  std::string getImplibPath();
+
+  // The import name is calculated as follows:
+  //
+  //        | LIBRARY w/ ext |   LIBRARY w/o ext   | no LIBRARY
+  //   -----+----------------+---------------------+------------------
+  //   LINK | {value}        | {value}.{.dll/.exe} | {output name}
+  //    LIB | {value}        | {value}.dll         | {output name}.dll
+  //
+  std::string getImportName(bool asLib);
+
+  void createImportLibrary(bool asLib);
+
+  void parseModuleDefs(StringRef path);
+
+  // Parse an /order file. If an option is given, the linker places COMDAT
+  // sections int he same order as their names appear in the given file.
+  void parseOrderFile(StringRef arg);
+
+  void parseCallGraphFile(StringRef path);
+
+  void parsePDBAltPath();
+
   // Parses LIB environment which contains a list of search paths.
   void addLibSearchPaths();
 
@@ -147,61 +192,80 @@ private:
   std::vector<StringRef> filePaths;
   std::vector<MemoryBufferRef> resources;
 
-  llvm::StringSet<> directivesExports;
-};
+  llvm::DenseSet<StringRef> directivesExports;
+  llvm::DenseSet<StringRef> excludedSymbols;
+
+  COFFLinkerContext &ctx;
 
-// Functions below this line are defined in DriverUtils.cpp.
+  llvm::ToolsetLayout vsLayout = llvm::ToolsetLayout::OlderVS;
+  std::string vcToolChainPath;
+  llvm::SmallString<128> diaPath;
+  bool useWinSysRootLibPath = false;
+  llvm::SmallString<128> universalCRTLibPath;
+  int sdkMajor = 0;
+  llvm::SmallString<128> windowsSdkLibPath;
 
-void printHelp(const char *argv0);
+  // Functions below this line are defined in DriverUtils.cpp.
 
-// Parses a string in the form of "<integer>[,<integer>]".
-void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size = nullptr);
+  void printHelp(const char *argv0);
 
-void parseGuard(StringRef arg);
+  // Parses a string in the form of "<integer>[,<integer>]".
+  void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size = nullptr);
 
-// Parses a string in the form of "<integer>[.<integer>]".
-// Minor's default value is 0.
-void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor);
+  void parseGuard(StringRef arg);
 
-// Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]".
-void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major,
-                    uint32_t *minor, bool *gotVersion = nullptr);
+  // Parses a string in the form of "<integer>[.<integer>]".
+  // Minor's default value is 0.
+  void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor);
 
-void parseAlternateName(StringRef);
-void parseMerge(StringRef);
-void parseSection(StringRef);
-void parseAligncomm(StringRef);
+  // Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]".
+  void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major,
+                      uint32_t *minor, bool *gotVersion = nullptr);
 
-// Parses a string in the form of "[:<integer>]"
-void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine);
+  void parseAlternateName(StringRef);
+  void parseMerge(StringRef);
+  void parsePDBPageSize(StringRef);
+  void parseSection(StringRef);
+  void parseAligncomm(StringRef);
 
-// Parses a string in the form of "EMBED[,=<integer>]|NO".
-void parseManifest(StringRef arg);
+  // Parses a string in the form of "[:<integer>]"
+  void parseFunctionPadMin(llvm::opt::Arg *a);
 
-// Parses a string in the form of "level=<string>|uiAccess=<string>"
-void parseManifestUAC(StringRef arg);
+  // Parses a string in the form of "EMBED[,=<integer>]|NO".
+  void parseManifest(StringRef arg);
 
-// Parses a string in the form of "cd|net[,(cd|net)]*"
-void parseSwaprun(StringRef arg);
+  // Parses a string in the form of "level=<string>|uiAccess=<string>"
+  void parseManifestUAC(StringRef arg);
 
-// Create a resource file containing a manifest XML.
-std::unique_ptr<MemoryBuffer> createManifestRes();
-void createSideBySideManifest();
+  // Parses a string in the form of "cd|net[,(cd|net)]*"
+  void parseSwaprun(StringRef arg);
 
-// Used for dllexported symbols.
-Export parseExport(StringRef arg);
-void fixupExports();
-void assignExportOrdinals();
+  // Create a resource file containing a manifest XML.
+  std::unique_ptr<MemoryBuffer> createManifestRes();
+  void createSideBySideManifest();
+  std::string createDefaultXml();
+  std::string createManifestXmlWithInternalMt(StringRef defaultXml);
+  std::string createManifestXmlWithExternalMt(StringRef defaultXml);
+  std::string createManifestXml();
 
-// Parses a string in the form of "key=value" and check
-// if value matches previous values for the key.
-// This feature used in the directive section to reject
-// incompatible objects.
-void checkFailIfMismatch(StringRef arg, InputFile *source);
+  std::unique_ptr<llvm::WritableMemoryBuffer>
+  createMemoryBufferForManifestRes(size_t manifestRes);
 
-// Convert Windows resource files (.res files) to a .obj file.
-MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
-                                 ArrayRef<ObjFile *> objs);
+  // Used for dllexported symbols.
+  Export parseExport(StringRef arg);
+  void fixupExports();
+  void assignExportOrdinals();
+
+  // Parses a string in the form of "key=value" and check
+  // if value matches previous values for the key.
+  // This feature used in the directive section to reject
+  // incompatible objects.
+  void checkFailIfMismatch(StringRef arg, InputFile *source);
+
+  // Convert Windows resource files (.res files) to a .obj file.
+  MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
+                                   ArrayRef<ObjFile *> objs);
+};
 
 // Create enum with OPT_xxx values for each option in Options.td
 enum {
@@ -211,7 +275,6 @@ enum {
 #undef OPTION
 };
 
-} // namespace coff
-} // namespace lld
+} // namespace lld::coff
 
 #endif
index b5abe8b..d70f53a 100644 (file)
 //
 //===----------------------------------------------------------------------===//
 
-#include "Config.h"
+#include "COFFLinkerContext.h"
 #include "Driver.h"
 #include "Symbols.h"
 #include "lld/Common/ErrorHandler.h"
 #include "lld/Common/Memory.h"
-#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/BinaryFormat/COFF.h"
 #include "llvm/Object/COFF.h"
@@ -34,6 +34,7 @@
 #include "llvm/WindowsManifest/WindowsManifestMerger.h"
 #include <limits>
 #include <memory>
+#include <optional>
 
 using namespace llvm::COFF;
 using namespace llvm;
@@ -48,17 +49,17 @@ const uint16_t RT_MANIFEST = 24;
 
 class Executor {
 public:
-  explicit Executor(StringRef s) : prog(saver.save(s)) {}
-  void add(StringRef s) { args.push_back(saver.save(s)); }
-  void add(std::string &s) { args.push_back(saver.save(s)); }
-  void add(Twine s) { args.push_back(saver.save(s)); }
-  void add(const char *s) { args.push_back(saver.save(s)); }
+  explicit Executor(StringRef s) : prog(saver().save(s)) {}
+  void add(StringRef s) { args.push_back(saver().save(s)); }
+  void add(std::string &s) { args.push_back(saver().save(s)); }
+  void add(Twine s) { args.push_back(saver().save(s)); }
+  void add(const char *s) { args.push_back(saver().save(s)); }
 
   void run() {
     ErrorOr<std::string> exeOrErr = sys::findProgramByName(prog);
     if (auto ec = exeOrErr.getError())
       fatal("unable to find " + prog + " in PATH: " + ec.message());
-    StringRef exe = saver.save(*exeOrErr);
+    StringRef exe = saver().save(*exeOrErr);
     args.insert(args.begin(), exe);
 
     if (sys::ExecuteAndWait(args[0], args) != 0)
@@ -74,9 +75,8 @@ private:
 } // anonymous namespace
 
 // Parses a string in the form of "<integer>[,<integer>]".
-void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size) {
-  StringRef s1, s2;
-  std::tie(s1, s2) = arg.split(',');
+void LinkerDriver::parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size) {
+  auto [s1, s2] = arg.split(',');
   if (s1.getAsInteger(0, *addr))
     fatal("invalid number: " + s1);
   if (size && !s2.empty() && s2.getAsInteger(0, *size))
@@ -85,9 +85,9 @@ void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size) {
 
 // Parses a string in the form of "<integer>[.<integer>]".
 // If second number is not present, Minor is set to 0.
-void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor) {
-  StringRef s1, s2;
-  std::tie(s1, s2) = arg.split('.');
+void LinkerDriver::parseVersion(StringRef arg, uint32_t *major,
+                                uint32_t *minor) {
+  auto [s1, s2] = arg.split('.');
   if (s1.getAsInteger(10, *major))
     fatal("invalid number: " + s1);
   *minor = 0;
@@ -95,32 +95,30 @@ void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor) {
     fatal("invalid number: " + s2);
 }
 
-void parseGuard(StringRef fullArg) {
+void LinkerDriver::parseGuard(StringRef fullArg) {
   SmallVector<StringRef, 1> splitArgs;
   fullArg.split(splitArgs, ",");
   for (StringRef arg : splitArgs) {
     if (arg.equals_insensitive("no"))
-      config->guardCF = GuardCFLevel::Off;
+      ctx.config.guardCF = GuardCFLevel::Off;
     else if (arg.equals_insensitive("nolongjmp"))
-      config->guardCF &= ~GuardCFLevel::LongJmp;
+      ctx.config.guardCF &= ~GuardCFLevel::LongJmp;
     else if (arg.equals_insensitive("noehcont"))
-      config->guardCF &= ~GuardCFLevel::EHCont;
-    else if (arg.equals_insensitive("cf"))
-      config->guardCF = GuardCFLevel::CF;
-    else if (arg.equals_insensitive("longjmp"))
-      config->guardCF |= GuardCFLevel::CF | GuardCFLevel::LongJmp;
+      ctx.config.guardCF &= ~GuardCFLevel::EHCont;
+    else if (arg.equals_insensitive("cf") || arg.equals_insensitive("longjmp"))
+      ctx.config.guardCF |= GuardCFLevel::CF | GuardCFLevel::LongJmp;
     else if (arg.equals_insensitive("ehcont"))
-      config->guardCF |= GuardCFLevel::CF | GuardCFLevel::EHCont;
+      ctx.config.guardCF |= GuardCFLevel::CF | GuardCFLevel::EHCont;
     else
       fatal("invalid argument to /guard: " + arg);
   }
 }
 
 // Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]".
-void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major,
-                    uint32_t *minor, bool *gotVersion) {
-  StringRef sysStr, ver;
-  std::tie(sysStr, ver) = arg.split(',');
+void LinkerDriver::parseSubsystem(StringRef arg, WindowsSubsystem *sys,
+                                  uint32_t *major, uint32_t *minor,
+                                  bool *gotVersion) {
+  auto [sysStr, ver] = arg.split(',');
   std::string sysStrLower = sysStr.lower();
   *sys = StringSwitch<WindowsSubsystem>(sysStrLower)
     .Case("boot_application", IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION)
@@ -144,29 +142,27 @@ void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major,
 
 // Parse a string of the form of "<from>=<to>".
 // Results are directly written to Config.
-void parseAlternateName(StringRef s) {
-  StringRef from, to;
-  std::tie(from, to) = s.split('=');
+void LinkerDriver::parseAlternateName(StringRef s) {
+  auto [from, to] = s.split('=');
   if (from.empty() || to.empty())
     fatal("/alternatename: invalid argument: " + s);
-  auto it = config->alternateNames.find(from);
-  if (it != config->alternateNames.end() && it->second != to)
+  auto it = ctx.config.alternateNames.find(from);
+  if (it != ctx.config.alternateNames.end() && it->second != to)
     fatal("/alternatename: conflicts: " + s);
-  config->alternateNames.insert(it, std::make_pair(from, to));
+  ctx.config.alternateNames.insert(it, std::make_pair(from, to));
 }
 
 // Parse a string of the form of "<from>=<to>".
 // Results are directly written to Config.
-void parseMerge(StringRef s) {
-  StringRef from, to;
-  std::tie(from, to) = s.split('=');
+void LinkerDriver::parseMerge(StringRef s) {
+  auto [from, to] = s.split('=');
   if (from.empty() || to.empty())
     fatal("/merge: invalid argument: " + s);
   if (from == ".rsrc" || to == ".rsrc")
     fatal("/merge: cannot merge '.rsrc' with any section");
   if (from == ".reloc" || to == ".reloc")
     fatal("/merge: cannot merge '.reloc' with any section");
-  auto pair = config->merge.insert(std::make_pair(from, to));
+  auto pair = ctx.config.merge.insert(std::make_pair(from, to));
   bool inserted = pair.second;
   if (!inserted) {
     StringRef existing = pair.first->second;
@@ -175,6 +171,20 @@ void parseMerge(StringRef s) {
   }
 }
 
+void LinkerDriver::parsePDBPageSize(StringRef s) {
+  int v;
+  if (s.getAsInteger(0, v)) {
+    error("/pdbpagesize: invalid argument: " + s);
+    return;
+  }
+  if (v != 4096 && v != 8192 && v != 16384 && v != 32768) {
+    error("/pdbpagesize: invalid argument: " + s);
+    return;
+  }
+
+  ctx.config.pdbPageSize = v;
+}
+
 static uint32_t parseSectionAttributes(StringRef s) {
   uint32_t ret = 0;
   for (char c : s.lower()) {
@@ -208,18 +218,16 @@ static uint32_t parseSectionAttributes(StringRef s) {
 }
 
 // Parses /section option argument.
-void parseSection(StringRef s) {
-  StringRef name, attrs;
-  std::tie(name, attrs) = s.split(',');
+void LinkerDriver::parseSection(StringRef s) {
+  auto [name, attrs] = s.split(',');
   if (name.empty() || attrs.empty())
     fatal("/section: invalid argument: " + s);
-  config->section[name] = parseSectionAttributes(attrs);
+  ctx.config.section[name] = parseSectionAttributes(attrs);
 }
 
 // Parses /aligncomm option argument.
-void parseAligncomm(StringRef s) {
-  StringRef name, align;
-  std::tie(name, align) = s.split(',');
+void LinkerDriver::parseAligncomm(StringRef s) {
+  auto [name, align] = s.split(',');
   if (name.empty() || align.empty()) {
     error("/aligncomm: invalid argument: " + s);
     return;
@@ -229,56 +237,57 @@ void parseAligncomm(StringRef s) {
     error("/aligncomm: invalid argument: " + s);
     return;
   }
-  config->alignComm[std::string(name)] =
-      std::max(config->alignComm[std::string(name)], 1 << v);
+  ctx.config.alignComm[std::string(name)] =
+      std::max(ctx.config.alignComm[std::string(name)], 1 << v);
 }
 
 // Parses /functionpadmin option argument.
-void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine) {
+void LinkerDriver::parseFunctionPadMin(llvm::opt::Arg *a) {
   StringRef arg = a->getNumValues() ? a->getValue() : "";
   if (!arg.empty()) {
     // Optional padding in bytes is given.
-    if (arg.getAsInteger(0, config->functionPadMin))
+    if (arg.getAsInteger(0, ctx.config.functionPadMin))
       error("/functionpadmin: invalid argument: " + arg);
     return;
   }
   // No optional argument given.
   // Set default padding based on machine, similar to link.exe.
   // There is no default padding for ARM platforms.
-  if (machine == I386) {
-    config->functionPadMin = 5;
-  } else if (machine == AMD64) {
-    config->functionPadMin = 6;
+  if (ctx.config.machine == I386) {
+    ctx.config.functionPadMin = 5;
+  } else if (ctx.config.machine == AMD64) {
+    ctx.config.functionPadMin = 6;
   } else {
     error("/functionpadmin: invalid argument for this machine: " + arg);
   }
 }
 
 // Parses a string in the form of "EMBED[,=<integer>]|NO".
-// Results are directly written to Config.
-void parseManifest(StringRef arg) {
+// Results are directly written to
+// Config.
+void LinkerDriver::parseManifest(StringRef arg) {
   if (arg.equals_insensitive("no")) {
-    config->manifest = Configuration::No;
+    ctx.config.manifest = Configuration::No;
     return;
   }
   if (!arg.startswith_insensitive("embed"))
     fatal("invalid option " + arg);
-  config->manifest = Configuration::Embed;
+  ctx.config.manifest = Configuration::Embed;
   arg = arg.substr(strlen("embed"));
   if (arg.empty())
     return;
   if (!arg.startswith_insensitive(",id="))
     fatal("invalid option " + arg);
   arg = arg.substr(strlen(",id="));
-  if (arg.getAsInteger(0, config->manifestID))
+  if (arg.getAsInteger(0, ctx.config.manifestID))
     fatal("invalid option " + arg);
 }
 
 // Parses a string in the form of "level=<string>|uiAccess=<string>|NO".
 // Results are directly written to Config.
-void parseManifestUAC(StringRef arg) {
+void LinkerDriver::parseManifestUAC(StringRef arg) {
   if (arg.equals_insensitive("no")) {
-    config->manifestUAC = false;
+    ctx.config.manifestUAC = false;
     return;
   }
   for (;;) {
@@ -287,12 +296,12 @@ void parseManifestUAC(StringRef arg) {
       return;
     if (arg.startswith_insensitive("level=")) {
       arg = arg.substr(strlen("level="));
-      std::tie(config->manifestLevel, arg) = arg.split(" ");
+      std::tie(ctx.config.manifestLevel, arg) = arg.split(" ");
       continue;
     }
     if (arg.startswith_insensitive("uiaccess=")) {
       arg = arg.substr(strlen("uiaccess="));
-      std::tie(config->manifestUIAccess, arg) = arg.split(" ");
+      std::tie(ctx.config.manifestUIAccess, arg) = arg.split(" ");
       continue;
     }
     fatal("invalid option " + arg);
@@ -301,14 +310,13 @@ void parseManifestUAC(StringRef arg) {
 
 // Parses a string in the form of "cd|net[,(cd|net)]*"
 // Results are directly written to Config.
-void parseSwaprun(StringRef arg) {
+void LinkerDriver::parseSwaprun(StringRef arg) {
   do {
-    StringRef swaprun, newArg;
-    std::tie(swaprun, newArg) = arg.split(',');
+    auto [swaprun, newArg] = arg.split(',');
     if (swaprun.equals_insensitive("cd"))
-      config->swaprunCD = true;
+      ctx.config.swaprunCD = true;
     else if (swaprun.equals_insensitive("net"))
-      config->swaprunNet = true;
+      ctx.config.swaprunNet = true;
     else if (swaprun.empty())
       error("/swaprun: missing argument");
     else
@@ -366,7 +374,7 @@ public:
 };
 }
 
-static std::string createDefaultXml() {
+std::string LinkerDriver::createDefaultXml() {
   std::string ret;
   raw_string_ostream os(ret);
 
@@ -375,20 +383,20 @@ static std::string createDefaultXml() {
   os << "<?xml version=\"1.0\" standalone=\"yes\"?>\n"
      << "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\"\n"
      << "          manifestVersion=\"1.0\">\n";
-  if (config->manifestUAC) {
+  if (ctx.config.manifestUAC) {
     os << "  <trustInfo>\n"
        << "    <security>\n"
        << "      <requestedPrivileges>\n"
-       << "         <requestedExecutionLevel level=" << config->manifestLevel
-       << " uiAccess=" << config->manifestUIAccess << "/>\n"
+       << "         <requestedExecutionLevel level=" << ctx.config.manifestLevel
+       << " uiAccess=" << ctx.config.manifestUIAccess << "/>\n"
        << "      </requestedPrivileges>\n"
        << "    </security>\n"
        << "  </trustInfo>\n";
   }
-  if (!config->manifestDependency.empty()) {
+  for (auto manifestDependency : ctx.config.manifestDependencies) {
     os << "  <dependency>\n"
        << "    <dependentAssembly>\n"
-       << "      <assemblyIdentity " << config->manifestDependency << " />\n"
+       << "      <assemblyIdentity " << manifestDependency << " />\n"
        << "    </dependentAssembly>\n"
        << "  </dependency>\n";
   }
@@ -396,7 +404,8 @@ static std::string createDefaultXml() {
   return os.str();
 }
 
-static std::string createManifestXmlWithInternalMt(StringRef defaultXml) {
+std::string
+LinkerDriver::createManifestXmlWithInternalMt(StringRef defaultXml) {
   std::unique_ptr<MemoryBuffer> defaultXmlCopy =
       MemoryBuffer::getMemBufferCopy(defaultXml);
 
@@ -405,10 +414,11 @@ static std::string createManifestXmlWithInternalMt(StringRef defaultXml) {
     fatal("internal manifest tool failed on default xml: " +
           toString(std::move(e)));
 
-  for (StringRef filename : config->manifestInput) {
+  for (StringRef filename : ctx.config.manifestInput) {
     std::unique_ptr<MemoryBuffer> manifest =
         check(MemoryBuffer::getFile(filename));
-    if (auto e = merger.merge(*manifest.get()))
+    // Call takeBuffer to include in /reproduce: output if applicable.
+    if (auto e = merger.merge(takeBuffer(std::move(manifest))))
       fatal("internal manifest tool failed on file " + filename + ": " +
             toString(std::move(e)));
   }
@@ -416,7 +426,8 @@ static std::string createManifestXmlWithInternalMt(StringRef defaultXml) {
   return std::string(merger.getMergedManifest().get()->getBuffer());
 }
 
-static std::string createManifestXmlWithExternalMt(StringRef defaultXml) {
+std::string
+LinkerDriver::createManifestXmlWithExternalMt(StringRef defaultXml) {
   // Create the default manifest file as a temporary file.
   TemporaryFile Default("defaultxml", "manifest");
   std::error_code ec;
@@ -433,9 +444,14 @@ static std::string createManifestXmlWithExternalMt(StringRef defaultXml) {
   Executor e("mt.exe");
   e.add("/manifest");
   e.add(Default.path);
-  for (StringRef filename : config->manifestInput) {
+  for (StringRef filename : ctx.config.manifestInput) {
     e.add("/manifest");
     e.add(filename);
+
+    // Manually add the file to the /reproduce: tar if needed.
+    if (tar)
+      if (auto mbOrErr = MemoryBuffer::getFile(filename))
+        takeBuffer(std::move(*mbOrErr));
   }
   e.add("/nologo");
   e.add("/out:" + StringRef(user.path));
@@ -447,9 +463,9 @@ static std::string createManifestXmlWithExternalMt(StringRef defaultXml) {
           ->getBuffer());
 }
 
-static std::string createManifestXml() {
+std::string LinkerDriver::createManifestXml() {
   std::string defaultXml = createDefaultXml();
-  if (config->manifestInput.empty())
+  if (ctx.config.manifestInput.empty())
     return defaultXml;
 
   if (windows_manifest::isAvailable())
@@ -458,14 +474,14 @@ static std::string createManifestXml() {
   return createManifestXmlWithExternalMt(defaultXml);
 }
 
-static std::unique_ptr<WritableMemoryBuffer>
-createMemoryBufferForManifestRes(size_t manifestSize) {
+std::unique_ptr<WritableMemoryBuffer>
+LinkerDriver::createMemoryBufferForManifestRes(size_t manifestSize) {
   size_t resSize = alignTo(
       object::WIN_RES_MAGIC_SIZE + object::WIN_RES_NULL_ENTRY_SIZE +
           sizeof(object::WinResHeaderPrefix) + sizeof(object::WinResIDs) +
           sizeof(object::WinResHeaderSuffix) + manifestSize,
       object::WIN_RES_DATA_ALIGNMENT);
-  return WritableMemoryBuffer::getNewMemBuffer(resSize, config->outputFile +
+  return WritableMemoryBuffer::getNewMemBuffer(resSize, ctx.config.outputFile +
                                                             ".manifest.res");
 }
 
@@ -476,7 +492,8 @@ static void writeResFileHeader(char *&buf) {
   buf += object::WIN_RES_NULL_ENTRY_SIZE;
 }
 
-static void writeResEntryHeader(char *&buf, size_t manifestSize) {
+static void writeResEntryHeader(char *&buf, size_t manifestSize,
+                                int manifestID) {
   // Write the prefix.
   auto *prefix = reinterpret_cast<object::WinResHeaderPrefix *>(buf);
   prefix->DataSize = manifestSize;
@@ -488,7 +505,7 @@ static void writeResEntryHeader(char *&buf, size_t manifestSize) {
   // Write the Type/Name IDs.
   auto *iDs = reinterpret_cast<object::WinResIDs *>(buf);
   iDs->setType(RT_MANIFEST);
-  iDs->setName(config->manifestID);
+  iDs->setName(manifestID);
   buf += sizeof(object::WinResIDs);
 
   // Write the suffix.
@@ -502,7 +519,7 @@ static void writeResEntryHeader(char *&buf, size_t manifestSize) {
 }
 
 // Create a resource file containing a manifest XML.
-std::unique_ptr<MemoryBuffer> createManifestRes() {
+std::unique_ptr<MemoryBuffer> LinkerDriver::createManifestRes() {
   std::string manifest = createManifestXml();
 
   std::unique_ptr<WritableMemoryBuffer> res =
@@ -510,17 +527,17 @@ std::unique_ptr<MemoryBuffer> createManifestRes() {
 
   char *buf = res->getBufferStart();
   writeResFileHeader(buf);
-  writeResEntryHeader(buf, manifest.size());
+  writeResEntryHeader(buf, manifest.size(), ctx.config.manifestID);
 
   // Copy the manifest data into the .res file.
   std::copy(manifest.begin(), manifest.end(), buf);
   return std::move(res);
 }
 
-void createSideBySideManifest() {
-  std::string path = std::string(config->manifestFile);
+void LinkerDriver::createSideBySideManifest() {
+  std::string path = std::string(ctx.config.manifestFile);
   if (path == "")
-    path = config->outputFile + ".manifest";
+    path = ctx.config.outputFile + ".manifest";
   std::error_code ec;
   raw_fd_ostream out(path, ec, sys::fs::OF_TextWithCRLF);
   if (ec)
@@ -532,7 +549,7 @@ void createSideBySideManifest() {
 // "<name>[=<internalname>][,@ordinal[,NONAME]][,DATA][,PRIVATE]"
 // or "<name>=<dllname>.<name>".
 // Used for parsing /export arguments.
-Export parseExport(StringRef arg) {
+Export LinkerDriver::parseExport(StringRef arg) {
   Export e;
   StringRef rest;
   std::tie(e.name, rest) = arg.split(",");
@@ -540,8 +557,7 @@ Export parseExport(StringRef arg) {
     goto err;
 
   if (e.name.contains('=')) {
-    StringRef x, y;
-    std::tie(x, y) = e.name.split("=");
+    auto [x, y] = e.name.split("=");
 
     // If "<name>=<dllname>.<name>".
     if (y.contains(".")) {
@@ -595,14 +611,14 @@ err:
   fatal("invalid /export: " + arg);
 }
 
-static StringRef undecorate(StringRef sym) {
-  if (config->machine != I386)
+static StringRef undecorate(COFFLinkerContext &ctx, StringRef sym) {
+  if (ctx.config.machine != I386)
     return sym;
   // In MSVC mode, a fully decorated stdcall function is exported
   // as-is with the leading underscore (with type IMPORT_NAME).
   // In MinGW mode, a decorated stdcall function gets the underscore
   // removed, just like normal cdecl functions.
-  if (sym.startswith("_") && sym.contains('@') && !config->mingw)
+  if (sym.startswith("_") && sym.contains('@') && !ctx.config.mingw)
     return sym;
   return sym.startswith("_") ? sym.substr(1) : sym;
 }
@@ -616,39 +632,39 @@ static StringRef killAt(StringRef sym, bool prefix) {
   sym = sym.substr(0, sym.find('@', 1));
   if (!sym.startswith("@")) {
     if (prefix && !sym.startswith("_"))
-      return saver.save("_" + sym);
+      return saver().save("_" + sym);
     return sym;
   }
   // For fastcall, remove the leading @ and replace it with an
   // underscore, if prefixes are used.
   sym = sym.substr(1);
   if (prefix)
-    sym = saver.save("_" + sym);
+    sym = saver().save("_" + sym);
   return sym;
 }
 
 // Performs error checking on all /export arguments.
 // It also sets ordinals.
-void fixupExports() {
+void LinkerDriver::fixupExports() {
   // Symbol ordinals must be unique.
   std::set<uint16_t> ords;
-  for (Export &e : config->exports) {
+  for (Export &e : ctx.config.exports) {
     if (e.ordinal == 0)
       continue;
     if (!ords.insert(e.ordinal).second)
       fatal("duplicate export ordinal: " + e.name);
   }
 
-  for (Export &e : config->exports) {
+  for (Export &e : ctx.config.exports) {
     if (!e.forwardTo.empty()) {
-      e.exportName = undecorate(e.name);
+      e.exportName = undecorate(ctx, e.name);
     } else {
-      e.exportName = undecorate(e.extName.empty() ? e.name : e.extName);
+      e.exportName = undecorate(ctx, e.extName.empty() ? e.name : e.extName);
     }
   }
 
-  if (config->killAt && config->machine == I386) {
-    for (Export &e : config->exports) {
+  if (ctx.config.killAt && ctx.config.machine == I386) {
+    for (Export &e : ctx.config.exports) {
       e.name = killAt(e.name, true);
       e.exportName = killAt(e.exportName, false);
       e.extName = killAt(e.extName, true);
@@ -657,9 +673,9 @@ void fixupExports() {
   }
 
   // Uniquefy by name.
-  DenseMap<StringRef, Export *> map(config->exports.size());
+  DenseMap<StringRef, Export *> map(ctx.config.exports.size());
   std::vector<Export> v;
-  for (Export &e : config->exports) {
+  for (Export &e : ctx.config.exports) {
     auto pair = map.insert(std::make_pair(e.exportName, &e));
     bool inserted = pair.second;
     if (inserted) {
@@ -671,36 +687,34 @@ void fixupExports() {
       continue;
     warn("duplicate /export option: " + e.name);
   }
-  config->exports = std::move(v);
+  ctx.config.exports = std::move(v);
 
   // Sort by name.
-  std::sort(config->exports.begin(), config->exports.end(),
-            [](const Export &a, const Export &b) {
-              return a.exportName < b.exportName;
-            });
+  llvm::sort(ctx.config.exports, [](const Export &a, const Export &b) {
+    return a.exportName < b.exportName;
+  });
 }
 
-void assignExportOrdinals() {
+void LinkerDriver::assignExportOrdinals() {
   // Assign unique ordinals if default (= 0).
   uint32_t max = 0;
-  for (Export &e : config->exports)
+  for (Export &e : ctx.config.exports)
     max = std::max(max, (uint32_t)e.ordinal);
-  for (Export &e : config->exports)
+  for (Export &e : ctx.config.exports)
     if (e.ordinal == 0)
       e.ordinal = ++max;
   if (max > std::numeric_limits<uint16_t>::max())
-    fatal("too many exported symbols (max " +
+    fatal("too many exported symbols (got " + Twine(max) + ", max " +
           Twine(std::numeric_limits<uint16_t>::max()) + ")");
 }
 
 // Parses a string in the form of "key=value" and check
 // if value matches previous values for the same key.
-void checkFailIfMismatch(StringRef arg, InputFile *source) {
-  StringRef k, v;
-  std::tie(k, v) = arg.split('=');
+void LinkerDriver::checkFailIfMismatch(StringRef arg, InputFile *source) {
+  auto [k, v] = arg.split('=');
   if (k.empty() || v.empty())
     fatal("/failifmismatch: invalid argument: " + arg);
-  std::pair<StringRef, InputFile *> existing = config->mustMatch[k];
+  std::pair<StringRef, InputFile *> existing = ctx.config.mustMatch[k];
   if (!existing.first.empty() && v != existing.first) {
     std::string sourceStr = source ? toString(source) : "cmd-line";
     std::string existingStr =
@@ -709,14 +723,14 @@ void checkFailIfMismatch(StringRef arg, InputFile *source) {
           existingStr + " has value " + existing.first + "\n>>> " + sourceStr +
           " has value " + v);
   }
-  config->mustMatch[k] = {v, source};
+  ctx.config.mustMatch[k] = {v, source};
 }
 
 // Convert Windows resource files (.res files) to a .obj file.
 // Does what cvtres.exe does, but in-process and cross-platform.
-MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
-                                 ArrayRef<ObjFile *> objs) {
-  object::WindowsResourceParser parser(/* MinGW */ config->mingw);
+MemoryBufferRef LinkerDriver::convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
+                                               ArrayRef<ObjFile *> objs) {
+  object::WindowsResourceParser parser(/* MinGW */ ctx.config.mingw);
 
   std::vector<std::string> duplicates;
   for (MemoryBufferRef mb : mbs) {
@@ -741,18 +755,18 @@ MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
       fatal(toString(std::move(ec)));
   }
 
-  if (config->mingw)
+  if (ctx.config.mingw)
     parser.cleanUpManifests(duplicates);
 
   for (const auto &dupeDiag : duplicates)
-    if (config->forceMultipleRes)
+    if (ctx.config.forceMultipleRes)
       warn(dupeDiag);
     else
       error(dupeDiag);
 
   Expected<std::unique_ptr<MemoryBuffer>> e =
-      llvm::object::writeWindowsResourceCOFF(config->machine, parser,
-                                             config->timestamp);
+      llvm::object::writeWindowsResourceCOFF(ctx.config.machine, parser,
+                                             ctx.config.timestamp);
   if (!e)
     fatal("failed to write .res to COFF: " + toString(e.takeError()));
 
@@ -764,12 +778,15 @@ MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
 // Create OptTable
 
 // Create prefix string literals used in Options.td
-#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#define PREFIX(NAME, VALUE)                                                    \
+  static constexpr llvm::StringLiteral NAME##_init[] = VALUE;                  \
+  static constexpr llvm::ArrayRef<llvm::StringLiteral> NAME(                   \
+      NAME##_init, std::size(NAME##_init) - 1);
 #include "Options.inc"
 #undef PREFIX
 
 // Create table mapping all options defined in Options.td
-static const llvm::opt::OptTable::Info infoTable[] = {
+static constexpr llvm::opt::OptTable::Info infoTable[] = {
 #define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12)      \
   {X1, X2, X10,         X11,         OPT_##ID, llvm::opt::Option::KIND##Class, \
    X9, X8, OPT_##GROUP, OPT_##ALIAS, X7,       X12},
@@ -777,9 +794,7 @@ static const llvm::opt::OptTable::Info infoTable[] = {
 #undef OPTION
 };
 
-COFFOptTable::COFFOptTable() : OptTable(infoTable, true) {}
-
-COFFOptTable optTable;
+COFFOptTable::COFFOptTable() : GenericOptTable(infoTable, true) {}
 
 // Set color diagnostics according to --color-diagnostics={auto,always,never}
 // or --no-color-diagnostics flags.
@@ -816,6 +831,8 @@ static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) {
   return cl::TokenizeWindowsCommandLine;
 }
 
+ArgParser::ArgParser(COFFLinkerContext &c) : ctx(c) {}
+
 // Parses a given list of options.
 opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
   // Make InputArgList from string vectors.
@@ -826,7 +843,8 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
   // options so we parse here before and ignore all the options but
   // --rsp-quoting and /lldignoreenv.
   // (This means --rsp-quoting can't be added through %LINK%.)
-  opt::InputArgList args = optTable.ParseArgs(argv, missingIndex, missingCount);
+  opt::InputArgList args =
+      ctx.optTable.ParseArgs(argv, missingIndex, missingCount);
 
   // Expand response files (arguments in the form of @<filename>) and insert
   // flags from %LINK% and %_LINK_%, and then parse the argument again.
@@ -834,9 +852,9 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
                                               argv.data() + argv.size());
   if (!args.hasArg(OPT_lldignoreenv))
     addLINK(expandedArgv);
-  cl::ExpandResponseFiles(saver, getQuotingStyle(args), expandedArgv);
-  args = optTable.ParseArgs(makeArrayRef(expandedArgv).drop_front(),
-                            missingIndex, missingCount);
+  cl::ExpandResponseFiles(saver(), getQuotingStyle(args), expandedArgv);
+  args = ctx.optTable.ParseArgs(ArrayRef(expandedArgv).drop_front(),
+                                missingIndex, missingCount);
 
   // Print the real command line if response files are expanded.
   if (args.hasArg(OPT_verbose) && argv.size() != expandedArgv.size()) {
@@ -847,8 +865,13 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
   }
 
   // Save the command line after response file expansion so we can write it to
-  // the PDB if necessary.
-  config->argv = {expandedArgv.begin(), expandedArgv.end()};
+  // the PDB if necessary. Mimic MSVC, which skips input files.
+  ctx.config.argv = {argv[0]};
+  for (opt::Arg *arg : args) {
+    if (arg->getOption().getKind() != opt::Option::InputClass) {
+      ctx.config.argv.push_back(args.getArgString(arg->getIndex()));
+    }
+  }
 
   // Handle /WX early since it converts missing argument warnings to errors.
   errorHandler().fatalWarnings = args.hasFlag(OPT_WX, OPT_WX_no, false);
@@ -860,7 +883,7 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
 
   for (opt::Arg *arg : args.filtered(OPT_UNKNOWN)) {
     std::string nearest;
-    if (optTable.findNearest(arg->getAsString(args), nearest) > 1)
+    if (ctx.optTable.findNearest(arg->getAsString(args), nearest) > 1)
       warn("ignoring unknown argument '" + arg->getAsString(args) + "'");
     else
       warn("ignoring unknown argument '" + arg->getAsString(args) +
@@ -881,7 +904,7 @@ ParsedDirectives ArgParser::parseDirectives(StringRef s) {
   // Handle /EXPORT and /INCLUDE in a fast path. These directives can appear for
   // potentially every symbol in the object, so they must be handled quickly.
   SmallVector<StringRef, 16> tokens;
-  cl::TokenizeWindowsCommandLineNoCopy(s, saver, tokens);
+  cl::TokenizeWindowsCommandLineNoCopy(s, saver(), tokens);
   for (StringRef tok : tokens) {
     if (tok.startswith_insensitive("/export:") ||
         tok.startswith_insensitive("-export:"))
@@ -889,12 +912,15 @@ ParsedDirectives ArgParser::parseDirectives(StringRef s) {
     else if (tok.startswith_insensitive("/include:") ||
              tok.startswith_insensitive("-include:"))
       result.includes.push_back(tok.substr(strlen("/include:")));
+    else if (tok.startswith_insensitive("/exclude-symbols:") ||
+             tok.startswith_insensitive("-exclude-symbols:"))
+      result.excludes.push_back(tok.substr(strlen("/exclude-symbols:")));
     else {
       // Copy substrings that are not valid C strings. The tokenizer may have
       // already copied quoted arguments for us, so those do not need to be
       // copied again.
       bool HasNul = tok.end() != s.end() && tok.data()[tok.size()] == '\0';
-      rest.push_back(HasNul ? tok.data() : saver.save(tok).data());
+      rest.push_back(HasNul ? tok.data() : saver().save(tok).data());
     }
   }
 
@@ -902,7 +928,7 @@ ParsedDirectives ArgParser::parseDirectives(StringRef s) {
   unsigned missingIndex;
   unsigned missingCount;
 
-  result.args = optTable.ParseArgs(rest, missingIndex, missingCount);
+  result.args = ctx.optTable.ParseArgs(rest, missingIndex, missingCount);
 
   if (missingCount)
     fatal(Twine(result.args.getArgString(missingIndex)) + ": missing argument");
@@ -916,11 +942,11 @@ ParsedDirectives ArgParser::parseDirectives(StringRef s) {
 // So you can pass extra arguments using them.
 void ArgParser::addLINK(SmallVector<const char *, 256> &argv) {
   // Concatenate LINK env and command line arguments, and then parse them.
-  if (Optional<std::string> s = Process::GetEnv("LINK")) {
+  if (std::optional<std::string> s = Process::GetEnv("LINK")) {
     std::vector<const char *> v = tokenize(*s);
     argv.insert(std::next(argv.begin()), v.begin(), v.end());
   }
-  if (Optional<std::string> s = Process::GetEnv("_LINK_")) {
+  if (std::optional<std::string> s = Process::GetEnv("_LINK_")) {
     std::vector<const char *> v = tokenize(*s);
     argv.insert(std::next(argv.begin()), v.begin(), v.end());
   }
@@ -928,14 +954,14 @@ void ArgParser::addLINK(SmallVector<const char *, 256> &argv) {
 
 std::vector<const char *> ArgParser::tokenize(StringRef s) {
   SmallVector<const char *, 16> tokens;
-  cl::TokenizeWindowsCommandLine(s, saver, tokens);
+  cl::TokenizeWindowsCommandLine(s, saver(), tokens);
   return std::vector<const char *>(tokens.begin(), tokens.end());
 }
 
-void printHelp(const char *argv0) {
-  optTable.printHelp(lld::outs(),
-                     (std::string(argv0) + " [options] file...").c_str(),
-                     "LLVM Linker", false);
+void LinkerDriver::printHelp(const char *argv0) {
+  ctx.optTable.printHelp(lld::outs(),
+                         (std::string(argv0) + " [options] file...").c_str(),
+                         "LLVM Linker", false);
 }
 
 } // namespace coff
index 7326469..7ece725 100644 (file)
@@ -18,6 +18,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "ICF.h"
+#include "COFFLinkerContext.h"
 #include "Chunks.h"
 #include "Symbols.h"
 #include "lld/Common/ErrorHandler.h"
 
 using namespace llvm;
 
-namespace lld {
-namespace coff {
-
-static Timer icfTimer("ICF", Timer::root());
+namespace lld::coff {
 
 class ICF {
 public:
-  ICF(ICFLevel icfLevel) : icfLevel(icfLevel){};
-  void run(ArrayRef<Chunk *> v);
+  ICF(COFFLinkerContext &c) : ctx(c){};
+  void run();
 
 private:
   void segregate(size_t begin, size_t end, bool constant);
@@ -63,7 +61,8 @@ private:
   std::vector<SectionChunk *> chunks;
   int cnt = 0;
   std::atomic<bool> repeat = {false};
-  ICFLevel icfLevel = ICFLevel::All;
+
+  COFFLinkerContext &ctx;
 };
 
 // Returns true if section S is subject of ICF.
@@ -84,7 +83,7 @@ bool ICF::isEligible(SectionChunk *c) {
     return false;
 
   // Under regular (not safe) ICF, all code sections are eligible.
-  if ((icfLevel == ICFLevel::All) &&
+  if ((ctx.config.doICF == ICFLevel::All) &&
       c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE)
     return true;
 
@@ -232,10 +231,10 @@ void ICF::forEachClass(std::function<void(size_t, size_t)> fn) {
   size_t boundaries[numShards + 1];
   boundaries[0] = 0;
   boundaries[numShards] = chunks.size();
-  parallelForEachN(1, numShards, [&](size_t i) {
+  parallelFor(1, numShards, [&](size_t i) {
     boundaries[i] = findBoundary((i - 1) * step, chunks.size());
   });
-  parallelForEachN(1, numShards + 1, [&](size_t i) {
+  parallelFor(1, numShards + 1, [&](size_t i) {
     if (boundaries[i - 1] < boundaries[i]) {
       forEachClassRange(boundaries[i - 1], boundaries[i], fn);
     }
@@ -246,12 +245,12 @@ void ICF::forEachClass(std::function<void(size_t, size_t)> fn) {
 // Merge identical COMDAT sections.
 // Two sections are considered the same if their section headers,
 // contents and relocations are all the same.
-void ICF::run(ArrayRef<Chunk *> vec) {
-  ScopedTimer t(icfTimer);
+void ICF::run() {
+  ScopedTimer t(ctx.icfTimer);
 
   // Collect only mergeable sections and group by hash value.
   uint32_t nextId = 1;
-  for (Chunk *c : vec) {
+  for (Chunk *c : ctx.symtab.getChunks()) {
     if (auto *sc = dyn_cast<SectionChunk>(c)) {
       if (isEligible(sc))
         chunks.push_back(sc);
@@ -262,7 +261,7 @@ void ICF::run(ArrayRef<Chunk *> vec) {
 
   // Make sure that ICF doesn't merge sections that are being handled by string
   // tail merging.
-  for (MergeChunk *mc : MergeChunk::instances)
+  for (MergeChunk *mc : ctx.mergeChunkInstances)
     if (mc)
       for (SectionChunk *sc : mc->sections)
         sc->eqClass[0] = nextId++;
@@ -317,9 +316,6 @@ void ICF::run(ArrayRef<Chunk *> vec) {
 }
 
 // Entry point to ICF.
-void doICF(ArrayRef<Chunk *> chunks, ICFLevel icfLevel) {
-  ICF(icfLevel).run(chunks);
-}
+void doICF(COFFLinkerContext &ctx) { ICF(ctx).run(); }
 
-} // namespace coff
-} // namespace lld
+} // namespace lld::coff
index f8cc807..f9ed8ed 100644 (file)
 #include "lld/Common/LLVM.h"
 #include "llvm/ADT/ArrayRef.h"
 
-namespace lld {
-namespace coff {
+namespace lld::coff {
 
-class Chunk;
+class COFFLinkerContext;
 
-void doICF(ArrayRef<Chunk *> chunks, ICFLevel);
+void doICF(COFFLinkerContext &ctx);
 
-} // namespace coff
-} // namespace lld
+} // namespace lld::coff
 
 #endif
index f32353c..f79aa3f 100644 (file)
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "InputFiles.h"
+#include "COFFLinkerContext.h"
 #include "Chunks.h"
 #include "Config.h"
 #include "DebugTypes.h"
@@ -14,8 +15,6 @@
 #include "SymbolTable.h"
 #include "Symbols.h"
 #include "lld/Common/DWARF.h"
-#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Memory.h"
 #include "llvm-c/lto.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/Triple.h"
@@ -38,6 +37,7 @@
 #include "llvm/Support/Path.h"
 #include "llvm/Target/TargetOptions.h"
 #include <cstring>
+#include <optional>
 #include <system_error>
 #include <utility>
 
@@ -69,15 +69,10 @@ std::string lld::toString(const coff::InputFile *file) {
       .str();
 }
 
-std::vector<ObjFile *> ObjFile::instances;
-std::map<std::string, PDBInputFile *> PDBInputFile::instances;
-std::vector<ImportFile *> ImportFile::instances;
-std::vector<BitcodeFile *> BitcodeFile::instances;
-
 /// Checks that Source is compatible with being a weak alias to Target.
 /// If Source is Undefined and has no weak alias set, makes it a weak
 /// alias to Target.
-static void checkAndSetWeakAlias(SymbolTable *symtab, InputFile *f,
+static void checkAndSetWeakAlias(COFFLinkerContext &ctx, InputFile *f,
                                  Symbol *source, Symbol *target) {
   if (auto *u = dyn_cast<Undefined>(source)) {
     if (u->weakAlias && u->weakAlias != target) {
@@ -86,9 +81,9 @@ static void checkAndSetWeakAlias(SymbolTable *symtab, InputFile *f,
       // 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)
+      if (ctx.config.mingw)
         return;
-      symtab->reportDuplicate(source, f);
+      ctx.symtab.reportDuplicate(source, f);
     }
     u->weakAlias = target;
   }
@@ -98,7 +93,8 @@ static bool ignoredSymbolName(StringRef name) {
   return name == "@feat.00" || name == "@comp.id";
 }
 
-ArchiveFile::ArchiveFile(MemoryBufferRef m) : InputFile(ArchiveKind, m) {}
+ArchiveFile::ArchiveFile(COFFLinkerContext &ctx, MemoryBufferRef m)
+    : InputFile(ctx, ArchiveKind, m) {}
 
 void ArchiveFile::parse() {
   // Parse a MemoryBufferRef as an archive file.
@@ -106,20 +102,20 @@ void ArchiveFile::parse() {
 
   // Read the symbol table to construct Lazy objects.
   for (const Archive::Symbol &sym : file->symbols())
-    symtab->addLazyArchive(this, sym);
+    ctx.symtab.addLazyArchive(this, sym);
 }
 
 // Returns a buffer pointing to a member file containing a given symbol.
 void ArchiveFile::addMember(const Archive::Symbol &sym) {
   const Archive::Child &c =
       CHECK(sym.getMember(),
-            "could not get the member for symbol " + toCOFFString(sym));
+            "could not get the member for symbol " + toCOFFString(ctx, sym));
 
   // Return an empty buffer if we have already returned the same buffer.
   if (!seen.insert(c.getChildOffset()).second)
     return;
 
-  driver->enqueueArchiveMember(c, sym, getName());
+  ctx.driver.enqueueArchiveMember(c, sym, getName());
 }
 
 std::vector<MemoryBufferRef> lld::coff::getArchiveMembers(Archive *file) {
@@ -138,31 +134,7 @@ std::vector<MemoryBufferRef> lld::coff::getArchiveMembers(Archive *file) {
   return v;
 }
 
-void LazyObjFile::fetch() {
-  if (mb.getBuffer().empty())
-    return;
-
-  InputFile *file;
-  if (isBitcode(mb))
-    file = make<BitcodeFile>(mb, "", 0, std::move(symbols));
-  else
-    file = make<ObjFile>(mb, std::move(symbols));
-  mb = {};
-  symtab->addFile(file);
-}
-
-void LazyObjFile::parse() {
-  if (isBitcode(this->mb)) {
-    // Bitcode file.
-    std::unique_ptr<lto::InputFile> obj =
-        CHECK(lto::InputFile::create(this->mb), this);
-    for (const lto::InputFile::Symbol &sym : obj->symbols()) {
-      if (!sym.isUndefined())
-        symtab->addLazyObject(this, sym.getName());
-    }
-    return;
-  }
-
+void ObjFile::parseLazy() {
   // Native object file.
   std::unique_ptr<Binary> coffObjPtr = CHECK(createBinary(mb), this);
   COFFObjectFile *coffObj = cast<COFFObjectFile>(coffObjPtr.get());
@@ -175,7 +147,7 @@ void LazyObjFile::parse() {
     StringRef name = check(coffObj->getSymbolName(coffSym));
     if (coffSym.isAbsolute() && ignoredSymbolName(name))
       continue;
-    symtab->addLazyObject(this, name);
+    ctx.symtab.addLazyObject(this, name);
     i += coffSym.getNumberOfAuxSymbols();
   }
 }
@@ -265,7 +237,7 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
   // and then write it to a separate .pdb file.
 
   // Ignore DWARF debug info unless /debug is given.
-  if (!config->debug && name.startswith(".debug_"))
+  if (!ctx.config.debug && name.startswith(".debug_"))
     return nullptr;
 
   if (sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
@@ -288,12 +260,12 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
     guardEHContChunks.push_back(c);
   else if (name == ".sxdata")
     sxDataChunks.push_back(c);
-  else if (config->tailMerge && sec->NumberOfRelocations == 0 &&
+  else if (ctx.config.tailMerge && sec->NumberOfRelocations == 0 &&
            name == ".rdata" && leaderName.startswith("??_C@"))
     // COFF sections that look like string literal sections (i.e. no
     // relocations, in .rdata, leader symbol name matches the MSVC name mangling
     // for string literals) are subject to string tail merging.
-    MergeChunk::addSection(c);
+    MergeChunk::addSection(ctx, c);
   else if (name == ".rsrc" || name.startswith(".rsrc$"))
     resourceChunks.push_back(c);
   else
@@ -387,16 +359,16 @@ Symbol *ObjFile::createRegular(COFFSymbolRef sym) {
   if (sym.isExternal()) {
     StringRef name = check(coffObj->getSymbolName(sym));
     if (sc)
-      return symtab->addRegular(this, name, sym.getGeneric(), sc,
-                                sym.getValue());
+      return ctx.symtab.addRegular(this, name, sym.getGeneric(), sc,
+                                   sym.getValue());
     // 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."))
+    if (ctx.config.mingw && name.startswith(".weak."))
       return nullptr;
-    return symtab->addUndefined(name, this, false);
+    return ctx.symtab.addUndefined(name, this, false);
   }
   if (sc)
     return make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false,
@@ -425,15 +397,15 @@ void ObjFile::initializeSymbols() {
       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 =
+    } else if (std::optional<Symbol *> optSym =
                    createDefined(coffSym, comdatDefs, prevailingComdat)) {
       symbols[i] = *optSym;
-      if (config->mingw && prevailingComdat)
+      if (ctx.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
-      // two cases:
+      // createDefined() returns std::nullopt if a symbol belongs to a section
+      // that was pending at the point when the symbol was read. This can happen
+      // in two cases:
       // 1) section definition symbol for a comdat leader;
       // 2) symbol belongs to a comdat section associated with another section.
       // In both of these cases, we can expect the section to be resolved by
@@ -449,7 +421,7 @@ void ObjFile::initializeSymbols() {
     if (const coff_aux_section_definition *def = sym.getSectionDefinition()) {
       if (def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
         readAssociativeDefinition(sym, def);
-      else if (config->mingw)
+      else if (ctx.config.mingw)
         maybeAssociateSEHForMingw(sym, def, prevailingSectionMap);
     }
     if (sparseChunks[sym.getSectionNumber()] == pendingComdat) {
@@ -464,7 +436,7 @@ void ObjFile::initializeSymbols() {
   for (auto &kv : weakAliases) {
     Symbol *sym = kv.first;
     uint32_t idx = kv.second;
-    checkAndSetWeakAlias(symtab, this, sym, symbols[idx]);
+    checkAndSetWeakAlias(ctx, this, sym, symbols[idx]);
   }
 
   // Free the memory used by sparseChunks now that symbol loading is finished.
@@ -473,7 +445,7 @@ void ObjFile::initializeSymbols() {
 
 Symbol *ObjFile::createUndefined(COFFSymbolRef sym) {
   StringRef name = check(coffObj->getSymbolName(sym));
-  return symtab->addUndefined(name, this, sym.isWeakExternal());
+  return ctx.symtab.addUndefined(name, this, sym.isWeakExternal());
 }
 
 static const coff_aux_section_definition *findSectionDef(COFFObjectFile *obj,
@@ -524,10 +496,10 @@ void ObjFile::handleComdatSelection(
   // Clang on the other hand picks "any". To be able to link two object files
   // with a __declspec(selectany) declaration, one compiled with gcc and the
   // other with clang, we merge them as proper "same size as"
-  if (config->mingw && ((selection == IMAGE_COMDAT_SELECT_ANY &&
-                         leaderSelection == IMAGE_COMDAT_SELECT_SAME_SIZE) ||
-                        (selection == IMAGE_COMDAT_SELECT_SAME_SIZE &&
-                         leaderSelection == IMAGE_COMDAT_SELECT_ANY))) {
+  if (ctx.config.mingw && ((selection == IMAGE_COMDAT_SELECT_ANY &&
+                            leaderSelection == IMAGE_COMDAT_SELECT_SAME_SIZE) ||
+                           (selection == IMAGE_COMDAT_SELECT_SAME_SIZE &&
+                            leaderSelection == IMAGE_COMDAT_SELECT_ANY))) {
     leaderSelection = selection = IMAGE_COMDAT_SELECT_SAME_SIZE;
   }
 
@@ -539,17 +511,17 @@ void ObjFile::handleComdatSelection(
   // seems better though.
   // (This behavior matches ModuleLinker::getComdatResult().)
   if (selection != leaderSelection) {
-    log(("conflicting comdat type for " + toString(*leader) + ": " +
+    log(("conflicting comdat type for " + toString(ctx, *leader) + ": " +
          Twine((int)leaderSelection) + " in " + toString(leader->getFile()) +
          " and " + Twine((int)selection) + " in " + toString(this))
             .str());
-    symtab->reportDuplicate(leader, this);
+    ctx.symtab.reportDuplicate(leader, this);
     return;
   }
 
   switch (selection) {
   case IMAGE_COMDAT_SELECT_NODUPLICATES:
-    symtab->reportDuplicate(leader, this);
+    ctx.symtab.reportDuplicate(leader, this);
     break;
 
   case IMAGE_COMDAT_SELECT_ANY:
@@ -558,15 +530,15 @@ void ObjFile::handleComdatSelection(
 
   case IMAGE_COMDAT_SELECT_SAME_SIZE:
     if (leaderChunk->getSize() != getSection(sym)->SizeOfRawData) {
-      if (!config->mingw) {
-        symtab->reportDuplicate(leader, this);
+      if (!ctx.config.mingw) {
+        ctx.symtab.reportDuplicate(leader, this);
       } else {
         const coff_aux_section_definition *leaderDef = nullptr;
         if (leaderChunk->file)
           leaderDef = findSectionDef(leaderChunk->file->getCOFFObj(),
                                      leaderChunk->getSectionNumber());
         if (!leaderDef || leaderDef->Length != def->Length)
-          symtab->reportDuplicate(leader, this);
+          ctx.symtab.reportDuplicate(leader, this);
       }
     }
     break;
@@ -577,7 +549,7 @@ void ObjFile::handleComdatSelection(
     // if the two comdat sections have e.g. different alignment.
     // Match that.
     if (leaderChunk->getContents() != newChunk.getContents())
-      symtab->reportDuplicate(leader, this, &newChunk, sym.getValue());
+      ctx.symtab.reportDuplicate(leader, this, &newChunk, sym.getValue());
     break;
   }
 
@@ -610,7 +582,7 @@ void ObjFile::handleComdatSelection(
   }
 }
 
-Optional<Symbol *> ObjFile::createDefined(
+std::optional<Symbol *> ObjFile::createDefined(
     COFFSymbolRef sym,
     std::vector<const coff_aux_section_definition *> &comdatDefs,
     bool &prevailing) {
@@ -620,8 +592,8 @@ Optional<Symbol *> ObjFile::createDefined(
   if (sym.isCommon()) {
     auto *c = make<CommonChunk>(sym);
     chunks.push_back(c);
-    return symtab->addCommon(this, getName(), sym.getValue(), sym.getGeneric(),
-                             c);
+    return ctx.symtab.addCommon(this, getName(), sym.getValue(),
+                                sym.getGeneric(), c);
   }
 
   if (sym.isAbsolute()) {
@@ -634,8 +606,8 @@ Optional<Symbol *> ObjFile::createDefined(
       return nullptr;
 
     if (sym.isExternal())
-      return symtab->addAbsolute(name, sym);
-    return make<DefinedAbsolute>(name, sym);
+      return ctx.symtab.addAbsolute(name, sym);
+    return make<DefinedAbsolute>(ctx, name, sym);
   }
 
   int32_t sectionNumber = sym.getSectionNumber();
@@ -657,8 +629,8 @@ Optional<Symbol *> ObjFile::createDefined(
   // The second symbol entry has the name of the comdat symbol, called the
   // "comdat leader".
   // When this function is called for the first symbol entry of a comdat,
-  // it sets comdatDefs and returns None, and when it's called for the second
-  // symbol entry it reads comdatDefs and then sets it back to nullptr.
+  // it sets comdatDefs and returns std::nullopt, and when it's called for the
+  // second symbol entry it reads comdatDefs and then sets it back to nullptr.
 
   // Handle comdat leader.
   if (const coff_aux_section_definition *def = comdatDefs[sectionNumber]) {
@@ -667,7 +639,7 @@ Optional<Symbol *> ObjFile::createDefined(
 
     if (sym.isExternal()) {
       std::tie(leader, prevailing) =
-          symtab->addComdat(this, getName(), sym.getGeneric());
+          ctx.symtab.addComdat(this, getName(), sym.getGeneric());
     } else {
       leader = make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false,
                                     /*IsExternal*/ false, sym.getGeneric());
@@ -705,7 +677,7 @@ Optional<Symbol *> ObjFile::createDefined(
       if (def->Selection != IMAGE_COMDAT_SELECT_ASSOCIATIVE)
         comdatDefs[sectionNumber] = def;
     }
-    return None;
+    return std::nullopt;
   }
 
   return createRegular(sym);
@@ -763,7 +735,8 @@ void ObjFile::initializeFlags() {
       if (sym->kind() == SymbolKind::S_OBJNAME) {
         auto objName = cantFail(SymbolDeserializer::deserializeAs<ObjNameSym>(
             sym.get()));
-        pchSignature = objName.Signature;
+        if (objName.Signature)
+          pchSignature = objName.Signature;
       }
       offset += sym->length();
     }
@@ -778,7 +751,7 @@ void ObjFile::initializeFlags() {
 // DebugTypes.h). Both cases only happen with cl.exe: clang-cl produces regular
 // output even with /Yc and /Yu and with /Zi.
 void ObjFile::initializeDependencies() {
-  if (!config->debug)
+  if (!ctx.config.debug)
     return;
 
   bool isPCH = false;
@@ -789,12 +762,11 @@ void ObjFile::initializeDependencies() {
   else
     data = getDebugSection(".debug$T");
 
-  // Don't make a TpiSource for objects with no debug info. If the object has
   // symbols but no types, make a plain, empty TpiSource anyway, because it
   // simplifies adding the symbols later.
   if (data.empty()) {
     if (!debugChunks.empty())
-      debugTypesObj = makeTpiSource(this);
+      debugTypesObj = makeTpiSource(ctx, this);
     return;
   }
 
@@ -812,7 +784,7 @@ void ObjFile::initializeDependencies() {
 
   // This object file is a PCH file that others will depend on.
   if (isPCH) {
-    debugTypesObj = makePrecompSource(this);
+    debugTypesObj = makePrecompSource(ctx, this);
     return;
   }
 
@@ -820,8 +792,8 @@ void ObjFile::initializeDependencies() {
   if (firstType->kind() == LF_TYPESERVER2) {
     TypeServer2Record ts = cantFail(
         TypeDeserializer::deserializeAs<TypeServer2Record>(firstType->data()));
-    debugTypesObj = makeUseTypeServerSource(this, ts);
-    PDBInputFile::enqueue(ts.getName(), this);
+    debugTypesObj = makeUseTypeServerSource(ctx, this, ts);
+    enqueuePdbFile(ts.getName(), this);
     return;
   }
 
@@ -830,14 +802,18 @@ void ObjFile::initializeDependencies() {
   if (firstType->kind() == LF_PRECOMP) {
     PrecompRecord precomp = cantFail(
         TypeDeserializer::deserializeAs<PrecompRecord>(firstType->data()));
-    debugTypesObj = makeUsePrecompSource(this, precomp);
+    // We're better off trusting the LF_PRECOMP signature. In some cases the
+    // S_OBJNAME record doesn't contain a valid PCH signature.
+    if (precomp.Signature)
+      pchSignature = precomp.Signature;
+    debugTypesObj = makeUsePrecompSource(ctx, this, precomp);
     // Drop the LF_PRECOMP record from the input stream.
     debugTypes = debugTypes.drop_front(firstType->RecordData.size());
     return;
   }
 
   // This is a plain old object file.
-  debugTypesObj = makeTpiSource(this);
+  debugTypesObj = makeTpiSource(ctx, this);
 }
 
 // Make a PDB path assuming the PDB is in the same folder as the OBJ
@@ -855,7 +831,7 @@ static std::string getPdbBaseName(ObjFile *file, StringRef tSPath) {
 
 // The casing of the PDB path stamped in the OBJ can differ from the actual path
 // on disk. With this, we ensure to always use lowercase as a key for the
-// PDBInputFile::instances map, at least on Windows.
+// pdbInputFileInstances map, at least on Windows.
 static std::string normalizePdbPath(StringRef path) {
 #if defined(_WIN32)
   return path.lower();
@@ -865,8 +841,8 @@ static std::string normalizePdbPath(StringRef path) {
 }
 
 // If existing, return the actual PDB path on disk.
-static Optional<std::string> findPdbPath(StringRef pdbPath,
-                                         ObjFile *dependentFile) {
+static std::optional<std::string> findPdbPath(StringRef pdbPath,
+                                              ObjFile *dependentFile) {
   // Ensure the file exists before anything else. In some cases, if the path
   // points to a removable device, Driver::enqueuePath() would fail with an
   // error (EAGAIN, "resource unavailable try again") which we want to skip
@@ -876,43 +852,37 @@ static Optional<std::string> findPdbPath(StringRef pdbPath,
   std::string ret = getPdbBaseName(dependentFile, pdbPath);
   if (llvm::sys::fs::exists(ret))
     return normalizePdbPath(ret);
-  return None;
+  return std::nullopt;
 }
 
-PDBInputFile::PDBInputFile(MemoryBufferRef m) : InputFile(PDBKind, m) {}
+PDBInputFile::PDBInputFile(COFFLinkerContext &ctx, MemoryBufferRef m)
+    : InputFile(ctx, PDBKind, m) {}
 
 PDBInputFile::~PDBInputFile() = default;
 
-PDBInputFile *PDBInputFile::findFromRecordPath(StringRef path,
+PDBInputFile *PDBInputFile::findFromRecordPath(const COFFLinkerContext &ctx,
+                                               StringRef path,
                                                ObjFile *fromFile) {
   auto p = findPdbPath(path.str(), fromFile);
   if (!p)
     return nullptr;
-  auto it = PDBInputFile::instances.find(*p);
-  if (it != PDBInputFile::instances.end())
+  auto it = ctx.pdbInputFileInstances.find(*p);
+  if (it != ctx.pdbInputFileInstances.end())
     return it->second;
   return nullptr;
 }
 
-void PDBInputFile::enqueue(StringRef path, ObjFile *fromFile) {
-  auto p = findPdbPath(path.str(), fromFile);
-  if (!p)
-    return;
-  auto it = PDBInputFile::instances.emplace(*p, nullptr);
-  if (!it.second)
-    return; // already scheduled for load
-  driver->enqueuePDB(*p);
-}
-
 void PDBInputFile::parse() {
-  PDBInputFile::instances[mb.getBufferIdentifier().str()] = this;
+  ctx.pdbInputFileInstances[mb.getBufferIdentifier().str()] = this;
 
   std::unique_ptr<pdb::IPDBSession> thisSession;
-  loadErr.emplace(pdb::NativeSession::createFromPdb(
-      MemoryBuffer::getMemBuffer(mb, false), thisSession));
-  if (*loadErr)
+  Error E = pdb::NativeSession::createFromPdb(
+      MemoryBuffer::getMemBuffer(mb, false), thisSession);
+  if (E) {
+    loadErrorStr.emplace(toString(std::move(E)));
     return; // fail silently at this point - the error will be handled later,
             // when merging the debug type stream
+  }
 
   session.reset(static_cast<pdb::NativeSession *>(thisSession.release()));
 
@@ -920,43 +890,57 @@ void PDBInputFile::parse() {
   auto expectedInfo = pdbFile.getPDBInfoStream();
   // All PDB Files should have an Info stream.
   if (!expectedInfo) {
-    loadErr.emplace(expectedInfo.takeError());
+    loadErrorStr.emplace(toString(expectedInfo.takeError()));
     return;
   }
-  debugTypesObj = makeTypeServerSource(this);
+  debugTypesObj = makeTypeServerSource(ctx, this);
 }
 
 // Used only for DWARF debug info, which is not common (except in MinGW
 // environments). This returns an optional pair of file name and line
 // number for where the variable was defined.
-Optional<std::pair<StringRef, uint32_t>>
+std::optional<std::pair<StringRef, uint32_t>>
 ObjFile::getVariableLocation(StringRef var) {
   if (!dwarf) {
     dwarf = make<DWARFCache>(DWARFContext::create(*getCOFFObj()));
     if (!dwarf)
-      return None;
+      return std::nullopt;
   }
-  if (config->machine == I386)
+  if (ctx.config.machine == I386)
     var.consume_front("_");
-  Optional<std::pair<std::string, unsigned>> ret = dwarf->getVariableLoc(var);
+  std::optional<std::pair<std::string, unsigned>> ret =
+      dwarf->getVariableLoc(var);
   if (!ret)
-    return None;
-  return std::make_pair(saver.save(ret->first), ret->second);
+    return std::nullopt;
+  return std::make_pair(saver().save(ret->first), ret->second);
 }
 
 // Used only for DWARF debug info, which is not common (except in MinGW
 // environments).
-Optional<DILineInfo> ObjFile::getDILineInfo(uint32_t offset,
-                                            uint32_t sectionIndex) {
+std::optional<DILineInfo> ObjFile::getDILineInfo(uint32_t offset,
+                                                 uint32_t sectionIndex) {
   if (!dwarf) {
     dwarf = make<DWARFCache>(DWARFContext::create(*getCOFFObj()));
     if (!dwarf)
-      return None;
+      return std::nullopt;
   }
 
   return dwarf->getDILineInfo(offset, sectionIndex);
 }
 
+void ObjFile::enqueuePdbFile(StringRef path, ObjFile *fromFile) {
+  auto p = findPdbPath(path.str(), fromFile);
+  if (!p)
+    return;
+  auto it = ctx.pdbInputFileInstances.emplace(*p, nullptr);
+  if (!it.second)
+    return; // already scheduled for load
+  ctx.driver.enqueuePDB(*p);
+}
+
+ImportFile::ImportFile(COFFLinkerContext &ctx, MemoryBufferRef m)
+    : InputFile(ctx, ImportKind, m), live(!ctx.config.doGC), thunkLive(live) {}
+
 void ImportFile::parse() {
   const char *buf = mb.getBufferStart();
   const auto *hdr = reinterpret_cast<const coff_import_header *>(buf);
@@ -966,8 +950,8 @@ void ImportFile::parse() {
     fatal("broken import library");
 
   // Read names and create an __imp_ symbol.
-  StringRef name = saver.save(StringRef(buf + sizeof(*hdr)));
-  StringRef impName = saver.save("__imp_" + name);
+  StringRef name = saver().save(StringRef(buf + sizeof(*hdr)));
+  StringRef impName = saver().save("__imp_" + name);
   const char *nameStart = buf + sizeof(coff_import_header) + name.size() + 1;
   dllName = std::string(StringRef(nameStart));
   StringRef extName;
@@ -990,34 +974,32 @@ void ImportFile::parse() {
   this->hdr = hdr;
   externalName = extName;
 
-  impSym = symtab->addImportData(impName, this);
+  impSym = ctx.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));
+    static_cast<void>(ctx.symtab.addImportData(name, this));
 
   // If type is function, we need to create a thunk which jump to an
   // address pointed by the __imp_ symbol. (This allows you to call
   // DLL functions just like regular non-DLL functions.)
   if (hdr->getType() == llvm::COFF::IMPORT_CODE)
-    thunkSym = symtab->addImportThunk(
+    thunkSym = ctx.symtab.addImportThunk(
         name, cast_or_null<DefinedImportData>(impSym), hdr->Machine);
 }
 
-BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
-                         uint64_t offsetInArchive)
-    : BitcodeFile(mb, archiveName, offsetInArchive, {}) {}
-
-BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
-                         uint64_t offsetInArchive,
-                         std::vector<Symbol *> &&symbols)
-    : InputFile(BitcodeKind, mb), symbols(std::move(symbols)) {
+BitcodeFile::BitcodeFile(COFFLinkerContext &ctx, MemoryBufferRef mb,
+                         StringRef archiveName, uint64_t offsetInArchive,
+                         bool lazy)
+    : InputFile(ctx, BitcodeKind, mb, lazy) {
   std::string path = mb.getBufferIdentifier().str();
-  if (config->thinLTOIndexOnly)
-    path = replaceThinLTOSuffix(mb.getBufferIdentifier());
+  if (ctx.config.thinLTOIndexOnly)
+    path = replaceThinLTOSuffix(mb.getBufferIdentifier(),
+                                ctx.config.thinLTOObjectSuffixReplace.first,
+                                ctx.config.thinLTOObjectSuffixReplace.second);
 
   // ThinLTO assumes that all MemoryBufferRefs given to it have a unique
   // name. If two archives define two members with the same name, this
@@ -1025,90 +1007,72 @@ BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
   // into consideration at LTO time (which very likely causes undefined
   // symbols later in the link stage). So we append file offset to make
   // filename unique.
-  MemoryBufferRef mbref(
-      mb.getBuffer(),
-      saver.save(archiveName.empty() ? path
-                                     : archiveName + sys::path::filename(path) +
-                                           utostr(offsetInArchive)));
+  MemoryBufferRef mbref(mb.getBuffer(),
+                        saver().save(archiveName.empty()
+                                         ? path
+                                         : archiveName +
+                                               sys::path::filename(path) +
+                                               utostr(offsetInArchive)));
 
   obj = check(lto::InputFile::create(mbref));
 }
 
 BitcodeFile::~BitcodeFile() = default;
 
-namespace {
-// Convenience class for initializing a coff_section with specific flags.
-class FakeSection {
-public:
-  FakeSection(int c) { section.Characteristics = c; }
-
-  coff_section section;
-};
-
-// Convenience class for initializing a SectionChunk with specific flags.
-class FakeSectionChunk {
-public:
-  FakeSectionChunk(const coff_section *section) : chunk(nullptr, section) {
-    // Comdats from LTO files can't be fully treated as regular comdats
-    // at this point; we don't know what size or contents they are going to
-    // have, so we can't do proper checking of such aspects of them.
-    chunk.selection = IMAGE_COMDAT_SELECT_ANY;
-  }
-
-  SectionChunk chunk;
-};
-
-FakeSection ltoTextSection(IMAGE_SCN_MEM_EXECUTE);
-FakeSection ltoDataSection(IMAGE_SCN_CNT_INITIALIZED_DATA);
-FakeSectionChunk ltoTextSectionChunk(&ltoTextSection.section);
-FakeSectionChunk ltoDataSectionChunk(&ltoDataSection.section);
-} // namespace
-
 void BitcodeFile::parse() {
+  llvm::StringSaver &saver = lld::saver();
+
   std::vector<std::pair<Symbol *, bool>> comdat(obj->getComdatTable().size());
   for (size_t i = 0; i != obj->getComdatTable().size(); ++i)
     // FIXME: Check nodeduplicate
     comdat[i] =
-        symtab->addComdat(this, saver.save(obj->getComdatTable()[i].first));
+        ctx.symtab.addComdat(this, saver.save(obj->getComdatTable()[i].first));
   for (const lto::InputFile::Symbol &objSym : obj->symbols()) {
     StringRef symName = saver.save(objSym.getName());
     int comdatIndex = objSym.getComdatIndex();
     Symbol *sym;
     SectionChunk *fakeSC = nullptr;
     if (objSym.isExecutable())
-      fakeSC = &ltoTextSectionChunk.chunk;
+      fakeSC = &ctx.ltoTextSectionChunk.chunk;
     else
-      fakeSC = &ltoDataSectionChunk.chunk;
+      fakeSC = &ctx.ltoDataSectionChunk.chunk;
     if (objSym.isUndefined()) {
-      sym = symtab->addUndefined(symName, this, false);
+      sym = ctx.symtab.addUndefined(symName, this, false);
     } else if (objSym.isCommon()) {
-      sym = symtab->addCommon(this, symName, objSym.getCommonSize());
+      sym = ctx.symtab.addCommon(this, symName, objSym.getCommonSize());
     } else if (objSym.isWeak() && objSym.isIndirect()) {
       // Weak external.
-      sym = symtab->addUndefined(symName, this, true);
+      sym = ctx.symtab.addUndefined(symName, this, true);
       std::string fallback = std::string(objSym.getCOFFWeakExternalFallback());
-      Symbol *alias = symtab->addUndefined(saver.save(fallback));
-      checkAndSetWeakAlias(symtab, this, sym, alias);
+      Symbol *alias = ctx.symtab.addUndefined(saver.save(fallback));
+      checkAndSetWeakAlias(ctx, this, sym, alias);
     } else if (comdatIndex != -1) {
       if (symName == obj->getComdatTable()[comdatIndex].first) {
         sym = comdat[comdatIndex].first;
         if (cast<DefinedRegular>(sym)->data == nullptr)
           cast<DefinedRegular>(sym)->data = &fakeSC->repl;
       } else if (comdat[comdatIndex].second) {
-        sym = symtab->addRegular(this, symName, nullptr, fakeSC);
+        sym = ctx.symtab.addRegular(this, symName, nullptr, fakeSC);
       } else {
-        sym = symtab->addUndefined(symName, this, false);
+        sym = ctx.symtab.addUndefined(symName, this, false);
       }
     } else {
-      sym = symtab->addRegular(this, symName, nullptr, fakeSC);
+      sym = ctx.symtab.addRegular(this, symName, nullptr, fakeSC, 0,
+                                  objSym.isWeak());
     }
     symbols.push_back(sym);
     if (objSym.isUsed())
-      config->gcroot.push_back(sym);
+      ctx.config.gcroot.push_back(sym);
   }
   directives = obj->getCOFFLinkerOpts();
 }
 
+void BitcodeFile::parseLazy() {
+  for (const lto::InputFile::Symbol &sym : obj->symbols())
+    if (!sym.isUndefined())
+      ctx.symtab.addLazyObject(this, sym.getName());
+}
+
 MachineTypes BitcodeFile::getMachineType() {
   switch (Triple(obj->getTargetTriple()).getArch()) {
   case Triple::x86_64:
@@ -1124,10 +1088,8 @@ MachineTypes BitcodeFile::getMachineType() {
   }
 }
 
-std::string lld::coff::replaceThinLTOSuffix(StringRef path) {
-  StringRef suffix = config->thinLTOObjectSuffixReplace.first;
-  StringRef repl = config->thinLTOObjectSuffixReplace.second;
-
+std::string lld::coff::replaceThinLTOSuffix(StringRef path, StringRef suffix,
+                                            StringRef repl) {
   if (path.consume_back(suffix))
     return (path + repl).str();
   return std::string(path);
@@ -1180,14 +1142,14 @@ void DLLFile::parse() {
     s->nameType = ImportNameType::IMPORT_NAME;
 
     if (coffObj->getMachine() == I386) {
-      s->symbolName = symbolName = saver.save("_" + symbolName);
+      s->symbolName = symbolName = saver().save("_" + symbolName);
       s->nameType = ImportNameType::IMPORT_NAME_NOPREFIX;
     }
 
-    StringRef impName = saver.save("__imp_" + symbolName);
-    symtab->addLazyDLLSymbol(this, s, impName);
+    StringRef impName = saver().save("__imp_" + symbolName);
+    ctx.symtab.addLazyDLLSymbol(this, s, impName);
     if (code)
-      symtab->addLazyDLLSymbol(this, s, symbolName);
+      ctx.symtab.addLazyDLLSymbol(this, s, symbolName);
   }
 }
 
@@ -1203,7 +1165,7 @@ void DLLFile::makeImport(DLLFile::Symbol *s) {
 
   size_t impSize = s->dllName.size() + s->symbolName.size() + 2; // +2 for NULs
   size_t size = sizeof(coff_import_header) + impSize;
-  char *buf = bAlloc.Allocate<char>(size);
+  char *buf = bAlloc().Allocate<char>(size);
   memset(buf, 0, size);
   char *p = buf;
   auto *imp = reinterpret_cast<coff_import_header *>(p);
@@ -1219,6 +1181,6 @@ void DLLFile::makeImport(DLLFile::Symbol *s) {
   p += s->symbolName.size() + 1;
   memcpy(p, s->dllName.data(), s->dllName.size());
   MemoryBufferRef mbref = MemoryBufferRef(StringRef(buf, size), s->dllName);
-  ImportFile *impFile = make<ImportFile>(mbref);
-  symtab->addFile(impFile);
+  ImportFile *impFile = make<ImportFile>(ctx, mbref);
+  ctx.symtab.addFile(impFile);
 }
index 47b5c58..3acd74f 100644 (file)
@@ -38,6 +38,7 @@ namespace lld {
 class DWARFCache;
 
 namespace coff {
+class COFFLinkerContext;
 
 std::vector<MemoryBufferRef> getArchiveMembers(llvm::object::Archive *file);
 
@@ -91,19 +92,26 @@ public:
   // Returns .drectve section contents if exist.
   StringRef getDirectives() { return directives; }
 
+  COFFLinkerContext &ctx;
+
 protected:
-  InputFile(Kind k, MemoryBufferRef m) : mb(m), fileKind(k) {}
+  InputFile(COFFLinkerContext &c, Kind k, MemoryBufferRef m, bool lazy = false)
+      : mb(m), ctx(c), fileKind(k), lazy(lazy) {}
 
   StringRef directives;
 
 private:
   const Kind fileKind;
+
+public:
+  // True if this is a lazy ObjFile or BitcodeFile.
+  bool lazy = false;
 };
 
 // .lib or .a file.
 class ArchiveFile : public InputFile {
 public:
-  explicit ArchiveFile(MemoryBufferRef m);
+  explicit ArchiveFile(COFFLinkerContext &ctx, MemoryBufferRef m);
   static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; }
   void parse() override;
 
@@ -117,30 +125,14 @@ private:
   llvm::DenseSet<uint64_t> seen;
 };
 
-// .obj or .o file between -start-lib and -end-lib.
-class LazyObjFile : public InputFile {
-public:
-  explicit LazyObjFile(MemoryBufferRef m) : InputFile(LazyObjectKind, m) {}
-  static bool classof(const InputFile *f) {
-    return f->kind() == LazyObjectKind;
-  }
-  // Makes this object file part of the link.
-  void fetch();
-  // Adds the symbols in this file to the symbol table as LazyObject symbols.
-  void parse() override;
-
-private:
-  std::vector<Symbol *> symbols;
-};
-
 // .obj or .o file. This may be a member of an archive file.
 class ObjFile : public InputFile {
 public:
-  explicit ObjFile(MemoryBufferRef m) : InputFile(ObjectKind, m) {}
-  explicit ObjFile(MemoryBufferRef m, std::vector<Symbol *> &&symbols)
-      : InputFile(ObjectKind, m), symbols(std::move(symbols)) {}
+  explicit ObjFile(COFFLinkerContext &ctx, MemoryBufferRef m, bool lazy = false)
+      : InputFile(ctx, ObjectKind, m, lazy) {}
   static bool classof(const InputFile *f) { return f->kind() == ObjectKind; }
   void parse() override;
+  void parseLazy();
   MachineTypes getMachineType() override;
   ArrayRef<Chunk *> getChunks() { return chunks; }
   ArrayRef<SectionChunk *> getDebugChunks() { return debugChunks; }
@@ -175,8 +167,6 @@ public:
 
   bool isResourceObjFile() const { return !resourceChunks.empty(); }
 
-  static std::vector<ObjFile *> instances;
-
   // Flags in the absolute @feat.00 symbol if it is present. These usually
   // indicate if an object was compiled with certain security features enabled
   // like stack guard, safeseh, /guard:cf, or other things.
@@ -202,7 +192,7 @@ public:
   // 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;
+  std::optional<uint32_t> pchSignature;
 
   // Whether this file was compiled with /hotpatch.
   bool hotPatchable = false;
@@ -216,11 +206,11 @@ public:
   // The .debug$P or .debug$T section data if present. Empty otherwise.
   ArrayRef<uint8_t> debugTypes;
 
-  llvm::Optional<std::pair<StringRef, uint32_t>>
+  std::optional<std::pair<StringRef, uint32_t>>
   getVariableLocation(StringRef var);
 
-  llvm::Optional<llvm::DILineInfo> getDILineInfo(uint32_t offset,
-                                                 uint32_t sectionIndex);
+  std::optional<llvm::DILineInfo> getDILineInfo(uint32_t offset,
+                                                uint32_t sectionIndex);
 
 private:
   const coff_section* getSection(uint32_t i);
@@ -228,6 +218,8 @@ private:
     return getSection(sym.getSectionNumber());
   }
 
+  void enqueuePdbFile(StringRef path, ObjFile *fromFile);
+
   void initializeChunks();
   void initializeSymbols();
   void initializeFlags();
@@ -266,7 +258,7 @@ private:
                         bool &prevailing, DefinedRegular *leader,
                         const llvm::object::coff_aux_section_definition *def);
 
-  llvm::Optional<Symbol *>
+  std::optional<Symbol *>
   createDefined(COFFSymbolRef sym,
                 std::vector<const llvm::object::coff_aux_section_definition *>
                     &comdatDefs,
@@ -318,19 +310,16 @@ private:
 // stream.
 class PDBInputFile : public InputFile {
 public:
-  explicit PDBInputFile(MemoryBufferRef m);
+  explicit PDBInputFile(COFFLinkerContext &ctx, MemoryBufferRef m);
   ~PDBInputFile();
   static bool classof(const InputFile *f) { return f->kind() == PDBKind; }
   void parse() override;
 
-  static void enqueue(StringRef path, ObjFile *fromFile);
-
-  static PDBInputFile *findFromRecordPath(StringRef path, ObjFile *fromFile);
-
-  static std::map<std::string, PDBInputFile *> instances;
+  static PDBInputFile *findFromRecordPath(const COFFLinkerContext &ctx,
+                                          StringRef path, ObjFile *fromFile);
 
   // Record possible errors while opening the PDB file
-  llvm::Optional<Error> loadErr;
+  std::optional<std::string> loadErrorStr;
 
   // This is the actual interface to the PDB (if it was opened successfully)
   std::unique_ptr<llvm::pdb::NativeSession> session;
@@ -344,12 +333,10 @@ public:
 // for details about the format.
 class ImportFile : public InputFile {
 public:
-  explicit ImportFile(MemoryBufferRef m) : InputFile(ImportKind, m) {}
+  explicit ImportFile(COFFLinkerContext &ctx, MemoryBufferRef m);
 
   static bool classof(const InputFile *f) { return f->kind() == ImportKind; }
 
-  static std::vector<ImportFile *> instances;
-
   Symbol *impSym = nullptr;
   Symbol *thunkSym = nullptr;
   std::string dllName;
@@ -370,23 +357,21 @@ public:
   // symbols provided by this import library member. We also track whether the
   // imported symbol is used separately from whether the thunk is used in order
   // to avoid creating unnecessary thunks.
-  bool live = !config->doGC;
-  bool thunkLive = !config->doGC;
+  bool live;
+  bool thunkLive;
 };
 
 // Used for LTO.
 class BitcodeFile : public InputFile {
 public:
-  BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
-              uint64_t offsetInArchive);
-  explicit BitcodeFile(MemoryBufferRef m, StringRef archiveName,
-                       uint64_t offsetInArchive,
-                       std::vector<Symbol *> &&symbols);
+  explicit BitcodeFile(COFFLinkerContext &ctx, MemoryBufferRef mb,
+                       StringRef archiveName, uint64_t offsetInArchive,
+                       bool lazy);
   ~BitcodeFile();
   static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
   ArrayRef<Symbol *> getSymbols() { return symbols; }
   MachineTypes getMachineType() override;
-  static std::vector<BitcodeFile *> instances;
+  void parseLazy();
   std::unique_ptr<llvm::lto::InputFile> obj;
 
 private:
@@ -398,7 +383,8 @@ private:
 // .dll file. MinGW only.
 class DLLFile : public InputFile {
 public:
-  explicit DLLFile(MemoryBufferRef m) : InputFile(DLLKind, m) {}
+  explicit DLLFile(COFFLinkerContext &ctx, MemoryBufferRef m)
+      : InputFile(ctx, DLLKind, m) {}
   static bool classof(const InputFile *f) { return f->kind() == DLLKind; }
   void parse() override;
   MachineTypes getMachineType() override;
@@ -421,7 +407,8 @@ inline bool isBitcode(MemoryBufferRef mb) {
   return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode;
 }
 
-std::string replaceThinLTOSuffix(StringRef path);
+std::string replaceThinLTOSuffix(StringRef path, StringRef suffix,
+                                 StringRef repl);
 } // namespace coff
 
 std::string toString(const coff::InputFile *file);
index 79df33a..c14480a 100644 (file)
@@ -19,6 +19,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "LLDMapFile.h"
+#include "COFFLinkerContext.h"
 #include "SymbolTable.h"
 #include "Symbols.h"
 #include "Writer.h"
@@ -44,9 +45,9 @@ static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size,
 }
 
 // Returns a list of all symbols that we want to print out.
-static std::vector<DefinedRegular *> getSymbols() {
+static std::vector<DefinedRegular *> getSymbols(const COFFLinkerContext &ctx) {
   std::vector<DefinedRegular *> v;
-  for (ObjFile *file : ObjFile::instances)
+  for (ObjFile *file : ctx.objFileInstances)
     for (Symbol *b : file->getSymbols())
       if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))
         if (sym && !sym->getCOFFSymbol().isSectionDefinition())
@@ -72,12 +73,13 @@ static SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> syms) {
 
 // Construct a map from symbols to their stringified representations.
 static DenseMap<DefinedRegular *, std::string>
-getSymbolStrings(ArrayRef<DefinedRegular *> syms) {
+getSymbolStrings(const COFFLinkerContext &ctx,
+                 ArrayRef<DefinedRegular *> syms) {
   std::vector<std::string> str(syms.size());
-  parallelForEachN((size_t)0, syms.size(), [&](size_t i) {
+  parallelFor((size_t)0, syms.size(), [&](size_t i) {
     raw_string_ostream os(str[i]);
     writeHeader(os, syms[i]->getRVA(), 0, 0);
-    os << indent16 << toString(*syms[i]);
+    os << indent16 << toString(ctx, *syms[i]);
   });
 
   DenseMap<DefinedRegular *, std::string> ret;
@@ -86,25 +88,25 @@ getSymbolStrings(ArrayRef<DefinedRegular *> syms) {
   return ret;
 }
 
-void lld::coff::writeLLDMapFile(ArrayRef<OutputSection *> outputSections) {
-  if (config->lldmapFile.empty())
+void lld::coff::writeLLDMapFile(const COFFLinkerContext &ctx) {
+  if (ctx.config.lldmapFile.empty())
     return;
 
   std::error_code ec;
-  raw_fd_ostream os(config->lldmapFile, ec, sys::fs::OF_None);
+  raw_fd_ostream os(ctx.config.lldmapFile, ec, sys::fs::OF_None);
   if (ec)
-    fatal("cannot open " + config->lldmapFile + ": " + ec.message());
+    fatal("cannot open " + ctx.config.lldmapFile + ": " + ec.message());
 
   // Collect symbol info that we want to print out.
-  std::vector<DefinedRegular *> syms = getSymbols();
+  std::vector<DefinedRegular *> syms = getSymbols(ctx);
   SymbolMapTy sectionSyms = getSectionSyms(syms);
-  DenseMap<DefinedRegular *, std::string> symStr = getSymbolStrings(syms);
+  DenseMap<DefinedRegular *, std::string> symStr = getSymbolStrings(ctx, syms);
 
   // Print out the header line.
   os << "Address  Size     Align Out     In      Symbol\n";
 
   // Print out file contents.
-  for (OutputSection *sec : outputSections) {
+  for (OutputSection *sec : ctx.outputSections) {
     writeHeader(os, sec->getRVA(), sec->getVirtualSize(), /*align=*/pageSize);
     os << sec->name << '\n';
 
index b731293..7cbc21f 100644 (file)
@@ -9,13 +9,9 @@
 #ifndef LLD_COFF_LLDMAPFILE_H
 #define LLD_COFF_LLDMAPFILE_H
 
-#include "llvm/ADT/ArrayRef.h"
-
-namespace lld {
-namespace coff {
-class OutputSection;
-void writeLLDMapFile(llvm::ArrayRef<OutputSection *> outputSections);
-}
+namespace lld::coff {
+class COFFLinkerContext;
+void writeLLDMapFile(const COFFLinkerContext &ctx);
 }
 
 #endif
index d117abf..b5643b8 100644 (file)
@@ -7,11 +7,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "LTO.h"
+#include "COFFLinkerContext.h"
 #include "Config.h"
 #include "InputFiles.h"
 #include "Symbols.h"
 #include "lld/Common/Args.h"
-#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/CommonLinkerContext.h"
 #include "lld/Common/Strings.h"
 #include "lld/Common/TargetOptionsCommandFlags.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/Bitcode/BitcodeWriter.h"
 #include "llvm/IR/DiagnosticPrinter.h"
-#include "llvm/LTO/Caching.h"
 #include "llvm/LTO/Config.h"
 #include "llvm/LTO/LTO.h"
 #include "llvm/Object/SymbolicFile.h"
+#include "llvm/Support/Caching.h"
 #include "llvm/Support/CodeGen.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/FileSystem.h"
@@ -53,16 +54,18 @@ static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
   return ret;
 }
 
-static std::string getThinLTOOutputFile(StringRef path) {
+std::string BitcodeCompiler::getThinLTOOutputFile(StringRef path) {
   return lto::getThinLTOOutputFile(
-      std::string(path), std::string(config->thinLTOPrefixReplace.first),
-      std::string(config->thinLTOPrefixReplace.second));
+      std::string(path), std::string(ctx.config.thinLTOPrefixReplace.first),
+      std::string(ctx.config.thinLTOPrefixReplace.second));
 }
 
-static lto::Config createConfig() {
+lto::Config BitcodeCompiler::createConfig() {
   lto::Config c;
   c.Options = initTargetOptionsFromCodeGenFlags();
   c.Options.EmitAddrsig = true;
+  for (StringRef C : ctx.config.mllvmOpts)
+    c.MllvmArgs.emplace_back(C.str());
 
   // Always emit a section per function/datum with LTO. LLVM LTO should get most
   // of the benefit of linker GC, but there are still opportunities for ICF.
@@ -72,48 +75,52 @@ static lto::Config createConfig() {
   // Use static reloc model on 32-bit x86 because it usually results in more
   // compact code, and because there are also known code generation bugs when
   // using the PIC model (see PR34306).
-  if (config->machine == COFF::IMAGE_FILE_MACHINE_I386)
+  if (ctx.config.machine == COFF::IMAGE_FILE_MACHINE_I386)
     c.RelocModel = Reloc::Static;
   else
     c.RelocModel = Reloc::PIC_;
+#ifndef NDEBUG
+  c.DisableVerify = false;
+#else
   c.DisableVerify = true;
+#endif
   c.DiagHandler = diagnosticHandler;
-  c.OptLevel = config->ltoo;
+  c.OptLevel = ctx.config.ltoo;
   c.CPU = getCPUStr();
   c.MAttrs = getMAttrs();
-  c.CGOptLevel = args::getCGOptLevel(config->ltoo);
-  c.AlwaysEmitRegularLTOObj = !config->ltoObjPath.empty();
-  c.UseNewPM = config->ltoNewPassManager;
-  c.DebugPassManager = config->ltoDebugPassManager;
-  c.CSIRProfile = std::string(config->ltoCSProfileFile);
-  c.RunCSIRInstr = config->ltoCSProfileGenerate;
-
-  if (config->saveTemps)
-    checkError(c.addSaveTemps(std::string(config->outputFile) + ".",
+  c.CGOptLevel = args::getCGOptLevel(ctx.config.ltoo);
+  c.AlwaysEmitRegularLTOObj = !ctx.config.ltoObjPath.empty();
+  c.DebugPassManager = ctx.config.ltoDebugPassManager;
+  c.CSIRProfile = std::string(ctx.config.ltoCSProfileFile);
+  c.RunCSIRInstr = ctx.config.ltoCSProfileGenerate;
+  c.PGOWarnMismatch = ctx.config.ltoPGOWarnMismatch;
+
+  if (ctx.config.saveTemps)
+    checkError(c.addSaveTemps(std::string(ctx.config.outputFile) + ".",
                               /*UseInputModulePath*/ true));
   return c;
 }
 
-BitcodeCompiler::BitcodeCompiler() {
+BitcodeCompiler::BitcodeCompiler(COFFLinkerContext &c) : ctx(c) {
   // Initialize indexFile.
-  if (!config->thinLTOIndexOnlyArg.empty())
-    indexFile = openFile(config->thinLTOIndexOnlyArg);
+  if (!ctx.config.thinLTOIndexOnlyArg.empty())
+    indexFile = openFile(ctx.config.thinLTOIndexOnlyArg);
 
   // Initialize ltoObj.
   lto::ThinBackend backend;
-  if (config->thinLTOIndexOnly) {
+  if (ctx.config.thinLTOIndexOnly) {
     auto OnIndexWrite = [&](StringRef S) { thinIndices.erase(S); };
     backend = lto::createWriteIndexesThinBackend(
-        std::string(config->thinLTOPrefixReplace.first),
-        std::string(config->thinLTOPrefixReplace.second),
-        config->thinLTOEmitImportsFiles, indexFile.get(), OnIndexWrite);
+        std::string(ctx.config.thinLTOPrefixReplace.first),
+        std::string(ctx.config.thinLTOPrefixReplace.second),
+        ctx.config.thinLTOEmitImportsFiles, indexFile.get(), OnIndexWrite);
   } else {
     backend = lto::createInProcessThinBackend(
-        llvm::heavyweight_hardware_concurrency(config->thinLTOJobs));
+        llvm::heavyweight_hardware_concurrency(ctx.config.thinLTOJobs));
   }
 
   ltoObj = std::make_unique<lto::LTO>(createConfig(), backend,
-                                       config->ltoPartitions);
+                                      ctx.config.ltoPartitions);
 }
 
 BitcodeCompiler::~BitcodeCompiler() = default;
@@ -126,7 +133,7 @@ void BitcodeCompiler::add(BitcodeFile &f) {
   std::vector<Symbol *> symBodies = f.getSymbols();
   std::vector<lto::SymbolResolution> resols(symBodies.size());
 
-  if (config->thinLTOIndexOnly)
+  if (ctx.config.thinLTOIndexOnly)
     thinIndices.insert(obj.getName());
 
   // Provide a resolution to the LTO API for each symbol.
@@ -159,21 +166,25 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
   unsigned maxTasks = ltoObj->getMaxTasks();
   buf.resize(maxTasks);
   files.resize(maxTasks);
+  file_names.resize(maxTasks);
 
   // The /lldltocache option specifies the path to a directory in which to cache
   // native object files for ThinLTO incremental builds. If a path was
   // specified, configure LTO to use it as the cache directory.
-  lto::NativeObjectCache cache;
-  if (!config->ltoCache.empty())
-    cache = check(lto::localCache(
-        config->ltoCache, [&](size_t task, std::unique_ptr<MemoryBuffer> mb) {
-          files[task] = std::move(mb);
-        }));
+  FileCache cache;
+  if (!ctx.config.ltoCache.empty())
+    cache = check(localCache("ThinLTO", "Thin", ctx.config.ltoCache,
+                             [&](size_t task, const Twine &moduleName,
+                                 std::unique_ptr<MemoryBuffer> mb) {
+                               files[task] = std::move(mb);
+                               file_names[task] = moduleName.str();
+                             }));
 
   checkError(ltoObj->run(
-      [&](size_t task) {
-        return std::make_unique<lto::NativeObjectStream>(
-            std::make_unique<raw_svector_ostream>(buf[task]));
+      [&](size_t task, const Twine &moduleName) {
+        buf[task].first = moduleName.str();
+        return std::make_unique<CachedFileStream>(
+            std::make_unique<raw_svector_ostream>(buf[task].second));
       },
       cache));
 
@@ -181,49 +192,61 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
   for (StringRef s : thinIndices) {
     std::string path = getThinLTOOutputFile(s);
     openFile(path + ".thinlto.bc");
-    if (config->thinLTOEmitImportsFiles)
+    if (ctx.config.thinLTOEmitImportsFiles)
       openFile(path + ".imports");
   }
 
   // ThinLTO with index only option is required to generate only the index
   // files. After that, we exit from linker and ThinLTO backend runs in a
   // distributed environment.
-  if (config->thinLTOIndexOnly) {
-    if (!config->ltoObjPath.empty())
-      saveBuffer(buf[0], config->ltoObjPath);
+  if (ctx.config.thinLTOIndexOnly) {
+    if (!ctx.config.ltoObjPath.empty())
+      saveBuffer(buf[0].second, ctx.config.ltoObjPath);
     if (indexFile)
       indexFile->close();
     return {};
   }
 
-  if (!config->ltoCache.empty())
-    pruneCache(config->ltoCache, config->ltoCachePolicy);
+  if (!ctx.config.ltoCache.empty())
+    pruneCache(ctx.config.ltoCache, ctx.config.ltoCachePolicy, files);
 
   std::vector<InputFile *> ret;
   for (unsigned i = 0; i != maxTasks; ++i) {
-    // Assign unique names to LTO objects. This ensures they have unique names
-    // in the PDB if one is produced. The names should look like:
-    // - foo.exe.lto.obj
-    // - foo.exe.lto.1.obj
-    // - ...
-    StringRef ltoObjName =
-        saver.save(Twine(config->outputFile) + ".lto" +
-                   (i == 0 ? Twine("") : Twine('.') + Twine(i)) + ".obj");
-
+    StringRef bitcodeFilePath;
     // Get the native object contents either from the cache or from memory.  Do
     // not use the cached MemoryBuffer directly, or the PDB will not be
     // deterministic.
     StringRef objBuf;
-    if (files[i])
+    if (files[i]) {
       objBuf = files[i]->getBuffer();
-    else
-      objBuf = buf[i];
+      bitcodeFilePath = file_names[i];
+    } else {
+      objBuf = buf[i].second;
+      bitcodeFilePath = buf[i].first;
+    }
     if (objBuf.empty())
       continue;
 
-    if (config->saveTemps)
-      saveBuffer(buf[i], ltoObjName);
-    ret.push_back(make<ObjFile>(MemoryBufferRef(objBuf, ltoObjName)));
+    // If the input bitcode file is path/to/a.obj, then the corresponding lto
+    // object file name will look something like: path/to/main.exe.lto.a.obj.
+    StringRef ltoObjName;
+    if (bitcodeFilePath == "ld-temp.o") {
+      ltoObjName =
+          saver().save(Twine(ctx.config.outputFile) + ".lto" +
+                       (i == 0 ? Twine("") : Twine('.') + Twine(i)) + ".obj");
+    } else {
+      StringRef directory = sys::path::parent_path(bitcodeFilePath);
+      StringRef baseName = sys::path::filename(bitcodeFilePath);
+      StringRef outputFileBaseName = sys::path::filename(ctx.config.outputFile);
+      SmallString<64> path;
+      sys::path::append(path, directory,
+                        outputFileBaseName + ".lto." + baseName);
+      sys::path::remove_dots(path, true);
+      ltoObjName = saver().save(path.str());
+    }
+    if (ctx.config.saveTemps)
+      saveBuffer(buf[i].second, ltoObjName);
+    ret.push_back(make<ObjFile>(ctx, MemoryBufferRef(objBuf, ltoObjName)));
   }
 
   return ret;
index a2b321d..6826251 100644 (file)
 #include <memory>
 #include <vector>
 
-namespace llvm {
-namespace lto {
+namespace llvm::lto {
+struct Config;
 class LTO;
 }
-}
 
-namespace lld {
-namespace coff {
+namespace lld::coff {
 
 class BitcodeFile;
 class InputFile;
+class COFFLinkerContext;
 
 class BitcodeCompiler {
 public:
-  BitcodeCompiler();
+  BitcodeCompiler(COFFLinkerContext &ctx);
   ~BitcodeCompiler();
 
   void add(BitcodeFile &f);
@@ -49,12 +48,17 @@ public:
 
 private:
   std::unique_ptr<llvm::lto::LTO> ltoObj;
-  std::vector<SmallString<0>> buf;
+  std::vector<std::pair<std::string, SmallString<0>>> buf;
   std::vector<std::unique_ptr<MemoryBuffer>> files;
+  std::vector<std::string> file_names;
   std::unique_ptr<llvm::raw_fd_ostream> indexFile;
   llvm::DenseSet<StringRef> thinIndices;
+
+  std::string getThinLTOOutputFile(StringRef path);
+  llvm::lto::Config createConfig();
+
+  COFFLinkerContext &ctx;
 };
 }
-}
 
 #endif
index 41e169e..f7a4ef9 100644 (file)
@@ -28,6 +28,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "MapFile.h"
+#include "COFFLinkerContext.h"
 #include "SymbolTable.h"
 #include "Symbols.h"
 #include "Writer.h"
@@ -42,11 +43,6 @@ using namespace llvm::object;
 using namespace lld;
 using namespace lld::coff;
 
-static Timer totalMapTimer("MAP emission (Cumulative)", Timer::root());
-static Timer symbolGatherTimer("Gather symbols", totalMapTimer);
-static Timer symbolStringsTimer("Build symbol strings", totalMapTimer);
-static Timer writeTimer("Write to file", totalMapTimer);
-
 // Print out the first two columns of a line.
 static void writeHeader(raw_ostream &os, uint32_t sec, uint64_t addr) {
   os << format(" %04x:%08llx", sec, addr);
@@ -67,7 +63,8 @@ static void writeFormattedTimestamp(raw_ostream &os, time_t tds) {
                time->tm_sec, time->tm_year + 1900);
 }
 
-static void sortUniqueSymbols(std::vector<Defined *> &syms) {
+static void sortUniqueSymbols(std::vector<Defined *> &syms,
+                              uint64_t imageBase) {
   // Build helper vector
   using SortEntry = std::pair<Defined *, size_t>;
   std::vector<SortEntry> v;
@@ -84,11 +81,11 @@ static void sortUniqueSymbols(std::vector<Defined *> &syms) {
   v.erase(end, v.end());
 
   // Sort by RVA then original order
-  parallelSort(v, [](const SortEntry &a, const SortEntry &b) {
-    // Add config->imageBase to avoid comparing "negative" RVAs.
+  parallelSort(v, [imageBase](const SortEntry &a, const SortEntry &b) {
+    // Add config.imageBase to avoid comparing "negative" RVAs.
     // This can happen with symbols of Absolute kind
-    uint64_t rvaa = config->imageBase + a.first->getRVA();
-    uint64_t rvab = config->imageBase + b.first->getRVA();
+    uint64_t rvaa = imageBase + a.first->getRVA();
+    uint64_t rvab = imageBase + b.first->getRVA();
     return rvaa < rvab || (rvaa == rvab && a.second < b.second);
   });
 
@@ -98,10 +95,11 @@ static void sortUniqueSymbols(std::vector<Defined *> &syms) {
 }
 
 // Returns the lists of all symbols that we want to print out.
-static void getSymbols(std::vector<Defined *> &syms,
+static void getSymbols(const COFFLinkerContext &ctx,
+                       std::vector<Defined *> &syms,
                        std::vector<Defined *> &staticSyms) {
 
-  for (ObjFile *file : ObjFile::instances)
+  for (ObjFile *file : ctx.objFileInstances)
     for (Symbol *b : file->getSymbols()) {
       if (!b || !b->isLive())
         continue;
@@ -119,7 +117,7 @@ static void getSymbols(std::vector<Defined *> &syms,
       }
     }
 
-  for (ImportFile *file : ImportFile::instances) {
+  for (ImportFile *file : ctx.importFileInstances) {
     if (!file->live)
       continue;
 
@@ -136,15 +134,15 @@ static void getSymbols(std::vector<Defined *> &syms,
       syms.push_back(impSym);
   }
 
-  sortUniqueSymbols(syms);
-  sortUniqueSymbols(staticSyms);
+  sortUniqueSymbols(syms, ctx.config.imageBase);
+  sortUniqueSymbols(staticSyms, ctx.config.imageBase);
 }
 
 // Construct a map from symbols to their stringified representations.
 static DenseMap<Defined *, std::string>
-getSymbolStrings(ArrayRef<Defined *> syms) {
+getSymbolStrings(const COFFLinkerContext &ctx, ArrayRef<Defined *> syms) {
   std::vector<std::string> str(syms.size());
-  parallelForEachN((size_t)0, syms.size(), [&](size_t i) {
+  parallelFor((size_t)0, syms.size(), [&](size_t i) {
     raw_string_ostream os(str[i]);
     Defined *sym = syms[i];
 
@@ -161,7 +159,7 @@ getSymbolStrings(ArrayRef<Defined *> syms) {
       fileDescr = "<common>";
     } else if (Chunk *chunk = sym->getChunk()) {
       address = sym->getRVA();
-      if (OutputSection *sec = chunk->getOutputSection())
+      if (OutputSection *sec = ctx.getOutputSection(chunk))
         address -= sec->header.VirtualAddress;
 
       sectionIdx = chunk->getOutputSectionIdx();
@@ -187,7 +185,7 @@ getSymbolStrings(ArrayRef<Defined *> syms) {
     os << "       ";
     os << left_justify(sym->getName(), 26);
     os << " ";
-    os << format_hex_no_prefix((config->imageBase + sym->getRVA()), 16);
+    os << format_hex_no_prefix((ctx.config.imageBase + sym->getRVA()), 16);
     if (!fileDescr.empty()) {
       os << "     "; // FIXME : Handle "f" and "i" flags sometimes generated
                      // by link.exe in those spaces
@@ -201,54 +199,56 @@ getSymbolStrings(ArrayRef<Defined *> syms) {
   return ret;
 }
 
-void lld::coff::writeMapFile(ArrayRef<OutputSection *> outputSections) {
-  if (config->mapFile.empty())
+void lld::coff::writeMapFile(COFFLinkerContext &ctx) {
+  if (ctx.config.mapFile.empty())
     return;
 
   std::error_code ec;
-  raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None);
+  raw_fd_ostream os(ctx.config.mapFile, ec, sys::fs::OF_None);
   if (ec)
-    fatal("cannot open " + config->mapFile + ": " + ec.message());
+    fatal("cannot open " + ctx.config.mapFile + ": " + ec.message());
 
-  ScopedTimer t1(totalMapTimer);
+  ScopedTimer t1(ctx.totalMapTimer);
 
   // Collect symbol info that we want to print out.
-  ScopedTimer t2(symbolGatherTimer);
+  ScopedTimer t2(ctx.symbolGatherTimer);
   std::vector<Defined *> syms;
   std::vector<Defined *> staticSyms;
-  getSymbols(syms, staticSyms);
+  getSymbols(ctx, syms, staticSyms);
   t2.stop();
 
-  ScopedTimer t3(symbolStringsTimer);
-  DenseMap<Defined *, std::string> symStr = getSymbolStrings(syms);
-  DenseMap<Defined *, std::string> staticSymStr = getSymbolStrings(staticSyms);
+  ScopedTimer t3(ctx.symbolStringsTimer);
+  DenseMap<Defined *, std::string> symStr = getSymbolStrings(ctx, syms);
+  DenseMap<Defined *, std::string> staticSymStr =
+      getSymbolStrings(ctx, staticSyms);
   t3.stop();
 
-  ScopedTimer t4(writeTimer);
-  SmallString<128> AppName = sys::path::filename(config->outputFile);
+  ScopedTimer t4(ctx.writeTimer);
+  SmallString<128> AppName = sys::path::filename(ctx.config.outputFile);
   sys::path::replace_extension(AppName, "");
 
   // Print out the file header
   os << " " << AppName << "\n";
   os << "\n";
 
-  os << " Timestamp is " << format_hex_no_prefix(config->timestamp, 8) << " (";
-  if (config->repro) {
+  os << " Timestamp is " << format_hex_no_prefix(ctx.config.timestamp, 8)
+     << " (";
+  if (ctx.config.repro) {
     os << "Repro mode";
   } else {
-    writeFormattedTimestamp(os, config->timestamp);
+    writeFormattedTimestamp(os, ctx.config.timestamp);
   }
   os << ")\n";
 
   os << "\n";
   os << " Preferred load address is "
-     << format_hex_no_prefix(config->imageBase, 16) << "\n";
+     << format_hex_no_prefix(ctx.config.imageBase, 16) << "\n";
   os << "\n";
 
   // Print out section table.
   os << " Start         Length     Name                   Class\n";
 
-  for (OutputSection *sec : outputSections) {
+  for (OutputSection *sec : ctx.outputSections) {
     // Merge display of chunks with same sectionName
     std::vector<std::pair<SectionChunk *, SectionChunk *>> ChunkRanges;
     for (Chunk *c : sec->chunks) {
@@ -297,13 +297,13 @@ void lld::coff::writeMapFile(ArrayRef<OutputSection *> outputSections) {
   uint16_t entrySecIndex = 0;
   uint64_t entryAddress = 0;
 
-  if (!config->noEntry) {
-    Defined *entry = dyn_cast_or_null<Defined>(config->entry);
+  if (!ctx.config.noEntry) {
+    Defined *entry = dyn_cast_or_null<Defined>(ctx.config.entry);
     if (entry) {
       Chunk *chunk = entry->getChunk();
       entrySecIndex = chunk->getOutputSectionIdx();
       entryAddress =
-          entry->getRVA() - chunk->getOutputSection()->header.VirtualAddress;
+          entry->getRVA() - ctx.getOutputSection(chunk)->header.VirtualAddress;
     }
   }
   os << " entry point at         ";
@@ -317,6 +317,19 @@ void lld::coff::writeMapFile(ArrayRef<OutputSection *> outputSections) {
   for (Defined *sym : staticSyms)
     os << staticSymStr[sym] << '\n';
 
+  // Print out the exported functions
+  if (ctx.config.mapInfo) {
+    os << "\n";
+    os << " Exports\n";
+    os << "\n";
+    os << "  ordinal    name\n\n";
+    for (Export &e : ctx.config.exports) {
+      os << format("  %7d", e.ordinal) << "    " << e.name << "\n";
+      if (!e.extName.empty() && e.extName != e.name)
+        os << "               exported name: " << e.extName << "\n";
+    }
+  }
+
   t4.stop();
   t1.stop();
 }
index 2bf01bd..de1e990 100644 (file)
@@ -9,13 +9,9 @@
 #ifndef LLD_COFF_MAPFILE_H
 #define LLD_COFF_MAPFILE_H
 
-#include "llvm/ADT/ArrayRef.h"
-
-namespace lld {
-namespace coff {
-class OutputSection;
-void writeMapFile(llvm::ArrayRef<OutputSection *> outputSections);
-}
+namespace lld::coff {
+class COFFLinkerContext;
+void writeMapFile(COFFLinkerContext &ctx);
 }
 
 #endif
index 0afa615..ad8c340 100644 (file)
@@ -6,22 +6,20 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "COFFLinkerContext.h"
 #include "Chunks.h"
 #include "Symbols.h"
 #include "lld/Common/Timer.h"
 #include "llvm/ADT/STLExtras.h"
 #include <vector>
 
-namespace lld {
-namespace coff {
-
-static Timer gctimer("GC", Timer::root());
+namespace lld::coff {
 
 // Set live bit on for each reachable chunk. Unmarked (unreachable)
 // COMDAT chunks will be ignored by Writer, so they will be excluded
 // from the final output.
-void markLive(ArrayRef<Chunk *> chunks) {
-  ScopedTimer t(gctimer);
+void markLive(COFFLinkerContext &ctx) {
+  ScopedTimer t(ctx.gcTimer);
 
   // We build up a worklist of sections which have been marked as live. We only
   // push into the worklist when we discover an unmarked section, and we mark
@@ -31,7 +29,7 @@ void markLive(ArrayRef<Chunk *> chunks) {
   // COMDAT section chunks are dead by default. Add non-COMDAT chunks. Do not
   // traverse DWARF sections. They are live, but they should not keep other
   // sections alive.
-  for (Chunk *c : chunks)
+  for (Chunk *c : ctx.symtab.getChunks())
     if (auto *sc = dyn_cast<SectionChunk>(c))
       if (sc->live && !sc->isDWARF())
         worklist.push_back(sc);
@@ -53,7 +51,7 @@ void markLive(ArrayRef<Chunk *> chunks) {
   };
 
   // Add GC root chunks.
-  for (Symbol *b : config->gcroot)
+  for (Symbol *b : ctx.config.gcroot)
     addSym(b);
 
   while (!worklist.empty()) {
@@ -70,6 +68,4 @@ void markLive(ArrayRef<Chunk *> chunks) {
       enqueue(&c);
   }
 }
-
-}
 }
index e4e4c31..8382223 100644 (file)
 #define LLD_COFF_MARKLIVE_H
 
 #include "lld/Common/LLVM.h"
-#include "llvm/ADT/ArrayRef.h"
 
-namespace lld {
-namespace coff {
+namespace lld::coff {
 
-class Chunk;
+class COFFLinkerContext;
 
-void markLive(ArrayRef<Chunk *> chunks);
+void markLive(COFFLinkerContext &ctx);
 
-} // namespace coff
-} // namespace lld
+} // namespace lld::coff
 
 #endif // LLD_COFF_MARKLIVE_H
index 7c1891e..71aa596 100644 (file)
@@ -7,10 +7,10 @@
 //===----------------------------------------------------------------------===//
 
 #include "MinGW.h"
+#include "COFFLinkerContext.h"
 #include "Driver.h"
 #include "InputFiles.h"
 #include "SymbolTable.h"
-#include "lld/Common/ErrorHandler.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/Object/COFF.h"
@@ -23,7 +23,10 @@ using namespace llvm::COFF;
 using namespace lld;
 using namespace lld::coff;
 
-AutoExporter::AutoExporter() {
+AutoExporter::AutoExporter(
+    COFFLinkerContext &ctx,
+    const llvm::DenseSet<StringRef> &manualExcludeSymbols)
+    : manualExcludeSymbols(manualExcludeSymbols), ctx(ctx) {
   excludeLibs = {
       "libgcc",
       "libgcc_s",
@@ -46,6 +49,9 @@ AutoExporter::AutoExporter() {
       "libclang_rt.profile-x86_64",
       "libc++",
       "libc++abi",
+      "libFortran_main",
+      "libFortranRuntime",
+      "libFortranDecimal",
       "libunwind",
       "libmsvcrt",
       "libucrtbase",
@@ -78,7 +84,7 @@ AutoExporter::AutoExporter() {
       "_NULL_THUNK_DATA",
   };
 
-  if (config->machine == I386) {
+  if (ctx.config.machine == I386) {
     excludeSymbols = {
         "__NULL_IMPORT_DESCRIPTOR",
         "__pei386_runtime_relocator",
@@ -122,6 +128,10 @@ void AutoExporter::addWholeArchive(StringRef path) {
   excludeLibs.erase(libName);
 }
 
+void AutoExporter::addExcludedSymbol(StringRef symbol) {
+  excludeSymbols.insert(symbol);
+}
+
 bool AutoExporter::shouldExport(Defined *sym) const {
   if (!sym || !sym->getChunk())
     return false;
@@ -130,7 +140,7 @@ bool AutoExporter::shouldExport(Defined *sym) const {
   // disallow import symbols.
   if (!isa<DefinedRegular>(sym) && !isa<DefinedCommon>(sym))
     return false;
-  if (excludeSymbols.count(sym->getName()))
+  if (excludeSymbols.count(sym->getName()) || manualExcludeSymbols.count(sym->getName()))
     return false;
 
   for (StringRef prefix : excludeSymbolPrefixes.keys())
@@ -141,7 +151,7 @@ bool AutoExporter::shouldExport(Defined *sym) const {
       return false;
 
   // If a corresponding __imp_ symbol exists and is defined, don't export it.
-  if (symtab->find(("__imp_" + sym->getName()).str()))
+  if (ctx.symtab.find(("__imp_" + sym->getName()).str()))
     return false;
 
   // Check that file is non-null before dereferencing it, symbols not
@@ -160,14 +170,15 @@ bool AutoExporter::shouldExport(Defined *sym) const {
   return !excludeObjects.count(fileName);
 }
 
-void lld::coff::writeDefFile(StringRef name) {
+void lld::coff::writeDefFile(StringRef name,
+                             const std::vector<Export> &exports) {
   std::error_code ec;
   raw_fd_ostream os(name, ec, sys::fs::OF_None);
   if (ec)
     fatal("cannot open " + name + ": " + ec.message());
 
   os << "EXPORTS\n";
-  for (Export &e : config->exports) {
+  for (const Export &e : exports) {
     os << "    " << e.exportName << " "
        << "@" << e.ordinal;
     if (auto *def = dyn_cast_or_null<Defined>(e.sym)) {
@@ -179,11 +190,11 @@ void lld::coff::writeDefFile(StringRef name) {
   }
 }
 
-static StringRef mangle(Twine sym) {
-  assert(config->machine != IMAGE_FILE_MACHINE_UNKNOWN);
-  if (config->machine == I386)
-    return saver.save("_" + sym);
-  return saver.save(sym);
+static StringRef mangle(Twine sym, MachineTypes machine) {
+  assert(machine != IMAGE_FILE_MACHINE_UNKNOWN);
+  if (machine == I386)
+    return saver().save("_" + sym);
+  return saver().save(sym);
 }
 
 // Handles -wrap option.
@@ -192,7 +203,7 @@ static StringRef mangle(Twine sym) {
 // like they are not being used at all, so we explicitly set some flags so
 // that LTO won't eliminate them.
 std::vector<WrappedSymbol>
-lld::coff::addWrappedSymbols(opt::InputArgList &args) {
+lld::coff::addWrappedSymbols(COFFLinkerContext &ctx, opt::InputArgList &args) {
   std::vector<WrappedSymbol> v;
   DenseSet<StringRef> seen;
 
@@ -201,18 +212,20 @@ lld::coff::addWrappedSymbols(opt::InputArgList &args) {
     if (!seen.insert(name).second)
       continue;
 
-    Symbol *sym = symtab->findUnderscore(name);
+    Symbol *sym = ctx.symtab.findUnderscore(name);
     if (!sym)
       continue;
 
-    Symbol *real = symtab->addUndefined(mangle("__real_" + name));
-    Symbol *wrap = symtab->addUndefined(mangle("__wrap_" + name));
+    Symbol *real =
+        ctx.symtab.addUndefined(mangle("__real_" + name, ctx.config.machine));
+    Symbol *wrap =
+        ctx.symtab.addUndefined(mangle("__wrap_" + name, ctx.config.machine));
     v.push_back({sym, real, wrap});
 
     // These symbols may seem undefined initially, but don't bail out
-    // at symtab->reportUnresolvable() due to them, but let wrapSymbols
+    // at symtab.reportUnresolvable() due to them, but let wrapSymbols
     // below sort things out before checking finally with
-    // symtab->resolveRemainingUndefines().
+    // symtab.resolveRemainingUndefines().
     sym->deferUndefined = true;
     real->deferUndefined = true;
     // We want to tell LTO not to inline symbols to be overwritten
@@ -233,28 +246,29 @@ lld::coff::addWrappedSymbols(opt::InputArgList &args) {
 // 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.
-void lld::coff::wrapSymbols(ArrayRef<WrappedSymbol> wrapped) {
+void lld::coff::wrapSymbols(COFFLinkerContext &ctx,
+                            ArrayRef<WrappedSymbol> wrapped) {
   DenseMap<Symbol *, Symbol *> map;
   for (const WrappedSymbol &w : wrapped) {
     map[w.sym] = w.wrap;
     map[w.real] = w.sym;
     if (Defined *d = dyn_cast<Defined>(w.wrap)) {
-      Symbol *imp = symtab->find(("__imp_" + w.sym->getName()).str());
+      Symbol *imp = ctx.symtab.find(("__imp_" + w.sym->getName()).str());
       // Create a new defined local import for the wrap symbol. If
       // no imp prefixed symbol existed, there's no need for it.
       // (We can't easily distinguish whether any object file actually
       // referenced it or not, though.)
       if (imp) {
         DefinedLocalImport *wrapimp = make<DefinedLocalImport>(
-            saver.save("__imp_" + w.wrap->getName()), d);
-        symtab->localImportChunks.push_back(wrapimp->getChunk());
+            ctx, saver().save("__imp_" + w.wrap->getName()), d);
+        ctx.symtab.localImportChunks.push_back(wrapimp->getChunk());
         map[imp] = wrapimp;
       }
     }
   }
 
   // Update pointers in input files.
-  parallelForEach(ObjFile::instances, [&](ObjFile *file) {
+  parallelForEach(ctx.objFileInstances, [&](ObjFile *file) {
     MutableArrayRef<Symbol *> syms = file->getMutableSymbols();
     for (size_t i = 0, e = syms.size(); i != e; ++i)
       if (Symbol *s = map.lookup(syms[i]))
index 2f2bd11..aa5e532 100644 (file)
 #include "Symbols.h"
 #include "lld/Common/LLVM.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/StringSet.h"
 #include "llvm/Option/ArgList.h"
 #include <vector>
 
-namespace lld {
-namespace coff {
+namespace lld::coff {
+class COFFLinkerContext;
 
 // Logic for deciding what symbols to export, when exporting all
 // symbols for MinGW.
 class AutoExporter {
 public:
-  AutoExporter();
+  AutoExporter(COFFLinkerContext &ctx,
+               const llvm::DenseSet<StringRef> &manualExcludeSymbols);
 
   void addWholeArchive(StringRef path);
+  void addExcludedSymbol(StringRef symbol);
 
   llvm::StringSet<> excludeSymbols;
   llvm::StringSet<> excludeSymbolPrefixes;
@@ -34,10 +37,15 @@ public:
   llvm::StringSet<> excludeLibs;
   llvm::StringSet<> excludeObjects;
 
+  const llvm::DenseSet<StringRef> &manualExcludeSymbols;
+
   bool shouldExport(Defined *sym) const;
+
+private:
+  COFFLinkerContext &ctx;
 };
 
-void writeDefFile(StringRef name);
+void writeDefFile(StringRef name, const std::vector<Export> &exports);
 
 // The -wrap option is a feature to rename symbols so that you can write
 // wrappers for existing functions. If you pass `-wrap:foo`, all
@@ -53,11 +61,11 @@ struct WrappedSymbol {
   Symbol *wrap;
 };
 
-std::vector<WrappedSymbol> addWrappedSymbols(llvm::opt::InputArgList &args);
+std::vector<WrappedSymbol> addWrappedSymbols(COFFLinkerContext &ctx,
+                                             llvm::opt::InputArgList &args);
 
-void wrapSymbols(ArrayRef<WrappedSymbol> wrapped);
+void wrapSymbols(COFFLinkerContext &ctx, ArrayRef<WrappedSymbol> wrapped);
 
-} // namespace coff
-} // namespace lld
+} // namespace lld::coff
 
 #endif
index 2ce1455..2a89509 100644 (file)
@@ -41,9 +41,12 @@ def color_diagnostics_eq: Joined<["--"], "color-diagnostics=">,
     MetaVarName<"[auto,always,never]">;
 def defaultlib : P<"defaultlib", "Add the library to the list of input files">;
 def delayload : P<"delayload", "Delay loaded DLL name">;
+def diasdkdir : P<"diasdkdir", "Set the location of the DIA SDK">;
 def entry   : P<"entry", "Name of entry point symbol">;
 def errorlimit : P<"errorlimit",
     "Maximum number of errors to emit before stopping (0 = no limit)">;
+def exclude_symbols  : P<"exclude-symbols", "Exclude symbols from automatic export">,
+    MetaVarName<"<symbol[,symbol,...]>">;
 def export  : P<"export", "Export a function">;
 // No help text because /failifmismatch is not intended to be used by the user.
 def failifmismatch : P<"failifmismatch", "">;
@@ -55,6 +58,8 @@ def guard   : P<"guard", "Control flow guard">;
 def heap    : P<"heap", "Size of the heap">;
 def ignore : P<"ignore", "Specify warning codes to ignore">;
 def implib  : P<"implib", "Import library name">;
+def noimplib : F<"noimplib">,
+    HelpText<"Don't output an import lib">;
 def lib : F<"lib">,
     HelpText<"Act like lib.exe; must be first argument if present">;
 def libpath : P<"libpath", "Additional library search path">;
@@ -78,8 +83,9 @@ def order   : P<"order", "Put functions in order">;
 def out     : P<"out", "Path to file to write output">;
 def natvis : P<"natvis", "Path to natvis file to embed in the PDB">;
 def pdb : P<"pdb", "PDB file path">;
-def pdbstripped : P<"pdbstripped", "Stripped PDB file path">;
 def pdbaltpath : P<"pdbaltpath", "PDB file path to embed in the image">;
+def pdbpagesize : P<"pdbpagesize", "PDB page size">;
+def pdbstripped : P<"pdbstripped", "Stripped PDB file path">;
 def pdbstream : Joined<["/", "-", "/?", "-?"], "pdbstream:">,
     MetaVarName<"<name>=<file>">,
     HelpText<"Embed the contents of <file> in the PDB as named stream <name>">;
@@ -88,9 +94,16 @@ def stack   : P<"stack", "Size of the stack">;
 def stub    : P<"stub", "Specify DOS stub file">;
 def subsystem : P<"subsystem", "Specify subsystem">;
 def timestamp : P<"timestamp", "Specify the PE header timestamp">;
+def vctoolsdir : P<"vctoolsdir", "Set the location of the VC tools">;
+def vctoolsversion : P<"vctoolsversion",
+    "Specify which VC tools version to use">;
 def version : P<"version", "Specify a version number in the PE header">;
 def wholearchive_file : P<"wholearchive",
     "Include all object files from this library">;
+def winsdkdir : P<"winsdkdir", "Set the location of the Windows SDK">;
+def winsdkversion : P<"winsdkversion", "Specify which SDK version to use">;
+def winsysroot : P<"winsysroot",
+    "Adds several subdirectories to the library search paths">;
 
 def disallowlib : Joined<["/", "-", "/?", "-?"], "disallowlib:">,
     Alias<nodefaultlib>;
@@ -148,6 +161,8 @@ def swaprun_net : F<"swaprun:net">, Alias<swaprun>, AliasArgs<["net"]>,
 def verbose : F<"verbose">;
 def wholearchive_flag : F<"wholearchive">,
     HelpText<"Include all object files from all libraries">;
+def release : F<"release">,
+    HelpText<"Set the Checksum in the header of an PE file">;
 
 def force : F<"force">,
     HelpText<"Allow undefined and multiply defined symbols">;
@@ -157,7 +172,8 @@ def force_multiple : F<"force:multiple">,
     HelpText<"Allow multiply defined symbols when creating executables">;
 def force_multipleres : F<"force:multipleres">,
     HelpText<"Allow multiply defined resources when creating executables">;
-defm WX : B<"WX", "Treat warnings as errors", "Don't treat warnings as errors">;
+defm WX : B<"WX", "Treat warnings as errors",
+                  "Don't treat warnings as errors (default)">;
 
 defm allowbind : B<"allowbind", "Enable DLL binding (default)",
                    "Disable DLL binding">;
@@ -178,6 +194,9 @@ defm highentropyva : B<"highentropyva",
 defm incremental : B<"incremental",
                      "Keep original import library if contents are unchanged",
                      "Overwrite import library even if contents are unchanged">;
+defm inferasanlibs : B<"inferasanlibs",
+                       "Unused, generates a warning",
+                       "No effect (default)">;
 defm integritycheck : B<"integritycheck",
                         "Set FORCE_INTEGRITY bit in PE header",
                         "No effect (default)">;
@@ -244,13 +263,17 @@ def lto_cs_profile_generate: F<"lto-cs-profile-generate">,
     HelpText<"Perform context sensitive PGO instrumentation">;
 def lto_cs_profile_file : P<"lto-cs-profile-file",
     "Context sensitive profile file path">;
+defm lto_pgo_warn_mismatch: B<
+     "lto-pgo-warn-mismatch",
+     "turn on warnings about profile cfg mismatch (default)>",
+     "turn off warnings about profile cfg mismatch">;
 def dash_dash_version : Flag<["--"], "version">,
   HelpText<"Display the version number and exit">;
 def threads
     : P<"threads", "Number of threads. '1' disables multi-threading. By "
                    "default all available hardware threads are used">;
 def call_graph_ordering_file: P<
-    "call-graph-ordering-file", 
+    "call-graph-ordering-file",
     "Layout sections to optimize the given callgraph">;
 defm call_graph_profile_sort: B<
     "call-graph-profile-sort",
@@ -262,11 +285,14 @@ def print_symbol_order: P<
     "/call-graph-profile-sort into the specified file">;
 def wrap : P_priv<"wrap">;
 
+def vfsoverlay : P<"vfsoverlay", "Path to a vfsoverlay yaml file to optionally look for /defaultlib's in">;
+
 // Flags for debugging
 def lldmap : F<"lldmap">;
-def lldmap_file : Joined<["/", "-", "/?", "-?"], "lldmap:">;
+def lldmap_file : P_priv<"lldmap">;
 def map : F<"map">;
-def map_file : Joined<["/", "-", "/?", "-?"], "map:">;
+def map_file : P_priv<"map">;
+def map_info : P<"mapinfo", "Include the specified information in a map file">;
 def show_timing : F<"time">;
 def summary : F<"summary">;
 
@@ -274,19 +300,26 @@ def summary : F<"summary">;
 // The flags below do nothing. They are defined only for link.exe compatibility.
 //==============================================================================
 
-class QF<string name> : Joined<["/", "-", "/?", "-?"], name#":">;
-
 def ignoreidl : F<"ignoreidl">;
+def ltcg : F<"ltcg">;
+def assemblydebug : F<"assemblydebug">;
 def nologo : F<"nologo">;
 def throwingnew : F<"throwingnew">;
 def editandcontinue : F<"editandcontinue">;
 def fastfail : F<"fastfail">;
+def kernel : F<"kernel">;
+def pdbcompress : F<"pdbcompress">;
+def emitpogophaseinfo : F<"emitpogophaseinfo">;
 
-def delay : QF<"delay">;
-def errorreport : QF<"errorreport">;
-def idlout : QF<"idlout">;
-def maxilksize : QF<"maxilksize">;
-def tlbid : QF<"tlbid">;
-def tlbout : QF<"tlbout">;
-def verbose_all : QF<"verbose">;
-def guardsym : QF<"guardsym">;
+def delay : P_priv<"delay">;
+def errorreport : P_priv<"errorreport">;
+def idlout : P_priv<"idlout">;
+def ilk : P_priv<"ilk">;
+def ltcg_opt : P_priv<"ltcg">;
+def assemblydebug_opt : P_priv<"assemblydebug">;
+def ltcgout : P_priv<"ltcgout">;
+def maxilksize : P_priv<"maxilksize">;
+def tlbid : P_priv<"tlbid">;
+def tlbout : P_priv<"tlbout">;
+def verbose_all : P_priv<"verbose">;
+def guardsym : P_priv<"guardsym">;
index e355857..745afab 100644 (file)
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "PDB.h"
+#include "COFFLinkerContext.h"
 #include "Chunks.h"
 #include "Config.h"
 #include "DebugTypes.h"
 #include "Symbols.h"
 #include "TypeMerger.h"
 #include "Writer.h"
-#include "lld/Common/ErrorHandler.h"
 #include "lld/Common/Timer.h"
 #include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h"
+#include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h"
+#include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h"
 #include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h"
 #include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h"
 #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
@@ -55,6 +57,7 @@
 #include "llvm/Support/Path.h"
 #include "llvm/Support/ScopedPrinter.h"
 #include <memory>
+#include <optional>
 
 using namespace llvm;
 using namespace llvm::codeview;
@@ -64,18 +67,6 @@ using namespace lld::coff;
 using llvm::object::coff_section;
 using llvm::pdb::StringTableFixup;
 
-static ExitOnError exitOnErr;
-
-static Timer totalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root());
-static Timer addObjectsTimer("Add Objects", totalPdbLinkTimer);
-Timer lld::coff::loadGHashTimer("Global Type Hashing", addObjectsTimer);
-Timer lld::coff::mergeGHashTimer("GHash Type Merging", addObjectsTimer);
-static Timer typeMergingTimer("Type Merging", addObjectsTimer);
-static Timer symbolMergingTimer("Symbol Merging", addObjectsTimer);
-static Timer publicsLayoutTimer("Publics Stream Layout", totalPdbLinkTimer);
-static Timer tpiStreamLayoutTimer("TPI Stream Layout", totalPdbLinkTimer);
-static Timer diskCommitTimer("Commit to Disk", totalPdbLinkTimer);
-
 namespace {
 class DebugSHandler;
 
@@ -83,8 +74,8 @@ class PDBLinker {
   friend DebugSHandler;
 
 public:
-  PDBLinker(SymbolTable *symtab)
-      : symtab(symtab), builder(bAlloc), tMerger(bAlloc) {
+  PDBLinker(COFFLinkerContext &ctx)
+      : builder(bAlloc()), tMerger(ctx, bAlloc()), ctx(ctx) {
     // This isn't strictly necessary, but link.exe usually puts an empty string
     // as the first "valid" string in the string table, so we do the same in
     // order to maintain as much byte-for-byte compatibility as possible.
@@ -107,7 +98,7 @@ public:
   void addPublicsToPDB();
 
   /// Link info for each import file in the symbol table into the PDB.
-  void addImportFilesToPDB(ArrayRef<OutputSection *> outputSections);
+  void addImportFilesToPDB();
 
   void createModuleDBI(ObjFile *file);
 
@@ -127,7 +118,7 @@ public:
                                std::vector<StringTableFixup> &stringTableFixups,
                                BinaryStreamRef symData);
 
-  // Write all module symbols from all all live debug symbol subsections of the
+  // Write all module symbols from all live debug symbol subsections of the
   // given object file into the given stream writer.
   Error writeAllModuleSymbolRecords(ObjFile *file, BinaryStreamWriter &writer);
 
@@ -144,8 +135,7 @@ public:
                          std::vector<uint8_t> &storage);
 
   /// Add the section map and section contributions to the PDB.
-  void addSections(ArrayRef<OutputSection *> outputSections,
-                   ArrayRef<uint8_t> sectionTable);
+  void addSections(ArrayRef<uint8_t> sectionTable);
 
   /// Write the PDB to disk and store the Guid generated for it in *Guid.
   void commit(codeview::GUID *guid);
@@ -154,12 +144,18 @@ public:
   void printStats();
 
 private:
-  SymbolTable *symtab;
+  void pdbMakeAbsolute(SmallVectorImpl<char> &fileName);
+  void translateIdSymbols(MutableArrayRef<uint8_t> &recordData,
+                          TpiSource *source);
+  void addCommonLinkerModuleSymbols(StringRef path,
+                                    pdb::DbiModuleDescriptorBuilder &mod);
 
   pdb::PDBFileBuilder builder;
 
   TypeMerger tMerger;
 
+  COFFLinkerContext &ctx;
+
   /// PDBs use a single global string table for filenames in the file checksum
   /// table.
   DebugStringTableSubsection pdbStrTab;
@@ -248,7 +244,7 @@ public:
 // 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) {
+void PDBLinker::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
@@ -263,9 +259,10 @@ static void pdbMakeAbsolute(SmallVectorImpl<char> &fileName) {
   // 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.
-  if (config->pdbSourcePath.empty()) {
+  if (ctx.config.pdbSourcePath.empty()) {
     sys::path::native(fileName);
     sys::fs::make_absolute(fileName);
+    sys::path::remove_dots(fileName, true);
     return;
   }
 
@@ -273,7 +270,7 @@ static void pdbMakeAbsolute(SmallVectorImpl<char> &fileName) {
   // Since PDB's are more of a Windows thing, we make this conservative and only
   // decide that it's a unix path if we're fairly certain.  Specifically, if
   // it starts with a forward slash.
-  SmallString<128> absoluteFileName = config->pdbSourcePath;
+  SmallString<128> absoluteFileName = ctx.config.pdbSourcePath;
   sys::path::Style guessedStyle = absoluteFileName.startswith("/")
                                       ? sys::path::Style::posix
                                       : sys::path::Style::windows;
@@ -298,18 +295,19 @@ static void addTypeInfo(pdb::TpiStreamBuilder &tpiBuilder,
   });
 }
 
-static void addGHashTypeInfo(pdb::PDBFileBuilder &builder) {
+static void addGHashTypeInfo(COFFLinkerContext &ctx,
+                             pdb::PDBFileBuilder &builder) {
   // Start the TPI or IPI stream header.
   builder.getTpiBuilder().setVersionHeader(pdb::PdbTpiV80);
   builder.getIpiBuilder().setVersionHeader(pdb::PdbTpiV80);
-  for_each(TpiSource::instances, [&](TpiSource *source) {
+  for (TpiSource *source : ctx.tpiSourceList) {
     builder.getTpiBuilder().addTypeRecords(source->mergedTpi.recs,
                                            source->mergedTpi.recSizes,
                                            source->mergedTpi.recHashes);
     builder.getIpiBuilder().addTypeRecords(source->mergedIpi.recs,
                                            source->mergedIpi.recSizes,
                                            source->mergedIpi.recHashes);
-  });
+  }
 }
 
 static void
@@ -343,8 +341,8 @@ static SymbolKind symbolKind(ArrayRef<uint8_t> recordData) {
 }
 
 /// MSVC translates S_PROC_ID_END to S_END, and S_[LG]PROC32_ID to S_[LG]PROC32
-static void translateIdSymbols(MutableArrayRef<uint8_t> &recordData,
-                               TypeMerger &tMerger, TpiSource *source) {
+void PDBLinker::translateIdSymbols(MutableArrayRef<uint8_t> &recordData,
+                                   TpiSource *source) {
   RecordPrefix *prefix = reinterpret_cast<RecordPrefix *>(recordData.data());
 
   SymbolKind kind = symbolKind(recordData);
@@ -375,7 +373,7 @@ static void translateIdSymbols(MutableArrayRef<uint8_t> &recordData,
     // in both cases we just need the second type index.
     if (!ti->isSimple() && !ti->isNoneType()) {
       TypeIndex newType = TypeIndex(SimpleTypeKind::NotTranslated);
-      if (config->debugGHashes) {
+      if (ctx.config.debugGHashes) {
         auto idToType = tMerger.funcIdToType.find(*ti);
         if (idToType != tMerger.funcIdToType.end())
           newType = idToType->second;
@@ -447,7 +445,6 @@ static bool symbolGoesInModuleStream(const CVSymbol &sym,
                                      unsigned symbolScopeDepth) {
   switch (sym.kind()) {
   case SymbolKind::S_GDATA32:
-  case SymbolKind::S_CONSTANT:
   case SymbolKind::S_GTHREAD32:
   // 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
@@ -456,8 +453,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.
+  // S_UDT and S_CONSTANT records go in the module stream if it is not a global record.
   case SymbolKind::S_UDT:
+  case SymbolKind::S_CONSTANT:
     return symbolScopeDepth > 0;
   // S_GDATA32 does not go in the module stream, but S_LDATA32 does.
   case SymbolKind::S_LDATA32:
@@ -470,7 +468,6 @@ static bool symbolGoesInModuleStream(const CVSymbol &sym,
 static bool symbolGoesInGlobalsStream(const CVSymbol &sym,
                                       unsigned symbolScopeDepth) {
   switch (sym.kind()) {
-  case SymbolKind::S_CONSTANT:
   case SymbolKind::S_GDATA32:
   case SymbolKind::S_GTHREAD32:
   case SymbolKind::S_GPROC32:
@@ -487,6 +484,7 @@ static bool symbolGoesInGlobalsStream(const CVSymbol &sym,
   case SymbolKind::S_UDT:
   case SymbolKind::S_LDATA32:
   case SymbolKind::S_LTHREAD32:
+  case SymbolKind::S_CONSTANT:
     return symbolScopeDepth == 0;
   default:
     return false;
@@ -496,7 +494,7 @@ static bool symbolGoesInGlobalsStream(const CVSymbol &sym,
 static void addGlobalSymbol(pdb::GSIStreamBuilder &builder, uint16_t modIndex,
                             unsigned symOffset,
                             std::vector<uint8_t> &symStorage) {
-  CVSymbol sym(makeArrayRef(symStorage));
+  CVSymbol sym{ArrayRef(symStorage)};
   switch (sym.kind()) {
   case SymbolKind::S_CONSTANT:
   case SymbolKind::S_UDT:
@@ -508,9 +506,9 @@ static void addGlobalSymbol(pdb::GSIStreamBuilder &builder, uint16_t modIndex,
   case SymbolKind::S_LPROCREF: {
     // sym is a temporary object, so we have to copy and reallocate the record
     // to stabilize it.
-    uint8_t *mem = bAlloc.Allocate<uint8_t>(sym.length());
+    uint8_t *mem = bAlloc().Allocate<uint8_t>(sym.length());
     memcpy(mem, sym.data().data(), sym.length());
-    builder.addGlobalSymbol(CVSymbol(makeArrayRef(mem, sym.length())));
+    builder.addGlobalSymbol(CVSymbol(ArrayRef(mem, sym.length())));
     break;
   }
   case SymbolKind::S_GPROC32:
@@ -580,7 +578,7 @@ void PDBLinker::writeSymbolRecord(SectionChunk *debugChunk,
 
   // An object file may have S_xxx_ID symbols, but these get converted to
   // "real" symbols in a PDB.
-  translateIdSymbols(recordBytes, tMerger, source);
+  translateIdSymbols(recordBytes, source);
 }
 
 void PDBLinker::analyzeSymbolSubsection(
@@ -647,6 +645,7 @@ void PDBLinker::analyzeSymbolSubsection(
 
 Error PDBLinker::writeAllModuleSymbolRecords(ObjFile *file,
                                              BinaryStreamWriter &writer) {
+  ExitOnError exitOnErr;
   std::vector<uint8_t> storage;
   SmallVector<uint32_t, 4> scopes;
 
@@ -718,8 +717,9 @@ Error PDBLinker::commitSymbolsForObject(void *ctx, void *obj,
       static_cast<ObjFile *>(obj), writer);
 }
 
-static pdb::SectionContrib createSectionContrib(const Chunk *c, uint32_t modi) {
-  OutputSection *os = c ? c->getOutputSection() : nullptr;
+static pdb::SectionContrib createSectionContrib(COFFLinkerContext &ctx,
+                                                const Chunk *c, uint32_t modi) {
+  OutputSection *os = c ? ctx.getOutputSection(c) : nullptr;
   pdb::SectionContrib sc;
   memset(&sc, 0, sizeof(sc));
   sc.ISect = os ? os->sectionIndex : llvm::pdb::kInvalidStreamIndex;
@@ -762,6 +762,7 @@ void DebugSHandler::handleDebugS(SectionChunk *debugChunk) {
   contents = SectionChunk::consumeDebugMagic(contents, ".debug$S");
   DebugSubsectionArray subsections;
   BinaryStreamReader reader(contents, support::little);
+  ExitOnError exitOnErr;
   exitOnErr(reader.readArray(subsections, contents.size()));
   debugChunk->sortRelocations();
 
@@ -816,6 +817,10 @@ void DebugSHandler::handleDebugS(SectionChunk *debugChunk) {
       // Unclear what this is for.
       break;
 
+    case DebugSubsectionKind::XfgHashType:
+    case DebugSubsectionKind::XfgHashVirtual:
+      break;
+
     default:
       warn("ignoring unknown debug$S subsection kind 0x" +
            utohexstr(uint32_t(ss.kind())) + " in file " + toString(&file));
@@ -867,6 +872,7 @@ Error UnrelocatedDebugSubsection::commit(BinaryStreamWriter &writer) const {
     TpiSource *source = debugChunk->file->debugTypesObj;
     DebugInlineeLinesSubsectionRef inlineeLines;
     BinaryStreamReader storageReader(relocatedBytes, support::little);
+    ExitOnError exitOnErr;
     exitOnErr(inlineeLines.initialize(storageReader));
     for (const InlineeSourceLine &line : inlineeLines) {
       TypeIndex &inlinee = *const_cast<TypeIndex *>(&line.Header->Inlinee);
@@ -935,6 +941,8 @@ void DebugSHandler::finish() {
     return;
   }
 
+  ExitOnError exitOnErr;
+
   // Handle FPO data. Each subsection begins with a single image base
   // relocation, which is then added to the RvaStart of each frame data record
   // when it is added to the PDB. The string table indices for the FPO program
@@ -981,10 +989,10 @@ void DebugSHandler::finish() {
   // size as the original. Otherwise, the file references in the line and
   // inlinee line tables will be incorrect.
   auto newChecksums = std::make_unique<DebugChecksumsSubsection>(linker.pdbStrTab);
-  for (FileChecksumEntry &fc : checksums) {
+  for (const FileChecksumEntry &fc : checksums) {
     SmallString<128> filename =
         exitOnErr(cvStrTab.getString(fc.FileNameOffset));
-    pdbMakeAbsolute(filename);
+    linker.pdbMakeAbsolute(filename);
     exitOnErr(dbiBuilder.addModuleSourceFile(*file.moduleDBI, filename));
     newChecksums->addChecksum(filename, fc.Kind, fc.Checksum);
   }
@@ -995,8 +1003,8 @@ void DebugSHandler::finish() {
   file.moduleDBI->addDebugSubsection(std::move(newChecksums));
 }
 
-static void warnUnusable(InputFile *f, Error e) {
-  if (!config->warnDebugInfoUnusable) {
+static void warnUnusable(InputFile *f, Error e, bool shouldWarn) {
+  if (!shouldWarn) {
     consumeError(std::move(e));
     return;
   }
@@ -1009,11 +1017,11 @@ static void warnUnusable(InputFile *f, Error e) {
 
 // Allocate memory for a .debug$S / .debug$F section and relocate it.
 static ArrayRef<uint8_t> relocateDebugChunk(SectionChunk &debugChunk) {
-  uint8_t *buffer = bAlloc.Allocate<uint8_t>(debugChunk.getSize());
+  uint8_t *buffer = bAlloc().Allocate<uint8_t>(debugChunk.getSize());
   assert(debugChunk.getOutputSectionIdx() == 0 &&
          "debug sections should not be in output sections");
   debugChunk.writeTo(buffer);
-  return makeArrayRef(buffer, debugChunk.getSize());
+  return ArrayRef(buffer, debugChunk.getSize());
 }
 
 void PDBLinker::addDebugSymbols(TpiSource *source) {
@@ -1022,7 +1030,8 @@ void PDBLinker::addDebugSymbols(TpiSource *source) {
   if (!source->file)
     return;
 
-  ScopedTimer t(symbolMergingTimer);
+  ScopedTimer t(ctx.symbolMergingTimer);
+  ExitOnError exitOnErr;
   pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
   DebugSHandler dsh(*this, *source->file, source);
   // Now do all live .debug$S and .debug$F sections.
@@ -1064,6 +1073,7 @@ void PDBLinker::addDebugSymbols(TpiSource *source) {
 void PDBLinker::createModuleDBI(ObjFile *file) {
   pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
   SmallString<128> objName;
+  ExitOnError exitOnErr;
 
   bool inArchive = !file->parentName.empty();
   objName = inArchive ? file->parentName : file->getName();
@@ -1081,7 +1091,7 @@ void PDBLinker::createModuleDBI(ObjFile *file) {
     auto *secChunk = dyn_cast<SectionChunk>(c);
     if (!secChunk || !secChunk->live)
       continue;
-    pdb::SectionContrib sc = createSectionContrib(secChunk, modi);
+    pdb::SectionContrib sc = createSectionContrib(ctx, secChunk, modi);
     file->moduleDBI->setFirstSectionContrib(sc);
     break;
   }
@@ -1093,11 +1103,12 @@ void PDBLinker::addDebug(TpiSource *source) {
   // the PDB first, so that we can get the map from object file type and item
   // indices to PDB type and item indices.  If we are using ghashes, types have
   // already been merged.
-  if (!config->debugGHashes) {
-    ScopedTimer t(typeMergingTimer);
+  if (!ctx.config.debugGHashes) {
+    ScopedTimer t(ctx.typeMergingTimer);
     if (Error e = source->mergeDebugT(&tMerger)) {
       // If type merging failed, ignore the symbols.
-      warnUnusable(source->file, std::move(e));
+      warnUnusable(source->file, std::move(e),
+                   ctx.config.warnDebugInfoUnusable);
       return;
     }
   }
@@ -1105,14 +1116,15 @@ void PDBLinker::addDebug(TpiSource *source) {
   // If type merging failed, ignore the symbols.
   Error typeError = std::move(source->typeMergingError);
   if (typeError) {
-    warnUnusable(source->file, std::move(typeError));
+    warnUnusable(source->file, std::move(typeError),
+                 ctx.config.warnDebugInfoUnusable);
     return;
   }
 
   addDebugSymbols(source);
 }
 
-static pdb::BulkPublic createPublic(Defined *def) {
+static pdb::BulkPublic createPublic(COFFLinkerContext &ctx, Defined *def) {
   pdb::BulkPublic pub;
   pub.Name = def->getName().data();
   pub.NameLen = def->getName().size();
@@ -1126,7 +1138,7 @@ static pdb::BulkPublic createPublic(Defined *def) {
   }
   pub.setFlags(flags);
 
-  OutputSection *os = def->getChunk()->getOutputSection();
+  OutputSection *os = ctx.getOutputSection(def->getChunk());
   assert(os && "all publics should be in final image");
   pub.Offset = def->getRVA() - os->getRVA();
   pub.Segment = os->sectionIndex;
@@ -1136,52 +1148,54 @@ static pdb::BulkPublic createPublic(Defined *def) {
 // Add all object files to the PDB. Merge .debug$T sections into IpiData and
 // TpiData.
 void PDBLinker::addObjectsToPDB() {
-  ScopedTimer t1(addObjectsTimer);
+  ScopedTimer t1(ctx.addObjectsTimer);
 
   // Create module descriptors
-  for_each(ObjFile::instances, [&](ObjFile *obj) { createModuleDBI(obj); });
+  for (ObjFile *obj : ctx.objFileInstances)
+    createModuleDBI(obj);
 
   // Reorder dependency type sources to come first.
-  TpiSource::sortDependencies();
+  tMerger.sortDependencies();
 
   // Merge type information from input files using global type hashing.
-  if (config->debugGHashes)
+  if (ctx.config.debugGHashes)
     tMerger.mergeTypesWithGHash();
 
   // Merge dependencies and then regular objects.
-  for_each(TpiSource::dependencySources,
-           [&](TpiSource *source) { addDebug(source); });
-  for_each(TpiSource::objectSources,
-           [&](TpiSource *source) { addDebug(source); });
+  for (TpiSource *source : tMerger.dependencySources)
+    addDebug(source);
+  for (TpiSource *source : tMerger.objectSources)
+    addDebug(source);
 
   builder.getStringTableBuilder().setStrings(pdbStrTab);
   t1.stop();
 
   // Construct TPI and IPI stream contents.
-  ScopedTimer t2(tpiStreamLayoutTimer);
+  ScopedTimer t2(ctx.tpiStreamLayoutTimer);
+
   // Collect all the merged types.
-  if (config->debugGHashes) {
-    addGHashTypeInfo(builder);
+  if (ctx.config.debugGHashes) {
+    addGHashTypeInfo(ctx, builder);
   } else {
     addTypeInfo(builder.getTpiBuilder(), tMerger.getTypeTable());
     addTypeInfo(builder.getIpiBuilder(), tMerger.getIDTable());
   }
   t2.stop();
 
-  if (config->showSummary) {
-    for_each(TpiSource::instances, [&](TpiSource *source) {
+  if (ctx.config.showSummary) {
+    for (TpiSource *source : ctx.tpiSourceList) {
       nbTypeRecords += source->nbTypeRecords;
       nbTypeRecordsBytes += source->nbTypeRecordsBytes;
-    });
+    }
   }
 }
 
 void PDBLinker::addPublicsToPDB() {
-  ScopedTimer t3(publicsLayoutTimer);
+  ScopedTimer t3(ctx.publicsLayoutTimer);
   // Compute the public symbols.
   auto &gsiBuilder = builder.getGsiBuilder();
   std::vector<pdb::BulkPublic> publics;
-  symtab->forEachSymbol([&publics](Symbol *s) {
+  ctx.symtab.forEachSymbol([&publics, this](Symbol *s) {
     // Only emit external, defined, live symbols that have a chunk. Static,
     // non-external symbols do not appear in the symbol table.
     auto *def = dyn_cast<Defined>(s);
@@ -1195,14 +1209,14 @@ void PDBLinker::addPublicsToPDB() {
       StringRef name = def->getName();
       if (name.data()[0] == '_' && name.data()[1] == '_') {
         // Drop the '_' prefix for x86.
-        if (config->machine == I386)
+        if (ctx.config.machine == I386)
           name = name.drop_front(1);
         if (name.startswith("__profd_") || name.startswith("__profc_") ||
             name.startswith("__covrec_")) {
           return;
         }
       }
-      publics.push_back(createPublic(def));
+      publics.push_back(createPublic(ctx, def));
     }
   });
 
@@ -1213,7 +1227,7 @@ void PDBLinker::addPublicsToPDB() {
 }
 
 void PDBLinker::printStats() {
-  if (!config->showSummary)
+  if (!ctx.config.showSummary)
     return;
 
   SmallString<256> buffer;
@@ -1226,10 +1240,10 @@ void PDBLinker::printStats() {
     stream << format_decimal(v, 15) << " " << s << '\n';
   };
 
-  print(ObjFile::instances.size(),
+  print(ctx.objFileInstances.size(),
         "Input OBJ files (expanded from all cmd-line inputs)");
-  print(TpiSource::countTypeServerPDBs(), "PDB type server dependencies");
-  print(TpiSource::countPrecompObjs(), "Precomp OBJ dependencies");
+  print(ctx.typeServerSourceMappings.size(), "PDB type server dependencies");
+  print(ctx.precompSourceMappings.size(), "Precomp OBJ dependencies");
   print(nbTypeRecords, "Input type records");
   print(nbTypeRecordsBytes, "Input type records bytes");
   print(builder.getTpiBuilder().getRecordCount(), "Merged TPI records");
@@ -1281,11 +1295,11 @@ void PDBLinker::printStats() {
           << "Run llvm-pdbutil to print details about a particular record:\n";
       stream << formatv("llvm-pdbutil dump -{0}s -{0}-index {1:X} {2}\n",
                         (name == "TPI" ? "type" : "id"),
-                        tsis.back().typeIndex.getIndex(), config->pdbPath);
+                        tsis.back().typeIndex.getIndex(), ctx.config.pdbPath);
     }
   };
 
-  if (!config->debugGHashes) {
+  if (!ctx.config.debugGHashes) {
     // FIXME: Reimplement for ghash.
     printLargeInputTypeRecs("TPI", tMerger.tpiCounts, tMerger.getTypeTable());
     printLargeInputTypeRecs("IPI", tMerger.ipiCounts, tMerger.getIDTable());
@@ -1295,7 +1309,7 @@ void PDBLinker::printStats() {
 }
 
 void PDBLinker::addNatvisFiles() {
-  for (StringRef file : config->natvisFiles) {
+  for (StringRef file : ctx.config.natvisFiles) {
     ErrorOr<std::unique_ptr<MemoryBuffer>> dataOrErr =
         MemoryBuffer::getFile(file);
     if (!dataOrErr) {
@@ -1305,16 +1319,17 @@ void PDBLinker::addNatvisFiles() {
     std::unique_ptr<MemoryBuffer> data = std::move(*dataOrErr);
 
     // Can't use takeBuffer() here since addInjectedSource() takes ownership.
-    if (driver->tar)
-      driver->tar->append(relativeToRoot(data->getBufferIdentifier()),
-                          data->getBuffer());
+    if (ctx.driver.tar)
+      ctx.driver.tar->append(relativeToRoot(data->getBufferIdentifier()),
+                             data->getBuffer());
 
     builder.addInjectedSource(file, std::move(data));
   }
 }
 
 void PDBLinker::addNamedStreams() {
-  for (const auto &streamFile : config->namedStreams) {
+  ExitOnError exitOnErr;
+  for (const auto &streamFile : ctx.config.namedStreams) {
     const StringRef stream = streamFile.getKey(), file = streamFile.getValue();
     ErrorOr<std::unique_ptr<MemoryBuffer>> dataOrErr =
         MemoryBuffer::getFile(file);
@@ -1324,7 +1339,7 @@ void PDBLinker::addNamedStreams() {
     }
     std::unique_ptr<MemoryBuffer> data = std::move(*dataOrErr);
     exitOnErr(builder.addNamedStream(stream, data->getBuffer()));
-    driver->takeBuffer(std::move(data));
+    ctx.driver.takeBuffer(std::move(data));
   }
 }
 
@@ -1354,8 +1369,8 @@ static std::string quote(ArrayRef<StringRef> args) {
   for (StringRef a : args) {
     if (!r.empty())
       r.push_back(' ');
-    bool hasWS = a.find(' ') != StringRef::npos;
-    bool hasQ = a.find('"') != StringRef::npos;
+    bool hasWS = a.contains(' ');
+    bool hasQ = a.contains('"');
     if (hasWS || hasQ)
       r.push_back('"');
     if (hasQ) {
@@ -1371,8 +1386,8 @@ static std::string quote(ArrayRef<StringRef> args) {
   return r;
 }
 
-static void fillLinkerVerRecord(Compile3Sym &cs) {
-  cs.Machine = toCodeViewMachine(config->machine);
+static void fillLinkerVerRecord(Compile3Sym &cs, MachineTypes machine) {
+  cs.Machine = toCodeViewMachine(machine);
   // Interestingly, if we set the string to 0.0.0.0, then when trying to view
   // local variables WinDbg emits an error that private symbols are not present.
   // By setting this to a valid MSVC linker version string, local variables are
@@ -1397,33 +1412,34 @@ static void fillLinkerVerRecord(Compile3Sym &cs) {
   cs.setLanguage(SourceLanguage::Link);
 }
 
-static void addCommonLinkerModuleSymbols(StringRef path,
-                                         pdb::DbiModuleDescriptorBuilder &mod) {
+void PDBLinker::addCommonLinkerModuleSymbols(
+    StringRef path, pdb::DbiModuleDescriptorBuilder &mod) {
   ObjNameSym ons(SymbolRecordKind::ObjNameSym);
   EnvBlockSym ebs(SymbolRecordKind::EnvBlockSym);
   Compile3Sym cs(SymbolRecordKind::Compile3Sym);
-  fillLinkerVerRecord(cs);
+  fillLinkerVerRecord(cs, ctx.config.machine);
 
   ons.Name = "* Linker *";
   ons.Signature = 0;
 
-  ArrayRef<StringRef> args = makeArrayRef(config->argv).drop_front();
+  ArrayRef<StringRef> args = ArrayRef(ctx.config.argv).drop_front();
   std::string argStr = quote(args);
   ebs.Fields.push_back("cwd");
   SmallString<64> cwd;
-  if (config->pdbSourcePath.empty())
+  if (ctx.config.pdbSourcePath.empty())
     sys::fs::current_path(cwd);
   else
-    cwd = config->pdbSourcePath;
+    cwd = ctx.config.pdbSourcePath;
   ebs.Fields.push_back(cwd);
   ebs.Fields.push_back("exe");
-  SmallString<64> exe = config->argv[0];
+  SmallString<64> exe = ctx.config.argv[0];
   pdbMakeAbsolute(exe);
   ebs.Fields.push_back(exe);
   ebs.Fields.push_back("pdb");
   ebs.Fields.push_back(path);
   ebs.Fields.push_back("cmd");
   ebs.Fields.push_back(argStr);
+  llvm::BumpPtrAllocator &bAlloc = lld::bAlloc();
   mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
       ons, bAlloc, CodeViewContainer::Pdb));
   mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
@@ -1455,11 +1471,11 @@ static void addLinkerModuleCoffGroup(PartialSection *sec,
     cgs.Characteristics |= llvm::COFF::IMAGE_SCN_MEM_WRITE;
 
   mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
-      cgs, bAlloc, CodeViewContainer::Pdb));
+      cgs, bAlloc(), CodeViewContainer::Pdb));
 }
 
 static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod,
-                                         OutputSection &os) {
+                                         OutputSection &os, bool isMinGW) {
   SectionSym sym(SymbolRecordKind::SectionSym);
   sym.Alignment = 12; // 2^12 = 4KB
   sym.Characteristics = os.header.Characteristics;
@@ -1468,11 +1484,11 @@ static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod,
   sym.Rva = os.getRVA();
   sym.SectionNumber = os.sectionIndex;
   mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
-      sym, bAlloc, CodeViewContainer::Pdb));
+      sym, bAlloc(), CodeViewContainer::Pdb));
 
   // Skip COFF groups in MinGW because it adds a significant footprint to the
   // PDB, due to each function being in its own section
-  if (config->mingw)
+  if (isMinGW)
     return;
 
   // Output COFF groups for individual chunks of this section.
@@ -1482,13 +1498,14 @@ static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod,
 }
 
 // Add all import files as modules to the PDB.
-void PDBLinker::addImportFilesToPDB(ArrayRef<OutputSection *> outputSections) {
-  if (ImportFile::instances.empty())
+void PDBLinker::addImportFilesToPDB() {
+  if (ctx.importFileInstances.empty())
     return;
 
+  ExitOnError exitOnErr;
   std::map<std::string, llvm::pdb::DbiModuleDescriptorBuilder *> dllToModuleDbi;
 
-  for (ImportFile *file : ImportFile::instances) {
+  for (ImportFile *file : ctx.importFileInstances) {
     if (!file->live)
       continue;
 
@@ -1512,7 +1529,7 @@ void PDBLinker::addImportFilesToPDB(ArrayRef<OutputSection *> outputSections) {
           exitOnErr(dbiBuilder.addModuleInfo(file->dllName));
       firstMod.setObjFileName(libPath);
       pdb::SectionContrib sc =
-          createSectionContrib(nullptr, llvm::pdb::kInvalidStreamIndex);
+          createSectionContrib(ctx, nullptr, llvm::pdb::kInvalidStreamIndex);
       firstMod.setFirstSectionContrib(sc);
 
       // The second module is where the import stream goes.
@@ -1522,7 +1539,7 @@ void PDBLinker::addImportFilesToPDB(ArrayRef<OutputSection *> outputSections) {
 
     DefinedImportThunk *thunk = cast<DefinedImportThunk>(file->thunkSym);
     Chunk *thunkChunk = thunk->getChunk();
-    OutputSection *thunkOS = thunkChunk->getOutputSection();
+    OutputSection *thunkOS = ctx.getOutputSection(thunkChunk);
 
     ObjNameSym ons(SymbolRecordKind::ObjNameSym);
     Compile3Sym cs(SymbolRecordKind::Compile3Sym);
@@ -1532,7 +1549,7 @@ void PDBLinker::addImportFilesToPDB(ArrayRef<OutputSection *> outputSections) {
     ons.Name = file->dllName;
     ons.Signature = 0;
 
-    fillLinkerVerRecord(cs);
+    fillLinkerVerRecord(cs, ctx.config.machine);
 
     ts.Name = thunk->getName();
     ts.Parent = 0;
@@ -1543,6 +1560,7 @@ void PDBLinker::addImportFilesToPDB(ArrayRef<OutputSection *> outputSections) {
     ts.Segment = thunkOS->sectionIndex;
     ts.Offset = thunkChunk->getRVA() - thunkOS->getRVA();
 
+    llvm::BumpPtrAllocator &bAlloc = lld::bAlloc();
     mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol(
         ons, bAlloc, CodeViewContainer::Pdb));
     mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol(
@@ -1564,28 +1582,27 @@ void PDBLinker::addImportFilesToPDB(ArrayRef<OutputSection *> outputSections) {
     mod->addSymbol(newSym);
 
     pdb::SectionContrib sc =
-        createSectionContrib(thunk->getChunk(), mod->getModuleIndex());
+        createSectionContrib(ctx, thunk->getChunk(), mod->getModuleIndex());
     mod->setFirstSectionContrib(sc);
   }
 }
 
 // Creates a PDB file.
-void lld::coff::createPDB(SymbolTable *symtab,
-                          ArrayRef<OutputSection *> outputSections,
+void lld::coff::createPDB(COFFLinkerContext &ctx,
                           ArrayRef<uint8_t> sectionTable,
                           llvm::codeview::DebugInfo *buildId) {
-  ScopedTimer t1(totalPdbLinkTimer);
-  PDBLinker pdb(symtab);
+  ScopedTimer t1(ctx.totalPdbLinkTimer);
+  PDBLinker pdb(ctx);
 
   pdb.initialize(buildId);
   pdb.addObjectsToPDB();
-  pdb.addImportFilesToPDB(outputSections);
-  pdb.addSections(outputSections, sectionTable);
+  pdb.addImportFilesToPDB();
+  pdb.addSections(sectionTable);
   pdb.addNatvisFiles();
   pdb.addNamedStreams();
   pdb.addPublicsToPDB();
 
-  ScopedTimer t2(diskCommitTimer);
+  ScopedTimer t2(ctx.diskCommitTimer);
   codeview::GUID guid;
   pdb.commit(&guid);
   memcpy(&buildId->PDB70.Signature, &guid, 16);
@@ -1596,7 +1613,8 @@ void lld::coff::createPDB(SymbolTable *symtab,
 }
 
 void PDBLinker::initialize(llvm::codeview::DebugInfo *buildId) {
-  exitOnErr(builder.initialize(4096)); // 4096 is blocksize
+  ExitOnError exitOnErr;
+  exitOnErr(builder.initialize(ctx.config.pdbPageSize));
 
   buildId->Signature.CVSignature = OMF::Signature::PDB70;
   // Signature is set to a hash of the PDB contents when the PDB is done.
@@ -1617,7 +1635,7 @@ void PDBLinker::initialize(llvm::codeview::DebugInfo *buildId) {
   pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
   dbiBuilder.setAge(buildId->PDB70.Age);
   dbiBuilder.setVersionHeader(pdb::PdbDbiV70);
-  dbiBuilder.setMachineType(config->machine);
+  dbiBuilder.setMachineType(ctx.config.machine);
   // Technically we are not link.exe 14.11, but there are known cases where
   // debugging tools on Windows expect Microsoft-specific version numbers or
   // they fail to work at all.  Since we know we produce PDBs that are
@@ -1625,11 +1643,11 @@ void PDBLinker::initialize(llvm::codeview::DebugInfo *buildId) {
   dbiBuilder.setBuildNumber(14, 11);
 }
 
-void PDBLinker::addSections(ArrayRef<OutputSection *> outputSections,
-                            ArrayRef<uint8_t> sectionTable) {
+void PDBLinker::addSections(ArrayRef<uint8_t> sectionTable) {
+  ExitOnError exitOnErr;
   // It's not entirely clear what this is, but the * Linker * module uses it.
   pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
-  nativePath = config->pdbPath;
+  nativePath = ctx.config.pdbPath;
   pdbMakeAbsolute(nativePath);
   uint32_t pdbFilePathNI = dbiBuilder.addECName(nativePath);
   auto &linkerModule = exitOnErr(dbiBuilder.addModuleInfo("* Linker *"));
@@ -1637,11 +1655,11 @@ void PDBLinker::addSections(ArrayRef<OutputSection *> outputSections,
   addCommonLinkerModuleSymbols(nativePath, linkerModule);
 
   // Add section contributions. They must be ordered by ascending RVA.
-  for (OutputSection *os : outputSections) {
-    addLinkerModuleSectionSymbol(linkerModule, *os);
+  for (OutputSection *os : ctx.outputSections) {
+    addLinkerModuleSectionSymbol(linkerModule, *os, ctx.config.mingw);
     for (Chunk *c : os->chunks) {
       pdb::SectionContrib sc =
-          createSectionContrib(c, linkerModule.getModuleIndex());
+          createSectionContrib(ctx, c, linkerModule.getModuleIndex());
       builder.getDbiBuilder().addSectionContrib(sc);
     }
   }
@@ -1650,7 +1668,7 @@ void PDBLinker::addSections(ArrayRef<OutputSection *> outputSections,
   // to provide trampolines thunks for incremental function patching. Set this
   // as "unused" because LLD doesn't support /INCREMENTAL link.
   pdb::SectionContrib sc =
-      createSectionContrib(nullptr, llvm::pdb::kInvalidStreamIndex);
+      createSectionContrib(ctx, nullptr, llvm::pdb::kInvalidStreamIndex);
   linkerModule.setFirstSectionContrib(sc);
 
   // Add Section Map stream.
@@ -1668,14 +1686,14 @@ void PDBLinker::commit(codeview::GUID *guid) {
   // Print an error and continue if PDB writing fails. This is done mainly so
   // the user can see the output of /time and /summary, which is very helpful
   // when trying to figure out why a PDB file is too large.
-  if (Error e = builder.commit(config->pdbPath, guid)) {
+  if (Error e = builder.commit(ctx.config.pdbPath, guid)) {
     checkError(std::move(e));
-    error("failed to write PDB file " + Twine(config->pdbPath));
+    error("failed to write PDB file " + Twine(ctx.config.pdbPath));
   }
 }
 
-static uint32_t getSecrelReloc() {
-  switch (config->machine) {
+static uint32_t getSecrelReloc(llvm::COFF::MachineTypes machine) {
+  switch (machine) {
   case AMD64:
     return COFF::IMAGE_REL_AMD64_SECREL;
   case I386:
@@ -1700,7 +1718,7 @@ static bool findLineTable(const SectionChunk *c, uint32_t addr,
                           DebugLinesSubsectionRef &lines,
                           uint32_t &offsetInLinetable) {
   ExitOnError exitOnErr;
-  uint32_t secrelReloc = getSecrelReloc();
+  const uint32_t secrelReloc = getSecrelReloc(c->file->ctx.config.machine);
 
   for (SectionChunk *dbgC : c->file->getDebugChunks()) {
     if (dbgC->getSectionName() != ".debug$S")
@@ -1774,9 +1792,9 @@ static bool findLineTable(const SectionChunk *c, uint32_t addr,
 }
 
 // Use CodeView line tables to resolve a file and line number for the given
-// offset into the given chunk and return them, or None if a line table was
-// not found.
-Optional<std::pair<StringRef, uint32_t>>
+// offset into the given chunk and return them, or std::nullopt if a line table
+// was not found.
+std::optional<std::pair<StringRef, uint32_t>>
 lld::coff::getFileLineCodeView(const SectionChunk *c, uint32_t addr) {
   ExitOnError exitOnErr;
 
@@ -1786,11 +1804,11 @@ lld::coff::getFileLineCodeView(const SectionChunk *c, uint32_t addr) {
   uint32_t offsetInLinetable;
 
   if (!findLineTable(c, addr, cvStrTab, checksums, lines, offsetInLinetable))
-    return None;
+    return std::nullopt;
 
-  Optional<uint32_t> nameIndex;
-  Optional<uint32_t> lineNumber;
-  for (LineColumnEntry &entry : lines) {
+  std::optional<uint32_t> nameIndex;
+  std::optional<uint32_t> lineNumber;
+  for (const LineColumnEntry &entry : lines) {
     for (const LineNumberEntry &ln : entry.LineNumbers) {
       LineInfo li(ln.Flags);
       if (ln.Offset > offsetInLinetable) {
@@ -1807,7 +1825,7 @@ lld::coff::getFileLineCodeView(const SectionChunk *c, uint32_t addr) {
     }
   }
   if (!nameIndex)
-    return None;
+    return std::nullopt;
   StringRef filename = exitOnErr(getFileName(cvStrTab, checksums, *nameIndex));
   return std::make_pair(filename, *lineNumber);
 }
index 53506d4..991805c 100644 (file)
 #define LLD_COFF_PDB_H
 
 #include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/StringRef.h"
+#include <optional>
 
-namespace llvm {
-namespace codeview {
+namespace llvm::codeview {
 union DebugInfo;
 }
-}
 
 namespace lld {
 class Timer;
 
 namespace coff {
-class OutputSection;
 class SectionChunk;
-class SymbolTable;
+class COFFLinkerContext;
 
-void createPDB(SymbolTable *symtab,
-               llvm::ArrayRef<OutputSection *> outputSections,
-               llvm::ArrayRef<uint8_t> sectionTable,
+void createPDB(COFFLinkerContext &ctx, llvm::ArrayRef<uint8_t> sectionTable,
                llvm::codeview::DebugInfo *buildId);
 
-llvm::Optional<std::pair<llvm::StringRef, uint32_t>>
+std::optional<std::pair<llvm::StringRef, uint32_t>>
 getFileLineCodeView(const SectionChunk *c, uint32_t addr);
 
-extern Timer loadGHashTimer;
-extern Timer mergeGHashTimer;
-
 } // namespace coff
 } // namespace lld
 
index 536f343..2ca7b82 100644 (file)
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "SymbolTable.h"
+#include "COFFLinkerContext.h"
 #include "Config.h"
 #include "Driver.h"
 #include "LTO.h"
@@ -15,7 +16,7 @@
 #include "lld/Common/ErrorHandler.h"
 #include "lld/Common/Memory.h"
 #include "lld/Common/Timer.h"
-#include "llvm/DebugInfo/Symbolize/Symbolize.h"
+#include "llvm/DebugInfo/DIContext.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/LTO/LTO.h"
 #include "llvm/Object/WindowsMachineFlag.h"
@@ -25,8 +26,7 @@
 
 using namespace llvm;
 
-namespace lld {
-namespace coff {
+namespace lld::coff {
 
 StringRef ltrim1(StringRef s, const char *chars) {
   if (!s.empty() && strchr(chars, s[0]))
@@ -34,36 +34,39 @@ StringRef ltrim1(StringRef s, const char *chars) {
   return s;
 }
 
-static Timer ltoTimer("LTO", Timer::root());
-
-SymbolTable *symtab;
-
 void SymbolTable::addFile(InputFile *file) {
   log("Reading " + toString(file));
-  file->parse();
+  if (file->lazy) {
+    if (auto *f = dyn_cast<BitcodeFile>(file))
+      f->parseLazy();
+    else
+      cast<ObjFile>(file)->parseLazy();
+  } else {
+    file->parse();
+    if (auto *f = dyn_cast<ObjFile>(file)) {
+      ctx.objFileInstances.push_back(f);
+    } else if (auto *f = dyn_cast<BitcodeFile>(file)) {
+      ctx.bitcodeFileInstances.push_back(f);
+    } else if (auto *f = dyn_cast<ImportFile>(file)) {
+      ctx.importFileInstances.push_back(f);
+    }
+  }
 
   MachineTypes mt = file->getMachineType();
-  if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) {
-    config->machine = mt;
-  } else if (mt != IMAGE_FILE_MACHINE_UNKNOWN && config->machine != mt) {
+  if (ctx.config.machine == IMAGE_FILE_MACHINE_UNKNOWN) {
+    ctx.config.machine = mt;
+    ctx.driver.addWinSysRootLibSearchPaths();
+  } else if (mt != IMAGE_FILE_MACHINE_UNKNOWN && ctx.config.machine != mt) {
     error(toString(file) + ": machine type " + machineToStr(mt) +
-          " conflicts with " + machineToStr(config->machine));
+          " conflicts with " + machineToStr(ctx.config.machine));
     return;
   }
 
-  if (auto *f = dyn_cast<ObjFile>(file)) {
-    ObjFile::instances.push_back(f);
-  } else if (auto *f = dyn_cast<BitcodeFile>(file)) {
-    BitcodeFile::instances.push_back(f);
-  } else if (auto *f = dyn_cast<ImportFile>(file)) {
-    ImportFile::instances.push_back(f);
-  }
-
-  driver->parseDirectives(file);
+  ctx.driver.parseDirectives(file);
 }
 
-static void errorOrWarn(const Twine &s) {
-  if (config->forceUnresolved)
+static void errorOrWarn(const Twine &s, bool forceUnresolved) {
+  if (forceUnresolved)
     warn(s);
   else
     error(s);
@@ -78,9 +81,11 @@ static void forceLazy(Symbol *s) {
     l->file->addMember(l->sym);
     break;
   }
-  case Symbol::Kind::LazyObjectKind:
-    cast<LazyObject>(s)->file->fetch();
+  case Symbol::Kind::LazyObjectKind: {
+    InputFile *file = cast<LazyObject>(s)->file;
+    file->ctx.symtab.addFile(file);
     break;
+  }
   case Symbol::Kind::LazyDLLSymbolKind: {
     auto *l = cast<LazyDLLSymbol>(s);
     l->file->makeImport(l->sym);
@@ -120,25 +125,25 @@ static std::vector<std::string> getSymbolLocations(BitcodeFile *file) {
   return {res};
 }
 
-static Optional<std::pair<StringRef, uint32_t>>
+static std::optional<std::pair<StringRef, uint32_t>>
 getFileLineDwarf(const SectionChunk *c, uint32_t addr) {
-  Optional<DILineInfo> optionalLineInfo =
+  std::optional<DILineInfo> optionalLineInfo =
       c->file->getDILineInfo(addr, c->getSectionNumber() - 1);
   if (!optionalLineInfo)
-    return None;
+    return std::nullopt;
   const DILineInfo &lineInfo = *optionalLineInfo;
   if (lineInfo.FileName == DILineInfo::BadString)
-    return None;
-  return std::make_pair(saver.save(lineInfo.FileName), lineInfo.Line);
+    return std::nullopt;
+  return std::make_pair(saver().save(lineInfo.FileName), lineInfo.Line);
 }
 
-static Optional<std::pair<StringRef, uint32_t>>
+static std::optional<std::pair<StringRef, uint32_t>>
 getFileLine(const SectionChunk *c, uint32_t addr) {
   // MinGW can optionally use codeview, even if the default is dwarf.
-  Optional<std::pair<StringRef, uint32_t>> fileLine =
+  std::optional<std::pair<StringRef, uint32_t>> fileLine =
       getFileLineCodeView(c, addr);
   // If codeview didn't yield any result, check dwarf in MinGW mode.
-  if (!fileLine && config->mingw)
+  if (!fileLine && c->file->ctx.config.mingw)
     fileLine = getFileLineDwarf(c, addr);
   return fileLine;
 }
@@ -169,7 +174,7 @@ getSymbolLocations(ObjFile *file, uint32_t symIndex, size_t maxStrings) {
       if (locations.size() >= maxStrings)
         continue;
 
-      Optional<std::pair<StringRef, uint32_t>> fileLine =
+      std::optional<std::pair<StringRef, uint32_t>> fileLine =
           getFileLine(sc, r.VirtualAddress);
       Symbol *sym = getSymbol(sc, r.VirtualAddress);
       if (fileLine)
@@ -196,7 +201,7 @@ getSymbolLocations(ObjFile *file, uint32_t symIndex, size_t maxStrings) {
          << "\n>>>               ";
     os << toString(file);
     if (loc.sym)
-      os << ":(" << toString(*loc.sym) << ')';
+      os << ":(" << toString(file->ctx, *loc.sym) << ')';
   }
   return std::make_pair(symbolLocations, numLocations);
 }
@@ -231,17 +236,16 @@ struct UndefinedDiag {
   std::vector<File> files;
 };
 
-static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) {
+static void reportUndefinedSymbol(const COFFLinkerContext &ctx,
+                                  const UndefinedDiag &undefDiag) {
   std::string out;
   llvm::raw_string_ostream os(out);
-  os << "undefined symbol: " << toString(*undefDiag.sym);
+  os << "undefined symbol: " << toString(ctx, *undefDiag.sym);
 
   const size_t maxUndefReferences = 3;
   size_t numDisplayedRefs = 0, numRefs = 0;
   for (const UndefinedDiag::File &ref : undefDiag.files) {
-    std::vector<std::string> symbolLocations;
-    size_t totalLocations = 0;
-    std::tie(symbolLocations, totalLocations) = getSymbolLocations(
+    auto [symbolLocations, totalLocations] = getSymbolLocations(
         ref.file, ref.symIndex, maxUndefReferences - numDisplayedRefs);
 
     numRefs += totalLocations;
@@ -252,7 +256,7 @@ static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) {
   }
   if (numDisplayedRefs < numRefs)
     os << "\n>>> referenced " << numRefs - numDisplayedRefs << " more times";
-  errorOrWarn(os.str());
+  errorOrWarn(os.str(), ctx.config.forceUnresolved);
 }
 
 void SymbolTable::loadMinGWSymbols() {
@@ -266,9 +270,9 @@ void SymbolTable::loadMinGWSymbols() {
 
     StringRef name = undef->getName();
 
-    if (config->machine == I386 && config->stdcallFixup) {
+    if (ctx.config.machine == I386 && ctx.config.stdcallFixup) {
       // Check if we can resolve an undefined decorated symbol by finding
-      // the indended target as an undecorated symbol (only with a leading
+      // the intended target as an undecorated symbol (only with a leading
       // underscore).
       StringRef origName = name;
       StringRef baseName = name;
@@ -287,7 +291,7 @@ void SymbolTable::loadMinGWSymbols() {
         }
         // If it's lazy or already defined, hook it up as weak alias.
         if (l->isLazy() || isa<Defined>(l)) {
-          if (config->warnStdcallFixup)
+          if (ctx.config.warnStdcallFixup)
             warn("Resolving " + origName + " by linking to " + newName);
           else
             log("Resolving " + origName + " by linking to " + newName);
@@ -297,7 +301,7 @@ void SymbolTable::loadMinGWSymbols() {
       }
     }
 
-    if (config->autoImport) {
+    if (ctx.config.autoImport) {
       if (name.startswith("__imp_"))
         continue;
       // If we have an undefined symbol, but we have a lazy symbol we could
@@ -355,7 +359,7 @@ bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) {
   // 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) {
+  if (refptr && refptr->getChunk()->getSize() == ctx.config.wordsize) {
     SectionChunk *sc = dyn_cast_or_null<SectionChunk>(refptr->getChunk());
     if (sc && sc->getRelocs().size() == 1 && *sc->symbols().begin() == sym) {
       log("Replacing .refptr." + name + " with " + imp->getName());
@@ -372,23 +376,21 @@ bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) {
 /// defined symbol imported" diagnostic for symbols in localImports.
 /// objFiles and bitcodeFiles (if not nullptr) are used to report where
 /// undefined symbols are referenced.
-static void
-reportProblemSymbols(const SmallPtrSetImpl<Symbol *> &undefs,
-                     const DenseMap<Symbol *, Symbol *> *localImports,
-                     const std::vector<ObjFile *> objFiles,
-                     const std::vector<BitcodeFile *> *bitcodeFiles) {
-
+static void reportProblemSymbols(
+    const COFFLinkerContext &ctx, const SmallPtrSetImpl<Symbol *> &undefs,
+    const DenseMap<Symbol *, Symbol *> *localImports, bool needBitcodeFiles) {
   // Return early if there is nothing to report (which should be
   // the common case).
   if (undefs.empty() && (!localImports || localImports->empty()))
     return;
 
-  for (Symbol *b : config->gcroot) {
+  for (Symbol *b : ctx.config.gcroot) {
     if (undefs.count(b))
-      errorOrWarn("<root>: undefined symbol: " + toString(*b));
+      errorOrWarn("<root>: undefined symbol: " + toString(ctx, *b),
+                  ctx.config.forceUnresolved);
     if (localImports)
       if (Symbol *imp = localImports->lookup(b))
-        warn("<root>: locally defined symbol imported: " + toString(*imp) +
+        warn("<root>: locally defined symbol imported: " + toString(ctx, *imp) +
              " (defined in " + toString(imp->getFile()) + ") [LNK4217]");
   }
 
@@ -413,20 +415,20 @@ reportProblemSymbols(const SmallPtrSetImpl<Symbol *> &undefs,
       if (localImports)
         if (Symbol *imp = localImports->lookup(sym))
           warn(toString(file) +
-               ": locally defined symbol imported: " + toString(*imp) +
+               ": locally defined symbol imported: " + toString(ctx, *imp) +
                " (defined in " + toString(imp->getFile()) + ") [LNK4217]");
     }
   };
 
-  for (ObjFile *file : objFiles)
+  for (ObjFile *file : ctx.objFileInstances)
     processFile(file, file->getSymbols());
 
-  if (bitcodeFiles)
-    for (BitcodeFile *file : *bitcodeFiles)
+  if (needBitcodeFiles)
+    for (BitcodeFile *file : ctx.bitcodeFileInstances)
       processFile(file, file->getSymbols());
 
   for (const UndefinedDiag &undefDiag : undefDiags)
-    reportUndefinedSymbol(undefDiag);
+    reportUndefinedSymbol(ctx, undefDiag);
 }
 
 void SymbolTable::reportUnresolvable() {
@@ -446,14 +448,13 @@ void SymbolTable::reportUnresolvable() {
     }
     if (name.contains("_PchSym_"))
       continue;
-    if (config->autoImport && impSymbol(name))
+    if (ctx.config.autoImport && impSymbol(name))
       continue;
     undefs.insert(sym);
   }
 
-  reportProblemSymbols(undefs,
-                       /* localImports */ nullptr, ObjFile::instances,
-                       &BitcodeFile::instances);
+  reportProblemSymbols(ctx, undefs,
+                       /* localImports */ nullptr, true);
 }
 
 void SymbolTable::resolveRemainingUndefines() {
@@ -492,7 +493,7 @@ void SymbolTable::resolveRemainingUndefines() {
       Symbol *imp = find(name.substr(strlen("__imp_")));
       if (imp && isa<Defined>(imp)) {
         auto *d = cast<Defined>(imp);
-        replaceSymbol<DefinedLocalImport>(sym, name, d);
+        replaceSymbol<DefinedLocalImport>(sym, ctx, name, d);
         localImportChunks.push_back(cast<DefinedLocalImport>(sym)->getChunk());
         localImports[sym] = d;
         continue;
@@ -504,19 +505,19 @@ void SymbolTable::resolveRemainingUndefines() {
     if (name.contains("_PchSym_"))
       continue;
 
-    if (config->autoImport && handleMinGWAutomaticImport(sym, name))
+    if (ctx.config.autoImport && handleMinGWAutomaticImport(sym, name))
       continue;
 
     // Remaining undefined symbols are not fatal if /force is specified.
     // They are replaced with dummy defined symbols.
-    if (config->forceUnresolved)
-      replaceSymbol<DefinedAbsolute>(sym, name, 0);
+    if (ctx.config.forceUnresolved)
+      replaceSymbol<DefinedAbsolute>(sym, ctx, name, 0);
     undefs.insert(sym);
   }
 
   reportProblemSymbols(
-      undefs, config->warnLocallyDefinedImported ? &localImports : nullptr,
-      ObjFile::instances, /* bitcode files no longer needed */ nullptr);
+      ctx, undefs,
+      ctx.config.warnLocallyDefinedImported ? &localImports : nullptr, false);
 }
 
 std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) {
@@ -541,9 +542,7 @@ std::pair<Symbol *, bool> SymbolTable::insert(StringRef name, InputFile *file) {
 
 Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
                                   bool isWeakAlias) {
-  Symbol *s;
-  bool wasInserted;
-  std::tie(s, wasInserted) = insert(name, f);
+  auto [s, wasInserted] = insert(name, f);
   if (wasInserted || (s->isLazy() && isWeakAlias)) {
     replaceSymbol<Undefined>(s, name);
     return s;
@@ -555,9 +554,7 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
 
 void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) {
   StringRef name = sym.getName();
-  Symbol *s;
-  bool wasInserted;
-  std::tie(s, wasInserted) = insert(name);
+  auto [s, wasInserted] = insert(name);
   if (wasInserted) {
     replaceSymbol<LazyArchive>(s, f, sym);
     return;
@@ -569,10 +566,9 @@ void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) {
   f->addMember(sym);
 }
 
-void SymbolTable::addLazyObject(LazyObjFile *f, StringRef n) {
-  Symbol *s;
-  bool wasInserted;
-  std::tie(s, wasInserted) = insert(n, f);
+void SymbolTable::addLazyObject(InputFile *f, StringRef n) {
+  assert(f->lazy);
+  auto [s, wasInserted] = insert(n, f);
   if (wasInserted) {
     replaceSymbol<LazyObject>(s, f, n);
     return;
@@ -581,14 +577,13 @@ void SymbolTable::addLazyObject(LazyObjFile *f, StringRef n) {
   if (!u || u->weakAlias || s->pendingArchiveLoad)
     return;
   s->pendingArchiveLoad = true;
-  f->fetch();
+  f->lazy = false;
+  addFile(f);
 }
 
 void SymbolTable::addLazyDLLSymbol(DLLFile *f, DLLFile::Symbol *sym,
                                    StringRef n) {
-  Symbol *s;
-  bool wasInserted;
-  std::tie(s, wasInserted) = insert(n);
+  auto [s, wasInserted] = insert(n);
   if (wasInserted) {
     replaceSymbol<LazyDLLSymbol>(s, f, sym, n);
     return;
@@ -611,7 +606,7 @@ static std::string getSourceLocationBitcode(BitcodeFile *file) {
 
 static std::string getSourceLocationObj(ObjFile *file, SectionChunk *sc,
                                         uint32_t offset, StringRef name) {
-  Optional<std::pair<StringRef, uint32_t>> fileLine;
+  std::optional<std::pair<StringRef, uint32_t>> fileLine;
   if (sc)
     fileLine = getFileLine(sc, offset);
   if (!fileLine)
@@ -649,7 +644,7 @@ void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile,
                                   uint32_t newSectionOffset) {
   std::string msg;
   llvm::raw_string_ostream os(msg);
-  os << "duplicate symbol: " << toString(*existing);
+  os << "duplicate symbol: " << toString(ctx, *existing);
 
   DefinedRegular *d = dyn_cast<DefinedRegular>(existing);
   if (d && isa<ObjFile>(d->getFile())) {
@@ -661,19 +656,17 @@ void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile,
   os << getSourceLocation(newFile, newSc, newSectionOffset,
                           existing->getName());
 
-  if (config->forceMultiple)
+  if (ctx.config.forceMultiple)
     warn(os.str());
   else
     error(os.str());
 }
 
 Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) {
-  Symbol *s;
-  bool wasInserted;
-  std::tie(s, wasInserted) = insert(n, nullptr);
+  auto [s, wasInserted] = insert(n, nullptr);
   s->isUsedInRegularObj = true;
   if (wasInserted || isa<Undefined>(s) || s->isLazy())
-    replaceSymbol<DefinedAbsolute>(s, n, sym);
+    replaceSymbol<DefinedAbsolute>(s, ctx, n, sym);
   else if (auto *da = dyn_cast<DefinedAbsolute>(s)) {
     if (da->getVA() != sym.getValue())
       reportDuplicate(s, nullptr);
@@ -683,12 +676,10 @@ 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, nullptr);
+  auto [s, wasInserted] = insert(n, nullptr);
   s->isUsedInRegularObj = true;
   if (wasInserted || isa<Undefined>(s) || s->isLazy())
-    replaceSymbol<DefinedAbsolute>(s, n, va);
+    replaceSymbol<DefinedAbsolute>(s, ctx, n, va);
   else if (auto *da = dyn_cast<DefinedAbsolute>(s)) {
     if (da->getVA() != va)
       reportDuplicate(s, nullptr);
@@ -698,9 +689,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, nullptr);
+  auto [s, wasInserted] = insert(n, nullptr);
   s->isUsedInRegularObj = true;
   if (wasInserted || isa<Undefined>(s) || s->isLazy())
     replaceSymbol<DefinedSynthetic>(s, n, c);
@@ -711,14 +700,12 @@ Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) {
 
 Symbol *SymbolTable::addRegular(InputFile *f, StringRef n,
                                 const coff_symbol_generic *sym, SectionChunk *c,
-                                uint32_t sectionOffset) {
-  Symbol *s;
-  bool wasInserted;
-  std::tie(s, wasInserted) = insert(n, f);
-  if (wasInserted || !isa<DefinedRegular>(s))
+                                uint32_t sectionOffset, bool isWeak) {
+  auto [s, wasInserted] = insert(n, f);
+  if (wasInserted || !isa<DefinedRegular>(s) || s->isWeak)
     replaceSymbol<DefinedRegular>(s, f, n, /*IsCOMDAT*/ false,
-                                  /*IsExternal*/ true, sym, c);
-  else
+                                  /*IsExternal*/ true, sym, c, isWeak);
+  else if (!isWeak)
     reportDuplicate(s, f, c, sectionOffset);
   return s;
 }
@@ -726,9 +713,7 @@ Symbol *SymbolTable::addRegular(InputFile *f, StringRef n,
 std::pair<DefinedRegular *, bool>
 SymbolTable::addComdat(InputFile *f, StringRef n,
                        const coff_symbol_generic *sym) {
-  Symbol *s;
-  bool wasInserted;
-  std::tie(s, wasInserted) = insert(n, f);
+  auto [s, wasInserted] = insert(n, f);
   if (wasInserted || !isa<DefinedRegular>(s)) {
     replaceSymbol<DefinedRegular>(s, f, n, /*IsCOMDAT*/ true,
                                   /*IsExternal*/ true, sym, nullptr);
@@ -742,9 +727,7 @@ SymbolTable::addComdat(InputFile *f, StringRef n,
 
 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, f);
+  auto [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))
@@ -754,9 +737,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, nullptr);
+  auto [s, wasInserted] = insert(n, nullptr);
   s->isUsedInRegularObj = true;
   if (wasInserted || isa<Undefined>(s) || s->isLazy()) {
     replaceSymbol<DefinedImportData>(s, n, f);
@@ -769,12 +750,10 @@ Symbol *SymbolTable::addImportData(StringRef n, ImportFile *f) {
 
 Symbol *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id,
                                     uint16_t machine) {
-  Symbol *s;
-  bool wasInserted;
-  std::tie(s, wasInserted) = insert(name, nullptr);
+  auto [s, wasInserted] = insert(name, nullptr);
   s->isUsedInRegularObj = true;
   if (wasInserted || isa<Undefined>(s) || s->isLazy()) {
-    replaceSymbol<DefinedImportThunk>(s, name, id, machine);
+    replaceSymbol<DefinedImportThunk>(s, ctx, name, id, machine);
     return s;
   }
 
@@ -797,21 +776,21 @@ void SymbolTable::addLibcall(StringRef name) {
   }
 }
 
-std::vector<Chunk *> SymbolTable::getChunks() {
+std::vector<Chunk *> SymbolTable::getChunks() const {
   std::vector<Chunk *> res;
-  for (ObjFile *file : ObjFile::instances) {
+  for (ObjFile *file : ctx.objFileInstances) {
     ArrayRef<Chunk *> v = file->getChunks();
     res.insert(res.end(), v.begin(), v.end());
   }
   return res;
 }
 
-Symbol *SymbolTable::find(StringRef name) {
+Symbol *SymbolTable::find(StringRef name) const {
   return symMap.lookup(CachedHashStringRef(name));
 }
 
-Symbol *SymbolTable::findUnderscore(StringRef name) {
-  if (config->machine == I386)
+Symbol *SymbolTable::findUnderscore(StringRef name) const {
+  if (ctx.config.machine == I386)
     return find(("_" + name).str());
   return find(name);
 }
@@ -832,9 +811,17 @@ std::vector<Symbol *> SymbolTable::getSymsWithPrefix(StringRef prefix) {
 }
 
 Symbol *SymbolTable::findMangle(StringRef name) {
-  if (Symbol *sym = find(name))
-    if (!isa<Undefined>(sym))
+  if (Symbol *sym = find(name)) {
+    if (auto *u = dyn_cast<Undefined>(sym)) {
+      // We're specifically looking for weak aliases that ultimately resolve to
+      // defined symbols, hence the call to getWeakAlias() instead of just using
+      // the weakAlias member variable. This matches link.exe's behavior.
+      if (Symbol *weakAlias = u->getWeakAlias())
+        return weakAlias;
+    } else {
       return sym;
+    }
+  }
 
   // Efficient fuzzy string lookup is impossible with a hash table, so iterate
   // the symbol table once and collect all possibly matching symbols into this
@@ -850,7 +837,7 @@ Symbol *SymbolTable::findMangle(StringRef name) {
   };
 
   // For non-x86, just look for C++ functions.
-  if (config->machine != I386)
+  if (ctx.config.machine != I386)
     return findByPrefix("?" + name + "@@Y");
 
   if (!name.startswith("_"))
@@ -872,20 +859,19 @@ Symbol *SymbolTable::addUndefined(StringRef name) {
   return addUndefined(name, nullptr, false);
 }
 
-void SymbolTable::addCombinedLTOObjects() {
-  if (BitcodeFile::instances.empty())
+void SymbolTable::compileBitcodeFiles() {
+  if (ctx.bitcodeFileInstances.empty())
     return;
 
-  ScopedTimer t(ltoTimer);
-  lto.reset(new BitcodeCompiler);
-  for (BitcodeFile *f : BitcodeFile::instances)
+  ScopedTimer t(ctx.ltoTimer);
+  lto.reset(new BitcodeCompiler(ctx));
+  for (BitcodeFile *f : ctx.bitcodeFileInstances)
     lto->add(*f);
   for (InputFile *newObj : lto->compile()) {
     ObjFile *obj = cast<ObjFile>(newObj);
     obj->parse();
-    ObjFile::instances.push_back(obj);
+    ctx.objFileInstances.push_back(obj);
   }
 }
 
-} // namespace coff
-} // namespace lld
+} // namespace lld::coff
index e88002c..33ed65c 100644 (file)
@@ -20,11 +20,11 @@ namespace llvm {
 struct LTOCodeGenerator;
 }
 
-namespace lld {
-namespace coff {
+namespace lld::coff {
 
 class Chunk;
 class CommonChunk;
+class COFFLinkerContext;
 class Defined;
 class DefinedAbsolute;
 class DefinedRegular;
@@ -47,6 +47,8 @@ class Symbol;
 // There is one add* function per symbol type.
 class SymbolTable {
 public:
+  SymbolTable(COFFLinkerContext &c) : ctx(c) {}
+
   void addFile(InputFile *file);
 
   // Emit errors for symbols that cannot be resolved.
@@ -63,11 +65,11 @@ public:
   bool handleMinGWAutomaticImport(Symbol *sym, StringRef name);
 
   // Returns a list of chunks of selected symbols.
-  std::vector<Chunk *> getChunks();
+  std::vector<Chunk *> getChunks() const;
 
   // Returns a symbol for a given name. Returns a nullptr if not found.
-  Symbol *find(StringRef name);
-  Symbol *findUnderscore(StringRef name);
+  Symbol *find(StringRef name) const;
+  Symbol *findUnderscore(StringRef name) const;
 
   // Occasionally we have to resolve an undefined symbol to its
   // mangled symbol. This function tries to find a mangled name
@@ -78,7 +80,7 @@ public:
   // Build a set of COFF objects representing the combined contents of
   // BitcodeFiles and add them to the symbol table. Called after all files are
   // added and before the writer writes results to a file.
-  void addCombinedLTOObjects();
+  void compileBitcodeFiles();
 
   // Creates an Undefined symbol for a given name.
   Symbol *addUndefined(StringRef name);
@@ -88,12 +90,13 @@ public:
 
   Symbol *addUndefined(StringRef name, InputFile *f, bool isWeakAlias);
   void addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym);
-  void addLazyObject(LazyObjFile *f, StringRef n);
+  void addLazyObject(InputFile *f, StringRef n);
   void addLazyDLLSymbol(DLLFile *f, DLLFile::Symbol *sym, StringRef n);
   Symbol *addAbsolute(StringRef n, COFFSymbolRef s);
   Symbol *addRegular(InputFile *f, StringRef n,
                      const llvm::object::coff_symbol_generic *s = nullptr,
-                     SectionChunk *c = nullptr, uint32_t sectionOffset = 0);
+                     SectionChunk *c = nullptr, uint32_t sectionOffset = 0,
+                     bool isWeak = false);
   std::pair<DefinedRegular *, bool>
   addComdat(InputFile *f, StringRef n,
             const llvm::object::coff_symbol_generic *s = nullptr);
@@ -131,15 +134,14 @@ private:
 
   llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> symMap;
   std::unique_ptr<BitcodeCompiler> lto;
-};
 
-extern SymbolTable *symtab;
+  COFFLinkerContext &ctx;
+};
 
 std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex);
 
 StringRef ltrim1(StringRef s, const char *chars);
 
-} // namespace coff
-} // namespace lld
+} // namespace lld::coff
 
 #endif
index 8a6a9b2..c042386 100644 (file)
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "Symbols.h"
+#include "COFFLinkerContext.h"
 #include "InputFiles.h"
 #include "lld/Common/ErrorHandler.h"
 #include "lld/Common/Memory.h"
@@ -27,27 +28,29 @@ static_assert(sizeof(SymbolUnion) <= 48,
               "symbols should be optimized for memory usage");
 
 // Returns a symbol name for an error message.
-static std::string maybeDemangleSymbol(StringRef symName) {
-  if (config->demangle) {
+static std::string maybeDemangleSymbol(const COFFLinkerContext &ctx,
+                                       StringRef symName) {
+  if (ctx.config.demangle) {
     std::string prefix;
     StringRef prefixless = symName;
     if (prefixless.consume_front("__imp_"))
       prefix = "__declspec(dllimport) ";
     StringRef demangleInput = prefixless;
-    if (config->machine == I386)
+    if (ctx.config.machine == I386)
       demangleInput.consume_front("_");
-    std::string demangled = demangle(std::string(demangleInput));
+    std::string demangled = demangle(demangleInput.str());
     if (demangled != demangleInput)
-      return prefix + demangle(std::string(demangleInput));
+      return prefix + demangle(demangleInput.str());
     return (prefix + prefixless).str();
   }
   return std::string(symName);
 }
-std::string toString(coff::Symbol &b) {
-  return maybeDemangleSymbol(b.getName());
+std::string toString(const COFFLinkerContext &ctx, coff::Symbol &b) {
+  return maybeDemangleSymbol(ctx, b.getName());
 }
-std::string toCOFFString(const Archive::Symbol &b) {
-  return maybeDemangleSymbol(b.getName());
+std::string toCOFFString(const COFFLinkerContext &ctx,
+                         const Archive::Symbol &b) {
+  return maybeDemangleSymbol(ctx, b.getName());
 }
 
 namespace coff {
@@ -102,23 +105,24 @@ COFFSymbolRef DefinedCOFF::getCOFFSymbol() {
   return COFFSymbolRef(reinterpret_cast<const coff_symbol32 *>(sym));
 }
 
-uint16_t DefinedAbsolute::numOutputSections;
+uint64_t DefinedAbsolute::getRVA() { return va - ctx.config.imageBase; }
 
-static Chunk *makeImportThunk(DefinedImportData *s, uint16_t machine) {
+static Chunk *makeImportThunk(COFFLinkerContext &ctx, DefinedImportData *s,
+                              uint16_t machine) {
   if (machine == AMD64)
-    return make<ImportThunkChunkX64>(s);
+    return make<ImportThunkChunkX64>(ctx, s);
   if (machine == I386)
-    return make<ImportThunkChunkX86>(s);
+    return make<ImportThunkChunkX86>(ctx, s);
   if (machine == ARM64)
-    return make<ImportThunkChunkARM64>(s);
+    return make<ImportThunkChunkARM64>(ctx, s);
   assert(machine == ARMNT);
-  return make<ImportThunkChunkARM>(s);
+  return make<ImportThunkChunkARM>(ctx, s);
 }
 
-DefinedImportThunk::DefinedImportThunk(StringRef name, DefinedImportData *s,
-                                       uint16_t machine)
+DefinedImportThunk::DefinedImportThunk(COFFLinkerContext &ctx, StringRef name,
+                                       DefinedImportData *s, uint16_t machine)
     : Defined(DefinedImportThunkKind, name), wrappedSym(s),
-      data(makeImportThunk(s, machine)) {}
+      data(makeImportThunk(ctx, s, machine)) {}
 
 Defined *Undefined::getWeakAlias() {
   // A weak alias may be a weak alias to another symbol, so check recursively.
@@ -130,11 +134,11 @@ Defined *Undefined::getWeakAlias() {
 
 MemoryBufferRef LazyArchive::getMemberBuffer() {
   Archive::Child c =
-    CHECK(sym.getMember(),
-          "could not get the member for symbol " + toCOFFString(sym));
+      CHECK(sym.getMember(), "could not get the member for symbol " +
+                                 toCOFFString(file->ctx, sym));
   return CHECK(c.getMemoryBufferRef(),
-      "could not get the buffer for the member defining symbol " +
-      toCOFFString(sym));
+               "could not get the buffer for the member defining symbol " +
+                   toCOFFString(file->ctx, sym));
 }
 } // namespace coff
 } // namespace lld
index bb91117..750269f 100644 (file)
 
 namespace lld {
 
-std::string toString(coff::Symbol &b);
-
-// There are two different ways to convert an Archive::Symbol to a string:
-// One for Microsoft name mangling and one for Itanium name mangling.
-// Call the functions toCOFFString and toELFString, not just toString.
-std::string toCOFFString(const coff::Archive::Symbol &b);
-
 namespace coff {
 
 using llvm::object::Archive;
@@ -37,6 +30,7 @@ using llvm::object::coff_import_header;
 using llvm::object::coff_symbol_generic;
 
 class ArchiveFile;
+class COFFLinkerContext;
 class InputFile;
 class ObjFile;
 class SymbolTable;
@@ -106,7 +100,11 @@ protected:
       : symbolKind(k), isExternal(true), isCOMDAT(false),
         writtenToSymtab(false), pendingArchiveLoad(false), isGCRoot(false),
         isRuntimePseudoReloc(false), deferUndefined(false), canInline(true),
-        nameSize(n.size()), nameData(n.empty() ? nullptr : n.data()) {}
+        isWeak(false), nameSize(n.size()),
+        nameData(n.empty() ? nullptr : n.data()) {
+    assert((!n.empty() || k <= LastDefinedCOFFKind) &&
+           "If the name is empty, the Symbol must be a DefinedCOFF.");
+  }
 
   const unsigned symbolKind : 8;
   unsigned isExternal : 1;
@@ -142,6 +140,11 @@ public:
   // doesn't know the final contents of the symbol.
   unsigned canInline : 1;
 
+  // True if the symbol is weak. This is only tracked for bitcode/LTO symbols.
+  // This information isn't written to the output; rather, it's used for
+  // managing weak symbol overrides.
+  unsigned isWeak : 1;
+
 protected:
   // Symbol name length. Assume symbol lengths fit in a 32-bit integer.
   uint32_t nameSize;
@@ -197,10 +200,11 @@ public:
   DefinedRegular(InputFile *f, StringRef n, bool isCOMDAT,
                  bool isExternal = false,
                  const coff_symbol_generic *s = nullptr,
-                 SectionChunk *c = nullptr)
+                 SectionChunk *c = nullptr, bool isWeak = false)
       : DefinedCOFF(DefinedRegularKind, f, n, s), data(c ? &c->repl : nullptr) {
     this->isExternal = isExternal;
     this->isCOMDAT = isCOMDAT;
+    this->isWeak = isWeak;
   }
 
   static bool classof(const Symbol *s) {
@@ -240,29 +244,25 @@ private:
 // Absolute symbols.
 class DefinedAbsolute : public Defined {
 public:
-  DefinedAbsolute(StringRef n, COFFSymbolRef s)
-      : Defined(DefinedAbsoluteKind, n), va(s.getValue()) {
+  DefinedAbsolute(const COFFLinkerContext &c, StringRef n, COFFSymbolRef s)
+      : Defined(DefinedAbsoluteKind, n), va(s.getValue()), ctx(c) {
     isExternal = s.isExternal();
   }
 
-  DefinedAbsolute(StringRef n, uint64_t v)
-      : Defined(DefinedAbsoluteKind, n), va(v) {}
+  DefinedAbsolute(const COFFLinkerContext &c, StringRef n, uint64_t v)
+      : Defined(DefinedAbsoluteKind, n), va(v), ctx(c) {}
 
   static bool classof(const Symbol *s) {
     return s->kind() == DefinedAbsoluteKind;
   }
 
-  uint64_t getRVA() { return va - config->imageBase; }
+  uint64_t getRVA();
   void setVA(uint64_t v) { va = v; }
   uint64_t getVA() const { return va; }
 
-  // Section index relocations against absolute symbols resolve to
-  // this 16 bit number, and it is the largest valid section index
-  // plus one. This variable keeps it.
-  static uint16_t numOutputSections;
-
 private:
   uint64_t va;
+  const COFFLinkerContext &ctx;
 };
 
 // This symbol is used for linker-synthesized symbols like __ImageBase and
@@ -305,10 +305,9 @@ public:
 
 class LazyObject : public Symbol {
 public:
-  LazyObject(LazyObjFile *f, StringRef n)
-      : Symbol(LazyObjectKind, n), file(f) {}
+  LazyObject(InputFile *f, StringRef n) : Symbol(LazyObjectKind, n), file(f) {}
   static bool classof(const Symbol *s) { return s->kind() == LazyObjectKind; }
-  LazyObjFile *file;
+  InputFile *file;
 };
 
 // MinGW only.
@@ -384,7 +383,8 @@ public:
 // a regular name. A function pointer is given as a DefinedImportData.
 class DefinedImportThunk : public Defined {
 public:
-  DefinedImportThunk(StringRef name, DefinedImportData *s, uint16_t machine);
+  DefinedImportThunk(COFFLinkerContext &ctx, StringRef name,
+                     DefinedImportData *s, uint16_t machine);
 
   static bool classof(const Symbol *s) {
     return s->kind() == DefinedImportThunkKind;
@@ -406,8 +406,9 @@ private:
 // This is here just for compatibility with MSVC.
 class DefinedLocalImport : public Defined {
 public:
-  DefinedLocalImport(StringRef n, Defined *s)
-      : Defined(DefinedLocalImportKind, n), data(make<LocalImportChunk>(s)) {}
+  DefinedLocalImport(COFFLinkerContext &ctx, StringRef n, Defined *s)
+      : Defined(DefinedLocalImportKind, n),
+        data(make<LocalImportChunk>(ctx, s)) {}
 
   static bool classof(const Symbol *s) {
     return s->kind() == DefinedLocalImportKind;
@@ -502,6 +503,10 @@ void replaceSymbol(Symbol *s, ArgT &&... arg) {
 }
 } // namespace coff
 
+std::string toString(const coff::COFFLinkerContext &ctx, coff::Symbol &b);
+std::string toCOFFString(const coff::COFFLinkerContext &ctx,
+                         const llvm::object::Archive::Symbol &b);
+
 } // namespace lld
 
 #endif
index 72fd5fc..b4e3d6e 100644 (file)
 #define LLD_COFF_TYPEMERGER_H
 
 #include "Config.h"
+#include "DebugTypes.h"
+#include "lld/Common/Timer.h"
 #include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h"
 #include "llvm/DebugInfo/CodeView/TypeHashing.h"
 #include "llvm/Support/Allocator.h"
 #include <atomic>
 
-namespace lld {
-namespace coff {
+namespace lld::coff {
 
 using llvm::codeview::GloballyHashedType;
 using llvm::codeview::TypeIndex;
@@ -25,19 +26,19 @@ struct GHashState;
 
 class TypeMerger {
 public:
-  TypeMerger(llvm::BumpPtrAllocator &alloc);
+  TypeMerger(COFFLinkerContext &ctx, llvm::BumpPtrAllocator &alloc);
 
   ~TypeMerger();
 
   /// Get the type table or the global type table if /DEBUG:GHASH is enabled.
   inline llvm::codeview::TypeCollection &getTypeTable() {
-    assert(!config->debugGHashes);
+    assert(!ctx.config.debugGHashes);
     return typeTable;
   }
 
   /// Get the ID table or the global ID table if /DEBUG:GHASH is enabled.
   inline llvm::codeview::TypeCollection &getIDTable() {
-    assert(!config->debugGHashes);
+    assert(!ctx.config.debugGHashes);
     return idTable;
   }
 
@@ -59,9 +60,24 @@ public:
   // keyed by type index.
   SmallVector<uint32_t, 0> tpiCounts;
   SmallVector<uint32_t, 0> ipiCounts;
+
+  /// Dependency type sources, such as type servers or PCH object files. These
+  /// must be processed before objects that rely on them. Set by
+  /// sortDependencies.
+  ArrayRef<TpiSource *> dependencySources;
+
+  /// Object file sources. These must be processed after dependencySources.
+  ArrayRef<TpiSource *> objectSources;
+
+  /// Sorts the dependencies and reassigns TpiSource indices.
+  void sortDependencies();
+
+private:
+  void clearGHashes();
+
+  COFFLinkerContext &ctx;
 };
 
-} // namespace coff
-} // namespace lld
+} // namespace lld::coff
 
 #endif
index 37cbe2b..a8fc38c 100644 (file)
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "Writer.h"
+#include "COFFLinkerContext.h"
 #include "CallGraphSort.h"
 #include "Config.h"
 #include "DLL.h"
@@ -22,7 +23,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringSet.h"
-#include "llvm/ADT/StringSwitch.h"
+#include "llvm/BinaryFormat/COFF.h"
 #include "llvm/Support/BinaryStreamReader.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/Endian.h"
@@ -80,23 +81,14 @@ static_assert(dosStubSize % 8 == 0, "DOSStub size must be multiple of 8");
 
 static const int numberOfDataDirectory = 16;
 
-// Global vector of all output sections. After output sections are finalized,
-// this can be indexed by Chunk::getOutputSection.
-static std::vector<OutputSection *> outputSections;
-
-OutputSection *Chunk::getOutputSection() const {
-  return osidx == 0 ? nullptr : outputSections[osidx - 1];
-}
-
-void OutputSection::clear() { outputSections.clear(); }
-
 namespace {
 
 class DebugDirectoryChunk : public NonSectionChunk {
 public:
-  DebugDirectoryChunk(const std::vector<std::pair<COFF::DebugType, Chunk *>> &r,
+  DebugDirectoryChunk(const COFFLinkerContext &c,
+                      const std::vector<std::pair<COFF::DebugType, Chunk *>> &r,
                       bool writeRepro)
-      : records(r), writeRepro(writeRepro) {}
+      : records(r), writeRepro(writeRepro), ctx(c) {}
 
   size_t getSize() const override {
     return (records.size() + int(writeRepro)) * sizeof(debug_directory);
@@ -107,7 +99,7 @@ public:
 
     for (const std::pair<COFF::DebugType, Chunk *>& record : records) {
       Chunk *c = record.second;
-      OutputSection *os = c->getOutputSection();
+      const OutputSection *os = ctx.getOutputSection(c);
       uint64_t offs = os->getFileOff() + (c->getRVA() - os->getRVA());
       fillEntry(d, record.first, c->getSize(), c->getRVA(), offs);
       ++d;
@@ -146,12 +138,15 @@ private:
   mutable std::vector<support::ulittle32_t *> timeDateStamps;
   const std::vector<std::pair<COFF::DebugType, Chunk *>> &records;
   bool writeRepro;
+  const COFFLinkerContext &ctx;
 };
 
 class CVDebugRecordChunk : public NonSectionChunk {
 public:
+  CVDebugRecordChunk(const COFFLinkerContext &c) : ctx(c) {}
+
   size_t getSize() const override {
-    return sizeof(codeview::DebugInfo) + config->pdbAltPath.size() + 1;
+    return sizeof(codeview::DebugInfo) + ctx.config.pdbAltPath.size() + 1;
   }
 
   void writeTo(uint8_t *b) const override {
@@ -161,12 +156,15 @@ public:
 
     // variable sized field (PDB Path)
     char *p = reinterpret_cast<char *>(b + sizeof(*buildId));
-    if (!config->pdbAltPath.empty())
-      memcpy(p, config->pdbAltPath.data(), config->pdbAltPath.size());
-    p[config->pdbAltPath.size()] = '\0';
+    if (!ctx.config.pdbAltPath.empty())
+      memcpy(p, ctx.config.pdbAltPath.data(), ctx.config.pdbAltPath.size());
+    p[ctx.config.pdbAltPath.size()] = '\0';
   }
 
   mutable codeview::DebugInfo *buildId = nullptr;
+
+private:
+  const COFFLinkerContext &ctx;
 };
 
 class ExtendedDllCharacteristicsChunk : public NonSectionChunk {
@@ -190,7 +188,7 @@ public:
 
   bool operator<(const PartialSectionKey &other) const {
     int c = name.compare(other.name);
-    if (c == 1)
+    if (c > 0)
       return false;
     if (c == 0)
       return characteristics < other.characteristics;
@@ -201,7 +199,8 @@ public:
 // The writer writes a SymbolTable result to a file.
 class Writer {
 public:
-  Writer() : buffer(errorHandler().outputBuffer) {}
+  Writer(COFFLinkerContext &c)
+      : buffer(errorHandler().outputBuffer), delayIdata(c), edata(c), ctx(c) {}
   void run();
 
 private:
@@ -214,6 +213,12 @@ private:
   void mergeSections();
   void removeUnusedSections();
   void assignAddresses();
+  bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin);
+  std::pair<Defined *, bool> getThunk(DenseMap<uint64_t, Defined *> &lastThunks,
+                                      Defined *target, uint64_t p,
+                                      uint16_t type, int margin);
+  bool createThunks(OutputSection *os, int margin);
+  bool verifyRanges(const std::vector<Chunk *> chunks);
   void finalizeAddresses();
   void removeEmptySections();
   void assignOutputSectionIndices();
@@ -223,6 +228,7 @@ private:
   void createSEHTable();
   void createRuntimePseudoRelocs();
   void insertCtorDtorSymbols();
+  void markSymbolsWithRelocations(ObjFile *file, SymbolRVASet &usedSymbols);
   void createGuardCFTables();
   void markSymbolsForRVATable(ObjFile *file,
                               ArrayRef<SectionChunk *> symIdxChunks,
@@ -235,17 +241,19 @@ private:
   void setSectionPermissions();
   void writeSections();
   void writeBuildId();
+  void writePEChecksum();
   void sortSections();
   void sortExceptionTable();
   void sortCRTSectionChunks(std::vector<Chunk *> &chunks);
   void addSyntheticIdata();
+  void sortBySectionOrder(std::vector<Chunk *> &chunks);
   void fixPartialSectionChars(StringRef name, uint32_t chars);
   bool fixGnuImportChunks();
   void fixTlsAlignment();
   PartialSection *createPartialSection(StringRef name, uint32_t outChars);
   PartialSection *findPartialSection(StringRef name, uint32_t outChars);
 
-  llvm::Optional<coff_symbol16> createSymbol(Defined *d);
+  std::optional<coff_symbol16> createSymbol(Defined *d);
   size_t addEntryToStringTable(StringRef str);
 
   OutputSection *findSection(StringRef name);
@@ -254,6 +262,9 @@ private:
 
   uint32_t getSizeOfInitializedData();
 
+  void checkLoadConfig();
+  template <typename T> void checkLoadConfigGuardData(const T *loadConfig);
+
   std::unique_ptr<FileOutputBuffer> &buffer;
   std::map<PartialSectionKey, PartialSection *> partialSections;
   std::vector<char> strtab;
@@ -304,13 +315,12 @@ private:
   // files, so we need to keep track of them separately.
   Chunk *firstPdata = nullptr;
   Chunk *lastPdata;
+
+  COFFLinkerContext &ctx;
 };
 } // anonymous namespace
 
-static Timer codeLayoutTimer("Code Layout", Timer::root());
-static Timer diskCommitTimer("Commit Output File", Timer::root());
-
-void lld::coff::writeResult() { Writer().run(); }
+void lld::coff::writeResult(COFFLinkerContext &ctx) { Writer(ctx).run(); }
 
 void OutputSection::addChunk(Chunk *c) {
   chunks.push_back(c);
@@ -334,14 +344,14 @@ void OutputSection::merge(OutputSection *other) {
 }
 
 // Write the section header to a given buffer.
-void OutputSection::writeHeaderTo(uint8_t *buf) {
+void OutputSection::writeHeaderTo(uint8_t *buf, bool isDebug) {
   auto *hdr = reinterpret_cast<coff_section *>(buf);
   *hdr = header;
   if (stringTableOff) {
     // If name is too long, write offset into the string table as a name.
-    sprintf(hdr->Name, "/%d", stringTableOff);
+    encodeSectionName(hdr->Name, stringTableOff);
   } else {
-    assert(!config->debug || name.size() <= COFF::NameSize ||
+    assert(!isDebug || name.size() <= COFF::NameSize ||
            (hdr->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0);
     strncpy(hdr->Name, name.data(),
             std::min(name.size(), (size_t)COFF::NameSize));
@@ -354,8 +364,8 @@ void OutputSection::addContributingPartialSection(PartialSection *sec) {
 
 // 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) {
-  if (config->machine == ARMNT) {
+bool Writer::isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) {
+  if (ctx.config.machine == ARMNT) {
     int64_t diff = AbsoluteDifference(s, p + 4) + margin;
     switch (relType) {
     case IMAGE_REL_ARM_BRANCH20T:
@@ -366,7 +376,7 @@ static bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) {
     default:
       return true;
     }
-  } else if (config->machine == ARM64) {
+  } else if (ctx.config.machine == ARM64) {
     int64_t diff = AbsoluteDifference(s, p) + margin;
     switch (relType) {
     case IMAGE_REL_ARM64_BRANCH26:
@@ -385,24 +395,24 @@ static bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) {
 
 // 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) {
+std::pair<Defined *, bool>
+Writer::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};
   Chunk *c;
-  switch (config->machine) {
+  switch (ctx.config.machine) {
   case ARMNT:
-    c = make<RangeExtensionThunkARM>(target);
+    c = make<RangeExtensionThunkARM>(ctx, target);
     break;
   case ARM64:
-    c = make<RangeExtensionThunkARM64>(target);
+    c = make<RangeExtensionThunkARM64>(ctx, target);
     break;
   default:
     llvm_unreachable("Unexpected architecture");
   }
-  Defined *d = make<DefinedSynthetic>("", c);
+  Defined *d = make<DefinedSynthetic>("range_extension_thunk", c);
   lastThunk = d;
   return {d, true};
 }
@@ -418,7 +428,7 @@ getThunk(DenseMap<uint64_t, Defined *> &lastThunks, Defined *target, uint64_t p,
 // 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(OutputSection *os, int margin) {
+bool Writer::createThunks(OutputSection *os, int margin) {
   bool addressesChanged = false;
   DenseMap<uint64_t, Defined *> lastThunks;
   DenseMap<std::pair<ObjFile *, Defined *>, uint32_t> thunkSymtabIndices;
@@ -458,11 +468,8 @@ static bool createThunks(OutputSection *os, int margin) {
       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 the target isn't in range, hook it up to an existing or new thunk.
+      auto [thunk, wasNew] = getThunk(lastThunks, sym, p, rel.Type, margin);
       if (wasNew) {
         Chunk *thunkChunk = thunk->getChunk();
         thunkChunk->setRVA(
@@ -491,11 +498,11 @@ static bool createThunks(OutputSection *os, int margin) {
     ArrayRef<coff_relocation> curRelocs = sc->getRelocs();
     MutableArrayRef<coff_relocation> newRelocs;
     if (originalRelocs.data() == curRelocs.data()) {
-      newRelocs = makeMutableArrayRef(
-          bAlloc.Allocate<coff_relocation>(originalRelocs.size()),
+      newRelocs = MutableArrayRef(
+          bAlloc().Allocate<coff_relocation>(originalRelocs.size()),
           originalRelocs.size());
     } else {
-      newRelocs = makeMutableArrayRef(
+      newRelocs = MutableArrayRef(
           const_cast<coff_relocation *>(curRelocs.data()), curRelocs.size());
     }
 
@@ -517,7 +524,7 @@ static bool createThunks(OutputSection *os, int margin) {
 }
 
 // Verify that all relocations are in range, with no extra margin requirements.
-static bool verifyRanges(const std::vector<Chunk *> chunks) {
+bool Writer::verifyRanges(const std::vector<Chunk *> chunks) {
   for (Chunk *c : chunks) {
     SectionChunk *sc = dyn_cast_or_null<SectionChunk>(c);
     if (!sc)
@@ -545,11 +552,11 @@ static bool verifyRanges(const std::vector<Chunk *> chunks) {
 // Assign addresses and add thunks if necessary.
 void Writer::finalizeAddresses() {
   assignAddresses();
-  if (config->machine != ARMNT && config->machine != ARM64)
+  if (ctx.config.machine != ARMNT && ctx.config.machine != ARM64)
     return;
 
   size_t origNumChunks = 0;
-  for (OutputSection *sec : outputSections) {
+  for (OutputSection *sec : ctx.outputSections) {
     sec->origChunks = sec->chunks;
     origNumChunks += sec->chunks.size();
   }
@@ -561,7 +568,7 @@ void Writer::finalizeAddresses() {
     // adding them turned out ok.
     bool rangesOk = true;
     size_t numChunks = 0;
-    for (OutputSection *sec : outputSections) {
+    for (OutputSection *sec : ctx.outputSections) {
       if (!verifyRanges(sec->chunks)) {
         rangesOk = false;
         break;
@@ -582,7 +589,7 @@ void Writer::finalizeAddresses() {
       // 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)
+      for (OutputSection *sec : ctx.outputSections)
         sec->chunks = sec->origChunks;
       margin *= 2;
     }
@@ -590,7 +597,7 @@ void Writer::finalizeAddresses() {
     // 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)
+    for (OutputSection *sec : ctx.outputSections)
       addressesChanged |= createThunks(sec, margin);
     // If the verification above thought we needed thunks, we should have
     // added some.
@@ -605,9 +612,46 @@ void Writer::finalizeAddresses() {
   }
 }
 
+void Writer::writePEChecksum() {
+  if (!ctx.config.writeCheckSum) {
+    return;
+  }
+
+  // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#checksum
+  uint32_t *buf = (uint32_t *)buffer->getBufferStart();
+  uint32_t size = (uint32_t)(buffer->getBufferSize());
+
+  coff_file_header *coffHeader =
+      (coff_file_header *)((uint8_t *)buf + dosStubSize + sizeof(PEMagic));
+  pe32_header *peHeader =
+      (pe32_header *)((uint8_t *)coffHeader + sizeof(coff_file_header));
+
+  uint64_t sum = 0;
+  uint32_t count = size;
+  ulittle16_t *addr = (ulittle16_t *)buf;
+
+  // The PE checksum algorithm, implemented as suggested in RFC1071
+  while (count > 1) {
+    sum += *addr++;
+    count -= 2;
+  }
+
+  // Add left-over byte, if any
+  if (count > 0)
+    sum += *(unsigned char *)addr;
+
+  // Fold 32-bit sum to 16 bits
+  while (sum >> 16) {
+    sum = (sum & 0xffff) + (sum >> 16);
+  }
+
+  sum += size;
+  peHeader->CheckSum = sum;
+}
+
 // The main function of the writer.
 void Writer::run() {
-  ScopedTimer t1(codeLayoutTimer);
+  ScopedTimer t1(ctx.codeLayoutTimer);
 
   createImportTables();
   createSections();
@@ -627,13 +671,14 @@ void Writer::run() {
     fatal("image size (" + Twine(fileSize) + ") " +
         "exceeds maximum allowable size (" + Twine(UINT32_MAX) + ")");
 
-  openFile(config->outputFile);
-  if (config->is64()) {
+  openFile(ctx.config.outputFile);
+  if (ctx.config.is64()) {
     writeHeader<pe32plus_header>();
   } else {
     writeHeader<pe32_header>();
   }
   writeSections();
+  checkLoadConfig();
   sortExceptionTable();
 
   // Fix up the alignment in the TLS Directory's characteristic field,
@@ -643,21 +688,24 @@ void Writer::run() {
 
   t1.stop();
 
-  if (!config->pdbPath.empty() && config->debug) {
+  if (!ctx.config.pdbPath.empty() && ctx.config.debug) {
     assert(buildId);
-    createPDB(symtab, outputSections, sectionTable, buildId->buildId);
+    createPDB(ctx, sectionTable, buildId->buildId);
   }
   writeBuildId();
 
-  writeLLDMapFile(outputSections);
-  writeMapFile(outputSections);
+  writeLLDMapFile(ctx);
+  writeMapFile(ctx);
+
+  writePEChecksum();
 
   if (errorCount())
     return;
 
-  ScopedTimer t2(diskCommitTimer);
+  ScopedTimer t2(ctx.outputCommitTimer);
   if (auto e = buffer->commit())
-    fatal("failed to write the output file: " + toString(std::move(e)));
+    fatal("failed to write output '" + buffer->getPath() +
+          "': " + toString(std::move(e)));
 }
 
 static StringRef getOutputSectionName(StringRef name) {
@@ -669,11 +717,11 @@ static StringRef getOutputSectionName(StringRef name) {
 }
 
 // For /order.
-static void sortBySectionOrder(std::vector<Chunk *> &chunks) {
-  auto getPriority = [](const Chunk *c) {
+void Writer::sortBySectionOrder(std::vector<Chunk *> &chunks) {
+  auto getPriority = [&ctx = ctx](const Chunk *c) {
     if (auto *sec = dyn_cast<SectionChunk>(c))
       if (sec->sym)
-        return config->order.lookup(sec->sym->getName());
+        return ctx.config.order.lookup(sec->sym->getName());
     return 0;
   };
 
@@ -719,7 +767,7 @@ bool Writer::fixGnuImportChunks() {
 
   bool hasIdata = false;
   // Sort all .idata$* chunks, grouping chunks from the same library,
-  // with alphabetical ordering of the object fils within a library.
+  // with alphabetical ordering of the object files within a library.
   for (auto it : partialSections) {
     PartialSection *pSec = it.second;
     if (!pSec->name.startswith(".idata"))
@@ -752,7 +800,7 @@ bool Writer::fixGnuImportChunks() {
 // terminator in .idata$2.
 void Writer::addSyntheticIdata() {
   uint32_t rdata = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ;
-  idata.create();
+  idata.create(ctx);
 
   // Add the .idata content in the right section groups, to allow
   // chunks from other linked in object files to be grouped together.
@@ -795,7 +843,8 @@ void Writer::locateImportTables() {
 // Return whether a SectionChunk's suffix (the dollar and any trailing
 // suffix) should be removed and sorted into the main suffixless
 // PartialSection.
-static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name) {
+static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name,
+                                     bool isMinGW) {
   // On MinGW, comdat groups are formed by putting the comdat group name
   // after the '$' in the section name. For .eh_frame$<symbol>, that must
   // still be sorted before the .eh_frame trailer from crtend.o, thus just
@@ -805,7 +854,7 @@ static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name) {
   // hypothetical case of comdat .CRT$XCU, we definitely need to keep the
   // suffix for sorting. Thus, to play it safe, only strip the suffix for
   // the standard sections.
-  if (!config->mingw)
+  if (!isMinGW)
     return false;
   if (!sc || !sc->isCOMDAT())
     return false;
@@ -815,14 +864,15 @@ static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name) {
 }
 
 void Writer::sortSections() {
-  if (!config->callGraphProfile.empty()) {
-    DenseMap<const SectionChunk *, int> order = computeCallGraphProfileOrder();
+  if (!ctx.config.callGraphProfile.empty()) {
+    DenseMap<const SectionChunk *, int> order =
+        computeCallGraphProfileOrder(ctx);
     for (auto it : order) {
       if (DefinedRegular *sym = it.first->sym)
-        config->order[sym->getName()] = it.second;
+        ctx.config.order[sym->getName()] = it.second;
     }
   }
-  if (!config->order.empty())
+  if (!ctx.config.order.empty())
     for (auto it : partialSections)
       sortBySectionOrder(it.second->chunks);
 }
@@ -843,7 +893,7 @@ void Writer::createSections() {
     OutputSection *&sec = sections[{name, outChars}];
     if (!sec) {
       sec = make<OutputSection>(name, outChars);
-      outputSections.push_back(sec);
+      ctx.outputSections.push_back(sec);
     }
     return sec;
   };
@@ -864,15 +914,15 @@ void Writer::createSections() {
   dtorsSec = createSection(".dtors", data | r | w);
 
   // Then bin chunks by name and output characteristics.
-  for (Chunk *c : symtab->getChunks()) {
+  for (Chunk *c : ctx.symtab.getChunks()) {
     auto *sc = dyn_cast<SectionChunk>(c);
     if (sc && !sc->live) {
-      if (config->verbose)
+      if (ctx.config.verbose)
         sc->printDiscardedMessage();
       continue;
     }
     StringRef name = c->getSectionName();
-    if (shouldStripSectionSuffix(sc, name))
+    if (shouldStripSectionSuffix(sc, name, ctx.config.mingw))
       name = name.split('$').first;
 
     if (name.startswith(".tls"))
@@ -932,8 +982,14 @@ void Writer::createSections() {
     // Move DISCARDABLE (or non-memory-mapped) sections to the end of file
     // because the loader cannot handle holes. Stripping can remove other
     // discardable ones than .reloc, which is first of them (created early).
-    if (s->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
+    if (s->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) {
+      // Move discardable sections named .debug_ to the end, after other
+      // discardable sections. Stripping only removes the sections named
+      // .debug_* - thus try to avoid leaving holes after stripping.
+      if (s->name.startswith(".debug_"))
+        return 3;
       return 2;
+    }
     // .rsrc should come at the end of the non-discardable sections because its
     // size may change by the Win32 UpdateResources() function, causing
     // subsequent sections to move (see https://crbug.com/827082).
@@ -941,14 +997,16 @@ void Writer::createSections() {
       return 1;
     return 0;
   };
-  llvm::stable_sort(outputSections,
+  llvm::stable_sort(ctx.outputSections,
                     [&](const OutputSection *s, const OutputSection *t) {
                       return sectionOrder(s) < sectionOrder(t);
                     });
 }
 
 void Writer::createMiscChunks() {
-  for (MergeChunk *p : MergeChunk::instances) {
+  Configuration *config = &ctx.config;
+
+  for (MergeChunk *p : ctx.mergeChunkInstances) {
     if (p) {
       p->finalizeContents();
       rdataSec->addChunk(p);
@@ -956,15 +1014,16 @@ void Writer::createMiscChunks() {
   }
 
   // Create thunks for locally-dllimported symbols.
-  if (!symtab->localImportChunks.empty()) {
-    for (Chunk *c : symtab->localImportChunks)
+  if (!ctx.symtab.localImportChunks.empty()) {
+    for (Chunk *c : ctx.symtab.localImportChunks)
       rdataSec->addChunk(c);
   }
 
   // Create Debug Information Chunks
   OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec;
   if (config->debug || config->repro || config->cetCompat) {
-    debugDirectory = make<DebugDirectoryChunk>(debugRecords, config->repro);
+    debugDirectory =
+        make<DebugDirectoryChunk>(ctx, debugRecords, config->repro);
     debugDirectory->setAlignment(4);
     debugInfoSec->addChunk(debugDirectory);
   }
@@ -974,7 +1033,7 @@ void Writer::createMiscChunks() {
     // 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.
-    buildId = make<CVDebugRecordChunk>();
+    buildId = make<CVDebugRecordChunk>(ctx);
     debugRecords.push_back({COFF::IMAGE_DEBUG_TYPE_CODEVIEW, buildId});
   }
 
@@ -1013,21 +1072,21 @@ void Writer::createImportTables() {
   // Initialize DLLOrder so that import entries are ordered in
   // the same order as in the command line. (That affects DLL
   // initialization order, and this ordering is MSVC-compatible.)
-  for (ImportFile *file : ImportFile::instances) {
+  for (ImportFile *file : ctx.importFileInstances) {
     if (!file->live)
       continue;
 
     std::string dll = StringRef(file->dllName).lower();
-    if (config->dllOrder.count(dll) == 0)
-      config->dllOrder[dll] = config->dllOrder.size();
+    if (ctx.config.dllOrder.count(dll) == 0)
+      ctx.config.dllOrder[dll] = ctx.config.dllOrder.size();
 
     if (file->impSym && !isa<DefinedImportData>(file->impSym))
-      fatal(toString(*file->impSym) + " was replaced");
+      fatal(toString(ctx, *file->impSym) + " was replaced");
     DefinedImportData *impSym = cast_or_null<DefinedImportData>(file->impSym);
-    if (config->delayLoads.count(StringRef(file->dllName).lower())) {
+    if (ctx.config.delayLoads.count(StringRef(file->dllName).lower())) {
       if (!file->thunkSym)
         fatal("cannot delay-load " + toString(file) +
-              " due to import of data: " + toString(*impSym));
+              " due to import of data: " + toString(ctx, *impSym));
       delayIdata.add(impSym);
     } else {
       idata.add(impSym);
@@ -1036,10 +1095,10 @@ void Writer::createImportTables() {
 }
 
 void Writer::appendImportThunks() {
-  if (ImportFile::instances.empty())
+  if (ctx.importFileInstances.empty())
     return;
 
-  for (ImportFile *file : ImportFile::instances) {
+  for (ImportFile *file : ctx.importFileInstances) {
     if (!file->live)
       continue;
 
@@ -1047,14 +1106,14 @@ void Writer::appendImportThunks() {
       continue;
 
     if (!isa<DefinedImportThunk>(file->thunkSym))
-      fatal(toString(*file->thunkSym) + " was replaced");
+      fatal(toString(ctx, *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);
+    Defined *helper = cast<Defined>(ctx.config.delayLoadHelper);
     delayIdata.create(helper);
     for (Chunk *c : delayIdata.getChunks())
       didatSec->addChunk(c);
@@ -1062,6 +1121,10 @@ void Writer::appendImportThunks() {
       dataSec->addChunk(c);
     for (Chunk *c : delayIdata.getCodeChunks())
       textSec->addChunk(c);
+    for (Chunk *c : delayIdata.getCodePData())
+      pdataSec->addChunk(c);
+    for (Chunk *c : delayIdata.getCodeUnwindInfo())
+      rdataSec->addChunk(c);
   }
 }
 
@@ -1069,9 +1132,9 @@ void Writer::createExportTable() {
   if (!edataSec->chunks.empty()) {
     // Allow using a custom built export table from input object files, instead
     // of having the linker synthesize the tables.
-    if (config->hadExplicitExports)
+    if (ctx.config.hadExplicitExports)
       warn("literal .edata sections override exports");
-  } else if (!config->exports.empty()) {
+  } else if (!ctx.config.exports.empty()) {
     for (Chunk *c : edata.chunks)
       edataSec->addChunk(c);
   }
@@ -1080,9 +1143,9 @@ void Writer::createExportTable() {
     edataEnd = edataSec->chunks.back();
   }
   // Warn on exported deleting destructor.
-  for (auto e : config->exports)
+  for (auto e : ctx.config.exports)
     if (e.sym && e.sym->getName().startswith("??_G"))
-      warn("export of deleting dtor: " + toString(*e.sym));
+      warn("export of deleting dtor: " + toString(ctx, *e.sym));
 }
 
 void Writer::removeUnusedSections() {
@@ -1095,25 +1158,21 @@ void Writer::removeUnusedSections() {
     // 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());
+  llvm::erase_if(ctx.outputSections, isUnused);
 }
 
 // The Windows loader doesn't seem to like empty sections,
 // so we remove them if any.
 void Writer::removeEmptySections() {
   auto isEmpty = [](OutputSection *s) { return s->getVirtualSize() == 0; };
-  outputSections.erase(
-      std::remove_if(outputSections.begin(), outputSections.end(), isEmpty),
-      outputSections.end());
+  llvm::erase_if(ctx.outputSections, isEmpty);
 }
 
 void Writer::assignOutputSectionIndices() {
   // Assign final output section indices, and assign each chunk to its output
   // section.
   uint32_t idx = 1;
-  for (OutputSection *os : outputSections) {
+  for (OutputSection *os : ctx.outputSections) {
     os->sectionIndex = idx;
     for (Chunk *c : os->chunks)
       c->setOutputSectionIdx(idx);
@@ -1122,7 +1181,7 @@ void Writer::assignOutputSectionIndices() {
 
   // Merge chunks are containers of chunks, so assign those an output section
   // too.
-  for (MergeChunk *mc : MergeChunk::instances)
+  for (MergeChunk *mc : ctx.mergeChunkInstances)
     if (mc)
       for (SectionChunk *sc : mc->sections)
         if (sc && sc->live)
@@ -1137,25 +1196,29 @@ size_t Writer::addEntryToStringTable(StringRef str) {
   return offsetOfEntry;
 }
 
-Optional<coff_symbol16> Writer::createSymbol(Defined *def) {
+std::optional<coff_symbol16> Writer::createSymbol(Defined *def) {
   coff_symbol16 sym;
   switch (def->kind()) {
-  case Symbol::DefinedAbsoluteKind:
-    sym.Value = def->getRVA();
+  case Symbol::DefinedAbsoluteKind: {
+    auto *da = dyn_cast<DefinedAbsolute>(def);
+    // Note: COFF symbol can only store 32-bit values, so 64-bit absolute
+    // values will be truncated.
+    sym.Value = da->getVA();
     sym.SectionNumber = IMAGE_SYM_ABSOLUTE;
     break;
-  case Symbol::DefinedSyntheticKind:
-    // Relative symbols are unrepresentable in a COFF symbol table.
-    return None;
+  }
   default: {
     // Don't write symbols that won't be written to the output to the symbol
     // table.
+    // We also try to write DefinedSynthetic as a normal symbol. Some of these
+    // symbols do point to an actual chunk, like __safe_se_handler_table. Others
+    // like __ImageBase are outside of sections and thus cannot be represented.
     Chunk *c = def->getChunk();
     if (!c)
-      return None;
-    OutputSection *os = c->getOutputSection();
+      return std::nullopt;
+    OutputSection *os = ctx.getOutputSection(c);
     if (!os)
-      return None;
+      return std::nullopt;
 
     sym.Value = def->getRVA() - os->getRVA();
     sym.SectionNumber = os->sectionIndex;
@@ -1168,7 +1231,7 @@ Optional<coff_symbol16> Writer::createSymbol(Defined *def) {
   // instead. Avoid emitting them to the symbol table, as they can confuse
   // debuggers.
   if (def->isRuntimePseudoReloc)
-    return None;
+    return std::nullopt;
 
   StringRef name = def->getName();
   if (name.size() > COFF::NameSize) {
@@ -1183,6 +1246,10 @@ Optional<coff_symbol16> Writer::createSymbol(Defined *def) {
     COFFSymbolRef ref = d->getCOFFSymbol();
     sym.Type = ref.getType();
     sym.StorageClass = ref.getStorageClass();
+  } else if (def->kind() == Symbol::DefinedImportThunkKind) {
+    sym.Type = (IMAGE_SYM_DTYPE_FUNCTION << SCT_COMPLEX_TYPE_SHIFT) |
+               IMAGE_SYM_TYPE_NULL;
+    sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL;
   } else {
     sym.Type = IMAGE_SYM_TYPE_NULL;
     sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL;
@@ -1200,12 +1267,12 @@ void Writer::createSymbolAndStringTable() {
   // solution where discardable sections have long names preserved and
   // non-discardable sections have their names truncated, to ensure that any
   // section which is mapped at runtime also has its name mapped at runtime.
-  for (OutputSection *sec : outputSections) {
+  for (OutputSection *sec : ctx.outputSections) {
     if (sec->name.size() <= COFF::NameSize)
       continue;
     if ((sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0)
       continue;
-    if (config->warnLongSectionNames) {
+    if (ctx.config.warnLongSectionNames) {
       warn("section name " + sec->name +
            " is longer than 8 characters and will use a non-standard string "
            "table");
@@ -1213,16 +1280,31 @@ void Writer::createSymbolAndStringTable() {
     sec->setStringTableOff(addEntryToStringTable(sec->name));
   }
 
-  if (config->debugDwarf || config->debugSymtab) {
-    for (ObjFile *file : ObjFile::instances) {
+  if (ctx.config.debugDwarf || ctx.config.debugSymtab) {
+    for (ObjFile *file : ctx.objFileInstances) {
       for (Symbol *b : file->getSymbols()) {
         auto *d = dyn_cast_or_null<Defined>(b);
         if (!d || d->writtenToSymtab)
           continue;
         d->writtenToSymtab = true;
-
-        if (Optional<coff_symbol16> sym = createSymbol(d))
+        if (auto *dc = dyn_cast_or_null<DefinedCOFF>(d)) {
+          COFFSymbolRef symRef = dc->getCOFFSymbol();
+          if (symRef.isSectionDefinition() ||
+              symRef.getStorageClass() == COFF::IMAGE_SYM_CLASS_LABEL)
+            continue;
+        }
+
+        if (std::optional<coff_symbol16> sym = createSymbol(d))
           outputSymtab.push_back(*sym);
+
+        if (auto *dthunk = dyn_cast<DefinedImportThunk>(d)) {
+          if (!dthunk->wrappedSym->writtenToSymtab) {
+            dthunk->wrappedSym->writtenToSymtab = true;
+            if (std::optional<coff_symbol16> sym =
+                    createSymbol(dthunk->wrappedSym))
+              outputSymtab.push_back(*sym);
+          }
+        }
       }
     }
   }
@@ -1235,7 +1317,7 @@ void Writer::createSymbolAndStringTable() {
   pointerToSymbolTable = fileOff;
   fileOff += outputSymtab.size() * sizeof(coff_symbol16);
   fileOff += 4 + strtab.size();
-  fileSize = alignTo(fileOff, config->fileAlign);
+  fileSize = alignTo(fileOff, ctx.config.fileAlign);
 }
 
 void Writer::mergeSections() {
@@ -1244,16 +1326,16 @@ void Writer::mergeSections() {
     lastPdata = pdataSec->chunks.back();
   }
 
-  for (auto &p : config->merge) {
+  for (auto &p : ctx.config.merge) {
     StringRef toName = p.second;
     if (p.first == toName)
       continue;
     StringSet<> names;
-    while (1) {
+    while (true) {
       if (!names.insert(toName).second)
         fatal("/merge: cycle found for section '" + p.first + "'");
-      auto i = config->merge.find(toName);
-      if (i == config->merge.end())
+      auto i = ctx.config.merge.find(toName);
+      if (i == ctx.config.merge.end())
         break;
       toName = i->second;
     }
@@ -1272,9 +1354,11 @@ void Writer::mergeSections() {
 // Visits all sections to assign incremental, non-overlapping RVAs and
 // file offsets.
 void Writer::assignAddresses() {
+  Configuration *config = &ctx.config;
+
   sizeOfHeaders = dosStubSize + sizeof(PEMagic) + sizeof(coff_file_header) +
                   sizeof(data_directory) * numberOfDataDirectory +
-                  sizeof(coff_section) * outputSections.size();
+                  sizeof(coff_section) * ctx.outputSections.size();
   sizeOfHeaders +=
       config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header);
   sizeOfHeaders = alignTo(sizeOfHeaders, config->fileAlign);
@@ -1283,7 +1367,7 @@ void Writer::assignAddresses() {
   // The first page is kept unmapped.
   uint64_t rva = alignTo(sizeOfHeaders, config->align);
 
-  for (OutputSection *sec : outputSections) {
+  for (OutputSection *sec : ctx.outputSections) {
     if (sec == relocSec)
       addBaserels();
     uint64_t rawSize = 0, virtualSize = 0;
@@ -1318,7 +1402,7 @@ void Writer::assignAddresses() {
   sizeOfImage = alignTo(rva, config->align);
 
   // Assign addresses to sections in MergeChunks.
-  for (MergeChunk *mc : MergeChunk::instances)
+  for (MergeChunk *mc : ctx.mergeChunkInstances)
     if (mc)
       mc->assignSubsectionRVAs();
 }
@@ -1329,6 +1413,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
   // under DOS, that program gets run (usually to just print an error message).
   // When run under Windows, the loader looks at AddressOfNewExeHeader and uses
   // the PE header instead.
+  Configuration *config = &ctx.config;
   uint8_t *buf = buffer->getBufferStart();
   auto *dos = reinterpret_cast<dos_header *>(buf);
   buf += sizeof(dos_header);
@@ -1353,7 +1438,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
   auto *coff = reinterpret_cast<coff_file_header *>(buf);
   buf += sizeof(*coff);
   coff->Machine = config->machine;
-  coff->NumberOfSections = outputSections.size();
+  coff->NumberOfSections = ctx.outputSections.size();
   coff->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE;
   if (config->largeAddressAware)
     coff->Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE;
@@ -1466,7 +1551,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
     dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = relocSec->getRVA();
     dir[BASE_RELOCATION_TABLE].Size = relocSec->getVirtualSize();
   }
-  if (Symbol *sym = symtab->findUnderscore("_tls_used")) {
+  if (Symbol *sym = ctx.symtab.findUnderscore("_tls_used")) {
     if (Defined *b = dyn_cast<Defined>(sym)) {
       dir[TLS_TABLE].RelativeVirtualAddress = b->getRVA();
       dir[TLS_TABLE].Size = config->is64()
@@ -1478,7 +1563,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
     dir[DEBUG_DIRECTORY].RelativeVirtualAddress = debugDirectory->getRVA();
     dir[DEBUG_DIRECTORY].Size = debugDirectory->getSize();
   }
-  if (Symbol *sym = symtab->findUnderscore("_load_config_used")) {
+  if (Symbol *sym = ctx.symtab.findUnderscore("_load_config_used")) {
     if (auto *b = dyn_cast<DefinedRegular>(sym)) {
       SectionChunk *sc = b->getChunk();
       assert(b->getRVA() >= sc->getRVA());
@@ -1502,12 +1587,12 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
   }
 
   // Write section table
-  for (OutputSection *sec : outputSections) {
-    sec->writeHeaderTo(buf);
+  for (OutputSection *sec : ctx.outputSections) {
+    sec->writeHeaderTo(buf, config->debug);
     buf += sizeof(coff_section);
   }
   sectionTable = ArrayRef<uint8_t>(
-      buf - outputSections.size() * sizeof(coff_section), buf);
+      buf - ctx.outputSections.size() * sizeof(coff_section), buf);
 
   if (outputSymtab.empty() && strtab.empty())
     return;
@@ -1535,7 +1620,7 @@ void Writer::openFile(StringRef path) {
 
 void Writer::createSEHTable() {
   SymbolRVASet handlers;
-  for (ObjFile *file : ObjFile::instances) {
+  for (ObjFile *file : ctx.objFileInstances) {
     if (!file->hasSafeSEH())
       error("/safeseh: " + file->getName() + " is not compatible with SEH");
     markSymbolsForRVATable(file, file->getSXDataChunks(), handlers);
@@ -1544,7 +1629,7 @@ void Writer::createSEHTable() {
   // Set the "no SEH" characteristic if there really were no handlers, or if
   // there is no load config object to point to the table of handlers.
   setNoSEHCharacteristic =
-      handlers.empty() || !symtab->findUnderscore("_load_config_used");
+      handlers.empty() || !ctx.symtab.findUnderscore("_load_config_used");
 
   maybeAddRVATable(std::move(handlers), "__safe_se_handler_table",
                    "__safe_se_handler_count");
@@ -1612,8 +1697,8 @@ static void maybeAddAddressTakenFunction(SymbolRVASet &addressTakenSyms,
 
 // Visit all relocations from all section contributions of this object file and
 // mark the relocation target as address-taken.
-static void markSymbolsWithRelocations(ObjFile *file,
-                                       SymbolRVASet &usedSymbols) {
+void Writer::markSymbolsWithRelocations(ObjFile *file,
+                                        SymbolRVASet &usedSymbols) {
   for (Chunk *c : file->getChunks()) {
     // We only care about live section chunks. Common chunks and other chunks
     // don't generally contain relocations.
@@ -1622,7 +1707,8 @@ static void markSymbolsWithRelocations(ObjFile *file,
       continue;
 
     for (const coff_relocation &reloc : sc->getRelocs()) {
-      if (config->machine == I386 && reloc.Type == COFF::IMAGE_REL_I386_REL32)
+      if (ctx.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;
@@ -1637,12 +1723,14 @@ static void markSymbolsWithRelocations(ObjFile *file,
 // address-taken functions. It is sorted and uniqued, just like the safe SEH
 // table.
 void Writer::createGuardCFTables() {
+  Configuration *config = &ctx.config;
+
   SymbolRVASet addressTakenSyms;
   SymbolRVASet giatsRVASet;
   std::vector<Symbol *> giatsSymbols;
   SymbolRVASet longJmpTargets;
   SymbolRVASet ehContTargets;
-  for (ObjFile *file : ObjFile::instances) {
+  for (ObjFile *file : ctx.objFileInstances) {
     // If the object was compiled with /guard:cf, the address taken symbols
     // are in .gfids$y sections, the longjmp targets are in .gljmp$y sections,
     // and ehcont targets are in .gehcont$y sections. If the object was not
@@ -1702,13 +1790,13 @@ void Writer::createGuardCFTables() {
 
   // Set __guard_flags, which will be used in the load config to indicate that
   // /guard:cf was enabled.
-  uint32_t guardFlags = uint32_t(coff_guard_flags::CFInstrumented) |
-                        uint32_t(coff_guard_flags::HasFidTable);
+  uint32_t guardFlags = uint32_t(GuardFlags::CF_INSTRUMENTED) |
+                        uint32_t(GuardFlags::CF_FUNCTION_TABLE_PRESENT);
   if (config->guardCF & GuardCFLevel::LongJmp)
-    guardFlags |= uint32_t(coff_guard_flags::HasLongJmpTable);
+    guardFlags |= uint32_t(GuardFlags::CF_LONGJUMP_TABLE_PRESENT);
   if (config->guardCF & GuardCFLevel::EHCont)
-    guardFlags |= uint32_t(coff_guard_flags::HasEHContTable);
-  Symbol *flagSym = symtab->findUnderscore("__guard_flags");
+    guardFlags |= uint32_t(GuardFlags::EH_CONTINUATION_TABLE_PRESENT);
+  Symbol *flagSym = ctx.symtab.findUnderscore("__guard_flags");
   cast<DefinedAbsolute>(flagSym)->setVA(guardFlags);
 }
 
@@ -1780,8 +1868,8 @@ void Writer::maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym,
     tableChunk = make<RVATableChunk>(std::move(tableSymbols));
   rdataSec->addChunk(tableChunk);
 
-  Symbol *t = symtab->findUnderscore(tableSym);
-  Symbol *c = symtab->findUnderscore(countSym);
+  Symbol *t = ctx.symtab.findUnderscore(tableSym);
+  Symbol *c = ctx.symtab.findUnderscore(countSym);
   replaceSymbol<DefinedSynthetic>(t, t->getName(), tableChunk);
   cast<DefinedAbsolute>(c)->setVA(tableChunk->getSize() / (hasFlag ? 5 : 4));
 }
@@ -1793,14 +1881,14 @@ void Writer::maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym,
 void Writer::createRuntimePseudoRelocs() {
   std::vector<RuntimePseudoReloc> rels;
 
-  for (Chunk *c : symtab->getChunks()) {
+  for (Chunk *c : ctx.symtab.getChunks()) {
     auto *sc = dyn_cast<SectionChunk>(c);
     if (!sc || !sc->live)
       continue;
     sc->getRuntimePseudoRelocs(rels);
   }
 
-  if (!config->pseudoRelocs) {
+  if (!ctx.config.pseudoRelocs) {
     // Not writing any pseudo relocs; if some were needed, error out and
     // indicate what required them.
     for (const RuntimePseudoReloc &rpr : rels)
@@ -1816,8 +1904,9 @@ void Writer::createRuntimePseudoRelocs() {
   EmptyChunk *endOfList = make<EmptyChunk>();
   rdataSec->addChunk(endOfList);
 
-  Symbol *headSym = symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST__");
-  Symbol *endSym = symtab->findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST_END__");
+  Symbol *headSym = ctx.symtab.findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST__");
+  Symbol *endSym =
+      ctx.symtab.findUnderscore("__RUNTIME_PSEUDO_RELOC_LIST_END__");
   replaceSymbol<DefinedSynthetic>(headSym, headSym->getName(), table);
   replaceSymbol<DefinedSynthetic>(endSym, endSym->getName(), endOfList);
 }
@@ -1828,17 +1917,17 @@ void Writer::createRuntimePseudoRelocs() {
 // 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);
+  AbsolutePointerChunk *ctorListHead = make<AbsolutePointerChunk>(ctx, -1);
+  AbsolutePointerChunk *ctorListEnd = make<AbsolutePointerChunk>(ctx, 0);
+  AbsolutePointerChunk *dtorListHead = make<AbsolutePointerChunk>(ctx, -1);
+  AbsolutePointerChunk *dtorListEnd = make<AbsolutePointerChunk>(ctx, 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__");
+  Symbol *ctorListSym = ctx.symtab.findUnderscore("__CTOR_LIST__");
+  Symbol *dtorListSym = ctx.symtab.findUnderscore("__DTOR_LIST__");
   replaceSymbol<DefinedSynthetic>(ctorListSym, ctorListSym->getName(),
                                   ctorListHead);
   replaceSymbol<DefinedSynthetic>(dtorListSym, dtorListSym->getName(),
@@ -1848,10 +1937,10 @@ void Writer::insertCtorDtorSymbols() {
 // Handles /section options to allow users to overwrite
 // section attributes.
 void Writer::setSectionPermissions() {
-  for (auto &p : config->section) {
+  for (auto &p : ctx.config.section) {
     StringRef name = p.first;
     uint32_t perm = p.second;
-    for (OutputSection *sec : outputSections)
+    for (OutputSection *sec : ctx.outputSections)
       if (sec->name == name)
         sec->setPermissions(perm);
   }
@@ -1859,12 +1948,8 @@ void Writer::setSectionPermissions() {
 
 // Write section contents to a mmap'ed file.
 void Writer::writeSections() {
-  // Record the number of sections to apply section index relocations
-  // against absolute symbols. See applySecIdx in Chunks.cpp..
-  DefinedAbsolute::numOutputSections = outputSections.size();
-
   uint8_t *buf = buffer->getBufferStart();
-  for (OutputSection *sec : outputSections) {
+  for (OutputSection *sec : ctx.outputSections) {
     uint8_t *secBuf = buf + sec->getFileOff();
     // Fill gaps between functions in .text with INT3 instructions
     // instead of leaving as NUL bytes (which can be interpreted as
@@ -1884,6 +1969,8 @@ void Writer::writeBuildId() {
   // 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
   // PE contents.
+  Configuration *config = &ctx.config;
+
   if (config->debug) {
     assert(buildId && "BuildId is not set!");
     // BuildId->BuildId was filled in when the PDB was written.
@@ -1934,13 +2021,13 @@ void Writer::sortExceptionTable() {
     return;
   // We assume .pdata contains function table entries only.
   auto bufAddr = [&](Chunk *c) {
-    OutputSection *os = c->getOutputSection();
+    OutputSection *os = ctx.getOutputSection(c);
     return buffer->getBufferStart() + os->getFileOff() + c->getRVA() -
            os->getRVA();
   };
   uint8_t *begin = bufAddr(firstPdata);
   uint8_t *end = bufAddr(lastPdata) + lastPdata->getSize();
-  if (config->machine == AMD64) {
+  if (ctx.config.machine == AMD64) {
     struct Entry { ulittle32_t begin, end, unwind; };
     if ((end - begin) % sizeof(Entry) != 0) {
       fatal("unexpected .pdata size: " + Twine(end - begin) +
@@ -1951,7 +2038,7 @@ void Writer::sortExceptionTable() {
         [](const Entry &a, const Entry &b) { return a.begin < b.begin; });
     return;
   }
-  if (config->machine == ARMNT || config->machine == ARM64) {
+  if (ctx.config.machine == ARMNT || ctx.config.machine == ARM64) {
     struct Entry { ulittle32_t begin, unwind; };
     if ((end - begin) % sizeof(Entry) != 0) {
       fatal("unexpected .pdata size: " + Twine(end - begin) +
@@ -1992,7 +2079,7 @@ void Writer::sortCRTSectionChunks(std::vector<Chunk *> &chunks) {
   };
   llvm::stable_sort(chunks, sectionChunkOrder);
 
-  if (config->verbose) {
+  if (ctx.config.verbose) {
     for (auto &c : chunks) {
       auto sc = dyn_cast<SectionChunk>(c);
       log("  " + sc->file->mb.getBufferIdentifier().str() +
@@ -2002,7 +2089,7 @@ void Writer::sortCRTSectionChunks(std::vector<Chunk *> &chunks) {
 }
 
 OutputSection *Writer::findSection(StringRef name) {
-  for (OutputSection *sec : outputSections)
+  for (OutputSection *sec : ctx.outputSections)
     if (sec->name == name)
       return sec;
   return nullptr;
@@ -2010,7 +2097,7 @@ OutputSection *Writer::findSection(StringRef name) {
 
 uint32_t Writer::getSizeOfInitializedData() {
   uint32_t res = 0;
-  for (OutputSection *s : outputSections)
+  for (OutputSection *s : ctx.outputSections)
     if (s->header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
       res += s->getRawSize();
   return res;
@@ -2018,11 +2105,11 @@ uint32_t Writer::getSizeOfInitializedData() {
 
 // Add base relocations to .reloc section.
 void Writer::addBaserels() {
-  if (!config->relocatable)
+  if (!ctx.config.relocatable)
     return;
   relocSec->chunks.clear();
   std::vector<Baserel> v;
-  for (OutputSection *sec : outputSections) {
+  for (OutputSection *sec : ctx.outputSections) {
     if (sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
       continue;
     // Collect all locations for base relocations.
@@ -2071,24 +2158,24 @@ PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) {
 
 void Writer::fixTlsAlignment() {
   Defined *tlsSym =
-      dyn_cast_or_null<Defined>(symtab->findUnderscore("_tls_used"));
+      dyn_cast_or_null<Defined>(ctx.symtab.findUnderscore("_tls_used"));
   if (!tlsSym)
     return;
 
-  OutputSection *sec = tlsSym->getChunk()->getOutputSection();
+  OutputSection *sec = ctx.getOutputSection(tlsSym->getChunk());
   assert(sec && tlsSym->getRVA() >= sec->getRVA() &&
          "no output section for _tls_used");
 
   uint8_t *secBuf = buffer->getBufferStart() + sec->getFileOff();
   uint64_t tlsOffset = tlsSym->getRVA() - sec->getRVA();
-  uint64_t directorySize = config->is64()
+  uint64_t directorySize = ctx.config.is64()
                                ? sizeof(object::coff_tls_directory64)
                                : sizeof(object::coff_tls_directory32);
 
   if (tlsOffset + directorySize > sec->getRawSize())
     fatal("_tls_used sym is malformed");
 
-  if (config->is64()) {
+  if (ctx.config.is64()) {
     object::coff_tls_directory64 *tlsDir =
         reinterpret_cast<object::coff_tls_directory64 *>(&secBuf[tlsOffset]);
     tlsDir->setAlignment(tlsAlignment);
@@ -2098,3 +2185,86 @@ void Writer::fixTlsAlignment() {
     tlsDir->setAlignment(tlsAlignment);
   }
 }
+
+void Writer::checkLoadConfig() {
+  Symbol *sym = ctx.symtab.findUnderscore("_load_config_used");
+  auto *b = cast_if_present<DefinedRegular>(sym);
+  if (!b) {
+    if (ctx.config.guardCF != GuardCFLevel::Off)
+      warn("Control Flow Guard is enabled but '_load_config_used' is missing");
+    return;
+  }
+
+  OutputSection *sec = ctx.getOutputSection(b->getChunk());
+  uint8_t *buf = buffer->getBufferStart();
+  uint8_t *secBuf = buf + sec->getFileOff();
+  uint8_t *symBuf = secBuf + (b->getRVA() - sec->getRVA());
+  uint32_t expectedAlign = ctx.config.is64() ? 8 : 4;
+  if (b->getChunk()->getAlignment() < expectedAlign)
+    warn("'_load_config_used' is misaligned (expected alignment to be " +
+         Twine(expectedAlign) + " bytes, got " +
+         Twine(b->getChunk()->getAlignment()) + " instead)");
+  else if (!isAligned(Align(expectedAlign), b->getRVA()))
+    warn("'_load_config_used' is misaligned (RVA is 0x" +
+         Twine::utohexstr(b->getRVA()) + " not aligned to " +
+         Twine(expectedAlign) + " bytes)");
+
+  if (ctx.config.is64())
+    checkLoadConfigGuardData(
+        reinterpret_cast<const coff_load_configuration64 *>(symBuf));
+  else
+    checkLoadConfigGuardData(
+        reinterpret_cast<const coff_load_configuration32 *>(symBuf));
+}
+
+template <typename T>
+void Writer::checkLoadConfigGuardData(const T *loadConfig) {
+  size_t loadConfigSize = loadConfig->Size;
+
+#define RETURN_IF_NOT_CONTAINS(field)                                          \
+  if (loadConfigSize < offsetof(T, field) + sizeof(T::field)) {                \
+    warn("'_load_config_used' structure too small to include " #field);        \
+    return;                                                                    \
+  }
+
+#define IF_CONTAINS(field)                                                     \
+  if (loadConfigSize >= offsetof(T, field) + sizeof(T::field))
+
+#define CHECK_VA(field, sym)                                                   \
+  if (auto *s = dyn_cast<DefinedSynthetic>(ctx.symtab.findUnderscore(sym)))    \
+    if (loadConfig->field != ctx.config.imageBase + s->getRVA())               \
+      warn(#field " not set correctly in '_load_config_used'");
+
+#define CHECK_ABSOLUTE(field, sym)                                             \
+  if (auto *s = dyn_cast<DefinedAbsolute>(ctx.symtab.findUnderscore(sym)))     \
+    if (loadConfig->field != s->getVA())                                       \
+      warn(#field " not set correctly in '_load_config_used'");
+
+  if (ctx.config.guardCF == GuardCFLevel::Off)
+    return;
+  RETURN_IF_NOT_CONTAINS(GuardFlags)
+  CHECK_VA(GuardCFFunctionTable, "__guard_fids_table")
+  CHECK_ABSOLUTE(GuardCFFunctionCount, "__guard_fids_count")
+  CHECK_ABSOLUTE(GuardFlags, "__guard_flags")
+  IF_CONTAINS(GuardAddressTakenIatEntryCount) {
+    CHECK_VA(GuardAddressTakenIatEntryTable, "__guard_iat_table")
+    CHECK_ABSOLUTE(GuardAddressTakenIatEntryCount, "__guard_iat_count")
+  }
+
+  if (!(ctx.config.guardCF & GuardCFLevel::LongJmp))
+    return;
+  RETURN_IF_NOT_CONTAINS(GuardLongJumpTargetCount)
+  CHECK_VA(GuardLongJumpTargetTable, "__guard_longjmp_table")
+  CHECK_ABSOLUTE(GuardLongJumpTargetCount, "__guard_longjmp_count")
+
+  if (!(ctx.config.guardCF & GuardCFLevel::EHCont))
+    return;
+  RETURN_IF_NOT_CONTAINS(GuardEHContinuationCount)
+  CHECK_VA(GuardEHContinuationTable, "__guard_eh_cont_table")
+  CHECK_ABSOLUTE(GuardEHContinuationCount, "__guard_eh_cont_count")
+
+#undef RETURN_IF_NOT_CONTAINS
+#undef IF_CONTAINS
+#undef CHECK_VA
+#undef CHECK_ABSOLUTE
+}
index 2bb26da..4a74aa7 100644 (file)
 #include <cstdint>
 #include <vector>
 
-namespace lld {
-namespace coff {
+namespace lld::coff {
 static const int pageSize = 4096;
+class COFFLinkerContext;
 
-void writeResult();
+void writeResult(COFFLinkerContext &ctx);
 
 class PartialSection {
 public:
@@ -45,14 +45,11 @@ public:
   void insertChunkAtStart(Chunk *c);
   void merge(OutputSection *other);
   void setPermissions(uint32_t c);
-  uint64_t getRVA() { return header.VirtualAddress; }
-  uint64_t getFileOff() { return header.PointerToRawData; }
-  void writeHeaderTo(uint8_t *buf);
+  uint64_t getRVA() const { return header.VirtualAddress; }
+  uint64_t getFileOff() const { return header.PointerToRawData; }
+  void writeHeaderTo(uint8_t *buf, bool isDebug);
   void addContributingPartialSection(PartialSection *sec);
 
-  // Clear the output sections static container.
-  static void clear();
-
   // Returns the size of this section in an executable memory image.
   // This may be smaller than the raw size (the raw size is multiple
   // of disk sector size, so there may be padding at end), or may be
@@ -82,7 +79,6 @@ private:
   uint32_t stringTableOff = 0;
 };
 
-} // namespace coff
-} // namespace lld
+} // namespace lld::coff
 
 #endif
index fe7300d..388c15b 100644 (file)
@@ -54,8 +54,9 @@ int64_t lld::args::getHex(opt::InputArgList &args, unsigned key,
   return ::getInteger(args, key, Default, 16);
 }
 
-std::vector<StringRef> lld::args::getStrings(opt::InputArgList &args, int id) {
-  std::vector<StringRef> v;
+SmallVector<StringRef, 0> lld::args::getStrings(opt::InputArgList &args,
+                                                int id) {
+  SmallVector<StringRef, 0> v;
   for (auto *arg : args.filtered(id))
     v.push_back(arg->getValue());
   return v;
index 0437d5a..71df89c 100644 (file)
@@ -1,14 +1,8 @@
-set(LLD_SYSTEM_LIBS ${LLVM_PTHREAD_LIB})
-
-if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB)
-  list(APPEND LLD_SYSTEM_LIBS atomic)
-endif()
-
 find_first_existing_vc_file("${LLVM_MAIN_SRC_DIR}" llvm_vc)
 find_first_existing_vc_file("${LLD_SOURCE_DIR}" lld_vc)
 
 set(version_inc "${CMAKE_CURRENT_BINARY_DIR}/VCSVersion.inc")
-set(generate_vcs_version_script "${LLVM_CMAKE_PATH}/GenerateVersionFromVCS.cmake")
+set(generate_vcs_version_script "${LLVM_CMAKE_DIR}/GenerateVersionFromVCS.cmake")
 
 if(lld_vc AND LLVM_APPEND_VC_REV)
   set(lld_source_dir ${LLD_SOURCE_DIR})
@@ -28,6 +22,7 @@ set_source_files_properties("${version_inc}"
 
 add_lld_library(lldCommon
   Args.cpp
+  CommonLinkerContext.cpp
   DWARF.cpp
   ErrorHandler.cpp
   Filesystem.cpp
@@ -51,9 +46,11 @@ add_lld_library(lldCommon
   Option
   Support
   Target
+  TargetParser
 
   LINK_LIBS
-  ${LLD_SYSTEM_LIBS}
+  ${LLVM_PTHREAD_LIB}
+  ${LLVM_ATOMIC_LIB}
 
   DEPENDS
   intrinsics_gen
diff --git a/gnu/llvm/lld/Common/CommonLinkerContext.cpp b/gnu/llvm/lld/Common/CommonLinkerContext.cpp
new file mode 100644 (file)
index 0000000..12f56bc
--- /dev/null
@@ -0,0 +1,51 @@
+//===- CommonLinkerContext.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lld/Common/CommonLinkerContext.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+
+#include "llvm/CodeGen/CommandFlags.h"
+
+using namespace llvm;
+using namespace lld;
+
+// Reference to the current LLD instance. This is a temporary situation, until
+// we pass this context everywhere by reference, or we make it a thread_local,
+// as in https://reviews.llvm.org/D108850?id=370678 where each thread can be
+// associated with a LLD instance. Only then will LLD be free of global
+// state.
+static CommonLinkerContext *lctx;
+
+CommonLinkerContext::CommonLinkerContext() {
+  lctx = this;
+  // Fire off the static initializations in CGF's constructor.
+  codegen::RegisterCodeGenFlags CGF;
+}
+
+CommonLinkerContext::~CommonLinkerContext() {
+  assert(lctx);
+  // Explicitly call the destructors since we created the objects with placement
+  // new in SpecificAlloc::create().
+  for (auto &it : instances)
+    it.second->~SpecificAllocBase();
+  lctx = nullptr;
+}
+
+CommonLinkerContext &lld::commonContext() {
+  assert(lctx);
+  return *lctx;
+}
+
+bool lld::hasContext() { return lctx != nullptr; }
+
+void CommonLinkerContext::destroy() {
+  if (lctx == nullptr)
+    return;
+  delete lctx;
+}
index 077adbc..2cd8ca4 100644 (file)
@@ -69,27 +69,27 @@ DWARFCache::DWARFCache(std::unique_ptr<llvm::DWARFContext> d)
 
 // Returns the pair of file name and line number describing location of data
 // object (variable, array, etc) definition.
-Optional<std::pair<std::string, unsigned>>
+std::optional<std::pair<std::string, unsigned>>
 DWARFCache::getVariableLoc(StringRef name) {
   // Return if we have no debug information about data object.
   auto it = variableLoc.find(name);
   if (it == variableLoc.end())
-    return None;
+    return std::nullopt;
 
   // Take file name string from line table.
   std::string fileName;
   if (!it->second.lt->getFileNameByIndex(
           it->second.file, {},
           DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, fileName))
-    return None;
+    return std::nullopt;
 
   return std::make_pair(fileName, it->second.line);
 }
 
 // Returns source line information for a given offset
 // using DWARF debug info.
-Optional<DILineInfo> DWARFCache::getDILineInfo(uint64_t offset,
-                                               uint64_t sectionIndex) {
+std::optional<DILineInfo> DWARFCache::getDILineInfo(uint64_t offset,
+                                                    uint64_t sectionIndex) {
   DILineInfo info;
   for (const llvm::DWARFDebugLine::LineTable *lt : lineTables) {
     if (lt->getFileLineInfoForAddress(
@@ -97,7 +97,7 @@ Optional<DILineInfo> DWARFCache::getDILineInfo(uint64_t offset,
             DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, info))
       return info;
   }
-  return None;
+  return std::nullopt;
 }
 
 } // namespace lld
index 269a0f6..0a8fc8f 100644 (file)
@@ -10,6 +10,7 @@
 
 #include "llvm/Support/Parallel.h"
 
+#include "lld/Common/CommonLinkerContext.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/IR/DiagnosticInfo.h"
 #include "llvm/IR/DiagnosticPrinter.h"
 #include "llvm/Support/Process.h"
 #include "llvm/Support/Program.h"
 #include "llvm/Support/raw_ostream.h"
-#include <mutex>
 #include <regex>
 
 using namespace llvm;
 using namespace lld;
 
-// The functions defined in this file can be called from multiple threads,
-// but lld::outs() or lld::errs() are not thread-safe. We protect them using a
-// mutex.
-static std::mutex mu;
-
-// We want to separate multi-line messages with a newline. `sep` is "\n"
-// if the last messages was multi-line. Otherwise "".
-static StringRef sep;
-
 static StringRef getSeparator(const Twine &msg) {
   if (StringRef(msg.str()).contains('\n'))
     return "\n";
   return "";
 }
 
-raw_ostream *lld::stdoutOS;
-raw_ostream *lld::stderrOS;
+ErrorHandler::~ErrorHandler() {
+  if (cleanupCallback)
+    cleanupCallback();
+}
+
+void ErrorHandler::initialize(llvm::raw_ostream &stdoutOS,
+                              llvm::raw_ostream &stderrOS, bool exitEarly,
+                              bool disableOutput) {
+  this->stdoutOS = &stdoutOS;
+  this->stderrOS = &stderrOS;
+  stderrOS.enable_colors(stderrOS.has_colors());
+  this->exitEarly = exitEarly;
+  this->disableOutput = disableOutput;
+}
+
+void ErrorHandler::flushStreams() {
+  std::lock_guard<std::mutex> lock(mu);
+  outs().flush();
+  errs().flush();
+}
+
+ErrorHandler &lld::errorHandler() { return context().e; }
 
-ErrorHandler &lld::errorHandler() {
-  static ErrorHandler handler;
-  return handler;
+void lld::error(const Twine &msg) { errorHandler().error(msg); }
+void lld::error(const Twine &msg, ErrorTag tag, ArrayRef<StringRef> args) {
+  errorHandler().error(msg, tag, args);
+}
+void lld::fatal(const Twine &msg) { errorHandler().fatal(msg); }
+void lld::log(const Twine &msg) { errorHandler().log(msg); }
+void lld::message(const Twine &msg, llvm::raw_ostream &s) {
+  errorHandler().message(msg, s);
 }
+void lld::warn(const Twine &msg) { errorHandler().warn(msg); }
+uint64_t lld::errorCount() { return errorHandler().errorCount; }
 
 raw_ostream &lld::outs() {
-  if (errorHandler().disableOutput)
+  ErrorHandler &e = errorHandler();
+  return e.outs();
+}
+
+raw_ostream &lld::errs() {
+  ErrorHandler &e = errorHandler();
+  return e.errs();
+}
+
+raw_ostream &ErrorHandler::outs() {
+  if (disableOutput)
     return llvm::nulls();
   return stdoutOS ? *stdoutOS : llvm::outs();
 }
 
-raw_ostream &lld::errs() {
-  if (errorHandler().disableOutput)
+raw_ostream &ErrorHandler::errs() {
+  if (disableOutput)
     return llvm::nulls();
   return stderrOS ? *stderrOS : llvm::errs();
 }
 
 void lld::exitLld(int val) {
-  // Delete any temporary file, while keeping the memory mapping open.
-  if (errorHandler().outputBuffer)
-    errorHandler().outputBuffer->discard();
+  if (hasContext()) {
+    ErrorHandler &e = errorHandler();
+    // Delete any temporary file, while keeping the memory mapping open.
+    if (e.outputBuffer)
+      e.outputBuffer->discard();
+  }
 
   // Re-throw a possible signal or exception once/if it was catched by
   // safeLldMain().
@@ -75,11 +106,9 @@ void lld::exitLld(int val) {
   if (!CrashRecoveryContext::GetCurrent())
     llvm_shutdown();
 
-  {
-    std::lock_guard<std::mutex> lock(mu);
-    lld::outs().flush();
-    lld::errs().flush();
-  }
+  if (hasContext())
+    lld::errorHandler().flushStreams();
+
   // When running inside safeLldMain(), restore the control flow back to the
   // CrashRecoveryContext. Otherwise simply use _exit(), meanning no cleanup,
   // since we want to avoid further crashes on shutdown.
@@ -90,6 +119,13 @@ void lld::diagnosticHandler(const DiagnosticInfo &di) {
   SmallString<128> s;
   raw_svector_ostream os(s);
   DiagnosticPrinterRawOStream dp(os);
+
+  // For an inline asm diagnostic, prepend the module name to get something like
+  // "$module <inline asm>:1:5: ".
+  if (auto *dism = dyn_cast<DiagnosticInfoSrcMgr>(&di))
+    if (dism->isInlineAsmDiag())
+      os << dism->getModuleName() << ' ';
+
   di.print(dp);
   switch (di.getSeverity()) {
   case DS_Error:
@@ -168,19 +204,36 @@ std::string ErrorHandler::getLocation(const Twine &msg) {
   return std::string(logName);
 }
 
+void ErrorHandler::reportDiagnostic(StringRef location, Colors c,
+                                    StringRef diagKind, const Twine &msg) {
+  SmallString<256> buf;
+  raw_svector_ostream os(buf);
+  os << sep << location << ": ";
+  if (!diagKind.empty()) {
+    if (lld::errs().colors_enabled()) {
+      os.enable_colors(true);
+      os << c << diagKind << ": " << Colors::RESET;
+    } else {
+      os << diagKind << ": ";
+    }
+  }
+  os << msg << '\n';
+  lld::errs() << buf;
+}
+
 void ErrorHandler::log(const Twine &msg) {
   if (!verbose || disableOutput)
     return;
   std::lock_guard<std::mutex> lock(mu);
-  lld::errs() << logName << ": " << msg << "\n";
+  reportDiagnostic(logName, Colors::RESET, "", msg);
 }
 
-void ErrorHandler::message(const Twine &msg) {
+void ErrorHandler::message(const Twine &msg, llvm::raw_ostream &s) {
   if (disableOutput)
     return;
   std::lock_guard<std::mutex> lock(mu);
-  lld::outs() << msg << "\n";
-  lld::outs().flush();
+  s << msg << "\n";
+  s.flush();
 }
 
 void ErrorHandler::warn(const Twine &msg) {
@@ -189,9 +242,11 @@ void ErrorHandler::warn(const Twine &msg) {
     return;
   }
 
+  if (suppressWarnings)
+    return;
+
   std::lock_guard<std::mutex> lock(mu);
-  lld::errs() << sep << getLocation(msg) << ": " << Colors::MAGENTA
-              << "warning: " << Colors::RESET << msg << "\n";
+  reportDiagnostic(getLocation(msg), Colors::MAGENTA, "warning", msg);
   sep = getSeparator(msg);
 }
 
@@ -217,12 +272,9 @@ void ErrorHandler::error(const Twine &msg) {
     std::lock_guard<std::mutex> lock(mu);
 
     if (errorLimit == 0 || errorCount < errorLimit) {
-      lld::errs() << sep << getLocation(msg) << ": " << Colors::RED
-                  << "error: " << Colors::RESET << msg << "\n";
+      reportDiagnostic(getLocation(msg), Colors::RED, "error", msg);
     } else if (errorCount == errorLimit) {
-      lld::errs() << sep << getLocation(msg) << ": " << Colors::RED
-                  << "error: " << Colors::RESET << errorLimitExceededMsg
-                  << "\n";
+      reportDiagnostic(logName, Colors::RED, "error", errorLimitExceededMsg);
       exit = exitEarly;
     }
 
index c53e1d3..7c90ff1 100644 (file)
@@ -7,16 +7,19 @@
 //===----------------------------------------------------------------------===//
 
 #include "lld/Common/Memory.h"
+#include "lld/Common/CommonLinkerContext.h"
 
 using namespace llvm;
 using namespace lld;
 
-BumpPtrAllocator lld::bAlloc;
-StringSaver lld::saver{bAlloc};
-std::vector<SpecificAllocBase *> lld::SpecificAllocBase::instances;
-
-void lld::freeArena() {
-  for (SpecificAllocBase *alloc : SpecificAllocBase::instances)
-    alloc->reset();
-  bAlloc.Reset();
+SpecificAllocBase *
+lld::SpecificAllocBase::getOrCreate(void *tag, size_t size, size_t align,
+                                    SpecificAllocBase *(&creator)(void *)) {
+  auto &instances = context().instances;
+  auto &instance = instances[tag];
+  if (instance == nullptr) {
+    void *storage = context().bAlloc.Allocate(size, align);
+    instance = creator(storage);
+  }
+  return instance;
 }
index 7bf3364..31397b7 100644 (file)
@@ -9,7 +9,6 @@
 #include "lld/Common/Strings.h"
 #include "lld/Common/ErrorHandler.h"
 #include "lld/Common/LLVM.h"
-#include "llvm/Demangle/Demangle.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/GlobPattern.h"
 #include <algorithm>
 using namespace llvm;
 using namespace lld;
 
-// Returns the demangled C++ symbol name for name.
-std::string lld::demangleItanium(StringRef name) {
-  // demangleItanium() can be called for all symbols. Only demangle C++ symbols,
-  // to avoid getting unexpected result for a C symbol that happens to match a
-  // mangled type name such as "Pi" (which would demangle to "int*").
-  if (!name.startswith("_Z") && !name.startswith("__Z") &&
-      !name.startswith("___Z") && !name.startswith("____Z"))
-    return std::string(name);
-
-  return demangle(std::string(name));
-}
-
 SingleStringMatcher::SingleStringMatcher(StringRef Pattern) {
   if (Pattern.size() > 2 && Pattern.startswith("\"") &&
       Pattern.endswith("\"")) {
@@ -59,8 +46,8 @@ bool StringMatcher::match(StringRef s) const {
 }
 
 // Converts a hex string (e.g. "deadbeef") to a vector.
-std::vector<uint8_t> lld::parseHex(StringRef s) {
-  std::vector<uint8_t> hex;
+SmallVector<uint8_t, 0> lld::parseHex(StringRef s) {
+  SmallVector<uint8_t, 0> hex;
   while (!s.empty()) {
     StringRef b = s.substr(0, 2);
     s = s.substr(2);
index d39477e..0efe679 100644 (file)
@@ -7,21 +7,20 @@
 //===----------------------------------------------------------------------===//
 
 #include "lld/Common/TargetOptionsCommandFlags.h"
-
+#include "llvm/ADT/Triple.h"
 #include "llvm/CodeGen/CommandFlags.h"
 #include "llvm/Target/TargetOptions.h"
-
-static llvm::codegen::RegisterCodeGenFlags CGF;
+#include <optional>
 
 llvm::TargetOptions lld::initTargetOptionsFromCodeGenFlags() {
   return llvm::codegen::InitTargetOptionsFromCodeGenFlags(llvm::Triple());
 }
 
-llvm::Optional<llvm::Reloc::Model> lld::getRelocModelFromCMModel() {
+std::optional<llvm::Reloc::Model> lld::getRelocModelFromCMModel() {
   return llvm::codegen::getExplicitRelocModel();
 }
 
-llvm::Optional<llvm::CodeModel::Model> lld::getCodeModelFromCMModel() {
+std::optional<llvm::CodeModel::Model> lld::getCodeModelFromCMModel() {
   return llvm::codegen::getExplicitCodeModel();
 }
 
index 16c518e..29838c9 100644 (file)
@@ -9,6 +9,7 @@
 #include "lld/Common/Timer.h"
 #include "lld/Common/ErrorHandler.h"
 #include "llvm/Support/Format.h"
+#include <ratio>
 
 using namespace lld;
 using namespace llvm;
@@ -26,18 +27,14 @@ void ScopedTimer::stop() {
 
 ScopedTimer::~ScopedTimer() { stop(); }
 
-Timer::Timer(llvm::StringRef name) : name(std::string(name)) {}
-Timer::Timer(llvm::StringRef name, Timer &parent) : name(std::string(name)) {
+Timer::Timer(llvm::StringRef name) : total(0), name(std::string(name)) {}
+Timer::Timer(llvm::StringRef name, Timer &parent)
+    : total(0), name(std::string(name)) {
   parent.children.push_back(this);
 }
 
-Timer &Timer::root() {
-  static Timer rootTimer("Total Link Time");
-  return rootTimer;
-}
-
 void Timer::print() {
-  double totalDuration = static_cast<double>(root().millis());
+  double totalDuration = static_cast<double>(millis());
 
   // We want to print the grand total under all the intermediate phases, so we
   // print all children first, then print the total under that.
@@ -47,7 +44,7 @@ void Timer::print() {
 
   message(std::string(50, '-'));
 
-  root().print(0, root().millis(), false);
+  print(0, millis(), false);
 }
 
 double Timer::millis() const {
index f376809..ec6eda6 100644 (file)
 #include "VCSVersion.inc"
 
 // Returns a version string, e.g.:
-// lld 9.0.0 (https://github.com/llvm/llvm-project.git 9efdd7ac5e914d3c9fa1ef)
+// LLD 14.0.0 (https://github.com/llvm/llvm-project.git
+// 2d9759c7902c5cbc9a7e3ab623321d5578d51687)
 std::string lld::getLLDVersion() {
 #ifdef LLD_VENDOR
 #define LLD_VENDOR_DISPLAY LLD_VENDOR " "
 #else
 #define LLD_VENDOR_DISPLAY
 #endif
-#if defined(LLD_REPOSITORY) && defined(LLD_REVISION)
-  return LLD_VENDOR_DISPLAY "LLD " LLD_VERSION_STRING " (" LLD_REPOSITORY
-                            " " LLD_REVISION ")";
-#else
   return LLD_VENDOR_DISPLAY "LLD " LLD_VERSION_STRING;
-#endif
 #undef LLD_VENDOR_DISPLAY
 }
index b9fd4cd..6ec7e33 100644 (file)
 //===----------------------------------------------------------------------===//
 
 #include "AArch64ErrataFix.h"
-#include "Config.h"
+#include "InputFiles.h"
 #include "LinkerScript.h"
 #include "OutputSections.h"
 #include "Relocations.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
-#include "lld/Common/Memory.h"
+#include "lld/Common/CommonLinkerContext.h"
 #include "lld/Common/Strings.h"
 #include "llvm/Support/Endian.h"
-#include "llvm/Support/raw_ostream.h"
 #include <algorithm>
 
 using namespace llvm;
@@ -56,7 +55,7 @@ static bool isADRP(uint32_t instr) {
   return (instr & 0x9f000000) == 0x90000000;
 }
 
-// Load and store bit patterns from ARMv8-A ARM ARM.
+// Load and store bit patterns from ARMv8-A.
 // Instructions appear in order of appearance starting from table in
 // C4.1.3 Loads and Stores.
 
@@ -351,7 +350,7 @@ static uint64_t scanCortexA53Errata843419(InputSection *isec, uint64_t &off,
   }
 
   uint64_t patchOff = 0;
-  const uint8_t *buf = isec->data().begin();
+  const uint8_t *buf = isec->content().begin();
   const ulittle32_t *instBuf = reinterpret_cast<const ulittle32_t *>(buf + off);
   uint32_t instr1 = *instBuf++;
   uint32_t instr2 = *instBuf++;
@@ -370,7 +369,7 @@ static uint64_t scanCortexA53Errata843419(InputSection *isec, uint64_t &off,
   return patchOff;
 }
 
-class elf::Patch843419Section : public SyntheticSection {
+class elf::Patch843419Section final : public SyntheticSection {
 public:
   Patch843419Section(InputSection *p, uint64_t off);
 
@@ -398,9 +397,9 @@ Patch843419Section::Patch843419Section(InputSection *p, uint64_t off)
       patchee(p), patcheeOffset(off) {
   this->parent = p->getParent();
   patchSym = addSyntheticLocal(
-      saver.save("__CortexA53843419_" + utohexstr(getLDSTAddr())), STT_FUNC, 0,
-      getSize(), *this);
-  addSyntheticLocal(saver.save("$x"), STT_NOTYPE, 0, 0, *this);
+      saver().save("__CortexA53843419_" + utohexstr(getLDSTAddr())), STT_FUNC,
+      0, getSize(), *this);
+  addSyntheticLocal(saver().save("$x"), STT_NOTYPE, 0, 0, *this);
 }
 
 uint64_t Patch843419Section::getLDSTAddr() const {
@@ -410,10 +409,10 @@ uint64_t Patch843419Section::getLDSTAddr() const {
 void 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->content().begin() + patcheeOffset));
 
   // Apply any relocation transferred from the original patchee section.
-  relocateAlloc(buf, buf + getSize());
+  target->relocateAlloc(*this, buf);
 
   // Return address is the next instruction after the one we have just copied.
   uint64_t s = getLDSTAddr() + 4;
@@ -440,9 +439,8 @@ void AArch64Err843419Patcher::init() {
   };
 
   // Collect mapping symbols for every executable InputSection.
-  for (InputFile *file : objectFiles) {
-    auto *f = cast<ObjFile<ELF64LE>>(file);
-    for (Symbol *b : f->getLocalSymbols()) {
+  for (ELFFileBase *file : ctx.objectFiles) {
+    for (Symbol *b : file->getLocalSymbols()) {
       auto *def = dyn_cast<Defined>(b);
       if (!def)
         continue;
@@ -513,7 +511,7 @@ void AArch64Err843419Patcher::insertPatches(
   // determine the insertion point. This is ok as we only merge into an
   // InputSectionDescription once per pass, and at the end of the pass
   // assignAddresses() will recalculate all the outSecOff values.
-  std::vector<InputSection *> tmp;
+  SmallVector<InputSection *, 0> tmp;
   tmp.reserve(isd.sections.size() + patches.size());
   auto mergeCmp = [](const InputSection *a, const InputSection *b) {
     if (a->outSecOff != b->outSecOff)
@@ -546,10 +544,10 @@ static void implementPatch(uint64_t adrpAddr, uint64_t patcheeOffset,
   // and replace the relocation with a R_AARCH_JUMP26 branch relocation.
   // Case 4: No relocation. We must create a new R_AARCH64_JUMP26 branch
   // relocation at the offset.
-  auto relIt = llvm::find_if(isec->relocations, [=](const Relocation &r) {
+  auto relIt = llvm::find_if(isec->relocs(), [=](const Relocation &r) {
     return r.offset == patcheeOffset;
   });
-  if (relIt != isec->relocations.end() &&
+  if (relIt != isec->relocs().end() &&
       (relIt->type == R_AARCH64_JUMP26 || relIt->expr == R_RELAX_TLS_IE_TO_LE))
     return;
 
@@ -563,12 +561,11 @@ static void implementPatch(uint64_t adrpAddr, uint64_t patcheeOffset,
     return Relocation{R_PC, R_AARCH64_JUMP26, offset, 0, patchSym};
   };
 
-  if (relIt != isec->relocations.end()) {
-    ps->relocations.push_back(
-        {relIt->expr, relIt->type, 0, relIt->addend, relIt->sym});
+  if (relIt != isec->relocs().end()) {
+    ps->addReloc({relIt->expr, relIt->type, 0, relIt->addend, relIt->sym});
     *relIt = makeRelToPatch(patcheeOffset, ps->patchSym);
   } else
-    isec->relocations.push_back(makeRelToPatch(patcheeOffset, ps->patchSym));
+    isec->addReloc(makeRelToPatch(patcheeOffset, ps->patchSym));
 }
 
 // Scan all the instructions in InputSectionDescription, for each instance of
@@ -593,8 +590,8 @@ AArch64Err843419Patcher::patchInputSectionDescription(
     while (codeSym != mapSyms.end()) {
       auto dataSym = std::next(codeSym);
       uint64_t off = (*codeSym)->value;
-      uint64_t limit =
-          (dataSym == mapSyms.end()) ? isec->data().size() : (*dataSym)->value;
+      uint64_t limit = (dataSym == mapSyms.end()) ? isec->content().size()
+                                                  : (*dataSym)->value;
 
       while (off < limit) {
         uint64_t startAddr = isec->getVA(off);
@@ -630,8 +627,8 @@ bool AArch64Err843419Patcher::createFixes() {
   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)) {
+    for (SectionCommand *cmd : os->commands)
+      if (auto *isd = dyn_cast<InputSectionDescription>(cmd)) {
         std::vector<Patch843419Section *> patches =
             patchInputSectionDescription(*isd);
         if (!patches.empty()) {
index dfe57b9..fa34beb 100644 (file)
 #define LLD_ELF_AARCH64ERRATAFIX_H
 
 #include "lld/Common/LLVM.h"
-#include <map>
+#include "llvm/ADT/DenseMap.h"
 #include <vector>
 
-namespace lld {
-namespace elf {
+namespace lld::elf {
 
 class Defined;
 class InputSection;
 class InputSectionDescription;
-class OutputSection;
 class Patch843419Section;
 
 class AArch64Err843419Patcher {
@@ -39,12 +37,11 @@ private:
   // A cache of the mapping symbols defined by the InputSection sorted in order
   // of ascending value with redundant symbols removed. These describe
   // the ranges of code and data in an executable InputSection.
-  std::map<InputSection *, std::vector<const Defined *>> sectionMap;
+  llvm::DenseMap<InputSection *, std::vector<const Defined *>> sectionMap;
 
   bool initialized = false;
 };
 
-} // namespace elf
-} // namespace lld
+} // namespace lld::elf
 
 #endif
index 7762378..9fbff86 100644 (file)
 //===----------------------------------------------------------------------===//
 
 #include "ARMErrataFix.h"
-
-#include "Config.h"
+#include "InputFiles.h"
 #include "LinkerScript.h"
 #include "OutputSections.h"
 #include "Relocations.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
-#include "lld/Common/Memory.h"
+#include "lld/Common/CommonLinkerContext.h"
 #include "lld/Common/Strings.h"
 #include "llvm/Support/Endian.h"
-#include "llvm/Support/raw_ostream.h"
 #include <algorithm>
 
 using namespace llvm;
@@ -70,7 +68,7 @@ using namespace lld::elf;
 // 00001002       2 - bytes padding
 // 00001004 __CortexA8657417_00000FFE: B.w func
 
-class elf::Patch657417Section : public SyntheticSection {
+class elf::Patch657417Section final : public SyntheticSection {
 public:
   Patch657417Section(InputSection *p, uint64_t off, uint32_t instr, bool isARM);
 
@@ -142,9 +140,9 @@ Patch657417Section::Patch657417Section(InputSection *p, uint64_t off,
       patchee(p), patcheeOffset(off), instr(instr), isARM(isARM) {
   parent = p->getParent();
   patchSym = addSyntheticLocal(
-      saver.save("__CortexA8657417_" + utohexstr(getBranchAddr())), STT_FUNC,
+      saver().save("__CortexA8657417_" + utohexstr(getBranchAddr())), STT_FUNC,
       isARM ? 0 : 1, getSize(), *this);
-  addSyntheticLocal(saver.save(isARM ? "$a" : "$t"), STT_NOTYPE, 0, 0, *this);
+  addSyntheticLocal(saver().save(isARM ? "$a" : "$t"), STT_NOTYPE, 0, 0, *this);
 }
 
 uint64_t Patch657417Section::getBranchAddr() const {
@@ -183,8 +181,8 @@ void Patch657417Section::writeTo(uint8_t *buf) {
   else
     write32le(buf, 0x9000f000);
   // If we have a relocation then apply it.
-  if (!relocations.empty()) {
-    relocateAlloc(buf, buf + getSize());
+  if (!relocs().empty()) {
+    target->relocateAlloc(*this, buf);
     return;
   }
 
@@ -209,7 +207,7 @@ static bool branchDestInFirstRegion(const InputSection *isec, uint64_t off,
                                     uint32_t instr, const Relocation *r) {
   uint64_t sourceAddr = isec->getVA(0) + off;
   assert((sourceAddr & 0xfff) == 0xffe);
-  uint64_t destAddr = sourceAddr;
+  uint64_t destAddr;
   // If there is a branch relocation at the same offset we must use this to
   // find the destination address as the branch could be indirected via a thunk
   // or the PLT.
@@ -268,7 +266,7 @@ static ScanResult scanCortexA8Errata657417(InputSection *isec, uint64_t &off,
   }
 
   ScanResult scanRes = {0, 0, nullptr};
-  const uint8_t *buf = isec->data().begin();
+  const uint8_t *buf = isec->content().begin();
   // ARMv7-A Thumb 32-bit instructions are encoded 2 consecutive
   // little-endian halfwords.
   const ulittle16_t *instBuf = reinterpret_cast<const ulittle16_t *>(buf + off);
@@ -283,12 +281,12 @@ static ScanResult scanCortexA8Errata657417(InputSection *isec, uint64_t &off,
       // Find a relocation for the branch if it exists. This will be used
       // to determine the target.
       uint64_t branchOff = off + 4;
-      auto relIt = llvm::find_if(isec->relocations, [=](const Relocation &r) {
+      auto relIt = llvm::find_if(isec->relocs(), [=](const Relocation &r) {
         return r.offset == branchOff &&
                (r.type == R_ARM_THM_JUMP19 || r.type == R_ARM_THM_JUMP24 ||
                 r.type == R_ARM_THM_CALL);
       });
-      if (relIt != isec->relocations.end())
+      if (relIt != isec->relocs().end())
         scanRes.rel = &(*relIt);
       if (branchDestInFirstRegion(isec, branchOff, instr2, scanRes.rel)) {
         if (patchInRange(isec, branchOff, instr2)) {
@@ -329,9 +327,8 @@ void ARMErr657417Patcher::init() {
   };
 
   // Collect mapping symbols for every executable InputSection.
-  for (InputFile *file : objectFiles) {
-    auto *f = cast<ObjFile<ELF32LE>>(file);
-    for (Symbol *s : f->getLocalSymbols()) {
+  for (ELFFileBase *file : ctx.objectFiles) {
+    for (Symbol *s : file->getLocalSymbols()) {
       auto *def = dyn_cast<Defined>(s);
       if (!def)
         continue;
@@ -396,7 +393,7 @@ void ARMErr657417Patcher::insertPatches(
   // determine the insertion point. This is ok as we only merge into an
   // InputSectionDescription once per pass, and at the end of the pass
   // assignAddresses() will recalculate all the outSecOff values.
-  std::vector<InputSection *> tmp;
+  SmallVector<InputSection *, 0> tmp;
   tmp.reserve(isd.sections.size() + patches.size());
   auto mergeCmp = [](const InputSection *a, const InputSection *b) {
     if (a->outSecOff != b->outSecOff)
@@ -454,7 +451,7 @@ static void implementPatch(ScanResult sr, InputSection *isec,
       patchRelType = R_ARM_JUMP24;
       patchRelAddend -= 4;
     }
-    psec->relocations.push_back(
+    psec->addReloc(
         Relocation{sr.rel->expr, patchRelType, 0, patchRelAddend, sr.rel->sym});
     // Redirect the existing branch relocation to the patch.
     sr.rel->expr = R_PC;
@@ -473,8 +470,7 @@ static void implementPatch(ScanResult sr, InputSection *isec,
       type = R_ARM_THM_JUMP24;
     else
       type = R_ARM_THM_CALL;
-    isec->relocations.push_back(
-        Relocation{R_PC, type, sr.off, -4, psec->patchSym});
+    isec->addReloc(Relocation{R_PC, type, sr.off, -4, psec->patchSym});
   }
   patches.push_back(psec);
 }
@@ -501,8 +497,8 @@ ARMErr657417Patcher::patchInputSectionDescription(
     while (thumbSym != mapSyms.end()) {
       auto nonThumbSym = std::next(thumbSym);
       uint64_t off = (*thumbSym)->value;
-      uint64_t limit = (nonThumbSym == mapSyms.end()) ? isec->data().size()
-                                                      : (*nonThumbSym)->value;
+      uint64_t limit = nonThumbSym == mapSyms.end() ? isec->content().size()
+                                                    : (*nonThumbSym)->value;
 
       while (off < limit) {
         ScanResult sr = scanCortexA8Errata657417(isec, off, limit);
@@ -525,8 +521,8 @@ bool ARMErr657417Patcher::createFixes() {
   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)) {
+    for (SectionCommand *cmd : os->commands)
+      if (auto *isd = dyn_cast<InputSectionDescription>(cmd)) {
         std::vector<Patch657417Section *> patches =
             patchInputSectionDescription(*isd);
         if (!patches.empty()) {
index a93609b..2a7e6aa 100644 (file)
 
 #include "lld/Common/LLVM.h"
 #include "llvm/ADT/DenseMap.h"
-#include <map>
 #include <vector>
 
-namespace lld {
-namespace elf {
+namespace lld::elf {
 
 class Defined;
 class InputSection;
 class InputSectionDescription;
-class OutputSection;
 class Patch657417Section;
 
 class ARMErr657417Patcher {
@@ -45,7 +42,6 @@ private:
   bool initialized = false;
 };
 
-} // namespace elf
-} // namespace lld
+} // namespace lld::elf
 
 #endif
index 466ad81..e560549 100644 (file)
@@ -10,7 +10,7 @@
 #include "Symbols.h"
 #include "Target.h"
 #include "lld/Common/ErrorHandler.h"
-#include "llvm/Object/ELF.h"
+#include "llvm/BinaryFormat/ELF.h"
 #include "llvm/Support/Endian.h"
 
 using namespace llvm;
@@ -40,7 +40,6 @@ public:
 AMDGPU::AMDGPU() {
   relativeRel = R_AMDGPU_RELATIVE64;
   gotRel = R_AMDGPU_ABS64;
-  noneRel = R_AMDGPU_NONE;
   symbolicRel = R_AMDGPU_ABS64;
 }
 
@@ -49,10 +48,10 @@ static uint32_t getEFlags(InputFile *file) {
 }
 
 uint32_t AMDGPU::calcEFlagsV3() const {
-  uint32_t ret = getEFlags(objectFiles[0]);
+  uint32_t ret = getEFlags(ctx.objectFiles[0]);
 
   // Verify that all input files have the same e_flags.
-  for (InputFile *f : makeArrayRef(objectFiles).slice(1)) {
+  for (InputFile *f : ArrayRef(ctx.objectFiles).slice(1)) {
     if (ret == getEFlags(f))
       continue;
     error("incompatible e_flags: " + toString(f));
@@ -62,14 +61,15 @@ uint32_t AMDGPU::calcEFlagsV3() const {
 }
 
 uint32_t AMDGPU::calcEFlagsV4() const {
-  uint32_t retMach = getEFlags(objectFiles[0]) & EF_AMDGPU_MACH;
-  uint32_t retXnack = getEFlags(objectFiles[0]) & EF_AMDGPU_FEATURE_XNACK_V4;
+  uint32_t retMach = getEFlags(ctx.objectFiles[0]) & EF_AMDGPU_MACH;
+  uint32_t retXnack =
+      getEFlags(ctx.objectFiles[0]) & EF_AMDGPU_FEATURE_XNACK_V4;
   uint32_t retSramEcc =
-      getEFlags(objectFiles[0]) & EF_AMDGPU_FEATURE_SRAMECC_V4;
+      getEFlags(ctx.objectFiles[0]) & EF_AMDGPU_FEATURE_SRAMECC_V4;
 
   // Verify that all input files have compatible e_flags (same mach, all
   // features in the same category are either ANY, ANY and ON, or ANY and OFF).
-  for (InputFile *f : makeArrayRef(objectFiles).slice(1)) {
+  for (InputFile *f : ArrayRef(ctx.objectFiles).slice(1)) {
     if (retMach != (getEFlags(f) & EF_AMDGPU_MACH)) {
       error("incompatible mach: " + toString(f));
       return 0;
@@ -106,15 +106,19 @@ uint32_t AMDGPU::calcEFlagsV4() const {
 }
 
 uint32_t AMDGPU::calcEFlags() const {
-  assert(!objectFiles.empty());
+  if (ctx.objectFiles.empty())
+    return 0;
 
-  uint8_t abiVersion = cast<ObjFile<ELF64LE>>(objectFiles[0])->getObj()
-      .getHeader().e_ident[EI_ABIVERSION];
+  uint8_t abiVersion = cast<ObjFile<ELF64LE>>(ctx.objectFiles[0])
+                           ->getObj()
+                           .getHeader()
+                           .e_ident[EI_ABIVERSION];
   switch (abiVersion) {
   case ELFABIVERSION_AMDGPU_HSA_V2:
   case ELFABIVERSION_AMDGPU_HSA_V3:
     return calcEFlagsV3();
   case ELFABIVERSION_AMDGPU_HSA_V4:
+  case ELFABIVERSION_AMDGPU_HSA_V5:
     return calcEFlagsV4();
   default:
     error("unknown abi version: " + Twine(abiVersion));
index d909a32..24d78eb 100644 (file)
@@ -6,13 +6,11 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "InputFiles.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
-#include "Thunks.h"
 #include "lld/Common/ErrorHandler.h"
-#include "llvm/Object/ELF.h"
+#include "llvm/BinaryFormat/ELF.h"
 #include "llvm/Support/Endian.h"
 
 using namespace llvm;
@@ -52,13 +50,11 @@ ARM::ARM() {
   relativeRel = R_ARM_RELATIVE;
   iRelativeRel = R_ARM_IRELATIVE;
   gotRel = R_ARM_GLOB_DAT;
-  noneRel = R_ARM_NONE;
   pltRel = R_ARM_JUMP_SLOT;
   symbolicRel = R_ARM_ABS32;
   tlsGotRel = R_ARM_TLS_TPOFF32;
   tlsModuleIndexRel = R_ARM_TLS_DTPMOD32;
   tlsOffsetRel = R_ARM_TLS_DTPOFF32;
-  gotBaseSymInGotPlt = false;
   pltHeaderSize = 32;
   pltEntrySize = 16;
   ipltEntrySize = 16;
@@ -86,6 +82,13 @@ uint32_t ARM::calcEFlags() const {
 RelExpr ARM::getRelExpr(RelType type, const Symbol &s,
                         const uint8_t *loc) const {
   switch (type) {
+  case R_ARM_ABS32:
+  case R_ARM_MOVW_ABS_NC:
+  case R_ARM_MOVT_ABS:
+  case R_ARM_THM_MOVW_ABS_NC:
+  case R_ARM_THM_MOVT_ABS:
+    return R_ABS;
+  case R_ARM_THM_JUMP8:
   case R_ARM_THM_JUMP11:
     return R_PC;
   case R_ARM_CALL:
@@ -135,7 +138,16 @@ RelExpr ARM::getRelExpr(RelType type, const Symbol &s,
   case R_ARM_THM_MOVT_PREL:
     return R_PC;
   case R_ARM_ALU_PC_G0:
+  case R_ARM_ALU_PC_G0_NC:
+  case R_ARM_ALU_PC_G1:
+  case R_ARM_ALU_PC_G1_NC:
+  case R_ARM_ALU_PC_G2:
   case R_ARM_LDR_PC_G0:
+  case R_ARM_LDR_PC_G1:
+  case R_ARM_LDR_PC_G2:
+  case R_ARM_LDRS_PC_G0:
+  case R_ARM_LDRS_PC_G1:
+  case R_ARM_LDRS_PC_G2:
   case R_ARM_THM_ALU_PREL_11_0:
   case R_ARM_THM_PC8:
   case R_ARM_THM_PC12:
@@ -158,7 +170,9 @@ RelExpr ARM::getRelExpr(RelType type, const Symbol &s,
     // not ARMv4 output, we can just ignore it.
     return R_NONE;
   default:
-    return R_ABS;
+    error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
+          ") against symbol " + toString(s));
+    return R_NONE;
   }
 }
 
@@ -178,7 +192,7 @@ void ARM::writeIgotPlt(uint8_t *buf, const Symbol &s) const {
 }
 
 // Long form PLT Header that does not have any restrictions on the displacement
-// of the .plt from the .plt.got.
+// of the .plt from the .got.plt.
 static void writePltHeaderLong(uint8_t *buf) {
   const uint8_t pltData[] = {
       0x04, 0xe0, 0x2d, 0xe5, //     str lr, [sp,#-4]!
@@ -195,7 +209,7 @@ static void writePltHeaderLong(uint8_t *buf) {
   write32le(buf + 16, gotPlt - l1 - 8);
 }
 
-// The default PLT header requires the .plt.got to be within 128 Mb of the
+// The default PLT header requires the .got.plt to be within 128 Mb of the
 // .plt in the positive direction.
 void ARM::writePltHeader(uint8_t *buf) const {
   // Use a similar sequence to that in writePlt(), the difference is the calling
@@ -231,21 +245,21 @@ void ARM::addPltHeaderSymbols(InputSection &isec) const {
 }
 
 // Long form PLT entries that do not have any restrictions on the displacement
-// of the .plt from the .plt.got.
+// of the .plt from the .got.plt.
 static void writePltLong(uint8_t *buf, uint64_t gotPltEntryAddr,
                          uint64_t pltEntryAddr) {
   const uint8_t pltData[] = {
       0x04, 0xc0, 0x9f, 0xe5, //     ldr ip, L2
       0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc
       0x00, 0xf0, 0x9c, 0xe5, //     ldr pc, [ip]
-      0x00, 0x00, 0x00, 0x00, // L2: .word   Offset(&(.plt.got) - L1 - 8
+      0x00, 0x00, 0x00, 0x00, // L2: .word   Offset(&(.got.plt) - L1 - 8
   };
   memcpy(buf, pltData, sizeof(pltData));
   uint64_t l1 = pltEntryAddr + 4;
   write32le(buf + 12, gotPltEntryAddr - l1 - 8);
 }
 
-// The default PLT entries require the .plt.got to be within 128 Mb of the
+// The default PLT entries require the .got.plt to be within 128 Mb of the
 // .plt in the positive direction.
 void ARM::writePlt(uint8_t *buf, const Symbol &sym,
                    uint64_t pltEntryAddr) const {
@@ -255,9 +269,9 @@ void ARM::writePlt(uint8_t *buf, const Symbol &sym,
   // hard code the most compact rotations for simplicity. This saves a load
   // instruction over the long plt sequences.
   const uint32_t pltData[] = {
-      0xe28fc600, // L1: add ip, pc,  #0x0NN00000  Offset(&(.plt.got) - L1 - 8
-      0xe28cca00, //     add ip, ip,  #0x000NN000  Offset(&(.plt.got) - L1 - 8
-      0xe5bcf000, //     ldr pc, [ip, #0x00000NNN] Offset(&(.plt.got) - L1 - 8
+      0xe28fc600, // L1: add ip, pc,  #0x0NN00000  Offset(&(.got.plt) - L1 - 8
+      0xe28cca00, //     add ip, ip,  #0x000NN000  Offset(&(.got.plt) - L1 - 8
+      0xe5bcf000, //     ldr pc, [ip, #0x00000NNN] Offset(&(.got.plt) - L1 - 8
   };
 
   uint64_t offset = sym.getGotPltVA() - pltEntryAddr - 8;
@@ -280,9 +294,11 @@ void ARM::addPltSymbols(InputSection &isec, uint64_t off) const {
 bool ARM::needsThunk(RelExpr expr, RelType type, const InputFile *file,
                      uint64_t branchAddr, const Symbol &s,
                      int64_t a) const {
-  // If S is an undefined weak symbol and does not have a PLT entry then it
-  // will be resolved as a branch to the next instruction.
-  if (s.isUndefWeak() && !s.isInPlt())
+  // If s is an undefined weak symbol and does not have a PLT entry then it will
+  // be resolved as a branch to the next instruction. If it is hidden, its
+  // binding has been converted to local, so we just check isUndefined() here. A
+  // undefined non-weak symbol will have been errored.
+  if (s.isUndefined() && !s.isInPlt())
     return false;
   // A state change from ARM to Thumb and vice versa must go through an
   // interworking thunk if the relocation type is not R_ARM_CALL or
@@ -295,10 +311,11 @@ bool ARM::needsThunk(RelExpr expr, RelType type, const InputFile *file,
     // Otherwise we need to interwork if STT_FUNC Symbol has bit 0 set (Thumb).
     if (s.isFunc() && expr == R_PC && (s.getVA() & 1))
       return true;
-    LLVM_FALLTHROUGH;
+    [[fallthrough]];
   case R_ARM_CALL: {
     uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA();
-    return !inBranchRange(type, branchAddr, dst + a);
+    return !inBranchRange(type, branchAddr, dst + a) ||
+        (!config->armHasBlx && (s.getVA() & 1));
   }
   case R_ARM_THM_JUMP19:
   case R_ARM_THM_JUMP24:
@@ -306,10 +323,11 @@ bool ARM::needsThunk(RelExpr expr, RelType type, const InputFile *file,
     // Otherwise we need to interwork if STT_FUNC Symbol has bit 0 clear (ARM).
     if (expr == R_PLT_PC || (s.isFunc() && (s.getVA() & 1) == 0))
       return true;
-    LLVM_FALLTHROUGH;
+    [[fallthrough]];
   case R_ARM_THM_CALL: {
     uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA();
-    return !inBranchRange(type, branchAddr, dst + a);
+    return !inBranchRange(type, branchAddr, dst + a) ||
+        (!config->armHasBlx && (s.getVA() & 1) == 0);;
   }
   }
   return false;
@@ -382,73 +400,105 @@ bool ARM::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
 // or Thumb.
 static void stateChangeWarning(uint8_t *loc, RelType relt, const Symbol &s) {
   assert(!s.isFunc());
+  const ErrorPlace place = getErrorPlace(loc);
+  std::string hint;
+  if (!place.srcLoc.empty())
+    hint = "; " + place.srcLoc;
   if (s.isSection()) {
     // Section symbols must be defined and in a section. Users cannot change
     // the type. Use the section name as getName() returns an empty string.
-    warn(getErrorLocation(loc) + "branch and link relocation: " +
-         toString(relt) + " to STT_SECTION symbol " +
-         cast<Defined>(s).section->name + " ; interworking not performed");
+    warn(place.loc + "branch and link relocation: " + toString(relt) +
+         " to STT_SECTION symbol " + cast<Defined>(s).section->name +
+         " ; interworking not performed" + hint);
   } else {
     // Warn with hint on how to alter the symbol type.
     warn(getErrorLocation(loc) + "branch and link relocation: " +
          toString(relt) + " to non STT_FUNC symbol: " + s.getName() +
          " interworking not performed; consider using directive '.type " +
          s.getName() +
-         ", %function' to give symbol type STT_FUNC if"
-         " interworking between ARM and Thumb is required");
+         ", %function' to give symbol type STT_FUNC if interworking between "
+         "ARM and Thumb is required" +
+         hint);
   }
 }
 
-// Utility functions taken from ARMAddressingModes.h, only changes are LLD
-// coding style.
-
 // Rotate a 32-bit unsigned value right by a specified amt of bits.
 static uint32_t rotr32(uint32_t val, uint32_t amt) {
   assert(amt < 32 && "Invalid rotate amount");
   return (val >> amt) | (val << ((32 - amt) & 31));
 }
 
-// Rotate a 32-bit unsigned value left by a specified amt of bits.
-static uint32_t rotl32(uint32_t val, uint32_t amt) {
-  assert(amt < 32 && "Invalid rotate amount");
-  return (val << amt) | (val >> ((32 - amt) & 31));
+static std::pair<uint32_t, uint32_t> getRemAndLZForGroup(unsigned group,
+                                                         uint32_t val) {
+  uint32_t rem, lz;
+  do {
+    lz = llvm::countLeadingZeros(val) & ~1;
+    rem = val;
+    if (lz == 32) // implies rem == 0
+      break;
+    val &= 0xffffff >> lz;
+  } while (group--);
+  return {rem, lz};
 }
 
-// Try to encode a 32-bit unsigned immediate imm with an immediate shifter
-// operand, this form is an 8-bit immediate rotated right by an even number of
-// bits. We compute the rotate amount to use.  If this immediate value cannot be
-// handled with a single shifter-op, determine a good rotate amount that will
-// take a maximal chunk of bits out of the immediate.
-static uint32_t getSOImmValRotate(uint32_t imm) {
-  // 8-bit (or less) immediates are trivially shifter_operands with a rotate
-  // of zero.
-  if ((imm & ~255U) == 0)
-    return 0;
-
-  // Use CTZ to compute the rotate amount.
-  unsigned tz = llvm::countTrailingZeros(imm);
-
-  // Rotate amount must be even.  Something like 0x200 must be rotated 8 bits,
-  // not 9.
-  unsigned rotAmt = tz & ~1;
-
-  // If we can handle this spread, return it.
-  if ((rotr32(imm, rotAmt) & ~255U) == 0)
-    return (32 - rotAmt) & 31; // HW rotates right, not left.
+static void encodeAluGroup(uint8_t *loc, const Relocation &rel, uint64_t val,
+                           int group, bool check) {
+  // ADD/SUB (immediate) add = bit23, sub = bit22
+  // immediate field carries is a 12-bit modified immediate, made up of a 4-bit
+  // even rotate right and an 8-bit immediate.
+  uint32_t opcode = 0x00800000;
+  if (val >> 63) {
+    opcode = 0x00400000;
+    val = -val;
+  }
+  uint32_t imm, lz;
+  std::tie(imm, lz) = getRemAndLZForGroup(group, val);
+  uint32_t rot = 0;
+  if (lz < 24) {
+    imm = rotr32(imm, 24 - lz);
+    rot = (lz + 8) << 7;
+  }
+  if (check && imm > 0xff)
+    error(getErrorLocation(loc) + "unencodeable immediate " + Twine(val).str() +
+          " for relocation " + toString(rel.type));
+  write32le(loc, (read32le(loc) & 0xff3ff000) | opcode | rot | (imm & 0xff));
+}
 
-  // For values like 0xF000000F, we should ignore the low 6 bits, then
-  // retry the hunt.
-  if (imm & 63U) {
-    unsigned tz2 = countTrailingZeros(imm & ~63U);
-    unsigned rotAmt2 = tz2 & ~1;
-    if ((rotr32(imm, rotAmt2) & ~255U) == 0)
-      return (32 - rotAmt2) & 31; // HW rotates right, not left.
+static void encodeLdrGroup(uint8_t *loc, const Relocation &rel, uint64_t val,
+                           int group) {
+  // R_ARM_LDR_PC_Gn is S + A - P, we have ((S + A) | T) - P, if S is a
+  // function then addr is 0 (modulo 2) and Pa is 0 (modulo 4) so we can clear
+  // bottom bit to recover S + A - P.
+  if (rel.sym->isFunc())
+    val &= ~0x1;
+  // LDR (literal) u = bit23
+  uint32_t opcode = 0x00800000;
+  if (val >> 63) {
+    opcode = 0x0;
+    val = -val;
   }
+  uint32_t imm = getRemAndLZForGroup(group, val).first;
+  checkUInt(loc, imm, 12, rel);
+  write32le(loc, (read32le(loc) & 0xff7ff000) | opcode | imm);
+}
 
-  // Otherwise, we have no way to cover this span of bits with a single
-  // shifter_op immediate.  Return a chunk of bits that will be useful to
-  // handle.
-  return (32 - rotAmt) & 31; // HW rotates right, not left.
+static void encodeLdrsGroup(uint8_t *loc, const Relocation &rel, uint64_t val,
+                            int group) {
+  // R_ARM_LDRS_PC_Gn is S + A - P, we have ((S + A) | T) - P, if S is a
+  // function then addr is 0 (modulo 2) and Pa is 0 (modulo 4) so we can clear
+  // bottom bit to recover S + A - P.
+  if (rel.sym->isFunc())
+    val &= ~0x1;
+  // LDRD/LDRH/LDRSB/LDRSH (literal) u = bit23
+  uint32_t opcode = 0x00800000;
+  if (val >> 63) {
+    opcode = 0x0;
+    val = -val;
+  }
+  uint32_t imm = getRemAndLZForGroup(group, val).first;
+  checkUInt(loc, imm, 8, rel);
+  write32le(loc, (read32le(loc) & 0xff7ff0f0) | opcode | ((imm & 0xf0) << 4) |
+                     (imm & 0xf));
 }
 
 void ARM::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
@@ -502,14 +552,20 @@ void ARM::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
     write32le(loc, 0xeb000000 | (read32le(loc) & 0x00ffffff));
     // fall through as BL encoding is shared with B
   }
-    LLVM_FALLTHROUGH;
+    [[fallthrough]];
   case R_ARM_JUMP24:
   case R_ARM_PC24:
   case R_ARM_PLT32:
     checkInt(loc, val, 26, rel);
     write32le(loc, (read32le(loc) & ~0x00ffffff) | ((val >> 2) & 0x00ffffff));
     break;
+  case R_ARM_THM_JUMP8:
+    // We do a 9 bit check because val is right-shifted by 1 bit.
+    checkInt(loc, val, 9, rel);
+    write16le(loc, (read32le(loc) & 0xff00) | ((val >> 1) & 0x00ff));
+    break;
   case R_ARM_THM_JUMP11:
+    // We do a 12 bit check because val is right-shifted by 1 bit.
     checkInt(loc, val, 12, rel);
     write16le(loc, (read32le(loc) & 0xf800) | ((val >> 1) & 0x07ff));
     break;
@@ -563,7 +619,7 @@ void ARM::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
     }
   }
     // Fall through as rest of encoding is the same as B.W
-    LLVM_FALLTHROUGH;
+    [[fallthrough]];
   case R_ARM_THM_JUMP24:
     // Encoding B  T4, BL T1, BLX T2: Val = S:I1:I2:imm10:imm11:0
     checkInt(loc, val, 25, rel);
@@ -615,45 +671,39 @@ void ARM::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
                   ((val << 4) & 0x7000) |    // imm3
                   (val & 0x00ff));           // imm8
     break;
-  case R_ARM_ALU_PC_G0: {
-    // ADR (literal) add = bit23, sub = bit22
-    // literal is a 12-bit modified immediate, made up of a 4-bit even rotate
-    // right and an 8-bit immediate. The code-sequence here is derived from
-    // ARMAddressingModes.h in llvm/Target/ARM/MCTargetDesc. In our case we
-    // want to give an error if we cannot encode the constant.
-    uint32_t opcode = 0x00800000;
-    if (val >> 63) {
-      opcode = 0x00400000;
-      val = ~val + 1;
-    }
-    if ((val & ~255U) != 0) {
-      uint32_t rotAmt = getSOImmValRotate(val);
-      // Error if we cannot encode this with a single shift
-      if (rotr32(~255U, rotAmt) & val)
-        error(getErrorLocation(loc) + "unencodeable immediate " +
-              Twine(val).str() + " for relocation " + toString(rel.type));
-      val = rotl32(val, rotAmt) | ((rotAmt >> 1) << 8);
-    }
-    write32le(loc, (read32le(loc) & 0xff0ff000) | opcode | val);
+  case R_ARM_ALU_PC_G0:
+    encodeAluGroup(loc, rel, val, 0, true);
     break;
-  }
-  case R_ARM_LDR_PC_G0: {
-    // R_ARM_LDR_PC_G0 is S + A - P, we have ((S + A) | T) - P, if S is a
-    // function then addr is 0 (modulo 2) and Pa is 0 (modulo 4) so we can clear
-    // bottom bit to recover S + A - P.
-    if (rel.sym->isFunc())
-      val &= ~0x1;
-    // LDR (literal) u = bit23
-    int64_t imm = val;
-    uint32_t u = 0x00800000;
-    if (imm < 0) {
-      imm = -imm;
-      u = 0;
-    }
-    checkUInt(loc, imm, 12, rel);
-    write32le(loc, (read32le(loc) & 0xff7ff000) | u | imm);
+  case R_ARM_ALU_PC_G0_NC:
+    encodeAluGroup(loc, rel, val, 0, false);
+    break;
+  case R_ARM_ALU_PC_G1:
+    encodeAluGroup(loc, rel, val, 1, true);
+    break;
+  case R_ARM_ALU_PC_G1_NC:
+    encodeAluGroup(loc, rel, val, 1, false);
+    break;
+  case R_ARM_ALU_PC_G2:
+    encodeAluGroup(loc, rel, val, 2, true);
+    break;
+  case R_ARM_LDR_PC_G0:
+    encodeLdrGroup(loc, rel, val, 0);
+    break;
+  case R_ARM_LDR_PC_G1:
+    encodeLdrGroup(loc, rel, val, 1);
+    break;
+  case R_ARM_LDR_PC_G2:
+    encodeLdrGroup(loc, rel, val, 2);
+    break;
+  case R_ARM_LDRS_PC_G0:
+    encodeLdrsGroup(loc, rel, val, 0);
+    break;
+  case R_ARM_LDRS_PC_G1:
+    encodeLdrsGroup(loc, rel, val, 1);
+    break;
+  case R_ARM_LDRS_PC_G2:
+    encodeLdrsGroup(loc, rel, val, 2);
     break;
-  }
   case R_ARM_THM_ALU_PREL_11_0: {
     // ADR encoding T2 (sub), T3 (add) i:imm3:imm8
     int64_t imm = val;
@@ -699,8 +749,7 @@ void ARM::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
     break;
   }
   default:
-    error(getErrorLocation(loc) + "unrecognized relocation " +
-          toString(rel.type));
+    llvm_unreachable("unknown relocation");
   }
 }
 
@@ -738,6 +787,8 @@ int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const {
   case R_ARM_PC24:
   case R_ARM_PLT32:
     return SignExtend64<26>(read32le(buf) << 2);
+  case R_ARM_THM_JUMP8:
+    return SignExtend64<9>(read16le(buf) << 1);
   case R_ARM_THM_JUMP11:
     return SignExtend64<12>(read16le(buf) << 1);
   case R_ARM_THM_JUMP19: {
@@ -760,7 +811,7 @@ int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const {
                               ((lo & 0x7ff) << 1));  // imm11:0
       break;
     }
-    LLVM_FALLTHROUGH;
+    [[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)
@@ -797,7 +848,11 @@ int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const {
                             ((lo & 0x7000) >> 4) |  // imm3
                             (lo & 0x00ff));         // imm8
   }
-  case R_ARM_ALU_PC_G0: {
+  case R_ARM_ALU_PC_G0:
+  case R_ARM_ALU_PC_G0_NC:
+  case R_ARM_ALU_PC_G1:
+  case R_ARM_ALU_PC_G1_NC:
+  case R_ARM_ALU_PC_G2: {
     // 12-bit immediate is a modified immediate made up of a 4-bit even
     // right rotation and 8-bit constant. After the rotation the value
     // is zero-extended. When bit 23 is set the instruction is an add, when
@@ -806,13 +861,25 @@ int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const {
     uint32_t val = rotr32(instr & 0xff, ((instr & 0xf00) >> 8) * 2);
     return (instr & 0x00400000) ? -val : val;
   }
-  case R_ARM_LDR_PC_G0: {
+  case R_ARM_LDR_PC_G0:
+  case R_ARM_LDR_PC_G1:
+  case R_ARM_LDR_PC_G2: {
     // ADR (literal) add = bit23, sub = bit22
     // LDR (literal) u = bit23 unsigned imm12
     bool u = read32le(buf) & 0x00800000;
     uint32_t imm12 = read32le(buf) & 0xfff;
     return u ? imm12 : -imm12;
   }
+  case R_ARM_LDRS_PC_G0:
+  case R_ARM_LDRS_PC_G1:
+  case R_ARM_LDRS_PC_G2: {
+    // LDRD/LDRH/LDRSB/LDRSH (literal) u = bit23 unsigned imm8
+    uint32_t opcode = read32le(buf);
+    bool u = opcode & 0x00800000;
+    uint32_t imm4l = opcode & 0xf;
+    uint32_t imm4h = (opcode & 0xf00) >> 4;
+    return u ? (imm4h | imm4l) : -(imm4h | imm4l);
+  }
   case R_ARM_THM_ALU_PREL_11_0: {
     // Thumb2 ADR, which is an alias for a sub or add instruction with an
     // unsigned immediate.
@@ -838,6 +905,7 @@ int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const {
     return u ? imm12 : -imm12;
   }
   case R_ARM_NONE:
+  case R_ARM_V4BX:
   case R_ARM_JUMP_SLOT:
     // These relocations are defined as not having an implicit addend.
     return 0;
index d0d2472..f0bb8ef 100644 (file)
@@ -29,7 +29,7 @@
 #include "Symbols.h"
 #include "Target.h"
 #include "lld/Common/ErrorHandler.h"
-#include "llvm/Object/ELF.h"
+#include "llvm/BinaryFormat/ELF.h"
 #include "llvm/Support/Endian.h"
 
 using namespace llvm;
@@ -42,7 +42,6 @@ using namespace lld::elf;
 namespace {
 class AVR final : public TargetInfo {
 public:
-  AVR();
   uint32_t calcEFlags() const override;
   RelExpr getRelExpr(RelType type, const Symbol &s,
                      const uint8_t *loc) const override;
@@ -51,8 +50,6 @@ public:
 };
 } // namespace
 
-AVR::AVR() { noneRel = R_AVR_NONE; }
-
 RelExpr AVR::getRelExpr(RelType type, const Symbol &s,
                         const uint8_t *loc) const {
   switch (type) {
@@ -77,6 +74,7 @@ RelExpr AVR::getRelExpr(RelType type, const Symbol &s,
   case R_AVR_HI8_LDI_PM_NEG:
   case R_AVR_HH8_LDI_PM:
   case R_AVR_HH8_LDI_PM_NEG:
+  case R_AVR_LDS_STS_16:
   case R_AVR_PORT5:
   case R_AVR_PORT6:
   case R_AVR_CALL:
@@ -173,6 +171,14 @@ void AVR::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
     writeLDI(loc, (-val >> 17) & 0xff);
     break;
 
+  case R_AVR_LDS_STS_16: {
+    checkUInt(loc, val, 7, rel);
+    const uint16_t hi = val >> 4;
+    const uint16_t lo = val & 0xf;
+    write16le(loc, (read16le(loc) & 0xf8f0) | ((hi << 8) | lo));
+    break;
+  }
+
   case R_AVR_PORT5:
     checkUInt(loc, val, 5, rel);
     write16le(loc, (read16le(loc) & 0xff07) | (val << 3));
@@ -229,12 +235,12 @@ static uint32_t getEFlags(InputFile *file) {
 }
 
 uint32_t AVR::calcEFlags() const {
-  assert(!objectFiles.empty());
+  assert(!ctx.objectFiles.empty());
 
-  uint32_t flags = getEFlags(objectFiles[0]);
+  uint32_t flags = getEFlags(ctx.objectFiles[0]);
   bool hasLinkRelaxFlag = flags & EF_AVR_LINKRELAX_PREPARED;
 
-  for (InputFile *f : makeArrayRef(objectFiles).slice(1)) {
+  for (InputFile *f : ArrayRef(ctx.objectFiles).slice(1)) {
     uint32_t objFlags = getEFlags(f);
     if ((objFlags & EF_AVR_ARCH_MASK) != (flags & EF_AVR_ARCH_MASK))
       error(toString(f) +
index 02d872d..bc26653 100644 (file)
@@ -12,7 +12,6 @@
 #include "Target.h"
 #include "lld/Common/ErrorHandler.h"
 #include "llvm/BinaryFormat/ELF.h"
-#include "llvm/Object/ELF.h"
 #include "llvm/Support/Endian.h"
 
 using namespace llvm;
@@ -44,6 +43,7 @@ Hexagon::Hexagon() {
   gotRel = R_HEX_GLOB_DAT;
   symbolicRel = R_HEX_32;
 
+  gotBaseSymInGotPlt = true;
   // The zero'th GOT entry is reserved for the address of _DYNAMIC.  The
   // next 3 are reserved for the dynamic loader.
   gotPltHeaderEntriesNum = 4;
@@ -53,19 +53,18 @@ Hexagon::Hexagon() {
 
   // Hexagon Linux uses 64K pages by default.
   defaultMaxPageSize = 0x10000;
-  noneRel = R_HEX_NONE;
   tlsGotRel = R_HEX_TPREL_32;
   tlsModuleIndexRel = R_HEX_DTPMOD_32;
   tlsOffsetRel = R_HEX_DTPREL_32;
 }
 
 uint32_t Hexagon::calcEFlags() const {
-  assert(!objectFiles.empty());
+  assert(!ctx.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) {
+  for (InputFile *f : ctx.objectFiles) {
     uint32_t eflags = cast<ObjFile<ELF32LE>>(f)->getObj().getHeader().e_flags;
     if (eflags > ret)
       ret = eflags;
@@ -146,7 +145,6 @@ RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s,
   case R_HEX_IE_GOT_32_6_X:
   case R_HEX_IE_GOT_HI16:
   case R_HEX_IE_GOT_LO16:
-    config->hasStaticTlsModel = true;
     return R_GOTPLT;
   case R_HEX_TPREL_11_X:
   case R_HEX_TPREL_16:
@@ -162,6 +160,28 @@ RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s,
   }
 }
 
+// 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}};
+
 static bool isDuplex(uint32_t insn) {
   // 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
@@ -170,29 +190,6 @@ static bool isDuplex(uint32_t insn) {
 }
 
 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}};
-
   if (isDuplex(insn))
     return 0x03f00000;
 
@@ -200,7 +197,7 @@ static uint32_t findMaskR6(uint32_t insn) {
     if ((0xff000000 & insn) == i.cmpMask)
       return i.relocMask;
 
-  error("unrecognized instruction for R_HEX_6 relocation: 0x" +
+  error("unrecognized instruction for 6_X relocation: 0x" +
         utohexstr(insn));
   return 0;
 }
@@ -232,7 +229,11 @@ static uint32_t findMaskR16(uint32_t insn) {
   if (isDuplex(insn))
     return 0x03f00000;
 
-  error("unrecognized instruction for R_HEX_16_X relocation: 0x" +
+  for (InstructionMask i : r6)
+    if ((0xff000000 & insn) == i.cmpMask)
+      return i.relocMask;
+
+  error("unrecognized instruction for 16_X type: 0x" +
         utohexstr(insn));
   return 0;
 }
index 4af90b4..378b287 100644 (file)
 //
 //===----------------------------------------------------------------------===//
 
-#include "InputFiles.h"
 #include "Symbols.h"
 #include "Target.h"
 #include "lld/Common/ErrorHandler.h"
-#include "llvm/Object/ELF.h"
+#include "llvm/BinaryFormat/ELF.h"
 #include "llvm/Support/Endian.h"
 
 using namespace llvm;
index a233a01..d5a335c 100644 (file)
@@ -11,9 +11,8 @@
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
-#include "Thunks.h"
 #include "lld/Common/ErrorHandler.h"
-#include "llvm/Object/ELF.h"
+#include "llvm/BinaryFormat/ELF.h"
 
 using namespace llvm;
 using namespace llvm::object;
@@ -46,11 +45,9 @@ public:
 template <class ELFT> MIPS<ELFT>::MIPS() {
   gotPltHeaderEntriesNum = 2;
   defaultMaxPageSize = 65536;
-  gotBaseSymInGotPlt = false;
   pltEntrySize = 16;
   pltHeaderSize = 32;
   copyRel = R_MIPS_COPY;
-  noneRel = R_MIPS_NONE;
   pltRel = R_MIPS_JUMP_SLOT;
   needsThunks = true;
 
@@ -128,18 +125,19 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType type, const Symbol &s,
       return R_MIPS_GOT_GP_PC;
     if (&s == ElfSym::mipsLocalGp)
       return R_MIPS_GOT_GP;
-    LLVM_FALLTHROUGH;
+    [[fallthrough]];
   case R_MIPS_32:
   case R_MIPS_64:
   case R_MIPS_GOT_OFST:
   case R_MIPS_SUB:
+    return R_ABS;
   case R_MIPS_TLS_DTPREL_HI16:
   case R_MIPS_TLS_DTPREL_LO16:
   case R_MIPS_TLS_DTPREL32:
   case R_MIPS_TLS_DTPREL64:
   case R_MICROMIPS_TLS_DTPREL_HI16:
   case R_MICROMIPS_TLS_DTPREL_LO16:
-    return R_ABS;
+    return R_DTPREL;
   case R_MIPS_TLS_TPREL_HI16:
   case R_MIPS_TLS_TPREL_LO16:
   case R_MIPS_TLS_TPREL32:
@@ -166,7 +164,7 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType type, const Symbol &s,
   case R_MICROMIPS_GOT16:
     if (s.isLocal())
       return R_MIPS_GOT_LOCAL_PAGE;
-    LLVM_FALLTHROUGH;
+    [[fallthrough]];
   case R_MIPS_CALL16:
   case R_MIPS_GOT_DISP:
   case R_MIPS_TLS_GOTTPREL:
@@ -629,7 +627,7 @@ void MIPS<ELFT>::relocate(uint8_t *loc, const Relocation &rel,
   case R_MIPS_TLS_GOTTPREL:
   case R_MIPS_TLS_LDM:
     checkInt(loc, val, 16, rel);
-    LLVM_FALLTHROUGH;
+    [[fallthrough]];
   case R_MIPS_CALL_LO16:
   case R_MIPS_GOT_LO16:
   case R_MIPS_GOT_OFST:
index 77c05a8..4466173 100644 (file)
@@ -16,7 +16,6 @@
 
 #include "lld/Common/ErrorHandler.h"
 #include "llvm/BinaryFormat/ELF.h"
-#include "llvm/Object/ELF.h"
 #include "llvm/Support/MipsABIFlags.h"
 
 using namespace llvm;
@@ -296,7 +295,7 @@ static uint32_t getArchFlags(ArrayRef<FileFlags> files) {
 
 template <class ELFT> uint32_t elf::calcMipsEFlags() {
   std::vector<FileFlags> v;
-  for (InputFile *f : objectFiles)
+  for (InputFile *f : ctx.objectFiles)
     v.push_back({f, cast<ObjFile<ELFT>>(f)->getObj().getHeader().e_flags});
   if (v.empty()) {
     // If we don't have any input files, we'll have to rely on the information
index 9e18ae4..4ae742c 100644 (file)
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "InputFiles.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
@@ -35,7 +34,6 @@ 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;
   symbolicRel = R_SPARC_64;
index df769f0..8d4f258 100644 (file)
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "InputFiles.h"
+#include "OutputSections.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
@@ -38,28 +38,22 @@ public:
                 uint64_t val) const override;
 
   RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override;
-  void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
-                      uint64_t val) const override;
-  void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
-                      uint64_t val) const override;
-  void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
-                      uint64_t val) const override;
-  void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
-                      uint64_t val) const override;
+  void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override;
 };
 } // namespace
 
 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;
   symbolicRel = R_386_32;
+  tlsDescRel = R_386_TLS_DESC;
   tlsGotRel = R_386_TLS_TPOFF;
   tlsModuleIndexRel = R_386_TLS_DTPMOD32;
   tlsOffsetRel = R_386_TLS_DTPOFF32;
+  gotBaseSymInGotPlt = true;
   pltHeaderSize = 16;
   pltEntrySize = 16;
   ipltEntrySize = 16;
@@ -71,19 +65,12 @@ X86::X86() {
 }
 
 int X86::getTlsGdRelaxSkip(RelType type) const {
-  return 2;
+  // TLSDESC relocations are processed separately. See relaxTlsGdToLe below.
+  return type == R_386_TLS_GOTDESC || type == R_386_TLS_DESC_CALL ? 1 : 2;
 }
 
 RelExpr X86::getRelExpr(RelType type, const Symbol &s,
                         const uint8_t *loc) const {
-  // There are 4 different TLS variable models with varying degrees of
-  // flexibility and performance. LocalExec and InitialExec models are fast but
-  // less-flexible models. If they are in use, we set DF_STATIC_TLS flag in the
-  // dynamic section to let runtime know about that.
-  if (type == R_386_TLS_LE || type == R_386_TLS_LE_32 || type == R_386_TLS_IE ||
-      type == R_386_TLS_GOTIE)
-    config->hasStaticTlsModel = true;
-
   switch (type) {
   case R_386_8:
   case R_386_16:
@@ -143,6 +130,10 @@ RelExpr X86::getRelExpr(RelType type, const Symbol &s,
     // the byte, we can determine whether the instruction uses the operand as an
     // absolute address (R_GOT) or a register-relative address (R_GOTPLT).
     return (loc[-1] & 0xc7) == 0x5 ? R_GOT : R_GOTPLT;
+  case R_386_TLS_GOTDESC:
+    return R_TLSDESC_GOTPLT;
+  case R_386_TLS_DESC_CALL:
+    return R_TLSDESC_CALL;
   case R_386_TLS_GOTIE:
     return R_GOTPLT;
   case R_386_GOTOFF:
@@ -167,7 +158,8 @@ RelExpr X86::adjustTlsExpr(RelType type, RelExpr expr) const {
   case R_RELAX_TLS_GD_TO_IE:
     return R_RELAX_TLS_GD_TO_IE_GOTPLT;
   case R_RELAX_TLS_GD_TO_LE:
-    return R_RELAX_TLS_GD_TO_LE_NEG;
+    return type == R_386_TLS_GD ? R_RELAX_TLS_GD_TO_LE_NEG
+                                : R_RELAX_TLS_GD_TO_LE;
   }
 }
 
@@ -218,7 +210,7 @@ void X86::writePltHeader(uint8_t *buf) const {
 
 void X86::writePlt(uint8_t *buf, const Symbol &sym,
                    uint64_t pltEntryAddr) const {
-  unsigned relOff = in.relaPlt->entsize * sym.pltIndex;
+  unsigned relOff = in.relaPlt->entsize * sym.getPltIdx();
   if (config->isPic) {
     const uint8_t inst[] = {
         0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx)
@@ -259,6 +251,8 @@ int64_t X86::getImplicitAddend(const uint8_t *buf, RelType type) const {
   case R_386_PC32:
   case R_386_PLT32:
   case R_386_RELATIVE:
+  case R_386_TLS_GOTDESC:
+  case R_386_TLS_DESC_CALL:
   case R_386_TLS_DTPMOD32:
   case R_386_TLS_DTPOFF32:
   case R_386_TLS_LDO_32:
@@ -273,6 +267,8 @@ int64_t X86::getImplicitAddend(const uint8_t *buf, RelType type) const {
   case R_386_TLS_TPOFF:
   case R_386_TLS_TPOFF32:
     return SignExtend64<32>(read32le(buf));
+  case R_386_TLS_DESC:
+    return SignExtend64<32>(read32le(buf + 4));
   case R_386_NONE:
   case R_386_JUMP_SLOT:
     // These relocations are defined as not having an implicit addend.
@@ -323,6 +319,8 @@ void X86::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
   case R_386_PC32:
   case R_386_PLT32:
   case R_386_RELATIVE:
+  case R_386_TLS_GOTDESC:
+  case R_386_TLS_DESC_CALL:
   case R_386_TLS_DTPMOD32:
   case R_386_TLS_DTPOFF32:
   case R_386_TLS_GD:
@@ -337,45 +335,85 @@ void X86::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
     checkInt(loc, val, 32, rel);
     write32le(loc, val);
     break;
+  case R_386_TLS_DESC:
+    // The addend is stored in the second 32-bit word.
+    write32le(loc + 4, val);
+    break;
   default:
     llvm_unreachable("unknown relocation");
   }
 }
 
-void X86::relaxTlsGdToLe(uint8_t *loc, const Relocation &, uint64_t val) const {
-  // Convert
-  //   leal x@tlsgd(, %ebx, 1),
-  //   call __tls_get_addr@plt
-  // to
-  //   movl %gs:0,%eax
-  //   subl $x@ntpoff,%eax
-  const uint8_t inst[] = {
-      0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax
-      0x81, 0xe8, 0, 0, 0, 0,             // subl Val(%ebx), %eax
-  };
-  memcpy(loc - 3, inst, sizeof(inst));
-  write32le(loc + 5, val);
+static void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) {
+  if (rel.type == R_386_TLS_GD) {
+    // Convert (loc[-2] == 0x04)
+    //   leal x@tlsgd(, %ebx, 1), %eax
+    //   call ___tls_get_addr@plt
+    // or
+    //   leal x@tlsgd(%reg), %eax
+    //   call *___tls_get_addr@got(%reg)
+    // to
+    const uint8_t inst[] = {
+        0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax
+        0x81, 0xe8, 0,    0,    0,    0,    // subl x@ntpoff(%ebx), %eax
+    };
+    uint8_t *w = loc[-2] == 0x04 ? loc - 3 : loc - 2;
+    memcpy(w, inst, sizeof(inst));
+    write32le(w + 8, val);
+  } else if (rel.type == R_386_TLS_GOTDESC) {
+    // Convert leal x@tlsdesc(%ebx), %eax to leal x@ntpoff, %eax.
+    //
+    // Note: call *x@tlsdesc(%eax) may not immediately follow this instruction.
+    if (memcmp(loc - 2, "\x8d\x83", 2)) {
+      error(getErrorLocation(loc - 2) +
+            "R_386_TLS_GOTDESC must be used in leal x@tlsdesc(%ebx), %eax");
+      return;
+    }
+    loc[-1] = 0x05;
+    write32le(loc, val);
+  } else {
+    // Convert call *x@tlsdesc(%eax) to xchg ax, ax.
+    assert(rel.type == R_386_TLS_DESC_CALL);
+    loc[0] = 0x66;
+    loc[1] = 0x90;
+  }
 }
 
-void X86::relaxTlsGdToIe(uint8_t *loc, const Relocation &, uint64_t val) const {
-  // Convert
-  //   leal x@tlsgd(, %ebx, 1),
-  //   call __tls_get_addr@plt
-  // to
-  //   movl %gs:0, %eax
-  //   addl x@gotntpoff(%ebx), %eax
-  const uint8_t inst[] = {
-      0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax
-      0x03, 0x83, 0, 0, 0, 0,             // addl Val(%ebx), %eax
-  };
-  memcpy(loc - 3, inst, sizeof(inst));
-  write32le(loc + 5, val);
+static void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) {
+  if (rel.type == R_386_TLS_GD) {
+    // Convert (loc[-2] == 0x04)
+    //   leal x@tlsgd(, %ebx, 1), %eax
+    //   call ___tls_get_addr@plt
+    // or
+    //   leal x@tlsgd(%reg), %eax
+    //   call *___tls_get_addr@got(%reg)
+    const uint8_t inst[] = {
+        0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax
+        0x03, 0x83, 0,    0,    0,    0,    // addl x@gottpoff(%ebx), %eax
+    };
+    uint8_t *w = loc[-2] == 0x04 ? loc - 3 : loc - 2;
+    memcpy(w, inst, sizeof(inst));
+    write32le(w + 8, val);
+  } else if (rel.type == R_386_TLS_GOTDESC) {
+    // Convert leal x@tlsdesc(%ebx), %eax to movl x@gotntpoff(%ebx), %eax.
+    if (memcmp(loc - 2, "\x8d\x83", 2)) {
+      error(getErrorLocation(loc - 2) +
+            "R_386_TLS_GOTDESC must be used in leal x@tlsdesc(%ebx), %eax");
+      return;
+    }
+    loc[-2] = 0x8b;
+    write32le(loc, val);
+  } else {
+    // Convert call *x@tlsdesc(%eax) to xchg ax, ax.
+    assert(rel.type == R_386_TLS_DESC_CALL);
+    loc[0] = 0x66;
+    loc[1] = 0x90;
+  }
 }
 
 // In some conditions, relocations can be optimized to avoid using GOT.
 // This function does that for Initial Exec to Local Exec case.
-void X86::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
-                         uint64_t val) const {
+static void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) {
   // Ulrich's document section 6.2 says that @gotntpoff can
   // be used with MOVL or ADDL instructions.
   // @indntpoff is similar to @gotntpoff, but for use in
@@ -412,28 +450,68 @@ void X86::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
   write32le(loc, val);
 }
 
-void X86::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
-                         uint64_t val) const {
+static void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) {
   if (rel.type == R_386_TLS_LDO_32) {
     write32le(loc, val);
     return;
   }
 
+  if (loc[4] == 0xe8) {
+    // Convert
+    //   leal x(%reg),%eax
+    //   call ___tls_get_addr@plt
+    // to
+    const uint8_t inst[] = {
+        0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0,%eax
+        0x90,                               // nop
+        0x8d, 0x74, 0x26, 0x00,             // leal 0(%esi,1),%esi
+    };
+    memcpy(loc - 2, inst, sizeof(inst));
+    return;
+  }
+
   // Convert
-  //   leal foo(%reg),%eax
-  //   call ___tls_get_addr
+  //   leal x(%reg),%eax
+  //   call *___tls_get_addr@got(%reg)
   // to
-  //   movl %gs:0,%eax
-  //   nop
-  //   leal 0(%esi,1),%esi
   const uint8_t inst[] = {
       0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0,%eax
-      0x90,                               // nop
-      0x8d, 0x74, 0x26, 0x00,             // leal 0(%esi,1),%esi
+      0x8d, 0xb6, 0x00, 0x00, 0x00, 0x00, // leal (%esi),%esi
   };
   memcpy(loc - 2, inst, sizeof(inst));
 }
 
+void X86::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
+  uint64_t secAddr = sec.getOutputSection()->addr;
+  if (auto *s = dyn_cast<InputSection>(&sec))
+    secAddr += s->outSecOff;
+  for (const Relocation &rel : sec.relocs()) {
+    uint8_t *loc = buf + rel.offset;
+    const uint64_t val = SignExtend64(
+        sec.getRelocTargetVA(sec.file, rel.type, rel.addend,
+                             secAddr + rel.offset, *rel.sym, rel.expr),
+        32);
+    switch (rel.expr) {
+    case R_RELAX_TLS_GD_TO_IE_GOTPLT:
+      relaxTlsGdToIe(loc, rel, val);
+      continue;
+    case R_RELAX_TLS_GD_TO_LE:
+    case R_RELAX_TLS_GD_TO_LE_NEG:
+      relaxTlsGdToLe(loc, rel, val);
+      continue;
+    case R_RELAX_TLS_LD_TO_LE:
+      relaxTlsLdToLe(loc, rel, val);
+      break;
+    case R_RELAX_TLS_IE_TO_LE:
+      relaxTlsIeToLe(loc, rel, val);
+      continue;
+    default:
+      relocate(loc, rel, val);
+      break;
+    }
+  }
+}
+
 // If Intel Indirect Branch Tracking is enabled, we have to emit special PLT
 // entries containing endbr32 instructions. A PLT entry will be split into two
 // parts, one in .plt.sec (writePlt), and the other in .plt (writeIBTPlt).
@@ -454,7 +532,7 @@ IntelIBT::IntelIBT() { pltHeaderSize = 0; }
 
 void IntelIBT::writeGotPlt(uint8_t *buf, const Symbol &s) const {
   uint64_t va =
-      in.ibtPlt->getVA() + IBTPltHeaderSize + s.pltIndex * pltEntrySize;
+      in.ibtPlt->getVA() + IBTPltHeaderSize + s.getPltIdx() * pltEntrySize;
   write32le(buf, va);
 }
 
@@ -552,7 +630,7 @@ void RetpolinePic::writePltHeader(uint8_t *buf) const {
 
 void RetpolinePic::writePlt(uint8_t *buf, const Symbol &sym,
                             uint64_t pltEntryAddr) const {
-  unsigned relOff = in.relaPlt->entsize * sym.pltIndex;
+  unsigned relOff = in.relaPlt->entsize * sym.getPltIdx();
   const uint8_t insn[] = {
       0x50,                            // pushl %eax
       0x8b, 0x83, 0,    0,    0,    0, // mov foo@GOT(%ebx), %eax
@@ -611,7 +689,7 @@ void RetpolineNoPic::writePltHeader(uint8_t *buf) const {
 
 void RetpolineNoPic::writePlt(uint8_t *buf, const Symbol &sym,
                               uint64_t pltEntryAddr) const {
-  unsigned relOff = in.relaPlt->entsize * sym.pltIndex;
+  unsigned relOff = in.relaPlt->entsize * sym.getPltIdx();
   const uint8_t insn[] = {
       0x50,                         // 0:  pushl %eax
       0xa1, 0,    0,    0,    0,    // 1:  mov foo_in_GOT, %eax
index f85d0fb..8e6a746 100644 (file)
@@ -2,6 +2,22 @@ set(LLVM_TARGET_DEFINITIONS Options.td)
 tablegen(LLVM Options.inc -gen-opt-parser-defs)
 add_public_tablegen_target(ELFOptionsTableGen)
 
+if(LLVM_ENABLE_ZLIB)
+  set(imported_libs ZLIB::ZLIB)
+endif()
+
+if(LLVM_ENABLE_ZSTD)
+  if(TARGET zstd::libzstd_shared AND NOT LLVM_USE_STATIC_ZSTD)
+    set(zstd_target zstd::libzstd_shared)
+  else()
+    set(zstd_target zstd::libzstd_static)
+  endif()
+endif()
+
+if(LLVM_ENABLE_ZSTD)
+  list(APPEND imported_libs ${zstd_target})
+endif()
+
 add_lld_library(lldELF
   AArch64ErrataFix.cpp
   Arch/AArch64.cpp
@@ -55,9 +71,11 @@ add_lld_library(lldELF
   Option
   Passes
   Support
+  TargetParser
 
   LINK_LIBS
   lldCommon
+  ${imported_libs}
   ${LLVM_PTHREAD_LIB}
 
   DEPENDS
index 15da4d2..ff72731 100644 (file)
 //===----------------------------------------------------------------------===//
 
 #include "CallGraphSort.h"
-#include "OutputSections.h"
-#include "SymbolTable.h"
+#include "InputFiles.h"
+#include "InputSection.h"
 #include "Symbols.h"
+#include "llvm/Support/FileSystem.h"
 
 #include <numeric>
 
@@ -114,8 +115,8 @@ CallGraphSort::CallGraphSort() {
 
   // Create the graph.
   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);
+    const auto *fromSB = cast<InputSectionBase>(c.first.first);
+    const auto *toSB = cast<InputSectionBase>(c.first.second);
     uint64_t weight = c.second;
 
     // Ignore edges between input sections belonging to different output
@@ -155,7 +156,7 @@ static bool isNewDensityBad(Cluster &a, Cluster &b) {
 // Find the leader of V's belonged cluster (represented as an equivalence
 // class). We apply union-find path-halving technique (simple to implement) in
 // the meantime as it decreases depths and the time complexity.
-static int getLeader(std::vector<int> &leaders, int v) {
+static int getLeader(int *leaders, int v) {
   while (leaders[v] != v) {
     leaders[v] = leaders[leaders[v]];
     v = leaders[v];
@@ -180,9 +181,9 @@ static void mergeClusters(std::vector<Cluster> &cs, Cluster &into, int intoIdx,
 // then sort the clusters by density.
 DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
   std::vector<int> sorted(clusters.size());
-  std::vector<int> leaders(clusters.size());
+  std::unique_ptr<int[]> leaders(new int[clusters.size()]);
 
-  std::iota(leaders.begin(), leaders.end(), 0);
+  std::iota(leaders.get(), leaders.get() + clusters.size(), 0);
   std::iota(sorted.begin(), sorted.end(), 0);
   llvm::stable_sort(sorted, [&](int a, int b) {
     return clusters[a].getDensity() > clusters[b].getDensity();
@@ -197,7 +198,7 @@ DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
     if (c.bestPred.from == -1 || c.bestPred.weight * 10 <= c.initialWeight)
       continue;
 
-    int predL = getLeader(leaders, c.bestPred.from);
+    int predL = getLeader(leaders.get(), c.bestPred.from);
     if (l == predL)
       continue;
 
@@ -259,7 +260,7 @@ DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
   return orderMap;
 }
 
-// Sort sections by the profile data provided by -callgraph-profile-file
+// Sort sections by the profile data provided by --callgraph-profile-file.
 //
 // This first builds a call graph based on the profile data then merges sections
 // according to the C³ heuristic. All clusters are then sorted by a density
index 5a09227..4997cb1 100644 (file)
 
 #include "llvm/ADT/DenseMap.h"
 
-namespace lld {
-namespace elf {
+namespace lld::elf {
 class InputSectionBase;
 
 llvm::DenseMap<const InputSectionBase *, int> computeCallGraphProfileOrder();
-} // namespace elf
-} // namespace lld
+} // namespace lld::elf
 
 #endif
index 707a6eb..a56454c 100644 (file)
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// The -gdb-index option instructs the linker to emit a .gdb_index section.
+// The --gdb-index option instructs the linker to emit a .gdb_index section.
 // The section contains information to make gdb startup faster.
 // The format of the section is described at
 // https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html.
@@ -14,8 +14,8 @@
 //===----------------------------------------------------------------------===//
 
 #include "DWARF.h"
+#include "InputSection.h"
 #include "Symbols.h"
-#include "Target.h"
 #include "lld/Common/Memory.h"
 #include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h"
 #include "llvm/Object/ELFObjectFile.h"
@@ -27,11 +27,9 @@ using namespace lld::elf;
 
 template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *obj) {
   // Get the ELF sections to retrieve sh_flags. See the SHF_GROUP comment below.
-  ArrayRef<typename ELFT::Shdr> objSections =
-      CHECK(obj->getObj().sections(), obj);
+  ArrayRef<typename ELFT::Shdr> objSections = obj->template getELFShdrs<ELFT>();
   assert(objSections.size() == obj->getSections().size());
-  for (auto it : llvm::enumerate(obj->getSections())) {
-    InputSectionBase *sec = it.value();
+  for (auto [i, sec] : llvm::enumerate(obj->getSections())) {
     if (!sec)
       continue;
 
@@ -46,19 +44,19 @@ template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *obj) {
                 .Case(".debug_str_offsets", &strOffsetsSection)
                 .Case(".debug_line", &lineSection)
                 .Default(nullptr)) {
-      m->Data = toStringRef(sec->data());
+      m->Data = toStringRef(sec->contentMaybeDecompress());
       m->sec = sec;
       continue;
     }
 
     if (sec->name == ".debug_abbrev")
-      abbrevSection = toStringRef(sec->data());
+      abbrevSection = toStringRef(sec->contentMaybeDecompress());
     else if (sec->name == ".debug_str")
-      strSection = toStringRef(sec->data());
+      strSection = toStringRef(sec->contentMaybeDecompress());
     else if (sec->name == ".debug_line_str")
-      lineStrSection = toStringRef(sec->data());
+      lineStrSection = toStringRef(sec->contentMaybeDecompress());
     else if (sec->name == ".debug_info" &&
-             !(objSections[it.index()].sh_flags & ELF::SHF_GROUP)) {
+             !(objSections[i].sh_flags & ELF::SHF_GROUP)) {
       // In DWARF v5, -fdebug-types-section places type units in .debug_info
       // sections in COMDAT groups. They are not compile units and thus should
       // be ignored for .gdb_index/diagnostics purposes.
@@ -68,7 +66,7 @@ template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *obj) {
       // need to perform a lightweight parsing. We drop the SHF_GROUP flag when
       // the InputSection was created, so we need to retrieve sh_flags from the
       // associated ELF section header.
-      infoSection.Data = toStringRef(sec->data());
+      infoSection.Data = toStringRef(sec->contentMaybeDecompress());
       infoSection.sec = sec;
     }
   }
@@ -103,13 +101,13 @@ template <class ELFT> struct LLDRelocationResolver<Elf_Rel_Impl<ELFT, false>> {
 // to llvm since it has no idea about InputSection.
 template <class ELFT>
 template <class RelTy>
-Optional<RelocAddrEntry>
+std::optional<RelocAddrEntry>
 LLDDwarfObj<ELFT>::findAux(const InputSectionBase &sec, uint64_t pos,
                            ArrayRef<RelTy> rels) const {
   auto it =
       partition_point(rels, [=](const RelTy &a) { return a.r_offset < pos; });
   if (it == rels.end() || it->r_offset != pos)
-    return None;
+    return std::nullopt;
   const RelTy &rel = *it;
 
   const ObjFile<ELFT> *file = sec.getFile<ELFT>();
@@ -129,17 +127,18 @@ LLDDwarfObj<ELFT>::findAux(const InputSectionBase &sec, uint64_t pos,
   DataRefImpl d;
   d.p = getAddend<ELFT>(rel);
   return RelocAddrEntry{secIndex, RelocationRef(d, nullptr),
-                        val,      Optional<object::RelocationRef>(),
+                        val,      std::optional<object::RelocationRef>(),
                         0,        LLDRelocationResolver<RelTy>::resolve};
 }
 
 template <class ELFT>
-Optional<RelocAddrEntry> LLDDwarfObj<ELFT>::find(const llvm::DWARFSection &s,
-                                                 uint64_t pos) const {
+std::optional<RelocAddrEntry>
+LLDDwarfObj<ELFT>::find(const llvm::DWARFSection &s, uint64_t pos) const {
   auto &sec = static_cast<const LLDDWARFSection &>(s);
-  if (sec.sec->areRelocsRela)
-    return findAux(*sec.sec, pos, sec.sec->template relas<ELFT>());
-  return findAux(*sec.sec, pos, sec.sec->template rels<ELFT>());
+  const RelsOrRelas<ELFT> rels = sec.sec->template relsOrRelas<ELFT>();
+  if (rels.areRelocsRel())
+    return findAux(*sec.sec, pos, rels.rels);
+  return findAux(*sec.sec, pos, rels.relas);
 }
 
 template class elf::LLDDwarfObj<ELF32LE>;
index 900c63d..9a79939 100644 (file)
@@ -14,8 +14,7 @@
 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
 #include "llvm/Object/ELF.h"
 
-namespace lld {
-namespace elf {
+namespace lld::elf {
 
 class InputSection;
 
@@ -77,14 +76,14 @@ public:
     return ELFT::TargetEndianness == llvm::support::little;
   }
 
-  llvm::Optional<llvm::RelocAddrEntry> find(const llvm::DWARFSection &sec,
-                                            uint64_t pos) const override;
+  std::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;
+  std::optional<llvm::RelocAddrEntry> findAux(const InputSectionBase &sec,
+                                              uint64_t pos,
+                                              ArrayRef<RelTy> rels) const;
 
   LLDDWARFSection gnuPubnamesSection;
   LLDDWARFSection gnuPubtypesSection;
@@ -100,7 +99,6 @@ private:
   StringRef lineStrSection;
 };
 
-} // namespace elf
-} // namespace lld
+} // namespace lld::elf
 
 #endif
index 96d0400..16cb522 100644 (file)
@@ -9,47 +9,14 @@
 #ifndef LLD_ELF_DRIVER_H
 #define LLD_ELF_DRIVER_H
 
-#include "LTO.h"
-#include "SymbolTable.h"
 #include "lld/Common/LLVM.h"
-#include "lld/Common/Reproduce.h"
-#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/StringSet.h"
 #include "llvm/Option/ArgList.h"
-#include "llvm/Support/raw_ostream.h"
-
-namespace lld {
-namespace elf {
-
-extern class LinkerDriver *driver;
-
-class LinkerDriver {
-public:
-  void linkerMain(ArrayRef<const char *> args);
-  void addFile(StringRef path, bool withLOption);
-  void addLibrary(StringRef name);
-
-private:
-  void createFiles(llvm::opt::InputArgList &args);
-  void inferMachineType();
-  template <class ELFT> void link(llvm::opt::InputArgList &args);
-  template <class ELFT> void compileBitcodeFiles();
-
-  // True if we are in --whole-archive and --no-whole-archive.
-  bool inWholeArchive = false;
-
-  // True if we are in --start-lib and --end-lib.
-  bool inLib = false;
-
-  // For LTO.
-  std::unique_ptr<BitcodeCompiler> lto;
-
-  std::vector<InputFile *> files;
-};
+#include <optional>
 
+namespace lld::elf {
 // Parses command line options.
-class ELFOptTable : public llvm::opt::OptTable {
+class ELFOptTable : public llvm::opt::GenericOptTable {
 public:
   ELFOptTable();
   llvm::opt::InputArgList parse(ArrayRef<const char *> argv);
@@ -66,12 +33,11 @@ enum {
 void printHelp();
 std::string createResponseFile(const llvm::opt::InputArgList &args);
 
-llvm::Optional<std::string> findFromSearchPaths(StringRef path);
-llvm::Optional<std::string> searchScript(StringRef path);
-llvm::Optional<std::string> searchLibraryBaseName(StringRef path);
-llvm::Optional<std::string> searchLibrary(StringRef path);
+std::optional<std::string> findFromSearchPaths(StringRef path);
+std::optional<std::string> searchScript(StringRef path);
+std::optional<std::string> searchLibraryBaseName(StringRef path);
+std::optional<std::string> searchLibrary(StringRef path);
 
-} // namespace elf
-} // namespace lld
+} // namespace lld::elf
 
 #endif
index 578c640..db58c54 100644 (file)
@@ -36,14 +36,13 @@ namespace {
 class EhReader {
 public:
   EhReader(InputSectionBase *s, ArrayRef<uint8_t> d) : isec(s), d(d) {}
-  size_t readEhRecordSize();
   uint8_t getFdeEncoding();
   bool hasLSDA();
 
 private:
   template <class P> void failOn(const P *loc, const Twine &msg) {
     fatal("corrupted .eh_frame: " + msg + "\n>>> defined in " +
-          isec->getObjMsg((const uint8_t *)loc - isec->data().data()));
+          isec->getObjMsg((const uint8_t *)loc - isec->content().data()));
   }
 
   uint8_t readByte();
@@ -58,28 +57,6 @@ private:
 };
 }
 
-size_t elf::readEhRecordSize(InputSectionBase *s, size_t off) {
-  return EhReader(s, s->data().slice(off)).readEhRecordSize();
-}
-
-// .eh_frame section is a sequence of records. Each record starts with
-// a 4 byte length field. This function reads the length.
-size_t EhReader::readEhRecordSize() {
-  if (d.size() < 4)
-    failOn(d.data(), "CIE/FDE too small");
-
-  // First 4 bytes of CIE/FDE is the size of the record.
-  // If it is 0xFFFFFFFF, the next 8 bytes contain the size instead,
-  // but we do not support that format yet.
-  uint64_t v = read32(d.data());
-  if (v == UINT32_MAX)
-    failOn(d.data(), "CIE/FDE too large");
-  uint64_t size = v + 4;
-  if (size > d.size())
-    failOn(d.data(), "CIE/FDE ends past the end of the section");
-  return size;
-}
-
 // Read a byte and advance D by one byte.
 uint8_t EhReader::readByte() {
   if (d.empty())
@@ -194,7 +171,7 @@ uint8_t EhReader::getFdeEncoding() {
       readByte();
     else if (c == 'P')
       skipAugP();
-    else if (c != 'B' && c != 'S')
+    else if (c != 'B' && c != 'S' && c != 'G')
       failOn(aug.data(), "unknown .eh_frame augmentation string: " + aug);
   }
   return DW_EH_PE_absptr;
@@ -211,7 +188,7 @@ bool EhReader::hasLSDA() {
       skipAugP();
     else if (c == 'R')
       readByte();
-    else if (c != 'B' && c != 'S')
+    else if (c != 'B' && c != 'S' && c != 'G')
       failOn(aug.data(), "unknown .eh_frame augmentation string: " + aug);
   }
   return false;
index e448323..9526416 100644 (file)
 
 #include "lld/Common/LLVM.h"
 
-namespace lld {
-namespace elf {
-class InputSectionBase;
+namespace lld::elf {
 struct EhSectionPiece;
 
-size_t readEhRecordSize(InputSectionBase *s, size_t off);
 uint8_t getFdeEncoding(EhSectionPiece *p);
 bool hasLSDA(const EhSectionPiece &p);
-} // namespace elf
-} // namespace lld
+}
 
 #endif
index 5cf944d..c2b0ce9 100644 (file)
 
 #include "ICF.h"
 #include "Config.h"
-#include "EhFrame.h"
+#include "InputFiles.h"
 #include "LinkerScript.h"
 #include "OutputSections.h"
 #include "SymbolTable.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
-#include "Writer.h"
-#include "llvm/ADT/StringExtras.h"
 #include "llvm/BinaryFormat/ELF.h"
 #include "llvm/Object/ELF.h"
 #include "llvm/Support/Parallel.h"
@@ -122,7 +120,7 @@ private:
 
   void forEachClass(llvm::function_ref<void(size_t, size_t)> fn);
 
-  std::vector<InputSection *> sections;
+  SmallVector<InputSection *, 0> sections;
 
   // We repeat the main loop while `Repeat` is true.
   std::atomic<bool> repeat;
@@ -239,6 +237,8 @@ template <class ELFT>
 template <class RelTy>
 bool ICF<ELFT>::constantEq(const InputSection *secA, ArrayRef<RelTy> ra,
                            const InputSection *secB, ArrayRef<RelTy> rb) {
+  if (ra.size() != rb.size())
+    return false;
   for (size_t i = 0; i < ra.size(); ++i) {
     if (ra[i].r_offset != rb[i].r_offset ||
         ra[i].getType(config->isMips64EL) != rb[i].getType(config->isMips64EL))
@@ -312,8 +312,8 @@ bool ICF<ELFT>::constantEq(const InputSection *secA, ArrayRef<RelTy> ra,
 // except relocation targets.
 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())
+  if (a->flags != b->flags || a->getSize() != b->getSize() ||
+      a->content() != b->content())
     return false;
 
   // If two sections have different output sections, we cannot merge them.
@@ -321,10 +321,11 @@ bool ICF<ELFT>::equalsConstant(const InputSection *a, const InputSection *b) {
   if (a->getParent() != b->getParent())
     return false;
 
-  if (a->areRelocsRela)
-    return constantEq(a, a->template relas<ELFT>(), b,
-                      b->template relas<ELFT>());
-  return constantEq(a, a->template rels<ELFT>(), b, b->template rels<ELFT>());
+  const RelsOrRelas<ELFT> ra = a->template relsOrRelas<ELFT>();
+  const RelsOrRelas<ELFT> rb = b->template relsOrRelas<ELFT>();
+  return ra.areRelocsRel() || rb.areRelocsRel()
+             ? constantEq(a, ra.rels, b, rb.rels)
+             : constantEq(a, ra.relas, b, rb.relas);
 }
 
 // Compare two lists of relocations. Returns true if all pairs of
@@ -369,10 +370,11 @@ bool ICF<ELFT>::variableEq(const InputSection *secA, ArrayRef<RelTy> ra,
 // Compare "moving" part of two InputSections, namely relocation targets.
 template <class ELFT>
 bool ICF<ELFT>::equalsVariable(const InputSection *a, const InputSection *b) {
-  if (a->areRelocsRela)
-    return variableEq(a, a->template relas<ELFT>(), b,
-                      b->template relas<ELFT>());
-  return variableEq(a, a->template rels<ELFT>(), b, b->template rels<ELFT>());
+  const RelsOrRelas<ELFT> ra = a->template relsOrRelas<ELFT>();
+  const RelsOrRelas<ELFT> rb = b->template relsOrRelas<ELFT>();
+  return ra.areRelocsRel() || rb.areRelocsRel()
+             ? variableEq(a, ra.rels, b, rb.rels)
+             : variableEq(a, ra.relas, b, rb.relas);
 }
 
 template <class ELFT> size_t ICF<ELFT>::findBoundary(size_t begin, size_t end) {
@@ -422,11 +424,11 @@ void ICF<ELFT>::forEachClass(llvm::function_ref<void(size_t, size_t)> fn) {
   boundaries[0] = 0;
   boundaries[numShards] = sections.size();
 
-  parallelForEachN(1, numShards, [&](size_t i) {
+  parallelFor(1, numShards, [&](size_t i) {
     boundaries[i] = findBoundary((i - 1) * step, sections.size());
   });
 
-  parallelForEachN(1, numShards + 1, [&](size_t i) {
+  parallelFor(1, numShards + 1, [&](size_t i) {
     if (boundaries[i - 1] < boundaries[i])
       forEachClassRange(boundaries[i - 1], boundaries[i], fn);
   });
@@ -459,8 +461,9 @@ template <class ELFT> void ICF<ELFT>::run() {
   // Compute isPreemptible early. We may add more symbols later, so this loop
   // cannot be merged with the later computeIsPreemptible() pass which is used
   // by scanRelocations().
-  for (Symbol *sym : symtab->symbols())
-    sym->isPreemptible = computeIsPreemptible(*sym);
+  if (config->hasDynSymTab)
+    for (Symbol *sym : symtab.getSymbols())
+      sym->isPreemptible = computeIsPreemptible(*sym);
 
   // Two text sections may have identical content and relocations but different
   // LSDA, e.g. the two functions may have catch blocks of different types. If a
@@ -476,9 +479,9 @@ template <class ELFT> void ICF<ELFT>::run() {
         [&](InputSection &s) { s.eqClass[0] = s.eqClass[1] = ++uniqueId; });
 
   // Collect sections to merge.
-  for (InputSectionBase *sec : inputSections) {
-    auto *s = cast<InputSection>(sec);
-    if (s->eqClass[0] == 0) {
+  for (InputSectionBase *sec : ctx.inputSections) {
+    auto *s = dyn_cast<InputSection>(sec);
+    if (s && s->eqClass[0] == 0) {
       if (isEligible(s))
         sections.push_back(s);
       else
@@ -491,7 +494,7 @@ 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 unique IDs.
-    s->eqClass[0] = xxHash64(s->data()) | (1U << 31);
+    s->eqClass[0] = xxHash64(s->content()) | (1U << 31);
   });
 
   // Perform 2 rounds of relocation hash propagation. 2 is an empirical value to
@@ -499,10 +502,11 @@ template <class ELFT> void ICF<ELFT>::run() {
   // a large time complexity will have less work to do.
   for (unsigned cnt = 0; cnt != 2; ++cnt) {
     parallelForEach(sections, [&](InputSection *s) {
-      if (s->areRelocsRela)
-        combineRelocHashes<ELFT>(cnt, s, s->template relas<ELFT>());
+      const RelsOrRelas<ELFT> rels = s->template relsOrRelas<ELFT>();
+      if (rels.areRelocsRel())
+        combineRelocHashes<ELFT>(cnt, s, rels.rels);
       else
-        combineRelocHashes<ELFT>(cnt, s, s->template rels<ELFT>());
+        combineRelocHashes<ELFT>(cnt, s, rels.relas);
     });
   }
 
@@ -547,12 +551,28 @@ template <class ELFT> void ICF<ELFT>::run() {
     }
   });
 
+  // Change Defined symbol's section field to the canonical one.
+  auto fold = [](Symbol *sym) {
+    if (auto *d = dyn_cast<Defined>(sym))
+      if (auto *sec = dyn_cast_or_null<InputSection>(d->section))
+        if (sec->repl != d->section) {
+          d->section = sec->repl;
+          d->folded = true;
+        }
+  };
+  for (Symbol *sym : symtab.getSymbols())
+    fold(sym);
+  parallelForEach(ctx.objectFiles, [&](ELFFileBase *file) {
+    for (Symbol *sym : file->getLocalSymbols())
+      fold(sym);
+  });
+
   // InputSectionDescription::sections is populated by processSectionCommands().
   // ICF may fold some input sections assigned to output sections. Remove them.
-  for (BaseCommand *base : script->sectionCommands)
-    if (auto *sec = dyn_cast<OutputSection>(base))
-      for (BaseCommand *sub_base : sec->sectionCommands)
-        if (auto *isd = dyn_cast<InputSectionDescription>(sub_base))
+  for (SectionCommand *cmd : script->sectionCommands)
+    if (auto *osd = dyn_cast<OutputDesc>(cmd))
+      for (SectionCommand *subCmd : osd->osec.commands)
+        if (auto *isd = dyn_cast<InputSectionDescription>(subCmd))
           llvm::erase_if(isd->sections,
                          [](InputSection *isec) { return !isec->isLive(); });
 }
index ed828fc..3246cc3 100644 (file)
@@ -9,12 +9,10 @@
 #ifndef LLD_ELF_ICF_H
 #define LLD_ELF_ICF_H
 
-namespace lld {
-namespace elf {
+namespace lld::elf {
 
 template <class ELFT> void doIcf();
 
-} // namespace elf
-} // namespace lld
+}
 
 #endif
index c914d0b..356ccda 100644 (file)
@@ -9,29 +9,36 @@
 #ifndef LLD_ELF_INPUT_SECTION_H
 #define LLD_ELF_INPUT_SECTION_H
 
-#include "Config.h"
 #include "Relocations.h"
-#include "Thunks.h"
+#include "lld/Common/CommonLinkerContext.h"
 #include "lld/Common/LLVM.h"
+#include "lld/Common/Memory.h"
 #include "llvm/ADT/CachedHashString.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/TinyPtrVector.h"
 #include "llvm/Object/ELF.h"
+#include "llvm/Support/Compiler.h"
 
 namespace lld {
 namespace elf {
 
+class InputFile;
 class Symbol;
-struct SectionPiece;
 
 class Defined;
 struct Partition;
 class SyntheticSection;
-class MergeSyntheticSection;
 template <class ELFT> class ObjFile;
 class OutputSection;
 
-extern std::vector<Partition> partitions;
+LLVM_LIBRARY_VISIBILITY extern std::vector<Partition> partitions;
+
+// Returned by InputSectionBase::relsOrRelas. At least one member is empty.
+template <class ELFT> struct RelsOrRelas {
+  ArrayRef<typename ELFT::Rel> rels;
+  ArrayRef<typename ELFT::Rela> relas;
+  bool areRelocsRel() const { return rels.size(); }
+};
 
 // This is the base class of all sections that lld handles. Some are sections in
 // input files, some are sections in the produced output file and some exist
@@ -39,19 +46,10 @@ extern std::vector<Partition> partitions;
 // sections.
 class SectionBase {
 public:
-  enum Kind { Regular, EHFrame, Merge, Synthetic, Output };
+  enum Kind { Regular, Synthetic, EHFrame, Merge, Output };
 
   Kind kind() const { return (Kind)sectionKind; }
 
-  StringRef name;
-
-  // This pointer points to the "real" instance of this instance.
-  // Usually Repl == this. However, if ICF merges two sections,
-  // Repl pointer of one section points to another section. So,
-  // if you need to get a pointer to this instance, do not use
-  // this but instead this->Repl.
-  SectionBase *repl;
-
   uint8_t sectionKind : 3;
 
   // The next two bit fields are only used by InputSectionBase, but we
@@ -62,17 +60,19 @@ public:
   // Set for sections that should not be folded by ICF.
   uint8_t keepUnique : 1;
 
+  uint8_t partition = 1;
+  uint32_t type;
+  StringRef name;
+
   // The 1-indexed partition that this section is assigned to by the garbage
   // collector, or 0 if this section is dead. Normally there is only one
   // partition, so this will either be 0 or 1.
-  uint8_t partition;
   elf::Partition &getPartition() const;
 
   // These corresponds to the fields in Elf_Shdr.
-  uint32_t alignment;
   uint64_t flags;
-  uint64_t entsize;
-  uint32_t type;
+  uint32_t addralign;
+  uint32_t entsize;
   uint32_t link;
   uint32_t info;
 
@@ -92,14 +92,16 @@ public:
   void markDead() { partition = 0; }
 
 protected:
-  SectionBase(Kind sectionKind, StringRef name, uint64_t flags,
-              uint64_t entsize, uint64_t alignment, uint32_t type,
-              uint32_t info, uint32_t link)
-      : name(name), repl(this), sectionKind(sectionKind), bss(false),
-        keepUnique(false), partition(0), alignment(alignment), flags(flags),
-        entsize(entsize), type(type), link(link), info(info) {}
+  constexpr SectionBase(Kind sectionKind, StringRef name, uint64_t flags,
+                        uint32_t entsize, uint32_t addralign, uint32_t type,
+                        uint32_t info, uint32_t link)
+      : sectionKind(sectionKind), bss(false), keepUnique(false), type(type),
+        name(name), flags(flags), addralign(addralign), entsize(entsize),
+        link(link), info(info) {}
 };
 
+struct RISCVRelaxAux;
+
 // This corresponds to a section of an input file.
 class InputSectionBase : public SectionBase {
 public:
@@ -109,80 +111,74 @@ public:
 
   InputSectionBase(InputFile *file, uint64_t flags, uint32_t type,
                    uint64_t entsize, uint32_t link, uint32_t info,
-                   uint32_t alignment, ArrayRef<uint8_t> data, StringRef name,
+                   uint32_t addralign, ArrayRef<uint8_t> data, StringRef name,
                    Kind sectionKind);
 
   static bool classof(const SectionBase *s) { return s->kind() != Output; }
 
-  // Relocations that refer to this section.
-  unsigned numRelocations : 31;
-  unsigned areRelocsRela : 1;
-  const void *firstRelocation = nullptr;
-
   // The file which contains this section. Its dynamic type is always
   // ObjFile<ELFT>, but in order to avoid ELFT, we use InputFile as
   // its static type.
   InputFile *file;
 
+  // Input sections are part of an output section. Special sections
+  // like .eh_frame and merge sections are first combined into a
+  // synthetic section that is then added to an output section. In all
+  // cases this points one level up.
+  SectionBase *parent = nullptr;
+
+  // Section index of the relocation section if exists.
+  uint32_t relSecIdx = 0;
+
   template <class ELFT> ObjFile<ELFT> *getFile() const {
     return cast_or_null<ObjFile<ELFT>>(file);
   }
 
-  // If basic block sections are enabled, many code sections could end up with
-  // one or two jump instructions at the end that could be relaxed to a smaller
-  // instruction. The members below help trimming the trailing jump instruction
-  // and shrinking a section.
-  unsigned bytesDropped = 0;
+  // Used by --optimize-bb-jumps and RISC-V linker relaxation temporarily to
+  // indicate the number of bytes which is not counted in the size. This should
+  // be reset to zero after uses.
+  uint16_t bytesDropped = 0;
+
+  mutable bool compressed = false;
 
   // Whether the section needs to be padded with a NOP filler due to
   // deleteFallThruJmpInsn.
   bool nopFiller = false;
 
-  void drop_back(uint64_t num) { bytesDropped += num; }
+  void drop_back(unsigned num) {
+    assert(bytesDropped + num < 256);
+    bytesDropped += num;
+  }
 
   void push_back(uint64_t num) {
     assert(bytesDropped >= num);
     bytesDropped -= num;
   }
 
+  mutable const uint8_t *content_;
+  uint64_t size;
+
   void trim() {
     if (bytesDropped) {
-      rawData = rawData.drop_back(bytesDropped);
+      size -= bytesDropped;
       bytesDropped = 0;
     }
   }
 
-  ArrayRef<uint8_t> data() const {
-    if (uncompressedSize >= 0)
-      uncompress();
-    return rawData;
+  ArrayRef<uint8_t> content() const {
+    return ArrayRef<uint8_t>(content_, size);
+  }
+  ArrayRef<uint8_t> contentMaybeDecompress() const {
+    if (compressed)
+      decompress();
+    return content();
   }
-
-  uint64_t getOffsetInFile() const;
-
-  // Input sections are part of an output section. Special sections
-  // like .eh_frame and merge sections are first combined into a
-  // synthetic section that is then added to an output section. In all
-  // cases this points one level up.
-  SectionBase *parent = nullptr;
 
   // The next member in the section group if this section is in a group. This is
   // used by --gc-sections.
   InputSectionBase *nextInSectionGroup = nullptr;
 
-  template <class ELFT> ArrayRef<typename ELFT::Rel> rels() const {
-    assert(!areRelocsRela);
-    return llvm::makeArrayRef(
-        static_cast<const typename ELFT::Rel *>(firstRelocation),
-        numRelocations);
-  }
-
-  template <class ELFT> ArrayRef<typename ELFT::Rela> relas() const {
-    assert(areRelocsRela);
-    return llvm::makeArrayRef(
-        static_cast<const typename ELFT::Rela *>(firstRelocation),
-        numRelocations);
-  }
+  template <class ELFT> RelsOrRelas<ELFT> relsOrRelas() const;
 
   // InputSections that are dependent on us (reverse dependency for GC)
   llvm::TinyPtrVector<InputSection *> dependentSections;
@@ -194,11 +190,10 @@ public:
 
   // Get the function symbol that encloses this offset from within the
   // section.
-  template <class ELFT>
   Defined *getEnclosingFunction(uint64_t offset);
 
   // Returns a source location string. Used to construct an error message.
-  template <class ELFT> std::string getLocation(uint64_t offset);
+  std::string getLocation(uint64_t offset);
   std::string getSrcMsg(const Symbol &sym, uint64_t offset);
   std::string getObjMsg(uint64_t offset);
 
@@ -206,7 +201,6 @@ public:
   // relocations, assuming that Buf points to this section's copy in
   // the mmap'ed output buffer.
   template <class ELFT> void relocate(uint8_t *buf, uint8_t *bufEnd);
-  void relocateAlloc(uint8_t *buf, uint8_t *bufEnd);
   static uint64_t getRelocTargetVA(const InputFile *File, RelType Type,
                                    int64_t A, uint64_t P, const Symbol &Sym,
                                    RelExpr Expr);
@@ -216,11 +210,24 @@ public:
   // This vector contains such "cooked" relocations.
   SmallVector<Relocation, 0> relocations;
 
-  // These are modifiers to jump instructions that are necessary when basic
-  // block sections are enabled.  Basic block sections creates opportunities to
-  // relax jump instructions at basic block boundaries after reordering the
-  // basic blocks.
-  SmallVector<JumpInstrMod, 0> jumpInstrMods;
+  void addReloc(const Relocation &r) { relocations.push_back(r); }
+  MutableArrayRef<Relocation> relocs() { return relocations; }
+  ArrayRef<Relocation> relocs() const { return relocations; }
+
+  union {
+    // These are modifiers to jump instructions that are necessary when basic
+    // block sections are enabled.  Basic block sections creates opportunities
+    // to relax jump instructions at basic block boundaries after reordering the
+    // basic blocks.
+    JumpInstrMod *jumpInstrMod = nullptr;
+
+    // Auxiliary information for RISC-V linker relaxation. RISC-V does not use
+    // jumpInstrMod.
+    RISCVRelaxAux *relaxAux;
+
+    // The compressed content size when `compressed` is true.
+    size_t compressedSize;
+  };
 
   // A function compiled with -fsplit-stack calling a function
   // compiled without -fsplit-stack needs its prologue adjusted. Find
@@ -232,23 +239,15 @@ public:
 
 
   template <typename T> llvm::ArrayRef<T> getDataAs() const {
-    size_t s = data().size();
+    size_t s = content().size();
     assert(s % sizeof(T) == 0);
-    return llvm::makeArrayRef<T>((const T *)data().data(), s / sizeof(T));
+    return llvm::ArrayRef<T>((const T *)content().data(), s / sizeof(T));
   }
 
 protected:
   template <typename ELFT>
   void parseCompressedHeader();
-  void uncompress() const;
-
-  mutable ArrayRef<uint8_t> rawData;
-
-  // This field stores the uncompressed size of the compressed data in rawData,
-  // or -1 if rawData is not compressed (either because the section wasn't
-  // compressed in the first place, or because we ended up uncompressing it).
-  // Since the feature is not used often, this is usually -1.
-  mutable int64_t uncompressedSize = -1;
+  void decompress() const;
 };
 
 // SectionPiece represents a piece of splittable section contents.
@@ -256,8 +255,9 @@ protected:
 // have to be as compact as possible, which is why we don't store the size (can
 // be found by looking at the next one).
 struct SectionPiece {
+  SectionPiece() = default;
   SectionPiece(size_t off, uint32_t hash, bool live)
-      : inputOff(off), live(live || !config->gcSections), hash(hash >> 1) {}
+      : inputOff(off), live(live), hash(hash >> 1) {}
 
   uint32_t inputOff;
   uint32_t live : 1;
@@ -285,7 +285,7 @@ public:
 
   // Splittable sections are handled as a sequence of data
   // rather than a single large blob of data.
-  std::vector<SectionPiece> pieces;
+  SmallVector<SectionPiece, 0> pieces;
 
   // Returns I'th piece's data. This function is very hot when
   // string merging is enabled, so we want to inline.
@@ -293,20 +293,22 @@ 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) ? content().size() : pieces[i + 1].inputOff;
+    return {toStringRef(content().slice(begin, end - begin)), pieces[i].hash};
   }
 
   // Returns the SectionPiece at a given input section offset.
-  SectionPiece *getSectionPiece(uint64_t offset);
-  const SectionPiece *getSectionPiece(uint64_t offset) const {
+  SectionPiece &getSectionPiece(uint64_t offset);
+  const SectionPiece &getSectionPiece(uint64_t offset) const {
     return const_cast<MergeInputSection *>(this)->getSectionPiece(offset);
   }
 
-  SyntheticSection *getParent() const;
+  SyntheticSection *getParent() const {
+    return cast_or_null<SyntheticSection>(parent);
+  }
 
 private:
-  void splitStrings(ArrayRef<uint8_t> a, size_t size);
+  void splitStrings(StringRef s, size_t size);
   void splitNonStrings(ArrayRef<uint8_t> a, size_t size);
 };
 
@@ -316,7 +318,7 @@ struct EhSectionPiece {
       : inputOff(off), sec(sec), size(size), firstRelocation(firstRelocation) {}
 
   ArrayRef<uint8_t> data() const {
-    return {sec->data().data() + this->inputOff, size};
+    return {sec->content().data() + this->inputOff, size};
   }
 
   size_t inputOff;
@@ -338,9 +340,10 @@ public:
 
   // Splittable sections are handled as a sequence of data
   // rather than a single large blob of data.
-  std::vector<EhSectionPiece> pieces;
+  SmallVector<EhSectionPiece, 0> cies, fdes;
 
   SyntheticSection *getParent() const;
+  uint64_t getParentOffset(uint64_t offset) const;
 };
 
 // This is a section that is added directly to an output section
@@ -349,19 +352,24 @@ public:
 // .eh_frame. It also includes the synthetic sections themselves.
 class InputSection : public InputSectionBase {
 public:
-  InputSection(InputFile *f, uint64_t flags, uint32_t type, uint32_t alignment,
+  InputSection(InputFile *f, uint64_t flags, uint32_t type, uint32_t addralign,
                ArrayRef<uint8_t> data, StringRef name, Kind k = Regular);
   template <class ELFT>
   InputSection(ObjFile<ELFT> &f, const typename ELFT::Shdr &header,
                StringRef name);
 
+  static bool classof(const SectionBase *s) {
+    return s->kind() == SectionBase::Regular ||
+           s->kind() == SectionBase::Synthetic;
+  }
+
   // Write this section to a mmap'ed file, assuming Buf is pointing to
   // beginning of the output section.
   template <class ELFT> void writeTo(uint8_t *buf);
 
-  uint64_t getOffset(uint64_t offset) const { return outSecOff + offset; }
-
-  OutputSection *getParent() const;
+  OutputSection *getParent() const {
+    return reinterpret_cast<OutputSection *>(parent);
+  }
 
   // This variable has two usages. Initially, it represents an index in the
   // OutputSection's InputSection list, and is used when ordering SHF_LINK_ORDER
@@ -369,13 +377,15 @@ public:
   // the beginning of the output section this section was assigned to.
   uint64_t outSecOff = 0;
 
-  static bool classof(const SectionBase *s);
-
   InputSectionBase *getRelocatedSection() const;
 
   template <class ELFT, class RelTy>
   void relocateNonAlloc(uint8_t *buf, llvm::ArrayRef<RelTy> rels);
 
+  // Points to the canonical section. If ICF folds two sections, repl pointer of
+  // one section points to the other.
+  InputSection *repl = this;
+
   // Used by ICF.
   uint32_t eqClass[2] = {0, 0};
 
@@ -391,20 +401,34 @@ private:
   template <class ELFT> void copyShtGroup(uint8_t *buf);
 };
 
-#ifdef _WIN32
-static_assert(sizeof(InputSection) <= 192, "InputSection is too big");
-#else
-static_assert(sizeof(InputSection) <= 184, "InputSection is too big");
-#endif
+static_assert(sizeof(InputSection) <= 152, "InputSection is too big");
+
+class SyntheticSection : public InputSection {
+public:
+  SyntheticSection(uint64_t flags, uint32_t type, uint32_t addralign,
+                   StringRef name)
+      : InputSection(nullptr, flags, type, addralign, {}, name,
+                     InputSectionBase::Synthetic) {}
+
+  virtual ~SyntheticSection() = default;
+  virtual size_t getSize() const = 0;
+  virtual bool updateAllocSize() { return false; }
+  // If the section has the SHF_ALLOC flag and the size may be changed if
+  // thunks are added, update the section size.
+  virtual bool isNeeded() const { return true; }
+  virtual void finalizeContents() {}
+  virtual void writeTo(uint8_t *buf) = 0;
+
+  static bool classof(const SectionBase *sec) {
+    return sec->kind() == InputSectionBase::Synthetic;
+  }
+};
 
 inline bool isDebugSection(const InputSectionBase &sec) {
   return (sec.flags & llvm::ELF::SHF_ALLOC) == 0 &&
-         (sec.name.startswith(".debug") || sec.name.startswith(".zdebug"));
+         sec.name.startswith(".debug");
 }
 
-// The list of all input sections.
-extern std::vector<InputSectionBase *> inputSections;
-
 // The set of TOC entries (.toc + addend) for which we should not apply
 // toc-indirect to toc-relative relaxation. const Symbol * refers to the
 // STT_SECTION symbol associated to the .toc input section.
index e8710e3..b80f1f4 100644 (file)
@@ -9,24 +9,20 @@
 #include "LTO.h"
 #include "Config.h"
 #include "InputFiles.h"
-#include "LinkerScript.h"
 #include "SymbolTable.h"
 #include "Symbols.h"
 #include "lld/Common/Args.h"
 #include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Strings.h"
 #include "lld/Common/TargetOptionsCommandFlags.h"
-#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/BinaryFormat/ELF.h"
-#include "llvm/Bitcode/BitcodeReader.h"
 #include "llvm/Bitcode/BitcodeWriter.h"
-#include "llvm/IR/DiagnosticPrinter.h"
-#include "llvm/LTO/Caching.h"
 #include "llvm/LTO/Config.h"
 #include "llvm/LTO/LTO.h"
-#include "llvm/Object/SymbolicFile.h"
+#include "llvm/Support/Caching.h"
 #include "llvm/Support/CodeGen.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/FileSystem.h"
@@ -81,8 +77,9 @@ static lto::Config createConfig() {
 
   // LLD supports the new relocations and address-significance tables.
   c.Options = initTargetOptionsFromCodeGenFlags();
-  c.Options.RelaxELFRelocations = true;
   c.Options.EmitAddrsig = true;
+  for (StringRef C : config->mllvmOpts)
+    c.MllvmArgs.emplace_back(C.str());
 
   // Always emit a section per function/datum with LTO.
   c.Options.FunctionSections = true;
@@ -112,14 +109,13 @@ static lto::Config createConfig() {
     }
   }
 
-  c.Options.PseudoProbeForProfiling = config->ltoPseudoProbeForProfiling;
   c.Options.UniqueBasicBlockSectionNames =
       config->ltoUniqueBasicBlockSectionNames;
 
   if (auto relocModel = getRelocModelFromCMModel())
     c.RelocModel = *relocModel;
   else if (config->relocatable)
-    c.RelocModel = None;
+    c.RelocModel = std::nullopt;
   else if (config->isPic)
     c.RelocModel = Reloc::PIC_;
   else
@@ -147,8 +143,12 @@ static lto::Config createConfig() {
   c.RemarksHotnessThreshold = config->optRemarksHotnessThreshold;
   c.RemarksFormat = std::string(config->optRemarksFormat);
 
+  // Set up output file to emit statistics.
+  c.StatsFile = std::string(config->optStatsFilename);
+
   c.SampleProfile = std::string(config->ltoSampleProfile);
-  c.UseNewPM = config->ltoNewPassManager;
+  for (StringRef pluginFn : config->passPlugins)
+    c.PassPlugins.push_back(std::string(pluginFn));
   c.DebugPassManager = config->ltoDebugPassManager;
   c.DwoDir = std::string(config->dwoDir);
 
@@ -163,6 +163,9 @@ static lto::Config createConfig() {
 
   c.CSIRProfile = std::string(config->ltoCSProfileFile);
   c.RunCSIRInstr = config->ltoCSProfileGenerate;
+  c.PGOWarnMismatch = config->ltoPGOWarnMismatch;
+
+  c.OpaquePointers = config->opaquePointers;
 
   if (config->emitLLVM) {
     c.PostInternalizeModuleHook = [](size_t task, const Module &m) {
@@ -173,12 +176,15 @@ static lto::Config createConfig() {
     };
   }
 
-  if (config->ltoEmitAsm)
+  if (config->ltoEmitAsm) {
     c.CGFileType = CGFT_AssemblyFile;
+    c.Options.MCOptions.AsmVerbose = true;
+  }
 
-  if (config->saveTemps)
+  if (!config->saveTempsArgs.empty())
     checkError(c.addSaveTemps(config->outputFile.str() + ".",
-                              /*UseInputModulePath*/ true));
+                              /*UseInputModulePath*/ true,
+                              config->saveTempsArgs));
   return c;
 }
 
@@ -189,22 +195,28 @@ BitcodeCompiler::BitcodeCompiler() {
 
   // Initialize ltoObj.
   lto::ThinBackend backend;
+  auto onIndexWrite = [&](StringRef s) { thinIndices.erase(s); };
   if (config->thinLTOIndexOnly) {
-    auto onIndexWrite = [&](StringRef s) { thinIndices.erase(s); };
     backend = lto::createWriteIndexesThinBackend(
         std::string(config->thinLTOPrefixReplace.first),
         std::string(config->thinLTOPrefixReplace.second),
         config->thinLTOEmitImportsFiles, indexFile.get(), onIndexWrite);
   } else {
     backend = lto::createInProcessThinBackend(
-        llvm::heavyweight_hardware_concurrency(config->thinLTOJobs));
+        llvm::heavyweight_hardware_concurrency(config->thinLTOJobs),
+        onIndexWrite, config->thinLTOEmitIndexFiles,
+        config->thinLTOEmitImportsFiles);
   }
 
   ltoObj = std::make_unique<lto::LTO>(createConfig(), backend,
                                        config->ltoPartitions);
 
   // Initialize usedStartStop.
-  for (Symbol *sym : symtab->symbols()) {
+  if (ctx.bitcodeFiles.empty())
+    return;
+  for (Symbol *sym : symtab.getSymbols()) {
+    if (sym->isPlaceholder())
+      continue;
     StringRef s = sym->getName();
     for (StringRef prefix : {"__start_", "__stop_"})
       if (s.startswith(prefix))
@@ -218,7 +230,7 @@ void BitcodeCompiler::add(BitcodeFile &f) {
   lto::InputFile &obj = *f.obj;
   bool isExec = !config->shared && !config->relocatable;
 
-  if (config->thinLTOIndexOnly)
+  if (config->thinLTOEmitIndexFiles)
     thinIndices.insert(obj.getName());
 
   ArrayRef<Symbol *> syms = f.getSymbols();
@@ -243,18 +255,21 @@ void BitcodeCompiler::add(BitcodeFile &f) {
     //    for doing final link.
     // 2) Symbols that are used in regular objects.
     // 3) C named sections if we have corresponding __start_/__stop_ symbol.
-    // 4) Symbols that are defined in bitcode files and used for dynamic linking.
+    // 4) Symbols that are defined in bitcode files and used for dynamic
+    //    linking.
+    // 5) Symbols that will be referenced after linker wrapping is performed.
     r.VisibleToRegularObj = config->relocatable || sym->isUsedInRegularObj ||
+                            sym->referencedAfterWrap ||
                             (r.Prevailing && sym->includeInDynsym()) ||
                             usedStartStop.count(objSym.getSectionName());
     // Identify symbols exported dynamically, and that therefore could be
     // referenced by a shared library not visible to the linker.
-    r.ExportDynamic = sym->computeBinding() != STB_LOCAL &&
-                      (sym->isExportDynamic(sym->kind(), sym->visibility) ||
-                       sym->exportDynamic || sym->inDynamicList);
+    r.ExportDynamic =
+        sym->computeBinding() != STB_LOCAL &&
+        (config->exportDynamic || sym->exportDynamic || sym->inDynamicList);
     const auto *dr = dyn_cast<Defined>(sym);
     r.FinalDefinitionInLinkageUnit =
-        (isExec || sym->visibility != STV_DEFAULT) && dr &&
+        (isExec || sym->visibility() != STV_DEFAULT) && dr &&
         // Skip absolute symbols from ELF objects, otherwise PC-rel relocations
         // will be generated by for them, triggering linker errors.
         // Symbol section is always null for bitcode symbols, hence the check
@@ -263,13 +278,13 @@ void BitcodeCompiler::add(BitcodeFile &f) {
         !(dr->section == nullptr && (!sym->file || sym->file->isElf()));
 
     if (r.Prevailing)
-      sym->replace(Undefined{nullptr, sym->getName(), STB_GLOBAL, STV_DEFAULT,
-                             sym->type});
+      Undefined(nullptr, StringRef(), STB_GLOBAL, STV_DEFAULT, sym->type)
+          .overwrite(*sym);
 
     // We tell LTO to not apply interprocedural optimization for wrapped
     // (with --wrap) symbols because otherwise LTO would inline them while
     // their values are still not final.
-    r.LinkerRedefined = !sym->canInline;
+    r.LinkerRedefined = sym->scriptDefined;
   }
   checkError(ltoObj->add(std::move(f.obj), resols));
 }
@@ -278,17 +293,24 @@ void BitcodeCompiler::add(BitcodeFile &f) {
 // This is needed because this is what GNU gold plugin does and we have a
 // distributed build system that depends on that behavior.
 static void thinLTOCreateEmptyIndexFiles() {
-  for (LazyObjFile *f : lazyObjFiles) {
-    if (f->fetched || !isBitcode(f->mb))
+  DenseSet<StringRef> linkedBitCodeFiles;
+  for (BitcodeFile *f : ctx.bitcodeFiles)
+    linkedBitCodeFiles.insert(f->getName());
+
+  for (BitcodeFile *f : ctx.lazyBitcodeFiles) {
+    if (!f->lazy)
+      continue;
+    if (linkedBitCodeFiles.contains(f->getName()))
       continue;
-    std::string path = replaceThinLTOSuffix(getThinLTOOutputFile(f->getName()));
+    std::string path =
+        replaceThinLTOSuffix(getThinLTOOutputFile(f->obj->getName()));
     std::unique_ptr<raw_fd_ostream> os = openFile(path + ".thinlto.bc");
     if (!os)
       continue;
 
     ModuleSummaryIndex m(/*HaveGVs*/ false);
     m.setSkipModuleByDistributedBackend();
-    WriteIndexToFile(m, *os);
+    writeIndexToFile(m, *os);
     if (config->thinLTOEmitImportsFiles)
       openFile(path + ".imports");
   }
@@ -304,18 +326,18 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
   // The --thinlto-cache-dir option specifies the path to a directory in which
   // to cache native object files for ThinLTO incremental builds. If a path was
   // specified, configure LTO to use it as the cache directory.
-  lto::NativeObjectCache cache;
+  FileCache cache;
   if (!config->thinLTOCacheDir.empty())
-    cache = check(
-        lto::localCache(config->thinLTOCacheDir,
-                        [&](size_t task, std::unique_ptr<MemoryBuffer> mb) {
-                          files[task] = std::move(mb);
-                        }));
+    cache = check(localCache("ThinLTO", "Thin", config->thinLTOCacheDir,
+                             [&](size_t task, const Twine &moduleName,
+                                 std::unique_ptr<MemoryBuffer> mb) {
+                               files[task] = std::move(mb);
+                             }));
 
-  if (!bitcodeFiles.empty())
+  if (!ctx.bitcodeFiles.empty())
     checkError(ltoObj->run(
-        [&](size_t task) {
-          return std::make_unique<lto::NativeObjectStream>(
+        [&](size_t task, const Twine &moduleName) {
+          return std::make_unique<CachedFileStream>(
               std::make_unique<raw_svector_ostream>(buf[task]));
         },
         cache));
@@ -330,9 +352,10 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
     }
   }
 
-  if (config->thinLTOIndexOnly) {
+  if (config->thinLTOEmitIndexFiles)
     thinLTOCreateEmptyIndexFiles();
 
+  if (config->thinLTOIndexOnly) {
     if (!config->ltoObjPath.empty())
       saveBuffer(buf[0], config->ltoObjPath);
 
@@ -345,7 +368,7 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
   }
 
   if (!config->thinLTOCacheDir.empty())
-    pruneCache(config->thinLTOCacheDir, config->thinLTOCachePolicy);
+    pruneCache(config->thinLTOCacheDir, config->thinLTOCachePolicy, files);
 
   if (!config->ltoObjPath.empty()) {
     saveBuffer(buf[0], config->ltoObjPath);
@@ -353,7 +376,7 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
       saveBuffer(buf[i], config->ltoObjPath + Twine(i));
   }
 
-  if (config->saveTemps) {
+  if (config->saveTempsArgs.contains("prelink")) {
     if (!buf[0].empty())
       saveBuffer(buf[0], config->outputFile + ".lto.o");
     for (unsigned i = 1; i != maxTasks; ++i)
@@ -370,10 +393,10 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
   std::vector<InputFile *> ret;
   for (unsigned i = 0; i != maxTasks; ++i)
     if (!buf[i].empty())
-      ret.push_back(createObjectFile(MemoryBufferRef(buf[i], "lto.tmp")));
+      ret.push_back(createObjFile(MemoryBufferRef(buf[i], "lto.tmp")));
 
   for (std::unique_ptr<MemoryBuffer> &file : files)
     if (file)
-      ret.push_back(createObjectFile(*file));
+      ret.push_back(createObjFile(*file));
   return ret;
 }
index 4cb42d8..7ab6541 100644 (file)
 #include <memory>
 #include <vector>
 
-namespace llvm {
-namespace lto {
+namespace llvm::lto {
 class LTO;
 }
-} // namespace llvm
 
-namespace lld {
-namespace elf {
+namespace lld::elf {
 
 class BitcodeFile;
 class InputFile;
-class LazyObjFile;
 
 class BitcodeCompiler {
 public:
@@ -56,7 +52,6 @@ private:
   std::unique_ptr<llvm::raw_fd_ostream> indexFile;
   llvm::DenseSet<StringRef> thinIndices;
 };
-} // namespace elf
-} // namespace lld
+} // namespace lld::elf
 
 #endif
index d2487ae..4e56685 100644 (file)
 #include "lld/Common/Strings.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/MapVector.h"
 #include "llvm/ADT/StringRef.h"
-#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Compiler.h"
 #include <cstddef>
 #include <cstdint>
 #include <functional>
 #include <memory>
-#include <vector>
 
-namespace lld {
-namespace elf {
+namespace lld::elf {
 
 class Defined;
 class InputFile;
@@ -34,14 +31,14 @@ class InputSection;
 class InputSectionBase;
 class OutputSection;
 class SectionBase;
-class Symbol;
 class ThunkSection;
+struct OutputDesc;
 
 // This represents an r-value in the linker script.
 struct ExprValue {
   ExprValue(SectionBase *sec, bool forceAbsolute, uint64_t val,
             const Twine &loc)
-      : sec(sec), forceAbsolute(forceAbsolute), val(val), loc(loc.str()) {}
+      : sec(sec), val(val), forceAbsolute(forceAbsolute), loc(loc.str()) {}
 
   ExprValue(uint64_t val) : ExprValue(nullptr, false, val, "") {}
 
@@ -53,10 +50,6 @@ struct ExprValue {
   // If a value is relative to a section, it has a non-null Sec.
   SectionBase *sec;
 
-  // True if this expression is enclosed in ABSOLUTE().
-  // This flag affects the return value of getValue().
-  bool forceAbsolute;
-
   uint64_t val;
   uint64_t alignment = 1;
 
@@ -64,6 +57,10 @@ struct ExprValue {
   // resets type to STT_NOTYPE.
   uint8_t type = llvm::ELF::STT_NOTYPE;
 
+  // True if this expression is enclosed in ABSOLUTE().
+  // This flag affects the return value of getValue().
+  bool forceAbsolute;
+
   // Original source location. Used for error messages.
   std::string loc;
 };
@@ -82,17 +79,18 @@ enum SectionsCommandKind {
   ByteKind    // BYTE(expr), SHORT(expr), LONG(expr) or QUAD(expr)
 };
 
-struct BaseCommand {
-  BaseCommand(int k) : kind(k) {}
+struct SectionCommand {
+  SectionCommand(int k) : kind(k) {}
   int kind;
 };
 
 // This represents ". = <expr>" or "<symbol> = <expr>".
-struct SymbolAssignment : BaseCommand {
+struct SymbolAssignment : SectionCommand {
   SymbolAssignment(StringRef name, Expr e, std::string loc)
-      : BaseCommand(AssignmentKind), name(name), expression(e), location(loc) {}
+      : SectionCommand(AssignmentKind), name(name), expression(e),
+        location(loc) {}
 
-  static bool classof(const BaseCommand *c) {
+  static bool classof(const SectionCommand *c) {
     return c->kind == AssignmentKind;
   }
 
@@ -132,16 +130,32 @@ enum class ConstraintKind { NoConstraint, ReadOnly, ReadWrite };
 // MEMORY command.
 struct MemoryRegion {
   MemoryRegion(StringRef name, Expr origin, Expr length, uint32_t flags,
-               uint32_t negFlags)
+               uint32_t invFlags, uint32_t negFlags, uint32_t negInvFlags)
       : name(std::string(name)), origin(origin), length(length), flags(flags),
-        negFlags(negFlags) {}
+        invFlags(invFlags), negFlags(negFlags), negInvFlags(negInvFlags) {}
 
   std::string name;
   Expr origin;
   Expr length;
+  // A section can be assigned to the region if any of these ELF section flags
+  // are set...
   uint32_t flags;
+  // ... or any of these flags are not set.
+  // For example, the memory region attribute "r" maps to SHF_WRITE.
+  uint32_t invFlags;
+  // A section cannot be assigned to the region if any of these ELF section
+  // flags are set...
   uint32_t negFlags;
+  // ... or any of these flags are not set.
+  // For example, the memory region attribute "!r" maps to SHF_WRITE.
+  uint32_t negInvFlags;
   uint64_t curPos = 0;
+
+  bool compatibleWith(uint32_t secFlags) const {
+    if ((secFlags & negFlags) || (~secFlags & negInvFlags))
+      return false;
+    return (secFlags & flags) || (~secFlags & invFlags);
+  }
 };
 
 // This struct represents one section match pattern in SECTIONS() command.
@@ -151,7 +165,7 @@ class SectionPattern {
   StringMatcher excludedFilePat;
 
   // Cache of the most recent input argument and result of excludesFile().
-  mutable llvm::Optional<std::pair<const InputFile *, bool>> excludesFileCache;
+  mutable std::optional<std::pair<const InputFile *, bool>> excludesFileCache;
 
 public:
   SectionPattern(StringMatcher &&pat1, StringMatcher &&pat2)
@@ -166,19 +180,19 @@ public:
   SortSectionPolicy sortInner;
 };
 
-class InputSectionDescription : public BaseCommand {
+class InputSectionDescription : public SectionCommand {
   SingleStringMatcher filePat;
 
   // Cache of the most recent input argument and result of matchesFile().
-  mutable llvm::Optional<std::pair<const InputFile *, bool>> matchesFileCache;
+  mutable std::optional<std::pair<const InputFile *, bool>> matchesFileCache;
 
 public:
   InputSectionDescription(StringRef filePattern, uint64_t withFlags = 0,
                           uint64_t withoutFlags = 0)
-      : BaseCommand(InputSectionKind), filePat(filePattern),
+      : SectionCommand(InputSectionKind), filePat(filePattern),
         withFlags(withFlags), withoutFlags(withoutFlags) {}
 
-  static bool classof(const BaseCommand *c) {
+  static bool classof(const SectionCommand *c) {
     return c->kind == InputSectionKind;
   }
 
@@ -186,20 +200,20 @@ public:
 
   // Input sections that matches at least one of SectionPatterns
   // will be associated with this InputSectionDescription.
-  std::vector<SectionPattern> sectionPatterns;
+  SmallVector<SectionPattern, 0> sectionPatterns;
 
   // Includes InputSections and MergeInputSections. Used temporarily during
   // assignment of input sections to output sections.
-  std::vector<InputSectionBase *> sectionBases;
+  SmallVector<InputSectionBase *, 0> sectionBases;
 
   // Used after the finalizeInputSections() pass. MergeInputSections have been
   // merged into MergeSyntheticSections.
-  std::vector<InputSection *> sections;
+  SmallVector<InputSection *, 0> sections;
 
   // Temporary record of synthetic ThunkSection instances and the pass that
   // they were created in. This is used to insert newly created ThunkSections
   // into Sections at the end of a createThunks() pass.
-  std::vector<std::pair<ThunkSection *, uint32_t>> thunkSections;
+  SmallVector<std::pair<ThunkSection *, uint32_t>, 0> thunkSections;
 
   // SectionPatterns can be filtered with the INPUT_SECTION_FLAGS command.
   uint64_t withFlags;
@@ -207,12 +221,12 @@ public:
 };
 
 // Represents BYTE(), SHORT(), LONG(), or QUAD().
-struct ByteCommand : BaseCommand {
+struct ByteCommand : SectionCommand {
   ByteCommand(Expr e, unsigned size, std::string commandString)
-      : BaseCommand(ByteKind), commandString(commandString), expression(e),
+      : SectionCommand(ByteKind), commandString(commandString), expression(e),
         size(size) {}
 
-  static bool classof(const BaseCommand *c) { return c->kind == ByteKind; }
+  static bool classof(const SectionCommand *c) { return c->kind == ByteKind; }
 
   // Keeps string representing the command. Used for -Map" is perhaps better.
   std::string commandString;
@@ -227,7 +241,7 @@ struct ByteCommand : BaseCommand {
 };
 
 struct InsertCommand {
-  std::vector<StringRef> names;
+  SmallVector<StringRef, 0> names;
   bool isAfter;
   StringRef where;
 };
@@ -237,7 +251,7 @@ struct PhdrsCommand {
   unsigned type = llvm::ELF::PT_NULL;
   bool hasFilehdr = false;
   bool hasPhdrs = false;
-  llvm::Optional<unsigned> flags;
+  std::optional<unsigned> flags;
   Expr lmaExpr = nullptr;
 };
 
@@ -254,7 +268,7 @@ class LinkerScript final {
     uint64_t tbssAddr = 0;
   };
 
-  llvm::DenseMap<StringRef, OutputSection *> nameToOutputSection;
+  llvm::DenseMap<llvm::CachedHashStringRef, OutputDesc *> nameToOutputSection;
 
   void addSymbol(SymbolAssignment *cmd);
   void assignSymbol(SymbolAssignment *cmd, bool inSec);
@@ -262,97 +276,95 @@ class LinkerScript final {
   void expandOutputSection(uint64_t size);
   void expandMemoryRegions(uint64_t size);
 
-  std::vector<InputSectionBase *>
+  SmallVector<InputSectionBase *, 0>
   computeInputSections(const InputSectionDescription *,
                        ArrayRef<InputSectionBase *>);
 
-  std::vector<InputSectionBase *> createInputSectionList(OutputSection &cmd);
+  SmallVector<InputSectionBase *, 0> createInputSectionList(OutputSection &cmd);
 
   void discardSynthetic(OutputSection &);
 
-  std::vector<size_t> getPhdrIndices(OutputSection *sec);
-
-  MemoryRegion *findMemoryRegion(OutputSection *sec);
+  SmallVector<size_t, 0> getPhdrIndices(OutputSection *sec);
 
-  void switchTo(OutputSection *sec);
-  uint64_t advance(uint64_t size, unsigned align);
-  void output(InputSection *sec);
+  std::pair<MemoryRegion *, MemoryRegion *>
+  findMemoryRegion(OutputSection *sec, MemoryRegion *hint);
 
   void assignOffsets(OutputSection *sec);
 
-  // Ctx captures the local AddressState and makes it accessible
+  // This captures the local AddressState and makes it accessible
   // deliberately. This is needed as there are some cases where we cannot just
   // thread the current state through to a lambda function created by the
   // script parser.
   // This should remain a plain pointer as its lifetime is smaller than
   // LinkerScript.
-  AddressState *ctx = nullptr;
+  AddressState *state = nullptr;
 
   OutputSection *aether;
 
   uint64_t dot;
 
 public:
-  OutputSection *createOutputSection(StringRef name, StringRef location);
-  OutputSection *getOrCreateOutputSection(StringRef name);
+  OutputDesc *createOutputSection(StringRef name, StringRef location);
+  OutputDesc *getOrCreateOutputSection(StringRef name);
 
   bool hasPhdrsCommands() { return !phdrsCommands.empty(); }
   uint64_t getDot() { return dot; }
-  void discard(InputSectionBase *s);
+  void discard(InputSectionBase &s);
 
   ExprValue getSymbolValue(StringRef name, const Twine &loc);
 
   void addOrphanSections();
   void diagnoseOrphanHandling() const;
-  void adjustSectionsBeforeSorting();
+  void adjustOutputSections();
   void adjustSectionsAfterSorting();
 
-  std::vector<PhdrEntry *> createPhdrs();
+  SmallVector<PhdrEntry *, 0> createPhdrs();
   bool needsInterpSection();
 
   bool shouldKeep(InputSectionBase *s);
   const Defined *assignAddresses();
-  void allocateHeaders(std::vector<PhdrEntry *> &phdrs);
+  void allocateHeaders(SmallVector<PhdrEntry *, 0> &phdrs);
   void processSectionCommands();
   void processSymbolAssignments();
   void declareSymbols();
 
+  bool isDiscarded(const OutputSection *sec) const;
+
   // Used to handle INSERT AFTER statements.
   void processInsertCommands();
 
   // SECTIONS command list.
-  std::vector<BaseCommand *> sectionCommands;
+  SmallVector<SectionCommand *, 0> sectionCommands;
 
   // PHDRS command list.
-  std::vector<PhdrsCommand> phdrsCommands;
+  SmallVector<PhdrsCommand, 0> phdrsCommands;
 
   bool hasSectionsCommand = false;
   bool errorOnMissingSection = false;
 
   // List of section patterns specified with KEEP commands. They will
   // be kept even if they are unused and --gc-sections is specified.
-  std::vector<InputSectionDescription *> keptSections;
+  SmallVector<InputSectionDescription *, 0> keptSections;
 
   // A map from memory region name to a memory region descriptor.
   llvm::MapVector<llvm::StringRef, MemoryRegion *> memoryRegions;
 
   // A list of symbols referenced by the script.
-  std::vector<llvm::StringRef> referencedSymbols;
+  SmallVector<llvm::StringRef, 0> referencedSymbols;
 
   // Used to implement INSERT [AFTER|BEFORE]. Contains output sections that need
   // to be reordered.
-  std::vector<InsertCommand> insertCommands;
+  SmallVector<InsertCommand, 0> insertCommands;
 
   // OutputSections specified by OVERWRITE_SECTIONS.
-  std::vector<OutputSection *> overwriteSections;
+  SmallVector<OutputDesc *, 0> overwriteSections;
 
   // Sections that will be warned/errored by --orphan-handling.
-  std::vector<const InputSectionBase *> orphanSections;
+  SmallVector<const InputSectionBase *, 0> orphanSections;
 };
 
-extern LinkerScript *script;
+LLVM_LIBRARY_VISIBILITY extern std::unique_ptr<LinkerScript> script;
 
-} // end namespace elf
-} // end namespace lld
+} // end namespace lld::elf
 
 #endif // LLD_ELF_LINKER_SCRIPT_H
index 239c6c3..e28656c 100644 (file)
 #include "InputFiles.h"
 #include "LinkerScript.h"
 #include "OutputSections.h"
-#include "SymbolTable.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
-#include "lld/Common/Strings.h"
 #include "llvm/ADT/MapVector.h"
 #include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Parallel.h"
 #include "llvm/Support/TimeProfiler.h"
 #include "llvm/Support/raw_ostream.h"
@@ -37,7 +37,8 @@ using namespace llvm::object;
 using namespace lld;
 using namespace lld::elf;
 
-using SymbolMapTy = DenseMap<const SectionBase *, SmallVector<Defined *, 4>>;
+using SymbolMapTy = DenseMap<const SectionBase *,
+                             SmallVector<std::pair<Defined *, uint64_t>, 0>>;
 
 static constexpr char indent8[] = "        ";          // 8 spaces
 static constexpr char indent16[] = "                "; // 16 spaces
@@ -54,11 +55,11 @@ static void writeHeader(raw_ostream &os, uint64_t vma, uint64_t lma,
 // Returns a list of all symbols that we want to print out.
 static std::vector<Defined *> getSymbols() {
   std::vector<Defined *> v;
-  for (InputFile *file : objectFiles)
+  for (ELFFileBase *file : ctx.objectFiles)
     for (Symbol *b : file->getSymbols())
       if (auto *dr = dyn_cast<Defined>(b))
         if (!dr->isSection() && dr->section && dr->section->isLive() &&
-            (dr->file == file || dr->needsPltAddr || dr->section->bss))
+            (dr->file == file || dr->hasFlag(NEEDS_COPY) || dr->section->bss))
           v.push_back(dr);
   return v;
 }
@@ -67,15 +68,21 @@ static std::vector<Defined *> getSymbols() {
 static SymbolMapTy getSectionSyms(ArrayRef<Defined *> syms) {
   SymbolMapTy ret;
   for (Defined *dr : syms)
-    ret[dr->section].push_back(dr);
+    ret[dr->section].emplace_back(dr, dr->getVA());
 
   // Sort symbols by address. We want to print out symbols in the
   // order in the output file rather than the order they appeared
   // in the input files.
-  for (auto &it : ret)
-    llvm::stable_sort(it.second, [](Defined *a, Defined *b) {
-      return a->getVA() < b->getVA();
+  SmallPtrSet<Defined *, 4> set;
+  for (auto &it : ret) {
+    // Deduplicate symbols which need a canonical PLT entry/copy relocation.
+    set.clear();
+    llvm::erase_if(it.second, [&](std::pair<Defined *, uint64_t> a) {
+      return !set.insert(a.first).second;
     });
+
+    llvm::stable_sort(it.second, llvm::less_second());
+  }
   return ret;
 }
 
@@ -84,9 +91,9 @@ static SymbolMapTy getSectionSyms(ArrayRef<Defined *> syms) {
 // we do that in batch using parallel-for.
 static DenseMap<Symbol *, std::string>
 getSymbolStrings(ArrayRef<Defined *> syms) {
-  std::vector<std::string> str(syms.size());
-  parallelForEachN(0, syms.size(), [&](size_t i) {
-    raw_string_ostream os(str[i]);
+  auto strs = std::make_unique<std::string[]>(syms.size());
+  parallelFor(0, syms.size(), [&](size_t i) {
+    raw_string_ostream os(strs[i]);
     OutputSection *osec = syms[i]->getOutputSection();
     uint64_t vma = syms[i]->getVA();
     uint64_t lma = osec ? osec->getLMA() + vma - osec->getVA(0) : 0;
@@ -96,7 +103,7 @@ getSymbolStrings(ArrayRef<Defined *> syms) {
 
   DenseMap<Symbol *, std::string> ret;
   for (size_t i = 0, e = syms.size(); i < e; ++i)
-    ret[syms[i]] = std::move(str[i]);
+    ret[syms[i]] = std::move(strs[i]);
   return ret;
 }
 
@@ -139,20 +146,7 @@ static void printEhFrame(raw_ostream &os, const EhFrameSection *sec) {
   }
 }
 
-void elf::writeMapFile() {
-  if (config->mapFile.empty())
-    return;
-
-  llvm::TimeTraceScope timeScope("Write map file");
-
-  // Open a map file for writing.
-  std::error_code ec;
-  raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None);
-  if (ec) {
-    error("cannot open " + config->mapFile + ": " + ec.message());
-    return;
-  }
-
+static void writeMapFile(raw_fd_ostream &os) {
   // Collect symbol info that we want to print out.
   std::vector<Defined *> syms = getSymbols();
   SymbolMapTy sectionSyms = getSectionSyms(syms);
@@ -164,61 +158,58 @@ void elf::writeMapFile() {
      << "     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)
+  for (SectionCommand *cmd : script->sectionCommands) {
+    if (auto *assign = dyn_cast<SymbolAssignment>(cmd)) {
+      if (assign->provide && !assign->sym)
         continue;
-      uint64_t lma = osec ? osec->getLMA() + cmd->addr - osec->getVA(0) : 0;
-      writeHeader(os, cmd->addr, lma, cmd->size, 1);
-      os << cmd->commandString << '\n';
+      uint64_t lma = osec ? osec->getLMA() + assign->addr - osec->getVA(0) : 0;
+      writeHeader(os, assign->addr, lma, assign->size, 1);
+      os << assign->commandString << '\n';
       continue;
     }
 
-    osec = cast<OutputSection>(base);
-    writeHeader(os, osec->addr, osec->getLMA(), osec->size, osec->alignment);
+    osec = &cast<OutputDesc>(cmd)->osec;
+    writeHeader(os, osec->addr, osec->getLMA(), osec->size, osec->addralign);
     os << osec->name << '\n';
 
     // Dump symbols for each input section.
-    for (BaseCommand *base : osec->sectionCommands) {
-      if (auto *isd = dyn_cast<InputSectionDescription>(base)) {
+    for (SectionCommand *subCmd : osec->commands) {
+      if (auto *isd = dyn_cast<InputSectionDescription>(subCmd)) {
         for (InputSection *isec : isd->sections) {
           if (auto *ehSec = dyn_cast<EhFrameSection>(isec)) {
             printEhFrame(os, ehSec);
             continue;
           }
 
-          writeHeader(os, isec->getVA(0), osec->getLMA() + isec->getOffset(0),
-                      isec->getSize(), isec->alignment);
+          writeHeader(os, isec->getVA(), osec->getLMA() + isec->outSecOff,
+                      isec->getSize(), isec->addralign);
           os << indent8 << toString(isec) << '\n';
-          for (Symbol *sym : sectionSyms[isec])
+          for (Symbol *sym : llvm::make_first_range(sectionSyms[isec]))
             os << symStr[sym] << '\n';
         }
         continue;
       }
 
-      if (auto *cmd = dyn_cast<ByteCommand>(base)) {
-        writeHeader(os, osec->addr + cmd->offset, osec->getLMA() + cmd->offset,
-                    cmd->size, 1);
-        os << indent8 << cmd->commandString << '\n';
+      if (auto *data = dyn_cast<ByteCommand>(subCmd)) {
+        writeHeader(os, osec->addr + data->offset,
+                    osec->getLMA() + data->offset, data->size, 1);
+        os << indent8 << data->commandString << '\n';
         continue;
       }
 
-      if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
-        if (cmd->provide && !cmd->sym)
+      if (auto *assign = dyn_cast<SymbolAssignment>(subCmd)) {
+        if (assign->provide && !assign->sym)
           continue;
-        writeHeader(os, cmd->addr, osec->getLMA() + cmd->addr - osec->getVA(0),
-                    cmd->size, 1);
-        os << indent8 << cmd->commandString << '\n';
+        writeHeader(os, assign->addr,
+                    osec->getLMA() + assign->addr - osec->getVA(0),
+                    assign->size, 1);
+        os << indent8 << assign->commandString << '\n';
         continue;
       }
     }
   }
 }
 
-static void print(StringRef a, StringRef b) {
-  lld::outs() << left_justify(a, 49) << " " << b << "\n";
-}
-
 // Output a cross reference table to stdout. This is for --cref.
 //
 // For each global symbol, we print out a file that defines the symbol
@@ -230,13 +221,10 @@ static void print(StringRef a, StringRef b) {
 //
 // In this case, strlen is defined by libc.so.6 and used by other two
 // files.
-void elf::writeCrossReferenceTable() {
-  if (!config->cref)
-    return;
-
+static void writeCref(raw_fd_ostream &os) {
   // Collect symbols and files.
   MapVector<Symbol *, SetVector<InputFile *>> map;
-  for (InputFile *file : objectFiles) {
+  for (ELFFileBase *file : ctx.objectFiles) {
     for (Symbol *sym : file->getSymbols()) {
       if (isa<SharedSymbol>(sym))
         map[sym].insert(file);
@@ -246,8 +234,12 @@ void elf::writeCrossReferenceTable() {
     }
   }
 
-  // Print out a header.
-  lld::outs() << "Cross Reference Table\n\n";
+  auto print = [&](StringRef a, StringRef b) {
+    os << left_justify(a, 49) << ' ' << b << '\n';
+  };
+
+  // Print a blank line and a header. The format matches GNU ld.
+  os << "\nCross Reference Table\n\n";
   print("Symbol", "File");
 
   // Print out a table.
@@ -262,20 +254,23 @@ void elf::writeCrossReferenceTable() {
   }
 }
 
-void elf::writeArchiveStats() {
-  if (config->printArchiveStats.empty())
+void elf::writeMapAndCref() {
+  if (config->mapFile.empty() && !config->cref)
     return;
 
+  llvm::TimeTraceScope timeScope("Write map file");
+
+  // Open a map file for writing.
   std::error_code ec;
-  raw_fd_ostream os(config->printArchiveStats, ec, sys::fs::OF_None);
+  StringRef mapFile = config->mapFile.empty() ? "-" : config->mapFile;
+  raw_fd_ostream os(mapFile, ec, sys::fs::OF_None);
   if (ec) {
-    error("--print-archive-stats=: cannot open " + config->printArchiveStats +
-          ": " + ec.message());
+    error("cannot open " + mapFile + ": " + ec.message());
     return;
   }
 
-  os << "members\tfetched\tarchive\n";
-  for (const ArchiveFile *f : archiveFiles)
-    os << f->getMemberCount() << '\t' << f->getFetchedMemberCount() << '\t'
-       << f->getName() << '\n';
+  if (!config->mapFile.empty())
+    writeMapFile(os);
+  if (config->cref)
+    writeCref(os);
 }
index c4da18f..b271f62 100644 (file)
@@ -9,12 +9,8 @@
 #ifndef LLD_ELF_MAPFILE_H
 #define LLD_ELF_MAPFILE_H
 
-namespace lld {
-namespace elf {
-void writeMapFile();
-void writeCrossReferenceTable();
-void writeArchiveStats();
-} // namespace elf
-} // namespace lld
+namespace lld::elf {
+void writeMapAndCref();
+}
 
 #endif
index e828429..f6665e3 100644 (file)
 //===----------------------------------------------------------------------===//
 
 #include "MarkLive.h"
+#include "InputFiles.h"
 #include "InputSection.h"
 #include "LinkerScript.h"
-#include "OutputSections.h"
 #include "SymbolTable.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
-#include "lld/Common/Memory.h"
+#include "lld/Common/CommonLinkerContext.h"
 #include "lld/Common/Strings.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Object/ELF.h"
 #include "llvm/Support/TimeProfiler.h"
-#include <functional>
 #include <vector>
 
 using namespace llvm;
@@ -68,15 +67,15 @@ private:
   SmallVector<InputSection *, 0> queue;
 
   // There are normally few input sections whose names are valid C
-  // identifiers, so we just store a std::vector instead of a multimap.
-  DenseMap<StringRef, std::vector<InputSectionBase *>> cNamedSections;
+  // identifiers, so we just store a SmallVector instead of a multimap.
+  DenseMap<StringRef, SmallVector<InputSectionBase *, 0>> cNamedSections;
 };
 } // namespace
 
 template <class ELFT>
 static uint64_t getAddend(InputSectionBase &sec,
                           const typename ELFT::Rel &rel) {
-  return target->getImplicitAddend(sec.data().begin() + rel.r_offset,
+  return target->getImplicitAddend(sec.content().begin() + rel.r_offset,
                                    rel.getType(config->isMips64EL));
 }
 
@@ -120,7 +119,7 @@ void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
 
   if (auto *ss = dyn_cast<SharedSymbol>(&sym))
     if (!ss->isWeak())
-      ss->getFile().isNeeded = true;
+      cast<SharedFile>(ss->file)->isNeeded = true;
 
   for (InputSectionBase *sec : cNamedSections.lookup(sym.getName()))
     enqueue(sec, 0);
@@ -144,20 +143,14 @@ template <class ELFT>
 template <class RelTy>
 void MarkLive<ELFT>::scanEhFrameSection(EhInputSection &eh,
                                         ArrayRef<RelTy> rels) {
-  for (size_t i = 0, end = eh.pieces.size(); i < end; ++i) {
-    EhSectionPiece &piece = eh.pieces[i];
-    size_t firstRelI = piece.firstRelocation;
+  for (const EhSectionPiece &cie : eh.cies)
+    if (cie.firstRelocation != unsigned(-1))
+      resolveReloc(eh, rels[cie.firstRelocation], false);
+  for (const EhSectionPiece &fde : eh.fdes) {
+    size_t firstRelI = fde.firstRelocation;
     if (firstRelI == (unsigned)-1)
       continue;
-
-    if (read32<ELFT::TargetEndianness>(piece.data().data() + 4) == 0) {
-      // This is a CIE, we only need to worry about the first relocation. It is
-      // known to point to the personality function.
-      resolveReloc(eh, rels[firstRelI], false);
-      continue;
-    }
-
-    uint64_t pieceEnd = piece.inputOff + piece.size;
+    uint64_t pieceEnd = fde.inputOff + fde.size;
     for (size_t j = firstRelI, end2 = rels.size();
          j < end2 && rels[j].r_offset < pieceEnd; ++j)
       resolveReloc(eh, rels[j], true);
@@ -177,27 +170,22 @@ static bool isReserved(InputSectionBase *sec) {
     // SHT_NOTE sections in a group are subject to garbage collection.
     return !sec->nextInSectionGroup;
   default:
+    // Support SHT_PROGBITS .init_array (https://golang.org/issue/50295) and
+    // .init_array.N (https://github.com/rust-lang/rust/issues/92181) for a
+    // while.
     StringRef s = sec->name;
-    return s.startswith(".ctors") || s.startswith(".dtors") ||
-           s.startswith(".init") || s.startswith(".fini") ||
-           s.startswith(".jcr");
+    return s == ".init" || s == ".fini" || s.startswith(".init_array") ||
+           s == ".jcr" || s.startswith(".ctors") || s.startswith(".dtors");
   }
 }
 
 template <class ELFT>
 void MarkLive<ELFT>::enqueue(InputSectionBase *sec, uint64_t offset) {
-  // Skip over discarded sections. This in theory shouldn't happen, because
-  // the ELF spec doesn't allow a relocation to point to a deduplicated
-  // COMDAT section directly. Unfortunately this happens in practice (e.g.
-  // .eh_frame) so we need to add a check.
-  if (sec == &InputSection::discarded)
-    return;
-
   // Usually, a whole section is marked as live or dead, but in mergeable
   // (splittable) sections, each piece of data has independent liveness bit.
   // So we explicitly tell it which offset is in use.
   if (auto *ms = dyn_cast<MergeInputSection>(sec))
-    ms->getSectionPiece(offset)->live = true;
+    ms->getSectionPiece(offset).live = true;
 
   // Set Sec->Partition to the meet (i.e. the "minimum") of Partition and
   // Sec->Partition in the following lattice: 1 < other < 0. If Sec->Partition
@@ -225,7 +213,7 @@ template <class ELFT> void MarkLive<ELFT>::run() {
 
   // Preserve externally-visible symbols if the symbols defined by this
   // file can interrupt other ELF file's symbols at runtime.
-  for (Symbol *sym : symtab->symbols())
+  for (Symbol *sym : symtab.getSymbols())
     if (sym->includeInDynsym() && sym->partition == partition)
       markSymbol(sym);
 
@@ -235,32 +223,26 @@ template <class ELFT> void MarkLive<ELFT>::run() {
     return;
   }
 
-  markSymbol(symtab->find(config->entry));
-  markSymbol(symtab->find(config->init));
-  markSymbol(symtab->find(config->fini));
+  markSymbol(symtab.find(config->entry));
+  markSymbol(symtab.find(config->init));
+  markSymbol(symtab.find(config->fini));
   for (StringRef s : config->undefined)
-    markSymbol(symtab->find(s));
+    markSymbol(symtab.find(s));
   for (StringRef s : script->referencedSymbols)
-    markSymbol(symtab->find(s));
-
-  // Preserve special sections and those which are specified in linker
-  // script KEEP command.
-  for (InputSectionBase *sec : inputSections) {
-    // Mark .eh_frame sections as live because there are usually no relocations
-    // that point to .eh_frames. Otherwise, the garbage collector would drop
-    // all of them. We also want to preserve personality routines and LSDA
-    // referenced by .eh_frame sections, so we scan them for that here.
-    if (auto *eh = dyn_cast<EhInputSection>(sec)) {
-      eh->markLive();
-      if (!eh->numRelocations)
-        continue;
-
-      if (eh->areRelocsRela)
-        scanEhFrameSection(*eh, eh->template relas<ELFT>());
-      else
-        scanEhFrameSection(*eh, eh->template rels<ELFT>());
-    }
-
+    markSymbol(symtab.find(s));
+
+  // Mark .eh_frame sections as live because there are usually no relocations
+  // that point to .eh_frames. Otherwise, the garbage collector would drop
+  // all of them. We also want to preserve personality routines and LSDA
+  // referenced by .eh_frame sections, so we scan them for that here.
+  for (EhInputSection *eh : ctx.ehInputSections) {
+    const RelsOrRelas<ELFT> rels = eh->template relsOrRelas<ELFT>();
+    if (rels.areRelocsRel())
+      scanEhFrameSection(*eh, rels.rels);
+    else if (rels.relas.size())
+      scanEhFrameSection(*eh, rels.relas);
+  }
+  for (InputSectionBase *sec : ctx.inputSections) {
     if (sec->flags & SHF_GNU_RETAIN) {
       enqueue(sec, 0);
       continue;
@@ -268,6 +250,39 @@ template <class ELFT> void MarkLive<ELFT>::run() {
     if (sec->flags & SHF_LINK_ORDER)
       continue;
 
+    // Usually, non-SHF_ALLOC sections are not removed even if they are
+    // unreachable through relocations because reachability is not a good signal
+    // whether they are garbage or not (e.g. there is usually no section
+    // referring to a .comment section, but we want to keep it.) When a
+    // non-SHF_ALLOC section is retained, we also retain sections dependent on
+    // it.
+    //
+    // Note on SHF_LINK_ORDER: Such sections contain metadata and they
+    // have a reverse dependency on the InputSection they are linked with.
+    // We are able to garbage collect them.
+    //
+    // Note on SHF_REL{,A}: Such sections reach here only when -r
+    // or --emit-reloc were given. And they are subject of garbage
+    // collection because, if we remove a text section, we also
+    // remove its relocation section.
+    //
+    // Note on nextInSectionGroup: The ELF spec says that group sections are
+    // included or omitted as a unit. We take the interpretation that:
+    //
+    // - Group members (nextInSectionGroup != nullptr) are subject to garbage
+    //   collection.
+    // - Groups members are retained or discarded as a unit.
+    if (!(sec->flags & SHF_ALLOC)) {
+      bool isRel = sec->type == SHT_REL || sec->type == SHT_RELA;
+      if (!isRel && !sec->nextInSectionGroup) {
+        sec->markLive();
+        for (InputSection *isec : sec->dependentSections)
+          isec->markLive();
+      }
+    }
+
+    // Preserve special sections and those which are specified in linker
+    // script KEEP command.
     if (isReserved(sec) || script->shouldKeep(sec)) {
       enqueue(sec, 0);
     } else if ((!config->zStartStopGC || sec->name.startswith("__libc_")) &&
@@ -275,8 +290,8 @@ template <class ELFT> void MarkLive<ELFT>::run() {
       // As a workaround for glibc libc.a before 2.34
       // (https://sourceware.org/PR27492), retain __libc_atexit and similar
       // sections regardless of zStartStopGC.
-      cNamedSections[saver.save("__start_" + sec->name)].push_back(sec);
-      cNamedSections[saver.save("__stop_" + sec->name)].push_back(sec);
+      cNamedSections[saver().save("__start_" + sec->name)].push_back(sec);
+      cNamedSections[saver().save("__stop_" + sec->name)].push_back(sec);
     }
   }
 
@@ -288,13 +303,11 @@ template <class ELFT> void MarkLive<ELFT>::mark() {
   while (!queue.empty()) {
     InputSectionBase &sec = *queue.pop_back_val();
 
-    if (sec.areRelocsRela) {
-      for (const typename ELFT::Rela &rel : sec.template relas<ELFT>())
-        resolveReloc(sec, rel, false);
-    } else {
-      for (const typename ELFT::Rel &rel : sec.template rels<ELFT>())
-        resolveReloc(sec, rel, false);
-    }
+    const RelsOrRelas<ELFT> rels = sec.template relsOrRelas<ELFT>();
+    for (const typename ELFT::Rel &rel : rels.rels)
+      resolveReloc(sec, rel, false);
+    for (const typename ELFT::Rela &rel : rels.relas)
+      resolveReloc(sec, rel, false);
 
     for (InputSectionBase *isec : sec.dependentSections)
       enqueue(isec, 0);
@@ -315,18 +328,18 @@ template <class ELFT> void MarkLive<ELFT>::mark() {
 // to from __start_/__stop_ symbols because there will only be one set of
 // symbols for the whole program.
 template <class ELFT> void MarkLive<ELFT>::moveToMain() {
-  for (InputFile *file : objectFiles)
+  for (ELFFileBase *file : ctx.objectFiles)
     for (Symbol *s : file->getSymbols())
       if (auto *d = dyn_cast<Defined>(s))
         if ((d->type == STT_GNU_IFUNC || d->type == STT_TLS) && d->section &&
             d->section->isLive())
           markSymbol(s);
 
-  for (InputSectionBase *sec : inputSections) {
+  for (InputSectionBase *sec : ctx.inputSections) {
     if (!sec->isLive() || !isValidCIdentifier(sec->name))
       continue;
-    if (symtab->find(("__start_" + sec->name).str()) ||
-        symtab->find(("__stop_" + sec->name).str()))
+    if (symtab.find(("__start_" + sec->name).str()) ||
+        symtab.find(("__stop_" + sec->name).str()))
       enqueue(sec, 0);
   }
 
@@ -338,58 +351,18 @@ template <class ELFT> void MarkLive<ELFT>::moveToMain() {
 // so that they are emitted to the output file.
 template <class ELFT> void elf::markLive() {
   llvm::TimeTraceScope timeScope("markLive");
-  // If -gc-sections is not given, no sections are removed.
+  // If --gc-sections is not given, retain all input sections.
   if (!config->gcSections) {
-    for (InputSectionBase *sec : inputSections)
-      sec->markLive();
-
     // If a DSO defines a symbol referenced in a regular object, it is needed.
-    for (Symbol *sym : symtab->symbols())
+    for (Symbol *sym : symtab.getSymbols())
       if (auto *s = dyn_cast<SharedSymbol>(sym))
         if (s->isUsedInRegularObj && !s->isWeak())
-          s->getFile().isNeeded = true;
+          cast<SharedFile>(s->file)->isNeeded = true;
     return;
   }
 
-  // Otherwise, do mark-sweep GC.
-  //
-  // The -gc-sections option works only for SHF_ALLOC sections (sections that
-  // are memory-mapped at runtime). So we can unconditionally make non-SHF_ALLOC
-  // sections alive except SHF_LINK_ORDER, SHT_REL/SHT_RELA sections, and
-  // sections in a group.
-  //
-  // Usually, non-SHF_ALLOC sections are not removed even if they are
-  // unreachable through relocations because reachability is not a good signal
-  // whether they are garbage or not (e.g. there is usually no section referring
-  // to a .comment section, but we want to keep it.) When a non-SHF_ALLOC
-  // section is retained, we also retain sections dependent on it.
-  //
-  // Note on SHF_LINK_ORDER: Such sections contain metadata and they
-  // have a reverse dependency on the InputSection they are linked with.
-  // We are able to garbage collect them.
-  //
-  // Note on SHF_REL{,A}: Such sections reach here only when -r
-  // or -emit-reloc were given. And they are subject of garbage
-  // collection because, if we remove a text section, we also
-  // remove its relocation section.
-  //
-  // Note on nextInSectionGroup: The ELF spec says that group sections are
-  // included or omitted as a unit. We take the interpretation that:
-  //
-  // - Group members (nextInSectionGroup != nullptr) are subject to garbage
-  //   collection.
-  // - Groups members are retained or discarded as a unit.
-  for (InputSectionBase *sec : inputSections) {
-    bool isAlloc = (sec->flags & SHF_ALLOC);
-    bool isLinkOrder = (sec->flags & SHF_LINK_ORDER);
-    bool isRel = (sec->type == SHT_REL || sec->type == SHT_RELA);
-
-    if (!isAlloc && !isLinkOrder && !isRel && !sec->nextInSectionGroup) {
-      sec->markLive();
-      for (InputSection *isec : sec->dependentSections)
-        isec->markLive();
-    }
-  }
+  for (InputSectionBase *sec : ctx.inputSections)
+    sec->markDead();
 
   // Follow the graph to mark all live sections.
   for (unsigned curPart = 1; curPart <= partitions.size(); ++curPart)
@@ -403,7 +376,7 @@ template <class ELFT> void elf::markLive() {
 
   // Report garbage-collected sections.
   if (config->printGcSections)
-    for (InputSectionBase *sec : inputSections)
+    for (InputSectionBase *sec : ctx.inputSections)
       if (!sec->isLive())
         message("removing unused section " + toString(sec));
 }
index 63b5b26..ef62fdf 100644 (file)
@@ -9,12 +9,10 @@
 #ifndef LLD_ELF_MARKLIVE_H
 #define LLD_ELF_MARKLIVE_H
 
-namespace lld {
-namespace elf {
+namespace lld::elf {
 
 template <class ELFT> void markLive();
 
-} // namespace elf
-} // namespace lld
+}
 
 #endif // LLD_ELF_MARKLIVE_H
index 088d1cd..dc40f07 100644 (file)
@@ -8,21 +8,25 @@
 
 #include "OutputSections.h"
 #include "Config.h"
+#include "InputFiles.h"
 #include "LinkerScript.h"
-#include "SymbolTable.h"
+#include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
+#include "lld/Common/Arrays.h"
 #include "lld/Common/Memory.h"
-#include "lld/Common/Strings.h"
 #include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/Config/llvm-config.h" // LLVM_ENABLE_ZLIB
 #include "llvm/Support/Compression.h"
-#include "llvm/Support/MD5.h"
-#include "llvm/Support/MathExtras.h"
 #include "llvm/Support/Parallel.h"
-#include "llvm/Support/SHA1.h"
+#include "llvm/Support/Path.h"
 #include "llvm/Support/TimeProfiler.h"
-#include <regex>
-#include <unordered_set>
+#if LLVM_ENABLE_ZLIB
+#include <zlib.h>
+#endif
+#if LLVM_ENABLE_ZSTD
+#include <zstd.h>
+#endif
 
 using namespace llvm;
 using namespace llvm::dwarf;
@@ -33,7 +37,6 @@ using namespace lld;
 using namespace lld::elf;
 
 uint8_t *Out::bufferStart;
-uint8_t Out::first;
 PhdrEntry *Out::tlsPhdr;
 OutputSection *Out::elfHeader;
 OutputSection *Out::programHeaders;
@@ -41,7 +44,7 @@ OutputSection *Out::preinitArray;
 OutputSection *Out::initArray;
 OutputSection *Out::finiArray;
 
-std::vector<OutputSection *> elf::outputSections;
+SmallVector<OutputSection *, 0> elf::outputSections;
 
 uint32_t OutputSection::getPhdrFlags() const {
   uint32_t ret = 0;
@@ -57,7 +60,7 @@ uint32_t OutputSection::getPhdrFlags() const {
 template <class ELFT>
 void OutputSection::writeHeaderTo(typename ELFT::Shdr *shdr) {
   shdr->sh_entsize = entsize;
-  shdr->sh_addralign = alignment;
+  shdr->sh_addralign = addralign;
   shdr->sh_type = type;
   shdr->sh_offset = offset;
   shdr->sh_flags = flags;
@@ -69,8 +72,7 @@ void OutputSection::writeHeaderTo(typename ELFT::Shdr *shdr) {
 }
 
 OutputSection::OutputSection(StringRef name, uint32_t type, uint64_t flags)
-    : BaseCommand(OutputSectionKind),
-      SectionBase(Output, name, flags, /*Entsize*/ 0, /*Alignment*/ 1, type,
+    : SectionBase(Output, name, flags, /*Entsize*/ 0, /*Alignment*/ 1, type,
                   /*Info*/ 0, /*Link*/ 0) {}
 
 // We allow sections of types listed below to merged into a
@@ -100,10 +102,9 @@ static bool canMergeToProgbits(unsigned type) {
 void OutputSection::recordSection(InputSectionBase *isec) {
   partition = isec->partition;
   isec->parent = this;
-  if (sectionCommands.empty() ||
-      !isa<InputSectionDescription>(sectionCommands.back()))
-    sectionCommands.push_back(make<InputSectionDescription>(""));
-  auto *isd = cast<InputSectionDescription>(sectionCommands.back());
+  if (commands.empty() || !isa<InputSectionDescription>(commands.back()))
+    commands.push_back(make<InputSectionDescription>(""));
+  auto *isd = cast<InputSectionDescription>(commands.back());
   isd->sectionBases.push_back(isec);
 }
 
@@ -111,32 +112,40 @@ void OutputSection::recordSection(InputSectionBase *isec) {
 // isec. Also check whether the InputSection flags and type are consistent with
 // other InputSections.
 void OutputSection::commitSection(InputSection *isec) {
+  if (LLVM_UNLIKELY(type != isec->type)) {
+    if (hasInputSections || typeIsSet) {
+      if (typeIsSet || !canMergeToProgbits(type) ||
+          !canMergeToProgbits(isec->type)) {
+        // Changing the type of a (NOLOAD) section is fishy, but some projects
+        // (e.g. https://github.com/ClangBuiltLinux/linux/issues/1597)
+        // traditionally rely on the behavior. Issue a warning to not break
+        // them. Other types get an error.
+        auto diagnose = type == SHT_NOBITS ? warn : errorOrWarn;
+        diagnose("section type mismatch for " + isec->name + "\n>>> " +
+                 toString(isec) + ": " +
+                 getELFSectionTypeName(config->emachine, isec->type) +
+                 "\n>>> output section " + name + ": " +
+                 getELFSectionTypeName(config->emachine, type));
+      }
+      if (!typeIsSet)
+        type = SHT_PROGBITS;
+    } else {
+      type = isec->type;
+    }
+  }
   if (!hasInputSections) {
     // If IS is the first section to be added to this section,
     // initialize type, entsize and flags from isec.
     hasInputSections = true;
-    type = isec->type;
     entsize = isec->entsize;
     flags = isec->flags;
   } else {
     // Otherwise, check if new type or flags are compatible with existing ones.
     if ((flags ^ isec->flags) & SHF_TLS)
-      error("incompatible section flags for " + name + "\n>>> " + toString(isec) +
-            ": 0x" + utohexstr(isec->flags) + "\n>>> output section " + name +
-            ": 0x" + utohexstr(flags));
-
-    if (type != isec->type) {
-      if (!canMergeToProgbits(type) || !canMergeToProgbits(isec->type))
-        error("section type mismatch for " + isec->name + "\n>>> " +
-              toString(isec) + ": " +
-              getELFSectionTypeName(config->emachine, isec->type) +
-              "\n>>> output section " + name + ": " +
-              getELFSectionTypeName(config->emachine, type));
-      type = SHT_PROGBITS;
-    }
+      error("incompatible section flags for " + name + "\n>>> " +
+            toString(isec) + ": 0x" + utohexstr(isec->flags) +
+            "\n>>> output section " + name + ": 0x" + utohexstr(flags));
   }
-  if (noload)
-    type = SHT_NOBITS;
 
   isec->parent = this;
   uint64_t andMask =
@@ -148,7 +157,7 @@ void OutputSection::commitSection(InputSection *isec) {
   if (nonAlloc)
     flags &= ~(uint64_t)SHF_ALLOC;
 
-  alignment = std::max(alignment, isec->alignment);
+  addralign = std::max(addralign, isec->addralign);
 
   // If this section contains a table of fixed-size entries, sh_entsize
   // holds the element size. If it contains elements of different size we
@@ -157,6 +166,15 @@ void OutputSection::commitSection(InputSection *isec) {
     entsize = 0;
 }
 
+static MergeSyntheticSection *createMergeSynthetic(StringRef name,
+                                                   uint32_t type,
+                                                   uint64_t flags,
+                                                   uint32_t addralign) {
+  if ((flags & SHF_STRINGS) && config->optimize >= 2)
+    return make<MergeTailSection>(name, type, flags, addralign);
+  return make<MergeNoTailSection>(name, type, flags, addralign);
+}
+
 // This function scans over the InputSectionBase list sectionBases to create
 // InputSectionDescription::sections.
 //
@@ -166,15 +184,15 @@ void OutputSection::commitSection(InputSection *isec) {
 // to compute an output offset for each piece of each input section.
 void OutputSection::finalizeInputSections() {
   std::vector<MergeSyntheticSection *> mergeSections;
-  for (BaseCommand *base : sectionCommands) {
-    auto *cmd = dyn_cast<InputSectionDescription>(base);
-    if (!cmd)
+  for (SectionCommand *cmd : commands) {
+    auto *isd = dyn_cast<InputSectionDescription>(cmd);
+    if (!isd)
       continue;
-    cmd->sections.reserve(cmd->sectionBases.size());
-    for (InputSectionBase *s : cmd->sectionBases) {
+    isd->sections.reserve(isd->sectionBases.size());
+    for (InputSectionBase *s : isd->sectionBases) {
       MergeInputSection *ms = dyn_cast<MergeInputSection>(s);
       if (!ms) {
-        cmd->sections.push_back(cast<InputSection>(s));
+        isd->sections.push_back(cast<InputSection>(s));
         continue;
       }
 
@@ -195,25 +213,25 @@ void OutputSection::finalizeInputSections() {
         //
         // SHF_STRINGS section with different alignments should not be merged.
         return sec->flags == ms->flags && sec->entsize == ms->entsize &&
-               (sec->alignment == ms->alignment || !(sec->flags & SHF_STRINGS));
+               (sec->addralign == ms->addralign || !(sec->flags & SHF_STRINGS));
       });
       if (i == mergeSections.end()) {
         MergeSyntheticSection *syn =
-            createMergeSynthetic(name, ms->type, ms->flags, ms->alignment);
+            createMergeSynthetic(name, ms->type, ms->flags, ms->addralign);
         mergeSections.push_back(syn);
         i = std::prev(mergeSections.end());
         syn->entsize = ms->entsize;
-        cmd->sections.push_back(syn);
+        isd->sections.push_back(syn);
       }
       (*i)->addSection(ms);
     }
 
     // sectionBases should not be used from this point onwards. Clear it to
     // catch misuses.
-    cmd->sectionBases.clear();
+    isd->sectionBases.clear();
 
     // Some input sections may be removed from the list after ICF.
-    for (InputSection *s : cmd->sections)
+    for (InputSection *s : isd->sections)
       commitSection(s);
   }
   for (auto *ms : mergeSections)
@@ -237,13 +255,9 @@ uint64_t elf::getHeaderSize() {
   return Out::elfHeader->size + Out::programHeaders->size;
 }
 
-bool OutputSection::classof(const BaseCommand *c) {
-  return c->kind == OutputSectionKind;
-}
-
 void OutputSection::sort(llvm::function_ref<int(InputSectionBase *s)> order) {
   assert(isLive());
-  for (BaseCommand *b : sectionCommands)
+  for (SectionCommand *b : commands)
     if (auto *isd = dyn_cast<InputSectionDescription>(b))
       sortByOrder(isd->sections, order);
 }
@@ -277,38 +291,140 @@ static void fill(uint8_t *buf, size_t size,
   memcpy(buf + i, filler.data(), size - i);
 }
 
+#if LLVM_ENABLE_ZLIB
+static SmallVector<uint8_t, 0> deflateShard(ArrayRef<uint8_t> in, int level,
+                                            int flush) {
+  // 15 and 8 are default. windowBits=-15 is negative to generate raw deflate
+  // data with no zlib header or trailer.
+  z_stream s = {};
+  deflateInit2(&s, level, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
+  s.next_in = const_cast<uint8_t *>(in.data());
+  s.avail_in = in.size();
+
+  // Allocate a buffer of half of the input size, and grow it by 1.5x if
+  // insufficient.
+  SmallVector<uint8_t, 0> out;
+  size_t pos = 0;
+  out.resize_for_overwrite(std::max<size_t>(in.size() / 2, 64));
+  do {
+    if (pos == out.size())
+      out.resize_for_overwrite(out.size() * 3 / 2);
+    s.next_out = out.data() + pos;
+    s.avail_out = out.size() - pos;
+    (void)deflate(&s, flush);
+    pos = s.next_out - out.data();
+  } while (s.avail_out == 0);
+  assert(s.avail_in == 0);
+
+  out.truncate(pos);
+  deflateEnd(&s);
+  return out;
+}
+#endif
+
 // Compress section contents if this section contains debug info.
 template <class ELFT> void OutputSection::maybeCompress() {
   using Elf_Chdr = typename ELFT::Chdr;
+  (void)sizeof(Elf_Chdr);
 
   // Compress only DWARF debug sections.
-  if (!config->compressDebugSections || (flags & SHF_ALLOC) ||
-      !name.startswith(".debug_"))
+  if (config->compressDebugSections == DebugCompressionType::None ||
+      (flags & SHF_ALLOC) || !name.startswith(".debug_") || size == 0)
     return;
 
   llvm::TimeTraceScope timeScope("Compress debug sections");
+  compressed.uncompressedSize = size;
+  auto buf = std::make_unique<uint8_t[]>(size);
+  // Write uncompressed data to a temporary zero-initialized buffer.
+  {
+    parallel::TaskGroup tg;
+    writeTo<ELFT>(buf.get(), tg);
+  }
 
-  // Create a section header.
-  zDebugHeader.resize(sizeof(Elf_Chdr));
-  auto *hdr = reinterpret_cast<Elf_Chdr *>(zDebugHeader.data());
-  hdr->ch_type = ELFCOMPRESS_ZLIB;
-  hdr->ch_size = size;
-  hdr->ch_addralign = alignment;
-
-  // Write section contents to a temporary buffer and compress it.
-  std::vector<uint8_t> buf(size);
-  writeTo<ELFT>(buf.data());
-  // We chose 1 as the default compression level because it is the fastest. If
-  // -O2 is given, we use level 6 to compress debug info more by ~15%. We found
-  // that level 7 to 9 doesn't make much difference (~1% more compression) while
-  // they take significant amount of time (~2x), so level 6 seems enough.
-  if (Error e = zlib::compress(toStringRef(buf), compressedData,
-                               config->optimize >= 2 ? 6 : 1))
-    fatal("compress failed: " + llvm::toString(std::move(e)));
-
-  // Update section headers.
-  size = sizeof(Elf_Chdr) + compressedData.size();
+#if LLVM_ENABLE_ZSTD
+  // Use ZSTD's streaming compression API which permits parallel workers working
+  // on the stream. See http://facebook.github.io/zstd/zstd_manual.html
+  // "Streaming compression - HowTo".
+  if (config->compressDebugSections == DebugCompressionType::Zstd) {
+    // Allocate a buffer of half of the input size, and grow it by 1.5x if
+    // insufficient.
+    compressed.shards = std::make_unique<SmallVector<uint8_t, 0>[]>(1);
+    SmallVector<uint8_t, 0> &out = compressed.shards[0];
+    out.resize_for_overwrite(std::max<size_t>(size / 2, 32));
+    size_t pos = 0;
+
+    ZSTD_CCtx *cctx = ZSTD_createCCtx();
+    // Ignore error if zstd was not built with ZSTD_MULTITHREAD.
+    (void)ZSTD_CCtx_setParameter(cctx, ZSTD_c_nbWorkers,
+                                 parallel::strategy.compute_thread_count());
+    ZSTD_outBuffer zob = {out.data(), out.size(), 0};
+    ZSTD_EndDirective directive = ZSTD_e_continue;
+    const size_t blockSize = ZSTD_CStreamInSize();
+    do {
+      const size_t n = std::min(static_cast<size_t>(size - pos), blockSize);
+      if (n == size - pos)
+        directive = ZSTD_e_end;
+      ZSTD_inBuffer zib = {buf.get() + pos, n, 0};
+      size_t bytesRemaining = 0;
+      while (zib.pos != zib.size ||
+             (directive == ZSTD_e_end && bytesRemaining != 0)) {
+        if (zob.pos == zob.size) {
+          out.resize_for_overwrite(out.size() * 3 / 2);
+          zob.dst = out.data();
+          zob.size = out.size();
+        }
+        bytesRemaining = ZSTD_compressStream2(cctx, &zob, &zib, directive);
+        assert(!ZSTD_isError(bytesRemaining));
+      }
+      pos += n;
+    } while (directive != ZSTD_e_end);
+    out.resize(zob.pos);
+    ZSTD_freeCCtx(cctx);
+
+    size = sizeof(Elf_Chdr) + out.size();
+    flags |= SHF_COMPRESSED;
+    return;
+  }
+#endif
+
+#if LLVM_ENABLE_ZLIB
+  // We chose 1 (Z_BEST_SPEED) as the default compression level because it is
+  // the fastest. If -O2 is given, we use level 6 to compress debug info more by
+  // ~15%. We found that level 7 to 9 doesn't make much difference (~1% more
+  // compression) while they take significant amount of time (~2x), so level 6
+  // seems enough.
+  const int level = config->optimize >= 2 ? 6 : Z_BEST_SPEED;
+
+  // Split input into 1-MiB shards.
+  constexpr size_t shardSize = 1 << 20;
+  auto shardsIn = split(ArrayRef<uint8_t>(buf.get(), size), shardSize);
+  const size_t numShards = shardsIn.size();
+
+  // Compress shards and compute Alder-32 checksums. Use Z_SYNC_FLUSH for all
+  // shards but the last to flush the output to a byte boundary to be
+  // concatenated with the next shard.
+  auto shardsOut = std::make_unique<SmallVector<uint8_t, 0>[]>(numShards);
+  auto shardsAdler = std::make_unique<uint32_t[]>(numShards);
+  parallelFor(0, numShards, [&](size_t i) {
+    shardsOut[i] = deflateShard(shardsIn[i], level,
+                                i != numShards - 1 ? Z_SYNC_FLUSH : Z_FINISH);
+    shardsAdler[i] = adler32(1, shardsIn[i].data(), shardsIn[i].size());
+  });
+
+  // Update section size and combine Alder-32 checksums.
+  uint32_t checksum = 1;       // Initial Adler-32 value
+  size = sizeof(Elf_Chdr) + 2; // Elf_Chdir and zlib header
+  for (size_t i = 0; i != numShards; ++i) {
+    size += shardsOut[i].size();
+    checksum = adler32_combine(checksum, shardsAdler[i], shardsIn[i].size());
+  }
+  size += 4; // checksum
+
+  compressed.shards = std::move(shardsOut);
+  compressed.numShards = numShards;
+  compressed.checksum = checksum;
   flags |= SHF_COMPRESSED;
+#endif
 }
 
 static void writeInt(uint8_t *buf, uint64_t data, uint64_t size) {
@@ -324,62 +440,117 @@ static void writeInt(uint8_t *buf, uint64_t data, uint64_t size) {
     llvm_unreachable("unsupported Size argument");
 }
 
-template <class ELFT> void OutputSection::writeTo(uint8_t *buf) {
+template <class ELFT>
+void OutputSection::writeTo(uint8_t *buf, parallel::TaskGroup &tg) {
+  llvm::TimeTraceScope timeScope("Write sections", name);
   if (type == SHT_NOBITS)
     return;
 
-  // If -compress-debug-section is specified and if this is a debug section,
+  // If --compress-debug-section is specified and if this is a debug section,
   // we've already compressed section contents. If that's the case,
   // just write it down.
-  if (!compressedData.empty()) {
-    memcpy(buf, zDebugHeader.data(), zDebugHeader.size());
-    memcpy(buf + zDebugHeader.size(), compressedData.data(),
-           compressedData.size());
+  if (compressed.shards) {
+    auto *chdr = reinterpret_cast<typename ELFT::Chdr *>(buf);
+    chdr->ch_size = compressed.uncompressedSize;
+    chdr->ch_addralign = addralign;
+    buf += sizeof(*chdr);
+    if (config->compressDebugSections == DebugCompressionType::Zstd) {
+      chdr->ch_type = ELFCOMPRESS_ZSTD;
+      memcpy(buf, compressed.shards[0].data(), compressed.shards[0].size());
+      return;
+    }
+    chdr->ch_type = ELFCOMPRESS_ZLIB;
+
+    // Compute shard offsets.
+    auto offsets = std::make_unique<size_t[]>(compressed.numShards);
+    offsets[0] = 2; // zlib header
+    for (size_t i = 1; i != compressed.numShards; ++i)
+      offsets[i] = offsets[i - 1] + compressed.shards[i - 1].size();
+
+    buf[0] = 0x78; // CMF
+    buf[1] = 0x01; // FLG: best speed
+    parallelFor(0, compressed.numShards, [&](size_t i) {
+      memcpy(buf + offsets[i], compressed.shards[i].data(),
+             compressed.shards[i].size());
+    });
+
+    write32be(buf + (size - sizeof(*chdr) - 4), compressed.checksum);
     return;
   }
 
   // Write leading padding.
-  std::vector<InputSection *> sections = getInputSections(this);
+  ArrayRef<InputSection *> sections = getInputSections(*this, storage);
   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) {
-    InputSection *isec = sections[i];
-    isec->writeTo<ELFT>(buf);
-
-    // Fill gaps between sections.
-    if (nonZeroFiller) {
-      uint8_t *start = buf + isec->outSecOff + isec->getSize();
-      uint8_t *end;
-      if (i + 1 == sections.size())
-        end = buf + size;
+  auto fn = [=](size_t begin, size_t end) {
+    size_t numSections = sections.size();
+    for (size_t i = begin; i != end; ++i) {
+      InputSection *isec = sections[i];
+      if (auto *s = dyn_cast<SyntheticSection>(isec))
+        s->writeTo(buf + isec->outSecOff);
       else
-        end = buf + sections[i + 1]->outSecOff;
-      if (isec->nopFiller) {
-        assert(target->nopInstrs);
-        nopInstrFill(start, end - start);
-      } else
-        fill(start, end - start, filler);
+        isec->writeTo<ELFT>(buf + isec->outSecOff);
+
+      // Fill gaps between sections.
+      if (nonZeroFiller) {
+        uint8_t *start = buf + isec->outSecOff + isec->getSize();
+        uint8_t *end;
+        if (i + 1 == numSections)
+          end = buf + size;
+        else
+          end = buf + sections[i + 1]->outSecOff;
+        if (isec->nopFiller) {
+          assert(target->nopInstrs);
+          nopInstrFill(start, end - start);
+        } else
+          fill(start, end - start, filler);
+      }
     }
-  });
-
-  // Linker scripts may have BYTE()-family commands with which you
-  // can write arbitrary bytes to the output. Process them if any.
-  for (BaseCommand *base : sectionCommands)
-    if (auto *data = dyn_cast<ByteCommand>(base))
+  };
+
+  // If there is any BYTE()-family command (rare), write the section content
+  // first then process BYTE to overwrite the filler content. The write is
+  // serial due to the limitation of llvm/Support/Parallel.h.
+  bool written = false;
+  size_t numSections = sections.size();
+  for (SectionCommand *cmd : commands)
+    if (auto *data = dyn_cast<ByteCommand>(cmd)) {
+      if (!std::exchange(written, true))
+        fn(0, numSections);
       writeInt(buf + data->offset, data->expression().getValue(), data->size);
-}
+    }
+  if (written || !numSections)
+    return;
 
-static void finalizeShtGroup(OutputSection *os,
-                             InputSection *section) {
-  assert(config->relocatable);
+  // There is no data command. Write content asynchronously to overlap the write
+  // time with other output sections. Note, if a linker script specifies
+  // overlapping output sections (needs --noinhibit-exec or --no-check-sections
+  // to supress the error), the output may be non-deterministic.
+  const size_t taskSizeLimit = 4 << 20;
+  for (size_t begin = 0, i = 0, taskSize = 0;;) {
+    taskSize += sections[i]->getSize();
+    bool done = ++i == numSections;
+    if (done || taskSize >= taskSizeLimit) {
+      tg.execute([=] { fn(begin, i); });
+      if (done)
+        break;
+      begin = i;
+      taskSize = 0;
+    }
+  }
+}
 
+static void finalizeShtGroup(OutputSection *os, InputSection *section) {
   // sh_link field for SHT_GROUP sections should contain the section index of
   // the symbol table.
   os->link = in.symTab->getParent()->sectionIndex;
 
+  if (!section)
+    return;
+
   // sh_info then contain index of an entry in symbol table section which
   // provides signature of the section group.
   ArrayRef<Symbol *> symbols = section->file->getSymbols();
@@ -387,7 +558,7 @@ static void finalizeShtGroup(OutputSection *os,
 
   // Some group members may be combined or discarded, so we need to compute the
   // new size. The content will be rewritten in InputSection::copyShtGroup.
-  std::unordered_set<uint32_t> seen;
+  DenseSet<uint32_t> seen;
   ArrayRef<InputSectionBase *> sections = section->file->getSections();
   for (const uint32_t &idx : section->getDataAs<uint32_t>().slice(1))
     if (OutputSection *osec = sections[read32(&idx)]->getOutputSection())
@@ -437,18 +608,15 @@ void OutputSection::finalize() {
 // crtbegin files.
 //
 // Gcc uses any of crtbegin[<empty>|S|T].o.
-// Clang uses Gcc's plus clang_rt.crtbegin[<empty>|S|T][-<arch>|<empty>].o.
-
-static bool isCrtbegin(StringRef s) {
-  static std::regex re(R"((clang_rt\.)?crtbegin[ST]?(-.*)?\.o)");
-  s = sys::path::filename(s);
-  return std::regex_match(s.begin(), s.end(), re);
-}
+// Clang uses Gcc's plus clang_rt.crtbegin[-<arch>|<empty>].o.
 
-static bool isCrtend(StringRef s) {
-  static std::regex re(R"((clang_rt\.)?crtend[ST]?(-.*)?\.o)");
+static bool isCrt(StringRef s, StringRef beginEnd) {
   s = sys::path::filename(s);
-  return std::regex_match(s.begin(), s.end(), re);
+  if (!s.consume_back(".o"))
+    return false;
+  if (s.consume_front("clang_rt."))
+    return s.consume_front(beginEnd);
+  return s.consume_front(beginEnd) && s.size() <= 1;
 }
 
 // .ctors and .dtors are sorted by this order:
@@ -470,12 +638,12 @@ static bool isCrtend(StringRef s) {
 // are too many real-world use cases of .ctors, so we had no choice to
 // support that with this rather ad-hoc semantics.
 static bool compCtors(const InputSection *a, const InputSection *b) {
-  bool beginA = isCrtbegin(a->file->getName());
-  bool beginB = isCrtbegin(b->file->getName());
+  bool beginA = isCrt(a->file->getName(), "crtbegin");
+  bool beginB = isCrt(b->file->getName(), "crtbegin");
   if (beginA != beginB)
     return beginA;
-  bool endA = isCrtend(a->file->getName());
-  bool endB = isCrtend(b->file->getName());
+  bool endA = isCrt(a->file->getName(), "crtend");
+  bool endB = isCrt(b->file->getName(), "crtend");
   if (endA != endB)
     return endB;
   return getPriority(a->name) > getPriority(b->name);
@@ -485,8 +653,8 @@ static bool compCtors(const InputSection *a, const InputSection *b) {
 // Unfortunately, the rules are different from the one for .{init,fini}_array.
 // Read the comment above.
 void OutputSection::sortCtorsDtors() {
-  assert(sectionCommands.size() == 1);
-  auto *isd = cast<InputSectionDescription>(sectionCommands[0]);
+  assert(commands.size() == 1);
+  auto *isd = cast<InputSectionDescription>(commands[0]);
   llvm::stable_sort(isd->sections, compCtors);
 }
 
@@ -505,19 +673,31 @@ int elf::getPriority(StringRef s) {
 }
 
 InputSection *elf::getFirstInputSection(const OutputSection *os) {
-  for (BaseCommand *base : os->sectionCommands)
-    if (auto *isd = dyn_cast<InputSectionDescription>(base))
+  for (SectionCommand *cmd : os->commands)
+    if (auto *isd = dyn_cast<InputSectionDescription>(cmd))
       if (!isd->sections.empty())
         return isd->sections[0];
   return nullptr;
 }
 
-std::vector<InputSection *> elf::getInputSections(const OutputSection *os) {
-  std::vector<InputSection *> ret;
-  for (BaseCommand *base : os->sectionCommands)
-    if (auto *isd = dyn_cast<InputSectionDescription>(base))
-      ret.insert(ret.end(), isd->sections.begin(), isd->sections.end());
-  return ret;
+ArrayRef<InputSection *>
+elf::getInputSections(const OutputSection &os,
+                      SmallVector<InputSection *, 0> &storage) {
+  ArrayRef<InputSection *> ret;
+  storage.clear();
+  for (SectionCommand *cmd : os.commands) {
+    auto *isd = dyn_cast<InputSectionDescription>(cmd);
+    if (!isd)
+      continue;
+    if (ret.empty()) {
+      ret = isd->sections;
+    } else {
+      if (storage.empty())
+        storage.assign(ret.begin(), ret.end());
+      storage.insert(storage.end(), isd->sections.begin(), isd->sections.end());
+    }
+  }
+  return storage.empty() ? ret : ArrayRef(storage);
 }
 
 // Sorts input sections by section name suffixes, so that .foo.N comes
@@ -542,8 +722,9 @@ std::array<uint8_t, 4> OutputSection::getFiller() {
 void OutputSection::checkDynRelAddends(const uint8_t *bufStart) {
   assert(config->writeAddends && config->checkDynamicRelocs);
   assert(type == SHT_REL || type == SHT_RELA);
-  std::vector<InputSection *> sections = getInputSections(this);
-  parallelForEachN(0, sections.size(), [&](size_t i) {
+  SmallVector<InputSection *, 0> storage;
+  ArrayRef<InputSection *> sections = getInputSections(*this, storage);
+  parallelFor(0, sections.size(), [&](size_t i) {
     // When linking with -r or --emit-relocs we might also call this function
     // for input .rel[a].<sec> sections which we simply pass through to the
     // output. We skip over those and only look at the synthetic relocation
@@ -552,7 +733,7 @@ void OutputSection::checkDynRelAddends(const uint8_t *bufStart) {
     if (!sec)
       return;
     for (const DynamicReloc &rel : sec->relocs) {
-      int64_t addend = rel.computeAddend();
+      int64_t addend = rel.addend;
       const OutputSection *relOsec = rel.inputSec->getOutputSection();
       assert(relOsec != nullptr && "missing output section for relocation");
       const uint8_t *relocTarget =
@@ -579,10 +760,14 @@ template void OutputSection::writeHeaderTo<ELF32BE>(ELF32BE::Shdr *Shdr);
 template void OutputSection::writeHeaderTo<ELF64LE>(ELF64LE::Shdr *Shdr);
 template void OutputSection::writeHeaderTo<ELF64BE>(ELF64BE::Shdr *Shdr);
 
-template void OutputSection::writeTo<ELF32LE>(uint8_t *Buf);
-template void OutputSection::writeTo<ELF32BE>(uint8_t *Buf);
-template void OutputSection::writeTo<ELF64LE>(uint8_t *Buf);
-template void OutputSection::writeTo<ELF64BE>(uint8_t *Buf);
+template void OutputSection::writeTo<ELF32LE>(uint8_t *,
+                                              llvm::parallel::TaskGroup &);
+template void OutputSection::writeTo<ELF32BE>(uint8_t *,
+                                              llvm::parallel::TaskGroup &);
+template void OutputSection::writeTo<ELF64LE>(uint8_t *,
+                                              llvm::parallel::TaskGroup &);
+template void OutputSection::writeTo<ELF64BE>(uint8_t *,
+                                              llvm::parallel::TaskGroup &);
 
 template void OutputSection::maybeCompress<ELF32LE>();
 template void OutputSection::maybeCompress<ELF32BE>();
index a0f8066..c793147 100644 (file)
@@ -9,27 +9,30 @@
 #ifndef LLD_ELF_OUTPUT_SECTIONS_H
 #define LLD_ELF_OUTPUT_SECTIONS_H
 
-#include "Config.h"
 #include "InputSection.h"
 #include "LinkerScript.h"
-#include "Relocations.h"
 #include "lld/Common/LLVM.h"
-#include "llvm/MC/StringTableBuilder.h"
-#include "llvm/Object/ELF.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Parallel.h"
+
 #include <array>
 
-namespace lld {
-namespace elf {
+namespace lld::elf {
 
 struct PhdrEntry;
-class InputSection;
-class InputSectionBase;
+
+struct CompressedData {
+  std::unique_ptr<SmallVector<uint8_t, 0>[]> shards;
+  uint32_t numShards = 0;
+  uint32_t checksum = 0;
+  uint64_t uncompressedSize;
+};
 
 // This represents a section in an output file.
 // It is composed of multiple InputSections.
 // The writer creates multiple OutputSections and assign them unique,
 // non-overlapping file offsets and VAs.
-class OutputSection final : public BaseCommand, public SectionBase {
+class OutputSection final : public SectionBase {
 public:
   OutputSection(StringRef name, uint32_t type, uint64_t flags);
 
@@ -37,8 +40,6 @@ public:
     return s->kind() == SectionBase::Output;
   }
 
-  static bool classof(const BaseCommand *c);
-
   uint64_t getLMA() const { return ptLoad ? addr + ptLoad->lmaOffset : addr; }
   template <typename ELFT> void writeHeaderTo(typename ELFT::Shdr *sHdr);
 
@@ -82,15 +83,15 @@ public:
   Expr alignExpr;
   Expr lmaExpr;
   Expr subalignExpr;
-  std::vector<BaseCommand *> sectionCommands;
-  std::vector<StringRef> phdrs;
-  llvm::Optional<std::array<uint8_t, 4>> filler;
+  SmallVector<SectionCommand *, 0> commands;
+  SmallVector<StringRef, 0> phdrs;
+  std::optional<std::array<uint8_t, 4>> filler;
   ConstraintKind constraint = ConstraintKind::NoConstraint;
   std::string location;
   std::string memoryRegionName;
   std::string lmaRegionName;
   bool nonAlloc = false;
-  bool noload = false;
+  bool typeIsSet = false;
   bool expressionsUseSymbols = false;
   bool usedInExpression = false;
   bool inOverlay = false;
@@ -100,8 +101,13 @@ public:
   // that wasn't needed). This is needed for orphan placement.
   bool hasInputSections = false;
 
+  // The output section description is specified between DATA_SEGMENT_ALIGN and
+  // DATA_RELRO_END.
+  bool relro = false;
+
   void finalize();
-  template <class ELFT> void writeTo(uint8_t *buf);
+  template <class ELFT>
+  void writeTo(uint8_t *buf, llvm::parallel::TaskGroup &tg);
   // Check that the addends for dynamic relocations were written correctly.
   void checkDynRelAddends(const uint8_t *bufStart);
   template <class ELFT> void maybeCompress();
@@ -111,24 +117,36 @@ public:
   void sortCtorsDtors();
 
 private:
+  SmallVector<InputSection *, 0> storage;
+
   // Used for implementation of --compress-debug-sections option.
-  std::vector<uint8_t> zDebugHeader;
-  llvm::SmallVector<char, 0> compressedData;
+  CompressedData compressed;
 
   std::array<uint8_t, 4> getFiller();
 };
 
+struct OutputDesc final : SectionCommand {
+  OutputSection osec;
+  OutputDesc(StringRef name, uint32_t type, uint64_t flags)
+      : SectionCommand(OutputSectionKind), osec(name, type, flags) {}
+
+  static bool classof(const SectionCommand *c) {
+    return c->kind == OutputSectionKind;
+  }
+};
+
 int getPriority(StringRef s);
 
 InputSection *getFirstInputSection(const OutputSection *os);
-std::vector<InputSection *> getInputSections(const OutputSection *os);
+llvm::ArrayRef<InputSection *>
+getInputSections(const OutputSection &os,
+                 SmallVector<InputSection *, 0> &storage);
 
 // All output sections that are handled by the linker specially are
 // globally accessible. Writer initializes them, so don't use them
 // until Writer is initialized.
 struct Out {
   static uint8_t *bufferStart;
-  static uint8_t first;
   static PhdrEntry *tlsPhdr;
   static OutputSection *elfHeader;
   static OutputSection *programHeaders;
@@ -139,8 +157,8 @@ struct Out {
 
 uint64_t getHeaderSize();
 
-extern std::vector<OutputSection *> outputSections;
-} // namespace elf
-} // namespace lld
+LLVM_LIBRARY_VISIBILITY extern llvm::SmallVector<OutputSection *, 0>
+    outputSections;
+} // namespace lld::elf
 
 #endif
index a702aac..29e3ede 100644 (file)
 
 #include "lld/Common/LLVM.h"
 #include "llvm/ADT/DenseMap.h"
-#include <map>
+#include "llvm/ADT/STLExtras.h"
 #include <vector>
 
-namespace lld {
-namespace elf {
+namespace lld::elf {
 class Symbol;
 class InputSection;
 class InputSectionBase;
@@ -45,6 +44,8 @@ enum RelExpr {
   R_PC,
   R_PLT,
   R_PLT_PC,
+  R_PLT_GOTPLT,
+  R_RELAX_HINT,
   R_RELAX_GOT_PC,
   R_RELAX_GOT_PC_NOPIC,
   R_RELAX_TLS_GD_TO_IE,
@@ -62,6 +63,7 @@ enum RelExpr {
   R_TLSDESC,
   R_TLSDESC_CALL,
   R_TLSDESC_PC,
+  R_TLSDESC_GOTPLT,
   R_TLSGD_GOT,
   R_TLSGD_GOTPLT,
   R_TLSGD_PC,
@@ -115,17 +117,17 @@ struct Relocation {
 // jump instruction opcodes at basic block boundaries and are particularly
 // useful when basic block sections are enabled.
 struct JumpInstrMod {
-  JumpModType original;
   uint64_t offset;
+  JumpModType original;
   unsigned size;
 };
 
 // This function writes undefined symbol diagnostics to an internal buffer.
 // Call reportUndefinedSymbols() after calling scanRelocations() to emit
 // the diagnostics.
-template <class ELFT> void scanRelocations(InputSectionBase &);
-
-template <class ELFT> void reportUndefinedSymbols();
+template <class ELFT> void scanRelocations();
+void reportUndefinedSymbols();
+void postScanRelocations();
 
 void hexagonTLSSymbolUpdate(ArrayRef<OutputSection *> outputSections);
 bool hexagonNeedsTLSSymbol(ArrayRef<OutputSection *> outputSections);
@@ -137,12 +139,7 @@ class InputSectionDescription;
 class ThunkCreator {
 public:
   // Return true if Thunks have been added to OutputSections
-  bool createThunks(ArrayRef<OutputSection *> outputSections);
-
-  // The number of completed passes of createThunks this permits us
-  // to do one time initialization on Pass 0 and put a limit on the
-  // number of times it can be called to prevent infinite loops.
-  uint32_t pass = 0;
+  bool createThunks(uint32_t pass, ArrayRef<OutputSection *> outputSections);
 
 private:
   void mergeThunks(ArrayRef<OutputSection *> outputSections);
@@ -184,6 +181,11 @@ private:
   // so we need to make sure that there is only one of them.
   // The Mips LA25 Thunk is an example of an inline ThunkSection.
   llvm::DenseMap<InputSection *, ThunkSection *> thunkedSections;
+
+  // The number of completed passes of createThunks this permits us
+  // to do one time initialization on Pass 0 and put a limit on the
+  // number of times it can be called to prevent infinite loops.
+  uint32_t pass = 0;
 };
 
 // Return a int64_t to make sure we get the sign extension out of the way as
@@ -209,7 +211,6 @@ ArrayRef<RelTy> sortRels(ArrayRef<RelTy> rels, SmallVector<RelTy, 0> &storage) {
   }
   return rels;
 }
-} // namespace elf
-} // namespace lld
+} // namespace lld::elf
 
 #endif
index 236a188..e0a8ed7 100644 (file)
@@ -34,6 +34,8 @@
 #include "ScriptLexer.h"
 #include "lld/Common/ErrorHandler.h"
 #include "llvm/ADT/Twine.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <algorithm>
 
 using namespace llvm;
 using namespace lld;
@@ -132,10 +134,14 @@ void ScriptLexer::tokenize(MemoryBufferRef mb) {
       continue;
     }
 
-    // ">foo" is parsed to ">" and "foo", but ">>" is parsed to ">>".
-    // "|", "||", "&" and "&&" are different operators.
-    if (s.startswith("<<") || s.startswith("<=") || s.startswith(">>") ||
-        s.startswith(">=") || s.startswith("||") || s.startswith("&&")) {
+    // Some operators form separate tokens.
+    if (s.startswith("<<=") || s.startswith(">>=")) {
+      vec.push_back(s.substr(0, 3));
+      s = s.substr(3);
+      continue;
+    }
+    if (s.size() > 1 && ((s[1] == '=' && strchr("*/+-<>&|", s[0])) ||
+                         (s[0] == s[1] && strchr("<>&|", s[0])))) {
       vec.push_back(s.substr(0, 2));
       s = s.substr(2);
       continue;
@@ -190,7 +196,7 @@ bool ScriptLexer::atEOF() { return errorCount() || tokens.size() == pos; }
 // Split a given string as an expression.
 // This function returns "3", "*" and "5" for "3*5" for example.
 static std::vector<StringRef> tokenizeExpr(StringRef s) {
-  StringRef ops = "+-*/:!~=<>"; // List of operators
+  StringRef ops = "!~*/+-<>?:="; // List of operators
 
   // Quoted strings are literal strings, so we don't want to split it.
   if (s.startswith("\""))
index 405fc73..17c0b52 100644 (file)
 
 #include "lld/Common/LLVM.h"
 #include "llvm/ADT/StringRef.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include <utility>
+#include "llvm/Support/MemoryBufferRef.h"
 #include <vector>
 
-namespace lld {
-namespace elf {
+namespace lld::elf {
 
 class ScriptLexer {
 public:
@@ -53,7 +51,6 @@ private:
   size_t getColumnNumber();
 };
 
-} // namespace elf
-} // namespace lld
+} // namespace lld::elf
 
 #endif
index eed1958..34b27d2 100644 (file)
 #define LLD_ELF_SCRIPT_PARSER_H
 
 #include "lld/Common/LLVM.h"
-#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/MemoryBufferRef.h"
 
-namespace lld {
-namespace elf {
+namespace lld::elf {
 
 // Parses a linker script. Calling this function updates
 // lld::elf::config and lld::elf::script.
@@ -29,7 +28,6 @@ void readDefsym(StringRef name, MemoryBufferRef mb);
 
 bool hasWildcard(StringRef s);
 
-} // namespace elf
-} // namespace lld
+} // namespace lld::elf
 
 #endif
index 54c4b11..5255e8b 100644 (file)
@@ -9,15 +9,15 @@
 #ifndef LLD_ELF_SYMBOL_TABLE_H
 #define LLD_ELF_SYMBOL_TABLE_H
 
-#include "InputFiles.h"
 #include "Symbols.h"
-#include "lld/Common/Strings.h"
 #include "llvm/ADT/CachedHashString.h"
 #include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Compiler.h"
 
-namespace lld {
-namespace elf {
+namespace lld::elf {
+
+class InputFile;
+class SharedFile;
 
 // SymbolTable is a bucket of all known symbols, including defined,
 // undefined, or lazy symbols (the last one is symbols in archive
@@ -32,22 +32,19 @@ namespace elf {
 // add*() functions, which are called by input files as they are parsed. There
 // is one add* function per symbol type.
 class SymbolTable {
-  struct FilterOutPlaceholder {
-    bool operator()(Symbol *S) const { return !S->isPlaceholder(); }
-  };
-  using iterator = llvm::filter_iterator<std::vector<Symbol *>::const_iterator,
-                                         FilterOutPlaceholder>;
-
 public:
-  llvm::iterator_range<iterator> symbols() const {
-    return llvm::make_filter_range(symVector, FilterOutPlaceholder());
-  }
+  ArrayRef<Symbol *> getSymbols() const { return symVector; }
 
   void wrap(Symbol *sym, Symbol *real, Symbol *wrap);
 
   Symbol *insert(StringRef name);
 
-  Symbol *addSymbol(const Symbol &newSym);
+  template <typename T> Symbol *addSymbol(const T &newSym) {
+    Symbol *sym = insert(newSym.getName());
+    sym->resolve(newSym);
+    return sym;
+  }
+  Symbol *addAndCheckDuplicate(const Defined &newSym);
 
   void scanVersionScript();
 
@@ -56,7 +53,7 @@ public:
   void handleDynamicList();
 
   // Set of .so files to not link the same shared object file more than once.
-  llvm::DenseMap<StringRef, SharedFile *> soNames;
+  llvm::DenseMap<llvm::CachedHashStringRef, SharedFile *> soNames;
 
   // Comdat groups define "link once" sections. If two comdat groups have the
   // same name, only one of them is linked, and the other is ignored. This map
@@ -64,11 +61,11 @@ public:
   llvm::DenseMap<llvm::CachedHashStringRef, const InputFile *> comdatGroups;
 
 private:
-  std::vector<Symbol *> findByVersion(SymbolVersion ver);
-  std::vector<Symbol *> findAllByVersion(SymbolVersion ver,
-                                         bool includeNonDefault);
+  SmallVector<Symbol *, 0> findByVersion(SymbolVersion ver);
+  SmallVector<Symbol *, 0> findAllByVersion(SymbolVersion ver,
+                                            bool includeNonDefault);
 
-  llvm::StringMap<std::vector<Symbol *>> &getDemangledSyms();
+  llvm::StringMap<SmallVector<Symbol *, 0>> &getDemangledSyms();
   bool assignExactVersion(SymbolVersion ver, uint16_t versionId,
                           StringRef versionName, bool includeNonDefault);
   void assignWildcardVersion(SymbolVersion ver, uint16_t versionId,
@@ -82,18 +79,17 @@ private:
   // FIXME: Experiment with passing in a custom hashing or sorting the symbols
   // once symbol resolution is finished.
   llvm::DenseMap<llvm::CachedHashStringRef, int> symMap;
-  std::vector<Symbol *> symVector;
+  SmallVector<Symbol *, 0> symVector;
 
   // A map from demangled symbol names to their symbol objects.
   // This mapping is 1:N because two symbols with different versions
   // can have the same name. We use this map to handle "extern C++ {}"
   // directive in version scripts.
-  llvm::Optional<llvm::StringMap<std::vector<Symbol *>>> demangledSyms;
+  std::optional<llvm::StringMap<SmallVector<Symbol *, 0>>> demangledSyms;
 };
 
-extern SymbolTable *symtab;
+LLVM_LIBRARY_VISIBILITY extern SymbolTable symtab;
 
-} // namespace elf
-} // namespace lld
+} // namespace lld::elf
 
 #endif
index d3e54f7..3873c7a 100644 (file)
@@ -91,42 +91,33 @@ TargetInfo *elf::getTarget() {
   llvm_unreachable("unknown target machine");
 }
 
-template <class ELFT> static ErrorPlace getErrPlace(const uint8_t *loc) {
+ErrorPlace elf::getErrorPlace(const uint8_t *loc) {
   assert(loc != nullptr);
-  for (InputSectionBase *d : inputSections) {
-    auto *isec = cast<InputSection>(d);
-    if (!isec->getParent() || (isec->type & SHT_NOBITS))
+  for (InputSectionBase *d : ctx.inputSections) {
+    auto *isec = dyn_cast<InputSection>(d);
+    if (!isec || !isec->getParent() || (isec->type & SHT_NOBITS))
       continue;
 
     const uint8_t *isecLoc =
         Out::bufferStart
             ? (Out::bufferStart + isec->getParent()->offset + isec->outSecOff)
-            : isec->data().data();
+            : isec->contentMaybeDecompress().data();
     if (isecLoc == nullptr) {
       assert(isa<SyntheticSection>(isec) && "No data but not synthetic?");
       continue;
     }
-    if (isecLoc <= loc && loc < isecLoc + isec->getSize())
-      return {isec, isec->template getLocation<ELFT>(loc - isecLoc) + ": "};
+    if (isecLoc <= loc && loc < isecLoc + isec->getSize()) {
+      std::string objLoc = isec->getLocation(loc - isecLoc);
+      // Return object file location and source file location.
+      // TODO: Refactor getSrcMsg not to take a variable.
+      Undefined dummy(nullptr, "", STB_LOCAL, 0, 0);
+      return {isec, objLoc + ": ",
+              isec->file ? isec->getSrcMsg(dummy, loc - isecLoc) : ""};
+    }
   }
   return {};
 }
 
-ErrorPlace elf::getErrorPlace(const uint8_t *loc) {
-  switch (config->ekind) {
-  case ELF32LEKind:
-    return getErrPlace<ELF32LE>(loc);
-  case ELF32BEKind:
-    return getErrPlace<ELF32BE>(loc);
-  case ELF64LEKind:
-    return getErrPlace<ELF64LE>(loc);
-  case ELF64BEKind:
-    return getErrPlace<ELF64BE>(loc);
-  default:
-    llvm_unreachable("unknown ELF type");
-  }
-}
-
 TargetInfo::~TargetInfo() {}
 
 int64_t TargetInfo::getImplicitAddend(const uint8_t *buf, RelType type) const {
@@ -161,33 +152,24 @@ RelExpr TargetInfo::adjustGotPcExpr(RelType type, int64_t addend,
   return R_GOT_PC;
 }
 
-void TargetInfo::relaxGot(uint8_t *loc, const Relocation &rel,
-                          uint64_t val) const {
-  llvm_unreachable("Should not have claimed to be relaxable");
-}
-
-void TargetInfo::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
-                                uint64_t val) const {
-  llvm_unreachable("Should not have claimed to be relaxable");
-}
-
-void TargetInfo::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
-                                uint64_t val) const {
-  llvm_unreachable("Should not have claimed to be relaxable");
-}
-
-void TargetInfo::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
-                                uint64_t val) const {
-  llvm_unreachable("Should not have claimed to be relaxable");
-}
-
-void TargetInfo::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
-                                uint64_t val) const {
-  llvm_unreachable("Should not have claimed to be relaxable");
+void TargetInfo::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
+  const unsigned bits = config->is64 ? 64 : 32;
+  uint64_t secAddr = sec.getOutputSection()->addr;
+  if (auto *s = dyn_cast<InputSection>(&sec))
+    secAddr += s->outSecOff;
+  for (const Relocation &rel : sec.relocs()) {
+    uint8_t *loc = buf + rel.offset;
+    const uint64_t val = SignExtend64(
+        sec.getRelocTargetVA(sec.file, rel.type, rel.addend,
+                             secAddr + rel.offset, *rel.sym, rel.expr),
+        bits);
+    if (rel.expr != R_RELAX_HINT)
+      relocate(loc, rel, val);
+  }
 }
 
 uint64_t TargetInfo::getImageBase() const {
-  // Use -image-base if set. Fall back to the target default if not.
+  // Use --image-base if set. Fall back to the target default if not.
   if (config->imageBase)
     return *config->imageBase;
   return config->isPic ? 0 : defaultImageBase;
index 1fe3217..e6a7816 100644 (file)
@@ -9,9 +9,11 @@
 #ifndef LLD_ELF_TARGET_H
 #define LLD_ELF_TARGET_H
 
+#include "Config.h"
 #include "InputSection.h"
 #include "lld/Common/ErrorHandler.h"
 #include "llvm/Object/ELF.h"
+#include "llvm/Support/Compiler.h"
 #include "llvm/Support/MathExtras.h"
 #include <array>
 
@@ -87,6 +89,10 @@ public:
   void relocateNoSym(uint8_t *loc, RelType type, uint64_t val) const {
     relocate(loc, Relocation{R_NONE, type, 0, 0, nullptr}, val);
   }
+  virtual void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const;
+
+  // Do a linker relaxation pass and return true if we changed something.
+  virtual bool relaxOnce(int pass) const { return false; }
 
   virtual void applyJumpInstrMod(uint8_t *loc, JumpModType type,
                                  JumpModType val) const {}
@@ -109,11 +115,11 @@ public:
   uint64_t getImageBase() const;
 
   // True if _GLOBAL_OFFSET_TABLE_ is relative to .got.plt, false if .got.
-  bool gotBaseSymInGotPlt = true;
+  bool gotBaseSymInGotPlt = false;
 
+  static constexpr RelType noneRel = 0;
   RelType copyRel;
   RelType gotRel;
-  RelType noneRel;
   RelType pltRel;
   RelType relativeRel;
   RelType iRelativeRel;
@@ -142,7 +148,7 @@ public:
 
   // Stores the NOP instructions of different sizes for the target and is used
   // to pad sections that are relaxed.
-  llvm::Optional<std::vector<std::vector<uint8_t>>> nopInstrs;
+  std::optional<std::vector<std::vector<uint8_t>>> nopInstrs;
 
   // If a target needs to rewrite calls to __morestack to instead call
   // __morestack_non_split when a split-stack enabled caller calls a
@@ -152,16 +158,6 @@ public:
   virtual RelExpr adjustTlsExpr(RelType type, RelExpr expr) const;
   virtual RelExpr adjustGotPcExpr(RelType type, int64_t addend,
                                   const uint8_t *loc) const;
-  virtual void relaxGot(uint8_t *loc, const Relocation &rel,
-                        uint64_t val) const;
-  virtual void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
-                              uint64_t val) const;
-  virtual void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
-                              uint64_t val) const;
-  virtual void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
-                              uint64_t val) const;
-  virtual void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
-                              uint64_t val) const;
 
 protected:
   // On FreeBSD x86_64 the first page cannot be mmaped.
@@ -188,6 +184,7 @@ template <class ELFT> TargetInfo *getMipsTargetInfo();
 struct ErrorPlace {
   InputSectionBase *isec;
   std::string loc;
+  std::string srcLoc;
 };
 
 // Returns input section and corresponding source string for the given location.
@@ -199,7 +196,6 @@ static inline std::string getErrorLocation(const uint8_t *loc) {
 
 void writePPC32GlinkSection(uint8_t *buf, size_t numEntries);
 
-bool tryRelaxPPC64TocIndirection(const Relocation &rel, uint8_t *bufLoc);
 unsigned getPPCDFormOp(unsigned secondaryOp);
 
 // In the PowerPC64 Elf V2 abi a function can have 2 entry points.  The first
@@ -211,10 +207,6 @@ unsigned getPPCDFormOp(unsigned secondaryOp);
 // to the local entry-point.
 unsigned getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther);
 
-// Returns true if a relocation is a small code model relocation that accesses
-// the .toc section.
-bool isPPC64SmallCodeModelTocReloc(RelType type);
-
 // Write a prefixed instruction, which is a 4-byte prefix followed by a 4-byte
 // instruction (regardless of endianness). Therefore, the prefix is always in
 // lower memory than the instruction.
@@ -223,8 +215,10 @@ void writePrefixedInstruction(uint8_t *loc, uint64_t insn);
 void addPPC64SaveRestore();
 uint64_t getPPC64TocBase();
 uint64_t getAArch64Page(uint64_t expr);
+void riscvFinalizeRelax(int passes);
+void mergeRISCVAttributesSections();
 
-extern const TargetInfo *target;
+LLVM_LIBRARY_VISIBILITY extern const TargetInfo *target;
 TargetInfo *getTarget();
 
 template <class ELFT> bool isMipsPIC(const Defined *sym);
@@ -291,4 +285,25 @@ inline void write64(void *p, uint64_t v) {
 } // namespace elf
 } // namespace lld
 
+#ifdef __clang__
+#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
+#endif
+#define invokeELFT(f, ...)                                                     \
+  switch (config->ekind) {                                                     \
+  case ELF32LEKind:                                                            \
+    f<ELF32LE>(__VA_ARGS__);                                                   \
+    break;                                                                     \
+  case ELF32BEKind:                                                            \
+    f<ELF32BE>(__VA_ARGS__);                                                   \
+    break;                                                                     \
+  case ELF64LEKind:                                                            \
+    f<ELF64LE>(__VA_ARGS__);                                                   \
+    break;                                                                     \
+  case ELF64BEKind:                                                            \
+    f<ELF64BE>(__VA_ARGS__);                                                   \
+    break;                                                                     \
+  default:                                                                     \
+    llvm_unreachable("unknown config->ekind");                                 \
+  }
+
 #endif
index dbc476f..5964196 100644 (file)
 
 #include "Thunks.h"
 #include "Config.h"
+#include "InputFiles.h"
 #include "InputSection.h"
 #include "OutputSections.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
-#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Memory.h"
+#include "lld/Common/CommonLinkerContext.h"
 #include "llvm/BinaryFormat/ELF.h"
 #include "llvm/Support/Casting.h"
-#include "llvm/Support/Endian.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/MathExtras.h"
 #include <cstdint>
@@ -162,37 +161,96 @@ public:
   void addSymbols(ThunkSection &isec) override;
 };
 
-// Implementations of Thunks for older Arm architectures that do not support
-// the movt/movw instructions. These thunks require at least Architecture v5
-// as used on processors such as the Arm926ej-s. There are no Thumb entry
-// points as there is no Thumb branch instruction on these architecture that
-// can result in a thunk
-class ARMV5ABSLongThunk final : public ARMThunk {
+// Implementations of Thunks for Arm v6-M. Only Thumb instructions are permitted
+class ThumbV6MABSLongThunk final : public ThumbThunk {
+public:
+  ThumbV6MABSLongThunk(Symbol &dest, int64_t addend)
+      : ThumbThunk(dest, addend) {}
+
+  uint32_t sizeLong() override { return 12; }
+  void writeLong(uint8_t *buf) override;
+  void addSymbols(ThunkSection &isec) override;
+};
+
+class ThumbV6MPILongThunk final : public ThumbThunk {
+public:
+  ThumbV6MPILongThunk(Symbol &dest, int64_t addend)
+      : ThumbThunk(dest, addend) {}
+
+  uint32_t sizeLong() override { return 16; }
+  void writeLong(uint8_t *buf) override;
+  void addSymbols(ThunkSection &isec) override;
+};
+
+// Architectures v4, v5 and v6 do not support the movt/movw instructions. v5 and
+// v6 support BLX to which BL instructions can be rewritten inline. There are no
+// Thumb entrypoints for v5 and v6 as there is no Thumb branch instruction on
+// these architecture that can result in a thunk.
+
+// LDR on v5 and v6 can switch processor state, so for v5 and v6,
+// ARMV5LongLdrPcThunk can be used for both Arm->Arm and Arm->Thumb calls. v4
+// can also use this thunk, but only for Arm->Arm calls.
+class ARMV5LongLdrPcThunk final : public ARMThunk {
 public:
-  ARMV5ABSLongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {}
+  ARMV5LongLdrPcThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {}
 
   uint32_t sizeLong() override { return 8; }
   void writeLong(uint8_t *buf) override;
   void addSymbols(ThunkSection &isec) override;
-  bool isCompatibleWith(const InputSection &isec,
-                        const Relocation &rel) const override;
 };
 
-class ARMV5PILongThunk final : public ARMThunk {
+// Implementations of Thunks for v4. BLX is not supported, and loads
+// will not invoke Arm/Thumb state changes.
+class ARMV4PILongBXThunk final : public ARMThunk {
 public:
-  ARMV5PILongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {}
+  ARMV4PILongBXThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {}
 
   uint32_t sizeLong() override { return 16; }
   void writeLong(uint8_t *buf) override;
   void addSymbols(ThunkSection &isec) override;
-  bool isCompatibleWith(const InputSection &isec,
-                        const Relocation &rel) const override;
 };
 
-// Implementations of Thunks for Arm v6-M. Only Thumb instructions are permitted
-class ThumbV6MABSLongThunk final : public ThumbThunk {
+class ARMV4PILongThunk final : public ARMThunk {
 public:
-  ThumbV6MABSLongThunk(Symbol &dest, int64_t addend)
+  ARMV4PILongThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {}
+
+  uint32_t sizeLong() override { return 12; }
+  void writeLong(uint8_t *buf) override;
+  void addSymbols(ThunkSection &isec) override;
+};
+
+class ThumbV4PILongBXThunk final : public ThumbThunk {
+public:
+  ThumbV4PILongBXThunk(Symbol &dest, int64_t addend)
+      : ThumbThunk(dest, addend) {}
+
+  uint32_t sizeLong() override { return 16; }
+  void writeLong(uint8_t *buf) override;
+  void addSymbols(ThunkSection &isec) override;
+};
+
+class ThumbV4PILongThunk final : public ThumbThunk {
+public:
+  ThumbV4PILongThunk(Symbol &dest, int64_t addend)
+      : ThumbThunk(dest, addend) {}
+
+  uint32_t sizeLong() override { return 20; }
+  void writeLong(uint8_t *buf) override;
+  void addSymbols(ThunkSection &isec) override;
+};
+
+class ARMV4ABSLongBXThunk final : public ARMThunk {
+public:
+  ARMV4ABSLongBXThunk(Symbol &dest, int64_t addend) : ARMThunk(dest, addend) {}
+
+  uint32_t sizeLong() override { return 12; }
+  void writeLong(uint8_t *buf) override;
+  void addSymbols(ThunkSection &isec) override;
+};
+
+class ThumbV4ABSLongBXThunk final : public ThumbThunk {
+public:
+  ThumbV4ABSLongBXThunk(Symbol &dest, int64_t addend)
       : ThumbThunk(dest, addend) {}
 
   uint32_t sizeLong() override { return 12; }
@@ -200,9 +258,9 @@ public:
   void addSymbols(ThunkSection &isec) override;
 };
 
-class ThumbV6MPILongThunk final : public ThumbThunk {
+class ThumbV4ABSLongThunk final : public ThumbThunk {
 public:
-  ThumbV6MPILongThunk(Symbol &dest, int64_t addend)
+  ThumbV4ABSLongThunk(Symbol &dest, int64_t addend)
       : ThumbThunk(dest, addend) {}
 
   uint32_t sizeLong() override { return 16; }
@@ -381,10 +439,10 @@ public:
   PPC64PILongBranchThunk(Symbol &dest, int64_t addend)
       : PPC64LongBranchThunk(dest, addend) {
     assert(!dest.isPreemptible);
-    if (Optional<uint32_t> index =
+    if (std::optional<uint32_t> index =
             in.ppc64LongBranchTarget->addEntry(&dest, addend)) {
       mainPart->relaDyn->addRelativeReloc(
-          target->relativeRel, in.ppc64LongBranchTarget, *index * UINT64_C(8),
+          target->relativeRel, *in.ppc64LongBranchTarget, *index * UINT64_C(8),
           dest, addend + getPPC64GlobalEntryToLocalEntryOffset(dest.stOther),
           target->symbolicRel, R_ABS);
     }
@@ -399,24 +457,6 @@ public:
   }
 };
 
-// A bl instruction uses a signed 24 bit offset, with an implicit 4 byte
-// alignment. This gives a possible 26 bits of 'reach'. If the caller and
-// callee do not use toc and the call offset is larger than 26 bits,
-// we need to emit a pc-rel based long-branch thunk. The target address of
-// the callee is computed with a PC-relative offset.
-class PPC64PCRelLongBranchThunk final : public Thunk {
-public:
-  PPC64PCRelLongBranchThunk(Symbol &dest, int64_t addend)
-      : Thunk(dest, addend) {
-    alignment = 16;
-  }
-  uint32_t size() override { return 32; }
-  void writeTo(uint8_t *buf) override;
-  void addSymbols(ThunkSection &isec) override;
-  bool isCompatibleWith(const InputSection &isec,
-                        const Relocation &rel) const override;
-};
-
 } // end anonymous namespace
 
 Defined *Thunk::addSymbol(StringRef name, uint8_t type, uint64_t value,
@@ -452,7 +492,7 @@ void AArch64ABSLongThunk::writeTo(uint8_t *buf) {
 }
 
 void AArch64ABSLongThunk::addSymbols(ThunkSection &isec) {
-  addSymbol(saver.save("__AArch64AbsLongThunk_" + destination.getName()),
+  addSymbol(saver().save("__AArch64AbsLongThunk_" + destination.getName()),
             STT_FUNC, 0, isec);
   addSymbol("$x", STT_NOTYPE, 0, isec);
   addSymbol("$d", STT_NOTYPE, 8, isec);
@@ -478,8 +518,8 @@ void AArch64ADRPThunk::writeTo(uint8_t *buf) {
 }
 
 void AArch64ADRPThunk::addSymbols(ThunkSection &isec) {
-  addSymbol(saver.save("__AArch64ADRPThunk_" + destination.getName()), STT_FUNC,
-            0, isec);
+  addSymbol(saver().save("__AArch64ADRPThunk_" + destination.getName()),
+            STT_FUNC, 0, isec);
   addSymbol("$x", STT_NOTYPE, 0, isec);
 }
 
@@ -523,14 +563,22 @@ void ARMThunk::writeTo(uint8_t *buf) {
 
 bool ARMThunk::isCompatibleWith(const InputSection &isec,
                                 const Relocation &rel) const {
+  // v4T does not have BLX, so also deny R_ARM_THM_CALL
+  if (!config->armHasBlx && rel.type == R_ARM_THM_CALL)
+    return false;
+
   // Thumb branch relocations can't use BLX
   return rel.type != R_ARM_THM_JUMP19 && rel.type != R_ARM_THM_JUMP24;
 }
 
-// This function returns true if the target is Thumb and is within 2^25, and
-// it has not previously returned false (see comment for mayUseShortThunk).
+// This function returns true if:
+// the target is Thumb
+// && is within branch range
+// && this function has not previously returned false
+//    (see comment for mayUseShortThunk)
+// && the arch supports Thumb branch range extension.
 bool ThumbThunk::getMayUseShortThunk() {
-  if (!mayUseShortThunk)
+  if (!mayUseShortThunk || !config->armJ1J2BranchEncoding)
     return false;
   uint64_t s = getARMThunkDestVA(destination);
   if ((s & 1) == 0) {
@@ -561,6 +609,10 @@ void ThumbThunk::writeTo(uint8_t *buf) {
 
 bool ThumbThunk::isCompatibleWith(const InputSection &isec,
                                   const Relocation &rel) const {
+  // v4T does not have BLX, so also deny R_ARM_CALL
+  if (!config->armHasBlx && rel.type == R_ARM_CALL)
+    return false;
+
   // ARM branch relocations can't use BLX
   return rel.type != R_ARM_JUMP24 && rel.type != R_ARM_PC24 && rel.type != R_ARM_PLT32;
 }
@@ -578,7 +630,7 @@ void ARMV7ABSLongThunk::writeLong(uint8_t *buf) {
 }
 
 void ARMV7ABSLongThunk::addSymbols(ThunkSection &isec) {
-  addSymbol(saver.save("__ARMv7ABSLongThunk_" + destination.getName()),
+  addSymbol(saver().save("__ARMv7ABSLongThunk_" + destination.getName()),
             STT_FUNC, 0, isec);
   addSymbol("$a", STT_NOTYPE, 0, isec);
 }
@@ -596,7 +648,7 @@ void ThumbV7ABSLongThunk::writeLong(uint8_t *buf) {
 }
 
 void ThumbV7ABSLongThunk::addSymbols(ThunkSection &isec) {
-  addSymbol(saver.save("__Thumbv7ABSLongThunk_" + destination.getName()),
+  addSymbol(saver().save("__Thumbv7ABSLongThunk_" + destination.getName()),
             STT_FUNC, 1, isec);
   addSymbol("$t", STT_NOTYPE, 0, isec);
 }
@@ -617,8 +669,8 @@ void ARMV7PILongThunk::writeLong(uint8_t *buf) {
 }
 
 void ARMV7PILongThunk::addSymbols(ThunkSection &isec) {
-  addSymbol(saver.save("__ARMV7PILongThunk_" + destination.getName()), STT_FUNC,
-            0, isec);
+  addSymbol(saver().save("__ARMV7PILongThunk_" + destination.getName()),
+            STT_FUNC, 0, isec);
   addSymbol("$a", STT_NOTYPE, 0, isec);
 }
 
@@ -638,34 +690,134 @@ void ThumbV7PILongThunk::writeLong(uint8_t *buf) {
 }
 
 void ThumbV7PILongThunk::addSymbols(ThunkSection &isec) {
-  addSymbol(saver.save("__ThumbV7PILongThunk_" + destination.getName()),
+  addSymbol(saver().save("__ThumbV7PILongThunk_" + destination.getName()),
+            STT_FUNC, 1, isec);
+  addSymbol("$t", STT_NOTYPE, 0, isec);
+}
+
+void ThumbV6MABSLongThunk::writeLong(uint8_t *buf) {
+  // Most Thumb instructions cannot access the high registers r8 - r15. As the
+  // only register we can corrupt is r12 we must instead spill a low register
+  // to the stack to use as a scratch register. We push r1 even though we
+  // don't need to get some space to use for the return address.
+  const uint8_t data[] = {
+      0x03, 0xb4,            // push {r0, r1} ; Obtain scratch registers
+      0x01, 0x48,            // ldr r0, [pc, #4] ; L1
+      0x01, 0x90,            // str r0, [sp, #4] ; SP + 4 = S
+      0x01, 0xbd,            // pop {r0, pc} ; restore r0 and branch to dest
+      0x00, 0x00, 0x00, 0x00 // L1: .word S
+  };
+  uint64_t s = getARMThunkDestVA(destination);
+  memcpy(buf, data, sizeof(data));
+  target->relocateNoSym(buf + 8, R_ARM_ABS32, s);
+}
+
+void ThumbV6MABSLongThunk::addSymbols(ThunkSection &isec) {
+  addSymbol(saver().save("__Thumbv6MABSLongThunk_" + destination.getName()),
+            STT_FUNC, 1, isec);
+  addSymbol("$t", STT_NOTYPE, 0, isec);
+  addSymbol("$d", STT_NOTYPE, 8, isec);
+}
+
+void ThumbV6MPILongThunk::writeLong(uint8_t *buf) {
+  // Most Thumb instructions cannot access the high registers r8 - r15. As the
+  // only register we can corrupt is ip (r12) we must instead spill a low
+  // register to the stack to use as a scratch register.
+  const uint8_t data[] = {
+      0x01, 0xb4,             // P:  push {r0}        ; Obtain scratch register
+      0x02, 0x48,             //     ldr r0, [pc, #8] ; L2
+      0x84, 0x46,             //     mov ip, r0       ; high to low register
+      0x01, 0xbc,             //     pop {r0}         ; restore scratch register
+      0xe7, 0x44,             // L1: add pc, ip       ; transfer control
+      0xc0, 0x46,             //     nop              ; pad to 4-byte boundary
+      0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 4)
+  };
+  uint64_t s = getARMThunkDestVA(destination);
+  uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
+  memcpy(buf, data, sizeof(data));
+  target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 12);
+}
+
+void ThumbV6MPILongThunk::addSymbols(ThunkSection &isec) {
+  addSymbol(saver().save("__Thumbv6MPILongThunk_" + destination.getName()),
             STT_FUNC, 1, isec);
   addSymbol("$t", STT_NOTYPE, 0, isec);
+  addSymbol("$d", STT_NOTYPE, 12, isec);
 }
 
-void ARMV5ABSLongThunk::writeLong(uint8_t *buf) {
+void ARMV5LongLdrPcThunk::writeLong(uint8_t *buf) {
   const uint8_t data[] = {
-      0x04, 0xf0, 0x1f, 0xe5, //     ldr pc, [pc,#-4] ; L1
+      0x04, 0xf0, 0x1f, 0xe5, // ldr pc, [pc,#-4] ; L1
       0x00, 0x00, 0x00, 0x00, // L1: .word S
   };
   memcpy(buf, data, sizeof(data));
   target->relocateNoSym(buf + 4, R_ARM_ABS32, getARMThunkDestVA(destination));
 }
 
-void ARMV5ABSLongThunk::addSymbols(ThunkSection &isec) {
-  addSymbol(saver.save("__ARMv5ABSLongThunk_" + destination.getName()),
+void ARMV5LongLdrPcThunk::addSymbols(ThunkSection &isec) {
+  addSymbol(saver().save("__ARMv5LongLdrPcThunk_" + destination.getName()),
             STT_FUNC, 0, isec);
   addSymbol("$a", STT_NOTYPE, 0, isec);
   addSymbol("$d", STT_NOTYPE, 4, isec);
 }
 
-bool ARMV5ABSLongThunk::isCompatibleWith(const InputSection &isec,
-                                         const Relocation &rel) const {
-  // Thumb branch relocations can't use BLX
-  return rel.type != R_ARM_THM_JUMP19 && rel.type != R_ARM_THM_JUMP24;
+void ARMV4ABSLongBXThunk::writeLong(uint8_t *buf) {
+  const uint8_t data[] = {
+      0x00, 0xc0, 0x9f, 0xe5, // ldr r12, [pc] ; L1
+      0x1c, 0xff, 0x2f, 0xe1, // bx r12
+      0x00, 0x00, 0x00, 0x00, // L1: .word S
+  };
+  memcpy(buf, data, sizeof(data));
+  target->relocateNoSym(buf + 8, R_ARM_ABS32, getARMThunkDestVA(destination));
+}
+
+void ARMV4ABSLongBXThunk::addSymbols(ThunkSection &isec) {
+  addSymbol(saver().save("__ARMv4ABSLongBXThunk_" + destination.getName()),
+            STT_FUNC, 0, isec);
+  addSymbol("$a", STT_NOTYPE, 0, isec);
+  addSymbol("$d", STT_NOTYPE, 8, isec);
+}
+
+void ThumbV4ABSLongBXThunk::writeLong(uint8_t *buf) {
+  const uint8_t data[] = {
+      0x78, 0x47,             // bx pc
+      0xfd, 0xe7,             // b #-6 ; Arm recommended sequence to follow bx pc
+      0x04, 0xf0, 0x1f, 0xe5, // ldr pc, [pc, #-4] ; L1
+      0x00, 0x00, 0x00, 0x00, // L1: .word S
+  };
+  memcpy(buf, data, sizeof(data));
+  target->relocateNoSym(buf + 8, R_ARM_ABS32, getARMThunkDestVA(destination));
+}
+
+void ThumbV4ABSLongBXThunk::addSymbols(ThunkSection &isec) {
+  addSymbol(saver().save("__Thumbv4ABSLongBXThunk_" + destination.getName()),
+            STT_FUNC, 1, isec);
+  addSymbol("$t", STT_NOTYPE, 0, isec);
+  addSymbol("$a", STT_NOTYPE, 4, isec);
+  addSymbol("$d", STT_NOTYPE, 8, isec);
+}
+
+void ThumbV4ABSLongThunk::writeLong(uint8_t *buf) {
+  const uint8_t data[] = {
+      0x78, 0x47,             // bx pc
+      0xfd, 0xe7,             // b #-6 ; Arm recommended sequence to follow bx pc
+      0x00, 0xc0, 0x9f, 0xe5, // ldr r12, [pc] ; L1
+      0x1c, 0xff, 0x2f, 0xe1, // bx r12
+      0x00, 0x00, 0x00, 0x00, // L1: .word S
+  };
+  memcpy(buf, data, sizeof(data));
+  target->relocateNoSym(buf + 12, R_ARM_ABS32, getARMThunkDestVA(destination));
+}
+
+void ThumbV4ABSLongThunk::addSymbols(ThunkSection &isec) {
+  addSymbol(saver().save("__Thumbv4ABSLongThunk_" + destination.getName()),
+            STT_FUNC, 1, isec);
+  addSymbol("$t", STT_NOTYPE, 0, isec);
+  addSymbol("$a", STT_NOTYPE, 4, isec);
+  addSymbol("$d", STT_NOTYPE, 12, isec);
 }
 
-void ARMV5PILongThunk::writeLong(uint8_t *buf) {
+void ARMV4PILongBXThunk::writeLong(uint8_t *buf) {
   const uint8_t data[] = {
       0x04, 0xc0, 0x9f, 0xe5, // P:  ldr ip, [pc,#4] ; L2
       0x0c, 0xc0, 0x8f, 0xe0, // L1: add ip, pc, ip
@@ -678,67 +830,75 @@ void ARMV5PILongThunk::writeLong(uint8_t *buf) {
   target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 12);
 }
 
-void ARMV5PILongThunk::addSymbols(ThunkSection &isec) {
-  addSymbol(saver.save("__ARMV5PILongThunk_" + destination.getName()), STT_FUNC,
-            0, isec);
+void ARMV4PILongBXThunk::addSymbols(ThunkSection &isec) {
+  addSymbol(saver().save("__ARMv4PILongBXThunk_" + destination.getName()),
+            STT_FUNC, 0, isec);
   addSymbol("$a", STT_NOTYPE, 0, isec);
   addSymbol("$d", STT_NOTYPE, 12, isec);
 }
 
-bool ARMV5PILongThunk::isCompatibleWith(const InputSection &isec,
-                                        const Relocation &rel) const {
-  // Thumb branch relocations can't use BLX
-  return rel.type != R_ARM_THM_JUMP19 && rel.type != R_ARM_THM_JUMP24;
+void ARMV4PILongThunk::writeLong(uint8_t *buf) {
+  const uint8_t data[] = {
+      0x00, 0xc0, 0x9f, 0xe5, // P:  ldr ip, [pc] ; L2
+      0x0c, 0xf0, 0x8f, 0xe0, // L1: add pc, pc, r12
+      0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 8)
+  };
+  uint64_t s = getARMThunkDestVA(destination);
+  uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
+  memcpy(buf, data, sizeof(data));
+  target->relocateNoSym(buf + 8, R_ARM_REL32, s - p - 12);
 }
 
-void ThumbV6MABSLongThunk::writeLong(uint8_t *buf) {
-  // Most Thumb instructions cannot access the high registers r8 - r15. As the
-  // only register we can corrupt is r12 we must instead spill a low register
-  // to the stack to use as a scratch register. We push r1 even though we
-  // don't need to get some space to use for the return address.
+void ARMV4PILongThunk::addSymbols(ThunkSection &isec) {
+  addSymbol(saver().save("__ARMv4PILongThunk_" + destination.getName()),
+            STT_FUNC, 0, isec);
+  addSymbol("$a", STT_NOTYPE, 0, isec);
+  addSymbol("$d", STT_NOTYPE, 8, isec);
+}
+
+void ThumbV4PILongBXThunk::writeLong(uint8_t *buf) {
   const uint8_t data[] = {
-      0x03, 0xb4,            // push {r0, r1} ; Obtain scratch registers
-      0x01, 0x48,            // ldr r0, [pc, #4] ; L1
-      0x01, 0x90,            // str r0, [sp, #4] ; SP + 4 = S
-      0x01, 0xbd,            // pop {r0, pc} ; restore r0 and branch to dest
-      0x00, 0x00, 0x00, 0x00 // L1: .word S
+      0x78, 0x47,             // P:  bx pc
+      0xfd, 0xe7,             //     b #-6 ; Arm recommended sequence to follow bx pc
+      0x00, 0xc0, 0x9f, 0xe5, //     ldr r12, [pc] ; L2
+      0x0f, 0xf0, 0x8c, 0xe0, // L1: add pc, r12, pc
+      0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 8)
   };
   uint64_t s = getARMThunkDestVA(destination);
+  uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
   memcpy(buf, data, sizeof(data));
-  target->relocateNoSym(buf + 8, R_ARM_ABS32, s);
+  target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 16);
 }
 
-void ThumbV6MABSLongThunk::addSymbols(ThunkSection &isec) {
-  addSymbol(saver.save("__Thumbv6MABSLongThunk_" + destination.getName()),
+void ThumbV4PILongBXThunk::addSymbols(ThunkSection &isec) {
+  addSymbol(saver().save("__Thumbv4PILongBXThunk_" + destination.getName()),
             STT_FUNC, 1, isec);
   addSymbol("$t", STT_NOTYPE, 0, isec);
-  addSymbol("$d", STT_NOTYPE, 8, isec);
+  addSymbol("$a", STT_NOTYPE, 4, isec);
+  addSymbol("$d", STT_NOTYPE, 12, isec);
 }
 
-void ThumbV6MPILongThunk::writeLong(uint8_t *buf) {
-  // Most Thumb instructions cannot access the high registers r8 - r15. As the
-  // only register we can corrupt is ip (r12) we must instead spill a low
-  // register to the stack to use as a scratch register.
+void ThumbV4PILongThunk::writeLong(uint8_t *buf) {
   const uint8_t data[] = {
-      0x01, 0xb4,             // P:  push {r0}        ; Obtain scratch register
-      0x02, 0x48,             //     ldr r0, [pc, #8] ; L2
-      0x84, 0x46,             //     mov ip, r0       ; high to low register
-      0x01, 0xbc,             //     pop {r0}         ; restore scratch register
-      0xe7, 0x44,             // L1: add pc, ip       ; transfer control
-      0xc0, 0x46,             //     nop              ; pad to 4-byte boundary
-      0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 4)
+      0x78, 0x47,             // P:  bx pc
+      0xfd, 0xe7,             //     b #-6 ; Arm recommended sequence to follow bx pc
+      0x04, 0xc0, 0x9f, 0xe5, //     ldr ip, [pc,#4] ; L2
+      0x0c, 0xc0, 0x8f, 0xe0, // L1: add ip, pc, ip
+      0x1c, 0xff, 0x2f, 0xe1, //     bx ip
+      0x00, 0x00, 0x00, 0x00, // L2: .word S - (P + (L1 - P) + 8)
   };
   uint64_t s = getARMThunkDestVA(destination);
   uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
   memcpy(buf, data, sizeof(data));
-  target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 12);
+  target->relocateNoSym(buf + 16, R_ARM_REL32, s - p - 16);
 }
 
-void ThumbV6MPILongThunk::addSymbols(ThunkSection &isec) {
-  addSymbol(saver.save("__Thumbv6MPILongThunk_" + destination.getName()),
+void ThumbV4PILongThunk::addSymbols(ThunkSection &isec) {
+  addSymbol(saver().save("__Thumbv4PILongThunk_" + destination.getName()),
             STT_FUNC, 1, isec);
   addSymbol("$t", STT_NOTYPE, 0, isec);
-  addSymbol("$d", STT_NOTYPE, 12, isec);
+  addSymbol("$a", STT_NOTYPE, 4, isec);
+  addSymbol("$d", STT_NOTYPE, 16, isec);
 }
 
 // Write MIPS LA25 thunk code to call PIC function from the non-PIC one.
@@ -753,7 +913,7 @@ void MipsThunk::writeTo(uint8_t *buf) {
 }
 
 void MipsThunk::addSymbols(ThunkSection &isec) {
-  addSymbol(saver.save("__LA25Thunk_" + destination.getName()), STT_FUNC, 0,
+  addSymbol(saver().save("__LA25Thunk_" + destination.getName()), STT_FUNC, 0,
             isec);
 }
 
@@ -776,8 +936,9 @@ void MicroMipsThunk::writeTo(uint8_t *buf) {
 }
 
 void MicroMipsThunk::addSymbols(ThunkSection &isec) {
-  Defined *d = addSymbol(
-      saver.save("__microLA25Thunk_" + destination.getName()), STT_FUNC, 0, isec);
+  Defined *d =
+      addSymbol(saver().save("__microLA25Thunk_" + destination.getName()),
+                STT_FUNC, 0, isec);
   d->stOther |= STO_MIPS_MICROMIPS;
 }
 
@@ -800,8 +961,9 @@ void MicroMipsR6Thunk::writeTo(uint8_t *buf) {
 }
 
 void MicroMipsR6Thunk::addSymbols(ThunkSection &isec) {
-  Defined *d = addSymbol(
-      saver.save("__microLA25Thunk_" + destination.getName()), STT_FUNC, 0, isec);
+  Defined *d =
+      addSymbol(saver().save("__microLA25Thunk_" + destination.getName()),
+                STT_FUNC, 0, isec);
   d->stOther |= STO_MIPS_MICROMIPS;
 }
 
@@ -824,8 +986,9 @@ void elf::writePPC32PltCallStub(uint8_t *buf, uint64_t gotPltVA,
     // The stub loads an address relative to r30 (.got2+Addend). Addend is
     // almost always 0x8000. The address of .got2 is different in another object
     // file, so a stub cannot be shared.
-    offset = gotPltVA - (in.ppc32Got2->getParent()->getVA() +
-                         file->ppc32Got2OutSecOff + addend);
+    offset = gotPltVA -
+             (in.ppc32Got2->getParent()->getVA() +
+              (file->ppc32Got2 ? file->ppc32Got2->outSecOff : 0) + addend);
   } else {
     // The stub loads an address relative to _GLOBAL_OFFSET_TABLE_ (which is
     // currently the address of .got).
@@ -860,7 +1023,7 @@ void PPC32PltCallStub::addSymbols(ThunkSection &isec) {
   else
     os << ".plt_pic32.";
   os << destination.getName();
-  addSymbol(saver.save(os.str()), STT_FUNC, 0, isec);
+  addSymbol(saver().save(os.str()), STT_FUNC, 0, isec);
 }
 
 bool PPC32PltCallStub::isCompatibleWith(const InputSection &isec,
@@ -869,7 +1032,7 @@ bool PPC32PltCallStub::isCompatibleWith(const InputSection &isec,
 }
 
 void PPC32LongThunk::addSymbols(ThunkSection &isec) {
-  addSymbol(saver.save("__LongThunk_" + destination.getName()), STT_FUNC, 0,
+  addSymbol(saver().save("__LongThunk_" + destination.getName()), STT_FUNC, 0,
             isec);
 }
 
@@ -913,8 +1076,8 @@ void PPC64PltCallStub::writeTo(uint8_t *buf) {
 }
 
 void PPC64PltCallStub::addSymbols(ThunkSection &isec) {
-  Defined *s = addSymbol(saver.save("__plt_" + destination.getName()), STT_FUNC,
-                         0, isec);
+  Defined *s = addSymbol(saver().save("__plt_" + destination.getName()),
+                         STT_FUNC, 0, isec);
   s->needsTocRestore = true;
   s->file = destination.file;
 }
@@ -932,25 +1095,18 @@ void PPC64R2SaveStub::writeTo(uint8_t *buf) {
     write32(buf + 4, 0x48000000 | (offset & 0x03fffffc)); // b    <offset>
   } else if (isInt<34>(offset)) {
     int nextInstOffset;
-    if (!config->Power10Stub) {
-      uint64_t tocOffset = destination.getVA() - getPPC64TocBase();
-      if (tocOffset >> 16 > 0) {
-        const uint64_t addi = ADDI_R12_TO_R12_NO_DISP | (tocOffset & 0xffff);
-        const uint64_t addis = ADDIS_R12_TO_R2_NO_DISP | ((tocOffset >> 16) & 0xffff);
-        write32(buf + 4, addis); // addis r12, r2 , top of offset
-        write32(buf + 8, addi);  // addi  r12, r12, bottom of offset
-        nextInstOffset = 12;
-      } else {
-        const uint64_t addi = ADDI_R12_TO_R2_NO_DISP | (tocOffset & 0xffff);
-        write32(buf + 4, addi); // addi r12, r2, offset
-        nextInstOffset = 8;
-      }
-    } else {
-      const uint64_t paddi = PADDI_R12_NO_DISP |
-                             (((offset >> 16) & 0x3ffff) << 32) |
-                             (offset & 0xffff);
-      writePrefixedInstruction(buf + 4, paddi); // paddi r12, 0, func@pcrel, 1
+    uint64_t tocOffset = destination.getVA() - getPPC64TocBase();
+    if (tocOffset >> 16 > 0) {
+      const uint64_t addi = ADDI_R12_TO_R12_NO_DISP | (tocOffset & 0xffff);
+      const uint64_t addis =
+          ADDIS_R12_TO_R2_NO_DISP | ((tocOffset >> 16) & 0xffff);
+      write32(buf + 4, addis); // addis r12, r2 , top of offset
+      write32(buf + 8, addi);  // addi  r12, r12, bottom of offset
       nextInstOffset = 12;
+    } else {
+      const uint64_t addi = ADDI_R12_TO_R2_NO_DISP | (tocOffset & 0xffff);
+      write32(buf + 4, addi); // addi r12, r2, offset
+      nextInstOffset = 8;
     }
     write32(buf + nextInstOffset, MTCTR_R12); // mtctr r12
     write32(buf + nextInstOffset + 4, BCTR);  // bctr
@@ -964,7 +1120,7 @@ void PPC64R2SaveStub::writeTo(uint8_t *buf) {
 }
 
 void PPC64R2SaveStub::addSymbols(ThunkSection &isec) {
-  Defined *s = addSymbol(saver.save("__toc_save_" + destination.getName()),
+  Defined *s = addSymbol(saver().save("__toc_save_" + destination.getName()),
                          STT_FUNC, 0, isec);
   s->needsTocRestore = true;
 }
@@ -980,7 +1136,7 @@ void PPC64R12SetupStub::writeTo(uint8_t *buf) {
     reportRangeError(buf, offset, 34, destination, "R12 setup stub offset");
 
   int nextInstOffset;
-  if (!config->Power10Stub) {
+  if (!config->power10Stubs) {
     uint32_t off = destination.getVA(addend) - getThunkTargetSym()->getVA() - 8;
     write32(buf + 0, 0x7c0802a6);                      // mflr r12
     write32(buf + 4, 0x429f0005);                      // bcl 20,31,.+4
@@ -1000,7 +1156,7 @@ void PPC64R12SetupStub::writeTo(uint8_t *buf) {
 }
 
 void PPC64R12SetupStub::addSymbols(ThunkSection &isec) {
-  addSymbol(saver.save("__gep_setup_" + destination.getName()), STT_FUNC, 0,
+  addSymbol(saver().save("__gep_setup_" + destination.getName()), STT_FUNC, 0,
             isec);
 }
 
@@ -1013,7 +1169,7 @@ void PPC64PCRelPLTStub::writeTo(uint8_t *buf) {
   int nextInstOffset = 0;
   int64_t offset = destination.getGotPltVA() - getThunkTargetSym()->getVA();
 
-  if (config->Power10Stub) {
+  if (config->power10Stubs) {
     if (!isInt<34>(offset))
       reportRangeError(buf, offset, 34, destination,
                        "PC-relative PLT stub offset");
@@ -1036,7 +1192,7 @@ void PPC64PCRelPLTStub::writeTo(uint8_t *buf) {
 }
 
 void PPC64PCRelPLTStub::addSymbols(ThunkSection &isec) {
-  addSymbol(saver.save("__plt_pcrel_" + destination.getName()), STT_FUNC, 0,
+  addSymbol(saver().save("__plt_pcrel_" + destination.getName()), STT_FUNC, 0,
             isec);
 }
 
@@ -1052,7 +1208,7 @@ void PPC64LongBranchThunk::writeTo(uint8_t *buf) {
 }
 
 void PPC64LongBranchThunk::addSymbols(ThunkSection &isec) {
-  addSymbol(saver.save("__long_branch_" + destination.getName()), STT_FUNC, 0,
+  addSymbol(saver().save("__long_branch_" + destination.getName()), STT_FUNC, 0,
             isec);
 }
 
@@ -1061,42 +1217,6 @@ bool PPC64LongBranchThunk::isCompatibleWith(const InputSection &isec,
   return rel.type == R_PPC64_REL24 || rel.type == R_PPC64_REL14;
 }
 
-void PPC64PCRelLongBranchThunk::writeTo(uint8_t *buf) {
-  int64_t offset = destination.getVA() - getThunkTargetSym()->getVA();
-  if (!isInt<34>(offset))
-    reportRangeError(buf, offset, 34, destination,
-                     "PC-relative long branch stub offset");
-
-  int nextInstOffset;
-  if (!config->Power10Stub) {
-    uint32_t off = destination.getVA(addend) - getThunkTargetSym()->getVA() - 8;
-    write32(buf + 0, 0x7c0802a6);                      // mflr r12
-    write32(buf + 4, 0x429f0005);                      // bcl 20,31,.+4
-    write32(buf + 8, 0x7d6802a6);                      // mflr r11
-    write32(buf + 12, 0x7d8803a6);                     // mtlr r12
-    write32(buf + 16, 0x3d8b0000 | computeHiBits(off)); // addis r12,r11,off@ha
-    write32(buf + 20, 0x398c0000 | (off & 0xffff));    // addi r12,r12,off@l
-    nextInstOffset = 24;
-  } else {
-    uint64_t paddi = PADDI_R12_NO_DISP | (((offset >> 16) & 0x3ffff) << 32) |
-                     (offset & 0xffff);
-    writePrefixedInstruction(buf + 0, paddi); // paddi r12, 0, func@pcrel, 1
-    nextInstOffset = 8;
-  }
-  write32(buf + nextInstOffset, MTCTR_R12); // mtctr r12
-  write32(buf + nextInstOffset + 4, BCTR);  // bctr
-}
-
-void PPC64PCRelLongBranchThunk::addSymbols(ThunkSection &isec) {
-  addSymbol(saver.save("__long_branch_pcrel_" + destination.getName()),
-            STT_FUNC, 0, isec);
-}
-
-bool PPC64PCRelLongBranchThunk::isCompatibleWith(const InputSection &isec,
-                                                 const Relocation &rel) const {
-  return rel.type == R_PPC64_REL24_NOTOC;
-}
-
 Thunk::Thunk(Symbol &d, int64_t a) : destination(d), addend(a), offset(0) {}
 
 Thunk::~Thunk() = default;
@@ -1110,12 +1230,50 @@ static Thunk *addThunkAArch64(RelType type, Symbol &s, int64_t a) {
   return make<AArch64ABSLongThunk>(s, a);
 }
 
-// Creates a thunk for Thumb-ARM interworking.
-// Arm Architectures v5 and v6 do not support Thumb2 technology. This means
+// Creates a thunk for long branches or Thumb-ARM interworking.
+// Arm Architectures v4t does not support Thumb2 technology, and does not
+// support BLX or LDR Arm/Thumb state switching. This means that
+// - MOVT and MOVW instructions cannot be used.
+// - We can't rewrite BL in place to BLX. We will need thunks.
+//
+// TODO: use B for short Thumb->Arm thunks instead of LDR (this doesn't work for
+//       Arm->Thumb, as in Arm state no BX PC trick; it doesn't switch state).
+static Thunk *addThunkArmv4(RelType reloc, Symbol &s, int64_t a) {
+  bool thumb_target = s.getVA(a) & 1;
+
+  switch (reloc) {
+  case R_ARM_PC24:
+  case R_ARM_PLT32:
+  case R_ARM_JUMP24:
+  case R_ARM_CALL:
+    if (config->picThunk) {
+      if (thumb_target)
+        return make<ARMV4PILongBXThunk>(s, a);
+      return make<ARMV4PILongThunk>(s, a);
+    }
+    if (thumb_target)
+      return make<ARMV4ABSLongBXThunk>(s, a);
+    return make<ARMV5LongLdrPcThunk>(s, a);
+  case R_ARM_THM_CALL:
+    if (config->picThunk) {
+      if (thumb_target)
+        return make<ThumbV4PILongThunk>(s, a);
+      return make<ThumbV4PILongBXThunk>(s, a);
+    }
+    if (thumb_target)
+      return make<ThumbV4ABSLongThunk>(s, a);
+    return make<ThumbV4ABSLongBXThunk>(s, a);
+  }
+  fatal("relocation " + toString(reloc) + " to " + toString(s) +
+        " not supported for Armv4 or Armv4T target");
+}
+
+// Creates a thunk for Thumb-ARM interworking compatible with Armv5 and Armv6.
+// Arm Architectures v5 and v6 do not support Thumb2 technology. This means that
 // - MOVT and MOVW instructions cannot be used
 // - Only Thumb relocation that can generate a Thunk is a BL, this can always
 //   be transformed into a BLX
-static Thunk *addThunkPreArmv7(RelType reloc, Symbol &s, int64_t a) {
+static Thunk *addThunkArmv5v6(RelType reloc, Symbol &s, int64_t a) {
   switch (reloc) {
   case R_ARM_PC24:
   case R_ARM_PLT32:
@@ -1123,8 +1281,8 @@ static Thunk *addThunkPreArmv7(RelType reloc, Symbol &s, int64_t a) {
   case R_ARM_CALL:
   case R_ARM_THM_CALL:
     if (config->picThunk)
-      return make<ARMV5PILongThunk>(s, a);
-    return make<ARMV5ABSLongThunk>(s, a);
+      return make<ARMV4PILongBXThunk>(s, a);
+    return make<ARMV5LongLdrPcThunk>(s, a);
   }
   fatal("relocation " + toString(reloc) + " to " + toString(s) +
         " not supported for Armv5 or Armv6 targets");
@@ -1167,9 +1325,11 @@ static Thunk *addThunkArm(RelType reloc, Symbol &s, int64_t a) {
   // of the input objects. InputFiles.cpp contains the mapping from ARM
   // architecture to flag.
   if (!config->armHasMovtMovw) {
-    if (!config->armJ1J2BranchEncoding)
-      return addThunkPreArmv7(reloc, s, a);
-    return addThunkV6M(reloc, s, a);
+    if (config->armJ1J2BranchEncoding)
+      return addThunkV6M(reloc, s, a);
+    if (config->armHasBlx)
+      return addThunkArmv5v6(reloc, s, a);
+    return addThunkArmv4(reloc, s, a);
   }
 
   switch (reloc) {
@@ -1223,9 +1383,7 @@ static Thunk *addThunkPPC64(RelType type, Symbol &s, int64_t a) {
     return make<PPC64R2SaveStub>(s, a);
 
   if (type == R_PPC64_REL24_NOTOC)
-    return (s.stOther >> 5) > 1
-               ? (Thunk *)make<PPC64R12SetupStub>(s)
-               : (Thunk *)make<PPC64PCRelLongBranchThunk>(s, a);
+    return make<PPC64R12SetupStub>(s);
 
   if (config->picThunk)
     return make<PPC64PILongBranchThunk>(s, a);
index 5558da1..86aaee1 100644 (file)
@@ -12,8 +12,7 @@
 #include "llvm/ADT/SmallVector.h"
 #include "Relocations.h"
 
-namespace lld {
-namespace elf {
+namespace lld::elf {
 class Defined;
 class InputFile;
 class Symbol;
@@ -78,7 +77,6 @@ static inline uint16_t computeHiBits(uint32_t toCompute) {
   return (toCompute + 0x8000) >> 16;
 }
 
-} // namespace elf
-} // namespace lld
+} // namespace lld::elf
 
 #endif
index 42c7b89..c20d344 100644 (file)
@@ -32,38 +32,38 @@ struct ARM : TargetInfo {
   void relocateOne(uint8_t *loc, const Reloc &, uint64_t va,
                    uint64_t pc) const override;
 
-  void writeStub(uint8_t *buf, const Symbol &) const override;
+  void writeStub(uint8_t *buf, const Symbol &, uint64_t) const override;
   void writeStubHelperHeader(uint8_t *buf) const override;
-  void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &,
+  void writeStubHelperEntry(uint8_t *buf, const Symbol &,
                             uint64_t entryAddr) const override;
 
+  void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
+                            uint64_t stubOffset, uint64_t selrefsVA,
+                            uint64_t selectorIndex, uint64_t gotAddr,
+                            uint64_t msgSendIndex) const override;
+
   void relaxGotLoad(uint8_t *loc, uint8_t type) const override;
-  const RelocAttrs &getRelocAttrs(uint8_t type) const override;
   uint64_t getPageSize() const override { return 4 * 1024; }
-};
 
+  void handleDtraceReloc(const Symbol *sym, const Reloc &r,
+                         uint8_t *loc) const override;
+};
 } // namespace
 
-const RelocAttrs &ARM::getRelocAttrs(uint8_t type) const {
-  static const std::array<RelocAttrs, 10> relocAttrsArray{{
+static constexpr std::array<RelocAttrs, 10> relocAttrsArray{{
 #define B(x) RelocAttrBits::x
-      {"VANILLA", /* FIXME populate this */ B(_0)},
-      {"PAIR", /* FIXME populate this */ B(_0)},
-      {"SECTDIFF", /* FIXME populate this */ B(_0)},
-      {"LOCAL_SECTDIFF", /* FIXME populate this */ B(_0)},
-      {"PB_LA_PTR", /* FIXME populate this */ B(_0)},
-      {"BR24", B(PCREL) | B(LOCAL) | B(EXTERN) | B(BRANCH) | B(BYTE4)},
-      {"BR22", B(PCREL) | B(LOCAL) | B(EXTERN) | B(BRANCH) | B(BYTE4)},
-      {"32BIT_BRANCH", /* FIXME populate this */ B(_0)},
-      {"HALF", /* FIXME populate this */ B(_0)},
-      {"HALF_SECTDIFF", /* FIXME populate this */ B(_0)},
+    {"VANILLA", /* FIXME populate this */ B(_0)},
+    {"PAIR", /* FIXME populate this */ B(_0)},
+    {"SECTDIFF", /* FIXME populate this */ B(_0)},
+    {"LOCAL_SECTDIFF", /* FIXME populate this */ B(_0)},
+    {"PB_LA_PTR", /* FIXME populate this */ B(_0)},
+    {"BR24", B(PCREL) | B(LOCAL) | B(EXTERN) | B(BRANCH) | B(BYTE4)},
+    {"BR22", B(PCREL) | B(LOCAL) | B(EXTERN) | B(BRANCH) | B(BYTE4)},
+    {"32BIT_BRANCH", /* FIXME populate this */ B(_0)},
+    {"HALF", /* FIXME populate this */ B(_0)},
+    {"HALF_SECTDIFF", /* FIXME populate this */ B(_0)},
 #undef B
-  }};
-  assert(type < relocAttrsArray.size() && "invalid relocation type");
-  if (type >= relocAttrsArray.size())
-    return invalidRelocAttrs;
-  return relocAttrsArray[type];
-}
+}};
 
 int64_t ARM::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,
                                relocation_info rel) const {
@@ -116,7 +116,7 @@ void ARM::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
         return;
       } else if (isBlx && !defined->thumb) {
         Bitfield::set<Cond>(base, 0xe); // unconditional BL
-        Bitfield::set<BitfieldFlag<24>>(base, 1);
+        Bitfield::set<BitfieldFlag<24>>(base, true);
         isBlx = false;
       }
     } else {
@@ -140,7 +140,7 @@ void ARM::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
   }
 }
 
-void ARM::writeStub(uint8_t *buf, const Symbol &sym) const {
+void ARM::writeStub(uint8_t *buf, const Symbol &sym, uint64_t) const {
   fatal("TODO: implement this");
 }
 
@@ -148,11 +148,18 @@ void ARM::writeStubHelperHeader(uint8_t *buf) const {
   fatal("TODO: implement this");
 }
 
-void ARM::writeStubHelperEntry(uint8_t *buf, const DylibSymbol &sym,
+void ARM::writeStubHelperEntry(uint8_t *buf, const Symbol &sym,
                                uint64_t entryAddr) const {
   fatal("TODO: implement this");
 }
 
+void ARM::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
+                               uint64_t stubOffset, uint64_t selrefsVA,
+                               uint64_t selectorIndex, uint64_t gotAddr,
+                               uint64_t msgSendIndex) const {
+  fatal("TODO: implement this");
+}
+
 void ARM::relaxGotLoad(uint8_t *loc, uint8_t type) const {
   fatal("TODO: implement this");
 }
@@ -164,9 +171,44 @@ ARM::ARM(uint32_t cpuSubtype) : TargetInfo(ILP32()) {
   stubSize = 0 /* FIXME */;
   stubHelperHeaderSize = 0 /* FIXME */;
   stubHelperEntrySize = 0 /* FIXME */;
+
+  relocAttrs = {relocAttrsArray.data(), relocAttrsArray.size()};
 }
 
 TargetInfo *macho::createARMTargetInfo(uint32_t cpuSubtype) {
   static ARM t(cpuSubtype);
   return &t;
 }
+
+void ARM::handleDtraceReloc(const Symbol *sym, const Reloc &r,
+                            uint8_t *loc) const {
+  if (config->outputType == MH_OBJECT)
+    return;
+
+  switch (r.type) {
+  case ARM_RELOC_BR24:
+    if (sym->getName().startswith("___dtrace_probe")) {
+      // change call site to a NOP
+      write32le(loc, 0xE1A00000);
+    } else if (sym->getName().startswith("___dtrace_isenabled")) {
+      // change call site to 'eor r0, r0, r0'
+      write32le(loc, 0xE0200000);
+    } else {
+      error("Unrecognized dtrace symbol prefix: " + toString(*sym));
+    }
+    break;
+  case ARM_THUMB_RELOC_BR22:
+    if (sym->getName().startswith("___dtrace_probe")) {
+      // change 32-bit blx call site to two thumb NOPs
+      write32le(loc, 0x46C046C0);
+    } else if (sym->getName().startswith("___dtrace_isenabled")) {
+      // change 32-bit blx call site to 'nop', 'eor r0, r0'
+      write32le(loc, 0x46C04040);
+    } else {
+      error("Unrecognized dtrace symbol prefix: " + toString(*sym));
+    }
+    break;
+  default:
+    llvm_unreachable("Unsupported dtrace relocation type for ARM");
+  }
+}
index 36c3cc6..6627d41 100644 (file)
 #include "Target.h"
 
 #include "lld/Common/ErrorHandler.h"
+#include "mach-o/compact_unwind_encoding.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/BinaryFormat/MachO.h"
 #include "llvm/Support/Endian.h"
+#include "llvm/Support/LEB128.h"
 #include "llvm/Support/MathExtras.h"
 
 using namespace llvm;
@@ -29,12 +31,17 @@ namespace {
 
 struct ARM64 : ARM64Common {
   ARM64();
-  void writeStub(uint8_t *buf, const Symbol &) const override;
+  void writeStub(uint8_t *buf, const Symbol &, uint64_t) const override;
   void writeStubHelperHeader(uint8_t *buf) const override;
-  void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &,
+  void writeStubHelperEntry(uint8_t *buf, const Symbol &,
                             uint64_t entryAddr) const override;
-  const RelocAttrs &getRelocAttrs(uint8_t type) const override;
+
+  void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
+                            uint64_t stubOffset, uint64_t selrefsVA,
+                            uint64_t selectorIndex, uint64_t gotAddr,
+                            uint64_t msgSendIndex) const override;
   void populateThunk(InputSection *thunk, Symbol *funcSym) override;
+  void applyOptimizationHints(uint8_t *, const ObjFile &) const override;
 };
 
 } // namespace
@@ -45,31 +52,24 @@ struct ARM64 : ARM64Common {
 // absolute version of this relocation. The semantics of the absolute relocation
 // are weird -- it results in the value of the GOT slot being written, instead
 // of the address. Let's not support it unless we find a real-world use case.
-
-const RelocAttrs &ARM64::getRelocAttrs(uint8_t type) const {
-  static const std::array<RelocAttrs, 11> relocAttrsArray{{
+static constexpr std::array<RelocAttrs, 11> relocAttrsArray{{
 #define B(x) RelocAttrBits::x
-      {"UNSIGNED",
-       B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4) | B(BYTE8)},
-      {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4) | B(BYTE8)},
-      {"BRANCH26", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)},
-      {"PAGE21", B(PCREL) | B(EXTERN) | B(BYTE4)},
-      {"PAGEOFF12", B(ABSOLUTE) | B(EXTERN) | B(BYTE4)},
-      {"GOT_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(GOT) | B(BYTE4)},
-      {"GOT_LOAD_PAGEOFF12",
-       B(ABSOLUTE) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)},
-      {"POINTER_TO_GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)},
-      {"TLVP_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(TLV) | B(BYTE4)},
-      {"TLVP_LOAD_PAGEOFF12",
-       B(ABSOLUTE) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)},
-      {"ADDEND", B(ADDEND)},
+    {"UNSIGNED",
+     B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4) | B(BYTE8)},
+    {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4) | B(BYTE8)},
+    {"BRANCH26", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)},
+    {"PAGE21", B(PCREL) | B(EXTERN) | B(BYTE4)},
+    {"PAGEOFF12", B(ABSOLUTE) | B(EXTERN) | B(BYTE4)},
+    {"GOT_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(GOT) | B(BYTE4)},
+    {"GOT_LOAD_PAGEOFF12",
+     B(ABSOLUTE) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)},
+    {"POINTER_TO_GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)},
+    {"TLVP_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(TLV) | B(BYTE4)},
+    {"TLVP_LOAD_PAGEOFF12",
+     B(ABSOLUTE) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)},
+    {"ADDEND", B(ADDEND)},
 #undef B
-  }};
-  assert(type < relocAttrsArray.size() && "invalid relocation type");
-  if (type >= relocAttrsArray.size())
-    return invalidRelocAttrs;
-  return relocAttrsArray[type];
-}
+}};
 
 static constexpr uint32_t stubCode[] = {
     0x90000010, // 00: adrp  x16, __la_symbol_ptr@page
@@ -77,8 +77,9 @@ static constexpr uint32_t stubCode[] = {
     0xd61f0200, // 08: br    x16
 };
 
-void ARM64::writeStub(uint8_t *buf8, const Symbol &sym) const {
-  ::writeStub<LP64>(buf8, stubCode, sym);
+void ARM64::writeStub(uint8_t *buf8, const Symbol &sym,
+                      uint64_t pointerVA) const {
+  ::writeStub(buf8, stubCode, sym, pointerVA);
 }
 
 static constexpr uint32_t stubHelperHeaderCode[] = {
@@ -100,11 +101,31 @@ static constexpr uint32_t stubHelperEntryCode[] = {
     0x00000000, // 08: l0: .long 0
 };
 
-void ARM64::writeStubHelperEntry(uint8_t *buf8, const DylibSymbol &sym,
+void ARM64::writeStubHelperEntry(uint8_t *buf8, const Symbol &sym,
                                  uint64_t entryVA) const {
   ::writeStubHelperEntry(buf8, stubHelperEntryCode, sym, entryVA);
 }
 
+static constexpr uint32_t objcStubsFastCode[] = {
+    0x90000001, // adrp  x1, __objc_selrefs@page
+    0xf9400021, // ldr   x1, [x1, @selector("foo")@pageoff]
+    0x90000010, // adrp  x16, _got@page
+    0xf9400210, // ldr   x16, [x16, _objc_msgSend@pageoff]
+    0xd61f0200, // br    x16
+    0xd4200020, // brk   #0x1
+    0xd4200020, // brk   #0x1
+    0xd4200020, // brk   #0x1
+};
+
+void ARM64::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
+                                 uint64_t stubOffset, uint64_t selrefsVA,
+                                 uint64_t selectorIndex, uint64_t gotAddr,
+                                 uint64_t msgSendIndex) const {
+  ::writeObjCMsgSendStub<LP64>(buf, objcStubsFastCode, sym, stubsAddr,
+                               stubOffset, selrefsVA, selectorIndex, gotAddr,
+                               msgSendIndex);
+}
+
 // A thunk is the relaxed variation of stubCode. We don't need the
 // extra indirection through a lazy pointer because the target address
 // is known at link time.
@@ -134,9 +155,558 @@ ARM64::ARM64() : ARM64Common(LP64()) {
 
   stubSize = sizeof(stubCode);
   thunkSize = sizeof(thunkCode);
-  branchRange = maxIntN(28) - thunkSize;
+
+  objcStubsFastSize = sizeof(objcStubsFastCode);
+  objcStubsAlignment = 32;
+
+  // Branch immediate is two's complement 26 bits, which is implicitly
+  // multiplied by 4 (since all functions are 4-aligned: The branch range
+  // is -4*(2**(26-1))..4*(2**(26-1) - 1).
+  backwardBranchRange = 128 * 1024 * 1024;
+  forwardBranchRange = backwardBranchRange - 4;
+
+  modeDwarfEncoding = UNWIND_ARM64_MODE_DWARF;
+  subtractorRelocType = ARM64_RELOC_SUBTRACTOR;
+  unsignedRelocType = ARM64_RELOC_UNSIGNED;
+
   stubHelperHeaderSize = sizeof(stubHelperHeaderCode);
   stubHelperEntrySize = sizeof(stubHelperEntryCode);
+
+  relocAttrs = {relocAttrsArray.data(), relocAttrsArray.size()};
+}
+
+namespace {
+struct Adrp {
+  uint32_t destRegister;
+  int64_t addend;
+};
+
+struct Add {
+  uint8_t destRegister;
+  uint8_t srcRegister;
+  uint32_t addend;
+};
+
+enum ExtendType { ZeroExtend = 1, Sign64 = 2, Sign32 = 3 };
+
+struct Ldr {
+  uint8_t destRegister;
+  uint8_t baseRegister;
+  uint8_t p2Size;
+  bool isFloat;
+  ExtendType extendType;
+  int64_t offset;
+};
+} // namespace
+
+static bool parseAdrp(uint32_t insn, Adrp &adrp) {
+  if ((insn & 0x9f000000) != 0x90000000)
+    return false;
+  adrp.destRegister = insn & 0x1f;
+  uint64_t immHi = (insn >> 5) & 0x7ffff;
+  uint64_t immLo = (insn >> 29) & 0x3;
+  adrp.addend = SignExtend64<21>(immLo | (immHi << 2)) * 4096;
+  return true;
+}
+
+static bool parseAdd(uint32_t insn, Add &add) {
+  if ((insn & 0xffc00000) != 0x91000000)
+    return false;
+  add.destRegister = insn & 0x1f;
+  add.srcRegister = (insn >> 5) & 0x1f;
+  add.addend = (insn >> 10) & 0xfff;
+  return true;
+}
+
+static bool parseLdr(uint32_t insn, Ldr &ldr) {
+  ldr.destRegister = insn & 0x1f;
+  ldr.baseRegister = (insn >> 5) & 0x1f;
+  uint8_t size = insn >> 30;
+  uint8_t opc = (insn >> 22) & 3;
+
+  if ((insn & 0x3fc00000) == 0x39400000) {
+    // LDR (immediate), LDRB (immediate), LDRH (immediate)
+    ldr.p2Size = size;
+    ldr.extendType = ZeroExtend;
+    ldr.isFloat = false;
+  } else if ((insn & 0x3f800000) == 0x39800000) {
+    // LDRSB (immediate), LDRSH (immediate), LDRSW (immediate)
+    ldr.p2Size = size;
+    ldr.extendType = static_cast<ExtendType>(opc);
+    ldr.isFloat = false;
+  } else if ((insn & 0x3f400000) == 0x3d400000) {
+    // LDR (immediate, SIMD&FP)
+    ldr.extendType = ZeroExtend;
+    ldr.isFloat = true;
+    if (opc == 1)
+      ldr.p2Size = size;
+    else if (size == 0 && opc == 3)
+      ldr.p2Size = 4;
+    else
+      return false;
+  } else {
+    return false;
+  }
+  ldr.offset = ((insn >> 10) & 0xfff) << ldr.p2Size;
+  return true;
+}
+
+static bool isValidAdrOffset(int32_t delta) { return isInt<21>(delta); }
+
+static void writeAdr(void *loc, uint32_t dest, int32_t delta) {
+  assert(isValidAdrOffset(delta));
+  uint32_t opcode = 0x10000000;
+  uint32_t immHi = (delta & 0x001ffffc) << 3;
+  uint32_t immLo = (delta & 0x00000003) << 29;
+  write32le(loc, opcode | immHi | immLo | dest);
+}
+
+static void writeNop(void *loc) { write32le(loc, 0xd503201f); }
+
+static bool isLiteralLdrEligible(const Ldr &ldr) {
+  return ldr.p2Size > 1 && isShiftedInt<19, 2>(ldr.offset);
+}
+
+static void writeLiteralLdr(void *loc, const Ldr &ldr) {
+  assert(isLiteralLdrEligible(ldr));
+  uint32_t imm19 = (ldr.offset / 4 & maskTrailingOnes<uint32_t>(19)) << 5;
+  uint32_t opcode;
+  switch (ldr.p2Size) {
+  case 2:
+    if (ldr.isFloat)
+      opcode = 0x1c000000;
+    else
+      opcode = ldr.extendType == Sign64 ? 0x98000000 : 0x18000000;
+    break;
+  case 3:
+    opcode = ldr.isFloat ? 0x5c000000 : 0x58000000;
+    break;
+  case 4:
+    opcode = 0x9c000000;
+    break;
+  default:
+    llvm_unreachable("Invalid literal ldr size");
+  }
+  write32le(loc, opcode | imm19 | ldr.destRegister);
+}
+
+static bool isImmediateLdrEligible(const Ldr &ldr) {
+  // Note: We deviate from ld64's behavior, which converts to immediate loads
+  // only if ldr.offset < 4096, even though the offset is divided by the load's
+  // size in the 12-bit immediate operand. Only the unsigned offset variant is
+  // supported.
+
+  uint32_t size = 1 << ldr.p2Size;
+  return ldr.offset >= 0 && (ldr.offset % size) == 0 &&
+         isUInt<12>(ldr.offset >> ldr.p2Size);
+}
+
+static void writeImmediateLdr(void *loc, const Ldr &ldr) {
+  assert(isImmediateLdrEligible(ldr));
+  uint32_t opcode = 0x39000000;
+  if (ldr.isFloat) {
+    opcode |= 0x04000000;
+    assert(ldr.extendType == ZeroExtend);
+  }
+  opcode |= ldr.destRegister;
+  opcode |= ldr.baseRegister << 5;
+  uint8_t size, opc;
+  if (ldr.p2Size == 4) {
+    size = 0;
+    opc = 3;
+  } else {
+    opc = ldr.extendType;
+    size = ldr.p2Size;
+  }
+  uint32_t immBits = ldr.offset >> ldr.p2Size;
+  write32le(loc, opcode | (immBits << 10) | (opc << 22) | (size << 30));
+}
+
+// Transforms a pair of adrp+add instructions into an adr instruction if the
+// target is within the +/- 1 MiB range allowed by the adr's 21 bit signed
+// immediate offset.
+//
+//   adrp xN, _foo@PAGE
+//   add  xM, xN, _foo@PAGEOFF
+// ->
+//   adr  xM, _foo
+//   nop
+static void applyAdrpAdd(uint8_t *buf, const ConcatInputSection *isec,
+                         uint64_t offset1, uint64_t offset2) {
+  uint32_t ins1 = read32le(buf + offset1);
+  uint32_t ins2 = read32le(buf + offset2);
+  Adrp adrp;
+  Add add;
+  if (!parseAdrp(ins1, adrp) || !parseAdd(ins2, add))
+    return;
+  if (adrp.destRegister != add.srcRegister)
+    return;
+
+  uint64_t addr1 = isec->getVA() + offset1;
+  uint64_t referent = pageBits(addr1) + adrp.addend + add.addend;
+  int64_t delta = referent - addr1;
+  if (!isValidAdrOffset(delta))
+    return;
+
+  writeAdr(buf + offset1, add.destRegister, delta);
+  writeNop(buf + offset2);
+}
+
+// Transforms two adrp instructions into a single adrp if their referent
+// addresses are located on the same 4096 byte page.
+//
+//   adrp xN, _foo@PAGE
+//   adrp xN, _bar@PAGE
+// ->
+//   adrp xN, _foo@PAGE
+//   nop
+static void applyAdrpAdrp(uint8_t *buf, const ConcatInputSection *isec,
+                          uint64_t offset1, uint64_t offset2) {
+  uint32_t ins1 = read32le(buf + offset1);
+  uint32_t ins2 = read32le(buf + offset2);
+  Adrp adrp1, adrp2;
+  if (!parseAdrp(ins1, adrp1) || !parseAdrp(ins2, adrp2))
+    return;
+  if (adrp1.destRegister != adrp2.destRegister)
+    return;
+
+  uint64_t page1 = pageBits(offset1 + isec->getVA()) + adrp1.addend;
+  uint64_t page2 = pageBits(offset2 + isec->getVA()) + adrp2.addend;
+  if (page1 != page2)
+    return;
+
+  writeNop(buf + offset2);
+}
+
+// Transforms a pair of adrp+ldr (immediate) instructions into an ldr (literal)
+// load from a PC-relative address if it is 4-byte aligned and within +/- 1 MiB,
+// as ldr can encode a signed 19-bit offset that gets multiplied by 4.
+//
+//   adrp xN, _foo@PAGE
+//   ldr  xM, [xN, _foo@PAGEOFF]
+// ->
+//   nop
+//   ldr  xM, _foo
+static void applyAdrpLdr(uint8_t *buf, const ConcatInputSection *isec,
+                         uint64_t offset1, uint64_t offset2) {
+  uint32_t ins1 = read32le(buf + offset1);
+  uint32_t ins2 = read32le(buf + offset2);
+  Adrp adrp;
+  Ldr ldr;
+  if (!parseAdrp(ins1, adrp) || !parseLdr(ins2, ldr))
+    return;
+  if (adrp.destRegister != ldr.baseRegister)
+    return;
+
+  uint64_t addr1 = isec->getVA() + offset1;
+  uint64_t addr2 = isec->getVA() + offset2;
+  uint64_t referent = pageBits(addr1) + adrp.addend + ldr.offset;
+  ldr.offset = referent - addr2;
+  if (!isLiteralLdrEligible(ldr))
+    return;
+
+  writeNop(buf + offset1);
+  writeLiteralLdr(buf + offset2, ldr);
+}
+
+// GOT loads are emitted by the compiler as a pair of adrp and ldr instructions,
+// but they may be changed to adrp+add by relaxGotLoad(). This hint performs
+// the AdrpLdr or AdrpAdd transformation depending on whether it was relaxed.
+static void applyAdrpLdrGot(uint8_t *buf, const ConcatInputSection *isec,
+                            uint64_t offset1, uint64_t offset2) {
+  uint32_t ins2 = read32le(buf + offset2);
+  Add add;
+  Ldr ldr;
+  if (parseAdd(ins2, add))
+    applyAdrpAdd(buf, isec, offset1, offset2);
+  else if (parseLdr(ins2, ldr))
+    applyAdrpLdr(buf, isec, offset1, offset2);
+}
+
+// Optimizes an adrp+add+ldr sequence used for loading from a local symbol's
+// address by loading directly if it's close enough, or to an adrp(p)+ldr
+// sequence if it's not.
+//
+//   adrp x0, _foo@PAGE
+//   add  x1, x0, _foo@PAGEOFF
+//   ldr  x2, [x1, #off]
+static void applyAdrpAddLdr(uint8_t *buf, const ConcatInputSection *isec,
+                            uint64_t offset1, uint64_t offset2,
+                            uint64_t offset3) {
+  uint32_t ins1 = read32le(buf + offset1);
+  Adrp adrp;
+  if (!parseAdrp(ins1, adrp))
+    return;
+  uint32_t ins2 = read32le(buf + offset2);
+  Add add;
+  if (!parseAdd(ins2, add))
+    return;
+  uint32_t ins3 = read32le(buf + offset3);
+  Ldr ldr;
+  if (!parseLdr(ins3, ldr))
+    return;
+  if (adrp.destRegister != add.srcRegister)
+    return;
+  if (add.destRegister != ldr.baseRegister)
+    return;
+
+  // Load from the target address directly.
+  //   nop
+  //   nop
+  //   ldr x2, [_foo + #off]
+  uint64_t addr1 = isec->getVA() + offset1;
+  uint64_t addr3 = isec->getVA() + offset3;
+  uint64_t referent = pageBits(addr1) + adrp.addend + add.addend;
+  Ldr literalLdr = ldr;
+  literalLdr.offset += referent - addr3;
+  if (isLiteralLdrEligible(literalLdr)) {
+    writeNop(buf + offset1);
+    writeNop(buf + offset2);
+    writeLiteralLdr(buf + offset3, literalLdr);
+    return;
+  }
+
+  // Load the target address into a register and load from there indirectly.
+  //   adr x1, _foo
+  //   nop
+  //   ldr x2, [x1, #off]
+  int64_t adrOffset = referent - addr1;
+  if (isValidAdrOffset(adrOffset)) {
+    writeAdr(buf + offset1, ldr.baseRegister, adrOffset);
+    // Note: ld64 moves the offset into the adr instruction for AdrpAddLdr, but
+    // not for AdrpLdrGotLdr. Its effect is the same either way.
+    writeNop(buf + offset2);
+    return;
+  }
+
+  // Move the target's page offset into the ldr's immediate offset.
+  //   adrp x0, _foo@PAGE
+  //   nop
+  //   ldr x2, [x0, _foo@PAGEOFF + #off]
+  Ldr immediateLdr = ldr;
+  immediateLdr.baseRegister = adrp.destRegister;
+  immediateLdr.offset += add.addend;
+  if (isImmediateLdrEligible(immediateLdr)) {
+    writeNop(buf + offset2);
+    writeImmediateLdr(buf + offset3, immediateLdr);
+    return;
+  }
+}
+
+// Relaxes a GOT-indirect load.
+// If the referenced symbol is external and its GOT entry is within +/- 1 MiB,
+// the GOT entry can be loaded with a single literal ldr instruction.
+// If the referenced symbol is local and thus has been relaxed to adrp+add+ldr,
+// we perform the AdrpAddLdr transformation.
+static void applyAdrpLdrGotLdr(uint8_t *buf, const ConcatInputSection *isec,
+                               uint64_t offset1, uint64_t offset2,
+                               uint64_t offset3) {
+  uint32_t ins2 = read32le(buf + offset2);
+  Add add;
+  Ldr ldr2;
+
+  if (parseAdd(ins2, add)) {
+    applyAdrpAddLdr(buf, isec, offset1, offset2, offset3);
+  } else if (parseLdr(ins2, ldr2)) {
+    // adrp x1, _foo@GOTPAGE
+    // ldr  x2, [x1, _foo@GOTPAGEOFF]
+    // ldr  x3, [x2, #off]
+
+    uint32_t ins1 = read32le(buf + offset1);
+    Adrp adrp;
+    if (!parseAdrp(ins1, adrp))
+      return;
+    uint32_t ins3 = read32le(buf + offset3);
+    Ldr ldr3;
+    if (!parseLdr(ins3, ldr3))
+      return;
+
+    if (ldr2.baseRegister != adrp.destRegister)
+      return;
+    if (ldr3.baseRegister != ldr2.destRegister)
+      return;
+    // Loads from the GOT must be pointer sized.
+    if (ldr2.p2Size != 3 || ldr2.isFloat)
+      return;
+
+    uint64_t addr1 = isec->getVA() + offset1;
+    uint64_t addr2 = isec->getVA() + offset2;
+    uint64_t referent = pageBits(addr1) + adrp.addend + ldr2.offset;
+    // Load the GOT entry's address directly.
+    //   nop
+    //   ldr x2, _foo@GOTPAGE + _foo@GOTPAGEOFF
+    //   ldr x3, [x2, #off]
+    Ldr literalLdr = ldr2;
+    literalLdr.offset = referent - addr2;
+    if (isLiteralLdrEligible(literalLdr)) {
+      writeNop(buf + offset1);
+      writeLiteralLdr(buf + offset2, literalLdr);
+    }
+  }
+}
+
+static uint64_t readValue(const uint8_t *&ptr, const uint8_t *end) {
+  unsigned int n = 0;
+  uint64_t value = decodeULEB128(ptr, &n, end);
+  ptr += n;
+  return value;
+}
+
+template <typename Callback>
+static void forEachHint(ArrayRef<uint8_t> data, Callback callback) {
+  std::array<uint64_t, 3> args;
+
+  for (const uint8_t *p = data.begin(), *end = data.end(); p < end;) {
+    uint64_t type = readValue(p, end);
+    if (type == 0)
+      break;
+
+    uint64_t argCount = readValue(p, end);
+    // All known LOH types as of 2022-09 have 3 or fewer arguments; skip others.
+    if (argCount > 3) {
+      for (unsigned i = 0; i < argCount; ++i)
+        readValue(p, end);
+      continue;
+    }
+
+    for (unsigned i = 0; i < argCount; ++i)
+      args[i] = readValue(p, end);
+    callback(type, ArrayRef<uint64_t>(args.data(), argCount));
+  }
+}
+
+// On RISC architectures like arm64, materializing a memory address generally
+// takes multiple instructions. If the referenced symbol is located close enough
+// in memory, fewer instructions are needed.
+//
+// Linker optimization hints record where addresses are computed. After
+// addresses have been assigned, if possible, we change them to a shorter
+// sequence of instructions. The size of the binary is not modified; the
+// eliminated instructions are replaced with NOPs. This still leads to faster
+// code as the CPU can skip over NOPs quickly.
+//
+// LOHs are specified by the LC_LINKER_OPTIMIZATION_HINTS load command, which
+// points to a sequence of ULEB128-encoded numbers. Each entry specifies a
+// transformation kind, and 2 or 3 addresses where the instructions are located.
+void ARM64::applyOptimizationHints(uint8_t *outBuf, const ObjFile &obj) const {
+  ArrayRef<uint8_t> data = obj.getOptimizationHints();
+  if (data.empty())
+    return;
+
+  const ConcatInputSection *section = nullptr;
+  uint64_t sectionAddr = 0;
+  uint8_t *buf = nullptr;
+
+  auto findSection = [&](uint64_t addr) {
+    if (section && addr >= sectionAddr &&
+        addr < sectionAddr + section->getSize())
+      return true;
+
+    auto secIt = std::prev(llvm::upper_bound(
+        obj.sections, addr,
+        [](uint64_t off, const Section *sec) { return off < sec->addr; }));
+    const Section *sec = *secIt;
+
+    auto subsecIt = std::prev(llvm::upper_bound(
+        sec->subsections, addr - sec->addr,
+        [](uint64_t off, Subsection subsec) { return off < subsec.offset; }));
+    const Subsection &subsec = *subsecIt;
+    const ConcatInputSection *isec =
+        dyn_cast_or_null<ConcatInputSection>(subsec.isec);
+    if (!isec || isec->shouldOmitFromOutput())
+      return false;
+
+    section = isec;
+    sectionAddr = subsec.offset + sec->addr;
+    buf = outBuf + section->outSecOff + section->parent->fileOff;
+    return true;
+  };
+
+  auto isValidOffset = [&](uint64_t offset) {
+    if (offset < sectionAddr || offset >= sectionAddr + section->getSize()) {
+      error(toString(&obj) +
+            ": linker optimization hint spans multiple sections");
+      return false;
+    }
+    return true;
+  };
+
+  bool hasAdrpAdrp = false;
+  forEachHint(data, [&](uint64_t kind, ArrayRef<uint64_t> args) {
+    if (kind == LOH_ARM64_ADRP_ADRP) {
+      hasAdrpAdrp = true;
+      return;
+    }
+
+    if (!findSection(args[0]))
+      return;
+    switch (kind) {
+    case LOH_ARM64_ADRP_ADD:
+      if (isValidOffset(args[1]))
+        applyAdrpAdd(buf, section, args[0] - sectionAddr,
+                     args[1] - sectionAddr);
+      break;
+    case LOH_ARM64_ADRP_LDR:
+      if (isValidOffset(args[1]))
+        applyAdrpLdr(buf, section, args[0] - sectionAddr,
+                     args[1] - sectionAddr);
+      break;
+    case LOH_ARM64_ADRP_LDR_GOT:
+      if (isValidOffset(args[1]))
+        applyAdrpLdrGot(buf, section, args[0] - sectionAddr,
+                        args[1] - sectionAddr);
+      break;
+    case LOH_ARM64_ADRP_ADD_LDR:
+      if (isValidOffset(args[1]) && isValidOffset(args[2]))
+        applyAdrpAddLdr(buf, section, args[0] - sectionAddr,
+                        args[1] - sectionAddr, args[2] - sectionAddr);
+      break;
+    case LOH_ARM64_ADRP_LDR_GOT_LDR:
+      if (isValidOffset(args[1]) && isValidOffset(args[2]))
+        applyAdrpLdrGotLdr(buf, section, args[0] - sectionAddr,
+                           args[1] - sectionAddr, args[2] - sectionAddr);
+      break;
+    case LOH_ARM64_ADRP_ADD_STR:
+    case LOH_ARM64_ADRP_LDR_GOT_STR:
+      // TODO: Implement these
+      break;
+    }
+  });
+
+  if (!hasAdrpAdrp)
+    return;
+
+  // AdrpAdrp optimization hints are performed in a second pass because they
+  // might interfere with other transformations. For instance, consider the
+  // following input:
+  //
+  //   adrp x0, _foo@PAGE
+  //   add  x1, x0, _foo@PAGEOFF
+  //   adrp x0, _bar@PAGE
+  //   add  x2, x0, _bar@PAGEOFF
+  //
+  // If we perform the AdrpAdrp relaxation first, we get:
+  //
+  //   adrp x0, _foo@PAGE
+  //   add  x1, x0, _foo@PAGEOFF
+  //   nop
+  //   add x2, x0, _bar@PAGEOFF
+  //
+  // If we then apply AdrpAdd to the first two instructions, the add will have a
+  // garbage value in x0:
+  //
+  //   adr  x1, _foo
+  //   nop
+  //   nop
+  //   add  x2, x0, _bar@PAGEOFF
+  forEachHint(data, [&](uint64_t kind, ArrayRef<uint64_t> args) {
+    if (kind != LOH_ARM64_ADRP_ADRP)
+      return;
+    if (!findSection(args[0]))
+      return;
+    if (isValidOffset(args[1]))
+      applyAdrpAdrp(buf, section, args[0] - sectionAddr, args[1] - sectionAddr);
+  });
 }
 
 TargetInfo *macho::createARM64TargetInfo() {
index 67e7292..45cf0ea 100644 (file)
@@ -38,56 +38,57 @@ int64_t ARM64Common::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,
   }
 }
 
+static void writeValue(uint8_t *loc, const Reloc &r, uint64_t value) {
+  switch (r.length) {
+  case 2:
+    checkInt(loc, r, value, 32);
+    write32le(loc, value);
+    break;
+  case 3:
+    write64le(loc, value);
+    break;
+  default:
+    llvm_unreachable("invalid r_length");
+  }
+}
+
 // For instruction relocations (load, store, add), the base
 // instruction is pre-populated in the text section. A pre-populated
 // instruction has opcode & register-operand bits set, with immediate
 // operands zeroed. We read it from text, OR-in the immediate
 // operands, then write-back the completed instruction.
-
 void ARM64Common::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
                               uint64_t pc) const {
+  auto loc32 = reinterpret_cast<uint32_t *>(loc);
   uint32_t base = ((r.length == 2) ? read32le(loc) : 0);
   switch (r.type) {
   case ARM64_RELOC_BRANCH26:
-    value = encodeBranch26(r, base, value - pc);
+    encodeBranch26(loc32, r, base, value - pc);
     break;
   case ARM64_RELOC_SUBTRACTOR:
   case ARM64_RELOC_UNSIGNED:
-    if (r.length == 2)
-      checkInt(r, value, 32);
+    writeValue(loc, r, value);
     break;
   case ARM64_RELOC_POINTER_TO_GOT:
     if (r.pcrel)
       value -= pc;
-    checkInt(r, value, 32);
+    writeValue(loc, r, value);
     break;
   case ARM64_RELOC_PAGE21:
   case ARM64_RELOC_GOT_LOAD_PAGE21:
-  case ARM64_RELOC_TLVP_LOAD_PAGE21: {
+  case ARM64_RELOC_TLVP_LOAD_PAGE21:
     assert(r.pcrel);
-    value = encodePage21(r, base, pageBits(value) - pageBits(pc));
+    encodePage21(loc32, r, base, pageBits(value) - pageBits(pc));
     break;
-  }
   case ARM64_RELOC_PAGEOFF12:
   case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
   case ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
     assert(!r.pcrel);
-    value = encodePageOff12(base, value);
+    encodePageOff12(loc32, r, base, value);
     break;
   default:
     llvm_unreachable("unexpected relocation type");
   }
-
-  switch (r.length) {
-  case 2:
-    write32le(loc, value);
-    break;
-  case 3:
-    write64le(loc, value);
-    break;
-  default:
-    llvm_unreachable("invalid r_length");
-  }
 }
 
 void ARM64Common::relaxGotLoad(uint8_t *loc, uint8_t type) const {
@@ -108,3 +109,44 @@ void ARM64Common::relaxGotLoad(uint8_t *loc, uint8_t type) const {
   instruction = ((instruction & 0x001fffff) | 0x91000000);
   write32le(loc, instruction);
 }
+
+void ARM64Common::handleDtraceReloc(const Symbol *sym, const Reloc &r,
+                                    uint8_t *loc) const {
+  assert(r.type == ARM64_RELOC_BRANCH26);
+
+  if (config->outputType == MH_OBJECT)
+    return;
+
+  if (sym->getName().startswith("___dtrace_probe")) {
+    // change call site to a NOP
+    write32le(loc, 0xD503201F);
+  } else if (sym->getName().startswith("___dtrace_isenabled")) {
+    // change call site to 'MOVZ X0,0'
+    write32le(loc, 0xD2800000);
+  } else {
+    error("Unrecognized dtrace symbol prefix: " + toString(*sym));
+  }
+}
+
+static void reportUnalignedLdrStr(Twine loc, uint64_t va, int align,
+                                  const Symbol *sym) {
+  std::string symbolHint;
+  if (sym)
+    symbolHint = " (" + toString(*sym) + ")";
+  error(loc + ": " + Twine(8 * align) + "-bit LDR/STR to 0x" +
+        llvm::utohexstr(va) + symbolHint + " is not " + Twine(align) +
+        "-byte aligned");
+}
+
+void macho::reportUnalignedLdrStr(void *loc, const lld::macho::Reloc &r,
+                                  uint64_t va, int align) {
+  uint64_t off = reinterpret_cast<const uint8_t *>(loc) - in.bufferStart;
+  const InputSection *isec = offsetToInputSection(&off);
+  std::string locStr = isec ? isec->getLocation(off) : "(invalid location)";
+  ::reportUnalignedLdrStr(locStr, va, align, r.referent.dyn_cast<Symbol *>());
+}
+
+void macho::reportUnalignedLdrStr(void *loc, lld::macho::SymbolDiagnostic d,
+                                  uint64_t va, int align) {
+  ::reportUnalignedLdrStr(d.reason, va, align, d.symbol);
+}
index 934101c..8dc2412 100644 (file)
@@ -16,8 +16,7 @@
 
 #include "llvm/BinaryFormat/MachO.h"
 
-namespace lld {
-namespace macho {
+namespace lld::macho {
 
 struct ARM64Common : TargetInfo {
   template <class LP> ARM64Common(LP lp) : TargetInfo(lp) {}
@@ -29,6 +28,9 @@ struct ARM64Common : TargetInfo {
 
   void relaxGotLoad(uint8_t *loc, uint8_t type) const override;
   uint64_t getPageSize() const override { return 16 * 1024; }
+
+  void handleDtraceReloc(const Symbol *sym, const Reloc &r,
+                         uint8_t *loc) const override;
 };
 
 inline uint64_t bitField(uint64_t value, int right, int width, int left) {
@@ -40,16 +42,18 @@ inline uint64_t bitField(uint64_t value, int right, int width, int left) {
 // |           |                       imm26                       |
 // +-----------+---------------------------------------------------+
 
-inline uint64_t encodeBranch26(const Reloc &r, uint64_t base, uint64_t va) {
-  checkInt(r, va, 28);
+inline void encodeBranch26(uint32_t *loc, const Reloc &r, uint32_t base,
+                           uint64_t va) {
+  checkInt(loc, r, va, 28);
   // Since branch destinations are 4-byte aligned, the 2 least-
   // significant bits are 0. They are right shifted off the end.
-  return (base | bitField(va, 2, 26, 0));
+  llvm::support::endian::write32le(loc, base | bitField(va, 2, 26, 0));
 }
 
-inline uint64_t encodeBranch26(SymbolDiagnostic d, uint64_t base, uint64_t va) {
-  checkInt(d, va, 28);
-  return (base | bitField(va, 2, 26, 0));
+inline void encodeBranch26(uint32_t *loc, SymbolDiagnostic d, uint32_t base,
+                           uint64_t va) {
+  checkInt(loc, d, va, 28);
+  llvm::support::endian::write32le(loc, base | bitField(va, 2, 26, 0));
 }
 
 //   30 29          23                                  5
@@ -57,32 +61,45 @@ inline uint64_t encodeBranch26(SymbolDiagnostic d, uint64_t base, uint64_t va) {
 // | |ilo|         |                immhi                |         |
 // +-+---+---------+-------------------------------------+---------+
 
-inline uint64_t encodePage21(const Reloc &r, uint64_t base, uint64_t va) {
-  checkInt(r, va, 35);
-  return (base | bitField(va, 12, 2, 29) | bitField(va, 14, 19, 5));
+inline void encodePage21(uint32_t *loc, const Reloc &r, uint32_t base,
+                         uint64_t va) {
+  checkInt(loc, r, va, 35);
+  llvm::support::endian::write32le(loc, base | bitField(va, 12, 2, 29) |
+                                            bitField(va, 14, 19, 5));
 }
 
-inline uint64_t encodePage21(SymbolDiagnostic d, uint64_t base, uint64_t va) {
-  checkInt(d, va, 35);
-  return (base | bitField(va, 12, 2, 29) | bitField(va, 14, 19, 5));
+inline void encodePage21(uint32_t *loc, SymbolDiagnostic d, uint32_t base,
+                         uint64_t va) {
+  checkInt(loc, d, va, 35);
+  llvm::support::endian::write32le(loc, base | bitField(va, 12, 2, 29) |
+                                            bitField(va, 14, 19, 5));
 }
 
+void reportUnalignedLdrStr(void *loc, const Reloc &, uint64_t va, int align);
+void reportUnalignedLdrStr(void *loc, SymbolDiagnostic, uint64_t va, int align);
+
 //                      21                   10
 // +-------------------+-----------------------+-------------------+
 // |                   |         imm12         |                   |
 // +-------------------+-----------------------+-------------------+
 
-inline uint64_t encodePageOff12(uint32_t base, uint64_t va) {
+template <typename Target>
+inline void encodePageOff12(uint32_t *loc, Target t, uint32_t base,
+                            uint64_t va) {
   int scale = 0;
   if ((base & 0x3b00'0000) == 0x3900'0000) { // load/store
     scale = base >> 30;
     if (scale == 0 && (base & 0x0480'0000) == 0x0480'0000) // 128-bit variant
       scale = 4;
   }
+  const int size = 1 << scale;
+  if ((va & (size - 1)) != 0)
+    reportUnalignedLdrStr(loc, t, va, size);
 
   // TODO(gkm): extract embedded addend and warn if != 0
   // uint64_t addend = ((base & 0x003FFC00) >> 10);
-  return (base | bitField(va, scale, 12 - scale, 10));
+  llvm::support::endian::write32le(loc,
+                                   base | bitField(va, scale, 12 - scale, 10));
 }
 
 inline uint64_t pageBits(uint64_t address) {
@@ -90,18 +107,15 @@ inline uint64_t pageBits(uint64_t address) {
   return address & pageMask;
 }
 
-template <class LP>
 inline void writeStub(uint8_t *buf8, const uint32_t stubCode[3],
-                      const macho::Symbol &sym) {
+                      const macho::Symbol &sym, uint64_t pointerVA) {
   auto *buf32 = reinterpret_cast<uint32_t *>(buf8);
   constexpr size_t stubCodeSize = 3 * sizeof(uint32_t);
+  SymbolDiagnostic d = {&sym, "stub"};
   uint64_t pcPageBits =
       pageBits(in.stubs->addr + sym.stubsIndex * stubCodeSize);
-  uint64_t lazyPointerVA =
-      in.lazyPointers->addr + sym.stubsIndex * LP::wordSize;
-  buf32[0] = encodePage21({&sym, "stub"}, stubCode[0],
-                          pageBits(lazyPointerVA) - pcPageBits);
-  buf32[1] = encodePageOff12(stubCode[1], lazyPointerVA);
+  encodePage21(&buf32[0], d, stubCode[0], pageBits(pointerVA) - pcPageBits);
+  encodePageOff12(&buf32[1], d, stubCode[1], pointerVA);
   buf32[2] = stubCode[2];
 }
 
@@ -114,31 +128,57 @@ inline void writeStubHelperHeader(uint8_t *buf8,
   };
   uint64_t loaderVA = in.imageLoaderCache->getVA();
   SymbolDiagnostic d = {nullptr, "stub header helper"};
-  buf32[0] = encodePage21(d, stubHelperHeaderCode[0],
-                          pageBits(loaderVA) - pcPageBits(0));
-  buf32[1] = encodePageOff12(stubHelperHeaderCode[1], loaderVA);
+  encodePage21(&buf32[0], d, stubHelperHeaderCode[0],
+               pageBits(loaderVA) - pcPageBits(0));
+  encodePageOff12(&buf32[1], d, stubHelperHeaderCode[1], loaderVA);
   buf32[2] = stubHelperHeaderCode[2];
   uint64_t binderVA =
       in.got->addr + in.stubHelper->stubBinder->gotIndex * LP::wordSize;
-  buf32[3] = encodePage21(d, stubHelperHeaderCode[3],
-                          pageBits(binderVA) - pcPageBits(3));
-  buf32[4] = encodePageOff12(stubHelperHeaderCode[4], binderVA);
+  encodePage21(&buf32[3], d, stubHelperHeaderCode[3],
+               pageBits(binderVA) - pcPageBits(3));
+  encodePageOff12(&buf32[4], d, stubHelperHeaderCode[4], binderVA);
   buf32[5] = stubHelperHeaderCode[5];
 }
 
 inline void writeStubHelperEntry(uint8_t *buf8,
                                  const uint32_t stubHelperEntryCode[3],
-                                 const DylibSymbol &sym, uint64_t entryVA) {
+                                 const Symbol &sym, uint64_t entryVA) {
   auto *buf32 = reinterpret_cast<uint32_t *>(buf8);
   auto pcVA = [entryVA](int i) { return entryVA + i * sizeof(uint32_t); };
   uint64_t stubHelperHeaderVA = in.stubHelper->addr;
   buf32[0] = stubHelperEntryCode[0];
-  buf32[1] = encodeBranch26({&sym, "stub helper"}, stubHelperEntryCode[1],
-                            stubHelperHeaderVA - pcVA(1));
+  encodeBranch26(&buf32[1], {&sym, "stub helper"}, stubHelperEntryCode[1],
+                 stubHelperHeaderVA - pcVA(1));
   buf32[2] = sym.lazyBindOffset;
 }
 
-} // namespace macho
-} // namespace lld
+template <class LP>
+inline void
+writeObjCMsgSendStub(uint8_t *buf, const uint32_t objcStubsFastCode[8],
+                     Symbol *sym, uint64_t stubsAddr, uint64_t stubOffset,
+                     uint64_t selrefsVA, uint64_t selectorIndex,
+                     uint64_t gotAddr, uint64_t msgSendIndex) {
+  SymbolDiagnostic d = {sym, sym->getName()};
+  auto *buf32 = reinterpret_cast<uint32_t *>(buf);
+
+  auto pcPageBits = [stubsAddr, stubOffset](int i) {
+    return pageBits(stubsAddr + stubOffset + i * sizeof(uint32_t));
+  };
+
+  uint64_t selectorOffset = selectorIndex * LP::wordSize;
+  encodePage21(&buf32[0], d, objcStubsFastCode[0],
+               pageBits(selrefsVA + selectorOffset) - pcPageBits(0));
+  encodePageOff12(&buf32[1], d, objcStubsFastCode[1],
+                  selrefsVA + selectorOffset);
+  encodePage21(&buf32[2], d, objcStubsFastCode[2],
+               pageBits(gotAddr) - pcPageBits(2));
+  encodePage21(&buf32[3], d, objcStubsFastCode[3], msgSendIndex * LP::wordSize);
+  buf32[4] = objcStubsFastCode[4];
+  buf32[5] = objcStubsFastCode[5];
+  buf32[6] = objcStubsFastCode[6];
+  buf32[7] = objcStubsFastCode[7];
+}
+
+} // namespace lld::macho
 
 #endif
index f7b1439..c6bb6ee 100644 (file)
@@ -29,40 +29,37 @@ namespace {
 
 struct ARM64_32 : ARM64Common {
   ARM64_32();
-  void writeStub(uint8_t *buf, const Symbol &) const override;
+  void writeStub(uint8_t *buf, const Symbol &, uint64_t) const override;
   void writeStubHelperHeader(uint8_t *buf) const override;
-  void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &,
+  void writeStubHelperEntry(uint8_t *buf, const Symbol &,
                             uint64_t entryAddr) const override;
-  const RelocAttrs &getRelocAttrs(uint8_t type) const override;
+  void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
+                            uint64_t stubOffset, uint64_t selrefsVA,
+                            uint64_t selectorIndex, uint64_t gotAddr,
+                            uint64_t msgSendIndex) const override;
 };
 
 } // namespace
 
 // These are very similar to ARM64's relocation attributes, except that we don't
 // have the BYTE8 flag set.
-const RelocAttrs &ARM64_32::getRelocAttrs(uint8_t type) const {
-  static const std::array<RelocAttrs, 11> relocAttrsArray{{
+static constexpr std::array<RelocAttrs, 11> relocAttrsArray{{
 #define B(x) RelocAttrBits::x
-      {"UNSIGNED", B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4)},
-      {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4)},
-      {"BRANCH26", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)},
-      {"PAGE21", B(PCREL) | B(EXTERN) | B(BYTE4)},
-      {"PAGEOFF12", B(ABSOLUTE) | B(EXTERN) | B(BYTE4)},
-      {"GOT_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(GOT) | B(BYTE4)},
-      {"GOT_LOAD_PAGEOFF12",
-       B(ABSOLUTE) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)},
-      {"POINTER_TO_GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)},
-      {"TLVP_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(TLV) | B(BYTE4)},
-      {"TLVP_LOAD_PAGEOFF12",
-       B(ABSOLUTE) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)},
-      {"ADDEND", B(ADDEND)},
+    {"UNSIGNED", B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4)},
+    {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4)},
+    {"BRANCH26", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)},
+    {"PAGE21", B(PCREL) | B(EXTERN) | B(BYTE4)},
+    {"PAGEOFF12", B(ABSOLUTE) | B(EXTERN) | B(BYTE4)},
+    {"GOT_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(GOT) | B(BYTE4)},
+    {"GOT_LOAD_PAGEOFF12",
+     B(ABSOLUTE) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)},
+    {"POINTER_TO_GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)},
+    {"TLVP_LOAD_PAGE21", B(PCREL) | B(EXTERN) | B(TLV) | B(BYTE4)},
+    {"TLVP_LOAD_PAGEOFF12",
+     B(ABSOLUTE) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)},
+    {"ADDEND", B(ADDEND)},
 #undef B
-  }};
-  assert(type < relocAttrsArray.size() && "invalid relocation type");
-  if (type >= relocAttrsArray.size())
-    return invalidRelocAttrs;
-  return relocAttrsArray[type];
-}
+}};
 
 // The stub code is fairly similar to ARM64's, except that we load pointers into
 // 32-bit 'w' registers, instead of the 64-bit 'x' ones.
@@ -73,8 +70,9 @@ static constexpr uint32_t stubCode[] = {
     0xd61f0200, // 08: br    x16
 };
 
-void ARM64_32::writeStub(uint8_t *buf8, const Symbol &sym) const {
-  ::writeStub<ILP32>(buf8, stubCode, sym);
+void ARM64_32::writeStub(uint8_t *buf8, const Symbol &sym,
+                         uint64_t pointerVA) const {
+  ::writeStub(buf8, stubCode, sym, pointerVA);
 }
 
 static constexpr uint32_t stubHelperHeaderCode[] = {
@@ -96,18 +94,32 @@ static constexpr uint32_t stubHelperEntryCode[] = {
     0x00000000, // 08: l0: .long 0
 };
 
-void ARM64_32::writeStubHelperEntry(uint8_t *buf8, const DylibSymbol &sym,
+void ARM64_32::writeStubHelperEntry(uint8_t *buf8, const Symbol &sym,
                                     uint64_t entryVA) const {
   ::writeStubHelperEntry(buf8, stubHelperEntryCode, sym, entryVA);
 }
 
+void ARM64_32::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym,
+                                    uint64_t stubsAddr, uint64_t stubOffset,
+                                    uint64_t selrefsVA, uint64_t selectorIndex,
+                                    uint64_t gotAddr,
+                                    uint64_t msgSendIndex) const {
+  fatal("TODO: implement this");
+}
+
 ARM64_32::ARM64_32() : ARM64Common(ILP32()) {
   cpuType = CPU_TYPE_ARM64_32;
   cpuSubtype = CPU_SUBTYPE_ARM64_V8;
 
+  modeDwarfEncoding = 0x04000000;              // UNWIND_ARM_MODE_DWARF
+  subtractorRelocType = GENERIC_RELOC_INVALID; // FIXME
+  unsignedRelocType = GENERIC_RELOC_INVALID;   // FIXME
+
   stubSize = sizeof(stubCode);
   stubHelperHeaderSize = sizeof(stubHelperHeaderCode);
   stubHelperEntrySize = sizeof(stubHelperEntryCode);
+
+  relocAttrs = {relocAttrsArray.data(), relocAttrsArray.size()};
 }
 
 TargetInfo *macho::createARM64_32TargetInfo() {
index 7e21558..048702f 100644 (file)
@@ -12,6 +12,7 @@
 #include "Target.h"
 
 #include "lld/Common/ErrorHandler.h"
+#include "mach-o/compact_unwind_encoding.h"
 #include "llvm/BinaryFormat/MachO.h"
 #include "llvm/Support/Endian.h"
 
@@ -30,39 +31,40 @@ struct X86_64 : TargetInfo {
   void relocateOne(uint8_t *loc, const Reloc &, uint64_t va,
                    uint64_t relocVA) const override;
 
-  void writeStub(uint8_t *buf, const Symbol &) const override;
+  void writeStub(uint8_t *buf, const Symbol &,
+                 uint64_t pointerVA) const override;
   void writeStubHelperHeader(uint8_t *buf) const override;
-  void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &,
+  void writeStubHelperEntry(uint8_t *buf, const Symbol &,
                             uint64_t entryAddr) const override;
 
+  void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
+                            uint64_t stubOffset, uint64_t selrefsVA,
+                            uint64_t selectorIndex, uint64_t gotAddr,
+                            uint64_t msgSendIndex) const override;
+
   void relaxGotLoad(uint8_t *loc, uint8_t type) const override;
-  const RelocAttrs &getRelocAttrs(uint8_t type) const override;
   uint64_t getPageSize() const override { return 4 * 1024; }
-};
 
+  void handleDtraceReloc(const Symbol *sym, const Reloc &r,
+                         uint8_t *loc) const override;
+};
 } // namespace
 
-const RelocAttrs &X86_64::getRelocAttrs(uint8_t type) const {
-  static const std::array<RelocAttrs, 10> relocAttrsArray{{
+static constexpr std::array<RelocAttrs, 10> relocAttrsArray{{
 #define B(x) RelocAttrBits::x
-      {"UNSIGNED",
-       B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4) | B(BYTE8)},
-      {"SIGNED", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},
-      {"BRANCH", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)},
-      {"GOT_LOAD", B(PCREL) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)},
-      {"GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)},
-      {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4) | B(BYTE8)},
-      {"SIGNED_1", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},
-      {"SIGNED_2", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},
-      {"SIGNED_4", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},
-      {"TLV", B(PCREL) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)},
+    {"UNSIGNED",
+     B(UNSIGNED) | B(ABSOLUTE) | B(EXTERN) | B(LOCAL) | B(BYTE4) | B(BYTE8)},
+    {"SIGNED", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},
+    {"BRANCH", B(PCREL) | B(EXTERN) | B(BRANCH) | B(BYTE4)},
+    {"GOT_LOAD", B(PCREL) | B(EXTERN) | B(GOT) | B(LOAD) | B(BYTE4)},
+    {"GOT", B(PCREL) | B(EXTERN) | B(GOT) | B(POINTER) | B(BYTE4)},
+    {"SUBTRACTOR", B(SUBTRAHEND) | B(EXTERN) | B(BYTE4) | B(BYTE8)},
+    {"SIGNED_1", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},
+    {"SIGNED_2", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},
+    {"SIGNED_4", B(PCREL) | B(EXTERN) | B(LOCAL) | B(BYTE4)},
+    {"TLV", B(PCREL) | B(EXTERN) | B(TLV) | B(LOAD) | B(BYTE4)},
 #undef B
-  }};
-  assert(type < relocAttrsArray.size() && "invalid relocation type");
-  if (type >= relocAttrsArray.size())
-    return invalidRelocAttrs;
-  return relocAttrsArray[type];
-}
+}};
 
 static int pcrelOffset(uint8_t type) {
   switch (type) {
@@ -102,9 +104,9 @@ void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
   switch (r.length) {
   case 2:
     if (r.type == X86_64_RELOC_UNSIGNED)
-      checkUInt(r, value, 32);
+      checkUInt(loc, r, value, 32);
     else
-      checkInt(r, value, 32);
+      checkInt(loc, r, value, 32);
     write32le(loc, value);
     break;
   case 3:
@@ -127,7 +129,7 @@ void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
 static void writeRipRelative(SymbolDiagnostic d, uint8_t *buf, uint64_t bufAddr,
                              uint64_t bufOff, uint64_t destAddr) {
   uint64_t rip = bufAddr + bufOff;
-  checkInt(d, destAddr - rip, 32);
+  checkInt(buf, d, destAddr - rip, 32);
   // For the instructions we care about, the RIP-relative address is always
   // stored in the last 4 bytes of the instruction.
   write32le(buf + bufOff - 4, destAddr - rip);
@@ -137,11 +139,11 @@ static constexpr uint8_t stub[] = {
     0xff, 0x25, 0, 0, 0, 0, // jmpq *__la_symbol_ptr(%rip)
 };
 
-void X86_64::writeStub(uint8_t *buf, const Symbol &sym) const {
+void X86_64::writeStub(uint8_t *buf, const Symbol &sym,
+                       uint64_t pointerVA) const {
   memcpy(buf, stub, 2); // just copy the two nonzero bytes
   uint64_t stubAddr = in.stubs->addr + sym.stubsIndex * sizeof(stub);
-  writeRipRelative({&sym, "stub"}, buf, stubAddr, sizeof(stub),
-                   in.lazyPointers->addr + sym.stubsIndex * LP64::wordSize);
+  writeRipRelative({&sym, "stub"}, buf, stubAddr, sizeof(stub), pointerVA);
 }
 
 static constexpr uint8_t stubHelperHeader[] = {
@@ -166,7 +168,7 @@ static constexpr uint8_t stubHelperEntry[] = {
     0xe9, 0, 0, 0, 0, // 0x5: jmp <__stub_helper>
 };
 
-void X86_64::writeStubHelperEntry(uint8_t *buf, const DylibSymbol &sym,
+void X86_64::writeStubHelperEntry(uint8_t *buf, const Symbol &sym,
                                   uint64_t entryAddr) const {
   memcpy(buf, stubHelperEntry, sizeof(stubHelperEntry));
   write32le(buf + 1, sym.lazyBindOffset);
@@ -174,6 +176,24 @@ void X86_64::writeStubHelperEntry(uint8_t *buf, const DylibSymbol &sym,
                    sizeof(stubHelperEntry), in.stubHelper->addr);
 }
 
+static constexpr uint8_t objcStubsFastCode[] = {
+    0x48, 0x8b, 0x35, 0, 0, 0, 0, // 0x0: movq selrefs@selector(%rip), %rsi
+    0xff, 0x25, 0,    0, 0, 0,    // 0x7: jmpq *_objc_msgSend@GOT(%rip)
+};
+
+void X86_64::writeObjCMsgSendStub(uint8_t *buf, Symbol *sym, uint64_t stubsAddr,
+                                  uint64_t stubOffset, uint64_t selrefsVA,
+                                  uint64_t selectorIndex, uint64_t gotAddr,
+                                  uint64_t msgSendIndex) const {
+  memcpy(buf, objcStubsFastCode, sizeof(objcStubsFastCode));
+  SymbolDiagnostic d = {sym, sym->getName()};
+  uint64_t stubAddr = stubsAddr + stubOffset;
+  writeRipRelative(d, buf, stubAddr, 7,
+                   selrefsVA + selectorIndex * LP64::wordSize);
+  writeRipRelative(d, buf, stubAddr, 0xd,
+                   gotAddr + msgSendIndex * LP64::wordSize);
+}
+
 void X86_64::relaxGotLoad(uint8_t *loc, uint8_t type) const {
   // Convert MOVQ to LEAQ
   if (loc[-2] != 0x8b)
@@ -185,12 +205,41 @@ X86_64::X86_64() : TargetInfo(LP64()) {
   cpuType = CPU_TYPE_X86_64;
   cpuSubtype = CPU_SUBTYPE_X86_64_ALL;
 
+  modeDwarfEncoding = UNWIND_X86_MODE_DWARF;
+  subtractorRelocType = X86_64_RELOC_SUBTRACTOR;
+  unsignedRelocType = X86_64_RELOC_UNSIGNED;
+
   stubSize = sizeof(stub);
   stubHelperHeaderSize = sizeof(stubHelperHeader);
   stubHelperEntrySize = sizeof(stubHelperEntry);
+
+  objcStubsFastSize = sizeof(objcStubsFastCode);
+  objcStubsAlignment = 1;
+
+  relocAttrs = {relocAttrsArray.data(), relocAttrsArray.size()};
 }
 
 TargetInfo *macho::createX86_64TargetInfo() {
   static X86_64 t;
   return &t;
 }
+
+void X86_64::handleDtraceReloc(const Symbol *sym, const Reloc &r,
+                               uint8_t *loc) const {
+  assert(r.type == X86_64_RELOC_BRANCH);
+
+  if (config->outputType == MH_OBJECT)
+    return;
+
+  if (sym->getName().startswith("___dtrace_probe")) {
+    // change call site to a NOP
+    loc[-1] = 0x90;
+    write32le(loc, 0x00401F0F);
+  } else if (sym->getName().startswith("___dtrace_isenabled")) {
+    // change call site to a clear eax
+    loc[-1] = 0x33;
+    write32le(loc, 0x909090C0);
+  } else {
+    error("Unrecognized dtrace symbol prefix: " + toString(*sym));
+  }
+}
index eff1812..ea26889 100644 (file)
@@ -4,7 +4,7 @@ add_public_tablegen_target(MachOOptionsTableGen)
 
 include_directories(${LLVM_MAIN_SRC_DIR}/../libunwind/include)
 
-add_lld_library(lldMachO2
+add_lld_library(lldMachO
   Arch/ARM.cpp
   Arch/ARM64.cpp
   Arch/ARM64Common.cpp
@@ -14,6 +14,7 @@ add_lld_library(lldMachO2
   Driver.cpp
   DriverUtils.cpp
   Dwarf.cpp
+  EhFrame.cpp
   ExportTrie.cpp
   ICF.cpp
   InputFiles.cpp
@@ -25,6 +26,7 @@ add_lld_library(lldMachO2
   OutputSection.cpp
   OutputSegment.cpp
   Relocations.cpp
+  SectionPriorities.cpp
   SymbolTable.cpp
   Symbols.cpp
   SyntheticSections.cpp
@@ -36,8 +38,10 @@ add_lld_library(lldMachO2
   ${LLVM_TARGETS_TO_BUILD}
   BinaryFormat
   BitReader
+  BitWriter
   Core
   DebugInfoDWARF
+  Demangle
   LTO
   MC
   ObjCARCOpts
@@ -45,6 +49,7 @@ add_lld_library(lldMachO2
   Option
   Passes
   Support
+  TargetParser
   TextAPI
 
   LINK_LIBS
@@ -58,5 +63,5 @@ add_lld_library(lldMachO2
   )
 
 if(LLVM_HAVE_LIBXAR)
-  target_link_libraries(lldMachO2 PRIVATE ${XAR_LIB})
+  target_link_libraries(lldMachO PRIVATE ${XAR_LIB})
 endif()
index 78590cf..cbd3a24 100644 (file)
@@ -13,8 +13,7 @@
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
-#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Memory.h"
+#include "lld/Common/CommonLinkerContext.h"
 #include "llvm/BinaryFormat/MachO.h"
 #include "llvm/Support/ScopedPrinter.h"
 #include "llvm/Support/TimeProfiler.h"
@@ -53,19 +52,19 @@ void ConcatOutputSection::addInput(ConcatInputSection *input) {
 //     multiple thunks to the same destination distributed throughout a large
 //     program so that all call sites can have one within range.
 //
-// The optimal approach is to mix islands for distinations within two hops,
+// The optimal approach is to mix islands for destinations within two hops,
 // and use thunks for destinations at greater distance. For now, we only
 // implement thunks. TODO: Adding support for branch islands!
 //
 // Internally -- as expressed in LLD's data structures -- a
-// branch-range-extension thunk comprises ...
+// branch-range-extension thunk consists of:
 //
-// (1) new Defined privateExtern symbol for the thunk named
+// (1) new Defined symbol for the thunk named
 //     <FUNCTION>.thunk.<SEQUENCE>, which references ...
 // (2) new InputSection, which contains ...
 // (3.1) new data for the instructions to load & branch to the far address +
 // (3.2) new Relocs on instructions to load the far address, which reference ...
-// (4.1) existing Defined extern symbol for the real function in __text, or
+// (4.1) existing Defined symbol for the real function in __text, or
 // (4.2) existing DylibSymbol for the real function in a dylib
 //
 // Nearly-optimal thunk-placement algorithm features:
@@ -85,15 +84,17 @@ void ConcatOutputSection::addInput(ConcatInputSection *input) {
 //   distant call sites might be unable to reach the same thunk, so multiple
 //   thunks are necessary to serve all call sites in a very large program. A
 //   thunkInfo stores state for all thunks associated with a particular
-//   function: (a) thunk symbol, (b) input section containing stub code, and
-//   (c) sequence number for the active thunk incarnation. When an old thunk
-//   goes out of range, we increment the sequence number and create a new
-//   thunk named <FUNCTION>.thunk.<SEQUENCE>.
+//   function:
+//     (a) thunk symbol
+//     (b) input section containing stub code, and
+//     (c) sequence number for the active thunk incarnation.
+//   When an old thunk goes out of range, we increment the sequence number and
+//   create a new thunk named <FUNCTION>.thunk.<SEQUENCE>.
 //
-// * A thunk incarnation comprises (a) private-extern Defined symbol pointing
-//   to (b) an InputSection holding machine instructions (similar to a MachO
-//   stub), and (c) Reloc(s) that reference the real function for fixing-up
-//   the stub code.
+// * A thunk consists of
+//     (a) a Defined symbol pointing to
+//     (b) an InputSection holding machine code (similar to a MachO stub), and
+//     (c) relocs referencing the real function for fixing up the stub code.
 //
 // * std::vector<InputSection *> MergedInputSection::thunks: A vector parallel
 //   to the inputs vector. We store new thunks via cheap vector append, rather
@@ -109,7 +110,7 @@ void ConcatOutputSection::addInput(ConcatInputSection *input) {
 //   thus, we place thunks at monotonically increasing addresses. Once a thunk
 //   is placed, it and all previous input-section addresses are final.
 //
-// * MergedInputSection::finalize() and MergedInputSection::writeTo() merge
+// * ConcatInputSection::finalize() and ConcatInputSection::writeTo() merge
 //   the inputs and thunks vectors (both ordered by ascending address), which
 //   is simple and cheap.
 
@@ -120,31 +121,31 @@ DenseMap<Symbol *, ThunkInfo> lld::macho::thunkMap;
 // instructions, whereas CISC (i.e., x86) generally doesn't. RISC only needs
 // thunks for programs so large that branch source & destination addresses
 // might differ more than the range of branch instruction(s).
-bool ConcatOutputSection::needsThunks() const {
+bool TextOutputSection::needsThunks() const {
   if (!target->usesThunks())
     return false;
   uint64_t isecAddr = addr;
-  for (InputSection *isec : inputs)
+  for (ConcatInputSection *isec : inputs)
     isecAddr = alignTo(isecAddr, isec->align) + isec->getSize();
-  if (isecAddr - addr + in.stubs->getSize() <= target->branchRange)
+  if (isecAddr - addr + in.stubs->getSize() <=
+      std::min(target->backwardBranchRange, target->forwardBranchRange))
     return false;
   // Yes, this program is large enough to need thunks.
-  for (InputSection *isec : inputs) {
+  for (ConcatInputSection *isec : inputs) {
     for (Reloc &r : isec->relocs) {
       if (!target->hasAttr(r.type, RelocAttrBits::BRANCH))
         continue;
       auto *sym = r.referent.get<Symbol *>();
       // Pre-populate the thunkMap and memoize call site counts for every
       // InputSection and ThunkInfo. We do this for the benefit of
-      // ConcatOutputSection::estimateStubsInRangeVA()
+      // estimateStubsInRangeVA().
       ThunkInfo &thunkInfo = thunkMap[sym];
       // Knowing ThunkInfo call site count will help us know whether or not we
       // might need to create more for this referent at the time we are
-      // estimating distance to __stubs in .
+      // estimating distance to __stubs in estimateStubsInRangeVA().
       ++thunkInfo.callSiteCount;
-      // Knowing InputSection call site count will help us avoid work on those
-      // that have no BRANCH relocs.
-      ++isec->callSiteCount;
+      // We can avoid work on InputSections that have no BRANCH relocs.
+      isec->hasCallSites = true;
     }
   }
   return true;
@@ -152,61 +153,69 @@ bool ConcatOutputSection::needsThunks() const {
 
 // Since __stubs is placed after __text, we must estimate the address
 // beyond which stubs are within range of a simple forward branch.
-uint64_t ConcatOutputSection::estimateStubsInRangeVA(size_t callIdx) const {
-  uint64_t branchRange = target->branchRange;
-  size_t endIdx = inputs.size();
-  ConcatInputSection *isec = inputs[callIdx];
-  uint64_t isecVA = isec->getVA();
-  // Tally the non-stub functions which still have call sites
-  // remaining to process, which yields the maximum number
-  // of thunks we might yet place.
+// This is called exactly once, when the last input section has been finalized.
+uint64_t TextOutputSection::estimateStubsInRangeVA(size_t callIdx) const {
+  // Tally the functions which still have call sites remaining to process,
+  // which yields the maximum number of thunks we might yet place.
   size_t maxPotentialThunks = 0;
   for (auto &tp : thunkMap) {
     ThunkInfo &ti = tp.second;
-    maxPotentialThunks +=
-        !tp.first->isInStubs() && ti.callSitesUsed < ti.callSiteCount;
+    // This overcounts: Only sections that are in forward jump range from the
+    // currently-active section get finalized, and all input sections are
+    // finalized when estimateStubsInRangeVA() is called. So only backward
+    // jumps will need thunks, but we count all jumps.
+    if (ti.callSitesUsed < ti.callSiteCount)
+      maxPotentialThunks += 1;
   }
   // Tally the total size of input sections remaining to process.
-  uint64_t isecEnd = isec->getVA();
-  for (size_t i = callIdx; i < endIdx; i++) {
+  uint64_t isecVA = inputs[callIdx]->getVA();
+  uint64_t isecEnd = isecVA;
+  for (size_t i = callIdx; i < inputs.size(); i++) {
     InputSection *isec = inputs[i];
     isecEnd = alignTo(isecEnd, isec->align) + isec->getSize();
   }
   // Estimate the address after which call sites can safely call stubs
   // directly rather than through intermediary thunks.
+  uint64_t forwardBranchRange = target->forwardBranchRange;
+  assert(isecEnd > forwardBranchRange &&
+         "should not run thunk insertion if all code fits in jump range");
+  assert(isecEnd - isecVA <= forwardBranchRange &&
+         "should only finalize sections in jump range");
   uint64_t stubsInRangeVA = isecEnd + maxPotentialThunks * target->thunkSize +
-                            in.stubs->getSize() - branchRange;
+                            in.stubs->getSize() - forwardBranchRange;
   log("thunks = " + std::to_string(thunkMap.size()) +
       ", potential = " + std::to_string(maxPotentialThunks) +
       ", stubs = " + std::to_string(in.stubs->getSize()) + ", isecVA = " +
-      to_hexString(isecVA) + ", threshold = " + to_hexString(stubsInRangeVA) +
-      ", isecEnd = " + to_hexString(isecEnd) +
-      ", tail = " + to_hexString(isecEnd - isecVA) +
-      ", slop = " + to_hexString(branchRange - (isecEnd - isecVA)));
+      utohexstr(isecVA) + ", threshold = " + utohexstr(stubsInRangeVA) +
+      ", isecEnd = " + utohexstr(isecEnd) +
+      ", tail = " + utohexstr(isecEnd - isecVA) +
+      ", slop = " + utohexstr(forwardBranchRange - (isecEnd - isecVA)));
   return stubsInRangeVA;
 }
 
-void ConcatOutputSection::finalize() {
-  uint64_t isecAddr = addr;
-  uint64_t isecFileOff = fileOff;
-  auto finalizeOne = [&](ConcatInputSection *isec) {
-    isecAddr = alignTo(isecAddr, isec->align);
-    isecFileOff = alignTo(isecFileOff, isec->align);
-    isec->outSecOff = isecAddr - addr;
-    isec->isFinal = true;
-    isecAddr += isec->getSize();
-    isecFileOff += isec->getFileSize();
-  };
+void ConcatOutputSection::finalizeOne(ConcatInputSection *isec) {
+  size = alignTo(size, isec->align);
+  fileSize = alignTo(fileSize, isec->align);
+  isec->outSecOff = size;
+  isec->isFinal = true;
+  size += isec->getSize();
+  fileSize += isec->getFileSize();
+}
+
+void ConcatOutputSection::finalizeContents() {
+  for (ConcatInputSection *isec : inputs)
+    finalizeOne(isec);
+}
 
+void TextOutputSection::finalize() {
   if (!needsThunks()) {
     for (ConcatInputSection *isec : inputs)
       finalizeOne(isec);
-    size = isecAddr - addr;
-    fileSize = isecFileOff - fileOff;
     return;
   }
 
-  uint64_t branchRange = target->branchRange;
+  uint64_t forwardBranchRange = target->forwardBranchRange;
+  uint64_t backwardBranchRange = target->backwardBranchRange;
   uint64_t stubsInRangeVA = TargetInfo::outOfRangeVA;
   size_t thunkSize = target->thunkSize;
   size_t relocCount = 0;
@@ -214,6 +223,11 @@ void ConcatOutputSection::finalize() {
   size_t thunkCallCount = 0;
   size_t thunkCount = 0;
 
+  // Walk all sections in order. Finalize all sections that are less than
+  // forwardBranchRange in front of it.
+  // isecVA is the address of the current section.
+  // addr + size is the start address of the first non-finalized section.
+
   // inputs[finalIdx] is for finalization (address-assignment)
   size_t finalIdx = 0;
   // Kick-off by ensuring that the first input section has an address
@@ -224,12 +238,22 @@ void ConcatOutputSection::finalize() {
     ConcatInputSection *isec = inputs[callIdx];
     assert(isec->isFinal);
     uint64_t isecVA = isec->getVA();
-    // Assign addresses up-to the forward branch-range limit
-    while (finalIdx < endIdx &&
-           isecAddr + inputs[finalIdx]->getSize() < isecVA + branchRange)
+
+    // Assign addresses up-to the forward branch-range limit.
+    // Every call instruction needs a small number of bytes (on Arm64: 4),
+    // and each inserted thunk needs a slightly larger number of bytes
+    // (on Arm64: 12). If a section starts with a branch instruction and
+    // contains several branch instructions in succession, then the distance
+    // from the current position to the position where the thunks are inserted
+    // grows. So leave room for a bunch of thunks.
+    unsigned slop = 1024 * thunkSize;
+    while (finalIdx < endIdx && addr + size + inputs[finalIdx]->getSize() <
+                                    isecVA + forwardBranchRange - slop)
       finalizeOne(inputs[finalIdx++]);
-    if (isec->callSiteCount == 0)
+
+    if (!isec->hasCallSites)
       continue;
+
     if (finalIdx == endIdx && stubsInRangeVA == TargetInfo::outOfRangeVA) {
       // When we have finalized all input sections, __stubs (destined
       // to follow __text) comes within range of forward branches and
@@ -254,20 +278,22 @@ void ConcatOutputSection::finalize() {
       ++callSiteCount;
       // Calculate branch reachability boundaries
       uint64_t callVA = isecVA + r.offset;
-      uint64_t lowVA = branchRange < callVA ? callVA - branchRange : 0;
-      uint64_t highVA = callVA + branchRange;
+      uint64_t lowVA =
+          backwardBranchRange < callVA ? callVA - backwardBranchRange : 0;
+      uint64_t highVA = callVA + forwardBranchRange;
       // Calculate our call referent address
       auto *funcSym = r.referent.get<Symbol *>();
       ThunkInfo &thunkInfo = thunkMap[funcSym];
       // The referent is not reachable, so we need to use a thunk ...
       if (funcSym->isInStubs() && callVA >= stubsInRangeVA) {
+        assert(callVA != TargetInfo::outOfRangeVA);
         // ... Oh, wait! We are close enough to the end that __stubs
         // are now within range of a simple forward branch.
         continue;
       }
       uint64_t funcVA = funcSym->resolveBranchVA();
       ++thunkInfo.callSitesUsed;
-      if (lowVA < funcVA && funcVA < highVA) {
+      if (lowVA <= funcVA && funcVA <= highVA) {
         // The referent is reachable with a simple call instruction.
         continue;
       }
@@ -276,40 +302,51 @@ void ConcatOutputSection::finalize() {
       // If an existing thunk is reachable, use it ...
       if (thunkInfo.sym) {
         uint64_t thunkVA = thunkInfo.isec->getVA();
-        if (lowVA < thunkVA && thunkVA < highVA) {
+        if (lowVA <= thunkVA && thunkVA <= highVA) {
           r.referent = thunkInfo.sym;
           continue;
         }
       }
-      // ... otherwise, create a new thunk
-      if (isecAddr > highVA) {
-        // When there is small-to-no margin between highVA and
-        // isecAddr and the distance between subsequent call sites is
-        // smaller than thunkSize, then a new thunk can go out of
-        // range.  Fix by unfinalizing inputs[finalIdx] to reduce the
-        // distance between callVA and highVA, then shift some thunks
-        // to occupy address-space formerly occupied by the
-        // unfinalized inputs[finalIdx].
+      // ... otherwise, create a new thunk.
+      if (addr + size > highVA) {
+        // There were too many consecutive branch instructions for `slop`
+        // above. If you hit this: For the current algorithm, just bumping up
+        // slop above and trying again is probably simplest. (See also PR51578
+        // comment 5).
         fatal(Twine(__FUNCTION__) + ": FIXME: thunk range overrun");
       }
       thunkInfo.isec =
-          make<ConcatInputSection>(isec->getSegName(), isec->getName());
+          makeSyntheticInputSection(isec->getSegName(), isec->getName());
       thunkInfo.isec->parent = this;
-      StringRef thunkName = saver.save(funcSym->getName() + ".thunk." +
-                                       std::to_string(thunkInfo.sequence++));
-      r.referent = thunkInfo.sym = symtab->addDefined(
-          thunkName, /*file=*/nullptr, thunkInfo.isec, /*value=*/0,
-          /*size=*/thunkSize, /*isWeakDef=*/false, /*isPrivateExtern=*/true,
-          /*isThumb=*/false, /*isReferencedDynamically=*/false,
-          /*noDeadStrip=*/false);
+
+      // This code runs after dead code removal. Need to set the `live` bit
+      // on the thunk isec so that asserts that check that only live sections
+      // get written are happy.
+      thunkInfo.isec->live = true;
+
+      StringRef thunkName = saver().save(funcSym->getName() + ".thunk." +
+                                         std::to_string(thunkInfo.sequence++));
+      if (!isa<Defined>(funcSym) || cast<Defined>(funcSym)->isExternal()) {
+        r.referent = thunkInfo.sym = symtab->addDefined(
+            thunkName, /*file=*/nullptr, thunkInfo.isec, /*value=*/0, thunkSize,
+            /*isWeakDef=*/false, /*isPrivateExtern=*/true,
+            /*isThumb=*/false, /*isReferencedDynamically=*/false,
+            /*noDeadStrip=*/false, /*isWeakDefCanBeHidden=*/false);
+      } else {
+        r.referent = thunkInfo.sym = make<Defined>(
+            thunkName, /*file=*/nullptr, thunkInfo.isec, /*value=*/0, thunkSize,
+            /*isWeakDef=*/false, /*isExternal=*/false, /*isPrivateExtern=*/true,
+            /*includeInSymtab=*/true, /*isThumb=*/false,
+            /*isReferencedDynamically=*/false, /*noDeadStrip=*/false,
+            /*isWeakDefCanBeHidden=*/false);
+      }
+      thunkInfo.sym->used = true;
       target->populateThunk(thunkInfo.isec, funcSym);
       finalizeOne(thunkInfo.isec);
       thunks.push_back(thunkInfo.isec);
       ++thunkCount;
     }
   }
-  size = isecAddr - addr;
-  fileSize = isecFileOff - fileOff;
 
   log("thunks for " + parent->name + "," + name +
       ": funcs = " + std::to_string(thunkMap.size()) +
@@ -320,11 +357,16 @@ void ConcatOutputSection::finalize() {
 }
 
 void ConcatOutputSection::writeTo(uint8_t *buf) const {
+  for (ConcatInputSection *isec : inputs)
+    isec->writeTo(buf + isec->outSecOff);
+}
+
+void TextOutputSection::writeTo(uint8_t *buf) const {
   // Merge input sections from thunk & ordinary vectors
   size_t i = 0, ie = inputs.size();
   size_t t = 0, te = thunks.size();
   while (i < ie || t < te) {
-    while (i < ie && (t == te || inputs[i]->getSize() == 0 ||
+    while (i < ie && (t == te || inputs[i]->empty() ||
                       inputs[i]->outSecOff < thunks[t]->outSecOff)) {
       inputs[i]->writeTo(buf + inputs[i]->outSecOff);
       ++i;
@@ -364,8 +406,14 @@ ConcatOutputSection *
 ConcatOutputSection::getOrCreateForInput(const InputSection *isec) {
   NamePair names = maybeRenameSection({isec->getSegName(), isec->getName()});
   ConcatOutputSection *&osec = concatOutputSections[names];
-  if (!osec)
-    osec = make<ConcatOutputSection>(names.second);
+  if (!osec) {
+    if (isec->getSegName() == segment_names::text &&
+        isec->getName() != section_names::gccExceptTab &&
+        isec->getName() != section_names::ehFrame)
+      osec = make<TextOutputSection>(names.second);
+    else
+      osec = make<ConcatOutputSection>(names.second);
+  }
   return osec;
 }
 
index ec3d6bf..9af661d 100644 (file)
@@ -6,8 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLD_MACHO_MERGED_OUTPUT_SECTION_H
-#define LLD_MACHO_MERGED_OUTPUT_SECTION_H
+#ifndef LLD_MACHO_CONCAT_OUTPUT_SECTION_H
+#define LLD_MACHO_CONCAT_OUTPUT_SECTION_H
 
 #include "InputSection.h"
 #include "OutputSection.h"
@@ -15,8 +15,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/MapVector.h"
 
-namespace lld {
-namespace macho {
+namespace lld::macho {
 
 class Defined;
 
@@ -24,7 +23,7 @@ class Defined;
 // files that are labeled with the same segment and section name. This class
 // contains all such sections and writes the data from each section sequentially
 // in the final binary.
-class ConcatOutputSection final : public OutputSection {
+class ConcatOutputSection : public OutputSection {
 public:
   explicit ConcatOutputSection(StringRef name)
       : OutputSection(ConcatKind, name) {}
@@ -37,27 +36,46 @@ public:
   uint64_t getSize() const override { return size; }
   uint64_t getFileSize() const override { return fileSize; }
 
-  void addInput(ConcatInputSection *input);
-  void finalize() override;
-  bool needsThunks() const;
-  uint64_t estimateStubsInRangeVA(size_t callIdx) const;
+  // Assign values to InputSection::outSecOff. In contrast to TextOutputSection,
+  // which does this in its implementation of `finalize()`, we can do this
+  // without `finalize()`'s sequential guarantees detailed in the block comment
+  // of `OutputSection::finalize()`.
+  virtual void finalizeContents();
 
+  void addInput(ConcatInputSection *input);
   void writeTo(uint8_t *buf) const override;
 
-  std::vector<ConcatInputSection *> inputs;
-  std::vector<ConcatInputSection *> thunks;
-
   static bool classof(const OutputSection *sec) {
     return sec->kind() == ConcatKind;
   }
 
   static ConcatOutputSection *getOrCreateForInput(const InputSection *);
 
-private:
-  void finalizeFlags(InputSection *input);
+  std::vector<ConcatInputSection *> inputs;
 
+protected:
   size_t size = 0;
   uint64_t fileSize = 0;
+  void finalizeOne(ConcatInputSection *);
+
+private:
+  void finalizeFlags(InputSection *input);
+};
+
+// ConcatOutputSections that contain code (text) require special handling to
+// support thunk insertion.
+class TextOutputSection : public ConcatOutputSection {
+public:
+  explicit TextOutputSection(StringRef name) : ConcatOutputSection(name) {}
+  void finalizeContents() override {}
+  void finalize() override;
+  bool needsThunks() const;
+  void writeTo(uint8_t *buf) const override;
+
+private:
+  uint64_t estimateStubsInRangeVA(size_t callIdx) const;
+
+  std::vector<ConcatInputSection *> thunks;
 };
 
 // We maintain one ThunkInfo per real function.
@@ -90,7 +108,6 @@ extern llvm::MapVector<NamePair, ConcatOutputSection *> concatOutputSections;
 
 extern llvm::DenseMap<Symbol *, ThunkInfo> thunkMap;
 
-} // namespace macho
-} // namespace lld
+} // namespace lld::macho
 
 #endif
index 0f47015..220fb99 100644 (file)
@@ -12,6 +12,8 @@
 #include "llvm/ADT/CachedHashString.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSet.h"
 #include "llvm/BinaryFormat/MachO.h"
@@ -27,8 +29,8 @@
 namespace lld {
 namespace macho {
 
+class InputSection;
 class Symbol;
-struct SymbolPriorityEntry;
 
 using NamePair = std::pair<llvm::StringRef, llvm::StringRef>;
 using SectionRenameMap = llvm::DenseMap<NamePair, NamePair>;
@@ -42,8 +44,8 @@ struct PlatformInfo {
 
 inline uint32_t encodeVersion(const llvm::VersionTuple &version) {
   return ((version.getMajor() << 020) |
-          (version.getMinor().getValueOr(0) << 010) |
-          version.getSubminor().getValueOr(0));
+          (version.getMinor().value_or(0) << 010) |
+          version.getSubminor().value_or(0));
 }
 
 enum class NamespaceKind {
@@ -66,6 +68,11 @@ enum class ICFLevel {
   all,
 };
 
+enum class ObjCStubsMode {
+  fast,
+  small,
+};
+
 struct SectionAlign {
   llvm::StringRef segName;
   llvm::StringRef sectName;
@@ -93,6 +100,13 @@ public:
   bool match(llvm::StringRef symbolName) const;
 };
 
+enum class SymtabPresence {
+  All,
+  None,
+  SelectivelyIncluded,
+  SelectivelyExcluded,
+};
+
 struct Configuration {
   Symbol *entry = nullptr;
   bool hasReexports = false;
@@ -101,12 +115,11 @@ struct Configuration {
   bool archMultiple = false;
   bool exportDynamic = false;
   bool forceLoadObjC = false;
-  bool forceLoadSwift = false;
+  bool forceLoadSwift = false; // Only applies to LC_LINKER_OPTIONs.
   bool staticLink = false;
   bool implicitDylibs = false;
   bool isPic = false;
   bool headerPadMaxInstallNames = false;
-  bool ltoNewPassManager = LLVM_ENABLE_NEW_PASS_MANAGER;
   bool markDeadStrippableDylib = false;
   bool printDylibSearch = false;
   bool printEachFile = false;
@@ -118,9 +131,19 @@ struct Configuration {
   bool emitBitcodeBundle = false;
   bool emitDataInCodeInfo = false;
   bool emitEncryptionInfo = false;
+  bool emitInitOffsets = false;
+  bool emitChainedFixups = false;
+  bool thinLTOEmitImportsFiles;
+  bool thinLTOEmitIndexFiles;
+  bool thinLTOIndexOnly;
   bool timeTraceEnabled = false;
   bool dataConst = false;
-  bool dedupLiterals = true;
+  bool dedupStrings = true;
+  bool deadStripDuplicates = false;
+  bool omitDebugInfo = false;
+  bool warnDylibInstallName = false;
+  bool ignoreOptimizationHints = false;
+  bool forceExactCpuSubtypeMatch = false;
   uint32_t headerPad;
   uint32_t dylibCompatibilityVersion = 0;
   uint32_t dylibCurrentVersion = 0;
@@ -144,19 +167,32 @@ struct Configuration {
   uint32_t ltoo = 2;
   llvm::CachePruningPolicy thinLTOCachePolicy;
   llvm::StringRef thinLTOCacheDir;
+  llvm::StringRef thinLTOIndexOnlyArg;
+  std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
+  std::pair<llvm::StringRef, llvm::StringRef> thinLTOPrefixReplace;
   bool deadStripDylibs = false;
   bool demangle = false;
   bool deadStrip = false;
+  bool errorForArchMismatch = false;
+  bool ignoreAutoLink = false;
+  // ld64 allows invalid auto link options as long as the link succeeds. LLD
+  // does not, but there are cases in the wild where the invalid linker options
+  // exist. This allows users to ignore the specific invalid options in the case
+  // they can't easily fix them.
+  llvm::StringSet<> ignoreAutoLinkOptions;
+  bool strictAutoLink = false;
   PlatformInfo platformInfo;
+  std::optional<PlatformInfo> secondaryPlatformInfo;
   NamespaceKind namespaceKind = NamespaceKind::twolevel;
   UndefinedSymbolTreatment undefinedSymbolTreatment =
       UndefinedSymbolTreatment::error;
   ICFLevel icfLevel = ICFLevel::none;
+  ObjCStubsMode objcStubsMode = ObjCStubsMode::fast;
   llvm::MachO::HeaderFileType outputType;
   std::vector<llvm::StringRef> systemLibraryRoots;
   std::vector<llvm::StringRef> librarySearchPaths;
   std::vector<llvm::StringRef> frameworkSearchPaths;
-  std::vector<llvm::StringRef> runtimePaths;
+  llvm::SmallVector<llvm::StringRef, 0> runtimePaths;
   std::vector<std::string> astPaths;
   std::vector<Symbol *> explicitUndefineds;
   llvm::StringSet<> explicitDynamicLookups;
@@ -165,37 +201,37 @@ struct Configuration {
   std::vector<SectionAlign> sectionAlignments;
   std::vector<SegmentProtection> segmentProtections;
 
-  llvm::DenseMap<llvm::StringRef, SymbolPriorityEntry> priorities;
+  bool callGraphProfileSort = false;
+  llvm::StringRef printSymbolOrder;
+
   SectionRenameMap sectionRenameMap;
   SegmentRenameMap segmentRenameMap;
 
+  bool hasExplicitExports = false;
   SymbolPatterns exportedSymbols;
   SymbolPatterns unexportedSymbols;
+  SymbolPatterns whyLive;
+
+  std::vector<std::pair<llvm::StringRef, llvm::StringRef>> aliasedSymbols;
 
-  bool zeroModTime = false;
+  SymtabPresence localSymbolsPresence = SymtabPresence::All;
+  SymbolPatterns localSymbolPatterns;
+  llvm::SmallVector<llvm::StringRef, 0> mllvmOpts;
+
+  bool zeroModTime = true;
+
+  llvm::StringRef osoPrefix;
+
+  std::vector<llvm::StringRef> dyldEnvs;
 
   llvm::MachO::Architecture arch() const { return platformInfo.target.Arch; }
 
-  llvm::MachO::PlatformKind platform() const {
+  llvm::MachO::PlatformType platform() const {
     return platformInfo.target.Platform;
   }
 };
 
-// The symbol with the highest priority should be ordered first in the output
-// section (modulo input section contiguity constraints). Using priority
-// (highest first) instead of order (lowest first) has the convenient property
-// that the default-constructed zero priority -- for symbols/sections without a
-// user-defined order -- naturally ends up putting them at the end of the
-// output.
-struct SymbolPriorityEntry {
-  // The priority given to a matching symbol, regardless of which object file
-  // it originated from.
-  size_t anyObjectFile = 0;
-  // The priority given to a matching symbol from a particular object file.
-  llvm::DenseMap<llvm::StringRef, size_t> objectFiles;
-};
-
-extern Configuration *config;
+extern std::unique_ptr<Configuration> config;
 
 } // namespace macho
 } // namespace lld
index a8c11b6..fe8540e 100644 (file)
@@ -15,6 +15,7 @@
 #include "ObjC.h"
 #include "OutputSection.h"
 #include "OutputSegment.h"
+#include "SectionPriorities.h"
 #include "SymbolTable.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
@@ -23,6 +24,7 @@
 #include "Writer.h"
 
 #include "lld/Common/Args.h"
+#include "lld/Common/CommonLinkerContext.h"
 #include "lld/Common/Driver.h"
 #include "lld/Common/ErrorHandler.h"
 #include "lld/Common/LLVM.h"
@@ -59,8 +61,8 @@ using namespace llvm::sys;
 using namespace lld;
 using namespace lld::macho;
 
-Configuration *macho::config;
-DependencyTracker *macho::depTracker;
+std::unique_ptr<Configuration> macho::config;
+std::unique_ptr<DependencyTracker> macho::depTracker;
 
 static HeaderFileType getOutputType(const InputArgList &args) {
   // TODO: -r, -dylinker, -preload...
@@ -80,19 +82,39 @@ static HeaderFileType getOutputType(const InputArgList &args) {
   }
 }
 
-static Optional<StringRef> findLibrary(StringRef name) {
-  if (config->searchDylibsFirst) {
-    if (Optional<StringRef> path = findPathCombination(
-            "lib" + name, config->librarySearchPaths, {".tbd", ".dylib"}))
-      return path;
+static DenseMap<CachedHashStringRef, StringRef> resolvedLibraries;
+static std::optional<StringRef> findLibrary(StringRef name) {
+  CachedHashStringRef key(name);
+  auto entry = resolvedLibraries.find(key);
+  if (entry != resolvedLibraries.end())
+    return entry->second;
+
+  auto doFind = [&] {
+    if (config->searchDylibsFirst) {
+      if (std::optional<StringRef> path = findPathCombination(
+              "lib" + name, config->librarySearchPaths, {".tbd", ".dylib"}))
+        return path;
+      return findPathCombination("lib" + name, config->librarySearchPaths,
+                                 {".a"});
+    }
     return findPathCombination("lib" + name, config->librarySearchPaths,
-                               {".a"});
-  }
-  return findPathCombination("lib" + name, config->librarySearchPaths,
-                             {".tbd", ".dylib", ".a"});
+                               {".tbd", ".dylib", ".a"});
+  };
+
+  std::optional<StringRef> path = doFind();
+  if (path)
+    resolvedLibraries[key] = *path;
+
+  return path;
 }
 
-static Optional<std::string> findFramework(StringRef name) {
+static DenseMap<CachedHashStringRef, StringRef> resolvedFrameworks;
+static std::optional<StringRef> findFramework(StringRef name) {
+  CachedHashStringRef key(name);
+  auto entry = resolvedFrameworks.find(key);
+  if (entry != resolvedFrameworks.end())
+    return entry->second;
+
   SmallString<260> symlink;
   StringRef suffix;
   std::tie(name, suffix) = name.split(",");
@@ -108,13 +130,13 @@ static Optional<std::string> findFramework(StringRef name) {
         // only append suffix if realpath() succeeds
         Twine suffixed = location + suffix;
         if (fs::exists(suffixed))
-          return suffixed.str();
+          return resolvedFrameworks[key] = saver().save(suffixed.str());
       }
       // Suffix lookup failed, fall through to the no-suffix case.
     }
 
-    if (Optional<std::string> path = resolveDylibPath(symlink))
-      return path;
+    if (std::optional<StringRef> path = resolveDylibPath(symlink.str()))
+      return resolvedFrameworks[key] = *path;
   }
   return {};
 }
@@ -145,7 +167,7 @@ getSearchPaths(unsigned optionCode, InputArgList &args,
         path::append(buffer, path);
         // Do not warn about paths that are computed via the syslib roots
         if (fs::is_directory(buffer)) {
-          paths.push_back(saver.save(buffer.str()));
+          paths.push_back(saver().save(buffer.str()));
           found = true;
         }
       }
@@ -163,7 +185,7 @@ getSearchPaths(unsigned optionCode, InputArgList &args,
       SmallString<261> buffer(root);
       path::append(buffer, path);
       if (fs::is_directory(buffer))
-        paths.push_back(saver.save(buffer.str()));
+        paths.push_back(saver().save(buffer.str()));
     }
   }
   return paths;
@@ -174,7 +196,7 @@ static std::vector<StringRef> getSystemLibraryRoots(InputArgList &args) {
   for (const Arg *arg : args.filtered(OPT_syslibroot))
     roots.push_back(arg->getValue());
   // NOTE: the final `-syslibroot` being `/` will ignore all roots
-  if (roots.size() && roots.back() == "/")
+  if (!roots.empty() && roots.back() == "/")
     roots.clear();
   // NOTE: roots can never be empty - add an empty root to simplify the library
   // and framework search path computation.
@@ -203,10 +225,12 @@ static llvm::CachePruningPolicy getLTOCachePolicy(InputArgList &args) {
     val.toVector(ltoPolicy);
   };
   for (const Arg *arg :
-       args.filtered(OPT_thinlto_cache_policy, OPT_prune_interval_lto,
+       args.filtered(OPT_thinlto_cache_policy_eq, OPT_prune_interval_lto,
                      OPT_prune_after_lto, OPT_max_relative_cache_size_lto)) {
     switch (arg->getOption().getID()) {
-    case OPT_thinlto_cache_policy: add(arg->getValue()); break;
+    case OPT_thinlto_cache_policy_eq:
+      add(arg->getValue());
+      break;
     case OPT_prune_interval_lto:
       if (!strcmp("-1", arg->getValue()))
         add("prune_interval=87600h"); // 10 years
@@ -224,54 +248,28 @@ static llvm::CachePruningPolicy getLTOCachePolicy(InputArgList &args) {
   return CHECK(parseCachePruningPolicy(ltoPolicy), "invalid LTO cache policy");
 }
 
-namespace {
-struct ArchiveMember {
-  MemoryBufferRef mbref;
-  uint32_t modTime;
-  uint64_t offsetInArchive;
+// What caused a given library to be loaded. Only relevant for archives.
+// Note that this does not tell us *how* we should load the library, i.e.
+// whether we should do it lazily or eagerly (AKA force loading). The "how" is
+// decided within addFile().
+enum class LoadType {
+  CommandLine,      // Library was passed as a regular CLI argument
+  CommandLineForce, // Library was passed via `-force_load`
+  LCLinkerOption,   // Library was passed via LC_LINKER_OPTIONS
+};
+
+struct ArchiveFileInfo {
+  ArchiveFile *file;
+  bool isCommandLineLoad;
 };
-} // namespace
-
-// Returns slices of MB by parsing MB as an archive file.
-// Each slice consists of a member file in the archive.
-static std::vector<ArchiveMember> getArchiveMembers(MemoryBufferRef mb) {
-  std::unique_ptr<Archive> file =
-      CHECK(Archive::create(mb),
-            mb.getBufferIdentifier() + ": failed to parse archive");
-  Archive *archive = file.get();
-  make<std::unique_ptr<Archive>>(std::move(file)); // take ownership
-
-  std::vector<ArchiveMember> v;
-  Error err = Error::success();
-
-  // Thin archives refer to .o files, so --reproduce needs the .o files too.
-  bool addToTar = archive->isThin() && tar;
-
-  for (const Archive::Child &c : archive->children(err)) {
-    MemoryBufferRef mbref =
-        CHECK(c.getMemoryBufferRef(),
-              mb.getBufferIdentifier() +
-                  ": could not get the buffer for a child of the archive");
-    if (addToTar)
-      tar->append(relativeToRoot(check(c.getFullName())), mbref.getBuffer());
-    uint32_t modTime = toTimeT(
-        CHECK(c.getLastModified(), mb.getBufferIdentifier() +
-                                       ": could not get the modification "
-                                       "time for a child of the archive"));
-    v.push_back({mbref, modTime, c.getChildOffset()});
-  }
-  if (err)
-    fatal(mb.getBufferIdentifier() +
-          ": Archive::children failed: " + toString(std::move(err)));
-
-  return v;
-}
 
-static DenseMap<StringRef, ArchiveFile *> loadedArchives;
+static DenseMap<StringRef, ArchiveFileInfo> loadedArchives;
 
-static InputFile *addFile(StringRef path, bool forceLoadArchive,
-                          bool isExplicit = true, bool isBundleLoader = false) {
-  Optional<MemoryBufferRef> buffer = readFile(path);
+static InputFile *addFile(StringRef path, LoadType loadType,
+                          bool isLazy = false, bool isExplicit = true,
+                          bool isBundleLoader = false,
+                          bool isForceHidden = false) {
+  std::optional<MemoryBufferRef> buffer = readFile(path);
   if (!buffer)
     return nullptr;
   MemoryBufferRef mbref = *buffer;
@@ -280,73 +278,101 @@ static InputFile *addFile(StringRef path, bool forceLoadArchive,
   file_magic magic = identify_magic(mbref.getBuffer());
   switch (magic) {
   case file_magic::archive: {
+    bool isCommandLineLoad = loadType != LoadType::LCLinkerOption;
     // Avoid loading archives twice. If the archives are being force-loaded,
     // loading them twice would create duplicate symbol errors. In the
     // non-force-loading case, this is just a minor performance optimization.
     // We don't take a reference to cachedFile here because the
     // loadArchiveMember() call below may recursively call addFile() and
     // invalidate this reference.
-    if (ArchiveFile *cachedFile = loadedArchives[path])
-      return cachedFile;
-
-    std::unique_ptr<object::Archive> file = CHECK(
-        object::Archive::create(mbref), path + ": failed to parse archive");
-
-    if (!file->isEmpty() && !file->hasSymbolTable())
-      error(path + ": archive has no index; run ranlib to add one");
-
-    if (config->allLoad || forceLoadArchive) {
-      if (Optional<MemoryBufferRef> buffer = readFile(path)) {
-        for (const ArchiveMember &member : getArchiveMembers(*buffer)) {
-          if (Optional<InputFile *> file = loadArchiveMember(
-                  member.mbref, member.modTime, path, /*objCOnly=*/false,
-                  member.offsetInArchive)) {
-            inputFiles.insert(*file);
-            printArchiveMemberLoad(
-                (forceLoadArchive ? "-force_load" : "-all_load"),
-                inputFiles.back());
+    auto entry = loadedArchives.find(path);
+
+    ArchiveFile *file;
+    if (entry == loadedArchives.end()) {
+      // No cached archive, we need to create a new one
+      std::unique_ptr<object::Archive> archive = CHECK(
+          object::Archive::create(mbref), path + ": failed to parse archive");
+
+      if (!archive->isEmpty() && !archive->hasSymbolTable())
+        error(path + ": archive has no index; run ranlib to add one");
+      file = make<ArchiveFile>(std::move(archive), isForceHidden);
+    } else {
+      file = entry->second.file;
+      // Command-line loads take precedence. If file is previously loaded via
+      // command line, or is loaded via LC_LINKER_OPTION and being loaded via
+      // LC_LINKER_OPTION again, using the cached archive is enough.
+      if (entry->second.isCommandLineLoad || !isCommandLineLoad)
+        return file;
+    }
+
+    bool isLCLinkerForceLoad = loadType == LoadType::LCLinkerOption &&
+                               config->forceLoadSwift &&
+                               path::filename(path).startswith("libswift");
+    if ((isCommandLineLoad && config->allLoad) ||
+        loadType == LoadType::CommandLineForce || isLCLinkerForceLoad) {
+      if (std::optional<MemoryBufferRef> buffer = readFile(path)) {
+        Error e = Error::success();
+        for (const object::Archive::Child &c : file->getArchive().children(e)) {
+          StringRef reason;
+          switch (loadType) {
+            case LoadType::LCLinkerOption:
+              reason = "LC_LINKER_OPTION";
+              break;
+            case LoadType::CommandLineForce:
+              reason = "-force_load";
+              break;
+            case LoadType::CommandLine:
+              reason = "-all_load";
+              break;
           }
+          if (Error e = file->fetch(c, reason))
+            error(toString(file) + ": " + reason +
+                  " failed to load archive member: " + toString(std::move(e)));
         }
+        if (e)
+          error(toString(file) +
+                ": Archive::children failed: " + toString(std::move(e)));
       }
-    } else if (config->forceLoadObjC) {
-      for (const object::Archive::Symbol &sym : file->symbols())
+    } else if (isCommandLineLoad && config->forceLoadObjC) {
+      for (const object::Archive::Symbol &sym : file->getArchive().symbols())
         if (sym.getName().startswith(objc::klass))
-          symtab->addUndefined(sym.getName(), /*file=*/nullptr,
-                               /*isWeakRef=*/false);
+          file->fetch(sym);
 
       // TODO: no need to look for ObjC sections for a given archive member if
-      // we already found that it contains an ObjC symbol. We should also
-      // consider creating a LazyObjFile class in order to avoid double-loading
-      // these files here and below (as part of the ArchiveFile).
-      if (Optional<MemoryBufferRef> buffer = readFile(path)) {
-        for (const ArchiveMember &member : getArchiveMembers(*buffer)) {
-          if (Optional<InputFile *> file = loadArchiveMember(
-                  member.mbref, member.modTime, path, /*objCOnly=*/true,
-                  member.offsetInArchive)) {
-            inputFiles.insert(*file);
-            printArchiveMemberLoad("-ObjC", inputFiles.back());
-          }
+      // we already found that it contains an ObjC symbol.
+      if (std::optional<MemoryBufferRef> buffer = readFile(path)) {
+        Error e = Error::success();
+        for (const object::Archive::Child &c : file->getArchive().children(e)) {
+          Expected<MemoryBufferRef> mb = c.getMemoryBufferRef();
+          if (!mb || !hasObjCSection(*mb))
+            continue;
+          if (Error e = file->fetch(c, "-ObjC"))
+            error(toString(file) + ": -ObjC failed to load archive member: " +
+                  toString(std::move(e)));
         }
+        if (e)
+          error(toString(file) +
+                ": Archive::children failed: " + toString(std::move(e)));
       }
     }
 
-    newFile = loadedArchives[path] = make<ArchiveFile>(std::move(file));
+    file->addLazySymbols();
+    loadedArchives[path] = ArchiveFileInfo{file, isCommandLineLoad};
+    newFile = file;
     break;
   }
   case file_magic::macho_object:
-    newFile = make<ObjFile>(mbref, getModTime(path), "");
+    newFile = make<ObjFile>(mbref, getModTime(path), "", isLazy);
     break;
   case file_magic::macho_dynamically_linked_shared_lib:
   case file_magic::macho_dynamically_linked_shared_lib_stub:
   case file_magic::tapi_file:
-    if (DylibFile *dylibFile = loadDylib(mbref)) {
-      if (isExplicit)
-        dylibFile->explicitlyLinked = true;
+    if (DylibFile *dylibFile =
+            loadDylib(mbref, nullptr, /*isBundleLoader=*/false, isExplicit))
       newFile = dylibFile;
-    }
     break;
   case file_magic::bitcode:
-    newFile = make<BitcodeFile>(mbref, "", 0);
+    newFile = make<BitcodeFile>(mbref, "", 0, isLazy);
     break;
   case file_magic::macho_executable:
   case file_magic::macho_bundle:
@@ -361,20 +387,34 @@ static InputFile *addFile(StringRef path, bool forceLoadArchive,
     error(path + ": unhandled file type");
   }
   if (newFile && !isa<DylibFile>(newFile)) {
+    if ((isa<ObjFile>(newFile) || isa<BitcodeFile>(newFile)) && newFile->lazy &&
+        config->forceLoadObjC) {
+      for (Symbol *sym : newFile->symbols)
+        if (sym && sym->getName().startswith(objc::klass)) {
+          extract(*newFile, "-ObjC");
+          break;
+        }
+      if (newFile->lazy && hasObjCSection(mbref))
+        extract(*newFile, "-ObjC");
+    }
+
     // printArchiveMemberLoad() prints both .a and .o names, so no need to
-    // print the .a name here.
-    if (config->printEachFile && magic != file_magic::archive)
+    // print the .a name here. Similarly skip lazy files.
+    if (config->printEachFile && magic != file_magic::archive && !isLazy)
       message(toString(newFile));
     inputFiles.insert(newFile);
   }
   return newFile;
 }
 
+static std::vector<StringRef> missingAutolinkWarnings;
 static void addLibrary(StringRef name, bool isNeeded, bool isWeak,
-                       bool isReexport, bool isExplicit, bool forceLoad) {
-  if (Optional<StringRef> path = findLibrary(name)) {
+                       bool isReexport, bool isHidden, bool isExplicit,
+                       LoadType loadType, InputFile *originFile = nullptr) {
+  if (std::optional<StringRef> path = findLibrary(name)) {
     if (auto *dylibFile = dyn_cast_or_null<DylibFile>(
-            addFile(*path, forceLoad, isExplicit))) {
+            addFile(*path, loadType, /*isLazy=*/false, isExplicit,
+                    /*isBundleLoader=*/false, isHidden))) {
       if (isNeeded)
         dylibFile->forceNeeded = true;
       if (isWeak)
@@ -386,14 +426,27 @@ static void addLibrary(StringRef name, bool isNeeded, bool isWeak,
     }
     return;
   }
+  if (loadType == LoadType::LCLinkerOption) {
+    assert(originFile);
+    missingAutolinkWarnings.push_back(
+        saver().save(toString(originFile) +
+                     ": auto-linked library not found for -l" + name));
+    return;
+  }
   error("library not found for -l" + name);
 }
 
+static DenseSet<StringRef> loadedObjectFrameworks;
 static void addFramework(StringRef name, bool isNeeded, bool isWeak,
-                         bool isReexport, bool isExplicit) {
-  if (Optional<std::string> path = findFramework(name)) {
-    if (auto *dylibFile = dyn_cast_or_null<DylibFile>(
-            addFile(*path, /*forceLoadArchive=*/false, isExplicit))) {
+                         bool isReexport, bool isExplicit, LoadType loadType,
+                         InputFile *originFile = nullptr) {
+  if (std::optional<StringRef> path = findFramework(name)) {
+    if (loadedObjectFrameworks.contains(*path))
+      return;
+
+    InputFile *file =
+        addFile(*path, loadType, /*isLazy=*/false, isExplicit, false);
+    if (auto *dylibFile = dyn_cast_or_null<DylibFile>(file)) {
       if (isNeeded)
         dylibFile->forceNeeded = true;
       if (isWeak)
@@ -402,16 +455,35 @@ static void addFramework(StringRef name, bool isNeeded, bool isWeak,
         config->hasReexports = true;
         dylibFile->reexport = true;
       }
+    } else if (isa_and_nonnull<ObjFile>(file) ||
+               isa_and_nonnull<BitcodeFile>(file)) {
+      // Cache frameworks containing object or bitcode files to avoid duplicate
+      // symbols. Frameworks containing static archives are cached separately
+      // in addFile() to share caching with libraries, and frameworks
+      // containing dylibs should allow overwriting of attributes such as
+      // forceNeeded by subsequent loads
+      loadedObjectFrameworks.insert(*path);
     }
     return;
   }
+  if (loadType == LoadType::LCLinkerOption) {
+    assert(originFile);
+    missingAutolinkWarnings.push_back(saver().save(
+        toString(originFile) +
+        ": auto-linked framework not found for -framework " + name));
+    return;
+  }
   error("framework not found for -framework " + name);
 }
 
 // Parses LC_LINKER_OPTION contents, which can add additional command line
-// flags.
+// flags. This directly parses the flags instead of using the standard argument
+// parser to improve performance.
 void macho::parseLCLinkerOption(InputFile *f, unsigned argc, StringRef data) {
-  SmallVector<const char *, 4> argv;
+  if (config->ignoreAutoLink)
+    return;
+
+  SmallVector<StringRef, 4> argv;
   size_t offset = 0;
   for (unsigned i = 0; i < argc && offset < data.size(); ++i) {
     argv.push_back(data.data() + offset);
@@ -420,109 +492,33 @@ void macho::parseLCLinkerOption(InputFile *f, unsigned argc, StringRef data) {
   if (argv.size() != argc || offset > data.size())
     fatal(toString(f) + ": invalid LC_LINKER_OPTION");
 
-  MachOOptTable table;
-  unsigned missingIndex, missingCount;
-  InputArgList args = table.ParseArgs(argv, missingIndex, missingCount);
-  if (missingCount)
-    fatal(Twine(args.getArgString(missingIndex)) + ": missing argument");
-  for (const Arg *arg : args.filtered(OPT_UNKNOWN))
-    error("unknown argument: " + arg->getAsString(args));
-
-  for (const Arg *arg : args) {
-    switch (arg->getOption().getID()) {
-    case OPT_l: {
-      StringRef name = arg->getValue();
-      bool forceLoad =
-          config->forceLoadSwift ? name.startswith("swift") : false;
-      addLibrary(name, /*isNeeded=*/false, /*isWeak=*/false,
-                 /*isReexport=*/false, /*isExplicit=*/false, forceLoad);
-      break;
-    }
-    case OPT_framework:
-      addFramework(arg->getValue(), /*isNeeded=*/false, /*isWeak=*/false,
-                   /*isReexport=*/false, /*isExplicit=*/false);
-      break;
-    default:
-      error(arg->getSpelling() + " is not allowed in LC_LINKER_OPTION");
-    }
+  unsigned i = 0;
+  StringRef arg = argv[i];
+  if (arg.consume_front("-l")) {
+    if (config->ignoreAutoLinkOptions.contains(arg))
+      return;
+    addLibrary(arg, /*isNeeded=*/false, /*isWeak=*/false,
+               /*isReexport=*/false, /*isHidden=*/false, /*isExplicit=*/false,
+               LoadType::LCLinkerOption, f);
+  } else if (arg == "-framework") {
+    StringRef name = argv[++i];
+    if (config->ignoreAutoLinkOptions.contains(name))
+      return;
+    addFramework(name, /*isNeeded=*/false, /*isWeak=*/false,
+                 /*isReexport=*/false, /*isExplicit=*/false,
+                 LoadType::LCLinkerOption, f);
+  } else {
+    error(arg + " is not allowed in LC_LINKER_OPTION");
   }
 }
 
-static void addFileList(StringRef path) {
-  Optional<MemoryBufferRef> buffer = readFile(path);
+static void addFileList(StringRef path, bool isLazy) {
+  std::optional<MemoryBufferRef> buffer = readFile(path);
   if (!buffer)
     return;
   MemoryBufferRef mbref = *buffer;
   for (StringRef path : args::getLines(mbref))
-    addFile(rerootPath(path), /*forceLoadArchive=*/false);
-}
-
-// An order file has one entry per line, in the following format:
-//
-//   <cpu>:<object file>:<symbol name>
-//
-// <cpu> and <object file> are optional. If not specified, then that entry
-// matches any symbol of that name. Parsing this format is not quite
-// straightforward because the symbol name itself can contain colons, so when
-// encountering a colon, we consider the preceding characters to decide if it
-// can be a valid CPU type or file path.
-//
-// If a symbol is matched by multiple entries, then it takes the lowest-ordered
-// entry (the one nearest to the front of the list.)
-//
-// The file can also have line comments that start with '#'.
-static void parseOrderFile(StringRef path) {
-  Optional<MemoryBufferRef> buffer = readFile(path);
-  if (!buffer) {
-    error("Could not read order file at " + path);
-    return;
-  }
-
-  MemoryBufferRef mbref = *buffer;
-  size_t priority = std::numeric_limits<size_t>::max();
-  for (StringRef line : args::getLines(mbref)) {
-    StringRef objectFile, symbol;
-    line = line.take_until([](char c) { return c == '#'; }); // ignore comments
-    line = line.ltrim();
-
-    CPUType cpuType = StringSwitch<CPUType>(line)
-                          .StartsWith("i386:", CPU_TYPE_I386)
-                          .StartsWith("x86_64:", CPU_TYPE_X86_64)
-                          .StartsWith("arm:", CPU_TYPE_ARM)
-                          .StartsWith("arm64:", CPU_TYPE_ARM64)
-                          .StartsWith("ppc:", CPU_TYPE_POWERPC)
-                          .StartsWith("ppc64:", CPU_TYPE_POWERPC64)
-                          .Default(CPU_TYPE_ANY);
-
-    if (cpuType != CPU_TYPE_ANY && cpuType != target->cpuType)
-      continue;
-
-    // Drop the CPU type as well as the colon
-    if (cpuType != CPU_TYPE_ANY)
-      line = line.drop_until([](char c) { return c == ':'; }).drop_front();
-
-    constexpr std::array<StringRef, 2> fileEnds = {".o:", ".o):"};
-    for (StringRef fileEnd : fileEnds) {
-      size_t pos = line.find(fileEnd);
-      if (pos != StringRef::npos) {
-        // Split the string around the colon
-        objectFile = line.take_front(pos + fileEnd.size() - 1);
-        line = line.drop_front(pos + fileEnd.size());
-        break;
-      }
-    }
-    symbol = line.trim();
-
-    if (!symbol.empty()) {
-      SymbolPriorityEntry &entry = config->priorities[symbol];
-      if (!objectFile.empty())
-        entry.objectFiles.insert(std::make_pair(objectFile, priority));
-      else
-        entry.anyObjectFile = std::max(entry.anyObjectFile, priority);
-    }
-
-    --priority;
-  }
+    addFile(rerootPath(path), LoadType::CommandLine, isLazy);
 }
 
 // We expect sub-library names of the form "libfoo", which will match a dylib
@@ -534,8 +530,7 @@ static bool markReexport(StringRef searchName, ArrayRef<StringRef> extensions) {
     if (auto *dylibFile = dyn_cast<DylibFile>(file)) {
       StringRef filename = path::filename(dylibFile->getName());
       if (filename.consume_front(searchName) &&
-          (filename.empty() ||
-           find(extensions, filename) != extensions.end())) {
+          (filename.empty() || llvm::is_contained(extensions, filename))) {
         dylibFile->reexport = true;
         return true;
       }
@@ -555,23 +550,19 @@ static void initLLVM() {
   InitializeAllAsmParsers();
 }
 
-static void compileBitcodeFiles() {
-  // FIXME: Remove this once LTO.cpp honors config->exportDynamic.
-  if (config->exportDynamic)
-    for (InputFile *file : inputFiles)
-      if (isa<BitcodeFile>(file)) {
-        warn("the effect of -export_dynamic on LTO is not yet implemented");
-        break;
-      }
-
+static bool compileBitcodeFiles() {
   TimeTraceScope timeScope("LTO");
   auto *lto = make<BitcodeCompiler>();
   for (InputFile *file : inputFiles)
     if (auto *bitcodeFile = dyn_cast<BitcodeFile>(file))
-      lto->add(*bitcodeFile);
+      if (!file->lazy)
+        lto->add(*bitcodeFile);
 
-  for (ObjFile *file : lto->compile())
+  std::vector<ObjFile *> compiled = lto->compile();
+  for (ObjFile *file : compiled)
     inputFiles.insert(file);
+
+  return !compiled.empty();
 }
 
 // Replaces common symbols with defined symbols residing in __common sections.
@@ -590,9 +581,11 @@ static void replaceCommonSymbols() {
     // but it's not really worth supporting the linking of 64-bit programs on
     // 32-bit archs.
     ArrayRef<uint8_t> data = {nullptr, static_cast<size_t>(common->size)};
-    auto *isec = make<ConcatInputSection>(
-        segment_names::data, section_names::common, common->getFile(), data,
-        common->align, S_ZEROFILL);
+    // FIXME avoid creating one Section per symbol?
+    auto *section =
+        make<Section>(common->getFile(), segment_names::data,
+                      section_names::common, S_ZEROFILL, /*addr=*/0);
+    auto *isec = make<ConcatInputSection>(*section, data, common->align);
     if (!osec)
       osec = ConcatOutputSection::getOrCreateForInput(isec);
     isec->parent = osec;
@@ -600,14 +593,11 @@ static void replaceCommonSymbols() {
 
     // FIXME: CommonSymbol should store isReferencedDynamically, noDeadStrip
     // and pass them on here.
-    replaceSymbol<Defined>(sym, sym->getName(), isec->getFile(), isec,
-                           /*value=*/0,
-                           /*size=*/0,
-                           /*isWeakDef=*/false,
-                           /*isExternal=*/true, common->privateExtern,
-                           /*isThumb=*/false,
-                           /*isReferencedDynamically=*/false,
-                           /*noDeadStrip=*/false);
+    replaceSymbol<Defined>(
+        sym, sym->getName(), common->getFile(), isec, /*value=*/0, common->size,
+        /*isWeakDef=*/false, /*isExternal=*/true, common->privateExtern,
+        /*includeInSymtab=*/true, /*isThumb=*/false,
+        /*isReferencedDynamically=*/false, /*noDeadStrip=*/false);
   }
 }
 
@@ -626,7 +616,7 @@ static void initializeSectionRenameMap() {
                              section_names::objcCatList,
                              section_names::objcNonLazyCatList,
                              section_names::objcProtoList,
-                             section_names::objcImageInfo};
+                             section_names::objCImageInfo};
     for (StringRef s : v)
       config->sectionRenameMap[{segment_names::data, s}] = {
           segment_names::dataConst, s};
@@ -651,59 +641,114 @@ static std::string lowerDash(StringRef s) {
                      map_iterator(s.end(), toLowerDash));
 }
 
-// Has the side-effect of setting Config::platformInfo.
-static PlatformKind parsePlatformVersion(const ArgList &args) {
-  const Arg *arg = args.getLastArg(OPT_platform_version);
-  if (!arg) {
-    error("must specify -platform_version");
-    return PlatformKind::unknown;
-  }
+struct PlatformVersion {
+  PlatformType platform = PLATFORM_UNKNOWN;
+  llvm::VersionTuple minimum;
+  llvm::VersionTuple sdk;
+};
 
+static PlatformVersion parsePlatformVersion(const Arg *arg) {
+  assert(arg->getOption().getID() == OPT_platform_version);
   StringRef platformStr = arg->getValue(0);
   StringRef minVersionStr = arg->getValue(1);
   StringRef sdkVersionStr = arg->getValue(2);
 
+  PlatformVersion platformVersion;
+
   // TODO(compnerd) see if we can generate this case list via XMACROS
-  PlatformKind platform =
-      StringSwitch<PlatformKind>(lowerDash(platformStr))
-          .Cases("macos", "1", PlatformKind::macOS)
-          .Cases("ios", "2", PlatformKind::iOS)
-          .Cases("tvos", "3", PlatformKind::tvOS)
-          .Cases("watchos", "4", PlatformKind::watchOS)
-          .Cases("bridgeos", "5", PlatformKind::bridgeOS)
-          .Cases("mac-catalyst", "6", PlatformKind::macCatalyst)
-          .Cases("ios-simulator", "7", PlatformKind::iOSSimulator)
-          .Cases("tvos-simulator", "8", PlatformKind::tvOSSimulator)
-          .Cases("watchos-simulator", "9", PlatformKind::watchOSSimulator)
-          .Cases("driverkit", "10", PlatformKind::driverKit)
-          .Default(PlatformKind::unknown);
-  if (platform == PlatformKind::unknown)
+  platformVersion.platform =
+      StringSwitch<PlatformType>(lowerDash(platformStr))
+          .Cases("macos", "1", PLATFORM_MACOS)
+          .Cases("ios", "2", PLATFORM_IOS)
+          .Cases("tvos", "3", PLATFORM_TVOS)
+          .Cases("watchos", "4", PLATFORM_WATCHOS)
+          .Cases("bridgeos", "5", PLATFORM_BRIDGEOS)
+          .Cases("mac-catalyst", "6", PLATFORM_MACCATALYST)
+          .Cases("ios-simulator", "7", PLATFORM_IOSSIMULATOR)
+          .Cases("tvos-simulator", "8", PLATFORM_TVOSSIMULATOR)
+          .Cases("watchos-simulator", "9", PLATFORM_WATCHOSSIMULATOR)
+          .Cases("driverkit", "10", PLATFORM_DRIVERKIT)
+          .Default(PLATFORM_UNKNOWN);
+  if (platformVersion.platform == PLATFORM_UNKNOWN)
     error(Twine("malformed platform: ") + platformStr);
   // TODO: check validity of version strings, which varies by platform
   // NOTE: ld64 accepts version strings with 5 components
   // llvm::VersionTuple accepts no more than 4 components
   // Has Apple ever published version strings with 5 components?
-  if (config->platformInfo.minimum.tryParse(minVersionStr))
+  if (platformVersion.minimum.tryParse(minVersionStr))
     error(Twine("malformed minimum version: ") + minVersionStr);
-  if (config->platformInfo.sdk.tryParse(sdkVersionStr))
+  if (platformVersion.sdk.tryParse(sdkVersionStr))
     error(Twine("malformed sdk version: ") + sdkVersionStr);
-  return platform;
+  return platformVersion;
+}
+
+// Has the side-effect of setting Config::platformInfo.
+static PlatformType parsePlatformVersions(const ArgList &args) {
+  std::map<PlatformType, PlatformVersion> platformVersions;
+  const PlatformVersion *lastVersionInfo = nullptr;
+  for (const Arg *arg : args.filtered(OPT_platform_version)) {
+    PlatformVersion version = parsePlatformVersion(arg);
+
+    // For each platform, the last flag wins:
+    // `-platform_version macos 2 3 -platform_version macos 4 5` has the same
+    // effect as just passing `-platform_version macos 4 5`.
+    // FIXME: ld64 warns on multiple flags for one platform. Should we?
+    platformVersions[version.platform] = version;
+    lastVersionInfo = &platformVersions[version.platform];
+  }
+
+  if (platformVersions.empty()) {
+    error("must specify -platform_version");
+    return PLATFORM_UNKNOWN;
+  }
+  if (platformVersions.size() > 2) {
+    error("must specify -platform_version at most twice");
+    return PLATFORM_UNKNOWN;
+  }
+  if (platformVersions.size() == 2) {
+    bool isZipperedCatalyst = platformVersions.count(PLATFORM_MACOS) &&
+                              platformVersions.count(PLATFORM_MACCATALYST);
+
+    if (!isZipperedCatalyst) {
+      error("lld supports writing zippered outputs only for "
+            "macos and mac-catalyst");
+    } else if (config->outputType != MH_DYLIB &&
+               config->outputType != MH_BUNDLE) {
+      error("writing zippered outputs only valid for -dylib and -bundle");
+    } else {
+      config->platformInfo.minimum = platformVersions[PLATFORM_MACOS].minimum;
+      config->platformInfo.sdk = platformVersions[PLATFORM_MACOS].sdk;
+      config->secondaryPlatformInfo = PlatformInfo{};
+      config->secondaryPlatformInfo->minimum =
+          platformVersions[PLATFORM_MACCATALYST].minimum;
+      config->secondaryPlatformInfo->sdk =
+          platformVersions[PLATFORM_MACCATALYST].sdk;
+    }
+    return PLATFORM_MACOS;
+  }
+
+  config->platformInfo.minimum = lastVersionInfo->minimum;
+  config->platformInfo.sdk = lastVersionInfo->sdk;
+  return lastVersionInfo->platform;
 }
 
 // Has the side-effect of setting Config::target.
 static TargetInfo *createTargetInfo(InputArgList &args) {
   StringRef archName = args.getLastArgValue(OPT_arch);
-  if (archName.empty())
-    fatal("must specify -arch");
-  PlatformKind platform = parsePlatformVersion(args);
+  if (archName.empty()) {
+    error("must specify -arch");
+    return nullptr;
+  }
 
+  PlatformType platform = parsePlatformVersions(args);
   config->platformInfo.target =
       MachO::Target(getArchitectureFromName(archName), platform);
+  if (config->secondaryPlatformInfo) {
+    config->secondaryPlatformInfo->target =
+        MachO::Target(getArchitectureFromName(archName), PLATFORM_MACCATALYST);
+  }
 
-  uint32_t cpuType;
-  uint32_t cpuSubtype;
-  std::tie(cpuType, cpuSubtype) = getCPUTypeFromArchitecture(config->arch());
-
+  auto [cpuType, cpuSubtype] = getCPUTypeFromArchitecture(config->arch());
   switch (cpuType) {
   case CPU_TYPE_X86_64:
     return createX86_64TargetInfo();
@@ -714,7 +759,8 @@ static TargetInfo *createTargetInfo(InputArgList &args) {
   case CPU_TYPE_ARM:
     return createARMTargetInfo(cpuSubtype);
   default:
-    fatal("missing or unsupported -arch " + archName);
+    error("missing or unsupported -arch " + archName);
+    return nullptr;
   }
 }
 
@@ -736,16 +782,15 @@ getUndefinedSymbolTreatment(const ArgList &args) {
              (treatment == UndefinedSymbolTreatment::warning ||
               treatment == UndefinedSymbolTreatment::suppress)) {
     if (treatment == UndefinedSymbolTreatment::warning)
-      error("'-undefined warning' only valid with '-flat_namespace'");
+      fatal("'-undefined warning' only valid with '-flat_namespace'");
     else
-      error("'-undefined suppress' only valid with '-flat_namespace'");
+      fatal("'-undefined suppress' only valid with '-flat_namespace'");
     treatment = UndefinedSymbolTreatment::error;
   }
   return treatment;
 }
 
 static ICFLevel getICFLevel(const ArgList &args) {
-  bool noDeduplicate = args.hasArg(OPT_no_deduplicate);
   StringRef icfLevelStr = args.getLastArgValue(OPT_icf_eq);
   auto icfLevel = StringSwitch<ICFLevel>(icfLevelStr)
                       .Cases("none", "", ICFLevel::none)
@@ -756,17 +801,21 @@ static ICFLevel getICFLevel(const ArgList &args) {
     warn(Twine("unknown --icf=OPTION `") + icfLevelStr +
          "', defaulting to `none'");
     icfLevel = ICFLevel::none;
-  } else if (icfLevel != ICFLevel::none && noDeduplicate) {
-    warn(Twine("`--icf=" + icfLevelStr +
-               "' conflicts with -no_deduplicate, setting to `none'"));
-    icfLevel = ICFLevel::none;
-  } else if (icfLevel == ICFLevel::safe) {
-    warn(Twine("`--icf=safe' is not yet implemented, reverting to `none'"));
-    icfLevel = ICFLevel::none;
   }
   return icfLevel;
 }
 
+static ObjCStubsMode getObjCStubsMode(const ArgList &args) {
+  const Arg *arg = args.getLastArg(OPT_objc_stubs_fast, OPT_objc_stubs_small);
+  if (!arg)
+    return ObjCStubsMode::fast;
+
+  if (arg->getOption().getID() == OPT_objc_stubs_small)
+    warn("-objc_stubs_small is not yet implemented, defaulting to "
+         "-objc_stubs_fast");
+  return ObjCStubsMode::fast;
+}
+
 static void warnIfDeprecatedOption(const Option &opt) {
   if (!opt.getGroup().isValid())
     return;
@@ -794,6 +843,8 @@ static void warnIfUnimplementedOption(const Option &opt) {
   case OPT_grp_ignored:
     warn("Option `" + opt.getPrefixedName() + "' is ignored.");
     break;
+  case OPT_grp_ignored_silently:
+    break;
   default:
     warn("Option `" + opt.getPrefixedName() +
          "' is not yet implemented. Stay tuned...");
@@ -807,6 +858,20 @@ static const char *getReproduceOption(InputArgList &args) {
   return getenv("LLD_REPRODUCE");
 }
 
+// Parse options of the form "old;new".
+static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args,
+                                                        unsigned id) {
+  auto *arg = args.getLastArg(id);
+  if (!arg)
+    return {"", ""};
+
+  StringRef s = arg->getValue();
+  std::pair<StringRef, StringRef> ret = s.split(';');
+  if (ret.second.empty())
+    error(arg->getSpelling() + " expects 'old;new' format, but got " + s);
+  return ret;
+}
+
 static void parseClangOption(StringRef opt, const Twine &msg) {
   std::string err;
   raw_string_ostream os(err);
@@ -884,27 +949,41 @@ static std::vector<SectionAlign> parseSectAlign(const opt::InputArgList &args) {
   return sectAligns;
 }
 
-PlatformKind macho::removeSimulator(PlatformKind platform) {
+PlatformType macho::removeSimulator(PlatformType platform) {
   switch (platform) {
-  case PlatformKind::iOSSimulator:
-    return PlatformKind::iOS;
-  case PlatformKind::tvOSSimulator:
-    return PlatformKind::tvOS;
-  case PlatformKind::watchOSSimulator:
-    return PlatformKind::watchOS;
+  case PLATFORM_IOSSIMULATOR:
+    return PLATFORM_IOS;
+  case PLATFORM_TVOSSIMULATOR:
+    return PLATFORM_TVOS;
+  case PLATFORM_WATCHOSSIMULATOR:
+    return PLATFORM_WATCHOS;
   default:
     return platform;
   }
 }
 
+static bool supportsNoPie() {
+  return !(config->arch() == AK_arm64 || config->arch() == AK_arm64e ||
+           config->arch() == AK_arm64_32);
+}
+
+static bool shouldAdhocSignByDefault(Architecture arch, PlatformType platform) {
+  if (arch != AK_arm64 && arch != AK_arm64e)
+    return false;
+
+  return platform == PLATFORM_MACOS || platform == PLATFORM_IOSSIMULATOR ||
+         platform == PLATFORM_TVOSSIMULATOR ||
+         platform == PLATFORM_WATCHOSSIMULATOR;
+}
+
 static bool dataConstDefault(const InputArgList &args) {
-  static const std::vector<std::pair<PlatformKind, VersionTuple>> minVersion = {
-      {PlatformKind::macOS, VersionTuple(10, 15)},
-      {PlatformKind::iOS, VersionTuple(13, 0)},
-      {PlatformKind::tvOS, VersionTuple(13, 0)},
-      {PlatformKind::watchOS, VersionTuple(6, 0)},
-      {PlatformKind::bridgeOS, VersionTuple(4, 0)}};
-  PlatformKind platform = removeSimulator(config->platformInfo.target.Platform);
+  static const std::array<std::pair<PlatformType, VersionTuple>, 5> minVersion =
+      {{{PLATFORM_MACOS, VersionTuple(10, 15)},
+        {PLATFORM_IOS, VersionTuple(13, 0)},
+        {PLATFORM_TVOS, VersionTuple(13, 0)},
+        {PLATFORM_WATCHOS, VersionTuple(6, 0)},
+        {PLATFORM_BRIDGEOS, VersionTuple(4, 0)}}};
+  PlatformType platform = removeSimulator(config->platformInfo.target.Platform);
   auto it = llvm::find_if(minVersion,
                           [&](const auto &p) { return p.first == platform; });
   if (it != minVersion.end())
@@ -913,7 +992,7 @@ static bool dataConstDefault(const InputArgList &args) {
 
   switch (config->outputType) {
   case MH_EXECUTE:
-    return !args.hasArg(OPT_no_pie);
+    return !(args.hasArg(OPT_no_pie) && supportsNoPie());
   case MH_BUNDLE:
     // FIXME: return false when -final_name ...
     // has prefix "/System/Library/UserEventPlugins/"
@@ -930,6 +1009,47 @@ static bool dataConstDefault(const InputArgList &args) {
   return false;
 }
 
+static bool shouldEmitChainedFixups(const InputArgList &args) {
+  const Arg *arg = args.getLastArg(OPT_fixup_chains, OPT_no_fixup_chains);
+  if (arg && arg->getOption().matches(OPT_no_fixup_chains))
+    return false;
+
+  bool isRequested = arg != nullptr;
+
+  // Version numbers taken from the Xcode 13.3 release notes.
+  static const std::array<std::pair<PlatformType, VersionTuple>, 4> minVersion =
+      {{{PLATFORM_MACOS, VersionTuple(11, 0)},
+        {PLATFORM_IOS, VersionTuple(13, 4)},
+        {PLATFORM_TVOS, VersionTuple(14, 0)},
+        {PLATFORM_WATCHOS, VersionTuple(7, 0)}}};
+  PlatformType platform = removeSimulator(config->platformInfo.target.Platform);
+  auto it = llvm::find_if(minVersion,
+                          [&](const auto &p) { return p.first == platform; });
+  if (it != minVersion.end() && it->second > config->platformInfo.minimum) {
+    if (!isRequested)
+      return false;
+
+    warn("-fixup_chains requires " + getPlatformName(config->platform()) + " " +
+         it->second.getAsString() + ", which is newer than target minimum of " +
+         config->platformInfo.minimum.getAsString());
+  }
+
+  if (!is_contained({AK_x86_64, AK_x86_64h, AK_arm64}, config->arch())) {
+    if (isRequested)
+      error("-fixup_chains is only supported on x86_64 and arm64 targets");
+    return false;
+  }
+
+  if (!config->isPic) {
+    if (isRequested)
+      error("-fixup_chains is incompatible with -no_pie");
+    return false;
+  }
+
+  // TODO: Enable by default once stable.
+  return isRequested;
+}
+
 void SymbolPatterns::clear() {
   literals.clear();
   globs.clear();
@@ -959,32 +1079,37 @@ bool SymbolPatterns::match(StringRef symbolName) const {
   return matchLiteral(symbolName) || matchGlob(symbolName);
 }
 
+static void parseSymbolPatternsFile(const Arg *arg,
+                                    SymbolPatterns &symbolPatterns) {
+  StringRef path = arg->getValue();
+  std::optional<MemoryBufferRef> buffer = readFile(path);
+  if (!buffer) {
+    error("Could not read symbol file: " + path);
+    return;
+  }
+  MemoryBufferRef mbref = *buffer;
+  for (StringRef line : args::getLines(mbref)) {
+    line = line.take_until([](char c) { return c == '#'; }).trim();
+    if (!line.empty())
+      symbolPatterns.insert(line);
+  }
+}
+
 static void handleSymbolPatterns(InputArgList &args,
                                  SymbolPatterns &symbolPatterns,
                                  unsigned singleOptionCode,
                                  unsigned listFileOptionCode) {
   for (const Arg *arg : args.filtered(singleOptionCode))
     symbolPatterns.insert(arg->getValue());
-  for (const Arg *arg : args.filtered(listFileOptionCode)) {
-    StringRef path = arg->getValue();
-    Optional<MemoryBufferRef> buffer = readFile(path);
-    if (!buffer) {
-      error("Could not read symbol file: " + path);
-      continue;
-    }
-    MemoryBufferRef mbref = *buffer;
-    for (StringRef line : args::getLines(mbref)) {
-      line = line.take_until([](char c) { return c == '#'; }).trim();
-      if (!line.empty())
-        symbolPatterns.insert(line);
-    }
-  }
+  for (const Arg *arg : args.filtered(listFileOptionCode))
+    parseSymbolPatternsFile(arg, symbolPatterns);
 }
 
-void createFiles(const InputArgList &args) {
+static void createFiles(const InputArgList &args) {
   TimeTraceScope timeScope("Load input files");
   // This loop should be reserved for options whose exact ordering matters.
   // Other options should be handled via filtered() and/or getLastArg().
+  bool isLazy = false;
   for (const Arg *arg : args) {
     const Option &opt = arg->getOption();
     warnIfDeprecatedOption(opt);
@@ -992,38 +1117,45 @@ void createFiles(const InputArgList &args) {
 
     switch (opt.getID()) {
     case OPT_INPUT:
-      addFile(rerootPath(arg->getValue()), /*forceLoadArchive=*/false);
+      addFile(rerootPath(arg->getValue()), LoadType::CommandLine, isLazy);
       break;
     case OPT_needed_library:
       if (auto *dylibFile = dyn_cast_or_null<DylibFile>(
-              addFile(rerootPath(arg->getValue()), false)))
+              addFile(rerootPath(arg->getValue()), LoadType::CommandLine)))
         dylibFile->forceNeeded = true;
       break;
     case OPT_reexport_library:
-      if (auto *dylibFile = dyn_cast_or_null<DylibFile>(addFile(
-              rerootPath(arg->getValue()), /*forceLoadArchive=*/false))) {
+      if (auto *dylibFile = dyn_cast_or_null<DylibFile>(
+              addFile(rerootPath(arg->getValue()), LoadType::CommandLine))) {
         config->hasReexports = true;
         dylibFile->reexport = true;
       }
       break;
     case OPT_weak_library:
       if (auto *dylibFile = dyn_cast_or_null<DylibFile>(
-              addFile(rerootPath(arg->getValue()), /*forceLoadArchive=*/false)))
+              addFile(rerootPath(arg->getValue()), LoadType::CommandLine)))
         dylibFile->forceWeakImport = true;
       break;
     case OPT_filelist:
-      addFileList(arg->getValue());
+      addFileList(arg->getValue(), isLazy);
       break;
     case OPT_force_load:
-      addFile(rerootPath(arg->getValue()), /*forceLoadArchive=*/true);
+      addFile(rerootPath(arg->getValue()), LoadType::CommandLineForce);
+      break;
+    case OPT_load_hidden:
+      addFile(rerootPath(arg->getValue()), LoadType::CommandLine,
+              /*isLazy=*/false, /*isExplicit=*/true, /*isBundleLoader=*/false,
+              /*isForceHidden=*/true);
       break;
     case OPT_l:
     case OPT_needed_l:
     case OPT_reexport_l:
     case OPT_weak_l:
+    case OPT_hidden_l:
       addLibrary(arg->getValue(), opt.getID() == OPT_needed_l,
                  opt.getID() == OPT_weak_l, opt.getID() == OPT_reexport_l,
-                 /*isExplicit=*/true, /*forceLoad=*/false);
+                 opt.getID() == OPT_hidden_l,
+                 /*isExplicit=*/true, LoadType::CommandLine);
       break;
     case OPT_framework:
     case OPT_needed_framework:
@@ -1031,7 +1163,18 @@ void createFiles(const InputArgList &args) {
     case OPT_weak_framework:
       addFramework(arg->getValue(), opt.getID() == OPT_needed_framework,
                    opt.getID() == OPT_weak_framework,
-                   opt.getID() == OPT_reexport_framework, /*isExplicit=*/true);
+                   opt.getID() == OPT_reexport_framework, /*isExplicit=*/true,
+                   LoadType::CommandLine);
+      break;
+    case OPT_start_lib:
+      if (isLazy)
+        error("nested --start-lib");
+      isLazy = true;
+      break;
+    case OPT_end_lib:
+      if (!isLazy)
+        error("stray --end-lib");
+      isLazy = false;
       break;
     default:
       break;
@@ -1043,15 +1186,19 @@ static void gatherInputSections() {
   TimeTraceScope timeScope("Gathering input sections");
   int inputOrder = 0;
   for (const InputFile *file : inputFiles) {
-    for (const SubsectionMap &map : file->subsections) {
+    for (const Section *section : file->sections) {
+      // Compact unwind entries require special handling elsewhere. (In
+      // contrast, EH frames are handled like regular ConcatInputSections.)
+      if (section->name == section_names::compactUnwind)
+        continue;
       ConcatOutputSection *osec = nullptr;
-      for (const SubsectionEntry &entry : map) {
-        if (auto *isec = dyn_cast<ConcatInputSection>(entry.isec)) {
+      for (const Subsection &subsection : section->subsections) {
+        if (auto *isec = dyn_cast<ConcatInputSection>(subsection.isec)) {
           if (isec->isCoalescedWeak())
             continue;
-          if (isec->getSegName() == segment_names::ld) {
-            assert(isec->getName() == section_names::compactUnwind);
-            in.unwindInfo->addInput(isec);
+          if (config->emitInitOffsets &&
+              sectionType(isec->getFlags()) == S_MOD_INIT_FUNC_POINTERS) {
+            in.initOffsets->addInput(isec);
             continue;
           }
           isec->outSecOff = inputOrder++;
@@ -1059,11 +1206,19 @@ static void gatherInputSections() {
             osec = ConcatOutputSection::getOrCreateForInput(isec);
           isec->parent = osec;
           inputSections.push_back(isec);
-        } else if (auto *isec = dyn_cast<CStringInputSection>(entry.isec)) {
-          if (in.cStringSection->inputOrder == UnspecifiedInputOrder)
-            in.cStringSection->inputOrder = inputOrder++;
-          in.cStringSection->addInput(isec);
-        } else if (auto *isec = dyn_cast<WordLiteralInputSection>(entry.isec)) {
+        } else if (auto *isec =
+                       dyn_cast<CStringInputSection>(subsection.isec)) {
+          if (isec->getName() == section_names::objcMethname) {
+            if (in.objcMethnameSection->inputOrder == UnspecifiedInputOrder)
+              in.objcMethnameSection->inputOrder = inputOrder++;
+            in.objcMethnameSection->addInput(isec);
+          } else {
+            if (in.cStringSection->inputOrder == UnspecifiedInputOrder)
+              in.cStringSection->inputOrder = inputOrder++;
+            in.cStringSection->addInput(isec);
+          }
+        } else if (auto *isec =
+                       dyn_cast<WordLiteralInputSection>(subsection.isec)) {
           if (in.wordLiteralSection->inputOrder == UnspecifiedInputOrder)
             in.wordLiteralSection->inputOrder = inputOrder++;
           in.wordLiteralSection->addInput(isec);
@@ -1072,17 +1227,48 @@ static void gatherInputSections() {
         }
       }
     }
+    if (!file->objCImageInfo.empty())
+      in.objCImageInfo->addFile(file);
   }
   assert(inputOrder <= UnspecifiedInputOrder);
 }
 
 static void foldIdenticalLiterals() {
+  TimeTraceScope timeScope("Fold identical literals");
   // We always create a cStringSection, regardless of whether dedupLiterals is
   // true. If it isn't, we simply create a non-deduplicating CStringSection.
   // Either way, we must unconditionally finalize it here.
   in.cStringSection->finalizeContents();
-  if (in.wordLiteralSection)
-    in.wordLiteralSection->finalizeContents();
+  in.objcMethnameSection->finalizeContents();
+  in.wordLiteralSection->finalizeContents();
+}
+
+static void addSynthenticMethnames() {
+  std::string &data = *make<std::string>();
+  llvm::raw_string_ostream os(data);
+  const int prefixLength = ObjCStubsSection::symbolPrefix.size();
+  for (Symbol *sym : symtab->getSymbols())
+    if (isa<Undefined>(sym))
+      if (sym->getName().startswith(ObjCStubsSection::symbolPrefix))
+        os << sym->getName().drop_front(prefixLength) << '\0';
+
+  if (data.empty())
+    return;
+
+  const auto *buf = reinterpret_cast<const uint8_t *>(data.c_str());
+  Section &section = *make<Section>(/*file=*/nullptr, segment_names::text,
+                                    section_names::objcMethname,
+                                    S_CSTRING_LITERALS, /*addr=*/0);
+
+  auto *isec =
+      make<CStringInputSection>(section, ArrayRef<uint8_t>{buf, data.size()},
+                                /*align=*/1, /*dedupLiterals=*/true);
+  isec->splitIntoPieces();
+  for (auto &piece : isec->pieces)
+    piece.live = true;
+  section.subsections.push_back({0, isec});
+  in.objcMethnameSection->addInput(isec);
+  in.objcMethnameSection->isec->markLive(0);
 }
 
 static void referenceStubBinder() {
@@ -1097,29 +1283,124 @@ static void referenceStubBinder() {
   // dyld_stub_binder is in libSystem.dylib, which is usually linked in. This
   // isn't needed for correctness, but the presence of that symbol suppresses
   // "no symbols" diagnostics from `nm`.
-  // StubHelperSection::setup() adds a reference and errors out if
+  // StubHelperSection::setUp() adds a reference and errors out if
   // dyld_stub_binder doesn't exist in case it is actually needed.
   symtab->addUndefined("dyld_stub_binder", /*file=*/nullptr, /*isWeak=*/false);
 }
 
-bool macho::link(ArrayRef<const char *> argsArr, bool canExitEarly,
-                 raw_ostream &stdoutOS, raw_ostream &stderrOS) {
-  lld::stdoutOS = &stdoutOS;
-  lld::stderrOS = &stderrOS;
+static void createAliases() {
+  for (const auto &pair : config->aliasedSymbols) {
+    if (const auto &sym = symtab->find(pair.first)) {
+      if (const auto &defined = dyn_cast<Defined>(sym)) {
+        symtab->aliasDefined(defined, pair.second, defined->getFile())
+            ->noDeadStrip = true;
+      } else {
+        error("TODO: support aliasing to symbols of kind " +
+              Twine(sym->kind()));
+      }
+    } else {
+      warn("undefined base symbol '" + pair.first + "' for alias '" +
+           pair.second + "'\n");
+    }
+  }
+
+  for (const InputFile *file : inputFiles) {
+    if (auto *objFile = dyn_cast<ObjFile>(file)) {
+      for (const AliasSymbol *alias : objFile->aliases) {
+        if (const auto &aliased = symtab->find(alias->getAliasedName())) {
+          if (const auto &defined = dyn_cast<Defined>(aliased)) {
+            symtab->aliasDefined(defined, alias->getName(), alias->getFile(),
+                                 alias->privateExtern);
+          } else {
+            // Common, dylib, and undefined symbols are all valid alias
+            // referents (undefineds can become valid Defined symbols later on
+            // in the link.)
+            error("TODO: support aliasing to symbols of kind " +
+                  Twine(aliased->kind()));
+          }
+        } else {
+          // This shouldn't happen since MC generates undefined symbols to
+          // represent the alias referents. Thus we fatal() instead of just
+          // warning here.
+          fatal("unable to find alias referent " + alias->getAliasedName() +
+                " for " + alias->getName());
+        }
+      }
+    }
+  }
+}
+
+static void handleExplicitExports() {
+  if (config->hasExplicitExports) {
+    parallelForEach(symtab->getSymbols(), [](Symbol *sym) {
+      if (auto *defined = dyn_cast<Defined>(sym)) {
+        StringRef symbolName = defined->getName();
+        if (config->exportedSymbols.match(symbolName)) {
+          if (defined->privateExtern) {
+            if (defined->weakDefCanBeHidden) {
+              // weak_def_can_be_hidden symbols behave similarly to
+              // private_extern symbols in most cases, except for when
+              // it is explicitly exported.
+              // The former can be exported but the latter cannot.
+              defined->privateExtern = false;
+            } else {
+              warn("cannot export hidden symbol " + toString(*defined) +
+                   "\n>>> defined in " + toString(defined->getFile()));
+            }
+          }
+        } else {
+          defined->privateExtern = true;
+        }
+      }
+    });
+  } else if (!config->unexportedSymbols.empty()) {
+    parallelForEach(symtab->getSymbols(), [](Symbol *sym) {
+      if (auto *defined = dyn_cast<Defined>(sym))
+        if (config->unexportedSymbols.match(defined->getName()))
+          defined->privateExtern = true;
+    });
+  }
+}
 
-  errorHandler().cleanupCallback = []() { freeArena(); };
+bool macho::link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
+                 llvm::raw_ostream &stderrOS, bool exitEarly,
+                 bool disableOutput) {
+  // This driver-specific context will be freed later by lldMain().
+  auto *ctx = new CommonLinkerContext;
+
+  ctx->e.initialize(stdoutOS, stderrOS, exitEarly, disableOutput);
+  ctx->e.cleanupCallback = []() {
+    resolvedFrameworks.clear();
+    resolvedLibraries.clear();
+    cachedReads.clear();
+    concatOutputSections.clear();
+    inputFiles.clear();
+    inputSections.clear();
+    loadedArchives.clear();
+    loadedObjectFrameworks.clear();
+    missingAutolinkWarnings.clear();
+    syntheticSections.clear();
+    thunkMap.clear();
+
+    firstTLVDataSection = nullptr;
+    tar = nullptr;
+    memset(&in, 0, sizeof(in));
+
+    resetLoadedDylibs();
+    resetOutputSegments();
+    resetWriter();
+    InputFile::resetIdCount();
+  };
 
-  errorHandler().logName = args::getFilenameWithoutExe(argsArr[0]);
-  stderrOS.enable_colors(stderrOS.has_colors());
+  ctx->e.logName = args::getFilenameWithoutExe(argsArr[0]);
 
   MachOOptTable parser;
   InputArgList args = parser.parse(argsArr.slice(1));
 
-  errorHandler().errorLimitExceededMsg =
-      "too many errors emitted, stopping now "
-      "(use --error-limit=0 to see all errors)";
-  errorHandler().errorLimit = args::getInteger(args, OPT_error_limit_eq, 20);
-  errorHandler().verbose = args.hasArg(OPT_verbose);
+  ctx->e.errorLimitExceededMsg = "too many errors emitted, stopping now "
+                                 "(use --error-limit=0 to see all errors)";
+  ctx->e.errorLimit = args::getInteger(args, OPT_error_limit_eq, 20);
+  ctx->e.verbose = args.hasArg(OPT_verbose);
 
   if (args.hasArg(OPT_help_hidden)) {
     parser.printHelp(argsArr[0], /*showHidden=*/true);
@@ -1134,11 +1415,70 @@ bool macho::link(ArrayRef<const char *> argsArr, bool canExitEarly,
     return true;
   }
 
-  config = make<Configuration>();
-  symtab = make<SymbolTable>();
+  config = std::make_unique<Configuration>();
+  symtab = std::make_unique<SymbolTable>();
+  config->outputType = getOutputType(args);
   target = createTargetInfo(args);
-  depTracker =
-      make<DependencyTracker>(args.getLastArgValue(OPT_dependency_info));
+  depTracker = std::make_unique<DependencyTracker>(
+      args.getLastArgValue(OPT_dependency_info));
+  if (errorCount())
+    return false;
+
+  if (args.hasArg(OPT_pagezero_size)) {
+    uint64_t pagezeroSize = args::getHex(args, OPT_pagezero_size, 0);
+
+    // ld64 does something really weird. It attempts to realign the value to the
+    // page size, but assumes the page size is 4K. This doesn't work with most
+    // of Apple's ARM64 devices, which use a page size of 16K. This means that
+    // it will first 4K align it by rounding down, then round up to 16K.  This
+    // probably only happened because no one using this arg with anything other
+    // then 0, so no one checked if it did what is what it says it does.
+
+    // So we are not copying this weird behavior and doing the it in a logical
+    // way, by always rounding down to page size.
+    if (!isAligned(Align(target->getPageSize()), pagezeroSize)) {
+      pagezeroSize -= pagezeroSize % target->getPageSize();
+      warn("__PAGEZERO size is not page aligned, rounding down to 0x" +
+           Twine::utohexstr(pagezeroSize));
+    }
+
+    target->pageZeroSize = pagezeroSize;
+  }
+
+  config->osoPrefix = args.getLastArgValue(OPT_oso_prefix);
+  if (!config->osoPrefix.empty()) {
+    // Expand special characters, such as ".", "..", or  "~", if present.
+    // Note: LD64 only expands "." and not other special characters.
+    // That seems silly to imitate so we will not try to follow it, but rather
+    // just use real_path() to do it.
+
+    // The max path length is 4096, in theory. However that seems quite long
+    // and seems unlikely that any one would want to strip everything from the
+    // path. Hence we've picked a reasonably large number here.
+    SmallString<1024> expanded;
+    if (!fs::real_path(config->osoPrefix, expanded,
+                       /*expand_tilde=*/true)) {
+      // Note: LD64 expands "." to be `<current_dir>/`
+      // (ie., it has a slash suffix) whereas real_path() doesn't.
+      // So we have to append '/' to be consistent.
+      StringRef sep = sys::path::get_separator();
+      // real_path removes trailing slashes as part of the normalization, but
+      // these are meaningful for our text based stripping
+      if (config->osoPrefix.equals(".") || config->osoPrefix.endswith(sep))
+        expanded += sep;
+      config->osoPrefix = saver().save(expanded.str());
+    }
+  }
+
+  bool pie = args.hasFlag(OPT_pie, OPT_no_pie, true);
+  if (!supportsNoPie() && !pie) {
+    warn("-no_pie ignored for arm64");
+    pie = true;
+  }
+
+  config->isPic = config->outputType == MH_DYLIB ||
+                  config->outputType == MH_BUNDLE ||
+                  (config->outputType == MH_EXECUTE && pie);
 
   // Must be set before any InputSections and Symbols are created.
   config->deadStrip = args.hasArg(OPT_dead_strip);
@@ -1193,29 +1533,57 @@ bool macho::link(ArrayRef<const char *> argsArr, bool canExitEarly,
       args.hasArg(OPT_print_dylib_search) || getenv("RC_TRACE_DYLIB_SEARCHING");
   config->printEachFile = args.hasArg(OPT_t);
   config->printWhyLoad = args.hasArg(OPT_why_load);
-  config->outputType = getOutputType(args);
+  config->omitDebugInfo = args.hasArg(OPT_S);
+  config->errorForArchMismatch = args.hasArg(OPT_arch_errors_fatal);
   if (const Arg *arg = args.getLastArg(OPT_bundle_loader)) {
     if (config->outputType != MH_BUNDLE)
       error("-bundle_loader can only be used with MachO bundle output");
-    addFile(arg->getValue(), /*forceLoadArchive=*/false, /*isExplicit=*/false,
-            /*isBundleLoader=*/true);
+    addFile(arg->getValue(), LoadType::CommandLine, /*isLazy=*/false,
+            /*isExplicit=*/false, /*isBundleLoader=*/true);
   }
+  for (auto *arg : args.filtered(OPT_dyld_env)) {
+    StringRef envPair(arg->getValue());
+    if (!envPair.contains('='))
+      error("-dyld_env's argument is  malformed. Expected "
+            "-dyld_env <ENV_VAR>=<VALUE>, got `" +
+            envPair + "`");
+    config->dyldEnvs.push_back(envPair);
+  }
+  if (!config->dyldEnvs.empty() && config->outputType != MH_EXECUTE)
+    error("-dyld_env can only be used when creating executable output");
+
   if (const Arg *arg = args.getLastArg(OPT_umbrella)) {
     if (config->outputType != MH_DYLIB)
       warn("-umbrella used, but not creating dylib");
     config->umbrella = arg->getValue();
   }
   config->ltoObjPath = args.getLastArgValue(OPT_object_path_lto);
-  config->ltoNewPassManager =
-      args.hasFlag(OPT_no_lto_legacy_pass_manager, OPT_lto_legacy_pass_manager,
-                   LLVM_ENABLE_NEW_PASS_MANAGER);
   config->ltoo = args::getInteger(args, OPT_lto_O, 2);
   if (config->ltoo > 3)
     error("--lto-O: invalid optimization level: " + Twine(config->ltoo));
   config->thinLTOCacheDir = args.getLastArgValue(OPT_cache_path_lto);
   config->thinLTOCachePolicy = getLTOCachePolicy(args);
+  config->thinLTOEmitImportsFiles = args.hasArg(OPT_thinlto_emit_imports_files);
+  config->thinLTOEmitIndexFiles = args.hasArg(OPT_thinlto_emit_index_files) ||
+                                  args.hasArg(OPT_thinlto_index_only) ||
+                                  args.hasArg(OPT_thinlto_index_only_eq);
+  config->thinLTOIndexOnly = args.hasArg(OPT_thinlto_index_only) ||
+                             args.hasArg(OPT_thinlto_index_only_eq);
+  config->thinLTOIndexOnlyArg = args.getLastArgValue(OPT_thinlto_index_only_eq);
+  config->thinLTOObjectSuffixReplace =
+      getOldNewOptions(args, OPT_thinlto_object_suffix_replace_eq);
+  config->thinLTOPrefixReplace =
+      getOldNewOptions(args, OPT_thinlto_prefix_replace_eq);
+  if (config->thinLTOEmitIndexFiles && !config->thinLTOIndexOnly) {
+    if (args.hasArg(OPT_thinlto_object_suffix_replace_eq))
+      error("--thinlto-object-suffix-replace is not supported with "
+            "--thinlto-emit-index-files");
+    else if (args.hasArg(OPT_thinlto_prefix_replace_eq))
+      error("--thinlto-prefix-replace is not supported with "
+            "--thinlto-emit-index-files");
+  }
   config->runtimePaths = args::getStrings(args, OPT_rpath);
-  config->allLoad = args.hasArg(OPT_all_load);
+  config->allLoad = args.hasFlag(OPT_all_load, OPT_noall_load, false);
   config->archMultiple = args.hasArg(OPT_arch_multiple);
   config->applicationExtension = args.hasFlag(
       OPT_application_extension, OPT_no_application_extension, false);
@@ -1230,15 +1598,38 @@ bool macho::link(ArrayRef<const char *> argsArr, bool canExitEarly,
   config->emitBitcodeBundle = args.hasArg(OPT_bitcode_bundle);
   config->emitDataInCodeInfo =
       args.hasFlag(OPT_data_in_code_info, OPT_no_data_in_code_info, true);
+  config->emitChainedFixups = shouldEmitChainedFixups(args);
+  config->emitInitOffsets =
+      config->emitChainedFixups || args.hasArg(OPT_init_offsets);
   config->icfLevel = getICFLevel(args);
-  config->dedupLiterals = args.hasArg(OPT_deduplicate_literals) ||
-                          config->icfLevel != ICFLevel::none;
+  config->dedupStrings =
+      args.hasFlag(OPT_deduplicate_strings, OPT_no_deduplicate_strings, true);
+  config->deadStripDuplicates = args.hasArg(OPT_dead_strip_duplicates);
+  config->warnDylibInstallName = args.hasFlag(
+      OPT_warn_dylib_install_name, OPT_no_warn_dylib_install_name, false);
+  config->ignoreOptimizationHints = args.hasArg(OPT_ignore_optimization_hints);
+  config->callGraphProfileSort = args.hasFlag(
+      OPT_call_graph_profile_sort, OPT_no_call_graph_profile_sort, true);
+  config->printSymbolOrder = args.getLastArgValue(OPT_print_symbol_order_eq);
+  config->forceExactCpuSubtypeMatch =
+      getenv("LD_DYLIB_CPU_SUBTYPES_MUST_MATCH");
+  config->objcStubsMode = getObjCStubsMode(args);
+  config->ignoreAutoLink = args.hasArg(OPT_ignore_auto_link);
+  for (const Arg *arg : args.filtered(OPT_ignore_auto_link_option))
+    config->ignoreAutoLinkOptions.insert(arg->getValue());
+  config->strictAutoLink = args.hasArg(OPT_strict_auto_link);
+
+  for (const Arg *arg : args.filtered(OPT_alias)) {
+    config->aliasedSymbols.push_back(
+        std::make_pair(arg->getValue(0), arg->getValue(1)));
+  }
 
   // FIXME: Add a commandline flag for this too.
-  config->zeroModTime = getenv("ZERO_AR_DATE");
+  if (const char *zero = getenv("ZERO_AR_DATE"))
+    config->zeroModTime = strcmp(zero, "0") != 0;
 
-  std::array<PlatformKind, 3> encryptablePlatforms{
-      PlatformKind::iOS, PlatformKind::watchOS, PlatformKind::tvOS};
+  std::array<PlatformType, 3> encryptablePlatforms{
+      PLATFORM_IOS, PLATFORM_WATCHOS, PLATFORM_TVOS};
   config->emitEncryptionInfo =
       args.hasFlag(OPT_encryptable, OPT_no_encryption,
                    is_contained(encryptablePlatforms, config->platform()));
@@ -1249,8 +1640,10 @@ bool macho::link(ArrayRef<const char *> argsArr, bool canExitEarly,
 #endif
 
   if (const Arg *arg = args.getLastArg(OPT_install_name)) {
-    if (config->outputType != MH_DYLIB)
-      warn(arg->getAsString(args) + ": ignored, only has effect with -dylib");
+    if (config->warnDylibInstallName && config->outputType != MH_DYLIB)
+      warn(
+          arg->getAsString(args) +
+          ": ignored, only has effect with -dylib [--warn-dylib-install-name]");
     else
       config->installName = arg->getValue();
   } else if (config->outputType == MH_DYLIB) {
@@ -1330,45 +1723,96 @@ bool macho::link(ArrayRef<const char *> argsArr, bool canExitEarly,
     config->segmentProtections.push_back({segName, maxProt, initProt});
   }
 
+  config->hasExplicitExports =
+      args.hasArg(OPT_no_exported_symbols) ||
+      args.hasArgNoClaim(OPT_exported_symbol, OPT_exported_symbols_list);
   handleSymbolPatterns(args, config->exportedSymbols, OPT_exported_symbol,
                        OPT_exported_symbols_list);
   handleSymbolPatterns(args, config->unexportedSymbols, OPT_unexported_symbol,
                        OPT_unexported_symbols_list);
-  if (!config->exportedSymbols.empty() && !config->unexportedSymbols.empty()) {
-    error("cannot use both -exported_symbol* and -unexported_symbol* options\n"
-          ">>> ignoring unexports");
-    config->unexportedSymbols.clear();
+  if (config->hasExplicitExports && !config->unexportedSymbols.empty())
+    error("cannot use both -exported_symbol* and -unexported_symbol* options");
+
+  if (args.hasArg(OPT_no_exported_symbols) && !config->exportedSymbols.empty())
+    error("cannot use both -exported_symbol* and -no_exported_symbols options");
+
+  // Imitating LD64's:
+  // -non_global_symbols_no_strip_list and -non_global_symbols_strip_list can't
+  // both be present.
+  // But -x can be used with either of these two, in which case, the last arg
+  // takes effect.
+  // (TODO: This is kind of confusing - considering disallowing using them
+  // together for a more straightforward behaviour)
+  {
+    bool includeLocal = false;
+    bool excludeLocal = false;
+    for (const Arg *arg :
+         args.filtered(OPT_x, OPT_non_global_symbols_no_strip_list,
+                       OPT_non_global_symbols_strip_list)) {
+      switch (arg->getOption().getID()) {
+      case OPT_x:
+        config->localSymbolsPresence = SymtabPresence::None;
+        break;
+      case OPT_non_global_symbols_no_strip_list:
+        if (excludeLocal) {
+          error("cannot use both -non_global_symbols_no_strip_list and "
+                "-non_global_symbols_strip_list");
+        } else {
+          includeLocal = true;
+          config->localSymbolsPresence = SymtabPresence::SelectivelyIncluded;
+          parseSymbolPatternsFile(arg, config->localSymbolPatterns);
+        }
+        break;
+      case OPT_non_global_symbols_strip_list:
+        if (includeLocal) {
+          error("cannot use both -non_global_symbols_no_strip_list and "
+                "-non_global_symbols_strip_list");
+        } else {
+          excludeLocal = true;
+          config->localSymbolsPresence = SymtabPresence::SelectivelyExcluded;
+          parseSymbolPatternsFile(arg, config->localSymbolPatterns);
+        }
+        break;
+      default:
+        llvm_unreachable("unexpected option");
+      }
+    }
   }
   // Explicitly-exported literal symbols must be defined, but might
-  // languish in an archive if unreferenced elsewhere. Light a fire
-  // under those lazy symbols!
+  // languish in an archive if unreferenced elsewhere or if they are in the
+  // non-global strip list. Light a fire under those lazy symbols!
   for (const CachedHashStringRef &cachedName : config->exportedSymbols.literals)
     symtab->addUndefined(cachedName.val(), /*file=*/nullptr,
                          /*isWeakRef=*/false);
 
+  for (const Arg *arg : args.filtered(OPT_why_live))
+    config->whyLive.insert(arg->getValue());
+  if (!config->whyLive.empty() && !config->deadStrip)
+    warn("-why_live has no effect without -dead_strip, ignoring");
+
   config->saveTemps = args.hasArg(OPT_save_temps);
 
   config->adhocCodesign = args.hasFlag(
       OPT_adhoc_codesign, OPT_no_adhoc_codesign,
-      (config->arch() == AK_arm64 || config->arch() == AK_arm64e) &&
-          config->platform() == PlatformKind::macOS);
+      shouldAdhocSignByDefault(config->arch(), config->platform()));
 
   if (args.hasArg(OPT_v)) {
-    message(getLLDVersion());
+    message(getLLDVersion(), lld::errs());
     message(StringRef("Library search paths:") +
-            (config->librarySearchPaths.empty()
-                 ? ""
-                 : "\n\t" + join(config->librarySearchPaths, "\n\t")));
+                (config->librarySearchPaths.empty()
+                     ? ""
+                     : "\n\t" + join(config->librarySearchPaths, "\n\t")),
+            lld::errs());
     message(StringRef("Framework search paths:") +
-            (config->frameworkSearchPaths.empty()
-                 ? ""
-                 : "\n\t" + join(config->frameworkSearchPaths, "\n\t")));
+                (config->frameworkSearchPaths.empty()
+                     ? ""
+                     : "\n\t" + join(config->frameworkSearchPaths, "\n\t")),
+            lld::errs());
   }
 
   config->progName = argsArr[0];
 
-  config->timeTraceEnabled = args.hasArg(
-      OPT_time_trace, OPT_time_trace_granularity_eq, OPT_time_trace_file_eq);
+  config->timeTraceEnabled = args.hasArg(OPT_time_trace_eq);
   config->timeTraceGranularity =
       args::getInteger(args, OPT_time_trace_granularity_eq, 500);
 
@@ -1382,11 +1826,6 @@ bool macho::link(ArrayRef<const char *> argsArr, bool canExitEarly,
     initLLVM(); // must be run before any call to addFile()
     createFiles(args);
 
-    config->isPic = config->outputType == MH_DYLIB ||
-                    config->outputType == MH_BUNDLE ||
-                    (config->outputType == MH_EXECUTE &&
-                     args.hasFlag(OPT_pie, OPT_no_pie, true));
-
     // Now that all dylibs have been loaded, search for those that should be
     // re-exported.
     {
@@ -1407,60 +1846,71 @@ bool macho::link(ArrayRef<const char *> argsArr, bool canExitEarly,
         reexportHandler(arg, extensions);
     }
 
+    cl::ResetAllOptionOccurrences();
+
     // Parse LTO options.
     if (const Arg *arg = args.getLastArg(OPT_mcpu))
-      parseClangOption(saver.save("-mcpu=" + StringRef(arg->getValue())),
+      parseClangOption(saver().save("-mcpu=" + StringRef(arg->getValue())),
                        arg->getSpelling());
 
-    for (const Arg *arg : args.filtered(OPT_mllvm))
+    for (const Arg *arg : args.filtered(OPT_mllvm)) {
       parseClangOption(arg->getValue(), arg->getSpelling());
+      config->mllvmOpts.emplace_back(arg->getValue());
+    }
 
-    compileBitcodeFiles();
+    createSyntheticSections();
+    createSyntheticSymbols();
+    addSynthenticMethnames();
+
+    createAliases();
+    // If we are in "explicit exports" mode, hide everything that isn't
+    // explicitly exported. Do this before running LTO so that LTO can better
+    // optimize.
+    handleExplicitExports();
+
+    bool didCompileBitcodeFiles = compileBitcodeFiles();
+
+    // If --thinlto-index-only is given, we should create only "index
+    // files" and not object files. Index file creation is already done
+    // in compileBitcodeFiles, so we are done if that's the case.
+    if (config->thinLTOIndexOnly)
+      return errorCount() == 0;
+
+    // LTO may emit a non-hidden (extern) object file symbol even if the
+    // corresponding bitcode symbol is hidden. In particular, this happens for
+    // cross-module references to hidden symbols under ThinLTO. Thus, if we
+    // compiled any bitcode files, we must redo the symbol hiding.
+    if (didCompileBitcodeFiles)
+      handleExplicitExports();
     replaceCommonSymbols();
 
     StringRef orderFile = args.getLastArgValue(OPT_order_file);
     if (!orderFile.empty())
-      parseOrderFile(orderFile);
+      priorityBuilder.parseOrderFile(orderFile);
 
     referenceStubBinder();
 
     // FIXME: should terminate the link early based on errors encountered so
     // far?
 
-    createSyntheticSections();
-    createSyntheticSymbols();
-
-    if (!config->exportedSymbols.empty()) {
-      for (Symbol *sym : symtab->getSymbols()) {
-        if (auto *defined = dyn_cast<Defined>(sym)) {
-          StringRef symbolName = defined->getName();
-          if (config->exportedSymbols.match(symbolName)) {
-            if (defined->privateExtern) {
-              error("cannot export hidden symbol " + symbolName +
-                    "\n>>> defined in " + toString(defined->getFile()));
-            }
-          } else {
-            defined->privateExtern = true;
-          }
-        }
-      }
-    } else if (!config->unexportedSymbols.empty()) {
-      for (Symbol *sym : symtab->getSymbols())
-        if (auto *defined = dyn_cast<Defined>(sym))
-          if (config->unexportedSymbols.match(defined->getName()))
-            defined->privateExtern = true;
-    }
-
     for (const Arg *arg : args.filtered(OPT_sectcreate)) {
       StringRef segName = arg->getValue(0);
       StringRef sectName = arg->getValue(1);
       StringRef fileName = arg->getValue(2);
-      Optional<MemoryBufferRef> buffer = readFile(fileName);
+      std::optional<MemoryBufferRef> buffer = readFile(fileName);
       if (buffer)
         inputFiles.insert(make<OpaqueFile>(*buffer, segName, sectName));
     }
 
+    for (const Arg *arg : args.filtered(OPT_add_empty_section)) {
+      StringRef segName = arg->getValue(0);
+      StringRef sectName = arg->getValue(1);
+      inputFiles.insert(make<OpaqueFile>(MemoryBufferRef(), segName, sectName));
+    }
+
     gatherInputSections();
+    if (config->callGraphProfileSort)
+      priorityBuilder.extractCallGraphProfile();
 
     if (config->deadStrip)
       markLive();
@@ -1468,8 +1918,13 @@ bool macho::link(ArrayRef<const char *> argsArr, bool canExitEarly,
     // ICF assumes that all literals have been folded already, so we must run
     // foldIdenticalLiterals before foldIdenticalSections.
     foldIdenticalLiterals();
-    if (config->icfLevel != ICFLevel::none)
-      foldIdenticalSections();
+    if (config->icfLevel != ICFLevel::none) {
+      if (config->icfLevel == ICFLevel::safe)
+        markAddrSigSymbols();
+      foldIdenticalSections(/*onlyCfStrings=*/false);
+    } else if (config->dedupStrings) {
+      foldIdenticalSections(/*onlyCfStrings=*/true);
+    }
 
     // Write to an output file.
     if (target->wordSize == 8)
@@ -1481,18 +1936,15 @@ bool macho::link(ArrayRef<const char *> argsArr, bool canExitEarly,
   }
 
   if (config->timeTraceEnabled) {
-    if (auto E = timeTraceProfilerWrite(
-            args.getLastArgValue(OPT_time_trace_file_eq).str(),
-            config->outputFile)) {
-      handleAllErrors(std::move(E),
-                      [&](const StringError &SE) { error(SE.getMessage()); });
-    }
+    checkError(timeTraceProfilerWrite(
+        args.getLastArgValue(OPT_time_trace_eq).str(), config->outputFile));
 
     timeTraceProfilerCleanup();
   }
 
-  if (canExitEarly)
-    exitLld(errorCount() ? 1 : 0);
+  if (errorCount() != 0 || config->strictAutoLink)
+    for (const auto &warning : missingAutolinkWarnings)
+      warn(warning);
 
-  return !errorCount();
+  return errorCount() == 0;
 }
index 10f307e..bed752f 100644 (file)
 #define LLD_MACHO_DRIVER_H
 
 #include "lld/Common/LLVM.h"
-#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/SetVector.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/MachO.h"
 #include "llvm/Option/OptTable.h"
 #include "llvm/Support/MemoryBuffer.h"
+#include <optional>
 
 #include <set>
 #include <type_traits>
 
-namespace llvm {
-namespace MachO {
-class InterfaceFile;
-enum class PlatformKind : unsigned;
-} // namespace MachO
-} // namespace llvm
-
-namespace lld {
-namespace macho {
+namespace lld::macho {
 
 class DylibFile;
 class InputFile;
 
-class MachOOptTable : public llvm::opt::OptTable {
+class MachOOptTable : public llvm::opt::GenericOptTable {
 public:
   MachOOptTable();
   llvm::opt::InputArgList parse(ArrayRef<const char *> argv);
@@ -52,14 +45,16 @@ void parseLCLinkerOption(InputFile *, unsigned argc, StringRef data);
 std::string createResponseFile(const llvm::opt::InputArgList &args);
 
 // Check for both libfoo.dylib and libfoo.tbd (in that order).
-llvm::Optional<std::string> resolveDylibPath(llvm::StringRef path);
+std::optional<StringRef> resolveDylibPath(llvm::StringRef path);
 
 DylibFile *loadDylib(llvm::MemoryBufferRef mbref, DylibFile *umbrella = nullptr,
-                     bool isBundleLoader = false);
+                     bool isBundleLoader = false,
+                     bool explicitlyLinked = false);
+void resetLoadedDylibs();
 
 // Search for all possible combinations of `{root}/{name}.{extension}`.
 // If \p extensions are not specified, then just search for `{root}/{name}`.
-llvm::Optional<llvm::StringRef>
+std::optional<llvm::StringRef>
 findPathCombination(const llvm::Twine &name,
                     const std::vector<llvm::StringRef> &roots,
                     ArrayRef<llvm::StringRef> extensions = {""});
@@ -68,17 +63,12 @@ findPathCombination(const llvm::Twine &name,
 // rerooted.
 llvm::StringRef rerootPath(llvm::StringRef path);
 
-llvm::Optional<InputFile *> loadArchiveMember(MemoryBufferRef, uint32_t modTime,
-                                              StringRef archiveName,
-                                              bool objCOnly,
-                                              uint64_t offsetInArchive);
-
 uint32_t getModTime(llvm::StringRef path);
 
 void printArchiveMemberLoad(StringRef reason, const InputFile *);
 
 // Map simulator platforms to their underlying device platform.
-llvm::MachO::PlatformKind removeSimulator(llvm::MachO::PlatformKind platform);
+llvm::MachO::PlatformType removeSimulator(llvm::MachO::PlatformType platform);
 
 // Helper class to export dependency info.
 class DependencyTracker {
@@ -91,9 +81,8 @@ public:
       notFounds.insert(path.str());
   }
 
-  // Writes the dependencies to specified path.
-  // The content is sorted by its Op Code, then within each section,
-  // alphabetical order.
+  // Writes the dependencies to specified path. The content is first sorted by
+  // OpCode and then by the filename (in alphabetical order).
   void write(llvm::StringRef version,
              const llvm::SetVector<InputFile *> &inputs,
              llvm::StringRef output);
@@ -119,9 +108,8 @@ private:
   std::set<std::string> notFounds;
 };
 
-extern DependencyTracker *depTracker;
+extern std::unique_ptr<DependencyTracker> depTracker;
 
-} // namespace macho
-} // namespace lld
+} // namespace lld::macho
 
 #endif
index fc25182..d1f8ec2 100644 (file)
 #include "Target.h"
 
 #include "lld/Common/Args.h"
-#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Memory.h"
+#include "lld/Common/CommonLinkerContext.h"
 #include "lld/Common/Reproduce.h"
 #include "llvm/ADT/CachedHashString.h"
 #include "llvm/ADT/DenseMap.h"
-#include "llvm/Bitcode/BitcodeReader.h"
 #include "llvm/LTO/LTO.h"
 #include "llvm/Option/Arg.h"
 #include "llvm/Option/ArgList.h"
@@ -37,12 +35,15 @@ using namespace lld;
 using namespace lld::macho;
 
 // Create prefix string literals used in Options.td
-#define PREFIX(NAME, VALUE) const char *NAME[] = VALUE;
+#define PREFIX(NAME, VALUE)                                                    \
+  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
+  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
+                                                std::size(NAME##_init) - 1);
 #include "Options.inc"
 #undef PREFIX
 
 // Create table mapping all options defined in Options.td
-static const OptTable::Info optInfo[] = {
+static constexpr OptTable::Info optInfo[] = {
 #define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12)      \
   {X1, X2, X10,         X11,         OPT_##ID, Option::KIND##Class,            \
    X9, X8, OPT_##GROUP, OPT_##ALIAS, X7,       X12},
@@ -50,7 +51,7 @@ static const OptTable::Info optInfo[] = {
 #undef OPTION
 };
 
-MachOOptTable::MachOOptTable() : OptTable(optInfo) {}
+MachOOptTable::MachOOptTable() : GenericOptTable(optInfo) {}
 
 // Set color diagnostics according to --color-diagnostics={auto,always,never}
 // or --no-color-diagnostics flags.
@@ -83,12 +84,13 @@ InputArgList MachOOptTable::parse(ArrayRef<const char *> argv) {
 
   // Expand response files (arguments in the form of @<filename>)
   // and then parse the argument again.
-  cl::ExpandResponseFiles(saver, cl::TokenizeGNUCommandLine, vec);
+  cl::ExpandResponseFiles(saver(), cl::TokenizeGNUCommandLine, vec);
   InputArgList args = ParseArgs(vec, missingIndex, missingCount);
 
   // Handle -fatal_warnings early since it converts missing argument warnings
   // to errors.
   errorHandler().fatalWarnings = args.hasArg(OPT_fatal_warnings);
+  errorHandler().suppressWarnings = args.hasArg(OPT_w);
 
   if (missingCount)
     error(Twine(args.getArgString(missingIndex)) + ": missing argument");
@@ -145,12 +147,13 @@ std::string macho::createResponseFile(const InputArgList &args) {
       os << "-o " << quote(path::filename(arg->getValue())) << "\n";
       break;
     case OPT_filelist:
-      if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
+      if (std::optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
         for (StringRef path : args::getLines(*buffer))
           os << quote(rewriteInputPath(path)) << "\n";
       break;
     case OPT_force_load:
     case OPT_weak_library:
+    case OPT_load_hidden:
       os << arg->getSpelling() << " "
          << quote(rewriteInputPath(arg->getValue())) << "\n";
       break;
@@ -159,7 +162,6 @@ std::string macho::createResponseFile(const InputArgList &args) {
     case OPT_bundle_loader:
     case OPT_exported_symbols_list:
     case OPT_order_file:
-    case OPT_rpath:
     case OPT_syslibroot:
     case OPT_unexported_symbols_list:
       os << arg->getSpelling() << " " << quote(rewritePath(arg->getValue()))
@@ -184,20 +186,20 @@ static void searchedDylib(const Twine &path, bool found) {
     depTracker->logFileNotFound(path);
 }
 
-Optional<std::string> macho::resolveDylibPath(StringRef dylibPath) {
+std::optional<StringRef> macho::resolveDylibPath(StringRef dylibPath) {
   // TODO: if a tbd and dylib are both present, we should check to make sure
   // they are consistent.
-  bool dylibExists = fs::exists(dylibPath);
-  searchedDylib(dylibPath, dylibExists);
-  if (dylibExists)
-    return std::string(dylibPath);
-
   SmallString<261> tbdPath = dylibPath;
   path::replace_extension(tbdPath, ".tbd");
   bool tbdExists = fs::exists(tbdPath);
   searchedDylib(tbdPath, tbdExists);
   if (tbdExists)
-    return std::string(tbdPath);
+    return saver().save(tbdPath.str());
+
+  bool dylibExists = fs::exists(dylibPath);
+  searchedDylib(dylibPath, dylibExists);
+  if (dylibExists)
+    return saver().save(dylibPath);
   return {};
 }
 
@@ -206,11 +208,14 @@ Optional<std::string> macho::resolveDylibPath(StringRef dylibPath) {
 static DenseMap<CachedHashStringRef, DylibFile *> loadedDylibs;
 
 DylibFile *macho::loadDylib(MemoryBufferRef mbref, DylibFile *umbrella,
-                            bool isBundleLoader) {
+                            bool isBundleLoader, bool explicitlyLinked) {
   CachedHashStringRef path(mbref.getBufferIdentifier());
   DylibFile *&file = loadedDylibs[path];
-  if (file)
+  if (file) {
+    if (explicitlyLinked)
+      file->setExplicitlyLinked();
     return file;
+  }
 
   DylibFile *newFile;
   file_magic magic = identify_magic(mbref.getBuffer());
@@ -221,7 +226,8 @@ DylibFile *macho::loadDylib(MemoryBufferRef mbref, DylibFile *umbrella,
             ": " + toString(result.takeError()));
       return nullptr;
     }
-    file = make<DylibFile>(**result, umbrella, isBundleLoader);
+    file =
+        make<DylibFile>(**result, umbrella, isBundleLoader, explicitlyLinked);
 
     // parseReexports() can recursively call loadDylib(). That's fine since
     // we wrote the DylibFile we just loaded to the loadDylib cache via the
@@ -236,7 +242,7 @@ DylibFile *macho::loadDylib(MemoryBufferRef mbref, DylibFile *umbrella,
            magic == file_magic::macho_dynamically_linked_shared_lib_stub ||
            magic == file_magic::macho_executable ||
            magic == file_magic::macho_bundle);
-    file = make<DylibFile>(mbref, umbrella, isBundleLoader);
+    file = make<DylibFile>(mbref, umbrella, isBundleLoader, explicitlyLinked);
 
     // parseLoadCommands() can also recursively call loadDylib(). See comment
     // in previous block for why this means we must copy `file` here.
@@ -247,7 +253,9 @@ DylibFile *macho::loadDylib(MemoryBufferRef mbref, DylibFile *umbrella,
   return newFile;
 }
 
-Optional<StringRef>
+void macho::resetLoadedDylibs() { loadedDylibs.clear(); }
+
+std::optional<StringRef>
 macho::findPathCombination(const Twine &name,
                            const std::vector<StringRef> &roots,
                            ArrayRef<StringRef> extensions) {
@@ -260,7 +268,7 @@ macho::findPathCombination(const Twine &name,
       bool exists = fs::exists(location);
       searchedDylib(location, exists);
       if (exists)
-        return saver.save(location.str());
+        return saver().save(location.str());
     }
   }
   return {};
@@ -270,37 +278,13 @@ StringRef macho::rerootPath(StringRef path) {
   if (!path::is_absolute(path, path::Style::posix) || path.endswith(".o"))
     return path;
 
-  if (Optional<StringRef> rerootedPath =
+  if (std::optional<StringRef> rerootedPath =
           findPathCombination(path, config->systemLibraryRoots))
     return *rerootedPath;
 
   return path;
 }
 
-Optional<InputFile *> macho::loadArchiveMember(MemoryBufferRef mb,
-                                               uint32_t modTime,
-                                               StringRef archiveName,
-                                               bool objCOnly,
-                                               uint64_t offsetInArchive) {
-  if (config->zeroModTime)
-    modTime = 0;
-
-  switch (identify_magic(mb.getBuffer())) {
-  case file_magic::macho_object:
-    if (!objCOnly || hasObjCSection(mb))
-      return make<ObjFile>(mb, modTime, archiveName);
-    return None;
-  case file_magic::bitcode:
-    if (!objCOnly || check(isBitcodeContainingObjCCategory(mb)))
-      return make<BitcodeFile>(mb, archiveName, offsetInArchive);
-    return None;
-  default:
-    error(archiveName + ": archive member " + mb.getBufferIdentifier() +
-          " has unhandled file type");
-    return None;
-  }
-}
-
 uint32_t macho::getModTime(StringRef path) {
   if (config->zeroModTime)
     return 0;
index c142cc1..47dc51e 100644 (file)
@@ -20,15 +20,17 @@ using namespace llvm;
 std::unique_ptr<DwarfObject> DwarfObject::create(ObjFile *obj) {
   auto dObj = std::make_unique<DwarfObject>();
   bool hasDwarfInfo = false;
-  // LLD only needs to extract the source file path from the debug info, so we
-  // initialize DwarfObject with just the sections necessary to get that path.
-  // The debugger will locate the debug info via the object file paths that we
-  // emit in our STABS symbols, so we don't need to process & emit them
-  // ourselves.
+  // LLD only needs to extract the source file path and line numbers from the
+  // debug info, so we initialize DwarfObject with just the sections necessary
+  // to get that path. The debugger will locate the debug info via the object
+  // file paths that we emit in our STABS symbols, so we don't need to process &
+  // emit them ourselves.
   for (const InputSection *isec : obj->debugSections) {
     if (StringRef *s =
             StringSwitch<StringRef *>(isec->getName())
                 .Case(section_names::debugInfo, &dObj->infoSection.Data)
+                .Case(section_names::debugLine, &dObj->lineSection.Data)
+                .Case(section_names::debugStrOffs, &dObj->strOffsSection.Data)
                 .Case(section_names::debugAbbrev, &dObj->abbrevSection)
                 .Case(section_names::debugStr, &dObj->strSection)
                 .Default(nullptr)) {
index 119f277..31a9f82 100644 (file)
@@ -12,8 +12,7 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/DebugInfo/DWARF/DWARFObject.h"
 
-namespace lld {
-namespace macho {
+namespace lld::macho {
 
 class ObjFile;
 
@@ -23,10 +22,10 @@ class DwarfObject final : public llvm::DWARFObject {
 public:
   bool isLittleEndian() const override { return true; }
 
-  llvm::Optional<llvm::RelocAddrEntry> find(const llvm::DWARFSection &sec,
-                                            uint64_t pos) const override {
+  std::optional<llvm::RelocAddrEntry> find(const llvm::DWARFSection &sec,
+                                           uint64_t pos) const override {
     // TODO: implement this
-    return llvm::None;
+    return std::nullopt;
   }
 
   void forEachInfoSections(
@@ -37,17 +36,26 @@ public:
   llvm::StringRef getAbbrevSection() const override { return abbrevSection; }
   llvm::StringRef getStrSection() const override { return strSection; }
 
+  llvm::DWARFSection const &getLineSection() const override {
+    return lineSection;
+  }
+
+  llvm::DWARFSection const &getStrOffsetsSection() const override {
+    return strOffsSection;
+  }
+
   // Returns an instance of DwarfObject if the given object file has the
   // relevant DWARF debug sections.
   static std::unique_ptr<DwarfObject> create(ObjFile *);
 
 private:
   llvm::DWARFSection infoSection;
+  llvm::DWARFSection lineSection;
+  llvm::DWARFSection strOffsSection;
   llvm::StringRef abbrevSection;
   llvm::StringRef strSection;
 };
 
-} // namespace macho
-} // namespace lld
+} // namespace lld::macho
 
 #endif
diff --git a/gnu/llvm/lld/MachO/EhFrame.cpp b/gnu/llvm/lld/MachO/EhFrame.cpp
new file mode 100644 (file)
index 0000000..55a85f3
--- /dev/null
@@ -0,0 +1,140 @@
+//===- EhFrame.cpp --------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "EhFrame.h"
+#include "InputFiles.h"
+
+#include "lld/Common/ErrorHandler.h"
+#include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/Support/Endian.h"
+
+using namespace llvm;
+using namespace lld;
+using namespace lld::macho;
+using namespace llvm::support::endian;
+
+uint64_t EhReader::readLength(size_t *off) const {
+  const size_t errOff = *off;
+  if (*off + 4 > data.size())
+    failOn(errOff, "CIE/FDE too small");
+  uint64_t len = read32le(data.data() + *off);
+  *off += 4;
+  if (len == dwarf::DW_LENGTH_DWARF64) {
+    // FIXME: test this DWARF64 code path
+    if (*off + 8 > data.size())
+      failOn(errOff, "CIE/FDE too small");
+    len = read64le(data.data() + *off);
+    *off += 8;
+  }
+  if (*off + len > data.size())
+    failOn(errOff, "CIE/FDE extends past the end of the section");
+  return len;
+}
+
+void EhReader::skipValidLength(size_t *off) const {
+  uint32_t len = read32le(data.data() + *off);
+  *off += 4;
+  if (len == dwarf::DW_LENGTH_DWARF64)
+    *off += 8;
+}
+
+// Read a byte and advance off by one byte.
+uint8_t EhReader::readByte(size_t *off) const {
+  if (*off + 1 > data.size())
+    failOn(*off, "unexpected end of CIE/FDE");
+  return data[(*off)++];
+}
+
+uint32_t EhReader::readU32(size_t *off) const {
+  if (*off + 4 > data.size())
+    failOn(*off, "unexpected end of CIE/FDE");
+  uint32_t v = read32le(data.data() + *off);
+  *off += 4;
+  return v;
+}
+
+uint64_t EhReader::readPointer(size_t *off, uint8_t size) const {
+  if (*off + size > data.size())
+    failOn(*off, "unexpected end of CIE/FDE");
+  uint64_t v;
+  if (size == 8)
+    v = read64le(data.data() + *off);
+  else {
+    assert(size == 4);
+    v = read32le(data.data() + *off);
+  }
+  *off += size;
+  return v;
+}
+
+// Read a null-terminated string.
+StringRef EhReader::readString(size_t *off) const {
+  if (*off > data.size())
+    failOn(*off, "corrupted CIE (failed to read string)");
+  const size_t maxlen = data.size() - *off;
+  auto *c = reinterpret_cast<const char *>(data.data() + *off);
+  size_t len = strnlen(c, maxlen);
+  if (len == maxlen) // we failed to find the null terminator
+    failOn(*off, "corrupted CIE (failed to read string)");
+  *off += len + 1; // skip the null byte too
+  return StringRef(c, len);
+}
+
+void EhReader::skipLeb128(size_t *off) const {
+  const size_t errOff = *off;
+  while (*off < data.size()) {
+    uint8_t val = data[(*off)++];
+    if ((val & 0x80) == 0)
+      return;
+  }
+  failOn(errOff, "corrupted CIE (failed to read LEB128)");
+}
+
+void EhReader::failOn(size_t errOff, const Twine &msg) const {
+  fatal(toString(file) + ":(__eh_frame+0x" +
+        Twine::utohexstr(dataOff + errOff) + "): " + msg);
+}
+
+/*
+ * Create a pair of relocs to write the value of:
+ *   `b - (offset + a)` if Invert == false
+ *   `(a + offset) - b` if Invert == true
+ */
+template <bool Invert = false>
+static void createSubtraction(PointerUnion<Symbol *, InputSection *> a,
+                              PointerUnion<Symbol *, InputSection *> b,
+                              uint64_t off, uint8_t length,
+                              SmallVectorImpl<Reloc> *newRelocs) {
+  auto subtrahend = a;
+  auto minuend = b;
+  if (Invert)
+    std::swap(subtrahend, minuend);
+  assert(subtrahend.is<Symbol *>());
+  Reloc subtrahendReloc(target->subtractorRelocType, /*pcrel=*/false, length,
+                        off, /*addend=*/0, subtrahend);
+  Reloc minuendReloc(target->unsignedRelocType, /*pcrel=*/false, length, off,
+                     (Invert ? 1 : -1) * off, minuend);
+  newRelocs->push_back(subtrahendReloc);
+  newRelocs->push_back(minuendReloc);
+}
+
+void EhRelocator::makePcRel(uint64_t off,
+                            PointerUnion<Symbol *, InputSection *> target,
+                            uint8_t length) {
+  createSubtraction(isec->symbols[0], target, off, length, &newRelocs);
+}
+
+void EhRelocator::makeNegativePcRel(
+    uint64_t off, PointerUnion<Symbol *, InputSection *> target,
+    uint8_t length) {
+  createSubtraction</*Invert=*/true>(isec, target, off, length, &newRelocs);
+}
+
+void EhRelocator::commit() {
+  isec->relocs.insert(isec->relocs.end(), newRelocs.begin(), newRelocs.end());
+}
diff --git a/gnu/llvm/lld/MachO/EhFrame.h b/gnu/llvm/lld/MachO/EhFrame.h
new file mode 100644 (file)
index 0000000..d5afab3
--- /dev/null
@@ -0,0 +1,116 @@
+//===- EhFrame.h ------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_MACHO_EH_FRAME_H
+#define LLD_MACHO_EH_FRAME_H
+
+#include "InputSection.h"
+#include "Relocations.h"
+
+#include "lld/Common/LLVM.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+
+/*
+ * NOTE: The main bulk of the EH frame parsing logic is in InputFiles.cpp as it
+ * is closely coupled with other file parsing logic; EhFrame.h just contains a
+ * few helpers.
+ */
+
+/*
+ * === The EH frame format ===
+ *
+ * EH frames can either be Common Information Entries (CIEs) or Frame
+ * Description Entries (FDEs). CIEs contain information that is common amongst
+ * several FDEs. Each FDE contains a pointer to its CIE. Thus all the EH frame
+ * entries together form a forest of two-level trees, with CIEs as the roots
+ * and FDEs as the leaves. Note that a CIE must precede the FDEs which point
+ * to it.
+ *
+ * A CIE comprises the following fields in order:
+ * 1.   Length of the entry (4 or 12 bytes)
+ * 2.   CIE offset (4 bytes; always 0 for CIEs)
+ * 3.   CIE version (byte)
+ * 4.   Null-terminated augmentation string
+ * 5-8. LEB128 values that we don't care about
+ * 9.   Augmentation data, to be interpreted using the aug string
+ * 10.  DWARF instructions (ignored by LLD)
+ *
+ * An FDE comprises of the following:
+ * 1. Length of the entry (4 or 12 bytes)
+ * 2. CIE offset (4 bytes pcrel offset that points backwards to this FDE's CIE)
+ * 3. Function address (pointer-sized pcrel offset)
+ * 4. (std::optional) Augmentation data length
+ * 5. (std::optional) LSDA address (pointer-sized pcrel offset)
+ * 6. DWARF instructions (ignored by LLD)
+ */
+namespace lld::macho {
+
+class EhReader {
+public:
+  EhReader(const ObjFile *file, ArrayRef<uint8_t> data, size_t dataOff)
+      : file(file), data(data), dataOff(dataOff) {}
+  size_t size() const { return data.size(); }
+  // Read and validate the length field.
+  uint64_t readLength(size_t *off) const;
+  // Skip the length field without doing validation.
+  void skipValidLength(size_t *off) const;
+  uint8_t readByte(size_t *off) const;
+  uint32_t readU32(size_t *off) const;
+  uint64_t readPointer(size_t *off, uint8_t size) const;
+  StringRef readString(size_t *off) const;
+  void skipLeb128(size_t *off) const;
+  void failOn(size_t errOff, const Twine &msg) const;
+
+private:
+  const ObjFile *file;
+  ArrayRef<uint8_t> data;
+  // The offset of the data array within its section. Used only for error
+  // reporting.
+  const size_t dataOff;
+};
+
+// The EH frame format, when emitted by llvm-mc, consists of a number of
+// "abs-ified" relocations, i.e. relocations that are implicitly encoded as
+// pcrel offsets in the section data. The offsets refer to the locations of
+// symbols in the input object file. When we ingest these EH frames, we convert
+// these implicit relocations into explicit Relocs.
+//
+// These pcrel relocations are semantically similar to X86_64_RELOC_SIGNED_4.
+// However, we need this operation to be cross-platform, and ARM does not have a
+// similar relocation that is applicable. We therefore use the more verbose (but
+// more generic) subtractor relocation to encode these pcrel values. ld64
+// appears to do something similar -- its `-r` output contains these explicit
+// subtractor relocations.
+class EhRelocator {
+public:
+  EhRelocator(InputSection *isec) : isec(isec) {}
+
+  // For the next two methods, let `PC` denote `isec address + off`.
+  // Create relocs writing the value of target - PC to PC.
+  void makePcRel(uint64_t off,
+                 llvm::PointerUnion<Symbol *, InputSection *> target,
+                 uint8_t length);
+  // Create relocs writing the value of PC - target to PC.
+  void makeNegativePcRel(uint64_t off,
+                         llvm::PointerUnion<Symbol *, InputSection *> target,
+                         uint8_t length);
+  // Insert the new relocations into isec->relocs.
+  void commit();
+
+private:
+  InputSection *isec;
+  // Insert new relocs here so that we don't invalidate iterators into the
+  // existing relocs vector.
+  SmallVector<Reloc, 6> newRelocs;
+};
+
+} // namespace lld::macho
+
+#endif
index 372690a..3ca8d35 100644 (file)
@@ -39,9 +39,9 @@
 
 #include "lld/Common/ErrorHandler.h"
 #include "lld/Common/Memory.h"
-#include "llvm/ADT/Optional.h"
 #include "llvm/BinaryFormat/MachO.h"
 #include "llvm/Support/LEB128.h"
+#include <optional>
 
 using namespace llvm;
 using namespace lld;
@@ -81,7 +81,7 @@ struct ExportInfo {
 
 struct macho::TrieNode {
   std::vector<Edge> edges;
-  Optional<ExportInfo> info;
+  std::optional<ExportInfo> info;
   // Estimated offset from the start of the serialized trie to the current node.
   // This will converge to the true offset when updateOffset() is run to a
   // fixpoint.
@@ -145,8 +145,13 @@ void TrieNode::writeTo(uint8_t *buf) const {
   }
 }
 
+TrieBuilder::~TrieBuilder() {
+  for (TrieNode *node : nodes)
+    delete node;
+}
+
 TrieNode *TrieBuilder::makeNode() {
-  auto *node = make<TrieNode>();
+  auto *node = new TrieNode();
   nodes.emplace_back(node);
   return node;
 }
index a43f4f2..aa7e3b0 100644 (file)
 
 #include <vector>
 
-namespace lld {
-namespace macho {
+namespace lld::macho {
 
 struct TrieNode;
 class Symbol;
 
 class TrieBuilder {
 public:
+  ~TrieBuilder();
   void setImageBase(uint64_t addr) { imageBase = addr; }
   void addSymbol(const Symbol &sym) { exported.push_back(&sym); }
   // Returns the size in bytes of the serialized trie.
@@ -43,7 +43,6 @@ using TrieEntryCallback =
 
 void parseTrie(const uint8_t *buf, size_t size, const TrieEntryCallback &);
 
-} // namespace macho
-} // namespace lld
+} // namespace lld::macho
 
 #endif
index 370a325..3dfc464 100644 (file)
@@ -8,12 +8,17 @@
 
 #include "ICF.h"
 #include "ConcatOutputSection.h"
+#include "Config.h"
 #include "InputSection.h"
+#include "SymbolTable.h"
 #include "Symbols.h"
 #include "UnwindInfoSection.h"
 
+#include "lld/Common/CommonLinkerContext.h"
+#include "llvm/Support/LEB128.h"
 #include "llvm/Support/Parallel.h"
 #include "llvm/Support/TimeProfiler.h"
+#include "llvm/Support/xxhash.h"
 
 #include <atomic>
 
@@ -21,23 +26,34 @@ using namespace llvm;
 using namespace lld;
 using namespace lld::macho;
 
+static constexpr bool verboseDiagnostics = false;
+
 class ICF {
 public:
   ICF(std::vector<ConcatInputSection *> &inputs);
-
   void run();
-  void segregate(size_t begin, size_t end,
-                 std::function<bool(const ConcatInputSection *,
-                                    const ConcatInputSection *)>
-                     equals);
+
+  using EqualsFn = bool (ICF::*)(const ConcatInputSection *,
+                                 const ConcatInputSection *);
+  void segregate(size_t begin, size_t end, EqualsFn);
   size_t findBoundary(size_t begin, size_t end);
   void forEachClassRange(size_t begin, size_t end,
-                         std::function<void(size_t, size_t)> func);
-  void forEachClass(std::function<void(size_t, size_t)> func);
+                         llvm::function_ref<void(size_t, size_t)> func);
+  void forEachClass(llvm::function_ref<void(size_t, size_t)> func);
+
+  bool equalsConstant(const ConcatInputSection *ia,
+                      const ConcatInputSection *ib);
+  bool equalsVariable(const ConcatInputSection *ia,
+                      const ConcatInputSection *ib);
 
   // ICF needs a copy of the inputs vector because its equivalence-class
   // segregation algorithm destroys the proper sequence.
   std::vector<ConcatInputSection *> icfInputs;
+
+  unsigned icfPass = 0;
+  std::atomic<bool> icfRepeat{false};
+  std::atomic<uint64_t> equalsConstantCount{0};
+  std::atomic<uint64_t> equalsVariableCount{0};
 };
 
 ICF::ICF(std::vector<ConcatInputSection *> &inputs) {
@@ -74,13 +90,12 @@ ICF::ICF(std::vector<ConcatInputSection *> &inputs) {
 // FIXME(gkm): implement keep-unique attributes
 // FIXME(gkm): implement address-significance tables for MachO object files
 
-static unsigned icfPass = 0;
-static std::atomic<bool> icfRepeat{false};
-
 // Compare "non-moving" parts of two ConcatInputSections, namely everything
 // except references to other ConcatInputSections.
-static bool equalsConstant(const ConcatInputSection *ia,
-                           const ConcatInputSection *ib) {
+bool ICF::equalsConstant(const ConcatInputSection *ia,
+                         const ConcatInputSection *ib) {
+  if (verboseDiagnostics)
+    ++equalsConstantCount;
   // We can only fold within the same OutputSection.
   if (ia->parent != ib->parent)
     return false;
@@ -99,31 +114,33 @@ static bool equalsConstant(const ConcatInputSection *ia,
       return false;
     if (ra.offset != rb.offset)
       return false;
-    if (ra.addend != rb.addend)
-      return false;
     if (ra.referent.is<Symbol *>() != rb.referent.is<Symbol *>())
       return false;
 
     InputSection *isecA, *isecB;
+
+    uint64_t valueA = 0;
+    uint64_t valueB = 0;
     if (ra.referent.is<Symbol *>()) {
       const auto *sa = ra.referent.get<Symbol *>();
       const auto *sb = rb.referent.get<Symbol *>();
       if (sa->kind() != sb->kind())
         return false;
-      if (isa<Defined>(sa)) {
-        const auto *da = cast<Defined>(sa);
-        const auto *db = cast<Defined>(sb);
-        if (da->isec && db->isec) {
-          isecA = da->isec;
-          isecB = db->isec;
-        } else {
-          assert(da->isAbsolute() && db->isAbsolute());
-          return da->value == db->value;
-        }
-      } else {
-        assert(isa<DylibSymbol>(sa));
-        return sa == sb;
+      // ICF runs before Undefineds are treated (and potentially converted into
+      // DylibSymbols).
+      if (isa<DylibSymbol>(sa) || isa<Undefined>(sa))
+        return sa == sb && ra.addend == rb.addend;
+      assert(isa<Defined>(sa));
+      const auto *da = cast<Defined>(sa);
+      const auto *db = cast<Defined>(sb);
+      if (!da->isec || !db->isec) {
+        assert(da->isAbsolute() && db->isAbsolute());
+        return da->value + ra.addend == db->value + rb.addend;
       }
+      isecA = da->isec;
+      valueA = da->value;
+      isecB = db->isec;
+      valueB = db->value;
     } else {
       isecA = ra.referent.get<InputSection *>();
       isecB = rb.referent.get<InputSection *>();
@@ -135,10 +152,20 @@ static bool equalsConstant(const ConcatInputSection *ia,
     assert(isecA->kind() == isecB->kind());
     // We will compare ConcatInputSection contents in equalsVariable.
     if (isa<ConcatInputSection>(isecA))
-      return true;
+      return ra.addend == rb.addend;
     // Else we have two literal sections. References to them are equal iff their
     // offsets in the output section are equal.
-    return isecA->getOffset(ra.addend) == isecB->getOffset(rb.addend);
+    if (ra.referent.is<Symbol *>())
+      // For symbol relocs, we compare the contents at the symbol address. We
+      // don't do `getOffset(value + addend)` because value + addend may not be
+      // a valid offset in the literal section.
+      return isecA->getOffset(valueA) == isecB->getOffset(valueB) &&
+             ra.addend == rb.addend;
+    else {
+      assert(valueA == 0 && valueB == 0);
+      // For section relocs, we compare the content at the section offset.
+      return isecA->getOffset(ra.addend) == isecB->getOffset(rb.addend);
+    }
   };
   return std::equal(ia->relocs.begin(), ia->relocs.end(), ib->relocs.begin(),
                     f);
@@ -146,10 +173,12 @@ static bool equalsConstant(const ConcatInputSection *ia,
 
 // Compare the "moving" parts of two ConcatInputSections -- i.e. everything not
 // handled by equalsConstant().
-static bool equalsVariable(const ConcatInputSection *ia,
-                           const ConcatInputSection *ib) {
+bool ICF::equalsVariable(const ConcatInputSection *ia,
+                         const ConcatInputSection *ib) {
+  if (verboseDiagnostics)
+    ++equalsVariableCount;
   assert(ia->relocs.size() == ib->relocs.size());
-  auto f = [](const Reloc &ra, const Reloc &rb) {
+  auto f = [this](const Reloc &ra, const Reloc &rb) {
     // We already filtered out mismatching values/addends in equalsConstant.
     if (ra.referent == rb.referent)
       return true;
@@ -176,8 +205,31 @@ static bool equalsVariable(const ConcatInputSection *ia,
     }
     return isecA->icfEqClass[icfPass % 2] == isecB->icfEqClass[icfPass % 2];
   };
-  return std::equal(ia->relocs.begin(), ia->relocs.end(), ib->relocs.begin(),
-                    f);
+  if (!std::equal(ia->relocs.begin(), ia->relocs.end(), ib->relocs.begin(), f))
+    return false;
+
+  // If there are symbols with associated unwind info, check that the unwind
+  // info matches. For simplicity, we only handle the case where there are only
+  // symbols at offset zero within the section (which is typically the case with
+  // .subsections_via_symbols.)
+  auto hasUnwind = [](Defined *d) { return d->unwindEntry != nullptr; };
+  auto itA = std::find_if(ia->symbols.begin(), ia->symbols.end(), hasUnwind);
+  auto itB = std::find_if(ib->symbols.begin(), ib->symbols.end(), hasUnwind);
+  if (itA == ia->symbols.end())
+    return itB == ib->symbols.end();
+  if (itB == ib->symbols.end())
+    return false;
+  const Defined *da = *itA;
+  const Defined *db = *itB;
+  if (da->unwindEntry->icfEqClass[icfPass % 2] !=
+          db->unwindEntry->icfEqClass[icfPass % 2] ||
+      da->value != 0 || db->value != 0)
+    return false;
+  auto isZero = [](Defined *d) { return d->value == 0; };
+  return std::find_if_not(std::next(itA), ia->symbols.end(), isZero) ==
+             ia->symbols.end() &&
+         std::find_if_not(std::next(itB), ib->symbols.end(), isZero) ==
+             ib->symbols.end();
 }
 
 // Find the first InputSection after BEGIN whose equivalence class differs
@@ -191,7 +243,7 @@ size_t ICF::findBoundary(size_t begin, size_t end) {
 
 // Invoke FUNC on subranges with matching equivalence class
 void ICF::forEachClassRange(size_t begin, size_t end,
-                            std::function<void(size_t, size_t)> func) {
+                            llvm::function_ref<void(size_t, size_t)> func) {
   while (begin < end) {
     size_t mid = findBoundary(begin, end);
     func(begin, mid);
@@ -201,7 +253,7 @@ void ICF::forEachClassRange(size_t begin, size_t end,
 
 // Split icfInputs into shards, then parallelize invocation of FUNC on subranges
 // with matching equivalence class
-void ICF::forEachClass(std::function<void(size_t, size_t)> func) {
+void ICF::forEachClass(llvm::function_ref<void(size_t, size_t)> func) {
   // Only use threads when the benefits outweigh the overhead.
   const size_t threadingThreshold = 1024;
   if (icfInputs.size() < threadingThreshold) {
@@ -218,10 +270,10 @@ void ICF::forEachClass(std::function<void(size_t, size_t)> func) {
   size_t boundaries[shards + 1];
   boundaries[0] = 0;
   boundaries[shards] = icfInputs.size();
-  parallelForEachN(1, shards, [&](size_t i) {
+  parallelFor(1, shards, [&](size_t i) {
     boundaries[i] = findBoundary((i - 1) * step, icfInputs.size());
   });
-  parallelForEachN(1, shards + 1, [&](size_t i) {
+  parallelFor(1, shards + 1, [&](size_t i) {
     if (boundaries[i - 1] < boundaries[i]) {
       forEachClassRange(boundaries[i - 1], boundaries[i], func);
     }
@@ -233,27 +285,28 @@ void ICF::run() {
   // Into each origin-section hash, combine all reloc referent section hashes.
   for (icfPass = 0; icfPass < 2; ++icfPass) {
     parallelForEach(icfInputs, [&](ConcatInputSection *isec) {
-      uint64_t hash = isec->icfEqClass[icfPass % 2];
+      uint32_t hash = isec->icfEqClass[icfPass % 2];
       for (const Reloc &r : isec->relocs) {
         if (auto *sym = r.referent.dyn_cast<Symbol *>()) {
-          if (auto *dylibSym = dyn_cast<DylibSymbol>(sym))
-            hash += dylibSym->stubsHelperIndex;
-          else if (auto *defined = dyn_cast<Defined>(sym)) {
+          if (auto *defined = dyn_cast<Defined>(sym)) {
             if (defined->isec) {
-              if (auto isec = dyn_cast<ConcatInputSection>(defined->isec))
-                hash += defined->value + isec->icfEqClass[icfPass % 2];
+              if (auto referentIsec =
+                      dyn_cast<ConcatInputSection>(defined->isec))
+                hash += defined->value + referentIsec->icfEqClass[icfPass % 2];
               else
                 hash += defined->isec->kind() +
                         defined->isec->getOffset(defined->value);
             } else {
               hash += defined->value;
             }
-          } else
-            llvm_unreachable("foldIdenticalSections symbol kind");
+          } else {
+            // ICF runs before Undefined diags
+            assert(isa<Undefined>(sym) || isa<DylibSymbol>(sym));
+          }
         }
       }
       // Set MSB to 1 to avoid collisions with non-hashed classes.
-      isec->icfEqClass[(icfPass + 1) % 2] = hash | (1ull << 63);
+      isec->icfEqClass[(icfPass + 1) % 2] = hash | (1ull << 31);
     });
   }
 
@@ -261,17 +314,22 @@ void ICF::run() {
       icfInputs, [](const ConcatInputSection *a, const ConcatInputSection *b) {
         return a->icfEqClass[0] < b->icfEqClass[0];
       });
-  forEachClass(
-      [&](size_t begin, size_t end) { segregate(begin, end, equalsConstant); });
+  forEachClass([&](size_t begin, size_t end) {
+    segregate(begin, end, &ICF::equalsConstant);
+  });
 
   // Split equivalence groups by comparing relocations until convergence
   do {
     icfRepeat = false;
     forEachClass([&](size_t begin, size_t end) {
-      segregate(begin, end, equalsVariable);
+      segregate(begin, end, &ICF::equalsVariable);
     });
   } while (icfRepeat);
   log("ICF needed " + Twine(icfPass) + " iterations");
+  if (verboseDiagnostics) {
+    log("equalsConstant() called " + Twine(equalsConstantCount) + " times");
+    log("equalsVariable() called " + Twine(equalsVariableCount) + " times");
+  }
 
   // Fold sections within equivalence classes
   forEachClass([&](size_t begin, size_t end) {
@@ -284,18 +342,15 @@ void ICF::run() {
 }
 
 // Split an equivalence class into smaller classes.
-void ICF::segregate(
-    size_t begin, size_t end,
-    std::function<bool(const ConcatInputSection *, const ConcatInputSection *)>
-        equals) {
+void ICF::segregate(size_t begin, size_t end, EqualsFn equals) {
   while (begin < end) {
     // Divide [begin, end) into two. Let mid be the start index of the
     // second group.
-    auto bound = std::stable_partition(icfInputs.begin() + begin + 1,
-                                       icfInputs.begin() + end,
-                                       [&](ConcatInputSection *isec) {
-                                         return equals(icfInputs[begin], isec);
-                                       });
+    auto bound = std::stable_partition(
+        icfInputs.begin() + begin + 1, icfInputs.begin() + end,
+        [&](ConcatInputSection *isec) {
+          return (this->*equals)(icfInputs[begin], isec);
+        });
     size_t mid = bound - icfInputs.begin();
 
     // Split [begin, end) into [begin, mid) and [mid, end). We use mid as an
@@ -311,23 +366,36 @@ void ICF::segregate(
   }
 }
 
-template <class Ptr>
-DenseSet<const InputSection *> findFunctionsWithUnwindInfo() {
-  DenseSet<const InputSection *> result;
-  for (ConcatInputSection *isec : in.unwindInfo->getInputs()) {
-    for (size_t i = 0; i < isec->relocs.size(); ++i) {
-      Reloc &r = isec->relocs[i];
-      assert(target->hasAttr(r.type, RelocAttrBits::UNSIGNED));
-      if (r.offset % sizeof(CompactUnwindEntry<Ptr>) !=
-          offsetof(CompactUnwindEntry<Ptr>, functionAddress))
-        continue;
-      result.insert(r.referent.get<InputSection *>());
+void macho::markSymAsAddrSig(Symbol *s) {
+  if (auto *d = dyn_cast_or_null<Defined>(s))
+    if (d->isec)
+      d->isec->keepUnique = true;
+}
+
+void macho::markAddrSigSymbols() {
+  TimeTraceScope timeScope("Mark addrsig symbols");
+  for (InputFile *file : inputFiles) {
+    ObjFile *obj = dyn_cast<ObjFile>(file);
+    if (!obj)
+      continue;
+
+    Section *addrSigSection = obj->addrSigSection;
+    if (!addrSigSection)
+      continue;
+    assert(addrSigSection->subsections.size() == 1);
+
+    const InputSection *isec = addrSigSection->subsections[0].isec;
+
+    for (const Reloc &r : isec->relocs) {
+      if (auto *sym = r.referent.dyn_cast<Symbol *>())
+        markSymAsAddrSig(sym);
+      else
+        error(toString(isec) + ": unexpected section relocation");
     }
   }
-  return result;
 }
 
-void macho::foldIdenticalSections() {
+void macho::foldIdenticalSections(bool onlyCfStrings) {
   TimeTraceScope timeScope("Fold Identical Code Sections");
   // The ICF equivalence-class segregation algorithm relies on pre-computed
   // hashes of InputSection::data for the ConcatOutputSection::inputs and all
@@ -336,11 +404,6 @@ void macho::foldIdenticalSections() {
   // parallelization. Therefore, we hash every InputSection here where we have
   // them all accessible as simple vectors.
 
-  // ICF can't fold functions with unwind info
-  DenseSet<const InputSection *> functionsWithUnwindInfo =
-      target->wordSize == 8 ? findFunctionsWithUnwindInfo<uint64_t>()
-                            : findFunctionsWithUnwindInfo<uint32_t>();
-
   // If an InputSection is ineligible for ICF, we give it a unique ID to force
   // it into an unfoldable singleton equivalence class.  Begin the unique-ID
   // space at inputSections.size(), so that it will never intersect with
@@ -348,22 +411,55 @@ void macho::foldIdenticalSections() {
   // coexist with equivalence-class IDs, this is not necessary, but might help
   // someone keep the numbers straight in case we ever need to debug the
   // ICF::segregate()
-  std::vector<ConcatInputSection *> hashable;
+  std::vector<ConcatInputSection *> foldable;
   uint64_t icfUniqueID = inputSections.size();
   for (ConcatInputSection *isec : inputSections) {
-    // FIXME: consider non-code __text sections as hashable?
-    bool isHashable = (isCodeSection(isec) || isCfStringSection(isec)) &&
-                      !isec->shouldOmitFromOutput() &&
-                      !functionsWithUnwindInfo.contains(isec) &&
-                      isec->isHashableForICF();
-    if (isHashable)
-      hashable.push_back(isec);
-    else
+    bool isFoldableWithAddendsRemoved = isCfStringSection(isec) ||
+                                        isClassRefsSection(isec) ||
+                                        isSelRefsSection(isec);
+    // NOTE: __objc_selrefs is typically marked as no_dead_strip by MC, but we
+    // can still fold it.
+    bool hasFoldableFlags = (isSelRefsSection(isec) ||
+                             sectionType(isec->getFlags()) == MachO::S_REGULAR);
+    // FIXME: consider non-code __text sections as foldable?
+    bool isFoldable = (!onlyCfStrings || isCfStringSection(isec)) &&
+                      (isCodeSection(isec) || isFoldableWithAddendsRemoved ||
+                       isGccExceptTabSection(isec)) &&
+                      !isec->keepUnique && !isec->hasAltEntry &&
+                      !isec->shouldOmitFromOutput() && hasFoldableFlags;
+    if (isFoldable) {
+      foldable.push_back(isec);
+      for (Defined *d : isec->symbols)
+        if (d->unwindEntry)
+          foldable.push_back(d->unwindEntry);
+
+      // Some sections have embedded addends that foil ICF's hashing / equality
+      // checks. (We can ignore embedded addends when doing ICF because the same
+      // information gets recorded in our Reloc structs.) We therefore create a
+      // mutable copy of the section data and zero out the embedded addends
+      // before performing any hashing / equality checks.
+      if (isFoldableWithAddendsRemoved) {
+        // We have to do this copying serially as the BumpPtrAllocator is not
+        // thread-safe. FIXME: Make a thread-safe allocator.
+        MutableArrayRef<uint8_t> copy = isec->data.copy(bAlloc());
+        for (const Reloc &r : isec->relocs)
+          target->relocateOne(copy.data() + r.offset, r, /*va=*/0,
+                              /*relocVA=*/0);
+        isec->data = copy;
+      }
+    } else if (!isEhFrameSection(isec)) {
+      // EH frames are gathered as foldables from unwindEntry above; give a
+      // unique ID to everything else.
       isec->icfEqClass[0] = ++icfUniqueID;
+    }
   }
-  parallelForEach(hashable,
-                  [](ConcatInputSection *isec) { isec->hashForICF(); });
+  parallelForEach(foldable, [](ConcatInputSection *isec) {
+    assert(isec->icfEqClass[0] == 0); // don't overwrite a unique ID!
+    // Turn-on the top bit to guarantee that valid hashes have no collisions
+    // with the small-integer unique IDs for ICF-ineligible sections
+    isec->icfEqClass[0] = xxHash64(isec->data) | (1ull << 31);
+  });
   // Now that every input section is either hashed or marked as unique, run the
   // segregation algorithm to detect foldable subsections.
-  ICF(hashable).run();
+  ICF(foldable).run();
 }
index 9500a94..34ceb1c 100644 (file)
@@ -9,15 +9,17 @@
 #ifndef LLD_MACHO_ICF_H
 #define LLD_MACHO_ICF_H
 
+#include "InputFiles.h"
 #include "lld/Common/LLVM.h"
 #include <vector>
 
-namespace lld {
-namespace macho {
+namespace lld::macho {
+class Symbol;
 
-void foldIdenticalSections();
+void markAddrSigSymbols();
+void markSymAsAddrSig(Symbol *s);
+void foldIdenticalSections(bool onlyCfStrings);
 
-} // namespace macho
-} // namespace lld
+} // namespace lld::macho
 
 #endif
index a4fb903..e128910 100644 (file)
@@ -45,6 +45,7 @@
 #include "Config.h"
 #include "Driver.h"
 #include "Dwarf.h"
+#include "EhFrame.h"
 #include "ExportTrie.h"
 #include "InputSection.h"
 #include "MachOStructs.h"
 #include "SyntheticSections.h"
 #include "Target.h"
 
+#include "lld/Common/CommonLinkerContext.h"
 #include "lld/Common/DWARF.h"
-#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Memory.h"
 #include "lld/Common/Reproduce.h"
 #include "llvm/ADT/iterator.h"
 #include "llvm/BinaryFormat/MachO.h"
 #include "llvm/LTO/LTO.h"
+#include "llvm/Support/BinaryStreamReader.h"
 #include "llvm/Support/Endian.h"
+#include "llvm/Support/LEB128.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/TarWriter.h"
+#include "llvm/Support/TimeProfiler.h"
 #include "llvm/TextAPI/Architecture.h"
 #include "llvm/TextAPI/InterfaceFile.h"
 
+#include <optional>
+#include <type_traits>
+
 using namespace llvm;
 using namespace llvm::MachO;
 using namespace llvm::support::endian;
@@ -92,6 +98,10 @@ std::string lld::toString(const InputFile *f) {
   return (f->archiveName + "(" + path::filename(f->getName()) + ")").str();
 }
 
+std::string lld::toString(const Section &sec) {
+  return (toString(sec.file) + ":(" + sec.name + ")").str();
+}
+
 SetVector<InputFile *> macho::inputFiles;
 std::unique_ptr<TarWriter> macho::tar;
 int InputFile::idCount = 0;
@@ -109,10 +119,11 @@ static std::vector<PlatformInfo> getPlatformInfos(const InputFile *input) {
 
   const char *hdr = input->mb.getBufferStart();
 
+  // "Zippered" object files can have multiple LC_BUILD_VERSION load commands.
   std::vector<PlatformInfo> platformInfos;
   for (auto *cmd : findCommands<build_version_command>(hdr, LC_BUILD_VERSION)) {
     PlatformInfo info;
-    info.target.Platform = static_cast<PlatformKind>(cmd->platform);
+    info.target.Platform = static_cast<PlatformType>(cmd->platform);
     info.minimum = decodeVersion(cmd->minos);
     platformInfos.emplace_back(std::move(info));
   }
@@ -122,16 +133,16 @@ static std::vector<PlatformInfo> getPlatformInfos(const InputFile *input) {
     PlatformInfo info;
     switch (cmd->cmd) {
     case LC_VERSION_MIN_MACOSX:
-      info.target.Platform = PlatformKind::macOS;
+      info.target.Platform = PLATFORM_MACOS;
       break;
     case LC_VERSION_MIN_IPHONEOS:
-      info.target.Platform = PlatformKind::iOS;
+      info.target.Platform = PLATFORM_IOS;
       break;
     case LC_VERSION_MIN_TVOS:
-      info.target.Platform = PlatformKind::tvOS;
+      info.target.Platform = PLATFORM_TVOS;
       break;
     case LC_VERSION_MIN_WATCHOS:
-      info.target.Platform = PlatformKind::watchOS;
+      info.target.Platform = PLATFORM_WATCHOS;
       break;
     }
     info.minimum = decodeVersion(cmd->version);
@@ -173,12 +184,23 @@ static bool checkCompatibility(const InputFile *input) {
   return true;
 }
 
+// This cache mostly exists to store system libraries (and .tbds) as they're
+// loaded, rather than the input archives, which are already cached at a higher
+// level, and other files like the filelist that are only read once.
+// Theoretically this caching could be more efficient by hoisting it, but that
+// would require altering many callers to track the state.
+DenseMap<CachedHashStringRef, MemoryBufferRef> macho::cachedReads;
 // Open a given file path and return it as a memory-mapped file.
-Optional<MemoryBufferRef> macho::readFile(StringRef path) {
+std::optional<MemoryBufferRef> macho::readFile(StringRef path) {
+  CachedHashStringRef key(path);
+  auto entry = cachedReads.find(key);
+  if (entry != cachedReads.end())
+    return entry->second;
+
   ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr = MemoryBuffer::getFile(path);
   if (std::error_code ec = mbOrErr.getError()) {
     error("cannot open " + path + ": " + ec.message());
-    return None;
+    return std::nullopt;
   }
 
   std::unique_ptr<MemoryBuffer> &mb = *mbOrErr;
@@ -192,24 +214,38 @@ Optional<MemoryBufferRef> macho::readFile(StringRef path) {
       read32be(&hdr->magic) != FAT_MAGIC) {
     if (tar)
       tar->append(relativeToRoot(path), mbref.getBuffer());
-    return mbref;
+    return cachedReads[key] = mbref;
   }
 
+  llvm::BumpPtrAllocator &bAlloc = lld::bAlloc();
+
   // Object files and archive files may be fat files, which contain multiple
   // real files for different CPU ISAs. Here, we search for a file that matches
   // with the current link target and returns it as a MemoryBufferRef.
   const auto *arch = reinterpret_cast<const fat_arch *>(buf + sizeof(*hdr));
+  auto getArchName = [](uint32_t cpuType, uint32_t cpuSubtype) {
+    return getArchitectureName(getArchitectureFromCpuType(cpuType, cpuSubtype));
+  };
 
+  std::vector<StringRef> archs;
   for (uint32_t i = 0, n = read32be(&hdr->nfat_arch); i < n; ++i) {
     if (reinterpret_cast<const char *>(arch + i + 1) >
         buf + mbref.getBufferSize()) {
       error(path + ": fat_arch struct extends beyond end of file");
-      return None;
+      return std::nullopt;
     }
 
-    if (read32be(&arch[i].cputype) != static_cast<uint32_t>(target->cpuType) ||
-        read32be(&arch[i].cpusubtype) != target->cpuSubtype)
+    uint32_t cpuType = read32be(&arch[i].cputype);
+    uint32_t cpuSubtype =
+        read32be(&arch[i].cpusubtype) & ~MachO::CPU_SUBTYPE_MASK;
+
+    // FIXME: LD64 has a more complex fallback logic here.
+    // Consider implementing that as well?
+    if (cpuType != static_cast<uint32_t>(target->cpuType) ||
+        cpuSubtype != target->cpuSubtype) {
+      archs.emplace_back(getArchName(cpuType, cpuSubtype));
       continue;
+    }
 
     uint32_t offset = read32be(&arch[i].offset);
     uint32_t size = read32be(&arch[i].size);
@@ -217,86 +253,194 @@ Optional<MemoryBufferRef> macho::readFile(StringRef path) {
       error(path + ": slice extends beyond end of file");
     if (tar)
       tar->append(relativeToRoot(path), mbref.getBuffer());
-    return MemoryBufferRef(StringRef(buf + offset, size), path.copy(bAlloc));
+    return cachedReads[key] = MemoryBufferRef(StringRef(buf + offset, size),
+                                              path.copy(bAlloc));
   }
 
-  error("unable to find matching architecture in " + path);
-  return None;
+  auto targetArchName = getArchName(target->cpuType, target->cpuSubtype);
+  warn(path + ": ignoring file because it is universal (" + join(archs, ",") +
+       ") but does not contain the " + targetArchName + " architecture");
+  return std::nullopt;
 }
 
 InputFile::InputFile(Kind kind, const InterfaceFile &interface)
-    : id(idCount++), fileKind(kind), name(saver.save(interface.getPath())) {}
+    : id(idCount++), fileKind(kind), name(saver().save(interface.getPath())) {}
+
+// Some sections comprise of fixed-size records, so instead of splitting them at
+// symbol boundaries, we split them based on size. Records are distinct from
+// literals in that they may contain references to other sections, instead of
+// being leaf nodes in the InputSection graph.
+//
+// Note that "record" is a term I came up with. In contrast, "literal" is a term
+// used by the Mach-O format.
+static std::optional<size_t> getRecordSize(StringRef segname, StringRef name) {
+  if (name == section_names::compactUnwind) {
+    if (segname == segment_names::ld)
+      return target->wordSize == 8 ? 32 : 20;
+  }
+  if (!config->dedupStrings)
+    return {};
+
+  if (name == section_names::cfString && segname == segment_names::data)
+    return target->wordSize == 8 ? 32 : 16;
+
+  if (config->icfLevel == ICFLevel::none)
+    return {};
+
+  if (name == section_names::objcClassRefs && segname == segment_names::data)
+    return target->wordSize;
+
+  if (name == section_names::objcSelrefs && segname == segment_names::data)
+    return target->wordSize;
+  return {};
+}
 
-template <class Section>
-void ObjFile::parseSections(ArrayRef<Section> sections) {
-  subsections.reserve(sections.size());
+static Error parseCallGraph(ArrayRef<uint8_t> data,
+                            std::vector<CallGraphEntry> &callGraph) {
+  TimeTraceScope timeScope("Parsing call graph section");
+  BinaryStreamReader reader(data, support::little);
+  while (!reader.empty()) {
+    uint32_t fromIndex, toIndex;
+    uint64_t count;
+    if (Error err = reader.readInteger(fromIndex))
+      return err;
+    if (Error err = reader.readInteger(toIndex))
+      return err;
+    if (Error err = reader.readInteger(count))
+      return err;
+    callGraph.emplace_back(fromIndex, toIndex, count);
+  }
+  return Error::success();
+}
+
+// Parse the sequence of sections within a single LC_SEGMENT(_64).
+// Split each section into subsections.
+template <class SectionHeader>
+void ObjFile::parseSections(ArrayRef<SectionHeader> sectionHeaders) {
+  sections.reserve(sectionHeaders.size());
   auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
 
-  for (const Section &sec : sections) {
+  for (const SectionHeader &sec : sectionHeaders) {
     StringRef name =
         StringRef(sec.sectname, strnlen(sec.sectname, sizeof(sec.sectname)));
     StringRef segname =
         StringRef(sec.segname, strnlen(sec.segname, sizeof(sec.segname)));
-    ArrayRef<uint8_t> data = {isZeroFill(sec.flags) ? nullptr
-                                                    : buf + sec.offset,
-                              static_cast<size_t>(sec.size)};
+    sections.push_back(make<Section>(this, segname, name, sec.flags, sec.addr));
     if (sec.align >= 32) {
       error("alignment " + std::to_string(sec.align) + " of section " + name +
             " is too large");
-      subsections.push_back({});
       continue;
     }
+    Section &section = *sections.back();
     uint32_t align = 1 << sec.align;
-    uint32_t flags = sec.flags;
-
-    if (sectionType(sec.flags) == S_CSTRING_LITERALS ||
-        (config->dedupLiterals && isWordLiteralSection(sec.flags))) {
-      if (sec.nreloc && config->dedupLiterals)
-        fatal(toString(this) + " contains relocations in " + sec.segname + "," +
-              sec.sectname +
-              ", so LLD cannot deduplicate literals. Try re-running without "
-              "--deduplicate-literals.");
-
-      InputSection *isec;
-      if (sectionType(sec.flags) == S_CSTRING_LITERALS) {
-        isec =
-            make<CStringInputSection>(segname, name, this, data, align, flags);
-        // FIXME: parallelize this?
-        cast<CStringInputSection>(isec)->splitIntoPieces();
-      } else {
-        isec = make<WordLiteralInputSection>(segname, name, this, data, align,
-                                             flags);
+    ArrayRef<uint8_t> data = {isZeroFill(sec.flags) ? nullptr
+                                                    : buf + sec.offset,
+                              static_cast<size_t>(sec.size)};
+
+    auto splitRecords = [&](size_t recordSize) -> void {
+      if (data.empty())
+        return;
+      Subsections &subsections = section.subsections;
+      subsections.reserve(data.size() / recordSize);
+      for (uint64_t off = 0; off < data.size(); off += recordSize) {
+        auto *isec = make<ConcatInputSection>(
+            section, data.slice(off, std::min(data.size(), recordSize)), align);
+        subsections.push_back({off, isec});
       }
-      subsections.push_back({{0, isec}});
-    } else if (config->icfLevel != ICFLevel::none &&
-               (name == section_names::cfString &&
-                segname == segment_names::data)) {
-      uint64_t literalSize = target->wordSize == 8 ? 32 : 16;
-      subsections.push_back({});
-      SubsectionMap &subsecMap = subsections.back();
-      for (uint64_t off = 0; off < data.size(); off += literalSize)
-        subsecMap.push_back(
-            {off, make<ConcatInputSection>(segname, name, this,
-                                           data.slice(off, literalSize), align,
-                                           flags)});
+      section.doneSplitting = true;
+    };
+
+    if (sectionType(sec.flags) == S_CSTRING_LITERALS) {
+      if (sec.nreloc)
+        fatal(toString(this) + ": " + sec.segname + "," + sec.sectname +
+              " contains relocations, which is unsupported");
+      bool dedupLiterals =
+          name == section_names::objcMethname || config->dedupStrings;
+      InputSection *isec =
+          make<CStringInputSection>(section, data, align, dedupLiterals);
+      // FIXME: parallelize this?
+      cast<CStringInputSection>(isec)->splitIntoPieces();
+      section.subsections.push_back({0, isec});
+    } else if (isWordLiteralSection(sec.flags)) {
+      if (sec.nreloc)
+        fatal(toString(this) + ": " + sec.segname + "," + sec.sectname +
+              " contains relocations, which is unsupported");
+      InputSection *isec = make<WordLiteralInputSection>(section, data, align);
+      section.subsections.push_back({0, isec});
+    } else if (auto recordSize = getRecordSize(segname, name)) {
+      splitRecords(*recordSize);
+    } else if (name == section_names::ehFrame &&
+               segname == segment_names::text) {
+      splitEhFrames(data, *sections.back());
+    } else if (segname == segment_names::llvm) {
+      if (config->callGraphProfileSort && name == section_names::cgProfile)
+        checkError(parseCallGraph(data, callGraph));
+      // ld64 does not appear to emit contents from sections within the __LLVM
+      // segment. Symbols within those sections point to bitcode metadata
+      // instead of actual symbols. Global symbols within those sections could
+      // have the same name without causing duplicate symbol errors. To avoid
+      // spurious duplicate symbol errors, we do not parse these sections.
+      // TODO: Evaluate whether the bitcode metadata is needed.
+    } else if (name == section_names::objCImageInfo &&
+               segname == segment_names::data) {
+      objCImageInfo = data;
     } else {
-      auto *isec =
-          make<ConcatInputSection>(segname, name, this, data, align, flags);
-      if (!(isDebugSection(isec->getFlags()) &&
-            isec->getSegName() == segment_names::dwarf)) {
-        subsections.push_back({{0, isec}});
-      } else {
+      if (name == section_names::addrSig)
+        addrSigSection = sections.back();
+
+      auto *isec = make<ConcatInputSection>(section, data, align);
+      if (isDebugSection(isec->getFlags()) &&
+          isec->getSegName() == segment_names::dwarf) {
         // Instead of emitting DWARF sections, we emit STABS symbols to the
         // object files that contain them. We filter them out early to avoid
-        // parsing their relocations unnecessarily. But we must still push an
-        // empty map to ensure the indices line up for the remaining sections.
-        subsections.push_back({});
+        // parsing their relocations unnecessarily.
         debugSections.push_back(isec);
+      } else {
+        section.subsections.push_back({0, isec});
       }
     }
   }
 }
 
+void ObjFile::splitEhFrames(ArrayRef<uint8_t> data, Section &ehFrameSection) {
+  EhReader reader(this, data, /*dataOff=*/0);
+  size_t off = 0;
+  while (off < reader.size()) {
+    uint64_t frameOff = off;
+    uint64_t length = reader.readLength(&off);
+    if (length == 0)
+      break;
+    uint64_t fullLength = length + (off - frameOff);
+    off += length;
+    // We hard-code an alignment of 1 here because we don't actually want our
+    // EH frames to be aligned to the section alignment. EH frame decoders don't
+    // expect this alignment. Moreover, each EH frame must start where the
+    // previous one ends, and where it ends is indicated by the length field.
+    // Unless we update the length field (troublesome), we should keep the
+    // alignment to 1.
+    // Note that we still want to preserve the alignment of the overall section,
+    // just not of the individual EH frames.
+    ehFrameSection.subsections.push_back(
+        {frameOff, make<ConcatInputSection>(ehFrameSection,
+                                            data.slice(frameOff, fullLength),
+                                            /*align=*/1)});
+  }
+  ehFrameSection.doneSplitting = true;
+}
+
+template <class T>
+static Section *findContainingSection(const std::vector<Section *> &sections,
+                                      T *offset) {
+  static_assert(std::is_same<uint64_t, T>::value ||
+                    std::is_same<uint32_t, T>::value,
+                "unexpected type for offset");
+  auto it = std::prev(llvm::upper_bound(
+      sections, *offset,
+      [](uint64_t value, const Section *sec) { return value < sec->addr; }));
+  *offset -= (*it)->addr;
+  return *it;
+}
+
 // Find the subsection corresponding to the greatest section offset that is <=
 // that of the given offset.
 //
@@ -304,18 +448,35 @@ void ObjFile::parseSections(ArrayRef<Section> sections) {
 // any subsection splitting has occurred). It will be updated to represent the
 // same location as an offset relative to the start of the containing
 // subsection.
-static InputSection *findContainingSubsection(SubsectionMap &map,
-                                              uint64_t *offset) {
+template <class T>
+static InputSection *findContainingSubsection(const Section &section,
+                                              T *offset) {
+  static_assert(std::is_same<uint64_t, T>::value ||
+                    std::is_same<uint32_t, T>::value,
+                "unexpected type for offset");
   auto it = std::prev(llvm::upper_bound(
-      map, *offset, [](uint64_t value, SubsectionEntry subsecEntry) {
-        return value < subsecEntry.offset;
-      }));
+      section.subsections, *offset,
+      [](uint64_t value, Subsection subsec) { return value < subsec.offset; }));
   *offset -= it->offset;
   return it->isec;
 }
 
-template <class Section>
-static bool validateRelocationInfo(InputFile *file, const Section &sec,
+// Find a symbol at offset `off` within `isec`.
+static Defined *findSymbolAtOffset(const ConcatInputSection *isec,
+                                   uint64_t off) {
+  auto it = llvm::lower_bound(isec->symbols, off, [](Defined *d, uint64_t off) {
+    return d->value < off;
+  });
+  // The offset should point at the exact address of a symbol (with no addend.)
+  if (it == isec->symbols.end() || (*it)->value != off) {
+    assert(isec->wasCoalesced);
+    return nullptr;
+  }
+  return *it;
+}
+
+template <class SectionHeader>
+static bool validateRelocationInfo(InputFile *file, const SectionHeader &sec,
                                    relocation_info rel) {
   const RelocAttrs &relocAttrs = target->getRelocAttrs(rel.r_type);
   bool valid = true;
@@ -346,14 +507,15 @@ static bool validateRelocationInfo(InputFile *file, const Section &sec,
   return valid;
 }
 
-template <class Section>
-void ObjFile::parseRelocations(ArrayRef<Section> sectionHeaders,
-                               const Section &sec, SubsectionMap &subsecMap) {
+template <class SectionHeader>
+void ObjFile::parseRelocations(ArrayRef<SectionHeader> sectionHeaders,
+                               const SectionHeader &sec, Section &section) {
   auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
   ArrayRef<relocation_info> relInfos(
       reinterpret_cast<const relocation_info *>(buf + sec.reloff), sec.nreloc);
 
-  auto subsecIt = subsecMap.rbegin();
+  Subsections &subsections = section.subsections;
+  auto subsecIt = subsections.rbegin();
   for (size_t i = 0; i < relInfos.size(); i++) {
     // Paired relocations serve as Mach-O's method for attaching a
     // supplemental datum to a primary relocation record. ELF does not
@@ -369,19 +531,20 @@ void ObjFile::parseRelocations(ArrayRef<Section> sectionHeaders,
     // ARM64_RELOC_BRANCH26 or ARM64_RELOC_PAGE21/PAGEOFF12 holds the
     // base symbolic address.
     //
-    // Note: X86 does not use *_RELOC_ADDEND because it can embed an
-    // addend into the instruction stream. On X86, a relocatable address
-    // field always occupies an entire contiguous sequence of byte(s),
-    // so there is no need to merge opcode bits with address
-    // bits. Therefore, it's easy and convenient to store addends in the
-    // instruction-stream bytes that would otherwise contain zeroes. By
-    // contrast, RISC ISAs such as ARM64 mix opcode bits with with
-    // address bits so that bitwise arithmetic is necessary to extract
-    // and insert them. Storing addends in the instruction stream is
-    // possible, but inconvenient and more costly at link time.
+    // Note: X86 does not use *_RELOC_ADDEND because it can embed an addend into
+    // the instruction stream. On X86, a relocatable address field always
+    // occupies an entire contiguous sequence of byte(s), so there is no need to
+    // merge opcode bits with address bits. Therefore, it's easy and convenient
+    // to store addends in the instruction-stream bytes that would otherwise
+    // contain zeroes. By contrast, RISC ISAs such as ARM64 mix opcode bits with
+    // address bits so that bitwise arithmetic is necessary to extract and
+    // insert them. Storing addends in the instruction stream is possible, but
+    // inconvenient and more costly at link time.
 
-    int64_t pairedAddend = 0;
     relocation_info relInfo = relInfos[i];
+    bool isSubtrahend =
+        target->hasAttr(relInfo.r_type, RelocAttrBits::SUBTRAHEND);
+    int64_t pairedAddend = 0;
     if (target->hasAttr(relInfo.r_type, RelocAttrBits::ADDEND)) {
       pairedAddend = SignExtend64<24>(relInfo.r_symbolnum);
       relInfo = relInfos[++i];
@@ -392,8 +555,6 @@ void ObjFile::parseRelocations(ArrayRef<Section> sectionHeaders,
     if (relInfo.r_address & R_SCATTERED)
       fatal("TODO: Scattered relocations not supported");
 
-    bool isSubtrahend =
-        target->hasAttr(relInfo.r_type, RelocAttrBits::SUBTRAHEND);
     int64_t embeddedAddend = target->getEmbeddedAddend(mb, sec.offset, relInfo);
     assert(!(embeddedAddend && pairedAddend));
     int64_t totalAddend = pairedAddend + embeddedAddend;
@@ -407,7 +568,8 @@ void ObjFile::parseRelocations(ArrayRef<Section> sectionHeaders,
       r.addend = isSubtrahend ? 0 : totalAddend;
     } else {
       assert(!isSubtrahend);
-      const Section &referentSec = sectionHeaders[relInfo.r_symbolnum - 1];
+      const SectionHeader &referentSecHead =
+          sectionHeaders[relInfo.r_symbolnum - 1];
       uint64_t referentOffset;
       if (relInfo.r_pcrel) {
         // The implicit addend for pcrel section relocations is the pcrel offset
@@ -417,14 +579,14 @@ void ObjFile::parseRelocations(ArrayRef<Section> sectionHeaders,
         // have pcrel section relocations. We may want to factor this out into
         // the arch-specific .cpp file.
         assert(target->hasAttr(r.type, RelocAttrBits::BYTE4));
-        referentOffset =
-            sec.addr + relInfo.r_address + 4 + totalAddend - referentSec.addr;
+        referentOffset = sec.addr + relInfo.r_address + 4 + totalAddend -
+                         referentSecHead.addr;
       } else {
         // The addend for a non-pcrel relocation is its absolute address.
-        referentOffset = totalAddend - referentSec.addr;
+        referentOffset = totalAddend - referentSecHead.addr;
       }
-      SubsectionMap &referentSubsecMap = subsections[relInfo.r_symbolnum - 1];
-      r.referent = findContainingSubsection(referentSubsecMap, &referentOffset);
+      r.referent = findContainingSubsection(*sections[relInfo.r_symbolnum - 1],
+                                            &referentOffset);
       r.addend = referentOffset;
     }
 
@@ -434,14 +596,14 @@ void ObjFile::parseRelocations(ArrayRef<Section> sectionHeaders,
     // unsorted relocations (in `-r` mode), so we have a fallback for that
     // uncommon case.
     InputSection *subsec;
-    while (subsecIt != subsecMap.rend() && subsecIt->offset > r.offset)
+    while (subsecIt != subsections.rend() && subsecIt->offset > r.offset)
       ++subsecIt;
-    if (subsecIt == subsecMap.rend() ||
+    if (subsecIt == subsections.rend() ||
         subsecIt->offset + subsecIt->isec->getSize() <= r.offset) {
-      subsec = findContainingSubsection(subsecMap, &r.offset);
+      subsec = findContainingSubsection(section, &r.offset);
       // Now that we know the relocs are unsorted, avoid trying the 'fast path'
       // for the other relocations.
-      subsecIt = subsecMap.rend();
+      subsecIt = subsections.rend();
     } else {
       subsec = subsecIt->isec;
       r.offset -= subsecIt->offset;
@@ -462,10 +624,8 @@ void ObjFile::parseRelocations(ArrayRef<Section> sectionHeaders,
       } else {
         uint64_t referentOffset =
             totalAddend - sectionHeaders[minuendInfo.r_symbolnum - 1].addr;
-        SubsectionMap &referentSubsecMap =
-            subsections[minuendInfo.r_symbolnum - 1];
-        p.referent =
-            findContainingSubsection(referentSubsecMap, &referentOffset);
+        p.referent = findContainingSubsection(
+            *sections[minuendInfo.r_symbolnum - 1], &referentOffset);
         p.addend = referentOffset;
       }
       subsec->relocs.push_back(p);
@@ -473,10 +633,16 @@ void ObjFile::parseRelocations(ArrayRef<Section> sectionHeaders,
   }
 }
 
+// Symbols with `l` or `L` as a prefix are linker-private and never appear in
+// the output.
+static bool isPrivateLabel(StringRef name) {
+  return name.startswith("l") || name.startswith("L");
+}
+
 template <class NList>
 static macho::Symbol *createDefined(const NList &sym, StringRef name,
                                     InputSection *isec, uint64_t value,
-                                    uint64_t size) {
+                                    uint64_t size, bool forceHidden) {
   // Symbol scope is determined by sym.n_type & (N_EXT | N_PEXT):
   // N_EXT: Global symbols. These go in the symbol table during the link,
   //        and also in the export table of the output so that the dynamic
@@ -495,7 +661,10 @@ static macho::Symbol *createDefined(const NList &sym, StringRef name,
       (sym.n_desc & (N_WEAK_DEF | N_WEAK_REF)) == (N_WEAK_DEF | N_WEAK_REF);
 
   if (sym.n_type & N_EXT) {
-    bool isPrivateExtern = sym.n_type & N_PEXT;
+    // -load_hidden makes us treat global symbols as linkage unit scoped.
+    // Duplicates are reported but the symbol does not go in the export trie.
+    bool isPrivateExtern = sym.n_type & N_PEXT || forceHidden;
+
     // lld's behavior for merging symbols is slightly different from ld64:
     // ld64 picks the winning symbol based on several criteria (see
     // pickBetweenRegularAtoms() in ld64's SymbolTable.cpp), while lld
@@ -520,23 +689,27 @@ static macho::Symbol *createDefined(const NList &sym, StringRef name,
     // with ld64's semantics, because it means the non-private-extern
     // definition will continue to take priority if more private extern
     // definitions are encountered. With lld's semantics there's no observable
-    // difference between a symbol that's isWeakDefCanBeHidden or one that's
-    // privateExtern -- neither makes it into the dynamic symbol table. So just
-    // promote isWeakDefCanBeHidden to isPrivateExtern here.
-    if (isWeakDefCanBeHidden)
+    // difference between a symbol that's isWeakDefCanBeHidden(autohide) or one
+    // that's privateExtern -- neither makes it into the dynamic symbol table,
+    // unless the autohide symbol is explicitly exported.
+    // But if a symbol is both privateExtern and autohide then it can't
+    // be exported.
+    // So we nullify the autohide flag when privateExtern is present
+    // and promote the symbol to privateExtern when it is not already.
+    if (isWeakDefCanBeHidden && isPrivateExtern)
+      isWeakDefCanBeHidden = false;
+    else if (isWeakDefCanBeHidden)
       isPrivateExtern = true;
-
     return symtab->addDefined(
         name, isec->getFile(), isec, value, size, sym.n_desc & N_WEAK_DEF,
         isPrivateExtern, sym.n_desc & N_ARM_THUMB_DEF,
-        sym.n_desc & REFERENCED_DYNAMICALLY, sym.n_desc & N_NO_DEAD_STRIP);
+        sym.n_desc & REFERENCED_DYNAMICALLY, sym.n_desc & N_NO_DEAD_STRIP,
+        isWeakDefCanBeHidden);
   }
-
-  assert(!isWeakDefCanBeHidden &&
-         "weak_def_can_be_hidden on already-hidden symbol?");
+  bool includeInSymtab = !isPrivateLabel(name) && !isEhFrameSection(isec);
   return make<Defined>(
       name, isec->getFile(), isec, value, size, sym.n_desc & N_WEAK_DEF,
-      /*isExternal=*/false, /*isPrivateExtern=*/false,
+      /*isExternal=*/false, /*isPrivateExtern=*/false, includeInSymtab,
       sym.n_desc & N_ARM_THUMB_DEF, sym.n_desc & REFERENCED_DYNAMICALLY,
       sym.n_desc & N_NO_DEAD_STRIP);
 }
@@ -545,37 +718,52 @@ static macho::Symbol *createDefined(const NList &sym, StringRef name,
 // InputSection. They cannot be weak.
 template <class NList>
 static macho::Symbol *createAbsolute(const NList &sym, InputFile *file,
-                                     StringRef name) {
+                                     StringRef name, bool forceHidden) {
   if (sym.n_type & N_EXT) {
+    bool isPrivateExtern = sym.n_type & N_PEXT || forceHidden;
     return symtab->addDefined(
         name, file, nullptr, sym.n_value, /*size=*/0,
-        /*isWeakDef=*/false, sym.n_type & N_PEXT, sym.n_desc & N_ARM_THUMB_DEF,
-        /*isReferencedDynamically=*/false, sym.n_desc & N_NO_DEAD_STRIP);
+        /*isWeakDef=*/false, isPrivateExtern, sym.n_desc & N_ARM_THUMB_DEF,
+        /*isReferencedDynamically=*/false, sym.n_desc & N_NO_DEAD_STRIP,
+        /*isWeakDefCanBeHidden=*/false);
   }
   return make<Defined>(name, file, nullptr, sym.n_value, /*size=*/0,
                        /*isWeakDef=*/false,
                        /*isExternal=*/false, /*isPrivateExtern=*/false,
-                       sym.n_desc & N_ARM_THUMB_DEF,
+                       /*includeInSymtab=*/true, sym.n_desc & N_ARM_THUMB_DEF,
                        /*isReferencedDynamically=*/false,
                        sym.n_desc & N_NO_DEAD_STRIP);
 }
 
 template <class NList>
 macho::Symbol *ObjFile::parseNonSectionSymbol(const NList &sym,
-                                              StringRef name) {
+                                              const char *strtab) {
+  StringRef name = StringRef(strtab + sym.n_strx);
   uint8_t type = sym.n_type & N_TYPE;
+  bool isPrivateExtern = sym.n_type & N_PEXT || forceHidden;
   switch (type) {
   case N_UNDF:
     return sym.n_value == 0
                ? symtab->addUndefined(name, this, sym.n_desc & N_WEAK_REF)
                : symtab->addCommon(name, this, sym.n_value,
                                    1 << GET_COMM_ALIGN(sym.n_desc),
-                                   sym.n_type & N_PEXT);
+                                   isPrivateExtern);
   case N_ABS:
-    return createAbsolute(sym, this, name);
+    return createAbsolute(sym, this, name, forceHidden);
+  case N_INDR: {
+    // Not much point in making local aliases -- relocs in the current file can
+    // just refer to the actual symbol itself. ld64 ignores these symbols too.
+    if (!(sym.n_type & N_EXT))
+      return nullptr;
+    StringRef aliasedName = StringRef(strtab + sym.n_value);
+    // isPrivateExtern is the only symbol flag that has an impact on the final
+    // aliased symbol.
+    auto alias = make<AliasSymbol>(this, name, aliasedName, isPrivateExtern);
+    aliases.push_back(alias);
+    return alias;
+  }
   case N_PBUD:
-  case N_INDR:
-    error("TODO: support symbols of type " + std::to_string(type));
+    error("TODO: support symbols of type N_PBUD");
     return nullptr;
   case N_SECT:
     llvm_unreachable(
@@ -585,8 +773,7 @@ macho::Symbol *ObjFile::parseNonSectionSymbol(const NList &sym,
   }
 }
 
-template <class NList>
-static bool isUndef(const NList &sym) {
+template <class NList> static bool isUndef(const NList &sym) {
   return (sym.n_type & N_TYPE) == N_UNDF && sym.n_value == 0;
 }
 
@@ -597,7 +784,7 @@ void ObjFile::parseSymbols(ArrayRef<typename LP::section> sectionHeaders,
   using NList = typename LP::nlist;
 
   // Groups indices of the symbols by the sections that contain them.
-  std::vector<std::vector<uint32_t>> symbolsBySection(subsections.size());
+  std::vector<std::vector<uint32_t>> symbolsBySection(sections.size());
   symbols.resize(nList.size());
   SmallVector<unsigned, 32> undefineds;
   for (uint32_t i = 0; i < nList.size(); ++i) {
@@ -608,65 +795,78 @@ void ObjFile::parseSymbols(ArrayRef<typename LP::section> sectionHeaders,
     if (sym.n_type & N_STAB)
       continue;
 
-    StringRef name = strtab + sym.n_strx;
     if ((sym.n_type & N_TYPE) == N_SECT) {
-      SubsectionMap &subsecMap = subsections[sym.n_sect - 1];
+      Subsections &subsections = sections[sym.n_sect - 1]->subsections;
       // parseSections() may have chosen not to parse this section.
-      if (subsecMap.empty())
+      if (subsections.empty())
         continue;
       symbolsBySection[sym.n_sect - 1].push_back(i);
     } else if (isUndef(sym)) {
       undefineds.push_back(i);
     } else {
-      symbols[i] = parseNonSectionSymbol(sym, name);
+      symbols[i] = parseNonSectionSymbol(sym, strtab);
     }
   }
 
-  for (size_t i = 0; i < subsections.size(); ++i) {
-    SubsectionMap &subsecMap = subsections[i];
-    if (subsecMap.empty())
+  for (size_t i = 0; i < sections.size(); ++i) {
+    Subsections &subsections = sections[i]->subsections;
+    if (subsections.empty())
       continue;
-
     std::vector<uint32_t> &symbolIndices = symbolsBySection[i];
     uint64_t sectionAddr = sectionHeaders[i].addr;
     uint32_t sectionAlign = 1u << sectionHeaders[i].align;
 
-    InputSection *isec = subsecMap.back().isec;
-    // __cfstring has already been split into subsections during
+    // Some sections have already been split into subsections during
     // parseSections(), so we simply need to match Symbols to the corresponding
     // subsection here.
-    if (config->icfLevel != ICFLevel::none && isCfStringSection(isec)) {
+    if (sections[i]->doneSplitting) {
       for (size_t j = 0; j < symbolIndices.size(); ++j) {
-        uint32_t symIndex = symbolIndices[j];
+        const uint32_t symIndex = symbolIndices[j];
         const NList &sym = nList[symIndex];
         StringRef name = strtab + sym.n_strx;
         uint64_t symbolOffset = sym.n_value - sectionAddr;
-        InputSection *isec = findContainingSubsection(subsecMap, &symbolOffset);
+        InputSection *isec =
+            findContainingSubsection(*sections[i], &symbolOffset);
         if (symbolOffset != 0) {
-          error(toString(this) + ": __cfstring contains symbol " + name +
+          error(toString(*sections[i]) + ":  symbol " + name +
                 " at misaligned offset");
           continue;
         }
-        symbols[symIndex] = createDefined(sym, name, isec, 0, isec->getSize());
+        symbols[symIndex] =
+            createDefined(sym, name, isec, 0, isec->getSize(), forceHidden);
       }
       continue;
     }
+    sections[i]->doneSplitting = true;
+
+    auto getSymName = [strtab](const NList& sym) -> StringRef {
+      return StringRef(strtab + sym.n_strx);
+    };
 
     // Calculate symbol sizes and create subsections by splitting the sections
     // along symbol boundaries.
-    // We populate subsecMap by repeatedly splitting the last (highest address)
-    // subsection.
+    // We populate subsections by repeatedly splitting the last (highest
+    // address) subsection.
     llvm::stable_sort(symbolIndices, [&](uint32_t lhs, uint32_t rhs) {
+      // Put private-label symbols that have no flags after other symbols at the
+      // same address.
+      StringRef lhsName = getSymName(nList[lhs]);
+      StringRef rhsName = getSymName(nList[rhs]);
+      if (nList[lhs].n_value == nList[rhs].n_value) {
+        if (isPrivateLabel(lhsName) && isPrivateLabel(rhsName))
+          return nList[lhs].n_desc > nList[rhs].n_desc;
+        return !isPrivateLabel(lhsName) && isPrivateLabel(rhsName);
+      }
       return nList[lhs].n_value < nList[rhs].n_value;
     });
-    SubsectionEntry subsecEntry = subsecMap.back();
     for (size_t j = 0; j < symbolIndices.size(); ++j) {
-      uint32_t symIndex = symbolIndices[j];
+      const uint32_t symIndex = symbolIndices[j];
       const NList &sym = nList[symIndex];
-      StringRef name = strtab + sym.n_strx;
-      InputSection *isec = subsecEntry.isec;
+      StringRef name = getSymName(sym);
+      Subsection &subsec = subsections.back();
+      InputSection *isec = subsec.isec;
 
-      uint64_t subsecAddr = sectionAddr + subsecEntry.offset;
+      uint64_t subsecAddr = sectionAddr + subsec.offset;
       size_t symbolOffset = sym.n_value - subsecAddr;
       uint64_t symbolSize =
           j + 1 < symbolIndices.size()
@@ -681,14 +881,23 @@ void ObjFile::parseSymbols(ArrayRef<typename LP::section> sectionHeaders,
       //   4. If we have a literal section (e.g. __cstring and __literal4).
       if (!subsectionsViaSymbols || symbolOffset == 0 ||
           sym.n_desc & N_ALT_ENTRY || !isa<ConcatInputSection>(isec)) {
-        symbols[symIndex] =
-            createDefined(sym, name, isec, symbolOffset, symbolSize);
+        isec->hasAltEntry = symbolOffset != 0;
+        // If we have an private-label symbol that's an alias, and that alias
+        // doesn't have any flags of its own, then we can just reuse the aliased
+        // symbol. Our sorting step above ensures that any such symbols will
+        // appear after the non-private-label ones. See weak-def-alias-ignored.s
+        // for the motivation behind this.
+        if (symbolOffset == 0 && isPrivateLabel(name) && j != 0 &&
+            sym.n_desc == 0)
+          symbols[symIndex] = symbols[symbolIndices[j - 1]];
+        else
+          symbols[symIndex] = createDefined(sym, name, isec, symbolOffset,
+                                            symbolSize, forceHidden);
         continue;
       }
       auto *concatIsec = cast<ConcatInputSection>(isec);
 
       auto *nextIsec = make<ConcatInputSection>(*concatIsec);
-      nextIsec->numRefs = 0;
       nextIsec->wasCoalesced = false;
       if (isZeroFill(isec->getFlags())) {
         // Zero-fill sections have NULL data.data() non-zero data.size()
@@ -701,14 +910,13 @@ void ObjFile::parseSymbols(ArrayRef<typename LP::section> sectionHeaders,
 
       // By construction, the symbol will be at offset zero in the new
       // subsection.
-      symbols[symIndex] =
-          createDefined(sym, name, nextIsec, /*value=*/0, symbolSize);
+      symbols[symIndex] = createDefined(sym, name, nextIsec, /*value=*/0,
+                                        symbolSize, forceHidden);
       // TODO: ld64 appears to preserve the original alignment as well as each
       // subsection's offset from the last aligned address. We should consider
       // emulating that behavior.
       nextIsec->align = MinAlign(sectionAlign, sym.n_value);
-      subsecMap.push_back({sym.n_value - sectionAddr, nextIsec});
-      subsecEntry = subsecMap.back();
+      subsections.push_back({sym.n_value - sectionAddr, nextIsec});
     }
   }
 
@@ -718,11 +926,8 @@ void ObjFile::parseSymbols(ArrayRef<typename LP::section> sectionHeaders,
   // symbol resolution behavior. In addition, a set of interconnected symbols
   // will all be resolved to the same file, instead of being resolved to
   // different files.
-  for (unsigned i : undefineds) {
-    const NList &sym = nList[i];
-    StringRef name = strtab + sym.n_strx;
-    symbols[i] = parseNonSectionSymbol(sym, name);
-  }
+  for (unsigned i : undefineds)
+    symbols[i] = parseNonSectionSymbol(nList[i], strtab);
 }
 
 OpaqueFile::OpaqueFile(MemoryBufferRef mb, StringRef segName,
@@ -730,36 +935,52 @@ OpaqueFile::OpaqueFile(MemoryBufferRef mb, StringRef segName,
     : InputFile(OpaqueKind, mb) {
   const auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
   ArrayRef<uint8_t> data = {buf, mb.getBufferSize()};
-  ConcatInputSection *isec =
-      make<ConcatInputSection>(segName.take_front(16), sectName.take_front(16),
-                               /*file=*/this, data);
+  sections.push_back(make<Section>(/*file=*/this, segName.take_front(16),
+                                   sectName.take_front(16),
+                                   /*flags=*/0, /*addr=*/0));
+  Section &section = *sections.back();
+  ConcatInputSection *isec = make<ConcatInputSection>(section, data);
   isec->live = true;
-  subsections.push_back({{0, isec}});
+  section.subsections.push_back({0, isec});
 }
 
-ObjFile::ObjFile(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName)
-    : InputFile(ObjKind, mb), modTime(modTime) {
+ObjFile::ObjFile(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName,
+                 bool lazy, bool forceHidden)
+    : InputFile(ObjKind, mb, lazy), modTime(modTime), forceHidden(forceHidden) {
   this->archiveName = std::string(archiveName);
-  if (target->wordSize == 8)
-    parse<LP64>();
-  else
-    parse<ILP32>();
+  if (lazy) {
+    if (target->wordSize == 8)
+      parseLazy<LP64>();
+    else
+      parseLazy<ILP32>();
+  } else {
+    if (target->wordSize == 8)
+      parse<LP64>();
+    else
+      parse<ILP32>();
+  }
 }
 
 template <class LP> void ObjFile::parse() {
   using Header = typename LP::mach_header;
   using SegmentCommand = typename LP::segment_command;
-  using Section = typename LP::section;
+  using SectionHeader = typename LP::section;
   using NList = typename LP::nlist;
 
   auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
   auto *hdr = reinterpret_cast<const Header *>(mb.getBufferStart());
 
-  Architecture arch = getArchitectureFromCpuType(hdr->cputype, hdr->cpusubtype);
-  if (arch != config->arch()) {
-    error(toString(this) + " has architecture " + getArchitectureName(arch) +
-          " which is incompatible with target architecture " +
-          getArchitectureName(config->arch()));
+  uint32_t cpuType;
+  std::tie(cpuType, std::ignore) = getCPUTypeFromArchitecture(config->arch());
+  if (hdr->cputype != cpuType) {
+    Architecture arch =
+        getArchitectureFromCpuType(hdr->cputype, hdr->cpusubtype);
+    auto msg = config->errorForArchMismatch
+                   ? static_cast<void (*)(const Twine &)>(error)
+                   : warn;
+    msg(toString(this) + " has architecture " + getArchitectureName(arch) +
+        " which is incompatible with target architecture " +
+        getArchitectureName(config->arch()));
     return;
   }
 
@@ -772,11 +993,11 @@ template <class LP> void ObjFile::parse() {
     parseLCLinkerOption(this, cmd->count, data);
   }
 
-  ArrayRef<Section> sectionHeaders;
+  ArrayRef<SectionHeader> sectionHeaders;
   if (const load_command *cmd = findCommand(hdr, LP::segmentLCType)) {
     auto *c = reinterpret_cast<const SegmentCommand *>(cmd);
-    sectionHeaders =
-        ArrayRef<Section>{reinterpret_cast<const Section *>(c + 1), c->nsects};
+    sectionHeaders = ArrayRef<SectionHeader>{
+        reinterpret_cast<const SectionHeader *>(c + 1), c->nsects};
     parseSections(sectionHeaders);
   }
 
@@ -792,13 +1013,51 @@ template <class LP> void ObjFile::parse() {
 
   // The relocations may refer to the symbols, so we parse them after we have
   // parsed all the symbols.
-  for (size_t i = 0, n = subsections.size(); i < n; ++i)
-    if (!subsections[i].empty())
-      parseRelocations(sectionHeaders, sectionHeaders[i], subsections[i]);
+  for (size_t i = 0, n = sections.size(); i < n; ++i)
+    if (!sections[i]->subsections.empty())
+      parseRelocations(sectionHeaders, sectionHeaders[i], *sections[i]);
 
   parseDebugInfo();
-  if (config->emitDataInCodeInfo)
-    parseDataInCode();
+
+  Section *ehFrameSection = nullptr;
+  Section *compactUnwindSection = nullptr;
+  for (Section *sec : sections) {
+    Section **s = StringSwitch<Section **>(sec->name)
+                      .Case(section_names::compactUnwind, &compactUnwindSection)
+                      .Case(section_names::ehFrame, &ehFrameSection)
+                      .Default(nullptr);
+    if (s)
+      *s = sec;
+  }
+  if (compactUnwindSection)
+    registerCompactUnwind(*compactUnwindSection);
+  if (ehFrameSection)
+    registerEhFrames(*ehFrameSection);
+}
+
+template <class LP> void ObjFile::parseLazy() {
+  using Header = typename LP::mach_header;
+  using NList = typename LP::nlist;
+
+  auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
+  auto *hdr = reinterpret_cast<const Header *>(mb.getBufferStart());
+  const load_command *cmd = findCommand(hdr, LC_SYMTAB);
+  if (!cmd)
+    return;
+  auto *c = reinterpret_cast<const symtab_command *>(cmd);
+  ArrayRef<NList> nList(reinterpret_cast<const NList *>(buf + c->symoff),
+                        c->nsyms);
+  const char *strtab = reinterpret_cast<const char *>(buf) + c->stroff;
+  symbols.resize(nList.size());
+  for (const auto &[i, sym] : llvm::enumerate(nList)) {
+    if ((sym.n_type & N_EXT) && !isUndef(sym)) {
+      // TODO: Bound checking
+      StringRef name = strtab + sym.n_strx;
+      symbols[i] = symtab->addLazyObject(name, *this);
+      if (!lazy)
+        break;
+    }
+  }
 }
 
 void ObjFile::parseDebugInfo() {
@@ -806,6 +1065,8 @@ void ObjFile::parseDebugInfo() {
   if (!dObj)
     return;
 
+  // We do not re-use the context from getDwarf() here as that function
+  // constructs an expensive DWARFCache object.
   auto *ctx = make<DWARFContext>(
       std::move(dObj), "",
       [&](Error err) {
@@ -821,27 +1082,459 @@ void ObjFile::parseDebugInfo() {
   // FIXME: There can be more than one compile unit per object file. See
   // PR48637.
   auto it = units.begin();
-  compileUnit = it->get();
+  compileUnit = it != units.end() ? it->get() : nullptr;
 }
 
-void ObjFile::parseDataInCode() {
+ArrayRef<data_in_code_entry> ObjFile::getDataInCode() const {
   const auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
   const load_command *cmd = findCommand(buf, LC_DATA_IN_CODE);
   if (!cmd)
-    return;
+    return {};
   const auto *c = reinterpret_cast<const linkedit_data_command *>(cmd);
-  dataInCodeEntries = {
-      reinterpret_cast<const data_in_code_entry *>(buf + c->dataoff),
-      c->datasize / sizeof(data_in_code_entry)};
-  assert(is_sorted(dataInCodeEntries, [](const data_in_code_entry &lhs,
-                                         const data_in_code_entry &rhs) {
-    return lhs.offset < rhs.offset;
-  }));
+  return {reinterpret_cast<const data_in_code_entry *>(buf + c->dataoff),
+          c->datasize / sizeof(data_in_code_entry)};
+}
+
+ArrayRef<uint8_t> ObjFile::getOptimizationHints() const {
+  const auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
+  if (auto *cmd =
+          findCommand<linkedit_data_command>(buf, LC_LINKER_OPTIMIZATION_HINT))
+    return {buf + cmd->dataoff, cmd->datasize};
+  return {};
 }
 
+// Create pointers from symbols to their associated compact unwind entries.
+void ObjFile::registerCompactUnwind(Section &compactUnwindSection) {
+  for (const Subsection &subsection : compactUnwindSection.subsections) {
+    ConcatInputSection *isec = cast<ConcatInputSection>(subsection.isec);
+    // Hack!! Each compact unwind entry (CUE) has its UNSIGNED relocations embed
+    // their addends in its data. Thus if ICF operated naively and compared the
+    // entire contents of each CUE, entries with identical unwind info but e.g.
+    // belonging to different functions would never be considered equivalent. To
+    // work around this problem, we remove some parts of the data containing the
+    // embedded addends. In particular, we remove the function address and LSDA
+    // pointers.  Since these locations are at the start and end of the entry,
+    // we can do this using a simple, efficient slice rather than performing a
+    // copy.  We are not losing any information here because the embedded
+    // addends have already been parsed in the corresponding Reloc structs.
+    //
+    // Removing these pointers would not be safe if they were pointers to
+    // absolute symbols. In that case, there would be no corresponding
+    // relocation. However, (AFAIK) MC cannot emit references to absolute
+    // symbols for either the function address or the LSDA. However, it *can* do
+    // so for the personality pointer, so we are not slicing that field away.
+    //
+    // Note that we do not adjust the offsets of the corresponding relocations;
+    // instead, we rely on `relocateCompactUnwind()` to correctly handle these
+    // truncated input sections.
+    isec->data = isec->data.slice(target->wordSize, 8 + target->wordSize);
+    uint32_t encoding = read32le(isec->data.data() + sizeof(uint32_t));
+    // llvm-mc omits CU entries for functions that need DWARF encoding, but
+    // `ld -r` doesn't. We can ignore them because we will re-synthesize these
+    // CU entries from the DWARF info during the output phase.
+    if ((encoding & static_cast<uint32_t>(UNWIND_MODE_MASK)) ==
+        target->modeDwarfEncoding)
+      continue;
+
+    ConcatInputSection *referentIsec;
+    for (auto it = isec->relocs.begin(); it != isec->relocs.end();) {
+      Reloc &r = *it;
+      // CUE::functionAddress is at offset 0. Skip personality & LSDA relocs.
+      if (r.offset != 0) {
+        ++it;
+        continue;
+      }
+      uint64_t add = r.addend;
+      if (auto *sym = cast_or_null<Defined>(r.referent.dyn_cast<Symbol *>())) {
+        // Check whether the symbol defined in this file is the prevailing one.
+        // Skip if it is e.g. a weak def that didn't prevail.
+        if (sym->getFile() != this) {
+          ++it;
+          continue;
+        }
+        add += sym->value;
+        referentIsec = cast<ConcatInputSection>(sym->isec);
+      } else {
+        referentIsec =
+            cast<ConcatInputSection>(r.referent.dyn_cast<InputSection *>());
+      }
+      // Unwind info lives in __DATA, and finalization of __TEXT will occur
+      // before finalization of __DATA. Moreover, the finalization of unwind
+      // info depends on the exact addresses that it references. So it is safe
+      // for compact unwind to reference addresses in __TEXT, but not addresses
+      // in any other segment.
+      if (referentIsec->getSegName() != segment_names::text)
+        error(isec->getLocation(r.offset) + " references section " +
+              referentIsec->getName() + " which is not in segment __TEXT");
+      // The functionAddress relocations are typically section relocations.
+      // However, unwind info operates on a per-symbol basis, so we search for
+      // the function symbol here.
+      Defined *d = findSymbolAtOffset(referentIsec, add);
+      if (!d) {
+        ++it;
+        continue;
+      }
+      d->unwindEntry = isec;
+      // Now that the symbol points to the unwind entry, we can remove the reloc
+      // that points from the unwind entry back to the symbol.
+      //
+      // First, the symbol keeps the unwind entry alive (and not vice versa), so
+      // this keeps dead-stripping simple.
+      //
+      // Moreover, it reduces the work that ICF needs to do to figure out if
+      // functions with unwind info are foldable.
+      //
+      // However, this does make it possible for ICF to fold CUEs that point to
+      // distinct functions (if the CUEs are otherwise identical).
+      // UnwindInfoSection takes care of this by re-duplicating the CUEs so that
+      // each one can hold a distinct functionAddress value.
+      //
+      // Given that clang emits relocations in reverse order of address, this
+      // relocation should be at the end of the vector for most of our input
+      // object files, so this erase() is typically an O(1) operation.
+      it = isec->relocs.erase(it);
+    }
+  }
+}
+
+struct CIE {
+  macho::Symbol *personalitySymbol = nullptr;
+  bool fdesHaveAug = false;
+  uint8_t lsdaPtrSize = 0; // 0 => no LSDA
+  uint8_t funcPtrSize = 0;
+};
+
+static uint8_t pointerEncodingToSize(uint8_t enc) {
+  switch (enc & 0xf) {
+  case dwarf::DW_EH_PE_absptr:
+    return target->wordSize;
+  case dwarf::DW_EH_PE_sdata4:
+    return 4;
+  case dwarf::DW_EH_PE_sdata8:
+    // ld64 doesn't actually support sdata8, but this seems simple enough...
+    return 8;
+  default:
+    return 0;
+  };
+}
+
+static CIE parseCIE(const InputSection *isec, const EhReader &reader,
+                    size_t off) {
+  // Handling the full generality of possible DWARF encodings would be a major
+  // pain. We instead take advantage of our knowledge of how llvm-mc encodes
+  // DWARF and handle just that.
+  constexpr uint8_t expectedPersonalityEnc =
+      dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_sdata4;
+
+  CIE cie;
+  uint8_t version = reader.readByte(&off);
+  if (version != 1 && version != 3)
+    fatal("Expected CIE version of 1 or 3, got " + Twine(version));
+  StringRef aug = reader.readString(&off);
+  reader.skipLeb128(&off); // skip code alignment
+  reader.skipLeb128(&off); // skip data alignment
+  reader.skipLeb128(&off); // skip return address register
+  reader.skipLeb128(&off); // skip aug data length
+  uint64_t personalityAddrOff = 0;
+  for (char c : aug) {
+    switch (c) {
+    case 'z':
+      cie.fdesHaveAug = true;
+      break;
+    case 'P': {
+      uint8_t personalityEnc = reader.readByte(&off);
+      if (personalityEnc != expectedPersonalityEnc)
+        reader.failOn(off, "unexpected personality encoding 0x" +
+                               Twine::utohexstr(personalityEnc));
+      personalityAddrOff = off;
+      off += 4;
+      break;
+    }
+    case 'L': {
+      uint8_t lsdaEnc = reader.readByte(&off);
+      cie.lsdaPtrSize = pointerEncodingToSize(lsdaEnc);
+      if (cie.lsdaPtrSize == 0)
+        reader.failOn(off, "unexpected LSDA encoding 0x" +
+                               Twine::utohexstr(lsdaEnc));
+      break;
+    }
+    case 'R': {
+      uint8_t pointerEnc = reader.readByte(&off);
+      cie.funcPtrSize = pointerEncodingToSize(pointerEnc);
+      if (cie.funcPtrSize == 0 || !(pointerEnc & dwarf::DW_EH_PE_pcrel))
+        reader.failOn(off, "unexpected pointer encoding 0x" +
+                               Twine::utohexstr(pointerEnc));
+      break;
+    }
+    default:
+      break;
+    }
+  }
+  if (personalityAddrOff != 0) {
+    auto personalityRelocIt =
+        llvm::find_if(isec->relocs, [=](const macho::Reloc &r) {
+          return r.offset == personalityAddrOff;
+        });
+    if (personalityRelocIt == isec->relocs.end())
+      reader.failOn(off, "Failed to locate relocation for personality symbol");
+    cie.personalitySymbol = personalityRelocIt->referent.get<macho::Symbol *>();
+  }
+  return cie;
+}
+
+// EH frame target addresses may be encoded as pcrel offsets. However, instead
+// of using an actual pcrel reloc, ld64 emits subtractor relocations instead.
+// This function recovers the target address from the subtractors, essentially
+// performing the inverse operation of EhRelocator.
+//
+// Concretely, we expect our relocations to write the value of `PC -
+// target_addr` to `PC`. `PC` itself is denoted by a minuend relocation that
+// points to a symbol plus an addend.
+//
+// It is important that the minuend relocation point to a symbol within the
+// same section as the fixup value, since sections may get moved around.
+//
+// For example, for arm64, llvm-mc emits relocations for the target function
+// address like so:
+//
+//   ltmp:
+//     <CIE start>
+//     ...
+//     <CIE end>
+//     ... multiple FDEs ...
+//     <FDE start>
+//     <target function address - (ltmp + pcrel offset)>
+//     ...
+//
+// If any of the FDEs in `multiple FDEs` get dead-stripped, then `FDE start`
+// will move to an earlier address, and `ltmp + pcrel offset` will no longer
+// reflect an accurate pcrel value. To avoid this problem, we "canonicalize"
+// our relocation by adding an `EH_Frame` symbol at `FDE start`, and updating
+// the reloc to be `target function address - (EH_Frame + new pcrel offset)`.
+//
+// If `Invert` is set, then we instead expect `target_addr - PC` to be written
+// to `PC`.
+template <bool Invert = false>
+Defined *
+targetSymFromCanonicalSubtractor(const InputSection *isec,
+                                 std::vector<macho::Reloc>::iterator relocIt) {
+  macho::Reloc &subtrahend = *relocIt;
+  macho::Reloc &minuend = *std::next(relocIt);
+  assert(target->hasAttr(subtrahend.type, RelocAttrBits::SUBTRAHEND));
+  assert(target->hasAttr(minuend.type, RelocAttrBits::UNSIGNED));
+  // Note: pcSym may *not* be exactly at the PC; there's usually a non-zero
+  // addend.
+  auto *pcSym = cast<Defined>(subtrahend.referent.get<macho::Symbol *>());
+  Defined *target =
+      cast_or_null<Defined>(minuend.referent.dyn_cast<macho::Symbol *>());
+  if (!pcSym) {
+    auto *targetIsec =
+        cast<ConcatInputSection>(minuend.referent.get<InputSection *>());
+    target = findSymbolAtOffset(targetIsec, minuend.addend);
+  }
+  if (Invert)
+    std::swap(pcSym, target);
+  if (pcSym->isec == isec) {
+    if (pcSym->value - (Invert ? -1 : 1) * minuend.addend != subtrahend.offset)
+      fatal("invalid FDE relocation in __eh_frame");
+  } else {
+    // Ensure the pcReloc points to a symbol within the current EH frame.
+    // HACK: we should really verify that the original relocation's semantics
+    // are preserved. In particular, we should have
+    // `oldSym->value + oldOffset == newSym + newOffset`. However, we don't
+    // have an easy way to access the offsets from this point in the code; some
+    // refactoring is needed for that.
+    macho::Reloc &pcReloc = Invert ? minuend : subtrahend;
+    pcReloc.referent = isec->symbols[0];
+    assert(isec->symbols[0]->value == 0);
+    minuend.addend = pcReloc.offset * (Invert ? 1LL : -1LL);
+  }
+  return target;
+}
+
+Defined *findSymbolAtAddress(const std::vector<Section *> &sections,
+                             uint64_t addr) {
+  Section *sec = findContainingSection(sections, &addr);
+  auto *isec = cast<ConcatInputSection>(findContainingSubsection(*sec, &addr));
+  return findSymbolAtOffset(isec, addr);
+}
+
+// For symbols that don't have compact unwind info, associate them with the more
+// general-purpose (and verbose) DWARF unwind info found in __eh_frame.
+//
+// This requires us to parse the contents of __eh_frame. See EhFrame.h for a
+// description of its format.
+//
+// While parsing, we also look for what MC calls "abs-ified" relocations -- they
+// are relocations which are implicitly encoded as offsets in the section data.
+// We convert them into explicit Reloc structs so that the EH frames can be
+// handled just like a regular ConcatInputSection later in our output phase.
+//
+// We also need to handle the case where our input object file has explicit
+// relocations. This is the case when e.g. it's the output of `ld -r`. We only
+// look for the "abs-ified" relocation if an explicit relocation is absent.
+void ObjFile::registerEhFrames(Section &ehFrameSection) {
+  DenseMap<const InputSection *, CIE> cieMap;
+  for (const Subsection &subsec : ehFrameSection.subsections) {
+    auto *isec = cast<ConcatInputSection>(subsec.isec);
+    uint64_t isecOff = subsec.offset;
+
+    // Subtractor relocs require the subtrahend to be a symbol reloc. Ensure
+    // that all EH frames have an associated symbol so that we can generate
+    // subtractor relocs that reference them.
+    if (isec->symbols.size() == 0)
+      make<Defined>("EH_Frame", isec->getFile(), isec, /*value=*/0,
+                    isec->getSize(), /*isWeakDef=*/false, /*isExternal=*/false,
+                    /*isPrivateExtern=*/false, /*includeInSymtab=*/false,
+                    /*isThumb=*/false, /*isReferencedDynamically=*/false,
+                    /*noDeadStrip=*/false);
+    else if (isec->symbols[0]->value != 0)
+      fatal("found symbol at unexpected offset in __eh_frame");
+
+    EhReader reader(this, isec->data, subsec.offset);
+    size_t dataOff = 0; // Offset from the start of the EH frame.
+    reader.skipValidLength(&dataOff); // readLength() already validated this.
+    // cieOffOff is the offset from the start of the EH frame to the cieOff
+    // value, which is itself an offset from the current PC to a CIE.
+    const size_t cieOffOff = dataOff;
+
+    EhRelocator ehRelocator(isec);
+    auto cieOffRelocIt = llvm::find_if(
+        isec->relocs, [=](const Reloc &r) { return r.offset == cieOffOff; });
+    InputSection *cieIsec = nullptr;
+    if (cieOffRelocIt != isec->relocs.end()) {
+      // We already have an explicit relocation for the CIE offset.
+      cieIsec =
+          targetSymFromCanonicalSubtractor</*Invert=*/true>(isec, cieOffRelocIt)
+              ->isec;
+      dataOff += sizeof(uint32_t);
+    } else {
+      // If we haven't found a relocation, then the CIE offset is most likely
+      // embedded in the section data (AKA an "abs-ified" reloc.). Parse that
+      // and generate a Reloc struct.
+      uint32_t cieMinuend = reader.readU32(&dataOff);
+      if (cieMinuend == 0) {
+        cieIsec = isec;
+      } else {
+        uint32_t cieOff = isecOff + dataOff - cieMinuend;
+        cieIsec = findContainingSubsection(ehFrameSection, &cieOff);
+        if (cieIsec == nullptr)
+          fatal("failed to find CIE");
+      }
+      if (cieIsec != isec)
+        ehRelocator.makeNegativePcRel(cieOffOff, cieIsec->symbols[0],
+                                      /*length=*/2);
+    }
+    if (cieIsec == isec) {
+      cieMap[cieIsec] = parseCIE(isec, reader, dataOff);
+      continue;
+    }
+
+    assert(cieMap.count(cieIsec));
+    const CIE &cie = cieMap[cieIsec];
+    // Offset of the function address within the EH frame.
+    const size_t funcAddrOff = dataOff;
+    uint64_t funcAddr = reader.readPointer(&dataOff, cie.funcPtrSize) +
+                        ehFrameSection.addr + isecOff + funcAddrOff;
+    uint32_t funcLength = reader.readPointer(&dataOff, cie.funcPtrSize);
+    size_t lsdaAddrOff = 0; // Offset of the LSDA address within the EH frame.
+    std::optional<uint64_t> lsdaAddrOpt;
+    if (cie.fdesHaveAug) {
+      reader.skipLeb128(&dataOff);
+      lsdaAddrOff = dataOff;
+      if (cie.lsdaPtrSize != 0) {
+        uint64_t lsdaOff = reader.readPointer(&dataOff, cie.lsdaPtrSize);
+        if (lsdaOff != 0) // FIXME possible to test this?
+          lsdaAddrOpt = ehFrameSection.addr + isecOff + lsdaAddrOff + lsdaOff;
+      }
+    }
+
+    auto funcAddrRelocIt = isec->relocs.end();
+    auto lsdaAddrRelocIt = isec->relocs.end();
+    for (auto it = isec->relocs.begin(); it != isec->relocs.end(); ++it) {
+      if (it->offset == funcAddrOff)
+        funcAddrRelocIt = it++; // Found subtrahend; skip over minuend reloc
+      else if (lsdaAddrOpt && it->offset == lsdaAddrOff)
+        lsdaAddrRelocIt = it++; // Found subtrahend; skip over minuend reloc
+    }
+
+    Defined *funcSym;
+    if (funcAddrRelocIt != isec->relocs.end()) {
+      funcSym = targetSymFromCanonicalSubtractor(isec, funcAddrRelocIt);
+      // Canonicalize the symbol. If there are multiple symbols at the same
+      // address, we want both `registerEhFrame` and `registerCompactUnwind`
+      // to register the unwind entry under same symbol.
+      // This is not particularly efficient, but we should run into this case
+      // infrequently (only when handling the output of `ld -r`).
+      if (funcSym->isec)
+        funcSym = findSymbolAtOffset(cast<ConcatInputSection>(funcSym->isec),
+                                     funcSym->value);
+    } else {
+      funcSym = findSymbolAtAddress(sections, funcAddr);
+      ehRelocator.makePcRel(funcAddrOff, funcSym, target->p2WordSize);
+    }
+    // The symbol has been coalesced, or already has a compact unwind entry.
+    if (!funcSym || funcSym->getFile() != this || funcSym->unwindEntry) {
+      // We must prune unused FDEs for correctness, so we cannot rely on
+      // -dead_strip being enabled.
+      isec->live = false;
+      continue;
+    }
+
+    InputSection *lsdaIsec = nullptr;
+    if (lsdaAddrRelocIt != isec->relocs.end()) {
+      lsdaIsec = targetSymFromCanonicalSubtractor(isec, lsdaAddrRelocIt)->isec;
+    } else if (lsdaAddrOpt) {
+      uint64_t lsdaAddr = *lsdaAddrOpt;
+      Section *sec = findContainingSection(sections, &lsdaAddr);
+      lsdaIsec =
+          cast<ConcatInputSection>(findContainingSubsection(*sec, &lsdaAddr));
+      ehRelocator.makePcRel(lsdaAddrOff, lsdaIsec, target->p2WordSize);
+    }
+
+    fdes[isec] = {funcLength, cie.personalitySymbol, lsdaIsec};
+    funcSym->unwindEntry = isec;
+    ehRelocator.commit();
+  }
+
+  // __eh_frame is marked as S_ATTR_LIVE_SUPPORT in input files, because FDEs
+  // are normally required to be kept alive if they reference a live symbol.
+  // However, we've explicitly created a dependency from a symbol to its FDE, so
+  // dead-stripping will just work as usual, and S_ATTR_LIVE_SUPPORT will only
+  // serve to incorrectly prevent us from dead-stripping duplicate FDEs for a
+  // live symbol (e.g. if there were multiple weak copies). Remove this flag to
+  // let dead-stripping proceed correctly.
+  ehFrameSection.flags &= ~S_ATTR_LIVE_SUPPORT;
+}
+
+std::string ObjFile::sourceFile() const {
+  SmallString<261> dir(compileUnit->getCompilationDir());
+  StringRef sep = sys::path::get_separator();
+  // We don't use `path::append` here because we want an empty `dir` to result
+  // in an absolute path. `append` would give us a relative path for that case.
+  if (!dir.endswith(sep))
+    dir += sep;
+  return (dir + compileUnit->getUnitDIE().getShortName()).str();
+}
+
+lld::DWARFCache *ObjFile::getDwarf() {
+  llvm::call_once(initDwarf, [this]() {
+    auto dwObj = DwarfObject::create(this);
+    if (!dwObj)
+      return;
+    dwarfCache = std::make_unique<DWARFCache>(std::make_unique<DWARFContext>(
+        std::move(dwObj), "",
+        [&](Error err) { warn(getName() + ": " + toString(std::move(err))); },
+        [&](Error warning) {
+          warn(getName() + ": " + toString(std::move(warning)));
+        }));
+  });
+
+  return dwarfCache.get();
+}
 // The path can point to either a dylib or a .tbd file.
 static DylibFile *loadDylib(StringRef path, DylibFile *umbrella) {
-  Optional<MemoryBufferRef> mbref = readFile(path);
+  std::optional<MemoryBufferRef> mbref = readFile(path);
   if (!mbref) {
     error("could not read dylib file at " + path);
     return nullptr;
@@ -871,10 +1564,11 @@ static DylibFile *findDylib(StringRef path, DylibFile *umbrella,
       for (StringRef dir : config->frameworkSearchPaths) {
         SmallString<128> candidate = dir;
         path::append(candidate, frameworkName);
-        if (Optional<std::string> dylibPath = resolveDylibPath(candidate))
+        if (std::optional<StringRef> dylibPath =
+                resolveDylibPath(candidate.str()))
           return loadDylib(*dylibPath, umbrella);
       }
-    } else if (Optional<StringRef> dylibPath = findPathCombination(
+    } else if (std::optional<StringRef> dylibPath = findPathCombination(
                    stem, config->librarySearchPaths, {".tbd", ".dylib"}))
       return loadDylib(*dylibPath, umbrella);
   }
@@ -882,7 +1576,7 @@ static DylibFile *findDylib(StringRef path, DylibFile *umbrella,
   // 2. As absolute path.
   if (path::is_absolute(path, path::Style::posix))
     for (StringRef root : config->systemLibraryRoots)
-      if (Optional<std::string> dylibPath =
+      if (std::optional<StringRef> dylibPath =
               resolveDylibPath((root + path).str()))
         return loadDylib(*dylibPath, umbrella);
 
@@ -912,7 +1606,7 @@ static DylibFile *findDylib(StringRef path, DylibFile *umbrella,
         path::remove_filename(newPath);
       }
       path::append(newPath, rpath, path.drop_front(strlen("@rpath/")));
-      if (Optional<std::string> dylibPath = resolveDylibPath(newPath))
+      if (std::optional<StringRef> dylibPath = resolveDylibPath(newPath.str()))
         return loadDylib(*dylibPath, umbrella);
     }
   }
@@ -923,14 +1617,15 @@ static DylibFile *findDylib(StringRef path, DylibFile *umbrella,
          make_pointee_range(currentTopLevelTapi->documents())) {
       assert(child.documents().empty());
       if (path == child.getInstallName()) {
-        auto file = make<DylibFile>(child, umbrella);
+        auto file = make<DylibFile>(child, umbrella, /*isBundleLoader=*/false,
+                                    /*explicitlyLinked=*/false);
         file->parseReexports(child);
         return file;
       }
     }
   }
 
-  if (Optional<std::string> dylibPath = resolveDylibPath(path))
+  if (std::optional<StringRef> dylibPath = resolveDylibPath(path))
     return loadDylib(*dylibPath, umbrella);
 
   return nullptr;
@@ -956,23 +1651,23 @@ static bool isImplicitlyLinked(StringRef path) {
   return false;
 }
 
-static void loadReexport(StringRef path, DylibFile *umbrella,
+void DylibFile::loadReexport(StringRef path, DylibFile *umbrella,
                          const InterfaceFile *currentTopLevelTapi) {
   DylibFile *reexport = findDylib(path, umbrella, currentTopLevelTapi);
   if (!reexport)
-    error("unable to locate re-export with install name " + path);
+    error(toString(this) + ": unable to locate re-export with install name " +
+          path);
 }
 
 DylibFile::DylibFile(MemoryBufferRef mb, DylibFile *umbrella,
-                     bool isBundleLoader)
+                     bool isBundleLoader, bool explicitlyLinked)
     : InputFile(DylibKind, mb), refState(RefState::Unreferenced),
-      isBundleLoader(isBundleLoader) {
+      explicitlyLinked(explicitlyLinked), isBundleLoader(isBundleLoader) {
   assert(!isBundleLoader || !umbrella);
   if (umbrella == nullptr)
     umbrella = this;
   this->umbrella = umbrella;
 
-  auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
   auto *hdr = reinterpret_cast<const mach_header *>(mb.getBufferStart());
 
   // Initialize installName.
@@ -985,7 +1680,7 @@ DylibFile::DylibFile(MemoryBufferRef mb, DylibFile *umbrella,
   } else if (!isBundleLoader) {
     // macho_executable and macho_bundle don't have LC_ID_DYLIB,
     // so it's OK.
-    error("dylib " + toString(this) + " missing LC_ID_DYLIB load command");
+    error(toString(this) + ": dylib missing LC_ID_DYLIB load command");
     return;
   }
 
@@ -1007,24 +1702,56 @@ DylibFile::DylibFile(MemoryBufferRef mb, DylibFile *umbrella,
 
   // Initialize symbols.
   exportingFile = isImplicitlyLinked(installName) ? this : this->umbrella;
-  if (const load_command *cmd = findCommand(hdr, LC_DYLD_INFO_ONLY)) {
-    auto *c = reinterpret_cast<const dyld_info_command *>(cmd);
-    parseTrie(buf + c->export_off, c->export_size,
-              [&](const Twine &name, uint64_t flags) {
-                StringRef savedName = saver.save(name);
-                if (handleLDSymbol(savedName))
-                  return;
-                bool isWeakDef = flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION;
-                bool isTlv = flags & EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL;
-                symbols.push_back(symtab->addDylib(savedName, exportingFile,
-                                                   isWeakDef, isTlv));
-              });
+
+  const auto *dyldInfo = findCommand<dyld_info_command>(hdr, LC_DYLD_INFO_ONLY);
+  const auto *exportsTrie =
+      findCommand<linkedit_data_command>(hdr, LC_DYLD_EXPORTS_TRIE);
+  if (dyldInfo && exportsTrie) {
+    // It's unclear what should happen in this case. Maybe we should only error
+    // out if the two load commands refer to different data?
+    error(toString(this) +
+          ": dylib has both LC_DYLD_INFO_ONLY and LC_DYLD_EXPORTS_TRIE");
+    return;
+  } else if (dyldInfo) {
+    parseExportedSymbols(dyldInfo->export_off, dyldInfo->export_size);
+  } else if (exportsTrie) {
+    parseExportedSymbols(exportsTrie->dataoff, exportsTrie->datasize);
   } else {
-    error("LC_DYLD_INFO_ONLY not found in " + toString(this));
+    error("No LC_DYLD_INFO_ONLY or LC_DYLD_EXPORTS_TRIE found in " +
+          toString(this));
     return;
   }
 }
 
+void DylibFile::parseExportedSymbols(uint32_t offset, uint32_t size) {
+  struct TrieEntry {
+    StringRef name;
+    uint64_t flags;
+  };
+
+  auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
+  std::vector<TrieEntry> entries;
+  // Find all the $ld$* symbols to process first.
+  parseTrie(buf + offset, size, [&](const Twine &name, uint64_t flags) {
+    StringRef savedName = saver().save(name);
+    if (handleLDSymbol(savedName))
+      return;
+    entries.push_back({savedName, flags});
+  });
+
+  // Process the "normal" symbols.
+  for (TrieEntry &entry : entries) {
+    if (exportingFile->hiddenSymbols.contains(CachedHashStringRef(entry.name)))
+      continue;
+
+    bool isWeakDef = entry.flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION;
+    bool isTlv = entry.flags & EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL;
+
+    symbols.push_back(
+        symtab->addDylib(entry.name, exportingFile, isWeakDef, isTlv));
+  }
+}
+
 void DylibFile::parseLoadCommands(MemoryBufferRef mb) {
   auto *hdr = reinterpret_cast<const mach_header *>(mb.getBufferStart());
   const uint8_t *p = reinterpret_cast<const uint8_t *>(mb.getBufferStart()) +
@@ -1057,24 +1784,63 @@ void DylibFile::parseLoadCommands(MemoryBufferRef mb) {
   }
 }
 
-// Some versions of XCode ship with .tbd files that don't have the right
+// Some versions of Xcode ship with .tbd files that don't have the right
 // platform settings.
-static constexpr std::array<StringRef, 3> skipPlatformChecks{
+constexpr std::array<StringRef, 3> skipPlatformChecks{
     "/usr/lib/system/libsystem_kernel.dylib",
     "/usr/lib/system/libsystem_platform.dylib",
     "/usr/lib/system/libsystem_pthread.dylib"};
 
+static bool skipPlatformCheckForCatalyst(const InterfaceFile &interface,
+                                         bool explicitlyLinked) {
+  // Catalyst outputs can link against implicitly linked macOS-only libraries.
+  if (config->platform() != PLATFORM_MACCATALYST || explicitlyLinked)
+    return false;
+  return is_contained(interface.targets(),
+                      MachO::Target(config->arch(), PLATFORM_MACOS));
+}
+
+static bool isArchABICompatible(ArchitectureSet archSet,
+                                Architecture targetArch) {
+  uint32_t cpuType;
+  uint32_t targetCpuType;
+  std::tie(targetCpuType, std::ignore) = getCPUTypeFromArchitecture(targetArch);
+
+  return llvm::any_of(archSet, [&](const auto &p) {
+    std::tie(cpuType, std::ignore) = getCPUTypeFromArchitecture(p);
+    return cpuType == targetCpuType;
+  });
+}
+
+static bool isTargetPlatformArchCompatible(
+    InterfaceFile::const_target_range interfaceTargets, Target target) {
+  if (is_contained(interfaceTargets, target))
+    return true;
+
+  if (config->forceExactCpuSubtypeMatch)
+    return false;
+
+  ArchitectureSet archSet;
+  for (const auto &p : interfaceTargets)
+    if (p.Platform == target.Platform)
+      archSet.set(p.Arch);
+  if (archSet.empty())
+    return false;
+
+  return isArchABICompatible(archSet, target.Arch);
+}
+
 DylibFile::DylibFile(const InterfaceFile &interface, DylibFile *umbrella,
-                     bool isBundleLoader)
+                     bool isBundleLoader, bool explicitlyLinked)
     : InputFile(DylibKind, interface), refState(RefState::Unreferenced),
-      isBundleLoader(isBundleLoader) {
+      explicitlyLinked(explicitlyLinked), isBundleLoader(isBundleLoader) {
   // FIXME: Add test for the missing TBD code path.
 
   if (umbrella == nullptr)
     umbrella = this;
   this->umbrella = umbrella;
 
-  installName = saver.save(interface.getInstallName());
+  installName = saver().save(interface.getInstallName());
   compatibilityVersion = interface.getCompatibilityVersion().rawValue();
   currentVersion = interface.getCurrentVersion().rawValue();
 
@@ -1083,7 +1849,9 @@ DylibFile::DylibFile(const InterfaceFile &interface, DylibFile *umbrella,
   inputFiles.insert(this);
 
   if (!is_contained(skipPlatformChecks, installName) &&
-      !is_contained(interface.targets(), config->platformInfo.target)) {
+      !isTargetPlatformArchCompatible(interface.targets(),
+                                      config->platformInfo.target) &&
+      !skipPlatformCheckForCatalyst(interface, explicitlyLinked)) {
     error(toString(this) + " is incompatible with " +
           std::string(config->platformInfo.target));
     return;
@@ -1092,51 +1860,108 @@ DylibFile::DylibFile(const InterfaceFile &interface, DylibFile *umbrella,
   checkAppExtensionSafety(interface.isApplicationExtensionSafe());
 
   exportingFile = isImplicitlyLinked(installName) ? this : umbrella;
-  auto addSymbol = [&](const Twine &name) -> void {
-    symbols.push_back(symtab->addDylib(saver.save(name), exportingFile,
-                                       /*isWeakDef=*/false,
-                                       /*isTlv=*/false));
+  auto addSymbol = [&](const llvm::MachO::Symbol &symbol,
+                       const Twine &name) -> void {
+    StringRef savedName = saver().save(name);
+    if (exportingFile->hiddenSymbols.contains(CachedHashStringRef(savedName)))
+      return;
+
+    symbols.push_back(symtab->addDylib(savedName, exportingFile,
+                                       symbol.isWeakDefined(),
+                                       symbol.isThreadLocalValue()));
   };
-  // TODO(compnerd) filter out symbols based on the target platform
-  // TODO: handle weak defs, thread locals
+
+  std::vector<const llvm::MachO::Symbol *> normalSymbols;
+  normalSymbols.reserve(interface.symbolsCount());
   for (const auto *symbol : interface.symbols()) {
-    if (!symbol->getArchitectures().has(config->arch()))
+    if (!isArchABICompatible(symbol->getArchitectures(), config->arch()))
       continue;
-
     if (handleLDSymbol(symbol->getName()))
       continue;
 
     switch (symbol->getKind()) {
     case SymbolKind::GlobalSymbol:
-      addSymbol(symbol->getName());
+    case SymbolKind::ObjectiveCClass:
+    case SymbolKind::ObjectiveCClassEHType:
+    case SymbolKind::ObjectiveCInstanceVariable:
+      normalSymbols.push_back(symbol);
+    }
+  }
+
+  // TODO(compnerd) filter out symbols based on the target platform
+  for (const auto *symbol : normalSymbols) {
+    switch (symbol->getKind()) {
+    case SymbolKind::GlobalSymbol:
+      addSymbol(*symbol, symbol->getName());
       break;
     case SymbolKind::ObjectiveCClass:
       // XXX ld64 only creates these symbols when -ObjC is passed in. We may
       // want to emulate that.
-      addSymbol(objc::klass + symbol->getName());
-      addSymbol(objc::metaclass + symbol->getName());
+      addSymbol(*symbol, objc::klass + symbol->getName());
+      addSymbol(*symbol, objc::metaclass + symbol->getName());
       break;
     case SymbolKind::ObjectiveCClassEHType:
-      addSymbol(objc::ehtype + symbol->getName());
+      addSymbol(*symbol, objc::ehtype + symbol->getName());
       break;
     case SymbolKind::ObjectiveCInstanceVariable:
-      addSymbol(objc::ivar + symbol->getName());
+      addSymbol(*symbol, objc::ivar + symbol->getName());
       break;
     }
   }
 }
 
+DylibFile::DylibFile(DylibFile *umbrella)
+    : InputFile(DylibKind, MemoryBufferRef{}), refState(RefState::Unreferenced),
+      explicitlyLinked(false), isBundleLoader(false) {
+  if (umbrella == nullptr)
+    umbrella = this;
+  this->umbrella = umbrella;
+}
+
 void DylibFile::parseReexports(const InterfaceFile &interface) {
   const InterfaceFile *topLevel =
       interface.getParent() == nullptr ? &interface : interface.getParent();
-  for (InterfaceFileRef intfRef : interface.reexportedLibraries()) {
+  for (const InterfaceFileRef &intfRef : interface.reexportedLibraries()) {
     InterfaceFile::const_target_range targets = intfRef.targets();
     if (is_contained(skipPlatformChecks, intfRef.getInstallName()) ||
-        is_contained(targets, config->platformInfo.target))
+        isTargetPlatformArchCompatible(targets, config->platformInfo.target))
       loadReexport(intfRef.getInstallName(), exportingFile, topLevel);
   }
 }
 
+bool DylibFile::isExplicitlyLinked() const {
+  if (!explicitlyLinked)
+    return false;
+
+  // If this dylib was explicitly linked, but at least one of the symbols
+  // of the synthetic dylibs it created via $ld$previous symbols is
+  // referenced, then that synthetic dylib fulfils the explicit linkedness
+  // and we can deadstrip this dylib if it's unreferenced.
+  for (const auto *dylib : extraDylibs)
+    if (dylib->isReferenced())
+      return false;
+
+  return true;
+}
+
+DylibFile *DylibFile::getSyntheticDylib(StringRef installName,
+                                        uint32_t currentVersion,
+                                        uint32_t compatVersion) {
+  for (DylibFile *dylib : extraDylibs)
+    if (dylib->installName == installName) {
+      // FIXME: Check what to do if different $ld$previous symbols
+      // request the same dylib, but with different versions.
+      return dylib;
+    }
+
+  auto *dylib = make<DylibFile>(umbrella == this ? nullptr : umbrella);
+  dylib->installName = saver().save(installName);
+  dylib->currentVersion = currentVersion;
+  dylib->compatibilityVersion = compatVersion;
+  extraDylibs.push_back(dylib);
+  return dylib;
+}
+
 // $ld$ symbols modify the properties/behavior of the library (e.g. its install
 // name, compatibility version or hide/add symbols) for specific target
 // versions.
@@ -1151,6 +1976,8 @@ bool DylibFile::handleLDSymbol(StringRef originalName) {
     handleLDPreviousSymbol(name, originalName);
   else if (action == "install_name")
     handleLDInstallNameSymbol(name, originalName);
+  else if (action == "hide")
+    handleLDHideSymbol(name, originalName);
   return true;
 }
 
@@ -1170,10 +1997,9 @@ void DylibFile::handleLDPreviousSymbol(StringRef name, StringRef originalName) {
   std::tie(platformStr, name) = name.split('$');
   std::tie(startVersion, name) = name.split('$');
   std::tie(endVersion, name) = name.split('$');
-  std::tie(symbolName, rest) = name.split('$');
-  // TODO: ld64 contains some logic for non-empty symbolName as well.
-  if (!symbolName.empty())
-    return;
+  std::tie(symbolName, rest) = name.rsplit('$');
+
+  // FIXME: Does this do the right thing for zippered files?
   unsigned platform;
   if (platformStr.getAsInteger(10, platform) ||
       platform != static_cast<unsigned>(config->platform()))
@@ -1181,30 +2007,57 @@ void DylibFile::handleLDPreviousSymbol(StringRef name, StringRef originalName) {
 
   VersionTuple start;
   if (start.tryParse(startVersion)) {
-    warn("failed to parse start version, symbol '" + originalName +
-         "' ignored");
+    warn(toString(this) + ": failed to parse start version, symbol '" +
+         originalName + "' ignored");
     return;
   }
   VersionTuple end;
   if (end.tryParse(endVersion)) {
-    warn("failed to parse end version, symbol '" + originalName + "' ignored");
+    warn(toString(this) + ": failed to parse end version, symbol '" +
+         originalName + "' ignored");
     return;
   }
   if (config->platformInfo.minimum < start ||
       config->platformInfo.minimum >= end)
     return;
 
-  this->installName = saver.save(installName);
-
+  // Initialized to compatibilityVersion for the symbolName branch below.
+  uint32_t newCompatibilityVersion = compatibilityVersion;
+  uint32_t newCurrentVersionForSymbol = currentVersion;
   if (!compatVersion.empty()) {
     VersionTuple cVersion;
     if (cVersion.tryParse(compatVersion)) {
-      warn("failed to parse compatibility version, symbol '" + originalName +
+      warn(toString(this) +
+           ": failed to parse compatibility version, symbol '" + originalName +
            "' ignored");
       return;
     }
-    compatibilityVersion = encodeVersion(cVersion);
+    newCompatibilityVersion = encodeVersion(cVersion);
+    newCurrentVersionForSymbol = newCompatibilityVersion;
+  }
+
+  if (!symbolName.empty()) {
+    // A $ld$previous$ symbol with symbol name adds a symbol with that name to
+    // a dylib with given name and version.
+    auto *dylib = getSyntheticDylib(installName, newCurrentVersionForSymbol,
+                                    newCompatibilityVersion);
+
+    // The tbd file usually contains the $ld$previous symbol for an old version,
+    // and then the symbol itself later, for newer deployment targets, like so:
+    //    symbols: [
+    //      '$ld$previous$/Another$$1$3.0$14.0$_zzz$',
+    //      _zzz,
+    //    ]
+    // Since the symbols are sorted, adding them to the symtab in the given
+    // order means the $ld$previous version of _zzz will prevail, as desired.
+    dylib->symbols.push_back(symtab->addDylib(
+        saver().save(symbolName), dylib, /*isWeakDef=*/false, /*isTlv=*/false));
+    return;
   }
+
+  // A $ld$previous$ symbol without symbol name modifies the dylib it's in.
+  this->installName = saver().save(installName);
+  this->compatibilityVersion = newCompatibilityVersion;
 }
 
 void DylibFile::handleLDInstallNameSymbol(StringRef name,
@@ -1214,9 +2067,33 @@ void DylibFile::handleLDInstallNameSymbol(StringRef name,
   std::tie(condition, installName) = name.split('$');
   VersionTuple version;
   if (!condition.consume_front("os") || version.tryParse(condition))
-    warn("failed to parse os version, symbol '" + originalName + "' ignored");
+    warn(toString(this) + ": failed to parse os version, symbol '" +
+         originalName + "' ignored");
   else if (version == config->platformInfo.minimum)
-    this->installName = saver.save(installName);
+    this->installName = saver().save(installName);
+}
+
+void DylibFile::handleLDHideSymbol(StringRef name, StringRef originalName) {
+  StringRef symbolName;
+  bool shouldHide = true;
+  if (name.startswith("os")) {
+    // If it's hidden based on versions.
+    name = name.drop_front(2);
+    StringRef minVersion;
+    std::tie(minVersion, symbolName) = name.split('$');
+    VersionTuple versionTup;
+    if (versionTup.tryParse(minVersion)) {
+      warn(toString(this) + ": failed to parse hidden version, symbol `" + originalName +
+           "` ignored.");
+      return;
+    }
+    shouldHide = versionTup == config->platformInfo.minimum;
+  } else {
+    symbolName = name;
+  }
+
+  if (shouldHide)
+    exportingFile->hiddenSymbols.insert(CachedHashStringRef(symbolName));
 }
 
 void DylibFile::checkAppExtensionSafety(bool dylibIsAppExtensionSafe) const {
@@ -1224,59 +2101,85 @@ void DylibFile::checkAppExtensionSafety(bool dylibIsAppExtensionSafe) const {
     warn("using '-application_extension' with unsafe dylib: " + toString(this));
 }
 
-ArchiveFile::ArchiveFile(std::unique_ptr<object::Archive> &&f)
-    : InputFile(ArchiveKind, f->getMemoryBufferRef()), file(std::move(f)) {
+ArchiveFile::ArchiveFile(std::unique_ptr<object::Archive> &&f, bool forceHidden)
+    : InputFile(ArchiveKind, f->getMemoryBufferRef()), file(std::move(f)),
+      forceHidden(forceHidden) {}
+
+void ArchiveFile::addLazySymbols() {
   for (const object::Archive::Symbol &sym : file->symbols())
-    symtab->addLazy(sym.getName(), this, sym);
+    symtab->addLazyArchive(sym.getName(), this, sym);
 }
 
-void ArchiveFile::fetch(const object::Archive::Symbol &sym) {
-  object::Archive::Child c =
-      CHECK(sym.getMember(), toString(this) +
-                                 ": could not get the member for symbol " +
-                                 toMachOString(sym));
+static Expected<InputFile *>
+loadArchiveMember(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName,
+                  uint64_t offsetInArchive, bool forceHidden) {
+  if (config->zeroModTime)
+    modTime = 0;
+
+  switch (identify_magic(mb.getBuffer())) {
+  case file_magic::macho_object:
+    return make<ObjFile>(mb, modTime, archiveName, /*lazy=*/false, forceHidden);
+  case file_magic::bitcode:
+    return make<BitcodeFile>(mb, archiveName, offsetInArchive, /*lazy=*/false,
+                             forceHidden);
+  default:
+    return createStringError(inconvertibleErrorCode(),
+                             mb.getBufferIdentifier() +
+                                 " has unhandled file type");
+  }
+}
 
+Error ArchiveFile::fetch(const object::Archive::Child &c, StringRef reason) {
   if (!seen.insert(c.getChildOffset()).second)
-    return;
+    return Error::success();
 
-  MemoryBufferRef mb =
-      CHECK(c.getMemoryBufferRef(),
-            toString(this) +
-                ": could not get the buffer for the member defining symbol " +
-                toMachOString(sym));
+  Expected<MemoryBufferRef> mb = c.getMemoryBufferRef();
+  if (!mb)
+    return mb.takeError();
 
+  // Thin archives refer to .o files, so --reproduce needs the .o files too.
   if (tar && c.getParent()->isThin())
-    tar->append(relativeToRoot(CHECK(c.getFullName(), this)), mb.getBuffer());
+    tar->append(relativeToRoot(CHECK(c.getFullName(), this)), mb->getBuffer());
+
+  Expected<TimePoint<std::chrono::seconds>> modTime = c.getLastModified();
+  if (!modTime)
+    return modTime.takeError();
 
-  uint32_t modTime = toTimeT(
-      CHECK(c.getLastModified(), toString(this) +
-                                     ": could not get the modification time "
-                                     "for the member defining symbol " +
-                                     toMachOString(sym)));
+  Expected<InputFile *> file = loadArchiveMember(
+      *mb, toTimeT(*modTime), getName(), c.getChildOffset(), forceHidden);
+
+  if (!file)
+    return file.takeError();
+
+  inputFiles.insert(*file);
+  printArchiveMemberLoad(reason, *file);
+  return Error::success();
+}
+
+void ArchiveFile::fetch(const object::Archive::Symbol &sym) {
+  object::Archive::Child c =
+      CHECK(sym.getMember(), toString(this) +
+                                 ": could not get the member defining symbol " +
+                                 toMachOString(sym));
 
   // `sym` is owned by a LazySym, which will be replace<>()d by make<ObjFile>
   // and become invalid after that call. Copy it to the stack so we can refer
   // to it later.
   const object::Archive::Symbol symCopy = sym;
 
-  if (Optional<InputFile *> file = loadArchiveMember(
-          mb, modTime, getName(), /*objCOnly=*/false, c.getChildOffset())) {
-    inputFiles.insert(*file);
-    // ld64 doesn't demangle sym here even with -demangle.
-    // Match that: intentionally don't call toMachOString().
-    printArchiveMemberLoad(symCopy.getName(), *file);
-  }
+  // ld64 doesn't demangle sym here even with -demangle.
+  // Match that: intentionally don't call toMachOString().
+  if (Error e = fetch(c, symCopy.getName()))
+    error(toString(this) + ": could not get the member defining symbol " +
+          toMachOString(symCopy) + ": " + toString(std::move(e)));
 }
 
 static macho::Symbol *createBitcodeSymbol(const lto::InputFile::Symbol &objSym,
                                           BitcodeFile &file) {
-  StringRef name = saver.save(objSym.getName());
+  StringRef name = saver().save(objSym.getName());
 
-  // TODO: support weak references
   if (objSym.isUndefined())
-    return symtab->addUndefined(name, &file, /*isWeakRef=*/false);
-
-  assert(!objSym.isCommon() && "TODO: support common symbols in LTO");
+    return symtab->addUndefined(name, &file, /*isWeakRef=*/objSym.isWeak());
 
   // TODO: Write a test demonstrating why computing isPrivateExtern before
   // LTO compilation is important.
@@ -1291,37 +2194,91 @@ static macho::Symbol *createBitcodeSymbol(const lto::InputFile::Symbol &objSym,
   case GlobalValue::DefaultVisibility:
     break;
   }
+  isPrivateExtern = isPrivateExtern || objSym.canBeOmittedFromSymbolTable() ||
+                    file.forceHidden;
+
+  if (objSym.isCommon())
+    return symtab->addCommon(name, &file, objSym.getCommonSize(),
+                             objSym.getCommonAlignment(), isPrivateExtern);
 
   return symtab->addDefined(name, &file, /*isec=*/nullptr, /*value=*/0,
                             /*size=*/0, objSym.isWeak(), isPrivateExtern,
                             /*isThumb=*/false,
                             /*isReferencedDynamically=*/false,
-                            /*noDeadStrip=*/false);
+                            /*noDeadStrip=*/false,
+                            /*isWeakDefCanBeHidden=*/false);
 }
 
 BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
-                         uint64_t offsetInArchive)
-    : InputFile(BitcodeKind, mb) {
+                         uint64_t offsetInArchive, bool lazy, bool forceHidden)
+    : InputFile(BitcodeKind, mb, lazy), forceHidden(forceHidden) {
+  this->archiveName = std::string(archiveName);
   std::string path = mb.getBufferIdentifier().str();
+  if (config->thinLTOIndexOnly)
+    path = replaceThinLTOSuffix(mb.getBufferIdentifier());
+
   // ThinLTO assumes that all MemoryBufferRefs given to it have a unique
   // name. If two members with the same name are provided, this causes a
   // collision and ThinLTO can't proceed.
   // So, we append the archive name to disambiguate two members with the same
   // name from multiple different archives, and offset within the archive to
   // disambiguate two members of the same name from a single archive.
-  MemoryBufferRef mbref(
-      mb.getBuffer(),
-      saver.save(archiveName.empty() ? path
-                                     : archiveName + sys::path::filename(path) +
-                                           utostr(offsetInArchive)));
+  MemoryBufferRef mbref(mb.getBuffer(),
+                        saver().save(archiveName.empty()
+                                         ? path
+                                         : archiveName +
+                                               sys::path::filename(path) +
+                                               utostr(offsetInArchive)));
 
   obj = check(lto::InputFile::create(mbref));
+  if (lazy)
+    parseLazy();
+  else
+    parse();
+}
 
+void BitcodeFile::parse() {
   // Convert LTO Symbols to LLD Symbols in order to perform resolution. The
   // "winning" symbol will then be marked as Prevailing at LTO compilation
   // time.
+  symbols.clear();
   for (const lto::InputFile::Symbol &objSym : obj->symbols())
     symbols.push_back(createBitcodeSymbol(objSym, *this));
 }
 
+void BitcodeFile::parseLazy() {
+  symbols.resize(obj->symbols().size());
+  for (const auto &[i, objSym] : llvm::enumerate(obj->symbols())) {
+    if (!objSym.isUndefined()) {
+      symbols[i] = symtab->addLazyObject(saver().save(objSym.getName()), *this);
+      if (!lazy)
+        break;
+    }
+  }
+}
+
+std::string macho::replaceThinLTOSuffix(StringRef path) {
+  auto [suffix, repl] = config->thinLTOObjectSuffixReplace;
+  if (path.consume_back(suffix))
+    return (path + repl).str();
+  return std::string(path);
+}
+
+void macho::extract(InputFile &file, StringRef reason) {
+  if (!file.lazy)
+    return;
+  file.lazy = false;
+
+  printArchiveMemberLoad(reason, &file);
+  if (auto *bitcode = dyn_cast<BitcodeFile>(&file)) {
+    bitcode->parse();
+  } else {
+    auto &f = cast<ObjFile>(file);
+    if (target->wordSize == 8)
+      f.parse<LP64>();
+    else
+      f.parse<ILP32>();
+  }
+}
+
 template void ObjFile::parse<LP64>();
index 0101fb7..66d46e4 100644 (file)
 #include "MachOStructs.h"
 #include "Target.h"
 
+#include "lld/Common/DWARF.h"
 #include "lld/Common/LLVM.h"
 #include "lld/Common/Memory.h"
+#include "llvm/ADT/CachedHashString.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/SetVector.h"
 #include "llvm/BinaryFormat/MachO.h"
 #include "llvm/DebugInfo/DWARF/DWARFUnit.h"
 #include "llvm/Object/Archive.h"
 #include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Threading.h"
 #include "llvm/TextAPI/TextAPIReader.h"
 
 #include <vector>
@@ -40,6 +43,8 @@ namespace macho {
 struct PlatformInfo;
 class ConcatInputSection;
 class Symbol;
+class Defined;
+class AliasSymbol;
 struct Reloc;
 enum class RefState : uint8_t;
 
@@ -50,11 +55,52 @@ extern std::unique_ptr<llvm::TarWriter> tar;
 // If .subsections_via_symbols is set, each InputSection will be split along
 // symbol boundaries. The field offset represents the offset of the subsection
 // from the start of the original pre-split InputSection.
-struct SubsectionEntry {
-  uint64_t offset;
-  InputSection *isec;
+struct Subsection {
+  uint64_t offset = 0;
+  InputSection *isec = nullptr;
+};
+
+using Subsections = std::vector<Subsection>;
+class InputFile;
+
+class Section {
+public:
+  InputFile *file;
+  StringRef segname;
+  StringRef name;
+  uint32_t flags;
+  uint64_t addr;
+  Subsections subsections;
+
+  Section(InputFile *file, StringRef segname, StringRef name, uint32_t flags,
+          uint64_t addr)
+      : file(file), segname(segname), name(name), flags(flags), addr(addr) {}
+  // Ensure pointers to Sections are never invalidated.
+  Section(const Section &) = delete;
+  Section &operator=(const Section &) = delete;
+  Section(Section &&) = delete;
+  Section &operator=(Section &&) = delete;
+
+private:
+  // Whether we have already split this section into individual subsections.
+  // For sections that cannot be split (e.g. literal sections), this is always
+  // false.
+  bool doneSplitting = false;
+  friend class ObjFile;
+};
+
+// Represents a call graph profile edge.
+struct CallGraphEntry {
+  // The index of the caller in the symbol table.
+  uint32_t fromIndex;
+  // The index of the callee in the symbol table.
+  uint32_t toIndex;
+  // Number of calls from callee to caller in the profile.
+  uint64_t count;
+
+  CallGraphEntry(uint32_t fromIndex, uint32_t toIndex, uint64_t count)
+      : fromIndex(fromIndex), toIndex(toIndex), count(count) {}
 };
-using SubsectionMap = std::vector<SubsectionEntry>;
 
 class InputFile {
 public:
@@ -69,21 +115,28 @@ public:
   virtual ~InputFile() = default;
   Kind kind() const { return fileKind; }
   StringRef getName() const { return name; }
+  static void resetIdCount() { idCount = 0; }
 
   MemoryBufferRef mb;
 
   std::vector<Symbol *> symbols;
-  std::vector<SubsectionMap> subsections;
-  // Provides an easy way to sort InputFiles deterministically.
-  const int id;
+  std::vector<Section *> sections;
+  ArrayRef<uint8_t> objCImageInfo;
 
   // If not empty, this stores the name of the archive containing this file.
   // We use this string for creating error messages.
   std::string archiveName;
 
+  // Provides an easy way to sort InputFiles deterministically.
+  const int id;
+
+  // True if this is a lazy ObjFile or BitcodeFile.
+  bool lazy = false;
+
 protected:
-  InputFile(Kind kind, MemoryBufferRef mb)
-      : mb(mb), id(idCount++), fileKind(kind), name(mb.getBufferIdentifier()) {}
+  InputFile(Kind kind, MemoryBufferRef mb, bool lazy = false)
+      : mb(mb), id(idCount++), lazy(lazy), fileKind(kind),
+        name(mb.getBufferIdentifier()) {}
 
   InputFile(Kind, const llvm::MachO::InterfaceFile &);
 
@@ -94,31 +147,55 @@ private:
   static int idCount;
 };
 
+struct FDE {
+  uint32_t funcLength;
+  Symbol *personality;
+  InputSection *lsda;
+};
+
 // .o file
 class ObjFile final : public InputFile {
 public:
-  ObjFile(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName);
+  ObjFile(MemoryBufferRef mb, uint32_t modTime, StringRef archiveName,
+          bool lazy = false, bool forceHidden = false);
+  ArrayRef<llvm::MachO::data_in_code_entry> getDataInCode() const;
+  ArrayRef<uint8_t> getOptimizationHints() const;
+  template <class LP> void parse();
+
   static bool classof(const InputFile *f) { return f->kind() == ObjKind; }
 
+  std::string sourceFile() const;
+  // Parses line table information for diagnostics. compileUnit should be used
+  // for other purposes.
+  lld::DWARFCache *getDwarf();
+
   llvm::DWARFUnit *compileUnit = nullptr;
+  std::unique_ptr<lld::DWARFCache> dwarfCache;
+  Section *addrSigSection = nullptr;
   const uint32_t modTime;
+  bool forceHidden;
   std::vector<ConcatInputSection *> debugSections;
-  ArrayRef<llvm::MachO::data_in_code_entry> dataInCodeEntries;
+  std::vector<CallGraphEntry> callGraph;
+  llvm::DenseMap<ConcatInputSection *, FDE> fdes;
+  std::vector<AliasSymbol *> aliases;
 
 private:
-  template <class LP> void parse();
-  template <class Section> void parseSections(ArrayRef<Section>);
+  llvm::once_flag initDwarf;
+  template <class LP> void parseLazy();
+  template <class SectionHeader> void parseSections(ArrayRef<SectionHeader>);
   template <class LP>
   void parseSymbols(ArrayRef<typename LP::section> sectionHeaders,
                     ArrayRef<typename LP::nlist> nList, const char *strtab,
                     bool subsectionsViaSymbols);
   template <class NList>
-  Symbol *parseNonSectionSymbol(const NList &sym, StringRef name);
-  template <class Section>
-  void parseRelocations(ArrayRef<Section> sectionHeaders, const Section &,
-                        SubsectionMap &);
+  Symbol *parseNonSectionSymbol(const NList &sym, const char *strtab);
+  template <class SectionHeader>
+  void parseRelocations(ArrayRef<SectionHeader> sectionHeaders,
+                        const SectionHeader &, Section &);
   void parseDebugInfo();
-  void parseDataInCode();
+  void splitEhFrames(ArrayRef<uint8_t> dataArr, Section &ehFrameSection);
+  void registerCompactUnwind(Section &compactUnwindSection);
+  void registerEhFrames(Section &ehFrameSection);
 };
 
 // command-line -sectcreate file
@@ -139,13 +216,17 @@ public:
   // to the root. On the other hand, if a dylib is being directly loaded
   // (through an -lfoo flag), then `umbrella` should be a nullptr.
   explicit DylibFile(MemoryBufferRef mb, DylibFile *umbrella,
-                     bool isBundleLoader = false);
+                     bool isBundleLoader, bool explicitlyLinked);
   explicit DylibFile(const llvm::MachO::InterfaceFile &interface,
-                     DylibFile *umbrella = nullptr,
-                     bool isBundleLoader = false);
+                     DylibFile *umbrella, bool isBundleLoader,
+                     bool explicitlyLinked);
+  explicit DylibFile(DylibFile *umbrella);
 
   void parseLoadCommands(MemoryBufferRef mb);
   void parseReexports(const llvm::MachO::InterfaceFile &interface);
+  bool isReferenced() const { return numReferencedSymbols > 0; }
+  bool isExplicitlyLinked() const;
+  void setExplicitlyLinked() { explicitlyLinked = true; }
 
   static bool classof(const InputFile *f) { return f->kind() == DylibKind; }
 
@@ -156,56 +237,87 @@ public:
   uint32_t compatibilityVersion = 0;
   uint32_t currentVersion = 0;
   int64_t ordinal = 0; // Ordinal numbering starts from 1, so 0 is a sentinel
+  unsigned numReferencedSymbols = 0;
   RefState refState;
   bool reexport = false;
   bool forceNeeded = false;
   bool forceWeakImport = false;
   bool deadStrippable = false;
-  bool explicitlyLinked = false;
-
-  unsigned numReferencedSymbols = 0;
 
-  bool isReferenced() const { return numReferencedSymbols > 0; }
+private:
+  bool explicitlyLinked = false; // Access via isExplicitlyLinked().
 
+public:
   // An executable can be used as a bundle loader that will load the output
   // file being linked, and that contains symbols referenced, but not
   // implemented in the bundle. When used like this, it is very similar
-  // to a Dylib, so we re-used the same class to represent it.
+  // to a dylib, so we've used the same class to represent it.
   bool isBundleLoader;
 
+  // Synthetic Dylib objects created by $ld$previous symbols in this dylib.
+  // Usually empty. These synthetic dylibs won't have synthetic dylibs
+  // themselves.
+  SmallVector<DylibFile *, 2> extraDylibs;
+
 private:
+  DylibFile *getSyntheticDylib(StringRef installName, uint32_t currentVersion,
+                               uint32_t compatVersion);
+
   bool handleLDSymbol(StringRef originalName);
   void handleLDPreviousSymbol(StringRef name, StringRef originalName);
   void handleLDInstallNameSymbol(StringRef name, StringRef originalName);
+  void handleLDHideSymbol(StringRef name, StringRef originalName);
   void checkAppExtensionSafety(bool dylibIsAppExtensionSafe) const;
+  void parseExportedSymbols(uint32_t offset, uint32_t size);
+  void loadReexport(StringRef path, DylibFile *umbrella,
+                    const llvm::MachO::InterfaceFile *currentTopLevelTapi);
+
+  llvm::DenseSet<llvm::CachedHashStringRef> hiddenSymbols;
 };
 
 // .a file
 class ArchiveFile final : public InputFile {
 public:
-  explicit ArchiveFile(std::unique_ptr<llvm::object::Archive> &&file);
+  explicit ArchiveFile(std::unique_ptr<llvm::object::Archive> &&file,
+                       bool forceHidden);
+  void addLazySymbols();
+  void fetch(const llvm::object::Archive::Symbol &);
+  // LLD normally doesn't use Error for error-handling, but the underlying
+  // Archive library does, so this is the cleanest way to wrap it.
+  Error fetch(const llvm::object::Archive::Child &, StringRef reason);
+  const llvm::object::Archive &getArchive() const { return *file; };
   static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; }
-  void fetch(const llvm::object::Archive::Symbol &sym);
 
 private:
   std::unique_ptr<llvm::object::Archive> file;
   // Keep track of children fetched from the archive by tracking
   // which address offsets have been fetched already.
   llvm::DenseSet<uint64_t> seen;
+  // Load all symbols with hidden visibility (-load_hidden).
+  bool forceHidden;
 };
 
 class BitcodeFile final : public InputFile {
 public:
   explicit BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
-                       uint64_t offsetInArchive);
+                       uint64_t offsetInArchive, bool lazy = false,
+                       bool forceHidden = false);
   static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
+  void parse();
 
   std::unique_ptr<llvm::lto::InputFile> obj;
+  bool forceHidden;
+
+private:
+  void parseLazy();
 };
 
 extern llvm::SetVector<InputFile *> inputFiles;
+extern llvm::DenseMap<llvm::CachedHashStringRef, MemoryBufferRef> cachedReads;
+
+std::optional<MemoryBufferRef> readFile(StringRef path);
 
-llvm::Optional<MemoryBufferRef> readFile(StringRef path);
+void extract(InputFile &file, StringRef reason);
 
 namespace detail {
 
@@ -245,9 +357,11 @@ std::vector<const CommandType *> findCommands(const void *anyHdr,
   return detail::findCommands<CommandType>(anyHdr, 0, types...);
 }
 
+std::string replaceThinLTOSuffix(StringRef path);
 } // namespace macho
 
 std::string toString(const macho::InputFile *file);
+std::string toString(const macho::Section &);
 } // namespace lld
 
 #endif
index eb5acf6..1d8d584 100644 (file)
@@ -16,6 +16,8 @@
 #include "Target.h"
 #include "UnwindInfoSection.h"
 #include "Writer.h"
+
+#include "lld/Common/ErrorHandler.h"
 #include "lld/Common/Memory.h"
 #include "llvm/Support/Endian.h"
 #include "llvm/Support/xxhash.h"
@@ -26,6 +28,14 @@ using namespace llvm::support;
 using namespace lld;
 using namespace lld::macho;
 
+// Verify ConcatInputSection's size on 64-bit builds. The size of std::vector
+// can differ based on STL debug levels (e.g. iterator debugging on MSVC's STL),
+// so account for that.
+static_assert(sizeof(void *) != 8 ||
+                  sizeof(ConcatInputSection) == sizeof(std::vector<Reloc>) + 88,
+              "Try to minimize ConcatInputSection's size, we create many "
+              "instances of it");
+
 std::vector<ConcatInputSection *> macho::inputSections;
 
 uint64_t InputSection::getFileSize() const {
@@ -47,55 +57,104 @@ static uint64_t resolveSymbolVA(const Symbol *sym, uint8_t type) {
   return sym->getVA();
 }
 
-// ICF needs to hash any section that might potentially be duplicated so
-// that it can match on content rather than identity.
-bool ConcatInputSection::isHashableForICF() const {
-  switch (sectionType(getFlags())) {
-  case S_REGULAR:
-    return true;
-  case S_CSTRING_LITERALS:
-  case S_4BYTE_LITERALS:
-  case S_8BYTE_LITERALS:
-  case S_16BYTE_LITERALS:
-  case S_LITERAL_POINTERS:
-    llvm_unreachable("found unexpected literal type in ConcatInputSection");
-  case S_ZEROFILL:
-  case S_GB_ZEROFILL:
-  case S_NON_LAZY_SYMBOL_POINTERS:
-  case S_LAZY_SYMBOL_POINTERS:
-  case S_SYMBOL_STUBS:
-  case S_MOD_INIT_FUNC_POINTERS:
-  case S_MOD_TERM_FUNC_POINTERS:
-  case S_COALESCED:
-  case S_INTERPOSING:
-  case S_DTRACE_DOF:
-  case S_LAZY_DYLIB_SYMBOL_POINTERS:
-  case S_THREAD_LOCAL_REGULAR:
-  case S_THREAD_LOCAL_ZEROFILL:
-  case S_THREAD_LOCAL_VARIABLES:
-  case S_THREAD_LOCAL_VARIABLE_POINTERS:
-  case S_THREAD_LOCAL_INIT_FUNCTION_POINTERS:
-    return false;
-  default:
-    llvm_unreachable("Section type");
+const Defined *InputSection::getContainingSymbol(uint64_t off) const {
+  auto *nextSym = llvm::upper_bound(
+      symbols, off, [](uint64_t a, const Defined *b) { return a < b->value; });
+  if (nextSym == symbols.begin())
+    return nullptr;
+  return *std::prev(nextSym);
+}
+
+std::string InputSection::getLocation(uint64_t off) const {
+  // First, try to find a symbol that's near the offset. Use it as a reference
+  // point.
+  if (auto *sym = getContainingSymbol(off))
+    return (toString(getFile()) + ":(symbol " + toString(*sym) + "+0x" +
+            Twine::utohexstr(off - sym->value) + ")")
+        .str();
+
+  // If that fails, use the section itself as a reference point.
+  for (const Subsection &subsec : section.subsections) {
+    if (subsec.isec == this) {
+      off += subsec.offset;
+      break;
+    }
   }
+
+  return (toString(getFile()) + ":(" + getName() + "+0x" +
+          Twine::utohexstr(off) + ")")
+      .str();
 }
 
-void ConcatInputSection::hashForICF() {
-  assert(data.data()); // zeroFill section data has nullptr with non-zero size
-  assert(icfEqClass[0] == 0); // don't overwrite a unique ID!
-  // Turn-on the top bit to guarantee that valid hashes have no collisions
-  // with the small-integer unique IDs for ICF-ineligible sections
-  icfEqClass[0] = xxHash64(data) | (1ull << 63);
+std::string InputSection::getSourceLocation(uint64_t off) const {
+  auto *obj = dyn_cast_or_null<ObjFile>(getFile());
+  if (!obj)
+    return {};
+
+  DWARFCache *dwarf = obj->getDwarf();
+  if (!dwarf)
+    return std::string();
+
+  for (const Subsection &subsec : section.subsections) {
+    if (subsec.isec == this) {
+      off += subsec.offset;
+      break;
+    }
+  }
+
+  auto createMsg = [&](StringRef path, unsigned line) {
+    std::string filename = sys::path::filename(path).str();
+    std::string lineStr = (":" + Twine(line)).str();
+    if (filename == path)
+      return filename + lineStr;
+    return (filename + lineStr + " (" + path + lineStr + ")").str();
+  };
+
+  // First, look up a function for a given offset.
+  if (std::optional<DILineInfo> li = dwarf->getDILineInfo(
+          section.addr + off, object::SectionedAddress::UndefSection))
+    return createMsg(li->FileName, li->Line);
+
+  // If it failed, look up again as a variable.
+  if (const Defined *sym = getContainingSymbol(off)) {
+    // Symbols are generally prefixed with an underscore, which is not included
+    // in the debug information.
+    StringRef symName = sym->getName();
+    if (!symName.empty() && symName[0] == '_')
+      symName = symName.substr(1);
+
+    if (std::optional<std::pair<std::string, unsigned>> fileLine =
+            dwarf->getVariableLoc(symName))
+      return createMsg(fileLine->first, fileLine->second);
+  }
+
+  // Try to get the source file's name from the DWARF information.
+  if (obj->compileUnit)
+    return obj->sourceFile();
+
+  return {};
 }
 
 void ConcatInputSection::foldIdentical(ConcatInputSection *copy) {
   align = std::max(align, copy->align);
   copy->live = false;
   copy->wasCoalesced = true;
-  numRefs += copy->numRefs;
-  copy->numRefs = 0;
   copy->replacement = this;
+  for (auto &copySym : copy->symbols) {
+    copySym->wasIdenticalCodeFolded = true;
+    copySym->size = 0;
+  }
+
+  symbols.insert(symbols.end(), copy->symbols.begin(), copy->symbols.end());
+  copy->symbols.clear();
+
+  // Remove duplicate compact unwind info for symbols at the same address.
+  if (symbols.empty())
+    return;
+  for (auto it = symbols.begin() + 1; it != symbols.end(); ++it) {
+    assert((*it)->value == 0);
+    (*it)->unwindEntry = nullptr;
+  }
 }
 
 void ConcatInputSection::writeTo(uint8_t *buf) {
@@ -110,6 +169,9 @@ void ConcatInputSection::writeTo(uint8_t *buf) {
     const Reloc &r = relocs[i];
     uint8_t *loc = buf + r.offset;
     uint64_t referentVA = 0;
+
+    const bool needsFixup = config->emitChainedFixups &&
+                            target->hasAttr(r.type, RelocAttrBits::UNSIGNED);
     if (target->hasAttr(r.type, RelocAttrBits::SUBTRAHEND)) {
       const Symbol *fromSym = r.referent.get<Symbol *>();
       const Reloc &minuend = relocs[++i];
@@ -126,34 +188,59 @@ void ConcatInputSection::writeTo(uint8_t *buf) {
       if (target->hasAttr(r.type, RelocAttrBits::LOAD) &&
           !referentSym->isInGot())
         target->relaxGotLoad(loc, r.type);
+      // For dtrace symbols, do not handle them as normal undefined symbols
+      if (referentSym->getName().startswith("___dtrace_")) {
+        // Change dtrace call site to pre-defined instructions
+        target->handleDtraceReloc(referentSym, r, loc);
+        continue;
+      }
       referentVA = resolveSymbolVA(referentSym, r.type) + r.addend;
 
-      if (isThreadLocalVariables(getFlags())) {
+      if (isThreadLocalVariables(getFlags()) && isa<Defined>(referentSym)) {
         // References from thread-local variable sections are treated as offsets
         // relative to the start of the thread-local data memory area, which
         // is initialized via copying all the TLV data sections (which are all
         // contiguous).
-        if (isa<Defined>(referentSym))
-          referentVA -= firstTLVDataSection->addr;
+        referentVA -= firstTLVDataSection->addr;
+      } else if (needsFixup) {
+        writeChainedFixup(loc, referentSym, r.addend);
+        continue;
       }
     } else if (auto *referentIsec = r.referent.dyn_cast<InputSection *>()) {
       assert(!::shouldOmitFromOutput(referentIsec));
       referentVA = referentIsec->getVA(r.addend);
+
+      if (needsFixup) {
+        writeChainedRebase(loc, referentVA);
+        continue;
+      }
     }
     target->relocateOne(loc, r, referentVA, getVA() + r.offset);
   }
 }
 
+ConcatInputSection *macho::makeSyntheticInputSection(StringRef segName,
+                                                     StringRef sectName,
+                                                     uint32_t flags,
+                                                     ArrayRef<uint8_t> data,
+                                                     uint32_t align) {
+  Section &section =
+      *make<Section>(/*file=*/nullptr, segName, sectName, flags, /*addr=*/0);
+  auto isec = make<ConcatInputSection>(section, data, align);
+  section.subsections.push_back({0, isec});
+  return isec;
+}
+
 void CStringInputSection::splitIntoPieces() {
   size_t off = 0;
   StringRef s = toStringRef(data);
   while (!s.empty()) {
     size_t end = s.find(0);
     if (end == StringRef::npos)
-      fatal(toString(this) + ": string is not null terminated");
-    size_t size = end + 1;
-    uint32_t hash = config->dedupLiterals ? xxHash64(s.substr(0, size)) : 0;
+      fatal(getLocation(off) + ": string is not null terminated");
+    uint32_t hash = deduplicateLiterals ? xxHash64(s.take_front(end)) : 0;
     pieces.emplace_back(off, hash);
+    size_t size = end + 1; // include null terminator
     s = s.substr(size);
     off += size;
   }
@@ -178,13 +265,11 @@ uint64_t CStringInputSection::getOffset(uint64_t off) const {
   return piece.outSecOff + addend;
 }
 
-WordLiteralInputSection::WordLiteralInputSection(StringRef segname,
-                                                 StringRef name,
-                                                 InputFile *file,
+WordLiteralInputSection::WordLiteralInputSection(const Section &section,
                                                  ArrayRef<uint8_t> data,
-                                                 uint32_t align, uint32_t flags)
-    : InputSection(WordLiteralKind, segname, name, file, data, align, flags) {
-  switch (sectionType(flags)) {
+                                                 uint32_t align)
+    : InputSection(WordLiteralKind, section, data, align) {
+  switch (sectionType(getFlags())) {
   case S_4BYTE_LITERALS:
     power2LiteralSize = 2;
     break;
@@ -203,14 +288,14 @@ WordLiteralInputSection::WordLiteralInputSection(StringRef segname,
 
 uint64_t WordLiteralInputSection::getOffset(uint64_t off) const {
   auto *osec = cast<WordLiteralSection>(parent);
-  const uint8_t *buf = data.data();
+  const uintptr_t buf = reinterpret_cast<uintptr_t>(data.data());
   switch (sectionType(getFlags())) {
   case S_4BYTE_LITERALS:
-    return osec->getLiteral4Offset(buf + off);
+    return osec->getLiteral4Offset(buf + (off & ~3LLU)) | (off & 3);
   case S_8BYTE_LITERALS:
-    return osec->getLiteral8Offset(buf + off);
+    return osec->getLiteral8Offset(buf + (off & ~7LLU)) | (off & 7);
   case S_16BYTE_LITERALS:
-    return osec->getLiteral16Offset(buf + off);
+    return osec->getLiteral16Offset(buf + (off & ~15LLU)) | (off & 15);
   default:
     llvm_unreachable("invalid literal section type");
   }
@@ -238,6 +323,26 @@ bool macho::isCfStringSection(const InputSection *isec) {
          isec->getSegName() == segment_names::data;
 }
 
+bool macho::isClassRefsSection(const InputSection *isec) {
+  return isec->getName() == section_names::objcClassRefs &&
+         isec->getSegName() == segment_names::data;
+}
+
+bool macho::isSelRefsSection(const InputSection *isec) {
+  return isec->getName() == section_names::objcSelrefs &&
+         isec->getSegName() == segment_names::data;
+}
+
+bool macho::isEhFrameSection(const InputSection *isec) {
+  return isec->getName() == section_names::ehFrame &&
+         isec->getSegName() == segment_names::text;
+}
+
+bool macho::isGccExceptTabSection(const InputSection *isec) {
+  return isec->getName() == section_names::gccExceptTab &&
+         isec->getSegName() == segment_names::text;
+}
+
 std::string lld::toString(const InputSection *isec) {
   return (toString(isec->getFile()) + ":(" + isec->getName() + ")").str();
 }
index a104570..5a6a205 100644 (file)
 
 #include "Config.h"
 #include "Relocations.h"
+#include "Symbols.h"
 
 #include "lld/Common/LLVM.h"
 #include "lld/Common/Memory.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/BitVector.h"
 #include "llvm/ADT/CachedHashString.h"
+#include "llvm/ADT/TinyPtrVector.h"
 #include "llvm/BinaryFormat/MachO.h"
 
 namespace lld {
@@ -24,71 +26,75 @@ namespace macho {
 
 class InputFile;
 class OutputSection;
-class Defined;
 
 class InputSection {
 public:
-  enum Kind {
+  enum Kind : uint8_t {
     ConcatKind,
     CStringLiteralKind,
     WordLiteralKind,
   };
 
-  Kind kind() const { return shared->sectionKind; }
+  Kind kind() const { return sectionKind; }
   virtual ~InputSection() = default;
   virtual uint64_t getSize() const { return data.size(); }
-  InputFile *getFile() const { return shared->file; }
-  StringRef getName() const { return shared->name; }
-  StringRef getSegName() const { return shared->segname; }
-  uint32_t getFlags() const { return shared->flags; }
+  virtual bool empty() const { return data.empty(); }
+  InputFile *getFile() const { return section.file; }
+  StringRef getName() const { return section.name; }
+  StringRef getSegName() const { return section.segname; }
+  uint32_t getFlags() const { return section.flags; }
   uint64_t getFileSize() const;
   // Translates \p off -- an offset relative to this InputSection -- into an
   // offset from the beginning of its parent OutputSection.
   virtual uint64_t getOffset(uint64_t off) const = 0;
   // The offset from the beginning of the file.
   uint64_t getVA(uint64_t off) const;
+  // Return a user-friendly string for use in diagnostics.
+  // Format: /path/to/object.o:(symbol _func+0x123)
+  std::string getLocation(uint64_t off) const;
+  // Return the source line corresponding to an address, or the empty string.
+  // Format: Source.cpp:123 (/path/to/Source.cpp:123)
+  std::string getSourceLocation(uint64_t off) const;
   // Whether the data at \p off in this InputSection is live.
   virtual bool isLive(uint64_t off) const = 0;
   virtual void markLive(uint64_t off) = 0;
   virtual InputSection *canonical() { return this; }
+  virtual const InputSection *canonical() const { return this; }
 
-  OutputSection *parent = nullptr;
+protected:
+  InputSection(Kind kind, const Section &section, ArrayRef<uint8_t> data,
+               uint32_t align)
+      : sectionKind(kind), keepUnique(false), hasAltEntry(false), align(align),
+        data(data), section(section) {}
 
-  uint32_t align = 1;
-  uint32_t callSiteCount : 31;
+  InputSection(const InputSection &rhs)
+      : sectionKind(rhs.sectionKind), keepUnique(false), hasAltEntry(false),
+        align(rhs.align), data(rhs.data), section(rhs.section) {}
+
+  Kind sectionKind;
+
+public:
   // is address assigned?
-  uint32_t isFinal : 1;
+  bool isFinal = false;
+  // keep the address of the symbol(s) in this section unique in the final
+  // binary ?
+  bool keepUnique : 1;
+  // Does this section have symbols at offsets other than zero? (NOTE: only
+  // applies to ConcatInputSections.)
+  bool hasAltEntry : 1;
+  uint32_t align = 1;
 
+  OutputSection *parent = nullptr;
   ArrayRef<uint8_t> data;
   std::vector<Reloc> relocs;
+  // The symbols that belong to this InputSection, sorted by value. With
+  // .subsections_via_symbols, there is typically only one element here.
+  llvm::TinyPtrVector<Defined *> symbols;
 
 protected:
-  // The fields in this struct are immutable. Since we create a lot of
-  // InputSections with identical values for them (due to
-  // .subsections_via_symbols), factoring them out into a shared struct reduces
-  // memory consumption and makes copying cheaper.
-  struct Shared {
-    InputFile *file;
-    StringRef name;
-    StringRef segname;
-    uint32_t flags;
-    Kind sectionKind;
-    Shared(InputFile *file, StringRef name, StringRef segname, uint32_t flags,
-           Kind kind)
-        : file(file), name(name), segname(segname), flags(flags),
-          sectionKind(kind) {}
-  };
+  const Section &section;
 
-  InputSection(Kind kind, StringRef segname, StringRef name)
-      : callSiteCount(0), isFinal(false),
-        shared(make<Shared>(nullptr, name, segname, 0, kind)) {}
-
-  InputSection(Kind kind, StringRef segname, StringRef name, InputFile *file,
-               ArrayRef<uint8_t> data, uint32_t align, uint32_t flags)
-      : align(align), callSiteCount(0), isFinal(false), data(data),
-        shared(make<Shared>(file, name, segname, flags, kind)) {}
-
-  const Shared *const shared;
+  const Defined *getContainingSymbol(uint64_t off) const;
 };
 
 // ConcatInputSections are combined into (Concat)OutputSections through simple
@@ -96,27 +102,24 @@ protected:
 // contents merged before output.
 class ConcatInputSection final : public InputSection {
 public:
-  ConcatInputSection(StringRef segname, StringRef name)
-      : InputSection(ConcatKind, segname, name) {}
-
-  ConcatInputSection(StringRef segname, StringRef name, InputFile *file,
-                     ArrayRef<uint8_t> data, uint32_t align = 1,
-                     uint32_t flags = 0)
-      : InputSection(ConcatKind, segname, name, file, data, align, flags) {}
+  ConcatInputSection(const Section &section, ArrayRef<uint8_t> data,
+                     uint32_t align = 1)
+      : InputSection(ConcatKind, section, data, align) {}
 
   uint64_t getOffset(uint64_t off) const override { return outSecOff + off; }
   uint64_t getVA() const { return InputSection::getVA(0); }
   // ConcatInputSections are entirely live or dead, so the offset is irrelevant.
   bool isLive(uint64_t off) const override { return live; }
   void markLive(uint64_t off) override { live = true; }
-  bool isCoalescedWeak() const { return wasCoalesced && numRefs == 0; }
+  bool isCoalescedWeak() const { return wasCoalesced && symbols.empty(); }
   bool shouldOmitFromOutput() const { return !live || isCoalescedWeak(); }
-  bool isHashableForICF() const;
-  void hashForICF();
   void writeTo(uint8_t *buf);
 
   void foldIdentical(ConcatInputSection *redundant);
-  InputSection *canonical() override {
+  ConcatInputSection *canonical() override {
+    return replacement ? replacement : this;
+  }
+  const InputSection *canonical() const override {
     return replacement ? replacement : this;
   }
 
@@ -125,9 +128,9 @@ public:
   }
 
   // Points to the surviving section after this one is folded by ICF
-  InputSection *replacement = nullptr;
+  ConcatInputSection *replacement = nullptr;
   // Equivalence-class ID for ICF
-  uint64_t icfEqClass[2] = {0, 0};
+  uint32_t icfEqClass[2] = {0, 0};
 
   // With subsections_via_symbols, most symbols have their own InputSection,
   // and for weak symbols (e.g. from inline functions), only the
@@ -136,18 +139,19 @@ public:
   // first and not copied to the output.
   bool wasCoalesced = false;
   bool live = !config->deadStrip;
-  // How many symbols refer to this InputSection.
-  uint32_t numRefs = 0;
+  bool hasCallSites = false;
   // This variable has two usages. Initially, it represents the input order.
   // After assignAddresses is called, it represents the offset from the
   // beginning of the output section this section was assigned to.
   uint64_t outSecOff = 0;
 };
 
-// Verify ConcatInputSection's size on 64-bit builds.
-static_assert(sizeof(int) != 8 || sizeof(ConcatInputSection) == 112,
-              "Try to minimize ConcatInputSection's size, we create many "
-              "instances of it");
+// Initialize a fake InputSection that does not belong to any InputFile.
+ConcatInputSection *makeSyntheticInputSection(StringRef segName,
+                                              StringRef sectName,
+                                              uint32_t flags = 0,
+                                              ArrayRef<uint8_t> data = {},
+                                              uint32_t align = 1);
 
 // Helper functions to make it easy to sprinkle asserts.
 
@@ -190,10 +194,11 @@ static_assert(sizeof(StringPiece) == 16, "StringPiece is too big!");
 // conservative behavior we can certainly implement that.
 class CStringInputSection final : public InputSection {
 public:
-  CStringInputSection(StringRef segname, StringRef name, InputFile *file,
-                      ArrayRef<uint8_t> data, uint32_t align, uint32_t flags)
-      : InputSection(CStringLiteralKind, segname, name, file, data, align,
-                     flags) {}
+  CStringInputSection(const Section &section, ArrayRef<uint8_t> data,
+                      uint32_t align, bool dedupLiterals)
+      : InputSection(CStringLiteralKind, section, data, align),
+        deduplicateLiterals(dedupLiterals) {}
+
   uint64_t getOffset(uint64_t off) const override;
   bool isLive(uint64_t off) const override { return getStringPiece(off).live; }
   void markLive(uint64_t off) override { getStringPiece(off).live = true; }
@@ -206,8 +211,10 @@ public:
   LLVM_ATTRIBUTE_ALWAYS_INLINE
   StringRef getStringRef(size_t i) const {
     size_t begin = pieces[i].inSecOff;
+    // The endpoint should be *at* the null terminator, not after. This matches
+    // the behavior of StringRef(const char *Str).
     size_t end =
-        (pieces.size() - 1 == i) ? data.size() : pieces[i + 1].inSecOff;
+        ((pieces.size() - 1 == i) ? data.size() : pieces[i + 1].inSecOff) - 1;
     return toStringRef(data.slice(begin, end - begin));
   }
 
@@ -215,7 +222,7 @@ public:
   // string merging is enabled, so we want to inline.
   LLVM_ATTRIBUTE_ALWAYS_INLINE
   llvm::CachedHashStringRef getCachedHashStringRef(size_t i) const {
-    assert(config->dedupLiterals);
+    assert(deduplicateLiterals);
     return {getStringRef(i), pieces[i].hash};
   }
 
@@ -223,19 +230,21 @@ public:
     return isec->kind() == CStringLiteralKind;
   }
 
+  bool deduplicateLiterals = false;
   std::vector<StringPiece> pieces;
 };
 
 class WordLiteralInputSection final : public InputSection {
 public:
-  WordLiteralInputSection(StringRef segname, StringRef name, InputFile *file,
-                          ArrayRef<uint8_t> data, uint32_t align,
-                          uint32_t flags);
+  WordLiteralInputSection(const Section &section, ArrayRef<uint8_t> data,
+                          uint32_t align);
   uint64_t getOffset(uint64_t off) const override;
   bool isLive(uint64_t off) const override {
     return live[off >> power2LiteralSize];
   }
-  void markLive(uint64_t off) override { live[off >> power2LiteralSize] = 1; }
+  void markLive(uint64_t off) override {
+    live[off >> power2LiteralSize] = true;
+  }
 
   static bool classof(const InputSection *isec) {
     return isec->kind() == WordLiteralKind;
@@ -277,8 +286,11 @@ inline bool isWordLiteralSection(uint32_t flags) {
 }
 
 bool isCodeSection(const InputSection *);
-
 bool isCfStringSection(const InputSection *);
+bool isClassRefsSection(const InputSection *);
+bool isSelRefsSection(const InputSection *);
+bool isEhFrameSection(const InputSection *);
+bool isGccExceptTabSection(const InputSection *);
 
 extern std::vector<ConcatInputSection *> inputSections;
 
@@ -290,20 +302,26 @@ constexpr const char binding[] = "__binding";
 constexpr const char bitcodeBundle[] = "__bundle";
 constexpr const char cString[] = "__cstring";
 constexpr const char cfString[] = "__cfstring";
+constexpr const char cgProfile[] = "__cg_profile";
+constexpr const char chainFixups[] = "__chainfixups";
 constexpr const char codeSignature[] = "__code_signature";
 constexpr const char common[] = "__common";
 constexpr const char compactUnwind[] = "__compact_unwind";
 constexpr const char data[] = "__data";
 constexpr const char debugAbbrev[] = "__debug_abbrev";
 constexpr const char debugInfo[] = "__debug_info";
+constexpr const char debugLine[] = "__debug_line";
 constexpr const char debugStr[] = "__debug_str";
+constexpr const char debugStrOffs[] = "__debug_str_offs";
 constexpr const char ehFrame[] = "__eh_frame";
+constexpr const char gccExceptTab[] = "__gcc_except_tab";
 constexpr const char export_[] = "__export";
 constexpr const char dataInCode[] = "__data_in_code";
 constexpr const char functionStarts[] = "__func_starts";
 constexpr const char got[] = "__got";
 constexpr const char header[] = "__mach_header";
 constexpr const char indirectSymbolTable[] = "__ind_sym_tab";
+constexpr const char initOffsets[] = "__init_offsets";
 constexpr const char const_[] = "__const";
 constexpr const char lazySymbolPtr[] = "__la_symbol_ptr";
 constexpr const char lazyBinding[] = "__lazy_binding";
@@ -313,8 +331,12 @@ constexpr const char moduleTermFunc[] = "__mod_term_func";
 constexpr const char nonLazySymbolPtr[] = "__nl_symbol_ptr";
 constexpr const char objcCatList[] = "__objc_catlist";
 constexpr const char objcClassList[] = "__objc_classlist";
+constexpr const char objcClassRefs[] = "__objc_classrefs";
 constexpr const char objcConst[] = "__objc_const";
-constexpr const char objcImageInfo[] = "__objc_imageinfo";
+constexpr const char objCImageInfo[] = "__objc_imageinfo";
+constexpr const char objcStubs[] = "__objc_stubs";
+constexpr const char objcSelrefs[] = "__objc_selrefs";
+constexpr const char objcMethname[] = "__objc_methname";
 constexpr const char objcNonLazyCatList[] = "__objc_nlcatlist";
 constexpr const char objcNonLazyClassList[] = "__objc_nlclslist";
 constexpr const char objcProtoList[] = "__objc_protolist";
@@ -334,6 +356,7 @@ constexpr const char threadVars[] = "__thread_vars";
 constexpr const char unwindInfo[] = "__unwind_info";
 constexpr const char weakBinding[] = "__weak_binding";
 constexpr const char zeroFill[] = "__zerofill";
+constexpr const char addrSig[] = "__llvm_addrsig";
 
 } // namespace section_names
 
index 366193a..2f5e9d0 100644 (file)
 #include "Target.h"
 
 #include "lld/Common/Args.h"
-#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/CommonLinkerContext.h"
 #include "lld/Common/Strings.h"
 #include "lld/Common/TargetOptionsCommandFlags.h"
-#include "llvm/LTO/Caching.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
 #include "llvm/LTO/Config.h"
 #include "llvm/LTO/LTO.h"
+#include "llvm/Support/Caching.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/raw_ostream.h"
@@ -31,16 +32,41 @@ using namespace llvm;
 using namespace llvm::MachO;
 using namespace llvm::sys;
 
+// Creates an empty file to store a list of object files for final
+// linking of distributed ThinLTO.
+static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
+  std::error_code ec;
+  auto ret =
+      std::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::OF_None);
+  if (ec) {
+    error("cannot open " + file + ": " + ec.message());
+    return nullptr;
+  }
+  return ret;
+}
+
+static std::string getThinLTOOutputFile(StringRef modulePath) {
+  return lto::getThinLTOOutputFile(
+      std::string(modulePath), std::string(config->thinLTOPrefixReplace.first),
+      std::string(config->thinLTOPrefixReplace.second));
+}
+
 static lto::Config createConfig() {
   lto::Config c;
   c.Options = initTargetOptionsFromCodeGenFlags();
+  c.Options.EmitAddrsig = config->icfLevel == ICFLevel::safe;
+  for (StringRef C : config->mllvmOpts)
+    c.MllvmArgs.emplace_back(C.str());
   c.CodeModel = getCodeModelFromCMModel();
   c.CPU = getCPUStr();
   c.MAttrs = getMAttrs();
-  c.UseNewPM = config->ltoNewPassManager;
+  c.DiagHandler = diagnosticHandler;
   c.PreCodeGenPassesHook = [](legacy::PassManager &pm) {
     pm.add(createObjCARCContractPass());
   };
+
+  c.AlwaysEmitRegularLTOObj = !config->ltoObjPath.empty();
+
   c.TimeTraceEnabled = config->timeTraceEnabled;
   c.TimeTraceGranularity = config->timeTraceGranularity;
   c.OptLevel = config->ltoo;
@@ -51,18 +77,54 @@ static lto::Config createConfig() {
   return c;
 }
 
+// If `originalPath` exists, hardlinks `path` to `originalPath`. If that fails,
+// or `originalPath` is not set, saves `buffer` to `path`.
+static void saveOrHardlinkBuffer(StringRef buffer, const Twine &path,
+                                 std::optional<StringRef> originalPath) {
+  if (originalPath) {
+    auto err = fs::create_hard_link(*originalPath, path);
+    if (!err)
+      return;
+  }
+  saveBuffer(buffer, path);
+}
+
 BitcodeCompiler::BitcodeCompiler() {
-  lto::ThinBackend backend = lto::createInProcessThinBackend(
-      heavyweight_hardware_concurrency(config->thinLTOJobs));
+  // Initialize indexFile.
+  if (!config->thinLTOIndexOnlyArg.empty())
+    indexFile = openFile(config->thinLTOIndexOnlyArg);
+
+  // Initialize ltoObj.
+  lto::ThinBackend backend;
+  auto onIndexWrite = [&](StringRef S) { thinIndices.erase(S); };
+  if (config->thinLTOIndexOnly) {
+    backend = lto::createWriteIndexesThinBackend(
+        std::string(config->thinLTOPrefixReplace.first),
+        std::string(config->thinLTOPrefixReplace.second),
+        config->thinLTOEmitImportsFiles, indexFile.get(), onIndexWrite);
+  } else {
+    backend = lto::createInProcessThinBackend(
+        llvm::heavyweight_hardware_concurrency(config->thinLTOJobs),
+        onIndexWrite, config->thinLTOEmitIndexFiles,
+        config->thinLTOEmitImportsFiles);
+  }
+
   ltoObj = std::make_unique<lto::LTO>(createConfig(), backend);
 }
 
 void BitcodeCompiler::add(BitcodeFile &f) {
-  ArrayRef<lto::InputFile::Symbol> objSyms = f.obj->symbols();
+  lto::InputFile &obj = *f.obj;
+
+  if (config->thinLTOEmitIndexFiles)
+    thinIndices.insert(obj.getName());
+
+  ArrayRef<lto::InputFile::Symbol> objSyms = obj.symbols();
   std::vector<lto::SymbolResolution> resols;
   resols.reserve(objSyms.size());
 
   // Provide a resolution to the LTO API for each symbol.
+  bool exportDynamic =
+      config->outputType != MH_EXECUTE || config->exportDynamic;
   auto symIt = f.symbols.begin();
   for (const lto::InputFile::Symbol &objSym : objSyms) {
     resols.emplace_back();
@@ -76,24 +138,61 @@ void BitcodeCompiler::add(BitcodeFile &f) {
     // be removed.
     r.Prevailing = !objSym.isUndefined() && sym->getFile() == &f;
 
-    // FIXME: What about other output types? And we can probably be less
-    // restrictive with -flat_namespace, but it's an infrequent use case.
-    // FIXME: Honor config->exportDynamic.
-    r.VisibleToRegularObj = config->outputType != MH_EXECUTE ||
-                            config->namespaceKind == NamespaceKind::flat ||
-                            sym->isUsedInRegularObj;
+    if (const auto *defined = dyn_cast<Defined>(sym)) {
+      r.ExportDynamic =
+          defined->isExternal() && !defined->privateExtern && exportDynamic;
+      r.FinalDefinitionInLinkageUnit =
+          !defined->isExternalWeakDef() && !defined->interposable;
+    } else if (const auto *common = dyn_cast<CommonSymbol>(sym)) {
+      r.ExportDynamic = !common->privateExtern && exportDynamic;
+      r.FinalDefinitionInLinkageUnit = true;
+    }
+
+    r.VisibleToRegularObj =
+        sym->isUsedInRegularObj || (r.Prevailing && r.ExportDynamic);
 
     // Un-define the symbol so that we don't get duplicate symbol errors when we
     // load the ObjFile emitted by LTO compilation.
     if (r.Prevailing)
       replaceSymbol<Undefined>(sym, sym->getName(), sym->getFile(),
-                               RefState::Strong);
+                               RefState::Strong, /*wasBitcodeSymbol=*/true);
 
     // TODO: set the other resolution configs properly
   }
   checkError(ltoObj->add(std::move(f.obj), resols));
 }
 
+// If LazyObjFile has not been added to link, emit empty index files.
+// This is needed because this is what GNU gold plugin does and we have a
+// distributed build system that depends on that behavior.
+static void thinLTOCreateEmptyIndexFiles() {
+  DenseSet<StringRef> linkedBitCodeFiles;
+  for (InputFile *file : inputFiles)
+    if (auto *f = dyn_cast<BitcodeFile>(file))
+      if (!f->lazy)
+        linkedBitCodeFiles.insert(f->getName());
+
+  for (InputFile *file : inputFiles) {
+    if (auto *f = dyn_cast<BitcodeFile>(file)) {
+      if (!f->lazy)
+        continue;
+      if (linkedBitCodeFiles.contains(f->getName()))
+        continue;
+      std::string path =
+          replaceThinLTOSuffix(getThinLTOOutputFile(f->obj->getName()));
+      std::unique_ptr<raw_fd_ostream> os = openFile(path + ".thinlto.bc");
+      if (!os)
+        continue;
+
+      ModuleSummaryIndex m(/*HaveGVs=*/false);
+      m.setSkipModuleByDistributedBackend();
+      writeIndexToFile(m, *os);
+      if (config->thinLTOEmitImportsFiles)
+        openFile(path + ".imports");
+    }
+  }
+}
+
 // Merge all the bitcode files we have seen, codegen the result
 // and return the resulting ObjectFile(s).
 std::vector<ObjFile *> BitcodeCompiler::compile() {
@@ -104,53 +203,105 @@ std::vector<ObjFile *> BitcodeCompiler::compile() {
   // The -cache_path_lto option specifies the path to a directory in which
   // to cache native object files for ThinLTO incremental builds. If a path was
   // specified, configure LTO to use it as the cache directory.
-  lto::NativeObjectCache cache;
+  FileCache cache;
   if (!config->thinLTOCacheDir.empty())
-    cache = check(
-        lto::localCache(config->thinLTOCacheDir,
-                        [&](size_t task, std::unique_ptr<MemoryBuffer> mb) {
-                          files[task] = std::move(mb);
-                        }));
+    cache = check(localCache("ThinLTO", "Thin", config->thinLTOCacheDir,
+                             [&](size_t task, const Twine &moduleName,
+                                 std::unique_ptr<MemoryBuffer> mb) {
+                               files[task] = std::move(mb);
+                             }));
 
   checkError(ltoObj->run(
-      [&](size_t task) {
-        return std::make_unique<lto::NativeObjectStream>(
+      [&](size_t task, const Twine &moduleName) {
+        return std::make_unique<CachedFileStream>(
             std::make_unique<raw_svector_ostream>(buf[task]));
       },
       cache));
 
-  if (!config->thinLTOCacheDir.empty())
-    pruneCache(config->thinLTOCacheDir, config->thinLTOCachePolicy);
+  // Emit empty index files for non-indexed files
+  for (StringRef s : thinIndices) {
+    std::string path = getThinLTOOutputFile(s);
+    openFile(path + ".thinlto.bc");
+    if (config->thinLTOEmitImportsFiles)
+      openFile(path + ".imports");
+  }
+
+  if (config->thinLTOEmitIndexFiles)
+    thinLTOCreateEmptyIndexFiles();
+
+  // In ThinLTO mode, Clang passes a temporary directory in -object_path_lto,
+  // while the argument is a single file in FullLTO mode.
+  bool objPathIsDir = true;
+  if (!config->ltoObjPath.empty()) {
+    if (std::error_code ec = fs::create_directories(config->ltoObjPath))
+      fatal("cannot create LTO object path " + config->ltoObjPath + ": " +
+            ec.message());
+
+    if (!fs::is_directory(config->ltoObjPath)) {
+      objPathIsDir = false;
+      unsigned objCount =
+          count_if(buf, [](const SmallString<0> &b) { return !b.empty(); });
+      if (objCount > 1)
+        fatal("-object_path_lto must specify a directory when using ThinLTO");
+    }
+  }
+
+  auto outputFilePath = [objPathIsDir](int i) {
+    SmallString<261> filePath("/tmp/lto.tmp");
+    if (!config->ltoObjPath.empty()) {
+      filePath = config->ltoObjPath;
+      if (objPathIsDir)
+        path::append(filePath, Twine(i) + "." +
+                                   getArchitectureName(config->arch()) +
+                                   ".lto.o");
+    }
+    return filePath;
+  };
 
-  if (config->saveTemps) {
-    if (!buf[0].empty())
-      saveBuffer(buf[0], config->outputFile + ".lto.o");
-    for (unsigned i = 1; i != maxTasks; ++i)
-      saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.o");
+  // ThinLTO with index only option is required to generate only the index
+  // files. After that, we exit from linker and ThinLTO backend runs in a
+  // distributed environment.
+  if (config->thinLTOIndexOnly) {
+    if (!config->ltoObjPath.empty())
+      saveBuffer(buf[0], outputFilePath(0));
+    if (indexFile)
+      indexFile->close();
+    return {};
   }
 
-  if (!config->ltoObjPath.empty())
-    fs::create_directories(config->ltoObjPath);
+  if (!config->thinLTOCacheDir.empty())
+    pruneCache(config->thinLTOCacheDir, config->thinLTOCachePolicy, files);
 
   std::vector<ObjFile *> ret;
-  for (unsigned i = 0; i != maxTasks; ++i) {
-    if (buf[i].empty())
+  for (unsigned i = 0; i < maxTasks; ++i) {
+    // Get the native object contents either from the cache or from memory.  Do
+    // not use the cached MemoryBuffer directly to ensure dsymutil does not
+    // race with the cache pruner.
+    StringRef objBuf;
+    std::optional<StringRef> cachePath;
+    if (files[i]) {
+      objBuf = files[i]->getBuffer();
+      cachePath = files[i]->getBufferIdentifier();
+    } else {
+      objBuf = buf[i];
+    }
+    if (objBuf.empty())
       continue;
-    SmallString<261> filePath("/tmp/lto.tmp");
+
+    // FIXME: should `saveTemps` and `ltoObjPath` use the same file name?
+    if (config->saveTemps)
+      saveBuffer(objBuf,
+                 config->outputFile + ((i == 0) ? "" : Twine(i)) + ".lto.o");
+
+    auto filePath = outputFilePath(i);
     uint32_t modTime = 0;
     if (!config->ltoObjPath.empty()) {
-      filePath = config->ltoObjPath;
-      path::append(filePath, Twine(i) + "." +
-                                 getArchitectureName(config->arch()) +
-                                 ".lto.o");
-      saveBuffer(buf[i], filePath);
+      saveOrHardlinkBuffer(objBuf, filePath, cachePath);
       modTime = getModTime(filePath);
     }
     ret.push_back(make<ObjFile>(
-        MemoryBufferRef(buf[i], saver.save(filePath.str())), modTime, ""));
+        MemoryBufferRef(objBuf, saver().save(filePath.str())), modTime, ""));
   }
-  for (std::unique_ptr<MemoryBuffer> &file : files)
-    if (file)
-      ret.push_back(make<ObjFile>(*file, 0, ""));
+
   return ret;
 }
index d64016f..f07b1e3 100644 (file)
@@ -9,19 +9,19 @@
 #ifndef LLD_MACHO_LTO_H
 #define LLD_MACHO_LTO_H
 
+#include "lld/Common/LLVM.h"
+#include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/raw_ostream.h"
 #include <memory>
 #include <vector>
 
-namespace llvm {
-namespace lto {
+namespace llvm::lto {
 class LTO;
-} // namespace lto
-} // namespace llvm
+} // namespace llvm::lto
 
-namespace lld {
-namespace macho {
+namespace lld::macho {
 
 class BitcodeFile;
 class ObjFile;
@@ -37,9 +37,10 @@ private:
   std::unique_ptr<llvm::lto::LTO> ltoObj;
   std::vector<llvm::SmallString<0>> buf;
   std::vector<std::unique_ptr<llvm::MemoryBuffer>> files;
+  std::unique_ptr<llvm::raw_fd_ostream> indexFile;
+  llvm::DenseSet<StringRef> thinIndices;
 };
 
-} // namespace macho
-} // namespace lld
+} // namespace lld::macho
 
 #endif
index 44715bf..42390a2 100644 (file)
@@ -17,9 +17,7 @@
 
 #include "llvm/Support/Endian.h"
 
-namespace lld {
-
-namespace structs {
+namespace lld::structs {
 
 struct nlist_64 {
   llvm::support::ulittle32_t n_strx;
@@ -44,8 +42,6 @@ struct entry_point_command {
   llvm::support::ulittle64_t stacksize;
 };
 
-} // namespace structs
-
-} // namespace lld
+} // namespace lld::structs
 
 #endif
index 79471ee..c16e046 100644 (file)
@@ -6,9 +6,10 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// This file implements the -map option. It shows lists in order and
-// hierarchically the outputFile, arch, input files, output sections and
-// symbol:
+// This file implements the -map option, which maps address ranges to their
+// respective contents, plus the input file these contents were originally from.
+// The contents (typically symbols) are listed in address order. Dead-stripped
+// contents are included as well.
 //
 // # Path: test
 // # Arch: x86_84
 // [  0] linker synthesized
 // [  1] a.o
 // # Sections:
-// # Address  Size      Segment  Section
-// 0x1000005C0  0x0000004C  __TEXT  __text
+// # Address    Size       Segment  Section
+// 0x1000005C0  0x0000004C __TEXT   __text
 // # Symbols:
-// # Address  File  Name
-// 0x1000005C0  [  1] _main
+// # Address    Size       File  Name
+// 0x1000005C0  0x00000001 [  1] _main
+// # Dead Stripped Symbols:
+// #            Size       File  Name
+// <<dead>>     0x00000001 [  1] _foo
 //
 //===----------------------------------------------------------------------===//
 
 #include "MapFile.h"
+#include "ConcatOutputSection.h"
 #include "Config.h"
 #include "InputFiles.h"
 #include "InputSection.h"
-#include "OutputSection.h"
 #include "OutputSegment.h"
 #include "Symbols.h"
+#include "SyntheticSections.h"
 #include "Target.h"
+#include "lld/Common/ErrorHandler.h"
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/Support/Parallel.h"
 #include "llvm/Support/TimeProfiler.h"
 
@@ -40,55 +47,103 @@ using namespace llvm::sys;
 using namespace lld;
 using namespace lld::macho;
 
-using SymbolMapTy = DenseMap<const InputSection *, SmallVector<Defined *, 4>>;
-
-// Returns a map from sections to their symbols.
-static SymbolMapTy getSectionSyms(ArrayRef<Defined *> syms) {
-  SymbolMapTy ret;
-  for (Defined *dr : syms)
-    ret[dr->isec].push_back(dr);
-
-  // Sort symbols by address. We want to print out symbols in the order they
-  // appear in the output file rather than the order they appeared in the input
-  // files.
-  for (auto &it : ret)
-    parallelSort(
-        it.second.begin(), it.second.end(), [](Defined *a, Defined *b) {
-          return a->getVA() != b->getVA() ? a->getVA() < b->getVA()
-                                          : a->getName() < b->getName();
-        });
-  return ret;
-}
+struct CStringInfo {
+  uint32_t fileIndex;
+  StringRef str;
+};
+
+struct MapInfo {
+  SmallVector<InputFile *> files;
+  SmallVector<Defined *> deadSymbols;
+  DenseMap<const OutputSection *,
+           SmallVector<std::pair<uint64_t /*addr*/, CStringInfo>>>
+      liveCStringsForSection;
+  SmallVector<CStringInfo> deadCStrings;
+};
+
+static MapInfo gatherMapInfo() {
+  MapInfo info;
+  for (InputFile *file : inputFiles) {
+    bool isReferencedFile = false;
+
+    if (isa<ObjFile>(file) || isa<BitcodeFile>(file)) {
+      uint32_t fileIndex = info.files.size() + 1;
 
-// Returns a list of all symbols that we want to print out.
-static std::vector<Defined *> getSymbols() {
-  std::vector<Defined *> v;
-  for (InputFile *file : inputFiles)
-    if (isa<ObjFile>(file))
-      for (Symbol *sym : file->symbols)
+      // Gather the dead symbols. We don't have to bother with the live ones
+      // because we will pick them up as we iterate over the OutputSections
+      // later.
+      for (Symbol *sym : file->symbols) {
         if (auto *d = dyn_cast_or_null<Defined>(sym))
-          if (d->isLive() && d->isec && d->getFile() == file) {
-            assert(!shouldOmitFromOutput(d->isec));
-            v.push_back(d);
+          // Only emit the prevailing definition of a symbol. Also, don't emit
+          // the symbol if it is part of a cstring section (we use the literal
+          // value instead, similar to ld64)
+          if (d->isec && d->getFile() == file &&
+              !isa<CStringInputSection>(d->isec)) {
+            isReferencedFile = true;
+            if (!d->isLive())
+              info.deadSymbols.push_back(d);
           }
-  return v;
+      }
+
+      // Gather all the cstrings (both live and dead). A CString(Output)Section
+      // doesn't provide us a way of figuring out which InputSections its
+      // cstring contents came from, so we need to build up that mapping here.
+      for (const Section *sec : file->sections) {
+        for (const Subsection &subsec : sec->subsections) {
+          if (auto isec = dyn_cast<CStringInputSection>(subsec.isec)) {
+            auto &liveCStrings = info.liveCStringsForSection[isec->parent];
+            for (const auto &[i, piece] : llvm::enumerate(isec->pieces)) {
+              if (piece.live)
+                liveCStrings.push_back({isec->parent->addr + piece.outSecOff,
+                                        {fileIndex, isec->getStringRef(i)}});
+              else
+                info.deadCStrings.push_back({fileIndex, isec->getStringRef(i)});
+              isReferencedFile = true;
+            }
+          } else {
+            break;
+          }
+        }
+      }
+    } else if (const auto *dylibFile = dyn_cast<DylibFile>(file)) {
+      isReferencedFile = dylibFile->isReferenced();
+    }
+
+    if (isReferencedFile)
+      info.files.push_back(file);
+  }
+
+  // cstrings are not stored in sorted order in their OutputSections, so we sort
+  // them here.
+  for (auto &liveCStrings : info.liveCStringsForSection)
+    parallelSort(liveCStrings.second, [](const auto &p1, const auto &p2) {
+      return p1.first < p2.first;
+    });
+  return info;
+}
+
+// For printing the contents of the __stubs and __la_symbol_ptr sections.
+void printStubsEntries(
+    raw_fd_ostream &os,
+    const DenseMap<lld::macho::InputFile *, uint32_t> &readerToFileOrdinal,
+    const OutputSection *osec, size_t entrySize) {
+  for (const Symbol *sym : in.stubs->getEntries())
+    os << format("0x%08llX\t0x%08zX\t[%3u] %s\n",
+                 osec->addr + sym->stubsIndex * entrySize, entrySize,
+                 readerToFileOrdinal.lookup(sym->getFile()),
+                 sym->getName().str().data());
 }
 
-// Construct a map from symbols to their stringified representations.
-// Demangling symbols (which is what toString() does) is slow, so
-// we do that in batch using parallel-for.
-static DenseMap<Symbol *, std::string>
-getSymbolStrings(ArrayRef<Defined *> syms) {
-  std::vector<std::string> str(syms.size());
-  parallelForEachN(0, syms.size(), [&](size_t i) {
-    raw_string_ostream os(str[i]);
-    os << toString(*syms[i]);
-  });
-
-  DenseMap<Symbol *, std::string> ret;
-  for (size_t i = 0, e = syms.size(); i < e; ++i)
-    ret[syms[i]] = std::move(str[i]);
-  return ret;
+void printNonLazyPointerSection(raw_fd_ostream &os,
+                                NonLazyPointerSectionBase *osec) {
+  // ld64 considers stubs to belong to particular files, but considers GOT
+  // entries to be linker-synthesized. Not sure why they made that decision, but
+  // I think we can follow suit unless there's demand for better symbol-to-file
+  // associations.
+  for (const Symbol *sym : osec->getEntries())
+    os << format("0x%08llX\t0x%08zX\t[  0] non-lazy-pointer-to-local: %s\n",
+                 osec->addr + sym->gotIndex * target->wordSize,
+                 target->wordSize, sym->getName().str().data());
 }
 
 void macho::writeMapFile() {
@@ -105,31 +160,21 @@ void macho::writeMapFile() {
     return;
   }
 
-  // Dump output path.
   os << format("# Path: %s\n", config->outputFile.str().c_str());
-
-  // Dump output architecture.
   os << format("# Arch: %s\n",
                getArchitectureName(config->arch()).str().c_str());
 
-  // Dump table of object files.
+  MapInfo info = gatherMapInfo();
+
   os << "# Object files:\n";
   os << format("[%3u] %s\n", 0, (const char *)"linker synthesized");
   uint32_t fileIndex = 1;
   DenseMap<lld::macho::InputFile *, uint32_t> readerToFileOrdinal;
-  for (InputFile *file : inputFiles) {
-    if (isa<ObjFile>(file)) {
-      os << format("[%3u] %s\n", fileIndex, file->getName().str().c_str());
-      readerToFileOrdinal[file] = fileIndex++;
-    }
+  for (InputFile *file : info.files) {
+    os << format("[%3u] %s\n", fileIndex, file->getName().str().c_str());
+    readerToFileOrdinal[file] = fileIndex++;
   }
 
-  // Collect symbol info that we want to print out.
-  std::vector<Defined *> syms = getSymbols();
-  SymbolMapTy sectionSyms = getSectionSyms(syms);
-  DenseMap<Symbol *, std::string> symStr = getSymbolStrings(syms);
-
-  // Dump table of sections
   os << "# Sections:\n";
   os << "# Address\tSize    \tSegment\tSection\n";
   for (OutputSegment *seg : outputSegments)
@@ -141,19 +186,63 @@ void macho::writeMapFile() {
                    seg->name.str().c_str(), osec->name.str().c_str());
     }
 
-  // Dump table of symbols
   os << "# Symbols:\n";
-  os << "# Address\t    File  Name\n";
-  for (InputSection *isec : inputSections) {
-    auto symsIt = sectionSyms.find(isec);
-    assert(!shouldOmitFromOutput(isec) || (symsIt == sectionSyms.end()));
-    if (symsIt == sectionSyms.end())
-      continue;
-    for (Symbol *sym : symsIt->second) {
-      os << format("0x%08llX\t[%3u] %s\n", sym->getVA(),
-                   readerToFileOrdinal[sym->getFile()], symStr[sym].c_str());
+  os << "# Address\tSize    \tFile  Name\n";
+  for (const OutputSegment *seg : outputSegments) {
+    for (const OutputSection *osec : seg->getSections()) {
+      if (auto *concatOsec = dyn_cast<ConcatOutputSection>(osec)) {
+        for (const InputSection *isec : concatOsec->inputs) {
+          for (Defined *sym : isec->symbols)
+            os << format("0x%08llX\t0x%08llX\t[%3u] %s\n", sym->getVA(),
+                         sym->size, readerToFileOrdinal[sym->getFile()],
+                         sym->getName().str().data());
+        }
+      } else if (osec == in.cStringSection || osec == in.objcMethnameSection) {
+        const auto &liveCStrings = info.liveCStringsForSection.lookup(osec);
+        uint64_t lastAddr = 0; // strings will never start at address 0, so this
+                               // is a sentinel value
+        for (const auto &[addr, info] : liveCStrings) {
+          uint64_t size = 0;
+          if (addr != lastAddr)
+            size = info.str.size() + 1; // include null terminator
+          lastAddr = addr;
+          os << format("0x%08llX\t0x%08llX\t[%3u] literal string: ", addr, size,
+                       info.fileIndex);
+          os.write_escaped(info.str) << "\n";
+        }
+      } else if (osec == (void *)in.unwindInfo) {
+        os << format("0x%08llX\t0x%08llX\t[  0] compact unwind info\n",
+                     osec->addr, osec->getSize());
+      } else if (osec == in.stubs) {
+        printStubsEntries(os, readerToFileOrdinal, osec, target->stubSize);
+      } else if (osec == in.lazyPointers) {
+        printStubsEntries(os, readerToFileOrdinal, osec, target->wordSize);
+      } else if (osec == in.stubHelper) {
+        // yes, ld64 calls it "helper helper"...
+        os << format("0x%08llX\t0x%08llX\t[  0] helper helper\n", osec->addr,
+                     osec->getSize());
+      } else if (osec == in.got) {
+        printNonLazyPointerSection(os, in.got);
+      } else if (osec == in.tlvPointers) {
+        printNonLazyPointerSection(os, in.tlvPointers);
+      }
+      // TODO print other synthetic sections
     }
   }
 
-  // TODO: when we implement -dead_strip, we should dump dead stripped symbols
+  if (config->deadStrip) {
+    os << "# Dead Stripped Symbols:\n";
+    os << "#        \tSize    \tFile  Name\n";
+    for (Defined *sym : info.deadSymbols) {
+      assert(!sym->isLive());
+      os << format("<<dead>>\t0x%08llX\t[%3u] %s\n", sym->size,
+                   readerToFileOrdinal[sym->getFile()],
+                   sym->getName().str().data());
+    }
+    for (CStringInfo &cstrInfo : info.deadCStrings) {
+      os << format("<<dead>>\t0x%08zX\t[%3u] literal string: ",
+                   cstrInfo.str.size() + 1, cstrInfo.fileIndex);
+      os.write_escaped(cstrInfo.str) << "\n";
+    }
+  }
 }
index bf16ffd..37436d7 100644 (file)
@@ -9,10 +9,8 @@
 #ifndef LLD_MACHO_MAPFILE_H
 #define LLD_MACHO_MAPFILE_H
 
-namespace lld {
-namespace macho {
+namespace lld::macho {
 void writeMapFile();
-} // namespace macho
-} // namespace lld
+} // namespace lld::macho
 
 #endif
index 1166912..a37213d 100644 (file)
 #include "SymbolTable.h"
 #include "Symbols.h"
 #include "UnwindInfoSection.h"
-#include "mach-o/compact_unwind_encoding.h"
+
+#include "lld/Common/ErrorHandler.h"
 #include "llvm/Support/TimeProfiler.h"
 
-namespace lld {
-namespace macho {
+#include "mach-o/compact_unwind_encoding.h"
+
+namespace lld::macho {
 
 using namespace llvm;
 using namespace llvm::MachO;
 
+struct WhyLiveEntry {
+  InputSection *isec;
+  // Keep track of the entry that caused us to mark `isec` as live.
+  const WhyLiveEntry *prev;
+
+  WhyLiveEntry(InputSection *isec, const WhyLiveEntry *prev)
+      : isec(isec), prev(prev) {}
+};
+
+// Type-erased interface to MarkLiveImpl. Used for adding roots to the liveness
+// graph.
+class MarkLive {
+public:
+  virtual void enqueue(InputSection *isec, uint64_t off) = 0;
+  virtual void addSym(Symbol *s) = 0;
+  virtual void markTransitively() = 0;
+  virtual ~MarkLive() = default;
+};
+
+template <bool RecordWhyLive> class MarkLiveImpl : public MarkLive {
+public:
+  // -why_live is a rarely used option, so we don't want support for that flag
+  // to slow down the main -dead_strip code path. As such, we employ templates
+  // to avoid the usage of WhyLiveEntry in the main code path. This saves us
+  // from needless allocations and pointer indirections.
+  using WorklistEntry =
+      std::conditional_t<RecordWhyLive, WhyLiveEntry, InputSection>;
+
+  void enqueue(InputSection *isec, uint64_t off) override {
+    enqueue(isec, off, nullptr);
+  }
+  void addSym(Symbol *s) override { addSym(s, nullptr); }
+  void markTransitively() override;
+
+private:
+  void enqueue(InputSection *isec, uint64_t off, const WorklistEntry *prev);
+  void addSym(Symbol *s, const WorklistEntry *prev);
+  const InputSection *getInputSection(const WorklistEntry *) const;
+  WorklistEntry *makeEntry(InputSection *, const WorklistEntry *prev) const;
+
+  // We build up a worklist of sections which have been marked as live. We
+  // only push into the worklist when we discover an unmarked section, and we
+  // mark as we push, so sections never appear twice in the list. Literal
+  // sections cannot contain references to other sections, so we only store
+  // ConcatInputSections in our worklist.
+  SmallVector<WorklistEntry *, 256> worklist;
+};
+
+template <bool RecordWhyLive>
+void MarkLiveImpl<RecordWhyLive>::enqueue(
+    InputSection *isec, uint64_t off,
+    const typename MarkLiveImpl<RecordWhyLive>::WorklistEntry *prev) {
+  if (isec->isLive(off))
+    return;
+  isec->markLive(off);
+  if (auto s = dyn_cast<ConcatInputSection>(isec)) {
+    assert(!s->isCoalescedWeak());
+    worklist.push_back(makeEntry(s, prev));
+  }
+}
+
+static void printWhyLive(const Symbol *s, const WhyLiveEntry *prev) {
+  std::string out = toString(*s) + " from " + toString(s->getFile());
+  int indent = 2;
+  for (const WhyLiveEntry *entry = prev; entry;
+       entry = entry->prev, indent += 2) {
+    const TinyPtrVector<Defined *> &symbols = entry->isec->symbols;
+    // With .subsections_with_symbols set, most isecs will have exactly one
+    // entry in their symbols vector, so we just print the first one.
+    if (!symbols.empty())
+      out += "\n" + std::string(indent, ' ') + toString(*symbols.front()) +
+             " from " + toString(symbols.front()->getFile());
+  }
+  message(out);
+}
+
+template <bool RecordWhyLive>
+void MarkLiveImpl<RecordWhyLive>::addSym(
+    Symbol *s,
+    const typename MarkLiveImpl<RecordWhyLive>::WorklistEntry *prev) {
+  if (s->used)
+    return;
+  s->used = true;
+  if constexpr (RecordWhyLive)
+    if (!config->whyLive.empty() && config->whyLive.match(s->getName()))
+      printWhyLive(s, prev);
+  if (auto *d = dyn_cast<Defined>(s)) {
+    if (d->isec)
+      enqueue(d->isec, d->value, prev);
+    if (d->unwindEntry)
+      enqueue(d->unwindEntry, 0, prev);
+  }
+}
+
+template <bool RecordWhyLive>
+const InputSection *MarkLiveImpl<RecordWhyLive>::getInputSection(
+    const MarkLiveImpl<RecordWhyLive>::WorklistEntry *entry) const {
+  if constexpr (RecordWhyLive)
+    return entry->isec;
+  else
+    return entry;
+}
+
+template <bool RecordWhyLive>
+typename MarkLiveImpl<RecordWhyLive>::WorklistEntry *
+MarkLiveImpl<RecordWhyLive>::makeEntry(
+    InputSection *isec,
+    const MarkLiveImpl<RecordWhyLive>::WorklistEntry *prev) const {
+  if constexpr (RecordWhyLive) {
+    if (!isec) {
+      assert(!prev);
+      return nullptr;
+    }
+    return make<WhyLiveEntry>(isec, prev);
+  } else {
+    return isec;
+  }
+}
+
+template <bool RecordWhyLive>
+void MarkLiveImpl<RecordWhyLive>::markTransitively() {
+  do {
+    // Mark things reachable from GC roots as live.
+    while (!worklist.empty()) {
+      WorklistEntry *entry = worklist.pop_back_val();
+      // Entries that get placed onto the worklist always contain
+      // ConcatInputSections. `WhyLiveEntry::prev` may point to entries that
+      // contain other types of InputSections (due to S_ATTR_LIVE_SUPPORT), but
+      // those entries should never be pushed onto the worklist.
+      auto *isec = cast<ConcatInputSection>(getInputSection(entry));
+      assert(isec->live && "We mark as live when pushing onto the worklist!");
+
+      // Mark all symbols listed in the relocation table for this section.
+      for (const Reloc &r : isec->relocs) {
+        if (auto *s = r.referent.dyn_cast<Symbol *>())
+          addSym(s, entry);
+        else
+          enqueue(r.referent.get<InputSection *>(), r.addend, entry);
+      }
+      for (Defined *d : getInputSection(entry)->symbols)
+        addSym(d, entry);
+    }
+
+    // S_ATTR_LIVE_SUPPORT sections are live if they point _to_ a live
+    // section. Process them in a second pass.
+    for (ConcatInputSection *isec : inputSections) {
+      // FIXME: Check if copying all S_ATTR_LIVE_SUPPORT sections into a
+      // separate vector and only walking that here is faster.
+      if (!(isec->getFlags() & S_ATTR_LIVE_SUPPORT) || isec->live)
+        continue;
+
+      for (const Reloc &r : isec->relocs) {
+        if (auto *s = r.referent.dyn_cast<Symbol *>()) {
+          if (s->isLive()) {
+            InputSection *referentIsec = nullptr;
+            if (auto *d = dyn_cast<Defined>(s))
+              referentIsec = d->isec;
+            enqueue(isec, 0, makeEntry(referentIsec, nullptr));
+          }
+        } else {
+          auto *referentIsec = r.referent.get<InputSection *>();
+          if (referentIsec->isLive(r.addend))
+            enqueue(isec, 0, makeEntry(referentIsec, nullptr));
+        }
+      }
+    }
+
+    // S_ATTR_LIVE_SUPPORT could have marked additional sections live,
+    // which in turn could mark additional S_ATTR_LIVE_SUPPORT sections live.
+    // Iterate. In practice, the second iteration won't mark additional
+    // S_ATTR_LIVE_SUPPORT sections live.
+  } while (!worklist.empty());
+}
+
 // Set live bit on for each reachable chunk. Unmarked (unreachable)
 // InputSections will be ignored by Writer, so they will be excluded
 // from the final output.
 void markLive() {
   TimeTraceScope timeScope("markLive");
-
-  // We build up a worklist of sections which have been marked as live. We only
-  // push into the worklist when we discover an unmarked section, and we mark
-  // as we push, so sections never appear twice in the list.
-  // Literal sections cannot contain references to other sections, so we only
-  // store ConcatInputSections in our worklist.
-  SmallVector<ConcatInputSection *, 256> worklist;
-
-  auto enqueue = [&](InputSection *isec, uint64_t off) {
-    if (isec->isLive(off))
-      return;
-    isec->markLive(off);
-    if (auto s = dyn_cast<ConcatInputSection>(isec)) {
-      assert(!s->isCoalescedWeak());
-      worklist.push_back(s);
-    }
-  };
-
-  auto addSym = [&](Symbol *s) {
-    s->used = true;
-    if (auto *d = dyn_cast<Defined>(s))
-      if (d->isec)
-        enqueue(d->isec, d->value);
-  };
-
+  MarkLive *marker;
+  if (config->whyLive.empty())
+    marker = make<MarkLiveImpl<false>>();
+  else
+    marker = make<MarkLiveImpl<true>>();
   // Add GC roots.
   if (config->entry)
-    addSym(config->entry);
+    marker->addSym(config->entry);
   for (Symbol *sym : symtab->getSymbols()) {
     if (auto *defined = dyn_cast<Defined>(sym)) {
       // -exported_symbol(s_list)
       if (!config->exportedSymbols.empty() &&
           config->exportedSymbols.match(defined->getName())) {
-        // FIXME: Instead of doing this here, maybe the Driver code doing
-        // the matching should add them to explicitUndefineds? Then the
-        // explicitUndefineds code below would handle this automatically.
-        assert(!defined->privateExtern &&
-               "should have been rejected by driver");
-        addSym(defined);
+        // NOTE: Even though exporting private externs is an ill-defined
+        // operation, we are purposely not checking for privateExtern in
+        // order to follow ld64's behavior of treating all exported private
+        // extern symbols as live, irrespective of whether they are autohide.
+        marker->addSym(defined);
         continue;
       }
 
       // public symbols explicitly marked .no_dead_strip
       if (defined->referencedDynamically || defined->noDeadStrip) {
-        addSym(defined);
+        marker->addSym(defined);
         continue;
       }
 
-      // FIXME: When we implement these flags, make symbols from them GC roots:
+      // FIXME: When we implement these flags, make symbols from them GC
+      // roots:
       // * -reexported_symbol(s_list)
-      // * -alias(-list)
+      // * -alias_list
       // * -init
 
       // In dylibs and bundles and in executables with -export_dynamic,
@@ -84,105 +240,45 @@ void markLive() {
       bool externsAreRoots =
           config->outputType != MH_EXECUTE || config->exportDynamic;
       if (externsAreRoots && !defined->privateExtern) {
-        addSym(defined);
+        marker->addSym(defined);
         continue;
       }
     }
   }
   // -u symbols
   for (Symbol *sym : config->explicitUndefineds)
-    if (auto *defined = dyn_cast<Defined>(sym))
-      addSym(defined);
+    marker->addSym(sym);
   // local symbols explicitly marked .no_dead_strip
   for (const InputFile *file : inputFiles)
     if (auto *objFile = dyn_cast<ObjFile>(file))
       for (Symbol *sym : objFile->symbols)
         if (auto *defined = dyn_cast_or_null<Defined>(sym))
           if (!defined->isExternal() && defined->noDeadStrip)
-            addSym(defined);
+            marker->addSym(defined);
   if (auto *stubBinder =
           dyn_cast_or_null<DylibSymbol>(symtab->find("dyld_stub_binder")))
-    addSym(stubBinder);
+    marker->addSym(stubBinder);
   for (ConcatInputSection *isec : inputSections) {
     // Sections marked no_dead_strip
     if (isec->getFlags() & S_ATTR_NO_DEAD_STRIP) {
-      enqueue(isec, 0);
+      marker->enqueue(isec, 0);
       continue;
     }
 
     // mod_init_funcs, mod_term_funcs sections
     if (sectionType(isec->getFlags()) == S_MOD_INIT_FUNC_POINTERS ||
         sectionType(isec->getFlags()) == S_MOD_TERM_FUNC_POINTERS) {
-      enqueue(isec, 0);
+      assert(!config->emitInitOffsets ||
+             sectionType(isec->getFlags()) != S_MOD_INIT_FUNC_POINTERS);
+      marker->enqueue(isec, 0);
       continue;
     }
   }
 
-  // Dead strip runs before UnwindInfoSection handling so we need to keep
-  // __LD,__compact_unwind alive here.
-  // But that section contains absolute references to __TEXT,__text and
-  // keeps most code alive due to that. So we can't just enqueue() the
-  // section: We must skip the relocations for the functionAddress
-  // in each CompactUnwindEntry.
-  // See also scanEhFrameSection() in lld/ELF/MarkLive.cpp.
-  for (ConcatInputSection *isec : in.unwindInfo->getInputs()) {
-    isec->live = true;
-    const int compactUnwindEntrySize =
-        target->wordSize == 8 ? sizeof(CompactUnwindEntry<uint64_t>)
-                              : sizeof(CompactUnwindEntry<uint32_t>);
-    for (const Reloc &r : isec->relocs) {
-      // This is the relocation for the address of the function itself.
-      // Ignore it, else these would keep everything alive.
-      if (r.offset % compactUnwindEntrySize == 0)
-        continue;
-
-      if (auto *s = r.referent.dyn_cast<Symbol *>())
-        addSym(s);
-      else
-        enqueue(r.referent.get<InputSection *>(), r.addend);
-    }
-  }
+  for (ConcatInputSection *isec : in.initOffsets->inputs())
+    marker->enqueue(isec, 0);
 
-  do {
-    // Mark things reachable from GC roots as live.
-    while (!worklist.empty()) {
-      ConcatInputSection *s = worklist.pop_back_val();
-      assert(s->live && "We mark as live when pushing onto the worklist!");
-
-      // Mark all symbols listed in the relocation table for this section.
-      for (const Reloc &r : s->relocs) {
-        if (auto *s = r.referent.dyn_cast<Symbol *>())
-          addSym(s);
-        else
-          enqueue(r.referent.get<InputSection *>(), r.addend);
-      }
-    }
-
-    // S_ATTR_LIVE_SUPPORT sections are live if they point _to_ a live section.
-    // Process them in a second pass.
-    for (ConcatInputSection *isec : inputSections) {
-      // FIXME: Check if copying all S_ATTR_LIVE_SUPPORT sections into a
-      // separate vector and only walking that here is faster.
-      if (!(isec->getFlags() & S_ATTR_LIVE_SUPPORT) || isec->live)
-        continue;
-
-      for (const Reloc &r : isec->relocs) {
-        bool referentLive;
-        if (auto *s = r.referent.dyn_cast<Symbol *>())
-          referentLive = s->isLive();
-        else
-          referentLive = r.referent.get<InputSection *>()->isLive(r.addend);
-        if (referentLive)
-          enqueue(isec, 0);
-      }
-    }
-
-    // S_ATTR_LIVE_SUPPORT could have marked additional sections live,
-    // which in turn could mark additional S_ATTR_LIVE_SUPPORT sections live.
-    // Iterate. In practice, the second iteration won't mark additional
-    // S_ATTR_LIVE_SUPPORT sections live.
-  } while (!worklist.empty());
+  marker->markTransitively();
 }
 
-} // namespace macho
-} // namespace lld
+} // namespace lld::macho
index 4db657c..d7fe7ff 100644 (file)
@@ -9,12 +9,10 @@
 #ifndef LLD_MACHO_MARKLIVE_H
 #define LLD_MACHO_MARKLIVE_H
 
-namespace lld {
-namespace macho {
+namespace lld::macho {
 
 void markLive();
 
-} // namespace macho
-} // namespace lld
+} // namespace lld::macho
 
 #endif // LLD_MACHO_MARKLIVE_H
index 7ed8008..d484c40 100644 (file)
 #include "OutputSegment.h"
 #include "Target.h"
 
+#include "lld/Common/ErrorHandler.h"
 #include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Bitcode/BitcodeReader.h"
 
 using namespace llvm;
 using namespace llvm::MachO;
 using namespace lld;
 using namespace lld::macho;
 
-template <class LP> static bool hasObjCSection(MemoryBufferRef mb) {
-  using Section = typename LP::section;
+template <class LP> static bool objectHasObjCSection(MemoryBufferRef mb) {
+  using SectionHeader = typename LP::section;
 
   auto *hdr =
       reinterpret_cast<const typename LP::mach_header *>(mb.getBufferStart());
@@ -29,16 +31,17 @@ template <class LP> static bool hasObjCSection(MemoryBufferRef mb) {
 
   if (const auto *c =
           findCommand<typename LP::segment_command>(hdr, LP::segmentLCType)) {
-    auto sectionHeaders =
-        ArrayRef<Section>{reinterpret_cast<const Section *>(c + 1), c->nsects};
-    for (const Section &sec : sectionHeaders) {
-      StringRef sectname(sec.sectname,
-                         strnlen(sec.sectname, sizeof(sec.sectname)));
-      StringRef segname(sec.segname, strnlen(sec.segname, sizeof(sec.segname)));
+    auto sectionHeaders = ArrayRef<SectionHeader>{
+        reinterpret_cast<const SectionHeader *>(c + 1), c->nsects};
+    for (const SectionHeader &secHead : sectionHeaders) {
+      StringRef sectname(secHead.sectname,
+                         strnlen(secHead.sectname, sizeof(secHead.sectname)));
+      StringRef segname(secHead.segname,
+                        strnlen(secHead.segname, sizeof(secHead.segname)));
       if ((segname == segment_names::data &&
            sectname == section_names::objcCatList) ||
           (segname == segment_names::text &&
-           sectname == section_names::swift)) {
+           sectname.startswith(section_names::swift))) {
         return true;
       }
     }
@@ -46,9 +49,20 @@ template <class LP> static bool hasObjCSection(MemoryBufferRef mb) {
   return false;
 }
 
-bool macho::hasObjCSection(MemoryBufferRef mb) {
+static bool objectHasObjCSection(MemoryBufferRef mb) {
   if (target->wordSize == 8)
-    return ::hasObjCSection<LP64>(mb);
+    return ::objectHasObjCSection<LP64>(mb);
   else
-    return ::hasObjCSection<ILP32>(mb);
+    return ::objectHasObjCSection<ILP32>(mb);
+}
+
+bool macho::hasObjCSection(MemoryBufferRef mb) {
+  switch (identify_magic(mb.getBuffer())) {
+  case file_magic::macho_object:
+    return objectHasObjCSection(mb);
+  case file_magic::bitcode:
+    return check(isBitcodeContainingObjCCategory(mb));
+  default:
+    return false;
+  }
 }
index 8db459a..67fa411 100644 (file)
@@ -11,8 +11,7 @@
 
 #include "llvm/Support/MemoryBuffer.h"
 
-namespace lld {
-namespace macho {
+namespace lld::macho {
 
 namespace objc {
 
@@ -25,7 +24,6 @@ constexpr const char ivar[] = "_OBJC_IVAR_$_";
 
 bool hasObjCSection(llvm::MemoryBufferRef);
 
-} // namespace macho
-} // namespace lld
+} // namespace lld::macho
 
 #endif
index cda857d..f05835d 100644 (file)
@@ -28,9 +28,22 @@ def color_diagnostics_eq: Joined<["--"], "color-diagnostics=">,
 def threads_eq : Joined<["--"], "threads=">,
     HelpText<"Number of threads. '1' disables multi-threading. By default all available hardware threads are used">,
     Group<grp_lld>;
+def thinlto_emit_imports_files: Flag<["--"], "thinlto-emit-imports-files">,
+    Group<grp_lld>;
+def thinlto_emit_index_files: Flag<["--"], "thinlto-emit-index-files">,
+    Group<grp_lld>;
+def thinlto_index_only: Flag<["--"], "thinlto-index-only">,
+    Group<grp_lld>;
+def thinlto_index_only_eq: Joined<["--"], "thinlto-index-only=">,
+    Group<grp_lld>;
 def thinlto_jobs_eq : Joined<["--"], "thinlto-jobs=">,
     HelpText<"Number of ThinLTO jobs. Default to --threads=">,
     Group<grp_lld>;
+def thinlto_object_suffix_replace_eq:
+    Joined<["--"], "thinlto-object-suffix-replace=">,
+    Group<grp_lld>;
+def thinlto_prefix_replace_eq: Joined<["--"], "thinlto-prefix-replace=">,
+    Group<grp_lld>;
 def reproduce: Separate<["--"], "reproduce">,
     Group<grp_lld>;
 def reproduce_eq: Joined<["--"], "reproduce=">,
@@ -40,22 +53,28 @@ def reproduce_eq: Joined<["--"], "reproduce=">,
 def version: Flag<["--"], "version">,
     HelpText<"Display the version number and exit">,
     Group<grp_lld>;
-def lto_legacy_pass_manager: Flag<["--"], "lto-legacy-pass-manager">,
-    HelpText<"Use the legacy pass manager in LLVM">,
-    Group<grp_lld>;
 def no_lto_legacy_pass_manager : Flag<["--"], "no-lto-legacy-pass-manager">,
     HelpText<"Use the new pass manager in LLVM">,
     Group<grp_lld>;
-def time_trace: Flag<["--"], "time-trace">, HelpText<"Record time trace">,
+def time_trace_eq: Joined<["--"], "time-trace=">,
+    HelpText<"Record time trace to <file>">,
+    MetaVarName<"<file>">,
+    Group<grp_lld>;
+def : Flag<["--"], "time-trace">,
+    Alias<time_trace_eq>,
+    HelpText<"Record time trace to file next to output">,
     Group<grp_lld>;
 def time_trace_granularity_eq: Joined<["--"], "time-trace-granularity=">,
     HelpText<"Minimum time granularity (in microseconds) traced by time profiler">,
     Group<grp_lld>;
-def time_trace_file_eq: Joined<["--"], "time-trace-file=">,
-    HelpText<"Specify time trace output file">,
+def deduplicate_strings: Flag<["--"], "deduplicate-strings">,
+    HelpText<"Enable string deduplication">,
+    Group<grp_lld>;
+def no_deduplicate_strings: Flag<["--"], "no-deduplicate-strings">,
+    HelpText<"Disable string deduplication. This helps uncover cases of comparing string addresses instead of equality and might have a link time performance benefit.">,
     Group<grp_lld>;
-def deduplicate_literals: Flag<["--"], "deduplicate-literals">,
-    HelpText<"Enable literal deduplication. This is implied by --icf={safe,all}">,
+def dead_strip_duplicates: Flag<["--"], "dead-strip-duplicates">,
+    HelpText<"Do not error on duplicate symbols that will be dead stripped.">,
     Group<grp_lld>;
 def print_dylib_search: Flag<["--"], "print-dylib-search">,
     HelpText<"Print which paths lld searched when trying to find dylibs">,
@@ -68,11 +87,39 @@ def lto_O: Joined<["--"], "lto-O">,
     HelpText<"Set optimization level for LTO (default: 2)">,
     MetaVarName<"<opt-level>">,
     Group<grp_lld>;
-def thinlto_cache_policy: Joined<["--"], "thinlto-cache-policy=">,
+def thinlto_cache_policy_eq: Joined<["--"], "thinlto-cache-policy=">,
     HelpText<"Pruning policy for the ThinLTO cache">,
     Group<grp_lld>;
 def O : JoinedOrSeparate<["-"], "O">,
     HelpText<"Optimize output file size">;
+def start_lib: Flag<["--"], "start-lib">,
+    HelpText<"Start a grouping of objects that should be treated as if they were together in an archive">;
+def end_lib: Flag<["--"], "end-lib">,
+    HelpText<"End a grouping of objects that should be treated as if they were together in an archive">;
+def no_warn_dylib_install_name: Flag<["--"], "no-warn-dylib-install-name">,
+    HelpText<"Do not warn on -install_name if -dylib is not passed (default)">,
+    Group<grp_lld>;
+def warn_dylib_install_name: Flag<["--"], "warn-dylib-install-name">,
+    HelpText<"Warn on -install_name if -dylib is not passed">,
+    Group<grp_lld>;
+def call_graph_profile_sort: Flag<["--"], "call-graph-profile-sort">,
+    HelpText<"Reorder sections with call graph profile (default)">,
+    Group<grp_lld>;
+def no_call_graph_profile_sort : Flag<["--"], "no-call-graph-profile-sort">,
+    HelpText<"Do not reorder sections with call graph profile">,
+    Group<grp_lld>;
+def print_symbol_order_eq: Joined<["--"], "print-symbol-order=">,
+    HelpText<"Print a symbol order specified by --call-graph-profile-sort into the specified file">,
+    Group<grp_lld>;
+def ignore_auto_link_option : Separate<["--"], "ignore-auto-link-option">,
+    Group<grp_lld>;
+def ignore_auto_link_option_eq : Joined<["--"], "ignore-auto-link-option=">,
+    Alias<!cast<Separate>(ignore_auto_link_option)>,
+    HelpText<"Ignore a single auto-linked library or framework. Useful to ignore invalid options that ld64 ignores">,
+    Group<grp_lld>;
+def strict_auto_link : Flag<["--"], "strict-auto-link">,
+    HelpText<"Always warn for missing frameworks or libraries if they are loaded via LC_LINKER_OPTIONS">,
+    Group<grp_lld>;
 
 // This is a complete Options.td compiled from Apple's ld(1) manpage
 // dated 2018-03-07 and cross checked with ld64 source code in repo
@@ -208,6 +255,9 @@ def F : JoinedOrSeparate<["-"], "F">,
 def all_load : Flag<["-"], "all_load">,
     HelpText<"Load all members of all static archive libraries">,
     Group<grp_libs>;
+def noall_load : Flag<["-"], "noall_load">,
+    HelpText<"Don't load all static members from archives, this is the default, this negates -all_load">,
+    Group<grp_libs>;
 def ObjC : Flag<["-"], "ObjC">,
     HelpText<"Load all members of static archives that are an Objective-C class or category.">,
     Group<grp_libs>;
@@ -218,6 +268,14 @@ def force_load : Separate<["-"], "force_load">,
 def force_load_swift_libs : Flag<["-"], "force_load_swift_libs">,
     HelpText<"Apply -force_load to libraries listed in LC_LINKER_OPTIONS whose names start with 'swift'">,
     Group<grp_libs>;
+def load_hidden : Separate<["-"], "load_hidden">,
+    MetaVarName<"<path>">,
+    HelpText<"Load all symbols from static library with hidden visibility">,
+    Group<grp_libs>;
+def hidden_l : Joined<["-"], "hidden-l">,
+    MetaVarName<"<name>">,
+    HelpText<"Like -l<name>, but load all symbols with hidden visibility">,
+    Group<grp_libs>;
 
 def grp_content : OptionGroup<"content">, HelpText<"ADDITIONAL CONTENT">;
 
@@ -230,6 +288,10 @@ def segcreate : MultiArg<["-"], "segcreate", 3>,
     Alias<sectcreate>,
     HelpText<"Alias for -sectcreate">,
     Group<grp_content>;
+def add_empty_section : MultiArg<["-"], "add_empty_section", 2>,
+    MetaVarName<"<segment> <section>">,
+    HelpText<"Create an empty <section> in <segment>">,
+    Group<grp_content>;
 def filelist : Separate<["-"], "filelist">,
     MetaVarName<"<file>">,
     HelpText<"Read names of files to link from <file>">,
@@ -289,7 +351,8 @@ def no_branch_islands : Flag<["-"], "no_branch_islands">,
     Flags<[HelpHidden]>,
     Group<grp_opts>;
 def no_deduplicate : Flag<["-"], "no_deduplicate">,
-    HelpText<"Disable code deduplicaiton (synonym for `--icf=none')">,
+    HelpText<"Disable code deduplication (synonym for `--icf=none')">,
+    Alias<icf_eq>, AliasArgs<["none"]>,
     Group<grp_opts>;
 
 def grp_version : OptionGroup<"version">, HelpText<"VERSION TARGETING">;
@@ -417,7 +480,6 @@ def no_pie : Flag<["-"], "no_pie">,
 def pagezero_size : Separate<["-"], "pagezero_size">,
     MetaVarName<"<size>">,
     HelpText<"Size of unreadable segment at address zero is hex <size> (default is 4KB on 32-bit and 4GB on 64-bit)">,
-    Flags<[HelpHidden]>,
     Group<grp_main>;
 def stack_size : Separate<["-"], "stack_size">,
     MetaVarName<"<size>">,
@@ -460,6 +522,9 @@ def exported_symbols_list : Separate<["-"], "exported_symbols_list">,
     MetaVarName<"<file>">,
     HelpText<"Symbols specified in <file> remain global, while others become private externs">,
     Group<grp_resolve>;
+def no_exported_symbols : Flag<["-"], "no_exported_symbols">,
+    HelpText<"Don't export any symbols from the binary, useful for main executables that don't have plugins">,
+    Group<grp_resolve>;
 def unexported_symbol : Separate<["-"], "unexported_symbol">,
     MetaVarName<"<symbol>">,
     HelpText<"Global <symbol> becomes private extern">,
@@ -476,7 +541,6 @@ def reexported_symbols_list : Separate<["-"], "reexported_symbols_list">,
 def alias : MultiArg<["-"], "alias", 2>,
     MetaVarName<"<symbol_name> <alternate_name>">,
     HelpText<"Create a symbol alias with default global visibility">,
-    Flags<[HelpHidden]>,
     Group<grp_resolve>;
 def alias_list : Separate<["-"], "alias_list">,
     MetaVarName<"<file>">,
@@ -523,7 +587,6 @@ def whyload : Flag<["-"], "whyload">,
 def why_live : Separate<["-"], "why_live">,
     MetaVarName<"<symbol>">,
     HelpText<"Log a chain of references to <symbol>, for use with -dead_strip">,
-    Flags<[HelpHidden]>,
     Group<grp_introspect>;
 def print_statistics : Flag<["-"], "print_statistics">,
     HelpText<"Log the linker's memory and CPU usage">,
@@ -556,26 +619,21 @@ def grp_symtab : OptionGroup<"symtab">, HelpText<"SYMBOL TABLE">;
 
 def S : Flag<["-"], "S">,
     HelpText<"Strip debug information (STABS or DWARF) from the output">,
-    Flags<[HelpHidden]>,
     Group<grp_symtab>;
 def x : Flag<["-"], "x">,
     HelpText<"Exclude non-global symbols from the output symbol table">,
-    Flags<[HelpHidden]>,
     Group<grp_symtab>;
 def non_global_symbols_strip_list : Separate<["-"], "non_global_symbols_strip_list">,
     MetaVarName<"<path>">,
     HelpText<"Specify in <path> the non-global symbols that should be removed from the output symbol table">,
-    Flags<[HelpHidden]>,
     Group<grp_symtab>;
 def non_global_symbols_no_strip_list : Separate<["-"], "non_global_symbols_no_strip_list">,
     MetaVarName<"<path>">,
     HelpText<"Specify in <path> the non-global symbols that should remain in the output symbol table">,
-    Flags<[HelpHidden]>,
     Group<grp_symtab>;
 def oso_prefix : Separate<["-"], "oso_prefix">,
     MetaVarName<"<path>">,
     HelpText<"Remove the prefix <path> from OSO symbols in the debug map">,
-    Flags<[HelpHidden]>,
     Group<grp_symtab>;
 def add_ast_path : Separate<["-"], "add_ast_path">,
     MetaVarName<"<path>">,
@@ -863,7 +921,6 @@ def no_arch_warnings : Flag<["-"], "no_arch_warnings">,
     Group<grp_rare>;
 def arch_errors_fatal : Flag<["-"], "arch_errors_fatal">,
     HelpText<"Escalate to errors any warnings about inputs whose architecture does not match the -arch option">,
-    Flags<[HelpHidden]>,
     Group<grp_rare>;
 def e : Separate<["-"], "e">,
     MetaVarName<"<symbol>">,
@@ -871,7 +928,6 @@ def e : Separate<["-"], "e">,
     Group<grp_rare>;
 def w : Flag<["-"], "w">,
     HelpText<"Suppress all warnings">,
-    Flags<[HelpHidden]>,
     Group<grp_rare>;
 def final_output : Separate<["-"], "final_output">,
     MetaVarName<"<name>">,
@@ -947,6 +1003,22 @@ def mllvm : Separate<["-"], "mllvm">,
 def mcpu : Separate<["-"], "mcpu">,
     HelpText<"Processor family target for LTO code generation">,
     Group<grp_rare>;
+def no_dtrace_dof : Flag<["-"], "no_dtrace_dof">,
+    HelpText<"Disable dtrace-dof processing (default).">,
+    Group<grp_rare>;
+def objc_stubs_fast : Flag<["-"], "objc_stubs_fast">,
+    HelpText<"Produce larger stubs for Objective-C method calls with fewer jumps (default).">,
+    Group<grp_rare>;
+def objc_stubs_small : Flag<["-"], "objc_stubs_small">,
+    HelpText<"Produce smaller stubs for Objective-C method calls with more jumps.">,
+    Group<grp_rare>;
+def dyld_env : Separate<["-"], "dyld_env">,
+    MetaVarName<"<dyld_env_var>">,
+    HelpText<"Specifies a LC_DYLD_ENVIRONMENT variable value pair.">,
+    Group<grp_rare>;
+def ignore_auto_link : Flag<["-"], "ignore_auto_link">,
+    HelpText<"Ignore LC_LINKER_OPTIONs">,
+    Group<grp_rare>;
 
 def grp_deprecated : OptionGroup<"deprecated">, HelpText<"DEPRECATED">;
 
@@ -973,10 +1045,6 @@ def no_dead_strip_inits_and_terms : Flag<["-"], "no_dead_strip_inits_and_terms">
     HelpText<"Unnecessary option: initialization and termination are roots of the dead strip graph, so never dead stripped">,
     Flags<[HelpHidden]>,
     Group<grp_deprecated>;
-def noall_load : Flag<["-"], "noall_load">,
-    HelpText<"Unnecessary option: this is already the default">,
-    Flags<[HelpHidden]>,
-    Group<grp_deprecated>;
 
 def grp_obsolete : OptionGroup<"obsolete">, HelpText<"OBSOLETE">;
 
@@ -1155,7 +1223,7 @@ def allow_simulator_linking_to_macosx_dylibs : Flag<["-"], "allow_simulator_link
     HelpText<"This option is undocumented in ld64">,
     Flags<[HelpHidden]>,
     Group<grp_undocumented>;
-def bitcode_process_mode : Flag<["-"], "bitcode_process_mode">,
+def bitcode_process_mode : Separate<["-"], "bitcode_process_mode">,
     HelpText<"This option is undocumented in ld64">,
     Flags<[HelpHidden]>,
     Group<grp_undocumented>;
@@ -1179,10 +1247,6 @@ def debug_snapshot : Flag<["-"], "debug_snapshot">,
     Group<grp_undocumented>;
 def demangle : Flag<["-"], "demangle">,
     HelpText<"Demangle symbol names in diagnostics">;
-def dyld_env : Flag<["-"], "dyld_env">,
-    HelpText<"This option is undocumented in ld64">,
-    Flags<[HelpHidden]>,
-    Group<grp_undocumented>;
 def encryptable : Flag<["-"], "encryptable">,
     HelpText<"Generate the LC_ENCRYPTION_INFO load command">,
     Group<grp_undocumented>;
@@ -1194,8 +1258,10 @@ def executable_path : Flag<["-"], "executable_path">,
     Flags<[HelpHidden]>,
     Group<grp_undocumented>;
 def fixup_chains : Flag<["-"], "fixup_chains">,
-    HelpText<"This option is undocumented in ld64">,
-    Flags<[HelpHidden]>,
+    HelpText<"Emit chained fixups">,
+    Group<grp_undocumented>;
+def no_fixup_chains : Flag<["-"], "no_fixup_chains">,
+    HelpText<"Emit fixup information as classic dyld opcodes">,
     Group<grp_undocumented>;
 def fixup_chains_section : Flag<["-"], "fixup_chains_section">,
     HelpText<"This option is undocumented in ld64">,
@@ -1213,11 +1279,11 @@ def force_symbols_coalesce_list : Flag<["-"], "force_symbols_coalesce_list">,
     HelpText<"This option is undocumented in ld64">,
     Flags<[HelpHidden]>,
     Group<grp_undocumented>;
-def force_symbols_not_weak_list : Flag<["-"], "force_symbols_not_weak_list">,
+def force_symbols_not_weak_list : Separate<["-"], "force_symbols_not_weak_list">,
     HelpText<"This option is undocumented in ld64">,
     Flags<[HelpHidden]>,
     Group<grp_undocumented>;
-def force_symbols_weak_list : Flag<["-"], "force_symbols_weak_list">,
+def force_symbols_weak_list : Separate<["-"], "force_symbols_weak_list">,
     HelpText<"This option is undocumented in ld64">,
     Flags<[HelpHidden]>,
     Group<grp_undocumented>;
@@ -1232,17 +1298,11 @@ def i : Flag<["-"], "i">,
     HelpText<"This option is undocumented in ld64">,
     Flags<[HelpHidden]>,
     Group<grp_undocumented>;
-def ignore_auto_link : Flag<["-"], "ignore_auto_link">,
-    HelpText<"This option is undocumented in ld64">,
-    Flags<[HelpHidden]>,
-    Group<grp_undocumented>;
 def ignore_optimization_hints : Flag<["-"], "ignore_optimization_hints">,
-    HelpText<"This option is undocumented in ld64">,
-    Flags<[HelpHidden]>,
+    HelpText<"Ignore Linker Optimization Hints">,
     Group<grp_undocumented>;
 def init_offsets : Flag<["-"], "init_offsets">,
-    HelpText<"This option is undocumented in ld64">,
-    Flags<[HelpHidden]>,
+    HelpText<"Store __TEXT segment offsets of static initializers">,
     Group<grp_undocumented>;
 def keep_dwarf_unwind : Flag<["-"], "keep_dwarf_unwind">,
     HelpText<"This option is undocumented in ld64">,
@@ -1272,18 +1332,10 @@ def no_compact_unwind : Flag<["-"], "no_compact_unwind">,
     HelpText<"This option is undocumented in ld64">,
     Flags<[HelpHidden]>,
     Group<grp_undocumented>;
-def no_dtrace_dof : Flag<["-"], "no_dtrace_dof">,
-    HelpText<"This option is undocumented in ld64">,
-    Flags<[HelpHidden]>,
-    Group<grp_undocumented>;
 def no_new_main : Flag<["-"], "no_new_main">,
     HelpText<"This option is undocumented in ld64">,
     Flags<[HelpHidden]>,
     Group<grp_undocumented>;
-def objc_abi_version : Separate<["-"], "objc_abi_version">,
-    HelpText<"This option is undocumented in ld64">,
-    Flags<[HelpHidden]>,
-    Group<grp_undocumented>;
 def pause : Flag<["-"], "pause">,
     HelpText<"This option is undocumented in ld64">,
     Flags<[HelpHidden]>,
@@ -1327,3 +1379,10 @@ def new_linker : Flag<["-"], "new_linker">,
     HelpText<"This option is ignored in ld64">,
     Flags<[HelpHidden]>,
     Group<grp_ignored>;
+
+def grp_ignored_silently : OptionGroup<"ignored_silently">, HelpText<"IGNORED SILENTLY">;
+
+def objc_abi_version : Separate<["-"], "objc_abi_version">,
+    HelpText<"This option only applies to i386 in ld64">,
+    Flags<[HelpHidden]>,
+    Group<grp_ignored_silently>;
index 8d7a29c..461d4f8 100644 (file)
@@ -13,9 +13,7 @@ using namespace llvm;
 using namespace lld;
 using namespace lld::macho;
 
-uint64_t OutputSection::getSegmentOffset() const {
-  return addr - parent->addr;
-}
+uint64_t OutputSection::getSegmentOffset() const { return addr - parent->addr; }
 
 void OutputSection::assignAddressesToStartEndSymbols() {
   for (Defined *d : sectionStartSymbols)
index eb55485..5297a03 100644 (file)
@@ -16,8 +16,7 @@
 
 #include <limits>
 
-namespace lld {
-namespace macho {
+namespace lld::macho {
 
 class Defined;
 class InputSection;
@@ -58,13 +57,23 @@ public:
   // Unneeded sections are omitted entirely (header and body).
   virtual bool isNeeded() const { return true; }
 
-  virtual void finalize() {
-    // TODO investigate refactoring synthetic section finalization logic into
-    // overrides of this function.
-  }
+  // The implementations of this method can assume that it is only called right
+  // before addresses get assigned to this particular OutputSection. In
+  // particular, this means that it gets called only after addresses have been
+  // assigned to output sections that occur earlier in the output binary.
+  // Naturally, this means different sections' finalize() methods cannot execute
+  // concurrently with each other. As such, avoid using this method for
+  // operations that do not require this strict sequential guarantee.
+  //
+  // Operations that need to occur late in the linking process, but which do not
+  // need the sequential guarantee, should be named `finalizeContents()`. See
+  // e.g. LinkEditSection::finalizeContents() and
+  // CStringSection::finalizeContents().
+  virtual void finalize() {}
 
   virtual void writeTo(uint8_t *buf) const = 0;
 
+  // Handle section$start$ and section$end$ symbols.
   void assignAddressesToStartEndSymbols();
 
   StringRef name;
@@ -88,7 +97,6 @@ private:
   Kind sectionKind;
 };
 
-} // namespace macho
-} // namespace lld
+} // namespace lld::macho
 
 #endif
index 3bbaf7f..a887bc4 100644 (file)
@@ -44,6 +44,12 @@ static uint32_t maxProt(StringRef name) {
   return initProt(name);
 }
 
+static uint32_t flags(StringRef name) {
+  // If we ever implement shared cache output support, SG_READ_ONLY should not
+  // be used for dylibs that can be placed in it.
+  return name == segment_names::dataConst ? (uint32_t)SG_READ_ONLY : 0;
+}
+
 size_t OutputSegment::numNonHiddenSections() const {
   size_t count = 0;
   for (const OutputSection *osec : sections)
@@ -84,10 +90,12 @@ static int sectionOrder(OutputSection *osec) {
   // Sections are uniquely identified by their segment + section name.
   if (segname == segment_names::text) {
     return StringSwitch<int>(osec->name)
-        .Case(section_names::header, -4)
-        .Case(section_names::text, -3)
-        .Case(section_names::stubs, -2)
-        .Case(section_names::stubHelper, -1)
+        .Case(section_names::header, -6)
+        .Case(section_names::text, -5)
+        .Case(section_names::stubs, -4)
+        .Case(section_names::stubHelper, -3)
+        .Case(section_names::objcStubs, -2)
+        .Case(section_names::initOffsets, -1)
         .Case(section_names::unwindInfo, std::numeric_limits<int>::max() - 1)
         .Case(section_names::ehFrame, std::numeric_limits<int>::max())
         .Default(osec->inputOrder);
@@ -118,6 +126,7 @@ static int sectionOrder(OutputSection *osec) {
     }
   } else if (segname == segment_names::linkEdit) {
     return StringSwitch<int>(osec->name)
+        .Case(section_names::chainFixups, -11)
         .Case(section_names::rebase, -10)
         .Case(section_names::binding, -9)
         .Case(section_names::weakBinding, -8)
@@ -161,6 +170,11 @@ void macho::sortOutputSegments() {
 static DenseMap<StringRef, OutputSegment *> nameToOutputSegment;
 std::vector<OutputSegment *> macho::outputSegments;
 
+void macho::resetOutputSegments() {
+  outputSegments.clear();
+  nameToOutputSegment.clear();
+}
+
 static StringRef maybeRenameSegment(StringRef name) {
   auto newName = config->segmentRenameMap.find(name);
   if (newName != config->segmentRenameMap.end())
@@ -179,6 +193,7 @@ OutputSegment *macho::getOrCreateOutputSegment(StringRef name) {
   segRef->name = name;
   segRef->maxProt = maxProt(name);
   segRef->initProt = initProt(name);
+  segRef->flags = flags(name);
 
   outputSegments.push_back(segRef);
   return segRef;
index b3863f4..7a0c4a2 100644 (file)
@@ -17,8 +17,7 @@
 #include <limits>
 #include <vector>
 
-namespace lld {
-namespace macho {
+namespace lld::macho {
 
 namespace segment_names {
 
@@ -56,6 +55,7 @@ public:
   StringRef name;
   uint32_t maxProt = 0;
   uint32_t initProt = 0;
+  uint32_t flags = 0;
   uint8_t index;
 
   llvm::TinyPtrVector<Defined *> segmentStartSymbols;
@@ -68,10 +68,10 @@ private:
 extern std::vector<OutputSegment *> outputSegments;
 
 void sortOutputSegments();
+void resetOutputSegments();
 
 OutputSegment *getOrCreateOutputSegment(StringRef name);
 
-} // namespace macho
-} // namespace lld
+} // namespace lld::macho
 
 #endif
index 03cb697..9e5ac69 100644 (file)
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "Relocations.h"
+#include "ConcatOutputSection.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
@@ -17,37 +18,86 @@ using namespace llvm;
 using namespace lld;
 using namespace lld::macho;
 
+static_assert(sizeof(void *) != 8 || sizeof(Reloc) == 24,
+              "Try to minimize Reloc's size; we create many instances");
+
 bool macho::validateSymbolRelocation(const Symbol *sym,
                                      const InputSection *isec, const Reloc &r) {
   const RelocAttrs &relocAttrs = target->getRelocAttrs(r.type);
   bool valid = true;
-  auto message = [relocAttrs, sym, isec, &valid](const Twine &diagnostic) {
+  auto message = [&](const Twine &diagnostic) {
     valid = false;
-    return (relocAttrs.name + " relocation " + diagnostic + " for `" +
-            sym->getName() + "' in " + toString(isec))
+    return (isec->getLocation(r.offset) + ": " + relocAttrs.name +
+            " relocation " + diagnostic)
         .str();
   };
 
   if (relocAttrs.hasAttr(RelocAttrBits::TLV) != sym->isTlv())
-    error(message(Twine("requires that variable ") +
+    error(message(Twine("requires that symbol ") + sym->getName() + " " +
                   (sym->isTlv() ? "not " : "") + "be thread-local"));
 
   return valid;
 }
 
-void macho::reportRangeError(const Reloc &r, const Twine &v, uint8_t bits,
-                             int64_t min, uint64_t max) {
+// Given an offset in the output buffer, figure out which ConcatInputSection (if
+// any) maps to it. At the same time, update the offset such that it is relative
+// to the InputSection rather than to the output buffer.
+//
+// Obtaining the InputSection allows us to have better error diagnostics.
+// However, many of our relocation-handling methods do not take the InputSection
+// as a parameter. Since we are already passing the buffer offsets to our Target
+// methods, this function allows us to emit better errors without threading an
+// additional InputSection argument through the call stack.
+//
+// This is implemented as a slow linear search through OutputSegments,
+// OutputSections, and finally the InputSections themselves. However, this
+// function should be called only on error paths, so some overhead is fine.
+InputSection *macho::offsetToInputSection(uint64_t *off) {
+  for (OutputSegment *seg : outputSegments) {
+    if (*off < seg->fileOff || *off >= seg->fileOff + seg->fileSize)
+      continue;
+
+    const std::vector<OutputSection *> &sections = seg->getSections();
+    size_t osecIdx = 0;
+    for (; osecIdx < sections.size(); ++osecIdx)
+      if (*off < sections[osecIdx]->fileOff)
+        break;
+    assert(osecIdx > 0);
+    // We should be only calling this function on offsets that belong to
+    // ConcatOutputSections.
+    auto *osec = cast<ConcatOutputSection>(sections[osecIdx - 1]);
+    *off -= osec->fileOff;
+
+    size_t isecIdx = 0;
+    for (; isecIdx < osec->inputs.size(); ++isecIdx) {
+      const ConcatInputSection *isec = osec->inputs[isecIdx];
+      if (*off < isec->outSecOff)
+        break;
+    }
+    assert(isecIdx > 0);
+    ConcatInputSection *isec = osec->inputs[isecIdx - 1];
+    *off -= isec->outSecOff;
+    return isec;
+  }
+  return nullptr;
+}
+
+void macho::reportRangeError(void *loc, const Reloc &r, const Twine &v,
+                             uint8_t bits, int64_t min, uint64_t max) {
   std::string hint;
+  uint64_t off = reinterpret_cast<const uint8_t *>(loc) - in.bufferStart;
+  const InputSection *isec = offsetToInputSection(&off);
+  std::string locStr = isec ? isec->getLocation(off) : "(invalid location)";
   if (auto *sym = r.referent.dyn_cast<Symbol *>())
     hint = "; references " + toString(*sym);
-  // TODO: get location of reloc using something like LLD-ELF's getErrorPlace()
-  error("relocation " + target->getRelocAttrs(r.type).name +
+  error(locStr + ": relocation " + target->getRelocAttrs(r.type).name +
         " is out of range: " + v + " is not in [" + Twine(min) + ", " +
         Twine(max) + "]" + hint);
 }
 
-void macho::reportRangeError(SymbolDiagnostic d, const Twine &v, uint8_t bits,
-                             int64_t min, uint64_t max) {
+void macho::reportRangeError(void *loc, SymbolDiagnostic d, const Twine &v,
+                             uint8_t bits, int64_t min, uint64_t max) {
+  // FIXME: should we use `loc` somehow to provide a better error message?
   std::string hint;
   if (d.symbol)
     hint = "; references " + toString(*d.symbol);
index 91b2d00..023d25a 100644 (file)
@@ -17,8 +17,7 @@
 #include <cstddef>
 #include <cstdint>
 
-namespace lld {
-namespace macho {
+namespace lld::macho {
 LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
 
 class Symbol;
@@ -56,11 +55,18 @@ struct Reloc {
   uint8_t length = 0;
   // The offset from the start of the subsection that this relocation belongs
   // to.
-  uint64_t offset = 0;
+  uint32_t offset = 0;
   // Adding this offset to the address of the referent symbol or subsection
   // gives the destination that this relocation refers to.
   int64_t addend = 0;
   llvm::PointerUnion<Symbol *, InputSection *> referent = nullptr;
+
+  Reloc() = default;
+
+  Reloc(uint8_t type, bool pcrel, uint8_t length, uint32_t offset,
+        int64_t addend, llvm::PointerUnion<Symbol *, InputSection *> referent)
+      : type(type), pcrel(pcrel), length(length), offset(offset),
+        addend(addend), referent(referent) {}
 };
 
 bool validateSymbolRelocation(const Symbol *, const InputSection *,
@@ -70,28 +76,28 @@ bool validateSymbolRelocation(const Symbol *, const InputSection *,
  * v: The value the relocation is attempting to encode
  * bits: The number of bits actually available to encode this relocation
  */
-void reportRangeError(const Reloc &, const llvm::Twine &v, uint8_t bits,
-                      int64_t min, uint64_t max);
+void reportRangeError(void *loc, const Reloc &, const llvm::Twine &v,
+                      uint8_t bits, int64_t min, uint64_t max);
 
 struct SymbolDiagnostic {
   const Symbol *symbol;
   llvm::StringRef reason;
 };
 
-void reportRangeError(SymbolDiagnostic, const llvm::Twine &v, uint8_t bits,
-                      int64_t min, uint64_t max);
+void reportRangeError(void *loc, SymbolDiagnostic, const llvm::Twine &v,
+                      uint8_t bits, int64_t min, uint64_t max);
 
 template <typename Diagnostic>
-inline void checkInt(Diagnostic d, int64_t v, int bits) {
+inline void checkInt(void *loc, Diagnostic d, int64_t v, int bits) {
   if (v != llvm::SignExtend64(v, bits))
-    reportRangeError(d, llvm::Twine(v), bits, llvm::minIntN(bits),
+    reportRangeError(loc, d, llvm::Twine(v), bits, llvm::minIntN(bits),
                      llvm::maxIntN(bits));
 }
 
 template <typename Diagnostic>
-inline void checkUInt(Diagnostic d, uint64_t v, int bits) {
+inline void checkUInt(void *loc, Diagnostic d, uint64_t v, int bits) {
   if ((v >> bits) != 0)
-    reportRangeError(d, llvm::Twine(v), bits, 0, llvm::maxUIntN(bits));
+    reportRangeError(loc, d, llvm::Twine(v), bits, 0, llvm::maxUIntN(bits));
 }
 
 inline void writeAddress(uint8_t *loc, uint64_t addr, uint8_t length) {
@@ -107,9 +113,10 @@ inline void writeAddress(uint8_t *loc, uint64_t addr, uint8_t length) {
   }
 }
 
+InputSection *offsetToInputSection(uint64_t *);
+
 extern const RelocAttrs invalidRelocAttrs;
 
-} // namespace macho
-} // namespace lld
+} // namespace lld::Macho
 
 #endif
diff --git a/gnu/llvm/lld/MachO/SectionPriorities.cpp b/gnu/llvm/lld/MachO/SectionPriorities.cpp
new file mode 100644 (file)
index 0000000..976ea03
--- /dev/null
@@ -0,0 +1,386 @@
+//===- SectionPriorities.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// This is based on the ELF port, see ELF/CallGraphSort.cpp for the details
+/// about the algorithm.
+///
+//===----------------------------------------------------------------------===//
+
+#include "SectionPriorities.h"
+#include "Config.h"
+#include "InputFiles.h"
+#include "Symbols.h"
+#include "Target.h"
+
+#include "lld/Common/Args.h"
+#include "lld/Common/CommonLinkerContext.h"
+#include "lld/Common/ErrorHandler.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/MapVector.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/TimeProfiler.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <numeric>
+
+using namespace llvm;
+using namespace llvm::MachO;
+using namespace llvm::sys;
+using namespace lld;
+using namespace lld::macho;
+
+PriorityBuilder macho::priorityBuilder;
+
+namespace {
+
+size_t highestAvailablePriority = std::numeric_limits<size_t>::max();
+
+struct Edge {
+  int from;
+  uint64_t weight;
+};
+
+struct Cluster {
+  Cluster(int sec, size_t s) : next(sec), prev(sec), size(s) {}
+
+  double getDensity() const {
+    if (size == 0)
+      return 0;
+    return double(weight) / double(size);
+  }
+
+  int next;
+  int prev;
+  uint64_t size;
+  uint64_t weight = 0;
+  uint64_t initialWeight = 0;
+  Edge bestPred = {-1, 0};
+};
+
+class CallGraphSort {
+public:
+  CallGraphSort(const MapVector<SectionPair, uint64_t> &profile);
+
+  DenseMap<const InputSection *, size_t> run();
+
+private:
+  std::vector<Cluster> clusters;
+  std::vector<const InputSection *> sections;
+};
+// Maximum amount the combined cluster density can be worse than the original
+// cluster to consider merging.
+constexpr int MAX_DENSITY_DEGRADATION = 8;
+} // end anonymous namespace
+
+// Take the edge list in callGraphProfile, resolve symbol names to Symbols, and
+// generate a graph between InputSections with the provided weights.
+CallGraphSort::CallGraphSort(const MapVector<SectionPair, uint64_t> &profile) {
+  DenseMap<const InputSection *, int> secToCluster;
+
+  auto getOrCreateCluster = [&](const InputSection *isec) -> int {
+    auto res = secToCluster.try_emplace(isec, clusters.size());
+    if (res.second) {
+      sections.push_back(isec);
+      clusters.emplace_back(clusters.size(), isec->getSize());
+    }
+    return res.first->second;
+  };
+
+  // Create the graph
+  for (const std::pair<SectionPair, uint64_t> &c : profile) {
+    const auto fromSec = c.first.first->canonical();
+    const auto toSec = c.first.second->canonical();
+    uint64_t weight = c.second;
+    // Ignore edges between input sections belonging to different output
+    // sections.  This is done because otherwise we would end up with clusters
+    // containing input sections that can't actually be placed adjacently in the
+    // output.  This messes with the cluster size and density calculations.  We
+    // would also end up moving input sections in other output sections without
+    // moving them closer to what calls them.
+    if (fromSec->parent != toSec->parent)
+      continue;
+
+    int from = getOrCreateCluster(fromSec);
+    int to = getOrCreateCluster(toSec);
+
+    clusters[to].weight += weight;
+
+    if (from == to)
+      continue;
+
+    // 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;
+}
+
+// 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);
+  return newDensity < a.getDensity() / MAX_DENSITY_DEGRADATION;
+}
+
+// Find the leader of V's belonged cluster (represented as an equivalence
+// class). We apply union-find path-halving technique (simple to implement) in
+// the meantime as it decreases depths and the time complexity.
+static int getLeader(std::vector<int> &leaders, int v) {
+  while (leaders[v] != v) {
+    leaders[v] = leaders[leaders[v]];
+    v = leaders[v];
+  }
+  return v;
+}
+
+static void mergeClusters(std::vector<Cluster> &cs, Cluster &into, int intoIdx,
+                          Cluster &from, int fromIdx) {
+  int tail1 = into.prev, tail2 = from.prev;
+  into.prev = tail2;
+  cs[tail2].next = intoIdx;
+  from.prev = tail1;
+  cs[tail1].next = fromIdx;
+  into.size += from.size;
+  into.weight += from.weight;
+  from.size = 0;
+  from.weight = 0;
+}
+
+// Group InputSections into clusters using the Call-Chain Clustering heuristic
+// then sort the clusters by density.
+DenseMap<const InputSection *, size_t> CallGraphSort::run() {
+  const uint64_t maxClusterSize = target->getPageSize();
+
+  // Cluster indices sorted by density.
+  std::vector<int> sorted(clusters.size());
+  // For union-find.
+  std::vector<int> leaders(clusters.size());
+
+  std::iota(leaders.begin(), leaders.end(), 0);
+  std::iota(sorted.begin(), sorted.end(), 0);
+
+  llvm::stable_sort(sorted, [&](int a, int b) {
+    return clusters[a].getDensity() > clusters[b].getDensity();
+  });
+
+  for (int l : sorted) {
+    // The cluster index is the same as the index of its leader here because
+    // clusters[L] has not been merged into another cluster yet.
+    Cluster &c = clusters[l];
+
+    // Don't consider merging if the edge is unlikely.
+    if (c.bestPred.from == -1 || c.bestPred.weight * 10 <= c.initialWeight)
+      continue;
+
+    int predL = getLeader(leaders, c.bestPred.from);
+    // Already in the same cluster.
+    if (l == predL)
+      continue;
+
+    Cluster *predC = &clusters[predL];
+    if (c.size + predC->size > maxClusterSize)
+      continue;
+
+    if (isNewDensityBad(*predC, c))
+      continue;
+
+    leaders[l] = predL;
+    mergeClusters(clusters, *predC, predL, c, l);
+  }
+  // Sort remaining non-empty clusters by density.
+  sorted.clear();
+  for (int i = 0, e = (int)clusters.size(); i != e; ++i)
+    if (clusters[i].size > 0)
+      sorted.push_back(i);
+  llvm::stable_sort(sorted, [&](int a, int b) {
+    return clusters[a].getDensity() > clusters[b].getDensity();
+  });
+
+  DenseMap<const InputSection *, size_t> orderMap;
+
+  // Sections will be sorted by decreasing order. Absent sections will have
+  // priority 0 and be placed at the end of sections.
+  // NB: This is opposite from COFF/ELF to be compatible with the existing
+  // order-file code.
+  int curOrder = highestAvailablePriority;
+  for (int leader : sorted) {
+    for (int i = leader;;) {
+      orderMap[sections[i]] = curOrder--;
+      i = clusters[i].next;
+      if (i == leader)
+        break;
+    }
+  }
+  if (!config->printSymbolOrder.empty()) {
+    std::error_code ec;
+    raw_fd_ostream os(config->printSymbolOrder, ec, sys::fs::OF_None);
+    if (ec) {
+      error("cannot open " + config->printSymbolOrder + ": " + ec.message());
+      return orderMap;
+    }
+    // Print the symbols ordered by C3, in the order of decreasing curOrder
+    // Instead of sorting all the orderMap, just repeat the loops above.
+    for (int leader : sorted)
+      for (int i = leader;;) {
+        const InputSection *isec = sections[i];
+        // Search all the symbols in the file of the section
+        // and find out a Defined symbol with name that is within the
+        // section.
+        for (Symbol *sym : isec->getFile()->symbols) {
+          if (auto *d = dyn_cast_or_null<Defined>(sym)) {
+            if (d->isec == isec)
+              os << sym->getName() << "\n";
+          }
+        }
+        i = clusters[i].next;
+        if (i == leader)
+          break;
+      }
+  }
+
+  return orderMap;
+}
+
+std::optional<size_t>
+macho::PriorityBuilder::getSymbolPriority(const Defined *sym) {
+  if (sym->isAbsolute())
+    return std::nullopt;
+
+  auto it = priorities.find(sym->getName());
+  if (it == priorities.end())
+    return std::nullopt;
+  const SymbolPriorityEntry &entry = it->second;
+  const InputFile *f = sym->isec->getFile();
+  if (!f)
+    return entry.anyObjectFile;
+  // We don't use toString(InputFile *) here because it returns the full path
+  // for object files, and we only want the basename.
+  StringRef filename;
+  if (f->archiveName.empty())
+    filename = path::filename(f->getName());
+  else
+    filename = saver().save(path::filename(f->archiveName) + "(" +
+                            path::filename(f->getName()) + ")");
+  return std::max(entry.objectFiles.lookup(filename), entry.anyObjectFile);
+}
+
+void macho::PriorityBuilder::extractCallGraphProfile() {
+  TimeTraceScope timeScope("Extract call graph profile");
+  bool hasOrderFile = !priorities.empty();
+  for (const InputFile *file : inputFiles) {
+    auto *obj = dyn_cast_or_null<ObjFile>(file);
+    if (!obj)
+      continue;
+    for (const CallGraphEntry &entry : obj->callGraph) {
+      assert(entry.fromIndex < obj->symbols.size() &&
+             entry.toIndex < obj->symbols.size());
+      auto *fromSym = dyn_cast_or_null<Defined>(obj->symbols[entry.fromIndex]);
+      auto *toSym = dyn_cast_or_null<Defined>(obj->symbols[entry.toIndex]);
+      if (fromSym && toSym &&
+          (!hasOrderFile ||
+           (!getSymbolPriority(fromSym) && !getSymbolPriority(toSym))))
+        callGraphProfile[{fromSym->isec, toSym->isec}] += entry.count;
+    }
+  }
+}
+
+void macho::PriorityBuilder::parseOrderFile(StringRef path) {
+  assert(callGraphProfile.empty() &&
+         "Order file must be parsed before call graph profile is processed");
+  std::optional<MemoryBufferRef> buffer = readFile(path);
+  if (!buffer) {
+    error("Could not read order file at " + path);
+    return;
+  }
+
+  MemoryBufferRef mbref = *buffer;
+  for (StringRef line : args::getLines(mbref)) {
+    StringRef objectFile, symbol;
+    line = line.take_until([](char c) { return c == '#'; }); // ignore comments
+    line = line.ltrim();
+
+    CPUType cpuType = StringSwitch<CPUType>(line)
+                          .StartsWith("i386:", CPU_TYPE_I386)
+                          .StartsWith("x86_64:", CPU_TYPE_X86_64)
+                          .StartsWith("arm:", CPU_TYPE_ARM)
+                          .StartsWith("arm64:", CPU_TYPE_ARM64)
+                          .StartsWith("ppc:", CPU_TYPE_POWERPC)
+                          .StartsWith("ppc64:", CPU_TYPE_POWERPC64)
+                          .Default(CPU_TYPE_ANY);
+
+    if (cpuType != CPU_TYPE_ANY && cpuType != target->cpuType)
+      continue;
+
+    // Drop the CPU type as well as the colon
+    if (cpuType != CPU_TYPE_ANY)
+      line = line.drop_until([](char c) { return c == ':'; }).drop_front();
+
+    constexpr std::array<StringRef, 2> fileEnds = {".o:", ".o):"};
+    for (StringRef fileEnd : fileEnds) {
+      size_t pos = line.find(fileEnd);
+      if (pos != StringRef::npos) {
+        // Split the string around the colon
+        objectFile = line.take_front(pos + fileEnd.size() - 1);
+        line = line.drop_front(pos + fileEnd.size());
+        break;
+      }
+    }
+    symbol = line.trim();
+
+    if (!symbol.empty()) {
+      SymbolPriorityEntry &entry = priorities[symbol];
+      if (!objectFile.empty())
+        entry.objectFiles.insert(
+            std::make_pair(objectFile, highestAvailablePriority));
+      else
+        entry.anyObjectFile =
+            std::max(entry.anyObjectFile, highestAvailablePriority);
+    }
+
+    --highestAvailablePriority;
+  }
+}
+
+DenseMap<const InputSection *, size_t>
+macho::PriorityBuilder::buildInputSectionPriorities() {
+  DenseMap<const InputSection *, size_t> sectionPriorities;
+  if (config->callGraphProfileSort) {
+    // Sort sections by the profile data provided by __LLVM,__cg_profile
+    // sections.
+    //
+    // This first builds a call graph based on the profile data then merges
+    // sections according to the C³ heuristic. All clusters are then sorted by a
+    // density metric to further improve locality.
+    TimeTraceScope timeScope("Call graph profile sort");
+    sectionPriorities = CallGraphSort(callGraphProfile).run();
+  }
+
+  if (priorities.empty())
+    return sectionPriorities;
+
+  auto addSym = [&](const Defined *sym) {
+    std::optional<size_t> symbolPriority = getSymbolPriority(sym);
+    if (!symbolPriority)
+      return;
+    size_t &priority = sectionPriorities[sym->isec];
+    priority = std::max(priority, *symbolPriority);
+  };
+
+  // TODO: Make sure this handles weak symbols correctly.
+  for (const InputFile *file : inputFiles) {
+    if (isa<ObjFile>(file))
+      for (Symbol *sym : file->symbols)
+        if (auto *d = dyn_cast_or_null<Defined>(sym))
+          addSym(d);
+  }
+
+  return sectionPriorities;
+}
diff --git a/gnu/llvm/lld/MachO/SectionPriorities.h b/gnu/llvm/lld/MachO/SectionPriorities.h
new file mode 100644 (file)
index 0000000..9906ea4
--- /dev/null
@@ -0,0 +1,80 @@
+//===- SectionPriorities.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_MACHO_SECTION_PRIORITIES_H
+#define LLD_MACHO_SECTION_PRIORITIES_H
+
+#include "InputSection.h"
+#include "llvm/ADT/DenseMap.h"
+
+namespace lld::macho {
+
+using SectionPair = std::pair<const InputSection *, const InputSection *>;
+
+class PriorityBuilder {
+public:
+  // Reads every input section's call graph profile, and combines them into
+  // callGraphProfile. If an order file is present, any edges where one or both
+  // of the vertices are specified in the order file are discarded.
+  void extractCallGraphProfile();
+
+  // Reads the order file at `path` into config->priorities.
+  //
+  // An order file has one entry per line, in the following format:
+  //
+  //   <cpu>:<object file>:<symbol name>
+  //
+  // <cpu> and <object file> are optional. If not specified, then that entry
+  // matches any symbol of that name. Parsing this format is not quite
+  // straightforward because the symbol name itself can contain colons, so when
+  // encountering a colon, we consider the preceding characters to decide if it
+  // can be a valid CPU type or file path.
+  //
+  // If a symbol is matched by multiple entries, then it takes the
+  // lowest-ordered entry (the one nearest to the front of the list.)
+  //
+  // The file can also have line comments that start with '#'.
+  void parseOrderFile(StringRef path);
+
+  // Returns layout priorities for some or all input sections. Sections are laid
+  // out in decreasing order; that is, a higher priority section will be closer
+  // to the beginning of its output section.
+  //
+  // If either an order file or a call graph profile are present, this is used
+  // as the source of priorities. If both are present, the order file takes
+  // precedence, but the call graph profile is still used for symbols that don't
+  // appear in the order file. If neither is present, an empty map is returned.
+  //
+  // Each section gets assigned the priority of the highest-priority symbol it
+  // contains.
+  llvm::DenseMap<const InputSection *, size_t> buildInputSectionPriorities();
+
+private:
+  // The symbol with the highest priority should be ordered first in the output
+  // section (modulo input section contiguity constraints). Using priority
+  // (highest first) instead of order (lowest first) has the convenient property
+  // that the default-constructed zero priority -- for symbols/sections without
+  // a user-defined order -- naturally ends up putting them at the end of the
+  // output.
+  struct SymbolPriorityEntry {
+    // The priority given to a matching symbol, regardless of which object file
+    // it originated from.
+    size_t anyObjectFile = 0;
+    // The priority given to a matching symbol from a particular object file.
+    llvm::DenseMap<llvm::StringRef, size_t> objectFiles;
+  };
+
+  std::optional<size_t> getSymbolPriority(const Defined *sym);
+  llvm::DenseMap<llvm::StringRef, SymbolPriorityEntry> priorities;
+  llvm::MapVector<SectionPair, uint64_t> callGraphProfile;
+};
+
+extern PriorityBuilder priorityBuilder;
+} // namespace lld::macho
+
+#endif
index c5808a8..0363123 100644 (file)
 #include "ConcatOutputSection.h"
 #include "Config.h"
 #include "InputFiles.h"
+#include "InputSection.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "lld/Common/ErrorHandler.h"
 #include "lld/Common/Memory.h"
+#include "llvm/Demangle/Demangle.h"
 
 using namespace llvm;
 using namespace lld;
@@ -44,72 +46,113 @@ std::pair<Symbol *, bool> SymbolTable::insert(StringRef name,
   return {sym, p.second};
 }
 
+namespace {
+struct DuplicateSymbolDiag {
+  // Pair containing source location and source file
+  const std::pair<std::string, std::string> src1;
+  const std::pair<std::string, std::string> src2;
+  const Symbol *sym;
+
+  DuplicateSymbolDiag(const std::pair<std::string, std::string> src1,
+                      const std::pair<std::string, std::string> src2,
+                      const Symbol *sym)
+      : src1(src1), src2(src2), sym(sym) {}
+};
+SmallVector<DuplicateSymbolDiag> dupSymDiags;
+} // namespace
+
 Defined *SymbolTable::addDefined(StringRef name, InputFile *file,
                                  InputSection *isec, uint64_t value,
                                  uint64_t size, bool isWeakDef,
                                  bool isPrivateExtern, bool isThumb,
-                                 bool isReferencedDynamically,
-                                 bool noDeadStrip) {
-  Symbol *s;
-  bool wasInserted;
+                                 bool isReferencedDynamically, bool noDeadStrip,
+                                 bool isWeakDefCanBeHidden) {
   bool overridesWeakDef = false;
-  std::tie(s, wasInserted) = insert(name, file);
+  auto [s, wasInserted] = insert(name, file);
 
-  assert(!isWeakDef || (isa<BitcodeFile>(file) && !isec) ||
-         (isa<ObjFile>(file) && file == isec->getFile()));
+  assert(!file || !isa<BitcodeFile>(file) || !isec);
 
   if (!wasInserted) {
     if (auto *defined = dyn_cast<Defined>(s)) {
       if (isWeakDef) {
+        // See further comment in createDefined() in InputFiles.cpp
         if (defined->isWeakDef()) {
-          // Both old and new symbol weak (e.g. inline function in two TUs):
-          // If one of them isn't private extern, the merged symbol isn't.
           defined->privateExtern &= isPrivateExtern;
+          defined->weakDefCanBeHidden &= isWeakDefCanBeHidden;
           defined->referencedDynamically |= isReferencedDynamically;
           defined->noDeadStrip |= noDeadStrip;
-
-          // FIXME: Handle this for bitcode files.
-          // FIXME: We currently only do this if both symbols are weak.
-          //        We could do this if either is weak (but getting the
-          //        case where !isWeakDef && defined->isWeakDef() right
-          //        requires some care and testing).
-          if (auto concatIsec = dyn_cast_or_null<ConcatInputSection>(isec))
-            concatIsec->wasCoalesced = true;
         }
-
+        // FIXME: Handle this for bitcode files.
+        if (auto concatIsec = dyn_cast_or_null<ConcatInputSection>(isec))
+          concatIsec->wasCoalesced = true;
         return defined;
       }
-      if (!defined->isWeakDef())
-        error("duplicate symbol: " + name + "\n>>> defined in " +
-              toString(defined->getFile()) + "\n>>> defined in " +
-              toString(file));
+
+      if (defined->isWeakDef()) {
+        // FIXME: Handle this for bitcode files.
+        if (auto concatIsec =
+                dyn_cast_or_null<ConcatInputSection>(defined->isec)) {
+          concatIsec->wasCoalesced = true;
+          concatIsec->symbols.erase(llvm::find(concatIsec->symbols, defined));
+        }
+      } else {
+        std::string srcLoc1 = defined->getSourceLocation();
+        std::string srcLoc2 = isec ? isec->getSourceLocation(value) : "";
+        std::string srcFile1 = toString(defined->getFile());
+        std::string srcFile2 = toString(file);
+
+        dupSymDiags.push_back({make_pair(srcLoc1, srcFile1),
+                               make_pair(srcLoc2, srcFile2), defined});
+      }
+
     } else if (auto *dysym = dyn_cast<DylibSymbol>(s)) {
       overridesWeakDef = !isWeakDef && dysym->isWeakDef();
       dysym->unreference();
+    } else if (auto *undef = dyn_cast<Undefined>(s)) {
+      // Preserve the original bitcode file name (instead of using the object
+      // file name).
+      if (undef->wasBitcodeSymbol)
+        file = undef->getFile();
     }
     // Defined symbols take priority over other types of symbols, so in case
     // of a name conflict, we fall through to the replaceSymbol() call below.
   }
 
+  // With -flat_namespace, all extern symbols in dylibs are interposable.
+  // FIXME: Add support for `-interposable` (PR53680).
+  bool interposable = config->namespaceKind == NamespaceKind::flat &&
+                      config->outputType != MachO::MH_EXECUTE &&
+                      !isPrivateExtern;
   Defined *defined = replaceSymbol<Defined>(
       s, name, file, isec, value, size, isWeakDef, /*isExternal=*/true,
-      isPrivateExtern, isThumb, isReferencedDynamically, noDeadStrip);
-  defined->overridesWeakDef = overridesWeakDef;
+      isPrivateExtern, /*includeInSymtab=*/true, isThumb,
+      isReferencedDynamically, noDeadStrip, overridesWeakDef,
+      isWeakDefCanBeHidden, interposable);
   return defined;
 }
 
+Defined *SymbolTable::aliasDefined(Defined *src, StringRef target,
+                                   InputFile *newFile, bool makePrivateExtern) {
+  bool isPrivateExtern = makePrivateExtern || src->privateExtern;
+  return addDefined(target, newFile, src->isec, src->value, src->size,
+                    src->isWeakDef(), isPrivateExtern, src->thumb,
+                    src->referencedDynamically, src->noDeadStrip,
+                    src->weakDefCanBeHidden);
+}
+
 Symbol *SymbolTable::addUndefined(StringRef name, InputFile *file,
                                   bool isWeakRef) {
-  Symbol *s;
-  bool wasInserted;
-  std::tie(s, wasInserted) = insert(name, file);
+  auto [s, wasInserted] = insert(name, file);
 
   RefState refState = isWeakRef ? RefState::Weak : RefState::Strong;
 
   if (wasInserted)
-    replaceSymbol<Undefined>(s, name, file, refState);
-  else if (auto *lazy = dyn_cast<LazySymbol>(s))
+    replaceSymbol<Undefined>(s, name, file, refState,
+                             /*wasBitcodeSymbol=*/false);
+  else if (auto *lazy = dyn_cast<LazyArchive>(s))
     lazy->fetchArchiveMember();
+  else if (isa<LazyObject>(s))
+    extract(*s->getFile(), s->getName());
   else if (auto *dynsym = dyn_cast<DylibSymbol>(s))
     dynsym->reference(refState);
   else if (auto *undefined = dyn_cast<Undefined>(s))
@@ -119,9 +162,7 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *file,
 
 Symbol *SymbolTable::addCommon(StringRef name, InputFile *file, uint64_t size,
                                uint32_t align, bool isPrivateExtern) {
-  Symbol *s;
-  bool wasInserted;
-  std::tie(s, wasInserted) = insert(name, file);
+  auto [s, wasInserted] = insert(name, file);
 
   if (!wasInserted) {
     if (auto *common = dyn_cast<CommonSymbol>(s)) {
@@ -140,9 +181,7 @@ Symbol *SymbolTable::addCommon(StringRef name, InputFile *file, uint64_t size,
 
 Symbol *SymbolTable::addDylib(StringRef name, DylibFile *file, bool isWeakDef,
                               bool isTlv) {
-  Symbol *s;
-  bool wasInserted;
-  std::tie(s, wasInserted) = insert(name, file);
+  auto [s, wasInserted] = insert(name, file);
 
   RefState refState = RefState::Unreferenced;
   if (!wasInserted) {
@@ -173,16 +212,40 @@ Symbol *SymbolTable::addDynamicLookup(StringRef name) {
   return addDylib(name, /*file=*/nullptr, /*isWeakDef=*/false, /*isTlv=*/false);
 }
 
-Symbol *SymbolTable::addLazy(StringRef name, ArchiveFile *file,
-                             const object::Archive::Symbol &sym) {
-  Symbol *s;
-  bool wasInserted;
-  std::tie(s, wasInserted) = insert(name, file);
+Symbol *SymbolTable::addLazyArchive(StringRef name, ArchiveFile *file,
+                                    const object::Archive::Symbol &sym) {
+  auto [s, wasInserted] = insert(name, file);
 
-  if (wasInserted)
-    replaceSymbol<LazySymbol>(s, file, sym);
-  else if (isa<Undefined>(s) || (isa<DylibSymbol>(s) && s->isWeakDef()))
+  if (wasInserted) {
+    replaceSymbol<LazyArchive>(s, file, sym);
+  } else if (isa<Undefined>(s)) {
     file->fetch(sym);
+  } else if (auto *dysym = dyn_cast<DylibSymbol>(s)) {
+    if (dysym->isWeakDef()) {
+      if (dysym->getRefState() != RefState::Unreferenced)
+        file->fetch(sym);
+      else
+        replaceSymbol<LazyArchive>(s, file, sym);
+    }
+  }
+  return s;
+}
+
+Symbol *SymbolTable::addLazyObject(StringRef name, InputFile &file) {
+  auto [s, wasInserted] = insert(name, &file);
+
+  if (wasInserted) {
+    replaceSymbol<LazyObject>(s, file, name);
+  } else if (isa<Undefined>(s)) {
+    extract(file, name);
+  } else if (auto *dysym = dyn_cast<DylibSymbol>(s)) {
+    if (dysym->isWeakDef()) {
+      if (dysym->getRefState() != RefState::Unreferenced)
+        extract(file, name);
+      else
+        replaceSymbol<LazyObject>(s, file, name);
+    }
+  }
   return s;
 }
 
@@ -190,10 +253,12 @@ Defined *SymbolTable::addSynthetic(StringRef name, InputSection *isec,
                                    uint64_t value, bool isPrivateExtern,
                                    bool includeInSymtab,
                                    bool referencedDynamically) {
-  Defined *s = addDefined(name, nullptr, isec, value, /*size=*/0,
-                          /*isWeakDef=*/false, isPrivateExtern,
-                          /*isThumb=*/false, referencedDynamically,
-                          /*noDeadStrip=*/false);
+  assert(!isec || !isec->getFile()); // See makeSyntheticInputSection().
+  Defined *s =
+      addDefined(name, /*file=*/nullptr, isec, value, /*size=*/0,
+                 /*isWeakDef=*/false, isPrivateExtern, /*isThumb=*/false,
+                 referencedDynamically, /*noDeadStrip=*/false,
+                 /*isWeakDefCanBeHidden=*/false);
   s->includeInSymtab = includeInSymtab;
   return s;
 }
@@ -211,8 +276,7 @@ static Defined *createBoundarySymbol(const Undefined &sym) {
 
 static void handleSectionBoundarySymbol(const Undefined &sym, StringRef segSect,
                                         Boundary which) {
-  StringRef segName, sectName;
-  std::tie(segName, sectName) = segSect.split('$');
+  auto [segName, sectName] = segSect.split('$');
 
   // Attach the symbol to any InputSection that will end up in the right
   // OutputSection -- it doesn't matter which one we pick.
@@ -230,7 +294,7 @@ static void handleSectionBoundarySymbol(const Undefined &sym, StringRef segSect,
     }
 
   if (!osec) {
-    ConcatInputSection *isec = make<ConcatInputSection>(segName, sectName);
+    ConcatInputSection *isec = makeSyntheticInputSection(segName, sectName);
 
     // This runs after markLive() and is only called for Undefineds that are
     // live. Marking the isec live ensures an OutputSection is created that the
@@ -259,50 +323,289 @@ static void handleSegmentBoundarySymbol(const Undefined &sym, StringRef segName,
     seg->segmentEndSymbols.push_back(createBoundarySymbol(sym));
 }
 
-void lld::macho::treatUndefinedSymbol(const Undefined &sym, StringRef source) {
+// Try to find a definition for an undefined symbol.
+// Returns true if a definition was found and no diagnostics are needed.
+static bool recoverFromUndefinedSymbol(const Undefined &sym) {
   // Handle start/end symbols.
   StringRef name = sym.getName();
-  if (name.consume_front("section$start$"))
-    return handleSectionBoundarySymbol(sym, name, Boundary::Start);
-  if (name.consume_front("section$end$"))
-    return handleSectionBoundarySymbol(sym, name, Boundary::End);
-  if (name.consume_front("segment$start$"))
-    return handleSegmentBoundarySymbol(sym, name, Boundary::Start);
-  if (name.consume_front("segment$end$"))
-    return handleSegmentBoundarySymbol(sym, name, Boundary::End);
+  if (name.consume_front("section$start$")) {
+    handleSectionBoundarySymbol(sym, name, Boundary::Start);
+    return true;
+  }
+  if (name.consume_front("section$end$")) {
+    handleSectionBoundarySymbol(sym, name, Boundary::End);
+    return true;
+  }
+  if (name.consume_front("segment$start$")) {
+    handleSegmentBoundarySymbol(sym, name, Boundary::Start);
+    return true;
+  }
+  if (name.consume_front("segment$end$")) {
+    handleSegmentBoundarySymbol(sym, name, Boundary::End);
+    return true;
+  }
+
+  // Leave dtrace symbols, since we will handle them when we do the relocation
+  if (name.startswith("___dtrace_"))
+    return true;
 
   // Handle -U.
   if (config->explicitDynamicLookups.count(sym.getName())) {
     symtab->addDynamicLookup(sym.getName());
-    return;
+    return true;
   }
 
   // Handle -undefined.
-  auto message = [source, &sym]() {
-    std::string message = "undefined symbol";
-    if (config->archMultiple)
-      message += (" for arch " + getArchitectureName(config->arch())).str();
-    message += ": " + toString(sym);
-    if (!source.empty())
-      message += "\n>>> referenced by " + source.str();
-    else
-      message += "\n>>> referenced by " + toString(sym.getFile());
-    return message;
-  };
-  switch (config->undefinedSymbolTreatment) {
-  case UndefinedSymbolTreatment::error:
-    error(message());
-    break;
-  case UndefinedSymbolTreatment::warning:
-    warn(message());
-    LLVM_FALLTHROUGH;
-  case UndefinedSymbolTreatment::dynamic_lookup:
-  case UndefinedSymbolTreatment::suppress:
+  if (config->undefinedSymbolTreatment ==
+          UndefinedSymbolTreatment::dynamic_lookup ||
+      config->undefinedSymbolTreatment == UndefinedSymbolTreatment::suppress) {
     symtab->addDynamicLookup(sym.getName());
-    break;
-  case UndefinedSymbolTreatment::unknown:
-    llvm_unreachable("unknown -undefined TREATMENT");
+    return true;
   }
+
+  // We do not return true here, as we still need to print diagnostics.
+  if (config->undefinedSymbolTreatment == UndefinedSymbolTreatment::warning)
+    symtab->addDynamicLookup(sym.getName());
+
+  return false;
+}
+
+namespace {
+struct UndefinedDiag {
+  struct SectionAndOffset {
+    const InputSection *isec;
+    uint64_t offset;
+  };
+
+  std::vector<SectionAndOffset> codeReferences;
+  std::vector<std::string> otherReferences;
+};
+
+MapVector<const Undefined *, UndefinedDiag> undefs;
+}
+
+void macho::reportPendingDuplicateSymbols() {
+  for (const auto &duplicate : dupSymDiags) {
+    if (!config->deadStripDuplicates || duplicate.sym->isLive()) {
+      std::string message =
+          "duplicate symbol: " + toString(*duplicate.sym) + "\n>>> defined in ";
+      if (!duplicate.src1.first.empty())
+        message += duplicate.src1.first + "\n>>>            ";
+      message += duplicate.src1.second + "\n>>> defined in ";
+      if (!duplicate.src2.first.empty())
+        message += duplicate.src2.first + "\n>>>            ";
+      error(message + duplicate.src2.second);
+    }
+  }
+}
+
+// Check whether the definition name def is a mangled function name that matches
+// the reference name ref.
+static bool canSuggestExternCForCXX(StringRef ref, StringRef def) {
+  llvm::ItaniumPartialDemangler d;
+  std::string name = def.str();
+  if (d.partialDemangle(name.c_str()))
+    return false;
+  char *buf = d.getFunctionName(nullptr, nullptr);
+  if (!buf)
+    return false;
+  bool ret = ref == buf;
+  free(buf);
+  return ret;
+}
+
+// Suggest an alternative spelling of an "undefined symbol" diagnostic. Returns
+// the suggested symbol, which is either in the symbol table, or in the same
+// file of sym.
+static const Symbol *getAlternativeSpelling(const Undefined &sym,
+                                            std::string &pre_hint,
+                                            std::string &post_hint) {
+  DenseMap<StringRef, const Symbol *> map;
+  if (sym.getFile() && sym.getFile()->kind() == InputFile::ObjKind) {
+    // Build a map of local defined symbols.
+    for (const Symbol *s : sym.getFile()->symbols)
+      if (auto *defined = dyn_cast_or_null<Defined>(s))
+        if (!defined->isExternal())
+          map.try_emplace(s->getName(), s);
+  }
+
+  auto suggest = [&](StringRef newName) -> const Symbol * {
+    // If defined locally.
+    if (const Symbol *s = map.lookup(newName))
+      return s;
+
+    // If in the symbol table and not undefined.
+    if (const Symbol *s = symtab->find(newName))
+      if (dyn_cast<Undefined>(s) == nullptr)
+        return s;
+
+    return nullptr;
+  };
+
+  // This loop enumerates all strings of Levenshtein distance 1 as typo
+  // correction candidates and suggests the one that exists as a non-undefined
+  // symbol.
+  StringRef name = sym.getName();
+  for (size_t i = 0, e = name.size(); i != e + 1; ++i) {
+    // Insert a character before name[i].
+    std::string newName = (name.substr(0, i) + "0" + name.substr(i)).str();
+    for (char c = '0'; c <= 'z'; ++c) {
+      newName[i] = c;
+      if (const Symbol *s = suggest(newName))
+        return s;
+    }
+    if (i == e)
+      break;
+
+    // Substitute name[i].
+    newName = std::string(name);
+    for (char c = '0'; c <= 'z'; ++c) {
+      newName[i] = c;
+      if (const Symbol *s = suggest(newName))
+        return s;
+    }
+
+    // Transpose name[i] and name[i+1]. This is of edit distance 2 but it is
+    // common.
+    if (i + 1 < e) {
+      newName[i] = name[i + 1];
+      newName[i + 1] = name[i];
+      if (const Symbol *s = suggest(newName))
+        return s;
+    }
+
+    // Delete name[i].
+    newName = (name.substr(0, i) + name.substr(i + 1)).str();
+    if (const Symbol *s = suggest(newName))
+      return s;
+  }
+
+  // Case mismatch, e.g. Foo vs FOO.
+  for (auto &it : map)
+    if (name.equals_insensitive(it.first))
+      return it.second;
+  for (Symbol *sym : symtab->getSymbols())
+    if (dyn_cast<Undefined>(sym) == nullptr &&
+        name.equals_insensitive(sym->getName()))
+      return sym;
+
+  // The reference may be a mangled name while the definition is not. Suggest a
+  // missing extern "C".
+  if (name.startswith("__Z")) {
+    std::string buf = name.str();
+    llvm::ItaniumPartialDemangler d;
+    if (!d.partialDemangle(buf.c_str()))
+      if (char *buf = d.getFunctionName(nullptr, nullptr)) {
+        const Symbol *s = suggest((Twine("_") + buf).str());
+        free(buf);
+        if (s) {
+          pre_hint = ": extern \"C\" ";
+          return s;
+        }
+      }
+  } else {
+    StringRef name_without_underscore = name;
+    name_without_underscore.consume_front("_");
+    const Symbol *s = nullptr;
+    for (auto &it : map)
+      if (canSuggestExternCForCXX(name_without_underscore, it.first)) {
+        s = it.second;
+        break;
+      }
+    if (!s)
+      for (Symbol *sym : symtab->getSymbols())
+        if (canSuggestExternCForCXX(name_without_underscore, sym->getName())) {
+          s = sym;
+          break;
+        }
+    if (s) {
+      pre_hint = " to declare ";
+      post_hint = " as extern \"C\"?";
+      return s;
+    }
+  }
+
+  return nullptr;
+}
+
+static void reportUndefinedSymbol(const Undefined &sym,
+                                  const UndefinedDiag &locations,
+                                  bool correctSpelling) {
+  std::string message = "undefined symbol";
+  if (config->archMultiple)
+    message += (" for arch " + getArchitectureName(config->arch())).str();
+  message += ": " + toString(sym);
+
+  const size_t maxUndefinedReferences = 3;
+  size_t i = 0;
+  for (const std::string &loc : locations.otherReferences) {
+    if (i >= maxUndefinedReferences)
+      break;
+    message += "\n>>> referenced by " + loc;
+    ++i;
+  }
+
+  for (const UndefinedDiag::SectionAndOffset &loc : locations.codeReferences) {
+    if (i >= maxUndefinedReferences)
+      break;
+    message += "\n>>> referenced by ";
+    std::string src = loc.isec->getSourceLocation(loc.offset);
+    if (!src.empty())
+      message += src + "\n>>>               ";
+    message += loc.isec->getLocation(loc.offset);
+    ++i;
+  }
+
+  size_t totalReferences =
+      locations.otherReferences.size() + locations.codeReferences.size();
+  if (totalReferences > i)
+    message +=
+        ("\n>>> referenced " + Twine(totalReferences - i) + " more times")
+            .str();
+
+  if (correctSpelling) {
+    std::string pre_hint = ": ", post_hint;
+    if (const Symbol *corrected =
+            getAlternativeSpelling(sym, pre_hint, post_hint)) {
+      message +=
+          "\n>>> did you mean" + pre_hint + toString(*corrected) + post_hint;
+      if (corrected->getFile())
+        message += "\n>>> defined in: " + toString(corrected->getFile());
+    }
+  }
+
+  if (config->undefinedSymbolTreatment == UndefinedSymbolTreatment::error)
+    error(message);
+  else if (config->undefinedSymbolTreatment ==
+           UndefinedSymbolTreatment::warning)
+    warn(message);
+  else
+    assert(false && "diagnostics make sense for -undefined error|warning only");
+}
+
+void macho::reportPendingUndefinedSymbols() {
+  // Enable spell corrector for the first 2 diagnostics.
+  for (const auto &[i, undef] : llvm::enumerate(undefs))
+    reportUndefinedSymbol(*undef.first, undef.second, i < 2);
+
+  // This function is called multiple times during execution. Clear the printed
+  // diagnostics to avoid printing the same things again the next time.
+  undefs.clear();
+}
+
+void macho::treatUndefinedSymbol(const Undefined &sym, StringRef source) {
+  if (recoverFromUndefinedSymbol(sym))
+    return;
+
+  undefs[&sym].otherReferences.push_back(source.str());
+}
+
+void macho::treatUndefinedSymbol(const Undefined &sym, const InputSection *isec,
+                                 uint64_t offset) {
+  if (recoverFromUndefinedSymbol(sym))
+    return;
+
+  undefs[&sym].codeReferences.push_back({isec, offset});
 }
 
-SymbolTable *macho::symtab;
+std::unique_ptr<SymbolTable> macho::symtab;
index 17f1ecb..ea02367 100644 (file)
@@ -16,8 +16,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/Object/Archive.h"
 
-namespace lld {
-namespace macho {
+namespace lld::macho {
 
 class ArchiveFile;
 class DylibFile;
@@ -40,7 +39,11 @@ public:
   Defined *addDefined(StringRef name, InputFile *, InputSection *,
                       uint64_t value, uint64_t size, bool isWeakDef,
                       bool isPrivateExtern, bool isThumb,
-                      bool isReferencedDynamically, bool noDeadStrip);
+                      bool isReferencedDynamically, bool noDeadStrip,
+                      bool isWeakDefCanBeHidden);
+
+  Defined *aliasDefined(Defined *src, StringRef target, InputFile *newFile,
+                        bool makePrivateExtern = false);
 
   Symbol *addUndefined(StringRef name, InputFile *, bool isWeakRef);
 
@@ -50,8 +53,9 @@ public:
   Symbol *addDylib(StringRef name, DylibFile *file, bool isWeakDef, bool isTlv);
   Symbol *addDynamicLookup(StringRef name);
 
-  Symbol *addLazy(StringRef name, ArchiveFile *file,
-                  const llvm::object::Archive::Symbol &sym);
+  Symbol *addLazyArchive(StringRef name, ArchiveFile *file,
+                         const llvm::object::Archive::Symbol &sym);
+  Symbol *addLazyObject(StringRef name, InputFile &file);
 
   Defined *addSynthetic(StringRef name, InputSection *, uint64_t value,
                         bool isPrivateExtern, bool includeInSymtab,
@@ -67,11 +71,16 @@ private:
   std::vector<Symbol *> symVector;
 };
 
-void treatUndefinedSymbol(const Undefined &, StringRef source = "");
+void reportPendingUndefinedSymbols();
+void reportPendingDuplicateSymbols();
+
+// Call reportPendingUndefinedSymbols() to emit diagnostics.
+void treatUndefinedSymbol(const Undefined &, StringRef source);
+void treatUndefinedSymbol(const Undefined &, const InputSection *,
+                          uint64_t offset);
 
-extern SymbolTable *symtab;
+extern std::unique_ptr<SymbolTable> symtab;
 
-} // namespace macho
-} // namespace lld
+} // namespace lld::macho
 
 #endif
index 47f30d4..cb3b271 100644 (file)
@@ -9,48 +9,80 @@
 #include "Symbols.h"
 #include "InputFiles.h"
 #include "SyntheticSections.h"
+#include "llvm/Demangle/Demangle.h"
 
 using namespace llvm;
 using namespace lld;
 using namespace lld::macho;
 
-// Returns a symbol for an error message.
-static std::string demangle(StringRef symName) {
-  if (config->demangle)
-    return demangleItanium(symName);
-  return std::string(symName);
+static_assert(sizeof(void *) != 8 || sizeof(Symbol) == 56,
+              "Try to minimize Symbol's size; we create many instances");
+
+// The Microsoft ABI doesn't support using parent class tail padding for child
+// members, hence the _MSC_VER check.
+#if !defined(_MSC_VER)
+static_assert(sizeof(void *) != 8 || sizeof(Defined) == 88,
+              "Try to minimize Defined's size; we create many instances");
+#endif
+
+static_assert(sizeof(SymbolUnion) == sizeof(Defined),
+              "Defined should be the largest Symbol kind");
+
+// Returns a symbol name for an error message.
+static std::string maybeDemangleSymbol(StringRef symName) {
+  if (config->demangle) {
+    symName.consume_front("_");
+    return demangle(symName.str());
+  }
+  return symName.str();
 }
 
-std::string lld::toString(const Symbol &sym) { return demangle(sym.getName()); }
+std::string lld::toString(const Symbol &sym) {
+  return maybeDemangleSymbol(sym.getName());
+}
 
 std::string lld::toMachOString(const object::Archive::Symbol &b) {
-  return demangle(b.getName());
+  return maybeDemangleSymbol(b.getName());
 }
 
 uint64_t Symbol::getStubVA() const { return in.stubs->getVA(stubsIndex); }
+uint64_t Symbol::getLazyPtrVA() const {
+  return in.lazyPointers->getVA(stubsIndex);
+}
 uint64_t Symbol::getGotVA() const { return in.got->getVA(gotIndex); }
 uint64_t Symbol::getTlvVA() const { return in.tlvPointers->getVA(gotIndex); }
 
-bool Symbol::isLive() const {
-  if (isa<DylibSymbol>(this) || isa<Undefined>(this))
-    return used;
-
-  if (auto *d = dyn_cast<Defined>(this)) {
-    // Non-absolute symbols might be alive because their section is
-    // no_dead_strip or live_support. In that case, the section will know
-    // that it's live but `used` might be false. Non-absolute symbols always
-    // have to use the section's `live` bit as source of truth.
-    if (d->isAbsolute())
-      return used;
-    return d->isec->canonical()->isLive(d->value);
+Defined::Defined(StringRefZ name, InputFile *file, InputSection *isec,
+                 uint64_t value, uint64_t size, bool isWeakDef, bool isExternal,
+                 bool isPrivateExtern, bool includeInSymtab, bool isThumb,
+                 bool isReferencedDynamically, bool noDeadStrip,
+                 bool canOverrideWeakDef, bool isWeakDefCanBeHidden,
+                 bool interposable)
+    : Symbol(DefinedKind, name, file), overridesWeakDef(canOverrideWeakDef),
+      privateExtern(isPrivateExtern), includeInSymtab(includeInSymtab),
+      wasIdenticalCodeFolded(false), thumb(isThumb),
+      referencedDynamically(isReferencedDynamically), noDeadStrip(noDeadStrip),
+      interposable(interposable), weakDefCanBeHidden(isWeakDefCanBeHidden),
+      weakDef(isWeakDef), external(isExternal), isec(isec), value(value),
+      size(size) {
+  if (isec) {
+    isec->symbols.push_back(this);
+    // Maintain sorted order.
+    for (auto it = isec->symbols.rbegin(), rend = isec->symbols.rend();
+         it != rend; ++it) {
+      auto next = std::next(it);
+      if (next == rend)
+        break;
+      if ((*it)->value < (*next)->value)
+        std::swap(*next, *it);
+      else
+        break;
+    }
   }
+}
 
-  assert(!isa<CommonSymbol>(this) &&
-         "replaceCommonSymbols() runs before dead code stripping, and isLive() "
-         "should only be called after dead code stripping");
-
-  // Assume any other kind of symbol is live.
-  return true;
+bool Defined::isTlv() const {
+  return !isAbsolute() && isThreadLocalVariables(isec->getFlags());
 }
 
 uint64_t Defined::getVA() const {
@@ -59,7 +91,7 @@ uint64_t Defined::getVA() const {
   if (isAbsolute())
     return value;
 
-  if (!isec->canonical()->isFinal) {
+  if (!isec->isFinal) {
     // A target arch that does not use thunks ought never ask for
     // the address of a function that has not yet been finalized.
     assert(target->usesThunks());
@@ -70,11 +102,28 @@ uint64_t Defined::getVA() const {
     // expedient to return a contrived out-of-range address.
     return TargetInfo::outOfRangeVA;
   }
-  return isec->canonical()->getVA(value);
+  return isec->getVA(value);
+}
+
+ObjFile *Defined::getObjectFile() const {
+  return isec ? dyn_cast_or_null<ObjFile>(isec->getFile()) : nullptr;
+}
+
+void Defined::canonicalize() {
+  if (unwindEntry)
+    unwindEntry = unwindEntry->canonical();
+  if (isec)
+    isec = isec->canonical();
+}
+
+std::string Defined::getSourceLocation() {
+  if (!isec)
+    return {};
+  return isec->getSourceLocation(value);
 }
 
 uint64_t DylibSymbol::getVA() const {
   return isInStubs() ? getStubVA() : Symbol::getVA();
 }
 
-void LazySymbol::fetchArchiveMember() { getFile()->fetch(sym); }
+void LazyArchive::fetchArchiveMember() { getFile()->fetch(sym); }
index f7aac7c..6113f2d 100644 (file)
@@ -9,18 +9,16 @@
 #ifndef LLD_MACHO_SYMBOLS_H
 #define LLD_MACHO_SYMBOLS_H
 
+#include "Config.h"
 #include "InputFiles.h"
-#include "InputSection.h"
 #include "Target.h"
-#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Strings.h"
+
 #include "llvm/Object/Archive.h"
 #include "llvm/Support/MathExtras.h"
 
 namespace lld {
 namespace macho {
 
-class InputSection;
 class MachHeaderSection;
 
 struct StringRefZ {
@@ -38,7 +36,9 @@ public:
     UndefinedKind,
     CommonKind,
     DylibKind,
-    LazyKind,
+    LazyArchiveKind,
+    LazyObjectKind,
+    AliasKind,
   };
 
   virtual ~Symbol() {}
@@ -51,7 +51,10 @@ public:
     return {nameData, nameSize};
   }
 
-  bool isLive() const;
+  bool isLive() const { return used; }
+  bool isLazy() const {
+    return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind;
+  }
 
   virtual uint64_t getVA() const { return 0; }
 
@@ -60,7 +63,7 @@ public:
   // Only undefined or dylib symbols can be weak references. A weak reference
   // need not be satisfied at runtime, e.g. due to the symbol not being
   // available on a given target platform.
-  virtual bool isWeakRef() const { llvm_unreachable("cannot be a weak ref"); }
+  virtual bool isWeakRef() const { return false; }
 
   virtual bool isTlv() const { llvm_unreachable("cannot be TLV"); }
 
@@ -71,6 +74,7 @@ public:
   bool isInStubs() const { return stubsIndex != UINT32_MAX; }
 
   uint64_t getStubVA() const;
+  uint64_t getLazyPtrVA() const;
   uint64_t getGotVA() const;
   uint64_t getTlvVA() const;
   uint64_t resolveBranchVA() const {
@@ -84,29 +88,29 @@ public:
   // on whether it is a thread-local. A given symbol cannot be referenced by
   // both these sections at once.
   uint32_t gotIndex = UINT32_MAX;
-
+  uint32_t lazyBindOffset = UINT32_MAX;
+  uint32_t stubsHelperIndex = UINT32_MAX;
   uint32_t stubsIndex = UINT32_MAX;
-
   uint32_t symtabIndex = UINT32_MAX;
 
   InputFile *getFile() const { return file; }
 
 protected:
   Symbol(Kind k, StringRefZ name, InputFile *file)
-      : symbolKind(k), nameData(name.data), nameSize(name.size), file(file),
+      : symbolKind(k), nameData(name.data), file(file), nameSize(name.size),
         isUsedInRegularObj(!file || isa<ObjFile>(file)),
         used(!config->deadStrip) {}
 
   Kind symbolKind;
   const char *nameData;
-  mutable uint32_t nameSize;
   InputFile *file;
+  mutable uint32_t nameSize;
 
 public:
   // True if this symbol was referenced by a regular (non-bitcode) object.
   bool isUsedInRegularObj : 1;
 
-  // True if an undefined or dylib symbol is used from a live section.
+  // True if this symbol is used from a live section.
   bool used : 1;
 };
 
@@ -114,43 +118,42 @@ class Defined : public Symbol {
 public:
   Defined(StringRefZ name, InputFile *file, InputSection *isec, uint64_t value,
           uint64_t size, bool isWeakDef, bool isExternal, bool isPrivateExtern,
-          bool isThumb, bool isReferencedDynamically, bool noDeadStrip)
-      : Symbol(DefinedKind, name, file), isec(isec), value(value), size(size),
-        overridesWeakDef(false), privateExtern(isPrivateExtern),
-        includeInSymtab(true), thumb(isThumb),
-        referencedDynamically(isReferencedDynamically),
-        noDeadStrip(noDeadStrip), weakDef(isWeakDef), external(isExternal) {
-    if (auto concatIsec = dyn_cast_or_null<ConcatInputSection>(isec))
-      concatIsec->numRefs++;
-  }
+          bool includeInSymtab, bool isThumb, bool isReferencedDynamically,
+          bool noDeadStrip, bool canOverrideWeakDef = false,
+          bool isWeakDefCanBeHidden = false, bool interposable = false);
 
   bool isWeakDef() const override { return weakDef; }
   bool isExternalWeakDef() const {
     return isWeakDef() && isExternal() && !privateExtern;
   }
-  bool isTlv() const override {
-    return !isAbsolute() && isThreadLocalVariables(isec->getFlags());
-  }
+  bool isTlv() const override;
 
   bool isExternal() const { return external; }
   bool isAbsolute() const { return isec == nullptr; }
 
   uint64_t getVA() const override;
 
-  static bool classof(const Symbol *s) { return s->kind() == DefinedKind; }
+  // Returns the object file that this symbol was defined in. This value differs
+  // from `getFile()` if the symbol originated from a bitcode file.
+  ObjFile *getObjectFile() const;
 
-  InputSection *isec;
-  // Contains the offset from the containing subsection. Note that this is
-  // different from nlist::n_value, which is the absolute address of the symbol.
-  uint64_t value;
-  // size is only calculated for regular (non-bitcode) symbols.
-  uint64_t size;
+  std::string getSourceLocation();
+
+  // Ensure this symbol's pointers to InputSections point to their canonical
+  // copies.
+  void canonicalize();
+
+  static bool classof(const Symbol *s) { return s->kind() == DefinedKind; }
 
+  // Place the bitfields first so that they can get placed in the tail padding
+  // of the parent class, on platforms which support it.
   bool overridesWeakDef : 1;
   // Whether this symbol should appear in the output binary's export trie.
   bool privateExtern : 1;
   // Whether this symbol should appear in the output symbol table.
   bool includeInSymtab : 1;
+  // Whether this symbol was folded into a different symbol during ICF.
+  bool wasIdenticalCodeFolded : 1;
   // Only relevant when compiling for Thumb-supporting arm32 archs.
   bool thumb : 1;
   // Symbols marked referencedDynamically won't be removed from the output's
@@ -165,10 +168,30 @@ public:
   // metadata. This is information only for the static linker and not written
   // to the output.
   bool noDeadStrip : 1;
+  // Whether references to this symbol can be interposed at runtime to point to
+  // a different symbol definition (with the same name). For example, if both
+  // dylib A and B define an interposable symbol _foo, and we load A before B at
+  // runtime, then all references to _foo within dylib B will point to the
+  // definition in dylib A.
+  //
+  // Only extern symbols may be interposable.
+  bool interposable : 1;
+
+  bool weakDefCanBeHidden : 1;
 
 private:
   const bool weakDef : 1;
   const bool external : 1;
+
+public:
+  InputSection *isec;
+  // Contains the offset from the containing subsection. Note that this is
+  // different from nlist::n_value, which is the absolute address of the symbol.
+  uint64_t value;
+  // size is only calculated for regular (non-bitcode) symbols.
+  uint64_t size;
+  // This can be a subsection of either __compact_unwind or __eh_frame.
+  ConcatInputSection *unwindEntry = nullptr;
 };
 
 // This enum does double-duty: as a symbol property, it indicates whether & how
@@ -180,8 +203,10 @@ enum class RefState : uint8_t { Unreferenced = 0, Weak = 1, Strong = 2 };
 
 class Undefined : public Symbol {
 public:
-  Undefined(StringRefZ name, InputFile *file, RefState refState)
-      : Symbol(UndefinedKind, name, file), refState(refState) {
+  Undefined(StringRefZ name, InputFile *file, RefState refState,
+            bool wasBitcodeSymbol)
+      : Symbol(UndefinedKind, name, file), refState(refState),
+        wasBitcodeSymbol(wasBitcodeSymbol) {
     assert(refState != RefState::Unreferenced);
   }
 
@@ -190,6 +215,7 @@ public:
   static bool classof(const Symbol *s) { return s->kind() == UndefinedKind; }
 
   RefState refState : 2;
+  bool wasBitcodeSymbol;
 };
 
 // On Unix, it is traditionally allowed to write variable definitions without
@@ -236,7 +262,12 @@ public:
 
   uint64_t getVA() const override;
   bool isWeakDef() const override { return weakDef; }
-  bool isWeakRef() const override { return refState == RefState::Weak; }
+
+  // Symbols from weak libraries/frameworks are also weakly-referenced.
+  bool isWeakRef() const override {
+    return refState == RefState::Weak ||
+           (file && getFile()->umbrella->forceWeakImport);
+  }
   bool isReferenced() const { return refState != RefState::Unreferenced; }
   bool isTlv() const override { return tlv; }
   bool isDynamicLookup() const { return file == nullptr; }
@@ -249,9 +280,6 @@ public:
 
   static bool classof(const Symbol *s) { return s->kind() == DylibKind; }
 
-  uint32_t stubsHelperIndex = UINT32_MAX;
-  uint32_t lazyBindOffset = UINT32_MAX;
-
   RefState getRefState() const { return refState; }
 
   void reference(RefState newState) {
@@ -275,26 +303,60 @@ private:
   const bool tlv : 1;
 };
 
-class LazySymbol : public Symbol {
+class LazyArchive : public Symbol {
 public:
-  LazySymbol(ArchiveFile *file, const llvm::object::Archive::Symbol &sym)
-      : Symbol(LazyKind, sym.getName(), file), sym(sym) {}
+  LazyArchive(ArchiveFile *file, const llvm::object::Archive::Symbol &sym)
+      : Symbol(LazyArchiveKind, sym.getName(), file), sym(sym) {}
 
   ArchiveFile *getFile() const { return cast<ArchiveFile>(file); }
   void fetchArchiveMember();
 
-  static bool classof(const Symbol *s) { return s->kind() == LazyKind; }
+  static bool classof(const Symbol *s) { return s->kind() == LazyArchiveKind; }
 
 private:
   const llvm::object::Archive::Symbol sym;
 };
 
+// A defined symbol in an ObjFile/BitcodeFile surrounded by --start-lib and
+// --end-lib.
+class LazyObject : public Symbol {
+public:
+  LazyObject(InputFile &file, StringRef name)
+      : Symbol(LazyObjectKind, name, &file) {
+    isUsedInRegularObj = false;
+  }
+
+  static bool classof(const Symbol *s) { return s->kind() == LazyObjectKind; }
+};
+
+// Represents N_INDR symbols. Note that if we are given valid, linkable inputs,
+// then all AliasSymbol instances will be converted into one of the other Symbol
+// types after `createAliases()` runs.
+class AliasSymbol final : public Symbol {
+public:
+  AliasSymbol(InputFile *file, StringRef name, StringRef aliasedName,
+              bool isPrivateExtern)
+      : Symbol(AliasKind, name, file), privateExtern(isPrivateExtern),
+        aliasedName(aliasedName) {}
+
+  StringRef getAliasedName() const { return aliasedName; }
+
+  static bool classof(const Symbol *s) { return s->kind() == AliasKind; }
+
+  const bool privateExtern;
+
+private:
+  StringRef aliasedName;
+};
+
 union SymbolUnion {
   alignas(Defined) char a[sizeof(Defined)];
   alignas(Undefined) char b[sizeof(Undefined)];
   alignas(CommonSymbol) char c[sizeof(CommonSymbol)];
   alignas(DylibSymbol) char d[sizeof(DylibSymbol)];
-  alignas(LazySymbol) char e[sizeof(LazySymbol)];
+  alignas(LazyArchive) char e[sizeof(LazyArchive)];
+  alignas(LazyObject) char f[sizeof(LazyObject)];
+  alignas(AliasSymbol) char g[sizeof(AliasSymbol)];
 };
 
 template <typename T, typename... ArgT>
@@ -313,6 +375,14 @@ T *replaceSymbol(Symbol *s, ArgT &&...arg) {
   return sym;
 }
 
+// Can a symbol's address only be resolved at runtime?
+inline bool needsBinding(const Symbol *sym) {
+  if (isa<DylibSymbol>(sym))
+    return true;
+  if (const auto *defined = dyn_cast<Defined>(sym))
+    return defined->isExternalWeakDef() || defined->interposable;
+  return false;
+}
 } // namespace macho
 
 std::string toString(const macho::Symbol &);
index f493406..267f97a 100644 (file)
 #include "SymbolTable.h"
 #include "Symbols.h"
 
-#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Memory.h"
+#include "lld/Common/CommonLinkerContext.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Config/llvm-config.h"
 #include "llvm/Support/EndianStream.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/LEB128.h"
+#include "llvm/Support/Parallel.h"
 #include "llvm/Support/Path.h"
-#include "llvm/Support/SHA256.h"
+#include "llvm/Support/xxhash.h"
 
 #if defined(__APPLE__)
 #include <sys/mman.h>
+
+#define COMMON_DIGEST_FOR_OPENSSL
+#include <CommonCrypto/CommonDigest.h>
+#else
+#include "llvm/Support/SHA256.h"
 #endif
 
 #ifdef LLVM_HAVE_LIBXAR
 #include <fcntl.h>
+extern "C" {
 #include <xar/xar.h>
+}
 #endif
 
 using namespace llvm;
@@ -42,13 +49,27 @@ using namespace llvm::support::endian;
 using namespace lld;
 using namespace lld::macho;
 
+// Reads `len` bytes at data and writes the 32-byte SHA256 checksum to `output`.
+static void sha256(const uint8_t *data, size_t len, uint8_t *output) {
+#if defined(__APPLE__)
+  // FIXME: Make LLVM's SHA256 faster and use it unconditionally. See PR56121
+  // for some notes on this.
+  CC_SHA256(data, len, output);
+#else
+  ArrayRef<uint8_t> block(data, len);
+  std::array<uint8_t, 32> hash = SHA256::hash(block);
+  static_assert(hash.size() == CodeSignatureSection::hashSize);
+  memcpy(output, hash.data(), hash.size());
+#endif
+}
+
 InStruct macho::in;
 std::vector<SyntheticSection *> macho::syntheticSections;
 
 SyntheticSection::SyntheticSection(const char *segname, const char *name)
     : OutputSection(SyntheticKind, name) {
   std::tie(this->segname, this->name) = maybeRenameSection({segname, name});
-  isec = make<ConcatInputSection>(segname, name);
+  isec = makeSyntheticInputSection(segname, name);
   isec->parent = this;
   syntheticSections.push_back(this);
 }
@@ -83,13 +104,23 @@ static uint32_t cpuSubtype() {
 
   if (config->outputType == MH_EXECUTE && !config->staticLink &&
       target->cpuSubtype == CPU_SUBTYPE_X86_64_ALL &&
-      config->platform() == PlatformKind::macOS &&
+      config->platform() == PLATFORM_MACOS &&
       config->platformInfo.minimum >= VersionTuple(10, 5))
     subtype |= CPU_SUBTYPE_LIB64;
 
   return subtype;
 }
 
+static bool hasWeakBinding() {
+  return config->emitChainedFixups ? in.chainedFixups->hasWeakBinding()
+                                   : in.weakBinding->hasEntry();
+}
+
+static bool hasNonWeakDefinition() {
+  return config->emitChainedFixups ? in.chainedFixups->hasNonWeakDefinition()
+                                   : in.weakBinding->hasNonWeakDefinition();
+}
+
 void MachHeaderSection::writeTo(uint8_t *buf) const {
   auto *hdr = reinterpret_cast<mach_header *>(buf);
   hdr->magic = target->magic;
@@ -115,10 +146,10 @@ void MachHeaderSection::writeTo(uint8_t *buf) const {
   if (config->outputType == MH_DYLIB && config->applicationExtension)
     hdr->flags |= MH_APP_EXTENSION_SAFE;
 
-  if (in.exports->hasWeakSymbol || in.weakBinding->hasNonWeakDefinition())
+  if (in.exports->hasWeakSymbol || hasNonWeakDefinition())
     hdr->flags |= MH_WEAK_DEFINES;
 
-  if (in.exports->hasWeakSymbol || in.weakBinding->hasEntry())
+  if (in.exports->hasWeakSymbol || hasWeakBinding())
     hdr->flags |= MH_BINDS_TO_WEAK;
 
   for (const OutputSegment *seg : outputSegments) {
@@ -144,52 +175,108 @@ RebaseSection::RebaseSection()
     : LinkEditSection(segment_names::linkEdit, section_names::rebase) {}
 
 namespace {
-struct Rebase {
-  OutputSegment *segment = nullptr;
-  uint64_t offset = 0;
-  uint64_t consecutiveCount = 0;
+struct RebaseState {
+  uint64_t sequenceLength;
+  uint64_t skipLength;
 };
 } // namespace
 
-// Rebase opcodes allow us to describe a contiguous sequence of rebase location
-// using a single DO_REBASE opcode. To take advantage of it, we delay emitting
-// `DO_REBASE` until we have reached the end of a contiguous sequence.
-static void encodeDoRebase(Rebase &rebase, raw_svector_ostream &os) {
-  assert(rebase.consecutiveCount != 0);
-  if (rebase.consecutiveCount <= REBASE_IMMEDIATE_MASK) {
-    os << static_cast<uint8_t>(REBASE_OPCODE_DO_REBASE_IMM_TIMES |
-                               rebase.consecutiveCount);
+static void emitIncrement(uint64_t incr, raw_svector_ostream &os) {
+  assert(incr != 0);
+
+  if ((incr >> target->p2WordSize) <= REBASE_IMMEDIATE_MASK &&
+      (incr % target->wordSize) == 0) {
+    os << static_cast<uint8_t>(REBASE_OPCODE_ADD_ADDR_IMM_SCALED |
+                               (incr >> target->p2WordSize));
   } else {
-    os << static_cast<uint8_t>(REBASE_OPCODE_DO_REBASE_ULEB_TIMES);
-    encodeULEB128(rebase.consecutiveCount, os);
+    os << static_cast<uint8_t>(REBASE_OPCODE_ADD_ADDR_ULEB);
+    encodeULEB128(incr, os);
   }
-  rebase.consecutiveCount = 0;
 }
 
-static void encodeRebase(const OutputSection *osec, uint64_t outSecOff,
-                         Rebase &lastRebase, raw_svector_ostream &os) {
-  OutputSegment *seg = osec->parent;
-  uint64_t offset = osec->getSegmentOffset() + outSecOff;
-  if (lastRebase.segment != seg || lastRebase.offset != offset) {
-    if (lastRebase.consecutiveCount != 0)
-      encodeDoRebase(lastRebase, os);
-
-    if (lastRebase.segment != seg) {
-      os << static_cast<uint8_t>(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB |
-                                 seg->index);
-      encodeULEB128(offset, os);
-      lastRebase.segment = seg;
-      lastRebase.offset = offset;
+static void flushRebase(const RebaseState &state, raw_svector_ostream &os) {
+  assert(state.sequenceLength > 0);
+
+  if (state.skipLength == target->wordSize) {
+    if (state.sequenceLength <= REBASE_IMMEDIATE_MASK) {
+      os << static_cast<uint8_t>(REBASE_OPCODE_DO_REBASE_IMM_TIMES |
+                                 state.sequenceLength);
     } else {
-      assert(lastRebase.offset != offset);
-      os << static_cast<uint8_t>(REBASE_OPCODE_ADD_ADDR_ULEB);
-      encodeULEB128(offset - lastRebase.offset, os);
-      lastRebase.offset = offset;
+      os << static_cast<uint8_t>(REBASE_OPCODE_DO_REBASE_ULEB_TIMES);
+      encodeULEB128(state.sequenceLength, os);
     }
+  } else if (state.sequenceLength == 1) {
+    os << static_cast<uint8_t>(REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB);
+    encodeULEB128(state.skipLength - target->wordSize, os);
+  } else {
+    os << static_cast<uint8_t>(
+        REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB);
+    encodeULEB128(state.sequenceLength, os);
+    encodeULEB128(state.skipLength - target->wordSize, os);
   }
-  ++lastRebase.consecutiveCount;
-  // DO_REBASE causes dyld to both perform the binding and increment the offset
-  lastRebase.offset += target->wordSize;
+}
+
+// Rebases are communicated to dyld using a bytecode, whose opcodes cause the
+// memory location at a specific address to be rebased and/or the address to be
+// incremented.
+//
+// Opcode REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB is the most generic
+// one, encoding a series of evenly spaced addresses. This algorithm works by
+// splitting up the sorted list of addresses into such chunks. If the locations
+// are consecutive or the sequence consists of a single location, flushRebase
+// will use a smaller, more specialized encoding.
+static void encodeRebases(const OutputSegment *seg,
+                          MutableArrayRef<Location> locations,
+                          raw_svector_ostream &os) {
+  // dyld operates on segments. Translate section offsets into segment offsets.
+  for (Location &loc : locations)
+    loc.offset =
+        loc.isec->parent->getSegmentOffset() + loc.isec->getOffset(loc.offset);
+  // The algorithm assumes that locations are unique.
+  Location *end =
+      llvm::unique(locations, [](const Location &a, const Location &b) {
+        return a.offset == b.offset;
+      });
+  size_t count = end - locations.begin();
+
+  os << static_cast<uint8_t>(REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB |
+                             seg->index);
+  assert(!locations.empty());
+  uint64_t offset = locations[0].offset;
+  encodeULEB128(offset, os);
+
+  RebaseState state{1, target->wordSize};
+
+  for (size_t i = 1; i < count; ++i) {
+    offset = locations[i].offset;
+
+    uint64_t skip = offset - locations[i - 1].offset;
+    assert(skip != 0 && "duplicate locations should have been weeded out");
+
+    if (skip == state.skipLength) {
+      ++state.sequenceLength;
+    } else if (state.sequenceLength == 1) {
+      ++state.sequenceLength;
+      state.skipLength = skip;
+    } else if (skip < state.skipLength) {
+      // The address is lower than what the rebase pointer would be if the last
+      // location would be part of a sequence. We start a new sequence from the
+      // previous location.
+      --state.sequenceLength;
+      flushRebase(state, os);
+
+      state.sequenceLength = 2;
+      state.skipLength = skip;
+    } else {
+      // The address is at some positive offset from the rebase pointer. We
+      // start a new sequence which begins with the current location.
+      flushRebase(state, os);
+      emitIncrement(skip - state.skipLength, os);
+      state.sequenceLength = 1;
+      state.skipLength = target->wordSize;
+    }
+  }
+  flushRebase(state, os);
 }
 
 void RebaseSection::finalizeContents() {
@@ -197,19 +284,20 @@ void RebaseSection::finalizeContents() {
     return;
 
   raw_svector_ostream os{contents};
-  Rebase lastRebase;
-
   os << static_cast<uint8_t>(REBASE_OPCODE_SET_TYPE_IMM | REBASE_TYPE_POINTER);
 
   llvm::sort(locations, [](const Location &a, const Location &b) {
     return a.isec->getVA(a.offset) < b.isec->getVA(b.offset);
   });
-  for (const Location &loc : locations)
-    encodeRebase(loc.isec->parent, loc.isec->getOffset(loc.offset), lastRebase,
-                 os);
-  if (lastRebase.consecutiveCount != 0)
-    encodeDoRebase(lastRebase, os);
 
+  for (size_t i = 0, count = locations.size(); i < count;) {
+    const OutputSegment *seg = locations[i].isec->parent->parent;
+    size_t j = i + 1;
+    while (j < count && locations[j].isec->parent->parent == seg)
+      ++j;
+    encodeRebases(seg, {locations.data() + i, locations.data() + j}, os);
+    i = j;
+  }
   os << static_cast<uint8_t>(REBASE_OPCODE_DONE);
 }
 
@@ -226,6 +314,16 @@ NonLazyPointerSectionBase::NonLazyPointerSectionBase(const char *segname,
 void macho::addNonLazyBindingEntries(const Symbol *sym,
                                      const InputSection *isec, uint64_t offset,
                                      int64_t addend) {
+  if (config->emitChainedFixups) {
+    if (needsBinding(sym))
+      in.chainedFixups->addBinding(sym, isec, offset, addend);
+    else if (isa<Defined>(sym))
+      in.chainedFixups->addRebase(isec, offset);
+    else
+      llvm_unreachable("cannot bind to an undefined symbol");
+    return;
+  }
+
   if (const auto *dysym = dyn_cast<DylibSymbol>(sym)) {
     in.binding->addEntry(dysym, isec, offset, addend);
     if (dysym->isWeakDef())
@@ -234,6 +332,8 @@ void macho::addNonLazyBindingEntries(const Symbol *sym,
     in.rebase->addEntry(isec, offset);
     if (defined->isExternalWeakDef())
       in.weakBinding->addEntry(sym, isec, offset, addend);
+    else if (defined->interposable)
+      in.binding->addEntry(sym, isec, offset, addend);
   } else {
     // Undefined symbols are filtered out in scanRelocations(); we should never
     // get here
@@ -250,14 +350,56 @@ void NonLazyPointerSectionBase::addEntry(Symbol *sym) {
   }
 }
 
+void macho::writeChainedRebase(uint8_t *buf, uint64_t targetVA) {
+  assert(config->emitChainedFixups);
+  assert(target->wordSize == 8 && "Only 64-bit platforms are supported");
+  auto *rebase = reinterpret_cast<dyld_chained_ptr_64_rebase *>(buf);
+  rebase->target = targetVA & 0xf'ffff'ffff;
+  rebase->high8 = (targetVA >> 56);
+  rebase->reserved = 0;
+  rebase->next = 0;
+  rebase->bind = 0;
+
+  // The fixup format places a 64 GiB limit on the output's size.
+  // Should we handle this gracefully?
+  uint64_t encodedVA = rebase->target | ((uint64_t)rebase->high8 << 56);
+  if (encodedVA != targetVA)
+    error("rebase target address 0x" + Twine::utohexstr(targetVA) +
+          " does not fit into chained fixup. Re-link with -no_fixup_chains");
+}
+
+static void writeChainedBind(uint8_t *buf, const Symbol *sym, int64_t addend) {
+  assert(config->emitChainedFixups);
+  assert(target->wordSize == 8 && "Only 64-bit platforms are supported");
+  auto *bind = reinterpret_cast<dyld_chained_ptr_64_bind *>(buf);
+  auto [ordinal, inlineAddend] = in.chainedFixups->getBinding(sym, addend);
+  bind->ordinal = ordinal;
+  bind->addend = inlineAddend;
+  bind->reserved = 0;
+  bind->next = 0;
+  bind->bind = 1;
+}
+
+void macho::writeChainedFixup(uint8_t *buf, const Symbol *sym, int64_t addend) {
+  if (needsBinding(sym))
+    writeChainedBind(buf, sym, addend);
+  else
+    writeChainedRebase(buf, sym->getVA() + addend);
+}
+
 void NonLazyPointerSectionBase::writeTo(uint8_t *buf) const {
-  for (size_t i = 0, n = entries.size(); i < n; ++i)
-    if (auto *defined = dyn_cast<Defined>(entries[i]))
-      write64le(&buf[i * target->wordSize], defined->getVA());
+  if (config->emitChainedFixups) {
+    for (const auto &[i, entry] : llvm::enumerate(entries))
+      writeChainedFixup(&buf[i * target->wordSize], entry, 0);
+  } else {
+    for (const auto &[i, entry] : llvm::enumerate(entries))
+      if (auto *defined = dyn_cast<Defined>(entry))
+        write64le(&buf[i * target->wordSize], defined->getVA());
+  }
 }
 
 GotSection::GotSection()
-    : NonLazyPointerSectionBase(segment_names::dataConst, section_names::got) {
+    : NonLazyPointerSectionBase(segment_names::data, section_names::got) {
   flags = S_NON_LAZY_SYMBOL_POINTERS;
 }
 
@@ -416,6 +558,13 @@ static int16_t ordinalForDylibSymbol(const DylibSymbol &dysym) {
   return dysym.getFile()->ordinal;
 }
 
+static int16_t ordinalForSymbol(const Symbol &sym) {
+  if (const auto *dysym = dyn_cast<DylibSymbol>(&sym))
+    return ordinalForDylibSymbol(*dysym);
+  assert(cast<Defined>(&sym)->interposable);
+  return BIND_SPECIAL_DYLIB_FLAT_LOOKUP;
+}
+
 static void encodeDylibOrdinal(int16_t ordinal, raw_svector_ostream &os) {
   if (ordinal <= 0) {
     os << static_cast<uint8_t>(BIND_OPCODE_SET_DYLIB_SPECIAL_IMM |
@@ -485,14 +634,14 @@ void BindingSection::finalizeContents() {
   int16_t lastOrdinal = 0;
 
   for (auto &p : sortBindings(bindingsMap)) {
-    const DylibSymbol *sym = p.first;
+    const Symbol *sym = p.first;
     std::vector<BindingEntry> &bindings = p.second;
     uint8_t flags = BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM;
     if (sym->isWeakRef())
       flags |= BIND_SYMBOL_FLAGS_WEAK_IMPORT;
     os << flags << sym->getName() << '\0'
        << static_cast<uint8_t>(BIND_OPCODE_SET_TYPE_IMM | BIND_TYPE_POINTER);
-    int16_t ordinal = ordinalForDylibSymbol(*sym);
+    int16_t ordinal = ordinalForSymbol(*sym);
     if (ordinal != lastOrdinal) {
       encodeDylibOrdinal(ordinal, os);
       lastOrdinal = ordinal;
@@ -565,18 +714,52 @@ uint64_t StubsSection::getSize() const {
 void StubsSection::writeTo(uint8_t *buf) const {
   size_t off = 0;
   for (const Symbol *sym : entries) {
-    target->writeStub(buf + off, *sym);
+    uint64_t pointerVA =
+        config->emitChainedFixups ? sym->getGotVA() : sym->getLazyPtrVA();
+    target->writeStub(buf + off, *sym, pointerVA);
     off += target->stubSize;
   }
 }
 
 void StubsSection::finalize() { isFinal = true; }
 
-bool StubsSection::addEntry(Symbol *sym) {
+static void addBindingsForStub(Symbol *sym) {
+  assert(!config->emitChainedFixups);
+  if (auto *dysym = dyn_cast<DylibSymbol>(sym)) {
+    if (sym->isWeakDef()) {
+      in.binding->addEntry(dysym, in.lazyPointers->isec,
+                           sym->stubsIndex * target->wordSize);
+      in.weakBinding->addEntry(sym, in.lazyPointers->isec,
+                               sym->stubsIndex * target->wordSize);
+    } else {
+      in.lazyBinding->addEntry(dysym);
+    }
+  } else if (auto *defined = dyn_cast<Defined>(sym)) {
+    if (defined->isExternalWeakDef()) {
+      in.rebase->addEntry(in.lazyPointers->isec,
+                          sym->stubsIndex * target->wordSize);
+      in.weakBinding->addEntry(sym, in.lazyPointers->isec,
+                               sym->stubsIndex * target->wordSize);
+    } else if (defined->interposable) {
+      in.lazyBinding->addEntry(sym);
+    } else {
+      llvm_unreachable("invalid stub target");
+    }
+  } else {
+    llvm_unreachable("invalid stub target symbol type");
+  }
+}
+
+void StubsSection::addEntry(Symbol *sym) {
   bool inserted = entries.insert(sym);
-  if (inserted)
+  if (inserted) {
     sym->stubsIndex = entries.size() - 1;
-  return inserted;
+
+    if (config->emitChainedFixups)
+      in.got->addEntry(sym);
+    else
+      addBindingsForStub(sym);
+  }
 }
 
 StubHelperSection::StubHelperSection()
@@ -595,13 +778,13 @@ bool StubHelperSection::isNeeded() const { return in.lazyBinding->isNeeded(); }
 void StubHelperSection::writeTo(uint8_t *buf) const {
   target->writeStubHelperHeader(buf);
   size_t off = target->stubHelperHeaderSize;
-  for (const DylibSymbol *sym : in.lazyBinding->getEntries()) {
+  for (const Symbol *sym : in.lazyBinding->getEntries()) {
     target->writeStubHelperEntry(buf + off, *sym, addr + off);
     off += target->stubHelperEntrySize;
   }
 }
 
-void StubHelperSection::setup() {
+void StubHelperSection::setUp() {
   Symbol *binder = symtab->addUndefined("dyld_stub_binder", /*file=*/nullptr,
                                         /*isWeakRef=*/false);
   if (auto *undefined = dyn_cast<Undefined>(binder))
@@ -619,14 +802,89 @@ void StubHelperSection::setup() {
       ConcatOutputSection::getOrCreateForInput(in.imageLoaderCache);
   inputSections.push_back(in.imageLoaderCache);
   // Since this isn't in the symbol table or in any input file, the noDeadStrip
-  // argument doesn't matter. It's kept alive by ImageLoaderCacheSection()
-  // setting `live` to true on the backing InputSection.
+  // argument doesn't matter.
   dyldPrivate =
       make<Defined>("__dyld_private", nullptr, in.imageLoaderCache, 0, 0,
                     /*isWeakDef=*/false,
                     /*isExternal=*/false, /*isPrivateExtern=*/false,
+                    /*includeInSymtab=*/true,
                     /*isThumb=*/false, /*isReferencedDynamically=*/false,
                     /*noDeadStrip=*/false);
+  dyldPrivate->used = true;
+}
+
+ObjCStubsSection::ObjCStubsSection()
+    : SyntheticSection(segment_names::text, section_names::objcStubs) {
+  flags = S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS;
+  align = target->objcStubsAlignment;
+}
+
+void ObjCStubsSection::addEntry(Symbol *sym) {
+  assert(sym->getName().startswith(symbolPrefix) && "not an objc stub");
+  StringRef methname = sym->getName().drop_front(symbolPrefix.size());
+  offsets.push_back(
+      in.objcMethnameSection->getStringOffset(methname).outSecOff);
+  Defined *newSym = replaceSymbol<Defined>(
+      sym, sym->getName(), nullptr, isec,
+      /*value=*/symbols.size() * target->objcStubsFastSize,
+      /*size=*/target->objcStubsFastSize,
+      /*isWeakDef=*/false, /*isExternal=*/true, /*isPrivateExtern=*/true,
+      /*includeInSymtab=*/true, /*isThumb=*/false,
+      /*isReferencedDynamically=*/false, /*noDeadStrip=*/false);
+  symbols.push_back(newSym);
+}
+
+void ObjCStubsSection::setUp() {
+  Symbol *objcMsgSend = symtab->addUndefined("_objc_msgSend", /*file=*/nullptr,
+                                             /*isWeakRef=*/false);
+  objcMsgSend->used = true;
+  in.got->addEntry(objcMsgSend);
+  assert(objcMsgSend->isInGot());
+  objcMsgSendGotIndex = objcMsgSend->gotIndex;
+
+  size_t size = offsets.size() * target->wordSize;
+  uint8_t *selrefsData = bAlloc().Allocate<uint8_t>(size);
+  for (size_t i = 0, n = offsets.size(); i < n; ++i)
+    write64le(&selrefsData[i * target->wordSize], offsets[i]);
+
+  in.objcSelrefs =
+      makeSyntheticInputSection(segment_names::data, section_names::objcSelrefs,
+                                S_LITERAL_POINTERS | S_ATTR_NO_DEAD_STRIP,
+                                ArrayRef<uint8_t>{selrefsData, size},
+                                /*align=*/target->wordSize);
+  in.objcSelrefs->live = true;
+
+  for (size_t i = 0, n = offsets.size(); i < n; ++i) {
+    in.objcSelrefs->relocs.push_back(
+        {/*type=*/target->unsignedRelocType,
+         /*pcrel=*/false, /*length=*/3,
+         /*offset=*/static_cast<uint32_t>(i * target->wordSize),
+         /*addend=*/offsets[i] * in.objcMethnameSection->align,
+         /*referent=*/in.objcMethnameSection->isec});
+  }
+
+  in.objcSelrefs->parent =
+      ConcatOutputSection::getOrCreateForInput(in.objcSelrefs);
+  inputSections.push_back(in.objcSelrefs);
+  in.objcSelrefs->isFinal = true;
+}
+
+uint64_t ObjCStubsSection::getSize() const {
+  return target->objcStubsFastSize * symbols.size();
+}
+
+void ObjCStubsSection::writeTo(uint8_t *buf) const {
+  assert(in.objcSelrefs->live);
+  assert(in.objcSelrefs->isFinal);
+
+  uint64_t stubOffset = 0;
+  for (size_t i = 0, n = symbols.size(); i < n; ++i) {
+    Defined *sym = symbols[i];
+    target->writeObjCMsgSendStub(buf + stubOffset, sym, in.objcStubs->addr,
+                                 stubOffset, in.objcSelrefs->getVA(), i,
+                                 in.got->addr, objcMsgSendGotIndex);
+    stubOffset += target->objcStubsFastSize;
+  }
 }
 
 LazyPointerSection::LazyPointerSection()
@@ -666,7 +924,7 @@ LazyBindingSection::LazyBindingSection()
 void LazyBindingSection::finalizeContents() {
   // TODO: Just precompute output size here instead of writing to a temporary
   // buffer
-  for (DylibSymbol *sym : entries)
+  for (Symbol *sym : entries)
     sym->lazyBindOffset = encode(*sym);
 }
 
@@ -674,11 +932,12 @@ void LazyBindingSection::writeTo(uint8_t *buf) const {
   memcpy(buf, contents.data(), contents.size());
 }
 
-void LazyBindingSection::addEntry(DylibSymbol *dysym) {
-  if (entries.insert(dysym)) {
-    dysym->stubsHelperIndex = entries.size() - 1;
+void LazyBindingSection::addEntry(Symbol *sym) {
+  assert(!config->emitChainedFixups && "Chained fixups always bind eagerly");
+  if (entries.insert(sym)) {
+    sym->stubsHelperIndex = entries.size() - 1;
     in.rebase->addEntry(in.lazyPointers->isec,
-                        dysym->stubsIndex * target->wordSize);
+                        sym->stubsIndex * target->wordSize);
   }
 }
 
@@ -688,15 +947,15 @@ void LazyBindingSection::addEntry(DylibSymbol *dysym) {
 // BIND_OPCODE_DONE terminator. As such, unlike in the non-lazy-binding case,
 // we cannot encode just the differences between symbols; we have to emit the
 // complete bind information for each symbol.
-uint32_t LazyBindingSection::encode(const DylibSymbol &sym) {
+uint32_t LazyBindingSection::encode(const Symbol &sym) {
   uint32_t opstreamOffset = contents.size();
   OutputSegment *dataSeg = in.lazyPointers->parent;
   os << static_cast<uint8_t>(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB |
                              dataSeg->index);
-  uint64_t offset = in.lazyPointers->addr - dataSeg->addr +
-                    sym.stubsIndex * target->wordSize;
+  uint64_t offset =
+      in.lazyPointers->addr - dataSeg->addr + sym.stubsIndex * target->wordSize;
   encodeULEB128(offset, os);
-  encodeDylibOrdinal(ordinalForDylibSymbol(sym), os);
+  encodeDylibOrdinal(ordinalForSymbol(sym), os);
 
   uint8_t flags = BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM;
   if (sym.isWeakRef())
@@ -731,42 +990,36 @@ DataInCodeSection::DataInCodeSection()
 
 template <class LP>
 static std::vector<MachO::data_in_code_entry> collectDataInCodeEntries() {
-  using SegmentCommand = typename LP::segment_command;
-  using Section = typename LP::section;
-
   std::vector<MachO::data_in_code_entry> dataInCodeEntries;
   for (const InputFile *inputFile : inputFiles) {
     if (!isa<ObjFile>(inputFile))
       continue;
     const ObjFile *objFile = cast<ObjFile>(inputFile);
-    const auto *c = reinterpret_cast<const SegmentCommand *>(
-        findCommand(objFile->mb.getBufferStart(), LP::segmentLCType));
-    if (!c)
-      continue;
-    ArrayRef<Section> sections{reinterpret_cast<const Section *>(c + 1),
-                               c->nsects};
-
-    ArrayRef<MachO::data_in_code_entry> entries = objFile->dataInCodeEntries;
+    ArrayRef<MachO::data_in_code_entry> entries = objFile->getDataInCode();
     if (entries.empty())
       continue;
+
+    assert(is_sorted(entries, [](const data_in_code_entry &lhs,
+                                 const data_in_code_entry &rhs) {
+      return lhs.offset < rhs.offset;
+    }));
     // For each code subsection find 'data in code' entries residing in it.
     // Compute the new offset values as
     // <offset within subsection> + <subsection address> - <__TEXT address>.
-    for (size_t i = 0, n = sections.size(); i < n; ++i) {
-      const SubsectionMap &subsecMap = objFile->subsections[i];
-      for (const SubsectionEntry &subsecEntry : subsecMap) {
-        const InputSection *isec = subsecEntry.isec;
+    for (const Section *section : objFile->sections) {
+      for (const Subsection &subsec : section->subsections) {
+        const InputSection *isec = subsec.isec;
         if (!isCodeSection(isec))
           continue;
         if (cast<ConcatInputSection>(isec)->shouldOmitFromOutput())
           continue;
-        const uint64_t beginAddr = sections[i].addr + subsecEntry.offset;
+        const uint64_t beginAddr = section->addr + subsec.offset;
         auto it = llvm::lower_bound(
             entries, beginAddr,
             [](const MachO::data_in_code_entry &entry, uint64_t addr) {
               return entry.offset < addr;
             });
-        const uint64_t endAddr = beginAddr + isec->getFileSize();
+        const uint64_t endAddr = beginAddr + isec->getSize();
         for (const auto end = entries.end();
              it != end && it->offset + it->length <= endAddr; ++it)
           dataInCodeEntries.push_back(
@@ -776,6 +1029,12 @@ static std::vector<MachO::data_in_code_entry> collectDataInCodeEntries() {
       }
     }
   }
+
+  // ld64 emits the table in sorted order too.
+  llvm::sort(dataInCodeEntries,
+             [](const data_in_code_entry &lhs, const data_in_code_entry &rhs) {
+               return lhs.offset < rhs.offset;
+             });
   return dataInCodeEntries;
 }
 
@@ -795,16 +1054,18 @@ FunctionStartsSection::FunctionStartsSection()
 void FunctionStartsSection::finalizeContents() {
   raw_svector_ostream os{contents};
   std::vector<uint64_t> addrs;
-  for (const Symbol *sym : symtab->getSymbols()) {
-    if (const auto *defined = dyn_cast<Defined>(sym)) {
-      if (!defined->isec || !isCodeSection(defined->isec) || !defined->isLive())
-        continue;
-      if (const auto *concatIsec = dyn_cast<ConcatInputSection>(defined->isec))
-        if (concatIsec->shouldOmitFromOutput())
-          continue;
-      // TODO: Add support for thumbs, in that case
-      // the lowest bit of nextAddr needs to be set to 1.
-      addrs.push_back(defined->getVA());
+  for (const InputFile *file : inputFiles) {
+    if (auto *objFile = dyn_cast<ObjFile>(file)) {
+      for (const Symbol *sym : objFile->symbols) {
+        if (const auto *defined = dyn_cast_or_null<Defined>(sym)) {
+          if (!defined->isec || !isCodeSection(defined->isec) ||
+              !defined->isLive())
+            continue;
+          // TODO: Add support for thumbs, in that case
+          // the lowest bit of nextAddr needs to be set to 1.
+          addrs.push_back(defined->getVA());
+        }
+      }
     }
   }
   llvm::sort(addrs);
@@ -827,16 +1088,9 @@ SymtabSection::SymtabSection(StringTableSection &stringTableSection)
     : LinkEditSection(segment_names::linkEdit, section_names::symbolTable),
       stringTableSection(stringTableSection) {}
 
-void SymtabSection::emitBeginSourceStab(DWARFUnit *compileUnit) {
+void SymtabSection::emitBeginSourceStab(StringRef sourceFile) {
   StabsEntry stab(N_SO);
-  SmallString<261> dir(compileUnit->getCompilationDir());
-  StringRef sep = sys::path::get_separator();
-  // We don't use `path::append` here because we want an empty `dir` to result
-  // in an absolute path. `append` would give us a relative path for that case.
-  if (!dir.endswith(sep))
-    dir += sep;
-  stab.strx = stringTableSection.addString(
-      saver.save(dir + compileUnit->getUnitDIE().getShortName()));
+  stab.strx = stringTableSection.addString(saver().save(sourceFile));
   stabs.emplace_back(std::move(stab));
 }
 
@@ -858,7 +1112,10 @@ void SymtabSection::emitObjectFileStab(ObjFile *file) {
   if (!file->archiveName.empty())
     path.append({"(", file->getName(), ")"});
 
-  stab.strx = stringTableSection.addString(saver.save(path.str()));
+  StringRef adjustedPath = saver().save(path.str());
+  adjustedPath.consume_front(config->osoPrefix);
+
+  stab.strx = stringTableSection.addString(adjustedPath);
   stab.desc = 1;
   stab.value = file->modTime;
   stabs.emplace_back(std::move(stab));
@@ -871,38 +1128,54 @@ void SymtabSection::emitEndFunStab(Defined *defined) {
 }
 
 void SymtabSection::emitStabs() {
+  if (config->omitDebugInfo)
+    return;
+
   for (const std::string &s : config->astPaths) {
     StabsEntry astStab(N_AST);
     astStab.strx = stringTableSection.addString(s);
     stabs.emplace_back(std::move(astStab));
   }
 
-  std::vector<Defined *> symbolsNeedingStabs;
+  // Cache the file ID for each symbol in an std::pair for faster sorting.
+  using SortingPair = std::pair<Defined *, int>;
+  std::vector<SortingPair> symbolsNeedingStabs;
   for (const SymtabEntry &entry :
        concat<SymtabEntry>(localSymbols, externalSymbols)) {
     Symbol *sym = entry.sym;
     assert(sym->isLive() &&
            "dead symbols should not be in localSymbols, externalSymbols");
     if (auto *defined = dyn_cast<Defined>(sym)) {
+      // Excluded symbols should have been filtered out in finalizeContents().
+      assert(defined->includeInSymtab);
+
       if (defined->isAbsolute())
         continue;
-      InputSection *isec = defined->isec;
-      ObjFile *file = dyn_cast_or_null<ObjFile>(isec->getFile());
+
+      // Constant-folded symbols go in the executable's symbol table, but don't
+      // get a stabs entry.
+      if (defined->wasIdenticalCodeFolded)
+        continue;
+
+      ObjFile *file = defined->getObjectFile();
       if (!file || !file->compileUnit)
         continue;
-      symbolsNeedingStabs.push_back(defined);
+
+      symbolsNeedingStabs.emplace_back(defined, defined->isec->getFile()->id);
     }
   }
 
-  llvm::stable_sort(symbolsNeedingStabs, [&](Defined *a, Defined *b) {
-    return a->isec->getFile()->id < b->isec->getFile()->id;
-  });
+  llvm::stable_sort(symbolsNeedingStabs,
+                    [&](const SortingPair &a, const SortingPair &b) {
+                      return a.second < b.second;
+                    });
 
   // Emit STABS symbols so that dsymutil and/or the debugger can map address
   // regions in the final binary to the source and object files from which they
   // originated.
   InputFile *lastFile = nullptr;
-  for (Defined *defined : symbolsNeedingStabs) {
+  for (SortingPair &pair : symbolsNeedingStabs) {
+    Defined *defined = pair.first;
     InputSection *isec = defined->isec;
     ObjFile *file = cast<ObjFile>(isec->getFile());
 
@@ -911,12 +1184,12 @@ void SymtabSection::emitStabs() {
         emitEndSourceStab();
       lastFile = file;
 
-      emitBeginSourceStab(file->compileUnit);
+      emitBeginSourceStab(file->sourceFile());
       emitObjectFileStab(file);
     }
 
     StabsEntry symStab;
-    symStab.sect = defined->isec->canonical()->parent->index;
+    symStab.sect = defined->isec->parent->index;
     symStab.strx = stringTableSection.addString(defined->getName());
     symStab.value = defined->getVA();
 
@@ -940,16 +1213,42 @@ void SymtabSection::finalizeContents() {
     symbols.push_back({sym, strx});
   };
 
+  std::function<void(Symbol *)> localSymbolsHandler;
+  switch (config->localSymbolsPresence) {
+  case SymtabPresence::All:
+    localSymbolsHandler = [&](Symbol *sym) { addSymbol(localSymbols, sym); };
+    break;
+  case SymtabPresence::None:
+    localSymbolsHandler = [&](Symbol *) { /* Do nothing*/ };
+    break;
+  case SymtabPresence::SelectivelyIncluded:
+    localSymbolsHandler = [&](Symbol *sym) {
+      if (config->localSymbolPatterns.match(sym->getName()))
+        addSymbol(localSymbols, sym);
+    };
+    break;
+  case SymtabPresence::SelectivelyExcluded:
+    localSymbolsHandler = [&](Symbol *sym) {
+      if (!config->localSymbolPatterns.match(sym->getName()))
+        addSymbol(localSymbols, sym);
+    };
+    break;
+  }
+
   // Local symbols aren't in the SymbolTable, so we walk the list of object
   // files to gather them.
-  for (const InputFile *file : inputFiles) {
-    if (auto *objFile = dyn_cast<ObjFile>(file)) {
-      for (Symbol *sym : objFile->symbols) {
-        if (auto *defined = dyn_cast_or_null<Defined>(sym)) {
-          if (!defined->isExternal() && defined->isLive()) {
-            StringRef name = defined->getName();
-            if (!name.startswith("l") && !name.startswith("L"))
-              addSymbol(localSymbols, sym);
+  // But if `-x` is set, then we don't need to. localSymbolsHandler() will do
+  // the right thing regardless, but this check is a perf optimization because
+  // iterating through all the input files and their symbols is expensive.
+  if (config->localSymbolsPresence != SymtabPresence::None) {
+    for (const InputFile *file : inputFiles) {
+      if (auto *objFile = dyn_cast<ObjFile>(file)) {
+        for (Symbol *sym : objFile->symbols) {
+          if (auto *defined = dyn_cast_or_null<Defined>(sym)) {
+            if (defined->isExternal() || !defined->isLive() ||
+                !defined->includeInSymtab)
+              continue;
+            localSymbolsHandler(sym);
           }
         }
       }
@@ -958,8 +1257,8 @@ void SymtabSection::finalizeContents() {
 
   // __dyld_private is a local symbol too. It's linker-created and doesn't
   // exist in any object file.
-  if (Defined *dyldPrivate = in.stubHelper->dyldPrivate)
-    addSymbol(localSymbols, dyldPrivate);
+  if (in.stubHelper && in.stubHelper->dyldPrivate)
+    localSymbolsHandler(in.stubHelper->dyldPrivate);
 
   for (Symbol *sym : symtab->getSymbols()) {
     if (!sym->isLive())
@@ -969,7 +1268,7 @@ void SymtabSection::finalizeContents() {
         continue;
       assert(defined->isExternal());
       if (defined->privateExtern)
-        addSymbol(localSymbols, defined);
+        localSymbolsHandler(defined);
       else
         addSymbol(externalSymbols, defined);
     } else if (auto *dysym = dyn_cast<DylibSymbol>(sym)) {
@@ -1041,7 +1340,7 @@ template <class LP> void SymtabSectionImpl<LP>::writeTo(uint8_t *buf) const {
         nList->n_value = defined->value;
       } else {
         nList->n_type = scope | N_SECT;
-        nList->n_sect = defined->isec->canonical()->parent->index;
+        nList->n_sect = defined->isec->parent->index;
         // For the N_SECT symbol type, n_value is the address of the symbol
         nList->n_value = defined->getVA();
       }
@@ -1081,8 +1380,12 @@ IndirectSymtabSection::IndirectSymtabSection()
                       section_names::indirectSymbolTable) {}
 
 uint32_t IndirectSymtabSection::getNumSymbols() const {
-  return in.got->getEntries().size() + in.tlvPointers->getEntries().size() +
-         2 * in.stubs->getEntries().size();
+  uint32_t size = in.got->getEntries().size() +
+                  in.tlvPointers->getEntries().size() +
+                  in.stubs->getEntries().size();
+  if (!config->emitChainedFixups)
+    size += in.stubs->getEntries().size();
+  return size;
 }
 
 bool IndirectSymtabSection::isNeeded() const {
@@ -1097,13 +1400,19 @@ void IndirectSymtabSection::finalizeContents() {
   in.tlvPointers->reserved1 = off;
   off += in.tlvPointers->getEntries().size();
   in.stubs->reserved1 = off;
-  off += in.stubs->getEntries().size();
-  in.lazyPointers->reserved1 = off;
+  if (in.lazyPointers) {
+    off += in.stubs->getEntries().size();
+    in.lazyPointers->reserved1 = off;
+  }
 }
 
 static uint32_t indirectValue(const Symbol *sym) {
-  return sym->symtabIndex != UINT32_MAX ? sym->symtabIndex
-                                        : INDIRECT_SYMBOL_LOCAL;
+  if (sym->symtabIndex == UINT32_MAX)
+    return INDIRECT_SYMBOL_LOCAL;
+  if (auto *defined = dyn_cast<Defined>(sym))
+    if (defined->privateExtern)
+      return INDIRECT_SYMBOL_LOCAL;
+  return sym->symtabIndex;
 }
 
 void IndirectSymtabSection::writeTo(uint8_t *buf) const {
@@ -1120,14 +1429,17 @@ void IndirectSymtabSection::writeTo(uint8_t *buf) const {
     write32le(buf + off * sizeof(uint32_t), indirectValue(sym));
     ++off;
   }
-  // There is a 1:1 correspondence between stubs and LazyPointerSection
-  // entries. But giving __stubs and __la_symbol_ptr the same reserved1
-  // (the offset into the indirect symbol table) so that they both refer
-  // to the same range of offsets confuses `strip`, so write the stubs
-  // symbol table offsets a second time.
-  for (const Symbol *sym : in.stubs->getEntries()) {
-    write32le(buf + off * sizeof(uint32_t), indirectValue(sym));
-    ++off;
+
+  if (in.lazyPointers) {
+    // There is a 1:1 correspondence between stubs and LazyPointerSection
+    // entries. But giving __stubs and __la_symbol_ptr the same reserved1
+    // (the offset into the indirect symbol table) so that they both refer
+    // to the same range of offsets confuses `strip`, so write the stubs
+    // symbol table offsets a second time.
+    for (const Symbol *sym : in.stubs->getEntries()) {
+      write32le(buf + off * sizeof(uint32_t), indirectValue(sym));
+      ++off;
+    }
   }
 }
 
@@ -1149,8 +1461,8 @@ void StringTableSection::writeTo(uint8_t *buf) const {
   }
 }
 
-static_assert((CodeSignatureSection::blobHeadersSize % 8) == 0, "");
-static_assert((CodeSignatureSection::fixedHeadersSize % 8) == 0, "");
+static_assert((CodeSignatureSection::blobHeadersSize % 8) == 0);
+static_assert((CodeSignatureSection::fixedHeadersSize % 8) == 0);
 
 CodeSignatureSection::CodeSignatureSection()
     : LinkEditSection(segment_names::linkEdit, section_names::codeSignature) {
@@ -1160,6 +1472,9 @@ CodeSignatureSection::CodeSignatureSection()
   size_t slashIndex = fileName.rfind("/");
   if (slashIndex != std::string::npos)
     fileName = fileName.drop_front(slashIndex + 1);
+
+  // NOTE: Any changes to these calculations should be repeated
+  // in llvm-objcopy's MachOLayoutBuilder::layoutTail.
   allHeadersSize = alignTo<16>(fixedHeadersSize + fileName.size() + 1);
   fileNamePad = allHeadersSize - fixedHeadersSize - fileName.size();
 }
@@ -1173,20 +1488,14 @@ uint64_t CodeSignatureSection::getRawSize() const {
 }
 
 void CodeSignatureSection::writeHashes(uint8_t *buf) const {
-  uint8_t *code = buf;
-  uint8_t *codeEnd = buf + fileOff;
-  uint8_t *hashes = codeEnd + allHeadersSize;
-  while (code < codeEnd) {
-    StringRef block(reinterpret_cast<char *>(code),
-                    std::min(codeEnd - code, static_cast<ssize_t>(blockSize)));
-    SHA256 hasher;
-    hasher.update(block);
-    StringRef hash = hasher.final();
-    assert(hash.size() == hashSize);
-    memcpy(hashes, hash.data(), hashSize);
-    code += blockSize;
-    hashes += hashSize;
-  }
+  // NOTE: Changes to this functionality should be repeated in llvm-objcopy's
+  // MachOWriter::writeSignatureData.
+  uint8_t *hashes = buf + fileOff + allHeadersSize;
+  parallelFor(0, getBlockCount(), [&](size_t i) {
+    sha256(buf + i * blockSize,
+           std::min(static_cast<size_t>(fileOff - i * blockSize), blockSize),
+           hashes + i * hashSize);
+  });
 #if defined(__APPLE__)
   // This is macOS-specific work-around and makes no sense for any
   // other host OS. See https://openradar.appspot.com/FB8914231
@@ -1203,6 +1512,8 @@ void CodeSignatureSection::writeHashes(uint8_t *buf) const {
 }
 
 void CodeSignatureSection::writeTo(uint8_t *buf) const {
+  // NOTE: Changes to this functionality should be repeated in llvm-objcopy's
+  // MachOWriter::writeSignatureData.
   uint32_t signatureSize = static_cast<uint32_t>(getSize());
   auto *superBlob = reinterpret_cast<CS_SuperBlob *>(buf);
   write32be(&superBlob->magic, CSMAGIC_EMBEDDED_SIGNATURE);
@@ -1268,7 +1579,10 @@ void BitcodeBundleSection::finalize() {
   using namespace llvm::sys::fs;
   CHECK_EC(createTemporaryFile("bitcode-bundle", "xar", xarPath));
 
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
   xar_t xar(xar_open(xarPath.data(), O_RDWR));
+#pragma clang diagnostic pop
   if (!xar)
     fatal("failed to open XAR temporary file at " + xarPath);
   CHECK_EC(xar_opt_set(xar, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE));
@@ -1295,8 +1609,8 @@ void BitcodeBundleSection::writeTo(uint8_t *buf) const {
   remove(xarPath);
 }
 
-CStringSection::CStringSection()
-    : SyntheticSection(segment_names::text, section_names::cString) {
+CStringSection::CStringSection(const char *name)
+    : SyntheticSection(segment_names::text, name) {
   flags = S_CSTRING_LITERALS;
 }
 
@@ -1309,11 +1623,11 @@ void CStringSection::addInput(CStringInputSection *isec) {
 
 void CStringSection::writeTo(uint8_t *buf) const {
   for (const CStringInputSection *isec : inputs) {
-    for (size_t i = 0, e = isec->pieces.size(); i != e; ++i) {
-      if (!isec->pieces[i].live)
+    for (const auto &[i, piece] : llvm::enumerate(isec->pieces)) {
+      if (!piece.live)
         continue;
       StringRef string = isec->getStringRef(i);
-      memcpy(buf + isec->pieces[i].outSecOff, string.data(), string.size());
+      memcpy(buf + piece.outSecOff, string.data(), string.size());
     }
   }
 }
@@ -1321,71 +1635,117 @@ void CStringSection::writeTo(uint8_t *buf) const {
 void CStringSection::finalizeContents() {
   uint64_t offset = 0;
   for (CStringInputSection *isec : inputs) {
-    for (size_t i = 0, e = isec->pieces.size(); i != e; ++i) {
-      if (!isec->pieces[i].live)
+    for (const auto &[i, piece] : llvm::enumerate(isec->pieces)) {
+      if (!piece.live)
         continue;
-      uint32_t pieceAlign = MinAlign(isec->pieces[i].inSecOff, align);
+      // See comment above DeduplicatedCStringSection for how alignment is
+      // handled.
+      uint32_t pieceAlign = 1
+                            << countTrailingZeros(isec->align | piece.inSecOff);
       offset = alignTo(offset, pieceAlign);
-      isec->pieces[i].outSecOff = offset;
+      piece.outSecOff = offset;
       isec->isFinal = true;
       StringRef string = isec->getStringRef(i);
-      offset += string.size();
+      offset += string.size() + 1; // account for null terminator
     }
   }
   size = offset;
 }
+
 // Mergeable cstring literals are found under the __TEXT,__cstring section. In
 // contrast to ELF, which puts strings that need different alignments into
 // different sections, clang's Mach-O backend puts them all in one section.
 // Strings that need to be aligned have the .p2align directive emitted before
-// them, which simply translates into zero padding in the object file.
+// them, which simply translates into zero padding in the object file. In other
+// words, we have to infer the desired alignment of these cstrings from their
+// addresses.
 //
-// I *think* ld64 extracts the desired per-string alignment from this data by
-// preserving each string's offset from the last section-aligned address. I'm
-// not entirely certain since it doesn't seem consistent about doing this, and
-// in fact doesn't seem to be correct in general: we can in fact can induce ld64
-// to produce a crashing binary just by linking in an additional object file
-// that only contains a duplicate cstring at a different alignment. See PR50563
-// for details.
+// We differ slightly from ld64 in how we've chosen to align these cstrings.
+// Both LLD and ld64 preserve the number of trailing zeros in each cstring's
+// address in the input object files. When deduplicating identical cstrings,
+// both linkers pick the cstring whose address has more trailing zeros, and
+// preserve the alignment of that address in the final binary. However, ld64
+// goes a step further and also preserves the offset of the cstring from the
+// last section-aligned address.  I.e. if a cstring is at offset 18 in the
+// input, with a section alignment of 16, then both LLD and ld64 will ensure the
+// final address is 2-byte aligned (since 18 == 16 + 2). But ld64 will also
+// ensure that the final address is of the form 16 * k + 2 for some k.
 //
-// On x86_64, the cstrings we've seen so far that require special alignment are
-// all accessed by SIMD operations -- x86_64 requires SIMD accesses to be
-// 16-byte-aligned. arm64 also seems to require 16-byte-alignment in some cases
-// (PR50791), but I haven't tracked down the root cause. So for now, I'm just
-// aligning all strings to 16 bytes.  This is indeed wasteful, but
-// implementation-wise it's simpler than preserving per-string
-// alignment+offsets. It also avoids the aforementioned crash after
-// deduplication of differently-aligned strings.  Finally, the overhead is not
-// huge: using 16-byte alignment (vs no alignment) is only a 0.5% size overhead
-// when linking chromium_framework on x86_64.
-DeduplicatedCStringSection::DeduplicatedCStringSection()
-    : builder(StringTableBuilder::RAW, /*Alignment=*/16) {}
-
+// Note that ld64's heuristic means that a dedup'ed cstring's final address is
+// dependent on the order of the input object files. E.g. if in addition to the
+// cstring at offset 18 above, we have a duplicate one in another file with a
+// `.cstring` section alignment of 2 and an offset of zero, then ld64 will pick
+// the cstring from the object file earlier on the command line (since both have
+// the same number of trailing zeros in their address). So the final cstring may
+// either be at some address `16 * k + 2` or at some address `2 * k`.
+//
+// I've opted not to follow this behavior primarily for implementation
+// simplicity, and secondarily to save a few more bytes. It's not clear to me
+// that preserving the section alignment + offset is ever necessary, and there
+// are many cases that are clearly redundant. In particular, if an x86_64 object
+// file contains some strings that are accessed via SIMD instructions, then the
+// .cstring section in the object file will be 16-byte-aligned (since SIMD
+// requires its operand addresses to be 16-byte aligned). However, there will
+// typically also be other cstrings in the same file that aren't used via SIMD
+// and don't need this alignment. They will be emitted at some arbitrary address
+// `A`, but ld64 will treat them as being 16-byte aligned with an offset of `16
+// % A`.
 void DeduplicatedCStringSection::finalizeContents() {
-  // Add all string pieces to the string table builder to create section
-  // contents.
-  for (const CStringInputSection *isec : inputs)
-    for (size_t i = 0, e = isec->pieces.size(); i != e; ++i)
-      if (isec->pieces[i].live)
-        builder.add(isec->getCachedHashStringRef(i));
-
-  // Fix the string table content. After this, the contents will never change.
-  builder.finalizeInOrder();
-
-  // finalize() fixed tail-optimized strings, so we can now get
-  // offsets of strings. Get an offset for each string and save it
-  // to a corresponding SectionPiece for easy access.
+  // Find the largest alignment required for each string.
+  for (const CStringInputSection *isec : inputs) {
+    for (const auto &[i, piece] : llvm::enumerate(isec->pieces)) {
+      if (!piece.live)
+        continue;
+      auto s = isec->getCachedHashStringRef(i);
+      assert(isec->align != 0);
+      uint8_t trailingZeros = countTrailingZeros(isec->align | piece.inSecOff);
+      auto it = stringOffsetMap.insert(
+          std::make_pair(s, StringOffset(trailingZeros)));
+      if (!it.second && it.first->second.trailingZeros < trailingZeros)
+        it.first->second.trailingZeros = trailingZeros;
+    }
+  }
+
+  // Assign an offset for each string and save it to the corresponding
+  // StringPieces for easy access.
   for (CStringInputSection *isec : inputs) {
-    for (size_t i = 0, e = isec->pieces.size(); i != e; ++i) {
-      if (!isec->pieces[i].live)
+    for (const auto &[i, piece] : llvm::enumerate(isec->pieces)) {
+      if (!piece.live)
         continue;
-      isec->pieces[i].outSecOff =
-          builder.getOffset(isec->getCachedHashStringRef(i));
-      isec->isFinal = true;
+      auto s = isec->getCachedHashStringRef(i);
+      auto it = stringOffsetMap.find(s);
+      assert(it != stringOffsetMap.end());
+      StringOffset &offsetInfo = it->second;
+      if (offsetInfo.outSecOff == UINT64_MAX) {
+        offsetInfo.outSecOff = alignTo(size, 1ULL << offsetInfo.trailingZeros);
+        size =
+            offsetInfo.outSecOff + s.size() + 1; // account for null terminator
+      }
+      piece.outSecOff = offsetInfo.outSecOff;
     }
+    isec->isFinal = true;
+  }
+}
+
+void DeduplicatedCStringSection::writeTo(uint8_t *buf) const {
+  for (const auto &p : stringOffsetMap) {
+    StringRef data = p.first.val();
+    uint64_t off = p.second.outSecOff;
+    if (!data.empty())
+      memcpy(buf + off, data.data(), data.size());
   }
 }
 
+DeduplicatedCStringSection::StringOffset
+DeduplicatedCStringSection::getStringOffset(StringRef str) const {
+  // StringPiece uses 31 bits to store the hashes, so we replicate that
+  uint32_t hash = xxHash64(str) & 0x7fffffff;
+  auto offset = stringOffsetMap.find(CachedHashStringRef(str, hash));
+  assert(offset != stringOffsetMap.end() &&
+         "Looked-up strings should always exist in section");
+  return offset->second;
+}
+
 // This section is actually emitted as __TEXT,__const by ld64, but clang may
 // emit input sections of that name, and LLD doesn't currently support mixing
 // synthetic and concat-type OutputSections. To work around this, I've given
@@ -1456,10 +1816,152 @@ void WordLiteralSection::writeTo(uint8_t *buf) const {
     memcpy(buf + p.second * 4, &p.first, 4);
 }
 
+ObjCImageInfoSection::ObjCImageInfoSection()
+    : SyntheticSection(segment_names::data, section_names::objCImageInfo) {}
+
+ObjCImageInfoSection::ImageInfo
+ObjCImageInfoSection::parseImageInfo(const InputFile *file) {
+  ImageInfo info;
+  ArrayRef<uint8_t> data = file->objCImageInfo;
+  // The image info struct has the following layout:
+  // struct {
+  //   uint32_t version;
+  //   uint32_t flags;
+  // };
+  if (data.size() < 8) {
+    warn(toString(file) + ": invalid __objc_imageinfo size");
+    return info;
+  }
+
+  auto *buf = reinterpret_cast<const uint32_t *>(data.data());
+  if (read32le(buf) != 0) {
+    warn(toString(file) + ": invalid __objc_imageinfo version");
+    return info;
+  }
+
+  uint32_t flags = read32le(buf + 1);
+  info.swiftVersion = (flags >> 8) & 0xff;
+  info.hasCategoryClassProperties = flags & 0x40;
+  return info;
+}
+
+static std::string swiftVersionString(uint8_t version) {
+  switch (version) {
+    case 1:
+      return "1.0";
+    case 2:
+      return "1.1";
+    case 3:
+      return "2.0";
+    case 4:
+      return "3.0";
+    case 5:
+      return "4.0";
+    default:
+      return ("0x" + Twine::utohexstr(version)).str();
+  }
+}
+
+// Validate each object file's __objc_imageinfo and use them to generate the
+// image info for the output binary. Only two pieces of info are relevant:
+// 1. The Swift version (should be identical across inputs)
+// 2. `bool hasCategoryClassProperties` (true only if true for all inputs)
+void ObjCImageInfoSection::finalizeContents() {
+  assert(files.size() != 0); // should have already been checked via isNeeded()
+
+  info.hasCategoryClassProperties = true;
+  const InputFile *firstFile;
+  for (auto file : files) {
+    ImageInfo inputInfo = parseImageInfo(file);
+    info.hasCategoryClassProperties &= inputInfo.hasCategoryClassProperties;
+
+    // swiftVersion 0 means no Swift is present, so no version checking required
+    if (inputInfo.swiftVersion == 0)
+      continue;
+
+    if (info.swiftVersion != 0 && info.swiftVersion != inputInfo.swiftVersion) {
+      error("Swift version mismatch: " + toString(firstFile) + " has version " +
+            swiftVersionString(info.swiftVersion) + " but " + toString(file) +
+            " has version " + swiftVersionString(inputInfo.swiftVersion));
+    } else {
+      info.swiftVersion = inputInfo.swiftVersion;
+      firstFile = file;
+    }
+  }
+}
+
+void ObjCImageInfoSection::writeTo(uint8_t *buf) const {
+  uint32_t flags = info.hasCategoryClassProperties ? 0x40 : 0x0;
+  flags |= info.swiftVersion << 8;
+  write32le(buf + 4, flags);
+}
+
+InitOffsetsSection::InitOffsetsSection()
+    : SyntheticSection(segment_names::text, section_names::initOffsets) {
+  flags = S_INIT_FUNC_OFFSETS;
+  align = 4; // This section contains 32-bit integers.
+}
+
+uint64_t InitOffsetsSection::getSize() const {
+  size_t count = 0;
+  for (const ConcatInputSection *isec : sections)
+    count += isec->relocs.size();
+  return count * sizeof(uint32_t);
+}
+
+void InitOffsetsSection::writeTo(uint8_t *buf) const {
+  // FIXME: Add function specified by -init when that argument is implemented.
+  for (ConcatInputSection *isec : sections) {
+    for (const Reloc &rel : isec->relocs) {
+      const Symbol *referent = rel.referent.dyn_cast<Symbol *>();
+      assert(referent && "section relocation should have been rejected");
+      uint64_t offset = referent->getVA() - in.header->addr;
+      // FIXME: Can we handle this gracefully?
+      if (offset > UINT32_MAX)
+        fatal(isec->getLocation(rel.offset) + ": offset to initializer " +
+              referent->getName() + " (" + utohexstr(offset) +
+              ") does not fit in 32 bits");
+
+      // Entries need to be added in the order they appear in the section, but
+      // relocations aren't guaranteed to be sorted.
+      size_t index = rel.offset >> target->p2WordSize;
+      write32le(&buf[index * sizeof(uint32_t)], offset);
+    }
+    buf += isec->relocs.size() * sizeof(uint32_t);
+  }
+}
+
+// The inputs are __mod_init_func sections, which contain pointers to
+// initializer functions, therefore all relocations should be of the UNSIGNED
+// type. InitOffsetsSection stores offsets, so if the initializer's address is
+// not known at link time, stub-indirection has to be used.
+void InitOffsetsSection::setUp() {
+  for (const ConcatInputSection *isec : sections) {
+    for (const Reloc &rel : isec->relocs) {
+      RelocAttrs attrs = target->getRelocAttrs(rel.type);
+      if (!attrs.hasAttr(RelocAttrBits::UNSIGNED))
+        error(isec->getLocation(rel.offset) +
+              ": unsupported relocation type: " + attrs.name);
+      if (rel.addend != 0)
+        error(isec->getLocation(rel.offset) +
+              ": relocation addend is not representable in __init_offsets");
+      if (rel.referent.is<InputSection *>())
+        error(isec->getLocation(rel.offset) +
+              ": unexpected section relocation");
+
+      Symbol *sym = rel.referent.dyn_cast<Symbol *>();
+      if (auto *undefined = dyn_cast<Undefined>(sym))
+        treatUndefinedSymbol(*undefined, isec, rel.offset);
+      if (needsBinding(sym))
+        in.stubs->addEntry(sym);
+    }
+  }
+}
+
 void macho::createSyntheticSymbols() {
   auto addHeaderSymbol = [](const char *name) {
     symtab->addSynthetic(name, in.header->isec, /*value=*/0,
-                         /*privateExtern=*/true, /*includeInSymtab=*/false,
+                         /*isPrivateExtern=*/true, /*includeInSymtab=*/false,
                          /*referencedDynamically=*/false);
   };
 
@@ -1472,11 +1974,11 @@ void macho::createSyntheticSymbols() {
     // Otherwise, it's an absolute symbol.
     if (config->isPic)
       symtab->addSynthetic("__mh_execute_header", in.header->isec, /*value=*/0,
-                           /*privateExtern=*/false, /*includeInSymtab=*/true,
+                           /*isPrivateExtern=*/false, /*includeInSymtab=*/true,
                            /*referencedDynamically=*/true);
     else
       symtab->addSynthetic("__mh_execute_header", /*isec=*/nullptr, /*value=*/0,
-                           /*privateExtern=*/false, /*includeInSymtab=*/true,
+                           /*isPrivateExtern=*/false, /*includeInSymtab=*/true,
                            /*referencedDynamically=*/true);
     break;
 
@@ -1508,5 +2010,247 @@ void macho::createSyntheticSymbols() {
   addHeaderSymbol("___dso_handle");
 }
 
+ChainedFixupsSection::ChainedFixupsSection()
+    : LinkEditSection(segment_names::linkEdit, section_names::chainFixups) {}
+
+bool ChainedFixupsSection::isNeeded() const {
+  assert(config->emitChainedFixups);
+  // dyld always expects LC_DYLD_CHAINED_FIXUPS to point to a valid
+  // dyld_chained_fixups_header, so we create this section even if there aren't
+  // any fixups.
+  return true;
+}
+
+static bool needsWeakBind(const Symbol &sym) {
+  if (auto *dysym = dyn_cast<DylibSymbol>(&sym))
+    return dysym->isWeakDef();
+  if (auto *defined = dyn_cast<Defined>(&sym))
+    return defined->isExternalWeakDef();
+  return false;
+}
+
+void ChainedFixupsSection::addBinding(const Symbol *sym,
+                                      const InputSection *isec, uint64_t offset,
+                                      int64_t addend) {
+  locations.emplace_back(isec, offset);
+  int64_t outlineAddend = (addend < 0 || addend > 0xFF) ? addend : 0;
+  auto [it, inserted] = bindings.insert(
+      {{sym, outlineAddend}, static_cast<uint32_t>(bindings.size())});
+
+  if (inserted) {
+    symtabSize += sym->getName().size() + 1;
+    hasWeakBind = hasWeakBind || needsWeakBind(*sym);
+    if (!isInt<23>(outlineAddend))
+      needsLargeAddend = true;
+    else if (outlineAddend != 0)
+      needsAddend = true;
+  }
+}
+
+std::pair<uint32_t, uint8_t>
+ChainedFixupsSection::getBinding(const Symbol *sym, int64_t addend) const {
+  int64_t outlineAddend = (addend < 0 || addend > 0xFF) ? addend : 0;
+  auto it = bindings.find({sym, outlineAddend});
+  assert(it != bindings.end() && "binding not found in the imports table");
+  if (outlineAddend == 0)
+    return {it->second, addend};
+  return {it->second, 0};
+}
+
+static size_t writeImport(uint8_t *buf, int format, uint32_t libOrdinal,
+                          bool weakRef, uint32_t nameOffset, int64_t addend) {
+  switch (format) {
+  case DYLD_CHAINED_IMPORT: {
+    auto *import = reinterpret_cast<dyld_chained_import *>(buf);
+    import->lib_ordinal = libOrdinal;
+    import->weak_import = weakRef;
+    import->name_offset = nameOffset;
+    return sizeof(dyld_chained_import);
+  }
+  case DYLD_CHAINED_IMPORT_ADDEND: {
+    auto *import = reinterpret_cast<dyld_chained_import_addend *>(buf);
+    import->lib_ordinal = libOrdinal;
+    import->weak_import = weakRef;
+    import->name_offset = nameOffset;
+    import->addend = addend;
+    return sizeof(dyld_chained_import_addend);
+  }
+  case DYLD_CHAINED_IMPORT_ADDEND64: {
+    auto *import = reinterpret_cast<dyld_chained_import_addend64 *>(buf);
+    import->lib_ordinal = libOrdinal;
+    import->weak_import = weakRef;
+    import->name_offset = nameOffset;
+    import->addend = addend;
+    return sizeof(dyld_chained_import_addend64);
+  }
+  default:
+    llvm_unreachable("Unknown import format");
+  }
+}
+
+size_t ChainedFixupsSection::SegmentInfo::getSize() const {
+  assert(pageStarts.size() > 0 && "SegmentInfo for segment with no fixups?");
+  return alignTo<8>(sizeof(dyld_chained_starts_in_segment) +
+                    pageStarts.back().first * sizeof(uint16_t));
+}
+
+size_t ChainedFixupsSection::SegmentInfo::writeTo(uint8_t *buf) const {
+  auto *segInfo = reinterpret_cast<dyld_chained_starts_in_segment *>(buf);
+  segInfo->size = getSize();
+  segInfo->page_size = target->getPageSize();
+  // FIXME: Use DYLD_CHAINED_PTR_64_OFFSET on newer OS versions.
+  segInfo->pointer_format = DYLD_CHAINED_PTR_64;
+  segInfo->segment_offset = oseg->addr - in.header->addr;
+  segInfo->max_valid_pointer = 0; // not used on 64-bit
+  segInfo->page_count = pageStarts.back().first + 1;
+
+  uint16_t *starts = segInfo->page_start;
+  for (size_t i = 0; i < segInfo->page_count; ++i)
+    starts[i] = DYLD_CHAINED_PTR_START_NONE;
+
+  for (auto [pageIdx, startAddr] : pageStarts)
+    starts[pageIdx] = startAddr;
+  return segInfo->size;
+}
+
+static size_t importEntrySize(int format) {
+  switch (format) {
+  case DYLD_CHAINED_IMPORT:
+    return sizeof(dyld_chained_import);
+  case DYLD_CHAINED_IMPORT_ADDEND:
+    return sizeof(dyld_chained_import_addend);
+  case DYLD_CHAINED_IMPORT_ADDEND64:
+    return sizeof(dyld_chained_import_addend64);
+  default:
+    llvm_unreachable("Unknown import format");
+  }
+}
+
+// This is step 3 of the algorithm described in the class comment of
+// ChainedFixupsSection.
+//
+// LC_DYLD_CHAINED_FIXUPS data consists of (in this order):
+// * A dyld_chained_fixups_header
+// * A dyld_chained_starts_in_image
+// * One dyld_chained_starts_in_segment per segment
+// * List of all imports (dyld_chained_import, dyld_chained_import_addend, or
+//   dyld_chained_import_addend64)
+// * Names of imported symbols
+void ChainedFixupsSection::writeTo(uint8_t *buf) const {
+  auto *header = reinterpret_cast<dyld_chained_fixups_header *>(buf);
+  header->fixups_version = 0;
+  header->imports_count = bindings.size();
+  header->imports_format = importFormat;
+  header->symbols_format = 0;
+
+  buf += alignTo<8>(sizeof(*header));
+
+  auto curOffset = [&buf, &header]() -> uint32_t {
+    return buf - reinterpret_cast<uint8_t *>(header);
+  };
+
+  header->starts_offset = curOffset();
+
+  auto *imageInfo = reinterpret_cast<dyld_chained_starts_in_image *>(buf);
+  imageInfo->seg_count = outputSegments.size();
+  uint32_t *segStarts = imageInfo->seg_info_offset;
+
+  // dyld_chained_starts_in_image ends in a flexible array member containing an
+  // uint32_t for each segment. Leave room for it, and fill it via segStarts.
+  buf += alignTo<8>(offsetof(dyld_chained_starts_in_image, seg_info_offset) +
+                    outputSegments.size() * sizeof(uint32_t));
+
+  // Initialize all offsets to 0, which indicates that the segment does not have
+  // fixups. Those that do have them will be filled in below.
+  for (size_t i = 0; i < outputSegments.size(); ++i)
+    segStarts[i] = 0;
+
+  for (const SegmentInfo &seg : fixupSegments) {
+    segStarts[seg.oseg->index] = curOffset() - header->starts_offset;
+    buf += seg.writeTo(buf);
+  }
+
+  // Write imports table.
+  header->imports_offset = curOffset();
+  uint64_t nameOffset = 0;
+  for (auto [import, idx] : bindings) {
+    const Symbol &sym = *import.first;
+    int16_t libOrdinal = needsWeakBind(sym)
+                             ? (int64_t)BIND_SPECIAL_DYLIB_WEAK_LOOKUP
+                             : ordinalForSymbol(sym);
+    buf += writeImport(buf, importFormat, libOrdinal, sym.isWeakRef(),
+                       nameOffset, import.second);
+    nameOffset += sym.getName().size() + 1;
+  }
+
+  // Write imported symbol names.
+  header->symbols_offset = curOffset();
+  for (auto [import, idx] : bindings) {
+    StringRef name = import.first->getName();
+    memcpy(buf, name.data(), name.size());
+    buf += name.size() + 1; // account for null terminator
+  }
+
+  assert(curOffset() == getRawSize());
+}
+
+// This is step 2 of the algorithm described in the class comment of
+// ChainedFixupsSection.
+void ChainedFixupsSection::finalizeContents() {
+  assert(target->wordSize == 8 && "Only 64-bit platforms are supported");
+  assert(config->emitChainedFixups);
+
+  if (!isUInt<32>(symtabSize))
+    error("cannot encode chained fixups: imported symbols table size " +
+          Twine(symtabSize) + " exceeds 4 GiB");
+
+  if (needsLargeAddend || !isUInt<23>(symtabSize))
+    importFormat = DYLD_CHAINED_IMPORT_ADDEND64;
+  else if (needsAddend)
+    importFormat = DYLD_CHAINED_IMPORT_ADDEND;
+  else
+    importFormat = DYLD_CHAINED_IMPORT;
+
+  for (Location &loc : locations)
+    loc.offset =
+        loc.isec->parent->getSegmentOffset() + loc.isec->getOffset(loc.offset);
+
+  llvm::sort(locations, [](const Location &a, const Location &b) {
+    const OutputSegment *segA = a.isec->parent->parent;
+    const OutputSegment *segB = b.isec->parent->parent;
+    if (segA == segB)
+      return a.offset < b.offset;
+    return segA->addr < segB->addr;
+  });
+
+  auto sameSegment = [](const Location &a, const Location &b) {
+    return a.isec->parent->parent == b.isec->parent->parent;
+  };
+
+  const uint64_t pageSize = target->getPageSize();
+  for (size_t i = 0, count = locations.size(); i < count;) {
+    const Location &firstLoc = locations[i];
+    fixupSegments.emplace_back(firstLoc.isec->parent->parent);
+    while (i < count && sameSegment(locations[i], firstLoc)) {
+      uint32_t pageIdx = locations[i].offset / pageSize;
+      fixupSegments.back().pageStarts.emplace_back(
+          pageIdx, locations[i].offset % pageSize);
+      ++i;
+      while (i < count && sameSegment(locations[i], firstLoc) &&
+             locations[i].offset / pageSize == pageIdx)
+        ++i;
+    }
+  }
+
+  // Compute expected encoded size.
+  size = alignTo<8>(sizeof(dyld_chained_fixups_header));
+  size += alignTo<8>(offsetof(dyld_chained_starts_in_image, seg_info_offset) +
+                     outputSegments.size() * sizeof(uint32_t));
+  for (const SegmentInfo &seg : fixupSegments)
+    size += seg.getSize();
+  size += importEntrySize(importFormat) * bindings.size();
+  size += symtabSize;
+}
+
 template SymtabSection *macho::makeSymtabSection<LP64>(StringTableSection &);
 template SymtabSection *macho::makeSymtabSection<ILP32>(StringTableSection &);
index bbb7adc..b17e991 100644 (file)
@@ -20,7 +20,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/Hashing.h"
 #include "llvm/ADT/SetVector.h"
-#include "llvm/MC/StringTableBuilder.h"
+#include "llvm/BinaryFormat/MachO.h"
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/raw_ostream.h"
 
@@ -30,8 +30,7 @@ namespace llvm {
 class DWARFUnit;
 } // namespace llvm
 
-namespace lld {
-namespace macho {
+namespace lld::macho {
 
 class Defined;
 class DylibSymbol;
@@ -62,12 +61,14 @@ public:
     align = target->wordSize;
   }
 
+  // Implementations of this method can assume that the regular (non-__LINKEDIT)
+  // sections already have their addresses assigned.
   virtual void finalizeContents() {}
 
   // Sections in __LINKEDIT are special: their offsets are recorded in the
   // load commands like LC_DYLD_INFO_ONLY and LC_SYMTAB, instead of in section
   // headers.
-  bool isHidden() const override final { return true; }
+  bool isHidden() const final { return true; }
 
   virtual uint64_t getRawSize() const = 0;
 
@@ -77,9 +78,7 @@ public:
   //
   // NOTE: This assumes that the extra bytes required for alignment can be
   // zero-valued bytes.
-  uint64_t getSize() const override final {
-    return llvm::alignTo(getRawSize(), align);
-  }
+  uint64_t getSize() const final { return llvm::alignTo(getRawSize(), align); }
 };
 
 // The header of the Mach-O file, which must have a file offset of zero.
@@ -103,6 +102,7 @@ class PageZeroSection final : public SyntheticSection {
 public:
   PageZeroSection();
   bool isHidden() const override { return true; }
+  bool isNeeded() const override { return target->pageZeroSize != 0; }
   uint64_t getSize() const override { return target->pageZeroSize; }
   uint64_t getFileSize() const override { return 0; }
   void writeTo(uint8_t *buf) const override {}
@@ -189,13 +189,13 @@ public:
   bool isNeeded() const override { return !bindingsMap.empty(); }
   void writeTo(uint8_t *buf) const override;
 
-  void addEntry(const DylibSymbol *dysym, const InputSection *isec,
-                uint64_t offset, int64_t addend = 0) {
+  void addEntry(const Symbol *dysym, const InputSection *isec, uint64_t offset,
+                int64_t addend = 0) {
     bindingsMap[dysym].emplace_back(addend, Location(isec, offset));
   }
 
 private:
-  BindingsMap<const DylibSymbol *> bindingsMap;
+  BindingsMap<const Symbol *> bindingsMap;
   SmallVector<char, 128> contents;
 };
 
@@ -269,6 +269,12 @@ private:
 // order that the weak bindings may overwrite the non-lazy bindings if an
 // appropriate symbol is found at runtime. However, the bound addresses will
 // still be written (non-lazily) into the LazyPointerSection.
+//
+// Symbols are always bound eagerly when chained fixups are used. In that case,
+// StubsSection contains indirect jumps to addresses stored in the GotSection.
+// The GOT directly contains the fixup entries, which will be replaced by the
+// address of the target symbols on load. LazyPointerSection and
+// StubHelperSection are not used.
 
 class StubsSection final : public SyntheticSection {
 public:
@@ -278,9 +284,9 @@ public:
   void finalize() override;
   void writeTo(uint8_t *buf) const override;
   const llvm::SetVector<Symbol *> &getEntries() const { return entries; }
-  // Returns whether the symbol was added. Note that every stubs entry will
-  // have a corresponding entry in the LazyPointerSection.
-  bool addEntry(Symbol *);
+  // Creates a stub for the symbol and the corresponding entry in the
+  // LazyPointerSection.
+  void addEntry(Symbol *);
   uint64_t getVA(uint32_t stubsIndex) const {
     assert(isFinal || target->usesThunks());
     // ConcatOutputSection::finalize() can seek the address of a
@@ -303,12 +309,36 @@ public:
   bool isNeeded() const override;
   void writeTo(uint8_t *buf) const override;
 
-  void setup();
+  void setUp();
 
   DylibSymbol *stubBinder = nullptr;
   Defined *dyldPrivate = nullptr;
 };
 
+// Objective-C stubs are hoisted objc_msgSend calls per selector called in the
+// program. Apple Clang produces undefined symbols to each stub, such as
+// '_objc_msgSend$foo', which are then synthesized by the linker. The stubs
+// load the particular selector 'foo' from __objc_selrefs, setting it to the
+// first argument of the objc_msgSend call, and then jumps to objc_msgSend. The
+// actual stub contents are mirrored from ld64.
+class ObjCStubsSection final : public SyntheticSection {
+public:
+  ObjCStubsSection();
+  void addEntry(Symbol *sym);
+  uint64_t getSize() const override;
+  bool isNeeded() const override { return !symbols.empty(); }
+  void finalize() override { isec->isFinal = true; }
+  void writeTo(uint8_t *buf) const override;
+  void setUp();
+
+  static constexpr llvm::StringLiteral symbolPrefix = "_objc_msgSend$";
+
+private:
+  std::vector<Defined *> symbols;
+  std::vector<uint32_t> offsets;
+  int objcMsgSendGotIndex = 0;
+};
+
 // Note that this section may also be targeted by non-lazy bindings. In
 // particular, this happens when branch relocations target weak symbols.
 class LazyPointerSection final : public SyntheticSection {
@@ -317,6 +347,9 @@ public:
   uint64_t getSize() const override;
   bool isNeeded() const override;
   void writeTo(uint8_t *buf) const override;
+  uint64_t getVA(uint32_t index) const {
+    return addr + (index << target->p2WordSize);
+  }
 };
 
 class LazyBindingSection final : public LinkEditSection {
@@ -328,13 +361,13 @@ public:
   void writeTo(uint8_t *buf) const override;
   // Note that every entry here will by referenced by a corresponding entry in
   // the StubHelperSection.
-  void addEntry(DylibSymbol *dysym);
-  const llvm::SetVector<DylibSymbol *> &getEntries() const { return entries; }
+  void addEntry(Symbol *dysym);
+  const llvm::SetVector<Symbol *> &getEntries() const { return entries; }
 
 private:
-  uint32_t encode(const DylibSymbol &);
+  uint32_t encode(const Symbol &);
 
-  llvm::SetVector<DylibSymbol *> entries;
+  llvm::SetVector<Symbol *> entries;
   SmallVector<char, 128> contents;
   llvm::raw_svector_ostream os{contents};
 };
@@ -345,6 +378,7 @@ public:
   ExportSection();
   void finalizeContents() override;
   uint64_t getRawSize() const override { return size; }
+  bool isNeeded() const override { return size; }
   void writeTo(uint8_t *buf) const override;
 
   bool hasWeakSymbol = false;
@@ -354,8 +388,9 @@ private:
   size_t size = 0;
 };
 
-// Stores 'data in code' entries that describe the locations of
-// data regions inside code sections.
+// Stores 'data in code' entries that describe the locations of data regions
+// inside code sections. This is used by llvm-objdump to distinguish jump tables
+// and stop them from being disassembled as instructions.
 class DataInCodeSection final : public LinkEditSection {
 public:
   DataInCodeSection();
@@ -431,7 +466,7 @@ public:
   uint32_t getNumUndefinedSymbols() const { return undefinedSymbols.size(); }
 
 private:
-  void emitBeginSourceStab(llvm::DWARFUnit *compileUnit);
+  void emitBeginSourceStab(StringRef);
   void emitEndSourceStab();
   void emitObjectFileStab(ObjFile *);
   void emitEndFunStab(Defined *);
@@ -476,6 +511,8 @@ public:
 // The code signature comes at the very end of the linked output file.
 class CodeSignatureSection final : public LinkEditSection {
 public:
+  // NOTE: These values are duplicated in llvm-objcopy's MachO/Object.h file
+  // and any changes here, should be repeated there.
   static constexpr uint8_t blockSizeShift = 12;
   static constexpr size_t blockSize = (1 << blockSizeShift); // 4 KiB
   static constexpr size_t hashSize = 256 / 8;
@@ -510,7 +547,7 @@ private:
 
 class CStringSection : public SyntheticSection {
 public:
-  CStringSection();
+  CStringSection(const char *name);
   void addInput(CStringInputSection *);
   uint64_t getSize() const override { return size; }
   virtual void finalizeContents();
@@ -525,13 +562,23 @@ private:
 
 class DeduplicatedCStringSection final : public CStringSection {
 public:
-  DeduplicatedCStringSection();
-  uint64_t getSize() const override { return builder.getSize(); }
+  DeduplicatedCStringSection(const char *name) : CStringSection(name){};
+  uint64_t getSize() const override { return size; }
   void finalizeContents() override;
-  void writeTo(uint8_t *buf) const override { builder.write(buf); }
+  void writeTo(uint8_t *buf) const override;
+
+  struct StringOffset {
+    uint8_t trailingZeros;
+    uint64_t outSecOff = UINT64_MAX;
+
+    explicit StringOffset(uint8_t zeros) : trailingZeros(zeros) {}
+  };
+
+  StringOffset getStringOffset(StringRef str) const;
 
 private:
-  llvm::StringTableBuilder builder;
+  llvm::DenseMap<llvm::CachedHashStringRef, StringOffset> stringOffsetMap;
+  size_t size = 0;
 };
 
 /*
@@ -543,7 +590,7 @@ public:
   using UInt128 = std::pair<uint64_t, uint64_t>;
   // I don't think the standard guarantees the size of a pair, so let's make
   // sure it's exact -- that way we can construct it via `mmap`.
-  static_assert(sizeof(UInt128) == 16, "");
+  static_assert(sizeof(UInt128) == 16);
 
   WordLiteralSection();
   void addInput(WordLiteralInputSection *);
@@ -560,16 +607,16 @@ public:
            !literal8Map.empty();
   }
 
-  uint64_t getLiteral16Offset(const uint8_t *buf) const {
+  uint64_t getLiteral16Offset(uintptr_t buf) const {
     return literal16Map.at(*reinterpret_cast<const UInt128 *>(buf)) * 16;
   }
 
-  uint64_t getLiteral8Offset(const uint8_t *buf) const {
+  uint64_t getLiteral8Offset(uintptr_t buf) const {
     return literal16Map.size() * 16 +
            literal8Map.at(*reinterpret_cast<const uint64_t *>(buf)) * 8;
   }
 
-  uint64_t getLiteral4Offset(const uint8_t *buf) const {
+  uint64_t getLiteral4Offset(uintptr_t buf) const {
     return literal16Map.size() * 16 + literal8Map.size() * 8 +
            literal4Map.at(*reinterpret_cast<const uint32_t *>(buf)) * 4;
   }
@@ -588,9 +635,163 @@ private:
   std::unordered_map<uint32_t, uint64_t> literal4Map;
 };
 
+class ObjCImageInfoSection final : public SyntheticSection {
+public:
+  ObjCImageInfoSection();
+  bool isNeeded() const override { return !files.empty(); }
+  uint64_t getSize() const override { return 8; }
+  void addFile(const InputFile *file) {
+    assert(!file->objCImageInfo.empty());
+    files.push_back(file);
+  }
+  void finalizeContents();
+  void writeTo(uint8_t *buf) const override;
+
+private:
+  struct ImageInfo {
+    uint8_t swiftVersion = 0;
+    bool hasCategoryClassProperties = false;
+  } info;
+  static ImageInfo parseImageInfo(const InputFile *);
+  std::vector<const InputFile *> files; // files with image info
+};
+
+// This section stores 32-bit __TEXT segment offsets of initializer functions.
+//
+// The compiler stores pointers to initializers in __mod_init_func. These need
+// to be fixed up at load time, which takes time and dirties memory. By
+// synthesizing InitOffsetsSection from them, this data can live in the
+// read-only __TEXT segment instead. This section is used by default when
+// chained fixups are enabled.
+//
+// There is no similar counterpart to __mod_term_func, as that section is
+// deprecated, and static destructors are instead handled by registering them
+// via __cxa_atexit from an autogenerated initializer function (see D121736).
+class InitOffsetsSection final : public SyntheticSection {
+public:
+  InitOffsetsSection();
+  bool isNeeded() const override { return !sections.empty(); }
+  uint64_t getSize() const override;
+  void writeTo(uint8_t *buf) const override;
+  void setUp();
+
+  void addInput(ConcatInputSection *isec) { sections.push_back(isec); }
+  const std::vector<ConcatInputSection *> &inputs() const { return sections; }
+
+private:
+  std::vector<ConcatInputSection *> sections;
+};
+
+// Chained fixups are a replacement for classic dyld opcodes. In this format,
+// most of the metadata necessary for binding symbols and rebasing addresses is
+// stored directly in the memory location that will have the fixup applied.
+//
+// The fixups form singly linked lists; each one covering a single page in
+// memory. The __LINKEDIT,__chainfixups section stores the page offset of the
+// first fixup of each page; the rest can be found by walking the chain using
+// the offset that is embedded in each entry.
+//
+// This setup allows pages to be relocated lazily at page-in time and without
+// being dirtied. The kernel can discard and load them again as needed. This
+// technique, called page-in linking, was introduced in macOS 13.
+//
+// The benefits of this format are:
+//  - smaller __LINKEDIT segment, as most of the fixup information is stored in
+//    the data segment
+//  - faster startup, since not all relocations need to be done upfront
+//  - slightly lower memory usage, as fewer pages are dirtied
+//
+// Userspace x86_64 and arm64 binaries have two types of fixup entries:
+//   - Rebase entries contain an absolute address, to which the object's load
+//     address will be added to get the final value. This is used for loading
+//     the address of a symbol defined in the same binary.
+//   - Binding entries are mostly used for symbols imported from other dylibs,
+//     but for weakly bound and interposable symbols as well. They are looked up
+//     by a (symbol name, library) pair stored in __chainfixups. This import
+//     entry also encodes whether the import is weak (i.e. if the symbol is
+//     missing, it should be set to null instead of producing a load error).
+//     The fixup encodes an ordinal associated with the import, and an optional
+//     addend.
+//
+// The entries are tightly packed 64-bit bitfields. One of the bits specifies
+// which kind of fixup to interpret them as.
+//
+// LLD generates the fixup data in 5 stages:
+//   1. While scanning relocations, we make a note of each location that needs
+//      a fixup by calling addRebase() or addBinding(). During this, we assign
+//      a unique ordinal for each (symbol name, library, addend) import tuple.
+//   2. After addresses have been assigned to all sections, and thus the memory
+//      layout of the linked image is final; finalizeContents() is called. Here,
+//      the page offsets of the chain start entries are calculated.
+//   3. ChainedFixupsSection::writeTo() writes the page start offsets and the
+//      imports table to the output file.
+//   4. Each section's fixup entries are encoded and written to disk in
+//      ConcatInputSection::writeTo(), but without writing the offsets that form
+//      the chain.
+//   5. Finally, each page's (which might correspond to multiple sections)
+//      fixups are linked together in Writer::buildFixupChains().
+class ChainedFixupsSection final : public LinkEditSection {
+public:
+  ChainedFixupsSection();
+  void finalizeContents() override;
+  uint64_t getRawSize() const override { return size; }
+  bool isNeeded() const override;
+  void writeTo(uint8_t *buf) const override;
+
+  void addRebase(const InputSection *isec, uint64_t offset) {
+    locations.emplace_back(isec, offset);
+  }
+  void addBinding(const Symbol *dysym, const InputSection *isec,
+                  uint64_t offset, int64_t addend = 0);
+
+  void setHasNonWeakDefinition() { hasNonWeakDef = true; }
+
+  // Returns an (ordinal, inline addend) tuple used by dyld_chained_ptr_64_bind.
+  std::pair<uint32_t, uint8_t> getBinding(const Symbol *sym,
+                                          int64_t addend) const;
+
+  const std::vector<Location> &getLocations() const { return locations; }
+
+  bool hasWeakBinding() const { return hasWeakBind; }
+  bool hasNonWeakDefinition() const { return hasNonWeakDef; }
+
+private:
+  // Location::offset initially stores the offset within an InputSection, but
+  // contains output segment offsets after finalizeContents().
+  std::vector<Location> locations;
+  // (target symbol, addend) => import ordinal
+  llvm::MapVector<std::pair<const Symbol *, int64_t>, uint32_t> bindings;
+
+  struct SegmentInfo {
+    SegmentInfo(const OutputSegment *oseg) : oseg(oseg) {}
+
+    const OutputSegment *oseg;
+    // (page index, fixup starts offset)
+    llvm::SmallVector<std::pair<uint16_t, uint16_t>> pageStarts;
+
+    size_t getSize() const;
+    size_t writeTo(uint8_t *buf) const;
+  };
+  llvm::SmallVector<SegmentInfo, 4> fixupSegments;
+
+  size_t symtabSize = 0;
+  size_t size = 0;
+
+  bool needsAddend = false;
+  bool needsLargeAddend = false;
+  bool hasWeakBind = false;
+  bool hasNonWeakDef = false;
+  llvm::MachO::ChainedImportFormat importFormat;
+};
+
+void writeChainedRebase(uint8_t *buf, uint64_t targetVA);
+void writeChainedFixup(uint8_t *buf, const Symbol *sym, int64_t addend);
+
 struct InStruct {
+  const uint8_t *bufferStart = nullptr;
   MachHeaderSection *header = nullptr;
   CStringSection *cStringSection = nullptr;
+  DeduplicatedCStringSection *objcMethnameSection = nullptr;
   WordLiteralSection *wordLiteralSection = nullptr;
   RebaseSection *rebase = nullptr;
   BindingSection *binding = nullptr;
@@ -602,8 +803,13 @@ struct InStruct {
   LazyPointerSection *lazyPointers = nullptr;
   StubsSection *stubs = nullptr;
   StubHelperSection *stubHelper = nullptr;
+  ObjCStubsSection *objcStubs = nullptr;
+  ConcatInputSection *objcSelrefs = nullptr;
   UnwindInfoSection *unwindInfo = nullptr;
+  ObjCImageInfoSection *objCImageInfo = nullptr;
   ConcatInputSection *imageLoaderCache = nullptr;
+  InitOffsetsSection *initOffsets = nullptr;
+  ChainedFixupsSection *chainedFixups = nullptr;
 };
 
 extern InStruct in;
@@ -611,7 +817,6 @@ extern std::vector<SyntheticSection *> syntheticSections;
 
 void createSyntheticSymbols();
 
-} // namespace macho
-} // namespace lld
+} // namespace lld::macho
 
 #endif
index a5da764..44a8552 100644 (file)
 
 #include "llvm/ADT/BitmaskEnum.h"
 #include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/MathExtras.h"
 #include "llvm/Support/MemoryBuffer.h"
 
 #include <cstddef>
 #include <cstdint>
 
-namespace lld {
-namespace macho {
+#include "mach-o/compact_unwind_encoding.h"
+
+namespace lld::macho {
 LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
 
 class Symbol;
 class Defined;
 class DylibSymbol;
 class InputSection;
+class ObjFile;
+
+static_assert(static_cast<uint32_t>(UNWIND_X86_64_MODE_MASK) ==
+                  static_cast<uint32_t>(UNWIND_X86_MODE_MASK) &&
+              static_cast<uint32_t>(UNWIND_ARM64_MODE_MASK) ==
+                  static_cast<uint32_t>(UNWIND_X86_64_MODE_MASK));
+
+// Since the mode masks have the same value on all targets, define
+// a common one for convenience.
+constexpr uint32_t UNWIND_MODE_MASK = UNWIND_X86_64_MODE_MASK;
 
 class TargetInfo {
 public:
@@ -37,6 +49,7 @@ public:
     pageZeroSize = LP::pageZeroSize;
     headerSize = sizeof(typename LP::mach_header);
     wordSize = LP::wordSize;
+    p2WordSize = llvm::CTLog2<LP::wordSize>();
   }
 
   virtual ~TargetInfo() = default;
@@ -50,11 +63,18 @@ public:
 
   // Write code for lazy binding. See the comments on StubsSection for more
   // details.
-  virtual void writeStub(uint8_t *buf, const Symbol &) const = 0;
+  virtual void writeStub(uint8_t *buf, const Symbol &,
+                         uint64_t pointerVA) const = 0;
   virtual void writeStubHelperHeader(uint8_t *buf) const = 0;
-  virtual void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &,
+  virtual void writeStubHelperEntry(uint8_t *buf, const Symbol &,
                                     uint64_t entryAddr) const = 0;
 
+  virtual void writeObjCMsgSendStub(uint8_t *buf, Symbol *sym,
+                                    uint64_t stubsAddr, uint64_t stubOffset,
+                                    uint64_t selrefsVA, uint64_t selectorIndex,
+                                    uint64_t gotAddr,
+                                    uint64_t msgSendIndex) const = 0;
+
   // Symbols may be referenced via either the GOT or the stubs section,
   // depending on the relocation type. prepareSymbolRelocation() will set up the
   // GOT/stubs entries, and resolveSymbolVA() will return the addresses of those
@@ -62,20 +82,35 @@ public:
   // on a level of address indirection.
   virtual void relaxGotLoad(uint8_t *loc, uint8_t type) const = 0;
 
-  virtual const RelocAttrs &getRelocAttrs(uint8_t type) const = 0;
-
   virtual uint64_t getPageSize() const = 0;
 
   virtual void populateThunk(InputSection *thunk, Symbol *funcSym) {
     llvm_unreachable("target does not use thunks");
   }
 
+  const RelocAttrs &getRelocAttrs(uint8_t type) const {
+    assert(type < relocAttrs.size() && "invalid relocation type");
+    if (type >= relocAttrs.size())
+      return invalidRelocAttrs;
+    return relocAttrs[type];
+  }
+
   bool hasAttr(uint8_t type, RelocAttrBits bit) const {
     return getRelocAttrs(type).hasAttr(bit);
   }
 
   bool usesThunks() const { return thunkSize > 0; }
 
+  // For now, handleDtraceReloc only implements -no_dtrace_dof, and ensures
+  // that the linking would not fail even when there are user-provided dtrace
+  // symbols. However, unlike ld64, lld currently does not emit __dof sections.
+  virtual void handleDtraceReloc(const Symbol *sym, const Reloc &r,
+                                 uint8_t *loc) const {
+    llvm_unreachable("Unsupported architecture for dtrace symbols");
+  }
+
+  virtual void applyOptimizationHints(uint8_t *, const ObjFile &) const {};
+
   uint32_t magic;
   llvm::MachO::CPUType cpuType;
   uint32_t cpuSubtype;
@@ -85,10 +120,20 @@ public:
   size_t stubSize;
   size_t stubHelperHeaderSize;
   size_t stubHelperEntrySize;
+  size_t objcStubsFastSize;
+  size_t objcStubsAlignment;
+  uint8_t p2WordSize;
   size_t wordSize;
 
   size_t thunkSize = 0;
-  uint64_t branchRange = 0;
+  uint64_t forwardBranchRange = 0;
+  uint64_t backwardBranchRange = 0;
+
+  uint32_t modeDwarfEncoding;
+  uint8_t subtractorRelocType;
+  uint8_t unsignedRelocType;
+
+  llvm::ArrayRef<RelocAttrs> relocAttrs;
 
   // We contrive this value as sufficiently far from any valid address that it
   // will always be out-of-range for any architecture. UINT64_MAX is not a
@@ -136,7 +181,6 @@ struct ILP32 {
 
 extern TargetInfo *target;
 
-} // namespace macho
-} // namespace lld
+} // namespace lld::macho
 
 #endif
index 3efc646..470f335 100644 (file)
@@ -7,8 +7,6 @@
 //===----------------------------------------------------------------------===//
 
 #include "UnwindInfoSection.h"
-#include "ConcatOutputSection.h"
-#include "Config.h"
 #include "InputSection.h"
 #include "OutputSection.h"
 #include "OutputSegment.h"
 
 #include "lld/Common/ErrorHandler.h"
 #include "lld/Common/Memory.h"
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/SmallVector.h"
 #include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/Parallel.h"
+
+#include "mach-o/compact_unwind_encoding.h"
+
+#include <numeric>
 
 using namespace llvm;
 using namespace llvm::MachO;
+using namespace llvm::support::endian;
 using namespace lld;
 using namespace lld::macho;
 
@@ -78,20 +82,57 @@ using namespace lld::macho;
 // advantage, achieving a 3-order-of-magnitude reduction in the
 // number of entries.
 //
-// * The __TEXT,__unwind_info format can accommodate up to 127 unique
-// encodings for the space-efficient compressed format. In practice,
-// fewer than a dozen unique encodings are used by C++ programs of
-// all sizes. Therefore, we don't even bother implementing the regular
-// non-compressed format. Time will tell if anyone in the field ever
-// overflows the 127-encodings limit.
-//
 // Refer to the definition of unwind_info_section_header in
 // compact_unwind_encoding.h for an overview of the format we are encoding
 // here.
 
-// TODO(gkm): prune __eh_frame entries superseded by __unwind_info, PR50410
 // TODO(gkm): how do we align the 2nd-level pages?
 
+// The offsets of various fields in the on-disk representation of each compact
+// unwind entry.
+struct CompactUnwindOffsets {
+  uint32_t functionAddress;
+  uint32_t functionLength;
+  uint32_t encoding;
+  uint32_t personality;
+  uint32_t lsda;
+
+  CompactUnwindOffsets(size_t wordSize) {
+    if (wordSize == 8)
+      init<uint64_t>();
+    else {
+      assert(wordSize == 4);
+      init<uint32_t>();
+    }
+  }
+
+private:
+  template <class Ptr> void init() {
+    functionAddress = offsetof(Layout<Ptr>, functionAddress);
+    functionLength = offsetof(Layout<Ptr>, functionLength);
+    encoding = offsetof(Layout<Ptr>, encoding);
+    personality = offsetof(Layout<Ptr>, personality);
+    lsda = offsetof(Layout<Ptr>, lsda);
+  }
+
+  template <class Ptr> struct Layout {
+    Ptr functionAddress;
+    uint32_t functionLength;
+    compact_unwind_encoding_t encoding;
+    Ptr personality;
+    Ptr lsda;
+  };
+};
+
+// LLD's internal representation of a compact unwind entry.
+struct CompactUnwindEntry {
+  uint64_t functionAddress;
+  uint32_t functionLength;
+  compact_unwind_encoding_t encoding;
+  Symbol *personality;
+  InputSection *lsda;
+};
+
 using EncodingMap = DenseMap<compact_unwind_encoding_t, size_t>;
 
 struct SecondLevelPage {
@@ -103,49 +144,94 @@ struct SecondLevelPage {
   EncodingMap localEncodingIndexes;
 };
 
-template <class Ptr>
+// UnwindInfoSectionImpl allows us to avoid cluttering our header file with a
+// lengthy definition of UnwindInfoSection.
 class UnwindInfoSectionImpl final : public UnwindInfoSection {
 public:
-  void prepareRelocations(ConcatInputSection *) override;
-  void addInput(ConcatInputSection *) override;
+  UnwindInfoSectionImpl() : cuOffsets(target->wordSize) {}
+  uint64_t getSize() const override { return unwindInfoSize; }
+  void prepare() override;
   void finalize() override;
   void writeTo(uint8_t *buf) const override;
 
 private:
+  void prepareRelocations(ConcatInputSection *);
+  void relocateCompactUnwind(std::vector<CompactUnwindEntry> &);
+  void encodePersonalities();
+  Symbol *canonicalizePersonality(Symbol *);
+
+  uint64_t unwindInfoSize = 0;
+  std::vector<decltype(symbols)::value_type> symbolsVec;
+  CompactUnwindOffsets cuOffsets;
   std::vector<std::pair<compact_unwind_encoding_t, size_t>> commonEncodings;
   EncodingMap commonEncodingIndexes;
-  // Indices of personality functions within the GOT.
-  std::vector<uint32_t> personalities;
+  // The entries here will be in the same order as their originating symbols
+  // in symbolsVec.
+  std::vector<CompactUnwindEntry> cuEntries;
+  // Indices into the cuEntries vector.
+  std::vector<size_t> cuIndices;
+  std::vector<Symbol *> personalities;
   SmallDenseMap<std::pair<InputSection *, uint64_t /* addend */>, Symbol *>
       personalityTable;
-  std::vector<unwind_info_section_header_lsda_index_entry> lsdaEntries;
-  // Map of function offset (from the image base) to an index within the LSDA
-  // array.
-  DenseMap<uint32_t, uint32_t> functionToLsdaIndex;
-  std::vector<CompactUnwindEntry<Ptr>> cuVector;
-  std::vector<CompactUnwindEntry<Ptr> *> cuPtrVector;
+  // Indices into cuEntries for CUEs with a non-null LSDA.
+  std::vector<size_t> entriesWithLsda;
+  // Map of cuEntries index to an index within the LSDA array.
+  DenseMap<size_t, uint32_t> lsdaIndex;
   std::vector<SecondLevelPage> secondLevelPages;
   uint64_t level2PagesOffset = 0;
+  // The highest-address function plus its size. The unwinder needs this to
+  // determine the address range that is covered by unwind info.
+  uint64_t cueEndBoundary = 0;
 };
 
 UnwindInfoSection::UnwindInfoSection()
     : SyntheticSection(segment_names::text, section_names::unwindInfo) {
   align = 4;
-  compactUnwindSection =
-      make<ConcatOutputSection>(section_names::compactUnwind);
 }
 
-void UnwindInfoSection::prepareRelocations() {
-  for (ConcatInputSection *isec : compactUnwindSection->inputs)
-    prepareRelocations(isec);
+// Record function symbols that may need entries emitted in __unwind_info, which
+// stores unwind data for address ranges.
+//
+// Note that if several adjacent functions have the same unwind encoding and
+// personality function and no LSDA, they share one unwind entry. For this to
+// work, functions without unwind info need explicit "no unwind info" unwind
+// entries -- else the unwinder would think they have the unwind info of the
+// closest function with unwind info right before in the image. Thus, we add
+// function symbols for each unique address regardless of whether they have
+// associated unwind info.
+void UnwindInfoSection::addSymbol(const Defined *d) {
+  if (d->unwindEntry)
+    allEntriesAreOmitted = false;
+  // We don't yet know the final output address of this symbol, but we know that
+  // they are uniquely determined by a combination of the isec and value, so
+  // we use that as the key here.
+  auto p = symbols.insert({{d->isec, d->value}, d});
+  // If we have multiple symbols at the same address, only one of them can have
+  // an associated unwind entry.
+  if (!p.second && d->unwindEntry) {
+    assert(p.first->second == d || !p.first->second->unwindEntry);
+    p.first->second = d;
+  }
 }
 
-template <class Ptr>
-void UnwindInfoSectionImpl<Ptr>::addInput(ConcatInputSection *isec) {
-  assert(isec->getSegName() == segment_names::ld &&
-         isec->getName() == section_names::compactUnwind);
-  isec->parent = compactUnwindSection;
-  compactUnwindSection->addInput(isec);
+void UnwindInfoSectionImpl::prepare() {
+  // This iteration needs to be deterministic, since prepareRelocations may add
+  // entries to the GOT. Hence the use of a MapVector for
+  // UnwindInfoSection::symbols.
+  for (const Defined *d : make_second_range(symbols))
+    if (d->unwindEntry) {
+      if (d->unwindEntry->getName() == section_names::compactUnwind) {
+        prepareRelocations(d->unwindEntry);
+      } else {
+        // We don't have to add entries to the GOT here because FDEs have
+        // explicit GOT relocations, so Writer::scanRelocations() will add those
+        // GOT entries. However, we still need to canonicalize the personality
+        // pointers (like prepareRelocations() does for CU entries) in order
+        // to avoid overflowing the 3-personality limit.
+        FDE &fde = cast<ObjFile>(d->getFile())->fdes[d->unwindEntry];
+        fde.personality = canonicalizePersonality(fde.personality);
+      }
+    }
 }
 
 // Compact unwind relocations have different semantics, so we handle them in a
@@ -153,8 +239,7 @@ void UnwindInfoSectionImpl<Ptr>::addInput(ConcatInputSection *isec) {
 // rebase opcodes for __LD,__compact_unwind, because that section doesn't
 // actually end up in the final binary. Second, personality pointers always
 // reside in the GOT and must be treated specially.
-template <class Ptr>
-void UnwindInfoSectionImpl<Ptr>::prepareRelocations(ConcatInputSection *isec) {
+void UnwindInfoSectionImpl::prepareRelocations(ConcatInputSection *isec) {
   assert(!isec->shouldOmitFromOutput() &&
          "__compact_unwind section should not be omitted");
 
@@ -166,30 +251,46 @@ void UnwindInfoSectionImpl<Ptr>::prepareRelocations(ConcatInputSection *isec) {
   for (size_t i = 0; i < isec->relocs.size(); ++i) {
     Reloc &r = isec->relocs[i];
     assert(target->hasAttr(r.type, RelocAttrBits::UNSIGNED));
-
-    if (r.offset % sizeof(CompactUnwindEntry<Ptr>) == 0) {
-      InputSection *referentIsec;
-      if (auto *isec = r.referent.dyn_cast<InputSection *>())
-        referentIsec = isec;
-      else
-        referentIsec = cast<Defined>(r.referent.dyn_cast<Symbol *>())->isec;
-
-      if (!cast<ConcatInputSection>(referentIsec)->shouldOmitFromOutput())
-        allEntriesAreOmitted = false;
-      continue;
-    }
-
-    if (r.offset % sizeof(CompactUnwindEntry<Ptr>) !=
-        offsetof(CompactUnwindEntry<Ptr>, personality))
+    // Since compact unwind sections aren't part of the inputSections vector,
+    // they don't get canonicalized by scanRelocations(), so we have to do the
+    // canonicalization here.
+    if (auto *referentIsec = r.referent.dyn_cast<InputSection *>())
+      r.referent = referentIsec->canonical();
+
+    // Functions and LSDA entries always reside in the same object file as the
+    // compact unwind entries that references them, and thus appear as section
+    // relocs. There is no need to prepare them. We only prepare relocs for
+    // personality functions.
+    if (r.offset != cuOffsets.personality)
       continue;
 
     if (auto *s = r.referent.dyn_cast<Symbol *>()) {
+      // Personality functions are nearly always system-defined (e.g.,
+      // ___gxx_personality_v0 for C++) and relocated as dylib symbols.  When an
+      // application provides its own personality function, it might be
+      // referenced by an extern Defined symbol reloc, or a local section reloc.
+      if (auto *defined = dyn_cast<Defined>(s)) {
+        // XXX(vyng) This is a special case for handling duplicate personality
+        // symbols. Note that LD64's behavior is a bit different and it is
+        // inconsistent with how symbol resolution usually work
+        //
+        // So we've decided not to follow it. Instead, simply pick the symbol
+        // with the same name from the symbol table to replace the local one.
+        //
+        // (See discussions/alternatives already considered on D107533)
+        if (!defined->isExternal())
+          if (Symbol *sym = symtab->find(defined->getName()))
+            if (!sym->isLazy())
+              r.referent = s = sym;
+      }
       if (auto *undefined = dyn_cast<Undefined>(s)) {
-        treatUndefinedSymbol(*undefined);
+        treatUndefinedSymbol(*undefined, isec, r.offset);
         // treatUndefinedSymbol() can replace s with a DylibSymbol; re-check.
         if (isa<Undefined>(s))
           continue;
       }
+
+      // Similar to canonicalizePersonality(), but we also register a GOT entry.
       if (auto *defined = dyn_cast<Defined>(s)) {
         // Check if we have created a synthetic symbol at the same address.
         Symbol *&personality =
@@ -202,6 +303,7 @@ void UnwindInfoSectionImpl<Ptr>::prepareRelocations(ConcatInputSection *isec) {
         }
         continue;
       }
+
       assert(isa<DylibSymbol>(s));
       in.got->addEntry(s);
       continue;
@@ -219,8 +321,10 @@ void UnwindInfoSectionImpl<Ptr>::prepareRelocations(ConcatInputSection *isec) {
         s = make<Defined>("<internal>", /*file=*/nullptr, referentIsec,
                           r.addend, /*size=*/0, /*isWeakDef=*/false,
                           /*isExternal=*/false, /*isPrivateExtern=*/false,
+                          /*includeInSymtab=*/true,
                           /*isThumb=*/false, /*isReferencedDynamically=*/false,
                           /*noDeadStrip=*/false);
+        s->used = true;
         in.got->addEntry(s);
       }
       r.referent = s;
@@ -229,119 +333,86 @@ void UnwindInfoSectionImpl<Ptr>::prepareRelocations(ConcatInputSection *isec) {
   }
 }
 
-// Unwind info lives in __DATA, and finalization of __TEXT will occur before
-// finalization of __DATA. Moreover, the finalization of unwind info depends on
-// the exact addresses that it references. So it is safe for compact unwind to
-// reference addresses in __TEXT, but not addresses in any other segment.
-static ConcatInputSection *checkTextSegment(InputSection *isec) {
-  if (isec->getSegName() != segment_names::text)
-    error("compact unwind references address in " + toString(isec) +
-          " which is not in segment __TEXT");
-  // __text should always be a ConcatInputSection.
-  return cast<ConcatInputSection>(isec);
+Symbol *UnwindInfoSectionImpl::canonicalizePersonality(Symbol *personality) {
+  if (auto *defined = dyn_cast_or_null<Defined>(personality)) {
+    // Check if we have created a synthetic symbol at the same address.
+    Symbol *&synth = personalityTable[{defined->isec, defined->value}];
+    if (synth == nullptr)
+      synth = defined;
+    else if (synth != defined)
+      return synth;
+  }
+  return personality;
 }
 
-template <class Ptr>
-constexpr Ptr TombstoneValue = std::numeric_limits<Ptr>::max();
-
 // We need to apply the relocations to the pre-link compact unwind section
 // before converting it to post-link form. There should only be absolute
 // relocations here: since we are not emitting the pre-link CU section, there
 // is no source address to make a relative location meaningful.
-template <class Ptr>
-static void
-relocateCompactUnwind(ConcatOutputSection *compactUnwindSection,
-                      std::vector<CompactUnwindEntry<Ptr>> &cuVector) {
-  for (const ConcatInputSection *isec : compactUnwindSection->inputs) {
-    assert(isec->parent == compactUnwindSection);
-
-    uint8_t *buf =
-        reinterpret_cast<uint8_t *>(cuVector.data()) + isec->outSecOff;
-    memcpy(buf, isec->data.data(), isec->data.size());
-
-    for (const Reloc &r : isec->relocs) {
-      uint64_t referentVA = TombstoneValue<Ptr>;
-      if (auto *referentSym = r.referent.dyn_cast<Symbol *>()) {
-        if (!isa<Undefined>(referentSym)) {
-          if (auto *defined = dyn_cast<Defined>(referentSym))
-            checkTextSegment(defined->isec);
-          // At this point in the link, we may not yet know the final address of
-          // the GOT, so we just encode the index. We make it a 1-based index so
-          // that we can distinguish the null pointer case.
-          referentVA = referentSym->gotIndex + 1;
-        }
-      } else {
-        auto *referentIsec = r.referent.get<InputSection *>();
-        ConcatInputSection *concatIsec = checkTextSegment(referentIsec);
-        if (!concatIsec->shouldOmitFromOutput())
-          referentVA = referentIsec->getVA(r.addend);
+void UnwindInfoSectionImpl::relocateCompactUnwind(
+    std::vector<CompactUnwindEntry> &cuEntries) {
+  parallelFor(0, symbolsVec.size(), [&](size_t i) {
+    CompactUnwindEntry &cu = cuEntries[i];
+    const Defined *d = symbolsVec[i].second;
+    cu.functionAddress = d->getVA();
+    if (!d->unwindEntry)
+      return;
+
+    // If we have DWARF unwind info, create a CU entry that points to it.
+    if (d->unwindEntry->getName() == section_names::ehFrame) {
+      cu.encoding = target->modeDwarfEncoding | d->unwindEntry->outSecOff;
+      const FDE &fde = cast<ObjFile>(d->getFile())->fdes[d->unwindEntry];
+      cu.functionLength = fde.funcLength;
+      cu.personality = fde.personality;
+      cu.lsda = fde.lsda;
+      return;
+    }
+
+    assert(d->unwindEntry->getName() == section_names::compactUnwind);
+
+    auto buf = reinterpret_cast<const uint8_t *>(d->unwindEntry->data.data()) -
+               target->wordSize;
+    cu.functionLength =
+        support::endian::read32le(buf + cuOffsets.functionLength);
+    cu.encoding = support::endian::read32le(buf + cuOffsets.encoding);
+    for (const Reloc &r : d->unwindEntry->relocs) {
+      if (r.offset == cuOffsets.personality) {
+        cu.personality = r.referent.get<Symbol *>();
+      } else if (r.offset == cuOffsets.lsda) {
+        if (auto *referentSym = r.referent.dyn_cast<Symbol *>())
+          cu.lsda = cast<Defined>(referentSym)->isec;
+        else
+          cu.lsda = r.referent.get<InputSection *>();
       }
-      writeAddress(buf + r.offset, referentVA, r.length);
     }
-  }
+  });
 }
 
 // There should only be a handful of unique personality pointers, so we can
 // encode them as 2-bit indices into a small array.
-template <class Ptr>
-static void
-encodePersonalities(const std::vector<CompactUnwindEntry<Ptr> *> &cuPtrVector,
-                    std::vector<uint32_t> &personalities) {
-  for (CompactUnwindEntry<Ptr> *cu : cuPtrVector) {
-    if (cu->personality == 0)
+void UnwindInfoSectionImpl::encodePersonalities() {
+  for (size_t idx : cuIndices) {
+    CompactUnwindEntry &cu = cuEntries[idx];
+    if (cu.personality == nullptr)
       continue;
     // Linear search is fast enough for a small array.
-    auto it = find(personalities, cu->personality);
+    auto it = find(personalities, cu.personality);
     uint32_t personalityIndex; // 1-based index
     if (it != personalities.end()) {
       personalityIndex = std::distance(personalities.begin(), it) + 1;
     } else {
-      personalities.push_back(cu->personality);
+      personalities.push_back(cu.personality);
       personalityIndex = personalities.size();
     }
-    cu->encoding |=
+    cu.encoding |=
         personalityIndex << countTrailingZeros(
             static_cast<compact_unwind_encoding_t>(UNWIND_PERSONALITY_MASK));
   }
   if (personalities.size() > 3)
-    error("too many personalities (" + std::to_string(personalities.size()) +
+    error("too many personalities (" + Twine(personalities.size()) +
           ") for compact unwind to encode");
 }
 
-// __unwind_info stores unwind data for address ranges. If several
-// adjacent functions have the same unwind encoding, LSDA, and personality
-// function, they share one unwind entry. For this to work, functions without
-// unwind info need explicit "no unwind info" unwind entries -- else the
-// unwinder would think they have the unwind info of the closest function
-// with unwind info right before in the image.
-template <class Ptr>
-static void addEntriesForFunctionsWithoutUnwindInfo(
-    std::vector<CompactUnwindEntry<Ptr>> &cuVector) {
-  DenseSet<Ptr> hasUnwindInfo;
-  for (CompactUnwindEntry<Ptr> &cuEntry : cuVector)
-    if (cuEntry.functionAddress != TombstoneValue<Ptr>)
-      hasUnwindInfo.insert(cuEntry.functionAddress);
-
-  // Add explicit "has no unwind info" entries for all global and local symbols
-  // without unwind info.
-  auto markNoUnwindInfo = [&cuVector, &hasUnwindInfo](const Defined *d) {
-    if (d->isLive() && d->isec && isCodeSection(d->isec)) {
-      Ptr ptr = d->getVA();
-      if (!hasUnwindInfo.count(ptr))
-        cuVector.push_back({ptr, 0, 0, 0, 0});
-    }
-  };
-  for (Symbol *sym : symtab->getSymbols())
-    if (auto *d = dyn_cast<Defined>(sym))
-      markNoUnwindInfo(d);
-  for (const InputFile *file : inputFiles)
-    if (auto *objFile = dyn_cast<ObjFile>(file))
-      for (Symbol *sym : objFile->symbols)
-        if (auto *d = dyn_cast_or_null<Defined>(sym))
-          if (!d->isExternal())
-            markNoUnwindInfo(d);
-}
-
 static bool canFoldEncoding(compact_unwind_encoding_t encoding) {
   // From compact_unwind_encoding.h:
   //  UNWIND_X86_64_MODE_STACK_IND:
@@ -355,10 +426,10 @@ static bool canFoldEncoding(compact_unwind_encoding_t encoding) {
   // of the unwind info's unwind address, two functions that have identical
   // unwind info can't be folded if it's using this encoding since both
   // entries need unique addresses.
-  static_assert(UNWIND_X86_64_MODE_MASK == UNWIND_X86_MODE_MASK, "");
-  static_assert(UNWIND_X86_64_MODE_STACK_IND == UNWIND_X86_MODE_STACK_IND, "");
+  static_assert(static_cast<uint32_t>(UNWIND_X86_64_MODE_STACK_IND) ==
+                static_cast<uint32_t>(UNWIND_X86_MODE_STACK_IND));
   if ((target->cpuType == CPU_TYPE_X86_64 || target->cpuType == CPU_TYPE_X86) &&
-      (encoding & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_STACK_IND) {
+      (encoding & UNWIND_MODE_MASK) == UNWIND_X86_64_MODE_STACK_IND) {
     // FIXME: Consider passing in the two function addresses and getting
     // their two stack sizes off the `subq` and only returning false if they're
     // actually different.
@@ -368,87 +439,86 @@ static bool canFoldEncoding(compact_unwind_encoding_t encoding) {
 }
 
 // Scan the __LD,__compact_unwind entries and compute the space needs of
-// __TEXT,__unwind_info and __TEXT,__eh_frame
-template <class Ptr> void UnwindInfoSectionImpl<Ptr>::finalize() {
-  if (compactUnwindSection == nullptr)
+// __TEXT,__unwind_info and __TEXT,__eh_frame.
+void UnwindInfoSectionImpl::finalize() {
+  if (symbols.empty())
     return;
 
   // At this point, the address space for __TEXT,__text has been
   // assigned, so we can relocate the __LD,__compact_unwind entries
   // into a temporary buffer. Relocation is necessary in order to sort
   // the CU entries by function address. Sorting is necessary so that
-  // we can fold adjacent CU entries with identical
-  // encoding+personality+lsda. Folding is necessary because it reduces
-  // the number of CU entries by as much as 3 orders of magnitude!
-  compactUnwindSection->finalize();
-  assert(compactUnwindSection->getSize() % sizeof(CompactUnwindEntry<Ptr>) ==
-         0);
-  size_t cuCount =
-      compactUnwindSection->getSize() / sizeof(CompactUnwindEntry<Ptr>);
-  cuVector.resize(cuCount);
-  relocateCompactUnwind(compactUnwindSection, cuVector);
-
-  addEntriesForFunctionsWithoutUnwindInfo(cuVector);
+  // we can fold adjacent CU entries with identical encoding+personality
+  // and without any LSDA. Folding is necessary because it reduces the
+  // number of CU entries by as much as 3 orders of magnitude!
+  cuEntries.resize(symbols.size());
+  // The "map" part of the symbols MapVector was only needed for deduplication
+  // in addSymbol(). Now that we are done adding, move the contents to a plain
+  // std::vector for indexed access.
+  symbolsVec = symbols.takeVector();
+  relocateCompactUnwind(cuEntries);
 
   // Rather than sort & fold the 32-byte entries directly, we create a
-  // vector of pointers to entries and sort & fold that instead.
-  cuPtrVector.reserve(cuVector.size());
-  for (CompactUnwindEntry<Ptr> &cuEntry : cuVector)
-    cuPtrVector.emplace_back(&cuEntry);
-  llvm::sort(cuPtrVector, [](const CompactUnwindEntry<Ptr> *a,
-                             const CompactUnwindEntry<Ptr> *b) {
-    return a->functionAddress < b->functionAddress;
+  // vector of indices to entries and sort & fold that instead.
+  cuIndices.resize(cuEntries.size());
+  std::iota(cuIndices.begin(), cuIndices.end(), 0);
+  llvm::sort(cuIndices, [&](size_t a, size_t b) {
+    return cuEntries[a].functionAddress < cuEntries[b].functionAddress;
   });
 
-  // Dead-stripped functions get a functionAddress of TombstoneValue in
-  // relocateCompactUnwind(). Filter them out here.
-  // FIXME: This doesn't yet collect associated data like LSDAs kept
-  // alive only by a now-removed CompactUnwindEntry or other comdat-like
-  // data (`kindNoneGroupSubordinate*` in ld64).
-  CompactUnwindEntry<Ptr> tombstone;
-  tombstone.functionAddress = TombstoneValue<Ptr>;
-  cuPtrVector.erase(
-      std::lower_bound(cuPtrVector.begin(), cuPtrVector.end(), &tombstone,
-                       [](const CompactUnwindEntry<Ptr> *a,
-                          const CompactUnwindEntry<Ptr> *b) {
-                         return a->functionAddress < b->functionAddress;
-                       }),
-      cuPtrVector.end());
-
-  // If there are no entries left after adding explicit "no unwind info"
-  // entries and removing entries for dead-stripped functions, don't write
-  // an __unwind_info section at all.
-  assert(allEntriesAreOmitted == cuPtrVector.empty());
-  if (cuPtrVector.empty())
-    return;
+  // Record the ending boundary before we fold the entries.
+  cueEndBoundary = cuEntries[cuIndices.back()].functionAddress +
+                   cuEntries[cuIndices.back()].functionLength;
 
-  // Fold adjacent entries with matching encoding+personality+lsda
-  // We use three iterators on the same cuPtrVector to fold in-situ:
+  // Fold adjacent entries with matching encoding+personality and without LSDA
+  // We use three iterators on the same cuIndices to fold in-situ:
   // (1) `foldBegin` is the first of a potential sequence of matching entries
   // (2) `foldEnd` is the first non-matching entry after `foldBegin`.
   // The semi-open interval [ foldBegin .. foldEnd ) contains a range
   // entries that can be folded into a single entry and written to ...
   // (3) `foldWrite`
-  auto foldWrite = cuPtrVector.begin();
-  for (auto foldBegin = cuPtrVector.begin(); foldBegin < cuPtrVector.end();) {
+  auto foldWrite = cuIndices.begin();
+  for (auto foldBegin = cuIndices.begin(); foldBegin < cuIndices.end();) {
     auto foldEnd = foldBegin;
-    while (++foldEnd < cuPtrVector.end() &&
-           (*foldBegin)->encoding == (*foldEnd)->encoding &&
-           (*foldBegin)->personality == (*foldEnd)->personality &&
-           (*foldBegin)->lsda == (*foldEnd)->lsda &&
-           canFoldEncoding((*foldEnd)->encoding))
+    // Common LSDA encodings (e.g. for C++ and Objective-C) contain offsets from
+    // a base address. The base address is normally not contained directly in
+    // the LSDA, and in that case, the personality function treats the starting
+    // address of the function (which is computed by the unwinder) as the base
+    // address and interprets the LSDA accordingly. The unwinder computes the
+    // starting address of a function as the address associated with its CU
+    // entry. For this reason, we cannot fold adjacent entries if they have an
+    // LSDA, because folding would make the unwinder compute the wrong starting
+    // address for the functions with the folded entries, which in turn would
+    // cause the personality function to misinterpret the LSDA for those
+    // functions. In the very rare case where the base address is encoded
+    // directly in the LSDA, two functions at different addresses would
+    // necessarily have different LSDAs, so their CU entries would not have been
+    // folded anyway.
+    while (++foldEnd < cuIndices.end() &&
+           cuEntries[*foldBegin].encoding == cuEntries[*foldEnd].encoding &&
+           !cuEntries[*foldBegin].lsda && !cuEntries[*foldEnd].lsda &&
+           // If we've gotten to this point, we don't have an LSDA, which should
+           // also imply that we don't have a personality function, since in all
+           // likelihood a personality function needs the LSDA to do anything
+           // useful. It can be technically valid to have a personality function
+           // and no LSDA though (e.g. the C++ personality __gxx_personality_v0
+           // is just a no-op without LSDA), so we still check for personality
+           // function equivalence to handle that case.
+           cuEntries[*foldBegin].personality ==
+               cuEntries[*foldEnd].personality &&
+           canFoldEncoding(cuEntries[*foldEnd].encoding))
       ;
     *foldWrite++ = *foldBegin;
     foldBegin = foldEnd;
   }
-  cuPtrVector.erase(foldWrite, cuPtrVector.end());
+  cuIndices.erase(foldWrite, cuIndices.end());
 
-  encodePersonalities(cuPtrVector, personalities);
+  encodePersonalities();
 
   // Count frequencies of the folded encodings
   EncodingMap encodingFrequencies;
-  for (const CompactUnwindEntry<Ptr> *cuPtrEntry : cuPtrVector)
-    encodingFrequencies[cuPtrEntry->encoding]++;
+  for (size_t idx : cuIndices)
+    encodingFrequencies[cuEntries[idx].encoding]++;
 
   // Make a vector of encodings, sorted by descending frequency
   for (const auto &frequency : encodingFrequencies)
@@ -481,19 +551,21 @@ template <class Ptr> void UnwindInfoSectionImpl<Ptr>::finalize() {
   //     and 127..255 references a local per-second-level-page table.
   // First we try the compact format and determine how many entries fit.
   // If more entries fit in the regular format, we use that.
-  for (size_t i = 0; i < cuPtrVector.size();) {
+  for (size_t i = 0; i < cuIndices.size();) {
+    size_t idx = cuIndices[i];
     secondLevelPages.emplace_back();
     SecondLevelPage &page = secondLevelPages.back();
     page.entryIndex = i;
-    uintptr_t functionAddressMax =
-        cuPtrVector[i]->functionAddress + COMPRESSED_ENTRY_FUNC_OFFSET_MASK;
+    uint64_t functionAddressMax =
+        cuEntries[idx].functionAddress + COMPRESSED_ENTRY_FUNC_OFFSET_MASK;
     size_t n = commonEncodings.size();
     size_t wordsRemaining =
         SECOND_LEVEL_PAGE_WORDS -
         sizeof(unwind_info_compressed_second_level_page_header) /
             sizeof(uint32_t);
-    while (wordsRemaining >= 1 && i < cuPtrVector.size()) {
-      const CompactUnwindEntry<Ptr> *cuPtr = cuPtrVector[i];
+    while (wordsRemaining >= 1 && i < cuIndices.size()) {
+      idx = cuIndices[i];
+      const CompactUnwindEntry *cuPtr = &cuEntries[idx];
       if (cuPtr->functionAddress >= functionAddressMax) {
         break;
       } else if (commonEncodingIndexes.count(cuPtr->encoding) ||
@@ -511,47 +583,43 @@ template <class Ptr> void UnwindInfoSectionImpl<Ptr>::finalize() {
     }
     page.entryCount = i - page.entryIndex;
 
-    // If this is not the final page, see if it's possible to fit more
-    // entries by using the regular format. This can happen when there
-    // are many unique encodings, and we we saturated the local
-    // encoding table early.
-    if (i < cuPtrVector.size() &&
+    // If this is not the final page, see if it's possible to fit more entries
+    // by using the regular format. This can happen when there are many unique
+    // encodings, and we saturated the local encoding table early.
+    if (i < cuIndices.size() &&
         page.entryCount < REGULAR_SECOND_LEVEL_ENTRIES_MAX) {
       page.kind = UNWIND_SECOND_LEVEL_REGULAR;
       page.entryCount = std::min(REGULAR_SECOND_LEVEL_ENTRIES_MAX,
-                                 cuPtrVector.size() - page.entryIndex);
+                                 cuIndices.size() - page.entryIndex);
       i = page.entryIndex + page.entryCount;
     } else {
       page.kind = UNWIND_SECOND_LEVEL_COMPRESSED;
     }
   }
 
-  for (const CompactUnwindEntry<Ptr> *cu : cuPtrVector) {
-    uint32_t functionOffset = cu->functionAddress - in.header->addr;
-    functionToLsdaIndex[functionOffset] = lsdaEntries.size();
-    if (cu->lsda != 0)
-      lsdaEntries.push_back(
-          {functionOffset, static_cast<uint32_t>(cu->lsda - in.header->addr)});
+  for (size_t idx : cuIndices) {
+    lsdaIndex[idx] = entriesWithLsda.size();
+    if (cuEntries[idx].lsda)
+      entriesWithLsda.push_back(idx);
   }
 
   // compute size of __TEXT,__unwind_info section
-  level2PagesOffset =
-      sizeof(unwind_info_section_header) +
-      commonEncodings.size() * sizeof(uint32_t) +
-      personalities.size() * sizeof(uint32_t) +
-      // The extra second-level-page entry is for the sentinel
-      (secondLevelPages.size() + 1) *
-          sizeof(unwind_info_section_header_index_entry) +
-      lsdaEntries.size() * sizeof(unwind_info_section_header_lsda_index_entry);
+  level2PagesOffset = sizeof(unwind_info_section_header) +
+                      commonEncodings.size() * sizeof(uint32_t) +
+                      personalities.size() * sizeof(uint32_t) +
+                      // The extra second-level-page entry is for the sentinel
+                      (secondLevelPages.size() + 1) *
+                          sizeof(unwind_info_section_header_index_entry) +
+                      entriesWithLsda.size() *
+                          sizeof(unwind_info_section_header_lsda_index_entry);
   unwindInfoSize =
       level2PagesOffset + secondLevelPages.size() * SECOND_LEVEL_PAGE_BYTES;
 }
 
 // All inputs are relocated and output addresses are known, so write!
 
-template <class Ptr>
-void UnwindInfoSectionImpl<Ptr>::writeTo(uint8_t *buf) const {
-  assert(!cuPtrVector.empty() && "call only if there is unwind info");
+void UnwindInfoSectionImpl::writeTo(uint8_t *buf) const {
+  assert(!cuIndices.empty() && "call only if there is unwind info");
 
   // section header
   auto *uip = reinterpret_cast<unwind_info_section_header *>(buf);
@@ -572,9 +640,11 @@ void UnwindInfoSectionImpl<Ptr>::writeTo(uint8_t *buf) const {
     *i32p++ = encoding.first;
 
   // Personalities
-  for (const uint32_t &personality : personalities)
-    *i32p++ =
-        in.got->addr + (personality - 1) * target->wordSize - in.header->addr;
+  for (const Symbol *personality : personalities)
+    *i32p++ = personality->getGotVA() - in.header->addr;
+
+  // FIXME: LD64 checks and warns aboutgaps or overlapse in cuEntries address
+  // ranges. We should do the same too
 
   // Level-1 index
   uint32_t lsdaOffset =
@@ -583,39 +653,42 @@ void UnwindInfoSectionImpl<Ptr>::writeTo(uint8_t *buf) const {
   uint64_t l2PagesOffset = level2PagesOffset;
   auto *iep = reinterpret_cast<unwind_info_section_header_index_entry *>(i32p);
   for (const SecondLevelPage &page : secondLevelPages) {
-    iep->functionOffset =
-        cuPtrVector[page.entryIndex]->functionAddress - in.header->addr;
+    size_t idx = cuIndices[page.entryIndex];
+    iep->functionOffset = cuEntries[idx].functionAddress - in.header->addr;
     iep->secondLevelPagesSectionOffset = l2PagesOffset;
     iep->lsdaIndexArraySectionOffset =
-        lsdaOffset + functionToLsdaIndex.lookup(iep->functionOffset) *
+        lsdaOffset + lsdaIndex.lookup(idx) *
                          sizeof(unwind_info_section_header_lsda_index_entry);
     iep++;
     l2PagesOffset += SECOND_LEVEL_PAGE_BYTES;
   }
   // Level-1 sentinel
-  const CompactUnwindEntry<Ptr> &cuEnd = *cuPtrVector.back();
-  assert(cuEnd.functionAddress != TombstoneValue<Ptr>);
-  iep->functionOffset =
-      cuEnd.functionAddress - in.header->addr + cuEnd.functionLength;
+  // XXX(vyng): Note that LD64 adds +1 here.
+  // Unsure whether it's a bug or it's their workaround for something else.
+  // See comments from https://reviews.llvm.org/D138320.
+  iep->functionOffset = cueEndBoundary - in.header->addr;
   iep->secondLevelPagesSectionOffset = 0;
   iep->lsdaIndexArraySectionOffset =
-      lsdaOffset +
-      lsdaEntries.size() * sizeof(unwind_info_section_header_lsda_index_entry);
+      lsdaOffset + entriesWithLsda.size() *
+                       sizeof(unwind_info_section_header_lsda_index_entry);
   iep++;
 
   // LSDAs
-  size_t lsdaBytes =
-      lsdaEntries.size() * sizeof(unwind_info_section_header_lsda_index_entry);
-  if (lsdaBytes > 0)
-    memcpy(iep, lsdaEntries.data(), lsdaBytes);
+  auto *lep =
+      reinterpret_cast<unwind_info_section_header_lsda_index_entry *>(iep);
+  for (size_t idx : entriesWithLsda) {
+    const CompactUnwindEntry &cu = cuEntries[idx];
+    lep->lsdaOffset = cu.lsda->getVA(/*off=*/0) - in.header->addr;
+    lep->functionOffset = cu.functionAddress - in.header->addr;
+    lep++;
+  }
 
   // Level-2 pages
-  auto *pp = reinterpret_cast<uint32_t *>(reinterpret_cast<uint8_t *>(iep) +
-                                          lsdaBytes);
+  auto *pp = reinterpret_cast<uint32_t *>(lep);
   for (const SecondLevelPage &page : secondLevelPages) {
     if (page.kind == UNWIND_SECOND_LEVEL_COMPRESSED) {
       uintptr_t functionAddressBase =
-          cuPtrVector[page.entryIndex]->functionAddress;
+          cuEntries[cuIndices[page.entryIndex]].functionAddress;
       auto *p2p =
           reinterpret_cast<unwind_info_compressed_second_level_page_header *>(
               pp);
@@ -628,14 +701,15 @@ void UnwindInfoSectionImpl<Ptr>::writeTo(uint8_t *buf) const {
       p2p->encodingsCount = page.localEncodings.size();
       auto *ep = reinterpret_cast<uint32_t *>(&p2p[1]);
       for (size_t i = 0; i < page.entryCount; i++) {
-        const CompactUnwindEntry<Ptr> *cuep = cuPtrVector[page.entryIndex + i];
-        auto it = commonEncodingIndexes.find(cuep->encoding);
+        const CompactUnwindEntry &cue =
+            cuEntries[cuIndices[page.entryIndex + i]];
+        auto it = commonEncodingIndexes.find(cue.encoding);
         if (it == commonEncodingIndexes.end())
-          it = page.localEncodingIndexes.find(cuep->encoding);
+          it = page.localEncodingIndexes.find(cue.encoding);
         *ep++ = (it->second << COMPRESSED_ENTRY_FUNC_OFFSET_BITS) |
-                (cuep->functionAddress - functionAddressBase);
+                (cue.functionAddress - functionAddressBase);
       }
-      if (page.localEncodings.size() != 0)
+      if (!page.localEncodings.empty())
         memcpy(ep, page.localEncodings.data(),
                page.localEncodings.size() * sizeof(uint32_t));
     } else {
@@ -647,9 +721,10 @@ void UnwindInfoSectionImpl<Ptr>::writeTo(uint8_t *buf) const {
       p2p->entryCount = page.entryCount;
       auto *ep = reinterpret_cast<uint32_t *>(&p2p[1]);
       for (size_t i = 0; i < page.entryCount; i++) {
-        const CompactUnwindEntry<Ptr> *cuep = cuPtrVector[page.entryIndex + i];
-        *ep++ = cuep->functionAddress;
-        *ep++ = cuep->encoding;
+        const CompactUnwindEntry &cue =
+            cuEntries[cuIndices[page.entryIndex + i]];
+        *ep++ = cue.functionAddress;
+        *ep++ = cue.encoding;
       }
     }
     pp += SECOND_LEVEL_PAGE_WORDS;
@@ -657,8 +732,5 @@ void UnwindInfoSectionImpl<Ptr>::writeTo(uint8_t *buf) const {
 }
 
 UnwindInfoSection *macho::makeUnwindInfoSection() {
-  if (target->wordSize == 8)
-    return make<UnwindInfoSectionImpl<uint64_t>>();
-  else
-    return make<UnwindInfoSectionImpl<uint32_t>>();
+  return make<UnwindInfoSectionImpl>();
 }
index fca11de..826573b 100644 (file)
 
 #include "ConcatOutputSection.h"
 #include "SyntheticSections.h"
+#include "llvm/ADT/MapVector.h"
 
-#include "mach-o/compact_unwind_encoding.h"
-
-namespace lld {
-namespace macho {
-
-template <class Ptr> struct CompactUnwindEntry {
-  Ptr functionAddress;
-  uint32_t functionLength;
-  compact_unwind_encoding_t encoding;
-  Ptr personality;
-  Ptr lsda;
-};
+namespace lld::macho {
 
 class UnwindInfoSection : public SyntheticSection {
 public:
-  bool isNeeded() const override {
-    return !compactUnwindSection->inputs.empty() && !allEntriesAreOmitted;
-  }
-  uint64_t getSize() const override { return unwindInfoSize; }
-  virtual void addInput(ConcatInputSection *) = 0;
-  std::vector<ConcatInputSection *> getInputs() {
-    return compactUnwindSection->inputs;
-  }
-  void prepareRelocations();
+  // If all functions are free of unwind info, we can omit the unwind info
+  // section entirely.
+  bool isNeeded() const override { return !allEntriesAreOmitted; }
+  void addSymbol(const Defined *);
+  virtual void prepare() = 0;
 
 protected:
   UnwindInfoSection();
-  virtual void prepareRelocations(ConcatInputSection *) = 0;
 
-  ConcatOutputSection *compactUnwindSection;
-  uint64_t unwindInfoSize = 0;
+  llvm::MapVector<std::pair<const InputSection *, uint64_t /*Defined::value*/>,
+                  const Defined *>
+      symbols;
   bool allEntriesAreOmitted = true;
 };
 
 UnwindInfoSection *makeUnwindInfoSection();
 
-} // namespace macho
-} // namespace lld
+} // namespace lld::macho
 
 #endif
index d9c9cf5..a099206 100644 (file)
@@ -14,6 +14,7 @@
 #include "MapFile.h"
 #include "OutputSection.h"
 #include "OutputSegment.h"
+#include "SectionPriorities.h"
 #include "SymbolTable.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "UnwindInfoSection.h"
 
 #include "lld/Common/Arrays.h"
-#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Memory.h"
+#include "lld/Common/CommonLinkerContext.h"
 #include "llvm/BinaryFormat/MachO.h"
 #include "llvm/Config/llvm-config.h"
 #include "llvm/Support/LEB128.h"
-#include "llvm/Support/MathExtras.h"
 #include "llvm/Support/Parallel.h"
 #include "llvm/Support/Path.h"
+#include "llvm/Support/ThreadPool.h"
 #include "llvm/Support/TimeProfiler.h"
 #include "llvm/Support/xxhash.h"
 
@@ -58,12 +58,15 @@ public:
 
   void openFile();
   void writeSections();
+  void applyOptimizationHints();
+  void buildFixupChains();
   void writeUuid();
   void writeCodeSignature();
   void writeOutputFile();
 
   template <class LP> void run();
 
+  ThreadPool threadPool;
   std::unique_ptr<FileOutputBuffer> &buffer;
   uint64_t addr = 0;
   uint64_t fileOff = 0;
@@ -130,8 +133,8 @@ public:
   LCSubFramework(StringRef umbrella) : umbrella(umbrella) {}
 
   uint32_t getSize() const override {
-    return alignTo(sizeof(sub_framework_command) + umbrella.size() + 1,
-                   target->wordSize);
+    return alignToPowerOf2(sizeof(sub_framework_command) + umbrella.size() + 1,
+                           target->wordSize);
   }
 
   void writeTo(uint8_t *buf) const override {
@@ -227,7 +230,7 @@ public:
 
   void writeTo(uint8_t *buf) const override {
     using SegmentCommand = typename LP::segment_command;
-    using Section = typename LP::section;
+    using SectionHeader = typename LP::section;
 
     auto *c = reinterpret_cast<SegmentCommand *>(buf);
     buf += sizeof(SegmentCommand);
@@ -243,13 +246,14 @@ public:
     c->vmsize = seg->vmSize;
     c->filesize = seg->fileSize;
     c->nsects = seg->numNonHiddenSections();
+    c->flags = seg->flags;
 
     for (const OutputSection *osec : seg->getSections()) {
       if (osec->isHidden())
         continue;
 
-      auto *sectHdr = reinterpret_cast<Section *>(buf);
-      buf += sizeof(Section);
+      auto *sectHdr = reinterpret_cast<SectionHeader *>(buf);
+      buf += sizeof(SectionHeader);
 
       memcpy(sectHdr->sectname, osec->name.data(), osec->name.size());
       memcpy(sectHdr->segname, name.data(), name.size());
@@ -324,7 +328,8 @@ public:
   }
 
   uint32_t getSize() const override {
-    return alignTo(sizeof(dylib_command) + path.size() + 1, 8);
+    return alignToPowerOf2(sizeof(dylib_command) + path.size() + 1,
+                           target->wordSize);
   }
 
   void writeTo(uint8_t *buf) const override {
@@ -343,6 +348,7 @@ public:
   }
 
   static uint32_t getInstanceCount() { return instanceCount; }
+  static void resetInstanceCount() { instanceCount = 0; }
 
 private:
   LoadCommandType type;
@@ -357,7 +363,8 @@ uint32_t LCDylib::instanceCount = 0;
 class LCLoadDylinker final : public LoadCommand {
 public:
   uint32_t getSize() const override {
-    return alignTo(sizeof(dylinker_command) + path.size() + 1, 8);
+    return alignToPowerOf2(sizeof(dylinker_command) + path.size() + 1,
+                           target->wordSize);
   }
 
   void writeTo(uint8_t *buf) const override {
@@ -383,7 +390,8 @@ public:
   explicit LCRPath(StringRef path) : path(path) {}
 
   uint32_t getSize() const override {
-    return alignTo(sizeof(rpath_command) + path.size() + 1, target->wordSize);
+    return alignToPowerOf2(sizeof(rpath_command) + path.size() + 1,
+                           target->wordSize);
   }
 
   void writeTo(uint8_t *buf) const override {
@@ -402,6 +410,31 @@ private:
   StringRef path;
 };
 
+class LCDyldEnv final : public LoadCommand {
+public:
+  explicit LCDyldEnv(StringRef name) : name(name) {}
+
+  uint32_t getSize() const override {
+    return alignToPowerOf2(sizeof(dyld_env_command) + name.size() + 1,
+                           target->wordSize);
+  }
+
+  void writeTo(uint8_t *buf) const override {
+    auto *c = reinterpret_cast<dyld_env_command *>(buf);
+    buf += sizeof(dyld_env_command);
+
+    c->cmd = LC_DYLD_ENVIRONMENT;
+    c->cmdsize = getSize();
+    c->name = sizeof(dyld_env_command);
+
+    memcpy(buf, name.data(), name.size());
+    buf[name.size()] = '\0';
+  }
+
+private:
+  StringRef name;
+};
+
 class LCMinVersion final : public LoadCommand {
 public:
   explicit LCMinVersion(const PlatformInfo &platformInfo)
@@ -412,19 +445,19 @@ public:
   void writeTo(uint8_t *buf) const override {
     auto *c = reinterpret_cast<version_min_command *>(buf);
     switch (platformInfo.target.Platform) {
-    case PlatformKind::macOS:
+    case PLATFORM_MACOS:
       c->cmd = LC_VERSION_MIN_MACOSX;
       break;
-    case PlatformKind::iOS:
-    case PlatformKind::iOSSimulator:
+    case PLATFORM_IOS:
+    case PLATFORM_IOSSIMULATOR:
       c->cmd = LC_VERSION_MIN_IPHONEOS;
       break;
-    case PlatformKind::tvOS:
-    case PlatformKind::tvOSSimulator:
+    case PLATFORM_TVOS:
+    case PLATFORM_TVOSSIMULATOR:
       c->cmd = LC_VERSION_MIN_TVOS;
       break;
-    case PlatformKind::watchOS:
-    case PlatformKind::watchOSSimulator:
+    case PLATFORM_WATCHOS:
+    case PLATFORM_WATCHOSSIMULATOR:
       c->cmd = LC_VERSION_MIN_WATCHOS;
       break;
     default:
@@ -455,9 +488,11 @@ public:
     auto *c = reinterpret_cast<build_version_command *>(buf);
     c->cmd = LC_BUILD_VERSION;
     c->cmdsize = getSize();
+
     c->platform = static_cast<uint32_t>(platformInfo.target.Platform);
     c->minos = encodeVersion(platformInfo.minimum);
     c->sdk = encodeVersion(platformInfo.sdk);
+
     c->ntools = ntools;
     auto *t = reinterpret_cast<build_tool_version *>(&c[1]);
     t->tool = TOOL_LD;
@@ -546,6 +581,40 @@ public:
   CodeSignatureSection *section;
 };
 
+class LCExportsTrie final : public LoadCommand {
+public:
+  LCExportsTrie(ExportSection *section) : section(section) {}
+
+  uint32_t getSize() const override { return sizeof(linkedit_data_command); }
+
+  void writeTo(uint8_t *buf) const override {
+    auto *c = reinterpret_cast<linkedit_data_command *>(buf);
+    c->cmd = LC_DYLD_EXPORTS_TRIE;
+    c->cmdsize = getSize();
+    c->dataoff = section->fileOff;
+    c->datasize = section->getSize();
+  }
+
+  ExportSection *section;
+};
+
+class LCChainedFixups final : public LoadCommand {
+public:
+  LCChainedFixups(ChainedFixupsSection *section) : section(section) {}
+
+  uint32_t getSize() const override { return sizeof(linkedit_data_command); }
+
+  void writeTo(uint8_t *buf) const override {
+    auto *c = reinterpret_cast<linkedit_data_command *>(buf);
+    c->cmd = LC_DYLD_CHAINED_FIXUPS;
+    c->cmdsize = getSize();
+    c->dataoff = section->fileOff;
+    c->datasize = section->getSize();
+  }
+
+  ChainedFixupsSection *section;
+};
+
 } // namespace
 
 void Writer::treatSpecialUndefineds() {
@@ -569,50 +638,14 @@ void Writer::treatSpecialUndefineds() {
   }
 }
 
-// Add stubs and bindings where necessary (e.g. if the symbol is a
-// DylibSymbol.)
-static void prepareBranchTarget(Symbol *sym) {
-  if (auto *dysym = dyn_cast<DylibSymbol>(sym)) {
-    if (in.stubs->addEntry(dysym)) {
-      if (sym->isWeakDef()) {
-        in.binding->addEntry(dysym, in.lazyPointers->isec,
-                             sym->stubsIndex * target->wordSize);
-        in.weakBinding->addEntry(sym, in.lazyPointers->isec,
-                                 sym->stubsIndex * target->wordSize);
-      } else {
-        in.lazyBinding->addEntry(dysym);
-      }
-    }
-  } else if (auto *defined = dyn_cast<Defined>(sym)) {
-    if (defined->isExternalWeakDef()) {
-      if (in.stubs->addEntry(sym)) {
-        in.rebase->addEntry(in.lazyPointers->isec,
-                            sym->stubsIndex * target->wordSize);
-        in.weakBinding->addEntry(sym, in.lazyPointers->isec,
-                                 sym->stubsIndex * target->wordSize);
-      }
-    }
-  } else {
-    llvm_unreachable("invalid branch target symbol type");
-  }
-}
-
-// Can a symbol's address can only be resolved at runtime?
-static bool needsBinding(const Symbol *sym) {
-  if (isa<DylibSymbol>(sym))
-    return true;
-  if (const auto *defined = dyn_cast<Defined>(sym))
-    return defined->isExternalWeakDef();
-  return false;
-}
-
 static void prepareSymbolRelocation(Symbol *sym, const InputSection *isec,
-                                    const Reloc &r) {
+                                    const lld::macho::Reloc &r) {
   assert(sym->isLive());
   const RelocAttrs &relocAttrs = target->getRelocAttrs(r.type);
 
   if (relocAttrs.hasAttr(RelocAttrBits::BRANCH)) {
-    prepareBranchTarget(sym);
+    if (needsBinding(sym))
+      in.stubs->addEntry(sym);
   } else if (relocAttrs.hasAttr(RelocAttrBits::GOT)) {
     if (relocAttrs.hasAttr(RelocAttrBits::POINTER) || needsBinding(sym))
       in.got->addEntry(sym);
@@ -640,7 +673,7 @@ void Writer::scanRelocations() {
       continue;
 
     for (auto it = isec->relocs.begin(); it != isec->relocs.end(); ++it) {
-      Reloc &r = *it;
+      lld::macho::Reloc &r = *it;
       if (target->hasAttr(r.type, RelocAttrBits::SUBTRAHEND)) {
         // Skip over the following UNSIGNED relocation -- it's just there as the
         // minuend, and doesn't have the usual UNSIGNED semantics. We don't want
@@ -650,7 +683,7 @@ void Writer::scanRelocations() {
       }
       if (auto *sym = r.referent.dyn_cast<Symbol *>()) {
         if (auto *undefined = dyn_cast<Undefined>(sym))
-          treatUndefinedSymbol(*undefined);
+          treatUndefinedSymbol(*undefined, isec, r.offset);
         // treatUndefinedSymbol() can replace sym with a DylibSymbol; re-check.
         if (!isa<Undefined>(sym) && validateSymbolRelocation(sym, isec, r))
           prepareSymbolRelocation(sym, isec, r);
@@ -660,41 +693,74 @@ void Writer::scanRelocations() {
         // too...
         auto *referentIsec = r.referent.get<InputSection *>();
         r.referent = referentIsec->canonical();
-        if (!r.pcrel)
-          in.rebase->addEntry(isec, r.offset);
+        if (!r.pcrel) {
+          if (config->emitChainedFixups)
+            in.chainedFixups->addRebase(isec, r.offset);
+          else
+            in.rebase->addEntry(isec, r.offset);
+        }
       }
     }
   }
 
-  in.unwindInfo->prepareRelocations();
+  in.unwindInfo->prepare();
+}
+
+static void addNonWeakDefinition(const Defined *defined) {
+  if (config->emitChainedFixups)
+    in.chainedFixups->setHasNonWeakDefinition();
+  else
+    in.weakBinding->addNonWeakDefinition(defined);
 }
 
 void Writer::scanSymbols() {
   TimeTraceScope timeScope("Scan symbols");
-  for (const Symbol *sym : symtab->getSymbols()) {
-    if (const auto *defined = dyn_cast<Defined>(sym)) {
-      if (defined->overridesWeakDef && defined->isLive())
-        in.weakBinding->addNonWeakDefinition(defined);
+  for (Symbol *sym : symtab->getSymbols()) {
+    if (auto *defined = dyn_cast<Defined>(sym)) {
+      if (!defined->isLive())
+        continue;
+      defined->canonicalize();
+      if (defined->overridesWeakDef)
+        addNonWeakDefinition(defined);
+      if (!defined->isAbsolute() && isCodeSection(defined->isec))
+        in.unwindInfo->addSymbol(defined);
     } else if (const auto *dysym = dyn_cast<DylibSymbol>(sym)) {
       // This branch intentionally doesn't check isLive().
       if (dysym->isDynamicLookup())
         continue;
       dysym->getFile()->refState =
           std::max(dysym->getFile()->refState, dysym->getRefState());
+    } else if (isa<Undefined>(sym)) {
+      if (sym->getName().startswith(ObjCStubsSection::symbolPrefix))
+        in.objcStubs->addEntry(sym);
     }
   }
+
+  for (const InputFile *file : inputFiles) {
+    if (auto *objFile = dyn_cast<ObjFile>(file))
+      for (Symbol *sym : objFile->symbols) {
+        if (auto *defined = dyn_cast_or_null<Defined>(sym)) {
+          if (!defined->isLive())
+            continue;
+          defined->canonicalize();
+          if (!defined->isExternal() && !defined->isAbsolute() &&
+              isCodeSection(defined->isec))
+            in.unwindInfo->addSymbol(defined);
+        }
+      }
+  }
 }
 
 // TODO: ld64 enforces the old load commands in a few other cases.
 static bool useLCBuildVersion(const PlatformInfo &platformInfo) {
-  static const std::vector<std::pair<PlatformKind, VersionTuple>> minVersion = {
-      {PlatformKind::macOS, VersionTuple(10, 14)},
-      {PlatformKind::iOS, VersionTuple(12, 0)},
-      {PlatformKind::iOSSimulator, VersionTuple(13, 0)},
-      {PlatformKind::tvOS, VersionTuple(12, 0)},
-      {PlatformKind::tvOSSimulator, VersionTuple(13, 0)},
-      {PlatformKind::watchOS, VersionTuple(5, 0)},
-      {PlatformKind::watchOSSimulator, VersionTuple(6, 0)}};
+  static const std::array<std::pair<PlatformType, VersionTuple>, 7> minVersion =
+      {{{PLATFORM_MACOS, VersionTuple(10, 14)},
+        {PLATFORM_IOS, VersionTuple(12, 0)},
+        {PLATFORM_IOSSIMULATOR, VersionTuple(13, 0)},
+        {PLATFORM_TVOS, VersionTuple(12, 0)},
+        {PLATFORM_TVOSSIMULATOR, VersionTuple(13, 0)},
+        {PLATFORM_WATCHOS, VersionTuple(5, 0)},
+        {PLATFORM_WATCHOSSIMULATOR, VersionTuple(6, 0)}}};
   auto it = llvm::find_if(minVersion, [&](const auto &p) {
     return p.first == platformInfo.target.Platform;
   });
@@ -708,8 +774,13 @@ template <class LP> void Writer::createLoadCommands() {
     seg->index = segIndex++;
   }
 
-  in.header->addLoadCommand(make<LCDyldInfo>(
-      in.rebase, in.binding, in.weakBinding, in.lazyBinding, in.exports));
+  if (config->emitChainedFixups) {
+    in.header->addLoadCommand(make<LCChainedFixups>(in.chainedFixups));
+    in.header->addLoadCommand(make<LCExportsTrie>(in.exports));
+  } else {
+    in.header->addLoadCommand(make<LCDyldInfo>(
+        in.rebase, in.binding, in.weakBinding, in.lazyBinding, in.exports));
+  }
   in.header->addLoadCommand(make<LCSymtab>(symtabSection, stringTableSection));
   in.header->addLoadCommand(
       make<LCDysymtab>(symtabSection, indirectSymtabSection));
@@ -743,76 +814,94 @@ template <class LP> void Writer::createLoadCommands() {
   else
     in.header->addLoadCommand(make<LCMinVersion>(config->platformInfo));
 
+  if (config->secondaryPlatformInfo) {
+    in.header->addLoadCommand(
+        make<LCBuildVersion>(*config->secondaryPlatformInfo));
+  }
+
   // This is down here to match ld64's load command order.
   if (config->outputType == MH_EXECUTE)
     in.header->addLoadCommand(make<LCMain>());
 
+  // See ld64's OutputFile::buildDylibOrdinalMapping for the corresponding
+  // library ordinal computation code in ld64.
   int64_t dylibOrdinal = 1;
   DenseMap<StringRef, int64_t> ordinalForInstallName;
+
+  std::vector<DylibFile *> dylibFiles;
   for (InputFile *file : inputFiles) {
-    if (auto *dylibFile = dyn_cast<DylibFile>(file)) {
-      if (dylibFile->isBundleLoader) {
-        dylibFile->ordinal = BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE;
-        // Shortcut since bundle-loader does not re-export the symbols.
+    if (auto *dylibFile = dyn_cast<DylibFile>(file))
+      dylibFiles.push_back(dylibFile);
+  }
+  for (size_t i = 0; i < dylibFiles.size(); ++i)
+    dylibFiles.insert(dylibFiles.end(), dylibFiles[i]->extraDylibs.begin(),
+                      dylibFiles[i]->extraDylibs.end());
 
-        dylibFile->reexport = false;
-        continue;
-      }
+  for (DylibFile *dylibFile : dylibFiles) {
+    if (dylibFile->isBundleLoader) {
+      dylibFile->ordinal = BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE;
+      // Shortcut since bundle-loader does not re-export the symbols.
 
-      // Don't emit load commands for a dylib that is not referenced if:
-      // - it was added implicitly (via a reexport, an LC_LOAD_DYLINKER --
-      //   if it's on the linker command line, it's explicit)
-      // - or it's marked MH_DEAD_STRIPPABLE_DYLIB
-      // - or the flag -dead_strip_dylibs is used
-      // FIXME: `isReferenced()` is currently computed before dead code
-      // stripping, so references from dead code keep a dylib alive. This
-      // matches ld64, but it's something we should do better.
-      if (!dylibFile->isReferenced() && !dylibFile->forceNeeded &&
-          (!dylibFile->explicitlyLinked || dylibFile->deadStrippable ||
-           config->deadStripDylibs))
-        continue;
+      dylibFile->reexport = false;
+      continue;
+    }
 
-      // Several DylibFiles can have the same installName. Only emit a single
-      // load command for that installName and give all these DylibFiles the
-      // same ordinal.
-      // This can happen in several cases:
-      // - a new framework could change its installName to an older
-      //   framework name via an $ld$ symbol depending on platform_version
-      // - symlinks (for example, libpthread.tbd is a symlink to libSystem.tbd;
-      //   Foo.framework/Foo.tbd is usually a symlink to
-      //   Foo.framework/Versions/Current/Foo.tbd, where
-      //   Foo.framework/Versions/Current is usually a symlink to
-      //   Foo.framework/Versions/A)
-      // - a framework can be linked both explicitly on the linker
-      //   command line and implicitly as a reexport from a different
-      //   framework. The re-export will usually point to the tbd file
-      //   in Foo.framework/Versions/A/Foo.tbd, while the explicit link will
-      //   usually find Foo.framework/Foo.tbd. These are usually symlinks,
-      //   but in a --reproduce archive they will be identical but distinct
-      //   files.
-      // In the first case, *semantically distinct* DylibFiles will have the
-      // same installName.
-      int64_t &ordinal = ordinalForInstallName[dylibFile->installName];
-      if (ordinal) {
-        dylibFile->ordinal = ordinal;
-        continue;
-      }
+    // Don't emit load commands for a dylib that is not referenced if:
+    // - it was added implicitly (via a reexport, an LC_LOAD_DYLINKER --
+    //   if it's on the linker command line, it's explicit)
+    // - or it's marked MH_DEAD_STRIPPABLE_DYLIB
+    // - or the flag -dead_strip_dylibs is used
+    // FIXME: `isReferenced()` is currently computed before dead code
+    // stripping, so references from dead code keep a dylib alive. This
+    // matches ld64, but it's something we should do better.
+    if (!dylibFile->isReferenced() && !dylibFile->forceNeeded &&
+        (!dylibFile->isExplicitlyLinked() || dylibFile->deadStrippable ||
+         config->deadStripDylibs))
+      continue;
 
-      ordinal = dylibFile->ordinal = dylibOrdinal++;
-      LoadCommandType lcType =
-          dylibFile->forceWeakImport || dylibFile->refState == RefState::Weak
-              ? LC_LOAD_WEAK_DYLIB
-              : LC_LOAD_DYLIB;
-      in.header->addLoadCommand(make<LCDylib>(lcType, dylibFile->installName,
-                                              dylibFile->compatibilityVersion,
-                                              dylibFile->currentVersion));
-
-      if (dylibFile->reexport)
-        in.header->addLoadCommand(
-            make<LCDylib>(LC_REEXPORT_DYLIB, dylibFile->installName));
+    // Several DylibFiles can have the same installName. Only emit a single
+    // load command for that installName and give all these DylibFiles the
+    // same ordinal.
+    // This can happen in several cases:
+    // - a new framework could change its installName to an older
+    //   framework name via an $ld$ symbol depending on platform_version
+    // - symlinks (for example, libpthread.tbd is a symlink to libSystem.tbd;
+    //   Foo.framework/Foo.tbd is usually a symlink to
+    //   Foo.framework/Versions/Current/Foo.tbd, where
+    //   Foo.framework/Versions/Current is usually a symlink to
+    //   Foo.framework/Versions/A)
+    // - a framework can be linked both explicitly on the linker
+    //   command line and implicitly as a reexport from a different
+    //   framework. The re-export will usually point to the tbd file
+    //   in Foo.framework/Versions/A/Foo.tbd, while the explicit link will
+    //   usually find Foo.framework/Foo.tbd. These are usually symlinks,
+    //   but in a --reproduce archive they will be identical but distinct
+    //   files.
+    // In the first case, *semantically distinct* DylibFiles will have the
+    // same installName.
+    int64_t &ordinal = ordinalForInstallName[dylibFile->installName];
+    if (ordinal) {
+      dylibFile->ordinal = ordinal;
+      continue;
     }
+
+    ordinal = dylibFile->ordinal = dylibOrdinal++;
+    LoadCommandType lcType =
+        dylibFile->forceWeakImport || dylibFile->refState == RefState::Weak
+            ? LC_LOAD_WEAK_DYLIB
+            : LC_LOAD_DYLIB;
+    in.header->addLoadCommand(make<LCDylib>(lcType, dylibFile->installName,
+                                            dylibFile->compatibilityVersion,
+                                            dylibFile->currentVersion));
+
+    if (dylibFile->reexport)
+      in.header->addLoadCommand(
+          make<LCDylib>(LC_REEXPORT_DYLIB, dylibFile->installName));
   }
 
+  for (const auto &dyldEnv : config->dyldEnvs)
+    in.header->addLoadCommand(make<LCDyldEnv>(dyldEnv));
+
   if (functionStartsSection)
     in.header->addLoadCommand(make<LCFunctionStarts>(functionStartsSection));
   if (dataInCodeSection)
@@ -827,52 +916,6 @@ template <class LP> void Writer::createLoadCommands() {
                               : 0));
 }
 
-static size_t getSymbolPriority(const SymbolPriorityEntry &entry,
-                                const InputFile *f) {
-  // We don't use toString(InputFile *) here because it returns the full path
-  // for object files, and we only want the basename.
-  StringRef filename;
-  if (f->archiveName.empty())
-    filename = path::filename(f->getName());
-  else
-    filename = saver.save(path::filename(f->archiveName) + "(" +
-                          path::filename(f->getName()) + ")");
-  return std::max(entry.objectFiles.lookup(filename), entry.anyObjectFile);
-}
-
-// Each section gets assigned the priority of the highest-priority symbol it
-// contains.
-static DenseMap<const InputSection *, size_t> buildInputSectionPriorities() {
-  DenseMap<const InputSection *, size_t> sectionPriorities;
-
-  if (config->priorities.empty())
-    return sectionPriorities;
-
-  auto addSym = [&](Defined &sym) {
-    if (sym.isAbsolute())
-      return;
-
-    auto it = config->priorities.find(sym.getName());
-    if (it == config->priorities.end())
-      return;
-
-    SymbolPriorityEntry &entry = it->second;
-    size_t &priority = sectionPriorities[sym.isec];
-    priority =
-        std::max(priority, getSymbolPriority(entry, sym.isec->getFile()));
-  };
-
-  // TODO: Make sure this handles weak symbols correctly.
-  for (const InputFile *file : inputFiles) {
-    if (isa<ObjFile>(file))
-      for (Symbol *sym : file->symbols)
-        if (auto *d = dyn_cast_or_null<Defined>(sym))
-          addSym(*d);
-  }
-
-  return sectionPriorities;
-}
-
 // Sorting only can happen once all outputs have been collected. Here we sort
 // segments, output sections within each segment, and input sections within each
 // output segment.
@@ -881,25 +924,40 @@ static void sortSegmentsAndSections() {
   sortOutputSegments();
 
   DenseMap<const InputSection *, size_t> isecPriorities =
-      buildInputSectionPriorities();
+      priorityBuilder.buildInputSectionPriorities();
 
   uint32_t sectionIndex = 0;
   for (OutputSegment *seg : outputSegments) {
     seg->sortOutputSections();
+    // References from thread-local variable sections are treated as offsets
+    // relative to the start of the thread-local data memory area, which
+    // is initialized via copying all the TLV data sections (which are all
+    // contiguous). If later data sections require a greater alignment than
+    // earlier ones, the offsets of data within those sections won't be
+    // guaranteed to aligned unless we normalize alignments. We therefore use
+    // the largest alignment for all TLV data sections.
+    uint32_t tlvAlign = 0;
+    for (const OutputSection *osec : seg->getSections())
+      if (isThreadLocalData(osec->flags) && osec->align > tlvAlign)
+        tlvAlign = osec->align;
+
     for (OutputSection *osec : seg->getSections()) {
       // Now that the output sections are sorted, assign the final
       // output section indices.
       if (!osec->isHidden())
         osec->index = ++sectionIndex;
-      if (!firstTLVDataSection && isThreadLocalData(osec->flags))
-        firstTLVDataSection = osec;
+      if (isThreadLocalData(osec->flags)) {
+        if (!firstTLVDataSection)
+          firstTLVDataSection = osec;
+        osec->align = tlvAlign;
+      }
 
       if (!isecPriorities.empty()) {
         if (auto *merged = dyn_cast<ConcatOutputSection>(osec)) {
-          llvm::stable_sort(merged->inputs,
-                            [&](InputSection *a, InputSection *b) {
-                              return isecPriorities[a] > isecPriorities[b];
-                            });
+          llvm::stable_sort(
+              merged->inputs, [&](InputSection *a, InputSection *b) {
+                return isecPriorities.lookup(a) > isecPriorities.lookup(b);
+              });
         }
       }
     }
@@ -948,13 +1006,30 @@ template <class LP> void Writer::createOutputSections() {
     StringRef segname = it.first.first;
     ConcatOutputSection *osec = it.second;
     assert(segname != segment_names::ld);
-    if (osec->isNeeded())
+    if (osec->isNeeded()) {
+      // See comment in ObjFile::splitEhFrames()
+      if (osec->name == section_names::ehFrame &&
+          segname == segment_names::text)
+        osec->align = target->wordSize;
+
+      // MC keeps the default 1-byte alignment for __thread_vars, even though it
+      // contains pointers that are fixed up by dyld, which requires proper
+      // alignment.
+      if (isThreadLocalVariables(osec->flags))
+        osec->align = std::max<uint32_t>(osec->align, target->wordSize);
+
       getOrCreateOutputSegment(segname)->addOutputSection(osec);
+    }
   }
 
   for (SyntheticSection *ssec : syntheticSections) {
     auto it = concatOutputSections.find({ssec->segname, ssec->name});
-    if (ssec->isNeeded()) {
+    // We add all LinkEdit sections here because we don't know if they are
+    // needed until their finalizeContents() methods get called later. While
+    // this means that we add some redundant sections to __LINKEDIT, there is
+    // is no redundancy in the output, as we do not emit section headers for
+    // any LinkEdit sections.
+    if (ssec->isNeeded() || ssec->segname == segment_names::linkEdit) {
       if (it == concatOutputSections.end()) {
         getOrCreateOutputSegment(ssec->segname)->addOutputSection(ssec);
       } else {
@@ -973,6 +1048,21 @@ template <class LP> void Writer::createOutputSections() {
 void Writer::finalizeAddresses() {
   TimeTraceScope timeScope("Finalize addresses");
   uint64_t pageSize = target->getPageSize();
+
+  // We could parallelize this loop, but local benchmarking indicates it is
+  // faster to do it all in the main thread.
+  for (OutputSegment *seg : outputSegments) {
+    if (seg == linkEditSegment)
+      continue;
+    for (OutputSection *osec : seg->getSections()) {
+      if (!osec->isNeeded())
+        continue;
+      // Other kinds of OutputSections have already been finalized.
+      if (auto concatOsec = dyn_cast<ConcatOutputSection>(osec))
+        concatOsec->finalizeContents();
+    }
+  }
+
   // Ensure that segments (and the sections they contain) are allocated
   // addresses in ascending order, which dyld requires.
   //
@@ -988,8 +1078,8 @@ void Writer::finalizeAddresses() {
     // `fileOff + fileSize == next segment fileOff`. So we call alignTo() before
     // (instead of after) computing fileSize to ensure that the segments are
     // contiguous. We handle addr / vmSize similarly for the same reason.
-    fileOff = alignTo(fileOff, pageSize);
-    addr = alignTo(addr, pageSize);
+    fileOff = alignToPowerOf2(fileOff, pageSize);
+    addr = alignToPowerOf2(addr, pageSize);
     seg->vmSize = addr - seg->addr;
     seg->fileSize = fileOff - seg->fileOff;
     seg->assignAddressesToStartEndSymbols();
@@ -999,21 +1089,21 @@ void Writer::finalizeAddresses() {
 void Writer::finalizeLinkEditSegment() {
   TimeTraceScope timeScope("Finalize __LINKEDIT segment");
   // Fill __LINKEDIT contents.
-  std::vector<LinkEditSection *> linkEditSections{
-      in.rebase,
-      in.binding,
-      in.weakBinding,
-      in.lazyBinding,
-      in.exports,
-      symtabSection,
-      indirectSymtabSection,
-      dataInCodeSection,
-      functionStartsSection,
+  std::array<LinkEditSection *, 10> linkEditSections{
+      in.rebase,         in.binding,
+      in.weakBinding,    in.lazyBinding,
+      in.exports,        in.chainedFixups,
+      symtabSection,     indirectSymtabSection,
+      dataInCodeSection, functionStartsSection,
   };
-  parallelForEach(linkEditSections, [](LinkEditSection *osec) {
+  SmallVector<std::shared_future<void>> threadFutures;
+  threadFutures.reserve(linkEditSections.size());
+  for (LinkEditSection *osec : linkEditSections)
     if (osec)
-      osec->finalizeContents();
-  });
+      threadFutures.emplace_back(threadPool.async(
+          [](LinkEditSection *osec) { osec->finalizeContents(); }, osec));
+  for (std::shared_future<void> &future : threadFutures)
+    future.wait();
 
   // Now that __LINKEDIT is filled out, do a proper calculation of its
   // addresses and offsets.
@@ -1048,17 +1138,33 @@ void Writer::openFile() {
                                FileOutputBuffer::F_executable);
 
   if (!bufferOrErr)
-    error("failed to open " + config->outputFile + ": " +
+    fatal("failed to open " + config->outputFile + ": " +
           llvm::toString(bufferOrErr.takeError()));
-  else
-    buffer = std::move(*bufferOrErr);
+  buffer = std::move(*bufferOrErr);
+  in.bufferStart = buffer->getBufferStart();
 }
 
 void Writer::writeSections() {
   uint8_t *buf = buffer->getBufferStart();
+  std::vector<const OutputSection *> osecs;
   for (const OutputSegment *seg : outputSegments)
-    for (const OutputSection *osec : seg->getSections())
-      osec->writeTo(buf + osec->fileOff);
+    append_range(osecs, seg->getSections());
+
+  parallelForEach(osecs.begin(), osecs.end(), [&](const OutputSection *osec) {
+    osec->writeTo(buf + osec->fileOff);
+  });
+}
+
+void Writer::applyOptimizationHints() {
+  if (config->arch() != AK_arm64 || config->ignoreOptimizationHints)
+    return;
+
+  uint8_t *buf = buffer->getBufferStart();
+  TimeTraceScope timeScope("Apply linker optimization hints");
+  parallelForEach(inputFiles, [buf](const InputFile *file) {
+    if (const auto *objFile = dyn_cast<ObjFile>(file))
+      target->applyOptimizationHints(buf, *objFile);
+  });
 }
 
 // In order to utilize multiple cores, we first split the buffer into chunks,
@@ -1066,46 +1172,131 @@ void Writer::writeSections() {
 // values.
 void Writer::writeUuid() {
   TimeTraceScope timeScope("Computing UUID");
+
   ArrayRef<uint8_t> data{buffer->getBufferStart(), buffer->getBufferEnd()};
   unsigned chunkCount = parallel::strategy.compute_thread_count() * 10;
   // Round-up integer division
   size_t chunkSize = (data.size() + chunkCount - 1) / chunkCount;
   std::vector<ArrayRef<uint8_t>> chunks = split(data, chunkSize);
-  std::vector<uint64_t> hashes(chunks.size());
-  parallelForEachN(0, chunks.size(),
-                   [&](size_t i) { hashes[i] = xxHash64(chunks[i]); });
+  // Leave one slot for filename
+  std::vector<uint64_t> hashes(chunks.size() + 1);
+  SmallVector<std::shared_future<void>> threadFutures;
+  threadFutures.reserve(chunks.size());
+  for (size_t i = 0; i < chunks.size(); ++i)
+    threadFutures.emplace_back(threadPool.async(
+        [&](size_t j) { hashes[j] = xxHash64(chunks[j]); }, i));
+  for (std::shared_future<void> &future : threadFutures)
+    future.wait();
+  // Append the output filename so that identical binaries with different names
+  // don't get the same UUID.
+  hashes[chunks.size()] = xxHash64(sys::path::filename(config->finalOutput));
   uint64_t digest = xxHash64({reinterpret_cast<uint8_t *>(hashes.data()),
                               hashes.size() * sizeof(uint64_t)});
   uuidCommand->writeUuid(digest);
 }
 
+// This is step 5 of the algorithm described in the class comment of
+// ChainedFixupsSection.
+void Writer::buildFixupChains() {
+  if (!config->emitChainedFixups)
+    return;
+
+  const std::vector<Location> &loc = in.chainedFixups->getLocations();
+  if (loc.empty())
+    return;
+
+  TimeTraceScope timeScope("Build fixup chains");
+
+  const uint64_t pageSize = target->getPageSize();
+  constexpr uint32_t stride = 4; // for DYLD_CHAINED_PTR_64
+
+  for (size_t i = 0, count = loc.size(); i < count;) {
+    const OutputSegment *oseg = loc[i].isec->parent->parent;
+    uint8_t *buf = buffer->getBufferStart() + oseg->fileOff;
+    uint64_t pageIdx = loc[i].offset / pageSize;
+    ++i;
+
+    while (i < count && loc[i].isec->parent->parent == oseg &&
+           (loc[i].offset / pageSize) == pageIdx) {
+      uint64_t offset = loc[i].offset - loc[i - 1].offset;
+
+      auto fail = [&](Twine message) {
+        error(loc[i].isec->getSegName() + "," + loc[i].isec->getName() +
+              ", offset " +
+              Twine(loc[i].offset - loc[i].isec->parent->getSegmentOffset()) +
+              ": " + message);
+      };
+
+      if (offset < target->wordSize)
+        return fail("fixups overlap");
+      if (offset % stride != 0)
+        return fail(
+            "fixups are unaligned (offset " + Twine(offset) +
+            " is not a multiple of the stride). Re-link with -no_fixup_chains");
+
+      // The "next" field is in the same location for bind and rebase entries.
+      reinterpret_cast<dyld_chained_ptr_64_bind *>(buf + loc[i - 1].offset)
+          ->next = offset / stride;
+      ++i;
+    }
+  }
+}
+
 void Writer::writeCodeSignature() {
-  if (codeSignatureSection)
+  if (codeSignatureSection) {
+    TimeTraceScope timeScope("Write code signature");
     codeSignatureSection->writeHashes(buffer->getBufferStart());
+  }
 }
 
 void Writer::writeOutputFile() {
   TimeTraceScope timeScope("Write output file");
   openFile();
+  reportPendingUndefinedSymbols();
   if (errorCount())
     return;
   writeSections();
+  applyOptimizationHints();
+  buildFixupChains();
   writeUuid();
   writeCodeSignature();
 
   if (auto e = buffer->commit())
-    error("failed to write to the output file: " + toString(std::move(e)));
+    fatal("failed to write output '" + buffer->getPath() +
+          "': " + toString(std::move(e)));
 }
 
 template <class LP> void Writer::run() {
   treatSpecialUndefineds();
-  if (config->entry && !isa<Undefined>(config->entry))
-    prepareBranchTarget(config->entry);
-  scanRelocations();
-  if (in.stubHelper->isNeeded())
-    in.stubHelper->setup();
+  if (config->entry && needsBinding(config->entry))
+    in.stubs->addEntry(config->entry);
+
+  // Canonicalization of all pointers to InputSections should be handled by
+  // these two scan* methods. I.e. from this point onward, for all live
+  // InputSections, we should have `isec->canonical() == isec`.
   scanSymbols();
+  if (in.objcStubs->isNeeded())
+    in.objcStubs->setUp();
+  scanRelocations();
+  if (in.initOffsets->isNeeded())
+    in.initOffsets->setUp();
+
+  // Do not proceed if there were undefined or duplicate symbols.
+  reportPendingUndefinedSymbols();
+  reportPendingDuplicateSymbols();
+  if (errorCount())
+    return;
+
+  if (in.stubHelper && in.stubHelper->isNeeded())
+    in.stubHelper->setUp();
+
+  if (in.objCImageInfo->isNeeded())
+    in.objCImageInfo->finalizeContents();
+
+  // At this point, we should know exactly which output sections are needed,
+  // courtesy of scanSymbols() and scanRelocations().
   createOutputSections<LP>();
+
   // After this point, we create no new segments; HOWEVER, we might
   // yet create branch-range extension thunks for architectures whose
   // hardware call instructions have limited range, e.g., ARM(64).
@@ -1114,42 +1305,58 @@ template <class LP> void Writer::run() {
   sortSegmentsAndSections();
   createLoadCommands<LP>();
   finalizeAddresses();
+  threadPool.async([&] {
+    if (LLVM_ENABLE_THREADS && config->timeTraceEnabled)
+      timeTraceProfilerInitialize(config->timeTraceGranularity, "writeMapFile");
+    writeMapFile();
+    if (LLVM_ENABLE_THREADS && config->timeTraceEnabled)
+      timeTraceProfilerFinishThread();
+  });
   finalizeLinkEditSegment();
-  writeMapFile();
   writeOutputFile();
 }
 
 template <class LP> void macho::writeResult() { Writer().run<LP>(); }
 
+void macho::resetWriter() { LCDylib::resetInstanceCount(); }
+
 void macho::createSyntheticSections() {
   in.header = make<MachHeaderSection>();
-  if (config->dedupLiterals) {
-    in.cStringSection = make<DeduplicatedCStringSection>();
+  if (config->dedupStrings)
+    in.cStringSection =
+        make<DeduplicatedCStringSection>(section_names::cString);
+  else
+    in.cStringSection = make<CStringSection>(section_names::cString);
+  in.objcMethnameSection =
+      make<DeduplicatedCStringSection>(section_names::objcMethname);
+  in.wordLiteralSection = make<WordLiteralSection>();
+  if (config->emitChainedFixups) {
+    in.chainedFixups = make<ChainedFixupsSection>();
   } else {
-    in.cStringSection = make<CStringSection>();
+    in.rebase = make<RebaseSection>();
+    in.binding = make<BindingSection>();
+    in.weakBinding = make<WeakBindingSection>();
+    in.lazyBinding = make<LazyBindingSection>();
+    in.lazyPointers = make<LazyPointerSection>();
+    in.stubHelper = make<StubHelperSection>();
   }
-  in.wordLiteralSection =
-      config->dedupLiterals ? make<WordLiteralSection>() : nullptr;
-  in.rebase = make<RebaseSection>();
-  in.binding = make<BindingSection>();
-  in.weakBinding = make<WeakBindingSection>();
-  in.lazyBinding = make<LazyBindingSection>();
   in.exports = make<ExportSection>();
   in.got = make<GotSection>();
   in.tlvPointers = make<TlvPointerSection>();
-  in.lazyPointers = make<LazyPointerSection>();
   in.stubs = make<StubsSection>();
-  in.stubHelper = make<StubHelperSection>();
+  in.objcStubs = make<ObjCStubsSection>();
   in.unwindInfo = makeUnwindInfoSection();
+  in.objCImageInfo = make<ObjCImageInfoSection>();
+  in.initOffsets = make<InitOffsetsSection>();
 
   // This section contains space for just a single word, and will be used by
   // dyld to cache an address to the image loader it uses.
-  uint8_t *arr = bAlloc.Allocate<uint8_t>(target->wordSize);
+  uint8_t *arr = bAlloc().Allocate<uint8_t>(target->wordSize);
   memset(arr, 0, target->wordSize);
-  in.imageLoaderCache = make<ConcatInputSection>(
-      segment_names::data, section_names::data, /*file=*/nullptr,
+  in.imageLoaderCache = makeSyntheticInputSection(
+      segment_names::data, section_names::data, S_REGULAR,
       ArrayRef<uint8_t>{arr, target->wordSize},
-      /*align=*/target->wordSize, /*flags=*/S_REGULAR);
+      /*align=*/target->wordSize);
   // References from dyld are not visible to us, so ensure this section is
   // always treated as live.
   in.imageLoaderCache->live = true;
index 56f6f7a..066a0fd 100644 (file)
@@ -11,8 +11,7 @@
 
 #include <cstdint>
 
-namespace lld {
-namespace macho {
+namespace lld::macho {
 
 class OutputSection;
 class InputSection;
@@ -26,6 +25,7 @@ public:
 };
 
 template <class LP> void writeResult();
+void resetWriter();
 
 void createSyntheticSections();
 
@@ -35,7 +35,6 @@ void addNonLazyBindingEntries(const Symbol *, const InputSection *,
 
 extern OutputSection *firstTLVDataSection;
 
-} // namespace macho
-} // namespace lld
+} // namespace lld::macho
 
 #endif
index 1dc04d7..35eb6f8 100644 (file)
@@ -8,6 +8,7 @@ add_lld_library(lldMinGW
   LINK_COMPONENTS
   Option
   Support
+  TargetParser
 
   LINK_LIBS
   lldCOFF
index 7c6b865..9cb21b7 100644 (file)
 //===----------------------------------------------------------------------===//
 
 #include "lld/Common/Driver.h"
+#include "lld/Common/CommonLinkerContext.h"
 #include "lld/Common/ErrorHandler.h"
 #include "lld/Common/Memory.h"
 #include "lld/Common/Version.h"
 #include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/Triple.h"
@@ -44,6 +44,7 @@
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Host.h"
 #include "llvm/Support/Path.h"
+#include <optional>
 
 #if !defined(_MSC_VER) && !defined(__MINGW32__)
 #include <unistd.h>
@@ -61,12 +62,15 @@ enum {
 };
 
 // Create prefix string literals used in Options.td
-#define PREFIX(NAME, VALUE) static const char *const NAME[] = VALUE;
+#define PREFIX(NAME, VALUE)                                                    \
+  static constexpr llvm::StringLiteral NAME##_init[] = VALUE;                  \
+  static constexpr llvm::ArrayRef<llvm::StringLiteral> NAME(                   \
+      NAME##_init, std::size(NAME##_init) - 1);
 #include "Options.inc"
 #undef PREFIX
 
 // Create table mapping all options defined in Options.td
-static const opt::OptTable::Info infoTable[] = {
+static constexpr opt::OptTable::Info infoTable[] = {
 #define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12)      \
   {X1, X2, X10,         X11,         OPT_##ID, opt::Option::KIND##Class,       \
    X9, X8, OPT_##GROUP, OPT_##ALIAS, X7,       X12},
@@ -75,9 +79,9 @@ static const opt::OptTable::Info infoTable[] = {
 };
 
 namespace {
-class MinGWOptTable : public opt::OptTable {
+class MinGWOptTable : public opt::GenericOptTable {
 public:
-  MinGWOptTable() : OptTable(infoTable, false) {}
+  MinGWOptTable() : opt::GenericOptTable(infoTable, false) {}
   opt::InputArgList parse(ArrayRef<const char *> argv);
 };
 } // namespace
@@ -100,7 +104,7 @@ opt::InputArgList MinGWOptTable::parse(ArrayRef<const char *> argv) {
   unsigned missingCount;
 
   SmallVector<const char *, 256> vec(argv.data(), argv.data() + argv.size());
-  cl::ExpandResponseFiles(saver, getQuotingStyle(), vec);
+  cl::ExpandResponseFiles(saver(), getQuotingStyle(), vec);
   opt::InputArgList args = this->ParseArgs(vec, missingIndex, missingCount);
 
   if (missingCount)
@@ -111,12 +115,13 @@ opt::InputArgList MinGWOptTable::parse(ArrayRef<const char *> argv) {
 }
 
 // Find a file by concatenating given paths.
-static Optional<std::string> findFile(StringRef path1, const Twine &path2) {
+static std::optional<std::string> findFile(StringRef path1,
+                                           const Twine &path2) {
   SmallString<128> s;
   sys::path::append(s, path1, path2);
   if (sys::fs::exists(s))
     return std::string(s);
-  return None;
+  return std::nullopt;
 }
 
 // This is for -lfoo. We'll look for libfoo.dll.a or libfoo.a from search paths.
@@ -124,7 +129,7 @@ static std::string
 searchLibrary(StringRef name, ArrayRef<StringRef> searchPaths, bool bStatic) {
   if (name.startswith(":")) {
     for (StringRef dir : searchPaths)
-      if (Optional<std::string> s = findFile(dir, name.substr(1)))
+      if (std::optional<std::string> s = findFile(dir, name.substr(1)))
         return *s;
     error("unable to find library -l" + name);
     return "";
@@ -132,19 +137,19 @@ searchLibrary(StringRef name, ArrayRef<StringRef> searchPaths, bool bStatic) {
 
   for (StringRef dir : searchPaths) {
     if (!bStatic) {
-      if (Optional<std::string> s = findFile(dir, "lib" + name + ".dll.a"))
+      if (std::optional<std::string> s = findFile(dir, "lib" + name + ".dll.a"))
         return *s;
-      if (Optional<std::string> s = findFile(dir, name + ".dll.a"))
+      if (std::optional<std::string> s = findFile(dir, name + ".dll.a"))
         return *s;
     }
-    if (Optional<std::string> s = findFile(dir, "lib" + name + ".a"))
+    if (std::optional<std::string> s = findFile(dir, "lib" + name + ".a"))
+      return *s;
+    if (std::optional<std::string> s = findFile(dir, name + ".lib"))
       return *s;
     if (!bStatic) {
-      if (Optional<std::string> s = findFile(dir, name + ".lib"))
-        return *s;
-      if (Optional<std::string> s = findFile(dir, "lib" + name + ".dll"))
+      if (std::optional<std::string> s = findFile(dir, "lib" + name + ".dll"))
         return *s;
-      if (Optional<std::string> s = findFile(dir, name + ".dll"))
+      if (std::optional<std::string> s = findFile(dir, name + ".dll"))
         return *s;
     }
   }
@@ -154,12 +159,11 @@ searchLibrary(StringRef name, ArrayRef<StringRef> searchPaths, bool bStatic) {
 
 // Convert Unix-ish command line arguments to Windows-ish ones and
 // then call coff::link.
-bool mingw::link(ArrayRef<const char *> argsArr, bool canExitEarly,
-                 raw_ostream &stdoutOS, raw_ostream &stderrOS) {
-  lld::stdoutOS = &stdoutOS;
-  lld::stderrOS = &stderrOS;
-
-  stderrOS.enable_colors(stderrOS.has_colors());
+bool mingw::link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
+                 llvm::raw_ostream &stderrOS, bool exitEarly,
+                 bool disableOutput) {
+  auto *ctx = new CommonLinkerContext;
+  ctx->e.initialize(stdoutOS, stderrOS, exitEarly, disableOutput);
 
   MinGWOptTable parser;
   opt::InputArgList args = parser.parse(argsArr.slice(1));
@@ -265,6 +269,8 @@ bool mingw::link(ArrayRef<const char *> argsArr, bool canExitEarly,
     add("-filealign:" + StringRef(a->getValue()));
   if (auto *a = args.getLastArg(OPT_section_alignment))
     add("-align:" + StringRef(a->getValue()));
+  if (auto *a = args.getLastArg(OPT_heap))
+    add("-heap:" + StringRef(a->getValue()));
 
   if (auto *a = args.getLastArg(OPT_o))
     add("-out:" + StringRef(a->getValue()));
@@ -322,6 +328,9 @@ bool mingw::link(ArrayRef<const char *> argsArr, bool canExitEarly,
   if (args.hasFlag(OPT_disable_tsaware, OPT_tsaware, false))
     add("-tsaware:no");
 
+  if (args.hasFlag(OPT_disable_reloc_section, OPT_enable_reloc_section, false))
+    add("-fixed");
+
   if (args.hasFlag(OPT_no_insert_timestamp, OPT_insert_timestamp, false))
     add("-timestamp:0");
 
@@ -375,6 +384,26 @@ bool mingw::link(ArrayRef<const char *> argsArr, bool canExitEarly,
       error("unknown parameter: -m" + s);
   }
 
+  if (args.hasFlag(OPT_guard_cf, OPT_no_guard_cf, false)) {
+    if (args.hasFlag(OPT_guard_longjmp, OPT_no_guard_longjmp, true))
+      add("-guard:cf,longjmp");
+    else
+      add("-guard:cf,nolongjmp");
+  } else if (args.hasFlag(OPT_guard_longjmp, OPT_no_guard_longjmp, false)) {
+    auto *a = args.getLastArg(OPT_guard_longjmp);
+    warn("parameter " + a->getSpelling() +
+         " only takes effect when used with --guard-cf");
+  }
+
+  if (auto *a = args.getLastArg(OPT_error_limit)) {
+    int n;
+    StringRef s = a->getValue();
+    if (s.getAsInteger(10, n))
+      error(a->getSpelling() + ": number expected, but got " + s);
+    else
+      add("-errorlimit:" + s);
+  }
+
   for (auto *a : args.filtered(OPT_mllvm))
     add("-mllvm:" + StringRef(a->getValue()));
 
@@ -394,6 +423,8 @@ bool mingw::link(ArrayRef<const char *> argsArr, bool canExitEarly,
     add("-delayload:" + StringRef(a->getValue()));
   for (auto *a : args.filtered(OPT_wrap))
     add("-wrap:" + StringRef(a->getValue()));
+  for (auto *a : args.filtered(OPT_exclude_symbols))
+    add("-exclude-symbols:" + StringRef(a->getValue()));
 
   std::vector<StringRef> searchPaths;
   for (auto *a : args.filtered(OPT_L)) {
@@ -445,5 +476,9 @@ bool mingw::link(ArrayRef<const char *> argsArr, bool canExitEarly,
   // Pass the actual binary name, to make error messages be printed with
   // the right prefix.
   vec[0] = argsArr[0];
-  return coff::link(vec, canExitEarly, stdoutOS, stderrOS);
+
+  // The context will be re-created in the COFF driver.
+  lld::CommonLinkerContext::destroy();
+
+  return coff::link(vec, stdoutOS, stderrOS, exitEarly, disableOutput);
 }
index 50ac71b..c9c6063 100644 (file)
@@ -31,6 +31,11 @@ multiclass B_disable<string name, string help1, string help2> {
   def disable_ # NAME: Flag<["--", "-"], "disable-" # name>, HelpText<help2>;
 }
 
+multiclass B_enable_disable<string name, string help1, string help2> {
+  def enable_ # NAME: Flag<["--", "-"], "enable-" # name>, HelpText<help1>;
+  def disable_ # NAME: Flag<["--", "-"], "disable-" # name>, HelpText<help2>;
+}
+
 def L: JoinedOrSeparate<["-"], "L">, MetaVarName<"<dir>">,
   HelpText<"Add a directory to the library search path">;
 defm allow_multiple_definition: B<"allow-multiple-definition",
@@ -57,6 +62,8 @@ def enable_stdcall_fixup: F<"enable-stdcall-fixup">,
 defm entry: Eq<"entry", "Name of entry point symbol">, MetaVarName<"<entry>">;
 def exclude_all_symbols: F<"exclude-all-symbols">,
     HelpText<"Don't automatically export any symbols">;
+defm exclude_symbols: Eq<"exclude-symbols",
+    "Exclude symbols from automatic export">, MetaVarName<"<symbol[,symbol,...]>">;
 def export_all_symbols: F<"export-all-symbols">,
     HelpText<"Export all symbols even if a def file or dllexport attributes are used">;
 defm fatal_warnings: B<"fatal-warnings",
@@ -66,6 +73,7 @@ defm file_alignment: Eq<"file-alignment", "Set file alignment">;
 defm gc_sections: B<"gc-sections",
     "Remove unused sections",
     "Don't remove unused sections">;
+defm heap: Eq<"heap", "Set size of the initial heap">;
 def help: F<"help">, HelpText<"Print option help">;
 defm high_entropy_va: B_disable<"high-entropy-va",
     "Set the 'high entropy VA' flag", "Don't set the 'high entropy VA' flag">;
@@ -97,6 +105,8 @@ def o: JoinedOrSeparate<["-"], "o">, MetaVarName<"<path>">,
   HelpText<"Path to file to write output">;
 defm out_implib: Eq<"out-implib", "Import library name">;
 defm output_def: Eq<"output-def", "Output def file">;
+defm reloc_section: B_enable_disable<"reloc-section",
+     "Enable base relocations", "Disable base relocations">;
 defm section_alignment: Eq<"section-alignment", "Set section alignment">;
 def shared: F<"shared">, HelpText<"Build a shared object">;
 defm subs: Eq<"subsystem", "Specify subsystem">;
@@ -131,6 +141,13 @@ defm pdb: Eq<"pdb", "Output PDB debug info file, chosen implicitly if the argume
 defm thinlto_cache_dir: EqLong<"thinlto-cache-dir",
   "Path to ThinLTO cached object file directory">;
 defm Xlink : Eq<"Xlink", "Pass <arg> to the COFF linker">, MetaVarName<"<arg>">;
+defm guard_cf : B<"guard-cf", "Enable Control Flow Guard" ,
+  "Do not enable Control Flow Guard (default)">;
+defm guard_longjmp : B<"guard-longjmp",
+  "Enable Control Flow Guard long jump hardening (default for --guard-cf)" ,
+  "Do not enable Control Flow Guard long jump hardening">;
+defm error_limit:
+  EqLong<"error-limit", "Maximum number of errors to emit before stopping (0 = no limit)">;
 
 // Alias
 def alias_Bdynamic_call_shared: Flag<["-"], "call_shared">, Alias<Bdynamic>;
@@ -146,6 +163,7 @@ def alias_undefined_u: JoinedOrSeparate<["-"], "u">, Alias<undefined>;
 
 // Ignored options
 def: Joined<["-"], "O">;
+def: F<"as-needed">;
 def: F<"build-id">;
 def: F<"disable-auto-image-base">;
 def: F<"enable-auto-image-base">;
index 9883475..d3924f7 100644 (file)
@@ -1,3 +1,4 @@
+include(GNUInstallDirs)
 include(LLVMDistributionSupport)
 
 macro(add_lld_library name)
@@ -19,7 +20,7 @@ macro(add_lld_library name)
       ${export_to_lldtargets}
       LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX}
       ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX}
-      RUNTIME DESTINATION bin)
+      RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
 
     if (${ARG_SHARED} AND NOT CMAKE_CONFIGURATION_TYPES)
       add_llvm_install_targets(install-${name}
@@ -46,7 +47,7 @@ macro(add_lld_tool name)
     get_target_export_arg(${name} LLD export_to_lldtargets)
     install(TARGETS ${name}
       ${export_to_lldtargets}
-      RUNTIME DESTINATION bin
+      RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
       COMPONENT ${name})
 
     if(NOT CMAKE_CONFIGURATION_TYPES)
@@ -59,7 +60,7 @@ macro(add_lld_tool name)
 endmacro()
 
 macro(add_lld_symlink name dest)
-  add_llvm_tool_symlink(${name} ${dest} ALWAYS_GENERATE)
+  llvm_add_tool_symlink(LLD ${name} ${dest} ALWAYS_GENERATE)
   # Always generate install targets
-  llvm_install_symlink(${name} ${dest} ALWAYS_GENERATE)
+  llvm_install_symlink(LLD ${name} ${dest} ALWAYS_GENERATE)
 endmacro()
index 62d03fa..66db035 100644 (file)
@@ -1,12 +1,21 @@
+include(GNUInstallPackageDir)
+include(ExtendPath)
+include(FindPrefixFromConfig)
+
 # Generate a list of CMake library targets so that other CMake projects can
 # link against them. LLVM calls its version of this file LLVMExports.cmake, but
 # the usual CMake convention seems to be ${Project}Targets.cmake.
-set(LLD_INSTALL_PACKAGE_DIR lib${LLVM_LIBDIR_SUFFIX}/cmake/lld)
-set(lld_cmake_builddir "${CMAKE_BINARY_DIR}/${LLD_INSTALL_PACKAGE_DIR}")
+set(LLD_INSTALL_PACKAGE_DIR "${CMAKE_INSTALL_PACKAGEDIR}/lld" CACHE STRING
+  "Path for CMake subdirectory for LLD (defaults to '${CMAKE_INSTALL_PACKAGEDIR}/lld')")
+# CMAKE_INSTALL_PACKAGEDIR might be absolute, so don't reuse below.
+set(lld_cmake_builddir "${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}/cmake/lld")
 
 # Keep this in sync with llvm/cmake/CMakeLists.txt!
-set(LLVM_INSTALL_PACKAGE_DIR lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm)
-set(llvm_cmake_builddir "${LLVM_BINARY_DIR}/${LLVM_INSTALL_PACKAGE_DIR}")
+set(LLVM_INSTALL_PACKAGE_DIR "${CMAKE_INSTALL_PACKAGEDIR}/llvm" CACHE STRING
+  "Path for CMake subdirectory for LLVM (defaults to '${CMAKE_INSTALL_PACKAGEDIR}/llvm')")
+# CMAKE_INSTALL_PACKAGEDIR might be absolute, so don't reuse below.
+string(REPLACE "${CMAKE_CFG_INTDIR}" "." llvm_cmake_builddir "${LLVM_LIBRARY_DIR}")
+set(llvm_cmake_builddir "${llvm_cmake_builddir}/cmake/llvm")
 
 get_property(LLD_EXPORTS GLOBAL PROPERTY LLD_EXPORTS)
 export(TARGETS ${LLD_EXPORTS} FILE ${lld_cmake_builddir}/LLDTargets.cmake)
@@ -23,28 +32,27 @@ configure_file(
   ${CMAKE_CURRENT_SOURCE_DIR}/LLDConfig.cmake.in
   ${lld_cmake_builddir}/LLDConfig.cmake
   @ONLY)
+configure_file(
+  ${CMAKE_CURRENT_SOURCE_DIR}/LLDConfigVersion.cmake.in
+  ${lld_cmake_builddir}/LLDConfigVersion.cmake
+  @ONLY)
 set(LLD_CONFIG_CMAKE_DIR)
 set(LLD_CONFIG_LLVM_CMAKE_DIR)
 
 # Generate LLDConfig.cmake for the install tree.
-set(LLD_CONFIG_CODE "
-# Compute the installation prefix from this LLVMConfig.cmake file location.
-get_filename_component(LLD_INSTALL_PREFIX \"\${CMAKE_CURRENT_LIST_FILE}\" PATH)")
-# Construct the proper number of get_filename_component(... PATH)
-# calls to compute the installation prefix.
-string(REGEX REPLACE "/" ";" _count "${LLD_INSTALL_PACKAGE_DIR}")
-foreach(p ${_count})
-  set(LLD_CONFIG_CODE "${LLD_CONFIG_CODE}
-get_filename_component(LLD_INSTALL_PREFIX \"\${LLD_INSTALL_PREFIX}\" PATH)")
-endforeach(p)
-set(LLD_CONFIG_CMAKE_DIR "\${LLD_INSTALL_PREFIX}/${LLD_INSTALL_PACKAGE_DIR}")
-set(LLD_CONFIG_LLVM_CMAKE_DIR "\${LLD_INSTALL_PREFIX}/${LLVM_INSTALL_PACKAGE_DIR}")
+find_prefix_from_config(LLD_CONFIG_CODE LLD_INSTALL_PREFIX "${LLD_INSTALL_PACKAGE_DIR}")
+extend_path(LLD_CONFIG_CMAKE_DIR "\${LLD_INSTALL_PREFIX}" "${LLD_INSTALL_PACKAGE_DIR}")
+extend_path(LLD_CONFIG_LLVM_CMAKE_DIR "\${LLD_INSTALL_PREFIX}" "${LLVM_INSTALL_PACKAGE_DIR}")
 get_config_exports_includes(LLD LLD_CONFIG_INCLUDE_EXPORTS)
-set(LLD_CONFIG_INCLUDE_DIRS "\${LLD_INSTALL_PREFIX}/include")
+extend_path(LLD_CONFIG_INCLUDE_DIRS "\${LLD_INSTALL_PREFIX}" "${CMAKE_INSTALL_INCLUDEDIR}")
 configure_file(
   ${CMAKE_CURRENT_SOURCE_DIR}/LLDConfig.cmake.in
   ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/LLDConfig.cmake
   @ONLY)
+configure_file(
+  ${CMAKE_CURRENT_SOURCE_DIR}/LLDConfigVersion.cmake.in
+  ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/LLDConfigVersion.cmake
+  @ONLY)
 set(LLD_CONFIG_CODE)
 set(LLD_CONFIG_CMAKE_DIR)
 
@@ -53,6 +61,7 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
 
   install(FILES
     ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/LLDConfig.cmake
+    ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/LLDConfigVersion.cmake
     DESTINATION ${LLD_INSTALL_PACKAGE_DIR}
     COMPONENT lld-cmake-exports)
 
index 38ca429..6c48de6 100644 (file)
@@ -2,7 +2,8 @@
 
 @LLD_CONFIG_CODE@
 
-find_package(LLVM REQUIRED CONFIG
+set(LLVM_VERSION @LLVM_VERSION_MAJOR@.@LLVM_VERSION_MINOR@.@LLVM_VERSION_PATCH@)
+find_package(LLVM ${LLVM_VERSION} EXACT REQUIRED CONFIG
              HINTS "@LLD_CONFIG_LLVM_CMAKE_DIR@")
 
 set(LLD_EXPORTED_TARGETS "@LLD_EXPORTS@")
diff --git a/gnu/llvm/lld/cmake/modules/LLDConfigVersion.cmake.in b/gnu/llvm/lld/cmake/modules/LLDConfigVersion.cmake.in
new file mode 100644 (file)
index 0000000..e9ac4ed
--- /dev/null
@@ -0,0 +1,13 @@
+set(PACKAGE_VERSION "@PACKAGE_VERSION@")
+
+# LLVM is API-compatible only with matching major.minor versions
+# and patch versions not less than that requested.
+if("@LLVM_VERSION_MAJOR@.@LLVM_VERSION_MINOR@" VERSION_EQUAL
+    "${PACKAGE_FIND_VERSION_MAJOR}.${PACKAGE_FIND_VERSION_MINOR}"
+   AND NOT "@LLVM_VERSION_PATCH@" VERSION_LESS "${PACKAGE_FIND_VERSION_PATCH}")
+  set(PACKAGE_VERSION_COMPATIBLE 1)
+  if("@LLVM_VERSION_PATCH@" VERSION_EQUAL
+      "${PACKAGE_FIND_VERSION_PATCH}")
+    set(PACKAGE_VERSION_EXACT 1)
+  endif()
+endif()
index af29996..bc20375 100644 (file)
@@ -61,13 +61,16 @@ In the case where no linker script has been provided or every ``SECTIONS``
 command is followed by ``INSERT``, LLD applies built-in rules which are similar
 to GNU ld's internal linker scripts.
 
-- Align the first section in a ``PT_LOAD`` segment according to ``-z noseparate-code``,
-  ``-z separate-code``, or ``-z separate-loadable-segments``
-- Define ``__bss_start``, ``end``, ``_end``, ``etext``, ``_etext``, ``edata``, ``_edata``
-- Sort ``.ctors.*``/``.dtors.*``/``.init_array.*``/``.fini_array.*`` and PowerPC64 specific ``.toc``
+- Align the first section in a ``PT_LOAD`` segment according to
+  ``-z noseparate-code``, ``-z separate-code``, or
+  ``-z separate-loadable-segments``
+- Define ``__bss_start``, ``end``, ``_end``, ``etext``, ``_etext``, ``edata``,
+  ``_edata``
+- Sort ``.ctors.*``/``.dtors.*``/``.init_array.*``/``.fini_array.*`` and
+  PowerPC64 specific ``.toc``
 - Place input ``.text.*`` into output ``.text``, and handle certain variants
-  (``.text.hot.``, ``.text.unknown.``, ``.text.unlikely.``, etc) in the precense of
-  ``-z keep-text-section-prefix``.
+  (``.text.hot.``, ``.text.unknown.``, ``.text.unlikely.``, etc) in the
+  presence of ``-z keep-text-section-prefix``.
 
 Output section description
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -94,6 +97,21 @@ The presence of ``address`` can cause the condition unsatisfied. LLD will warn.
 GNU ld from Binutils 2.35 onwards will reduce sh_addralign so that
 sh_addr=0 (modulo sh_addralign).
 
+Output section type
+-------------------
+
+When an *OutputSection* *S* has ``(type)``, LLD will set ``sh_type`` or
+``sh_flags`` of *S*. ``type`` is one of:
+
+- ``NOLOAD``: set ``sh_type`` to ``SHT_NOBITS``.
+- ``COPY``, ``INFO``, ``OVERLAY``: clear the ``SHF_ALLOC`` bit in ``sh_flags``.
+- ``TYPE=<value>``: set ``sh_type`` to the specified value. ``<value>`` must be
+  an integer or one of ``SHT_PROGBITS, SHT_NOTE, SHT_NOBITS, SHT_INIT_ARRAY,
+  SHT_FINI_ARRAY, SHT_PREINIT_ARRAY``.
+
+When ``sh_type`` is specified, it is an error if an input section in *S* has a
+different type.
+
 Output section alignment
 ------------------------
 
diff --git a/gnu/llvm/lld/docs/ELF/start-stop-gc.rst b/gnu/llvm/lld/docs/ELF/start-stop-gc.rst
new file mode 100644 (file)
index 0000000..0baa1ca
--- /dev/null
@@ -0,0 +1,66 @@
+-z start-stop-gc
+================
+
+If your ``-Wl,--gc-sections`` build fail with a linker error like this:
+
+    error: undefined symbol: __start_meta
+    >>> referenced by {{.*}}
+    >>> the encapsulation symbol needs to be retained under --gc-sections properly; consider -z nostart-stop-gc (see https://lld.llvm.org/start-stop-gc) 
+
+it is likely your C identifier name sections are not properly annotated to
+suffice under ``--gc-sections``.
+
+``__start_meta`` and ``__stop_meta`` are sometimed called encapsulation
+symbols. In October 2015, GNU ld switched behavior and made a ``__start_meta``
+reference from a live section retain all ``meta`` input sections. This
+conservative behavior works for existing code which does not take GC into fair
+consideration, but unnecessarily increases sizes for modern metadata section
+usage which desires precise GC.
+
+GNU ld 2.37 added ``-z start-stop-gc`` to restore the traditional behavior
+ld.lld 13.0.0 defaults to ``-z start-stop-gc`` and supports ``-z nostart-stop-gc``
+to switch to the conservative behavior.
+
+The Apple ld64 linker has a similar ``section$start`` feature and always
+allowed GC (like ``-z start-stop-gc``).
+
+Annotate C identifier name sections
+-----------------------------------
+
+A C identifier name section (``meta``) sometimes depends on another section.
+Let that section reference ``meta`` via a relocation.
+
+.. code-block:: c
+
+  asm(".pushsection .init_array,\"aw\",@init_array\n" \
+      ".reloc ., R_AARCH64_NONE, meta\n"              \
+      ".popsection\n")
+
+If a relocation is inconvenient, consider using ``__attribute__((retain))``
+(GCC 11 with modern binutils, Clang 13).
+
+.. code-block:: c
+
+  #pragma GCC diagnostic push
+  #pragma GCC diagnostic ignored "-Wattributes"
+  __attribute__((retain,used,section("meta")))
+  static const char dummy[0];
+  #pragma GCC diagnostic pop
+
+GCC before 11 and Clang before 13 do not recognize ``__attribute__((retain))``,
+so ``-Wattributes`` may need to be ignored. On ELF targets,
+``__attribute__((used))`` prevents compiler discarding, but does not affect
+linker ``--gc-sections``.
+
+In a macro, you may use:
+
+.. code-block:: c
+
+  _Pragma("GCC diagnostic push")
+  _Pragma("GCC diagnostic ignored \"-Wattributes\"")
+  ...
+  _Pragma("GCC diagnostic pop")
+
+If you use the ``SECTIONS`` command in a linker script, use
+`the ``KEEP`` keyword <https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html>`_, e.g.
+``meta : { KEEP(*(meta)) }``
diff --git a/gnu/llvm/lld/docs/MachO/index.rst b/gnu/llvm/lld/docs/MachO/index.rst
new file mode 100644 (file)
index 0000000..d6b3a55
--- /dev/null
@@ -0,0 +1,62 @@
+Mach-O LLD Port
+===============
+
+LLD is a linker from the LLVM project that is a drop-in replacement
+for system linkers and runs much faster than them. It also provides
+features that are useful for toolchain developers. This document
+will describe the Mach-O port.
+
+Features
+--------
+
+- LLD is a drop-in replacement for Apple's Mach-O linker, ld64, that accepts the
+  same command line arguments.
+
+- LLD is very fast. When you link a large program on a multicore
+  machine, you can expect that LLD runs more than twice as fast as the ld64
+  linker.
+
+Download
+--------
+
+LLD is available as a pre-built binary by going to the `latest release <https://github.com/llvm/llvm-project/releases>`_,
+downloading the appropriate bundle (``clang+llvm-<version>-<your architecture>-<your platform>.tar.xz``),
+decompressing it, and locating the binary at ``bin/ld64.lld``. Note
+that if ``ld64.lld`` is moved out of ``bin``, it must still be accompanied
+by its sibling file ``lld``, as ``ld64.lld`` is technically a symlink to ``lld``.
+
+Build
+-----
+
+The easiest way to build LLD is to
+check out the entire LLVM projects/sub-projects from a git mirror and
+build that tree. You need ``cmake`` and of course a C++ compiler.
+
+.. code-block:: console
+
+  $ git clone https://github.com/llvm/llvm-project llvm-project
+  $ mkdir build
+  $ cd build
+  $ cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS='lld' ../llvm-project/llvm
+  $ ninja check-lld-macho
+
+Then you can find output binary at ``build/bin/ld64.lld``. Note
+that if ``ld64.lld`` is moved out of ``bin``, it must still be accompanied
+by its sibling file ``lld``, as ``ld64.lld`` is technically a symlink to ``lld``.
+
+Using LLD
+---------
+
+LLD can be used by adding ``-fuse-ld=/path/to/ld64.lld`` to the linker flags.
+For Xcode, this can be done by adding it to "Other linker flags" in the build
+settings. For Bazel, this can be done with ``--linkopt`` or with
+`rules_apple_linker <https://github.com/keith/rules_apple_linker>`_.
+
+.. seealso::
+
+  :doc:`ld64-vs-lld` has more info on the differences between the two linkers.
+
+.. toctree::
+   :hidden:
+
+   ld64-vs-lld
diff --git a/gnu/llvm/lld/docs/MachO/ld64-vs-lld.rst b/gnu/llvm/lld/docs/MachO/ld64-vs-lld.rst
new file mode 100644 (file)
index 0000000..327bf0a
--- /dev/null
@@ -0,0 +1,56 @@
+=================
+ld64 vs LLD-MachO
+=================
+
+This doc lists all significant deliberate differences in behavior between ld64
+and LLD-MachO.
+
+Dead Stripping Duplicate Symbols
+********************************
+ld64 strips dead code before reporting duplicate symbols. By default, LLD does
+the opposite. ld64's behavior hides ODR violations, so we have chosen not
+to follow it. But, to make adoption easy, LLD can mimic this behavior via
+the ``--dead-strip-duplicates`` flag. Usage of this flag is discouraged, and
+this behavior should be fixed in the source. However, for sources that are not
+within the user's control, this will mitigate users for adoption.
+
+``-no_deduplicate`` Flag
+************************
+- ld64: This turns off ICF (deduplication pass) in the linker.
+- LLD: This turns off ICF and string merging in the linker.
+
+String Alignment
+****************
+LLD is `slightly less conservative about aligning cstrings
+<https://reviews.llvm.org/D121342>`_, allowing it to pack them more compactly.
+This should not result in any meaningful semantic difference.
+
+ObjC Symbols Treatment
+**********************
+There are differences in how LLD and ld64 handle ObjC symbols loaded from
+archives.
+
+- ld64:
+   1. Duplicate ObjC symbols from the same archives will not raise an error.
+      ld64 will pick the first one.
+   2. Duplicate ObjC symbols from different archives will raise a "duplicate
+      symbol" error.
+- LLD: Duplicate symbols, regardless of which archives they are from, will
+  raise errors.
+
+Aliases
+=======
+ld64 treats all aliases as strong extern definitions. Having two aliases of the
+same name, or an alias plus a regular extern symbol of the same name, both
+result in duplicate symbol errors. LLD does not check for duplicate aliases;
+instead we perform alias resolution first, and only then do we check for
+duplicate symbols. In particular, we will not report a duplicate symbol error if
+the aliased symbols turn out to be weak definitions, but ld64 will.
+
+``ZERO_AR_DATE`` enabled by default
+***********************************
+ld64 has a special mode where it sets some stabs modification times to 0 for
+hermetic builds, enabled by setting any value for the ``ZERO_AR_DATE``
+environment variable. LLD flips this default to perfer hermetic builds, but
+allows disabling this behavior by setting ``ZERO_AR_DATE=0``. Any other value
+of ``ZERO_AR_DATE`` will be ignored.
index 50af6e7..a450923 100644 (file)
@@ -1,19 +1,21 @@
-========================
-lld 13.0.0 Release Notes
-========================
+===========================
+lld |release| Release Notes
+===========================
 
 .. contents::
     :local:
 
-.. warning::
-   These are in-progress notes for the upcoming LLVM 13.0.0 release.
-   Release notes for previous releases can be found on
-   `the Download Page <https://releases.llvm.org/download.html>`_.
+.. only:: PreRelease
+
+  .. warning::
+     These are in-progress notes for the upcoming LLVM |release| release.
+     Release notes for previous releases can be found on
+     `the Download Page <https://releases.llvm.org/download.html>`_.
 
 Introduction
 ============
 
-This document contains the release notes for the lld linker, release 13.0.0.
+This document contains the release notes for the lld linker, release |release|.
 Here we describe the status of lld, including major improvements
 from the previous release. All lld releases may be downloaded
 from the `LLVM releases web site <https://llvm.org/releases/>`_.
@@ -24,169 +26,80 @@ Non-comprehensive list of changes in this release
 ELF Improvements
 ----------------
 
-* ``-z start-stop-gc`` is now supported and becomes the default.
-  (`D96914 <https://reviews.llvm.org/D96914>`_)
-  (`rG6d2d3bd0 <https://reviews.llvm.org/rG6d2d3bd0a61f5fc7fd9f61f48bc30e9ca77cc619>`_)
-* ``--shuffle-sections=<seed>`` has been changed to ``--shuffle-sections=<section-glob>=<seed>``.
-  If seed is -1, the matched input sections are reversed.
-  (`D98445 <https://reviews.llvm.org/D98445>`_)
-  (`D98679 <https://reviews.llvm.org/D98679>`_)
-* ``-Bsymbolic -Bsymbolic-functions`` has been changed to behave the same as ``-Bsymbolic-functions``. This matches GNU ld.
-  (`D102461 <https://reviews.llvm.org/D102461>`_)
-* ``-Bno-symbolic`` has been added.
-  (`D102461 <https://reviews.llvm.org/D102461>`_)
-* A new linker script command ``OVERWRITE_SECTIONS`` has been added.
-  (`D103303 <https://reviews.llvm.org/D103303>`_)
-* ``-Bsymbolic-non-weak-functions`` has been added as a ``STB_GLOBAL`` subset of ``-Bsymbolic-functions``.
-  (`D102570 <https://reviews.llvm.org/D102570>`_)
-* ``--no-allow-shlib-undefined`` has been improved to catch more cases.
-  (`D101996 <https://reviews.llvm.org/D101996>`_)
-* ``__rela_iplt_start`` is no longer defined for -pie/-shared.
-  This makes GCC/Clang ``-static-pie`` built executables work.
-  (`rG8cb78e99 <https://reviews.llvm.org/rf8cb78e99aae9aa3f89f7bfe667db2c5b767f21f>`_)
-* IRELATIVE/TLSDESC relocations now support ``-z rel``.
-  (`D100544 <https://reviews.llvm.org/D100544>`_)
-* Section groups with a zero flag are now supported.
-  This is used by ``comdat nodeduplicate`` in LLVM IR.
-  (`D96636 <https://reviews.llvm.org/D96636>`_)
-  (`D106228 <https://reviews.llvm.org/D106228>`_)
-* Defined symbols are now resolved before undefined symbols to stabilize the bheavior of archive member extraction.
-  (`D95985 <https://reviews.llvm.org/D95985>`_)
-* ``STB_WEAK`` symbols are now preferred over COMMON symbols as a fix to a ``--fortran-common`` regression.
-  (`D105945 <https://reviews.llvm.org/D105945>`_)
-* Absolute relocations referencing undef weak now produce dynamic relocations for -pie, matching GOT-generating relocations.
-  (`D105164 <https://reviews.llvm.org/D105164>`_)
-* Exported symbols are now communicated to the LTO library so as to make LTO
-  based whole program devirtualization (``-flto=thin -fwhole-program-vtables``)
-  work with shared objects.
-  (`D91583 <https://reviews.llvm.org/D91583>`_)
-* Whole program devirtualization now respects ``local:`` version nodes in a version script.
-  (`D98220 <https://reviews.llvm.org/D98220>`_)
-  (`D98686 <https://reviews.llvm.org/D98686>`_)
-* ``local:`` version nodes in a version script now apply to non-default version symbols.
-  (`D107234 <https://reviews.llvm.org/D107234>`_)
-* If an object file defines both ``foo`` and ``foo@v1``, now only ``foo@v1`` will be in the output.
-  (`D107235 <https://reviews.llvm.org/D107235>`_)
-* Copy relocations on non-default version symbols are now supported.
-  (`D107535 <https://reviews.llvm.org/D107535>`_)
-
-Linker script changes:
-
-* ``.``, ``$``, and double quotes can now be used in symbol names in expressions.
-  (`D98306 <https://reviews.llvm.org/D98306>`_)
-  (`rGe7a7ad13 <https://reviews.llvm.org/rGe7a7ad134fe182aad190cb3ebc441164470e92f5>`_)
-* Fixed value of ``.`` in the output section description of ``.tbss``.
-  (`D107288 <https://reviews.llvm.org/D107288>`_)
-* ``NOLOAD`` sections can now be placed in a ``PT_LOAD`` program header.
-  (`D103815 <https://reviews.llvm.org/D103815>`_)
-* ``OUTPUT_FORMAT(default, big, little)`` now consults ``-EL`` and ``-EB``.
-  (`D96214 <https://reviews.llvm.org/D96214>`_)
-* The ``OVERWRITE_SECTIONS`` command has been added.
-  (`D103303 <https://reviews.llvm.org/D103303>`_)
-* The section order within an ``INSERT AFTER`` command is now preserved.
-  (`D105158 <https://reviews.llvm.org/D105158>`_)
-
-Architecture specific changes:
-
-* aarch64_be is now supported.
-  (`D96188 <https://reviews.llvm.org/D96188>`_)
-* The AMDGPU port now supports ``--amdhsa-code-object-version=4`` object files;
-  (`D95811 <https://reviews.llvm.org/D95811>`_)
-* The ARM port now accounts for PC biases in range extension thunk creation.
-  (`D97550 <https://reviews.llvm.org/D97550>`_)
-* The AVR port now computes ``e_flags``.
-  (`D99754 <https://reviews.llvm.org/D99754>`_)
-* The Mips port now omits unneeded dynamic relocations for PIE non-preemptible TLS.
-  (`D101382 <https://reviews.llvm.org/D101382>`_)
-* The PowerPC port now supports ``--power10-stubs=no`` to omit Power10 instructions from call stubs.
-  (`D94625 <https://reviews.llvm.org/D94625>`_)
-* Fixed a thunk creation bug in the PowerPC port when TOC/NOTOC calls are mixed.
-  (`D101837 <https://reviews.llvm.org/D101837>`_)
-* The RISC-V port now resolves undefined weak relocations to the current location if not using PLT.
-  (`D103001 <https://reviews.llvm.org/D103001>`_)
-* ``R_386_GOTOFF`` relocations from .debug_info are now allowed to be compatible with GCC.
-  (`D95994 <https://reviews.llvm.org/D95994>`_)
-* ``gotEntrySize`` has been added to improve support for the ILP32 ABI of x86-64.
-  (`D102569 <https://reviews.llvm.org/D102569>`_)
+* Link speed improved greatly compared with lld 15.0. Notably input section
+  initialization and relocation scanning are now parallel.
+  (`D130810 <https://reviews.llvm.org/D130810>`_)
+  (`D133003 <https://reviews.llvm.org/D133003>`_)
+* ``ELFCOMPRESS_ZSTD`` compressed input sections are now supported.
+  (`D129406 <https://reviews.llvm.org/D129406>`_)
+* ``--compress-debug-sections=zstd`` is now available to compress debug
+  sections with zstd (``ELFCOMPRESS_ZSTD``).
+  (`D133548 <https://reviews.llvm.org/D133548>`_)
+* ``--no-warnings``/``-w`` is now available to suppress warnings.
+  (`D136569 <https://reviews.llvm.org/D136569>`_)
+* ``DT_RISCV_VARIANT_CC`` is now produced if at least one ``R_RISCV_JUMP_SLOT``
+  relocation references a symbol with the ``STO_RISCV_VARIANT_CC`` bit.
+  (`D107951 <https://reviews.llvm.org/D107951>`_)
+* ``DT_STATIC_TLS`` is now set for AArch64/PPC32/PPC64 initial-exec TLS models
+  when producing a shared object.
+* ``--no-undefined-version`` is now the default; symbols named in version
+  scripts that have no matching symbol in the output will be reported. Use
+  ``--undefined-version`` to revert to the old behavior.
+  (`D135402 <https://reviews.llvm.org/D135402>`_)
+* ``-V`` is now an alias for ``-v`` to support ``gcc -fuse-ld=lld -v`` on many targets.
+* ``-r`` no longer defines ``__global_pointer$`` or ``_TLS_MODULE_BASE_``.
+* A corner case of mixed GCC and Clang object files (``STB_WEAK`` and
+  ``STB_GNU_UNIQUE`` in different COMDATs) is now supported.
+  (`D136381 <https://reviews.llvm.org/D136381>`_)
+* The output ``SHT_RISCV_ATTRIBUTES`` section now merges all input components
+  instead of picking the first input component.
+  (`D138550 <https://reviews.llvm.org/D138550>`_)
+* For x86-32, ``-fno-plt`` GD/LD TLS models ``call *___tls_get_addr@GOT(%reg)``
+  are now supported. Previous output might have runtime crash.
+* Armv4(T) thunks are now supported.
+  (`D139888 <https://reviews.llvm.org/D139888>`_)
+  (`D141272 <https://reviews.llvm.org/D141272>`_)
 
 Breaking changes
 ----------------
 
-* ``--shuffle-sections=<seed>`` has been changed to ``--shuffle-sections=<section-glob>=<seed>``.
-  Specify ``*`` as ``<section-glob>`` to get the previous behavior.
-
 COFF Improvements
 -----------------
 
-* Avoid thread exhaustion when running on 32 bit Windows.
-  (`D105506 <https://reviews.llvm.org/D105506>`_)
-
-* Improve terminating the process on Windows while a thread pool might be
-  running. (`D102944 <https://reviews.llvm.org/D102944>`_)
+* The linker command line entry in ``S_ENVBLOCK`` of the PDB is now stripped
+  from input files, to align with MSVC behavior.
+  (`D137723 <https://reviews.llvm.org/D137723>`_)
+* Switched from SHA1 to BLAKE3 for PDB type hashing / ``-gcodeview-ghash``
+  (`D137101 <https://reviews.llvm.org/D137101>`_)
+* Improvements to the PCH.OBJ files handling. Now LLD behaves the same as MSVC
+  link.exe when merging PCH.OBJ files that don't have the same signature.
+  (`D136762 <https://reviews.llvm.org/D136762>`_)
+* Changed the OrdinalBase for DLLs from 0 to 1, matching the output from
+  both MS link.exe and GNU ld. (`D134140 <https://reviews.llvm.org/D134140>`_)
 
 MinGW Improvements
 ------------------
 
-* Support for linking directly against a DLL without using an import library
-  has been added. (`D104530 <https://reviews.llvm.org/D104530>`_ and
-  `D104531 <https://reviews.llvm.org/D104531>`_)
-
-* Fix linking with ``--export-all-symbols`` in combination with
-  ``-function-sections``. (`D101522 <https://reviews.llvm.org/D101522>`_ and
-  `D101615 <https://reviews.llvm.org/D101615>`_)
-
-* Fix automatic export of symbols from LTO objects.
-  (`D101569 <https://reviews.llvm.org/D101569>`_)
-
-* Accept more spellings of some options.
-  (`D107237 <https://reviews.llvm.org/D107237>`_ and
-  `D107253 <https://reviews.llvm.org/D107253>`_)
-
-Mach-O Improvements
--------------------
-
-The Mach-O backend is now able to link several large, real-world programs,
-though we are still working out the kinks.
-
-* arm64 is now supported as a target. (`D88629 <https://reviews.llvm.org/D88629>`_)
-* arm64_32 is now supported as a target. (`D99822 <https://reviews.llvm.org/D99822>`_)
-* Branch-range-extension thunks are now supported. (`D100818 <https://reviews.llvm.org/D100818>`_)
-* ``-dead_strip`` is now supported. (`D103324 <https://reviews.llvm.org/D103324>`_)
-* Support for identical code folding (``--icf=all``) has been added.
-  (`D103292 <https://reviews.llvm.org/D103292>`_)
-* Support for special ``$start`` and ``$end`` symbols for segment & sections has been
-  added. (`D106767 <https://reviews.llvm.org/D106767>`_, `D106629 <https://reviews.llvm.org/D106629>`_)
-* ``$ld$previous`` symbols are now supported. (`D103505 <https://reviews.llvm.org/D103505 >`_)
-* ``$ld$install_name`` symbols are now supported. (`D103746 <https://reviews.llvm.org/D103746>`_)
-* ``__mh_*_header`` symbols are now supported. (`D97007 <https://reviews.llvm.org/D97007>`_)
-* LC_CODE_SIGNATURE is now supported. (`D96164 <https://reviews.llvm.org/D96164>`_)
-* LC_FUNCTION_STARTS is now supported. (`D97260 <https://reviews.llvm.org/D97260>`_)
-* LC_DATA_IN_CODE is now supported. (`D103006 <https://reviews.llvm.org/D103006>`_)
-* Bind opcodes are more compactly encoded. (`D106128 <https://reviews.llvm.org/D106128>`_,
-  `D105075 <https://reviews.llvm.org/D105075>`_)
-* LTO cache support has been added. (`D105922 <https://reviews.llvm.org/D105922>`_)
-* ``-application_extension`` is now supported. (`D105818 <https://reviews.llvm.org/D105818>`_)
-* ``-export_dynamic`` is now partially supported. (`D105482 <https://reviews.llvm.org/D105482>`_)
-* ``-arch_multiple`` is now supported. (`D105450 <https://reviews.llvm.org/D105450>`_)
-* ``-final_output`` is now supported. (`D105449 <https://reviews.llvm.org/D105449>`_)
-* ``-umbrella`` is now supported. (`D105448 <https://reviews.llvm.org/D105448>`_)
-* ``--print-dylib-search`` is now supported. (`D103985 <https://reviews.llvm.org/D103985>`_)
-* ``-force_load_swift_libs`` is now supported. (`D103709 <https://reviews.llvm.org/D103709>`_)
-* ``-reexport_framework``, ``-reexport_library``, ``-reexport-l`` are now supported.
-  (`D103497 <https://reviews.llvm.org/D103497>`_)
-* ``.weak_def_can_be_hidden`` is now supported. (`D101080 <https://reviews.llvm.org/D101080>`_)
-* ``-add_ast_path`` is now supported. (`D100076 <https://reviews.llvm.org/D100076>`_)
-* ``-segprot`` is now supported.  (`D99389 <https://reviews.llvm.org/D99389>`_)
-* ``-dependency_info`` is now partially supported. (`D98559 <https://reviews.llvm.org/D98559>`_)
-* ``--time-trace`` is now supported. (`D98419 <https://reviews.llvm.org/D98419>`_)
-* ``-mark_dead_strippable_dylib`` is now supported. (`D98262 <https://reviews.llvm.org/D98262>`_)
-* ``-[un]exported_symbol[s_list]`` is now supported. (`D98223 <https://reviews.llvm.org/D98223>`_)
-* ``-flat_namespace`` is now supported. (`D97641 <https://reviews.llvm.org/D97641>`_)
-* ``-rename_section`` and ``-rename_segment`` are now supported. (`D97600 <https://reviews.llvm.org/D97600>`_)
-* ``-bundle_loader`` is now supported. (`D95913 <https://reviews.llvm.org/D95913>`_)
-* ``-map`` is now partially supported. (`D98323 <https://reviews.llvm.org/D98323>`_)
-
-There were numerous other bug-fixes as well.
+* The lld-specific options ``--guard-cf``, ``--no-guard-cf``,
+  ``--guard-longjmp`` and ``--no-guard-longjmp`` has been added to allow
+  enabling Control Flow Guard and long jump hardening. These options are
+  disabled by default, but enabling ``--guard-cf`` will also enable
+  ``--guard-longjmp`` unless ``--no-guard-longjmp`` is also specified.
+  ``--guard-longjmp`` depends on ``--guard-cf`` and cannot be used by itself.
+  Note that these features require the ``_load_config_used`` symbol to contain
+  the load config directory and be filled with the required symbols.
+  (`D132808 <https://reviews.llvm.org/D132808>`_)
+
+* Pick up libraries named ``<name>.lib`` when linked with ``-l<name>``, even
+  if ``-static`` has been specified. This fixes conformance to what
+  GNU ld does. (`D135651 <https://reviews.llvm.org/D135651>`_)
+
+* Unwinding in Rust code on i386 in MinGW builds has been fixed, by avoiding
+  to leave out the ``rust_eh_personality`` symbol.
+  (`D136879 <https://reviews.llvm.org/D136879>`_)
+
+MachO Improvements
+------------------
 
 WebAssembly Improvements
 ------------------------
index c01df99..dad3177 100644 (file)
@@ -19,7 +19,7 @@ This is the object format that the llvm will produce when run with the
 Usage
 -----
 
-The WebAssembly version of lld is installed as **wasm-ld**.  It shared many 
+The WebAssembly version of lld is installed as **wasm-ld**.  It shared many
 common linker flags with **ld.lld** but also includes several
 WebAssembly-specific options:
 
@@ -75,6 +75,11 @@ WebAssembly-specific options:
   flag which corresponds to ``--unresolve-symbols=ignore`` +
   ``--import-undefined``.
 
+.. option:: --allow-undefined-file=<filename>
+
+  Like ``--allow-undefined``, but the filename specified a flat list of
+  symbols, one per line, which are allowed to be undefined.
+
 .. option:: --unresolved-symbols=<method>
 
   This is a more full featured version of ``--allow-undefined``.
@@ -92,6 +97,21 @@ WebAssembly-specific options:
      this is trivial.  For direct function calls, the linker will generate a
      trapping stub function in place of the undefined function.
 
+  import-dynamic:
+
+     Undefined symbols generate WebAssembly imports, including undefined data
+     symbols.  This is somewhat similar to the --import-undefined option but
+     works all symbol types.  This options puts limitations on the type of
+     relocations that are allowed for imported data symbols.  Relocations that
+     require absolute data addresses (i.e. All R_WASM_MEMORY_ADDR_I32) will
+     generate an error if they cannot be resolved statically.  For clang/llvm
+     this means inputs should be compiled with `-fPIC` (i.e. `pic` or
+     `dynamic-no-pic` relocation models).  This options is useful for linking
+     binaries that are themselves static (non-relocatable) but whose undefined
+     symbols are resolved by a dynamic linker.  Since the dynamic linking API is
+     experimental, this option currently requires `--experimental-pic` to also
+     be specified.
+
 .. option:: --import-memory
 
   Import memory from the environment.
@@ -167,11 +187,39 @@ Imports
 By default no undefined symbols are allowed in the final binary.  The flag
 ``--allow-undefined`` results in a WebAssembly import being defined for each
 undefined symbol.  It is then up to the runtime to provide such symbols.
+``--allow-undefined-file`` is the same but allows a list of symbols to be
+specified.
 
 Alternatively symbols can be marked in the source code as with the
 ``import_name`` and/or ``import_module`` clang attributes which signals that
 they are expected to be undefined at static link time.
 
+Stub Libraries
+~~~~~~~~~~~~~~
+
+Another way to specify imports and exports is via a "stub library".  This
+feature is inspired by the ELF stub objects which are supported by the Solaris
+linker.  Stub libraries are text files that can be passed as normal linker
+inputs, similar to how linker scripts can be passed to the ELF linker.  The stub
+library is a stand-in for a set of symbols that will be available at runtime,
+but doesn't contain any actual code or data.  Instead it contains just a list of
+symbols, one per line.  Each symbol can specify zero or more dependencies.
+These dependencies are symbols that must be defined, and exported, by the output
+module if the symbol is question is imported/required by the output module.
+
+For example, imagine the runtime provides an external symbol ``foo`` that
+depends on the ``malloc`` and ``free``.  This can be expressed simply as::
+
+  #STUB
+  foo: malloc,free
+
+Here we are saying that ``foo`` is allowed to be imported (undefined) but that
+if it is imported, then the output module must also export ``malloc`` and
+``free`` to the runtime.  If ``foo`` is imported (undefined), but the output
+module does not define ``malloc`` and ``free`` then the link will fail.
+
+Stub libraries must begin with ``#STUB`` on a line by itself.
+
 Garbage Collection
 ~~~~~~~~~~~~~~~~~~
 
@@ -205,6 +253,6 @@ Missing features
   supported.
 - No support for creating shared libraries.  The spec for shared libraries in
   WebAssembly is still in flux:
-  https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
+  https://github.com/WebAssembly/tool-conventions/blob/main/DynamicLinking.md
 
-.. _linking: https://github.com/WebAssembly/tool-conventions/blob/master/Linking.md
+.. _linking: https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md
index 588be93..f9ecb72 100644 (file)
@@ -1,4 +1,9 @@
 <h3>Bugs</h3>
 
-<p>lld bugs should be reported at the
-  LLVM <a href="https://bugs.llvm.org/">Bugzilla</a>.</p>
+<p>
+To report bugs, please visit
+<a href="https://github.com/llvm/llvm-project/labels/lld:COFF">PE/COFF</a>,
+<a href="https://github.com/llvm/llvm-project/labels/lld:ELF">ELF</a>,
+<a href="https://github.com/llvm/llvm-project/labels/lld:MachO">Mach-O</a>, or
+<a href="https://github.com/llvm/llvm-project/labels/lld:wasm">WebAssembly</a>.
+</p>
index 2df1aa7..95befdd 100644 (file)
@@ -43,15 +43,6 @@ master_doc = 'index'
 project = u'lld'
 copyright = u'2011-%d, LLVM Project' % date.today().year
 
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# The short version.
-version = '13'
-# The full version, including alpha/beta/rc tags.
-release = '13'
-
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
 #language = None
index 40da6d7..ce63203 100644 (file)
@@ -10,9 +10,7 @@ WebAssembly in descending order of completeness. Internally, LLD consists of
 several different linkers. The ELF port is the one that will be described in
 this document. The PE/COFF port is complete, including
 Windows debug info (PDB) support. The WebAssembly port is still a work in
-progress (See :doc:`WebAssembly`).  The Mach-O port is built based on a
-different architecture than the others. For the details about Mach-O, please
-read :doc:`AtomLLD`.
+progress (See :doc:`WebAssembly`).
 
 Features
 --------
@@ -20,15 +18,6 @@ Features
 - LLD is a drop-in replacement for the GNU linkers that accepts the
   same command line arguments and linker scripts as GNU.
 
-  We are currently working closely with the FreeBSD project to make
-  LLD default system linker in future versions of the operating
-  system, so we are serious about addressing compatibility issues. As
-  of February 2017, LLD is able to link the entire FreeBSD/amd64 base
-  system including the kernel. With a few work-in-progress patches it
-  can link approximately 95% of the ports collection on AMD64. For the
-  details, see `FreeBSD quarterly status report
-  <https://www.freebsd.org/news/status/report-2016-10-2016-12.html#Using-LLVM%27s-LLD-Linker-as-FreeBSD%27s-System-Linker>`_.
-
 - LLD is very fast. When you link a large program on a multicore
   machine, you can expect that LLD runs more than twice as fast as the GNU
   gold linker. Your mileage may vary, though.
@@ -170,7 +159,6 @@ document soon.
    :maxdepth: 1
 
    NewLLD
-   AtomLLD
    WebAssembly
    windows_support
    missingkeyfunction
@@ -178,4 +166,6 @@ document soon.
    Partitions
    ReleaseNotes
    ELF/linker_script
+   ELF/start-stop-gc
    ELF/warn_backrefs
+   MachO/index
index 48f7b40..40c8ee4 100644 (file)
@@ -30,7 +30,8 @@ int64_t getInteger(llvm::opt::InputArgList &args, unsigned key,
 
 int64_t getHex(llvm::opt::InputArgList &args, unsigned key, int64_t Default);
 
-std::vector<StringRef> getStrings(llvm::opt::InputArgList &args, int id);
+llvm::SmallVector<StringRef, 0> getStrings(llvm::opt::InputArgList &args,
+                                           int id);
 
 uint64_t getZOptionValue(llvm::opt::InputArgList &args, int id, StringRef key,
                          uint64_t Default);
diff --git a/gnu/llvm/lld/include/lld/Common/CommonLinkerContext.h b/gnu/llvm/lld/include/lld/Common/CommonLinkerContext.h
new file mode 100644 (file)
index 0000000..0627bbd
--- /dev/null
@@ -0,0 +1,61 @@
+//===- CommonLinkerContext.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Entry point for all global state in lldCommon. The objective is for LLD to be
+// used "as a library" in a thread-safe manner.
+//
+// Instead of program-wide globals or function-local statics, we prefer
+// aggregating all "global" states into a heap-based structure
+// (CommonLinkerContext). This also achieves deterministic initialization &
+// shutdown for all "global" states.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_COMMON_COMMONLINKINGCONTEXT_H
+#define LLD_COMMON_COMMONLINKINGCONTEXT_H
+
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+#include "llvm/Support/StringSaver.h"
+
+namespace llvm {
+class raw_ostream;
+} // namespace llvm
+
+namespace lld {
+struct SpecificAllocBase;
+class CommonLinkerContext {
+public:
+  CommonLinkerContext();
+  virtual ~CommonLinkerContext();
+
+  static void destroy();
+
+  llvm::BumpPtrAllocator bAlloc;
+  llvm::StringSaver saver{bAlloc};
+  llvm::DenseMap<void *, SpecificAllocBase *> instances;
+
+  ErrorHandler e;
+};
+
+// Retrieve the global state. Currently only one state can exist per process,
+// but in the future we plan on supporting an arbitrary number of LLD instances
+// in a single process.
+CommonLinkerContext &commonContext();
+
+template <typename T = CommonLinkerContext> T &context() {
+  return static_cast<T &>(commonContext());
+}
+
+bool hasContext();
+
+inline llvm::StringSaver &saver() { return context().saver; }
+inline llvm::BumpPtrAllocator &bAlloc() { return context().bAlloc; }
+} // namespace lld
+
+#endif
index b77985a..6310d7a 100644 (file)
@@ -26,9 +26,9 @@ namespace lld {
 class DWARFCache {
 public:
   DWARFCache(std::unique_ptr<llvm::DWARFContext> dwarf);
-  llvm::Optional<llvm::DILineInfo> getDILineInfo(uint64_t offset,
-                                                 uint64_t sectionIndex);
-  llvm::Optional<std::pair<std::string, unsigned>>
+  std::optional<llvm::DILineInfo> getDILineInfo(uint64_t offset,
+                                                uint64_t sectionIndex);
+  std::optional<std::pair<std::string, unsigned>>
   getVariableLoc(StringRef name);
 
   llvm::DWARFContext *getContext() { return dwarf.get(); }
index eb5bc7b..19573e6 100644 (file)
@@ -28,34 +28,29 @@ SafeReturn safeLldMain(int argc, const char **argv, llvm::raw_ostream &stdoutOS,
                        llvm::raw_ostream &stderrOS);
 
 namespace coff {
-bool link(llvm::ArrayRef<const char *> args, bool canExitEarly,
-          llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS);
+bool link(llvm::ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,
+          llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput);
 }
 
 namespace mingw {
-bool link(llvm::ArrayRef<const char *> args, bool canExitEarly,
-          llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS);
+bool link(llvm::ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,
+          llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput);
 }
 
 namespace elf {
-bool link(llvm::ArrayRef<const char *> args, bool canExitEarly,
-          llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS);
-}
-
-namespace mach_o {
-bool link(llvm::ArrayRef<const char *> args, bool canExitEarly,
-          llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS);
+bool link(llvm::ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,
+          llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput);
 }
 
 namespace macho {
-bool link(llvm::ArrayRef<const char *> args, bool canExitEarly,
-          llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS);
+bool link(llvm::ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,
+          llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput);
 }
 
 namespace wasm {
-bool link(llvm::ArrayRef<const char *> args, bool canExitEarly,
-          llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS);
-}
+bool link(llvm::ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,
+          llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput);
 }
+} // namespace lld
 
 #endif
index 95d92f3..0b69bb6 100644 (file)
@@ -73,6 +73,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/FileOutputBuffer.h"
+#include <mutex>
 
 namespace llvm {
 class DiagnosticInfo;
@@ -81,11 +82,6 @@ class raw_ostream;
 
 namespace lld {
 
-// We wrap stdout and stderr so that you can pass alternative stdout/stderr as
-// arguments to lld::*::link() functions.
-extern llvm::raw_ostream *stdoutOS;
-extern llvm::raw_ostream *stderrOS;
-
 llvm::raw_ostream &outs();
 llvm::raw_ostream &errs();
 
@@ -93,6 +89,11 @@ enum class ErrorTag { LibNotFound, SymbolNotFound };
 
 class ErrorHandler {
 public:
+  ~ErrorHandler();
+
+  void initialize(llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS,
+                  bool exitEarly, bool disableOutput);
+
   uint64_t errorCount = 0;
   uint64_t errorLimit = 20;
   StringRef errorLimitExceededMsg = "too many errors emitted, stopping now";
@@ -100,6 +101,7 @@ public:
   StringRef logName = "lld";
   bool exitEarly = true;
   bool fatalWarnings = false;
+  bool suppressWarnings = false;
   bool verbose = false;
   bool vsDiagnostics = false;
   bool disableOutput = false;
@@ -109,14 +111,12 @@ public:
   void error(const Twine &msg, ErrorTag tag, ArrayRef<StringRef> args);
   [[noreturn]] void fatal(const Twine &msg);
   void log(const Twine &msg);
-  void message(const Twine &msg);
+  void message(const Twine &msg, llvm::raw_ostream &s);
   void warn(const Twine &msg);
 
-  void reset() {
-    if (cleanupCallback)
-      cleanupCallback();
-    *this = ErrorHandler();
-  }
+  raw_ostream &outs();
+  raw_ostream &errs();
+  void flushStreams();
 
   std::unique_ptr<llvm::FileOutputBuffer> outputBuffer;
 
@@ -124,20 +124,33 @@ private:
   using Colors = raw_ostream::Colors;
 
   std::string getLocation(const Twine &msg);
+  void reportDiagnostic(StringRef location, Colors c, StringRef diagKind,
+                        const Twine &msg);
+
+  // We want to separate multi-line messages with a newline. `sep` is "\n"
+  // if the last messages was multi-line. Otherwise "".
+  llvm::StringRef sep;
+
+  // We wrap stdout and stderr so that you can pass alternative stdout/stderr as
+  // arguments to lld::*::link() functions. Since lld::outs() or lld::errs() can
+  // be indirectly called from multiple threads, we protect them using a mutex.
+  // In the future, we plan on supporting several concurrent linker contexts,
+  // which explains why the mutex is not a global but part of this context.
+  std::mutex mu;
+  llvm::raw_ostream *stdoutOS{};
+  llvm::raw_ostream *stderrOS{};
 };
 
 /// Returns the default error handler.
 ErrorHandler &errorHandler();
 
-inline void error(const Twine &msg) { errorHandler().error(msg); }
-inline void error(const Twine &msg, ErrorTag tag, ArrayRef<StringRef> args) {
-  errorHandler().error(msg, tag, args);
-}
-[[noreturn]] inline void fatal(const Twine &msg) { errorHandler().fatal(msg); }
-inline void log(const Twine &msg) { errorHandler().log(msg); }
-inline void message(const Twine &msg) { errorHandler().message(msg); }
-inline void warn(const Twine &msg) { errorHandler().warn(msg); }
-inline uint64_t errorCount() { return errorHandler().errorCount; }
+void error(const Twine &msg);
+void error(const Twine &msg, ErrorTag tag, ArrayRef<StringRef> args);
+[[noreturn]] void fatal(const Twine &msg);
+void log(const Twine &msg);
+void message(const Twine &msg, llvm::raw_ostream &s = outs());
+void warn(const Twine &msg);
+uint64_t errorCount();
 
 [[noreturn]] void exitLld(int val);
 
index c19364a..6872adf 100644 (file)
@@ -45,7 +45,6 @@ class WasmSymbol;
 
 namespace wasm {
 struct WasmTag;
-struct WasmTagType;
 struct WasmFunction;
 struct WasmGlobal;
 struct WasmGlobalType;
@@ -97,7 +96,6 @@ using llvm::wasm::WasmSignature;
 using llvm::wasm::WasmTable;
 using llvm::wasm::WasmTableType;
 using llvm::wasm::WasmTag;
-using llvm::wasm::WasmTagType;
 } // end namespace lld.
 
 namespace std {
index f516a32..c7612a0 100644 (file)
 #define LLD_COMMON_MEMORY_H
 
 #include "llvm/Support/Allocator.h"
-#include "llvm/Support/StringSaver.h"
-#include <vector>
 
 namespace lld {
-
-// Use this arena if your object doesn't have a destructor.
-extern llvm::BumpPtrAllocator bAlloc;
-extern llvm::StringSaver saver;
-
-void freeArena();
-
-// These two classes are hack to keep track of all
-// SpecificBumpPtrAllocator instances.
+// A base class only used by the CommonLinkerContext to keep track of the
+// SpecificAlloc<> instances.
 struct SpecificAllocBase {
-  SpecificAllocBase() { instances.push_back(this); }
   virtual ~SpecificAllocBase() = default;
-  virtual void reset() = 0;
-  static std::vector<SpecificAllocBase *> instances;
+  static SpecificAllocBase *getOrCreate(void *tag, size_t size, size_t align,
+                                        SpecificAllocBase *(&creator)(void *));
 };
 
+// An arena of specific types T, created on-demand.
 template <class T> struct SpecificAlloc : public SpecificAllocBase {
-  void reset() override { alloc.DestroyAll(); }
+  static SpecificAllocBase *create(void *storage) {
+    return new (storage) SpecificAlloc<T>();
+  }
   llvm::SpecificBumpPtrAllocator<T> alloc;
+  static int tag;
 };
 
-// Use a static local for these singletons so they are only registered if an
-// object of this instance is ever constructed. Otherwise we will create and
-// register ELF allocators for COFF and the reverse.
+// The address of this static member is only used as a key in
+// CommonLinkerContext::instances. Its value does not matter.
+template <class T> int SpecificAlloc<T>::tag = 0;
+
+// Creates the arena on-demand on the first call; or returns it, if it was
+// already created.
 template <typename T>
 inline llvm::SpecificBumpPtrAllocator<T> &getSpecificAllocSingleton() {
-  static SpecificAlloc<T> instance;
-  return instance.alloc;
+  SpecificAllocBase *instance = SpecificAllocBase::getOrCreate(
+      &SpecificAlloc<T>::tag, sizeof(SpecificAlloc<T>),
+      alignof(SpecificAlloc<T>), SpecificAlloc<T>::create);
+  return ((SpecificAlloc<T> *)instance)->alloc;
 }
 
-// Use this arena if your object has a destructor.
-// Your destructor will be invoked from freeArena().
+// Creates new instances of T off a (almost) contiguous arena/object pool. The
+// instances are destroyed whenever lldMain() goes out of scope.
 template <typename T, typename... U> T *make(U &&... args) {
   return new (getSpecificAllocSingleton<T>().Allocate())
       T(std::forward<U>(args)...);
 }
 
+template <typename T>
+inline llvm::SpecificBumpPtrAllocator<T> &
+getSpecificAllocSingletonThreadLocal() {
+  thread_local SpecificAlloc<T> instance;
+  return instance.alloc;
+}
+
+// Create a new instance of T off a thread-local SpecificAlloc, used by code
+// like parallel input section initialization. The use cases assume that the
+// return value outlives the containing parallelForEach (if exists), which is
+// currently guaranteed: when parallelForEach returns, the threads allocating
+// the TLS are not destroyed.
+//
+// Note: Some ports (e.g. ELF) have lots of global states which are currently
+// infeasible to remove, and context() just adds overhead with no benefit. The
+// allocation performance is of higher importance, so we simply use thread_local
+// allocators instead of doing context indirection and pthread_getspecific.
+template <typename T, typename... U> T *makeThreadLocal(U &&...args) {
+  return new (getSpecificAllocSingletonThreadLocal<T>().Allocate())
+      T(std::forward<U>(args)...);
+}
+
+template <typename T> T *makeThreadLocalN(size_t n) {
+  return new (getSpecificAllocSingletonThreadLocal<T>().Allocate(n)) T[n];
+}
+
 } // namespace lld
 
 #endif
index 71126f6..2216968 100644 (file)
 #define LLD_STRINGS_H
 
 #include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/GlobPattern.h"
 #include <string>
 #include <vector>
 
 namespace lld {
-// Returns a demangled C++ symbol name. If Name is not a mangled
-// name, it returns name.
-std::string demangleItanium(llvm::StringRef name);
 
-std::vector<uint8_t> parseHex(llvm::StringRef s);
+llvm::SmallVector<uint8_t, 0> parseHex(llvm::StringRef s);
 bool isValidCIdentifier(llvm::StringRef s);
 
 // Write the contents of the a buffer to a file
index 04428b5..9bc22d4 100644 (file)
 #ifndef LLD_COMMON_TARGETOPTIONSCOMMANDFLAGS_H
 #define LLD_COMMON_TARGETOPTIONSCOMMANDFLAGS_H
 
-#include "llvm/ADT/Optional.h"
 #include "llvm/Support/CodeGen.h"
 #include "llvm/Target/TargetOptions.h"
+#include <optional>
 
 namespace lld {
 llvm::TargetOptions initTargetOptionsFromCodeGenFlags();
-llvm::Optional<llvm::Reloc::Model> getRelocModelFromCMModel();
-llvm::Optional<llvm::CodeModel::Model> getCodeModelFromCMModel();
+std::optional<llvm::Reloc::Model> getRelocModelFromCMModel();
+std::optional<llvm::CodeModel::Model> getCodeModelFromCMModel();
 std::string getCPUStr();
 std::vector<std::string> getMAttrs();
 }
index b37388c..7aca966 100644 (file)
@@ -38,7 +38,8 @@ class Timer {
 public:
   Timer(llvm::StringRef name, Timer &parent);
 
-  static Timer &root();
+  // Creates the root timer.
+  explicit Timer(llvm::StringRef name);
 
   void addToTotal(std::chrono::nanoseconds time) { total += time.count(); }
   void print();
@@ -46,7 +47,6 @@ public:
   double millis() const;
 
 private:
-  explicit Timer(llvm::StringRef name);
   void print(int depth, double totalDuration, bool recurse = true) const;
 
   std::atomic<std::chrono::nanoseconds::rep> total;
index e77b216..1262839 100644 (file)
@@ -1,31 +1,39 @@
 set(LLVM_LINK_COMPONENTS
   Support
+  TargetParser
   )
 
 add_lld_tool(lld
   lld.cpp
 
   SUPPORT_PLUGINS
+  GENERATE_DRIVER
   )
 export_executable_symbols_for_plugins(lld)
 
-target_link_libraries(lld
+function(lld_target_link_libraries target type)
+  target_link_libraries(${target} ${type} ${ARGN})
+  if (TARGET obj.${target})
+    target_link_libraries(obj.${target} ${ARGN})
+  endif()
+endfunction()
+
+lld_target_link_libraries(lld
   PRIVATE
   lldCommon
   lldCOFF
-  lldDriver
   lldELF
-  lldMachO2
+  lldMachO
   lldMinGW
   lldWasm
   )
 
 install(TARGETS lld
-  RUNTIME DESTINATION bin)
+  RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
 
 if(NOT LLD_SYMLINKS_TO_CREATE)
   set(LLD_SYMLINKS_TO_CREATE
-      lld-link ld.lld ld64.lld ld64.lld.darwinnew ld64.lld.darwinold wasm-ld)
+      lld-link ld.lld ld64.lld wasm-ld)
 endif()
 
 foreach(link ${LLD_SYMLINKS_TO_CREATE})
index 427ba58..6033bfb 100644 (file)
@@ -29,6 +29,7 @@ add_lld_library(lldWasm
   Option
   Passes
   Support
+  TargetParser
 
   LINK_LIBS
   lldCommon
index 7b20a6b..dba4a5c 100644 (file)
@@ -9,16 +9,21 @@
 #ifndef LLD_WASM_CONFIG_H
 #define LLD_WASM_CONFIG_H
 
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSet.h"
 #include "llvm/BinaryFormat/Wasm.h"
 #include "llvm/Support/CachePruning.h"
+#include <optional>
 
 namespace lld {
 namespace wasm {
 
+class InputFile;
+class Symbol;
+
 // For --unresolved-symbols.
-enum class UnresolvedPolicy { ReportError, Warn, Ignore };
+enum class UnresolvedPolicy { ReportError, Warn, Ignore, ImportDynamic };
 
 // This struct contains the global configuration for the linker.
 // Most fields are direct mapping from the command line options
@@ -35,13 +40,15 @@ struct Configuration {
   bool exportAll;
   bool exportDynamic;
   bool exportTable;
+  bool extendedConst;
   bool growableTable;
   bool gcSections;
-  bool importMemory;
+  std::optional<std::pair<llvm::StringRef, llvm::StringRef>> memoryImport;
+  std::optional<llvm::StringRef> memoryExport;
   bool sharedMemory;
   bool importTable;
   bool importUndefined;
-  llvm::Optional<bool> is64;
+  std::optional<bool> is64;
   bool mergeDataSegments;
   bool pie;
   bool printGcSections;
@@ -51,6 +58,7 @@ struct Configuration {
   bool stripAll;
   bool stripDebug;
   bool stackFirst;
+  bool isStatic = false;
   bool trace;
   uint64_t globalBase;
   uint64_t initialMemory;
@@ -60,7 +68,6 @@ struct Configuration {
   unsigned ltoo;
   unsigned optimize;
   llvm::StringRef thinLTOJobs;
-  bool ltoNewPassManager;
   bool ltoDebugPassManager;
   UnresolvedPolicy unresolvedSymbols;
 
@@ -68,16 +75,19 @@ struct Configuration {
   llvm::StringRef mapFile;
   llvm::StringRef outputFile;
   llvm::StringRef thinLTOCacheDir;
+  llvm::StringRef whyExtract;
 
   llvm::StringSet<> allowUndefinedSymbols;
   llvm::StringSet<> exportedSymbols;
   std::vector<llvm::StringRef> requiredExports;
-  std::vector<llvm::StringRef> searchPaths;
+  llvm::SmallVector<llvm::StringRef, 0> searchPaths;
   llvm::CachePruningPolicy thinLTOCachePolicy;
-  llvm::Optional<std::vector<std::string>> features;
+  std::optional<std::vector<std::string>> features;
+  std::optional<std::vector<std::string>> extraFeatures;
 
   // The following config options do not directly correspond to any
-  // particualr command line options.
+  // particular command line options, and should probably be moved to seperate
+  // Ctx struct as in ELF/Config.h
 
   // True if we are creating position-independent code.
   bool isPic;
@@ -91,6 +101,15 @@ struct Configuration {
   // for shared libraries (since they always added to a dynamic offset at
   // runtime).
   uint32_t tableBase = 0;
+
+  // Will be set to true if bss data segments should be emitted. In most cases
+  // this is not necessary.
+  bool emitBssSegments = false;
+
+  // A tuple of (reference, extractedFile, sym). Used by --why-extract=.
+  llvm::SmallVector<std::tuple<std::string, const InputFile *, const Symbol &>,
+                    0>
+      whyExtractRecords;
 };
 
 // The only instance of Configuration struct.
index 7e0c030..762d4c9 100644 (file)
@@ -14,6 +14,7 @@
 #include "SymbolTable.h"
 #include "Writer.h"
 #include "lld/Common/Args.h"
+#include "lld/Common/CommonLinkerContext.h"
 #include "lld/Common/ErrorHandler.h"
 #include "lld/Common/Filesystem.h"
 #include "lld/Common/Memory.h"
@@ -32,6 +33,7 @@
 #include "llvm/Support/Process.h"
 #include "llvm/Support/TarWriter.h"
 #include "llvm/Support/TargetSelect.h"
+#include <optional>
 
 #define DEBUG_TYPE "lld"
 
@@ -81,18 +83,15 @@ private:
 };
 } // anonymous namespace
 
-bool link(ArrayRef<const char *> args, bool canExitEarly, raw_ostream &stdoutOS,
-          raw_ostream &stderrOS) {
-  lld::stdoutOS = &stdoutOS;
-  lld::stderrOS = &stderrOS;
+bool link(ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,
+          llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput) {
+  // This driver-specific context will be freed later by lldMain().
+  auto *ctx = new CommonLinkerContext;
 
-  errorHandler().cleanupCallback = []() { freeArena(); };
-
-  errorHandler().logName = args::getFilenameWithoutExe(args[0]);
-  errorHandler().errorLimitExceededMsg =
-      "too many errors emitted, stopping now (use "
-      "-error-limit=0 to see all errors)";
-  stderrOS.enable_colors(stderrOS.has_colors());
+  ctx->e.initialize(stdoutOS, stderrOS, exitEarly, disableOutput);
+  ctx->e.logName = args::getFilenameWithoutExe(args[0]);
+  ctx->e.errorLimitExceededMsg = "too many errors emitted, stopping now (use "
+                                 "-error-limit=0 to see all errors)";
 
   config = make<Configuration>();
   symtab = make<SymbolTable>();
@@ -100,22 +99,19 @@ bool link(ArrayRef<const char *> args, bool canExitEarly, raw_ostream &stdoutOS,
   initLLVM();
   LinkerDriver().linkerMain(args);
 
-  // Exit immediately if we don't need to return to the caller.
-  // This saves time because the overhead of calling destructors
-  // for all globally-allocated objects is not negligible.
-  if (canExitEarly)
-    exitLld(errorCount() ? 1 : 0);
-
-  return !errorCount();
+  return errorCount() == 0;
 }
 
 // Create prefix string literals used in Options.td
-#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#define PREFIX(NAME, VALUE)                                                    \
+  static constexpr StringLiteral NAME##_init[] = VALUE;                        \
+  static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
+                                                std::size(NAME##_init) - 1);
 #include "Options.inc"
 #undef PREFIX
 
 // Create table mapping all options defined in Options.td
-static const opt::OptTable::Info optInfo[] = {
+static constexpr opt::OptTable::Info optInfo[] = {
 #define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12)      \
   {X1, X2, X10,         X11,         OPT_##ID, opt::Option::KIND##Class,       \
    X9, X8, OPT_##GROUP, OPT_##ALIAS, X7,       X12},
@@ -124,9 +120,9 @@ static const opt::OptTable::Info optInfo[] = {
 };
 
 namespace {
-class WasmOptTable : public llvm::opt::OptTable {
+class WasmOptTable : public opt::GenericOptTable {
 public:
-  WasmOptTable() : OptTable(optInfo) {}
+  WasmOptTable() : opt::GenericOptTable(optInfo) {}
   opt::InputArgList parse(ArrayRef<const char *> argv);
 };
 } // namespace
@@ -168,12 +164,13 @@ static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) {
 }
 
 // Find a file by concatenating given paths.
-static Optional<std::string> findFile(StringRef path1, const Twine &path2) {
+static std::optional<std::string> findFile(StringRef path1,
+                                           const Twine &path2) {
   SmallString<128> s;
   path::append(s, path1, path2);
   if (fs::exists(s))
     return std::string(s);
-  return None;
+  return std::nullopt;
 }
 
 opt::InputArgList WasmOptTable::parse(ArrayRef<const char *> argv) {
@@ -189,10 +186,13 @@ opt::InputArgList WasmOptTable::parse(ArrayRef<const char *> argv) {
 
   // Expand response files (arguments in the form of @<filename>)
   // and then parse the argument again.
-  cl::ExpandResponseFiles(saver, getQuotingStyle(args), vec);
+  cl::ExpandResponseFiles(saver(), getQuotingStyle(args), vec);
   args = this->ParseArgs(vec, missingIndex, missingCount);
 
   handleColorDiagnostics(args);
+  if (missingCount)
+    error(Twine(args.getArgString(missingIndex)) + ": missing argument");
+
   for (auto *arg : args.filtered(OPT_UNKNOWN))
     error("unknown argument: " + arg->getAsString(args));
   return args;
@@ -206,7 +206,7 @@ opt::InputArgList WasmOptTable::parse(ArrayRef<const char *> argv) {
 // attribute/flag in the object file itself.
 // See: https://github.com/WebAssembly/tool-conventions/issues/35
 static void readImportFile(StringRef filename) {
-  if (Optional<MemoryBufferRef> buf = readFile(filename))
+  if (std::optional<MemoryBufferRef> buf = readFile(filename))
     for (StringRef sym : args::getLines(*buf))
       config->allowUndefinedSymbols.insert(sym);
 }
@@ -239,8 +239,8 @@ std::vector<MemoryBufferRef> static getArchiveMembers(MemoryBufferRef mb) {
 }
 
 void LinkerDriver::addFile(StringRef path) {
-  Optional<MemoryBufferRef> buffer = readFile(path);
-  if (!buffer.hasValue())
+  std::optional<MemoryBufferRef> buffer = readFile(path);
+  if (!buffer)
     return;
   MemoryBufferRef mbref = *buffer;
 
@@ -280,32 +280,69 @@ void LinkerDriver::addFile(StringRef path) {
   case file_magic::wasm_object:
     files.push_back(createObjectFile(mbref));
     break;
+  case file_magic::unknown:
+    if (mbref.getBuffer().starts_with("#STUB")) {
+      files.push_back(make<StubFile>(mbref));
+      break;
+    }
+    [[fallthrough]];
   default:
     error("unknown file type: " + mbref.getBufferIdentifier());
   }
 }
 
-// Add a given library by searching it from input search paths.
-void LinkerDriver::addLibrary(StringRef name) {
+static std::optional<std::string> findFromSearchPaths(StringRef path) {
+  for (StringRef dir : config->searchPaths)
+    if (std::optional<std::string> s = findFile(dir, path))
+      return s;
+  return std::nullopt;
+}
+
+// This is for -l<basename>. We'll look for lib<basename>.a from
+// search paths.
+static std::optional<std::string> searchLibraryBaseName(StringRef name) {
   for (StringRef dir : config->searchPaths) {
-    if (Optional<std::string> s = findFile(dir, "lib" + name + ".a")) {
-      addFile(*s);
-      return;
-    }
+    // Currently we don't enable dyanmic linking at all unless -shared or -pie
+    // are used, so don't even look for .so files in that case..
+    if (config->isPic && !config->isStatic)
+      if (std::optional<std::string> s = findFile(dir, "lib" + name + ".so"))
+        return s;
+    if (std::optional<std::string> s = findFile(dir, "lib" + name + ".a"))
+      return s;
   }
+  return std::nullopt;
+}
+
+// This is for -l<namespec>.
+static std::optional<std::string> searchLibrary(StringRef name) {
+  if (name.startswith(":"))
+    return findFromSearchPaths(name.substr(1));
+  return searchLibraryBaseName(name);
+}
 
-  error("unable to find library -l" + name);
+// Add a given library by searching it from input search paths.
+void LinkerDriver::addLibrary(StringRef name) {
+  if (std::optional<std::string> path = searchLibrary(name))
+    addFile(saver().save(*path));
+  else
+    error("unable to find library -l" + name, ErrorTag::LibNotFound, {name});
 }
 
 void LinkerDriver::createFiles(opt::InputArgList &args) {
   for (auto *arg : args) {
     switch (arg->getOption().getID()) {
-    case OPT_l:
+    case OPT_library:
       addLibrary(arg->getValue());
       break;
     case OPT_INPUT:
       addFile(arg->getValue());
       break;
+    case OPT_Bstatic:
+      config->isStatic = true;
+      break;
+    case OPT_Bdynamic:
+      config->isStatic = false;
+      break;
     case OPT_whole_archive:
       inWholeArchive = true;
       break;
@@ -344,6 +381,8 @@ static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &args) {
     StringRef s = arg->getValue();
     if (s == "ignore-all")
       return UnresolvedPolicy::Ignore;
+    if (s == "import-dynamic")
+      return UnresolvedPolicy::ImportDynamic;
     if (s == "report-all")
       return errorOrWarn;
     error("unknown --unresolved-symbols value: " + s);
@@ -366,17 +405,32 @@ static void readConfigs(opt::InputArgList &args) {
   config->exportAll = args.hasArg(OPT_export_all);
   config->exportTable = args.hasArg(OPT_export_table);
   config->growableTable = args.hasArg(OPT_growable_table);
-  errorHandler().fatalWarnings =
-      args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false);
-  config->importMemory = args.hasArg(OPT_import_memory);
+
+  if (args.hasArg(OPT_import_memory_with_name)) {
+    config->memoryImport =
+        args.getLastArgValue(OPT_import_memory_with_name).split(",");
+  } else if (args.hasArg(OPT_import_memory)) {
+    config->memoryImport =
+        std::pair<llvm::StringRef, llvm::StringRef>(defaultModule, memoryName);
+  } else {
+    config->memoryImport =
+        std::optional<std::pair<llvm::StringRef, llvm::StringRef>>();
+  }
+
+  if (args.hasArg(OPT_export_memory_with_name)) {
+    config->memoryExport =
+        args.getLastArgValue(OPT_export_memory_with_name);
+  } else if (args.hasArg(OPT_export_memory)) {
+    config->memoryExport = memoryName;
+  } else {
+    config->memoryExport = std::optional<llvm::StringRef>();
+  }
+
   config->sharedMemory = args.hasArg(OPT_shared_memory);
   config->importTable = args.hasArg(OPT_import_table);
   config->importUndefined = args.hasArg(OPT_import_undefined);
   config->ltoo = args::getInteger(args, OPT_lto_O, 2);
   config->ltoPartitions = args::getInteger(args, OPT_lto_partitions, 1);
-  config->ltoNewPassManager =
-      args.hasFlag(OPT_no_lto_legacy_pass_manager, OPT_lto_legacy_pass_manager,
-                   LLVM_ENABLE_NEW_PASS_MANAGER);
   config->ltoDebugPassManager = args.hasArg(OPT_lto_debug_pass_manager);
   config->mapFile = args.getLastArgValue(OPT_Map);
   config->optimize = args::getInteger(args, OPT_O, 1);
@@ -391,7 +445,7 @@ static void readConfigs(opt::InputArgList &args) {
   config->printGcSections =
       args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false);
   config->saveTemps = args.hasArg(OPT_save_temps);
-  config->searchPaths = args::getStrings(args, OPT_L);
+  config->searchPaths = args::getStrings(args, OPT_library_path);
   config->shared = args.hasArg(OPT_shared);
   config->stripAll = args.hasArg(OPT_strip_all);
   config->stripDebug = args.hasArg(OPT_strip_debug);
@@ -402,11 +456,12 @@ static void readConfigs(opt::InputArgList &args) {
       parseCachePruningPolicy(args.getLastArgValue(OPT_thinlto_cache_policy)),
       "--thinlto-cache-policy: invalid cache policy");
   config->unresolvedSymbols = getUnresolvedSymbolPolicy(args);
+  config->whyExtract = args.getLastArgValue(OPT_why_extract);
   errorHandler().verbose = args.hasArg(OPT_verbose);
   LLVM_DEBUG(errorHandler().verbose = true);
 
   config->initialMemory = args::getInteger(args, OPT_initial_memory, 0);
-  config->globalBase = args::getInteger(args, OPT_global_base, 1024);
+  config->globalBase = args::getInteger(args, OPT_global_base, 0);
   config->maxMemory = args::getInteger(args, OPT_max_memory, 0);
   config->zStackSize =
       args::getZOptionValue(args, OPT_z, "stack-size", WasmPageSize);
@@ -442,11 +497,18 @@ static void readConfigs(opt::InputArgList &args) {
 
   if (auto *arg = args.getLastArg(OPT_features)) {
     config->features =
-        llvm::Optional<std::vector<std::string>>(std::vector<std::string>());
+        std::optional<std::vector<std::string>>(std::vector<std::string>());
     for (StringRef s : arg->getValues())
       config->features->push_back(std::string(s));
   }
 
+  if (auto *arg = args.getLastArg(OPT_extra_features)) {
+    config->extraFeatures =
+        std::optional<std::vector<std::string>>(std::vector<std::string>());
+    for (StringRef s : arg->getValues())
+      config->extraFeatures->push_back(std::string(s));
+  }
+
   // Legacy --allow-undefined flag which is equivalent to
   // --unresolve-symbols=ignore + --import-undefined
   if (args.hasArg(OPT_allow_undefined)) {
@@ -481,9 +543,20 @@ static void setConfigs() {
   }
 
   if (config->shared) {
-    config->importMemory = true;
+    if (config->memoryExport.has_value()) {
+      error("--export-memory is incompatible with --shared");
+    }
+    if (!config->memoryImport.has_value()) {
+      config->memoryImport =
+          std::pair<llvm::StringRef, llvm::StringRef>(defaultModule, memoryName);
+    }
     config->importUndefined = true;
-    config->unresolvedSymbols = UnresolvedPolicy::Ignore;
+  }
+
+  // If neither export-memory nor import-memory is specified, default to
+  // exporting memory under its default name.
+  if (!config->memoryExport.has_value() && !config->memoryImport.has_value()) {
+    config->memoryExport = memoryName;
   }
 }
 
@@ -523,6 +596,8 @@ static void checkOptions(opt::InputArgList &args) {
       error("-r and -pie may not be used together");
     if (config->sharedMemory)
       error("-r and --shared-memory may not be used together");
+    if (config->globalBase)
+      error("-r and --global-base may not by used together");
   }
 
   // To begin to prepare for Module Linking-style shared libraries, start
@@ -540,15 +615,30 @@ static void checkOptions(opt::InputArgList &args) {
     if (config->pie) {
       warn("creating PIEs, with -pie, is not yet stable");
     }
+
+    if (config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic) {
+      warn("dynamic imports are not yet stable "
+           "(--unresolved-symbols=import-dynamic)");
+    }
   }
 
   if (config->bsymbolic && !config->shared) {
     warn("-Bsymbolic is only meaningful when combined with -shared");
   }
+
+  if (config->globalBase && config->isPic) {
+    error("--global-base may not be used with -shared/-pie");
+  }
+}
+
+static const char *getReproduceOption(opt::InputArgList &args) {
+  if (auto *arg = args.getLastArg(OPT_reproduce))
+    return arg->getValue();
+  return getenv("LLD_REPRODUCE");
 }
 
 // Force Sym to be entered in the output. Used for -u or equivalent.
-static Symbol *handleUndefined(StringRef name) {
+static Symbol *handleUndefined(StringRef name, const char *option) {
   Symbol *sym = symtab->find(name);
   if (!sym)
     return nullptr;
@@ -557,8 +647,11 @@ static Symbol *handleUndefined(StringRef name) {
   // eliminate it. Mark the symbol as "used" to prevent it.
   sym->isUsedInRegularObj = true;
 
-  if (auto *lazySym = dyn_cast<LazySymbol>(sym))
+  if (auto *lazySym = dyn_cast<LazySymbol>(sym)) {
     lazySym->fetch();
+    if (!config->whyExtract.empty())
+      config->whyExtractRecords.emplace_back(option, sym->getFile(), *sym);
+  }
 
   return sym;
 }
@@ -570,15 +663,53 @@ static void handleLibcall(StringRef name) {
 
   if (auto *lazySym = dyn_cast<LazySymbol>(sym)) {
     MemoryBufferRef mb = lazySym->getMemberBuffer();
-    if (isBitcode(mb))
+    if (isBitcode(mb)) {
+      if (!config->whyExtract.empty())
+        config->whyExtractRecords.emplace_back("<libcall>", sym->getFile(),
+                                               *sym);
       lazySym->fetch();
+    }
+  }
+}
+
+static void writeWhyExtract() {
+  if (config->whyExtract.empty())
+    return;
+
+  std::error_code ec;
+  raw_fd_ostream os(config->whyExtract, ec, sys::fs::OF_None);
+  if (ec) {
+    error("cannot open --why-extract= file " + config->whyExtract + ": " +
+          ec.message());
+    return;
+  }
+
+  os << "reference\textracted\tsymbol\n";
+  for (auto &entry : config->whyExtractRecords) {
+    os << std::get<0>(entry) << '\t' << toString(std::get<1>(entry)) << '\t'
+       << toString(std::get<2>(entry)) << '\n';
+  }
+}
+
+// Equivalent of demote demoteSharedAndLazySymbols() in the ELF linker
+static void demoteLazySymbols() {
+  for (Symbol *sym : symtab->symbols()) {
+    if (auto* s = dyn_cast<LazySymbol>(sym)) {
+      if (s->signature) {
+        LLVM_DEBUG(llvm::dbgs()
+                   << "demoting lazy func: " << s->getName() << "\n");
+        replaceSymbol<UndefinedFunction>(s, s->getName(), std::nullopt,
+                                         std::nullopt, WASM_SYMBOL_BINDING_WEAK,
+                                         s->getFile(), s->signature);
+      }
+    }
   }
 }
 
 static UndefinedGlobal *
 createUndefinedGlobal(StringRef name, llvm::wasm::WasmGlobalType *type) {
   auto *sym = cast<UndefinedGlobal>(symtab->addUndefinedGlobal(
-      name, None, None, WASM_SYMBOL_UNDEFINED, nullptr, type));
+      name, std::nullopt, std::nullopt, WASM_SYMBOL_UNDEFINED, nullptr, type));
   config->allowUndefinedSymbols.insert(sym->getName());
   sym->isUsedInRegularObj = true;
   return sym;
@@ -586,7 +717,7 @@ createUndefinedGlobal(StringRef name, llvm::wasm::WasmGlobalType *type) {
 
 static InputGlobal *createGlobal(StringRef name, bool isMutable) {
   llvm::wasm::WasmGlobal wasmGlobal;
-  bool is64 = config->is64.getValueOr(false);
+  bool is64 = config->is64.value_or(false);
   wasmGlobal.Type = {uint8_t(is64 ? WASM_TYPE_I64 : WASM_TYPE_I32), isMutable};
   wasmGlobal.InitExpr = intConst(0, is64);
   wasmGlobal.SymbolName = name;
@@ -621,18 +752,18 @@ static void createSyntheticSymbols() {
       "__wasm_call_ctors", WASM_SYMBOL_VISIBILITY_HIDDEN,
       make<SyntheticFunction>(nullSignature, "__wasm_call_ctors"));
 
-    bool is64 = config->is64.getValueOr(false);
+  bool is64 = config->is64.value_or(false);
 
   if (config->isPic) {
     WasmSym::stackPointer =
-        createUndefinedGlobal("__stack_pointer", config->is64.getValueOr(false)
+        createUndefinedGlobal("__stack_pointer", config->is64.value_or(false)
                                                      ? &mutableGlobalTypeI64
                                                      : &mutableGlobalTypeI32);
     // For PIC code, we import two global variables (__memory_base and
     // __table_base) from the environment and use these as the offset at
     // which to load our static data and function table.
     // See:
-    // https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
+    // https://github.com/WebAssembly/tool-conventions/blob/main/DynamicLinking.md
     auto *globalType = is64 ? &globalTypeI64 : &globalTypeI32;
     WasmSym::memoryBase = createUndefinedGlobal("__memory_base", globalType);
     WasmSym::tableBase = createUndefinedGlobal("__table_base", globalType);
@@ -651,7 +782,7 @@ static void createSyntheticSymbols() {
     WasmSym::stackPointer->markLive();
   }
 
-  if (config->sharedMemory && !config->relocatable) {
+  if (config->sharedMemory) {
     WasmSym::tlsBase = createGlobalVariable("__tls_base", true);
     WasmSym::tlsSize = createGlobalVariable("__tls_size", false);
     WasmSym::tlsAlign = createGlobalVariable("__tls_align", false);
@@ -661,6 +792,17 @@ static void createSyntheticSymbols() {
             is64 ? i64ArgSignature : i32ArgSignature,
             "__wasm_init_tls"));
   }
+
+  if (config->isPic ||
+      config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic) {
+    // For PIC code, or when dynamically importing addresses, we create
+    // synthetic functions that apply relocations.  These get called from
+    // __wasm_call_ctors before the user-level constructors.
+    WasmSym::applyDataRelocs = symtab->addSyntheticFunction(
+        "__wasm_apply_data_relocs",
+        WASM_SYMBOL_VISIBILITY_DEFAULT | WASM_SYMBOL_EXPORTED,
+        make<SyntheticFunction>(nullSignature, "__wasm_apply_data_relocs"));
+  }
 }
 
 static void createOptionalSymbols() {
@@ -673,18 +815,21 @@ static void createOptionalSymbols() {
     WasmSym::dataEnd = symtab->addOptionalDataSymbol("__data_end");
 
   if (!config->isPic) {
+    WasmSym::stackLow = symtab->addOptionalDataSymbol("__stack_low");
+    WasmSym::stackHigh = symtab->addOptionalDataSymbol("__stack_high");
     WasmSym::globalBase = symtab->addOptionalDataSymbol("__global_base");
     WasmSym::heapBase = symtab->addOptionalDataSymbol("__heap_base");
+    WasmSym::heapEnd = symtab->addOptionalDataSymbol("__heap_end");
     WasmSym::definedMemoryBase = symtab->addOptionalDataSymbol("__memory_base");
     WasmSym::definedTableBase = symtab->addOptionalDataSymbol("__table_base");
-    if (config->is64.getValueOr(false))
+    if (config->is64.value_or(false))
       WasmSym::definedTableBase32 =
           symtab->addOptionalDataSymbol("__table_base32");
   }
 
   // For non-shared memory programs we still need to define __tls_base since we
   // allow object files built with TLS to be linked into single threaded
-  // programs, and such object files can contains refernced to this symbol.
+  // programs, and such object files can contain references to this symbol.
   //
   // However, in this case __tls_base is immutable and points directly to the
   // start of the `.tdata` static segment.
@@ -695,6 +840,53 @@ static void createOptionalSymbols() {
     WasmSym::tlsBase = createOptionalGlobal("__tls_base", false);
 }
 
+static void processStubLibraries() {
+  log("-- processStubLibraries");
+  for (auto &stub_file : symtab->stubFiles) {
+    LLVM_DEBUG(llvm::dbgs()
+               << "processing stub file: " << stub_file->getName() << "\n");
+    for (auto [name, deps]: stub_file->symbolDependencies) {
+      auto* sym = symtab->find(name);
+      if (!sym || !sym->isUndefined() || !sym->isUsedInRegularObj ||
+          sym->forceImport) {
+        LLVM_DEBUG(llvm::dbgs() << "stub not in needed: " << name << "\n");
+        continue;
+      }
+      // The first stub library to define a given symbol sets this and
+      // definitions in later stub libraries are ignored.
+      sym->forceImport = true;
+      if (sym->traced)
+        message(toString(stub_file) + ": importing " + name);
+      else
+        LLVM_DEBUG(llvm::dbgs()
+                   << toString(stub_file) << ": importing " << name << "\n");
+      for (const auto dep : deps) {
+        auto* needed = symtab->find(dep);
+        if (!needed) {
+          error(toString(stub_file) + ": undefined symbol: " + dep +
+                ". Required by " + toString(*sym));
+        } else if (needed->isUndefined()) {
+          error(toString(stub_file) +
+                ": undefined symbol: " + toString(*needed) +
+                ". Required by " + toString(*sym));
+        } else {
+          LLVM_DEBUG(llvm::dbgs()
+                     << "force export: " << toString(*needed) << "\n");
+          needed->forceExport = true;
+          needed->isUsedInRegularObj = true;
+          if (auto *lazy = dyn_cast<LazySymbol>(needed)) {
+            lazy->fetch();
+            if (!config->whyExtract.empty())
+              config->whyExtractRecords.emplace_back(stub_file->getName(),
+                                                     sym->getFile(), *sym);
+          }
+        }
+      }
+    }
+  }
+  log("-- done processStubLibraries");
+}
+
 // Reconstructs command line arguments so that so that you can re-run
 // the same command with the same inputs. This is for --reproduce.
 static std::string createResponseFile(const opt::InputArgList &args) {
@@ -738,8 +930,9 @@ struct WrappedSymbol {
 };
 
 static Symbol *addUndefined(StringRef name) {
-  return symtab->addUndefinedFunction(name, None, None, WASM_SYMBOL_UNDEFINED,
-                                      nullptr, nullptr, false);
+  return symtab->addUndefinedFunction(name, std::nullopt, std::nullopt,
+                                      WASM_SYMBOL_UNDEFINED, nullptr, nullptr,
+                                      false);
 }
 
 // Handles -wrap option.
@@ -760,8 +953,8 @@ static std::vector<WrappedSymbol> addWrappedSymbols(opt::InputArgList &args) {
     if (!sym)
       continue;
 
-    Symbol *real = addUndefined(saver.save("__real_" + name));
-    Symbol *wrap = addUndefined(saver.save("__wrap_" + name));
+    Symbol *real = addUndefined(saver().save("__real_" + name));
+    Symbol *wrap = addUndefined(saver().save("__wrap_" + name));
     v.push_back({sym, real, wrap});
 
     // We want to tell LTO not to inline symbols to be overwritten
@@ -818,10 +1011,28 @@ static void splitSections() {
   });
 }
 
+static bool isKnownZFlag(StringRef s) {
+  // For now, we only support a very limited set of -z flags
+  return s.startswith("stack-size=");
+}
+
+// Report a warning for an unknown -z option.
+static void checkZOptions(opt::InputArgList &args) {
+  for (auto *arg : args.filtered(OPT_z))
+    if (!isKnownZFlag(arg->getValue()))
+      warn("unknown -z value: " + StringRef(arg->getValue()));
+}
+
 void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   WasmOptTable parser;
   opt::InputArgList args = parser.parse(argsArr.slice(1));
 
+  // Interpret these flags early because error()/warn() depend on them.
+  errorHandler().errorLimit = args::getInteger(args, OPT_error_limit, 20);
+  errorHandler().fatalWarnings =
+      args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false);
+  checkZOptions(args);
+
   // Handle --help
   if (args.hasArg(OPT_help)) {
     parser.printHelp(lld::outs(),
@@ -837,8 +1048,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   }
 
   // Handle --reproduce
-  if (auto *arg = args.getLastArg(OPT_reproduce)) {
-    StringRef path = arg->getValue();
+  if (const char *path = getReproduceOption(args)) {
     Expected<std::unique_ptr<TarWriter>> errOrWriter =
         TarWriter::create(path, path::stem(path));
     if (errOrWriter) {
@@ -858,15 +1068,13 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   cl::ResetAllOptionOccurrences();
   cl::ParseCommandLineOptions(v.size(), v.data());
 
-  errorHandler().errorLimit = args::getInteger(args, OPT_error_limit, 20);
-
   readConfigs(args);
+  setConfigs();
 
   createFiles(args);
   if (errorCount())
     return;
 
-  setConfigs();
   checkOptions(args);
   if (errorCount())
     return;
@@ -907,16 +1115,16 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
 
   // Handle the `--undefined <sym>` options.
   for (auto *arg : args.filtered(OPT_undefined))
-    handleUndefined(arg->getValue());
+    handleUndefined(arg->getValue(), "<internal>");
 
   // Handle the `--export <sym>` options
   // This works like --undefined but also exports the symbol if its found
   for (auto &iter : config->exportedSymbols)
-    handleUndefined(iter.first());
+    handleUndefined(iter.first(), "--export");
 
   Symbol *entrySym = nullptr;
   if (!config->relocatable && !config->entry.empty()) {
-    entrySym = handleUndefined(config->entry);
+    entrySym = handleUndefined(config->entry, "--entry");
     if (entrySym && entrySym->isDefined())
       entrySym->forceExport = true;
     else
@@ -933,7 +1141,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
       !WasmSym::callCtors->isUsedInRegularObj &&
       WasmSym::callCtors->getName() != config->entry &&
       !config->exportedSymbols.count(WasmSym::callCtors->getName())) {
-    if (Symbol *callDtors = handleUndefined("__wasm_call_dtors")) {
+    if (Symbol *callDtors =
+            handleUndefined("__wasm_call_dtors", "<internal>")) {
       if (auto *callDtorsFunc = dyn_cast<DefinedFunction>(callDtors)) {
         if (callDtorsFunc->signature &&
             (!callDtorsFunc->signature->Params.empty() ||
@@ -947,8 +1156,6 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
     }
   }
 
-  createOptionalSymbols();
-
   if (errorCount())
     return;
 
@@ -970,12 +1177,18 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   if (errorCount())
     return;
 
+  writeWhyExtract();
+
   // Do link-time optimization if given files are LLVM bitcode files.
   // This compiles bitcode files into real object files.
-  symtab->addCombinedLTOObject();
+  symtab->compileBitcodeFiles();
   if (errorCount())
     return;
 
+  processStubLibraries();
+
+  createOptionalSymbols();
+
   // Resolve any variant symbols that were created due to signature
   // mismatchs.
   symtab->handleSymbolVariants();
@@ -1008,6 +1221,9 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
   // collection.
   splitSections();
 
+  // Any remaining lazy symbols should be demoted to Undefined
+  demoteLazySymbols();
+
   // Do size optimizations: garbage collection
   markLive();
 
index 1207f52..53b5288 100644 (file)
@@ -51,7 +51,7 @@ bool relocIs64(uint8_t relocType) {
 }
 
 std::string toString(const wasm::InputChunk *c) {
-  return (toString(c->file) + ":(" + c->getName() + ")").str();
+  return (toString(c->file) + ":(" + c->name + ")").str();
 }
 
 namespace wasm {
@@ -116,7 +116,10 @@ void InputChunk::relocate(uint8_t *buf) const {
       LLVM_DEBUG(dbgs() << " sym=" << file->getSymbols()[rel.Index]->getName());
     LLVM_DEBUG(dbgs() << " addend=" << rel.Addend << " index=" << rel.Index
                       << " offset=" << rel.Offset << "\n");
-    auto value = file->calcNewValue(rel, tombstone, this);
+    // TODO(sbc): Check that the value is within the range of the
+    // relocation type below.  Most likely we must error out here
+    // if its not with range.
+    uint64_t value = file->calcNewValue(rel, tombstone, this);
 
     switch (rel.Type) {
     case R_WASM_TYPE_INDEX_LEB:
@@ -125,7 +128,7 @@ void InputChunk::relocate(uint8_t *buf) const {
     case R_WASM_TAG_INDEX_LEB:
     case R_WASM_MEMORY_ADDR_LEB:
     case R_WASM_TABLE_NUMBER_LEB:
-      encodeULEB128(value, loc, 5);
+      encodeULEB128(static_cast<uint32_t>(value), loc, 5);
       break;
     case R_WASM_MEMORY_ADDR_LEB64:
       encodeULEB128(value, loc, 10);
@@ -193,14 +196,14 @@ uint64_t InputChunk::getTombstone() const {
 }
 
 void InputFunction::setFunctionIndex(uint32_t index) {
-  LLVM_DEBUG(dbgs() << "InputFunction::setFunctionIndex: " << getName()
-                    << " -> " << index << "\n");
+  LLVM_DEBUG(dbgs() << "InputFunction::setFunctionIndex: " << name << " -> "
+                    << index << "\n");
   assert(!hasFunctionIndex());
   functionIndex = index;
 }
 
 void InputFunction::setTableIndex(uint32_t index) {
-  LLVM_DEBUG(dbgs() << "InputFunction::setTableIndex: " << getName() << " -> "
+  LLVM_DEBUG(dbgs() << "InputFunction::setTableIndex: " << name << " -> "
                     << index << "\n");
   assert(!hasTableIndex());
   tableIndex = index;
@@ -268,7 +271,7 @@ void InputFunction::calculateSize() {
   if (!file || !config->compressRelocations)
     return;
 
-  LLVM_DEBUG(dbgs() << "calculateSize: " << getName() << "\n");
+  LLVM_DEBUG(dbgs() << "calculateSize: " << name << "\n");
 
   const uint8_t *secStart = file->codeSection->Content.data();
   const uint8_t *funcStart = secStart + getInputSectionOffset();
@@ -315,7 +318,7 @@ void InputFunction::writeCompressed(uint8_t *buf) const {
   decodeULEB128(funcStart, &count);
   funcStart += count;
 
-  LLVM_DEBUG(dbgs() << "write func: " << getName() << "\n");
+  LLVM_DEBUG(dbgs() << "write func: " << name << "\n");
   buf += encodeULEB128(compressedFuncSize, buf);
   const uint8_t *lastRelocEnd = funcStart;
   for (const WasmRelocation &rel : relocations) {
@@ -336,7 +339,7 @@ void InputFunction::writeCompressed(uint8_t *buf) const {
 
 uint64_t InputChunk::getChunkOffset(uint64_t offset) const {
   if (const auto *ms = dyn_cast<MergeInputChunk>(this)) {
-    LLVM_DEBUG(dbgs() << "getChunkOffset(merged): " << getName() << "\n");
+    LLVM_DEBUG(dbgs() << "getChunkOffset(merged): " << name << "\n");
     LLVM_DEBUG(dbgs() << "offset: " << offset << "\n");
     LLVM_DEBUG(dbgs() << "parentOffset: " << ms->getParentOffset(offset)
                       << "\n");
@@ -355,13 +358,13 @@ uint64_t InputChunk::getVA(uint64_t offset) const {
 }
 
 // Generate code to apply relocations to the data section at runtime.
-// This is only called when generating shared libaries (PIC) where address are
+// This is only called when generating shared libraries (PIC) where address are
 // not known at static link time.
 void InputChunk::generateRelocationCode(raw_ostream &os) const {
-  LLVM_DEBUG(dbgs() << "generating runtime relocations: " << getName()
+  LLVM_DEBUG(dbgs() << "generating runtime relocations: " << name
                     << " count=" << relocations.size() << "\n");
 
-  bool is64 = config->is64.getValueOr(false);
+  bool is64 = config->is64.value_or(false);
   unsigned opcode_ptr_const = is64 ? WASM_OPCODE_I64_CONST
                                    : WASM_OPCODE_I32_CONST;
   unsigned opcode_ptr_add = is64 ? WASM_OPCODE_I64_ADD
@@ -373,19 +376,26 @@ void InputChunk::generateRelocationCode(raw_ostream &os) const {
   for (const WasmRelocation &rel : relocations) {
     uint64_t offset = getVA(rel.Offset) - getInputSectionOffset();
 
+    Symbol *sym = file->getSymbol(rel);
+    if (!config->isPic && sym->isDefined())
+      continue;
+
     LLVM_DEBUG(dbgs() << "gen reloc: type=" << relocTypeToString(rel.Type)
                       << " addend=" << rel.Addend << " index=" << rel.Index
                       << " output offset=" << offset << "\n");
 
-    // Get __memory_base
-    writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
-    writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "memory_base");
-
-    // Add the offset of the relocation
+    // Calculate the address at which to apply the relocations
     writeU8(os, opcode_ptr_const, "CONST");
     writeSleb128(os, offset, "offset");
-    writeU8(os, opcode_ptr_add, "ADD");
 
+    // In PIC mode we need to add the __memory_base
+    if (config->isPic) {
+      writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
+      writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "memory_base");
+      writeU8(os, opcode_ptr_add, "ADD");
+    }
+
+    // Now figure out what we want to store at this location
     bool is64 = relocIs64(rel.Type);
     unsigned opcode_reloc_const =
         is64 ? WASM_OPCODE_I64_CONST : WASM_OPCODE_I32_CONST;
@@ -394,8 +404,6 @@ void InputChunk::generateRelocationCode(raw_ostream &os) const {
     unsigned opcode_reloc_store =
         is64 ? WASM_OPCODE_I64_STORE : WASM_OPCODE_I32_STORE;
 
-    Symbol *sym = file->getSymbol(rel);
-    // Now figure out what we want to store
     if (sym->hasGOTIndex()) {
       writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
       writeUleb128(os, sym->getGOTIndex(), "global index");
@@ -405,6 +413,7 @@ void InputChunk::generateRelocationCode(raw_ostream &os) const {
         writeU8(os, opcode_reloc_add, "ADD");
       }
     } else {
+      assert(config->isPic);
       const GlobalSymbol* baseSymbol = WasmSym::memoryBase;
       if (rel.Type == R_WASM_TABLE_INDEX_I32 ||
           rel.Type == R_WASM_TABLE_INDEX_I64)
index f1174d9..1e43083 100644 (file)
@@ -27,6 +27,7 @@
 #include "llvm/ADT/CachedHashString.h"
 #include "llvm/MC/StringTableBuilder.h"
 #include "llvm/Object/Wasm.h"
+#include <optional>
 
 namespace lld {
 namespace wasm {
@@ -49,8 +50,6 @@ public:
   StringRef name;
   StringRef debugName;
 
-  StringRef getName() const { return name; }
-  StringRef getDebugName() const { return debugName; }
   Kind kind() const { return (Kind)sectionKind; }
 
   uint32_t getSize() const;
@@ -81,12 +80,7 @@ public:
   void writeRelocations(llvm::raw_ostream &os) const;
   void generateRelocationCode(raw_ostream &os) const;
 
-  bool isTLS() const {
-    // Older object files don't include WASM_SEG_FLAG_TLS and instead
-    // relied on the naming convention.
-    return flags & llvm::wasm::WASM_SEG_FLAG_TLS || name.startswith(".tdata") ||
-           name.startswith(".tbss");
-  }
+  bool isTLS() const { return flags & llvm::wasm::WASM_SEG_FLAG_TLS; }
 
   ObjFile *file;
   OutputSection *outputSec = nullptr;
@@ -230,7 +224,8 @@ class SyntheticMergedChunk : public InputChunk {
 public:
   SyntheticMergedChunk(StringRef name, uint32_t alignment, uint32_t flags)
       : InputChunk(nullptr, InputChunk::MergedChunk, name, alignment, flags),
-        builder(llvm::StringTableBuilder::RAW, 1ULL << alignment) {}
+        builder(llvm::StringTableBuilder::RAW, llvm::Align(1ULL << alignment)) {
+  }
 
   static bool classof(const InputChunk *c) {
     return c->kind() == InputChunk::MergedChunk;
@@ -256,9 +251,9 @@ class InputFunction : public InputChunk {
 public:
   InputFunction(const WasmSignature &s, const WasmFunction *func, ObjFile *f)
       : InputChunk(f, InputChunk::Function, func->SymbolName), signature(s),
-        function(func), exportName(func && func->ExportName.hasValue()
-                                       ? (*func->ExportName).str()
-                                       : llvm::Optional<std::string>()) {
+        function(func),
+        exportName(func && func->ExportName ? (*func->ExportName).str()
+                                            : std::optional<std::string>()) {
     inputSectionOffset = function->CodeSectionOffset;
     rawData =
         file->codeSection->Content.slice(inputSectionOffset, function->Size);
@@ -274,18 +269,18 @@ public:
            c->kind() == InputChunk::SyntheticFunction;
   }
 
-  llvm::Optional<StringRef> getExportName() const {
-    return exportName.hasValue() ? llvm::Optional<StringRef>(*exportName)
-                                 : llvm::Optional<StringRef>();
+  std::optional<StringRef> getExportName() const {
+    return exportName ? std::optional<StringRef>(*exportName)
+                      : std::optional<StringRef>();
   }
   void setExportName(std::string exportName) { this->exportName = exportName; }
   uint32_t getFunctionInputOffset() const { return getInputSectionOffset(); }
   uint32_t getFunctionCodeOffset() const { return function->CodeOffset; }
-  uint32_t getFunctionIndex() const { return functionIndex.getValue(); }
-  bool hasFunctionIndex() const { return functionIndex.hasValue(); }
+  uint32_t getFunctionIndex() const { return *functionIndex; }
+  bool hasFunctionIndex() const { return functionIndex.has_value(); }
   void setFunctionIndex(uint32_t index);
-  uint32_t getTableIndex() const { return tableIndex.getValue(); }
-  bool hasTableIndex() const { return tableIndex.hasValue(); }
+  uint32_t getTableIndex() const { return *tableIndex; }
+  bool hasTableIndex() const { return tableIndex.has_value(); }
   void setTableIndex(uint32_t index);
   void writeCompressed(uint8_t *buf) const;
 
@@ -305,9 +300,9 @@ public:
   const WasmFunction *function;
 
 protected:
-  llvm::Optional<std::string> exportName;
-  llvm::Optional<uint32_t> functionIndex;
-  llvm::Optional<uint32_t> tableIndex;
+  std::optional<std::string> exportName;
+  std::optional<uint32_t> functionIndex;
+  std::optional<uint32_t> tableIndex;
   uint32_t compressedFuncSize = 0;
   uint32_t compressedSize = 0;
 };
index d00e60b..46e21d7 100644 (file)
@@ -14,6 +14,7 @@
 #include "WriterUtils.h"
 #include "lld/Common/LLVM.h"
 #include "llvm/Object/Wasm.h"
+#include <optional>
 
 namespace lld {
 namespace wasm {
@@ -27,8 +28,8 @@ protected:
 
 public:
   StringRef getName() const { return name; }
-  uint32_t getAssignedIndex() const { return assignedIndex.getValue(); }
-  bool hasAssignedIndex() const { return assignedIndex.hasValue(); }
+  uint32_t getAssignedIndex() const { return *assignedIndex; }
+  bool hasAssignedIndex() const { return assignedIndex.has_value(); }
   void assignIndex(uint32_t index) {
     assert(!hasAssignedIndex());
     assignedIndex = index;
@@ -39,17 +40,18 @@ public:
 
 protected:
   StringRef name;
-  llvm::Optional<uint32_t> assignedIndex;
+  std::optional<uint32_t> assignedIndex;
 };
 
 inline WasmInitExpr intConst(uint64_t value, bool is64) {
   WasmInitExpr ie;
+  ie.Extended = false;
   if (is64) {
-    ie.Opcode = llvm::wasm::WASM_OPCODE_I64_CONST;
-    ie.Value.Int64 = static_cast<int64_t>(value);
+    ie.Inst.Opcode = llvm::wasm::WASM_OPCODE_I64_CONST;
+    ie.Inst.Value.Int64 = static_cast<int64_t>(value);
   } else {
-    ie.Opcode = llvm::wasm::WASM_OPCODE_I32_CONST;
-    ie.Value.Int32 = static_cast<int32_t>(value);
+    ie.Inst.Opcode = llvm::wasm::WASM_OPCODE_I32_CONST;
+    ie.Inst.Value.Int32 = static_cast<int32_t>(value);
   }
   return ie;
 }
@@ -63,7 +65,7 @@ public:
   const WasmInitExpr &getInitExpr() const { return initExpr; }
 
   void setPointerValue(uint64_t value) {
-    initExpr = intConst(value, config->is64.getValueOr(false));
+    initExpr = intConst(value, config->is64.value_or(false));
   }
 
 private:
@@ -74,14 +76,9 @@ private:
 class InputTag : public InputElement {
 public:
   InputTag(const WasmSignature &s, const WasmTag &t, ObjFile *f)
-      : InputElement(t.SymbolName, f), signature(s), type(t.Type) {}
-
-  const WasmTagType &getType() const { return type; }
+      : InputElement(t.SymbolName, f), signature(s) {}
 
   const WasmSignature &signature;
-
-private:
-  WasmTagType type;
 };
 
 class InputTable : public InputElement {
index 35d8dd1..a03166d 100644 (file)
 #include "InputElement.h"
 #include "OutputSegment.h"
 #include "SymbolTable.h"
-#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Memory.h"
+#include "lld/Common/Args.h"
+#include "lld/Common/CommonLinkerContext.h"
 #include "lld/Common/Reproduce.h"
 #include "llvm/Object/Binary.h"
 #include "llvm/Object/Wasm.h"
+#include "llvm/Support/Path.h"
 #include "llvm/Support/TarWriter.h"
 #include "llvm/Support/raw_ostream.h"
+#include <optional>
 
 #define DEBUG_TYPE "lld"
 
 using namespace llvm;
 using namespace llvm::object;
 using namespace llvm::wasm;
+using namespace llvm::sys;
 
 namespace lld {
 
@@ -43,10 +46,10 @@ namespace wasm {
 
 void InputFile::checkArch(Triple::ArchType arch) const {
   bool is64 = arch == Triple::wasm64;
-  if (is64 && !config->is64.hasValue()) {
+  if (is64 && !config->is64) {
     fatal(toString(this) +
           ": must specify -mwasm64 to process wasm64 object files");
-  } else if (config->is64.getValueOr(false) != is64) {
+  } else if (config->is64.value_or(false) != is64) {
     fatal(toString(this) +
           ": wasm32 object file can't be linked in wasm64 mode");
   }
@@ -54,13 +57,13 @@ void InputFile::checkArch(Triple::ArchType arch) const {
 
 std::unique_ptr<llvm::TarWriter> tar;
 
-Optional<MemoryBufferRef> readFile(StringRef path) {
+std::optional<MemoryBufferRef> readFile(StringRef path) {
   log("Loading: " + path);
 
   auto mbOrErr = MemoryBuffer::getFile(path);
   if (auto ec = mbOrErr.getError()) {
     error("cannot open " + path + ": " + ec.message());
-    return None;
+    return std::nullopt;
   }
   std::unique_ptr<MemoryBuffer> &mb = *mbOrErr;
   MemoryBufferRef mbref = mb->getMemBufferRef();
@@ -71,7 +74,8 @@ Optional<MemoryBufferRef> readFile(StringRef path) {
   return mbref;
 }
 
-InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName) {
+InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName,
+                            uint64_t offsetInArchive) {
   file_magic magic = identify_magic(mb.getBuffer());
   if (magic == file_magic::wasm_object) {
     std::unique_ptr<Binary> bin =
@@ -83,18 +87,14 @@ InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName) {
   }
 
   if (magic == file_magic::bitcode)
-    return make<BitcodeFile>(mb, archiveName);
+    return make<BitcodeFile>(mb, archiveName, offsetInArchive);
 
-  fatal("unknown file type: " + mb.getBufferIdentifier());
-}
+  std::string name = mb.getBufferIdentifier().str();
+  if (!archiveName.empty()) {
+    name = archiveName.str() + "(" + name + ")";
+  }
 
-void ObjFile::dumpInfo() const {
-  log("info for: " + toString(this) +
-      "\n              Symbols : " + Twine(symbols.size()) +
-      "\n     Function Imports : " + Twine(wasmObj->getNumImportedFunctions()) +
-      "\n       Global Imports : " + Twine(wasmObj->getNumImportedGlobals()) +
-      "\n          Tag Imports : " + Twine(wasmObj->getNumImportedTags()) +
-      "\n        Table Imports : " + Twine(wasmObj->getNumImportedTables()));
+  fatal("unknown file type: " + name);
 }
 
 // Relocations contain either symbol or type indices.  This function takes a
@@ -113,7 +113,7 @@ uint32_t ObjFile::calcNewIndex(const WasmRelocation &reloc) const {
 
 // Relocations can contain addend for combined sections. This function takes a
 // relocation and returns updated addend by offset in the output section.
-uint64_t ObjFile::calcNewAddend(const WasmRelocation &reloc) const {
+int64_t ObjFile::calcNewAddend(const WasmRelocation &reloc) const {
   switch (reloc.Type) {
   case R_WASM_MEMORY_ADDR_LEB:
   case R_WASM_MEMORY_ADDR_LEB64:
@@ -175,18 +175,12 @@ uint64_t ObjFile::calcNewValue(const WasmRelocation &reloc, uint64_t tombstone,
   case R_WASM_MEMORY_ADDR_REL_SLEB64:
   case R_WASM_MEMORY_ADDR_I32:
   case R_WASM_MEMORY_ADDR_I64:
+  case R_WASM_MEMORY_ADDR_TLS_SLEB:
+  case R_WASM_MEMORY_ADDR_TLS_SLEB64:
   case R_WASM_MEMORY_ADDR_LOCREL_I32: {
     if (isa<UndefinedData>(sym) || sym->isUndefWeak())
       return 0;
     auto D = cast<DefinedData>(sym);
-    // Treat non-TLS relocation against symbols that live in the TLS segment
-    // like TLS relocations.  This beaviour exists to support older object
-    // files created before we introduced TLS relocations.
-    // TODO(sbc): Remove this legacy behaviour one day.  This will break
-    // backward compat with old object files built with `-fPIC`.
-    if (D->segment && D->segment->outputSeg->isTLS())
-      return D->getOutputSegmentOffset() + reloc.Addend;
-
     uint64_t value = D->getVA() + reloc.Addend;
     if (reloc.Type == R_WASM_MEMORY_ADDR_LOCREL_I32) {
       const auto *segment = cast<InputSegment>(chunk);
@@ -196,12 +190,6 @@ uint64_t ObjFile::calcNewValue(const WasmRelocation &reloc, uint64_t tombstone,
     }
     return value;
   }
-  case R_WASM_MEMORY_ADDR_TLS_SLEB:
-  case R_WASM_MEMORY_ADDR_TLS_SLEB64:
-    if (isa<UndefinedData>(sym) || sym->isUndefWeak())
-      return 0;
-    // TLS relocations are relative to the start of the TLS output segment
-    return cast<DefinedData>(sym)->getOutputSegmentOffset() + reloc.Addend;
   case R_WASM_TYPE_INDEX_LEB:
     return typeMap[reloc.Index];
   case R_WASM_FUNCTION_INDEX_LEB:
@@ -215,6 +203,9 @@ uint64_t ObjFile::calcNewValue(const WasmRelocation &reloc, uint64_t tombstone,
     return getTagSymbol(reloc.Index)->getTagIndex();
   case R_WASM_FUNCTION_OFFSET_I32:
   case R_WASM_FUNCTION_OFFSET_I64: {
+    if (isa<UndefinedFunction>(sym)) {
+      return tombstone ? tombstone : reloc.Addend;
+    }
     auto *f = cast<DefinedFunction>(sym);
     return f->function->getOffset(f->function->getFunctionCodeOffset() +
                                   reloc.Addend);
@@ -345,10 +336,9 @@ void ObjFile::addLegacyIndirectFunctionTableIfNeeded(
   LLVM_DEBUG(dbgs() << "Synthesizing symbol for table import: " << info->Name
                     << "\n");
   const WasmGlobalType *globalType = nullptr;
-  const WasmTagType *tagType = nullptr;
   const WasmSignature *signature = nullptr;
-  auto *wasmSym = make<WasmSymbol>(*info, globalType, &tableImport->Table,
-                                   tagType, signature);
+  auto *wasmSym =
+      make<WasmSymbol>(*info, globalType, &tableImport->Table, signature);
   Symbol *sym = createUndefined(*wasmSym, false);
   // We're only sure it's a TableSymbol if the createUndefined succeeded.
   if (errorCount())
@@ -424,10 +414,12 @@ void ObjFile::parse(bool ignoreComdats) {
   tableEntries.resize(totalFunctions);
   for (const WasmElemSegment &seg : wasmObj->elements()) {
     int64_t offset;
-    if (seg.Offset.Opcode == WASM_OPCODE_I32_CONST)
-      offset = seg.Offset.Value.Int32;
-    else if (seg.Offset.Opcode == WASM_OPCODE_I64_CONST)
-      offset = seg.Offset.Value.Int64;
+    if (seg.Offset.Extended)
+      fatal(toString(this) + ": extended init exprs not supported");
+    else if (seg.Offset.Inst.Opcode == WASM_OPCODE_I32_CONST)
+      offset = seg.Offset.Inst.Value.Int32;
+    else if (seg.Offset.Inst.Opcode == WASM_OPCODE_I64_CONST)
+      offset = seg.Offset.Inst.Value.Int64;
     else
       fatal(toString(this) + ": invalid table elements");
     for (size_t index = 0; index < seg.Functions.size(); index++) {
@@ -486,24 +478,28 @@ void ObjFile::parse(bool ignoreComdats) {
   // Populate `Segments`.
   for (const WasmSegment &s : wasmObj->dataSegments()) {
     InputChunk *seg;
-    if (shouldMerge(s)) {
+    if (shouldMerge(s))
       seg = make<MergeInputChunk>(s, this);
-    else
+    else
       seg = make<InputSegment>(s, this);
     seg->discarded = isExcludedByComdat(seg);
-
+    // Older object files did not include WASM_SEG_FLAG_TLS and instead
+    // relied on the naming convention.  To maintain compat with such objects
+    // we still imply the TLS flag based on the name of the segment.
+    if (!seg->isTLS() &&
+        (seg->name.startswith(".tdata") || seg->name.startswith(".tbss")))
+      seg->flags |= WASM_SEG_FLAG_TLS;
     segments.emplace_back(seg);
   }
   setRelocs(segments, dataSection);
 
   // Populate `Functions`.
   ArrayRef<WasmFunction> funcs = wasmObj->functions();
-  ArrayRef<uint32_t> funcTypes = wasmObj->functionTypes();
   ArrayRef<WasmSignature> types = wasmObj->types();
   functions.reserve(funcs.size());
 
-  for (size_t i = 0, e = funcs.size(); i != e; ++i) {
-    auto* func = make<InputFunction>(types[funcTypes[i]], &funcs[i], this);
+  for (auto &f : funcs) {
+    auto *func = make<InputFunction>(types[f.SigIndex], &f, this);
     func->discarded = isExcludedByComdat(func);
     functions.emplace_back(func);
   }
@@ -519,7 +515,7 @@ void ObjFile::parse(bool ignoreComdats) {
 
   // Populate `Tags`.
   for (const WasmTag &t : wasmObj->tags())
-    tags.emplace_back(make<InputTag>(types[t.Type.SigIndex], t, this));
+    tags.emplace_back(make<InputTag>(types[t.SigIndex], t, this));
 
   // Populate `Symbols` based on the symbols in the object.
   symbols.reserve(wasmObj->getNumberOfSymbols());
@@ -543,7 +539,7 @@ void ObjFile::parse(bool ignoreComdats) {
   addLegacyIndirectFunctionTableIfNeeded(tableSymbolCount);
 }
 
-bool ObjFile::isExcludedByComdat(InputChunk *chunk) const {
+bool ObjFile::isExcludedByComdat(const InputChunk *chunk) const {
   uint32_t c = chunk->getComdat();
   if (c == UINT32_MAX)
     return false;
@@ -592,6 +588,11 @@ Symbol *ObjFile::createDefined(const WasmSymbol &sym) {
     InputChunk *seg = segments[sym.Info.DataRef.Segment];
     auto offset = sym.Info.DataRef.Offset;
     auto size = sym.Info.DataRef.Size;
+    // Support older (e.g. llvm 13) object files that pre-date the per-symbol
+    // TLS flag, and symbols were assumed to be TLS by being defined in a TLS
+    // segment.
+    if (!(flags & WASM_SYMBOL_TLS) && seg->isTLS())
+      flags |= WASM_SYMBOL_TLS;
     if (sym.isBindingLocal())
       return make<DefinedData>(name, flags, this, seg, offset, size);
     if (seg->discarded)
@@ -664,12 +665,66 @@ Symbol *ObjFile::createUndefined(const WasmSymbol &sym, bool isCalledDirectly) {
     return symtab->addUndefinedTable(name, sym.Info.ImportName,
                                      sym.Info.ImportModule, flags, this,
                                      sym.TableType);
+  case WASM_SYMBOL_TYPE_TAG:
+    if (sym.isBindingLocal())
+      return make<UndefinedTag>(name, sym.Info.ImportName,
+                                sym.Info.ImportModule, flags, this,
+                                sym.Signature);
+    return symtab->addUndefinedTag(name, sym.Info.ImportName,
+                                   sym.Info.ImportModule, flags, this,
+                                   sym.Signature);
   case WASM_SYMBOL_TYPE_SECTION:
     llvm_unreachable("section symbols cannot be undefined");
   }
   llvm_unreachable("unknown symbol kind");
 }
 
+
+StringRef strip(StringRef s) {
+  while (s.starts_with(" ")) {
+    s = s.drop_front();
+  }
+  while (s.ends_with(" ")) {
+    s = s.drop_back();
+  }
+  return s;
+}
+
+void StubFile::parse() {
+  bool first = true;
+
+  SmallVector<StringRef> lines;
+  mb.getBuffer().split(lines, '\n');
+  for (StringRef line : lines) {
+    line = line.trim();
+
+    // File must begin with #STUB
+    if (first) {
+      assert(line == "#STUB");
+      first = false;
+    }
+
+    // Lines starting with # are considered comments
+    if (line.startswith("#"))
+      continue;
+
+    StringRef sym;
+    StringRef rest;
+    std::tie(sym, rest) = line.split(':');
+    sym = strip(sym);
+    rest = strip(rest);
+
+    symbolDependencies[sym] = {};
+
+    while (rest.size()) {
+      StringRef dep;
+      std::tie(dep, rest) = rest.split(',');
+      dep = strip(dep);
+      symbolDependencies[sym].push_back(dep);
+    }
+  }
+}
+
 void ArchiveFile::parse() {
   // Parse a MemoryBufferRef as an archive file.
   LLVM_DEBUG(dbgs() << "Parsing library: " << toString(this) << "\n");
@@ -682,6 +737,7 @@ void ArchiveFile::parse() {
     ++count;
   }
   LLVM_DEBUG(dbgs() << "Read " << count << " symbols\n");
+  (void) count;
 }
 
 void ArchiveFile::addMember(const Archive::Symbol *sym) {
@@ -702,7 +758,7 @@ void ArchiveFile::addMember(const Archive::Symbol *sym) {
             "could not get the buffer for the member defining symbol " +
                 sym->getName());
 
-  InputFile *obj = createObjectFile(mb, getName());
+  InputFile *obj = createObjectFile(mb, getName(), c.getChildOffset());
   symtab->addFile(obj);
 }
 
@@ -720,7 +776,7 @@ static uint8_t mapVisibility(GlobalValue::VisibilityTypes gvVisibility) {
 static Symbol *createBitcodeSymbol(const std::vector<bool> &keptComdats,
                                    const lto::InputFile::Symbol &objSym,
                                    BitcodeFile &f) {
-  StringRef name = saver.save(objSym.getName());
+  StringRef name = saver().save(objSym.getName());
 
   uint32_t flags = objSym.isWeak() ? WASM_SYMBOL_BINDING_WEAK : 0;
   flags |= mapVisibility(objSym.getVisibility());
@@ -731,8 +787,8 @@ static Symbol *createBitcodeSymbol(const std::vector<bool> &keptComdats,
   if (objSym.isUndefined() || excludedByComdat) {
     flags |= WASM_SYMBOL_UNDEFINED;
     if (objSym.isExecutable())
-      return symtab->addUndefinedFunction(name, None, None, flags, &f, nullptr,
-                                          true);
+      return symtab->addUndefinedFunction(name, std::nullopt, std::nullopt,
+                                          flags, &f, nullptr, true);
     return symtab->addUndefinedData(name, flags, &f);
   }
 
@@ -741,6 +797,32 @@ static Symbol *createBitcodeSymbol(const std::vector<bool> &keptComdats,
   return symtab->addDefinedData(name, flags, &f, nullptr, 0, 0);
 }
 
+BitcodeFile::BitcodeFile(MemoryBufferRef m, StringRef archiveName,
+                         uint64_t offsetInArchive)
+    : InputFile(BitcodeKind, m) {
+  this->archiveName = std::string(archiveName);
+
+  std::string path = mb.getBufferIdentifier().str();
+
+  // ThinLTO assumes that all MemoryBufferRefs given to it have a unique
+  // name. If two archives define two members with the same name, this
+  // causes a collision which result in only one of the objects being taken
+  // into consideration at LTO time (which very likely causes undefined
+  // symbols later in the link stage). So we append file offset to make
+  // filename unique.
+  StringRef name = archiveName.empty()
+                       ? saver().save(path)
+                       : saver().save(archiveName + "(" + path::filename(path) +
+                                      " at " + utostr(offsetInArchive) + ")");
+  MemoryBufferRef mbref(mb.getBuffer(), name);
+
+  obj = check(lto::InputFile::create(mbref));
+
+  // If this isn't part of an archive, it's eagerly linked, so mark it live.
+  if (archiveName.empty())
+    markLive();
+}
+
 bool BitcodeFile::doneLTO = false;
 
 void BitcodeFile::parse() {
@@ -749,8 +831,6 @@ void BitcodeFile::parse() {
     return;
   }
 
-  obj = check(lto::InputFile::create(MemoryBufferRef(
-      mb.getBuffer(), saver.save(archiveName + mb.getBufferIdentifier()))));
   Triple t(obj->getTargetTriple());
   if (!t.isWasm()) {
     error(toString(this) + ": machine type must be wasm32 or wasm64");
index f4ff8bb..4c46ae8 100644 (file)
@@ -18,6 +18,7 @@
 #include "llvm/Object/Archive.h"
 #include "llvm/Object/Wasm.h"
 #include "llvm/Support/MemoryBuffer.h"
+#include <optional>
 #include <vector>
 
 namespace llvm {
@@ -46,6 +47,7 @@ public:
     SharedKind,
     ArchiveKind,
     BitcodeKind,
+    StubKind,
   };
 
   virtual ~InputFile() {}
@@ -116,12 +118,10 @@ public:
   // Returns the underlying wasm file.
   const WasmObjectFile *getWasmObj() const { return wasmObj.get(); }
 
-  void dumpInfo() const;
-
   uint32_t calcNewIndex(const WasmRelocation &reloc) const;
   uint64_t calcNewValue(const WasmRelocation &reloc, uint64_t tombstone,
                         const InputChunk *chunk) const;
-  uint64_t calcNewAddend(const WasmRelocation &reloc) const;
+  int64_t calcNewAddend(const WasmRelocation &reloc) const;
   Symbol *getSymbol(const WasmRelocation &reloc) const {
     return symbols[reloc.Index];
   };
@@ -156,7 +156,7 @@ private:
   Symbol *createDefined(const WasmSymbol &sym);
   Symbol *createUndefined(const WasmSymbol &sym, bool isCalledDirectly);
 
-  bool isExcludedByComdat(InputChunk *chunk) const;
+  bool isExcludedByComdat(const InputChunk *chunk) const;
   void addLegacyIndirectFunctionTableIfNeeded(uint32_t tableSymbolCount);
 
   std::unique_ptr<WasmObjectFile> wasmObj;
@@ -172,14 +172,8 @@ public:
 // .bc file
 class BitcodeFile : public InputFile {
 public:
-  explicit BitcodeFile(MemoryBufferRef m, StringRef archiveName)
-      : InputFile(BitcodeKind, m) {
-    this->archiveName = std::string(archiveName);
-
-    // If this isn't part of an archive, it's eagerly linked, so mark it live.
-    if (archiveName.empty())
-      markLive();
-  }
+  BitcodeFile(MemoryBufferRef m, StringRef archiveName,
+              uint64_t offsetInArchive);
   static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
 
   void parse();
@@ -190,16 +184,29 @@ public:
   static bool doneLTO;
 };
 
+// Stub libray (See docs/WebAssembly.rst)
+class StubFile : public InputFile {
+public:
+  explicit StubFile(MemoryBufferRef m) : InputFile(StubKind, m) {}
+
+  static bool classof(const InputFile *f) { return f->kind() == StubKind; }
+
+  void parse();
+
+  llvm::DenseMap<StringRef, std::vector<StringRef>> symbolDependencies;
+};
+
 inline bool isBitcode(MemoryBufferRef mb) {
   return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode;
 }
 
 // Will report a fatal() error if the input buffer is not a valid bitcode
 // or wasm object file.
-InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName = "");
+InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName = "",
+                            uint64_t offsetInArchive = 0);
 
 // Opens a given file.
-llvm::Optional<MemoryBufferRef> readFile(StringRef path);
+std::optional<MemoryBufferRef> readFile(StringRef path);
 
 } // namespace wasm
 
index 4f76fc0..150e663 100644 (file)
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/IR/DiagnosticPrinter.h"
-#include "llvm/LTO/Caching.h"
 #include "llvm/LTO/Config.h"
 #include "llvm/LTO/LTO.h"
 #include "llvm/Object/SymbolicFile.h"
+#include "llvm/Support/Caching.h"
 #include "llvm/Support/CodeGen.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/FileSystem.h"
@@ -52,11 +52,10 @@ static std::unique_ptr<lto::LTO> createLTO() {
   c.OptLevel = config->ltoo;
   c.MAttrs = getMAttrs();
   c.CGOptLevel = args::getCGOptLevel(config->ltoo);
-  c.UseNewPM = config->ltoNewPassManager;
   c.DebugPassManager = config->ltoDebugPassManager;
 
   if (config->relocatable)
-    c.RelocModel = None;
+    c.RelocModel = std::nullopt;
   else if (config->isPic)
     c.RelocModel = Reloc::PIC_;
   else
@@ -77,8 +76,9 @@ BitcodeCompiler::~BitcodeCompiler() = default;
 
 static void undefine(Symbol *s) {
   if (auto f = dyn_cast<DefinedFunction>(s))
-    replaceSymbol<UndefinedFunction>(f, f->getName(), None, None, 0,
-                                     f->getFile(), f->signature);
+    replaceSymbol<UndefinedFunction>(f, f->getName(), std::nullopt,
+                                     std::nullopt, 0, f->getFile(),
+                                     f->signature);
   else if (isa<DefinedData>(s))
     replaceSymbol<UndefinedData>(s, s->getName(), 0, s->getFile());
   else
@@ -127,23 +127,23 @@ std::vector<StringRef> BitcodeCompiler::compile() {
   // The --thinlto-cache-dir option specifies the path to a directory in which
   // to cache native object files for ThinLTO incremental builds. If a path was
   // specified, configure LTO to use it as the cache directory.
-  lto::NativeObjectCache cache;
+  FileCache cache;
   if (!config->thinLTOCacheDir.empty())
-    cache = check(
-        lto::localCache(config->thinLTOCacheDir,
-                        [&](size_t task, std::unique_ptr<MemoryBuffer> mb) {
-                          files[task] = std::move(mb);
-                        }));
+    cache = check(localCache("ThinLTO", "Thin", config->thinLTOCacheDir,
+                             [&](size_t task, const Twine &moduleName,
+                                 std::unique_ptr<MemoryBuffer> mb) {
+                               files[task] = std::move(mb);
+                             }));
 
   checkError(ltoObj->run(
-      [&](size_t task) {
-        return std::make_unique<lto::NativeObjectStream>(
+      [&](size_t task, const Twine &moduleName) {
+        return std::make_unique<CachedFileStream>(
             std::make_unique<raw_svector_ostream>(buf[task]));
       },
       cache));
 
   if (!config->thinLTOCacheDir.empty())
-    pruneCache(config->thinLTOCacheDir, config->thinLTOCachePolicy);
+    pruneCache(config->thinLTOCacheDir, config->thinLTOCachePolicy, files);
 
   std::vector<StringRef> ret;
   for (unsigned i = 0; i != maxTasks; ++i) {
index 9dbab50..288c189 100644 (file)
@@ -28,7 +28,6 @@
 #include "SyntheticSections.h"
 #include "lld/Common/Strings.h"
 #include "llvm/ADT/MapVector.h"
-#include "llvm/ADT/SetVector.h"
 #include "llvm/Support/Parallel.h"
 #include "llvm/Support/raw_ostream.h"
 
@@ -75,7 +74,7 @@ static SymbolMapTy getSectionSyms(ArrayRef<Symbol *> syms) {
 static DenseMap<Symbol *, std::string>
 getSymbolStrings(ArrayRef<Symbol *> syms) {
   std::vector<std::string> str(syms.size());
-  parallelForEachN(0, syms.size(), [&](size_t i) {
+  parallelFor(0, syms.size(), [&](size_t i) {
     raw_string_ostream os(str[i]);
     auto *chunk = syms[i]->getChunk();
     if (chunk == nullptr)
index 3e5d234..2b8c8e1 100644 (file)
@@ -90,7 +90,7 @@ void MarkLive::run() {
     enqueue(symtab->find(config->entry));
 
   // We need to preserve any no-strip or exported symbol
-  for (Symbol *sym : symtab->getSymbols())
+  for (Symbol *sym : symtab->symbols())
     if (sym->isNoStrip() || sym->isExported())
       enqueue(sym);
 
index 40cb314..b0c727c 100644 (file)
@@ -1,5 +1,23 @@
 include "llvm/Option/OptParser.td"
 
+// Convenience classes for long options which only accept two dashes. For lld
+// specific or newer long options, we prefer two dashes to avoid collision with
+// short options. For many others, we have to accept both forms to be compatible
+// with GNU ld.
+class FF<string name> : Flag<["--"], name>;
+class JJ<string name>: Joined<["--"], name>;
+
+multiclass EEq<string name, string help> {
+  def NAME: Separate<["--"], name>;
+  def NAME # _eq: Joined<["--"], name # "=">, Alias<!cast<Separate>(NAME)>,
+    HelpText<help>;
+}
+
+multiclass BB<string name, string help1, string help2> {
+  def NAME: Flag<["--"], name>, HelpText<help1>;
+  def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>;
+}
+
 // For options whose names are multiple letters, either one dash or
 // two can precede the option name except those that start with 'o'.
 class F<string name>: Flag<["--", "-"], name>;
@@ -17,14 +35,13 @@ multiclass B<string name, string help1, string help2> {
   def no_ # NAME: Flag<["--", "-"], "no-" # name>, HelpText<help2>;
 }
 
-multiclass BB<string name, string help1, string help2> {
-  def NAME: Flag<["--"], name>, HelpText<help1>;
-  def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>;
-}
-
 // The following flags are shared with the ELF linker
 def Bsymbolic: F<"Bsymbolic">, HelpText<"Bind defined symbols locally">;
 
+def Bdynamic: F<"Bdynamic">, HelpText<"Link against shared libraries (default)">;
+
+def Bstatic: F<"Bstatic">, HelpText<"Do not link against shared libraries">;
+
 defm color_diagnostics: B<"color-diagnostics",
   "Alias for --color-diagnostics=always",
   "Alias for --color-diagnostics=never">;
@@ -51,8 +68,8 @@ defm export_dynamic: B<"export-dynamic",
 def entry: S<"entry">, MetaVarName<"<entry>">,
   HelpText<"Name of entry point symbol">;
 
-def error_limit: J<"error-limit=">,
-  HelpText<"Maximum number of errors to emit before stopping (0 = no limit)">;
+defm error_limit:
+  EEq<"error-limit", "Maximum number of errors to emit before stopping (0 = no limit)">;
 
 def fatal_warnings: F<"fatal-warnings">,
   HelpText<"Treat warnings as errors">;
@@ -61,21 +78,21 @@ defm gc_sections: B<"gc-sections",
     "Enable garbage collection of unused sections",
     "Disable garbage collection of unused sections">;
 
-defm merge_data_segments: B<"merge-data-segments",
+defm merge_data_segments: BB<"merge-data-segments",
     "Enable merging data segments",
     "Disable merging data segments">;
 
 def help: F<"help">, HelpText<"Print option help">;
 
-def l: JoinedOrSeparate<["-"], "l">, MetaVarName<"<libName>">,
+def library: JoinedOrSeparate<["-"], "l">, MetaVarName<"<libName>">,
   HelpText<"Root name of library to use">;
 
-def L: JoinedOrSeparate<["-"], "L">, MetaVarName<"<dir>">,
+def library_path: JoinedOrSeparate<["-"], "L">, MetaVarName<"<dir>">,
   HelpText<"Add a directory to the library search path">;
 
 def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">;
 
-def mllvm: S<"mllvm">, HelpText<"Options to pass to LLVM">;
+defm mllvm: Eq<"mllvm", "Additional arguments to forward to LLVM's option processing">;
 
 defm Map: Eq<"Map", "Print a link map to the specified file">;
 
@@ -99,7 +116,7 @@ def print_map: F<"print-map">,
 
 def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">;
 
-defm reproduce: Eq<"reproduce", "Dump linker invocation and input files for debugging">;
+defm reproduce: EEq<"reproduce", "Dump linker invocation and input files for debugging">;
 
 defm rsp_quoting: Eq<"rsp-quoting", "Quoting style for response files">,
   MetaVarName<"[posix,windows]">;
@@ -141,8 +158,9 @@ def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"<option>">,
 // The follow flags are unique to wasm
 
 def allow_undefined: F<"allow-undefined">,
-  HelpText<"Allow undefined symbols in linked binary. This options is equivelant "
-           "to --import-undefined and --unresolved-symbols=ignore-all">;
+  HelpText<"Allow undefined symbols in linked binary. "
+           "This options is equivalent to --import-undefined "
+           "and --unresolved-symbols=ignore-all">;
 
 def import_undefined: F<"import-undefined">,
   HelpText<"Turn undefined symbols into imports where possible">;
@@ -158,55 +176,77 @@ defm export: Eq<"export", "Force a symbol to be exported">;
 defm export_if_defined: Eq<"export-if-defined",
      "Force a symbol to be exported, if it is defined in the input">;
 
-def export_all: F<"export-all">,
+def export_all: FF<"export-all">,
   HelpText<"Export all symbols (normally combined with --no-gc-sections)">;
 
-def export_table: F<"export-table">,
+def export_table: FF<"export-table">,
   HelpText<"Export function table to the environment">;
 
-def growable_table: F<"growable-table">,
+def growable_table: FF<"growable-table">,
   HelpText<"Remove maximum size from function table, allowing table to grow">;
 
-def global_base: J<"global-base=">,
+def global_base: JJ<"global-base=">,
   HelpText<"Where to start to place global data">;
 
-def import_memory: F<"import-memory">,
-  HelpText<"Import memory from the environment">;
+def import_memory: FF<"import-memory">,
+  HelpText<"Import the module's memory from the default module of \"env\" with the name \"memory\".">;
+def import_memory_with_name: JJ<"import-memory=">,
+  HelpText<"Import the module's memory from the passed module with the passed name.">,
+  MetaVarName<"<module>,<name>">;
+
+def export_memory: FF<"export-memory">,
+  HelpText<"Export the module's memory with the default name of \"memory\"">;
+def export_memory_with_name: JJ<"export-memory=">,
+  HelpText<"Export the module's memory with the passed name">;
 
-def shared_memory: F<"shared-memory">,
+def shared_memory: FF<"shared-memory">,
   HelpText<"Use shared linear memory">;
 
-def import_table: F<"import-table">,
+def import_table: FF<"import-table">,
   HelpText<"Import function table from the environment">;
 
-def initial_memory: J<"initial-memory=">,
+def initial_memory: JJ<"initial-memory=">,
   HelpText<"Initial size of the linear memory">;
 
-def max_memory: J<"max-memory=">,
+def max_memory: JJ<"max-memory=">,
   HelpText<"Maximum size of the linear memory">;
 
-def no_entry: F<"no-entry">,
+def no_entry: FF<"no-entry">,
   HelpText<"Do not output any entry point">;
 
-def stack_first: F<"stack-first">,
+def stack_first: FF<"stack-first">,
   HelpText<"Place stack at start of linear memory rather than after data">;
 
 defm whole_archive: B<"whole-archive",
     "Force load of all members in a static library",
     "Do not force load of all members in a static library (default)">;
 
-defm check_features: B<"check-features",
+def why_extract: JJ<"why-extract=">, HelpText<"Print to a file about why archive members are extracted">;
+
+defm check_features: BB<"check-features",
     "Check feature compatibility of linked objects (default)",
     "Ignore feature compatibility of linked objects">;
 
 def features: CommaJoined<["--", "-"], "features=">,
   HelpText<"Comma-separated used features, inferred from input objects by default.">;
 
+def extra_features: CommaJoined<["--", "-"], "extra-features=">,
+  HelpText<"Comma-separated list of features to add to the default set of features inferred from input objects.">;
+
 // Aliases
 def: JoinedOrSeparate<["-"], "e">, Alias<entry>;
 def: J<"entry=">, Alias<entry>;
+def: F<"call_shared">, Alias<Bdynamic>, HelpText<"Alias for --Bdynamic">;
+def: F<"dy">, Alias<Bdynamic>, HelpText<"Alias for --Bdynamic">;
+def: F<"dn">, Alias<Bstatic>, HelpText<"Alias for --Bstatic">;
+def: F<"non_shared">, Alias<Bstatic>, HelpText<"Alias for --Bstatic">;
+def: F<"static">, Alias<Bstatic>, HelpText<"Alias for --Bstatic">;
 def: Flag<["-"], "E">, Alias<export_dynamic>, HelpText<"Alias for --export-dynamic">;
 def: Flag<["-"], "i">, Alias<initial_memory>;
+def: Separate<["--", "-"], "library">, Alias<library>;
+def: Joined<["--", "-"], "library=">, Alias<library>;
+def: Separate<["--", "-"], "library-path">, Alias<library_path>;
+def: Joined<["--", "-"], "library-path=">, Alias<library_path>;
 def: Flag<["-"], "M">, Alias<print_map>, HelpText<"Alias for --print-map">;
 def: Flag<["-"], "r">, Alias<relocatable>;
 def: Flag<["-"], "s">, Alias<strip_all>, HelpText<"Alias for --strip-all">;
@@ -216,21 +256,22 @@ def: JoinedOrSeparate<["-"], "y">, Alias<trace_symbol>, HelpText<"Alias for --tr
 def: JoinedOrSeparate<["-"], "u">, Alias<undefined>;
 
 // LTO-related options.
-def lto_O: J<"lto-O">, MetaVarName<"<opt-level>">,
+def lto_O: JJ<"lto-O">, MetaVarName<"<opt-level>">,
   HelpText<"Optimization level for LTO">;
-def lto_partitions: J<"lto-partitions=">,
+def lto_partitions: JJ<"lto-partitions=">,
   HelpText<"Number of LTO codegen partitions">;
 def disable_verify: F<"disable-verify">;
 def save_temps: F<"save-temps">, HelpText<"Save intermediate LTO compilation results">;
-def thinlto_cache_dir: J<"thinlto-cache-dir=">,
+def thinlto_cache_dir: JJ<"thinlto-cache-dir=">,
   HelpText<"Path to ThinLTO cached object file directory">;
-defm thinlto_cache_policy: Eq<"thinlto-cache-policy", "Pruning policy for the ThinLTO cache">;
-def thinlto_jobs: J<"thinlto-jobs=">,
+defm thinlto_cache_policy: EEq<"thinlto-cache-policy", "Pruning policy for the ThinLTO cache">;
+def thinlto_jobs: JJ<"thinlto-jobs=">,
   HelpText<"Number of ThinLTO jobs. Default to --threads=">;
-defm lto_legacy_pass_manager: BB<"lto-legacy-pass-manager", "Use legacy pass manager", "Use new pass manager">;
-def lto_debug_pass_manager: F<"lto-debug-pass-manager">,
+def no_lto_legacy_pass_manager: FF<"no-lto-legacy-pass-manager">,
+  HelpText<"Use new pass manager">;
+def lto_debug_pass_manager: FF<"lto-debug-pass-manager">,
   HelpText<"Debug new pass manager">;
 
 // Experimental PIC mode.
-def experimental_pic: F<"experimental-pic">,
+def experimental_pic: FF<"experimental-pic">,
   HelpText<"Enable Experimental PIC">;
index 85994ea..9c4383c 100644 (file)
@@ -33,41 +33,6 @@ std::string toString(const wasm::OutputSection &sec) {
 }
 
 namespace wasm {
-static StringRef sectionTypeToString(uint32_t sectionType) {
-  switch (sectionType) {
-  case WASM_SEC_CUSTOM:
-    return "CUSTOM";
-  case WASM_SEC_TYPE:
-    return "TYPE";
-  case WASM_SEC_IMPORT:
-    return "IMPORT";
-  case WASM_SEC_FUNCTION:
-    return "FUNCTION";
-  case WASM_SEC_TABLE:
-    return "TABLE";
-  case WASM_SEC_MEMORY:
-    return "MEMORY";
-  case WASM_SEC_GLOBAL:
-    return "GLOBAL";
-  case WASM_SEC_TAG:
-    return "TAG";
-  case WASM_SEC_EXPORT:
-    return "EXPORT";
-  case WASM_SEC_START:
-    return "START";
-  case WASM_SEC_ELEM:
-    return "ELEM";
-  case WASM_SEC_CODE:
-    return "CODE";
-  case WASM_SEC_DATA:
-    return "DATA";
-  case WASM_SEC_DATACOUNT:
-    return "DATACOUNT";
-  default:
-    fatal("invalid section type");
-  }
-}
-
 StringRef OutputSection::getSectionName() const {
   return sectionTypeToString(type);
 }
@@ -101,8 +66,8 @@ void CodeSection::finalizeContents() {
 }
 
 void CodeSection::writeTo(uint8_t *buf) {
-  log("writing " + toString(*this));
-  log(" size=" + Twine(getSize()));
+  log("writing " + toString(*this) + " offset=" + Twine(offset) +
+      " size=" + Twine(getSize()));
   log(" headersize=" + Twine(header.size()));
   log(" codeheadersize=" + Twine(codeSectionHeader.size()));
   buf += offset;
@@ -133,40 +98,53 @@ void CodeSection::writeRelocations(raw_ostream &os) const {
 
 void DataSection::finalizeContents() {
   raw_string_ostream os(dataSectionHeader);
-  unsigned segmentCount =
-      std::count_if(segments.begin(), segments.end(),
-                    [](OutputSegment *segment) { return !segment->isBss; });
-
+  unsigned segmentCount = llvm::count_if(segments, [](OutputSegment *segment) {
+    return segment->requiredInBinary();
+  });
 #ifndef NDEBUG
-  unsigned activeCount = std::count_if(
-      segments.begin(), segments.end(), [](OutputSegment *segment) {
-        return (segment->initFlags & WASM_DATA_SEGMENT_IS_PASSIVE) == 0;
-      });
+  unsigned activeCount = llvm::count_if(segments, [](OutputSegment *segment) {
+    return (segment->initFlags & WASM_DATA_SEGMENT_IS_PASSIVE) == 0;
+  });
 #endif
 
-  assert((config->sharedMemory || !config->isPic || activeCount <= 1) &&
+  assert((config->sharedMemory || !config->isPic || config->extendedConst ||
+          activeCount <= 1) &&
          "output segments should have been combined by now");
 
   writeUleb128(os, segmentCount, "data segment count");
   os.flush();
   bodySize = dataSectionHeader.size();
+  bool is64 = config->is64.value_or(false);
 
   for (OutputSegment *segment : segments) {
-    if (segment->isBss)
+    if (!segment->requiredInBinary())
       continue;
     raw_string_ostream os(segment->header);
     writeUleb128(os, segment->initFlags, "init flags");
     if (segment->initFlags & WASM_DATA_SEGMENT_HAS_MEMINDEX)
       writeUleb128(os, 0, "memory index");
     if ((segment->initFlags & WASM_DATA_SEGMENT_IS_PASSIVE) == 0) {
-      WasmInitExpr initExpr;
-      if (config->isPic) {
-        initExpr.Opcode = WASM_OPCODE_GLOBAL_GET;
-        initExpr.Value.Global = WasmSym::memoryBase->getGlobalIndex();
+      if (config->isPic && config->extendedConst) {
+        writeU8(os, WASM_OPCODE_GLOBAL_GET, "global get");
+        writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(),
+                     "literal (global index)");
+        if (segment->startVA) {
+          writePtrConst(os, segment->startVA, is64, "offset");
+          writeU8(os, is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD, "add");
+        }
+        writeU8(os, WASM_OPCODE_END, "opcode:end");
       } else {
-        initExpr = intConst(segment->startVA, config->is64.getValueOr(false));
+        WasmInitExpr initExpr;
+        initExpr.Extended = false;
+        if (config->isPic) {
+          assert(segment->startVA == 0);
+          initExpr.Inst.Opcode = WASM_OPCODE_GLOBAL_GET;
+          initExpr.Inst.Value.Global = WasmSym::memoryBase->getGlobalIndex();
+        } else {
+          initExpr = intConst(segment->startVA, is64);
+        }
+        writeInitExpr(os, initExpr);
       }
-      writeInitExpr(os, initExpr);
     }
     writeUleb128(os, segment->size, "segment size");
     os.flush();
@@ -187,8 +165,8 @@ void DataSection::finalizeContents() {
 }
 
 void DataSection::writeTo(uint8_t *buf) {
-  log("writing " + toString(*this) + " size=" + Twine(getSize()) +
-      " body=" + Twine(bodySize));
+  log("writing " + toString(*this) + " offset=" + Twine(offset) +
+      " size=" + Twine(getSize()) + " body=" + Twine(bodySize));
   buf += offset;
 
   // Write section header
@@ -199,7 +177,7 @@ void DataSection::writeTo(uint8_t *buf) {
   memcpy(buf, dataSectionHeader.data(), dataSectionHeader.size());
 
   for (const OutputSegment *segment : segments) {
-    if (segment->isBss)
+    if (!segment->requiredInBinary())
       continue;
     // Write data segment header
     uint8_t *segStart = buf + segment->sectionOffset;
@@ -227,7 +205,7 @@ void DataSection::writeRelocations(raw_ostream &os) const {
 
 bool DataSection::isNeeded() const {
   for (const OutputSegment *seg : segments)
-    if (!seg->isBss)
+    if (seg->requiredInBinary())
       return true;
   return false;
 }
@@ -279,8 +257,8 @@ void CustomSection::finalizeContents() {
 }
 
 void CustomSection::writeTo(uint8_t *buf) {
-  log("writing " + toString(*this) + " size=" + Twine(getSize()) +
-      " chunks=" + Twine(inputSections.size()));
+  log("writing " + toString(*this) + " offset=" + Twine(offset) +
+      " size=" + Twine(getSize()) + " chunks=" + Twine(inputSections.size()));
 
   assert(offset);
   buf += offset;
index c3becf6..fcc8cf4 100644 (file)
@@ -33,10 +33,7 @@ public:
   virtual ~OutputSection() = default;
 
   StringRef getSectionName() const;
-  void setOffset(size_t newOffset) {
-    log("setOffset: " + toString(*this) + ": " + Twine(newOffset));
-    offset = newOffset;
-  }
+  void setOffset(size_t newOffset) { offset = newOffset; }
   void createHeader(size_t bodySize);
   virtual bool isNeeded() const { return true; }
   virtual size_t getSize() const = 0;
index c09d5c3..86b5384 100644 (file)
@@ -23,8 +23,8 @@ void OutputSegment::addInputSegment(InputChunk *inSeg) {
   alignment = std::max(alignment, inSeg->alignment);
   inputSegments.push_back(inSeg);
   size = llvm::alignTo(size, 1ULL << inSeg->alignment);
-  LLVM_DEBUG(dbgs() << "addInputSegment: " << inSeg->getName()
-                    << " oname=" << name << " size=" << inSeg->getSize()
+  LLVM_DEBUG(dbgs() << "addInputSegment: " << inSeg->name << " oname=" << name
+                    << " size=" << inSeg->getSize()
                     << " align=" << inSeg->alignment << " at:" << size << "\n");
   inSeg->outputSeg = this;
   inSeg->outputSegmentOffset = size;
@@ -75,7 +75,7 @@ void OutputSegment::finalizeInputSegments() {
   size = 0;
   for (InputChunk *seg : inputSegments) {
     size = llvm::alignTo(size, 1ULL << seg->alignment);
-    LLVM_DEBUG(llvm::dbgs() << "outputSegmentOffset set: " << seg->getName()
+    LLVM_DEBUG(llvm::dbgs() << "outputSegmentOffset set: " << seg->name
                             << " -> " << size << "\n");
     seg->outputSegmentOffset = size;
     size += seg->getSize();
index 1d5cf91..3b7a0f5 100644 (file)
@@ -24,6 +24,11 @@ public:
 
   void addInputSegment(InputChunk *inSeg);
   void finalizeInputSegments();
+  // In most circumstances BSS segments don't need to be written
+  // to the output binary.  However if the memory is imported, and
+  // we can't use memory.fill during startup (due to lack of bulk
+  // memory feature) then we include BSS segments verbatim.
+  bool requiredInBinary() const { return !isBss || config->emitBssSegments; }
 
   bool isTLS() const { return name == ".tdata"; }
 
index 1dca6d6..a725b6d 100644 (file)
@@ -20,7 +20,8 @@ namespace lld {
 namespace wasm {
 
 static bool requiresGOTAccess(const Symbol *sym) {
-  if (!config->isPic)
+  if (!config->isPic &&
+      config->unresolvedSymbols != UnresolvedPolicy::ImportDynamic)
     return false;
   if (sym->isHidden() || sym->isLocal())
     return false;
@@ -32,17 +33,13 @@ static bool requiresGOTAccess(const Symbol *sym) {
 }
 
 static bool allowUndefined(const Symbol* sym) {
-  // Undefined functions and globals with explicit import name are allowed to be
-  // undefined at link time.
-  if (auto *f = dyn_cast<UndefinedFunction>(sym))
-    if (f->importName || config->importUndefined)
-      return true;
-  if (auto *g = dyn_cast<UndefinedGlobal>(sym))
-    if (g->importName)
-      return true;
-  if (auto *g = dyn_cast<UndefinedGlobal>(sym))
-    if (g->importName)
-      return true;
+  // Symbols that are explicitly imported are always allowed to be undefined at
+  // link time.
+  if (sym->isImported())
+    return true;
+  if (isa<UndefinedFunction>(sym) && config->importUndefined)
+    return true;
+
   return config->allowUndefinedSymbols.count(sym->getName()) != 0;
 }
 
@@ -70,6 +67,8 @@ static void reportUndefined(Symbol *sym) {
         }
       }
       break;
+    case UnresolvedPolicy::ImportDynamic:
+      break;
     }
   }
 }
@@ -116,10 +115,21 @@ void scanRelocations(InputChunk *chunk) {
       break;
     case R_WASM_MEMORY_ADDR_TLS_SLEB:
     case R_WASM_MEMORY_ADDR_TLS_SLEB64:
+      if (!sym->isDefined()) {
+        error(toString(file) + ": relocation " + relocTypeToString(reloc.Type) +
+              " cannot be used against an undefined symbol `" + toString(*sym) +
+              "`");
+      }
       // In single-threaded builds TLS is lowered away and TLS data can be
       // merged with normal data and allowing TLS relocation in non-TLS
       // segments.
       if (config->sharedMemory) {
+        if (!sym->isTLS()) {
+          error(toString(file) + ": relocation " +
+                relocTypeToString(reloc.Type) +
+                " cannot be used against non-TLS symbol `" + toString(*sym) +
+                "`");
+        }
         if (auto *D = dyn_cast<DefinedData>(sym)) {
           if (!D->segment->outputSeg->isTLS()) {
             error(toString(file) + ": relocation " +
@@ -132,7 +142,9 @@ void scanRelocations(InputChunk *chunk) {
       break;
     }
 
-    if (config->isPic) {
+    if (config->isPic ||
+        (sym->isUndefined() &&
+         config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic)) {
       switch (reloc.Type) {
       case R_WASM_TABLE_INDEX_SLEB:
       case R_WASM_TABLE_INDEX_SLEB64:
@@ -143,17 +155,8 @@ void scanRelocations(InputChunk *chunk) {
         // Certain relocation types can't be used when building PIC output,
         // since they would require absolute symbol addresses at link time.
         error(toString(file) + ": relocation " + relocTypeToString(reloc.Type) +
-              " cannot be used against symbol " + toString(*sym) +
-              "; recompile with -fPIC");
-        break;
-      case R_WASM_MEMORY_ADDR_TLS_SLEB:
-      case R_WASM_MEMORY_ADDR_TLS_SLEB64:
-        if (!sym->isDefined()) {
-          error(toString(file) +
-                ": TLS symbol is undefined, but TLS symbols cannot yet be "
-                "imported: `" +
-                toString(*sym) + "`");
-        }
+              " cannot be used against symbol `" + toString(*sym) +
+              "`; recompile with -fPIC");
         break;
       case R_WASM_TABLE_INDEX_I32:
       case R_WASM_TABLE_INDEX_I64:
@@ -161,7 +164,7 @@ void scanRelocations(InputChunk *chunk) {
       case R_WASM_MEMORY_ADDR_I64:
         // These relocation types are only present in the data section and
         // will be converted into code by `generateRelocationCode`.  This code
-        // requires the symbols to have GOT entires.
+        // requires the symbols to have GOT entries.
         if (requiresGOTAccess(sym))
           addGOTEntry(sym);
         break;
index 1a52133..e5898c5 100644 (file)
@@ -11,9 +11,8 @@
 #include "InputChunks.h"
 #include "InputElement.h"
 #include "WriterUtils.h"
-#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Memory.h"
-#include "llvm/ADT/SetVector.h"
+#include "lld/Common/CommonLinkerContext.h"
+#include <optional>
 
 #define DEBUG_TYPE "lld"
 
@@ -40,6 +39,13 @@ void SymbolTable::addFile(InputFile *file) {
     return;
   }
 
+  // stub file
+  if (auto *f = dyn_cast<StubFile>(file)) {
+    f->parse();
+    stubFiles.push_back(f);
+    return;
+  }
+
   if (config->trace)
     message(toString(file));
 
@@ -63,7 +69,7 @@ void SymbolTable::addFile(InputFile *file) {
 // using LLVM functions and replaces bitcode symbols with the results.
 // Because all bitcode files that the program consists of are passed
 // to the compiler at once, it can do whole-program optimization.
-void SymbolTable::addCombinedLTOObject() {
+void SymbolTable::compileBitcodeFiles() {
   // Prevent further LTO objects being included
   BitcodeFile::doneLTO = true;
 
@@ -113,6 +119,7 @@ std::pair<Symbol *, bool> SymbolTable::insertName(StringRef name) {
   sym->canInline = true;
   sym->traced = trace;
   sym->forceExport = false;
+  sym->referenced = !config->gcSections;
   symVector.emplace_back(sym);
   return {sym, true};
 }
@@ -144,7 +151,7 @@ static bool signatureMatches(FunctionSymbol *existing,
                              const WasmSignature *newSig) {
   const WasmSignature *oldSig = existing->signature;
 
-  // If either function is missing a signature (this happend for bitcode
+  // If either function is missing a signature (this happens for bitcode
   // symbols) then assume they match.  Any mismatch will be reported later
   // when the LTO objects are added.
   if (!newSig || !oldSig)
@@ -169,7 +176,6 @@ static void checkGlobalType(const Symbol *existing, const InputFile *file,
 }
 
 static void checkTagType(const Symbol *existing, const InputFile *file,
-                         const WasmTagType *newType,
                          const WasmSignature *newSig) {
   const auto *existingTag = dyn_cast<TagSymbol>(existing);
   if (!isa<TagSymbol>(existing)) {
@@ -177,12 +183,7 @@ static void checkTagType(const Symbol *existing, const InputFile *file,
     return;
   }
 
-  const WasmTagType *oldType = cast<TagSymbol>(existing)->getTagType();
   const WasmSignature *oldSig = existingTag->signature;
-  if (newType->Attribute != oldType->Attribute)
-    error("Tag type mismatch: " + existing->getName() + "\n>>> defined as " +
-          toString(*oldType) + " in " + toString(existing->getFile()) +
-          "\n>>> defined as " + toString(*newType) + " in " + toString(file));
   if (*newSig != *oldSig)
     warn("Tag signature mismatch: " + existing->getName() +
          "\n>>> defined as " + toString(*oldSig) + " in " +
@@ -258,11 +259,11 @@ DefinedGlobal *SymbolTable::addSyntheticGlobal(StringRef name, uint32_t flags,
 
 DefinedGlobal *SymbolTable::addOptionalGlobalSymbol(StringRef name,
                                                     InputGlobal *global) {
-  LLVM_DEBUG(dbgs() << "addOptionalGlobalSymbol: " << name << " -> " << global
-                    << "\n");
   Symbol *s = find(name);
   if (!s || s->isDefined())
     return nullptr;
+  LLVM_DEBUG(dbgs() << "addOptionalGlobalSymbol: " << name << " -> " << global
+                    << "\n");
   syntheticGlobals.emplace_back(global);
   return replaceSymbol<DefinedGlobal>(s, name, WASM_SYMBOL_VISIBILITY_HIDDEN,
                                       nullptr, global);
@@ -430,7 +431,7 @@ Symbol *SymbolTable::addDefinedTag(StringRef name, uint32_t flags,
     return s;
   }
 
-  checkTagType(s, file, &tag->getType(), &tag->signature);
+  checkTagType(s, file, &tag->signature);
 
   if (shouldReplace(s, file, flags))
     replaceSym();
@@ -468,8 +469,9 @@ Symbol *SymbolTable::addDefinedTable(StringRef name, uint32_t flags,
 // become available when the LTO object is read.  In this case we silently
 // replace the empty attributes with the valid ones.
 template <typename T>
-static void setImportAttributes(T *existing, Optional<StringRef> importName,
-                                Optional<StringRef> importModule,
+static void setImportAttributes(T *existing,
+                                std::optional<StringRef> importName,
+                                std::optional<StringRef> importModule,
                                 uint32_t flags, InputFile *file) {
   if (importName) {
     if (!existing->importName)
@@ -499,8 +501,8 @@ static void setImportAttributes(T *existing, Optional<StringRef> importName,
 }
 
 Symbol *SymbolTable::addUndefinedFunction(StringRef name,
-                                          Optional<StringRef> importName,
-                                          Optional<StringRef> importModule,
+                                          std::optional<StringRef> importName,
+                                          std::optional<StringRef> importModule,
                                           uint32_t flags, InputFile *file,
                                           const WasmSignature *sig,
                                           bool isCalledDirectly) {
@@ -529,6 +531,9 @@ Symbol *SymbolTable::addUndefinedFunction(StringRef name,
       lazy->signature = sig;
     } else {
       lazy->fetch();
+      if (!config->whyExtract.empty())
+        config->whyExtractRecords.emplace_back(toString(file), s->getFile(),
+                                               *s);
     }
   } else {
     auto existingFunction = dyn_cast<FunctionSymbol>(s);
@@ -548,9 +553,12 @@ Symbol *SymbolTable::addUndefinedFunction(StringRef name,
       else if (getFunctionVariant(s, sig, file, &s))
         replaceSym();
     }
-    if (existingUndefined)
+    if (existingUndefined) {
       setImportAttributes(existingUndefined, importName, importModule, flags,
                           file);
+      if (isCalledDirectly)
+        existingUndefined->isCalledDirectly = true;
+    }
   }
 
   return s;
@@ -581,8 +589,8 @@ Symbol *SymbolTable::addUndefinedData(StringRef name, uint32_t flags,
 }
 
 Symbol *SymbolTable::addUndefinedGlobal(StringRef name,
-                                        Optional<StringRef> importName,
-                                        Optional<StringRef> importModule,
+                                        std::optional<StringRef> importName,
+                                        std::optional<StringRef> importModule,
                                         uint32_t flags, InputFile *file,
                                         const WasmGlobalType *type) {
   LLVM_DEBUG(dbgs() << "addUndefinedGlobal: " << name << "\n");
@@ -605,8 +613,8 @@ Symbol *SymbolTable::addUndefinedGlobal(StringRef name,
 }
 
 Symbol *SymbolTable::addUndefinedTable(StringRef name,
-                                       Optional<StringRef> importName,
-                                       Optional<StringRef> importModule,
+                                       std::optional<StringRef> importName,
+                                       std::optional<StringRef> importModule,
                                        uint32_t flags, InputFile *file,
                                        const WasmTableType *type) {
   LLVM_DEBUG(dbgs() << "addUndefinedTable: " << name << "\n");
@@ -628,6 +636,30 @@ Symbol *SymbolTable::addUndefinedTable(StringRef name,
   return s;
 }
 
+Symbol *SymbolTable::addUndefinedTag(StringRef name,
+                                     std::optional<StringRef> importName,
+                                     std::optional<StringRef> importModule,
+                                     uint32_t flags, InputFile *file,
+                                     const WasmSignature *sig) {
+  LLVM_DEBUG(dbgs() << "addUndefinedTag: " << name << "\n");
+  assert(flags & WASM_SYMBOL_UNDEFINED);
+
+  Symbol *s;
+  bool wasInserted;
+  std::tie(s, wasInserted) = insert(name, file);
+  if (s->traced)
+    printTraceSymbolUndefined(name, file);
+
+  if (wasInserted)
+    replaceSymbol<UndefinedTag>(s, name, importName, importModule, flags, file,
+                                sig);
+  else if (auto *lazy = dyn_cast<LazySymbol>(s))
+    lazy->fetch();
+  else if (s->isDefined())
+    checkTagType(s, file, sig);
+  return s;
+}
+
 TableSymbol *SymbolTable::createUndefinedIndirectFunctionTable(StringRef name) {
   WasmLimits limits{0, 0, 0}; // Set by the writer.
   WasmTableType *type = make<WasmTableType>();
@@ -726,7 +758,10 @@ void SymbolTable::addLazy(ArchiveFile *file, const Archive::Symbol *sym) {
   }
 
   LLVM_DEBUG(dbgs() << "replacing existing undefined\n");
+  const InputFile *oldFile = s->getFile();
   file->addMember(sym);
+  if (!config->whyExtract.empty())
+    config->whyExtractRecords.emplace_back(toString(oldFile), s->getFile(), *s);
 }
 
 bool SymbolTable::addComdat(StringRef name) {
@@ -822,7 +857,7 @@ InputFunction *SymbolTable::replaceWithUnreachable(Symbol *sym,
 void SymbolTable::replaceWithUndefined(Symbol *sym) {
   // Add a synthetic dummy for weak undefined functions.  These dummies will
   // be GC'd if not used as the target of any "call" instructions.
-  StringRef debugName = saver.save("undefined_weak:" + toString(*sym));
+  StringRef debugName = saver().save("undefined_weak:" + toString(*sym));
   replaceWithUnreachable(sym, *sym->getSignature(), debugName);
   // Hide our dummy to prevent export.
   sym->setHidden(true);
@@ -833,8 +868,8 @@ void SymbolTable::replaceWithUndefined(Symbol *sym) {
 // will abort at runtime, so that relocations can still provided an operand to
 // the call instruction that passes Wasm validation.
 void SymbolTable::handleWeakUndefines() {
-  for (Symbol *sym : getSymbols()) {
-    if (sym->isUndefWeak()) {
+  for (Symbol *sym : symbols()) {
+    if (sym->isUndefWeak() && sym->isUsedInRegularObj) {
       if (sym->getSignature()) {
         replaceWithUndefined(sym);
       } else {
@@ -920,7 +955,8 @@ void SymbolTable::handleSymbolVariants() {
       if (symbol != defined) {
         auto *f = cast<FunctionSymbol>(symbol);
         reportFunctionSignatureMismatch(symName, f, defined, false);
-        StringRef debugName = saver.save("signature_mismatch:" + toString(*f));
+        StringRef debugName =
+            saver().save("signature_mismatch:" + toString(*f));
         replaceWithUnreachable(f, *f->signature, debugName);
       }
     }
index aea2f16..311d418 100644 (file)
@@ -15,8 +15,8 @@
 #include "lld/Common/LLVM.h"
 #include "llvm/ADT/CachedHashString.h"
 #include "llvm/ADT/DenseSet.h"
-#include "llvm/ADT/Optional.h"
 #include "llvm/BinaryFormat/WasmTraits.h"
+#include <optional>
 
 namespace lld {
 namespace wasm {
@@ -37,13 +37,13 @@ class InputSegment;
 // There is one add* function per symbol type.
 class SymbolTable {
 public:
+  ArrayRef<Symbol *> symbols() const { return symVector; }
+
   void wrap(Symbol *sym, Symbol *real, Symbol *wrap);
 
   void addFile(InputFile *file);
 
-  void addCombinedLTOObject();
-
-  ArrayRef<Symbol *> getSymbols() const { return symVector; }
+  void compileBitcodeFiles();
 
   Symbol *find(StringRef name);
 
@@ -63,22 +63,24 @@ public:
                           InputTable *t);
 
   Symbol *addUndefinedFunction(StringRef name,
-                               llvm::Optional<StringRef> importName,
-                               llvm::Optional<StringRef> importModule,
+                               std::optional<StringRef> importName,
+                               std::optional<StringRef> importModule,
                                uint32_t flags, InputFile *file,
                                const WasmSignature *signature,
                                bool isCalledDirectly);
   Symbol *addUndefinedData(StringRef name, uint32_t flags, InputFile *file);
   Symbol *addUndefinedGlobal(StringRef name,
-                             llvm::Optional<StringRef> importName,
-                             llvm::Optional<StringRef> importModule,
+                             std::optional<StringRef> importName,
+                             std::optional<StringRef> importModule,
                              uint32_t flags, InputFile *file,
                              const WasmGlobalType *type);
-  Symbol *addUndefinedTable(StringRef name,
-                            llvm::Optional<StringRef> importName,
-                            llvm::Optional<StringRef> importModule,
+  Symbol *addUndefinedTable(StringRef name, std::optional<StringRef> importName,
+                            std::optional<StringRef> importModule,
                             uint32_t flags, InputFile *file,
                             const WasmTableType *type);
+  Symbol *addUndefinedTag(StringRef name, std::optional<StringRef> importName,
+                          std::optional<StringRef> importModule, uint32_t flags,
+                          InputFile *file, const WasmSignature *sig);
 
   TableSymbol *resolveIndirectFunctionTable(bool required);
 
@@ -101,6 +103,7 @@ public:
   DefinedFunction *createUndefinedStub(const WasmSignature &sig);
 
   std::vector<ObjFile *> objectFiles;
+  std::vector<StubFile *> stubFiles;
   std::vector<SharedFile *> sharedFiles;
   std::vector<BitcodeFile *> bitcodeFiles;
   std::vector<InputFunction *> syntheticFunctions;
index 37a26b3..9a92355 100644 (file)
 #include "OutputSegment.h"
 #include "lld/Common/ErrorHandler.h"
 #include "lld/Common/Memory.h"
-#include "lld/Common/Strings.h"
+#include "llvm/Demangle/Demangle.h"
 
 #define DEBUG_TYPE "lld"
 
 using namespace llvm;
 using namespace llvm::object;
 using namespace llvm::wasm;
+using namespace lld::wasm;
 
 namespace lld {
 std::string toString(const wasm::Symbol &sym) {
@@ -34,8 +35,8 @@ std::string maybeDemangleSymbol(StringRef name) {
   if (name == "__main_argc_argv")
     return "main";
   if (wasm::config->demangle)
-    return demangleItanium(name);
-  return std::string(name);
+    return demangle(name.str());
+  return name.str();
 }
 
 std::string toString(wasm::Symbol::Kind kind) {
@@ -58,6 +59,8 @@ std::string toString(wasm::Symbol::Kind kind) {
     return "UndefinedGlobal";
   case wasm::Symbol::UndefinedTableKind:
     return "UndefinedTable";
+  case wasm::Symbol::UndefinedTagKind:
+    return "UndefinedTag";
   case wasm::Symbol::LazyKind:
     return "LazyKind";
   case wasm::Symbol::SectionKind:
@@ -74,14 +77,18 @@ DefinedFunction *WasmSym::callDtors;
 DefinedFunction *WasmSym::initMemory;
 DefinedFunction *WasmSym::applyDataRelocs;
 DefinedFunction *WasmSym::applyGlobalRelocs;
+DefinedFunction *WasmSym::applyGlobalTLSRelocs;
 DefinedFunction *WasmSym::initTLS;
 DefinedFunction *WasmSym::startFunction;
 DefinedData *WasmSym::dsoHandle;
 DefinedData *WasmSym::dataEnd;
 DefinedData *WasmSym::globalBase;
 DefinedData *WasmSym::heapBase;
+DefinedData *WasmSym::heapEnd;
 DefinedData *WasmSym::initMemoryFlag;
 GlobalSymbol *WasmSym::stackPointer;
+DefinedData *WasmSym::stackLow;
+DefinedData *WasmSym::stackHigh;
 GlobalSymbol *WasmSym::tlsBase;
 GlobalSymbol *WasmSym::tlsSize;
 GlobalSymbol *WasmSym::tlsAlign;
@@ -112,6 +119,8 @@ WasmSymbolType Symbol::getWasmType() const {
 const WasmSignature *Symbol::getSignature() const {
   if (auto* f = dyn_cast<FunctionSymbol>(this))
     return f->signature;
+  if (auto *t = dyn_cast<TagSymbol>(this))
+    return t->signature;
   if (auto *l = dyn_cast<LazySymbol>(this))
     return l->signature;
   return nullptr;
@@ -149,7 +158,7 @@ bool Symbol::isLive() const {
 void Symbol::markLive() {
   assert(!isDiscarded());
   referenced = true;
-  if (file != NULL && isDefined())
+  if (file != nullptr && isDefined())
     file->markLive();
   if (auto *g = dyn_cast<DefinedGlobal>(this))
     g->global->live = true;
@@ -185,11 +194,6 @@ void Symbol::setOutputSymbolIndex(uint32_t index) {
 void Symbol::setGOTIndex(uint32_t index) {
   LLVM_DEBUG(dbgs() << "setGOTIndex " << name << " -> " << index << "\n");
   assert(gotIndex == INVALID_INDEX);
-  if (config->isPic) {
-    // Any symbol that is assigned a GOT entry must be exported otherwise the
-    // dynamic linker won't be able create the entry that contains it.
-    forceExport = true;
-  }
   gotIndex = index;
 }
 
@@ -205,6 +209,8 @@ bool Symbol::isHidden() const {
   return (flags & WASM_SYMBOL_VISIBILITY_MASK) == WASM_SYMBOL_VISIBILITY_HIDDEN;
 }
 
+bool Symbol::isTLS() const { return flags & WASM_SYMBOL_TLS; }
+
 void Symbol::setHidden(bool isHidden) {
   LLVM_DEBUG(dbgs() << "setHidden: " << name << " -> " << isHidden << "\n");
   flags &= ~WASM_SYMBOL_VISIBILITY_MASK;
@@ -214,10 +220,20 @@ void Symbol::setHidden(bool isHidden) {
     flags |= WASM_SYMBOL_VISIBILITY_DEFAULT;
 }
 
+bool Symbol::isImported() const {
+  return isUndefined() && (importName.has_value() || forceImport);
+}
+
 bool Symbol::isExported() const {
   if (!isDefined() || isLocal())
     return false;
 
+  // Shared libraries must export all weakly defined symbols
+  // in case they contain the version that will be chosen by
+  // the dynamic linker.
+  if (config->shared && isLive() && isWeak() && !isHidden())
+    return true;
+
   if (config->exportAll || (config->exportDynamic && !isHidden()))
     return true;
 
@@ -233,15 +249,13 @@ bool Symbol::isNoStrip() const {
 }
 
 uint32_t FunctionSymbol::getFunctionIndex() const {
-  if (auto *f = dyn_cast<DefinedFunction>(this))
-    return f->function->getFunctionIndex();
-  if (const auto *u = dyn_cast<UndefinedFunction>(this)) {
-    if (u->stubFunction) {
+  if (const auto *u = dyn_cast<UndefinedFunction>(this))
+    if (u->stubFunction)
       return u->stubFunction->getFunctionIndex();
-    }
-  }
-  assert(functionIndex != INVALID_INDEX);
-  return functionIndex;
+  if (functionIndex != INVALID_INDEX)
+    return functionIndex;
+  auto *f = cast<DefinedFunction>(this);
+  return f->function->getFunctionIndex();
 }
 
 void FunctionSymbol::setFunctionIndex(uint32_t index) {
@@ -288,8 +302,17 @@ DefinedFunction::DefinedFunction(StringRef name, uint32_t flags, InputFile *f,
                      function ? &function->signature : nullptr),
       function(function) {}
 
+uint32_t DefinedFunction::getExportedFunctionIndex() const {
+  return function->getFunctionIndex();
+}
+
 uint64_t DefinedData::getVA() const {
   LLVM_DEBUG(dbgs() << "getVA: " << getName() << "\n");
+  // In the shared memory case, TLS symbols are relative to the start of the TLS
+  // output segment (__tls_base).  When building without shared memory, TLS
+  // symbols absolute, just like non-TLS.
+  if (isTLS() && config->sharedMemory)
+    return getOutputSegmentOffset() + value;
   if (segment)
     return segment->getVA(value);
   return value;
@@ -358,7 +381,6 @@ bool TagSymbol::hasTagIndex() const {
 DefinedTag::DefinedTag(StringRef name, uint32_t flags, InputFile *file,
                        InputTag *tag)
     : TagSymbol(name, DefinedTagKind, flags, file,
-                tag ? &tag->getType() : nullptr,
                 tag ? &tag->signature : nullptr),
       tag(tag) {}
 
@@ -439,6 +461,7 @@ void printTraceSymbol(Symbol *sym) {
 
 const char *defaultModule = "env";
 const char *functionTableName = "__indirect_function_table";
+const char *memoryName = "memory";
 
 } // namespace wasm
 } // namespace lld
index a883b8a..232339f 100644 (file)
@@ -11,9 +11,9 @@
 
 #include "Config.h"
 #include "lld/Common/LLVM.h"
-#include "llvm/ADT/Optional.h"
 #include "llvm/Object/Archive.h"
 #include "llvm/Object/Wasm.h"
+#include <optional>
 
 namespace lld {
 namespace wasm {
@@ -26,6 +26,9 @@ extern const char *defaultModule;
 // The name under which to import or export the wasm table.
 extern const char *functionTableName;
 
+// The name under which to import or export the wasm memory.
+extern const char *memoryName;
+
 using llvm::wasm::WasmSymbolType;
 
 class InputFile;
@@ -55,6 +58,7 @@ public:
     UndefinedDataKind,
     UndefinedGlobalKind,
     UndefinedTableKind,
+    UndefinedTagKind,
     LazyKind,
   };
 
@@ -66,7 +70,7 @@ public:
     return symbolKind == UndefinedFunctionKind ||
            symbolKind == UndefinedDataKind ||
            symbolKind == UndefinedGlobalKind ||
-           symbolKind == UndefinedTableKind;
+           symbolKind == UndefinedTableKind || symbolKind == UndefinedTagKind;
   }
 
   bool isLazy() const { return symbolKind == LazyKind; }
@@ -74,6 +78,7 @@ public:
   bool isLocal() const;
   bool isWeak() const;
   bool isHidden() const;
+  bool isTLS() const;
 
   // Returns true if this symbol exists in a discarded (due to COMDAT) section
   bool isDiscarded() const;
@@ -109,6 +114,7 @@ public:
   void setOutputSymbolIndex(uint32_t index);
 
   WasmSymbolType getWasmType() const;
+  bool isImported() const;
   bool isExported() const;
   bool isExportedExplicit() const;
 
@@ -130,7 +136,8 @@ protected:
   Symbol(StringRef name, Kind k, uint32_t flags, InputFile *f)
       : name(name), file(f), symbolKind(k), referenced(!config->gcSections),
         requiresGOT(false), isUsedInRegularObj(false), forceExport(false),
-        canInline(false), traced(false), isStub(false), flags(flags) {}
+        forceImport(false), canInline(false), traced(false), isStub(false),
+        flags(flags) {}
 
   StringRef name;
   InputFile *file;
@@ -151,10 +158,12 @@ public:
   // are unreferenced except by other bitcode objects.
   bool isUsedInRegularObj : 1;
 
-  // True if ths symbol is explicitly marked for export (i.e. via the
+  // True if this symbol is explicitly marked for export (i.e. via the
   // -e/--export command line flag)
   bool forceExport : 1;
 
+  bool forceImport : 1;
+
   // False if LTO shouldn't inline whatever this symbol points to. If a symbol
   // is overwritten after LTO, LTO shouldn't inline the symbol because it
   // doesn't know the final contents of the symbol.
@@ -173,8 +182,8 @@ public:
 
   uint32_t flags;
 
-  llvm::Optional<StringRef> importName;
-  llvm::Optional<StringRef> importModule;
+  std::optional<StringRef> importName;
+  std::optional<StringRef> importModule;
 };
 
 class FunctionSymbol : public Symbol {
@@ -214,13 +223,19 @@ public:
     return s->kind() == DefinedFunctionKind;
   }
 
+  // Get the function index to be used when exporting.  This only applies to
+  // defined functions and can be differ from the regular function index for
+  // weakly defined functions (that are imported and used via one index but
+  // defined and exported via another).
+  uint32_t getExportedFunctionIndex() const;
+
   InputFunction *function;
 };
 
 class UndefinedFunction : public FunctionSymbol {
 public:
-  UndefinedFunction(StringRef name, llvm::Optional<StringRef> importName,
-                    llvm::Optional<StringRef> importModule, uint32_t flags,
+  UndefinedFunction(StringRef name, std::optional<StringRef> importName,
+                    std::optional<StringRef> importModule, uint32_t flags,
                     InputFile *file = nullptr,
                     const WasmSignature *type = nullptr,
                     bool isCalledDirectly = true)
@@ -353,8 +368,8 @@ public:
 
 class UndefinedGlobal : public GlobalSymbol {
 public:
-  UndefinedGlobal(StringRef name, llvm::Optional<StringRef> importName,
-                  llvm::Optional<StringRef> importModule, uint32_t flags,
+  UndefinedGlobal(StringRef name, std::optional<StringRef> importName,
+                  std::optional<StringRef> importModule, uint32_t flags,
                   InputFile *file = nullptr,
                   const WasmGlobalType *type = nullptr)
       : GlobalSymbol(name, UndefinedGlobalKind, flags, file, type) {
@@ -402,8 +417,8 @@ public:
 
 class UndefinedTable : public TableSymbol {
 public:
-  UndefinedTable(StringRef name, llvm::Optional<StringRef> importName,
-                 llvm::Optional<StringRef> importModule, uint32_t flags,
+  UndefinedTable(StringRef name, std::optional<StringRef> importName,
+                 std::optional<StringRef> importModule, uint32_t flags,
                  InputFile *file, const WasmTableType *type)
       : TableSymbol(name, UndefinedTableKind, flags, file, type) {
     this->importName = importName;
@@ -430,9 +445,9 @@ public:
 // and is named '__cpp_exception' for linking.
 class TagSymbol : public Symbol {
 public:
-  static bool classof(const Symbol *s) { return s->kind() == DefinedTagKind; }
-
-  const WasmTagType *getTagType() const { return tagType; }
+  static bool classof(const Symbol *s) {
+    return s->kind() == DefinedTagKind || s->kind() == UndefinedTagKind;
+  }
 
   // Get/set the tag index
   uint32_t getTagIndex() const;
@@ -443,10 +458,9 @@ public:
 
 protected:
   TagSymbol(StringRef name, Kind k, uint32_t flags, InputFile *f,
-            const WasmTagType *tagType, const WasmSignature *sig)
-      : Symbol(name, k, flags, f), signature(sig), tagType(tagType) {}
+            const WasmSignature *sig)
+      : Symbol(name, k, flags, f), signature(sig) {}
 
-  const WasmTagType *tagType;
   uint32_t tagIndex = INVALID_INDEX;
 };
 
@@ -459,6 +473,19 @@ public:
   InputTag *tag;
 };
 
+class UndefinedTag : public TagSymbol {
+public:
+  UndefinedTag(StringRef name, std::optional<StringRef> importName,
+               std::optional<StringRef> importModule, uint32_t flags,
+               InputFile *file = nullptr, const WasmSignature *sig = nullptr)
+      : TagSymbol(name, UndefinedTagKind, flags, file, sig) {
+    this->importName = importName;
+    this->importModule = importModule;
+  }
+
+  static bool classof(const Symbol *s) { return s->kind() == UndefinedTagKind; }
+};
+
 // LazySymbol represents a symbol that is not yet in the link, but we know where
 // to find it if needed. If the resolver finds both Undefined and Lazy for the
 // same name, it will ask the Lazy to load a file.
@@ -480,7 +507,7 @@ public:
   MemoryBufferRef getMemberBuffer();
 
   // Lazy symbols can have a signature because they can replace an
-  // UndefinedFunction which which case we need to be able to preserve the
+  // UndefinedFunction in which case we need to be able to preserve the
   // signature.
   // TODO(sbc): This repetition of the signature field is inelegant.  Revisit
   // the use of class hierarchy to represent symbol taxonomy.
@@ -496,10 +523,13 @@ struct WasmSym {
   // Symbol marking the start of the global section.
   static DefinedData *globalBase;
 
-  // __stack_pointer
-  // Global that holds the address of the top of the explicit value stack in
-  // linear memory.
+  // __stack_pointer/__stack_low/__stack_high
+  // Global that holds current value of stack pointer and data symbols marking
+  // the start and end of the stack region.  stackPointer is initialized to
+  // stackHigh and grows downwards towards stackLow
   static GlobalSymbol *stackPointer;
+  static DefinedData *stackLow;
+  static DefinedData *stackHigh;
 
   // __tls_base
   // Global that holds the address of the base of the current thread's
@@ -518,11 +548,14 @@ struct WasmSym {
   // Symbol marking the end of the data and bss.
   static DefinedData *dataEnd;
 
-  // __heap_base
-  // Symbol marking the end of the data, bss and explicit stack.  Any linear
-  // memory following this address is not used by the linked code and can
-  // therefore be used as a backing store for brk()/malloc() implementations.
+  // __heap_base/__heap_end
+  // Symbols marking the beginning and end of the "heap". It starts at the end
+  // of the data, bss and explicit stack, and extends to the end of the linear
+  // memory allocated by wasm-ld. This region of memory is not used by the
+  // linked code, so it may be used as a backing store for `sbrk` or `malloc`
+  // implementations.
   static DefinedData *heapBase;
+  static DefinedData *heapEnd;
 
   // __wasm_init_memory_flag
   // Symbol whose contents are nonzero iff memory has already been initialized.
@@ -545,10 +578,15 @@ struct WasmSym {
   static DefinedFunction *applyDataRelocs;
 
   // __wasm_apply_global_relocs
-  // Function that applies relocations to data segment post-instantiation.
+  // Function that applies relocations to wasm globals post-instantiation.
   // Unlike __wasm_apply_data_relocs this needs to run on every thread.
   static DefinedFunction *applyGlobalRelocs;
 
+  // __wasm_apply_global_tls_relocs
+  // Like applyGlobalRelocs but for globals that hold TLS addresses.  These
+  // must be delayed until __wasm_init_tls.
+  static DefinedFunction *applyGlobalTLSRelocs;
+
   // __wasm_init_tls
   // Function that allocates thread-local storage and initializes it.
   static DefinedFunction *initTLS;
@@ -622,8 +660,10 @@ T *replaceSymbol(Symbol *s, ArgT &&... arg) {
   T *s2 = new (s) T(std::forward<ArgT>(arg)...);
   s2->isUsedInRegularObj = symCopy.isUsedInRegularObj;
   s2->forceExport = symCopy.forceExport;
+  s2->forceImport = symCopy.forceImport;
   s2->canInline = symCopy.canInline;
   s2->traced = symCopy.traced;
+  s2->referenced = symCopy.referenced;
 
   // Print out a log message if --trace-symbol was specified.
   // This is for debugging.
index fc37115..5808ebb 100644 (file)
@@ -17,6 +17,7 @@
 #include "OutputSegment.h"
 #include "SymbolTable.h"
 #include "llvm/Support/Path.h"
+#include <optional>
 
 using namespace llvm;
 using namespace llvm::wasm;
@@ -54,16 +55,86 @@ public:
 
 } // namespace
 
+bool DylinkSection::isNeeded() const {
+  return config->isPic ||
+         config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic ||
+         !symtab->sharedFiles.empty();
+}
+
 void DylinkSection::writeBody() {
   raw_ostream &os = bodyOutputStream;
 
-  writeUleb128(os, memSize, "MemSize");
-  writeUleb128(os, memAlign, "MemAlign");
-  writeUleb128(os, out.elemSec->numEntries(), "TableSize");
-  writeUleb128(os, 0, "TableAlign");
-  writeUleb128(os, symtab->sharedFiles.size(), "Needed");
-  for (auto *so : symtab->sharedFiles)
-    writeStr(os, llvm::sys::path::filename(so->getName()), "so name");
+  {
+    SubSection sub(WASM_DYLINK_MEM_INFO);
+    writeUleb128(sub.os, memSize, "MemSize");
+    writeUleb128(sub.os, memAlign, "MemAlign");
+    writeUleb128(sub.os, out.elemSec->numEntries(), "TableSize");
+    writeUleb128(sub.os, 0, "TableAlign");
+    sub.writeTo(os);
+  }
+
+  if (symtab->sharedFiles.size()) {
+    SubSection sub(WASM_DYLINK_NEEDED);
+    writeUleb128(sub.os, symtab->sharedFiles.size(), "Needed");
+    for (auto *so : symtab->sharedFiles)
+      writeStr(sub.os, llvm::sys::path::filename(so->getName()), "so name");
+    sub.writeTo(os);
+  }
+
+  // Under certain circumstances we need to include extra information about our
+  // exports and/or imports to the dynamic linker.
+  // For exports we need to notify the linker when an export is TLS since the
+  // exported value is relative to __tls_base rather than __memory_base.
+  // For imports we need to notify the dynamic linker when an import is weak
+  // so that knows not to report an error for such symbols.
+  std::vector<const Symbol *> importInfo;
+  std::vector<const Symbol *> exportInfo;
+  for (const Symbol *sym : symtab->symbols()) {
+    if (sym->isLive()) {
+      if (sym->isExported() && sym->isTLS() && isa<DefinedData>(sym)) {
+        exportInfo.push_back(sym);
+      }
+      if (sym->isUndefWeak()) {
+        importInfo.push_back(sym);
+      }
+    }
+  }
+
+  if (!exportInfo.empty()) {
+    SubSection sub(WASM_DYLINK_EXPORT_INFO);
+    writeUleb128(sub.os, exportInfo.size(), "num exports");
+
+    for (const Symbol *sym : exportInfo) {
+      LLVM_DEBUG(llvm::dbgs() << "export info: " << toString(*sym) << "\n");
+      StringRef name = sym->getName();
+      if (auto *f = dyn_cast<DefinedFunction>(sym)) {
+        if (std::optional<StringRef> exportName =
+                f->function->getExportName()) {
+          name = *exportName;
+        }
+      }
+      writeStr(sub.os, name, "sym name");
+      writeUleb128(sub.os, sym->flags, "sym flags");
+    }
+
+    sub.writeTo(os);
+  }
+
+  if (!importInfo.empty()) {
+    SubSection sub(WASM_DYLINK_IMPORT_INFO);
+    writeUleb128(sub.os, importInfo.size(), "num imports");
+
+    for (const Symbol *sym : importInfo) {
+      LLVM_DEBUG(llvm::dbgs() << "imports info: " << toString(*sym) << "\n");
+      StringRef module = sym->importModule.value_or(defaultModule);
+      StringRef name = sym->importName.value_or(sym->getName());
+      writeStr(sub.os, module, "import module");
+      writeStr(sub.os, name, "import name");
+      writeUleb128(sub.os, sym->flags, "sym flags");
+    }
+
+    sub.writeTo(os);
+  }
 }
 
 uint32_t TypeSection::registerType(const WasmSignature &sig) {
@@ -93,7 +164,7 @@ void TypeSection::writeBody() {
 uint32_t ImportSection::getNumImports() const {
   assert(isSealed);
   uint32_t numImports = importedSymbols.size() + gotSymbols.size();
-  if (config->importMemory)
+  if (config->memoryImport.has_value())
     ++numImports;
   return numImports;
 }
@@ -104,13 +175,19 @@ void ImportSection::addGOTEntry(Symbol *sym) {
     return;
   LLVM_DEBUG(dbgs() << "addGOTEntry: " << toString(*sym) << "\n");
   sym->setGOTIndex(numImportedGlobals++);
+  if (config->isPic) {
+    // Any symbol that is assigned an normal GOT entry must be exported
+    // otherwise the dynamic linker won't be able create the entry that contains
+    // it.
+    sym->forceExport = true;
+  }
   gotSymbols.push_back(sym);
 }
 
 void ImportSection::addImport(Symbol *sym) {
   assert(!isSealed);
-  StringRef module = sym->importModule.getValueOr(defaultModule);
-  StringRef name = sym->importName.getValueOr(sym->getName());
+  StringRef module = sym->importModule.value_or(defaultModule);
+  StringRef name = sym->importName.value_or(sym->getName());
   if (auto *f = dyn_cast<FunctionSymbol>(sym)) {
     ImportKey<WasmSignature> key(*(f->getSignature()), module, name);
     auto entry = importedFunctions.try_emplace(key, numImportedFunctions);
@@ -130,10 +207,14 @@ void ImportSection::addImport(Symbol *sym) {
       g->setGlobalIndex(entry.first->second);
     }
   } else if (auto *t = dyn_cast<TagSymbol>(sym)) {
-    // NB: There's currently only one possible kind of tag, and no
-    // `UndefinedTag`, so we don't bother de-duplicating tag imports.
-    importedSymbols.emplace_back(sym);
-    t->setTagIndex(numImportedTags++);
+    ImportKey<WasmSignature> key(*(t->getSignature()), module, name);
+    auto entry = importedTags.try_emplace(key, numImportedTags);
+    if (entry.second) {
+      importedSymbols.emplace_back(sym);
+      t->setTagIndex(numImportedTags++);
+    } else {
+      t->setTagIndex(entry.first->second);
+    }
   } else {
     assert(TableSymbol::classof(sym));
     auto *table = cast<TableSymbol>(sym);
@@ -153,12 +234,12 @@ void ImportSection::writeBody() {
 
   writeUleb128(os, getNumImports(), "import count");
 
-  bool is64 = config->is64.getValueOr(false);
+  bool is64 = config->is64.value_or(false);
 
-  if (config->importMemory) {
+  if (config->memoryImport) {
     WasmImport import;
-    import.Module = defaultModule;
-    import.Field = "memory";
+    import.Module = config->memoryImport->first;
+    import.Field = config->memoryImport->second;
     import.Kind = WASM_EXTERNAL_MEMORY;
     import.Memory.Flags = 0;
     import.Memory.Minimum = out.memorySec->numMemoryPages;
@@ -175,8 +256,8 @@ void ImportSection::writeBody() {
 
   for (const Symbol *sym : importedSymbols) {
     WasmImport import;
-    import.Field = sym->importName.getValueOr(sym->getName());
-    import.Module = sym->importModule.getValueOr(defaultModule);
+    import.Field = sym->importName.value_or(sym->getName());
+    import.Module = sym->importModule.value_or(defaultModule);
 
     if (auto *functionSym = dyn_cast<FunctionSymbol>(sym)) {
       import.Kind = WASM_EXTERNAL_FUNCTION;
@@ -186,8 +267,7 @@ void ImportSection::writeBody() {
       import.Global = *globalSym->getGlobalType();
     } else if (auto *tagSym = dyn_cast<TagSymbol>(sym)) {
       import.Kind = WASM_EXTERNAL_TAG;
-      import.Tag.Attribute = tagSym->getTagType()->Attribute;
-      import.Tag.SigIndex = out.typeSec->lookupType(*tagSym->signature);
+      import.SigIndex = out.typeSec->lookupType(*tagSym->signature);
     } else {
       auto *tableSym = cast<TableSymbol>(sym);
       import.Kind = WASM_EXTERNAL_TABLE;
@@ -279,7 +359,7 @@ void MemorySection::writeBody() {
     flags |= WASM_LIMITS_FLAG_HAS_MAX;
   if (config->sharedMemory)
     flags |= WASM_LIMITS_FLAG_IS_SHARED;
-  if (config->is64.getValueOr(false))
+  if (config->is64.value_or(false))
     flags |= WASM_LIMITS_FLAG_IS_64;
   writeUleb128(os, flags, "memory limits flags");
   writeUleb128(os, numMemoryPages, "initial pages");
@@ -292,9 +372,8 @@ void TagSection::writeBody() {
 
   writeUleb128(os, inputTags.size(), "tag count");
   for (InputTag *t : inputTags) {
-    WasmTagType type = t->getType();
-    type.SigIndex = out.typeSec->lookupType(t->signature);
-    writeTagType(os, type);
+    writeUleb128(os, 0, "tag attribute"); // Reserved "attribute" field
+    writeUleb128(os, out.typeSec->lookupType(t->signature), "sig index");
   }
 }
 
@@ -336,18 +415,26 @@ void GlobalSection::addInternalGOTEntry(Symbol *sym) {
   internalGotSymbols.push_back(sym);
 }
 
-void GlobalSection::generateRelocationCode(raw_ostream &os) const {
-  bool is64 = config->is64.getValueOr(false);
+void GlobalSection::generateRelocationCode(raw_ostream &os, bool TLS) const {
+  assert(!config->extendedConst);
+  bool is64 = config->is64.value_or(false);
   unsigned opcode_ptr_const = is64 ? WASM_OPCODE_I64_CONST
                                    : WASM_OPCODE_I32_CONST;
   unsigned opcode_ptr_add = is64 ? WASM_OPCODE_I64_ADD
                                  : WASM_OPCODE_I32_ADD;
 
   for (const Symbol *sym : internalGotSymbols) {
+    if (TLS != sym->isTLS())
+      continue;
+
     if (auto *d = dyn_cast<DefinedData>(sym)) {
       // Get __memory_base
       writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
-      writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "__memory_base");
+      if (sym->isTLS())
+        writeUleb128(os, WasmSym::tlsBase->getGlobalIndex(), "__tls_base");
+      else
+        writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(),
+                     "__memory_base");
 
       // Add the virtual address of the data symbol
       writeU8(os, opcode_ptr_const, "CONST");
@@ -380,25 +467,49 @@ void GlobalSection::writeBody() {
     writeGlobalType(os, g->getType());
     writeInitExpr(os, g->getInitExpr());
   }
-  bool is64 = config->is64.getValueOr(false);
+  bool is64 = config->is64.value_or(false);
   uint8_t itype = is64 ? WASM_TYPE_I64 : WASM_TYPE_I32;
   for (const Symbol *sym : internalGotSymbols) {
-    // In the case of dynamic linking, internal GOT entries
-    // need to be mutable since they get updated to the correct
-    // runtime value during `__wasm_apply_global_relocs`.
-    bool mutable_ = config->isPic & !sym->isStub;
-    WasmGlobalType type{itype, mutable_};
-    WasmInitExpr initExpr;
-    if (auto *d = dyn_cast<DefinedData>(sym))
-      initExpr = intConst(d->getVA(), is64);
-    else if (auto *f = dyn_cast<FunctionSymbol>(sym))
-      initExpr = intConst(f->isStub ? 0 : f->getTableIndex(), is64);
-    else {
-      assert(isa<UndefinedData>(sym));
-      initExpr = intConst(0, is64);
+    bool mutable_ = false;
+    if (!sym->isStub) {
+      // In the case of dynamic linking, unless we have 'extended-const'
+      // available, these global must to be mutable since they get updated to
+      // the correct runtime value during `__wasm_apply_global_relocs`.
+      if (!config->extendedConst && config->isPic && !sym->isTLS())
+        mutable_ = true;
+      // With multi-theadeding any TLS globals must be mutable since they get
+      // set during `__wasm_apply_global_tls_relocs`
+      if (config->sharedMemory && sym->isTLS())
+        mutable_ = true;
     }
+    WasmGlobalType type{itype, mutable_};
     writeGlobalType(os, type);
-    writeInitExpr(os, initExpr);
+
+    if (config->extendedConst && config->isPic && !sym->isTLS() &&
+        isa<DefinedData>(sym)) {
+      // We can use an extended init expression to add a constant
+      // offset of __memory_base.
+      auto *d = cast<DefinedData>(sym);
+      writeU8(os, WASM_OPCODE_GLOBAL_GET, "global get");
+      writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(),
+                   "literal (global index)");
+      if (d->getVA()) {
+        writePtrConst(os, d->getVA(), is64, "offset");
+        writeU8(os, is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD, "add");
+      }
+      writeU8(os, WASM_OPCODE_END, "opcode:end");
+    } else {
+      WasmInitExpr initExpr;
+      if (auto *d = dyn_cast<DefinedData>(sym))
+        initExpr = intConst(d->getVA(), is64);
+      else if (auto *f = dyn_cast<FunctionSymbol>(sym))
+        initExpr = intConst(f->isStub ? 0 : f->getTableIndex(), is64);
+      else {
+        assert(isa<UndefinedData>(sym));
+        initExpr = intConst(0, is64);
+      }
+      writeInitExpr(os, initExpr);
+    }
   }
   for (const DefinedData *sym : dataAddressGlobals) {
     WasmGlobalType type{itype, false};
@@ -456,15 +567,16 @@ void ElemSection::writeBody() {
     writeUleb128(os, tableNumber, "table number");
 
   WasmInitExpr initExpr;
+  initExpr.Extended = false;
   if (config->isPic) {
-    initExpr.Opcode = WASM_OPCODE_GLOBAL_GET;
-    initExpr.Value.Global =
-        (config->is64.getValueOr(false) ? WasmSym::tableBase32
-                                        : WasmSym::tableBase)
+    initExpr.Inst.Opcode = WASM_OPCODE_GLOBAL_GET;
+    initExpr.Inst.Value.Global =
+        (config->is64.value_or(false) ? WasmSym::tableBase32
+                                      : WasmSym::tableBase)
             ->getGlobalIndex();
   } else {
-    initExpr.Opcode = WASM_OPCODE_I32_CONST;
-    initExpr.Value.Int32 = config->tableBase;
+    initExpr.Inst.Opcode = WASM_OPCODE_I32_CONST;
+    initExpr.Inst.Value.Int32 = config->tableBase;
   }
   writeInitExpr(os, initExpr);
 
@@ -479,6 +591,7 @@ void ElemSection::writeBody() {
   uint32_t tableIndex = config->tableBase;
   for (const FunctionSymbol *sym : indirectFunctions) {
     assert(sym->getTableIndex() == tableIndex);
+    (void) tableIndex;
     writeUleb128(os, sym->getFunctionIndex(), "function index");
     ++tableIndex;
   }
@@ -486,9 +599,9 @@ void ElemSection::writeBody() {
 
 DataCountSection::DataCountSection(ArrayRef<OutputSegment *> segments)
     : SyntheticSection(llvm::wasm::WASM_SEC_DATACOUNT),
-      numSegments(std::count_if(
-          segments.begin(), segments.end(),
-          [](OutputSegment *const segment) { return !segment->isBss; })) {}
+      numSegments(llvm::count_if(segments, [](OutputSegment *const segment) {
+        return segment->requiredInBinary();
+      })) {}
 
 void DataCountSection::writeBody() {
   writeUleb128(bodyOutputStream, numSegments, "data count");
@@ -516,7 +629,11 @@ void LinkingSection::writeBody() {
       writeUleb128(sub.os, flags, "sym flags");
 
       if (auto *f = dyn_cast<FunctionSymbol>(sym)) {
-        writeUleb128(sub.os, f->getFunctionIndex(), "index");
+        if (auto *d = dyn_cast<DefinedFunction>(sym)) {
+          writeUleb128(sub.os, d->getExportedFunctionIndex(), "index");
+        } else {
+          writeUleb128(sub.os, f->getFunctionIndex(), "index");
+        }
         if (sym->isDefined() || (flags & WASM_SYMBOL_EXPLICIT_NAME) != 0)
           writeStr(sub.os, sym->getName(), "sym name");
       } else if (auto *g = dyn_cast<GlobalSymbol>(sym)) {
@@ -619,7 +736,7 @@ unsigned NameSection::numNamedFunctions() const {
   unsigned numNames = out.importSec->getNumImportedFunctions();
 
   for (const InputFunction *f : out.functionSec->inputFunctions)
-    if (!f->getName().empty() || !f->getDebugName().empty())
+    if (!f->name.empty() || !f->debugName.empty())
       ++numNames;
 
   return numNames;
@@ -640,7 +757,7 @@ unsigned NameSection::numNamedDataSegments() const {
   unsigned numNames = 0;
 
   for (const OutputSegment *s : segments)
-    if (!s->name.empty() && !s->isBss)
+    if (!s->name.empty() && s->requiredInBinary())
       ++numNames;
 
   return numNames;
@@ -663,12 +780,12 @@ void NameSection::writeBody() {
       }
     }
     for (const InputFunction *f : out.functionSec->inputFunctions) {
-      if (!f->getName().empty()) {
+      if (!f->name.empty()) {
         writeUleb128(sub.os, f->getFunctionIndex(), "func index");
-        if (!f->getDebugName().empty()) {
-          writeStr(sub.os, f->getDebugName(), "symbol name");
+        if (!f->debugName.empty()) {
+          writeStr(sub.os, f->debugName, "symbol name");
         } else {
-          writeStr(sub.os, maybeDemangleSymbol(f->getName()), "symbol name");
+          writeStr(sub.os, maybeDemangleSymbol(f->name), "symbol name");
         }
       }
     }
@@ -713,7 +830,7 @@ void NameSection::writeBody() {
     writeUleb128(sub.os, count, "name count");
 
     for (OutputSegment *s : segments) {
-      if (!s->name.empty() && !s->isBss) {
+      if (!s->name.empty() && s->requiredInBinary()) {
         writeUleb128(sub.os, s->index, "global index");
         writeStr(sub.os, s->name, "segment name");
       }
@@ -728,8 +845,7 @@ void ProducersSection::addInfo(const WasmProducerInfo &info) {
        {std::make_pair(&info.Languages, &languages),
         std::make_pair(&info.Tools, &tools), std::make_pair(&info.SDKs, &sDKs)})
     for (auto &producer : *producers.first)
-      if (producers.second->end() ==
-          llvm::find_if(*producers.second,
+      if (llvm::none_of(*producers.second,
                         [&](std::pair<std::string, std::string> seen) {
                           return seen.first == producer.first;
                         }))
index 12517b6..bda3f8e 100644 (file)
@@ -20,6 +20,7 @@
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/BinaryFormat/WasmTraits.h"
+#include <optional>
 
 #define DEBUG_TYPE "lld"
 
@@ -71,11 +72,11 @@ protected:
 // Create the custom "dylink" section containing information for the dynamic
 // linker.
 // See
-// https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
+// https://github.com/WebAssembly/tool-conventions/blob/main/DynamicLinking.md
 class DylinkSection : public SyntheticSection {
 public:
-  DylinkSection() : SyntheticSection(llvm::wasm::WASM_SEC_CUSTOM, "dylink") {}
-  bool isNeeded() const override { return config->isPic; }
+  DylinkSection() : SyntheticSection(llvm::wasm::WASM_SEC_CUSTOM, "dylink.0") {}
+  bool isNeeded() const override;
   void writeBody() override;
 
   uint32_t memAlign = 0;
@@ -107,15 +108,15 @@ public:
 
 public:
   T type;
-  llvm::Optional<StringRef> importModule;
-  llvm::Optional<StringRef> importName;
+  std::optional<StringRef> importModule;
+  std::optional<StringRef> importName;
   State state;
 
 public:
   ImportKey(T type) : type(type), state(State::Plain) {}
   ImportKey(T type, State state) : type(type), state(state) {}
-  ImportKey(T type, llvm::Optional<StringRef> importModule,
-            llvm::Optional<StringRef> importName)
+  ImportKey(T type, std::optional<StringRef> importModule,
+            std::optional<StringRef> importName)
       : type(type), importModule(importModule), importName(importName),
         state(State::Plain) {}
 };
@@ -198,6 +199,7 @@ protected:
   llvm::DenseMap<ImportKey<WasmGlobalType>, uint32_t> importedGlobals;
   llvm::DenseMap<ImportKey<WasmSignature>, uint32_t> importedFunctions;
   llvm::DenseMap<ImportKey<WasmTableType>, uint32_t> importedTables;
+  llvm::DenseMap<ImportKey<WasmSignature>, uint32_t> importedTags;
 };
 
 class FunctionSection : public SyntheticSection {
@@ -229,7 +231,7 @@ class MemorySection : public SyntheticSection {
 public:
   MemorySection() : SyntheticSection(llvm::wasm::WASM_SEC_MEMORY) {}
 
-  bool isNeeded() const override { return !config->importMemory; }
+  bool isNeeded() const override { return !config->memoryImport.has_value(); }
   void writeBody() override;
 
   uint64_t numMemoryPages = 0;
@@ -286,10 +288,19 @@ public:
   // specific relocation types combined with linker relaxation which could
   // transform a `global.get` to an `i32.const`.
   void addInternalGOTEntry(Symbol *sym);
-  bool needsRelocations() { return internalGotSymbols.size(); }
-  void generateRelocationCode(raw_ostream &os) const;
+  bool needsRelocations() {
+    if (config->extendedConst)
+      return false;
+    return llvm::any_of(internalGotSymbols,
+                        [=](Symbol *sym) { return !sym->isTLS(); });
+  }
+  bool needsTLSRelocations() {
+    return llvm::any_of(internalGotSymbols,
+                        [=](Symbol *sym) { return sym->isTLS(); });
+  }
+  void generateRelocationCode(raw_ostream &os, bool TLS) const;
 
-  std::vector<const DefinedData *> dataAddressGlobals;
+  std::vector<DefinedData *> dataAddressGlobals;
   std::vector<InputGlobal *> inputGlobals;
   std::vector<Symbol *> internalGotSymbols;
 
index cf9356b..304897b 100644 (file)
@@ -17,8 +17,7 @@
 #include "SymbolTable.h"
 #include "SyntheticSections.h"
 #include "WriterUtils.h"
-#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Memory.h"
+#include "lld/Common/CommonLinkerContext.h"
 #include "lld/Common/Strings.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/SmallSet.h"
@@ -34,6 +33,7 @@
 
 #include <cstdarg>
 #include <map>
+#include <optional>
 
 #define DEBUG_TYPE "lld"
 
@@ -63,6 +63,7 @@ private:
   void createStartFunction();
   void createApplyDataRelocationsFunction();
   void createApplyGlobalRelocationsFunction();
+  void createApplyGlobalTLSRelocationsFunction();
   void createCallCtorsFunction();
   void createInitTLSFunction();
   void createCommandExportWrappers();
@@ -72,6 +73,10 @@ private:
   void populateSymtab();
   void populateProducers();
   void populateTargetFeatures();
+  // populateTargetFeatures happens early on so some checks are delayed
+  // until imports and exports are finalized.  There are run unstead
+  // in checkImportExportTargetFeatures
+  void checkImportExportTargetFeatures();
   void calculateInitFunctions();
   void calculateImports();
   void calculateExports();
@@ -126,7 +131,7 @@ void Writer::calculateCustomSections() {
       // Exclude COMDAT sections that are not selected for inclusion
       if (section->discarded)
         continue;
-      StringRef name = section->getName();
+      StringRef name = section->name;
       // These custom sections are known the linker and synthesized rather than
       // blindly copied.
       if (name == "linking" || name == "name" || name == "producers" ||
@@ -185,7 +190,7 @@ void Writer::createRelocSections() {
     else if (sec->type == WASM_SEC_CODE)
       name = "reloc.CODE";
     else if (sec->type == WASM_SEC_CUSTOM)
-      name = saver.save("reloc." + sec->name);
+      name = saver().save("reloc." + sec->name);
     else
       llvm_unreachable(
           "relocations only supported for code, data, or custom sections");
@@ -214,6 +219,7 @@ void Writer::writeSections() {
 }
 
 static void setGlobalPtr(DefinedGlobal *g, uint64_t memoryPtr) {
+  LLVM_DEBUG(dbgs() << "setGlobalPtr " << g->getName() << " -> " << memoryPtr << "\n");
   g->global->setPointerValue(memoryPtr);
 }
 
@@ -221,9 +227,9 @@ static void setGlobalPtr(DefinedGlobal *g, uint64_t memoryPtr) {
 // to each of the input data sections as well as the explicit stack region.
 // The default memory layout is as follows, from low to high.
 //
-//  - initialized data (starting at Config->globalBase)
+//  - initialized data (starting at config->globalBase)
 //  - BSS data (not currently implemented in llvm)
-//  - explicit stack (Config->ZStackSize)
+//  - explicit stack (config->ZStackSize)
 //  - heap start / unallocated
 //
 // The --stack-first option means that stack is placed before any static data.
@@ -237,22 +243,40 @@ void Writer::layoutMemory() {
     if (config->relocatable || config->isPic)
       return;
     memoryPtr = alignTo(memoryPtr, stackAlignment);
+    if (WasmSym::stackLow)
+      WasmSym::stackLow->setVA(memoryPtr);
     if (config->zStackSize != alignTo(config->zStackSize, stackAlignment))
       error("stack size must be " + Twine(stackAlignment) + "-byte aligned");
     log("mem: stack size  = " + Twine(config->zStackSize));
     log("mem: stack base  = " + Twine(memoryPtr));
     memoryPtr += config->zStackSize;
     setGlobalPtr(cast<DefinedGlobal>(WasmSym::stackPointer), memoryPtr);
+    if (WasmSym::stackHigh)
+      WasmSym::stackHigh->setVA(memoryPtr);
     log("mem: stack top   = " + Twine(memoryPtr));
   };
 
   if (config->stackFirst) {
     placeStack();
+    if (config->globalBase) {
+      if (config->globalBase < memoryPtr) {
+        error("--global-base cannot be less than stack size when --stack-first is used");
+        return;
+      }
+      memoryPtr = config->globalBase;
+    }
   } else {
+    if (!config->globalBase && !config->relocatable && !config->isPic) {
+      // The default offset for static/global data, for when --global-base is
+      // not specified on the command line.  The precise value of 1024 is
+      // somewhat arbitrary, and pre-dates wasm-ld (Its the value that
+      // emscripten used prior to wasm-ld).
+      config->globalBase = 1024;
+    }
     memoryPtr = config->globalBase;
-    log("mem: global base = " + Twine(config->globalBase));
   }
 
+  log("mem: global base = " + Twine(memoryPtr));
   if (WasmSym::globalBase)
     WasmSym::globalBase->setVA(memoryPtr);
 
@@ -272,13 +296,15 @@ void Writer::layoutMemory() {
                 memoryPtr, seg->size, seg->alignment));
 
     if (!config->relocatable && seg->isTLS()) {
-      if (config->sharedMemory) {
+      if (WasmSym::tlsSize) {
         auto *tlsSize = cast<DefinedGlobal>(WasmSym::tlsSize);
         setGlobalPtr(tlsSize, seg->size);
-
+      }
+      if (WasmSym::tlsAlign) {
         auto *tlsAlign = cast<DefinedGlobal>(WasmSym::tlsAlign);
         setGlobalPtr(tlsAlign, int64_t{1} << seg->alignment);
-      } else {
+      }
+      if (!config->sharedMemory && WasmSym::tlsBase) {
         auto *tlsBase = cast<DefinedGlobal>(WasmSym::tlsBase);
         setGlobalPtr(tlsBase, memoryPtr);
       }
@@ -321,8 +347,7 @@ void Writer::layoutMemory() {
     WasmSym::heapBase->setVA(memoryPtr);
   }
 
-  uint64_t maxMemorySetting = 1ULL
-                              << (config->is64.getValueOr(false) ? 48 : 32);
+  uint64_t maxMemorySetting = 1ULL << (config->is64.value_or(false) ? 48 : 32);
 
   if (config->initialMemory != 0) {
     if (config->initialMemory != alignTo(config->initialMemory, WasmPageSize))
@@ -334,10 +359,20 @@ void Writer::layoutMemory() {
             Twine(maxMemorySetting));
     memoryPtr = config->initialMemory;
   }
-  out.memorySec->numMemoryPages =
-      alignTo(memoryPtr, WasmPageSize) / WasmPageSize;
+
+  memoryPtr = alignTo(memoryPtr, WasmPageSize);
+
+  out.memorySec->numMemoryPages = memoryPtr / WasmPageSize;
   log("mem: total pages = " + Twine(out.memorySec->numMemoryPages));
 
+  if (WasmSym::heapEnd) {
+    // Set `__heap_end` to follow the end of the statically allocated linear
+    // memory. The fact that this comes last means that a malloc/brk
+    // implementation can grow the heap at runtime.
+    log("mem: heap end    = " + Twine(memoryPtr));
+    WasmSym::heapEnd->setVA(memoryPtr);
+  }
+
   if (config->maxMemory != 0) {
     if (config->maxMemory != alignTo(config->maxMemory, WasmPageSize))
       error("maximum memory must be " + Twine(WasmPageSize) + "-byte aligned");
@@ -357,7 +392,7 @@ void Writer::layoutMemory() {
       if (config->isPic)
         max = maxMemorySetting;
       else
-        max = alignTo(memoryPtr, WasmPageSize);
+        max = memoryPtr;
     }
     out.memorySec->maxMemoryPages = max / WasmPageSize;
     log("mem: max pages   = " + Twine(out.memorySec->maxMemoryPages));
@@ -384,8 +419,8 @@ static void addStartStopSymbols(const OutputSegment *seg) {
   LLVM_DEBUG(dbgs() << "addStartStopSymbols: " << name << "\n");
   uint64_t start = seg->startVA;
   uint64_t stop = start + seg->size;
-  symtab->addOptionalDataSymbol(saver.save("__start_" + name), start);
-  symtab->addOptionalDataSymbol(saver.save("__stop_" + name), stop);
+  symtab->addOptionalDataSymbol(saver().save("__start_" + name), start);
+  symtab->addOptionalDataSymbol(saver().save("__stop_" + name), stop);
 }
 
 void Writer::addSections() {
@@ -432,14 +467,26 @@ void Writer::populateTargetFeatures() {
   SmallSet<std::string, 8> &allowed = out.targetFeaturesSec->features;
   bool tlsUsed = false;
 
+  if (config->isPic) {
+    // This should not be necessary because all PIC objects should
+    // contain the mutable-globals feature.
+    // TODO(https://bugs.llvm.org/show_bug.cgi?id=52339)
+    allowed.insert("mutable-globals");
+  }
+
+  if (config->extraFeatures.has_value()) {
+    auto &extraFeatures = *config->extraFeatures;
+    allowed.insert(extraFeatures.begin(), extraFeatures.end());
+  }
+
   // Only infer used features if user did not specify features
-  bool inferFeatures = !config->features.hasValue();
+  bool inferFeatures = !config->features.has_value();
 
   if (!inferFeatures) {
-    auto &explicitFeatures = config->features.getValue();
+    auto &explicitFeatures = *config->features;
     allowed.insert(explicitFeatures.begin(), explicitFeatures.end());
     if (!config->checkFeatures)
-      return;
+      goto done;
   }
 
   // Find the sets of used, required, and disallowed features
@@ -467,8 +514,7 @@ void Writer::populateTargetFeatures() {
     auto isTLS = [](InputChunk *segment) {
       return segment->live && segment->isTLS();
     };
-    tlsUsed = tlsUsed ||
-              std::any_of(file->segments.begin(), file->segments.end(), isTLS);
+    tlsUsed = tlsUsed || llvm::any_of(file->segments, isTLS);
   }
 
   if (inferFeatures)
@@ -476,26 +522,7 @@ void Writer::populateTargetFeatures() {
       allowed.insert(std::string(key));
 
   if (!config->checkFeatures)
-    return;
-
-  if (!config->relocatable && allowed.count("mutable-globals") == 0) {
-    for (const Symbol *sym : out.importSec->importedSymbols) {
-      if (auto *global = dyn_cast<GlobalSymbol>(sym)) {
-        if (global->getGlobalType()->Mutable) {
-          error(Twine("mutable global imported but 'mutable-globals' feature "
-                      "not present in inputs: `") +
-                toString(*sym) + "`. Use --no-check-features to suppress.");
-        }
-      }
-    }
-    for (const Symbol *sym : out.exportSec->exportedSymbols) {
-      if (isa<GlobalSymbol>(sym)) {
-        error(Twine("mutable global exported but 'mutable-globals' feature "
-                    "not present in inputs: `") +
-              toString(*sym) + "`. Use --no-check-features to suppress.");
-      }
-    }
-  }
+    goto done;
 
   if (config->sharedMemory) {
     if (disallowed.count("shared-mem"))
@@ -518,7 +545,7 @@ void Writer::populateTargetFeatures() {
 
   // Validate that used features are allowed in output
   if (!inferFeatures) {
-    for (auto &feature : used.keys()) {
+    for (const auto &feature : used.keys()) {
       if (!allowed.count(std::string(feature)))
         error(Twine("Target feature '") + feature + "' used by " +
               used[feature] + " is not allowed.");
@@ -529,7 +556,7 @@ void Writer::populateTargetFeatures() {
   for (ObjFile *file : symtab->objectFiles) {
     StringRef fileName(file->getName());
     SmallSet<std::string, 8> objectFeatures;
-    for (auto &feature : file->getWasmObj()->getTargetFeatures()) {
+    for (const auto &feature : file->getWasmObj()->getTargetFeatures()) {
       if (feature.Prefix == WASM_FEATURE_PREFIX_DISALLOWED)
         continue;
       objectFeatures.insert(feature.Name);
@@ -538,36 +565,89 @@ void Writer::populateTargetFeatures() {
               fileName + " is disallowed by " + disallowed[feature.Name] +
               ". Use --no-check-features to suppress.");
     }
-    for (auto &feature : required.keys()) {
+    for (const auto &feature : required.keys()) {
       if (!objectFeatures.count(std::string(feature)))
         error(Twine("Missing target feature '") + feature + "' in " + fileName +
               ", required by " + required[feature] +
               ". Use --no-check-features to suppress.");
     }
   }
+
+done:
+  // Normally we don't include bss segments in the binary.  In particular if
+  // memory is not being imported then we can assume its zero initialized.
+  // In the case the memory is imported, and we can use the memory.fill
+  // instruction, then we can also avoid including the segments.
+  if (config->memoryImport.has_value() && !allowed.count("bulk-memory"))
+    config->emitBssSegments = true;
+
+  if (allowed.count("extended-const"))
+    config->extendedConst = true;
+
+  for (auto &feature : allowed)
+    log("Allowed feature: " + feature);
+}
+
+void Writer::checkImportExportTargetFeatures() {
+  if (config->relocatable || !config->checkFeatures)
+    return;
+
+  if (out.targetFeaturesSec->features.count("mutable-globals") == 0) {
+    for (const Symbol *sym : out.importSec->importedSymbols) {
+      if (auto *global = dyn_cast<GlobalSymbol>(sym)) {
+        if (global->getGlobalType()->Mutable) {
+          error(Twine("mutable global imported but 'mutable-globals' feature "
+                      "not present in inputs: `") +
+                toString(*sym) + "`. Use --no-check-features to suppress.");
+        }
+      }
+    }
+    for (const Symbol *sym : out.exportSec->exportedSymbols) {
+      if (isa<GlobalSymbol>(sym)) {
+        error(Twine("mutable global exported but 'mutable-globals' feature "
+                    "not present in inputs: `") +
+              toString(*sym) + "`. Use --no-check-features to suppress.");
+      }
+    }
+  }
 }
 
 static bool shouldImport(Symbol *sym) {
-  if (!sym->isUndefined())
-    return false;
-  if (sym->isWeak() && !config->relocatable && !config->isPic)
+  // We don't generate imports for data symbols. They however can be imported
+  // as GOT entries.
+  if (isa<DataSymbol>(sym))
     return false;
   if (!sym->isLive())
     return false;
   if (!sym->isUsedInRegularObj)
     return false;
 
-  // We don't generate imports for data symbols. They however can be imported
-  // as GOT entries.
-  if (isa<DataSymbol>(sym))
+  // When a symbol is weakly defined in a shared library we need to allow
+  // it to be overridden by another module so need to both import
+  // and export the symbol.
+  if (config->shared && sym->isWeak() && !sym->isUndefined() &&
+      !sym->isHidden())
+    return true;
+  if (!sym->isUndefined())
     return false;
+  if (sym->isWeak() && !config->relocatable && !config->isPic)
+    return false;
+
+  // In PIC mode we only need to import functions when they are called directly.
+  // Indirect usage all goes via GOT imports.
+  if (config->isPic) {
+    if (auto *f = dyn_cast<UndefinedFunction>(sym))
+      if (!f->isCalledDirectly)
+        return false;
+  }
 
-  if (config->isPic || config->relocatable || config->importUndefined)
+  if (config->isPic || config->relocatable || config->importUndefined ||
+      config->unresolvedSymbols == UnresolvedPolicy::ImportDynamic)
     return true;
   if (config->allowUndefinedSymbols.count(sym->getName()) != 0)
     return true;
 
-  return sym->importName.hasValue();
+  return sym->isImported();
 }
 
 void Writer::calculateImports() {
@@ -578,7 +658,7 @@ void Writer::calculateImports() {
       shouldImport(WasmSym::indirectFunctionTable))
     out.importSec->addImport(WasmSym::indirectFunctionTable);
 
-  for (Symbol *sym : symtab->getSymbols()) {
+  for (Symbol *sym : symtab->symbols()) {
     if (!shouldImport(sym))
       continue;
     if (sym == WasmSym::indirectFunctionTable)
@@ -592,14 +672,15 @@ void Writer::calculateExports() {
   if (config->relocatable)
     return;
 
-  if (!config->relocatable && !config->importMemory)
+  if (!config->relocatable && config->memoryExport.has_value()) {
     out.exportSec->exports.push_back(
-        WasmExport{"memory", WASM_EXTERNAL_MEMORY, 0});
+        WasmExport{*config->memoryExport, WASM_EXTERNAL_MEMORY, 0});
+  }
 
   unsigned globalIndex =
       out.importSec->getNumImportedGlobals() + out.globalSec->numGlobals();
 
-  for (Symbol *sym : symtab->getSymbols()) {
+  for (Symbol *sym : symtab->symbols()) {
     if (!sym->isExported())
       continue;
     if (!sym->isLive())
@@ -608,10 +689,10 @@ void Writer::calculateExports() {
     StringRef name = sym->getName();
     WasmExport export_;
     if (auto *f = dyn_cast<DefinedFunction>(sym)) {
-      if (Optional<StringRef> exportName = f->function->getExportName()) {
+      if (std::optional<StringRef> exportName = f->function->getExportName()) {
         name = *exportName;
       }
-      export_ = {name, WASM_EXTERNAL_FUNCTION, f->getFunctionIndex()};
+      export_ = {name, WASM_EXTERNAL_FUNCTION, f->getExportedFunctionIndex()};
     } else if (auto *g = dyn_cast<DefinedGlobal>(sym)) {
       if (g->getGlobalType()->Mutable && !g->getFile() && !g->forceExport) {
         // Avoid exporting mutable globals are linker synthesized (e.g.
@@ -626,12 +707,6 @@ void Writer::calculateExports() {
     } else if (auto *t = dyn_cast<DefinedTag>(sym)) {
       export_ = {name, WASM_EXTERNAL_TAG, t->getTagIndex()};
     } else if (auto *d = dyn_cast<DefinedData>(sym)) {
-      if (d->segment && d->segment->isTLS()) {
-        // We can't currenly export TLS data symbols.
-        if (sym->isExportedExplicit())
-          error("TLS symbols cannot yet be exported: `" + toString(*sym) + "`");
-        continue;
-      }
       out.globalSec->dataAddressGlobals.push_back(d);
       export_ = {name, WASM_EXTERNAL_GLOBAL, globalIndex++};
     } else {
@@ -649,7 +724,7 @@ void Writer::populateSymtab() {
   if (!config->relocatable && !config->emitRelocs)
     return;
 
-  for (Symbol *sym : symtab->getSymbols())
+  for (Symbol *sym : symtab->symbols())
     if (sym->isUsedInRegularObj && sym->isLive())
       out.linkingSec->addToSymtab(sym);
 
@@ -698,12 +773,12 @@ void Writer::createCommandExportWrappers() {
 
   // If there are no ctors and there's no libc `__wasm_call_dtors` to
   // call, don't wrap the exports.
-  if (initFunctions.empty() && WasmSym::callDtors == NULL)
+  if (initFunctions.empty() && WasmSym::callDtors == nullptr)
     return;
 
   std::vector<DefinedFunction *> toWrap;
 
-  for (Symbol *sym : symtab->getSymbols())
+  for (Symbol *sym : symtab->symbols())
     if (sym->isExported())
       if (auto *f = dyn_cast<DefinedFunction>(sym))
         toWrap.push_back(f);
@@ -714,7 +789,7 @@ void Writer::createCommandExportWrappers() {
     const std::string &funcName = commandExportWrapperNames.back();
 
     auto func = make<SyntheticFunction>(*f->getSignature(), funcName);
-    if (f->function->getExportName().hasValue())
+    if (f->function->getExportName())
       func->setExportName(f->function->getExportName()->str());
     else
       func->setExportName(f->getName().str());
@@ -818,18 +893,17 @@ static StringRef getOutputDataSegmentName(const InputChunk &seg) {
   // symbols are be relative to single __tls_base.
   if (seg.isTLS())
     return ".tdata";
-  StringRef name = seg.getName();
   if (!config->mergeDataSegments)
-    return name;
-  if (name.startswith(".text."))
+    return seg.name;
+  if (seg.name.startswith(".text."))
     return ".text";
-  if (name.startswith(".data."))
+  if (seg.name.startswith(".data."))
     return ".data";
-  if (name.startswith(".bss."))
+  if (seg.name.startswith(".bss."))
     return ".bss";
-  if (name.startswith(".rodata."))
+  if (seg.name.startswith(".rodata."))
     return ".rodata";
-  return name;
+  return seg.name;
 }
 
 OutputSegment *Writer::createOutputSegment(StringRef name) {
@@ -837,11 +911,7 @@ OutputSegment *Writer::createOutputSegment(StringRef name) {
   OutputSegment *s = make<OutputSegment>(name);
   if (config->sharedMemory)
     s->initFlags = WASM_DATA_SEGMENT_IS_PASSIVE;
-  // Exported memories are guaranteed to be zero-initialized, so no need
-  // to emit data segments for bss sections.
-  // TODO: consider initializing bss sections with memory.fill
-  // instructions when memory is imported and bulk-memory is available.
-  if (!config->importMemory && !config->relocatable && name.startswith(".bss"))
+  if (!config->relocatable && name.startswith(".bss"))
     s->isBss = true;
   segments.push_back(s);
   return s;
@@ -895,9 +965,9 @@ void Writer::combineOutputSegments() {
   // With PIC code we currently only support a single active data segment since
   // we only have a single __memory_base to use as our base address.  This pass
   // combines all data segments into a single .data segment.
-  // This restructions can be relaxed once we have extended constant
-  // expressions available:
-  // https://github.com/WebAssembly/extended-const
+  // This restriction does not apply when the extended const extension is
+  // available: https://github.com/WebAssembly/extended-const
+  assert(!config->extendedConst);
   assert(config->isPic && !config->sharedMemory);
   if (segments.size() <= 1)
     return;
@@ -915,7 +985,7 @@ void Writer::combineOutputSegments() {
       combined->addInputSegment(inSeg);
 #ifndef NDEBUG
       uint64_t newVA = inSeg->getVA();
-      LLVM_DEBUG(dbgs() << "added input segment. name=" << inSeg->getName()
+      LLVM_DEBUG(dbgs() << "added input segment. name=" << inSeg->name
                         << " oldVA=" << oldVA << " newVA=" << newVA << "\n");
       assert(oldVA == newVA);
 #endif
@@ -932,20 +1002,22 @@ static void createFunction(DefinedFunction *func, StringRef bodyContent) {
     writeUleb128(os, bodyContent.size(), "function size");
     os << bodyContent;
   }
-  ArrayRef<uint8_t> body = arrayRefFromStringRef(saver.save(functionBody));
+  ArrayRef<uint8_t> body = arrayRefFromStringRef(saver().save(functionBody));
   cast<SyntheticFunction>(func->function)->setBody(body);
 }
 
 bool Writer::needsPassiveInitialization(const OutputSegment *segment) {
-  return segment->initFlags & WASM_DATA_SEGMENT_IS_PASSIVE &&
-         !segment->isTLS() && !segment->isBss;
+  // If bulk memory features is supported then we can perform bss initialization
+  // (via memory.fill) during `__wasm_init_memory`.
+  if (config->memoryImport.has_value() && !segment->requiredInBinary())
+    return true;
+  return segment->initFlags & WASM_DATA_SEGMENT_IS_PASSIVE;
 }
 
 bool Writer::hasPassiveInitializedSegments() {
-  return std::find_if(segments.begin(), segments.end(),
-                      [this](const OutputSegment *s) {
-                        return this->needsPassiveInitialization(s);
-                      }) != segments.end();
+  return llvm::any_of(segments, [this](const OutputSegment *s) {
+    return this->needsPassiveInitialization(s);
+  });
 }
 
 void Writer::createSyntheticInitFunctions() {
@@ -957,30 +1029,40 @@ void Writer::createSyntheticInitFunctions() {
   // Passive segments are used to avoid memory being reinitialized on each
   // thread's instantiation. These passive segments are initialized and
   // dropped in __wasm_init_memory, which is registered as the start function
-  if (config->sharedMemory && hasPassiveInitializedSegments()) {
+  // We also initialize bss segments (using memory.fill) as part of this
+  // function.
+  if (hasPassiveInitializedSegments()) {
     WasmSym::initMemory = symtab->addSyntheticFunction(
         "__wasm_init_memory", WASM_SYMBOL_VISIBILITY_HIDDEN,
         make<SyntheticFunction>(nullSignature, "__wasm_init_memory"));
     WasmSym::initMemory->markLive();
+    if (config->sharedMemory) {
+      // This global is assigned during  __wasm_init_memory in the shared memory
+      // case.
+      WasmSym::tlsBase->markLive();
+    }
   }
 
-  if (config->isPic) {
-    // For PIC code we create synthetic functions that apply relocations.
-    // These get called from __wasm_call_ctors before the user-level
-    // constructors.
-    WasmSym::applyDataRelocs = symtab->addSyntheticFunction(
-        "__wasm_apply_data_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
-        make<SyntheticFunction>(nullSignature, "__wasm_apply_data_relocs"));
-    WasmSym::applyDataRelocs->markLive();
-
-    if (out.globalSec->needsRelocations()) {
-      WasmSym::applyGlobalRelocs = symtab->addSyntheticFunction(
-          "__wasm_apply_global_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
-          make<SyntheticFunction>(nullSignature, "__wasm_apply_global_relocs"));
-      WasmSym::applyGlobalRelocs->markLive();
-    }
+  if (config->sharedMemory && out.globalSec->needsTLSRelocations()) {
+    WasmSym::applyGlobalTLSRelocs = symtab->addSyntheticFunction(
+        "__wasm_apply_global_tls_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
+        make<SyntheticFunction>(nullSignature,
+                                "__wasm_apply_global_tls_relocs"));
+    WasmSym::applyGlobalTLSRelocs->markLive();
+    // TLS relocations depend on  the __tls_base symbols
+    WasmSym::tlsBase->markLive();
   }
 
+  if (config->isPic && out.globalSec->needsRelocations()) {
+    WasmSym::applyGlobalRelocs = symtab->addSyntheticFunction(
+        "__wasm_apply_global_relocs", WASM_SYMBOL_VISIBILITY_HIDDEN,
+        make<SyntheticFunction>(nullSignature, "__wasm_apply_global_relocs"));
+    WasmSym::applyGlobalRelocs->markLive();
+  }
+
+  // If there is only one start function we can just use that function
+  // itself as the Wasm start function, otherwise we need to synthesize
+  // a new function to call them in sequence.
   if (WasmSym::applyGlobalRelocs && WasmSym::initMemory) {
     WasmSym::startFunction = symtab->addSyntheticFunction(
         "__wasm_start", WASM_SYMBOL_VISIBILITY_HIDDEN,
@@ -992,39 +1074,37 @@ void Writer::createSyntheticInitFunctions() {
 void Writer::createInitMemoryFunction() {
   LLVM_DEBUG(dbgs() << "createInitMemoryFunction\n");
   assert(WasmSym::initMemory);
-  assert(WasmSym::initMemoryFlag);
   assert(hasPassiveInitializedSegments());
-  uint64_t flagAddress = WasmSym::initMemoryFlag->getVA();
-  bool is64 = config->is64.getValueOr(false);
+  uint64_t flagAddress;
+  if (config->sharedMemory) {
+    assert(WasmSym::initMemoryFlag);
+    flagAddress = WasmSym::initMemoryFlag->getVA();
+  }
+  bool is64 = config->is64.value_or(false);
   std::string bodyContent;
   {
     raw_string_ostream os(bodyContent);
     // Initialize memory in a thread-safe manner. The thread that successfully
-    // increments the flag from 0 to 1 is is responsible for performing the
-    // memory initialization. Other threads go sleep on the flag until the
-    // first thread finishing initializing memory, increments the flag to 2,
-    // and wakes all the other threads. Once the flag has been set to 2,
-    // subsequently started threads will skip the sleep. All threads
-    // unconditionally drop their passive data segments once memory has been
-    // initialized. The generated code is as follows:
+    // increments the flag from 0 to 1 is responsible for performing the memory
+    // initialization. Other threads go sleep on the flag until the first thread
+    // finishing initializing memory, increments the flag to 2, and wakes all
+    // the other threads. Once the flag has been set to 2, subsequently started
+    // threads will skip the sleep. All threads unconditionally drop their
+    // passive data segments once memory has been initialized. The generated
+    // code is as follows:
     //
     // (func $__wasm_init_memory
-    //  (if
-    //   (i32.atomic.rmw.cmpxchg align=2 offset=0
-    //    (i32.const $__init_memory_flag)
-    //    (i32.const 0)
-    //    (i32.const 1)
-    //   )
-    //   (then
-    //    (drop
-    //     (i32.atomic.wait align=2 offset=0
-    //      (i32.const $__init_memory_flag)
-    //      (i32.const 1)
-    //      (i32.const -1)
+    //  (block $drop
+    //   (block $wait
+    //    (block $init
+    //     (br_table $init $wait $drop
+    //      (i32.atomic.rmw.cmpxchg align=2 offset=0
+    //       (i32.const $__init_memory_flag)
+    //       (i32.const 0)
+    //       (i32.const 1)
+    //      )
     //     )
-    //    )
-    //   )
-    //   (else
+    //    ) ;; $init
     //    ( ... initialize data segments ... )
     //    (i32.atomic.store align=2 offset=0
     //     (i32.const $__init_memory_flag)
@@ -1036,8 +1116,16 @@ void Writer::createInitMemoryFunction() {
     //      (i32.const -1u)
     //     )
     //    )
+    //    (br $drop)
+    //   ) ;; $wait
+    //   (drop
+    //    (i32.atomic.wait align=2 offset=0
+    //     (i32.const $__init_memory_flag)
+    //     (i32.const 1)
+    //     (i32.const -1)
+    //    )
     //   )
-    //  )
+    //  ) ;; $drop
     //  ( ... drop data segments ... )
     // )
     //
@@ -1047,21 +1135,6 @@ void Writer::createInitMemoryFunction() {
     //    (i32.const $__init_memory_flag)
     //    (i32.const 1)
 
-    // With PIC code we cache the flag address in local 0
-    if (config->isPic) {
-      writeUleb128(os, 1, "num local decls");
-      writeUleb128(os, 1, "local count");
-      writeU8(os, is64 ? WASM_TYPE_I64 : WASM_TYPE_I32, "address type");
-      writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
-      writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "memory_base");
-      writePtrConst(os, flagAddress, is64, "flag address");
-      writeU8(os, is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD, "add");
-      writeU8(os, WASM_OPCODE_LOCAL_SET, "local.set");
-      writeUleb128(os, 0, "local 0");
-    } else {
-      writeUleb128(os, 0, "num locals");
-    }
-
     auto writeGetFlagAddress = [&]() {
       if (config->isPic) {
         writeU8(os, WASM_OPCODE_LOCAL_GET, "local.get");
@@ -1071,78 +1144,152 @@ void Writer::createInitMemoryFunction() {
       }
     };
 
-    // Atomically check whether this is the main thread.
-    writeGetFlagAddress();
-    writeI32Const(os, 0, "expected flag value");
-    writeI32Const(os, 1, "flag value");
-    writeU8(os, WASM_OPCODE_ATOMICS_PREFIX, "atomics prefix");
-    writeUleb128(os, WASM_OPCODE_I32_RMW_CMPXCHG, "i32.atomic.rmw.cmpxchg");
-    writeMemArg(os, 2, 0);
-    writeU8(os, WASM_OPCODE_IF, "IF");
-    writeU8(os, WASM_TYPE_NORESULT, "blocktype");
-
-    // Did not increment 0, so wait for main thread to initialize memory
-    writeGetFlagAddress();
-    writeI32Const(os, 1, "expected flag value");
-    writeI64Const(os, -1, "timeout");
-
-    writeU8(os, WASM_OPCODE_ATOMICS_PREFIX, "atomics prefix");
-    writeUleb128(os, WASM_OPCODE_I32_ATOMIC_WAIT, "i32.atomic.wait");
-    writeMemArg(os, 2, 0);
-    writeU8(os, WASM_OPCODE_DROP, "drop");
-
-    writeU8(os, WASM_OPCODE_ELSE, "ELSE");
-
-    // Did increment 0, so conditionally initialize passive data segments
+    if (config->sharedMemory) {
+      // With PIC code we cache the flag address in local 0
+      if (config->isPic) {
+        writeUleb128(os, 1, "num local decls");
+        writeUleb128(os, 2, "local count");
+        writeU8(os, is64 ? WASM_TYPE_I64 : WASM_TYPE_I32, "address type");
+        writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
+        writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "memory_base");
+        writePtrConst(os, flagAddress, is64, "flag address");
+        writeU8(os, is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD, "add");
+        writeU8(os, WASM_OPCODE_LOCAL_SET, "local.set");
+        writeUleb128(os, 0, "local 0");
+      } else {
+        writeUleb128(os, 0, "num locals");
+      }
+
+      // Set up destination blocks
+      writeU8(os, WASM_OPCODE_BLOCK, "block $drop");
+      writeU8(os, WASM_TYPE_NORESULT, "block type");
+      writeU8(os, WASM_OPCODE_BLOCK, "block $wait");
+      writeU8(os, WASM_TYPE_NORESULT, "block type");
+      writeU8(os, WASM_OPCODE_BLOCK, "block $init");
+      writeU8(os, WASM_TYPE_NORESULT, "block type");
+
+      // Atomically check whether we win the race.
+      writeGetFlagAddress();
+      writeI32Const(os, 0, "expected flag value");
+      writeI32Const(os, 1, "new flag value");
+      writeU8(os, WASM_OPCODE_ATOMICS_PREFIX, "atomics prefix");
+      writeUleb128(os, WASM_OPCODE_I32_RMW_CMPXCHG, "i32.atomic.rmw.cmpxchg");
+      writeMemArg(os, 2, 0);
+
+      // Based on the value, decide what to do next.
+      writeU8(os, WASM_OPCODE_BR_TABLE, "br_table");
+      writeUleb128(os, 2, "label vector length");
+      writeUleb128(os, 0, "label $init");
+      writeUleb128(os, 1, "label $wait");
+      writeUleb128(os, 2, "default label $drop");
+
+      // Initialize passive data segments
+      writeU8(os, WASM_OPCODE_END, "end $init");
+    } else {
+      writeUleb128(os, 0, "num local decls");
+    }
+
     for (const OutputSegment *s : segments) {
       if (needsPassiveInitialization(s)) {
-        // destination address
+        // For passive BSS segments we can simple issue a memory.fill(0).
+        // For non-BSS segments we do a memory.init.  Both these
+        // instructions take as their first argument the destination
+        // address.
         writePtrConst(os, s->startVA, is64, "destination address");
         if (config->isPic) {
           writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
           writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(),
-                       "memory_base");
+                       "__memory_base");
           writeU8(os, is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD,
                   "i32.add");
         }
-        // source segment offset
-        writeI32Const(os, 0, "segment offset");
-        // memory region size
-        writeI32Const(os, s->size, "memory region size");
-        // memory.init instruction
-        writeU8(os, WASM_OPCODE_MISC_PREFIX, "bulk-memory prefix");
-        writeUleb128(os, WASM_OPCODE_MEMORY_INIT, "memory.init");
-        writeUleb128(os, s->index, "segment index immediate");
-        writeU8(os, 0, "memory index immediate");
+
+        // When we initialize the TLS segment we also set the `__tls_base`
+        // global.  This allows the runtime to use this static copy of the
+        // TLS data for the first/main thread.
+        if (config->sharedMemory && s->isTLS()) {
+          if (config->isPic) {
+            // Cache the result of the addionion in local 0
+            writeU8(os, WASM_OPCODE_LOCAL_TEE, "local.tee");
+            writeUleb128(os, 1, "local 1");
+          } else {
+            writePtrConst(os, s->startVA, is64, "destination address");
+          }
+          writeU8(os, WASM_OPCODE_GLOBAL_SET, "GLOBAL_SET");
+          writeUleb128(os, WasmSym::tlsBase->getGlobalIndex(),
+                       "__tls_base");
+          if (config->isPic) {
+            writeU8(os, WASM_OPCODE_LOCAL_GET, "local.tee");
+            writeUleb128(os, 1, "local 1");
+          }
+        }
+
+        if (s->isBss) {
+          writeI32Const(os, 0, "fill value");
+          writePtrConst(os, s->size, is64, "memory region size");
+          writeU8(os, WASM_OPCODE_MISC_PREFIX, "bulk-memory prefix");
+          writeUleb128(os, WASM_OPCODE_MEMORY_FILL, "memory.fill");
+          writeU8(os, 0, "memory index immediate");
+        } else {
+          writeI32Const(os, 0, "source segment offset");
+          writeI32Const(os, s->size, "memory region size");
+          writeU8(os, WASM_OPCODE_MISC_PREFIX, "bulk-memory prefix");
+          writeUleb128(os, WASM_OPCODE_MEMORY_INIT, "memory.init");
+          writeUleb128(os, s->index, "segment index immediate");
+          writeU8(os, 0, "memory index immediate");
+        }
       }
     }
 
-    // Set flag to 2 to mark end of initialization
-    writeGetFlagAddress();
-    writeI32Const(os, 2, "flag value");
-    writeU8(os, WASM_OPCODE_ATOMICS_PREFIX, "atomics prefix");
-    writeUleb128(os, WASM_OPCODE_I32_ATOMIC_STORE, "i32.atomic.store");
-    writeMemArg(os, 2, 0);
-
-    // Notify any waiters that memory initialization is complete
-    writeGetFlagAddress();
-    writeI32Const(os, -1, "number of waiters");
-    writeU8(os, WASM_OPCODE_ATOMICS_PREFIX, "atomics prefix");
-    writeUleb128(os, WASM_OPCODE_ATOMIC_NOTIFY, "atomic.notify");
-    writeMemArg(os, 2, 0);
-    writeU8(os, WASM_OPCODE_DROP, "drop");
-
-    writeU8(os, WASM_OPCODE_END, "END");
+    if (config->sharedMemory) {
+      // Set flag to 2 to mark end of initialization
+      writeGetFlagAddress();
+      writeI32Const(os, 2, "flag value");
+      writeU8(os, WASM_OPCODE_ATOMICS_PREFIX, "atomics prefix");
+      writeUleb128(os, WASM_OPCODE_I32_ATOMIC_STORE, "i32.atomic.store");
+      writeMemArg(os, 2, 0);
+
+      // Notify any waiters that memory initialization is complete
+      writeGetFlagAddress();
+      writeI32Const(os, -1, "number of waiters");
+      writeU8(os, WASM_OPCODE_ATOMICS_PREFIX, "atomics prefix");
+      writeUleb128(os, WASM_OPCODE_ATOMIC_NOTIFY, "atomic.notify");
+      writeMemArg(os, 2, 0);
+      writeU8(os, WASM_OPCODE_DROP, "drop");
+
+      // Branch to drop the segments
+      writeU8(os, WASM_OPCODE_BR, "br");
+      writeUleb128(os, 1, "label $drop");
+
+      // Wait for the winning thread to initialize memory
+      writeU8(os, WASM_OPCODE_END, "end $wait");
+      writeGetFlagAddress();
+      writeI32Const(os, 1, "expected flag value");
+      writeI64Const(os, -1, "timeout");
+
+      writeU8(os, WASM_OPCODE_ATOMICS_PREFIX, "atomics prefix");
+      writeUleb128(os, WASM_OPCODE_I32_ATOMIC_WAIT, "i32.atomic.wait");
+      writeMemArg(os, 2, 0);
+      writeU8(os, WASM_OPCODE_DROP, "drop");
+
+      // Unconditionally drop passive data segments
+      writeU8(os, WASM_OPCODE_END, "end $drop");
+    }
 
-    // Unconditionally drop passive data segments
     for (const OutputSegment *s : segments) {
-      if (needsPassiveInitialization(s)) {
+      if (needsPassiveInitialization(s) && !s->isBss) {
+        // The TLS region should not be dropped since its is needed
+        // during the initialization of each thread (__wasm_init_tls).
+        if (config->sharedMemory && s->isTLS())
+          continue;
         // data.drop instruction
         writeU8(os, WASM_OPCODE_MISC_PREFIX, "bulk-memory prefix");
         writeUleb128(os, WASM_OPCODE_DATA_DROP, "data.drop");
         writeUleb128(os, s->index, "segment index immediate");
       }
     }
+
+    // End the function
     writeU8(os, WASM_OPCODE_END, "END");
   }
 
@@ -1150,16 +1297,18 @@ void Writer::createInitMemoryFunction() {
 }
 
 void Writer::createStartFunction() {
-  if (WasmSym::startFunction) {
+  // If the start function exists when we have more than one function to call.
+  if (WasmSym::initMemory && WasmSym::applyGlobalRelocs) {
+    assert(WasmSym::startFunction);
     std::string bodyContent;
     {
       raw_string_ostream os(bodyContent);
       writeUleb128(os, 0, "num locals");
       writeU8(os, WASM_OPCODE_CALL, "CALL");
-      writeUleb128(os, WasmSym::initMemory->getFunctionIndex(),
+      writeUleb128(os, WasmSym::applyGlobalRelocs->getFunctionIndex(),
                    "function index");
       writeU8(os, WASM_OPCODE_CALL, "CALL");
-      writeUleb128(os, WasmSym::applyGlobalRelocs->getFunctionIndex(),
+      writeUleb128(os, WasmSym::initMemory->getFunctionIndex(),
                    "function index");
       writeU8(os, WASM_OPCODE_END, "END");
     }
@@ -1173,8 +1322,8 @@ void Writer::createStartFunction() {
 
 // For -shared (PIC) output, we create create a synthetic function which will
 // apply any relocations to the data segments on startup.  This function is
-// called `__wasm_apply_data_relocs` and is added at the beginning of
-// `__wasm_call_ctors` before any of the constructors run.
+// called `__wasm_apply_data_relocs` and is expected to be called before
+// any user code (i.e. before `__wasm_call_ctors`).
 void Writer::createApplyDataRelocationsFunction() {
   LLVM_DEBUG(dbgs() << "createApplyDataRelocationsFunction\n");
   // First write the body's contents to a string.
@@ -1193,7 +1342,7 @@ void Writer::createApplyDataRelocationsFunction() {
 }
 
 // Similar to createApplyDataRelocationsFunction but generates relocation code
-// fro WebAssembly globals. Because these globals are not shared between threads
+// for WebAssembly globals. Because these globals are not shared between threads
 // these relocation need to run on every thread.
 void Writer::createApplyGlobalRelocationsFunction() {
   // First write the body's contents to a string.
@@ -1201,21 +1350,35 @@ void Writer::createApplyGlobalRelocationsFunction() {
   {
     raw_string_ostream os(bodyContent);
     writeUleb128(os, 0, "num locals");
-    out.globalSec->generateRelocationCode(os);
+    out.globalSec->generateRelocationCode(os, false);
     writeU8(os, WASM_OPCODE_END, "END");
   }
 
   createFunction(WasmSym::applyGlobalRelocs, bodyContent);
 }
 
+// Similar to createApplyGlobalRelocationsFunction but for
+// TLS symbols.  This cannot be run during the start function
+// but must be delayed until __wasm_init_tls is called.
+void Writer::createApplyGlobalTLSRelocationsFunction() {
+  // First write the body's contents to a string.
+  std::string bodyContent;
+  {
+    raw_string_ostream os(bodyContent);
+    writeUleb128(os, 0, "num locals");
+    out.globalSec->generateRelocationCode(os, true);
+    writeU8(os, WASM_OPCODE_END, "END");
+  }
+
+  createFunction(WasmSym::applyGlobalTLSRelocs, bodyContent);
+}
+
 // Create synthetic "__wasm_call_ctors" function based on ctor functions
 // in input object.
 void Writer::createCallCtorsFunction() {
-  // If __wasm_call_ctors isn't referenced, there aren't any ctors, and we
-  // aren't calling `__wasm_apply_data_relocs` for Emscripten-style PIC, don't
+  // If __wasm_call_ctors isn't referenced, there aren't any ctors, don't
   // define the `__wasm_call_ctors` function.
-  if (!WasmSym::callCtors->isLive() && !WasmSym::applyDataRelocs &&
-      initFunctions.empty())
+  if (!WasmSym::callCtors->isLive() && initFunctions.empty())
     return;
 
   // First write the body's contents to a string.
@@ -1224,12 +1387,6 @@ void Writer::createCallCtorsFunction() {
     raw_string_ostream os(bodyContent);
     writeUleb128(os, 0, "num locals");
 
-    if (WasmSym::applyDataRelocs) {
-      writeU8(os, WASM_OPCODE_CALL, "CALL");
-      writeUleb128(os, WasmSym::applyDataRelocs->getFunctionIndex(),
-                   "function index");
-    }
-
     // Call constructors
     for (const WasmInitEntry &f : initFunctions) {
       writeU8(os, WASM_OPCODE_CALL, "CALL");
@@ -1318,6 +1475,12 @@ void Writer::createInitTLSFunction() {
       writeUleb128(os, tlsSeg->index, "segment index immediate");
       writeU8(os, 0, "memory index immediate");
     }
+
+    if (WasmSym::applyGlobalTLSRelocs) {
+      writeU8(os, WASM_OPCODE_CALL, "CALL");
+      writeUleb128(os, WasmSym::applyGlobalTLSRelocs->getFunctionIndex(),
+                   "function index");
+    }
     writeU8(os, WASM_OPCODE_END, "end function");
   }
 
@@ -1376,9 +1539,6 @@ void Writer::createSyntheticSectionsPostLayout() {
 }
 
 void Writer::run() {
-  if (config->relocatable || config->isPic)
-    config->globalBase = 0;
-
   // For PIC code the table base is assigned dynamically by the loader.
   // For non-PIC, we start at 1 so that accessing table index 0 always traps.
   if (!config->isPic) {
@@ -1410,8 +1570,8 @@ void Writer::run() {
       sym->forceExport = true;
   }
 
-  // Delay reporting error about explict exports until after addStartStopSymbols
-  // which can create optional symbols.
+  // Delay reporting errors about explicit exports until after
+  // addStartStopSymbols which can create optional symbols.
   for (auto &name : config->requiredExports) {
     Symbol *sym = symtab->find(name);
     if (!sym || !sym->isDefined()) {
@@ -1422,8 +1582,15 @@ void Writer::run() {
     }
   }
 
-  if (config->isPic && !config->sharedMemory) {
-    // In shared memory mode all data segments are passive and initilized
+  log("-- populateTargetFeatures");
+  populateTargetFeatures();
+
+  // When outputting PIC code each segment lives at at fixes offset from the
+  // `__memory_base` import.  Unless we support the extended const expression we
+  // can't do addition inside the constant expression, so we much combine the
+  // segments into a single one that can live at `__memory_base`.
+  if (config->isPic && !config->extendedConst && !config->sharedMemory) {
+    // In shared memory mode all data segments are passive and initialized
     // via __wasm_init_memory.
     log("-- combineOutputSegments");
     combineOutputSegments();
@@ -1452,6 +1619,8 @@ void Writer::run() {
       createApplyDataRelocationsFunction();
     if (WasmSym::applyGlobalRelocs)
       createApplyGlobalRelocationsFunction();
+    if (WasmSym::applyGlobalTLSRelocs)
+      createApplyGlobalTLSRelocationsFunction();
     if (WasmSym::initMemory)
       createInitMemoryFunction();
     createStartFunction();
@@ -1471,8 +1640,10 @@ void Writer::run() {
     }
   }
 
-  if (WasmSym::initTLS && WasmSym::initTLS->isLive())
+  if (WasmSym::initTLS && WasmSym::initTLS->isLive()) {
+    log("-- createInitTLSFunction");
     createInitTLSFunction();
+  }
 
   if (errorCount())
     return;
@@ -1485,8 +1656,8 @@ void Writer::run() {
   calculateCustomSections();
   log("-- populateSymtab");
   populateSymtab();
-  log("-- populateTargetFeatures");
-  populateTargetFeatures();
+  log("-- checkImportExportTargetFeatures");
+  checkImportExportTargetFeatures();
   log("-- addSections");
   addSections();
 
@@ -1500,8 +1671,6 @@ void Writer::run() {
     log("Global Imports   : " + Twine(out.importSec->getNumImportedGlobals()));
     log("Tag Imports      : " + Twine(out.importSec->getNumImportedTags()));
     log("Table Imports    : " + Twine(out.importSec->getNumImportedTables()));
-    for (ObjFile *file : symtab->objectFiles)
-      file->dumpInfo();
   }
 
   createHeader();
@@ -1524,7 +1693,8 @@ void Writer::run() {
     return;
 
   if (Error e = buffer->commit())
-    fatal("failed to write the output file: " + toString(std::move(e)));
+    fatal("failed to write output '" + buffer->getPath() +
+          "': " + toString(std::move(e)));
 }
 
 // Open a result file.
index 17e7c17..03c66e8 100644 (file)
@@ -58,12 +58,6 @@ std::string toString(const WasmGlobalType &type) {
          toString(static_cast<ValType>(type.Type));
 }
 
-std::string toString(const WasmTagType &type) {
-  if (type.Attribute == WASM_TAG_ATTRIBUTE_EXCEPTION)
-    return "exception";
-  return "unknown";
-}
-
 static std::string toString(const llvm::wasm::WasmLimits &limits) {
   std::string ret;
   ret += "flags=0x" + std::to_string(limits.Flags);
@@ -80,9 +74,11 @@ std::string toString(const WasmTableType &type) {
 }
 
 namespace wasm {
+#ifdef LLVM_DEBUG
 void debugWrite(uint64_t offset, const Twine &msg) {
   LLVM_DEBUG(dbgs() << format("  | %08lld: ", offset) << msg << "\n");
 }
+#endif
 
 void writeUleb128(raw_ostream &os, uint64_t number, const Twine &msg) {
   debugWrite(os.tell(), msg + "[" + utohexstr(number) + "]");
@@ -163,6 +159,11 @@ void writeMemArg(raw_ostream &os, uint32_t alignment, uint64_t offset) {
 }
 
 void writeInitExpr(raw_ostream &os, const WasmInitExpr &initExpr) {
+  assert(!initExpr.Extended);
+  writeInitExprMVP(os, initExpr.Inst);
+}
+
+void writeInitExprMVP(raw_ostream &os, const WasmInitExprMVP &initExpr) {
   writeU8(os, initExpr.Opcode, "opcode");
   switch (initExpr.Opcode) {
   case WASM_OPCODE_I32_CONST:
@@ -202,15 +203,6 @@ void writeGlobalType(raw_ostream &os, const WasmGlobalType &type) {
   writeU8(os, type.Mutable, "global mutable");
 }
 
-void writeTagType(raw_ostream &os, const WasmTagType &type) {
-  writeUleb128(os, type.Attribute, "tag attribute");
-  writeUleb128(os, type.SigIndex, "sig index");
-}
-
-void writeTag(raw_ostream &os, const WasmTag &tag) {
-  writeTagType(os, tag.Type);
-}
-
 void writeTableType(raw_ostream &os, const WasmTableType &type) {
   writeValueType(os, ValType(type.ElemType), "table type");
   writeLimits(os, type.Limits);
@@ -228,7 +220,8 @@ void writeImport(raw_ostream &os, const WasmImport &import) {
     writeGlobalType(os, import.Global);
     break;
   case WASM_EXTERNAL_TAG:
-    writeTagType(os, import.Tag);
+    writeUleb128(os, 0, "tag attribute"); // Reserved "attribute" field
+    writeUleb128(os, import.SigIndex, "import sig index");
     break;
   case WASM_EXTERNAL_MEMORY:
     writeLimits(os, import.Memory);
index 40daf45..2be79d1 100644 (file)
 namespace lld {
 namespace wasm {
 
+#ifdef LLVM_DEBUG
 void debugWrite(uint64_t offset, const Twine &msg);
+#else
+#define debugWrite(...) (void *)0
+#endif
 
 void writeUleb128(raw_ostream &os, uint64_t number, const Twine &msg);
 
@@ -47,14 +51,13 @@ void writeMemArg(raw_ostream &os, uint32_t alignment, uint64_t offset);
 
 void writeInitExpr(raw_ostream &os, const llvm::wasm::WasmInitExpr &initExpr);
 
+void writeInitExprMVP(raw_ostream &os,
+                      const llvm::wasm::WasmInitExprMVP &initExpr);
+
 void writeLimits(raw_ostream &os, const llvm::wasm::WasmLimits &limits);
 
 void writeGlobalType(raw_ostream &os, const llvm::wasm::WasmGlobalType &type);
 
-void writeTagType(raw_ostream &os, const llvm::wasm::WasmTagType &type);
-
-void writeTag(raw_ostream &os, const llvm::wasm::WasmTag &tag);
-
 void writeTableType(raw_ostream &os, const llvm::wasm::WasmTableType &type);
 
 void writeImport(raw_ostream &os, const llvm::wasm::WasmImport &import);
@@ -66,7 +69,6 @@ void writeExport(raw_ostream &os, const llvm::wasm::WasmExport &export_);
 std::string toString(llvm::wasm::ValType type);
 std::string toString(const llvm::wasm::WasmSignature &sig);
 std::string toString(const llvm::wasm::WasmGlobalType &type);
-std::string toString(const llvm::wasm::WasmTagType &type);
 std::string toString(const llvm::wasm::WasmTableType &type);
 
 } // namespace lld