remove embedded LLD

we no longer have any patches against upstream LLD
This commit is contained in:
Andrew Kelley 2020-01-16 13:09:45 -05:00
parent fbe6af81fd
commit ba4cc03b4f
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
2988 changed files with 8 additions and 217692 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 ""

View File

@ -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
View File

@ -1,4 +0,0 @@
{
"repository.callsign" : "LLD",
"conduit_uri" : "https://reviews.llvm.org/"
}

View File

@ -1 +0,0 @@
BasedOnStyle: LLVM

24
deps/lld/.gitignore vendored
View File

@ -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

View File

@ -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)

View File

@ -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/*)

View File

@ -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}
)

View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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

View File

@ -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());
}

View File

@ -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

File diff suppressed because it is too large Load Diff

202
deps/lld/COFF/Driver.h vendored
View File

@ -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

View File

@ -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
View File

@ -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
View File

@ -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

View File

@ -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();
}

View File

@ -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
View File

@ -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
View File

@ -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

View File

@ -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';
}
}
}

View File

@ -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

View File

@ -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);
}
}
}
}

View File

@ -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

View File

@ -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
View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

37
deps/lld/COFF/PDB.h vendored
View File

@ -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

View File

@ -1 +0,0 @@
See docs/NewLLD.rst

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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);
}

View File

@ -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}
)

View File

@ -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);
}

View File

@ -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());
}

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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; }

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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
}

View File

@ -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;
}

View File

@ -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

View File

@ -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(); }

View File

@ -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 &target;
}

View File

@ -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 &target;
}

View File

@ -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 &target;
}

View File

@ -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 &target;
}

View File

@ -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 &target;
}

View File

@ -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 &target;
}
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 *);

View File

@ -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>();

View File

@ -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 &target;
}

File diff suppressed because it is too large Load Diff

View File

@ -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 &target;
}

View File

@ -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 &target;
}

View File

@ -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;
}

View File

@ -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(); }

View File

@ -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}
)

View File

@ -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();
}

View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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

File diff suppressed because it is too large Load Diff

77
deps/lld/ELF/Driver.h vendored
View File

@ -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

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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
View File

@ -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
View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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
View File

@ -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
View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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));
}
}

View File

@ -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

View File

@ -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>();

View File

@ -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

View File

@ -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]>;

View File

@ -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