mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
Merge branch 'embed-lld'
Zig now depends on LLVM 5.0.0. For the latest version that supports LLVM 4.0.1, use 2a49c876be76dc98996a3251310728ad32b22363. Unfortunately we had to embed LLD into Zig due to some MACH-O related LLD bugs. One of them is already upstream and another is awaiting feedback on the llvm-dev mailing list. You can use cmake option -DZIG_FORCE_EXTERNAL_LLD=ON to still use external LLD if you want to live with the MACH-O bugs or if your system LLD is patched. Closes #273
This commit is contained in:
commit
d7a539906d
27
.travis.yml
27
.travis.yml
@ -1,23 +1,16 @@
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
dist: trusty
|
||||
osx_image: xcode8.3
|
||||
sudo: required
|
||||
language: cpp
|
||||
before_install:
|
||||
- sudo sh -c 'echo "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-4.0 main" >> /etc/apt/sources.list'
|
||||
- wget -O - http://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
|
||||
- sudo apt-get update -q
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_before_install; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_before_install; fi
|
||||
install:
|
||||
- sudo apt-get remove -y llvm-*
|
||||
- sudo rm -rf /usr/local/*
|
||||
- sudo apt-get install -y clang-4.0 libclang-4.0 libclang-4.0-dev llvm-4.0 llvm-4.0-dev liblld-4.0 liblld-4.0-dev cmake
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_install; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_install; fi
|
||||
script:
|
||||
- export CC=clang-4.0
|
||||
- export CXX=clang++-4.0
|
||||
- which $CC
|
||||
- which $CXX
|
||||
- echo $PATH
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd) -DZIG_LIBC_LIB_DIR=$(dirname $($CC -print-file-name=crt1.o)) -DZIG_LIBC_INCLUDE_DIR=$(echo -n | $CC -E -x c - -v 2>&1 | grep -B1 "End of search list." | head -n1 | cut -c 2- | sed "s/ .*//") -DZIG_LIBC_STATIC_LIB_DIR=$(dirname $($CC -print-file-name=crtbegin.o))
|
||||
- make VERBOSE=1
|
||||
- make install
|
||||
- ./zig build --build-file ../build.zig test
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_script; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_script; fi
|
||||
|
||||
160
CMakeLists.txt
160
CMakeLists.txt
@ -22,6 +22,9 @@ set(ZIG_EACH_LIB_RPATH off CACHE BOOL "Add each dynamic library to rpath for nat
|
||||
|
||||
option(ZIG_TEST_COVERAGE "Build Zig with test coverage instrumentation" OFF)
|
||||
|
||||
# To see what patches have been applied to LLD in this repository:
|
||||
# git log -p -- deps/lld
|
||||
option(ZIG_FORCE_EXTERNAL_LLD "If your system has the LLD patches use it instead of the embedded LLD" OFF)
|
||||
|
||||
|
||||
find_package(llvm)
|
||||
@ -31,8 +34,137 @@ link_directories(${LLVM_LIBDIRS})
|
||||
find_package(clang)
|
||||
include_directories(${CLANG_INCLUDE_DIRS})
|
||||
|
||||
find_package(lld)
|
||||
include_directories(${LLD_INCLUDE_DIRS})
|
||||
if(ZIG_FORCE_EXTERNAL_LLD)
|
||||
find_package(lld)
|
||||
include_directories(${LLD_INCLUDE_DIRS})
|
||||
else()
|
||||
set(EMBEDDED_LLD_LIB_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/Driver/DarwinLdDriver.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/Config/Version.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/LayoutPass.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/ArchHandler.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/ArchHandler_arm64.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/ObjCPass.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryReader.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/CompactUnwindPass.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/TLVPass.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/MachONormalizedFileYAML.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/GOTPass.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/MachONormalizedFileBinaryWriter.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/MachOLinkingContext.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/ShimPass.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/WriterMachO.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/StubsPass.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/ArchHandler_arm.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/MachO/MachONormalizedFileFromAtoms.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/ReaderWriter/FileArchive.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/Core/TargetOptionsCommandFlags.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/Core/File.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/Core/Error.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/Core/SymbolTable.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/Core/Reader.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/Core/Reproduce.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/Core/Writer.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/Core/LinkingContext.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/Core/Resolver.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/lib/Core/DefinedAtom.cpp"
|
||||
)
|
||||
set(EMBEDDED_LLD_ELF_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/ScriptLexer.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/AMDGPU.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/PPC.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/ARM.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/AVR.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/SPARCV9.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/Mips.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/AArch64.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/X86_64.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/PPC64.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/MipsArchTree.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Arch/X86.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/GdbIndex.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Driver.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Relocations.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Error.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/LTO.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Strings.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/ScriptParser.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/MarkLive.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/SyntheticSections.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/SymbolTable.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/LinkerScript.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/EhFrame.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Target.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Filesystem.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/OutputSections.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Symbols.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/ICF.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/InputFiles.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Thunks.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/DriverUtils.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/Writer.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/InputSection.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF/MapFile.cpp"
|
||||
)
|
||||
set(EMBEDDED_LLD_COFF_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/DLL.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/Driver.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/Chunks.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/PDB.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/Error.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/LTO.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/Strings.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/MarkLive.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/SymbolTable.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/Symbols.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/ICF.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/InputFiles.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/DriverUtils.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/Writer.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF/MapFile.cpp"
|
||||
)
|
||||
add_library(embedded_lld_lib ${EMBEDDED_LLD_LIB_SOURCES})
|
||||
add_library(embedded_lld_elf ${EMBEDDED_LLD_ELF_SOURCES})
|
||||
add_library(embedded_lld_coff ${EMBEDDED_LLD_COFF_SOURCES})
|
||||
set_target_properties(embedded_lld_lib PROPERTIES
|
||||
COMPILE_FLAGS "-std=c++11 -fno-exceptions -fno-rtti -Wno-comment"
|
||||
LINK_FLAGS " "
|
||||
)
|
||||
set_target_properties(embedded_lld_elf PROPERTIES
|
||||
COMPILE_FLAGS "-std=c++11 -fno-exceptions -fno-rtti -Wno-comment"
|
||||
LINK_FLAGS " "
|
||||
)
|
||||
set_target_properties(embedded_lld_coff PROPERTIES
|
||||
COMPILE_FLAGS "-std=c++11 -fno-exceptions -fno-rtti -Wno-comment"
|
||||
LINK_FLAGS " "
|
||||
)
|
||||
target_include_directories(embedded_lld_lib PUBLIC
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/include"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld-prebuilt"
|
||||
)
|
||||
target_include_directories(embedded_lld_elf PUBLIC
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/ELF"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/include"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld-prebuilt/ELF"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld-prebuilt"
|
||||
)
|
||||
target_include_directories(embedded_lld_coff PUBLIC
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/COFF"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld/include"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld-prebuilt/COFF"
|
||||
"${CMAKE_SOURCE_DIR}/deps/lld-prebuilt"
|
||||
)
|
||||
set(LLD_INCLUDE_DIRS "")
|
||||
set(LLD_LIBRARIES
|
||||
embedded_lld_elf
|
||||
embedded_lld_coff
|
||||
embedded_lld_lib
|
||||
)
|
||||
endif()
|
||||
|
||||
find_package(Threads)
|
||||
|
||||
@ -65,26 +197,6 @@ set(ZIG_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"
|
||||
)
|
||||
|
||||
set(ZIG_HOST_LINK_VERSION)
|
||||
if (APPLE)
|
||||
set(LD_V_OUTPUT)
|
||||
execute_process(
|
||||
COMMAND sh -c "${CMAKE_LINKER} -v 2>&1 | head -1"
|
||||
RESULT_VARIABLE HAD_ERROR
|
||||
OUTPUT_VARIABLE LD_V_OUTPUT
|
||||
)
|
||||
if (NOT HAD_ERROR)
|
||||
if ("${LD_V_OUTPUT}" MATCHES ".*ld64-([0-9.]+).*")
|
||||
string(REGEX REPLACE ".*ld64-([0-9.]+).*" "\\1" ZIG_HOST_LINK_VERSION ${LD_V_OUTPUT})
|
||||
elseif ("${LD_V_OUTPUT}" MATCHES "[^0-9]*([0-9.]+).*")
|
||||
string(REGEX REPLACE "[^0-9]*([0-9.]+).*" "\\1" ZIG_HOST_LINK_VERSION ${LD_V_OUTPUT})
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "${CMAKE_LINKER} failed with status ${HAD_ERROR}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
set(C_HEADERS_DEST "lib/zig/include")
|
||||
set(ZIG_STD_DEST "lib/zig/std")
|
||||
set(CONFIGURE_OUT_FILE "${CMAKE_BINARY_DIR}/config.h")
|
||||
@ -297,10 +409,10 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/mem.zig" DESTINATION "${ZIG_STD_DEST}")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/net.zig" DESTINATION "${ZIG_STD_DEST}")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/os/child_process.zig" DESTINATION "${ZIG_STD_DEST}/os")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/os/darwin.zig" DESTINATION "${ZIG_STD_DEST}/os")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/os/darwin_x86_64.zig" DESTINATION "${ZIG_STD_DEST}/os")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/os/errno.zig" DESTINATION "${ZIG_STD_DEST}/os")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/os/darwin_errno.zig" DESTINATION "${ZIG_STD_DEST}/os")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/os/index.zig" DESTINATION "${ZIG_STD_DEST}/os")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/os/linux.zig" DESTINATION "${ZIG_STD_DEST}/os")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/os/linux_errno.zig" DESTINATION "${ZIG_STD_DEST}/os")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/os/linux_i386.zig" DESTINATION "${ZIG_STD_DEST}/os")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/os/linux_x86_64.zig" DESTINATION "${ZIG_STD_DEST}/os")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/os/path.zig" DESTINATION "${ZIG_STD_DEST}/os")
|
||||
|
||||
@ -76,7 +76,7 @@ the Zig compiler itself:
|
||||
These libraries must be installed on your system, with the development files
|
||||
available. The Zig compiler links against them.
|
||||
|
||||
* LLVM, Clang, and LLD libraries == 4.x
|
||||
* LLVM, Clang, and LLD libraries == 5.x
|
||||
|
||||
### Debug / Development Build
|
||||
|
||||
|
||||
8
ci/travis_linux_before_install
Executable file
8
ci/travis_linux_before_install
Executable file
@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -x
|
||||
|
||||
sudo sh -c 'echo "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-5.0 main" >> /etc/apt/sources.list'
|
||||
wget -O - http://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
|
||||
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
sudo apt-get update -q
|
||||
7
ci/travis_linux_install
Executable file
7
ci/travis_linux_install
Executable file
@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -x
|
||||
|
||||
sudo apt-get remove -y llvm-*
|
||||
sudo rm -rf /usr/local/*
|
||||
sudo apt-get install -y clang-5.0 libclang-5.0 libclang-5.0-dev llvm-5.0 llvm-5.0-dev liblld-5.0 liblld-5.0-dev cmake
|
||||
15
ci/travis_linux_script
Executable file
15
ci/travis_linux_script
Executable file
@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -x
|
||||
|
||||
export CC=clang-5.0
|
||||
export CXX=clang++-5.0
|
||||
which $CC
|
||||
which $CXX
|
||||
echo $PATH
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd) -DZIG_LIBC_LIB_DIR=$(dirname $($CC -print-file-name=crt1.o)) -DZIG_LIBC_INCLUDE_DIR=$(echo -n | $CC -E -x c - -v 2>&1 | grep -B1 "End of search list." | head -n1 | cut -c 2- | sed "s/ .*//") -DZIG_LIBC_STATIC_LIB_DIR=$(dirname $($CC -print-file-name=crtbegin.o)) -DZIG_FORCE_EXTERNAL_LLD=ON
|
||||
make VERBOSE=1
|
||||
make install
|
||||
./zig build --build-file ../build.zig test
|
||||
5
ci/travis_osx_before_install
Executable file
5
ci/travis_osx_before_install
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -x
|
||||
|
||||
brew update
|
||||
19
ci/travis_osx_install
Executable file
19
ci/travis_osx_install
Executable file
@ -0,0 +1,19 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -x
|
||||
|
||||
brew install gcc@7
|
||||
brew outdated gcc@7 || brew upgrade gcc@7
|
||||
brew link --overwrite gcc@7
|
||||
|
||||
SRC_DIR=$(pwd)
|
||||
PREFIX_DIR=$HOME/local/llvm5
|
||||
export CC=/usr/local/opt/gcc/bin/gcc-7
|
||||
export CXX=/usr/local/opt/gcc/bin/g++-7
|
||||
|
||||
mkdir -p $HOME/local
|
||||
cd $HOME/local
|
||||
wget http://s3.amazonaws.com/superjoe/temp/llvm5.tar.xz
|
||||
tar xfp llvm5.tar.xz
|
||||
|
||||
cd $SRC_DIR
|
||||
15
ci/travis_osx_script
Executable file
15
ci/travis_osx_script
Executable file
@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -x
|
||||
|
||||
PREFIX_DIR=$HOME/local/llvm5
|
||||
export CC=/usr/local/opt/gcc/bin/gcc-7
|
||||
export CXX=/usr/local/opt/gcc/bin/g++-7
|
||||
|
||||
echo $PATH
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_PREFIX_PATH=$PREFIX_DIR -DCMAKE_INSTALL_PREFIX=$(pwd) -DZIG_LIBC_LIB_DIR=$(dirname $($CC -print-file-name=crt1.o)) -DZIG_LIBC_INCLUDE_DIR=$(echo -n | $CC -E -x c - -v 2>&1 | grep -B1 "End of search list." | head -n1 | cut -c 2- | sed "s/ .*//") -DZIG_LIBC_STATIC_LIB_DIR=$(dirname $($CC -print-file-name=crtbegin.o)) -DZIG_FORCE_EXTERNAL_LLD=ON
|
||||
make VERBOSE=1
|
||||
make install
|
||||
./zig build --build-file ../build.zig test
|
||||
@ -8,14 +8,14 @@
|
||||
|
||||
find_path(CLANG_INCLUDE_DIRS NAMES clang/Frontend/ASTUnit.h
|
||||
PATHS
|
||||
/usr/lib/llvm-4.0/include
|
||||
/usr/lib/llvm-5.0/include
|
||||
/mingw64/include)
|
||||
|
||||
macro(FIND_AND_ADD_CLANG_LIB _libname_)
|
||||
string(TOUPPER ${_libname_} _prettylibname_)
|
||||
find_library(CLANG_${_prettylibname_}_LIB NAMES ${_libname_}
|
||||
PATHS
|
||||
/usr/lib/llvm-4.0/lib
|
||||
/usr/lib/llvm-5.0/lib
|
||||
/mingw64/lib
|
||||
/c/msys64/mingw64/lib
|
||||
c:\\msys64\\mingw64\\lib)
|
||||
|
||||
@ -8,10 +8,10 @@
|
||||
|
||||
find_path(LLD_INCLUDE_DIRS NAMES lld/Driver/Driver.h
|
||||
PATHS
|
||||
/usr/lib/llvm-4.0/include
|
||||
/usr/lib/llvm-5.0/include
|
||||
/mingw64/include)
|
||||
|
||||
find_library(LLD_LIBRARY NAMES lld-4.0 lld PATHS /usr/lib/llvm-4.0/lib)
|
||||
find_library(LLD_LIBRARY NAMES lld-5.0 lld PATHS /usr/lib/llvm-5.0/lib)
|
||||
if(EXISTS ${LLD_LIBRARY})
|
||||
set(LLD_LIBRARIES ${LLD_LIBRARY})
|
||||
else()
|
||||
@ -19,7 +19,7 @@ else()
|
||||
string(TOUPPER ${_libname_} _prettylibname_)
|
||||
find_library(LLD_${_prettylibname_}_LIB NAMES ${_libname_}
|
||||
PATHS
|
||||
/usr/lib/llvm-4.0/lib
|
||||
/usr/lib/llvm-5.0/lib
|
||||
/mingw64/lib
|
||||
/c/msys64/mingw64/lib
|
||||
c:/msys64/mingw64/lib)
|
||||
|
||||
@ -8,12 +8,12 @@
|
||||
# LLVM_LIBDIRS
|
||||
|
||||
find_program(LLVM_CONFIG_EXE
|
||||
NAMES llvm-config llvm-config-4.0
|
||||
NAMES llvm-config llvm-config-5.0
|
||||
PATHS
|
||||
"/mingw64/bin"
|
||||
"/c/msys64/mingw64/bin"
|
||||
"c:/msys64/mingw64/bin"
|
||||
"C:/Libraries/llvm-4.0.0/bin")
|
||||
"C:/Libraries/llvm-5.0.0/bin")
|
||||
|
||||
execute_process(
|
||||
COMMAND ${LLVM_CONFIG_EXE} --libs
|
||||
|
||||
174
deps/lld-prebuilt/COFF/Options.inc
vendored
Normal file
174
deps/lld-prebuilt/COFF/Options.inc
vendored
Normal file
@ -0,0 +1,174 @@
|
||||
/*===- TableGen'erated file -------------------------------------*- C++ -*-===*\
|
||||
|* *|
|
||||
|* Option Parsing Definitions *|
|
||||
|* *|
|
||||
|* Automatically generated file, do not edit! *|
|
||||
|* *|
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
/////////
|
||||
// Prefixes
|
||||
|
||||
#ifdef PREFIX
|
||||
#define COMMA ,
|
||||
PREFIX(prefix_0, {nullptr})
|
||||
PREFIX(prefix_2, {"/" COMMA "-" COMMA nullptr})
|
||||
PREFIX(prefix_1, {"/" COMMA "-" COMMA "-?" COMMA nullptr})
|
||||
PREFIX(prefix_3, {"/?" COMMA "-?" COMMA nullptr})
|
||||
#undef COMMA
|
||||
#endif // PREFIX
|
||||
|
||||
/////////
|
||||
// Groups
|
||||
|
||||
#ifdef OPTION
|
||||
|
||||
//////////
|
||||
// Options
|
||||
|
||||
OPTION(prefix_0, "<input>", INPUT, Input, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_0, "<unknown>", UNKNOWN, Unknown, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "align:", align, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Section alignment", nullptr, nullptr)
|
||||
OPTION(prefix_1, "allowbind:no", allowbind_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Disable DLL binding", nullptr, nullptr)
|
||||
OPTION(prefix_1, "allowbind", allowbind, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "allowisolation:no", allowisolation_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Set NO_ISOLATION bit", nullptr, nullptr)
|
||||
OPTION(prefix_1, "allowisolation", allowisolation, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "alternatename:", alternatename, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Define weak alias", nullptr, nullptr)
|
||||
OPTION(prefix_1, "appcontainer:no", appcontainer_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Image can only be run in an app container", nullptr, nullptr)
|
||||
OPTION(prefix_1, "appcontainer", appcontainer, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "base:", base, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Base address of the program", nullptr, nullptr)
|
||||
OPTION(prefix_1, "debugtype:", debugtype, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Debug Info Options", nullptr, nullptr)
|
||||
OPTION(prefix_1, "debug", debug, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Embed a symbol table in the image", nullptr, nullptr)
|
||||
OPTION(prefix_2, "def:", deffile, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Use module-definition file", nullptr, nullptr)
|
||||
OPTION(prefix_1, "defaultlib:", defaultlib, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Add the library to the list of input files", nullptr, nullptr)
|
||||
OPTION(prefix_1, "delay:", delay, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "delayload:", delayload, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Delay loaded DLL name", nullptr, nullptr)
|
||||
OPTION(prefix_1, "disallowlib:", disallowlib, Joined, INVALID, nodefaultlib, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "dll", dll, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Create a DLL", nullptr, nullptr)
|
||||
OPTION(prefix_1, "driver:", driver, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Generate a Windows NT Kernel Mode Driver", nullptr, nullptr)
|
||||
OPTION(prefix_1, "dynamicbase:no", dynamicbase_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Disable address space layout randomization", nullptr, nullptr)
|
||||
OPTION(prefix_1, "dynamicbase", dynamicbase, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "editandcontinue", editandcontinue, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "entry:", entry, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Name of entry point symbol", nullptr, nullptr)
|
||||
OPTION(prefix_1, "errorlimit:", errorlimit, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Maximum number of errors to emit before stopping (0 = no limit)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "errorreport:", errorreport, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "export:", export, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Export a function", nullptr, nullptr)
|
||||
OPTION(prefix_1, "failifmismatch:", failifmismatch, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"", nullptr, nullptr)
|
||||
OPTION(prefix_1, "fastfail", fastfail, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "fixed:no", fixed_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Enable base relocations", nullptr, nullptr)
|
||||
OPTION(prefix_1, "fixed", fixed, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "force:unresolved", force_unresolved, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "force", force, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Allow undefined symbols when creating executables", nullptr, nullptr)
|
||||
OPTION(prefix_1, "functionpadmin", functionpadmin, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "guardsym:", guardsym, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "heap:", heap, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Size of the heap", nullptr, nullptr)
|
||||
OPTION(prefix_1, "help", help, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "highentropyva:no", highentropyva_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Set HIGH_ENTROPY_VA bit", nullptr, nullptr)
|
||||
OPTION(prefix_1, "highentropyva", highentropyva, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "idlout:", idlout, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "ignore:", ignore, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "ignoreidl", ignoreidl, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "implib:", implib, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Import library name", nullptr, nullptr)
|
||||
OPTION(prefix_2, "include:", incl, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Force symbol to be added to symbol table as undefined one", nullptr, nullptr)
|
||||
OPTION(prefix_1, "incremental:no", no_incremental, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "incremental", incremental, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "largeaddressaware:no", largeaddressaware_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Disable large addresses", nullptr, nullptr)
|
||||
OPTION(prefix_1, "largeaddressaware", largeaddressaware, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "libpath:", libpath, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Additional library search path", nullptr, nullptr)
|
||||
OPTION(prefix_1, "linkrepro:", linkrepro, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Dump linker invocation and input files for debugging", nullptr, nullptr)
|
||||
OPTION(prefix_2, "lldmap:", lldmap_file, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "lldmap", lldmap, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "lldsavetemps", lldsavetemps, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Save temporary files instead of deleting them", nullptr, nullptr)
|
||||
OPTION(prefix_1, "machine:", machine, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Specify target platform", nullptr, nullptr)
|
||||
OPTION(prefix_1, "manifest:", manifest_colon, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Create manifest file", nullptr, nullptr)
|
||||
OPTION(prefix_1, "manifestdependency:", manifestdependency, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Attributes for <dependency> in manifest file", nullptr, nullptr)
|
||||
OPTION(prefix_1, "manifestfile:", manifestfile, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Manifest file path", nullptr, nullptr)
|
||||
OPTION(prefix_1, "manifestinput:", manifestinput, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Specify manifest file", nullptr, nullptr)
|
||||
OPTION(prefix_1, "manifestuac:", manifestuac, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"User access control", nullptr, nullptr)
|
||||
OPTION(prefix_1, "manifest", manifest, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "maxilksize:", maxilksize, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "merge:", merge, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Combine sections", nullptr, nullptr)
|
||||
OPTION(prefix_1, "mllvm:", mllvm, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Options to pass to LLVM", nullptr, nullptr)
|
||||
OPTION(prefix_1, "msvclto", msvclto, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "nodefaultlib:", nodefaultlib, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Remove a default library", nullptr, nullptr)
|
||||
OPTION(prefix_1, "nodefaultlib", nodefaultlib_all, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "noentry", noentry, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "nologo", nologo, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "nopdb", nopdb, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Disable PDB generation for DWARF users", nullptr, nullptr)
|
||||
OPTION(prefix_1, "nosymtab", nosymtab, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "nxcompat:no", nxcompat_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Disable data execution provention", nullptr, nullptr)
|
||||
OPTION(prefix_1, "nxcompat", nxcompat, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "opt:", opt, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Control optimizations", nullptr, nullptr)
|
||||
OPTION(prefix_1, "out:", out, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Path to file to write output", nullptr, nullptr)
|
||||
OPTION(prefix_1, "pdb:", pdb, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"PDB file path", nullptr, nullptr)
|
||||
OPTION(prefix_1, "pdbaltpath:", pdbaltpath, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "profile", profile, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "safeseh:no", safeseh_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Produce an image with Safe Exception Handler", nullptr, nullptr)
|
||||
OPTION(prefix_1, "safeseh", safeseh, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "section:", section, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Specify section attributes", nullptr, nullptr)
|
||||
OPTION(prefix_1, "stack:", stack, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Size of the stack", nullptr, nullptr)
|
||||
OPTION(prefix_1, "stub:", stub, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Specify DOS stub file", nullptr, nullptr)
|
||||
OPTION(prefix_1, "subsystem:", subsystem, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Specify subsystem", nullptr, nullptr)
|
||||
OPTION(prefix_1, "swaprun:cd", swaprun_cd, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "swaprun:net", swaprun_net, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "throwingnew", throwingnew, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "tlbid:", tlbid, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "tlbout:", tlbout, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "tsaware:no", tsaware_no, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Create non-Terminal Server aware executable", nullptr, nullptr)
|
||||
OPTION(prefix_1, "tsaware", tsaware, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "verbose:", verbose_all, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "verbose", verbose, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "version:", version, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Specify a version number in the PE header", nullptr, nullptr)
|
||||
OPTION(prefix_1, "wx:no", wx_no, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "wx", wx, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_3, "", help_q, Flag, INVALID, help, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
#endif // OPTION
|
||||
189
deps/lld-prebuilt/DarwinLdOptions.inc
vendored
Normal file
189
deps/lld-prebuilt/DarwinLdOptions.inc
vendored
Normal file
@ -0,0 +1,189 @@
|
||||
/*===- TableGen'erated file -------------------------------------*- C++ -*-===*\
|
||||
|* *|
|
||||
|* Option Parsing Definitions *|
|
||||
|* *|
|
||||
|* Automatically generated file, do not edit! *|
|
||||
|* *|
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
/////////
|
||||
// Prefixes
|
||||
|
||||
#ifdef PREFIX
|
||||
#define COMMA ,
|
||||
PREFIX(prefix_0, {nullptr})
|
||||
PREFIX(prefix_1, {"-" COMMA nullptr})
|
||||
#undef COMMA
|
||||
#endif // PREFIX
|
||||
|
||||
/////////
|
||||
// Groups
|
||||
|
||||
#ifdef OPTION
|
||||
OPTION(nullptr, "opts", grp_bundle, Group, INVALID, INVALID, nullptr, 0, 0,
|
||||
"BUNDLE EXECUTABLE OPTIONS", nullptr, nullptr)
|
||||
OPTION(nullptr, "opts", grp_dylib, Group, INVALID, INVALID, nullptr, 0, 0,
|
||||
"DYLIB EXECUTABLE OPTIONS", nullptr, nullptr)
|
||||
OPTION(nullptr, "outs", grp_kind, Group, INVALID, INVALID, nullptr, 0, 0,
|
||||
"OUTPUT KIND", nullptr, nullptr)
|
||||
OPTION(nullptr, "libs", grp_libs, Group, INVALID, INVALID, nullptr, 0, 0,
|
||||
"LIBRARY OPTIONS", nullptr, nullptr)
|
||||
OPTION(nullptr, "opts", grp_main, Group, INVALID, INVALID, nullptr, 0, 0,
|
||||
"MAIN EXECUTABLE OPTIONS", nullptr, nullptr)
|
||||
OPTION(nullptr, "obsolete", grp_obsolete, Group, INVALID, INVALID, nullptr, 0, 0,
|
||||
"OBSOLETE OPTIONS", nullptr, nullptr)
|
||||
OPTION(nullptr, "opts", grp_opts, Group, INVALID, INVALID, nullptr, 0, 0,
|
||||
"OPTIMIZATIONS", nullptr, nullptr)
|
||||
|
||||
//////////
|
||||
// Options
|
||||
|
||||
OPTION(prefix_0, "<input>", INPUT, Input, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_0, "<unknown>", UNKNOWN, Unknown, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "all_load", all_load, Flag, grp_libs, INVALID, nullptr, 0, 0,
|
||||
"Forces all members of all static libraries to be loaded", nullptr, nullptr)
|
||||
OPTION(prefix_1, "arch", arch, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Architecture to link", "<arch-name>", nullptr)
|
||||
OPTION(prefix_1, "bundle_loader", bundle_loader, Separate, grp_bundle, INVALID, nullptr, 0, 0,
|
||||
"The executable that will be loading this Mach-O bundle", "<path>", nullptr)
|
||||
OPTION(prefix_1, "bundle", bundle, Flag, grp_kind, INVALID, nullptr, 0, 0,
|
||||
"Create dynamic bundle", nullptr, nullptr)
|
||||
OPTION(prefix_1, "compatibility_version", compatibility_version, Separate, grp_dylib, INVALID, nullptr, 0, 0,
|
||||
"The dylib's compatibility version", "<version>", nullptr)
|
||||
OPTION(prefix_1, "current_version", current_version, Separate, grp_dylib, INVALID, nullptr, 0, 0,
|
||||
"The dylib's current version", "<version>", nullptr)
|
||||
OPTION(prefix_1, "data_in_code_info", data_in_code_info, Flag, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Force generation of a data in code load command", nullptr, nullptr)
|
||||
OPTION(prefix_1, "dead_strip", dead_strip, Flag, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Remove unreference code and data", nullptr, nullptr)
|
||||
OPTION(prefix_1, "demangle", demangle, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Demangles symbol names in errors and warnings", nullptr, nullptr)
|
||||
OPTION(prefix_1, "dependency_info", dependency_info, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Write binary list of files used during link", "<file>", nullptr)
|
||||
OPTION(prefix_1, "dylib_compatibility_version", dylib_compatibility_version, Separate, INVALID, compatibility_version, nullptr, 0, 0, nullptr, "<version>", nullptr)
|
||||
OPTION(prefix_1, "dylib_current_version", dylib_current_version, Separate, INVALID, current_version, nullptr, 0, 0, nullptr, "<version>", nullptr)
|
||||
OPTION(prefix_1, "dylib_install_name", dylib_install_name, Separate, INVALID, install_name, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "dylib", dylib, Flag, grp_kind, INVALID, nullptr, 0, 0,
|
||||
"Create dynamic library", nullptr, nullptr)
|
||||
OPTION(prefix_1, "dynamic", dynamic, Flag, grp_kind, INVALID, nullptr, 0, 0,
|
||||
"Create dynamic executable (default)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "execute", execute, Flag, grp_kind, INVALID, nullptr, 0, 0,
|
||||
"Create main executable (default)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "export_dynamic", export_dynamic, Flag, grp_main, INVALID, nullptr, 0, 0,
|
||||
"Preserves all global symbols in main executables during LTO", nullptr, nullptr)
|
||||
OPTION(prefix_1, "exported_symbols_list", exported_symbols_list, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Restricts which symbols will be exported", "<file-path>", nullptr)
|
||||
OPTION(prefix_1, "exported_symbol", exported_symbol, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Restricts which symbols will be exported", "<symbol>", nullptr)
|
||||
OPTION(prefix_1, "e", entry, Separate, grp_main, INVALID, nullptr, 0, 0,
|
||||
"entry symbol name", "<entry-name>", nullptr)
|
||||
OPTION(prefix_1, "filelist", filelist, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"file containing paths to input files", "<path>", nullptr)
|
||||
OPTION(prefix_1, "flat_namespace", flat_namespace, Flag, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Resolves symbols in any (transitively) linked dynamic libraries. Source libraries are not recorded: dyld will re-search all images at runtime and use the first definition found.", nullptr, nullptr)
|
||||
OPTION(prefix_1, "force_load", force_load, Separate, grp_libs, INVALID, nullptr, 0, 0,
|
||||
"Forces all members of specified static libraries to be loaded", "<library-path>", nullptr)
|
||||
OPTION(prefix_1, "framework", framework, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Base name of framework searched for in -F directories", "<name>", nullptr)
|
||||
OPTION(prefix_1, "function_starts", function_starts, Flag, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Force generation of a function starts load command", nullptr, nullptr)
|
||||
OPTION(prefix_1, "F", F, JoinedOrSeparate, grp_libs, INVALID, nullptr, 0, 0,
|
||||
"Add directory to framework search path", "<dir>", nullptr)
|
||||
OPTION(prefix_1, "image_base", image_base, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "install_name", install_name, Separate, grp_dylib, INVALID, nullptr, 0, 0,
|
||||
"The dylib's install name", "<path>", nullptr)
|
||||
OPTION(prefix_1, "ios_simulator_version_min", ios_simulator_version_min, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Minimum iOS simulator version", "<version>", nullptr)
|
||||
OPTION(prefix_1, "ios_version_min", ios_version_min, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Minimum iOS version", "<version>", nullptr)
|
||||
OPTION(prefix_1, "iphoneos_version_min", iphoneos_version_min, Separate, INVALID, ios_version_min, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "keep_private_externs", keep_private_externs, Flag, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Private extern (hidden) symbols should not be transformed into local symbols", nullptr, nullptr)
|
||||
OPTION(prefix_1, "L", L, JoinedOrSeparate, grp_libs, INVALID, nullptr, 0, 0,
|
||||
"Add directory to library search path", "<dir>", nullptr)
|
||||
OPTION(prefix_1, "l", l, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Base name of library searched for in -L directories", "<libname>", nullptr)
|
||||
OPTION(prefix_1, "macosx_version_min", macosx_version_min, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Minimum Mac OS X version", "<version>", nullptr)
|
||||
OPTION(prefix_1, "mark_dead_strippable_dylib", mark_dead_strippable_dylib, Flag, grp_dylib, INVALID, nullptr, 0, 0,
|
||||
"Marks the dylib as having no side effects during initialization", nullptr, nullptr)
|
||||
OPTION(prefix_1, "mllvm", mllvm, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Options to pass to LLVM during LTO", "<option>", nullptr)
|
||||
OPTION(prefix_1, "multi_module", multi_module, Flag, grp_obsolete, INVALID, nullptr, 0, 0,
|
||||
"Unsupported way to build dylibs", nullptr, nullptr)
|
||||
OPTION(prefix_1, "no_data_in_code_info", no_data_in_code_info, Flag, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Disable generation of a data in code load command", nullptr, nullptr)
|
||||
OPTION(prefix_1, "no_function_starts", no_function_starts, Flag, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Disable generation of a function starts load command", nullptr, nullptr)
|
||||
OPTION(prefix_1, "no_objc_category_merging", no_objc_category_merging, Flag, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Disables the optimisation which merges Objective-C categories on a class in to the class itself.", nullptr, nullptr)
|
||||
OPTION(prefix_1, "no_pie", no_pie, Flag, grp_main, INVALID, nullptr, 0, 0,
|
||||
"Do not create Position Independent Executable", nullptr, nullptr)
|
||||
OPTION(prefix_1, "no_version_load_command", no_version_load_command, Flag, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Disable generation of a version load command", nullptr, nullptr)
|
||||
OPTION(prefix_1, "objc_gc_compaction", objc_gc_compaction, Flag, grp_obsolete, INVALID, nullptr, 0, 0,
|
||||
"Unsupported ObjC GC option", nullptr, nullptr)
|
||||
OPTION(prefix_1, "objc_gc_only", objc_gc_only, Flag, grp_obsolete, INVALID, nullptr, 0, 0,
|
||||
"Unsupported ObjC GC option", nullptr, nullptr)
|
||||
OPTION(prefix_1, "objc_gc", objc_gc, Flag, grp_obsolete, INVALID, nullptr, 0, 0,
|
||||
"Unsupported ObjC GC option", nullptr, nullptr)
|
||||
OPTION(prefix_1, "order_file", order_file, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"re-order and move specified symbols to start of their section", "<file-path>", nullptr)
|
||||
OPTION(prefix_1, "o", output, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Output file path", "<path>", nullptr)
|
||||
OPTION(prefix_1, "path_exists", path_exists, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Used with -test_file_usage to declare a path", "<path>", nullptr)
|
||||
OPTION(prefix_1, "pie", pie, Flag, grp_main, INVALID, nullptr, 0, 0,
|
||||
"Create Position Independent Executable (for ASLR)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "preload", preload, Flag, grp_kind, INVALID, nullptr, 0, 0,
|
||||
"Create binary for use with embedded systems", nullptr, nullptr)
|
||||
OPTION(prefix_1, "print_atoms", print_atoms, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Emit output as yaml atoms", nullptr, nullptr)
|
||||
OPTION(prefix_1, "rpath", rpath, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Add path to the runpath search path list for image being created", "<path>", nullptr)
|
||||
OPTION(prefix_1, "r", relocatable, Flag, grp_kind, INVALID, nullptr, 0, 0,
|
||||
"Create relocatable object file", nullptr, nullptr)
|
||||
OPTION(prefix_1, "sdk_version", sdk_version, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"SDK version", "<version>", nullptr)
|
||||
OPTION(prefix_1, "sectalign", sectalign, MultiArg, INVALID, INVALID, nullptr, 0, 3,
|
||||
"Alignment for segment/section", "<segname> <sectname> <alignment>", nullptr)
|
||||
OPTION(prefix_1, "sectcreate", sectcreate, MultiArg, INVALID, INVALID, nullptr, 0, 3,
|
||||
"Create section <segname>/<sectname> from contents of <file>", "<segname> <sectname> <file>", nullptr)
|
||||
OPTION(prefix_1, "seg1addr", seg1addr, Separate, INVALID, image_base, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "single_module", single_module, Flag, grp_obsolete, INVALID, nullptr, 0, 0,
|
||||
"Default for dylibs", nullptr, nullptr)
|
||||
OPTION(prefix_1, "source_version", source_version, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Source version", "<version>", nullptr)
|
||||
OPTION(prefix_1, "stack_size", stack_size, Separate, grp_main, INVALID, nullptr, 0, 0,
|
||||
"Specifies the maximum stack size for the main thread in a program. Must be a page-size multiple. (default=8Mb)", nullptr, nullptr)
|
||||
OPTION(prefix_1, "static", static, Flag, grp_kind, INVALID, nullptr, 0, 0,
|
||||
"Create static executable", nullptr, nullptr)
|
||||
OPTION(prefix_1, "syslibroot", syslibroot, Separate, grp_libs, INVALID, nullptr, 0, 0,
|
||||
"Add path to SDK to all absolute library search paths", "<dir>", nullptr)
|
||||
OPTION(prefix_1, "S", S, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Remove debug information (STABS or DWARF) from the output file", nullptr, nullptr)
|
||||
OPTION(prefix_1, "test_file_usage", test_file_usage, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Only files specified by -file_exists are considered to exist. Print which files would be used", nullptr, nullptr)
|
||||
OPTION(prefix_1, "twolevel_namespace", twolevel_namespace, Flag, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Resolves symbols in listed libraries only. Source libraries are recorded in the symbol table.", nullptr, nullptr)
|
||||
OPTION(prefix_1, "t", t, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Print the names of the input files as ld processes them", nullptr, nullptr)
|
||||
OPTION(prefix_1, "undefined", undefined, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Determines how undefined symbols are handled.", "<undefined>", nullptr)
|
||||
OPTION(prefix_1, "unexported_symbols_list", unexported_symbols_list, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Lists symbols that should not be exported", "<file-path>", nullptr)
|
||||
OPTION(prefix_1, "unexported_symbol", unexported_symbol, Separate, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"A symbol which should not be exported", "<symbol>", nullptr)
|
||||
OPTION(prefix_1, "upward-l", upward_l, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Base name of upward library searched for in -L directories", "<libname>", nullptr)
|
||||
OPTION(prefix_1, "upward_framework", upward_framework, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Base name of upward framework searched for in -F directories", "<name>", nullptr)
|
||||
OPTION(prefix_1, "upward_library", upward_library, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"path to upward dylib to link with", "<path>", nullptr)
|
||||
OPTION(prefix_1, "version_load_command", version_load_command, Flag, grp_opts, INVALID, nullptr, 0, 0,
|
||||
"Force generation of a version load command", nullptr, nullptr)
|
||||
OPTION(prefix_1, "v", v, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Print linker information", nullptr, nullptr)
|
||||
OPTION(prefix_1, "Z", Z, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not search standard directories for libraries or frameworks", nullptr, nullptr)
|
||||
#endif // OPTION
|
||||
352
deps/lld-prebuilt/ELF/Options.inc
vendored
Normal file
352
deps/lld-prebuilt/ELF/Options.inc
vendored
Normal file
@ -0,0 +1,352 @@
|
||||
/*===- TableGen'erated file -------------------------------------*- C++ -*-===*\
|
||||
|* *|
|
||||
|* Option Parsing Definitions *|
|
||||
|* *|
|
||||
|* Automatically generated file, do not edit! *|
|
||||
|* *|
|
||||
\*===----------------------------------------------------------------------===*/
|
||||
|
||||
/////////
|
||||
// Prefixes
|
||||
|
||||
#ifdef PREFIX
|
||||
#define COMMA ,
|
||||
PREFIX(prefix_0, {nullptr})
|
||||
PREFIX(prefix_1, {"-" COMMA nullptr})
|
||||
PREFIX(prefix_3, {"--" COMMA nullptr})
|
||||
PREFIX(prefix_2, {"--" COMMA "-" COMMA nullptr})
|
||||
#undef COMMA
|
||||
#endif // PREFIX
|
||||
|
||||
/////////
|
||||
// Groups
|
||||
|
||||
#ifdef OPTION
|
||||
|
||||
//////////
|
||||
// Options
|
||||
|
||||
OPTION(prefix_0, "<input>", INPUT, Input, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_0, "<unknown>", UNKNOWN, Unknown, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "(", start_group_paren, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, ")", end_group_paren, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "allow-multiple-definition", allow_multiple_definition, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Allow multiple definitions", nullptr, nullptr)
|
||||
OPTION(prefix_2, "allow-shlib-undefined", allow_shlib_undefined, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "as-needed", as_needed, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Only set DT_NEEDED for shared libraries if used", nullptr, nullptr)
|
||||
OPTION(prefix_2, "auxiliary", auxiliary, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Set DT_AUXILIARY field to the specified name", nullptr, nullptr)
|
||||
OPTION(prefix_2, "Bdynamic", Bdynamic, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Link against shared libraries", nullptr, nullptr)
|
||||
OPTION(prefix_2, "Bshareable", alias_shared_Bshareable, Flag, INVALID, shared, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "Bstatic", Bstatic, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not link against shared libraries", nullptr, nullptr)
|
||||
OPTION(prefix_2, "Bsymbolic-functions", Bsymbolic_functions, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Bind defined function symbols locally", nullptr, nullptr)
|
||||
OPTION(prefix_2, "Bsymbolic", Bsymbolic, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Bind defined symbols locally", nullptr, nullptr)
|
||||
OPTION(prefix_2, "build-id=", build_id_eq, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Generate build ID note", nullptr, nullptr)
|
||||
OPTION(prefix_2, "build-id", build_id, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Generate build ID note", nullptr, nullptr)
|
||||
OPTION(prefix_2, "b", alias_format_b, Separate, INVALID, format, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "call_shared", alias_Bdynamic_call_shared, Flag, INVALID, Bdynamic, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "color-diagnostics=", color_diagnostics_eq, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Use colors in diagnostics", nullptr, nullptr)
|
||||
OPTION(prefix_2, "color-diagnostics", color_diagnostics, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Use colors in diagnostics", nullptr, nullptr)
|
||||
OPTION(prefix_2, "compress-debug-sections=", compress_debug_sections, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Compress DWARF debug sections", nullptr, nullptr)
|
||||
OPTION(prefix_3, "cref", cref, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "dc", alias_define_common_dc, Flag, INVALID, define_common, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "define-common", define_common, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Assign space to common symbols", nullptr, nullptr)
|
||||
OPTION(prefix_2, "defsym=", defsym, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Define a symbol alias", nullptr, nullptr)
|
||||
OPTION(prefix_2, "defsym", alias_defsym, Separate, INVALID, defsym, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "demangle", demangle, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Demangle symbol names", nullptr, nullptr)
|
||||
OPTION(prefix_2, "detect-odr-violations", detect_odr_violations, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "disable-new-dtags", disable_new_dtags, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Disable new dynamic tags", nullptr, nullptr)
|
||||
OPTION(prefix_2, "disable-verify", disable_verify, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "discard-all", discard_all, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Delete all local symbols", nullptr, nullptr)
|
||||
OPTION(prefix_2, "discard-locals", discard_locals, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Delete temporary local symbols", nullptr, nullptr)
|
||||
OPTION(prefix_2, "discard-none", discard_none, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Keep all symbols in the symbol table", nullptr, nullptr)
|
||||
OPTION(prefix_2, "dn", alias_Bstatic_dn, Flag, INVALID, Bstatic, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "dp", alias_define_common_dp, Flag, INVALID, define_common, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "dynamic-linker", dynamic_linker, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Which dynamic linker to use", nullptr, nullptr)
|
||||
OPTION(prefix_2, "dynamic-list=", alias_dynamic_list, Joined, INVALID, dynamic_list, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "dynamic-list", dynamic_list, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Read a list of dynamic symbols", nullptr, nullptr)
|
||||
OPTION(prefix_2, "dy", alias_Bdynamic_dy, Flag, INVALID, Bdynamic, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "d", alias_define_common_d, Flag, INVALID, define_common, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "EB", EB, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "eh-frame-hdr", eh_frame_hdr, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header", nullptr, nullptr)
|
||||
OPTION(prefix_2, "EL", EL, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "emit-relocs", emit_relocs, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Generate relocations in output", nullptr, nullptr)
|
||||
OPTION(prefix_2, "enable-new-dtags", enable_new_dtags, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Enable new dynamic tags", nullptr, nullptr)
|
||||
OPTION(prefix_2, "end-group", end_group, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "end-lib", end_lib, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"End a grouping of objects that should be treated as if they were together in an archive", nullptr, nullptr)
|
||||
OPTION(prefix_2, "entry=", alias_entry_entry, Joined, INVALID, entry, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "entry", entry, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Name of entry point symbol", "<entry>", nullptr)
|
||||
OPTION(prefix_2, "error-limit=", alias_error_limit, Joined, INVALID, error_limit, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "error-limit", error_limit, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Maximum number of errors to emit before stopping (0 = no limit)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "error-unresolved-symbols", error_unresolved_symbols, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Report unresolved symbols as errors", nullptr, nullptr)
|
||||
OPTION(prefix_2, "exclude-libs=", alias_exclude_libs, Joined, INVALID, exclude_libs, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "exclude-libs", exclude_libs, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Exclude static libraries from automatic export", nullptr, nullptr)
|
||||
OPTION(prefix_2, "export-dynamic-symbol=", alias_export_dynamic_symbol, Joined, INVALID, export_dynamic_symbol, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "export-dynamic-symbol", export_dynamic_symbol, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Put a symbol in the dynamic symbol table", nullptr, nullptr)
|
||||
OPTION(prefix_2, "export-dynamic", export_dynamic, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Put symbols in the dynamic symbol table", nullptr, nullptr)
|
||||
OPTION(prefix_1, "E", alias_export_dynamic_E, Flag, INVALID, export_dynamic, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "e", alias_entry_e, JoinedOrSeparate, INVALID, entry, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "fatal-warnings", fatal_warnings, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Treat warnings as errors", nullptr, nullptr)
|
||||
OPTION(prefix_2, "filter=", filter, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Set DT_FILTER field to the specified name", nullptr, nullptr)
|
||||
OPTION(prefix_2, "fini=", alias_fini_fini, Joined, INVALID, fini, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "fini", fini, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Specify a finalizer function", "<symbol>", nullptr)
|
||||
OPTION(prefix_2, "format=", format, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Change the input format of the inputs following this option", "<input-format>", nullptr)
|
||||
OPTION(prefix_2, "full-shutdown", full_shutdown, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Perform a full shutdown instead of calling _exit", nullptr, nullptr)
|
||||
OPTION(prefix_1, "F", alias_filter, Separate, INVALID, filter, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "f", alias_auxiliary, Separate, INVALID, auxiliary, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "gc-sections", gc_sections, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Enable garbage collection of unused sections", nullptr, nullptr)
|
||||
OPTION(prefix_2, "gdb-index", gdb_index, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Generate .gdb_index section", nullptr, nullptr)
|
||||
OPTION(prefix_1, "G", G, JoinedOrSeparate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "g", g, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "hash-style=", alias_hash_style_hash_style, Joined, INVALID, hash_style, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "hash-style", hash_style, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Specify hash style (sysv, gnu or both)", nullptr, nullptr)
|
||||
OPTION(prefix_2, "help", help, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Print option help", nullptr, nullptr)
|
||||
OPTION(prefix_1, "h", alias_soname_h, JoinedOrSeparate, INVALID, soname, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "icf=all", icf_all, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Enable identical code folding", nullptr, nullptr)
|
||||
OPTION(prefix_2, "icf=none", icf_none, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Disable identical code folding", nullptr, nullptr)
|
||||
OPTION(prefix_2, "image-base=", image_base, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Set the base address", nullptr, nullptr)
|
||||
OPTION(prefix_2, "init=", alias_init_init, Joined, INVALID, init, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "init", init, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Specify an initializer function", "<symbol>", nullptr)
|
||||
OPTION(prefix_2, "library-path=", alias_L__library_path, Joined, INVALID, L, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "library=", alias_l__library, Joined, INVALID, l, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "lto-aa-pipeline=", lto_aa_pipeline, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"AA pipeline to run during LTO. Used in conjunction with -lto-newpm-passes", nullptr, nullptr)
|
||||
OPTION(prefix_2, "lto-newpm-passes=", lto_newpm_passes, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Passes to run during LTO", nullptr, nullptr)
|
||||
OPTION(prefix_2, "lto-O", lto_O, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Optimization level for LTO", "<opt-level>", nullptr)
|
||||
OPTION(prefix_2, "lto-partitions=", lto_partitions, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Number of LTO codegen partitions", nullptr, nullptr)
|
||||
OPTION(prefix_1, "L", L, JoinedOrSeparate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Add a directory to the library search path", "<dir>", nullptr)
|
||||
OPTION(prefix_1, "l", l, JoinedOrSeparate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Root name of library to use", "<libName>", nullptr)
|
||||
OPTION(prefix_2, "Map=", alias_Map_eq, Joined, INVALID, Map, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "Map", Map, JoinedOrSeparate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Print a link map to the specified file", nullptr, nullptr)
|
||||
OPTION(prefix_2, "mllvm", mllvm, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "M", alias_print_map_M, Flag, INVALID, print_map, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "m", m, JoinedOrSeparate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Set target emulation", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-add-needed", no_add_needed, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-allow-shlib-undefined", no_allow_shlib_undefined, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-as-needed", no_as_needed, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Always DT_NEEDED for shared libraries", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-color-diagnostics", no_color_diagnostics, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not use colors in diagnostics", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-copy-dt-needed-entries", no_copy_dt_needed_entries, Flag, INVALID, no_add_needed, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-define-common", no_define_common, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not assign space to common symbols", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-demangle", no_demangle, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not demangle symbol names", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-dynamic-linker", no_dynamic_linker, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Inhibit output of .interp section", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-export-dynamic", no_export_dynamic, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-fatal-warnings", no_fatal_warnings, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-gc-sections", no_gc_sections, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Disable garbage collection of unused sections", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-gnu-unique", no_gnu_unique, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Disable STB_GNU_UNIQUE symbol binding", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-keep-memory", no_keep_memory, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-mmap-output-file", no_mmap_output_file, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-rosegment", no_rosegment, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not put read-only non-executable sections in their own segment", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-threads", no_threads, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not run the linker multi-threaded", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-undefined-version", no_undefined_version, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Report version scripts that refer undefined symbols", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-undefined", no_undefined, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Report unresolved symbols even if the linker is creating a shared library", nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-warn-common", no_warn_common, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-warn-mismatch", no_warn_mismatch, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "no-whole-archive", no_whole_archive, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Restores the default behavior of loading archive members", nullptr, nullptr)
|
||||
OPTION(prefix_2, "noinhibit-exec", noinhibit_exec, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Retain the executable output file whenever it is still usable", nullptr, nullptr)
|
||||
OPTION(prefix_2, "non_shared", alias_Bstatic_non_shared, Flag, INVALID, Bstatic, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "nopie", nopie, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Do not create a position independent executable", nullptr, nullptr)
|
||||
OPTION(prefix_2, "nostdlib", nostdlib, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Only search directories specified on the command line", nullptr, nullptr)
|
||||
OPTION(prefix_1, "N", alias_omagic, Flag, INVALID, omagic, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_3, "oformat", oformat, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Specify the binary format for the output object file", "<format>", nullptr)
|
||||
OPTION(prefix_3, "omagic", omagic, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Set the text and data sections to be readable and writable", "<magic>", nullptr)
|
||||
OPTION(prefix_3, "opt-remarks-filename", opt_remarks_filename, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"YAML output file for optimization remarks", nullptr, nullptr)
|
||||
OPTION(prefix_3, "opt-remarks-with-hotness", opt_remarks_with_hotness, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Include hotness informations in the optimization remarks file", nullptr, nullptr)
|
||||
OPTION(prefix_3, "output=", alias_o_output, Joined, INVALID, o, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_3, "output", alias_o_output2, Separate, INVALID, o, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "O", O, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Optimize output file size", nullptr, nullptr)
|
||||
OPTION(prefix_1, "o", o, JoinedOrSeparate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Path to file to write output", "<path>", nullptr)
|
||||
OPTION(prefix_2, "pic-executable", alias_pie_pic_executable, Flag, INVALID, pie, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "pie", pie, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Create a position independent executable", nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt=", plugin_opt_eq, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin-opt", plugin_opt, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin=", plugin_eq, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "plugin", plugin, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "print-gc-sections", print_gc_sections, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"List removed unused sections", nullptr, nullptr)
|
||||
OPTION(prefix_2, "print-map", print_map, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Print a link map to the standard output", nullptr, nullptr)
|
||||
OPTION(prefix_2, "Qy", Qy, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "q", alias_emit_relocs, Flag, INVALID, emit_relocs, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "relocatable", relocatable, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Create relocatable object file", nullptr, nullptr)
|
||||
OPTION(prefix_2, "reproduce=", alias_reproduce_eq, Joined, INVALID, reproduce, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "reproduce", reproduce, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Dump linker invocation and input files for debugging", nullptr, nullptr)
|
||||
OPTION(prefix_2, "retain-symbols-file=", retain_symbols_file, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Retain only the symbols listed in the file", "<file>", nullptr)
|
||||
OPTION(prefix_2, "retain-symbols-file", alias_retain_symbols_file, Separate, INVALID, retain_symbols_file, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "rpath-link=", rpath_link_eq, Joined, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "rpath-link", rpath_link, Separate, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "rpath=", alias_rpath_rpath, Joined, INVALID, rpath, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "rpath", rpath, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Add a DT_RUNPATH to the output", nullptr, nullptr)
|
||||
OPTION(prefix_2, "rsp-quoting=", rsp_quoting, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Quoting style for response files. Values supported: windows|posix", nullptr, nullptr)
|
||||
OPTION(prefix_1, "R", alias_rpath_R, JoinedOrSeparate, INVALID, rpath, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "r", alias_relocatable_r, Flag, INVALID, relocatable, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "save-temps", save_temps, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "script=", alias_script, Joined, INVALID, script, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "script", script, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Read linker script", nullptr, nullptr)
|
||||
OPTION(prefix_2, "section-start", section_start, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Set address of section", "<address>", nullptr)
|
||||
OPTION(prefix_2, "shared", shared, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Build a shared object", nullptr, nullptr)
|
||||
OPTION(prefix_2, "soname=", soname, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Set DT_SONAME", nullptr, nullptr)
|
||||
OPTION(prefix_2, "soname", alias_soname_soname, Separate, INVALID, soname, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "sort-common", sort_common, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "sort-section=", alias_sort_section, Joined, INVALID, sort_section, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "sort-section", sort_section, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Specifies sections sorting rule when linkerscript is used", nullptr, nullptr)
|
||||
OPTION(prefix_2, "start-group", start_group, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "start-lib", start_lib, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Start a grouping of objects that should be treated as if they were together in an archive", nullptr, nullptr)
|
||||
OPTION(prefix_2, "static", alias_Bstatic_static, Flag, INVALID, Bstatic, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "stats", stats, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "strip-all", strip_all, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Strip all symbols", nullptr, nullptr)
|
||||
OPTION(prefix_2, "strip-debug", strip_debug, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Strip debugging information", nullptr, nullptr)
|
||||
OPTION(prefix_2, "symbol-ordering-file", symbol_ordering_file, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Layout sections in the order specified by symbol file", nullptr, nullptr)
|
||||
OPTION(prefix_2, "sysroot=", sysroot, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Set the system root", nullptr, nullptr)
|
||||
OPTION(prefix_1, "S", alias_strip_debug_S, Flag, INVALID, strip_debug, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "s", alias_strip_all, Flag, INVALID, strip_all, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "target1-abs", target1_abs, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Interpret R_ARM_TARGET1 as R_ARM_ABS32", nullptr, nullptr)
|
||||
OPTION(prefix_2, "target1-rel", target1_rel, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Interpret R_ARM_TARGET1 as R_ARM_REL32", nullptr, nullptr)
|
||||
OPTION(prefix_2, "target2=", target2, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Interpret R_ARM_TARGET2 as <type>, where <type> is one of rel, abs, or got-rel", "<type>", nullptr)
|
||||
OPTION(prefix_2, "Tbss=", alias_Tbss, Joined, INVALID, Tbss, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "Tbss", Tbss, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Same as --section-start with .bss as the sectionname", nullptr, nullptr)
|
||||
OPTION(prefix_2, "Tdata=", alias_Tdata, Joined, INVALID, Tdata, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "Tdata", Tdata, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Same as --section-start with .data as the sectionname", nullptr, nullptr)
|
||||
OPTION(prefix_2, "thinlto-cache-dir=", thinlto_cache_dir, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Path to ThinLTO cached object file directory", nullptr, nullptr)
|
||||
OPTION(prefix_2, "thinlto-cache-policy", thinlto_cache_policy, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Pruning policy for the ThinLTO cache", nullptr, nullptr)
|
||||
OPTION(prefix_2, "thinlto-jobs=", thinlto_jobs, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Number of ThinLTO jobs", nullptr, nullptr)
|
||||
OPTION(prefix_2, "threads", threads, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Run the linker multi-threaded", nullptr, nullptr)
|
||||
OPTION(prefix_2, "trace-symbol=", trace_trace_symbol_eq, Joined, INVALID, trace_symbol, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "trace-symbol", trace_symbol, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Trace references to symbols", nullptr, nullptr)
|
||||
OPTION(prefix_2, "trace", trace, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Print the names of the input files", nullptr, nullptr)
|
||||
OPTION(prefix_2, "Ttext-segment=", alias_Ttext_segment_eq, Joined, INVALID, Ttext, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "Ttext-segment", alias_Ttext_segment, Separate, INVALID, Ttext, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "Ttext=", alias_Ttext, Joined, INVALID, Ttext, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "Ttext", Ttext, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Same as --section-start with .text as the sectionname", nullptr, nullptr)
|
||||
OPTION(prefix_1, "T", alias_script_T, JoinedOrSeparate, INVALID, script, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "t", alias_trace, Flag, INVALID, trace, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "undefined=", alias_undefined_eq, Joined, INVALID, undefined, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "undefined", undefined, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Force undefined symbol during linking", nullptr, nullptr)
|
||||
OPTION(prefix_2, "unresolved-symbols=", unresolved_symbols, Joined, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Determine how to handle unresolved symbols", nullptr, nullptr)
|
||||
OPTION(prefix_1, "u", alias_undefined_u, JoinedOrSeparate, INVALID, undefined, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "verbose", verbose, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Verbose mode", nullptr, nullptr)
|
||||
OPTION(prefix_2, "version-script=", alias_version_script_eq, Joined, INVALID, version_script, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "version-script", version_script, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Read a version script", nullptr, nullptr)
|
||||
OPTION(prefix_2, "version", version, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Display the version number and exit", nullptr, nullptr)
|
||||
OPTION(prefix_1, "V", alias_version_V, Flag, INVALID, version, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "v", v, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Display the version number", nullptr, nullptr)
|
||||
OPTION(prefix_2, "warn-common", warn_common, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Warn about duplicate common symbols", nullptr, nullptr)
|
||||
OPTION(prefix_2, "warn-execstack", warn_execstack, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "warn-shared-textrel", warn_shared_textrel, Flag, INVALID, INVALID, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "warn-unresolved-symbols", warn_unresolved_symbols, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Report unresolved symbols as warnings", nullptr, nullptr)
|
||||
OPTION(prefix_2, "whole-archive", whole_archive, Flag, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Force load of all members in a static library", nullptr, nullptr)
|
||||
OPTION(prefix_2, "wrap=", alias_wrap_wrap, Joined, INVALID, wrap, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_2, "wrap", wrap, Separate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Use wrapper functions for symbol", "<symbol>", nullptr)
|
||||
OPTION(prefix_1, "X", alias_discard_locals_X, Flag, INVALID, discard_locals, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "x", alias_discard_all_x, Flag, INVALID, discard_all, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "y", alias_trace_symbol_y, JoinedOrSeparate, INVALID, trace_symbol, nullptr, 0, 0, nullptr, nullptr, nullptr)
|
||||
OPTION(prefix_1, "z", z, JoinedOrSeparate, INVALID, INVALID, nullptr, 0, 0,
|
||||
"Linker option extensions", "<option>", nullptr)
|
||||
#endif // OPTION
|
||||
6
deps/lld-prebuilt/lld/Config/Version.inc
vendored
Normal file
6
deps/lld-prebuilt/lld/Config/Version.inc
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
#define LLD_VERSION 5.0.0
|
||||
#define LLD_VERSION_STRING "5.0.0"
|
||||
#define LLD_VERSION_MAJOR 5
|
||||
#define LLD_VERSION_MINOR 0
|
||||
#define LLD_REVISION_STRING ""
|
||||
#define LLD_REPOSITORY_STRING ""
|
||||
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
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