mirror of
https://github.com/ziglang/zig.git
synced 2026-02-11 12:01:18 +00:00
remove embedded LLD
we no longer have any patches against upstream LLD
This commit is contained in:
parent
fbe6af81fd
commit
ba4cc03b4f
210
CMakeLists.txt
210
CMakeLists.txt
@ -58,12 +58,14 @@ string(REGEX REPLACE "\\\\" "\\\\\\\\" ZIG_LIBC_INCLUDE_DIR_ESCAPED "${ZIG_LIBC_
|
||||
|
||||
option(ZIG_TEST_COVERAGE "Build Zig with test coverage instrumentation" OFF)
|
||||
|
||||
# To see what patches have been applied to LLD in this repository:
|
||||
# git log -p -- deps/lld
|
||||
option(ZIG_FORCE_EXTERNAL_LLD "If your system has the LLD patches use it instead of the embedded LLD" OFF)
|
||||
# Zig no longer has embedded LLD. This option is kept for package maintainers
|
||||
# so that they don't have to update their scripts in case we ever re-introduce
|
||||
# LLD to the tree. This option does nothing.
|
||||
option(ZIG_FORCE_EXTERNAL_LLD "does nothing" OFF)
|
||||
|
||||
find_package(llvm)
|
||||
find_package(clang)
|
||||
find_package(lld)
|
||||
|
||||
if(APPLE AND ZIG_STATIC)
|
||||
list(REMOVE_ITEM LLVM_LIBRARIES "-lz")
|
||||
@ -83,205 +85,9 @@ foreach(CONFIG_TYPE ${CMAKE_CONFIGURATION_TYPES})
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${CONFIG_TYPE} ${ZIG_CPP_LIB_DIR})
|
||||
endforeach(CONFIG_TYPE CMAKE_CONFIGURATION_TYPES)
|
||||
|
||||
if(ZIG_FORCE_EXTERNAL_LLD)
|
||||
find_package(lld)
|
||||
include_directories(${LLVM_INCLUDE_DIRS})
|
||||
include_directories(${LLD_INCLUDE_DIRS})
|
||||
include_directories(${CLANG_INCLUDE_DIRS})
|
||||
else()
|
||||
# This goes first so that we find embedded LLD instead
|
||||
# of system LLD.
|
||||
include_directories("${CMAKE_SOURCE_DIR}/deps/lld/include")
|
||||
|
||||
include_directories(${LLVM_INCLUDE_DIRS})
|
||||
include_directories(${CLANG_INCLUDE_DIRS})
|
||||
set(EMBEDDED_LLD_LIB_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/Common/Args.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/Common/ErrorHandler.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/Common/Filesystem.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/Common/Memory.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/Common/Reproduce.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/Common/Strings.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/Common/TargetOptionsCommandFlags.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/Common/Threads.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/Common/Timer.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/Common/Version.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/Core/DefinedAtom.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/Core/Error.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/Core/File.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/Core/LinkingContext.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/Core/Reader.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/Core/Resolver.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/Core/SymbolTable.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/Core/Writer.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/Driver/DarwinLdDriver.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/FileArchive.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/ArchHandler.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/ArchHandler_arm.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/CompactUnwindPass.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/GOTPass.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/LayoutPass.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/ObjCPass.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/ShimPass.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/StubsPass.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/TLVPass.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/WriterMachO.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp"
|
||||
)
|
||||
|
||||
set(EMBEDDED_LLD_ELF_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/AArch64ErrataFix.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/AArch64.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/AMDGPU.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/ARM.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/AVR.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/Hexagon.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/MSP430.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/Mips.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/MipsArchTree.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/PPC.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/PPC64.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/RISCV.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/SPARCV9.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/X86.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/X86_64.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/CallGraphSort.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/DWARF.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Driver.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/DriverUtils.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/EhFrame.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/ICF.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/InputFiles.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/InputSection.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/LTO.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/LinkerScript.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/MapFile.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/MarkLive.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/OutputSections.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Relocations.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/ScriptLexer.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/ScriptParser.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/SymbolTable.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Symbols.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/SyntheticSections.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Target.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Thunks.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Writer.cpp"
|
||||
)
|
||||
|
||||
set(EMBEDDED_LLD_COFF_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/Chunks.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/DLL.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/DebugTypes.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/Driver.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/DriverUtils.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/ICF.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/InputFiles.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/LTO.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/MapFile.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/MarkLive.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/MinGW.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/PDB.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/SymbolTable.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/Symbols.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/Writer.cpp"
|
||||
)
|
||||
set(EMBEDDED_LLD_MINGW_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/MinGW/Driver.cpp"
|
||||
)
|
||||
set(EMBEDDED_LLD_WASM_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/wasm/Driver.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/wasm/InputChunks.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/wasm/InputFiles.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/wasm/LTO.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/wasm/MarkLive.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/wasm/OutputSections.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/wasm/Relocations.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/wasm/SymbolTable.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/wasm/Symbols.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/wasm/SyntheticSections.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/wasm/Writer.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/wasm/WriterUtils.cpp"
|
||||
)
|
||||
add_library(embedded_lld_lib STATIC ${EMBEDDED_LLD_LIB_SOURCES})
|
||||
add_library(embedded_lld_elf STATIC ${EMBEDDED_LLD_ELF_SOURCES})
|
||||
add_library(embedded_lld_coff STATIC ${EMBEDDED_LLD_COFF_SOURCES})
|
||||
add_library(embedded_lld_mingw STATIC ${EMBEDDED_LLD_MINGW_SOURCES})
|
||||
add_library(embedded_lld_wasm STATIC ${EMBEDDED_LLD_WASM_SOURCES})
|
||||
if(MSVC)
|
||||
set(ZIG_LLD_COMPILE_FLAGS "-std=c++11 -D_CRT_SECURE_NO_WARNINGS /w")
|
||||
else()
|
||||
set(ZIG_LLD_COMPILE_FLAGS "-std=c++11 -fvisibility-inlines-hidden -fno-exceptions -fno-rtti -Wno-comment")
|
||||
if(MINGW)
|
||||
set(ZIG_LLD_COMPILE_FLAGS "${ZIG_LLD_COMPILE_FLAGS} -D__STDC_FORMAT_MACROS -D__USE_MINGW_ANSI_STDIO")
|
||||
endif()
|
||||
endif()
|
||||
set_target_properties(embedded_lld_lib PROPERTIES
|
||||
COMPILE_FLAGS ${ZIG_LLD_COMPILE_FLAGS}
|
||||
LINK_FLAGS " "
|
||||
)
|
||||
set_target_properties(embedded_lld_elf PROPERTIES
|
||||
COMPILE_FLAGS ${ZIG_LLD_COMPILE_FLAGS}
|
||||
LINK_FLAGS " "
|
||||
)
|
||||
set_target_properties(embedded_lld_coff PROPERTIES
|
||||
COMPILE_FLAGS ${ZIG_LLD_COMPILE_FLAGS}
|
||||
LINK_FLAGS " "
|
||||
)
|
||||
set_target_properties(embedded_lld_mingw PROPERTIES
|
||||
COMPILE_FLAGS ${ZIG_LLD_COMPILE_FLAGS}
|
||||
LINK_FLAGS " "
|
||||
)
|
||||
set_target_properties(embedded_lld_wasm PROPERTIES
|
||||
COMPILE_FLAGS ${ZIG_LLD_COMPILE_FLAGS}
|
||||
LINK_FLAGS " "
|
||||
)
|
||||
target_include_directories(embedded_lld_lib PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/include"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld-prebuilt"
|
||||
)
|
||||
target_include_directories(embedded_lld_elf PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/include"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld-prebuilt/ELF"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld-prebuilt"
|
||||
)
|
||||
target_include_directories(embedded_lld_coff PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/include"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld-prebuilt/COFF"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld-prebuilt"
|
||||
)
|
||||
target_include_directories(embedded_lld_mingw PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/MinGW"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/include"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld-prebuilt/MinGW"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld-prebuilt"
|
||||
)
|
||||
target_include_directories(embedded_lld_wasm PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/wasm"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/include"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld-prebuilt/wasm"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld-prebuilt"
|
||||
)
|
||||
set(LLD_INCLUDE_DIRS "")
|
||||
set(LLD_LIBRARIES
|
||||
embedded_lld_elf
|
||||
embedded_lld_coff
|
||||
embedded_lld_mingw
|
||||
embedded_lld_wasm
|
||||
embedded_lld_lib
|
||||
)
|
||||
endif()
|
||||
include_directories(${LLVM_INCLUDE_DIRS})
|
||||
include_directories(${LLD_INCLUDE_DIRS})
|
||||
include_directories(${CLANG_INCLUDE_DIRS})
|
||||
|
||||
# No patches have been applied to SoftFloat-3e
|
||||
set(EMBEDDED_SOFTFLOAT_SOURCES
|
||||
|
||||
275
deps/lld-prebuilt/COFF/Options.inc
vendored
275
deps/lld-prebuilt/COFF/Options.inc
vendored
@ -1,275 +0,0 @@
|
||||
/*===- TableGen'erated file -------------------------------------*- C++ -*-===*\
|
||||
|* *|
|
||||
|* Option Parsing Definitions *|
|
||||
|* *|
|
||||
|* Automatically generated file, do not edit! *|
|
||||
|* *|
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
/////////
|
||||
// Prefixes
|
||||
|
||||
#ifdef PREFIX
|
||||
#define COMMA ,
|
||||
PREFIX(prefix_0, {nullptr})
|
||||
PREFIX(prefix_2, {"--" COMMA nullptr})
|
||||
PREFIX(prefix_1, {"/" COMMA "-" COMMA "/?" COMMA "-?" COMMA nullptr})
|
||||
PREFIX(prefix_3, {"/??" COMMA "-??" COMMA "/?" COMMA "-?" COMMA nullptr})
|
||||
#undef COMMA
|
||||
#endif // PREFIX
|
||||
|
||||
/////////
|
||||
// Groups
|
||||
|
||||
#ifdef OPTION
|
||||
|
||||
//////////
|
||||
// Options
|
||||
|
||||
OPTION(prefix_0, "<input>", INPUT, Input, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_0, "<unknown>", UNKNOWN, Unknown, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "align:", align, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Section alignment", nullptr, nullptr)
|
||||
OPTION(prefix_1, "aligncomm:", aligncomm, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Set common symbol alignment", nullptr, nullptr)
|
||||
OPTION(prefix_1, "allowbind:no", allowbind_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Disable DLL binding", nullptr, nullptr)
|
||||
OPTION(prefix_1, "allowbind", allowbind, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Enable DLL binding (default)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "allowisolation:no", allowisolation_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Disable DLL isolation", nullptr, nullptr)
|
||||
OPTION(prefix_1, "allowisolation", allowisolation, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Enable DLL isolation (default)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "alternatename:", alternatename, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Define weak alias", nullptr, nullptr)
|
||||
OPTION(prefix_1, "appcontainer:no", appcontainer_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Image can run outside an app container (default)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "appcontainer", appcontainer, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Image can only be run in an app container", nullptr, nullptr)
|
||||
OPTION(prefix_1, "base:", base, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Base address of the program", nullptr, nullptr)
|
||||
OPTION(prefix_1, "Brepro", repro, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Use a hash of the executable as the PE header timestamp", nullptr, nullptr)
|
||||
OPTION(prefix_2, "color-diagnostics=", color_diagnostics_eq, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Use colors in diagnostics; one of 'always', 'never', 'auto'", nullptr, nullptr)
|
||||
OPTION(prefix_2, "color-diagnostics", color_diagnostics, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Use colors in diagnostics", nullptr, nullptr)
|
||||
OPTION(prefix_1, "debug:", debug_opt, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Embed a symbol table in the image with option", nullptr, nullptr)
|
||||
OPTION(prefix_1, "debugtype:", debugtype, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Debug Info Options", nullptr, nullptr)
|
||||
OPTION(prefix_1, "debug", debug, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Embed a symbol table in the image", nullptr, nullptr)
|
||||
OPTION(prefix_1, "def:", deffile, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Use module-definition file", nullptr, nullptr)
|
||||
OPTION(prefix_1, "defaultlib:", defaultlib, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Add the library to the list of input files", nullptr, nullptr)
|
||||
OPTION(prefix_1, "delay:", delay, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "delayload:", delayload, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Delay loaded DLL name", nullptr, nullptr)
|
||||
OPTION(prefix_1, "demangle:no", demangle_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not demangle symbols in output", nullptr, nullptr)
|
||||
OPTION(prefix_1, "demangle", demangle, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Demangle symbols in output (default)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "disallowlib:", disallowlib, Joined, INVALID, nodefaultlib, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "dll", dll, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Create a DLL", nullptr, nullptr)
|
||||
OPTION(prefix_1, "driver:", driver, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Generate a Windows NT Kernel Mode Driver", nullptr, nullptr)
|
||||
OPTION(prefix_1, "dynamicbase:no", dynamicbase_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Disable ASLR (default when /fixed)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "dynamicbase", dynamicbase, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Enable ASLR (default unless /fixed)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "editandcontinue", editandcontinue, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "entry:", entry, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Name of entry point symbol", nullptr, nullptr)
|
||||
OPTION(prefix_1, "errorlimit:", errorlimit, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Maximum number of errors to emit before stopping (0 = no limit)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "errorreport:", errorreport, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "exclude-all-symbols", exclude_all_symbols, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "export-all-symbols", export_all_symbols, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "export:", export, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Export a function", nullptr, nullptr)
|
||||
OPTION(prefix_1, "failifmismatch:", failifmismatch, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"", nullptr, nullptr)
|
||||
OPTION(prefix_1, "fastfail", fastfail, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "filealign:", filealign, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Section alignment in the output file", nullptr, nullptr)
|
||||
OPTION(prefix_1, "fixed:no", fixed_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Enable base relocations (default)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "fixed", fixed, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Disable base relocations", nullptr, nullptr)
|
||||
OPTION(prefix_1, "force:multipleres", force_multipleres, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Allow multiply defined resources when creating executables", nullptr, nullptr)
|
||||
OPTION(prefix_1, "force:multiple", force_multiple, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Allow multiply defined symbols when creating executables", nullptr, nullptr)
|
||||
OPTION(prefix_1, "force:unresolved", force_unresolved, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Allow undefined symbols when creating executables", nullptr, nullptr)
|
||||
OPTION(prefix_1, "force", force, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Allow undefined and multiply defined symbols when creating executables", nullptr, nullptr)
|
||||
OPTION(prefix_1, "functionpadmin:", functionpadmin_opt, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Prepares an image for hotpatching", nullptr, nullptr)
|
||||
OPTION(prefix_1, "functionpadmin", functionpadmin, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "guard:", guard, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Control flow guard", nullptr, nullptr)
|
||||
OPTION(prefix_1, "guardsym:", guardsym, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "heap:", heap, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Size of the heap", nullptr, nullptr)
|
||||
OPTION(prefix_1, "help", help, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "highentropyva:no", highentropyva_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Disable 64-bit ASLR", nullptr, nullptr)
|
||||
OPTION(prefix_1, "highentropyva", highentropyva, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Enable 64-bit ASLR (default on 64-bit)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "idlout:", idlout, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "ignore:", ignore, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Specify warning codes to ignore", nullptr, nullptr)
|
||||
OPTION(prefix_1, "ignoreidl", ignoreidl, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "implib:", implib, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Import library name", nullptr, nullptr)
|
||||
OPTION(prefix_1, "include:", incl, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Force symbol to be added to symbol table as undefined one", nullptr, nullptr)
|
||||
OPTION(prefix_1, "includeoptional:", include_optional, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Add symbol as undefined, but allow it to remain undefined", nullptr, nullptr)
|
||||
OPTION(prefix_1, "incremental:no", incremental_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Overwrite import library even if contents are unchanged", nullptr, nullptr)
|
||||
OPTION(prefix_1, "incremental", incremental, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Keep original import library if contents are unchanged", nullptr, nullptr)
|
||||
OPTION(prefix_1, "integritycheck:no", integritycheck_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"No effect (default)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "integritycheck", integritycheck, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Set FORCE_INTEGRITY bit in PE header", nullptr, nullptr)
|
||||
OPTION(prefix_1, "kill-at", kill_at, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "largeaddressaware:no", largeaddressaware_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Disable large addresses (default on 32-bit)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "largeaddressaware", largeaddressaware, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Enable large addresses (default on 64-bit)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "libpath:", libpath, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Additional library search path", nullptr, nullptr)
|
||||
OPTION(prefix_1, "lib", lib, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Act like lib.exe; must be first argument if present", nullptr, nullptr)
|
||||
OPTION(prefix_1, "linkrepro:", linkrepro, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Dump linker invocation and input files for debugging", nullptr, nullptr)
|
||||
OPTION(prefix_1, "lldltocache:", lldltocache, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Path to ThinLTO cached object file directory", nullptr, nullptr)
|
||||
OPTION(prefix_1, "lldltocachepolicy:", lldltocachepolicy, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Pruning policy for the ThinLTO cache", nullptr, nullptr)
|
||||
OPTION(prefix_1, "lldmap:", lldmap_file, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "lldmap", lldmap, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "lldmingw", lldmingw, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "lldsavetemps", lldsavetemps, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Save temporary files instead of deleting them", nullptr, nullptr)
|
||||
OPTION(prefix_1, "machine:", machine, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Specify target platform", nullptr, nullptr)
|
||||
OPTION(prefix_1, "manifest:", manifest_colon, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"NO disables manifest output; EMBED[,ID=#] embeds manifest as resource in the image", nullptr, nullptr)
|
||||
OPTION(prefix_1, "manifestdependency:", manifestdependency, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Attributes for <dependency> element in manifest file; implies /manifest", nullptr, nullptr)
|
||||
OPTION(prefix_1, "manifestfile:", manifestfile, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Manifest output path, with /manifest", nullptr, nullptr)
|
||||
OPTION(prefix_1, "manifestinput:", manifestinput, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Additional manifest inputs; only valid with /manifest:embed", nullptr, nullptr)
|
||||
OPTION(prefix_1, "manifestuac:", manifestuac, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"User access control", nullptr, nullptr)
|
||||
OPTION(prefix_1, "manifest", manifest, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Create .manifest file", nullptr, nullptr)
|
||||
OPTION(prefix_1, "maxilksize:", maxilksize, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "merge:", merge, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Combine sections", nullptr, nullptr)
|
||||
OPTION(prefix_1, "mllvm:", mllvm, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Options to pass to LLVM", nullptr, nullptr)
|
||||
OPTION(prefix_1, "natvis:", natvis, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Path to natvis file to embed in the PDB", nullptr, nullptr)
|
||||
OPTION(prefix_1, "no-color-diagnostics", no_color_diagnostics, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not use colors in diagnostics", nullptr, nullptr)
|
||||
OPTION(prefix_1, "nodefaultlib:", nodefaultlib, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Remove a default library", nullptr, nullptr)
|
||||
OPTION(prefix_1, "nodefaultlib", nodefaultlib_all, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Remove all default libraries", nullptr, nullptr)
|
||||
OPTION(prefix_1, "noentry", noentry, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Don't add reference to DllMainCRTStartup; only valid with /dll", nullptr, nullptr)
|
||||
OPTION(prefix_1, "nologo", nologo, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "nxcompat:no", nxcompat_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Disable data execution provention", nullptr, nullptr)
|
||||
OPTION(prefix_1, "nxcompat", nxcompat, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Enable data execution prevention (default)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "opt:", opt, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Control optimizations", nullptr, nullptr)
|
||||
OPTION(prefix_1, "order:", order, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Put functions in order", nullptr, nullptr)
|
||||
OPTION(prefix_1, "out:", out, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Path to file to write output", nullptr, nullptr)
|
||||
OPTION(prefix_1, "output-def:", output_def, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "pdb:", pdb, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"PDB file path", nullptr, nullptr)
|
||||
OPTION(prefix_1, "pdbaltpath:", pdbaltpath, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"PDB file path to embed in the image", nullptr, nullptr)
|
||||
OPTION(prefix_1, "pdbsourcepath:", pdb_source_path, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Base path used to make relative source file path absolute in PDB", nullptr, nullptr)
|
||||
OPTION(prefix_1, "profile", profile, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "rsp-quoting=", rsp_quoting, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Quoting style for response files, 'windows' (default) or 'posix'", nullptr, nullptr)
|
||||
OPTION(prefix_1, "safeseh:no", safeseh_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Don't produce an image with Safe Exception Handler", nullptr, nullptr)
|
||||
OPTION(prefix_1, "safeseh", safeseh, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Produce an image with Safe Exception Handler (only for x86)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "section:", section, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Specify section attributes", nullptr, nullptr)
|
||||
OPTION(prefix_1, "stack:", stack, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Size of the stack", nullptr, nullptr)
|
||||
OPTION(prefix_1, "stub:", stub, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Specify DOS stub file", nullptr, nullptr)
|
||||
OPTION(prefix_1, "subsystem:", subsystem, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Specify subsystem", nullptr, nullptr)
|
||||
OPTION(prefix_1, "summary", summary, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "swaprun:cd", swaprun_cd, Flag, INVALID, swaprun, "cd\0", 0, 0,
|
||||
"Make loader run output binary from swap instead of from CD", nullptr, nullptr)
|
||||
OPTION(prefix_1, "swaprun:net", swaprun_net, Flag, INVALID, swaprun, "net\0", 0, 0,
|
||||
"Make loader run output binary from swap instead of from network", nullptr, nullptr)
|
||||
OPTION(prefix_1, "swaprun:", swaprun, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Comma-separated list of 'cd' or 'net'", nullptr, nullptr)
|
||||
OPTION(prefix_1, "thinlto-emit-imports-files", thinlto_emit_imports_files, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Emit .imports files with -thinlto-index-only", nullptr, nullptr)
|
||||
OPTION(prefix_1, "thinlto-index-only:", thinlto_index_only_arg, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"-thinlto-index-only and also write native module names to file", nullptr, nullptr)
|
||||
OPTION(prefix_1, "thinlto-index-only", thinlto_index_only, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Instead of linking, emit ThinLTO index files", nullptr, nullptr)
|
||||
OPTION(prefix_1, "thinlto-object-suffix-replace:", thinlto_object_suffix_replace, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"'old;new' replace old suffix with new suffix in ThinLTO index", nullptr, nullptr)
|
||||
OPTION(prefix_1, "thinlto-prefix-replace:", thinlto_prefix_replace, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"'old;new' replace old prefix with new prefix in ThinLTO outputs", nullptr, nullptr)
|
||||
OPTION(prefix_1, "threads:no", threads_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not run the linker multi-threaded", nullptr, nullptr)
|
||||
OPTION(prefix_1, "threads", threads, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Run the linker multi-threaded (default)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "throwingnew", throwingnew, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "timestamp:", timestamp, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Specify the PE header timestamp", nullptr, nullptr)
|
||||
OPTION(prefix_1, "time", show_timing, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "tlbid:", tlbid, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "tlbout:", tlbout, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "tsaware:no", tsaware_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Create non-Terminal Server aware executable", nullptr, nullptr)
|
||||
OPTION(prefix_1, "tsaware", tsaware, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Create Terminal Server aware executable (default)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "verbose:", verbose_all, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "verbose", verbose, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "version:", version, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Specify a version number in the PE header", nullptr, nullptr)
|
||||
OPTION(prefix_2, "version", dash_dash_version, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Print version information", nullptr, nullptr)
|
||||
OPTION(prefix_1, "wholearchive:", wholearchive_file, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Include all object files from this archive", nullptr, nullptr)
|
||||
OPTION(prefix_1, "wholearchive", wholearchive_flag, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "WX:no", WX_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Don't treat warnings as errors", nullptr, nullptr)
|
||||
OPTION(prefix_1, "WX", WX, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Treat warnings as errors", nullptr, nullptr)
|
||||
OPTION(prefix_3, "", help_q, Flag, INVALID, help, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
#endif // OPTION
|
||||
|
||||
#ifdef OPTTABLE_ARG_INIT
|
||||
//////////
|
||||
// Option Values
|
||||
|
||||
|
||||
#endif // OPTTABLE_ARG_INIT
|
||||
201
deps/lld-prebuilt/DarwinLdOptions.inc
vendored
201
deps/lld-prebuilt/DarwinLdOptions.inc
vendored
@ -1,201 +0,0 @@
|
||||
/*===- TableGen'erated file -------------------------------------*- C++ -*-===*\
|
||||
|* *|
|
||||
|* Option Parsing Definitions *|
|
||||
|* *|
|
||||
|* Automatically generated file, do not edit! *|
|
||||
|* *|
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
/////////
|
||||
// Prefixes
|
||||
|
||||
#ifdef PREFIX
|
||||
#define COMMA ,
|
||||
PREFIX(prefix_0, {nullptr})
|
||||
PREFIX(prefix_1, {"-" COMMA nullptr})
|
||||
PREFIX(prefix_2, {"-" COMMA "--" COMMA nullptr})
|
||||
#undef COMMA
|
||||
#endif // PREFIX
|
||||
|
||||
/////////
|
||||
// Groups
|
||||
|
||||
#ifdef OPTION
|
||||
OPTION(nullptr, "opts", grp_bundle, Group, INVALID, INVALID, nullptr, 0, 0,
|
||||
"BUNDLE EXECUTABLE OPTIONS", nullptr, nullptr)
|
||||
OPTION(nullptr, "opts", grp_dylib, Group, INVALID, INVALID, nullptr, 0, 0,
|
||||
"DYLIB EXECUTABLE OPTIONS", nullptr, nullptr)
|
||||
OPTION(nullptr, "outs", grp_kind, Group, INVALID, INVALID, nullptr, 0, 0,
|
||||
"OUTPUT KIND", nullptr, nullptr)
|
||||
OPTION(nullptr, "libs", grp_libs, Group, INVALID, INVALID, nullptr, 0, 0,
|
||||
"LIBRARY OPTIONS", nullptr, nullptr)
|
||||
OPTION(nullptr, "opts", grp_main, Group, INVALID, INVALID, nullptr, 0, 0,
|
||||
"MAIN EXECUTABLE OPTIONS", nullptr, nullptr)
|
||||
OPTION(nullptr, "obsolete", grp_obsolete, Group, INVALID, INVALID, nullptr, 0, 0,
|
||||
"OBSOLETE OPTIONS", nullptr, nullptr)
|
||||
OPTION(nullptr, "opts", grp_opts, Group, INVALID, INVALID, nullptr, 0, 0,
|
||||
"OPTIMIZATIONS", nullptr, nullptr)
|
||||
|
||||
//////////
|
||||
// Options
|
||||
|
||||
OPTION(prefix_0, "<input>", INPUT, Input, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_0, "<unknown>", UNKNOWN, Unknown, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "all_load", all_load, Flag, grp_libs, INVALID, nullptr, 0, 0,
|
||||
"Forces all members of all static libraries to be loaded", nullptr, nullptr)
|
||||
OPTION(prefix_1, "arch", arch, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Architecture to link", "<arch-name>", nullptr)
|
||||
OPTION(prefix_1, "bundle_loader", bundle_loader, Separate, grp_bundle, INVALID, nullptr, 0, 0,
|
||||
"The executable that will be loading this Mach-O bundle", "<path>", nullptr)
|
||||
OPTION(prefix_1, "bundle", bundle, Flag, grp_kind, INVALID, nullptr, 0, 0,
|
||||
"Create dynamic bundle", nullptr, nullptr)
|
||||
OPTION(prefix_1, "compatibility_version", compatibility_version, Separate, grp_dylib, INVALID, nullptr, 0, 0,
|
||||
"The dylib's compatibility version", "<version>", nullptr)
|
||||
OPTION(prefix_1, "current_version", current_version, Separate, grp_dylib, INVALID, nullptr, 0, 0,
|
||||
"The dylib's current version", "<version>", nullptr)
|
||||
OPTION(prefix_1, "data_in_code_info", data_in_code_info, Flag, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Force generation of a data in code load command", nullptr, nullptr)
|
||||
OPTION(prefix_1, "dead_strip", dead_strip, Flag, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Remove unreference code and data", nullptr, nullptr)
|
||||
OPTION(prefix_1, "demangle", demangle, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Demangles symbol names in errors and warnings", nullptr, nullptr)
|
||||
OPTION(prefix_1, "dependency_info", dependency_info, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Write binary list of files used during link", "<file>", nullptr)
|
||||
OPTION(prefix_1, "dylib_compatibility_version", dylib_compatibility_version, Separate, INVALID, compatibility_version, nullptr, 0, 0, nullptr, "<version>", nullptr)
|
||||
OPTION(prefix_1, "dylib_current_version", dylib_current_version, Separate, INVALID, current_version, nullptr, 0, 0, nullptr, "<version>", nullptr)
|
||||
OPTION(prefix_1, "dylib_install_name", dylib_install_name, Separate, INVALID, install_name, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "dylib", dylib, Flag, grp_kind, INVALID, nullptr, 0, 0,
|
||||
"Create dynamic library", nullptr, nullptr)
|
||||
OPTION(prefix_1, "dynamic", dynamic, Flag, grp_kind, INVALID, nullptr, 0, 0,
|
||||
"Create dynamic executable (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "error-limit", error_limit, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Maximum number of errors to emit before stopping (0 = no limit)", "<number>", nullptr)
|
||||
OPTION(prefix_1, "execute", execute, Flag, grp_kind, INVALID, nullptr, 0, 0,
|
||||
"Create main executable (default)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "export_dynamic", export_dynamic, Flag, grp_main, INVALID, nullptr, 0, 0,
|
||||
"Preserves all global symbols in main executables during LTO", nullptr, nullptr)
|
||||
OPTION(prefix_1, "exported_symbols_list", exported_symbols_list, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Restricts which symbols will be exported", "<file-path>", nullptr)
|
||||
OPTION(prefix_1, "exported_symbol", exported_symbol, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Restricts which symbols will be exported", "<symbol>", nullptr)
|
||||
OPTION(prefix_1, "e", entry, Separate, grp_main, INVALID, nullptr, 0, 0,
|
||||
"entry symbol name", "<entry-name>", nullptr)
|
||||
OPTION(prefix_1, "filelist", filelist, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"file containing paths to input files", "<path>", nullptr)
|
||||
OPTION(prefix_1, "flat_namespace", flat_namespace, Flag, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Resolves symbols in any (transitively) linked dynamic libraries. Source libraries are not recorded: dyld will re-search all images at runtime and use the first definition found.", nullptr, nullptr)
|
||||
OPTION(prefix_1, "force_load", force_load, Separate, grp_libs, INVALID, nullptr, 0, 0,
|
||||
"Forces all members of specified static libraries to be loaded", "<library-path>", nullptr)
|
||||
OPTION(prefix_1, "framework", framework, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Base name of framework searched for in -F directories", "<name>", nullptr)
|
||||
OPTION(prefix_1, "function_starts", function_starts, Flag, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Force generation of a function starts load command", nullptr, nullptr)
|
||||
OPTION(prefix_1, "F", F, JoinedOrSeparate, grp_libs, INVALID, nullptr, 0, 0,
|
||||
"Add directory to framework search path", "<dir>", nullptr)
|
||||
OPTION(prefix_1, "image_base", image_base, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "install_name", install_name, Separate, grp_dylib, INVALID, nullptr, 0, 0,
|
||||
"The dylib's install name", "<path>", nullptr)
|
||||
OPTION(prefix_1, "ios_simulator_version_min", ios_simulator_version_min, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Minimum iOS simulator version", "<version>", nullptr)
|
||||
OPTION(prefix_1, "ios_version_min", ios_version_min, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Minimum iOS version", "<version>", nullptr)
|
||||
OPTION(prefix_1, "iphoneos_version_min", iphoneos_version_min, Separate, INVALID, ios_version_min, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "keep_private_externs", keep_private_externs, Flag, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Private extern (hidden) symbols should not be transformed into local symbols", nullptr, nullptr)
|
||||
OPTION(prefix_1, "lto_library", lto_library, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Ignored for compatibility with other linkers", "<path>", nullptr)
|
||||
OPTION(prefix_1, "L", L, JoinedOrSeparate, grp_libs, INVALID, nullptr, 0, 0,
|
||||
"Add directory to library search path", "<dir>", nullptr)
|
||||
OPTION(prefix_1, "l", l, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Base name of library searched for in -L directories", "<libname>", nullptr)
|
||||
OPTION(prefix_1, "macosx_version_min", macosx_version_min, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Minimum Mac OS X version", "<version>", nullptr)
|
||||
OPTION(prefix_1, "mark_dead_strippable_dylib", mark_dead_strippable_dylib, Flag, grp_dylib, INVALID, nullptr, 0, 0,
|
||||
"Marks the dylib as having no side effects during initialization", nullptr, nullptr)
|
||||
OPTION(prefix_1, "mllvm", mllvm, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Options to pass to LLVM during LTO", "<option>", nullptr)
|
||||
OPTION(prefix_1, "multi_module", multi_module, Flag, grp_obsolete, INVALID, nullptr, 0, 0,
|
||||
"Unsupported way to build dylibs", nullptr, nullptr)
|
||||
OPTION(prefix_1, "no_data_in_code_info", no_data_in_code_info, Flag, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Disable generation of a data in code load command", nullptr, nullptr)
|
||||
OPTION(prefix_1, "no_function_starts", no_function_starts, Flag, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Disable generation of a function starts load command", nullptr, nullptr)
|
||||
OPTION(prefix_1, "no_objc_category_merging", no_objc_category_merging, Flag, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Disables the optimisation which merges Objective-C categories on a class in to the class itself.", nullptr, nullptr)
|
||||
OPTION(prefix_1, "no_pie", no_pie, Flag, grp_main, INVALID, nullptr, 0, 0,
|
||||
"Do not create Position Independent Executable", nullptr, nullptr)
|
||||
OPTION(prefix_1, "no_version_load_command", no_version_load_command, Flag, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Disable generation of a version load command", nullptr, nullptr)
|
||||
OPTION(prefix_1, "objc_gc_compaction", objc_gc_compaction, Flag, grp_obsolete, INVALID, nullptr, 0, 0,
|
||||
"Unsupported ObjC GC option", nullptr, nullptr)
|
||||
OPTION(prefix_1, "objc_gc_only", objc_gc_only, Flag, grp_obsolete, INVALID, nullptr, 0, 0,
|
||||
"Unsupported ObjC GC option", nullptr, nullptr)
|
||||
OPTION(prefix_1, "objc_gc", objc_gc, Flag, grp_obsolete, INVALID, nullptr, 0, 0,
|
||||
"Unsupported ObjC GC option", nullptr, nullptr)
|
||||
OPTION(prefix_1, "order_file", order_file, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"re-order and move specified symbols to start of their section", "<file-path>", nullptr)
|
||||
OPTION(prefix_1, "o", output, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Output file path", "<path>", nullptr)
|
||||
OPTION(prefix_1, "path_exists", path_exists, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Used with -test_file_usage to declare a path", "<path>", nullptr)
|
||||
OPTION(prefix_1, "pie", pie, Flag, grp_main, INVALID, nullptr, 0, 0,
|
||||
"Create Position Independent Executable (for ASLR)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "preload", preload, Flag, grp_kind, INVALID, nullptr, 0, 0,
|
||||
"Create binary for use with embedded systems", nullptr, nullptr)
|
||||
OPTION(prefix_1, "print_atoms", print_atoms, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Emit output as yaml atoms", nullptr, nullptr)
|
||||
OPTION(prefix_1, "rpath", rpath, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Add path to the runpath search path list for image being created", "<path>", nullptr)
|
||||
OPTION(prefix_1, "r", relocatable, Flag, grp_kind, INVALID, nullptr, 0, 0,
|
||||
"Create relocatable object file", nullptr, nullptr)
|
||||
OPTION(prefix_1, "sdk_version", sdk_version, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"SDK version", "<version>", nullptr)
|
||||
OPTION(prefix_1, "sectalign", sectalign, MultiArg, INVALID, INVALID, nullptr, 0, 3,
|
||||
"Alignment for segment/section", "<segname> <sectname> <alignment>", nullptr)
|
||||
OPTION(prefix_1, "sectcreate", sectcreate, MultiArg, INVALID, INVALID, nullptr, 0, 3,
|
||||
"Create section <segname>/<sectname> from contents of <file>", "<segname> <sectname> <file>", nullptr)
|
||||
OPTION(prefix_1, "seg1addr", seg1addr, Separate, INVALID, image_base, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "single_module", single_module, Flag, grp_obsolete, INVALID, nullptr, 0, 0,
|
||||
"Default for dylibs", nullptr, nullptr)
|
||||
OPTION(prefix_1, "source_version", source_version, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Source version", "<version>", nullptr)
|
||||
OPTION(prefix_1, "stack_size", stack_size, Separate, grp_main, INVALID, nullptr, 0, 0,
|
||||
"Specifies the maximum stack size for the main thread in a program. Must be a page-size multiple. (default=8Mb)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "static", static, Flag, grp_kind, INVALID, nullptr, 0, 0,
|
||||
"Create static executable", nullptr, nullptr)
|
||||
OPTION(prefix_1, "syslibroot", syslibroot, Separate, grp_libs, INVALID, nullptr, 0, 0,
|
||||
"Add path to SDK to all absolute library search paths", "<dir>", nullptr)
|
||||
OPTION(prefix_1, "S", S, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Remove debug information (STABS or DWARF) from the output file", nullptr, nullptr)
|
||||
OPTION(prefix_1, "test_file_usage", test_file_usage, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Only files specified by -file_exists are considered to exist. Print which files would be used", nullptr, nullptr)
|
||||
OPTION(prefix_1, "twolevel_namespace", twolevel_namespace, Flag, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Resolves symbols in listed libraries only. Source libraries are recorded in the symbol table.", nullptr, nullptr)
|
||||
OPTION(prefix_1, "t", t, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Print the names of the input files as ld processes them", nullptr, nullptr)
|
||||
OPTION(prefix_1, "undefined", undefined, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Determines how undefined symbols are handled.", "<undefined>", nullptr)
|
||||
OPTION(prefix_1, "unexported_symbols_list", unexported_symbols_list, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Lists symbols that should not be exported", "<file-path>", nullptr)
|
||||
OPTION(prefix_1, "unexported_symbol", unexported_symbol, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"A symbol which should not be exported", "<symbol>", nullptr)
|
||||
OPTION(prefix_1, "upward-l", upward_l, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Base name of upward library searched for in -L directories", "<libname>", nullptr)
|
||||
OPTION(prefix_1, "upward_framework", upward_framework, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Base name of upward framework searched for in -F directories", "<name>", nullptr)
|
||||
OPTION(prefix_1, "upward_library", upward_library, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"path to upward dylib to link with", "<path>", nullptr)
|
||||
OPTION(prefix_1, "version_load_command", version_load_command, Flag, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Force generation of a version load command", nullptr, nullptr)
|
||||
OPTION(prefix_1, "v", v, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Print linker information", nullptr, nullptr)
|
||||
OPTION(prefix_1, "Z", Z, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not search standard directories for libraries or frameworks", nullptr, nullptr)
|
||||
#endif // OPTION
|
||||
|
||||
#ifdef OPTTABLE_ARG_INIT
|
||||
//////////
|
||||
// Option Values
|
||||
|
||||
|
||||
#endif // OPTTABLE_ARG_INIT
|
||||
598
deps/lld-prebuilt/ELF/Options.inc
vendored
598
deps/lld-prebuilt/ELF/Options.inc
vendored
@ -1,598 +0,0 @@
|
||||
/*===- TableGen'erated file -------------------------------------*- C++ -*-===*\
|
||||
|* *|
|
||||
|* Option Parsing Definitions *|
|
||||
|* *|
|
||||
|* Automatically generated file, do not edit! *|
|
||||
|* *|
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
/////////
|
||||
// Prefixes
|
||||
|
||||
#ifdef PREFIX
|
||||
#define COMMA ,
|
||||
PREFIX(prefix_0, {nullptr})
|
||||
PREFIX(prefix_1, {"-" COMMA nullptr})
|
||||
PREFIX(prefix_4, {"-" COMMA "--" COMMA nullptr})
|
||||
PREFIX(prefix_3, {"--" COMMA nullptr})
|
||||
PREFIX(prefix_2, {"--" COMMA "-" COMMA nullptr})
|
||||
#undef COMMA
|
||||
#endif // PREFIX
|
||||
|
||||
/////////
|
||||
// Groups
|
||||
|
||||
#ifdef OPTION
|
||||
|
||||
//////////
|
||||
// Options
|
||||
|
||||
OPTION(prefix_0, "<input>", INPUT, Input, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_0, "<unknown>", UNKNOWN, Unknown, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "(", anonymous_31, Flag, INVALID, start_group, nullptr, 0, 0,
|
||||
"Alias for --start-group", nullptr, nullptr)
|
||||
OPTION(prefix_1, ")", anonymous_12, Flag, INVALID, end_group, nullptr, 0, 0,
|
||||
"Alias for --end-group", nullptr, nullptr)
|
||||
OPTION(prefix_2, "allow-multiple-definition", allow_multiple_definition, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Allow multiple definitions", nullptr, nullptr)
|
||||
OPTION(prefix_2, "allow-shlib-undefined", allow_shlib_undefined, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Allow unresolved references in shared libraries (default when linking a shared library)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "apply-dynamic-relocs", apply_dynamic_relocs, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Apply link-time values for dynamic relocations", nullptr, nullptr)
|
||||
OPTION(prefix_2, "as-needed", as_needed, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Only set DT_NEEDED for shared libraries if used", nullptr, nullptr)
|
||||
OPTION(prefix_2, "auxiliary=", auxiliary_eq, Joined, INVALID, auxiliary, nullptr, 0, 0,
|
||||
"Set DT_AUXILIARY field to the specified name", nullptr, nullptr)
|
||||
OPTION(prefix_2, "auxiliary", auxiliary, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "Bdynamic", Bdynamic, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Link against shared libraries (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "Bshareable", anonymous_29, Flag, INVALID, shared, nullptr, 0, 0,
|
||||
"Alias for --shared", nullptr, nullptr)
|
||||
OPTION(prefix_2, "Bstatic", Bstatic, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not link against shared libraries", nullptr, nullptr)
|
||||
OPTION(prefix_2, "Bsymbolic-functions", Bsymbolic_functions, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Bind defined function symbols locally", nullptr, nullptr)
|
||||
OPTION(prefix_2, "Bsymbolic", Bsymbolic, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Bind defined symbols locally", nullptr, nullptr)
|
||||
OPTION(prefix_2, "build-id=", build_id_eq, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Generate build ID note", "[fast,md5,sha1,uuid,0x<hexstring>]", nullptr)
|
||||
OPTION(prefix_2, "build-id", build_id, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Alias for --build-id=fast", nullptr, nullptr)
|
||||
OPTION(prefix_1, "b", anonymous_16, Separate, INVALID, format, nullptr, 0, 0,
|
||||
"Alias for --format", nullptr, nullptr)
|
||||
OPTION(prefix_2, "call-graph-ordering-file=", call_graph_ordering_file_eq, Joined, INVALID, call_graph_ordering_file, nullptr, 0, 0,
|
||||
"Layout sections to optimize the given callgraph", nullptr, nullptr)
|
||||
OPTION(prefix_2, "call-graph-ordering-file", call_graph_ordering_file, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "call-graph-profile-sort", call_graph_profile_sort, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Reorder sections with call graph profile (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "call_shared", anonymous_1, Flag, INVALID, Bdynamic, nullptr, 0, 0,
|
||||
"Alias for --Bdynamic", nullptr, nullptr)
|
||||
OPTION(prefix_2, "check-sections", check_sections, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Check section addresses for overlaps (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "chroot", chroot, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "color-diagnostics=", color_diagnostics_eq, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Use colors in diagnostics", "[auto,always,never]", nullptr)
|
||||
OPTION(prefix_2, "color-diagnostics", color_diagnostics, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Alias for --color-diagnostics=always", nullptr, nullptr)
|
||||
OPTION(prefix_2, "compress-debug-sections=", compress_debug_sections_eq, Joined, INVALID, compress_debug_sections, nullptr, 0, 0,
|
||||
"Compress DWARF debug sections", "[none,zlib]", nullptr)
|
||||
OPTION(prefix_2, "compress-debug-sections", compress_debug_sections, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, "[none,zlib]", nullptr)
|
||||
OPTION(prefix_2, "cref", cref, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Output cross reference table", nullptr, nullptr)
|
||||
OPTION(prefix_2, "dc", anonymous_7, Flag, INVALID, define_common, nullptr, 0, 0,
|
||||
"Alias for --define-common", nullptr, nullptr)
|
||||
OPTION(prefix_2, "define-common", define_common, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Assign space to common symbols", nullptr, nullptr)
|
||||
OPTION(prefix_2, "defsym=", defsym_eq, Joined, INVALID, defsym, nullptr, 0, 0,
|
||||
"Define a symbol alias", "<symbol>=<value>", nullptr)
|
||||
OPTION(prefix_2, "defsym", defsym, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, "<symbol>=<value>", nullptr)
|
||||
OPTION(prefix_2, "demangle", demangle, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Demangle symbol names (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "dependent-libraries", dependent_libraries, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Process dependent library specifiers from input files (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "detect-odr-violations", anonymous_50, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "disable-new-dtags", disable_new_dtags, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Disable new dynamic tags", nullptr, nullptr)
|
||||
OPTION(prefix_2, "disable-verify", disable_verify, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "discard-all", discard_all, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Delete all local symbols", nullptr, nullptr)
|
||||
OPTION(prefix_2, "discard-locals", discard_locals, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Delete temporary local symbols", nullptr, nullptr)
|
||||
OPTION(prefix_2, "discard-none", discard_none, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Keep all symbols in the symbol table", nullptr, nullptr)
|
||||
OPTION(prefix_2, "dn", anonymous_3, Flag, INVALID, Bstatic, nullptr, 0, 0,
|
||||
"Alias for --Bstatic", nullptr, nullptr)
|
||||
OPTION(prefix_2, "dp", anonymous_8, Flag, INVALID, define_common, nullptr, 0, 0,
|
||||
"Alias for --define-common", nullptr, nullptr)
|
||||
OPTION(prefix_2, "dynamic-linker=", dynamic_linker_eq, Joined, INVALID, dynamic_linker, nullptr, 0, 0,
|
||||
"Which dynamic linker to use", nullptr, nullptr)
|
||||
OPTION(prefix_2, "dynamic-linker", dynamic_linker, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "dynamic-list=", dynamic_list_eq, Joined, INVALID, dynamic_list, nullptr, 0, 0,
|
||||
"Read a list of dynamic symbols", nullptr, nullptr)
|
||||
OPTION(prefix_2, "dynamic-list", dynamic_list, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "dy", anonymous_2, Flag, INVALID, Bdynamic, nullptr, 0, 0,
|
||||
"Alias for --Bdynamic", nullptr, nullptr)
|
||||
OPTION(prefix_1, "d", anonymous_6, Flag, INVALID, define_common, nullptr, 0, 0,
|
||||
"Alias for --define-common", nullptr, nullptr)
|
||||
OPTION(prefix_2, "EB", anonymous_69, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "eh-frame-hdr", eh_frame_hdr, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header", nullptr, nullptr)
|
||||
OPTION(prefix_2, "EL", anonymous_70, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "emit-relocs", emit_relocs, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Generate relocations in output", nullptr, nullptr)
|
||||
OPTION(prefix_2, "enable-new-dtags", enable_new_dtags, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Enable new dynamic tags (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "end-group", end_group, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Ignored for compatibility with GNU unless you pass --warn-backrefs", nullptr, nullptr)
|
||||
OPTION(prefix_2, "end-lib", end_lib, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"End a grouping of objects that should be treated as if they were together in an archive", nullptr, nullptr)
|
||||
OPTION(prefix_2, "entry=", entry_eq, Joined, INVALID, entry, nullptr, 0, 0,
|
||||
"Name of entry point symbol", "<entry>", nullptr)
|
||||
OPTION(prefix_2, "entry", entry, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, "<entry>", nullptr)
|
||||
OPTION(prefix_2, "error-limit=", error_limit_eq, Joined, INVALID, error_limit, nullptr, 0, 0,
|
||||
"Maximum number of errors to emit before stopping (0 = no limit)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "error-limit", error_limit, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "error-unresolved-symbols", error_unresolved_symbols, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Report unresolved symbols as errors", nullptr, nullptr)
|
||||
OPTION(prefix_2, "exclude-libs=", exclude_libs_eq, Joined, INVALID, exclude_libs, nullptr, 0, 0,
|
||||
"Exclude static libraries from automatic export", nullptr, nullptr)
|
||||
OPTION(prefix_2, "exclude-libs", exclude_libs, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "execute-only", execute_only, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Mark executable sections unreadable", nullptr, nullptr)
|
||||
OPTION(prefix_2, "export-dynamic-symbol=", export_dynamic_symbol_eq, Joined, INVALID, export_dynamic_symbol, nullptr, 0, 0,
|
||||
"Put a symbol in the dynamic symbol table", nullptr, nullptr)
|
||||
OPTION(prefix_2, "export-dynamic-symbol", export_dynamic_symbol, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "export-dynamic", export_dynamic, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Put symbols in the dynamic symbol table", nullptr, nullptr)
|
||||
OPTION(prefix_1, "E", anonymous_14, Flag, INVALID, export_dynamic, nullptr, 0, 0,
|
||||
"Alias for --export-dynamic", nullptr, nullptr)
|
||||
OPTION(prefix_1, "e", anonymous_13, JoinedOrSeparate, INVALID, entry, nullptr, 0, 0,
|
||||
"Alias for --entry", nullptr, nullptr)
|
||||
OPTION(prefix_2, "fatal-warnings", fatal_warnings, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Treat warnings as errors", nullptr, nullptr)
|
||||
OPTION(prefix_2, "filter=", filter_eq, Joined, INVALID, filter, nullptr, 0, 0,
|
||||
"Set DT_FILTER field to the specified name", nullptr, nullptr)
|
||||
OPTION(prefix_2, "filter", filter, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "fini=", fini_eq, Joined, INVALID, fini, nullptr, 0, 0,
|
||||
"Specify a finalizer function", "<symbol>", nullptr)
|
||||
OPTION(prefix_2, "fini", fini, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, "<symbol>", nullptr)
|
||||
OPTION(prefix_2, "fix-cortex-a53-843419", fix_cortex_a53_843419, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Apply fixes for AArch64 Cortex-A53 erratum 843419", nullptr, nullptr)
|
||||
OPTION(prefix_2, "force-bti", force_bti, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Force enable AArch64 BTI in PLT, warn if Input ELF file does not have GNU_PROPERTY_AARCH64_FEATURE_1_BTI property", nullptr, nullptr)
|
||||
OPTION(prefix_2, "format=", format_eq, Joined, INVALID, format, nullptr, 0, 0,
|
||||
"Change the input format of the inputs following this option", "[default,elf,binary]", nullptr)
|
||||
OPTION(prefix_2, "format", format, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, "[default,elf,binary]", nullptr)
|
||||
OPTION(prefix_1, "F", anonymous_15, Separate, INVALID, filter, nullptr, 0, 0,
|
||||
"Alias for --filter", nullptr, nullptr)
|
||||
OPTION(prefix_1, "f", anonymous_0, Separate, INVALID, auxiliary, nullptr, 0, 0,
|
||||
"Alias for --auxiliary", nullptr, nullptr)
|
||||
OPTION(prefix_2, "gc-sections", gc_sections, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Enable garbage collection of unused sections", nullptr, nullptr)
|
||||
OPTION(prefix_2, "gdb-index", gdb_index, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Generate .gdb_index section", nullptr, nullptr)
|
||||
OPTION(prefix_2, "gnu-unique", gnu_unique, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Enable STB_GNU_UNIQUE symbol binding (default)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "G", anonymous_71, JoinedOrSeparate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "g", anonymous_51, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "hash-style=", hash_style_eq, Joined, INVALID, hash_style, nullptr, 0, 0,
|
||||
"Specify hash style (sysv, gnu or both)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "hash-style", hash_style, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "help", help, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Print option help", nullptr, nullptr)
|
||||
OPTION(prefix_1, "h", anonymous_30, JoinedOrSeparate, INVALID, soname, nullptr, 0, 0,
|
||||
"Alias for --soname", nullptr, nullptr)
|
||||
OPTION(prefix_2, "icf=all", icf_all, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Enable identical code folding", nullptr, nullptr)
|
||||
OPTION(prefix_2, "icf=none", icf_none, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Disable identical code folding (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "icf=safe", icf_safe, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Enable safe identical code folding", nullptr, nullptr)
|
||||
OPTION(prefix_2, "ignore-data-address-equality", ignore_data_address_equality, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"lld can break the address equality of data", nullptr, nullptr)
|
||||
OPTION(prefix_2, "ignore-function-address-equality", ignore_function_address_equality, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"lld can break the address equality of functions", nullptr, nullptr)
|
||||
OPTION(prefix_2, "image-base=", image_base_eq, Joined, INVALID, image_base, nullptr, 0, 0,
|
||||
"Set the base address", nullptr, nullptr)
|
||||
OPTION(prefix_2, "image-base", image_base, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "init=", init_eq, Joined, INVALID, init, nullptr, 0, 0,
|
||||
"Specify an initializer function", "<symbol>", nullptr)
|
||||
OPTION(prefix_2, "init", init, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, "<symbol>", nullptr)
|
||||
OPTION(prefix_2, "just-symbols=", just_symbols_eq, Joined, INVALID, just_symbols, nullptr, 0, 0,
|
||||
"Just link symbols", nullptr, nullptr)
|
||||
OPTION(prefix_2, "just-symbols", just_symbols, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "keep-unique=", keep_unique_eq, Joined, INVALID, keep_unique, nullptr, 0, 0,
|
||||
"Do not fold this symbol during ICF", nullptr, nullptr)
|
||||
OPTION(prefix_2, "keep-unique", keep_unique, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "library-path=", library_path_eq, Joined, INVALID, library_path, nullptr, 0, 0,
|
||||
"Add a directory to the library search path", "<dir>", nullptr)
|
||||
OPTION(prefix_2, "library-path", library_path, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, "<dir>", nullptr)
|
||||
OPTION(prefix_2, "library=", library_eq, Joined, INVALID, library, nullptr, 0, 0,
|
||||
"Root name of library to use", "<libName>", nullptr)
|
||||
OPTION(prefix_2, "library", library, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, "<libName>", nullptr)
|
||||
OPTION(prefix_2, "long-plt", anonymous_52, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "lto-aa-pipeline=", lto_aa_pipeline, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"AA pipeline to run during LTO. Used in conjunction with -lto-newpm-passes", nullptr, nullptr)
|
||||
OPTION(prefix_2, "lto-cs-profile-file=", lto_cs_profile_file, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Context sensitive profile file path", nullptr, nullptr)
|
||||
OPTION(prefix_2, "lto-cs-profile-generate", lto_cs_profile_generate, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Perform context senstive PGO instrumentation", nullptr, nullptr)
|
||||
OPTION(prefix_2, "lto-debug-pass-manager", lto_debug_pass_manager, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Debug new pass manager", nullptr, nullptr)
|
||||
OPTION(prefix_2, "lto-new-pass-manager", lto_new_pass_manager, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Use new pass manager", nullptr, nullptr)
|
||||
OPTION(prefix_2, "lto-newpm-passes=", lto_newpm_passes, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Passes to run during LTO", nullptr, nullptr)
|
||||
OPTION(prefix_2, "lto-O", lto_O, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Optimization level for LTO", "<opt-level>", nullptr)
|
||||
OPTION(prefix_2, "lto-partitions=", lto_partitions, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Number of LTO codegen partitions", nullptr, nullptr)
|
||||
OPTION(prefix_2, "lto-sample-profile=", lto_sample_profile, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Sample profile file path", nullptr, nullptr)
|
||||
OPTION(prefix_1, "L", anonymous_18, JoinedOrSeparate, INVALID, library_path, nullptr, 0, 0,
|
||||
"Alias for --library-path", nullptr, nullptr)
|
||||
OPTION(prefix_1, "l", anonymous_17, JoinedOrSeparate, INVALID, library, nullptr, 0, 0,
|
||||
"Alias for --library", nullptr, nullptr)
|
||||
OPTION(prefix_2, "Map=", Map_eq, Joined, INVALID, Map, nullptr, 0, 0,
|
||||
"Print a link map to the specified file", nullptr, nullptr)
|
||||
OPTION(prefix_2, "Map", Map, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "merge-exidx-entries", merge_exidx_entries, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Enable merging .ARM.exidx entries (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "mips-got-size=", mips_got_size_eq, Joined, INVALID, mips_got_size, nullptr, HelpHidden, 0,
|
||||
"Max size of a single MIPS GOT. 0x10000 by default.", nullptr, nullptr)
|
||||
OPTION(prefix_2, "mips-got-size", mips_got_size, Separate, INVALID, INVALID, nullptr, HelpHidden, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "mllvm=", mllvm_eq, Joined, INVALID, mllvm, nullptr, 0, 0,
|
||||
"Additional arguments to forward to LLVM's option processing", nullptr, nullptr)
|
||||
OPTION(prefix_2, "mllvm", mllvm, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "M", anonymous_25, Flag, INVALID, print_map, nullptr, 0, 0,
|
||||
"Alias for --print-map", nullptr, nullptr)
|
||||
OPTION(prefix_1, "m", m, JoinedOrSeparate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Set target emulation", nullptr, nullptr)
|
||||
OPTION(prefix_2, "nmagic", nmagic, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not page align sections, link against static libraries.", "<magic>", nullptr)
|
||||
OPTION(prefix_2, "no-add-needed", anonymous_53, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-allow-multiple-definition", no_allow_multiple_definition, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not allow multiple definitions (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-allow-shlib-undefined", no_allow_shlib_undefined, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not allow unresolved references in shared libraries (default when linking an executable)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-apply-dynamic-relocs", no_apply_dynamic_relocs, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not apply link-time values for dynamic relocations (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-as-needed", no_as_needed, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Always set DT_NEEDED for shared libraries (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-call-graph-profile-sort", no_call_graph_profile_sort, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not reorder sections with call graph profile", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-check-sections", no_check_sections, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not check section addresses for overlaps", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-color-diagnostics", no_color_diagnostics, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not use colors in diagnostics", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-copy-dt-needed-entries", anonymous_54, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-cref", no_cref, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not output cross reference table", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-ctors-in-init-array", anonymous_55, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-define-common", no_define_common, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not assign space to common symbols", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-demangle", no_demangle, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not demangle symbol names", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-dependent-libraries", no_dependent_libraries, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Ignore dependent library specifiers from input files", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-dynamic-linker", no_dynamic_linker, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Inhibit output of .interp section", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-eh-frame-hdr", no_eh_frame_hdr, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not create .eh_frame_hdr section", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-execute-only", no_execute_only, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Mark executable sections readable (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-export-dynamic", no_export_dynamic, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not put symbols in the dynamic symbol table (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-fatal-warnings", no_fatal_warnings, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not treat warnings as errors (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-gc-sections", no_gc_sections, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Disable garbage collection of unused sections (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-gdb-index", no_gdb_index, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not generate .gdb_index section (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-gnu-unique", no_gnu_unique, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Disable STB_GNU_UNIQUE symbol binding", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-keep-memory", anonymous_56, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-merge-exidx-entries", no_merge_exidx_entries, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Disable merging .ARM.exidx entries", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-mmap-output-file", anonymous_57, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-nmagic", no_nmagic, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Page align sections (default)", "<magic>", nullptr)
|
||||
OPTION(prefix_2, "no-omagic", no_omagic, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not set the text data sections to be writable, page align sections (default)", "<magic>", nullptr)
|
||||
OPTION(prefix_2, "no-pic-executable", anonymous_19, Flag, INVALID, no_pie, nullptr, 0, 0,
|
||||
"Alias for --no-pie", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-pie", no_pie, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not create a position independent executable (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-pipeline-knowledge", anonymous_58, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-print-gc-sections", no_print_gc_sections, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not list removed unused sections (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-print-icf-sections", no_print_icf_sections, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not list identical folded sections (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-rosegment", no_rosegment, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not put read-only non-executable sections in their own segment", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-threads", no_threads, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not run the linker multi-threaded", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-toc-optimize", no_toc_optimize, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"(PowerPC64) Disable TOC related optimizations", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-undefined-version", no_undefined_version, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Report version scripts that refer undefined symbols", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-undefined", no_undefined, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Report unresolved symbols even if the linker is creating a shared library", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-use-android-relr-tags", no_use_android_relr_tags, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Use SHT_RELR / DT_RELR* tags (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-warn-backrefs", no_warn_backrefs, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not warn about backward symbol references to fetch archive members (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-warn-common", no_warn_common, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not warn about duplicate common symbols (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-warn-ifunc-textrel", no_warn_ifunc_textrel, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not warn about using ifunc symbols with text relocations (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-warn-mismatch", anonymous_59, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-warn-symbol-ordering", no_warn_symbol_ordering, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not warn about problems with the symbol ordering file", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-whole-archive", no_whole_archive, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not force load of all members in a static library (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "noinhibit-exec", noinhibit_exec, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Retain the executable output file whenever it is still usable", nullptr, nullptr)
|
||||
OPTION(prefix_2, "non_shared", anonymous_4, Flag, INVALID, Bstatic, nullptr, 0, 0,
|
||||
"Alias for --Bstatic", nullptr, nullptr)
|
||||
OPTION(prefix_2, "nostdlib", nostdlib, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Only search directories specified on the command line", nullptr, nullptr)
|
||||
OPTION(prefix_1, "N", anonymous_21, Flag, INVALID, omagic, nullptr, 0, 0,
|
||||
"Alias for --omagic", nullptr, nullptr)
|
||||
OPTION(prefix_1, "n", anonymous_20, Flag, INVALID, nmagic, nullptr, 0, 0,
|
||||
"Alias for --nmagic", nullptr, nullptr)
|
||||
OPTION(prefix_3, "oformat", oformat, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Specify the binary format for the output object file", "<format>", nullptr)
|
||||
OPTION(prefix_3, "omagic", omagic, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Set the text and data sections to be readable and writable, do not page align sections, link against static libraries", "<magic>", nullptr)
|
||||
OPTION(prefix_3, "opt-remarks-filename", opt_remarks_filename, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"YAML output file for optimization remarks", nullptr, nullptr)
|
||||
OPTION(prefix_3, "opt-remarks-format", opt_remarks_format, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"The format used for serializing remarks (default: YAML)", nullptr, nullptr)
|
||||
OPTION(prefix_3, "opt-remarks-passes", opt_remarks_passes, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Regex for the passes that need to be serialized to the output file", nullptr, nullptr)
|
||||
OPTION(prefix_3, "opt-remarks-with-hotness", opt_remarks_with_hotness, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Include hotness information in the optimization remarks file", nullptr, nullptr)
|
||||
OPTION(prefix_2, "orphan-handling=", orphan_handling_eq, Joined, INVALID, orphan_handling, nullptr, 0, 0,
|
||||
"Control how orphan sections are handled when linker script used", nullptr, nullptr)
|
||||
OPTION(prefix_2, "orphan-handling", orphan_handling, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_3, "output=", anonymous_22, Joined, INVALID, o, nullptr, 0, 0,
|
||||
"Alias for -o", nullptr, nullptr)
|
||||
OPTION(prefix_3, "output", anonymous_23, Separate, INVALID, o, nullptr, 0, 0,
|
||||
"Alias for -o", nullptr, nullptr)
|
||||
OPTION(prefix_1, "O", O, JoinedOrSeparate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Optimize output file size", nullptr, nullptr)
|
||||
OPTION(prefix_1, "o", o, JoinedOrSeparate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Path to file to write output", "<path>", nullptr)
|
||||
OPTION(prefix_2, "pac-plt", pac_plt, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"AArch64 only, use pointer authentication in PLT", nullptr, nullptr)
|
||||
OPTION(prefix_2, "pack-dyn-relocs=", pack_dyn_relocs_eq, Joined, INVALID, pack_dyn_relocs, nullptr, 0, 0,
|
||||
"Pack dynamic relocations in the given format", "[none,android,relr,android+relr]", nullptr)
|
||||
OPTION(prefix_2, "pack-dyn-relocs", pack_dyn_relocs, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, "[none,android,relr,android+relr]", nullptr)
|
||||
OPTION(prefix_2, "pic-executable", anonymous_24, Flag, INVALID, pie, nullptr, 0, 0,
|
||||
"Alias for --pie", nullptr, nullptr)
|
||||
OPTION(prefix_2, "pic-veneer", pic_veneer, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Always generate position independent thunks (veneers)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "pie", pie, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Create a position independent executable", nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=-fresolution=", plugin_opt_fresolution_eq, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=-pass-through=", plugin_opt_pass_through_eq, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=/", plugin_opt_slash, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=cs-profile-generate", anonymous_46, Flag, INVALID, lto_cs_profile_generate, nullptr, 0, 0,
|
||||
"Alias for -lto-cs-profile-generate", nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=cs-profile-path=", anonymous_47, Joined, INVALID, lto_cs_profile_file, nullptr, 0, 0,
|
||||
"Alias for -lto-cs-profile-file", nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=debug-pass-manager", anonymous_41, Flag, INVALID, lto_debug_pass_manager, nullptr, 0, 0,
|
||||
"Alias for -lto-debug-pass-manager", nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=disable-verify", anonymous_42, Flag, INVALID, disable_verify, nullptr, 0, 0,
|
||||
"Alias for -disable-verify", nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=dwo_dir=", plugin_opt_dwo_dir_eq, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Directory to store .dwo files when LTO and debug fission are used", nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=emit-llvm", plugin_opt_emit_llvm, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=jobs=", anonymous_43, Joined, INVALID, thinlto_jobs, nullptr, 0, 0,
|
||||
"Alias for -thinlto-jobs", nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=lto-partitions=", anonymous_44, Joined, INVALID, lto_partitions, nullptr, 0, 0,
|
||||
"Alias for -lto-partitions", nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=mcpu=", plugin_opt_mcpu_eq, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=new-pass-manager", anonymous_45, Flag, INVALID, lto_new_pass_manager, nullptr, 0, 0,
|
||||
"Alias for -lto-new-pass-manager", nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=obj-path=", plugin_opt_obj_path_eq, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=O", anonymous_40, Joined, INVALID, lto_O, nullptr, 0, 0,
|
||||
"Alias for -lto-O", nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=sample-profile=", anonymous_48, Joined, INVALID, lto_sample_profile, nullptr, 0, 0,
|
||||
"Alias for -lto-sample-profile", nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=save-temps", anonymous_49, Flag, INVALID, save_temps, nullptr, 0, 0,
|
||||
"Alias for -save-temps", nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=thinlto-emit-imports-files", plugin_opt_thinlto_emit_imports_files, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=thinlto-index-only=", plugin_opt_thinlto_index_only_eq, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=thinlto-index-only", plugin_opt_thinlto_index_only, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=thinlto-object-suffix-replace=", plugin_opt_thinlto_object_suffix_replace_eq, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=thinlto-prefix-replace=", plugin_opt_thinlto_prefix_replace_eq, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=thinlto", plugin_opt_thinlto, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=", plugin_opt_eq, Joined, INVALID, plugin_opt, nullptr, 0, 0,
|
||||
"specifies LTO options for compatibility with GNU linkers", nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt", plugin_opt, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin=", plugin_eq, Joined, INVALID, plugin, nullptr, 0, 0,
|
||||
"Ignored for compatibility with GNU linkers", nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin", plugin, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "pop-state", pop_state, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Undo the effect of -push-state", nullptr, nullptr)
|
||||
OPTION(prefix_2, "print-gc-sections", print_gc_sections, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"List removed unused sections", nullptr, nullptr)
|
||||
OPTION(prefix_2, "print-icf-sections", print_icf_sections, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"List identical folded sections", nullptr, nullptr)
|
||||
OPTION(prefix_2, "print-map", print_map, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Print a link map to the standard output", nullptr, nullptr)
|
||||
OPTION(prefix_2, "print-symbol-order=", print_symbol_order_eq, Joined, INVALID, print_symbol_order, nullptr, 0, 0,
|
||||
"Print a symbol order specified by --call-graph-ordering-file into the speficied file", nullptr, nullptr)
|
||||
OPTION(prefix_2, "print-symbol-order", print_symbol_order, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "push-state", push_state, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Save the current state of -as-needed, -static and -whole-archive", nullptr, nullptr)
|
||||
OPTION(prefix_1, "p", anonymous_60, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "Qy", anonymous_72, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "q", anonymous_11, Flag, INVALID, emit_relocs, nullptr, 0, 0,
|
||||
"Alias for --emit-relocs", nullptr, nullptr)
|
||||
OPTION(prefix_2, "relocatable", relocatable, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Create relocatable object file", nullptr, nullptr)
|
||||
OPTION(prefix_2, "reproduce=", reproduce_eq, Joined, INVALID, reproduce, nullptr, 0, 0,
|
||||
"Dump linker invocation and input files for debugging", nullptr, nullptr)
|
||||
OPTION(prefix_2, "reproduce", reproduce, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "require-cet", require_cet, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "retain-symbols-file=", retain_symbols_file_eq, Joined, INVALID, retain_symbols_file, nullptr, 0, 0,
|
||||
"Retain only the symbols listed in the file", "<file>", nullptr)
|
||||
OPTION(prefix_2, "retain-symbols-file", retain_symbols_file, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, "<file>", nullptr)
|
||||
OPTION(prefix_2, "rpath-link=", anonymous_62, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "rpath-link", anonymous_61, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "rpath=", rpath_eq, Joined, INVALID, rpath, nullptr, 0, 0,
|
||||
"Add a DT_RUNPATH to the output", nullptr, nullptr)
|
||||
OPTION(prefix_2, "rpath", rpath, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "rsp-quoting=", rsp_quoting_eq, Joined, INVALID, rsp_quoting, nullptr, 0, 0,
|
||||
"Quoting style for response files", "[posix,windows]", nullptr)
|
||||
OPTION(prefix_2, "rsp-quoting", rsp_quoting, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, "[posix,windows]", nullptr)
|
||||
OPTION(prefix_1, "R", anonymous_27, JoinedOrSeparate, INVALID, rpath, nullptr, 0, 0,
|
||||
"Alias for --rpath", nullptr, nullptr)
|
||||
OPTION(prefix_1, "r", anonymous_26, Flag, INVALID, relocatable, nullptr, 0, 0,
|
||||
"Alias for --relocatable", nullptr, nullptr)
|
||||
OPTION(prefix_2, "save-temps", save_temps, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "script=", script_eq, Joined, INVALID, script, nullptr, 0, 0,
|
||||
"Read linker script", nullptr, nullptr)
|
||||
OPTION(prefix_2, "script", script, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "section-start=", section_start_eq, Joined, INVALID, section_start, nullptr, 0, 0,
|
||||
"Set address of section", "<address>", nullptr)
|
||||
OPTION(prefix_2, "section-start", section_start, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, "<address>", nullptr)
|
||||
OPTION(prefix_2, "secure-plt", anonymous_63, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "shared", shared, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Build a shared object", nullptr, nullptr)
|
||||
OPTION(prefix_2, "soname=", soname_eq, Joined, INVALID, soname, nullptr, 0, 0,
|
||||
"Set DT_SONAME", nullptr, nullptr)
|
||||
OPTION(prefix_2, "soname", soname, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "sort-common", anonymous_64, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "sort-section=", sort_section_eq, Joined, INVALID, sort_section, nullptr, 0, 0,
|
||||
"Specifies sections sorting rule when linkerscript is used", nullptr, nullptr)
|
||||
OPTION(prefix_2, "sort-section", sort_section, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "split-stack-adjust-size=", split_stack_adjust_size_eq, Joined, INVALID, split_stack_adjust_size, nullptr, 0, 0,
|
||||
"Specify adjustment to stack size when a split-stack function calls a non-split-stack function", "<value>", nullptr)
|
||||
OPTION(prefix_2, "split-stack-adjust-size", split_stack_adjust_size, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, "<value>", nullptr)
|
||||
OPTION(prefix_2, "start-group", start_group, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Ignored for compatibility with GNU unless you pass --warn-backrefs", nullptr, nullptr)
|
||||
OPTION(prefix_2, "start-lib", start_lib, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Start a grouping of objects that should be treated as if they were together in an archive", nullptr, nullptr)
|
||||
OPTION(prefix_2, "static", anonymous_5, Flag, INVALID, Bstatic, nullptr, 0, 0,
|
||||
"Alias for --Bstatic", nullptr, nullptr)
|
||||
OPTION(prefix_2, "stats", anonymous_65, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "strip-all", strip_all, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Strip all symbols", nullptr, nullptr)
|
||||
OPTION(prefix_2, "strip-debug", strip_debug, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Strip debugging information", nullptr, nullptr)
|
||||
OPTION(prefix_2, "symbol-ordering-file=", symbol_ordering_file_eq, Joined, INVALID, symbol_ordering_file, nullptr, 0, 0,
|
||||
"Layout sections to place symbols in the order specified by symbol ordering file", nullptr, nullptr)
|
||||
OPTION(prefix_2, "symbol-ordering-file", symbol_ordering_file, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "sysroot=", sysroot_eq, Joined, INVALID, sysroot, nullptr, 0, 0,
|
||||
"Set the system root", nullptr, nullptr)
|
||||
OPTION(prefix_2, "sysroot", sysroot, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "S", anonymous_33, Flag, INVALID, strip_debug, nullptr, 0, 0,
|
||||
"Alias for --strip-debug", nullptr, nullptr)
|
||||
OPTION(prefix_1, "s", anonymous_32, Flag, INVALID, strip_all, nullptr, 0, 0,
|
||||
"Alias for --strip-all", nullptr, nullptr)
|
||||
OPTION(prefix_2, "target1-abs", target1_abs, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Interpret R_ARM_TARGET1 as R_ARM_ABS32 (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "target1-rel", target1_rel, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Interpret R_ARM_TARGET1 as R_ARM_REL32", nullptr, nullptr)
|
||||
OPTION(prefix_2, "target2=", target2_eq, Joined, INVALID, target2, nullptr, 0, 0,
|
||||
"Interpret R_ARM_TARGET2 as <type>, where <type> is one of rel, abs, or got-rel", "<type>", nullptr)
|
||||
OPTION(prefix_2, "target2", target2, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, "<type>", nullptr)
|
||||
OPTION(prefix_2, "Tbss=", Tbss_eq, Joined, INVALID, Tbss, nullptr, 0, 0,
|
||||
"Same as --section-start with .bss as the sectionname", nullptr, nullptr)
|
||||
OPTION(prefix_2, "Tbss", Tbss, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "Tdata=", Tdata_eq, Joined, INVALID, Tdata, nullptr, 0, 0,
|
||||
"Same as --section-start with .data as the sectionname", nullptr, nullptr)
|
||||
OPTION(prefix_2, "Tdata", Tdata, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "thinlto-cache-dir=", thinlto_cache_dir, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Path to ThinLTO cached object file directory", nullptr, nullptr)
|
||||
OPTION(prefix_2, "thinlto-cache-policy=", thinlto_cache_policy_eq, Joined, INVALID, thinlto_cache_policy, nullptr, 0, 0,
|
||||
"Pruning policy for the ThinLTO cache", nullptr, nullptr)
|
||||
OPTION(prefix_2, "thinlto-cache-policy", thinlto_cache_policy, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "thinlto-jobs=", thinlto_jobs, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Number of ThinLTO jobs", nullptr, nullptr)
|
||||
OPTION(prefix_2, "threads", threads, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Run the linker multi-threaded (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "toc-optimize", toc_optimize, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"(PowerPC64) Enable TOC related optimizations (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "trace-symbol=", trace_symbol_eq, Joined, INVALID, trace_symbol, nullptr, 0, 0,
|
||||
"Trace references to symbols", nullptr, nullptr)
|
||||
OPTION(prefix_2, "trace-symbol", trace_symbol, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "trace", trace, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Print the names of the input files", nullptr, nullptr)
|
||||
OPTION(prefix_4, "Ttext-segment=", anonymous_37, Joined, INVALID, Ttext, nullptr, 0, 0,
|
||||
"Alias for --Ttext", nullptr, nullptr)
|
||||
OPTION(prefix_4, "Ttext-segment", anonymous_36, Separate, INVALID, Ttext, nullptr, 0, 0,
|
||||
"Alias for --Ttext", nullptr, nullptr)
|
||||
OPTION(prefix_2, "Ttext=", Ttext_eq, Joined, INVALID, Ttext, nullptr, 0, 0,
|
||||
"Same as --section-start with .text as the sectionname", nullptr, nullptr)
|
||||
OPTION(prefix_2, "Ttext", Ttext, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "T", anonymous_28, JoinedOrSeparate, INVALID, script, nullptr, 0, 0,
|
||||
"Alias for --script", nullptr, nullptr)
|
||||
OPTION(prefix_1, "t", anonymous_34, Flag, INVALID, trace, nullptr, 0, 0,
|
||||
"Alias for --trace", nullptr, nullptr)
|
||||
OPTION(prefix_2, "undefined-glob=", undefined_glob_eq, Joined, INVALID, undefined_glob, nullptr, 0, 0,
|
||||
"Force undefined symbol during linking", "<pattern>", nullptr)
|
||||
OPTION(prefix_2, "undefined-glob", undefined_glob, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, "<pattern>", nullptr)
|
||||
OPTION(prefix_2, "undefined-version", undefined_version, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Allow unused version in version script (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "undefined=", undefined_eq, Joined, INVALID, undefined, nullptr, 0, 0,
|
||||
"Force undefined symbol during linking", "<symbol>", nullptr)
|
||||
OPTION(prefix_2, "undefined", undefined, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, "<symbol>", nullptr)
|
||||
OPTION(prefix_2, "unresolved-symbols=", unresolved_symbols_eq, Joined, INVALID, unresolved_symbols, nullptr, 0, 0,
|
||||
"Determine how to handle unresolved symbols", nullptr, nullptr)
|
||||
OPTION(prefix_2, "unresolved-symbols", unresolved_symbols, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "use-android-relr-tags", use_android_relr_tags, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Use SHT_ANDROID_RELR / DT_ANDROID_RELR* tags instead of SHT_RELR / DT_RELR*", nullptr, nullptr)
|
||||
OPTION(prefix_1, "u", anonymous_38, JoinedOrSeparate, INVALID, undefined, nullptr, 0, 0,
|
||||
"Alias for --undefined", nullptr, nullptr)
|
||||
OPTION(prefix_2, "verbose", verbose, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Verbose mode", nullptr, nullptr)
|
||||
OPTION(prefix_2, "version-script=", version_script_eq, Joined, INVALID, version_script, nullptr, 0, 0,
|
||||
"Read a version script", nullptr, nullptr)
|
||||
OPTION(prefix_2, "version-script", version_script, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "version", version, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Display the version number and exit", nullptr, nullptr)
|
||||
OPTION(prefix_2, "vs-diagnostics", visual_studio_diagnostics_format, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Format diagnostics for Visual Studio compatiblity", nullptr, nullptr)
|
||||
OPTION(prefix_1, "V", anonymous_39, Flag, INVALID, version, nullptr, 0, 0,
|
||||
"Alias for --version", nullptr, nullptr)
|
||||
OPTION(prefix_1, "v", v, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Display the version number", nullptr, nullptr)
|
||||
OPTION(prefix_2, "warn-backrefs", warn_backrefs, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Warn about backward symbol references to fetch archive members", nullptr, nullptr)
|
||||
OPTION(prefix_2, "warn-common", warn_common, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Warn about duplicate common symbols", nullptr, nullptr)
|
||||
OPTION(prefix_2, "warn-execstack", anonymous_66, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "warn-ifunc-textrel", warn_ifunc_textrel, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Warn about using ifunc symbols with text relocations", nullptr, nullptr)
|
||||
OPTION(prefix_2, "warn-once", anonymous_67, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "warn-shared-textrel", anonymous_68, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "warn-symbol-ordering", warn_symbol_ordering, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Warn about problems with the symbol ordering file (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "warn-unresolved-symbols", warn_unresolved_symbols, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Report unresolved symbols as warnings", nullptr, nullptr)
|
||||
OPTION(prefix_2, "whole-archive", whole_archive, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Force load of all members in a static library", nullptr, nullptr)
|
||||
OPTION(prefix_2, "wrap=", wrap_eq, Joined, INVALID, wrap, nullptr, 0, 0,
|
||||
"Use wrapper functions for symbol", "<symbol>=<symbol>", nullptr)
|
||||
OPTION(prefix_2, "wrap", wrap, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, "<symbol>=<symbol>", nullptr)
|
||||
OPTION(prefix_1, "X", anonymous_10, Flag, INVALID, discard_locals, nullptr, 0, 0,
|
||||
"Alias for --discard-locals", nullptr, nullptr)
|
||||
OPTION(prefix_1, "x", anonymous_9, Flag, INVALID, discard_all, nullptr, 0, 0,
|
||||
"Alias for --discard-all", nullptr, nullptr)
|
||||
OPTION(prefix_1, "y", anonymous_35, JoinedOrSeparate, INVALID, trace_symbol, nullptr, 0, 0,
|
||||
"Alias for --trace-symbol", nullptr, nullptr)
|
||||
OPTION(prefix_1, "z", z, JoinedOrSeparate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Linker option extensions", "<option>", nullptr)
|
||||
#endif // OPTION
|
||||
|
||||
#ifdef OPTTABLE_ARG_INIT
|
||||
//////////
|
||||
// Option Values
|
||||
|
||||
|
||||
#endif // OPTTABLE_ARG_INIT
|
||||
160
deps/lld-prebuilt/MinGW/Options.inc
vendored
160
deps/lld-prebuilt/MinGW/Options.inc
vendored
@ -1,160 +0,0 @@
|
||||
/*===- TableGen'erated file -------------------------------------*- C++ -*-===*\
|
||||
|* *|
|
||||
|* Option Parsing Definitions *|
|
||||
|* *|
|
||||
|* Automatically generated file, do not edit! *|
|
||||
|* *|
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
/////////
|
||||
// Prefixes
|
||||
|
||||
#ifdef PREFIX
|
||||
#define COMMA ,
|
||||
PREFIX(prefix_0, {nullptr})
|
||||
PREFIX(prefix_1, {"-" COMMA nullptr})
|
||||
PREFIX(prefix_3, {"--" COMMA nullptr})
|
||||
PREFIX(prefix_2, {"--" COMMA "-" COMMA nullptr})
|
||||
#undef COMMA
|
||||
#endif // PREFIX
|
||||
|
||||
/////////
|
||||
// Groups
|
||||
|
||||
#ifdef OPTION
|
||||
|
||||
//////////
|
||||
// Options
|
||||
|
||||
OPTION(prefix_0, "<input>", INPUT, Input, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_0, "<unknown>", UNKNOWN, Unknown, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "###", _HASH_HASH_HASH, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Print (but do not run) the commands to run for this compilation", nullptr, nullptr)
|
||||
OPTION(prefix_2, "appcontainer", appcontainer, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Set the appcontainer flag in the executable", nullptr, nullptr)
|
||||
OPTION(prefix_2, "Bdynamic", Bdynamic, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Link against shared libraries", nullptr, nullptr)
|
||||
OPTION(prefix_2, "Bstatic", Bstatic, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not link against shared libraries", nullptr, nullptr)
|
||||
OPTION(prefix_2, "build-id", anonymous_1, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "delayload=", delayload_eq, Joined, INVALID, delayload, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "delayload", delayload, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"DLL to load only on demand", nullptr, nullptr)
|
||||
OPTION(prefix_2, "disable-auto-image-base", anonymous_2, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "dynamicbase", dynamicbase, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Enable ASLR", nullptr, nullptr)
|
||||
OPTION(prefix_2, "enable-auto-image-base", anonymous_3, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "enable-auto-import", anonymous_4, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Ignored; listed for libtool compatibility", nullptr, nullptr)
|
||||
OPTION(prefix_2, "end-group", anonymous_5, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "entry", entry, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Name of entry point symbol", "<entry>", nullptr)
|
||||
OPTION(prefix_2, "exclude-all-symbols", exclude_all_symbols, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Don't automatically export any symbols", nullptr, nullptr)
|
||||
OPTION(prefix_2, "export-all-symbols", export_all_symbols, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Export all symbols even if a def file or dllexport attributes are used", nullptr, nullptr)
|
||||
OPTION(prefix_1, "e", alias_entry_e, JoinedOrSeparate, INVALID, entry, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_3, "full-shutdown", anonymous_6, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "gc-sections", gc_sections, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Remove unused sections", nullptr, nullptr)
|
||||
OPTION(prefix_2, "help", help, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Print option help", nullptr, nullptr)
|
||||
OPTION(prefix_2, "high-entropy-va", anonymous_7, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "icf=", icf, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Identical code folding", nullptr, nullptr)
|
||||
OPTION(prefix_2, "image-base", image_base, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Base address of the program", nullptr, nullptr)
|
||||
OPTION(prefix_2, "insert-timestamp", insert_timestamp, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Include PE header timestamp", nullptr, nullptr)
|
||||
OPTION(prefix_2, "kill-at", kill_at, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Remove @n from exported symbols", nullptr, nullptr)
|
||||
OPTION(prefix_3, "large-address-aware", large_address_aware, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Enable large addresses", nullptr, nullptr)
|
||||
OPTION(prefix_1, "L", L, JoinedOrSeparate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Add a directory to the library search path", "<dir>", nullptr)
|
||||
OPTION(prefix_1, "l", l, JoinedOrSeparate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Root name of library to use", "<libName>", nullptr)
|
||||
OPTION(prefix_2, "major-image-version", anonymous_8, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_3, "major-os-version=", major_os_version_eq, Joined, INVALID, major_os_version, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_3, "major-os-version", major_os_version, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Set the OS and subsystem major version", nullptr, nullptr)
|
||||
OPTION(prefix_3, "major-subsystem-version=", major_subsystem_version_eq, Joined, INVALID, major_subsystem_version, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_3, "major-subsystem-version", major_subsystem_version, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Set the OS and subsystem major version", nullptr, nullptr)
|
||||
OPTION(prefix_2, "Map=", map_eq, Joined, INVALID, map, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "Map", map, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Output a linker map", nullptr, nullptr)
|
||||
OPTION(prefix_2, "minor-image-version", anonymous_9, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_3, "minor-os-version=", minor_os_version_eq, Joined, INVALID, minor_os_version, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_3, "minor-os-version", minor_os_version, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Set the OS and subsystem minor version", nullptr, nullptr)
|
||||
OPTION(prefix_3, "minor-subsystem-version=", minor_subsystem_version_eq, Joined, INVALID, minor_subsystem_version, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_3, "minor-subsystem-version", minor_subsystem_version, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Set the OS and subsystem minor version", nullptr, nullptr)
|
||||
OPTION(prefix_2, "mllvm", mllvm, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "m", m, JoinedOrSeparate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Set target emulation", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-gc-sections", no_gc_sections, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Don't remove unused sections", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-insert-timestamp", no_insert_timestamp, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Don't include PE header timestamp", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-seh", anonymous_10, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-whole-archive", no_whole_archive, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"No longer include all object files for following archives", nullptr, nullptr)
|
||||
OPTION(prefix_2, "nxcompat", anonymous_11, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_3, "out-implib=", out_implib_eq, Joined, INVALID, out_implib, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_3, "out-implib", out_implib, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Import library name", nullptr, nullptr)
|
||||
OPTION(prefix_2, "output-def", output_def, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Output def file", nullptr, nullptr)
|
||||
OPTION(prefix_1, "O", anonymous_0, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "o", o, JoinedOrSeparate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Path to file to write output", "<path>", nullptr)
|
||||
OPTION(prefix_2, "pdb=", pdb_eq, Joined, INVALID, pdb, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "pdb", pdb, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Output PDB debug info file, chosen implicitly if the argument is empty", nullptr, nullptr)
|
||||
OPTION(prefix_2, "pic-executable", anonymous_12, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=", anonymous_16, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt", anonymous_15, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin=", anonymous_14, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin", anonymous_13, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "require-defined=", require_defined_eq, Joined, INVALID, require_defined, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "require-defined", require_defined, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Force symbol to be added to symbol table as an undefined one", nullptr, nullptr)
|
||||
OPTION(prefix_2, "shared", shared, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Build a shared object", nullptr, nullptr)
|
||||
OPTION(prefix_2, "stack", stack, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "start-group", anonymous_18, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "strip-all", strip_all, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Omit all symbol information from the output binary", nullptr, nullptr)
|
||||
OPTION(prefix_2, "strip-debug", strip_debug, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Omit all debug information, but keep symbol information", nullptr, nullptr)
|
||||
OPTION(prefix_2, "subsystem=", subs_eq, Joined, INVALID, subs, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "subsystem", subs, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Specify subsystem", nullptr, nullptr)
|
||||
OPTION(prefix_2, "sysroot", anonymous_17, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "S", alias_strip_S, Flag, INVALID, strip_debug, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "s", alias_strip_s, Flag, INVALID, strip_all, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "tsaware", anonymous_19, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "undefined=", undefined_eq, Joined, INVALID, undefined, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "undefined", undefined_long, Separate, INVALID, undefined, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "u", undefined, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Include symbol in the link, if available", nullptr, nullptr)
|
||||
OPTION(prefix_2, "verbose", verbose, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Verbose mode", nullptr, nullptr)
|
||||
OPTION(prefix_2, "version", version, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Display the version number and exit", nullptr, nullptr)
|
||||
OPTION(prefix_1, "v", v, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Display the version number", nullptr, nullptr)
|
||||
OPTION(prefix_2, "whole-archive", whole_archive, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Include all object files for following archives", nullptr, nullptr)
|
||||
OPTION(prefix_2, "Xlink=", Xlink, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Pass <arg> to the COFF linker", "<arg>", nullptr)
|
||||
#endif // OPTION
|
||||
|
||||
#ifdef OPTTABLE_ARG_INIT
|
||||
//////////
|
||||
// Option Values
|
||||
|
||||
|
||||
#endif // OPTTABLE_ARG_INIT
|
||||
6
deps/lld-prebuilt/lld/Common/Version.inc
vendored
6
deps/lld-prebuilt/lld/Common/Version.inc
vendored
@ -1,6 +0,0 @@
|
||||
#define LLD_VERSION 9.0.0
|
||||
#define LLD_VERSION_STRING "9.0.0"
|
||||
#define LLD_VERSION_MAJOR 9
|
||||
#define LLD_VERSION_MINOR 0
|
||||
#define LLD_REVISION_STRING ""
|
||||
#define LLD_REPOSITORY_STRING ""
|
||||
195
deps/lld-prebuilt/wasm/Options.inc
vendored
195
deps/lld-prebuilt/wasm/Options.inc
vendored
@ -1,195 +0,0 @@
|
||||
/*===- TableGen'erated file -------------------------------------*- C++ -*-===*\
|
||||
|* *|
|
||||
|* Option Parsing Definitions *|
|
||||
|* *|
|
||||
|* Automatically generated file, do not edit! *|
|
||||
|* *|
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
/////////
|
||||
// Prefixes
|
||||
|
||||
#ifdef PREFIX
|
||||
#define COMMA ,
|
||||
PREFIX(prefix_0, {nullptr})
|
||||
PREFIX(prefix_2, {"-" COMMA nullptr})
|
||||
PREFIX(prefix_1, {"--" COMMA "-" COMMA nullptr})
|
||||
#undef COMMA
|
||||
#endif // PREFIX
|
||||
|
||||
/////////
|
||||
// Groups
|
||||
|
||||
#ifdef OPTION
|
||||
|
||||
//////////
|
||||
// Options
|
||||
|
||||
OPTION(prefix_0, "<input>", INPUT, Input, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_0, "<unknown>", UNKNOWN, Unknown, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "active-segments", active_segments, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Force segments to be active (default with unshared memory)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "allow-undefined-file=", allow_undefined_file, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Allow symbols listed in <file> to be undefined in linked binary", nullptr, nullptr)
|
||||
OPTION(prefix_2, "allow-undefined-file", allow_undefined_file_s, Separate, INVALID, allow_undefined_file, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "allow-undefined", allow_undefined, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Allow undefined symbols in linked binary", nullptr, nullptr)
|
||||
OPTION(prefix_1, "check-features", check_features, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Check feature compatibility of linked objects (default)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "color-diagnostics=", color_diagnostics_eq, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Use colors in diagnostics; one of 'always', 'never', 'auto'", nullptr, nullptr)
|
||||
OPTION(prefix_1, "color-diagnostics", color_diagnostics, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Use colors in diagnostics", nullptr, nullptr)
|
||||
OPTION(prefix_1, "compress-relocations", compress_relocations, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Compress the relocation targets in the code section.", nullptr, nullptr)
|
||||
OPTION(prefix_1, "demangle", demangle, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Demangle symbol names", nullptr, nullptr)
|
||||
OPTION(prefix_1, "disable-verify", disable_verify, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "emit-relocs", emit_relocs, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Generate relocations in output", nullptr, nullptr)
|
||||
OPTION(prefix_1, "entry=", anonymous_1, Joined, INVALID, entry, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "entry", entry, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Name of entry point symbol", "<entry>", nullptr)
|
||||
OPTION(prefix_1, "error-limit=", error_limit, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Maximum number of errors to emit before stopping (0 = no limit)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "export-all", export_all, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Export all symbols (normally combined with --no-gc-sections)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "export-dynamic", export_dynamic, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Put symbols in the dynamic symbol table", nullptr, nullptr)
|
||||
OPTION(prefix_1, "export-table", export_table, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Export function table to the environment", nullptr, nullptr)
|
||||
OPTION(prefix_1, "export=", export_eq, Joined, INVALID, export, nullptr, 0, 0,
|
||||
"Force a symbol to be exported", nullptr, nullptr)
|
||||
OPTION(prefix_1, "export", export, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "E", anonymous_2, Flag, INVALID, export_dynamic, nullptr, 0, 0,
|
||||
"Alias for --export-dynamic", nullptr, nullptr)
|
||||
OPTION(prefix_2, "e", anonymous_0, JoinedOrSeparate, INVALID, entry, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "fatal-warnings", fatal_warnings, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Treat warnings as errors", nullptr, nullptr)
|
||||
OPTION(prefix_1, "features=", features, CommaJoined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Comma-separated used features, inferred from input objects by default.", nullptr, nullptr)
|
||||
OPTION(prefix_1, "gc-sections", gc_sections, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Enable garbage collection of unused sections", nullptr, nullptr)
|
||||
OPTION(prefix_1, "global-base=", global_base, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Where to start to place global data", nullptr, nullptr)
|
||||
OPTION(prefix_1, "help", help, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Print option help", nullptr, nullptr)
|
||||
OPTION(prefix_1, "import-memory", import_memory, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Import memory from the environment", nullptr, nullptr)
|
||||
OPTION(prefix_1, "import-table", import_table, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Import function table from the environment", nullptr, nullptr)
|
||||
OPTION(prefix_1, "initial-memory=", initial_memory, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Initial size of the linear memory", nullptr, nullptr)
|
||||
OPTION(prefix_2, "i", anonymous_3, Flag, INVALID, initial_memory, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "lto-O", lto_O, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Optimization level for LTO", "<opt-level>", nullptr)
|
||||
OPTION(prefix_1, "lto-partitions=", lto_partitions, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Number of LTO codegen partitions", nullptr, nullptr)
|
||||
OPTION(prefix_2, "L", L, JoinedOrSeparate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Add a directory to the library search path", "<dir>", nullptr)
|
||||
OPTION(prefix_2, "l", l, JoinedOrSeparate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Root name of library to use", "<libName>", nullptr)
|
||||
OPTION(prefix_1, "max-memory=", max_memory, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Maximum size of the linear memory", nullptr, nullptr)
|
||||
OPTION(prefix_1, "merge-data-segments", merge_data_segments, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Enable merging data segments", nullptr, nullptr)
|
||||
OPTION(prefix_1, "mllvm", mllvm, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Options to pass to LLVM", nullptr, nullptr)
|
||||
OPTION(prefix_2, "m", anonymous_4, Flag, INVALID, max_memory, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "no-check-features", no_check_features, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Ignore feature compatibility of linked objects", nullptr, nullptr)
|
||||
OPTION(prefix_1, "no-color-diagnostics", no_color_diagnostics, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not use colors in diagnostics", nullptr, nullptr)
|
||||
OPTION(prefix_1, "no-demangle", no_demangle, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not demangle symbol names", nullptr, nullptr)
|
||||
OPTION(prefix_1, "no-entry", no_entry, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not output any entry point", nullptr, nullptr)
|
||||
OPTION(prefix_1, "no-export-dynamic", no_export_dynamic, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not put symbols in the dynamic symbol table (default)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "no-fatal-warnings", no_fatal_warnings, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "no-gc-sections", no_gc_sections, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Disable garbage collection of unused sections", nullptr, nullptr)
|
||||
OPTION(prefix_1, "no-merge-data-segments", no_merge_data_segments, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Disable merging data segments", nullptr, nullptr)
|
||||
OPTION(prefix_1, "no-pie", no_pie, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not create a position independent executable (default)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "no-print-gc-sections", no_print_gc_sections, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not list removed unused sections", nullptr, nullptr)
|
||||
OPTION(prefix_1, "no-threads", no_threads, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not run the linker multi-threaded", nullptr, nullptr)
|
||||
OPTION(prefix_1, "no-whole-archive", no_whole_archive, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not force load of all members in a static library (default)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "O", O, JoinedOrSeparate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Optimize output file size", nullptr, nullptr)
|
||||
OPTION(prefix_2, "o", o, JoinedOrSeparate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Path to file to write output", "<path>", nullptr)
|
||||
OPTION(prefix_1, "passive-segments", passive_segments, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Force segments to be passive (default with shared memory)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "pie", pie, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Create a position independent executable", nullptr, nullptr)
|
||||
OPTION(prefix_1, "print-gc-sections", print_gc_sections, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"List removed unused sections", nullptr, nullptr)
|
||||
OPTION(prefix_1, "relocatable", relocatable, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Create relocatable object file", nullptr, nullptr)
|
||||
OPTION(prefix_1, "reproduce=", reproduce_eq, Joined, INVALID, reproduce, nullptr, 0, 0,
|
||||
"Dump linker invocation and input files for debugging", nullptr, nullptr)
|
||||
OPTION(prefix_1, "reproduce", reproduce, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "r", anonymous_5, Flag, INVALID, relocatable, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "save-temps", save_temps, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "shared-memory", shared_memory, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Use shared linear memory", nullptr, nullptr)
|
||||
OPTION(prefix_1, "shared", shared, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Build a shared object", nullptr, nullptr)
|
||||
OPTION(prefix_1, "stack-first", stack_first, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Place stack at start of linear memory rather than after data", nullptr, nullptr)
|
||||
OPTION(prefix_1, "strip-all", strip_all, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Strip all symbols", nullptr, nullptr)
|
||||
OPTION(prefix_1, "strip-debug", strip_debug, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Strip debugging information", nullptr, nullptr)
|
||||
OPTION(prefix_2, "S", anonymous_7, Flag, INVALID, strip_debug, nullptr, 0, 0,
|
||||
"Alias for --strip-debug", nullptr, nullptr)
|
||||
OPTION(prefix_2, "s", anonymous_6, Flag, INVALID, strip_all, nullptr, 0, 0,
|
||||
"Alias for --strip-all", nullptr, nullptr)
|
||||
OPTION(prefix_1, "thinlto-cache-dir=", thinlto_cache_dir, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Path to ThinLTO cached object file directory", nullptr, nullptr)
|
||||
OPTION(prefix_1, "thinlto-cache-policy=", thinlto_cache_policy_eq, Joined, INVALID, thinlto_cache_policy, nullptr, 0, 0,
|
||||
"Pruning policy for the ThinLTO cache", nullptr, nullptr)
|
||||
OPTION(prefix_1, "thinlto-cache-policy", thinlto_cache_policy, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "thinlto-jobs=", thinlto_jobs, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Number of ThinLTO jobs", nullptr, nullptr)
|
||||
OPTION(prefix_1, "threads", threads, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Run the linker multi-threaded", nullptr, nullptr)
|
||||
OPTION(prefix_1, "trace-symbol=", trace_symbol_eq, Joined, INVALID, trace_symbol, nullptr, 0, 0,
|
||||
"Trace references to symbols", nullptr, nullptr)
|
||||
OPTION(prefix_1, "trace-symbol", trace_symbol, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "trace", trace, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Print the names of the input files", nullptr, nullptr)
|
||||
OPTION(prefix_2, "t", anonymous_8, Flag, INVALID, trace, nullptr, 0, 0,
|
||||
"Alias for --trace", nullptr, nullptr)
|
||||
OPTION(prefix_1, "undefined=", undefined_eq, Joined, INVALID, undefined, nullptr, 0, 0,
|
||||
"Force undefined symbol during linking", nullptr, nullptr)
|
||||
OPTION(prefix_1, "undefined", undefined, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "u", anonymous_10, JoinedOrSeparate, INVALID, undefined, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "verbose", verbose, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Verbose mode", nullptr, nullptr)
|
||||
OPTION(prefix_1, "version", version, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Display the version number and exit", nullptr, nullptr)
|
||||
OPTION(prefix_2, "v", v, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Display the version number", nullptr, nullptr)
|
||||
OPTION(prefix_1, "whole-archive", whole_archive, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Force load of all members in a static library", nullptr, nullptr)
|
||||
OPTION(prefix_1, "wrap=", wrap_eq, Joined, INVALID, wrap, nullptr, 0, 0,
|
||||
"Use wrapper functions for symbol", "<symbol>=<symbol>", nullptr)
|
||||
OPTION(prefix_1, "wrap", wrap, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, "<symbol>=<symbol>", nullptr)
|
||||
OPTION(prefix_2, "y", anonymous_9, JoinedOrSeparate, INVALID, trace_symbol, nullptr, 0, 0,
|
||||
"Alias for --trace-symbol", nullptr, nullptr)
|
||||
OPTION(prefix_2, "z", z, JoinedOrSeparate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Linker option extensions", "<option>", nullptr)
|
||||
#endif // OPTION
|
||||
|
||||
#ifdef OPTTABLE_ARG_INIT
|
||||
//////////
|
||||
// Option Values
|
||||
|
||||
|
||||
#endif // OPTTABLE_ARG_INIT
|
||||
4
deps/lld/.arcconfig
vendored
4
deps/lld/.arcconfig
vendored
@ -1,4 +0,0 @@
|
||||
{
|
||||
"repository.callsign" : "LLD",
|
||||
"conduit_uri" : "https://reviews.llvm.org/"
|
||||
}
|
||||
1
deps/lld/.clang-format
vendored
1
deps/lld/.clang-format
vendored
@ -1 +0,0 @@
|
||||
BasedOnStyle: LLVM
|
||||
24
deps/lld/.gitignore
vendored
24
deps/lld/.gitignore
vendored
@ -1,24 +0,0 @@
|
||||
#==============================================================================#
|
||||
# This file specifies intentionally untracked files that git should ignore.
|
||||
# See: http://www.kernel.org/pub/software/scm/git/docs/gitignore.html
|
||||
#==============================================================================#
|
||||
|
||||
#==============================================================================#
|
||||
# File extensions to be ignored anywhere in the tree.
|
||||
#==============================================================================#
|
||||
# Temp files created by most text editors.
|
||||
*~
|
||||
# Merge files created by git.
|
||||
*.orig
|
||||
# Byte compiled python modules.
|
||||
*.pyc
|
||||
# vim swap files
|
||||
.*.swp
|
||||
# Mac OS X Finder layout info
|
||||
.DS_Store
|
||||
|
||||
#==============================================================================#
|
||||
# Directories to be ignored.
|
||||
#==============================================================================#
|
||||
# Sphinx build files.
|
||||
docs/_build
|
||||
225
deps/lld/CMakeLists.txt
vendored
225
deps/lld/CMakeLists.txt
vendored
@ -1,225 +0,0 @@
|
||||
# Check if lld is built as a standalone project.
|
||||
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
||||
project(lld)
|
||||
cmake_minimum_required(VERSION 3.4.3)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
set(LLD_BUILT_STANDALONE TRUE)
|
||||
|
||||
find_program(LLVM_CONFIG_PATH "llvm-config" DOC "Path to llvm-config binary")
|
||||
if(NOT LLVM_CONFIG_PATH)
|
||||
message(FATAL_ERROR "llvm-config not found: specify LLVM_CONFIG_PATH")
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND "${LLVM_CONFIG_PATH}"
|
||||
"--obj-root"
|
||||
"--includedir"
|
||||
"--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}")
|
||||
endif()
|
||||
|
||||
string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" LLVM_CONFIG_OUTPUT "${LLVM_CONFIG_OUTPUT}")
|
||||
|
||||
list(GET LLVM_CONFIG_OUTPUT 0 OBJ_ROOT)
|
||||
list(GET LLVM_CONFIG_OUTPUT 1 MAIN_INCLUDE_DIR)
|
||||
list(GET LLVM_CONFIG_OUTPUT 2 LLVM_CMAKE_PATH)
|
||||
list(GET LLVM_CONFIG_OUTPUT 3 MAIN_SRC_DIR)
|
||||
|
||||
set(LLVM_OBJ_ROOT ${OBJ_ROOT} CACHE PATH "path to LLVM build tree")
|
||||
set(LLVM_MAIN_INCLUDE_DIR ${MAIN_INCLUDE_DIR} CACHE PATH "path to llvm/include")
|
||||
set(LLVM_MAIN_SRC_DIR ${MAIN_SRC_DIR} CACHE PATH "Path to LLVM source tree")
|
||||
|
||||
file(TO_CMAKE_PATH ${LLVM_OBJ_ROOT} LLVM_BINARY_DIR)
|
||||
|
||||
if(NOT EXISTS "${LLVM_CMAKE_PATH}/LLVMConfig.cmake")
|
||||
message(FATAL_ERROR "LLVMConfig.cmake not found")
|
||||
endif()
|
||||
include("${LLVM_CMAKE_PATH}/LLVMConfig.cmake")
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_PATH}")
|
||||
|
||||
set(PACKAGE_VERSION "${LLVM_PACKAGE_VERSION}")
|
||||
include_directories("${LLVM_BINARY_DIR}/include" ${LLVM_INCLUDE_DIRS})
|
||||
link_directories(${LLVM_LIBRARY_DIRS})
|
||||
|
||||
set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX})
|
||||
set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin)
|
||||
find_program(LLVM_TABLEGEN_EXE "llvm-tblgen" ${LLVM_TOOLS_BINARY_DIR} NO_DEFAULT_PATH)
|
||||
|
||||
include(AddLLVM)
|
||||
include(TableGen)
|
||||
include(HandleLLVMOptions)
|
||||
|
||||
if(LLVM_INCLUDE_TESTS)
|
||||
include(FindPythonInterp)
|
||||
if(NOT PYTHONINTERP_FOUND)
|
||||
message(FATAL_ERROR
|
||||
"Unable to find Python interpreter, required for testing.
|
||||
|
||||
Please install Python or specify the PYTHON_EXECUTABLE CMake variable.")
|
||||
endif()
|
||||
|
||||
if(${PYTHON_VERSION_STRING} VERSION_LESS 2.7)
|
||||
message(FATAL_ERROR "Python 2.7 or newer is required")
|
||||
endif()
|
||||
|
||||
# Check prebuilt llvm/utils.
|
||||
if(EXISTS ${LLVM_TOOLS_BINARY_DIR}/FileCheck${CMAKE_EXECUTABLE_SUFFIX}
|
||||
AND EXISTS ${LLVM_TOOLS_BINARY_DIR}/not${CMAKE_EXECUTABLE_SUFFIX})
|
||||
set(LLVM_UTILS_PROVIDED ON)
|
||||
endif()
|
||||
|
||||
if(EXISTS ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py)
|
||||
# Note: path not really used, except for checking if lit was found
|
||||
set(LLVM_LIT ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py)
|
||||
if(NOT LLVM_UTILS_PROVIDED)
|
||||
add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/FileCheck utils/FileCheck)
|
||||
add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/not utils/not)
|
||||
set(LLVM_UTILS_PROVIDED ON)
|
||||
set(LLD_TEST_DEPS FileCheck not)
|
||||
endif()
|
||||
set(UNITTEST_DIR ${LLVM_MAIN_SRC_DIR}/utils/unittest)
|
||||
if(EXISTS ${UNITTEST_DIR}/googletest/include/gtest/gtest.h
|
||||
AND NOT EXISTS ${LLVM_LIBRARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}
|
||||
AND EXISTS ${UNITTEST_DIR}/CMakeLists.txt)
|
||||
add_subdirectory(${UNITTEST_DIR} utils/unittest)
|
||||
endif()
|
||||
else()
|
||||
# Seek installed Lit.
|
||||
find_program(LLVM_LIT
|
||||
NAMES llvm-lit lit.py lit
|
||||
PATHS "${LLVM_MAIN_SRC_DIR}/utils/lit"
|
||||
DOC "Path to lit.py")
|
||||
endif()
|
||||
|
||||
if(LLVM_LIT)
|
||||
# Define the default arguments to use with 'lit', and an option for the user
|
||||
# to override.
|
||||
set(LIT_ARGS_DEFAULT "-sv")
|
||||
if (MSVC OR XCODE)
|
||||
set(LIT_ARGS_DEFAULT "${LIT_ARGS_DEFAULT} --no-progress-bar")
|
||||
endif()
|
||||
set(LLVM_LIT_ARGS "${LIT_ARGS_DEFAULT}" CACHE STRING "Default options for lit")
|
||||
|
||||
# On Win32 hosts, provide an option to specify the path to the GnuWin32 tools.
|
||||
if(WIN32 AND NOT CYGWIN)
|
||||
set(LLVM_LIT_TOOLS_DIR "" CACHE PATH "Path to GnuWin32 tools")
|
||||
endif()
|
||||
else()
|
||||
set(LLVM_INCLUDE_TESTS OFF)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(LLD_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(LLD_INCLUDE_DIR ${LLD_SOURCE_DIR}/include )
|
||||
set(LLD_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
# Compute the LLD version from the LLVM version.
|
||||
string(REGEX MATCH "[0-9]+\\.[0-9]+(\\.[0-9]+)?" LLD_VERSION
|
||||
${PACKAGE_VERSION})
|
||||
message(STATUS "LLD version: ${LLD_VERSION}")
|
||||
|
||||
string(REGEX REPLACE "([0-9]+)\\.[0-9]+(\\.[0-9]+)?" "\\1" LLD_VERSION_MAJOR
|
||||
${LLD_VERSION})
|
||||
string(REGEX REPLACE "[0-9]+\\.([0-9]+)(\\.[0-9]+)?" "\\1" LLD_VERSION_MINOR
|
||||
${LLD_VERSION})
|
||||
|
||||
# Determine LLD revision and repository.
|
||||
# TODO: Figure out a way to get the revision and the repository on windows.
|
||||
if ( NOT CMAKE_SYSTEM_NAME MATCHES "Windows" )
|
||||
execute_process(COMMAND ${CMAKE_SOURCE_DIR}/utils/GetSourceVersion ${LLD_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE LLD_REVISION)
|
||||
|
||||
execute_process(COMMAND ${CMAKE_SOURCE_DIR}/utils/GetRepositoryPath ${LLD_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE LLD_REPOSITORY)
|
||||
if ( LLD_REPOSITORY )
|
||||
# Replace newline characters with spaces
|
||||
string(REGEX REPLACE "(\r?\n)+" " " LLD_REPOSITORY ${LLD_REPOSITORY})
|
||||
# Remove leading spaces
|
||||
STRING(REGEX REPLACE "^[ \t\r\n]+" "" LLD_REPOSITORY "${LLD_REPOSITORY}" )
|
||||
# Remove trailing spaces
|
||||
string(REGEX REPLACE "(\ )+$" "" LLD_REPOSITORY ${LLD_REPOSITORY})
|
||||
endif()
|
||||
|
||||
if ( LLD_REVISION )
|
||||
# Replace newline characters with spaces
|
||||
string(REGEX REPLACE "(\r?\n)+" " " LLD_REVISION ${LLD_REVISION})
|
||||
# Remove leading spaces
|
||||
STRING(REGEX REPLACE "^[ \t\r\n]+" "" LLD_REVISION "${LLD_REVISION}" )
|
||||
# Remove trailing spaces
|
||||
string(REGEX REPLACE "(\ )+$" "" LLD_REVISION ${LLD_REVISION})
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
# Configure the Version.inc file.
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/lld/Common/Version.inc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/lld/Common/Version.inc)
|
||||
|
||||
|
||||
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
|
||||
message(FATAL_ERROR "In-source builds are not allowed. CMake would overwrite "
|
||||
"the makefiles distributed with LLVM. Please create a directory and run cmake "
|
||||
"from there, passing the path to this source directory as the last argument. "
|
||||
"This process created the file `CMakeCache.txt' and the directory "
|
||||
"`CMakeFiles'. Please delete them.")
|
||||
endif()
|
||||
|
||||
list (APPEND CMAKE_MODULE_PATH "${LLD_SOURCE_DIR}/cmake/modules")
|
||||
|
||||
include(AddLLD)
|
||||
|
||||
option(LLD_USE_VTUNE
|
||||
"Enable VTune user task tracking."
|
||||
OFF)
|
||||
if (LLD_USE_VTUNE)
|
||||
find_package(VTune)
|
||||
if (VTUNE_FOUND)
|
||||
include_directories(${VTune_INCLUDE_DIRS})
|
||||
list(APPEND LLVM_COMMON_LIBS ${VTune_LIBRARIES})
|
||||
add_definitions(-DLLD_HAS_VTUNE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(LLD_BUILD_TOOLS
|
||||
"Build the lld tools. If OFF, just generate build targets." ON)
|
||||
|
||||
if (MSVC)
|
||||
add_definitions(-wd4530) # Suppress 'warning C4530: C++ exception handler used, but unwind semantics are not enabled.'
|
||||
add_definitions(-wd4062) # Suppress 'warning C4062: enumerator X in switch of enum Y is not handled' from system header.
|
||||
endif()
|
||||
|
||||
include_directories(BEFORE
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
)
|
||||
|
||||
if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
|
||||
install(DIRECTORY include/
|
||||
DESTINATION include
|
||||
FILES_MATCHING
|
||||
PATTERN "*.h"
|
||||
PATTERN ".svn" EXCLUDE
|
||||
)
|
||||
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)
|
||||
add_subdirectory(COFF)
|
||||
add_subdirectory(ELF)
|
||||
add_subdirectory(MinGW)
|
||||
add_subdirectory(wasm)
|
||||
22
deps/lld/CODE_OWNERS.TXT
vendored
22
deps/lld/CODE_OWNERS.TXT
vendored
@ -1,22 +0,0 @@
|
||||
This file is a list of the people responsible for ensuring that patches for a
|
||||
particular part of LLD are reviewed, either by themself or by someone else.
|
||||
They are also the gatekeepers for their part of LLD, with the final word on
|
||||
what goes in or not.
|
||||
|
||||
The list is sorted by surname and formatted to allow easy grepping and
|
||||
beautification by scripts. The fields are: name (N), email (E), web-address
|
||||
(W), PGP key ID and fingerprint (P), description (D), and snail-mail address
|
||||
(S). Each entry should contain at least the (N), (E) and (D) fields.
|
||||
|
||||
|
||||
N: Rui Ueyama
|
||||
E: ruiu@google.com
|
||||
D: COFF, ELF backends (COFF/* ELF/*)
|
||||
|
||||
N: Lang Hames, Nick Kledzik
|
||||
E: lhames@gmail.com, kledzik@apple.com
|
||||
D: Mach-O backend
|
||||
|
||||
N: Sam Clegg
|
||||
E: sbc@chromium.org
|
||||
D: WebAssembly backend (wasm/*)
|
||||
48
deps/lld/COFF/CMakeLists.txt
vendored
48
deps/lld/COFF/CMakeLists.txt
vendored
@ -1,48 +0,0 @@
|
||||
set(LLVM_TARGET_DEFINITIONS Options.td)
|
||||
tablegen(LLVM Options.inc -gen-opt-parser-defs)
|
||||
add_public_tablegen_target(COFFOptionsTableGen)
|
||||
|
||||
if(NOT LLD_BUILT_STANDALONE)
|
||||
set(tablegen_deps intrinsics_gen)
|
||||
endif()
|
||||
|
||||
add_lld_library(lldCOFF
|
||||
Chunks.cpp
|
||||
DebugTypes.cpp
|
||||
DLL.cpp
|
||||
Driver.cpp
|
||||
DriverUtils.cpp
|
||||
ICF.cpp
|
||||
InputFiles.cpp
|
||||
LTO.cpp
|
||||
MapFile.cpp
|
||||
MarkLive.cpp
|
||||
MinGW.cpp
|
||||
PDB.cpp
|
||||
SymbolTable.cpp
|
||||
Symbols.cpp
|
||||
Writer.cpp
|
||||
|
||||
LINK_COMPONENTS
|
||||
${LLVM_TARGETS_TO_BUILD}
|
||||
BinaryFormat
|
||||
Core
|
||||
DebugInfoCodeView
|
||||
DebugInfoMSF
|
||||
DebugInfoPDB
|
||||
LibDriver
|
||||
LTO
|
||||
MC
|
||||
Object
|
||||
Option
|
||||
Support
|
||||
WindowsManifest
|
||||
|
||||
LINK_LIBS
|
||||
lldCommon
|
||||
${LLVM_PTHREAD_LIB}
|
||||
|
||||
DEPENDS
|
||||
COFFOptionsTableGen
|
||||
${tablegen_deps}
|
||||
)
|
||||
922
deps/lld/COFF/Chunks.cpp
vendored
922
deps/lld/COFF/Chunks.cpp
vendored
@ -1,922 +0,0 @@
|
||||
//===- Chunks.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 "Chunks.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "Writer.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/BinaryFormat/COFF.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::COFF;
|
||||
using llvm::support::ulittle32_t;
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
SectionChunk::SectionChunk(ObjFile *f, const coff_section *h)
|
||||
: Chunk(SectionKind), file(f), header(h), repl(this) {
|
||||
// Initialize relocs.
|
||||
setRelocs(file->getCOFFObj()->getRelocations(header));
|
||||
|
||||
// Initialize sectionName.
|
||||
StringRef sectionName;
|
||||
if (Expected<StringRef> e = file->getCOFFObj()->getSectionName(header))
|
||||
sectionName = *e;
|
||||
sectionNameData = sectionName.data();
|
||||
sectionNameSize = sectionName.size();
|
||||
|
||||
setAlignment(header->getAlignment());
|
||||
|
||||
hasData = !(header->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA);
|
||||
|
||||
// If linker GC is disabled, every chunk starts out alive. If linker GC is
|
||||
// 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.
|
||||
live = !config->doGC || !isCOMDAT();
|
||||
}
|
||||
|
||||
// SectionChunk is one of the most frequently allocated classes, so it is
|
||||
// important to keep it as compact as possible. As of this writing, the number
|
||||
// below is the size of this class on x64 platforms.
|
||||
static_assert(sizeof(SectionChunk) <= 88, "SectionChunk grew unexpectedly");
|
||||
|
||||
static void add16(uint8_t *p, int16_t v) { write16le(p, read16le(p) + v); }
|
||||
static void add32(uint8_t *p, int32_t v) { write32le(p, read32le(p) + v); }
|
||||
static void add64(uint8_t *p, int64_t v) { write64le(p, read64le(p) + v); }
|
||||
static void or16(uint8_t *p, uint16_t v) { write16le(p, read16le(p) | v); }
|
||||
static void or32(uint8_t *p, uint32_t v) { write32le(p, read32le(p) | v); }
|
||||
|
||||
// Verify that given sections are appropriate targets for SECREL
|
||||
// relocations. This check is relaxed because unfortunately debug
|
||||
// sections have section-relative relocations against absolute symbols.
|
||||
static bool checkSecRel(const SectionChunk *sec, OutputSection *os) {
|
||||
if (os)
|
||||
return true;
|
||||
if (sec->isCodeView())
|
||||
return false;
|
||||
error("SECREL relocation cannot be applied to absolute symbols");
|
||||
return false;
|
||||
}
|
||||
|
||||
static void applySecRel(const SectionChunk *sec, uint8_t *off,
|
||||
OutputSection *os, uint64_t s) {
|
||||
if (!checkSecRel(sec, os))
|
||||
return;
|
||||
uint64_t secRel = s - os->getRVA();
|
||||
if (secRel > UINT32_MAX) {
|
||||
error("overflow in SECREL relocation in section: " + sec->getSectionName());
|
||||
return;
|
||||
}
|
||||
add32(off, secRel);
|
||||
}
|
||||
|
||||
static void applySecIdx(uint8_t *off, OutputSection *os) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
void SectionChunk::applyRelX64(uint8_t *off, uint16_t type, OutputSection *os,
|
||||
uint64_t s, uint64_t p) 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_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;
|
||||
case IMAGE_REL_AMD64_REL32_2: add32(off, s - p - 6); break;
|
||||
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_SECREL: applySecRel(this, off, os, s); break;
|
||||
default:
|
||||
error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " +
|
||||
toString(file));
|
||||
}
|
||||
}
|
||||
|
||||
void SectionChunk::applyRelX86(uint8_t *off, uint16_t type, OutputSection *os,
|
||||
uint64_t s, uint64_t p) const {
|
||||
switch (type) {
|
||||
case IMAGE_REL_I386_ABSOLUTE: break;
|
||||
case IMAGE_REL_I386_DIR32: add32(off, s + config->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_SECREL: applySecRel(this, off, os, s); break;
|
||||
default:
|
||||
error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " +
|
||||
toString(file));
|
||||
}
|
||||
}
|
||||
|
||||
static void applyMOV(uint8_t *off, uint16_t v) {
|
||||
write16le(off, (read16le(off) & 0xfbf0) | ((v & 0x800) >> 1) | ((v >> 12) & 0xf));
|
||||
write16le(off + 2, (read16le(off + 2) & 0x8f00) | ((v & 0x700) << 4) | (v & 0xff));
|
||||
}
|
||||
|
||||
static uint16_t readMOV(uint8_t *off, bool movt) {
|
||||
uint16_t op1 = read16le(off);
|
||||
if ((op1 & 0xfbf0) != (movt ? 0xf2c0 : 0xf240))
|
||||
error("unexpected instruction in " + Twine(movt ? "MOVT" : "MOVW") +
|
||||
" instruction in MOV32T relocation");
|
||||
uint16_t op2 = read16le(off + 2);
|
||||
if ((op2 & 0x8000) != 0)
|
||||
error("unexpected instruction in " + Twine(movt ? "MOVT" : "MOVW") +
|
||||
" instruction in MOV32T relocation");
|
||||
return (op2 & 0x00ff) | ((op2 >> 4) & 0x0700) | ((op1 << 1) & 0x0800) |
|
||||
((op1 & 0x000f) << 12);
|
||||
}
|
||||
|
||||
void applyMOV32T(uint8_t *off, uint32_t v) {
|
||||
uint16_t immW = readMOV(off, false); // read MOVW operand
|
||||
uint16_t immT = readMOV(off + 4, true); // read MOVT operand
|
||||
uint32_t imm = immW | (immT << 16);
|
||||
v += imm; // add the immediate offset
|
||||
applyMOV(off, v); // set MOVW operand
|
||||
applyMOV(off + 4, v >> 16); // set MOVT operand
|
||||
}
|
||||
|
||||
static void applyBranch20T(uint8_t *off, int32_t v) {
|
||||
if (!isInt<21>(v))
|
||||
error("relocation out of range");
|
||||
uint32_t s = v < 0 ? 1 : 0;
|
||||
uint32_t j1 = (v >> 19) & 1;
|
||||
uint32_t j2 = (v >> 18) & 1;
|
||||
or16(off, (s << 10) | ((v >> 12) & 0x3f));
|
||||
or16(off + 2, (j1 << 13) | (j2 << 11) | ((v >> 1) & 0x7ff));
|
||||
}
|
||||
|
||||
void applyBranch24T(uint8_t *off, int32_t v) {
|
||||
if (!isInt<25>(v))
|
||||
error("relocation out of range");
|
||||
uint32_t s = v < 0 ? 1 : 0;
|
||||
uint32_t j1 = ((~v >> 23) & 1) ^ s;
|
||||
uint32_t j2 = ((~v >> 22) & 1) ^ s;
|
||||
or16(off, (s << 10) | ((v >> 12) & 0x3ff));
|
||||
// Clear out the J1 and J2 bits which may be set.
|
||||
write16le(off + 2, (read16le(off + 2) & 0xd000) | (j1 << 13) | (j2 << 11) | ((v >> 1) & 0x7ff));
|
||||
}
|
||||
|
||||
void SectionChunk::applyRelARM(uint8_t *off, uint16_t type, OutputSection *os,
|
||||
uint64_t s, uint64_t p) 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_ADDR32NB: add32(off, sx); break;
|
||||
case IMAGE_REL_ARM_MOV32T: applyMOV32T(off, sx + config->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_SECREL: applySecRel(this, off, os, s); break;
|
||||
case IMAGE_REL_ARM_REL32: add32(off, sx - p - 4); break;
|
||||
default:
|
||||
error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " +
|
||||
toString(file));
|
||||
}
|
||||
}
|
||||
|
||||
// Interpret the existing immediate value as a byte offset to the
|
||||
// target symbol, then update the instruction with the immediate as
|
||||
// the page offset from the current instruction to the target.
|
||||
void applyArm64Addr(uint8_t *off, uint64_t s, uint64_t p, int shift) {
|
||||
uint32_t orig = read32le(off);
|
||||
uint64_t imm = ((orig >> 29) & 0x3) | ((orig >> 3) & 0x1FFFFC);
|
||||
s += imm;
|
||||
imm = (s >> shift) - (p >> shift);
|
||||
uint32_t immLo = (imm & 0x3) << 29;
|
||||
uint32_t immHi = (imm & 0x1FFFFC) << 3;
|
||||
uint64_t mask = (0x3 << 29) | (0x1FFFFC << 3);
|
||||
write32le(off, (orig & ~mask) | immLo | immHi);
|
||||
}
|
||||
|
||||
// Update the immediate field in a AARCH64 ldr, str, and add instruction.
|
||||
// Optionally limit the range of the written immediate by one or more bits
|
||||
// (rangeLimit).
|
||||
void applyArm64Imm(uint8_t *off, uint64_t imm, uint32_t rangeLimit) {
|
||||
uint32_t orig = read32le(off);
|
||||
imm += (orig >> 10) & 0xFFF;
|
||||
orig &= ~(0xFFF << 10);
|
||||
write32le(off, orig | ((imm & (0xFFF >> rangeLimit)) << 10));
|
||||
}
|
||||
|
||||
// Add the 12 bit page offset to the existing immediate.
|
||||
// Ldr/str instructions store the opcode immediate scaled
|
||||
// by the load/store size (giving a larger range for larger
|
||||
// loads/stores). The immediate is always (both before and after
|
||||
// fixing up the relocation) stored scaled similarly.
|
||||
// Even if larger loads/stores have a larger range, limit the
|
||||
// effective offset to 12 bit, since it is intended to be a
|
||||
// page offset.
|
||||
static void applyArm64Ldr(uint8_t *off, uint64_t imm) {
|
||||
uint32_t orig = read32le(off);
|
||||
uint32_t size = orig >> 30;
|
||||
// 0x04000000 indicates SIMD/FP registers
|
||||
// 0x00800000 indicates 128 bit
|
||||
if ((orig & 0x4800000) == 0x4800000)
|
||||
size += 4;
|
||||
if ((imm & ((1 << size) - 1)) != 0)
|
||||
error("misaligned ldr/str offset");
|
||||
applyArm64Imm(off, imm >> size, size);
|
||||
}
|
||||
|
||||
static void applySecRelLow12A(const SectionChunk *sec, uint8_t *off,
|
||||
OutputSection *os, uint64_t s) {
|
||||
if (checkSecRel(sec, os))
|
||||
applyArm64Imm(off, (s - os->getRVA()) & 0xfff, 0);
|
||||
}
|
||||
|
||||
static void applySecRelHigh12A(const SectionChunk *sec, uint8_t *off,
|
||||
OutputSection *os, uint64_t s) {
|
||||
if (!checkSecRel(sec, os))
|
||||
return;
|
||||
uint64_t secRel = (s - os->getRVA()) >> 12;
|
||||
if (0xfff < secRel) {
|
||||
error("overflow in SECREL_HIGH12A relocation in section: " +
|
||||
sec->getSectionName());
|
||||
return;
|
||||
}
|
||||
applyArm64Imm(off, secRel & 0xfff, 0);
|
||||
}
|
||||
|
||||
static void applySecRelLdr(const SectionChunk *sec, uint8_t *off,
|
||||
OutputSection *os, uint64_t s) {
|
||||
if (checkSecRel(sec, os))
|
||||
applyArm64Ldr(off, (s - os->getRVA()) & 0xfff);
|
||||
}
|
||||
|
||||
void applyArm64Branch26(uint8_t *off, int64_t v) {
|
||||
if (!isInt<28>(v))
|
||||
error("relocation out of range");
|
||||
or32(off, (v & 0x0FFFFFFC) >> 2);
|
||||
}
|
||||
|
||||
static void applyArm64Branch19(uint8_t *off, int64_t v) {
|
||||
if (!isInt<21>(v))
|
||||
error("relocation out of range");
|
||||
or32(off, (v & 0x001FFFFC) << 3);
|
||||
}
|
||||
|
||||
static void applyArm64Branch14(uint8_t *off, int64_t v) {
|
||||
if (!isInt<16>(v))
|
||||
error("relocation out of range");
|
||||
or32(off, (v & 0x0000FFFC) << 3);
|
||||
}
|
||||
|
||||
void SectionChunk::applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os,
|
||||
uint64_t s, uint64_t p) const {
|
||||
switch (type) {
|
||||
case IMAGE_REL_ARM64_PAGEBASE_REL21: applyArm64Addr(off, s, p, 12); break;
|
||||
case IMAGE_REL_ARM64_REL21: applyArm64Addr(off, s, p, 0); break;
|
||||
case IMAGE_REL_ARM64_PAGEOFFSET_12A: applyArm64Imm(off, s & 0xfff, 0); break;
|
||||
case IMAGE_REL_ARM64_PAGEOFFSET_12L: applyArm64Ldr(off, s & 0xfff); break;
|
||||
case IMAGE_REL_ARM64_BRANCH26: 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_ADDR32NB: add32(off, s); break;
|
||||
case IMAGE_REL_ARM64_ADDR64: add64(off, s + config->imageBase); break;
|
||||
case IMAGE_REL_ARM64_SECREL: applySecRel(this, off, os, s); break;
|
||||
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_REL32: add32(off, s - p - 4); break;
|
||||
default:
|
||||
error("unsupported relocation type 0x" + Twine::utohexstr(type) + " in " +
|
||||
toString(file));
|
||||
}
|
||||
}
|
||||
|
||||
static void maybeReportRelocationToDiscarded(const SectionChunk *fromChunk,
|
||||
Defined *sym,
|
||||
const coff_relocation &rel) {
|
||||
// Don't report these errors when the relocation comes from a debug info
|
||||
// section or in mingw mode. MinGW mode object files (built by GCC) can
|
||||
// have leftover sections with relocations against discarded comdat
|
||||
// sections. Such sections are left as is, with relocations untouched.
|
||||
if (fromChunk->isCodeView() || fromChunk->isDWARF() || config->mingw)
|
||||
return;
|
||||
|
||||
// Get the name of the symbol. If it's null, it was discarded early, so we
|
||||
// have to go back to the object file.
|
||||
ObjFile *file = fromChunk->file;
|
||||
StringRef name;
|
||||
if (sym) {
|
||||
name = sym->getName();
|
||||
} else {
|
||||
COFFSymbolRef coffSym =
|
||||
check(file->getCOFFObj()->getSymbol(rel.SymbolTableIndex));
|
||||
file->getCOFFObj()->getSymbolName(coffSym, name);
|
||||
}
|
||||
|
||||
std::vector<std::string> symbolLocations =
|
||||
getSymbolLocations(file, rel.SymbolTableIndex);
|
||||
|
||||
std::string out;
|
||||
llvm::raw_string_ostream os(out);
|
||||
os << "relocation against symbol in discarded section: " + name;
|
||||
for (const std::string &s : symbolLocations)
|
||||
os << s;
|
||||
error(os.str());
|
||||
}
|
||||
|
||||
void SectionChunk::writeTo(uint8_t *buf) const {
|
||||
if (!hasData)
|
||||
return;
|
||||
// Copy section contents from source object file to output file.
|
||||
ArrayRef<uint8_t> a = getContents();
|
||||
if (!a.empty())
|
||||
memcpy(buf, a.data(), a.size());
|
||||
|
||||
// Apply relocations.
|
||||
size_t inputSize = getSize();
|
||||
for (size_t i = 0, e = relocsSize; i < e; i++) {
|
||||
const coff_relocation &rel = relocsData[i];
|
||||
|
||||
// Check for an invalid relocation offset. This check isn't perfect, because
|
||||
// we don't have the relocation size, which is only known after checking the
|
||||
// machine and relocation type. As a result, a relocation may overwrite the
|
||||
// beginning of the following input section.
|
||||
if (rel.VirtualAddress >= inputSize) {
|
||||
error("relocation points beyond the end of its parent section");
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t *off = buf + rel.VirtualAddress;
|
||||
|
||||
auto *sym =
|
||||
dyn_cast_or_null<Defined>(file->getSymbol(rel.SymbolTableIndex));
|
||||
|
||||
// Get the output section of the symbol for this relocation. The output
|
||||
// section is needed to compute SECREL and SECTION relocations used in debug
|
||||
// info.
|
||||
Chunk *c = sym ? sym->getChunk() : nullptr;
|
||||
OutputSection *os = c ? c->getOutputSection() : 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
|
||||
// null. If it was discarded late, the output section will be null, unless
|
||||
// it was an absolute or synthetic symbol.
|
||||
if (!sym ||
|
||||
(!os && !isa<DefinedAbsolute>(sym) && !isa<DefinedSynthetic>(sym))) {
|
||||
maybeReportRelocationToDiscarded(this, sym, rel);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t s = sym->getRVA();
|
||||
|
||||
// Compute the RVA of the relocation for relative relocations.
|
||||
uint64_t p = rva + rel.VirtualAddress;
|
||||
switch (config->machine) {
|
||||
case AMD64:
|
||||
applyRelX64(off, rel.Type, os, s, p);
|
||||
break;
|
||||
case I386:
|
||||
applyRelX86(off, rel.Type, os, s, p);
|
||||
break;
|
||||
case ARMNT:
|
||||
applyRelARM(off, rel.Type, os, s, p);
|
||||
break;
|
||||
case ARM64:
|
||||
applyRelARM64(off, rel.Type, os, s, p);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("unknown machine type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SectionChunk::addAssociative(SectionChunk *child) {
|
||||
// Insert this child at the head of the list.
|
||||
assert(child->assocChildren == nullptr &&
|
||||
"associated sections cannot have their own associated children");
|
||||
child->assocChildren = assocChildren;
|
||||
assocChildren = child;
|
||||
}
|
||||
|
||||
static uint8_t getBaserelType(const coff_relocation &rel) {
|
||||
switch (config->machine) {
|
||||
case AMD64:
|
||||
if (rel.Type == IMAGE_REL_AMD64_ADDR64)
|
||||
return IMAGE_REL_BASED_DIR64;
|
||||
return IMAGE_REL_BASED_ABSOLUTE;
|
||||
case I386:
|
||||
if (rel.Type == IMAGE_REL_I386_DIR32)
|
||||
return IMAGE_REL_BASED_HIGHLOW;
|
||||
return IMAGE_REL_BASED_ABSOLUTE;
|
||||
case ARMNT:
|
||||
if (rel.Type == IMAGE_REL_ARM_ADDR32)
|
||||
return IMAGE_REL_BASED_HIGHLOW;
|
||||
if (rel.Type == IMAGE_REL_ARM_MOV32T)
|
||||
return IMAGE_REL_BASED_ARM_MOV32T;
|
||||
return IMAGE_REL_BASED_ABSOLUTE;
|
||||
case ARM64:
|
||||
if (rel.Type == IMAGE_REL_ARM64_ADDR64)
|
||||
return IMAGE_REL_BASED_DIR64;
|
||||
return IMAGE_REL_BASED_ABSOLUTE;
|
||||
default:
|
||||
llvm_unreachable("unknown machine type");
|
||||
}
|
||||
}
|
||||
|
||||
// Windows-specific.
|
||||
// Collect all locations that contain absolute addresses, which need to be
|
||||
// fixed by the loader if load-time relocation is needed.
|
||||
// Only called when base relocation is enabled.
|
||||
void SectionChunk::getBaserels(std::vector<Baserel> *res) {
|
||||
for (size_t i = 0, e = relocsSize; i < e; i++) {
|
||||
const coff_relocation &rel = relocsData[i];
|
||||
uint8_t ty = getBaserelType(rel);
|
||||
if (ty == IMAGE_REL_BASED_ABSOLUTE)
|
||||
continue;
|
||||
Symbol *target = file->getSymbol(rel.SymbolTableIndex);
|
||||
if (!target || isa<DefinedAbsolute>(target))
|
||||
continue;
|
||||
res->emplace_back(rva + rel.VirtualAddress, ty);
|
||||
}
|
||||
}
|
||||
|
||||
// MinGW specific.
|
||||
// Check whether a static relocation of type Type can be deferred and
|
||||
// handled at runtime as a pseudo relocation (for references to a module
|
||||
// local variable, which turned out to actually need to be imported from
|
||||
// another DLL) This returns the size the relocation is supposed to update,
|
||||
// in bits, or 0 if the relocation cannot be handled as a runtime pseudo
|
||||
// relocation.
|
||||
static int getRuntimePseudoRelocSize(uint16_t type) {
|
||||
// Relocations that either contain an absolute address, or a plain
|
||||
// relative offset, since the runtime pseudo reloc implementation
|
||||
// adds 8/16/32/64 bit values to a memory address.
|
||||
//
|
||||
// Given a pseudo relocation entry,
|
||||
//
|
||||
// typedef struct {
|
||||
// DWORD sym;
|
||||
// DWORD target;
|
||||
// DWORD flags;
|
||||
// } runtime_pseudo_reloc_item_v2;
|
||||
//
|
||||
// the runtime relocation performs this adjustment:
|
||||
// *(base + .target) += *(base + .sym) - (base + .sym)
|
||||
//
|
||||
// This works for both absolute addresses (IMAGE_REL_*_ADDR32/64,
|
||||
// IMAGE_REL_I386_DIR32, where the memory location initially contains
|
||||
// the address of the IAT slot, and for relative addresses (IMAGE_REL*_REL32),
|
||||
// where the memory location originally contains the relative offset to the
|
||||
// IAT slot.
|
||||
//
|
||||
// This requires the target address to be writable, either directly out of
|
||||
// the image, or temporarily changed at runtime with VirtualProtect.
|
||||
// Since this only operates on direct address values, it doesn't work for
|
||||
// ARM/ARM64 relocations, other than the plain ADDR32/ADDR64 relocations.
|
||||
switch (config->machine) {
|
||||
case AMD64:
|
||||
switch (type) {
|
||||
case IMAGE_REL_AMD64_ADDR64:
|
||||
return 64;
|
||||
case IMAGE_REL_AMD64_ADDR32:
|
||||
case IMAGE_REL_AMD64_REL32:
|
||||
case IMAGE_REL_AMD64_REL32_1:
|
||||
case IMAGE_REL_AMD64_REL32_2:
|
||||
case IMAGE_REL_AMD64_REL32_3:
|
||||
case IMAGE_REL_AMD64_REL32_4:
|
||||
case IMAGE_REL_AMD64_REL32_5:
|
||||
return 32;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
case I386:
|
||||
switch (type) {
|
||||
case IMAGE_REL_I386_DIR32:
|
||||
case IMAGE_REL_I386_REL32:
|
||||
return 32;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
case ARMNT:
|
||||
switch (type) {
|
||||
case IMAGE_REL_ARM_ADDR32:
|
||||
return 32;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
case ARM64:
|
||||
switch (type) {
|
||||
case IMAGE_REL_ARM64_ADDR64:
|
||||
return 64;
|
||||
case IMAGE_REL_ARM64_ADDR32:
|
||||
return 32;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
llvm_unreachable("unknown machine type");
|
||||
}
|
||||
}
|
||||
|
||||
// MinGW specific.
|
||||
// Append information to the provided vector about all relocations that
|
||||
// need to be handled at runtime as runtime pseudo relocations (references
|
||||
// to a module local variable, which turned out to actually need to be
|
||||
// imported from another DLL).
|
||||
void SectionChunk::getRuntimePseudoRelocs(
|
||||
std::vector<RuntimePseudoReloc> &res) {
|
||||
for (const coff_relocation &rel : getRelocs()) {
|
||||
auto *target =
|
||||
dyn_cast_or_null<Defined>(file->getSymbol(rel.SymbolTableIndex));
|
||||
if (!target || !target->isRuntimePseudoReloc)
|
||||
continue;
|
||||
int sizeInBits = getRuntimePseudoRelocSize(rel.Type);
|
||||
if (sizeInBits == 0) {
|
||||
error("unable to automatically import from " + target->getName() +
|
||||
" with relocation type " +
|
||||
file->getCOFFObj()->getRelocationTypeName(rel.Type) + " in " +
|
||||
toString(file));
|
||||
continue;
|
||||
}
|
||||
// sizeInBits is used to initialize the Flags field; currently no
|
||||
// other flags are defined.
|
||||
res.emplace_back(
|
||||
RuntimePseudoReloc(target, this, rel.VirtualAddress, sizeInBits));
|
||||
}
|
||||
}
|
||||
|
||||
bool SectionChunk::isCOMDAT() const {
|
||||
return header->Characteristics & IMAGE_SCN_LNK_COMDAT;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
StringRef SectionChunk::getDebugName() const {
|
||||
if (sym)
|
||||
return sym->getName();
|
||||
return "";
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> SectionChunk::getContents() const {
|
||||
ArrayRef<uint8_t> a;
|
||||
cantFail(file->getCOFFObj()->getSectionContents(header, a));
|
||||
return a;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> SectionChunk::consumeDebugMagic() {
|
||||
assert(isCodeView());
|
||||
return consumeDebugMagic(getContents(), getSectionName());
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> SectionChunk::consumeDebugMagic(ArrayRef<uint8_t> data,
|
||||
StringRef sectionName) {
|
||||
if (data.empty())
|
||||
return {};
|
||||
|
||||
// First 4 bytes are section magic.
|
||||
if (data.size() < 4)
|
||||
fatal("the section is too short: " + sectionName);
|
||||
|
||||
if (!sectionName.startswith(".debug$"))
|
||||
fatal("invalid section: " + sectionName);
|
||||
|
||||
uint32_t magic = support::endian::read32le(data.data());
|
||||
uint32_t expectedMagic = sectionName == ".debug$H"
|
||||
? DEBUG_HASHES_SECTION_MAGIC
|
||||
: DEBUG_SECTION_MAGIC;
|
||||
if (magic != expectedMagic) {
|
||||
warn("ignoring section " + sectionName + " with unrecognized magic 0x" +
|
||||
utohexstr(magic));
|
||||
return {};
|
||||
}
|
||||
return data.slice(4);
|
||||
}
|
||||
|
||||
SectionChunk *SectionChunk::findByName(ArrayRef<SectionChunk *> sections,
|
||||
StringRef name) {
|
||||
for (SectionChunk *c : sections)
|
||||
if (c->getSectionName() == name)
|
||||
return c;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SectionChunk::replace(SectionChunk *other) {
|
||||
p2Align = std::max(p2Align, other->p2Align);
|
||||
other->repl = repl;
|
||||
other->live = false;
|
||||
}
|
||||
|
||||
uint32_t SectionChunk::getSectionNumber() const {
|
||||
DataRefImpl r;
|
||||
r.p = reinterpret_cast<uintptr_t>(header);
|
||||
SectionRef s(r, file->getCOFFObj());
|
||||
return s.getIndex() + 1;
|
||||
}
|
||||
|
||||
CommonChunk::CommonChunk(const COFFSymbolRef s) : sym(s) {
|
||||
// The value of a common symbol is its size. Align all common symbols smaller
|
||||
// than 32 bytes naturally, i.e. round the size up to the next power of two.
|
||||
// This is what MSVC link.exe does.
|
||||
setAlignment(std::min(32U, uint32_t(PowerOf2Ceil(sym.getValue()))));
|
||||
hasData = false;
|
||||
}
|
||||
|
||||
uint32_t CommonChunk::getOutputCharacteristics() const {
|
||||
return IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ |
|
||||
IMAGE_SCN_MEM_WRITE;
|
||||
}
|
||||
|
||||
void StringChunk::writeTo(uint8_t *buf) const {
|
||||
memcpy(buf, str.data(), str.size());
|
||||
buf[str.size()] = '\0';
|
||||
}
|
||||
|
||||
ImportThunkChunkX64::ImportThunkChunkX64(Defined *s) : ImportThunkChunk(s) {
|
||||
// Intel Optimization Manual says that all branch targets
|
||||
// should be 16-byte aligned. MSVC linker does this too.
|
||||
setAlignment(16);
|
||||
}
|
||||
|
||||
void ImportThunkChunkX64::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() - rva - getSize());
|
||||
}
|
||||
|
||||
void ImportThunkChunkX86::getBaserels(std::vector<Baserel> *res) {
|
||||
res->emplace_back(getRVA() + 2);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void ImportThunkChunkARM::getBaserels(std::vector<Baserel> *res) {
|
||||
res->emplace_back(getRVA(), IMAGE_REL_BASED_ARM_MOV32T);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void ImportThunkChunkARM64::writeTo(uint8_t *buf) const {
|
||||
int64_t off = impSymbol->getRVA() & 0xfff;
|
||||
memcpy(buf, importThunkARM64, sizeof(importThunkARM64));
|
||||
applyArm64Addr(buf, impSymbol->getRVA(), rva, 12);
|
||||
applyArm64Ldr(buf + 4, off);
|
||||
}
|
||||
|
||||
// A Thumb2, PIC, non-interworking range extension thunk.
|
||||
const uint8_t armThunk[] = {
|
||||
0x40, 0xf2, 0x00, 0x0c, // P: movw ip,:lower16:S - (P + (L1-P) + 4)
|
||||
0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P) + 4)
|
||||
0xe7, 0x44, // L1: add pc, ip
|
||||
};
|
||||
|
||||
size_t RangeExtensionThunkARM::getSize() const {
|
||||
assert(config->machine == ARMNT);
|
||||
return sizeof(armThunk);
|
||||
}
|
||||
|
||||
void RangeExtensionThunkARM::writeTo(uint8_t *buf) const {
|
||||
assert(config->machine == ARMNT);
|
||||
uint64_t offset = target->getRVA() - rva - 12;
|
||||
memcpy(buf, armThunk, sizeof(armThunk));
|
||||
applyMOV32T(buf, uint32_t(offset));
|
||||
}
|
||||
|
||||
// A position independent ARM64 adrp+add thunk, with a maximum range of
|
||||
// +/- 4 GB, which is enough for any PE-COFF.
|
||||
const uint8_t arm64Thunk[] = {
|
||||
0x10, 0x00, 0x00, 0x90, // adrp x16, Dest
|
||||
0x10, 0x02, 0x00, 0x91, // add x16, x16, :lo12:Dest
|
||||
0x00, 0x02, 0x1f, 0xd6, // br x16
|
||||
};
|
||||
|
||||
size_t RangeExtensionThunkARM64::getSize() const {
|
||||
assert(config->machine == ARM64);
|
||||
return sizeof(arm64Thunk);
|
||||
}
|
||||
|
||||
void RangeExtensionThunkARM64::writeTo(uint8_t *buf) const {
|
||||
assert(config->machine == ARM64);
|
||||
memcpy(buf, arm64Thunk, sizeof(arm64Thunk));
|
||||
applyArm64Addr(buf + 0, target->getRVA(), rva, 12);
|
||||
applyArm64Imm(buf + 4, target->getRVA() & 0xfff, 0);
|
||||
}
|
||||
|
||||
void LocalImportChunk::getBaserels(std::vector<Baserel> *res) {
|
||||
res->emplace_back(getRVA());
|
||||
}
|
||||
|
||||
size_t LocalImportChunk::getSize() const { return config->wordsize; }
|
||||
|
||||
void LocalImportChunk::writeTo(uint8_t *buf) const {
|
||||
if (config->is64()) {
|
||||
write64le(buf, sym->getRVA() + config->imageBase);
|
||||
} else {
|
||||
write32le(buf, sym->getRVA() + config->imageBase);
|
||||
}
|
||||
}
|
||||
|
||||
void RVATableChunk::writeTo(uint8_t *buf) const {
|
||||
ulittle32_t *begin = reinterpret_cast<ulittle32_t *>(buf);
|
||||
size_t cnt = 0;
|
||||
for (const ChunkAndOffset &co : syms)
|
||||
begin[cnt++] = co.inputChunk->getRVA() + co.offset;
|
||||
std::sort(begin, begin + cnt);
|
||||
assert(std::unique(begin, begin + cnt) == begin + cnt &&
|
||||
"RVA tables should be de-duplicated");
|
||||
}
|
||||
|
||||
// MinGW specific, for the "automatic import of variables from DLLs" feature.
|
||||
size_t PseudoRelocTableChunk::getSize() const {
|
||||
if (relocs.empty())
|
||||
return 0;
|
||||
return 12 + 12 * relocs.size();
|
||||
}
|
||||
|
||||
// MinGW specific.
|
||||
void PseudoRelocTableChunk::writeTo(uint8_t *buf) const {
|
||||
if (relocs.empty())
|
||||
return;
|
||||
|
||||
ulittle32_t *table = reinterpret_cast<ulittle32_t *>(buf);
|
||||
// This is the list header, to signal the runtime pseudo relocation v2
|
||||
// format.
|
||||
table[0] = 0;
|
||||
table[1] = 0;
|
||||
table[2] = 1;
|
||||
|
||||
size_t idx = 3;
|
||||
for (const RuntimePseudoReloc &rpr : relocs) {
|
||||
table[idx + 0] = rpr.sym->getRVA();
|
||||
table[idx + 1] = rpr.target->getRVA() + rpr.targetOffset;
|
||||
table[idx + 2] = rpr.flags;
|
||||
idx += 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Windows-specific. This class represents a block in .reloc section.
|
||||
// The format is described here.
|
||||
//
|
||||
// On Windows, each DLL is linked against a fixed base address and
|
||||
// usually loaded to that address. However, if there's already another
|
||||
// DLL that overlaps, the loader has to relocate it. To do that, DLLs
|
||||
// contain .reloc sections which contain offsets that need to be fixed
|
||||
// up at runtime. If the loader finds that a DLL cannot be loaded to its
|
||||
// desired base address, it loads it to somewhere else, and add <actual
|
||||
// base address> - <desired base address> to each offset that is
|
||||
// specified by the .reloc section. In ELF terms, .reloc sections
|
||||
// contain relative relocations in REL format (as opposed to RELA.)
|
||||
//
|
||||
// This already significantly reduces the size of relocations compared
|
||||
// to ELF .rel.dyn, but Windows does more to reduce it (probably because
|
||||
// it was invented for PCs in the late '80s or early '90s.) Offsets in
|
||||
// .reloc are grouped by page where the page size is 12 bits, and
|
||||
// offsets sharing the same page address are stored consecutively to
|
||||
// represent them with less space. This is very similar to the page
|
||||
// table which is grouped by (multiple stages of) pages.
|
||||
//
|
||||
// For example, let's say we have 0x00030, 0x00500, 0x00700, 0x00A00,
|
||||
// 0x20004, and 0x20008 in a .reloc section for x64. The uppermost 4
|
||||
// bits have a type IMAGE_REL_BASED_DIR64 or 0xA. In the section, they
|
||||
// are represented like this:
|
||||
//
|
||||
// 0x00000 -- page address (4 bytes)
|
||||
// 16 -- size of this block (4 bytes)
|
||||
// 0xA030 -- entries (2 bytes each)
|
||||
// 0xA500
|
||||
// 0xA700
|
||||
// 0xAA00
|
||||
// 0x20000 -- page address (4 bytes)
|
||||
// 12 -- size of this block (4 bytes)
|
||||
// 0xA004 -- entries (2 bytes each)
|
||||
// 0xA008
|
||||
//
|
||||
// Usually we have a lot of relocations for each page, so the number of
|
||||
// bytes for one .reloc entry is close to 2 bytes on average.
|
||||
BaserelChunk::BaserelChunk(uint32_t page, Baserel *begin, Baserel *end) {
|
||||
// Block header consists of 4 byte page RVA and 4 byte block size.
|
||||
// Each entry is 2 byte. Last entry may be padding.
|
||||
data.resize(alignTo((end - begin) * 2 + 8, 4));
|
||||
uint8_t *p = data.data();
|
||||
write32le(p, page);
|
||||
write32le(p + 4, data.size());
|
||||
p += 8;
|
||||
for (Baserel *i = begin; i != end; ++i) {
|
||||
write16le(p, (i->type << 12) | (i->rva - page));
|
||||
p += 2;
|
||||
}
|
||||
}
|
||||
|
||||
void BaserelChunk::writeTo(uint8_t *buf) const {
|
||||
memcpy(buf, data.data(), data.size());
|
||||
}
|
||||
|
||||
uint8_t Baserel::getDefaultType() {
|
||||
switch (config->machine) {
|
||||
case AMD64:
|
||||
case ARM64:
|
||||
return IMAGE_REL_BASED_DIR64;
|
||||
case I386:
|
||||
case ARMNT:
|
||||
return IMAGE_REL_BASED_HIGHLOW;
|
||||
default:
|
||||
llvm_unreachable("unknown machine type");
|
||||
}
|
||||
}
|
||||
|
||||
MergeChunk *MergeChunk::instances[Log2MaxSectionAlignment + 1] = {};
|
||||
|
||||
MergeChunk::MergeChunk(uint32_t alignment)
|
||||
: builder(StringTableBuilder::RAW, alignment) {
|
||||
setAlignment(alignment);
|
||||
}
|
||||
|
||||
void MergeChunk::addSection(SectionChunk *c) {
|
||||
assert(isPowerOf2_32(c->getAlignment()));
|
||||
uint8_t p2Align = llvm::Log2_32(c->getAlignment());
|
||||
assert(p2Align < array_lengthof(instances));
|
||||
auto *&mc = instances[p2Align];
|
||||
if (!mc)
|
||||
mc = make<MergeChunk>(c->getAlignment());
|
||||
mc->sections.push_back(c);
|
||||
}
|
||||
|
||||
void MergeChunk::finalizeContents() {
|
||||
assert(!finalized && "should only finalize once");
|
||||
for (SectionChunk *c : sections)
|
||||
if (c->live)
|
||||
builder.add(toStringRef(c->getContents()));
|
||||
builder.finalize();
|
||||
finalized = true;
|
||||
}
|
||||
|
||||
void MergeChunk::assignSubsectionRVAs() {
|
||||
for (SectionChunk *c : sections) {
|
||||
if (!c->live)
|
||||
continue;
|
||||
size_t off = builder.getOffset(toStringRef(c->getContents()));
|
||||
c->setRVA(rva + off);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t MergeChunk::getOutputCharacteristics() const {
|
||||
return IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA;
|
||||
}
|
||||
|
||||
size_t MergeChunk::getSize() const {
|
||||
return builder.getSize();
|
||||
}
|
||||
|
||||
void MergeChunk::writeTo(uint8_t *buf) const {
|
||||
builder.write(buf);
|
||||
}
|
||||
|
||||
// MinGW specific.
|
||||
size_t AbsolutePointerChunk::getSize() const { return config->wordsize; }
|
||||
|
||||
void AbsolutePointerChunk::writeTo(uint8_t *buf) const {
|
||||
if (config->is64()) {
|
||||
write64le(buf, value);
|
||||
} else {
|
||||
write32le(buf, value);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
686
deps/lld/COFF/Chunks.h
vendored
686
deps/lld/COFF/Chunks.h
vendored
@ -1,686 +0,0 @@
|
||||
//===- Chunks.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_CHUNKS_H
|
||||
#define LLD_COFF_CHUNKS_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "InputFiles.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/PointerIntPair.h"
|
||||
#include "llvm/ADT/iterator.h"
|
||||
#include "llvm/ADT/iterator_range.h"
|
||||
#include "llvm/MC/StringTableBuilder.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
using llvm::COFF::ImportDirectoryTableEntry;
|
||||
using llvm::object::COFFSymbolRef;
|
||||
using llvm::object::SectionRef;
|
||||
using llvm::object::coff_relocation;
|
||||
using llvm::object::coff_section;
|
||||
|
||||
class Baserel;
|
||||
class Defined;
|
||||
class DefinedImportData;
|
||||
class DefinedRegular;
|
||||
class ObjFile;
|
||||
class OutputSection;
|
||||
class RuntimePseudoReloc;
|
||||
class Symbol;
|
||||
|
||||
// Mask for permissions (discardable, writable, readable, executable, etc).
|
||||
const uint32_t permMask = 0xFE000000;
|
||||
|
||||
// Mask for section types (code, data, bss).
|
||||
const uint32_t typeMask = 0x000000E0;
|
||||
|
||||
// The log base 2 of the largest section alignment, which is log2(8192), or 13.
|
||||
enum : unsigned { Log2MaxSectionAlignment = 13 };
|
||||
|
||||
// A Chunk represents a chunk of data that will occupy space in the
|
||||
// output (if the resolver chose that). It may or may not be backed by
|
||||
// a section of an input file. It could be linker-created data, or
|
||||
// doesn't even have actual data (if common or bss).
|
||||
class Chunk {
|
||||
public:
|
||||
enum Kind : uint8_t { SectionKind, OtherKind, ImportThunkKind };
|
||||
Kind kind() const { return chunkKind; }
|
||||
|
||||
// Returns the size of this chunk (even if this is a common or BSS.)
|
||||
size_t getSize() const;
|
||||
|
||||
// Returns chunk alignment in power of two form. Value values are powers of
|
||||
// two from 1 to 8192.
|
||||
uint32_t getAlignment() const { return 1U << p2Align; }
|
||||
|
||||
// Update the chunk section alignment measured in bytes. Internally alignment
|
||||
// is stored in log2.
|
||||
void setAlignment(uint32_t align) {
|
||||
// Treat zero byte alignment as 1 byte alignment.
|
||||
align = align ? align : 1;
|
||||
assert(llvm::isPowerOf2_32(align) && "alignment is not a power of 2");
|
||||
p2Align = llvm::Log2_32(align);
|
||||
assert(p2Align <= Log2MaxSectionAlignment &&
|
||||
"impossible requested alignment");
|
||||
}
|
||||
|
||||
// Write this chunk to a mmap'ed file, assuming Buf is pointing to
|
||||
// beginning of the file. Because this function may use RVA values
|
||||
// of other chunks for relocations, you need to set them properly
|
||||
// before calling this function.
|
||||
void writeTo(uint8_t *buf) const;
|
||||
|
||||
// The writer sets and uses the addresses. In practice, PE images cannot be
|
||||
// larger than 2GB. Chunks are always laid as part of the image, so Chunk RVAs
|
||||
// can be stored with 32 bits.
|
||||
uint32_t getRVA() const { return rva; }
|
||||
void setRVA(uint64_t v) {
|
||||
rva = (uint32_t)v;
|
||||
assert(rva == v && "RVA truncated");
|
||||
}
|
||||
|
||||
// Returns readable/writable/executable bits.
|
||||
uint32_t getOutputCharacteristics() const;
|
||||
|
||||
// Returns the section name if this is a section chunk.
|
||||
// It is illegal to call this function on non-section chunks.
|
||||
StringRef getSectionName() const;
|
||||
|
||||
// An output section has pointers to chunks in the section, and each
|
||||
// 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.
|
||||
void getBaserels(std::vector<Baserel> *res);
|
||||
|
||||
// Returns a human-readable name of this chunk. Chunks are unnamed chunks of
|
||||
// bytes, so this is used only for logging or debugging.
|
||||
StringRef getDebugName() const;
|
||||
|
||||
// Return true if this file has the hotpatch flag set to true in the
|
||||
// S_COMPILE3 record in codeview debug info. Also returns true for some thunks
|
||||
// synthesized by the linker.
|
||||
bool isHotPatchable() const;
|
||||
|
||||
protected:
|
||||
Chunk(Kind k = OtherKind) : chunkKind(k), hasData(true), p2Align(0) {}
|
||||
|
||||
const Kind chunkKind;
|
||||
|
||||
public:
|
||||
// Returns true if this has non-zero data. BSS chunks return
|
||||
// false. If false is returned, the space occupied by this chunk
|
||||
// will be filled with zeros. Corresponds to the
|
||||
// IMAGE_SCN_CNT_UNINITIALIZED_DATA section characteristic bit.
|
||||
uint8_t hasData : 1;
|
||||
|
||||
public:
|
||||
// The alignment of this chunk, stored in log2 form. The writer uses the
|
||||
// value.
|
||||
uint8_t p2Align : 7;
|
||||
|
||||
// The output section index for this chunk. The first valid section number is
|
||||
// one.
|
||||
uint16_t osidx = 0;
|
||||
|
||||
// The RVA of this chunk in the output. The writer sets a value.
|
||||
uint32_t rva = 0;
|
||||
};
|
||||
|
||||
class NonSectionChunk : public Chunk {
|
||||
public:
|
||||
virtual ~NonSectionChunk() = default;
|
||||
|
||||
// Returns the size of this chunk (even if this is a common or BSS.)
|
||||
virtual size_t getSize() const = 0;
|
||||
|
||||
virtual uint32_t getOutputCharacteristics() const { return 0; }
|
||||
|
||||
// Write this chunk to a mmap'ed file, assuming Buf is pointing to
|
||||
// beginning of the file. Because this function may use RVA values
|
||||
// of other chunks for relocations, you need to set them properly
|
||||
// before calling this function.
|
||||
virtual void writeTo(uint8_t *buf) const {}
|
||||
|
||||
// Returns the section name if this is a section chunk.
|
||||
// It is illegal to call this function on non-section chunks.
|
||||
virtual StringRef getSectionName() const {
|
||||
llvm_unreachable("unimplemented getSectionName");
|
||||
}
|
||||
|
||||
// Windows-specific.
|
||||
// Collect all locations that contain absolute addresses for base relocations.
|
||||
virtual void getBaserels(std::vector<Baserel> *res) {}
|
||||
|
||||
// Returns a human-readable name of this chunk. Chunks are unnamed chunks of
|
||||
// bytes, so this is used only for logging or debugging.
|
||||
virtual StringRef getDebugName() const { return ""; }
|
||||
|
||||
static bool classof(const Chunk *c) { return c->kind() != SectionKind; }
|
||||
|
||||
protected:
|
||||
NonSectionChunk(Kind k = OtherKind) : Chunk(k) {}
|
||||
};
|
||||
|
||||
// A chunk corresponding a section of an input file.
|
||||
class SectionChunk final : public Chunk {
|
||||
// Identical COMDAT Folding feature accesses section internal data.
|
||||
friend class ICF;
|
||||
|
||||
public:
|
||||
class symbol_iterator : public llvm::iterator_adaptor_base<
|
||||
symbol_iterator, const coff_relocation *,
|
||||
std::random_access_iterator_tag, Symbol *> {
|
||||
friend SectionChunk;
|
||||
|
||||
ObjFile *file;
|
||||
|
||||
symbol_iterator(ObjFile *file, const coff_relocation *i)
|
||||
: symbol_iterator::iterator_adaptor_base(i), file(file) {}
|
||||
|
||||
public:
|
||||
symbol_iterator() = default;
|
||||
|
||||
Symbol *operator*() const { return file->getSymbol(I->SymbolTableIndex); }
|
||||
};
|
||||
|
||||
SectionChunk(ObjFile *file, const coff_section *header);
|
||||
static bool classof(const Chunk *c) { return c->kind() == SectionKind; }
|
||||
size_t getSize() const { return header->SizeOfRawData; }
|
||||
ArrayRef<uint8_t> getContents() const;
|
||||
void writeTo(uint8_t *buf) const;
|
||||
|
||||
uint32_t getOutputCharacteristics() const {
|
||||
return header->Characteristics & (permMask | typeMask);
|
||||
}
|
||||
StringRef getSectionName() const {
|
||||
return StringRef(sectionNameData, sectionNameSize);
|
||||
}
|
||||
void getBaserels(std::vector<Baserel> *res);
|
||||
bool isCOMDAT() const;
|
||||
void applyRelX64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
|
||||
uint64_t p) const;
|
||||
void applyRelX86(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
|
||||
uint64_t p) const;
|
||||
void applyRelARM(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
|
||||
uint64_t p) const;
|
||||
void applyRelARM64(uint8_t *off, uint16_t type, OutputSection *os, uint64_t s,
|
||||
uint64_t p) const;
|
||||
|
||||
void getRuntimePseudoRelocs(std::vector<RuntimePseudoReloc> &res);
|
||||
|
||||
// Called if the garbage collector decides to not include this chunk
|
||||
// in a final output. It's supposed to print out a log message to stdout.
|
||||
void printDiscardedMessage() const;
|
||||
|
||||
// Adds COMDAT associative sections to this COMDAT section. A chunk
|
||||
// and its children are treated as a group by the garbage collector.
|
||||
void addAssociative(SectionChunk *child);
|
||||
|
||||
StringRef getDebugName() const;
|
||||
|
||||
// True if this is a codeview debug info chunk. These will not be laid out in
|
||||
// the image. Instead they will end up in the PDB, if one is requested.
|
||||
bool isCodeView() const {
|
||||
return getSectionName() == ".debug" || getSectionName().startswith(".debug$");
|
||||
}
|
||||
|
||||
// True if this is a DWARF debug info or exception handling chunk.
|
||||
bool isDWARF() const {
|
||||
return getSectionName().startswith(".debug_") || getSectionName() == ".eh_frame";
|
||||
}
|
||||
|
||||
// Allow iteration over the bodies of this chunk's relocated symbols.
|
||||
llvm::iterator_range<symbol_iterator> symbols() const {
|
||||
return llvm::make_range(symbol_iterator(file, relocsData),
|
||||
symbol_iterator(file, relocsData + relocsSize));
|
||||
}
|
||||
|
||||
ArrayRef<coff_relocation> getRelocs() const {
|
||||
return llvm::makeArrayRef(relocsData, relocsSize);
|
||||
}
|
||||
|
||||
// Reloc setter used by ARM range extension thunk insertion.
|
||||
void setRelocs(ArrayRef<coff_relocation> newRelocs) {
|
||||
relocsData = newRelocs.data();
|
||||
relocsSize = newRelocs.size();
|
||||
assert(relocsSize == newRelocs.size() && "reloc size truncation");
|
||||
}
|
||||
|
||||
// Single linked list iterator for associated comdat children.
|
||||
class AssociatedIterator
|
||||
: public llvm::iterator_facade_base<
|
||||
AssociatedIterator, std::forward_iterator_tag, SectionChunk> {
|
||||
public:
|
||||
AssociatedIterator() = default;
|
||||
AssociatedIterator(SectionChunk *head) : cur(head) {}
|
||||
AssociatedIterator &operator=(const AssociatedIterator &r) {
|
||||
cur = r.cur;
|
||||
return *this;
|
||||
}
|
||||
bool operator==(const AssociatedIterator &r) const { return cur == r.cur; }
|
||||
const SectionChunk &operator*() const { return *cur; }
|
||||
SectionChunk &operator*() { return *cur; }
|
||||
AssociatedIterator &operator++() {
|
||||
cur = cur->assocChildren;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
SectionChunk *cur = nullptr;
|
||||
};
|
||||
|
||||
// Allow iteration over the associated child chunks for this section.
|
||||
llvm::iterator_range<AssociatedIterator> children() const {
|
||||
return llvm::make_range(AssociatedIterator(assocChildren),
|
||||
AssociatedIterator(nullptr));
|
||||
}
|
||||
|
||||
// The section ID this chunk belongs to in its Obj.
|
||||
uint32_t getSectionNumber() const;
|
||||
|
||||
ArrayRef<uint8_t> consumeDebugMagic();
|
||||
|
||||
static ArrayRef<uint8_t> consumeDebugMagic(ArrayRef<uint8_t> data,
|
||||
StringRef sectionName);
|
||||
|
||||
static SectionChunk *findByName(ArrayRef<SectionChunk *> sections,
|
||||
StringRef name);
|
||||
|
||||
// The file that this chunk was created from.
|
||||
ObjFile *file;
|
||||
|
||||
// Pointer to the COFF section header in the input file.
|
||||
const coff_section *header;
|
||||
|
||||
// The COMDAT leader symbol if this is a COMDAT chunk.
|
||||
DefinedRegular *sym = nullptr;
|
||||
|
||||
// The CRC of the contents as described in the COFF spec 4.5.5.
|
||||
// Auxiliary Format 5: Section Definitions. Used for ICF.
|
||||
uint32_t checksum = 0;
|
||||
|
||||
// Used by the garbage collector.
|
||||
bool live;
|
||||
|
||||
// Whether this section needs to be kept distinct from other sections during
|
||||
// ICF. This is set by the driver using address-significance tables.
|
||||
bool keepUnique = false;
|
||||
|
||||
// The COMDAT selection if this is a COMDAT chunk.
|
||||
llvm::COFF::COMDATType selection = (llvm::COFF::COMDATType)0;
|
||||
|
||||
// A pointer pointing to a replacement for this chunk.
|
||||
// Initially it points to "this" object. If this chunk is merged
|
||||
// with other chunk by ICF, it points to another chunk,
|
||||
// and this chunk is considered as dead.
|
||||
SectionChunk *repl;
|
||||
|
||||
private:
|
||||
SectionChunk *assocChildren = nullptr;
|
||||
|
||||
// Used for ICF (Identical COMDAT Folding)
|
||||
void replace(SectionChunk *other);
|
||||
uint32_t eqClass[2] = {0, 0};
|
||||
|
||||
// Relocations for this section. Size is stored below.
|
||||
const coff_relocation *relocsData;
|
||||
|
||||
// Section name string. Size is stored below.
|
||||
const char *sectionNameData;
|
||||
|
||||
uint32_t relocsSize = 0;
|
||||
uint32_t sectionNameSize = 0;
|
||||
};
|
||||
|
||||
// Inline methods to implement faux-virtual dispatch for SectionChunk.
|
||||
|
||||
inline size_t Chunk::getSize() const {
|
||||
if (isa<SectionChunk>(this))
|
||||
return static_cast<const SectionChunk *>(this)->getSize();
|
||||
else
|
||||
return static_cast<const NonSectionChunk *>(this)->getSize();
|
||||
}
|
||||
|
||||
inline uint32_t Chunk::getOutputCharacteristics() const {
|
||||
if (isa<SectionChunk>(this))
|
||||
return static_cast<const SectionChunk *>(this)->getOutputCharacteristics();
|
||||
else
|
||||
return static_cast<const NonSectionChunk *>(this)
|
||||
->getOutputCharacteristics();
|
||||
}
|
||||
|
||||
inline void Chunk::writeTo(uint8_t *buf) const {
|
||||
if (isa<SectionChunk>(this))
|
||||
static_cast<const SectionChunk *>(this)->writeTo(buf);
|
||||
else
|
||||
static_cast<const NonSectionChunk *>(this)->writeTo(buf);
|
||||
}
|
||||
|
||||
inline StringRef Chunk::getSectionName() const {
|
||||
if (isa<SectionChunk>(this))
|
||||
return static_cast<const SectionChunk *>(this)->getSectionName();
|
||||
else
|
||||
return static_cast<const NonSectionChunk *>(this)->getSectionName();
|
||||
}
|
||||
|
||||
inline void Chunk::getBaserels(std::vector<Baserel> *res) {
|
||||
if (isa<SectionChunk>(this))
|
||||
static_cast<SectionChunk *>(this)->getBaserels(res);
|
||||
else
|
||||
static_cast<NonSectionChunk *>(this)->getBaserels(res);
|
||||
}
|
||||
|
||||
inline StringRef Chunk::getDebugName() const {
|
||||
if (isa<SectionChunk>(this))
|
||||
return static_cast<const SectionChunk *>(this)->getDebugName();
|
||||
else
|
||||
return static_cast<const NonSectionChunk *>(this)->getDebugName();
|
||||
}
|
||||
|
||||
// This class is used to implement an lld-specific feature (not implemented in
|
||||
// MSVC) that minimizes the output size by finding string literals sharing tail
|
||||
// parts and merging them.
|
||||
//
|
||||
// If string tail merging is enabled and a section is identified as containing a
|
||||
// string literal, it is added to a MergeChunk with an appropriate alignment.
|
||||
// The MergeChunk then tail merges the strings using the StringTableBuilder
|
||||
// class and assigns RVAs and section offsets to each of the member chunks based
|
||||
// on the offsets assigned by the StringTableBuilder.
|
||||
class MergeChunk : public NonSectionChunk {
|
||||
public:
|
||||
MergeChunk(uint32_t alignment);
|
||||
static void addSection(SectionChunk *c);
|
||||
void finalizeContents();
|
||||
void assignSubsectionRVAs();
|
||||
|
||||
uint32_t getOutputCharacteristics() const override;
|
||||
StringRef getSectionName() const override { return ".rdata"; }
|
||||
size_t getSize() const override;
|
||||
void writeTo(uint8_t *buf) const override;
|
||||
|
||||
static MergeChunk *instances[Log2MaxSectionAlignment + 1];
|
||||
std::vector<SectionChunk *> sections;
|
||||
|
||||
private:
|
||||
llvm::StringTableBuilder builder;
|
||||
bool finalized = false;
|
||||
};
|
||||
|
||||
// A chunk for common symbols. Common chunks don't have actual data.
|
||||
class CommonChunk : public NonSectionChunk {
|
||||
public:
|
||||
CommonChunk(const COFFSymbolRef sym);
|
||||
size_t getSize() const override { return sym.getValue(); }
|
||||
uint32_t getOutputCharacteristics() const override;
|
||||
StringRef getSectionName() const override { return ".bss"; }
|
||||
|
||||
private:
|
||||
const COFFSymbolRef sym;
|
||||
};
|
||||
|
||||
// A chunk for linker-created strings.
|
||||
class StringChunk : public NonSectionChunk {
|
||||
public:
|
||||
explicit StringChunk(StringRef s) : str(s) {}
|
||||
size_t getSize() const override { return str.size() + 1; }
|
||||
void writeTo(uint8_t *buf) const override;
|
||||
|
||||
private:
|
||||
StringRef str;
|
||||
};
|
||||
|
||||
static const uint8_t importThunkX86[] = {
|
||||
0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // JMP *0x0
|
||||
};
|
||||
|
||||
static const uint8_t importThunkARM[] = {
|
||||
0x40, 0xf2, 0x00, 0x0c, // mov.w ip, #0
|
||||
0xc0, 0xf2, 0x00, 0x0c, // mov.t ip, #0
|
||||
0xdc, 0xf8, 0x00, 0xf0, // ldr.w pc, [ip]
|
||||
};
|
||||
|
||||
static const uint8_t importThunkARM64[] = {
|
||||
0x10, 0x00, 0x00, 0x90, // adrp x16, #0
|
||||
0x10, 0x02, 0x40, 0xf9, // ldr x16, [x16]
|
||||
0x00, 0x02, 0x1f, 0xd6, // br x16
|
||||
};
|
||||
|
||||
// Windows-specific.
|
||||
// A chunk for DLL import jump table entry. In a final output, its
|
||||
// contents will be a JMP instruction to some __imp_ symbol.
|
||||
class ImportThunkChunk : public NonSectionChunk {
|
||||
public:
|
||||
ImportThunkChunk(Defined *s)
|
||||
: NonSectionChunk(ImportThunkKind), impSymbol(s) {}
|
||||
static bool classof(const Chunk *c) { return c->kind() == ImportThunkKind; }
|
||||
|
||||
protected:
|
||||
Defined *impSymbol;
|
||||
};
|
||||
|
||||
class ImportThunkChunkX64 : public ImportThunkChunk {
|
||||
public:
|
||||
explicit ImportThunkChunkX64(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) {}
|
||||
size_t getSize() const override { return sizeof(importThunkX86); }
|
||||
void getBaserels(std::vector<Baserel> *res) override;
|
||||
void writeTo(uint8_t *buf) const override;
|
||||
};
|
||||
|
||||
class ImportThunkChunkARM : public ImportThunkChunk {
|
||||
public:
|
||||
explicit ImportThunkChunkARM(Defined *s) : ImportThunkChunk(s) {}
|
||||
size_t getSize() const override { return sizeof(importThunkARM); }
|
||||
void getBaserels(std::vector<Baserel> *res) override;
|
||||
void writeTo(uint8_t *buf) const override;
|
||||
};
|
||||
|
||||
class ImportThunkChunkARM64 : public ImportThunkChunk {
|
||||
public:
|
||||
explicit ImportThunkChunkARM64(Defined *s) : ImportThunkChunk(s) {}
|
||||
size_t getSize() const override { return sizeof(importThunkARM64); }
|
||||
void writeTo(uint8_t *buf) const override;
|
||||
};
|
||||
|
||||
class RangeExtensionThunkARM : public NonSectionChunk {
|
||||
public:
|
||||
explicit RangeExtensionThunkARM(Defined *t) : target(t) {}
|
||||
size_t getSize() const override;
|
||||
void writeTo(uint8_t *buf) const override;
|
||||
|
||||
Defined *target;
|
||||
};
|
||||
|
||||
class RangeExtensionThunkARM64 : public NonSectionChunk {
|
||||
public:
|
||||
explicit RangeExtensionThunkARM64(Defined *t) : target(t) {}
|
||||
size_t getSize() const override;
|
||||
void writeTo(uint8_t *buf) const override;
|
||||
|
||||
Defined *target;
|
||||
};
|
||||
|
||||
// Windows-specific.
|
||||
// See comments for DefinedLocalImport class.
|
||||
class LocalImportChunk : public NonSectionChunk {
|
||||
public:
|
||||
explicit LocalImportChunk(Defined *s) : sym(s) {
|
||||
setAlignment(config->wordsize);
|
||||
}
|
||||
size_t getSize() const override;
|
||||
void getBaserels(std::vector<Baserel> *res) override;
|
||||
void writeTo(uint8_t *buf) const override;
|
||||
|
||||
private:
|
||||
Defined *sym;
|
||||
};
|
||||
|
||||
// Duplicate RVAs are not allowed in RVA tables, so unique symbols by chunk and
|
||||
// offset into the chunk. Order does not matter as the RVA table will be sorted
|
||||
// later.
|
||||
struct ChunkAndOffset {
|
||||
Chunk *inputChunk;
|
||||
uint32_t offset;
|
||||
|
||||
struct DenseMapInfo {
|
||||
static ChunkAndOffset getEmptyKey() {
|
||||
return {llvm::DenseMapInfo<Chunk *>::getEmptyKey(), 0};
|
||||
}
|
||||
static ChunkAndOffset getTombstoneKey() {
|
||||
return {llvm::DenseMapInfo<Chunk *>::getTombstoneKey(), 0};
|
||||
}
|
||||
static unsigned getHashValue(const ChunkAndOffset &co) {
|
||||
return llvm::DenseMapInfo<std::pair<Chunk *, uint32_t>>::getHashValue(
|
||||
{co.inputChunk, co.offset});
|
||||
}
|
||||
static bool isEqual(const ChunkAndOffset &lhs, const ChunkAndOffset &rhs) {
|
||||
return lhs.inputChunk == rhs.inputChunk && lhs.offset == rhs.offset;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
using SymbolRVASet = llvm::DenseSet<ChunkAndOffset>;
|
||||
|
||||
// Table which contains symbol RVAs. Used for /safeseh and /guard:cf.
|
||||
class RVATableChunk : public NonSectionChunk {
|
||||
public:
|
||||
explicit RVATableChunk(SymbolRVASet s) : syms(std::move(s)) {}
|
||||
size_t getSize() const override { return syms.size() * 4; }
|
||||
void writeTo(uint8_t *buf) const override;
|
||||
|
||||
private:
|
||||
SymbolRVASet syms;
|
||||
};
|
||||
|
||||
// Windows-specific.
|
||||
// This class represents a block in .reloc section.
|
||||
// See the PE/COFF spec 5.6 for details.
|
||||
class BaserelChunk : public NonSectionChunk {
|
||||
public:
|
||||
BaserelChunk(uint32_t page, Baserel *begin, Baserel *end);
|
||||
size_t getSize() const override { return data.size(); }
|
||||
void writeTo(uint8_t *buf) const override;
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> data;
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
uint32_t rva;
|
||||
uint8_t type;
|
||||
};
|
||||
|
||||
// This is a placeholder Chunk, to allow attaching a DefinedSynthetic to a
|
||||
// specific place in a section, without any data. This is used for the MinGW
|
||||
// specific symbol __RUNTIME_PSEUDO_RELOC_LIST_END__, even though the concept
|
||||
// of an empty chunk isn't MinGW specific.
|
||||
class EmptyChunk : public NonSectionChunk {
|
||||
public:
|
||||
EmptyChunk() {}
|
||||
size_t getSize() const override { return 0; }
|
||||
void writeTo(uint8_t *buf) const override {}
|
||||
};
|
||||
|
||||
// MinGW specific, for the "automatic import of variables from DLLs" feature.
|
||||
// This provides the table of runtime pseudo relocations, for variable
|
||||
// references that turned out to need to be imported from a DLL even though
|
||||
// the reference didn't use the dllimport attribute. The MinGW runtime will
|
||||
// process this table after loading, before handling control over to user
|
||||
// code.
|
||||
class PseudoRelocTableChunk : public NonSectionChunk {
|
||||
public:
|
||||
PseudoRelocTableChunk(std::vector<RuntimePseudoReloc> &relocs)
|
||||
: relocs(std::move(relocs)) {
|
||||
setAlignment(4);
|
||||
}
|
||||
size_t getSize() const override;
|
||||
void writeTo(uint8_t *buf) const override;
|
||||
|
||||
private:
|
||||
std::vector<RuntimePseudoReloc> relocs;
|
||||
};
|
||||
|
||||
// MinGW specific; information about one individual location in the image
|
||||
// that needs to be fixed up at runtime after loading. This represents
|
||||
// one individual element in the PseudoRelocTableChunk table.
|
||||
class RuntimePseudoReloc {
|
||||
public:
|
||||
RuntimePseudoReloc(Defined *sym, SectionChunk *target, uint32_t targetOffset,
|
||||
int flags)
|
||||
: sym(sym), target(target), targetOffset(targetOffset), flags(flags) {}
|
||||
|
||||
Defined *sym;
|
||||
SectionChunk *target;
|
||||
uint32_t targetOffset;
|
||||
// The Flags field contains the size of the relocation, in bits. No other
|
||||
// flags are currently defined.
|
||||
int flags;
|
||||
};
|
||||
|
||||
// MinGW specific. A Chunk that contains one pointer-sized absolute value.
|
||||
class AbsolutePointerChunk : public NonSectionChunk {
|
||||
public:
|
||||
AbsolutePointerChunk(uint64_t value) : value(value) {
|
||||
setAlignment(getSize());
|
||||
}
|
||||
size_t getSize() const override;
|
||||
void writeTo(uint8_t *buf) const override;
|
||||
|
||||
private:
|
||||
uint64_t value;
|
||||
};
|
||||
|
||||
// Return true if this file has the hotpatch flag set to true in the S_COMPILE3
|
||||
// record in codeview debug info. Also returns true for some thunks synthesized
|
||||
// by the linker.
|
||||
inline bool Chunk::isHotPatchable() const {
|
||||
if (auto *sc = dyn_cast<SectionChunk>(this))
|
||||
return sc->file->hotPatchable;
|
||||
else if (isa<ImportThunkChunk>(this))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void applyMOV32T(uint8_t *off, uint32_t v);
|
||||
void applyBranch24T(uint8_t *off, int32_t v);
|
||||
|
||||
void applyArm64Addr(uint8_t *off, uint64_t s, uint64_t p, int shift);
|
||||
void applyArm64Imm(uint8_t *off, uint64_t imm, uint32_t rangeLimit);
|
||||
void applyArm64Branch26(uint8_t *off, int64_t v);
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
namespace llvm {
|
||||
template <>
|
||||
struct DenseMapInfo<lld::coff::ChunkAndOffset>
|
||||
: lld::coff::ChunkAndOffset::DenseMapInfo {};
|
||||
}
|
||||
|
||||
#endif
|
||||
232
deps/lld/COFF/Config.h
vendored
232
deps/lld/COFF/Config.h
vendored
@ -1,232 +0,0 @@
|
||||
//===- Config.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_CONFIG_H
|
||||
#define LLD_COFF_CONFIG_H
|
||||
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include "llvm/Support/CachePruning.h"
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN;
|
||||
using llvm::COFF::WindowsSubsystem;
|
||||
using llvm::StringRef;
|
||||
class DefinedAbsolute;
|
||||
class DefinedRelative;
|
||||
class StringChunk;
|
||||
class Symbol;
|
||||
class InputFile;
|
||||
|
||||
// Short aliases.
|
||||
static const auto AMD64 = llvm::COFF::IMAGE_FILE_MACHINE_AMD64;
|
||||
static const auto ARM64 = llvm::COFF::IMAGE_FILE_MACHINE_ARM64;
|
||||
static const auto ARMNT = llvm::COFF::IMAGE_FILE_MACHINE_ARMNT;
|
||||
static const auto I386 = llvm::COFF::IMAGE_FILE_MACHINE_I386;
|
||||
|
||||
// Represents an /export option.
|
||||
struct Export {
|
||||
StringRef name; // N in /export:N or /export:E=N
|
||||
StringRef extName; // E in /export:E=N
|
||||
Symbol *sym = nullptr;
|
||||
uint16_t ordinal = 0;
|
||||
bool noname = false;
|
||||
bool data = false;
|
||||
bool isPrivate = false;
|
||||
bool constant = false;
|
||||
|
||||
// If an export is a form of /export:foo=dllname.bar, that means
|
||||
// that foo should be exported as an alias to bar in the DLL.
|
||||
// forwardTo is set to "dllname.bar" part. Usually empty.
|
||||
StringRef forwardTo;
|
||||
StringChunk *forwardChunk = nullptr;
|
||||
|
||||
// True if this /export option was in .drectves section.
|
||||
bool directives = false;
|
||||
StringRef symbolName;
|
||||
StringRef exportName; // Name in DLL
|
||||
|
||||
bool operator==(const Export &e) {
|
||||
return (name == e.name && extName == e.extName &&
|
||||
ordinal == e.ordinal && noname == e.noname &&
|
||||
data == e.data && isPrivate == e.isPrivate);
|
||||
}
|
||||
};
|
||||
|
||||
enum class DebugType {
|
||||
None = 0x0,
|
||||
CV = 0x1, /// CodeView
|
||||
PData = 0x2, /// Procedure Data
|
||||
Fixup = 0x4, /// Relocation Table
|
||||
};
|
||||
|
||||
enum class GuardCFLevel {
|
||||
Off,
|
||||
NoLongJmp, // Emit gfids but no longjmp tables
|
||||
Full, // Enable all protections.
|
||||
};
|
||||
|
||||
// Global configuration.
|
||||
struct Configuration {
|
||||
enum ManifestKind { SideBySide, Embed, No };
|
||||
bool is64() { return machine == AMD64 || machine == ARM64; }
|
||||
|
||||
llvm::COFF::MachineTypes machine = IMAGE_FILE_MACHINE_UNKNOWN;
|
||||
size_t wordsize;
|
||||
bool verbose = false;
|
||||
WindowsSubsystem subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN;
|
||||
Symbol *entry = nullptr;
|
||||
bool noEntry = false;
|
||||
std::string outputFile;
|
||||
std::string importName;
|
||||
bool demangle = true;
|
||||
bool doGC = true;
|
||||
bool doICF = true;
|
||||
bool tailMerge;
|
||||
bool relocatable = true;
|
||||
bool forceMultiple = false;
|
||||
bool forceMultipleRes = false;
|
||||
bool forceUnresolved = false;
|
||||
bool debug = false;
|
||||
bool debugDwarf = false;
|
||||
bool debugGHashes = false;
|
||||
bool debugSymtab = false;
|
||||
bool showTiming = false;
|
||||
bool showSummary = false;
|
||||
unsigned debugTypes = static_cast<unsigned>(DebugType::None);
|
||||
std::vector<std::string> natvisFiles;
|
||||
llvm::SmallString<128> pdbAltPath;
|
||||
llvm::SmallString<128> pdbPath;
|
||||
llvm::SmallString<128> pdbSourcePath;
|
||||
std::vector<llvm::StringRef> argv;
|
||||
|
||||
// Symbols in this set are considered as live by the garbage collector.
|
||||
std::vector<Symbol *> gcroot;
|
||||
|
||||
std::set<std::string> noDefaultLibs;
|
||||
bool noDefaultLibAll = false;
|
||||
|
||||
// True if we are creating a DLL.
|
||||
bool dll = false;
|
||||
StringRef implib;
|
||||
std::vector<Export> exports;
|
||||
std::set<std::string> delayLoads;
|
||||
std::map<std::string, int> dllOrder;
|
||||
Symbol *delayLoadHelper = nullptr;
|
||||
|
||||
bool saveTemps = false;
|
||||
|
||||
// /guard:cf
|
||||
GuardCFLevel guardCF = GuardCFLevel::Off;
|
||||
|
||||
// Used for SafeSEH.
|
||||
bool safeSEH = false;
|
||||
Symbol *sehTable = nullptr;
|
||||
Symbol *sehCount = nullptr;
|
||||
|
||||
// Used for /opt:lldlto=N
|
||||
unsigned ltoo = 2;
|
||||
|
||||
// Used for /opt:lldltojobs=N
|
||||
unsigned thinLTOJobs = 0;
|
||||
// Used for /opt:lldltopartitions=N
|
||||
unsigned ltoPartitions = 1;
|
||||
|
||||
// Used for /opt:lldltocache=path
|
||||
StringRef ltoCache;
|
||||
// Used for /opt:lldltocachepolicy=policy
|
||||
llvm::CachePruningPolicy ltoCachePolicy;
|
||||
|
||||
// Used for /merge:from=to (e.g. /merge:.rdata=.text)
|
||||
std::map<StringRef, StringRef> merge;
|
||||
|
||||
// Used for /section=.name,{DEKPRSW} to set section attributes.
|
||||
std::map<StringRef, uint32_t> section;
|
||||
|
||||
// Options for manifest files.
|
||||
ManifestKind manifest = No;
|
||||
int manifestID = 1;
|
||||
StringRef manifestDependency;
|
||||
bool manifestUAC = true;
|
||||
std::vector<std::string> manifestInput;
|
||||
StringRef manifestLevel = "'asInvoker'";
|
||||
StringRef manifestUIAccess = "'false'";
|
||||
StringRef manifestFile;
|
||||
|
||||
// Used for /aligncomm.
|
||||
std::map<std::string, int> alignComm;
|
||||
|
||||
// Used for /failifmismatch.
|
||||
std::map<StringRef, std::pair<StringRef, InputFile *>> mustMatch;
|
||||
|
||||
// Used for /alternatename.
|
||||
std::map<StringRef, StringRef> alternateNames;
|
||||
|
||||
// Used for /order.
|
||||
llvm::StringMap<int> order;
|
||||
|
||||
// Used for /lldmap.
|
||||
std::string mapFile;
|
||||
|
||||
// Used for /thinlto-index-only:
|
||||
llvm::StringRef thinLTOIndexOnlyArg;
|
||||
|
||||
// Used for /thinlto-object-prefix-replace:
|
||||
std::pair<llvm::StringRef, llvm::StringRef> thinLTOPrefixReplace;
|
||||
|
||||
// Used for /thinlto-object-suffix-replace:
|
||||
std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
|
||||
|
||||
uint64_t align = 4096;
|
||||
uint64_t imageBase = -1;
|
||||
uint64_t fileAlign = 512;
|
||||
uint64_t stackReserve = 1024 * 1024;
|
||||
uint64_t stackCommit = 4096;
|
||||
uint64_t heapReserve = 1024 * 1024;
|
||||
uint64_t heapCommit = 4096;
|
||||
uint32_t majorImageVersion = 0;
|
||||
uint32_t minorImageVersion = 0;
|
||||
uint32_t majorOSVersion = 6;
|
||||
uint32_t minorOSVersion = 0;
|
||||
uint32_t timestamp = 0;
|
||||
uint32_t functionPadMin = 0;
|
||||
bool dynamicBase = true;
|
||||
bool allowBind = true;
|
||||
bool nxCompat = true;
|
||||
bool allowIsolation = true;
|
||||
bool terminalServerAware = true;
|
||||
bool largeAddressAware = false;
|
||||
bool highEntropyVA = false;
|
||||
bool appContainer = false;
|
||||
bool mingw = false;
|
||||
bool warnMissingOrderSymbol = true;
|
||||
bool warnLocallyDefinedImported = true;
|
||||
bool warnDebugInfoUnusable = true;
|
||||
bool incremental = true;
|
||||
bool integrityCheck = false;
|
||||
bool killAt = false;
|
||||
bool repro = false;
|
||||
bool swaprunCD = false;
|
||||
bool swaprunNet = false;
|
||||
bool thinLTOEmitImportsFiles;
|
||||
bool thinLTOIndexOnly;
|
||||
};
|
||||
|
||||
extern Configuration *config;
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
736
deps/lld/COFF/DLL.cpp
vendored
736
deps/lld/COFF/DLL.cpp
vendored
@ -1,736 +0,0 @@
|
||||
//===- DLL.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 file defines various types of chunks for the DLL import or export
|
||||
// descriptor tables. They are inherently Windows-specific.
|
||||
// You need to read Microsoft PE/COFF spec to understand details
|
||||
// about the data structures.
|
||||
//
|
||||
// If you are not particularly interested in linking against Windows
|
||||
// DLL, you can skip this file, and you should still be able to
|
||||
// understand the rest of the linker.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "DLL.h"
|
||||
#include "Chunks.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::COFF;
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
namespace {
|
||||
|
||||
// Import table
|
||||
|
||||
// A chunk for the import descriptor table.
|
||||
class HintNameChunk : public NonSectionChunk {
|
||||
public:
|
||||
HintNameChunk(StringRef n, uint16_t h) : name(n), hint(h) {}
|
||||
|
||||
size_t getSize() const override {
|
||||
// Starts with 2 byte Hint field, followed by a null-terminated string,
|
||||
// ends with 0 or 1 byte padding.
|
||||
return alignTo(name.size() + 3, 2);
|
||||
}
|
||||
|
||||
void writeTo(uint8_t *buf) const override {
|
||||
memset(buf, 0, getSize());
|
||||
write16le(buf, hint);
|
||||
memcpy(buf + 2, name.data(), name.size());
|
||||
}
|
||||
|
||||
private:
|
||||
StringRef name;
|
||||
uint16_t hint;
|
||||
};
|
||||
|
||||
// A chunk for the import descriptor table.
|
||||
class LookupChunk : public NonSectionChunk {
|
||||
public:
|
||||
explicit LookupChunk(Chunk *c) : hintName(c) {
|
||||
setAlignment(config->wordsize);
|
||||
}
|
||||
size_t getSize() const override { return config->wordsize; }
|
||||
|
||||
void writeTo(uint8_t *buf) const override {
|
||||
if (config->is64())
|
||||
write64le(buf, hintName->getRVA());
|
||||
else
|
||||
write32le(buf, hintName->getRVA());
|
||||
}
|
||||
|
||||
Chunk *hintName;
|
||||
};
|
||||
|
||||
// A chunk for the import descriptor table.
|
||||
// This chunk represent import-by-ordinal symbols.
|
||||
// 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);
|
||||
}
|
||||
size_t getSize() const override { return config->wordsize; }
|
||||
|
||||
void writeTo(uint8_t *buf) const override {
|
||||
// An import-by-ordinal slot has MSB 1 to indicate that
|
||||
// this is import-by-ordinal (and not import-by-name).
|
||||
if (config->is64()) {
|
||||
write64le(buf, (1ULL << 63) | ordinal);
|
||||
} else {
|
||||
write32le(buf, (1ULL << 31) | ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t ordinal;
|
||||
};
|
||||
|
||||
// A chunk for the import descriptor table.
|
||||
class ImportDirectoryChunk : public NonSectionChunk {
|
||||
public:
|
||||
explicit ImportDirectoryChunk(Chunk *n) : dllName(n) {}
|
||||
size_t getSize() const override { return sizeof(ImportDirectoryTableEntry); }
|
||||
|
||||
void writeTo(uint8_t *buf) const override {
|
||||
memset(buf, 0, getSize());
|
||||
|
||||
auto *e = (coff_import_directory_table_entry *)(buf);
|
||||
e->ImportLookupTableRVA = lookupTab->getRVA();
|
||||
e->NameRVA = dllName->getRVA();
|
||||
e->ImportAddressTableRVA = addressTab->getRVA();
|
||||
}
|
||||
|
||||
Chunk *dllName;
|
||||
Chunk *lookupTab;
|
||||
Chunk *addressTab;
|
||||
};
|
||||
|
||||
// A chunk representing null terminator in the import table.
|
||||
// Contents of this chunk is always null bytes.
|
||||
class NullChunk : public NonSectionChunk {
|
||||
public:
|
||||
explicit NullChunk(size_t n) : size(n) { hasData = false; }
|
||||
size_t getSize() const override { return size; }
|
||||
|
||||
void writeTo(uint8_t *buf) const override {
|
||||
memset(buf, 0, size);
|
||||
}
|
||||
|
||||
private:
|
||||
size_t size;
|
||||
};
|
||||
|
||||
static std::vector<std::vector<DefinedImportData *>>
|
||||
binImports(const std::vector<DefinedImportData *> &imports) {
|
||||
// Group DLL-imported symbols by DLL name because that's how
|
||||
// symbols are layed out in the import descriptor table.
|
||||
auto less = [](const std::string &a, const std::string &b) {
|
||||
return config->dllOrder[a] < config->dllOrder[b];
|
||||
};
|
||||
std::map<std::string, std::vector<DefinedImportData *>,
|
||||
bool(*)(const std::string &, const std::string &)> m(less);
|
||||
for (DefinedImportData *sym : imports)
|
||||
m[sym->getDLLName().lower()].push_back(sym);
|
||||
|
||||
std::vector<std::vector<DefinedImportData *>> v;
|
||||
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();
|
||||
});
|
||||
v.push_back(std::move(syms));
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
// Export table
|
||||
// See Microsoft PE/COFF spec 4.3 for details.
|
||||
|
||||
// A chunk for the delay import descriptor table etnry.
|
||||
class DelayDirectoryChunk : public NonSectionChunk {
|
||||
public:
|
||||
explicit DelayDirectoryChunk(Chunk *n) : dllName(n) {}
|
||||
|
||||
size_t getSize() const override {
|
||||
return sizeof(delay_import_directory_table_entry);
|
||||
}
|
||||
|
||||
void writeTo(uint8_t *buf) const override {
|
||||
memset(buf, 0, getSize());
|
||||
|
||||
auto *e = (delay_import_directory_table_entry *)(buf);
|
||||
e->Attributes = 1;
|
||||
e->Name = dllName->getRVA();
|
||||
e->ModuleHandle = moduleHandle->getRVA();
|
||||
e->DelayImportAddressTable = addressTab->getRVA();
|
||||
e->DelayImportNameTable = nameTab->getRVA();
|
||||
}
|
||||
|
||||
Chunk *dllName;
|
||||
Chunk *moduleHandle;
|
||||
Chunk *addressTab;
|
||||
Chunk *nameTab;
|
||||
};
|
||||
|
||||
// Initial contents for delay-loaded functions.
|
||||
// This code calls __delayLoadHelper2 function to resolve a symbol
|
||||
// and then overwrites its jump table slot with the result
|
||||
// for subsequent function calls.
|
||||
static const uint8_t thunkX64[] = {
|
||||
0x48, 0x8D, 0x05, 0, 0, 0, 0, // lea rax, [__imp_<FUNCNAME>]
|
||||
0xE9, 0, 0, 0, 0, // jmp __tailMerge_<lib>
|
||||
};
|
||||
|
||||
static const uint8_t tailMergeX64[] = {
|
||||
0x51, // push rcx
|
||||
0x52, // push rdx
|
||||
0x41, 0x50, // push r8
|
||||
0x41, 0x51, // push r9
|
||||
0x48, 0x83, 0xEC, 0x48, // sub rsp, 48h
|
||||
0x66, 0x0F, 0x7F, 0x04, 0x24, // movdqa xmmword ptr [rsp], xmm0
|
||||
0x66, 0x0F, 0x7F, 0x4C, 0x24, 0x10, // movdqa xmmword ptr [rsp+10h], xmm1
|
||||
0x66, 0x0F, 0x7F, 0x54, 0x24, 0x20, // movdqa xmmword ptr [rsp+20h], xmm2
|
||||
0x66, 0x0F, 0x7F, 0x5C, 0x24, 0x30, // movdqa xmmword ptr [rsp+30h], xmm3
|
||||
0x48, 0x8B, 0xD0, // mov rdx, rax
|
||||
0x48, 0x8D, 0x0D, 0, 0, 0, 0, // lea rcx, [___DELAY_IMPORT_...]
|
||||
0xE8, 0, 0, 0, 0, // call __delayLoadHelper2
|
||||
0x66, 0x0F, 0x6F, 0x04, 0x24, // movdqa xmm0, xmmword ptr [rsp]
|
||||
0x66, 0x0F, 0x6F, 0x4C, 0x24, 0x10, // movdqa xmm1, xmmword ptr [rsp+10h]
|
||||
0x66, 0x0F, 0x6F, 0x54, 0x24, 0x20, // movdqa xmm2, xmmword ptr [rsp+20h]
|
||||
0x66, 0x0F, 0x6F, 0x5C, 0x24, 0x30, // movdqa xmm3, xmmword ptr [rsp+30h]
|
||||
0x48, 0x83, 0xC4, 0x48, // add rsp, 48h
|
||||
0x41, 0x59, // pop r9
|
||||
0x41, 0x58, // pop r8
|
||||
0x5A, // pop rdx
|
||||
0x59, // pop rcx
|
||||
0xFF, 0xE0, // jmp rax
|
||||
};
|
||||
|
||||
static const uint8_t thunkX86[] = {
|
||||
0xB8, 0, 0, 0, 0, // mov eax, offset ___imp__<FUNCNAME>
|
||||
0xE9, 0, 0, 0, 0, // jmp __tailMerge_<lib>
|
||||
};
|
||||
|
||||
static const uint8_t tailMergeX86[] = {
|
||||
0x51, // push ecx
|
||||
0x52, // push edx
|
||||
0x50, // push eax
|
||||
0x68, 0, 0, 0, 0, // push offset ___DELAY_IMPORT_DESCRIPTOR_<DLLNAME>_dll
|
||||
0xE8, 0, 0, 0, 0, // call ___delayLoadHelper2@8
|
||||
0x5A, // pop edx
|
||||
0x59, // pop ecx
|
||||
0xFF, 0xE0, // jmp eax
|
||||
};
|
||||
|
||||
static const uint8_t thunkARM[] = {
|
||||
0x40, 0xf2, 0x00, 0x0c, // mov.w ip, #0 __imp_<FUNCNAME>
|
||||
0xc0, 0xf2, 0x00, 0x0c, // mov.t ip, #0 __imp_<FUNCNAME>
|
||||
0x00, 0xf0, 0x00, 0xb8, // b.w __tailMerge_<lib>
|
||||
};
|
||||
|
||||
static const uint8_t tailMergeARM[] = {
|
||||
0x2d, 0xe9, 0x0f, 0x48, // push.w {r0, r1, r2, r3, r11, lr}
|
||||
0x0d, 0xf2, 0x10, 0x0b, // addw r11, sp, #16
|
||||
0x2d, 0xed, 0x10, 0x0b, // vpush {d0, d1, d2, d3, d4, d5, d6, d7}
|
||||
0x61, 0x46, // mov r1, ip
|
||||
0x40, 0xf2, 0x00, 0x00, // mov.w r0, #0 DELAY_IMPORT_DESCRIPTOR
|
||||
0xc0, 0xf2, 0x00, 0x00, // mov.t r0, #0 DELAY_IMPORT_DESCRIPTOR
|
||||
0x00, 0xf0, 0x00, 0xd0, // bl #0 __delayLoadHelper2
|
||||
0x84, 0x46, // mov ip, r0
|
||||
0xbd, 0xec, 0x10, 0x0b, // vpop {d0, d1, d2, d3, d4, d5, d6, d7}
|
||||
0xbd, 0xe8, 0x0f, 0x48, // pop.w {r0, r1, r2, r3, r11, lr}
|
||||
0x60, 0x47, // bx ip
|
||||
};
|
||||
|
||||
static const uint8_t thunkARM64[] = {
|
||||
0x11, 0x00, 0x00, 0x90, // adrp x17, #0 __imp_<FUNCNAME>
|
||||
0x31, 0x02, 0x00, 0x91, // add x17, x17, #0 :lo12:__imp_<FUNCNAME>
|
||||
0x00, 0x00, 0x00, 0x14, // b __tailMerge_<lib>
|
||||
};
|
||||
|
||||
static const uint8_t tailMergeARM64[] = {
|
||||
0xfd, 0x7b, 0xb3, 0xa9, // stp x29, x30, [sp, #-208]!
|
||||
0xfd, 0x03, 0x00, 0x91, // mov x29, sp
|
||||
0xe0, 0x07, 0x01, 0xa9, // stp x0, x1, [sp, #16]
|
||||
0xe2, 0x0f, 0x02, 0xa9, // stp x2, x3, [sp, #32]
|
||||
0xe4, 0x17, 0x03, 0xa9, // stp x4, x5, [sp, #48]
|
||||
0xe6, 0x1f, 0x04, 0xa9, // stp x6, x7, [sp, #64]
|
||||
0xe0, 0x87, 0x02, 0xad, // stp q0, q1, [sp, #80]
|
||||
0xe2, 0x8f, 0x03, 0xad, // stp q2, q3, [sp, #112]
|
||||
0xe4, 0x97, 0x04, 0xad, // stp q4, q5, [sp, #144]
|
||||
0xe6, 0x9f, 0x05, 0xad, // stp q6, q7, [sp, #176]
|
||||
0xe1, 0x03, 0x11, 0xaa, // mov x1, x17
|
||||
0x00, 0x00, 0x00, 0x90, // adrp x0, #0 DELAY_IMPORT_DESCRIPTOR
|
||||
0x00, 0x00, 0x00, 0x91, // add x0, x0, #0 :lo12:DELAY_IMPORT_DESCRIPTOR
|
||||
0x00, 0x00, 0x00, 0x94, // bl #0 __delayLoadHelper2
|
||||
0xf0, 0x03, 0x00, 0xaa, // mov x16, x0
|
||||
0xe6, 0x9f, 0x45, 0xad, // ldp q6, q7, [sp, #176]
|
||||
0xe4, 0x97, 0x44, 0xad, // ldp q4, q5, [sp, #144]
|
||||
0xe2, 0x8f, 0x43, 0xad, // ldp q2, q3, [sp, #112]
|
||||
0xe0, 0x87, 0x42, 0xad, // ldp q0, q1, [sp, #80]
|
||||
0xe6, 0x1f, 0x44, 0xa9, // ldp x6, x7, [sp, #64]
|
||||
0xe4, 0x17, 0x43, 0xa9, // ldp x4, x5, [sp, #48]
|
||||
0xe2, 0x0f, 0x42, 0xa9, // ldp x2, x3, [sp, #32]
|
||||
0xe0, 0x07, 0x41, 0xa9, // ldp x0, x1, [sp, #16]
|
||||
0xfd, 0x7b, 0xcd, 0xa8, // ldp x29, x30, [sp], #208
|
||||
0x00, 0x02, 0x1f, 0xd6, // br x16
|
||||
};
|
||||
|
||||
// A chunk for the delay import thunk.
|
||||
class ThunkChunkX64 : public NonSectionChunk {
|
||||
public:
|
||||
ThunkChunkX64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {}
|
||||
|
||||
size_t getSize() const override { return sizeof(thunkX64); }
|
||||
|
||||
void writeTo(uint8_t *buf) const override {
|
||||
memcpy(buf, thunkX64, sizeof(thunkX64));
|
||||
write32le(buf + 3, imp->getRVA() - rva - 7);
|
||||
write32le(buf + 8, tailMerge->getRVA() - rva - 12);
|
||||
}
|
||||
|
||||
Defined *imp = nullptr;
|
||||
Chunk *tailMerge = nullptr;
|
||||
};
|
||||
|
||||
class TailMergeChunkX64 : public NonSectionChunk {
|
||||
public:
|
||||
TailMergeChunkX64(Chunk *d, Defined *h) : desc(d), helper(h) {}
|
||||
|
||||
size_t getSize() const override { return sizeof(tailMergeX64); }
|
||||
|
||||
void writeTo(uint8_t *buf) const override {
|
||||
memcpy(buf, tailMergeX64, sizeof(tailMergeX64));
|
||||
write32le(buf + 39, desc->getRVA() - rva - 43);
|
||||
write32le(buf + 44, helper->getRVA() - rva - 48);
|
||||
}
|
||||
|
||||
Chunk *desc = nullptr;
|
||||
Defined *helper = nullptr;
|
||||
};
|
||||
|
||||
class ThunkChunkX86 : public NonSectionChunk {
|
||||
public:
|
||||
ThunkChunkX86(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {}
|
||||
|
||||
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 + 6, tailMerge->getRVA() - rva - 10);
|
||||
}
|
||||
|
||||
void getBaserels(std::vector<Baserel> *res) override {
|
||||
res->emplace_back(rva + 1);
|
||||
}
|
||||
|
||||
Defined *imp = nullptr;
|
||||
Chunk *tailMerge = nullptr;
|
||||
};
|
||||
|
||||
class TailMergeChunkX86 : public NonSectionChunk {
|
||||
public:
|
||||
TailMergeChunkX86(Chunk *d, Defined *h) : desc(d), helper(h) {}
|
||||
|
||||
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 + 9, helper->getRVA() - rva - 13);
|
||||
}
|
||||
|
||||
void getBaserels(std::vector<Baserel> *res) override {
|
||||
res->emplace_back(rva + 4);
|
||||
}
|
||||
|
||||
Chunk *desc = nullptr;
|
||||
Defined *helper = nullptr;
|
||||
};
|
||||
|
||||
class ThunkChunkARM : public NonSectionChunk {
|
||||
public:
|
||||
ThunkChunkARM(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {}
|
||||
|
||||
size_t getSize() const override { return sizeof(thunkARM); }
|
||||
|
||||
void writeTo(uint8_t *buf) const override {
|
||||
memcpy(buf, thunkARM, sizeof(thunkARM));
|
||||
applyMOV32T(buf + 0, imp->getRVA() + config->imageBase);
|
||||
applyBranch24T(buf + 8, tailMerge->getRVA() - rva - 12);
|
||||
}
|
||||
|
||||
void getBaserels(std::vector<Baserel> *res) override {
|
||||
res->emplace_back(rva + 0, IMAGE_REL_BASED_ARM_MOV32T);
|
||||
}
|
||||
|
||||
Defined *imp = nullptr;
|
||||
Chunk *tailMerge = nullptr;
|
||||
};
|
||||
|
||||
class TailMergeChunkARM : public NonSectionChunk {
|
||||
public:
|
||||
TailMergeChunkARM(Chunk *d, Defined *h) : desc(d), helper(h) {}
|
||||
|
||||
size_t getSize() const override { return sizeof(tailMergeARM); }
|
||||
|
||||
void writeTo(uint8_t *buf) const override {
|
||||
memcpy(buf, tailMergeARM, sizeof(tailMergeARM));
|
||||
applyMOV32T(buf + 14, desc->getRVA() + config->imageBase);
|
||||
applyBranch24T(buf + 22, helper->getRVA() - rva - 26);
|
||||
}
|
||||
|
||||
void getBaserels(std::vector<Baserel> *res) override {
|
||||
res->emplace_back(rva + 14, IMAGE_REL_BASED_ARM_MOV32T);
|
||||
}
|
||||
|
||||
Chunk *desc = nullptr;
|
||||
Defined *helper = nullptr;
|
||||
};
|
||||
|
||||
class ThunkChunkARM64 : public NonSectionChunk {
|
||||
public:
|
||||
ThunkChunkARM64(Defined *i, Chunk *tm) : imp(i), tailMerge(tm) {}
|
||||
|
||||
size_t getSize() const override { return sizeof(thunkARM64); }
|
||||
|
||||
void writeTo(uint8_t *buf) const override {
|
||||
memcpy(buf, thunkARM64, sizeof(thunkARM64));
|
||||
applyArm64Addr(buf + 0, imp->getRVA(), rva + 0, 12);
|
||||
applyArm64Imm(buf + 4, imp->getRVA() & 0xfff, 0);
|
||||
applyArm64Branch26(buf + 8, tailMerge->getRVA() - rva - 8);
|
||||
}
|
||||
|
||||
Defined *imp = nullptr;
|
||||
Chunk *tailMerge = nullptr;
|
||||
};
|
||||
|
||||
class TailMergeChunkARM64 : public NonSectionChunk {
|
||||
public:
|
||||
TailMergeChunkARM64(Chunk *d, Defined *h) : desc(d), helper(h) {}
|
||||
|
||||
size_t getSize() const override { return sizeof(tailMergeARM64); }
|
||||
|
||||
void writeTo(uint8_t *buf) const override {
|
||||
memcpy(buf, tailMergeARM64, sizeof(tailMergeARM64));
|
||||
applyArm64Addr(buf + 44, desc->getRVA(), rva + 44, 12);
|
||||
applyArm64Imm(buf + 48, desc->getRVA() & 0xfff, 0);
|
||||
applyArm64Branch26(buf + 52, helper->getRVA() - rva - 52);
|
||||
}
|
||||
|
||||
Chunk *desc = nullptr;
|
||||
Defined *helper = nullptr;
|
||||
};
|
||||
|
||||
// A chunk for the import descriptor table.
|
||||
class DelayAddressChunk : public NonSectionChunk {
|
||||
public:
|
||||
explicit DelayAddressChunk(Chunk *c) : thunk(c) {
|
||||
setAlignment(config->wordsize);
|
||||
}
|
||||
size_t getSize() const override { return config->wordsize; }
|
||||
|
||||
void writeTo(uint8_t *buf) const override {
|
||||
if (config->is64()) {
|
||||
write64le(buf, thunk->getRVA() + config->imageBase);
|
||||
} else {
|
||||
uint32_t bit = 0;
|
||||
// Pointer to thumb code must have the LSB set, so adjust it.
|
||||
if (config->machine == ARMNT)
|
||||
bit = 1;
|
||||
write32le(buf, (thunk->getRVA() + config->imageBase) | bit);
|
||||
}
|
||||
}
|
||||
|
||||
void getBaserels(std::vector<Baserel> *res) override {
|
||||
res->emplace_back(rva);
|
||||
}
|
||||
|
||||
Chunk *thunk;
|
||||
};
|
||||
|
||||
// Export table
|
||||
// Read Microsoft PE/COFF spec 5.3 for details.
|
||||
|
||||
// A chunk for the export descriptor table.
|
||||
class ExportDirectoryChunk : public NonSectionChunk {
|
||||
public:
|
||||
ExportDirectoryChunk(int i, int j, Chunk *d, Chunk *a, Chunk *n, Chunk *o)
|
||||
: maxOrdinal(i), nameTabSize(j), dllName(d), addressTab(a), nameTab(n),
|
||||
ordinalTab(o) {}
|
||||
|
||||
size_t getSize() const override {
|
||||
return sizeof(export_directory_table_entry);
|
||||
}
|
||||
|
||||
void writeTo(uint8_t *buf) const override {
|
||||
memset(buf, 0, getSize());
|
||||
|
||||
auto *e = (export_directory_table_entry *)(buf);
|
||||
e->NameRVA = dllName->getRVA();
|
||||
e->OrdinalBase = 0;
|
||||
e->AddressTableEntries = maxOrdinal + 1;
|
||||
e->NumberOfNamePointers = nameTabSize;
|
||||
e->ExportAddressTableRVA = addressTab->getRVA();
|
||||
e->NamePointerRVA = nameTab->getRVA();
|
||||
e->OrdinalTableRVA = ordinalTab->getRVA();
|
||||
}
|
||||
|
||||
uint16_t maxOrdinal;
|
||||
uint16_t nameTabSize;
|
||||
Chunk *dllName;
|
||||
Chunk *addressTab;
|
||||
Chunk *nameTab;
|
||||
Chunk *ordinalTab;
|
||||
};
|
||||
|
||||
class AddressTableChunk : public NonSectionChunk {
|
||||
public:
|
||||
explicit AddressTableChunk(size_t maxOrdinal) : size(maxOrdinal + 1) {}
|
||||
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;
|
||||
uint32_t bit = 0;
|
||||
// Pointer to thumb code must have the LSB set, so adjust it.
|
||||
if (config->machine == ARMNT && !e.data)
|
||||
bit = 1;
|
||||
if (e.forwardChunk) {
|
||||
write32le(p, e.forwardChunk->getRVA() | bit);
|
||||
} else {
|
||||
write32le(p, cast<Defined>(e.sym)->getRVA() | bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
size_t size;
|
||||
};
|
||||
|
||||
class NamePointersChunk : public NonSectionChunk {
|
||||
public:
|
||||
explicit NamePointersChunk(std::vector<Chunk *> &v) : chunks(v) {}
|
||||
size_t getSize() const override { return chunks.size() * 4; }
|
||||
|
||||
void writeTo(uint8_t *buf) const override {
|
||||
for (Chunk *c : chunks) {
|
||||
write32le(buf, c->getRVA());
|
||||
buf += 4;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Chunk *> chunks;
|
||||
};
|
||||
|
||||
class ExportOrdinalChunk : public NonSectionChunk {
|
||||
public:
|
||||
explicit ExportOrdinalChunk(size_t i) : size(i) {}
|
||||
size_t getSize() const override { return size * 2; }
|
||||
|
||||
void writeTo(uint8_t *buf) const override {
|
||||
for (Export &e : config->exports) {
|
||||
if (e.noname)
|
||||
continue;
|
||||
write16le(buf, e.ordinal);
|
||||
buf += 2;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
size_t size;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void IdataContents::create() {
|
||||
std::vector<std::vector<DefinedImportData *>> v = binImports(imports);
|
||||
|
||||
// Create .idata contents for each DLL.
|
||||
for (std::vector<DefinedImportData *> &syms : v) {
|
||||
// Create lookup and address tables. If they have external names,
|
||||
// we need to create hintName chunks to store the names.
|
||||
// If they don't (if they are import-by-ordinals), we store only
|
||||
// ordinal values to the table.
|
||||
size_t base = lookups.size();
|
||||
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));
|
||||
continue;
|
||||
}
|
||||
auto *c = make<HintNameChunk>(s->getExternalName(), ord);
|
||||
lookups.push_back(make<LookupChunk>(c));
|
||||
addresses.push_back(make<LookupChunk>(c));
|
||||
hints.push_back(c);
|
||||
}
|
||||
// Terminate with null values.
|
||||
lookups.push_back(make<NullChunk>(config->wordsize));
|
||||
addresses.push_back(make<NullChunk>(config->wordsize));
|
||||
|
||||
for (int i = 0, e = syms.size(); i < e; ++i)
|
||||
syms[i]->setLocation(addresses[base + i]);
|
||||
|
||||
// Create the import table header.
|
||||
dllNames.push_back(make<StringChunk>(syms[0]->getDLLName()));
|
||||
auto *dir = make<ImportDirectoryChunk>(dllNames.back());
|
||||
dir->lookupTab = lookups[base];
|
||||
dir->addressTab = addresses[base];
|
||||
dirs.push_back(dir);
|
||||
}
|
||||
// Add null terminator.
|
||||
dirs.push_back(make<NullChunk>(sizeof(ImportDirectoryTableEntry)));
|
||||
}
|
||||
|
||||
std::vector<Chunk *> DelayLoadContents::getChunks() {
|
||||
std::vector<Chunk *> v;
|
||||
v.insert(v.end(), dirs.begin(), dirs.end());
|
||||
v.insert(v.end(), names.begin(), names.end());
|
||||
v.insert(v.end(), hintNames.begin(), hintNames.end());
|
||||
v.insert(v.end(), dllNames.begin(), dllNames.end());
|
||||
return v;
|
||||
}
|
||||
|
||||
std::vector<Chunk *> DelayLoadContents::getDataChunks() {
|
||||
std::vector<Chunk *> v;
|
||||
v.insert(v.end(), moduleHandles.begin(), moduleHandles.end());
|
||||
v.insert(v.end(), addresses.begin(), addresses.end());
|
||||
return v;
|
||||
}
|
||||
|
||||
uint64_t DelayLoadContents::getDirSize() {
|
||||
return dirs.size() * sizeof(delay_import_directory_table_entry);
|
||||
}
|
||||
|
||||
void DelayLoadContents::create(Defined *h) {
|
||||
helper = h;
|
||||
std::vector<std::vector<DefinedImportData *>> v = binImports(imports);
|
||||
|
||||
// Create .didat contents for each DLL.
|
||||
for (std::vector<DefinedImportData *> &syms : v) {
|
||||
// Create the delay import table header.
|
||||
dllNames.push_back(make<StringChunk>(syms[0]->getDLLName()));
|
||||
auto *dir = make<DelayDirectoryChunk>(dllNames.back());
|
||||
|
||||
size_t base = addresses.size();
|
||||
Chunk *tm = newTailMergeChunk(dir);
|
||||
for (DefinedImportData *s : syms) {
|
||||
Chunk *t = newThunkChunk(s, tm);
|
||||
auto *a = make<DelayAddressChunk>(t);
|
||||
addresses.push_back(a);
|
||||
thunks.push_back(t);
|
||||
StringRef extName = s->getExternalName();
|
||||
if (extName.empty()) {
|
||||
names.push_back(make<OrdinalOnlyChunk>(s->getOrdinal()));
|
||||
} else {
|
||||
auto *c = make<HintNameChunk>(extName, 0);
|
||||
names.push_back(make<LookupChunk>(c));
|
||||
hintNames.push_back(c);
|
||||
}
|
||||
}
|
||||
thunks.push_back(tm);
|
||||
// Terminate with null values.
|
||||
addresses.push_back(make<NullChunk>(8));
|
||||
names.push_back(make<NullChunk>(8));
|
||||
|
||||
for (int i = 0, e = syms.size(); i < e; ++i)
|
||||
syms[i]->setLocation(addresses[base + i]);
|
||||
auto *mh = make<NullChunk>(8);
|
||||
mh->setAlignment(8);
|
||||
moduleHandles.push_back(mh);
|
||||
|
||||
// Fill the delay import table header fields.
|
||||
dir->moduleHandle = mh;
|
||||
dir->addressTab = addresses[base];
|
||||
dir->nameTab = names[base];
|
||||
dirs.push_back(dir);
|
||||
}
|
||||
// Add null terminator.
|
||||
dirs.push_back(make<NullChunk>(sizeof(delay_import_directory_table_entry)));
|
||||
}
|
||||
|
||||
Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) {
|
||||
switch (config->machine) {
|
||||
case AMD64:
|
||||
return make<TailMergeChunkX64>(dir, helper);
|
||||
case I386:
|
||||
return make<TailMergeChunkX86>(dir, helper);
|
||||
case ARMNT:
|
||||
return make<TailMergeChunkARM>(dir, helper);
|
||||
case ARM64:
|
||||
return make<TailMergeChunkARM64>(dir, helper);
|
||||
default:
|
||||
llvm_unreachable("unsupported machine type");
|
||||
}
|
||||
}
|
||||
|
||||
Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s,
|
||||
Chunk *tailMerge) {
|
||||
switch (config->machine) {
|
||||
case AMD64:
|
||||
return make<ThunkChunkX64>(s, tailMerge);
|
||||
case I386:
|
||||
return make<ThunkChunkX86>(s, tailMerge);
|
||||
case ARMNT:
|
||||
return make<ThunkChunkARM>(s, tailMerge);
|
||||
case ARM64:
|
||||
return make<ThunkChunkARM64>(s, tailMerge);
|
||||
default:
|
||||
llvm_unreachable("unsupported machine type");
|
||||
}
|
||||
}
|
||||
|
||||
EdataContents::EdataContents() {
|
||||
uint16_t maxOrdinal = 0;
|
||||
for (Export &e : config->exports)
|
||||
maxOrdinal = std::max(maxOrdinal, e.ordinal);
|
||||
|
||||
auto *dllName = make<StringChunk>(sys::path::filename(config->outputFile));
|
||||
auto *addressTab = make<AddressTableChunk>(maxOrdinal);
|
||||
std::vector<Chunk *> names;
|
||||
for (Export &e : config->exports)
|
||||
if (!e.noname)
|
||||
names.push_back(make<StringChunk>(e.exportName));
|
||||
|
||||
std::vector<Chunk *> forwards;
|
||||
for (Export &e : config->exports) {
|
||||
if (e.forwardTo.empty())
|
||||
continue;
|
||||
e.forwardChunk = make<StringChunk>(e.forwardTo);
|
||||
forwards.push_back(e.forwardChunk);
|
||||
}
|
||||
|
||||
auto *nameTab = make<NamePointersChunk>(names);
|
||||
auto *ordinalTab = make<ExportOrdinalChunk>(names.size());
|
||||
auto *dir = make<ExportDirectoryChunk>(maxOrdinal, names.size(), dllName,
|
||||
addressTab, nameTab, ordinalTab);
|
||||
chunks.push_back(dir);
|
||||
chunks.push_back(dllName);
|
||||
chunks.push_back(addressTab);
|
||||
chunks.push_back(nameTab);
|
||||
chunks.push_back(ordinalTab);
|
||||
chunks.insert(chunks.end(), names.begin(), names.end());
|
||||
chunks.insert(chunks.end(), forwards.begin(), forwards.end());
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
82
deps/lld/COFF/DLL.h
vendored
82
deps/lld/COFF/DLL.h
vendored
@ -1,82 +0,0 @@
|
||||
//===- DLL.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_DLL_H
|
||||
#define LLD_COFF_DLL_H
|
||||
|
||||
#include "Chunks.h"
|
||||
#include "Symbols.h"
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
// Windows-specific.
|
||||
// IdataContents creates all chunks for the DLL import table.
|
||||
// You are supposed to call add() to add symbols and then
|
||||
// call create() to populate the chunk vectors.
|
||||
class IdataContents {
|
||||
public:
|
||||
void add(DefinedImportData *sym) { imports.push_back(sym); }
|
||||
bool empty() { return imports.empty(); }
|
||||
|
||||
void create();
|
||||
|
||||
std::vector<DefinedImportData *> imports;
|
||||
std::vector<Chunk *> dirs;
|
||||
std::vector<Chunk *> lookups;
|
||||
std::vector<Chunk *> addresses;
|
||||
std::vector<Chunk *> hints;
|
||||
std::vector<Chunk *> dllNames;
|
||||
};
|
||||
|
||||
// Windows-specific.
|
||||
// DelayLoadContents creates all chunks for the delay-load DLL import table.
|
||||
class DelayLoadContents {
|
||||
public:
|
||||
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; }
|
||||
|
||||
uint64_t getDirRVA() { return dirs[0]->getRVA(); }
|
||||
uint64_t getDirSize();
|
||||
|
||||
private:
|
||||
Chunk *newThunkChunk(DefinedImportData *s, Chunk *tailMerge);
|
||||
Chunk *newTailMergeChunk(Chunk *dir);
|
||||
|
||||
Defined *helper;
|
||||
std::vector<DefinedImportData *> imports;
|
||||
std::vector<Chunk *> dirs;
|
||||
std::vector<Chunk *> moduleHandles;
|
||||
std::vector<Chunk *> addresses;
|
||||
std::vector<Chunk *> names;
|
||||
std::vector<Chunk *> hintNames;
|
||||
std::vector<Chunk *> thunks;
|
||||
std::vector<Chunk *> dllNames;
|
||||
};
|
||||
|
||||
// Windows-specific.
|
||||
// EdataContents creates all chunks for the DLL export table.
|
||||
class EdataContents {
|
||||
public:
|
||||
EdataContents();
|
||||
std::vector<Chunk *> chunks;
|
||||
|
||||
uint64_t getRVA() { return chunks[0]->getRVA(); }
|
||||
uint64_t getSize() {
|
||||
return chunks.back()->getRVA() + chunks.back()->getSize() - getRVA();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
268
deps/lld/COFF/DebugTypes.cpp
vendored
268
deps/lld/COFF/DebugTypes.cpp
vendored
@ -1,268 +0,0 @@
|
||||
//===- DebugTypes.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 "DebugTypes.h"
|
||||
#include "Driver.h"
|
||||
#include "InputFiles.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/DebugInfo/PDB/GenericError.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::coff;
|
||||
using namespace llvm;
|
||||
using namespace llvm::codeview;
|
||||
|
||||
namespace {
|
||||
// The TypeServerSource class represents a PDB type server, a file referenced by
|
||||
// OBJ files compiled with MSVC /Zi. A single PDB can be shared by several OBJ
|
||||
// files, therefore there must be only once instance per OBJ lot. The file path
|
||||
// is discovered from the dependent OBJ's debug type stream. The
|
||||
// TypeServerSource object is then queued and loaded by the COFF Driver. The
|
||||
// debug type stream for such PDB files will be merged first in the final PDB,
|
||||
// before any dependent OBJ.
|
||||
class TypeServerSource : public TpiSource {
|
||||
public:
|
||||
explicit TypeServerSource(MemoryBufferRef m, llvm::pdb::NativeSession *s)
|
||||
: TpiSource(PDB, nullptr), session(s), mb(m) {}
|
||||
|
||||
// Queue a PDB type server for loading in the COFF Driver
|
||||
static void enqueue(const ObjFile *dependentFile,
|
||||
const TypeServer2Record &ts);
|
||||
|
||||
// Create an instance
|
||||
static Expected<TypeServerSource *> getInstance(MemoryBufferRef m);
|
||||
|
||||
// Fetch the PDB instance loaded for a corresponding dependent OBJ.
|
||||
static Expected<TypeServerSource *>
|
||||
findFromFile(const ObjFile *dependentFile);
|
||||
|
||||
static std::map<std::string, std::pair<std::string, TypeServerSource *>>
|
||||
instances;
|
||||
|
||||
// The interface to the PDB (if it was opened successfully)
|
||||
std::unique_ptr<llvm::pdb::NativeSession> session;
|
||||
|
||||
private:
|
||||
MemoryBufferRef mb;
|
||||
};
|
||||
|
||||
// This class represents the debug type stream of an OBJ file that depends on a
|
||||
// PDB type server (see TypeServerSource).
|
||||
class UseTypeServerSource : public TpiSource {
|
||||
public:
|
||||
UseTypeServerSource(const ObjFile *f, const TypeServer2Record *ts)
|
||||
: TpiSource(UsingPDB, f), typeServerDependency(*ts) {}
|
||||
|
||||
// Information about the PDB type server dependency, that needs to be loaded
|
||||
// in before merging this OBJ.
|
||||
TypeServer2Record typeServerDependency;
|
||||
};
|
||||
|
||||
// This class represents the debug type stream of a Microsoft precompiled
|
||||
// headers OBJ (PCH OBJ). This OBJ kind needs to be merged first in the output
|
||||
// PDB, before any other OBJs that depend on this. Note that only MSVC generate
|
||||
// such files, clang does not.
|
||||
class PrecompSource : public TpiSource {
|
||||
public:
|
||||
PrecompSource(const ObjFile *f) : TpiSource(PCH, f) {}
|
||||
};
|
||||
|
||||
// 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(const ObjFile *f, const PrecompRecord *precomp)
|
||||
: TpiSource(UsingPCH, f), precompDependency(*precomp) {}
|
||||
|
||||
// Information about the Precomp OBJ dependency, that needs to be loaded in
|
||||
// before merging this OBJ.
|
||||
PrecompRecord precompDependency;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
static std::vector<std::unique_ptr<TpiSource>> GC;
|
||||
|
||||
TpiSource::TpiSource(TpiKind k, const ObjFile *f) : kind(k), file(f) {
|
||||
GC.push_back(std::unique_ptr<TpiSource>(this));
|
||||
}
|
||||
|
||||
TpiSource *lld::coff::makeTpiSource(const ObjFile *f) {
|
||||
return new TpiSource(TpiSource::Regular, f);
|
||||
}
|
||||
|
||||
TpiSource *lld::coff::makeUseTypeServerSource(const ObjFile *f,
|
||||
const TypeServer2Record *ts) {
|
||||
TypeServerSource::enqueue(f, *ts);
|
||||
return new UseTypeServerSource(f, ts);
|
||||
}
|
||||
|
||||
TpiSource *lld::coff::makePrecompSource(const ObjFile *f) {
|
||||
return new PrecompSource(f);
|
||||
}
|
||||
|
||||
TpiSource *lld::coff::makeUsePrecompSource(const ObjFile *f,
|
||||
const PrecompRecord *precomp) {
|
||||
return new UsePrecompSource(f, precomp);
|
||||
}
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
template <>
|
||||
const PrecompRecord &retrieveDependencyInfo(const TpiSource *source) {
|
||||
assert(source->kind == TpiSource::UsingPCH);
|
||||
return ((const UsePrecompSource *)source)->precompDependency;
|
||||
}
|
||||
|
||||
template <>
|
||||
const TypeServer2Record &retrieveDependencyInfo(const TpiSource *source) {
|
||||
assert(source->kind == TpiSource::UsingPDB);
|
||||
return ((const UseTypeServerSource *)source)->typeServerDependency;
|
||||
}
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
std::map<std::string, std::pair<std::string, TypeServerSource *>>
|
||||
TypeServerSource::instances;
|
||||
|
||||
// Make a PDB path assuming the PDB is in the same folder as the OBJ
|
||||
static std::string getPdbBaseName(const ObjFile *file, StringRef tSPath) {
|
||||
StringRef localPath =
|
||||
!file->parentName.empty() ? file->parentName : file->getName();
|
||||
SmallString<128> path = sys::path::parent_path(localPath);
|
||||
|
||||
// Currently, type server PDBs are only created by MSVC cl, which only runs
|
||||
// on Windows, so we can assume type server paths are Windows style.
|
||||
sys::path::append(path, sys::path::filename(tSPath, sys::path::Style::windows));
|
||||
return path.str();
|
||||
}
|
||||
|
||||
// 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.
|
||||
static std::string normalizePdbPath(StringRef path) {
|
||||
#if defined(_WIN32)
|
||||
return path.lower();
|
||||
#else // LINUX
|
||||
return path;
|
||||
#endif
|
||||
}
|
||||
|
||||
// If existing, return the actual PDB path on disk.
|
||||
static Optional<std::string> findPdbPath(StringRef pdbPath,
|
||||
const 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
|
||||
// silently.
|
||||
if (llvm::sys::fs::exists(pdbPath))
|
||||
return normalizePdbPath(pdbPath);
|
||||
std::string ret = getPdbBaseName(dependentFile, pdbPath);
|
||||
if (llvm::sys::fs::exists(ret))
|
||||
return normalizePdbPath(ret);
|
||||
return None;
|
||||
}
|
||||
|
||||
// Fetch the PDB instance that was already loaded by the COFF Driver.
|
||||
Expected<TypeServerSource *>
|
||||
TypeServerSource::findFromFile(const ObjFile *dependentFile) {
|
||||
const TypeServer2Record &ts =
|
||||
retrieveDependencyInfo<TypeServer2Record>(dependentFile->debugTypesObj);
|
||||
|
||||
Optional<std::string> p = findPdbPath(ts.Name, dependentFile);
|
||||
if (!p)
|
||||
return createFileError(ts.Name, errorCodeToError(std::error_code(
|
||||
ENOENT, std::generic_category())));
|
||||
|
||||
auto it = TypeServerSource::instances.find(*p);
|
||||
// The PDB file exists on disk, at this point we expect it to have been
|
||||
// inserted in the map by TypeServerSource::loadPDB()
|
||||
assert(it != TypeServerSource::instances.end());
|
||||
|
||||
std::pair<std::string, TypeServerSource *> &pdb = it->second;
|
||||
|
||||
if (!pdb.second)
|
||||
return createFileError(
|
||||
*p, createStringError(inconvertibleErrorCode(), pdb.first.c_str()));
|
||||
|
||||
pdb::PDBFile &pdbFile = (pdb.second)->session->getPDBFile();
|
||||
pdb::InfoStream &info = cantFail(pdbFile.getPDBInfoStream());
|
||||
|
||||
// Just because a file with a matching name was found doesn't mean it can be
|
||||
// used. The GUID must match between the PDB header and the OBJ
|
||||
// TypeServer2 record. The 'Age' is used by MSVC incremental compilation.
|
||||
if (info.getGuid() != ts.getGuid())
|
||||
return createFileError(
|
||||
ts.Name,
|
||||
make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date));
|
||||
|
||||
return pdb.second;
|
||||
}
|
||||
|
||||
// FIXME: Temporary interface until PDBLinker::maybeMergeTypeServerPDB() is
|
||||
// moved here.
|
||||
Expected<llvm::pdb::NativeSession *>
|
||||
lld::coff::findTypeServerSource(const ObjFile *f) {
|
||||
Expected<TypeServerSource *> ts = TypeServerSource::findFromFile(f);
|
||||
if (!ts)
|
||||
return ts.takeError();
|
||||
return ts.get()->session.get();
|
||||
}
|
||||
|
||||
// Queue a PDB type server for loading in the COFF Driver
|
||||
void TypeServerSource::enqueue(const ObjFile *dependentFile,
|
||||
const TypeServer2Record &ts) {
|
||||
// Start by finding where the PDB is located (either the record path or next
|
||||
// to the OBJ file)
|
||||
Optional<std::string> p = findPdbPath(ts.Name, dependentFile);
|
||||
if (!p)
|
||||
return;
|
||||
auto it = TypeServerSource::instances.emplace(
|
||||
*p, std::pair<std::string, TypeServerSource *>{});
|
||||
if (!it.second)
|
||||
return; // another OBJ already scheduled this PDB for load
|
||||
|
||||
driver->enqueuePath(*p, false);
|
||||
}
|
||||
|
||||
// Create an instance of TypeServerSource or an error string if the PDB couldn't
|
||||
// be loaded. The error message will be displayed later, when the referring OBJ
|
||||
// will be merged in. NOTE - a PDB load failure is not a link error: some
|
||||
// debug info will simply be missing from the final PDB - that is the default
|
||||
// accepted behavior.
|
||||
void lld::coff::loadTypeServerSource(llvm::MemoryBufferRef m) {
|
||||
std::string path = normalizePdbPath(m.getBufferIdentifier());
|
||||
|
||||
Expected<TypeServerSource *> ts = TypeServerSource::getInstance(m);
|
||||
if (!ts)
|
||||
TypeServerSource::instances[path] = {toString(ts.takeError()), nullptr};
|
||||
else
|
||||
TypeServerSource::instances[path] = {{}, *ts};
|
||||
}
|
||||
|
||||
Expected<TypeServerSource *> TypeServerSource::getInstance(MemoryBufferRef m) {
|
||||
std::unique_ptr<llvm::pdb::IPDBSession> iSession;
|
||||
Error err = pdb::NativeSession::createFromPdb(
|
||||
MemoryBuffer::getMemBuffer(m, false), iSession);
|
||||
if (err)
|
||||
return std::move(err);
|
||||
|
||||
std::unique_ptr<llvm::pdb::NativeSession> session(
|
||||
static_cast<pdb::NativeSession *>(iSession.release()));
|
||||
|
||||
pdb::PDBFile &pdbFile = session->getPDBFile();
|
||||
Expected<pdb::InfoStream &> info = pdbFile.getPDBInfoStream();
|
||||
// All PDB Files should have an Info stream.
|
||||
if (!info)
|
||||
return info.takeError();
|
||||
return new TypeServerSource(m, session.release());
|
||||
}
|
||||
60
deps/lld/COFF/DebugTypes.h
vendored
60
deps/lld/COFF/DebugTypes.h
vendored
@ -1,60 +0,0 @@
|
||||
//===- DebugTypes.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_DEBUGTYPES_H
|
||||
#define LLD_COFF_DEBUGTYPES_H
|
||||
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace codeview {
|
||||
class PrecompRecord;
|
||||
class TypeServer2Record;
|
||||
} // namespace codeview
|
||||
namespace pdb {
|
||||
class NativeSession;
|
||||
}
|
||||
} // namespace llvm
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
class ObjFile;
|
||||
|
||||
class TpiSource {
|
||||
public:
|
||||
enum TpiKind { Regular, PCH, UsingPCH, PDB, UsingPDB };
|
||||
|
||||
TpiSource(TpiKind k, const ObjFile *f);
|
||||
virtual ~TpiSource() {}
|
||||
|
||||
const TpiKind kind;
|
||||
const ObjFile *file;
|
||||
};
|
||||
|
||||
TpiSource *makeTpiSource(const ObjFile *f);
|
||||
TpiSource *makeUseTypeServerSource(const ObjFile *f,
|
||||
const llvm::codeview::TypeServer2Record *ts);
|
||||
TpiSource *makePrecompSource(const ObjFile *f);
|
||||
TpiSource *makeUsePrecompSource(const ObjFile *f,
|
||||
const llvm::codeview::PrecompRecord *precomp);
|
||||
|
||||
void loadTypeServerSource(llvm::MemoryBufferRef m);
|
||||
|
||||
// Temporary interface to get the dependency
|
||||
template <typename T> const T &retrieveDependencyInfo(const TpiSource *source);
|
||||
|
||||
// Temporary interface until we move PDBLinker::maybeMergeTypeServerPDB here
|
||||
llvm::Expected<llvm::pdb::NativeSession *>
|
||||
findTypeServerSource(const ObjFile *f);
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
1917
deps/lld/COFF/Driver.cpp
vendored
1917
deps/lld/COFF/Driver.cpp
vendored
File diff suppressed because it is too large
Load Diff
202
deps/lld/COFF/Driver.h
vendored
202
deps/lld/COFF/Driver.h
vendored
@ -1,202 +0,0 @@
|
||||
//===- Driver.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_DRIVER_H
|
||||
#define LLD_COFF_DRIVER_H
|
||||
|
||||
#include "Config.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/Object/Archive.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include "llvm/Option/Arg.h"
|
||||
#include "llvm/Option/ArgList.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/TarWriter.h"
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
class LinkerDriver;
|
||||
extern LinkerDriver *driver;
|
||||
|
||||
using llvm::COFF::MachineTypes;
|
||||
using llvm::COFF::WindowsSubsystem;
|
||||
using llvm::Optional;
|
||||
|
||||
class COFFOptTable : public llvm::opt::OptTable {
|
||||
public:
|
||||
COFFOptTable();
|
||||
};
|
||||
|
||||
class ArgParser {
|
||||
public:
|
||||
// Concatenate LINK environment variable and given arguments and parse them.
|
||||
llvm::opt::InputArgList parseLINK(std::vector<const char *> args);
|
||||
|
||||
// Tokenizes a given string and then parses as command line options.
|
||||
llvm::opt::InputArgList parse(StringRef s) { return parse(tokenize(s)); }
|
||||
|
||||
// Tokenizes a given string and then parses as command line options in
|
||||
// .drectve section. /EXPORT options are returned in second element
|
||||
// to be processed in fastpath.
|
||||
std::pair<llvm::opt::InputArgList, std::vector<StringRef>>
|
||||
parseDirectives(StringRef s);
|
||||
|
||||
private:
|
||||
// Parses command line options.
|
||||
llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> args);
|
||||
|
||||
std::vector<const char *> tokenize(StringRef s);
|
||||
|
||||
COFFOptTable table;
|
||||
};
|
||||
|
||||
class LinkerDriver {
|
||||
public:
|
||||
void link(llvm::ArrayRef<const char *> args);
|
||||
|
||||
// Used by the resolver to parse .drectve section contents.
|
||||
void parseDirectives(InputFile *file);
|
||||
|
||||
// Used by ArchiveFile to enqueue members.
|
||||
void enqueueArchiveMember(const Archive::Child &c, const Archive::Symbol &sym,
|
||||
StringRef parentName);
|
||||
|
||||
MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb);
|
||||
|
||||
void enqueuePath(StringRef path, bool wholeArchive);
|
||||
|
||||
private:
|
||||
std::unique_ptr<llvm::TarWriter> tar; // for /linkrepro
|
||||
|
||||
// Opens a file. Path has to be resolved already.
|
||||
MemoryBufferRef openFile(StringRef path);
|
||||
|
||||
// Searches a file from search paths.
|
||||
Optional<StringRef> findFile(StringRef filename);
|
||||
Optional<StringRef> findLib(StringRef filename);
|
||||
StringRef doFindFile(StringRef filename);
|
||||
StringRef doFindLib(StringRef filename);
|
||||
StringRef doFindLibMinGW(StringRef filename);
|
||||
|
||||
// Parses LIB environment which contains a list of search paths.
|
||||
void addLibSearchPaths();
|
||||
|
||||
// Library search path. The first element is always "" (current directory).
|
||||
std::vector<StringRef> searchPaths;
|
||||
|
||||
void maybeExportMinGWSymbols(const llvm::opt::InputArgList &args);
|
||||
|
||||
// We don't want to add the same file more than once.
|
||||
// Files are uniquified by their filesystem and file number.
|
||||
std::set<llvm::sys::fs::UniqueID> visitedFiles;
|
||||
|
||||
std::set<std::string> visitedLibs;
|
||||
|
||||
Symbol *addUndefined(StringRef sym);
|
||||
|
||||
StringRef mangleMaybe(Symbol *s);
|
||||
|
||||
// Windows specific -- "main" is not the only main function in Windows.
|
||||
// You can choose one from these four -- {w,}{WinMain,main}.
|
||||
// There are four different entry point functions for them,
|
||||
// {w,}{WinMain,main}CRTStartup, respectively. The linker needs to
|
||||
// choose the right one depending on which "main" function is defined.
|
||||
// This function looks up the symbol table and resolve corresponding
|
||||
// entry point name.
|
||||
StringRef findDefaultEntry();
|
||||
WindowsSubsystem inferSubsystem();
|
||||
|
||||
void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive);
|
||||
void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName,
|
||||
StringRef parentName, uint64_t offsetInArchive);
|
||||
|
||||
void enqueueTask(std::function<void()> task);
|
||||
bool run();
|
||||
|
||||
std::list<std::function<void()>> taskQueue;
|
||||
std::vector<StringRef> filePaths;
|
||||
std::vector<MemoryBufferRef> resources;
|
||||
|
||||
llvm::StringSet<> directivesExports;
|
||||
};
|
||||
|
||||
// Functions below this line are defined in DriverUtils.cpp.
|
||||
|
||||
void printHelp(const char *argv0);
|
||||
|
||||
// Parses a string in the form of "<integer>[,<integer>]".
|
||||
void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size = nullptr);
|
||||
|
||||
void parseGuard(StringRef arg);
|
||||
|
||||
// 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);
|
||||
|
||||
// Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]".
|
||||
void parseSubsystem(StringRef arg, WindowsSubsystem *sys, 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 "[:<integer>]"
|
||||
void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine);
|
||||
|
||||
// Parses a string in the form of "EMBED[,=<integer>]|NO".
|
||||
void parseManifest(StringRef arg);
|
||||
|
||||
// Parses a string in the form of "level=<string>|uiAccess=<string>"
|
||||
void parseManifestUAC(StringRef arg);
|
||||
|
||||
// Parses a string in the form of "cd|net[,(cd|net)]*"
|
||||
void parseSwaprun(StringRef arg);
|
||||
|
||||
// Create a resource file containing a manifest XML.
|
||||
std::unique_ptr<MemoryBuffer> createManifestRes();
|
||||
void createSideBySideManifest();
|
||||
|
||||
// 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);
|
||||
|
||||
void runMSVCLinker(std::string rsp, ArrayRef<StringRef> objects);
|
||||
|
||||
// Create enum with OPT_xxx values for each option in Options.td
|
||||
enum {
|
||||
OPT_INVALID = 0,
|
||||
#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID,
|
||||
#include "Options.inc"
|
||||
#undef OPTION
|
||||
};
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
897
deps/lld/COFF/DriverUtils.cpp
vendored
897
deps/lld/COFF/DriverUtils.cpp
vendored
@ -1,897 +0,0 @@
|
||||
//===- DriverUtils.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 file contains utility functions for the driver. Because there
|
||||
// are so many small functions, we created this separate file to make
|
||||
// Driver.cpp less cluttered.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Config.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/StringSwitch.h"
|
||||
#include "llvm/BinaryFormat/COFF.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include "llvm/Object/WindowsResource.h"
|
||||
#include "llvm/Option/Arg.h"
|
||||
#include "llvm/Option/ArgList.h"
|
||||
#include "llvm/Option/Option.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/FileUtilities.h"
|
||||
#include "llvm/Support/MathExtras.h"
|
||||
#include "llvm/Support/Process.h"
|
||||
#include "llvm/Support/Program.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/WindowsManifest/WindowsManifestMerger.h"
|
||||
#include <memory>
|
||||
|
||||
using namespace llvm::COFF;
|
||||
using namespace llvm;
|
||||
using llvm::sys::Process;
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
namespace {
|
||||
|
||||
const uint16_t SUBLANG_ENGLISH_US = 0x0409;
|
||||
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)); }
|
||||
|
||||
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);
|
||||
args.insert(args.begin(), exe);
|
||||
|
||||
if (sys::ExecuteAndWait(args[0], args) != 0)
|
||||
fatal("ExecuteAndWait failed: " +
|
||||
llvm::join(args.begin(), args.end(), " "));
|
||||
}
|
||||
|
||||
private:
|
||||
StringRef prog;
|
||||
std::vector<StringRef> args;
|
||||
};
|
||||
|
||||
} // 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(',');
|
||||
if (s1.getAsInteger(0, *addr))
|
||||
fatal("invalid number: " + s1);
|
||||
if (size && !s2.empty() && s2.getAsInteger(0, *size))
|
||||
fatal("invalid number: " + s2);
|
||||
}
|
||||
|
||||
// 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('.');
|
||||
if (s1.getAsInteger(0, *major))
|
||||
fatal("invalid number: " + s1);
|
||||
*minor = 0;
|
||||
if (!s2.empty() && s2.getAsInteger(0, *minor))
|
||||
fatal("invalid number: " + s2);
|
||||
}
|
||||
|
||||
void parseGuard(StringRef fullArg) {
|
||||
SmallVector<StringRef, 1> splitArgs;
|
||||
fullArg.split(splitArgs, ",");
|
||||
for (StringRef arg : splitArgs) {
|
||||
if (arg.equals_lower("no"))
|
||||
config->guardCF = GuardCFLevel::Off;
|
||||
else if (arg.equals_lower("nolongjmp"))
|
||||
config->guardCF = GuardCFLevel::NoLongJmp;
|
||||
else if (arg.equals_lower("cf") || arg.equals_lower("longjmp"))
|
||||
config->guardCF = GuardCFLevel::Full;
|
||||
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) {
|
||||
StringRef sysStr, ver;
|
||||
std::tie(sysStr, ver) = arg.split(',');
|
||||
std::string sysStrLower = sysStr.lower();
|
||||
*sys = StringSwitch<WindowsSubsystem>(sysStrLower)
|
||||
.Case("boot_application", IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION)
|
||||
.Case("console", IMAGE_SUBSYSTEM_WINDOWS_CUI)
|
||||
.Case("default", IMAGE_SUBSYSTEM_UNKNOWN)
|
||||
.Case("efi_application", IMAGE_SUBSYSTEM_EFI_APPLICATION)
|
||||
.Case("efi_boot_service_driver", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER)
|
||||
.Case("efi_rom", IMAGE_SUBSYSTEM_EFI_ROM)
|
||||
.Case("efi_runtime_driver", IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER)
|
||||
.Case("native", IMAGE_SUBSYSTEM_NATIVE)
|
||||
.Case("posix", IMAGE_SUBSYSTEM_POSIX_CUI)
|
||||
.Case("windows", IMAGE_SUBSYSTEM_WINDOWS_GUI)
|
||||
.Default(IMAGE_SUBSYSTEM_UNKNOWN);
|
||||
if (*sys == IMAGE_SUBSYSTEM_UNKNOWN && sysStrLower != "default")
|
||||
fatal("unknown subsystem: " + sysStr);
|
||||
if (!ver.empty())
|
||||
parseVersion(ver, major, minor);
|
||||
}
|
||||
|
||||
// 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('=');
|
||||
if (from.empty() || to.empty())
|
||||
fatal("/alternatename: invalid argument: " + s);
|
||||
auto it = config->alternateNames.find(from);
|
||||
if (it != config->alternateNames.end() && it->second != to)
|
||||
fatal("/alternatename: conflicts: " + s);
|
||||
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('=');
|
||||
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));
|
||||
bool inserted = pair.second;
|
||||
if (!inserted) {
|
||||
StringRef existing = pair.first->second;
|
||||
if (existing != to)
|
||||
warn(s + ": already merged into " + existing);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t parseSectionAttributes(StringRef s) {
|
||||
uint32_t ret = 0;
|
||||
for (char c : s.lower()) {
|
||||
switch (c) {
|
||||
case 'd':
|
||||
ret |= IMAGE_SCN_MEM_DISCARDABLE;
|
||||
break;
|
||||
case 'e':
|
||||
ret |= IMAGE_SCN_MEM_EXECUTE;
|
||||
break;
|
||||
case 'k':
|
||||
ret |= IMAGE_SCN_MEM_NOT_CACHED;
|
||||
break;
|
||||
case 'p':
|
||||
ret |= IMAGE_SCN_MEM_NOT_PAGED;
|
||||
break;
|
||||
case 'r':
|
||||
ret |= IMAGE_SCN_MEM_READ;
|
||||
break;
|
||||
case 's':
|
||||
ret |= IMAGE_SCN_MEM_SHARED;
|
||||
break;
|
||||
case 'w':
|
||||
ret |= IMAGE_SCN_MEM_WRITE;
|
||||
break;
|
||||
default:
|
||||
fatal("/section: invalid argument: " + s);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Parses /section option argument.
|
||||
void parseSection(StringRef s) {
|
||||
StringRef name, attrs;
|
||||
std::tie(name, attrs) = s.split(',');
|
||||
if (name.empty() || attrs.empty())
|
||||
fatal("/section: invalid argument: " + s);
|
||||
config->section[name] = parseSectionAttributes(attrs);
|
||||
}
|
||||
|
||||
// Parses /aligncomm option argument.
|
||||
void parseAligncomm(StringRef s) {
|
||||
StringRef name, align;
|
||||
std::tie(name, align) = s.split(',');
|
||||
if (name.empty() || align.empty()) {
|
||||
error("/aligncomm: invalid argument: " + s);
|
||||
return;
|
||||
}
|
||||
int v;
|
||||
if (align.getAsInteger(0, v)) {
|
||||
error("/aligncomm: invalid argument: " + s);
|
||||
return;
|
||||
}
|
||||
config->alignComm[name] = std::max(config->alignComm[name], 1 << v);
|
||||
}
|
||||
|
||||
// Parses /functionpadmin option argument.
|
||||
void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine) {
|
||||
StringRef arg = a->getNumValues() ? a->getValue() : "";
|
||||
if (!arg.empty()) {
|
||||
// Optional padding in bytes is given.
|
||||
if (arg.getAsInteger(0, 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;
|
||||
} 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) {
|
||||
if (arg.equals_lower("no")) {
|
||||
config->manifest = Configuration::No;
|
||||
return;
|
||||
}
|
||||
if (!arg.startswith_lower("embed"))
|
||||
fatal("invalid option " + arg);
|
||||
config->manifest = Configuration::Embed;
|
||||
arg = arg.substr(strlen("embed"));
|
||||
if (arg.empty())
|
||||
return;
|
||||
if (!arg.startswith_lower(",id="))
|
||||
fatal("invalid option " + arg);
|
||||
arg = arg.substr(strlen(",id="));
|
||||
if (arg.getAsInteger(0, 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) {
|
||||
if (arg.equals_lower("no")) {
|
||||
config->manifestUAC = false;
|
||||
return;
|
||||
}
|
||||
for (;;) {
|
||||
arg = arg.ltrim();
|
||||
if (arg.empty())
|
||||
return;
|
||||
if (arg.startswith_lower("level=")) {
|
||||
arg = arg.substr(strlen("level="));
|
||||
std::tie(config->manifestLevel, arg) = arg.split(" ");
|
||||
continue;
|
||||
}
|
||||
if (arg.startswith_lower("uiaccess=")) {
|
||||
arg = arg.substr(strlen("uiaccess="));
|
||||
std::tie(config->manifestUIAccess, arg) = arg.split(" ");
|
||||
continue;
|
||||
}
|
||||
fatal("invalid option " + arg);
|
||||
}
|
||||
}
|
||||
|
||||
// Parses a string in the form of "cd|net[,(cd|net)]*"
|
||||
// Results are directly written to Config.
|
||||
void parseSwaprun(StringRef arg) {
|
||||
do {
|
||||
StringRef swaprun, newArg;
|
||||
std::tie(swaprun, newArg) = arg.split(',');
|
||||
if (swaprun.equals_lower("cd"))
|
||||
config->swaprunCD = true;
|
||||
else if (swaprun.equals_lower("net"))
|
||||
config->swaprunNet = true;
|
||||
else if (swaprun.empty())
|
||||
error("/swaprun: missing argument");
|
||||
else
|
||||
error("/swaprun: invalid argument: " + swaprun);
|
||||
// To catch trailing commas, e.g. `/spawrun:cd,`
|
||||
if (newArg.empty() && arg.endswith(","))
|
||||
error("/swaprun: missing argument");
|
||||
arg = newArg;
|
||||
} while (!arg.empty());
|
||||
}
|
||||
|
||||
// An RAII temporary file class that automatically removes a temporary file.
|
||||
namespace {
|
||||
class TemporaryFile {
|
||||
public:
|
||||
TemporaryFile(StringRef prefix, StringRef extn, StringRef contents = "") {
|
||||
SmallString<128> s;
|
||||
if (auto ec = sys::fs::createTemporaryFile("lld-" + prefix, extn, s))
|
||||
fatal("cannot create a temporary file: " + ec.message());
|
||||
path = s.str();
|
||||
|
||||
if (!contents.empty()) {
|
||||
std::error_code ec;
|
||||
raw_fd_ostream os(path, ec, sys::fs::F_None);
|
||||
if (ec)
|
||||
fatal("failed to open " + path + ": " + ec.message());
|
||||
os << contents;
|
||||
}
|
||||
}
|
||||
|
||||
TemporaryFile(TemporaryFile &&obj) {
|
||||
std::swap(path, obj.path);
|
||||
}
|
||||
|
||||
~TemporaryFile() {
|
||||
if (path.empty())
|
||||
return;
|
||||
if (sys::fs::remove(path))
|
||||
fatal("failed to remove " + path);
|
||||
}
|
||||
|
||||
// Returns a memory buffer of this temporary file.
|
||||
// Note that this function does not leave the file open,
|
||||
// so it is safe to remove the file immediately after this function
|
||||
// is called (you cannot remove an opened file on Windows.)
|
||||
std::unique_ptr<MemoryBuffer> getMemoryBuffer() {
|
||||
// IsVolatile=true forces MemoryBuffer to not use mmap().
|
||||
return CHECK(MemoryBuffer::getFile(path, /*FileSize=*/-1,
|
||||
/*RequiresNullTerminator=*/false,
|
||||
/*IsVolatile=*/true),
|
||||
"could not open " + path);
|
||||
}
|
||||
|
||||
std::string path;
|
||||
};
|
||||
}
|
||||
|
||||
static std::string createDefaultXml() {
|
||||
std::string ret;
|
||||
raw_string_ostream os(ret);
|
||||
|
||||
// Emit the XML. Note that we do *not* verify that the XML attributes are
|
||||
// syntactically correct. This is intentional for link.exe compatibility.
|
||||
os << "<?xml version=\"1.0\" standalone=\"yes\"?>\n"
|
||||
<< "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\"\n"
|
||||
<< " manifestVersion=\"1.0\">\n";
|
||||
if (config->manifestUAC) {
|
||||
os << " <trustInfo>\n"
|
||||
<< " <security>\n"
|
||||
<< " <requestedPrivileges>\n"
|
||||
<< " <requestedExecutionLevel level=" << config->manifestLevel
|
||||
<< " uiAccess=" << config->manifestUIAccess << "/>\n"
|
||||
<< " </requestedPrivileges>\n"
|
||||
<< " </security>\n"
|
||||
<< " </trustInfo>\n";
|
||||
}
|
||||
if (!config->manifestDependency.empty()) {
|
||||
os << " <dependency>\n"
|
||||
<< " <dependentAssembly>\n"
|
||||
<< " <assemblyIdentity " << config->manifestDependency << " />\n"
|
||||
<< " </dependentAssembly>\n"
|
||||
<< " </dependency>\n";
|
||||
}
|
||||
os << "</assembly>\n";
|
||||
return os.str();
|
||||
}
|
||||
|
||||
static std::string createManifestXmlWithInternalMt(StringRef defaultXml) {
|
||||
std::unique_ptr<MemoryBuffer> defaultXmlCopy =
|
||||
MemoryBuffer::getMemBufferCopy(defaultXml);
|
||||
|
||||
windows_manifest::WindowsManifestMerger merger;
|
||||
if (auto e = merger.merge(*defaultXmlCopy.get()))
|
||||
fatal("internal manifest tool failed on default xml: " +
|
||||
toString(std::move(e)));
|
||||
|
||||
for (StringRef filename : config->manifestInput) {
|
||||
std::unique_ptr<MemoryBuffer> manifest =
|
||||
check(MemoryBuffer::getFile(filename));
|
||||
if (auto e = merger.merge(*manifest.get()))
|
||||
fatal("internal manifest tool failed on file " + filename + ": " +
|
||||
toString(std::move(e)));
|
||||
}
|
||||
|
||||
return merger.getMergedManifest().get()->getBuffer();
|
||||
}
|
||||
|
||||
static std::string createManifestXmlWithExternalMt(StringRef defaultXml) {
|
||||
// Create the default manifest file as a temporary file.
|
||||
TemporaryFile Default("defaultxml", "manifest");
|
||||
std::error_code ec;
|
||||
raw_fd_ostream os(Default.path, ec, sys::fs::F_Text);
|
||||
if (ec)
|
||||
fatal("failed to open " + Default.path + ": " + ec.message());
|
||||
os << defaultXml;
|
||||
os.close();
|
||||
|
||||
// Merge user-supplied manifests if they are given. Since libxml2 is not
|
||||
// enabled, we must shell out to Microsoft's mt.exe tool.
|
||||
TemporaryFile user("user", "manifest");
|
||||
|
||||
Executor e("mt.exe");
|
||||
e.add("/manifest");
|
||||
e.add(Default.path);
|
||||
for (StringRef filename : config->manifestInput) {
|
||||
e.add("/manifest");
|
||||
e.add(filename);
|
||||
}
|
||||
e.add("/nologo");
|
||||
e.add("/out:" + StringRef(user.path));
|
||||
e.run();
|
||||
|
||||
return CHECK(MemoryBuffer::getFile(user.path), "could not open " + user.path)
|
||||
.get()
|
||||
->getBuffer();
|
||||
}
|
||||
|
||||
static std::string createManifestXml() {
|
||||
std::string defaultXml = createDefaultXml();
|
||||
if (config->manifestInput.empty())
|
||||
return defaultXml;
|
||||
|
||||
if (windows_manifest::isAvailable())
|
||||
return createManifestXmlWithInternalMt(defaultXml);
|
||||
|
||||
return createManifestXmlWithExternalMt(defaultXml);
|
||||
}
|
||||
|
||||
static std::unique_ptr<WritableMemoryBuffer>
|
||||
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 +
|
||||
".manifest.res");
|
||||
}
|
||||
|
||||
static void writeResFileHeader(char *&buf) {
|
||||
memcpy(buf, COFF::WinResMagic, sizeof(COFF::WinResMagic));
|
||||
buf += sizeof(COFF::WinResMagic);
|
||||
memset(buf, 0, object::WIN_RES_NULL_ENTRY_SIZE);
|
||||
buf += object::WIN_RES_NULL_ENTRY_SIZE;
|
||||
}
|
||||
|
||||
static void writeResEntryHeader(char *&buf, size_t manifestSize) {
|
||||
// Write the prefix.
|
||||
auto *prefix = reinterpret_cast<object::WinResHeaderPrefix *>(buf);
|
||||
prefix->DataSize = manifestSize;
|
||||
prefix->HeaderSize = sizeof(object::WinResHeaderPrefix) +
|
||||
sizeof(object::WinResIDs) +
|
||||
sizeof(object::WinResHeaderSuffix);
|
||||
buf += sizeof(object::WinResHeaderPrefix);
|
||||
|
||||
// Write the Type/Name IDs.
|
||||
auto *iDs = reinterpret_cast<object::WinResIDs *>(buf);
|
||||
iDs->setType(RT_MANIFEST);
|
||||
iDs->setName(config->manifestID);
|
||||
buf += sizeof(object::WinResIDs);
|
||||
|
||||
// Write the suffix.
|
||||
auto *suffix = reinterpret_cast<object::WinResHeaderSuffix *>(buf);
|
||||
suffix->DataVersion = 0;
|
||||
suffix->MemoryFlags = object::WIN_RES_PURE_MOVEABLE;
|
||||
suffix->Language = SUBLANG_ENGLISH_US;
|
||||
suffix->Version = 0;
|
||||
suffix->Characteristics = 0;
|
||||
buf += sizeof(object::WinResHeaderSuffix);
|
||||
}
|
||||
|
||||
// Create a resource file containing a manifest XML.
|
||||
std::unique_ptr<MemoryBuffer> createManifestRes() {
|
||||
std::string manifest = createManifestXml();
|
||||
|
||||
std::unique_ptr<WritableMemoryBuffer> res =
|
||||
createMemoryBufferForManifestRes(manifest.size());
|
||||
|
||||
char *buf = res->getBufferStart();
|
||||
writeResFileHeader(buf);
|
||||
writeResEntryHeader(buf, manifest.size());
|
||||
|
||||
// Copy the manifest data into the .res file.
|
||||
std::copy(manifest.begin(), manifest.end(), buf);
|
||||
return std::move(res);
|
||||
}
|
||||
|
||||
void createSideBySideManifest() {
|
||||
std::string path = config->manifestFile;
|
||||
if (path == "")
|
||||
path = config->outputFile + ".manifest";
|
||||
std::error_code ec;
|
||||
raw_fd_ostream out(path, ec, sys::fs::F_Text);
|
||||
if (ec)
|
||||
fatal("failed to create manifest: " + ec.message());
|
||||
out << createManifestXml();
|
||||
}
|
||||
|
||||
// Parse a string in the form of
|
||||
// "<name>[=<internalname>][,@ordinal[,NONAME]][,DATA][,PRIVATE]"
|
||||
// or "<name>=<dllname>.<name>".
|
||||
// Used for parsing /export arguments.
|
||||
Export parseExport(StringRef arg) {
|
||||
Export e;
|
||||
StringRef rest;
|
||||
std::tie(e.name, rest) = arg.split(",");
|
||||
if (e.name.empty())
|
||||
goto err;
|
||||
|
||||
if (e.name.contains('=')) {
|
||||
StringRef x, y;
|
||||
std::tie(x, y) = e.name.split("=");
|
||||
|
||||
// If "<name>=<dllname>.<name>".
|
||||
if (y.contains(".")) {
|
||||
e.name = x;
|
||||
e.forwardTo = y;
|
||||
return e;
|
||||
}
|
||||
|
||||
e.extName = x;
|
||||
e.name = y;
|
||||
if (e.name.empty())
|
||||
goto err;
|
||||
}
|
||||
|
||||
// If "<name>=<internalname>[,@ordinal[,NONAME]][,DATA][,PRIVATE]"
|
||||
while (!rest.empty()) {
|
||||
StringRef tok;
|
||||
std::tie(tok, rest) = rest.split(",");
|
||||
if (tok.equals_lower("noname")) {
|
||||
if (e.ordinal == 0)
|
||||
goto err;
|
||||
e.noname = true;
|
||||
continue;
|
||||
}
|
||||
if (tok.equals_lower("data")) {
|
||||
e.data = true;
|
||||
continue;
|
||||
}
|
||||
if (tok.equals_lower("constant")) {
|
||||
e.constant = true;
|
||||
continue;
|
||||
}
|
||||
if (tok.equals_lower("private")) {
|
||||
e.isPrivate = true;
|
||||
continue;
|
||||
}
|
||||
if (tok.startswith("@")) {
|
||||
int32_t ord;
|
||||
if (tok.substr(1).getAsInteger(0, ord))
|
||||
goto err;
|
||||
if (ord <= 0 || 65535 < ord)
|
||||
goto err;
|
||||
e.ordinal = ord;
|
||||
continue;
|
||||
}
|
||||
goto err;
|
||||
}
|
||||
return e;
|
||||
|
||||
err:
|
||||
fatal("invalid /export: " + arg);
|
||||
}
|
||||
|
||||
static StringRef undecorate(StringRef sym) {
|
||||
if (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)
|
||||
return sym;
|
||||
return sym.startswith("_") ? sym.substr(1) : sym;
|
||||
}
|
||||
|
||||
// Convert stdcall/fastcall style symbols into unsuffixed symbols,
|
||||
// with or without a leading underscore. (MinGW specific.)
|
||||
static StringRef killAt(StringRef sym, bool prefix) {
|
||||
if (sym.empty())
|
||||
return sym;
|
||||
// Strip any trailing stdcall suffix
|
||||
sym = sym.substr(0, sym.find('@', 1));
|
||||
if (!sym.startswith("@")) {
|
||||
if (prefix && !sym.startswith("_"))
|
||||
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);
|
||||
return sym;
|
||||
}
|
||||
|
||||
// Performs error checking on all /export arguments.
|
||||
// It also sets ordinals.
|
||||
void fixupExports() {
|
||||
// Symbol ordinals must be unique.
|
||||
std::set<uint16_t> ords;
|
||||
for (Export &e : config->exports) {
|
||||
if (e.ordinal == 0)
|
||||
continue;
|
||||
if (!ords.insert(e.ordinal).second)
|
||||
fatal("duplicate export ordinal: " + e.name);
|
||||
}
|
||||
|
||||
for (Export &e : config->exports) {
|
||||
if (!e.forwardTo.empty()) {
|
||||
e.exportName = undecorate(e.name);
|
||||
} else {
|
||||
e.exportName = undecorate(e.extName.empty() ? e.name : e.extName);
|
||||
}
|
||||
}
|
||||
|
||||
if (config->killAt && config->machine == I386) {
|
||||
for (Export &e : config->exports) {
|
||||
e.name = killAt(e.name, true);
|
||||
e.exportName = killAt(e.exportName, false);
|
||||
e.extName = killAt(e.extName, true);
|
||||
e.symbolName = killAt(e.symbolName, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Uniquefy by name.
|
||||
DenseMap<StringRef, Export *> map(config->exports.size());
|
||||
std::vector<Export> v;
|
||||
for (Export &e : config->exports) {
|
||||
auto pair = map.insert(std::make_pair(e.exportName, &e));
|
||||
bool inserted = pair.second;
|
||||
if (inserted) {
|
||||
v.push_back(e);
|
||||
continue;
|
||||
}
|
||||
Export *existing = pair.first->second;
|
||||
if (e == *existing || e.name != existing->name)
|
||||
continue;
|
||||
warn("duplicate /export option: " + e.name);
|
||||
}
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
void assignExportOrdinals() {
|
||||
// Assign unique ordinals if default (= 0).
|
||||
uint16_t max = 0;
|
||||
for (Export &e : config->exports)
|
||||
max = std::max(max, e.ordinal);
|
||||
for (Export &e : config->exports)
|
||||
if (e.ordinal == 0)
|
||||
e.ordinal = ++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('=');
|
||||
if (k.empty() || v.empty())
|
||||
fatal("/failifmismatch: invalid argument: " + arg);
|
||||
std::pair<StringRef, InputFile *> existing = config->mustMatch[k];
|
||||
if (!existing.first.empty() && v != existing.first) {
|
||||
std::string sourceStr = source ? toString(source) : "cmd-line";
|
||||
std::string existingStr =
|
||||
existing.second ? toString(existing.second) : "cmd-line";
|
||||
fatal("/failifmismatch: mismatch detected for '" + k + "':\n>>> " +
|
||||
existingStr + " has value " + existing.first + "\n>>> " + sourceStr +
|
||||
" has value " + v);
|
||||
}
|
||||
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) {
|
||||
object::WindowsResourceParser parser;
|
||||
|
||||
for (MemoryBufferRef mb : mbs) {
|
||||
std::unique_ptr<object::Binary> bin = check(object::createBinary(mb));
|
||||
object::WindowsResource *rf = dyn_cast<object::WindowsResource>(bin.get());
|
||||
if (!rf)
|
||||
fatal("cannot compile non-resource file as resource");
|
||||
|
||||
std::vector<std::string> duplicates;
|
||||
if (auto ec = parser.parse(rf, duplicates))
|
||||
fatal(toString(std::move(ec)));
|
||||
|
||||
for (const auto &dupeDiag : duplicates)
|
||||
if (config->forceMultipleRes)
|
||||
warn(dupeDiag);
|
||||
else
|
||||
error(dupeDiag);
|
||||
}
|
||||
|
||||
Expected<std::unique_ptr<MemoryBuffer>> e =
|
||||
llvm::object::writeWindowsResourceCOFF(config->machine, parser,
|
||||
config->timestamp);
|
||||
if (!e)
|
||||
fatal("failed to write .res to COFF: " + toString(e.takeError()));
|
||||
|
||||
MemoryBufferRef mbref = **e;
|
||||
make<std::unique_ptr<MemoryBuffer>>(std::move(*e)); // take ownership
|
||||
return mbref;
|
||||
}
|
||||
|
||||
// Create OptTable
|
||||
|
||||
// Create prefix string literals used in Options.td
|
||||
#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
|
||||
#include "Options.inc"
|
||||
#undef PREFIX
|
||||
|
||||
// Create table mapping all options defined in Options.td
|
||||
static const llvm::opt::OptTable::Info infoTable[] = {
|
||||
#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \
|
||||
{X1, X2, X10, X11, OPT_##ID, llvm::opt::Option::KIND##Class, \
|
||||
X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12},
|
||||
#include "Options.inc"
|
||||
#undef OPTION
|
||||
};
|
||||
|
||||
COFFOptTable::COFFOptTable() : OptTable(infoTable, true) {}
|
||||
|
||||
// Set color diagnostics according to --color-diagnostics={auto,always,never}
|
||||
// or --no-color-diagnostics flags.
|
||||
static void handleColorDiagnostics(opt::InputArgList &args) {
|
||||
auto *arg = args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq,
|
||||
OPT_no_color_diagnostics);
|
||||
if (!arg)
|
||||
return;
|
||||
if (arg->getOption().getID() == OPT_color_diagnostics) {
|
||||
errorHandler().colorDiagnostics = true;
|
||||
} else if (arg->getOption().getID() == OPT_no_color_diagnostics) {
|
||||
errorHandler().colorDiagnostics = false;
|
||||
} else {
|
||||
StringRef s = arg->getValue();
|
||||
if (s == "always")
|
||||
errorHandler().colorDiagnostics = true;
|
||||
else if (s == "never")
|
||||
errorHandler().colorDiagnostics = false;
|
||||
else if (s != "auto")
|
||||
error("unknown option: --color-diagnostics=" + s);
|
||||
}
|
||||
}
|
||||
|
||||
static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) {
|
||||
if (auto *arg = args.getLastArg(OPT_rsp_quoting)) {
|
||||
StringRef s = arg->getValue();
|
||||
if (s != "windows" && s != "posix")
|
||||
error("invalid response file quoting: " + s);
|
||||
if (s == "windows")
|
||||
return cl::TokenizeWindowsCommandLine;
|
||||
return cl::TokenizeGNUCommandLine;
|
||||
}
|
||||
// The COFF linker always defaults to Windows quoting.
|
||||
return cl::TokenizeWindowsCommandLine;
|
||||
}
|
||||
|
||||
// Parses a given list of options.
|
||||
opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
|
||||
// Make InputArgList from string vectors.
|
||||
unsigned missingIndex;
|
||||
unsigned missingCount;
|
||||
|
||||
// We need to get the quoting style for response files before parsing all
|
||||
// options so we parse here before and ignore all the options but
|
||||
// --rsp-quoting.
|
||||
opt::InputArgList args = table.ParseArgs(argv, missingIndex, missingCount);
|
||||
|
||||
// Expand response files (arguments in the form of @<filename>)
|
||||
// and then parse the argument again.
|
||||
SmallVector<const char *, 256> expandedArgv(argv.data(),
|
||||
argv.data() + argv.size());
|
||||
cl::ExpandResponseFiles(saver, getQuotingStyle(args), expandedArgv);
|
||||
args = table.ParseArgs(makeArrayRef(expandedArgv).drop_front(), missingIndex,
|
||||
missingCount);
|
||||
|
||||
// Print the real command line if response files are expanded.
|
||||
if (args.hasArg(OPT_verbose) && argv.size() != expandedArgv.size()) {
|
||||
std::string msg = "Command line:";
|
||||
for (const char *s : expandedArgv)
|
||||
msg += " " + std::string(s);
|
||||
message(msg);
|
||||
}
|
||||
|
||||
// Save the command line after response file expansion so we can write it to
|
||||
// the PDB if necessary.
|
||||
config->argv = {expandedArgv.begin(), expandedArgv.end()};
|
||||
|
||||
// Handle /WX early since it converts missing argument warnings to errors.
|
||||
errorHandler().fatalWarnings = args.hasFlag(OPT_WX, OPT_WX_no, false);
|
||||
|
||||
if (missingCount)
|
||||
fatal(Twine(args.getArgString(missingIndex)) + ": missing argument");
|
||||
|
||||
handleColorDiagnostics(args);
|
||||
|
||||
for (auto *arg : args.filtered(OPT_UNKNOWN)) {
|
||||
std::string nearest;
|
||||
if (table.findNearest(arg->getAsString(args), nearest) > 1)
|
||||
warn("ignoring unknown argument '" + arg->getAsString(args) + "'");
|
||||
else
|
||||
warn("ignoring unknown argument '" + arg->getAsString(args) +
|
||||
"', did you mean '" + nearest + "'");
|
||||
}
|
||||
|
||||
if (args.hasArg(OPT_lib))
|
||||
warn("ignoring /lib since it's not the first argument");
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
// Tokenizes and parses a given string as command line in .drective section.
|
||||
// /EXPORT options are processed in fastpath.
|
||||
std::pair<opt::InputArgList, std::vector<StringRef>>
|
||||
ArgParser::parseDirectives(StringRef s) {
|
||||
std::vector<StringRef> exports;
|
||||
SmallVector<const char *, 16> rest;
|
||||
|
||||
for (StringRef tok : tokenize(s)) {
|
||||
if (tok.startswith_lower("/export:") || tok.startswith_lower("-export:"))
|
||||
exports.push_back(tok.substr(strlen("/export:")));
|
||||
else
|
||||
rest.push_back(tok.data());
|
||||
}
|
||||
|
||||
// Make InputArgList from unparsed string vectors.
|
||||
unsigned missingIndex;
|
||||
unsigned missingCount;
|
||||
|
||||
opt::InputArgList args = table.ParseArgs(rest, missingIndex, missingCount);
|
||||
|
||||
if (missingCount)
|
||||
fatal(Twine(args.getArgString(missingIndex)) + ": missing argument");
|
||||
for (auto *arg : args.filtered(OPT_UNKNOWN))
|
||||
warn("ignoring unknown argument: " + arg->getAsString(args));
|
||||
return {std::move(args), std::move(exports)};
|
||||
}
|
||||
|
||||
// link.exe has an interesting feature. If LINK or _LINK_ environment
|
||||
// variables exist, their contents are handled as command line strings.
|
||||
// So you can pass extra arguments using them.
|
||||
opt::InputArgList ArgParser::parseLINK(std::vector<const char *> argv) {
|
||||
// Concatenate LINK env and command line arguments, and then parse them.
|
||||
if (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_")) {
|
||||
std::vector<const char *> v = tokenize(*s);
|
||||
argv.insert(std::next(argv.begin()), v.begin(), v.end());
|
||||
}
|
||||
return parse(argv);
|
||||
}
|
||||
|
||||
std::vector<const char *> ArgParser::tokenize(StringRef s) {
|
||||
SmallVector<const char *, 16> tokens;
|
||||
cl::TokenizeWindowsCommandLine(s, saver, tokens);
|
||||
return std::vector<const char *>(tokens.begin(), tokens.end());
|
||||
}
|
||||
|
||||
void printHelp(const char *argv0) {
|
||||
COFFOptTable().PrintHelp(outs(),
|
||||
(std::string(argv0) + " [options] file...").c_str(),
|
||||
"LLVM Linker", false);
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
317
deps/lld/COFF/ICF.cpp
vendored
317
deps/lld/COFF/ICF.cpp
vendored
@ -1,317 +0,0 @@
|
||||
//===- ICF.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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// ICF is short for Identical Code Folding. That is a size optimization to
|
||||
// identify and merge two or more read-only sections (typically functions)
|
||||
// that happened to have the same contents. It usually reduces output size
|
||||
// by a few percent.
|
||||
//
|
||||
// On Windows, ICF is enabled by default.
|
||||
//
|
||||
// See ELF/ICF.cpp for the details about the algortihm.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ICF.h"
|
||||
#include "Chunks.h"
|
||||
#include "Symbols.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/Threads.h"
|
||||
#include "lld/Common/Timer.h"
|
||||
#include "llvm/ADT/Hashing.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/Parallel.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/Support/xxhash.h"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
static Timer icfTimer("ICF", Timer::root());
|
||||
|
||||
class ICF {
|
||||
public:
|
||||
void run(ArrayRef<Chunk *> v);
|
||||
|
||||
private:
|
||||
void segregate(size_t begin, size_t end, bool constant);
|
||||
|
||||
bool assocEquals(const SectionChunk *a, const SectionChunk *b);
|
||||
|
||||
bool equalsConstant(const SectionChunk *a, const SectionChunk *b);
|
||||
bool equalsVariable(const SectionChunk *a, const SectionChunk *b);
|
||||
|
||||
bool isEligible(SectionChunk *c);
|
||||
|
||||
size_t findBoundary(size_t begin, size_t end);
|
||||
|
||||
void forEachClassRange(size_t begin, size_t end,
|
||||
std::function<void(size_t, size_t)> fn);
|
||||
|
||||
void forEachClass(std::function<void(size_t, size_t)> fn);
|
||||
|
||||
std::vector<SectionChunk *> chunks;
|
||||
int cnt = 0;
|
||||
std::atomic<bool> repeat = {false};
|
||||
};
|
||||
|
||||
// Returns true if section S is subject of ICF.
|
||||
//
|
||||
// Microsoft's documentation
|
||||
// (https://msdn.microsoft.com/en-us/library/bxwfs976.aspx; visited April
|
||||
// 2017) says that /opt:icf folds both functions and read-only data.
|
||||
// Despite that, the MSVC linker folds only functions. We found
|
||||
// a few instances of programs that are not safe for data merging.
|
||||
// Therefore, we merge only functions just like the MSVC tool. However, we also
|
||||
// merge read-only sections in a couple of cases where the address of the
|
||||
// section is insignificant to the user program and the behaviour matches that
|
||||
// of the Visual C++ linker.
|
||||
bool ICF::isEligible(SectionChunk *c) {
|
||||
// Non-comdat chunks, dead chunks, and writable chunks are not elegible.
|
||||
bool writable = c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
|
||||
if (!c->isCOMDAT() || !c->live || writable)
|
||||
return false;
|
||||
|
||||
// Code sections are eligible.
|
||||
if (c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE)
|
||||
return true;
|
||||
|
||||
// .pdata and .xdata unwind info sections are eligible.
|
||||
StringRef outSecName = c->getSectionName().split('$').first;
|
||||
if (outSecName == ".pdata" || outSecName == ".xdata")
|
||||
return true;
|
||||
|
||||
// So are vtables.
|
||||
if (c->sym && c->sym->getName().startswith("??_7"))
|
||||
return true;
|
||||
|
||||
// Anything else not in an address-significance table is eligible.
|
||||
return !c->keepUnique;
|
||||
}
|
||||
|
||||
// Split an equivalence class into smaller classes.
|
||||
void ICF::segregate(size_t begin, size_t end, bool constant) {
|
||||
while (begin < end) {
|
||||
// Divide [Begin, End) into two. Let Mid be the start index of the
|
||||
// second group.
|
||||
auto bound = std::stable_partition(
|
||||
chunks.begin() + begin + 1, chunks.begin() + end, [&](SectionChunk *s) {
|
||||
if (constant)
|
||||
return equalsConstant(chunks[begin], s);
|
||||
return equalsVariable(chunks[begin], s);
|
||||
});
|
||||
size_t mid = bound - chunks.begin();
|
||||
|
||||
// Split [Begin, End) into [Begin, Mid) and [Mid, End). We use Mid as an
|
||||
// equivalence class ID because every group ends with a unique index.
|
||||
for (size_t i = begin; i < mid; ++i)
|
||||
chunks[i]->eqClass[(cnt + 1) % 2] = mid;
|
||||
|
||||
// If we created a group, we need to iterate the main loop again.
|
||||
if (mid != end)
|
||||
repeat = true;
|
||||
|
||||
begin = mid;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if two sections' associative children are equal.
|
||||
bool ICF::assocEquals(const SectionChunk *a, const SectionChunk *b) {
|
||||
auto childClasses = [&](const SectionChunk *sc) {
|
||||
std::vector<uint32_t> classes;
|
||||
for (const SectionChunk &c : sc->children())
|
||||
if (!c.getSectionName().startswith(".debug") &&
|
||||
c.getSectionName() != ".gfids$y" && c.getSectionName() != ".gljmp$y")
|
||||
classes.push_back(c.eqClass[cnt % 2]);
|
||||
return classes;
|
||||
};
|
||||
return childClasses(a) == childClasses(b);
|
||||
}
|
||||
|
||||
// Compare "non-moving" part of two sections, namely everything
|
||||
// except relocation targets.
|
||||
bool ICF::equalsConstant(const SectionChunk *a, const SectionChunk *b) {
|
||||
if (a->relocsSize != b->relocsSize)
|
||||
return false;
|
||||
|
||||
// Compare relocations.
|
||||
auto eq = [&](const coff_relocation &r1, const coff_relocation &r2) {
|
||||
if (r1.Type != r2.Type ||
|
||||
r1.VirtualAddress != r2.VirtualAddress) {
|
||||
return false;
|
||||
}
|
||||
Symbol *b1 = a->file->getSymbol(r1.SymbolTableIndex);
|
||||
Symbol *b2 = b->file->getSymbol(r2.SymbolTableIndex);
|
||||
if (b1 == b2)
|
||||
return true;
|
||||
if (auto *d1 = dyn_cast<DefinedRegular>(b1))
|
||||
if (auto *d2 = dyn_cast<DefinedRegular>(b2))
|
||||
return d1->getValue() == d2->getValue() &&
|
||||
d1->getChunk()->eqClass[cnt % 2] == d2->getChunk()->eqClass[cnt % 2];
|
||||
return false;
|
||||
};
|
||||
if (!std::equal(a->getRelocs().begin(), a->getRelocs().end(),
|
||||
b->getRelocs().begin(), eq))
|
||||
return false;
|
||||
|
||||
// Compare section attributes and contents.
|
||||
return a->getOutputCharacteristics() == b->getOutputCharacteristics() &&
|
||||
a->getSectionName() == b->getSectionName() &&
|
||||
a->header->SizeOfRawData == b->header->SizeOfRawData &&
|
||||
a->checksum == b->checksum && a->getContents() == b->getContents() &&
|
||||
assocEquals(a, b);
|
||||
}
|
||||
|
||||
// Compare "moving" part of two sections, namely relocation targets.
|
||||
bool ICF::equalsVariable(const SectionChunk *a, const SectionChunk *b) {
|
||||
// Compare relocations.
|
||||
auto eq = [&](const coff_relocation &r1, const coff_relocation &r2) {
|
||||
Symbol *b1 = a->file->getSymbol(r1.SymbolTableIndex);
|
||||
Symbol *b2 = b->file->getSymbol(r2.SymbolTableIndex);
|
||||
if (b1 == b2)
|
||||
return true;
|
||||
if (auto *d1 = dyn_cast<DefinedRegular>(b1))
|
||||
if (auto *d2 = dyn_cast<DefinedRegular>(b2))
|
||||
return d1->getChunk()->eqClass[cnt % 2] == d2->getChunk()->eqClass[cnt % 2];
|
||||
return false;
|
||||
};
|
||||
return std::equal(a->getRelocs().begin(), a->getRelocs().end(),
|
||||
b->getRelocs().begin(), eq) &&
|
||||
assocEquals(a, b);
|
||||
}
|
||||
|
||||
// Find the first Chunk after Begin that has a different class from Begin.
|
||||
size_t ICF::findBoundary(size_t begin, size_t end) {
|
||||
for (size_t i = begin + 1; i < end; ++i)
|
||||
if (chunks[begin]->eqClass[cnt % 2] != chunks[i]->eqClass[cnt % 2])
|
||||
return i;
|
||||
return end;
|
||||
}
|
||||
|
||||
void ICF::forEachClassRange(size_t begin, size_t end,
|
||||
std::function<void(size_t, size_t)> fn) {
|
||||
while (begin < end) {
|
||||
size_t mid = findBoundary(begin, end);
|
||||
fn(begin, mid);
|
||||
begin = mid;
|
||||
}
|
||||
}
|
||||
|
||||
// Call Fn on each class group.
|
||||
void ICF::forEachClass(std::function<void(size_t, size_t)> fn) {
|
||||
// If the number of sections are too small to use threading,
|
||||
// call Fn sequentially.
|
||||
if (chunks.size() < 1024) {
|
||||
forEachClassRange(0, chunks.size(), fn);
|
||||
++cnt;
|
||||
return;
|
||||
}
|
||||
|
||||
// Shard into non-overlapping intervals, and call Fn in parallel.
|
||||
// The sharding must be completed before any calls to Fn are made
|
||||
// so that Fn can modify the Chunks in its shard without causing data
|
||||
// races.
|
||||
const size_t numShards = 256;
|
||||
size_t step = chunks.size() / numShards;
|
||||
size_t boundaries[numShards + 1];
|
||||
boundaries[0] = 0;
|
||||
boundaries[numShards] = chunks.size();
|
||||
parallelForEachN(1, numShards, [&](size_t i) {
|
||||
boundaries[i] = findBoundary((i - 1) * step, chunks.size());
|
||||
});
|
||||
parallelForEachN(1, numShards + 1, [&](size_t i) {
|
||||
if (boundaries[i - 1] < boundaries[i]) {
|
||||
forEachClassRange(boundaries[i - 1], boundaries[i], fn);
|
||||
}
|
||||
});
|
||||
++cnt;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// Collect only mergeable sections and group by hash value.
|
||||
uint32_t nextId = 1;
|
||||
for (Chunk *c : vec) {
|
||||
if (auto *sc = dyn_cast<SectionChunk>(c)) {
|
||||
if (isEligible(sc))
|
||||
chunks.push_back(sc);
|
||||
else
|
||||
sc->eqClass[0] = nextId++;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure that ICF doesn't merge sections that are being handled by string
|
||||
// tail merging.
|
||||
for (MergeChunk *mc : MergeChunk::instances)
|
||||
if (mc)
|
||||
for (SectionChunk *sc : mc->sections)
|
||||
sc->eqClass[0] = nextId++;
|
||||
|
||||
// Initially, we use hash values to partition sections.
|
||||
parallelForEach(chunks, [&](SectionChunk *sc) {
|
||||
sc->eqClass[0] = xxHash64(sc->getContents());
|
||||
});
|
||||
|
||||
// Combine the hashes of the sections referenced by each section into its
|
||||
// hash.
|
||||
for (unsigned cnt = 0; cnt != 2; ++cnt) {
|
||||
parallelForEach(chunks, [&](SectionChunk *sc) {
|
||||
uint32_t hash = sc->eqClass[cnt % 2];
|
||||
for (Symbol *b : sc->symbols())
|
||||
if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))
|
||||
hash += sym->getChunk()->eqClass[cnt % 2];
|
||||
// Set MSB to 1 to avoid collisions with non-hash classs.
|
||||
sc->eqClass[(cnt + 1) % 2] = hash | (1U << 31);
|
||||
});
|
||||
}
|
||||
|
||||
// From now on, sections in Chunks are ordered so that sections in
|
||||
// the same group are consecutive in the vector.
|
||||
llvm::stable_sort(chunks, [](const SectionChunk *a, const SectionChunk *b) {
|
||||
return a->eqClass[0] < b->eqClass[0];
|
||||
});
|
||||
|
||||
// Compare static contents and assign unique IDs for each static content.
|
||||
forEachClass([&](size_t begin, size_t end) { segregate(begin, end, true); });
|
||||
|
||||
// Split groups by comparing relocations until convergence is obtained.
|
||||
do {
|
||||
repeat = false;
|
||||
forEachClass(
|
||||
[&](size_t begin, size_t end) { segregate(begin, end, false); });
|
||||
} while (repeat);
|
||||
|
||||
log("ICF needed " + Twine(cnt) + " iterations");
|
||||
|
||||
// Merge sections in the same classs.
|
||||
forEachClass([&](size_t begin, size_t end) {
|
||||
if (end - begin == 1)
|
||||
return;
|
||||
|
||||
log("Selected " + chunks[begin]->getDebugName());
|
||||
for (size_t i = begin + 1; i < end; ++i) {
|
||||
log(" Removed " + chunks[i]->getDebugName());
|
||||
chunks[begin]->replace(chunks[i]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Entry point to ICF.
|
||||
void doICF(ArrayRef<Chunk *> chunks) { ICF().run(chunks); }
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
25
deps/lld/COFF/ICF.h
vendored
25
deps/lld/COFF/ICF.h
vendored
@ -1,25 +0,0 @@
|
||||
//===- ICF.h --------------------------------------------------------------===//
|
||||
//
|
||||
// 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_ICF_H
|
||||
#define LLD_COFF_ICF_H
|
||||
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
class Chunk;
|
||||
|
||||
void doICF(ArrayRef<Chunk *> chunks);
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
881
deps/lld/COFF/InputFiles.cpp
vendored
881
deps/lld/COFF/InputFiles.cpp
vendored
@ -1,881 +0,0 @@
|
||||
//===- InputFiles.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 "InputFiles.h"
|
||||
#include "Chunks.h"
|
||||
#include "Config.h"
|
||||
#include "DebugTypes.h"
|
||||
#include "Driver.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "llvm-c/lto.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/BinaryFormat/COFF.h"
|
||||
#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
|
||||
#include "llvm/Object/Binary.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/ErrorOr.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Target/TargetOptions.h"
|
||||
#include <cstring>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::COFF;
|
||||
using namespace llvm::codeview;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
|
||||
using llvm::Triple;
|
||||
using llvm::support::ulittle32_t;
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
std::vector<ObjFile *> ObjFile::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,
|
||||
Symbol *source, Symbol *target) {
|
||||
if (auto *u = dyn_cast<Undefined>(source)) {
|
||||
if (u->weakAlias && u->weakAlias != target) {
|
||||
// Weak aliases as produced by GCC are named in the form
|
||||
// .weak.<weaksymbol>.<othersymbol>, where <othersymbol> is the name
|
||||
// of another symbol emitted near the weak symbol.
|
||||
// Just use the definition from the first object file that defined
|
||||
// this weak symbol.
|
||||
if (config->mingw)
|
||||
return;
|
||||
symtab->reportDuplicate(source, f);
|
||||
}
|
||||
u->weakAlias = target;
|
||||
}
|
||||
}
|
||||
|
||||
ArchiveFile::ArchiveFile(MemoryBufferRef m) : InputFile(ArchiveKind, m) {}
|
||||
|
||||
void ArchiveFile::parse() {
|
||||
// Parse a MemoryBufferRef as an archive file.
|
||||
file = CHECK(Archive::create(mb), this);
|
||||
|
||||
// Read the symbol table to construct Lazy objects.
|
||||
for (const Archive::Symbol &sym : file->symbols())
|
||||
symtab->addLazy(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));
|
||||
|
||||
// Return an empty buffer if we have already returned the same buffer.
|
||||
if (!seen.insert(c.getChildOffset()).second)
|
||||
return;
|
||||
|
||||
driver->enqueueArchiveMember(c, sym, getName());
|
||||
}
|
||||
|
||||
std::vector<MemoryBufferRef> getArchiveMembers(Archive *file) {
|
||||
std::vector<MemoryBufferRef> v;
|
||||
Error err = Error::success();
|
||||
for (const ErrorOr<Archive::Child> &cOrErr : file->children(err)) {
|
||||
Archive::Child c =
|
||||
CHECK(cOrErr,
|
||||
file->getFileName() + ": could not get the child of the archive");
|
||||
MemoryBufferRef mbref =
|
||||
CHECK(c.getMemoryBufferRef(),
|
||||
file->getFileName() +
|
||||
": could not get the buffer for a child of the archive");
|
||||
v.push_back(mbref);
|
||||
}
|
||||
if (err)
|
||||
fatal(file->getFileName() +
|
||||
": Archive::children failed: " + toString(std::move(err)));
|
||||
return v;
|
||||
}
|
||||
|
||||
void ObjFile::parse() {
|
||||
// Parse a memory buffer as a COFF file.
|
||||
std::unique_ptr<Binary> bin = CHECK(createBinary(mb), this);
|
||||
|
||||
if (auto *obj = dyn_cast<COFFObjectFile>(bin.get())) {
|
||||
bin.release();
|
||||
coffObj.reset(obj);
|
||||
} else {
|
||||
fatal(toString(this) + " is not a COFF file");
|
||||
}
|
||||
|
||||
// Read section and symbol tables.
|
||||
initializeChunks();
|
||||
initializeSymbols();
|
||||
initializeFlags();
|
||||
initializeDependencies();
|
||||
}
|
||||
|
||||
const coff_section* ObjFile::getSection(uint32_t i) {
|
||||
const coff_section *sec;
|
||||
if (auto ec = coffObj->getSection(i, sec))
|
||||
fatal("getSection failed: #" + Twine(i) + ": " + ec.message());
|
||||
return sec;
|
||||
}
|
||||
|
||||
// We set SectionChunk pointers in the SparseChunks vector to this value
|
||||
// temporarily to mark comdat sections as having an unknown resolution. As we
|
||||
// walk the object file's symbol table, once we visit either a leader symbol or
|
||||
// an associative section definition together with the parent comdat's leader,
|
||||
// we set the pointer to either nullptr (to mark the section as discarded) or a
|
||||
// valid SectionChunk for that section.
|
||||
static SectionChunk *const pendingComdat = reinterpret_cast<SectionChunk *>(1);
|
||||
|
||||
void ObjFile::initializeChunks() {
|
||||
uint32_t numSections = coffObj->getNumberOfSections();
|
||||
chunks.reserve(numSections);
|
||||
sparseChunks.resize(numSections + 1);
|
||||
for (uint32_t i = 1; i < numSections + 1; ++i) {
|
||||
const coff_section *sec = getSection(i);
|
||||
if (sec->Characteristics & IMAGE_SCN_LNK_COMDAT)
|
||||
sparseChunks[i] = pendingComdat;
|
||||
else
|
||||
sparseChunks[i] = readSection(i, nullptr, "");
|
||||
}
|
||||
}
|
||||
|
||||
SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
|
||||
const coff_aux_section_definition *def,
|
||||
StringRef leaderName) {
|
||||
const coff_section *sec = getSection(sectionNumber);
|
||||
|
||||
StringRef name;
|
||||
if (Expected<StringRef> e = coffObj->getSectionName(sec))
|
||||
name = *e;
|
||||
else
|
||||
fatal("getSectionName failed: #" + Twine(sectionNumber) + ": " +
|
||||
toString(e.takeError()));
|
||||
|
||||
if (name == ".drectve") {
|
||||
ArrayRef<uint8_t> data;
|
||||
cantFail(coffObj->getSectionContents(sec, data));
|
||||
directives = StringRef((const char *)data.data(), data.size());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (name == ".llvm_addrsig") {
|
||||
addrsigSec = sec;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Object files may have DWARF debug info or MS CodeView debug info
|
||||
// (or both).
|
||||
//
|
||||
// DWARF sections don't need any special handling from the perspective
|
||||
// of the linker; they are just a data section containing relocations.
|
||||
// We can just link them to complete debug info.
|
||||
//
|
||||
// CodeView needs linker support. We need to interpret debug info,
|
||||
// and then write it to a separate .pdb file.
|
||||
|
||||
// Ignore DWARF debug info unless /debug is given.
|
||||
if (!config->debug && name.startswith(".debug_"))
|
||||
return nullptr;
|
||||
|
||||
if (sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
|
||||
return nullptr;
|
||||
auto *c = make<SectionChunk>(this, sec);
|
||||
if (def)
|
||||
c->checksum = def->CheckSum;
|
||||
|
||||
// link.exe uses the presence of .rsrc$01 for LNK4078, so match that.
|
||||
if (name == ".rsrc$01")
|
||||
isResourceObjFile = true;
|
||||
|
||||
// CodeView sections are stored to a different vector because they are not
|
||||
// linked in the regular manner.
|
||||
if (c->isCodeView())
|
||||
debugChunks.push_back(c);
|
||||
else if (name == ".gfids$y")
|
||||
guardFidChunks.push_back(c);
|
||||
else if (name == ".gljmp$y")
|
||||
guardLJmpChunks.push_back(c);
|
||||
else if (name == ".sxdata")
|
||||
sXDataChunks.push_back(c);
|
||||
else if (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);
|
||||
else
|
||||
chunks.push_back(c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void ObjFile::readAssociativeDefinition(
|
||||
COFFSymbolRef sym, const coff_aux_section_definition *def) {
|
||||
readAssociativeDefinition(sym, def, def->getNumber(sym.isBigObj()));
|
||||
}
|
||||
|
||||
void ObjFile::readAssociativeDefinition(COFFSymbolRef sym,
|
||||
const coff_aux_section_definition *def,
|
||||
uint32_t parentIndex) {
|
||||
SectionChunk *parent = sparseChunks[parentIndex];
|
||||
int32_t sectionNumber = sym.getSectionNumber();
|
||||
|
||||
auto diag = [&]() {
|
||||
StringRef name, parentName;
|
||||
coffObj->getSymbolName(sym, name);
|
||||
|
||||
const coff_section *parentSec = getSection(parentIndex);
|
||||
if (Expected<StringRef> e = coffObj->getSectionName(parentSec))
|
||||
parentName = *e;
|
||||
error(toString(this) + ": associative comdat " + name + " (sec " +
|
||||
Twine(sectionNumber) + ") has invalid reference to section " +
|
||||
parentName + " (sec " + Twine(parentIndex) + ")");
|
||||
};
|
||||
|
||||
if (parent == pendingComdat) {
|
||||
// This can happen if an associative comdat refers to another associative
|
||||
// comdat that appears after it (invalid per COFF spec) or to a section
|
||||
// without any symbols.
|
||||
diag();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check whether the parent is prevailing. If it is, so are we, and we read
|
||||
// the section; otherwise mark it as discarded.
|
||||
if (parent) {
|
||||
SectionChunk *c = readSection(sectionNumber, def, "");
|
||||
sparseChunks[sectionNumber] = c;
|
||||
if (c) {
|
||||
c->selection = IMAGE_COMDAT_SELECT_ASSOCIATIVE;
|
||||
parent->addAssociative(c);
|
||||
}
|
||||
} else {
|
||||
sparseChunks[sectionNumber] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ObjFile::recordPrevailingSymbolForMingw(
|
||||
COFFSymbolRef sym, DenseMap<StringRef, uint32_t> &prevailingSectionMap) {
|
||||
// For comdat symbols in executable sections, where this is the copy
|
||||
// of the section chunk we actually include instead of discarding it,
|
||||
// add the symbol to a map to allow using it for implicitly
|
||||
// associating .[px]data$<func> sections to it.
|
||||
int32_t sectionNumber = sym.getSectionNumber();
|
||||
SectionChunk *sc = sparseChunks[sectionNumber];
|
||||
if (sc && sc->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) {
|
||||
StringRef name;
|
||||
coffObj->getSymbolName(sym, name);
|
||||
if (getMachineType() == I386)
|
||||
name.consume_front("_");
|
||||
prevailingSectionMap[name] = sectionNumber;
|
||||
}
|
||||
}
|
||||
|
||||
void ObjFile::maybeAssociateSEHForMingw(
|
||||
COFFSymbolRef sym, const coff_aux_section_definition *def,
|
||||
const DenseMap<StringRef, uint32_t> &prevailingSectionMap) {
|
||||
StringRef name;
|
||||
coffObj->getSymbolName(sym, name);
|
||||
if (name.consume_front(".pdata$") || name.consume_front(".xdata$") ||
|
||||
name.consume_front(".eh_frame$")) {
|
||||
// For MinGW, treat .[px]data$<func> and .eh_frame$<func> as implicitly
|
||||
// associative to the symbol <func>.
|
||||
auto parentSym = prevailingSectionMap.find(name);
|
||||
if (parentSym != prevailingSectionMap.end())
|
||||
readAssociativeDefinition(sym, def, parentSym->second);
|
||||
}
|
||||
}
|
||||
|
||||
Symbol *ObjFile::createRegular(COFFSymbolRef sym) {
|
||||
SectionChunk *sc = sparseChunks[sym.getSectionNumber()];
|
||||
if (sym.isExternal()) {
|
||||
StringRef name;
|
||||
coffObj->getSymbolName(sym, name);
|
||||
if (sc)
|
||||
return symtab->addRegular(this, name, sym.getGeneric(), sc);
|
||||
// For MinGW symbols named .weak.* that point to a discarded section,
|
||||
// don't create an Undefined symbol. If nothing ever refers to the symbol,
|
||||
// everything should be fine. If something actually refers to the symbol
|
||||
// (e.g. the undefined weak alias), linking will fail due to undefined
|
||||
// references at the end.
|
||||
if (config->mingw && name.startswith(".weak."))
|
||||
return nullptr;
|
||||
return symtab->addUndefined(name, this, false);
|
||||
}
|
||||
if (sc)
|
||||
return make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false,
|
||||
/*IsExternal*/ false, sym.getGeneric(), sc);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ObjFile::initializeSymbols() {
|
||||
uint32_t numSymbols = coffObj->getNumberOfSymbols();
|
||||
symbols.resize(numSymbols);
|
||||
|
||||
SmallVector<std::pair<Symbol *, uint32_t>, 8> weakAliases;
|
||||
std::vector<uint32_t> pendingIndexes;
|
||||
pendingIndexes.reserve(numSymbols);
|
||||
|
||||
DenseMap<StringRef, uint32_t> prevailingSectionMap;
|
||||
std::vector<const coff_aux_section_definition *> comdatDefs(
|
||||
coffObj->getNumberOfSections() + 1);
|
||||
|
||||
for (uint32_t i = 0; i < numSymbols; ++i) {
|
||||
COFFSymbolRef coffSym = check(coffObj->getSymbol(i));
|
||||
bool prevailingComdat;
|
||||
if (coffSym.isUndefined()) {
|
||||
symbols[i] = createUndefined(coffSym);
|
||||
} else if (coffSym.isWeakExternal()) {
|
||||
symbols[i] = createUndefined(coffSym);
|
||||
uint32_t tagIndex = coffSym.getAux<coff_aux_weak_external>()->TagIndex;
|
||||
weakAliases.emplace_back(symbols[i], tagIndex);
|
||||
} else if (Optional<Symbol *> optSym =
|
||||
createDefined(coffSym, comdatDefs, prevailingComdat)) {
|
||||
symbols[i] = *optSym;
|
||||
if (config->mingw && prevailingComdat)
|
||||
recordPrevailingSymbolForMingw(coffSym, prevailingSectionMap);
|
||||
} else {
|
||||
// createDefined() returns None if a symbol belongs to a section that
|
||||
// was pending at the point when the symbol was read. This can happen in
|
||||
// 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
|
||||
// the time we finish visiting the remaining symbols in the symbol
|
||||
// table. So we postpone the handling of this symbol until that time.
|
||||
pendingIndexes.push_back(i);
|
||||
}
|
||||
i += coffSym.getNumberOfAuxSymbols();
|
||||
}
|
||||
|
||||
for (uint32_t i : pendingIndexes) {
|
||||
COFFSymbolRef sym = check(coffObj->getSymbol(i));
|
||||
if (const coff_aux_section_definition *def = sym.getSectionDefinition()) {
|
||||
if (def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
|
||||
readAssociativeDefinition(sym, def);
|
||||
else if (config->mingw)
|
||||
maybeAssociateSEHForMingw(sym, def, prevailingSectionMap);
|
||||
}
|
||||
if (sparseChunks[sym.getSectionNumber()] == pendingComdat) {
|
||||
StringRef name;
|
||||
coffObj->getSymbolName(sym, name);
|
||||
log("comdat section " + name +
|
||||
" without leader and unassociated, discarding");
|
||||
continue;
|
||||
}
|
||||
symbols[i] = createRegular(sym);
|
||||
}
|
||||
|
||||
for (auto &kv : weakAliases) {
|
||||
Symbol *sym = kv.first;
|
||||
uint32_t idx = kv.second;
|
||||
checkAndSetWeakAlias(symtab, this, sym, symbols[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
Symbol *ObjFile::createUndefined(COFFSymbolRef sym) {
|
||||
StringRef name;
|
||||
coffObj->getSymbolName(sym, name);
|
||||
return symtab->addUndefined(name, this, sym.isWeakExternal());
|
||||
}
|
||||
|
||||
void ObjFile::handleComdatSelection(COFFSymbolRef sym, COMDATType &selection,
|
||||
bool &prevailing, DefinedRegular *leader) {
|
||||
if (prevailing)
|
||||
return;
|
||||
// There's already an existing comdat for this symbol: `Leader`.
|
||||
// Use the comdats's selection field to determine if the new
|
||||
// symbol in `Sym` should be discarded, produce a duplicate symbol
|
||||
// error, etc.
|
||||
|
||||
SectionChunk *leaderChunk = nullptr;
|
||||
COMDATType leaderSelection = IMAGE_COMDAT_SELECT_ANY;
|
||||
|
||||
if (leader->data) {
|
||||
leaderChunk = leader->getChunk();
|
||||
leaderSelection = leaderChunk->selection;
|
||||
} else {
|
||||
// FIXME: comdats from LTO files don't know their selection; treat them
|
||||
// as "any".
|
||||
selection = leaderSelection;
|
||||
}
|
||||
|
||||
if ((selection == IMAGE_COMDAT_SELECT_ANY &&
|
||||
leaderSelection == IMAGE_COMDAT_SELECT_LARGEST) ||
|
||||
(selection == IMAGE_COMDAT_SELECT_LARGEST &&
|
||||
leaderSelection == IMAGE_COMDAT_SELECT_ANY)) {
|
||||
// cl.exe picks "any" for vftables when building with /GR- and
|
||||
// "largest" when building with /GR. To be able to link object files
|
||||
// compiled with each flag, "any" and "largest" are merged as "largest".
|
||||
leaderSelection = selection = IMAGE_COMDAT_SELECT_LARGEST;
|
||||
}
|
||||
|
||||
// Other than that, comdat selections must match. This is a bit more
|
||||
// strict than link.exe which allows merging "any" and "largest" if "any"
|
||||
// is the first symbol the linker sees, and it allows merging "largest"
|
||||
// with everything (!) if "largest" is the first symbol the linker sees.
|
||||
// Making this symmetric independent of which selection is seen first
|
||||
// seems better though.
|
||||
// (This behavior matches ModuleLinker::getComdatResult().)
|
||||
if (selection != leaderSelection) {
|
||||
log(("conflicting comdat type for " + toString(*leader) + ": " +
|
||||
Twine((int)leaderSelection) + " in " + toString(leader->getFile()) +
|
||||
" and " + Twine((int)selection) + " in " + toString(this))
|
||||
.str());
|
||||
symtab->reportDuplicate(leader, this);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (selection) {
|
||||
case IMAGE_COMDAT_SELECT_NODUPLICATES:
|
||||
symtab->reportDuplicate(leader, this);
|
||||
break;
|
||||
|
||||
case IMAGE_COMDAT_SELECT_ANY:
|
||||
// Nothing to do.
|
||||
break;
|
||||
|
||||
case IMAGE_COMDAT_SELECT_SAME_SIZE:
|
||||
if (leaderChunk->getSize() != getSection(sym)->SizeOfRawData)
|
||||
symtab->reportDuplicate(leader, this);
|
||||
break;
|
||||
|
||||
case IMAGE_COMDAT_SELECT_EXACT_MATCH: {
|
||||
SectionChunk newChunk(this, getSection(sym));
|
||||
// link.exe only compares section contents here and doesn't complain
|
||||
// if the two comdat sections have e.g. different alignment.
|
||||
// Match that.
|
||||
if (leaderChunk->getContents() != newChunk.getContents())
|
||||
symtab->reportDuplicate(leader, this);
|
||||
break;
|
||||
}
|
||||
|
||||
case IMAGE_COMDAT_SELECT_ASSOCIATIVE:
|
||||
// createDefined() is never called for IMAGE_COMDAT_SELECT_ASSOCIATIVE.
|
||||
// (This means lld-link doesn't produce duplicate symbol errors for
|
||||
// associative comdats while link.exe does, but associate comdats
|
||||
// are never extern in practice.)
|
||||
llvm_unreachable("createDefined not called for associative comdats");
|
||||
|
||||
case IMAGE_COMDAT_SELECT_LARGEST:
|
||||
if (leaderChunk->getSize() < getSection(sym)->SizeOfRawData) {
|
||||
// Replace the existing comdat symbol with the new one.
|
||||
StringRef name;
|
||||
coffObj->getSymbolName(sym, name);
|
||||
// FIXME: This is incorrect: With /opt:noref, the previous sections
|
||||
// make it into the final executable as well. Correct handling would
|
||||
// be to undo reading of the whole old section that's being replaced,
|
||||
// or doing one pass that determines what the final largest comdat
|
||||
// is for all IMAGE_COMDAT_SELECT_LARGEST comdats and then reading
|
||||
// only the largest one.
|
||||
replaceSymbol<DefinedRegular>(leader, this, name, /*IsCOMDAT*/ true,
|
||||
/*IsExternal*/ true, sym.getGeneric(),
|
||||
nullptr);
|
||||
prevailing = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case IMAGE_COMDAT_SELECT_NEWEST:
|
||||
llvm_unreachable("should have been rejected earlier");
|
||||
}
|
||||
}
|
||||
|
||||
Optional<Symbol *> ObjFile::createDefined(
|
||||
COFFSymbolRef sym,
|
||||
std::vector<const coff_aux_section_definition *> &comdatDefs,
|
||||
bool &prevailing) {
|
||||
prevailing = false;
|
||||
auto getName = [&]() {
|
||||
StringRef s;
|
||||
coffObj->getSymbolName(sym, s);
|
||||
return s;
|
||||
};
|
||||
|
||||
if (sym.isCommon()) {
|
||||
auto *c = make<CommonChunk>(sym);
|
||||
chunks.push_back(c);
|
||||
return symtab->addCommon(this, getName(), sym.getValue(), sym.getGeneric(),
|
||||
c);
|
||||
}
|
||||
|
||||
if (sym.isAbsolute()) {
|
||||
StringRef name = getName();
|
||||
|
||||
// Skip special symbols.
|
||||
if (name == "@comp.id")
|
||||
return nullptr;
|
||||
if (name == "@feat.00") {
|
||||
feat00Flags = sym.getValue();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (sym.isExternal())
|
||||
return symtab->addAbsolute(name, sym);
|
||||
return make<DefinedAbsolute>(name, sym);
|
||||
}
|
||||
|
||||
int32_t sectionNumber = sym.getSectionNumber();
|
||||
if (sectionNumber == llvm::COFF::IMAGE_SYM_DEBUG)
|
||||
return nullptr;
|
||||
|
||||
if (llvm::COFF::isReservedSectionNumber(sectionNumber))
|
||||
fatal(toString(this) + ": " + getName() +
|
||||
" should not refer to special section " + Twine(sectionNumber));
|
||||
|
||||
if ((uint32_t)sectionNumber >= sparseChunks.size())
|
||||
fatal(toString(this) + ": " + getName() +
|
||||
" should not refer to non-existent section " + Twine(sectionNumber));
|
||||
|
||||
// Comdat handling.
|
||||
// A comdat symbol consists of two symbol table entries.
|
||||
// The first symbol entry has the name of the section (e.g. .text), fixed
|
||||
// values for the other fields, and one auxilliary record.
|
||||
// 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.
|
||||
|
||||
// Handle comdat leader.
|
||||
if (const coff_aux_section_definition *def = comdatDefs[sectionNumber]) {
|
||||
comdatDefs[sectionNumber] = nullptr;
|
||||
DefinedRegular *leader;
|
||||
|
||||
if (sym.isExternal()) {
|
||||
std::tie(leader, prevailing) =
|
||||
symtab->addComdat(this, getName(), sym.getGeneric());
|
||||
} else {
|
||||
leader = make<DefinedRegular>(this, /*Name*/ "", /*IsCOMDAT*/ false,
|
||||
/*IsExternal*/ false, sym.getGeneric());
|
||||
prevailing = true;
|
||||
}
|
||||
|
||||
if (def->Selection < (int)IMAGE_COMDAT_SELECT_NODUPLICATES ||
|
||||
// Intentionally ends at IMAGE_COMDAT_SELECT_LARGEST: link.exe
|
||||
// doesn't understand IMAGE_COMDAT_SELECT_NEWEST either.
|
||||
def->Selection > (int)IMAGE_COMDAT_SELECT_LARGEST) {
|
||||
fatal("unknown comdat type " + std::to_string((int)def->Selection) +
|
||||
" for " + getName() + " in " + toString(this));
|
||||
}
|
||||
COMDATType selection = (COMDATType)def->Selection;
|
||||
|
||||
if (leader->isCOMDAT)
|
||||
handleComdatSelection(sym, selection, prevailing, leader);
|
||||
|
||||
if (prevailing) {
|
||||
SectionChunk *c = readSection(sectionNumber, def, getName());
|
||||
sparseChunks[sectionNumber] = c;
|
||||
c->sym = cast<DefinedRegular>(leader);
|
||||
c->selection = selection;
|
||||
cast<DefinedRegular>(leader)->data = &c->repl;
|
||||
} else {
|
||||
sparseChunks[sectionNumber] = nullptr;
|
||||
}
|
||||
return leader;
|
||||
}
|
||||
|
||||
// Prepare to handle the comdat leader symbol by setting the section's
|
||||
// ComdatDefs pointer if we encounter a non-associative comdat.
|
||||
if (sparseChunks[sectionNumber] == pendingComdat) {
|
||||
if (const coff_aux_section_definition *def = sym.getSectionDefinition()) {
|
||||
if (def->Selection != IMAGE_COMDAT_SELECT_ASSOCIATIVE)
|
||||
comdatDefs[sectionNumber] = def;
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
return createRegular(sym);
|
||||
}
|
||||
|
||||
MachineTypes ObjFile::getMachineType() {
|
||||
if (coffObj)
|
||||
return static_cast<MachineTypes>(coffObj->getMachine());
|
||||
return IMAGE_FILE_MACHINE_UNKNOWN;
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> ObjFile::getDebugSection(StringRef secName) {
|
||||
if (SectionChunk *sec = SectionChunk::findByName(debugChunks, secName))
|
||||
return sec->consumeDebugMagic();
|
||||
return {};
|
||||
}
|
||||
|
||||
// OBJ files systematically store critical informations in a .debug$S stream,
|
||||
// even if the TU was compiled with no debug info. At least two records are
|
||||
// always there. S_OBJNAME stores a 32-bit signature, which is loaded into the
|
||||
// PCHSignature member. S_COMPILE3 stores compile-time cmd-line flags. This is
|
||||
// currently used to initialize the hotPatchable member.
|
||||
void ObjFile::initializeFlags() {
|
||||
ArrayRef<uint8_t> data = getDebugSection(".debug$S");
|
||||
if (data.empty())
|
||||
return;
|
||||
|
||||
DebugSubsectionArray subsections;
|
||||
|
||||
BinaryStreamReader reader(data, support::little);
|
||||
ExitOnError exitOnErr;
|
||||
exitOnErr(reader.readArray(subsections, data.size()));
|
||||
|
||||
for (const DebugSubsectionRecord &ss : subsections) {
|
||||
if (ss.kind() != DebugSubsectionKind::Symbols)
|
||||
continue;
|
||||
|
||||
unsigned offset = 0;
|
||||
|
||||
// Only parse the first two records. We are only looking for S_OBJNAME
|
||||
// and S_COMPILE3, and they usually appear at the beginning of the
|
||||
// stream.
|
||||
for (unsigned i = 0; i < 2; ++i) {
|
||||
Expected<CVSymbol> sym = readSymbolFromStream(ss.getRecordData(), offset);
|
||||
if (!sym) {
|
||||
consumeError(sym.takeError());
|
||||
return;
|
||||
}
|
||||
if (sym->kind() == SymbolKind::S_COMPILE3) {
|
||||
auto cs =
|
||||
cantFail(SymbolDeserializer::deserializeAs<Compile3Sym>(sym.get()));
|
||||
hotPatchable =
|
||||
(cs.Flags & CompileSym3Flags::HotPatch) != CompileSym3Flags::None;
|
||||
}
|
||||
if (sym->kind() == SymbolKind::S_OBJNAME) {
|
||||
auto objName = cantFail(SymbolDeserializer::deserializeAs<ObjNameSym>(
|
||||
sym.get()));
|
||||
pchSignature = objName.Signature;
|
||||
}
|
||||
offset += sym->length();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Depending on the compilation flags, OBJs can refer to external files,
|
||||
// necessary to merge this OBJ into the final PDB. We currently support two
|
||||
// types of external files: Precomp/PCH OBJs, when compiling with /Yc and /Yu.
|
||||
// And PDB type servers, when compiling with /Zi. This function extracts these
|
||||
// dependencies and makes them available as a TpiSource interface (see
|
||||
// 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)
|
||||
return;
|
||||
|
||||
bool isPCH = false;
|
||||
|
||||
ArrayRef<uint8_t> data = getDebugSection(".debug$P");
|
||||
if (!data.empty())
|
||||
isPCH = true;
|
||||
else
|
||||
data = getDebugSection(".debug$T");
|
||||
|
||||
if (data.empty())
|
||||
return;
|
||||
|
||||
CVTypeArray types;
|
||||
BinaryStreamReader reader(data, support::little);
|
||||
cantFail(reader.readArray(types, reader.getLength()));
|
||||
|
||||
CVTypeArray::Iterator firstType = types.begin();
|
||||
if (firstType == types.end())
|
||||
return;
|
||||
|
||||
debugTypes.emplace(types);
|
||||
|
||||
if (isPCH) {
|
||||
debugTypesObj = makePrecompSource(this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (firstType->kind() == LF_TYPESERVER2) {
|
||||
TypeServer2Record ts = cantFail(
|
||||
TypeDeserializer::deserializeAs<TypeServer2Record>(firstType->data()));
|
||||
debugTypesObj = makeUseTypeServerSource(this, &ts);
|
||||
return;
|
||||
}
|
||||
|
||||
if (firstType->kind() == LF_PRECOMP) {
|
||||
PrecompRecord precomp = cantFail(
|
||||
TypeDeserializer::deserializeAs<PrecompRecord>(firstType->data()));
|
||||
debugTypesObj = makeUsePrecompSource(this, &precomp);
|
||||
return;
|
||||
}
|
||||
|
||||
debugTypesObj = makeTpiSource(this);
|
||||
}
|
||||
|
||||
StringRef ltrim1(StringRef s, const char *chars) {
|
||||
if (!s.empty() && strchr(chars, s[0]))
|
||||
return s.substr(1);
|
||||
return s;
|
||||
}
|
||||
|
||||
void ImportFile::parse() {
|
||||
const char *buf = mb.getBufferStart();
|
||||
const auto *hdr = reinterpret_cast<const coff_import_header *>(buf);
|
||||
|
||||
// Check if the total size is valid.
|
||||
if (mb.getBufferSize() != sizeof(*hdr) + hdr->SizeOfData)
|
||||
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);
|
||||
const char *nameStart = buf + sizeof(coff_import_header) + name.size() + 1;
|
||||
dllName = StringRef(nameStart);
|
||||
StringRef extName;
|
||||
switch (hdr->getNameType()) {
|
||||
case IMPORT_ORDINAL:
|
||||
extName = "";
|
||||
break;
|
||||
case IMPORT_NAME:
|
||||
extName = name;
|
||||
break;
|
||||
case IMPORT_NAME_NOPREFIX:
|
||||
extName = ltrim1(name, "?@_");
|
||||
break;
|
||||
case IMPORT_NAME_UNDECORATE:
|
||||
extName = ltrim1(name, "?@_");
|
||||
extName = extName.substr(0, extName.find('@'));
|
||||
break;
|
||||
}
|
||||
|
||||
this->hdr = hdr;
|
||||
externalName = extName;
|
||||
|
||||
impSym = symtab->addImportData(impName, this);
|
||||
// If this was a duplicate, we logged an error but may continue;
|
||||
// in this case, impSym is nullptr.
|
||||
if (!impSym)
|
||||
return;
|
||||
|
||||
if (hdr->getType() == llvm::COFF::IMPORT_CONST)
|
||||
static_cast<void>(symtab->addImportData(name, this));
|
||||
|
||||
// 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(
|
||||
name, cast_or_null<DefinedImportData>(impSym), hdr->Machine);
|
||||
}
|
||||
|
||||
BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
|
||||
uint64_t offsetInArchive)
|
||||
: InputFile(BitcodeKind, mb) {
|
||||
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 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.
|
||||
MemoryBufferRef mbref(
|
||||
mb.getBuffer(),
|
||||
saver.save(archiveName + path +
|
||||
(archiveName.empty() ? "" : utostr(offsetInArchive))));
|
||||
|
||||
obj = check(lto::InputFile::create(mbref));
|
||||
}
|
||||
|
||||
void BitcodeFile::parse() {
|
||||
std::vector<std::pair<Symbol *, bool>> comdat(obj->getComdatTable().size());
|
||||
for (size_t i = 0; i != obj->getComdatTable().size(); ++i)
|
||||
// FIXME: lto::InputFile doesn't keep enough data to do correct comdat
|
||||
// selection handling.
|
||||
comdat[i] = symtab->addComdat(this, saver.save(obj->getComdatTable()[i]));
|
||||
for (const lto::InputFile::Symbol &objSym : obj->symbols()) {
|
||||
StringRef symName = saver.save(objSym.getName());
|
||||
int comdatIndex = objSym.getComdatIndex();
|
||||
Symbol *sym;
|
||||
if (objSym.isUndefined()) {
|
||||
sym = symtab->addUndefined(symName, this, false);
|
||||
} else if (objSym.isCommon()) {
|
||||
sym = symtab->addCommon(this, symName, objSym.getCommonSize());
|
||||
} else if (objSym.isWeak() && objSym.isIndirect()) {
|
||||
// Weak external.
|
||||
sym = symtab->addUndefined(symName, this, true);
|
||||
std::string fallback = objSym.getCOFFWeakExternalFallback();
|
||||
Symbol *alias = symtab->addUndefined(saver.save(fallback));
|
||||
checkAndSetWeakAlias(symtab, this, sym, alias);
|
||||
} else if (comdatIndex != -1) {
|
||||
if (symName == obj->getComdatTable()[comdatIndex])
|
||||
sym = comdat[comdatIndex].first;
|
||||
else if (comdat[comdatIndex].second)
|
||||
sym = symtab->addRegular(this, symName);
|
||||
else
|
||||
sym = symtab->addUndefined(symName, this, false);
|
||||
} else {
|
||||
sym = symtab->addRegular(this, symName);
|
||||
}
|
||||
symbols.push_back(sym);
|
||||
if (objSym.isUsed())
|
||||
config->gcroot.push_back(sym);
|
||||
}
|
||||
directives = obj->getCOFFLinkerOpts();
|
||||
}
|
||||
|
||||
MachineTypes BitcodeFile::getMachineType() {
|
||||
switch (Triple(obj->getTargetTriple()).getArch()) {
|
||||
case Triple::x86_64:
|
||||
return AMD64;
|
||||
case Triple::x86:
|
||||
return I386;
|
||||
case Triple::arm:
|
||||
return ARMNT;
|
||||
case Triple::aarch64:
|
||||
return ARM64;
|
||||
default:
|
||||
return IMAGE_FILE_MACHINE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
std::string replaceThinLTOSuffix(StringRef path) {
|
||||
StringRef suffix = config->thinLTOObjectSuffixReplace.first;
|
||||
StringRef repl = config->thinLTOObjectSuffixReplace.second;
|
||||
|
||||
if (path.consume_back(suffix))
|
||||
return (path + repl).str();
|
||||
return path;
|
||||
}
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
// Returns the last element of a path, which is supposed to be a filename.
|
||||
static StringRef getBasename(StringRef path) {
|
||||
return sys::path::filename(path, sys::path::Style::windows);
|
||||
}
|
||||
|
||||
// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)".
|
||||
std::string lld::toString(const coff::InputFile *file) {
|
||||
if (!file)
|
||||
return "<internal>";
|
||||
if (file->parentName.empty() || file->kind() == coff::InputFile::ImportKind)
|
||||
return file->getName();
|
||||
|
||||
return (getBasename(file->parentName) + "(" + getBasename(file->getName()) +
|
||||
")")
|
||||
.str();
|
||||
}
|
||||
321
deps/lld/COFF/InputFiles.h
vendored
321
deps/lld/COFF/InputFiles.h
vendored
@ -1,321 +0,0 @@
|
||||
//===- InputFiles.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_INPUT_FILES_H
|
||||
#define LLD_COFF_INPUT_FILES_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/LTO/LTO.h"
|
||||
#include "llvm/Object/Archive.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include "llvm/Support/StringSaver.h"
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
namespace pdb {
|
||||
class DbiModuleDescriptorBuilder;
|
||||
}
|
||||
}
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
std::vector<MemoryBufferRef> getArchiveMembers(llvm::object::Archive *file);
|
||||
|
||||
using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN;
|
||||
using llvm::COFF::MachineTypes;
|
||||
using llvm::object::Archive;
|
||||
using llvm::object::COFFObjectFile;
|
||||
using llvm::object::COFFSymbolRef;
|
||||
using llvm::object::coff_import_header;
|
||||
using llvm::object::coff_section;
|
||||
|
||||
class Chunk;
|
||||
class Defined;
|
||||
class DefinedImportData;
|
||||
class DefinedImportThunk;
|
||||
class DefinedRegular;
|
||||
class Lazy;
|
||||
class SectionChunk;
|
||||
class Symbol;
|
||||
class Undefined;
|
||||
class TpiSource;
|
||||
|
||||
// The root class of input files.
|
||||
class InputFile {
|
||||
public:
|
||||
enum Kind { ArchiveKind, ObjectKind, ImportKind, BitcodeKind };
|
||||
Kind kind() const { return fileKind; }
|
||||
virtual ~InputFile() {}
|
||||
|
||||
// Returns the filename.
|
||||
StringRef getName() const { return mb.getBufferIdentifier(); }
|
||||
|
||||
// Reads a file (the constructor doesn't do that).
|
||||
virtual void parse() = 0;
|
||||
|
||||
// Returns the CPU type this file was compiled to.
|
||||
virtual MachineTypes getMachineType() { return IMAGE_FILE_MACHINE_UNKNOWN; }
|
||||
|
||||
MemoryBufferRef mb;
|
||||
|
||||
// An archive file name if this file is created from an archive.
|
||||
StringRef parentName;
|
||||
|
||||
// Returns .drectve section contents if exist.
|
||||
StringRef getDirectives() { return directives; }
|
||||
|
||||
protected:
|
||||
InputFile(Kind k, MemoryBufferRef m) : mb(m), fileKind(k) {}
|
||||
|
||||
StringRef directives;
|
||||
|
||||
private:
|
||||
const Kind fileKind;
|
||||
};
|
||||
|
||||
// .lib or .a file.
|
||||
class ArchiveFile : public InputFile {
|
||||
public:
|
||||
explicit ArchiveFile(MemoryBufferRef m);
|
||||
static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; }
|
||||
void parse() override;
|
||||
|
||||
// Enqueues an archive member load for the given symbol. If we've already
|
||||
// enqueued a load for the same archive member, this function does nothing,
|
||||
// which ensures that we don't load the same member more than once.
|
||||
void addMember(const Archive::Symbol &sym);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Archive> file;
|
||||
llvm::DenseSet<uint64_t> seen;
|
||||
};
|
||||
|
||||
// .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) {}
|
||||
static bool classof(const InputFile *f) { return f->kind() == ObjectKind; }
|
||||
void parse() override;
|
||||
MachineTypes getMachineType() override;
|
||||
ArrayRef<Chunk *> getChunks() { return chunks; }
|
||||
ArrayRef<SectionChunk *> getDebugChunks() { return debugChunks; }
|
||||
ArrayRef<SectionChunk *> getSXDataChunks() { return sXDataChunks; }
|
||||
ArrayRef<SectionChunk *> getGuardFidChunks() { return guardFidChunks; }
|
||||
ArrayRef<SectionChunk *> getGuardLJmpChunks() { return guardLJmpChunks; }
|
||||
ArrayRef<Symbol *> getSymbols() { return symbols; }
|
||||
|
||||
ArrayRef<uint8_t> getDebugSection(StringRef secName);
|
||||
|
||||
// Returns a Symbol object for the symbolIndex'th symbol in the
|
||||
// underlying object file.
|
||||
Symbol *getSymbol(uint32_t symbolIndex) {
|
||||
return symbols[symbolIndex];
|
||||
}
|
||||
|
||||
// Returns the underlying COFF file.
|
||||
COFFObjectFile *getCOFFObj() { return coffObj.get(); }
|
||||
|
||||
// Add a symbol for a range extension thunk. Return the new symbol table
|
||||
// index. This index can be used to modify a relocation.
|
||||
uint32_t addRangeThunkSymbol(Symbol *thunk) {
|
||||
symbols.push_back(thunk);
|
||||
return symbols.size() - 1;
|
||||
}
|
||||
|
||||
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.
|
||||
uint32_t feat00Flags = 0;
|
||||
|
||||
// True if this object file is compatible with SEH. COFF-specific and
|
||||
// x86-only. COFF spec 5.10.1. The .sxdata section.
|
||||
bool hasSafeSEH() { return feat00Flags & 0x1; }
|
||||
|
||||
// True if this file was compiled with /guard:cf.
|
||||
bool hasGuardCF() { return feat00Flags & 0x800; }
|
||||
|
||||
// Pointer to the PDB module descriptor builder. Various debug info records
|
||||
// will reference object files by "module index", which is here. Things like
|
||||
// source files and section contributions are also recorded here. Will be null
|
||||
// if we are not producing a PDB.
|
||||
llvm::pdb::DbiModuleDescriptorBuilder *moduleDBI = nullptr;
|
||||
|
||||
const coff_section *addrsigSec = nullptr;
|
||||
|
||||
// When using Microsoft precompiled headers, this is the PCH's key.
|
||||
// The same key is used by both the precompiled object, and objects using the
|
||||
// precompiled object. Any difference indicates out-of-date objects.
|
||||
llvm::Optional<uint32_t> pchSignature;
|
||||
|
||||
// Whether this is an object file created from .res files.
|
||||
bool isResourceObjFile = false;
|
||||
|
||||
// Whether this file was compiled with /hotpatch.
|
||||
bool hotPatchable = false;
|
||||
|
||||
// Whether the object was already merged into the final PDB.
|
||||
bool mergedIntoPDB = false;
|
||||
|
||||
// If the OBJ has a .debug$T stream, this tells how it will be handled.
|
||||
TpiSource *debugTypesObj = nullptr;
|
||||
|
||||
// The .debug$T stream if there's one.
|
||||
llvm::Optional<llvm::codeview::CVTypeArray> debugTypes;
|
||||
|
||||
private:
|
||||
const coff_section* getSection(uint32_t i);
|
||||
const coff_section *getSection(COFFSymbolRef sym) {
|
||||
return getSection(sym.getSectionNumber());
|
||||
}
|
||||
|
||||
void initializeChunks();
|
||||
void initializeSymbols();
|
||||
void initializeFlags();
|
||||
void initializeDependencies();
|
||||
|
||||
SectionChunk *
|
||||
readSection(uint32_t sectionNumber,
|
||||
const llvm::object::coff_aux_section_definition *def,
|
||||
StringRef leaderName);
|
||||
|
||||
void readAssociativeDefinition(
|
||||
COFFSymbolRef coffSym,
|
||||
const llvm::object::coff_aux_section_definition *def);
|
||||
|
||||
void readAssociativeDefinition(
|
||||
COFFSymbolRef coffSym,
|
||||
const llvm::object::coff_aux_section_definition *def,
|
||||
uint32_t parentSection);
|
||||
|
||||
void recordPrevailingSymbolForMingw(
|
||||
COFFSymbolRef coffSym,
|
||||
llvm::DenseMap<StringRef, uint32_t> &prevailingSectionMap);
|
||||
|
||||
void maybeAssociateSEHForMingw(
|
||||
COFFSymbolRef sym, const llvm::object::coff_aux_section_definition *def,
|
||||
const llvm::DenseMap<StringRef, uint32_t> &prevailingSectionMap);
|
||||
|
||||
// Given a new symbol Sym with comdat selection Selection, if the new
|
||||
// symbol is not (yet) Prevailing and the existing comdat leader set to
|
||||
// Leader, emits a diagnostic if the new symbol and its selection doesn't
|
||||
// match the existing symbol and its selection. If either old or new
|
||||
// symbol have selection IMAGE_COMDAT_SELECT_LARGEST, Sym might replace
|
||||
// the existing leader. In that case, Prevailing is set to true.
|
||||
void handleComdatSelection(COFFSymbolRef sym,
|
||||
llvm::COFF::COMDATType &selection,
|
||||
bool &prevailing, DefinedRegular *leader);
|
||||
|
||||
llvm::Optional<Symbol *>
|
||||
createDefined(COFFSymbolRef sym,
|
||||
std::vector<const llvm::object::coff_aux_section_definition *>
|
||||
&comdatDefs,
|
||||
bool &prevailingComdat);
|
||||
Symbol *createRegular(COFFSymbolRef sym);
|
||||
Symbol *createUndefined(COFFSymbolRef sym);
|
||||
|
||||
std::unique_ptr<COFFObjectFile> coffObj;
|
||||
|
||||
// List of all chunks defined by this file. This includes both section
|
||||
// chunks and non-section chunks for common symbols.
|
||||
std::vector<Chunk *> chunks;
|
||||
|
||||
// CodeView debug info sections.
|
||||
std::vector<SectionChunk *> debugChunks;
|
||||
|
||||
// Chunks containing symbol table indices of exception handlers. Only used for
|
||||
// 32-bit x86.
|
||||
std::vector<SectionChunk *> sXDataChunks;
|
||||
|
||||
// Chunks containing symbol table indices of address taken symbols and longjmp
|
||||
// targets. These are not linked into the final binary when /guard:cf is set.
|
||||
std::vector<SectionChunk *> guardFidChunks;
|
||||
std::vector<SectionChunk *> guardLJmpChunks;
|
||||
|
||||
// This vector contains the same chunks as Chunks, but they are
|
||||
// indexed such that you can get a SectionChunk by section index.
|
||||
// Nonexistent section indices are filled with null pointers.
|
||||
// (Because section number is 1-based, the first slot is always a
|
||||
// null pointer.)
|
||||
std::vector<SectionChunk *> sparseChunks;
|
||||
|
||||
// This vector contains a list of all symbols defined or referenced by this
|
||||
// file. They are indexed such that you can get a Symbol by symbol
|
||||
// index. Nonexistent indices (which are occupied by auxiliary
|
||||
// symbols in the real symbol table) are filled with null pointers.
|
||||
std::vector<Symbol *> symbols;
|
||||
};
|
||||
|
||||
// This type represents import library members that contain DLL names
|
||||
// and symbols exported from the DLLs. See Microsoft PE/COFF spec. 7
|
||||
// for details about the format.
|
||||
class ImportFile : public InputFile {
|
||||
public:
|
||||
explicit ImportFile(MemoryBufferRef m) : InputFile(ImportKind, 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;
|
||||
|
||||
private:
|
||||
void parse() override;
|
||||
|
||||
public:
|
||||
StringRef externalName;
|
||||
const coff_import_header *hdr;
|
||||
Chunk *location = nullptr;
|
||||
|
||||
// We want to eliminate dllimported symbols if no one actually refers them.
|
||||
// These "Live" bits are used to keep track of which import library members
|
||||
// are actually in use.
|
||||
//
|
||||
// If the Live bit is turned off by MarkLive, Writer will ignore dllimported
|
||||
// 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;
|
||||
};
|
||||
|
||||
// Used for LTO.
|
||||
class BitcodeFile : public InputFile {
|
||||
public:
|
||||
BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
|
||||
uint64_t offsetInArchive);
|
||||
static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
|
||||
ArrayRef<Symbol *> getSymbols() { return symbols; }
|
||||
MachineTypes getMachineType() override;
|
||||
static std::vector<BitcodeFile *> instances;
|
||||
std::unique_ptr<llvm::lto::InputFile> obj;
|
||||
|
||||
private:
|
||||
void parse() override;
|
||||
|
||||
std::vector<Symbol *> symbols;
|
||||
};
|
||||
|
||||
std::string replaceThinLTOSuffix(StringRef path);
|
||||
} // namespace coff
|
||||
|
||||
std::string toString(const coff::InputFile *file);
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
206
deps/lld/COFF/LTO.cpp
vendored
206
deps/lld/COFF/LTO.cpp
vendored
@ -1,206 +0,0 @@
|
||||
//===- LTO.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 "LTO.h"
|
||||
#include "Config.h"
|
||||
#include "InputFiles.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/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/CodeGen.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <vector>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::coff;
|
||||
|
||||
// Creates an empty file to and returns a raw_fd_ostream to write to it.
|
||||
static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
|
||||
std::error_code ec;
|
||||
auto ret =
|
||||
llvm::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::F_None);
|
||||
if (ec) {
|
||||
error("cannot open " + file + ": " + ec.message());
|
||||
return nullptr;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::string getThinLTOOutputFile(StringRef path) {
|
||||
return lto::getThinLTOOutputFile(path,
|
||||
config->thinLTOPrefixReplace.first,
|
||||
config->thinLTOPrefixReplace.second);
|
||||
}
|
||||
|
||||
static lto::Config createConfig() {
|
||||
lto::Config c;
|
||||
c.Options = initTargetOptionsFromCodeGenFlags();
|
||||
|
||||
// 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.
|
||||
c.Options.FunctionSections = true;
|
||||
c.Options.DataSections = true;
|
||||
|
||||
// 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)
|
||||
c.RelocModel = Reloc::Static;
|
||||
else
|
||||
c.RelocModel = Reloc::PIC_;
|
||||
c.DisableVerify = true;
|
||||
c.DiagHandler = diagnosticHandler;
|
||||
c.OptLevel = config->ltoo;
|
||||
c.CPU = getCPUStr();
|
||||
c.MAttrs = getMAttrs();
|
||||
c.CGOptLevel = args::getCGOptLevel(config->ltoo);
|
||||
|
||||
if (config->saveTemps)
|
||||
checkError(c.addSaveTemps(std::string(config->outputFile) + ".",
|
||||
/*UseInputModulePath*/ true));
|
||||
return c;
|
||||
}
|
||||
|
||||
BitcodeCompiler::BitcodeCompiler() {
|
||||
// Initialize indexFile.
|
||||
if (!config->thinLTOIndexOnlyArg.empty())
|
||||
indexFile = openFile(config->thinLTOIndexOnlyArg);
|
||||
|
||||
// Initialize ltoObj.
|
||||
lto::ThinBackend backend;
|
||||
if (config->thinLTOIndexOnly) {
|
||||
auto OnIndexWrite = [&](StringRef S) { thinIndices.erase(S); };
|
||||
backend = lto::createWriteIndexesThinBackend(
|
||||
config->thinLTOPrefixReplace.first, config->thinLTOPrefixReplace.second,
|
||||
config->thinLTOEmitImportsFiles, indexFile.get(), OnIndexWrite);
|
||||
} else if (config->thinLTOJobs != 0) {
|
||||
backend = lto::createInProcessThinBackend(config->thinLTOJobs);
|
||||
}
|
||||
|
||||
ltoObj = llvm::make_unique<lto::LTO>(createConfig(), backend,
|
||||
config->ltoPartitions);
|
||||
}
|
||||
|
||||
BitcodeCompiler::~BitcodeCompiler() = default;
|
||||
|
||||
static void undefine(Symbol *s) { replaceSymbol<Undefined>(s, s->getName()); }
|
||||
|
||||
void BitcodeCompiler::add(BitcodeFile &f) {
|
||||
lto::InputFile &obj = *f.obj;
|
||||
unsigned symNum = 0;
|
||||
std::vector<Symbol *> symBodies = f.getSymbols();
|
||||
std::vector<lto::SymbolResolution> resols(symBodies.size());
|
||||
|
||||
if (config->thinLTOIndexOnly)
|
||||
thinIndices.insert(obj.getName());
|
||||
|
||||
// Provide a resolution to the LTO API for each symbol.
|
||||
for (const lto::InputFile::Symbol &objSym : obj.symbols()) {
|
||||
Symbol *sym = symBodies[symNum];
|
||||
lto::SymbolResolution &r = resols[symNum];
|
||||
++symNum;
|
||||
|
||||
// Ideally we shouldn't check for SF_Undefined but currently IRObjectFile
|
||||
// reports two symbols for module ASM defined. Without this check, lld
|
||||
// flags an undefined in IR with a definition in ASM as prevailing.
|
||||
// Once IRObjectFile is fixed to report only one symbol this hack can
|
||||
// be removed.
|
||||
r.Prevailing = !objSym.isUndefined() && sym->getFile() == &f;
|
||||
r.VisibleToRegularObj = sym->isUsedInRegularObj;
|
||||
if (r.Prevailing)
|
||||
undefine(sym);
|
||||
}
|
||||
checkError(ltoObj->add(std::move(f.obj), resols));
|
||||
}
|
||||
|
||||
// Merge all the bitcode files we have seen, codegen the result
|
||||
// and return the resulting objects.
|
||||
std::vector<StringRef> BitcodeCompiler::compile() {
|
||||
unsigned maxTasks = ltoObj->getMaxTasks();
|
||||
buf.resize(maxTasks);
|
||||
files.resize(maxTasks);
|
||||
|
||||
// The /lldltocache option specifies the path to a directory in which to cache
|
||||
// native object files for ThinLTO incremental builds. If a path was
|
||||
// specified, configure LTO to use it as the cache directory.
|
||||
lto::NativeObjectCache cache;
|
||||
if (!config->ltoCache.empty())
|
||||
cache = check(lto::localCache(
|
||||
config->ltoCache, [&](size_t task, std::unique_ptr<MemoryBuffer> mb) {
|
||||
files[task] = std::move(mb);
|
||||
}));
|
||||
|
||||
checkError(ltoObj->run(
|
||||
[&](size_t task) {
|
||||
return llvm::make_unique<lto::NativeObjectStream>(
|
||||
llvm::make_unique<raw_svector_ostream>(buf[task]));
|
||||
},
|
||||
cache));
|
||||
|
||||
// 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");
|
||||
}
|
||||
|
||||
// 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 (indexFile)
|
||||
indexFile->close();
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!config->ltoCache.empty())
|
||||
pruneCache(config->ltoCache, config->ltoCachePolicy);
|
||||
|
||||
std::vector<StringRef> ret;
|
||||
for (unsigned i = 0; i != maxTasks; ++i) {
|
||||
if (buf[i].empty())
|
||||
continue;
|
||||
if (config->saveTemps) {
|
||||
if (i == 0)
|
||||
saveBuffer(buf[i], config->outputFile + ".lto.obj");
|
||||
else
|
||||
saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.obj");
|
||||
}
|
||||
ret.emplace_back(buf[i].data(), buf[i].size());
|
||||
}
|
||||
|
||||
for (std::unique_ptr<MemoryBuffer> &file : files)
|
||||
if (file)
|
||||
ret.push_back(file->getBuffer());
|
||||
|
||||
return ret;
|
||||
}
|
||||
60
deps/lld/COFF/LTO.h
vendored
60
deps/lld/COFF/LTO.h
vendored
@ -1,60 +0,0 @@
|
||||
//===- LTO.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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file provides a way to combine bitcode files into one COFF
|
||||
// file by compiling them using LLVM.
|
||||
//
|
||||
// If LTO is in use, your input files are not in regular COFF files
|
||||
// but instead LLVM bitcode files. In that case, the linker has to
|
||||
// convert bitcode files into the native format so that we can create
|
||||
// a COFF file that contains native code. This file provides that
|
||||
// functionality.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_COFF_LTO_H
|
||||
#define LLD_COFF_LTO_H
|
||||
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
namespace lto {
|
||||
class LTO;
|
||||
}
|
||||
}
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
class BitcodeFile;
|
||||
class InputFile;
|
||||
|
||||
class BitcodeCompiler {
|
||||
public:
|
||||
BitcodeCompiler();
|
||||
~BitcodeCompiler();
|
||||
|
||||
void add(BitcodeFile &f);
|
||||
std::vector<StringRef> compile();
|
||||
|
||||
private:
|
||||
std::unique_ptr<llvm::lto::LTO> ltoObj;
|
||||
std::vector<SmallString<0>> buf;
|
||||
std::vector<std::unique_ptr<MemoryBuffer>> files;
|
||||
std::unique_ptr<llvm::raw_fd_ostream> indexFile;
|
||||
llvm::DenseSet<StringRef> thinIndices;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
124
deps/lld/COFF/MapFile.cpp
vendored
124
deps/lld/COFF/MapFile.cpp
vendored
@ -1,124 +0,0 @@
|
||||
//===- MapFile.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 file implements the /lldmap option. It shows lists in order and
|
||||
// hierarchically the output sections, input sections, input files and
|
||||
// symbol:
|
||||
//
|
||||
// Address Size Align Out File Symbol
|
||||
// 00201000 00000015 4 .text
|
||||
// 00201000 0000000e 4 test.o:(.text)
|
||||
// 0020100e 00000000 0 local
|
||||
// 00201005 00000000 0 f(int)
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "MapFile.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.h"
|
||||
#include "Writer.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/Threads.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::coff;
|
||||
|
||||
using SymbolMapTy =
|
||||
DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>;
|
||||
|
||||
static const std::string indent8 = " "; // 8 spaces
|
||||
static const std::string indent16 = " "; // 16 spaces
|
||||
|
||||
// Print out the first three columns of a line.
|
||||
static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size,
|
||||
uint64_t align) {
|
||||
os << format("%08llx %08llx %5lld ", addr, size, align);
|
||||
}
|
||||
|
||||
// Returns a list of all symbols that we want to print out.
|
||||
static std::vector<DefinedRegular *> getSymbols() {
|
||||
std::vector<DefinedRegular *> v;
|
||||
for (ObjFile *file : ObjFile::instances)
|
||||
for (Symbol *b : file->getSymbols())
|
||||
if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))
|
||||
if (sym && !sym->getCOFFSymbol().isSectionDefinition())
|
||||
v.push_back(sym);
|
||||
return v;
|
||||
}
|
||||
|
||||
// Returns a map from sections to their symbols.
|
||||
static SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> syms) {
|
||||
SymbolMapTy ret;
|
||||
for (DefinedRegular *s : syms)
|
||||
ret[s->getChunk()].push_back(s);
|
||||
|
||||
// Sort symbols by address.
|
||||
for (auto &it : ret) {
|
||||
SmallVectorImpl<DefinedRegular *> &v = it.second;
|
||||
std::sort(v.begin(), v.end(), [](DefinedRegular *a, DefinedRegular *b) {
|
||||
return a->getRVA() < b->getRVA();
|
||||
});
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Construct a map from symbols to their stringified representations.
|
||||
static DenseMap<DefinedRegular *, std::string>
|
||||
getSymbolStrings(ArrayRef<DefinedRegular *> syms) {
|
||||
std::vector<std::string> str(syms.size());
|
||||
parallelForEachN((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]);
|
||||
});
|
||||
|
||||
DenseMap<DefinedRegular *, std::string> ret;
|
||||
for (size_t i = 0, e = syms.size(); i < e; ++i)
|
||||
ret[syms[i]] = std::move(str[i]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void coff::writeMapFile(ArrayRef<OutputSection *> outputSections) {
|
||||
if (config->mapFile.empty())
|
||||
return;
|
||||
|
||||
std::error_code ec;
|
||||
raw_fd_ostream os(config->mapFile, ec, sys::fs::F_None);
|
||||
if (ec)
|
||||
fatal("cannot open " + config->mapFile + ": " + ec.message());
|
||||
|
||||
// Collect symbol info that we want to print out.
|
||||
std::vector<DefinedRegular *> syms = getSymbols();
|
||||
SymbolMapTy sectionSyms = getSectionSyms(syms);
|
||||
DenseMap<DefinedRegular *, std::string> symStr = getSymbolStrings(syms);
|
||||
|
||||
// Print out the header line.
|
||||
os << "Address Size Align Out In Symbol\n";
|
||||
|
||||
// Print out file contents.
|
||||
for (OutputSection *sec : outputSections) {
|
||||
writeHeader(os, sec->getRVA(), sec->getVirtualSize(), /*align=*/pageSize);
|
||||
os << sec->name << '\n';
|
||||
|
||||
for (Chunk *c : sec->chunks) {
|
||||
auto *sc = dyn_cast<SectionChunk>(c);
|
||||
if (!sc)
|
||||
continue;
|
||||
|
||||
writeHeader(os, sc->getRVA(), sc->getSize(), sc->getAlignment());
|
||||
os << indent8 << sc->file->getName() << ":(" << sc->getSectionName()
|
||||
<< ")\n";
|
||||
for (DefinedRegular *sym : sectionSyms[sc])
|
||||
os << symStr[sym] << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
21
deps/lld/COFF/MapFile.h
vendored
21
deps/lld/COFF/MapFile.h
vendored
@ -1,21 +0,0 @@
|
||||
//===- MapFile.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_MAPFILE_H
|
||||
#define LLD_COFF_MAPFILE_H
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
class OutputSection;
|
||||
void writeMapFile(llvm::ArrayRef<OutputSection *> outputSections);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
73
deps/lld/COFF/MarkLive.cpp
vendored
73
deps/lld/COFF/MarkLive.cpp
vendored
@ -1,73 +0,0 @@
|
||||
//===- MarkLive.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 "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());
|
||||
|
||||
// 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);
|
||||
|
||||
// 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.
|
||||
SmallVector<SectionChunk *, 256> worklist;
|
||||
|
||||
// COMDAT section chunks are dead by default. Add non-COMDAT chunks.
|
||||
for (Chunk *c : chunks)
|
||||
if (auto *sc = dyn_cast<SectionChunk>(c))
|
||||
if (sc->live)
|
||||
worklist.push_back(sc);
|
||||
|
||||
auto enqueue = [&](SectionChunk *c) {
|
||||
if (c->live)
|
||||
return;
|
||||
c->live = true;
|
||||
worklist.push_back(c);
|
||||
};
|
||||
|
||||
auto addSym = [&](Symbol *b) {
|
||||
if (auto *sym = dyn_cast<DefinedRegular>(b))
|
||||
enqueue(sym->getChunk());
|
||||
else if (auto *sym = dyn_cast<DefinedImportData>(b))
|
||||
sym->file->live = true;
|
||||
else if (auto *sym = dyn_cast<DefinedImportThunk>(b))
|
||||
sym->wrappedSym->file->live = sym->wrappedSym->file->thunkLive = true;
|
||||
};
|
||||
|
||||
// Add GC root chunks.
|
||||
for (Symbol *b : config->gcroot)
|
||||
addSym(b);
|
||||
|
||||
while (!worklist.empty()) {
|
||||
SectionChunk *sc = worklist.pop_back_val();
|
||||
assert(sc->live && "We mark as live when pushing onto the worklist!");
|
||||
|
||||
// Mark all symbols listed in the relocation table for this section.
|
||||
for (Symbol *b : sc->symbols())
|
||||
if (b)
|
||||
addSym(b);
|
||||
|
||||
// Mark associative sections if any.
|
||||
for (SectionChunk &c : sc->children())
|
||||
enqueue(&c);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
25
deps/lld/COFF/MarkLive.h
vendored
25
deps/lld/COFF/MarkLive.h
vendored
@ -1,25 +0,0 @@
|
||||
//===- MarkLive.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_MARKLIVE_H
|
||||
#define LLD_COFF_MARKLIVE_H
|
||||
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
class Chunk;
|
||||
|
||||
void markLive(ArrayRef<Chunk *> chunks);
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
#endif // LLD_COFF_MARKLIVE_H
|
||||
166
deps/lld/COFF/MinGW.cpp
vendored
166
deps/lld/COFF/MinGW.cpp
vendored
@ -1,166 +0,0 @@
|
||||
//===- MinGW.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 "MinGW.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::coff;
|
||||
using namespace llvm;
|
||||
using namespace llvm::COFF;
|
||||
|
||||
AutoExporter::AutoExporter() {
|
||||
excludeLibs = {
|
||||
"libgcc",
|
||||
"libgcc_s",
|
||||
"libstdc++",
|
||||
"libmingw32",
|
||||
"libmingwex",
|
||||
"libg2c",
|
||||
"libsupc++",
|
||||
"libobjc",
|
||||
"libgcj",
|
||||
"libclang_rt.builtins",
|
||||
"libclang_rt.builtins-aarch64",
|
||||
"libclang_rt.builtins-arm",
|
||||
"libclang_rt.builtins-i386",
|
||||
"libclang_rt.builtins-x86_64",
|
||||
"libc++",
|
||||
"libc++abi",
|
||||
"libunwind",
|
||||
"libmsvcrt",
|
||||
"libucrtbase",
|
||||
};
|
||||
|
||||
excludeObjects = {
|
||||
"crt0.o", "crt1.o", "crt1u.o", "crt2.o", "crt2u.o", "dllcrt1.o",
|
||||
"dllcrt2.o", "gcrt0.o", "gcrt1.o", "gcrt2.o", "crtbegin.o", "crtend.o",
|
||||
};
|
||||
|
||||
excludeSymbolPrefixes = {
|
||||
// Import symbols
|
||||
"__imp_",
|
||||
"__IMPORT_DESCRIPTOR_",
|
||||
// Extra import symbols from GNU import libraries
|
||||
"__nm_",
|
||||
// C++ symbols
|
||||
"__rtti_",
|
||||
"__builtin_",
|
||||
// Artifical symbols such as .refptr
|
||||
".",
|
||||
};
|
||||
|
||||
excludeSymbolSuffixes = {
|
||||
"_iname",
|
||||
"_NULL_THUNK_DATA",
|
||||
};
|
||||
|
||||
if (config->machine == I386) {
|
||||
excludeSymbols = {
|
||||
"__NULL_IMPORT_DESCRIPTOR",
|
||||
"__pei386_runtime_relocator",
|
||||
"_do_pseudo_reloc",
|
||||
"_impure_ptr",
|
||||
"__impure_ptr",
|
||||
"__fmode",
|
||||
"_environ",
|
||||
"___dso_handle",
|
||||
// These are the MinGW names that differ from the standard
|
||||
// ones (lacking an extra underscore).
|
||||
"_DllMain@12",
|
||||
"_DllEntryPoint@12",
|
||||
"_DllMainCRTStartup@12",
|
||||
};
|
||||
excludeSymbolPrefixes.insert("__head_");
|
||||
} else {
|
||||
excludeSymbols = {
|
||||
"__NULL_IMPORT_DESCRIPTOR",
|
||||
"_pei386_runtime_relocator",
|
||||
"do_pseudo_reloc",
|
||||
"impure_ptr",
|
||||
"_impure_ptr",
|
||||
"_fmode",
|
||||
"environ",
|
||||
"__dso_handle",
|
||||
// These are the MinGW names that differ from the standard
|
||||
// ones (lacking an extra underscore).
|
||||
"DllMain",
|
||||
"DllEntryPoint",
|
||||
"DllMainCRTStartup",
|
||||
};
|
||||
excludeSymbolPrefixes.insert("_head_");
|
||||
}
|
||||
}
|
||||
|
||||
void AutoExporter::addWholeArchive(StringRef path) {
|
||||
StringRef libName = sys::path::filename(path);
|
||||
// Drop the file extension, to match the processing below.
|
||||
libName = libName.substr(0, libName.rfind('.'));
|
||||
excludeLibs.erase(libName);
|
||||
}
|
||||
|
||||
bool AutoExporter::shouldExport(Defined *sym) const {
|
||||
if (!sym || !sym->isLive() || !sym->getChunk())
|
||||
return false;
|
||||
|
||||
// Only allow the symbol kinds that make sense to export; in particular,
|
||||
// disallow import symbols.
|
||||
if (!isa<DefinedRegular>(sym) && !isa<DefinedCommon>(sym))
|
||||
return false;
|
||||
if (excludeSymbols.count(sym->getName()))
|
||||
return false;
|
||||
|
||||
for (StringRef prefix : excludeSymbolPrefixes.keys())
|
||||
if (sym->getName().startswith(prefix))
|
||||
return false;
|
||||
for (StringRef suffix : excludeSymbolSuffixes.keys())
|
||||
if (sym->getName().endswith(suffix))
|
||||
return false;
|
||||
|
||||
// If a corresponding __imp_ symbol exists and is defined, don't export it.
|
||||
if (symtab->find(("__imp_" + sym->getName()).str()))
|
||||
return false;
|
||||
|
||||
// Check that file is non-null before dereferencing it, symbols not
|
||||
// originating in regular object files probably shouldn't be exported.
|
||||
if (!sym->getFile())
|
||||
return false;
|
||||
|
||||
StringRef libName = sys::path::filename(sym->getFile()->parentName);
|
||||
|
||||
// Drop the file extension.
|
||||
libName = libName.substr(0, libName.rfind('.'));
|
||||
if (!libName.empty())
|
||||
return !excludeLibs.count(libName);
|
||||
|
||||
StringRef fileName = sys::path::filename(sym->getFile()->getName());
|
||||
return !excludeObjects.count(fileName);
|
||||
}
|
||||
|
||||
void coff::writeDefFile(StringRef name) {
|
||||
std::error_code ec;
|
||||
raw_fd_ostream os(name, ec, sys::fs::F_None);
|
||||
if (ec)
|
||||
fatal("cannot open " + name + ": " + ec.message());
|
||||
|
||||
os << "EXPORTS\n";
|
||||
for (Export &e : config->exports) {
|
||||
os << " " << e.exportName << " "
|
||||
<< "@" << e.ordinal;
|
||||
if (auto *def = dyn_cast_or_null<Defined>(e.sym)) {
|
||||
if (def && def->getChunk() &&
|
||||
!(def->getChunk()->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE))
|
||||
os << " DATA";
|
||||
}
|
||||
os << "\n";
|
||||
}
|
||||
}
|
||||
41
deps/lld/COFF/MinGW.h
vendored
41
deps/lld/COFF/MinGW.h
vendored
@ -1,41 +0,0 @@
|
||||
//===- MinGW.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_MINGW_H
|
||||
#define LLD_COFF_MINGW_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "Symbols.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
// Logic for deciding what symbols to export, when exporting all
|
||||
// symbols for MinGW.
|
||||
class AutoExporter {
|
||||
public:
|
||||
AutoExporter();
|
||||
|
||||
void addWholeArchive(StringRef path);
|
||||
|
||||
llvm::StringSet<> excludeSymbols;
|
||||
llvm::StringSet<> excludeSymbolPrefixes;
|
||||
llvm::StringSet<> excludeSymbolSuffixes;
|
||||
llvm::StringSet<> excludeLibs;
|
||||
llvm::StringSet<> excludeObjects;
|
||||
|
||||
bool shouldExport(Defined *sym) const;
|
||||
};
|
||||
|
||||
void writeDefFile(StringRef name);
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
225
deps/lld/COFF/Options.td
vendored
225
deps/lld/COFF/Options.td
vendored
@ -1,225 +0,0 @@
|
||||
include "llvm/Option/OptParser.td"
|
||||
|
||||
// link.exe accepts options starting with either a dash or a slash.
|
||||
|
||||
// Flag that takes no arguments.
|
||||
class F<string name> : Flag<["/", "-", "/?", "-?"], name>;
|
||||
|
||||
// Flag that takes one argument after ":".
|
||||
class P<string name, string help> :
|
||||
Joined<["/", "-", "/?", "-?"], name#":">, HelpText<help>;
|
||||
|
||||
// Boolean flag which can be suffixed by ":no". Using it unsuffixed turns the
|
||||
// flag on and using it suffixed by ":no" turns it off.
|
||||
multiclass B<string name, string help_on, string help_off> {
|
||||
def "" : F<name>, HelpText<help_on>;
|
||||
def _no : F<name#":no">, HelpText<help_off>;
|
||||
}
|
||||
|
||||
def align : P<"align", "Section alignment">;
|
||||
def aligncomm : P<"aligncomm", "Set common symbol alignment">;
|
||||
def alternatename : P<"alternatename", "Define weak alias">;
|
||||
def base : P<"base", "Base address of the program">;
|
||||
def color_diagnostics: Flag<["--"], "color-diagnostics">,
|
||||
HelpText<"Use colors in diagnostics">;
|
||||
def color_diagnostics_eq: Joined<["--"], "color-diagnostics=">,
|
||||
HelpText<"Use colors in diagnostics; one of 'always', 'never', 'auto'">;
|
||||
def defaultlib : P<"defaultlib", "Add the library to the list of input files">;
|
||||
def delayload : P<"delayload", "Delay loaded DLL name">;
|
||||
def entry : P<"entry", "Name of entry point symbol">;
|
||||
def errorlimit : P<"errorlimit",
|
||||
"Maximum number of errors to emit before stopping (0 = no limit)">;
|
||||
def export : P<"export", "Export a function">;
|
||||
// No help text because /failifmismatch is not intended to be used by the user.
|
||||
def failifmismatch : P<"failifmismatch", "">;
|
||||
def filealign : P<"filealign", "Section alignment in the output file">;
|
||||
def functionpadmin : F<"functionpadmin">;
|
||||
def functionpadmin_opt : P<"functionpadmin", "Prepares an image for hotpatching">;
|
||||
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 lib : F<"lib">,
|
||||
HelpText<"Act like lib.exe; must be first argument if present">;
|
||||
def libpath : P<"libpath", "Additional library search path">;
|
||||
def linkrepro : P<"linkrepro", "Dump linker invocation and input files for debugging">;
|
||||
def lldltocache : P<"lldltocache", "Path to ThinLTO cached object file directory">;
|
||||
def lldltocachepolicy : P<"lldltocachepolicy", "Pruning policy for the ThinLTO cache">;
|
||||
def lldsavetemps : F<"lldsavetemps">,
|
||||
HelpText<"Save temporary files instead of deleting them">;
|
||||
def machine : P<"machine", "Specify target platform">;
|
||||
def merge : P<"merge", "Combine sections">;
|
||||
def mllvm : P<"mllvm", "Options to pass to LLVM">;
|
||||
def nodefaultlib : P<"nodefaultlib", "Remove a default library">;
|
||||
def opt : P<"opt", "Control optimizations">;
|
||||
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 no_color_diagnostics: F<"no-color-diagnostics">,
|
||||
HelpText<"Do not use colors in diagnostics">;
|
||||
def pdb : P<"pdb", "PDB file path">;
|
||||
def pdbaltpath : P<"pdbaltpath", "PDB file path to embed in the image">;
|
||||
def section : P<"section", "Specify section attributes">;
|
||||
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 version : P<"version", "Specify a version number in the PE header">;
|
||||
def wholearchive_file : P<"wholearchive", "Include all object files from this archive">;
|
||||
|
||||
def disallowlib : Joined<["/", "-", "/?", "-?"], "disallowlib:">,
|
||||
Alias<nodefaultlib>;
|
||||
|
||||
def manifest : F<"manifest">, HelpText<"Create .manifest file">;
|
||||
def manifest_colon : P<
|
||||
"manifest",
|
||||
"NO disables manifest output; EMBED[,ID=#] embeds manifest as resource in the image">;
|
||||
def manifestuac : P<"manifestuac", "User access control">;
|
||||
def manifestfile : P<"manifestfile", "Manifest output path, with /manifest">;
|
||||
def manifestdependency : P<
|
||||
"manifestdependency",
|
||||
"Attributes for <dependency> element in manifest file; implies /manifest">;
|
||||
def manifestinput : P<
|
||||
"manifestinput",
|
||||
"Additional manifest inputs; only valid with /manifest:embed">;
|
||||
|
||||
// We cannot use multiclass P because class name "incl" is different
|
||||
// from its command line option name. We do this because "include" is
|
||||
// a reserved keyword in tablegen.
|
||||
def incl : Joined<["/", "-", "/?", "-?"], "include:">,
|
||||
HelpText<"Force symbol to be added to symbol table as undefined one">;
|
||||
|
||||
// "def" is also a keyword.
|
||||
def deffile : Joined<["/", "-", "/?", "-?"], "def:">,
|
||||
HelpText<"Use module-definition file">;
|
||||
|
||||
def debug : F<"debug">, HelpText<"Embed a symbol table in the image">;
|
||||
def debug_opt : P<"debug", "Embed a symbol table in the image with option">;
|
||||
def debugtype : P<"debugtype", "Debug Info Options">;
|
||||
def dll : F<"dll">, HelpText<"Create a DLL">;
|
||||
def driver : P<"driver", "Generate a Windows NT Kernel Mode Driver">;
|
||||
def nodefaultlib_all : F<"nodefaultlib">,
|
||||
HelpText<"Remove all default libraries">;
|
||||
def noentry : F<"noentry">,
|
||||
HelpText<"Don't add reference to DllMainCRTStartup; only valid with /dll">;
|
||||
def profile : F<"profile">;
|
||||
def repro : F<"Brepro">,
|
||||
HelpText<"Use a hash of the executable as the PE header timestamp">;
|
||||
def swaprun : P<"swaprun",
|
||||
"Comma-separated list of 'cd' or 'net'">;
|
||||
def swaprun_cd : F<"swaprun:cd">, Alias<swaprun>, AliasArgs<["cd"]>,
|
||||
HelpText<"Make loader run output binary from swap instead of from CD">;
|
||||
def swaprun_net : F<"swaprun:net">, Alias<swaprun>, AliasArgs<["net"]>,
|
||||
HelpText<"Make loader run output binary from swap instead of from network">;
|
||||
def verbose : F<"verbose">;
|
||||
def wholearchive_flag : F<"wholearchive">;
|
||||
|
||||
def force : F<"force">,
|
||||
HelpText<"Allow undefined and multiply defined symbols when creating executables">;
|
||||
def force_unresolved : F<"force:unresolved">,
|
||||
HelpText<"Allow undefined symbols when creating executables">;
|
||||
def force_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 allowbind : B<"allowbind", "Enable DLL binding (default)",
|
||||
"Disable DLL binding">;
|
||||
defm allowisolation : B<"allowisolation", "Enable DLL isolation (default)",
|
||||
"Disable DLL isolation">;
|
||||
defm appcontainer : B<"appcontainer",
|
||||
"Image can only be run in an app container",
|
||||
"Image can run outside an app container (default)">;
|
||||
defm dynamicbase : B<"dynamicbase", "Enable ASLR (default unless /fixed)",
|
||||
"Disable ASLR (default when /fixed)">;
|
||||
defm fixed : B<"fixed", "Disable base relocations",
|
||||
"Enable base relocations (default)">;
|
||||
defm highentropyva : B<"highentropyva",
|
||||
"Enable 64-bit ASLR (default on 64-bit)",
|
||||
"Disable 64-bit ASLR">;
|
||||
defm incremental : B<"incremental",
|
||||
"Keep original import library if contents are unchanged",
|
||||
"Overwrite import library even if contents are unchanged">;
|
||||
defm integritycheck : B<"integritycheck",
|
||||
"Set FORCE_INTEGRITY bit in PE header",
|
||||
"No effect (default)">;
|
||||
defm largeaddressaware : B<"largeaddressaware",
|
||||
"Enable large addresses (default on 64-bit)",
|
||||
"Disable large addresses (default on 32-bit)">;
|
||||
defm nxcompat : B<"nxcompat", "Enable data execution prevention (default)",
|
||||
"Disable data execution provention">;
|
||||
defm safeseh : B<"safeseh",
|
||||
"Produce an image with Safe Exception Handler (only for x86)",
|
||||
"Don't produce an image with Safe Exception Handler">;
|
||||
defm tsaware : B<"tsaware",
|
||||
"Create Terminal Server aware executable (default)",
|
||||
"Create non-Terminal Server aware executable">;
|
||||
|
||||
def help : F<"help">;
|
||||
|
||||
// /?? and -?? must be before /? and -? to not confuse lib/Options.
|
||||
def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias<help>;
|
||||
|
||||
// LLD extensions
|
||||
def exclude_all_symbols : F<"exclude-all-symbols">;
|
||||
def export_all_symbols : F<"export-all-symbols">;
|
||||
defm demangle : B<"demangle",
|
||||
"Demangle symbols in output (default)",
|
||||
"Do not demangle symbols in output">;
|
||||
def include_optional : Joined<["/", "-", "/?", "-?"], "includeoptional:">,
|
||||
HelpText<"Add symbol as undefined, but allow it to remain undefined">;
|
||||
def kill_at : F<"kill-at">;
|
||||
def lldmingw : F<"lldmingw">;
|
||||
def output_def : Joined<["/", "-", "/?", "-?"], "output-def:">;
|
||||
def pdb_source_path : P<"pdbsourcepath",
|
||||
"Base path used to make relative source file path absolute in PDB">;
|
||||
def rsp_quoting : Joined<["--"], "rsp-quoting=">,
|
||||
HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">;
|
||||
def thinlto_emit_imports_files :
|
||||
F<"thinlto-emit-imports-files">,
|
||||
HelpText<"Emit .imports files with -thinlto-index-only">;
|
||||
def thinlto_index_only :
|
||||
F<"thinlto-index-only">,
|
||||
HelpText<"Instead of linking, emit ThinLTO index files">;
|
||||
def thinlto_index_only_arg : P<
|
||||
"thinlto-index-only",
|
||||
"-thinlto-index-only and also write native module names to file">;
|
||||
def thinlto_object_suffix_replace : P<
|
||||
"thinlto-object-suffix-replace",
|
||||
"'old;new' replace old suffix with new suffix in ThinLTO index">;
|
||||
def thinlto_prefix_replace: P<
|
||||
"thinlto-prefix-replace",
|
||||
"'old;new' replace old prefix with new prefix in ThinLTO outputs">;
|
||||
def dash_dash_version : Flag<["--"], "version">,
|
||||
HelpText<"Print version information">;
|
||||
defm threads: B<"threads",
|
||||
"Run the linker multi-threaded (default)",
|
||||
"Do not run the linker multi-threaded">;
|
||||
|
||||
// Flags for debugging
|
||||
def lldmap : F<"lldmap">;
|
||||
def lldmap_file : Joined<["/", "-", "/?", "-?"], "lldmap:">;
|
||||
def show_timing : F<"time">;
|
||||
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 nologo : F<"nologo">;
|
||||
def throwingnew : F<"throwingnew">;
|
||||
def editandcontinue : F<"editandcontinue">;
|
||||
def fastfail : F<"fastfail">;
|
||||
|
||||
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">;
|
||||
1836
deps/lld/COFF/PDB.cpp
vendored
1836
deps/lld/COFF/PDB.cpp
vendored
File diff suppressed because it is too large
Load Diff
37
deps/lld/COFF/PDB.h
vendored
37
deps/lld/COFF/PDB.h
vendored
@ -1,37 +0,0 @@
|
||||
//===- PDB.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_PDB_H
|
||||
#define LLD_COFF_PDB_H
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
namespace llvm {
|
||||
namespace codeview {
|
||||
union DebugInfo;
|
||||
}
|
||||
}
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
class OutputSection;
|
||||
class SectionChunk;
|
||||
class SymbolTable;
|
||||
|
||||
void createPDB(SymbolTable *symtab,
|
||||
llvm::ArrayRef<OutputSection *> outputSections,
|
||||
llvm::ArrayRef<uint8_t> sectionTable,
|
||||
llvm::codeview::DebugInfo *buildId);
|
||||
|
||||
std::pair<llvm::StringRef, uint32_t> getFileLine(const SectionChunk *c,
|
||||
uint32_t addr);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
1
deps/lld/COFF/README.md
vendored
1
deps/lld/COFF/README.md
vendored
@ -1 +0,0 @@
|
||||
See docs/NewLLD.rst
|
||||
615
deps/lld/COFF/SymbolTable.cpp
vendored
615
deps/lld/COFF/SymbolTable.cpp
vendored
@ -1,615 +0,0 @@
|
||||
//===- SymbolTable.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 "SymbolTable.h"
|
||||
#include "Config.h"
|
||||
#include "Driver.h"
|
||||
#include "LTO.h"
|
||||
#include "PDB.h"
|
||||
#include "Symbols.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "lld/Common/Timer.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/Object/WindowsMachineFlag.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <utility>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
static Timer ltoTimer("LTO", Timer::root());
|
||||
|
||||
SymbolTable *symtab;
|
||||
|
||||
void SymbolTable::addFile(InputFile *file) {
|
||||
log("Reading " + toString(file));
|
||||
file->parse();
|
||||
|
||||
MachineTypes mt = file->getMachineType();
|
||||
if (config->machine == IMAGE_FILE_MACHINE_UNKNOWN) {
|
||||
config->machine = mt;
|
||||
} else if (mt != IMAGE_FILE_MACHINE_UNKNOWN && config->machine != mt) {
|
||||
error(toString(file) + ": machine type " + machineToStr(mt) +
|
||||
" conflicts with " + machineToStr(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);
|
||||
}
|
||||
|
||||
static void errorOrWarn(const Twine &s) {
|
||||
if (config->forceUnresolved)
|
||||
warn(s);
|
||||
else
|
||||
error(s);
|
||||
}
|
||||
|
||||
// Returns the symbol in SC whose value is <= Addr that is closest to Addr.
|
||||
// This is generally the global variable or function whose definition contains
|
||||
// Addr.
|
||||
static Symbol *getSymbol(SectionChunk *sc, uint32_t addr) {
|
||||
DefinedRegular *candidate = nullptr;
|
||||
|
||||
for (Symbol *s : sc->file->getSymbols()) {
|
||||
auto *d = dyn_cast_or_null<DefinedRegular>(s);
|
||||
if (!d || !d->data || d->getChunk() != sc || d->getValue() > addr ||
|
||||
(candidate && d->getValue() < candidate->getValue()))
|
||||
continue;
|
||||
|
||||
candidate = d;
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
|
||||
// Given a file and the index of a symbol in that file, returns a description
|
||||
// of all references to that symbol from that file. If no debug information is
|
||||
// available, returns just the name of the file, else one string per actual
|
||||
// reference as described in the debug info.
|
||||
std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex) {
|
||||
struct Location {
|
||||
Symbol *sym;
|
||||
std::pair<StringRef, uint32_t> fileLine;
|
||||
};
|
||||
std::vector<Location> locations;
|
||||
|
||||
for (Chunk *c : file->getChunks()) {
|
||||
auto *sc = dyn_cast<SectionChunk>(c);
|
||||
if (!sc)
|
||||
continue;
|
||||
for (const coff_relocation &r : sc->getRelocs()) {
|
||||
if (r.SymbolTableIndex != symIndex)
|
||||
continue;
|
||||
std::pair<StringRef, uint32_t> fileLine =
|
||||
getFileLine(sc, r.VirtualAddress);
|
||||
Symbol *sym = getSymbol(sc, r.VirtualAddress);
|
||||
if (!fileLine.first.empty() || sym)
|
||||
locations.push_back({sym, fileLine});
|
||||
}
|
||||
}
|
||||
|
||||
if (locations.empty())
|
||||
return std::vector<std::string>({"\n>>> referenced by " + toString(file)});
|
||||
|
||||
std::vector<std::string> symbolLocations(locations.size());
|
||||
size_t i = 0;
|
||||
for (Location loc : locations) {
|
||||
llvm::raw_string_ostream os(symbolLocations[i++]);
|
||||
os << "\n>>> referenced by ";
|
||||
if (!loc.fileLine.first.empty())
|
||||
os << loc.fileLine.first << ":" << loc.fileLine.second
|
||||
<< "\n>>> ";
|
||||
os << toString(file);
|
||||
if (loc.sym)
|
||||
os << ":(" << toString(*loc.sym) << ')';
|
||||
}
|
||||
return symbolLocations;
|
||||
}
|
||||
|
||||
// For an undefined symbol, stores all files referencing it and the index of
|
||||
// the undefined symbol in each file.
|
||||
struct UndefinedDiag {
|
||||
Symbol *sym;
|
||||
struct File {
|
||||
ObjFile *oFile;
|
||||
uint64_t symIndex;
|
||||
};
|
||||
std::vector<File> files;
|
||||
};
|
||||
|
||||
static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) {
|
||||
std::string out;
|
||||
llvm::raw_string_ostream os(out);
|
||||
os << "undefined symbol: " << toString(*undefDiag.sym);
|
||||
|
||||
const size_t maxUndefReferences = 10;
|
||||
size_t i = 0, numRefs = 0;
|
||||
for (const UndefinedDiag::File &ref : undefDiag.files) {
|
||||
std::vector<std::string> symbolLocations =
|
||||
getSymbolLocations(ref.oFile, ref.symIndex);
|
||||
numRefs += symbolLocations.size();
|
||||
for (const std::string &s : symbolLocations) {
|
||||
if (i >= maxUndefReferences)
|
||||
break;
|
||||
os << s;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (i < numRefs)
|
||||
os << "\n>>> referenced " << numRefs - i << " more times";
|
||||
errorOrWarn(os.str());
|
||||
}
|
||||
|
||||
void SymbolTable::loadMinGWAutomaticImports() {
|
||||
for (auto &i : symMap) {
|
||||
Symbol *sym = i.second;
|
||||
auto *undef = dyn_cast<Undefined>(sym);
|
||||
if (!undef)
|
||||
continue;
|
||||
if (!sym->isUsedInRegularObj)
|
||||
continue;
|
||||
|
||||
StringRef name = undef->getName();
|
||||
|
||||
if (name.startswith("__imp_"))
|
||||
continue;
|
||||
// If we have an undefined symbol, but we have a Lazy representing a
|
||||
// symbol we could load from file, make sure to load that.
|
||||
Lazy *l = dyn_cast_or_null<Lazy>(find(("__imp_" + name).str()));
|
||||
if (!l || l->pendingArchiveLoad)
|
||||
continue;
|
||||
|
||||
log("Loading lazy " + l->getName() + " from " + l->file->getName() +
|
||||
" for automatic import");
|
||||
l->pendingArchiveLoad = true;
|
||||
l->file->addMember(l->sym);
|
||||
}
|
||||
}
|
||||
|
||||
bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) {
|
||||
if (name.startswith("__imp_"))
|
||||
return false;
|
||||
Defined *imp = dyn_cast_or_null<Defined>(find(("__imp_" + name).str()));
|
||||
if (!imp)
|
||||
return false;
|
||||
|
||||
// Replace the reference directly to a variable with a reference
|
||||
// to the import address table instead. This obviously isn't right,
|
||||
// but we mark the symbol as isRuntimePseudoReloc, and a later pass
|
||||
// will add runtime pseudo relocations for every relocation against
|
||||
// this Symbol. The runtime pseudo relocation framework expects the
|
||||
// reference itself to point at the IAT entry.
|
||||
size_t impSize = 0;
|
||||
if (isa<DefinedImportData>(imp)) {
|
||||
log("Automatically importing " + name + " from " +
|
||||
cast<DefinedImportData>(imp)->getDLLName());
|
||||
impSize = sizeof(DefinedImportData);
|
||||
} else if (isa<DefinedRegular>(imp)) {
|
||||
log("Automatically importing " + name + " from " +
|
||||
toString(cast<DefinedRegular>(imp)->file));
|
||||
impSize = sizeof(DefinedRegular);
|
||||
} else {
|
||||
warn("unable to automatically import " + name + " from " + imp->getName() +
|
||||
" from " + toString(cast<DefinedRegular>(imp)->file) +
|
||||
"; unexpected symbol type");
|
||||
return false;
|
||||
}
|
||||
sym->replaceKeepingName(imp, impSize);
|
||||
sym->isRuntimePseudoReloc = true;
|
||||
|
||||
// There may exist symbols named .refptr.<name> which only consist
|
||||
// of a single pointer to <name>. If it turns out <name> is
|
||||
// automatically imported, we don't need to keep the .refptr.<name>
|
||||
// pointer at all, but redirect all accesses to it to the IAT entry
|
||||
// for __imp_<name> instead, and drop the whole .refptr.<name> chunk.
|
||||
DefinedRegular *refptr =
|
||||
dyn_cast_or_null<DefinedRegular>(find((".refptr." + name).str()));
|
||||
if (refptr && refptr->getChunk()->getSize() == config->wordsize) {
|
||||
SectionChunk *sc = dyn_cast_or_null<SectionChunk>(refptr->getChunk());
|
||||
if (sc && sc->getRelocs().size() == 1 && *sc->symbols().begin() == sym) {
|
||||
log("Replacing .refptr." + name + " with " + imp->getName());
|
||||
refptr->getChunk()->live = false;
|
||||
refptr->replaceKeepingName(imp, impSize);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SymbolTable::reportRemainingUndefines() {
|
||||
SmallPtrSet<Symbol *, 8> undefs;
|
||||
DenseMap<Symbol *, Symbol *> localImports;
|
||||
|
||||
for (auto &i : symMap) {
|
||||
Symbol *sym = i.second;
|
||||
auto *undef = dyn_cast<Undefined>(sym);
|
||||
if (!undef)
|
||||
continue;
|
||||
if (!sym->isUsedInRegularObj)
|
||||
continue;
|
||||
|
||||
StringRef name = undef->getName();
|
||||
|
||||
// A weak alias may have been resolved, so check for that.
|
||||
if (Defined *d = undef->getWeakAlias()) {
|
||||
// We want to replace Sym with D. However, we can't just blindly
|
||||
// copy sizeof(SymbolUnion) bytes from D to Sym because D may be an
|
||||
// internal symbol, and internal symbols are stored as "unparented"
|
||||
// Symbols. For that reason we need to check which type of symbol we
|
||||
// are dealing with and copy the correct number of bytes.
|
||||
if (isa<DefinedRegular>(d))
|
||||
memcpy(sym, d, sizeof(DefinedRegular));
|
||||
else if (isa<DefinedAbsolute>(d))
|
||||
memcpy(sym, d, sizeof(DefinedAbsolute));
|
||||
else
|
||||
memcpy(sym, d, sizeof(SymbolUnion));
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we can resolve a symbol by removing __imp_ prefix, do that.
|
||||
// This odd rule is for compatibility with MSVC linker.
|
||||
if (name.startswith("__imp_")) {
|
||||
Symbol *imp = find(name.substr(strlen("__imp_")));
|
||||
if (imp && isa<Defined>(imp)) {
|
||||
auto *d = cast<Defined>(imp);
|
||||
replaceSymbol<DefinedLocalImport>(sym, name, d);
|
||||
localImportChunks.push_back(cast<DefinedLocalImport>(sym)->getChunk());
|
||||
localImports[sym] = d;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// We don't want to report missing Microsoft precompiled headers symbols.
|
||||
// A proper message will be emitted instead in PDBLinker::aquirePrecompObj
|
||||
if (name.contains("_PchSym_"))
|
||||
continue;
|
||||
|
||||
if (config->mingw && handleMinGWAutomaticImport(sym, name))
|
||||
continue;
|
||||
|
||||
// Remaining undefined symbols are not fatal if /force is specified.
|
||||
// They are replaced with dummy defined symbols.
|
||||
if (config->forceUnresolved)
|
||||
replaceSymbol<DefinedAbsolute>(sym, name, 0);
|
||||
undefs.insert(sym);
|
||||
}
|
||||
|
||||
if (undefs.empty() && localImports.empty())
|
||||
return;
|
||||
|
||||
for (Symbol *b : config->gcroot) {
|
||||
if (undefs.count(b))
|
||||
errorOrWarn("<root>: undefined symbol: " + toString(*b));
|
||||
if (config->warnLocallyDefinedImported)
|
||||
if (Symbol *imp = localImports.lookup(b))
|
||||
warn("<root>: locally defined symbol imported: " + toString(*imp) +
|
||||
" (defined in " + toString(imp->getFile()) + ") [LNK4217]");
|
||||
}
|
||||
|
||||
std::vector<UndefinedDiag> undefDiags;
|
||||
DenseMap<Symbol *, int> firstDiag;
|
||||
|
||||
for (ObjFile *file : ObjFile::instances) {
|
||||
size_t symIndex = (size_t)-1;
|
||||
for (Symbol *sym : file->getSymbols()) {
|
||||
++symIndex;
|
||||
if (!sym)
|
||||
continue;
|
||||
if (undefs.count(sym)) {
|
||||
auto it = firstDiag.find(sym);
|
||||
if (it == firstDiag.end()) {
|
||||
firstDiag[sym] = undefDiags.size();
|
||||
undefDiags.push_back({sym, {{file, symIndex}}});
|
||||
} else {
|
||||
undefDiags[it->second].files.push_back({file, symIndex});
|
||||
}
|
||||
}
|
||||
if (config->warnLocallyDefinedImported)
|
||||
if (Symbol *imp = localImports.lookup(sym))
|
||||
warn(toString(file) +
|
||||
": locally defined symbol imported: " + toString(*imp) +
|
||||
" (defined in " + toString(imp->getFile()) + ") [LNK4217]");
|
||||
}
|
||||
}
|
||||
|
||||
for (const UndefinedDiag& undefDiag : undefDiags)
|
||||
reportUndefinedSymbol(undefDiag);
|
||||
}
|
||||
|
||||
std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) {
|
||||
bool inserted = false;
|
||||
Symbol *&sym = symMap[CachedHashStringRef(name)];
|
||||
if (!sym) {
|
||||
sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
|
||||
sym->isUsedInRegularObj = false;
|
||||
sym->pendingArchiveLoad = false;
|
||||
inserted = true;
|
||||
}
|
||||
return {sym, inserted};
|
||||
}
|
||||
|
||||
std::pair<Symbol *, bool> SymbolTable::insert(StringRef name, InputFile *file) {
|
||||
std::pair<Symbol *, bool> result = insert(name);
|
||||
if (!file || !isa<BitcodeFile>(file))
|
||||
result.first->isUsedInRegularObj = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
|
||||
bool isWeakAlias) {
|
||||
Symbol *s;
|
||||
bool wasInserted;
|
||||
std::tie(s, wasInserted) = insert(name, f);
|
||||
if (wasInserted || (isa<Lazy>(s) && isWeakAlias)) {
|
||||
replaceSymbol<Undefined>(s, name);
|
||||
return s;
|
||||
}
|
||||
if (auto *l = dyn_cast<Lazy>(s)) {
|
||||
if (!s->pendingArchiveLoad) {
|
||||
s->pendingArchiveLoad = true;
|
||||
l->file->addMember(l->sym);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void SymbolTable::addLazy(ArchiveFile *f, const Archive::Symbol &sym) {
|
||||
StringRef name = sym.getName();
|
||||
Symbol *s;
|
||||
bool wasInserted;
|
||||
std::tie(s, wasInserted) = insert(name);
|
||||
if (wasInserted) {
|
||||
replaceSymbol<Lazy>(s, f, sym);
|
||||
return;
|
||||
}
|
||||
auto *u = dyn_cast<Undefined>(s);
|
||||
if (!u || u->weakAlias || s->pendingArchiveLoad)
|
||||
return;
|
||||
s->pendingArchiveLoad = true;
|
||||
f->addMember(sym);
|
||||
}
|
||||
|
||||
void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile) {
|
||||
std::string msg = "duplicate symbol: " + toString(*existing) + " in " +
|
||||
toString(existing->getFile()) + " and in " +
|
||||
toString(newFile);
|
||||
|
||||
if (config->forceMultiple)
|
||||
warn(msg);
|
||||
else
|
||||
error(msg);
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) {
|
||||
Symbol *s;
|
||||
bool wasInserted;
|
||||
std::tie(s, wasInserted) = insert(n, nullptr);
|
||||
s->isUsedInRegularObj = true;
|
||||
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s))
|
||||
replaceSymbol<DefinedAbsolute>(s, n, sym);
|
||||
else if (!isa<DefinedCOFF>(s))
|
||||
reportDuplicate(s, nullptr);
|
||||
return s;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addAbsolute(StringRef n, uint64_t va) {
|
||||
Symbol *s;
|
||||
bool wasInserted;
|
||||
std::tie(s, wasInserted) = insert(n, nullptr);
|
||||
s->isUsedInRegularObj = true;
|
||||
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s))
|
||||
replaceSymbol<DefinedAbsolute>(s, n, va);
|
||||
else if (!isa<DefinedCOFF>(s))
|
||||
reportDuplicate(s, nullptr);
|
||||
return s;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) {
|
||||
Symbol *s;
|
||||
bool wasInserted;
|
||||
std::tie(s, wasInserted) = insert(n, nullptr);
|
||||
s->isUsedInRegularObj = true;
|
||||
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s))
|
||||
replaceSymbol<DefinedSynthetic>(s, n, c);
|
||||
else if (!isa<DefinedCOFF>(s))
|
||||
reportDuplicate(s, nullptr);
|
||||
return s;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addRegular(InputFile *f, StringRef n,
|
||||
const coff_symbol_generic *sym,
|
||||
SectionChunk *c) {
|
||||
Symbol *s;
|
||||
bool wasInserted;
|
||||
std::tie(s, wasInserted) = insert(n, f);
|
||||
if (wasInserted || !isa<DefinedRegular>(s))
|
||||
replaceSymbol<DefinedRegular>(s, f, n, /*IsCOMDAT*/ false,
|
||||
/*IsExternal*/ true, sym, c);
|
||||
else
|
||||
reportDuplicate(s, f);
|
||||
return s;
|
||||
}
|
||||
|
||||
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);
|
||||
if (wasInserted || !isa<DefinedRegular>(s)) {
|
||||
replaceSymbol<DefinedRegular>(s, f, n, /*IsCOMDAT*/ true,
|
||||
/*IsExternal*/ true, sym, nullptr);
|
||||
return {cast<DefinedRegular>(s), true};
|
||||
}
|
||||
auto *existingSymbol = cast<DefinedRegular>(s);
|
||||
if (!existingSymbol->isCOMDAT)
|
||||
reportDuplicate(s, f);
|
||||
return {existingSymbol, false};
|
||||
}
|
||||
|
||||
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);
|
||||
if (wasInserted || !isa<DefinedCOFF>(s))
|
||||
replaceSymbol<DefinedCommon>(s, f, n, size, sym, c);
|
||||
else if (auto *dc = dyn_cast<DefinedCommon>(s))
|
||||
if (size > dc->getSize())
|
||||
replaceSymbol<DefinedCommon>(s, f, n, size, sym, c);
|
||||
return s;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addImportData(StringRef n, ImportFile *f) {
|
||||
Symbol *s;
|
||||
bool wasInserted;
|
||||
std::tie(s, wasInserted) = insert(n, nullptr);
|
||||
s->isUsedInRegularObj = true;
|
||||
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) {
|
||||
replaceSymbol<DefinedImportData>(s, n, f);
|
||||
return s;
|
||||
}
|
||||
|
||||
reportDuplicate(s, f);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id,
|
||||
uint16_t machine) {
|
||||
Symbol *s;
|
||||
bool wasInserted;
|
||||
std::tie(s, wasInserted) = insert(name, nullptr);
|
||||
s->isUsedInRegularObj = true;
|
||||
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) {
|
||||
replaceSymbol<DefinedImportThunk>(s, name, id, machine);
|
||||
return s;
|
||||
}
|
||||
|
||||
reportDuplicate(s, id->file);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SymbolTable::addLibcall(StringRef name) {
|
||||
Symbol *sym = findUnderscore(name);
|
||||
if (!sym)
|
||||
return;
|
||||
|
||||
if (Lazy *l = dyn_cast<Lazy>(sym)) {
|
||||
MemoryBufferRef mb = l->getMemberBuffer();
|
||||
if (identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode)
|
||||
addUndefined(sym->getName());
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Chunk *> SymbolTable::getChunks() {
|
||||
std::vector<Chunk *> res;
|
||||
for (ObjFile *file : ObjFile::instances) {
|
||||
ArrayRef<Chunk *> v = file->getChunks();
|
||||
res.insert(res.end(), v.begin(), v.end());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::find(StringRef name) {
|
||||
return symMap.lookup(CachedHashStringRef(name));
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::findUnderscore(StringRef name) {
|
||||
if (config->machine == I386)
|
||||
return find(("_" + name).str());
|
||||
return find(name);
|
||||
}
|
||||
|
||||
// Return all symbols that start with Prefix, possibly ignoring the first
|
||||
// character of Prefix or the first character symbol.
|
||||
std::vector<Symbol *> SymbolTable::getSymsWithPrefix(StringRef prefix) {
|
||||
std::vector<Symbol *> syms;
|
||||
for (auto pair : symMap) {
|
||||
StringRef name = pair.first.val();
|
||||
if (name.startswith(prefix) || name.startswith(prefix.drop_front()) ||
|
||||
name.drop_front().startswith(prefix) ||
|
||||
name.drop_front().startswith(prefix.drop_front())) {
|
||||
syms.push_back(pair.second);
|
||||
}
|
||||
}
|
||||
return syms;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::findMangle(StringRef name) {
|
||||
if (Symbol *sym = find(name))
|
||||
if (!isa<Undefined>(sym))
|
||||
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
|
||||
// vector. Then compare each possibly matching symbol with each possible
|
||||
// mangling.
|
||||
std::vector<Symbol *> syms = getSymsWithPrefix(name);
|
||||
auto findByPrefix = [&syms](const Twine &t) -> Symbol * {
|
||||
std::string prefix = t.str();
|
||||
for (auto *s : syms)
|
||||
if (s->getName().startswith(prefix))
|
||||
return s;
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
// For non-x86, just look for C++ functions.
|
||||
if (config->machine != I386)
|
||||
return findByPrefix("?" + name + "@@Y");
|
||||
|
||||
if (!name.startswith("_"))
|
||||
return nullptr;
|
||||
// Search for x86 stdcall function.
|
||||
if (Symbol *s = findByPrefix(name + "@"))
|
||||
return s;
|
||||
// Search for x86 fastcall function.
|
||||
if (Symbol *s = findByPrefix("@" + name.substr(1) + "@"))
|
||||
return s;
|
||||
// Search for x86 vectorcall function.
|
||||
if (Symbol *s = findByPrefix(name.substr(1) + "@@"))
|
||||
return s;
|
||||
// Search for x86 C++ non-member function.
|
||||
return findByPrefix("?" + name.substr(1) + "@@Y");
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addUndefined(StringRef name) {
|
||||
return addUndefined(name, nullptr, false);
|
||||
}
|
||||
|
||||
std::vector<StringRef> SymbolTable::compileBitcodeFiles() {
|
||||
lto.reset(new BitcodeCompiler);
|
||||
for (BitcodeFile *f : BitcodeFile::instances)
|
||||
lto->add(*f);
|
||||
return lto->compile();
|
||||
}
|
||||
|
||||
void SymbolTable::addCombinedLTOObjects() {
|
||||
if (BitcodeFile::instances.empty())
|
||||
return;
|
||||
|
||||
ScopedTimer t(ltoTimer);
|
||||
for (StringRef object : compileBitcodeFiles()) {
|
||||
auto *obj = make<ObjFile>(MemoryBufferRef(object, "lto.tmp"));
|
||||
obj->parse();
|
||||
ObjFile::instances.push_back(obj);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
132
deps/lld/COFF/SymbolTable.h
vendored
132
deps/lld/COFF/SymbolTable.h
vendored
@ -1,132 +0,0 @@
|
||||
//===- SymbolTable.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_SYMBOL_TABLE_H
|
||||
#define LLD_COFF_SYMBOL_TABLE_H
|
||||
|
||||
#include "InputFiles.h"
|
||||
#include "LTO.h"
|
||||
#include "llvm/ADT/CachedHashString.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/DenseMapInfo.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
namespace llvm {
|
||||
struct LTOCodeGenerator;
|
||||
}
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
class Chunk;
|
||||
class CommonChunk;
|
||||
class Defined;
|
||||
class DefinedAbsolute;
|
||||
class DefinedRegular;
|
||||
class DefinedRelative;
|
||||
class Lazy;
|
||||
class SectionChunk;
|
||||
class Symbol;
|
||||
|
||||
// SymbolTable is a bucket of all known symbols, including defined,
|
||||
// undefined, or lazy symbols (the last one is symbols in archive
|
||||
// files whose archive members are not yet loaded).
|
||||
//
|
||||
// We put all symbols of all files to a SymbolTable, and the
|
||||
// SymbolTable selects the "best" symbols if there are name
|
||||
// conflicts. For example, obviously, a defined symbol is better than
|
||||
// an undefined symbol. Or, if there's a conflict between a lazy and a
|
||||
// undefined, it'll read an archive member to read a real definition
|
||||
// to replace the lazy symbol. The logic is implemented in the
|
||||
// add*() functions, which are called by input files as they are parsed.
|
||||
// There is one add* function per symbol type.
|
||||
class SymbolTable {
|
||||
public:
|
||||
void addFile(InputFile *file);
|
||||
|
||||
// Try to resolve any undefined symbols and update the symbol table
|
||||
// accordingly, then print an error message for any remaining undefined
|
||||
// symbols.
|
||||
void reportRemainingUndefines();
|
||||
|
||||
void loadMinGWAutomaticImports();
|
||||
bool handleMinGWAutomaticImport(Symbol *sym, StringRef name);
|
||||
|
||||
// Returns a list of chunks of selected symbols.
|
||||
std::vector<Chunk *> getChunks();
|
||||
|
||||
// Returns a symbol for a given name. Returns a nullptr if not found.
|
||||
Symbol *find(StringRef name);
|
||||
Symbol *findUnderscore(StringRef name);
|
||||
|
||||
// Occasionally we have to resolve an undefined symbol to its
|
||||
// mangled symbol. This function tries to find a mangled name
|
||||
// for U from the symbol table, and if found, set the symbol as
|
||||
// a weak alias for U.
|
||||
Symbol *findMangle(StringRef name);
|
||||
|
||||
// Build a set of COFF objects representing the combined contents of
|
||||
// BitcodeFiles and add them to the symbol table. Called after all files are
|
||||
// added and before the writer writes results to a file.
|
||||
void addCombinedLTOObjects();
|
||||
std::vector<StringRef> compileBitcodeFiles();
|
||||
|
||||
// Creates an Undefined symbol for a given name.
|
||||
Symbol *addUndefined(StringRef name);
|
||||
|
||||
Symbol *addSynthetic(StringRef n, Chunk *c);
|
||||
Symbol *addAbsolute(StringRef n, uint64_t va);
|
||||
|
||||
Symbol *addUndefined(StringRef name, InputFile *f, bool isWeakAlias);
|
||||
void addLazy(ArchiveFile *f, const Archive::Symbol &sym);
|
||||
Symbol *addAbsolute(StringRef n, COFFSymbolRef s);
|
||||
Symbol *addRegular(InputFile *f, StringRef n,
|
||||
const llvm::object::coff_symbol_generic *s = nullptr,
|
||||
SectionChunk *c = nullptr);
|
||||
std::pair<DefinedRegular *, bool>
|
||||
addComdat(InputFile *f, StringRef n,
|
||||
const llvm::object::coff_symbol_generic *s = nullptr);
|
||||
Symbol *addCommon(InputFile *f, StringRef n, uint64_t size,
|
||||
const llvm::object::coff_symbol_generic *s = nullptr,
|
||||
CommonChunk *c = nullptr);
|
||||
Symbol *addImportData(StringRef n, ImportFile *f);
|
||||
Symbol *addImportThunk(StringRef name, DefinedImportData *s,
|
||||
uint16_t machine);
|
||||
void addLibcall(StringRef name);
|
||||
|
||||
void reportDuplicate(Symbol *existing, InputFile *newFile);
|
||||
|
||||
// A list of chunks which to be added to .rdata.
|
||||
std::vector<Chunk *> localImportChunks;
|
||||
|
||||
// Iterates symbols in non-determinstic hash table order.
|
||||
template <typename T> void forEachSymbol(T callback) {
|
||||
for (auto &pair : symMap)
|
||||
callback(pair.second);
|
||||
}
|
||||
|
||||
private:
|
||||
/// Inserts symbol if not already present.
|
||||
std::pair<Symbol *, bool> insert(StringRef name);
|
||||
/// Same as insert(Name), but also sets isUsedInRegularObj.
|
||||
std::pair<Symbol *, bool> insert(StringRef name, InputFile *f);
|
||||
|
||||
std::vector<Symbol *> getSymsWithPrefix(StringRef prefix);
|
||||
|
||||
llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> symMap;
|
||||
std::unique_ptr<BitcodeCompiler> lto;
|
||||
};
|
||||
|
||||
extern SymbolTable *symtab;
|
||||
|
||||
std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex);
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
131
deps/lld/COFF/Symbols.cpp
vendored
131
deps/lld/COFF/Symbols.cpp
vendored
@ -1,131 +0,0 @@
|
||||
//===- Symbols.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 "Symbols.h"
|
||||
#include "InputFiles.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "lld/Common/Strings.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
|
||||
using namespace lld::coff;
|
||||
|
||||
namespace lld {
|
||||
|
||||
static_assert(sizeof(SymbolUnion) <= 48,
|
||||
"symbols should be optimized for memory usage");
|
||||
|
||||
// Returns a symbol name for an error message.
|
||||
static std::string demangle(StringRef symName) {
|
||||
if (config->demangle)
|
||||
if (Optional<std::string> s = demangleMSVC(symName))
|
||||
return *s;
|
||||
return symName;
|
||||
}
|
||||
std::string toString(coff::Symbol &b) { return demangle(b.getName()); }
|
||||
std::string toCOFFString(const Archive::Symbol &b) {
|
||||
return demangle(b.getName());
|
||||
}
|
||||
|
||||
namespace coff {
|
||||
|
||||
StringRef Symbol::getName() {
|
||||
// COFF symbol names are read lazily for a performance reason.
|
||||
// Non-external symbol names are never used by the linker except for logging
|
||||
// or debugging. Their internal references are resolved not by name but by
|
||||
// symbol index. And because they are not external, no one can refer them by
|
||||
// name. Object files contain lots of non-external symbols, and creating
|
||||
// StringRefs for them (which involves lots of strlen() on the string table)
|
||||
// is a waste of time.
|
||||
if (nameData == nullptr) {
|
||||
auto *d = cast<DefinedCOFF>(this);
|
||||
StringRef nameStr;
|
||||
cast<ObjFile>(d->file)->getCOFFObj()->getSymbolName(d->sym, nameStr);
|
||||
nameData = nameStr.data();
|
||||
nameSize = nameStr.size();
|
||||
assert(nameSize == nameStr.size() && "name length truncated");
|
||||
}
|
||||
return StringRef(nameData, nameSize);
|
||||
}
|
||||
|
||||
InputFile *Symbol::getFile() {
|
||||
if (auto *sym = dyn_cast<DefinedCOFF>(this))
|
||||
return sym->file;
|
||||
if (auto *sym = dyn_cast<Lazy>(this))
|
||||
return sym->file;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Symbol::isLive() const {
|
||||
if (auto *r = dyn_cast<DefinedRegular>(this))
|
||||
return r->getChunk()->live;
|
||||
if (auto *imp = dyn_cast<DefinedImportData>(this))
|
||||
return imp->file->live;
|
||||
if (auto *imp = dyn_cast<DefinedImportThunk>(this))
|
||||
return imp->wrappedSym->file->thunkLive;
|
||||
// Assume any other kind of symbol is live.
|
||||
return true;
|
||||
}
|
||||
|
||||
// MinGW specific.
|
||||
void Symbol::replaceKeepingName(Symbol *other, size_t size) {
|
||||
StringRef origName = getName();
|
||||
memcpy(this, other, size);
|
||||
nameData = origName.data();
|
||||
nameSize = origName.size();
|
||||
}
|
||||
|
||||
COFFSymbolRef DefinedCOFF::getCOFFSymbol() {
|
||||
size_t symSize = cast<ObjFile>(file)->getCOFFObj()->getSymbolTableEntrySize();
|
||||
if (symSize == sizeof(coff_symbol16))
|
||||
return COFFSymbolRef(reinterpret_cast<const coff_symbol16 *>(sym));
|
||||
assert(symSize == sizeof(coff_symbol32));
|
||||
return COFFSymbolRef(reinterpret_cast<const coff_symbol32 *>(sym));
|
||||
}
|
||||
|
||||
uint16_t DefinedAbsolute::numOutputSections;
|
||||
|
||||
static Chunk *makeImportThunk(DefinedImportData *s, uint16_t machine) {
|
||||
if (machine == AMD64)
|
||||
return make<ImportThunkChunkX64>(s);
|
||||
if (machine == I386)
|
||||
return make<ImportThunkChunkX86>(s);
|
||||
if (machine == ARM64)
|
||||
return make<ImportThunkChunkARM64>(s);
|
||||
assert(machine == ARMNT);
|
||||
return make<ImportThunkChunkARM>(s);
|
||||
}
|
||||
|
||||
DefinedImportThunk::DefinedImportThunk(StringRef name, DefinedImportData *s,
|
||||
uint16_t machine)
|
||||
: Defined(DefinedImportThunkKind, name), wrappedSym(s),
|
||||
data(makeImportThunk(s, machine)) {}
|
||||
|
||||
Defined *Undefined::getWeakAlias() {
|
||||
// A weak alias may be a weak alias to another symbol, so check recursively.
|
||||
for (Symbol *a = weakAlias; a; a = cast<Undefined>(a)->weakAlias)
|
||||
if (auto *d = dyn_cast<Defined>(a))
|
||||
return d;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MemoryBufferRef Lazy::getMemberBuffer() {
|
||||
Archive::Child c =
|
||||
CHECK(sym.getMember(),
|
||||
"could not get the member for symbol " + toCOFFString(sym));
|
||||
return CHECK(c.getMemoryBufferRef(),
|
||||
"could not get the buffer for the member defining symbol " +
|
||||
toCOFFString(sym));
|
||||
}
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
444
deps/lld/COFF/Symbols.h
vendored
444
deps/lld/COFF/Symbols.h
vendored
@ -1,444 +0,0 @@
|
||||
//===- Symbols.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_SYMBOLS_H
|
||||
#define LLD_COFF_SYMBOLS_H
|
||||
|
||||
#include "Chunks.h"
|
||||
#include "Config.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/Object/Archive.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
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;
|
||||
using llvm::object::COFFSymbolRef;
|
||||
using llvm::object::coff_import_header;
|
||||
using llvm::object::coff_symbol_generic;
|
||||
|
||||
class ArchiveFile;
|
||||
class InputFile;
|
||||
class ObjFile;
|
||||
class SymbolTable;
|
||||
|
||||
// The base class for real symbol classes.
|
||||
class Symbol {
|
||||
public:
|
||||
enum Kind {
|
||||
// The order of these is significant. We start with the regular defined
|
||||
// symbols as those are the most prevalent and the zero tag is the cheapest
|
||||
// to set. Among the defined kinds, the lower the kind is preferred over
|
||||
// the higher kind when testing whether one symbol should take precedence
|
||||
// over another.
|
||||
DefinedRegularKind = 0,
|
||||
DefinedCommonKind,
|
||||
DefinedLocalImportKind,
|
||||
DefinedImportThunkKind,
|
||||
DefinedImportDataKind,
|
||||
DefinedAbsoluteKind,
|
||||
DefinedSyntheticKind,
|
||||
|
||||
UndefinedKind,
|
||||
LazyKind,
|
||||
|
||||
LastDefinedCOFFKind = DefinedCommonKind,
|
||||
LastDefinedKind = DefinedSyntheticKind,
|
||||
};
|
||||
|
||||
Kind kind() const { return static_cast<Kind>(symbolKind); }
|
||||
|
||||
// Returns the symbol name.
|
||||
StringRef getName();
|
||||
|
||||
void replaceKeepingName(Symbol *other, size_t size);
|
||||
|
||||
// Returns the file from which this symbol was created.
|
||||
InputFile *getFile();
|
||||
|
||||
// Indicates that this symbol will be included in the final image. Only valid
|
||||
// after calling markLive.
|
||||
bool isLive() const;
|
||||
|
||||
protected:
|
||||
friend SymbolTable;
|
||||
explicit Symbol(Kind k, StringRef n = "")
|
||||
: symbolKind(k), isExternal(true), isCOMDAT(false),
|
||||
writtenToSymtab(false), pendingArchiveLoad(false), isGCRoot(false),
|
||||
isRuntimePseudoReloc(false), nameSize(n.size()),
|
||||
nameData(n.empty() ? nullptr : n.data()) {}
|
||||
|
||||
const unsigned symbolKind : 8;
|
||||
unsigned isExternal : 1;
|
||||
|
||||
public:
|
||||
// This bit is used by the \c DefinedRegular subclass.
|
||||
unsigned isCOMDAT : 1;
|
||||
|
||||
// This bit is used by Writer::createSymbolAndStringTable() to prevent
|
||||
// symbols from being written to the symbol table more than once.
|
||||
unsigned writtenToSymtab : 1;
|
||||
|
||||
// True if this symbol was referenced by a regular (non-bitcode) object.
|
||||
unsigned isUsedInRegularObj : 1;
|
||||
|
||||
// True if we've seen both a lazy and an undefined symbol with this symbol
|
||||
// name, which means that we have enqueued an archive member load and should
|
||||
// not load any more archive members to resolve the same symbol.
|
||||
unsigned pendingArchiveLoad : 1;
|
||||
|
||||
/// True if we've already added this symbol to the list of GC roots.
|
||||
unsigned isGCRoot : 1;
|
||||
|
||||
unsigned isRuntimePseudoReloc : 1;
|
||||
|
||||
protected:
|
||||
// Symbol name length. Assume symbol lengths fit in a 32-bit integer.
|
||||
uint32_t nameSize;
|
||||
|
||||
const char *nameData;
|
||||
};
|
||||
|
||||
// The base class for any defined symbols, including absolute symbols,
|
||||
// etc.
|
||||
class Defined : public Symbol {
|
||||
public:
|
||||
Defined(Kind k, StringRef n) : Symbol(k, n) {}
|
||||
|
||||
static bool classof(const Symbol *s) { return s->kind() <= LastDefinedKind; }
|
||||
|
||||
// Returns the RVA (relative virtual address) of this symbol. The
|
||||
// writer sets and uses RVAs.
|
||||
uint64_t getRVA();
|
||||
|
||||
// Returns the chunk containing this symbol. Absolute symbols and __ImageBase
|
||||
// do not have chunks, so this may return null.
|
||||
Chunk *getChunk();
|
||||
};
|
||||
|
||||
// Symbols defined via a COFF object file or bitcode file. For COFF files, this
|
||||
// stores a coff_symbol_generic*, and names of internal symbols are lazily
|
||||
// loaded through that. For bitcode files, Sym is nullptr and the name is stored
|
||||
// as a decomposed StringRef.
|
||||
class DefinedCOFF : public Defined {
|
||||
friend Symbol;
|
||||
|
||||
public:
|
||||
DefinedCOFF(Kind k, InputFile *f, StringRef n, const coff_symbol_generic *s)
|
||||
: Defined(k, n), file(f), sym(s) {}
|
||||
|
||||
static bool classof(const Symbol *s) {
|
||||
return s->kind() <= LastDefinedCOFFKind;
|
||||
}
|
||||
|
||||
InputFile *getFile() { return file; }
|
||||
|
||||
COFFSymbolRef getCOFFSymbol();
|
||||
|
||||
InputFile *file;
|
||||
|
||||
protected:
|
||||
const coff_symbol_generic *sym;
|
||||
};
|
||||
|
||||
// Regular defined symbols read from object file symbol tables.
|
||||
class DefinedRegular : public DefinedCOFF {
|
||||
public:
|
||||
DefinedRegular(InputFile *f, StringRef n, bool isCOMDAT,
|
||||
bool isExternal = false,
|
||||
const coff_symbol_generic *s = nullptr,
|
||||
SectionChunk *c = nullptr)
|
||||
: DefinedCOFF(DefinedRegularKind, f, n, s), data(c ? &c->repl : nullptr) {
|
||||
this->isExternal = isExternal;
|
||||
this->isCOMDAT = isCOMDAT;
|
||||
}
|
||||
|
||||
static bool classof(const Symbol *s) {
|
||||
return s->kind() == DefinedRegularKind;
|
||||
}
|
||||
|
||||
uint64_t getRVA() const { return (*data)->getRVA() + sym->Value; }
|
||||
SectionChunk *getChunk() const { return *data; }
|
||||
uint32_t getValue() const { return sym->Value; }
|
||||
|
||||
SectionChunk **data;
|
||||
};
|
||||
|
||||
class DefinedCommon : public DefinedCOFF {
|
||||
public:
|
||||
DefinedCommon(InputFile *f, StringRef n, uint64_t size,
|
||||
const coff_symbol_generic *s = nullptr,
|
||||
CommonChunk *c = nullptr)
|
||||
: DefinedCOFF(DefinedCommonKind, f, n, s), data(c), size(size) {
|
||||
this->isExternal = true;
|
||||
}
|
||||
|
||||
static bool classof(const Symbol *s) {
|
||||
return s->kind() == DefinedCommonKind;
|
||||
}
|
||||
|
||||
uint64_t getRVA() { return data->getRVA(); }
|
||||
CommonChunk *getChunk() { return data; }
|
||||
|
||||
private:
|
||||
friend SymbolTable;
|
||||
uint64_t getSize() const { return size; }
|
||||
CommonChunk *data;
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
// Absolute symbols.
|
||||
class DefinedAbsolute : public Defined {
|
||||
public:
|
||||
DefinedAbsolute(StringRef n, COFFSymbolRef s)
|
||||
: Defined(DefinedAbsoluteKind, n), va(s.getValue()) {
|
||||
isExternal = s.isExternal();
|
||||
}
|
||||
|
||||
DefinedAbsolute(StringRef n, uint64_t v)
|
||||
: Defined(DefinedAbsoluteKind, n), va(v) {}
|
||||
|
||||
static bool classof(const Symbol *s) {
|
||||
return s->kind() == DefinedAbsoluteKind;
|
||||
}
|
||||
|
||||
uint64_t getRVA() { return va - config->imageBase; }
|
||||
void setVA(uint64_t v) { va = v; }
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
// This symbol is used for linker-synthesized symbols like __ImageBase and
|
||||
// __safe_se_handler_table.
|
||||
class DefinedSynthetic : public Defined {
|
||||
public:
|
||||
explicit DefinedSynthetic(StringRef name, Chunk *c)
|
||||
: Defined(DefinedSyntheticKind, name), c(c) {}
|
||||
|
||||
static bool classof(const Symbol *s) {
|
||||
return s->kind() == DefinedSyntheticKind;
|
||||
}
|
||||
|
||||
// A null chunk indicates that this is __ImageBase. Otherwise, this is some
|
||||
// other synthesized chunk, like SEHTableChunk.
|
||||
uint32_t getRVA() { return c ? c->getRVA() : 0; }
|
||||
Chunk *getChunk() { return c; }
|
||||
|
||||
private:
|
||||
Chunk *c;
|
||||
};
|
||||
|
||||
// This class represents a symbol defined in an archive file. It is
|
||||
// created from an archive file header, and it knows how to load an
|
||||
// object file from an archive to replace itself with a defined
|
||||
// symbol. If the resolver finds both Undefined and Lazy for
|
||||
// the same name, it will ask the Lazy to load a file.
|
||||
class Lazy : public Symbol {
|
||||
public:
|
||||
Lazy(ArchiveFile *f, const Archive::Symbol s)
|
||||
: Symbol(LazyKind, s.getName()), file(f), sym(s) {}
|
||||
|
||||
static bool classof(const Symbol *s) { return s->kind() == LazyKind; }
|
||||
|
||||
MemoryBufferRef getMemberBuffer();
|
||||
|
||||
ArchiveFile *file;
|
||||
|
||||
private:
|
||||
friend SymbolTable;
|
||||
|
||||
private:
|
||||
const Archive::Symbol sym;
|
||||
};
|
||||
|
||||
// Undefined symbols.
|
||||
class Undefined : public Symbol {
|
||||
public:
|
||||
explicit Undefined(StringRef n) : Symbol(UndefinedKind, n) {}
|
||||
|
||||
static bool classof(const Symbol *s) { return s->kind() == UndefinedKind; }
|
||||
|
||||
// An undefined symbol can have a fallback symbol which gives an
|
||||
// undefined symbol a second chance if it would remain undefined.
|
||||
// If it remains undefined, it'll be replaced with whatever the
|
||||
// Alias pointer points to.
|
||||
Symbol *weakAlias = nullptr;
|
||||
|
||||
// If this symbol is external weak, try to resolve it to a defined
|
||||
// symbol by searching the chain of fallback symbols. Returns the symbol if
|
||||
// successful, otherwise returns null.
|
||||
Defined *getWeakAlias();
|
||||
};
|
||||
|
||||
// Windows-specific classes.
|
||||
|
||||
// This class represents a symbol imported from a DLL. This has two
|
||||
// names for internal use and external use. The former is used for
|
||||
// name resolution, and the latter is used for the import descriptor
|
||||
// table in an output. The former has "__imp_" prefix.
|
||||
class DefinedImportData : public Defined {
|
||||
public:
|
||||
DefinedImportData(StringRef n, ImportFile *f)
|
||||
: Defined(DefinedImportDataKind, n), file(f) {
|
||||
}
|
||||
|
||||
static bool classof(const Symbol *s) {
|
||||
return s->kind() == DefinedImportDataKind;
|
||||
}
|
||||
|
||||
uint64_t getRVA() { return file->location->getRVA(); }
|
||||
Chunk *getChunk() { return file->location; }
|
||||
void setLocation(Chunk *addressTable) { file->location = addressTable; }
|
||||
|
||||
StringRef getDLLName() { return file->dllName; }
|
||||
StringRef getExternalName() { return file->externalName; }
|
||||
uint16_t getOrdinal() { return file->hdr->OrdinalHint; }
|
||||
|
||||
ImportFile *file;
|
||||
};
|
||||
|
||||
// This class represents a symbol for a jump table entry which jumps
|
||||
// to a function in a DLL. Linker are supposed to create such symbols
|
||||
// without "__imp_" prefix for all function symbols exported from
|
||||
// DLLs, so that you can call DLL functions as regular functions with
|
||||
// a regular name. A function pointer is given as a DefinedImportData.
|
||||
class DefinedImportThunk : public Defined {
|
||||
public:
|
||||
DefinedImportThunk(StringRef name, DefinedImportData *s, uint16_t machine);
|
||||
|
||||
static bool classof(const Symbol *s) {
|
||||
return s->kind() == DefinedImportThunkKind;
|
||||
}
|
||||
|
||||
uint64_t getRVA() { return data->getRVA(); }
|
||||
Chunk *getChunk() { return data; }
|
||||
|
||||
DefinedImportData *wrappedSym;
|
||||
|
||||
private:
|
||||
Chunk *data;
|
||||
};
|
||||
|
||||
// If you have a symbol "foo" in your object file, a symbol name
|
||||
// "__imp_foo" becomes automatically available as a pointer to "foo".
|
||||
// This class is for such automatically-created symbols.
|
||||
// Yes, this is an odd feature. We didn't intend to implement that.
|
||||
// This is here just for compatibility with MSVC.
|
||||
class DefinedLocalImport : public Defined {
|
||||
public:
|
||||
DefinedLocalImport(StringRef n, Defined *s)
|
||||
: Defined(DefinedLocalImportKind, n), data(make<LocalImportChunk>(s)) {}
|
||||
|
||||
static bool classof(const Symbol *s) {
|
||||
return s->kind() == DefinedLocalImportKind;
|
||||
}
|
||||
|
||||
uint64_t getRVA() { return data->getRVA(); }
|
||||
Chunk *getChunk() { return data; }
|
||||
|
||||
private:
|
||||
LocalImportChunk *data;
|
||||
};
|
||||
|
||||
inline uint64_t Defined::getRVA() {
|
||||
switch (kind()) {
|
||||
case DefinedAbsoluteKind:
|
||||
return cast<DefinedAbsolute>(this)->getRVA();
|
||||
case DefinedSyntheticKind:
|
||||
return cast<DefinedSynthetic>(this)->getRVA();
|
||||
case DefinedImportDataKind:
|
||||
return cast<DefinedImportData>(this)->getRVA();
|
||||
case DefinedImportThunkKind:
|
||||
return cast<DefinedImportThunk>(this)->getRVA();
|
||||
case DefinedLocalImportKind:
|
||||
return cast<DefinedLocalImport>(this)->getRVA();
|
||||
case DefinedCommonKind:
|
||||
return cast<DefinedCommon>(this)->getRVA();
|
||||
case DefinedRegularKind:
|
||||
return cast<DefinedRegular>(this)->getRVA();
|
||||
case LazyKind:
|
||||
case UndefinedKind:
|
||||
llvm_unreachable("Cannot get the address for an undefined symbol.");
|
||||
}
|
||||
llvm_unreachable("unknown symbol kind");
|
||||
}
|
||||
|
||||
inline Chunk *Defined::getChunk() {
|
||||
switch (kind()) {
|
||||
case DefinedRegularKind:
|
||||
return cast<DefinedRegular>(this)->getChunk();
|
||||
case DefinedAbsoluteKind:
|
||||
return nullptr;
|
||||
case DefinedSyntheticKind:
|
||||
return cast<DefinedSynthetic>(this)->getChunk();
|
||||
case DefinedImportDataKind:
|
||||
return cast<DefinedImportData>(this)->getChunk();
|
||||
case DefinedImportThunkKind:
|
||||
return cast<DefinedImportThunk>(this)->getChunk();
|
||||
case DefinedLocalImportKind:
|
||||
return cast<DefinedLocalImport>(this)->getChunk();
|
||||
case DefinedCommonKind:
|
||||
return cast<DefinedCommon>(this)->getChunk();
|
||||
case LazyKind:
|
||||
case UndefinedKind:
|
||||
llvm_unreachable("Cannot get the chunk of an undefined symbol.");
|
||||
}
|
||||
llvm_unreachable("unknown symbol kind");
|
||||
}
|
||||
|
||||
// A buffer class that is large enough to hold any Symbol-derived
|
||||
// object. We allocate memory using this class and instantiate a symbol
|
||||
// using the placement new.
|
||||
union SymbolUnion {
|
||||
alignas(DefinedRegular) char a[sizeof(DefinedRegular)];
|
||||
alignas(DefinedCommon) char b[sizeof(DefinedCommon)];
|
||||
alignas(DefinedAbsolute) char c[sizeof(DefinedAbsolute)];
|
||||
alignas(DefinedSynthetic) char d[sizeof(DefinedSynthetic)];
|
||||
alignas(Lazy) char e[sizeof(Lazy)];
|
||||
alignas(Undefined) char f[sizeof(Undefined)];
|
||||
alignas(DefinedImportData) char g[sizeof(DefinedImportData)];
|
||||
alignas(DefinedImportThunk) char h[sizeof(DefinedImportThunk)];
|
||||
alignas(DefinedLocalImport) char i[sizeof(DefinedLocalImport)];
|
||||
};
|
||||
|
||||
template <typename T, typename... ArgT>
|
||||
void replaceSymbol(Symbol *s, ArgT &&... arg) {
|
||||
static_assert(std::is_trivially_destructible<T>(),
|
||||
"Symbol types must be trivially destructible");
|
||||
static_assert(sizeof(T) <= sizeof(SymbolUnion), "Symbol too small");
|
||||
static_assert(alignof(T) <= alignof(SymbolUnion),
|
||||
"SymbolUnion not aligned enough");
|
||||
assert(static_cast<Symbol *>(static_cast<T *>(nullptr)) == nullptr &&
|
||||
"Not a Symbol");
|
||||
new (s) T(std::forward<ArgT>(arg)...);
|
||||
}
|
||||
} // namespace coff
|
||||
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
65
deps/lld/COFF/TypeMerger.h
vendored
65
deps/lld/COFF/TypeMerger.h
vendored
@ -1,65 +0,0 @@
|
||||
//===- TypeMerger.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_TYPEMERGER_H
|
||||
#define LLD_COFF_TYPEMERGER_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h"
|
||||
#include "llvm/DebugInfo/CodeView/MergingTypeTableBuilder.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
class TypeMerger {
|
||||
public:
|
||||
TypeMerger(llvm::BumpPtrAllocator &alloc)
|
||||
: typeTable(alloc), iDTable(alloc), globalTypeTable(alloc),
|
||||
globalIDTable(alloc) {}
|
||||
|
||||
/// Get the type table or the global type table if /DEBUG:GHASH is enabled.
|
||||
inline llvm::codeview::TypeCollection &getTypeTable() {
|
||||
if (config->debugGHashes)
|
||||
return globalTypeTable;
|
||||
return typeTable;
|
||||
}
|
||||
|
||||
/// Get the ID table or the global ID table if /DEBUG:GHASH is enabled.
|
||||
inline llvm::codeview::TypeCollection &getIDTable() {
|
||||
if (config->debugGHashes)
|
||||
return globalIDTable;
|
||||
return iDTable;
|
||||
}
|
||||
|
||||
/// Type records that will go into the PDB TPI stream.
|
||||
llvm::codeview::MergingTypeTableBuilder typeTable;
|
||||
|
||||
/// Item records that will go into the PDB IPI stream.
|
||||
llvm::codeview::MergingTypeTableBuilder iDTable;
|
||||
|
||||
/// Type records that will go into the PDB TPI stream (for /DEBUG:GHASH)
|
||||
llvm::codeview::GlobalTypeTableBuilder globalTypeTable;
|
||||
|
||||
/// Item records that will go into the PDB IPI stream (for /DEBUG:GHASH)
|
||||
llvm::codeview::GlobalTypeTableBuilder globalIDTable;
|
||||
};
|
||||
|
||||
/// Map from type index and item index in a type server PDB to the
|
||||
/// corresponding index in the destination PDB.
|
||||
struct CVIndexMap {
|
||||
llvm::SmallVector<llvm::codeview::TypeIndex, 0> tpiMap;
|
||||
llvm::SmallVector<llvm::codeview::TypeIndex, 0> ipiMap;
|
||||
bool isTypeServerMap = false;
|
||||
bool isPrecompiledTypeMap = false;
|
||||
};
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
1932
deps/lld/COFF/Writer.cpp
vendored
1932
deps/lld/COFF/Writer.cpp
vendored
File diff suppressed because it is too large
Load Diff
85
deps/lld/COFF/Writer.h
vendored
85
deps/lld/COFF/Writer.h
vendored
@ -1,85 +0,0 @@
|
||||
//===- Writer.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_WRITER_H
|
||||
#define LLD_COFF_WRITER_H
|
||||
|
||||
#include "Chunks.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
static const int pageSize = 4096;
|
||||
|
||||
void writeResult();
|
||||
|
||||
class PartialSection {
|
||||
public:
|
||||
PartialSection(StringRef n, uint32_t chars)
|
||||
: name(n), characteristics(chars) {}
|
||||
StringRef name;
|
||||
unsigned characteristics;
|
||||
std::vector<Chunk *> chunks;
|
||||
};
|
||||
|
||||
// OutputSection represents a section in an output file. It's a
|
||||
// container of chunks. OutputSection and Chunk are 1:N relationship.
|
||||
// Chunks cannot belong to more than one OutputSections. The writer
|
||||
// creates multiple OutputSections and assign them unique,
|
||||
// non-overlapping file offsets and RVAs.
|
||||
class OutputSection {
|
||||
public:
|
||||
OutputSection(llvm::StringRef n, uint32_t chars) : name(n) {
|
||||
header.Characteristics = chars;
|
||||
}
|
||||
void addChunk(Chunk *c);
|
||||
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);
|
||||
void addContributingPartialSection(PartialSection *sec);
|
||||
|
||||
// Returns the size of this section in an executable memory image.
|
||||
// This may be smaller than the raw size (the raw size is multiple
|
||||
// of disk sector size, so there may be padding at end), or may be
|
||||
// larger (if that's the case, the loader reserves spaces after end
|
||||
// of raw data).
|
||||
uint64_t getVirtualSize() { return header.VirtualSize; }
|
||||
|
||||
// Returns the size of the section in the output file.
|
||||
uint64_t getRawSize() { return header.SizeOfRawData; }
|
||||
|
||||
// Set offset into the string table storing this section name.
|
||||
// Used only when the name is longer than 8 bytes.
|
||||
void setStringTableOff(uint32_t v) { stringTableOff = v; }
|
||||
|
||||
// N.B. The section index is one based.
|
||||
uint32_t sectionIndex = 0;
|
||||
|
||||
llvm::StringRef name;
|
||||
llvm::object::coff_section header = {};
|
||||
|
||||
std::vector<Chunk *> chunks;
|
||||
std::vector<Chunk *> origChunks;
|
||||
|
||||
std::vector<PartialSection *> contribSections;
|
||||
|
||||
private:
|
||||
uint32_t stringTableOff = 0;
|
||||
};
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
82
deps/lld/Common/Args.cpp
vendored
82
deps/lld/Common/Args.cpp
vendored
@ -1,82 +0,0 @@
|
||||
//===- Args.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/Args.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Option/ArgList.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace lld;
|
||||
|
||||
// TODO(sbc): Remove this once CGOptLevel can be set completely based on bitcode
|
||||
// function metadata.
|
||||
CodeGenOpt::Level lld::args::getCGOptLevel(int optLevelLTO) {
|
||||
if (optLevelLTO == 3)
|
||||
return CodeGenOpt::Aggressive;
|
||||
assert(optLevelLTO < 3);
|
||||
return CodeGenOpt::Default;
|
||||
}
|
||||
|
||||
int64_t lld::args::getInteger(opt::InputArgList &args, unsigned key,
|
||||
int64_t Default) {
|
||||
auto *a = args.getLastArg(key);
|
||||
if (!a)
|
||||
return Default;
|
||||
|
||||
int64_t v;
|
||||
if (to_integer(a->getValue(), v, 10))
|
||||
return v;
|
||||
|
||||
StringRef spelling = args.getArgString(a->getIndex());
|
||||
error(spelling + ": number expected, but got '" + a->getValue() + "'");
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<StringRef> lld::args::getStrings(opt::InputArgList &args, int id) {
|
||||
std::vector<StringRef> v;
|
||||
for (auto *arg : args.filtered(id))
|
||||
v.push_back(arg->getValue());
|
||||
return v;
|
||||
}
|
||||
|
||||
uint64_t lld::args::getZOptionValue(opt::InputArgList &args, int id,
|
||||
StringRef key, uint64_t Default) {
|
||||
for (auto *arg : args.filtered_reverse(id)) {
|
||||
std::pair<StringRef, StringRef> kv = StringRef(arg->getValue()).split('=');
|
||||
if (kv.first == key) {
|
||||
uint64_t result = Default;
|
||||
if (!to_integer(kv.second, result))
|
||||
error("invalid " + key + ": " + kv.second);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return Default;
|
||||
}
|
||||
|
||||
std::vector<StringRef> lld::args::getLines(MemoryBufferRef mb) {
|
||||
SmallVector<StringRef, 0> arr;
|
||||
mb.getBuffer().split(arr, '\n');
|
||||
|
||||
std::vector<StringRef> ret;
|
||||
for (StringRef s : arr) {
|
||||
s = s.trim();
|
||||
if (!s.empty() && s[0] != '#')
|
||||
ret.push_back(s);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
StringRef lld::args::getFilenameWithoutExe(StringRef path) {
|
||||
if (path.endswith_lower(".exe"))
|
||||
return sys::path::stem(path);
|
||||
return sys::path::filename(path);
|
||||
}
|
||||
60
deps/lld/Common/CMakeLists.txt
vendored
60
deps/lld/Common/CMakeLists.txt
vendored
@ -1,60 +0,0 @@
|
||||
if(NOT LLD_BUILT_STANDALONE)
|
||||
set(tablegen_deps intrinsics_gen)
|
||||
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")
|
||||
|
||||
if(lld_vc)
|
||||
set(lld_source_dir ${LLD_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
add_custom_command(OUTPUT "${version_inc}"
|
||||
DEPENDS "${lld_vc}" "${generate_vcs_version_script}"
|
||||
COMMAND ${CMAKE_COMMAND} "-DNAMES=LLD"
|
||||
"-DLLD_SOURCE_DIR=${LLD_SOURCE_DIR}"
|
||||
"-DHEADER_FILE=${version_inc}"
|
||||
-P "${generate_vcs_version_script}")
|
||||
|
||||
# Mark the generated header as being generated.
|
||||
set_source_files_properties("${version_inc}"
|
||||
PROPERTIES GENERATED TRUE
|
||||
HEADER_FILE_ONLY TRUE)
|
||||
|
||||
set_property(SOURCE Version.cpp APPEND PROPERTY
|
||||
COMPILE_DEFINITIONS "HAVE_VCS_VERSION_INC")
|
||||
|
||||
add_lld_library(lldCommon
|
||||
Args.cpp
|
||||
ErrorHandler.cpp
|
||||
Filesystem.cpp
|
||||
Memory.cpp
|
||||
Reproduce.cpp
|
||||
Strings.cpp
|
||||
TargetOptionsCommandFlags.cpp
|
||||
Threads.cpp
|
||||
Timer.cpp
|
||||
VCSVersion.inc
|
||||
Version.cpp
|
||||
|
||||
ADDITIONAL_HEADER_DIRS
|
||||
${LLD_INCLUDE_DIR}/lld/Common
|
||||
|
||||
LINK_COMPONENTS
|
||||
Codegen
|
||||
Core
|
||||
Demangle
|
||||
MC
|
||||
Option
|
||||
Support
|
||||
Target
|
||||
|
||||
LINK_LIBS
|
||||
${LLVM_PTHREAD_LIB}
|
||||
|
||||
DEPENDS
|
||||
${tablegen_deps}
|
||||
)
|
||||
178
deps/lld/Common/ErrorHandler.cpp
vendored
178
deps/lld/Common/ErrorHandler.cpp
vendored
@ -1,178 +0,0 @@
|
||||
//===- ErrorHandler.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/ErrorHandler.h"
|
||||
|
||||
#include "lld/Common/Threads.h"
|
||||
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/IR/DiagnosticInfo.h"
|
||||
#include "llvm/IR/DiagnosticPrinter.h"
|
||||
#include "llvm/Support/ManagedStatic.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <mutex>
|
||||
#include <regex>
|
||||
|
||||
#if !defined(_MSC_VER) && !defined(__MINGW32__)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
using namespace llvm;
|
||||
using namespace lld;
|
||||
|
||||
// The functions defined in this file can be called from multiple threads,
|
||||
// but outs() or errs() are not thread-safe. We protect them using a mutex.
|
||||
static std::mutex mu;
|
||||
|
||||
// Prints "\n" or does nothing, depending on Msg contents of
|
||||
// the previous call of this function.
|
||||
static void newline(raw_ostream *errorOS, const Twine &msg) {
|
||||
// True if the previous error message contained "\n".
|
||||
// We want to separate multi-line error messages with a newline.
|
||||
static bool flag;
|
||||
|
||||
if (flag)
|
||||
*errorOS << "\n";
|
||||
flag = StringRef(msg.str()).contains('\n');
|
||||
}
|
||||
|
||||
ErrorHandler &lld::errorHandler() {
|
||||
static ErrorHandler handler;
|
||||
return handler;
|
||||
}
|
||||
|
||||
void lld::exitLld(int val) {
|
||||
// Delete any temporary file, while keeping the memory mapping open.
|
||||
if (errorHandler().outputBuffer)
|
||||
errorHandler().outputBuffer->discard();
|
||||
|
||||
// Dealloc/destroy ManagedStatic variables before calling
|
||||
// _exit(). In a non-LTO build, this is a nop. In an LTO
|
||||
// build allows us to get the output of -time-passes.
|
||||
llvm_shutdown();
|
||||
|
||||
outs().flush();
|
||||
errs().flush();
|
||||
_exit(val);
|
||||
}
|
||||
|
||||
void lld::diagnosticHandler(const DiagnosticInfo &di) {
|
||||
SmallString<128> s;
|
||||
raw_svector_ostream os(s);
|
||||
DiagnosticPrinterRawOStream dp(os);
|
||||
di.print(dp);
|
||||
switch (di.getSeverity()) {
|
||||
case DS_Error:
|
||||
error(s);
|
||||
break;
|
||||
case DS_Warning:
|
||||
warn(s);
|
||||
break;
|
||||
case DS_Remark:
|
||||
case DS_Note:
|
||||
message(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void lld::checkError(Error e) {
|
||||
handleAllErrors(std::move(e),
|
||||
[&](ErrorInfoBase &eib) { error(eib.message()); });
|
||||
}
|
||||
|
||||
static std::string getLocation(std::string msg, std::string defaultMsg) {
|
||||
static std::vector<std::regex> Regexes{
|
||||
std::regex(R"(^undefined symbol:.*\n>>> referenced by (\S+):(\d+)\n.*)"),
|
||||
std::regex(R"(^undefined symbol:.*\n>>> referenced by (.*):)"),
|
||||
std::regex(
|
||||
R"(^duplicate symbol: .*\n>>> defined in (\S+)\n>>> defined in.*)"),
|
||||
std::regex(
|
||||
R"(^duplicate symbol: .*\n>>> defined at (\S+):(\d+).*)"),
|
||||
std::regex(
|
||||
R"(.*\n>>> defined in .*\n>>> referenced by (\S+):(\d+))"),
|
||||
std::regex(
|
||||
R"(^undefined (internal|hidden|protected) symbol: .*\n>>> referenced by (\S+):(\d+)\n.*)"),
|
||||
std::regex(R"((\S+):(\d+): unclosed quote)"),
|
||||
};
|
||||
|
||||
std::smatch Match;
|
||||
for (std::regex &Re : Regexes) {
|
||||
if (std::regex_search(msg, Match, Re)) {
|
||||
return Match.size() > 2 ? Match.str(1) + "(" + Match.str(2) + ")"
|
||||
: Match.str(1);
|
||||
}
|
||||
}
|
||||
return defaultMsg;
|
||||
}
|
||||
|
||||
void ErrorHandler::printHeader(StringRef s, raw_ostream::Colors c,
|
||||
const Twine &msg) {
|
||||
|
||||
if (vsDiagnostics) {
|
||||
// A Visual Studio-style error message starts with an error location.
|
||||
// If a location cannot be extracted then we default to LogName.
|
||||
*errorOS << getLocation(msg.str(), logName) << ": ";
|
||||
} else {
|
||||
*errorOS << logName << ": ";
|
||||
}
|
||||
|
||||
if (colorDiagnostics) {
|
||||
errorOS->changeColor(c, true);
|
||||
*errorOS << s;
|
||||
errorOS->resetColor();
|
||||
} else {
|
||||
*errorOS << s;
|
||||
}
|
||||
}
|
||||
|
||||
void ErrorHandler::log(const Twine &msg) {
|
||||
if (verbose) {
|
||||
std::lock_guard<std::mutex> lock(mu);
|
||||
*errorOS << logName << ": " << msg << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void ErrorHandler::message(const Twine &msg) {
|
||||
std::lock_guard<std::mutex> lock(mu);
|
||||
outs() << msg << "\n";
|
||||
outs().flush();
|
||||
}
|
||||
|
||||
void ErrorHandler::warn(const Twine &msg) {
|
||||
if (fatalWarnings) {
|
||||
error(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(mu);
|
||||
newline(errorOS, msg);
|
||||
printHeader("warning: ", raw_ostream::MAGENTA, msg);
|
||||
*errorOS << msg << "\n";
|
||||
}
|
||||
|
||||
void ErrorHandler::error(const Twine &msg) {
|
||||
std::lock_guard<std::mutex> lock(mu);
|
||||
newline(errorOS, msg);
|
||||
|
||||
if (errorLimit == 0 || errorCount < errorLimit) {
|
||||
printHeader("error: ", raw_ostream::RED, msg);
|
||||
*errorOS << msg << "\n";
|
||||
} else if (errorCount == errorLimit) {
|
||||
printHeader("error: ", raw_ostream::RED, msg);
|
||||
*errorOS << errorLimitExceededMsg << "\n";
|
||||
if (exitEarly)
|
||||
exitLld(1);
|
||||
}
|
||||
|
||||
++errorCount;
|
||||
}
|
||||
|
||||
void ErrorHandler::fatal(const Twine &msg) {
|
||||
error(msg);
|
||||
exitLld(1);
|
||||
}
|
||||
99
deps/lld/Common/Filesystem.cpp
vendored
99
deps/lld/Common/Filesystem.cpp
vendored
@ -1,99 +0,0 @@
|
||||
//===- Filesystem.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 file contains a few utility functions to handle files.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lld/Common/Filesystem.h"
|
||||
#include "lld/Common/Threads.h"
|
||||
#include "llvm/Config/llvm-config.h"
|
||||
#include "llvm/Support/FileOutputBuffer.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#if LLVM_ON_UNIX
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <thread>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace lld;
|
||||
|
||||
// Removes a given file asynchronously. This is a performance hack,
|
||||
// so remove this when operating systems are improved.
|
||||
//
|
||||
// On Linux (and probably on other Unix-like systems), unlink(2) is a
|
||||
// noticeably slow system call. As of 2016, unlink takes 250
|
||||
// milliseconds to remove a 1 GB file on ext4 filesystem on my machine.
|
||||
//
|
||||
// To create a new result file, we first remove existing file. So, if
|
||||
// you repeatedly link a 1 GB program in a regular compile-link-debug
|
||||
// cycle, every cycle wastes 250 milliseconds only to remove a file.
|
||||
// Since LLD can link a 1 GB binary in about 5 seconds, that waste
|
||||
// actually counts.
|
||||
//
|
||||
// This function spawns a background thread to remove the file.
|
||||
// The calling thread returns almost immediately.
|
||||
void lld::unlinkAsync(StringRef path) {
|
||||
// Removing a file is async on windows.
|
||||
#if defined(_WIN32)
|
||||
sys::fs::remove(path);
|
||||
#else
|
||||
if (!threadsEnabled || !sys::fs::exists(path) ||
|
||||
!sys::fs::is_regular_file(path))
|
||||
return;
|
||||
|
||||
// We cannot just remove path from a different thread because we are now going
|
||||
// to create path as a new file.
|
||||
// Instead we open the file and unlink it on this thread. The unlink is fast
|
||||
// since the open fd guarantees that it is not removing the last reference.
|
||||
int fd;
|
||||
std::error_code ec = sys::fs::openFileForRead(path, fd);
|
||||
sys::fs::remove(path);
|
||||
|
||||
if (ec)
|
||||
return;
|
||||
|
||||
// close and therefore remove TempPath in background.
|
||||
std::mutex m;
|
||||
std::condition_variable cv;
|
||||
bool started = false;
|
||||
std::thread([&, fd] {
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m);
|
||||
started = true;
|
||||
cv.notify_all();
|
||||
}
|
||||
::close(fd);
|
||||
}).detach();
|
||||
|
||||
// GLIBC 2.26 and earlier have race condition that crashes an entire process
|
||||
// if the main thread calls exit(2) while other thread is starting up.
|
||||
std::unique_lock<std::mutex> l(m);
|
||||
cv.wait(l, [&] { return started; });
|
||||
#endif
|
||||
}
|
||||
|
||||
// Simulate file creation to see if Path is writable.
|
||||
//
|
||||
// Determining whether a file is writable or not is amazingly hard,
|
||||
// and after all the only reliable way of doing that is to actually
|
||||
// create a file. But we don't want to do that in this function
|
||||
// because LLD shouldn't update any file if it will end in a failure.
|
||||
// We also don't want to reimplement heuristics to determine if a
|
||||
// file is writable. So we'll let FileOutputBuffer do the work.
|
||||
//
|
||||
// FileOutputBuffer doesn't touch a desitnation file until commit()
|
||||
// is called. We use that class without calling commit() to predict
|
||||
// if the given file is writable.
|
||||
std::error_code lld::tryCreateFile(StringRef path) {
|
||||
if (path.empty())
|
||||
return std::error_code();
|
||||
if (path == "-")
|
||||
return std::error_code();
|
||||
return errorToErrorCode(FileOutputBuffer::create(path, 1).takeError());
|
||||
}
|
||||
22
deps/lld/Common/Memory.cpp
vendored
22
deps/lld/Common/Memory.cpp
vendored
@ -1,22 +0,0 @@
|
||||
//===- Memory.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/Memory.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();
|
||||
}
|
||||
61
deps/lld/Common/Reproduce.cpp
vendored
61
deps/lld/Common/Reproduce.cpp
vendored
@ -1,61 +0,0 @@
|
||||
//===- Reproduce.cpp - Utilities for creating reproducers -----------------===//
|
||||
//
|
||||
// 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/Reproduce.h"
|
||||
#include "llvm/Option/Arg.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
|
||||
using namespace lld;
|
||||
using namespace llvm;
|
||||
using namespace llvm::sys;
|
||||
|
||||
// Makes a given pathname an absolute path first, and then remove
|
||||
// beginning /. For example, "../foo.o" is converted to "home/john/foo.o",
|
||||
// assuming that the current directory is "/home/john/bar".
|
||||
// Returned string is a forward slash separated path even on Windows to avoid
|
||||
// a mess with backslash-as-escape and backslash-as-path-separator.
|
||||
std::string lld::relativeToRoot(StringRef path) {
|
||||
SmallString<128> abs = path;
|
||||
if (fs::make_absolute(abs))
|
||||
return path;
|
||||
path::remove_dots(abs, /*remove_dot_dot=*/true);
|
||||
|
||||
// This is Windows specific. root_name() returns a drive letter
|
||||
// (e.g. "c:") or a UNC name (//net). We want to keep it as part
|
||||
// of the result.
|
||||
SmallString<128> res;
|
||||
StringRef root = path::root_name(abs);
|
||||
if (root.endswith(":"))
|
||||
res = root.drop_back();
|
||||
else if (root.startswith("//"))
|
||||
res = root.substr(2);
|
||||
|
||||
path::append(res, path::relative_path(abs));
|
||||
return path::convert_to_slash(res);
|
||||
}
|
||||
|
||||
// Quote a given string if it contains a space character.
|
||||
std::string lld::quote(StringRef s) {
|
||||
if (s.contains(' '))
|
||||
return ("\"" + s + "\"").str();
|
||||
return s;
|
||||
}
|
||||
|
||||
// Converts an Arg to a string representation suitable for a response file.
|
||||
// To show an Arg in a diagnostic, use Arg::getAsString() instead.
|
||||
std::string lld::toString(const opt::Arg &arg) {
|
||||
std::string k = arg.getSpelling();
|
||||
if (arg.getNumValues() == 0)
|
||||
return k;
|
||||
std::string v = quote(arg.getValue());
|
||||
if (arg.getOption().getRenderStyle() == opt::Option::RenderJoinedStyle)
|
||||
return k + v;
|
||||
return k + " " + v;
|
||||
}
|
||||
103
deps/lld/Common/Strings.cpp
vendored
103
deps/lld/Common/Strings.cpp
vendored
@ -1,103 +0,0 @@
|
||||
//===- Strings.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/Strings.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "llvm/Demangle/Demangle.h"
|
||||
#include "llvm/Support/GlobPattern.h"
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace lld;
|
||||
|
||||
// Returns the demangled C++ symbol name for Name.
|
||||
Optional<std::string> lld::demangleItanium(StringRef name) {
|
||||
// itaniumDemangle can be used to demangle strings other than symbol
|
||||
// names which do not necessarily start with "_Z". Name can be
|
||||
// either a C or C++ symbol. Don't call itaniumDemangle if the name
|
||||
// does not look like a C++ symbol name to avoid getting unexpected
|
||||
// result for a C symbol that happens to match a mangled type name.
|
||||
if (!name.startswith("_Z"))
|
||||
return None;
|
||||
|
||||
char *buf = itaniumDemangle(name.str().c_str(), nullptr, nullptr, nullptr);
|
||||
if (!buf)
|
||||
return None;
|
||||
std::string s(buf);
|
||||
free(buf);
|
||||
return s;
|
||||
}
|
||||
|
||||
Optional<std::string> lld::demangleMSVC(StringRef name) {
|
||||
std::string prefix;
|
||||
if (name.consume_front("__imp_"))
|
||||
prefix = "__declspec(dllimport) ";
|
||||
|
||||
// Demangle only C++ names.
|
||||
if (!name.startswith("?"))
|
||||
return None;
|
||||
|
||||
char *buf = microsoftDemangle(name.str().c_str(), nullptr, nullptr, nullptr);
|
||||
if (!buf)
|
||||
return None;
|
||||
std::string s(buf);
|
||||
free(buf);
|
||||
return prefix + s;
|
||||
}
|
||||
|
||||
StringMatcher::StringMatcher(ArrayRef<StringRef> pat) {
|
||||
for (StringRef s : pat) {
|
||||
Expected<GlobPattern> pat = GlobPattern::create(s);
|
||||
if (!pat)
|
||||
error(toString(pat.takeError()));
|
||||
else
|
||||
patterns.push_back(*pat);
|
||||
}
|
||||
}
|
||||
|
||||
bool StringMatcher::match(StringRef s) const {
|
||||
for (const GlobPattern &pat : patterns)
|
||||
if (pat.match(s))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Converts a hex string (e.g. "deadbeef") to a vector.
|
||||
std::vector<uint8_t> lld::parseHex(StringRef s) {
|
||||
std::vector<uint8_t> hex;
|
||||
while (!s.empty()) {
|
||||
StringRef b = s.substr(0, 2);
|
||||
s = s.substr(2);
|
||||
uint8_t h;
|
||||
if (!to_integer(b, h, 16)) {
|
||||
error("not a hexadecimal value: " + b);
|
||||
return {};
|
||||
}
|
||||
hex.push_back(h);
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
|
||||
// Returns true if S is valid as a C language identifier.
|
||||
bool lld::isValidCIdentifier(StringRef s) {
|
||||
return !s.empty() && (isAlpha(s[0]) || s[0] == '_') &&
|
||||
std::all_of(s.begin() + 1, s.end(),
|
||||
[](char c) { return c == '_' || isAlnum(c); });
|
||||
}
|
||||
|
||||
// Write the contents of the a buffer to a file
|
||||
void lld::saveBuffer(StringRef buffer, const Twine &path) {
|
||||
std::error_code ec;
|
||||
raw_fd_ostream os(path.str(), ec, sys::fs::OpenFlags::F_None);
|
||||
if (ec)
|
||||
error("cannot create " + path + ": " + ec.message());
|
||||
os << buffer;
|
||||
}
|
||||
35
deps/lld/Common/TargetOptionsCommandFlags.cpp
vendored
35
deps/lld/Common/TargetOptionsCommandFlags.cpp
vendored
@ -1,35 +0,0 @@
|
||||
//===-- TargetOptionsCommandFlags.cpp ---------------------------*- 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file exists as a place for global variables defined in LLVM's
|
||||
// CodeGen/CommandFlags.inc. By putting the resulting object file in
|
||||
// an archive and linking with it, the definitions will automatically be
|
||||
// included when needed and skipped when already present.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lld/Common/TargetOptionsCommandFlags.h"
|
||||
|
||||
#include "llvm/CodeGen/CommandFlags.inc"
|
||||
#include "llvm/Target/TargetOptions.h"
|
||||
|
||||
// Define an externally visible version of
|
||||
// initTargetOptionsFromCodeGenFlags, so that its functionality can be
|
||||
// used without having to include llvm/CodeGen/CommandFlags.inc, which
|
||||
// would lead to multiple definitions of the command line flags.
|
||||
llvm::TargetOptions lld::initTargetOptionsFromCodeGenFlags() {
|
||||
return ::InitTargetOptionsFromCodeGenFlags();
|
||||
}
|
||||
|
||||
llvm::Optional<llvm::CodeModel::Model> lld::getCodeModelFromCMModel() {
|
||||
return getCodeModel();
|
||||
}
|
||||
|
||||
std::string lld::getCPUStr() { return ::getCPUStr(); }
|
||||
|
||||
std::vector<std::string> lld::getMAttrs() { return ::MAttrs; }
|
||||
11
deps/lld/Common/Threads.cpp
vendored
11
deps/lld/Common/Threads.cpp
vendored
@ -1,11 +0,0 @@
|
||||
//===- Threads.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/Threads.h"
|
||||
|
||||
bool lld::threadsEnabled = true;
|
||||
79
deps/lld/Common/Timer.cpp
vendored
79
deps/lld/Common/Timer.cpp
vendored
@ -1,79 +0,0 @@
|
||||
//===- Timer.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/Timer.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Support/Format.h"
|
||||
|
||||
using namespace lld;
|
||||
using namespace llvm;
|
||||
|
||||
ScopedTimer::ScopedTimer(Timer &t) : t(&t) { t.start(); }
|
||||
|
||||
void ScopedTimer::stop() {
|
||||
if (!t)
|
||||
return;
|
||||
t->stop();
|
||||
t = nullptr;
|
||||
}
|
||||
|
||||
ScopedTimer::~ScopedTimer() { stop(); }
|
||||
|
||||
Timer::Timer(llvm::StringRef name) : name(name), parent(nullptr) {}
|
||||
Timer::Timer(llvm::StringRef name, Timer &parent)
|
||||
: name(name), parent(&parent) {}
|
||||
|
||||
void Timer::start() {
|
||||
if (parent && total.count() == 0)
|
||||
parent->children.push_back(this);
|
||||
startTime = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
void Timer::stop() {
|
||||
total += (std::chrono::high_resolution_clock::now() - startTime);
|
||||
}
|
||||
|
||||
Timer &Timer::root() {
|
||||
static Timer rootTimer("Total Link Time");
|
||||
return rootTimer;
|
||||
}
|
||||
|
||||
void Timer::print() {
|
||||
double totalDuration = static_cast<double>(root().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.
|
||||
for (const auto &child : children)
|
||||
child->print(1, totalDuration);
|
||||
|
||||
message(std::string(49, '-'));
|
||||
|
||||
root().print(0, root().millis(), false);
|
||||
}
|
||||
|
||||
double Timer::millis() const {
|
||||
return std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(
|
||||
total)
|
||||
.count();
|
||||
}
|
||||
|
||||
void Timer::print(int depth, double totalDuration, bool recurse) const {
|
||||
double p = 100.0 * millis() / totalDuration;
|
||||
|
||||
SmallString<32> str;
|
||||
llvm::raw_svector_ostream stream(str);
|
||||
std::string s = std::string(depth * 2, ' ') + name + std::string(":");
|
||||
stream << format("%-30s%5d ms (%5.1f%%)", s.c_str(), (int)millis(), p);
|
||||
|
||||
message(str);
|
||||
|
||||
if (recurse) {
|
||||
for (const auto &child : children)
|
||||
child->print(depth + 1, totalDuration);
|
||||
}
|
||||
}
|
||||
27
deps/lld/Common/Version.cpp
vendored
27
deps/lld/Common/Version.cpp
vendored
@ -1,27 +0,0 @@
|
||||
//===- lib/Common/Version.cpp - LLD Version Number ---------------*- 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines several version-related utility functions for LLD.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lld/Common/Version.h"
|
||||
|
||||
#ifdef HAVE_VCS_VERSION_INC
|
||||
#include "VCSVersion.inc"
|
||||
#endif
|
||||
|
||||
// Returns a version string, e.g.:
|
||||
// lld 9.0.0 (https://github.com/llvm/llvm-project.git 9efdd7ac5e914d3c9fa1ef)
|
||||
std::string lld::getLLDVersion() {
|
||||
#if defined(LLD_REPOSITORY) && defined(LLD_REVISION)
|
||||
return "LLD " LLD_VERSION_STRING " (" LLD_REPOSITORY " " LLD_REVISION ")";
|
||||
#else
|
||||
return "LLD " LLD_VERSION_STRING;
|
||||
#endif
|
||||
}
|
||||
651
deps/lld/ELF/AArch64ErrataFix.cpp
vendored
651
deps/lld/ELF/AArch64ErrataFix.cpp
vendored
@ -1,651 +0,0 @@
|
||||
//===- AArch64ErrataFix.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 file implements Section Patching for the purpose of working around
|
||||
// errata in CPUs. The general principle is that an erratum sequence of one or
|
||||
// more instructions is detected in the instruction stream, one of the
|
||||
// instructions in the sequence is replaced with a branch to a patch sequence
|
||||
// of replacement instructions. At the end of the replacement sequence the
|
||||
// patch branches back to the instruction stream.
|
||||
|
||||
// This technique is only suitable for fixing an erratum when:
|
||||
// - There is a set of necessary conditions required to trigger the erratum that
|
||||
// can be detected at static link time.
|
||||
// - There is a set of replacement instructions that can be used to remove at
|
||||
// least one of the necessary conditions that trigger the erratum.
|
||||
// - We can overwrite an instruction in the erratum sequence with a branch to
|
||||
// the replacement sequence.
|
||||
// - We can place the replacement sequence within range of the branch.
|
||||
|
||||
// FIXME:
|
||||
// - The implementation here only supports one patch, the AArch64 Cortex-53
|
||||
// errata 843419 that affects r0p0, r0p1, r0p2 and r0p4 versions of the core.
|
||||
// To keep the initial version simple there is no support for multiple
|
||||
// architectures or selection of different patches.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "AArch64ErrataFix.h"
|
||||
#include "Config.h"
|
||||
#include "LinkerScript.h"
|
||||
#include "OutputSections.h"
|
||||
#include "Relocations.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "lld/Common/Strings.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::ELF;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support;
|
||||
using namespace llvm::support::endian;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
// Helper functions to identify instructions and conditions needed to trigger
|
||||
// the Cortex-A53-843419 erratum.
|
||||
|
||||
// ADRP
|
||||
// | 1 | immlo (2) | 1 | 0 0 0 0 | immhi (19) | Rd (5) |
|
||||
static bool isADRP(uint32_t instr) {
|
||||
return (instr & 0x9f000000) == 0x90000000;
|
||||
}
|
||||
|
||||
// Load and store bit patterns from ARMv8-A ARM ARM.
|
||||
// Instructions appear in order of appearance starting from table in
|
||||
// C4.1.3 Loads and Stores.
|
||||
|
||||
// All loads and stores have 1 (at bit postion 27), (0 at bit position 25).
|
||||
// | op0 x op1 (2) | 1 op2 0 op3 (2) | x | op4 (5) | xxxx | op5 (2) | x (10) |
|
||||
static bool isLoadStoreClass(uint32_t instr) {
|
||||
return (instr & 0x0a000000) == 0x08000000;
|
||||
}
|
||||
|
||||
// LDN/STN multiple no offset
|
||||
// | 0 Q 00 | 1100 | 0 L 00 | 0000 | opcode (4) | size (2) | Rn (5) | Rt (5) |
|
||||
// LDN/STN multiple post-indexed
|
||||
// | 0 Q 00 | 1100 | 1 L 0 | Rm (5)| opcode (4) | size (2) | Rn (5) | Rt (5) |
|
||||
// L == 0 for stores.
|
||||
|
||||
// Utility routine to decode opcode field of LDN/STN multiple structure
|
||||
// instructions to find the ST1 instructions.
|
||||
// opcode == 0010 ST1 4 registers.
|
||||
// opcode == 0110 ST1 3 registers.
|
||||
// opcode == 0111 ST1 1 register.
|
||||
// opcode == 1010 ST1 2 registers.
|
||||
static bool isST1MultipleOpcode(uint32_t instr) {
|
||||
return (instr & 0x0000f000) == 0x00002000 ||
|
||||
(instr & 0x0000f000) == 0x00006000 ||
|
||||
(instr & 0x0000f000) == 0x00007000 ||
|
||||
(instr & 0x0000f000) == 0x0000a000;
|
||||
}
|
||||
|
||||
static bool isST1Multiple(uint32_t instr) {
|
||||
return (instr & 0xbfff0000) == 0x0c000000 && isST1MultipleOpcode(instr);
|
||||
}
|
||||
|
||||
// Writes to Rn (writeback).
|
||||
static bool isST1MultiplePost(uint32_t instr) {
|
||||
return (instr & 0xbfe00000) == 0x0c800000 && isST1MultipleOpcode(instr);
|
||||
}
|
||||
|
||||
// LDN/STN single no offset
|
||||
// | 0 Q 00 | 1101 | 0 L R 0 | 0000 | opc (3) S | size (2) | Rn (5) | Rt (5)|
|
||||
// LDN/STN single post-indexed
|
||||
// | 0 Q 00 | 1101 | 1 L R | Rm (5) | opc (3) S | size (2) | Rn (5) | Rt (5)|
|
||||
// L == 0 for stores
|
||||
|
||||
// Utility routine to decode opcode field of LDN/STN single structure
|
||||
// instructions to find the ST1 instructions.
|
||||
// R == 0 for ST1 and ST3, R == 1 for ST2 and ST4.
|
||||
// opcode == 000 ST1 8-bit.
|
||||
// opcode == 010 ST1 16-bit.
|
||||
// opcode == 100 ST1 32 or 64-bit (Size determines which).
|
||||
static bool isST1SingleOpcode(uint32_t instr) {
|
||||
return (instr & 0x0040e000) == 0x00000000 ||
|
||||
(instr & 0x0040e000) == 0x00004000 ||
|
||||
(instr & 0x0040e000) == 0x00008000;
|
||||
}
|
||||
|
||||
static bool isST1Single(uint32_t instr) {
|
||||
return (instr & 0xbfff0000) == 0x0d000000 && isST1SingleOpcode(instr);
|
||||
}
|
||||
|
||||
// Writes to Rn (writeback).
|
||||
static bool isST1SinglePost(uint32_t instr) {
|
||||
return (instr & 0xbfe00000) == 0x0d800000 && isST1SingleOpcode(instr);
|
||||
}
|
||||
|
||||
static bool isST1(uint32_t instr) {
|
||||
return isST1Multiple(instr) || isST1MultiplePost(instr) ||
|
||||
isST1Single(instr) || isST1SinglePost(instr);
|
||||
}
|
||||
|
||||
// Load/store exclusive
|
||||
// | size (2) 00 | 1000 | o2 L o1 | Rs (5) | o0 | Rt2 (5) | Rn (5) | Rt (5) |
|
||||
// L == 0 for Stores.
|
||||
static bool isLoadStoreExclusive(uint32_t instr) {
|
||||
return (instr & 0x3f000000) == 0x08000000;
|
||||
}
|
||||
|
||||
static bool isLoadExclusive(uint32_t instr) {
|
||||
return (instr & 0x3f400000) == 0x08400000;
|
||||
}
|
||||
|
||||
// Load register literal
|
||||
// | opc (2) 01 | 1 V 00 | imm19 | Rt (5) |
|
||||
static bool isLoadLiteral(uint32_t instr) {
|
||||
return (instr & 0x3b000000) == 0x18000000;
|
||||
}
|
||||
|
||||
// Load/store no-allocate pair
|
||||
// (offset)
|
||||
// | opc (2) 10 | 1 V 00 | 0 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) |
|
||||
// L == 0 for stores.
|
||||
// Never writes to register
|
||||
static bool isSTNP(uint32_t instr) {
|
||||
return (instr & 0x3bc00000) == 0x28000000;
|
||||
}
|
||||
|
||||
// Load/store register pair
|
||||
// (post-indexed)
|
||||
// | opc (2) 10 | 1 V 00 | 1 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) |
|
||||
// L == 0 for stores, V == 0 for Scalar, V == 1 for Simd/FP
|
||||
// Writes to Rn.
|
||||
static bool isSTPPost(uint32_t instr) {
|
||||
return (instr & 0x3bc00000) == 0x28800000;
|
||||
}
|
||||
|
||||
// (offset)
|
||||
// | opc (2) 10 | 1 V 01 | 0 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) |
|
||||
static bool isSTPOffset(uint32_t instr) {
|
||||
return (instr & 0x3bc00000) == 0x29000000;
|
||||
}
|
||||
|
||||
// (pre-index)
|
||||
// | opc (2) 10 | 1 V 01 | 1 L | imm7 | Rt2 (5) | Rn (5) | Rt (5) |
|
||||
// Writes to Rn.
|
||||
static bool isSTPPre(uint32_t instr) {
|
||||
return (instr & 0x3bc00000) == 0x29800000;
|
||||
}
|
||||
|
||||
static bool isSTP(uint32_t instr) {
|
||||
return isSTPPost(instr) || isSTPOffset(instr) || isSTPPre(instr);
|
||||
}
|
||||
|
||||
// Load/store register (unscaled immediate)
|
||||
// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 00 | Rn (5) | Rt (5) |
|
||||
// V == 0 for Scalar, V == 1 for Simd/FP.
|
||||
static bool isLoadStoreUnscaled(uint32_t instr) {
|
||||
return (instr & 0x3b000c00) == 0x38000000;
|
||||
}
|
||||
|
||||
// Load/store register (immediate post-indexed)
|
||||
// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 01 | Rn (5) | Rt (5) |
|
||||
static bool isLoadStoreImmediatePost(uint32_t instr) {
|
||||
return (instr & 0x3b200c00) == 0x38000400;
|
||||
}
|
||||
|
||||
// Load/store register (unprivileged)
|
||||
// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 10 | Rn (5) | Rt (5) |
|
||||
static bool isLoadStoreUnpriv(uint32_t instr) {
|
||||
return (instr & 0x3b200c00) == 0x38000800;
|
||||
}
|
||||
|
||||
// Load/store register (immediate pre-indexed)
|
||||
// | size (2) 11 | 1 V 00 | opc (2) 0 | imm9 | 11 | Rn (5) | Rt (5) |
|
||||
static bool isLoadStoreImmediatePre(uint32_t instr) {
|
||||
return (instr & 0x3b200c00) == 0x38000c00;
|
||||
}
|
||||
|
||||
// Load/store register (register offset)
|
||||
// | size (2) 11 | 1 V 00 | opc (2) 1 | Rm (5) | option (3) S | 10 | Rn | Rt |
|
||||
static bool isLoadStoreRegisterOff(uint32_t instr) {
|
||||
return (instr & 0x3b200c00) == 0x38200800;
|
||||
}
|
||||
|
||||
// Load/store register (unsigned immediate)
|
||||
// | size (2) 11 | 1 V 01 | opc (2) | imm12 | Rn (5) | Rt (5) |
|
||||
static bool isLoadStoreRegisterUnsigned(uint32_t instr) {
|
||||
return (instr & 0x3b000000) == 0x39000000;
|
||||
}
|
||||
|
||||
// Rt is always in bit position 0 - 4.
|
||||
static uint32_t getRt(uint32_t instr) { return (instr & 0x1f); }
|
||||
|
||||
// Rn is always in bit position 5 - 9.
|
||||
static uint32_t getRn(uint32_t instr) { return (instr >> 5) & 0x1f; }
|
||||
|
||||
// C4.1.2 Branches, Exception Generating and System instructions
|
||||
// | op0 (3) 1 | 01 op1 (4) | x (22) |
|
||||
// op0 == 010 101 op1 == 0xxx Conditional Branch.
|
||||
// op0 == 110 101 op1 == 1xxx Unconditional Branch Register.
|
||||
// op0 == x00 101 op1 == xxxx Unconditional Branch immediate.
|
||||
// op0 == x01 101 op1 == 0xxx Compare and branch immediate.
|
||||
// op0 == x01 101 op1 == 1xxx Test and branch immediate.
|
||||
static bool isBranch(uint32_t instr) {
|
||||
return ((instr & 0xfe000000) == 0xd6000000) || // Cond branch.
|
||||
((instr & 0xfe000000) == 0x54000000) || // Uncond branch reg.
|
||||
((instr & 0x7c000000) == 0x14000000) || // Uncond branch imm.
|
||||
((instr & 0x7c000000) == 0x34000000); // Compare and test branch.
|
||||
}
|
||||
|
||||
static bool isV8SingleRegisterNonStructureLoadStore(uint32_t instr) {
|
||||
return isLoadStoreUnscaled(instr) || isLoadStoreImmediatePost(instr) ||
|
||||
isLoadStoreUnpriv(instr) || isLoadStoreImmediatePre(instr) ||
|
||||
isLoadStoreRegisterOff(instr) || isLoadStoreRegisterUnsigned(instr);
|
||||
}
|
||||
|
||||
// Note that this function refers to v8.0 only and does not include the
|
||||
// additional load and store instructions added for in later revisions of
|
||||
// the architecture such as the Atomic memory operations introduced
|
||||
// in v8.1.
|
||||
static bool isV8NonStructureLoad(uint32_t instr) {
|
||||
if (isLoadExclusive(instr))
|
||||
return true;
|
||||
if (isLoadLiteral(instr))
|
||||
return true;
|
||||
else if (isV8SingleRegisterNonStructureLoadStore(instr)) {
|
||||
// For Load and Store single register, Loads are derived from a
|
||||
// combination of the Size, V and Opc fields.
|
||||
uint32_t size = (instr >> 30) & 0xff;
|
||||
uint32_t v = (instr >> 26) & 0x1;
|
||||
uint32_t opc = (instr >> 22) & 0x3;
|
||||
// For the load and store instructions that we are decoding.
|
||||
// Opc == 0 are all stores.
|
||||
// Opc == 1 with a couple of exceptions are loads. The exceptions are:
|
||||
// Size == 00 (0), V == 1, Opc == 10 (2) which is a store and
|
||||
// Size == 11 (3), V == 0, Opc == 10 (2) which is a prefetch.
|
||||
return opc != 0 && !(size == 0 && v == 1 && opc == 2) &&
|
||||
!(size == 3 && v == 0 && opc == 2);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// The following decode instructions are only complete up to the instructions
|
||||
// needed for errata 843419.
|
||||
|
||||
// Instruction with writeback updates the index register after the load/store.
|
||||
static bool hasWriteback(uint32_t instr) {
|
||||
return isLoadStoreImmediatePre(instr) || isLoadStoreImmediatePost(instr) ||
|
||||
isSTPPre(instr) || isSTPPost(instr) || isST1SinglePost(instr) ||
|
||||
isST1MultiplePost(instr);
|
||||
}
|
||||
|
||||
// For the load and store class of instructions, a load can write to the
|
||||
// destination register, a load and a store can write to the base register when
|
||||
// the instruction has writeback.
|
||||
static bool doesLoadStoreWriteToReg(uint32_t instr, uint32_t reg) {
|
||||
return (isV8NonStructureLoad(instr) && getRt(instr) == reg) ||
|
||||
(hasWriteback(instr) && getRn(instr) == reg);
|
||||
}
|
||||
|
||||
// Scanner for Cortex-A53 errata 843419
|
||||
// Full details are available in the Cortex A53 MPCore revision 0 Software
|
||||
// Developers Errata Notice (ARM-EPM-048406).
|
||||
//
|
||||
// The instruction sequence that triggers the erratum is common in compiled
|
||||
// AArch64 code, however it is sensitive to the offset of the sequence within
|
||||
// a 4k page. This means that by scanning and fixing the patch after we have
|
||||
// assigned addresses we only need to disassemble and fix instances of the
|
||||
// sequence in the range of affected offsets.
|
||||
//
|
||||
// In summary the erratum conditions are a series of 4 instructions:
|
||||
// 1.) An ADRP instruction that writes to register Rn with low 12 bits of
|
||||
// address of instruction either 0xff8 or 0xffc.
|
||||
// 2.) A load or store instruction that can be:
|
||||
// - A single register load or store, of either integer or vector registers.
|
||||
// - An STP or STNP, of either integer or vector registers.
|
||||
// - An Advanced SIMD ST1 store instruction.
|
||||
// - Must not write to Rn, but may optionally read from it.
|
||||
// 3.) An optional instruction that is not a branch and does not write to Rn.
|
||||
// 4.) A load or store from the Load/store register (unsigned immediate) class
|
||||
// that uses Rn as the base address register.
|
||||
//
|
||||
// Note that we do not attempt to scan for Sequence 2 as described in the
|
||||
// Software Developers Errata Notice as this has been assessed to be extremely
|
||||
// unlikely to occur in compiled code. This matches gold and ld.bfd behavior.
|
||||
|
||||
// Return true if the Instruction sequence Adrp, Instr2, and Instr4 match
|
||||
// the erratum sequence. The Adrp, Instr2 and Instr4 correspond to 1.), 2.),
|
||||
// and 4.) in the Scanner for Cortex-A53 errata comment above.
|
||||
static bool is843419ErratumSequence(uint32_t instr1, uint32_t instr2,
|
||||
uint32_t instr4) {
|
||||
if (!isADRP(instr1))
|
||||
return false;
|
||||
|
||||
uint32_t rn = getRt(instr1);
|
||||
return isLoadStoreClass(instr2) &&
|
||||
(isLoadStoreExclusive(instr2) || isLoadLiteral(instr2) ||
|
||||
isV8SingleRegisterNonStructureLoadStore(instr2) || isSTP(instr2) ||
|
||||
isSTNP(instr2) || isST1(instr2)) &&
|
||||
!doesLoadStoreWriteToReg(instr2, rn) &&
|
||||
isLoadStoreRegisterUnsigned(instr4) && getRn(instr4) == rn;
|
||||
}
|
||||
|
||||
// Scan the instruction sequence starting at Offset Off from the base of
|
||||
// InputSection IS. We update Off in this function rather than in the caller as
|
||||
// we can skip ahead much further into the section when we know how many
|
||||
// instructions we've scanned.
|
||||
// Return the offset of the load or store instruction in IS that we want to
|
||||
// patch or 0 if no patch required.
|
||||
static uint64_t scanCortexA53Errata843419(InputSection *isec, uint64_t &off,
|
||||
uint64_t limit) {
|
||||
uint64_t isecAddr = isec->getVA(0);
|
||||
|
||||
// Advance Off so that (ISAddr + Off) modulo 0x1000 is at least 0xff8.
|
||||
uint64_t initialPageOff = (isecAddr + off) & 0xfff;
|
||||
if (initialPageOff < 0xff8)
|
||||
off += 0xff8 - initialPageOff;
|
||||
|
||||
bool optionalAllowed = limit - off > 12;
|
||||
if (off >= limit || limit - off < 12) {
|
||||
// Need at least 3 4-byte sized instructions to trigger erratum.
|
||||
off = limit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t patchOff = 0;
|
||||
const uint8_t *buf = isec->data().begin();
|
||||
const ulittle32_t *instBuf = reinterpret_cast<const ulittle32_t *>(buf + off);
|
||||
uint32_t instr1 = *instBuf++;
|
||||
uint32_t instr2 = *instBuf++;
|
||||
uint32_t instr3 = *instBuf++;
|
||||
if (is843419ErratumSequence(instr1, instr2, instr3)) {
|
||||
patchOff = off + 8;
|
||||
} else if (optionalAllowed && !isBranch(instr3)) {
|
||||
uint32_t instr4 = *instBuf++;
|
||||
if (is843419ErratumSequence(instr1, instr2, instr4))
|
||||
patchOff = off + 12;
|
||||
}
|
||||
if (((isecAddr + off) & 0xfff) == 0xff8)
|
||||
off += 4;
|
||||
else
|
||||
off += 0xffc;
|
||||
return patchOff;
|
||||
}
|
||||
|
||||
class lld::elf::Patch843419Section : public SyntheticSection {
|
||||
public:
|
||||
Patch843419Section(InputSection *p, uint64_t off);
|
||||
|
||||
void writeTo(uint8_t *buf) override;
|
||||
|
||||
size_t getSize() const override { return 8; }
|
||||
|
||||
uint64_t getLDSTAddr() const;
|
||||
|
||||
// The Section we are patching.
|
||||
const InputSection *patchee;
|
||||
// The offset of the instruction in the Patchee section we are patching.
|
||||
uint64_t patcheeOffset;
|
||||
// A label for the start of the Patch that we can use as a relocation target.
|
||||
Symbol *patchSym;
|
||||
};
|
||||
|
||||
lld::elf::Patch843419Section::Patch843419Section(InputSection *p, uint64_t off)
|
||||
: SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 4,
|
||||
".text.patch"),
|
||||
patchee(p), patcheeOffset(off) {
|
||||
this->parent = p->getParent();
|
||||
patchSym = addSyntheticLocal(
|
||||
saver.save("__CortexA53843419_" + utohexstr(getLDSTAddr())), STT_FUNC, 0,
|
||||
getSize(), *this);
|
||||
addSyntheticLocal(saver.save("$x"), STT_NOTYPE, 0, 0, *this);
|
||||
}
|
||||
|
||||
uint64_t lld::elf::Patch843419Section::getLDSTAddr() const {
|
||||
return patchee->getVA(patcheeOffset);
|
||||
}
|
||||
|
||||
void lld::elf::Patch843419Section::writeTo(uint8_t *buf) {
|
||||
// Copy the instruction that we will be replacing with a branch in the
|
||||
// Patchee Section.
|
||||
write32le(buf, read32le(patchee->data().begin() + patcheeOffset));
|
||||
|
||||
// Apply any relocation transferred from the original PatcheeSection.
|
||||
// For a SyntheticSection Buf already has outSecOff added, but relocateAlloc
|
||||
// also adds outSecOff so we need to subtract to avoid double counting.
|
||||
this->relocateAlloc(buf - outSecOff, buf - outSecOff + getSize());
|
||||
|
||||
// Return address is the next instruction after the one we have just copied.
|
||||
uint64_t s = getLDSTAddr() + 4;
|
||||
uint64_t p = patchSym->getVA() + 4;
|
||||
target->relocateOne(buf + 4, R_AARCH64_JUMP26, s - p);
|
||||
}
|
||||
|
||||
void AArch64Err843419Patcher::init() {
|
||||
// The AArch64 ABI permits data in executable sections. We must avoid scanning
|
||||
// this data as if it were instructions to avoid false matches. We use the
|
||||
// mapping symbols in the InputObjects to identify this data, caching the
|
||||
// results in sectionMap so we don't have to recalculate it each pass.
|
||||
|
||||
// The ABI Section 4.5.4 Mapping symbols; defines local symbols that describe
|
||||
// half open intervals [Symbol Value, Next Symbol Value) of code and data
|
||||
// within sections. If there is no next symbol then the half open interval is
|
||||
// [Symbol Value, End of section). The type, code or data, is determined by
|
||||
// the mapping symbol name, $x for code, $d for data.
|
||||
auto isCodeMapSymbol = [](const Symbol *b) {
|
||||
return b->getName() == "$x" || b->getName().startswith("$x.");
|
||||
};
|
||||
auto isDataMapSymbol = [](const Symbol *b) {
|
||||
return b->getName() == "$d" || b->getName().startswith("$d.");
|
||||
};
|
||||
|
||||
// Collect mapping symbols for every executable InputSection.
|
||||
for (InputFile *file : objectFiles) {
|
||||
auto *f = cast<ObjFile<ELF64LE>>(file);
|
||||
for (Symbol *b : f->getLocalSymbols()) {
|
||||
auto *def = dyn_cast<Defined>(b);
|
||||
if (!def)
|
||||
continue;
|
||||
if (!isCodeMapSymbol(def) && !isDataMapSymbol(def))
|
||||
continue;
|
||||
if (auto *sec = dyn_cast_or_null<InputSection>(def->section))
|
||||
if (sec->flags & SHF_EXECINSTR)
|
||||
sectionMap[sec].push_back(def);
|
||||
}
|
||||
}
|
||||
// For each InputSection make sure the mapping symbols are in sorted in
|
||||
// ascending order and free from consecutive runs of mapping symbols with
|
||||
// the same type. For example we must remove the redundant $d.1 from $x.0
|
||||
// $d.0 $d.1 $x.1.
|
||||
for (auto &kv : sectionMap) {
|
||||
std::vector<const Defined *> &mapSyms = kv.second;
|
||||
if (mapSyms.size() <= 1)
|
||||
continue;
|
||||
llvm::stable_sort(mapSyms, [](const Defined *a, const Defined *b) {
|
||||
return a->value < b->value;
|
||||
});
|
||||
mapSyms.erase(
|
||||
std::unique(mapSyms.begin(), mapSyms.end(),
|
||||
[=](const Defined *a, const Defined *b) {
|
||||
return (isCodeMapSymbol(a) && isCodeMapSymbol(b)) ||
|
||||
(isDataMapSymbol(a) && isDataMapSymbol(b));
|
||||
}),
|
||||
mapSyms.end());
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
// Insert the PatchSections we have created back into the
|
||||
// InputSectionDescription. As inserting patches alters the addresses of
|
||||
// InputSections that follow them, we try and place the patches after all the
|
||||
// executable sections, although we may need to insert them earlier if the
|
||||
// InputSectionDescription is larger than the maximum branch range.
|
||||
void AArch64Err843419Patcher::insertPatches(
|
||||
InputSectionDescription &isd, std::vector<Patch843419Section *> &patches) {
|
||||
uint64_t isecLimit;
|
||||
uint64_t prevIsecLimit = isd.sections.front()->outSecOff;
|
||||
uint64_t patchUpperBound = prevIsecLimit + target->getThunkSectionSpacing();
|
||||
uint64_t outSecAddr = isd.sections.front()->getParent()->addr;
|
||||
|
||||
// Set the outSecOff of patches to the place where we want to insert them.
|
||||
// We use a similar strategy to Thunk placement. Place patches roughly
|
||||
// every multiple of maximum branch range.
|
||||
auto patchIt = patches.begin();
|
||||
auto patchEnd = patches.end();
|
||||
for (const InputSection *isec : isd.sections) {
|
||||
isecLimit = isec->outSecOff + isec->getSize();
|
||||
if (isecLimit > patchUpperBound) {
|
||||
while (patchIt != patchEnd) {
|
||||
if ((*patchIt)->getLDSTAddr() - outSecAddr >= prevIsecLimit)
|
||||
break;
|
||||
(*patchIt)->outSecOff = prevIsecLimit;
|
||||
++patchIt;
|
||||
}
|
||||
patchUpperBound = prevIsecLimit + target->getThunkSectionSpacing();
|
||||
}
|
||||
prevIsecLimit = isecLimit;
|
||||
}
|
||||
for (; patchIt != patchEnd; ++patchIt) {
|
||||
(*patchIt)->outSecOff = isecLimit;
|
||||
}
|
||||
|
||||
// merge all patch sections. We use the outSecOff assigned above to
|
||||
// determine the insertion point. This is ok as we only merge into an
|
||||
// InputSectionDescription once per pass, and at the end of the pass
|
||||
// assignAddresses() will recalculate all the outSecOff values.
|
||||
std::vector<InputSection *> tmp;
|
||||
tmp.reserve(isd.sections.size() + patches.size());
|
||||
auto mergeCmp = [](const InputSection *a, const InputSection *b) {
|
||||
if (a->outSecOff < b->outSecOff)
|
||||
return true;
|
||||
if (a->outSecOff == b->outSecOff && isa<Patch843419Section>(a) &&
|
||||
!isa<Patch843419Section>(b))
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
std::merge(isd.sections.begin(), isd.sections.end(), patches.begin(),
|
||||
patches.end(), std::back_inserter(tmp), mergeCmp);
|
||||
isd.sections = std::move(tmp);
|
||||
}
|
||||
|
||||
// Given an erratum sequence that starts at address adrpAddr, with an
|
||||
// instruction that we need to patch at patcheeOffset from the start of
|
||||
// InputSection IS, create a Patch843419 Section and add it to the
|
||||
// Patches that we need to insert.
|
||||
static void implementPatch(uint64_t adrpAddr, uint64_t patcheeOffset,
|
||||
InputSection *isec,
|
||||
std::vector<Patch843419Section *> &patches) {
|
||||
// There may be a relocation at the same offset that we are patching. There
|
||||
// are four cases that we need to consider.
|
||||
// Case 1: R_AARCH64_JUMP26 branch relocation. We have already patched this
|
||||
// instance of the erratum on a previous patch and altered the relocation. We
|
||||
// have nothing more to do.
|
||||
// Case 2: A TLS Relaxation R_RELAX_TLS_IE_TO_LE. In this case the ADRP that
|
||||
// we read will be transformed into a MOVZ later so we actually don't match
|
||||
// the sequence and have nothing more to do.
|
||||
// Case 3: A load/store register (unsigned immediate) class relocation. There
|
||||
// are two of these R_AARCH_LD64_ABS_LO12_NC and R_AARCH_LD64_GOT_LO12_NC and
|
||||
// they are both absolute. We need to add the same relocation to the patch,
|
||||
// and replace the relocation with a R_AARCH_JUMP26 branch relocation.
|
||||
// Case 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) {
|
||||
return r.offset == patcheeOffset;
|
||||
});
|
||||
if (relIt != isec->relocations.end() &&
|
||||
(relIt->type == R_AARCH64_JUMP26 || relIt->expr == R_RELAX_TLS_IE_TO_LE))
|
||||
return;
|
||||
|
||||
log("detected cortex-a53-843419 erratum sequence starting at " +
|
||||
utohexstr(adrpAddr) + " in unpatched output.");
|
||||
|
||||
auto *ps = make<Patch843419Section>(isec, patcheeOffset);
|
||||
patches.push_back(ps);
|
||||
|
||||
auto makeRelToPatch = [](uint64_t offset, Symbol *patchSym) {
|
||||
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});
|
||||
*relIt = makeRelToPatch(patcheeOffset, ps->patchSym);
|
||||
} else
|
||||
isec->relocations.push_back(makeRelToPatch(patcheeOffset, ps->patchSym));
|
||||
}
|
||||
|
||||
// Scan all the instructions in InputSectionDescription, for each instance of
|
||||
// the erratum sequence create a Patch843419Section. We return the list of
|
||||
// Patch843419Sections that need to be applied to ISD.
|
||||
std::vector<Patch843419Section *>
|
||||
AArch64Err843419Patcher::patchInputSectionDescription(
|
||||
InputSectionDescription &isd) {
|
||||
std::vector<Patch843419Section *> patches;
|
||||
for (InputSection *isec : isd.sections) {
|
||||
// LLD doesn't use the erratum sequence in SyntheticSections.
|
||||
if (isa<SyntheticSection>(isec))
|
||||
continue;
|
||||
// Use sectionMap to make sure we only scan code and not inline data.
|
||||
// We have already sorted MapSyms in ascending order and removed consecutive
|
||||
// mapping symbols of the same type. Our range of executable instructions to
|
||||
// scan is therefore [codeSym->value, dataSym->value) or [codeSym->value,
|
||||
// section size).
|
||||
std::vector<const Defined *> &mapSyms = sectionMap[isec];
|
||||
|
||||
auto codeSym = llvm::find_if(mapSyms, [&](const Defined *ms) {
|
||||
return ms->getName().startswith("$x");
|
||||
});
|
||||
|
||||
while (codeSym != mapSyms.end()) {
|
||||
auto dataSym = std::next(codeSym);
|
||||
uint64_t off = (*codeSym)->value;
|
||||
uint64_t limit =
|
||||
(dataSym == mapSyms.end()) ? isec->data().size() : (*dataSym)->value;
|
||||
|
||||
while (off < limit) {
|
||||
uint64_t startAddr = isec->getVA(off);
|
||||
if (uint64_t patcheeOffset = scanCortexA53Errata843419(isec, off, limit))
|
||||
implementPatch(startAddr, patcheeOffset, isec, patches);
|
||||
}
|
||||
if (dataSym == mapSyms.end())
|
||||
break;
|
||||
codeSym = std::next(dataSym);
|
||||
}
|
||||
}
|
||||
return patches;
|
||||
}
|
||||
|
||||
// For each InputSectionDescription make one pass over the executable sections
|
||||
// looking for the erratum sequence; creating a synthetic Patch843419Section
|
||||
// for each instance found. We insert these synthetic patch sections after the
|
||||
// executable code in each InputSectionDescription.
|
||||
//
|
||||
// PreConditions:
|
||||
// The Output and Input Sections have had their final addresses assigned.
|
||||
//
|
||||
// PostConditions:
|
||||
// Returns true if at least one patch was added. The addresses of the
|
||||
// Ouptut and Input Sections may have been changed.
|
||||
// Returns false if no patches were required and no changes were made.
|
||||
bool AArch64Err843419Patcher::createFixes() {
|
||||
if (initialized == false)
|
||||
init();
|
||||
|
||||
bool addressesChanged = false;
|
||||
for (OutputSection *os : outputSections) {
|
||||
if (!(os->flags & SHF_ALLOC) || !(os->flags & SHF_EXECINSTR))
|
||||
continue;
|
||||
for (BaseCommand *bc : os->sectionCommands)
|
||||
if (auto *isd = dyn_cast<InputSectionDescription>(bc)) {
|
||||
std::vector<Patch843419Section *> patches =
|
||||
patchInputSectionDescription(*isd);
|
||||
if (!patches.empty()) {
|
||||
insertPatches(*isd, patches);
|
||||
addressesChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return addressesChanged;
|
||||
}
|
||||
50
deps/lld/ELF/AArch64ErrataFix.h
vendored
50
deps/lld/ELF/AArch64ErrataFix.h
vendored
@ -1,50 +0,0 @@
|
||||
//===- AArch64ErrataFix.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_ELF_AARCH64ERRATAFIX_H
|
||||
#define LLD_ELF_AARCH64ERRATAFIX_H
|
||||
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
class Defined;
|
||||
class InputSection;
|
||||
struct InputSectionDescription;
|
||||
class OutputSection;
|
||||
class Patch843419Section;
|
||||
|
||||
class AArch64Err843419Patcher {
|
||||
public:
|
||||
// return true if Patches have been added to the OutputSections.
|
||||
bool createFixes();
|
||||
|
||||
private:
|
||||
std::vector<Patch843419Section *>
|
||||
patchInputSectionDescription(InputSectionDescription &isd);
|
||||
|
||||
void insertPatches(InputSectionDescription &isd,
|
||||
std::vector<Patch843419Section *> &patches);
|
||||
|
||||
void init();
|
||||
|
||||
// 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;
|
||||
|
||||
bool initialized = false;
|
||||
};
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
590
deps/lld/ELF/Arch/AArch64.cpp
vendored
590
deps/lld/ELF/Arch/AArch64.cpp
vendored
@ -1,590 +0,0 @@
|
||||
//===- AArch64.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 "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "Thunks.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
// Page(Expr) is the page address of the expression Expr, defined
|
||||
// as (Expr & ~0xFFF). (This applies even if the machine page size
|
||||
// supported by the platform has a different value.)
|
||||
uint64_t elf::getAArch64Page(uint64_t expr) {
|
||||
return expr & ~static_cast<uint64_t>(0xFFF);
|
||||
}
|
||||
|
||||
namespace {
|
||||
class AArch64 : public TargetInfo {
|
||||
public:
|
||||
AArch64();
|
||||
RelExpr getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const override;
|
||||
RelType getDynRel(RelType type) const override;
|
||||
void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
|
||||
void writePltHeader(uint8_t *buf) const override;
|
||||
void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
|
||||
int32_t index, unsigned relOff) const override;
|
||||
bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
|
||||
uint64_t branchAddr, const Symbol &s) const override;
|
||||
uint32_t getThunkSectionSpacing() const override;
|
||||
bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
|
||||
bool usesOnlyLowPageBits(RelType type) const override;
|
||||
void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
RelExpr adjustRelaxExpr(RelType type, const uint8_t *data,
|
||||
RelExpr expr) const override;
|
||||
void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
AArch64::AArch64() {
|
||||
copyRel = R_AARCH64_COPY;
|
||||
relativeRel = R_AARCH64_RELATIVE;
|
||||
iRelativeRel = R_AARCH64_IRELATIVE;
|
||||
gotRel = R_AARCH64_GLOB_DAT;
|
||||
noneRel = R_AARCH64_NONE;
|
||||
pltRel = R_AARCH64_JUMP_SLOT;
|
||||
symbolicRel = R_AARCH64_ABS64;
|
||||
tlsDescRel = R_AARCH64_TLSDESC;
|
||||
tlsGotRel = R_AARCH64_TLS_TPREL64;
|
||||
pltEntrySize = 16;
|
||||
pltHeaderSize = 32;
|
||||
defaultMaxPageSize = 65536;
|
||||
|
||||
// Align to the 2 MiB page size (known as a superpage or huge page).
|
||||
// FreeBSD automatically promotes 2 MiB-aligned allocations.
|
||||
defaultImageBase = 0x200000;
|
||||
|
||||
needsThunks = true;
|
||||
}
|
||||
|
||||
RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const {
|
||||
switch (type) {
|
||||
case R_AARCH64_TLSDESC_ADR_PAGE21:
|
||||
return R_AARCH64_TLSDESC_PAGE;
|
||||
case R_AARCH64_TLSDESC_LD64_LO12:
|
||||
case R_AARCH64_TLSDESC_ADD_LO12:
|
||||
return R_TLSDESC;
|
||||
case R_AARCH64_TLSDESC_CALL:
|
||||
return R_TLSDESC_CALL;
|
||||
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
|
||||
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
|
||||
case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
|
||||
case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
|
||||
case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
|
||||
case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
|
||||
case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC:
|
||||
return R_TLS;
|
||||
case R_AARCH64_CALL26:
|
||||
case R_AARCH64_CONDBR19:
|
||||
case R_AARCH64_JUMP26:
|
||||
case R_AARCH64_TSTBR14:
|
||||
return R_PLT_PC;
|
||||
case R_AARCH64_PREL16:
|
||||
case R_AARCH64_PREL32:
|
||||
case R_AARCH64_PREL64:
|
||||
case R_AARCH64_ADR_PREL_LO21:
|
||||
case R_AARCH64_LD_PREL_LO19:
|
||||
return R_PC;
|
||||
case R_AARCH64_ADR_PREL_PG_HI21:
|
||||
case R_AARCH64_ADR_PREL_PG_HI21_NC:
|
||||
return R_AARCH64_PAGE_PC;
|
||||
case R_AARCH64_LD64_GOT_LO12_NC:
|
||||
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
|
||||
return R_GOT;
|
||||
case R_AARCH64_ADR_GOT_PAGE:
|
||||
case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
|
||||
return R_AARCH64_GOT_PAGE_PC;
|
||||
case R_AARCH64_NONE:
|
||||
return R_NONE;
|
||||
default:
|
||||
return R_ABS;
|
||||
}
|
||||
}
|
||||
|
||||
RelExpr AArch64::adjustRelaxExpr(RelType type, const uint8_t *data,
|
||||
RelExpr expr) const {
|
||||
if (expr == R_RELAX_TLS_GD_TO_IE) {
|
||||
if (type == R_AARCH64_TLSDESC_ADR_PAGE21)
|
||||
return R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC;
|
||||
return R_RELAX_TLS_GD_TO_IE_ABS;
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
bool AArch64::usesOnlyLowPageBits(RelType type) const {
|
||||
switch (type) {
|
||||
default:
|
||||
return false;
|
||||
case R_AARCH64_ADD_ABS_LO12_NC:
|
||||
case R_AARCH64_LD64_GOT_LO12_NC:
|
||||
case R_AARCH64_LDST128_ABS_LO12_NC:
|
||||
case R_AARCH64_LDST16_ABS_LO12_NC:
|
||||
case R_AARCH64_LDST32_ABS_LO12_NC:
|
||||
case R_AARCH64_LDST64_ABS_LO12_NC:
|
||||
case R_AARCH64_LDST8_ABS_LO12_NC:
|
||||
case R_AARCH64_TLSDESC_ADD_LO12:
|
||||
case R_AARCH64_TLSDESC_LD64_LO12:
|
||||
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
RelType AArch64::getDynRel(RelType type) const {
|
||||
if (type == R_AARCH64_ABS64)
|
||||
return type;
|
||||
return R_AARCH64_NONE;
|
||||
}
|
||||
|
||||
void AArch64::writeGotPlt(uint8_t *buf, const Symbol &) const {
|
||||
write64le(buf, in.plt->getVA());
|
||||
}
|
||||
|
||||
void AArch64::writePltHeader(uint8_t *buf) const {
|
||||
const uint8_t pltData[] = {
|
||||
0xf0, 0x7b, 0xbf, 0xa9, // stp x16, x30, [sp,#-16]!
|
||||
0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[2]))
|
||||
0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[2]))]
|
||||
0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[2]))
|
||||
0x20, 0x02, 0x1f, 0xd6, // br x17
|
||||
0x1f, 0x20, 0x03, 0xd5, // nop
|
||||
0x1f, 0x20, 0x03, 0xd5, // nop
|
||||
0x1f, 0x20, 0x03, 0xd5 // nop
|
||||
};
|
||||
memcpy(buf, pltData, sizeof(pltData));
|
||||
|
||||
uint64_t got = in.gotPlt->getVA();
|
||||
uint64_t plt = in.plt->getVA();
|
||||
relocateOne(buf + 4, R_AARCH64_ADR_PREL_PG_HI21,
|
||||
getAArch64Page(got + 16) - getAArch64Page(plt + 4));
|
||||
relocateOne(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16);
|
||||
relocateOne(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16);
|
||||
}
|
||||
|
||||
void AArch64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
|
||||
uint64_t pltEntryAddr, int32_t index,
|
||||
unsigned relOff) const {
|
||||
const uint8_t inst[] = {
|
||||
0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[n]))
|
||||
0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[n]))]
|
||||
0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[n]))
|
||||
0x20, 0x02, 0x1f, 0xd6 // br x17
|
||||
};
|
||||
memcpy(buf, inst, sizeof(inst));
|
||||
|
||||
relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21,
|
||||
getAArch64Page(gotPltEntryAddr) - getAArch64Page(pltEntryAddr));
|
||||
relocateOne(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr);
|
||||
relocateOne(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr);
|
||||
}
|
||||
|
||||
bool AArch64::needsThunk(RelExpr expr, RelType type, const InputFile *file,
|
||||
uint64_t branchAddr, const Symbol &s) const {
|
||||
// ELF for the ARM 64-bit architecture, section Call and Jump relocations
|
||||
// only permits range extension thunks for R_AARCH64_CALL26 and
|
||||
// R_AARCH64_JUMP26 relocation types.
|
||||
if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26)
|
||||
return false;
|
||||
uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA();
|
||||
return !inBranchRange(type, branchAddr, dst);
|
||||
}
|
||||
|
||||
uint32_t AArch64::getThunkSectionSpacing() const {
|
||||
// See comment in Arch/ARM.cpp for a more detailed explanation of
|
||||
// getThunkSectionSpacing(). For AArch64 the only branches we are permitted to
|
||||
// Thunk have a range of +/- 128 MiB
|
||||
return (128 * 1024 * 1024) - 0x30000;
|
||||
}
|
||||
|
||||
bool AArch64::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
|
||||
if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26)
|
||||
return true;
|
||||
// The AArch64 call and unconditional branch instructions have a range of
|
||||
// +/- 128 MiB.
|
||||
uint64_t range = 128 * 1024 * 1024;
|
||||
if (dst > src) {
|
||||
// Immediate of branch is signed.
|
||||
range -= 4;
|
||||
return dst - src <= range;
|
||||
}
|
||||
return src - dst <= range;
|
||||
}
|
||||
|
||||
static void write32AArch64Addr(uint8_t *l, uint64_t imm) {
|
||||
uint32_t immLo = (imm & 0x3) << 29;
|
||||
uint32_t immHi = (imm & 0x1FFFFC) << 3;
|
||||
uint64_t mask = (0x3 << 29) | (0x1FFFFC << 3);
|
||||
write32le(l, (read32le(l) & ~mask) | immLo | immHi);
|
||||
}
|
||||
|
||||
// Return the bits [Start, End] from Val shifted Start bits.
|
||||
// For instance, getBits(0xF0, 4, 8) returns 0xF.
|
||||
static uint64_t getBits(uint64_t val, int start, int end) {
|
||||
uint64_t mask = ((uint64_t)1 << (end + 1 - start)) - 1;
|
||||
return (val >> start) & mask;
|
||||
}
|
||||
|
||||
static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); }
|
||||
|
||||
// Update the immediate field in a AARCH64 ldr, str, and add instruction.
|
||||
static void or32AArch64Imm(uint8_t *l, uint64_t imm) {
|
||||
or32le(l, (imm & 0xFFF) << 10);
|
||||
}
|
||||
|
||||
void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
switch (type) {
|
||||
case R_AARCH64_ABS16:
|
||||
case R_AARCH64_PREL16:
|
||||
checkIntUInt(loc, val, 16, type);
|
||||
write16le(loc, val);
|
||||
break;
|
||||
case R_AARCH64_ABS32:
|
||||
case R_AARCH64_PREL32:
|
||||
checkIntUInt(loc, val, 32, type);
|
||||
write32le(loc, val);
|
||||
break;
|
||||
case R_AARCH64_ABS64:
|
||||
case R_AARCH64_PREL64:
|
||||
write64le(loc, val);
|
||||
break;
|
||||
case R_AARCH64_ADD_ABS_LO12_NC:
|
||||
or32AArch64Imm(loc, val);
|
||||
break;
|
||||
case R_AARCH64_ADR_GOT_PAGE:
|
||||
case R_AARCH64_ADR_PREL_PG_HI21:
|
||||
case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
|
||||
case R_AARCH64_TLSDESC_ADR_PAGE21:
|
||||
checkInt(loc, val, 33, type);
|
||||
LLVM_FALLTHROUGH;
|
||||
case R_AARCH64_ADR_PREL_PG_HI21_NC:
|
||||
write32AArch64Addr(loc, val >> 12);
|
||||
break;
|
||||
case R_AARCH64_ADR_PREL_LO21:
|
||||
checkInt(loc, val, 21, type);
|
||||
write32AArch64Addr(loc, val);
|
||||
break;
|
||||
case R_AARCH64_JUMP26:
|
||||
// Normally we would just write the bits of the immediate field, however
|
||||
// when patching instructions for the cpu errata fix -fix-cortex-a53-843419
|
||||
// we want to replace a non-branch instruction with a branch immediate
|
||||
// instruction. By writing all the bits of the instruction including the
|
||||
// opcode and the immediate (0 001 | 01 imm26) we can do this
|
||||
// transformation by placing a R_AARCH64_JUMP26 relocation at the offset of
|
||||
// the instruction we want to patch.
|
||||
write32le(loc, 0x14000000);
|
||||
LLVM_FALLTHROUGH;
|
||||
case R_AARCH64_CALL26:
|
||||
checkInt(loc, val, 28, type);
|
||||
or32le(loc, (val & 0x0FFFFFFC) >> 2);
|
||||
break;
|
||||
case R_AARCH64_CONDBR19:
|
||||
case R_AARCH64_LD_PREL_LO19:
|
||||
checkAlignment(loc, val, 4, type);
|
||||
checkInt(loc, val, 21, type);
|
||||
or32le(loc, (val & 0x1FFFFC) << 3);
|
||||
break;
|
||||
case R_AARCH64_LDST8_ABS_LO12_NC:
|
||||
case R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
|
||||
or32AArch64Imm(loc, getBits(val, 0, 11));
|
||||
break;
|
||||
case R_AARCH64_LDST16_ABS_LO12_NC:
|
||||
case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
|
||||
checkAlignment(loc, val, 2, type);
|
||||
or32AArch64Imm(loc, getBits(val, 1, 11));
|
||||
break;
|
||||
case R_AARCH64_LDST32_ABS_LO12_NC:
|
||||
case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
|
||||
checkAlignment(loc, val, 4, type);
|
||||
or32AArch64Imm(loc, getBits(val, 2, 11));
|
||||
break;
|
||||
case R_AARCH64_LDST64_ABS_LO12_NC:
|
||||
case R_AARCH64_LD64_GOT_LO12_NC:
|
||||
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
|
||||
case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
|
||||
case R_AARCH64_TLSDESC_LD64_LO12:
|
||||
checkAlignment(loc, val, 8, type);
|
||||
or32AArch64Imm(loc, getBits(val, 3, 11));
|
||||
break;
|
||||
case R_AARCH64_LDST128_ABS_LO12_NC:
|
||||
case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC:
|
||||
checkAlignment(loc, val, 16, type);
|
||||
or32AArch64Imm(loc, getBits(val, 4, 11));
|
||||
break;
|
||||
case R_AARCH64_MOVW_UABS_G0_NC:
|
||||
or32le(loc, (val & 0xFFFF) << 5);
|
||||
break;
|
||||
case R_AARCH64_MOVW_UABS_G1_NC:
|
||||
or32le(loc, (val & 0xFFFF0000) >> 11);
|
||||
break;
|
||||
case R_AARCH64_MOVW_UABS_G2_NC:
|
||||
or32le(loc, (val & 0xFFFF00000000) >> 27);
|
||||
break;
|
||||
case R_AARCH64_MOVW_UABS_G3:
|
||||
or32le(loc, (val & 0xFFFF000000000000) >> 43);
|
||||
break;
|
||||
case R_AARCH64_TSTBR14:
|
||||
checkInt(loc, val, 16, type);
|
||||
or32le(loc, (val & 0xFFFC) << 3);
|
||||
break;
|
||||
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
|
||||
checkUInt(loc, val, 24, type);
|
||||
or32AArch64Imm(loc, val >> 12);
|
||||
break;
|
||||
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
|
||||
case R_AARCH64_TLSDESC_ADD_LO12:
|
||||
or32AArch64Imm(loc, val);
|
||||
break;
|
||||
default:
|
||||
error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
|
||||
}
|
||||
}
|
||||
|
||||
void AArch64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
// TLSDESC Global-Dynamic relocation are in the form:
|
||||
// adrp x0, :tlsdesc:v [R_AARCH64_TLSDESC_ADR_PAGE21]
|
||||
// ldr x1, [x0, #:tlsdesc_lo12:v [R_AARCH64_TLSDESC_LD64_LO12]
|
||||
// add x0, x0, :tlsdesc_los:v [R_AARCH64_TLSDESC_ADD_LO12]
|
||||
// .tlsdesccall [R_AARCH64_TLSDESC_CALL]
|
||||
// blr x1
|
||||
// And it can optimized to:
|
||||
// movz x0, #0x0, lsl #16
|
||||
// movk x0, #0x10
|
||||
// nop
|
||||
// nop
|
||||
checkUInt(loc, val, 32, type);
|
||||
|
||||
switch (type) {
|
||||
case R_AARCH64_TLSDESC_ADD_LO12:
|
||||
case R_AARCH64_TLSDESC_CALL:
|
||||
write32le(loc, 0xd503201f); // nop
|
||||
return;
|
||||
case R_AARCH64_TLSDESC_ADR_PAGE21:
|
||||
write32le(loc, 0xd2a00000 | (((val >> 16) & 0xffff) << 5)); // movz
|
||||
return;
|
||||
case R_AARCH64_TLSDESC_LD64_LO12:
|
||||
write32le(loc, 0xf2800000 | ((val & 0xffff) << 5)); // movk
|
||||
return;
|
||||
default:
|
||||
llvm_unreachable("unsupported relocation for TLS GD to LE relaxation");
|
||||
}
|
||||
}
|
||||
|
||||
void AArch64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
// TLSDESC Global-Dynamic relocation are in the form:
|
||||
// adrp x0, :tlsdesc:v [R_AARCH64_TLSDESC_ADR_PAGE21]
|
||||
// ldr x1, [x0, #:tlsdesc_lo12:v [R_AARCH64_TLSDESC_LD64_LO12]
|
||||
// add x0, x0, :tlsdesc_los:v [R_AARCH64_TLSDESC_ADD_LO12]
|
||||
// .tlsdesccall [R_AARCH64_TLSDESC_CALL]
|
||||
// blr x1
|
||||
// And it can optimized to:
|
||||
// adrp x0, :gottprel:v
|
||||
// ldr x0, [x0, :gottprel_lo12:v]
|
||||
// nop
|
||||
// nop
|
||||
|
||||
switch (type) {
|
||||
case R_AARCH64_TLSDESC_ADD_LO12:
|
||||
case R_AARCH64_TLSDESC_CALL:
|
||||
write32le(loc, 0xd503201f); // nop
|
||||
break;
|
||||
case R_AARCH64_TLSDESC_ADR_PAGE21:
|
||||
write32le(loc, 0x90000000); // adrp
|
||||
relocateOne(loc, R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21, val);
|
||||
break;
|
||||
case R_AARCH64_TLSDESC_LD64_LO12:
|
||||
write32le(loc, 0xf9400000); // ldr
|
||||
relocateOne(loc, R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC, val);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("unsupported relocation for TLS GD to LE relaxation");
|
||||
}
|
||||
}
|
||||
|
||||
void AArch64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
checkUInt(loc, val, 32, type);
|
||||
|
||||
if (type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) {
|
||||
// Generate MOVZ.
|
||||
uint32_t regNo = read32le(loc) & 0x1f;
|
||||
write32le(loc, (0xd2a00000 | regNo) | (((val >> 16) & 0xffff) << 5));
|
||||
return;
|
||||
}
|
||||
if (type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) {
|
||||
// Generate MOVK.
|
||||
uint32_t regNo = read32le(loc) & 0x1f;
|
||||
write32le(loc, (0xf2800000 | regNo) | ((val & 0xffff) << 5));
|
||||
return;
|
||||
}
|
||||
llvm_unreachable("invalid relocation for TLS IE to LE relaxation");
|
||||
}
|
||||
|
||||
// AArch64 may use security features in variant PLT sequences. These are:
|
||||
// Pointer Authentication (PAC), introduced in armv8.3-a and Branch Target
|
||||
// Indicator (BTI) introduced in armv8.5-a. The additional instructions used
|
||||
// in the variant Plt sequences are encoded in the Hint space so they can be
|
||||
// deployed on older architectures, which treat the instructions as a nop.
|
||||
// PAC and BTI can be combined leading to the following combinations:
|
||||
// writePltHeader
|
||||
// writePltHeaderBti (no PAC Header needed)
|
||||
// writePlt
|
||||
// writePltBti (BTI only)
|
||||
// writePltPac (PAC only)
|
||||
// writePltBtiPac (BTI and PAC)
|
||||
//
|
||||
// When PAC is enabled the dynamic loader encrypts the address that it places
|
||||
// in the .got.plt using the pacia1716 instruction which encrypts the value in
|
||||
// x17 using the modifier in x16. The static linker places autia1716 before the
|
||||
// indirect branch to x17 to authenticate the address in x17 with the modifier
|
||||
// in x16. This makes it more difficult for an attacker to modify the value in
|
||||
// the .got.plt.
|
||||
//
|
||||
// When BTI is enabled all indirect branches must land on a bti instruction.
|
||||
// The static linker must place a bti instruction at the start of any PLT entry
|
||||
// that may be the target of an indirect branch. As the PLT entries call the
|
||||
// lazy resolver indirectly this must have a bti instruction at start. In
|
||||
// general a bti instruction is not needed for a PLT entry as indirect calls
|
||||
// are resolved to the function address and not the PLT entry for the function.
|
||||
// There are a small number of cases where the PLT address can escape, such as
|
||||
// taking the address of a function or ifunc via a non got-generating
|
||||
// relocation, and a shared library refers to that symbol.
|
||||
//
|
||||
// We use the bti c variant of the instruction which permits indirect branches
|
||||
// (br) via x16/x17 and indirect function calls (blr) via any register. The ABI
|
||||
// guarantees that all indirect branches from code requiring BTI protection
|
||||
// will go via x16/x17
|
||||
|
||||
namespace {
|
||||
class AArch64BtiPac final : public AArch64 {
|
||||
public:
|
||||
AArch64BtiPac();
|
||||
void writePltHeader(uint8_t *buf) const override;
|
||||
void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
|
||||
int32_t index, unsigned relOff) const override;
|
||||
|
||||
private:
|
||||
bool btiHeader; // bti instruction needed in PLT Header
|
||||
bool btiEntry; // bti instruction needed in PLT Entry
|
||||
bool pacEntry; // autia1716 instruction needed in PLT Entry
|
||||
};
|
||||
} // namespace
|
||||
|
||||
AArch64BtiPac::AArch64BtiPac() {
|
||||
btiHeader = (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_BTI);
|
||||
// A BTI (Branch Target Indicator) Plt Entry is only required if the
|
||||
// address of the PLT entry can be taken by the program, which permits an
|
||||
// indirect jump to the PLT entry. This can happen when the address
|
||||
// of the PLT entry for a function is canonicalised due to the address of
|
||||
// the function in an executable being taken by a shared library.
|
||||
// FIXME: There is a potential optimization to omit the BTI if we detect
|
||||
// that the address of the PLT entry isn't taken.
|
||||
btiEntry = btiHeader && !config->shared;
|
||||
pacEntry = (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_PAC);
|
||||
|
||||
if (btiEntry || pacEntry)
|
||||
pltEntrySize = 24;
|
||||
}
|
||||
|
||||
void AArch64BtiPac::writePltHeader(uint8_t *buf) const {
|
||||
const uint8_t btiData[] = { 0x5f, 0x24, 0x03, 0xd5 }; // bti c
|
||||
const uint8_t pltData[] = {
|
||||
0xf0, 0x7b, 0xbf, 0xa9, // stp x16, x30, [sp,#-16]!
|
||||
0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[2]))
|
||||
0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[2]))]
|
||||
0x10, 0x02, 0x00, 0x91, // add x16, x16, Offset(&(.plt.got[2]))
|
||||
0x20, 0x02, 0x1f, 0xd6, // br x17
|
||||
0x1f, 0x20, 0x03, 0xd5, // nop
|
||||
0x1f, 0x20, 0x03, 0xd5 // nop
|
||||
};
|
||||
const uint8_t nopData[] = { 0x1f, 0x20, 0x03, 0xd5 }; // nop
|
||||
|
||||
uint64_t got = in.gotPlt->getVA();
|
||||
uint64_t plt = in.plt->getVA();
|
||||
|
||||
if (btiHeader) {
|
||||
// PltHeader is called indirectly by plt[N]. Prefix pltData with a BTI C
|
||||
// instruction.
|
||||
memcpy(buf, btiData, sizeof(btiData));
|
||||
buf += sizeof(btiData);
|
||||
plt += sizeof(btiData);
|
||||
}
|
||||
memcpy(buf, pltData, sizeof(pltData));
|
||||
|
||||
relocateOne(buf + 4, R_AARCH64_ADR_PREL_PG_HI21,
|
||||
getAArch64Page(got + 16) - getAArch64Page(plt + 8));
|
||||
relocateOne(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16);
|
||||
relocateOne(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16);
|
||||
if (!btiHeader)
|
||||
// We didn't add the BTI c instruction so round out size with NOP.
|
||||
memcpy(buf + sizeof(pltData), nopData, sizeof(nopData));
|
||||
}
|
||||
|
||||
void AArch64BtiPac::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
|
||||
uint64_t pltEntryAddr, int32_t index,
|
||||
unsigned relOff) const {
|
||||
// The PLT entry is of the form:
|
||||
// [btiData] addrInst (pacBr | stdBr) [nopData]
|
||||
const uint8_t btiData[] = { 0x5f, 0x24, 0x03, 0xd5 }; // bti c
|
||||
const uint8_t addrInst[] = {
|
||||
0x10, 0x00, 0x00, 0x90, // adrp x16, Page(&(.plt.got[n]))
|
||||
0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, Offset(&(.plt.got[n]))]
|
||||
0x10, 0x02, 0x00, 0x91 // add x16, x16, Offset(&(.plt.got[n]))
|
||||
};
|
||||
const uint8_t pacBr[] = {
|
||||
0x9f, 0x21, 0x03, 0xd5, // autia1716
|
||||
0x20, 0x02, 0x1f, 0xd6 // br x17
|
||||
};
|
||||
const uint8_t stdBr[] = {
|
||||
0x20, 0x02, 0x1f, 0xd6, // br x17
|
||||
0x1f, 0x20, 0x03, 0xd5 // nop
|
||||
};
|
||||
const uint8_t nopData[] = { 0x1f, 0x20, 0x03, 0xd5 }; // nop
|
||||
|
||||
if (btiEntry) {
|
||||
memcpy(buf, btiData, sizeof(btiData));
|
||||
buf += sizeof(btiData);
|
||||
pltEntryAddr += sizeof(btiData);
|
||||
}
|
||||
|
||||
memcpy(buf, addrInst, sizeof(addrInst));
|
||||
relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21,
|
||||
getAArch64Page(gotPltEntryAddr) -
|
||||
getAArch64Page(pltEntryAddr));
|
||||
relocateOne(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr);
|
||||
relocateOne(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr);
|
||||
|
||||
if (pacEntry)
|
||||
memcpy(buf + sizeof(addrInst), pacBr, sizeof(pacBr));
|
||||
else
|
||||
memcpy(buf + sizeof(addrInst), stdBr, sizeof(stdBr));
|
||||
if (!btiEntry)
|
||||
// We didn't add the BTI c instruction so round out size with NOP.
|
||||
memcpy(buf + sizeof(addrInst) + sizeof(stdBr), nopData, sizeof(nopData));
|
||||
}
|
||||
|
||||
static TargetInfo *getTargetInfo() {
|
||||
if (config->andFeatures & (GNU_PROPERTY_AARCH64_FEATURE_1_BTI |
|
||||
GNU_PROPERTY_AARCH64_FEATURE_1_PAC)) {
|
||||
static AArch64BtiPac t;
|
||||
return &t;
|
||||
}
|
||||
static AArch64 t;
|
||||
return &t;
|
||||
}
|
||||
|
||||
TargetInfo *elf::getAArch64TargetInfo() { return getTargetInfo(); }
|
||||
113
deps/lld/ELF/Arch/AMDGPU.cpp
vendored
113
deps/lld/ELF/Arch/AMDGPU.cpp
vendored
@ -1,113 +0,0 @@
|
||||
//===- AMDGPU.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 "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "Target.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace {
|
||||
class AMDGPU final : public TargetInfo {
|
||||
public:
|
||||
AMDGPU();
|
||||
uint32_t calcEFlags() const override;
|
||||
void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
RelExpr getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const override;
|
||||
RelType getDynRel(RelType type) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
AMDGPU::AMDGPU() {
|
||||
relativeRel = R_AMDGPU_RELATIVE64;
|
||||
gotRel = R_AMDGPU_ABS64;
|
||||
noneRel = R_AMDGPU_NONE;
|
||||
symbolicRel = R_AMDGPU_ABS64;
|
||||
}
|
||||
|
||||
static uint32_t getEFlags(InputFile *file) {
|
||||
return cast<ObjFile<ELF64LE>>(file)->getObj().getHeader()->e_flags;
|
||||
}
|
||||
|
||||
uint32_t AMDGPU::calcEFlags() const {
|
||||
assert(!objectFiles.empty());
|
||||
uint32_t ret = getEFlags(objectFiles[0]);
|
||||
|
||||
// Verify that all input files have the same e_flags.
|
||||
for (InputFile *f : makeArrayRef(objectFiles).slice(1)) {
|
||||
if (ret == getEFlags(f))
|
||||
continue;
|
||||
error("incompatible e_flags: " + toString(f));
|
||||
return 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void AMDGPU::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
switch (type) {
|
||||
case R_AMDGPU_ABS32:
|
||||
case R_AMDGPU_GOTPCREL:
|
||||
case R_AMDGPU_GOTPCREL32_LO:
|
||||
case R_AMDGPU_REL32:
|
||||
case R_AMDGPU_REL32_LO:
|
||||
write32le(loc, val);
|
||||
break;
|
||||
case R_AMDGPU_ABS64:
|
||||
case R_AMDGPU_REL64:
|
||||
write64le(loc, val);
|
||||
break;
|
||||
case R_AMDGPU_GOTPCREL32_HI:
|
||||
case R_AMDGPU_REL32_HI:
|
||||
write32le(loc, val >> 32);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("unknown relocation");
|
||||
}
|
||||
}
|
||||
|
||||
RelExpr AMDGPU::getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const {
|
||||
switch (type) {
|
||||
case R_AMDGPU_ABS32:
|
||||
case R_AMDGPU_ABS64:
|
||||
return R_ABS;
|
||||
case R_AMDGPU_REL32:
|
||||
case R_AMDGPU_REL32_LO:
|
||||
case R_AMDGPU_REL32_HI:
|
||||
case R_AMDGPU_REL64:
|
||||
return R_PC;
|
||||
case R_AMDGPU_GOTPCREL:
|
||||
case R_AMDGPU_GOTPCREL32_LO:
|
||||
case R_AMDGPU_GOTPCREL32_HI:
|
||||
return R_GOT_PC;
|
||||
default:
|
||||
error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
|
||||
") against symbol " + toString(s));
|
||||
return R_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
RelType AMDGPU::getDynRel(RelType type) const {
|
||||
if (type == R_AMDGPU_ABS64)
|
||||
return type;
|
||||
return R_AMDGPU_NONE;
|
||||
}
|
||||
|
||||
TargetInfo *elf::getAMDGPUTargetInfo() {
|
||||
static AMDGPU target;
|
||||
return ⌖
|
||||
}
|
||||
606
deps/lld/ELF/Arch/ARM.cpp
vendored
606
deps/lld/ELF/Arch/ARM.cpp
vendored
@ -1,606 +0,0 @@
|
||||
//===- ARM.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 "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "Thunks.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace {
|
||||
class ARM final : public TargetInfo {
|
||||
public:
|
||||
ARM();
|
||||
uint32_t calcEFlags() const override;
|
||||
RelExpr getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const override;
|
||||
RelType getDynRel(RelType type) const override;
|
||||
int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
|
||||
void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
|
||||
void writeIgotPlt(uint8_t *buf, const Symbol &s) const override;
|
||||
void writePltHeader(uint8_t *buf) const override;
|
||||
void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
|
||||
int32_t index, unsigned relOff) const override;
|
||||
void addPltSymbols(InputSection &isec, uint64_t off) const override;
|
||||
void addPltHeaderSymbols(InputSection &isd) const override;
|
||||
bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
|
||||
uint64_t branchAddr, const Symbol &s) const override;
|
||||
uint32_t getThunkSectionSpacing() const override;
|
||||
bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
|
||||
void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
ARM::ARM() {
|
||||
copyRel = R_ARM_COPY;
|
||||
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;
|
||||
pltEntrySize = 16;
|
||||
pltHeaderSize = 32;
|
||||
trapInstr = {0xd4, 0xd4, 0xd4, 0xd4};
|
||||
needsThunks = true;
|
||||
}
|
||||
|
||||
uint32_t ARM::calcEFlags() const {
|
||||
// The ABIFloatType is used by loaders to detect the floating point calling
|
||||
// convention.
|
||||
uint32_t abiFloatType = 0;
|
||||
if (config->armVFPArgs == ARMVFPArgKind::Base ||
|
||||
config->armVFPArgs == ARMVFPArgKind::Default)
|
||||
abiFloatType = EF_ARM_ABI_FLOAT_SOFT;
|
||||
else if (config->armVFPArgs == ARMVFPArgKind::VFP)
|
||||
abiFloatType = EF_ARM_ABI_FLOAT_HARD;
|
||||
|
||||
// We don't currently use any features incompatible with EF_ARM_EABI_VER5,
|
||||
// but we don't have any firm guarantees of conformance. Linux AArch64
|
||||
// kernels (as of 2016) require an EABI version to be set.
|
||||
return EF_ARM_EABI_VER5 | abiFloatType;
|
||||
}
|
||||
|
||||
RelExpr ARM::getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const {
|
||||
switch (type) {
|
||||
case R_ARM_THM_JUMP11:
|
||||
return R_PC;
|
||||
case R_ARM_CALL:
|
||||
case R_ARM_JUMP24:
|
||||
case R_ARM_PC24:
|
||||
case R_ARM_PLT32:
|
||||
case R_ARM_PREL31:
|
||||
case R_ARM_THM_JUMP19:
|
||||
case R_ARM_THM_JUMP24:
|
||||
case R_ARM_THM_CALL:
|
||||
return R_PLT_PC;
|
||||
case R_ARM_GOTOFF32:
|
||||
// (S + A) - GOT_ORG
|
||||
return R_GOTREL;
|
||||
case R_ARM_GOT_BREL:
|
||||
// GOT(S) + A - GOT_ORG
|
||||
return R_GOT_OFF;
|
||||
case R_ARM_GOT_PREL:
|
||||
case R_ARM_TLS_IE32:
|
||||
// GOT(S) + A - P
|
||||
return R_GOT_PC;
|
||||
case R_ARM_SBREL32:
|
||||
return R_ARM_SBREL;
|
||||
case R_ARM_TARGET1:
|
||||
return config->target1Rel ? R_PC : R_ABS;
|
||||
case R_ARM_TARGET2:
|
||||
if (config->target2 == Target2Policy::Rel)
|
||||
return R_PC;
|
||||
if (config->target2 == Target2Policy::Abs)
|
||||
return R_ABS;
|
||||
return R_GOT_PC;
|
||||
case R_ARM_TLS_GD32:
|
||||
return R_TLSGD_PC;
|
||||
case R_ARM_TLS_LDM32:
|
||||
return R_TLSLD_PC;
|
||||
case R_ARM_BASE_PREL:
|
||||
// B(S) + A - P
|
||||
// FIXME: currently B(S) assumed to be .got, this may not hold for all
|
||||
// platforms.
|
||||
return R_GOTONLY_PC;
|
||||
case R_ARM_MOVW_PREL_NC:
|
||||
case R_ARM_MOVT_PREL:
|
||||
case R_ARM_REL32:
|
||||
case R_ARM_THM_MOVW_PREL_NC:
|
||||
case R_ARM_THM_MOVT_PREL:
|
||||
return R_PC;
|
||||
case R_ARM_NONE:
|
||||
return R_NONE;
|
||||
case R_ARM_TLS_LE32:
|
||||
return R_TLS;
|
||||
case R_ARM_V4BX:
|
||||
// V4BX is just a marker to indicate there's a "bx rN" instruction at the
|
||||
// given address. It can be used to implement a special linker mode which
|
||||
// rewrites ARMv4T inputs to ARMv4. Since we support only ARMv4 input and
|
||||
// not ARMv4 output, we can just ignore it.
|
||||
return R_HINT;
|
||||
default:
|
||||
return R_ABS;
|
||||
}
|
||||
}
|
||||
|
||||
RelType ARM::getDynRel(RelType type) const {
|
||||
if ((type == R_ARM_ABS32) || (type == R_ARM_TARGET1 && !config->target1Rel))
|
||||
return R_ARM_ABS32;
|
||||
return R_ARM_NONE;
|
||||
}
|
||||
|
||||
void ARM::writeGotPlt(uint8_t *buf, const Symbol &) const {
|
||||
write32le(buf, in.plt->getVA());
|
||||
}
|
||||
|
||||
void ARM::writeIgotPlt(uint8_t *buf, const Symbol &s) const {
|
||||
// An ARM entry is the address of the ifunc resolver function.
|
||||
write32le(buf, s.getVA());
|
||||
}
|
||||
|
||||
// Long form PLT Header that does not have any restrictions on the displacement
|
||||
// of the .plt from the .plt.got.
|
||||
static void writePltHeaderLong(uint8_t *buf) {
|
||||
const uint8_t pltData[] = {
|
||||
0x04, 0xe0, 0x2d, 0xe5, // str lr, [sp,#-4]!
|
||||
0x04, 0xe0, 0x9f, 0xe5, // ldr lr, L2
|
||||
0x0e, 0xe0, 0x8f, 0xe0, // L1: add lr, pc, lr
|
||||
0x08, 0xf0, 0xbe, 0xe5, // ldr pc, [lr, #8]
|
||||
0x00, 0x00, 0x00, 0x00, // L2: .word &(.got.plt) - L1 - 8
|
||||
0xd4, 0xd4, 0xd4, 0xd4, // Pad to 32-byte boundary
|
||||
0xd4, 0xd4, 0xd4, 0xd4, // Pad to 32-byte boundary
|
||||
0xd4, 0xd4, 0xd4, 0xd4};
|
||||
memcpy(buf, pltData, sizeof(pltData));
|
||||
uint64_t gotPlt = in.gotPlt->getVA();
|
||||
uint64_t l1 = in.plt->getVA() + 8;
|
||||
write32le(buf + 16, gotPlt - l1 - 8);
|
||||
}
|
||||
|
||||
// The default PLT header requires the .plt.got to be within 128 Mb of the
|
||||
// .plt in the positive direction.
|
||||
void ARM::writePltHeader(uint8_t *buf) const {
|
||||
// Use a similar sequence to that in writePlt(), the difference is the calling
|
||||
// conventions mean we use lr instead of ip. The PLT entry is responsible for
|
||||
// saving lr on the stack, the dynamic loader is responsible for reloading
|
||||
// it.
|
||||
const uint32_t pltData[] = {
|
||||
0xe52de004, // L1: str lr, [sp,#-4]!
|
||||
0xe28fe600, // add lr, pc, #0x0NN00000 &(.got.plt - L1 - 4)
|
||||
0xe28eea00, // add lr, lr, #0x000NN000 &(.got.plt - L1 - 4)
|
||||
0xe5bef000, // ldr pc, [lr, #0x00000NNN] &(.got.plt -L1 - 4)
|
||||
};
|
||||
|
||||
uint64_t offset = in.gotPlt->getVA() - in.plt->getVA() - 4;
|
||||
if (!llvm::isUInt<27>(offset)) {
|
||||
// We cannot encode the Offset, use the long form.
|
||||
writePltHeaderLong(buf);
|
||||
return;
|
||||
}
|
||||
write32le(buf + 0, pltData[0]);
|
||||
write32le(buf + 4, pltData[1] | ((offset >> 20) & 0xff));
|
||||
write32le(buf + 8, pltData[2] | ((offset >> 12) & 0xff));
|
||||
write32le(buf + 12, pltData[3] | (offset & 0xfff));
|
||||
memcpy(buf + 16, trapInstr.data(), 4); // Pad to 32-byte boundary
|
||||
memcpy(buf + 20, trapInstr.data(), 4);
|
||||
memcpy(buf + 24, trapInstr.data(), 4);
|
||||
memcpy(buf + 28, trapInstr.data(), 4);
|
||||
}
|
||||
|
||||
void ARM::addPltHeaderSymbols(InputSection &isec) const {
|
||||
addSyntheticLocal("$a", STT_NOTYPE, 0, 0, isec);
|
||||
addSyntheticLocal("$d", STT_NOTYPE, 16, 0, isec);
|
||||
}
|
||||
|
||||
// Long form PLT entries that do not have any restrictions on the displacement
|
||||
// of the .plt from the .plt.got.
|
||||
static void writePltLong(uint8_t *buf, uint64_t gotPltEntryAddr,
|
||||
uint64_t pltEntryAddr, int32_t index,
|
||||
unsigned relOff) {
|
||||
const uint8_t pltData[] = {
|
||||
0x04, 0xc0, 0x9f, 0xe5, // ldr ip, L2
|
||||
0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc
|
||||
0x00, 0xf0, 0x9c, 0xe5, // ldr pc, [ip]
|
||||
0x00, 0x00, 0x00, 0x00, // L2: .word Offset(&(.plt.got) - 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
|
||||
// .plt in the positive direction.
|
||||
void ARM::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
|
||||
uint64_t pltEntryAddr, int32_t index,
|
||||
unsigned relOff) const {
|
||||
// The PLT entry is similar to the example given in Appendix A of ELF for
|
||||
// the Arm Architecture. Instead of using the Group Relocations to find the
|
||||
// optimal rotation for the 8-bit immediate used in the add instructions we
|
||||
// hard code the most compact rotations for simplicity. This saves a load
|
||||
// instruction over the long plt sequences.
|
||||
const uint32_t pltData[] = {
|
||||
0xe28fc600, // L1: add ip, pc, #0x0NN00000 Offset(&(.plt.got) - L1 - 8
|
||||
0xe28cca00, // add ip, ip, #0x000NN000 Offset(&(.plt.got) - L1 - 8
|
||||
0xe5bcf000, // ldr pc, [ip, #0x00000NNN] Offset(&(.plt.got) - L1 - 8
|
||||
};
|
||||
|
||||
uint64_t offset = gotPltEntryAddr - pltEntryAddr - 8;
|
||||
if (!llvm::isUInt<27>(offset)) {
|
||||
// We cannot encode the Offset, use the long form.
|
||||
writePltLong(buf, gotPltEntryAddr, pltEntryAddr, index, relOff);
|
||||
return;
|
||||
}
|
||||
write32le(buf + 0, pltData[0] | ((offset >> 20) & 0xff));
|
||||
write32le(buf + 4, pltData[1] | ((offset >> 12) & 0xff));
|
||||
write32le(buf + 8, pltData[2] | (offset & 0xfff));
|
||||
memcpy(buf + 12, trapInstr.data(), 4); // Pad to 16-byte boundary
|
||||
}
|
||||
|
||||
void ARM::addPltSymbols(InputSection &isec, uint64_t off) const {
|
||||
addSyntheticLocal("$a", STT_NOTYPE, off, 0, isec);
|
||||
addSyntheticLocal("$d", STT_NOTYPE, off + 12, 0, isec);
|
||||
}
|
||||
|
||||
bool ARM::needsThunk(RelExpr expr, RelType type, const InputFile *file,
|
||||
uint64_t branchAddr, const Symbol &s) const {
|
||||
// If S is an undefined weak symbol and does not have a PLT entry then it
|
||||
// will be resolved as a branch to the next instruction.
|
||||
if (s.isUndefWeak() && !s.isInPlt())
|
||||
return false;
|
||||
// A state change from ARM to Thumb and vice versa must go through an
|
||||
// interworking thunk if the relocation type is not R_ARM_CALL or
|
||||
// R_ARM_THM_CALL.
|
||||
switch (type) {
|
||||
case R_ARM_PC24:
|
||||
case R_ARM_PLT32:
|
||||
case R_ARM_JUMP24:
|
||||
// Source is ARM, all PLT entries are ARM so no interworking required.
|
||||
// Otherwise we need to interwork if Symbol has bit 0 set (Thumb).
|
||||
if (expr == R_PC && ((s.getVA() & 1) == 1))
|
||||
return true;
|
||||
LLVM_FALLTHROUGH;
|
||||
case R_ARM_CALL: {
|
||||
uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA();
|
||||
return !inBranchRange(type, branchAddr, dst);
|
||||
}
|
||||
case R_ARM_THM_JUMP19:
|
||||
case R_ARM_THM_JUMP24:
|
||||
// Source is Thumb, all PLT entries are ARM so interworking is required.
|
||||
// Otherwise we need to interwork if Symbol has bit 0 clear (ARM).
|
||||
if (expr == R_PLT_PC || ((s.getVA() & 1) == 0))
|
||||
return true;
|
||||
LLVM_FALLTHROUGH;
|
||||
case R_ARM_THM_CALL: {
|
||||
uint64_t dst = (expr == R_PLT_PC) ? s.getPltVA() : s.getVA();
|
||||
return !inBranchRange(type, branchAddr, dst);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t ARM::getThunkSectionSpacing() const {
|
||||
// The placing of pre-created ThunkSections is controlled by the value
|
||||
// thunkSectionSpacing returned by getThunkSectionSpacing(). The aim is to
|
||||
// place the ThunkSection such that all branches from the InputSections
|
||||
// prior to the ThunkSection can reach a Thunk placed at the end of the
|
||||
// ThunkSection. Graphically:
|
||||
// | up to thunkSectionSpacing .text input sections |
|
||||
// | ThunkSection |
|
||||
// | up to thunkSectionSpacing .text input sections |
|
||||
// | ThunkSection |
|
||||
|
||||
// Pre-created ThunkSections are spaced roughly 16MiB apart on ARMv7. This
|
||||
// is to match the most common expected case of a Thumb 2 encoded BL, BLX or
|
||||
// B.W:
|
||||
// ARM B, BL, BLX range +/- 32MiB
|
||||
// Thumb B.W, BL, BLX range +/- 16MiB
|
||||
// Thumb B<cc>.W range +/- 1MiB
|
||||
// If a branch cannot reach a pre-created ThunkSection a new one will be
|
||||
// created so we can handle the rare cases of a Thumb 2 conditional branch.
|
||||
// We intentionally use a lower size for thunkSectionSpacing than the maximum
|
||||
// branch range so the end of the ThunkSection is more likely to be within
|
||||
// range of the branch instruction that is furthest away. The value we shorten
|
||||
// thunkSectionSpacing by is set conservatively to allow us to create 16,384
|
||||
// 12 byte Thunks at any offset in a ThunkSection without risk of a branch to
|
||||
// one of the Thunks going out of range.
|
||||
|
||||
// On Arm the thunkSectionSpacing depends on the range of the Thumb Branch
|
||||
// range. On earlier Architectures such as ARMv4, ARMv5 and ARMv6 (except
|
||||
// ARMv6T2) the range is +/- 4MiB.
|
||||
|
||||
return (config->armJ1J2BranchEncoding) ? 0x1000000 - 0x30000
|
||||
: 0x400000 - 0x7500;
|
||||
}
|
||||
|
||||
bool ARM::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
|
||||
uint64_t range;
|
||||
uint64_t instrSize;
|
||||
|
||||
switch (type) {
|
||||
case R_ARM_PC24:
|
||||
case R_ARM_PLT32:
|
||||
case R_ARM_JUMP24:
|
||||
case R_ARM_CALL:
|
||||
range = 0x2000000;
|
||||
instrSize = 4;
|
||||
break;
|
||||
case R_ARM_THM_JUMP19:
|
||||
range = 0x100000;
|
||||
instrSize = 2;
|
||||
break;
|
||||
case R_ARM_THM_JUMP24:
|
||||
case R_ARM_THM_CALL:
|
||||
range = config->armJ1J2BranchEncoding ? 0x1000000 : 0x400000;
|
||||
instrSize = 2;
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
// PC at Src is 2 instructions ahead, immediate of branch is signed
|
||||
if (src > dst)
|
||||
range -= 2 * instrSize;
|
||||
else
|
||||
range += instrSize;
|
||||
|
||||
if ((dst & 0x1) == 0)
|
||||
// Destination is ARM, if ARM caller then Src is already 4-byte aligned.
|
||||
// If Thumb Caller (BLX) the Src address has bottom 2 bits cleared to ensure
|
||||
// destination will be 4 byte aligned.
|
||||
src &= ~0x3;
|
||||
else
|
||||
// Bit 0 == 1 denotes Thumb state, it is not part of the range
|
||||
dst &= ~0x1;
|
||||
|
||||
uint64_t distance = (src > dst) ? src - dst : dst - src;
|
||||
return distance <= range;
|
||||
}
|
||||
|
||||
void ARM::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
switch (type) {
|
||||
case R_ARM_ABS32:
|
||||
case R_ARM_BASE_PREL:
|
||||
case R_ARM_GOTOFF32:
|
||||
case R_ARM_GOT_BREL:
|
||||
case R_ARM_GOT_PREL:
|
||||
case R_ARM_REL32:
|
||||
case R_ARM_RELATIVE:
|
||||
case R_ARM_SBREL32:
|
||||
case R_ARM_TARGET1:
|
||||
case R_ARM_TARGET2:
|
||||
case R_ARM_TLS_GD32:
|
||||
case R_ARM_TLS_IE32:
|
||||
case R_ARM_TLS_LDM32:
|
||||
case R_ARM_TLS_LDO32:
|
||||
case R_ARM_TLS_LE32:
|
||||
case R_ARM_TLS_TPOFF32:
|
||||
case R_ARM_TLS_DTPOFF32:
|
||||
write32le(loc, val);
|
||||
break;
|
||||
case R_ARM_PREL31:
|
||||
checkInt(loc, val, 31, type);
|
||||
write32le(loc, (read32le(loc) & 0x80000000) | (val & ~0x80000000));
|
||||
break;
|
||||
case R_ARM_CALL:
|
||||
// R_ARM_CALL is used for BL and BLX instructions, depending on the
|
||||
// value of bit 0 of Val, we must select a BL or BLX instruction
|
||||
if (val & 1) {
|
||||
// If bit 0 of Val is 1 the target is Thumb, we must select a BLX.
|
||||
// The BLX encoding is 0xfa:H:imm24 where Val = imm24:H:'1'
|
||||
checkInt(loc, val, 26, type);
|
||||
write32le(loc, 0xfa000000 | // opcode
|
||||
((val & 2) << 23) | // H
|
||||
((val >> 2) & 0x00ffffff)); // imm24
|
||||
break;
|
||||
}
|
||||
if ((read32le(loc) & 0xfe000000) == 0xfa000000)
|
||||
// BLX (always unconditional) instruction to an ARM Target, select an
|
||||
// unconditional BL.
|
||||
write32le(loc, 0xeb000000 | (read32le(loc) & 0x00ffffff));
|
||||
// fall through as BL encoding is shared with B
|
||||
LLVM_FALLTHROUGH;
|
||||
case R_ARM_JUMP24:
|
||||
case R_ARM_PC24:
|
||||
case R_ARM_PLT32:
|
||||
checkInt(loc, val, 26, type);
|
||||
write32le(loc, (read32le(loc) & ~0x00ffffff) | ((val >> 2) & 0x00ffffff));
|
||||
break;
|
||||
case R_ARM_THM_JUMP11:
|
||||
checkInt(loc, val, 12, type);
|
||||
write16le(loc, (read32le(loc) & 0xf800) | ((val >> 1) & 0x07ff));
|
||||
break;
|
||||
case R_ARM_THM_JUMP19:
|
||||
// Encoding T3: Val = S:J2:J1:imm6:imm11:0
|
||||
checkInt(loc, val, 21, type);
|
||||
write16le(loc,
|
||||
(read16le(loc) & 0xfbc0) | // opcode cond
|
||||
((val >> 10) & 0x0400) | // S
|
||||
((val >> 12) & 0x003f)); // imm6
|
||||
write16le(loc + 2,
|
||||
0x8000 | // opcode
|
||||
((val >> 8) & 0x0800) | // J2
|
||||
((val >> 5) & 0x2000) | // J1
|
||||
((val >> 1) & 0x07ff)); // imm11
|
||||
break;
|
||||
case R_ARM_THM_CALL:
|
||||
// R_ARM_THM_CALL is used for BL and BLX instructions, depending on the
|
||||
// value of bit 0 of Val, we must select a BL or BLX instruction
|
||||
if ((val & 1) == 0) {
|
||||
// Ensure BLX destination is 4-byte aligned. As BLX instruction may
|
||||
// only be two byte aligned. This must be done before overflow check
|
||||
val = alignTo(val, 4);
|
||||
}
|
||||
// Bit 12 is 0 for BLX, 1 for BL
|
||||
write16le(loc + 2, (read16le(loc + 2) & ~0x1000) | (val & 1) << 12);
|
||||
if (!config->armJ1J2BranchEncoding) {
|
||||
// Older Arm architectures do not support R_ARM_THM_JUMP24 and have
|
||||
// different encoding rules and range due to J1 and J2 always being 1.
|
||||
checkInt(loc, val, 23, type);
|
||||
write16le(loc,
|
||||
0xf000 | // opcode
|
||||
((val >> 12) & 0x07ff)); // imm11
|
||||
write16le(loc + 2,
|
||||
(read16le(loc + 2) & 0xd000) | // opcode
|
||||
0x2800 | // J1 == J2 == 1
|
||||
((val >> 1) & 0x07ff)); // imm11
|
||||
break;
|
||||
}
|
||||
// Fall through as rest of encoding is the same as B.W
|
||||
LLVM_FALLTHROUGH;
|
||||
case R_ARM_THM_JUMP24:
|
||||
// Encoding B T4, BL T1, BLX T2: Val = S:I1:I2:imm10:imm11:0
|
||||
checkInt(loc, val, 25, type);
|
||||
write16le(loc,
|
||||
0xf000 | // opcode
|
||||
((val >> 14) & 0x0400) | // S
|
||||
((val >> 12) & 0x03ff)); // imm10
|
||||
write16le(loc + 2,
|
||||
(read16le(loc + 2) & 0xd000) | // opcode
|
||||
(((~(val >> 10)) ^ (val >> 11)) & 0x2000) | // J1
|
||||
(((~(val >> 11)) ^ (val >> 13)) & 0x0800) | // J2
|
||||
((val >> 1) & 0x07ff)); // imm11
|
||||
break;
|
||||
case R_ARM_MOVW_ABS_NC:
|
||||
case R_ARM_MOVW_PREL_NC:
|
||||
write32le(loc, (read32le(loc) & ~0x000f0fff) | ((val & 0xf000) << 4) |
|
||||
(val & 0x0fff));
|
||||
break;
|
||||
case R_ARM_MOVT_ABS:
|
||||
case R_ARM_MOVT_PREL:
|
||||
write32le(loc, (read32le(loc) & ~0x000f0fff) |
|
||||
(((val >> 16) & 0xf000) << 4) | ((val >> 16) & 0xfff));
|
||||
break;
|
||||
case R_ARM_THM_MOVT_ABS:
|
||||
case R_ARM_THM_MOVT_PREL:
|
||||
// Encoding T1: A = imm4:i:imm3:imm8
|
||||
write16le(loc,
|
||||
0xf2c0 | // opcode
|
||||
((val >> 17) & 0x0400) | // i
|
||||
((val >> 28) & 0x000f)); // imm4
|
||||
write16le(loc + 2,
|
||||
(read16le(loc + 2) & 0x8f00) | // opcode
|
||||
((val >> 12) & 0x7000) | // imm3
|
||||
((val >> 16) & 0x00ff)); // imm8
|
||||
break;
|
||||
case R_ARM_THM_MOVW_ABS_NC:
|
||||
case R_ARM_THM_MOVW_PREL_NC:
|
||||
// Encoding T3: A = imm4:i:imm3:imm8
|
||||
write16le(loc,
|
||||
0xf240 | // opcode
|
||||
((val >> 1) & 0x0400) | // i
|
||||
((val >> 12) & 0x000f)); // imm4
|
||||
write16le(loc + 2,
|
||||
(read16le(loc + 2) & 0x8f00) | // opcode
|
||||
((val << 4) & 0x7000) | // imm3
|
||||
(val & 0x00ff)); // imm8
|
||||
break;
|
||||
default:
|
||||
error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
|
||||
}
|
||||
}
|
||||
|
||||
int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const {
|
||||
switch (type) {
|
||||
default:
|
||||
return 0;
|
||||
case R_ARM_ABS32:
|
||||
case R_ARM_BASE_PREL:
|
||||
case R_ARM_GOTOFF32:
|
||||
case R_ARM_GOT_BREL:
|
||||
case R_ARM_GOT_PREL:
|
||||
case R_ARM_REL32:
|
||||
case R_ARM_TARGET1:
|
||||
case R_ARM_TARGET2:
|
||||
case R_ARM_TLS_GD32:
|
||||
case R_ARM_TLS_LDM32:
|
||||
case R_ARM_TLS_LDO32:
|
||||
case R_ARM_TLS_IE32:
|
||||
case R_ARM_TLS_LE32:
|
||||
return SignExtend64<32>(read32le(buf));
|
||||
case R_ARM_PREL31:
|
||||
return SignExtend64<31>(read32le(buf));
|
||||
case R_ARM_CALL:
|
||||
case R_ARM_JUMP24:
|
||||
case R_ARM_PC24:
|
||||
case R_ARM_PLT32:
|
||||
return SignExtend64<26>(read32le(buf) << 2);
|
||||
case R_ARM_THM_JUMP11:
|
||||
return SignExtend64<12>(read16le(buf) << 1);
|
||||
case R_ARM_THM_JUMP19: {
|
||||
// Encoding T3: A = S:J2:J1:imm10:imm6:0
|
||||
uint16_t hi = read16le(buf);
|
||||
uint16_t lo = read16le(buf + 2);
|
||||
return SignExtend64<20>(((hi & 0x0400) << 10) | // S
|
||||
((lo & 0x0800) << 8) | // J2
|
||||
((lo & 0x2000) << 5) | // J1
|
||||
((hi & 0x003f) << 12) | // imm6
|
||||
((lo & 0x07ff) << 1)); // imm11:0
|
||||
}
|
||||
case R_ARM_THM_CALL:
|
||||
if (!config->armJ1J2BranchEncoding) {
|
||||
// Older Arm architectures do not support R_ARM_THM_JUMP24 and have
|
||||
// different encoding rules and range due to J1 and J2 always being 1.
|
||||
uint16_t hi = read16le(buf);
|
||||
uint16_t lo = read16le(buf + 2);
|
||||
return SignExtend64<22>(((hi & 0x7ff) << 12) | // imm11
|
||||
((lo & 0x7ff) << 1)); // imm11:0
|
||||
break;
|
||||
}
|
||||
LLVM_FALLTHROUGH;
|
||||
case R_ARM_THM_JUMP24: {
|
||||
// Encoding B T4, BL T1, BLX T2: A = S:I1:I2:imm10:imm11:0
|
||||
// I1 = NOT(J1 EOR S), I2 = NOT(J2 EOR S)
|
||||
uint16_t hi = read16le(buf);
|
||||
uint16_t lo = read16le(buf + 2);
|
||||
return SignExtend64<24>(((hi & 0x0400) << 14) | // S
|
||||
(~((lo ^ (hi << 3)) << 10) & 0x00800000) | // I1
|
||||
(~((lo ^ (hi << 1)) << 11) & 0x00400000) | // I2
|
||||
((hi & 0x003ff) << 12) | // imm0
|
||||
((lo & 0x007ff) << 1)); // imm11:0
|
||||
}
|
||||
// ELF for the ARM Architecture 4.6.1.1 the implicit addend for MOVW and
|
||||
// MOVT is in the range -32768 <= A < 32768
|
||||
case R_ARM_MOVW_ABS_NC:
|
||||
case R_ARM_MOVT_ABS:
|
||||
case R_ARM_MOVW_PREL_NC:
|
||||
case R_ARM_MOVT_PREL: {
|
||||
uint64_t val = read32le(buf) & 0x000f0fff;
|
||||
return SignExtend64<16>(((val & 0x000f0000) >> 4) | (val & 0x00fff));
|
||||
}
|
||||
case R_ARM_THM_MOVW_ABS_NC:
|
||||
case R_ARM_THM_MOVT_ABS:
|
||||
case R_ARM_THM_MOVW_PREL_NC:
|
||||
case R_ARM_THM_MOVT_PREL: {
|
||||
// Encoding T3: A = imm4:i:imm3:imm8
|
||||
uint16_t hi = read16le(buf);
|
||||
uint16_t lo = read16le(buf + 2);
|
||||
return SignExtend64<16>(((hi & 0x000f) << 12) | // imm4
|
||||
((hi & 0x0400) << 1) | // i
|
||||
((lo & 0x7000) >> 4) | // imm3
|
||||
(lo & 0x00ff)); // imm8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TargetInfo *elf::getARMTargetInfo() {
|
||||
static ARM target;
|
||||
return ⌖
|
||||
}
|
||||
76
deps/lld/ELF/Arch/AVR.cpp
vendored
76
deps/lld/ELF/Arch/AVR.cpp
vendored
@ -1,76 +0,0 @@
|
||||
//===- AVR.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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// AVR is a Harvard-architecture 8-bit micrcontroller designed for small
|
||||
// baremetal programs. All AVR-family processors have 32 8-bit registers.
|
||||
// The tiniest AVR has 32 byte RAM and 1 KiB program memory, and the largest
|
||||
// one supports up to 2^24 data address space and 2^22 code address space.
|
||||
//
|
||||
// Since it is a baremetal programming, there's usually no loader to load
|
||||
// ELF files on AVRs. You are expected to link your program against address
|
||||
// 0 and pull out a .text section from the result using objcopy, so that you
|
||||
// can write the linked code to on-chip flush memory. You can do that with
|
||||
// the following commands:
|
||||
//
|
||||
// ld.lld -Ttext=0 -o foo foo.o
|
||||
// objcopy -O binary --only-section=.text foo output.bin
|
||||
//
|
||||
// Note that the current AVR support is very preliminary so you can't
|
||||
// link any useful program yet, though.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "Target.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace {
|
||||
class AVR final : public TargetInfo {
|
||||
public:
|
||||
AVR();
|
||||
RelExpr getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const override;
|
||||
void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
AVR::AVR() { noneRel = R_AVR_NONE; }
|
||||
|
||||
RelExpr AVR::getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const {
|
||||
return R_ABS;
|
||||
}
|
||||
|
||||
void AVR::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
switch (type) {
|
||||
case R_AVR_CALL: {
|
||||
uint16_t hi = val >> 17;
|
||||
uint16_t lo = val >> 1;
|
||||
write16le(loc, read16le(loc) | ((hi >> 1) << 4) | (hi & 1));
|
||||
write16le(loc + 2, lo);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
|
||||
}
|
||||
}
|
||||
|
||||
TargetInfo *elf::getAVRTargetInfo() {
|
||||
static AVR target;
|
||||
return ⌖
|
||||
}
|
||||
291
deps/lld/ELF/Arch/Hexagon.cpp
vendored
291
deps/lld/ELF/Arch/Hexagon.cpp
vendored
@ -1,291 +0,0 @@
|
||||
//===-- Hexagon.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 "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#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;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace {
|
||||
class Hexagon final : public TargetInfo {
|
||||
public:
|
||||
Hexagon();
|
||||
uint32_t calcEFlags() const override;
|
||||
RelExpr getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const override;
|
||||
void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
void writePltHeader(uint8_t *buf) const override;
|
||||
void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
|
||||
int32_t index, unsigned relOff) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
Hexagon::Hexagon() {
|
||||
pltRel = R_HEX_JMP_SLOT;
|
||||
relativeRel = R_HEX_RELATIVE;
|
||||
gotRel = R_HEX_GLOB_DAT;
|
||||
symbolicRel = R_HEX_32;
|
||||
|
||||
// The zero'th GOT entry is reserved for the address of _DYNAMIC. The
|
||||
// next 3 are reserved for the dynamic loader.
|
||||
gotPltHeaderEntriesNum = 4;
|
||||
|
||||
pltEntrySize = 16;
|
||||
pltHeaderSize = 32;
|
||||
|
||||
// Hexagon Linux uses 64K pages by default.
|
||||
defaultMaxPageSize = 0x10000;
|
||||
noneRel = R_HEX_NONE;
|
||||
}
|
||||
|
||||
uint32_t Hexagon::calcEFlags() const {
|
||||
assert(!objectFiles.empty());
|
||||
|
||||
// The architecture revision must always be equal to or greater than
|
||||
// greatest revision in the list of inputs.
|
||||
uint32_t ret = 0;
|
||||
for (InputFile *f : objectFiles) {
|
||||
uint32_t eflags = cast<ObjFile<ELF32LE>>(f)->getObj().getHeader()->e_flags;
|
||||
if (eflags > ret)
|
||||
ret = eflags;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t applyMask(uint32_t mask, uint32_t data) {
|
||||
uint32_t result = 0;
|
||||
size_t off = 0;
|
||||
|
||||
for (size_t bit = 0; bit != 32; ++bit) {
|
||||
uint32_t valBit = (data >> off) & 1;
|
||||
uint32_t maskBit = (mask >> bit) & 1;
|
||||
if (maskBit) {
|
||||
result |= (valBit << bit);
|
||||
++off;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const {
|
||||
switch (type) {
|
||||
case R_HEX_B9_PCREL:
|
||||
case R_HEX_B9_PCREL_X:
|
||||
case R_HEX_B13_PCREL:
|
||||
case R_HEX_B15_PCREL:
|
||||
case R_HEX_B15_PCREL_X:
|
||||
case R_HEX_6_PCREL_X:
|
||||
case R_HEX_32_PCREL:
|
||||
return R_PC;
|
||||
case R_HEX_B22_PCREL:
|
||||
case R_HEX_PLT_B22_PCREL:
|
||||
case R_HEX_B22_PCREL_X:
|
||||
case R_HEX_B32_PCREL_X:
|
||||
return R_PLT_PC;
|
||||
case R_HEX_GOT_11_X:
|
||||
case R_HEX_GOT_16_X:
|
||||
case R_HEX_GOT_32_6_X:
|
||||
return R_HEXAGON_GOT;
|
||||
default:
|
||||
return R_ABS;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t findMaskR6(uint32_t insn) {
|
||||
// There are (arguably too) many relocation masks for the DSP's
|
||||
// R_HEX_6_X type. The table below is used to select the correct mask
|
||||
// for the given instruction.
|
||||
struct InstructionMask {
|
||||
uint32_t cmpMask;
|
||||
uint32_t relocMask;
|
||||
};
|
||||
|
||||
static const InstructionMask r6[] = {
|
||||
{0x38000000, 0x0000201f}, {0x39000000, 0x0000201f},
|
||||
{0x3e000000, 0x00001f80}, {0x3f000000, 0x00001f80},
|
||||
{0x40000000, 0x000020f8}, {0x41000000, 0x000007e0},
|
||||
{0x42000000, 0x000020f8}, {0x43000000, 0x000007e0},
|
||||
{0x44000000, 0x000020f8}, {0x45000000, 0x000007e0},
|
||||
{0x46000000, 0x000020f8}, {0x47000000, 0x000007e0},
|
||||
{0x6a000000, 0x00001f80}, {0x7c000000, 0x001f2000},
|
||||
{0x9a000000, 0x00000f60}, {0x9b000000, 0x00000f60},
|
||||
{0x9c000000, 0x00000f60}, {0x9d000000, 0x00000f60},
|
||||
{0x9f000000, 0x001f0100}, {0xab000000, 0x0000003f},
|
||||
{0xad000000, 0x0000003f}, {0xaf000000, 0x00030078},
|
||||
{0xd7000000, 0x006020e0}, {0xd8000000, 0x006020e0},
|
||||
{0xdb000000, 0x006020e0}, {0xdf000000, 0x006020e0}};
|
||||
|
||||
// Duplex forms have a fixed mask and parse bits 15:14 are always
|
||||
// zero. Non-duplex insns will always have at least one bit set in the
|
||||
// parse field.
|
||||
if ((0xC000 & insn) == 0x0)
|
||||
return 0x03f00000;
|
||||
|
||||
for (InstructionMask i : r6)
|
||||
if ((0xff000000 & insn) == i.cmpMask)
|
||||
return i.relocMask;
|
||||
|
||||
error("unrecognized instruction for R_HEX_6 relocation: 0x" +
|
||||
utohexstr(insn));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t findMaskR8(uint32_t insn) {
|
||||
if ((0xff000000 & insn) == 0xde000000)
|
||||
return 0x00e020e8;
|
||||
if ((0xff000000 & insn) == 0x3c000000)
|
||||
return 0x0000207f;
|
||||
return 0x00001fe0;
|
||||
}
|
||||
|
||||
static uint32_t findMaskR11(uint32_t insn) {
|
||||
if ((0xff000000 & insn) == 0xa1000000)
|
||||
return 0x060020ff;
|
||||
return 0x06003fe0;
|
||||
}
|
||||
|
||||
static uint32_t findMaskR16(uint32_t insn) {
|
||||
if ((0xff000000 & insn) == 0x48000000)
|
||||
return 0x061f20ff;
|
||||
if ((0xff000000 & insn) == 0x49000000)
|
||||
return 0x061f3fe0;
|
||||
if ((0xff000000 & insn) == 0x78000000)
|
||||
return 0x00df3fe0;
|
||||
if ((0xff000000 & insn) == 0xb0000000)
|
||||
return 0x0fe03fe0;
|
||||
|
||||
error("unrecognized instruction for R_HEX_16_X relocation: 0x" +
|
||||
utohexstr(insn));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); }
|
||||
|
||||
void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
switch (type) {
|
||||
case R_HEX_NONE:
|
||||
break;
|
||||
case R_HEX_6_PCREL_X:
|
||||
case R_HEX_6_X:
|
||||
or32le(loc, applyMask(findMaskR6(read32le(loc)), val));
|
||||
break;
|
||||
case R_HEX_8_X:
|
||||
or32le(loc, applyMask(findMaskR8(read32le(loc)), val));
|
||||
break;
|
||||
case R_HEX_9_X:
|
||||
or32le(loc, applyMask(0x00003fe0, val & 0x3f));
|
||||
break;
|
||||
case R_HEX_10_X:
|
||||
or32le(loc, applyMask(0x00203fe0, val & 0x3f));
|
||||
break;
|
||||
case R_HEX_11_X:
|
||||
case R_HEX_GOT_11_X:
|
||||
or32le(loc, applyMask(findMaskR11(read32le(loc)), val & 0x3f));
|
||||
break;
|
||||
case R_HEX_12_X:
|
||||
or32le(loc, applyMask(0x000007e0, val));
|
||||
break;
|
||||
case R_HEX_16_X: // These relocs only have 6 effective bits.
|
||||
case R_HEX_GOT_16_X:
|
||||
or32le(loc, applyMask(findMaskR16(read32le(loc)), val & 0x3f));
|
||||
break;
|
||||
case R_HEX_32:
|
||||
case R_HEX_32_PCREL:
|
||||
or32le(loc, val);
|
||||
break;
|
||||
case R_HEX_32_6_X:
|
||||
case R_HEX_GOT_32_6_X:
|
||||
or32le(loc, applyMask(0x0fff3fff, val >> 6));
|
||||
break;
|
||||
case R_HEX_B9_PCREL:
|
||||
or32le(loc, applyMask(0x003000fe, val >> 2));
|
||||
break;
|
||||
case R_HEX_B9_PCREL_X:
|
||||
or32le(loc, applyMask(0x003000fe, val & 0x3f));
|
||||
break;
|
||||
case R_HEX_B13_PCREL:
|
||||
or32le(loc, applyMask(0x00202ffe, val >> 2));
|
||||
break;
|
||||
case R_HEX_B15_PCREL:
|
||||
or32le(loc, applyMask(0x00df20fe, val >> 2));
|
||||
break;
|
||||
case R_HEX_B15_PCREL_X:
|
||||
or32le(loc, applyMask(0x00df20fe, val & 0x3f));
|
||||
break;
|
||||
case R_HEX_B22_PCREL:
|
||||
case R_HEX_PLT_B22_PCREL:
|
||||
or32le(loc, applyMask(0x1ff3ffe, val >> 2));
|
||||
break;
|
||||
case R_HEX_B22_PCREL_X:
|
||||
or32le(loc, applyMask(0x1ff3ffe, val & 0x3f));
|
||||
break;
|
||||
case R_HEX_B32_PCREL_X:
|
||||
or32le(loc, applyMask(0x0fff3fff, val >> 6));
|
||||
break;
|
||||
case R_HEX_HI16:
|
||||
or32le(loc, applyMask(0x00c03fff, val >> 16));
|
||||
break;
|
||||
case R_HEX_LO16:
|
||||
or32le(loc, applyMask(0x00c03fff, val));
|
||||
break;
|
||||
default:
|
||||
error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Hexagon::writePltHeader(uint8_t *buf) const {
|
||||
const uint8_t pltData[] = {
|
||||
0x00, 0x40, 0x00, 0x00, // { immext (#0)
|
||||
0x1c, 0xc0, 0x49, 0x6a, // r28 = add (pc, ##GOT0@PCREL) } # @GOT0
|
||||
0x0e, 0x42, 0x9c, 0xe2, // { r14 -= add (r28, #16) # offset of GOTn
|
||||
0x4f, 0x40, 0x9c, 0x91, // r15 = memw (r28 + #8) # object ID at GOT2
|
||||
0x3c, 0xc0, 0x9c, 0x91, // r28 = memw (r28 + #4) }# dynamic link at GOT1
|
||||
0x0e, 0x42, 0x0e, 0x8c, // { r14 = asr (r14, #2) # index of PLTn
|
||||
0x00, 0xc0, 0x9c, 0x52, // jumpr r28 } # call dynamic linker
|
||||
0x0c, 0xdb, 0x00, 0x54, // trap0(#0xdb) # bring plt0 into 16byte alignment
|
||||
};
|
||||
memcpy(buf, pltData, sizeof(pltData));
|
||||
|
||||
// Offset from PLT0 to the GOT.
|
||||
uint64_t off = in.gotPlt->getVA() - in.plt->getVA();
|
||||
relocateOne(buf, R_HEX_B32_PCREL_X, off);
|
||||
relocateOne(buf + 4, R_HEX_6_PCREL_X, off);
|
||||
}
|
||||
|
||||
void Hexagon::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
|
||||
uint64_t pltEntryAddr, int32_t index,
|
||||
unsigned relOff) const {
|
||||
const uint8_t inst[] = {
|
||||
0x00, 0x40, 0x00, 0x00, // { immext (#0)
|
||||
0x0e, 0xc0, 0x49, 0x6a, // r14 = add (pc, ##GOTn@PCREL) }
|
||||
0x1c, 0xc0, 0x8e, 0x91, // r28 = memw (r14)
|
||||
0x00, 0xc0, 0x9c, 0x52, // jumpr r28
|
||||
};
|
||||
memcpy(buf, inst, sizeof(inst));
|
||||
|
||||
relocateOne(buf, R_HEX_B32_PCREL_X, gotPltEntryAddr - pltEntryAddr);
|
||||
relocateOne(buf + 4, R_HEX_6_PCREL_X, gotPltEntryAddr - pltEntryAddr);
|
||||
}
|
||||
|
||||
TargetInfo *elf::getHexagonTargetInfo() {
|
||||
static Hexagon target;
|
||||
return ⌖
|
||||
}
|
||||
93
deps/lld/ELF/Arch/MSP430.cpp
vendored
93
deps/lld/ELF/Arch/MSP430.cpp
vendored
@ -1,93 +0,0 @@
|
||||
//===- MSP430.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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The MSP430 is a 16-bit microcontroller RISC architecture. The instruction set
|
||||
// has only 27 core instructions orthogonally augmented with a variety
|
||||
// of addressing modes for source and destination operands. Entire address space
|
||||
// of MSP430 is 64KB (the extended MSP430X architecture is not considered here).
|
||||
// A typical MSP430 MCU has several kilobytes of RAM and ROM, plenty
|
||||
// of peripherals and is generally optimized for a low power consumption.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "Target.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace {
|
||||
class MSP430 final : public TargetInfo {
|
||||
public:
|
||||
MSP430();
|
||||
RelExpr getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const override;
|
||||
void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
MSP430::MSP430() {
|
||||
// mov.b #0, r3
|
||||
trapInstr = {0x43, 0x43, 0x43, 0x43};
|
||||
}
|
||||
|
||||
RelExpr MSP430::getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const {
|
||||
switch (type) {
|
||||
case R_MSP430_10_PCREL:
|
||||
case R_MSP430_16_PCREL:
|
||||
case R_MSP430_16_PCREL_BYTE:
|
||||
case R_MSP430_2X_PCREL:
|
||||
case R_MSP430_RL_PCREL:
|
||||
case R_MSP430_SYM_DIFF:
|
||||
return R_PC;
|
||||
default:
|
||||
return R_ABS;
|
||||
}
|
||||
}
|
||||
|
||||
void MSP430::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
switch (type) {
|
||||
case R_MSP430_8:
|
||||
checkIntUInt(loc, val, 8, type);
|
||||
*loc = val;
|
||||
break;
|
||||
case R_MSP430_16:
|
||||
case R_MSP430_16_PCREL:
|
||||
case R_MSP430_16_BYTE:
|
||||
case R_MSP430_16_PCREL_BYTE:
|
||||
checkIntUInt(loc, val, 16, type);
|
||||
write16le(loc, val);
|
||||
break;
|
||||
case R_MSP430_32:
|
||||
checkIntUInt(loc, val, 32, type);
|
||||
write32le(loc, val);
|
||||
break;
|
||||
case R_MSP430_10_PCREL: {
|
||||
int16_t offset = ((int16_t)val >> 1) - 1;
|
||||
checkInt(loc, offset, 10, type);
|
||||
write16le(loc, (read16le(loc) & 0xFC00) | (offset & 0x3FF));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
|
||||
}
|
||||
}
|
||||
|
||||
TargetInfo *elf::getMSP430TargetInfo() {
|
||||
static MSP430 target;
|
||||
return ⌖
|
||||
}
|
||||
741
deps/lld/ELF/Arch/Mips.cpp
vendored
741
deps/lld/ELF/Arch/Mips.cpp
vendored
@ -1,741 +0,0 @@
|
||||
//===- MIPS.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 "InputFiles.h"
|
||||
#include "OutputSections.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "Thunks.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace {
|
||||
template <class ELFT> class MIPS final : public TargetInfo {
|
||||
public:
|
||||
MIPS();
|
||||
uint32_t calcEFlags() const override;
|
||||
RelExpr getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const override;
|
||||
int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
|
||||
RelType getDynRel(RelType type) const override;
|
||||
void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
|
||||
void writePltHeader(uint8_t *buf) const override;
|
||||
void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
|
||||
int32_t index, unsigned relOff) const override;
|
||||
bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
|
||||
uint64_t branchAddr, const Symbol &s) const override;
|
||||
void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
bool usesOnlyLowPageBits(RelType type) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
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;
|
||||
|
||||
// Set `sigrie 1` as a trap instruction.
|
||||
write32(trapInstr.data(), 0x04170001);
|
||||
|
||||
if (ELFT::Is64Bits) {
|
||||
relativeRel = (R_MIPS_64 << 8) | R_MIPS_REL32;
|
||||
symbolicRel = R_MIPS_64;
|
||||
tlsGotRel = R_MIPS_TLS_TPREL64;
|
||||
tlsModuleIndexRel = R_MIPS_TLS_DTPMOD64;
|
||||
tlsOffsetRel = R_MIPS_TLS_DTPREL64;
|
||||
} else {
|
||||
relativeRel = R_MIPS_REL32;
|
||||
symbolicRel = R_MIPS_32;
|
||||
tlsGotRel = R_MIPS_TLS_TPREL32;
|
||||
tlsModuleIndexRel = R_MIPS_TLS_DTPMOD32;
|
||||
tlsOffsetRel = R_MIPS_TLS_DTPREL32;
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT> uint32_t MIPS<ELFT>::calcEFlags() const {
|
||||
return calcMipsEFlags<ELFT>();
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
RelExpr MIPS<ELFT>::getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const {
|
||||
// See comment in the calculateMipsRelChain.
|
||||
if (ELFT::Is64Bits || config->mipsN32Abi)
|
||||
type &= 0xff;
|
||||
|
||||
switch (type) {
|
||||
case R_MIPS_JALR:
|
||||
case R_MICROMIPS_JALR:
|
||||
return R_HINT;
|
||||
case R_MIPS_GPREL16:
|
||||
case R_MIPS_GPREL32:
|
||||
case R_MICROMIPS_GPREL16:
|
||||
case R_MICROMIPS_GPREL7_S2:
|
||||
return R_MIPS_GOTREL;
|
||||
case R_MIPS_26:
|
||||
case R_MICROMIPS_26_S1:
|
||||
return R_PLT;
|
||||
case R_MICROMIPS_PC26_S1:
|
||||
return R_PLT_PC;
|
||||
case R_MIPS_HI16:
|
||||
case R_MIPS_LO16:
|
||||
case R_MIPS_HIGHER:
|
||||
case R_MIPS_HIGHEST:
|
||||
case R_MICROMIPS_HI16:
|
||||
case R_MICROMIPS_LO16:
|
||||
// R_MIPS_HI16/R_MIPS_LO16 relocations against _gp_disp calculate
|
||||
// offset between start of function and 'gp' value which by default
|
||||
// equal to the start of .got section. In that case we consider these
|
||||
// relocations as relative.
|
||||
if (&s == ElfSym::mipsGpDisp)
|
||||
return R_MIPS_GOT_GP_PC;
|
||||
if (&s == ElfSym::mipsLocalGp)
|
||||
return R_MIPS_GOT_GP;
|
||||
LLVM_FALLTHROUGH;
|
||||
case R_MIPS_32:
|
||||
case R_MIPS_64:
|
||||
case R_MIPS_GOT_OFST:
|
||||
case R_MIPS_SUB:
|
||||
case R_MIPS_TLS_DTPREL_HI16:
|
||||
case R_MIPS_TLS_DTPREL_LO16:
|
||||
case R_MIPS_TLS_DTPREL32:
|
||||
case R_MIPS_TLS_DTPREL64:
|
||||
case R_MIPS_TLS_TPREL_HI16:
|
||||
case R_MIPS_TLS_TPREL_LO16:
|
||||
case R_MIPS_TLS_TPREL32:
|
||||
case R_MIPS_TLS_TPREL64:
|
||||
case R_MICROMIPS_TLS_DTPREL_HI16:
|
||||
case R_MICROMIPS_TLS_DTPREL_LO16:
|
||||
case R_MICROMIPS_TLS_TPREL_HI16:
|
||||
case R_MICROMIPS_TLS_TPREL_LO16:
|
||||
return R_ABS;
|
||||
case R_MIPS_PC32:
|
||||
case R_MIPS_PC16:
|
||||
case R_MIPS_PC19_S2:
|
||||
case R_MIPS_PC21_S2:
|
||||
case R_MIPS_PC26_S2:
|
||||
case R_MIPS_PCHI16:
|
||||
case R_MIPS_PCLO16:
|
||||
case R_MICROMIPS_PC7_S1:
|
||||
case R_MICROMIPS_PC10_S1:
|
||||
case R_MICROMIPS_PC16_S1:
|
||||
case R_MICROMIPS_PC18_S3:
|
||||
case R_MICROMIPS_PC19_S2:
|
||||
case R_MICROMIPS_PC23_S2:
|
||||
case R_MICROMIPS_PC21_S1:
|
||||
return R_PC;
|
||||
case R_MIPS_GOT16:
|
||||
case R_MICROMIPS_GOT16:
|
||||
if (s.isLocal())
|
||||
return R_MIPS_GOT_LOCAL_PAGE;
|
||||
LLVM_FALLTHROUGH;
|
||||
case R_MIPS_CALL16:
|
||||
case R_MIPS_GOT_DISP:
|
||||
case R_MIPS_TLS_GOTTPREL:
|
||||
case R_MICROMIPS_CALL16:
|
||||
case R_MICROMIPS_TLS_GOTTPREL:
|
||||
return R_MIPS_GOT_OFF;
|
||||
case R_MIPS_CALL_HI16:
|
||||
case R_MIPS_CALL_LO16:
|
||||
case R_MIPS_GOT_HI16:
|
||||
case R_MIPS_GOT_LO16:
|
||||
case R_MICROMIPS_CALL_HI16:
|
||||
case R_MICROMIPS_CALL_LO16:
|
||||
case R_MICROMIPS_GOT_HI16:
|
||||
case R_MICROMIPS_GOT_LO16:
|
||||
return R_MIPS_GOT_OFF32;
|
||||
case R_MIPS_GOT_PAGE:
|
||||
return R_MIPS_GOT_LOCAL_PAGE;
|
||||
case R_MIPS_TLS_GD:
|
||||
case R_MICROMIPS_TLS_GD:
|
||||
return R_MIPS_TLSGD;
|
||||
case R_MIPS_TLS_LDM:
|
||||
case R_MICROMIPS_TLS_LDM:
|
||||
return R_MIPS_TLSLD;
|
||||
case R_MIPS_NONE:
|
||||
return R_NONE;
|
||||
default:
|
||||
error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
|
||||
") against symbol " + toString(s));
|
||||
return R_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT> RelType MIPS<ELFT>::getDynRel(RelType type) const {
|
||||
if (type == symbolicRel)
|
||||
return type;
|
||||
return R_MIPS_NONE;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void MIPS<ELFT>::writeGotPlt(uint8_t *buf, const Symbol &) const {
|
||||
uint64_t va = in.plt->getVA();
|
||||
if (isMicroMips())
|
||||
va |= 1;
|
||||
write32<ELFT::TargetEndianness>(buf, va);
|
||||
}
|
||||
|
||||
template <endianness E> static uint32_t readShuffle(const uint8_t *loc) {
|
||||
// The major opcode of a microMIPS instruction needs to appear
|
||||
// in the first 16-bit word (lowest address) for efficient hardware
|
||||
// decode so that it knows if the instruction is 16-bit or 32-bit
|
||||
// as early as possible. To do so, little-endian binaries keep 16-bit
|
||||
// words in a big-endian order. That is why we have to swap these
|
||||
// words to get a correct value.
|
||||
uint32_t v = read32<E>(loc);
|
||||
if (E == support::little)
|
||||
return (v << 16) | (v >> 16);
|
||||
return v;
|
||||
}
|
||||
|
||||
template <endianness E>
|
||||
static void writeValue(uint8_t *loc, uint64_t v, uint8_t bitsSize,
|
||||
uint8_t shift) {
|
||||
uint32_t instr = read32<E>(loc);
|
||||
uint32_t mask = 0xffffffff >> (32 - bitsSize);
|
||||
uint32_t data = (instr & ~mask) | ((v >> shift) & mask);
|
||||
write32<E>(loc, data);
|
||||
}
|
||||
|
||||
template <endianness E>
|
||||
static void writeShuffleValue(uint8_t *loc, uint64_t v, uint8_t bitsSize,
|
||||
uint8_t shift) {
|
||||
// See comments in readShuffle for purpose of this code.
|
||||
uint16_t *words = (uint16_t *)loc;
|
||||
if (E == support::little)
|
||||
std::swap(words[0], words[1]);
|
||||
|
||||
writeValue<E>(loc, v, bitsSize, shift);
|
||||
|
||||
if (E == support::little)
|
||||
std::swap(words[0], words[1]);
|
||||
}
|
||||
|
||||
template <endianness E>
|
||||
static void writeMicroRelocation16(uint8_t *loc, uint64_t v, uint8_t bitsSize,
|
||||
uint8_t shift) {
|
||||
uint16_t instr = read16<E>(loc);
|
||||
uint16_t mask = 0xffff >> (16 - bitsSize);
|
||||
uint16_t data = (instr & ~mask) | ((v >> shift) & mask);
|
||||
write16<E>(loc, data);
|
||||
}
|
||||
|
||||
template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *buf) const {
|
||||
const endianness e = ELFT::TargetEndianness;
|
||||
if (isMicroMips()) {
|
||||
uint64_t gotPlt = in.gotPlt->getVA();
|
||||
uint64_t plt = in.plt->getVA();
|
||||
// Overwrite trap instructions written by Writer::writeTrapInstr.
|
||||
memset(buf, 0, pltHeaderSize);
|
||||
|
||||
write16<e>(buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - .
|
||||
write16<e>(buf + 4, 0xff23); // lw $25, 0($3)
|
||||
write16<e>(buf + 8, 0x0535); // subu16 $2, $2, $3
|
||||
write16<e>(buf + 10, 0x2525); // srl16 $2, $2, 2
|
||||
write16<e>(buf + 12, 0x3302); // addiu $24, $2, -2
|
||||
write16<e>(buf + 14, 0xfffe);
|
||||
write16<e>(buf + 16, 0x0dff); // move $15, $31
|
||||
if (isMipsR6()) {
|
||||
write16<e>(buf + 18, 0x0f83); // move $28, $3
|
||||
write16<e>(buf + 20, 0x472b); // jalrc $25
|
||||
write16<e>(buf + 22, 0x0c00); // nop
|
||||
relocateOne(buf, R_MICROMIPS_PC19_S2, gotPlt - plt);
|
||||
} else {
|
||||
write16<e>(buf + 18, 0x45f9); // jalrc $25
|
||||
write16<e>(buf + 20, 0x0f83); // move $28, $3
|
||||
write16<e>(buf + 22, 0x0c00); // nop
|
||||
relocateOne(buf, R_MICROMIPS_PC23_S2, gotPlt - plt);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (config->mipsN32Abi) {
|
||||
write32<e>(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
|
||||
write32<e>(buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14)
|
||||
write32<e>(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
|
||||
write32<e>(buf + 12, 0x030ec023); // subu $24, $24, $14
|
||||
write32<e>(buf + 16, 0x03e07825); // move $15, $31
|
||||
write32<e>(buf + 20, 0x0018c082); // srl $24, $24, 2
|
||||
} else if (ELFT::Is64Bits) {
|
||||
write32<e>(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
|
||||
write32<e>(buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14)
|
||||
write32<e>(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
|
||||
write32<e>(buf + 12, 0x030ec023); // subu $24, $24, $14
|
||||
write32<e>(buf + 16, 0x03e07825); // move $15, $31
|
||||
write32<e>(buf + 20, 0x0018c0c2); // srl $24, $24, 3
|
||||
} else {
|
||||
write32<e>(buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0])
|
||||
write32<e>(buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28)
|
||||
write32<e>(buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0])
|
||||
write32<e>(buf + 12, 0x031cc023); // subu $24, $24, $28
|
||||
write32<e>(buf + 16, 0x03e07825); // move $15, $31
|
||||
write32<e>(buf + 20, 0x0018c082); // srl $24, $24, 2
|
||||
}
|
||||
|
||||
uint32_t jalrInst = config->zHazardplt ? 0x0320fc09 : 0x0320f809;
|
||||
write32<e>(buf + 24, jalrInst); // jalr.hb $25 or jalr $25
|
||||
write32<e>(buf + 28, 0x2718fffe); // subu $24, $24, 2
|
||||
|
||||
uint64_t gotPlt = in.gotPlt->getVA();
|
||||
writeValue<e>(buf, gotPlt + 0x8000, 16, 16);
|
||||
writeValue<e>(buf + 4, gotPlt, 16, 0);
|
||||
writeValue<e>(buf + 8, gotPlt, 16, 0);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void MIPS<ELFT>::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
|
||||
uint64_t pltEntryAddr, int32_t index,
|
||||
unsigned relOff) const {
|
||||
const endianness e = ELFT::TargetEndianness;
|
||||
if (isMicroMips()) {
|
||||
// Overwrite trap instructions written by Writer::writeTrapInstr.
|
||||
memset(buf, 0, pltEntrySize);
|
||||
|
||||
if (isMipsR6()) {
|
||||
write16<e>(buf, 0x7840); // addiupc $2, (GOTPLT) - .
|
||||
write16<e>(buf + 4, 0xff22); // lw $25, 0($2)
|
||||
write16<e>(buf + 8, 0x0f02); // move $24, $2
|
||||
write16<e>(buf + 10, 0x4723); // jrc $25 / jr16 $25
|
||||
relocateOne(buf, R_MICROMIPS_PC19_S2, gotPltEntryAddr - pltEntryAddr);
|
||||
} else {
|
||||
write16<e>(buf, 0x7900); // addiupc $2, (GOTPLT) - .
|
||||
write16<e>(buf + 4, 0xff22); // lw $25, 0($2)
|
||||
write16<e>(buf + 8, 0x4599); // jrc $25 / jr16 $25
|
||||
write16<e>(buf + 10, 0x0f02); // move $24, $2
|
||||
relocateOne(buf, R_MICROMIPS_PC23_S2, gotPltEntryAddr - pltEntryAddr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t loadInst = ELFT::Is64Bits ? 0xddf90000 : 0x8df90000;
|
||||
uint32_t jrInst = isMipsR6() ? (config->zHazardplt ? 0x03200409 : 0x03200009)
|
||||
: (config->zHazardplt ? 0x03200408 : 0x03200008);
|
||||
uint32_t addInst = ELFT::Is64Bits ? 0x65f80000 : 0x25f80000;
|
||||
|
||||
write32<e>(buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry)
|
||||
write32<e>(buf + 4, loadInst); // l[wd] $25, %lo(.got.plt entry)($15)
|
||||
write32<e>(buf + 8, jrInst); // jr $25 / jr.hb $25
|
||||
write32<e>(buf + 12, addInst); // [d]addiu $24, $15, %lo(.got.plt entry)
|
||||
writeValue<e>(buf, gotPltEntryAddr + 0x8000, 16, 16);
|
||||
writeValue<e>(buf + 4, gotPltEntryAddr, 16, 0);
|
||||
writeValue<e>(buf + 12, gotPltEntryAddr, 16, 0);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
bool MIPS<ELFT>::needsThunk(RelExpr expr, RelType type, const InputFile *file,
|
||||
uint64_t branchAddr, const Symbol &s) const {
|
||||
// Any MIPS PIC code function is invoked with its address in register $t9.
|
||||
// So if we have a branch instruction from non-PIC code to the PIC one
|
||||
// we cannot make the jump directly and need to create a small stubs
|
||||
// to save the target function address.
|
||||
// See page 3-38 ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
|
||||
if (type != R_MIPS_26 && type != R_MIPS_PC26_S2 &&
|
||||
type != R_MICROMIPS_26_S1 && type != R_MICROMIPS_PC26_S1)
|
||||
return false;
|
||||
auto *f = dyn_cast_or_null<ObjFile<ELFT>>(file);
|
||||
if (!f)
|
||||
return false;
|
||||
// If current file has PIC code, LA25 stub is not required.
|
||||
if (f->getObj().getHeader()->e_flags & EF_MIPS_PIC)
|
||||
return false;
|
||||
auto *d = dyn_cast<Defined>(&s);
|
||||
// LA25 is required if target file has PIC code
|
||||
// or target symbol is a PIC symbol.
|
||||
return d && isMipsPIC<ELFT>(d);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *buf, RelType type) const {
|
||||
const endianness e = ELFT::TargetEndianness;
|
||||
switch (type) {
|
||||
case R_MIPS_32:
|
||||
case R_MIPS_GPREL32:
|
||||
case R_MIPS_TLS_DTPREL32:
|
||||
case R_MIPS_TLS_TPREL32:
|
||||
return SignExtend64<32>(read32<e>(buf));
|
||||
case R_MIPS_26:
|
||||
// FIXME (simon): If the relocation target symbol is not a PLT entry
|
||||
// we should use another expression for calculation:
|
||||
// ((A << 2) | (P & 0xf0000000)) >> 2
|
||||
return SignExtend64<28>(read32<e>(buf) << 2);
|
||||
case R_MIPS_GOT16:
|
||||
case R_MIPS_HI16:
|
||||
case R_MIPS_PCHI16:
|
||||
return SignExtend64<16>(read32<e>(buf)) << 16;
|
||||
case R_MIPS_GPREL16:
|
||||
case R_MIPS_LO16:
|
||||
case R_MIPS_PCLO16:
|
||||
case R_MIPS_TLS_DTPREL_HI16:
|
||||
case R_MIPS_TLS_DTPREL_LO16:
|
||||
case R_MIPS_TLS_TPREL_HI16:
|
||||
case R_MIPS_TLS_TPREL_LO16:
|
||||
return SignExtend64<16>(read32<e>(buf));
|
||||
case R_MICROMIPS_GOT16:
|
||||
case R_MICROMIPS_HI16:
|
||||
return SignExtend64<16>(readShuffle<e>(buf)) << 16;
|
||||
case R_MICROMIPS_GPREL16:
|
||||
case R_MICROMIPS_LO16:
|
||||
case R_MICROMIPS_TLS_DTPREL_HI16:
|
||||
case R_MICROMIPS_TLS_DTPREL_LO16:
|
||||
case R_MICROMIPS_TLS_TPREL_HI16:
|
||||
case R_MICROMIPS_TLS_TPREL_LO16:
|
||||
return SignExtend64<16>(readShuffle<e>(buf));
|
||||
case R_MICROMIPS_GPREL7_S2:
|
||||
return SignExtend64<9>(readShuffle<e>(buf) << 2);
|
||||
case R_MIPS_PC16:
|
||||
return SignExtend64<18>(read32<e>(buf) << 2);
|
||||
case R_MIPS_PC19_S2:
|
||||
return SignExtend64<21>(read32<e>(buf) << 2);
|
||||
case R_MIPS_PC21_S2:
|
||||
return SignExtend64<23>(read32<e>(buf) << 2);
|
||||
case R_MIPS_PC26_S2:
|
||||
return SignExtend64<28>(read32<e>(buf) << 2);
|
||||
case R_MIPS_PC32:
|
||||
return SignExtend64<32>(read32<e>(buf));
|
||||
case R_MICROMIPS_26_S1:
|
||||
return SignExtend64<27>(readShuffle<e>(buf) << 1);
|
||||
case R_MICROMIPS_PC7_S1:
|
||||
return SignExtend64<8>(read16<e>(buf) << 1);
|
||||
case R_MICROMIPS_PC10_S1:
|
||||
return SignExtend64<11>(read16<e>(buf) << 1);
|
||||
case R_MICROMIPS_PC16_S1:
|
||||
return SignExtend64<17>(readShuffle<e>(buf) << 1);
|
||||
case R_MICROMIPS_PC18_S3:
|
||||
return SignExtend64<21>(readShuffle<e>(buf) << 3);
|
||||
case R_MICROMIPS_PC19_S2:
|
||||
return SignExtend64<21>(readShuffle<e>(buf) << 2);
|
||||
case R_MICROMIPS_PC21_S1:
|
||||
return SignExtend64<22>(readShuffle<e>(buf) << 1);
|
||||
case R_MICROMIPS_PC23_S2:
|
||||
return SignExtend64<25>(readShuffle<e>(buf) << 2);
|
||||
case R_MICROMIPS_PC26_S1:
|
||||
return SignExtend64<27>(readShuffle<e>(buf) << 1);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static std::pair<uint32_t, uint64_t>
|
||||
calculateMipsRelChain(uint8_t *loc, RelType type, uint64_t val) {
|
||||
// MIPS N64 ABI packs multiple relocations into the single relocation
|
||||
// record. In general, all up to three relocations can have arbitrary
|
||||
// types. In fact, Clang and GCC uses only a few combinations. For now,
|
||||
// we support two of them. That is allow to pass at least all LLVM
|
||||
// test suite cases.
|
||||
// <any relocation> / R_MIPS_SUB / R_MIPS_HI16 | R_MIPS_LO16
|
||||
// <any relocation> / R_MIPS_64 / R_MIPS_NONE
|
||||
// The first relocation is a 'real' relocation which is calculated
|
||||
// using the corresponding symbol's value. The second and the third
|
||||
// relocations used to modify result of the first one: extend it to
|
||||
// 64-bit, extract high or low part etc. For details, see part 2.9 Relocation
|
||||
// at the https://dmz-portal.mips.com/mw/images/8/82/007-4658-001.pdf
|
||||
RelType type2 = (type >> 8) & 0xff;
|
||||
RelType type3 = (type >> 16) & 0xff;
|
||||
if (type2 == R_MIPS_NONE && type3 == R_MIPS_NONE)
|
||||
return std::make_pair(type, val);
|
||||
if (type2 == R_MIPS_64 && type3 == R_MIPS_NONE)
|
||||
return std::make_pair(type2, val);
|
||||
if (type2 == R_MIPS_SUB && (type3 == R_MIPS_HI16 || type3 == R_MIPS_LO16))
|
||||
return std::make_pair(type3, -val);
|
||||
error(getErrorLocation(loc) + "unsupported relocations combination " +
|
||||
Twine(type));
|
||||
return std::make_pair(type & 0xff, val);
|
||||
}
|
||||
|
||||
static bool isBranchReloc(RelType type) {
|
||||
return type == R_MIPS_26 || type == R_MIPS_PC26_S2 ||
|
||||
type == R_MIPS_PC21_S2 || type == R_MIPS_PC16;
|
||||
}
|
||||
|
||||
static bool isMicroBranchReloc(RelType type) {
|
||||
return type == R_MICROMIPS_26_S1 || type == R_MICROMIPS_PC16_S1 ||
|
||||
type == R_MICROMIPS_PC10_S1 || type == R_MICROMIPS_PC7_S1;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static uint64_t fixupCrossModeJump(uint8_t *loc, RelType type, uint64_t val) {
|
||||
// Here we need to detect jump/branch from regular MIPS code
|
||||
// to a microMIPS target and vice versa. In that cases jump
|
||||
// instructions need to be replaced by their "cross-mode"
|
||||
// equivalents.
|
||||
const endianness e = ELFT::TargetEndianness;
|
||||
bool isMicroTgt = val & 0x1;
|
||||
bool isCrossJump = (isMicroTgt && isBranchReloc(type)) ||
|
||||
(!isMicroTgt && isMicroBranchReloc(type));
|
||||
if (!isCrossJump)
|
||||
return val;
|
||||
|
||||
switch (type) {
|
||||
case R_MIPS_26: {
|
||||
uint32_t inst = read32<e>(loc) >> 26;
|
||||
if (inst == 0x3 || inst == 0x1d) { // JAL or JALX
|
||||
writeValue<e>(loc, 0x1d << 26, 32, 0);
|
||||
return val;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case R_MICROMIPS_26_S1: {
|
||||
uint32_t inst = readShuffle<e>(loc) >> 26;
|
||||
if (inst == 0x3d || inst == 0x3c) { // JAL32 or JALX32
|
||||
val >>= 1;
|
||||
writeShuffleValue<e>(loc, 0x3c << 26, 32, 0);
|
||||
return val;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case R_MIPS_PC26_S2:
|
||||
case R_MIPS_PC21_S2:
|
||||
case R_MIPS_PC16:
|
||||
case R_MICROMIPS_PC16_S1:
|
||||
case R_MICROMIPS_PC10_S1:
|
||||
case R_MICROMIPS_PC7_S1:
|
||||
// FIXME (simon): Support valid branch relocations.
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("unexpected jump/branch relocation");
|
||||
}
|
||||
|
||||
error(getErrorLocation(loc) +
|
||||
"unsupported jump/branch instruction between ISA modes referenced by " +
|
||||
toString(type) + " relocation");
|
||||
return val;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
const endianness e = ELFT::TargetEndianness;
|
||||
|
||||
if (ELFT::Is64Bits || config->mipsN32Abi)
|
||||
std::tie(type, val) = calculateMipsRelChain(loc, type, val);
|
||||
|
||||
// Detect cross-mode jump/branch and fix instruction.
|
||||
val = fixupCrossModeJump<ELFT>(loc, type, val);
|
||||
|
||||
// Thread pointer and DRP offsets from the start of TLS data area.
|
||||
// https://www.linux-mips.org/wiki/NPTL
|
||||
if (type == R_MIPS_TLS_DTPREL_HI16 || type == R_MIPS_TLS_DTPREL_LO16 ||
|
||||
type == R_MIPS_TLS_DTPREL32 || type == R_MIPS_TLS_DTPREL64 ||
|
||||
type == R_MICROMIPS_TLS_DTPREL_HI16 ||
|
||||
type == R_MICROMIPS_TLS_DTPREL_LO16) {
|
||||
val -= 0x8000;
|
||||
} else if (type == R_MIPS_TLS_TPREL_HI16 || type == R_MIPS_TLS_TPREL_LO16 ||
|
||||
type == R_MIPS_TLS_TPREL32 || type == R_MIPS_TLS_TPREL64 ||
|
||||
type == R_MICROMIPS_TLS_TPREL_HI16 ||
|
||||
type == R_MICROMIPS_TLS_TPREL_LO16) {
|
||||
val -= 0x7000;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case R_MIPS_32:
|
||||
case R_MIPS_GPREL32:
|
||||
case R_MIPS_TLS_DTPREL32:
|
||||
case R_MIPS_TLS_TPREL32:
|
||||
write32<e>(loc, val);
|
||||
break;
|
||||
case R_MIPS_64:
|
||||
case R_MIPS_TLS_DTPREL64:
|
||||
case R_MIPS_TLS_TPREL64:
|
||||
write64<e>(loc, val);
|
||||
break;
|
||||
case R_MIPS_26:
|
||||
writeValue<e>(loc, val, 26, 2);
|
||||
break;
|
||||
case R_MIPS_GOT16:
|
||||
// The R_MIPS_GOT16 relocation's value in "relocatable" linking mode
|
||||
// is updated addend (not a GOT index). In that case write high 16 bits
|
||||
// to store a correct addend value.
|
||||
if (config->relocatable) {
|
||||
writeValue<e>(loc, val + 0x8000, 16, 16);
|
||||
} else {
|
||||
checkInt(loc, val, 16, type);
|
||||
writeValue<e>(loc, val, 16, 0);
|
||||
}
|
||||
break;
|
||||
case R_MICROMIPS_GOT16:
|
||||
if (config->relocatable) {
|
||||
writeShuffleValue<e>(loc, val + 0x8000, 16, 16);
|
||||
} else {
|
||||
checkInt(loc, val, 16, type);
|
||||
writeShuffleValue<e>(loc, val, 16, 0);
|
||||
}
|
||||
break;
|
||||
case R_MIPS_CALL16:
|
||||
case R_MIPS_GOT_DISP:
|
||||
case R_MIPS_GOT_PAGE:
|
||||
case R_MIPS_GPREL16:
|
||||
case R_MIPS_TLS_GD:
|
||||
case R_MIPS_TLS_GOTTPREL:
|
||||
case R_MIPS_TLS_LDM:
|
||||
checkInt(loc, val, 16, type);
|
||||
LLVM_FALLTHROUGH;
|
||||
case R_MIPS_CALL_LO16:
|
||||
case R_MIPS_GOT_LO16:
|
||||
case R_MIPS_GOT_OFST:
|
||||
case R_MIPS_LO16:
|
||||
case R_MIPS_PCLO16:
|
||||
case R_MIPS_TLS_DTPREL_LO16:
|
||||
case R_MIPS_TLS_TPREL_LO16:
|
||||
writeValue<e>(loc, val, 16, 0);
|
||||
break;
|
||||
case R_MICROMIPS_GPREL16:
|
||||
case R_MICROMIPS_TLS_GD:
|
||||
case R_MICROMIPS_TLS_LDM:
|
||||
checkInt(loc, val, 16, type);
|
||||
writeShuffleValue<e>(loc, val, 16, 0);
|
||||
break;
|
||||
case R_MICROMIPS_CALL16:
|
||||
case R_MICROMIPS_CALL_LO16:
|
||||
case R_MICROMIPS_LO16:
|
||||
case R_MICROMIPS_TLS_DTPREL_LO16:
|
||||
case R_MICROMIPS_TLS_GOTTPREL:
|
||||
case R_MICROMIPS_TLS_TPREL_LO16:
|
||||
writeShuffleValue<e>(loc, val, 16, 0);
|
||||
break;
|
||||
case R_MICROMIPS_GPREL7_S2:
|
||||
checkInt(loc, val, 7, type);
|
||||
writeShuffleValue<e>(loc, val, 7, 2);
|
||||
break;
|
||||
case R_MIPS_CALL_HI16:
|
||||
case R_MIPS_GOT_HI16:
|
||||
case R_MIPS_HI16:
|
||||
case R_MIPS_PCHI16:
|
||||
case R_MIPS_TLS_DTPREL_HI16:
|
||||
case R_MIPS_TLS_TPREL_HI16:
|
||||
writeValue<e>(loc, val + 0x8000, 16, 16);
|
||||
break;
|
||||
case R_MICROMIPS_CALL_HI16:
|
||||
case R_MICROMIPS_GOT_HI16:
|
||||
case R_MICROMIPS_HI16:
|
||||
case R_MICROMIPS_TLS_DTPREL_HI16:
|
||||
case R_MICROMIPS_TLS_TPREL_HI16:
|
||||
writeShuffleValue<e>(loc, val + 0x8000, 16, 16);
|
||||
break;
|
||||
case R_MIPS_HIGHER:
|
||||
writeValue<e>(loc, val + 0x80008000, 16, 32);
|
||||
break;
|
||||
case R_MIPS_HIGHEST:
|
||||
writeValue<e>(loc, val + 0x800080008000, 16, 48);
|
||||
break;
|
||||
case R_MIPS_JALR:
|
||||
case R_MICROMIPS_JALR:
|
||||
// Ignore this optimization relocation for now
|
||||
break;
|
||||
case R_MIPS_PC16:
|
||||
checkAlignment(loc, val, 4, type);
|
||||
checkInt(loc, val, 18, type);
|
||||
writeValue<e>(loc, val, 16, 2);
|
||||
break;
|
||||
case R_MIPS_PC19_S2:
|
||||
checkAlignment(loc, val, 4, type);
|
||||
checkInt(loc, val, 21, type);
|
||||
writeValue<e>(loc, val, 19, 2);
|
||||
break;
|
||||
case R_MIPS_PC21_S2:
|
||||
checkAlignment(loc, val, 4, type);
|
||||
checkInt(loc, val, 23, type);
|
||||
writeValue<e>(loc, val, 21, 2);
|
||||
break;
|
||||
case R_MIPS_PC26_S2:
|
||||
checkAlignment(loc, val, 4, type);
|
||||
checkInt(loc, val, 28, type);
|
||||
writeValue<e>(loc, val, 26, 2);
|
||||
break;
|
||||
case R_MIPS_PC32:
|
||||
writeValue<e>(loc, val, 32, 0);
|
||||
break;
|
||||
case R_MICROMIPS_26_S1:
|
||||
case R_MICROMIPS_PC26_S1:
|
||||
checkInt(loc, val, 27, type);
|
||||
writeShuffleValue<e>(loc, val, 26, 1);
|
||||
break;
|
||||
case R_MICROMIPS_PC7_S1:
|
||||
checkInt(loc, val, 8, type);
|
||||
writeMicroRelocation16<e>(loc, val, 7, 1);
|
||||
break;
|
||||
case R_MICROMIPS_PC10_S1:
|
||||
checkInt(loc, val, 11, type);
|
||||
writeMicroRelocation16<e>(loc, val, 10, 1);
|
||||
break;
|
||||
case R_MICROMIPS_PC16_S1:
|
||||
checkInt(loc, val, 17, type);
|
||||
writeShuffleValue<e>(loc, val, 16, 1);
|
||||
break;
|
||||
case R_MICROMIPS_PC18_S3:
|
||||
checkInt(loc, val, 21, type);
|
||||
writeShuffleValue<e>(loc, val, 18, 3);
|
||||
break;
|
||||
case R_MICROMIPS_PC19_S2:
|
||||
checkInt(loc, val, 21, type);
|
||||
writeShuffleValue<e>(loc, val, 19, 2);
|
||||
break;
|
||||
case R_MICROMIPS_PC21_S1:
|
||||
checkInt(loc, val, 22, type);
|
||||
writeShuffleValue<e>(loc, val, 21, 1);
|
||||
break;
|
||||
case R_MICROMIPS_PC23_S2:
|
||||
checkInt(loc, val, 25, type);
|
||||
writeShuffleValue<e>(loc, val, 23, 2);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("unknown relocation");
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT> bool MIPS<ELFT>::usesOnlyLowPageBits(RelType type) const {
|
||||
return type == R_MIPS_LO16 || type == R_MIPS_GOT_OFST ||
|
||||
type == R_MICROMIPS_LO16;
|
||||
}
|
||||
|
||||
// Return true if the symbol is a PIC function.
|
||||
template <class ELFT> bool elf::isMipsPIC(const Defined *sym) {
|
||||
if (!sym->isFunc())
|
||||
return false;
|
||||
|
||||
if (sym->stOther & STO_MIPS_PIC)
|
||||
return true;
|
||||
|
||||
if (!sym->section)
|
||||
return false;
|
||||
|
||||
ObjFile<ELFT> *file =
|
||||
cast<InputSectionBase>(sym->section)->template getFile<ELFT>();
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
return file->getObj().getHeader()->e_flags & EF_MIPS_PIC;
|
||||
}
|
||||
|
||||
template <class ELFT> TargetInfo *elf::getMipsTargetInfo() {
|
||||
static MIPS<ELFT> target;
|
||||
return ⌖
|
||||
}
|
||||
|
||||
template TargetInfo *elf::getMipsTargetInfo<ELF32LE>();
|
||||
template TargetInfo *elf::getMipsTargetInfo<ELF32BE>();
|
||||
template TargetInfo *elf::getMipsTargetInfo<ELF64LE>();
|
||||
template TargetInfo *elf::getMipsTargetInfo<ELF64BE>();
|
||||
|
||||
template bool elf::isMipsPIC<ELF32LE>(const Defined *);
|
||||
template bool elf::isMipsPIC<ELF32BE>(const Defined *);
|
||||
template bool elf::isMipsPIC<ELF64LE>(const Defined *);
|
||||
template bool elf::isMipsPIC<ELF64BE>(const Defined *);
|
||||
389
deps/lld/ELF/Arch/MipsArchTree.cpp
vendored
389
deps/lld/ELF/Arch/MipsArchTree.cpp
vendored
@ -1,389 +0,0 @@
|
||||
//===- MipsArchTree.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 file contains a helper function for the Writer.
|
||||
//
|
||||
//===---------------------------------------------------------------------===//
|
||||
|
||||
#include "InputFiles.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Writer.h"
|
||||
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/BinaryFormat/ELF.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Support/MipsABIFlags.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::ELF;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace {
|
||||
struct ArchTreeEdge {
|
||||
uint32_t child;
|
||||
uint32_t parent;
|
||||
};
|
||||
|
||||
struct FileFlags {
|
||||
InputFile *file;
|
||||
uint32_t flags;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
static StringRef getAbiName(uint32_t flags) {
|
||||
switch (flags) {
|
||||
case 0:
|
||||
return "n64";
|
||||
case EF_MIPS_ABI2:
|
||||
return "n32";
|
||||
case EF_MIPS_ABI_O32:
|
||||
return "o32";
|
||||
case EF_MIPS_ABI_O64:
|
||||
return "o64";
|
||||
case EF_MIPS_ABI_EABI32:
|
||||
return "eabi32";
|
||||
case EF_MIPS_ABI_EABI64:
|
||||
return "eabi64";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static StringRef getNanName(bool isNan2008) {
|
||||
return isNan2008 ? "2008" : "legacy";
|
||||
}
|
||||
|
||||
static StringRef getFpName(bool isFp64) { return isFp64 ? "64" : "32"; }
|
||||
|
||||
static void checkFlags(ArrayRef<FileFlags> files) {
|
||||
assert(!files.empty() && "expected non-empty file list");
|
||||
|
||||
uint32_t abi = files[0].flags & (EF_MIPS_ABI | EF_MIPS_ABI2);
|
||||
bool nan = files[0].flags & EF_MIPS_NAN2008;
|
||||
bool fp = files[0].flags & EF_MIPS_FP64;
|
||||
|
||||
for (const FileFlags &f : files) {
|
||||
if (config->is64 && f.flags & EF_MIPS_MICROMIPS)
|
||||
error(toString(f.file) + ": microMIPS 64-bit is not supported");
|
||||
|
||||
uint32_t abi2 = f.flags & (EF_MIPS_ABI | EF_MIPS_ABI2);
|
||||
if (abi != abi2)
|
||||
error(toString(f.file) + ": ABI '" + getAbiName(abi2) +
|
||||
"' is incompatible with target ABI '" + getAbiName(abi) + "'");
|
||||
|
||||
bool nan2 = f.flags & EF_MIPS_NAN2008;
|
||||
if (nan != nan2)
|
||||
error(toString(f.file) + ": -mnan=" + getNanName(nan2) +
|
||||
" is incompatible with target -mnan=" + getNanName(nan));
|
||||
|
||||
bool fp2 = f.flags & EF_MIPS_FP64;
|
||||
if (fp != fp2)
|
||||
error(toString(f.file) + ": -mfp" + getFpName(fp2) +
|
||||
" is incompatible with target -mfp" + getFpName(fp));
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t getMiscFlags(ArrayRef<FileFlags> files) {
|
||||
uint32_t ret = 0;
|
||||
for (const FileFlags &f : files)
|
||||
ret |= f.flags &
|
||||
(EF_MIPS_ABI | EF_MIPS_ABI2 | EF_MIPS_ARCH_ASE | EF_MIPS_NOREORDER |
|
||||
EF_MIPS_MICROMIPS | EF_MIPS_NAN2008 | EF_MIPS_32BITMODE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t getPicFlags(ArrayRef<FileFlags> files) {
|
||||
// Check PIC/non-PIC compatibility.
|
||||
bool isPic = files[0].flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
|
||||
for (const FileFlags &f : files.slice(1)) {
|
||||
bool isPic2 = f.flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
|
||||
if (isPic && !isPic2)
|
||||
warn(toString(f.file) +
|
||||
": linking non-abicalls code with abicalls code " +
|
||||
toString(files[0].file));
|
||||
if (!isPic && isPic2)
|
||||
warn(toString(f.file) +
|
||||
": linking abicalls code with non-abicalls code " +
|
||||
toString(files[0].file));
|
||||
}
|
||||
|
||||
// Compute the result PIC/non-PIC flag.
|
||||
uint32_t ret = files[0].flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
|
||||
for (const FileFlags &f : files.slice(1))
|
||||
ret &= f.flags & (EF_MIPS_PIC | EF_MIPS_CPIC);
|
||||
|
||||
// PIC code is inherently CPIC and may not set CPIC flag explicitly.
|
||||
if (ret & EF_MIPS_PIC)
|
||||
ret |= EF_MIPS_CPIC;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ArchTreeEdge archTree[] = {
|
||||
// MIPS32R6 and MIPS64R6 are not compatible with other extensions
|
||||
// MIPS64R2 extensions.
|
||||
{EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON3, EF_MIPS_ARCH_64R2},
|
||||
{EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON2, EF_MIPS_ARCH_64R2},
|
||||
{EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_OCTEON, EF_MIPS_ARCH_64R2},
|
||||
{EF_MIPS_ARCH_64R2 | EF_MIPS_MACH_LS3A, EF_MIPS_ARCH_64R2},
|
||||
// MIPS64 extensions.
|
||||
{EF_MIPS_ARCH_64 | EF_MIPS_MACH_SB1, EF_MIPS_ARCH_64},
|
||||
{EF_MIPS_ARCH_64 | EF_MIPS_MACH_XLR, EF_MIPS_ARCH_64},
|
||||
{EF_MIPS_ARCH_64R2, EF_MIPS_ARCH_64},
|
||||
// MIPS V extensions.
|
||||
{EF_MIPS_ARCH_64, EF_MIPS_ARCH_5},
|
||||
// R5000 extensions.
|
||||
{EF_MIPS_ARCH_4 | EF_MIPS_MACH_5500, EF_MIPS_ARCH_4 | EF_MIPS_MACH_5400},
|
||||
// MIPS IV extensions.
|
||||
{EF_MIPS_ARCH_4 | EF_MIPS_MACH_5400, EF_MIPS_ARCH_4},
|
||||
{EF_MIPS_ARCH_4 | EF_MIPS_MACH_9000, EF_MIPS_ARCH_4},
|
||||
{EF_MIPS_ARCH_5, EF_MIPS_ARCH_4},
|
||||
// VR4100 extensions.
|
||||
{EF_MIPS_ARCH_3 | EF_MIPS_MACH_4111, EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100},
|
||||
{EF_MIPS_ARCH_3 | EF_MIPS_MACH_4120, EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100},
|
||||
// MIPS III extensions.
|
||||
{EF_MIPS_ARCH_3 | EF_MIPS_MACH_4010, EF_MIPS_ARCH_3},
|
||||
{EF_MIPS_ARCH_3 | EF_MIPS_MACH_4100, EF_MIPS_ARCH_3},
|
||||
{EF_MIPS_ARCH_3 | EF_MIPS_MACH_4650, EF_MIPS_ARCH_3},
|
||||
{EF_MIPS_ARCH_3 | EF_MIPS_MACH_5900, EF_MIPS_ARCH_3},
|
||||
{EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2E, EF_MIPS_ARCH_3},
|
||||
{EF_MIPS_ARCH_3 | EF_MIPS_MACH_LS2F, EF_MIPS_ARCH_3},
|
||||
{EF_MIPS_ARCH_4, EF_MIPS_ARCH_3},
|
||||
// MIPS32 extensions.
|
||||
{EF_MIPS_ARCH_32R2, EF_MIPS_ARCH_32},
|
||||
// MIPS II extensions.
|
||||
{EF_MIPS_ARCH_3, EF_MIPS_ARCH_2},
|
||||
{EF_MIPS_ARCH_32, EF_MIPS_ARCH_2},
|
||||
// MIPS I extensions.
|
||||
{EF_MIPS_ARCH_1 | EF_MIPS_MACH_3900, EF_MIPS_ARCH_1},
|
||||
{EF_MIPS_ARCH_2, EF_MIPS_ARCH_1},
|
||||
};
|
||||
|
||||
static bool isArchMatched(uint32_t New, uint32_t res) {
|
||||
if (New == res)
|
||||
return true;
|
||||
if (New == EF_MIPS_ARCH_32 && isArchMatched(EF_MIPS_ARCH_64, res))
|
||||
return true;
|
||||
if (New == EF_MIPS_ARCH_32R2 && isArchMatched(EF_MIPS_ARCH_64R2, res))
|
||||
return true;
|
||||
for (const auto &edge : archTree) {
|
||||
if (res == edge.child) {
|
||||
res = edge.parent;
|
||||
if (res == New)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static StringRef getMachName(uint32_t flags) {
|
||||
switch (flags & EF_MIPS_MACH) {
|
||||
case EF_MIPS_MACH_NONE:
|
||||
return "";
|
||||
case EF_MIPS_MACH_3900:
|
||||
return "r3900";
|
||||
case EF_MIPS_MACH_4010:
|
||||
return "r4010";
|
||||
case EF_MIPS_MACH_4100:
|
||||
return "r4100";
|
||||
case EF_MIPS_MACH_4650:
|
||||
return "r4650";
|
||||
case EF_MIPS_MACH_4120:
|
||||
return "r4120";
|
||||
case EF_MIPS_MACH_4111:
|
||||
return "r4111";
|
||||
case EF_MIPS_MACH_5400:
|
||||
return "vr5400";
|
||||
case EF_MIPS_MACH_5900:
|
||||
return "vr5900";
|
||||
case EF_MIPS_MACH_5500:
|
||||
return "vr5500";
|
||||
case EF_MIPS_MACH_9000:
|
||||
return "rm9000";
|
||||
case EF_MIPS_MACH_LS2E:
|
||||
return "loongson2e";
|
||||
case EF_MIPS_MACH_LS2F:
|
||||
return "loongson2f";
|
||||
case EF_MIPS_MACH_LS3A:
|
||||
return "loongson3a";
|
||||
case EF_MIPS_MACH_OCTEON:
|
||||
return "octeon";
|
||||
case EF_MIPS_MACH_OCTEON2:
|
||||
return "octeon2";
|
||||
case EF_MIPS_MACH_OCTEON3:
|
||||
return "octeon3";
|
||||
case EF_MIPS_MACH_SB1:
|
||||
return "sb1";
|
||||
case EF_MIPS_MACH_XLR:
|
||||
return "xlr";
|
||||
default:
|
||||
return "unknown machine";
|
||||
}
|
||||
}
|
||||
|
||||
static StringRef getArchName(uint32_t flags) {
|
||||
switch (flags & EF_MIPS_ARCH) {
|
||||
case EF_MIPS_ARCH_1:
|
||||
return "mips1";
|
||||
case EF_MIPS_ARCH_2:
|
||||
return "mips2";
|
||||
case EF_MIPS_ARCH_3:
|
||||
return "mips3";
|
||||
case EF_MIPS_ARCH_4:
|
||||
return "mips4";
|
||||
case EF_MIPS_ARCH_5:
|
||||
return "mips5";
|
||||
case EF_MIPS_ARCH_32:
|
||||
return "mips32";
|
||||
case EF_MIPS_ARCH_64:
|
||||
return "mips64";
|
||||
case EF_MIPS_ARCH_32R2:
|
||||
return "mips32r2";
|
||||
case EF_MIPS_ARCH_64R2:
|
||||
return "mips64r2";
|
||||
case EF_MIPS_ARCH_32R6:
|
||||
return "mips32r6";
|
||||
case EF_MIPS_ARCH_64R6:
|
||||
return "mips64r6";
|
||||
default:
|
||||
return "unknown arch";
|
||||
}
|
||||
}
|
||||
|
||||
static std::string getFullArchName(uint32_t flags) {
|
||||
StringRef arch = getArchName(flags);
|
||||
StringRef mach = getMachName(flags);
|
||||
if (mach.empty())
|
||||
return arch.str();
|
||||
return (arch + " (" + mach + ")").str();
|
||||
}
|
||||
|
||||
// There are (arguably too) many MIPS ISAs out there. Their relationships
|
||||
// can be represented as a forest. If all input files have ISAs which
|
||||
// reachable by repeated proceeding from the single child to the parent,
|
||||
// these input files are compatible. In that case we need to return "highest"
|
||||
// ISA. If there are incompatible input files, we show an error.
|
||||
// For example, mips1 is a "parent" of mips2 and such files are compatible.
|
||||
// Output file gets EF_MIPS_ARCH_2 flag. From the other side mips3 and mips32
|
||||
// are incompatible because nor mips3 is a parent for misp32, nor mips32
|
||||
// is a parent for mips3.
|
||||
static uint32_t getArchFlags(ArrayRef<FileFlags> files) {
|
||||
uint32_t ret = files[0].flags & (EF_MIPS_ARCH | EF_MIPS_MACH);
|
||||
|
||||
for (const FileFlags &f : files.slice(1)) {
|
||||
uint32_t New = f.flags & (EF_MIPS_ARCH | EF_MIPS_MACH);
|
||||
|
||||
// Check ISA compatibility.
|
||||
if (isArchMatched(New, ret))
|
||||
continue;
|
||||
if (!isArchMatched(ret, New)) {
|
||||
error("incompatible target ISA:\n>>> " + toString(files[0].file) + ": " +
|
||||
getFullArchName(ret) + "\n>>> " + toString(f.file) + ": " +
|
||||
getFullArchName(New));
|
||||
return 0;
|
||||
}
|
||||
ret = New;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <class ELFT> uint32_t elf::calcMipsEFlags() {
|
||||
std::vector<FileFlags> v;
|
||||
for (InputFile *f : objectFiles)
|
||||
v.push_back({f, cast<ObjFile<ELFT>>(f)->getObj().getHeader()->e_flags});
|
||||
if (v.empty())
|
||||
return 0;
|
||||
checkFlags(v);
|
||||
return getMiscFlags(v) | getPicFlags(v) | getArchFlags(v);
|
||||
}
|
||||
|
||||
static int compareMipsFpAbi(uint8_t fpA, uint8_t fpB) {
|
||||
if (fpA == fpB)
|
||||
return 0;
|
||||
if (fpB == Mips::Val_GNU_MIPS_ABI_FP_ANY)
|
||||
return 1;
|
||||
if (fpB == Mips::Val_GNU_MIPS_ABI_FP_64A &&
|
||||
fpA == Mips::Val_GNU_MIPS_ABI_FP_64)
|
||||
return 1;
|
||||
if (fpB != Mips::Val_GNU_MIPS_ABI_FP_XX)
|
||||
return -1;
|
||||
if (fpA == Mips::Val_GNU_MIPS_ABI_FP_DOUBLE ||
|
||||
fpA == Mips::Val_GNU_MIPS_ABI_FP_64 ||
|
||||
fpA == Mips::Val_GNU_MIPS_ABI_FP_64A)
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static StringRef getMipsFpAbiName(uint8_t fpAbi) {
|
||||
switch (fpAbi) {
|
||||
case Mips::Val_GNU_MIPS_ABI_FP_ANY:
|
||||
return "any";
|
||||
case Mips::Val_GNU_MIPS_ABI_FP_DOUBLE:
|
||||
return "-mdouble-float";
|
||||
case Mips::Val_GNU_MIPS_ABI_FP_SINGLE:
|
||||
return "-msingle-float";
|
||||
case Mips::Val_GNU_MIPS_ABI_FP_SOFT:
|
||||
return "-msoft-float";
|
||||
case Mips::Val_GNU_MIPS_ABI_FP_OLD_64:
|
||||
return "-mgp32 -mfp64 (old)";
|
||||
case Mips::Val_GNU_MIPS_ABI_FP_XX:
|
||||
return "-mfpxx";
|
||||
case Mips::Val_GNU_MIPS_ABI_FP_64:
|
||||
return "-mgp32 -mfp64";
|
||||
case Mips::Val_GNU_MIPS_ABI_FP_64A:
|
||||
return "-mgp32 -mfp64 -mno-odd-spreg";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t elf::getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag,
|
||||
StringRef fileName) {
|
||||
if (compareMipsFpAbi(newFlag, oldFlag) >= 0)
|
||||
return newFlag;
|
||||
if (compareMipsFpAbi(oldFlag, newFlag) < 0)
|
||||
error(fileName + ": floating point ABI '" + getMipsFpAbiName(newFlag) +
|
||||
"' is incompatible with target floating point ABI '" +
|
||||
getMipsFpAbiName(oldFlag) + "'");
|
||||
return oldFlag;
|
||||
}
|
||||
|
||||
template <class ELFT> static bool isN32Abi(const InputFile *f) {
|
||||
if (auto *ef = dyn_cast<ELFFileBase>(f))
|
||||
return ef->template getObj<ELFT>().getHeader()->e_flags & EF_MIPS_ABI2;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool elf::isMipsN32Abi(const InputFile *f) {
|
||||
switch (config->ekind) {
|
||||
case ELF32LEKind:
|
||||
return isN32Abi<ELF32LE>(f);
|
||||
case ELF32BEKind:
|
||||
return isN32Abi<ELF32BE>(f);
|
||||
case ELF64LEKind:
|
||||
return isN32Abi<ELF64LE>(f);
|
||||
case ELF64BEKind:
|
||||
return isN32Abi<ELF64BE>(f);
|
||||
default:
|
||||
llvm_unreachable("unknown Config->EKind");
|
||||
}
|
||||
}
|
||||
|
||||
bool elf::isMicroMips() { return config->eflags & EF_MIPS_MICROMIPS; }
|
||||
|
||||
bool elf::isMipsR6() {
|
||||
uint32_t arch = config->eflags & EF_MIPS_ARCH;
|
||||
return arch == EF_MIPS_ARCH_32R6 || arch == EF_MIPS_ARCH_64R6;
|
||||
}
|
||||
|
||||
template uint32_t elf::calcMipsEFlags<ELF32LE>();
|
||||
template uint32_t elf::calcMipsEFlags<ELF32BE>();
|
||||
template uint32_t elf::calcMipsEFlags<ELF64LE>();
|
||||
template uint32_t elf::calcMipsEFlags<ELF64BE>();
|
||||
441
deps/lld/ELF/Arch/PPC.cpp
vendored
441
deps/lld/ELF/Arch/PPC.cpp
vendored
@ -1,441 +0,0 @@
|
||||
//===- PPC.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 "OutputSections.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace {
|
||||
class PPC final : public TargetInfo {
|
||||
public:
|
||||
PPC();
|
||||
RelExpr getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const override;
|
||||
RelType getDynRel(RelType type) const override;
|
||||
void writeGotHeader(uint8_t *buf) const override;
|
||||
void writePltHeader(uint8_t *buf) const override {
|
||||
llvm_unreachable("should call writePPC32GlinkSection() instead");
|
||||
}
|
||||
void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
|
||||
int32_t index, unsigned relOff) const override {
|
||||
llvm_unreachable("should call writePPC32GlinkSection() instead");
|
||||
}
|
||||
void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
|
||||
bool needsThunk(RelExpr expr, RelType relocType, const InputFile *file,
|
||||
uint64_t branchAddr, const Symbol &s) const override;
|
||||
uint32_t getThunkSectionSpacing() const override;
|
||||
bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
|
||||
void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
RelExpr adjustRelaxExpr(RelType type, const uint8_t *data,
|
||||
RelExpr expr) const override;
|
||||
int getTlsGdRelaxSkip(RelType type) const override;
|
||||
void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
static uint16_t lo(uint32_t v) { return v; }
|
||||
static uint16_t ha(uint32_t v) { return (v + 0x8000) >> 16; }
|
||||
|
||||
static uint32_t readFromHalf16(const uint8_t *loc) {
|
||||
return read32(config->isLE ? loc : loc - 2);
|
||||
}
|
||||
|
||||
static void writeFromHalf16(uint8_t *loc, uint32_t insn) {
|
||||
write32(config->isLE ? loc : loc - 2, insn);
|
||||
}
|
||||
|
||||
void elf::writePPC32GlinkSection(uint8_t *buf, size_t numEntries) {
|
||||
// On PPC Secure PLT ABI, bl foo@plt jumps to a call stub, which loads an
|
||||
// absolute address from a specific .plt slot (usually called .got.plt on
|
||||
// other targets) and jumps there.
|
||||
//
|
||||
// a) With immediate binding (BIND_NOW), the .plt entry is resolved at load
|
||||
// time. The .glink section is not used.
|
||||
// b) With lazy binding, the .plt entry points to a `b PLTresolve`
|
||||
// instruction in .glink, filled in by PPC::writeGotPlt().
|
||||
|
||||
// Write N `b PLTresolve` first.
|
||||
for (size_t i = 0; i != numEntries; ++i)
|
||||
write32(buf + 4 * i, 0x48000000 | 4 * (numEntries - i));
|
||||
buf += 4 * numEntries;
|
||||
|
||||
// Then write PLTresolve(), which has two forms: PIC and non-PIC. PLTresolve()
|
||||
// computes the PLT index (by computing the distance from the landing b to
|
||||
// itself) and calls _dl_runtime_resolve() (in glibc).
|
||||
uint32_t got = in.got->getVA();
|
||||
uint32_t glink = in.plt->getVA(); // VA of .glink
|
||||
const uint8_t *end = buf + 64;
|
||||
if (config->isPic) {
|
||||
uint32_t afterBcl = in.plt->getSize() - target->pltHeaderSize + 12;
|
||||
uint32_t gotBcl = got + 4 - (glink + afterBcl);
|
||||
write32(buf + 0, 0x3d6b0000 | ha(afterBcl)); // addis r11,r11,1f-glink@ha
|
||||
write32(buf + 4, 0x7c0802a6); // mflr r0
|
||||
write32(buf + 8, 0x429f0005); // bcl 20,30,.+4
|
||||
write32(buf + 12, 0x396b0000 | lo(afterBcl)); // 1: addi r11,r11,1b-.glink@l
|
||||
write32(buf + 16, 0x7d8802a6); // mflr r12
|
||||
write32(buf + 20, 0x7c0803a6); // mtlr r0
|
||||
write32(buf + 24, 0x7d6c5850); // sub r11,r11,r12
|
||||
write32(buf + 28, 0x3d8c0000 | ha(gotBcl)); // addis 12,12,GOT+4-1b@ha
|
||||
if (ha(gotBcl) == ha(gotBcl + 4)) {
|
||||
write32(buf + 32, 0x800c0000 | lo(gotBcl)); // lwz r0,r12,GOT+4-1b@l(r12)
|
||||
write32(buf + 36,
|
||||
0x818c0000 | lo(gotBcl + 4)); // lwz r12,r12,GOT+8-1b@l(r12)
|
||||
} else {
|
||||
write32(buf + 32, 0x840c0000 | lo(gotBcl)); // lwzu r0,r12,GOT+4-1b@l(r12)
|
||||
write32(buf + 36, 0x818c0000 | 4); // lwz r12,r12,4(r12)
|
||||
}
|
||||
write32(buf + 40, 0x7c0903a6); // mtctr 0
|
||||
write32(buf + 44, 0x7c0b5a14); // add r0,11,11
|
||||
write32(buf + 48, 0x7d605a14); // add r11,0,11
|
||||
write32(buf + 52, 0x4e800420); // bctr
|
||||
buf += 56;
|
||||
} else {
|
||||
write32(buf + 0, 0x3d800000 | ha(got + 4)); // lis r12,GOT+4@ha
|
||||
write32(buf + 4, 0x3d6b0000 | ha(-glink)); // addis r11,r11,-Glink@ha
|
||||
if (ha(got + 4) == ha(got + 8))
|
||||
write32(buf + 8, 0x800c0000 | lo(got + 4)); // lwz r0,GOT+4@l(r12)
|
||||
else
|
||||
write32(buf + 8, 0x840c0000 | lo(got + 4)); // lwzu r0,GOT+4@l(r12)
|
||||
write32(buf + 12, 0x396b0000 | lo(-glink)); // addi r11,r11,-Glink@l
|
||||
write32(buf + 16, 0x7c0903a6); // mtctr r0
|
||||
write32(buf + 20, 0x7c0b5a14); // add r0,r11,r11
|
||||
if (ha(got + 4) == ha(got + 8))
|
||||
write32(buf + 24, 0x818c0000 | lo(got + 8)); // lwz r12,GOT+8@ha(r12)
|
||||
else
|
||||
write32(buf + 24, 0x818c0000 | 4); // lwz r12,4(r12)
|
||||
write32(buf + 28, 0x7d605a14); // add r11,r0,r11
|
||||
write32(buf + 32, 0x4e800420); // bctr
|
||||
buf += 36;
|
||||
}
|
||||
|
||||
// Pad with nop. They should not be executed.
|
||||
for (; buf < end; buf += 4)
|
||||
write32(buf, 0x60000000);
|
||||
}
|
||||
|
||||
PPC::PPC() {
|
||||
gotRel = R_PPC_GLOB_DAT;
|
||||
noneRel = R_PPC_NONE;
|
||||
pltRel = R_PPC_JMP_SLOT;
|
||||
relativeRel = R_PPC_RELATIVE;
|
||||
iRelativeRel = R_PPC_IRELATIVE;
|
||||
symbolicRel = R_PPC_ADDR32;
|
||||
gotBaseSymInGotPlt = false;
|
||||
gotHeaderEntriesNum = 3;
|
||||
gotPltHeaderEntriesNum = 0;
|
||||
pltHeaderSize = 64; // size of PLTresolve in .glink
|
||||
pltEntrySize = 4;
|
||||
|
||||
needsThunks = true;
|
||||
|
||||
tlsModuleIndexRel = R_PPC_DTPMOD32;
|
||||
tlsOffsetRel = R_PPC_DTPREL32;
|
||||
tlsGotRel = R_PPC_TPREL32;
|
||||
|
||||
defaultMaxPageSize = 65536;
|
||||
defaultImageBase = 0x10000000;
|
||||
|
||||
write32(trapInstr.data(), 0x7fe00008);
|
||||
}
|
||||
|
||||
void PPC::writeGotHeader(uint8_t *buf) const {
|
||||
// _GLOBAL_OFFSET_TABLE_[0] = _DYNAMIC
|
||||
// glibc stores _dl_runtime_resolve in _GLOBAL_OFFSET_TABLE_[1],
|
||||
// link_map in _GLOBAL_OFFSET_TABLE_[2].
|
||||
write32(buf, mainPart->dynamic->getVA());
|
||||
}
|
||||
|
||||
void PPC::writeGotPlt(uint8_t *buf, const Symbol &s) const {
|
||||
// Address of the symbol resolver stub in .glink .
|
||||
write32(buf, in.plt->getVA() + 4 * s.pltIndex);
|
||||
}
|
||||
|
||||
bool PPC::needsThunk(RelExpr expr, RelType type, const InputFile *file,
|
||||
uint64_t branchAddr, const Symbol &s) const {
|
||||
if (type != R_PPC_REL24 && type != R_PPC_PLTREL24)
|
||||
return false;
|
||||
if (s.isInPlt())
|
||||
return true;
|
||||
if (s.isUndefWeak())
|
||||
return false;
|
||||
return !(expr == R_PC && PPC::inBranchRange(type, branchAddr, s.getVA()));
|
||||
}
|
||||
|
||||
uint32_t PPC::getThunkSectionSpacing() const { return 0x2000000; }
|
||||
|
||||
bool PPC::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
|
||||
uint64_t offset = dst - src;
|
||||
if (type == R_PPC_REL24 || type == R_PPC_PLTREL24)
|
||||
return isInt<26>(offset);
|
||||
llvm_unreachable("unsupported relocation type used in branch");
|
||||
}
|
||||
|
||||
RelExpr PPC::getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const {
|
||||
switch (type) {
|
||||
case R_PPC_NONE:
|
||||
return R_NONE;
|
||||
case R_PPC_ADDR16_HA:
|
||||
case R_PPC_ADDR16_HI:
|
||||
case R_PPC_ADDR16_LO:
|
||||
case R_PPC_ADDR32:
|
||||
return R_ABS;
|
||||
case R_PPC_DTPREL16:
|
||||
case R_PPC_DTPREL16_HA:
|
||||
case R_PPC_DTPREL16_HI:
|
||||
case R_PPC_DTPREL16_LO:
|
||||
case R_PPC_DTPREL32:
|
||||
return R_DTPREL;
|
||||
case R_PPC_REL14:
|
||||
case R_PPC_REL32:
|
||||
case R_PPC_LOCAL24PC:
|
||||
case R_PPC_REL16_LO:
|
||||
case R_PPC_REL16_HI:
|
||||
case R_PPC_REL16_HA:
|
||||
return R_PC;
|
||||
case R_PPC_GOT16:
|
||||
return R_GOT_OFF;
|
||||
case R_PPC_REL24:
|
||||
return R_PLT_PC;
|
||||
case R_PPC_PLTREL24:
|
||||
return R_PPC32_PLTREL;
|
||||
case R_PPC_GOT_TLSGD16:
|
||||
return R_TLSGD_GOT;
|
||||
case R_PPC_GOT_TLSLD16:
|
||||
return R_TLSLD_GOT;
|
||||
case R_PPC_GOT_TPREL16:
|
||||
return R_GOT_OFF;
|
||||
case R_PPC_TLS:
|
||||
return R_TLSIE_HINT;
|
||||
case R_PPC_TLSGD:
|
||||
return R_TLSDESC_CALL;
|
||||
case R_PPC_TLSLD:
|
||||
return R_TLSLD_HINT;
|
||||
case R_PPC_TPREL16:
|
||||
case R_PPC_TPREL16_HA:
|
||||
case R_PPC_TPREL16_LO:
|
||||
case R_PPC_TPREL16_HI:
|
||||
return R_TLS;
|
||||
default:
|
||||
error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
|
||||
") against symbol " + toString(s));
|
||||
return R_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
RelType PPC::getDynRel(RelType type) const {
|
||||
if (type == R_PPC_ADDR32)
|
||||
return type;
|
||||
return R_PPC_NONE;
|
||||
}
|
||||
|
||||
static std::pair<RelType, uint64_t> fromDTPREL(RelType type, uint64_t val) {
|
||||
uint64_t dtpBiasedVal = val - 0x8000;
|
||||
switch (type) {
|
||||
case R_PPC_DTPREL16:
|
||||
return {R_PPC64_ADDR16, dtpBiasedVal};
|
||||
case R_PPC_DTPREL16_HA:
|
||||
return {R_PPC_ADDR16_HA, dtpBiasedVal};
|
||||
case R_PPC_DTPREL16_HI:
|
||||
return {R_PPC_ADDR16_HI, dtpBiasedVal};
|
||||
case R_PPC_DTPREL16_LO:
|
||||
return {R_PPC_ADDR16_LO, dtpBiasedVal};
|
||||
case R_PPC_DTPREL32:
|
||||
return {R_PPC_ADDR32, dtpBiasedVal};
|
||||
default:
|
||||
return {type, val};
|
||||
}
|
||||
}
|
||||
|
||||
void PPC::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
RelType newType;
|
||||
std::tie(newType, val) = fromDTPREL(type, val);
|
||||
switch (newType) {
|
||||
case R_PPC_ADDR16:
|
||||
checkIntUInt(loc, val, 16, type);
|
||||
write16(loc, val);
|
||||
break;
|
||||
case R_PPC_GOT16:
|
||||
case R_PPC_GOT_TLSGD16:
|
||||
case R_PPC_GOT_TLSLD16:
|
||||
case R_PPC_GOT_TPREL16:
|
||||
case R_PPC_TPREL16:
|
||||
checkInt(loc, val, 16, type);
|
||||
write16(loc, val);
|
||||
break;
|
||||
case R_PPC_ADDR16_HA:
|
||||
case R_PPC_DTPREL16_HA:
|
||||
case R_PPC_GOT_TLSGD16_HA:
|
||||
case R_PPC_GOT_TLSLD16_HA:
|
||||
case R_PPC_GOT_TPREL16_HA:
|
||||
case R_PPC_REL16_HA:
|
||||
case R_PPC_TPREL16_HA:
|
||||
write16(loc, ha(val));
|
||||
break;
|
||||
case R_PPC_ADDR16_HI:
|
||||
case R_PPC_DTPREL16_HI:
|
||||
case R_PPC_GOT_TLSGD16_HI:
|
||||
case R_PPC_GOT_TLSLD16_HI:
|
||||
case R_PPC_GOT_TPREL16_HI:
|
||||
case R_PPC_REL16_HI:
|
||||
case R_PPC_TPREL16_HI:
|
||||
write16(loc, val >> 16);
|
||||
break;
|
||||
case R_PPC_ADDR16_LO:
|
||||
case R_PPC_DTPREL16_LO:
|
||||
case R_PPC_GOT_TLSGD16_LO:
|
||||
case R_PPC_GOT_TLSLD16_LO:
|
||||
case R_PPC_GOT_TPREL16_LO:
|
||||
case R_PPC_REL16_LO:
|
||||
case R_PPC_TPREL16_LO:
|
||||
write16(loc, val);
|
||||
break;
|
||||
case R_PPC_ADDR32:
|
||||
case R_PPC_REL32:
|
||||
write32(loc, val);
|
||||
break;
|
||||
case R_PPC_REL14: {
|
||||
uint32_t mask = 0x0000FFFC;
|
||||
checkInt(loc, val, 16, type);
|
||||
checkAlignment(loc, val, 4, type);
|
||||
write32(loc, (read32(loc) & ~mask) | (val & mask));
|
||||
break;
|
||||
}
|
||||
case R_PPC_REL24:
|
||||
case R_PPC_LOCAL24PC:
|
||||
case R_PPC_PLTREL24: {
|
||||
uint32_t mask = 0x03FFFFFC;
|
||||
checkInt(loc, val, 26, type);
|
||||
checkAlignment(loc, val, 4, type);
|
||||
write32(loc, (read32(loc) & ~mask) | (val & mask));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
llvm_unreachable("unknown relocation");
|
||||
}
|
||||
}
|
||||
|
||||
RelExpr PPC::adjustRelaxExpr(RelType type, const uint8_t *data,
|
||||
RelExpr expr) const {
|
||||
if (expr == R_RELAX_TLS_GD_TO_IE)
|
||||
return R_RELAX_TLS_GD_TO_IE_GOT_OFF;
|
||||
if (expr == R_RELAX_TLS_LD_TO_LE)
|
||||
return R_RELAX_TLS_LD_TO_LE_ABS;
|
||||
return expr;
|
||||
}
|
||||
|
||||
int PPC::getTlsGdRelaxSkip(RelType type) const {
|
||||
// A __tls_get_addr call instruction is marked with 2 relocations:
|
||||
//
|
||||
// R_PPC_TLSGD / R_PPC_TLSLD: marker relocation
|
||||
// R_PPC_REL24: __tls_get_addr
|
||||
//
|
||||
// After the relaxation we no longer call __tls_get_addr and should skip both
|
||||
// relocations to not create a false dependence on __tls_get_addr being
|
||||
// defined.
|
||||
if (type == R_PPC_TLSGD || type == R_PPC_TLSLD)
|
||||
return 2;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void PPC::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
switch (type) {
|
||||
case R_PPC_GOT_TLSGD16: {
|
||||
// addi rT, rA, x@got@tlsgd --> lwz rT, x@got@tprel(rA)
|
||||
uint32_t insn = readFromHalf16(loc);
|
||||
writeFromHalf16(loc, 0x80000000 | (insn & 0x03ff0000));
|
||||
relocateOne(loc, R_PPC_GOT_TPREL16, val);
|
||||
break;
|
||||
}
|
||||
case R_PPC_TLSGD:
|
||||
// bl __tls_get_addr(x@tldgd) --> add r3, r3, r2
|
||||
write32(loc, 0x7c631214);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("unsupported relocation for TLS GD to IE relaxation");
|
||||
}
|
||||
}
|
||||
|
||||
void PPC::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
switch (type) {
|
||||
case R_PPC_GOT_TLSGD16:
|
||||
// addi r3, r31, x@got@tlsgd --> addis r3, r2, x@tprel@ha
|
||||
writeFromHalf16(loc, 0x3c620000 | ha(val));
|
||||
break;
|
||||
case R_PPC_TLSGD:
|
||||
// bl __tls_get_addr(x@tldgd) --> add r3, r3, x@tprel@l
|
||||
write32(loc, 0x38630000 | lo(val));
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("unsupported relocation for TLS GD to LE relaxation");
|
||||
}
|
||||
}
|
||||
|
||||
void PPC::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
switch (type) {
|
||||
case R_PPC_GOT_TLSLD16:
|
||||
// addi r3, rA, x@got@tlsgd --> addis r3, r2, 0
|
||||
writeFromHalf16(loc, 0x3c620000);
|
||||
break;
|
||||
case R_PPC_TLSLD:
|
||||
// r3+x@dtprel computes r3+x-0x8000, while we want it to compute r3+x@tprel
|
||||
// = r3+x-0x7000, so add 4096 to r3.
|
||||
// bl __tls_get_addr(x@tlsld) --> addi r3, r3, 4096
|
||||
write32(loc, 0x38631000);
|
||||
break;
|
||||
case R_PPC_DTPREL16:
|
||||
case R_PPC_DTPREL16_HA:
|
||||
case R_PPC_DTPREL16_HI:
|
||||
case R_PPC_DTPREL16_LO:
|
||||
relocateOne(loc, type, val);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("unsupported relocation for TLS LD to LE relaxation");
|
||||
}
|
||||
}
|
||||
|
||||
void PPC::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
switch (type) {
|
||||
case R_PPC_GOT_TPREL16: {
|
||||
// lwz rT, x@got@tprel(rA) --> addis rT, r2, x@tprel@ha
|
||||
uint32_t rt = readFromHalf16(loc) & 0x03e00000;
|
||||
writeFromHalf16(loc, 0x3c020000 | rt | ha(val));
|
||||
break;
|
||||
}
|
||||
case R_PPC_TLS: {
|
||||
uint32_t insn = read32(loc);
|
||||
if (insn >> 26 != 31)
|
||||
error("unrecognized instruction for IE to LE R_PPC_TLS");
|
||||
// addi rT, rT, x@tls --> addi rT, rT, x@tprel@l
|
||||
uint32_t dFormOp = getPPCDFormOp((read32(loc) & 0x000007fe) >> 1);
|
||||
if (dFormOp == 0)
|
||||
error("unrecognized instruction for IE to LE R_PPC_TLS");
|
||||
write32(loc, (dFormOp << 26) | (insn & 0x03ff0000) | lo(val));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
llvm_unreachable("unsupported relocation for TLS IE to LE relaxation");
|
||||
}
|
||||
}
|
||||
|
||||
TargetInfo *elf::getPPCTargetInfo() {
|
||||
static PPC target;
|
||||
return ⌖
|
||||
}
|
||||
1095
deps/lld/ELF/Arch/PPC64.cpp
vendored
1095
deps/lld/ELF/Arch/PPC64.cpp
vendored
File diff suppressed because it is too large
Load Diff
442
deps/lld/ELF/Arch/RISCV.cpp
vendored
442
deps/lld/ELF/Arch/RISCV.cpp
vendored
@ -1,442 +0,0 @@
|
||||
//===- RISCV.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 "InputFiles.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace {
|
||||
|
||||
class RISCV final : public TargetInfo {
|
||||
public:
|
||||
RISCV();
|
||||
uint32_t calcEFlags() const override;
|
||||
void writeGotHeader(uint8_t *buf) const override;
|
||||
void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
|
||||
void writePltHeader(uint8_t *buf) const override;
|
||||
void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
|
||||
int32_t index, unsigned relOff) const override;
|
||||
RelType getDynRel(RelType type) const override;
|
||||
RelExpr getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const override;
|
||||
void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
const uint64_t dtpOffset = 0x800;
|
||||
|
||||
enum Op {
|
||||
ADDI = 0x13,
|
||||
AUIPC = 0x17,
|
||||
JALR = 0x67,
|
||||
LD = 0x3003,
|
||||
LW = 0x2003,
|
||||
SRLI = 0x5013,
|
||||
SUB = 0x40000033,
|
||||
};
|
||||
|
||||
enum Reg {
|
||||
X_RA = 1,
|
||||
X_T0 = 5,
|
||||
X_T1 = 6,
|
||||
X_T2 = 7,
|
||||
X_T3 = 28,
|
||||
};
|
||||
|
||||
static uint32_t hi20(uint32_t val) { return (val + 0x800) >> 12; }
|
||||
static uint32_t lo12(uint32_t val) { return val & 4095; }
|
||||
|
||||
static uint32_t itype(uint32_t op, uint32_t rd, uint32_t rs1, uint32_t imm) {
|
||||
return op | (rd << 7) | (rs1 << 15) | (imm << 20);
|
||||
}
|
||||
static uint32_t rtype(uint32_t op, uint32_t rd, uint32_t rs1, uint32_t rs2) {
|
||||
return op | (rd << 7) | (rs1 << 15) | (rs2 << 20);
|
||||
}
|
||||
static uint32_t utype(uint32_t op, uint32_t rd, uint32_t imm) {
|
||||
return op | (rd << 7) | (imm << 12);
|
||||
}
|
||||
|
||||
RISCV::RISCV() {
|
||||
copyRel = R_RISCV_COPY;
|
||||
noneRel = R_RISCV_NONE;
|
||||
pltRel = R_RISCV_JUMP_SLOT;
|
||||
relativeRel = R_RISCV_RELATIVE;
|
||||
if (config->is64) {
|
||||
symbolicRel = R_RISCV_64;
|
||||
tlsModuleIndexRel = R_RISCV_TLS_DTPMOD64;
|
||||
tlsOffsetRel = R_RISCV_TLS_DTPREL64;
|
||||
tlsGotRel = R_RISCV_TLS_TPREL64;
|
||||
} else {
|
||||
symbolicRel = R_RISCV_32;
|
||||
tlsModuleIndexRel = R_RISCV_TLS_DTPMOD32;
|
||||
tlsOffsetRel = R_RISCV_TLS_DTPREL32;
|
||||
tlsGotRel = R_RISCV_TLS_TPREL32;
|
||||
}
|
||||
gotRel = symbolicRel;
|
||||
|
||||
// .got[0] = _DYNAMIC
|
||||
gotBaseSymInGotPlt = false;
|
||||
gotHeaderEntriesNum = 1;
|
||||
|
||||
// .got.plt[0] = _dl_runtime_resolve, .got.plt[1] = link_map
|
||||
gotPltHeaderEntriesNum = 2;
|
||||
|
||||
pltEntrySize = 16;
|
||||
pltHeaderSize = 32;
|
||||
}
|
||||
|
||||
static uint32_t getEFlags(InputFile *f) {
|
||||
if (config->is64)
|
||||
return cast<ObjFile<ELF64LE>>(f)->getObj().getHeader()->e_flags;
|
||||
return cast<ObjFile<ELF32LE>>(f)->getObj().getHeader()->e_flags;
|
||||
}
|
||||
|
||||
uint32_t RISCV::calcEFlags() const {
|
||||
assert(!objectFiles.empty());
|
||||
|
||||
uint32_t target = getEFlags(objectFiles.front());
|
||||
|
||||
for (InputFile *f : objectFiles) {
|
||||
uint32_t eflags = getEFlags(f);
|
||||
if (eflags & EF_RISCV_RVC)
|
||||
target |= EF_RISCV_RVC;
|
||||
|
||||
if ((eflags & EF_RISCV_FLOAT_ABI) != (target & EF_RISCV_FLOAT_ABI))
|
||||
error(toString(f) +
|
||||
": cannot link object files with different floating-point ABI");
|
||||
|
||||
if ((eflags & EF_RISCV_RVE) != (target & EF_RISCV_RVE))
|
||||
error(toString(f) +
|
||||
": cannot link object files with different EF_RISCV_RVE");
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
void RISCV::writeGotHeader(uint8_t *buf) const {
|
||||
if (config->is64)
|
||||
write64le(buf, mainPart->dynamic->getVA());
|
||||
else
|
||||
write32le(buf, mainPart->dynamic->getVA());
|
||||
}
|
||||
|
||||
void RISCV::writeGotPlt(uint8_t *buf, const Symbol &s) const {
|
||||
if (config->is64)
|
||||
write64le(buf, in.plt->getVA());
|
||||
else
|
||||
write32le(buf, in.plt->getVA());
|
||||
}
|
||||
|
||||
void RISCV::writePltHeader(uint8_t *buf) const {
|
||||
// 1: auipc t2, %pcrel_hi(.got.plt)
|
||||
// sub t1, t1, t3
|
||||
// l[wd] t3, %pcrel_lo(1b)(t2); t3 = _dl_runtime_resolve
|
||||
// addi t1, t1, -pltHeaderSize-12; t1 = &.plt[i] - &.plt[0]
|
||||
// addi t0, t2, %pcrel_lo(1b)
|
||||
// srli t1, t1, (rv64?1:2); t1 = &.got.plt[i] - &.got.plt[0]
|
||||
// l[wd] t0, Wordsize(t0); t0 = link_map
|
||||
// jr t3
|
||||
uint32_t offset = in.gotPlt->getVA() - in.plt->getVA();
|
||||
uint32_t load = config->is64 ? LD : LW;
|
||||
write32le(buf + 0, utype(AUIPC, X_T2, hi20(offset)));
|
||||
write32le(buf + 4, rtype(SUB, X_T1, X_T1, X_T3));
|
||||
write32le(buf + 8, itype(load, X_T3, X_T2, lo12(offset)));
|
||||
write32le(buf + 12, itype(ADDI, X_T1, X_T1, -target->pltHeaderSize - 12));
|
||||
write32le(buf + 16, itype(ADDI, X_T0, X_T2, lo12(offset)));
|
||||
write32le(buf + 20, itype(SRLI, X_T1, X_T1, config->is64 ? 1 : 2));
|
||||
write32le(buf + 24, itype(load, X_T0, X_T0, config->wordsize));
|
||||
write32le(buf + 28, itype(JALR, 0, X_T3, 0));
|
||||
}
|
||||
|
||||
void RISCV::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
|
||||
uint64_t pltEntryAddr, int32_t index,
|
||||
unsigned relOff) const {
|
||||
// 1: auipc t3, %pcrel_hi(f@.got.plt)
|
||||
// l[wd] t3, %pcrel_lo(1b)(t3)
|
||||
// jalr t1, t3
|
||||
// nop
|
||||
uint32_t offset = gotPltEntryAddr - pltEntryAddr;
|
||||
write32le(buf + 0, utype(AUIPC, X_T3, hi20(offset)));
|
||||
write32le(buf + 4, itype(config->is64 ? LD : LW, X_T3, X_T3, lo12(offset)));
|
||||
write32le(buf + 8, itype(JALR, X_T1, X_T3, 0));
|
||||
write32le(buf + 12, itype(ADDI, 0, 0, 0));
|
||||
}
|
||||
|
||||
RelType RISCV::getDynRel(RelType type) const {
|
||||
return type == target->symbolicRel ? type
|
||||
: static_cast<RelType>(R_RISCV_NONE);
|
||||
}
|
||||
|
||||
RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const {
|
||||
switch (type) {
|
||||
case R_RISCV_ADD8:
|
||||
case R_RISCV_ADD16:
|
||||
case R_RISCV_ADD32:
|
||||
case R_RISCV_ADD64:
|
||||
case R_RISCV_SET6:
|
||||
case R_RISCV_SET8:
|
||||
case R_RISCV_SET16:
|
||||
case R_RISCV_SET32:
|
||||
case R_RISCV_SUB6:
|
||||
case R_RISCV_SUB8:
|
||||
case R_RISCV_SUB16:
|
||||
case R_RISCV_SUB32:
|
||||
case R_RISCV_SUB64:
|
||||
return R_RISCV_ADD;
|
||||
case R_RISCV_JAL:
|
||||
case R_RISCV_BRANCH:
|
||||
case R_RISCV_PCREL_HI20:
|
||||
case R_RISCV_RVC_BRANCH:
|
||||
case R_RISCV_RVC_JUMP:
|
||||
case R_RISCV_32_PCREL:
|
||||
return R_PC;
|
||||
case R_RISCV_CALL:
|
||||
case R_RISCV_CALL_PLT:
|
||||
return R_PLT_PC;
|
||||
case R_RISCV_GOT_HI20:
|
||||
return R_GOT_PC;
|
||||
case R_RISCV_PCREL_LO12_I:
|
||||
case R_RISCV_PCREL_LO12_S:
|
||||
return R_RISCV_PC_INDIRECT;
|
||||
case R_RISCV_TLS_GD_HI20:
|
||||
return R_TLSGD_PC;
|
||||
case R_RISCV_TLS_GOT_HI20:
|
||||
config->hasStaticTlsModel = true;
|
||||
return R_GOT_PC;
|
||||
case R_RISCV_TPREL_HI20:
|
||||
case R_RISCV_TPREL_LO12_I:
|
||||
case R_RISCV_TPREL_LO12_S:
|
||||
return R_TLS;
|
||||
case R_RISCV_RELAX:
|
||||
case R_RISCV_ALIGN:
|
||||
case R_RISCV_TPREL_ADD:
|
||||
return R_HINT;
|
||||
default:
|
||||
return R_ABS;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract bits V[Begin:End], where range is inclusive, and Begin must be < 63.
|
||||
static uint32_t extractBits(uint64_t v, uint32_t begin, uint32_t end) {
|
||||
return (v & ((1ULL << (begin + 1)) - 1)) >> end;
|
||||
}
|
||||
|
||||
void RISCV::relocateOne(uint8_t *loc, const RelType type,
|
||||
const uint64_t val) const {
|
||||
const unsigned bits = config->wordsize * 8;
|
||||
|
||||
switch (type) {
|
||||
case R_RISCV_32:
|
||||
write32le(loc, val);
|
||||
return;
|
||||
case R_RISCV_64:
|
||||
write64le(loc, val);
|
||||
return;
|
||||
|
||||
case R_RISCV_RVC_BRANCH: {
|
||||
checkInt(loc, static_cast<int64_t>(val) >> 1, 8, type);
|
||||
checkAlignment(loc, val, 2, type);
|
||||
uint16_t insn = read16le(loc) & 0xE383;
|
||||
uint16_t imm8 = extractBits(val, 8, 8) << 12;
|
||||
uint16_t imm4_3 = extractBits(val, 4, 3) << 10;
|
||||
uint16_t imm7_6 = extractBits(val, 7, 6) << 5;
|
||||
uint16_t imm2_1 = extractBits(val, 2, 1) << 3;
|
||||
uint16_t imm5 = extractBits(val, 5, 5) << 2;
|
||||
insn |= imm8 | imm4_3 | imm7_6 | imm2_1 | imm5;
|
||||
|
||||
write16le(loc, insn);
|
||||
return;
|
||||
}
|
||||
|
||||
case R_RISCV_RVC_JUMP: {
|
||||
checkInt(loc, static_cast<int64_t>(val) >> 1, 11, type);
|
||||
checkAlignment(loc, val, 2, type);
|
||||
uint16_t insn = read16le(loc) & 0xE003;
|
||||
uint16_t imm11 = extractBits(val, 11, 11) << 12;
|
||||
uint16_t imm4 = extractBits(val, 4, 4) << 11;
|
||||
uint16_t imm9_8 = extractBits(val, 9, 8) << 9;
|
||||
uint16_t imm10 = extractBits(val, 10, 10) << 8;
|
||||
uint16_t imm6 = extractBits(val, 6, 6) << 7;
|
||||
uint16_t imm7 = extractBits(val, 7, 7) << 6;
|
||||
uint16_t imm3_1 = extractBits(val, 3, 1) << 3;
|
||||
uint16_t imm5 = extractBits(val, 5, 5) << 2;
|
||||
insn |= imm11 | imm4 | imm9_8 | imm10 | imm6 | imm7 | imm3_1 | imm5;
|
||||
|
||||
write16le(loc, insn);
|
||||
return;
|
||||
}
|
||||
|
||||
case R_RISCV_RVC_LUI: {
|
||||
int64_t imm = SignExtend64(val + 0x800, bits) >> 12;
|
||||
checkInt(loc, imm, 6, type);
|
||||
if (imm == 0) { // `c.lui rd, 0` is illegal, convert to `c.li rd, 0`
|
||||
write16le(loc, (read16le(loc) & 0x0F83) | 0x4000);
|
||||
} else {
|
||||
uint16_t imm17 = extractBits(val + 0x800, 17, 17) << 12;
|
||||
uint16_t imm16_12 = extractBits(val + 0x800, 16, 12) << 2;
|
||||
write16le(loc, (read16le(loc) & 0xEF83) | imm17 | imm16_12);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
case R_RISCV_JAL: {
|
||||
checkInt(loc, static_cast<int64_t>(val) >> 1, 20, type);
|
||||
checkAlignment(loc, val, 2, type);
|
||||
|
||||
uint32_t insn = read32le(loc) & 0xFFF;
|
||||
uint32_t imm20 = extractBits(val, 20, 20) << 31;
|
||||
uint32_t imm10_1 = extractBits(val, 10, 1) << 21;
|
||||
uint32_t imm11 = extractBits(val, 11, 11) << 20;
|
||||
uint32_t imm19_12 = extractBits(val, 19, 12) << 12;
|
||||
insn |= imm20 | imm10_1 | imm11 | imm19_12;
|
||||
|
||||
write32le(loc, insn);
|
||||
return;
|
||||
}
|
||||
|
||||
case R_RISCV_BRANCH: {
|
||||
checkInt(loc, static_cast<int64_t>(val) >> 1, 12, type);
|
||||
checkAlignment(loc, val, 2, type);
|
||||
|
||||
uint32_t insn = read32le(loc) & 0x1FFF07F;
|
||||
uint32_t imm12 = extractBits(val, 12, 12) << 31;
|
||||
uint32_t imm10_5 = extractBits(val, 10, 5) << 25;
|
||||
uint32_t imm4_1 = extractBits(val, 4, 1) << 8;
|
||||
uint32_t imm11 = extractBits(val, 11, 11) << 7;
|
||||
insn |= imm12 | imm10_5 | imm4_1 | imm11;
|
||||
|
||||
write32le(loc, insn);
|
||||
return;
|
||||
}
|
||||
|
||||
// auipc + jalr pair
|
||||
case R_RISCV_CALL:
|
||||
case R_RISCV_CALL_PLT: {
|
||||
int64_t hi = SignExtend64(val + 0x800, bits) >> 12;
|
||||
checkInt(loc, hi, 20, type);
|
||||
if (isInt<20>(hi)) {
|
||||
relocateOne(loc, R_RISCV_PCREL_HI20, val);
|
||||
relocateOne(loc + 4, R_RISCV_PCREL_LO12_I, val);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
case R_RISCV_GOT_HI20:
|
||||
case R_RISCV_PCREL_HI20:
|
||||
case R_RISCV_TLS_GD_HI20:
|
||||
case R_RISCV_TLS_GOT_HI20:
|
||||
case R_RISCV_TPREL_HI20:
|
||||
case R_RISCV_HI20: {
|
||||
uint64_t hi = val + 0x800;
|
||||
checkInt(loc, SignExtend64(hi, bits) >> 12, 20, type);
|
||||
write32le(loc, (read32le(loc) & 0xFFF) | (hi & 0xFFFFF000));
|
||||
return;
|
||||
}
|
||||
|
||||
case R_RISCV_PCREL_LO12_I:
|
||||
case R_RISCV_TPREL_LO12_I:
|
||||
case R_RISCV_LO12_I: {
|
||||
uint64_t hi = (val + 0x800) >> 12;
|
||||
uint64_t lo = val - (hi << 12);
|
||||
write32le(loc, (read32le(loc) & 0xFFFFF) | ((lo & 0xFFF) << 20));
|
||||
return;
|
||||
}
|
||||
|
||||
case R_RISCV_PCREL_LO12_S:
|
||||
case R_RISCV_TPREL_LO12_S:
|
||||
case R_RISCV_LO12_S: {
|
||||
uint64_t hi = (val + 0x800) >> 12;
|
||||
uint64_t lo = val - (hi << 12);
|
||||
uint32_t imm11_5 = extractBits(lo, 11, 5) << 25;
|
||||
uint32_t imm4_0 = extractBits(lo, 4, 0) << 7;
|
||||
write32le(loc, (read32le(loc) & 0x1FFF07F) | imm11_5 | imm4_0);
|
||||
return;
|
||||
}
|
||||
|
||||
case R_RISCV_ADD8:
|
||||
*loc += val;
|
||||
return;
|
||||
case R_RISCV_ADD16:
|
||||
write16le(loc, read16le(loc) + val);
|
||||
return;
|
||||
case R_RISCV_ADD32:
|
||||
write32le(loc, read32le(loc) + val);
|
||||
return;
|
||||
case R_RISCV_ADD64:
|
||||
write64le(loc, read64le(loc) + val);
|
||||
return;
|
||||
case R_RISCV_SUB6:
|
||||
*loc = (*loc & 0xc0) | (((*loc & 0x3f) - val) & 0x3f);
|
||||
return;
|
||||
case R_RISCV_SUB8:
|
||||
*loc -= val;
|
||||
return;
|
||||
case R_RISCV_SUB16:
|
||||
write16le(loc, read16le(loc) - val);
|
||||
return;
|
||||
case R_RISCV_SUB32:
|
||||
write32le(loc, read32le(loc) - val);
|
||||
return;
|
||||
case R_RISCV_SUB64:
|
||||
write64le(loc, read64le(loc) - val);
|
||||
return;
|
||||
case R_RISCV_SET6:
|
||||
*loc = (*loc & 0xc0) | (val & 0x3f);
|
||||
return;
|
||||
case R_RISCV_SET8:
|
||||
*loc = val;
|
||||
return;
|
||||
case R_RISCV_SET16:
|
||||
write16le(loc, val);
|
||||
return;
|
||||
case R_RISCV_SET32:
|
||||
case R_RISCV_32_PCREL:
|
||||
write32le(loc, val);
|
||||
return;
|
||||
|
||||
case R_RISCV_TLS_DTPREL32:
|
||||
write32le(loc, val - dtpOffset);
|
||||
break;
|
||||
case R_RISCV_TLS_DTPREL64:
|
||||
write64le(loc, val - dtpOffset);
|
||||
break;
|
||||
|
||||
case R_RISCV_ALIGN:
|
||||
case R_RISCV_RELAX:
|
||||
return; // Ignored (for now)
|
||||
case R_RISCV_NONE:
|
||||
return; // Do nothing
|
||||
|
||||
// These are handled by the dynamic linker
|
||||
case R_RISCV_RELATIVE:
|
||||
case R_RISCV_COPY:
|
||||
case R_RISCV_JUMP_SLOT:
|
||||
// GP-relative relocations are only produced after relaxation, which
|
||||
// we don't support for now
|
||||
case R_RISCV_GPREL_I:
|
||||
case R_RISCV_GPREL_S:
|
||||
default:
|
||||
error(getErrorLocation(loc) +
|
||||
"unimplemented relocation: " + toString(type));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TargetInfo *elf::getRISCVTargetInfo() {
|
||||
static RISCV target;
|
||||
return ⌖
|
||||
}
|
||||
149
deps/lld/ELF/Arch/SPARCV9.cpp
vendored
149
deps/lld/ELF/Arch/SPARCV9.cpp
vendored
@ -1,149 +0,0 @@
|
||||
//===- SPARCV9.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 "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace {
|
||||
class SPARCV9 final : public TargetInfo {
|
||||
public:
|
||||
SPARCV9();
|
||||
RelExpr getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const override;
|
||||
void writePlt(uint8_t *buf, uint64_t gotEntryAddr, uint64_t pltEntryAddr,
|
||||
int32_t index, unsigned relOff) const override;
|
||||
void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
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;
|
||||
pltEntrySize = 32;
|
||||
pltHeaderSize = 4 * pltEntrySize;
|
||||
|
||||
defaultCommonPageSize = 8192;
|
||||
defaultMaxPageSize = 0x100000;
|
||||
defaultImageBase = 0x100000;
|
||||
}
|
||||
|
||||
RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const {
|
||||
switch (type) {
|
||||
case R_SPARC_32:
|
||||
case R_SPARC_UA32:
|
||||
case R_SPARC_64:
|
||||
case R_SPARC_UA64:
|
||||
return R_ABS;
|
||||
case R_SPARC_PC10:
|
||||
case R_SPARC_PC22:
|
||||
case R_SPARC_DISP32:
|
||||
case R_SPARC_WDISP30:
|
||||
return R_PC;
|
||||
case R_SPARC_GOT10:
|
||||
return R_GOT_OFF;
|
||||
case R_SPARC_GOT22:
|
||||
return R_GOT_OFF;
|
||||
case R_SPARC_WPLT30:
|
||||
return R_PLT_PC;
|
||||
case R_SPARC_NONE:
|
||||
return R_NONE;
|
||||
default:
|
||||
error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
|
||||
") against symbol " + toString(s));
|
||||
return R_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
void SPARCV9::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
switch (type) {
|
||||
case R_SPARC_32:
|
||||
case R_SPARC_UA32:
|
||||
// V-word32
|
||||
checkUInt(loc, val, 32, type);
|
||||
write32be(loc, val);
|
||||
break;
|
||||
case R_SPARC_DISP32:
|
||||
// V-disp32
|
||||
checkInt(loc, val, 32, type);
|
||||
write32be(loc, val);
|
||||
break;
|
||||
case R_SPARC_WDISP30:
|
||||
case R_SPARC_WPLT30:
|
||||
// V-disp30
|
||||
checkInt(loc, val, 32, type);
|
||||
write32be(loc, (read32be(loc) & ~0x3fffffff) | ((val >> 2) & 0x3fffffff));
|
||||
break;
|
||||
case R_SPARC_22:
|
||||
// V-imm22
|
||||
checkUInt(loc, val, 22, type);
|
||||
write32be(loc, (read32be(loc) & ~0x003fffff) | (val & 0x003fffff));
|
||||
break;
|
||||
case R_SPARC_GOT22:
|
||||
case R_SPARC_PC22:
|
||||
// T-imm22
|
||||
write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff));
|
||||
break;
|
||||
case R_SPARC_WDISP19:
|
||||
// V-disp19
|
||||
checkInt(loc, val, 21, type);
|
||||
write32be(loc, (read32be(loc) & ~0x0007ffff) | ((val >> 2) & 0x0007ffff));
|
||||
break;
|
||||
case R_SPARC_GOT10:
|
||||
case R_SPARC_PC10:
|
||||
// T-simm10
|
||||
write32be(loc, (read32be(loc) & ~0x000003ff) | (val & 0x000003ff));
|
||||
break;
|
||||
case R_SPARC_64:
|
||||
case R_SPARC_UA64:
|
||||
// V-xword64
|
||||
write64be(loc, val);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("unknown relocation");
|
||||
}
|
||||
}
|
||||
|
||||
void SPARCV9::writePlt(uint8_t *buf, uint64_t gotEntryAddr,
|
||||
uint64_t pltEntryAddr, int32_t index,
|
||||
unsigned relOff) const {
|
||||
const uint8_t pltData[] = {
|
||||
0x03, 0x00, 0x00, 0x00, // sethi (. - .PLT0), %g1
|
||||
0x30, 0x68, 0x00, 0x00, // ba,a %xcc, .PLT1
|
||||
0x01, 0x00, 0x00, 0x00, // nop
|
||||
0x01, 0x00, 0x00, 0x00, // nop
|
||||
0x01, 0x00, 0x00, 0x00, // nop
|
||||
0x01, 0x00, 0x00, 0x00, // nop
|
||||
0x01, 0x00, 0x00, 0x00, // nop
|
||||
0x01, 0x00, 0x00, 0x00 // nop
|
||||
};
|
||||
memcpy(buf, pltData, sizeof(pltData));
|
||||
|
||||
uint64_t off = pltHeaderSize + pltEntrySize * index;
|
||||
relocateOne(buf, R_SPARC_22, off);
|
||||
relocateOne(buf + 4, R_SPARC_WDISP19, -(off + 4 - pltEntrySize));
|
||||
}
|
||||
|
||||
TargetInfo *elf::getSPARCV9TargetInfo() {
|
||||
static SPARCV9 target;
|
||||
return ⌖
|
||||
}
|
||||
554
deps/lld/ELF/Arch/X86.cpp
vendored
554
deps/lld/ELF/Arch/X86.cpp
vendored
@ -1,554 +0,0 @@
|
||||
//===- X86.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 "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace {
|
||||
class X86 : public TargetInfo {
|
||||
public:
|
||||
X86();
|
||||
int getTlsGdRelaxSkip(RelType type) const override;
|
||||
RelExpr getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const override;
|
||||
int64_t getImplicitAddend(const uint8_t *buf, RelType type) const override;
|
||||
void writeGotPltHeader(uint8_t *buf) const override;
|
||||
RelType getDynRel(RelType type) const override;
|
||||
void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
|
||||
void writeIgotPlt(uint8_t *buf, const Symbol &s) const override;
|
||||
void writePltHeader(uint8_t *buf) const override;
|
||||
void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
|
||||
int32_t index, unsigned relOff) const override;
|
||||
void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
|
||||
RelExpr adjustRelaxExpr(RelType type, const uint8_t *data,
|
||||
RelExpr expr) const override;
|
||||
void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) 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;
|
||||
tlsGotRel = R_386_TLS_TPOFF;
|
||||
tlsModuleIndexRel = R_386_TLS_DTPMOD32;
|
||||
tlsOffsetRel = R_386_TLS_DTPOFF32;
|
||||
pltEntrySize = 16;
|
||||
pltHeaderSize = 16;
|
||||
trapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3
|
||||
|
||||
// Align to the non-PAE large page size (known as a superpage or huge page).
|
||||
// FreeBSD automatically promotes large, superpage-aligned allocations.
|
||||
defaultImageBase = 0x400000;
|
||||
}
|
||||
|
||||
int X86::getTlsGdRelaxSkip(RelType type) const {
|
||||
return 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:
|
||||
case R_386_32:
|
||||
return R_ABS;
|
||||
case R_386_TLS_LDO_32:
|
||||
return R_DTPREL;
|
||||
case R_386_TLS_GD:
|
||||
return R_TLSGD_GOTPLT;
|
||||
case R_386_TLS_LDM:
|
||||
return R_TLSLD_GOTPLT;
|
||||
case R_386_PLT32:
|
||||
return R_PLT_PC;
|
||||
case R_386_PC8:
|
||||
case R_386_PC16:
|
||||
case R_386_PC32:
|
||||
return R_PC;
|
||||
case R_386_GOTPC:
|
||||
return R_GOTPLTONLY_PC;
|
||||
case R_386_TLS_IE:
|
||||
return R_GOT;
|
||||
case R_386_GOT32:
|
||||
case R_386_GOT32X:
|
||||
// These relocations are arguably mis-designed because their calculations
|
||||
// depend on the instructions they are applied to. This is bad because we
|
||||
// usually don't care about whether the target section contains valid
|
||||
// machine instructions or not. But this is part of the documented ABI, so
|
||||
// we had to implement as the standard requires.
|
||||
//
|
||||
// x86 does not support PC-relative data access. Therefore, in order to
|
||||
// access GOT contents, a GOT address needs to be known at link-time
|
||||
// (which means non-PIC) or compilers have to emit code to get a GOT
|
||||
// address at runtime (which means code is position-independent but
|
||||
// compilers need to emit extra code for each GOT access.) This decision
|
||||
// is made at compile-time. In the latter case, compilers emit code to
|
||||
// load an GOT address to a register, which is usually %ebx.
|
||||
//
|
||||
// So, there are two ways to refer to symbol foo's GOT entry: foo@GOT or
|
||||
// foo@GOT(%ebx).
|
||||
//
|
||||
// foo@GOT is not usable in PIC. If we are creating a PIC output and if we
|
||||
// find such relocation, we should report an error. foo@GOT is resolved to
|
||||
// an *absolute* address of foo's GOT entry, because both GOT address and
|
||||
// foo's offset are known. In other words, it's G + A.
|
||||
//
|
||||
// foo@GOT(%ebx) needs to be resolved to a *relative* offset from a GOT to
|
||||
// foo's GOT entry in the table, because GOT address is not known but foo's
|
||||
// offset in the table is known. It's G + A - GOT.
|
||||
//
|
||||
// It's unfortunate that compilers emit the same relocation for these
|
||||
// different use cases. In order to distinguish them, we have to read a
|
||||
// machine instruction.
|
||||
//
|
||||
// The following code implements it. We assume that Loc[0] is the first byte
|
||||
// of a displacement or an immediate field of a valid machine
|
||||
// instruction. That means a ModRM byte is at Loc[-1]. By taking a look at
|
||||
// the byte, we can determine whether the instruction 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_GOTIE:
|
||||
return R_GOTPLT;
|
||||
case R_386_GOTOFF:
|
||||
return R_GOTPLTREL;
|
||||
case R_386_TLS_LE:
|
||||
return R_TLS;
|
||||
case R_386_TLS_LE_32:
|
||||
return R_NEG_TLS;
|
||||
case R_386_NONE:
|
||||
return R_NONE;
|
||||
default:
|
||||
error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
|
||||
") against symbol " + toString(s));
|
||||
return R_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
RelExpr X86::adjustRelaxExpr(RelType type, const uint8_t *data,
|
||||
RelExpr expr) const {
|
||||
switch (expr) {
|
||||
default:
|
||||
return expr;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void X86::writeGotPltHeader(uint8_t *buf) const {
|
||||
write32le(buf, mainPart->dynamic->getVA());
|
||||
}
|
||||
|
||||
void X86::writeGotPlt(uint8_t *buf, const Symbol &s) const {
|
||||
// Entries in .got.plt initially points back to the corresponding
|
||||
// PLT entries with a fixed offset to skip the first instruction.
|
||||
write32le(buf, s.getPltVA() + 6);
|
||||
}
|
||||
|
||||
void X86::writeIgotPlt(uint8_t *buf, const Symbol &s) const {
|
||||
// An x86 entry is the address of the ifunc resolver function.
|
||||
write32le(buf, s.getVA());
|
||||
}
|
||||
|
||||
RelType X86::getDynRel(RelType type) const {
|
||||
if (type == R_386_TLS_LE)
|
||||
return R_386_TLS_TPOFF;
|
||||
if (type == R_386_TLS_LE_32)
|
||||
return R_386_TLS_TPOFF32;
|
||||
return type;
|
||||
}
|
||||
|
||||
void X86::writePltHeader(uint8_t *buf) const {
|
||||
if (config->isPic) {
|
||||
const uint8_t v[] = {
|
||||
0xff, 0xb3, 0x04, 0x00, 0x00, 0x00, // pushl 4(%ebx)
|
||||
0xff, 0xa3, 0x08, 0x00, 0x00, 0x00, // jmp *8(%ebx)
|
||||
0x90, 0x90, 0x90, 0x90 // nop
|
||||
};
|
||||
memcpy(buf, v, sizeof(v));
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t pltData[] = {
|
||||
0xff, 0x35, 0, 0, 0, 0, // pushl (GOTPLT+4)
|
||||
0xff, 0x25, 0, 0, 0, 0, // jmp *(GOTPLT+8)
|
||||
0x90, 0x90, 0x90, 0x90, // nop
|
||||
};
|
||||
memcpy(buf, pltData, sizeof(pltData));
|
||||
uint32_t gotPlt = in.gotPlt->getVA();
|
||||
write32le(buf + 2, gotPlt + 4);
|
||||
write32le(buf + 8, gotPlt + 8);
|
||||
}
|
||||
|
||||
void X86::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
|
||||
uint64_t pltEntryAddr, int32_t index,
|
||||
unsigned relOff) const {
|
||||
if (config->isPic) {
|
||||
const uint8_t inst[] = {
|
||||
0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx)
|
||||
0x68, 0, 0, 0, 0, // pushl $reloc_offset
|
||||
0xe9, 0, 0, 0, 0, // jmp .PLT0@PC
|
||||
};
|
||||
memcpy(buf, inst, sizeof(inst));
|
||||
write32le(buf + 2, gotPltEntryAddr - in.gotPlt->getVA());
|
||||
} else {
|
||||
const uint8_t inst[] = {
|
||||
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
|
||||
0x68, 0, 0, 0, 0, // pushl $reloc_offset
|
||||
0xe9, 0, 0, 0, 0, // jmp .PLT0@PC
|
||||
};
|
||||
memcpy(buf, inst, sizeof(inst));
|
||||
write32le(buf + 2, gotPltEntryAddr);
|
||||
}
|
||||
|
||||
write32le(buf + 7, relOff);
|
||||
write32le(buf + 12, -pltHeaderSize - pltEntrySize * index - 16);
|
||||
}
|
||||
|
||||
int64_t X86::getImplicitAddend(const uint8_t *buf, RelType type) const {
|
||||
switch (type) {
|
||||
case R_386_8:
|
||||
case R_386_PC8:
|
||||
return SignExtend64<8>(*buf);
|
||||
case R_386_16:
|
||||
case R_386_PC16:
|
||||
return SignExtend64<16>(read16le(buf));
|
||||
case R_386_32:
|
||||
case R_386_GOT32:
|
||||
case R_386_GOT32X:
|
||||
case R_386_GOTOFF:
|
||||
case R_386_GOTPC:
|
||||
case R_386_PC32:
|
||||
case R_386_PLT32:
|
||||
case R_386_TLS_LDO_32:
|
||||
case R_386_TLS_LE:
|
||||
return SignExtend64<32>(read32le(buf));
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void X86::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
switch (type) {
|
||||
case R_386_8:
|
||||
// R_386_{PC,}{8,16} are not part of the i386 psABI, but they are
|
||||
// being used for some 16-bit programs such as boot loaders, so
|
||||
// we want to support them.
|
||||
checkIntUInt(loc, val, 8, type);
|
||||
*loc = val;
|
||||
break;
|
||||
case R_386_PC8:
|
||||
checkInt(loc, val, 8, type);
|
||||
*loc = val;
|
||||
break;
|
||||
case R_386_16:
|
||||
checkIntUInt(loc, val, 16, type);
|
||||
write16le(loc, val);
|
||||
break;
|
||||
case R_386_PC16:
|
||||
// R_386_PC16 is normally used with 16 bit code. In that situation
|
||||
// the PC is 16 bits, just like the addend. This means that it can
|
||||
// point from any 16 bit address to any other if the possibility
|
||||
// of wrapping is included.
|
||||
// The only restriction we have to check then is that the destination
|
||||
// address fits in 16 bits. That is impossible to do here. The problem is
|
||||
// that we are passed the final value, which already had the
|
||||
// current location subtracted from it.
|
||||
// We just check that Val fits in 17 bits. This misses some cases, but
|
||||
// should have no false positives.
|
||||
checkInt(loc, val, 17, type);
|
||||
write16le(loc, val);
|
||||
break;
|
||||
case R_386_32:
|
||||
case R_386_GOT32:
|
||||
case R_386_GOT32X:
|
||||
case R_386_GOTOFF:
|
||||
case R_386_GOTPC:
|
||||
case R_386_PC32:
|
||||
case R_386_PLT32:
|
||||
case R_386_RELATIVE:
|
||||
case R_386_TLS_DTPMOD32:
|
||||
case R_386_TLS_DTPOFF32:
|
||||
case R_386_TLS_GD:
|
||||
case R_386_TLS_GOTIE:
|
||||
case R_386_TLS_IE:
|
||||
case R_386_TLS_LDM:
|
||||
case R_386_TLS_LDO_32:
|
||||
case R_386_TLS_LE:
|
||||
case R_386_TLS_LE_32:
|
||||
case R_386_TLS_TPOFF:
|
||||
case R_386_TLS_TPOFF32:
|
||||
checkInt(loc, val, 32, type);
|
||||
write32le(loc, val);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("unknown relocation");
|
||||
}
|
||||
}
|
||||
|
||||
void X86::relaxTlsGdToLe(uint8_t *loc, RelType type, 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);
|
||||
}
|
||||
|
||||
void X86::relaxTlsGdToIe(uint8_t *loc, RelType type, 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);
|
||||
}
|
||||
|
||||
// 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, RelType type, uint64_t val) const {
|
||||
// Ulrich's document section 6.2 says that @gotntpoff can
|
||||
// be used with MOVL or ADDL instructions.
|
||||
// @indntpoff is similar to @gotntpoff, but for use in
|
||||
// position dependent code.
|
||||
uint8_t reg = (loc[-1] >> 3) & 7;
|
||||
|
||||
if (type == R_386_TLS_IE) {
|
||||
if (loc[-1] == 0xa1) {
|
||||
// "movl foo@indntpoff,%eax" -> "movl $foo,%eax"
|
||||
// This case is different from the generic case below because
|
||||
// this is a 5 byte instruction while below is 6 bytes.
|
||||
loc[-1] = 0xb8;
|
||||
} else if (loc[-2] == 0x8b) {
|
||||
// "movl foo@indntpoff,%reg" -> "movl $foo,%reg"
|
||||
loc[-2] = 0xc7;
|
||||
loc[-1] = 0xc0 | reg;
|
||||
} else {
|
||||
// "addl foo@indntpoff,%reg" -> "addl $foo,%reg"
|
||||
loc[-2] = 0x81;
|
||||
loc[-1] = 0xc0 | reg;
|
||||
}
|
||||
} else {
|
||||
assert(type == R_386_TLS_GOTIE);
|
||||
if (loc[-2] == 0x8b) {
|
||||
// "movl foo@gottpoff(%rip),%reg" -> "movl $foo,%reg"
|
||||
loc[-2] = 0xc7;
|
||||
loc[-1] = 0xc0 | reg;
|
||||
} else {
|
||||
// "addl foo@gotntpoff(%rip),%reg" -> "leal foo(%reg),%reg"
|
||||
loc[-2] = 0x8d;
|
||||
loc[-1] = 0x80 | (reg << 3) | reg;
|
||||
}
|
||||
}
|
||||
write32le(loc, val);
|
||||
}
|
||||
|
||||
void X86::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
if (type == R_386_TLS_LDO_32) {
|
||||
write32le(loc, val);
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert
|
||||
// leal foo(%reg),%eax
|
||||
// call ___tls_get_addr
|
||||
// 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
|
||||
};
|
||||
memcpy(loc - 2, inst, sizeof(inst));
|
||||
}
|
||||
|
||||
namespace {
|
||||
class RetpolinePic : public X86 {
|
||||
public:
|
||||
RetpolinePic();
|
||||
void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
|
||||
void writePltHeader(uint8_t *buf) const override;
|
||||
void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
|
||||
int32_t index, unsigned relOff) const override;
|
||||
};
|
||||
|
||||
class RetpolineNoPic : public X86 {
|
||||
public:
|
||||
RetpolineNoPic();
|
||||
void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
|
||||
void writePltHeader(uint8_t *buf) const override;
|
||||
void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
|
||||
int32_t index, unsigned relOff) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
RetpolinePic::RetpolinePic() {
|
||||
pltHeaderSize = 48;
|
||||
pltEntrySize = 32;
|
||||
}
|
||||
|
||||
void RetpolinePic::writeGotPlt(uint8_t *buf, const Symbol &s) const {
|
||||
write32le(buf, s.getPltVA() + 17);
|
||||
}
|
||||
|
||||
void RetpolinePic::writePltHeader(uint8_t *buf) const {
|
||||
const uint8_t insn[] = {
|
||||
0xff, 0xb3, 4, 0, 0, 0, // 0: pushl 4(%ebx)
|
||||
0x50, // 6: pushl %eax
|
||||
0x8b, 0x83, 8, 0, 0, 0, // 7: mov 8(%ebx), %eax
|
||||
0xe8, 0x0e, 0x00, 0x00, 0x00, // d: call next
|
||||
0xf3, 0x90, // 12: loop: pause
|
||||
0x0f, 0xae, 0xe8, // 14: lfence
|
||||
0xeb, 0xf9, // 17: jmp loop
|
||||
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 19: int3; .align 16
|
||||
0x89, 0x0c, 0x24, // 20: next: mov %ecx, (%esp)
|
||||
0x8b, 0x4c, 0x24, 0x04, // 23: mov 0x4(%esp), %ecx
|
||||
0x89, 0x44, 0x24, 0x04, // 27: mov %eax ,0x4(%esp)
|
||||
0x89, 0xc8, // 2b: mov %ecx, %eax
|
||||
0x59, // 2d: pop %ecx
|
||||
0xc3, // 2e: ret
|
||||
0xcc, // 2f: int3; padding
|
||||
};
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
}
|
||||
|
||||
void RetpolinePic::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
|
||||
uint64_t pltEntryAddr, int32_t index,
|
||||
unsigned relOff) const {
|
||||
const uint8_t insn[] = {
|
||||
0x50, // pushl %eax
|
||||
0x8b, 0x83, 0, 0, 0, 0, // mov foo@GOT(%ebx), %eax
|
||||
0xe8, 0, 0, 0, 0, // call plt+0x20
|
||||
0xe9, 0, 0, 0, 0, // jmp plt+0x12
|
||||
0x68, 0, 0, 0, 0, // pushl $reloc_offset
|
||||
0xe9, 0, 0, 0, 0, // jmp plt+0
|
||||
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // int3; padding
|
||||
};
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
|
||||
uint32_t ebx = in.gotPlt->getVA();
|
||||
unsigned off = pltHeaderSize + pltEntrySize * index;
|
||||
write32le(buf + 3, gotPltEntryAddr - ebx);
|
||||
write32le(buf + 8, -off - 12 + 32);
|
||||
write32le(buf + 13, -off - 17 + 18);
|
||||
write32le(buf + 18, relOff);
|
||||
write32le(buf + 23, -off - 27);
|
||||
}
|
||||
|
||||
RetpolineNoPic::RetpolineNoPic() {
|
||||
pltHeaderSize = 48;
|
||||
pltEntrySize = 32;
|
||||
}
|
||||
|
||||
void RetpolineNoPic::writeGotPlt(uint8_t *buf, const Symbol &s) const {
|
||||
write32le(buf, s.getPltVA() + 16);
|
||||
}
|
||||
|
||||
void RetpolineNoPic::writePltHeader(uint8_t *buf) const {
|
||||
const uint8_t insn[] = {
|
||||
0xff, 0x35, 0, 0, 0, 0, // 0: pushl GOTPLT+4
|
||||
0x50, // 6: pushl %eax
|
||||
0xa1, 0, 0, 0, 0, // 7: mov GOTPLT+8, %eax
|
||||
0xe8, 0x0f, 0x00, 0x00, 0x00, // c: call next
|
||||
0xf3, 0x90, // 11: loop: pause
|
||||
0x0f, 0xae, 0xe8, // 13: lfence
|
||||
0xeb, 0xf9, // 16: jmp loop
|
||||
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 18: int3
|
||||
0xcc, 0xcc, 0xcc, // 1f: int3; .align 16
|
||||
0x89, 0x0c, 0x24, // 20: next: mov %ecx, (%esp)
|
||||
0x8b, 0x4c, 0x24, 0x04, // 23: mov 0x4(%esp), %ecx
|
||||
0x89, 0x44, 0x24, 0x04, // 27: mov %eax ,0x4(%esp)
|
||||
0x89, 0xc8, // 2b: mov %ecx, %eax
|
||||
0x59, // 2d: pop %ecx
|
||||
0xc3, // 2e: ret
|
||||
0xcc, // 2f: int3; padding
|
||||
};
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
|
||||
uint32_t gotPlt = in.gotPlt->getVA();
|
||||
write32le(buf + 2, gotPlt + 4);
|
||||
write32le(buf + 8, gotPlt + 8);
|
||||
}
|
||||
|
||||
void RetpolineNoPic::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
|
||||
uint64_t pltEntryAddr, int32_t index,
|
||||
unsigned relOff) const {
|
||||
const uint8_t insn[] = {
|
||||
0x50, // 0: pushl %eax
|
||||
0xa1, 0, 0, 0, 0, // 1: mov foo_in_GOT, %eax
|
||||
0xe8, 0, 0, 0, 0, // 6: call plt+0x20
|
||||
0xe9, 0, 0, 0, 0, // b: jmp plt+0x11
|
||||
0x68, 0, 0, 0, 0, // 10: pushl $reloc_offset
|
||||
0xe9, 0, 0, 0, 0, // 15: jmp plt+0
|
||||
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding
|
||||
0xcc, // 1f: int3; padding
|
||||
};
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
|
||||
unsigned off = pltHeaderSize + pltEntrySize * index;
|
||||
write32le(buf + 2, gotPltEntryAddr);
|
||||
write32le(buf + 7, -off - 11 + 32);
|
||||
write32le(buf + 12, -off - 16 + 17);
|
||||
write32le(buf + 17, relOff);
|
||||
write32le(buf + 22, -off - 26);
|
||||
}
|
||||
|
||||
TargetInfo *elf::getX86TargetInfo() {
|
||||
if (config->zRetpolineplt) {
|
||||
if (config->isPic) {
|
||||
static RetpolinePic t;
|
||||
return &t;
|
||||
}
|
||||
static RetpolineNoPic t;
|
||||
return &t;
|
||||
}
|
||||
|
||||
static X86 t;
|
||||
return &t;
|
||||
}
|
||||
701
deps/lld/ELF/Arch/X86_64.cpp
vendored
701
deps/lld/ELF/Arch/X86_64.cpp
vendored
@ -1,701 +0,0 @@
|
||||
//===- X86_64.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 "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace {
|
||||
class X86_64 : public TargetInfo {
|
||||
public:
|
||||
X86_64();
|
||||
int getTlsGdRelaxSkip(RelType type) const override;
|
||||
RelExpr getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const override;
|
||||
RelType getDynRel(RelType type) const override;
|
||||
void writeGotPltHeader(uint8_t *buf) const override;
|
||||
void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
|
||||
void writePltHeader(uint8_t *buf) const override;
|
||||
void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
|
||||
int32_t index, unsigned relOff) const override;
|
||||
void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
|
||||
RelExpr adjustRelaxExpr(RelType type, const uint8_t *data,
|
||||
RelExpr expr) const override;
|
||||
void relaxGot(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||
bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
|
||||
uint8_t stOther) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
X86_64::X86_64() {
|
||||
copyRel = R_X86_64_COPY;
|
||||
gotRel = R_X86_64_GLOB_DAT;
|
||||
noneRel = R_X86_64_NONE;
|
||||
pltRel = R_X86_64_JUMP_SLOT;
|
||||
relativeRel = R_X86_64_RELATIVE;
|
||||
iRelativeRel = R_X86_64_IRELATIVE;
|
||||
symbolicRel = R_X86_64_64;
|
||||
tlsDescRel = R_X86_64_TLSDESC;
|
||||
tlsGotRel = R_X86_64_TPOFF64;
|
||||
tlsModuleIndexRel = R_X86_64_DTPMOD64;
|
||||
tlsOffsetRel = R_X86_64_DTPOFF64;
|
||||
pltEntrySize = 16;
|
||||
pltHeaderSize = 16;
|
||||
trapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3
|
||||
|
||||
// Align to the large page size (known as a superpage or huge page).
|
||||
// FreeBSD automatically promotes large, superpage-aligned allocations.
|
||||
defaultImageBase = 0x200000;
|
||||
}
|
||||
|
||||
int X86_64::getTlsGdRelaxSkip(RelType type) const { return 2; }
|
||||
|
||||
RelExpr X86_64::getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const {
|
||||
if (type == R_X86_64_GOTTPOFF)
|
||||
config->hasStaticTlsModel = true;
|
||||
|
||||
switch (type) {
|
||||
case R_X86_64_8:
|
||||
case R_X86_64_16:
|
||||
case R_X86_64_32:
|
||||
case R_X86_64_32S:
|
||||
case R_X86_64_64:
|
||||
return R_ABS;
|
||||
case R_X86_64_DTPOFF32:
|
||||
case R_X86_64_DTPOFF64:
|
||||
return R_DTPREL;
|
||||
case R_X86_64_TPOFF32:
|
||||
return R_TLS;
|
||||
case R_X86_64_TLSDESC_CALL:
|
||||
return R_TLSDESC_CALL;
|
||||
case R_X86_64_TLSLD:
|
||||
return R_TLSLD_PC;
|
||||
case R_X86_64_TLSGD:
|
||||
return R_TLSGD_PC;
|
||||
case R_X86_64_SIZE32:
|
||||
case R_X86_64_SIZE64:
|
||||
return R_SIZE;
|
||||
case R_X86_64_PLT32:
|
||||
return R_PLT_PC;
|
||||
case R_X86_64_PC8:
|
||||
case R_X86_64_PC16:
|
||||
case R_X86_64_PC32:
|
||||
case R_X86_64_PC64:
|
||||
return R_PC;
|
||||
case R_X86_64_GOT32:
|
||||
case R_X86_64_GOT64:
|
||||
return R_GOTPLT;
|
||||
case R_X86_64_GOTPC32_TLSDESC:
|
||||
return R_TLSDESC_PC;
|
||||
case R_X86_64_GOTPCREL:
|
||||
case R_X86_64_GOTPCRELX:
|
||||
case R_X86_64_REX_GOTPCRELX:
|
||||
case R_X86_64_GOTTPOFF:
|
||||
return R_GOT_PC;
|
||||
case R_X86_64_GOTOFF64:
|
||||
return R_GOTPLTREL;
|
||||
case R_X86_64_GOTPC32:
|
||||
case R_X86_64_GOTPC64:
|
||||
return R_GOTPLTONLY_PC;
|
||||
case R_X86_64_NONE:
|
||||
return R_NONE;
|
||||
default:
|
||||
error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
|
||||
") against symbol " + toString(s));
|
||||
return R_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
void X86_64::writeGotPltHeader(uint8_t *buf) const {
|
||||
// The first entry holds the value of _DYNAMIC. It is not clear why that is
|
||||
// required, but it is documented in the psabi and the glibc dynamic linker
|
||||
// seems to use it (note that this is relevant for linking ld.so, not any
|
||||
// other program).
|
||||
write64le(buf, mainPart->dynamic->getVA());
|
||||
}
|
||||
|
||||
void X86_64::writeGotPlt(uint8_t *buf, const Symbol &s) const {
|
||||
// See comments in X86::writeGotPlt.
|
||||
write64le(buf, s.getPltVA() + 6);
|
||||
}
|
||||
|
||||
void X86_64::writePltHeader(uint8_t *buf) const {
|
||||
const uint8_t pltData[] = {
|
||||
0xff, 0x35, 0, 0, 0, 0, // pushq GOTPLT+8(%rip)
|
||||
0xff, 0x25, 0, 0, 0, 0, // jmp *GOTPLT+16(%rip)
|
||||
0x0f, 0x1f, 0x40, 0x00, // nop
|
||||
};
|
||||
memcpy(buf, pltData, sizeof(pltData));
|
||||
uint64_t gotPlt = in.gotPlt->getVA();
|
||||
uint64_t plt = in.plt->getVA();
|
||||
write32le(buf + 2, gotPlt - plt + 2); // GOTPLT+8
|
||||
write32le(buf + 8, gotPlt - plt + 4); // GOTPLT+16
|
||||
}
|
||||
|
||||
void X86_64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
|
||||
uint64_t pltEntryAddr, int32_t index,
|
||||
unsigned relOff) const {
|
||||
const uint8_t inst[] = {
|
||||
0xff, 0x25, 0, 0, 0, 0, // jmpq *got(%rip)
|
||||
0x68, 0, 0, 0, 0, // pushq <relocation index>
|
||||
0xe9, 0, 0, 0, 0, // jmpq plt[0]
|
||||
};
|
||||
memcpy(buf, inst, sizeof(inst));
|
||||
|
||||
write32le(buf + 2, gotPltEntryAddr - pltEntryAddr - 6);
|
||||
write32le(buf + 7, index);
|
||||
write32le(buf + 12, -pltHeaderSize - pltEntrySize * index - 16);
|
||||
}
|
||||
|
||||
RelType X86_64::getDynRel(RelType type) const {
|
||||
if (type == R_X86_64_64 || type == R_X86_64_PC64 || type == R_X86_64_SIZE32 ||
|
||||
type == R_X86_64_SIZE64)
|
||||
return type;
|
||||
return R_X86_64_NONE;
|
||||
}
|
||||
|
||||
void X86_64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
if (type == R_X86_64_TLSGD) {
|
||||
// Convert
|
||||
// .byte 0x66
|
||||
// leaq x@tlsgd(%rip), %rdi
|
||||
// .word 0x6666
|
||||
// rex64
|
||||
// call __tls_get_addr@plt
|
||||
// to the following two instructions.
|
||||
const uint8_t inst[] = {
|
||||
0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00,
|
||||
0x00, 0x00, // mov %fs:0x0,%rax
|
||||
0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea x@tpoff,%rax
|
||||
};
|
||||
memcpy(loc - 4, inst, sizeof(inst));
|
||||
|
||||
// The original code used a pc relative relocation and so we have to
|
||||
// compensate for the -4 in had in the addend.
|
||||
write32le(loc + 8, val + 4);
|
||||
} else {
|
||||
// Convert
|
||||
// lea x@tlsgd(%rip), %rax
|
||||
// call *(%rax)
|
||||
// to the following two instructions.
|
||||
assert(type == R_X86_64_GOTPC32_TLSDESC);
|
||||
if (memcmp(loc - 3, "\x48\x8d\x05", 3)) {
|
||||
error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used "
|
||||
"in callq *x@tlsdesc(%rip), %rax");
|
||||
return;
|
||||
}
|
||||
// movq $x@tpoff(%rip),%rax
|
||||
loc[-2] = 0xc7;
|
||||
loc[-1] = 0xc0;
|
||||
write32le(loc, val + 4);
|
||||
// xchg ax,ax
|
||||
loc[4] = 0x66;
|
||||
loc[5] = 0x90;
|
||||
}
|
||||
}
|
||||
|
||||
void X86_64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
if (type == R_X86_64_TLSGD) {
|
||||
// Convert
|
||||
// .byte 0x66
|
||||
// leaq x@tlsgd(%rip), %rdi
|
||||
// .word 0x6666
|
||||
// rex64
|
||||
// call __tls_get_addr@plt
|
||||
// to the following two instructions.
|
||||
const uint8_t inst[] = {
|
||||
0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00,
|
||||
0x00, 0x00, // mov %fs:0x0,%rax
|
||||
0x48, 0x03, 0x05, 0, 0, 0, 0, // addq x@gottpoff(%rip),%rax
|
||||
};
|
||||
memcpy(loc - 4, inst, sizeof(inst));
|
||||
|
||||
// Both code sequences are PC relatives, but since we are moving the
|
||||
// constant forward by 8 bytes we have to subtract the value by 8.
|
||||
write32le(loc + 8, val - 8);
|
||||
} else {
|
||||
// Convert
|
||||
// lea x@tlsgd(%rip), %rax
|
||||
// call *(%rax)
|
||||
// to the following two instructions.
|
||||
assert(type == R_X86_64_GOTPC32_TLSDESC);
|
||||
if (memcmp(loc - 3, "\x48\x8d\x05", 3)) {
|
||||
error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used "
|
||||
"in callq *x@tlsdesc(%rip), %rax");
|
||||
return;
|
||||
}
|
||||
// movq x@gottpoff(%rip),%rax
|
||||
loc[-2] = 0x8b;
|
||||
write32le(loc, val);
|
||||
// xchg ax,ax
|
||||
loc[4] = 0x66;
|
||||
loc[5] = 0x90;
|
||||
}
|
||||
}
|
||||
|
||||
// In some conditions, R_X86_64_GOTTPOFF relocation can be optimized to
|
||||
// R_X86_64_TPOFF32 so that it does not use GOT.
|
||||
void X86_64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
uint8_t *inst = loc - 3;
|
||||
uint8_t reg = loc[-1] >> 3;
|
||||
uint8_t *regSlot = loc - 1;
|
||||
|
||||
// Note that ADD with RSP or R12 is converted to ADD instead of LEA
|
||||
// because LEA with these registers needs 4 bytes to encode and thus
|
||||
// wouldn't fit the space.
|
||||
|
||||
if (memcmp(inst, "\x48\x03\x25", 3) == 0) {
|
||||
// "addq foo@gottpoff(%rip),%rsp" -> "addq $foo,%rsp"
|
||||
memcpy(inst, "\x48\x81\xc4", 3);
|
||||
} else if (memcmp(inst, "\x4c\x03\x25", 3) == 0) {
|
||||
// "addq foo@gottpoff(%rip),%r12" -> "addq $foo,%r12"
|
||||
memcpy(inst, "\x49\x81\xc4", 3);
|
||||
} else if (memcmp(inst, "\x4c\x03", 2) == 0) {
|
||||
// "addq foo@gottpoff(%rip),%r[8-15]" -> "leaq foo(%r[8-15]),%r[8-15]"
|
||||
memcpy(inst, "\x4d\x8d", 2);
|
||||
*regSlot = 0x80 | (reg << 3) | reg;
|
||||
} else if (memcmp(inst, "\x48\x03", 2) == 0) {
|
||||
// "addq foo@gottpoff(%rip),%reg -> "leaq foo(%reg),%reg"
|
||||
memcpy(inst, "\x48\x8d", 2);
|
||||
*regSlot = 0x80 | (reg << 3) | reg;
|
||||
} else if (memcmp(inst, "\x4c\x8b", 2) == 0) {
|
||||
// "movq foo@gottpoff(%rip),%r[8-15]" -> "movq $foo,%r[8-15]"
|
||||
memcpy(inst, "\x49\xc7", 2);
|
||||
*regSlot = 0xc0 | reg;
|
||||
} else if (memcmp(inst, "\x48\x8b", 2) == 0) {
|
||||
// "movq foo@gottpoff(%rip),%reg" -> "movq $foo,%reg"
|
||||
memcpy(inst, "\x48\xc7", 2);
|
||||
*regSlot = 0xc0 | reg;
|
||||
} else {
|
||||
error(getErrorLocation(loc - 3) +
|
||||
"R_X86_64_GOTTPOFF must be used in MOVQ or ADDQ instructions only");
|
||||
}
|
||||
|
||||
// The original code used a PC relative relocation.
|
||||
// Need to compensate for the -4 it had in the addend.
|
||||
write32le(loc, val + 4);
|
||||
}
|
||||
|
||||
void X86_64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
if (type == R_X86_64_DTPOFF64) {
|
||||
write64le(loc, val);
|
||||
return;
|
||||
}
|
||||
if (type == R_X86_64_DTPOFF32) {
|
||||
write32le(loc, val);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t inst[] = {
|
||||
0x66, 0x66, // .word 0x6666
|
||||
0x66, // .byte 0x66
|
||||
0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0,%rax
|
||||
};
|
||||
|
||||
if (loc[4] == 0xe8) {
|
||||
// Convert
|
||||
// leaq bar@tlsld(%rip), %rdi # 48 8d 3d <Loc>
|
||||
// callq __tls_get_addr@PLT # e8 <disp32>
|
||||
// leaq bar@dtpoff(%rax), %rcx
|
||||
// to
|
||||
// .word 0x6666
|
||||
// .byte 0x66
|
||||
// mov %fs:0,%rax
|
||||
// leaq bar@tpoff(%rax), %rcx
|
||||
memcpy(loc - 3, inst, sizeof(inst));
|
||||
return;
|
||||
}
|
||||
|
||||
if (loc[4] == 0xff && loc[5] == 0x15) {
|
||||
// Convert
|
||||
// leaq x@tlsld(%rip),%rdi # 48 8d 3d <Loc>
|
||||
// call *__tls_get_addr@GOTPCREL(%rip) # ff 15 <disp32>
|
||||
// to
|
||||
// .long 0x66666666
|
||||
// movq %fs:0,%rax
|
||||
// See "Table 11.9: LD -> LE Code Transition (LP64)" in
|
||||
// https://raw.githubusercontent.com/wiki/hjl-tools/x86-psABI/x86-64-psABI-1.0.pdf
|
||||
loc[-3] = 0x66;
|
||||
memcpy(loc - 2, inst, sizeof(inst));
|
||||
return;
|
||||
}
|
||||
|
||||
error(getErrorLocation(loc - 3) +
|
||||
"expected R_X86_64_PLT32 or R_X86_64_GOTPCRELX after R_X86_64_TLSLD");
|
||||
}
|
||||
|
||||
void X86_64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
switch (type) {
|
||||
case R_X86_64_8:
|
||||
checkIntUInt(loc, val, 8, type);
|
||||
*loc = val;
|
||||
break;
|
||||
case R_X86_64_PC8:
|
||||
checkInt(loc, val, 8, type);
|
||||
*loc = val;
|
||||
break;
|
||||
case R_X86_64_16:
|
||||
checkIntUInt(loc, val, 16, type);
|
||||
write16le(loc, val);
|
||||
break;
|
||||
case R_X86_64_PC16:
|
||||
checkInt(loc, val, 16, type);
|
||||
write16le(loc, val);
|
||||
break;
|
||||
case R_X86_64_32:
|
||||
checkUInt(loc, val, 32, type);
|
||||
write32le(loc, val);
|
||||
break;
|
||||
case R_X86_64_32S:
|
||||
case R_X86_64_TPOFF32:
|
||||
case R_X86_64_GOT32:
|
||||
case R_X86_64_GOTPC32:
|
||||
case R_X86_64_GOTPC32_TLSDESC:
|
||||
case R_X86_64_GOTPCREL:
|
||||
case R_X86_64_GOTPCRELX:
|
||||
case R_X86_64_REX_GOTPCRELX:
|
||||
case R_X86_64_PC32:
|
||||
case R_X86_64_GOTTPOFF:
|
||||
case R_X86_64_PLT32:
|
||||
case R_X86_64_TLSGD:
|
||||
case R_X86_64_TLSLD:
|
||||
case R_X86_64_DTPOFF32:
|
||||
case R_X86_64_SIZE32:
|
||||
checkInt(loc, val, 32, type);
|
||||
write32le(loc, val);
|
||||
break;
|
||||
case R_X86_64_64:
|
||||
case R_X86_64_DTPOFF64:
|
||||
case R_X86_64_PC64:
|
||||
case R_X86_64_SIZE64:
|
||||
case R_X86_64_GOT64:
|
||||
case R_X86_64_GOTOFF64:
|
||||
case R_X86_64_GOTPC64:
|
||||
write64le(loc, val);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("unknown relocation");
|
||||
}
|
||||
}
|
||||
|
||||
RelExpr X86_64::adjustRelaxExpr(RelType type, const uint8_t *data,
|
||||
RelExpr relExpr) const {
|
||||
if (type != R_X86_64_GOTPCRELX && type != R_X86_64_REX_GOTPCRELX)
|
||||
return relExpr;
|
||||
const uint8_t op = data[-2];
|
||||
const uint8_t modRm = data[-1];
|
||||
|
||||
// FIXME: When PIC is disabled and foo is defined locally in the
|
||||
// lower 32 bit address space, memory operand in mov can be converted into
|
||||
// immediate operand. Otherwise, mov must be changed to lea. We support only
|
||||
// latter relaxation at this moment.
|
||||
if (op == 0x8b)
|
||||
return R_RELAX_GOT_PC;
|
||||
|
||||
// Relax call and jmp.
|
||||
if (op == 0xff && (modRm == 0x15 || modRm == 0x25))
|
||||
return R_RELAX_GOT_PC;
|
||||
|
||||
// Relaxation of test, adc, add, and, cmp, or, sbb, sub, xor.
|
||||
// If PIC then no relaxation is available.
|
||||
// We also don't relax test/binop instructions without REX byte,
|
||||
// they are 32bit operations and not common to have.
|
||||
assert(type == R_X86_64_REX_GOTPCRELX);
|
||||
return config->isPic ? relExpr : R_RELAX_GOT_PC_NOPIC;
|
||||
}
|
||||
|
||||
// A subset of relaxations can only be applied for no-PIC. This method
|
||||
// handles such relaxations. Instructions encoding information was taken from:
|
||||
// "Intel 64 and IA-32 Architectures Software Developer's Manual V2"
|
||||
// (http://www.intel.com/content/dam/www/public/us/en/documents/manuals/
|
||||
// 64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf)
|
||||
static void relaxGotNoPic(uint8_t *loc, uint64_t val, uint8_t op,
|
||||
uint8_t modRm) {
|
||||
const uint8_t rex = loc[-3];
|
||||
// Convert "test %reg, foo@GOTPCREL(%rip)" to "test $foo, %reg".
|
||||
if (op == 0x85) {
|
||||
// See "TEST-Logical Compare" (4-428 Vol. 2B),
|
||||
// TEST r/m64, r64 uses "full" ModR / M byte (no opcode extension).
|
||||
|
||||
// ModR/M byte has form XX YYY ZZZ, where
|
||||
// YYY is MODRM.reg(register 2), ZZZ is MODRM.rm(register 1).
|
||||
// XX has different meanings:
|
||||
// 00: The operand's memory address is in reg1.
|
||||
// 01: The operand's memory address is reg1 + a byte-sized displacement.
|
||||
// 10: The operand's memory address is reg1 + a word-sized displacement.
|
||||
// 11: The operand is reg1 itself.
|
||||
// If an instruction requires only one operand, the unused reg2 field
|
||||
// holds extra opcode bits rather than a register code
|
||||
// 0xC0 == 11 000 000 binary.
|
||||
// 0x38 == 00 111 000 binary.
|
||||
// We transfer reg2 to reg1 here as operand.
|
||||
// See "2.1.3 ModR/M and SIB Bytes" (Vol. 2A 2-3).
|
||||
loc[-1] = 0xc0 | (modRm & 0x38) >> 3; // ModR/M byte.
|
||||
|
||||
// Change opcode from TEST r/m64, r64 to TEST r/m64, imm32
|
||||
// See "TEST-Logical Compare" (4-428 Vol. 2B).
|
||||
loc[-2] = 0xf7;
|
||||
|
||||
// Move R bit to the B bit in REX byte.
|
||||
// REX byte is encoded as 0100WRXB, where
|
||||
// 0100 is 4bit fixed pattern.
|
||||
// REX.W When 1, a 64-bit operand size is used. Otherwise, when 0, the
|
||||
// default operand size is used (which is 32-bit for most but not all
|
||||
// instructions).
|
||||
// REX.R This 1-bit value is an extension to the MODRM.reg field.
|
||||
// REX.X This 1-bit value is an extension to the SIB.index field.
|
||||
// REX.B This 1-bit value is an extension to the MODRM.rm field or the
|
||||
// SIB.base field.
|
||||
// See "2.2.1.2 More on REX Prefix Fields " (2-8 Vol. 2A).
|
||||
loc[-3] = (rex & ~0x4) | (rex & 0x4) >> 2;
|
||||
write32le(loc, val);
|
||||
return;
|
||||
}
|
||||
|
||||
// If we are here then we need to relax the adc, add, and, cmp, or, sbb, sub
|
||||
// or xor operations.
|
||||
|
||||
// Convert "binop foo@GOTPCREL(%rip), %reg" to "binop $foo, %reg".
|
||||
// Logic is close to one for test instruction above, but we also
|
||||
// write opcode extension here, see below for details.
|
||||
loc[-1] = 0xc0 | (modRm & 0x38) >> 3 | (op & 0x3c); // ModR/M byte.
|
||||
|
||||
// Primary opcode is 0x81, opcode extension is one of:
|
||||
// 000b = ADD, 001b is OR, 010b is ADC, 011b is SBB,
|
||||
// 100b is AND, 101b is SUB, 110b is XOR, 111b is CMP.
|
||||
// This value was wrote to MODRM.reg in a line above.
|
||||
// See "3.2 INSTRUCTIONS (A-M)" (Vol. 2A 3-15),
|
||||
// "INSTRUCTION SET REFERENCE, N-Z" (Vol. 2B 4-1) for
|
||||
// descriptions about each operation.
|
||||
loc[-2] = 0x81;
|
||||
loc[-3] = (rex & ~0x4) | (rex & 0x4) >> 2;
|
||||
write32le(loc, val);
|
||||
}
|
||||
|
||||
void X86_64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
const uint8_t op = loc[-2];
|
||||
const uint8_t modRm = loc[-1];
|
||||
|
||||
// Convert "mov foo@GOTPCREL(%rip),%reg" to "lea foo(%rip),%reg".
|
||||
if (op == 0x8b) {
|
||||
loc[-2] = 0x8d;
|
||||
write32le(loc, val);
|
||||
return;
|
||||
}
|
||||
|
||||
if (op != 0xff) {
|
||||
// We are relaxing a rip relative to an absolute, so compensate
|
||||
// for the old -4 addend.
|
||||
assert(!config->isPic);
|
||||
relaxGotNoPic(loc, val + 4, op, modRm);
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert call/jmp instructions.
|
||||
if (modRm == 0x15) {
|
||||
// ABI says we can convert "call *foo@GOTPCREL(%rip)" to "nop; call foo".
|
||||
// Instead we convert to "addr32 call foo" where addr32 is an instruction
|
||||
// prefix. That makes result expression to be a single instruction.
|
||||
loc[-2] = 0x67; // addr32 prefix
|
||||
loc[-1] = 0xe8; // call
|
||||
write32le(loc, val);
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert "jmp *foo@GOTPCREL(%rip)" to "jmp foo; nop".
|
||||
// jmp doesn't return, so it is fine to use nop here, it is just a stub.
|
||||
assert(modRm == 0x25);
|
||||
loc[-2] = 0xe9; // jmp
|
||||
loc[3] = 0x90; // nop
|
||||
write32le(loc - 1, val + 1);
|
||||
}
|
||||
|
||||
// A split-stack prologue starts by checking the amount of stack remaining
|
||||
// in one of two ways:
|
||||
// A) Comparing of the stack pointer to a field in the tcb.
|
||||
// B) Or a load of a stack pointer offset with an lea to r10 or r11.
|
||||
bool X86_64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
|
||||
uint8_t stOther) const {
|
||||
if (!config->is64) {
|
||||
error("Target doesn't support split stacks.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (loc + 8 >= end)
|
||||
return false;
|
||||
|
||||
// Replace "cmp %fs:0x70,%rsp" and subsequent branch
|
||||
// with "stc, nopl 0x0(%rax,%rax,1)"
|
||||
if (memcmp(loc, "\x64\x48\x3b\x24\x25", 5) == 0) {
|
||||
memcpy(loc, "\xf9\x0f\x1f\x84\x00\x00\x00\x00", 8);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Adjust "lea X(%rsp),%rYY" to lea "(X - 0x4000)(%rsp),%rYY" where rYY could
|
||||
// be r10 or r11. The lea instruction feeds a subsequent compare which checks
|
||||
// if there is X available stack space. Making X larger effectively reserves
|
||||
// that much additional space. The stack grows downward so subtract the value.
|
||||
if (memcmp(loc, "\x4c\x8d\x94\x24", 4) == 0 ||
|
||||
memcmp(loc, "\x4c\x8d\x9c\x24", 4) == 0) {
|
||||
// The offset bytes are encoded four bytes after the start of the
|
||||
// instruction.
|
||||
write32le(loc + 4, read32le(loc + 4) - 0x4000);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// These nonstandard PLT entries are to migtigate Spectre v2 security
|
||||
// vulnerability. In order to mitigate Spectre v2, we want to avoid indirect
|
||||
// branch instructions such as `jmp *GOTPLT(%rip)`. So, in the following PLT
|
||||
// entries, we use a CALL followed by MOV and RET to do the same thing as an
|
||||
// indirect jump. That instruction sequence is so-called "retpoline".
|
||||
//
|
||||
// We have two types of retpoline PLTs as a size optimization. If `-z now`
|
||||
// is specified, all dynamic symbols are resolved at load-time. Thus, when
|
||||
// that option is given, we can omit code for symbol lazy resolution.
|
||||
namespace {
|
||||
class Retpoline : public X86_64 {
|
||||
public:
|
||||
Retpoline();
|
||||
void writeGotPlt(uint8_t *buf, const Symbol &s) const override;
|
||||
void writePltHeader(uint8_t *buf) const override;
|
||||
void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
|
||||
int32_t index, unsigned relOff) const override;
|
||||
};
|
||||
|
||||
class RetpolineZNow : public X86_64 {
|
||||
public:
|
||||
RetpolineZNow();
|
||||
void writeGotPlt(uint8_t *buf, const Symbol &s) const override {}
|
||||
void writePltHeader(uint8_t *buf) const override;
|
||||
void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
|
||||
int32_t index, unsigned relOff) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
Retpoline::Retpoline() {
|
||||
pltHeaderSize = 48;
|
||||
pltEntrySize = 32;
|
||||
}
|
||||
|
||||
void Retpoline::writeGotPlt(uint8_t *buf, const Symbol &s) const {
|
||||
write64le(buf, s.getPltVA() + 17);
|
||||
}
|
||||
|
||||
void Retpoline::writePltHeader(uint8_t *buf) const {
|
||||
const uint8_t insn[] = {
|
||||
0xff, 0x35, 0, 0, 0, 0, // 0: pushq GOTPLT+8(%rip)
|
||||
0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // 6: mov GOTPLT+16(%rip), %r11
|
||||
0xe8, 0x0e, 0x00, 0x00, 0x00, // d: callq next
|
||||
0xf3, 0x90, // 12: loop: pause
|
||||
0x0f, 0xae, 0xe8, // 14: lfence
|
||||
0xeb, 0xf9, // 17: jmp loop
|
||||
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 19: int3; .align 16
|
||||
0x4c, 0x89, 0x1c, 0x24, // 20: next: mov %r11, (%rsp)
|
||||
0xc3, // 24: ret
|
||||
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 25: int3; padding
|
||||
0xcc, 0xcc, 0xcc, 0xcc, // 2c: int3; padding
|
||||
};
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
|
||||
uint64_t gotPlt = in.gotPlt->getVA();
|
||||
uint64_t plt = in.plt->getVA();
|
||||
write32le(buf + 2, gotPlt - plt - 6 + 8);
|
||||
write32le(buf + 9, gotPlt - plt - 13 + 16);
|
||||
}
|
||||
|
||||
void Retpoline::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
|
||||
uint64_t pltEntryAddr, int32_t index,
|
||||
unsigned relOff) const {
|
||||
const uint8_t insn[] = {
|
||||
0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // 0: mov foo@GOTPLT(%rip), %r11
|
||||
0xe8, 0, 0, 0, 0, // 7: callq plt+0x20
|
||||
0xe9, 0, 0, 0, 0, // c: jmp plt+0x12
|
||||
0x68, 0, 0, 0, 0, // 11: pushq <relocation index>
|
||||
0xe9, 0, 0, 0, 0, // 16: jmp plt+0
|
||||
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1b: int3; padding
|
||||
};
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
|
||||
uint64_t off = pltHeaderSize + pltEntrySize * index;
|
||||
|
||||
write32le(buf + 3, gotPltEntryAddr - pltEntryAddr - 7);
|
||||
write32le(buf + 8, -off - 12 + 32);
|
||||
write32le(buf + 13, -off - 17 + 18);
|
||||
write32le(buf + 18, index);
|
||||
write32le(buf + 23, -off - 27);
|
||||
}
|
||||
|
||||
RetpolineZNow::RetpolineZNow() {
|
||||
pltHeaderSize = 32;
|
||||
pltEntrySize = 16;
|
||||
}
|
||||
|
||||
void RetpolineZNow::writePltHeader(uint8_t *buf) const {
|
||||
const uint8_t insn[] = {
|
||||
0xe8, 0x0b, 0x00, 0x00, 0x00, // 0: call next
|
||||
0xf3, 0x90, // 5: loop: pause
|
||||
0x0f, 0xae, 0xe8, // 7: lfence
|
||||
0xeb, 0xf9, // a: jmp loop
|
||||
0xcc, 0xcc, 0xcc, 0xcc, // c: int3; .align 16
|
||||
0x4c, 0x89, 0x1c, 0x24, // 10: next: mov %r11, (%rsp)
|
||||
0xc3, // 14: ret
|
||||
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 15: int3; padding
|
||||
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding
|
||||
0xcc, // 1f: int3; padding
|
||||
};
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
}
|
||||
|
||||
void RetpolineZNow::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
|
||||
uint64_t pltEntryAddr, int32_t index,
|
||||
unsigned relOff) const {
|
||||
const uint8_t insn[] = {
|
||||
0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // mov foo@GOTPLT(%rip), %r11
|
||||
0xe9, 0, 0, 0, 0, // jmp plt+0
|
||||
0xcc, 0xcc, 0xcc, 0xcc, // int3; padding
|
||||
};
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
|
||||
write32le(buf + 3, gotPltEntryAddr - pltEntryAddr - 7);
|
||||
write32le(buf + 8, -pltHeaderSize - pltEntrySize * index - 12);
|
||||
}
|
||||
|
||||
static TargetInfo *getTargetInfo() {
|
||||
if (config->zRetpolineplt) {
|
||||
if (config->zNow) {
|
||||
static RetpolineZNow t;
|
||||
return &t;
|
||||
}
|
||||
static Retpoline t;
|
||||
return &t;
|
||||
}
|
||||
|
||||
static X86_64 t;
|
||||
return &t;
|
||||
}
|
||||
|
||||
TargetInfo *elf::getX86_64TargetInfo() { return getTargetInfo(); }
|
||||
67
deps/lld/ELF/CMakeLists.txt
vendored
67
deps/lld/ELF/CMakeLists.txt
vendored
@ -1,67 +0,0 @@
|
||||
set(LLVM_TARGET_DEFINITIONS Options.td)
|
||||
tablegen(LLVM Options.inc -gen-opt-parser-defs)
|
||||
add_public_tablegen_target(ELFOptionsTableGen)
|
||||
|
||||
if(NOT LLD_BUILT_STANDALONE)
|
||||
set(tablegen_deps intrinsics_gen)
|
||||
endif()
|
||||
|
||||
add_lld_library(lldELF
|
||||
AArch64ErrataFix.cpp
|
||||
Arch/AArch64.cpp
|
||||
Arch/AMDGPU.cpp
|
||||
Arch/ARM.cpp
|
||||
Arch/AVR.cpp
|
||||
Arch/Hexagon.cpp
|
||||
Arch/Mips.cpp
|
||||
Arch/MipsArchTree.cpp
|
||||
Arch/MSP430.cpp
|
||||
Arch/PPC.cpp
|
||||
Arch/PPC64.cpp
|
||||
Arch/RISCV.cpp
|
||||
Arch/SPARCV9.cpp
|
||||
Arch/X86.cpp
|
||||
Arch/X86_64.cpp
|
||||
CallGraphSort.cpp
|
||||
DWARF.cpp
|
||||
Driver.cpp
|
||||
DriverUtils.cpp
|
||||
EhFrame.cpp
|
||||
ICF.cpp
|
||||
InputFiles.cpp
|
||||
InputSection.cpp
|
||||
LTO.cpp
|
||||
LinkerScript.cpp
|
||||
MapFile.cpp
|
||||
MarkLive.cpp
|
||||
OutputSections.cpp
|
||||
Relocations.cpp
|
||||
ScriptLexer.cpp
|
||||
ScriptParser.cpp
|
||||
SymbolTable.cpp
|
||||
Symbols.cpp
|
||||
SyntheticSections.cpp
|
||||
Target.cpp
|
||||
Thunks.cpp
|
||||
Writer.cpp
|
||||
|
||||
LINK_COMPONENTS
|
||||
${LLVM_TARGETS_TO_BUILD}
|
||||
BinaryFormat
|
||||
BitWriter
|
||||
Core
|
||||
DebugInfoDWARF
|
||||
LTO
|
||||
MC
|
||||
Object
|
||||
Option
|
||||
Support
|
||||
|
||||
LINK_LIBS
|
||||
lldCommon
|
||||
${LLVM_PTHREAD_LIB}
|
||||
|
||||
DEPENDS
|
||||
ELFOptionsTableGen
|
||||
${tablegen_deps}
|
||||
)
|
||||
259
deps/lld/ELF/CallGraphSort.cpp
vendored
259
deps/lld/ELF/CallGraphSort.cpp
vendored
@ -1,259 +0,0 @@
|
||||
//===- CallGraphSort.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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// Implementation of Call-Chain Clustering from: Optimizing Function Placement
|
||||
/// for Large-Scale Data-Center Applications
|
||||
/// https://research.fb.com/wp-content/uploads/2017/01/cgo2017-hfsort-final1.pdf
|
||||
///
|
||||
/// The goal of this algorithm is to improve runtime performance of the final
|
||||
/// executable by arranging code sections such that page table and i-cache
|
||||
/// misses are minimized.
|
||||
///
|
||||
/// Definitions:
|
||||
/// * Cluster
|
||||
/// * An ordered list of input sections which are layed out as a unit. At the
|
||||
/// beginning of the algorithm each input section has its own cluster and
|
||||
/// the weight of the cluster is the sum of the weight of all incomming
|
||||
/// edges.
|
||||
/// * Call-Chain Clustering (C³) Heuristic
|
||||
/// * Defines when and how clusters are combined. Pick the highest weighted
|
||||
/// input section then add it to its most likely predecessor if it wouldn't
|
||||
/// penalize it too much.
|
||||
/// * Density
|
||||
/// * The weight of the cluster divided by the size of the cluster. This is a
|
||||
/// proxy for the ammount of execution time spent per byte of the cluster.
|
||||
///
|
||||
/// It does so given a call graph profile by the following:
|
||||
/// * Build a weighted call graph from the call graph profile
|
||||
/// * Sort input sections by weight
|
||||
/// * For each input section starting with the highest weight
|
||||
/// * Find its most likely predecessor cluster
|
||||
/// * Check if the combined cluster would be too large, or would have too low
|
||||
/// a density.
|
||||
/// * If not, then combine the clusters.
|
||||
/// * Sort non-empty clusters by density
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "CallGraphSort.h"
|
||||
#include "OutputSections.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace {
|
||||
struct Edge {
|
||||
int from;
|
||||
uint64_t weight;
|
||||
};
|
||||
|
||||
struct Cluster {
|
||||
Cluster(int sec, size_t s) : sections{sec}, size(s) {}
|
||||
|
||||
double getDensity() const {
|
||||
if (size == 0)
|
||||
return 0;
|
||||
return double(weight) / double(size);
|
||||
}
|
||||
|
||||
std::vector<int> sections;
|
||||
size_t size = 0;
|
||||
uint64_t weight = 0;
|
||||
uint64_t initialWeight = 0;
|
||||
Edge bestPred = {-1, 0};
|
||||
};
|
||||
|
||||
class CallGraphSort {
|
||||
public:
|
||||
CallGraphSort();
|
||||
|
||||
DenseMap<const InputSectionBase *, int> run();
|
||||
|
||||
private:
|
||||
std::vector<Cluster> clusters;
|
||||
std::vector<const InputSectionBase *> sections;
|
||||
|
||||
void groupClusters();
|
||||
};
|
||||
|
||||
// Maximum ammount the combined cluster density can be worse than the original
|
||||
// cluster to consider merging.
|
||||
constexpr int MAX_DENSITY_DEGRADATION = 8;
|
||||
|
||||
// Maximum cluster size in bytes.
|
||||
constexpr uint64_t MAX_CLUSTER_SIZE = 1024 * 1024;
|
||||
} // end anonymous namespace
|
||||
|
||||
using SectionPair =
|
||||
std::pair<const InputSectionBase *, const InputSectionBase *>;
|
||||
|
||||
// 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;
|
||||
DenseMap<const InputSectionBase *, int> secToCluster;
|
||||
|
||||
auto getOrCreateNode = [&](const InputSectionBase *isec) -> int {
|
||||
auto res = secToCluster.insert(std::make_pair(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 (std::pair<SectionPair, uint64_t> &c : profile) {
|
||||
const auto *fromSB = cast<InputSectionBase>(c.first.first->repl);
|
||||
const auto *toSB = cast<InputSectionBase>(c.first.second->repl);
|
||||
uint64_t weight = c.second;
|
||||
|
||||
// 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 (fromSB->getOutputSection() != toSB->getOutputSection())
|
||||
continue;
|
||||
|
||||
int from = getOrCreateNode(fromSB);
|
||||
int to = getOrCreateNode(toSB);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void mergeClusters(Cluster &into, Cluster &from) {
|
||||
into.sections.insert(into.sections.end(), from.sections.begin(),
|
||||
from.sections.end());
|
||||
into.size += from.size;
|
||||
into.weight += from.weight;
|
||||
from.sections.clear();
|
||||
from.size = 0;
|
||||
from.weight = 0;
|
||||
}
|
||||
|
||||
// Group InputSections into clusters using the Call-Chain Clustering heuristic
|
||||
// then sort the clusters by density.
|
||||
void CallGraphSort::groupClusters() {
|
||||
std::vector<int> sortedSecs(clusters.size());
|
||||
std::vector<Cluster *> secToCluster(clusters.size());
|
||||
|
||||
for (size_t i = 0; i < clusters.size(); ++i) {
|
||||
sortedSecs[i] = i;
|
||||
secToCluster[i] = &clusters[i];
|
||||
}
|
||||
|
||||
llvm::stable_sort(sortedSecs, [&](int a, int b) {
|
||||
return clusters[a].getDensity() > clusters[b].getDensity();
|
||||
});
|
||||
|
||||
for (int si : sortedSecs) {
|
||||
// clusters[si] is the same as secToClusters[si] here because it has not
|
||||
// been merged into another cluster yet.
|
||||
Cluster &c = clusters[si];
|
||||
|
||||
// Don't consider merging if the edge is unlikely.
|
||||
if (c.bestPred.from == -1 || c.bestPred.weight * 10 <= c.initialWeight)
|
||||
continue;
|
||||
|
||||
Cluster *predC = secToCluster[c.bestPred.from];
|
||||
if (predC == &c)
|
||||
continue;
|
||||
|
||||
if (c.size + predC->size > MAX_CLUSTER_SIZE)
|
||||
continue;
|
||||
|
||||
if (isNewDensityBad(*predC, c))
|
||||
continue;
|
||||
|
||||
// NOTE: Consider using a disjoint-set to track section -> cluster mapping
|
||||
// if this is ever slow.
|
||||
for (int si : c.sections)
|
||||
secToCluster[si] = predC;
|
||||
|
||||
mergeClusters(*predC, c);
|
||||
}
|
||||
|
||||
// Remove empty or dead nodes. Invalidates all cluster indices.
|
||||
llvm::erase_if(clusters, [](const Cluster &c) {
|
||||
return c.size == 0 || c.sections.empty();
|
||||
});
|
||||
|
||||
// Sort by density.
|
||||
llvm::stable_sort(clusters, [](const Cluster &a, const Cluster &b) {
|
||||
return a.getDensity() > b.getDensity();
|
||||
});
|
||||
}
|
||||
|
||||
DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
|
||||
groupClusters();
|
||||
|
||||
// Generate order.
|
||||
DenseMap<const InputSectionBase *, int> orderMap;
|
||||
ssize_t curOrder = 1;
|
||||
|
||||
for (const Cluster &c : clusters)
|
||||
for (int secIndex : c.sections)
|
||||
orderMap[sections[secIndex]] = curOrder++;
|
||||
|
||||
if (!config->printSymbolOrder.empty()) {
|
||||
std::error_code ec;
|
||||
raw_fd_ostream os(config->printSymbolOrder, ec, sys::fs::F_None);
|
||||
if (ec) {
|
||||
error("cannot open " + config->printSymbolOrder + ": " + ec.message());
|
||||
return orderMap;
|
||||
}
|
||||
|
||||
// Print the symbols ordered by C3, in the order of increasing curOrder
|
||||
// Instead of sorting all the orderMap, just repeat the loops above.
|
||||
for (const Cluster &c : clusters)
|
||||
for (int secIndex : c.sections)
|
||||
// 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: sections[secIndex]->file->getSymbols())
|
||||
if (!sym->isSection()) // Filter out section-type symbols here.
|
||||
if (auto *d = dyn_cast<Defined>(sym))
|
||||
if (sections[secIndex] == d->section)
|
||||
os << sym->getName() << "\n";
|
||||
}
|
||||
|
||||
return orderMap;
|
||||
}
|
||||
|
||||
// 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³ huristic. All clusters are then sorted by a density
|
||||
// metric to further improve locality.
|
||||
DenseMap<const InputSectionBase *, int> elf::computeCallGraphProfileOrder() {
|
||||
return CallGraphSort().run();
|
||||
}
|
||||
22
deps/lld/ELF/CallGraphSort.h
vendored
22
deps/lld/ELF/CallGraphSort.h
vendored
@ -1,22 +0,0 @@
|
||||
//===- CallGraphSort.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_ELF_CALL_GRAPH_SORT_H
|
||||
#define LLD_ELF_CALL_GRAPH_SORT_H
|
||||
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
class InputSectionBase;
|
||||
|
||||
llvm::DenseMap<const InputSectionBase *, int> computeCallGraphProfileOrder();
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
321
deps/lld/ELF/Config.h
vendored
321
deps/lld/ELF/Config.h
vendored
@ -1,321 +0,0 @@
|
||||
//===- Config.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_ELF_CONFIG_H
|
||||
#define LLD_ELF_CONFIG_H
|
||||
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/ADT/MapVector.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/StringSet.h"
|
||||
#include "llvm/BinaryFormat/ELF.h"
|
||||
#include "llvm/Support/CachePruning.h"
|
||||
#include "llvm/Support/CodeGen.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
class InputFile;
|
||||
class InputSectionBase;
|
||||
|
||||
enum ELFKind {
|
||||
ELFNoneKind,
|
||||
ELF32LEKind,
|
||||
ELF32BEKind,
|
||||
ELF64LEKind,
|
||||
ELF64BEKind
|
||||
};
|
||||
|
||||
// For --build-id.
|
||||
enum class BuildIdKind { None, Fast, Md5, Sha1, Hexstring, Uuid };
|
||||
|
||||
// For --discard-{all,locals,none}.
|
||||
enum class DiscardPolicy { Default, All, Locals, None };
|
||||
|
||||
// For --icf={none,safe,all}.
|
||||
enum class ICFLevel { None, Safe, All };
|
||||
|
||||
// For --strip-{all,debug}.
|
||||
enum class StripPolicy { None, All, Debug };
|
||||
|
||||
// For --unresolved-symbols.
|
||||
enum class UnresolvedPolicy { ReportError, Warn, Ignore };
|
||||
|
||||
// For --orphan-handling.
|
||||
enum class OrphanHandlingPolicy { Place, Warn, Error };
|
||||
|
||||
// For --sort-section and linkerscript sorting rules.
|
||||
enum class SortSectionPolicy { Default, None, Alignment, Name, Priority };
|
||||
|
||||
// For --target2
|
||||
enum class Target2Policy { Abs, Rel, GotRel };
|
||||
|
||||
// For tracking ARM Float Argument PCS
|
||||
enum class ARMVFPArgKind { Default, Base, VFP, ToolChain };
|
||||
|
||||
struct SymbolVersion {
|
||||
llvm::StringRef name;
|
||||
bool isExternCpp;
|
||||
bool hasWildcard;
|
||||
};
|
||||
|
||||
// This struct contains symbols version definition that
|
||||
// can be found in version script if it is used for link.
|
||||
struct VersionDefinition {
|
||||
llvm::StringRef name;
|
||||
uint16_t id = 0;
|
||||
std::vector<SymbolVersion> globals;
|
||||
};
|
||||
|
||||
// This struct contains the global configuration for the linker.
|
||||
// Most fields are direct mapping from the command line options
|
||||
// and such fields have the same name as the corresponding options.
|
||||
// Most fields are initialized by the driver.
|
||||
struct Configuration {
|
||||
uint8_t osabi = 0;
|
||||
uint32_t andFeatures = 0;
|
||||
llvm::CachePruningPolicy thinLTOCachePolicy;
|
||||
llvm::StringMap<uint64_t> sectionStartMap;
|
||||
llvm::StringRef chroot;
|
||||
llvm::StringRef dynamicLinker;
|
||||
llvm::StringRef dwoDir;
|
||||
llvm::StringRef entry;
|
||||
llvm::StringRef emulation;
|
||||
llvm::StringRef fini;
|
||||
llvm::StringRef init;
|
||||
llvm::StringRef ltoAAPipeline;
|
||||
llvm::StringRef ltoCSProfileFile;
|
||||
llvm::StringRef ltoNewPmPasses;
|
||||
llvm::StringRef ltoObjPath;
|
||||
llvm::StringRef ltoSampleProfile;
|
||||
llvm::StringRef mapFile;
|
||||
llvm::StringRef outputFile;
|
||||
llvm::StringRef optRemarksFilename;
|
||||
llvm::StringRef optRemarksPasses;
|
||||
llvm::StringRef optRemarksFormat;
|
||||
llvm::StringRef progName;
|
||||
llvm::StringRef printSymbolOrder;
|
||||
llvm::StringRef soName;
|
||||
llvm::StringRef sysroot;
|
||||
llvm::StringRef thinLTOCacheDir;
|
||||
llvm::StringRef thinLTOIndexOnlyArg;
|
||||
std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
|
||||
std::pair<llvm::StringRef, llvm::StringRef> thinLTOPrefixReplace;
|
||||
std::string rpath;
|
||||
std::vector<VersionDefinition> versionDefinitions;
|
||||
std::vector<llvm::StringRef> auxiliaryList;
|
||||
std::vector<llvm::StringRef> filterList;
|
||||
std::vector<llvm::StringRef> searchPaths;
|
||||
std::vector<llvm::StringRef> symbolOrderingFile;
|
||||
std::vector<llvm::StringRef> undefined;
|
||||
std::vector<SymbolVersion> dynamicList;
|
||||
std::vector<SymbolVersion> versionScriptGlobals;
|
||||
std::vector<SymbolVersion> versionScriptLocals;
|
||||
std::vector<uint8_t> buildIdVector;
|
||||
llvm::MapVector<std::pair<const InputSectionBase *, const InputSectionBase *>,
|
||||
uint64_t>
|
||||
callGraphProfile;
|
||||
bool allowMultipleDefinition;
|
||||
bool allowShlibUndefined;
|
||||
bool androidPackDynRelocs;
|
||||
bool armHasBlx = false;
|
||||
bool armHasMovtMovw = false;
|
||||
bool armJ1J2BranchEncoding = false;
|
||||
bool asNeeded = false;
|
||||
bool bsymbolic;
|
||||
bool bsymbolicFunctions;
|
||||
bool callGraphProfileSort;
|
||||
bool checkSections;
|
||||
bool compressDebugSections;
|
||||
bool cref;
|
||||
bool defineCommon;
|
||||
bool demangle = true;
|
||||
bool dependentLibraries;
|
||||
bool disableVerify;
|
||||
bool ehFrameHdr;
|
||||
bool emitLLVM;
|
||||
bool emitRelocs;
|
||||
bool enableNewDtags;
|
||||
bool executeOnly;
|
||||
bool exportDynamic;
|
||||
bool fixCortexA53Errata843419;
|
||||
bool forceBTI;
|
||||
bool formatBinary = false;
|
||||
bool requireCET;
|
||||
bool gcSections;
|
||||
bool gdbIndex;
|
||||
bool gnuHash = false;
|
||||
bool gnuUnique;
|
||||
bool hasDynamicList = false;
|
||||
bool hasDynSymTab;
|
||||
bool ignoreDataAddressEquality;
|
||||
bool ignoreFunctionAddressEquality;
|
||||
bool ltoCSProfileGenerate;
|
||||
bool ltoDebugPassManager;
|
||||
bool ltoNewPassManager;
|
||||
bool mergeArmExidx;
|
||||
bool mipsN32Abi = false;
|
||||
bool nmagic;
|
||||
bool noinhibitExec;
|
||||
bool nostdlib;
|
||||
bool oFormatBinary;
|
||||
bool omagic;
|
||||
bool optRemarksWithHotness;
|
||||
bool pacPlt;
|
||||
bool picThunk;
|
||||
bool pie;
|
||||
bool printGcSections;
|
||||
bool printIcfSections;
|
||||
bool relocatable;
|
||||
bool relrPackDynRelocs;
|
||||
bool saveTemps;
|
||||
bool singleRoRx;
|
||||
bool shared;
|
||||
bool isStatic = false;
|
||||
bool sysvHash = false;
|
||||
bool target1Rel;
|
||||
bool trace;
|
||||
bool thinLTOEmitImportsFiles;
|
||||
bool thinLTOIndexOnly;
|
||||
bool tocOptimize;
|
||||
bool undefinedVersion;
|
||||
bool useAndroidRelrTags = false;
|
||||
bool warnBackrefs;
|
||||
bool warnCommon;
|
||||
bool warnIfuncTextrel;
|
||||
bool warnMissingEntry;
|
||||
bool warnSymbolOrdering;
|
||||
bool writeAddends;
|
||||
bool zCombreloc;
|
||||
bool zCopyreloc;
|
||||
bool zExecstack;
|
||||
bool zGlobal;
|
||||
bool zHazardplt;
|
||||
bool zIfuncNoplt;
|
||||
bool zInitfirst;
|
||||
bool zInterpose;
|
||||
bool zKeepTextSectionPrefix;
|
||||
bool zNodefaultlib;
|
||||
bool zNodelete;
|
||||
bool zNodlopen;
|
||||
bool zNow;
|
||||
bool zOrigin;
|
||||
bool zRelro;
|
||||
bool zRodynamic;
|
||||
bool zText;
|
||||
bool zRetpolineplt;
|
||||
bool zWxneeded;
|
||||
DiscardPolicy discard;
|
||||
ICFLevel icf;
|
||||
OrphanHandlingPolicy orphanHandling;
|
||||
SortSectionPolicy sortSection;
|
||||
StripPolicy strip;
|
||||
UnresolvedPolicy unresolvedSymbols;
|
||||
Target2Policy target2;
|
||||
ARMVFPArgKind armVFPArgs = ARMVFPArgKind::Default;
|
||||
BuildIdKind buildId = BuildIdKind::None;
|
||||
ELFKind ekind = ELFNoneKind;
|
||||
uint16_t defaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL;
|
||||
uint16_t emachine = llvm::ELF::EM_NONE;
|
||||
llvm::Optional<uint64_t> imageBase;
|
||||
uint64_t commonPageSize;
|
||||
uint64_t maxPageSize;
|
||||
uint64_t mipsGotSize;
|
||||
uint64_t zStackSize;
|
||||
unsigned ltoPartitions;
|
||||
unsigned ltoo;
|
||||
unsigned optimize;
|
||||
unsigned thinLTOJobs;
|
||||
int32_t splitStackAdjustSize;
|
||||
|
||||
// The following config options do not directly correspond to any
|
||||
// particualr command line options.
|
||||
|
||||
// True if we need to pass through relocations in input files to the
|
||||
// output file. Usually false because we consume relocations.
|
||||
bool copyRelocs;
|
||||
|
||||
// True if the target is ELF64. False if ELF32.
|
||||
bool is64;
|
||||
|
||||
// True if the target is little-endian. False if big-endian.
|
||||
bool isLE;
|
||||
|
||||
// endianness::little if isLE is true. endianness::big otherwise.
|
||||
llvm::support::endianness endianness;
|
||||
|
||||
// True if the target is the little-endian MIPS64.
|
||||
//
|
||||
// The reason why we have this variable only for the MIPS is because
|
||||
// we use this often. Some ELF headers for MIPS64EL are in a
|
||||
// mixed-endian (which is horrible and I'd say that's a serious spec
|
||||
// bug), and we need to know whether we are reading MIPS ELF files or
|
||||
// not in various places.
|
||||
//
|
||||
// (Note that MIPS64EL is not a typo for MIPS64LE. This is the official
|
||||
// name whatever that means. A fun hypothesis is that "EL" is short for
|
||||
// little-endian written in the little-endian order, but I don't know
|
||||
// if that's true.)
|
||||
bool isMips64EL;
|
||||
|
||||
// True if we need to set the DF_STATIC_TLS flag to an output file,
|
||||
// which works as a hint to the dynamic loader that the file contains
|
||||
// code compiled with the static TLS model. The thread-local variable
|
||||
// compiled with the static TLS model is faster but less flexible, and
|
||||
// it may not be loaded using dlopen().
|
||||
//
|
||||
// We set this flag to true when we see a relocation for the static TLS
|
||||
// model. Once this becomes true, it will never become false.
|
||||
//
|
||||
// Since the flag is updated by multi-threaded code, we use std::atomic.
|
||||
// (Writing to a variable is not considered thread-safe even if the
|
||||
// variable is boolean and we always set the same value from all threads.)
|
||||
std::atomic<bool> hasStaticTlsModel{false};
|
||||
|
||||
// Holds set of ELF header flags for the target.
|
||||
uint32_t eflags = 0;
|
||||
|
||||
// The ELF spec defines two types of relocation table entries, RELA and
|
||||
// REL. RELA is a triplet of (offset, info, addend) while REL is a
|
||||
// tuple of (offset, info). Addends for REL are implicit and read from
|
||||
// the location where the relocations are applied. So, REL is more
|
||||
// compact than RELA but requires a bit of more work to process.
|
||||
//
|
||||
// (From the linker writer's view, this distinction is not necessary.
|
||||
// If the ELF had chosen whichever and sticked with it, it would have
|
||||
// been easier to write code to process relocations, but it's too late
|
||||
// to change the spec.)
|
||||
//
|
||||
// Each ABI defines its relocation type. IsRela is true if target
|
||||
// uses RELA. As far as we know, all 64-bit ABIs are using RELA. A
|
||||
// few 32-bit ABIs are using RELA too.
|
||||
bool isRela;
|
||||
|
||||
// True if we are creating position-independent code.
|
||||
bool isPic;
|
||||
|
||||
// 4 for ELF32, 8 for ELF64.
|
||||
int wordsize;
|
||||
};
|
||||
|
||||
// The only instance of Configuration struct.
|
||||
extern Configuration *config;
|
||||
|
||||
static inline void errorOrWarn(const Twine &msg) {
|
||||
if (!config->noinhibitExec)
|
||||
error(msg);
|
||||
else
|
||||
warn(msg);
|
||||
}
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
129
deps/lld/ELF/DWARF.cpp
vendored
129
deps/lld/ELF/DWARF.cpp
vendored
@ -1,129 +0,0 @@
|
||||
//===- DWARF.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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "DWARF.h"
|
||||
#include "Symbols.h"
|
||||
#include "Target.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h"
|
||||
#include "llvm/Object/ELFObjectFile.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *obj) {
|
||||
for (InputSectionBase *sec : obj->getSections()) {
|
||||
if (!sec)
|
||||
continue;
|
||||
|
||||
if (LLDDWARFSection *m =
|
||||
StringSwitch<LLDDWARFSection *>(sec->name)
|
||||
.Case(".debug_addr", &addrSection)
|
||||
.Case(".debug_gnu_pubnames", &gnuPubNamesSection)
|
||||
.Case(".debug_gnu_pubtypes", &gnuPubTypesSection)
|
||||
.Case(".debug_info", &infoSection)
|
||||
.Case(".debug_ranges", &rangeSection)
|
||||
.Case(".debug_rnglists", &rngListsSection)
|
||||
.Case(".debug_line", &lineSection)
|
||||
.Default(nullptr)) {
|
||||
m->Data = toStringRef(sec->data());
|
||||
m->sec = sec;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sec->name == ".debug_abbrev")
|
||||
abbrevSection = toStringRef(sec->data());
|
||||
else if (sec->name == ".debug_str")
|
||||
strSection = toStringRef(sec->data());
|
||||
else if (sec->name == ".debug_line_str")
|
||||
lineStringSection = toStringRef(sec->data());
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
template <class RelTy> struct LLDRelocationResolver {
|
||||
// In the ELF ABIs, S sepresents the value of the symbol in the relocation
|
||||
// entry. For Rela, the addend is stored as part of the relocation entry.
|
||||
static uint64_t resolve(object::RelocationRef ref, uint64_t s,
|
||||
uint64_t /* A */) {
|
||||
return s + ref.getRawDataRefImpl().p;
|
||||
}
|
||||
};
|
||||
|
||||
template <class ELFT> struct LLDRelocationResolver<Elf_Rel_Impl<ELFT, false>> {
|
||||
// For Rel, the addend A is supplied by the caller.
|
||||
static uint64_t resolve(object::RelocationRef /*Ref*/, uint64_t s,
|
||||
uint64_t a) {
|
||||
return s + a;
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// Find if there is a relocation at Pos in Sec. The code is a bit
|
||||
// more complicated than usual because we need to pass a section index
|
||||
// to llvm since it has no idea about InputSection.
|
||||
template <class ELFT>
|
||||
template <class RelTy>
|
||||
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;
|
||||
const RelTy &rel = *it;
|
||||
|
||||
const ObjFile<ELFT> *file = sec.getFile<ELFT>();
|
||||
uint32_t symIndex = rel.getSymbol(config->isMips64EL);
|
||||
const typename ELFT::Sym &sym = file->template getELFSyms<ELFT>()[symIndex];
|
||||
uint32_t secIndex = file->getSectionIndex(sym);
|
||||
|
||||
// An undefined symbol may be a symbol defined in a discarded section. We
|
||||
// shall still resolve it. This is important for --gdb-index: the end address
|
||||
// offset of an entry in .debug_ranges is relocated. If it is not resolved,
|
||||
// its zero value will terminate the decoding of .debug_ranges prematurely.
|
||||
Symbol &s = file->getRelocTargetSym(rel);
|
||||
uint64_t val = 0;
|
||||
if (auto *dr = dyn_cast<Defined>(&s)) {
|
||||
val = dr->value;
|
||||
|
||||
// FIXME: We should be consistent about always adding the file
|
||||
// offset or not.
|
||||
if (dr->section->flags & ELF::SHF_ALLOC)
|
||||
val += cast<InputSection>(dr->section)->getOffsetInFile();
|
||||
}
|
||||
|
||||
DataRefImpl d;
|
||||
d.p = getAddend<ELFT>(rel);
|
||||
return RelocAddrEntry{secIndex, RelocationRef(d, nullptr),
|
||||
val, Optional<object::RelocationRef>(),
|
||||
0, LLDRelocationResolver<RelTy>::resolve};
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
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>());
|
||||
}
|
||||
|
||||
template class elf::LLDDwarfObj<ELF32LE>;
|
||||
template class elf::LLDDwarfObj<ELF32BE>;
|
||||
template class elf::LLDDwarfObj<ELF64LE>;
|
||||
template class elf::LLDDwarfObj<ELF64BE>;
|
||||
92
deps/lld/ELF/DWARF.h
vendored
92
deps/lld/ELF/DWARF.h
vendored
@ -1,92 +0,0 @@
|
||||
//===- DWARF.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_ELF_DWARF_H
|
||||
#define LLD_ELF_DWARF_H
|
||||
|
||||
#include "InputFiles.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
class InputSection;
|
||||
|
||||
struct LLDDWARFSection final : public llvm::DWARFSection {
|
||||
InputSectionBase *sec = nullptr;
|
||||
};
|
||||
|
||||
template <class ELFT> class LLDDwarfObj final : public llvm::DWARFObject {
|
||||
public:
|
||||
explicit LLDDwarfObj(ObjFile<ELFT> *obj);
|
||||
|
||||
void forEachInfoSections(
|
||||
llvm::function_ref<void(const llvm::DWARFSection &)> f) const override {
|
||||
f(infoSection);
|
||||
}
|
||||
|
||||
const llvm::DWARFSection &getRangeSection() const override {
|
||||
return rangeSection;
|
||||
}
|
||||
|
||||
const llvm::DWARFSection &getRnglistsSection() const override {
|
||||
return rngListsSection;
|
||||
}
|
||||
|
||||
const llvm::DWARFSection &getLineSection() const override {
|
||||
return lineSection;
|
||||
}
|
||||
|
||||
const llvm::DWARFSection &getAddrSection() const override {
|
||||
return addrSection;
|
||||
}
|
||||
|
||||
const llvm::DWARFSection &getGnuPubNamesSection() const override {
|
||||
return gnuPubNamesSection;
|
||||
}
|
||||
|
||||
const llvm::DWARFSection &getGnuPubTypesSection() const override {
|
||||
return gnuPubTypesSection;
|
||||
}
|
||||
|
||||
StringRef getFileName() const override { return ""; }
|
||||
StringRef getAbbrevSection() const override { return abbrevSection; }
|
||||
StringRef getStringSection() const override { return strSection; }
|
||||
StringRef getLineStringSection() const override { return lineStringSection; }
|
||||
|
||||
bool isLittleEndian() const override {
|
||||
return ELFT::TargetEndianness == llvm::support::little;
|
||||
}
|
||||
|
||||
llvm::Optional<llvm::RelocAddrEntry> find(const llvm::DWARFSection &sec,
|
||||
uint64_t pos) const override;
|
||||
|
||||
private:
|
||||
template <class RelTy>
|
||||
llvm::Optional<llvm::RelocAddrEntry> findAux(const InputSectionBase &sec,
|
||||
uint64_t pos,
|
||||
ArrayRef<RelTy> rels) const;
|
||||
|
||||
LLDDWARFSection gnuPubNamesSection;
|
||||
LLDDWARFSection gnuPubTypesSection;
|
||||
LLDDWARFSection infoSection;
|
||||
LLDDWARFSection rangeSection;
|
||||
LLDDWARFSection rngListsSection;
|
||||
LLDDWARFSection lineSection;
|
||||
LLDDWARFSection addrSection;
|
||||
StringRef abbrevSection;
|
||||
StringRef strSection;
|
||||
StringRef lineStringSection;
|
||||
};
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
1911
deps/lld/ELF/Driver.cpp
vendored
1911
deps/lld/ELF/Driver.cpp
vendored
File diff suppressed because it is too large
Load Diff
77
deps/lld/ELF/Driver.h
vendored
77
deps/lld/ELF/Driver.h
vendored
@ -1,77 +0,0 @@
|
||||
//===- Driver.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_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 main(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;
|
||||
};
|
||||
|
||||
// Parses command line options.
|
||||
class ELFOptTable : public llvm::opt::OptTable {
|
||||
public:
|
||||
ELFOptTable();
|
||||
llvm::opt::InputArgList parse(ArrayRef<const char *> argv);
|
||||
};
|
||||
|
||||
// Create enum with OPT_xxx values for each option in Options.td
|
||||
enum {
|
||||
OPT_INVALID = 0,
|
||||
#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID,
|
||||
#include "Options.inc"
|
||||
#undef OPTION
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
253
deps/lld/ELF/DriverUtils.cpp
vendored
253
deps/lld/ELF/DriverUtils.cpp
vendored
@ -1,253 +0,0 @@
|
||||
//===- DriverUtils.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 file contains utility functions for the driver. Because there
|
||||
// are so many small functions, we created this separate file to make
|
||||
// Driver.cpp less cluttered.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Driver.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "lld/Common/Reproduce.h"
|
||||
#include "lld/Common/Version.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/Option/Option.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/Process.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::sys;
|
||||
using namespace llvm::opt;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
// Create OptTable
|
||||
|
||||
// Create prefix string literals used in Options.td
|
||||
#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
|
||||
#include "Options.inc"
|
||||
#undef PREFIX
|
||||
|
||||
// Create table mapping all options defined in Options.td
|
||||
static const 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},
|
||||
#include "Options.inc"
|
||||
#undef OPTION
|
||||
};
|
||||
|
||||
ELFOptTable::ELFOptTable() : OptTable(optInfo) {}
|
||||
|
||||
// Set color diagnostics according to -color-diagnostics={auto,always,never}
|
||||
// or -no-color-diagnostics flags.
|
||||
static void handleColorDiagnostics(opt::InputArgList &args) {
|
||||
auto *arg = args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq,
|
||||
OPT_no_color_diagnostics);
|
||||
if (!arg)
|
||||
return;
|
||||
if (arg->getOption().getID() == OPT_color_diagnostics) {
|
||||
errorHandler().colorDiagnostics = true;
|
||||
} else if (arg->getOption().getID() == OPT_no_color_diagnostics) {
|
||||
errorHandler().colorDiagnostics = false;
|
||||
} else {
|
||||
StringRef s = arg->getValue();
|
||||
if (s == "always")
|
||||
errorHandler().colorDiagnostics = true;
|
||||
else if (s == "never")
|
||||
errorHandler().colorDiagnostics = false;
|
||||
else if (s != "auto")
|
||||
error("unknown option: --color-diagnostics=" + s);
|
||||
}
|
||||
}
|
||||
|
||||
static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) {
|
||||
if (auto *arg = args.getLastArg(OPT_rsp_quoting)) {
|
||||
StringRef s = arg->getValue();
|
||||
if (s != "windows" && s != "posix")
|
||||
error("invalid response file quoting: " + s);
|
||||
if (s == "windows")
|
||||
return cl::TokenizeWindowsCommandLine;
|
||||
return cl::TokenizeGNUCommandLine;
|
||||
}
|
||||
if (Triple(sys::getProcessTriple()).getOS() == Triple::Win32)
|
||||
return cl::TokenizeWindowsCommandLine;
|
||||
return cl::TokenizeGNUCommandLine;
|
||||
}
|
||||
|
||||
// Gold LTO plugin takes a `--plugin-opt foo=bar` option as an alias for
|
||||
// `--plugin-opt=foo=bar`. We want to handle `--plugin-opt=foo=` as an
|
||||
// option name and `bar` as a value. Unfortunately, OptParser cannot
|
||||
// handle an option with a space in it.
|
||||
//
|
||||
// In this function, we concatenate command line arguments so that
|
||||
// `--plugin-opt <foo>` is converted to `--plugin-opt=<foo>`. This is a
|
||||
// bit hacky, but looks like it is still better than handling --plugin-opt
|
||||
// options by hand.
|
||||
static void concatLTOPluginOptions(SmallVectorImpl<const char *> &args) {
|
||||
SmallVector<const char *, 256> v;
|
||||
for (size_t i = 0, e = args.size(); i != e; ++i) {
|
||||
StringRef s = args[i];
|
||||
if ((s == "-plugin-opt" || s == "--plugin-opt") && i + 1 != e) {
|
||||
v.push_back(saver.save(s + "=" + args[i + 1]).data());
|
||||
++i;
|
||||
} else {
|
||||
v.push_back(args[i]);
|
||||
}
|
||||
}
|
||||
args = std::move(v);
|
||||
}
|
||||
|
||||
// Parses a given list of options.
|
||||
opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> argv) {
|
||||
// Make InputArgList from string vectors.
|
||||
unsigned missingIndex;
|
||||
unsigned missingCount;
|
||||
SmallVector<const char *, 256> vec(argv.data(), argv.data() + argv.size());
|
||||
|
||||
// We need to get the quoting style for response files before parsing all
|
||||
// options so we parse here before and ignore all the options but
|
||||
// --rsp-quoting.
|
||||
opt::InputArgList args = this->ParseArgs(vec, missingIndex, missingCount);
|
||||
|
||||
// Expand response files (arguments in the form of @<filename>)
|
||||
// and then parse the argument again.
|
||||
cl::ExpandResponseFiles(saver, getQuotingStyle(args), vec);
|
||||
concatLTOPluginOptions(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)) {
|
||||
std::string nearest;
|
||||
if (findNearest(arg->getAsString(args), nearest) > 1)
|
||||
error("unknown argument '" + arg->getAsString(args) + "'");
|
||||
else
|
||||
error("unknown argument '" + arg->getAsString(args) +
|
||||
"', did you mean '" + nearest + "'");
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
void elf::printHelp() {
|
||||
ELFOptTable().PrintHelp(
|
||||
outs(), (config->progName + " [options] file...").str().c_str(), "lld",
|
||||
false /*ShowHidden*/, true /*ShowAllAliases*/);
|
||||
outs() << "\n";
|
||||
|
||||
// Scripts generated by Libtool versions up to at least 2.4.6 (the most
|
||||
// recent version as of March 2017) expect /: supported targets:.* elf/
|
||||
// in a message for the -help option. If it doesn't match, the scripts
|
||||
// assume that the linker doesn't support very basic features such as
|
||||
// shared libraries. Therefore, we need to print out at least "elf".
|
||||
outs() << config->progName << ": supported targets: elf\n";
|
||||
}
|
||||
|
||||
static std::string rewritePath(StringRef s) {
|
||||
if (fs::exists(s))
|
||||
return relativeToRoot(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
// Reconstructs command line arguments so that so that you can re-run
|
||||
// the same command with the same inputs. This is for --reproduce.
|
||||
std::string elf::createResponseFile(const opt::InputArgList &args) {
|
||||
SmallString<0> data;
|
||||
raw_svector_ostream os(data);
|
||||
os << "--chroot .\n";
|
||||
|
||||
// Copy the command line to the output while rewriting paths.
|
||||
for (auto *arg : args) {
|
||||
switch (arg->getOption().getID()) {
|
||||
case OPT_reproduce:
|
||||
break;
|
||||
case OPT_INPUT:
|
||||
os << quote(rewritePath(arg->getValue())) << "\n";
|
||||
break;
|
||||
case OPT_o:
|
||||
// If -o path contains directories, "lld @response.txt" will likely
|
||||
// fail because the archive we are creating doesn't contain empty
|
||||
// directories for the output path (-o doesn't create directories).
|
||||
// Strip directories to prevent the issue.
|
||||
os << "-o " << quote(sys::path::filename(arg->getValue())) << "\n";
|
||||
break;
|
||||
case OPT_dynamic_list:
|
||||
case OPT_library_path:
|
||||
case OPT_rpath:
|
||||
case OPT_script:
|
||||
case OPT_symbol_ordering_file:
|
||||
case OPT_sysroot:
|
||||
case OPT_version_script:
|
||||
os << arg->getSpelling() << " " << quote(rewritePath(arg->getValue()))
|
||||
<< "\n";
|
||||
break;
|
||||
default:
|
||||
os << toString(*arg) << "\n";
|
||||
}
|
||||
}
|
||||
return data.str();
|
||||
}
|
||||
|
||||
// Find a file by concatenating given paths. If a resulting path
|
||||
// starts with "=", the character is replaced with a --sysroot value.
|
||||
static Optional<std::string> findFile(StringRef path1, const Twine &path2) {
|
||||
SmallString<128> s;
|
||||
if (path1.startswith("="))
|
||||
path::append(s, config->sysroot, path1.substr(1), path2);
|
||||
else
|
||||
path::append(s, path1, path2);
|
||||
|
||||
if (fs::exists(s))
|
||||
return s.str().str();
|
||||
return None;
|
||||
}
|
||||
|
||||
Optional<std::string> elf::findFromSearchPaths(StringRef path) {
|
||||
for (StringRef dir : config->searchPaths)
|
||||
if (Optional<std::string> s = findFile(dir, path))
|
||||
return s;
|
||||
return None;
|
||||
}
|
||||
|
||||
// This is for -l<basename>. We'll look for lib<basename>.so or lib<basename>.a from
|
||||
// search paths.
|
||||
Optional<std::string> elf::searchLibraryBaseName(StringRef name) {
|
||||
for (StringRef dir : config->searchPaths) {
|
||||
if (!config->isStatic)
|
||||
if (Optional<std::string> s = findFile(dir, "lib" + name + ".so"))
|
||||
return s;
|
||||
if (Optional<std::string> s = findFile(dir, "lib" + name + ".a"))
|
||||
return s;
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
// This is for -l<namespec>.
|
||||
Optional<std::string> elf::searchLibrary(StringRef name) {
|
||||
if (name.startswith(":"))
|
||||
return findFromSearchPaths(name.substr(1));
|
||||
return searchLibraryBaseName (name);
|
||||
}
|
||||
|
||||
// If a linker/version script doesn't exist in the current directory, we also
|
||||
// look for the script in the '-L' search paths. This matches the behaviour of
|
||||
// '-T', --version-script=, and linker script INPUT() command in ld.bfd.
|
||||
Optional<std::string> elf::searchScript(StringRef name) {
|
||||
if (fs::exists(name))
|
||||
return name.str();
|
||||
return findFromSearchPaths(name);
|
||||
}
|
||||
197
deps/lld/ELF/EhFrame.cpp
vendored
197
deps/lld/ELF/EhFrame.cpp
vendored
@ -1,197 +0,0 @@
|
||||
//===- 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// .eh_frame section contains information on how to unwind the stack when
|
||||
// an exception is thrown. The section consists of sequence of CIE and FDE
|
||||
// records. The linker needs to merge CIEs and associate FDEs to CIEs.
|
||||
// That means the linker has to understand the format of the section.
|
||||
//
|
||||
// This file contains a few utility functions to read .eh_frame contents.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "EhFrame.h"
|
||||
#include "Config.h"
|
||||
#include "InputSection.h"
|
||||
#include "Relocations.h"
|
||||
#include "Target.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/Strings.h"
|
||||
#include "llvm/BinaryFormat/Dwarf.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::ELF;
|
||||
using namespace llvm::dwarf;
|
||||
using namespace llvm::object;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace {
|
||||
class EhReader {
|
||||
public:
|
||||
EhReader(InputSectionBase *s, ArrayRef<uint8_t> d) : isec(s), d(d) {}
|
||||
size_t readEhRecordSize();
|
||||
uint8_t getFdeEncoding();
|
||||
|
||||
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()));
|
||||
}
|
||||
|
||||
uint8_t readByte();
|
||||
void skipBytes(size_t count);
|
||||
StringRef readString();
|
||||
void skipLeb128();
|
||||
void skipAugP();
|
||||
|
||||
InputSectionBase *isec;
|
||||
ArrayRef<uint8_t> d;
|
||||
};
|
||||
}
|
||||
|
||||
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())
|
||||
failOn(d.data(), "unexpected end of CIE");
|
||||
uint8_t b = d.front();
|
||||
d = d.slice(1);
|
||||
return b;
|
||||
}
|
||||
|
||||
void EhReader::skipBytes(size_t count) {
|
||||
if (d.size() < count)
|
||||
failOn(d.data(), "CIE is too small");
|
||||
d = d.slice(count);
|
||||
}
|
||||
|
||||
// Read a null-terminated string.
|
||||
StringRef EhReader::readString() {
|
||||
const uint8_t *end = llvm::find(d, '\0');
|
||||
if (end == d.end())
|
||||
failOn(d.data(), "corrupted CIE (failed to read string)");
|
||||
StringRef s = toStringRef(d.slice(0, end - d.begin()));
|
||||
d = d.slice(s.size() + 1);
|
||||
return s;
|
||||
}
|
||||
|
||||
// Skip an integer encoded in the LEB128 format.
|
||||
// Actual number is not of interest because only the runtime needs it.
|
||||
// But we need to be at least able to skip it so that we can read
|
||||
// the field that follows a LEB128 number.
|
||||
void EhReader::skipLeb128() {
|
||||
const uint8_t *errPos = d.data();
|
||||
while (!d.empty()) {
|
||||
uint8_t val = d.front();
|
||||
d = d.slice(1);
|
||||
if ((val & 0x80) == 0)
|
||||
return;
|
||||
}
|
||||
failOn(errPos, "corrupted CIE (failed to read LEB128)");
|
||||
}
|
||||
|
||||
static size_t getAugPSize(unsigned enc) {
|
||||
switch (enc & 0x0f) {
|
||||
case DW_EH_PE_absptr:
|
||||
case DW_EH_PE_signed:
|
||||
return config->wordsize;
|
||||
case DW_EH_PE_udata2:
|
||||
case DW_EH_PE_sdata2:
|
||||
return 2;
|
||||
case DW_EH_PE_udata4:
|
||||
case DW_EH_PE_sdata4:
|
||||
return 4;
|
||||
case DW_EH_PE_udata8:
|
||||
case DW_EH_PE_sdata8:
|
||||
return 8;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EhReader::skipAugP() {
|
||||
uint8_t enc = readByte();
|
||||
if ((enc & 0xf0) == DW_EH_PE_aligned)
|
||||
failOn(d.data() - 1, "DW_EH_PE_aligned encoding is not supported");
|
||||
size_t size = getAugPSize(enc);
|
||||
if (size == 0)
|
||||
failOn(d.data() - 1, "unknown FDE encoding");
|
||||
if (size >= d.size())
|
||||
failOn(d.data() - 1, "corrupted CIE");
|
||||
d = d.slice(size);
|
||||
}
|
||||
|
||||
uint8_t elf::getFdeEncoding(EhSectionPiece *p) {
|
||||
return EhReader(p->sec, p->data()).getFdeEncoding();
|
||||
}
|
||||
|
||||
uint8_t EhReader::getFdeEncoding() {
|
||||
skipBytes(8);
|
||||
int version = readByte();
|
||||
if (version != 1 && version != 3)
|
||||
failOn(d.data() - 1,
|
||||
"FDE version 1 or 3 expected, but got " + Twine(version));
|
||||
|
||||
StringRef aug = readString();
|
||||
|
||||
// Skip code and data alignment factors.
|
||||
skipLeb128();
|
||||
skipLeb128();
|
||||
|
||||
// Skip the return address register. In CIE version 1 this is a single
|
||||
// byte. In CIE version 3 this is an unsigned LEB128.
|
||||
if (version == 1)
|
||||
readByte();
|
||||
else
|
||||
skipLeb128();
|
||||
|
||||
// We only care about an 'R' value, but other records may precede an 'R'
|
||||
// record. Unfortunately records are not in TLV (type-length-value) format,
|
||||
// so we need to teach the linker how to skip records for each type.
|
||||
for (char c : aug) {
|
||||
if (c == 'R')
|
||||
return readByte();
|
||||
if (c == 'z') {
|
||||
skipLeb128();
|
||||
continue;
|
||||
}
|
||||
if (c == 'P') {
|
||||
skipAugP();
|
||||
continue;
|
||||
}
|
||||
if (c == 'L') {
|
||||
readByte();
|
||||
continue;
|
||||
}
|
||||
failOn(aug.data(), "unknown .eh_frame augmentation string: " + aug);
|
||||
}
|
||||
return DW_EH_PE_absptr;
|
||||
}
|
||||
24
deps/lld/ELF/EhFrame.h
vendored
24
deps/lld/ELF/EhFrame.h
vendored
@ -1,24 +0,0 @@
|
||||
//===- 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_ELF_EHFRAME_H
|
||||
#define LLD_ELF_EHFRAME_H
|
||||
|
||||
#include "lld/Common/LLVM.h"
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
class InputSectionBase;
|
||||
struct EhSectionPiece;
|
||||
|
||||
size_t readEhRecordSize(InputSectionBase *s, size_t off);
|
||||
uint8_t getFdeEncoding(EhSectionPiece *p);
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
510
deps/lld/ELF/ICF.cpp
vendored
510
deps/lld/ELF/ICF.cpp
vendored
@ -1,510 +0,0 @@
|
||||
//===- ICF.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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// ICF is short for Identical Code Folding. This is a size optimization to
|
||||
// identify and merge two or more read-only sections (typically functions)
|
||||
// that happened to have the same contents. It usually reduces output size
|
||||
// by a few percent.
|
||||
//
|
||||
// In ICF, two sections are considered identical if they have the same
|
||||
// section flags, section data, and relocations. Relocations are tricky,
|
||||
// because two relocations are considered the same if they have the same
|
||||
// relocation types, values, and if they point to the same sections *in
|
||||
// terms of ICF*.
|
||||
//
|
||||
// Here is an example. If foo and bar defined below are compiled to the
|
||||
// same machine instructions, ICF can and should merge the two, although
|
||||
// their relocations point to each other.
|
||||
//
|
||||
// void foo() { bar(); }
|
||||
// void bar() { foo(); }
|
||||
//
|
||||
// If you merge the two, their relocations point to the same section and
|
||||
// thus you know they are mergeable, but how do you know they are
|
||||
// mergeable in the first place? This is not an easy problem to solve.
|
||||
//
|
||||
// What we are doing in LLD is to partition sections into equivalence
|
||||
// classes. Sections in the same equivalence class when the algorithm
|
||||
// terminates are considered identical. Here are details:
|
||||
//
|
||||
// 1. First, we partition sections using their hash values as keys. Hash
|
||||
// values contain section types, section contents and numbers of
|
||||
// relocations. During this step, relocation targets are not taken into
|
||||
// account. We just put sections that apparently differ into different
|
||||
// equivalence classes.
|
||||
//
|
||||
// 2. Next, for each equivalence class, we visit sections to compare
|
||||
// relocation targets. Relocation targets are considered equivalent if
|
||||
// their targets are in the same equivalence class. Sections with
|
||||
// different relocation targets are put into different equivalence
|
||||
// clases.
|
||||
//
|
||||
// 3. If we split an equivalence class in step 2, two relocations
|
||||
// previously target the same equivalence class may now target
|
||||
// different equivalence classes. Therefore, we repeat step 2 until a
|
||||
// convergence is obtained.
|
||||
//
|
||||
// 4. For each equivalence class C, pick an arbitrary section in C, and
|
||||
// merge all the other sections in C with it.
|
||||
//
|
||||
// For small programs, this algorithm needs 3-5 iterations. For large
|
||||
// programs such as Chromium, it takes more than 20 iterations.
|
||||
//
|
||||
// This algorithm was mentioned as an "optimistic algorithm" in [1],
|
||||
// though gold implements a different algorithm than this.
|
||||
//
|
||||
// We parallelize each step so that multiple threads can work on different
|
||||
// equivalence classes concurrently. That gave us a large performance
|
||||
// boost when applying ICF on large programs. For example, MSVC link.exe
|
||||
// or GNU gold takes 10-20 seconds to apply ICF on Chromium, whose output
|
||||
// size is about 1.5 GB, but LLD can finish it in less than 2 seconds on a
|
||||
// 2.8 GHz 40 core machine. Even without threading, LLD's ICF is still
|
||||
// faster than MSVC or gold though.
|
||||
//
|
||||
// [1] Safe ICF: Pointer Safe and Unwinding aware Identical Code Folding
|
||||
// in the Gold Linker
|
||||
// http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/36912.pdf
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ICF.h"
|
||||
#include "Config.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Writer.h"
|
||||
#include "lld/Common/Threads.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/BinaryFormat/ELF.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Support/xxhash.h"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
using namespace llvm;
|
||||
using namespace llvm::ELF;
|
||||
using namespace llvm::object;
|
||||
|
||||
namespace {
|
||||
template <class ELFT> class ICF {
|
||||
public:
|
||||
void run();
|
||||
|
||||
private:
|
||||
void segregate(size_t begin, size_t end, bool constant);
|
||||
|
||||
template <class RelTy>
|
||||
bool constantEq(const InputSection *a, ArrayRef<RelTy> relsA,
|
||||
const InputSection *b, ArrayRef<RelTy> relsB);
|
||||
|
||||
template <class RelTy>
|
||||
bool variableEq(const InputSection *a, ArrayRef<RelTy> relsA,
|
||||
const InputSection *b, ArrayRef<RelTy> relsB);
|
||||
|
||||
bool equalsConstant(const InputSection *a, const InputSection *b);
|
||||
bool equalsVariable(const InputSection *a, const InputSection *b);
|
||||
|
||||
size_t findBoundary(size_t begin, size_t end);
|
||||
|
||||
void forEachClassRange(size_t begin, size_t end,
|
||||
llvm::function_ref<void(size_t, size_t)> fn);
|
||||
|
||||
void forEachClass(llvm::function_ref<void(size_t, size_t)> fn);
|
||||
|
||||
std::vector<InputSection *> sections;
|
||||
|
||||
// We repeat the main loop while `Repeat` is true.
|
||||
std::atomic<bool> repeat;
|
||||
|
||||
// The main loop counter.
|
||||
int cnt = 0;
|
||||
|
||||
// We have two locations for equivalence classes. On the first iteration
|
||||
// of the main loop, Class[0] has a valid value, and Class[1] contains
|
||||
// garbage. We read equivalence classes from slot 0 and write to slot 1.
|
||||
// So, Class[0] represents the current class, and Class[1] represents
|
||||
// the next class. On each iteration, we switch their roles and use them
|
||||
// alternately.
|
||||
//
|
||||
// Why are we doing this? Recall that other threads may be working on
|
||||
// other equivalence classes in parallel. They may read sections that we
|
||||
// are updating. We cannot update equivalence classes in place because
|
||||
// it breaks the invariance that all possibly-identical sections must be
|
||||
// in the same equivalence class at any moment. In other words, the for
|
||||
// loop to update equivalence classes is not atomic, and that is
|
||||
// observable from other threads. By writing new classes to other
|
||||
// places, we can keep the invariance.
|
||||
//
|
||||
// Below, `Current` has the index of the current class, and `Next` has
|
||||
// the index of the next class. If threading is enabled, they are either
|
||||
// (0, 1) or (1, 0).
|
||||
//
|
||||
// Note on single-thread: if that's the case, they are always (0, 0)
|
||||
// because we can safely read the next class without worrying about race
|
||||
// conditions. Using the same location makes this algorithm converge
|
||||
// faster because it uses results of the same iteration earlier.
|
||||
int current = 0;
|
||||
int next = 0;
|
||||
};
|
||||
}
|
||||
|
||||
// Returns true if section S is subject of ICF.
|
||||
static bool isEligible(InputSection *s) {
|
||||
if (!s->isLive() || s->keepUnique || !(s->flags & SHF_ALLOC))
|
||||
return false;
|
||||
|
||||
// Don't merge writable sections. .data.rel.ro sections are marked as writable
|
||||
// but are semantically read-only.
|
||||
if ((s->flags & SHF_WRITE) && s->name != ".data.rel.ro" &&
|
||||
!s->name.startswith(".data.rel.ro."))
|
||||
return false;
|
||||
|
||||
// SHF_LINK_ORDER sections are ICF'd as a unit with their dependent sections,
|
||||
// so we don't consider them for ICF individually.
|
||||
if (s->flags & SHF_LINK_ORDER)
|
||||
return false;
|
||||
|
||||
// Don't merge synthetic sections as their Data member is not valid and empty.
|
||||
// The Data member needs to be valid for ICF as it is used by ICF to determine
|
||||
// the equality of section contents.
|
||||
if (isa<SyntheticSection>(s))
|
||||
return false;
|
||||
|
||||
// .init and .fini contains instructions that must be executed to initialize
|
||||
// and finalize the process. They cannot and should not be merged.
|
||||
if (s->name == ".init" || s->name == ".fini")
|
||||
return false;
|
||||
|
||||
// A user program may enumerate sections named with a C identifier using
|
||||
// __start_* and __stop_* symbols. We cannot ICF any such sections because
|
||||
// that could change program semantics.
|
||||
if (isValidCIdentifier(s->name))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Split an equivalence class into smaller classes.
|
||||
template <class ELFT>
|
||||
void ICF<ELFT>::segregate(size_t begin, size_t end, bool constant) {
|
||||
// This loop rearranges sections in [Begin, End) so that all sections
|
||||
// that are equal in terms of equals{Constant,Variable} are contiguous
|
||||
// in [Begin, End).
|
||||
//
|
||||
// The algorithm is quadratic in the worst case, but that is not an
|
||||
// issue in practice because the number of the distinct sections in
|
||||
// each range is usually very small.
|
||||
|
||||
while (begin < end) {
|
||||
// Divide [Begin, End) into two. Let Mid be the start index of the
|
||||
// second group.
|
||||
auto bound =
|
||||
std::stable_partition(sections.begin() + begin + 1,
|
||||
sections.begin() + end, [&](InputSection *s) {
|
||||
if (constant)
|
||||
return equalsConstant(sections[begin], s);
|
||||
return equalsVariable(sections[begin], s);
|
||||
});
|
||||
size_t mid = bound - sections.begin();
|
||||
|
||||
// Now we split [Begin, End) into [Begin, Mid) and [Mid, End) by
|
||||
// updating the sections in [Begin, Mid). We use Mid as an equivalence
|
||||
// class ID because every group ends with a unique index.
|
||||
for (size_t i = begin; i < mid; ++i)
|
||||
sections[i]->eqClass[next] = mid;
|
||||
|
||||
// If we created a group, we need to iterate the main loop again.
|
||||
if (mid != end)
|
||||
repeat = true;
|
||||
|
||||
begin = mid;
|
||||
}
|
||||
}
|
||||
|
||||
// Compare two lists of relocations.
|
||||
template <class ELFT>
|
||||
template <class RelTy>
|
||||
bool ICF<ELFT>::constantEq(const InputSection *secA, ArrayRef<RelTy> ra,
|
||||
const InputSection *secB, ArrayRef<RelTy> rb) {
|
||||
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))
|
||||
return false;
|
||||
|
||||
uint64_t addA = getAddend<ELFT>(ra[i]);
|
||||
uint64_t addB = getAddend<ELFT>(rb[i]);
|
||||
|
||||
Symbol &sa = secA->template getFile<ELFT>()->getRelocTargetSym(ra[i]);
|
||||
Symbol &sb = secB->template getFile<ELFT>()->getRelocTargetSym(rb[i]);
|
||||
if (&sa == &sb) {
|
||||
if (addA == addB)
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto *da = dyn_cast<Defined>(&sa);
|
||||
auto *db = dyn_cast<Defined>(&sb);
|
||||
|
||||
// Placeholder symbols generated by linker scripts look the same now but
|
||||
// may have different values later.
|
||||
if (!da || !db || da->scriptDefined || db->scriptDefined)
|
||||
return false;
|
||||
|
||||
// Relocations referring to absolute symbols are constant-equal if their
|
||||
// values are equal.
|
||||
if (!da->section && !db->section && da->value + addA == db->value + addB)
|
||||
continue;
|
||||
if (!da->section || !db->section)
|
||||
return false;
|
||||
|
||||
if (da->section->kind() != db->section->kind())
|
||||
return false;
|
||||
|
||||
// Relocations referring to InputSections are constant-equal if their
|
||||
// section offsets are equal.
|
||||
if (isa<InputSection>(da->section)) {
|
||||
if (da->value + addA == db->value + addB)
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Relocations referring to MergeInputSections are constant-equal if their
|
||||
// offsets in the output section are equal.
|
||||
auto *x = dyn_cast<MergeInputSection>(da->section);
|
||||
if (!x)
|
||||
return false;
|
||||
auto *y = cast<MergeInputSection>(db->section);
|
||||
if (x->getParent() != y->getParent())
|
||||
return false;
|
||||
|
||||
uint64_t offsetA =
|
||||
sa.isSection() ? x->getOffset(addA) : x->getOffset(da->value) + addA;
|
||||
uint64_t offsetB =
|
||||
sb.isSection() ? y->getOffset(addB) : y->getOffset(db->value) + addB;
|
||||
if (offsetA != offsetB)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Compare "non-moving" part of two InputSections, namely everything
|
||||
// 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())
|
||||
return false;
|
||||
|
||||
// If two sections have different output sections, we cannot merge them.
|
||||
// FIXME: This doesn't do the right thing in the case where there is a linker
|
||||
// script. We probably need to move output section assignment before ICF to
|
||||
// get the correct behaviour here.
|
||||
if (getOutputSectionName(a) != getOutputSectionName(b))
|
||||
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>());
|
||||
}
|
||||
|
||||
// Compare two lists of relocations. Returns true if all pairs of
|
||||
// relocations point to the same section in terms of ICF.
|
||||
template <class ELFT>
|
||||
template <class RelTy>
|
||||
bool ICF<ELFT>::variableEq(const InputSection *secA, ArrayRef<RelTy> ra,
|
||||
const InputSection *secB, ArrayRef<RelTy> rb) {
|
||||
assert(ra.size() == rb.size());
|
||||
|
||||
for (size_t i = 0; i < ra.size(); ++i) {
|
||||
// The two sections must be identical.
|
||||
Symbol &sa = secA->template getFile<ELFT>()->getRelocTargetSym(ra[i]);
|
||||
Symbol &sb = secB->template getFile<ELFT>()->getRelocTargetSym(rb[i]);
|
||||
if (&sa == &sb)
|
||||
continue;
|
||||
|
||||
auto *da = cast<Defined>(&sa);
|
||||
auto *db = cast<Defined>(&sb);
|
||||
|
||||
// We already dealt with absolute and non-InputSection symbols in
|
||||
// constantEq, and for InputSections we have already checked everything
|
||||
// except the equivalence class.
|
||||
if (!da->section)
|
||||
continue;
|
||||
auto *x = dyn_cast<InputSection>(da->section);
|
||||
if (!x)
|
||||
continue;
|
||||
auto *y = cast<InputSection>(db->section);
|
||||
|
||||
// Ineligible sections are in the special equivalence class 0.
|
||||
// They can never be the same in terms of the equivalence class.
|
||||
if (x->eqClass[current] == 0)
|
||||
return false;
|
||||
if (x->eqClass[current] != y->eqClass[current])
|
||||
return false;
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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>());
|
||||
}
|
||||
|
||||
template <class ELFT> size_t ICF<ELFT>::findBoundary(size_t begin, size_t end) {
|
||||
uint32_t eqClass = sections[begin]->eqClass[current];
|
||||
for (size_t i = begin + 1; i < end; ++i)
|
||||
if (eqClass != sections[i]->eqClass[current])
|
||||
return i;
|
||||
return end;
|
||||
}
|
||||
|
||||
// Sections in the same equivalence class are contiguous in Sections
|
||||
// vector. Therefore, Sections vector can be considered as contiguous
|
||||
// groups of sections, grouped by the class.
|
||||
//
|
||||
// This function calls Fn on every group within [Begin, End).
|
||||
template <class ELFT>
|
||||
void ICF<ELFT>::forEachClassRange(size_t begin, size_t end,
|
||||
llvm::function_ref<void(size_t, size_t)> fn) {
|
||||
while (begin < end) {
|
||||
size_t mid = findBoundary(begin, end);
|
||||
fn(begin, mid);
|
||||
begin = mid;
|
||||
}
|
||||
}
|
||||
|
||||
// Call Fn on each equivalence class.
|
||||
template <class ELFT>
|
||||
void ICF<ELFT>::forEachClass(llvm::function_ref<void(size_t, size_t)> fn) {
|
||||
// If threading is disabled or the number of sections are
|
||||
// too small to use threading, call Fn sequentially.
|
||||
if (!threadsEnabled || sections.size() < 1024) {
|
||||
forEachClassRange(0, sections.size(), fn);
|
||||
++cnt;
|
||||
return;
|
||||
}
|
||||
|
||||
current = cnt % 2;
|
||||
next = (cnt + 1) % 2;
|
||||
|
||||
// Shard into non-overlapping intervals, and call Fn in parallel.
|
||||
// The sharding must be completed before any calls to Fn are made
|
||||
// so that Fn can modify the Chunks in its shard without causing data
|
||||
// races.
|
||||
const size_t numShards = 256;
|
||||
size_t step = sections.size() / numShards;
|
||||
size_t boundaries[numShards + 1];
|
||||
boundaries[0] = 0;
|
||||
boundaries[numShards] = sections.size();
|
||||
|
||||
parallelForEachN(1, numShards, [&](size_t i) {
|
||||
boundaries[i] = findBoundary((i - 1) * step, sections.size());
|
||||
});
|
||||
|
||||
parallelForEachN(1, numShards + 1, [&](size_t i) {
|
||||
if (boundaries[i - 1] < boundaries[i])
|
||||
forEachClassRange(boundaries[i - 1], boundaries[i], fn);
|
||||
});
|
||||
++cnt;
|
||||
}
|
||||
|
||||
// Combine the hashes of the sections referenced by the given section into its
|
||||
// hash.
|
||||
template <class ELFT, class RelTy>
|
||||
static void combineRelocHashes(unsigned cnt, InputSection *isec,
|
||||
ArrayRef<RelTy> rels) {
|
||||
uint32_t hash = isec->eqClass[cnt % 2];
|
||||
for (RelTy rel : rels) {
|
||||
Symbol &s = isec->template getFile<ELFT>()->getRelocTargetSym(rel);
|
||||
if (auto *d = dyn_cast<Defined>(&s))
|
||||
if (auto *relSec = dyn_cast_or_null<InputSection>(d->section))
|
||||
hash += relSec->eqClass[cnt % 2];
|
||||
}
|
||||
// Set MSB to 1 to avoid collisions with non-hash IDs.
|
||||
isec->eqClass[(cnt + 1) % 2] = hash | (1U << 31);
|
||||
}
|
||||
|
||||
static void print(const Twine &s) {
|
||||
if (config->printIcfSections)
|
||||
message(s);
|
||||
}
|
||||
|
||||
// The main function of ICF.
|
||||
template <class ELFT> void ICF<ELFT>::run() {
|
||||
// Collect sections to merge.
|
||||
for (InputSectionBase *sec : inputSections)
|
||||
if (auto *s = dyn_cast<InputSection>(sec))
|
||||
if (isEligible(s))
|
||||
sections.push_back(s);
|
||||
|
||||
// Initially, we use hash values to partition sections.
|
||||
parallelForEach(sections, [&](InputSection *s) {
|
||||
s->eqClass[0] = xxHash64(s->data());
|
||||
});
|
||||
|
||||
for (unsigned cnt = 0; cnt != 2; ++cnt) {
|
||||
parallelForEach(sections, [&](InputSection *s) {
|
||||
if (s->areRelocsRela)
|
||||
combineRelocHashes<ELFT>(cnt, s, s->template relas<ELFT>());
|
||||
else
|
||||
combineRelocHashes<ELFT>(cnt, s, s->template rels<ELFT>());
|
||||
});
|
||||
}
|
||||
|
||||
// From now on, sections in Sections vector are ordered so that sections
|
||||
// in the same equivalence class are consecutive in the vector.
|
||||
llvm::stable_sort(sections, [](const InputSection *a, const InputSection *b) {
|
||||
return a->eqClass[0] < b->eqClass[0];
|
||||
});
|
||||
|
||||
// Compare static contents and assign unique IDs for each static content.
|
||||
forEachClass([&](size_t begin, size_t end) { segregate(begin, end, true); });
|
||||
|
||||
// Split groups by comparing relocations until convergence is obtained.
|
||||
do {
|
||||
repeat = false;
|
||||
forEachClass(
|
||||
[&](size_t begin, size_t end) { segregate(begin, end, false); });
|
||||
} while (repeat);
|
||||
|
||||
log("ICF needed " + Twine(cnt) + " iterations");
|
||||
|
||||
// Merge sections by the equivalence class.
|
||||
forEachClassRange(0, sections.size(), [&](size_t begin, size_t end) {
|
||||
if (end - begin == 1)
|
||||
return;
|
||||
print("selected section " + toString(sections[begin]));
|
||||
for (size_t i = begin + 1; i < end; ++i) {
|
||||
print(" removing identical section " + toString(sections[i]));
|
||||
sections[begin]->replace(sections[i]);
|
||||
|
||||
// At this point we know sections merged are fully identical and hence
|
||||
// we want to remove duplicate implicit dependencies such as link order
|
||||
// and relocation sections.
|
||||
for (InputSection *isec : sections[i]->dependentSections)
|
||||
isec->markDead();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ICF entry point function.
|
||||
template <class ELFT> void elf::doIcf() { ICF<ELFT>().run(); }
|
||||
|
||||
template void elf::doIcf<ELF32LE>();
|
||||
template void elf::doIcf<ELF32BE>();
|
||||
template void elf::doIcf<ELF64LE>();
|
||||
template void elf::doIcf<ELF64BE>();
|
||||
20
deps/lld/ELF/ICF.h
vendored
20
deps/lld/ELF/ICF.h
vendored
@ -1,20 +0,0 @@
|
||||
//===- ICF.h --------------------------------------------------------------===//
|
||||
//
|
||||
// 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_ELF_ICF_H
|
||||
#define LLD_ELF_ICF_H
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
template <class ELFT> void doIcf();
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
1645
deps/lld/ELF/InputFiles.cpp
vendored
1645
deps/lld/ELF/InputFiles.cpp
vendored
File diff suppressed because it is too large
Load Diff
405
deps/lld/ELF/InputFiles.h
vendored
405
deps/lld/ELF/InputFiles.h
vendored
@ -1,405 +0,0 @@
|
||||
//===- InputFiles.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_ELF_INPUT_FILES_H
|
||||
#define LLD_ELF_INPUT_FILES_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "lld/Common/Reproduce.h"
|
||||
#include "llvm/ADT/CachedHashString.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
|
||||
#include "llvm/IR/Comdat.h"
|
||||
#include "llvm/Object/Archive.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Object/IRObjectFile.h"
|
||||
#include "llvm/Support/Threading.h"
|
||||
#include <map>
|
||||
|
||||
namespace llvm {
|
||||
class TarWriter;
|
||||
struct DILineInfo;
|
||||
namespace lto {
|
||||
class InputFile;
|
||||
}
|
||||
} // namespace llvm
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
class InputFile;
|
||||
class InputSectionBase;
|
||||
}
|
||||
|
||||
// Returns "<internal>", "foo.a(bar.o)" or "baz.o".
|
||||
std::string toString(const elf::InputFile *f);
|
||||
|
||||
namespace elf {
|
||||
|
||||
using llvm::object::Archive;
|
||||
|
||||
class Symbol;
|
||||
|
||||
// If -reproduce option is given, all input files are written
|
||||
// to this tar archive.
|
||||
extern std::unique_ptr<llvm::TarWriter> tar;
|
||||
|
||||
// Opens a given file.
|
||||
llvm::Optional<MemoryBufferRef> readFile(StringRef path);
|
||||
|
||||
// Add symbols in File to the symbol table.
|
||||
void parseFile(InputFile *file);
|
||||
|
||||
// The root class of input files.
|
||||
class InputFile {
|
||||
public:
|
||||
enum Kind {
|
||||
ObjKind,
|
||||
SharedKind,
|
||||
LazyObjKind,
|
||||
ArchiveKind,
|
||||
BitcodeKind,
|
||||
BinaryKind,
|
||||
};
|
||||
|
||||
Kind kind() const { return fileKind; }
|
||||
|
||||
bool isElf() const {
|
||||
Kind k = kind();
|
||||
return k == ObjKind || k == SharedKind;
|
||||
}
|
||||
|
||||
StringRef getName() const { return mb.getBufferIdentifier(); }
|
||||
MemoryBufferRef mb;
|
||||
|
||||
// Returns sections. It is a runtime error to call this function
|
||||
// on files that don't have the notion of sections.
|
||||
ArrayRef<InputSectionBase *> getSections() const {
|
||||
assert(fileKind == ObjKind || fileKind == BinaryKind);
|
||||
return sections;
|
||||
}
|
||||
|
||||
// Returns object file symbols. It is a runtime error to call this
|
||||
// function on files of other types.
|
||||
ArrayRef<Symbol *> getSymbols() { return getMutableSymbols(); }
|
||||
|
||||
MutableArrayRef<Symbol *> getMutableSymbols() {
|
||||
assert(fileKind == BinaryKind || fileKind == ObjKind ||
|
||||
fileKind == BitcodeKind);
|
||||
return symbols;
|
||||
}
|
||||
|
||||
// Filename of .a which contained this file. If this file was
|
||||
// not in an archive file, it is the empty string. We use this
|
||||
// string for creating error messages.
|
||||
std::string archiveName;
|
||||
|
||||
// If this is an architecture-specific file, the following members
|
||||
// have ELF type (i.e. ELF{32,64}{LE,BE}) and target machine type.
|
||||
ELFKind ekind = ELFNoneKind;
|
||||
uint16_t emachine = llvm::ELF::EM_NONE;
|
||||
uint8_t osabi = 0;
|
||||
uint8_t abiVersion = 0;
|
||||
|
||||
// Cache for toString(). Only toString() should use this member.
|
||||
mutable std::string toStringCache;
|
||||
|
||||
std::string getSrcMsg(const Symbol &sym, InputSectionBase &sec,
|
||||
uint64_t offset);
|
||||
|
||||
// True if this is an argument for --just-symbols. Usually false.
|
||||
bool justSymbols = false;
|
||||
|
||||
// outSecOff of .got2 in the current file. This is used by PPC32 -fPIC/-fPIE
|
||||
// to compute offsets in PLT call stubs.
|
||||
uint32_t ppc32Got2OutSecOff = 0;
|
||||
|
||||
// On PPC64 we need to keep track of which files contain small code model
|
||||
// relocations that access the .toc section. To minimize the chance of a
|
||||
// relocation overflow, files that do contain said relocations should have
|
||||
// their .toc sections sorted closer to the .got section than files that do
|
||||
// not contain any small code model relocations. Thats because the toc-pointer
|
||||
// is defined to point at .got + 0x8000 and the instructions used with small
|
||||
// code model relocations support immediates in the range [-0x8000, 0x7FFC],
|
||||
// making the addressable range relative to the toc pointer
|
||||
// [.got, .got + 0xFFFC].
|
||||
bool ppc64SmallCodeModelTocRelocs = false;
|
||||
|
||||
// groupId is used for --warn-backrefs which is an optional error
|
||||
// checking feature. All files within the same --{start,end}-group or
|
||||
// --{start,end}-lib get the same group ID. Otherwise, each file gets a new
|
||||
// group ID. For more info, see checkDependency() in SymbolTable.cpp.
|
||||
uint32_t groupId;
|
||||
static bool isInGroup;
|
||||
static uint32_t nextGroupId;
|
||||
|
||||
// Index of MIPS GOT built for this file.
|
||||
llvm::Optional<size_t> mipsGotIndex;
|
||||
|
||||
std::vector<Symbol *> symbols;
|
||||
|
||||
protected:
|
||||
InputFile(Kind k, MemoryBufferRef m);
|
||||
std::vector<InputSectionBase *> sections;
|
||||
|
||||
private:
|
||||
const Kind fileKind;
|
||||
};
|
||||
|
||||
class ELFFileBase : public InputFile {
|
||||
public:
|
||||
ELFFileBase(Kind k, MemoryBufferRef m);
|
||||
static bool classof(const InputFile *f) { return f->isElf(); }
|
||||
|
||||
template <typename ELFT> llvm::object::ELFFile<ELFT> getObj() const {
|
||||
return check(llvm::object::ELFFile<ELFT>::create(mb.getBuffer()));
|
||||
}
|
||||
|
||||
StringRef getStringTable() const { return stringTable; }
|
||||
|
||||
template <typename ELFT> typename ELFT::SymRange getELFSyms() const {
|
||||
return typename ELFT::SymRange(
|
||||
reinterpret_cast<const typename ELFT::Sym *>(elfSyms), numELFSyms);
|
||||
}
|
||||
template <typename ELFT> typename ELFT::SymRange getGlobalELFSyms() const {
|
||||
return getELFSyms<ELFT>().slice(firstGlobal);
|
||||
}
|
||||
|
||||
protected:
|
||||
// Initializes this class's member variables.
|
||||
template <typename ELFT> void init();
|
||||
|
||||
const void *elfSyms = nullptr;
|
||||
size_t numELFSyms = 0;
|
||||
uint32_t firstGlobal = 0;
|
||||
StringRef stringTable;
|
||||
};
|
||||
|
||||
// .o file.
|
||||
template <class ELFT> class ObjFile : public ELFFileBase {
|
||||
using Elf_Rel = typename ELFT::Rel;
|
||||
using Elf_Rela = typename ELFT::Rela;
|
||||
using Elf_Sym = typename ELFT::Sym;
|
||||
using Elf_Shdr = typename ELFT::Shdr;
|
||||
using Elf_Word = typename ELFT::Word;
|
||||
using Elf_CGProfile = typename ELFT::CGProfile;
|
||||
|
||||
public:
|
||||
static bool classof(const InputFile *f) { return f->kind() == ObjKind; }
|
||||
|
||||
llvm::object::ELFFile<ELFT> getObj() const {
|
||||
return this->ELFFileBase::getObj<ELFT>();
|
||||
}
|
||||
|
||||
ArrayRef<Symbol *> getLocalSymbols();
|
||||
ArrayRef<Symbol *> getGlobalSymbols();
|
||||
|
||||
ObjFile(MemoryBufferRef m, StringRef archiveName) : ELFFileBase(ObjKind, m) {
|
||||
this->archiveName = archiveName;
|
||||
}
|
||||
|
||||
void parse(bool ignoreComdats = false);
|
||||
|
||||
StringRef getShtGroupSignature(ArrayRef<Elf_Shdr> sections,
|
||||
const Elf_Shdr &sec);
|
||||
|
||||
Symbol &getSymbol(uint32_t symbolIndex) const {
|
||||
if (symbolIndex >= this->symbols.size())
|
||||
fatal(toString(this) + ": invalid symbol index");
|
||||
return *this->symbols[symbolIndex];
|
||||
}
|
||||
|
||||
uint32_t getSectionIndex(const Elf_Sym &sym) const;
|
||||
|
||||
template <typename RelT> Symbol &getRelocTargetSym(const RelT &rel) const {
|
||||
uint32_t symIndex = rel.getSymbol(config->isMips64EL);
|
||||
return getSymbol(symIndex);
|
||||
}
|
||||
|
||||
llvm::Optional<llvm::DILineInfo> getDILineInfo(InputSectionBase *, uint64_t);
|
||||
llvm::Optional<std::pair<std::string, unsigned>> getVariableLoc(StringRef name);
|
||||
|
||||
// MIPS GP0 value defined by this file. This value represents the gp value
|
||||
// used to create the relocatable object and required to support
|
||||
// R_MIPS_GPREL16 / R_MIPS_GPREL32 relocations.
|
||||
uint32_t mipsGp0 = 0;
|
||||
|
||||
uint32_t andFeatures = 0;
|
||||
|
||||
// Name of source file obtained from STT_FILE symbol value,
|
||||
// or empty string if there is no such symbol in object file
|
||||
// symbol table.
|
||||
StringRef sourceFile;
|
||||
|
||||
// True if the file defines functions compiled with
|
||||
// -fsplit-stack. Usually false.
|
||||
bool splitStack = false;
|
||||
|
||||
// True if the file defines functions compiled with -fsplit-stack,
|
||||
// but had one or more functions with the no_split_stack attribute.
|
||||
bool someNoSplitStack = false;
|
||||
|
||||
// Pointer to this input file's .llvm_addrsig section, if it has one.
|
||||
const Elf_Shdr *addrsigSec = nullptr;
|
||||
|
||||
// SHT_LLVM_CALL_GRAPH_PROFILE table
|
||||
ArrayRef<Elf_CGProfile> cgProfile;
|
||||
|
||||
private:
|
||||
void initializeSections(bool ignoreComdats);
|
||||
void initializeSymbols();
|
||||
void initializeJustSymbols();
|
||||
void initializeDwarf();
|
||||
InputSectionBase *getRelocTarget(const Elf_Shdr &sec);
|
||||
InputSectionBase *createInputSection(const Elf_Shdr &sec);
|
||||
StringRef getSectionName(const Elf_Shdr &sec);
|
||||
|
||||
bool shouldMerge(const Elf_Shdr &sec);
|
||||
|
||||
// Each ELF symbol contains a section index which the symbol belongs to.
|
||||
// However, because the number of bits dedicated for that is limited, a
|
||||
// symbol can directly point to a section only when the section index is
|
||||
// equal to or smaller than 65280.
|
||||
//
|
||||
// If an object file contains more than 65280 sections, the file must
|
||||
// contain .symtab_shndx section. The section contains an array of
|
||||
// 32-bit integers whose size is the same as the number of symbols.
|
||||
// Nth symbol's section index is in the Nth entry of .symtab_shndx.
|
||||
//
|
||||
// The following variable contains the contents of .symtab_shndx.
|
||||
// If the section does not exist (which is common), the array is empty.
|
||||
ArrayRef<Elf_Word> shndxTable;
|
||||
|
||||
// .shstrtab contents.
|
||||
StringRef sectionStringTable;
|
||||
|
||||
// Debugging information to retrieve source file and line for error
|
||||
// reporting. Linker may find reasonable number of errors in a
|
||||
// single object file, so we cache debugging information in order to
|
||||
// parse it only once for each object file we link.
|
||||
std::unique_ptr<llvm::DWARFContext> dwarf;
|
||||
std::vector<const llvm::DWARFDebugLine::LineTable *> lineTables;
|
||||
struct VarLoc {
|
||||
const llvm::DWARFDebugLine::LineTable *lt;
|
||||
unsigned file;
|
||||
unsigned line;
|
||||
};
|
||||
llvm::DenseMap<StringRef, VarLoc> variableLoc;
|
||||
llvm::once_flag initDwarfLine;
|
||||
};
|
||||
|
||||
// LazyObjFile is analogous to ArchiveFile in the sense that
|
||||
// the file contains lazy symbols. The difference is that
|
||||
// LazyObjFile wraps a single file instead of multiple files.
|
||||
//
|
||||
// This class is used for --start-lib and --end-lib options which
|
||||
// instruct the linker to link object files between them with the
|
||||
// archive file semantics.
|
||||
class LazyObjFile : public InputFile {
|
||||
public:
|
||||
LazyObjFile(MemoryBufferRef m, StringRef archiveName,
|
||||
uint64_t offsetInArchive)
|
||||
: InputFile(LazyObjKind, m), offsetInArchive(offsetInArchive) {
|
||||
this->archiveName = archiveName;
|
||||
}
|
||||
|
||||
static bool classof(const InputFile *f) { return f->kind() == LazyObjKind; }
|
||||
|
||||
template <class ELFT> void parse();
|
||||
void fetch();
|
||||
|
||||
private:
|
||||
uint64_t offsetInArchive;
|
||||
};
|
||||
|
||||
// An ArchiveFile object represents a .a file.
|
||||
class ArchiveFile : public InputFile {
|
||||
public:
|
||||
explicit ArchiveFile(std::unique_ptr<Archive> &&file);
|
||||
static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; }
|
||||
void parse();
|
||||
|
||||
// Pulls out an object file that contains a definition for Sym and
|
||||
// returns it. If the same file was instantiated before, this
|
||||
// function does nothing (so we don't instantiate the same file
|
||||
// more than once.)
|
||||
void fetch(const Archive::Symbol &sym);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Archive> file;
|
||||
llvm::DenseSet<uint64_t> seen;
|
||||
};
|
||||
|
||||
class BitcodeFile : public InputFile {
|
||||
public:
|
||||
BitcodeFile(MemoryBufferRef m, StringRef archiveName,
|
||||
uint64_t offsetInArchive);
|
||||
static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
|
||||
template <class ELFT> void parse();
|
||||
std::unique_ptr<llvm::lto::InputFile> obj;
|
||||
};
|
||||
|
||||
// .so file.
|
||||
class SharedFile : public ELFFileBase {
|
||||
public:
|
||||
SharedFile(MemoryBufferRef m, StringRef defaultSoName)
|
||||
: ELFFileBase(SharedKind, m), soName(defaultSoName),
|
||||
isNeeded(!config->asNeeded) {}
|
||||
|
||||
// This is actually a vector of Elf_Verdef pointers.
|
||||
std::vector<const void *> verdefs;
|
||||
|
||||
// If the output file needs Elf_Verneed data structures for this file, this is
|
||||
// a vector of Elf_Vernaux version identifiers that map onto the entries in
|
||||
// Verdefs, otherwise it is empty.
|
||||
std::vector<unsigned> vernauxs;
|
||||
|
||||
static unsigned vernauxNum;
|
||||
|
||||
std::vector<StringRef> dtNeeded;
|
||||
std::string soName;
|
||||
|
||||
static bool classof(const InputFile *f) { return f->kind() == SharedKind; }
|
||||
|
||||
template <typename ELFT> void parse();
|
||||
|
||||
// Used for --no-allow-shlib-undefined.
|
||||
bool allNeededIsKnown;
|
||||
|
||||
// Used for --as-needed
|
||||
bool isNeeded;
|
||||
};
|
||||
|
||||
class BinaryFile : public InputFile {
|
||||
public:
|
||||
explicit BinaryFile(MemoryBufferRef m) : InputFile(BinaryKind, m) {}
|
||||
static bool classof(const InputFile *f) { return f->kind() == BinaryKind; }
|
||||
void parse();
|
||||
};
|
||||
|
||||
InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName = "",
|
||||
uint64_t offsetInArchive = 0);
|
||||
|
||||
inline bool isBitcode(MemoryBufferRef mb) {
|
||||
return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode;
|
||||
}
|
||||
|
||||
std::string replaceThinLTOSuffix(StringRef path);
|
||||
|
||||
extern std::vector<BinaryFile *> binaryFiles;
|
||||
extern std::vector<BitcodeFile *> bitcodeFiles;
|
||||
extern std::vector<LazyObjFile *> lazyObjFiles;
|
||||
extern std::vector<InputFile *> objectFiles;
|
||||
extern std::vector<SharedFile *> sharedFiles;
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
1336
deps/lld/ELF/InputSection.cpp
vendored
1336
deps/lld/ELF/InputSection.cpp
vendored
File diff suppressed because it is too large
Load Diff
377
deps/lld/ELF/InputSection.h
vendored
377
deps/lld/ELF/InputSection.h
vendored
@ -1,377 +0,0 @@
|
||||
//===- InputSection.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_ELF_INPUT_SECTION_H
|
||||
#define LLD_ELF_INPUT_SECTION_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "Relocations.h"
|
||||
#include "Thunks.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "llvm/ADT/CachedHashString.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/TinyPtrVector.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
class Symbol;
|
||||
struct SectionPiece;
|
||||
|
||||
class Defined;
|
||||
struct Partition;
|
||||
class SyntheticSection;
|
||||
class MergeSyntheticSection;
|
||||
template <class ELFT> class ObjFile;
|
||||
class OutputSection;
|
||||
|
||||
extern std::vector<Partition> partitions;
|
||||
|
||||
// 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
|
||||
// just as a convenience for implementing special ways of combining some
|
||||
// sections.
|
||||
class SectionBase {
|
||||
public:
|
||||
enum Kind { Regular, EHFrame, Merge, Synthetic, 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;
|
||||
|
||||
unsigned sectionKind : 3;
|
||||
|
||||
// The next three bit fields are only used by InputSectionBase, but we
|
||||
// put them here so the struct packs better.
|
||||
|
||||
// True if this section has already been placed to a linker script
|
||||
// output section. This is needed because, in a linker script, you
|
||||
// can refer to the same section more than once. For example, in
|
||||
// the following linker script,
|
||||
//
|
||||
// .foo : { *(.text) }
|
||||
// .bar : { *(.text) }
|
||||
//
|
||||
// .foo takes all .text sections, and .bar becomes empty. To achieve
|
||||
// this, we need to memorize whether a section has been placed or
|
||||
// not for each input section.
|
||||
unsigned assigned : 1;
|
||||
|
||||
unsigned bss : 1;
|
||||
|
||||
// Set for sections that should not be folded by ICF.
|
||||
unsigned keepUnique : 1;
|
||||
|
||||
// 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 link;
|
||||
uint32_t info;
|
||||
|
||||
OutputSection *getOutputSection();
|
||||
const OutputSection *getOutputSection() const {
|
||||
return const_cast<SectionBase *>(this)->getOutputSection();
|
||||
}
|
||||
|
||||
// Translate an offset in the input section to an offset in the output
|
||||
// section.
|
||||
uint64_t getOffset(uint64_t offset) const;
|
||||
|
||||
uint64_t getVA(uint64_t offset = 0) const;
|
||||
|
||||
bool isLive() const { return partition != 0; }
|
||||
void markLive() { partition = 1; }
|
||||
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), assigned(false),
|
||||
bss(false), keepUnique(false), partition(0), alignment(alignment),
|
||||
flags(flags), entsize(entsize), type(type), link(link), info(info) {}
|
||||
};
|
||||
|
||||
// This corresponds to a section of an input file.
|
||||
class InputSectionBase : public SectionBase {
|
||||
public:
|
||||
template <class ELFT>
|
||||
InputSectionBase(ObjFile<ELFT> &file, const typename ELFT::Shdr &header,
|
||||
StringRef name, Kind sectionKind);
|
||||
|
||||
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,
|
||||
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;
|
||||
|
||||
template <class ELFT> ObjFile<ELFT> *getFile() const {
|
||||
return cast_or_null<ObjFile<ELFT>>(file);
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> data() const {
|
||||
if (uncompressedSize >= 0)
|
||||
uncompress();
|
||||
return rawData;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// InputSections that are dependent on us (reverse dependency for GC)
|
||||
llvm::TinyPtrVector<InputSection *> dependentSections;
|
||||
|
||||
// Returns the size of this section (even if this is a common or BSS.)
|
||||
size_t getSize() const;
|
||||
|
||||
InputSection *getLinkOrderDep() const;
|
||||
|
||||
// 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 getSrcMsg(const Symbol &sym, uint64_t offset);
|
||||
std::string getObjMsg(uint64_t offset);
|
||||
|
||||
// Each section knows how to relocate itself. These functions apply
|
||||
// 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);
|
||||
|
||||
// The native ELF reloc data type is not very convenient to handle.
|
||||
// So we convert ELF reloc records to our own records in Relocations.cpp.
|
||||
// This vector contains such "cooked" relocations.
|
||||
std::vector<Relocation> relocations;
|
||||
|
||||
// A function compiled with -fsplit-stack calling a function
|
||||
// compiled without -fsplit-stack needs its prologue adjusted. Find
|
||||
// such functions and adjust their prologues. This is very similar
|
||||
// to relocation. See https://gcc.gnu.org/wiki/SplitStacks for more
|
||||
// information.
|
||||
template <typename ELFT>
|
||||
void adjustSplitStackFunctionPrologues(uint8_t *buf, uint8_t *end);
|
||||
|
||||
|
||||
template <typename T> llvm::ArrayRef<T> getDataAs() const {
|
||||
size_t s = data().size();
|
||||
assert(s % sizeof(T) == 0);
|
||||
return llvm::makeArrayRef<T>((const T *)data().data(), s / sizeof(T));
|
||||
}
|
||||
|
||||
protected:
|
||||
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;
|
||||
};
|
||||
|
||||
// SectionPiece represents a piece of splittable section contents.
|
||||
// We allocate a lot of these and binary search on them. This means that they
|
||||
// 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(size_t off, uint32_t hash, bool live)
|
||||
: inputOff(off), live(live || !config->gcSections), hash(hash >> 1) {}
|
||||
|
||||
uint32_t inputOff;
|
||||
uint32_t live : 1;
|
||||
uint32_t hash : 31;
|
||||
uint64_t outputOff = 0;
|
||||
};
|
||||
|
||||
static_assert(sizeof(SectionPiece) == 16, "SectionPiece is too big");
|
||||
|
||||
// This corresponds to a SHF_MERGE section of an input file.
|
||||
class MergeInputSection : public InputSectionBase {
|
||||
public:
|
||||
template <class ELFT>
|
||||
MergeInputSection(ObjFile<ELFT> &f, const typename ELFT::Shdr &header,
|
||||
StringRef name);
|
||||
MergeInputSection(uint64_t flags, uint32_t type, uint64_t entsize,
|
||||
ArrayRef<uint8_t> data, StringRef name);
|
||||
|
||||
static bool classof(const SectionBase *s) { return s->kind() == Merge; }
|
||||
void splitIntoPieces();
|
||||
|
||||
// Translate an offset in the input section to an offset in the parent
|
||||
// MergeSyntheticSection.
|
||||
uint64_t getParentOffset(uint64_t offset) const;
|
||||
|
||||
// Splittable sections are handled as a sequence of data
|
||||
// rather than a single large blob of data.
|
||||
std::vector<SectionPiece> pieces;
|
||||
|
||||
// Returns I'th piece's data. This function is very hot when
|
||||
// string merging is enabled, so we want to inline.
|
||||
LLVM_ATTRIBUTE_ALWAYS_INLINE
|
||||
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};
|
||||
}
|
||||
|
||||
// Returns the SectionPiece at a given input section offset.
|
||||
SectionPiece *getSectionPiece(uint64_t offset);
|
||||
const SectionPiece *getSectionPiece(uint64_t offset) const {
|
||||
return const_cast<MergeInputSection *>(this)->getSectionPiece(offset);
|
||||
}
|
||||
|
||||
SyntheticSection *getParent() const;
|
||||
|
||||
private:
|
||||
void splitStrings(ArrayRef<uint8_t> a, size_t size);
|
||||
void splitNonStrings(ArrayRef<uint8_t> a, size_t size);
|
||||
};
|
||||
|
||||
struct EhSectionPiece {
|
||||
EhSectionPiece(size_t off, InputSectionBase *sec, uint32_t size,
|
||||
unsigned firstRelocation)
|
||||
: inputOff(off), sec(sec), size(size), firstRelocation(firstRelocation) {}
|
||||
|
||||
ArrayRef<uint8_t> data() {
|
||||
return {sec->data().data() + this->inputOff, size};
|
||||
}
|
||||
|
||||
size_t inputOff;
|
||||
ssize_t outputOff = -1;
|
||||
InputSectionBase *sec;
|
||||
uint32_t size;
|
||||
unsigned firstRelocation;
|
||||
};
|
||||
|
||||
// This corresponds to a .eh_frame section of an input file.
|
||||
class EhInputSection : public InputSectionBase {
|
||||
public:
|
||||
template <class ELFT>
|
||||
EhInputSection(ObjFile<ELFT> &f, const typename ELFT::Shdr &header,
|
||||
StringRef name);
|
||||
static bool classof(const SectionBase *s) { return s->kind() == EHFrame; }
|
||||
template <class ELFT> void split();
|
||||
template <class ELFT, class RelTy> void split(ArrayRef<RelTy> rels);
|
||||
|
||||
// Splittable sections are handled as a sequence of data
|
||||
// rather than a single large blob of data.
|
||||
std::vector<EhSectionPiece> pieces;
|
||||
|
||||
SyntheticSection *getParent() const;
|
||||
};
|
||||
|
||||
// This is a section that is added directly to an output section
|
||||
// instead of needing special combination via a synthetic section. This
|
||||
// includes all input sections with the exceptions of SHF_MERGE and
|
||||
// .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,
|
||||
ArrayRef<uint8_t> data, StringRef name, Kind k = Regular);
|
||||
template <class ELFT>
|
||||
InputSection(ObjFile<ELFT> &f, const typename ELFT::Shdr &header,
|
||||
StringRef name);
|
||||
|
||||
// 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;
|
||||
|
||||
// This variable has two usages. Initially, it represents an index in the
|
||||
// OutputSection's InputSection list, and is used when ordering SHF_LINK_ORDER
|
||||
// sections. After assignAddresses is called, it represents the offset from
|
||||
// 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);
|
||||
|
||||
// Used by ICF.
|
||||
uint32_t eqClass[2] = {0, 0};
|
||||
|
||||
// Called by ICF to merge two input sections.
|
||||
void replace(InputSection *other);
|
||||
|
||||
static InputSection discarded;
|
||||
|
||||
private:
|
||||
template <class ELFT, class RelTy>
|
||||
void copyRelocations(uint8_t *buf, llvm::ArrayRef<RelTy> rels);
|
||||
|
||||
template <class ELFT> void copyShtGroup(uint8_t *buf);
|
||||
};
|
||||
|
||||
// The list of all input sections.
|
||||
extern std::vector<InputSectionBase *> inputSections;
|
||||
|
||||
} // namespace elf
|
||||
|
||||
std::string toString(const elf::InputSectionBase *);
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
303
deps/lld/ELF/LTO.cpp
vendored
303
deps/lld/ELF/LTO.cpp
vendored
@ -1,303 +0,0 @@
|
||||
//===- LTO.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 "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/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/CodeGen.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <vector>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::ELF;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
// 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 =
|
||||
llvm::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::F_None);
|
||||
if (ec) {
|
||||
error("cannot open " + file + ": " + ec.message());
|
||||
return nullptr;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::string getThinLTOOutputFile(StringRef modulePath) {
|
||||
return lto::getThinLTOOutputFile(modulePath,
|
||||
config->thinLTOPrefixReplace.first,
|
||||
config->thinLTOPrefixReplace.second);
|
||||
}
|
||||
|
||||
static lto::Config createConfig() {
|
||||
lto::Config c;
|
||||
|
||||
// LLD supports the new relocations and address-significance tables.
|
||||
c.Options = initTargetOptionsFromCodeGenFlags();
|
||||
c.Options.RelaxELFRelocations = true;
|
||||
c.Options.EmitAddrsig = true;
|
||||
|
||||
// Always emit a section per function/datum with LTO.
|
||||
c.Options.FunctionSections = true;
|
||||
c.Options.DataSections = true;
|
||||
|
||||
if (config->relocatable)
|
||||
c.RelocModel = None;
|
||||
else if (config->isPic)
|
||||
c.RelocModel = Reloc::PIC_;
|
||||
else
|
||||
c.RelocModel = Reloc::Static;
|
||||
|
||||
c.CodeModel = getCodeModelFromCMModel();
|
||||
c.DisableVerify = config->disableVerify;
|
||||
c.DiagHandler = diagnosticHandler;
|
||||
c.OptLevel = config->ltoo;
|
||||
c.CPU = getCPUStr();
|
||||
c.MAttrs = getMAttrs();
|
||||
c.CGOptLevel = args::getCGOptLevel(config->ltoo);
|
||||
|
||||
// Set up a custom pipeline if we've been asked to.
|
||||
c.OptPipeline = config->ltoNewPmPasses;
|
||||
c.AAPipeline = config->ltoAAPipeline;
|
||||
|
||||
// Set up optimization remarks if we've been asked to.
|
||||
c.RemarksFilename = config->optRemarksFilename;
|
||||
c.RemarksPasses = config->optRemarksPasses;
|
||||
c.RemarksWithHotness = config->optRemarksWithHotness;
|
||||
c.RemarksFormat = config->optRemarksFormat;
|
||||
|
||||
c.SampleProfile = config->ltoSampleProfile;
|
||||
c.UseNewPM = config->ltoNewPassManager;
|
||||
c.DebugPassManager = config->ltoDebugPassManager;
|
||||
c.DwoDir = config->dwoDir;
|
||||
|
||||
c.CSIRProfile = config->ltoCSProfileFile;
|
||||
c.RunCSIRInstr = config->ltoCSProfileGenerate;
|
||||
|
||||
if (config->emitLLVM) {
|
||||
c.PostInternalizeModuleHook = [](size_t task, const Module &m) {
|
||||
if (std::unique_ptr<raw_fd_ostream> os = openFile(config->outputFile))
|
||||
WriteBitcodeToFile(m, *os, false);
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
if (config->saveTemps)
|
||||
checkError(c.addSaveTemps(config->outputFile.str() + ".",
|
||||
/*UseInputModulePath*/ true));
|
||||
return c;
|
||||
}
|
||||
|
||||
BitcodeCompiler::BitcodeCompiler() {
|
||||
// Initialize indexFile.
|
||||
if (!config->thinLTOIndexOnlyArg.empty())
|
||||
indexFile = openFile(config->thinLTOIndexOnlyArg);
|
||||
|
||||
// Initialize ltoObj.
|
||||
lto::ThinBackend backend;
|
||||
if (config->thinLTOIndexOnly) {
|
||||
auto onIndexWrite = [&](StringRef s) { thinIndices.erase(s); };
|
||||
backend = lto::createWriteIndexesThinBackend(
|
||||
config->thinLTOPrefixReplace.first, config->thinLTOPrefixReplace.second,
|
||||
config->thinLTOEmitImportsFiles, indexFile.get(), onIndexWrite);
|
||||
} else if (config->thinLTOJobs != -1U) {
|
||||
backend = lto::createInProcessThinBackend(config->thinLTOJobs);
|
||||
}
|
||||
|
||||
ltoObj = llvm::make_unique<lto::LTO>(createConfig(), backend,
|
||||
config->ltoPartitions);
|
||||
|
||||
// Initialize usedStartStop.
|
||||
symtab->forEachSymbol([&](Symbol *sym) {
|
||||
StringRef s = sym->getName();
|
||||
for (StringRef prefix : {"__start_", "__stop_"})
|
||||
if (s.startswith(prefix))
|
||||
usedStartStop.insert(s.substr(prefix.size()));
|
||||
});
|
||||
}
|
||||
|
||||
BitcodeCompiler::~BitcodeCompiler() = default;
|
||||
|
||||
void BitcodeCompiler::add(BitcodeFile &f) {
|
||||
lto::InputFile &obj = *f.obj;
|
||||
bool isExec = !config->shared && !config->relocatable;
|
||||
|
||||
if (config->thinLTOIndexOnly)
|
||||
thinIndices.insert(obj.getName());
|
||||
|
||||
ArrayRef<Symbol *> syms = f.getSymbols();
|
||||
ArrayRef<lto::InputFile::Symbol> objSyms = obj.symbols();
|
||||
std::vector<lto::SymbolResolution> resols(syms.size());
|
||||
|
||||
// Provide a resolution to the LTO API for each symbol.
|
||||
for (size_t i = 0, e = syms.size(); i != e; ++i) {
|
||||
Symbol *sym = syms[i];
|
||||
const lto::InputFile::Symbol &objSym = objSyms[i];
|
||||
lto::SymbolResolution &r = resols[i];
|
||||
|
||||
// Ideally we shouldn't check for SF_Undefined but currently IRObjectFile
|
||||
// reports two symbols for module ASM defined. Without this check, lld
|
||||
// flags an undefined in IR with a definition in ASM as prevailing.
|
||||
// Once IRObjectFile is fixed to report only one symbol this hack can
|
||||
// be removed.
|
||||
r.Prevailing = !objSym.isUndefined() && sym->file == &f;
|
||||
|
||||
// We ask LTO to preserve following global symbols:
|
||||
// 1) All symbols when doing relocatable link, so that them can be used
|
||||
// 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.
|
||||
r.VisibleToRegularObj = config->relocatable || sym->isUsedInRegularObj ||
|
||||
(r.Prevailing && sym->includeInDynsym()) ||
|
||||
usedStartStop.count(objSym.getSectionName());
|
||||
const auto *dr = dyn_cast<Defined>(sym);
|
||||
r.FinalDefinitionInLinkageUnit =
|
||||
(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
|
||||
// for isElf(). Skip linker script defined symbols as well: they have
|
||||
// no File defined.
|
||||
!(dr->section == nullptr && (!sym->file || sym->file->isElf()));
|
||||
|
||||
if (r.Prevailing)
|
||||
sym->replace(Undefined{nullptr, sym->getName(), STB_GLOBAL, STV_DEFAULT,
|
||||
sym->type});
|
||||
|
||||
// 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;
|
||||
}
|
||||
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() {
|
||||
for (LazyObjFile *f : lazyObjFiles) {
|
||||
if (!isBitcode(f->mb))
|
||||
continue;
|
||||
std::string path = replaceThinLTOSuffix(getThinLTOOutputFile(f->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<InputFile *> BitcodeCompiler::compile() {
|
||||
unsigned maxTasks = ltoObj->getMaxTasks();
|
||||
buf.resize(maxTasks);
|
||||
files.resize(maxTasks);
|
||||
|
||||
// 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;
|
||||
if (!config->thinLTOCacheDir.empty())
|
||||
cache = check(
|
||||
lto::localCache(config->thinLTOCacheDir,
|
||||
[&](size_t task, std::unique_ptr<MemoryBuffer> mb) {
|
||||
files[task] = std::move(mb);
|
||||
}));
|
||||
|
||||
if (!bitcodeFiles.empty())
|
||||
checkError(ltoObj->run(
|
||||
[&](size_t task) {
|
||||
return llvm::make_unique<lto::NativeObjectStream>(
|
||||
llvm::make_unique<raw_svector_ostream>(buf[task]));
|
||||
},
|
||||
cache));
|
||||
|
||||
// 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->thinLTOIndexOnly) {
|
||||
thinLTOCreateEmptyIndexFiles();
|
||||
|
||||
if (!config->ltoObjPath.empty())
|
||||
saveBuffer(buf[0], config->ltoObjPath);
|
||||
|
||||
// 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 (indexFile)
|
||||
indexFile->close();
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!config->thinLTOCacheDir.empty())
|
||||
pruneCache(config->thinLTOCacheDir, config->thinLTOCachePolicy);
|
||||
|
||||
if (!config->ltoObjPath.empty()) {
|
||||
saveBuffer(buf[0], config->ltoObjPath);
|
||||
for (unsigned i = 1; i != maxTasks; ++i)
|
||||
saveBuffer(buf[i], config->ltoObjPath + Twine(i));
|
||||
}
|
||||
|
||||
if (config->saveTemps) {
|
||||
saveBuffer(buf[0], config->outputFile + ".lto.o");
|
||||
for (unsigned i = 1; i != maxTasks; ++i)
|
||||
saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.o");
|
||||
}
|
||||
|
||||
std::vector<InputFile *> ret;
|
||||
for (unsigned i = 0; i != maxTasks; ++i)
|
||||
if (!buf[i].empty())
|
||||
ret.push_back(createObjectFile(MemoryBufferRef(buf[i], "lto.tmp")));
|
||||
|
||||
for (std::unique_ptr<MemoryBuffer> &file : files)
|
||||
if (file)
|
||||
ret.push_back(createObjectFile(*file));
|
||||
return ret;
|
||||
}
|
||||
62
deps/lld/ELF/LTO.h
vendored
62
deps/lld/ELF/LTO.h
vendored
@ -1,62 +0,0 @@
|
||||
//===- LTO.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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file provides a way to combine bitcode files into one ELF
|
||||
// file by compiling them using LLVM.
|
||||
//
|
||||
// If LTO is in use, your input files are not in regular ELF files
|
||||
// but instead LLVM bitcode files. In that case, the linker has to
|
||||
// convert bitcode files into the native format so that we can create
|
||||
// an ELF file that contains native code. This file provides that
|
||||
// functionality.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_LTO_H
|
||||
#define LLD_ELF_LTO_H
|
||||
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
namespace lto {
|
||||
class LTO;
|
||||
}
|
||||
} // namespace llvm
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
class BitcodeFile;
|
||||
class InputFile;
|
||||
class LazyObjFile;
|
||||
|
||||
class BitcodeCompiler {
|
||||
public:
|
||||
BitcodeCompiler();
|
||||
~BitcodeCompiler();
|
||||
|
||||
void add(BitcodeFile &f);
|
||||
std::vector<InputFile *> compile();
|
||||
|
||||
private:
|
||||
std::unique_ptr<llvm::lto::LTO> ltoObj;
|
||||
std::vector<SmallString<0>> buf;
|
||||
std::vector<std::unique_ptr<MemoryBuffer>> files;
|
||||
llvm::DenseSet<StringRef> usedStartStop;
|
||||
std::unique_ptr<llvm::raw_fd_ostream> indexFile;
|
||||
llvm::DenseSet<StringRef> thinIndices;
|
||||
};
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
1158
deps/lld/ELF/LinkerScript.cpp
vendored
1158
deps/lld/ELF/LinkerScript.cpp
vendored
File diff suppressed because it is too large
Load Diff
312
deps/lld/ELF/LinkerScript.h
vendored
312
deps/lld/ELF/LinkerScript.h
vendored
@ -1,312 +0,0 @@
|
||||
//===- LinkerScript.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_ELF_LINKER_SCRIPT_H
|
||||
#define LLD_ELF_LINKER_SCRIPT_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "Writer.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#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 <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
class Defined;
|
||||
class InputSection;
|
||||
class InputSectionBase;
|
||||
class OutputSection;
|
||||
class SectionBase;
|
||||
class Symbol;
|
||||
class ThunkSection;
|
||||
|
||||
// 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()) {}
|
||||
|
||||
ExprValue(uint64_t val) : ExprValue(nullptr, false, val, "") {}
|
||||
|
||||
bool isAbsolute() const { return forceAbsolute || sec == nullptr; }
|
||||
uint64_t getValue() const;
|
||||
uint64_t getSecAddr() const;
|
||||
uint64_t getSectionOffset() const;
|
||||
|
||||
// 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;
|
||||
|
||||
// Original source location. Used for error messages.
|
||||
std::string loc;
|
||||
};
|
||||
|
||||
// This represents an expression in the linker script.
|
||||
// ScriptParser::readExpr reads an expression and returns an Expr.
|
||||
// Later, we evaluate the expression by calling the function.
|
||||
using Expr = std::function<ExprValue()>;
|
||||
|
||||
// This enum is used to implement linker script SECTIONS command.
|
||||
// https://sourceware.org/binutils/docs/ld/SECTIONS.html#SECTIONS
|
||||
enum SectionsCommandKind {
|
||||
AssignmentKind, // . = expr or <sym> = expr
|
||||
OutputSectionKind,
|
||||
InputSectionKind,
|
||||
ByteKind // BYTE(expr), SHORT(expr), LONG(expr) or QUAD(expr)
|
||||
};
|
||||
|
||||
struct BaseCommand {
|
||||
BaseCommand(int k) : kind(k) {}
|
||||
int kind;
|
||||
};
|
||||
|
||||
// This represents ". = <expr>" or "<symbol> = <expr>".
|
||||
struct SymbolAssignment : BaseCommand {
|
||||
SymbolAssignment(StringRef name, Expr e, std::string loc)
|
||||
: BaseCommand(AssignmentKind), name(name), expression(e), location(loc) {}
|
||||
|
||||
static bool classof(const BaseCommand *c) {
|
||||
return c->kind == AssignmentKind;
|
||||
}
|
||||
|
||||
// The LHS of an expression. Name is either a symbol name or ".".
|
||||
StringRef name;
|
||||
Defined *sym = nullptr;
|
||||
|
||||
// The RHS of an expression.
|
||||
Expr expression;
|
||||
|
||||
// Command attributes for PROVIDE, HIDDEN and PROVIDE_HIDDEN.
|
||||
bool provide = false;
|
||||
bool hidden = false;
|
||||
|
||||
// Holds file name and line number for error reporting.
|
||||
std::string location;
|
||||
|
||||
// A string representation of this command. We use this for -Map.
|
||||
std::string commandString;
|
||||
|
||||
// Address of this assignment command.
|
||||
unsigned addr;
|
||||
|
||||
// Size of this assignment command. This is usually 0, but if
|
||||
// you move '.' this may be greater than 0.
|
||||
unsigned size;
|
||||
};
|
||||
|
||||
// Linker scripts allow additional constraints to be put on ouput sections.
|
||||
// If an output section is marked as ONLY_IF_RO, the section is created
|
||||
// only if its input sections are read-only. Likewise, an output section
|
||||
// with ONLY_IF_RW is created if all input sections are RW.
|
||||
enum class ConstraintKind { NoConstraint, ReadOnly, ReadWrite };
|
||||
|
||||
// This struct is used to represent the location and size of regions of
|
||||
// target memory. Instances of the struct are created by parsing the
|
||||
// MEMORY command.
|
||||
struct MemoryRegion {
|
||||
MemoryRegion(StringRef name, uint64_t origin, uint64_t length, uint32_t flags,
|
||||
uint32_t negFlags)
|
||||
: name(name), origin(origin), length(length), flags(flags),
|
||||
negFlags(negFlags) {}
|
||||
|
||||
std::string name;
|
||||
uint64_t origin;
|
||||
uint64_t length;
|
||||
uint32_t flags;
|
||||
uint32_t negFlags;
|
||||
uint64_t curPos = 0;
|
||||
};
|
||||
|
||||
// This struct represents one section match pattern in SECTIONS() command.
|
||||
// It can optionally have negative match pattern for EXCLUDED_FILE command.
|
||||
// Also it may be surrounded with SORT() command, so contains sorting rules.
|
||||
struct SectionPattern {
|
||||
SectionPattern(StringMatcher &&pat1, StringMatcher &&pat2)
|
||||
: excludedFilePat(pat1), sectionPat(pat2),
|
||||
sortOuter(SortSectionPolicy::Default),
|
||||
sortInner(SortSectionPolicy::Default) {}
|
||||
|
||||
StringMatcher excludedFilePat;
|
||||
StringMatcher sectionPat;
|
||||
SortSectionPolicy sortOuter;
|
||||
SortSectionPolicy sortInner;
|
||||
};
|
||||
|
||||
struct InputSectionDescription : BaseCommand {
|
||||
InputSectionDescription(StringRef filePattern)
|
||||
: BaseCommand(InputSectionKind), filePat(filePattern) {}
|
||||
|
||||
static bool classof(const BaseCommand *c) {
|
||||
return c->kind == InputSectionKind;
|
||||
}
|
||||
|
||||
StringMatcher filePat;
|
||||
|
||||
// Input sections that matches at least one of SectionPatterns
|
||||
// will be associated with this InputSectionDescription.
|
||||
std::vector<SectionPattern> sectionPatterns;
|
||||
|
||||
std::vector<InputSection *> 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;
|
||||
};
|
||||
|
||||
// Represents BYTE(), SHORT(), LONG(), or QUAD().
|
||||
struct ByteCommand : BaseCommand {
|
||||
ByteCommand(Expr e, unsigned size, std::string commandString)
|
||||
: BaseCommand(ByteKind), commandString(commandString), expression(e),
|
||||
size(size) {}
|
||||
|
||||
static bool classof(const BaseCommand *c) { return c->kind == ByteKind; }
|
||||
|
||||
// Keeps string representing the command. Used for -Map" is perhaps better.
|
||||
std::string commandString;
|
||||
|
||||
Expr expression;
|
||||
|
||||
// This is just an offset of this assignment command in the output section.
|
||||
unsigned offset;
|
||||
|
||||
// Size of this data command.
|
||||
unsigned size;
|
||||
};
|
||||
|
||||
struct PhdrsCommand {
|
||||
StringRef name;
|
||||
unsigned type = llvm::ELF::PT_NULL;
|
||||
bool hasFilehdr = false;
|
||||
bool hasPhdrs = false;
|
||||
llvm::Optional<unsigned> flags;
|
||||
Expr lmaExpr = nullptr;
|
||||
};
|
||||
|
||||
class LinkerScript final {
|
||||
// Temporary state used in processSectionCommands() and assignAddresses()
|
||||
// that must be reinitialized for each call to the above functions, and must
|
||||
// not be used outside of the scope of a call to the above functions.
|
||||
struct AddressState {
|
||||
AddressState();
|
||||
uint64_t threadBssOffset = 0;
|
||||
OutputSection *outSec = nullptr;
|
||||
MemoryRegion *memRegion = nullptr;
|
||||
MemoryRegion *lmaRegion = nullptr;
|
||||
uint64_t lmaOffset = 0;
|
||||
};
|
||||
|
||||
llvm::DenseMap<StringRef, OutputSection *> nameToOutputSection;
|
||||
|
||||
void addSymbol(SymbolAssignment *cmd);
|
||||
void assignSymbol(SymbolAssignment *cmd, bool inSec);
|
||||
void setDot(Expr e, const Twine &loc, bool inSec);
|
||||
void expandOutputSection(uint64_t size);
|
||||
void expandMemoryRegions(uint64_t size);
|
||||
|
||||
std::vector<InputSection *>
|
||||
computeInputSections(const InputSectionDescription *);
|
||||
|
||||
std::vector<InputSection *> createInputSectionList(OutputSection &cmd);
|
||||
|
||||
std::vector<size_t> getPhdrIndices(OutputSection *sec);
|
||||
|
||||
MemoryRegion *findMemoryRegion(OutputSection *sec);
|
||||
|
||||
void switchTo(OutputSection *sec);
|
||||
uint64_t advance(uint64_t size, unsigned align);
|
||||
void output(InputSection *sec);
|
||||
|
||||
void assignOffsets(OutputSection *sec);
|
||||
|
||||
// Ctx 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;
|
||||
|
||||
OutputSection *aether;
|
||||
|
||||
uint64_t dot;
|
||||
|
||||
public:
|
||||
OutputSection *createOutputSection(StringRef name, StringRef location);
|
||||
OutputSection *getOrCreateOutputSection(StringRef name);
|
||||
|
||||
bool hasPhdrsCommands() { return !phdrsCommands.empty(); }
|
||||
uint64_t getDot() { return dot; }
|
||||
void discard(ArrayRef<InputSection *> v);
|
||||
|
||||
ExprValue getSymbolValue(StringRef name, const Twine &loc);
|
||||
|
||||
void addOrphanSections();
|
||||
void adjustSectionsBeforeSorting();
|
||||
void adjustSectionsAfterSorting();
|
||||
|
||||
std::vector<PhdrEntry *> createPhdrs();
|
||||
bool needsInterpSection();
|
||||
|
||||
bool shouldKeep(InputSectionBase *s);
|
||||
void assignAddresses();
|
||||
void allocateHeaders(std::vector<PhdrEntry *> &phdrs);
|
||||
void processSectionCommands();
|
||||
void declareSymbols();
|
||||
|
||||
// Used to handle INSERT AFTER statements.
|
||||
void processInsertCommands();
|
||||
|
||||
// SECTIONS command list.
|
||||
std::vector<BaseCommand *> sectionCommands;
|
||||
|
||||
// PHDRS command list.
|
||||
std::vector<PhdrsCommand> 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// Used to implement INSERT [AFTER|BEFORE]. Contains commands that need
|
||||
// to be inserted into SECTIONS commands list.
|
||||
llvm::DenseMap<StringRef, std::vector<BaseCommand *>> insertAfterCommands;
|
||||
llvm::DenseMap<StringRef, std::vector<BaseCommand *>> insertBeforeCommands;
|
||||
};
|
||||
|
||||
extern LinkerScript *script;
|
||||
|
||||
} // end namespace elf
|
||||
} // end namespace lld
|
||||
|
||||
#endif // LLD_ELF_LINKER_SCRIPT_H
|
||||
261
deps/lld/ELF/MapFile.cpp
vendored
261
deps/lld/ELF/MapFile.cpp
vendored
@ -1,261 +0,0 @@
|
||||
//===- MapFile.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 file implements the -Map option. It shows lists in order and
|
||||
// hierarchically the output sections, input sections, input files and
|
||||
// symbol:
|
||||
//
|
||||
// Address Size Align Out In Symbol
|
||||
// 00201000 00000015 4 .text
|
||||
// 00201000 0000000e 4 test.o:(.text)
|
||||
// 0020100e 00000000 0 local
|
||||
// 00201005 00000000 0 f(int)
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "MapFile.h"
|
||||
#include "InputFiles.h"
|
||||
#include "LinkerScript.h"
|
||||
#include "OutputSections.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "lld/Common/Strings.h"
|
||||
#include "lld/Common/Threads.h"
|
||||
#include "llvm/ADT/MapVector.h"
|
||||
#include "llvm/ADT/SetVector.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
using SymbolMapTy = DenseMap<const SectionBase *, SmallVector<Defined *, 4>>;
|
||||
|
||||
static const std::string indent8 = " "; // 8 spaces
|
||||
static const std::string indent16 = " "; // 16 spaces
|
||||
|
||||
// Print out the first three columns of a line.
|
||||
static void writeHeader(raw_ostream &os, uint64_t vma, uint64_t lma,
|
||||
uint64_t size, uint64_t align) {
|
||||
if (config->is64)
|
||||
os << format("%16llx %16llx %8llx %5lld ", vma, lma, size, align);
|
||||
else
|
||||
os << format("%8llx %8llx %8llx %5lld ", vma, lma, size, align);
|
||||
}
|
||||
|
||||
// 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 (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))
|
||||
v.push_back(dr);
|
||||
return v;
|
||||
}
|
||||
|
||||
// Returns a map from sections to their symbols.
|
||||
static SymbolMapTy getSectionSyms(ArrayRef<Defined *> syms) {
|
||||
SymbolMapTy ret;
|
||||
for (Defined *dr : syms)
|
||||
ret[dr->section].push_back(dr);
|
||||
|
||||
// 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();
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
// 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]);
|
||||
OutputSection *osec = syms[i]->getOutputSection();
|
||||
uint64_t vma = syms[i]->getVA();
|
||||
uint64_t lma = osec ? osec->getLMA() + vma - osec->getVA(0) : 0;
|
||||
writeHeader(os, vma, lma, syms[i]->getSize(), 1);
|
||||
os << indent16 << 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;
|
||||
}
|
||||
|
||||
// Print .eh_frame contents. Since the section consists of EhSectionPieces,
|
||||
// we need a specialized printer for that section.
|
||||
//
|
||||
// .eh_frame tend to contain a lot of section pieces that are contiguous
|
||||
// both in input file and output file. Such pieces are squashed before
|
||||
// being displayed to make output compact.
|
||||
static void printEhFrame(raw_ostream &os, const EhFrameSection *sec) {
|
||||
std::vector<EhSectionPiece> pieces;
|
||||
|
||||
auto add = [&](const EhSectionPiece &p) {
|
||||
// If P is adjacent to Last, squash the two.
|
||||
if (!pieces.empty()) {
|
||||
EhSectionPiece &last = pieces.back();
|
||||
if (last.sec == p.sec && last.inputOff + last.size == p.inputOff &&
|
||||
last.outputOff + last.size == p.outputOff) {
|
||||
last.size += p.size;
|
||||
return;
|
||||
}
|
||||
}
|
||||
pieces.push_back(p);
|
||||
};
|
||||
|
||||
// Gather section pieces.
|
||||
for (const CieRecord *rec : sec->getCieRecords()) {
|
||||
add(*rec->cie);
|
||||
for (const EhSectionPiece *fde : rec->fdes)
|
||||
add(*fde);
|
||||
}
|
||||
|
||||
// Print out section pieces.
|
||||
const OutputSection *osec = sec->getOutputSection();
|
||||
for (EhSectionPiece &p : pieces) {
|
||||
writeHeader(os, osec->addr + p.outputOff, osec->getLMA() + p.outputOff,
|
||||
p.size, 1);
|
||||
os << indent8 << toString(p.sec->file) << ":(" << p.sec->name << "+0x"
|
||||
<< Twine::utohexstr(p.inputOff) + ")\n";
|
||||
}
|
||||
}
|
||||
|
||||
void elf::writeMapFile() {
|
||||
if (config->mapFile.empty())
|
||||
return;
|
||||
|
||||
// Open a map file for writing.
|
||||
std::error_code ec;
|
||||
raw_fd_ostream os(config->mapFile, ec, sys::fs::F_None);
|
||||
if (ec) {
|
||||
error("cannot open " + config->mapFile + ": " + ec.message());
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// Print out the header line.
|
||||
int w = config->is64 ? 16 : 8;
|
||||
os << right_justify("VMA", w) << ' ' << right_justify("LMA", w)
|
||||
<< " Size Align Out In Symbol\n";
|
||||
|
||||
OutputSection* osec = nullptr;
|
||||
for (BaseCommand *base : script->sectionCommands) {
|
||||
if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
|
||||
if (cmd->provide && !cmd->sym)
|
||||
continue;
|
||||
uint64_t lma = osec ? osec->getLMA() + cmd->addr - osec->getVA(0) : 0;
|
||||
writeHeader(os, cmd->addr, lma, cmd->size, 1);
|
||||
os << cmd->commandString << '\n';
|
||||
continue;
|
||||
}
|
||||
|
||||
osec = cast<OutputSection>(base);
|
||||
writeHeader(os, osec->addr, osec->getLMA(), osec->size, osec->alignment);
|
||||
os << osec->name << '\n';
|
||||
|
||||
// Dump symbols for each input section.
|
||||
for (BaseCommand *base : osec->sectionCommands) {
|
||||
if (auto *isd = dyn_cast<InputSectionDescription>(base)) {
|
||||
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);
|
||||
os << indent8 << toString(isec) << '\n';
|
||||
for (Symbol *sym : 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';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
|
||||
if (cmd->provide && !cmd->sym)
|
||||
continue;
|
||||
writeHeader(os, cmd->addr, osec->getLMA() + cmd->addr - osec->getVA(0),
|
||||
cmd->size, 1);
|
||||
os << indent8 << cmd->commandString << '\n';
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void print(StringRef a, StringRef b) {
|
||||
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
|
||||
// followed by files that uses that symbol. Here is an example.
|
||||
//
|
||||
// strlen /lib/x86_64-linux-gnu/libc.so.6
|
||||
// tools/lld/tools/lld/CMakeFiles/lld.dir/lld.cpp.o
|
||||
// lib/libLLVMSupport.a(PrettyStackTrace.cpp.o)
|
||||
//
|
||||
// In this case, strlen is defined by libc.so.6 and used by other two
|
||||
// files.
|
||||
void elf::writeCrossReferenceTable() {
|
||||
if (!config->cref)
|
||||
return;
|
||||
|
||||
// Collect symbols and files.
|
||||
MapVector<Symbol *, SetVector<InputFile *>> map;
|
||||
for (InputFile *file : objectFiles) {
|
||||
for (Symbol *sym : file->getSymbols()) {
|
||||
if (isa<SharedSymbol>(sym))
|
||||
map[sym].insert(file);
|
||||
if (auto *d = dyn_cast<Defined>(sym))
|
||||
if (!d->isLocal() && (!d->section || d->section->isLive()))
|
||||
map[d].insert(file);
|
||||
}
|
||||
}
|
||||
|
||||
// Print out a header.
|
||||
outs() << "Cross Reference Table\n\n";
|
||||
print("Symbol", "File");
|
||||
|
||||
// Print out a table.
|
||||
for (auto kv : map) {
|
||||
Symbol *sym = kv.first;
|
||||
SetVector<InputFile *> &files = kv.second;
|
||||
|
||||
print(toString(*sym), toString(sym->file));
|
||||
for (InputFile *file : files)
|
||||
if (file != sym->file)
|
||||
print("", toString(file));
|
||||
}
|
||||
}
|
||||
19
deps/lld/ELF/MapFile.h
vendored
19
deps/lld/ELF/MapFile.h
vendored
@ -1,19 +0,0 @@
|
||||
//===- MapFile.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_ELF_MAPFILE_H
|
||||
#define LLD_ELF_MAPFILE_H
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
void writeMapFile();
|
||||
void writeCrossReferenceTable();
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
373
deps/lld/ELF/MarkLive.cpp
vendored
373
deps/lld/ELF/MarkLive.cpp
vendored
@ -1,373 +0,0 @@
|
||||
//===- MarkLive.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 file implements --gc-sections, which is a feature to remove unused
|
||||
// sections from output. Unused sections are sections that are not reachable
|
||||
// from known GC-root symbols or sections. Naturally the feature is
|
||||
// implemented as a mark-sweep garbage collector.
|
||||
//
|
||||
// Here's how it works. Each InputSectionBase has a "Live" bit. The bit is off
|
||||
// by default. Starting with GC-root symbols or sections, markLive function
|
||||
// defined in this file visits all reachable sections to set their Live
|
||||
// bits. Writer will then ignore sections whose Live bits are off, so that
|
||||
// such sections are not included into output.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "MarkLive.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/Strings.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::ELF;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace {
|
||||
template <class ELFT> class MarkLive {
|
||||
public:
|
||||
MarkLive(unsigned partition) : partition(partition) {}
|
||||
|
||||
void run();
|
||||
void moveToMain();
|
||||
|
||||
private:
|
||||
void enqueue(InputSectionBase *sec, uint64_t offset);
|
||||
void markSymbol(Symbol *sym);
|
||||
void mark();
|
||||
|
||||
template <class RelTy>
|
||||
void resolveReloc(InputSectionBase &sec, RelTy &rel, bool isLSDA);
|
||||
|
||||
template <class RelTy>
|
||||
void scanEhFrameSection(EhInputSection &eh, ArrayRef<RelTy> rels);
|
||||
|
||||
// The index of the partition that we are currently processing.
|
||||
unsigned partition;
|
||||
|
||||
// A list of sections to visit.
|
||||
SmallVector<InputSection *, 256> 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;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
template <class ELFT>
|
||||
static uint64_t getAddend(InputSectionBase &sec,
|
||||
const typename ELFT::Rel &rel) {
|
||||
return target->getImplicitAddend(sec.data().begin() + rel.r_offset,
|
||||
rel.getType(config->isMips64EL));
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static uint64_t getAddend(InputSectionBase &sec,
|
||||
const typename ELFT::Rela &rel) {
|
||||
return rel.r_addend;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
template <class RelTy>
|
||||
void MarkLive<ELFT>::resolveReloc(InputSectionBase &sec, RelTy &rel,
|
||||
bool isLSDA) {
|
||||
Symbol &sym = sec.getFile<ELFT>()->getRelocTargetSym(rel);
|
||||
|
||||
// If a symbol is referenced in a live section, it is used.
|
||||
sym.used = true;
|
||||
|
||||
if (auto *d = dyn_cast<Defined>(&sym)) {
|
||||
auto *relSec = dyn_cast_or_null<InputSectionBase>(d->section);
|
||||
if (!relSec)
|
||||
return;
|
||||
|
||||
uint64_t offset = d->value;
|
||||
if (d->isSection())
|
||||
offset += getAddend<ELFT>(sec, rel);
|
||||
|
||||
if (!isLSDA || !(relSec->flags & SHF_EXECINSTR))
|
||||
enqueue(relSec, offset);
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto *ss = dyn_cast<SharedSymbol>(&sym))
|
||||
if (!ss->isWeak())
|
||||
ss->getFile().isNeeded = true;
|
||||
|
||||
for (InputSectionBase *sec : cNamedSections.lookup(sym.getName()))
|
||||
enqueue(sec, 0);
|
||||
}
|
||||
|
||||
// The .eh_frame section is an unfortunate special case.
|
||||
// The section is divided in CIEs and FDEs and the relocations it can have are
|
||||
// * CIEs can refer to a personality function.
|
||||
// * FDEs can refer to a LSDA
|
||||
// * FDEs refer to the function they contain information about
|
||||
// The last kind of relocation cannot keep the referred section alive, or they
|
||||
// would keep everything alive in a common object file. In fact, each FDE is
|
||||
// alive if the section it refers to is alive.
|
||||
// To keep things simple, in here we just ignore the last relocation kind. The
|
||||
// other two keep the referred section alive.
|
||||
//
|
||||
// A possible improvement would be to fully process .eh_frame in the middle of
|
||||
// the gc pass. With that we would be able to also gc some sections holding
|
||||
// LSDAs and personality functions if we found that they were unused.
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
// This is a FDE. The relocations point to the described function or to
|
||||
// a LSDA. We only need to keep the LSDA alive, so ignore anything that
|
||||
// points to executable sections.
|
||||
uint64_t pieceEnd = piece.inputOff + piece.size;
|
||||
for (size_t j = firstRelI, end2 = rels.size(); j < end2; ++j)
|
||||
if (rels[j].r_offset < pieceEnd)
|
||||
resolveReloc(eh, rels[j], true);
|
||||
}
|
||||
}
|
||||
|
||||
// Some sections are used directly by the loader, so they should never be
|
||||
// garbage-collected. This function returns true if a given section is such
|
||||
// section.
|
||||
static bool isReserved(InputSectionBase *sec) {
|
||||
switch (sec->type) {
|
||||
case SHT_FINI_ARRAY:
|
||||
case SHT_INIT_ARRAY:
|
||||
case SHT_NOTE:
|
||||
case SHT_PREINIT_ARRAY:
|
||||
return true;
|
||||
default:
|
||||
StringRef s = sec->name;
|
||||
return s.startswith(".ctors") || s.startswith(".dtors") ||
|
||||
s.startswith(".init") || s.startswith(".fini") ||
|
||||
s.startswith(".jcr");
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// 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
|
||||
// doesn't change, we don't need to do anything.
|
||||
if (sec->partition == 1 || sec->partition == partition)
|
||||
return;
|
||||
sec->partition = sec->partition ? 1 : partition;
|
||||
|
||||
// Add input section to the queue.
|
||||
if (InputSection *s = dyn_cast<InputSection>(sec))
|
||||
queue.push_back(s);
|
||||
}
|
||||
|
||||
template <class ELFT> void MarkLive<ELFT>::markSymbol(Symbol *sym) {
|
||||
if (auto *d = dyn_cast_or_null<Defined>(sym))
|
||||
if (auto *isec = dyn_cast_or_null<InputSectionBase>(d->section))
|
||||
enqueue(isec, d->value);
|
||||
}
|
||||
|
||||
// This is the main function of the garbage collector.
|
||||
// Starting from GC-root sections, this function visits all reachable
|
||||
// sections to set their "Live" bits.
|
||||
template <class ELFT> void MarkLive<ELFT>::run() {
|
||||
// Add GC root symbols.
|
||||
|
||||
// Preserve externally-visible symbols if the symbols defined by this
|
||||
// file can interrupt other ELF file's symbols at runtime.
|
||||
symtab->forEachSymbol([&](Symbol *sym) {
|
||||
if (sym->includeInDynsym() && sym->partition == partition)
|
||||
markSymbol(sym);
|
||||
});
|
||||
|
||||
// If this isn't the main partition, that's all that we need to preserve.
|
||||
if (partition != 1) {
|
||||
mark();
|
||||
return;
|
||||
}
|
||||
|
||||
markSymbol(symtab->find(config->entry));
|
||||
markSymbol(symtab->find(config->init));
|
||||
markSymbol(symtab->find(config->fini));
|
||||
for (StringRef s : config->undefined)
|
||||
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>());
|
||||
}
|
||||
|
||||
if (sec->flags & SHF_LINK_ORDER)
|
||||
continue;
|
||||
|
||||
if (isReserved(sec) || script->shouldKeep(sec)) {
|
||||
enqueue(sec, 0);
|
||||
} else if (isValidCIdentifier(sec->name)) {
|
||||
cNamedSections[saver.save("__start_" + sec->name)].push_back(sec);
|
||||
cNamedSections[saver.save("__stop_" + sec->name)].push_back(sec);
|
||||
}
|
||||
}
|
||||
|
||||
mark();
|
||||
}
|
||||
|
||||
template <class ELFT> void MarkLive<ELFT>::mark() {
|
||||
// Mark all reachable sections.
|
||||
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);
|
||||
}
|
||||
|
||||
for (InputSectionBase *isec : sec.dependentSections)
|
||||
enqueue(isec, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Move the sections for some symbols to the main partition, specifically ifuncs
|
||||
// (because they can result in an IRELATIVE being added to the main partition's
|
||||
// GOT, which means that the ifunc must be available when the main partition is
|
||||
// loaded) and TLS symbols (because we only know how to correctly process TLS
|
||||
// relocations for the main partition).
|
||||
template <class ELFT> void MarkLive<ELFT>::moveToMain() {
|
||||
for (InputFile *file : 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);
|
||||
|
||||
mark();
|
||||
}
|
||||
|
||||
// Before calling this function, Live bits are off for all
|
||||
// input sections. This function make some or all of them on
|
||||
// so that they are emitted to the output file.
|
||||
template <class ELFT> void elf::markLive() {
|
||||
// If -gc-sections is not given, no sections are removed.
|
||||
if (!config->gcSections) {
|
||||
for (InputSectionBase *sec : inputSections)
|
||||
sec->markLive();
|
||||
|
||||
// If a DSO defines a symbol referenced in a regular object, it is needed.
|
||||
symtab->forEachSymbol([](Symbol *sym) {
|
||||
if (auto *s = dyn_cast<SharedSymbol>(sym))
|
||||
if (s->isUsedInRegularObj && !s->isWeak())
|
||||
s->getFile().isNeeded = true;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Otheriwse, 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 and SHT_REL/SHT_RELA sections.
|
||||
//
|
||||
// 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.).
|
||||
//
|
||||
// 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.
|
||||
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->markLive();
|
||||
}
|
||||
|
||||
// Follow the graph to mark all live sections.
|
||||
for (unsigned curPart = 1; curPart <= partitions.size(); ++curPart)
|
||||
MarkLive<ELFT>(curPart).run();
|
||||
|
||||
// If we have multiple partitions, some sections need to live in the main
|
||||
// partition even if they were allocated to a loadable partition. Move them
|
||||
// there now.
|
||||
if (partitions.size() != 1)
|
||||
MarkLive<ELFT>(1).moveToMain();
|
||||
|
||||
// Report garbage-collected sections.
|
||||
if (config->printGcSections)
|
||||
for (InputSectionBase *sec : inputSections)
|
||||
if (!sec->isLive())
|
||||
message("removing unused section " + toString(sec));
|
||||
}
|
||||
|
||||
template void elf::markLive<ELF32LE>();
|
||||
template void elf::markLive<ELF32BE>();
|
||||
template void elf::markLive<ELF64LE>();
|
||||
template void elf::markLive<ELF64BE>();
|
||||
20
deps/lld/ELF/MarkLive.h
vendored
20
deps/lld/ELF/MarkLive.h
vendored
@ -1,20 +0,0 @@
|
||||
//===- MarkLive.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_ELF_MARKLIVE_H
|
||||
#define LLD_ELF_MARKLIVE_H
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
template <class ELFT> void markLive();
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif // LLD_ELF_MARKLIVE_H
|
||||
568
deps/lld/ELF/Options.td
vendored
568
deps/lld/ELF/Options.td
vendored
@ -1,568 +0,0 @@
|
||||
include "llvm/Option/OptParser.td"
|
||||
|
||||
// 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>;
|
||||
class J<string name>: Joined<["--", "-"], name>;
|
||||
|
||||
multiclass Eq<string name, string help> {
|
||||
def NAME: Separate<["--", "-"], name>;
|
||||
def NAME # _eq: Joined<["--", "-"], name # "=">, Alias<!cast<Separate>(NAME)>,
|
||||
HelpText<help>;
|
||||
}
|
||||
|
||||
multiclass B<string name, string help1, string help2> {
|
||||
def NAME: Flag<["--", "-"], name>, HelpText<help1>;
|
||||
def no_ # NAME: Flag<["--", "-"], "no-" # name>, HelpText<help2>;
|
||||
}
|
||||
|
||||
defm auxiliary: Eq<"auxiliary", "Set DT_AUXILIARY field to the specified name">;
|
||||
|
||||
def Bsymbolic: F<"Bsymbolic">, HelpText<"Bind defined symbols locally">;
|
||||
|
||||
def Bsymbolic_functions: F<"Bsymbolic-functions">,
|
||||
HelpText<"Bind defined function symbols locally">;
|
||||
|
||||
def Bdynamic: F<"Bdynamic">, HelpText<"Link against shared libraries (default)">;
|
||||
|
||||
def Bstatic: F<"Bstatic">, HelpText<"Do not link against shared libraries">;
|
||||
|
||||
def build_id: F<"build-id">, HelpText<"Alias for --build-id=fast">;
|
||||
|
||||
def build_id_eq: J<"build-id=">, HelpText<"Generate build ID note">,
|
||||
MetaVarName<"[fast,md5,sha1,uuid,0x<hexstring>]">;
|
||||
|
||||
defm check_sections: B<"check-sections",
|
||||
"Check section addresses for overlaps (default)",
|
||||
"Do not check section addresses for overlaps">;
|
||||
|
||||
defm compress_debug_sections:
|
||||
Eq<"compress-debug-sections", "Compress DWARF debug sections">,
|
||||
MetaVarName<"[none,zlib]">;
|
||||
|
||||
defm defsym: Eq<"defsym", "Define a symbol alias">, MetaVarName<"<symbol>=<value>">;
|
||||
|
||||
defm split_stack_adjust_size
|
||||
: Eq<"split-stack-adjust-size",
|
||||
"Specify adjustment to stack size when a split-stack function calls a "
|
||||
"non-split-stack function">,
|
||||
MetaVarName<"<value>">;
|
||||
|
||||
defm library_path:
|
||||
Eq<"library-path", "Add a directory to the library search path">, MetaVarName<"<dir>">;
|
||||
|
||||
def O: JoinedOrSeparate<["-"], "O">, HelpText<"Optimize output file size">;
|
||||
|
||||
defm Tbss: Eq<"Tbss", "Same as --section-start with .bss as the sectionname">;
|
||||
|
||||
defm Tdata: Eq<"Tdata", "Same as --section-start with .data as the sectionname">;
|
||||
|
||||
defm Ttext: Eq<"Ttext", "Same as --section-start with .text as the sectionname">;
|
||||
|
||||
defm allow_multiple_definition: B<"allow-multiple-definition",
|
||||
"Allow multiple definitions",
|
||||
"Do not allow multiple definitions (default)">;
|
||||
|
||||
defm allow_shlib_undefined: B<"allow-shlib-undefined",
|
||||
"Allow unresolved references in shared libraries (default when linking a shared library)",
|
||||
"Do not allow unresolved references in shared libraries (default when linking an executable)">;
|
||||
|
||||
defm apply_dynamic_relocs: B<"apply-dynamic-relocs",
|
||||
"Apply link-time values for dynamic relocations",
|
||||
"Do not apply link-time values for dynamic relocations (default)">;
|
||||
|
||||
defm dependent_libraries: B<"dependent-libraries",
|
||||
"Process dependent library specifiers from input files (default)",
|
||||
"Ignore dependent library specifiers from input files">;
|
||||
|
||||
defm as_needed: B<"as-needed",
|
||||
"Only set DT_NEEDED for shared libraries if used",
|
||||
"Always set DT_NEEDED for shared libraries (default)">;
|
||||
|
||||
defm call_graph_ordering_file:
|
||||
Eq<"call-graph-ordering-file", "Layout sections to optimize the given callgraph">;
|
||||
|
||||
defm call_graph_profile_sort: B<"call-graph-profile-sort",
|
||||
"Reorder sections with call graph profile (default)",
|
||||
"Do not reorder sections with call graph profile">;
|
||||
|
||||
// -chroot doesn't have a help text because it is an internal option.
|
||||
def chroot: Separate<["--", "-"], "chroot">;
|
||||
|
||||
def color_diagnostics: F<"color-diagnostics">,
|
||||
HelpText<"Alias for --color-diagnostics=always">;
|
||||
|
||||
def color_diagnostics_eq: J<"color-diagnostics=">,
|
||||
HelpText<"Use colors in diagnostics">,
|
||||
MetaVarName<"[auto,always,never]">;
|
||||
|
||||
defm cref: B<"cref",
|
||||
"Output cross reference table",
|
||||
"Do not output cross reference table">;
|
||||
|
||||
defm define_common: B<"define-common",
|
||||
"Assign space to common symbols",
|
||||
"Do not assign space to common symbols">;
|
||||
|
||||
defm demangle: B<"demangle",
|
||||
"Demangle symbol names (default)",
|
||||
"Do not demangle symbol names">;
|
||||
|
||||
def disable_new_dtags: F<"disable-new-dtags">,
|
||||
HelpText<"Disable new dynamic tags">;
|
||||
|
||||
def discard_all: F<"discard-all">, HelpText<"Delete all local symbols">;
|
||||
|
||||
def discard_locals: F<"discard-locals">,
|
||||
HelpText<"Delete temporary local symbols">;
|
||||
|
||||
def discard_none: F<"discard-none">,
|
||||
HelpText<"Keep all symbols in the symbol table">;
|
||||
|
||||
defm dynamic_linker: Eq<"dynamic-linker", "Which dynamic linker to use">;
|
||||
|
||||
defm dynamic_list: Eq<"dynamic-list", "Read a list of dynamic symbols">;
|
||||
|
||||
defm eh_frame_hdr: B<"eh-frame-hdr",
|
||||
"Request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header",
|
||||
"Do not create .eh_frame_hdr section">;
|
||||
|
||||
def emit_relocs: F<"emit-relocs">, HelpText<"Generate relocations in output">;
|
||||
|
||||
def enable_new_dtags: F<"enable-new-dtags">,
|
||||
HelpText<"Enable new dynamic tags (default)">;
|
||||
|
||||
def end_group: F<"end-group">,
|
||||
HelpText<"Ignored for compatibility with GNU unless you pass --warn-backrefs">;
|
||||
|
||||
def end_lib: F<"end-lib">,
|
||||
HelpText<"End a grouping of objects that should be treated as if they were together in an archive">;
|
||||
|
||||
defm entry: Eq<"entry", "Name of entry point symbol">,
|
||||
MetaVarName<"<entry>">;
|
||||
|
||||
defm error_limit:
|
||||
Eq<"error-limit", "Maximum number of errors to emit before stopping (0 = no limit)">;
|
||||
|
||||
def error_unresolved_symbols: F<"error-unresolved-symbols">,
|
||||
HelpText<"Report unresolved symbols as errors">;
|
||||
|
||||
defm exclude_libs: Eq<"exclude-libs", "Exclude static libraries from automatic export">;
|
||||
|
||||
defm execute_only: B<"execute-only",
|
||||
"Mark executable sections unreadable",
|
||||
"Mark executable sections readable (default)">;
|
||||
|
||||
defm export_dynamic: B<"export-dynamic",
|
||||
"Put symbols in the dynamic symbol table",
|
||||
"Do not put symbols in the dynamic symbol table (default)">;
|
||||
|
||||
defm export_dynamic_symbol:
|
||||
Eq<"export-dynamic-symbol", "Put a symbol in the dynamic symbol table">;
|
||||
|
||||
defm fatal_warnings: B<"fatal-warnings",
|
||||
"Treat warnings as errors",
|
||||
"Do not treat warnings as errors (default)">;
|
||||
|
||||
defm filter: Eq<"filter", "Set DT_FILTER field to the specified name">;
|
||||
|
||||
defm fini: Eq<"fini", "Specify a finalizer function">, MetaVarName<"<symbol>">;
|
||||
|
||||
def fix_cortex_a53_843419: F<"fix-cortex-a53-843419">,
|
||||
HelpText<"Apply fixes for AArch64 Cortex-A53 erratum 843419">;
|
||||
|
||||
// This option is intentionally hidden from the user as the implementation
|
||||
// is not complete.
|
||||
def require_cet: F<"require-cet">;
|
||||
|
||||
def force_bti: F<"force-bti">,
|
||||
HelpText<"Force enable AArch64 BTI in PLT, warn if Input ELF file does not have GNU_PROPERTY_AARCH64_FEATURE_1_BTI property">;
|
||||
|
||||
defm format: Eq<"format", "Change the input format of the inputs following this option">,
|
||||
MetaVarName<"[default,elf,binary]">;
|
||||
|
||||
defm gc_sections: B<"gc-sections",
|
||||
"Enable garbage collection of unused sections",
|
||||
"Disable garbage collection of unused sections (default)">;
|
||||
|
||||
defm gdb_index: B<"gdb-index",
|
||||
"Generate .gdb_index section",
|
||||
"Do not generate .gdb_index section (default)">;
|
||||
|
||||
defm gnu_unique: B<"gnu-unique",
|
||||
"Enable STB_GNU_UNIQUE symbol binding (default)",
|
||||
"Disable STB_GNU_UNIQUE symbol binding">;
|
||||
|
||||
defm hash_style: Eq<"hash-style", "Specify hash style (sysv, gnu or both)">;
|
||||
|
||||
def help: F<"help">, HelpText<"Print option help">;
|
||||
|
||||
def icf_all: F<"icf=all">, HelpText<"Enable identical code folding">;
|
||||
|
||||
def icf_safe: F<"icf=safe">, HelpText<"Enable safe identical code folding">;
|
||||
|
||||
def icf_none: F<"icf=none">, HelpText<"Disable identical code folding (default)">;
|
||||
|
||||
def ignore_function_address_equality: F<"ignore-function-address-equality">,
|
||||
HelpText<"lld can break the address equality of functions">;
|
||||
|
||||
def ignore_data_address_equality: F<"ignore-data-address-equality">,
|
||||
HelpText<"lld can break the address equality of data">;
|
||||
|
||||
defm image_base: Eq<"image-base", "Set the base address">;
|
||||
|
||||
defm init: Eq<"init", "Specify an initializer function">,
|
||||
MetaVarName<"<symbol>">;
|
||||
|
||||
defm just_symbols: Eq<"just-symbols", "Just link symbols">;
|
||||
|
||||
defm keep_unique: Eq<"keep-unique", "Do not fold this symbol during ICF">;
|
||||
|
||||
defm library: Eq<"library", "Root name of library to use">,
|
||||
MetaVarName<"<libName>">;
|
||||
|
||||
def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">;
|
||||
|
||||
defm Map: Eq<"Map", "Print a link map to the specified file">;
|
||||
|
||||
defm merge_exidx_entries: B<"merge-exidx-entries",
|
||||
"Enable merging .ARM.exidx entries (default)",
|
||||
"Disable merging .ARM.exidx entries">;
|
||||
|
||||
def nmagic: F<"nmagic">, MetaVarName<"<magic>">,
|
||||
HelpText<"Do not page align sections, link against static libraries.">;
|
||||
|
||||
def nostdlib: F<"nostdlib">,
|
||||
HelpText<"Only search directories specified on the command line">;
|
||||
|
||||
def no_color_diagnostics: F<"no-color-diagnostics">,
|
||||
HelpText<"Do not use colors in diagnostics">;
|
||||
|
||||
def no_dynamic_linker: F<"no-dynamic-linker">,
|
||||
HelpText<"Inhibit output of .interp section">;
|
||||
|
||||
def noinhibit_exec: F<"noinhibit-exec">,
|
||||
HelpText<"Retain the executable output file whenever it is still usable">;
|
||||
|
||||
def no_nmagic: F<"no-nmagic">, MetaVarName<"<magic>">,
|
||||
HelpText<"Page align sections (default)">;
|
||||
|
||||
def no_omagic: F<"no-omagic">, MetaVarName<"<magic>">,
|
||||
HelpText<"Do not set the text data sections to be writable, page align sections (default)">;
|
||||
|
||||
def no_rosegment: F<"no-rosegment">,
|
||||
HelpText<"Do not put read-only non-executable sections in their own segment">;
|
||||
|
||||
def no_undefined: F<"no-undefined">,
|
||||
HelpText<"Report unresolved symbols even if the linker is creating a shared library">;
|
||||
|
||||
def o: JoinedOrSeparate<["-"], "o">, MetaVarName<"<path>">,
|
||||
HelpText<"Path to file to write output">;
|
||||
|
||||
def oformat: Separate<["--"], "oformat">, MetaVarName<"<format>">,
|
||||
HelpText<"Specify the binary format for the output object file">;
|
||||
|
||||
def omagic: Flag<["--"], "omagic">, MetaVarName<"<magic>">,
|
||||
HelpText<"Set the text and data sections to be readable and writable, do not page align sections, link against static libraries">;
|
||||
|
||||
defm orphan_handling:
|
||||
Eq<"orphan-handling", "Control how orphan sections are handled when linker script used">;
|
||||
|
||||
defm pack_dyn_relocs:
|
||||
Eq<"pack-dyn-relocs", "Pack dynamic relocations in the given format">,
|
||||
MetaVarName<"[none,android,relr,android+relr]">;
|
||||
|
||||
def pac_plt: F<"pac-plt">,
|
||||
HelpText<"AArch64 only, use pointer authentication in PLT">;
|
||||
|
||||
defm use_android_relr_tags: B<"use-android-relr-tags",
|
||||
"Use SHT_ANDROID_RELR / DT_ANDROID_RELR* tags instead of SHT_RELR / DT_RELR*",
|
||||
"Use SHT_RELR / DT_RELR* tags (default)">;
|
||||
|
||||
def pic_veneer: F<"pic-veneer">,
|
||||
HelpText<"Always generate position independent thunks (veneers)">;
|
||||
|
||||
defm pie: B<"pie",
|
||||
"Create a position independent executable",
|
||||
"Do not create a position independent executable (default)">;
|
||||
|
||||
defm print_gc_sections: B<"print-gc-sections",
|
||||
"List removed unused sections",
|
||||
"Do not list removed unused sections (default)">;
|
||||
|
||||
defm print_icf_sections: B<"print-icf-sections",
|
||||
"List identical folded sections",
|
||||
"Do not list identical folded sections (default)">;
|
||||
|
||||
defm print_symbol_order: Eq<"print-symbol-order",
|
||||
"Print a symbol order specified by --call-graph-ordering-file into the speficied file">;
|
||||
|
||||
def pop_state: F<"pop-state">,
|
||||
HelpText<"Undo the effect of -push-state">;
|
||||
|
||||
def push_state: F<"push-state">,
|
||||
HelpText<"Save the current state of -as-needed, -static and -whole-archive">;
|
||||
|
||||
def print_map: F<"print-map">,
|
||||
HelpText<"Print a link map to the standard output">;
|
||||
|
||||
defm reproduce: Eq<"reproduce", "Dump linker invocation and input files for debugging">;
|
||||
|
||||
defm rpath: Eq<"rpath", "Add a DT_RUNPATH to the output">;
|
||||
|
||||
def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">;
|
||||
|
||||
defm retain_symbols_file:
|
||||
Eq<"retain-symbols-file", "Retain only the symbols listed in the file">,
|
||||
MetaVarName<"<file>">;
|
||||
|
||||
defm script: Eq<"script", "Read linker script">;
|
||||
|
||||
defm section_start: Eq<"section-start", "Set address of section">,
|
||||
MetaVarName<"<address>">;
|
||||
|
||||
def shared: F<"shared">, HelpText<"Build a shared object">;
|
||||
|
||||
defm soname: Eq<"soname", "Set DT_SONAME">;
|
||||
|
||||
defm sort_section:
|
||||
Eq<"sort-section", "Specifies sections sorting rule when linkerscript is used">;
|
||||
|
||||
def start_group: F<"start-group">,
|
||||
HelpText<"Ignored for compatibility with GNU unless you pass --warn-backrefs">;
|
||||
|
||||
def start_lib: F<"start-lib">,
|
||||
HelpText<"Start a grouping of objects that should be treated as if they were together in an archive">;
|
||||
|
||||
def strip_all: F<"strip-all">, HelpText<"Strip all symbols">;
|
||||
|
||||
def strip_debug: F<"strip-debug">, HelpText<"Strip debugging information">;
|
||||
|
||||
defm symbol_ordering_file:
|
||||
Eq<"symbol-ordering-file", "Layout sections to place symbols in the order specified by symbol ordering file">;
|
||||
|
||||
defm sysroot: Eq<"sysroot", "Set the system root">;
|
||||
|
||||
def target1_rel: F<"target1-rel">, HelpText<"Interpret R_ARM_TARGET1 as R_ARM_REL32">;
|
||||
|
||||
def target1_abs: F<"target1-abs">, HelpText<"Interpret R_ARM_TARGET1 as R_ARM_ABS32 (default)">;
|
||||
|
||||
defm target2:
|
||||
Eq<"target2", "Interpret R_ARM_TARGET2 as <type>, where <type> is one of rel, abs, or got-rel">,
|
||||
MetaVarName<"<type>">;
|
||||
|
||||
defm threads: B<"threads",
|
||||
"Run the linker multi-threaded (default)",
|
||||
"Do not run the linker multi-threaded">;
|
||||
|
||||
defm toc_optimize : B<"toc-optimize",
|
||||
"(PowerPC64) Enable TOC related optimizations (default)",
|
||||
"(PowerPC64) Disable TOC related optimizations">;
|
||||
|
||||
def trace: F<"trace">, HelpText<"Print the names of the input files">;
|
||||
|
||||
defm trace_symbol: Eq<"trace-symbol", "Trace references to symbols">;
|
||||
|
||||
defm undefined: Eq<"undefined", "Force undefined symbol during linking">,
|
||||
MetaVarName<"<symbol>">;
|
||||
|
||||
defm undefined_glob: Eq<"undefined-glob", "Force undefined symbol during linking">,
|
||||
MetaVarName<"<pattern>">;
|
||||
|
||||
defm unresolved_symbols:
|
||||
Eq<"unresolved-symbols", "Determine how to handle unresolved symbols">;
|
||||
|
||||
defm undefined_version: B<"undefined-version",
|
||||
"Allow unused version in version script (default)",
|
||||
"Report version scripts that refer undefined symbols">;
|
||||
|
||||
defm rsp_quoting: Eq<"rsp-quoting", "Quoting style for response files">,
|
||||
MetaVarName<"[posix,windows]">;
|
||||
|
||||
def v: Flag<["-"], "v">, HelpText<"Display the version number">;
|
||||
|
||||
def verbose: F<"verbose">, HelpText<"Verbose mode">;
|
||||
|
||||
def version: F<"version">, HelpText<"Display the version number and exit">;
|
||||
|
||||
defm version_script: Eq<"version-script", "Read a version script">;
|
||||
|
||||
defm warn_backrefs: B<"warn-backrefs",
|
||||
"Warn about backward symbol references to fetch archive members",
|
||||
"Do not warn about backward symbol references to fetch archive members (default)">;
|
||||
|
||||
defm warn_common: B<"warn-common",
|
||||
"Warn about duplicate common symbols",
|
||||
"Do not warn about duplicate common symbols (default)">;
|
||||
|
||||
defm warn_ifunc_textrel: B<"warn-ifunc-textrel",
|
||||
"Warn about using ifunc symbols with text relocations",
|
||||
"Do not warn about using ifunc symbols with text relocations (default)">;
|
||||
|
||||
defm warn_symbol_ordering: B<"warn-symbol-ordering",
|
||||
"Warn about problems with the symbol ordering file (default)",
|
||||
"Do not warn about problems with the symbol ordering file">;
|
||||
|
||||
def warn_unresolved_symbols: F<"warn-unresolved-symbols">,
|
||||
HelpText<"Report unresolved symbols as warnings">;
|
||||
|
||||
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 wrap: Eq<"wrap", "Use wrapper functions for symbol">,
|
||||
MetaVarName<"<symbol>=<symbol>">;
|
||||
|
||||
def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"<option>">,
|
||||
HelpText<"Linker option extensions">;
|
||||
|
||||
def visual_studio_diagnostics_format : F<"vs-diagnostics">,
|
||||
HelpText<"Format diagnostics for Visual Studio compatiblity">;
|
||||
|
||||
// Aliases
|
||||
def: Separate<["-"], "f">, Alias<auxiliary>, HelpText<"Alias for --auxiliary">;
|
||||
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<["-"], "d">, Alias<define_common>, HelpText<"Alias for --define-common">;
|
||||
def: F<"dc">, Alias<define_common>, HelpText<"Alias for --define-common">;
|
||||
def: F<"dp">, Alias<define_common>, HelpText<"Alias for --define-common">;
|
||||
def: Flag<["-"], "x">, Alias<discard_all>, HelpText<"Alias for --discard-all">;
|
||||
def: Flag<["-"], "X">, Alias<discard_locals>, HelpText<"Alias for --discard-locals">;
|
||||
def: Flag<["-"], "q">, Alias<emit_relocs>, HelpText<"Alias for --emit-relocs">;
|
||||
def: Flag<["-"], ")">, Alias<end_group>, HelpText<"Alias for --end-group">;
|
||||
def: JoinedOrSeparate<["-"], "e">, Alias<entry>, HelpText<"Alias for --entry">;
|
||||
def: Flag<["-"], "E">, Alias<export_dynamic>, HelpText<"Alias for --export-dynamic">;
|
||||
def: Separate<["-"], "F">, Alias<filter>, HelpText<"Alias for --filter">;
|
||||
def: Separate<["-"], "b">, Alias<format>, HelpText<"Alias for --format">;
|
||||
def: JoinedOrSeparate<["-"], "l">, Alias<library>, HelpText<"Alias for --library">;
|
||||
def: JoinedOrSeparate<["-"], "L">, Alias<library_path>, HelpText<"Alias for --library-path">;
|
||||
def: F<"no-pic-executable">, Alias<no_pie>, HelpText<"Alias for --no-pie">;
|
||||
def: Flag<["-"], "n">, Alias<nmagic>, HelpText<"Alias for --nmagic">;
|
||||
def: Flag<["-"], "N">, Alias<omagic>, HelpText<"Alias for --omagic">;
|
||||
def: Joined<["--"], "output=">, Alias<o>, HelpText<"Alias for -o">;
|
||||
def: Separate<["--"], "output">, Alias<o>, HelpText<"Alias for -o">;
|
||||
def: F<"pic-executable">, Alias<pie>, HelpText<"Alias for --pie">;
|
||||
def: Flag<["-"], "M">, Alias<print_map>, HelpText<"Alias for --print-map">;
|
||||
def: Flag<["-"], "r">, Alias<relocatable>, HelpText<"Alias for --relocatable">;
|
||||
def: JoinedOrSeparate<["-"], "R">, Alias<rpath>, HelpText<"Alias for --rpath">;
|
||||
def: JoinedOrSeparate<["-"], "T">, Alias<script>, HelpText<"Alias for --script">;
|
||||
def: F<"Bshareable">, Alias<shared>, HelpText<"Alias for --shared">;
|
||||
def: JoinedOrSeparate<["-"], "h">, Alias<soname>, HelpText<"Alias for --soname">;
|
||||
def: Flag<["-"], "(">, Alias<start_group>, HelpText<"Alias for --start-group">;
|
||||
def: Flag<["-"], "s">, Alias<strip_all>, HelpText<"Alias for --strip-all">;
|
||||
def: Flag<["-"], "S">, Alias<strip_debug>, HelpText<"Alias for --strip-debug">;
|
||||
def: Flag<["-"], "t">, Alias<trace>, HelpText<"Alias for --trace">;
|
||||
def: JoinedOrSeparate<["-"], "y">, Alias<trace_symbol>, HelpText<"Alias for --trace-symbol">;
|
||||
def: Separate<["-", "--"], "Ttext-segment">, Alias<Ttext>, HelpText<"Alias for --Ttext">;
|
||||
def: Joined<["-", "--"], "Ttext-segment=">, Alias<Ttext>, HelpText<"Alias for --Ttext">;
|
||||
def: JoinedOrSeparate<["-"], "u">, Alias<undefined>, HelpText<"Alias for --undefined">;
|
||||
def: Flag<["-"], "V">, Alias<version>, HelpText<"Alias for --version">;
|
||||
|
||||
// LTO-related options.
|
||||
def lto_aa_pipeline: J<"lto-aa-pipeline=">,
|
||||
HelpText<"AA pipeline to run during LTO. Used in conjunction with -lto-newpm-passes">;
|
||||
def lto_debug_pass_manager: F<"lto-debug-pass-manager">,
|
||||
HelpText<"Debug new pass manager">;
|
||||
def lto_new_pass_manager: F<"lto-new-pass-manager">,
|
||||
HelpText<"Use new pass manager">;
|
||||
def lto_newpm_passes: J<"lto-newpm-passes=">,
|
||||
HelpText<"Passes to run during LTO">;
|
||||
def lto_O: J<"lto-O">, MetaVarName<"<opt-level>">,
|
||||
HelpText<"Optimization level for LTO">;
|
||||
def lto_partitions: J<"lto-partitions=">,
|
||||
HelpText<"Number of LTO codegen partitions">;
|
||||
def lto_cs_profile_generate: F<"lto-cs-profile-generate">,
|
||||
HelpText<"Perform context senstive PGO instrumentation">;
|
||||
def lto_cs_profile_file: J<"lto-cs-profile-file=">,
|
||||
HelpText<"Context sensitive profile file path">;
|
||||
def lto_sample_profile: J<"lto-sample-profile=">,
|
||||
HelpText<"Sample profile file path">;
|
||||
def disable_verify: F<"disable-verify">;
|
||||
defm mllvm: Eq<"mllvm", "Additional arguments to forward to LLVM's option processing">;
|
||||
def opt_remarks_filename: Separate<["--"], "opt-remarks-filename">,
|
||||
HelpText<"YAML output file for optimization remarks">;
|
||||
def opt_remarks_passes: Separate<["--"], "opt-remarks-passes">,
|
||||
HelpText<"Regex for the passes that need to be serialized to the output file">;
|
||||
def opt_remarks_with_hotness: Flag<["--"], "opt-remarks-with-hotness">,
|
||||
HelpText<"Include hotness information in the optimization remarks file">;
|
||||
def opt_remarks_format: Separate<["--"], "opt-remarks-format">,
|
||||
HelpText<"The format used for serializing remarks (default: YAML)">;
|
||||
defm plugin_opt: Eq<"plugin-opt", "specifies LTO options for compatibility with GNU linkers">;
|
||||
def save_temps: F<"save-temps">;
|
||||
def thinlto_cache_dir: J<"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=">, HelpText<"Number of ThinLTO jobs">;
|
||||
|
||||
def: J<"plugin-opt=O">, Alias<lto_O>, HelpText<"Alias for -lto-O">;
|
||||
def: F<"plugin-opt=debug-pass-manager">,
|
||||
Alias<lto_debug_pass_manager>, HelpText<"Alias for -lto-debug-pass-manager">;
|
||||
def: F<"plugin-opt=disable-verify">, Alias<disable_verify>, HelpText<"Alias for -disable-verify">;
|
||||
def plugin_opt_dwo_dir_eq: J<"plugin-opt=dwo_dir=">,
|
||||
HelpText<"Directory to store .dwo files when LTO and debug fission are used">;
|
||||
def plugin_opt_emit_llvm: F<"plugin-opt=emit-llvm">;
|
||||
def: J<"plugin-opt=jobs=">, Alias<thinlto_jobs>, HelpText<"Alias for -thinlto-jobs">;
|
||||
def: J<"plugin-opt=lto-partitions=">, Alias<lto_partitions>, HelpText<"Alias for -lto-partitions">;
|
||||
def plugin_opt_mcpu_eq: J<"plugin-opt=mcpu=">;
|
||||
def: F<"plugin-opt=new-pass-manager">,
|
||||
Alias<lto_new_pass_manager>, HelpText<"Alias for -lto-new-pass-manager">;
|
||||
def plugin_opt_obj_path_eq: J<"plugin-opt=obj-path=">;
|
||||
def: F<"plugin-opt=cs-profile-generate">,
|
||||
Alias<lto_cs_profile_generate>, HelpText<"Alias for -lto-cs-profile-generate">;
|
||||
def: J<"plugin-opt=cs-profile-path=">,
|
||||
Alias<lto_cs_profile_file>, HelpText<"Alias for -lto-cs-profile-file">;
|
||||
def: J<"plugin-opt=sample-profile=">,
|
||||
Alias<lto_sample_profile>, HelpText<"Alias for -lto-sample-profile">;
|
||||
def: F<"plugin-opt=save-temps">, Alias<save_temps>, HelpText<"Alias for -save-temps">;
|
||||
def plugin_opt_thinlto_emit_imports_files: F<"plugin-opt=thinlto-emit-imports-files">;
|
||||
def plugin_opt_thinlto_index_only: F<"plugin-opt=thinlto-index-only">;
|
||||
def plugin_opt_thinlto_index_only_eq: J<"plugin-opt=thinlto-index-only=">;
|
||||
def plugin_opt_thinlto_object_suffix_replace_eq: J<"plugin-opt=thinlto-object-suffix-replace=">;
|
||||
def plugin_opt_thinlto_prefix_replace_eq: J<"plugin-opt=thinlto-prefix-replace=">;
|
||||
|
||||
// Ignore LTO plugin-related options.
|
||||
// clang -flto passes -plugin and -plugin-opt to the linker. This is required
|
||||
// for ld.gold and ld.bfd to get LTO working. But it's not for lld which doesn't
|
||||
// rely on a plugin. Instead of detecting which linker is used on clang side we
|
||||
// just ignore the option on lld side as it's easier. In fact, the linker could
|
||||
// be called 'ld' and understanding which linker is used would require parsing of
|
||||
// --version output.
|
||||
defm plugin: Eq<"plugin", "Ignored for compatibility with GNU linkers">;
|
||||
|
||||
def plugin_opt_fresolution_eq: J<"plugin-opt=-fresolution=">;
|
||||
def plugin_opt_pass_through_eq: J<"plugin-opt=-pass-through=">;
|
||||
def plugin_opt_thinlto: J<"plugin-opt=thinlto">;
|
||||
def plugin_opt_slash: J<"plugin-opt=/">;
|
||||
|
||||
// Options listed below are silently ignored for now for compatibility.
|
||||
def: F<"detect-odr-violations">;
|
||||
def: Flag<["-"], "g">;
|
||||
def: F<"long-plt">;
|
||||
def: F<"no-add-needed">;
|
||||
def: F<"no-copy-dt-needed-entries">;
|
||||
def: F<"no-ctors-in-init-array">;
|
||||
def: F<"no-keep-memory">;
|
||||
def: F<"no-mmap-output-file">;
|
||||
def: F<"no-pipeline-knowledge">;
|
||||
def: F<"no-warn-mismatch">;
|
||||
def: Flag<["-"], "p">;
|
||||
def: Separate<["--", "-"], "rpath-link">;
|
||||
def: J<"rpath-link=">;
|
||||
def: F<"secure-plt">;
|
||||
def: F<"sort-common">;
|
||||
def: F<"stats">;
|
||||
def: F<"warn-execstack">;
|
||||
def: F<"warn-once">;
|
||||
def: F<"warn-shared-textrel">;
|
||||
def: F<"EB">;
|
||||
def: F<"EL">;
|
||||
def: JoinedOrSeparate<["-"], "G">;
|
||||
def: F<"Qy">;
|
||||
|
||||
// Hidden option used for testing MIPS multi-GOT implementation.
|
||||
defm mips_got_size:
|
||||
Eq<"mips-got-size", "Max size of a single MIPS GOT. 0x10000 by default.">,
|
||||
Flags<[HelpHidden]>;
|
||||
421
deps/lld/ELF/OutputSections.cpp
vendored
421
deps/lld/ELF/OutputSections.cpp
vendored
@ -1,421 +0,0 @@
|
||||
//===- OutputSections.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 "OutputSections.h"
|
||||
#include "Config.h"
|
||||
#include "LinkerScript.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "lld/Common/Strings.h"
|
||||
#include "lld/Common/Threads.h"
|
||||
#include "llvm/BinaryFormat/Dwarf.h"
|
||||
#include "llvm/Support/Compression.h"
|
||||
#include "llvm/Support/MD5.h"
|
||||
#include "llvm/Support/MathExtras.h"
|
||||
#include "llvm/Support/SHA1.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::dwarf;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
uint8_t *Out::bufferStart;
|
||||
uint8_t Out::first;
|
||||
PhdrEntry *Out::tlsPhdr;
|
||||
OutputSection *Out::elfHeader;
|
||||
OutputSection *Out::programHeaders;
|
||||
OutputSection *Out::preinitArray;
|
||||
OutputSection *Out::initArray;
|
||||
OutputSection *Out::finiArray;
|
||||
|
||||
std::vector<OutputSection *> elf::outputSections;
|
||||
|
||||
uint32_t OutputSection::getPhdrFlags() const {
|
||||
uint32_t ret = 0;
|
||||
if (config->emachine != EM_ARM || !(flags & SHF_ARM_PURECODE))
|
||||
ret |= PF_R;
|
||||
if (flags & SHF_WRITE)
|
||||
ret |= PF_W;
|
||||
if (flags & SHF_EXECINSTR)
|
||||
ret |= PF_X;
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void OutputSection::writeHeaderTo(typename ELFT::Shdr *shdr) {
|
||||
shdr->sh_entsize = entsize;
|
||||
shdr->sh_addralign = alignment;
|
||||
shdr->sh_type = type;
|
||||
shdr->sh_offset = offset;
|
||||
shdr->sh_flags = flags;
|
||||
shdr->sh_info = info;
|
||||
shdr->sh_link = link;
|
||||
shdr->sh_addr = addr;
|
||||
shdr->sh_size = size;
|
||||
shdr->sh_name = shName;
|
||||
}
|
||||
|
||||
OutputSection::OutputSection(StringRef name, uint32_t type, uint64_t flags)
|
||||
: BaseCommand(OutputSectionKind),
|
||||
SectionBase(Output, name, flags, /*Entsize*/ 0, /*Alignment*/ 1, type,
|
||||
/*Info*/ 0, /*Link*/ 0) {}
|
||||
|
||||
// We allow sections of types listed below to merged into a
|
||||
// single progbits section. This is typically done by linker
|
||||
// scripts. Merging nobits and progbits will force disk space
|
||||
// to be allocated for nobits sections. Other ones don't require
|
||||
// any special treatment on top of progbits, so there doesn't
|
||||
// seem to be a harm in merging them.
|
||||
static bool canMergeToProgbits(unsigned type) {
|
||||
return type == SHT_NOBITS || type == SHT_PROGBITS || type == SHT_INIT_ARRAY ||
|
||||
type == SHT_PREINIT_ARRAY || type == SHT_FINI_ARRAY ||
|
||||
type == SHT_NOTE;
|
||||
}
|
||||
|
||||
void OutputSection::addSection(InputSection *isec) {
|
||||
if (!hasInputSections) {
|
||||
// If IS is the first section to be added to this section,
|
||||
// initialize Partition, Type, Entsize and flags from IS.
|
||||
hasInputSections = true;
|
||||
partition = isec->partition;
|
||||
type = isec->type;
|
||||
entsize = isec->entsize;
|
||||
flags = isec->flags;
|
||||
} else {
|
||||
// Otherwise, check if new type or flags are compatible with existing ones.
|
||||
unsigned mask = SHF_TLS | SHF_LINK_ORDER;
|
||||
if ((flags & mask) != (isec->flags & mask))
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
isec->parent = this;
|
||||
uint64_t andMask =
|
||||
config->emachine == EM_ARM ? (uint64_t)SHF_ARM_PURECODE : 0;
|
||||
uint64_t orMask = ~andMask;
|
||||
uint64_t andFlags = (flags & isec->flags) & andMask;
|
||||
uint64_t orFlags = (flags | isec->flags) & orMask;
|
||||
flags = andFlags | orFlags;
|
||||
|
||||
alignment = std::max(alignment, isec->alignment);
|
||||
|
||||
// If this section contains a table of fixed-size entries, sh_entsize
|
||||
// holds the element size. If it contains elements of different size we
|
||||
// set sh_entsize to 0.
|
||||
if (entsize != isec->entsize)
|
||||
entsize = 0;
|
||||
|
||||
if (!isec->assigned) {
|
||||
isec->assigned = true;
|
||||
if (sectionCommands.empty() ||
|
||||
!isa<InputSectionDescription>(sectionCommands.back()))
|
||||
sectionCommands.push_back(make<InputSectionDescription>(""));
|
||||
auto *isd = cast<InputSectionDescription>(sectionCommands.back());
|
||||
isd->sections.push_back(isec);
|
||||
}
|
||||
}
|
||||
|
||||
static void sortByOrder(MutableArrayRef<InputSection *> in,
|
||||
llvm::function_ref<int(InputSectionBase *s)> order) {
|
||||
std::vector<std::pair<int, InputSection *>> v;
|
||||
for (InputSection *s : in)
|
||||
v.push_back({order(s), s});
|
||||
llvm::stable_sort(v, less_first());
|
||||
|
||||
for (size_t i = 0; i < v.size(); ++i)
|
||||
in[i] = v[i].second;
|
||||
}
|
||||
|
||||
uint64_t elf::getHeaderSize() {
|
||||
if (config->oFormatBinary)
|
||||
return 0;
|
||||
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)
|
||||
if (auto *isd = dyn_cast<InputSectionDescription>(b))
|
||||
sortByOrder(isd->sections, order);
|
||||
}
|
||||
|
||||
// Fill [Buf, Buf + Size) with Filler.
|
||||
// This is used for linker script "=fillexp" command.
|
||||
static void fill(uint8_t *buf, size_t size,
|
||||
const std::array<uint8_t, 4> &filler) {
|
||||
size_t i = 0;
|
||||
for (; i + 4 < size; i += 4)
|
||||
memcpy(buf + i, filler.data(), 4);
|
||||
memcpy(buf + i, filler.data(), size - i);
|
||||
}
|
||||
|
||||
// Compress section contents if this section contains debug info.
|
||||
template <class ELFT> void OutputSection::maybeCompress() {
|
||||
using Elf_Chdr = typename ELFT::Chdr;
|
||||
|
||||
// Compress only DWARF debug sections.
|
||||
if (!config->compressDebugSections || (flags & SHF_ALLOC) ||
|
||||
!name.startswith(".debug_"))
|
||||
return;
|
||||
|
||||
// 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());
|
||||
if (Error e = zlib::compress(toStringRef(buf), compressedData))
|
||||
fatal("compress failed: " + llvm::toString(std::move(e)));
|
||||
|
||||
// Update section headers.
|
||||
size = sizeof(Elf_Chdr) + compressedData.size();
|
||||
flags |= SHF_COMPRESSED;
|
||||
}
|
||||
|
||||
static void writeInt(uint8_t *buf, uint64_t data, uint64_t size) {
|
||||
if (size == 1)
|
||||
*buf = data;
|
||||
else if (size == 2)
|
||||
write16(buf, data);
|
||||
else if (size == 4)
|
||||
write32(buf, data);
|
||||
else if (size == 8)
|
||||
write64(buf, data);
|
||||
else
|
||||
llvm_unreachable("unsupported Size argument");
|
||||
}
|
||||
|
||||
template <class ELFT> void OutputSection::writeTo(uint8_t *buf) {
|
||||
if (type == SHT_NOBITS)
|
||||
return;
|
||||
|
||||
// If -compress-debug-section is specified and if this is a debug seciton,
|
||||
// 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());
|
||||
return;
|
||||
}
|
||||
|
||||
// Write leading padding.
|
||||
std::vector<InputSection *> sections = getInputSections(this);
|
||||
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;
|
||||
else
|
||||
end = buf + sections[i + 1]->outSecOff;
|
||||
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))
|
||||
writeInt(buf + data->offset, data->expression().getValue(), data->size);
|
||||
}
|
||||
|
||||
static void finalizeShtGroup(OutputSection *os,
|
||||
InputSection *section) {
|
||||
assert(config->relocatable);
|
||||
|
||||
// sh_link field for SHT_GROUP sections should contain the section index of
|
||||
// the symbol table.
|
||||
os->link = in.symTab->getParent()->sectionIndex;
|
||||
|
||||
// sh_info then contain index of an entry in symbol table section which
|
||||
// provides signature of the section group.
|
||||
ArrayRef<Symbol *> symbols = section->file->getSymbols();
|
||||
os->info = in.symTab->getSymbolIndex(symbols[section->info]);
|
||||
}
|
||||
|
||||
void OutputSection::finalize() {
|
||||
std::vector<InputSection *> v = getInputSections(this);
|
||||
InputSection *first = v.empty() ? nullptr : v[0];
|
||||
|
||||
if (flags & SHF_LINK_ORDER) {
|
||||
// We must preserve the link order dependency of sections with the
|
||||
// SHF_LINK_ORDER flag. The dependency is indicated by the sh_link field. We
|
||||
// need to translate the InputSection sh_link to the OutputSection sh_link,
|
||||
// all InputSections in the OutputSection have the same dependency.
|
||||
if (auto *ex = dyn_cast<ARMExidxSyntheticSection>(first))
|
||||
link = ex->getLinkOrderDep()->getParent()->sectionIndex;
|
||||
else if (auto *d = first->getLinkOrderDep())
|
||||
link = d->getParent()->sectionIndex;
|
||||
}
|
||||
|
||||
if (type == SHT_GROUP) {
|
||||
finalizeShtGroup(this, first);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!config->copyRelocs || (type != SHT_RELA && type != SHT_REL))
|
||||
return;
|
||||
|
||||
if (isa<SyntheticSection>(first))
|
||||
return;
|
||||
|
||||
link = in.symTab->getParent()->sectionIndex;
|
||||
// sh_info for SHT_REL[A] sections should contain the section header index of
|
||||
// the section to which the relocation applies.
|
||||
InputSectionBase *s = first->getRelocatedSection();
|
||||
info = s->getOutputSection()->sectionIndex;
|
||||
flags |= SHF_INFO_LINK;
|
||||
}
|
||||
|
||||
// Returns true if S matches /Filename.?\.o$/.
|
||||
static bool isCrtBeginEnd(StringRef s, StringRef filename) {
|
||||
if (!s.endswith(".o"))
|
||||
return false;
|
||||
s = s.drop_back(2);
|
||||
if (s.endswith(filename))
|
||||
return true;
|
||||
return !s.empty() && s.drop_back().endswith(filename);
|
||||
}
|
||||
|
||||
static bool isCrtbegin(StringRef s) { return isCrtBeginEnd(s, "crtbegin"); }
|
||||
static bool isCrtend(StringRef s) { return isCrtBeginEnd(s, "crtend"); }
|
||||
|
||||
// .ctors and .dtors are sorted by this priority from highest to lowest.
|
||||
//
|
||||
// 1. The section was contained in crtbegin (crtbegin contains
|
||||
// some sentinel value in its .ctors and .dtors so that the runtime
|
||||
// can find the beginning of the sections.)
|
||||
//
|
||||
// 2. The section has an optional priority value in the form of ".ctors.N"
|
||||
// or ".dtors.N" where N is a number. Unlike .{init,fini}_array,
|
||||
// they are compared as string rather than number.
|
||||
//
|
||||
// 3. The section is just ".ctors" or ".dtors".
|
||||
//
|
||||
// 4. The section was contained in crtend, which contains an end marker.
|
||||
//
|
||||
// In an ideal world, we don't need this function because .init_array and
|
||||
// .ctors are duplicate features (and .init_array is newer.) However, there
|
||||
// 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());
|
||||
if (beginA != beginB)
|
||||
return beginA;
|
||||
bool endA = isCrtend(a->file->getName());
|
||||
bool endB = isCrtend(b->file->getName());
|
||||
if (endA != endB)
|
||||
return endB;
|
||||
StringRef x = a->name;
|
||||
StringRef y = b->name;
|
||||
assert(x.startswith(".ctors") || x.startswith(".dtors"));
|
||||
assert(y.startswith(".ctors") || y.startswith(".dtors"));
|
||||
x = x.substr(6);
|
||||
y = y.substr(6);
|
||||
return x < y;
|
||||
}
|
||||
|
||||
// Sorts input sections by the special rules for .ctors and .dtors.
|
||||
// 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]);
|
||||
llvm::stable_sort(isd->sections, compCtors);
|
||||
}
|
||||
|
||||
// If an input string is in the form of "foo.N" where N is a number,
|
||||
// return N. Otherwise, returns 65536, which is one greater than the
|
||||
// lowest priority.
|
||||
int elf::getPriority(StringRef s) {
|
||||
size_t pos = s.rfind('.');
|
||||
if (pos == StringRef::npos)
|
||||
return 65536;
|
||||
int v;
|
||||
if (!to_integer(s.substr(pos + 1), v, 10))
|
||||
return 65536;
|
||||
return v;
|
||||
}
|
||||
|
||||
std::vector<InputSection *> elf::getInputSections(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;
|
||||
}
|
||||
|
||||
// Sorts input sections by section name suffixes, so that .foo.N comes
|
||||
// before .foo.M if N < M. Used to sort .{init,fini}_array.N sections.
|
||||
// We want to keep the original order if the priorities are the same
|
||||
// because the compiler keeps the original initialization order in a
|
||||
// translation unit and we need to respect that.
|
||||
// For more detail, read the section of the GCC's manual about init_priority.
|
||||
void OutputSection::sortInitFini() {
|
||||
// Sort sections by priority.
|
||||
sort([](InputSectionBase *s) { return getPriority(s->name); });
|
||||
}
|
||||
|
||||
std::array<uint8_t, 4> OutputSection::getFiller() {
|
||||
if (filler)
|
||||
return *filler;
|
||||
if (flags & SHF_EXECINSTR)
|
||||
return target->trapInstr;
|
||||
return {0, 0, 0, 0};
|
||||
}
|
||||
|
||||
template void OutputSection::writeHeaderTo<ELF32LE>(ELF32LE::Shdr *Shdr);
|
||||
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::maybeCompress<ELF32LE>();
|
||||
template void OutputSection::maybeCompress<ELF32BE>();
|
||||
template void OutputSection::maybeCompress<ELF64LE>();
|
||||
template void OutputSection::maybeCompress<ELF64BE>();
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user