mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
embed LLD 5.0.0
This is 48aaa9f66120f72b5d7e4a90b32fee40a3386ce9 from the git monorepo.
This commit is contained in:
parent
ebdc6b594d
commit
4794281d64
4
deps/lld/.arcconfig
vendored
Normal file
4
deps/lld/.arcconfig
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"project_id" : "lld",
|
||||
"conduit_uri" : "https://reviews.llvm.org/"
|
||||
}
|
||||
1
deps/lld/.clang-format
vendored
Normal file
1
deps/lld/.clang-format
vendored
Normal file
@ -0,0 +1 @@
|
||||
BasedOnStyle: LLVM
|
||||
24
deps/lld/.gitignore
vendored
Normal file
24
deps/lld/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
#==============================================================================#
|
||||
# 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
|
||||
224
deps/lld/CMakeLists.txt
vendored
Normal file
224
deps/lld/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,224 @@
|
||||
# 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)
|
||||
set(Python_ADDITIONAL_VERSIONS 2.7)
|
||||
include(FindPythonInterp)
|
||||
if(NOT PYTHONINTERP_FOUND)
|
||||
message(FATAL_ERROR
|
||||
"Unable to find Python interpreter, required for testing.
|
||||
|
||||
Please install Python or specify the PYTHON_EXECUTABLE CMake variable.")
|
||||
endif()
|
||||
|
||||
if(${PYTHON_VERSION_STRING} VERSION_LESS 2.7)
|
||||
message(FATAL_ERROR "Python 2.7 or newer is required")
|
||||
endif()
|
||||
|
||||
# Check prebuilt llvm/utils.
|
||||
if(EXISTS ${LLVM_TOOLS_BINARY_DIR}/FileCheck${CMAKE_EXECUTABLE_SUFFIX}
|
||||
AND EXISTS ${LLVM_TOOLS_BINARY_DIR}/not${CMAKE_EXECUTABLE_SUFFIX})
|
||||
set(LLVM_UTILS_PROVIDED ON)
|
||||
endif()
|
||||
|
||||
if(EXISTS ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py)
|
||||
# Note: path not really used, except for checking if lit was found
|
||||
set(LLVM_LIT ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py)
|
||||
if(NOT LLVM_UTILS_PROVIDED)
|
||||
add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/FileCheck utils/FileCheck)
|
||||
add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/not utils/not)
|
||||
set(LLVM_UTILS_PROVIDED ON)
|
||||
set(LLD_TEST_DEPS FileCheck not)
|
||||
endif()
|
||||
set(UNITTEST_DIR ${LLVM_MAIN_SRC_DIR}/utils/unittest)
|
||||
if(EXISTS ${UNITTEST_DIR}/googletest/include/gtest/gtest.h
|
||||
AND NOT EXISTS ${LLVM_LIBRARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}
|
||||
AND EXISTS ${UNITTEST_DIR}/CMakeLists.txt)
|
||||
add_subdirectory(${UNITTEST_DIR} utils/unittest)
|
||||
endif()
|
||||
else()
|
||||
# Seek installed Lit.
|
||||
find_program(LLVM_LIT
|
||||
NAMES llvm-lit lit.py lit
|
||||
PATHS "${LLVM_MAIN_SRC_DIR}/utils/lit"
|
||||
DOC "Path to lit.py")
|
||||
endif()
|
||||
|
||||
if(LLVM_LIT)
|
||||
# Define the default arguments to use with 'lit', and an option for the user
|
||||
# to override.
|
||||
set(LIT_ARGS_DEFAULT "-sv")
|
||||
if (MSVC OR XCODE)
|
||||
set(LIT_ARGS_DEFAULT "${LIT_ARGS_DEFAULT} --no-progress-bar")
|
||||
endif()
|
||||
set(LLVM_LIT_ARGS "${LIT_ARGS_DEFAULT}" CACHE STRING "Default options for lit")
|
||||
|
||||
# On Win32 hosts, provide an option to specify the path to the GnuWin32 tools.
|
||||
if(WIN32 AND NOT CYGWIN)
|
||||
set(LLVM_LIT_TOOLS_DIR "" CACHE PATH "Path to GnuWin32 tools")
|
||||
endif()
|
||||
else()
|
||||
set(LLVM_INCLUDE_TESTS OFF)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(LLD_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
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/Config/Version.inc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/include/lld/Config/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(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)
|
||||
|
||||
19
deps/lld/CODE_OWNERS.TXT
vendored
Normal file
19
deps/lld/CODE_OWNERS.TXT
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
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
|
||||
|
||||
50
deps/lld/COFF/CMakeLists.txt
vendored
Normal file
50
deps/lld/COFF/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
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
|
||||
DLL.cpp
|
||||
Driver.cpp
|
||||
DriverUtils.cpp
|
||||
Error.cpp
|
||||
ICF.cpp
|
||||
InputFiles.cpp
|
||||
LTO.cpp
|
||||
MapFile.cpp
|
||||
MarkLive.cpp
|
||||
PDB.cpp
|
||||
Strings.cpp
|
||||
SymbolTable.cpp
|
||||
Symbols.cpp
|
||||
Writer.cpp
|
||||
|
||||
LINK_COMPONENTS
|
||||
${LLVM_TARGETS_TO_BUILD}
|
||||
BinaryFormat
|
||||
BitReader
|
||||
Core
|
||||
DebugInfoCodeView
|
||||
DebugInfoMSF
|
||||
DebugInfoPDB
|
||||
LTO
|
||||
LibDriver
|
||||
Object
|
||||
MC
|
||||
MCDisassembler
|
||||
Target
|
||||
Option
|
||||
Support
|
||||
|
||||
LINK_LIBS
|
||||
lldCore
|
||||
${LLVM_PTHREAD_LIB}
|
||||
|
||||
DEPENDS
|
||||
COFFOptionsTableGen
|
||||
${tablegen_deps}
|
||||
)
|
||||
500
deps/lld/COFF/Chunks.cpp
vendored
Normal file
500
deps/lld/COFF/Chunks.cpp
vendored
Normal file
@ -0,0 +1,500 @@
|
||||
//===- Chunks.cpp ---------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Chunks.h"
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "Writer.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(ObjectFile *F, const coff_section *H)
|
||||
: Chunk(SectionKind), Repl(this), Header(H), File(F),
|
||||
Relocs(File->getCOFFObj()->getRelocations(Header)),
|
||||
NumRelocs(std::distance(Relocs.begin(), Relocs.end())) {
|
||||
// Initialize SectionName.
|
||||
File->getCOFFObj()->getSectionName(Header, SectionName);
|
||||
|
||||
Align = Header->getAlignment();
|
||||
|
||||
// Chunks may be discarded during comdat merging.
|
||||
Discarded = false;
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
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); }
|
||||
|
||||
static void applySecRel(const SectionChunk *Sec, uint8_t *Off,
|
||||
OutputSection *OS, uint64_t S) {
|
||||
if (!OS) {
|
||||
if (Sec->isCodeView())
|
||||
return;
|
||||
fatal("SECREL relocation cannot be applied to absolute symbols");
|
||||
}
|
||||
uint64_t SecRel = S - OS->getRVA();
|
||||
assert(SecRel < INT32_MAX && "overflow in SECREL relocation");
|
||||
add32(Off, SecRel);
|
||||
}
|
||||
|
||||
static void applySecIdx(uint8_t *Off, OutputSection *OS) {
|
||||
// If we have no output section, this must be an absolute symbol. Use the
|
||||
// sentinel absolute symbol section index.
|
||||
uint16_t SecIdx = OS ? OS->SectionIndex : DefinedAbsolute::OutputSectionIndex;
|
||||
add16(Off, SecIdx);
|
||||
}
|
||||
|
||||
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:
|
||||
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
|
||||
}
|
||||
}
|
||||
|
||||
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:
|
||||
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
uint16_t Opcode1 = read16le(Off);
|
||||
uint16_t Opcode2 = read16le(Off + 2);
|
||||
uint16_t Imm = (Opcode2 & 0x00ff) | ((Opcode2 >> 4) & 0x0700);
|
||||
Imm |= ((Opcode1 << 1) & 0x0800) | ((Opcode1 & 0x000f) << 12);
|
||||
return Imm;
|
||||
}
|
||||
|
||||
static void applyMOV32T(uint8_t *Off, uint32_t V) {
|
||||
uint16_t ImmW = readMOV(Off); // read MOVW operand
|
||||
uint16_t ImmT = readMOV(Off + 4); // read MOVT operand
|
||||
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) {
|
||||
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));
|
||||
}
|
||||
|
||||
static void applyBranch24T(uint8_t *Off, int32_t V) {
|
||||
if (!isInt<25>(V))
|
||||
fatal("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->getPermissions() & 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;
|
||||
default:
|
||||
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
|
||||
}
|
||||
}
|
||||
|
||||
static void applyArm64Addr(uint8_t *Off, uint64_t Imm) {
|
||||
uint32_t ImmLo = (Imm & 0x3) << 29;
|
||||
uint32_t ImmHi = (Imm & 0x1FFFFC) << 3;
|
||||
uint64_t Mask = (0x3 << 29) | (0x1FFFFC << 3);
|
||||
write32le(Off, (read32le(Off) & ~Mask) | ImmLo | ImmHi);
|
||||
}
|
||||
|
||||
// Update the immediate field in a AARCH64 ldr, str, and add instruction.
|
||||
static void applyArm64Imm(uint8_t *Off, uint64_t Imm) {
|
||||
uint32_t Orig = read32le(Off);
|
||||
Imm += (Orig >> 10) & 0xFFF;
|
||||
Orig &= ~(0xFFF << 10);
|
||||
write32le(Off, Orig | ((Imm & 0xFFF) << 10));
|
||||
}
|
||||
|
||||
static void applyArm64Ldr(uint8_t *Off, uint64_t Imm) {
|
||||
int Size = read32le(Off) >> 30;
|
||||
Imm >>= Size;
|
||||
applyArm64Imm(Off, Imm);
|
||||
}
|
||||
|
||||
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 >> 12) - (P >> 12)); break;
|
||||
case IMAGE_REL_ARM64_PAGEOFFSET_12A: applyArm64Imm(Off, S & 0xfff); break;
|
||||
case IMAGE_REL_ARM64_PAGEOFFSET_12L: applyArm64Ldr(Off, S & 0xfff); break;
|
||||
case IMAGE_REL_ARM64_BRANCH26: or32(Off, ((S - P) & 0x0FFFFFFC) >> 2); break;
|
||||
case IMAGE_REL_ARM64_ADDR32: add32(Off, S + Config->ImageBase); break;
|
||||
case IMAGE_REL_ARM64_ADDR64: add64(Off, S + Config->ImageBase); break;
|
||||
default:
|
||||
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
memcpy(Buf + OutputSectionOff, A.data(), A.size());
|
||||
|
||||
// Apply relocations.
|
||||
size_t InputSize = getSize();
|
||||
for (const coff_relocation &Rel : Relocs) {
|
||||
// Check for an invalid relocation offset. This check isn't perfect, because
|
||||
// we don't have the relocation size, which is only known after checking the
|
||||
// machine and relocation type. As a result, a relocation may overwrite the
|
||||
// beginning of the following input section.
|
||||
if (Rel.VirtualAddress >= InputSize)
|
||||
fatal("relocation points beyond the end of its parent section");
|
||||
|
||||
uint8_t *Off = Buf + OutputSectionOff + Rel.VirtualAddress;
|
||||
|
||||
// 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.
|
||||
SymbolBody *Body = File->getSymbolBody(Rel.SymbolTableIndex);
|
||||
Defined *Sym = cast<Defined>(Body);
|
||||
Chunk *C = Sym->getChunk();
|
||||
OutputSection *OS = C ? C->getOutputSection() : nullptr;
|
||||
|
||||
// Only absolute and __ImageBase symbols lack an output section. For any
|
||||
// other symbol, this indicates that the chunk was discarded. Normally
|
||||
// relocations against discarded sections are an error. However, debug info
|
||||
// sections are not GC roots and can end up with these kinds of relocations.
|
||||
// Skip these relocations.
|
||||
if (!OS && !isa<DefinedAbsolute>(Sym) && !isa<DefinedSynthetic>(Sym)) {
|
||||
if (isCodeView() || isDWARF())
|
||||
continue;
|
||||
fatal("relocation against symbol in discarded section: " +
|
||||
Sym->getName());
|
||||
}
|
||||
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) {
|
||||
AssocChildren.push_back(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 (const coff_relocation &Rel : Relocs) {
|
||||
uint8_t Ty = getBaserelType(Rel);
|
||||
if (Ty == IMAGE_REL_BASED_ABSOLUTE)
|
||||
continue;
|
||||
SymbolBody *Body = File->getSymbolBody(Rel.SymbolTableIndex);
|
||||
if (isa<DefinedAbsolute>(Body))
|
||||
continue;
|
||||
Res->emplace_back(RVA + Rel.VirtualAddress, Ty);
|
||||
}
|
||||
}
|
||||
|
||||
bool SectionChunk::hasData() const {
|
||||
return !(Header->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA);
|
||||
}
|
||||
|
||||
uint32_t SectionChunk::getPermissions() const {
|
||||
return Header->Characteristics & PermMask;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (Discarded)
|
||||
message("Discarded comdat symbol " + Sym->getName());
|
||||
else if (!Live)
|
||||
message("Discarded " + Sym->getName());
|
||||
}
|
||||
}
|
||||
|
||||
StringRef SectionChunk::getDebugName() {
|
||||
if (Sym)
|
||||
return Sym->getName();
|
||||
return "";
|
||||
}
|
||||
|
||||
ArrayRef<uint8_t> SectionChunk::getContents() const {
|
||||
ArrayRef<uint8_t> A;
|
||||
File->getCOFFObj()->getSectionContents(Header, A);
|
||||
return A;
|
||||
}
|
||||
|
||||
void SectionChunk::replace(SectionChunk *Other) {
|
||||
Other->Repl = Repl;
|
||||
Other->Live = false;
|
||||
}
|
||||
|
||||
CommonChunk::CommonChunk(const COFFSymbolRef S) : Sym(S) {
|
||||
// Common symbols are aligned on natural boundaries up to 32 bytes.
|
||||
// This is what MSVC link.exe does.
|
||||
Align = std::min(uint64_t(32), PowerOf2Ceil(Sym.getValue()));
|
||||
}
|
||||
|
||||
uint32_t CommonChunk::getPermissions() const {
|
||||
return IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_MEM_READ |
|
||||
IMAGE_SCN_MEM_WRITE;
|
||||
}
|
||||
|
||||
void StringChunk::writeTo(uint8_t *Buf) const {
|
||||
memcpy(Buf + OutputSectionOff, Str.data(), Str.size());
|
||||
}
|
||||
|
||||
ImportThunkChunkX64::ImportThunkChunkX64(Defined *S) : ImpSymbol(S) {
|
||||
// Intel Optimization Manual says that all branch targets
|
||||
// should be 16-byte aligned. MSVC linker does this too.
|
||||
Align = 16;
|
||||
}
|
||||
|
||||
void ImportThunkChunkX64::writeTo(uint8_t *Buf) const {
|
||||
memcpy(Buf + OutputSectionOff, ImportThunkX86, sizeof(ImportThunkX86));
|
||||
// The first two bytes is a JMP instruction. Fill its operand.
|
||||
write32le(Buf + OutputSectionOff + 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 + OutputSectionOff, ImportThunkX86, sizeof(ImportThunkX86));
|
||||
// The first two bytes is a JMP instruction. Fill its operand.
|
||||
write32le(Buf + OutputSectionOff + 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 + OutputSectionOff, ImportThunkARM, sizeof(ImportThunkARM));
|
||||
// Fix mov.w and mov.t operands.
|
||||
applyMOV32T(Buf + OutputSectionOff, ImpSymbol->getRVA() + Config->ImageBase);
|
||||
}
|
||||
|
||||
void ImportThunkChunkARM64::writeTo(uint8_t *Buf) const {
|
||||
int64_t PageOff = (ImpSymbol->getRVA() >> 12) - (RVA >> 12);
|
||||
int64_t Off = ImpSymbol->getRVA() & 0xfff;
|
||||
memcpy(Buf + OutputSectionOff, ImportThunkARM64, sizeof(ImportThunkARM64));
|
||||
applyArm64Addr(Buf + OutputSectionOff, PageOff);
|
||||
applyArm64Ldr(Buf + OutputSectionOff + 4, Off);
|
||||
}
|
||||
|
||||
void LocalImportChunk::getBaserels(std::vector<Baserel> *Res) {
|
||||
Res->emplace_back(getRVA());
|
||||
}
|
||||
|
||||
size_t LocalImportChunk::getSize() const {
|
||||
return Config->is64() ? 8 : 4;
|
||||
}
|
||||
|
||||
void LocalImportChunk::writeTo(uint8_t *Buf) const {
|
||||
if (Config->is64()) {
|
||||
write64le(Buf + OutputSectionOff, Sym->getRVA() + Config->ImageBase);
|
||||
} else {
|
||||
write32le(Buf + OutputSectionOff, Sym->getRVA() + Config->ImageBase);
|
||||
}
|
||||
}
|
||||
|
||||
void SEHTableChunk::writeTo(uint8_t *Buf) const {
|
||||
ulittle32_t *Begin = reinterpret_cast<ulittle32_t *>(Buf + OutputSectionOff);
|
||||
size_t Cnt = 0;
|
||||
for (Defined *D : Syms)
|
||||
Begin[Cnt++] = D->getRVA();
|
||||
std::sort(Begin, Begin + Cnt);
|
||||
}
|
||||
|
||||
// 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 + OutputSectionOff, Data.data(), Data.size());
|
||||
}
|
||||
|
||||
uint8_t Baserel::getDefaultType() {
|
||||
switch (Config->Machine) {
|
||||
case AMD64:
|
||||
return IMAGE_REL_BASED_DIR64;
|
||||
case I386:
|
||||
return IMAGE_REL_BASED_HIGHLOW;
|
||||
default:
|
||||
llvm_unreachable("unknown machine type");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
375
deps/lld/COFF/Chunks.h
vendored
Normal file
375
deps/lld/COFF/Chunks.h
vendored
Normal file
@ -0,0 +1,375 @@
|
||||
//===- Chunks.h -------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_COFF_CHUNKS_H
|
||||
#define LLD_COFF_CHUNKS_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "InputFiles.h"
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/iterator.h"
|
||||
#include "llvm/ADT/iterator_range.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 ObjectFile;
|
||||
class OutputSection;
|
||||
class SymbolBody;
|
||||
|
||||
// Mask for section types (code, data, bss, disacardable, etc.)
|
||||
// and permissions (writable, readable or executable).
|
||||
const uint32_t PermMask = 0xFF0000F0;
|
||||
|
||||
// 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 { SectionKind, OtherKind };
|
||||
Kind kind() const { return ChunkKind; }
|
||||
virtual ~Chunk() = default;
|
||||
|
||||
// Returns the size of this chunk (even if this is a common or BSS.)
|
||||
virtual size_t getSize() const = 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 {}
|
||||
|
||||
// The writer sets and uses the addresses.
|
||||
uint64_t getRVA() const { return RVA; }
|
||||
uint32_t getAlign() const { return Align; }
|
||||
void setRVA(uint64_t V) { RVA = V; }
|
||||
|
||||
// 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.
|
||||
virtual bool hasData() const { return true; }
|
||||
|
||||
// Returns readable/writable/executable bits.
|
||||
virtual uint32_t getPermissions() const { return 0; }
|
||||
|
||||
// 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");
|
||||
}
|
||||
|
||||
// An output section has pointers to chunks in the section, and each
|
||||
// chunk has a back pointer to an output section.
|
||||
void setOutputSection(OutputSection *O) { Out = O; }
|
||||
OutputSection *getOutputSection() { return Out; }
|
||||
|
||||
// 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() { return ""; }
|
||||
|
||||
protected:
|
||||
Chunk(Kind K = OtherKind) : ChunkKind(K) {}
|
||||
const Kind ChunkKind;
|
||||
|
||||
// The alignment of this chunk. The writer uses the value.
|
||||
uint32_t Align = 1;
|
||||
|
||||
// The RVA of this chunk in the output. The writer sets a value.
|
||||
uint64_t RVA = 0;
|
||||
|
||||
public:
|
||||
// The offset from beginning of the output section. The writer sets a value.
|
||||
uint64_t OutputSectionOff = 0;
|
||||
|
||||
protected:
|
||||
// The output section for this chunk.
|
||||
OutputSection *Out = nullptr;
|
||||
};
|
||||
|
||||
// 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, SymbolBody *> {
|
||||
friend SectionChunk;
|
||||
|
||||
ObjectFile *File;
|
||||
|
||||
symbol_iterator(ObjectFile *File, const coff_relocation *I)
|
||||
: symbol_iterator::iterator_adaptor_base(I), File(File) {}
|
||||
|
||||
public:
|
||||
symbol_iterator() = default;
|
||||
|
||||
SymbolBody *operator*() const {
|
||||
return File->getSymbolBody(I->SymbolTableIndex);
|
||||
}
|
||||
};
|
||||
|
||||
SectionChunk(ObjectFile *File, const coff_section *Header);
|
||||
static bool classof(const Chunk *C) { return C->kind() == SectionKind; }
|
||||
size_t getSize() const override { return Header->SizeOfRawData; }
|
||||
ArrayRef<uint8_t> getContents() const;
|
||||
void writeTo(uint8_t *Buf) const override;
|
||||
bool hasData() const override;
|
||||
uint32_t getPermissions() const override;
|
||||
StringRef getSectionName() const override { return SectionName; }
|
||||
void getBaserels(std::vector<Baserel> *Res) override;
|
||||
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;
|
||||
|
||||
// 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() override;
|
||||
void setSymbol(DefinedRegular *S) { if (!Sym) Sym = S; }
|
||||
|
||||
// Returns true if the chunk was not dropped by GC or COMDAT deduplication.
|
||||
bool isLive() { return Live && !Discarded; }
|
||||
|
||||
// Used by the garbage collector.
|
||||
void markLive() {
|
||||
assert(Config->DoGC && "should only mark things live from GC");
|
||||
assert(!isLive() && "Cannot mark an already live section!");
|
||||
Live = true;
|
||||
}
|
||||
|
||||
// Returns true if this chunk was dropped by COMDAT deduplication.
|
||||
bool isDiscarded() const { return Discarded; }
|
||||
|
||||
// Used by the SymbolTable when discarding unused comdat sections. This is
|
||||
// redundant when GC is enabled, as all comdat sections will start out dead.
|
||||
void markDiscarded() { Discarded = true; }
|
||||
|
||||
// True if this is a codeview debug info chunk. These will not be laid out in
|
||||
// the image. Instead they will end up in the PDB, if one is requested.
|
||||
bool isCodeView() const {
|
||||
return SectionName == ".debug" || SectionName.startswith(".debug$");
|
||||
}
|
||||
|
||||
// True if this is a DWARF debug info chunk.
|
||||
bool isDWARF() const { return SectionName.startswith(".debug_"); }
|
||||
|
||||
// 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, Relocs.begin()),
|
||||
symbol_iterator(File, Relocs.end()));
|
||||
}
|
||||
|
||||
// Allow iteration over the associated child chunks for this section.
|
||||
ArrayRef<SectionChunk *> children() const { return AssocChildren; }
|
||||
|
||||
// A pointer pointing to a replacement for this chunk.
|
||||
// Initially it points to "this" object. If this chunk is merged
|
||||
// with other chunk by ICF, it points to another chunk,
|
||||
// and this chunk is considrered as dead.
|
||||
SectionChunk *Repl;
|
||||
|
||||
// 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;
|
||||
|
||||
const coff_section *Header;
|
||||
|
||||
// The file that this chunk was created from.
|
||||
ObjectFile *File;
|
||||
|
||||
private:
|
||||
StringRef SectionName;
|
||||
std::vector<SectionChunk *> AssocChildren;
|
||||
llvm::iterator_range<const coff_relocation *> Relocs;
|
||||
size_t NumRelocs;
|
||||
|
||||
// True if this chunk was discarded because it was a duplicate comdat section.
|
||||
bool Discarded;
|
||||
|
||||
// Used by the garbage collector.
|
||||
bool Live;
|
||||
|
||||
// Used for ICF (Identical COMDAT Folding)
|
||||
void replace(SectionChunk *Other);
|
||||
uint32_t Class[2] = {0, 0};
|
||||
|
||||
// Sym points to a section symbol if this is a COMDAT chunk.
|
||||
DefinedRegular *Sym = nullptr;
|
||||
};
|
||||
|
||||
// A chunk for common symbols. Common chunks don't have actual data.
|
||||
class CommonChunk : public Chunk {
|
||||
public:
|
||||
CommonChunk(const COFFSymbolRef Sym);
|
||||
size_t getSize() const override { return Sym.getValue(); }
|
||||
bool hasData() const override { return false; }
|
||||
uint32_t getPermissions() const override;
|
||||
StringRef getSectionName() const override { return ".bss"; }
|
||||
|
||||
private:
|
||||
const COFFSymbolRef Sym;
|
||||
};
|
||||
|
||||
// A chunk for linker-created strings.
|
||||
class StringChunk : public Chunk {
|
||||
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, it's
|
||||
// contents will be a JMP instruction to some __imp_ symbol.
|
||||
class ImportThunkChunkX64 : public Chunk {
|
||||
public:
|
||||
explicit ImportThunkChunkX64(Defined *S);
|
||||
size_t getSize() const override { return sizeof(ImportThunkX86); }
|
||||
void writeTo(uint8_t *Buf) const override;
|
||||
|
||||
private:
|
||||
Defined *ImpSymbol;
|
||||
};
|
||||
|
||||
class ImportThunkChunkX86 : public Chunk {
|
||||
public:
|
||||
explicit ImportThunkChunkX86(Defined *S) : ImpSymbol(S) {}
|
||||
size_t getSize() const override { return sizeof(ImportThunkX86); }
|
||||
void getBaserels(std::vector<Baserel> *Res) override;
|
||||
void writeTo(uint8_t *Buf) const override;
|
||||
|
||||
private:
|
||||
Defined *ImpSymbol;
|
||||
};
|
||||
|
||||
class ImportThunkChunkARM : public Chunk {
|
||||
public:
|
||||
explicit ImportThunkChunkARM(Defined *S) : ImpSymbol(S) {}
|
||||
size_t getSize() const override { return sizeof(ImportThunkARM); }
|
||||
void getBaserels(std::vector<Baserel> *Res) override;
|
||||
void writeTo(uint8_t *Buf) const override;
|
||||
|
||||
private:
|
||||
Defined *ImpSymbol;
|
||||
};
|
||||
|
||||
class ImportThunkChunkARM64 : public Chunk {
|
||||
public:
|
||||
explicit ImportThunkChunkARM64(Defined *S) : ImpSymbol(S) {}
|
||||
size_t getSize() const override { return sizeof(ImportThunkARM64); }
|
||||
void writeTo(uint8_t *Buf) const override;
|
||||
|
||||
private:
|
||||
Defined *ImpSymbol;
|
||||
};
|
||||
|
||||
// Windows-specific.
|
||||
// See comments for DefinedLocalImport class.
|
||||
class LocalImportChunk : public Chunk {
|
||||
public:
|
||||
explicit LocalImportChunk(Defined *S) : Sym(S) {}
|
||||
size_t getSize() const override;
|
||||
void getBaserels(std::vector<Baserel> *Res) override;
|
||||
void writeTo(uint8_t *Buf) const override;
|
||||
|
||||
private:
|
||||
Defined *Sym;
|
||||
};
|
||||
|
||||
// Windows-specific.
|
||||
// A chunk for SEH table which contains RVAs of safe exception handler
|
||||
// functions. x86-only.
|
||||
class SEHTableChunk : public Chunk {
|
||||
public:
|
||||
explicit SEHTableChunk(std::set<Defined *> S) : Syms(std::move(S)) {}
|
||||
size_t getSize() const override { return Syms.size() * 4; }
|
||||
void writeTo(uint8_t *Buf) const override;
|
||||
|
||||
private:
|
||||
std::set<Defined *> Syms;
|
||||
};
|
||||
|
||||
// Windows-specific.
|
||||
// This class represents a block in .reloc section.
|
||||
// See the PE/COFF spec 5.6 for details.
|
||||
class BaserelChunk : public Chunk {
|
||||
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;
|
||||
};
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
174
deps/lld/COFF/Config.h
vendored
Normal file
174
deps/lld/COFF/Config.h
vendored
Normal file
@ -0,0 +1,174 @@
|
||||
//===- Config.h -------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_COFF_CONFIG_H
|
||||
#define LLD_COFF_CONFIG_H
|
||||
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Object/COFF.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;
|
||||
struct Symbol;
|
||||
class SymbolBody;
|
||||
|
||||
// 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
|
||||
SymbolBody *Sym = nullptr;
|
||||
uint16_t Ordinal = 0;
|
||||
bool Noname = false;
|
||||
bool Data = false;
|
||||
bool Private = 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 && Private == E.Private);
|
||||
}
|
||||
};
|
||||
|
||||
enum class DebugType {
|
||||
None = 0x0,
|
||||
CV = 0x1, /// CodeView
|
||||
PData = 0x2, /// Procedure Data
|
||||
Fixup = 0x4, /// Relocation Table
|
||||
};
|
||||
|
||||
// Global configuration.
|
||||
struct Configuration {
|
||||
enum ManifestKind { SideBySide, Embed, No };
|
||||
bool is64() { return Machine == AMD64 || Machine == ARM64; }
|
||||
|
||||
llvm::COFF::MachineTypes Machine = IMAGE_FILE_MACHINE_UNKNOWN;
|
||||
bool Verbose = false;
|
||||
WindowsSubsystem Subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN;
|
||||
SymbolBody *Entry = nullptr;
|
||||
bool NoEntry = false;
|
||||
std::string OutputFile;
|
||||
std::string ImportName;
|
||||
bool ColorDiagnostics;
|
||||
bool DoGC = true;
|
||||
bool DoICF = true;
|
||||
uint64_t ErrorLimit = 20;
|
||||
bool Relocatable = true;
|
||||
bool Force = false;
|
||||
bool Debug = false;
|
||||
bool WriteSymtab = true;
|
||||
unsigned DebugTypes = static_cast<unsigned>(DebugType::None);
|
||||
llvm::SmallString<128> PDBPath;
|
||||
std::vector<llvm::StringRef> Argv;
|
||||
|
||||
// Symbols in this set are considered as live by the garbage collector.
|
||||
std::set<SymbolBody *> GCRoot;
|
||||
|
||||
std::set<StringRef> 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;
|
||||
SymbolBody *DelayLoadHelper = nullptr;
|
||||
|
||||
bool SaveTemps = false;
|
||||
|
||||
// Used for SafeSEH.
|
||||
Symbol *SEHTable = nullptr;
|
||||
Symbol *SEHCount = nullptr;
|
||||
|
||||
// Used for /opt:lldlto=N
|
||||
unsigned LTOOptLevel = 2;
|
||||
|
||||
// Used for /opt:lldltojobs=N
|
||||
unsigned LTOJobs = 0;
|
||||
// Used for /opt:lldltopartitions=N
|
||||
unsigned LTOPartitions = 1;
|
||||
|
||||
// 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 /failifmismatch.
|
||||
std::map<StringRef, StringRef> MustMatch;
|
||||
|
||||
// Used for /alternatename.
|
||||
std::map<StringRef, StringRef> AlternateNames;
|
||||
|
||||
// Used for /lldmap.
|
||||
std::string MapFile;
|
||||
|
||||
uint64_t ImageBase = -1;
|
||||
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;
|
||||
bool DynamicBase = true;
|
||||
bool NxCompat = true;
|
||||
bool AllowIsolation = true;
|
||||
bool TerminalServerAware = true;
|
||||
bool LargeAddressAware = false;
|
||||
bool HighEntropyVA = false;
|
||||
bool AppContainer = false;
|
||||
};
|
||||
|
||||
extern Configuration *Config;
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
548
deps/lld/COFF/DLL.cpp
vendored
Normal file
548
deps/lld/COFF/DLL.cpp
vendored
Normal file
@ -0,0 +1,548 @@
|
||||
//===- DLL.cpp ------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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 "Chunks.h"
|
||||
#include "DLL.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
|
||||
|
||||
static int ptrSize() { return Config->is64() ? 8 : 4; }
|
||||
|
||||
// A chunk for the import descriptor table.
|
||||
class HintNameChunk : public Chunk {
|
||||
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 {
|
||||
write16le(Buf + OutputSectionOff, Hint);
|
||||
memcpy(Buf + OutputSectionOff + 2, Name.data(), Name.size());
|
||||
}
|
||||
|
||||
private:
|
||||
StringRef Name;
|
||||
uint16_t Hint;
|
||||
};
|
||||
|
||||
// A chunk for the import descriptor table.
|
||||
class LookupChunk : public Chunk {
|
||||
public:
|
||||
explicit LookupChunk(Chunk *C) : HintName(C) {}
|
||||
size_t getSize() const override { return ptrSize(); }
|
||||
|
||||
void writeTo(uint8_t *Buf) const override {
|
||||
write32le(Buf + OutputSectionOff, 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 Chunk {
|
||||
public:
|
||||
explicit OrdinalOnlyChunk(uint16_t V) : Ordinal(V) {}
|
||||
size_t getSize() const override { return ptrSize(); }
|
||||
|
||||
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 + OutputSectionOff, (1ULL << 63) | Ordinal);
|
||||
} else {
|
||||
write32le(Buf + OutputSectionOff, (1ULL << 31) | Ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t Ordinal;
|
||||
};
|
||||
|
||||
// A chunk for the import descriptor table.
|
||||
class ImportDirectoryChunk : public Chunk {
|
||||
public:
|
||||
explicit ImportDirectoryChunk(Chunk *N) : DLLName(N) {}
|
||||
size_t getSize() const override { return sizeof(ImportDirectoryTableEntry); }
|
||||
|
||||
void writeTo(uint8_t *Buf) const override {
|
||||
auto *E = (coff_import_directory_table_entry *)(Buf + OutputSectionOff);
|
||||
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 Chunk {
|
||||
public:
|
||||
explicit NullChunk(size_t N) : Size(N) {}
|
||||
bool hasData() const override { return false; }
|
||||
size_t getSize() const override { return Size; }
|
||||
void setAlign(size_t N) { Align = N; }
|
||||
|
||||
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 Chunk {
|
||||
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 {
|
||||
auto *E = (delay_import_directory_table_entry *)(Buf + OutputSectionOff);
|
||||
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[] = {
|
||||
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, 0x8D, 0x15, 0, 0, 0, 0, // lea rdx, [__imp_<FUNCNAME>]
|
||||
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[] = {
|
||||
0x51, // push ecx
|
||||
0x52, // push edx
|
||||
0x68, 0, 0, 0, 0, // push offset ___imp__<FUNCNAME>
|
||||
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
|
||||
};
|
||||
|
||||
// A chunk for the delay import thunk.
|
||||
class ThunkChunkX64 : public Chunk {
|
||||
public:
|
||||
ThunkChunkX64(Defined *I, Chunk *D, Defined *H)
|
||||
: Imp(I), Desc(D), Helper(H) {}
|
||||
|
||||
size_t getSize() const override { return sizeof(ThunkX64); }
|
||||
|
||||
void writeTo(uint8_t *Buf) const override {
|
||||
memcpy(Buf + OutputSectionOff, ThunkX64, sizeof(ThunkX64));
|
||||
write32le(Buf + OutputSectionOff + 36, Imp->getRVA() - RVA - 40);
|
||||
write32le(Buf + OutputSectionOff + 43, Desc->getRVA() - RVA - 47);
|
||||
write32le(Buf + OutputSectionOff + 48, Helper->getRVA() - RVA - 52);
|
||||
}
|
||||
|
||||
Defined *Imp = nullptr;
|
||||
Chunk *Desc = nullptr;
|
||||
Defined *Helper = nullptr;
|
||||
};
|
||||
|
||||
class ThunkChunkX86 : public Chunk {
|
||||
public:
|
||||
ThunkChunkX86(Defined *I, Chunk *D, Defined *H)
|
||||
: Imp(I), Desc(D), Helper(H) {}
|
||||
|
||||
size_t getSize() const override { return sizeof(ThunkX86); }
|
||||
|
||||
void writeTo(uint8_t *Buf) const override {
|
||||
memcpy(Buf + OutputSectionOff, ThunkX86, sizeof(ThunkX86));
|
||||
write32le(Buf + OutputSectionOff + 3, Imp->getRVA() + Config->ImageBase);
|
||||
write32le(Buf + OutputSectionOff + 8, Desc->getRVA() + Config->ImageBase);
|
||||
write32le(Buf + OutputSectionOff + 13, Helper->getRVA() - RVA - 17);
|
||||
}
|
||||
|
||||
void getBaserels(std::vector<Baserel> *Res) override {
|
||||
Res->emplace_back(RVA + 3);
|
||||
Res->emplace_back(RVA + 8);
|
||||
}
|
||||
|
||||
Defined *Imp = nullptr;
|
||||
Chunk *Desc = nullptr;
|
||||
Defined *Helper = nullptr;
|
||||
};
|
||||
|
||||
// A chunk for the import descriptor table.
|
||||
class DelayAddressChunk : public Chunk {
|
||||
public:
|
||||
explicit DelayAddressChunk(Chunk *C) : Thunk(C) {}
|
||||
size_t getSize() const override { return ptrSize(); }
|
||||
|
||||
void writeTo(uint8_t *Buf) const override {
|
||||
if (Config->is64()) {
|
||||
write64le(Buf + OutputSectionOff, Thunk->getRVA() + Config->ImageBase);
|
||||
} else {
|
||||
write32le(Buf + OutputSectionOff, Thunk->getRVA() + Config->ImageBase);
|
||||
}
|
||||
}
|
||||
|
||||
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 Chunk {
|
||||
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 {
|
||||
auto *E = (export_directory_table_entry *)(Buf + OutputSectionOff);
|
||||
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 Chunk {
|
||||
public:
|
||||
explicit AddressTableChunk(size_t MaxOrdinal) : Size(MaxOrdinal + 1) {}
|
||||
size_t getSize() const override { return Size * 4; }
|
||||
|
||||
void writeTo(uint8_t *Buf) const override {
|
||||
for (Export &E : Config->Exports) {
|
||||
uint8_t *P = Buf + OutputSectionOff + E.Ordinal * 4;
|
||||
if (E.ForwardChunk) {
|
||||
write32le(P, E.ForwardChunk->getRVA());
|
||||
} else {
|
||||
write32le(P, cast<Defined>(E.Sym)->getRVA());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
size_t Size;
|
||||
};
|
||||
|
||||
class NamePointersChunk : public Chunk {
|
||||
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 {
|
||||
uint8_t *P = Buf + OutputSectionOff;
|
||||
for (Chunk *C : Chunks) {
|
||||
write32le(P, C->getRVA());
|
||||
P += 4;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Chunk *> Chunks;
|
||||
};
|
||||
|
||||
class ExportOrdinalChunk : public Chunk {
|
||||
public:
|
||||
explicit ExportOrdinalChunk(size_t I) : Size(I) {}
|
||||
size_t getSize() const override { return Size * 2; }
|
||||
|
||||
void writeTo(uint8_t *Buf) const override {
|
||||
uint8_t *P = Buf + OutputSectionOff;
|
||||
for (Export &E : Config->Exports) {
|
||||
if (E.Noname)
|
||||
continue;
|
||||
write16le(P, E.Ordinal);
|
||||
P += 2;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
size_t Size;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
uint64_t IdataContents::getDirSize() {
|
||||
return Dirs.size() * sizeof(ImportDirectoryTableEntry);
|
||||
}
|
||||
|
||||
uint64_t IdataContents::getIATSize() {
|
||||
return Addresses.size() * ptrSize();
|
||||
}
|
||||
|
||||
// Returns a list of .idata contents.
|
||||
// See Microsoft PE/COFF spec 5.4 for details.
|
||||
std::vector<Chunk *> IdataContents::getChunks() {
|
||||
create();
|
||||
|
||||
// The loader assumes a specific order of data.
|
||||
// Add each type in the correct order.
|
||||
std::vector<Chunk *> V;
|
||||
V.insert(V.end(), Dirs.begin(), Dirs.end());
|
||||
V.insert(V.end(), Lookups.begin(), Lookups.end());
|
||||
V.insert(V.end(), Addresses.begin(), Addresses.end());
|
||||
V.insert(V.end(), Hints.begin(), Hints.end());
|
||||
V.insert(V.end(), DLLNames.begin(), DLLNames.end());
|
||||
return V;
|
||||
}
|
||||
|
||||
void IdataContents::create() {
|
||||
std::vector<std::vector<DefinedImportData *>> V = binImports(Imports);
|
||||
|
||||
// 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>(ptrSize()));
|
||||
Addresses.push_back(make<NullChunk>(ptrSize()));
|
||||
|
||||
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();
|
||||
for (DefinedImportData *S : Syms) {
|
||||
Chunk *T = newThunkChunk(S, Dir);
|
||||
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);
|
||||
}
|
||||
}
|
||||
// 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->setAlign(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::newThunkChunk(DefinedImportData *S, Chunk *Dir) {
|
||||
switch (Config->Machine) {
|
||||
case AMD64:
|
||||
return make<ThunkChunkX64>(S, Dir, Helper);
|
||||
case I386:
|
||||
return make<ThunkChunkX86>(S, Dir, Helper);
|
||||
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
|
||||
84
deps/lld/COFF/DLL.h
vendored
Normal file
84
deps/lld/COFF/DLL.h
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
//===- DLL.h ----------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_COFF_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 getChunks() to get a list of chunks.
|
||||
class IdataContents {
|
||||
public:
|
||||
void add(DefinedImportData *Sym) { Imports.push_back(Sym); }
|
||||
bool empty() { return Imports.empty(); }
|
||||
std::vector<Chunk *> getChunks();
|
||||
|
||||
uint64_t getDirRVA() { return Dirs[0]->getRVA(); }
|
||||
uint64_t getDirSize();
|
||||
uint64_t getIATRVA() { return Addresses[0]->getRVA(); }
|
||||
uint64_t getIATSize();
|
||||
|
||||
private:
|
||||
void create();
|
||||
|
||||
std::vector<DefinedImportData *> Imports;
|
||||
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 *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;
|
||||
};
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
1181
deps/lld/COFF/Driver.cpp
vendored
Normal file
1181
deps/lld/COFF/Driver.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
188
deps/lld/COFF/Driver.h
vendored
Normal file
188
deps/lld/COFF/Driver.h
vendored
Normal file
@ -0,0 +1,188 @@
|
||||
//===- Driver.h -------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_COFF_DRIVER_H
|
||||
#define LLD_COFF_DRIVER_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Core/Reproduce.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Object/Archive.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include "llvm/Option/Arg.h"
|
||||
#include "llvm/Option/ArgList.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;
|
||||
|
||||
// Implemented in MarkLive.cpp.
|
||||
void markLive(const std::vector<Chunk *> &Chunks);
|
||||
|
||||
// Implemented in ICF.cpp.
|
||||
void doICF(const std::vector<Chunk *> &Chunks);
|
||||
|
||||
class ArgParser {
|
||||
public:
|
||||
// Parses command line options.
|
||||
llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> Args);
|
||||
|
||||
// Concatenate LINK environment varirable 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)); }
|
||||
|
||||
private:
|
||||
std::vector<const char *> tokenize(StringRef S);
|
||||
|
||||
std::vector<const char *> replaceResponseFiles(std::vector<const char *>);
|
||||
};
|
||||
|
||||
class LinkerDriver {
|
||||
public:
|
||||
LinkerDriver() { coff::Symtab = &Symtab; }
|
||||
void link(llvm::ArrayRef<const char *> Args);
|
||||
|
||||
// Used by the resolver to parse .drectve section contents.
|
||||
void parseDirectives(StringRef S);
|
||||
|
||||
// Used by ArchiveFile to enqueue members.
|
||||
void enqueueArchiveMember(const Archive::Child &C, StringRef SymName,
|
||||
StringRef ParentName);
|
||||
|
||||
private:
|
||||
ArgParser Parser;
|
||||
SymbolTable Symtab;
|
||||
|
||||
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);
|
||||
|
||||
// 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;
|
||||
std::set<std::string> VisitedFiles;
|
||||
std::set<std::string> VisitedLibs;
|
||||
|
||||
SymbolBody *addUndefined(StringRef Sym);
|
||||
StringRef mangle(StringRef Sym);
|
||||
|
||||
// Windows specific -- "main" is not the only main function in Windows.
|
||||
// You can choose one from these four -- {w,}{WinMain,main}.
|
||||
// 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 invokeMSVC(llvm::opt::InputArgList &Args);
|
||||
|
||||
MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> MB);
|
||||
void addBuffer(std::unique_ptr<MemoryBuffer> MB);
|
||||
void addArchiveBuffer(MemoryBufferRef MBRef, StringRef SymName,
|
||||
StringRef ParentName);
|
||||
|
||||
void enqueuePath(StringRef Path);
|
||||
|
||||
void enqueueTask(std::function<void()> Task);
|
||||
bool run();
|
||||
|
||||
std::list<std::function<void()>> TaskQueue;
|
||||
std::vector<StringRef> FilePaths;
|
||||
std::vector<MemoryBufferRef> Resources;
|
||||
};
|
||||
|
||||
// Functions below this line are defined in DriverUtils.cpp.
|
||||
|
||||
void printHelp(const char *Argv0);
|
||||
|
||||
// For /machine option.
|
||||
MachineTypes getMachineType(StringRef Arg);
|
||||
StringRef machineToStr(MachineTypes MT);
|
||||
|
||||
// Parses a string in the form of "<integer>[,<integer>]".
|
||||
void parseNumbers(StringRef Arg, uint64_t *Addr, uint64_t *Size = nullptr);
|
||||
|
||||
// Parses a string in the form of "<integer>[.<integer>]".
|
||||
// Minor's default value is 0.
|
||||
void parseVersion(StringRef Arg, uint32_t *Major, uint32_t *Minor);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// Convert Windows resource files (.res files) to a .obj file
|
||||
// using cvtres.exe.
|
||||
std::unique_ptr<MemoryBuffer>
|
||||
convertResToCOFF(const std::vector<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
|
||||
730
deps/lld/COFF/DriverUtils.cpp
vendored
Normal file
730
deps/lld/COFF/DriverUtils.cpp
vendored
Normal file
@ -0,0 +1,730 @@
|
||||
//===- DriverUtils.cpp ----------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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 "Error.h"
|
||||
#include "Memory.h"
|
||||
#include "Symbols.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 <memory>
|
||||
|
||||
using namespace llvm::COFF;
|
||||
using namespace llvm;
|
||||
using llvm::cl::ExpandResponseFiles;
|
||||
using llvm::cl::TokenizeWindowsCommandLine;
|
||||
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(EC, "unable to find " + Prog + " in PATH: ");
|
||||
StringRef Exe = Saver.save(*ExeOrErr);
|
||||
Args.insert(Args.begin(), Exe);
|
||||
|
||||
std::vector<const char *> Vec;
|
||||
for (StringRef S : Args)
|
||||
Vec.push_back(S.data());
|
||||
Vec.push_back(nullptr);
|
||||
|
||||
if (sys::ExecuteAndWait(Args[0], Vec.data()) != 0)
|
||||
fatal("ExecuteAndWait failed: " +
|
||||
llvm::join(Args.begin(), Args.end(), " "));
|
||||
}
|
||||
|
||||
private:
|
||||
StringRef Prog;
|
||||
std::vector<StringRef> Args;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// Returns /machine's value.
|
||||
MachineTypes getMachineType(StringRef S) {
|
||||
MachineTypes MT = StringSwitch<MachineTypes>(S.lower())
|
||||
.Cases("x64", "amd64", AMD64)
|
||||
.Cases("x86", "i386", I386)
|
||||
.Case("arm", ARMNT)
|
||||
.Case("arm64", ARM64)
|
||||
.Default(IMAGE_FILE_MACHINE_UNKNOWN);
|
||||
if (MT != IMAGE_FILE_MACHINE_UNKNOWN)
|
||||
return MT;
|
||||
fatal("unknown /machine argument: " + S);
|
||||
}
|
||||
|
||||
StringRef machineToStr(MachineTypes MT) {
|
||||
switch (MT) {
|
||||
case ARMNT:
|
||||
return "arm";
|
||||
case ARM64:
|
||||
return "arm64";
|
||||
case AMD64:
|
||||
return "x64";
|
||||
case I386:
|
||||
return "x86";
|
||||
default:
|
||||
llvm_unreachable("unknown machine type");
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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(',');
|
||||
*Sys = StringSwitch<WindowsSubsystem>(SysStr.lower())
|
||||
.Case("boot_application", IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION)
|
||||
.Case("console", IMAGE_SUBSYSTEM_WINDOWS_CUI)
|
||||
.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)
|
||||
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);
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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(EC, "cannot create a temporary file");
|
||||
Path = S.str();
|
||||
|
||||
if (!Contents.empty()) {
|
||||
std::error_code EC;
|
||||
raw_fd_ostream OS(Path, EC, sys::fs::F_None);
|
||||
if (EC)
|
||||
fatal(EC, "failed to open " + Path);
|
||||
OS << Contents;
|
||||
}
|
||||
}
|
||||
|
||||
TemporaryFile(TemporaryFile &&Obj) {
|
||||
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() {
|
||||
// IsVolatileSize=true forces MemoryBuffer to not use mmap().
|
||||
return check(MemoryBuffer::getFile(Path, /*FileSize=*/-1,
|
||||
/*RequiresNullTerminator=*/false,
|
||||
/*IsVolatileSize=*/true),
|
||||
"could not open " + Path);
|
||||
}
|
||||
|
||||
std::string Path;
|
||||
};
|
||||
}
|
||||
|
||||
// Create the default manifest file as a temporary file.
|
||||
TemporaryFile createDefaultXml() {
|
||||
// Create a temporary file.
|
||||
TemporaryFile File("defaultxml", "manifest");
|
||||
|
||||
// Open the temporary file for writing.
|
||||
std::error_code EC;
|
||||
raw_fd_ostream OS(File.Path, EC, sys::fs::F_Text);
|
||||
if (EC)
|
||||
fatal(EC, "failed to open " + File.Path);
|
||||
|
||||
// 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";
|
||||
OS.close();
|
||||
return File;
|
||||
}
|
||||
|
||||
static std::string readFile(StringRef Path) {
|
||||
std::unique_ptr<MemoryBuffer> MB =
|
||||
check(MemoryBuffer::getFile(Path), "could not open " + Path);
|
||||
return MB->getBuffer();
|
||||
}
|
||||
|
||||
static std::string createManifestXml() {
|
||||
// Create the default manifest file.
|
||||
TemporaryFile File1 = createDefaultXml();
|
||||
if (Config->ManifestInput.empty())
|
||||
return readFile(File1.Path);
|
||||
|
||||
// If manifest files are supplied by the user using /MANIFESTINPUT
|
||||
// option, we need to merge them with the default manifest.
|
||||
TemporaryFile File2("user", "manifest");
|
||||
|
||||
Executor E("mt.exe");
|
||||
E.add("/manifest");
|
||||
E.add(File1.Path);
|
||||
for (StringRef Filename : Config->ManifestInput) {
|
||||
E.add("/manifest");
|
||||
E.add(Filename);
|
||||
}
|
||||
E.add("/nologo");
|
||||
E.add("/out:" + StringRef(File2.Path));
|
||||
E.run();
|
||||
return readFile(File2.Path);
|
||||
}
|
||||
|
||||
static std::unique_ptr<MemoryBuffer>
|
||||
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 MemoryBuffer::getNewMemBuffer(ResSize);
|
||||
}
|
||||
|
||||
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<MemoryBuffer> Res =
|
||||
createMemoryBufferForManifestRes(Manifest.size());
|
||||
|
||||
char *Buf = const_cast<char *>(Res->getBufferStart());
|
||||
writeResFileHeader(Buf);
|
||||
writeResEntryHeader(Buf, Manifest.size());
|
||||
|
||||
// Copy the manifest data into the .res file.
|
||||
std::copy(Manifest.begin(), Manifest.end(), Buf);
|
||||
return 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(EC, "failed to create manifest");
|
||||
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.find('=') != StringRef::npos) {
|
||||
StringRef X, Y;
|
||||
std::tie(X, Y) = E.Name.split("=");
|
||||
|
||||
// If "<name>=<dllname>.<name>".
|
||||
if (Y.find(".") != StringRef::npos) {
|
||||
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.Private = 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;
|
||||
return Sym.startswith("_") ? Sym.substr(1) : 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) {
|
||||
SymbolBody *Sym = E.Sym;
|
||||
if (!E.ForwardTo.empty() || !Sym) {
|
||||
E.SymbolName = E.Name;
|
||||
} else {
|
||||
if (auto *U = dyn_cast<Undefined>(Sym))
|
||||
if (U->WeakAlias)
|
||||
Sym = U->WeakAlias;
|
||||
E.SymbolName = Sym->getName();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Uniquefy by name.
|
||||
std::map<StringRef, Export *> Map;
|
||||
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) {
|
||||
StringRef K, V;
|
||||
std::tie(K, V) = Arg.split('=');
|
||||
if (K.empty() || V.empty())
|
||||
fatal("/failifmismatch: invalid argument: " + Arg);
|
||||
StringRef Existing = Config->MustMatch[K];
|
||||
if (!Existing.empty() && V != Existing)
|
||||
fatal("/failifmismatch: mismatch detected: " + Existing + " and " + V +
|
||||
" for key " + K);
|
||||
Config->MustMatch[K] = V;
|
||||
}
|
||||
|
||||
// Convert Windows resource files (.res files) to a .obj file
|
||||
// using cvtres.exe.
|
||||
std::unique_ptr<MemoryBuffer>
|
||||
convertResToCOFF(const std::vector<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");
|
||||
if (auto EC = Parser.parse(RF))
|
||||
fatal(EC, "failed to parse .res file");
|
||||
}
|
||||
|
||||
Expected<std::unique_ptr<MemoryBuffer>> E =
|
||||
llvm::object::writeWindowsResourceCOFF(Config->Machine, Parser);
|
||||
if (!E)
|
||||
fatal(errorToErrorCode(E.takeError()), "failed to write .res to COFF");
|
||||
return std::move(E.get());
|
||||
}
|
||||
|
||||
// Run MSVC link.exe for given in-memory object files.
|
||||
// Command line options are copied from those given to LLD.
|
||||
// This is for the /msvclto option.
|
||||
void runMSVCLinker(std::string Rsp, ArrayRef<StringRef> Objects) {
|
||||
// Write the in-memory object files to disk.
|
||||
std::vector<TemporaryFile> Temps;
|
||||
for (StringRef S : Objects) {
|
||||
Temps.emplace_back("lto", "obj", S);
|
||||
Rsp += quote(Temps.back().Path) + "\n";
|
||||
}
|
||||
|
||||
log("link.exe " + Rsp);
|
||||
|
||||
// Run MSVC link.exe.
|
||||
Temps.emplace_back("lto", "rsp", Rsp);
|
||||
Executor E("link.exe");
|
||||
E.add(Twine("@" + Temps.back().Path));
|
||||
E.run();
|
||||
}
|
||||
|
||||
// Create OptTable
|
||||
|
||||
// Create prefix string literals used in Options.td
|
||||
#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
|
||||
};
|
||||
|
||||
class COFFOptTable : public llvm::opt::OptTable {
|
||||
public:
|
||||
COFFOptTable() : OptTable(infoTable, true) {}
|
||||
};
|
||||
|
||||
// Parses a given list of options.
|
||||
opt::InputArgList ArgParser::parse(ArrayRef<const char *> ArgsArr) {
|
||||
// First, replace respnose files (@<file>-style options).
|
||||
std::vector<const char *> Argv = replaceResponseFiles(ArgsArr);
|
||||
|
||||
// Make InputArgList from string vectors.
|
||||
COFFOptTable Table;
|
||||
unsigned MissingIndex;
|
||||
unsigned MissingCount;
|
||||
opt::InputArgList Args = Table.ParseArgs(Argv, MissingIndex, MissingCount);
|
||||
|
||||
// Print the real command line if response files are expanded.
|
||||
if (Args.hasArg(OPT_verbose) && ArgsArr.size() != Argv.size()) {
|
||||
std::string Msg = "Command line:";
|
||||
for (const char *S : Argv)
|
||||
Msg += " " + std::string(S);
|
||||
message(Msg);
|
||||
}
|
||||
|
||||
if (MissingCount)
|
||||
fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument");
|
||||
for (auto *Arg : Args.filtered(OPT_UNKNOWN))
|
||||
warn("ignoring unknown argument: " + Arg->getSpelling());
|
||||
return Args;
|
||||
}
|
||||
|
||||
// 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 *> Args) {
|
||||
// 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);
|
||||
Args.insert(Args.begin(), V.begin(), V.end());
|
||||
}
|
||||
if (Optional<std::string> S = Process::GetEnv("_LINK_")) {
|
||||
std::vector<const char *> V = tokenize(*S);
|
||||
Args.insert(Args.begin(), V.begin(), V.end());
|
||||
}
|
||||
return parse(Args);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
// Creates a new command line by replacing options starting with '@'
|
||||
// character. '@<filename>' is replaced by the file's contents.
|
||||
std::vector<const char *>
|
||||
ArgParser::replaceResponseFiles(std::vector<const char *> Argv) {
|
||||
SmallVector<const char *, 256> Tokens(Argv.data(), Argv.data() + Argv.size());
|
||||
ExpandResponseFiles(Saver, TokenizeWindowsCommandLine, Tokens);
|
||||
return std::vector<const char *>(Tokens.begin(), Tokens.end());
|
||||
}
|
||||
|
||||
void printHelp(const char *Argv0) {
|
||||
COFFOptTable Table;
|
||||
Table.PrintHelp(outs(), Argv0, "LLVM Linker", false);
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
114
deps/lld/COFF/Error.cpp
vendored
Normal file
114
deps/lld/COFF/Error.cpp
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
//===- Error.cpp ----------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "Config.h"
|
||||
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/ManagedStatic.h"
|
||||
#include "llvm/Support/Process.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <mutex>
|
||||
|
||||
#if !defined(_MSC_VER) && !defined(__MINGW32__)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace lld {
|
||||
// The functions defined in this file can be called from multiple threads,
|
||||
// but outs() or errs() are not thread-safe. We protect them using a mutex.
|
||||
static std::mutex Mu;
|
||||
|
||||
namespace coff {
|
||||
uint64_t ErrorCount;
|
||||
raw_ostream *ErrorOS;
|
||||
|
||||
static LLVM_ATTRIBUTE_NORETURN void exitLld(int Val) {
|
||||
// Dealloc/destroy ManagedStatic variables before calling
|
||||
// _exit(). In a non-LTO build, this is a nop. In an LTO
|
||||
// build allows us to get the output of -time-passes.
|
||||
llvm_shutdown();
|
||||
|
||||
outs().flush();
|
||||
errs().flush();
|
||||
_exit(Val);
|
||||
}
|
||||
|
||||
static void print(StringRef S, raw_ostream::Colors C) {
|
||||
*ErrorOS << Config->Argv[0] << ": ";
|
||||
if (Config->ColorDiagnostics) {
|
||||
ErrorOS->changeColor(C, true);
|
||||
*ErrorOS << S;
|
||||
ErrorOS->resetColor();
|
||||
} else {
|
||||
*ErrorOS << S;
|
||||
}
|
||||
}
|
||||
|
||||
void log(const Twine &Msg) {
|
||||
if (Config->Verbose) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
outs() << Config->Argv[0] << ": " << Msg << "\n";
|
||||
outs().flush();
|
||||
}
|
||||
}
|
||||
|
||||
void message(const Twine &Msg) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
outs() << Msg << "\n";
|
||||
outs().flush();
|
||||
}
|
||||
|
||||
void error(const Twine &Msg) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
|
||||
if (Config->ErrorLimit == 0 || ErrorCount < Config->ErrorLimit) {
|
||||
print("error: ", raw_ostream::RED);
|
||||
*ErrorOS << Msg << "\n";
|
||||
} else if (ErrorCount == Config->ErrorLimit) {
|
||||
print("error: ", raw_ostream::RED);
|
||||
*ErrorOS << "too many errors emitted, stopping now"
|
||||
<< " (use /ERRORLIMIT:0 to see all errors)\n";
|
||||
exitLld(1);
|
||||
}
|
||||
|
||||
++ErrorCount;
|
||||
}
|
||||
|
||||
void fatal(const Twine &Msg) {
|
||||
if (Config->ColorDiagnostics) {
|
||||
errs().changeColor(raw_ostream::RED, /*bold=*/true);
|
||||
errs() << "error: ";
|
||||
errs().resetColor();
|
||||
} else {
|
||||
errs() << "error: ";
|
||||
}
|
||||
errs() << Msg << "\n";
|
||||
exitLld(1);
|
||||
}
|
||||
|
||||
void fatal(std::error_code EC, const Twine &Msg) {
|
||||
fatal(Msg + ": " + EC.message());
|
||||
}
|
||||
|
||||
void fatal(llvm::Error &Err, const Twine &Msg) {
|
||||
fatal(errorToErrorCode(std::move(Err)), Msg);
|
||||
}
|
||||
|
||||
void warn(const Twine &Msg) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
print("warning: ", raw_ostream::MAGENTA);
|
||||
*ErrorOS << Msg << "\n";
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
62
deps/lld/COFF/Error.h
vendored
Normal file
62
deps/lld/COFF/Error.h
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
//===- Error.h --------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_COFF_ERROR_H
|
||||
#define LLD_COFF_ERROR_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
extern uint64_t ErrorCount;
|
||||
extern llvm::raw_ostream *ErrorOS;
|
||||
|
||||
void log(const Twine &Msg);
|
||||
void message(const Twine &Msg);
|
||||
void warn(const Twine &Msg);
|
||||
void error(const Twine &Msg);
|
||||
LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg);
|
||||
LLVM_ATTRIBUTE_NORETURN void fatal(std::error_code EC, const Twine &Prefix);
|
||||
LLVM_ATTRIBUTE_NORETURN void fatal(llvm::Error &Err, const Twine &Prefix);
|
||||
|
||||
template <class T> T check(ErrorOr<T> V, const Twine &Prefix) {
|
||||
if (auto EC = V.getError())
|
||||
fatal(EC, Prefix);
|
||||
return std::move(*V);
|
||||
}
|
||||
|
||||
template <class T> T check(Expected<T> E, const Twine &Prefix) {
|
||||
if (llvm::Error Err = E.takeError())
|
||||
fatal(Err, Prefix);
|
||||
return std::move(*E);
|
||||
}
|
||||
|
||||
template <class T> T check(ErrorOr<T> EO) {
|
||||
if (!EO)
|
||||
fatal(EO.getError().message());
|
||||
return std::move(*EO);
|
||||
}
|
||||
|
||||
template <class T> T check(Expected<T> E) {
|
||||
if (!E) {
|
||||
std::string Buf;
|
||||
llvm::raw_string_ostream OS(Buf);
|
||||
logAllUnhandledErrors(E.takeError(), OS, "");
|
||||
OS.flush();
|
||||
fatal(Buf);
|
||||
}
|
||||
return std::move(*E);
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
258
deps/lld/COFF/ICF.cpp
vendored
Normal file
258
deps/lld/COFF/ICF.cpp
vendored
Normal file
@ -0,0 +1,258 @@
|
||||
//===- ICF.cpp ------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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 "Chunks.h"
|
||||
#include "Error.h"
|
||||
#include "Symbols.h"
|
||||
#include "llvm/ADT/Hashing.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/Parallel.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
class ICF {
|
||||
public:
|
||||
void run(const std::vector<Chunk *> &V);
|
||||
|
||||
private:
|
||||
void segregate(size_t Begin, size_t End, bool Constant);
|
||||
|
||||
bool equalsConstant(const SectionChunk *A, const SectionChunk *B);
|
||||
bool equalsVariable(const SectionChunk *A, const SectionChunk *B);
|
||||
|
||||
uint32_t getHash(SectionChunk *C);
|
||||
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 a hash value for S.
|
||||
uint32_t ICF::getHash(SectionChunk *C) {
|
||||
return hash_combine(C->getPermissions(),
|
||||
hash_value(C->SectionName),
|
||||
C->NumRelocs,
|
||||
C->getAlign(),
|
||||
uint32_t(C->Header->SizeOfRawData),
|
||||
C->Checksum);
|
||||
}
|
||||
|
||||
// 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.
|
||||
bool ICF::isEligible(SectionChunk *C) {
|
||||
bool Global = C->Sym && C->Sym->isExternal();
|
||||
bool Executable = C->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_EXECUTE;
|
||||
bool Writable = C->getPermissions() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
|
||||
return C->isCOMDAT() && C->isLive() && Global && Executable && !Writable;
|
||||
}
|
||||
|
||||
// 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]->Class[(Cnt + 1) % 2] = Mid;
|
||||
|
||||
// If we created a group, we need to iterate the main loop again.
|
||||
if (Mid != End)
|
||||
Repeat = true;
|
||||
|
||||
Begin = Mid;
|
||||
}
|
||||
}
|
||||
|
||||
// Compare "non-moving" part of two sections, namely everything
|
||||
// except relocation targets.
|
||||
bool ICF::equalsConstant(const SectionChunk *A, const SectionChunk *B) {
|
||||
if (A->NumRelocs != B->NumRelocs)
|
||||
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;
|
||||
}
|
||||
SymbolBody *B1 = A->File->getSymbolBody(R1.SymbolTableIndex);
|
||||
SymbolBody *B2 = B->File->getSymbolBody(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()->Class[Cnt % 2] == D2->getChunk()->Class[Cnt % 2];
|
||||
return false;
|
||||
};
|
||||
if (!std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq))
|
||||
return false;
|
||||
|
||||
// Compare section attributes and contents.
|
||||
return A->getPermissions() == B->getPermissions() &&
|
||||
A->SectionName == B->SectionName &&
|
||||
A->getAlign() == B->getAlign() &&
|
||||
A->Header->SizeOfRawData == B->Header->SizeOfRawData &&
|
||||
A->Checksum == B->Checksum &&
|
||||
A->getContents() == B->getContents();
|
||||
}
|
||||
|
||||
// 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) {
|
||||
SymbolBody *B1 = A->File->getSymbolBody(R1.SymbolTableIndex);
|
||||
SymbolBody *B2 = B->File->getSymbolBody(R2.SymbolTableIndex);
|
||||
if (B1 == B2)
|
||||
return true;
|
||||
if (auto *D1 = dyn_cast<DefinedRegular>(B1))
|
||||
if (auto *D2 = dyn_cast<DefinedRegular>(B2))
|
||||
return D1->getChunk()->Class[Cnt % 2] == D2->getChunk()->Class[Cnt % 2];
|
||||
return false;
|
||||
};
|
||||
return std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq);
|
||||
}
|
||||
|
||||
size_t ICF::findBoundary(size_t Begin, size_t End) {
|
||||
for (size_t I = Begin + 1; I < End; ++I)
|
||||
if (Chunks[Begin]->Class[Cnt % 2] != Chunks[I]->Class[Cnt % 2])
|
||||
return I;
|
||||
return End;
|
||||
}
|
||||
|
||||
void ICF::forEachClassRange(size_t Begin, size_t End,
|
||||
std::function<void(size_t, size_t)> Fn) {
|
||||
if (Begin > 0)
|
||||
Begin = findBoundary(Begin - 1, End);
|
||||
|
||||
while (Begin < End) {
|
||||
size_t Mid = findBoundary(Begin, Chunks.size());
|
||||
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;
|
||||
}
|
||||
|
||||
// Split sections into 256 shards and call Fn in parallel.
|
||||
size_t NumShards = 256;
|
||||
size_t Step = Chunks.size() / NumShards;
|
||||
for_each_n(parallel::par, size_t(0), NumShards, [&](size_t I) {
|
||||
size_t End = (I == NumShards - 1) ? Chunks.size() : (I + 1) * Step;
|
||||
forEachClassRange(I * Step, End, 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(const std::vector<Chunk *> &Vec) {
|
||||
// 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->Class[0] = NextId++;
|
||||
}
|
||||
}
|
||||
|
||||
// Initially, we use hash values to partition sections.
|
||||
for (SectionChunk *SC : Chunks)
|
||||
// Set MSB to 1 to avoid collisions with non-hash classs.
|
||||
SC->Class[0] = getHash(SC) | (1 << 31);
|
||||
|
||||
// From now on, sections in Chunks are ordered so that sections in
|
||||
// the same group are consecutive in the vector.
|
||||
std::stable_sort(Chunks.begin(), Chunks.end(),
|
||||
[](SectionChunk *A, SectionChunk *B) {
|
||||
return A->Class[0] < B->Class[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(const std::vector<Chunk *> &Chunks) { ICF().run(Chunks); }
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
411
deps/lld/COFF/InputFiles.cpp
vendored
Normal file
411
deps/lld/COFF/InputFiles.cpp
vendored
Normal file
@ -0,0 +1,411 @@
|
||||
//===- InputFiles.cpp -----------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "InputFiles.h"
|
||||
#include "Chunks.h"
|
||||
#include "Config.h"
|
||||
#include "Driver.h"
|
||||
#include "Error.h"
|
||||
#include "Memory.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.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/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/Target/TargetOptions.h"
|
||||
#include <cstring>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::COFF;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
|
||||
using llvm::Triple;
|
||||
using llvm::support::ulittle32_t;
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
/// Checks that Source is compatible with being a weak alias to Target.
|
||||
/// If Source is Undefined and has no weak alias set, makes it a weak
|
||||
/// alias to Target.
|
||||
static void checkAndSetWeakAlias(SymbolTable *Symtab, InputFile *F,
|
||||
SymbolBody *Source, SymbolBody *Target) {
|
||||
if (auto *U = dyn_cast<Undefined>(Source)) {
|
||||
if (U->WeakAlias && U->WeakAlias != Target)
|
||||
Symtab->reportDuplicate(Source->symbol(), 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), toString(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 " + Sym->getName());
|
||||
|
||||
// Return an empty buffer if we have already returned the same buffer.
|
||||
if (!Seen.insert(C.getChildOffset()).second)
|
||||
return;
|
||||
|
||||
Driver->enqueueArchiveMember(C, Sym->getName(), getName());
|
||||
}
|
||||
|
||||
void ObjectFile::parse() {
|
||||
// Parse a memory buffer as a COFF file.
|
||||
std::unique_ptr<Binary> Bin = check(createBinary(MB), toString(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();
|
||||
initializeSEH();
|
||||
}
|
||||
|
||||
void ObjectFile::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;
|
||||
StringRef Name;
|
||||
if (auto EC = COFFObj->getSection(I, Sec))
|
||||
fatal(EC, "getSection failed: #" + Twine(I));
|
||||
if (auto EC = COFFObj->getSectionName(Sec, Name))
|
||||
fatal(EC, "getSectionName failed: #" + Twine(I));
|
||||
if (Name == ".sxdata") {
|
||||
SXData = Sec;
|
||||
continue;
|
||||
}
|
||||
if (Name == ".drectve") {
|
||||
ArrayRef<uint8_t> Data;
|
||||
COFFObj->getSectionContents(Sec, Data);
|
||||
Directives = std::string((const char *)Data.data(), Data.size());
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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 a linker support. We need to interpret and debug
|
||||
// info, and then write it to a separate .pdb file.
|
||||
|
||||
// Ignore debug info unless /debug is given.
|
||||
if (!Config->Debug && Name.startswith(".debug"))
|
||||
continue;
|
||||
|
||||
if (Sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
|
||||
continue;
|
||||
auto *C = make<SectionChunk>(this, Sec);
|
||||
|
||||
// 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
|
||||
Chunks.push_back(C);
|
||||
|
||||
SparseChunks[I] = C;
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectFile::initializeSymbols() {
|
||||
uint32_t NumSymbols = COFFObj->getNumberOfSymbols();
|
||||
SymbolBodies.reserve(NumSymbols);
|
||||
SparseSymbolBodies.resize(NumSymbols);
|
||||
|
||||
SmallVector<std::pair<SymbolBody *, uint32_t>, 8> WeakAliases;
|
||||
int32_t LastSectionNumber = 0;
|
||||
|
||||
for (uint32_t I = 0; I < NumSymbols; ++I) {
|
||||
// Get a COFFSymbolRef object.
|
||||
ErrorOr<COFFSymbolRef> SymOrErr = COFFObj->getSymbol(I);
|
||||
if (!SymOrErr)
|
||||
fatal(SymOrErr.getError(), "broken object file: " + toString(this));
|
||||
COFFSymbolRef Sym = *SymOrErr;
|
||||
|
||||
const void *AuxP = nullptr;
|
||||
if (Sym.getNumberOfAuxSymbols())
|
||||
AuxP = COFFObj->getSymbol(I + 1)->getRawPtr();
|
||||
bool IsFirst = (LastSectionNumber != Sym.getSectionNumber());
|
||||
|
||||
SymbolBody *Body = nullptr;
|
||||
if (Sym.isUndefined()) {
|
||||
Body = createUndefined(Sym);
|
||||
} else if (Sym.isWeakExternal()) {
|
||||
Body = createUndefined(Sym);
|
||||
uint32_t TagIndex =
|
||||
static_cast<const coff_aux_weak_external *>(AuxP)->TagIndex;
|
||||
WeakAliases.emplace_back(Body, TagIndex);
|
||||
} else {
|
||||
Body = createDefined(Sym, AuxP, IsFirst);
|
||||
}
|
||||
if (Body) {
|
||||
SymbolBodies.push_back(Body);
|
||||
SparseSymbolBodies[I] = Body;
|
||||
}
|
||||
I += Sym.getNumberOfAuxSymbols();
|
||||
LastSectionNumber = Sym.getSectionNumber();
|
||||
}
|
||||
|
||||
for (auto &KV : WeakAliases) {
|
||||
SymbolBody *Sym = KV.first;
|
||||
uint32_t Idx = KV.second;
|
||||
checkAndSetWeakAlias(Symtab, this, Sym, SparseSymbolBodies[Idx]);
|
||||
}
|
||||
}
|
||||
|
||||
SymbolBody *ObjectFile::createUndefined(COFFSymbolRef Sym) {
|
||||
StringRef Name;
|
||||
COFFObj->getSymbolName(Sym, Name);
|
||||
return Symtab->addUndefined(Name, this, Sym.isWeakExternal())->body();
|
||||
}
|
||||
|
||||
SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
|
||||
bool IsFirst) {
|
||||
StringRef Name;
|
||||
if (Sym.isCommon()) {
|
||||
auto *C = make<CommonChunk>(Sym);
|
||||
Chunks.push_back(C);
|
||||
COFFObj->getSymbolName(Sym, Name);
|
||||
Symbol *S =
|
||||
Symtab->addCommon(this, Name, Sym.getValue(), Sym.getGeneric(), C);
|
||||
return S->body();
|
||||
}
|
||||
if (Sym.isAbsolute()) {
|
||||
COFFObj->getSymbolName(Sym, Name);
|
||||
// Skip special symbols.
|
||||
if (Name == "@comp.id")
|
||||
return nullptr;
|
||||
// COFF spec 5.10.1. The .sxdata section.
|
||||
if (Name == "@feat.00") {
|
||||
if (Sym.getValue() & 1)
|
||||
SEHCompat = true;
|
||||
return nullptr;
|
||||
}
|
||||
if (Sym.isExternal())
|
||||
return Symtab->addAbsolute(Name, Sym)->body();
|
||||
else
|
||||
return make<DefinedAbsolute>(Name, Sym);
|
||||
}
|
||||
int32_t SectionNumber = Sym.getSectionNumber();
|
||||
if (SectionNumber == llvm::COFF::IMAGE_SYM_DEBUG)
|
||||
return nullptr;
|
||||
|
||||
// Reserved sections numbers don't have contents.
|
||||
if (llvm::COFF::isReservedSectionNumber(SectionNumber))
|
||||
fatal("broken object file: " + toString(this));
|
||||
|
||||
// This symbol references a section which is not present in the section
|
||||
// header.
|
||||
if ((uint32_t)SectionNumber >= SparseChunks.size())
|
||||
fatal("broken object file: " + toString(this));
|
||||
|
||||
// Nothing else to do without a section chunk.
|
||||
auto *SC = cast_or_null<SectionChunk>(SparseChunks[SectionNumber]);
|
||||
if (!SC)
|
||||
return nullptr;
|
||||
|
||||
// Handle section definitions
|
||||
if (IsFirst && AuxP) {
|
||||
auto *Aux = reinterpret_cast<const coff_aux_section_definition *>(AuxP);
|
||||
if (Aux->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
|
||||
if (auto *ParentSC = cast_or_null<SectionChunk>(
|
||||
SparseChunks[Aux->getNumber(Sym.isBigObj())])) {
|
||||
ParentSC->addAssociative(SC);
|
||||
// If we already discarded the parent, discard the child.
|
||||
if (ParentSC->isDiscarded())
|
||||
SC->markDiscarded();
|
||||
}
|
||||
SC->Checksum = Aux->CheckSum;
|
||||
}
|
||||
|
||||
DefinedRegular *B;
|
||||
if (Sym.isExternal()) {
|
||||
COFFObj->getSymbolName(Sym, Name);
|
||||
Symbol *S =
|
||||
Symtab->addRegular(this, Name, SC->isCOMDAT(), Sym.getGeneric(), SC);
|
||||
B = cast<DefinedRegular>(S->body());
|
||||
} else
|
||||
B = make<DefinedRegular>(this, /*Name*/ "", SC->isCOMDAT(),
|
||||
/*IsExternal*/ false, Sym.getGeneric(), SC);
|
||||
if (SC->isCOMDAT() && Sym.getValue() == 0 && !AuxP)
|
||||
SC->setSymbol(B);
|
||||
|
||||
return B;
|
||||
}
|
||||
|
||||
void ObjectFile::initializeSEH() {
|
||||
if (!SEHCompat || !SXData)
|
||||
return;
|
||||
ArrayRef<uint8_t> A;
|
||||
COFFObj->getSectionContents(SXData, A);
|
||||
if (A.size() % 4 != 0)
|
||||
fatal(".sxdata must be an array of symbol table indices");
|
||||
auto *I = reinterpret_cast<const ulittle32_t *>(A.data());
|
||||
auto *E = reinterpret_cast<const ulittle32_t *>(A.data() + A.size());
|
||||
for (; I != E; ++I)
|
||||
SEHandlers.insert(SparseSymbolBodies[*I]);
|
||||
}
|
||||
|
||||
MachineTypes ObjectFile::getMachineType() {
|
||||
if (COFFObj)
|
||||
return static_cast<MachineTypes>(COFFObj->getMachine());
|
||||
return IMAGE_FILE_MACHINE_UNKNOWN;
|
||||
}
|
||||
|
||||
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 char *End = MB.getBufferEnd();
|
||||
const auto *Hdr = reinterpret_cast<const coff_import_header *>(Buf);
|
||||
|
||||
// Check if the total size is valid.
|
||||
if ((size_t)(End - Buf) != (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 = cast<DefinedImportData>(
|
||||
Symtab->addImportData(ImpName, this)->body());
|
||||
if (Hdr->getType() == llvm::COFF::IMPORT_CONST)
|
||||
ConstSym =
|
||||
cast<DefinedImportData>(Symtab->addImportData(Name, this)->body());
|
||||
|
||||
// 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)
|
||||
return;
|
||||
ThunkSym = cast<DefinedImportThunk>(
|
||||
Symtab->addImportThunk(Name, ImpSym, Hdr->Machine)->body());
|
||||
}
|
||||
|
||||
void BitcodeFile::parse() {
|
||||
Obj = check(lto::InputFile::create(MemoryBufferRef(
|
||||
MB.getBuffer(), Saver.save(ParentName + MB.getBufferIdentifier()))));
|
||||
for (const lto::InputFile::Symbol &ObjSym : Obj->symbols()) {
|
||||
StringRef SymName = Saver.save(ObjSym.getName());
|
||||
Symbol *Sym;
|
||||
if (ObjSym.isUndefined()) {
|
||||
Sym = Symtab->addUndefined(SymName, this, false);
|
||||
} else if (ObjSym.isCommon()) {
|
||||
Sym = Symtab->addCommon(this, SymName, ObjSym.getCommonSize());
|
||||
} else if (ObjSym.isWeak() && ObjSym.isIndirect()) {
|
||||
// Weak external.
|
||||
Sym = Symtab->addUndefined(SymName, this, true);
|
||||
std::string Fallback = ObjSym.getCOFFWeakExternalFallback();
|
||||
SymbolBody *Alias = Symtab->addUndefined(Saver.save(Fallback));
|
||||
checkAndSetWeakAlias(Symtab, this, Sym->body(), Alias);
|
||||
} else {
|
||||
bool IsCOMDAT = ObjSym.getComdatIndex() != -1;
|
||||
Sym = Symtab->addRegular(this, SymName, IsCOMDAT);
|
||||
}
|
||||
SymbolBodies.push_back(Sym->body());
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
// Returns the last element of a path, which is supposed to be a filename.
|
||||
static StringRef getBasename(StringRef Path) {
|
||||
size_t Pos = Path.find_last_of("\\/");
|
||||
if (Pos == StringRef::npos)
|
||||
return Path;
|
||||
return Path.substr(Pos + 1);
|
||||
}
|
||||
|
||||
// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)".
|
||||
std::string lld::toString(coff::InputFile *File) {
|
||||
if (!File)
|
||||
return "(internal)";
|
||||
if (File->ParentName.empty())
|
||||
return File->getName().lower();
|
||||
|
||||
std::string Res =
|
||||
(getBasename(File->ParentName) + "(" + getBasename(File->getName()) + ")")
|
||||
.str();
|
||||
return StringRef(Res).lower();
|
||||
}
|
||||
223
deps/lld/COFF/InputFiles.h
vendored
Normal file
223
deps/lld/COFF/InputFiles.h
vendored
Normal file
@ -0,0 +1,223 @@
|
||||
//===- InputFiles.h ---------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_COFF_INPUT_FILES_H
|
||||
#define LLD_COFF_INPUT_FILES_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/DenseSet.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 {
|
||||
|
||||
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 Lazy;
|
||||
class SectionChunk;
|
||||
struct Symbol;
|
||||
class SymbolBody;
|
||||
class Undefined;
|
||||
|
||||
// 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() { 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 StringRef(Directives).trim(); }
|
||||
|
||||
protected:
|
||||
InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {}
|
||||
|
||||
std::string 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;
|
||||
std::string Filename;
|
||||
llvm::DenseSet<uint64_t> Seen;
|
||||
};
|
||||
|
||||
// .obj or .o file. This may be a member of an archive file.
|
||||
class ObjectFile : public InputFile {
|
||||
public:
|
||||
explicit ObjectFile(MemoryBufferRef M) : InputFile(ObjectKind, M) {}
|
||||
static bool classof(const InputFile *F) { return F->kind() == ObjectKind; }
|
||||
void parse() override;
|
||||
MachineTypes getMachineType() override;
|
||||
std::vector<Chunk *> &getChunks() { return Chunks; }
|
||||
std::vector<SectionChunk *> &getDebugChunks() { return DebugChunks; }
|
||||
std::vector<SymbolBody *> &getSymbols() { return SymbolBodies; }
|
||||
|
||||
// Returns a SymbolBody object for the SymbolIndex'th symbol in the
|
||||
// underlying object file.
|
||||
SymbolBody *getSymbolBody(uint32_t SymbolIndex) {
|
||||
return SparseSymbolBodies[SymbolIndex];
|
||||
}
|
||||
|
||||
// Returns the underying COFF file.
|
||||
COFFObjectFile *getCOFFObj() { return COFFObj.get(); }
|
||||
|
||||
// True if this object file is compatible with SEH.
|
||||
// COFF-specific and x86-only.
|
||||
bool SEHCompat = false;
|
||||
|
||||
// The list of safe exception handlers listed in .sxdata section.
|
||||
// COFF-specific and x86-only.
|
||||
std::set<SymbolBody *> SEHandlers;
|
||||
|
||||
// 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;
|
||||
|
||||
private:
|
||||
void initializeChunks();
|
||||
void initializeSymbols();
|
||||
void initializeSEH();
|
||||
|
||||
SymbolBody *createDefined(COFFSymbolRef Sym, const void *Aux, bool IsFirst);
|
||||
SymbolBody *createUndefined(COFFSymbolRef Sym);
|
||||
|
||||
std::unique_ptr<COFFObjectFile> COFFObj;
|
||||
const coff_section *SXData = nullptr;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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<Chunk *> SparseChunks;
|
||||
|
||||
// List of all symbols referenced or defined by this file.
|
||||
std::vector<SymbolBody *> SymbolBodies;
|
||||
|
||||
// This vector contains the same symbols as SymbolBodies, but they
|
||||
// are indexed such that you can get a SymbolBody by symbol
|
||||
// index. Nonexistent indices (which are occupied by auxiliary
|
||||
// symbols in the real symbol table) are filled with null pointers.
|
||||
std::vector<SymbolBody *> SparseSymbolBodies;
|
||||
};
|
||||
|
||||
// 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), Live(!Config->DoGC) {}
|
||||
|
||||
static bool classof(const InputFile *F) { return F->kind() == ImportKind; }
|
||||
|
||||
DefinedImportData *ImpSym = nullptr;
|
||||
DefinedImportData *ConstSym = nullptr;
|
||||
DefinedImportThunk *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.
|
||||
// This "Live" bit is 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.
|
||||
bool Live;
|
||||
};
|
||||
|
||||
// Used for LTO.
|
||||
class BitcodeFile : public InputFile {
|
||||
public:
|
||||
explicit BitcodeFile(MemoryBufferRef M) : InputFile(BitcodeKind, M) {}
|
||||
static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }
|
||||
std::vector<SymbolBody *> &getSymbols() { return SymbolBodies; }
|
||||
MachineTypes getMachineType() override;
|
||||
std::unique_ptr<llvm::lto::InputFile> Obj;
|
||||
|
||||
private:
|
||||
void parse() override;
|
||||
|
||||
std::vector<SymbolBody *> SymbolBodies;
|
||||
};
|
||||
} // namespace coff
|
||||
|
||||
std::string toString(coff::InputFile *File);
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
140
deps/lld/COFF/LTO.cpp
vendored
Normal file
140
deps/lld/COFF/LTO.cpp
vendored
Normal file
@ -0,0 +1,140 @@
|
||||
//===- LTO.cpp ------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "LTO.h"
|
||||
#include "Config.h"
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "lld/Core/TargetOptionsCommandFlags.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/IR/DiagnosticPrinter.h"
|
||||
#include "llvm/LTO/Config.h"
|
||||
#include "llvm/LTO/LTO.h"
|
||||
#include "llvm/Object/SymbolicFile.h"
|
||||
#include "llvm/Support/CodeGen.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <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;
|
||||
|
||||
static void diagnosticHandler(const DiagnosticInfo &DI) {
|
||||
SmallString<128> ErrStorage;
|
||||
raw_svector_ostream OS(ErrStorage);
|
||||
DiagnosticPrinterRawOStream DP(OS);
|
||||
DI.print(DP);
|
||||
warn(ErrStorage);
|
||||
}
|
||||
|
||||
static void checkError(Error E) {
|
||||
handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) -> Error {
|
||||
error(EIB.message());
|
||||
return Error::success();
|
||||
});
|
||||
}
|
||||
|
||||
static void saveBuffer(StringRef Buffer, const Twine &Path) {
|
||||
std::error_code EC;
|
||||
raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None);
|
||||
if (EC)
|
||||
error("cannot create " + Path + ": " + EC.message());
|
||||
OS << Buffer;
|
||||
}
|
||||
|
||||
static std::unique_ptr<lto::LTO> createLTO() {
|
||||
lto::Config Conf;
|
||||
Conf.Options = InitTargetOptionsFromCodeGenFlags();
|
||||
Conf.RelocModel = Reloc::PIC_;
|
||||
Conf.DisableVerify = true;
|
||||
Conf.DiagHandler = diagnosticHandler;
|
||||
Conf.OptLevel = Config->LTOOptLevel;
|
||||
if (Config->SaveTemps)
|
||||
checkError(Conf.addSaveTemps(std::string(Config->OutputFile) + ".",
|
||||
/*UseInputModulePath*/ true));
|
||||
lto::ThinBackend Backend;
|
||||
if (Config->LTOJobs != 0)
|
||||
Backend = lto::createInProcessThinBackend(Config->LTOJobs);
|
||||
return llvm::make_unique<lto::LTO>(std::move(Conf), Backend,
|
||||
Config->LTOPartitions);
|
||||
}
|
||||
|
||||
BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) {}
|
||||
|
||||
BitcodeCompiler::~BitcodeCompiler() = default;
|
||||
|
||||
static void undefine(Symbol *S) {
|
||||
replaceBody<Undefined>(S, S->body()->getName());
|
||||
}
|
||||
|
||||
void BitcodeCompiler::add(BitcodeFile &F) {
|
||||
lto::InputFile &Obj = *F.Obj;
|
||||
unsigned SymNum = 0;
|
||||
std::vector<SymbolBody *> SymBodies = F.getSymbols();
|
||||
std::vector<lto::SymbolResolution> Resols(SymBodies.size());
|
||||
|
||||
// Provide a resolution to the LTO API for each symbol.
|
||||
for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) {
|
||||
SymbolBody *B = SymBodies[SymNum];
|
||||
Symbol *Sym = B->symbol();
|
||||
lto::SymbolResolution &R = Resols[SymNum];
|
||||
++SymNum;
|
||||
|
||||
// Ideally we shouldn't check for SF_Undefined but currently IRObjectFile
|
||||
// reports two symbols for module ASM defined. Without this check, lld
|
||||
// flags an undefined in IR with a definition in ASM as prevailing.
|
||||
// Once IRObjectFile is fixed to report only one symbol this hack can
|
||||
// be removed.
|
||||
R.Prevailing = !ObjSym.isUndefined() && B->getFile() == &F;
|
||||
R.VisibleToRegularObj = Sym->IsUsedInRegularObj;
|
||||
if (R.Prevailing)
|
||||
undefine(Sym);
|
||||
}
|
||||
checkError(LTOObj->add(std::move(F.Obj), Resols));
|
||||
}
|
||||
|
||||
// Merge all the bitcode files we have seen, codegen the result
|
||||
// and return the resulting objects.
|
||||
std::vector<StringRef> BitcodeCompiler::compile() {
|
||||
unsigned MaxTasks = LTOObj->getMaxTasks();
|
||||
Buff.resize(MaxTasks);
|
||||
|
||||
checkError(LTOObj->run([&](size_t Task) {
|
||||
return llvm::make_unique<lto::NativeObjectStream>(
|
||||
llvm::make_unique<raw_svector_ostream>(Buff[Task]));
|
||||
}));
|
||||
|
||||
std::vector<StringRef> Ret;
|
||||
for (unsigned I = 0; I != MaxTasks; ++I) {
|
||||
if (Buff[I].empty())
|
||||
continue;
|
||||
if (Config->SaveTemps) {
|
||||
if (I == 0)
|
||||
saveBuffer(Buff[I], Config->OutputFile + ".lto.obj");
|
||||
else
|
||||
saveBuffer(Buff[I], Config->OutputFile + Twine(I) + ".lto.obj");
|
||||
}
|
||||
Ret.emplace_back(Buff[I].data(), Buff[I].size());
|
||||
}
|
||||
return Ret;
|
||||
}
|
||||
56
deps/lld/COFF/LTO.h
vendored
Normal file
56
deps/lld/COFF/LTO.h
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
//===- LTO.h ----------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file provides a way to combine bitcode files into one COFF
|
||||
// file by compiling them using LLVM.
|
||||
//
|
||||
// If LTO is in use, your input files are not in regular COFF files
|
||||
// but instead LLVM bitcode files. In that case, the linker has to
|
||||
// convert bitcode files into the native format so that we can create
|
||||
// a COFF file that contains native code. This file provides that
|
||||
// functionality.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_COFF_LTO_H
|
||||
#define LLD_COFF_LTO_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include <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>> Buff;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
125
deps/lld/COFF/MapFile.cpp
vendored
Normal file
125
deps/lld/COFF/MapFile.cpp
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
//===- MapFile.cpp --------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the /lldmap option. It shows lists in order and
|
||||
// hierarchically the output sections, input sections, input files and
|
||||
// symbol:
|
||||
//
|
||||
// Address Size Align Out 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 "Error.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.h"
|
||||
#include "Writer.h"
|
||||
|
||||
#include "llvm/Support/Parallel.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::coff;
|
||||
|
||||
typedef DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>
|
||||
SymbolMapTy;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
static std::string indent(int Depth) { return std::string(Depth * 8, ' '); }
|
||||
|
||||
// Returns a list of all symbols that we want to print out.
|
||||
static std::vector<DefinedRegular *> getSymbols() {
|
||||
std::vector<DefinedRegular *> V;
|
||||
for (coff::ObjectFile *File : Symtab->ObjectFiles)
|
||||
for (SymbolBody *B : File->getSymbols())
|
||||
if (auto *Sym = dyn_cast<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());
|
||||
for_each_n(parallel::par, (size_t)0, Syms.size(), [&](size_t I) {
|
||||
raw_string_ostream OS(Str[I]);
|
||||
writeHeader(OS, Syms[I]->getRVA(), 0, 0);
|
||||
OS << indent(2) << 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->getName() << '\n';
|
||||
|
||||
for (Chunk *C : Sec->getChunks()) {
|
||||
auto *SC = dyn_cast<SectionChunk>(C);
|
||||
if (!SC)
|
||||
continue;
|
||||
|
||||
writeHeader(OS, SC->getRVA(), SC->getSize(), SC->getAlign());
|
||||
OS << indent(1) << SC->File->getName() << ":(" << SC->getSectionName()
|
||||
<< ")\n";
|
||||
for (DefinedRegular *Sym : SectionSyms[SC])
|
||||
OS << SymStr[Sym] << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
22
deps/lld/COFF/MapFile.h
vendored
Normal file
22
deps/lld/COFF/MapFile.h
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
//===- MapFile.h ------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_COFF_MAPFILE_H
|
||||
#define LLD_COFF_MAPFILE_H
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
class OutputSection;
|
||||
void writeMapFile(llvm::ArrayRef<OutputSection *> OutputSections);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
75
deps/lld/COFF/MarkLive.cpp
vendored
Normal file
75
deps/lld/COFF/MarkLive.cpp
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
//===- MarkLive.cpp -------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Chunks.h"
|
||||
#include "Symbols.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
// Set live bit on for each reachable chunk. Unmarked (unreachable)
|
||||
// COMDAT chunks will be ignored by Writer, so they will be excluded
|
||||
// from the final output.
|
||||
void markLive(const std::vector<Chunk *> &Chunks) {
|
||||
// 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->isLive())
|
||||
Worklist.push_back(SC);
|
||||
|
||||
auto Enqueue = [&](SectionChunk *C) {
|
||||
if (C->isLive())
|
||||
return;
|
||||
C->markLive();
|
||||
Worklist.push_back(C);
|
||||
};
|
||||
|
||||
auto AddSym = [&](SymbolBody *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 = true;
|
||||
};
|
||||
|
||||
// Add GC root chunks.
|
||||
for (SymbolBody *B : Config->GCRoot)
|
||||
AddSym(B);
|
||||
|
||||
while (!Worklist.empty()) {
|
||||
SectionChunk *SC = Worklist.pop_back_val();
|
||||
|
||||
// If this section was discarded, there are relocations referring to
|
||||
// discarded sections. Ignore these sections to avoid crashing. They will be
|
||||
// diagnosed during relocation processing.
|
||||
if (SC->isDiscarded())
|
||||
continue;
|
||||
|
||||
assert(SC->isLive() && "We mark as live when pushing onto the worklist!");
|
||||
|
||||
// Mark all symbols listed in the relocation table for this section.
|
||||
for (SymbolBody *B : SC->symbols())
|
||||
AddSym(B);
|
||||
|
||||
// Mark associative sections if any.
|
||||
for (SectionChunk *C : SC->children())
|
||||
Enqueue(C);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
52
deps/lld/COFF/Memory.h
vendored
Normal file
52
deps/lld/COFF/Memory.h
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
//===- Memory.h -------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// See ELF/Memory.h
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_COFF_MEMORY_H
|
||||
#define LLD_COFF_MEMORY_H
|
||||
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "llvm/Support/StringSaver.h"
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
extern llvm::BumpPtrAllocator BAlloc;
|
||||
extern llvm::StringSaver Saver;
|
||||
|
||||
struct SpecificAllocBase {
|
||||
SpecificAllocBase() { Instances.push_back(this); }
|
||||
virtual ~SpecificAllocBase() = default;
|
||||
virtual void reset() = 0;
|
||||
static std::vector<SpecificAllocBase *> Instances;
|
||||
};
|
||||
|
||||
template <class T> struct SpecificAlloc : public SpecificAllocBase {
|
||||
void reset() override { Alloc.DestroyAll(); }
|
||||
llvm::SpecificBumpPtrAllocator<T> Alloc;
|
||||
};
|
||||
|
||||
template <typename T, typename... U> T *make(U &&... Args) {
|
||||
static SpecificAlloc<T> Alloc;
|
||||
return new (Alloc.Alloc.Allocate()) T(std::forward<U>(Args)...);
|
||||
}
|
||||
|
||||
inline void freeArena() {
|
||||
for (SpecificAllocBase *Alloc : SpecificAllocBase::Instances)
|
||||
Alloc->reset();
|
||||
BAlloc.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
139
deps/lld/COFF/Options.td
vendored
Normal file
139
deps/lld/COFF/Options.td
vendored
Normal file
@ -0,0 +1,139 @@
|
||||
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 suffixed by ":no".
|
||||
multiclass B<string name, string help> {
|
||||
def "" : F<name>;
|
||||
def _no : F<name#":no">, HelpText<help>;
|
||||
}
|
||||
|
||||
def align : P<"align", "Section alignment">;
|
||||
def alternatename : P<"alternatename", "Define weak alias">;
|
||||
def base : P<"base", "Base address of the program">;
|
||||
def defaultlib : P<"defaultlib", "Add the library to the list of input files">;
|
||||
def delayload : P<"delayload", "Delay loaded DLL name">;
|
||||
def entry : P<"entry", "Name of entry point symbol">;
|
||||
def errorlimit : P<"errorlimit",
|
||||
"Maximum number of errors to emit before stopping (0 = no limit)">;
|
||||
def export : P<"export", "Export a function">;
|
||||
// No help text because /failifmismatch is not intended to be used by the user.
|
||||
def failifmismatch : P<"failifmismatch", "">;
|
||||
def heap : P<"heap", "Size of the heap">;
|
||||
def implib : P<"implib", "Import library name">;
|
||||
def libpath : P<"libpath", "Additional library search path">;
|
||||
def linkrepro : P<"linkrepro", "Dump linker invocation and input files for debugging">;
|
||||
def lldsavetemps : F<"lldsavetemps">,
|
||||
HelpText<"Save temporary files instead of deleting them">;
|
||||
def machine : P<"machine", "Specify target platform">;
|
||||
def merge : P<"merge", "Combine sections">;
|
||||
def mllvm : P<"mllvm", "Options to pass to LLVM">;
|
||||
def nodefaultlib : P<"nodefaultlib", "Remove a default library">;
|
||||
def opt : P<"opt", "Control optimizations">;
|
||||
def out : P<"out", "Path to file to write output">;
|
||||
def pdb : P<"pdb", "PDB file path">;
|
||||
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 version : P<"version", "Specify a version number in the PE header">;
|
||||
|
||||
def disallowlib : Joined<["/", "-", "-?"], "disallowlib:">, Alias<nodefaultlib>;
|
||||
|
||||
def manifest : F<"manifest">;
|
||||
def manifest_colon : P<"manifest", "Create manifest file">;
|
||||
def manifestuac : P<"manifestuac", "User access control">;
|
||||
def manifestfile : P<"manifestfile", "Manifest file path">;
|
||||
def manifestdependency : P<"manifestdependency",
|
||||
"Attributes for <dependency> in manifest file">;
|
||||
def manifestinput : P<"manifestinput", "Specify manifest file">;
|
||||
|
||||
// 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 debugtype : P<"debugtype", "Debug Info Options">;
|
||||
def dll : F<"dll">, HelpText<"Create a DLL">;
|
||||
def driver : P<"driver", "Generate a Windows NT Kernel Mode Driver">;
|
||||
def nodefaultlib_all : F<"nodefaultlib">;
|
||||
def noentry : F<"noentry">;
|
||||
def profile : F<"profile">;
|
||||
def swaprun_cd : F<"swaprun:cd">;
|
||||
def swaprun_net : F<"swaprun:net">;
|
||||
def verbose : F<"verbose">;
|
||||
|
||||
def force : F<"force">,
|
||||
HelpText<"Allow undefined symbols when creating executables">;
|
||||
def force_unresolved : F<"force:unresolved">;
|
||||
|
||||
defm allowbind: B<"allowbind", "Disable DLL binding">;
|
||||
defm allowisolation : B<"allowisolation", "Set NO_ISOLATION bit">;
|
||||
defm appcontainer : B<"appcontainer",
|
||||
"Image can only be run in an app container">;
|
||||
defm dynamicbase : B<"dynamicbase",
|
||||
"Disable address space layout randomization">;
|
||||
defm fixed : B<"fixed", "Enable base relocations">;
|
||||
defm highentropyva : B<"highentropyva", "Set HIGH_ENTROPY_VA bit">;
|
||||
defm largeaddressaware : B<"largeaddressaware", "Disable large addresses">;
|
||||
defm nxcompat : B<"nxcompat", "Disable data execution provention">;
|
||||
defm safeseh : B<"safeseh", "Produce an image with Safe Exception Handler">;
|
||||
defm tsaware : B<"tsaware", "Create non-Terminal Server aware executable">;
|
||||
|
||||
def help : F<"help">;
|
||||
def help_q : Flag<["/?", "-?"], "">, Alias<help>;
|
||||
|
||||
// LLD extensions
|
||||
def nopdb : F<"nopdb">, HelpText<"Disable PDB generation for DWARF users">;
|
||||
def nosymtab : F<"nosymtab">;
|
||||
def msvclto : F<"msvclto">;
|
||||
|
||||
// Flags for debugging
|
||||
def lldmap : F<"lldmap">;
|
||||
def lldmap_file : Joined<["/", "-"], "lldmap:">;
|
||||
|
||||
//==============================================================================
|
||||
// The flags below do nothing. They are defined only for link.exe compatibility.
|
||||
//==============================================================================
|
||||
|
||||
class QF<string name> : Joined<["/", "-", "-?"], name#":">;
|
||||
|
||||
multiclass QB<string name> {
|
||||
def "" : F<name>;
|
||||
def _no : F<name#":no">;
|
||||
}
|
||||
|
||||
def functionpadmin : F<"functionpadmin">;
|
||||
def ignoreidl : F<"ignoreidl">;
|
||||
def incremental : F<"incremental">;
|
||||
def no_incremental : F<"incremental:no">;
|
||||
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 ignore : QF<"ignore">;
|
||||
def maxilksize : QF<"maxilksize">;
|
||||
def pdbaltpath : QF<"pdbaltpath">;
|
||||
def tlbid : QF<"tlbid">;
|
||||
def tlbout : QF<"tlbout">;
|
||||
def verbose_all : QF<"verbose">;
|
||||
def guardsym : QF<"guardsym">;
|
||||
|
||||
defm wx : QB<"wx">;
|
||||
672
deps/lld/COFF/PDB.cpp
vendored
Normal file
672
deps/lld/COFF/PDB.cpp
vendored
Normal file
@ -0,0 +1,672 @@
|
||||
//===- PDB.cpp ------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "PDB.h"
|
||||
#include "Chunks.h"
|
||||
#include "Config.h"
|
||||
#include "Error.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.h"
|
||||
#include "llvm/DebugInfo/CodeView/CVDebugRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h"
|
||||
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
|
||||
#include "llvm/DebugInfo/CodeView/SymbolSerializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h"
|
||||
#include "llvm/DebugInfo/MSF/MSFBuilder.h"
|
||||
#include "llvm/DebugInfo/MSF/MSFCommon.h"
|
||||
#include "llvm/DebugInfo/PDB/GenericError.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/PDBStringTableBuilder.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h"
|
||||
#include "llvm/DebugInfo/PDB/PDB.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include "llvm/Support/BinaryByteStream.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/FileOutputBuffer.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/ScopedPrinter.h"
|
||||
#include <memory>
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::coff;
|
||||
using namespace llvm;
|
||||
using namespace llvm::codeview;
|
||||
|
||||
using llvm::object::coff_section;
|
||||
|
||||
static ExitOnError ExitOnErr;
|
||||
|
||||
namespace {
|
||||
/// Map from type index and item index in a type server PDB to the
|
||||
/// corresponding index in the destination PDB.
|
||||
struct CVIndexMap {
|
||||
SmallVector<TypeIndex, 0> TPIMap;
|
||||
SmallVector<TypeIndex, 0> IPIMap;
|
||||
bool IsTypeServerMap = false;
|
||||
};
|
||||
|
||||
class PDBLinker {
|
||||
public:
|
||||
PDBLinker(SymbolTable *Symtab)
|
||||
: Alloc(), Symtab(Symtab), Builder(Alloc), TypeTable(Alloc),
|
||||
IDTable(Alloc) {}
|
||||
|
||||
/// Emit the basic PDB structure: initial streams, headers, etc.
|
||||
void initialize(const llvm::codeview::DebugInfo *DI);
|
||||
|
||||
/// Link CodeView from each object file in the symbol table into the PDB.
|
||||
void addObjectsToPDB();
|
||||
|
||||
/// Link CodeView from a single object file into the PDB.
|
||||
void addObjectFile(ObjectFile *File);
|
||||
|
||||
/// Produce a mapping from the type and item indices used in the object
|
||||
/// file to those in the destination PDB.
|
||||
///
|
||||
/// If the object file uses a type server PDB (compiled with /Zi), merge TPI
|
||||
/// and IPI from the type server PDB and return a map for it. Each unique type
|
||||
/// server PDB is merged at most once, so this may return an existing index
|
||||
/// mapping.
|
||||
///
|
||||
/// If the object does not use a type server PDB (compiled with /Z7), we merge
|
||||
/// all the type and item records from the .debug$S stream and fill in the
|
||||
/// caller-provided ObjectIndexMap.
|
||||
const CVIndexMap &mergeDebugT(ObjectFile *File, CVIndexMap &ObjectIndexMap);
|
||||
|
||||
const CVIndexMap &maybeMergeTypeServerPDB(ObjectFile *File,
|
||||
TypeServer2Record &TS);
|
||||
|
||||
/// Add the section map and section contributions to the PDB.
|
||||
void addSections(ArrayRef<uint8_t> SectionTable);
|
||||
|
||||
/// Write the PDB to disk.
|
||||
void commit();
|
||||
|
||||
private:
|
||||
BumpPtrAllocator Alloc;
|
||||
|
||||
SymbolTable *Symtab;
|
||||
|
||||
pdb::PDBFileBuilder Builder;
|
||||
|
||||
/// Type records that will go into the PDB TPI stream.
|
||||
TypeTableBuilder TypeTable;
|
||||
|
||||
/// Item records that will go into the PDB IPI stream.
|
||||
TypeTableBuilder IDTable;
|
||||
|
||||
/// PDBs use a single global string table for filenames in the file checksum
|
||||
/// table.
|
||||
DebugStringTableSubsection PDBStrTab;
|
||||
|
||||
llvm::SmallString<128> NativePath;
|
||||
|
||||
std::vector<pdb::SecMapEntry> SectionMap;
|
||||
|
||||
/// Type index mappings of type server PDBs that we've loaded so far.
|
||||
std::map<GUID, CVIndexMap> TypeServerIndexMappings;
|
||||
};
|
||||
}
|
||||
|
||||
// Returns a list of all SectionChunks.
|
||||
static void addSectionContribs(SymbolTable *Symtab,
|
||||
pdb::DbiStreamBuilder &DbiBuilder) {
|
||||
for (Chunk *C : Symtab->getChunks())
|
||||
if (auto *SC = dyn_cast<SectionChunk>(C))
|
||||
DbiBuilder.addSectionContrib(SC->File->ModuleDBI, SC->Header);
|
||||
}
|
||||
|
||||
static SectionChunk *findByName(std::vector<SectionChunk *> &Sections,
|
||||
StringRef Name) {
|
||||
for (SectionChunk *C : Sections)
|
||||
if (C->getSectionName() == Name)
|
||||
return C;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static ArrayRef<uint8_t> consumeDebugMagic(ArrayRef<uint8_t> Data,
|
||||
StringRef SecName) {
|
||||
// First 4 bytes are section magic.
|
||||
if (Data.size() < 4)
|
||||
fatal(SecName + " too short");
|
||||
if (support::endian::read32le(Data.data()) != COFF::DEBUG_SECTION_MAGIC)
|
||||
fatal(SecName + " has an invalid magic");
|
||||
return Data.slice(4);
|
||||
}
|
||||
|
||||
static ArrayRef<uint8_t> getDebugSection(ObjectFile *File, StringRef SecName) {
|
||||
if (SectionChunk *Sec = findByName(File->getDebugChunks(), SecName))
|
||||
return consumeDebugMagic(Sec->getContents(), SecName);
|
||||
return {};
|
||||
}
|
||||
|
||||
static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder,
|
||||
TypeTableBuilder &TypeTable) {
|
||||
// Start the TPI or IPI stream header.
|
||||
TpiBuilder.setVersionHeader(pdb::PdbTpiV80);
|
||||
|
||||
// Flatten the in memory type table.
|
||||
TypeTable.ForEachRecord([&](TypeIndex TI, ArrayRef<uint8_t> Rec) {
|
||||
// FIXME: Hash types.
|
||||
TpiBuilder.addTypeRecord(Rec, None);
|
||||
});
|
||||
}
|
||||
|
||||
static Optional<TypeServer2Record>
|
||||
maybeReadTypeServerRecord(CVTypeArray &Types) {
|
||||
auto I = Types.begin();
|
||||
if (I == Types.end())
|
||||
return None;
|
||||
const CVType &Type = *I;
|
||||
if (Type.kind() != LF_TYPESERVER2)
|
||||
return None;
|
||||
TypeServer2Record TS;
|
||||
if (auto EC = TypeDeserializer::deserializeAs(const_cast<CVType &>(Type), TS))
|
||||
fatal(EC, "error reading type server record");
|
||||
return std::move(TS);
|
||||
}
|
||||
|
||||
const CVIndexMap &PDBLinker::mergeDebugT(ObjectFile *File,
|
||||
CVIndexMap &ObjectIndexMap) {
|
||||
ArrayRef<uint8_t> Data = getDebugSection(File, ".debug$T");
|
||||
if (Data.empty())
|
||||
return ObjectIndexMap;
|
||||
|
||||
BinaryByteStream Stream(Data, support::little);
|
||||
CVTypeArray Types;
|
||||
BinaryStreamReader Reader(Stream);
|
||||
if (auto EC = Reader.readArray(Types, Reader.getLength()))
|
||||
fatal(EC, "Reader::readArray failed");
|
||||
|
||||
// Look through type servers. If we've already seen this type server, don't
|
||||
// merge any type information.
|
||||
if (Optional<TypeServer2Record> TS = maybeReadTypeServerRecord(Types))
|
||||
return maybeMergeTypeServerPDB(File, *TS);
|
||||
|
||||
// This is a /Z7 object. Fill in the temporary, caller-provided
|
||||
// ObjectIndexMap.
|
||||
if (auto Err = mergeTypeAndIdRecords(IDTable, TypeTable,
|
||||
ObjectIndexMap.TPIMap, Types))
|
||||
fatal(Err, "codeview::mergeTypeAndIdRecords failed");
|
||||
return ObjectIndexMap;
|
||||
}
|
||||
|
||||
static Expected<std::unique_ptr<pdb::NativeSession>>
|
||||
tryToLoadPDB(const GUID &GuidFromObj, StringRef TSPath) {
|
||||
std::unique_ptr<pdb::IPDBSession> ThisSession;
|
||||
if (auto EC =
|
||||
pdb::loadDataForPDB(pdb::PDB_ReaderType::Native, TSPath, ThisSession))
|
||||
return std::move(EC);
|
||||
|
||||
std::unique_ptr<pdb::NativeSession> NS(
|
||||
static_cast<pdb::NativeSession *>(ThisSession.release()));
|
||||
pdb::PDBFile &File = NS->getPDBFile();
|
||||
auto ExpectedInfo = File.getPDBInfoStream();
|
||||
// All PDB Files should have an Info stream.
|
||||
if (!ExpectedInfo)
|
||||
return ExpectedInfo.takeError();
|
||||
|
||||
// Just because a file with a matching name was found and it was an actual
|
||||
// PDB file doesn't mean it matches. For it to match the InfoStream's GUID
|
||||
// must match the GUID specified in the TypeServer2 record.
|
||||
if (ExpectedInfo->getGuid() != GuidFromObj)
|
||||
return make_error<pdb::GenericError>(
|
||||
pdb::generic_error_code::type_server_not_found, TSPath);
|
||||
|
||||
return std::move(NS);
|
||||
}
|
||||
|
||||
const CVIndexMap &PDBLinker::maybeMergeTypeServerPDB(ObjectFile *File,
|
||||
TypeServer2Record &TS) {
|
||||
// First, check if we already loaded a PDB with this GUID. Return the type
|
||||
// index mapping if we have it.
|
||||
auto Insertion = TypeServerIndexMappings.insert({TS.getGuid(), CVIndexMap()});
|
||||
CVIndexMap &IndexMap = Insertion.first->second;
|
||||
if (!Insertion.second)
|
||||
return IndexMap;
|
||||
|
||||
// Mark this map as a type server map.
|
||||
IndexMap.IsTypeServerMap = true;
|
||||
|
||||
// Check for a PDB at:
|
||||
// 1. The given file path
|
||||
// 2. Next to the object file or archive file
|
||||
auto ExpectedSession = tryToLoadPDB(TS.getGuid(), TS.getName());
|
||||
if (!ExpectedSession) {
|
||||
consumeError(ExpectedSession.takeError());
|
||||
StringRef LocalPath =
|
||||
!File->ParentName.empty() ? File->ParentName : File->getName();
|
||||
SmallString<128> Path = sys::path::parent_path(LocalPath);
|
||||
sys::path::append(
|
||||
Path, sys::path::filename(TS.getName(), sys::path::Style::windows));
|
||||
ExpectedSession = tryToLoadPDB(TS.getGuid(), Path);
|
||||
}
|
||||
if (auto E = ExpectedSession.takeError())
|
||||
fatal(E, "Type server PDB was not found");
|
||||
|
||||
// Merge TPI first, because the IPI stream will reference type indices.
|
||||
auto ExpectedTpi = (*ExpectedSession)->getPDBFile().getPDBTpiStream();
|
||||
if (auto E = ExpectedTpi.takeError())
|
||||
fatal(E, "Type server does not have TPI stream");
|
||||
if (auto Err = mergeTypeRecords(TypeTable, IndexMap.TPIMap,
|
||||
ExpectedTpi->typeArray()))
|
||||
fatal(Err, "codeview::mergeTypeRecords failed");
|
||||
|
||||
// Merge IPI.
|
||||
auto ExpectedIpi = (*ExpectedSession)->getPDBFile().getPDBIpiStream();
|
||||
if (auto E = ExpectedIpi.takeError())
|
||||
fatal(E, "Type server does not have TPI stream");
|
||||
if (auto Err = mergeIdRecords(IDTable, IndexMap.TPIMap, IndexMap.IPIMap,
|
||||
ExpectedIpi->typeArray()))
|
||||
fatal(Err, "codeview::mergeIdRecords failed");
|
||||
|
||||
return IndexMap;
|
||||
}
|
||||
|
||||
static bool remapTypeIndex(TypeIndex &TI, ArrayRef<TypeIndex> TypeIndexMap) {
|
||||
if (TI.isSimple())
|
||||
return true;
|
||||
if (TI.toArrayIndex() >= TypeIndexMap.size())
|
||||
return false;
|
||||
TI = TypeIndexMap[TI.toArrayIndex()];
|
||||
return true;
|
||||
}
|
||||
|
||||
static void remapTypesInSymbolRecord(ObjectFile *File,
|
||||
MutableArrayRef<uint8_t> Contents,
|
||||
const CVIndexMap &IndexMap,
|
||||
ArrayRef<TiReference> TypeRefs) {
|
||||
for (const TiReference &Ref : TypeRefs) {
|
||||
unsigned ByteSize = Ref.Count * sizeof(TypeIndex);
|
||||
if (Contents.size() < Ref.Offset + ByteSize)
|
||||
fatal("symbol record too short");
|
||||
|
||||
// This can be an item index or a type index. Choose the appropriate map.
|
||||
ArrayRef<TypeIndex> TypeOrItemMap = IndexMap.TPIMap;
|
||||
if (Ref.Kind == TiRefKind::IndexRef && IndexMap.IsTypeServerMap)
|
||||
TypeOrItemMap = IndexMap.IPIMap;
|
||||
|
||||
MutableArrayRef<TypeIndex> TIs(
|
||||
reinterpret_cast<TypeIndex *>(Contents.data() + Ref.Offset), Ref.Count);
|
||||
for (TypeIndex &TI : TIs) {
|
||||
if (!remapTypeIndex(TI, TypeOrItemMap)) {
|
||||
TI = TypeIndex(SimpleTypeKind::NotTranslated);
|
||||
log("ignoring symbol record in " + File->getName() +
|
||||
" with bad type index 0x" + utohexstr(TI.getIndex()));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// MSVC translates S_PROC_ID_END to S_END.
|
||||
uint16_t canonicalizeSymbolKind(SymbolKind Kind) {
|
||||
if (Kind == SymbolKind::S_PROC_ID_END)
|
||||
return SymbolKind::S_END;
|
||||
return Kind;
|
||||
}
|
||||
|
||||
/// Copy the symbol record. In a PDB, symbol records must be 4 byte aligned.
|
||||
/// The object file may not be aligned.
|
||||
static MutableArrayRef<uint8_t> copySymbolForPdb(const CVSymbol &Sym,
|
||||
BumpPtrAllocator &Alloc) {
|
||||
size_t Size = alignTo(Sym.length(), alignOf(CodeViewContainer::Pdb));
|
||||
assert(Size >= 4 && "record too short");
|
||||
assert(Size <= MaxRecordLength && "record too long");
|
||||
void *Mem = Alloc.Allocate(Size, 4);
|
||||
|
||||
// Copy the symbol record and zero out any padding bytes.
|
||||
MutableArrayRef<uint8_t> NewData(reinterpret_cast<uint8_t *>(Mem), Size);
|
||||
memcpy(NewData.data(), Sym.data().data(), Sym.length());
|
||||
memset(NewData.data() + Sym.length(), 0, Size - Sym.length());
|
||||
|
||||
// Update the record prefix length. It should point to the beginning of the
|
||||
// next record. MSVC does some canonicalization of the record kind, so we do
|
||||
// that as well.
|
||||
auto *Prefix = reinterpret_cast<RecordPrefix *>(Mem);
|
||||
Prefix->RecordKind = canonicalizeSymbolKind(Sym.kind());
|
||||
Prefix->RecordLen = Size - 2;
|
||||
return NewData;
|
||||
}
|
||||
|
||||
/// Return true if this symbol opens a scope. This implies that the symbol has
|
||||
/// "parent" and "end" fields, which contain the offset of the S_END or
|
||||
/// S_INLINESITE_END record.
|
||||
static bool symbolOpensScope(SymbolKind Kind) {
|
||||
switch (Kind) {
|
||||
case SymbolKind::S_GPROC32:
|
||||
case SymbolKind::S_LPROC32:
|
||||
case SymbolKind::S_LPROC32_ID:
|
||||
case SymbolKind::S_GPROC32_ID:
|
||||
case SymbolKind::S_BLOCK32:
|
||||
case SymbolKind::S_SEPCODE:
|
||||
case SymbolKind::S_THUNK32:
|
||||
case SymbolKind::S_INLINESITE:
|
||||
case SymbolKind::S_INLINESITE2:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool symbolEndsScope(SymbolKind Kind) {
|
||||
switch (Kind) {
|
||||
case SymbolKind::S_END:
|
||||
case SymbolKind::S_PROC_ID_END:
|
||||
case SymbolKind::S_INLINESITE_END:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
struct ScopeRecord {
|
||||
ulittle32_t PtrParent;
|
||||
ulittle32_t PtrEnd;
|
||||
};
|
||||
|
||||
struct SymbolScope {
|
||||
ScopeRecord *OpeningRecord;
|
||||
uint32_t ScopeOffset;
|
||||
};
|
||||
|
||||
static void scopeStackOpen(SmallVectorImpl<SymbolScope> &Stack,
|
||||
uint32_t CurOffset, CVSymbol &Sym) {
|
||||
assert(symbolOpensScope(Sym.kind()));
|
||||
SymbolScope S;
|
||||
S.ScopeOffset = CurOffset;
|
||||
S.OpeningRecord = const_cast<ScopeRecord *>(
|
||||
reinterpret_cast<const ScopeRecord *>(Sym.content().data()));
|
||||
S.OpeningRecord->PtrParent = Stack.empty() ? 0 : Stack.back().ScopeOffset;
|
||||
Stack.push_back(S);
|
||||
}
|
||||
|
||||
static void scopeStackClose(SmallVectorImpl<SymbolScope> &Stack,
|
||||
uint32_t CurOffset, ObjectFile *File) {
|
||||
if (Stack.empty()) {
|
||||
warn("symbol scopes are not balanced in " + File->getName());
|
||||
return;
|
||||
}
|
||||
SymbolScope S = Stack.pop_back_val();
|
||||
S.OpeningRecord->PtrEnd = CurOffset;
|
||||
}
|
||||
|
||||
static void mergeSymbolRecords(BumpPtrAllocator &Alloc, ObjectFile *File,
|
||||
const CVIndexMap &IndexMap,
|
||||
BinaryStreamRef SymData) {
|
||||
// FIXME: Improve error recovery by warning and skipping records when
|
||||
// possible.
|
||||
CVSymbolArray Syms;
|
||||
BinaryStreamReader Reader(SymData);
|
||||
ExitOnErr(Reader.readArray(Syms, Reader.getLength()));
|
||||
SmallVector<SymbolScope, 4> Scopes;
|
||||
for (const CVSymbol &Sym : Syms) {
|
||||
// Discover type index references in the record. Skip it if we don't know
|
||||
// where they are.
|
||||
SmallVector<TiReference, 32> TypeRefs;
|
||||
if (!discoverTypeIndices(Sym, TypeRefs)) {
|
||||
log("ignoring unknown symbol record with kind 0x" + utohexstr(Sym.kind()));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Copy the symbol record so we can mutate it.
|
||||
MutableArrayRef<uint8_t> NewData = copySymbolForPdb(Sym, Alloc);
|
||||
|
||||
// Re-map all the type index references.
|
||||
MutableArrayRef<uint8_t> Contents =
|
||||
NewData.drop_front(sizeof(RecordPrefix));
|
||||
remapTypesInSymbolRecord(File, Contents, IndexMap, TypeRefs);
|
||||
|
||||
// Fill in "Parent" and "End" fields by maintaining a stack of scopes.
|
||||
CVSymbol NewSym(Sym.kind(), NewData);
|
||||
if (symbolOpensScope(Sym.kind()))
|
||||
scopeStackOpen(Scopes, File->ModuleDBI->getNextSymbolOffset(), NewSym);
|
||||
else if (symbolEndsScope(Sym.kind()))
|
||||
scopeStackClose(Scopes, File->ModuleDBI->getNextSymbolOffset(), File);
|
||||
|
||||
// Add the symbol to the module.
|
||||
File->ModuleDBI->addSymbol(NewSym);
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate memory for a .debug$S section and relocate it.
|
||||
static ArrayRef<uint8_t> relocateDebugChunk(BumpPtrAllocator &Alloc,
|
||||
SectionChunk *DebugChunk) {
|
||||
uint8_t *Buffer = Alloc.Allocate<uint8_t>(DebugChunk->getSize());
|
||||
assert(DebugChunk->OutputSectionOff == 0 &&
|
||||
"debug sections should not be in output sections");
|
||||
DebugChunk->writeTo(Buffer);
|
||||
return consumeDebugMagic(makeArrayRef(Buffer, DebugChunk->getSize()),
|
||||
".debug$S");
|
||||
}
|
||||
|
||||
void PDBLinker::addObjectFile(ObjectFile *File) {
|
||||
// Add a module descriptor for every object file. We need to put an absolute
|
||||
// path to the object into the PDB. If this is a plain object, we make its
|
||||
// path absolute. If it's an object in an archive, we make the archive path
|
||||
// absolute.
|
||||
bool InArchive = !File->ParentName.empty();
|
||||
SmallString<128> Path = InArchive ? File->ParentName : File->getName();
|
||||
sys::fs::make_absolute(Path);
|
||||
sys::path::native(Path, sys::path::Style::windows);
|
||||
StringRef Name = InArchive ? File->getName() : StringRef(Path);
|
||||
|
||||
File->ModuleDBI = &ExitOnErr(Builder.getDbiBuilder().addModuleInfo(Name));
|
||||
File->ModuleDBI->setObjFileName(Path);
|
||||
|
||||
// Before we can process symbol substreams from .debug$S, we need to process
|
||||
// type information, file checksums, and the string table. Add type info to
|
||||
// the PDB first, so that we can get the map from object file type and item
|
||||
// indices to PDB type and item indices.
|
||||
CVIndexMap ObjectIndexMap;
|
||||
const CVIndexMap &IndexMap = mergeDebugT(File, ObjectIndexMap);
|
||||
|
||||
// Now do all live .debug$S sections.
|
||||
for (SectionChunk *DebugChunk : File->getDebugChunks()) {
|
||||
if (!DebugChunk->isLive() || DebugChunk->getSectionName() != ".debug$S")
|
||||
continue;
|
||||
|
||||
ArrayRef<uint8_t> RelocatedDebugContents =
|
||||
relocateDebugChunk(Alloc, DebugChunk);
|
||||
if (RelocatedDebugContents.empty())
|
||||
continue;
|
||||
|
||||
DebugSubsectionArray Subsections;
|
||||
BinaryStreamReader Reader(RelocatedDebugContents, support::little);
|
||||
ExitOnErr(Reader.readArray(Subsections, RelocatedDebugContents.size()));
|
||||
|
||||
DebugStringTableSubsectionRef CVStrTab;
|
||||
DebugChecksumsSubsectionRef Checksums;
|
||||
for (const DebugSubsectionRecord &SS : Subsections) {
|
||||
switch (SS.kind()) {
|
||||
case DebugSubsectionKind::StringTable:
|
||||
ExitOnErr(CVStrTab.initialize(SS.getRecordData()));
|
||||
break;
|
||||
case DebugSubsectionKind::FileChecksums:
|
||||
ExitOnErr(Checksums.initialize(SS.getRecordData()));
|
||||
break;
|
||||
case DebugSubsectionKind::Lines:
|
||||
// We can add the relocated line table directly to the PDB without
|
||||
// modification because the file checksum offsets will stay the same.
|
||||
File->ModuleDBI->addDebugSubsection(SS);
|
||||
break;
|
||||
case DebugSubsectionKind::Symbols:
|
||||
mergeSymbolRecords(Alloc, File, IndexMap, SS.getRecordData());
|
||||
break;
|
||||
default:
|
||||
// FIXME: Process the rest of the subsections.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Checksums.valid()) {
|
||||
// Make a new file checksum table that refers to offsets in the PDB-wide
|
||||
// string table. Generally the string table subsection appears after the
|
||||
// checksum table, so we have to do this after looping over all the
|
||||
// subsections.
|
||||
if (!CVStrTab.valid())
|
||||
fatal(".debug$S sections must have both a string table subsection "
|
||||
"and a checksum subsection table or neither");
|
||||
auto NewChecksums = make_unique<DebugChecksumsSubsection>(PDBStrTab);
|
||||
for (FileChecksumEntry &FC : Checksums) {
|
||||
StringRef FileName = ExitOnErr(CVStrTab.getString(FC.FileNameOffset));
|
||||
ExitOnErr(Builder.getDbiBuilder().addModuleSourceFile(*File->ModuleDBI,
|
||||
FileName));
|
||||
NewChecksums->addChecksum(FileName, FC.Kind, FC.Checksum);
|
||||
}
|
||||
File->ModuleDBI->addDebugSubsection(std::move(NewChecksums));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add all object files to the PDB. Merge .debug$T sections into IpiData and
|
||||
// TpiData.
|
||||
void PDBLinker::addObjectsToPDB() {
|
||||
for (ObjectFile *File : Symtab->ObjectFiles)
|
||||
addObjectFile(File);
|
||||
|
||||
Builder.getStringTableBuilder().setStrings(PDBStrTab);
|
||||
|
||||
// Construct TPI stream contents.
|
||||
addTypeInfo(Builder.getTpiBuilder(), TypeTable);
|
||||
|
||||
// Construct IPI stream contents.
|
||||
addTypeInfo(Builder.getIpiBuilder(), IDTable);
|
||||
|
||||
// Add public and symbol records stream.
|
||||
|
||||
// For now we don't actually write any thing useful to the publics stream, but
|
||||
// the act of "getting" it also creates it lazily so that we write an empty
|
||||
// stream.
|
||||
(void)Builder.getPublicsBuilder();
|
||||
}
|
||||
|
||||
static void addLinkerModuleSymbols(StringRef Path,
|
||||
pdb::DbiModuleDescriptorBuilder &Mod,
|
||||
BumpPtrAllocator &Allocator) {
|
||||
codeview::SymbolSerializer Serializer(Allocator, CodeViewContainer::Pdb);
|
||||
codeview::ObjNameSym ONS(SymbolRecordKind::ObjNameSym);
|
||||
codeview::Compile3Sym CS(SymbolRecordKind::Compile3Sym);
|
||||
codeview::EnvBlockSym EBS(SymbolRecordKind::EnvBlockSym);
|
||||
|
||||
ONS.Name = "* Linker *";
|
||||
ONS.Signature = 0;
|
||||
|
||||
CS.Machine = Config->is64() ? CPUType::X64 : CPUType::Intel80386;
|
||||
CS.Flags = CompileSym3Flags::None;
|
||||
CS.VersionBackendBuild = 0;
|
||||
CS.VersionBackendMajor = 0;
|
||||
CS.VersionBackendMinor = 0;
|
||||
CS.VersionBackendQFE = 0;
|
||||
CS.VersionFrontendBuild = 0;
|
||||
CS.VersionFrontendMajor = 0;
|
||||
CS.VersionFrontendMinor = 0;
|
||||
CS.VersionFrontendQFE = 0;
|
||||
CS.Version = "LLVM Linker";
|
||||
CS.setLanguage(SourceLanguage::Link);
|
||||
|
||||
ArrayRef<StringRef> Args = makeArrayRef(Config->Argv).drop_front();
|
||||
std::string ArgStr = llvm::join(Args, " ");
|
||||
EBS.Fields.push_back("cwd");
|
||||
SmallString<64> cwd;
|
||||
sys::fs::current_path(cwd);
|
||||
EBS.Fields.push_back(cwd);
|
||||
EBS.Fields.push_back("exe");
|
||||
EBS.Fields.push_back(Config->Argv[0]);
|
||||
EBS.Fields.push_back("pdb");
|
||||
EBS.Fields.push_back(Path);
|
||||
EBS.Fields.push_back("cmd");
|
||||
EBS.Fields.push_back(ArgStr);
|
||||
Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
|
||||
ONS, Allocator, CodeViewContainer::Pdb));
|
||||
Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
|
||||
CS, Allocator, CodeViewContainer::Pdb));
|
||||
Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
|
||||
EBS, Allocator, CodeViewContainer::Pdb));
|
||||
}
|
||||
|
||||
// Creates a PDB file.
|
||||
void coff::createPDB(SymbolTable *Symtab, ArrayRef<uint8_t> SectionTable,
|
||||
const llvm::codeview::DebugInfo *DI) {
|
||||
PDBLinker PDB(Symtab);
|
||||
PDB.initialize(DI);
|
||||
PDB.addObjectsToPDB();
|
||||
PDB.addSections(SectionTable);
|
||||
PDB.commit();
|
||||
}
|
||||
|
||||
void PDBLinker::initialize(const llvm::codeview::DebugInfo *DI) {
|
||||
ExitOnErr(Builder.initialize(4096)); // 4096 is blocksize
|
||||
|
||||
// Create streams in MSF for predefined streams, namely
|
||||
// PDB, TPI, DBI and IPI.
|
||||
for (int I = 0; I < (int)pdb::kSpecialStreamCount; ++I)
|
||||
ExitOnErr(Builder.getMsfBuilder().addStream(0));
|
||||
|
||||
// Add an Info stream.
|
||||
auto &InfoBuilder = Builder.getInfoBuilder();
|
||||
InfoBuilder.setAge(DI ? DI->PDB70.Age : 0);
|
||||
|
||||
GUID uuid{};
|
||||
if (DI)
|
||||
memcpy(&uuid, &DI->PDB70.Signature, sizeof(uuid));
|
||||
InfoBuilder.setGuid(uuid);
|
||||
InfoBuilder.setSignature(time(nullptr));
|
||||
InfoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70);
|
||||
|
||||
// Add an empty DBI stream.
|
||||
pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder();
|
||||
DbiBuilder.setVersionHeader(pdb::PdbDbiV70);
|
||||
ExitOnErr(DbiBuilder.addDbgStream(pdb::DbgHeaderType::NewFPO, {}));
|
||||
}
|
||||
|
||||
void PDBLinker::addSections(ArrayRef<uint8_t> SectionTable) {
|
||||
// Add Section Contributions.
|
||||
pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder();
|
||||
addSectionContribs(Symtab, DbiBuilder);
|
||||
|
||||
// Add Section Map stream.
|
||||
ArrayRef<object::coff_section> Sections = {
|
||||
(const object::coff_section *)SectionTable.data(),
|
||||
SectionTable.size() / sizeof(object::coff_section)};
|
||||
SectionMap = pdb::DbiStreamBuilder::createSectionMap(Sections);
|
||||
DbiBuilder.setSectionMap(SectionMap);
|
||||
|
||||
// It's not entirely clear what this is, but the * Linker * module uses it.
|
||||
NativePath = Config->PDBPath;
|
||||
sys::fs::make_absolute(NativePath);
|
||||
sys::path::native(NativePath, sys::path::Style::windows);
|
||||
uint32_t PdbFilePathNI = DbiBuilder.addECName(NativePath);
|
||||
auto &LinkerModule = ExitOnErr(DbiBuilder.addModuleInfo("* Linker *"));
|
||||
LinkerModule.setPdbFilePathNI(PdbFilePathNI);
|
||||
addLinkerModuleSymbols(NativePath, LinkerModule, Alloc);
|
||||
|
||||
// Add COFF section header stream.
|
||||
ExitOnErr(
|
||||
DbiBuilder.addDbgStream(pdb::DbgHeaderType::SectionHdr, SectionTable));
|
||||
}
|
||||
|
||||
void PDBLinker::commit() {
|
||||
// Write to a file.
|
||||
ExitOnErr(Builder.commit(Config->PDBPath));
|
||||
}
|
||||
31
deps/lld/COFF/PDB.h
vendored
Normal file
31
deps/lld/COFF/PDB.h
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
//===- PDB.h ----------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_COFF_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 SymbolTable;
|
||||
|
||||
void createPDB(SymbolTable *Symtab, llvm::ArrayRef<uint8_t> SectionTable,
|
||||
const llvm::codeview::DebugInfo *DI);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
1
deps/lld/COFF/README.md
vendored
Normal file
1
deps/lld/COFF/README.md
vendored
Normal file
@ -0,0 +1 @@
|
||||
See docs/NewLLD.rst
|
||||
35
deps/lld/COFF/Strings.cpp
vendored
Normal file
35
deps/lld/COFF/Strings.cpp
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
//===- Strings.cpp -------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Strings.h"
|
||||
#include <mutex>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <Windows.h>
|
||||
#include <DbgHelp.h>
|
||||
#pragma comment(lib, "dbghelp.lib")
|
||||
#endif
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::coff;
|
||||
using namespace llvm;
|
||||
|
||||
Optional<std::string> coff::demangle(StringRef S) {
|
||||
#if defined(_MSC_VER)
|
||||
// UnDecorateSymbolName is not thread-safe, so we need a mutex.
|
||||
static std::mutex Mu;
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
|
||||
char Buf[4096];
|
||||
if (S.startswith("?"))
|
||||
if (size_t Len = UnDecorateSymbolName(S.str().c_str(), Buf, sizeof(Buf), 0))
|
||||
return std::string(Buf, Len);
|
||||
#endif
|
||||
return None;
|
||||
}
|
||||
23
deps/lld/COFF/Strings.h
vendored
Normal file
23
deps/lld/COFF/Strings.h
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
//===- Strings.h ------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_COFF_STRINGS_H
|
||||
#define LLD_COFF_STRINGS_H
|
||||
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include <string>
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
llvm::Optional<std::string> demangle(llvm::StringRef S);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
375
deps/lld/COFF/SymbolTable.cpp
vendored
Normal file
375
deps/lld/COFF/SymbolTable.cpp
vendored
Normal file
@ -0,0 +1,375 @@
|
||||
//===- SymbolTable.cpp ----------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "SymbolTable.h"
|
||||
#include "Config.h"
|
||||
#include "Driver.h"
|
||||
#include "Error.h"
|
||||
#include "LTO.h"
|
||||
#include "Memory.h"
|
||||
#include "Symbols.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <utility>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
enum SymbolPreference {
|
||||
SP_EXISTING = -1,
|
||||
SP_CONFLICT = 0,
|
||||
SP_NEW = 1,
|
||||
};
|
||||
|
||||
/// Checks if an existing symbol S should be kept or replaced by a new symbol.
|
||||
/// Returns SP_EXISTING when S should be kept, SP_NEW when the new symbol
|
||||
/// should be kept, and SP_CONFLICT if no valid resolution exists.
|
||||
static SymbolPreference compareDefined(Symbol *S, bool WasInserted,
|
||||
bool NewIsCOMDAT) {
|
||||
// If the symbol wasn't previously known, the new symbol wins by default.
|
||||
if (WasInserted || !isa<Defined>(S->body()))
|
||||
return SP_NEW;
|
||||
|
||||
// If the existing symbol is a DefinedRegular, both it and the new symbol
|
||||
// must be comdats. In that case, we have no reason to prefer one symbol
|
||||
// over the other, and we keep the existing one. If one of the symbols
|
||||
// is not a comdat, we report a conflict.
|
||||
if (auto *R = dyn_cast<DefinedRegular>(S->body())) {
|
||||
if (NewIsCOMDAT && R->isCOMDAT())
|
||||
return SP_EXISTING;
|
||||
else
|
||||
return SP_CONFLICT;
|
||||
}
|
||||
|
||||
// Existing symbol is not a DefinedRegular; new symbol wins.
|
||||
return SP_NEW;
|
||||
}
|
||||
|
||||
SymbolTable *Symtab;
|
||||
|
||||
void SymbolTable::addFile(InputFile *File) {
|
||||
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) {
|
||||
fatal(toString(File) + ": machine type " + machineToStr(MT) +
|
||||
" conflicts with " + machineToStr(Config->Machine));
|
||||
}
|
||||
|
||||
if (auto *F = dyn_cast<ObjectFile>(File)) {
|
||||
ObjectFiles.push_back(F);
|
||||
} else if (auto *F = dyn_cast<BitcodeFile>(File)) {
|
||||
BitcodeFiles.push_back(F);
|
||||
} else if (auto *F = dyn_cast<ImportFile>(File)) {
|
||||
ImportFiles.push_back(F);
|
||||
}
|
||||
|
||||
StringRef S = File->getDirectives();
|
||||
if (S.empty())
|
||||
return;
|
||||
|
||||
log("Directives: " + toString(File) + ": " + S);
|
||||
Driver->parseDirectives(S);
|
||||
}
|
||||
|
||||
void SymbolTable::reportRemainingUndefines() {
|
||||
SmallPtrSet<SymbolBody *, 8> Undefs;
|
||||
for (auto &I : Symtab) {
|
||||
Symbol *Sym = I.second;
|
||||
auto *Undef = dyn_cast<Undefined>(Sym->body());
|
||||
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 resolve weak aliases by replacing the alias's SymbolBody with the
|
||||
// target's SymbolBody. This causes all SymbolBody pointers referring to
|
||||
// the old symbol to instead refer to the new symbol. However, we can't
|
||||
// just blindly copy sizeof(Symbol::Body) bytes from D to Sym->Body
|
||||
// because D may be an internal symbol, and internal symbols are stored as
|
||||
// "unparented" SymbolBodies. 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->Body.buffer, D, sizeof(DefinedRegular));
|
||||
else if (isa<DefinedAbsolute>(D))
|
||||
memcpy(Sym->Body.buffer, D, sizeof(DefinedAbsolute));
|
||||
else
|
||||
// No other internal symbols are possible.
|
||||
Sym->Body = D->symbol()->Body;
|
||||
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->body())) {
|
||||
auto *D = cast<Defined>(Imp->body());
|
||||
replaceBody<DefinedLocalImport>(Sym, Name, D);
|
||||
LocalImportChunks.push_back(
|
||||
cast<DefinedLocalImport>(Sym->body())->getChunk());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Remaining undefined symbols are not fatal if /force is specified.
|
||||
// They are replaced with dummy defined symbols.
|
||||
if (Config->Force)
|
||||
replaceBody<DefinedAbsolute>(Sym, Name, 0);
|
||||
Undefs.insert(Sym->body());
|
||||
}
|
||||
if (Undefs.empty())
|
||||
return;
|
||||
for (SymbolBody *B : Config->GCRoot)
|
||||
if (Undefs.count(B))
|
||||
warn("<root>: undefined symbol: " + B->getName());
|
||||
for (ObjectFile *File : ObjectFiles)
|
||||
for (SymbolBody *Sym : File->getSymbols())
|
||||
if (Undefs.count(Sym))
|
||||
warn(toString(File) + ": undefined symbol: " + Sym->getName());
|
||||
if (!Config->Force)
|
||||
fatal("link failed");
|
||||
}
|
||||
|
||||
std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) {
|
||||
Symbol *&Sym = Symtab[CachedHashStringRef(Name)];
|
||||
if (Sym)
|
||||
return {Sym, false};
|
||||
Sym = make<Symbol>();
|
||||
Sym->IsUsedInRegularObj = false;
|
||||
Sym->PendingArchiveLoad = false;
|
||||
return {Sym, true};
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addUndefined(StringRef Name, InputFile *F,
|
||||
bool IsWeakAlias) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(Name);
|
||||
if (!F || !isa<BitcodeFile>(F))
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || (isa<Lazy>(S->body()) && IsWeakAlias)) {
|
||||
replaceBody<Undefined>(S, Name);
|
||||
return S;
|
||||
}
|
||||
if (auto *L = dyn_cast<Lazy>(S->body())) {
|
||||
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) {
|
||||
replaceBody<Lazy>(S, F, Sym);
|
||||
return;
|
||||
}
|
||||
auto *U = dyn_cast<Undefined>(S->body());
|
||||
if (!U || U->WeakAlias || S->PendingArchiveLoad)
|
||||
return;
|
||||
S->PendingArchiveLoad = true;
|
||||
F->addMember(&Sym);
|
||||
}
|
||||
|
||||
void SymbolTable::reportDuplicate(Symbol *Existing, InputFile *NewFile) {
|
||||
error("duplicate symbol: " + toString(*Existing->body()) + " in " +
|
||||
toString(Existing->body()->getFile()) + " and in " +
|
||||
(NewFile ? toString(NewFile) : "(internal)"));
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addAbsolute(StringRef N, COFFSymbolRef Sym) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(N);
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
|
||||
replaceBody<DefinedAbsolute>(S, N, Sym);
|
||||
else if (!isa<DefinedCOFF>(S->body()))
|
||||
reportDuplicate(S, nullptr);
|
||||
return S;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addAbsolute(StringRef N, uint64_t VA) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(N);
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
|
||||
replaceBody<DefinedAbsolute>(S, N, VA);
|
||||
else if (!isa<DefinedCOFF>(S->body()))
|
||||
reportDuplicate(S, nullptr);
|
||||
return S;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addSynthetic(StringRef N, Chunk *C) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(N);
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
|
||||
replaceBody<DefinedSynthetic>(S, N, C);
|
||||
else if (!isa<DefinedCOFF>(S->body()))
|
||||
reportDuplicate(S, nullptr);
|
||||
return S;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addRegular(InputFile *F, StringRef N, bool IsCOMDAT,
|
||||
const coff_symbol_generic *Sym,
|
||||
SectionChunk *C) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(N);
|
||||
if (!isa<BitcodeFile>(F))
|
||||
S->IsUsedInRegularObj = true;
|
||||
SymbolPreference SP = compareDefined(S, WasInserted, IsCOMDAT);
|
||||
if (SP == SP_CONFLICT) {
|
||||
reportDuplicate(S, F);
|
||||
} else if (SP == SP_NEW) {
|
||||
replaceBody<DefinedRegular>(S, F, N, IsCOMDAT, /*IsExternal*/ true, Sym, C);
|
||||
} else if (SP == SP_EXISTING && IsCOMDAT && C) {
|
||||
C->markDiscarded();
|
||||
// Discard associative chunks that we've parsed so far. No need to recurse
|
||||
// because an associative section cannot have children.
|
||||
for (SectionChunk *Child : C->children())
|
||||
Child->markDiscarded();
|
||||
}
|
||||
return S;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size,
|
||||
const coff_symbol_generic *Sym, CommonChunk *C) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(N);
|
||||
if (!isa<BitcodeFile>(F))
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || !isa<DefinedCOFF>(S->body()))
|
||||
replaceBody<DefinedCommon>(S, F, N, Size, Sym, C);
|
||||
else if (auto *DC = dyn_cast<DefinedCommon>(S->body()))
|
||||
if (Size > DC->getSize())
|
||||
replaceBody<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);
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
|
||||
replaceBody<DefinedImportData>(S, N, F);
|
||||
else if (!isa<DefinedCOFF>(S->body()))
|
||||
reportDuplicate(S, nullptr);
|
||||
return S;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addImportThunk(StringRef Name, DefinedImportData *ID,
|
||||
uint16_t Machine) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(Name);
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
|
||||
replaceBody<DefinedImportThunk>(S, Name, ID, Machine);
|
||||
else if (!isa<DefinedCOFF>(S->body()))
|
||||
reportDuplicate(S, nullptr);
|
||||
return S;
|
||||
}
|
||||
|
||||
std::vector<Chunk *> SymbolTable::getChunks() {
|
||||
std::vector<Chunk *> Res;
|
||||
for (ObjectFile *File : ObjectFiles) {
|
||||
std::vector<Chunk *> &V = File->getChunks();
|
||||
Res.insert(Res.end(), V.begin(), V.end());
|
||||
}
|
||||
return Res;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::find(StringRef Name) {
|
||||
auto It = Symtab.find(CachedHashStringRef(Name));
|
||||
if (It == Symtab.end())
|
||||
return nullptr;
|
||||
return It->second;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::findUnderscore(StringRef Name) {
|
||||
if (Config->Machine == I386)
|
||||
return find(("_" + Name).str());
|
||||
return find(Name);
|
||||
}
|
||||
|
||||
StringRef SymbolTable::findByPrefix(StringRef Prefix) {
|
||||
for (auto Pair : Symtab) {
|
||||
StringRef Name = Pair.first.val();
|
||||
if (Name.startswith(Prefix))
|
||||
return Name;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
StringRef SymbolTable::findMangle(StringRef Name) {
|
||||
if (Symbol *Sym = find(Name))
|
||||
if (!isa<Undefined>(Sym->body()))
|
||||
return Name;
|
||||
if (Config->Machine != I386)
|
||||
return findByPrefix(("?" + Name + "@@Y").str());
|
||||
if (!Name.startswith("_"))
|
||||
return "";
|
||||
// Search for x86 C function.
|
||||
StringRef S = findByPrefix((Name + "@").str());
|
||||
if (!S.empty())
|
||||
return S;
|
||||
// Search for x86 C++ non-member function.
|
||||
return findByPrefix(("?" + Name.substr(1) + "@@Y").str());
|
||||
}
|
||||
|
||||
void SymbolTable::mangleMaybe(SymbolBody *B) {
|
||||
auto *U = dyn_cast<Undefined>(B);
|
||||
if (!U || U->WeakAlias)
|
||||
return;
|
||||
StringRef Alias = findMangle(U->getName());
|
||||
if (!Alias.empty())
|
||||
U->WeakAlias = addUndefined(Alias);
|
||||
}
|
||||
|
||||
SymbolBody *SymbolTable::addUndefined(StringRef Name) {
|
||||
return addUndefined(Name, nullptr, false)->body();
|
||||
}
|
||||
|
||||
std::vector<StringRef> SymbolTable::compileBitcodeFiles() {
|
||||
LTO.reset(new BitcodeCompiler);
|
||||
for (BitcodeFile *F : BitcodeFiles)
|
||||
LTO->add(*F);
|
||||
return LTO->compile();
|
||||
}
|
||||
|
||||
void SymbolTable::addCombinedLTOObjects() {
|
||||
if (BitcodeFiles.empty())
|
||||
return;
|
||||
for (StringRef Object : compileBitcodeFiles()) {
|
||||
auto *Obj = make<ObjectFile>(MemoryBufferRef(Object, "lto.tmp"));
|
||||
Obj->parse();
|
||||
ObjectFiles.push_back(Obj);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
124
deps/lld/COFF/SymbolTable.h
vendored
Normal file
124
deps/lld/COFF/SymbolTable.h
vendored
Normal file
@ -0,0 +1,124 @@
|
||||
//===- SymbolTable.h --------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_COFF_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 DefinedRelative;
|
||||
class Lazy;
|
||||
class SectionChunk;
|
||||
class SymbolBody;
|
||||
struct 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();
|
||||
|
||||
// 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.
|
||||
void mangleMaybe(SymbolBody *B);
|
||||
StringRef 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();
|
||||
|
||||
// The writer needs to handle DLL import libraries specially in
|
||||
// order to create the import descriptor table.
|
||||
std::vector<ImportFile *> ImportFiles;
|
||||
|
||||
// The writer needs to infer the machine type from the object files.
|
||||
std::vector<ObjectFile *> ObjectFiles;
|
||||
|
||||
// Creates an Undefined symbol for a given name.
|
||||
SymbolBody *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, bool IsCOMDAT,
|
||||
const llvm::object::coff_symbol_generic *S = nullptr,
|
||||
SectionChunk *C = nullptr);
|
||||
Symbol *addCommon(InputFile *F, StringRef N, uint64_t Size,
|
||||
const llvm::object::coff_symbol_generic *S = nullptr,
|
||||
CommonChunk *C = nullptr);
|
||||
Symbol *addImportData(StringRef N, ImportFile *F);
|
||||
Symbol *addImportThunk(StringRef Name, DefinedImportData *S,
|
||||
uint16_t Machine);
|
||||
|
||||
void reportDuplicate(Symbol *Existing, InputFile *NewFile);
|
||||
|
||||
// A list of chunks which to be added to .rdata.
|
||||
std::vector<Chunk *> LocalImportChunks;
|
||||
|
||||
private:
|
||||
std::pair<Symbol *, bool> insert(StringRef Name);
|
||||
StringRef findByPrefix(StringRef Prefix);
|
||||
|
||||
llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> Symtab;
|
||||
|
||||
std::vector<BitcodeFile *> BitcodeFiles;
|
||||
std::unique_ptr<BitcodeCompiler> LTO;
|
||||
};
|
||||
|
||||
extern SymbolTable *Symtab;
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
90
deps/lld/COFF/Symbols.cpp
vendored
Normal file
90
deps/lld/COFF/Symbols.cpp
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
//===- Symbols.cpp --------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Symbols.h"
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Memory.h"
|
||||
#include "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;
|
||||
|
||||
// Returns a symbol name for an error message.
|
||||
std::string lld::toString(coff::SymbolBody &B) {
|
||||
if (Optional<std::string> S = coff::demangle(B.getName()))
|
||||
return ("\"" + *S + "\" (" + B.getName() + ")").str();
|
||||
return B.getName();
|
||||
}
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
StringRef SymbolBody::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 (Name.empty()) {
|
||||
auto *D = cast<DefinedCOFF>(this);
|
||||
cast<ObjectFile>(D->File)->getCOFFObj()->getSymbolName(D->Sym, Name);
|
||||
}
|
||||
return Name;
|
||||
}
|
||||
|
||||
InputFile *SymbolBody::getFile() {
|
||||
if (auto *Sym = dyn_cast<DefinedCOFF>(this))
|
||||
return Sym->File;
|
||||
if (auto *Sym = dyn_cast<Lazy>(this))
|
||||
return Sym->File;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
COFFSymbolRef DefinedCOFF::getCOFFSymbol() {
|
||||
size_t SymSize =
|
||||
cast<ObjectFile>(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::OutputSectionIndex = 0;
|
||||
|
||||
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 (SymbolBody *A = WeakAlias; A; A = cast<Undefined>(A)->WeakAlias)
|
||||
if (auto *D = dyn_cast<Defined>(A))
|
||||
return D;
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
443
deps/lld/COFF/Symbols.h
vendored
Normal file
443
deps/lld/COFF/Symbols.h
vendored
Normal file
@ -0,0 +1,443 @@
|
||||
//===- Symbols.h ------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_COFF_SYMBOLS_H
|
||||
#define LLD_COFF_SYMBOLS_H
|
||||
|
||||
#include "Chunks.h"
|
||||
#include "Config.h"
|
||||
#include "Memory.h"
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/Object/Archive.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
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 ObjectFile;
|
||||
struct Symbol;
|
||||
class SymbolTable;
|
||||
|
||||
// The base class for real symbol classes.
|
||||
class SymbolBody {
|
||||
public:
|
||||
enum Kind {
|
||||
// The order of these is significant. We start with the regular defined
|
||||
// symbols as those are the most prevelant and the zero tag is the cheapest
|
||||
// to set. Among the defined kinds, the lower the kind is preferred over
|
||||
// the higher kind when testing wether 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 true if this is an external symbol.
|
||||
bool isExternal() { return IsExternal; }
|
||||
|
||||
// Returns the symbol name.
|
||||
StringRef getName();
|
||||
|
||||
// Returns the file from which this symbol was created.
|
||||
InputFile *getFile();
|
||||
|
||||
Symbol *symbol();
|
||||
const Symbol *symbol() const {
|
||||
return const_cast<SymbolBody *>(this)->symbol();
|
||||
}
|
||||
|
||||
protected:
|
||||
friend SymbolTable;
|
||||
explicit SymbolBody(Kind K, StringRef N = "")
|
||||
: SymbolKind(K), IsExternal(true), IsCOMDAT(false),
|
||||
WrittenToSymtab(false), Name(N) {}
|
||||
|
||||
const unsigned SymbolKind : 8;
|
||||
unsigned IsExternal : 1;
|
||||
|
||||
// This bit is used by the \c DefinedRegular subclass.
|
||||
unsigned IsCOMDAT : 1;
|
||||
|
||||
public:
|
||||
// This bit is used by Writer::createSymbolAndStringTable() to prevent
|
||||
// symbols from being written to the symbol table more than once.
|
||||
unsigned WrittenToSymtab : 1;
|
||||
|
||||
protected:
|
||||
StringRef Name;
|
||||
};
|
||||
|
||||
// The base class for any defined symbols, including absolute symbols,
|
||||
// etc.
|
||||
class Defined : public SymbolBody {
|
||||
public:
|
||||
Defined(Kind K, StringRef N) : SymbolBody(K, N) {}
|
||||
|
||||
static bool classof(const SymbolBody *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 StringRef.
|
||||
class DefinedCOFF : public Defined {
|
||||
friend SymbolBody;
|
||||
public:
|
||||
DefinedCOFF(Kind K, InputFile *F, StringRef N, const coff_symbol_generic *S)
|
||||
: Defined(K, N), File(F), Sym(S) {}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() <= LastDefinedCOFFKind;
|
||||
}
|
||||
|
||||
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 SymbolBody *S) {
|
||||
return S->kind() == DefinedRegularKind;
|
||||
}
|
||||
|
||||
uint64_t getRVA() { return (*Data)->getRVA() + Sym->Value; }
|
||||
bool isCOMDAT() { return IsCOMDAT; }
|
||||
SectionChunk *getChunk() { return *Data; }
|
||||
uint32_t getValue() { return Sym->Value; }
|
||||
|
||||
private:
|
||||
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 SymbolBody *S) {
|
||||
return S->kind() == DefinedCommonKind;
|
||||
}
|
||||
|
||||
uint64_t getRVA() { return Data->getRVA(); }
|
||||
Chunk *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 SymbolBody *S) {
|
||||
return S->kind() == DefinedAbsoluteKind;
|
||||
}
|
||||
|
||||
uint64_t getRVA() { return VA - Config->ImageBase; }
|
||||
void setVA(uint64_t V) { VA = V; }
|
||||
|
||||
// The sentinel absolute symbol section index. Section index relocations
|
||||
// against absolute symbols resolve to this 16 bit number, and it is the
|
||||
// largest valid section index plus one. This is written by the Writer.
|
||||
static uint16_t OutputSectionIndex;
|
||||
uint16_t getSecIdx() { return OutputSectionIndex; }
|
||||
|
||||
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 SymbolBody *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 SymbolBody {
|
||||
public:
|
||||
Lazy(ArchiveFile *F, const Archive::Symbol S)
|
||||
: SymbolBody(LazyKind, S.getName()), File(F), Sym(S) {}
|
||||
|
||||
static bool classof(const SymbolBody *S) { return S->kind() == LazyKind; }
|
||||
|
||||
ArchiveFile *File;
|
||||
|
||||
private:
|
||||
friend SymbolTable;
|
||||
|
||||
private:
|
||||
const Archive::Symbol Sym;
|
||||
};
|
||||
|
||||
// Undefined symbols.
|
||||
class Undefined : public SymbolBody {
|
||||
public:
|
||||
explicit Undefined(StringRef N) : SymbolBody(UndefinedKind, N) {}
|
||||
|
||||
static bool classof(const SymbolBody *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.
|
||||
SymbolBody *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 SymbolBody *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 SymbolBody *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 "__imp_foo" in your object file, a symbol name
|
||||
// "foo" becomes automatically available as a pointer to "__imp_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 SymbolBody *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 real symbol object, SymbolBody, is usually stored within a Symbol. There's
|
||||
// always one Symbol for each symbol name. The resolver updates the SymbolBody
|
||||
// stored in the Body field of this object as it resolves symbols. Symbol also
|
||||
// holds computed properties of symbol names.
|
||||
struct Symbol {
|
||||
// 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;
|
||||
|
||||
// This field is used to store the Symbol's SymbolBody. This instantiation of
|
||||
// AlignedCharArrayUnion gives us a struct with a char array field that is
|
||||
// large and aligned enough to store any derived class of SymbolBody.
|
||||
llvm::AlignedCharArrayUnion<
|
||||
DefinedRegular, DefinedCommon, DefinedAbsolute, DefinedSynthetic, Lazy,
|
||||
Undefined, DefinedImportData, DefinedImportThunk, DefinedLocalImport>
|
||||
Body;
|
||||
|
||||
SymbolBody *body() {
|
||||
return reinterpret_cast<SymbolBody *>(Body.buffer);
|
||||
}
|
||||
const SymbolBody *body() const { return const_cast<Symbol *>(this)->body(); }
|
||||
};
|
||||
|
||||
template <typename T, typename... ArgT>
|
||||
void replaceBody(Symbol *S, ArgT &&... Arg) {
|
||||
static_assert(sizeof(T) <= sizeof(S->Body), "Body too small");
|
||||
static_assert(alignof(T) <= alignof(decltype(S->Body)),
|
||||
"Body not aligned enough");
|
||||
assert(static_cast<SymbolBody *>(static_cast<T *>(nullptr)) == nullptr &&
|
||||
"Not a SymbolBody");
|
||||
new (S->Body.buffer) T(std::forward<ArgT>(Arg)...);
|
||||
}
|
||||
|
||||
inline Symbol *SymbolBody::symbol() {
|
||||
assert(isExternal());
|
||||
return reinterpret_cast<Symbol *>(reinterpret_cast<char *>(this) -
|
||||
offsetof(Symbol, Body));
|
||||
}
|
||||
} // namespace coff
|
||||
|
||||
std::string toString(coff::SymbolBody &B);
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
900
deps/lld/COFF/Writer.cpp
vendored
Normal file
900
deps/lld/COFF/Writer.cpp
vendored
Normal file
@ -0,0 +1,900 @@
|
||||
//===- Writer.cpp ---------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Writer.h"
|
||||
#include "Config.h"
|
||||
#include "DLL.h"
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "MapFile.h"
|
||||
#include "Memory.h"
|
||||
#include "PDB.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/FileOutputBuffer.h"
|
||||
#include "llvm/Support/Parallel.h"
|
||||
#include "llvm/Support/RandomNumberGenerator.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::COFF;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace lld;
|
||||
using namespace lld::coff;
|
||||
|
||||
static const int SectorSize = 512;
|
||||
static const int DOSStubSize = 64;
|
||||
static const int NumberfOfDataDirectory = 16;
|
||||
|
||||
namespace {
|
||||
|
||||
class DebugDirectoryChunk : public Chunk {
|
||||
public:
|
||||
DebugDirectoryChunk(const std::vector<Chunk *> &R) : Records(R) {}
|
||||
|
||||
size_t getSize() const override {
|
||||
return Records.size() * sizeof(debug_directory);
|
||||
}
|
||||
|
||||
void writeTo(uint8_t *B) const override {
|
||||
auto *D = reinterpret_cast<debug_directory *>(B + OutputSectionOff);
|
||||
|
||||
for (const Chunk *Record : Records) {
|
||||
D->Characteristics = 0;
|
||||
D->TimeDateStamp = 0;
|
||||
D->MajorVersion = 0;
|
||||
D->MinorVersion = 0;
|
||||
D->Type = COFF::IMAGE_DEBUG_TYPE_CODEVIEW;
|
||||
D->SizeOfData = Record->getSize();
|
||||
D->AddressOfRawData = Record->getRVA();
|
||||
// TODO(compnerd) get the file offset
|
||||
D->PointerToRawData = 0;
|
||||
|
||||
++D;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<Chunk *> &Records;
|
||||
};
|
||||
|
||||
class CVDebugRecordChunk : public Chunk {
|
||||
size_t getSize() const override {
|
||||
return sizeof(codeview::DebugInfo) + Config->PDBPath.size() + 1;
|
||||
}
|
||||
|
||||
void writeTo(uint8_t *B) const override {
|
||||
// Save off the DebugInfo entry to backfill the file signature (build id)
|
||||
// in Writer::writeBuildId
|
||||
DI = reinterpret_cast<codeview::DebugInfo *>(B + OutputSectionOff);
|
||||
|
||||
DI->Signature.CVSignature = OMF::Signature::PDB70;
|
||||
|
||||
// variable sized field (PDB Path)
|
||||
auto *P = reinterpret_cast<char *>(B + OutputSectionOff + sizeof(*DI));
|
||||
if (!Config->PDBPath.empty())
|
||||
memcpy(P, Config->PDBPath.data(), Config->PDBPath.size());
|
||||
P[Config->PDBPath.size()] = '\0';
|
||||
}
|
||||
|
||||
public:
|
||||
mutable codeview::DebugInfo *DI = nullptr;
|
||||
};
|
||||
|
||||
// The writer writes a SymbolTable result to a file.
|
||||
class Writer {
|
||||
public:
|
||||
Writer(SymbolTable *T) : Symtab(T) {}
|
||||
void run();
|
||||
|
||||
private:
|
||||
void createSections();
|
||||
void createMiscChunks();
|
||||
void createImportTables();
|
||||
void createExportTable();
|
||||
void assignAddresses();
|
||||
void removeEmptySections();
|
||||
void createSymbolAndStringTable();
|
||||
void openFile(StringRef OutputPath);
|
||||
template <typename PEHeaderTy> void writeHeader();
|
||||
void fixSafeSEHSymbols();
|
||||
void setSectionPermissions();
|
||||
void writeSections();
|
||||
void sortExceptionTable();
|
||||
void writeBuildId();
|
||||
|
||||
llvm::Optional<coff_symbol16> createSymbol(Defined *D);
|
||||
size_t addEntryToStringTable(StringRef Str);
|
||||
|
||||
OutputSection *findSection(StringRef Name);
|
||||
OutputSection *createSection(StringRef Name);
|
||||
void addBaserels(OutputSection *Dest);
|
||||
void addBaserelBlocks(OutputSection *Dest, std::vector<Baserel> &V);
|
||||
|
||||
uint32_t getSizeOfInitializedData();
|
||||
std::map<StringRef, std::vector<DefinedImportData *>> binImports();
|
||||
|
||||
SymbolTable *Symtab;
|
||||
std::unique_ptr<FileOutputBuffer> Buffer;
|
||||
std::vector<OutputSection *> OutputSections;
|
||||
std::vector<char> Strtab;
|
||||
std::vector<llvm::object::coff_symbol16> OutputSymtab;
|
||||
IdataContents Idata;
|
||||
DelayLoadContents DelayIdata;
|
||||
EdataContents Edata;
|
||||
SEHTableChunk *SEHTable = nullptr;
|
||||
|
||||
Chunk *DebugDirectory = nullptr;
|
||||
std::vector<Chunk *> DebugRecords;
|
||||
CVDebugRecordChunk *BuildId = nullptr;
|
||||
ArrayRef<uint8_t> SectionTable;
|
||||
|
||||
uint64_t FileSize;
|
||||
uint32_t PointerToSymbolTable = 0;
|
||||
uint64_t SizeOfImage;
|
||||
uint64_t SizeOfHeaders;
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
void writeResult(SymbolTable *T) { Writer(T).run(); }
|
||||
|
||||
void OutputSection::setRVA(uint64_t RVA) {
|
||||
Header.VirtualAddress = RVA;
|
||||
for (Chunk *C : Chunks)
|
||||
C->setRVA(C->getRVA() + RVA);
|
||||
}
|
||||
|
||||
void OutputSection::setFileOffset(uint64_t Off) {
|
||||
// If a section has no actual data (i.e. BSS section), we want to
|
||||
// set 0 to its PointerToRawData. Otherwise the output is rejected
|
||||
// by the loader.
|
||||
if (Header.SizeOfRawData == 0)
|
||||
return;
|
||||
Header.PointerToRawData = Off;
|
||||
}
|
||||
|
||||
void OutputSection::addChunk(Chunk *C) {
|
||||
Chunks.push_back(C);
|
||||
C->setOutputSection(this);
|
||||
uint64_t Off = Header.VirtualSize;
|
||||
Off = alignTo(Off, C->getAlign());
|
||||
C->setRVA(Off);
|
||||
C->OutputSectionOff = Off;
|
||||
Off += C->getSize();
|
||||
Header.VirtualSize = Off;
|
||||
if (C->hasData())
|
||||
Header.SizeOfRawData = alignTo(Off, SectorSize);
|
||||
}
|
||||
|
||||
void OutputSection::addPermissions(uint32_t C) {
|
||||
Header.Characteristics |= C & PermMask;
|
||||
}
|
||||
|
||||
void OutputSection::setPermissions(uint32_t C) {
|
||||
Header.Characteristics = C & PermMask;
|
||||
}
|
||||
|
||||
// Write the section header to a given buffer.
|
||||
void OutputSection::writeHeaderTo(uint8_t *Buf) {
|
||||
auto *Hdr = reinterpret_cast<coff_section *>(Buf);
|
||||
*Hdr = Header;
|
||||
if (StringTableOff) {
|
||||
// If name is too long, write offset into the string table as a name.
|
||||
sprintf(Hdr->Name, "/%d", StringTableOff);
|
||||
} else {
|
||||
assert(!Config->Debug || Name.size() <= COFF::NameSize);
|
||||
strncpy(Hdr->Name, Name.data(),
|
||||
std::min(Name.size(), (size_t)COFF::NameSize));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
// The main function of the writer.
|
||||
void Writer::run() {
|
||||
createSections();
|
||||
createMiscChunks();
|
||||
createImportTables();
|
||||
createExportTable();
|
||||
if (Config->Relocatable)
|
||||
createSection(".reloc");
|
||||
assignAddresses();
|
||||
removeEmptySections();
|
||||
setSectionPermissions();
|
||||
createSymbolAndStringTable();
|
||||
openFile(Config->OutputFile);
|
||||
if (Config->is64()) {
|
||||
writeHeader<pe32plus_header>();
|
||||
} else {
|
||||
writeHeader<pe32_header>();
|
||||
}
|
||||
fixSafeSEHSymbols();
|
||||
writeSections();
|
||||
sortExceptionTable();
|
||||
writeBuildId();
|
||||
|
||||
if (!Config->PDBPath.empty() && Config->Debug) {
|
||||
const llvm::codeview::DebugInfo *DI = nullptr;
|
||||
if (Config->DebugTypes & static_cast<unsigned>(coff::DebugType::CV))
|
||||
DI = BuildId->DI;
|
||||
createPDB(Symtab, SectionTable, DI);
|
||||
}
|
||||
|
||||
writeMapFile(OutputSections);
|
||||
|
||||
if (auto EC = Buffer->commit())
|
||||
fatal(EC, "failed to write the output file");
|
||||
}
|
||||
|
||||
static StringRef getOutputSection(StringRef Name) {
|
||||
StringRef S = Name.split('$').first;
|
||||
auto It = Config->Merge.find(S);
|
||||
if (It == Config->Merge.end())
|
||||
return S;
|
||||
return It->second;
|
||||
}
|
||||
|
||||
// Create output section objects and add them to OutputSections.
|
||||
void Writer::createSections() {
|
||||
// First, bin chunks by name.
|
||||
std::map<StringRef, std::vector<Chunk *>> Map;
|
||||
for (Chunk *C : Symtab->getChunks()) {
|
||||
auto *SC = dyn_cast<SectionChunk>(C);
|
||||
if (SC && !SC->isLive()) {
|
||||
if (Config->Verbose)
|
||||
SC->printDiscardedMessage();
|
||||
continue;
|
||||
}
|
||||
Map[C->getSectionName()].push_back(C);
|
||||
}
|
||||
|
||||
// Then create an OutputSection for each section.
|
||||
// '$' and all following characters in input section names are
|
||||
// discarded when determining output section. So, .text$foo
|
||||
// contributes to .text, for example. See PE/COFF spec 3.2.
|
||||
SmallDenseMap<StringRef, OutputSection *> Sections;
|
||||
for (auto Pair : Map) {
|
||||
StringRef Name = getOutputSection(Pair.first);
|
||||
OutputSection *&Sec = Sections[Name];
|
||||
if (!Sec) {
|
||||
Sec = make<OutputSection>(Name);
|
||||
OutputSections.push_back(Sec);
|
||||
}
|
||||
std::vector<Chunk *> &Chunks = Pair.second;
|
||||
for (Chunk *C : Chunks) {
|
||||
Sec->addChunk(C);
|
||||
Sec->addPermissions(C->getPermissions());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Writer::createMiscChunks() {
|
||||
OutputSection *RData = createSection(".rdata");
|
||||
|
||||
// Create thunks for locally-dllimported symbols.
|
||||
if (!Symtab->LocalImportChunks.empty()) {
|
||||
for (Chunk *C : Symtab->LocalImportChunks)
|
||||
RData->addChunk(C);
|
||||
}
|
||||
|
||||
// Create Debug Information Chunks
|
||||
if (Config->Debug) {
|
||||
DebugDirectory = make<DebugDirectoryChunk>(DebugRecords);
|
||||
|
||||
// TODO(compnerd) create a coffgrp entry if DebugType::CV is not enabled
|
||||
if (Config->DebugTypes & static_cast<unsigned>(coff::DebugType::CV)) {
|
||||
auto *Chunk = make<CVDebugRecordChunk>();
|
||||
|
||||
BuildId = Chunk;
|
||||
DebugRecords.push_back(Chunk);
|
||||
}
|
||||
|
||||
RData->addChunk(DebugDirectory);
|
||||
for (Chunk *C : DebugRecords)
|
||||
RData->addChunk(C);
|
||||
}
|
||||
|
||||
// Create SEH table. x86-only.
|
||||
if (Config->Machine != I386)
|
||||
return;
|
||||
|
||||
std::set<Defined *> Handlers;
|
||||
|
||||
for (lld::coff::ObjectFile *File : Symtab->ObjectFiles) {
|
||||
if (!File->SEHCompat)
|
||||
return;
|
||||
for (SymbolBody *B : File->SEHandlers) {
|
||||
// Make sure the handler is still live. Assume all handlers are regular
|
||||
// symbols.
|
||||
auto *D = dyn_cast<DefinedRegular>(B);
|
||||
if (D && D->getChunk()->isLive())
|
||||
Handlers.insert(D);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Handlers.empty()) {
|
||||
SEHTable = make<SEHTableChunk>(Handlers);
|
||||
RData->addChunk(SEHTable);
|
||||
}
|
||||
}
|
||||
|
||||
// Create .idata section for the DLL-imported symbol table.
|
||||
// The format of this section is inherently Windows-specific.
|
||||
// IdataContents class abstracted away the details for us,
|
||||
// so we just let it create chunks and add them to the section.
|
||||
void Writer::createImportTables() {
|
||||
if (Symtab->ImportFiles.empty())
|
||||
return;
|
||||
|
||||
// Initialize DLLOrder so that import entries are ordered in
|
||||
// the same order as in the command line. (That affects DLL
|
||||
// initialization order, and this ordering is MSVC-compatible.)
|
||||
for (ImportFile *File : Symtab->ImportFiles) {
|
||||
if (!File->Live)
|
||||
continue;
|
||||
|
||||
std::string DLL = StringRef(File->DLLName).lower();
|
||||
if (Config->DLLOrder.count(DLL) == 0)
|
||||
Config->DLLOrder[DLL] = Config->DLLOrder.size();
|
||||
}
|
||||
|
||||
OutputSection *Text = createSection(".text");
|
||||
for (ImportFile *File : Symtab->ImportFiles) {
|
||||
if (!File->Live)
|
||||
continue;
|
||||
|
||||
if (DefinedImportThunk *Thunk = File->ThunkSym)
|
||||
Text->addChunk(Thunk->getChunk());
|
||||
|
||||
if (Config->DelayLoads.count(StringRef(File->DLLName).lower())) {
|
||||
if (!File->ThunkSym)
|
||||
fatal("cannot delay-load " + toString(File) +
|
||||
" due to import of data: " + toString(*File->ImpSym));
|
||||
DelayIdata.add(File->ImpSym);
|
||||
} else {
|
||||
Idata.add(File->ImpSym);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Idata.empty()) {
|
||||
OutputSection *Sec = createSection(".idata");
|
||||
for (Chunk *C : Idata.getChunks())
|
||||
Sec->addChunk(C);
|
||||
}
|
||||
|
||||
if (!DelayIdata.empty()) {
|
||||
Defined *Helper = cast<Defined>(Config->DelayLoadHelper);
|
||||
DelayIdata.create(Helper);
|
||||
OutputSection *Sec = createSection(".didat");
|
||||
for (Chunk *C : DelayIdata.getChunks())
|
||||
Sec->addChunk(C);
|
||||
Sec = createSection(".data");
|
||||
for (Chunk *C : DelayIdata.getDataChunks())
|
||||
Sec->addChunk(C);
|
||||
Sec = createSection(".text");
|
||||
for (Chunk *C : DelayIdata.getCodeChunks())
|
||||
Sec->addChunk(C);
|
||||
}
|
||||
}
|
||||
|
||||
void Writer::createExportTable() {
|
||||
if (Config->Exports.empty())
|
||||
return;
|
||||
OutputSection *Sec = createSection(".edata");
|
||||
for (Chunk *C : Edata.Chunks)
|
||||
Sec->addChunk(C);
|
||||
}
|
||||
|
||||
// The Windows loader doesn't seem to like empty sections,
|
||||
// so we remove them if any.
|
||||
void Writer::removeEmptySections() {
|
||||
auto IsEmpty = [](OutputSection *S) { return S->getVirtualSize() == 0; };
|
||||
OutputSections.erase(
|
||||
std::remove_if(OutputSections.begin(), OutputSections.end(), IsEmpty),
|
||||
OutputSections.end());
|
||||
uint32_t Idx = 1;
|
||||
for (OutputSection *Sec : OutputSections)
|
||||
Sec->SectionIndex = Idx++;
|
||||
}
|
||||
|
||||
size_t Writer::addEntryToStringTable(StringRef Str) {
|
||||
assert(Str.size() > COFF::NameSize);
|
||||
size_t OffsetOfEntry = Strtab.size() + 4; // +4 for the size field
|
||||
Strtab.insert(Strtab.end(), Str.begin(), Str.end());
|
||||
Strtab.push_back('\0');
|
||||
return OffsetOfEntry;
|
||||
}
|
||||
|
||||
Optional<coff_symbol16> Writer::createSymbol(Defined *Def) {
|
||||
// Relative symbols are unrepresentable in a COFF symbol table.
|
||||
if (isa<DefinedSynthetic>(Def))
|
||||
return None;
|
||||
|
||||
if (auto *D = dyn_cast<DefinedRegular>(Def)) {
|
||||
// Don't write dead symbols or symbols in codeview sections to the symbol
|
||||
// table.
|
||||
if (!D->getChunk()->isLive() || D->getChunk()->isCodeView())
|
||||
return None;
|
||||
}
|
||||
|
||||
if (auto *Sym = dyn_cast<DefinedImportData>(Def))
|
||||
if (!Sym->File->Live)
|
||||
return None;
|
||||
|
||||
if (auto *Sym = dyn_cast<DefinedImportThunk>(Def))
|
||||
if (!Sym->WrappedSym->File->Live)
|
||||
return None;
|
||||
|
||||
coff_symbol16 Sym;
|
||||
StringRef Name = Def->getName();
|
||||
if (Name.size() > COFF::NameSize) {
|
||||
Sym.Name.Offset.Zeroes = 0;
|
||||
Sym.Name.Offset.Offset = addEntryToStringTable(Name);
|
||||
} else {
|
||||
memset(Sym.Name.ShortName, 0, COFF::NameSize);
|
||||
memcpy(Sym.Name.ShortName, Name.data(), Name.size());
|
||||
}
|
||||
|
||||
if (auto *D = dyn_cast<DefinedCOFF>(Def)) {
|
||||
COFFSymbolRef Ref = D->getCOFFSymbol();
|
||||
Sym.Type = Ref.getType();
|
||||
Sym.StorageClass = Ref.getStorageClass();
|
||||
} else {
|
||||
Sym.Type = IMAGE_SYM_TYPE_NULL;
|
||||
Sym.StorageClass = IMAGE_SYM_CLASS_EXTERNAL;
|
||||
}
|
||||
Sym.NumberOfAuxSymbols = 0;
|
||||
|
||||
switch (Def->kind()) {
|
||||
case SymbolBody::DefinedAbsoluteKind:
|
||||
Sym.Value = Def->getRVA();
|
||||
Sym.SectionNumber = IMAGE_SYM_ABSOLUTE;
|
||||
break;
|
||||
default: {
|
||||
uint64_t RVA = Def->getRVA();
|
||||
OutputSection *Sec = nullptr;
|
||||
for (OutputSection *S : OutputSections) {
|
||||
if (S->getRVA() > RVA)
|
||||
break;
|
||||
Sec = S;
|
||||
}
|
||||
Sym.Value = RVA - Sec->getRVA();
|
||||
Sym.SectionNumber = Sec->SectionIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Sym;
|
||||
}
|
||||
|
||||
void Writer::createSymbolAndStringTable() {
|
||||
if (!Config->Debug || !Config->WriteSymtab)
|
||||
return;
|
||||
|
||||
// Name field in the section table is 8 byte long. Longer names need
|
||||
// to be written to the string table. First, construct string table.
|
||||
for (OutputSection *Sec : OutputSections) {
|
||||
StringRef Name = Sec->getName();
|
||||
if (Name.size() <= COFF::NameSize)
|
||||
continue;
|
||||
Sec->setStringTableOff(addEntryToStringTable(Name));
|
||||
}
|
||||
|
||||
for (lld::coff::ObjectFile *File : Symtab->ObjectFiles) {
|
||||
for (SymbolBody *B : File->getSymbols()) {
|
||||
auto *D = dyn_cast<Defined>(B);
|
||||
if (!D || D->WrittenToSymtab)
|
||||
continue;
|
||||
D->WrittenToSymtab = true;
|
||||
|
||||
if (Optional<coff_symbol16> Sym = createSymbol(D))
|
||||
OutputSymtab.push_back(*Sym);
|
||||
}
|
||||
}
|
||||
|
||||
OutputSection *LastSection = OutputSections.back();
|
||||
// We position the symbol table to be adjacent to the end of the last section.
|
||||
uint64_t FileOff = LastSection->getFileOff() +
|
||||
alignTo(LastSection->getRawSize(), SectorSize);
|
||||
if (!OutputSymtab.empty()) {
|
||||
PointerToSymbolTable = FileOff;
|
||||
FileOff += OutputSymtab.size() * sizeof(coff_symbol16);
|
||||
}
|
||||
if (!Strtab.empty())
|
||||
FileOff += Strtab.size() + 4;
|
||||
FileSize = alignTo(FileOff, SectorSize);
|
||||
}
|
||||
|
||||
// Visits all sections to assign incremental, non-overlapping RVAs and
|
||||
// file offsets.
|
||||
void Writer::assignAddresses() {
|
||||
SizeOfHeaders = DOSStubSize + sizeof(PEMagic) + sizeof(coff_file_header) +
|
||||
sizeof(data_directory) * NumberfOfDataDirectory +
|
||||
sizeof(coff_section) * OutputSections.size();
|
||||
SizeOfHeaders +=
|
||||
Config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header);
|
||||
SizeOfHeaders = alignTo(SizeOfHeaders, SectorSize);
|
||||
uint64_t RVA = 0x1000; // The first page is kept unmapped.
|
||||
FileSize = SizeOfHeaders;
|
||||
// Move DISCARDABLE (or non-memory-mapped) sections to the end of file because
|
||||
// the loader cannot handle holes.
|
||||
std::stable_partition(
|
||||
OutputSections.begin(), OutputSections.end(), [](OutputSection *S) {
|
||||
return (S->getPermissions() & IMAGE_SCN_MEM_DISCARDABLE) == 0;
|
||||
});
|
||||
for (OutputSection *Sec : OutputSections) {
|
||||
if (Sec->getName() == ".reloc")
|
||||
addBaserels(Sec);
|
||||
Sec->setRVA(RVA);
|
||||
Sec->setFileOffset(FileSize);
|
||||
RVA += alignTo(Sec->getVirtualSize(), PageSize);
|
||||
FileSize += alignTo(Sec->getRawSize(), SectorSize);
|
||||
}
|
||||
SizeOfImage = SizeOfHeaders + alignTo(RVA - 0x1000, PageSize);
|
||||
}
|
||||
|
||||
template <typename PEHeaderTy> void Writer::writeHeader() {
|
||||
// Write DOS stub
|
||||
uint8_t *Buf = Buffer->getBufferStart();
|
||||
auto *DOS = reinterpret_cast<dos_header *>(Buf);
|
||||
Buf += DOSStubSize;
|
||||
DOS->Magic[0] = 'M';
|
||||
DOS->Magic[1] = 'Z';
|
||||
DOS->AddressOfRelocationTable = sizeof(dos_header);
|
||||
DOS->AddressOfNewExeHeader = DOSStubSize;
|
||||
|
||||
// Write PE magic
|
||||
memcpy(Buf, PEMagic, sizeof(PEMagic));
|
||||
Buf += sizeof(PEMagic);
|
||||
|
||||
// Write COFF header
|
||||
auto *COFF = reinterpret_cast<coff_file_header *>(Buf);
|
||||
Buf += sizeof(*COFF);
|
||||
COFF->Machine = Config->Machine;
|
||||
COFF->NumberOfSections = OutputSections.size();
|
||||
COFF->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE;
|
||||
if (Config->LargeAddressAware)
|
||||
COFF->Characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE;
|
||||
if (!Config->is64())
|
||||
COFF->Characteristics |= IMAGE_FILE_32BIT_MACHINE;
|
||||
if (Config->DLL)
|
||||
COFF->Characteristics |= IMAGE_FILE_DLL;
|
||||
if (!Config->Relocatable)
|
||||
COFF->Characteristics |= IMAGE_FILE_RELOCS_STRIPPED;
|
||||
COFF->SizeOfOptionalHeader =
|
||||
sizeof(PEHeaderTy) + sizeof(data_directory) * NumberfOfDataDirectory;
|
||||
|
||||
// Write PE header
|
||||
auto *PE = reinterpret_cast<PEHeaderTy *>(Buf);
|
||||
Buf += sizeof(*PE);
|
||||
PE->Magic = Config->is64() ? PE32Header::PE32_PLUS : PE32Header::PE32;
|
||||
|
||||
// If {Major,Minor}LinkerVersion is left at 0.0, then for some
|
||||
// reason signing the resulting PE file with Authenticode produces a
|
||||
// signature that fails to validate on Windows 7 (but is OK on 10).
|
||||
// Set it to 14.0, which is what VS2015 outputs, and which avoids
|
||||
// that problem.
|
||||
PE->MajorLinkerVersion = 14;
|
||||
PE->MinorLinkerVersion = 0;
|
||||
|
||||
PE->ImageBase = Config->ImageBase;
|
||||
PE->SectionAlignment = PageSize;
|
||||
PE->FileAlignment = SectorSize;
|
||||
PE->MajorImageVersion = Config->MajorImageVersion;
|
||||
PE->MinorImageVersion = Config->MinorImageVersion;
|
||||
PE->MajorOperatingSystemVersion = Config->MajorOSVersion;
|
||||
PE->MinorOperatingSystemVersion = Config->MinorOSVersion;
|
||||
PE->MajorSubsystemVersion = Config->MajorOSVersion;
|
||||
PE->MinorSubsystemVersion = Config->MinorOSVersion;
|
||||
PE->Subsystem = Config->Subsystem;
|
||||
PE->SizeOfImage = SizeOfImage;
|
||||
PE->SizeOfHeaders = SizeOfHeaders;
|
||||
if (!Config->NoEntry) {
|
||||
Defined *Entry = cast<Defined>(Config->Entry);
|
||||
PE->AddressOfEntryPoint = Entry->getRVA();
|
||||
// Pointer to thumb code must have the LSB set, so adjust it.
|
||||
if (Config->Machine == ARMNT)
|
||||
PE->AddressOfEntryPoint |= 1;
|
||||
}
|
||||
PE->SizeOfStackReserve = Config->StackReserve;
|
||||
PE->SizeOfStackCommit = Config->StackCommit;
|
||||
PE->SizeOfHeapReserve = Config->HeapReserve;
|
||||
PE->SizeOfHeapCommit = Config->HeapCommit;
|
||||
|
||||
// Import Descriptor Tables and Import Address Tables are merged
|
||||
// in our output. That's not compatible with the Binding feature
|
||||
// that is sort of prelinking. Setting this flag to make it clear
|
||||
// that our outputs are not for the Binding.
|
||||
PE->DLLCharacteristics = IMAGE_DLL_CHARACTERISTICS_NO_BIND;
|
||||
|
||||
if (Config->AppContainer)
|
||||
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_APPCONTAINER;
|
||||
if (Config->DynamicBase)
|
||||
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE;
|
||||
if (Config->HighEntropyVA)
|
||||
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA;
|
||||
if (Config->NxCompat)
|
||||
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NX_COMPAT;
|
||||
if (!Config->AllowIsolation)
|
||||
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION;
|
||||
if (Config->TerminalServerAware)
|
||||
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE;
|
||||
PE->NumberOfRvaAndSize = NumberfOfDataDirectory;
|
||||
if (OutputSection *Text = findSection(".text")) {
|
||||
PE->BaseOfCode = Text->getRVA();
|
||||
PE->SizeOfCode = Text->getRawSize();
|
||||
}
|
||||
PE->SizeOfInitializedData = getSizeOfInitializedData();
|
||||
|
||||
// Write data directory
|
||||
auto *Dir = reinterpret_cast<data_directory *>(Buf);
|
||||
Buf += sizeof(*Dir) * NumberfOfDataDirectory;
|
||||
if (OutputSection *Sec = findSection(".edata")) {
|
||||
Dir[EXPORT_TABLE].RelativeVirtualAddress = Sec->getRVA();
|
||||
Dir[EXPORT_TABLE].Size = Sec->getVirtualSize();
|
||||
}
|
||||
if (!Idata.empty()) {
|
||||
Dir[IMPORT_TABLE].RelativeVirtualAddress = Idata.getDirRVA();
|
||||
Dir[IMPORT_TABLE].Size = Idata.getDirSize();
|
||||
Dir[IAT].RelativeVirtualAddress = Idata.getIATRVA();
|
||||
Dir[IAT].Size = Idata.getIATSize();
|
||||
}
|
||||
if (OutputSection *Sec = findSection(".rsrc")) {
|
||||
Dir[RESOURCE_TABLE].RelativeVirtualAddress = Sec->getRVA();
|
||||
Dir[RESOURCE_TABLE].Size = Sec->getVirtualSize();
|
||||
}
|
||||
if (OutputSection *Sec = findSection(".pdata")) {
|
||||
Dir[EXCEPTION_TABLE].RelativeVirtualAddress = Sec->getRVA();
|
||||
Dir[EXCEPTION_TABLE].Size = Sec->getVirtualSize();
|
||||
}
|
||||
if (OutputSection *Sec = findSection(".reloc")) {
|
||||
Dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = Sec->getRVA();
|
||||
Dir[BASE_RELOCATION_TABLE].Size = Sec->getVirtualSize();
|
||||
}
|
||||
if (Symbol *Sym = Symtab->findUnderscore("_tls_used")) {
|
||||
if (Defined *B = dyn_cast<Defined>(Sym->body())) {
|
||||
Dir[TLS_TABLE].RelativeVirtualAddress = B->getRVA();
|
||||
Dir[TLS_TABLE].Size = Config->is64()
|
||||
? sizeof(object::coff_tls_directory64)
|
||||
: sizeof(object::coff_tls_directory32);
|
||||
}
|
||||
}
|
||||
if (Config->Debug) {
|
||||
Dir[DEBUG_DIRECTORY].RelativeVirtualAddress = DebugDirectory->getRVA();
|
||||
Dir[DEBUG_DIRECTORY].Size = DebugDirectory->getSize();
|
||||
}
|
||||
if (Symbol *Sym = Symtab->findUnderscore("_load_config_used")) {
|
||||
if (auto *B = dyn_cast<DefinedRegular>(Sym->body())) {
|
||||
SectionChunk *SC = B->getChunk();
|
||||
assert(B->getRVA() >= SC->getRVA());
|
||||
uint64_t OffsetInChunk = B->getRVA() - SC->getRVA();
|
||||
if (!SC->hasData() || OffsetInChunk + 4 > SC->getSize())
|
||||
fatal("_load_config_used is malformed");
|
||||
|
||||
ArrayRef<uint8_t> SecContents = SC->getContents();
|
||||
uint32_t LoadConfigSize =
|
||||
*reinterpret_cast<const ulittle32_t *>(&SecContents[OffsetInChunk]);
|
||||
if (OffsetInChunk + LoadConfigSize > SC->getSize())
|
||||
fatal("_load_config_used is too large");
|
||||
Dir[LOAD_CONFIG_TABLE].RelativeVirtualAddress = B->getRVA();
|
||||
Dir[LOAD_CONFIG_TABLE].Size = LoadConfigSize;
|
||||
}
|
||||
}
|
||||
if (!DelayIdata.empty()) {
|
||||
Dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress =
|
||||
DelayIdata.getDirRVA();
|
||||
Dir[DELAY_IMPORT_DESCRIPTOR].Size = DelayIdata.getDirSize();
|
||||
}
|
||||
|
||||
// Write section table
|
||||
for (OutputSection *Sec : OutputSections) {
|
||||
Sec->writeHeaderTo(Buf);
|
||||
Buf += sizeof(coff_section);
|
||||
}
|
||||
SectionTable = ArrayRef<uint8_t>(
|
||||
Buf - OutputSections.size() * sizeof(coff_section), Buf);
|
||||
|
||||
if (OutputSymtab.empty())
|
||||
return;
|
||||
|
||||
COFF->PointerToSymbolTable = PointerToSymbolTable;
|
||||
uint32_t NumberOfSymbols = OutputSymtab.size();
|
||||
COFF->NumberOfSymbols = NumberOfSymbols;
|
||||
auto *SymbolTable = reinterpret_cast<coff_symbol16 *>(
|
||||
Buffer->getBufferStart() + COFF->PointerToSymbolTable);
|
||||
for (size_t I = 0; I != NumberOfSymbols; ++I)
|
||||
SymbolTable[I] = OutputSymtab[I];
|
||||
// Create the string table, it follows immediately after the symbol table.
|
||||
// The first 4 bytes is length including itself.
|
||||
Buf = reinterpret_cast<uint8_t *>(&SymbolTable[NumberOfSymbols]);
|
||||
write32le(Buf, Strtab.size() + 4);
|
||||
if (!Strtab.empty())
|
||||
memcpy(Buf + 4, Strtab.data(), Strtab.size());
|
||||
}
|
||||
|
||||
void Writer::openFile(StringRef Path) {
|
||||
Buffer = check(
|
||||
FileOutputBuffer::create(Path, FileSize, FileOutputBuffer::F_executable),
|
||||
"failed to open " + Path);
|
||||
}
|
||||
|
||||
void Writer::fixSafeSEHSymbols() {
|
||||
if (!SEHTable)
|
||||
return;
|
||||
// Replace the absolute table symbol with a synthetic symbol pointing to the
|
||||
// SEHTable chunk so that we can emit base relocations for it and resolve
|
||||
// section relative relocations.
|
||||
Symbol *T = Symtab->find("___safe_se_handler_table");
|
||||
Symbol *C = Symtab->find("___safe_se_handler_count");
|
||||
replaceBody<DefinedSynthetic>(T, T->body()->getName(), SEHTable);
|
||||
cast<DefinedAbsolute>(C->body())->setVA(SEHTable->getSize() / 4);
|
||||
}
|
||||
|
||||
// Handles /section options to allow users to overwrite
|
||||
// section attributes.
|
||||
void Writer::setSectionPermissions() {
|
||||
for (auto &P : Config->Section) {
|
||||
StringRef Name = P.first;
|
||||
uint32_t Perm = P.second;
|
||||
if (auto *Sec = findSection(Name))
|
||||
Sec->setPermissions(Perm);
|
||||
}
|
||||
}
|
||||
|
||||
// Write section contents to a mmap'ed file.
|
||||
void Writer::writeSections() {
|
||||
// Record the section index that should be used when resolving a section
|
||||
// relocation against an absolute symbol.
|
||||
DefinedAbsolute::OutputSectionIndex = OutputSections.size() + 1;
|
||||
|
||||
uint8_t *Buf = Buffer->getBufferStart();
|
||||
for (OutputSection *Sec : OutputSections) {
|
||||
uint8_t *SecBuf = Buf + Sec->getFileOff();
|
||||
// Fill gaps between functions in .text with INT3 instructions
|
||||
// instead of leaving as NUL bytes (which can be interpreted as
|
||||
// ADD instructions).
|
||||
if (Sec->getPermissions() & IMAGE_SCN_CNT_CODE)
|
||||
memset(SecBuf, 0xCC, Sec->getRawSize());
|
||||
for_each(parallel::par, Sec->getChunks().begin(), Sec->getChunks().end(),
|
||||
[&](Chunk *C) { C->writeTo(SecBuf); });
|
||||
}
|
||||
}
|
||||
|
||||
// Sort .pdata section contents according to PE/COFF spec 5.5.
|
||||
void Writer::sortExceptionTable() {
|
||||
OutputSection *Sec = findSection(".pdata");
|
||||
if (!Sec)
|
||||
return;
|
||||
// We assume .pdata contains function table entries only.
|
||||
uint8_t *Begin = Buffer->getBufferStart() + Sec->getFileOff();
|
||||
uint8_t *End = Begin + Sec->getVirtualSize();
|
||||
if (Config->Machine == AMD64) {
|
||||
struct Entry { ulittle32_t Begin, End, Unwind; };
|
||||
sort(parallel::par, (Entry *)Begin, (Entry *)End,
|
||||
[](const Entry &A, const Entry &B) { return A.Begin < B.Begin; });
|
||||
return;
|
||||
}
|
||||
if (Config->Machine == ARMNT) {
|
||||
struct Entry { ulittle32_t Begin, Unwind; };
|
||||
sort(parallel::par, (Entry *)Begin, (Entry *)End,
|
||||
[](const Entry &A, const Entry &B) { return A.Begin < B.Begin; });
|
||||
return;
|
||||
}
|
||||
errs() << "warning: don't know how to handle .pdata.\n";
|
||||
}
|
||||
|
||||
// Backfill the CVSignature in a PDB70 Debug Record. This backfilling allows us
|
||||
// to get reproducible builds.
|
||||
void Writer::writeBuildId() {
|
||||
// There is nothing to backfill if BuildId was not setup.
|
||||
if (BuildId == nullptr)
|
||||
return;
|
||||
|
||||
assert(BuildId->DI->Signature.CVSignature == OMF::Signature::PDB70 &&
|
||||
"only PDB 7.0 is supported");
|
||||
assert(sizeof(BuildId->DI->PDB70.Signature) == 16 &&
|
||||
"signature size mismatch");
|
||||
|
||||
// Compute an MD5 hash.
|
||||
ArrayRef<uint8_t> Buf(Buffer->getBufferStart(), Buffer->getBufferEnd());
|
||||
memcpy(BuildId->DI->PDB70.Signature, MD5::hash(Buf).data(), 16);
|
||||
|
||||
// TODO(compnerd) track the Age
|
||||
BuildId->DI->PDB70.Age = 1;
|
||||
}
|
||||
|
||||
OutputSection *Writer::findSection(StringRef Name) {
|
||||
for (OutputSection *Sec : OutputSections)
|
||||
if (Sec->getName() == Name)
|
||||
return Sec;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t Writer::getSizeOfInitializedData() {
|
||||
uint32_t Res = 0;
|
||||
for (OutputSection *S : OutputSections)
|
||||
if (S->getPermissions() & IMAGE_SCN_CNT_INITIALIZED_DATA)
|
||||
Res += S->getRawSize();
|
||||
return Res;
|
||||
}
|
||||
|
||||
// Returns an existing section or create a new one if not found.
|
||||
OutputSection *Writer::createSection(StringRef Name) {
|
||||
if (auto *Sec = findSection(Name))
|
||||
return Sec;
|
||||
const auto DATA = IMAGE_SCN_CNT_INITIALIZED_DATA;
|
||||
const auto BSS = IMAGE_SCN_CNT_UNINITIALIZED_DATA;
|
||||
const auto CODE = IMAGE_SCN_CNT_CODE;
|
||||
const auto DISCARDABLE = IMAGE_SCN_MEM_DISCARDABLE;
|
||||
const auto R = IMAGE_SCN_MEM_READ;
|
||||
const auto W = IMAGE_SCN_MEM_WRITE;
|
||||
const auto X = IMAGE_SCN_MEM_EXECUTE;
|
||||
uint32_t Perms = StringSwitch<uint32_t>(Name)
|
||||
.Case(".bss", BSS | R | W)
|
||||
.Case(".data", DATA | R | W)
|
||||
.Cases(".didat", ".edata", ".idata", ".rdata", DATA | R)
|
||||
.Case(".reloc", DATA | DISCARDABLE | R)
|
||||
.Case(".text", CODE | R | X)
|
||||
.Default(0);
|
||||
if (!Perms)
|
||||
llvm_unreachable("unknown section name");
|
||||
auto Sec = make<OutputSection>(Name);
|
||||
Sec->addPermissions(Perms);
|
||||
OutputSections.push_back(Sec);
|
||||
return Sec;
|
||||
}
|
||||
|
||||
// Dest is .reloc section. Add contents to that section.
|
||||
void Writer::addBaserels(OutputSection *Dest) {
|
||||
std::vector<Baserel> V;
|
||||
for (OutputSection *Sec : OutputSections) {
|
||||
if (Sec == Dest)
|
||||
continue;
|
||||
// Collect all locations for base relocations.
|
||||
for (Chunk *C : Sec->getChunks())
|
||||
C->getBaserels(&V);
|
||||
// Add the addresses to .reloc section.
|
||||
if (!V.empty())
|
||||
addBaserelBlocks(Dest, V);
|
||||
V.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Add addresses to .reloc section. Note that addresses are grouped by page.
|
||||
void Writer::addBaserelBlocks(OutputSection *Dest, std::vector<Baserel> &V) {
|
||||
const uint32_t Mask = ~uint32_t(PageSize - 1);
|
||||
uint32_t Page = V[0].RVA & Mask;
|
||||
size_t I = 0, J = 1;
|
||||
for (size_t E = V.size(); J < E; ++J) {
|
||||
uint32_t P = V[J].RVA & Mask;
|
||||
if (P == Page)
|
||||
continue;
|
||||
Dest->addChunk(make<BaserelChunk>(Page, &V[I], &V[0] + J));
|
||||
I = J;
|
||||
Page = P;
|
||||
}
|
||||
if (I == J)
|
||||
return;
|
||||
Dest->addChunk(make<BaserelChunk>(Page, &V[I], &V[0] + J));
|
||||
}
|
||||
75
deps/lld/COFF/Writer.h
vendored
Normal file
75
deps/lld/COFF/Writer.h
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
//===- Writer.h -------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_COFF_WRITER_H
|
||||
#define LLD_COFF_WRITER_H
|
||||
|
||||
#include "Chunks.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
class SymbolTable;
|
||||
|
||||
static const int PageSize = 4096;
|
||||
|
||||
void writeResult(SymbolTable *T);
|
||||
|
||||
// OutputSection represents a section in an output file. It's a
|
||||
// container of chunks. OutputSection and Chunk are 1:N relationship.
|
||||
// Chunks cannot belong to more than one OutputSections. The writer
|
||||
// creates multiple OutputSections and assign them unique,
|
||||
// non-overlapping file offsets and RVAs.
|
||||
class OutputSection {
|
||||
public:
|
||||
OutputSection(llvm::StringRef N) : Name(N), Header({}) {}
|
||||
void setRVA(uint64_t);
|
||||
void setFileOffset(uint64_t);
|
||||
void addChunk(Chunk *C);
|
||||
llvm::StringRef getName() { return Name; }
|
||||
std::vector<Chunk *> &getChunks() { return Chunks; }
|
||||
void addPermissions(uint32_t C);
|
||||
void setPermissions(uint32_t C);
|
||||
uint32_t getPermissions() { return Header.Characteristics & PermMask; }
|
||||
uint32_t getCharacteristics() { return Header.Characteristics; }
|
||||
uint64_t getRVA() { return Header.VirtualAddress; }
|
||||
uint64_t getFileOff() { return Header.PointerToRawData; }
|
||||
void writeHeaderTo(uint8_t *Buf);
|
||||
|
||||
// Returns the size of this section in an executable memory image.
|
||||
// This may be smaller than the raw size (the raw size is multiple
|
||||
// of disk sector size, so there may be padding at end), or may be
|
||||
// larger (if that's the case, the loader reserves spaces after end
|
||||
// of raw data).
|
||||
uint64_t getVirtualSize() { return Header.VirtualSize; }
|
||||
|
||||
// Returns the size of the section in the output file.
|
||||
uint64_t getRawSize() { return Header.SizeOfRawData; }
|
||||
|
||||
// Set offset into the string table storing this section name.
|
||||
// Used only when the name is longer than 8 bytes.
|
||||
void setStringTableOff(uint32_t V) { StringTableOff = V; }
|
||||
|
||||
// N.B. The section index is one based.
|
||||
uint32_t SectionIndex = 0;
|
||||
|
||||
private:
|
||||
llvm::StringRef Name;
|
||||
llvm::object::coff_section Header;
|
||||
uint32_t StringTableOff = 0;
|
||||
std::vector<Chunk *> Chunks;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
376
deps/lld/ELF/Arch/AArch64.cpp
vendored
Normal file
376
deps/lld/ELF/Arch/AArch64.cpp
vendored
Normal file
@ -0,0 +1,376 @@
|
||||
//===- AArch64.cpp --------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "Thunks.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 final : public TargetInfo {
|
||||
public:
|
||||
AArch64();
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const override;
|
||||
bool isPicRel(uint32_t Type) const override;
|
||||
void writeGotPlt(uint8_t *Buf, const SymbolBody &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 usesOnlyLowPageBits(uint32_t Type) const override;
|
||||
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
RelExpr adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
|
||||
RelExpr Expr) const override;
|
||||
void relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
void relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
void relaxTlsIeToLe(uint8_t *Loc, uint32_t 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;
|
||||
PltRel = R_AARCH64_JUMP_SLOT;
|
||||
TlsDescRel = R_AARCH64_TLSDESC;
|
||||
TlsGotRel = R_AARCH64_TLS_TPREL64;
|
||||
GotEntrySize = 8;
|
||||
GotPltEntrySize = 8;
|
||||
PltEntrySize = 16;
|
||||
PltHeaderSize = 32;
|
||||
DefaultMaxPageSize = 65536;
|
||||
|
||||
// It doesn't seem to be documented anywhere, but tls on aarch64 uses variant
|
||||
// 1 of the tls structures and the tcb size is 16.
|
||||
TcbSize = 16;
|
||||
}
|
||||
|
||||
RelExpr AArch64::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
default:
|
||||
return R_ABS;
|
||||
case R_AARCH64_TLSDESC_ADR_PAGE21:
|
||||
return R_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:
|
||||
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:
|
||||
return R_PC;
|
||||
case R_AARCH64_ADR_PREL_PG_HI21:
|
||||
return R_PAGE_PC;
|
||||
case R_AARCH64_LD64_GOT_LO12_NC:
|
||||
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
|
||||
return R_GOT;
|
||||
case R_AARCH64_ADR_GOT_PAGE:
|
||||
case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
|
||||
return R_GOT_PAGE_PC;
|
||||
case R_AARCH64_NONE:
|
||||
return R_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
RelExpr AArch64::adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
|
||||
RelExpr Expr) const {
|
||||
if (Expr == R_RELAX_TLS_GD_TO_IE) {
|
||||
if (Type == R_AARCH64_TLSDESC_ADR_PAGE21)
|
||||
return R_RELAX_TLS_GD_TO_IE_PAGE_PC;
|
||||
return R_RELAX_TLS_GD_TO_IE_ABS;
|
||||
}
|
||||
return Expr;
|
||||
}
|
||||
|
||||
bool AArch64::usesOnlyLowPageBits(uint32_t 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;
|
||||
}
|
||||
}
|
||||
|
||||
bool AArch64::isPicRel(uint32_t Type) const {
|
||||
return Type == R_AARCH64_ABS32 || Type == R_AARCH64_ABS64;
|
||||
}
|
||||
|
||||
void AArch64::writeGotPlt(uint8_t *Buf, const SymbolBody &) const {
|
||||
write64le(Buf, InX::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 = InX::GotPlt->getVA();
|
||||
uint64_t Plt = InX::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);
|
||||
}
|
||||
|
||||
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, uint32_t Type, uint64_t Val) const {
|
||||
switch (Type) {
|
||||
case R_AARCH64_ABS16:
|
||||
case R_AARCH64_PREL16:
|
||||
checkIntUInt<16>(Loc, Val, Type);
|
||||
write16le(Loc, Val);
|
||||
break;
|
||||
case R_AARCH64_ABS32:
|
||||
case R_AARCH64_PREL32:
|
||||
checkIntUInt<32>(Loc, Val, Type);
|
||||
write32le(Loc, Val);
|
||||
break;
|
||||
case R_AARCH64_ABS64:
|
||||
case R_AARCH64_GLOB_DAT:
|
||||
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<33>(Loc, Val, Type);
|
||||
write32AArch64Addr(Loc, Val >> 12);
|
||||
break;
|
||||
case R_AARCH64_ADR_PREL_LO21:
|
||||
checkInt<21>(Loc, Val, Type);
|
||||
write32AArch64Addr(Loc, Val);
|
||||
break;
|
||||
case R_AARCH64_CALL26:
|
||||
case R_AARCH64_JUMP26:
|
||||
checkInt<28>(Loc, Val, Type);
|
||||
or32le(Loc, (Val & 0x0FFFFFFC) >> 2);
|
||||
break;
|
||||
case R_AARCH64_CONDBR19:
|
||||
checkInt<21>(Loc, Val, Type);
|
||||
or32le(Loc, (Val & 0x1FFFFC) << 3);
|
||||
break;
|
||||
case R_AARCH64_LD64_GOT_LO12_NC:
|
||||
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
|
||||
case R_AARCH64_TLSDESC_LD64_LO12:
|
||||
checkAlignment<8>(Loc, Val, Type);
|
||||
or32le(Loc, (Val & 0xFF8) << 7);
|
||||
break;
|
||||
case R_AARCH64_LDST8_ABS_LO12_NC:
|
||||
or32AArch64Imm(Loc, getBits(Val, 0, 11));
|
||||
break;
|
||||
case R_AARCH64_LDST16_ABS_LO12_NC:
|
||||
or32AArch64Imm(Loc, getBits(Val, 1, 11));
|
||||
break;
|
||||
case R_AARCH64_LDST32_ABS_LO12_NC:
|
||||
or32AArch64Imm(Loc, getBits(Val, 2, 11));
|
||||
break;
|
||||
case R_AARCH64_LDST64_ABS_LO12_NC:
|
||||
or32AArch64Imm(Loc, getBits(Val, 3, 11));
|
||||
break;
|
||||
case R_AARCH64_LDST128_ABS_LO12_NC:
|
||||
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<16>(Loc, Val, Type);
|
||||
or32le(Loc, (Val & 0xFFFC) << 3);
|
||||
break;
|
||||
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
|
||||
checkInt<24>(Loc, Val, 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 reloc " + Twine(Type));
|
||||
}
|
||||
}
|
||||
|
||||
void AArch64::relaxTlsGdToLe(uint8_t *Loc, uint32_t 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<32>(Loc, Val, 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, uint32_t 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, uint32_t Type, uint64_t Val) const {
|
||||
checkUInt<32>(Loc, Val, 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");
|
||||
}
|
||||
|
||||
TargetInfo *elf::getAArch64TargetInfo() {
|
||||
static AArch64 Target;
|
||||
return &Target;
|
||||
}
|
||||
84
deps/lld/ELF/Arch/AMDGPU.cpp
vendored
Normal file
84
deps/lld/ELF/Arch/AMDGPU.cpp
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
//===- AMDGPU.cpp ---------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "Target.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();
|
||||
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
AMDGPU::AMDGPU() {
|
||||
RelativeRel = R_AMDGPU_REL64;
|
||||
GotRel = R_AMDGPU_ABS64;
|
||||
GotEntrySize = 8;
|
||||
}
|
||||
|
||||
void AMDGPU::relocateOne(uint8_t *Loc, uint32_t 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:
|
||||
write64le(Loc, Val);
|
||||
break;
|
||||
case R_AMDGPU_GOTPCREL32_HI:
|
||||
case R_AMDGPU_REL32_HI:
|
||||
write32le(Loc, Val >> 32);
|
||||
break;
|
||||
default:
|
||||
error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
|
||||
}
|
||||
}
|
||||
|
||||
RelExpr AMDGPU::getRelExpr(uint32_t Type, const SymbolBody &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:
|
||||
return R_PC;
|
||||
case R_AMDGPU_GOTPCREL:
|
||||
case R_AMDGPU_GOTPCREL32_LO:
|
||||
case R_AMDGPU_GOTPCREL32_HI:
|
||||
return R_GOT_PC;
|
||||
default:
|
||||
error(toString(S.File) + ": unknown relocation type: " + toString(Type));
|
||||
return R_HINT;
|
||||
}
|
||||
}
|
||||
|
||||
TargetInfo *elf::getAMDGPUTargetInfo() {
|
||||
static AMDGPU Target;
|
||||
return &Target;
|
||||
}
|
||||
480
deps/lld/ELF/Arch/ARM.cpp
vendored
Normal file
480
deps/lld/ELF/Arch/ARM.cpp
vendored
Normal file
@ -0,0 +1,480 @@
|
||||
//===- ARM.cpp ------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "Thunks.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();
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const override;
|
||||
bool isPicRel(uint32_t Type) const override;
|
||||
uint32_t getDynRel(uint32_t Type) const override;
|
||||
int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override;
|
||||
void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
|
||||
void writeIgotPlt(uint8_t *Buf, const SymbolBody &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(InputSectionBase *IS, uint64_t Off) const override;
|
||||
void addPltHeaderSymbols(InputSectionBase *ISD) const override;
|
||||
bool needsThunk(RelExpr Expr, uint32_t RelocType, const InputFile *File,
|
||||
const SymbolBody &S) const override;
|
||||
bool inBranchRange(uint32_t RelocType, uint64_t Src,
|
||||
uint64_t Dst) const override;
|
||||
void relocateOne(uint8_t *Loc, uint32_t 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;
|
||||
PltRel = R_ARM_JUMP_SLOT;
|
||||
TlsGotRel = R_ARM_TLS_TPOFF32;
|
||||
TlsModuleIndexRel = R_ARM_TLS_DTPMOD32;
|
||||
TlsOffsetRel = R_ARM_TLS_DTPOFF32;
|
||||
GotEntrySize = 4;
|
||||
GotPltEntrySize = 4;
|
||||
PltEntrySize = 16;
|
||||
PltHeaderSize = 20;
|
||||
TrapInstr = 0xd4d4d4d4;
|
||||
// ARM uses Variant 1 TLS
|
||||
TcbSize = 8;
|
||||
NeedsThunks = true;
|
||||
}
|
||||
|
||||
RelExpr ARM::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
default:
|
||||
return R_ABS;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
bool ARM::isPicRel(uint32_t Type) const {
|
||||
return (Type == R_ARM_TARGET1 && !Config->Target1Rel) ||
|
||||
(Type == R_ARM_ABS32);
|
||||
}
|
||||
|
||||
uint32_t ARM::getDynRel(uint32_t Type) const {
|
||||
if (Type == R_ARM_TARGET1 && !Config->Target1Rel)
|
||||
return R_ARM_ABS32;
|
||||
if (Type == R_ARM_ABS32)
|
||||
return Type;
|
||||
// Keep it going with a dummy value so that we can find more reloc errors.
|
||||
return R_ARM_ABS32;
|
||||
}
|
||||
|
||||
void ARM::writeGotPlt(uint8_t *Buf, const SymbolBody &) const {
|
||||
write32le(Buf, InX::Plt->getVA());
|
||||
}
|
||||
|
||||
void ARM::writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const {
|
||||
// An ARM entry is the address of the ifunc resolver function.
|
||||
write32le(Buf, S.getVA());
|
||||
}
|
||||
|
||||
void ARM::writePltHeader(uint8_t *Buf) const {
|
||||
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
|
||||
};
|
||||
memcpy(Buf, PltData, sizeof(PltData));
|
||||
uint64_t GotPlt = InX::GotPlt->getVA();
|
||||
uint64_t L1 = InX::Plt->getVA() + 8;
|
||||
write32le(Buf + 16, GotPlt - L1 - 8);
|
||||
}
|
||||
|
||||
void ARM::addPltHeaderSymbols(InputSectionBase *ISD) const {
|
||||
auto *IS = cast<InputSection>(ISD);
|
||||
addSyntheticLocal("$a", STT_NOTYPE, 0, 0, IS);
|
||||
addSyntheticLocal("$d", STT_NOTYPE, 16, 0, IS);
|
||||
}
|
||||
|
||||
void ARM::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
uint64_t PltEntryAddr, int32_t Index,
|
||||
unsigned RelOff) const {
|
||||
// FIXME: Using simple code sequence with simple relocations.
|
||||
// There is a more optimal sequence but it requires support for the group
|
||||
// relocations. See ELF for the ARM Architecture Appendix A.3
|
||||
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);
|
||||
}
|
||||
|
||||
void ARM::addPltSymbols(InputSectionBase *ISD, uint64_t Off) const {
|
||||
auto *IS = cast<InputSection>(ISD);
|
||||
addSyntheticLocal("$a", STT_NOTYPE, Off, 0, IS);
|
||||
addSyntheticLocal("$d", STT_NOTYPE, Off + 12, 0, IS);
|
||||
}
|
||||
|
||||
bool ARM::needsThunk(RelExpr Expr, uint32_t RelocType, const InputFile *File,
|
||||
const SymbolBody &S) const {
|
||||
// If S is an undefined weak symbol in an executable we don't need a Thunk.
|
||||
// In a DSO calls to undefined symbols, including weak ones get PLT entries
|
||||
// which may need a thunk.
|
||||
if (S.isUndefined() && !S.isLocal() && S.symbol()->isWeak() &&
|
||||
!Config->Shared)
|
||||
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 (RelocType) {
|
||||
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;
|
||||
break;
|
||||
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;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ARM::inBranchRange(uint32_t RelocType, uint64_t Src, uint64_t Dst) const {
|
||||
uint64_t Range;
|
||||
uint64_t InstrSize;
|
||||
|
||||
switch (RelocType) {
|
||||
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 = 0x1000000;
|
||||
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, uint32_t Type, uint64_t Val) const {
|
||||
switch (Type) {
|
||||
case R_ARM_ABS32:
|
||||
case R_ARM_BASE_PREL:
|
||||
case R_ARM_GLOB_DAT:
|
||||
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_TLS_DTPMOD32:
|
||||
write32le(Loc, 1);
|
||||
break;
|
||||
case R_ARM_PREL31:
|
||||
checkInt<31>(Loc, Val, 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<26>(Loc, Val, 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<26>(Loc, Val, Type);
|
||||
write32le(Loc, (read32le(Loc) & ~0x00ffffff) | ((Val >> 2) & 0x00ffffff));
|
||||
break;
|
||||
case R_ARM_THM_JUMP11:
|
||||
checkInt<12>(Loc, Val, 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<21>(Loc, Val, 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);
|
||||
// Fall through as rest of encoding is the same as B.W
|
||||
LLVM_FALLTHROUGH;
|
||||
case R_ARM_THM_JUMP24:
|
||||
// Encoding B T4, BL T1, BLX T2: Val = S:I1:I2:imm10:imm11:0
|
||||
// FIXME: Use of I1 and I2 require v6T2ops
|
||||
checkInt<25>(Loc, Val, 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:
|
||||
checkInt<32>(Loc, Val, Type);
|
||||
write32le(Loc, (read32le(Loc) & ~0x000f0fff) |
|
||||
(((Val >> 16) & 0xf000) << 4) | ((Val >> 16) & 0xfff));
|
||||
break;
|
||||
case R_ARM_THM_MOVT_ABS:
|
||||
case R_ARM_THM_MOVT_PREL:
|
||||
// Encoding T1: A = imm4:i:imm3:imm8
|
||||
checkInt<32>(Loc, Val, Type);
|
||||
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 reloc " + Twine(Type));
|
||||
}
|
||||
}
|
||||
|
||||
int64_t ARM::getImplicitAddend(const uint8_t *Buf, uint32_t 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:
|
||||
case R_ARM_THM_JUMP24: {
|
||||
// Encoding B T4, BL T1, BLX T2: A = S:I1:I2:imm10:imm11:0
|
||||
// I1 = NOT(J1 EOR S), I2 = NOT(J2 EOR S)
|
||||
// FIXME: I1 and I2 require v6T2ops
|
||||
uint16_t Hi = read16le(Buf);
|
||||
uint16_t Lo = read16le(Buf + 2);
|
||||
return SignExtend64<24>(((Hi & 0x0400) << 14) | // S
|
||||
(~((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;
|
||||
}
|
||||
80
deps/lld/ELF/Arch/AVR.cpp
vendored
Normal file
80
deps/lld/ELF/Arch/AVR.cpp
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
//===- AVR.cpp ------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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 "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "Target.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:
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const override;
|
||||
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
RelExpr AVR::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
case R_AVR_CALL:
|
||||
return R_ABS;
|
||||
default:
|
||||
error(toString(S.File) + ": unknown relocation type: " + toString(Type));
|
||||
return R_HINT;
|
||||
}
|
||||
}
|
||||
|
||||
void AVR::relocateOne(uint8_t *Loc, uint32_t 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 reloc " + toString(Type));
|
||||
}
|
||||
}
|
||||
|
||||
TargetInfo *elf::getAVRTargetInfo() {
|
||||
static AVR Target;
|
||||
return &Target;
|
||||
}
|
||||
423
deps/lld/ELF/Arch/Mips.cpp
vendored
Normal file
423
deps/lld/ELF/Arch/Mips.cpp
vendored
Normal file
@ -0,0 +1,423 @@
|
||||
//===- MIPS.cpp -----------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "OutputSections.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "Thunks.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();
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const override;
|
||||
int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override;
|
||||
bool isPicRel(uint32_t Type) const override;
|
||||
uint32_t getDynRel(uint32_t Type) const override;
|
||||
void writeGotPlt(uint8_t *Buf, const SymbolBody &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, uint32_t RelocType, const InputFile *File,
|
||||
const SymbolBody &S) const override;
|
||||
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
bool usesOnlyLowPageBits(uint32_t Type) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
template <class ELFT> MIPS<ELFT>::MIPS() {
|
||||
GotPltHeaderEntriesNum = 2;
|
||||
DefaultMaxPageSize = 65536;
|
||||
GotEntrySize = sizeof(typename ELFT::uint);
|
||||
GotPltEntrySize = sizeof(typename ELFT::uint);
|
||||
PltEntrySize = 16;
|
||||
PltHeaderSize = 32;
|
||||
CopyRel = R_MIPS_COPY;
|
||||
PltRel = R_MIPS_JUMP_SLOT;
|
||||
NeedsThunks = true;
|
||||
TrapInstr = 0xefefefef;
|
||||
|
||||
if (ELFT::Is64Bits) {
|
||||
RelativeRel = (R_MIPS_64 << 8) | R_MIPS_REL32;
|
||||
TlsGotRel = R_MIPS_TLS_TPREL64;
|
||||
TlsModuleIndexRel = R_MIPS_TLS_DTPMOD64;
|
||||
TlsOffsetRel = R_MIPS_TLS_DTPREL64;
|
||||
} else {
|
||||
RelativeRel = R_MIPS_REL32;
|
||||
TlsGotRel = R_MIPS_TLS_TPREL32;
|
||||
TlsModuleIndexRel = R_MIPS_TLS_DTPMOD32;
|
||||
TlsOffsetRel = R_MIPS_TLS_DTPREL32;
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
RelExpr MIPS<ELFT>::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const {
|
||||
// See comment in the calculateMipsRelChain.
|
||||
if (ELFT::Is64Bits || Config->MipsN32Abi)
|
||||
Type &= 0xff;
|
||||
switch (Type) {
|
||||
default:
|
||||
return R_ABS;
|
||||
case R_MIPS_JALR:
|
||||
return R_HINT;
|
||||
case R_MIPS_GPREL16:
|
||||
case R_MIPS_GPREL32:
|
||||
return R_MIPS_GOTREL;
|
||||
case R_MIPS_26:
|
||||
return R_PLT;
|
||||
case R_MIPS_HI16:
|
||||
case R_MIPS_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_GOT_OFST:
|
||||
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:
|
||||
return R_PC;
|
||||
case R_MIPS_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:
|
||||
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:
|
||||
return R_MIPS_GOT_OFF32;
|
||||
case R_MIPS_GOT_PAGE:
|
||||
return R_MIPS_GOT_LOCAL_PAGE;
|
||||
case R_MIPS_TLS_GD:
|
||||
return R_MIPS_TLSGD;
|
||||
case R_MIPS_TLS_LDM:
|
||||
return R_MIPS_TLSLD;
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT> bool MIPS<ELFT>::isPicRel(uint32_t Type) const {
|
||||
return Type == R_MIPS_32 || Type == R_MIPS_64;
|
||||
}
|
||||
|
||||
template <class ELFT> uint32_t MIPS<ELFT>::getDynRel(uint32_t Type) const {
|
||||
return RelativeRel;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void MIPS<ELFT>::writeGotPlt(uint8_t *Buf, const SymbolBody &) const {
|
||||
write32<ELFT::TargetEndianness>(Buf, InX::Plt->getVA());
|
||||
}
|
||||
|
||||
template <endianness E, uint8_t BSIZE, uint8_t SHIFT>
|
||||
static int64_t getPcRelocAddend(const uint8_t *Loc) {
|
||||
uint32_t Instr = read32<E>(Loc);
|
||||
uint32_t Mask = 0xffffffff >> (32 - BSIZE);
|
||||
return SignExtend64<BSIZE + SHIFT>((Instr & Mask) << SHIFT);
|
||||
}
|
||||
|
||||
template <endianness E, uint8_t BSIZE, uint8_t SHIFT>
|
||||
static void applyMipsPcReloc(uint8_t *Loc, uint32_t Type, uint64_t V) {
|
||||
uint32_t Mask = 0xffffffff >> (32 - BSIZE);
|
||||
uint32_t Instr = read32<E>(Loc);
|
||||
if (SHIFT > 0)
|
||||
checkAlignment<(1 << SHIFT)>(Loc, V, Type);
|
||||
checkInt<BSIZE + SHIFT>(Loc, V, Type);
|
||||
write32<E>(Loc, (Instr & ~Mask) | ((V >> SHIFT) & Mask));
|
||||
}
|
||||
|
||||
template <endianness E> static void writeMipsHi16(uint8_t *Loc, uint64_t V) {
|
||||
uint32_t Instr = read32<E>(Loc);
|
||||
uint16_t Res = ((V + 0x8000) >> 16) & 0xffff;
|
||||
write32<E>(Loc, (Instr & 0xffff0000) | Res);
|
||||
}
|
||||
|
||||
template <endianness E> static void writeMipsHigher(uint8_t *Loc, uint64_t V) {
|
||||
uint32_t Instr = read32<E>(Loc);
|
||||
uint16_t Res = ((V + 0x80008000) >> 32) & 0xffff;
|
||||
write32<E>(Loc, (Instr & 0xffff0000) | Res);
|
||||
}
|
||||
|
||||
template <endianness E> static void writeMipsHighest(uint8_t *Loc, uint64_t V) {
|
||||
uint32_t Instr = read32<E>(Loc);
|
||||
uint16_t Res = ((V + 0x800080008000) >> 48) & 0xffff;
|
||||
write32<E>(Loc, (Instr & 0xffff0000) | Res);
|
||||
}
|
||||
|
||||
template <endianness E> static void writeMipsLo16(uint8_t *Loc, uint64_t V) {
|
||||
uint32_t Instr = read32<E>(Loc);
|
||||
write32<E>(Loc, (Instr & 0xffff0000) | (V & 0xffff));
|
||||
}
|
||||
|
||||
template <class ELFT> static bool isMipsR6() {
|
||||
const auto &FirstObj = cast<ELFFileBase<ELFT>>(*Config->FirstElf);
|
||||
uint32_t Arch = FirstObj.getObj().getHeader()->e_flags & EF_MIPS_ARCH;
|
||||
return Arch == EF_MIPS_ARCH_32R6 || Arch == EF_MIPS_ARCH_64R6;
|
||||
}
|
||||
|
||||
template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *Buf) const {
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
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
|
||||
} 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
|
||||
write32<E>(Buf + 24, 0x0320f809); // jalr $25
|
||||
write32<E>(Buf + 28, 0x2718fffe); // subu $24, $24, 2
|
||||
|
||||
uint64_t GotPlt = InX::GotPlt->getVA();
|
||||
writeMipsHi16<E>(Buf, GotPlt);
|
||||
writeMipsLo16<E>(Buf + 4, GotPlt);
|
||||
writeMipsLo16<E>(Buf + 8, GotPlt);
|
||||
}
|
||||
|
||||
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;
|
||||
write32<E>(Buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry)
|
||||
write32<E>(Buf + 4, 0x8df90000); // l[wd] $25, %lo(.got.plt entry)($15)
|
||||
// jr $25
|
||||
write32<E>(Buf + 8, isMipsR6<ELFT>() ? 0x03200009 : 0x03200008);
|
||||
write32<E>(Buf + 12, 0x25f80000); // addiu $24, $15, %lo(.got.plt entry)
|
||||
writeMipsHi16<E>(Buf, GotPltEntryAddr);
|
||||
writeMipsLo16<E>(Buf + 4, GotPltEntryAddr);
|
||||
writeMipsLo16<E>(Buf + 12, GotPltEntryAddr);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
bool MIPS<ELFT>::needsThunk(RelExpr Expr, uint32_t Type, const InputFile *File,
|
||||
const SymbolBody &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)
|
||||
return false;
|
||||
auto *F = dyn_cast_or_null<ELFFileBase<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<DefinedRegular>(&S);
|
||||
// LA25 is required if target file has PIC code
|
||||
// or target symbol is a PIC symbol.
|
||||
return D && D->isMipsPIC<ELFT>();
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *Buf, uint32_t Type) const {
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
switch (Type) {
|
||||
default:
|
||||
return 0;
|
||||
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) & 0x3ffffff) << 2);
|
||||
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_MIPS_PC16:
|
||||
return getPcRelocAddend<E, 16, 2>(Buf);
|
||||
case R_MIPS_PC19_S2:
|
||||
return getPcRelocAddend<E, 19, 2>(Buf);
|
||||
case R_MIPS_PC21_S2:
|
||||
return getPcRelocAddend<E, 21, 2>(Buf);
|
||||
case R_MIPS_PC26_S2:
|
||||
return getPcRelocAddend<E, 26, 2>(Buf);
|
||||
case R_MIPS_PC32:
|
||||
return getPcRelocAddend<E, 32, 0>(Buf);
|
||||
}
|
||||
}
|
||||
|
||||
static std::pair<uint32_t, uint64_t>
|
||||
calculateMipsRelChain(uint8_t *Loc, uint32_t 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
|
||||
uint32_t Type2 = (Type >> 8) & 0xff;
|
||||
uint32_t 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);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void MIPS<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
// 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)
|
||||
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)
|
||||
Val -= 0x7000;
|
||||
if (ELFT::Is64Bits || Config->MipsN32Abi)
|
||||
std::tie(Type, Val) = calculateMipsRelChain(Loc, Type, Val);
|
||||
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:
|
||||
write32<E>(Loc, (read32<E>(Loc) & ~0x3ffffff) | ((Val >> 2) & 0x3ffffff));
|
||||
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)
|
||||
writeMipsHi16<E>(Loc, Val);
|
||||
else {
|
||||
checkInt<16>(Loc, Val, Type);
|
||||
writeMipsLo16<E>(Loc, Val);
|
||||
}
|
||||
break;
|
||||
case R_MIPS_GOT_DISP:
|
||||
case R_MIPS_GOT_PAGE:
|
||||
case R_MIPS_GPREL16:
|
||||
case R_MIPS_TLS_GD:
|
||||
case R_MIPS_TLS_LDM:
|
||||
checkInt<16>(Loc, Val, Type);
|
||||
LLVM_FALLTHROUGH;
|
||||
case R_MIPS_CALL16:
|
||||
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_GOTTPREL:
|
||||
case R_MIPS_TLS_TPREL_LO16:
|
||||
writeMipsLo16<E>(Loc, Val);
|
||||
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:
|
||||
writeMipsHi16<E>(Loc, Val);
|
||||
break;
|
||||
case R_MIPS_HIGHER:
|
||||
writeMipsHigher<E>(Loc, Val);
|
||||
break;
|
||||
case R_MIPS_HIGHEST:
|
||||
writeMipsHighest<E>(Loc, Val);
|
||||
break;
|
||||
case R_MIPS_JALR:
|
||||
// Ignore this optimization relocation for now
|
||||
break;
|
||||
case R_MIPS_PC16:
|
||||
applyMipsPcReloc<E, 16, 2>(Loc, Type, Val);
|
||||
break;
|
||||
case R_MIPS_PC19_S2:
|
||||
applyMipsPcReloc<E, 19, 2>(Loc, Type, Val);
|
||||
break;
|
||||
case R_MIPS_PC21_S2:
|
||||
applyMipsPcReloc<E, 21, 2>(Loc, Type, Val);
|
||||
break;
|
||||
case R_MIPS_PC26_S2:
|
||||
applyMipsPcReloc<E, 26, 2>(Loc, Type, Val);
|
||||
break;
|
||||
case R_MIPS_PC32:
|
||||
applyMipsPcReloc<E, 32, 0>(Loc, Type, Val);
|
||||
break;
|
||||
default:
|
||||
error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
bool MIPS<ELFT>::usesOnlyLowPageBits(uint32_t Type) const {
|
||||
return Type == R_MIPS_LO16 || Type == R_MIPS_GOT_OFST;
|
||||
}
|
||||
|
||||
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>();
|
||||
369
deps/lld/ELF/Arch/MipsArchTree.cpp
vendored
Normal file
369
deps/lld/ELF/Arch/MipsArchTree.cpp
vendored
Normal file
@ -0,0 +1,369 @@
|
||||
//===- MipsArchTree.cpp --------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===---------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains a helper function for the Writer.
|
||||
//
|
||||
//===---------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Writer.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 {
|
||||
StringRef Filename;
|
||||
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) {
|
||||
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.slice(1)) {
|
||||
uint32_t ABI2 = F.Flags & (EF_MIPS_ABI | EF_MIPS_ABI2);
|
||||
if (ABI != ABI2)
|
||||
error("target ABI '" + getAbiName(ABI) + "' is incompatible with '" +
|
||||
getAbiName(ABI2) + "': " + F.Filename);
|
||||
|
||||
bool Nan2 = F.Flags & EF_MIPS_NAN2008;
|
||||
if (Nan != Nan2)
|
||||
error("target -mnan=" + getNanName(Nan) + " is incompatible with -mnan=" +
|
||||
getNanName(Nan2) + ": " + F.Filename);
|
||||
|
||||
bool Fp2 = F.Flags & EF_MIPS_FP64;
|
||||
if (Fp != Fp2)
|
||||
error("target -mfp" + getFpName(Fp) + " is incompatible with -mfp" +
|
||||
getFpName(Fp2) + ": " + F.Filename);
|
||||
}
|
||||
}
|
||||
|
||||
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("linking abicalls code with non-abicalls file: " + F.Filename);
|
||||
if (!IsPic && IsPic2)
|
||||
warn("linking non-abicalls code with abicalls file: " + F.Filename);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
StringRef S = getMachName(Flags);
|
||||
if (!S.empty())
|
||||
return S;
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
// 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("target ISA '" + getArchName(Ret) + "' is incompatible with '" +
|
||||
getArchName(New) + "': " + F.Filename);
|
||||
return 0;
|
||||
}
|
||||
Ret = New;
|
||||
}
|
||||
return Ret;
|
||||
}
|
||||
|
||||
template <class ELFT> uint32_t elf::getMipsEFlags() {
|
||||
std::vector<FileFlags> V;
|
||||
for (elf::ObjectFile<ELFT> *F : Symtab<ELFT>::X->getObjectFiles())
|
||||
V.push_back({F->getName(), 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 "-mips32r2 -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("target floating point ABI '" + getMipsFpAbiName(OldFlag) +
|
||||
"' is incompatible with '" + getMipsFpAbiName(NewFlag) +
|
||||
"': " + FileName);
|
||||
return OldFlag;
|
||||
}
|
||||
|
||||
template <class ELFT> static bool isN32Abi(const InputFile *F) {
|
||||
if (auto *EF = dyn_cast<ELFFileBase<ELFT>>(F))
|
||||
return EF->getObj().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");
|
||||
}
|
||||
}
|
||||
|
||||
template uint32_t elf::getMipsEFlags<ELF32LE>();
|
||||
template uint32_t elf::getMipsEFlags<ELF32BE>();
|
||||
template uint32_t elf::getMipsEFlags<ELF64LE>();
|
||||
template uint32_t elf::getMipsEFlags<ELF64BE>();
|
||||
65
deps/lld/ELF/Arch/PPC.cpp
vendored
Normal file
65
deps/lld/ELF/Arch/PPC.cpp
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
//===- PPC.cpp ------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "Symbols.h"
|
||||
#include "Target.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() { GotBaseSymOff = 0x8000; }
|
||||
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void PPC::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
switch (Type) {
|
||||
case R_PPC_ADDR16_HA:
|
||||
write16be(Loc, (Val + 0x8000) >> 16);
|
||||
break;
|
||||
case R_PPC_ADDR16_LO:
|
||||
write16be(Loc, Val);
|
||||
break;
|
||||
case R_PPC_ADDR32:
|
||||
case R_PPC_REL32:
|
||||
write32be(Loc, Val);
|
||||
break;
|
||||
case R_PPC_REL24:
|
||||
write32be(Loc, read32be(Loc) | (Val & 0x3FFFFFC));
|
||||
break;
|
||||
default:
|
||||
error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
|
||||
}
|
||||
}
|
||||
|
||||
RelExpr PPC::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
case R_PPC_REL24:
|
||||
case R_PPC_REL32:
|
||||
return R_PC;
|
||||
default:
|
||||
return R_ABS;
|
||||
}
|
||||
}
|
||||
|
||||
TargetInfo *elf::getPPCTargetInfo() {
|
||||
static PPC Target;
|
||||
return &Target;
|
||||
}
|
||||
217
deps/lld/ELF/Arch/PPC64.cpp
vendored
Normal file
217
deps/lld/ELF/Arch/PPC64.cpp
vendored
Normal file
@ -0,0 +1,217 @@
|
||||
//===- PPC64.cpp ----------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.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;
|
||||
|
||||
static uint64_t PPC64TocOffset = 0x8000;
|
||||
|
||||
uint64_t elf::getPPC64TocBase() {
|
||||
// The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The
|
||||
// TOC starts where the first of these sections starts. We always create a
|
||||
// .got when we see a relocation that uses it, so for us the start is always
|
||||
// the .got.
|
||||
uint64_t TocVA = InX::Got->getVA();
|
||||
|
||||
// Per the ppc64-elf-linux ABI, The TOC base is TOC value plus 0x8000
|
||||
// thus permitting a full 64 Kbytes segment. Note that the glibc startup
|
||||
// code (crt1.o) assumes that you can get from the TOC base to the
|
||||
// start of the .toc section with only a single (signed) 16-bit relocation.
|
||||
return TocVA + PPC64TocOffset;
|
||||
}
|
||||
|
||||
namespace {
|
||||
class PPC64 final : public TargetInfo {
|
||||
public:
|
||||
PPC64();
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) 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, uint32_t Type, uint64_t Val) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// Relocation masks following the #lo(value), #hi(value), #ha(value),
|
||||
// #higher(value), #highera(value), #highest(value), and #highesta(value)
|
||||
// macros defined in section 4.5.1. Relocation Types of the PPC-elf64abi
|
||||
// document.
|
||||
static uint16_t applyPPCLo(uint64_t V) { return V; }
|
||||
static uint16_t applyPPCHi(uint64_t V) { return V >> 16; }
|
||||
static uint16_t applyPPCHa(uint64_t V) { return (V + 0x8000) >> 16; }
|
||||
static uint16_t applyPPCHigher(uint64_t V) { return V >> 32; }
|
||||
static uint16_t applyPPCHighera(uint64_t V) { return (V + 0x8000) >> 32; }
|
||||
static uint16_t applyPPCHighest(uint64_t V) { return V >> 48; }
|
||||
static uint16_t applyPPCHighesta(uint64_t V) { return (V + 0x8000) >> 48; }
|
||||
|
||||
PPC64::PPC64() {
|
||||
PltRel = GotRel = R_PPC64_GLOB_DAT;
|
||||
RelativeRel = R_PPC64_RELATIVE;
|
||||
GotEntrySize = 8;
|
||||
GotPltEntrySize = 8;
|
||||
PltEntrySize = 32;
|
||||
PltHeaderSize = 0;
|
||||
|
||||
// We need 64K pages (at least under glibc/Linux, the loader won't
|
||||
// set different permissions on a finer granularity than that).
|
||||
DefaultMaxPageSize = 65536;
|
||||
|
||||
// The PPC64 ELF ABI v1 spec, says:
|
||||
//
|
||||
// It is normally desirable to put segments with different characteristics
|
||||
// in separate 256 Mbyte portions of the address space, to give the
|
||||
// operating system full paging flexibility in the 64-bit address space.
|
||||
//
|
||||
// And because the lowest non-zero 256M boundary is 0x10000000, PPC64 linkers
|
||||
// use 0x10000000 as the starting address.
|
||||
DefaultImageBase = 0x10000000;
|
||||
}
|
||||
|
||||
RelExpr PPC64::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
default:
|
||||
return R_ABS;
|
||||
case R_PPC64_TOC16:
|
||||
case R_PPC64_TOC16_DS:
|
||||
case R_PPC64_TOC16_HA:
|
||||
case R_PPC64_TOC16_HI:
|
||||
case R_PPC64_TOC16_LO:
|
||||
case R_PPC64_TOC16_LO_DS:
|
||||
return R_GOTREL;
|
||||
case R_PPC64_TOC:
|
||||
return R_PPC_TOC;
|
||||
case R_PPC64_REL24:
|
||||
return R_PPC_PLT_OPD;
|
||||
}
|
||||
}
|
||||
|
||||
void PPC64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
uint64_t PltEntryAddr, int32_t Index,
|
||||
unsigned RelOff) const {
|
||||
uint64_t Off = GotPltEntryAddr - getPPC64TocBase();
|
||||
|
||||
// FIXME: What we should do, in theory, is get the offset of the function
|
||||
// descriptor in the .opd section, and use that as the offset from %r2 (the
|
||||
// TOC-base pointer). Instead, we have the GOT-entry offset, and that will
|
||||
// be a pointer to the function descriptor in the .opd section. Using
|
||||
// this scheme is simpler, but requires an extra indirection per PLT dispatch.
|
||||
|
||||
write32be(Buf, 0xf8410028); // std %r2, 40(%r1)
|
||||
write32be(Buf + 4, 0x3d620000 | applyPPCHa(Off)); // addis %r11, %r2, X@ha
|
||||
write32be(Buf + 8, 0xe98b0000 | applyPPCLo(Off)); // ld %r12, X@l(%r11)
|
||||
write32be(Buf + 12, 0xe96c0000); // ld %r11,0(%r12)
|
||||
write32be(Buf + 16, 0x7d6903a6); // mtctr %r11
|
||||
write32be(Buf + 20, 0xe84c0008); // ld %r2,8(%r12)
|
||||
write32be(Buf + 24, 0xe96c0010); // ld %r11,16(%r12)
|
||||
write32be(Buf + 28, 0x4e800420); // bctr
|
||||
}
|
||||
|
||||
static std::pair<uint32_t, uint64_t> toAddr16Rel(uint32_t Type, uint64_t Val) {
|
||||
uint64_t V = Val - PPC64TocOffset;
|
||||
switch (Type) {
|
||||
case R_PPC64_TOC16:
|
||||
return {R_PPC64_ADDR16, V};
|
||||
case R_PPC64_TOC16_DS:
|
||||
return {R_PPC64_ADDR16_DS, V};
|
||||
case R_PPC64_TOC16_HA:
|
||||
return {R_PPC64_ADDR16_HA, V};
|
||||
case R_PPC64_TOC16_HI:
|
||||
return {R_PPC64_ADDR16_HI, V};
|
||||
case R_PPC64_TOC16_LO:
|
||||
return {R_PPC64_ADDR16_LO, V};
|
||||
case R_PPC64_TOC16_LO_DS:
|
||||
return {R_PPC64_ADDR16_LO_DS, V};
|
||||
default:
|
||||
return {Type, Val};
|
||||
}
|
||||
}
|
||||
|
||||
void PPC64::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
// For a TOC-relative relocation, proceed in terms of the corresponding
|
||||
// ADDR16 relocation type.
|
||||
std::tie(Type, Val) = toAddr16Rel(Type, Val);
|
||||
|
||||
switch (Type) {
|
||||
case R_PPC64_ADDR14: {
|
||||
checkAlignment<4>(Loc, Val, Type);
|
||||
// Preserve the AA/LK bits in the branch instruction
|
||||
uint8_t AALK = Loc[3];
|
||||
write16be(Loc + 2, (AALK & 3) | (Val & 0xfffc));
|
||||
break;
|
||||
}
|
||||
case R_PPC64_ADDR16:
|
||||
checkInt<16>(Loc, Val, Type);
|
||||
write16be(Loc, Val);
|
||||
break;
|
||||
case R_PPC64_ADDR16_DS:
|
||||
checkInt<16>(Loc, Val, Type);
|
||||
write16be(Loc, (read16be(Loc) & 3) | (Val & ~3));
|
||||
break;
|
||||
case R_PPC64_ADDR16_HA:
|
||||
case R_PPC64_REL16_HA:
|
||||
write16be(Loc, applyPPCHa(Val));
|
||||
break;
|
||||
case R_PPC64_ADDR16_HI:
|
||||
case R_PPC64_REL16_HI:
|
||||
write16be(Loc, applyPPCHi(Val));
|
||||
break;
|
||||
case R_PPC64_ADDR16_HIGHER:
|
||||
write16be(Loc, applyPPCHigher(Val));
|
||||
break;
|
||||
case R_PPC64_ADDR16_HIGHERA:
|
||||
write16be(Loc, applyPPCHighera(Val));
|
||||
break;
|
||||
case R_PPC64_ADDR16_HIGHEST:
|
||||
write16be(Loc, applyPPCHighest(Val));
|
||||
break;
|
||||
case R_PPC64_ADDR16_HIGHESTA:
|
||||
write16be(Loc, applyPPCHighesta(Val));
|
||||
break;
|
||||
case R_PPC64_ADDR16_LO:
|
||||
write16be(Loc, applyPPCLo(Val));
|
||||
break;
|
||||
case R_PPC64_ADDR16_LO_DS:
|
||||
case R_PPC64_REL16_LO:
|
||||
write16be(Loc, (read16be(Loc) & 3) | (applyPPCLo(Val) & ~3));
|
||||
break;
|
||||
case R_PPC64_ADDR32:
|
||||
case R_PPC64_REL32:
|
||||
checkInt<32>(Loc, Val, Type);
|
||||
write32be(Loc, Val);
|
||||
break;
|
||||
case R_PPC64_ADDR64:
|
||||
case R_PPC64_REL64:
|
||||
case R_PPC64_TOC:
|
||||
write64be(Loc, Val);
|
||||
break;
|
||||
case R_PPC64_REL24: {
|
||||
uint32_t Mask = 0x03FFFFFC;
|
||||
checkInt<24>(Loc, Val, Type);
|
||||
write32be(Loc, (read32be(Loc) & ~Mask) | (Val & Mask));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
|
||||
}
|
||||
}
|
||||
|
||||
TargetInfo *elf::getPPC64TargetInfo() {
|
||||
static PPC64 Target;
|
||||
return &Target;
|
||||
}
|
||||
149
deps/lld/ELF/Arch/SPARCV9.cpp
vendored
Normal file
149
deps/lld/ELF/Arch/SPARCV9.cpp
vendored
Normal file
@ -0,0 +1,149 @@
|
||||
//===- SPARCV9.cpp --------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.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(uint32_t Type, const SymbolBody &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, uint32_t Type, uint64_t Val) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
SPARCV9::SPARCV9() {
|
||||
CopyRel = R_SPARC_COPY;
|
||||
GotRel = R_SPARC_GLOB_DAT;
|
||||
PltRel = R_SPARC_JMP_SLOT;
|
||||
RelativeRel = R_SPARC_RELATIVE;
|
||||
GotEntrySize = 8;
|
||||
PltEntrySize = 32;
|
||||
PltHeaderSize = 4 * PltEntrySize;
|
||||
|
||||
PageSize = 8192;
|
||||
DefaultMaxPageSize = 0x100000;
|
||||
DefaultImageBase = 0x100000;
|
||||
}
|
||||
|
||||
RelExpr SPARCV9::getRelExpr(uint32_t Type, const SymbolBody &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(toString(S.File) + ": unknown relocation type: " + toString(Type));
|
||||
return R_HINT;
|
||||
}
|
||||
}
|
||||
|
||||
void SPARCV9::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
switch (Type) {
|
||||
case R_SPARC_32:
|
||||
case R_SPARC_UA32:
|
||||
// V-word32
|
||||
checkUInt<32>(Loc, Val, Type);
|
||||
write32be(Loc, Val);
|
||||
break;
|
||||
case R_SPARC_DISP32:
|
||||
// V-disp32
|
||||
checkInt<32>(Loc, Val, Type);
|
||||
write32be(Loc, Val);
|
||||
break;
|
||||
case R_SPARC_WDISP30:
|
||||
case R_SPARC_WPLT30:
|
||||
// V-disp30
|
||||
checkInt<32>(Loc, Val, Type);
|
||||
write32be(Loc, (read32be(Loc) & ~0x3fffffff) | ((Val >> 2) & 0x3fffffff));
|
||||
break;
|
||||
case R_SPARC_22:
|
||||
// V-imm22
|
||||
checkUInt<22>(Loc, Val, 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<21>(Loc, Val, 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:
|
||||
case R_SPARC_GLOB_DAT:
|
||||
// V-xword64
|
||||
write64be(Loc, Val);
|
||||
break;
|
||||
default:
|
||||
error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type));
|
||||
}
|
||||
}
|
||||
|
||||
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 + Index * PltEntrySize;
|
||||
relocateOne(Buf, R_SPARC_22, Off);
|
||||
relocateOne(Buf + 4, R_SPARC_WDISP19, -(Off + 4 - PltEntrySize));
|
||||
}
|
||||
|
||||
TargetInfo *elf::getSPARCV9TargetInfo() {
|
||||
static SPARCV9 Target;
|
||||
return &Target;
|
||||
}
|
||||
364
deps/lld/ELF/Arch/X86.cpp
vendored
Normal file
364
deps/lld/ELF/Arch/X86.cpp
vendored
Normal file
@ -0,0 +1,364 @@
|
||||
//===- X86.cpp ------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.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 final : public TargetInfo {
|
||||
public:
|
||||
X86();
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const override;
|
||||
int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override;
|
||||
void writeGotPltHeader(uint8_t *Buf) const override;
|
||||
uint32_t getDynRel(uint32_t Type) const override;
|
||||
void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
|
||||
void writeIgotPlt(uint8_t *Buf, const SymbolBody &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, uint32_t Type, uint64_t Val) const override;
|
||||
|
||||
RelExpr adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
|
||||
RelExpr Expr) const override;
|
||||
void relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
void relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
void relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
void relaxTlsLdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
X86::X86() {
|
||||
GotBaseSymOff = -1;
|
||||
CopyRel = R_386_COPY;
|
||||
GotRel = R_386_GLOB_DAT;
|
||||
PltRel = R_386_JUMP_SLOT;
|
||||
IRelativeRel = R_386_IRELATIVE;
|
||||
RelativeRel = R_386_RELATIVE;
|
||||
TlsGotRel = R_386_TLS_TPOFF;
|
||||
TlsModuleIndexRel = R_386_TLS_DTPMOD32;
|
||||
TlsOffsetRel = R_386_TLS_DTPOFF32;
|
||||
GotEntrySize = 4;
|
||||
GotPltEntrySize = 4;
|
||||
PltEntrySize = 16;
|
||||
PltHeaderSize = 16;
|
||||
TlsGdRelaxSkip = 2;
|
||||
TrapInstr = 0xcccccccc; // 0xcc = INT3
|
||||
}
|
||||
|
||||
RelExpr X86::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
case R_386_8:
|
||||
case R_386_16:
|
||||
case R_386_32:
|
||||
case R_386_TLS_LDO_32:
|
||||
return R_ABS;
|
||||
case R_386_TLS_GD:
|
||||
return R_TLSGD;
|
||||
case R_386_TLS_LDM:
|
||||
return R_TLSLD;
|
||||
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_GOTONLY_PC_FROM_END;
|
||||
case R_386_TLS_IE:
|
||||
return R_GOT;
|
||||
case R_386_GOT32:
|
||||
case R_386_GOT32X:
|
||||
// These relocations can be calculated in two different ways.
|
||||
// Usual calculation is G + A - GOT what means an offset in GOT table
|
||||
// (R_GOT_FROM_END). When instruction pointed by relocation has no base
|
||||
// register, then relocations can be used when PIC code is disabled. In that
|
||||
// case calculation is G + A, it resolves to an address of entry in GOT
|
||||
// (R_GOT) and not an offset.
|
||||
//
|
||||
// To check that instruction has no base register we scan ModR/M byte.
|
||||
// See "Table 2-2. 32-Bit Addressing Forms with the ModR/M Byte"
|
||||
// (http://www.intel.com/content/dam/www/public/us/en/documents/manuals/
|
||||
// 64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf)
|
||||
if ((Loc[-1] & 0xc7) != 0x5)
|
||||
return R_GOT_FROM_END;
|
||||
if (Config->Pic)
|
||||
error(toString(S.File) + ": relocation " + toString(Type) + " against '" +
|
||||
S.getName() +
|
||||
"' without base register can not be used when PIC enabled");
|
||||
return R_GOT;
|
||||
case R_386_TLS_GOTIE:
|
||||
return R_GOT_FROM_END;
|
||||
case R_386_GOTOFF:
|
||||
return R_GOTREL_FROM_END;
|
||||
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(toString(S.File) + ": unknown relocation type: " + toString(Type));
|
||||
return R_HINT;
|
||||
}
|
||||
}
|
||||
|
||||
RelExpr X86::adjustRelaxExpr(uint32_t 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_END;
|
||||
case R_RELAX_TLS_GD_TO_LE:
|
||||
return R_RELAX_TLS_GD_TO_LE_NEG;
|
||||
}
|
||||
}
|
||||
|
||||
void X86::writeGotPltHeader(uint8_t *Buf) const {
|
||||
write32le(Buf, InX::Dynamic->getVA());
|
||||
}
|
||||
|
||||
void X86::writeGotPlt(uint8_t *Buf, const SymbolBody &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 SymbolBody &S) const {
|
||||
// An x86 entry is the address of the ifunc resolver function.
|
||||
write32le(Buf, S.getVA());
|
||||
}
|
||||
|
||||
uint32_t X86::getDynRel(uint32_t 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->Pic) {
|
||||
const uint8_t V[] = {
|
||||
0xff, 0xb3, 0x04, 0x00, 0x00, 0x00, // pushl GOTPLT+4(%ebx)
|
||||
0xff, 0xa3, 0x08, 0x00, 0x00, 0x00, // jmp *GOTPLT+8(%ebx)
|
||||
0x90, 0x90, 0x90, 0x90 // nop
|
||||
};
|
||||
memcpy(Buf, V, sizeof(V));
|
||||
|
||||
uint32_t Ebx = InX::Got->getVA() + InX::Got->getSize();
|
||||
uint32_t GotPlt = InX::GotPlt->getVA() - Ebx;
|
||||
write32le(Buf + 2, GotPlt + 4);
|
||||
write32le(Buf + 8, GotPlt + 8);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t PltData[] = {
|
||||
0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // pushl (GOTPLT+4)
|
||||
0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *(GOTPLT+8)
|
||||
0x90, 0x90, 0x90, 0x90 // nop
|
||||
};
|
||||
memcpy(Buf, PltData, sizeof(PltData));
|
||||
uint32_t GotPlt = InX::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 {
|
||||
const uint8_t Inst[] = {
|
||||
0xff, 0x00, 0x00, 0x00, 0x00, 0x00, // jmp *foo_in_GOT|*foo@GOT(%ebx)
|
||||
0x68, 0x00, 0x00, 0x00, 0x00, // pushl $reloc_offset
|
||||
0xe9, 0x00, 0x00, 0x00, 0x00 // jmp .PLT0@PC
|
||||
};
|
||||
memcpy(Buf, Inst, sizeof(Inst));
|
||||
|
||||
if (Config->Pic) {
|
||||
// jmp *foo@GOT(%ebx)
|
||||
uint32_t Ebx = InX::Got->getVA() + InX::Got->getSize();
|
||||
Buf[1] = 0xa3;
|
||||
write32le(Buf + 2, GotPltEntryAddr - Ebx);
|
||||
} else {
|
||||
// jmp *foo_in_GOT
|
||||
Buf[1] = 0x25;
|
||||
write32le(Buf + 2, GotPltEntryAddr);
|
||||
}
|
||||
|
||||
write32le(Buf + 7, RelOff);
|
||||
write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16);
|
||||
}
|
||||
|
||||
int64_t X86::getImplicitAddend(const uint8_t *Buf, uint32_t Type) const {
|
||||
switch (Type) {
|
||||
default:
|
||||
return 0;
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
void X86::relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const {
|
||||
// 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.
|
||||
switch (Type) {
|
||||
case R_386_8:
|
||||
checkUInt<8>(Loc, Val, Type);
|
||||
*Loc = Val;
|
||||
break;
|
||||
case R_386_PC8:
|
||||
checkInt<8>(Loc, Val, Type);
|
||||
*Loc = Val;
|
||||
break;
|
||||
case R_386_16:
|
||||
checkUInt<16>(Loc, Val, 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<17>(Loc, Val, Type);
|
||||
write16le(Loc, Val);
|
||||
break;
|
||||
default:
|
||||
checkInt<32>(Loc, Val, Type);
|
||||
write32le(Loc, Val);
|
||||
}
|
||||
}
|
||||
|
||||
void X86::relaxTlsGdToLe(uint8_t *Loc, uint32_t 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, 0x00, 0x00, 0x00, 0x00 // subl 0(%ebx), %eax
|
||||
};
|
||||
memcpy(Loc - 3, Inst, sizeof(Inst));
|
||||
write32le(Loc + 5, Val);
|
||||
}
|
||||
|
||||
void X86::relaxTlsGdToIe(uint8_t *Loc, uint32_t 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, 0x00, 0x00, 0x00, 0x00 // addl 0(%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, uint32_t 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, uint32_t 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));
|
||||
}
|
||||
|
||||
TargetInfo *elf::getX86TargetInfo() {
|
||||
static X86 Target;
|
||||
return &Target;
|
||||
}
|
||||
473
deps/lld/ELF/Arch/X86_64.cpp
vendored
Normal file
473
deps/lld/ELF/Arch/X86_64.cpp
vendored
Normal file
@ -0,0 +1,473 @@
|
||||
//===- X86_64.cpp ---------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.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 X86_64 final : public TargetInfo {
|
||||
public:
|
||||
X86_64();
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const override;
|
||||
bool isPicRel(uint32_t Type) const override;
|
||||
void writeGotPltHeader(uint8_t *Buf) const override;
|
||||
void writeGotPlt(uint8_t *Buf, const SymbolBody &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, uint32_t Type, uint64_t Val) const override;
|
||||
|
||||
RelExpr adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
|
||||
RelExpr Expr) const override;
|
||||
void relaxGot(uint8_t *Loc, uint64_t Val) const override;
|
||||
void relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
void relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
void relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
void relaxTlsLdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
|
||||
private:
|
||||
void relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op,
|
||||
uint8_t ModRm) const;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
template <class ELFT> X86_64<ELFT>::X86_64() {
|
||||
GotBaseSymOff = -1;
|
||||
CopyRel = R_X86_64_COPY;
|
||||
GotRel = R_X86_64_GLOB_DAT;
|
||||
PltRel = R_X86_64_JUMP_SLOT;
|
||||
RelativeRel = R_X86_64_RELATIVE;
|
||||
IRelativeRel = R_X86_64_IRELATIVE;
|
||||
TlsGotRel = R_X86_64_TPOFF64;
|
||||
TlsModuleIndexRel = R_X86_64_DTPMOD64;
|
||||
TlsOffsetRel = R_X86_64_DTPOFF64;
|
||||
GotEntrySize = 8;
|
||||
GotPltEntrySize = 8;
|
||||
PltEntrySize = 16;
|
||||
PltHeaderSize = 16;
|
||||
TlsGdRelaxSkip = 2;
|
||||
TrapInstr = 0xcccccccc; // 0xcc = INT3
|
||||
|
||||
// Align to the large page size (known as a superpage or huge page).
|
||||
// FreeBSD automatically promotes large, superpage-aligned allocations.
|
||||
DefaultImageBase = 0x200000;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
RelExpr X86_64<ELFT>::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const {
|
||||
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:
|
||||
case R_X86_64_DTPOFF32:
|
||||
case R_X86_64_DTPOFF64:
|
||||
return R_ABS;
|
||||
case R_X86_64_TPOFF32:
|
||||
return R_TLS;
|
||||
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_PC32:
|
||||
case R_X86_64_PC64:
|
||||
return R_PC;
|
||||
case R_X86_64_GOT32:
|
||||
case R_X86_64_GOT64:
|
||||
return R_GOT_FROM_END;
|
||||
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_NONE:
|
||||
return R_NONE;
|
||||
default:
|
||||
error(toString(S.File) + ": unknown relocation type: " + toString(Type));
|
||||
return R_HINT;
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT> void X86_64<ELFT>::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, InX::Dynamic->getVA());
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void X86_64<ELFT>::writeGotPlt(uint8_t *Buf, const SymbolBody &S) const {
|
||||
// See comments in X86TargetInfo::writeGotPlt.
|
||||
write32le(Buf, S.getPltVA() + 6);
|
||||
}
|
||||
|
||||
template <class ELFT> void X86_64<ELFT>::writePltHeader(uint8_t *Buf) const {
|
||||
const uint8_t PltData[] = {
|
||||
0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // pushq GOTPLT+8(%rip)
|
||||
0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *GOTPLT+16(%rip)
|
||||
0x0f, 0x1f, 0x40, 0x00 // nop
|
||||
};
|
||||
memcpy(Buf, PltData, sizeof(PltData));
|
||||
uint64_t GotPlt = InX::GotPlt->getVA();
|
||||
uint64_t Plt = InX::Plt->getVA();
|
||||
write32le(Buf + 2, GotPlt - Plt + 2); // GOTPLT+8
|
||||
write32le(Buf + 8, GotPlt - Plt + 4); // GOTPLT+16
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void X86_64<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
uint64_t PltEntryAddr, int32_t Index,
|
||||
unsigned RelOff) const {
|
||||
const uint8_t Inst[] = {
|
||||
0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmpq *got(%rip)
|
||||
0x68, 0x00, 0x00, 0x00, 0x00, // pushq <relocation index>
|
||||
0xe9, 0x00, 0x00, 0x00, 0x00 // jmpq plt[0]
|
||||
};
|
||||
memcpy(Buf, Inst, sizeof(Inst));
|
||||
|
||||
write32le(Buf + 2, GotPltEntryAddr - PltEntryAddr - 6);
|
||||
write32le(Buf + 7, Index);
|
||||
write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16);
|
||||
}
|
||||
|
||||
template <class ELFT> bool X86_64<ELFT>::isPicRel(uint32_t Type) const {
|
||||
return Type != R_X86_64_PC32 && Type != R_X86_64_32 &&
|
||||
Type != R_X86_64_TPOFF32;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void X86_64<ELFT>::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type,
|
||||
uint64_t Val) const {
|
||||
// Convert
|
||||
// .byte 0x66
|
||||
// leaq x@tlsgd(%rip), %rdi
|
||||
// .word 0x6666
|
||||
// rex64
|
||||
// call __tls_get_addr@plt
|
||||
// to
|
||||
// mov %fs:0x0,%rax
|
||||
// lea x@tpoff,%rax
|
||||
const uint8_t Inst[] = {
|
||||
0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0x0,%rax
|
||||
0x48, 0x8d, 0x80, 0x00, 0x00, 0x00, 0x00 // 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);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void X86_64<ELFT>::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type,
|
||||
uint64_t Val) const {
|
||||
// Convert
|
||||
// .byte 0x66
|
||||
// leaq x@tlsgd(%rip), %rdi
|
||||
// .word 0x6666
|
||||
// rex64
|
||||
// call __tls_get_addr@plt
|
||||
// to
|
||||
// mov %fs:0x0,%rax
|
||||
// addq x@tpoff,%rax
|
||||
const uint8_t Inst[] = {
|
||||
0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0x0,%rax
|
||||
0x48, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00 // addq x@tpoff,%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);
|
||||
}
|
||||
|
||||
// In some conditions, R_X86_64_GOTTPOFF relocation can be optimized to
|
||||
// R_X86_64_TPOFF32 so that it does not use GOT.
|
||||
template <class ELFT>
|
||||
void X86_64<ELFT>::relaxTlsIeToLe(uint8_t *Loc, uint32_t 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);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void X86_64<ELFT>::relaxTlsLdToLe(uint8_t *Loc, uint32_t Type,
|
||||
uint64_t Val) const {
|
||||
// Convert
|
||||
// leaq bar@tlsld(%rip), %rdi
|
||||
// callq __tls_get_addr@PLT
|
||||
// leaq bar@dtpoff(%rax), %rcx
|
||||
// to
|
||||
// .word 0x6666
|
||||
// .byte 0x66
|
||||
// mov %fs:0,%rax
|
||||
// leaq bar@tpoff(%rax), %rcx
|
||||
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
|
||||
};
|
||||
memcpy(Loc - 3, Inst, sizeof(Inst));
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void X86_64<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type,
|
||||
uint64_t Val) const {
|
||||
switch (Type) {
|
||||
case R_X86_64_8:
|
||||
checkUInt<8>(Loc, Val, Type);
|
||||
*Loc = Val;
|
||||
break;
|
||||
case R_X86_64_16:
|
||||
checkUInt<16>(Loc, Val, Type);
|
||||
write16le(Loc, Val);
|
||||
break;
|
||||
case R_X86_64_32:
|
||||
checkUInt<32>(Loc, Val, Type);
|
||||
write32le(Loc, Val);
|
||||
break;
|
||||
case R_X86_64_32S:
|
||||
case R_X86_64_TPOFF32:
|
||||
case R_X86_64_GOT32:
|
||||
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<32>(Loc, Val, Type);
|
||||
write32le(Loc, Val);
|
||||
break;
|
||||
case R_X86_64_64:
|
||||
case R_X86_64_DTPOFF64:
|
||||
case R_X86_64_GLOB_DAT:
|
||||
case R_X86_64_PC64:
|
||||
case R_X86_64_SIZE64:
|
||||
case R_X86_64_GOT64:
|
||||
write64le(Loc, Val);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("unexpected relocation");
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
RelExpr X86_64<ELFT>::adjustRelaxExpr(uint32_t 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->Pic ? 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)
|
||||
template <class ELFT>
|
||||
void X86_64<ELFT>::relaxGotNoPic(uint8_t *Loc, uint64_t Val, uint8_t Op,
|
||||
uint8_t ModRm) const {
|
||||
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);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void X86_64<ELFT>::relaxGot(uint8_t *Loc, 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->Pic);
|
||||
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);
|
||||
}
|
||||
|
||||
TargetInfo *elf::getX32TargetInfo() {
|
||||
static X86_64<ELF32LE> Target;
|
||||
return &Target;
|
||||
}
|
||||
|
||||
TargetInfo *elf::getX86_64TargetInfo() {
|
||||
static X86_64<ELF64LE> Target;
|
||||
return &Target;
|
||||
}
|
||||
75
deps/lld/ELF/CMakeLists.txt
vendored
Normal file
75
deps/lld/ELF/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
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
|
||||
Arch/AArch64.cpp
|
||||
Arch/AMDGPU.cpp
|
||||
Arch/ARM.cpp
|
||||
Arch/AVR.cpp
|
||||
Arch/Mips.cpp
|
||||
Arch/MipsArchTree.cpp
|
||||
Arch/PPC.cpp
|
||||
Arch/PPC64.cpp
|
||||
Arch/SPARCV9.cpp
|
||||
Arch/X86.cpp
|
||||
Arch/X86_64.cpp
|
||||
Driver.cpp
|
||||
DriverUtils.cpp
|
||||
EhFrame.cpp
|
||||
Error.cpp
|
||||
Filesystem.cpp
|
||||
GdbIndex.cpp
|
||||
ICF.cpp
|
||||
InputFiles.cpp
|
||||
InputSection.cpp
|
||||
LTO.cpp
|
||||
LinkerScript.cpp
|
||||
MapFile.cpp
|
||||
MarkLive.cpp
|
||||
OutputSections.cpp
|
||||
Relocations.cpp
|
||||
ScriptLexer.cpp
|
||||
ScriptParser.cpp
|
||||
Strings.cpp
|
||||
SymbolTable.cpp
|
||||
Symbols.cpp
|
||||
SyntheticSections.cpp
|
||||
Target.cpp
|
||||
Thunks.cpp
|
||||
Writer.cpp
|
||||
|
||||
LINK_COMPONENTS
|
||||
${LLVM_TARGETS_TO_BUILD}
|
||||
Analysis
|
||||
BinaryFormat
|
||||
BitReader
|
||||
BitWriter
|
||||
Codegen
|
||||
Core
|
||||
DebugInfoDWARF
|
||||
Demangle
|
||||
IPO
|
||||
Linker
|
||||
LTO
|
||||
Object
|
||||
Option
|
||||
Passes
|
||||
MC
|
||||
Support
|
||||
Target
|
||||
TransformUtils
|
||||
|
||||
LINK_LIBS
|
||||
lldConfig
|
||||
lldCore
|
||||
${LLVM_PTHREAD_LIB}
|
||||
|
||||
DEPENDS
|
||||
ELFOptionsTableGen
|
||||
${tablegen_deps}
|
||||
)
|
||||
238
deps/lld/ELF/Config.h
vendored
Normal file
238
deps/lld/ELF/Config.h
vendored
Normal file
@ -0,0 +1,238 @@
|
||||
//===- Config.h -------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_CONFIG_H
|
||||
#define LLD_ELF_CONFIG_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 <vector>
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
class InputFile;
|
||||
struct Symbol;
|
||||
|
||||
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 --strip-{all,debug}.
|
||||
enum class StripPolicy { None, All, Debug };
|
||||
|
||||
// For --unresolved-symbols.
|
||||
enum class UnresolvedPolicy { ReportError, Warn, WarnAll, Ignore, IgnoreAll };
|
||||
|
||||
// For --sort-section and linkerscript sorting rules.
|
||||
enum class SortSectionPolicy { Default, None, Alignment, Name, Priority };
|
||||
|
||||
// For --target2
|
||||
enum class Target2Policy { Abs, Rel, GotRel };
|
||||
|
||||
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;
|
||||
size_t NameOff = 0; // Offset in the string table
|
||||
};
|
||||
|
||||
// Structure for mapping renamed symbols
|
||||
struct RenamedSymbol {
|
||||
Symbol *Target;
|
||||
uint8_t OriginalBinding;
|
||||
};
|
||||
|
||||
// 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 {
|
||||
InputFile *FirstElf = nullptr;
|
||||
uint8_t OSABI = 0;
|
||||
llvm::CachePruningPolicy ThinLTOCachePolicy;
|
||||
llvm::StringMap<uint64_t> SectionStartMap;
|
||||
llvm::StringRef DynamicLinker;
|
||||
llvm::StringRef Entry;
|
||||
llvm::StringRef Emulation;
|
||||
llvm::StringRef Fini;
|
||||
llvm::StringRef Init;
|
||||
llvm::StringRef LTOAAPipeline;
|
||||
llvm::StringRef LTONewPmPasses;
|
||||
llvm::StringRef MapFile;
|
||||
llvm::StringRef OutputFile;
|
||||
llvm::StringRef OptRemarksFilename;
|
||||
llvm::StringRef SoName;
|
||||
llvm::StringRef Sysroot;
|
||||
llvm::StringRef ThinLTOCacheDir;
|
||||
std::string Rpath;
|
||||
std::vector<VersionDefinition> VersionDefinitions;
|
||||
std::vector<llvm::StringRef> Argv;
|
||||
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> VersionScriptGlobals;
|
||||
std::vector<SymbolVersion> VersionScriptLocals;
|
||||
std::vector<uint8_t> BuildIdVector;
|
||||
llvm::MapVector<Symbol *, RenamedSymbol> RenamedSymbols;
|
||||
bool AllowMultipleDefinition;
|
||||
bool AsNeeded = false;
|
||||
bool Bsymbolic;
|
||||
bool BsymbolicFunctions;
|
||||
bool ColorDiagnostics = false;
|
||||
bool CompressDebugSections;
|
||||
bool DefineCommon;
|
||||
bool Demangle = true;
|
||||
bool DisableVerify;
|
||||
bool EhFrameHdr;
|
||||
bool EmitRelocs;
|
||||
bool EnableNewDtags;
|
||||
bool ExportDynamic;
|
||||
bool FatalWarnings;
|
||||
bool GcSections;
|
||||
bool GdbIndex;
|
||||
bool GnuHash;
|
||||
bool ICF;
|
||||
bool MipsN32Abi = false;
|
||||
bool NoGnuUnique;
|
||||
bool NoUndefinedVersion;
|
||||
bool Nostdlib;
|
||||
bool OFormatBinary;
|
||||
bool Omagic;
|
||||
bool OptRemarksWithHotness;
|
||||
bool Pie;
|
||||
bool PrintGcSections;
|
||||
bool Relocatable;
|
||||
bool SaveTemps;
|
||||
bool SingleRoRx;
|
||||
bool Shared;
|
||||
bool Static = false;
|
||||
bool SysvHash;
|
||||
bool Target1Rel;
|
||||
bool Threads;
|
||||
bool Trace;
|
||||
bool Verbose;
|
||||
bool WarnCommon;
|
||||
bool WarnMissingEntry;
|
||||
bool ZCombreloc;
|
||||
bool ZExecstack;
|
||||
bool ZNocopyreloc;
|
||||
bool ZNodelete;
|
||||
bool ZNodlopen;
|
||||
bool ZNow;
|
||||
bool ZOrigin;
|
||||
bool ZRelro;
|
||||
bool ZRodynamic;
|
||||
bool ZText;
|
||||
bool ExitEarly;
|
||||
bool ZWxneeded;
|
||||
DiscardPolicy Discard;
|
||||
SortSectionPolicy SortSection;
|
||||
StripPolicy Strip;
|
||||
UnresolvedPolicy UnresolvedSymbols;
|
||||
Target2Policy Target2;
|
||||
BuildIdKind BuildId = BuildIdKind::None;
|
||||
ELFKind EKind = ELFNoneKind;
|
||||
uint16_t DefaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL;
|
||||
uint16_t EMachine = llvm::ELF::EM_NONE;
|
||||
uint64_t ErrorLimit = 20;
|
||||
uint64_t ImageBase;
|
||||
uint64_t MaxPageSize;
|
||||
uint64_t ZStackSize;
|
||||
unsigned LTOPartitions;
|
||||
unsigned LTOO;
|
||||
unsigned Optimize;
|
||||
unsigned ThinLTOJobs;
|
||||
|
||||
// The following config options do not directly correspond to any
|
||||
// particualr command line options.
|
||||
|
||||
// True if we need to pass through relocations in input files to the
|
||||
// output file. Usually false because we consume relocations.
|
||||
bool CopyRelocs;
|
||||
|
||||
// True if the target is ELF64. False if ELF32.
|
||||
bool Is64;
|
||||
|
||||
// True if the target is little-endian. False if big-endian.
|
||||
bool IsLE;
|
||||
|
||||
// endianness::little if IsLE is true. endianness::big otherwise.
|
||||
llvm::support::endianness Endianness;
|
||||
|
||||
// True if the target is the little-endian MIPS64.
|
||||
//
|
||||
// The reason why we have this variable only for the MIPS is because
|
||||
// we use this often. Some ELF headers for MIPS64EL are in a
|
||||
// mixed-endian (which is horrible and I'd say that's a serious spec
|
||||
// bug), and we need to know whether we are reading MIPS ELF files or
|
||||
// not in various places.
|
||||
//
|
||||
// (Note that MIPS64EL is not a typo for MIPS64LE. This is the official
|
||||
// name whatever that means. A fun hypothesis is that "EL" is short for
|
||||
// little-endian written in the little-endian order, but I don't know
|
||||
// if that's true.)
|
||||
bool IsMips64EL;
|
||||
|
||||
// The ELF spec defines two types of relocation table entries, RELA and
|
||||
// REL. RELA is a triplet of (offset, info, addend) while REL is a
|
||||
// tuple of (offset, info). Addends for REL are implicit and read from
|
||||
// the location where the relocations are applied. So, REL is more
|
||||
// compact than RELA but requires a bit of more work to process.
|
||||
//
|
||||
// (From the linker writer's view, this distinction is not necessary.
|
||||
// If the ELF had chosen whichever and sticked with it, it would have
|
||||
// been easier to write code to process relocations, but it's too late
|
||||
// to change the spec.)
|
||||
//
|
||||
// Each ABI defines its relocation type. IsRela is true if target
|
||||
// uses RELA. As far as we know, all 64-bit ABIs are using RELA. A
|
||||
// few 32-bit ABIs are using RELA too.
|
||||
bool IsRela;
|
||||
|
||||
// True if we are creating position-independent code.
|
||||
bool Pic;
|
||||
|
||||
// 4 for ELF32, 8 for ELF64.
|
||||
int Wordsize;
|
||||
};
|
||||
|
||||
// The only instance of Configuration struct.
|
||||
extern Configuration *Config;
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
1061
deps/lld/ELF/Driver.cpp
vendored
Normal file
1061
deps/lld/ELF/Driver.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
75
deps/lld/ELF/Driver.h
vendored
Normal file
75
deps/lld/ELF/Driver.h
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
//===- Driver.h -------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_DRIVER_H
|
||||
#define LLD_ELF_DRIVER_H
|
||||
|
||||
#include "SymbolTable.h"
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Core/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, bool CanExitEarly);
|
||||
void addFile(StringRef Path, bool WithLOption);
|
||||
void addLibrary(StringRef Name);
|
||||
|
||||
private:
|
||||
void readConfigs(llvm::opt::InputArgList &Args);
|
||||
void createFiles(llvm::opt::InputArgList &Args);
|
||||
void inferMachineType();
|
||||
template <class ELFT> void link(llvm::opt::InputArgList &Args);
|
||||
|
||||
// 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;
|
||||
|
||||
// True if we are in -format=binary and -format=elf.
|
||||
bool InBinary = false;
|
||||
|
||||
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(const char *Argv0);
|
||||
std::string createResponseFile(const llvm::opt::InputArgList &Args);
|
||||
|
||||
llvm::Optional<std::string> findFromSearchPaths(StringRef Path);
|
||||
llvm::Optional<std::string> searchLibrary(StringRef Path);
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
208
deps/lld/ELF/DriverUtils.cpp
vendored
Normal file
208
deps/lld/ELF/DriverUtils.cpp
vendored
Normal file
@ -0,0 +1,208 @@
|
||||
//===- DriverUtils.cpp ----------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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 "Error.h"
|
||||
#include "Memory.h"
|
||||
#include "lld/Config/Version.h"
|
||||
#include "lld/Core/Reproduce.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 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) {}
|
||||
|
||||
// Parse -color-diagnostics={auto,always,never} or -no-color-diagnostics.
|
||||
static bool getColorDiagnostics(opt::InputArgList &Args) {
|
||||
auto *Arg = Args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq,
|
||||
OPT_no_color_diagnostics);
|
||||
if (!Arg)
|
||||
return ErrorOS->has_colors();
|
||||
if (Arg->getOption().getID() == OPT_color_diagnostics)
|
||||
return true;
|
||||
if (Arg->getOption().getID() == OPT_no_color_diagnostics)
|
||||
return false;
|
||||
|
||||
StringRef S = Arg->getValue();
|
||||
if (S == "auto")
|
||||
return ErrorOS->has_colors();
|
||||
if (S == "always")
|
||||
return true;
|
||||
if (S != "never")
|
||||
error("unknown option: -color-diagnostics=" + S);
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
Args = this->ParseArgs(Vec, MissingIndex, MissingCount);
|
||||
|
||||
// Interpret -color-diagnostics early so that error messages
|
||||
// for unknown flags are colored.
|
||||
Config->ColorDiagnostics = getColorDiagnostics(Args);
|
||||
if (MissingCount)
|
||||
error(Twine(Args.getArgString(MissingIndex)) + ": missing argument");
|
||||
|
||||
for (auto *Arg : Args.filtered(OPT_UNKNOWN))
|
||||
error("unknown argument: " + Arg->getSpelling());
|
||||
return Args;
|
||||
}
|
||||
|
||||
void elf::printHelp(const char *Argv0) {
|
||||
ELFOptTable Table;
|
||||
Table.PrintHelp(outs(), Argv0, "lld", false);
|
||||
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".
|
||||
// Here, we print out all the targets that we support.
|
||||
outs() << Argv0 << ": supported targets: "
|
||||
<< "elf32-i386 elf32-iamcu elf32-littlearm elf32-ntradbigmips "
|
||||
<< "elf32-ntradlittlemips elf32-powerpc elf32-tradbigmips "
|
||||
<< "elf32-tradlittlemips elf32-x86-64 "
|
||||
<< "elf64-amdgpu elf64-littleaarch64 elf64-powerpc elf64-tradbigmips "
|
||||
<< "elf64-tradlittlemips elf64-x86-64\n";
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// 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_L:
|
||||
case OPT_dynamic_list:
|
||||
case OPT_rpath:
|
||||
case OPT_alias_script_T:
|
||||
case OPT_script:
|
||||
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 -lfoo. We'll look for libfoo.so or libfoo.a from
|
||||
// search paths.
|
||||
Optional<std::string> elf::searchLibrary(StringRef Name) {
|
||||
if (Name.startswith(":"))
|
||||
return findFromSearchPaths(Name.substr(1));
|
||||
|
||||
for (StringRef Dir : Config->SearchPaths) {
|
||||
if (!Config->Static)
|
||||
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;
|
||||
}
|
||||
212
deps/lld/ELF/EhFrame.cpp
vendored
Normal file
212
deps/lld/ELF/EhFrame.cpp
vendored
Normal file
@ -0,0 +1,212 @@
|
||||
//===- EhFrame.cpp -------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// .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 "Error.h"
|
||||
#include "InputSection.h"
|
||||
#include "Relocations.h"
|
||||
#include "Strings.h"
|
||||
|
||||
#include "llvm/BinaryFormat/Dwarf.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::ELF;
|
||||
using namespace llvm::dwarf;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace {
|
||||
template <class ELFT> class EhReader {
|
||||
public:
|
||||
EhReader(InputSectionBase *S, ArrayRef<uint8_t> D) : IS(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 " +
|
||||
IS->getObjMsg<ELFT>((const uint8_t *)Loc - IS->Data.data()));
|
||||
}
|
||||
|
||||
uint8_t readByte();
|
||||
void skipBytes(size_t Count);
|
||||
StringRef readString();
|
||||
void skipLeb128();
|
||||
void skipAugP();
|
||||
|
||||
InputSectionBase *IS;
|
||||
ArrayRef<uint8_t> D;
|
||||
};
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
size_t elf::readEhRecordSize(InputSectionBase *S, size_t Off) {
|
||||
return EhReader<ELFT>(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.
|
||||
template <class ELFT> size_t EhReader<ELFT>::readEhRecordSize() {
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
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<E>(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.
|
||||
template <class ELFT> uint8_t EhReader<ELFT>::readByte() {
|
||||
if (D.empty())
|
||||
failOn(D.data(), "unexpected end of CIE");
|
||||
uint8_t B = D.front();
|
||||
D = D.slice(1);
|
||||
return B;
|
||||
}
|
||||
|
||||
template <class ELFT> void EhReader<ELFT>::skipBytes(size_t Count) {
|
||||
if (D.size() < Count)
|
||||
failOn(D.data(), "CIE is too small");
|
||||
D = D.slice(Count);
|
||||
}
|
||||
|
||||
// Read a null-terminated string.
|
||||
template <class ELFT> StringRef EhReader<ELFT>::readString() {
|
||||
const uint8_t *End = std::find(D.begin(), D.end(), '\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.
|
||||
template <class ELFT> void EhReader<ELFT>::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;
|
||||
}
|
||||
|
||||
template <class ELFT> void EhReader<ELFT>::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);
|
||||
}
|
||||
|
||||
template <class ELFT> uint8_t elf::getFdeEncoding(EhSectionPiece *P) {
|
||||
auto *IS = static_cast<InputSectionBase *>(P->ID);
|
||||
return EhReader<ELFT>(IS, P->data()).getFdeEncoding();
|
||||
}
|
||||
|
||||
template <class ELFT> uint8_t EhReader<ELFT>::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;
|
||||
}
|
||||
|
||||
template size_t elf::readEhRecordSize<ELF32LE>(InputSectionBase *S, size_t Off);
|
||||
template size_t elf::readEhRecordSize<ELF32BE>(InputSectionBase *S, size_t Off);
|
||||
template size_t elf::readEhRecordSize<ELF64LE>(InputSectionBase *S, size_t Off);
|
||||
template size_t elf::readEhRecordSize<ELF64BE>(InputSectionBase *S, size_t Off);
|
||||
|
||||
template uint8_t elf::getFdeEncoding<ELF32LE>(EhSectionPiece *P);
|
||||
template uint8_t elf::getFdeEncoding<ELF32BE>(EhSectionPiece *P);
|
||||
template uint8_t elf::getFdeEncoding<ELF64LE>(EhSectionPiece *P);
|
||||
template uint8_t elf::getFdeEncoding<ELF64BE>(EhSectionPiece *P);
|
||||
25
deps/lld/ELF/EhFrame.h
vendored
Normal file
25
deps/lld/ELF/EhFrame.h
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
//===- EhFrame.h ------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_EHFRAME_H
|
||||
#define LLD_ELF_EHFRAME_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
class InputSectionBase;
|
||||
struct EhSectionPiece;
|
||||
|
||||
template <class ELFT> size_t readEhRecordSize(InputSectionBase *S, size_t Off);
|
||||
template <class ELFT> uint8_t getFdeEncoding(EhSectionPiece *P);
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
116
deps/lld/ELF/Error.cpp
vendored
Normal file
116
deps/lld/ELF/Error.cpp
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
//===- Error.cpp ----------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "Config.h"
|
||||
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/ManagedStatic.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <mutex>
|
||||
|
||||
#if !defined(_MSC_VER) && !defined(__MINGW32__)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
uint64_t elf::ErrorCount;
|
||||
raw_ostream *elf::ErrorOS;
|
||||
|
||||
// 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(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()).find('\n') != StringRef::npos);
|
||||
}
|
||||
|
||||
static void print(StringRef S, raw_ostream::Colors C) {
|
||||
*ErrorOS << Config->Argv[0] << ": ";
|
||||
if (Config->ColorDiagnostics) {
|
||||
ErrorOS->changeColor(C, true);
|
||||
*ErrorOS << S;
|
||||
ErrorOS->resetColor();
|
||||
} else {
|
||||
*ErrorOS << S;
|
||||
}
|
||||
}
|
||||
|
||||
void elf::log(const Twine &Msg) {
|
||||
if (Config->Verbose) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
outs() << Config->Argv[0] << ": " << Msg << "\n";
|
||||
outs().flush();
|
||||
}
|
||||
}
|
||||
|
||||
void elf::message(const Twine &Msg) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
outs() << Msg << "\n";
|
||||
outs().flush();
|
||||
}
|
||||
|
||||
void elf::warn(const Twine &Msg) {
|
||||
if (Config->FatalWarnings) {
|
||||
error(Msg);
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
newline(Msg);
|
||||
print("warning: ", raw_ostream::MAGENTA);
|
||||
*ErrorOS << Msg << "\n";
|
||||
}
|
||||
|
||||
void elf::error(const Twine &Msg) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
newline(Msg);
|
||||
|
||||
if (Config->ErrorLimit == 0 || ErrorCount < Config->ErrorLimit) {
|
||||
print("error: ", raw_ostream::RED);
|
||||
*ErrorOS << Msg << "\n";
|
||||
} else if (ErrorCount == Config->ErrorLimit) {
|
||||
print("error: ", raw_ostream::RED);
|
||||
*ErrorOS << "too many errors emitted, stopping now"
|
||||
<< " (use -error-limit=0 to see all errors)\n";
|
||||
if (Config->ExitEarly)
|
||||
exitLld(1);
|
||||
}
|
||||
|
||||
++ErrorCount;
|
||||
}
|
||||
|
||||
void elf::exitLld(int Val) {
|
||||
// Dealloc/destroy ManagedStatic variables before calling
|
||||
// _exit(). In a non-LTO build, this is a nop. In an LTO
|
||||
// build allows us to get the output of -time-passes.
|
||||
llvm_shutdown();
|
||||
|
||||
outs().flush();
|
||||
errs().flush();
|
||||
_exit(Val);
|
||||
}
|
||||
|
||||
void elf::fatal(const Twine &Msg) {
|
||||
error(Msg);
|
||||
exitLld(1);
|
||||
}
|
||||
78
deps/lld/ELF/Error.h
vendored
Normal file
78
deps/lld/ELF/Error.h
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
//===- Error.h --------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// In LLD, we have three levels of errors: fatal, error or warn.
|
||||
//
|
||||
// Fatal makes the program exit immediately with an error message.
|
||||
// You shouldn't use it except for reporting a corrupted input file.
|
||||
//
|
||||
// Error prints out an error message and increment a global variable
|
||||
// ErrorCount to record the fact that we met an error condition. It does
|
||||
// not exit, so it is safe for a lld-as-a-library use case. It is generally
|
||||
// useful because it can report more than one error in a single run.
|
||||
//
|
||||
// Warn doesn't do anything but printing out a given message.
|
||||
//
|
||||
// It is not recommended to use llvm::outs() or llvm::errs() directly
|
||||
// in LLD because they are not thread-safe. The functions declared in
|
||||
// this file are mutually excluded, so you want to use them instead.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_ERROR_H
|
||||
#define LLD_ELF_ERROR_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
|
||||
#include "llvm/Support/Error.h"
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
extern uint64_t ErrorCount;
|
||||
extern llvm::raw_ostream *ErrorOS;
|
||||
|
||||
void log(const Twine &Msg);
|
||||
void message(const Twine &Msg);
|
||||
void warn(const Twine &Msg);
|
||||
void error(const Twine &Msg);
|
||||
LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg);
|
||||
|
||||
LLVM_ATTRIBUTE_NORETURN void exitLld(int Val);
|
||||
|
||||
// check() functions are convenient functions to strip errors
|
||||
// from error-or-value objects.
|
||||
template <class T> T check(ErrorOr<T> E) {
|
||||
if (auto EC = E.getError())
|
||||
fatal(EC.message());
|
||||
return std::move(*E);
|
||||
}
|
||||
|
||||
template <class T> T check(Expected<T> E) {
|
||||
if (!E)
|
||||
fatal(llvm::toString(E.takeError()));
|
||||
return std::move(*E);
|
||||
}
|
||||
|
||||
template <class T> T check(ErrorOr<T> E, const Twine &Prefix) {
|
||||
if (auto EC = E.getError())
|
||||
fatal(Prefix + ": " + EC.message());
|
||||
return std::move(*E);
|
||||
}
|
||||
|
||||
template <class T> T check(Expected<T> E, const Twine &Prefix) {
|
||||
if (!E)
|
||||
fatal(Prefix + ": " + toString(E.takeError()));
|
||||
return std::move(*E);
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
77
deps/lld/ELF/Filesystem.cpp
vendored
Normal file
77
deps/lld/ELF/Filesystem.cpp
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
//===- Filesystem.cpp -----------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains a few utility functions to handle files.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Filesystem.h"
|
||||
#include "Config.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/FileOutputBuffer.h"
|
||||
#include <thread>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
// 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 call unlink.
|
||||
// The calling thread returns almost immediately.
|
||||
void elf::unlinkAsync(StringRef Path) {
|
||||
if (!Config->Threads || !sys::fs::exists(Config->OutputFile) ||
|
||||
!sys::fs::is_regular_file(Config->OutputFile))
|
||||
return;
|
||||
|
||||
// First, rename Path to avoid race condition. We cannot remove
|
||||
// Path from a different thread because we are now going to create
|
||||
// Path as a new file. If we do that in a different thread, the new
|
||||
// thread can remove the new file.
|
||||
SmallString<128> TempPath;
|
||||
if (sys::fs::createUniqueFile(Path + "tmp%%%%%%%%", TempPath))
|
||||
return;
|
||||
if (sys::fs::rename(Path, TempPath)) {
|
||||
sys::fs::remove(TempPath);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove TempPath in background.
|
||||
std::thread([=] { ::remove(TempPath.str().str().c_str()); }).detach();
|
||||
}
|
||||
|
||||
// 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 elf::tryCreateFile(StringRef Path) {
|
||||
if (Path.empty())
|
||||
return std::error_code();
|
||||
return FileOutputBuffer::create(Path, 1).getError();
|
||||
}
|
||||
22
deps/lld/ELF/Filesystem.h
vendored
Normal file
22
deps/lld/ELF/Filesystem.h
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
//===- Filesystem.h ---------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_FILESYSTEM_H
|
||||
#define LLD_ELF_FILESYSTEM_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
void unlinkAsync(StringRef Path);
|
||||
std::error_code tryCreateFile(StringRef Path);
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
49
deps/lld/ELF/GdbIndex.cpp
vendored
Normal file
49
deps/lld/ELF/GdbIndex.cpp
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
//===- GdbIndex.cpp -------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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 "GdbIndex.h"
|
||||
#include "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;
|
||||
|
||||
std::pair<bool, GdbSymbol *> GdbHashTab::add(uint32_t Hash, size_t Offset) {
|
||||
GdbSymbol *&Sym = Map[Offset];
|
||||
if (Sym)
|
||||
return {false, Sym};
|
||||
Sym = make<GdbSymbol>(Hash, Offset);
|
||||
return {true, Sym};
|
||||
}
|
||||
|
||||
void GdbHashTab::finalizeContents() {
|
||||
uint32_t Size = std::max<uint32_t>(1024, NextPowerOf2(Map.size() * 4 / 3));
|
||||
uint32_t Mask = Size - 1;
|
||||
Table.resize(Size);
|
||||
|
||||
for (auto &P : Map) {
|
||||
GdbSymbol *Sym = P.second;
|
||||
uint32_t I = Sym->NameHash & Mask;
|
||||
uint32_t Step = ((Sym->NameHash * 17) & Mask) | 1;
|
||||
|
||||
while (Table[I])
|
||||
I = (I + Step) & Mask;
|
||||
Table[I] = Sym;
|
||||
}
|
||||
}
|
||||
82
deps/lld/ELF/GdbIndex.h
vendored
Normal file
82
deps/lld/ELF/GdbIndex.h
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
//===- GdbIndex.h --------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===-------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_GDB_INDEX_H
|
||||
#define LLD_ELF_GDB_INDEX_H
|
||||
|
||||
#include "InputFiles.h"
|
||||
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
class InputSection;
|
||||
|
||||
// Struct represents single entry of address area of gdb index.
|
||||
struct AddressEntry {
|
||||
InputSection *Section;
|
||||
uint64_t LowAddress;
|
||||
uint64_t HighAddress;
|
||||
uint32_t CuIndex;
|
||||
};
|
||||
|
||||
// Struct represents single entry of compilation units list area of gdb index.
|
||||
// It consist of CU offset in .debug_info section and it's size.
|
||||
struct CompilationUnitEntry {
|
||||
uint64_t CuOffset;
|
||||
uint64_t CuLength;
|
||||
};
|
||||
|
||||
// Represents data about symbol and type names which are used
|
||||
// to build symbol table and constant pool area of gdb index.
|
||||
struct NameTypeEntry {
|
||||
StringRef Name;
|
||||
uint8_t Type;
|
||||
};
|
||||
|
||||
// We fill one GdbIndexDataChunk for each object where scan of
|
||||
// debug information performed. That information futher used
|
||||
// for filling gdb index section areas.
|
||||
struct GdbIndexChunk {
|
||||
InputSection *DebugInfoSec;
|
||||
std::vector<AddressEntry> AddressArea;
|
||||
std::vector<CompilationUnitEntry> CompilationUnits;
|
||||
std::vector<NameTypeEntry> NamesAndTypes;
|
||||
};
|
||||
|
||||
// Element of GdbHashTab hash table.
|
||||
struct GdbSymbol {
|
||||
GdbSymbol(uint32_t Hash, size_t Offset)
|
||||
: NameHash(Hash), NameOffset(Offset) {}
|
||||
uint32_t NameHash;
|
||||
size_t NameOffset;
|
||||
size_t CuVectorIndex;
|
||||
};
|
||||
|
||||
// This class manages the hashed symbol table for the .gdb_index section.
|
||||
// The hash value for a table entry is computed by applying an iterative hash
|
||||
// function to the symbol's name.
|
||||
class GdbHashTab final {
|
||||
public:
|
||||
std::pair<bool, GdbSymbol *> add(uint32_t Hash, size_t Offset);
|
||||
|
||||
void finalizeContents();
|
||||
size_t getCapacity() { return Table.size(); }
|
||||
GdbSymbol *getSymbol(size_t I) { return Table[I]; }
|
||||
|
||||
private:
|
||||
llvm::DenseMap<size_t, GdbSymbol *> Map;
|
||||
std::vector<GdbSymbol *> Table;
|
||||
};
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
435
deps/lld/ELF/ICF.cpp
vendored
Normal file
435
deps/lld/ELF/ICF.cpp
vendored
Normal file
@ -0,0 +1,435 @@
|
||||
//===- ICF.cpp ------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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 "Threads.h"
|
||||
#include "llvm/ADT/Hashing.h"
|
||||
#include "llvm/BinaryFormat/ELF.h"
|
||||
#include "llvm/Object/ELF.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,
|
||||
std::function<void(size_t, size_t)> Fn);
|
||||
|
||||
void forEachClass(std::function<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 a hash value for S. Note that the information about
|
||||
// relocation targets is not included in the hash value.
|
||||
template <class ELFT> static uint32_t getHash(InputSection *S) {
|
||||
return hash_combine(S->Flags, S->getSize(), S->NumRelocations);
|
||||
}
|
||||
|
||||
// Returns true if section S is subject of ICF.
|
||||
static bool isEligible(InputSection *S) {
|
||||
// .init and .fini contains instructions that must be executed to
|
||||
// initialize and finalize the process. They cannot and should not
|
||||
// be merged.
|
||||
return S->Live && (S->Flags & SHF_ALLOC) && (S->Flags & SHF_EXECINSTR) &&
|
||||
!(S->Flags & SHF_WRITE) && S->Name != ".init" && S->Name != ".fini";
|
||||
}
|
||||
|
||||
// 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]->Class[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 *A, ArrayRef<RelTy> RelsA,
|
||||
const InputSection *B, ArrayRef<RelTy> RelsB) {
|
||||
auto Eq = [&](const RelTy &RA, const RelTy &RB) {
|
||||
if (RA.r_offset != RB.r_offset ||
|
||||
RA.getType(Config->IsMips64EL) != RB.getType(Config->IsMips64EL))
|
||||
return false;
|
||||
uint64_t AddA = getAddend<ELFT>(RA);
|
||||
uint64_t AddB = getAddend<ELFT>(RB);
|
||||
|
||||
SymbolBody &SA = A->template getFile<ELFT>()->getRelocTargetSym(RA);
|
||||
SymbolBody &SB = B->template getFile<ELFT>()->getRelocTargetSym(RB);
|
||||
if (&SA == &SB)
|
||||
return AddA == AddB;
|
||||
|
||||
auto *DA = dyn_cast<DefinedRegular>(&SA);
|
||||
auto *DB = dyn_cast<DefinedRegular>(&SB);
|
||||
if (!DA || !DB)
|
||||
return false;
|
||||
|
||||
// Relocations referring to absolute symbols are constant-equal if their
|
||||
// values are equal.
|
||||
if (!DA->Section || !DB->Section)
|
||||
return !DA->Section && !DB->Section &&
|
||||
DA->Value + AddA == DB->Value + AddB;
|
||||
|
||||
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))
|
||||
return DA->Value + AddA == DB->Value + AddB;
|
||||
|
||||
// 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;
|
||||
return OffsetA == OffsetB;
|
||||
};
|
||||
|
||||
return RelsA.size() == RelsB.size() &&
|
||||
std::equal(RelsA.begin(), RelsA.end(), RelsB.begin(), Eq);
|
||||
}
|
||||
|
||||
// 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 (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 *A, ArrayRef<RelTy> RelsA,
|
||||
const InputSection *B, ArrayRef<RelTy> RelsB) {
|
||||
auto Eq = [&](const RelTy &RA, const RelTy &RB) {
|
||||
// The two sections must be identical.
|
||||
SymbolBody &SA = A->template getFile<ELFT>()->getRelocTargetSym(RA);
|
||||
SymbolBody &SB = B->template getFile<ELFT>()->getRelocTargetSym(RB);
|
||||
if (&SA == &SB)
|
||||
return true;
|
||||
|
||||
auto *DA = cast<DefinedRegular>(&SA);
|
||||
auto *DB = cast<DefinedRegular>(&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)
|
||||
return true;
|
||||
auto *X = dyn_cast<InputSection>(DA->Section);
|
||||
if (!X)
|
||||
return true;
|
||||
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->Class[Current] == 0)
|
||||
return false;
|
||||
|
||||
return X->Class[Current] == Y->Class[Current];
|
||||
};
|
||||
|
||||
return std::equal(RelsA.begin(), RelsA.end(), RelsB.begin(), Eq);
|
||||
}
|
||||
|
||||
// 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 Class = Sections[Begin]->Class[Current];
|
||||
for (size_t I = Begin + 1; I < End; ++I)
|
||||
if (Class != Sections[I]->Class[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 that starts within [Begin, End).
|
||||
// Note that a group must start in that range but doesn't necessarily
|
||||
// have to end before End.
|
||||
template <class ELFT>
|
||||
void ICF<ELFT>::forEachClassRange(size_t Begin, size_t End,
|
||||
std::function<void(size_t, size_t)> Fn) {
|
||||
if (Begin > 0)
|
||||
Begin = findBoundary(Begin - 1, End);
|
||||
|
||||
while (Begin < End) {
|
||||
size_t Mid = findBoundary(Begin, Sections.size());
|
||||
Fn(Begin, Mid);
|
||||
Begin = Mid;
|
||||
}
|
||||
}
|
||||
|
||||
// Call Fn on each equivalence class.
|
||||
template <class ELFT>
|
||||
void ICF<ELFT>::forEachClass(std::function<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 (!Config->Threads || Sections.size() < 1024) {
|
||||
forEachClassRange(0, Sections.size(), Fn);
|
||||
++Cnt;
|
||||
return;
|
||||
}
|
||||
|
||||
Current = Cnt % 2;
|
||||
Next = (Cnt + 1) % 2;
|
||||
|
||||
// Split sections into 256 shards and call Fn in parallel.
|
||||
size_t NumShards = 256;
|
||||
size_t Step = Sections.size() / NumShards;
|
||||
parallelForEachN(0, NumShards, [&](size_t I) {
|
||||
size_t End = (I == NumShards - 1) ? Sections.size() : (I + 1) * Step;
|
||||
forEachClassRange(I * Step, End, Fn);
|
||||
});
|
||||
++Cnt;
|
||||
}
|
||||
|
||||
// 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.
|
||||
for (InputSection *S : Sections)
|
||||
// Set MSB to 1 to avoid collisions with non-hash IDs.
|
||||
S->Class[0] = getHash<ELFT>(S) | (1 << 31);
|
||||
|
||||
// From now on, sections in Sections vector are ordered so that sections
|
||||
// in the same equivalence class are consecutive in the vector.
|
||||
std::stable_sort(Sections.begin(), Sections.end(),
|
||||
[](InputSection *A, InputSection *B) {
|
||||
return A->Class[0] < B->Class[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.
|
||||
forEachClass([&](size_t Begin, size_t End) {
|
||||
if (End - Begin == 1)
|
||||
return;
|
||||
|
||||
log("selected " + Sections[Begin]->Name);
|
||||
for (size_t I = Begin + 1; I < End; ++I) {
|
||||
log(" removed " + Sections[I]->Name);
|
||||
Sections[Begin]->replace(Sections[I]);
|
||||
}
|
||||
});
|
||||
|
||||
// Mark ARM Exception Index table sections that refer to folded code
|
||||
// sections as not live. These sections have an implict dependency
|
||||
// via the link order dependency.
|
||||
if (Config->EMachine == EM_ARM)
|
||||
for (InputSectionBase *Sec : InputSections)
|
||||
if (auto *S = dyn_cast<InputSection>(Sec))
|
||||
if (S->Flags & SHF_LINK_ORDER)
|
||||
S->Live = S->getLinkOrderDep()->Live;
|
||||
}
|
||||
|
||||
// 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>();
|
||||
19
deps/lld/ELF/ICF.h
vendored
Normal file
19
deps/lld/ELF/ICF.h
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
//===- ICF.h --------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_ICF_H
|
||||
#define LLD_ELF_ICF_H
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
template <class ELFT> void doIcf();
|
||||
}
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
1109
deps/lld/ELF/InputFiles.cpp
vendored
Normal file
1109
deps/lld/ELF/InputFiles.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
346
deps/lld/ELF/InputFiles.h
vendored
Normal file
346
deps/lld/ELF/InputFiles.h
vendored
Normal file
@ -0,0 +1,346 @@
|
||||
//===- InputFiles.h ---------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_INPUT_FILES_H
|
||||
#define LLD_ELF_INPUT_FILES_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "Error.h"
|
||||
#include "InputSection.h"
|
||||
#include "Symbols.h"
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "lld/Core/Reproduce.h"
|
||||
#include "llvm/ADT/CachedHashString.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/STLExtras.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 DWARFDebugLine;
|
||||
class TarWriter;
|
||||
struct DILineInfo;
|
||||
namespace lto {
|
||||
class InputFile;
|
||||
}
|
||||
} // namespace llvm
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
class InputFile;
|
||||
}
|
||||
|
||||
// Returns "(internal)", "foo.a(bar.o)" or "baz.o".
|
||||
std::string toString(const elf::InputFile *F);
|
||||
|
||||
namespace elf {
|
||||
|
||||
using llvm::object::Archive;
|
||||
|
||||
class Lazy;
|
||||
class SymbolBody;
|
||||
|
||||
// If -reproduce option is given, all input files are written
|
||||
// to this tar archive.
|
||||
extern llvm::TarWriter *Tar;
|
||||
|
||||
// Opens a given file.
|
||||
llvm::Optional<MemoryBufferRef> readFile(StringRef Path);
|
||||
|
||||
// The root class of input files.
|
||||
class InputFile {
|
||||
public:
|
||||
enum Kind {
|
||||
ObjectKind,
|
||||
SharedKind,
|
||||
LazyObjectKind,
|
||||
ArchiveKind,
|
||||
BitcodeKind,
|
||||
BinaryKind,
|
||||
};
|
||||
|
||||
Kind kind() const { return FileKind; }
|
||||
|
||||
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 == ObjectKind || FileKind == BinaryKind);
|
||||
return Sections;
|
||||
}
|
||||
|
||||
// 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.
|
||||
StringRef 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;
|
||||
|
||||
// Cache for toString(). Only toString() should use this member.
|
||||
mutable std::string ToStringCache;
|
||||
|
||||
protected:
|
||||
InputFile(Kind K, MemoryBufferRef M);
|
||||
std::vector<InputSectionBase *> Sections;
|
||||
|
||||
private:
|
||||
const Kind FileKind;
|
||||
};
|
||||
|
||||
template <typename ELFT> class ELFFileBase : public InputFile {
|
||||
public:
|
||||
typedef typename ELFT::Shdr Elf_Shdr;
|
||||
typedef typename ELFT::Sym Elf_Sym;
|
||||
typedef typename ELFT::Word Elf_Word;
|
||||
typedef typename ELFT::SymRange Elf_Sym_Range;
|
||||
|
||||
ELFFileBase(Kind K, MemoryBufferRef M);
|
||||
static bool classof(const InputFile *F) {
|
||||
Kind K = F->kind();
|
||||
return K == ObjectKind || K == SharedKind;
|
||||
}
|
||||
|
||||
llvm::object::ELFFile<ELFT> getObj() const {
|
||||
return llvm::object::ELFFile<ELFT>(MB.getBuffer());
|
||||
}
|
||||
|
||||
StringRef getStringTable() const { return StringTable; }
|
||||
|
||||
uint32_t getSectionIndex(const Elf_Sym &Sym) const;
|
||||
|
||||
Elf_Sym_Range getGlobalSymbols();
|
||||
|
||||
protected:
|
||||
ArrayRef<Elf_Sym> Symbols;
|
||||
uint32_t FirstNonLocal = 0;
|
||||
ArrayRef<Elf_Word> SymtabSHNDX;
|
||||
StringRef StringTable;
|
||||
void initSymtab(ArrayRef<Elf_Shdr> Sections, const Elf_Shdr *Symtab);
|
||||
};
|
||||
|
||||
// .o file.
|
||||
template <class ELFT> class ObjectFile : public ELFFileBase<ELFT> {
|
||||
typedef ELFFileBase<ELFT> Base;
|
||||
typedef typename ELFT::Rel Elf_Rel;
|
||||
typedef typename ELFT::Rela Elf_Rela;
|
||||
typedef typename ELFT::Sym Elf_Sym;
|
||||
typedef typename ELFT::Shdr Elf_Shdr;
|
||||
typedef typename ELFT::Word Elf_Word;
|
||||
|
||||
StringRef getShtGroupSignature(ArrayRef<Elf_Shdr> Sections,
|
||||
const Elf_Shdr &Sec);
|
||||
ArrayRef<Elf_Word> getShtGroupEntries(const Elf_Shdr &Sec);
|
||||
|
||||
public:
|
||||
static bool classof(const InputFile *F) {
|
||||
return F->kind() == Base::ObjectKind;
|
||||
}
|
||||
|
||||
ArrayRef<SymbolBody *> getSymbols();
|
||||
ArrayRef<SymbolBody *> getLocalSymbols();
|
||||
|
||||
ObjectFile(MemoryBufferRef M, StringRef ArchiveName);
|
||||
void parse(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
|
||||
|
||||
InputSectionBase *getSection(const Elf_Sym &Sym) const;
|
||||
|
||||
SymbolBody &getSymbolBody(uint32_t SymbolIndex) const {
|
||||
if (SymbolIndex >= SymbolBodies.size())
|
||||
fatal(toString(this) + ": invalid symbol index");
|
||||
return *SymbolBodies[SymbolIndex];
|
||||
}
|
||||
|
||||
template <typename RelT>
|
||||
SymbolBody &getRelocTargetSym(const RelT &Rel) const {
|
||||
uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL);
|
||||
return getSymbolBody(SymIndex);
|
||||
}
|
||||
|
||||
// Returns source line information for a given offset.
|
||||
// If no information is available, returns "".
|
||||
std::string getLineInfo(InputSectionBase *S, uint64_t Offset);
|
||||
llvm::Optional<llvm::DILineInfo> getDILineInfo(InputSectionBase *, uint64_t);
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
private:
|
||||
void
|
||||
initializeSections(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
|
||||
void initializeSymbols();
|
||||
void initializeDwarfLine();
|
||||
InputSectionBase *getRelocTarget(const Elf_Shdr &Sec);
|
||||
InputSectionBase *createInputSection(const Elf_Shdr &Sec);
|
||||
StringRef getSectionName(const Elf_Shdr &Sec);
|
||||
|
||||
bool shouldMerge(const Elf_Shdr &Sec);
|
||||
SymbolBody *createSymbolBody(const Elf_Sym *Sym);
|
||||
|
||||
// List of all symbols referenced or defined by this file.
|
||||
std::vector<SymbolBody *> SymbolBodies;
|
||||
|
||||
// .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::DWARFDebugLine> DwarfLine;
|
||||
llvm::once_flag InitDwarfLine;
|
||||
};
|
||||
|
||||
// LazyObjectFile is analogous to ArchiveFile in the sense that
|
||||
// the file contains lazy symbols. The difference is that
|
||||
// LazyObjectFile 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 LazyObjectFile : public InputFile {
|
||||
public:
|
||||
LazyObjectFile(MemoryBufferRef M, StringRef ArchiveName,
|
||||
uint64_t OffsetInArchive)
|
||||
: InputFile(LazyObjectKind, M), OffsetInArchive(OffsetInArchive) {
|
||||
this->ArchiveName = ArchiveName;
|
||||
}
|
||||
|
||||
static bool classof(const InputFile *F) {
|
||||
return F->kind() == LazyObjectKind;
|
||||
}
|
||||
|
||||
template <class ELFT> void parse();
|
||||
MemoryBufferRef getBuffer();
|
||||
InputFile *fetch();
|
||||
|
||||
private:
|
||||
std::vector<StringRef> getSymbols();
|
||||
template <class ELFT> std::vector<StringRef> getElfSymbols();
|
||||
std::vector<StringRef> getBitcodeSymbols();
|
||||
|
||||
bool Seen = false;
|
||||
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; }
|
||||
template <class ELFT> void parse();
|
||||
ArrayRef<Symbol *> getSymbols() { return Symbols; }
|
||||
|
||||
// Returns a memory buffer for a given symbol and the offset in the archive
|
||||
// for the member. An empty memory buffer and an offset of zero
|
||||
// is returned if we have already returned the same memory buffer.
|
||||
// (So that we don't instantiate same members more than once.)
|
||||
std::pair<MemoryBufferRef, uint64_t> getMember(const Archive::Symbol *Sym);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Archive> File;
|
||||
llvm::DenseSet<uint64_t> Seen;
|
||||
std::vector<Symbol *> Symbols;
|
||||
};
|
||||
|
||||
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(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
|
||||
ArrayRef<Symbol *> getSymbols() { return Symbols; }
|
||||
std::unique_ptr<llvm::lto::InputFile> Obj;
|
||||
|
||||
private:
|
||||
std::vector<Symbol *> Symbols;
|
||||
};
|
||||
|
||||
// .so file.
|
||||
template <class ELFT> class SharedFile : public ELFFileBase<ELFT> {
|
||||
typedef ELFFileBase<ELFT> Base;
|
||||
typedef typename ELFT::Dyn Elf_Dyn;
|
||||
typedef typename ELFT::Shdr Elf_Shdr;
|
||||
typedef typename ELFT::Sym Elf_Sym;
|
||||
typedef typename ELFT::SymRange Elf_Sym_Range;
|
||||
typedef typename ELFT::Verdef Elf_Verdef;
|
||||
typedef typename ELFT::Versym Elf_Versym;
|
||||
|
||||
std::vector<StringRef> Undefs;
|
||||
const Elf_Shdr *VersymSec = nullptr;
|
||||
const Elf_Shdr *VerdefSec = nullptr;
|
||||
|
||||
public:
|
||||
std::string SoName;
|
||||
|
||||
const Elf_Shdr *getSection(const Elf_Sym &Sym) const;
|
||||
llvm::ArrayRef<StringRef> getUndefinedSymbols() { return Undefs; }
|
||||
|
||||
static bool classof(const InputFile *F) {
|
||||
return F->kind() == Base::SharedKind;
|
||||
}
|
||||
|
||||
SharedFile(MemoryBufferRef M, StringRef DefaultSoName);
|
||||
|
||||
void parseSoName();
|
||||
void parseRest();
|
||||
std::vector<const Elf_Verdef *> parseVerdefs(const Elf_Versym *&Versym);
|
||||
|
||||
struct NeededVer {
|
||||
// The string table offset of the version name in the output file.
|
||||
size_t StrTab;
|
||||
|
||||
// The version identifier for this version name.
|
||||
uint16_t Index;
|
||||
};
|
||||
|
||||
// Mapping from Elf_Verdef data structures to information about Elf_Vernaux
|
||||
// data structures in the output file.
|
||||
std::map<const Elf_Verdef *, NeededVer> VerdefMap;
|
||||
|
||||
// Used for --as-needed
|
||||
bool AsNeeded = false;
|
||||
bool IsUsed = false;
|
||||
bool isNeeded() const { return !AsNeeded || IsUsed; }
|
||||
};
|
||||
|
||||
class BinaryFile : public InputFile {
|
||||
public:
|
||||
explicit BinaryFile(MemoryBufferRef M) : InputFile(BinaryKind, M) {}
|
||||
static bool classof(const InputFile *F) { return F->kind() == BinaryKind; }
|
||||
template <class ELFT> void parse();
|
||||
};
|
||||
|
||||
InputFile *createObjectFile(MemoryBufferRef MB, StringRef ArchiveName = "",
|
||||
uint64_t OffsetInArchive = 0);
|
||||
InputFile *createSharedFile(MemoryBufferRef MB, StringRef DefaultSoName);
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
1040
deps/lld/ELF/InputSection.cpp
vendored
Normal file
1040
deps/lld/ELF/InputSection.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
339
deps/lld/ELF/InputSection.h
vendored
Normal file
339
deps/lld/ELF/InputSection.h
vendored
Normal file
@ -0,0 +1,339 @@
|
||||
//===- InputSection.h -------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_INPUT_SECTION_H
|
||||
#define LLD_ELF_INPUT_SECTION_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "Relocations.h"
|
||||
#include "Thunks.h"
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/ADT/CachedHashString.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/TinyPtrVector.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Support/Threading.h"
|
||||
#include <mutex>
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
class DefinedCommon;
|
||||
class SymbolBody;
|
||||
struct SectionPiece;
|
||||
|
||||
class DefinedRegular;
|
||||
class SyntheticSection;
|
||||
template <class ELFT> class EhFrameSection;
|
||||
class MergeSyntheticSection;
|
||||
template <class ELFT> class ObjectFile;
|
||||
class OutputSection;
|
||||
|
||||
// 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;
|
||||
|
||||
unsigned SectionKind : 3;
|
||||
|
||||
// The next two bit fields are only used by InputSectionBase, but we
|
||||
// put them here so the struct packs better.
|
||||
|
||||
// The garbage collector sets sections' Live bits.
|
||||
// If GC is disabled, all sections are considered live by default.
|
||||
unsigned Live : 1; // for garbage collection
|
||||
unsigned Assigned : 1; // for linker script
|
||||
|
||||
uint32_t Alignment;
|
||||
|
||||
// These corresponds to the fields in Elf_Shdr.
|
||||
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 getOffset(const DefinedRegular &Sym) const;
|
||||
|
||||
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), SectionKind(SectionKind), Alignment(Alignment),
|
||||
Flags(Flags), Entsize(Entsize), Type(Type), Link(Link), Info(Info) {
|
||||
Live = false;
|
||||
Assigned = false;
|
||||
}
|
||||
};
|
||||
|
||||
// This corresponds to a section of an input file.
|
||||
class InputSectionBase : public SectionBase {
|
||||
public:
|
||||
static bool classof(const SectionBase *S);
|
||||
|
||||
// The file this section is from.
|
||||
InputFile *File;
|
||||
|
||||
ArrayRef<uint8_t> Data;
|
||||
uint64_t getOffsetInFile() const;
|
||||
|
||||
static InputSectionBase Discarded;
|
||||
|
||||
InputSectionBase()
|
||||
: SectionBase(Regular, "", /*Flags*/ 0, /*Entsize*/ 0, /*Alignment*/ 0,
|
||||
/*Type*/ 0,
|
||||
/*Info*/ 0, /*Link*/ 0),
|
||||
Repl(this) {
|
||||
Live = false;
|
||||
Assigned = false;
|
||||
NumRelocations = 0;
|
||||
AreRelocsRela = false;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
InputSectionBase(ObjectFile<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);
|
||||
|
||||
// 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;
|
||||
|
||||
// Relocations that refer to this section.
|
||||
const void *FirstRelocation = nullptr;
|
||||
unsigned NumRelocations : 31;
|
||||
unsigned AreRelocsRela : 1;
|
||||
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);
|
||||
}
|
||||
|
||||
// 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.
|
||||
InputSectionBase *Repl;
|
||||
|
||||
// InputSections that are dependent on us (reverse dependency for GC)
|
||||
llvm::TinyPtrVector<InputSectionBase *> DependentSections;
|
||||
|
||||
// Returns the size of this section (even if this is a common or BSS.)
|
||||
size_t getSize() const;
|
||||
|
||||
template <class ELFT> ObjectFile<ELFT> *getFile() const;
|
||||
|
||||
template <class ELFT> llvm::object::ELFFile<ELFT> getObj() const {
|
||||
return getFile<ELFT>()->getObj();
|
||||
}
|
||||
|
||||
InputSection *getLinkOrderDep() const;
|
||||
|
||||
void uncompress();
|
||||
|
||||
// Returns a source location string. Used to construct an error message.
|
||||
template <class ELFT> std::string getLocation(uint64_t Offset);
|
||||
template <class ELFT> std::string getSrcMsg(uint64_t Offset);
|
||||
template <class ELFT> std::string getObjMsg(uint64_t Offset);
|
||||
|
||||
template <class ELFT> void relocate(uint8_t *Buf, uint8_t *BufEnd);
|
||||
void relocateAlloc(uint8_t *Buf, uint8_t *BufEnd);
|
||||
template <class ELFT> void relocateNonAlloc(uint8_t *Buf, uint8_t *BufEnd);
|
||||
|
||||
std::vector<Relocation> Relocations;
|
||||
|
||||
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));
|
||||
}
|
||||
};
|
||||
|
||||
// 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) and put the hash in a side table.
|
||||
struct SectionPiece {
|
||||
SectionPiece(size_t Off, bool Live = false)
|
||||
: InputOff(Off), OutputOff(-1), Live(Live || !Config->GcSections) {}
|
||||
|
||||
size_t InputOff;
|
||||
ssize_t OutputOff : 8 * sizeof(ssize_t) - 1;
|
||||
size_t Live : 1;
|
||||
};
|
||||
static_assert(sizeof(SectionPiece) == 2 * sizeof(size_t),
|
||||
"SectionPiece is too big");
|
||||
|
||||
// This corresponds to a SHF_MERGE section of an input file.
|
||||
class MergeInputSection : public InputSectionBase {
|
||||
public:
|
||||
template <class ELFT>
|
||||
MergeInputSection(ObjectFile<ELFT> *F, const typename ELFT::Shdr *Header,
|
||||
StringRef Name);
|
||||
static bool classof(const SectionBase *S);
|
||||
void splitIntoPieces();
|
||||
|
||||
// Mark the piece at a given offset live. Used by GC.
|
||||
void markLiveAt(uint64_t Offset) {
|
||||
assert(this->Flags & llvm::ELF::SHF_ALLOC);
|
||||
LiveOffsets.insert(Offset);
|
||||
}
|
||||
|
||||
// Translate an offset in the input section to an offset
|
||||
// in the output section.
|
||||
uint64_t getOffset(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;
|
||||
if (Pieces.size() - 1 == I)
|
||||
End = this->Data.size();
|
||||
else
|
||||
End = Pieces[I + 1].InputOff;
|
||||
|
||||
StringRef S = {(const char *)(this->Data.data() + Begin), End - Begin};
|
||||
return {S, Hashes[I]};
|
||||
}
|
||||
|
||||
// Returns the SectionPiece at a given input section offset.
|
||||
SectionPiece *getSectionPiece(uint64_t Offset);
|
||||
const SectionPiece *getSectionPiece(uint64_t Offset) const;
|
||||
|
||||
SyntheticSection *getParent() const;
|
||||
|
||||
private:
|
||||
void splitStrings(ArrayRef<uint8_t> A, size_t Size);
|
||||
void splitNonStrings(ArrayRef<uint8_t> A, size_t Size);
|
||||
|
||||
std::vector<uint32_t> Hashes;
|
||||
|
||||
mutable llvm::DenseMap<uint64_t, uint64_t> OffsetMap;
|
||||
mutable llvm::once_flag InitOffsetMap;
|
||||
|
||||
llvm::DenseSet<uint64_t> LiveOffsets;
|
||||
};
|
||||
|
||||
struct EhSectionPiece : public SectionPiece {
|
||||
EhSectionPiece(size_t Off, InputSectionBase *ID, uint32_t Size,
|
||||
unsigned FirstRelocation)
|
||||
: SectionPiece(Off, false), ID(ID), Size(Size),
|
||||
FirstRelocation(FirstRelocation) {}
|
||||
InputSectionBase *ID;
|
||||
uint32_t Size;
|
||||
uint32_t size() const { return Size; }
|
||||
|
||||
ArrayRef<uint8_t> data() { return {ID->Data.data() + this->InputOff, Size}; }
|
||||
unsigned FirstRelocation;
|
||||
};
|
||||
|
||||
// This corresponds to a .eh_frame section of an input file.
|
||||
class EhInputSection : public InputSectionBase {
|
||||
public:
|
||||
template <class ELFT>
|
||||
EhInputSection(ObjectFile<ELFT> *F, const typename ELFT::Shdr *Header,
|
||||
StringRef Name);
|
||||
static bool classof(const SectionBase *S);
|
||||
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(uint64_t Flags, uint32_t Type, uint32_t Alignment,
|
||||
ArrayRef<uint8_t> Data, StringRef Name, Kind K = Regular);
|
||||
template <class ELFT>
|
||||
InputSection(ObjectFile<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);
|
||||
|
||||
OutputSection *getParent() const;
|
||||
|
||||
// The offset from beginning of the output sections this section was assigned
|
||||
// to. The writer sets a value.
|
||||
uint64_t OutSecOff = 0;
|
||||
|
||||
static bool classof(const SectionBase *S);
|
||||
|
||||
InputSectionBase *getRelocatedSection();
|
||||
|
||||
template <class ELFT, class RelTy>
|
||||
void relocateNonAlloc(uint8_t *Buf, llvm::ArrayRef<RelTy> Rels);
|
||||
|
||||
// Used by ICF.
|
||||
uint32_t Class[2] = {0, 0};
|
||||
|
||||
// Called by ICF to merge two input sections.
|
||||
void replace(InputSection *Other);
|
||||
|
||||
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
|
||||
191
deps/lld/ELF/LTO.cpp
vendored
Normal file
191
deps/lld/ELF/LTO.cpp
vendored
Normal file
@ -0,0 +1,191 @@
|
||||
//===- LTO.cpp ------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "LTO.h"
|
||||
#include "Config.h"
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "lld/Core/TargetOptionsCommandFlags.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/BinaryFormat/ELF.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 llvm::ELF;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
// This is for use when debugging LTO.
|
||||
static void saveBuffer(StringRef Buffer, const Twine &Path) {
|
||||
std::error_code EC;
|
||||
raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None);
|
||||
if (EC)
|
||||
error("cannot create " + Path + ": " + EC.message());
|
||||
OS << Buffer;
|
||||
}
|
||||
|
||||
static void diagnosticHandler(const DiagnosticInfo &DI) {
|
||||
SmallString<128> ErrStorage;
|
||||
raw_svector_ostream OS(ErrStorage);
|
||||
DiagnosticPrinterRawOStream DP(OS);
|
||||
DI.print(DP);
|
||||
warn(ErrStorage);
|
||||
}
|
||||
|
||||
static void checkError(Error E) {
|
||||
handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) -> Error {
|
||||
error(EIB.message());
|
||||
return Error::success();
|
||||
});
|
||||
}
|
||||
|
||||
static std::unique_ptr<lto::LTO> createLTO() {
|
||||
lto::Config Conf;
|
||||
|
||||
// LLD supports the new relocations.
|
||||
Conf.Options = InitTargetOptionsFromCodeGenFlags();
|
||||
Conf.Options.RelaxELFRelocations = true;
|
||||
|
||||
if (Config->Relocatable)
|
||||
Conf.RelocModel = None;
|
||||
else if (Config->Pic)
|
||||
Conf.RelocModel = Reloc::PIC_;
|
||||
else
|
||||
Conf.RelocModel = Reloc::Static;
|
||||
Conf.CodeModel = GetCodeModelFromCMModel();
|
||||
Conf.DisableVerify = Config->DisableVerify;
|
||||
Conf.DiagHandler = diagnosticHandler;
|
||||
Conf.OptLevel = Config->LTOO;
|
||||
|
||||
// Set up a custom pipeline if we've been asked to.
|
||||
Conf.OptPipeline = Config->LTONewPmPasses;
|
||||
Conf.AAPipeline = Config->LTOAAPipeline;
|
||||
|
||||
// Set up optimization remarks if we've been asked to.
|
||||
Conf.RemarksFilename = Config->OptRemarksFilename;
|
||||
Conf.RemarksWithHotness = Config->OptRemarksWithHotness;
|
||||
|
||||
if (Config->SaveTemps)
|
||||
checkError(Conf.addSaveTemps(std::string(Config->OutputFile) + ".",
|
||||
/*UseInputModulePath*/ true));
|
||||
|
||||
lto::ThinBackend Backend;
|
||||
if (Config->ThinLTOJobs != -1u)
|
||||
Backend = lto::createInProcessThinBackend(Config->ThinLTOJobs);
|
||||
return llvm::make_unique<lto::LTO>(std::move(Conf), Backend,
|
||||
Config->LTOPartitions);
|
||||
}
|
||||
|
||||
BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) {}
|
||||
|
||||
BitcodeCompiler::~BitcodeCompiler() = default;
|
||||
|
||||
static void undefine(Symbol *S) {
|
||||
replaceBody<Undefined>(S, S->body()->getName(), /*IsLocal=*/false,
|
||||
STV_DEFAULT, S->body()->Type, nullptr);
|
||||
}
|
||||
|
||||
void BitcodeCompiler::add(BitcodeFile &F) {
|
||||
lto::InputFile &Obj = *F.Obj;
|
||||
unsigned SymNum = 0;
|
||||
std::vector<Symbol *> Syms = F.getSymbols();
|
||||
std::vector<lto::SymbolResolution> Resols(Syms.size());
|
||||
|
||||
// Provide a resolution to the LTO API for each symbol.
|
||||
for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) {
|
||||
Symbol *Sym = Syms[SymNum];
|
||||
lto::SymbolResolution &R = Resols[SymNum];
|
||||
++SymNum;
|
||||
SymbolBody *B = Sym->body();
|
||||
|
||||
// Ideally we shouldn't check for SF_Undefined but currently IRObjectFile
|
||||
// reports two symbols for module ASM defined. Without this check, lld
|
||||
// flags an undefined in IR with a definition in ASM as prevailing.
|
||||
// Once IRObjectFile is fixed to report only one symbol this hack can
|
||||
// be removed.
|
||||
R.Prevailing = !ObjSym.isUndefined() && B->File == &F;
|
||||
|
||||
R.VisibleToRegularObj =
|
||||
Sym->IsUsedInRegularObj || (R.Prevailing && Sym->includeInDynsym());
|
||||
if (R.Prevailing)
|
||||
undefine(Sym);
|
||||
R.LinkerRedefined = Config->RenamedSymbols.count(Sym);
|
||||
}
|
||||
checkError(LTOObj->add(std::move(F.Obj), Resols));
|
||||
}
|
||||
|
||||
// Merge all the bitcode files we have seen, codegen the result
|
||||
// and return the resulting ObjectFile(s).
|
||||
std::vector<InputFile *> BitcodeCompiler::compile() {
|
||||
std::vector<InputFile *> Ret;
|
||||
unsigned MaxTasks = LTOObj->getMaxTasks();
|
||||
Buff.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);
|
||||
}));
|
||||
|
||||
checkError(LTOObj->run(
|
||||
[&](size_t Task) {
|
||||
return llvm::make_unique<lto::NativeObjectStream>(
|
||||
llvm::make_unique<raw_svector_ostream>(Buff[Task]));
|
||||
},
|
||||
Cache));
|
||||
|
||||
if (!Config->ThinLTOCacheDir.empty())
|
||||
pruneCache(Config->ThinLTOCacheDir, Config->ThinLTOCachePolicy);
|
||||
|
||||
for (unsigned I = 0; I != MaxTasks; ++I) {
|
||||
if (Buff[I].empty())
|
||||
continue;
|
||||
if (Config->SaveTemps) {
|
||||
if (I == 0)
|
||||
saveBuffer(Buff[I], Config->OutputFile + ".lto.o");
|
||||
else
|
||||
saveBuffer(Buff[I], Config->OutputFile + Twine(I) + ".lto.o");
|
||||
}
|
||||
InputFile *Obj = createObjectFile(MemoryBufferRef(Buff[I], "lto.tmp"));
|
||||
Ret.push_back(Obj);
|
||||
}
|
||||
|
||||
for (std::unique_ptr<MemoryBuffer> &File : Files)
|
||||
if (File)
|
||||
Ret.push_back(createObjectFile(*File));
|
||||
|
||||
return Ret;
|
||||
}
|
||||
57
deps/lld/ELF/LTO.h
vendored
Normal file
57
deps/lld/ELF/LTO.h
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
//===- LTO.h ----------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file provides a way to combine bitcode files into one 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/Core/LLVM.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
namespace lto {
|
||||
class LTO;
|
||||
}
|
||||
} // namespace llvm
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
class BitcodeFile;
|
||||
class InputFile;
|
||||
|
||||
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>> Buff;
|
||||
std::vector<std::unique_ptr<MemoryBuffer>> Files;
|
||||
};
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
1255
deps/lld/ELF/LinkerScript.cpp
vendored
Normal file
1255
deps/lld/ELF/LinkerScript.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
306
deps/lld/ELF/LinkerScript.h
vendored
Normal file
306
deps/lld/ELF/LinkerScript.h
vendored
Normal file
@ -0,0 +1,306 @@
|
||||
//===- LinkerScript.h -------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_LINKER_SCRIPT_H
|
||||
#define LLD_ELF_LINKER_SCRIPT_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "Strings.h"
|
||||
#include "Writer.h"
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/DenseSet.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 DefinedCommon;
|
||||
class SymbolBody;
|
||||
class InputSectionBase;
|
||||
class InputSection;
|
||||
class OutputSection;
|
||||
class OutputSectionFactory;
|
||||
class InputSectionBase;
|
||||
class SectionBase;
|
||||
|
||||
struct ExprValue {
|
||||
SectionBase *Sec;
|
||||
uint64_t Val;
|
||||
bool ForceAbsolute;
|
||||
uint64_t Alignment = 1;
|
||||
std::string Loc;
|
||||
|
||||
ExprValue(SectionBase *Sec, bool ForceAbsolute, uint64_t Val,
|
||||
const Twine &Loc)
|
||||
: Sec(Sec), Val(Val), ForceAbsolute(ForceAbsolute), Loc(Loc.str()) {}
|
||||
ExprValue(SectionBase *Sec, uint64_t Val, const Twine &Loc)
|
||||
: ExprValue(Sec, false, Val, Loc) {}
|
||||
ExprValue(uint64_t Val) : ExprValue(nullptr, Val, "") {}
|
||||
bool isAbsolute() const { return ForceAbsolute || Sec == nullptr; }
|
||||
uint64_t getValue() const;
|
||||
uint64_t getSecAddr() const;
|
||||
};
|
||||
|
||||
// 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.
|
||||
typedef std::function<ExprValue()> Expr;
|
||||
|
||||
// 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,
|
||||
AssertKind, // ASSERT(expr)
|
||||
BytesDataKind // 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);
|
||||
|
||||
// The LHS of an expression. Name is either a symbol name or ".".
|
||||
StringRef Name;
|
||||
SymbolBody *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;
|
||||
};
|
||||
|
||||
// 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 {
|
||||
std::string Name;
|
||||
uint64_t Origin;
|
||||
uint64_t Length;
|
||||
uint32_t Flags;
|
||||
uint32_t NegFlags;
|
||||
};
|
||||
|
||||
struct OutputSectionCommand : BaseCommand {
|
||||
OutputSectionCommand(StringRef Name)
|
||||
: BaseCommand(OutputSectionKind), Name(Name) {}
|
||||
|
||||
static bool classof(const BaseCommand *C);
|
||||
|
||||
OutputSection *Sec = nullptr;
|
||||
MemoryRegion *MemRegion = nullptr;
|
||||
StringRef Name;
|
||||
Expr AddrExpr;
|
||||
Expr AlignExpr;
|
||||
Expr LMAExpr;
|
||||
Expr SubalignExpr;
|
||||
std::vector<BaseCommand *> Commands;
|
||||
std::vector<StringRef> Phdrs;
|
||||
llvm::Optional<uint32_t> Filler;
|
||||
ConstraintKind Constraint = ConstraintKind::NoConstraint;
|
||||
std::string Location;
|
||||
std::string MemoryRegionName;
|
||||
bool Noload = false;
|
||||
|
||||
template <class ELFT> void finalize();
|
||||
template <class ELFT> void writeTo(uint8_t *Buf);
|
||||
template <class ELFT> void maybeCompress();
|
||||
uint32_t getFiller();
|
||||
|
||||
void sort(std::function<int(InputSectionBase *S)> Order);
|
||||
void sortInitFini();
|
||||
void sortCtorsDtors();
|
||||
};
|
||||
|
||||
// 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) {}
|
||||
|
||||
StringMatcher ExcludedFilePat;
|
||||
StringMatcher SectionPat;
|
||||
SortSectionPolicy SortOuter;
|
||||
SortSectionPolicy SortInner;
|
||||
};
|
||||
|
||||
struct InputSectionDescription : BaseCommand {
|
||||
InputSectionDescription(StringRef FilePattern)
|
||||
: BaseCommand(InputSectionKind), FilePat(FilePattern) {}
|
||||
|
||||
static bool classof(const BaseCommand *C);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
// Represents an ASSERT().
|
||||
struct AssertCommand : BaseCommand {
|
||||
AssertCommand(Expr E) : BaseCommand(AssertKind), Expression(E) {}
|
||||
|
||||
static bool classof(const BaseCommand *C);
|
||||
|
||||
Expr Expression;
|
||||
};
|
||||
|
||||
// Represents BYTE(), SHORT(), LONG(), or QUAD().
|
||||
struct BytesDataCommand : BaseCommand {
|
||||
BytesDataCommand(Expr E, unsigned Size)
|
||||
: BaseCommand(BytesDataKind), Expression(E), Size(Size) {}
|
||||
|
||||
static bool classof(const BaseCommand *C);
|
||||
|
||||
Expr Expression;
|
||||
unsigned Offset;
|
||||
unsigned Size;
|
||||
};
|
||||
|
||||
struct PhdrsCommand {
|
||||
StringRef Name;
|
||||
unsigned Type;
|
||||
bool HasFilehdr;
|
||||
bool HasPhdrs;
|
||||
unsigned Flags;
|
||||
Expr LMAExpr;
|
||||
};
|
||||
|
||||
// ScriptConfiguration holds linker script parse results.
|
||||
struct ScriptConfiguration {
|
||||
// Used to assign addresses to sections.
|
||||
std::vector<BaseCommand *> Commands;
|
||||
|
||||
// Used to assign sections to headers.
|
||||
std::vector<PhdrsCommand> PhdrsCommands;
|
||||
|
||||
bool HasSections = 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::DenseMap<llvm::StringRef, MemoryRegion> MemoryRegions;
|
||||
|
||||
// A list of symbols referenced by the script.
|
||||
std::vector<llvm::StringRef> ReferencedSymbols;
|
||||
};
|
||||
|
||||
class LinkerScript final {
|
||||
// Temporary state used in processCommands() 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 {
|
||||
uint64_t ThreadBssOffset = 0;
|
||||
OutputSection *OutSec = nullptr;
|
||||
MemoryRegion *MemRegion = nullptr;
|
||||
llvm::DenseMap<const MemoryRegion *, uint64_t> MemRegionOffset;
|
||||
std::function<uint64_t()> LMAOffset;
|
||||
AddressState(const ScriptConfiguration &Opt);
|
||||
};
|
||||
llvm::DenseMap<OutputSection *, OutputSectionCommand *> SecToCommand;
|
||||
llvm::DenseMap<StringRef, OutputSectionCommand *> NameToOutputSectionCommand;
|
||||
|
||||
void assignSymbol(SymbolAssignment *Cmd, bool InSec);
|
||||
void setDot(Expr E, const Twine &Loc, bool InSec);
|
||||
|
||||
std::vector<InputSection *>
|
||||
computeInputSections(const InputSectionDescription *);
|
||||
|
||||
std::vector<InputSectionBase *>
|
||||
createInputSectionList(OutputSectionCommand &Cmd);
|
||||
|
||||
std::vector<size_t> getPhdrIndices(OutputSectionCommand *Cmd);
|
||||
size_t getPhdrIndex(const Twine &Loc, StringRef PhdrName);
|
||||
|
||||
MemoryRegion *findMemoryRegion(OutputSectionCommand *Cmd);
|
||||
|
||||
void switchTo(OutputSection *Sec);
|
||||
uint64_t advance(uint64_t Size, unsigned Align);
|
||||
void output(InputSection *Sec);
|
||||
void process(BaseCommand &Base);
|
||||
|
||||
AddressState *CurAddressState = nullptr;
|
||||
OutputSection *Aether;
|
||||
|
||||
uint64_t Dot;
|
||||
|
||||
public:
|
||||
bool ErrorOnMissingSection = false;
|
||||
OutputSectionCommand *createOutputSectionCommand(StringRef Name,
|
||||
StringRef Location);
|
||||
OutputSectionCommand *getOrCreateOutputSectionCommand(StringRef Name);
|
||||
|
||||
OutputSectionCommand *getCmd(OutputSection *Sec) const;
|
||||
bool hasPhdrsCommands() { return !Opt.PhdrsCommands.empty(); }
|
||||
uint64_t getDot() { return Dot; }
|
||||
void discard(ArrayRef<InputSectionBase *> V);
|
||||
|
||||
ExprValue getSymbolValue(const Twine &Loc, StringRef S);
|
||||
bool isDefined(StringRef S);
|
||||
|
||||
void fabricateDefaultCommands();
|
||||
void addOrphanSections(OutputSectionFactory &Factory);
|
||||
void removeEmptyCommands();
|
||||
void adjustSectionsBeforeSorting();
|
||||
void adjustSectionsAfterSorting();
|
||||
|
||||
std::vector<PhdrEntry> createPhdrs();
|
||||
bool ignoreInterpSection();
|
||||
|
||||
bool shouldKeep(InputSectionBase *S);
|
||||
void assignOffsets(OutputSectionCommand *Cmd);
|
||||
void processNonSectionCommands();
|
||||
void assignAddresses();
|
||||
void allocateHeaders(std::vector<PhdrEntry> &Phdrs);
|
||||
void addSymbol(SymbolAssignment *Cmd);
|
||||
void processCommands(OutputSectionFactory &Factory);
|
||||
|
||||
// Parsed linker script configurations are set to this struct.
|
||||
ScriptConfiguration Opt;
|
||||
};
|
||||
|
||||
extern LinkerScript *Script;
|
||||
|
||||
} // end namespace elf
|
||||
} // end namespace lld
|
||||
|
||||
#endif // LLD_ELF_LINKER_SCRIPT_H
|
||||
150
deps/lld/ELF/MapFile.cpp
vendored
Normal file
150
deps/lld/ELF/MapFile.cpp
vendored
Normal file
@ -0,0 +1,150 @@
|
||||
//===- MapFile.cpp --------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the -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 "Strings.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Threads.h"
|
||||
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
typedef DenseMap<const SectionBase *, SmallVector<DefinedRegular *, 4>>
|
||||
SymbolMapTy;
|
||||
|
||||
// Print out the first three columns of a line.
|
||||
template <class ELFT>
|
||||
static void writeHeader(raw_ostream &OS, uint64_t Addr, uint64_t Size,
|
||||
uint64_t Align) {
|
||||
int W = ELFT::Is64Bits ? 16 : 8;
|
||||
OS << format("%0*llx %0*llx %5lld ", W, Addr, W, Size, Align);
|
||||
}
|
||||
|
||||
static std::string indent(int Depth) { return std::string(Depth * 8, ' '); }
|
||||
|
||||
// Returns a list of all symbols that we want to print out.
|
||||
template <class ELFT> std::vector<DefinedRegular *> getSymbols() {
|
||||
std::vector<DefinedRegular *> V;
|
||||
for (elf::ObjectFile<ELFT> *File : Symtab<ELFT>::X->getObjectFiles())
|
||||
for (SymbolBody *B : File->getSymbols())
|
||||
if (B->File == File && !B->isSection())
|
||||
if (auto *Sym = dyn_cast<DefinedRegular>(B))
|
||||
if (Sym->Section && Sym->Section->Live)
|
||||
V.push_back(Sym);
|
||||
return V;
|
||||
}
|
||||
|
||||
// Returns a map from sections to their symbols.
|
||||
template <class ELFT>
|
||||
SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> Syms) {
|
||||
SymbolMapTy Ret;
|
||||
for (DefinedRegular *S : Syms)
|
||||
Ret[S->Section].push_back(S);
|
||||
|
||||
// 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) {
|
||||
SmallVectorImpl<DefinedRegular *> &V = It.second;
|
||||
std::sort(V.begin(), V.end(), [](DefinedRegular *A, DefinedRegular *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.
|
||||
template <class ELFT>
|
||||
DenseMap<DefinedRegular *, std::string>
|
||||
getSymbolStrings(ArrayRef<DefinedRegular *> Syms) {
|
||||
std::vector<std::string> Str(Syms.size());
|
||||
parallelForEachN(0, Syms.size(), [&](size_t I) {
|
||||
raw_string_ostream OS(Str[I]);
|
||||
writeHeader<ELFT>(OS, Syms[I]->getVA(), Syms[I]->template getSize<ELFT>(),
|
||||
0);
|
||||
OS << indent(2) << 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;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void elf::writeMapFile(llvm::ArrayRef<OutputSectionCommand *> Script) {
|
||||
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<DefinedRegular *> Syms = getSymbols<ELFT>();
|
||||
SymbolMapTy SectionSyms = getSectionSyms<ELFT>(Syms);
|
||||
DenseMap<DefinedRegular *, std::string> SymStr = getSymbolStrings<ELFT>(Syms);
|
||||
|
||||
// Print out the header line.
|
||||
int W = ELFT::Is64Bits ? 16 : 8;
|
||||
OS << left_justify("Address", W) << ' ' << left_justify("Size", W)
|
||||
<< " Align Out In Symbol\n";
|
||||
|
||||
// Print out file contents.
|
||||
for (OutputSectionCommand *Cmd : Script) {
|
||||
OutputSection *OSec = Cmd->Sec;
|
||||
writeHeader<ELFT>(OS, OSec->Addr, OSec->Size, OSec->Alignment);
|
||||
OS << OSec->Name << '\n';
|
||||
|
||||
// Dump symbols for each input section.
|
||||
for (BaseCommand *Base : Cmd->Commands) {
|
||||
auto *ISD = dyn_cast<InputSectionDescription>(Base);
|
||||
if (!ISD)
|
||||
continue;
|
||||
for (InputSection *IS : ISD->Sections) {
|
||||
writeHeader<ELFT>(OS, OSec->Addr + IS->OutSecOff, IS->getSize(),
|
||||
IS->Alignment);
|
||||
OS << indent(1) << toString(IS) << '\n';
|
||||
for (DefinedRegular *Sym : SectionSyms[IS])
|
||||
OS << SymStr[Sym] << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template void elf::writeMapFile<ELF32LE>(ArrayRef<OutputSectionCommand *>);
|
||||
template void elf::writeMapFile<ELF32BE>(ArrayRef<OutputSectionCommand *>);
|
||||
template void elf::writeMapFile<ELF64LE>(ArrayRef<OutputSectionCommand *>);
|
||||
template void elf::writeMapFile<ELF64BE>(ArrayRef<OutputSectionCommand *>);
|
||||
23
deps/lld/ELF/MapFile.h
vendored
Normal file
23
deps/lld/ELF/MapFile.h
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
//===- MapFile.h ------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_MAPFILE_H
|
||||
#define LLD_ELF_MAPFILE_H
|
||||
|
||||
#include <llvm/ADT/ArrayRef.h>
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
struct OutputSectionCommand;
|
||||
template <class ELFT>
|
||||
void writeMapFile(llvm::ArrayRef<OutputSectionCommand *> Script);
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
268
deps/lld/ELF/MarkLive.cpp
vendored
Normal file
268
deps/lld/ELF/MarkLive.cpp
vendored
Normal file
@ -0,0 +1,268 @@
|
||||
//===- MarkLive.cpp -------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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 "InputSection.h"
|
||||
#include "LinkerScript.h"
|
||||
#include "Memory.h"
|
||||
#include "OutputSections.h"
|
||||
#include "Strings.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.h"
|
||||
#include "Target.h"
|
||||
#include "Writer.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 {
|
||||
// A resolved relocation. The Sec and Offset fields are set if the relocation
|
||||
// was resolved to an offset within a section.
|
||||
struct ResolvedReloc {
|
||||
InputSectionBase *Sec;
|
||||
uint64_t Offset;
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
template <class ELFT>
|
||||
static typename ELFT::uint 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 typename ELFT::uint getAddend(InputSectionBase &Sec,
|
||||
const typename ELFT::Rela &Rel) {
|
||||
return Rel.r_addend;
|
||||
}
|
||||
|
||||
// There are normally few input sections whose names are valid C
|
||||
// identifiers, so we just store a std::vector instead of a multimap.
|
||||
static DenseMap<StringRef, std::vector<InputSectionBase *>> CNamedSections;
|
||||
|
||||
template <class ELFT, class RelT>
|
||||
static void resolveReloc(InputSectionBase &Sec, RelT &Rel,
|
||||
std::function<void(ResolvedReloc)> Fn) {
|
||||
SymbolBody &B = Sec.getFile<ELFT>()->getRelocTargetSym(Rel);
|
||||
if (auto *D = dyn_cast<DefinedRegular>(&B)) {
|
||||
if (!D->Section)
|
||||
return;
|
||||
typename ELFT::uint Offset = D->Value;
|
||||
if (D->isSection())
|
||||
Offset += getAddend<ELFT>(Sec, Rel);
|
||||
Fn({cast<InputSectionBase>(D->Section), Offset});
|
||||
} else if (auto *U = dyn_cast<Undefined>(&B)) {
|
||||
for (InputSectionBase *Sec : CNamedSections.lookup(U->getName()))
|
||||
Fn({Sec, 0});
|
||||
}
|
||||
}
|
||||
|
||||
// Calls Fn for each section that Sec refers to via relocations.
|
||||
template <class ELFT>
|
||||
static void forEachSuccessor(InputSection &Sec,
|
||||
std::function<void(ResolvedReloc)> Fn) {
|
||||
if (Sec.AreRelocsRela) {
|
||||
for (const typename ELFT::Rela &Rel : Sec.template relas<ELFT>())
|
||||
resolveReloc<ELFT>(Sec, Rel, Fn);
|
||||
} else {
|
||||
for (const typename ELFT::Rel &Rel : Sec.template rels<ELFT>())
|
||||
resolveReloc<ELFT>(Sec, Rel, Fn);
|
||||
}
|
||||
for (InputSectionBase *IS : Sec.DependentSections)
|
||||
Fn({IS, 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, class RelTy>
|
||||
static void scanEhFrameSection(EhInputSection &EH, ArrayRef<RelTy> Rels,
|
||||
std::function<void(ResolvedReloc)> Enqueue) {
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
for (unsigned I = 0, N = EH.Pieces.size(); I < N; ++I) {
|
||||
EhSectionPiece &Piece = EH.Pieces[I];
|
||||
unsigned FirstRelI = Piece.FirstRelocation;
|
||||
if (FirstRelI == (unsigned)-1)
|
||||
continue;
|
||||
if (read32<E>(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<ELFT>(EH, Rels[FirstRelI], Enqueue);
|
||||
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.
|
||||
typename ELFT::uint PieceEnd = Piece.InputOff + Piece.size();
|
||||
for (unsigned I2 = FirstRelI, N2 = Rels.size(); I2 < N2; ++I2) {
|
||||
const RelTy &Rel = Rels[I2];
|
||||
if (Rel.r_offset >= PieceEnd)
|
||||
break;
|
||||
resolveReloc<ELFT>(EH, Rels[I2], [&](ResolvedReloc R) {
|
||||
if (!R.Sec || R.Sec == &InputSection::Discarded)
|
||||
return;
|
||||
if (R.Sec->Flags & SHF_EXECINSTR)
|
||||
return;
|
||||
Enqueue({R.Sec, 0});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static void scanEhFrameSection(EhInputSection &EH,
|
||||
std::function<void(ResolvedReloc)> Enqueue) {
|
||||
if (!EH.NumRelocations)
|
||||
return;
|
||||
|
||||
// Unfortunately we need to split .eh_frame early since some relocations in
|
||||
// .eh_frame keep other section alive and some don't.
|
||||
EH.split<ELFT>();
|
||||
|
||||
if (EH.AreRelocsRela)
|
||||
scanEhFrameSection<ELFT>(EH, EH.template relas<ELFT>(), Enqueue);
|
||||
else
|
||||
scanEhFrameSection<ELFT>(EH, EH.template rels<ELFT>(), Enqueue);
|
||||
}
|
||||
|
||||
// We do not garbage-collect two types of sections:
|
||||
// 1) Sections used by the loader (.init, .fini, .ctors, .dtors or .jcr)
|
||||
// 2) Non-allocatable sections which typically contain debugging information
|
||||
template <class ELFT> 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:
|
||||
if (!(Sec->Flags & SHF_ALLOC))
|
||||
return true;
|
||||
|
||||
StringRef S = Sec->Name;
|
||||
return S.startswith(".ctors") || S.startswith(".dtors") ||
|
||||
S.startswith(".init") || S.startswith(".fini") ||
|
||||
S.startswith(".jcr");
|
||||
}
|
||||
}
|
||||
|
||||
// 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 elf::markLive() {
|
||||
SmallVector<InputSection *, 256> Q;
|
||||
CNamedSections.clear();
|
||||
|
||||
auto Enqueue = [&](ResolvedReloc R) {
|
||||
// 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 (R.Sec == &InputSection::Discarded)
|
||||
return;
|
||||
|
||||
// We don't gc non alloc sections.
|
||||
if (!(R.Sec->Flags & SHF_ALLOC))
|
||||
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>(R.Sec))
|
||||
MS->markLiveAt(R.Offset);
|
||||
|
||||
if (R.Sec->Live)
|
||||
return;
|
||||
R.Sec->Live = true;
|
||||
// Add input section to the queue.
|
||||
if (InputSection *S = dyn_cast<InputSection>(R.Sec))
|
||||
Q.push_back(S);
|
||||
};
|
||||
|
||||
auto MarkSymbol = [&](const SymbolBody *Sym) {
|
||||
if (auto *D = dyn_cast_or_null<DefinedRegular>(Sym))
|
||||
if (auto *IS = cast_or_null<InputSectionBase>(D->Section))
|
||||
Enqueue({IS, D->Value});
|
||||
};
|
||||
|
||||
// Add GC root symbols.
|
||||
MarkSymbol(Symtab<ELFT>::X->find(Config->Entry));
|
||||
MarkSymbol(Symtab<ELFT>::X->find(Config->Init));
|
||||
MarkSymbol(Symtab<ELFT>::X->find(Config->Fini));
|
||||
for (StringRef S : Config->Undefined)
|
||||
MarkSymbol(Symtab<ELFT>::X->find(S));
|
||||
for (StringRef S : Script->Opt.ReferencedSymbols)
|
||||
MarkSymbol(Symtab<ELFT>::X->find(S));
|
||||
|
||||
// Preserve externally-visible symbols if the symbols defined by this
|
||||
// file can interrupt other ELF file's symbols at runtime.
|
||||
for (const Symbol *S : Symtab<ELFT>::X->getSymbols())
|
||||
if (S->includeInDynsym())
|
||||
MarkSymbol(S->body());
|
||||
|
||||
// Preserve special sections and those which are specified in linker
|
||||
// script KEEP command.
|
||||
for (InputSectionBase *Sec : InputSections) {
|
||||
// .eh_frame is always marked as live now, but also it can reference to
|
||||
// sections that contain personality. We preserve all non-text sections
|
||||
// referred by .eh_frame here.
|
||||
if (auto *EH = dyn_cast_or_null<EhInputSection>(Sec))
|
||||
scanEhFrameSection<ELFT>(*EH, Enqueue);
|
||||
if (Sec->Flags & SHF_LINK_ORDER)
|
||||
continue;
|
||||
if (isReserved<ELFT>(Sec) || Script->shouldKeep(Sec))
|
||||
Enqueue({Sec, 0});
|
||||
else if (isValidCIdentifier(Sec->Name)) {
|
||||
CNamedSections[Saver.save("__start_" + Sec->Name)].push_back(Sec);
|
||||
CNamedSections[Saver.save("__end_" + Sec->Name)].push_back(Sec);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark all reachable sections.
|
||||
while (!Q.empty())
|
||||
forEachSuccessor<ELFT>(*Q.pop_back_val(), Enqueue);
|
||||
}
|
||||
|
||||
template void elf::markLive<ELF32LE>();
|
||||
template void elf::markLive<ELF32BE>();
|
||||
template void elf::markLive<ELF64LE>();
|
||||
template void elf::markLive<ELF64BE>();
|
||||
67
deps/lld/ELF/Memory.h
vendored
Normal file
67
deps/lld/ELF/Memory.h
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
//===- Memory.h -------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines arena allocators.
|
||||
//
|
||||
// Almost all large objects, such as files, sections or symbols, are
|
||||
// used for the entire lifetime of the linker once they are created.
|
||||
// This usage characteristic makes arena allocator an attractive choice
|
||||
// where the entire linker is one arena. With an arena, newly created
|
||||
// objects belong to the arena and freed all at once when everything is done.
|
||||
// Arena allocators are efficient and easy to understand.
|
||||
// Most objects are allocated using the arena allocators defined by this file.
|
||||
//
|
||||
// If you edit this file, please edit COFF/Memory.h too.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_MEMORY_H
|
||||
#define LLD_ELF_MEMORY_H
|
||||
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "llvm/Support/StringSaver.h"
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
// Use this arena if your object doesn't have a destructor.
|
||||
extern llvm::BumpPtrAllocator BAlloc;
|
||||
extern llvm::StringSaver Saver;
|
||||
|
||||
// These two classes are hack to keep track of all
|
||||
// SpecificBumpPtrAllocator instances.
|
||||
struct SpecificAllocBase {
|
||||
SpecificAllocBase() { Instances.push_back(this); }
|
||||
virtual ~SpecificAllocBase() = default;
|
||||
virtual void reset() = 0;
|
||||
static std::vector<SpecificAllocBase *> Instances;
|
||||
};
|
||||
|
||||
template <class T> struct SpecificAlloc : public SpecificAllocBase {
|
||||
void reset() override { Alloc.DestroyAll(); }
|
||||
llvm::SpecificBumpPtrAllocator<T> Alloc;
|
||||
};
|
||||
|
||||
// Use this arena if your object has a destructor.
|
||||
// Your destructor will be invoked from freeArena().
|
||||
template <typename T, typename... U> T *make(U &&... Args) {
|
||||
static SpecificAlloc<T> Alloc;
|
||||
return new (Alloc.Alloc.Allocate()) T(std::forward<U>(Args)...);
|
||||
}
|
||||
|
||||
inline void freeArena() {
|
||||
for (SpecificAllocBase *Alloc : SpecificAllocBase::Instances)
|
||||
Alloc->reset();
|
||||
BAlloc.Reset();
|
||||
}
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
414
deps/lld/ELF/Options.td
vendored
Normal file
414
deps/lld/ELF/Options.td
vendored
Normal file
@ -0,0 +1,414 @@
|
||||
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>;
|
||||
class S<string name>: Separate<["--", "-"], name>;
|
||||
class JS<string name>: JoinedOrSeparate<["--", "-"], name>;
|
||||
|
||||
def auxiliary: S<"auxiliary">, HelpText<"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">;
|
||||
|
||||
def Bstatic: F<"Bstatic">, HelpText<"Do not link against shared libraries">;
|
||||
|
||||
def build_id: F<"build-id">, HelpText<"Generate build ID note">;
|
||||
|
||||
def build_id_eq: J<"build-id=">, HelpText<"Generate build ID note">;
|
||||
|
||||
def compress_debug_sections : J<"compress-debug-sections=">,
|
||||
HelpText<"Compress DWARF debug sections">;
|
||||
|
||||
def defsym: J<"defsym=">, HelpText<"Define a symbol alias">;
|
||||
|
||||
def L: JoinedOrSeparate<["-"], "L">, MetaVarName<"<dir>">,
|
||||
HelpText<"Add a directory to the library search path">;
|
||||
|
||||
def O: Joined<["-"], "O">, HelpText<"Optimize output file size">;
|
||||
|
||||
def Tbss: S<"Tbss">, HelpText<"Same as --section-start with .bss as the sectionname">;
|
||||
|
||||
def Tdata: S<"Tdata">, HelpText<"Same as --section-start with .data as the sectionname">;
|
||||
|
||||
def Ttext: S<"Ttext">, HelpText<"Same as --section-start with .text as the sectionname">;
|
||||
|
||||
def allow_multiple_definition: F<"allow-multiple-definition">,
|
||||
HelpText<"Allow multiple definitions">;
|
||||
|
||||
def as_needed: F<"as-needed">,
|
||||
HelpText<"Only set DT_NEEDED for shared libraries if used">;
|
||||
|
||||
def color_diagnostics: F<"color-diagnostics">,
|
||||
HelpText<"Use colors in diagnostics">;
|
||||
|
||||
def color_diagnostics_eq: J<"color-diagnostics=">,
|
||||
HelpText<"Use colors in diagnostics">;
|
||||
|
||||
def define_common: F<"define-common">,
|
||||
HelpText<"Assign space to common symbols">;
|
||||
|
||||
def demangle: F<"demangle">, HelpText<"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">;
|
||||
|
||||
def dynamic_linker: S<"dynamic-linker">,
|
||||
HelpText<"Which dynamic linker to use">;
|
||||
|
||||
def dynamic_list: S<"dynamic-list">,
|
||||
HelpText<"Read a list of dynamic symbols">;
|
||||
|
||||
def eh_frame_hdr: F<"eh-frame-hdr">,
|
||||
HelpText<"Request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header">;
|
||||
|
||||
def emit_relocs: F<"emit-relocs">, HelpText<"Generate relocations in output">;
|
||||
|
||||
def enable_new_dtags: F<"enable-new-dtags">,
|
||||
HelpText<"Enable new dynamic tags">;
|
||||
|
||||
def end_lib: F<"end-lib">,
|
||||
HelpText<"End a grouping of objects that should be treated as if they were together in an archive">;
|
||||
|
||||
def entry: S<"entry">, MetaVarName<"<entry>">,
|
||||
HelpText<"Name of entry point symbol">;
|
||||
|
||||
def error_limit: S<"error-limit">,
|
||||
HelpText<"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">;
|
||||
|
||||
def exclude_libs: S<"exclude-libs">,
|
||||
HelpText<"Exclude static libraries from automatic export">;
|
||||
|
||||
def export_dynamic: F<"export-dynamic">,
|
||||
HelpText<"Put symbols in the dynamic symbol table">;
|
||||
|
||||
def export_dynamic_symbol: S<"export-dynamic-symbol">,
|
||||
HelpText<"Put a symbol in the dynamic symbol table">;
|
||||
|
||||
def fatal_warnings: F<"fatal-warnings">,
|
||||
HelpText<"Treat warnings as errors">;
|
||||
|
||||
def filter: J<"filter=">, HelpText<"Set DT_FILTER field to the specified name">;
|
||||
|
||||
def fini: S<"fini">, MetaVarName<"<symbol>">,
|
||||
HelpText<"Specify a finalizer function">;
|
||||
|
||||
def full_shutdown : F<"full-shutdown">,
|
||||
HelpText<"Perform a full shutdown instead of calling _exit">;
|
||||
|
||||
def format: J<"format=">, MetaVarName<"<input-format>">,
|
||||
HelpText<"Change the input format of the inputs following this option">;
|
||||
|
||||
def gc_sections: F<"gc-sections">,
|
||||
HelpText<"Enable garbage collection of unused sections">;
|
||||
|
||||
def gdb_index: F<"gdb-index">,
|
||||
HelpText<"Generate .gdb_index section">;
|
||||
|
||||
def hash_style: S<"hash-style">,
|
||||
HelpText<"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_none: F<"icf=none">, HelpText<"Disable identical code folding">;
|
||||
|
||||
def image_base : J<"image-base=">, HelpText<"Set the base address">;
|
||||
|
||||
def init: S<"init">, MetaVarName<"<symbol>">,
|
||||
HelpText<"Specify an initializer function">;
|
||||
|
||||
def l: JoinedOrSeparate<["-"], "l">, MetaVarName<"<libName>">,
|
||||
HelpText<"Root name of library to use">;
|
||||
|
||||
def lto_O: J<"lto-O">, MetaVarName<"<opt-level>">,
|
||||
HelpText<"Optimization level for LTO">;
|
||||
|
||||
def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">;
|
||||
|
||||
def Map: JS<"Map">, HelpText<"Print a link map to the specified file">;
|
||||
|
||||
def nostdlib: F<"nostdlib">,
|
||||
HelpText<"Only search directories specified on the command line">;
|
||||
|
||||
def no_as_needed: F<"no-as-needed">,
|
||||
HelpText<"Always DT_NEEDED for shared libraries">;
|
||||
|
||||
def no_color_diagnostics: F<"no-color-diagnostics">,
|
||||
HelpText<"Do not use colors in diagnostics">;
|
||||
|
||||
def no_define_common: F<"no-define-common">,
|
||||
HelpText<"Do not assign space to common symbols">;
|
||||
|
||||
def no_demangle: F<"no-demangle">,
|
||||
HelpText<"Do not demangle symbol names">;
|
||||
|
||||
def no_dynamic_linker: F<"no-dynamic-linker">,
|
||||
HelpText<"Inhibit output of .interp section">;
|
||||
|
||||
def no_export_dynamic: F<"no-export-dynamic">;
|
||||
def no_fatal_warnings: F<"no-fatal-warnings">;
|
||||
|
||||
def no_gc_sections: F<"no-gc-sections">,
|
||||
HelpText<"Disable garbage collection of unused sections">;
|
||||
|
||||
def no_gnu_unique: F<"no-gnu-unique">,
|
||||
HelpText<"Disable STB_GNU_UNIQUE symbol binding">;
|
||||
|
||||
def no_threads: F<"no-threads">,
|
||||
HelpText<"Do not run the linker multi-threaded">;
|
||||
|
||||
def no_whole_archive: F<"no-whole-archive">,
|
||||
HelpText<"Restores the default behavior of loading archive members">;
|
||||
|
||||
def noinhibit_exec: F<"noinhibit-exec">,
|
||||
HelpText<"Retain the executable output file whenever it is still usable">;
|
||||
|
||||
def nopie: F<"nopie">, HelpText<"Do not create a position independent executable">;
|
||||
|
||||
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 no_undefined_version: F<"no-undefined-version">,
|
||||
HelpText<"Report version scripts that refer undefined symbols">;
|
||||
|
||||
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">;
|
||||
|
||||
def pie: F<"pie">, HelpText<"Create a position independent executable">;
|
||||
|
||||
def print_gc_sections: F<"print-gc-sections">,
|
||||
HelpText<"List removed unused sections">;
|
||||
|
||||
def print_map: F<"print-map">,
|
||||
HelpText<"Print a link map to the standard output">;
|
||||
|
||||
def reproduce: S<"reproduce">,
|
||||
HelpText<"Dump linker invocation and input files for debugging">;
|
||||
|
||||
def rpath: S<"rpath">, HelpText<"Add a DT_RUNPATH to the output">;
|
||||
|
||||
def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">;
|
||||
|
||||
def retain_symbols_file: J<"retain-symbols-file=">, MetaVarName<"<file>">,
|
||||
HelpText<"Retain only the symbols listed in the file">;
|
||||
|
||||
def script: S<"script">, HelpText<"Read linker script">;
|
||||
|
||||
def section_start: S<"section-start">, MetaVarName<"<address>">,
|
||||
HelpText<"Set address of section">;
|
||||
|
||||
def shared: F<"shared">, HelpText<"Build a shared object">;
|
||||
|
||||
def soname: J<"soname=">, HelpText<"Set DT_SONAME">;
|
||||
|
||||
def sort_section: S<"sort-section">, HelpText<"Specifies sections sorting rule when linkerscript is used">;
|
||||
|
||||
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">;
|
||||
|
||||
def symbol_ordering_file: S<"symbol-ordering-file">,
|
||||
HelpText<"Layout sections in the order specified by symbol file">;
|
||||
|
||||
def sysroot: J<"sysroot=">, HelpText<"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">;
|
||||
|
||||
def target2: J<"target2=">, MetaVarName<"<type>">, HelpText<"Interpret R_ARM_TARGET2 as <type>, where <type> is one of rel, abs, or got-rel">;
|
||||
|
||||
def threads: F<"threads">, HelpText<"Run the linker multi-threaded">;
|
||||
|
||||
def trace: F<"trace">, HelpText<"Print the names of the input files">;
|
||||
|
||||
def trace_symbol : S<"trace-symbol">, HelpText<"Trace references to symbols">;
|
||||
|
||||
def undefined: S<"undefined">,
|
||||
HelpText<"Force undefined symbol during linking">;
|
||||
|
||||
def unresolved_symbols: J<"unresolved-symbols=">,
|
||||
HelpText<"Determine how to handle unresolved symbols">;
|
||||
|
||||
def rsp_quoting: J<"rsp-quoting=">,
|
||||
HelpText<"Quoting style for response files. Values supported: windows|posix">;
|
||||
|
||||
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">;
|
||||
|
||||
def version_script: S<"version-script">,
|
||||
HelpText<"Read a version script">;
|
||||
|
||||
def warn_common: F<"warn-common">,
|
||||
HelpText<"Warn about duplicate common symbols">;
|
||||
|
||||
def warn_unresolved_symbols: F<"warn-unresolved-symbols">,
|
||||
HelpText<"Report unresolved symbols as warnings">;
|
||||
|
||||
def whole_archive: F<"whole-archive">,
|
||||
HelpText<"Force load of all members in a static library">;
|
||||
|
||||
def wrap: S<"wrap">, MetaVarName<"<symbol>">,
|
||||
HelpText<"Use wrapper functions for symbol">;
|
||||
|
||||
def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"<option>">,
|
||||
HelpText<"Linker option extensions">;
|
||||
|
||||
// Aliases
|
||||
def alias_auxiliary: Separate<["-"], "f">, Alias<auxiliary>;
|
||||
def alias_Bdynamic_call_shared: F<"call_shared">, Alias<Bdynamic>;
|
||||
def alias_Bdynamic_dy: F<"dy">, Alias<Bdynamic>;
|
||||
def alias_Bstatic_dn: F<"dn">, Alias<Bstatic>;
|
||||
def alias_Bstatic_non_shared: F<"non_shared">, Alias<Bstatic>;
|
||||
def alias_Bstatic_static: F<"static">, Alias<Bstatic>;
|
||||
def alias_L__library_path: J<"library-path=">, Alias<L>;
|
||||
def alias_define_common_d: Flag<["-"], "d">, Alias<define_common>;
|
||||
def alias_define_common_dc: F<"dc">, Alias<define_common>;
|
||||
def alias_define_common_dp: F<"dp">, Alias<define_common>;
|
||||
def alias_defsym: S<"defsym">, Alias<defsym>;
|
||||
def alias_discard_all_x: Flag<["-"], "x">, Alias<discard_all>;
|
||||
def alias_discard_locals_X: Flag<["-"], "X">, Alias<discard_locals>;
|
||||
def alias_dynamic_list: J<"dynamic-list=">, Alias<dynamic_list>;
|
||||
def alias_emit_relocs: Flag<["-"], "q">, Alias<emit_relocs>;
|
||||
def alias_entry_e: JoinedOrSeparate<["-"], "e">, Alias<entry>;
|
||||
def alias_entry_entry: J<"entry=">, Alias<entry>;
|
||||
def alias_error_limit: J<"error-limit=">, Alias<error_limit>;
|
||||
def alias_exclude_libs: J<"exclude-libs=">, Alias<exclude_libs>;
|
||||
def alias_export_dynamic_E: Flag<["-"], "E">, Alias<export_dynamic>;
|
||||
def alias_export_dynamic_symbol: J<"export-dynamic-symbol=">,
|
||||
Alias<export_dynamic_symbol>;
|
||||
def alias_filter: Separate<["-"], "F">, Alias<filter>;
|
||||
def alias_fini_fini: J<"fini=">, Alias<fini>;
|
||||
def alias_format_b: S<"b">, Alias<format>;
|
||||
def alias_hash_style_hash_style: J<"hash-style=">, Alias<hash_style>;
|
||||
def alias_init_init: J<"init=">, Alias<init>;
|
||||
def alias_l__library: J<"library=">, Alias<l>;
|
||||
def alias_Map_eq: J<"Map=">, Alias<Map>;
|
||||
def alias_omagic: Flag<["-"], "N">, Alias<omagic>;
|
||||
def alias_o_output: Joined<["--"], "output=">, Alias<o>;
|
||||
def alias_o_output2 : Separate<["--"], "output">, Alias<o>;
|
||||
def alias_pie_pic_executable: F<"pic-executable">, Alias<pie>;
|
||||
def alias_print_map_M: Flag<["-"], "M">, Alias<print_map>;
|
||||
def alias_relocatable_r: Flag<["-"], "r">, Alias<relocatable>;
|
||||
def alias_reproduce_eq: J<"reproduce=">, Alias<reproduce>;
|
||||
def alias_retain_symbols_file: S<"retain-symbols-file">, Alias<retain_symbols_file>;
|
||||
def alias_rpath_R: JoinedOrSeparate<["-"], "R">, Alias<rpath>;
|
||||
def alias_rpath_rpath: J<"rpath=">, Alias<rpath>;
|
||||
def alias_script_T: JoinedOrSeparate<["-"], "T">, Alias<script>;
|
||||
def alias_shared_Bshareable: F<"Bshareable">, Alias<shared>;
|
||||
def alias_soname_h: JoinedOrSeparate<["-"], "h">, Alias<soname>;
|
||||
def alias_soname_soname: S<"soname">, Alias<soname>;
|
||||
def alias_sort_section: J<"sort-section=">, Alias<sort_section>;
|
||||
def alias_script: J<"script=">, Alias<script>;
|
||||
def alias_strip_all: Flag<["-"], "s">, Alias<strip_all>;
|
||||
def alias_strip_debug_S: Flag<["-"], "S">, Alias<strip_debug>;
|
||||
def alias_Tbss: J<"Tbss=">, Alias<Tbss>;
|
||||
def alias_Tdata: J<"Tdata=">, Alias<Tdata>;
|
||||
def alias_trace: Flag<["-"], "t">, Alias<trace>;
|
||||
def trace_trace_symbol_eq : J<"trace-symbol=">, Alias<trace_symbol>;
|
||||
def alias_trace_symbol_y : JoinedOrSeparate<["-"], "y">, Alias<trace_symbol>;
|
||||
def alias_Ttext: J<"Ttext=">, Alias<Ttext>;
|
||||
def alias_Ttext_segment: S<"Ttext-segment">, Alias<Ttext>;
|
||||
def alias_Ttext_segment_eq: J<"Ttext-segment=">, Alias<Ttext>;
|
||||
def alias_undefined_eq: J<"undefined=">, Alias<undefined>;
|
||||
def alias_undefined_u: JoinedOrSeparate<["-"], "u">, Alias<undefined>;
|
||||
def alias_version_script_eq: J<"version-script=">, Alias<version_script>;
|
||||
def alias_version_V: Flag<["-"], "V">, Alias<version>;
|
||||
def alias_wrap_wrap: J<"wrap=">, Alias<wrap>;
|
||||
|
||||
// Our symbol resolution algorithm handles symbols in archive files differently
|
||||
// than traditional linkers, so we don't need --start-group and --end-group.
|
||||
// These options are recongized for compatibility but ignored.
|
||||
def end_group: F<"end-group">;
|
||||
def end_group_paren: Flag<["-"], ")">;
|
||||
def start_group: F<"start-group">;
|
||||
def start_group_paren: Flag<["-"], "(">;
|
||||
|
||||
// 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_newpm_passes: J<"lto-newpm-passes=">,
|
||||
HelpText<"Passes to run during LTO">;
|
||||
def lto_partitions: J<"lto-partitions=">,
|
||||
HelpText<"Number of LTO codegen partitions">;
|
||||
def disable_verify: F<"disable-verify">;
|
||||
def mllvm: S<"mllvm">;
|
||||
def opt_remarks_filename: Separate<["--"], "opt-remarks-filename">,
|
||||
HelpText<"YAML output file for optimization remarks">;
|
||||
def opt_remarks_with_hotness: Flag<["--"], "opt-remarks-with-hotness">,
|
||||
HelpText<"Include hotness informations in the optimization remarks file">;
|
||||
def save_temps: F<"save-temps">;
|
||||
def thinlto_cache_dir: J<"thinlto-cache-dir=">,
|
||||
HelpText<"Path to ThinLTO cached object file directory">;
|
||||
def thinlto_cache_policy: S<"thinlto-cache-policy">,
|
||||
HelpText<"Pruning policy for the ThinLTO cache">;
|
||||
def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">;
|
||||
|
||||
// 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.
|
||||
def plugin: S<"plugin">;
|
||||
def plugin_eq: J<"plugin=">;
|
||||
def plugin_opt: S<"plugin-opt">;
|
||||
def plugin_opt_eq: J<"plugin-opt=">;
|
||||
|
||||
// Options listed below are silently ignored for now for compatibility.
|
||||
def allow_shlib_undefined: F<"allow-shlib-undefined">;
|
||||
def cref: Flag<["--"], "cref">;
|
||||
def detect_odr_violations: F<"detect-odr-violations">;
|
||||
def g: Flag<["-"], "g">;
|
||||
def no_add_needed: F<"no-add-needed">;
|
||||
def no_allow_shlib_undefined: F<"no-allow-shlib-undefined">;
|
||||
def no_copy_dt_needed_entries: F<"no-copy-dt-needed-entries">,
|
||||
Alias<no_add_needed>;
|
||||
def no_keep_memory: F<"no-keep-memory">;
|
||||
def no_mmap_output_file: F<"no-mmap-output-file">;
|
||||
def no_warn_common: F<"no-warn-common">;
|
||||
def no_warn_mismatch: F<"no-warn-mismatch">;
|
||||
def rpath_link: S<"rpath-link">;
|
||||
def rpath_link_eq: J<"rpath-link=">;
|
||||
def sort_common: F<"sort-common">;
|
||||
def stats: F<"stats">;
|
||||
def warn_execstack: F<"warn-execstack">;
|
||||
def warn_shared_textrel: F<"warn-shared-textrel">;
|
||||
def EB : F<"EB">;
|
||||
def EL : F<"EL">;
|
||||
def G: JoinedOrSeparate<["-"], "G">;
|
||||
def Qy : F<"Qy">;
|
||||
|
||||
277
deps/lld/ELF/OutputSections.cpp
vendored
Normal file
277
deps/lld/ELF/OutputSections.cpp
vendored
Normal file
@ -0,0 +1,277 @@
|
||||
//===- OutputSections.cpp -------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "OutputSections.h"
|
||||
#include "Config.h"
|
||||
#include "LinkerScript.h"
|
||||
#include "Memory.h"
|
||||
#include "Strings.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "Threads.h"
|
||||
#include "llvm/BinaryFormat/Dwarf.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::First;
|
||||
OutputSection *Out::Opd;
|
||||
uint8_t *Out::OpdBuf;
|
||||
PhdrEntry *Out::TlsPhdr;
|
||||
OutputSection *Out::DebugInfo;
|
||||
OutputSection *Out::ElfHeader;
|
||||
OutputSection *Out::ProgramHeaders;
|
||||
OutputSection *Out::PreinitArray;
|
||||
OutputSection *Out::InitArray;
|
||||
OutputSection *Out::FiniArray;
|
||||
|
||||
std::vector<OutputSection *> elf::OutputSections;
|
||||
std::vector<OutputSectionCommand *> elf::OutputSectionCommands;
|
||||
|
||||
uint32_t OutputSection::getPhdrFlags() const {
|
||||
uint32_t 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)
|
||||
: SectionBase(Output, Name, Flags, /*Entsize*/ 0, /*Alignment*/ 1, Type,
|
||||
/*Info*/ 0,
|
||||
/*Link*/ 0),
|
||||
SectionIndex(INT_MAX) {}
|
||||
|
||||
static uint64_t updateOffset(uint64_t Off, InputSection *S) {
|
||||
Off = alignTo(Off, S->Alignment);
|
||||
S->OutSecOff = Off;
|
||||
return Off + S->getSize();
|
||||
}
|
||||
|
||||
void OutputSection::addSection(InputSection *S) {
|
||||
assert(S->Live);
|
||||
Sections.push_back(S);
|
||||
S->Parent = this;
|
||||
this->updateAlignment(S->Alignment);
|
||||
|
||||
// The actual offsets will be computed by assignAddresses. For now, use
|
||||
// crude approximation so that it is at least easy for other code to know the
|
||||
// section order. It is also used to calculate the output section size early
|
||||
// for compressed debug sections.
|
||||
this->Size = updateOffset(Size, S);
|
||||
|
||||
// If this section contains a table of fixed-size entries, sh_entsize
|
||||
// holds the element size. Consequently, if this contains two or more
|
||||
// input sections, all of them must have the same sh_entsize. However,
|
||||
// you can put different types of input sections into one output
|
||||
// sectin by using linker scripts. I don't know what to do here.
|
||||
// Probably we sholuld handle that as an error. But for now we just
|
||||
// pick the largest sh_entsize.
|
||||
this->Entsize = std::max(this->Entsize, S->Entsize);
|
||||
}
|
||||
|
||||
static SectionKey createKey(InputSectionBase *C, StringRef OutsecName) {
|
||||
// The ELF spec just says
|
||||
// ----------------------------------------------------------------
|
||||
// In the first phase, input sections that match in name, type and
|
||||
// attribute flags should be concatenated into single sections.
|
||||
// ----------------------------------------------------------------
|
||||
//
|
||||
// However, it is clear that at least some flags have to be ignored for
|
||||
// section merging. At the very least SHF_GROUP and SHF_COMPRESSED have to be
|
||||
// ignored. We should not have two output .text sections just because one was
|
||||
// in a group and another was not for example.
|
||||
//
|
||||
// It also seems that that wording was a late addition and didn't get the
|
||||
// necessary scrutiny.
|
||||
//
|
||||
// Merging sections with different flags is expected by some users. One
|
||||
// reason is that if one file has
|
||||
//
|
||||
// int *const bar __attribute__((section(".foo"))) = (int *)0;
|
||||
//
|
||||
// gcc with -fPIC will produce a read only .foo section. But if another
|
||||
// file has
|
||||
//
|
||||
// int zed;
|
||||
// int *const bar __attribute__((section(".foo"))) = (int *)&zed;
|
||||
//
|
||||
// gcc with -fPIC will produce a read write section.
|
||||
//
|
||||
// Last but not least, when using linker script the merge rules are forced by
|
||||
// the script. Unfortunately, linker scripts are name based. This means that
|
||||
// expressions like *(.foo*) can refer to multiple input sections with
|
||||
// different flags. We cannot put them in different output sections or we
|
||||
// would produce wrong results for
|
||||
//
|
||||
// start = .; *(.foo.*) end = .; *(.bar)
|
||||
//
|
||||
// and a mapping of .foo1 and .bar1 to one section and .foo2 and .bar2 to
|
||||
// another. The problem is that there is no way to layout those output
|
||||
// sections such that the .foo sections are the only thing between the start
|
||||
// and end symbols.
|
||||
//
|
||||
// Given the above issues, we instead merge sections by name and error on
|
||||
// incompatible types and flags.
|
||||
|
||||
uint32_t Alignment = 0;
|
||||
uint64_t Flags = 0;
|
||||
if (Config->Relocatable && (C->Flags & SHF_MERGE)) {
|
||||
Alignment = std::max<uint64_t>(C->Alignment, C->Entsize);
|
||||
Flags = C->Flags & (SHF_MERGE | SHF_STRINGS);
|
||||
}
|
||||
|
||||
return SectionKey{OutsecName, Flags, Alignment};
|
||||
}
|
||||
|
||||
OutputSectionFactory::OutputSectionFactory() {}
|
||||
|
||||
static uint64_t getIncompatibleFlags(uint64_t Flags) {
|
||||
return Flags & (SHF_ALLOC | SHF_TLS);
|
||||
}
|
||||
|
||||
// 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 elf::reportDiscarded(InputSectionBase *IS) {
|
||||
if (!Config->PrintGcSections)
|
||||
return;
|
||||
message("removing unused section from '" + IS->Name + "' in file '" +
|
||||
IS->File->getName() + "'");
|
||||
}
|
||||
|
||||
void OutputSectionFactory::addInputSec(InputSectionBase *IS,
|
||||
StringRef OutsecName) {
|
||||
// Sections with the SHT_GROUP attribute reach here only when the - r option
|
||||
// is given. Such sections define "section groups", and InputFiles.cpp has
|
||||
// dedup'ed section groups by their signatures. For the -r, we want to pass
|
||||
// through all SHT_GROUP sections without merging them because merging them
|
||||
// creates broken section contents.
|
||||
if (IS->Type == SHT_GROUP) {
|
||||
OutputSection *Out = nullptr;
|
||||
addInputSec(IS, OutsecName, Out);
|
||||
return;
|
||||
}
|
||||
|
||||
// Imagine .zed : { *(.foo) *(.bar) } script. Both foo and bar may have
|
||||
// relocation sections .rela.foo and .rela.bar for example. Most tools do
|
||||
// not allow multiple REL[A] sections for output section. Hence we
|
||||
// should combine these relocation sections into single output.
|
||||
// We skip synthetic sections because it can be .rela.dyn/.rela.plt or any
|
||||
// other REL[A] sections created by linker itself.
|
||||
if (!isa<SyntheticSection>(IS) &&
|
||||
(IS->Type == SHT_REL || IS->Type == SHT_RELA)) {
|
||||
auto *Sec = cast<InputSection>(IS);
|
||||
OutputSection *Out = Sec->getRelocatedSection()->getOutputSection();
|
||||
addInputSec(IS, OutsecName, Out->RelocationSection);
|
||||
return;
|
||||
}
|
||||
|
||||
SectionKey Key = createKey(IS, OutsecName);
|
||||
OutputSection *&Sec = Map[Key];
|
||||
addInputSec(IS, OutsecName, Sec);
|
||||
}
|
||||
|
||||
void OutputSectionFactory::addInputSec(InputSectionBase *IS,
|
||||
StringRef OutsecName,
|
||||
OutputSection *&Sec) {
|
||||
if (!IS->Live) {
|
||||
reportDiscarded(IS);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Sec) {
|
||||
if (getIncompatibleFlags(Sec->Flags) != getIncompatibleFlags(IS->Flags))
|
||||
error("incompatible section flags for " + Sec->Name + "\n>>> " +
|
||||
toString(IS) + ": 0x" + utohexstr(IS->Flags) +
|
||||
"\n>>> output section " + Sec->Name + ": 0x" +
|
||||
utohexstr(Sec->Flags));
|
||||
if (Sec->Type != IS->Type) {
|
||||
if (canMergeToProgbits(Sec->Type) && canMergeToProgbits(IS->Type))
|
||||
Sec->Type = SHT_PROGBITS;
|
||||
else
|
||||
error("section type mismatch for " + IS->Name + "\n>>> " +
|
||||
toString(IS) + ": " +
|
||||
getELFSectionTypeName(Config->EMachine, IS->Type) +
|
||||
"\n>>> output section " + Sec->Name + ": " +
|
||||
getELFSectionTypeName(Config->EMachine, Sec->Type));
|
||||
}
|
||||
Sec->Flags |= IS->Flags;
|
||||
} else {
|
||||
Sec = make<OutputSection>(OutsecName, IS->Type, IS->Flags);
|
||||
OutputSections.push_back(Sec);
|
||||
}
|
||||
|
||||
Sec->addSection(cast<InputSection>(IS));
|
||||
}
|
||||
|
||||
OutputSectionFactory::~OutputSectionFactory() {}
|
||||
|
||||
SectionKey DenseMapInfo<SectionKey>::getEmptyKey() {
|
||||
return SectionKey{DenseMapInfo<StringRef>::getEmptyKey(), 0, 0};
|
||||
}
|
||||
|
||||
SectionKey DenseMapInfo<SectionKey>::getTombstoneKey() {
|
||||
return SectionKey{DenseMapInfo<StringRef>::getTombstoneKey(), 0, 0};
|
||||
}
|
||||
|
||||
unsigned DenseMapInfo<SectionKey>::getHashValue(const SectionKey &Val) {
|
||||
return hash_combine(Val.Name, Val.Flags, Val.Alignment);
|
||||
}
|
||||
|
||||
bool DenseMapInfo<SectionKey>::isEqual(const SectionKey &LHS,
|
||||
const SectionKey &RHS) {
|
||||
return DenseMapInfo<StringRef>::isEqual(LHS.Name, RHS.Name) &&
|
||||
LHS.Flags == RHS.Flags && LHS.Alignment == RHS.Alignment;
|
||||
}
|
||||
|
||||
uint64_t elf::getHeaderSize() {
|
||||
if (Config->OFormatBinary)
|
||||
return 0;
|
||||
return Out::ElfHeader->Size + Out::ProgramHeaders->Size;
|
||||
}
|
||||
|
||||
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);
|
||||
153
deps/lld/ELF/OutputSections.h
vendored
Normal file
153
deps/lld/ELF/OutputSections.h
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
//===- OutputSections.h -----------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_OUTPUT_SECTIONS_H
|
||||
#define LLD_ELF_OUTPUT_SECTIONS_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "InputSection.h"
|
||||
#include "Relocations.h"
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/MC/StringTableBuilder.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
struct PhdrEntry;
|
||||
class SymbolBody;
|
||||
struct EhSectionPiece;
|
||||
class EhInputSection;
|
||||
class InputSection;
|
||||
class InputSectionBase;
|
||||
class MergeInputSection;
|
||||
class OutputSection;
|
||||
template <class ELFT> class ObjectFile;
|
||||
template <class ELFT> class SharedFile;
|
||||
class SharedSymbol;
|
||||
class DefinedRegular;
|
||||
|
||||
// This represents a section in an output file.
|
||||
// It is composed of multiple InputSections.
|
||||
// The writer creates multiple OutputSections and assign them unique,
|
||||
// non-overlapping file offsets and VAs.
|
||||
class OutputSection final : public SectionBase {
|
||||
public:
|
||||
OutputSection(StringRef Name, uint32_t Type, uint64_t Flags);
|
||||
|
||||
static bool classof(const SectionBase *S) {
|
||||
return S->kind() == SectionBase::Output;
|
||||
}
|
||||
|
||||
uint64_t getLMA() const { return Addr + LMAOffset; }
|
||||
template <typename ELFT> void writeHeaderTo(typename ELFT::Shdr *SHdr);
|
||||
|
||||
unsigned SectionIndex;
|
||||
unsigned SortRank;
|
||||
|
||||
uint32_t getPhdrFlags() const;
|
||||
|
||||
void updateAlignment(uint32_t Val) {
|
||||
if (Val > Alignment)
|
||||
Alignment = Val;
|
||||
}
|
||||
|
||||
// Pointer to the first section in PT_LOAD segment, which this section
|
||||
// also resides in. This field is used to correctly compute file offset
|
||||
// of a section. When two sections share the same load segment, difference
|
||||
// between their file offsets should be equal to difference between their
|
||||
// virtual addresses. To compute some section offset we use the following
|
||||
// formula: Off = Off_first + VA - VA_first.
|
||||
OutputSection *FirstInPtLoad = nullptr;
|
||||
|
||||
// Pointer to a relocation section for this section. Usually nullptr because
|
||||
// we consume relocations, but if --emit-relocs is specified (which is rare),
|
||||
// it may have a non-null value.
|
||||
OutputSection *RelocationSection = nullptr;
|
||||
|
||||
// The following fields correspond to Elf_Shdr members.
|
||||
uint64_t Size = 0;
|
||||
uint64_t Offset = 0;
|
||||
uint64_t LMAOffset = 0;
|
||||
uint64_t Addr = 0;
|
||||
uint32_t ShName = 0;
|
||||
|
||||
void addSection(InputSection *S);
|
||||
std::vector<InputSection *> Sections;
|
||||
|
||||
// Used for implementation of --compress-debug-sections option.
|
||||
std::vector<uint8_t> ZDebugHeader;
|
||||
llvm::SmallVector<char, 1> CompressedData;
|
||||
|
||||
// Location in the output buffer.
|
||||
uint8_t *Loc = nullptr;
|
||||
};
|
||||
|
||||
// All output sections that are handled by the linker specially are
|
||||
// globally accessible. Writer initializes them, so don't use them
|
||||
// until Writer is initialized.
|
||||
struct Out {
|
||||
static uint8_t First;
|
||||
static OutputSection *Opd;
|
||||
static uint8_t *OpdBuf;
|
||||
static PhdrEntry *TlsPhdr;
|
||||
static OutputSection *DebugInfo;
|
||||
static OutputSection *ElfHeader;
|
||||
static OutputSection *ProgramHeaders;
|
||||
static OutputSection *PreinitArray;
|
||||
static OutputSection *InitArray;
|
||||
static OutputSection *FiniArray;
|
||||
};
|
||||
|
||||
struct SectionKey {
|
||||
StringRef Name;
|
||||
uint64_t Flags;
|
||||
uint32_t Alignment;
|
||||
};
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
namespace llvm {
|
||||
template <> struct DenseMapInfo<lld::elf::SectionKey> {
|
||||
static lld::elf::SectionKey getEmptyKey();
|
||||
static lld::elf::SectionKey getTombstoneKey();
|
||||
static unsigned getHashValue(const lld::elf::SectionKey &Val);
|
||||
static bool isEqual(const lld::elf::SectionKey &LHS,
|
||||
const lld::elf::SectionKey &RHS);
|
||||
};
|
||||
} // namespace llvm
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
// This class knows how to create an output section for a given
|
||||
// input section. Output section type is determined by various
|
||||
// factors, including input section's sh_flags, sh_type and
|
||||
// linker scripts.
|
||||
class OutputSectionFactory {
|
||||
public:
|
||||
OutputSectionFactory();
|
||||
~OutputSectionFactory();
|
||||
|
||||
void addInputSec(InputSectionBase *IS, StringRef OutsecName);
|
||||
void addInputSec(InputSectionBase *IS, StringRef OutsecName,
|
||||
OutputSection *&Sec);
|
||||
|
||||
private:
|
||||
llvm::SmallDenseMap<SectionKey, OutputSection *> Map;
|
||||
};
|
||||
|
||||
uint64_t getHeaderSize();
|
||||
void reportDiscarded(InputSectionBase *IS);
|
||||
|
||||
extern std::vector<OutputSection *> OutputSections;
|
||||
extern std::vector<OutputSectionCommand *> OutputSectionCommands;
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
1
deps/lld/ELF/README.md
vendored
Normal file
1
deps/lld/ELF/README.md
vendored
Normal file
@ -0,0 +1 @@
|
||||
See docs/NewLLD.rst
|
||||
1144
deps/lld/ELF/Relocations.cpp
vendored
Normal file
1144
deps/lld/ELF/Relocations.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
185
deps/lld/ELF/Relocations.h
vendored
Normal file
185
deps/lld/ELF/Relocations.h
vendored
Normal file
@ -0,0 +1,185 @@
|
||||
//===- Relocations.h -------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_RELOCATIONS_H
|
||||
#define LLD_ELF_RELOCATIONS_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
class SymbolBody;
|
||||
class InputSection;
|
||||
class InputSectionBase;
|
||||
class OutputSection;
|
||||
struct OutputSectionCommand;
|
||||
|
||||
// List of target-independent relocation types. Relocations read
|
||||
// from files are converted to these types so that the main code
|
||||
// doesn't have to know about architecture-specific details.
|
||||
enum RelExpr {
|
||||
R_ABS,
|
||||
R_ARM_SBREL,
|
||||
R_GOT,
|
||||
R_GOTONLY_PC,
|
||||
R_GOTONLY_PC_FROM_END,
|
||||
R_GOTREL,
|
||||
R_GOTREL_FROM_END,
|
||||
R_GOT_FROM_END,
|
||||
R_GOT_OFF,
|
||||
R_GOT_PAGE_PC,
|
||||
R_GOT_PC,
|
||||
R_HINT,
|
||||
R_MIPS_GOTREL,
|
||||
R_MIPS_GOT_GP,
|
||||
R_MIPS_GOT_GP_PC,
|
||||
R_MIPS_GOT_LOCAL_PAGE,
|
||||
R_MIPS_GOT_OFF,
|
||||
R_MIPS_GOT_OFF32,
|
||||
R_MIPS_TLSGD,
|
||||
R_MIPS_TLSLD,
|
||||
R_NEG_TLS,
|
||||
R_NONE,
|
||||
R_PAGE_PC,
|
||||
R_PC,
|
||||
R_PLT,
|
||||
R_PLT_PAGE_PC,
|
||||
R_PLT_PC,
|
||||
R_PPC_OPD,
|
||||
R_PPC_PLT_OPD,
|
||||
R_PPC_TOC,
|
||||
R_RELAX_GOT_PC,
|
||||
R_RELAX_GOT_PC_NOPIC,
|
||||
R_RELAX_TLS_GD_TO_IE,
|
||||
R_RELAX_TLS_GD_TO_IE_ABS,
|
||||
R_RELAX_TLS_GD_TO_IE_END,
|
||||
R_RELAX_TLS_GD_TO_IE_PAGE_PC,
|
||||
R_RELAX_TLS_GD_TO_LE,
|
||||
R_RELAX_TLS_GD_TO_LE_NEG,
|
||||
R_RELAX_TLS_IE_TO_LE,
|
||||
R_RELAX_TLS_LD_TO_LE,
|
||||
R_SIZE,
|
||||
R_TLS,
|
||||
R_TLSDESC,
|
||||
R_TLSDESC_CALL,
|
||||
R_TLSDESC_PAGE,
|
||||
R_TLSGD,
|
||||
R_TLSGD_PC,
|
||||
R_TLSLD,
|
||||
R_TLSLD_PC,
|
||||
};
|
||||
|
||||
// Build a bitmask with one bit set for each RelExpr.
|
||||
//
|
||||
// Constexpr function arguments can't be used in static asserts, so we
|
||||
// use template arguments to build the mask.
|
||||
// But function template partial specializations don't exist (needed
|
||||
// for base case of the recursion), so we need a dummy struct.
|
||||
template <RelExpr... Exprs> struct RelExprMaskBuilder {
|
||||
static inline uint64_t build() { return 0; }
|
||||
};
|
||||
|
||||
// Specialization for recursive case.
|
||||
template <RelExpr Head, RelExpr... Tail>
|
||||
struct RelExprMaskBuilder<Head, Tail...> {
|
||||
static inline uint64_t build() {
|
||||
static_assert(0 <= Head && Head < 64,
|
||||
"RelExpr is too large for 64-bit mask!");
|
||||
return (uint64_t(1) << Head) | RelExprMaskBuilder<Tail...>::build();
|
||||
}
|
||||
};
|
||||
|
||||
// Return true if `Expr` is one of `Exprs`.
|
||||
// There are fewer than 64 RelExpr's, so we can represent any set of
|
||||
// RelExpr's as a constant bit mask and test for membership with a
|
||||
// couple cheap bitwise operations.
|
||||
template <RelExpr... Exprs> bool isRelExprOneOf(RelExpr Expr) {
|
||||
assert(0 <= Expr && (int)Expr < 64 &&
|
||||
"RelExpr is too large for 64-bit mask!");
|
||||
return (uint64_t(1) << Expr) & RelExprMaskBuilder<Exprs...>::build();
|
||||
}
|
||||
|
||||
// Architecture-neutral representation of relocation.
|
||||
struct Relocation {
|
||||
RelExpr Expr;
|
||||
uint32_t Type;
|
||||
uint64_t Offset;
|
||||
int64_t Addend;
|
||||
SymbolBody *Sym;
|
||||
};
|
||||
|
||||
template <class ELFT> void scanRelocations(InputSectionBase &);
|
||||
|
||||
class ThunkSection;
|
||||
class Thunk;
|
||||
|
||||
class ThunkCreator {
|
||||
public:
|
||||
// Return true if Thunks have been added to OutputSections
|
||||
bool createThunks(ArrayRef<OutputSectionCommand *> OutputSections);
|
||||
|
||||
// The number of completed passes of createThunks this permits us
|
||||
// to do one time initialization on Pass 0 and put a limit on the
|
||||
// number of times it can be called to prevent infinite loops.
|
||||
uint32_t Pass = 0;
|
||||
|
||||
private:
|
||||
void mergeThunks();
|
||||
ThunkSection *getOSThunkSec(OutputSectionCommand *Cmd,
|
||||
std::vector<InputSection *> *ISR);
|
||||
ThunkSection *getISThunkSec(InputSection *IS, OutputSection *OS);
|
||||
void forEachExecInputSection(
|
||||
ArrayRef<OutputSectionCommand *> OutputSections,
|
||||
std::function<void(OutputSectionCommand *, std::vector<InputSection *> *,
|
||||
InputSection *)>
|
||||
Fn);
|
||||
std::pair<Thunk *, bool> getThunk(SymbolBody &Body, uint32_t Type);
|
||||
ThunkSection *addThunkSection(OutputSection *OS,
|
||||
std::vector<InputSection *> *, uint64_t Off);
|
||||
// Record all the available Thunks for a Symbol
|
||||
llvm::DenseMap<SymbolBody *, std::vector<Thunk *>> ThunkedSymbols;
|
||||
|
||||
// Find a Thunk from the Thunks symbol definition, we can use this to find
|
||||
// the Thunk from a relocation to the Thunks symbol definition.
|
||||
llvm::DenseMap<SymbolBody *, Thunk *> Thunks;
|
||||
|
||||
// Track InputSections that have an inline ThunkSection placed in front
|
||||
// an inline ThunkSection may have control fall through to the section below
|
||||
// so we need to make sure that there is only one of them.
|
||||
// The Mips LA25 Thunk is an example of an inline ThunkSection.
|
||||
llvm::DenseMap<InputSection *, ThunkSection *> ThunkedSections;
|
||||
|
||||
// All the ThunkSections that we have created, organised by OutputSection
|
||||
// will contain a mix of ThunkSections that have been created this pass, and
|
||||
// ThunkSections that have been merged into the OutputSection on previous
|
||||
// passes
|
||||
std::map<std::vector<InputSection *> *, std::vector<ThunkSection *>>
|
||||
ThunkSections;
|
||||
|
||||
// The ThunkSection for this vector of InputSections
|
||||
ThunkSection *CurTS;
|
||||
};
|
||||
|
||||
// Return a int64_t to make sure we get the sign extension out of the way as
|
||||
// early as possible.
|
||||
template <class ELFT>
|
||||
static inline int64_t getAddend(const typename ELFT::Rel &Rel) {
|
||||
return 0;
|
||||
}
|
||||
template <class ELFT>
|
||||
static inline int64_t getAddend(const typename ELFT::Rela &Rel) {
|
||||
return Rel.r_addend;
|
||||
}
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
285
deps/lld/ELF/ScriptLexer.cpp
vendored
Normal file
285
deps/lld/ELF/ScriptLexer.cpp
vendored
Normal file
@ -0,0 +1,285 @@
|
||||
//===- ScriptLexer.cpp ----------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines a lexer for the linker script.
|
||||
//
|
||||
// The linker script's grammar is not complex but ambiguous due to the
|
||||
// lack of the formal specification of the language. What we are trying to
|
||||
// do in this and other files in LLD is to make a "reasonable" linker
|
||||
// script processor.
|
||||
//
|
||||
// Among simplicity, compatibility and efficiency, we put the most
|
||||
// emphasis on simplicity when we wrote this lexer. Compatibility with the
|
||||
// GNU linkers is important, but we did not try to clone every tiny corner
|
||||
// case of their lexers, as even ld.bfd and ld.gold are subtly different
|
||||
// in various corner cases. We do not care much about efficiency because
|
||||
// the time spent in parsing linker scripts is usually negligible.
|
||||
//
|
||||
// Our grammar of the linker script is LL(2), meaning that it needs at
|
||||
// most two-token lookahead to parse. The only place we need two-token
|
||||
// lookahead is labels in version scripts, where we need to parse "local :"
|
||||
// as if "local:".
|
||||
//
|
||||
// Overall, this lexer works fine for most linker scripts. There might
|
||||
// be room for improving compatibility, but that's probably not at the
|
||||
// top of our todo list.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ScriptLexer.h"
|
||||
#include "Error.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
// Returns a whole line containing the current token.
|
||||
StringRef ScriptLexer::getLine() {
|
||||
StringRef S = getCurrentMB().getBuffer();
|
||||
StringRef Tok = Tokens[Pos - 1];
|
||||
|
||||
size_t Pos = S.rfind('\n', Tok.data() - S.data());
|
||||
if (Pos != StringRef::npos)
|
||||
S = S.substr(Pos + 1);
|
||||
return S.substr(0, S.find_first_of("\r\n"));
|
||||
}
|
||||
|
||||
// Returns 1-based line number of the current token.
|
||||
size_t ScriptLexer::getLineNumber() {
|
||||
StringRef S = getCurrentMB().getBuffer();
|
||||
StringRef Tok = Tokens[Pos - 1];
|
||||
return S.substr(0, Tok.data() - S.data()).count('\n') + 1;
|
||||
}
|
||||
|
||||
// Returns 0-based column number of the current token.
|
||||
size_t ScriptLexer::getColumnNumber() {
|
||||
StringRef Tok = Tokens[Pos - 1];
|
||||
return Tok.data() - getLine().data();
|
||||
}
|
||||
|
||||
std::string ScriptLexer::getCurrentLocation() {
|
||||
std::string Filename = getCurrentMB().getBufferIdentifier();
|
||||
if (!Pos)
|
||||
return Filename;
|
||||
return (Filename + ":" + Twine(getLineNumber())).str();
|
||||
}
|
||||
|
||||
ScriptLexer::ScriptLexer(MemoryBufferRef MB) { tokenize(MB); }
|
||||
|
||||
// We don't want to record cascading errors. Keep only the first one.
|
||||
void ScriptLexer::setError(const Twine &Msg) {
|
||||
if (Error)
|
||||
return;
|
||||
Error = true;
|
||||
|
||||
if (!Pos) {
|
||||
error(getCurrentLocation() + ": " + Msg);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string S = getCurrentLocation() + ": ";
|
||||
error(S + Msg);
|
||||
error(S + getLine());
|
||||
error(S + std::string(getColumnNumber(), ' ') + "^");
|
||||
}
|
||||
|
||||
// Split S into linker script tokens.
|
||||
void ScriptLexer::tokenize(MemoryBufferRef MB) {
|
||||
std::vector<StringRef> Vec;
|
||||
MBs.push_back(MB);
|
||||
StringRef S = MB.getBuffer();
|
||||
StringRef Begin = S;
|
||||
|
||||
for (;;) {
|
||||
S = skipSpace(S);
|
||||
if (S.empty())
|
||||
break;
|
||||
|
||||
// Quoted token. Note that double-quote characters are parts of a token
|
||||
// because, in a glob match context, only unquoted tokens are interpreted
|
||||
// as glob patterns. Double-quoted tokens are literal patterns in that
|
||||
// context.
|
||||
if (S.startswith("\"")) {
|
||||
size_t E = S.find("\"", 1);
|
||||
if (E == StringRef::npos) {
|
||||
StringRef Filename = MB.getBufferIdentifier();
|
||||
size_t Lineno = Begin.substr(0, S.data() - Begin.data()).count('\n');
|
||||
error(Filename + ":" + Twine(Lineno + 1) + ": unclosed quote");
|
||||
return;
|
||||
}
|
||||
|
||||
Vec.push_back(S.take_front(E + 1));
|
||||
S = S.substr(E + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Unquoted token. This is more relaxed than tokens in C-like language,
|
||||
// so that you can write "file-name.cpp" as one bare token, for example.
|
||||
size_t Pos = S.find_first_not_of(
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789_.$/\\~=+[]*?-!<>^:");
|
||||
|
||||
// A character that cannot start a word (which is usually a
|
||||
// punctuation) forms a single character token.
|
||||
if (Pos == 0)
|
||||
Pos = 1;
|
||||
Vec.push_back(S.substr(0, Pos));
|
||||
S = S.substr(Pos);
|
||||
}
|
||||
|
||||
Tokens.insert(Tokens.begin() + Pos, Vec.begin(), Vec.end());
|
||||
}
|
||||
|
||||
// Skip leading whitespace characters or comments.
|
||||
StringRef ScriptLexer::skipSpace(StringRef S) {
|
||||
for (;;) {
|
||||
if (S.startswith("/*")) {
|
||||
size_t E = S.find("*/", 2);
|
||||
if (E == StringRef::npos) {
|
||||
error("unclosed comment in a linker script");
|
||||
return "";
|
||||
}
|
||||
S = S.substr(E + 2);
|
||||
continue;
|
||||
}
|
||||
if (S.startswith("#")) {
|
||||
size_t E = S.find('\n', 1);
|
||||
if (E == StringRef::npos)
|
||||
E = S.size() - 1;
|
||||
S = S.substr(E + 1);
|
||||
continue;
|
||||
}
|
||||
size_t Size = S.size();
|
||||
S = S.ltrim();
|
||||
if (S.size() == Size)
|
||||
return S;
|
||||
}
|
||||
}
|
||||
|
||||
// An erroneous token is handled as if it were the last token before EOF.
|
||||
bool ScriptLexer::atEOF() { return Error || Tokens.size() == Pos; }
|
||||
|
||||
// Split a given string as an expression.
|
||||
// This function returns "3", "*" and "5" for "3*5" for example.
|
||||
static std::vector<StringRef> tokenizeExpr(StringRef S) {
|
||||
StringRef Ops = "+-*/:"; // List of operators
|
||||
|
||||
// Quoted strings are literal strings, so we don't want to split it.
|
||||
if (S.startswith("\""))
|
||||
return {S};
|
||||
|
||||
// Split S with +-*/ as separators.
|
||||
std::vector<StringRef> Ret;
|
||||
while (!S.empty()) {
|
||||
size_t E = S.find_first_of(Ops);
|
||||
|
||||
// No need to split if there is no operator.
|
||||
if (E == StringRef::npos) {
|
||||
Ret.push_back(S);
|
||||
break;
|
||||
}
|
||||
|
||||
// Get a token before the opreator.
|
||||
if (E != 0)
|
||||
Ret.push_back(S.substr(0, E));
|
||||
|
||||
// Get the operator as a token.
|
||||
Ret.push_back(S.substr(E, 1));
|
||||
S = S.substr(E + 1);
|
||||
}
|
||||
return Ret;
|
||||
}
|
||||
|
||||
// In contexts where expressions are expected, the lexer should apply
|
||||
// different tokenization rules than the default one. By default,
|
||||
// arithmetic operator characters are regular characters, but in the
|
||||
// expression context, they should be independent tokens.
|
||||
//
|
||||
// For example, "foo*3" should be tokenized to "foo", "*" and "3" only
|
||||
// in the expression context.
|
||||
//
|
||||
// This function may split the current token into multiple tokens.
|
||||
void ScriptLexer::maybeSplitExpr() {
|
||||
if (!InExpr || Error || atEOF())
|
||||
return;
|
||||
|
||||
std::vector<StringRef> V = tokenizeExpr(Tokens[Pos]);
|
||||
if (V.size() == 1)
|
||||
return;
|
||||
Tokens.erase(Tokens.begin() + Pos);
|
||||
Tokens.insert(Tokens.begin() + Pos, V.begin(), V.end());
|
||||
}
|
||||
|
||||
StringRef ScriptLexer::next() {
|
||||
maybeSplitExpr();
|
||||
|
||||
if (Error)
|
||||
return "";
|
||||
if (atEOF()) {
|
||||
setError("unexpected EOF");
|
||||
return "";
|
||||
}
|
||||
return Tokens[Pos++];
|
||||
}
|
||||
|
||||
StringRef ScriptLexer::peek() {
|
||||
StringRef Tok = next();
|
||||
if (Error)
|
||||
return "";
|
||||
Pos = Pos - 1;
|
||||
return Tok;
|
||||
}
|
||||
|
||||
bool ScriptLexer::consume(StringRef Tok) {
|
||||
if (peek() == Tok) {
|
||||
skip();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Consumes Tok followed by ":". Space is allowed between Tok and ":".
|
||||
bool ScriptLexer::consumeLabel(StringRef Tok) {
|
||||
if (consume((Tok + ":").str()))
|
||||
return true;
|
||||
if (Tokens.size() >= Pos + 2 && Tokens[Pos] == Tok &&
|
||||
Tokens[Pos + 1] == ":") {
|
||||
Pos += 2;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScriptLexer::skip() { (void)next(); }
|
||||
|
||||
void ScriptLexer::expect(StringRef Expect) {
|
||||
if (Error)
|
||||
return;
|
||||
StringRef Tok = next();
|
||||
if (Tok != Expect)
|
||||
setError(Expect + " expected, but got " + Tok);
|
||||
}
|
||||
|
||||
// Returns true if S encloses T.
|
||||
static bool encloses(StringRef S, StringRef T) {
|
||||
return S.bytes_begin() <= T.bytes_begin() && T.bytes_end() <= S.bytes_end();
|
||||
}
|
||||
|
||||
MemoryBufferRef ScriptLexer::getCurrentMB() {
|
||||
// Find input buffer containing the current token.
|
||||
assert(!MBs.empty());
|
||||
if (!Pos)
|
||||
return MBs[0];
|
||||
|
||||
for (MemoryBufferRef MB : MBs)
|
||||
if (encloses(MB.getBuffer(), Tokens[Pos - 1]))
|
||||
return MB;
|
||||
llvm_unreachable("getCurrentMB: failed to find a token");
|
||||
}
|
||||
56
deps/lld/ELF/ScriptLexer.h
vendored
Normal file
56
deps/lld/ELF/ScriptLexer.h
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
//===- ScriptLexer.h --------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_SCRIPT_LEXER_H
|
||||
#define LLD_ELF_SCRIPT_LEXER_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
class ScriptLexer {
|
||||
public:
|
||||
explicit ScriptLexer(MemoryBufferRef MB);
|
||||
|
||||
void setError(const Twine &Msg);
|
||||
void tokenize(MemoryBufferRef MB);
|
||||
static StringRef skipSpace(StringRef S);
|
||||
bool atEOF();
|
||||
StringRef next();
|
||||
StringRef peek();
|
||||
void skip();
|
||||
bool consume(StringRef Tok);
|
||||
void expect(StringRef Expect);
|
||||
bool consumeLabel(StringRef Tok);
|
||||
std::string getCurrentLocation();
|
||||
|
||||
std::vector<MemoryBufferRef> MBs;
|
||||
std::vector<StringRef> Tokens;
|
||||
bool InExpr = false;
|
||||
size_t Pos = 0;
|
||||
bool Error = false;
|
||||
|
||||
private:
|
||||
void maybeSplitExpr();
|
||||
StringRef getLine();
|
||||
size_t getLineNumber();
|
||||
size_t getColumnNumber();
|
||||
|
||||
MemoryBufferRef getCurrentMB();
|
||||
};
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
1247
deps/lld/ELF/ScriptParser.cpp
vendored
Normal file
1247
deps/lld/ELF/ScriptParser.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
31
deps/lld/ELF/ScriptParser.h
vendored
Normal file
31
deps/lld/ELF/ScriptParser.h
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
//===- ScriptParser.h -------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_SCRIPT_PARSER_H
|
||||
#define LLD_ELF_SCRIPT_PARSER_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
// Parses a linker script. Calling this function updates
|
||||
// Config and ScriptConfig.
|
||||
void readLinkerScript(MemoryBufferRef MB);
|
||||
|
||||
// Parses a version script.
|
||||
void readVersionScript(MemoryBufferRef MB);
|
||||
|
||||
void readDynamicList(MemoryBufferRef MB);
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
85
deps/lld/ELF/Strings.cpp
vendored
Normal file
85
deps/lld/ELF/Strings.cpp
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
//===- Strings.cpp -------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Strings.h"
|
||||
#include "Config.h"
|
||||
#include "Error.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Demangle/Demangle.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
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> elf::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;
|
||||
}
|
||||
|
||||
static bool isAlpha(char C) {
|
||||
return ('a' <= C && C <= 'z') || ('A' <= C && C <= 'Z') || C == '_';
|
||||
}
|
||||
|
||||
static bool isAlnum(char C) { return isAlpha(C) || ('0' <= C && C <= '9'); }
|
||||
|
||||
// Returns true if S is valid as a C language identifier.
|
||||
bool elf::isValidCIdentifier(StringRef S) {
|
||||
return !S.empty() && isAlpha(S[0]) &&
|
||||
std::all_of(S.begin() + 1, S.end(), isAlnum);
|
||||
}
|
||||
|
||||
// Returns the demangled C++ symbol name for Name.
|
||||
Optional<std::string> elf::demangle(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;
|
||||
}
|
||||
79
deps/lld/ELF/Strings.h
vendored
Normal file
79
deps/lld/ELF/Strings.h
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
//===- Strings.h ------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_STRINGS_H
|
||||
#define LLD_ELF_STRINGS_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/BitVector.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/GlobPattern.h"
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
std::vector<uint8_t> parseHex(StringRef S);
|
||||
bool isValidCIdentifier(StringRef S);
|
||||
|
||||
// This is a lazy version of StringRef. String size is computed lazily
|
||||
// when it is needed. It is more efficient than StringRef to instantiate
|
||||
// if you have a string whose size is unknown.
|
||||
//
|
||||
// ELF string tables contain a lot of null-terminated strings.
|
||||
// Most of them are not necessary for the linker because they are names
|
||||
// of local symbols and the linker doesn't use local symbol names for
|
||||
// name resolution. So, we use this class to represents strings read
|
||||
// from string tables.
|
||||
class StringRefZ {
|
||||
public:
|
||||
StringRefZ() : Start(nullptr), Size(0) {}
|
||||
StringRefZ(const char *S, size_t Size) : Start(S), Size(Size) {}
|
||||
|
||||
/*implicit*/ StringRefZ(const char *S) : Start(S), Size(-1) {}
|
||||
|
||||
/*implicit*/ StringRefZ(llvm::StringRef S)
|
||||
: Start(S.data()), Size(S.size()) {}
|
||||
|
||||
operator llvm::StringRef() const {
|
||||
if (Size == (size_t)-1)
|
||||
Size = strlen(Start);
|
||||
return {Start, Size};
|
||||
}
|
||||
|
||||
private:
|
||||
const char *Start;
|
||||
mutable size_t Size;
|
||||
};
|
||||
|
||||
// This class represents multiple glob patterns.
|
||||
class StringMatcher {
|
||||
public:
|
||||
StringMatcher() = default;
|
||||
explicit StringMatcher(ArrayRef<StringRef> Pat);
|
||||
|
||||
bool match(StringRef S) const;
|
||||
|
||||
private:
|
||||
std::vector<llvm::GlobPattern> Patterns;
|
||||
};
|
||||
|
||||
// Returns a demangled C++ symbol name. If Name is not a mangled
|
||||
// name, it returns Optional::None.
|
||||
llvm::Optional<std::string> demangle(StringRef Name);
|
||||
|
||||
inline ArrayRef<uint8_t> toArrayRef(StringRef S) {
|
||||
return {(const uint8_t *)S.data(), S.size()};
|
||||
}
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
782
deps/lld/ELF/SymbolTable.cpp
vendored
Normal file
782
deps/lld/ELF/SymbolTable.cpp
vendored
Normal file
@ -0,0 +1,782 @@
|
||||
//===- SymbolTable.cpp ----------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Symbol table is a bag of all known symbols. We put all symbols of
|
||||
// all input files to the symbol table. The symbol table is basically
|
||||
// a hash table with the logic to resolve symbol name conflicts using
|
||||
// the symbol types.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "SymbolTable.h"
|
||||
#include "Config.h"
|
||||
#include "Error.h"
|
||||
#include "LinkerScript.h"
|
||||
#include "Memory.h"
|
||||
#include "Symbols.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::ELF;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
// All input object files must be for the same architecture
|
||||
// (e.g. it does not make sense to link x86 object files with
|
||||
// MIPS object files.) This function checks for that error.
|
||||
template <class ELFT> static bool isCompatible(InputFile *F) {
|
||||
if (!isa<ELFFileBase<ELFT>>(F) && !isa<BitcodeFile>(F))
|
||||
return true;
|
||||
|
||||
if (F->EKind == Config->EKind && F->EMachine == Config->EMachine) {
|
||||
if (Config->EMachine != EM_MIPS)
|
||||
return true;
|
||||
if (isMipsN32Abi(F) == Config->MipsN32Abi)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Config->Emulation.empty())
|
||||
error(toString(F) + " is incompatible with " + Config->Emulation);
|
||||
else
|
||||
error(toString(F) + " is incompatible with " + toString(Config->FirstElf));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add symbols in File to the symbol table.
|
||||
template <class ELFT> void SymbolTable<ELFT>::addFile(InputFile *File) {
|
||||
if (!Config->FirstElf && isa<ELFFileBase<ELFT>>(File))
|
||||
Config->FirstElf = File;
|
||||
|
||||
if (!isCompatible<ELFT>(File))
|
||||
return;
|
||||
|
||||
// Binary file
|
||||
if (auto *F = dyn_cast<BinaryFile>(File)) {
|
||||
BinaryFiles.push_back(F);
|
||||
F->parse<ELFT>();
|
||||
return;
|
||||
}
|
||||
|
||||
// .a file
|
||||
if (auto *F = dyn_cast<ArchiveFile>(File)) {
|
||||
F->parse<ELFT>();
|
||||
return;
|
||||
}
|
||||
|
||||
// Lazy object file
|
||||
if (auto *F = dyn_cast<LazyObjectFile>(File)) {
|
||||
F->parse<ELFT>();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Config->Trace)
|
||||
message(toString(File));
|
||||
|
||||
// .so file
|
||||
if (auto *F = dyn_cast<SharedFile<ELFT>>(File)) {
|
||||
// DSOs are uniquified not by filename but by soname.
|
||||
F->parseSoName();
|
||||
if (ErrorCount || !SoNames.insert(F->SoName).second)
|
||||
return;
|
||||
SharedFiles.push_back(F);
|
||||
F->parseRest();
|
||||
return;
|
||||
}
|
||||
|
||||
// LLVM bitcode file
|
||||
if (auto *F = dyn_cast<BitcodeFile>(File)) {
|
||||
BitcodeFiles.push_back(F);
|
||||
F->parse<ELFT>(ComdatGroups);
|
||||
return;
|
||||
}
|
||||
|
||||
// Regular object file
|
||||
auto *F = cast<ObjectFile<ELFT>>(File);
|
||||
ObjectFiles.push_back(F);
|
||||
F->parse(ComdatGroups);
|
||||
}
|
||||
|
||||
// This function is where all the optimizations of link-time
|
||||
// optimization happens. When LTO is in use, some input files are
|
||||
// not in native object file format but in the LLVM bitcode format.
|
||||
// This function compiles bitcode files into a few big native files
|
||||
// using LLVM functions and replaces bitcode symbols with the results.
|
||||
// Because all bitcode files that consist of a program are passed
|
||||
// to the compiler at once, it can do whole-program optimization.
|
||||
template <class ELFT> void SymbolTable<ELFT>::addCombinedLTOObject() {
|
||||
if (BitcodeFiles.empty())
|
||||
return;
|
||||
|
||||
// Compile bitcode files and replace bitcode symbols.
|
||||
LTO.reset(new BitcodeCompiler);
|
||||
for (BitcodeFile *F : BitcodeFiles)
|
||||
LTO->add(*F);
|
||||
|
||||
for (InputFile *File : LTO->compile()) {
|
||||
ObjectFile<ELFT> *Obj = cast<ObjectFile<ELFT>>(File);
|
||||
DenseSet<CachedHashStringRef> DummyGroups;
|
||||
Obj->parse(DummyGroups);
|
||||
ObjectFiles.push_back(Obj);
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
DefinedRegular *SymbolTable<ELFT>::addAbsolute(StringRef Name,
|
||||
uint8_t Visibility,
|
||||
uint8_t Binding) {
|
||||
Symbol *Sym =
|
||||
addRegular(Name, Visibility, STT_NOTYPE, 0, 0, Binding, nullptr, nullptr);
|
||||
return cast<DefinedRegular>(Sym->body());
|
||||
}
|
||||
|
||||
// Add Name as an "ignored" symbol. An ignored symbol is a regular
|
||||
// linker-synthesized defined symbol, but is only defined if needed.
|
||||
template <class ELFT>
|
||||
DefinedRegular *SymbolTable<ELFT>::addIgnored(StringRef Name,
|
||||
uint8_t Visibility) {
|
||||
SymbolBody *S = find(Name);
|
||||
if (!S || S->isInCurrentDSO())
|
||||
return nullptr;
|
||||
return addAbsolute(Name, Visibility);
|
||||
}
|
||||
|
||||
// Set a flag for --trace-symbol so that we can print out a log message
|
||||
// if a new symbol with the same name is inserted into the symbol table.
|
||||
template <class ELFT> void SymbolTable<ELFT>::trace(StringRef Name) {
|
||||
Symtab.insert({CachedHashStringRef(Name), {-1, true}});
|
||||
}
|
||||
|
||||
// Rename SYM as __wrap_SYM. The original symbol is preserved as __real_SYM.
|
||||
// Used to implement --wrap.
|
||||
template <class ELFT> void SymbolTable<ELFT>::addSymbolWrap(StringRef Name) {
|
||||
SymbolBody *B = find(Name);
|
||||
if (!B)
|
||||
return;
|
||||
Symbol *Sym = B->symbol();
|
||||
Symbol *Real = addUndefined(Saver.save("__real_" + Name));
|
||||
Symbol *Wrap = addUndefined(Saver.save("__wrap_" + Name));
|
||||
|
||||
// Tell LTO not to eliminate this symbol
|
||||
Wrap->IsUsedInRegularObj = true;
|
||||
|
||||
Config->RenamedSymbols[Real] = {Sym, Real->Binding};
|
||||
Config->RenamedSymbols[Sym] = {Wrap, Sym->Binding};
|
||||
}
|
||||
|
||||
// Creates alias for symbol. Used to implement --defsym=ALIAS=SYM.
|
||||
template <class ELFT>
|
||||
void SymbolTable<ELFT>::addSymbolAlias(StringRef Alias, StringRef Name) {
|
||||
SymbolBody *B = find(Name);
|
||||
if (!B) {
|
||||
error("-defsym: undefined symbol: " + Name);
|
||||
return;
|
||||
}
|
||||
Symbol *Sym = B->symbol();
|
||||
Symbol *AliasSym = addUndefined(Alias);
|
||||
|
||||
// Tell LTO not to eliminate this symbol
|
||||
Sym->IsUsedInRegularObj = true;
|
||||
Config->RenamedSymbols[AliasSym] = {Sym, AliasSym->Binding};
|
||||
}
|
||||
|
||||
// Apply symbol renames created by -wrap and -defsym. The renames are created
|
||||
// before LTO in addSymbolWrap() and addSymbolAlias() to have a chance to inform
|
||||
// LTO (if LTO is running) not to include these symbols in IPO. Now that the
|
||||
// symbols are finalized, we can perform the replacement.
|
||||
template <class ELFT> void SymbolTable<ELFT>::applySymbolRenames() {
|
||||
for (auto &KV : Config->RenamedSymbols) {
|
||||
Symbol *Dst = KV.first;
|
||||
Symbol *Src = KV.second.Target;
|
||||
Dst->body()->copy(Src->body());
|
||||
Dst->Binding = KV.second.OriginalBinding;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t getMinVisibility(uint8_t VA, uint8_t VB) {
|
||||
if (VA == STV_DEFAULT)
|
||||
return VB;
|
||||
if (VB == STV_DEFAULT)
|
||||
return VA;
|
||||
return std::min(VA, VB);
|
||||
}
|
||||
|
||||
// Find an existing symbol or create and insert a new one.
|
||||
template <class ELFT>
|
||||
std::pair<Symbol *, bool> SymbolTable<ELFT>::insert(StringRef Name) {
|
||||
// <name>@@<version> means the symbol is the default version. In that
|
||||
// case <name>@@<version> will be used to resolve references to <name>.
|
||||
size_t Pos = Name.find("@@");
|
||||
if (Pos != StringRef::npos)
|
||||
Name = Name.take_front(Pos);
|
||||
|
||||
auto P = Symtab.insert(
|
||||
{CachedHashStringRef(Name), SymIndex((int)SymVector.size(), false)});
|
||||
SymIndex &V = P.first->second;
|
||||
bool IsNew = P.second;
|
||||
|
||||
if (V.Idx == -1) {
|
||||
IsNew = true;
|
||||
V = SymIndex((int)SymVector.size(), true);
|
||||
}
|
||||
|
||||
Symbol *Sym;
|
||||
if (IsNew) {
|
||||
Sym = make<Symbol>();
|
||||
Sym->InVersionScript = false;
|
||||
Sym->Binding = STB_WEAK;
|
||||
Sym->Visibility = STV_DEFAULT;
|
||||
Sym->IsUsedInRegularObj = false;
|
||||
Sym->ExportDynamic = false;
|
||||
Sym->Traced = V.Traced;
|
||||
Sym->VersionId = Config->DefaultSymbolVersion;
|
||||
SymVector.push_back(Sym);
|
||||
} else {
|
||||
Sym = SymVector[V.Idx];
|
||||
}
|
||||
return {Sym, IsNew};
|
||||
}
|
||||
|
||||
// Find an existing symbol or create and insert a new one, then apply the given
|
||||
// attributes.
|
||||
template <class ELFT>
|
||||
std::pair<Symbol *, bool>
|
||||
SymbolTable<ELFT>::insert(StringRef Name, uint8_t Type, uint8_t Visibility,
|
||||
bool CanOmitFromDynSym, InputFile *File) {
|
||||
bool IsUsedInRegularObj = !File || File->kind() == InputFile::ObjectKind;
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(Name);
|
||||
|
||||
// Merge in the new symbol's visibility.
|
||||
S->Visibility = getMinVisibility(S->Visibility, Visibility);
|
||||
|
||||
if (!CanOmitFromDynSym && (Config->Shared || Config->ExportDynamic))
|
||||
S->ExportDynamic = true;
|
||||
|
||||
if (IsUsedInRegularObj)
|
||||
S->IsUsedInRegularObj = true;
|
||||
|
||||
if (!WasInserted && S->body()->Type != SymbolBody::UnknownType &&
|
||||
((Type == STT_TLS) != S->body()->isTls())) {
|
||||
error("TLS attribute mismatch: " + toString(*S->body()) +
|
||||
"\n>>> defined in " + toString(S->body()->File) +
|
||||
"\n>>> defined in " + toString(File));
|
||||
}
|
||||
|
||||
return {S, WasInserted};
|
||||
}
|
||||
|
||||
template <class ELFT> Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name) {
|
||||
return addUndefined(Name, /*IsLocal=*/false, STB_GLOBAL, STV_DEFAULT,
|
||||
/*Type*/ 0,
|
||||
/*CanOmitFromDynSym*/ false, /*File*/ nullptr);
|
||||
}
|
||||
|
||||
static uint8_t getVisibility(uint8_t StOther) { return StOther & 3; }
|
||||
|
||||
template <class ELFT>
|
||||
Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name, bool IsLocal,
|
||||
uint8_t Binding, uint8_t StOther,
|
||||
uint8_t Type, bool CanOmitFromDynSym,
|
||||
InputFile *File) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
uint8_t Visibility = getVisibility(StOther);
|
||||
std::tie(S, WasInserted) =
|
||||
insert(Name, Type, Visibility, CanOmitFromDynSym, File);
|
||||
// An undefined symbol with non default visibility must be satisfied
|
||||
// in the same DSO.
|
||||
if (WasInserted ||
|
||||
(isa<SharedSymbol>(S->body()) && Visibility != STV_DEFAULT)) {
|
||||
S->Binding = Binding;
|
||||
replaceBody<Undefined>(S, Name, IsLocal, StOther, Type, File);
|
||||
return S;
|
||||
}
|
||||
if (Binding != STB_WEAK) {
|
||||
SymbolBody *B = S->body();
|
||||
if (B->isShared() || B->isLazy() || B->isUndefined())
|
||||
S->Binding = Binding;
|
||||
if (auto *SS = dyn_cast<SharedSymbol>(B))
|
||||
cast<SharedFile<ELFT>>(SS->File)->IsUsed = true;
|
||||
}
|
||||
if (auto *L = dyn_cast<Lazy>(S->body())) {
|
||||
// An undefined weak will not fetch archive members, but we have to remember
|
||||
// its type. See also comment in addLazyArchive.
|
||||
if (S->isWeak())
|
||||
L->Type = Type;
|
||||
else if (InputFile *F = L->fetch())
|
||||
addFile(F);
|
||||
}
|
||||
return S;
|
||||
}
|
||||
|
||||
// Using .symver foo,foo@@VER unfortunately creates two symbols: foo and
|
||||
// foo@@VER. We want to effectively ignore foo, so give precedence to
|
||||
// foo@@VER.
|
||||
// FIXME: If users can transition to using
|
||||
// .symver foo,foo@@@VER
|
||||
// we can delete this hack.
|
||||
static int compareVersion(Symbol *S, StringRef Name) {
|
||||
if (Name.find("@@") != StringRef::npos &&
|
||||
S->body()->getName().find("@@") == StringRef::npos)
|
||||
return 1;
|
||||
if (Name.find("@@") == StringRef::npos &&
|
||||
S->body()->getName().find("@@") != StringRef::npos)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We have a new defined symbol with the specified binding. Return 1 if the new
|
||||
// symbol should win, -1 if the new symbol should lose, or 0 if both symbols are
|
||||
// strong defined symbols.
|
||||
static int compareDefined(Symbol *S, bool WasInserted, uint8_t Binding,
|
||||
StringRef Name) {
|
||||
if (WasInserted)
|
||||
return 1;
|
||||
SymbolBody *Body = S->body();
|
||||
if (!Body->isInCurrentDSO())
|
||||
return 1;
|
||||
|
||||
if (int R = compareVersion(S, Name))
|
||||
return R;
|
||||
|
||||
if (Binding == STB_WEAK)
|
||||
return -1;
|
||||
if (S->isWeak())
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We have a new non-common defined symbol with the specified binding. Return 1
|
||||
// if the new symbol should win, -1 if the new symbol should lose, or 0 if there
|
||||
// is a conflict. If the new symbol wins, also update the binding.
|
||||
template <typename ELFT>
|
||||
static int compareDefinedNonCommon(Symbol *S, bool WasInserted, uint8_t Binding,
|
||||
bool IsAbsolute, typename ELFT::uint Value,
|
||||
StringRef Name) {
|
||||
if (int Cmp = compareDefined(S, WasInserted, Binding, Name)) {
|
||||
if (Cmp > 0)
|
||||
S->Binding = Binding;
|
||||
return Cmp;
|
||||
}
|
||||
SymbolBody *B = S->body();
|
||||
if (isa<DefinedCommon>(B)) {
|
||||
// Non-common symbols take precedence over common symbols.
|
||||
if (Config->WarnCommon)
|
||||
warn("common " + S->body()->getName() + " is overridden");
|
||||
return 1;
|
||||
} else if (auto *R = dyn_cast<DefinedRegular>(B)) {
|
||||
if (R->Section == nullptr && Binding == STB_GLOBAL && IsAbsolute &&
|
||||
R->Value == Value)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
Symbol *SymbolTable<ELFT>::addCommon(StringRef N, uint64_t Size,
|
||||
uint32_t Alignment, uint8_t Binding,
|
||||
uint8_t StOther, uint8_t Type,
|
||||
InputFile *File) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(N, Type, getVisibility(StOther),
|
||||
/*CanOmitFromDynSym*/ false, File);
|
||||
int Cmp = compareDefined(S, WasInserted, Binding, N);
|
||||
if (Cmp > 0) {
|
||||
S->Binding = Binding;
|
||||
replaceBody<DefinedCommon>(S, N, Size, Alignment, StOther, Type, File);
|
||||
} else if (Cmp == 0) {
|
||||
auto *C = dyn_cast<DefinedCommon>(S->body());
|
||||
if (!C) {
|
||||
// Non-common symbols take precedence over common symbols.
|
||||
if (Config->WarnCommon)
|
||||
warn("common " + S->body()->getName() + " is overridden");
|
||||
return S;
|
||||
}
|
||||
|
||||
if (Config->WarnCommon)
|
||||
warn("multiple common of " + S->body()->getName());
|
||||
|
||||
Alignment = C->Alignment = std::max(C->Alignment, Alignment);
|
||||
if (Size > C->Size)
|
||||
replaceBody<DefinedCommon>(S, N, Size, Alignment, StOther, Type, File);
|
||||
}
|
||||
return S;
|
||||
}
|
||||
|
||||
static void warnOrError(const Twine &Msg) {
|
||||
if (Config->AllowMultipleDefinition)
|
||||
warn(Msg);
|
||||
else
|
||||
error(Msg);
|
||||
}
|
||||
|
||||
static void reportDuplicate(SymbolBody *Sym, InputFile *NewFile) {
|
||||
warnOrError("duplicate symbol: " + toString(*Sym) + "\n>>> defined in " +
|
||||
toString(Sym->File) + "\n>>> defined in " + toString(NewFile));
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static void reportDuplicate(SymbolBody *Sym, InputSectionBase *ErrSec,
|
||||
typename ELFT::uint ErrOffset) {
|
||||
DefinedRegular *D = dyn_cast<DefinedRegular>(Sym);
|
||||
if (!D || !D->Section || !ErrSec) {
|
||||
reportDuplicate(Sym, ErrSec ? ErrSec->getFile<ELFT>() : nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
// Construct and print an error message in the form of:
|
||||
//
|
||||
// ld.lld: error: duplicate symbol: foo
|
||||
// >>> defined at bar.c:30
|
||||
// >>> bar.o (/home/alice/src/bar.o)
|
||||
// >>> defined at baz.c:563
|
||||
// >>> baz.o in archive libbaz.a
|
||||
auto *Sec1 = cast<InputSectionBase>(D->Section);
|
||||
std::string Src1 = Sec1->getSrcMsg<ELFT>(D->Value);
|
||||
std::string Obj1 = Sec1->getObjMsg<ELFT>(D->Value);
|
||||
std::string Src2 = ErrSec->getSrcMsg<ELFT>(ErrOffset);
|
||||
std::string Obj2 = ErrSec->getObjMsg<ELFT>(ErrOffset);
|
||||
|
||||
std::string Msg = "duplicate symbol: " + toString(*Sym) + "\n>>> defined at ";
|
||||
if (!Src1.empty())
|
||||
Msg += Src1 + "\n>>> ";
|
||||
Msg += Obj1 + "\n>>> defined at ";
|
||||
if (!Src2.empty())
|
||||
Msg += Src2 + "\n>>> ";
|
||||
Msg += Obj2;
|
||||
warnOrError(Msg);
|
||||
}
|
||||
|
||||
template <typename ELFT>
|
||||
Symbol *SymbolTable<ELFT>::addRegular(StringRef Name, uint8_t StOther,
|
||||
uint8_t Type, uint64_t Value,
|
||||
uint64_t Size, uint8_t Binding,
|
||||
SectionBase *Section, InputFile *File) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(Name, Type, getVisibility(StOther),
|
||||
/*CanOmitFromDynSym*/ false, File);
|
||||
int Cmp = compareDefinedNonCommon<ELFT>(S, WasInserted, Binding,
|
||||
Section == nullptr, Value, Name);
|
||||
if (Cmp > 0)
|
||||
replaceBody<DefinedRegular>(S, Name, /*IsLocal=*/false, StOther, Type,
|
||||
Value, Size, Section, File);
|
||||
else if (Cmp == 0)
|
||||
reportDuplicate<ELFT>(S->body(),
|
||||
dyn_cast_or_null<InputSectionBase>(Section), Value);
|
||||
return S;
|
||||
}
|
||||
|
||||
template <typename ELFT>
|
||||
void SymbolTable<ELFT>::addShared(SharedFile<ELFT> *File, StringRef Name,
|
||||
const Elf_Sym &Sym,
|
||||
const typename ELFT::Verdef *Verdef) {
|
||||
// DSO symbols do not affect visibility in the output, so we pass STV_DEFAULT
|
||||
// as the visibility, which will leave the visibility in the symbol table
|
||||
// unchanged.
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(Name, Sym.getType(), STV_DEFAULT,
|
||||
/*CanOmitFromDynSym*/ true, File);
|
||||
// Make sure we preempt DSO symbols with default visibility.
|
||||
if (Sym.getVisibility() == STV_DEFAULT)
|
||||
S->ExportDynamic = true;
|
||||
|
||||
SymbolBody *Body = S->body();
|
||||
// An undefined symbol with non default visibility must be satisfied
|
||||
// in the same DSO.
|
||||
if (WasInserted ||
|
||||
(isa<Undefined>(Body) && Body->getVisibility() == STV_DEFAULT)) {
|
||||
replaceBody<SharedSymbol>(S, File, Name, Sym.st_other, Sym.getType(), &Sym,
|
||||
Verdef);
|
||||
if (!S->isWeak())
|
||||
File->IsUsed = true;
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
Symbol *SymbolTable<ELFT>::addBitcode(StringRef Name, uint8_t Binding,
|
||||
uint8_t StOther, uint8_t Type,
|
||||
bool CanOmitFromDynSym, BitcodeFile *F) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) =
|
||||
insert(Name, Type, getVisibility(StOther), CanOmitFromDynSym, F);
|
||||
int Cmp = compareDefinedNonCommon<ELFT>(S, WasInserted, Binding,
|
||||
/*IsAbs*/ false, /*Value*/ 0, Name);
|
||||
if (Cmp > 0)
|
||||
replaceBody<DefinedRegular>(S, Name, /*IsLocal=*/false, StOther, Type, 0, 0,
|
||||
nullptr, F);
|
||||
else if (Cmp == 0)
|
||||
reportDuplicate(S->body(), F);
|
||||
return S;
|
||||
}
|
||||
|
||||
template <class ELFT> SymbolBody *SymbolTable<ELFT>::find(StringRef Name) {
|
||||
auto It = Symtab.find(CachedHashStringRef(Name));
|
||||
if (It == Symtab.end())
|
||||
return nullptr;
|
||||
SymIndex V = It->second;
|
||||
if (V.Idx == -1)
|
||||
return nullptr;
|
||||
return SymVector[V.Idx]->body();
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
SymbolBody *SymbolTable<ELFT>::findInCurrentDSO(StringRef Name) {
|
||||
if (SymbolBody *S = find(Name))
|
||||
if (S->isInCurrentDSO())
|
||||
return S;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
Symbol *SymbolTable<ELFT>::addLazyArchive(ArchiveFile *F,
|
||||
const object::Archive::Symbol Sym) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
StringRef Name = Sym.getName();
|
||||
std::tie(S, WasInserted) = insert(Name);
|
||||
if (WasInserted) {
|
||||
replaceBody<LazyArchive>(S, *F, Sym, SymbolBody::UnknownType);
|
||||
return S;
|
||||
}
|
||||
if (!S->body()->isUndefined())
|
||||
return S;
|
||||
|
||||
// Weak undefined symbols should not fetch members from archives. If we were
|
||||
// to keep old symbol we would not know that an archive member was available
|
||||
// if a strong undefined symbol shows up afterwards in the link. If a strong
|
||||
// undefined symbol never shows up, this lazy symbol will get to the end of
|
||||
// the link and must be treated as the weak undefined one. We already marked
|
||||
// this symbol as used when we added it to the symbol table, but we also need
|
||||
// to preserve its type. FIXME: Move the Type field to Symbol.
|
||||
if (S->isWeak()) {
|
||||
replaceBody<LazyArchive>(S, *F, Sym, S->body()->Type);
|
||||
return S;
|
||||
}
|
||||
std::pair<MemoryBufferRef, uint64_t> MBInfo = F->getMember(&Sym);
|
||||
if (!MBInfo.first.getBuffer().empty())
|
||||
addFile(createObjectFile(MBInfo.first, F->getName(), MBInfo.second));
|
||||
return S;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void SymbolTable<ELFT>::addLazyObject(StringRef Name, LazyObjectFile &Obj) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(Name);
|
||||
if (WasInserted) {
|
||||
replaceBody<LazyObject>(S, Name, Obj, SymbolBody::UnknownType);
|
||||
return;
|
||||
}
|
||||
if (!S->body()->isUndefined())
|
||||
return;
|
||||
|
||||
// See comment for addLazyArchive above.
|
||||
if (S->isWeak())
|
||||
replaceBody<LazyObject>(S, Name, Obj, S->body()->Type);
|
||||
else if (InputFile *F = Obj.fetch())
|
||||
addFile(F);
|
||||
}
|
||||
|
||||
// Process undefined (-u) flags by loading lazy symbols named by those flags.
|
||||
template <class ELFT> void SymbolTable<ELFT>::scanUndefinedFlags() {
|
||||
for (StringRef S : Config->Undefined)
|
||||
if (auto *L = dyn_cast_or_null<Lazy>(find(S)))
|
||||
if (InputFile *File = L->fetch())
|
||||
addFile(File);
|
||||
}
|
||||
|
||||
// This function takes care of the case in which shared libraries depend on
|
||||
// the user program (not the other way, which is usual). Shared libraries
|
||||
// may have undefined symbols, expecting that the user program provides
|
||||
// the definitions for them. An example is BSD's __progname symbol.
|
||||
// We need to put such symbols to the main program's .dynsym so that
|
||||
// shared libraries can find them.
|
||||
// Except this, we ignore undefined symbols in DSOs.
|
||||
template <class ELFT> void SymbolTable<ELFT>::scanShlibUndefined() {
|
||||
for (SharedFile<ELFT> *File : SharedFiles) {
|
||||
for (StringRef U : File->getUndefinedSymbols()) {
|
||||
SymbolBody *Sym = find(U);
|
||||
if (!Sym || !Sym->isDefined())
|
||||
continue;
|
||||
Sym->symbol()->ExportDynamic = true;
|
||||
|
||||
// If -dynamic-list is given, the default version is set to
|
||||
// VER_NDX_LOCAL, which prevents a symbol to be exported via .dynsym.
|
||||
// Set to VER_NDX_GLOBAL so the symbol will be handled as if it were
|
||||
// specified by -dynamic-list.
|
||||
Sym->symbol()->VersionId = VER_NDX_GLOBAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize DemangledSyms with a map from demangled symbols to symbol
|
||||
// objects. Used to handle "extern C++" directive in version scripts.
|
||||
//
|
||||
// The map will contain all demangled symbols. That can be very large,
|
||||
// and in LLD we generally want to avoid do anything for each symbol.
|
||||
// Then, why are we doing this? Here's why.
|
||||
//
|
||||
// Users can use "extern C++ {}" directive to match against demangled
|
||||
// C++ symbols. For example, you can write a pattern such as
|
||||
// "llvm::*::foo(int, ?)". Obviously, there's no way to handle this
|
||||
// other than trying to match a pattern against all demangled symbols.
|
||||
// So, if "extern C++" feature is used, we need to demangle all known
|
||||
// symbols.
|
||||
template <class ELFT>
|
||||
StringMap<std::vector<SymbolBody *>> &SymbolTable<ELFT>::getDemangledSyms() {
|
||||
if (!DemangledSyms) {
|
||||
DemangledSyms.emplace();
|
||||
for (Symbol *Sym : SymVector) {
|
||||
SymbolBody *B = Sym->body();
|
||||
if (B->isUndefined())
|
||||
continue;
|
||||
if (Optional<std::string> S = demangle(B->getName()))
|
||||
(*DemangledSyms)[*S].push_back(B);
|
||||
else
|
||||
(*DemangledSyms)[B->getName()].push_back(B);
|
||||
}
|
||||
}
|
||||
return *DemangledSyms;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
std::vector<SymbolBody *> SymbolTable<ELFT>::findByVersion(SymbolVersion Ver) {
|
||||
if (Ver.IsExternCpp)
|
||||
return getDemangledSyms().lookup(Ver.Name);
|
||||
if (SymbolBody *B = find(Ver.Name))
|
||||
if (!B->isUndefined())
|
||||
return {B};
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
std::vector<SymbolBody *>
|
||||
SymbolTable<ELFT>::findAllByVersion(SymbolVersion Ver) {
|
||||
std::vector<SymbolBody *> Res;
|
||||
StringMatcher M(Ver.Name);
|
||||
|
||||
if (Ver.IsExternCpp) {
|
||||
for (auto &P : getDemangledSyms())
|
||||
if (M.match(P.first()))
|
||||
Res.insert(Res.end(), P.second.begin(), P.second.end());
|
||||
return Res;
|
||||
}
|
||||
|
||||
for (Symbol *Sym : SymVector) {
|
||||
SymbolBody *B = Sym->body();
|
||||
if (!B->isUndefined() && M.match(B->getName()))
|
||||
Res.push_back(B);
|
||||
}
|
||||
return Res;
|
||||
}
|
||||
|
||||
// If there's only one anonymous version definition in a version
|
||||
// script file, the script does not actually define any symbol version,
|
||||
// but just specifies symbols visibilities.
|
||||
template <class ELFT> void SymbolTable<ELFT>::handleAnonymousVersion() {
|
||||
for (SymbolVersion &Ver : Config->VersionScriptGlobals)
|
||||
assignExactVersion(Ver, VER_NDX_GLOBAL, "global");
|
||||
for (SymbolVersion &Ver : Config->VersionScriptGlobals)
|
||||
assignWildcardVersion(Ver, VER_NDX_GLOBAL);
|
||||
for (SymbolVersion &Ver : Config->VersionScriptLocals)
|
||||
assignExactVersion(Ver, VER_NDX_LOCAL, "local");
|
||||
for (SymbolVersion &Ver : Config->VersionScriptLocals)
|
||||
assignWildcardVersion(Ver, VER_NDX_LOCAL);
|
||||
}
|
||||
|
||||
// Set symbol versions to symbols. This function handles patterns
|
||||
// containing no wildcard characters.
|
||||
template <class ELFT>
|
||||
void SymbolTable<ELFT>::assignExactVersion(SymbolVersion Ver,
|
||||
uint16_t VersionId,
|
||||
StringRef VersionName) {
|
||||
if (Ver.HasWildcard)
|
||||
return;
|
||||
|
||||
// Get a list of symbols which we need to assign the version to.
|
||||
std::vector<SymbolBody *> Syms = findByVersion(Ver);
|
||||
if (Syms.empty()) {
|
||||
if (Config->NoUndefinedVersion)
|
||||
error("version script assignment of '" + VersionName + "' to symbol '" +
|
||||
Ver.Name + "' failed: symbol not defined");
|
||||
return;
|
||||
}
|
||||
|
||||
// Assign the version.
|
||||
for (SymbolBody *B : Syms) {
|
||||
// Skip symbols containing version info because symbol versions
|
||||
// specified by symbol names take precedence over version scripts.
|
||||
// See parseSymbolVersion().
|
||||
if (B->getName().find('@') != StringRef::npos)
|
||||
continue;
|
||||
|
||||
Symbol *Sym = B->symbol();
|
||||
if (Sym->InVersionScript)
|
||||
warn("duplicate symbol '" + Ver.Name + "' in version script");
|
||||
Sym->VersionId = VersionId;
|
||||
Sym->InVersionScript = true;
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void SymbolTable<ELFT>::assignWildcardVersion(SymbolVersion Ver,
|
||||
uint16_t VersionId) {
|
||||
if (!Ver.HasWildcard)
|
||||
return;
|
||||
|
||||
// Exact matching takes precendence over fuzzy matching,
|
||||
// so we set a version to a symbol only if no version has been assigned
|
||||
// to the symbol. This behavior is compatible with GNU.
|
||||
for (SymbolBody *B : findAllByVersion(Ver))
|
||||
if (B->symbol()->VersionId == Config->DefaultSymbolVersion)
|
||||
B->symbol()->VersionId = VersionId;
|
||||
}
|
||||
|
||||
// This function processes version scripts by updating VersionId
|
||||
// member of symbols.
|
||||
template <class ELFT> void SymbolTable<ELFT>::scanVersionScript() {
|
||||
// Handle edge cases first.
|
||||
handleAnonymousVersion();
|
||||
|
||||
// Now we have version definitions, so we need to set version ids to symbols.
|
||||
// Each version definition has a glob pattern, and all symbols that match
|
||||
// with the pattern get that version.
|
||||
|
||||
// First, we assign versions to exact matching symbols,
|
||||
// i.e. version definitions not containing any glob meta-characters.
|
||||
for (VersionDefinition &V : Config->VersionDefinitions)
|
||||
for (SymbolVersion &Ver : V.Globals)
|
||||
assignExactVersion(Ver, V.Id, V.Name);
|
||||
|
||||
// Next, we assign versions to fuzzy matching symbols,
|
||||
// i.e. version definitions containing glob meta-characters.
|
||||
// Note that because the last match takes precedence over previous matches,
|
||||
// we iterate over the definitions in the reverse order.
|
||||
for (VersionDefinition &V : llvm::reverse(Config->VersionDefinitions))
|
||||
for (SymbolVersion &Ver : V.Globals)
|
||||
assignWildcardVersion(Ver, V.Id);
|
||||
|
||||
// Symbol themselves might know their versions because symbols
|
||||
// can contain versions in the form of <name>@<version>.
|
||||
// Let them parse and update their names to exclude version suffix.
|
||||
for (Symbol *Sym : SymVector)
|
||||
Sym->body()->parseSymbolVersion();
|
||||
}
|
||||
|
||||
template class elf::SymbolTable<ELF32LE>;
|
||||
template class elf::SymbolTable<ELF32BE>;
|
||||
template class elf::SymbolTable<ELF64LE>;
|
||||
template class elf::SymbolTable<ELF64BE>;
|
||||
147
deps/lld/ELF/SymbolTable.h
vendored
Normal file
147
deps/lld/ELF/SymbolTable.h
vendored
Normal file
@ -0,0 +1,147 @@
|
||||
//===- SymbolTable.h --------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_SYMBOL_TABLE_H
|
||||
#define LLD_ELF_SYMBOL_TABLE_H
|
||||
|
||||
#include "InputFiles.h"
|
||||
#include "LTO.h"
|
||||
#include "Strings.h"
|
||||
#include "llvm/ADT/CachedHashString.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
struct 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.
|
||||
template <class ELFT> class SymbolTable {
|
||||
typedef typename ELFT::Sym Elf_Sym;
|
||||
|
||||
public:
|
||||
void addFile(InputFile *File);
|
||||
void addCombinedLTOObject();
|
||||
void addSymbolAlias(StringRef Alias, StringRef Name);
|
||||
void addSymbolWrap(StringRef Name);
|
||||
void applySymbolRenames();
|
||||
|
||||
ArrayRef<Symbol *> getSymbols() const { return SymVector; }
|
||||
ArrayRef<ObjectFile<ELFT> *> getObjectFiles() const { return ObjectFiles; }
|
||||
ArrayRef<BinaryFile *> getBinaryFiles() const { return BinaryFiles; }
|
||||
ArrayRef<SharedFile<ELFT> *> getSharedFiles() const { return SharedFiles; }
|
||||
|
||||
DefinedRegular *addAbsolute(StringRef Name,
|
||||
uint8_t Visibility = llvm::ELF::STV_HIDDEN,
|
||||
uint8_t Binding = llvm::ELF::STB_GLOBAL);
|
||||
DefinedRegular *addIgnored(StringRef Name,
|
||||
uint8_t Visibility = llvm::ELF::STV_HIDDEN);
|
||||
|
||||
Symbol *addUndefined(StringRef Name);
|
||||
Symbol *addUndefined(StringRef Name, bool IsLocal, uint8_t Binding,
|
||||
uint8_t StOther, uint8_t Type, bool CanOmitFromDynSym,
|
||||
InputFile *File);
|
||||
|
||||
Symbol *addRegular(StringRef Name, uint8_t StOther, uint8_t Type,
|
||||
uint64_t Value, uint64_t Size, uint8_t Binding,
|
||||
SectionBase *Section, InputFile *File);
|
||||
|
||||
void addShared(SharedFile<ELFT> *F, StringRef Name, const Elf_Sym &Sym,
|
||||
const typename ELFT::Verdef *Verdef);
|
||||
|
||||
Symbol *addLazyArchive(ArchiveFile *F, const llvm::object::Archive::Symbol S);
|
||||
void addLazyObject(StringRef Name, LazyObjectFile &Obj);
|
||||
Symbol *addBitcode(StringRef Name, uint8_t Binding, uint8_t StOther,
|
||||
uint8_t Type, bool CanOmitFromDynSym, BitcodeFile *File);
|
||||
|
||||
Symbol *addCommon(StringRef N, uint64_t Size, uint32_t Alignment,
|
||||
uint8_t Binding, uint8_t StOther, uint8_t Type,
|
||||
InputFile *File);
|
||||
|
||||
std::pair<Symbol *, bool> insert(StringRef Name);
|
||||
std::pair<Symbol *, bool> insert(StringRef Name, uint8_t Type,
|
||||
uint8_t Visibility, bool CanOmitFromDynSym,
|
||||
InputFile *File);
|
||||
|
||||
void scanUndefinedFlags();
|
||||
void scanShlibUndefined();
|
||||
void scanVersionScript();
|
||||
|
||||
SymbolBody *find(StringRef Name);
|
||||
SymbolBody *findInCurrentDSO(StringRef Name);
|
||||
|
||||
void trace(StringRef Name);
|
||||
|
||||
private:
|
||||
std::vector<SymbolBody *> findByVersion(SymbolVersion Ver);
|
||||
std::vector<SymbolBody *> findAllByVersion(SymbolVersion Ver);
|
||||
|
||||
llvm::StringMap<std::vector<SymbolBody *>> &getDemangledSyms();
|
||||
void handleAnonymousVersion();
|
||||
void assignExactVersion(SymbolVersion Ver, uint16_t VersionId,
|
||||
StringRef VersionName);
|
||||
void assignWildcardVersion(SymbolVersion Ver, uint16_t VersionId);
|
||||
|
||||
struct SymIndex {
|
||||
SymIndex(int Idx, bool Traced) : Idx(Idx), Traced(Traced) {}
|
||||
int Idx : 31;
|
||||
unsigned Traced : 1;
|
||||
};
|
||||
|
||||
// The order the global symbols are in is not defined. We can use an arbitrary
|
||||
// order, but it has to be reproducible. That is true even when cross linking.
|
||||
// The default hashing of StringRef produces different results on 32 and 64
|
||||
// bit systems so we use a map to a vector. That is arbitrary, deterministic
|
||||
// but a bit inefficient.
|
||||
// FIXME: Experiment with passing in a custom hashing or sorting the symbols
|
||||
// once symbol resolution is finished.
|
||||
llvm::DenseMap<llvm::CachedHashStringRef, SymIndex> Symtab;
|
||||
std::vector<Symbol *> SymVector;
|
||||
|
||||
// Comdat groups define "link once" sections. If two comdat groups have the
|
||||
// same name, only one of them is linked, and the other is ignored. This set
|
||||
// is used to uniquify them.
|
||||
llvm::DenseSet<llvm::CachedHashStringRef> ComdatGroups;
|
||||
|
||||
std::vector<ObjectFile<ELFT> *> ObjectFiles;
|
||||
std::vector<SharedFile<ELFT> *> SharedFiles;
|
||||
std::vector<BitcodeFile *> BitcodeFiles;
|
||||
std::vector<BinaryFile *> BinaryFiles;
|
||||
|
||||
// Set of .so files to not link the same shared object file more than once.
|
||||
llvm::DenseSet<StringRef> SoNames;
|
||||
|
||||
// A map from demangled symbol names to their symbol objects.
|
||||
// This mapping is 1:N because two symbols with different versions
|
||||
// can have the same name. We use this map to handle "extern C++ {}"
|
||||
// directive in version scripts.
|
||||
llvm::Optional<llvm::StringMap<std::vector<SymbolBody *>>> DemangledSyms;
|
||||
|
||||
// For LTO.
|
||||
std::unique_ptr<BitcodeCompiler> LTO;
|
||||
};
|
||||
|
||||
template <class ELFT> struct Symtab { static SymbolTable<ELFT> *X; };
|
||||
template <class ELFT> SymbolTable<ELFT> *Symtab<ELFT>::X;
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
399
deps/lld/ELF/Symbols.cpp
vendored
Normal file
399
deps/lld/ELF/Symbols.cpp
vendored
Normal file
@ -0,0 +1,399 @@
|
||||
//===- Symbols.cpp --------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Symbols.h"
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "InputSection.h"
|
||||
#include "OutputSections.h"
|
||||
#include "Strings.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "Writer.h"
|
||||
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include <cstring>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::ELF;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
DefinedRegular *ElfSym::Bss;
|
||||
DefinedRegular *ElfSym::Etext1;
|
||||
DefinedRegular *ElfSym::Etext2;
|
||||
DefinedRegular *ElfSym::Edata1;
|
||||
DefinedRegular *ElfSym::Edata2;
|
||||
DefinedRegular *ElfSym::End1;
|
||||
DefinedRegular *ElfSym::End2;
|
||||
DefinedRegular *ElfSym::GlobalOffsetTable;
|
||||
DefinedRegular *ElfSym::MipsGp;
|
||||
DefinedRegular *ElfSym::MipsGpDisp;
|
||||
DefinedRegular *ElfSym::MipsLocalGp;
|
||||
|
||||
static uint64_t getSymVA(const SymbolBody &Body, int64_t &Addend) {
|
||||
switch (Body.kind()) {
|
||||
case SymbolBody::DefinedRegularKind: {
|
||||
auto &D = cast<DefinedRegular>(Body);
|
||||
SectionBase *IS = D.Section;
|
||||
if (auto *ISB = dyn_cast_or_null<InputSectionBase>(IS))
|
||||
IS = ISB->Repl;
|
||||
|
||||
// According to the ELF spec reference to a local symbol from outside
|
||||
// the group are not allowed. Unfortunately .eh_frame breaks that rule
|
||||
// and must be treated specially. For now we just replace the symbol with
|
||||
// 0.
|
||||
if (IS == &InputSection::Discarded)
|
||||
return 0;
|
||||
|
||||
// This is an absolute symbol.
|
||||
if (!IS)
|
||||
return D.Value;
|
||||
|
||||
uint64_t Offset = D.Value;
|
||||
|
||||
// An object in an SHF_MERGE section might be referenced via a
|
||||
// section symbol (as a hack for reducing the number of local
|
||||
// symbols).
|
||||
// Depending on the addend, the reference via a section symbol
|
||||
// refers to a different object in the merge section.
|
||||
// Since the objects in the merge section are not necessarily
|
||||
// contiguous in the output, the addend can thus affect the final
|
||||
// VA in a non-linear way.
|
||||
// To make this work, we incorporate the addend into the section
|
||||
// offset (and zero out the addend for later processing) so that
|
||||
// we find the right object in the section.
|
||||
if (D.isSection()) {
|
||||
Offset += Addend;
|
||||
Addend = 0;
|
||||
}
|
||||
|
||||
const OutputSection *OutSec = IS->getOutputSection();
|
||||
|
||||
// In the typical case, this is actually very simple and boils
|
||||
// down to adding together 3 numbers:
|
||||
// 1. The address of the output section.
|
||||
// 2. The offset of the input section within the output section.
|
||||
// 3. The offset within the input section (this addition happens
|
||||
// inside InputSection::getOffset).
|
||||
//
|
||||
// If you understand the data structures involved with this next
|
||||
// line (and how they get built), then you have a pretty good
|
||||
// understanding of the linker.
|
||||
uint64_t VA = (OutSec ? OutSec->Addr : 0) + IS->getOffset(Offset);
|
||||
|
||||
if (D.isTls() && !Config->Relocatable) {
|
||||
if (!Out::TlsPhdr)
|
||||
fatal(toString(D.File) +
|
||||
" has an STT_TLS symbol but doesn't have an SHF_TLS section");
|
||||
return VA - Out::TlsPhdr->p_vaddr;
|
||||
}
|
||||
return VA;
|
||||
}
|
||||
case SymbolBody::DefinedCommonKind:
|
||||
if (!Config->DefineCommon)
|
||||
return 0;
|
||||
return InX::Common->getParent()->Addr + InX::Common->OutSecOff +
|
||||
cast<DefinedCommon>(Body).Offset;
|
||||
case SymbolBody::SharedKind: {
|
||||
auto &SS = cast<SharedSymbol>(Body);
|
||||
if (SS.NeedsCopy)
|
||||
return SS.CopyRelSec->getParent()->Addr + SS.CopyRelSec->OutSecOff +
|
||||
SS.CopyRelSecOff;
|
||||
if (SS.NeedsPltAddr)
|
||||
return Body.getPltVA();
|
||||
return 0;
|
||||
}
|
||||
case SymbolBody::UndefinedKind:
|
||||
return 0;
|
||||
case SymbolBody::LazyArchiveKind:
|
||||
case SymbolBody::LazyObjectKind:
|
||||
assert(Body.symbol()->IsUsedInRegularObj && "lazy symbol reached writer");
|
||||
return 0;
|
||||
}
|
||||
llvm_unreachable("invalid symbol kind");
|
||||
}
|
||||
|
||||
SymbolBody::SymbolBody(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther,
|
||||
uint8_t Type)
|
||||
: SymbolKind(K), NeedsCopy(false), NeedsPltAddr(false), IsLocal(IsLocal),
|
||||
IsInGlobalMipsGot(false), Is32BitMipsGot(false), IsInIplt(false),
|
||||
IsInIgot(false), Type(Type), StOther(StOther), Name(Name) {}
|
||||
|
||||
// Returns true if a symbol can be replaced at load-time by a symbol
|
||||
// with the same name defined in other ELF executable or DSO.
|
||||
bool SymbolBody::isPreemptible() const {
|
||||
if (isLocal())
|
||||
return false;
|
||||
|
||||
// Shared symbols resolve to the definition in the DSO. The exceptions are
|
||||
// symbols with copy relocations (which resolve to .bss) or preempt plt
|
||||
// entries (which resolve to that plt entry).
|
||||
if (isShared())
|
||||
return !NeedsCopy && !NeedsPltAddr;
|
||||
|
||||
// That's all that can be preempted in a non-DSO.
|
||||
if (!Config->Shared)
|
||||
return false;
|
||||
|
||||
// Only symbols that appear in dynsym can be preempted.
|
||||
if (!symbol()->includeInDynsym())
|
||||
return false;
|
||||
|
||||
// Only default visibility symbols can be preempted.
|
||||
if (symbol()->Visibility != STV_DEFAULT)
|
||||
return false;
|
||||
|
||||
// -Bsymbolic means that definitions are not preempted.
|
||||
if (Config->Bsymbolic || (Config->BsymbolicFunctions && isFunc()))
|
||||
return !isDefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Overwrites all attributes with Other's so that this symbol becomes
|
||||
// an alias to Other. This is useful for handling some options such as
|
||||
// --wrap.
|
||||
void SymbolBody::copy(SymbolBody *Other) {
|
||||
memcpy(symbol()->Body.buffer, Other->symbol()->Body.buffer,
|
||||
sizeof(Symbol::Body));
|
||||
}
|
||||
|
||||
uint64_t SymbolBody::getVA(int64_t Addend) const {
|
||||
uint64_t OutVA = getSymVA(*this, Addend);
|
||||
return OutVA + Addend;
|
||||
}
|
||||
|
||||
uint64_t SymbolBody::getGotVA() const {
|
||||
return InX::Got->getVA() + getGotOffset();
|
||||
}
|
||||
|
||||
uint64_t SymbolBody::getGotOffset() const {
|
||||
return GotIndex * Target->GotEntrySize;
|
||||
}
|
||||
|
||||
uint64_t SymbolBody::getGotPltVA() const {
|
||||
if (this->IsInIgot)
|
||||
return InX::IgotPlt->getVA() + getGotPltOffset();
|
||||
return InX::GotPlt->getVA() + getGotPltOffset();
|
||||
}
|
||||
|
||||
uint64_t SymbolBody::getGotPltOffset() const {
|
||||
return GotPltIndex * Target->GotPltEntrySize;
|
||||
}
|
||||
|
||||
uint64_t SymbolBody::getPltVA() const {
|
||||
if (this->IsInIplt)
|
||||
return InX::Iplt->getVA() + PltIndex * Target->PltEntrySize;
|
||||
return InX::Plt->getVA() + Target->PltHeaderSize +
|
||||
PltIndex * Target->PltEntrySize;
|
||||
}
|
||||
|
||||
template <class ELFT> typename ELFT::uint SymbolBody::getSize() const {
|
||||
if (const auto *C = dyn_cast<DefinedCommon>(this))
|
||||
return C->Size;
|
||||
if (const auto *DR = dyn_cast<DefinedRegular>(this))
|
||||
return DR->Size;
|
||||
if (const auto *S = dyn_cast<SharedSymbol>(this))
|
||||
return S->getSize<ELFT>();
|
||||
return 0;
|
||||
}
|
||||
|
||||
OutputSection *SymbolBody::getOutputSection() const {
|
||||
if (auto *S = dyn_cast<DefinedRegular>(this)) {
|
||||
if (S->Section)
|
||||
return S->Section->getOutputSection();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (auto *S = dyn_cast<SharedSymbol>(this)) {
|
||||
if (S->NeedsCopy)
|
||||
return S->CopyRelSec->getParent();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (isa<DefinedCommon>(this)) {
|
||||
if (Config->DefineCommon)
|
||||
return InX::Common->getParent();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If a symbol name contains '@', the characters after that is
|
||||
// a symbol version name. This function parses that.
|
||||
void SymbolBody::parseSymbolVersion() {
|
||||
StringRef S = getName();
|
||||
size_t Pos = S.find('@');
|
||||
if (Pos == 0 || Pos == StringRef::npos)
|
||||
return;
|
||||
StringRef Verstr = S.substr(Pos + 1);
|
||||
if (Verstr.empty())
|
||||
return;
|
||||
|
||||
// Truncate the symbol name so that it doesn't include the version string.
|
||||
Name = {S.data(), Pos};
|
||||
|
||||
// If this is not in this DSO, it is not a definition.
|
||||
if (!isInCurrentDSO())
|
||||
return;
|
||||
|
||||
// '@@' in a symbol name means the default version.
|
||||
// It is usually the most recent one.
|
||||
bool IsDefault = (Verstr[0] == '@');
|
||||
if (IsDefault)
|
||||
Verstr = Verstr.substr(1);
|
||||
|
||||
for (VersionDefinition &Ver : Config->VersionDefinitions) {
|
||||
if (Ver.Name != Verstr)
|
||||
continue;
|
||||
|
||||
if (IsDefault)
|
||||
symbol()->VersionId = Ver.Id;
|
||||
else
|
||||
symbol()->VersionId = Ver.Id | VERSYM_HIDDEN;
|
||||
return;
|
||||
}
|
||||
|
||||
// It is an error if the specified version is not defined.
|
||||
// Usually version script is not provided when linking executable,
|
||||
// but we may still want to override a versioned symbol from DSO,
|
||||
// so we do not report error in this case.
|
||||
if (Config->Shared)
|
||||
error(toString(File) + ": symbol " + S + " has undefined version " +
|
||||
Verstr);
|
||||
}
|
||||
|
||||
Defined::Defined(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther,
|
||||
uint8_t Type)
|
||||
: SymbolBody(K, Name, IsLocal, StOther, Type) {}
|
||||
|
||||
template <class ELFT> bool DefinedRegular::isMipsPIC() const {
|
||||
typedef typename ELFT::Ehdr Elf_Ehdr;
|
||||
if (!Section || !isFunc())
|
||||
return false;
|
||||
|
||||
auto *Sec = cast<InputSectionBase>(Section);
|
||||
const Elf_Ehdr *Hdr = Sec->template getFile<ELFT>()->getObj().getHeader();
|
||||
return (this->StOther & STO_MIPS_MIPS16) == STO_MIPS_PIC ||
|
||||
(Hdr->e_flags & EF_MIPS_PIC);
|
||||
}
|
||||
|
||||
Undefined::Undefined(StringRefZ Name, bool IsLocal, uint8_t StOther,
|
||||
uint8_t Type, InputFile *File)
|
||||
: SymbolBody(SymbolBody::UndefinedKind, Name, IsLocal, StOther, Type) {
|
||||
this->File = File;
|
||||
}
|
||||
|
||||
DefinedCommon::DefinedCommon(StringRef Name, uint64_t Size, uint32_t Alignment,
|
||||
uint8_t StOther, uint8_t Type, InputFile *File)
|
||||
: Defined(SymbolBody::DefinedCommonKind, Name, /*IsLocal=*/false, StOther,
|
||||
Type),
|
||||
Alignment(Alignment), Size(Size) {
|
||||
this->File = File;
|
||||
}
|
||||
|
||||
// If a shared symbol is referred via a copy relocation, its alignment
|
||||
// becomes part of the ABI. This function returns a symbol alignment.
|
||||
// Because symbols don't have alignment attributes, we need to infer that.
|
||||
template <class ELFT> uint32_t SharedSymbol::getAlignment() const {
|
||||
auto *File = cast<SharedFile<ELFT>>(this->File);
|
||||
uint32_t SecAlign = File->getSection(getSym<ELFT>())->sh_addralign;
|
||||
uint64_t SymValue = getSym<ELFT>().st_value;
|
||||
uint32_t SymAlign = uint32_t(1) << countTrailingZeros(SymValue);
|
||||
return std::min(SecAlign, SymAlign);
|
||||
}
|
||||
|
||||
InputFile *Lazy::fetch() {
|
||||
if (auto *S = dyn_cast<LazyArchive>(this))
|
||||
return S->fetch();
|
||||
return cast<LazyObject>(this)->fetch();
|
||||
}
|
||||
|
||||
LazyArchive::LazyArchive(ArchiveFile &File,
|
||||
const llvm::object::Archive::Symbol S, uint8_t Type)
|
||||
: Lazy(LazyArchiveKind, S.getName(), Type), Sym(S) {
|
||||
this->File = &File;
|
||||
}
|
||||
|
||||
LazyObject::LazyObject(StringRef Name, LazyObjectFile &File, uint8_t Type)
|
||||
: Lazy(LazyObjectKind, Name, Type) {
|
||||
this->File = &File;
|
||||
}
|
||||
|
||||
InputFile *LazyArchive::fetch() {
|
||||
std::pair<MemoryBufferRef, uint64_t> MBInfo = file()->getMember(&Sym);
|
||||
|
||||
// getMember returns an empty buffer if the member was already
|
||||
// read from the library.
|
||||
if (MBInfo.first.getBuffer().empty())
|
||||
return nullptr;
|
||||
return createObjectFile(MBInfo.first, file()->getName(), MBInfo.second);
|
||||
}
|
||||
|
||||
InputFile *LazyObject::fetch() { return file()->fetch(); }
|
||||
|
||||
uint8_t Symbol::computeBinding() const {
|
||||
if (Config->Relocatable)
|
||||
return Binding;
|
||||
if (Visibility != STV_DEFAULT && Visibility != STV_PROTECTED)
|
||||
return STB_LOCAL;
|
||||
if (VersionId == VER_NDX_LOCAL && body()->isInCurrentDSO())
|
||||
return STB_LOCAL;
|
||||
if (Config->NoGnuUnique && Binding == STB_GNU_UNIQUE)
|
||||
return STB_GLOBAL;
|
||||
return Binding;
|
||||
}
|
||||
|
||||
bool Symbol::includeInDynsym() const {
|
||||
if (computeBinding() == STB_LOCAL)
|
||||
return false;
|
||||
return ExportDynamic || body()->isShared() ||
|
||||
(body()->isUndefined() && Config->Shared);
|
||||
}
|
||||
|
||||
// Print out a log message for --trace-symbol.
|
||||
void elf::printTraceSymbol(Symbol *Sym) {
|
||||
SymbolBody *B = Sym->body();
|
||||
std::string S;
|
||||
if (B->isUndefined())
|
||||
S = ": reference to ";
|
||||
else if (B->isCommon())
|
||||
S = ": common definition of ";
|
||||
else
|
||||
S = ": definition of ";
|
||||
|
||||
message(toString(B->File) + S + B->getName());
|
||||
}
|
||||
|
||||
// Returns a symbol for an error message.
|
||||
std::string lld::toString(const SymbolBody &B) {
|
||||
if (Config->Demangle)
|
||||
if (Optional<std::string> S = demangle(B.getName()))
|
||||
return *S;
|
||||
return B.getName();
|
||||
}
|
||||
|
||||
template uint32_t SymbolBody::template getSize<ELF32LE>() const;
|
||||
template uint32_t SymbolBody::template getSize<ELF32BE>() const;
|
||||
template uint64_t SymbolBody::template getSize<ELF64LE>() const;
|
||||
template uint64_t SymbolBody::template getSize<ELF64BE>() const;
|
||||
|
||||
template bool DefinedRegular::template isMipsPIC<ELF32LE>() const;
|
||||
template bool DefinedRegular::template isMipsPIC<ELF32BE>() const;
|
||||
template bool DefinedRegular::template isMipsPIC<ELF64LE>() const;
|
||||
template bool DefinedRegular::template isMipsPIC<ELF64BE>() const;
|
||||
|
||||
template uint32_t SharedSymbol::template getAlignment<ELF32LE>() const;
|
||||
template uint32_t SharedSymbol::template getAlignment<ELF32BE>() const;
|
||||
template uint32_t SharedSymbol::template getAlignment<ELF64LE>() const;
|
||||
template uint32_t SharedSymbol::template getAlignment<ELF64BE>() const;
|
||||
414
deps/lld/ELF/Symbols.h
vendored
Normal file
414
deps/lld/ELF/Symbols.h
vendored
Normal file
@ -0,0 +1,414 @@
|
||||
//===- Symbols.h ------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// All symbols are handled as SymbolBodies regardless of their types.
|
||||
// This file defines various types of SymbolBodies.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_SYMBOLS_H
|
||||
#define LLD_ELF_SYMBOLS_H
|
||||
|
||||
#include "InputSection.h"
|
||||
#include "Strings.h"
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/Object/Archive.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
class ArchiveFile;
|
||||
class BitcodeFile;
|
||||
class InputFile;
|
||||
class LazyObjectFile;
|
||||
template <class ELFT> class ObjectFile;
|
||||
class OutputSection;
|
||||
template <class ELFT> class SharedFile;
|
||||
|
||||
struct Symbol;
|
||||
|
||||
// The base class for real symbol classes.
|
||||
class SymbolBody {
|
||||
public:
|
||||
enum Kind {
|
||||
DefinedFirst,
|
||||
DefinedRegularKind = DefinedFirst,
|
||||
SharedKind,
|
||||
DefinedCommonKind,
|
||||
DefinedLast = DefinedCommonKind,
|
||||
UndefinedKind,
|
||||
LazyArchiveKind,
|
||||
LazyObjectKind,
|
||||
};
|
||||
|
||||
SymbolBody(Kind K) : SymbolKind(K) {}
|
||||
|
||||
Symbol *symbol();
|
||||
const Symbol *symbol() const {
|
||||
return const_cast<SymbolBody *>(this)->symbol();
|
||||
}
|
||||
|
||||
Kind kind() const { return static_cast<Kind>(SymbolKind); }
|
||||
|
||||
bool isUndefined() const { return SymbolKind == UndefinedKind; }
|
||||
bool isDefined() const { return SymbolKind <= DefinedLast; }
|
||||
bool isCommon() const { return SymbolKind == DefinedCommonKind; }
|
||||
bool isLazy() const {
|
||||
return SymbolKind == LazyArchiveKind || SymbolKind == LazyObjectKind;
|
||||
}
|
||||
bool isShared() const { return SymbolKind == SharedKind; }
|
||||
bool isInCurrentDSO() const {
|
||||
return !isUndefined() && !isShared() && !isLazy();
|
||||
}
|
||||
bool isLocal() const { return IsLocal; }
|
||||
bool isPreemptible() const;
|
||||
StringRef getName() const { return Name; }
|
||||
uint8_t getVisibility() const { return StOther & 0x3; }
|
||||
void parseSymbolVersion();
|
||||
void copy(SymbolBody *Other);
|
||||
|
||||
bool isInGot() const { return GotIndex != -1U; }
|
||||
bool isInPlt() const { return PltIndex != -1U; }
|
||||
|
||||
uint64_t getVA(int64_t Addend = 0) const;
|
||||
|
||||
uint64_t getGotOffset() const;
|
||||
uint64_t getGotVA() const;
|
||||
uint64_t getGotPltOffset() const;
|
||||
uint64_t getGotPltVA() const;
|
||||
uint64_t getPltVA() const;
|
||||
template <class ELFT> typename ELFT::uint getSize() const;
|
||||
OutputSection *getOutputSection() const;
|
||||
|
||||
// The file from which this symbol was created.
|
||||
InputFile *File = nullptr;
|
||||
|
||||
uint32_t DynsymIndex = 0;
|
||||
uint32_t GotIndex = -1;
|
||||
uint32_t GotPltIndex = -1;
|
||||
uint32_t PltIndex = -1;
|
||||
uint32_t GlobalDynIndex = -1;
|
||||
|
||||
protected:
|
||||
SymbolBody(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther,
|
||||
uint8_t Type);
|
||||
|
||||
const unsigned SymbolKind : 8;
|
||||
|
||||
public:
|
||||
// True if the linker has to generate a copy relocation.
|
||||
// For SharedSymbol only.
|
||||
unsigned NeedsCopy : 1;
|
||||
|
||||
// True the symbol should point to its PLT entry.
|
||||
// For SharedSymbol only.
|
||||
unsigned NeedsPltAddr : 1;
|
||||
|
||||
// True if this is a local symbol.
|
||||
unsigned IsLocal : 1;
|
||||
|
||||
// True if this symbol has an entry in the global part of MIPS GOT.
|
||||
unsigned IsInGlobalMipsGot : 1;
|
||||
|
||||
// True if this symbol is referenced by 32-bit GOT relocations.
|
||||
unsigned Is32BitMipsGot : 1;
|
||||
|
||||
// True if this symbol is in the Iplt sub-section of the Plt.
|
||||
unsigned IsInIplt : 1;
|
||||
|
||||
// True if this symbol is in the Igot sub-section of the .got.plt or .got.
|
||||
unsigned IsInIgot : 1;
|
||||
|
||||
// The following fields have the same meaning as the ELF symbol attributes.
|
||||
uint8_t Type; // symbol type
|
||||
uint8_t StOther; // st_other field value
|
||||
|
||||
// The Type field may also have this value. It means that we have not yet seen
|
||||
// a non-Lazy symbol with this name, so we don't know what its type is. The
|
||||
// Type field is normally set to this value for Lazy symbols unless we saw a
|
||||
// weak undefined symbol first, in which case we need to remember the original
|
||||
// symbol's type in order to check for TLS mismatches.
|
||||
enum { UnknownType = 255 };
|
||||
|
||||
bool isSection() const { return Type == llvm::ELF::STT_SECTION; }
|
||||
bool isTls() const { return Type == llvm::ELF::STT_TLS; }
|
||||
bool isFunc() const { return Type == llvm::ELF::STT_FUNC; }
|
||||
bool isGnuIFunc() const { return Type == llvm::ELF::STT_GNU_IFUNC; }
|
||||
bool isObject() const { return Type == llvm::ELF::STT_OBJECT; }
|
||||
bool isFile() const { return Type == llvm::ELF::STT_FILE; }
|
||||
|
||||
protected:
|
||||
StringRefZ Name;
|
||||
};
|
||||
|
||||
// The base class for any defined symbols.
|
||||
class Defined : public SymbolBody {
|
||||
public:
|
||||
Defined(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther, uint8_t Type);
|
||||
static bool classof(const SymbolBody *S) { return S->isDefined(); }
|
||||
};
|
||||
|
||||
class DefinedCommon : public Defined {
|
||||
public:
|
||||
DefinedCommon(StringRef N, uint64_t Size, uint32_t Alignment, uint8_t StOther,
|
||||
uint8_t Type, InputFile *File);
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() == SymbolBody::DefinedCommonKind;
|
||||
}
|
||||
|
||||
// The output offset of this common symbol in the output bss. Computed by the
|
||||
// writer.
|
||||
uint64_t Offset;
|
||||
|
||||
// The maximum alignment we have seen for this symbol.
|
||||
uint32_t Alignment;
|
||||
|
||||
uint64_t Size;
|
||||
};
|
||||
|
||||
// Regular defined symbols read from object file symbol tables.
|
||||
class DefinedRegular : public Defined {
|
||||
public:
|
||||
DefinedRegular(StringRefZ Name, bool IsLocal, uint8_t StOther, uint8_t Type,
|
||||
uint64_t Value, uint64_t Size, SectionBase *Section,
|
||||
InputFile *File)
|
||||
: Defined(SymbolBody::DefinedRegularKind, Name, IsLocal, StOther, Type),
|
||||
Value(Value), Size(Size), Section(Section) {
|
||||
this->File = File;
|
||||
}
|
||||
|
||||
// Return true if the symbol is a PIC function.
|
||||
template <class ELFT> bool isMipsPIC() const;
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() == SymbolBody::DefinedRegularKind;
|
||||
}
|
||||
|
||||
uint64_t Value;
|
||||
uint64_t Size;
|
||||
SectionBase *Section;
|
||||
};
|
||||
|
||||
class Undefined : public SymbolBody {
|
||||
public:
|
||||
Undefined(StringRefZ Name, bool IsLocal, uint8_t StOther, uint8_t Type,
|
||||
InputFile *F);
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() == UndefinedKind;
|
||||
}
|
||||
};
|
||||
|
||||
class SharedSymbol : public Defined {
|
||||
public:
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() == SymbolBody::SharedKind;
|
||||
}
|
||||
|
||||
SharedSymbol(InputFile *File, StringRef Name, uint8_t StOther, uint8_t Type,
|
||||
const void *ElfSym, const void *Verdef)
|
||||
: Defined(SymbolBody::SharedKind, Name, /*IsLocal=*/false, StOther, Type),
|
||||
Verdef(Verdef), ElfSym(ElfSym) {
|
||||
// IFuncs defined in DSOs are treated as functions by the static linker.
|
||||
if (isGnuIFunc())
|
||||
this->Type = llvm::ELF::STT_FUNC;
|
||||
this->File = File;
|
||||
}
|
||||
|
||||
template <class ELFT> uint64_t getShndx() const {
|
||||
return getSym<ELFT>().st_shndx;
|
||||
}
|
||||
|
||||
template <class ELFT> uint64_t getValue() const {
|
||||
return getSym<ELFT>().st_value;
|
||||
}
|
||||
|
||||
template <class ELFT> uint64_t getSize() const {
|
||||
return getSym<ELFT>().st_size;
|
||||
}
|
||||
|
||||
template <class ELFT> uint32_t getAlignment() const;
|
||||
|
||||
// This field is a pointer to the symbol's version definition.
|
||||
const void *Verdef;
|
||||
|
||||
// CopyRelSec and CopyRelSecOff are significant only when NeedsCopy is true.
|
||||
InputSection *CopyRelSec;
|
||||
uint64_t CopyRelSecOff;
|
||||
|
||||
private:
|
||||
template <class ELFT> const typename ELFT::Sym &getSym() const {
|
||||
return *(const typename ELFT::Sym *)ElfSym;
|
||||
}
|
||||
|
||||
const void *ElfSym;
|
||||
};
|
||||
|
||||
// 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 SymbolBody {
|
||||
public:
|
||||
static bool classof(const SymbolBody *S) { return S->isLazy(); }
|
||||
|
||||
// Returns an object file for this symbol, or a nullptr if the file
|
||||
// was already returned.
|
||||
InputFile *fetch();
|
||||
|
||||
protected:
|
||||
Lazy(SymbolBody::Kind K, StringRef Name, uint8_t Type)
|
||||
: SymbolBody(K, Name, /*IsLocal=*/false, llvm::ELF::STV_DEFAULT, Type) {}
|
||||
};
|
||||
|
||||
// LazyArchive symbols represents symbols in archive files.
|
||||
class LazyArchive : public Lazy {
|
||||
public:
|
||||
LazyArchive(ArchiveFile &File, const llvm::object::Archive::Symbol S,
|
||||
uint8_t Type);
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() == LazyArchiveKind;
|
||||
}
|
||||
|
||||
ArchiveFile *file() { return (ArchiveFile *)this->File; }
|
||||
InputFile *fetch();
|
||||
|
||||
private:
|
||||
const llvm::object::Archive::Symbol Sym;
|
||||
};
|
||||
|
||||
// LazyObject symbols represents symbols in object files between
|
||||
// --start-lib and --end-lib options.
|
||||
class LazyObject : public Lazy {
|
||||
public:
|
||||
LazyObject(StringRef Name, LazyObjectFile &File, uint8_t Type);
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() == LazyObjectKind;
|
||||
}
|
||||
|
||||
LazyObjectFile *file() { return (LazyObjectFile *)this->File; }
|
||||
InputFile *fetch();
|
||||
};
|
||||
|
||||
// Some linker-generated symbols need to be created as
|
||||
// DefinedRegular symbols.
|
||||
struct ElfSym {
|
||||
// __bss_start
|
||||
static DefinedRegular *Bss;
|
||||
|
||||
// etext and _etext
|
||||
static DefinedRegular *Etext1;
|
||||
static DefinedRegular *Etext2;
|
||||
|
||||
// edata and _edata
|
||||
static DefinedRegular *Edata1;
|
||||
static DefinedRegular *Edata2;
|
||||
|
||||
// end and _end
|
||||
static DefinedRegular *End1;
|
||||
static DefinedRegular *End2;
|
||||
|
||||
// The _GLOBAL_OFFSET_TABLE_ symbol is defined by target convention to
|
||||
// be at some offset from the base of the .got section, usually 0 or
|
||||
// the end of the .got.
|
||||
static DefinedRegular *GlobalOffsetTable;
|
||||
|
||||
// _gp, _gp_disp and __gnu_local_gp symbols. Only for MIPS.
|
||||
static DefinedRegular *MipsGp;
|
||||
static DefinedRegular *MipsGpDisp;
|
||||
static DefinedRegular *MipsLocalGp;
|
||||
};
|
||||
|
||||
// A real symbol object, SymbolBody, is usually stored within a Symbol. There's
|
||||
// always one Symbol for each symbol name. The resolver updates the SymbolBody
|
||||
// stored in the Body field of this object as it resolves symbols. Symbol also
|
||||
// holds computed properties of symbol names.
|
||||
struct Symbol {
|
||||
// Symbol binding. This is on the Symbol to track changes during resolution.
|
||||
// In particular:
|
||||
// An undefined weak is still weak when it resolves to a shared library.
|
||||
// An undefined weak will not fetch archive members, but we have to remember
|
||||
// it is weak.
|
||||
uint8_t Binding;
|
||||
|
||||
// Version definition index.
|
||||
uint16_t VersionId;
|
||||
|
||||
// Symbol visibility. This is the computed minimum visibility of all
|
||||
// observed non-DSO symbols.
|
||||
unsigned Visibility : 2;
|
||||
|
||||
// True if the symbol was used for linking and thus need to be added to the
|
||||
// output file's symbol table. This is true for all symbols except for
|
||||
// unreferenced DSO symbols and bitcode symbols that are unreferenced except
|
||||
// by other bitcode objects.
|
||||
unsigned IsUsedInRegularObj : 1;
|
||||
|
||||
// If this flag is true and the symbol has protected or default visibility, it
|
||||
// will appear in .dynsym. This flag is set by interposable DSO symbols in
|
||||
// executables, by most symbols in DSOs and executables built with
|
||||
// --export-dynamic, and by dynamic lists.
|
||||
unsigned ExportDynamic : 1;
|
||||
|
||||
// True if this symbol is specified by --trace-symbol option.
|
||||
unsigned Traced : 1;
|
||||
|
||||
// This symbol version was found in a version script.
|
||||
unsigned InVersionScript : 1;
|
||||
|
||||
bool includeInDynsym() const;
|
||||
uint8_t computeBinding() const;
|
||||
bool isWeak() const { return Binding == llvm::ELF::STB_WEAK; }
|
||||
|
||||
// This field is used to store the Symbol's SymbolBody. This instantiation of
|
||||
// AlignedCharArrayUnion gives us a struct with a char array field that is
|
||||
// large and aligned enough to store any derived class of SymbolBody.
|
||||
llvm::AlignedCharArrayUnion<DefinedCommon, DefinedRegular, Undefined,
|
||||
SharedSymbol, LazyArchive, LazyObject>
|
||||
Body;
|
||||
|
||||
SymbolBody *body() { return reinterpret_cast<SymbolBody *>(Body.buffer); }
|
||||
const SymbolBody *body() const { return const_cast<Symbol *>(this)->body(); }
|
||||
};
|
||||
|
||||
void printTraceSymbol(Symbol *Sym);
|
||||
|
||||
template <typename T, typename... ArgT>
|
||||
void replaceBody(Symbol *S, ArgT &&... Arg) {
|
||||
static_assert(sizeof(T) <= sizeof(S->Body), "Body too small");
|
||||
static_assert(alignof(T) <= alignof(decltype(S->Body)),
|
||||
"Body not aligned enough");
|
||||
assert(static_cast<SymbolBody *>(static_cast<T *>(nullptr)) == nullptr &&
|
||||
"Not a SymbolBody");
|
||||
|
||||
new (S->Body.buffer) T(std::forward<ArgT>(Arg)...);
|
||||
|
||||
// Print out a log message if --trace-symbol was specified.
|
||||
// This is for debugging.
|
||||
if (S->Traced)
|
||||
printTraceSymbol(S);
|
||||
}
|
||||
|
||||
inline Symbol *SymbolBody::symbol() {
|
||||
assert(!isLocal());
|
||||
return reinterpret_cast<Symbol *>(reinterpret_cast<char *>(this) -
|
||||
offsetof(Symbol, Body));
|
||||
}
|
||||
} // namespace elf
|
||||
|
||||
std::string toString(const elf::SymbolBody &B);
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
2431
deps/lld/ELF/SyntheticSections.cpp
vendored
Normal file
2431
deps/lld/ELF/SyntheticSections.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
805
deps/lld/ELF/SyntheticSections.h
vendored
Normal file
805
deps/lld/ELF/SyntheticSections.h
vendored
Normal file
@ -0,0 +1,805 @@
|
||||
//===- SyntheticSection.h ---------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Synthetic sections represent chunks of linker-created data. If you
|
||||
// need to create a chunk of data that to be included in some section
|
||||
// in the result, you probably want to create that as a synthetic section.
|
||||
//
|
||||
// Synthetic sections are designed as input sections as opposed to
|
||||
// output sections because we want to allow them to be manipulated
|
||||
// using linker scripts just like other input sections from regular
|
||||
// files.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_SYNTHETIC_SECTION_H
|
||||
#define LLD_ELF_SYNTHETIC_SECTION_H
|
||||
|
||||
#include "EhFrame.h"
|
||||
#include "GdbIndex.h"
|
||||
#include "InputSection.h"
|
||||
#include "llvm/ADT/MapVector.h"
|
||||
#include "llvm/MC/StringTableBuilder.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
class SyntheticSection : public InputSection {
|
||||
public:
|
||||
SyntheticSection(uint64_t Flags, uint32_t Type, uint32_t Alignment,
|
||||
StringRef Name)
|
||||
: InputSection(Flags, Type, Alignment, {}, Name,
|
||||
InputSectionBase::Synthetic) {
|
||||
this->Live = true;
|
||||
}
|
||||
|
||||
virtual ~SyntheticSection() = default;
|
||||
virtual void writeTo(uint8_t *Buf) = 0;
|
||||
virtual size_t getSize() const = 0;
|
||||
virtual void finalizeContents() {}
|
||||
// If the section has the SHF_ALLOC flag and the size may be changed if
|
||||
// thunks are added, update the section size.
|
||||
virtual void updateAllocSize() {}
|
||||
// If any additional finalization of contents are needed post thunk creation.
|
||||
virtual void postThunkContents() {}
|
||||
virtual bool empty() const { return false; }
|
||||
uint64_t getVA() const;
|
||||
|
||||
static bool classof(const SectionBase *D) {
|
||||
return D->kind() == InputSectionBase::Synthetic;
|
||||
}
|
||||
};
|
||||
|
||||
struct CieRecord {
|
||||
EhSectionPiece *Piece = nullptr;
|
||||
std::vector<EhSectionPiece *> FdePieces;
|
||||
};
|
||||
|
||||
// Section for .eh_frame.
|
||||
template <class ELFT> class EhFrameSection final : public SyntheticSection {
|
||||
typedef typename ELFT::Shdr Elf_Shdr;
|
||||
typedef typename ELFT::Rel Elf_Rel;
|
||||
typedef typename ELFT::Rela Elf_Rela;
|
||||
|
||||
void updateAlignment(uint64_t Val) {
|
||||
if (Val > this->Alignment)
|
||||
this->Alignment = Val;
|
||||
}
|
||||
|
||||
public:
|
||||
EhFrameSection();
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
void finalizeContents() override;
|
||||
bool empty() const override { return Sections.empty(); }
|
||||
size_t getSize() const override { return Size; }
|
||||
|
||||
void addSection(InputSectionBase *S);
|
||||
|
||||
size_t NumFdes = 0;
|
||||
|
||||
std::vector<EhInputSection *> Sections;
|
||||
|
||||
private:
|
||||
uint64_t Size = 0;
|
||||
template <class RelTy>
|
||||
void addSectionAux(EhInputSection *S, llvm::ArrayRef<RelTy> Rels);
|
||||
|
||||
template <class RelTy>
|
||||
CieRecord *addCie(EhSectionPiece &Piece, ArrayRef<RelTy> Rels);
|
||||
|
||||
template <class RelTy>
|
||||
bool isFdeLive(EhSectionPiece &Piece, ArrayRef<RelTy> Rels);
|
||||
|
||||
uint64_t getFdePc(uint8_t *Buf, size_t Off, uint8_t Enc);
|
||||
|
||||
std::vector<CieRecord *> Cies;
|
||||
|
||||
// CIE records are uniquified by their contents and personality functions.
|
||||
llvm::DenseMap<std::pair<ArrayRef<uint8_t>, SymbolBody *>, CieRecord> CieMap;
|
||||
};
|
||||
|
||||
class GotSection : public SyntheticSection {
|
||||
public:
|
||||
GotSection();
|
||||
size_t getSize() const override { return Size; }
|
||||
void finalizeContents() override;
|
||||
bool empty() const override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
|
||||
void addEntry(SymbolBody &Sym);
|
||||
bool addDynTlsEntry(SymbolBody &Sym);
|
||||
bool addTlsIndex();
|
||||
uint64_t getGlobalDynAddr(const SymbolBody &B) const;
|
||||
uint64_t getGlobalDynOffset(const SymbolBody &B) const;
|
||||
|
||||
uint64_t getTlsIndexVA() { return this->getVA() + TlsIndexOff; }
|
||||
uint32_t getTlsIndexOff() const { return TlsIndexOff; }
|
||||
|
||||
// Flag to force GOT to be in output if we have relocations
|
||||
// that relies on its address.
|
||||
bool HasGotOffRel = false;
|
||||
|
||||
protected:
|
||||
size_t NumEntries = 0;
|
||||
uint32_t TlsIndexOff = -1;
|
||||
uint64_t Size = 0;
|
||||
};
|
||||
|
||||
// .note.gnu.build-id section.
|
||||
class BuildIdSection : public SyntheticSection {
|
||||
// First 16 bytes are a header.
|
||||
static const unsigned HeaderSize = 16;
|
||||
|
||||
public:
|
||||
BuildIdSection();
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
size_t getSize() const override { return HeaderSize + HashSize; }
|
||||
void writeBuildId(llvm::ArrayRef<uint8_t> Buf);
|
||||
|
||||
private:
|
||||
void computeHash(llvm::ArrayRef<uint8_t> Buf,
|
||||
std::function<void(uint8_t *, ArrayRef<uint8_t>)> Hash);
|
||||
|
||||
size_t HashSize;
|
||||
uint8_t *HashBuf;
|
||||
};
|
||||
|
||||
// BssSection is used to reserve space for copy relocations and common symbols.
|
||||
// We create three instances of this class for .bss, .bss.rel.ro and "COMMON",
|
||||
// that are used for writable symbols, read-only symbols and common symbols,
|
||||
// respectively.
|
||||
class BssSection final : public SyntheticSection {
|
||||
public:
|
||||
BssSection(StringRef Name);
|
||||
void writeTo(uint8_t *) override {}
|
||||
bool empty() const override { return getSize() == 0; }
|
||||
size_t reserveSpace(uint64_t Size, uint32_t Alignment);
|
||||
size_t getSize() const override { return Size; }
|
||||
|
||||
private:
|
||||
uint64_t Size = 0;
|
||||
};
|
||||
|
||||
class MipsGotSection final : public SyntheticSection {
|
||||
public:
|
||||
MipsGotSection();
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
size_t getSize() const override { return Size; }
|
||||
void updateAllocSize() override;
|
||||
void finalizeContents() override;
|
||||
bool empty() const override;
|
||||
void addEntry(SymbolBody &Sym, int64_t Addend, RelExpr Expr);
|
||||
bool addDynTlsEntry(SymbolBody &Sym);
|
||||
bool addTlsIndex();
|
||||
uint64_t getPageEntryOffset(const SymbolBody &B, int64_t Addend) const;
|
||||
uint64_t getBodyEntryOffset(const SymbolBody &B, int64_t Addend) const;
|
||||
uint64_t getGlobalDynOffset(const SymbolBody &B) const;
|
||||
|
||||
// Returns the symbol which corresponds to the first entry of the global part
|
||||
// of GOT on MIPS platform. It is required to fill up MIPS-specific dynamic
|
||||
// table properties.
|
||||
// Returns nullptr if the global part is empty.
|
||||
const SymbolBody *getFirstGlobalEntry() const;
|
||||
|
||||
// Returns the number of entries in the local part of GOT including
|
||||
// the number of reserved entries.
|
||||
unsigned getLocalEntriesNum() const;
|
||||
|
||||
// Returns offset of TLS part of the MIPS GOT table. This part goes
|
||||
// after 'local' and 'global' entries.
|
||||
uint64_t getTlsOffset() const;
|
||||
|
||||
uint32_t getTlsIndexOff() const { return TlsIndexOff; }
|
||||
|
||||
uint64_t getGp() const;
|
||||
|
||||
private:
|
||||
// MIPS GOT consists of three parts: local, global and tls. Each part
|
||||
// contains different types of entries. Here is a layout of GOT:
|
||||
// - Header entries |
|
||||
// - Page entries | Local part
|
||||
// - Local entries (16-bit access) |
|
||||
// - Local entries (32-bit access) |
|
||||
// - Normal global entries || Global part
|
||||
// - Reloc-only global entries ||
|
||||
// - TLS entries ||| TLS part
|
||||
//
|
||||
// Header:
|
||||
// Two entries hold predefined value 0x0 and 0x80000000.
|
||||
// Page entries:
|
||||
// These entries created by R_MIPS_GOT_PAGE relocation and R_MIPS_GOT16
|
||||
// relocation against local symbols. They are initialized by higher 16-bit
|
||||
// of the corresponding symbol's value. So each 64kb of address space
|
||||
// requires a single GOT entry.
|
||||
// Local entries (16-bit access):
|
||||
// These entries created by GOT relocations against global non-preemptible
|
||||
// symbols so dynamic linker is not necessary to resolve the symbol's
|
||||
// values. "16-bit access" means that corresponding relocations address
|
||||
// GOT using 16-bit index. Each unique Symbol-Addend pair has its own
|
||||
// GOT entry.
|
||||
// Local entries (32-bit access):
|
||||
// These entries are the same as above but created by relocations which
|
||||
// address GOT using 32-bit index (R_MIPS_GOT_HI16/LO16 etc).
|
||||
// Normal global entries:
|
||||
// These entries created by GOT relocations against preemptible global
|
||||
// symbols. They need to be initialized by dynamic linker and they ordered
|
||||
// exactly as the corresponding entries in the dynamic symbols table.
|
||||
// Reloc-only global entries:
|
||||
// These entries created for symbols that are referenced by dynamic
|
||||
// relocations R_MIPS_REL32. These entries are not accessed with gp-relative
|
||||
// addressing, but MIPS ABI requires that these entries be present in GOT.
|
||||
// TLS entries:
|
||||
// Entries created by TLS relocations.
|
||||
|
||||
// Number of "Header" entries.
|
||||
static const unsigned HeaderEntriesNum = 2;
|
||||
// Number of allocated "Page" entries.
|
||||
uint32_t PageEntriesNum = 0;
|
||||
// Map output sections referenced by MIPS GOT relocations
|
||||
// to the first index of "Page" entries allocated for this section.
|
||||
llvm::SmallMapVector<const OutputSection *, size_t, 16> PageIndexMap;
|
||||
|
||||
typedef std::pair<const SymbolBody *, uint64_t> GotEntry;
|
||||
typedef std::vector<GotEntry> GotEntries;
|
||||
// Map from Symbol-Addend pair to the GOT index.
|
||||
llvm::DenseMap<GotEntry, size_t> EntryIndexMap;
|
||||
// Local entries (16-bit access).
|
||||
GotEntries LocalEntries;
|
||||
// Local entries (32-bit access).
|
||||
GotEntries LocalEntries32;
|
||||
|
||||
// Normal and reloc-only global entries.
|
||||
GotEntries GlobalEntries;
|
||||
|
||||
// TLS entries.
|
||||
std::vector<const SymbolBody *> TlsEntries;
|
||||
|
||||
uint32_t TlsIndexOff = -1;
|
||||
uint64_t Size = 0;
|
||||
};
|
||||
|
||||
class GotPltSection final : public SyntheticSection {
|
||||
public:
|
||||
GotPltSection();
|
||||
void addEntry(SymbolBody &Sym);
|
||||
size_t getSize() const override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
bool empty() const override { return Entries.empty(); }
|
||||
|
||||
private:
|
||||
std::vector<const SymbolBody *> Entries;
|
||||
};
|
||||
|
||||
// The IgotPltSection is a Got associated with the PltSection for GNU Ifunc
|
||||
// Symbols that will be relocated by Target->IRelativeRel.
|
||||
// On most Targets the IgotPltSection will immediately follow the GotPltSection
|
||||
// on ARM the IgotPltSection will immediately follow the GotSection.
|
||||
class IgotPltSection final : public SyntheticSection {
|
||||
public:
|
||||
IgotPltSection();
|
||||
void addEntry(SymbolBody &Sym);
|
||||
size_t getSize() const override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
bool empty() const override { return Entries.empty(); }
|
||||
|
||||
private:
|
||||
std::vector<const SymbolBody *> Entries;
|
||||
};
|
||||
|
||||
class StringTableSection final : public SyntheticSection {
|
||||
public:
|
||||
StringTableSection(StringRef Name, bool Dynamic);
|
||||
unsigned addString(StringRef S, bool HashIt = true);
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
size_t getSize() const override { return Size; }
|
||||
bool isDynamic() const { return Dynamic; }
|
||||
|
||||
private:
|
||||
const bool Dynamic;
|
||||
|
||||
uint64_t Size = 0;
|
||||
|
||||
llvm::DenseMap<StringRef, unsigned> StringMap;
|
||||
std::vector<StringRef> Strings;
|
||||
};
|
||||
|
||||
class DynamicReloc {
|
||||
public:
|
||||
DynamicReloc(uint32_t Type, const InputSectionBase *InputSec,
|
||||
uint64_t OffsetInSec, bool UseSymVA, SymbolBody *Sym,
|
||||
int64_t Addend)
|
||||
: Type(Type), Sym(Sym), InputSec(InputSec), OffsetInSec(OffsetInSec),
|
||||
UseSymVA(UseSymVA), Addend(Addend) {}
|
||||
|
||||
uint64_t getOffset() const;
|
||||
int64_t getAddend() const;
|
||||
uint32_t getSymIndex() const;
|
||||
const InputSectionBase *getInputSec() const { return InputSec; }
|
||||
|
||||
uint32_t Type;
|
||||
|
||||
private:
|
||||
SymbolBody *Sym;
|
||||
const InputSectionBase *InputSec = nullptr;
|
||||
uint64_t OffsetInSec;
|
||||
bool UseSymVA;
|
||||
int64_t Addend;
|
||||
};
|
||||
|
||||
template <class ELFT> class DynamicSection final : public SyntheticSection {
|
||||
typedef typename ELFT::Dyn Elf_Dyn;
|
||||
typedef typename ELFT::Rel Elf_Rel;
|
||||
typedef typename ELFT::Rela Elf_Rela;
|
||||
typedef typename ELFT::Shdr Elf_Shdr;
|
||||
typedef typename ELFT::Sym Elf_Sym;
|
||||
|
||||
// The .dynamic section contains information for the dynamic linker.
|
||||
// The section consists of fixed size entries, which consist of
|
||||
// type and value fields. Value are one of plain integers, symbol
|
||||
// addresses, or section addresses. This struct represents the entry.
|
||||
struct Entry {
|
||||
int32_t Tag;
|
||||
union {
|
||||
OutputSection *OutSec;
|
||||
InputSection *InSec;
|
||||
uint64_t Val;
|
||||
const SymbolBody *Sym;
|
||||
};
|
||||
enum KindT { SecAddr, SecSize, SymAddr, PlainInt, InSecAddr } Kind;
|
||||
Entry(int32_t Tag, OutputSection *OutSec, KindT Kind = SecAddr)
|
||||
: Tag(Tag), OutSec(OutSec), Kind(Kind) {}
|
||||
Entry(int32_t Tag, InputSection *Sec)
|
||||
: Tag(Tag), InSec(Sec), Kind(InSecAddr) {}
|
||||
Entry(int32_t Tag, uint64_t Val) : Tag(Tag), Val(Val), Kind(PlainInt) {}
|
||||
Entry(int32_t Tag, const SymbolBody *Sym)
|
||||
: Tag(Tag), Sym(Sym), Kind(SymAddr) {}
|
||||
};
|
||||
|
||||
// finalizeContents() fills this vector with the section contents.
|
||||
std::vector<Entry> Entries;
|
||||
|
||||
public:
|
||||
DynamicSection();
|
||||
void finalizeContents() override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
size_t getSize() const override { return Size; }
|
||||
|
||||
private:
|
||||
void addEntries();
|
||||
void add(Entry E) { Entries.push_back(E); }
|
||||
uint64_t Size = 0;
|
||||
};
|
||||
|
||||
template <class ELFT> class RelocationSection final : public SyntheticSection {
|
||||
typedef typename ELFT::Rel Elf_Rel;
|
||||
typedef typename ELFT::Rela Elf_Rela;
|
||||
|
||||
public:
|
||||
RelocationSection(StringRef Name, bool Sort);
|
||||
void addReloc(const DynamicReloc &Reloc);
|
||||
unsigned getRelocOffset();
|
||||
void finalizeContents() override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
bool empty() const override { return Relocs.empty(); }
|
||||
size_t getSize() const override { return Relocs.size() * this->Entsize; }
|
||||
size_t getRelativeRelocCount() const { return NumRelativeRelocs; }
|
||||
|
||||
private:
|
||||
bool Sort;
|
||||
size_t NumRelativeRelocs = 0;
|
||||
std::vector<DynamicReloc> Relocs;
|
||||
};
|
||||
|
||||
struct SymbolTableEntry {
|
||||
SymbolBody *Symbol;
|
||||
size_t StrTabOffset;
|
||||
};
|
||||
|
||||
class SymbolTableBaseSection : public SyntheticSection {
|
||||
public:
|
||||
SymbolTableBaseSection(StringTableSection &StrTabSec);
|
||||
void finalizeContents() override;
|
||||
void postThunkContents() override;
|
||||
size_t getSize() const override { return getNumSymbols() * Entsize; }
|
||||
void addSymbol(SymbolBody *Body);
|
||||
unsigned getNumSymbols() const { return Symbols.size() + 1; }
|
||||
size_t getSymbolIndex(SymbolBody *Body);
|
||||
ArrayRef<SymbolTableEntry> getSymbols() const { return Symbols; }
|
||||
|
||||
protected:
|
||||
// A vector of symbols and their string table offsets.
|
||||
std::vector<SymbolTableEntry> Symbols;
|
||||
|
||||
StringTableSection &StrTabSec;
|
||||
};
|
||||
|
||||
template <class ELFT>
|
||||
class SymbolTableSection final : public SymbolTableBaseSection {
|
||||
typedef typename ELFT::Sym Elf_Sym;
|
||||
|
||||
public:
|
||||
SymbolTableSection(StringTableSection &StrTabSec);
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
};
|
||||
|
||||
// Outputs GNU Hash section. For detailed explanation see:
|
||||
// https://blogs.oracle.com/ali/entry/gnu_hash_elf_sections
|
||||
class GnuHashTableSection final : public SyntheticSection {
|
||||
public:
|
||||
GnuHashTableSection();
|
||||
void finalizeContents() override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
size_t getSize() const override { return Size; }
|
||||
|
||||
// Adds symbols to the hash table.
|
||||
// Sorts the input to satisfy GNU hash section requirements.
|
||||
void addSymbols(std::vector<SymbolTableEntry> &Symbols);
|
||||
|
||||
private:
|
||||
size_t getShift2() const { return Config->Is64 ? 6 : 5; }
|
||||
|
||||
void writeBloomFilter(uint8_t *Buf);
|
||||
void writeHashTable(uint8_t *Buf);
|
||||
|
||||
struct Entry {
|
||||
SymbolBody *Body;
|
||||
size_t StrTabOffset;
|
||||
uint32_t Hash;
|
||||
};
|
||||
|
||||
std::vector<Entry> Symbols;
|
||||
size_t MaskWords;
|
||||
size_t NBuckets = 0;
|
||||
size_t Size = 0;
|
||||
};
|
||||
|
||||
template <class ELFT> class HashTableSection final : public SyntheticSection {
|
||||
public:
|
||||
HashTableSection();
|
||||
void finalizeContents() override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
size_t getSize() const override { return Size; }
|
||||
|
||||
private:
|
||||
size_t Size = 0;
|
||||
};
|
||||
|
||||
// The PltSection is used for both the Plt and Iplt. The former always has a
|
||||
// header as its first entry that is used at run-time to resolve lazy binding.
|
||||
// The latter is used for GNU Ifunc symbols, that will be subject to a
|
||||
// Target->IRelativeRel.
|
||||
class PltSection : public SyntheticSection {
|
||||
public:
|
||||
PltSection(size_t HeaderSize);
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
size_t getSize() const override;
|
||||
bool empty() const override { return Entries.empty(); }
|
||||
void addSymbols();
|
||||
|
||||
template <class ELFT> void addEntry(SymbolBody &Sym);
|
||||
|
||||
private:
|
||||
void writeHeader(uint8_t *Buf){};
|
||||
void addHeaderSymbols(){};
|
||||
unsigned getPltRelocOff() const;
|
||||
std::vector<std::pair<const SymbolBody *, unsigned>> Entries;
|
||||
// Iplt always has HeaderSize of 0, the Plt HeaderSize is always non-zero
|
||||
size_t HeaderSize;
|
||||
};
|
||||
|
||||
class GdbIndexSection final : public SyntheticSection {
|
||||
const unsigned OffsetTypeSize = 4;
|
||||
const unsigned CuListOffset = 6 * OffsetTypeSize;
|
||||
const unsigned CompilationUnitSize = 16;
|
||||
const unsigned AddressEntrySize = 16 + OffsetTypeSize;
|
||||
const unsigned SymTabEntrySize = 2 * OffsetTypeSize;
|
||||
|
||||
public:
|
||||
GdbIndexSection(std::vector<GdbIndexChunk> &&Chunks);
|
||||
void finalizeContents() override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
size_t getSize() const override;
|
||||
bool empty() const override;
|
||||
|
||||
// Symbol table is a hash table for types and names.
|
||||
// It is the area of gdb index.
|
||||
GdbHashTab SymbolTable;
|
||||
|
||||
// CU vector is a part of constant pool area of section.
|
||||
std::vector<std::set<uint32_t>> CuVectors;
|
||||
|
||||
// String pool is also a part of constant pool, it follows CU vectors.
|
||||
llvm::StringTableBuilder StringPool;
|
||||
|
||||
// Each chunk contains information gathered from a debug sections of single
|
||||
// object and used to build different areas of gdb index.
|
||||
std::vector<GdbIndexChunk> Chunks;
|
||||
|
||||
private:
|
||||
void buildIndex();
|
||||
|
||||
uint32_t CuTypesOffset;
|
||||
uint32_t SymTabOffset;
|
||||
uint32_t ConstantPoolOffset;
|
||||
uint32_t StringPoolOffset;
|
||||
|
||||
size_t CuVectorsSize = 0;
|
||||
std::vector<size_t> CuVectorsOffset;
|
||||
|
||||
bool Finalized = false;
|
||||
};
|
||||
|
||||
template <class ELFT> GdbIndexSection *createGdbIndex();
|
||||
|
||||
// --eh-frame-hdr option tells linker to construct a header for all the
|
||||
// .eh_frame sections. This header is placed to a section named .eh_frame_hdr
|
||||
// and also to a PT_GNU_EH_FRAME segment.
|
||||
// At runtime the unwinder then can find all the PT_GNU_EH_FRAME segments by
|
||||
// calling dl_iterate_phdr.
|
||||
// This section contains a lookup table for quick binary search of FDEs.
|
||||
// Detailed info about internals can be found in Ian Lance Taylor's blog:
|
||||
// http://www.airs.com/blog/archives/460 (".eh_frame")
|
||||
// http://www.airs.com/blog/archives/462 (".eh_frame_hdr")
|
||||
template <class ELFT> class EhFrameHeader final : public SyntheticSection {
|
||||
public:
|
||||
EhFrameHeader();
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
size_t getSize() const override;
|
||||
void addFde(uint32_t Pc, uint32_t FdeVA);
|
||||
bool empty() const override;
|
||||
|
||||
private:
|
||||
struct FdeData {
|
||||
uint32_t Pc;
|
||||
uint32_t FdeVA;
|
||||
};
|
||||
|
||||
std::vector<FdeData> Fdes;
|
||||
};
|
||||
|
||||
// For more information about .gnu.version and .gnu.version_r see:
|
||||
// https://www.akkadia.org/drepper/symbol-versioning
|
||||
|
||||
// The .gnu.version_d section which has a section type of SHT_GNU_verdef shall
|
||||
// contain symbol version definitions. The number of entries in this section
|
||||
// shall be contained in the DT_VERDEFNUM entry of the .dynamic section.
|
||||
// The section shall contain an array of Elf_Verdef structures, optionally
|
||||
// followed by an array of Elf_Verdaux structures.
|
||||
template <class ELFT>
|
||||
class VersionDefinitionSection final : public SyntheticSection {
|
||||
typedef typename ELFT::Verdef Elf_Verdef;
|
||||
typedef typename ELFT::Verdaux Elf_Verdaux;
|
||||
|
||||
public:
|
||||
VersionDefinitionSection();
|
||||
void finalizeContents() override;
|
||||
size_t getSize() const override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
|
||||
private:
|
||||
void writeOne(uint8_t *Buf, uint32_t Index, StringRef Name, size_t NameOff);
|
||||
|
||||
unsigned FileDefNameOff;
|
||||
};
|
||||
|
||||
// The .gnu.version section specifies the required version of each symbol in the
|
||||
// dynamic symbol table. It contains one Elf_Versym for each dynamic symbol
|
||||
// table entry. An Elf_Versym is just a 16-bit integer that refers to a version
|
||||
// identifier defined in the either .gnu.version_r or .gnu.version_d section.
|
||||
// The values 0 and 1 are reserved. All other values are used for versions in
|
||||
// the own object or in any of the dependencies.
|
||||
template <class ELFT>
|
||||
class VersionTableSection final : public SyntheticSection {
|
||||
typedef typename ELFT::Versym Elf_Versym;
|
||||
|
||||
public:
|
||||
VersionTableSection();
|
||||
void finalizeContents() override;
|
||||
size_t getSize() const override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
bool empty() const override;
|
||||
};
|
||||
|
||||
// The .gnu.version_r section defines the version identifiers used by
|
||||
// .gnu.version. It contains a linked list of Elf_Verneed data structures. Each
|
||||
// Elf_Verneed specifies the version requirements for a single DSO, and contains
|
||||
// a reference to a linked list of Elf_Vernaux data structures which define the
|
||||
// mapping from version identifiers to version names.
|
||||
template <class ELFT> class VersionNeedSection final : public SyntheticSection {
|
||||
typedef typename ELFT::Verneed Elf_Verneed;
|
||||
typedef typename ELFT::Vernaux Elf_Vernaux;
|
||||
|
||||
// A vector of shared files that need Elf_Verneed data structures and the
|
||||
// string table offsets of their sonames.
|
||||
std::vector<std::pair<SharedFile<ELFT> *, size_t>> Needed;
|
||||
|
||||
// The next available version identifier.
|
||||
unsigned NextIndex;
|
||||
|
||||
public:
|
||||
VersionNeedSection();
|
||||
void addSymbol(SharedSymbol *SS);
|
||||
void finalizeContents() override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
size_t getSize() const override;
|
||||
size_t getNeedNum() const { return Needed.size(); }
|
||||
bool empty() const override;
|
||||
};
|
||||
|
||||
// MergeSyntheticSection is a class that allows us to put mergeable sections
|
||||
// with different attributes in a single output sections. To do that
|
||||
// we put them into MergeSyntheticSection synthetic input sections which are
|
||||
// attached to regular output sections.
|
||||
class MergeSyntheticSection final : public SyntheticSection {
|
||||
public:
|
||||
MergeSyntheticSection(StringRef Name, uint32_t Type, uint64_t Flags,
|
||||
uint32_t Alignment);
|
||||
void addSection(MergeInputSection *MS);
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
void finalizeContents() override;
|
||||
bool shouldTailMerge() const;
|
||||
size_t getSize() const override;
|
||||
|
||||
private:
|
||||
void finalizeTailMerge();
|
||||
void finalizeNoTailMerge();
|
||||
|
||||
llvm::StringTableBuilder Builder;
|
||||
std::vector<MergeInputSection *> Sections;
|
||||
};
|
||||
|
||||
// .MIPS.abiflags section.
|
||||
template <class ELFT>
|
||||
class MipsAbiFlagsSection final : public SyntheticSection {
|
||||
typedef llvm::object::Elf_Mips_ABIFlags<ELFT> Elf_Mips_ABIFlags;
|
||||
|
||||
public:
|
||||
static MipsAbiFlagsSection *create();
|
||||
|
||||
MipsAbiFlagsSection(Elf_Mips_ABIFlags Flags);
|
||||
size_t getSize() const override { return sizeof(Elf_Mips_ABIFlags); }
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
|
||||
private:
|
||||
Elf_Mips_ABIFlags Flags;
|
||||
};
|
||||
|
||||
// .MIPS.options section.
|
||||
template <class ELFT> class MipsOptionsSection final : public SyntheticSection {
|
||||
typedef llvm::object::Elf_Mips_Options<ELFT> Elf_Mips_Options;
|
||||
typedef llvm::object::Elf_Mips_RegInfo<ELFT> Elf_Mips_RegInfo;
|
||||
|
||||
public:
|
||||
static MipsOptionsSection *create();
|
||||
|
||||
MipsOptionsSection(Elf_Mips_RegInfo Reginfo);
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
|
||||
size_t getSize() const override {
|
||||
return sizeof(Elf_Mips_Options) + sizeof(Elf_Mips_RegInfo);
|
||||
}
|
||||
|
||||
private:
|
||||
Elf_Mips_RegInfo Reginfo;
|
||||
};
|
||||
|
||||
// MIPS .reginfo section.
|
||||
template <class ELFT> class MipsReginfoSection final : public SyntheticSection {
|
||||
typedef llvm::object::Elf_Mips_RegInfo<ELFT> Elf_Mips_RegInfo;
|
||||
|
||||
public:
|
||||
static MipsReginfoSection *create();
|
||||
|
||||
MipsReginfoSection(Elf_Mips_RegInfo Reginfo);
|
||||
size_t getSize() const override { return sizeof(Elf_Mips_RegInfo); }
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
|
||||
private:
|
||||
Elf_Mips_RegInfo Reginfo;
|
||||
};
|
||||
|
||||
// This is a MIPS specific section to hold a space within the data segment
|
||||
// of executable file which is pointed to by the DT_MIPS_RLD_MAP entry.
|
||||
// See "Dynamic section" in Chapter 5 in the following document:
|
||||
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
|
||||
class MipsRldMapSection : public SyntheticSection {
|
||||
public:
|
||||
MipsRldMapSection();
|
||||
size_t getSize() const override { return Config->Wordsize; }
|
||||
void writeTo(uint8_t *Buf) override {}
|
||||
};
|
||||
|
||||
class ARMExidxSentinelSection : public SyntheticSection {
|
||||
public:
|
||||
ARMExidxSentinelSection();
|
||||
size_t getSize() const override { return 8; }
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
};
|
||||
|
||||
// A container for one or more linker generated thunks. Instances of these
|
||||
// thunks including ARM interworking and Mips LA25 PI to non-PI thunks.
|
||||
class ThunkSection : public SyntheticSection {
|
||||
public:
|
||||
// ThunkSection in OS, with desired OutSecOff of Off
|
||||
ThunkSection(OutputSection *OS, uint64_t Off);
|
||||
|
||||
// Add a newly created Thunk to this container:
|
||||
// Thunk is given offset from start of this InputSection
|
||||
// Thunk defines a symbol in this InputSection that can be used as target
|
||||
// of a relocation
|
||||
void addThunk(Thunk *T);
|
||||
size_t getSize() const override { return Size; }
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
InputSection *getTargetInputSection() const;
|
||||
|
||||
private:
|
||||
std::vector<const Thunk *> Thunks;
|
||||
size_t Size = 0;
|
||||
};
|
||||
|
||||
template <class ELFT> InputSection *createCommonSection();
|
||||
InputSection *createInterpSection();
|
||||
template <class ELFT> MergeInputSection *createCommentSection();
|
||||
void decompressAndMergeSections();
|
||||
|
||||
SymbolBody *addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value,
|
||||
uint64_t Size, InputSectionBase *Section);
|
||||
|
||||
// Linker generated sections which can be used as inputs.
|
||||
struct InX {
|
||||
static InputSection *ARMAttributes;
|
||||
static BssSection *Bss;
|
||||
static BssSection *BssRelRo;
|
||||
static BuildIdSection *BuildId;
|
||||
static InputSection *Common;
|
||||
static SyntheticSection *Dynamic;
|
||||
static StringTableSection *DynStrTab;
|
||||
static SymbolTableBaseSection *DynSymTab;
|
||||
static GnuHashTableSection *GnuHashTab;
|
||||
static InputSection *Interp;
|
||||
static GdbIndexSection *GdbIndex;
|
||||
static GotSection *Got;
|
||||
static GotPltSection *GotPlt;
|
||||
static IgotPltSection *IgotPlt;
|
||||
static MipsGotSection *MipsGot;
|
||||
static MipsRldMapSection *MipsRldMap;
|
||||
static PltSection *Plt;
|
||||
static PltSection *Iplt;
|
||||
static StringTableSection *ShStrTab;
|
||||
static StringTableSection *StrTab;
|
||||
static SymbolTableBaseSection *SymTab;
|
||||
};
|
||||
|
||||
template <class ELFT> struct In : public InX {
|
||||
static EhFrameHeader<ELFT> *EhFrameHdr;
|
||||
static EhFrameSection<ELFT> *EhFrame;
|
||||
static HashTableSection<ELFT> *HashTab;
|
||||
static RelocationSection<ELFT> *RelaDyn;
|
||||
static RelocationSection<ELFT> *RelaPlt;
|
||||
static RelocationSection<ELFT> *RelaIplt;
|
||||
static VersionDefinitionSection<ELFT> *VerDef;
|
||||
static VersionTableSection<ELFT> *VerSym;
|
||||
static VersionNeedSection<ELFT> *VerNeed;
|
||||
};
|
||||
|
||||
template <class ELFT> EhFrameHeader<ELFT> *In<ELFT>::EhFrameHdr;
|
||||
template <class ELFT> EhFrameSection<ELFT> *In<ELFT>::EhFrame;
|
||||
template <class ELFT> HashTableSection<ELFT> *In<ELFT>::HashTab;
|
||||
template <class ELFT> RelocationSection<ELFT> *In<ELFT>::RelaDyn;
|
||||
template <class ELFT> RelocationSection<ELFT> *In<ELFT>::RelaPlt;
|
||||
template <class ELFT> RelocationSection<ELFT> *In<ELFT>::RelaIplt;
|
||||
template <class ELFT> VersionDefinitionSection<ELFT> *In<ELFT>::VerDef;
|
||||
template <class ELFT> VersionTableSection<ELFT> *In<ELFT>::VerSym;
|
||||
template <class ELFT> VersionNeedSection<ELFT> *In<ELFT>::VerNeed;
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
167
deps/lld/ELF/Target.cpp
vendored
Normal file
167
deps/lld/ELF/Target.cpp
vendored
Normal file
@ -0,0 +1,167 @@
|
||||
//===- Target.cpp ---------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Machine-specific things, such as applying relocations, creation of
|
||||
// GOT or PLT entries, etc., are handled in this file.
|
||||
//
|
||||
// Refer the ELF spec for the single letter variables, S, A or P, used
|
||||
// in this file.
|
||||
//
|
||||
// Some functions defined in this file has "relaxTls" as part of their names.
|
||||
// They do peephole optimization for TLS variables by rewriting instructions.
|
||||
// They are not part of the ABI but optional optimization, so you can skip
|
||||
// them if you are not interested in how TLS variables are optimized.
|
||||
// See the following paper for the details.
|
||||
//
|
||||
// Ulrich Drepper, ELF Handling For Thread-Local Storage
|
||||
// http://www.akkadia.org/drepper/tls.pdf
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Target.h"
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "OutputSections.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
TargetInfo *elf::Target;
|
||||
|
||||
std::string lld::toString(uint32_t Type) {
|
||||
StringRef S = getELFRelocationTypeName(elf::Config->EMachine, Type);
|
||||
if (S == "Unknown")
|
||||
return ("Unknown (" + Twine(Type) + ")").str();
|
||||
return S;
|
||||
}
|
||||
|
||||
TargetInfo *elf::getTarget() {
|
||||
switch (Config->EMachine) {
|
||||
case EM_386:
|
||||
case EM_IAMCU:
|
||||
return getX86TargetInfo();
|
||||
case EM_AARCH64:
|
||||
return getAArch64TargetInfo();
|
||||
case EM_AMDGPU:
|
||||
return getAMDGPUTargetInfo();
|
||||
case EM_ARM:
|
||||
return getARMTargetInfo();
|
||||
case EM_AVR:
|
||||
return getAVRTargetInfo();
|
||||
case EM_MIPS:
|
||||
switch (Config->EKind) {
|
||||
case ELF32LEKind:
|
||||
return getMipsTargetInfo<ELF32LE>();
|
||||
case ELF32BEKind:
|
||||
return getMipsTargetInfo<ELF32BE>();
|
||||
case ELF64LEKind:
|
||||
return getMipsTargetInfo<ELF64LE>();
|
||||
case ELF64BEKind:
|
||||
return getMipsTargetInfo<ELF64BE>();
|
||||
default:
|
||||
fatal("unsupported MIPS target");
|
||||
}
|
||||
case EM_PPC:
|
||||
return getPPCTargetInfo();
|
||||
case EM_PPC64:
|
||||
return getPPC64TargetInfo();
|
||||
case EM_SPARCV9:
|
||||
return getSPARCV9TargetInfo();
|
||||
case EM_X86_64:
|
||||
if (Config->EKind == ELF32LEKind)
|
||||
return getX32TargetInfo();
|
||||
return getX86_64TargetInfo();
|
||||
}
|
||||
fatal("unknown target machine");
|
||||
}
|
||||
|
||||
template <class ELFT> static std::string getErrorLoc(const uint8_t *Loc) {
|
||||
for (InputSectionBase *D : InputSections) {
|
||||
auto *IS = dyn_cast_or_null<InputSection>(D);
|
||||
if (!IS || !IS->getParent())
|
||||
continue;
|
||||
|
||||
uint8_t *ISLoc = IS->getParent()->Loc + IS->OutSecOff;
|
||||
if (ISLoc <= Loc && Loc < ISLoc + IS->getSize())
|
||||
return IS->template getLocation<ELFT>(Loc - ISLoc) + ": ";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string elf::getErrorLocation(const uint8_t *Loc) {
|
||||
switch (Config->EKind) {
|
||||
case ELF32LEKind:
|
||||
return getErrorLoc<ELF32LE>(Loc);
|
||||
case ELF32BEKind:
|
||||
return getErrorLoc<ELF32BE>(Loc);
|
||||
case ELF64LEKind:
|
||||
return getErrorLoc<ELF64LE>(Loc);
|
||||
case ELF64BEKind:
|
||||
return getErrorLoc<ELF64BE>(Loc);
|
||||
default:
|
||||
llvm_unreachable("unknown ELF type");
|
||||
}
|
||||
}
|
||||
|
||||
TargetInfo::~TargetInfo() {}
|
||||
|
||||
int64_t TargetInfo::getImplicitAddend(const uint8_t *Buf, uint32_t Type) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool TargetInfo::usesOnlyLowPageBits(uint32_t Type) const { return false; }
|
||||
|
||||
bool TargetInfo::needsThunk(RelExpr Expr, uint32_t RelocType,
|
||||
const InputFile *File, const SymbolBody &S) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TargetInfo::inBranchRange(uint32_t RelocType, uint64_t Src,
|
||||
uint64_t Dst) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void TargetInfo::writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const {
|
||||
writeGotPlt(Buf, S);
|
||||
}
|
||||
|
||||
RelExpr TargetInfo::adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
|
||||
RelExpr Expr) const {
|
||||
return Expr;
|
||||
}
|
||||
|
||||
void TargetInfo::relaxGot(uint8_t *Loc, uint64_t Val) const {
|
||||
llvm_unreachable("Should not have claimed to be relaxable");
|
||||
}
|
||||
|
||||
void TargetInfo::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type,
|
||||
uint64_t Val) const {
|
||||
llvm_unreachable("Should not have claimed to be relaxable");
|
||||
}
|
||||
|
||||
void TargetInfo::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type,
|
||||
uint64_t Val) const {
|
||||
llvm_unreachable("Should not have claimed to be relaxable");
|
||||
}
|
||||
|
||||
void TargetInfo::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type,
|
||||
uint64_t Val) const {
|
||||
llvm_unreachable("Should not have claimed to be relaxable");
|
||||
}
|
||||
|
||||
void TargetInfo::relaxTlsLdToLe(uint8_t *Loc, uint32_t Type,
|
||||
uint64_t Val) const {
|
||||
llvm_unreachable("Should not have claimed to be relaxable");
|
||||
}
|
||||
162
deps/lld/ELF/Target.h
vendored
Normal file
162
deps/lld/ELF/Target.h
vendored
Normal file
@ -0,0 +1,162 @@
|
||||
//===- Target.h -------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_TARGET_H
|
||||
#define LLD_ELF_TARGET_H
|
||||
|
||||
#include "Error.h"
|
||||
#include "InputSection.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
|
||||
namespace lld {
|
||||
std::string toString(uint32_t RelType);
|
||||
|
||||
namespace elf {
|
||||
class InputFile;
|
||||
class SymbolBody;
|
||||
|
||||
class TargetInfo {
|
||||
public:
|
||||
virtual bool isPicRel(uint32_t Type) const { return true; }
|
||||
virtual uint32_t getDynRel(uint32_t Type) const { return Type; }
|
||||
virtual void writeGotPltHeader(uint8_t *Buf) const {}
|
||||
virtual void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const {};
|
||||
virtual void writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const;
|
||||
virtual int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const;
|
||||
|
||||
// If lazy binding is supported, the first entry of the PLT has code
|
||||
// to call the dynamic linker to resolve PLT entries the first time
|
||||
// they are called. This function writes that code.
|
||||
virtual void writePltHeader(uint8_t *Buf) const {}
|
||||
|
||||
virtual void writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
|
||||
uint64_t PltEntryAddr, int32_t Index,
|
||||
unsigned RelOff) const {}
|
||||
virtual void addPltHeaderSymbols(InputSectionBase *IS) const {}
|
||||
virtual void addPltSymbols(InputSectionBase *IS, uint64_t Off) const {}
|
||||
// Returns true if a relocation only uses the low bits of a value such that
|
||||
// all those bits are in in the same page. For example, if the relocation
|
||||
// only uses the low 12 bits in a system with 4k pages. If this is true, the
|
||||
// bits will always have the same value at runtime and we don't have to emit
|
||||
// a dynamic relocation.
|
||||
virtual bool usesOnlyLowPageBits(uint32_t Type) const;
|
||||
|
||||
// Decide whether a Thunk is needed for the relocation from File
|
||||
// targeting S.
|
||||
virtual bool needsThunk(RelExpr Expr, uint32_t RelocType,
|
||||
const InputFile *File, const SymbolBody &S) const;
|
||||
// Return true if we can reach Dst from Src with Relocation RelocType
|
||||
virtual bool inBranchRange(uint32_t RelocType, uint64_t Src,
|
||||
uint64_t Dst) const;
|
||||
virtual RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const = 0;
|
||||
virtual void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const = 0;
|
||||
virtual ~TargetInfo();
|
||||
|
||||
unsigned TlsGdRelaxSkip = 1;
|
||||
unsigned PageSize = 4096;
|
||||
unsigned DefaultMaxPageSize = 4096;
|
||||
|
||||
// On FreeBSD x86_64 the first page cannot be mmaped.
|
||||
// On Linux that is controled by vm.mmap_min_addr. At least on some x86_64
|
||||
// installs that is 65536, so the first 15 pages cannot be used.
|
||||
// Given that, the smallest value that can be used in here is 0x10000.
|
||||
uint64_t DefaultImageBase = 0x10000;
|
||||
|
||||
// Offset of _GLOBAL_OFFSET_TABLE_ from base of .got section. Use -1 for
|
||||
// end of .got
|
||||
uint64_t GotBaseSymOff = 0;
|
||||
|
||||
uint32_t CopyRel;
|
||||
uint32_t GotRel;
|
||||
uint32_t PltRel;
|
||||
uint32_t RelativeRel;
|
||||
uint32_t IRelativeRel;
|
||||
uint32_t TlsDescRel;
|
||||
uint32_t TlsGotRel;
|
||||
uint32_t TlsModuleIndexRel;
|
||||
uint32_t TlsOffsetRel;
|
||||
unsigned GotEntrySize = 0;
|
||||
unsigned GotPltEntrySize = 0;
|
||||
unsigned PltEntrySize;
|
||||
unsigned PltHeaderSize;
|
||||
|
||||
// At least on x86_64 positions 1 and 2 are used by the first plt entry
|
||||
// to support lazy loading.
|
||||
unsigned GotPltHeaderEntriesNum = 3;
|
||||
|
||||
// Set to 0 for variant 2
|
||||
unsigned TcbSize = 0;
|
||||
|
||||
bool NeedsThunks = false;
|
||||
|
||||
// A 4-byte field corresponding to one or more trap instructions, used to pad
|
||||
// executable OutputSections.
|
||||
uint32_t TrapInstr = 0;
|
||||
|
||||
virtual RelExpr adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
|
||||
RelExpr Expr) const;
|
||||
virtual void relaxGot(uint8_t *Loc, uint64_t Val) const;
|
||||
virtual void relaxTlsGdToIe(uint8_t *Loc, uint32_t Type, uint64_t Val) const;
|
||||
virtual void relaxTlsGdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const;
|
||||
virtual void relaxTlsIeToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const;
|
||||
virtual void relaxTlsLdToLe(uint8_t *Loc, uint32_t Type, uint64_t Val) const;
|
||||
};
|
||||
|
||||
TargetInfo *getAArch64TargetInfo();
|
||||
TargetInfo *getAMDGPUTargetInfo();
|
||||
TargetInfo *getARMTargetInfo();
|
||||
TargetInfo *getAVRTargetInfo();
|
||||
TargetInfo *getPPC64TargetInfo();
|
||||
TargetInfo *getPPCTargetInfo();
|
||||
TargetInfo *getSPARCV9TargetInfo();
|
||||
TargetInfo *getX32TargetInfo();
|
||||
TargetInfo *getX86TargetInfo();
|
||||
TargetInfo *getX86_64TargetInfo();
|
||||
template <class ELFT> TargetInfo *getMipsTargetInfo();
|
||||
|
||||
std::string getErrorLocation(const uint8_t *Loc);
|
||||
|
||||
uint64_t getPPC64TocBase();
|
||||
uint64_t getAArch64Page(uint64_t Expr);
|
||||
|
||||
extern TargetInfo *Target;
|
||||
TargetInfo *getTarget();
|
||||
|
||||
template <unsigned N>
|
||||
static void checkInt(uint8_t *Loc, int64_t V, uint32_t Type) {
|
||||
if (!llvm::isInt<N>(V))
|
||||
error(getErrorLocation(Loc) + "relocation " + lld::toString(Type) +
|
||||
" out of range");
|
||||
}
|
||||
|
||||
template <unsigned N>
|
||||
static void checkUInt(uint8_t *Loc, uint64_t V, uint32_t Type) {
|
||||
if (!llvm::isUInt<N>(V))
|
||||
error(getErrorLocation(Loc) + "relocation " + lld::toString(Type) +
|
||||
" out of range");
|
||||
}
|
||||
|
||||
template <unsigned N>
|
||||
static void checkIntUInt(uint8_t *Loc, uint64_t V, uint32_t Type) {
|
||||
if (!llvm::isInt<N>(V) && !llvm::isUInt<N>(V))
|
||||
error(getErrorLocation(Loc) + "relocation " + lld::toString(Type) +
|
||||
" out of range");
|
||||
}
|
||||
|
||||
template <unsigned N>
|
||||
static void checkAlignment(uint8_t *Loc, uint64_t V, uint32_t Type) {
|
||||
if ((V & (N - 1)) != 0)
|
||||
error(getErrorLocation(Loc) + "improper alignment for relocation " +
|
||||
lld::toString(Type));
|
||||
}
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
88
deps/lld/ELF/Threads.h
vendored
Normal file
88
deps/lld/ELF/Threads.h
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
//===- Threads.h ------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// LLD supports threads to distribute workloads to multiple cores. Using
|
||||
// multicore is most effective when more than one core are idle. At the
|
||||
// last step of a build, it is often the case that a linker is the only
|
||||
// active process on a computer. So, we are naturally interested in using
|
||||
// threads wisely to reduce latency to deliver results to users.
|
||||
//
|
||||
// That said, we don't want to do "too clever" things using threads.
|
||||
// Complex multi-threaded algorithms are sometimes extremely hard to
|
||||
// reason about and can easily mess up the entire design.
|
||||
//
|
||||
// Fortunately, when a linker links large programs (when the link time is
|
||||
// most critical), it spends most of the time to work on massive number of
|
||||
// small pieces of data of the same kind, and there are opportunities for
|
||||
// large parallelism there. Here are examples:
|
||||
//
|
||||
// - We have hundreds of thousands of input sections that need to be
|
||||
// copied to a result file at the last step of link. Once we fix a file
|
||||
// layout, each section can be copied to its destination and its
|
||||
// relocations can be applied independently.
|
||||
//
|
||||
// - We have tens of millions of small strings when constructing a
|
||||
// mergeable string section.
|
||||
//
|
||||
// For the cases such as the former, we can just use parallel_for_each
|
||||
// instead of std::for_each (or a plain for loop). Because tasks are
|
||||
// completely independent from each other, we can run them in parallel
|
||||
// without any coordination between them. That's very easy to understand
|
||||
// and reason about.
|
||||
//
|
||||
// For the cases such as the latter, we can use parallel algorithms to
|
||||
// deal with massive data. We have to write code for a tailored algorithm
|
||||
// for each problem, but the complexity of multi-threading is isolated in
|
||||
// a single pass and doesn't affect the linker's overall design.
|
||||
//
|
||||
// The above approach seems to be working fairly well. As an example, when
|
||||
// linking Chromium (output size 1.6 GB), using 4 cores reduces latency to
|
||||
// 75% compared to single core (from 12.66 seconds to 9.55 seconds) on my
|
||||
// Ivy Bridge Xeon 2.8 GHz machine. Using 40 cores reduces it to 63% (from
|
||||
// 12.66 seconds to 7.95 seconds). Because of the Amdahl's law, the
|
||||
// speedup is not linear, but as you add more cores, it gets faster.
|
||||
//
|
||||
// On a final note, if you are trying to optimize, keep the axiom "don't
|
||||
// guess, measure!" in mind. Some important passes of the linker are not
|
||||
// that slow. For example, resolving all symbols is not a very heavy pass,
|
||||
// although it would be very hard to parallelize it. You want to first
|
||||
// identify a slow pass and then optimize it.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_THREADS_H
|
||||
#define LLD_ELF_THREADS_H
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
#include "llvm/Support/Parallel.h"
|
||||
#include <functional>
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
template <class IterTy, class FuncTy>
|
||||
void parallelForEach(IterTy Begin, IterTy End, FuncTy Fn) {
|
||||
if (Config->Threads)
|
||||
for_each(llvm::parallel::par, Begin, End, Fn);
|
||||
else
|
||||
for_each(llvm::parallel::seq, Begin, End, Fn);
|
||||
}
|
||||
|
||||
inline void parallelForEachN(size_t Begin, size_t End,
|
||||
std::function<void(size_t)> Fn) {
|
||||
if (Config->Threads)
|
||||
for_each_n(llvm::parallel::par, Begin, End, Fn);
|
||||
else
|
||||
for_each_n(llvm::parallel::seq, Begin, End, Fn);
|
||||
}
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
273
deps/lld/ELF/Thunks.cpp
vendored
Normal file
273
deps/lld/ELF/Thunks.cpp
vendored
Normal file
@ -0,0 +1,273 @@
|
||||
//===- Thunks.cpp --------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===---------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains Thunk subclasses.
|
||||
//
|
||||
// A thunk is a small piece of code written after an input section
|
||||
// which is used to jump between "incompatible" functions
|
||||
// such as MIPS PIC and non-PIC or ARM non-Thumb and Thumb functions.
|
||||
//
|
||||
// If a jump target is too far and its address doesn't fit to a
|
||||
// short jump instruction, we need to create a thunk too, but we
|
||||
// haven't supported it yet.
|
||||
//
|
||||
// i386 and x86-64 don't need thunks.
|
||||
//
|
||||
//===---------------------------------------------------------------------===//
|
||||
|
||||
#include "Thunks.h"
|
||||
#include "Config.h"
|
||||
#include "Error.h"
|
||||
#include "InputSection.h"
|
||||
#include "Memory.h"
|
||||
#include "OutputSections.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "llvm/BinaryFormat/ELF.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/MathExtras.h"
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
namespace {
|
||||
|
||||
// Specific ARM Thunk implementations. The naming convention is:
|
||||
// Source State, TargetState, Target Requirement, ABS or PI, Range
|
||||
class ARMV7ABSLongThunk final : public Thunk {
|
||||
public:
|
||||
ARMV7ABSLongThunk(const SymbolBody &Dest) : Thunk(Dest) {}
|
||||
|
||||
uint32_t size() const override { return 12; }
|
||||
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
|
||||
void addSymbols(ThunkSection &IS) override;
|
||||
bool isCompatibleWith(uint32_t RelocType) const override;
|
||||
};
|
||||
|
||||
class ARMV7PILongThunk final : public Thunk {
|
||||
public:
|
||||
ARMV7PILongThunk(const SymbolBody &Dest) : Thunk(Dest) {}
|
||||
|
||||
uint32_t size() const override { return 16; }
|
||||
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
|
||||
void addSymbols(ThunkSection &IS) override;
|
||||
bool isCompatibleWith(uint32_t RelocType) const override;
|
||||
};
|
||||
|
||||
class ThumbV7ABSLongThunk final : public Thunk {
|
||||
public:
|
||||
ThumbV7ABSLongThunk(const SymbolBody &Dest) : Thunk(Dest) { Alignment = 2; }
|
||||
|
||||
uint32_t size() const override { return 10; }
|
||||
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
|
||||
void addSymbols(ThunkSection &IS) override;
|
||||
bool isCompatibleWith(uint32_t RelocType) const override;
|
||||
};
|
||||
|
||||
class ThumbV7PILongThunk final : public Thunk {
|
||||
public:
|
||||
ThumbV7PILongThunk(const SymbolBody &Dest) : Thunk(Dest) { Alignment = 2; }
|
||||
|
||||
uint32_t size() const override { return 12; }
|
||||
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
|
||||
void addSymbols(ThunkSection &IS) override;
|
||||
bool isCompatibleWith(uint32_t RelocType) const override;
|
||||
};
|
||||
|
||||
// MIPS LA25 thunk
|
||||
class MipsThunk final : public Thunk {
|
||||
public:
|
||||
MipsThunk(const SymbolBody &Dest) : Thunk(Dest) {}
|
||||
|
||||
uint32_t size() const override { return 16; }
|
||||
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
|
||||
void addSymbols(ThunkSection &IS) override;
|
||||
InputSection *getTargetInputSection() const override;
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
// ARM Target Thunks
|
||||
static uint64_t getARMThunkDestVA(const SymbolBody &S) {
|
||||
uint64_t V = S.isInPlt() ? S.getPltVA() : S.getVA();
|
||||
return SignExtend64<32>(V);
|
||||
}
|
||||
|
||||
void ARMV7ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
|
||||
const uint8_t Data[] = {
|
||||
0x00, 0xc0, 0x00, 0xe3, // movw ip,:lower16:S
|
||||
0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S
|
||||
0x1c, 0xff, 0x2f, 0xe1, // bx ip
|
||||
};
|
||||
uint64_t S = getARMThunkDestVA(Destination);
|
||||
memcpy(Buf, Data, sizeof(Data));
|
||||
Target->relocateOne(Buf, R_ARM_MOVW_ABS_NC, S);
|
||||
Target->relocateOne(Buf + 4, R_ARM_MOVT_ABS, S);
|
||||
}
|
||||
|
||||
void ARMV7ABSLongThunk::addSymbols(ThunkSection &IS) {
|
||||
ThunkSym = addSyntheticLocal(
|
||||
Saver.save("__ARMv7ABSLongThunk_" + Destination.getName()), STT_FUNC,
|
||||
Offset, size(), &IS);
|
||||
addSyntheticLocal("$a", STT_NOTYPE, Offset, 0, &IS);
|
||||
}
|
||||
|
||||
bool ARMV7ABSLongThunk::isCompatibleWith(uint32_t RelocType) const {
|
||||
// Thumb branch relocations can't use BLX
|
||||
return RelocType != R_ARM_THM_JUMP19 && RelocType != R_ARM_THM_JUMP24;
|
||||
}
|
||||
|
||||
void ThumbV7ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
|
||||
const uint8_t Data[] = {
|
||||
0x40, 0xf2, 0x00, 0x0c, // movw ip, :lower16:S
|
||||
0xc0, 0xf2, 0x00, 0x0c, // movt ip, :upper16:S
|
||||
0x60, 0x47, // bx ip
|
||||
};
|
||||
uint64_t S = getARMThunkDestVA(Destination);
|
||||
memcpy(Buf, Data, sizeof(Data));
|
||||
Target->relocateOne(Buf, R_ARM_THM_MOVW_ABS_NC, S);
|
||||
Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_ABS, S);
|
||||
}
|
||||
|
||||
void ThumbV7ABSLongThunk::addSymbols(ThunkSection &IS) {
|
||||
ThunkSym = addSyntheticLocal(
|
||||
Saver.save("__Thumbv7ABSLongThunk_" + Destination.getName()), STT_FUNC,
|
||||
Offset | 0x1, size(), &IS);
|
||||
addSyntheticLocal("$t", STT_NOTYPE, Offset, 0, &IS);
|
||||
}
|
||||
|
||||
bool ThumbV7ABSLongThunk::isCompatibleWith(uint32_t RelocType) const {
|
||||
// ARM branch relocations can't use BLX
|
||||
return RelocType != R_ARM_JUMP24 && RelocType != R_ARM_PC24 &&
|
||||
RelocType != R_ARM_PLT32;
|
||||
}
|
||||
|
||||
void ARMV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
|
||||
const uint8_t Data[] = {
|
||||
0xf0, 0xcf, 0x0f, 0xe3, // P: movw ip,:lower16:S - (P + (L1-P) +8)
|
||||
0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S - (P + (L1-P+4) +8)
|
||||
0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc
|
||||
0x1c, 0xff, 0x2f, 0xe1, // bx r12
|
||||
};
|
||||
uint64_t S = getARMThunkDestVA(Destination);
|
||||
uint64_t P = ThunkSym->getVA();
|
||||
memcpy(Buf, Data, sizeof(Data));
|
||||
Target->relocateOne(Buf, R_ARM_MOVW_PREL_NC, S - P - 16);
|
||||
Target->relocateOne(Buf + 4, R_ARM_MOVT_PREL, S - P - 12);
|
||||
}
|
||||
|
||||
void ARMV7PILongThunk::addSymbols(ThunkSection &IS) {
|
||||
ThunkSym = addSyntheticLocal(
|
||||
Saver.save("__ARMV7PILongThunk_" + Destination.getName()), STT_FUNC,
|
||||
Offset, size(), &IS);
|
||||
addSyntheticLocal("$a", STT_NOTYPE, Offset, 0, &IS);
|
||||
}
|
||||
|
||||
bool ARMV7PILongThunk::isCompatibleWith(uint32_t RelocType) const {
|
||||
// Thumb branch relocations can't use BLX
|
||||
return RelocType != R_ARM_THM_JUMP19 && RelocType != R_ARM_THM_JUMP24;
|
||||
}
|
||||
|
||||
void ThumbV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
|
||||
const uint8_t Data[] = {
|
||||
0x4f, 0xf6, 0xf4, 0x7c, // P: movw ip,:lower16:S - (P + (L1-P) + 4)
|
||||
0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P+4) + 4)
|
||||
0xfc, 0x44, // L1: add r12, pc
|
||||
0x60, 0x47, // bx r12
|
||||
};
|
||||
uint64_t S = getARMThunkDestVA(Destination);
|
||||
uint64_t P = ThunkSym->getVA() & ~0x1;
|
||||
memcpy(Buf, Data, sizeof(Data));
|
||||
Target->relocateOne(Buf, R_ARM_THM_MOVW_PREL_NC, S - P - 12);
|
||||
Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_PREL, S - P - 8);
|
||||
}
|
||||
|
||||
void ThumbV7PILongThunk::addSymbols(ThunkSection &IS) {
|
||||
ThunkSym = addSyntheticLocal(
|
||||
Saver.save("__ThumbV7PILongThunk_" + Destination.getName()), STT_FUNC,
|
||||
Offset | 0x1, size(), &IS);
|
||||
addSyntheticLocal("$t", STT_NOTYPE, Offset, 0, &IS);
|
||||
}
|
||||
|
||||
bool ThumbV7PILongThunk::isCompatibleWith(uint32_t RelocType) const {
|
||||
// ARM branch relocations can't use BLX
|
||||
return RelocType != R_ARM_JUMP24 && RelocType != R_ARM_PC24 &&
|
||||
RelocType != R_ARM_PLT32;
|
||||
}
|
||||
|
||||
// Write MIPS LA25 thunk code to call PIC function from the non-PIC one.
|
||||
void MipsThunk::writeTo(uint8_t *Buf, ThunkSection &) const {
|
||||
uint64_t S = Destination.getVA();
|
||||
write32(Buf, 0x3c190000, Config->Endianness); // lui $25, %hi(func)
|
||||
write32(Buf + 4, 0x08000000 | (S >> 2), Config->Endianness); // j func
|
||||
write32(Buf + 8, 0x27390000, Config->Endianness); // addiu $25, $25, %lo(func)
|
||||
write32(Buf + 12, 0x00000000, Config->Endianness); // nop
|
||||
Target->relocateOne(Buf, R_MIPS_HI16, S);
|
||||
Target->relocateOne(Buf + 8, R_MIPS_LO16, S);
|
||||
}
|
||||
|
||||
void MipsThunk::addSymbols(ThunkSection &IS) {
|
||||
ThunkSym =
|
||||
addSyntheticLocal(Saver.save("__LA25Thunk_" + Destination.getName()),
|
||||
STT_FUNC, Offset, size(), &IS);
|
||||
}
|
||||
|
||||
InputSection *MipsThunk::getTargetInputSection() const {
|
||||
auto *DR = dyn_cast<DefinedRegular>(&Destination);
|
||||
return dyn_cast<InputSection>(DR->Section);
|
||||
}
|
||||
|
||||
Thunk::Thunk(const SymbolBody &D) : Destination(D), Offset(0) {}
|
||||
|
||||
Thunk::~Thunk() = default;
|
||||
|
||||
// Creates a thunk for Thumb-ARM interworking.
|
||||
static Thunk *addThunkArm(uint32_t Reloc, SymbolBody &S) {
|
||||
// ARM relocations need ARM to Thumb interworking Thunks.
|
||||
// Thumb relocations need Thumb to ARM relocations.
|
||||
// Use position independent Thunks if we require position independent code.
|
||||
switch (Reloc) {
|
||||
case R_ARM_PC24:
|
||||
case R_ARM_PLT32:
|
||||
case R_ARM_JUMP24:
|
||||
if (Config->Pic)
|
||||
return make<ARMV7PILongThunk>(S);
|
||||
return make<ARMV7ABSLongThunk>(S);
|
||||
case R_ARM_THM_JUMP19:
|
||||
case R_ARM_THM_JUMP24:
|
||||
if (Config->Pic)
|
||||
return make<ThumbV7PILongThunk>(S);
|
||||
return make<ThumbV7ABSLongThunk>(S);
|
||||
}
|
||||
fatal("unrecognized relocation type");
|
||||
}
|
||||
|
||||
static Thunk *addThunkMips(SymbolBody &S) { return make<MipsThunk>(S); }
|
||||
|
||||
Thunk *addThunk(uint32_t RelocType, SymbolBody &S) {
|
||||
if (Config->EMachine == EM_ARM)
|
||||
return addThunkArm(RelocType, S);
|
||||
else if (Config->EMachine == EM_MIPS)
|
||||
return addThunkMips(S);
|
||||
llvm_unreachable("add Thunk only supported for ARM and Mips");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // end namespace elf
|
||||
} // end namespace lld
|
||||
63
deps/lld/ELF/Thunks.h
vendored
Normal file
63
deps/lld/ELF/Thunks.h
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
//===- Thunks.h --------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_THUNKS_H
|
||||
#define LLD_ELF_THUNKS_H
|
||||
|
||||
#include "Relocations.h"
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
class SymbolBody;
|
||||
class ThunkSection;
|
||||
// Class to describe an instance of a Thunk.
|
||||
// A Thunk is a code-sequence inserted by the linker in between a caller and
|
||||
// the callee. The relocation to the callee is redirected to the Thunk, which
|
||||
// after executing transfers control to the callee. Typical uses of Thunks
|
||||
// include transferring control from non-pi to pi and changing state on
|
||||
// targets like ARM.
|
||||
//
|
||||
// Thunks can be created for DefinedRegular, Shared and Undefined Symbols.
|
||||
// Thunks are assigned to synthetic ThunkSections
|
||||
class Thunk {
|
||||
public:
|
||||
Thunk(const SymbolBody &Destination);
|
||||
virtual ~Thunk();
|
||||
|
||||
virtual uint32_t size() const { return 0; }
|
||||
virtual void writeTo(uint8_t *Buf, ThunkSection &IS) const {}
|
||||
|
||||
// All Thunks must define at least one symbol ThunkSym so that we can
|
||||
// redirect relocations to it.
|
||||
virtual void addSymbols(ThunkSection &IS) {}
|
||||
|
||||
// Some Thunks must be placed immediately before their Target as they elide
|
||||
// a branch and fall through to the first Symbol in the Target.
|
||||
virtual InputSection *getTargetInputSection() const { return nullptr; }
|
||||
|
||||
// To reuse a Thunk the caller as identified by the RelocType must be
|
||||
// compatible with it.
|
||||
virtual bool isCompatibleWith(uint32_t RelocType) const { return true; }
|
||||
|
||||
// The alignment requirement for this Thunk, defaults to the size of the
|
||||
// typical code section alignment.
|
||||
const SymbolBody &Destination;
|
||||
SymbolBody *ThunkSym;
|
||||
uint64_t Offset;
|
||||
uint32_t Alignment = 4;
|
||||
};
|
||||
|
||||
// For a Relocation to symbol S create a Thunk to be added to a synthetic
|
||||
// ThunkSection. At present there are implementations for ARM and Mips Thunks.
|
||||
Thunk *addThunk(uint32_t RelocType, SymbolBody &S);
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
1913
deps/lld/ELF/Writer.cpp
vendored
Normal file
1913
deps/lld/ELF/Writer.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
61
deps/lld/ELF/Writer.h
vendored
Normal file
61
deps/lld/ELF/Writer.h
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
//===- Writer.h -------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_WRITER_H
|
||||
#define LLD_ELF_WRITER_H
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
class InputFile;
|
||||
class OutputSection;
|
||||
class InputSectionBase;
|
||||
template <class ELFT> class ObjectFile;
|
||||
template <class ELFT> class SymbolTable;
|
||||
template <class ELFT> void writeResult();
|
||||
template <class ELFT> void markLive();
|
||||
bool isRelroSection(const OutputSection *Sec);
|
||||
|
||||
// This describes a program header entry.
|
||||
// Each contains type, access flags and range of output sections that will be
|
||||
// placed in it.
|
||||
struct PhdrEntry {
|
||||
PhdrEntry(unsigned Type, unsigned Flags) : p_type(Type), p_flags(Flags) {}
|
||||
void add(OutputSection *Sec);
|
||||
|
||||
uint64_t p_paddr = 0;
|
||||
uint64_t p_vaddr = 0;
|
||||
uint64_t p_memsz = 0;
|
||||
uint64_t p_filesz = 0;
|
||||
uint64_t p_offset = 0;
|
||||
uint32_t p_align = 0;
|
||||
uint32_t p_type = 0;
|
||||
uint32_t p_flags = 0;
|
||||
|
||||
OutputSection *First = nullptr;
|
||||
OutputSection *Last = nullptr;
|
||||
bool HasLMA = false;
|
||||
};
|
||||
|
||||
llvm::StringRef getOutputSectionName(llvm::StringRef Name);
|
||||
|
||||
template <class ELFT> uint32_t getMipsEFlags();
|
||||
|
||||
uint8_t getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag,
|
||||
llvm::StringRef FileName);
|
||||
|
||||
bool isMipsN32Abi(const InputFile *F);
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user