mirror of
https://github.com/rui314/mold.git
synced 2026-11-09 04:49:33 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57f5c77174 |
No files matched your search
@@ -5,7 +5,7 @@ apt-get install -y wget xz-utils
|
||||
|
||||
# Install a 32-bit RISC-V toolchain
|
||||
mkdir /rv32
|
||||
wget -O- --progress=dot:mega https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2025.01.20/riscv32-glibc-ubuntu-22.04-gcc-nightly-2025.01.20-nightly.tar.xz | tar -C /rv32 --strip-components=1 --xz -xf -
|
||||
wget -O- -q https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2025.01.20/riscv32-glibc-ubuntu-22.04-gcc-nightly-2025.01.20-nightly.tar.xz | tar -C /rv32 --strip-components=1 --xz -xf -
|
||||
|
||||
ln -sf /rv32/sysroot /usr/riscv32-linux-gnu
|
||||
echo '/rv32/bin/riscv32-unknown-linux-gnu-gcc -L/usr/riscv32-linux-gnu "$@"' > /usr/bin/riscv32-linux-gnu-gcc
|
||||
@@ -18,7 +18,7 @@ done
|
||||
|
||||
# Install a LoongArch toolchain
|
||||
mkdir /larch
|
||||
wget -O- --progress=dot:mega https://github.com/loongson/build-tools/releases/download/2025.02.21/x86_64-cross-tools-loongarch64-binutils_2.44-gcc_14.2.0-glibc_2.41.tar.xz | tar -C /larch --strip-components=1 --xz -xf -
|
||||
wget -O- -q https://github.com/loongson/build-tools/releases/download/2025.02.21/x86_64-cross-tools-loongarch64-binutils_2.44-gcc_14.2.0-glibc_2.41.tar.xz | tar -C /larch --strip-components=1 --xz -xf -
|
||||
|
||||
cp -r /larch/loongarch64-unknown-linux-gnu/lib/* /larch/target/lib64
|
||||
ln -sf /larch/target /usr/loongarch64-linux-gnu
|
||||
@@ -27,39 +27,25 @@ for i in gcc g++ objdump objcopy strip; do
|
||||
ln -sf /larch/bin/loongarch64-unknown-linux-gnu-$i /usr/bin/loongarch64-linux-gnu-$i
|
||||
done
|
||||
|
||||
wget -O /usr/local/bin/qemu-loongarch64 --progress=dot:mega https://github.com/loongson/build-tools/releases/download/2024.11.01/qemu-loongarch64
|
||||
wget -O /usr/local/bin/qemu-loongarch64 -q https://github.com/loongson/build-tools/releases/download/2024.11.01/qemu-loongarch64
|
||||
chmod 755 /usr/local/bin/qemu-loongarch64
|
||||
|
||||
# Install ARM32 big-endian toolchain
|
||||
mkdir /armeb
|
||||
wget -O- --progress=dot:mega https://toolchains.bootlin.com/downloads/releases/toolchains/armebv7-eabihf/tarballs/armebv7-eabihf--glibc--stable-2024.05-1.tar.xz | tar -C /armeb --strip-components=1 --xz -xf -
|
||||
|
||||
ln -sf /armeb/armeb-buildroot-linux-gnueabihf/sysroot /usr/armeb-linux-gnueabihf
|
||||
echo '/armeb/bin/armeb-linux-gcc -L/usr/armeb-linux-gnu "$@"' > /usr/bin/armeb-linux-gnueabihf-gcc
|
||||
echo '/armeb/bin/armeb-linux-g++ -L/usr/armeb-linux-gnu "$@"' > /usr/bin/armeb-linux-gnueabihf-g++
|
||||
chmod 755 /usr/bin/armeb-linux-gnueabihf-{gcc,g++}
|
||||
|
||||
for i in objdump objcopy strip; do
|
||||
ln -sf /armeb/bin/armeb-linux-$i /usr/bin/armeb-linux-gnueabihf-$i
|
||||
done
|
||||
|
||||
# Install ARM64 big-endian toolchain
|
||||
mkdir /aarch64be
|
||||
wget -O- --progress=dot:mega https://toolchains.bootlin.com/downloads/releases/toolchains/aarch64be/tarballs/aarch64be--glibc--stable-2024.05-1.tar.xz | tar -C /aarch64be --strip-components=1 --xz -xf -
|
||||
wget -O- -q https://sources.buildroot.net/toolchain-external-arm-aarch64-be/arm-gnu-toolchain-13.3.rel1-x86_64-aarch64_be-none-linux-gnu.tar.xz | tar -C /aarch64be --strip-components=1 --xz -xf -
|
||||
|
||||
ln -sf /aarch64be/aarch64_be-buildroot-linux-gnu/sysroot /usr/aarch64_be-linux-gnu
|
||||
echo '/aarch64be/bin/aarch64_be-linux-gcc -L/usr/aarch64_be-linux-gnu "$@"' > /usr/bin/aarch64_be-linux-gnu-gcc
|
||||
echo '/aarch64be/bin/aarch64_be-linux-g++ -L/usr/aarch64_be-linux-gnu "$@"' > /usr/bin/aarch64_be-linux-gnu-g++
|
||||
ln -sf /aarch64be/aarch64_be-none-linux-gnu/libc /usr/aarch64_be-linux-gnu
|
||||
echo '/aarch64be/bin/aarch64_be-none-linux-gnu-gcc -L/usr/aarch64_be-linux-gnu "$@"' > /usr/bin/aarch64_be-linux-gnu-gcc
|
||||
echo '/aarch64be/bin/aarch64_be-none-linux-gnu-g++ -L/usr/aarch64_be-linux-gnu "$@"' > /usr/bin/aarch64_be-linux-gnu-g++
|
||||
chmod 755 /usr/bin/aarch64_be-linux-gnu-{gcc,g++}
|
||||
|
||||
for i in objdump objcopy strip; do
|
||||
ln -sf /aarch64be/bin/aarch64_be-linux-$i /usr/bin/aarch64_be-linux-gnu-$i
|
||||
ln -sf /aarch64be/bin/aarch64_be-none-linux-gnu-$i /usr/bin/aarch64_be-linux-gnu-$i
|
||||
done
|
||||
|
||||
# Install SH4 big-endian toolchain
|
||||
mkdir /sh4aeb
|
||||
wget -O- --progress=dot:mega https://toolchains.bootlin.com/downloads/releases/toolchains/sh-sh4aeb/tarballs/sh-sh4aeb--glibc--stable-2024.05-1.tar.xz | tar -C /sh4aeb --strip-components=1 --xz -xf -
|
||||
|
||||
wget -O- -q https://toolchains.bootlin.com/downloads/releases/toolchains/sh-sh4aeb/tarballs/sh-sh4aeb--glibc--stable-2024.05-1.tar.xz | tar -C /sh4aeb --strip-components=1 --xz -xf -
|
||||
ln -sf /sh4aeb/sh4aeb-buildroot-linux-gnu/sysroot /usr/sh4aeb-linux-gnu
|
||||
echo '/sh4aeb/bin/sh4aeb-linux-gcc -L/usr/sh4aeb-linux-gnu "$@"' > /usr/bin/sh4aeb-linux-gnu-gcc
|
||||
echo '/sh4aeb/bin/sh4aeb-linux-g++ -L/usr/sh4aeb-linux-gnu "$@"' > /usr/bin/sh4aeb-linux-gnu-g++
|
||||
@@ -71,5 +57,5 @@ done
|
||||
|
||||
# Install Intel SDE CPU emulator for CET-related tests
|
||||
mkdir /sde
|
||||
wget -O- --progress=dot:mega https://downloadmirror.intel.com/843185/sde-external-9.48.0-2024-11-25-lin.tar.xz | tar -C /sde --strip-components=1 --xz -xf -
|
||||
wget -O- -q https://downloadmirror.intel.com/843185/sde-external-9.48.0-2024-11-25-lin.tar.xz | tar -C /sde --strip-components=1 --xz -xf -
|
||||
ln -s /sde/sde64 /usr/bin
|
||||
+10
-13
@@ -44,7 +44,7 @@
|
||||
# features and behave exactly the same.
|
||||
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(mold VERSION 2.40.0)
|
||||
project(mold VERSION 2.37.1)
|
||||
|
||||
include(CMakeDependentOption)
|
||||
include(CheckSymbolExists)
|
||||
@@ -62,11 +62,7 @@ endif()
|
||||
# Build mold itself using mold if -DMOLD_USE_MOLD=ON
|
||||
option(MOLD_USE_MOLD "Use mold to build mold" OFF)
|
||||
if(MOLD_USE_MOLD)
|
||||
target_link_options(mold PRIVATE -fuse-ld=mold)
|
||||
|
||||
if(CMAKE_BUILD_TYPE MATCHES "Deb")
|
||||
target_link_options(mold PRIVATE -Wl,--gdb-index)
|
||||
endif()
|
||||
target_link_options(mold PRIVATE -fuse-ld=mold -Wl,--gdb-index)
|
||||
|
||||
if(CMAKE_BUILD_TYPE MATCHES "^Rel")
|
||||
target_link_options(mold PRIVATE -Wl,--gc-sections,--icf=safe)
|
||||
@@ -293,10 +289,10 @@ check_symbol_exists(uname sys/utsname.h HAVE_UNAME)
|
||||
add_custom_target(git_hash
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DSOURCE_DIR=${CMAKE_SOURCE_DIR}
|
||||
-DOUTPUT_FILE=${CMAKE_BINARY_DIR}/mold-git-hash.h
|
||||
-DOUTPUT_FILE=${CMAKE_BINARY_DIR}/git-hash.cc
|
||||
-P ${CMAKE_SOURCE_DIR}/lib/update-git-hash.cmake
|
||||
DEPENDS lib/update-git-hash.cmake
|
||||
BYPRODUCTS mold-git-hash.h
|
||||
BYPRODUCTS git-hash.cc
|
||||
VERBATIM)
|
||||
|
||||
add_dependencies(mold git_hash)
|
||||
@@ -314,12 +310,10 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
# compiler instances. This is hacky but greatly reduces compile time
|
||||
# on a multicore machine.
|
||||
list(APPEND MOLD_ELF_TARGETS
|
||||
X86_64 I386 ARM64LE ARM64BE ARM32LE ARM32BE RV32LE RV32BE RV64LE
|
||||
RV64BE PPC32 PPC64V1 PPC64V2 S390X SPARC64 M68K SH4LE SH4BE
|
||||
LOONGARCH32 LOONGARCH64)
|
||||
X86_64 I386 ARM64LE ARM64BE ARM32 RV32LE RV32BE RV64LE RV64BE PPC32
|
||||
PPC64V1 PPC64V2 S390X SPARC64 M68K SH4LE SH4BE LOONGARCH32 LOONGARCH64 ARC)
|
||||
|
||||
list(APPEND MOLD_ELF_TEMPLATE_FILES
|
||||
src/arch-arm32.cc
|
||||
src/arch-arm64.cc
|
||||
src/arch-loongarch.cc
|
||||
src/arch-riscv.cc
|
||||
@@ -379,6 +373,7 @@ endforeach()
|
||||
|
||||
# Add other non-template source files.
|
||||
target_sources(mold PRIVATE
|
||||
git-hash.cc
|
||||
lib/compress.cc
|
||||
lib/crc32.cc
|
||||
lib/demangle.cc
|
||||
@@ -390,6 +385,8 @@ target_sources(mold PRIVATE
|
||||
lib/perf.cc
|
||||
lib/random.cc
|
||||
lib/tar.cc
|
||||
src/arch-arc.cc
|
||||
src/arch-arm32.cc
|
||||
src/arch-i386.cc
|
||||
src/arch-m68k.cc
|
||||
src/arch-ppc32.cc
|
||||
@@ -398,8 +395,8 @@ target_sources(mold PRIVATE
|
||||
src/arch-s390x.cc
|
||||
src/arch-sparc64.cc
|
||||
src/arch-x86-64.cc
|
||||
src/config.cc
|
||||
src/elf.cc
|
||||
src/entry.cc
|
||||
third-party/rust-demangle/rust-demangle.c
|
||||
)
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ accept an absolute path as an argument for `-fuse-ld`.
|
||||
Create `.cargo/config.toml` in your project directory with the following:
|
||||
|
||||
```toml
|
||||
[target.'cfg(target_os = "linux")']
|
||||
[target.x86_64-unknown-linux-gnu]
|
||||
linker = "clang"
|
||||
rustflags = ["-C", "link-arg=-fuse-ld=/path/to/mold"]
|
||||
```
|
||||
@@ -131,7 +131,7 @@ example above, we use `clang` as a linker driver since it always accepts the
|
||||
may be able to remove the `linker = "clang"` line.
|
||||
|
||||
```toml
|
||||
[target.'cfg(target_os = "linux")']
|
||||
[target.x86_64-unknown-linux-gnu]
|
||||
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash -x
|
||||
set -e
|
||||
apt-get install -y git bzip2 ninja-build pkg-config libglib2.0-dev python3-distutils-extra libpixman-1-dev
|
||||
|
||||
cd /
|
||||
git clone --depth=1 https://github.com/foss-for-synopsys-dwc-arc-processors/qemu.git
|
||||
mkdir /qemu/build
|
||||
cd /qemu/build
|
||||
../configure --static --extra-cflags=-Wno-error
|
||||
ninja
|
||||
ln -s /qemu/build/qemu-arc /qemu/build/qemu-arc64 /usr/local/bin
|
||||
|
||||
|
||||
apt-get install -y libgmp-dev libmpfr-dev texinfo bison yacc flex
|
||||
cd /
|
||||
git clone --depth=1 https://github.com/foss-for-synopsys-dwc-arc-processors/binutils-gdb.git
|
||||
cd /binutils-gdb
|
||||
mkdir build
|
||||
cd build
|
||||
../configure --target=arc-linux
|
||||
make -j$(nproc)
|
||||
|
||||
|
||||
apt-get install -y mold
|
||||
mkdir /build
|
||||
cd /build
|
||||
cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DMOLD_USE_MOLD=1 /mold
|
||||
ninja
|
||||
TRIPLE=arc-linux-gnu bash -x /mold/test/hello-dynamic.sh
|
||||
@@ -126,10 +126,10 @@ RUN mkdir /build && \
|
||||
make install && \
|
||||
rm -rf /build
|
||||
|
||||
# Build LLVM 20
|
||||
# Build LLVM 19
|
||||
RUN mkdir /build && \
|
||||
cd /build && \
|
||||
wget -O- --no-check-certificate --progress=dot:mega https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-20.1.3.tar.gz | tar xzf - --strip-components=1 && \
|
||||
wget -O- --no-check-certificate --progress=dot:mega https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-19.1.7.tar.gz | tar xzf - --strip-components=1 && \
|
||||
mkdir b && \
|
||||
cd b && \
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS=clang ../llvm && \
|
||||
@@ -195,22 +195,14 @@ dest=mold-$version-$arch-linux
|
||||
|
||||
# We use the timestamp of the last Git commit as the file timestamp
|
||||
# for build artifacts.
|
||||
timestamp=$(git log -1 --format=%ct)
|
||||
timestamp="$(git log -1 --format=%ci)"
|
||||
|
||||
# Build mold in a container.
|
||||
mkdir -p dist
|
||||
|
||||
# If the host is ARM64, `uname -m` in an ARM32 container running on it
|
||||
# reports it not as ARM32 but as ARM64, which confuses BLAKE3's cmake script
|
||||
# and erroneously enables NEON SIMD instructions. `setarch` can be used
|
||||
# to change the output of `uname -m`.
|
||||
[ $arch = arm ] && setarch='setarch linux32'
|
||||
|
||||
podman run --arch $arch -it --rm --userns=host --pids-limit=-1 --network=none \
|
||||
--pull=never -v "$(pwd):/mold:ro" -v "$(pwd)/dist:/dist" $image \
|
||||
$setarch bash -c "
|
||||
-v "$(pwd):/mold:ro" -v "$(pwd)/dist:/dist" $image bash -c "
|
||||
set -e
|
||||
export SOURCE_DATE_EPOCH=$timestamp
|
||||
mkdir /build
|
||||
cd /build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DMOLD_MOSTLY_STATIC=1 -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ /mold
|
||||
@@ -220,7 +212,7 @@ cmake -DMOLD_USE_MOLD=1 .
|
||||
cmake --build . -j\$(nproc)
|
||||
ctest --output-on-failure -j\$(nproc)
|
||||
cmake --install . --prefix $dest --strip
|
||||
find $dest -print | xargs touch --no-dereference --date=@$timestamp
|
||||
find $dest -print | xargs touch --no-dereference --date='$timestamp'
|
||||
find $dest -print | sort | tar -cf - --no-recursion --files-from=- | gzip -9nc > /dist/$dest.tar.gz
|
||||
cp mold /dist
|
||||
sha256sum /dist/$dest.tar.gz
|
||||
|
||||
@@ -11,7 +11,7 @@ set -x
|
||||
|
||||
case "$ID" in
|
||||
ubuntu | pop | linuxmint | debian | raspbian)
|
||||
apt-get install -y qemu-user {gcc,g++}-{i686,aarch64,riscv64,powerpc,powerpc64,powerpc64le,s390x,sparc64,m68k,sh4}-linux-gnu {gcc,g++}-arm-linux-gnueabihf
|
||||
apt-get install -y qemu-user {gcc,g++}-{i686,aarch64,riscv64,powerpc,powerpc64,powerpc64le,s390x,sparc64,m68k,sh4,arc}-linux-gnu {gcc,g++}-arm-linux-gnueabihf
|
||||
;;
|
||||
*)
|
||||
echo "Error: don't know anything about build dependencies on $ID-$VERSION_ID"
|
||||
|
||||
+3
-1
@@ -69,6 +69,8 @@ inline char *output_tmpfile;
|
||||
inline u8 *output_buffer_start = nullptr;
|
||||
inline u8 *output_buffer_end = nullptr;
|
||||
|
||||
extern std::string mold_git_hash;
|
||||
|
||||
std::string errno_string();
|
||||
void cleanup();
|
||||
void install_signal_handler();
|
||||
@@ -665,7 +667,7 @@ public:
|
||||
|
||||
// Since the shard is circular, we need to handle the last entries
|
||||
// as if they were next to the first entries.
|
||||
while (begin < end && entries[end - 1].key)
|
||||
while (entries[end - 1].key)
|
||||
vec.push_back(entries + --end);
|
||||
|
||||
// Find entries contiguous in the buckets and sort them.
|
||||
|
||||
+2
-2
@@ -54,14 +54,14 @@ dir=gentoo/$git_hash
|
||||
|
||||
mkdir -p "$dir"/success "$dir"/failure
|
||||
|
||||
$podman chrt --idle 0 nice -n 19 bash -c "$cmd1 && $cmd2 && $cmd3" >& "$dir"/"$filename".mold
|
||||
$podman nice -n 19 bash -c "$cmd1 && $cmd2 && $cmd3" >& "$dir"/"$filename".mold
|
||||
if [ $? = 0 ]; then
|
||||
mv "$dir"/"$filename".mold "$dir"/success
|
||||
else
|
||||
mv "$dir"/"$filename".mold "$dir"/failure
|
||||
fi
|
||||
|
||||
$podman chrt --idle 0 nice -n 19 bash -c "$cmd2 && $cmd3" >& "$dir"/"$filename".ld
|
||||
$podman nice -n 19 bash -c "$cmd2 && $cmd3" >& "$dir"/"$filename".ld
|
||||
if [ $? = 0 ]; then
|
||||
mv "$dir"/"$filename".ld "$dir"/success
|
||||
else
|
||||
|
||||
@@ -17,11 +17,11 @@ if(EXISTS "${SOURCE_DIR}/.git/HEAD")
|
||||
endif()
|
||||
|
||||
# Create new file contents and update a given file if necessary.
|
||||
if("${HASH}" STREQUAL "")
|
||||
set(NEW_CONTENTS "")
|
||||
else()
|
||||
set(NEW_CONTENTS "#define MOLD_GIT_HASH \"${HASH}\"\n")
|
||||
endif()
|
||||
set(NEW_CONTENTS "#include <string>
|
||||
namespace mold {
|
||||
std::string mold_git_hash = \"${HASH}\";
|
||||
}
|
||||
")
|
||||
|
||||
if(EXISTS "${OUTPUT_FILE}")
|
||||
file(READ "${OUTPUT_FILE}" OLD_CONTENTS)
|
||||
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
// r31 is blink. r30 is I believe a scratch register.
|
||||
//
|
||||
// Programs might use one of several branch, jump, and link instructions
|
||||
// to control execution flow through direct and indirect function calls
|
||||
// and branching. For function calling, the conditional branch-and-link
|
||||
// instruction has a maximum branch range of ±1 MiB, and the target
|
||||
// address is 32-bit-aligned. The unconditional branch-and-link format has
|
||||
// a maximum branch range of ±16 MiB.
|
||||
//
|
||||
// https://github.com/foss-for-synopsys-dwc-arc-processors/arc-ABI-manual/blob/master/ARCv2_ABI.pdf
|
||||
|
||||
#include "mold.h"
|
||||
|
||||
namespace mold {
|
||||
|
||||
using E = ARC;
|
||||
|
||||
// An integer-like class for the mixed-endian encoding
|
||||
class M32 {
|
||||
public:
|
||||
operator u32() const {
|
||||
return (buf[1] << 24) | (buf[0] << 16) | (buf[3] << 8) | buf[2];
|
||||
}
|
||||
|
||||
M32 &operator=(u32 x) {
|
||||
buf[0] = x >> 16;
|
||||
buf[1] = x >> 24;
|
||||
buf[2] = x;
|
||||
buf[3] = x >> 8;
|
||||
return *this;
|
||||
}
|
||||
|
||||
M32 &operator&=(u32 x) { return *this = *this & x; }
|
||||
M32 &operator|=(u32 x) { return *this = *this | x; }
|
||||
u8 buf[4];
|
||||
};
|
||||
|
||||
static void write_disp7u(u8 *loc, u32 val) {
|
||||
*(ul16 *)loc |= (bits(val, 6, 3) << 4) | bits(val, 2, 0);
|
||||
}
|
||||
|
||||
static void write_disp9(u8 *loc, u32 val) {
|
||||
*(M32 *)loc |= bits(val, 8, 0);
|
||||
}
|
||||
|
||||
static void write_disp9ls(u8 *loc, u32 val) {
|
||||
*(M32 *)loc |= (bits(val, 7, 0) << 16) | (bit(val, 8) << 15);
|
||||
}
|
||||
|
||||
static void write_disp9s(u8 *loc, u32 val) {
|
||||
*(ul16 *)loc |= bits(val, 8, 0);
|
||||
}
|
||||
|
||||
static void write_disp10u(u8 *loc, u32 val) {
|
||||
*(ul16 *)loc |= bits(val, 9, 0);
|
||||
}
|
||||
|
||||
static void write_disp13s(u8 *loc, u32 val) {
|
||||
*(ul16 *)loc |= bits(val, 12, 0);
|
||||
}
|
||||
|
||||
static void write_disp21h(u8 *loc, u32 val) {
|
||||
*(M32 *)loc |= (bits(val, 10, 1) << 17) | (bits(val, 20, 11) << 6);
|
||||
}
|
||||
|
||||
static void write_disp21w(u8 *loc, u32 val) {
|
||||
*(M32 *)loc |= (bits(val, 10, 2) << 18) | (bits(val, 20, 11) << 6);
|
||||
}
|
||||
|
||||
static void write_disp25h(u8 *loc, u32 val) {
|
||||
*(M32 *)loc |= (bits(val, 10, 1) << 17) | (bits(val, 20, 11) << 6) |
|
||||
bits(val, 24, 21);
|
||||
}
|
||||
|
||||
static void write_disp25w(u8 *loc, u32 val) {
|
||||
*(M32 *)loc |= (bits(val, 10, 2) << 18) | (bits(val, 20, 11) << 6) |
|
||||
bits(val, 24, 21);
|
||||
}
|
||||
|
||||
static void write_disps9(u8 *loc, u32 val) {
|
||||
*(ul16 *)loc |= bits(val, 10, 2);
|
||||
}
|
||||
|
||||
static void write_disps12(u8 *loc, u32 val) {
|
||||
*(M32 *)loc |= (bits(val, 5, 0) << 6) | bits(val, 11, 6);
|
||||
}
|
||||
|
||||
template <>
|
||||
void write_plt_header(Context<E> &ctx, u8 *buf) {
|
||||
static const ul16 insn[] = {
|
||||
0x2730, 0x7f8b, 0, 0, // ld r11, [pcl,0]
|
||||
0x2730, 0x7f8a, 0, 0, // ld r10, [pcl,0]
|
||||
0x2020, 0x0280, // j [r10]
|
||||
0, 0, // (address of GOTPLT)
|
||||
};
|
||||
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(M32 *)(buf + 4) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr + 4;
|
||||
*(M32 *)(buf + 12) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr;
|
||||
*(M32 *)(buf + 20) = ctx.gotplt->shdr.sh_addr;
|
||||
}
|
||||
|
||||
template <>
|
||||
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
static const ul16 insn[] = {
|
||||
0x2730, 0x7f8c, 0, 0, // ld r12, [pcl,0]
|
||||
0x2021, 0x0300, // j.d [r12]
|
||||
0x240a, 0x1fc0, // mov r12,pcl
|
||||
};
|
||||
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(M32 *)(buf + 4) = sym.get_gotplt_addr(ctx) - sym.get_plt_addr(ctx);
|
||||
}
|
||||
|
||||
template <>
|
||||
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
static const ul16 insn[] = {
|
||||
0x2730, 0x7f8c, 0, 0, // ld r12, [pcl,0]
|
||||
0x2021, 0x0300, // j.d [r12]
|
||||
0x240a, 0x1fc0, // mov r12,pcl
|
||||
};
|
||||
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(M32 *)(buf + 4) = sym.get_got_pltgot_addr(ctx) - sym.get_plt_addr(ctx);
|
||||
}
|
||||
|
||||
template <>
|
||||
void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
|
||||
u64 offset, u64 val) {
|
||||
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_NONE:
|
||||
break;
|
||||
case R_ARC_32:
|
||||
*(ul32 *)loc = val;
|
||||
break;
|
||||
case R_ARC_32_PCREL:
|
||||
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
|
||||
break;
|
||||
default:
|
||||
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
if (rel.r_type == R_NONE)
|
||||
continue;
|
||||
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
u8 *loc = base + rel.r_offset;
|
||||
|
||||
u64 S = sym.get_addr(ctx);
|
||||
u64 A = rel.r_addend;
|
||||
u64 P = get_addr() + rel.r_offset;
|
||||
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
|
||||
u64 GOT = ctx.got->shdr.sh_addr;
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_ARC_32:
|
||||
*(ul32 *)loc = S + A;
|
||||
break;
|
||||
case R_ARC_32_ME:
|
||||
*(M32 *)loc = S + A;
|
||||
break;
|
||||
case R_ARC_S25H_PCREL:
|
||||
case R_ARC_S25H_PCREL_PLT:
|
||||
write_disp25h(loc, align_to(S + A - P, 2));
|
||||
break;
|
||||
case R_ARC_S25W_PCREL:
|
||||
case R_ARC_S25W_PCREL_PLT:
|
||||
write_disp25w(loc, align_to(S + A - P, 4));
|
||||
break;
|
||||
case R_ARC_PC32:
|
||||
*(M32 *)loc = S + A - P;
|
||||
break;
|
||||
case R_ARC_32_PCREL:
|
||||
*(ul32 *)loc = S + A - P;
|
||||
break;
|
||||
case R_ARC_GOTPC32:
|
||||
*(M32 *)loc = GOT + G + A - P;
|
||||
break;
|
||||
case R_ARC_TLS_LE_32:
|
||||
*(M32 *)loc = S + A - ctx.tp_addr;
|
||||
break;
|
||||
case R_ARC_TLS_IE_GOT:
|
||||
*(M32 *)loc = sym.get_gottp_addr(ctx) + A - P;
|
||||
break;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
|
||||
continue;
|
||||
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
u8 *loc = base + rel.r_offset;
|
||||
|
||||
SectionFragment<E> *frag;
|
||||
i64 frag_addend;
|
||||
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
|
||||
|
||||
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
|
||||
u64 A = frag ? frag_addend : (i64)rel.r_addend;
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_ARC_32:
|
||||
if (std::optional<u64> val = get_tombstone(sym, frag))
|
||||
*(ul32 *)loc = *val;
|
||||
else
|
||||
*(ul32 *)loc = S + A;
|
||||
break;
|
||||
default:
|
||||
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
|
||||
<< rel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
assert(shdr().sh_flags & SHF_ALLOC);
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
|
||||
continue;
|
||||
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
|
||||
if (sym.is_ifunc())
|
||||
Error(ctx) << sym << ": GNU ifunc symbol is not supported on m68k";
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_ARC_32:
|
||||
break;
|
||||
case R_ARC_32_ME:
|
||||
scan_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_ARC_S25H_PCREL:
|
||||
case R_ARC_S25W_PCREL:
|
||||
case R_ARC_32_PCREL:
|
||||
case R_ARC_PC32:
|
||||
scan_pcrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_ARC_S25H_PCREL_PLT:
|
||||
case R_ARC_S25W_PCREL_PLT:
|
||||
if (sym.is_imported)
|
||||
sym.flags |= NEEDS_PLT;
|
||||
break;
|
||||
case R_ARC_GOTPC32:
|
||||
sym.flags |= NEEDS_GOT;
|
||||
break;
|
||||
case R_ARC_TLS_LE_32:
|
||||
check_tlsle(ctx, sym, rel);
|
||||
break;
|
||||
case R_ARC_TLS_IE_GOT:
|
||||
sym.flags |= NEEDS_GOTTP;
|
||||
break;
|
||||
default:
|
||||
Error(ctx) << *this << ": unknown relocation: " << rel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
u64 get_eflags(Context<E> &ctx) {
|
||||
return 0x406;
|
||||
}
|
||||
|
||||
} // namespace mold
|
||||
+103
-173
@@ -31,21 +31,18 @@
|
||||
//
|
||||
// https://github.com/ARM-software/abi-aa/blob/main/aaelf32/aaelf32.rst
|
||||
|
||||
#if MOLD_ARM32LE || MOLD_ARM32BE
|
||||
|
||||
#include "mold.h"
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/parallel_for_each.h>
|
||||
|
||||
namespace mold {
|
||||
|
||||
using E = MOLD_TARGET;
|
||||
using E = ARM32;
|
||||
|
||||
template <>
|
||||
i64 get_addend(u8 *loc, const ElfRel<E> &rel) {
|
||||
U32<E> *arm = (U32<E> *)loc;
|
||||
U16<E> *thm = (U16<E> *)loc;
|
||||
ul32 *arm = (ul32 *)loc;
|
||||
ul16 *thm = (ul16 *)loc;
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_ARM_ABS32:
|
||||
@@ -62,7 +59,7 @@ i64 get_addend(u8 *loc, const ElfRel<E> &rel) {
|
||||
case R_ARM_TLS_GOTDESC:
|
||||
case R_ARM_TARGET1:
|
||||
case R_ARM_TARGET2:
|
||||
return (I32<E>)*arm;
|
||||
return (il32)*arm;
|
||||
case R_ARM_THM_JUMP8:
|
||||
return sign_extend(thm[0], 8) << 1;
|
||||
case R_ARM_THM_JUMP11:
|
||||
@@ -128,7 +125,7 @@ i64 get_addend(u8 *loc, const ElfRel<E> &rel) {
|
||||
static void write_arm_mov(u8 *loc, u32 val) {
|
||||
u32 imm12 = bits(val, 11, 0);
|
||||
u32 imm4 = bits(val, 15, 12);
|
||||
*(U32<E> *)loc = (*(U32<E> *)loc & 0xfff0'f000) | (imm4 << 16) | imm12;
|
||||
*(ul32 *)loc = (*(ul32 *)loc & 0xfff0'f000) | (imm4 << 16) | imm12;
|
||||
}
|
||||
|
||||
static void write_thm_b21(u8 *loc, u32 val) {
|
||||
@@ -138,7 +135,7 @@ static void write_thm_b21(u8 *loc, u32 val) {
|
||||
u32 imm6 = bits(val, 17, 12);
|
||||
u32 imm11 = bits(val, 11, 1);
|
||||
|
||||
U16<E> *buf = (U16<E> *)loc;
|
||||
ul16 *buf = (ul16 *)loc;
|
||||
buf[0] = (buf[0] & 0b1111'1011'1100'0000) | (S << 10) | imm6;
|
||||
buf[1] = (buf[1] & 0b1101'0000'0000'0000) | (J1 << 13) | (J2 << 11) | imm11;
|
||||
}
|
||||
@@ -152,7 +149,7 @@ static void write_thm_b25(u8 *loc, u32 val) {
|
||||
u32 imm10 = bits(val, 21, 12);
|
||||
u32 imm11 = bits(val, 11, 1);
|
||||
|
||||
U16<E> *buf = (U16<E> *)loc;
|
||||
ul16 *buf = (ul16 *)loc;
|
||||
buf[0] = (buf[0] & 0b1111'1000'0000'0000) | (S << 10) | imm10;
|
||||
buf[1] = (buf[1] & 0b1101'0000'0000'0000) | (J1 << 13) | (J2 << 11) | imm11;
|
||||
}
|
||||
@@ -163,7 +160,7 @@ static void write_thm_mov(u8 *loc, u32 val) {
|
||||
u32 imm3 = bits(val, 10, 8);
|
||||
u32 imm8 = bits(val, 7, 0);
|
||||
|
||||
U16<E> *buf = (U16<E> *)loc;
|
||||
ul16 *buf = (ul16 *)loc;
|
||||
buf[0] = (buf[0] & 0b1111'1011'1111'0000) | (i << 10) | imm4;
|
||||
buf[1] = (buf[1] & 0b1000'1111'0000'0000) | (imm3 << 12) | imm8;
|
||||
}
|
||||
@@ -187,13 +184,13 @@ void write_addend(u8 *loc, i64 val, const ElfRel<E> &rel) {
|
||||
case R_ARM_TLS_GOTDESC:
|
||||
case R_ARM_TARGET1:
|
||||
case R_ARM_TARGET2:
|
||||
*(U32<E> *)loc = val;
|
||||
*(ul32 *)loc = val;
|
||||
break;
|
||||
case R_ARM_THM_JUMP8:
|
||||
*(U16<E> *)loc = (*(U16<E> *)loc & 0xff00) | bits(val, 8, 1);
|
||||
*(ul16 *)loc = (*(ul16 *)loc & 0xff00) | bits(val, 8, 1);
|
||||
break;
|
||||
case R_ARM_THM_JUMP11:
|
||||
*(U16<E> *)loc = (*(U16<E> *)loc & 0xf800) | bits(val, 11, 1);
|
||||
*(ul16 *)loc = (*(ul16 *)loc & 0xf800) | bits(val, 11, 1);
|
||||
break;
|
||||
case R_ARM_THM_CALL:
|
||||
case R_ARM_THM_JUMP24:
|
||||
@@ -203,7 +200,7 @@ void write_addend(u8 *loc, i64 val, const ElfRel<E> &rel) {
|
||||
case R_ARM_CALL:
|
||||
case R_ARM_JUMP24:
|
||||
case R_ARM_PLT32:
|
||||
*(U32<E> *)loc = (*(U32<E> *)loc & 0xff00'0000) | bits(val, 25, 2);
|
||||
*(ul32 *)loc = (*(ul32 *)loc & 0xff00'0000) | bits(val, 25, 2);
|
||||
break;
|
||||
case R_ARM_MOVW_PREL_NC:
|
||||
case R_ARM_MOVW_ABS_NC:
|
||||
@@ -212,7 +209,7 @@ void write_addend(u8 *loc, i64 val, const ElfRel<E> &rel) {
|
||||
write_arm_mov(loc, val);
|
||||
break;
|
||||
case R_ARM_PREL31:
|
||||
*(U32<E> *)loc = (*(U32<E> *)loc & 0x8000'0000) | (val & 0x7fff'ffff);
|
||||
*(ul32 *)loc = (*(ul32 *)loc & 0x8000'0000) | (val & 0x7fff'ffff);
|
||||
break;
|
||||
case R_ARM_THM_MOVW_PREL_NC:
|
||||
case R_ARM_THM_MOVW_ABS_NC:
|
||||
@@ -239,7 +236,7 @@ void write_plt_header(Context<E> &ctx, u8 *buf) {
|
||||
};
|
||||
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(U32<E> *)(buf + 16) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 16;
|
||||
*(ul32 *)(buf + 16) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 16;
|
||||
}
|
||||
|
||||
static constexpr ul32 plt_entry[] = {
|
||||
@@ -252,13 +249,13 @@ static constexpr ul32 plt_entry[] = {
|
||||
template <>
|
||||
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
memcpy(buf, plt_entry, sizeof(plt_entry));
|
||||
*(U32<E> *)(buf + 12) = sym.get_gotplt_addr(ctx) - sym.get_plt_addr(ctx) - 12;
|
||||
*(ul32 *)(buf + 12) = sym.get_gotplt_addr(ctx) - sym.get_plt_addr(ctx) - 12;
|
||||
}
|
||||
|
||||
template <>
|
||||
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
memcpy(buf, plt_entry, sizeof(plt_entry));
|
||||
*(U32<E> *)(buf + 12) = sym.get_got_pltgot_addr(ctx) - sym.get_plt_addr(ctx) - 12;
|
||||
*(ul32 *)(buf + 12) = sym.get_got_pltgot_addr(ctx) - sym.get_plt_addr(ctx) - 12;
|
||||
}
|
||||
|
||||
template <>
|
||||
@@ -270,16 +267,24 @@ void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
|
||||
case R_NONE:
|
||||
break;
|
||||
case R_ARM_ABS32:
|
||||
*(U32<E> *)loc = val;
|
||||
*(ul32 *)loc = val;
|
||||
break;
|
||||
case R_ARM_REL32:
|
||||
*(U32<E> *)loc = val - this->shdr.sh_addr - offset;
|
||||
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
|
||||
break;
|
||||
default:
|
||||
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
|
||||
}
|
||||
}
|
||||
|
||||
static Thunk<E> &get_reachable_thunk(OutputSection<E> &osec, u64 addr) {
|
||||
auto it = std::upper_bound(osec.thunks.begin(), osec.thunks.end(), addr,
|
||||
[](u64 addr, std::unique_ptr<Thunk<E>> &thunk) {
|
||||
return addr < thunk->get_addr();
|
||||
});
|
||||
return **it;
|
||||
}
|
||||
|
||||
template <>
|
||||
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
@@ -307,11 +312,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
auto get_arm_thunk_addr = [&] { return sym.get_thunk_addr(ctx, P) + 4; };
|
||||
|
||||
auto get_tlsdesc_trampoline_addr = [&] {
|
||||
auto it = ranges::upper_bound(output_section->thunks, P, {},
|
||||
[](std::unique_ptr<Thunk<E>> &thunk) {
|
||||
return thunk->get_addr();
|
||||
});
|
||||
return (*it)->get_addr();
|
||||
return get_reachable_thunk(*output_section, P).get_addr();
|
||||
};
|
||||
|
||||
switch (rel.r_type) {
|
||||
@@ -319,13 +320,13 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
case R_ARM_TARGET1:
|
||||
break;
|
||||
case R_ARM_REL32:
|
||||
*(U32<E> *)loc = S + A - P;
|
||||
*(ul32 *)loc = S + A - P;
|
||||
break;
|
||||
case R_ARM_THM_CALL: {
|
||||
if (sym.is_remaining_undef_weak()) {
|
||||
// On ARM, calling an weak undefined symbol jumps to the
|
||||
// next instruction.
|
||||
*(U32<E> *)loc = 0x8000'f3af; // NOP.W
|
||||
*(ul32 *)loc = 0x8000'f3af; // NOP.W
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -336,61 +337,61 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
i64 val2 = align_to(S + A - P, 4);
|
||||
|
||||
if (T && is_int(val1, 25)) {
|
||||
*(U16<E> *)(loc + 2) |= 0x1000; // BL
|
||||
*(ul16 *)(loc + 2) |= 0x1000; // BL
|
||||
write_thm_b25(loc, val1);
|
||||
} else if (!T && is_int(val2, 25)) {
|
||||
*(U16<E> *)(loc + 2) &= ~0x1000; // BLX
|
||||
*(ul16 *)(loc + 2) &= ~0x1000; // BLX
|
||||
write_thm_b25(loc, val2);
|
||||
} else {
|
||||
*(U16<E> *)(loc + 2) |= 0x1000; // BL
|
||||
*(ul16 *)(loc + 2) |= 0x1000; // BL
|
||||
write_thm_b25(loc, get_thumb_thunk_addr() + A - P);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case R_ARM_BASE_PREL:
|
||||
*(U32<E> *)loc = GOT + A - P;
|
||||
*(ul32 *)loc = GOT + A - P;
|
||||
break;
|
||||
case R_ARM_GOTOFF32:
|
||||
*(U32<E> *)loc = ((S + A) | T) - GOT;
|
||||
*(ul32 *)loc = ((S + A) | T) - GOT;
|
||||
break;
|
||||
case R_ARM_GOT_PREL:
|
||||
case R_ARM_TARGET2:
|
||||
*(U32<E> *)loc = GOT + G + A - P;
|
||||
*(ul32 *)loc = GOT + G + A - P;
|
||||
break;
|
||||
case R_ARM_GOT_BREL:
|
||||
*(U32<E> *)loc = G + A;
|
||||
*(ul32 *)loc = G + A;
|
||||
break;
|
||||
case R_ARM_CALL: {
|
||||
if (sym.is_remaining_undef_weak()) {
|
||||
*(U32<E> *)loc = 0xe320'f000; // NOP
|
||||
*(ul32 *)loc = 0xe320'f000; // NOP
|
||||
break;
|
||||
}
|
||||
|
||||
// Just like THM_CALL, ARM_CALL relocation refers to either BL or
|
||||
// BLX instruction. We may need to rewrite BL → BLX or BLX → BL.
|
||||
bool is_bl = ((*(U32<E> *)loc & 0xff00'0000) == 0xeb00'0000);
|
||||
bool is_blx = ((*(U32<E> *)loc & 0xfe00'0000) == 0xfa00'0000);
|
||||
bool is_bl = ((*(ul32 *)loc & 0xff00'0000) == 0xeb00'0000);
|
||||
bool is_blx = ((*(ul32 *)loc & 0xfe00'0000) == 0xfa00'0000);
|
||||
if (!is_bl && !is_blx)
|
||||
Fatal(ctx) << *this << ": R_ARM_CALL refers to neither BL nor BLX";
|
||||
|
||||
i64 val = S + A - P;
|
||||
if (is_int(val, 26)) {
|
||||
if (T) {
|
||||
*(U32<E> *)loc = 0xfa00'0000; // BLX
|
||||
*(U32<E> *)loc |= (bit(val, 1) << 24) | bits(val, 25, 2);
|
||||
*(ul32 *)loc = 0xfa00'0000; // BLX
|
||||
*(ul32 *)loc |= (bit(val, 1) << 24) | bits(val, 25, 2);
|
||||
} else {
|
||||
*(U32<E> *)loc = 0xeb00'0000; // BL
|
||||
*(U32<E> *)loc |= bits(val, 25, 2);
|
||||
*(ul32 *)loc = 0xeb00'0000; // BL
|
||||
*(ul32 *)loc |= bits(val, 25, 2);
|
||||
}
|
||||
} else {
|
||||
*(U32<E> *)loc = 0xeb00'0000; // BL
|
||||
*(U32<E> *)loc |= bits(get_arm_thunk_addr() + A - P, 25, 2);
|
||||
*(ul32 *)loc = 0xeb00'0000; // BL
|
||||
*(ul32 *)loc |= bits(get_arm_thunk_addr() + A - P, 25, 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case R_ARM_JUMP24: {
|
||||
if (sym.is_remaining_undef_weak()) {
|
||||
*(U32<E> *)loc = 0xe320'f000; // NOP
|
||||
*(ul32 *)loc = 0xe320'f000; // NOP
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -403,26 +404,26 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
i64 val = S + A - P;
|
||||
if (T || !is_int(val, 26))
|
||||
val = get_arm_thunk_addr() + A - P;
|
||||
*(U32<E> *)loc = (*(U32<E> *)loc & 0xff00'0000) | bits(val, 25, 2);
|
||||
*(ul32 *)loc = (*(ul32 *)loc & 0xff00'0000) | bits(val, 25, 2);
|
||||
break;
|
||||
}
|
||||
case R_ARM_PLT32:
|
||||
if (sym.is_remaining_undef_weak()) {
|
||||
*(U32<E> *)loc = 0xe320'f000; // NOP
|
||||
*(ul32 *)loc = 0xe320'f000; // NOP
|
||||
} else {
|
||||
u64 val = (T ? get_arm_thunk_addr() : S) + A - P;
|
||||
*(U32<E> *)loc = (*(U32<E> *)loc & 0xff00'0000) | bits(val, 25, 2);
|
||||
*(ul32 *)loc = (*(ul32 *)loc & 0xff00'0000) | bits(val, 25, 2);
|
||||
}
|
||||
break;
|
||||
case R_ARM_THM_JUMP8:
|
||||
check(S + A - P, -(1 << 8), 1 << 8);
|
||||
*(U16<E> *)loc &= 0xff00;
|
||||
*(U16<E> *)loc |= bits(S + A - P, 8, 1);
|
||||
*(ul16 *)loc &= 0xff00;
|
||||
*(ul16 *)loc |= bits(S + A - P, 8, 1);
|
||||
break;
|
||||
case R_ARM_THM_JUMP11:
|
||||
check(S + A - P, -(1 << 11), 1 << 11);
|
||||
*(U16<E> *)loc &= 0xf800;
|
||||
*(U16<E> *)loc |= bits(S + A - P, 11, 1);
|
||||
*(ul16 *)loc &= 0xf800;
|
||||
*(ul16 *)loc |= bits(S + A - P, 11, 1);
|
||||
break;
|
||||
case R_ARM_THM_JUMP19:
|
||||
check(S + A - P, -(1 << 20), 1 << 20);
|
||||
@@ -430,7 +431,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
break;
|
||||
case R_ARM_THM_JUMP24: {
|
||||
if (sym.is_remaining_undef_weak()) {
|
||||
*(U32<E> *)loc = 0x8000'f3af; // NOP
|
||||
*(ul32 *)loc = 0x8000'f3af; // NOP
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -453,8 +454,8 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
break;
|
||||
case R_ARM_PREL31:
|
||||
check(S + A - P, -(1LL << 30), 1LL << 30);
|
||||
*(U32<E> *)loc &= 0x8000'0000;
|
||||
*(U32<E> *)loc |= (S + A - P) & 0x7fff'ffff;
|
||||
*(ul32 *)loc &= 0x8000'0000;
|
||||
*(ul32 *)loc |= (S + A - P) & 0x7fff'ffff;
|
||||
break;
|
||||
case R_ARM_THM_MOVW_ABS_NC:
|
||||
write_thm_mov(loc, (S + A) | T);
|
||||
@@ -472,19 +473,19 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
write_thm_mov(loc, (S + A) >> 16);
|
||||
break;
|
||||
case R_ARM_TLS_GD32:
|
||||
*(U32<E> *)loc = sym.get_tlsgd_addr(ctx) + A - P;
|
||||
*(ul32 *)loc = sym.get_tlsgd_addr(ctx) + A - P;
|
||||
break;
|
||||
case R_ARM_TLS_LDM32:
|
||||
*(U32<E> *)loc = ctx.got->get_tlsld_addr(ctx) + A - P;
|
||||
*(ul32 *)loc = ctx.got->get_tlsld_addr(ctx) + A - P;
|
||||
break;
|
||||
case R_ARM_TLS_LDO32:
|
||||
*(U32<E> *)loc = S + A - ctx.dtp_addr;
|
||||
*(ul32 *)loc = S + A - ctx.dtp_addr;
|
||||
break;
|
||||
case R_ARM_TLS_IE32:
|
||||
*(U32<E> *)loc = sym.get_gottp_addr(ctx) + A - P;
|
||||
*(ul32 *)loc = sym.get_gottp_addr(ctx) + A - P;
|
||||
break;
|
||||
case R_ARM_TLS_LE32:
|
||||
*(U32<E> *)loc = S + A - ctx.tp_addr;
|
||||
*(ul32 *)loc = S + A - ctx.tp_addr;
|
||||
break;
|
||||
case R_ARM_TLS_GOTDESC:
|
||||
// ARM32 TLSDESC uses the following code sequence to materialize
|
||||
@@ -513,35 +514,35 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
// .L2: .word foo(gottpoff) + . - .L1
|
||||
if (sym.has_tlsdesc(ctx)) {
|
||||
// A is odd if the corresponding TLS_CALL is Thumb.
|
||||
*(U32<E> *)loc = sym.get_tlsdesc_addr(ctx) - P + A - ((A & 1) ? 6 : 4);
|
||||
*(ul32 *)loc = sym.get_tlsdesc_addr(ctx) - P + A - ((A & 1) ? 6 : 4);
|
||||
} else if (sym.has_gottp(ctx)) {
|
||||
*(U32<E> *)loc = sym.get_gottp_addr(ctx) - P + A - ((A & 1) ? 5 : 8);
|
||||
*(ul32 *)loc = sym.get_gottp_addr(ctx) - P + A - ((A & 1) ? 5 : 8);
|
||||
} else {
|
||||
*(U32<E> *)loc = S - ctx.tp_addr;
|
||||
*(ul32 *)loc = S - ctx.tp_addr;
|
||||
}
|
||||
break;
|
||||
case R_ARM_TLS_CALL:
|
||||
if (sym.has_tlsdesc(ctx)) {
|
||||
*(U32<E> *)loc = 0xeb00'0000; // bl 0
|
||||
*(U32<E> *)loc |= bits(get_tlsdesc_trampoline_addr() - P - 8, 25, 2);
|
||||
*(ul32 *)loc = 0xeb00'0000; // bl 0
|
||||
*(ul32 *)loc |= bits(get_tlsdesc_trampoline_addr() - P - 8, 25, 2);
|
||||
} else if (sym.has_gottp(ctx)) {
|
||||
*(U32<E> *)loc = 0xe79f'0000; // ldr r0, [pc, r0]
|
||||
*(ul32 *)loc = 0xe79f'0000; // ldr r0, [pc, r0]
|
||||
} else {
|
||||
*(U32<E> *)loc = 0xe320'f000; // nop
|
||||
*(ul32 *)loc = 0xe320'f000; // nop
|
||||
}
|
||||
break;
|
||||
case R_ARM_THM_TLS_CALL:
|
||||
if (sym.has_tlsdesc(ctx)) {
|
||||
u64 val = align_to(get_tlsdesc_trampoline_addr() - P - 4, 4);
|
||||
write_thm_b25(loc, val);
|
||||
*(U16<E> *)(loc + 2) &= ~0x1000; // rewrite BL with BLX
|
||||
*(ul16 *)(loc + 2) &= ~0x1000; // rewrite BL with BLX
|
||||
} else if (sym.has_gottp(ctx)) {
|
||||
// Since `ldr r0, [pc, r0]` is not representable in Thumb,
|
||||
// we use two instructions instead.
|
||||
*(U16<E> *)loc = 0x4478; // add r0, pc
|
||||
*(U16<E> *)(loc + 2) = 0x6800; // ldr r0, [r0]
|
||||
*(ul16 *)loc = 0x4478; // add r0, pc
|
||||
*(ul16 *)(loc + 2) = 0x6800; // ldr r0, [r0]
|
||||
} else {
|
||||
*(U32<E> *)loc = 0x8000'f3af; // nop.w
|
||||
*(ul32 *)loc = 0x8000'f3af; // nop.w
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -572,15 +573,15 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
switch (rel.r_type) {
|
||||
case R_ARM_ABS32:
|
||||
if (std::optional<u64> val = get_tombstone(sym, frag))
|
||||
*(U32<E> *)loc = *val;
|
||||
*(ul32 *)loc = *val;
|
||||
else
|
||||
*(U32<E> *)loc = S + A;
|
||||
*(ul32 *)loc = S + A;
|
||||
break;
|
||||
case R_ARM_TLS_LDO32:
|
||||
if (std::optional<u64> val = get_tombstone(sym, frag))
|
||||
*(U32<E> *)loc = *val;
|
||||
*(ul32 *)loc = *val;
|
||||
else
|
||||
*(U32<E> *)loc = S + A - ctx.dtp_addr;
|
||||
*(ul32 *)loc = S + A - ctx.dtp_addr;
|
||||
break;
|
||||
default:
|
||||
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
|
||||
@@ -702,7 +703,7 @@ void Thunk<E>::copy_buf(Context<E> &ctx) {
|
||||
|
||||
for (Symbol<E> *sym : symbols) {
|
||||
memcpy(buf, entry, sizeof(entry));
|
||||
*(U32<E> *)(buf + 12) = sym->get_addr(ctx) - P - 16;
|
||||
*(ul32 *)(buf + 12) = sym->get_addr(ctx) - P - 16;
|
||||
|
||||
buf += sizeof(entry);
|
||||
P += sizeof(entry);
|
||||
@@ -711,14 +712,10 @@ void Thunk<E>::copy_buf(Context<E> &ctx) {
|
||||
|
||||
template <>
|
||||
u64 get_eflags(Context<E> &ctx) {
|
||||
if constexpr (E::is_le)
|
||||
return EF_ARM_EABI_VER5;
|
||||
else
|
||||
return EF_ARM_EABI_VER5 | EF_ARM_BE8;
|
||||
return EF_ARM_EABI_VER5;
|
||||
}
|
||||
|
||||
template <>
|
||||
void create_arm_exidx_section<E>(Context<E> &ctx) {
|
||||
void create_arm_exidx_section(Context<E> &ctx) {
|
||||
for (i64 i = 0; i < ctx.chunks.size(); i++) {
|
||||
OutputSection<E> *osec = ctx.chunks[i]->to_osec();
|
||||
|
||||
@@ -735,20 +732,28 @@ void create_arm_exidx_section<E>(Context<E> &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void Arm32ExidxSection<E>::compute_section_size(Context<E> &ctx) {
|
||||
void Arm32ExidxSection::compute_section_size(Context<E> &ctx) {
|
||||
output_section.compute_section_size(ctx);
|
||||
this->shdr.sh_size = output_section.shdr.sh_size + 8; // +8 for sentinel
|
||||
}
|
||||
|
||||
template <>
|
||||
void Arm32ExidxSection<E>::update_shdr(Context<E> &ctx) {
|
||||
void Arm32ExidxSection::update_shdr(Context<E> &ctx) {
|
||||
// .ARM.exidx's sh_link should be set to the .text section index.
|
||||
// Runtime doesn't care about it, but the binutils's strip command does.
|
||||
if (Chunk<E> *chunk = find_chunk(ctx, ".text"))
|
||||
this->shdr.sh_link = chunk->shndx;
|
||||
}
|
||||
|
||||
void Arm32ExidxSection::remove_duplicate_entries(Context<E> &ctx) {
|
||||
this->shdr.sh_size = get_contents(ctx).size();
|
||||
}
|
||||
|
||||
void Arm32ExidxSection::copy_buf(Context<E> &ctx) {
|
||||
std::vector<u8> contents = get_contents(ctx);
|
||||
assert(this->shdr.sh_size == contents.size());
|
||||
write_vector(ctx.buf + this->shdr.sh_offset, contents);
|
||||
}
|
||||
|
||||
// Returns the end of the text segment
|
||||
static u64 get_text_end(Context<E> &ctx) {
|
||||
u64 ret = 0;
|
||||
@@ -767,8 +772,7 @@ static u64 get_text_end(Context<E> &ctx) {
|
||||
// likely that it's due to some historical reason.
|
||||
//
|
||||
// This function returns contents of .ARM.exidx.
|
||||
template <>
|
||||
std::vector<u8> Arm32ExidxSection<E>::get_contents(Context<E> &ctx) {
|
||||
std::vector<u8> Arm32ExidxSection::get_contents(Context<E> &ctx) {
|
||||
// .ARM.exidx records consists of a signed 31-bit relative address
|
||||
// and a 32-bit value. The relative address indicates the start
|
||||
// address of a function that the record covers. The value is one of
|
||||
@@ -784,8 +788,8 @@ std::vector<u8> Arm32ExidxSection<E>::get_contents(Context<E> &ctx) {
|
||||
const u32 CANTUNWIND = 1;
|
||||
|
||||
struct Entry {
|
||||
U32<E> addr;
|
||||
U32<E> val;
|
||||
ul32 addr;
|
||||
ul32 val;
|
||||
};
|
||||
|
||||
// We reserve one extra slot for the sentinel
|
||||
@@ -816,13 +820,19 @@ std::vector<u8> Arm32ExidxSection<E>::get_contents(Context<E> &ctx) {
|
||||
ent[i].val = 0x7fff'ffff & (ent[i].val + offset);
|
||||
});
|
||||
|
||||
ranges::sort(ent, ent + num_entries, {}, &Entry::addr);
|
||||
std::sort(ent, ent + num_entries, [](const Entry &a, const Entry &b) {
|
||||
return a.addr < b.addr;
|
||||
});
|
||||
|
||||
// Remove duplicate adjacent entries. That is, if two adjacent functions
|
||||
// have the same compact unwind info or are both CANTUNWIND, we can
|
||||
// merge them into a single address range.
|
||||
auto tail = ranges::unique(ent, ent + num_entries, {}, &Entry::val);
|
||||
num_entries -= tail.size();
|
||||
auto it = std::unique(ent, ent + num_entries,
|
||||
[](const Entry &a, const Entry &b) {
|
||||
return a.val == b.val;
|
||||
});
|
||||
|
||||
num_entries = it - ent;
|
||||
buf.resize(num_entries * sizeof(Entry));
|
||||
|
||||
// Make addresses relative to themselves.
|
||||
@@ -836,84 +846,4 @@ std::vector<u8> Arm32ExidxSection<E>::get_contents(Context<E> &ctx) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
template <>
|
||||
void Arm32ExidxSection<E>::remove_duplicate_entries(Context<E> &ctx) {
|
||||
this->shdr.sh_size = get_contents(ctx).size();
|
||||
}
|
||||
|
||||
template <>
|
||||
void Arm32ExidxSection<E>::copy_buf(Context<E> &ctx) {
|
||||
std::vector<u8> contents = get_contents(ctx);
|
||||
assert(this->shdr.sh_size == contents.size());
|
||||
write_vector(ctx.buf + this->shdr.sh_offset, contents);
|
||||
}
|
||||
|
||||
#if MOLD_ARM32BE
|
||||
// Even though using ARM32 in big-endian mode is very rare, the processor
|
||||
// technically supports both little- and big-endian modes. There are two
|
||||
// variants of big-endian mode: BE32 and BE8. In BE32, instructions and
|
||||
// data are encoded in big-endian. In BE8, instructions are encoded in
|
||||
// little-endian, and only data is in big-endian. BE8 is the de facto
|
||||
// standard for ARMv6 or later. We support only BE8.
|
||||
//
|
||||
// A tricky thing is that instructions in an object file are always
|
||||
// big-endian if the file is compiled for big-endian mode. In other words,
|
||||
// the compiler always emit code in BE32 if -mbig-endian is specified. It
|
||||
// is the linker's responsibility to rewrite instructions from big-endian
|
||||
// to little-endian for an BE8 output. This function does that.
|
||||
//
|
||||
// The text section may contain a mix of 32-bit ARM instructions, 16-bit
|
||||
// Thumb instructions, and data. We need to distinguish them to swap 4
|
||||
// bytes, 2 bytes, or not swap bytes, respectively. The beginning of ARM
|
||||
// code, Thumb code, and data is labeled with a mapping symbol of $a, $t,
|
||||
// and $d, respectively. We use mapping symbols to determine what to do
|
||||
// with the text section.
|
||||
//
|
||||
// This function is called after we copy the input section contents to the
|
||||
// output file. We rewrite instructions in the output buffer in place.
|
||||
void arm32be_swap_bytes(Context<E> &ctx) {
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
// Collect mapping symbols
|
||||
std::vector<Symbol<E> *> syms;
|
||||
|
||||
for (Symbol<E> *sym : file->get_local_syms())
|
||||
if (InputSection<E> *isec = sym->get_input_section())
|
||||
if (isec->is_alive && (isec->shdr().sh_flags & SHF_EXECINSTR))
|
||||
if (std::string_view x = sym->name();
|
||||
x == "$a" || x.starts_with("$a.") ||
|
||||
x == "$t" || x.starts_with("$t.") ||
|
||||
x == "$d" || x.starts_with("$d."))
|
||||
syms.push_back(sym);
|
||||
|
||||
// Group mapping symbols by input section and sort by address
|
||||
ranges::stable_sort(syms, {}, [](const Symbol<E> *sym) {
|
||||
return std::tuple{(uintptr_t)sym->get_input_section(), sym->value};
|
||||
});
|
||||
|
||||
// Swap bytes
|
||||
for (i64 i = 0; i < syms.size(); i++) {
|
||||
Symbol<E> &sym = *syms[i];
|
||||
if (sym.name().starts_with("$d"))
|
||||
continue;
|
||||
|
||||
InputSection<E> &isec = *sym.get_input_section();
|
||||
u8 *base = ctx.buf + isec.output_section->shdr.sh_offset + isec.offset;
|
||||
u8 *start = base + sym.value;
|
||||
|
||||
u8 *end;
|
||||
if (i + 1 < syms.size() && syms[i + 1]->get_input_section() == &isec)
|
||||
end = base + syms[i + 1]->value;
|
||||
else
|
||||
end = base + isec.sh_size;
|
||||
|
||||
i64 sz = sym.name().starts_with("$a") ? 4 : 2;
|
||||
for (u8 *p = start; p < end; p += sz)
|
||||
std::reverse(p, p + sz);
|
||||
}
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace mold
|
||||
|
||||
#endif
|
||||
@@ -66,7 +66,7 @@ static u64 higher(u64 val, u64 pc) {
|
||||
// LU52I.D simply set bits to [51:31] and to [63:53], respectively.
|
||||
//
|
||||
// Compensating all the sign-extensions is a bit complicated. The
|
||||
// psABI gives the following formula.
|
||||
// psABI gave the following formula.
|
||||
val = val + 0x8000'0000 + ((val & 0x800) ? (0x1000 - 0x1'0000'0000) : 0);
|
||||
return page(val) - page(pc - 8);
|
||||
}
|
||||
|
||||
+12
-5
@@ -484,12 +484,17 @@ static InputSection<E> *get_opd_section(ObjectFile<E> &file) {
|
||||
}
|
||||
|
||||
static ElfRel<E> *
|
||||
get_relocation_at(Context<E> &ctx, InputSection<E> &isec, u64 offset) {
|
||||
get_relocation_at(Context<E> &ctx, InputSection<E> &isec, i64 offset) {
|
||||
std::span<ElfRel<E>> rels = isec.get_rels(ctx);
|
||||
auto it = ranges::lower_bound(rels, offset, {}, [](const ElfRel<E> &r) {
|
||||
return (u64)r.r_offset;
|
||||
|
||||
auto it = std::lower_bound(rels.begin(), rels.end(), offset,
|
||||
[](const ElfRel<E> &r, i64 offset) {
|
||||
return r.r_offset < offset;
|
||||
});
|
||||
if (it == rels.end() || it->r_offset != offset)
|
||||
|
||||
if (it == rels.end())
|
||||
return nullptr;
|
||||
if (it->r_offset != offset)
|
||||
return nullptr;
|
||||
return &*it;
|
||||
}
|
||||
@@ -504,7 +509,9 @@ struct OpdSymbol {
|
||||
static Symbol<E> *
|
||||
get_opd_sym_at(std::span<OpdSymbol> syms, u64 offset) {
|
||||
auto it = ranges::lower_bound(syms, offset, {}, &OpdSymbol::r_offset);
|
||||
if (it == syms.end() || it->r_offset != offset)
|
||||
if (it == syms.end())
|
||||
return nullptr;
|
||||
if (it->r_offset != offset)
|
||||
return nullptr;
|
||||
return it->sym;
|
||||
}
|
||||
|
||||
+9
-3
@@ -303,8 +303,8 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_RISCV_32:
|
||||
if (E::is_64)
|
||||
*(U32<E> *)loc = S + A;
|
||||
assert(E::is_64);
|
||||
*(U32<E> *)loc = S + A;
|
||||
break;
|
||||
case R_RISCV_64:
|
||||
break;
|
||||
@@ -321,6 +321,12 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
i64 val = S + A - P;
|
||||
i64 rd = get_rd(buf + rel.r_offset + 4);
|
||||
|
||||
// Calling an undefined weak symbol does not make sense.
|
||||
// We make such call into an infinite loop. This should
|
||||
// help debugging of a faulty program.
|
||||
if (sym.esym().is_undef_weak())
|
||||
val = 0;
|
||||
|
||||
if (removed_bytes == 4) {
|
||||
// auipc + jalr -> jal
|
||||
*(ul32 *)loc = (rd << 7) | 0b1101111;
|
||||
@@ -467,7 +473,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
}
|
||||
case R_RISCV_TLSDESC_HI20:
|
||||
// RISC-V TLSDESC uses the following code sequence to materialize
|
||||
// a TP-relative address in a0.
|
||||
// a TP-relative address in x0.
|
||||
//
|
||||
// .L0:
|
||||
// auipc tX, 0
|
||||
|
||||
+12
-25
@@ -587,7 +587,6 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
bool warn_shared_textrel = false;
|
||||
bool error_unresolved_symbols = true;
|
||||
std::optional<SeparateCodeKind> z_separate_code;
|
||||
std::optional<bool> allow_shlib_undefined;
|
||||
std::optional<bool> report_undefined;
|
||||
std::optional<bool> z_relro;
|
||||
std::optional<bool> z_dynamic_undefined_weak;
|
||||
@@ -706,20 +705,21 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
} else if (read_flag("no-dynamic-linker")) {
|
||||
ctx.arg.dynamic_linker = "";
|
||||
} else if (read_flag("v")) {
|
||||
Out(ctx) << mold_version;
|
||||
Out(ctx) << get_mold_version();
|
||||
version_shown = true;
|
||||
} else if (read_flag("version")) {
|
||||
Out(ctx) << mold_version;
|
||||
Out(ctx) << get_mold_version();
|
||||
exit(0);
|
||||
} else if (read_flag("V")) {
|
||||
Out(ctx) << mold_version
|
||||
Out(ctx) << get_mold_version()
|
||||
<< "\n Supported emulations:\n elf_x86_64\n elf_i386\n"
|
||||
<< " aarch64elf\n aarch64linux\n aarch64elfb\n"
|
||||
<< " aarch64linuxb\n armelf_linux_eabi\n elf64lriscv\n"
|
||||
<< " elf64briscv\n elf32lriscv\n elf32briscv\n"
|
||||
<< " elf32ppc\n elf64ppc\n elf64lppc\n elf64_s390\n"
|
||||
<< " elf64_sparc\n m68kelf\n shlelf_linux\n"
|
||||
<< " shelf_linux\n elf64loongarch\n elf32loongarch";
|
||||
<< " shelf_linux\n elf64loongarch\n elf32loongarch\n"
|
||||
<< " arclinux";
|
||||
version_shown = true;
|
||||
} else if (read_arg("mllvm")) {
|
||||
ctx.arg.plugin_opt.emplace_back(arg);
|
||||
@@ -733,9 +733,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
} else if (arg == "aarch64elfb" || arg == "aarch64linuxb") {
|
||||
ctx.arg.emulation = ARM64BE::name;
|
||||
} else if (arg == "armelf_linux_eabi") {
|
||||
ctx.arg.emulation = ARM32LE::name;
|
||||
} else if (arg == "armelfb_linux_eabi") {
|
||||
ctx.arg.emulation = ARM32BE::name;
|
||||
ctx.arg.emulation = ARM32::name;
|
||||
} else if (arg == "elf64lriscv") {
|
||||
ctx.arg.emulation = RV64LE::name;
|
||||
} else if (arg == "elf64briscv") {
|
||||
@@ -764,6 +762,8 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
ctx.arg.emulation = LOONGARCH64::name;
|
||||
} else if (arg == "elf32loongarch") {
|
||||
ctx.arg.emulation = LOONGARCH32::name;
|
||||
} else if (arg == "arclinux") {
|
||||
ctx.arg.emulation = ARC::name;
|
||||
} else {
|
||||
Fatal(ctx) << "unknown -m argument: " << arg;
|
||||
}
|
||||
@@ -1294,10 +1294,6 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
}
|
||||
} else if (read_flag("no-build-id")) {
|
||||
ctx.arg.build_id.kind = BuildId::NONE;
|
||||
} else if (read_flag("be8")) {
|
||||
ctx.arg.be8 = true;
|
||||
} else if (read_flag("be32")) {
|
||||
ctx.arg.be8 = false;
|
||||
} else if (read_arg("format") || read_arg("b")) {
|
||||
if (arg == "binary")
|
||||
Fatal(ctx)
|
||||
@@ -1310,9 +1306,9 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
} else if (read_arg("filter") || read_arg("F")) {
|
||||
ctx.arg.filter.push_back(arg);
|
||||
} else if (read_flag("allow-shlib-undefined")) {
|
||||
allow_shlib_undefined = true;
|
||||
ctx.arg.allow_shlib_undefined = true;
|
||||
} else if (read_flag("no-allow-shlib-undefined")) {
|
||||
allow_shlib_undefined = false;
|
||||
ctx.arg.allow_shlib_undefined = false;
|
||||
} else if (read_arg("O")) {
|
||||
} else if (read_flag("EB")) {
|
||||
} else if (read_flag("EL")) {
|
||||
@@ -1419,11 +1415,6 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
if (ctx.arg.pic)
|
||||
ctx.arg.image_base = 0;
|
||||
|
||||
if (allow_shlib_undefined)
|
||||
ctx.arg.allow_shlib_undefined = *allow_shlib_undefined;
|
||||
else
|
||||
ctx.arg.allow_shlib_undefined = ctx.arg.shared;
|
||||
|
||||
if (!report_undefined)
|
||||
report_undefined = !ctx.arg.shared;
|
||||
|
||||
@@ -1444,9 +1435,6 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
if (ctx.arg.relocatable)
|
||||
ctx.arg.static_ = true;
|
||||
|
||||
if (ctx.arg.static_)
|
||||
ctx.arg.dynamic_linker = "";
|
||||
|
||||
if (ctx.arg.shuffle_sections == SHUFFLE_SECTIONS_SHUFFLE) {
|
||||
if (shuffle_sections_seed)
|
||||
ctx.arg.shuffle_sections_seed = *shuffle_sections_seed;
|
||||
@@ -1500,9 +1488,6 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
if (ctx.arg.image_base % ctx.page_size)
|
||||
Fatal(ctx) << "-image-base must be a multiple of -max-page-size";
|
||||
|
||||
if (ctx.arg.emulation == ARM32BE::name && !ctx.arg.be8)
|
||||
Fatal(ctx) << "--be32 is not supported";
|
||||
|
||||
if (ctx.arg.thread_count == 0)
|
||||
ctx.arg.thread_count = get_default_thread_count();
|
||||
|
||||
@@ -1513,7 +1498,9 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
std::string ver = ctx.arg.soname;
|
||||
if (ver.empty())
|
||||
ver = path_filename(ctx.arg.output);
|
||||
|
||||
ctx.arg.version_definitions.push_back(ver);
|
||||
ctx.default_version = VER_NDX_LAST_RESERVED + 1;
|
||||
}
|
||||
|
||||
for (std::string_view path : version_scripts) {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#include "mold.h"
|
||||
|
||||
namespace mold {
|
||||
|
||||
std::string get_mold_version() {
|
||||
if (mold_git_hash.empty())
|
||||
return "mold "s + MOLD_VERSION + " (compatible with GNU ld)";
|
||||
return "mold "s + MOLD_VERSION + " (" + mold_git_hash +
|
||||
"; compatible with GNU ld)";
|
||||
}
|
||||
|
||||
} // namespace mold
|
||||
+73
-10
@@ -1,12 +1,11 @@
|
||||
#include "elf.h"
|
||||
#include <sstream>
|
||||
|
||||
namespace mold {
|
||||
|
||||
static std::string unknown_type(u32 r_type) {
|
||||
std::stringstream ss;
|
||||
ss << "unknown (0x" << std::hex << r_type << ")";
|
||||
return ss.str();
|
||||
char buf[50];
|
||||
snprintf(buf, sizeof(buf), "unknown (0x%x)", r_type);
|
||||
return buf;
|
||||
}
|
||||
|
||||
#define CASE(x) case x: return #x
|
||||
@@ -233,7 +232,7 @@ std::string rel_to_string<ARM64BE>(u32 r_type) {
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string rel_to_string<ARM32LE>(u32 r_type) {
|
||||
std::string rel_to_string<ARM32>(u32 r_type) {
|
||||
switch (r_type) {
|
||||
CASE(R_ARM_NONE);
|
||||
CASE(R_ARM_PC24);
|
||||
@@ -374,11 +373,6 @@ std::string rel_to_string<ARM32LE>(u32 r_type) {
|
||||
return unknown_type(r_type);
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string rel_to_string<ARM32BE>(u32 r_type) {
|
||||
return rel_to_string<ARM32LE>(r_type);
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string rel_to_string<RV64LE>(u32 r_type) {
|
||||
switch (r_type) {
|
||||
@@ -1044,4 +1038,73 @@ std::string rel_to_string<LOONGARCH32>(u32 r_type) {
|
||||
return rel_to_string<LOONGARCH64>(r_type);
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string rel_to_string<ARC>(u32 r_type) {
|
||||
switch (r_type) {
|
||||
CASE(R_ARC_8);
|
||||
CASE(R_ARC_16);
|
||||
CASE(R_ARC_24);
|
||||
CASE(R_ARC_32);
|
||||
CASE(R_ARC_N8);
|
||||
CASE(R_ARC_N16);
|
||||
CASE(R_ARC_N24);
|
||||
CASE(R_ARC_N32);
|
||||
CASE(R_ARC_SDA);
|
||||
CASE(R_ARC_SECTOFF);
|
||||
CASE(R_ARC_S21H_PCREL);
|
||||
CASE(R_ARC_S21W_PCREL);
|
||||
CASE(R_ARC_S25H_PCREL);
|
||||
CASE(R_ARC_S25W_PCREL);
|
||||
CASE(R_ARC_SDA32);
|
||||
CASE(R_ARC_SDA_LDST);
|
||||
CASE(R_ARC_SDA_LDST1);
|
||||
CASE(R_ARC_SDA_LDST2);
|
||||
CASE(R_ARC_SDA16_LD);
|
||||
CASE(R_ARC_SDA16_LD1);
|
||||
CASE(R_ARC_SDA16_LD2);
|
||||
CASE(R_ARC_S13_PCREL);
|
||||
CASE(R_ARC_W);
|
||||
CASE(R_ARC_32_ME);
|
||||
CASE(R_ARC_32_ME_S);
|
||||
CASE(R_ARC_N32_ME);
|
||||
CASE(R_ARC_SECTOFF_ME);
|
||||
CASE(R_ARC_SDA32_ME);
|
||||
CASE(R_ARC_W_ME);
|
||||
CASE(R_ARC_SECTOFF_ME_1);
|
||||
CASE(R_ARC_SECTOFF_ME_2);
|
||||
CASE(R_ARC_SECTOFF_1);
|
||||
CASE(R_ARC_SECTOFF_2);
|
||||
CASE(R_ARC_SDA_12);
|
||||
CASE(R_ARC_SDA16_ST2);
|
||||
CASE(R_ARC_32_PCREL);
|
||||
CASE(R_ARC_PC32);
|
||||
CASE(R_ARC_GOT32);
|
||||
CASE(R_ARC_GOTPC32);
|
||||
CASE(R_ARC_PLT32);
|
||||
CASE(R_ARC_COPY);
|
||||
CASE(R_ARC_GLOB_DAT);
|
||||
CASE(R_ARC_JMP_SLOT);
|
||||
CASE(R_ARC_RELATIVE);
|
||||
CASE(R_ARC_GOTOFF);
|
||||
CASE(R_ARC_GOTPC);
|
||||
CASE(R_ARC_S21W_PCREL_PLT);
|
||||
CASE(R_ARC_S25H_PCREL_PLT);
|
||||
CASE(R_ARC_JLI_SECTOFF);
|
||||
CASE(R_ARC_TLS_DTPMOD);
|
||||
CASE(R_ARC_TLS_TPOFF);
|
||||
CASE(R_ARC_TLS_GD_GOT);
|
||||
CASE(R_ARC_TLS_GD_LD);
|
||||
CASE(R_ARC_TLS_GD_CALL);
|
||||
CASE(R_ARC_TLS_IE_GOT);
|
||||
CASE(R_ARC_TLS_DTPOFF);
|
||||
CASE(R_ARC_TLS_DTPOFF_S9);
|
||||
CASE(R_ARC_TLS_LE_S9);
|
||||
CASE(R_ARC_TLS_LE_32);
|
||||
CASE(R_ARC_S25W_PCREL_PLT);
|
||||
CASE(R_ARC_S21H_PCREL_PLT);
|
||||
CASE(R_ARC_NPS_CMEM16);
|
||||
}
|
||||
return unknown_type(r_type);
|
||||
}
|
||||
|
||||
} // namespace mold
|
||||
@@ -13,8 +13,7 @@ struct X86_64;
|
||||
struct I386;
|
||||
struct ARM64LE;
|
||||
struct ARM64BE;
|
||||
struct ARM32LE;
|
||||
struct ARM32BE;
|
||||
struct ARM32;
|
||||
struct RV64LE;
|
||||
struct RV64BE;
|
||||
struct RV32LE;
|
||||
@@ -29,6 +28,7 @@ struct SH4LE;
|
||||
struct SH4BE;
|
||||
struct LOONGARCH64;
|
||||
struct LOONGARCH32;
|
||||
struct ARC;
|
||||
|
||||
template <typename E> struct ElfSym;
|
||||
template <typename E> struct ElfShdr;
|
||||
@@ -89,6 +89,7 @@ enum : u32 {
|
||||
SHT_HIOS = 0x6fffffff,
|
||||
SHT_X86_64_UNWIND = 0x70000001,
|
||||
SHT_ARM_EXIDX = 0x70000001,
|
||||
SHT_ARC_ATTRIBUTES = 0x70000001,
|
||||
SHT_ARM_ATTRIBUTES = 0x70000003,
|
||||
SHT_RISCV_ATTRIBUTES = 0x70000003,
|
||||
SHT_LOUSER = 0x80000000,
|
||||
@@ -236,6 +237,7 @@ enum : u32 {
|
||||
EM_SPARC64 = 43,
|
||||
EM_X86_64 = 62,
|
||||
EM_AARCH64 = 183,
|
||||
EM_ARC_COMPACT2 = 195,
|
||||
EM_RISCV = 243,
|
||||
EM_LOONGARCH = 258,
|
||||
};
|
||||
@@ -358,10 +360,9 @@ enum : u32 {
|
||||
};
|
||||
|
||||
enum : u32 {
|
||||
EF_ARM_ABI_FLOAT_SOFT = 0x200,
|
||||
EF_ARM_ABI_FLOAT_HARD = 0x400,
|
||||
EF_ARM_BE8 = 0x800000,
|
||||
EF_ARM_EABI_VER5 = 0x5000000,
|
||||
EF_ARM_ABI_FLOAT_SOFT = 0x00000200,
|
||||
EF_ARM_ABI_FLOAT_HARD = 0x00000400,
|
||||
EF_ARM_EABI_VER5 = 0x05000000,
|
||||
};
|
||||
|
||||
enum : u32 {
|
||||
@@ -1364,6 +1365,71 @@ enum : u32 {
|
||||
R_LARCH_TLS_DESC_PCREL20_S2 = 126,
|
||||
};
|
||||
|
||||
enum : u32 {
|
||||
R_ARC_8 = 1,
|
||||
R_ARC_16 = 2,
|
||||
R_ARC_24 = 3,
|
||||
R_ARC_32 = 4,
|
||||
R_ARC_N8 = 8,
|
||||
R_ARC_N16 = 9,
|
||||
R_ARC_N24 = 10,
|
||||
R_ARC_N32 = 11,
|
||||
R_ARC_SDA = 12,
|
||||
R_ARC_SECTOFF = 13,
|
||||
R_ARC_S21H_PCREL = 14,
|
||||
R_ARC_S21W_PCREL = 15,
|
||||
R_ARC_S25H_PCREL = 16,
|
||||
R_ARC_S25W_PCREL = 17,
|
||||
R_ARC_SDA32 = 18,
|
||||
R_ARC_SDA_LDST = 19,
|
||||
R_ARC_SDA_LDST1 = 20,
|
||||
R_ARC_SDA_LDST2 = 21,
|
||||
R_ARC_SDA16_LD = 22,
|
||||
R_ARC_SDA16_LD1 = 23,
|
||||
R_ARC_SDA16_LD2 = 24,
|
||||
R_ARC_S13_PCREL = 25,
|
||||
R_ARC_W = 26,
|
||||
R_ARC_32_ME = 27,
|
||||
R_ARC_32_ME_S = 105,
|
||||
R_ARC_N32_ME = 28,
|
||||
R_ARC_SECTOFF_ME = 29,
|
||||
R_ARC_SDA32_ME = 30,
|
||||
R_ARC_W_ME = 31,
|
||||
R_ARC_SECTOFF_ME_1 = 41,
|
||||
R_ARC_SECTOFF_ME_2 = 42,
|
||||
R_ARC_SECTOFF_1 = 43,
|
||||
R_ARC_SECTOFF_2 = 44,
|
||||
R_ARC_SDA_12 = 45,
|
||||
R_ARC_SDA16_ST2 = 48,
|
||||
R_ARC_32_PCREL = 49,
|
||||
R_ARC_PC32 = 50,
|
||||
R_ARC_GOT32 = 59,
|
||||
R_ARC_GOTPC32 = 51,
|
||||
R_ARC_PLT32 = 52,
|
||||
R_ARC_COPY = 53,
|
||||
R_ARC_GLOB_DAT = 54,
|
||||
R_ARC_JMP_SLOT = 55,
|
||||
R_ARC_RELATIVE = 56,
|
||||
R_ARC_GOTOFF = 57,
|
||||
R_ARC_GOTPC = 58,
|
||||
R_ARC_S21W_PCREL_PLT = 60,
|
||||
R_ARC_S25H_PCREL_PLT = 61,
|
||||
R_ARC_JLI_SECTOFF = 63,
|
||||
R_ARC_TLS_DTPMOD = 66,
|
||||
R_ARC_TLS_TPOFF = 68,
|
||||
R_ARC_TLS_GD_GOT = 69,
|
||||
R_ARC_TLS_GD_LD = 70,
|
||||
R_ARC_TLS_GD_CALL = 71,
|
||||
R_ARC_TLS_IE_GOT = 72,
|
||||
R_ARC_TLS_DTPOFF = 67,
|
||||
R_ARC_TLS_DTPOFF_S9 = 73,
|
||||
R_ARC_TLS_LE_S9 = 74,
|
||||
R_ARC_TLS_LE_32 = 75,
|
||||
R_ARC_S25W_PCREL_PLT = 76,
|
||||
R_ARC_S21H_PCREL_PLT = 77,
|
||||
R_ARC_NPS_CMEM16 = 78,
|
||||
};
|
||||
|
||||
// Returns true if a given relocation is of type used for direct
|
||||
// function call.
|
||||
template <typename E>
|
||||
@@ -1812,8 +1878,7 @@ template <typename E> concept is_x86_64 = std::same_as<E, X86_64>;
|
||||
template <typename E> concept is_i386 = std::same_as<E, I386>;
|
||||
template <typename E> concept is_arm64le = std::same_as<E, ARM64LE>;
|
||||
template <typename E> concept is_arm64be = std::same_as<E, ARM64BE>;
|
||||
template <typename E> concept is_arm32le = std::same_as<E, ARM32LE>;
|
||||
template <typename E> concept is_arm32be = std::same_as<E, ARM32BE>;
|
||||
template <typename E> concept is_arm32 = std::same_as<E, ARM32>;
|
||||
template <typename E> concept is_rv64le = std::same_as<E, RV64LE>;
|
||||
template <typename E> concept is_rv64be = std::same_as<E, RV64BE>;
|
||||
template <typename E> concept is_rv32le = std::same_as<E, RV32LE>;
|
||||
@@ -1828,9 +1893,9 @@ template <typename E> concept is_sh4le = std::same_as<E, SH4LE>;
|
||||
template <typename E> concept is_sh4be = std::same_as<E, SH4BE>;
|
||||
template <typename E> concept is_loongarch64 = std::same_as<E, LOONGARCH64>;
|
||||
template <typename E> concept is_loongarch32 = std::same_as<E, LOONGARCH32>;
|
||||
template <typename E> concept is_arc = std::same_as<E, ARC>;
|
||||
|
||||
template <typename E> concept is_x86 = is_x86_64<E> || is_i386<E>;
|
||||
template <typename E> concept is_arm32 = is_arm32le<E> || is_arm32be<E>;
|
||||
template <typename E> concept is_arm64 = is_arm64le<E> || is_arm64be<E>;
|
||||
template <typename E> concept is_arm = is_arm64<E> || is_arm32<E>;
|
||||
template <typename E> concept is_rv64 = is_rv64le<E> || is_rv64be<E>;
|
||||
@@ -1924,7 +1989,7 @@ struct ARM64BE : ARM64LE {
|
||||
static constexpr bool is_le = false;
|
||||
};
|
||||
|
||||
struct ARM32LE {
|
||||
struct ARM32 {
|
||||
static constexpr std::string_view name = "arm32";
|
||||
static constexpr bool is_64 = false;
|
||||
static constexpr bool is_le = true;
|
||||
@@ -1954,11 +2019,6 @@ struct ARM32LE {
|
||||
};
|
||||
};
|
||||
|
||||
struct ARM32BE : ARM32LE {
|
||||
static constexpr std::string_view name = "arm32be";
|
||||
static constexpr bool is_le = false;
|
||||
};
|
||||
|
||||
struct RV64LE {
|
||||
static constexpr std::string_view name = "riscv64";
|
||||
static constexpr bool is_64 = true;
|
||||
@@ -2237,4 +2297,27 @@ struct LOONGARCH32 {
|
||||
static constexpr u32 R_FUNCALL[] = { R_LARCH_B26, R_LARCH_CALL36 };
|
||||
};
|
||||
|
||||
struct ARC {
|
||||
static constexpr std::string_view name = "arc";
|
||||
static constexpr bool is_64 = false;
|
||||
static constexpr bool is_le = true;
|
||||
static constexpr bool is_rela = true;
|
||||
static constexpr u32 page_size = 8192;
|
||||
static constexpr u32 e_machine = EM_ARC_COMPACT2;
|
||||
static constexpr u32 plt_hdr_size = 24;
|
||||
static constexpr u32 plt_size = 16;
|
||||
static constexpr u32 pltgot_size = 16;
|
||||
static constexpr u8 filler[] = { 0, 0, 0, 0 };
|
||||
|
||||
static constexpr u32 R_COPY = R_ARC_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_ARC_GLOB_DAT;
|
||||
static constexpr u32 R_JUMP_SLOT = R_ARC_JMP_SLOT;
|
||||
static constexpr u32 R_ABS = R_ARC_32;
|
||||
static constexpr u32 R_RELATIVE = R_ARC_RELATIVE;
|
||||
static constexpr u32 R_DTPOFF = R_ARC_TLS_DTPOFF;
|
||||
static constexpr u32 R_TPOFF = R_ARC_TLS_TPOFF;
|
||||
static constexpr u32 R_DTPMOD = R_ARC_TLS_DTPMOD;
|
||||
static constexpr u32 R_FUNCALL[] = { R_ARC_S25W_PCREL_PLT };
|
||||
};
|
||||
|
||||
} // namespace mold
|
||||
@@ -1,14 +0,0 @@
|
||||
#include "mold.h"
|
||||
#include "mold-git-hash.h"
|
||||
|
||||
std::string mold::mold_version =
|
||||
#ifdef MOLD_GIT_HASH
|
||||
"mold " MOLD_VERSION " (" MOLD_GIT_HASH "; compatible with GNU ld)";
|
||||
#else
|
||||
"mold " MOLD_VERSION " (compatible with GNU ld)";
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
mold::set_mimalloc_options();
|
||||
return mold::mold_main<mold::X86_64>(argc, argv);
|
||||
}
|
||||
+3
-1
@@ -139,7 +139,7 @@ static std::string_view get_elf_type(u8 *buf) {
|
||||
case EM_X86_64:
|
||||
return X86_64::name;
|
||||
case EM_ARM:
|
||||
return is_le ? ARM32LE::name : ARM32BE::name;
|
||||
return ARM32::name;
|
||||
case EM_AARCH64:
|
||||
return is_le ? ARM64LE::name : ARM64BE::name;
|
||||
case EM_RISCV:
|
||||
@@ -160,6 +160,8 @@ static std::string_view get_elf_type(u8 *buf) {
|
||||
return is_le ? SH4LE::name : SH4BE::name;
|
||||
case EM_LOONGARCH:
|
||||
return is_64 ? LOONGARCH64::name : LOONGARCH32::name;
|
||||
case EM_ARC_COMPACT2:
|
||||
return ARC::name;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
||||
+19
-57
@@ -317,6 +317,10 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (is_arc<E>)
|
||||
if (shdr.sh_type == SHT_ARC_ATTRIBUTES)
|
||||
continue;
|
||||
|
||||
switch (shdr.sh_type) {
|
||||
case SHT_GROUP: {
|
||||
// Get the signature of this section group.
|
||||
@@ -425,7 +429,6 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
|
||||
continue;
|
||||
|
||||
this->sections[i] = std::make_unique<InputSection<E>>(ctx, *this, i);
|
||||
InputSection<E> *isec = this->sections[i].get();
|
||||
|
||||
// Save .llvm_addrsig for --icf=safe.
|
||||
if (shdr.sh_type == SHT_LLVM_ADDRSIG && !ctx.arg.relocatable) {
|
||||
@@ -447,17 +450,19 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
|
||||
this->has_ctors = true;
|
||||
|
||||
if (name == ".eh_frame")
|
||||
eh_frame_sections.push_back(isec);
|
||||
|
||||
if (name == ".debug_info" && !(shdr.sh_flags & SHF_ALLOC))
|
||||
debug_info = isec;
|
||||
eh_frame_sections.push_back(this->sections[i].get());
|
||||
|
||||
if constexpr (is_ppc32<E>)
|
||||
if (name == ".got2")
|
||||
extra.got2 = isec;
|
||||
extra.got2 = this->sections[i].get();
|
||||
|
||||
// Save debug sections for --gdb-index.
|
||||
if (ctx.arg.gdb_index) {
|
||||
InputSection<E> *isec = this->sections[i].get();
|
||||
|
||||
if (name == ".debug_info")
|
||||
debug_info = isec;
|
||||
|
||||
// If --gdb-index is given, contents of .debug_gnu_pubnames and
|
||||
// .debug_gnu_pubtypes are copied to .gdb_index, so keeping them
|
||||
// in an output file is just a waste of space.
|
||||
@@ -998,7 +1003,6 @@ void ObjectFile<E>::resolve_symbols(Context<E> &ctx) {
|
||||
}
|
||||
|
||||
std::scoped_lock lock(sym.mu);
|
||||
|
||||
if (get_rank(this, esym, !this->is_reachable) < get_rank(sym)) {
|
||||
sym.file = this;
|
||||
sym.set_input_section(isec);
|
||||
@@ -1006,7 +1010,6 @@ void ObjectFile<E>::resolve_symbols(Context<E> &ctx) {
|
||||
sym.sym_idx = i;
|
||||
sym.ver_idx = ctx.default_version;
|
||||
sym.is_weak = esym.is_weak();
|
||||
sym.is_versioned_default = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1274,48 +1277,23 @@ void SharedFile<E>::parse(Context<E> &ctx) {
|
||||
if (vers.empty() || esyms[i].is_undef())
|
||||
ver = VER_NDX_GLOBAL;
|
||||
else
|
||||
ver = vers[i] & ~VERSYM_HIDDEN;
|
||||
ver = (vers[i] & ~VERSYM_HIDDEN);
|
||||
|
||||
if (ver == VER_NDX_LOCAL)
|
||||
continue;
|
||||
|
||||
std::string_view name = this->symbol_strtab.data() + esyms[i].st_name;
|
||||
bool is_default = vers.empty() || !(vers[i] & VERSYM_HIDDEN);
|
||||
|
||||
this->elf_syms2.push_back(esyms[i]);
|
||||
this->versyms.push_back(ver);
|
||||
|
||||
std::string_view name = this->symbol_strtab.data() + esyms[i].st_name;
|
||||
|
||||
auto get_versioned_sym = [&] {
|
||||
std::string_view key = save_string(
|
||||
ctx, std::string(name) + "@" + std::string(version_strings[ver]));
|
||||
return get_symbol(ctx, key, name);
|
||||
};
|
||||
|
||||
// Symbol resolution involving symbol versioning is tricky because one
|
||||
// symbol can be resolved with two different identifiers. Among
|
||||
// symbols with the same name but different versions, one of them is
|
||||
// always marked as the "default" one. This symbol is often denoted
|
||||
// with two atsigns as `foo@@VERSION` and can be referred to either
|
||||
// as `foo` or `foo@VERSION`. No other symbols have two names like that.
|
||||
//
|
||||
// On contrary, a versioned non-default symbol can be referred only
|
||||
// with an explicit version suffix, e.g., `foo@VERSION`.
|
||||
//
|
||||
// Here is how we resolve versioned default symbols. We resolve `foo`
|
||||
// and `foo@VERSION` as usual, but with information to forward
|
||||
// references to `foo@VERSION` to `foo`. After name resolution, we
|
||||
// visit all symbol references to redirect `foo@VERSION` to `foo`.
|
||||
if (vers.empty() || ver == VER_NDX_GLOBAL) {
|
||||
// Unversioned symbol
|
||||
if (is_default) {
|
||||
this->symbols.push_back(get_symbol(ctx, name));
|
||||
this->symbols2.push_back(nullptr);
|
||||
} else if (vers[i] & VERSYM_HIDDEN) {
|
||||
// Versioned non-default symbol
|
||||
this->symbols.push_back(get_versioned_sym());
|
||||
this->symbols2.push_back(nullptr);
|
||||
} else {
|
||||
// Versioned default symbol
|
||||
this->symbols.push_back(get_symbol(ctx, name));
|
||||
this->symbols2.push_back(get_versioned_sym());
|
||||
std::string_view mangled_name = save_string(
|
||||
ctx, std::string(name) + "@" + std::string(version_strings[ver]));
|
||||
this->symbols.push_back(get_symbol(ctx, mangled_name, name));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1422,22 +1400,6 @@ void SharedFile<E>::resolve_symbols(Context<E> &ctx) {
|
||||
sym.sym_idx = i;
|
||||
sym.ver_idx = versyms[i];
|
||||
sym.is_weak = true;
|
||||
sym.is_versioned_default = false;
|
||||
}
|
||||
|
||||
// A symbol with the default version is a special case because, unlike
|
||||
// other symbols, the symbol can be referred by two names, `foo` and
|
||||
// `foo@VERSION`. Here, we resolve `foo@VERSOIN` as a proxy of `foo`.
|
||||
Symbol<E> *sym2 = this->symbols2[i];
|
||||
if (sym2 && sym2 != &sym) {
|
||||
std::scoped_lock lock2(sym2->mu);
|
||||
|
||||
if (get_rank(this, esym, false) < get_rank(*sym2)) {
|
||||
sym2->file = this;
|
||||
sym2->origin = (uintptr_t)&sym;
|
||||
sym2->sym_idx = i;
|
||||
sym2->is_versioned_default = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+24
-38
@@ -63,16 +63,16 @@ void InputSection<E>::uncompress(Context<E> &ctx) {
|
||||
return;
|
||||
|
||||
u8 *buf = new u8[sh_size];
|
||||
copy_contents_to(ctx, buf, sh_size);
|
||||
copy_contents(ctx, buf);
|
||||
contents = std::string_view((char *)buf, sh_size);
|
||||
ctx.string_pool.emplace_back(buf);
|
||||
uncompressed = true;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void InputSection<E>::copy_contents_to(Context<E> &ctx, u8 *buf, i64 sz) {
|
||||
void InputSection<E>::copy_contents(Context<E> &ctx, u8 *buf) {
|
||||
if (!(shdr().sh_flags & SHF_COMPRESSED) || uncompressed) {
|
||||
memcpy(buf, contents.data(), sz);
|
||||
memcpy(buf, contents.data(), contents.size());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -84,35 +84,16 @@ void InputSection<E>::copy_contents_to(Context<E> &ctx, u8 *buf, i64 sz) {
|
||||
|
||||
switch (hdr.ch_type) {
|
||||
case ELFCOMPRESS_ZLIB: {
|
||||
z_stream s = {};
|
||||
inflateInit(&s);
|
||||
s.next_in = (u8 *)data.data();
|
||||
s.avail_in = data.size();
|
||||
s.next_out = buf;
|
||||
s.avail_out = sz;
|
||||
|
||||
int r;
|
||||
while (s.total_out < sz && (r = inflate(&s, Z_NO_FLUSH)) == Z_OK);
|
||||
if (s.total_out < sz && r != Z_STREAM_END)
|
||||
Fatal(ctx) << *this << ": uncompress failed: " << s.msg;
|
||||
inflateEnd(&s);
|
||||
unsigned long size = sh_size;
|
||||
if (::uncompress(buf, &size, (u8 *)data.data(), data.size()) != Z_OK)
|
||||
Fatal(ctx) << *this << ": uncompress failed";
|
||||
assert(size == sh_size);
|
||||
break;
|
||||
}
|
||||
case ELFCOMPRESS_ZSTD: {
|
||||
ZSTD_DCtx *dctx = ZSTD_createDCtx();
|
||||
ZSTD_inBuffer in = { data.data(), data.size() };
|
||||
ZSTD_outBuffer out = { buf, (size_t)sz };
|
||||
|
||||
while (out.pos < out.size) {
|
||||
size_t r = ZSTD_decompressStream(dctx, &out, &in);
|
||||
if (ZSTD_isError(r))
|
||||
Fatal(ctx) << *this << ": uncompress failed: " << ZSTD_getErrorName(r);
|
||||
if (r == 0 && out.pos < out.size)
|
||||
Fatal(ctx) << *this << ": uncompress failed: premature end of input";
|
||||
}
|
||||
ZSTD_freeDCtx(dctx);
|
||||
case ELFCOMPRESS_ZSTD:
|
||||
if (ZSTD_decompress(buf, sh_size, (u8 *)data.data(), data.size()) != sh_size)
|
||||
Fatal(ctx) << *this << ": ZSTD_decompress failed";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Fatal(ctx) << *this << ": unsupported compression type: 0x"
|
||||
<< std::hex << hdr.ch_type;
|
||||
@@ -133,6 +114,12 @@ static void do_action(Context<E> &ctx, Action action, InputSection<E> &isec,
|
||||
<< sym << "' can not be used; recompile with -fPIC";
|
||||
break;
|
||||
case COPYREL:
|
||||
// Create a copy relocation
|
||||
if (!sym.file->is_dso) {
|
||||
assert(sym.esym().is_undef_weak());
|
||||
Error(ctx) << isec << ": cannot create a copy relocation for "
|
||||
<< sym <<"; recompile with -fPIE or -fPIC";
|
||||
}
|
||||
sym.flags |= NEEDS_COPYREL;
|
||||
break;
|
||||
case PLT:
|
||||
@@ -247,7 +234,7 @@ void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
|
||||
|
||||
if (deltas.empty()) {
|
||||
// If a section is not relaxed, we can copy it as a one big chunk.
|
||||
copy_contents_to(ctx, buf, sh_size);
|
||||
copy_contents(ctx, buf);
|
||||
} else {
|
||||
// A relaxed section is copied piece-wise.
|
||||
memcpy(buf, contents.data(), deltas[0].offset);
|
||||
@@ -263,7 +250,7 @@ void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
copy_contents_to(ctx, buf, sh_size);
|
||||
copy_contents(ctx, buf);
|
||||
}
|
||||
|
||||
// Apply relocations
|
||||
@@ -356,8 +343,8 @@ bool InputSection<E>::record_undef_error(Context<E> &ctx, const ElfRel<E> &rel)
|
||||
template <typename E>
|
||||
MergeableSection<E>::MergeableSection(Context<E> &ctx, MergedSection<E> &parent,
|
||||
std::unique_ptr<InputSection<E>> &isec)
|
||||
: parent(parent), p2align(isec->p2align), input_section(std::move(isec)) {
|
||||
input_section->uncompress(ctx);
|
||||
: parent(parent), p2align(isec->p2align), section(std::move(isec)) {
|
||||
section->uncompress(ctx);
|
||||
|
||||
std::scoped_lock lock(parent.mu);
|
||||
parent.members.push_back(this);
|
||||
@@ -393,9 +380,9 @@ static size_t find_null(std::string_view data, i64 pos, i64 entsize) {
|
||||
// We do not support mergeable sections that have relocations.
|
||||
template <typename E>
|
||||
void MergeableSection<E>::split_contents(Context<E> &ctx) {
|
||||
std::string_view data = input_section->contents;
|
||||
std::string_view data = section->contents;
|
||||
if (data.size() > UINT32_MAX)
|
||||
Fatal(ctx) << *input_section << ": mergeable section too large";
|
||||
Fatal(ctx) << *section << ": mergeable section too large";
|
||||
|
||||
i64 entsize = parent.shdr.sh_entsize;
|
||||
|
||||
@@ -405,13 +392,12 @@ void MergeableSection<E>::split_contents(Context<E> &ctx) {
|
||||
frag_offsets.push_back(pos);
|
||||
size_t end = find_null(data, pos, entsize);
|
||||
if (end == data.npos)
|
||||
Fatal(ctx) << *input_section << ": string is not null terminated";
|
||||
Fatal(ctx) << *section << ": string is not null terminated";
|
||||
pos = end + entsize;
|
||||
}
|
||||
} else {
|
||||
if (data.size() % entsize)
|
||||
Fatal(ctx) << *input_section
|
||||
<< ": section size is not multiple of sh_entsize";
|
||||
Fatal(ctx) << *section << ": section size is not multiple of sh_entsize";
|
||||
frag_offsets.reserve(data.size() / entsize);
|
||||
|
||||
for (i64 pos = 0; pos < data.size(); pos += entsize)
|
||||
|
||||
+9
-11
@@ -29,8 +29,8 @@
|
||||
//
|
||||
// 4. Continue the rest of the linking process as usual.
|
||||
//
|
||||
// When gcc or clang inovkes ld, they pass `-plugin /path/to/linker-plugin.so`
|
||||
// to the linker. The given .so file provides a way to call the compiler
|
||||
// When gcc or clang inovkes ld, they pass `-plugin linker-plugin.so` to
|
||||
// the linker. The given .so file provides a way to call the compiler
|
||||
// backend.
|
||||
//
|
||||
// The linker plugin API is documented at
|
||||
@@ -425,7 +425,7 @@ get_api_version(const char *plugin_identifier,
|
||||
if (LAPI_V1 < minimal_api_supported)
|
||||
Fatal(*gctx<E>) << "LTO plugin does not support V0 or V1 API";
|
||||
|
||||
std::string version = mold_version + "\0"s;
|
||||
std::string version = get_mold_version() + "\0"s;
|
||||
|
||||
*linker_identifier = "mold";
|
||||
*linker_version = version.data();
|
||||
@@ -600,10 +600,10 @@ create_plugin_input_file(Context<E> &ctx, MappedFile *mf) {
|
||||
template <typename E>
|
||||
ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile *mf) {
|
||||
if (ctx.arg.plugin.empty())
|
||||
Fatal(ctx) << mf->name << ": unable to handle this LTO object file because "
|
||||
<< "the -plugin option was not provided. Please make sure you "
|
||||
<< "added -flto not only when creating object files but also "
|
||||
<< "when linking the final executable.";
|
||||
Fatal(ctx) << mf->name << ": don't know how to handle this LTO object file "
|
||||
<< "because no -plugin option was given. Please make sure you "
|
||||
<< "added -flto not only for creating object files but also for "
|
||||
<< "creating the final executable.";
|
||||
|
||||
load_lto_plugin(ctx);
|
||||
|
||||
@@ -670,10 +670,8 @@ ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile *mf) {
|
||||
// section. We handle such symbols differently than comdat symbols in
|
||||
// a regular file because, unlike regular object files, IR files don't
|
||||
// have input sections.
|
||||
if (psym.comdat_key) {
|
||||
std::string_view key = save_string(ctx, psym.comdat_key);
|
||||
obj->lto_comdat_groups[i + 1] = insert_comdat_group(ctx, key);
|
||||
}
|
||||
if (psym.comdat_key)
|
||||
obj->lto_comdat_groups[i + 1] = insert_comdat_group(ctx, psym.comdat_key);
|
||||
}
|
||||
|
||||
obj->symbol_strtab = save_string(ctx, strtab);
|
||||
|
||||
+23
-25
@@ -15,6 +15,13 @@
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef MOLD_X86_64
|
||||
int main(int argc, char **argv) {
|
||||
mold::set_mimalloc_options();
|
||||
return mold::mold_main<mold::X86_64>(argc, argv);
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace mold {
|
||||
|
||||
template <typename E>
|
||||
@@ -37,8 +44,8 @@ static ObjectFile<E> *new_object_file(Context<E> &ctx, ReaderContext &rctx,
|
||||
ObjectFile<E> *file = new ObjectFile<E>(ctx, mf, archive_name);
|
||||
ctx.obj_pool.emplace_back(file);
|
||||
file->priority = ctx.file_priority++;
|
||||
file->as_needed =
|
||||
rctx.in_lib || (!archive_name.empty() && !rctx.whole_archive);
|
||||
file->is_reachable =
|
||||
!rctx.in_lib && (archive_name.empty() || rctx.whole_archive);
|
||||
|
||||
rctx.tg->run([file, &ctx] { file->parse(ctx); });
|
||||
if (ctx.arg.trace)
|
||||
@@ -58,8 +65,8 @@ static ObjectFile<E> *new_lto_obj(Context<E> &ctx, ReaderContext &rctx,
|
||||
ObjectFile<E> *file = read_lto_object(ctx, mf);
|
||||
file->priority = ctx.file_priority++;
|
||||
file->archive_name = archive_name;
|
||||
file->as_needed =
|
||||
rctx.in_lib || (!archive_name.empty() && !rctx.whole_archive);
|
||||
file->is_reachable =
|
||||
!rctx.in_lib && (archive_name.empty() || rctx.whole_archive);
|
||||
|
||||
if (ctx.arg.trace)
|
||||
Out(ctx) << "trace: " << *file;
|
||||
@@ -74,7 +81,7 @@ new_shared_file(Context<E> &ctx, ReaderContext &rctx, MappedFile *mf) {
|
||||
SharedFile<E> *file = new SharedFile<E>(ctx, mf);
|
||||
ctx.dso_pool.emplace_back(file);
|
||||
file->priority = ctx.file_priority++;
|
||||
file->as_needed = rctx.as_needed;
|
||||
file->is_reachable = !rctx.as_needed;
|
||||
|
||||
rctx.tg->run([file, &ctx] { file->parse(ctx); });
|
||||
if (ctx.arg.trace)
|
||||
@@ -489,8 +496,7 @@ int mold_main(int argc, char **argv) {
|
||||
// create as few segments as possible.
|
||||
sort_output_sections(ctx);
|
||||
|
||||
// Handle --separate-debug-file.
|
||||
if (ctx.gnu_debuglink)
|
||||
if (!ctx.arg.separate_debug_file.empty())
|
||||
separate_debug_sections(ctx);
|
||||
|
||||
// Compute sizes of output sections while assigning offsets
|
||||
@@ -508,10 +514,6 @@ int mold_main(int argc, char **argv) {
|
||||
// be added to .dynsym.
|
||||
sort_dynsyms(ctx);
|
||||
|
||||
// Sort .debug_info contents so that DWARF32 debug info precedes that of
|
||||
// DWARF64. This is to mitigate the possibility of a relocation overflow.
|
||||
sort_debug_info_sections(ctx);
|
||||
|
||||
// Print reports about undefined symbols, if needed.
|
||||
if (ctx.arg.unresolved_symbols == UNRESOLVED_ERROR)
|
||||
report_undef_errors(ctx);
|
||||
@@ -534,6 +536,13 @@ int mold_main(int argc, char **argv) {
|
||||
if (ctx.arg.emit_relocs)
|
||||
create_reloc_sections(ctx);
|
||||
|
||||
// We've created range extension thunks with a pessimistive assumption
|
||||
// that all out-of-section references are out of range. Now that we are
|
||||
// able to assign addresses to all SHF_ALLOC output sections, we can
|
||||
// eliminate excessive thunks.
|
||||
if constexpr (needs_thunk<E>)
|
||||
remove_redundant_thunks(ctx);
|
||||
|
||||
// Compute .symtab and .strtab sizes for each file.
|
||||
if (!ctx.arg.strip_all)
|
||||
create_output_symtab(ctx);
|
||||
@@ -553,14 +562,6 @@ int mold_main(int argc, char **argv) {
|
||||
filesize = set_osec_offsets(ctx);
|
||||
}
|
||||
|
||||
// We've created range extension thunks with a pessimistive assumption
|
||||
// that all out-of-section references are out of range. Now that we know
|
||||
// the addresses of all sections,, we can eliminate excessive thunks.
|
||||
if constexpr (needs_thunk<E>) {
|
||||
remove_redundant_thunks(ctx);
|
||||
filesize = set_osec_offsets(ctx);
|
||||
}
|
||||
|
||||
if constexpr (is_arm32<E>) {
|
||||
if (ctx.extra.exidx) {
|
||||
ctx.extra.exidx->remove_duplicate_entries(ctx);
|
||||
@@ -600,9 +601,6 @@ int mold_main(int argc, char **argv) {
|
||||
// Copy input sections to the output file and apply relocations.
|
||||
copy_chunks(ctx);
|
||||
|
||||
if constexpr (is_arm32be<E>)
|
||||
arm32be_swap_bytes(ctx);
|
||||
|
||||
if constexpr (is_x86_64<E>)
|
||||
if (ctx.arg.z_rewrite_endbr)
|
||||
rewrite_endbr(ctx);
|
||||
@@ -614,7 +612,7 @@ int mold_main(int argc, char **argv) {
|
||||
// .gdb_index's contents cannot be constructed before applying
|
||||
// relocations to other debug sections. We have relocated debug
|
||||
// sections now, so write the .gdb_index section.
|
||||
if (ctx.gdb_index && !ctx.gnu_debuglink)
|
||||
if (ctx.gdb_index && ctx.arg.separate_debug_file.empty())
|
||||
write_gdb_index(ctx);
|
||||
|
||||
// .note.gnu.build-id section contains a cryptographic hash of the
|
||||
@@ -623,7 +621,7 @@ int mold_main(int argc, char **argv) {
|
||||
if (ctx.buildid)
|
||||
write_build_id(ctx);
|
||||
|
||||
if (ctx.gnu_debuglink)
|
||||
if (!ctx.arg.separate_debug_file.empty())
|
||||
write_gnu_debuglink(ctx);
|
||||
|
||||
t_copy.stop();
|
||||
@@ -644,7 +642,7 @@ int mold_main(int argc, char **argv) {
|
||||
if (ctx.arg.print_map)
|
||||
print_map(ctx);
|
||||
|
||||
if (ctx.gnu_debuglink)
|
||||
if (!ctx.arg.separate_debug_file.empty())
|
||||
write_separate_debug_file(ctx);
|
||||
|
||||
// Show stats numbers
|
||||
|
||||
+8
-6
@@ -19,12 +19,14 @@ static Map<E> get_map(Context<E> &ctx) {
|
||||
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
for (Symbol<E> *sym : file->symbols) {
|
||||
if (sym->file == file && sym->get_type() != STT_SECTION) {
|
||||
if (InputSection<E> *isec = sym->get_input_section()) {
|
||||
typename Map<E>::accessor acc;
|
||||
map.insert(acc, {isec, {}});
|
||||
acc->second.push_back(sym);
|
||||
}
|
||||
if (sym->file != file || sym->get_type() == STT_SECTION)
|
||||
continue;
|
||||
|
||||
if (InputSection<E> *isec = sym->get_input_section()) {
|
||||
assert(file == &isec->file);
|
||||
typename Map<E>::accessor acc;
|
||||
map.insert(acc, {isec, {}});
|
||||
acc->second.push_back(sym);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
+36
-64
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "../lib/common.h"
|
||||
#include "elf.h"
|
||||
#include "mold-git-hash.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
@@ -50,7 +49,7 @@ struct ReaderContext;
|
||||
template <typename E>
|
||||
std::ostream &operator<<(std::ostream &out, const Symbol<E> &sym);
|
||||
|
||||
extern std::string mold_version;
|
||||
std::string get_mold_version();
|
||||
|
||||
//
|
||||
// Mergeable section fragments
|
||||
@@ -66,13 +65,9 @@ struct __attribute__((aligned(4))) SectionFragment {
|
||||
}
|
||||
|
||||
MergedSection<E> &output_section;
|
||||
i64 offset = -1;
|
||||
u32 offset = -1;
|
||||
Atomic<u8> p2align = 0;
|
||||
Atomic<bool> is_alive = false;
|
||||
|
||||
// True if this fragment must be placed within 2^32 bytes from the
|
||||
// start of the output section.
|
||||
Atomic<bool> is_32bit = false;
|
||||
};
|
||||
|
||||
// Additional class members for dynamic symbols. Because most symbols
|
||||
@@ -267,9 +262,9 @@ struct FdeRecord {
|
||||
template <typename E>
|
||||
struct InputSectionExtras {};
|
||||
|
||||
template <is_arm32 E>
|
||||
struct InputSectionExtras<E> {
|
||||
InputSection<E> *exidx = nullptr;
|
||||
template <>
|
||||
struct InputSectionExtras<ARM32> {
|
||||
InputSection<ARM32> *exidx = nullptr;
|
||||
};
|
||||
|
||||
struct RelocDelta {
|
||||
@@ -299,7 +294,7 @@ public:
|
||||
InputSection(Context<E> &ctx, ObjectFile<E> &file, i64 shndx);
|
||||
|
||||
void uncompress(Context<E> &ctx);
|
||||
void copy_contents_to(Context<E> &ctx, u8 *buf, i64 sz);
|
||||
void copy_contents(Context<E> &ctx, u8 *buf);
|
||||
void scan_relocations(Context<E> &ctx);
|
||||
void write_to(Context<E> &ctx, u8 *buf);
|
||||
void apply_reloc_alloc(Context<E> &ctx, u8 *base);
|
||||
@@ -460,8 +455,8 @@ public:
|
||||
// For example, range extension thunks adds function_name@thunk
|
||||
// symbol for each thunk entry. The following members are used
|
||||
// for such synthesizing symbols.
|
||||
virtual void compute_symtab_size(Context<E> &ctx) {}
|
||||
virtual void populate_symtab(Context<E> &ctx) {}
|
||||
virtual void compute_symtab_size(Context<E> &ctx) {};
|
||||
virtual void populate_symtab(Context<E> &ctx) {};
|
||||
|
||||
i64 local_symtab_idx = 0;
|
||||
i64 num_local_symtab = 0;
|
||||
@@ -575,7 +570,7 @@ public:
|
||||
void populate_symtab(Context<E> &ctx) override;
|
||||
|
||||
void scan_abs_relocations(Context<E> &ctx);
|
||||
void create_range_extension_thunks(Context<E> &ctx);
|
||||
void create_range_extension_thunks(Context<E> &ctx, bool first_pass);
|
||||
|
||||
std::vector<InputSection<E> *> members;
|
||||
std::vector<std::unique_ptr<Thunk<E>>> thunks;
|
||||
@@ -1306,11 +1301,11 @@ public:
|
||||
std::string_view get_contents(i64 idx);
|
||||
|
||||
MergedSection<E> &parent;
|
||||
u8 p2align = 0;
|
||||
std::unique_ptr<InputSection<E>> input_section;
|
||||
std::vector<SectionFragment<E> *> fragments;
|
||||
u8 p2align = 0;
|
||||
|
||||
private:
|
||||
std::unique_ptr<InputSection<E>> section;
|
||||
std::vector<u32> frag_offsets;
|
||||
std::vector<u32> hashes;
|
||||
};
|
||||
@@ -1342,7 +1337,6 @@ public:
|
||||
mark_live_objects(Context<E> &ctx,
|
||||
std::function<void(InputFile<E> *)> feeder) = 0;
|
||||
|
||||
std::span<Symbol<E> *> get_local_syms();
|
||||
std::span<Symbol<E> *> get_global_syms();
|
||||
std::string_view get_source_name() const;
|
||||
|
||||
@@ -1359,7 +1353,6 @@ public:
|
||||
std::string_view shstrtab;
|
||||
std::string_view symbol_strtab;
|
||||
|
||||
bool as_needed = false;
|
||||
bool has_init_array = false;
|
||||
bool has_ctors = false;
|
||||
|
||||
@@ -1436,7 +1429,6 @@ public:
|
||||
bool is_lto_obj = false;
|
||||
bool is_gcc_offload_obj = false;
|
||||
bool is_rust_obj = false;
|
||||
bool is_dwarf32 = false;
|
||||
|
||||
i64 fde_idx = 0;
|
||||
i64 fde_offset = 0;
|
||||
@@ -1495,7 +1487,6 @@ public:
|
||||
|
||||
std::string soname;
|
||||
std::vector<std::string_view> version_strings;
|
||||
std::vector<Symbol<E> *> symbols2;
|
||||
std::vector<ElfSym<E>> elf_syms2;
|
||||
|
||||
private:
|
||||
@@ -1696,7 +1687,6 @@ template <typename E> void scan_relocations(Context<E> &);
|
||||
template <typename E> void compute_imported_symbol_weakness(Context<E> &);
|
||||
template <typename E> void construct_relr(Context<E> &);
|
||||
template <typename E> void sort_dynsyms(Context<E> &);
|
||||
template <typename E> void sort_debug_info_sections(Context<E> &);
|
||||
template <typename E> void create_output_symtab(Context<E> &);
|
||||
template <typename E> void report_undef_errors(Context<E> &);
|
||||
template <typename E> void create_reloc_sections(Context<E> &);
|
||||
@@ -1727,34 +1717,28 @@ void rewrite_endbr(Context<X86_64> &ctx);
|
||||
// arch-arm32.cc
|
||||
//
|
||||
|
||||
template <is_arm32 E>
|
||||
class Arm32ExidxSection : public Chunk<E> {
|
||||
class Arm32ExidxSection : public Chunk<ARM32> {
|
||||
public:
|
||||
Arm32ExidxSection(OutputSection<E> &osec) : output_section(osec) {
|
||||
Arm32ExidxSection(OutputSection<ARM32> &osec) : output_section(osec) {
|
||||
this->name = ".ARM.exidx";
|
||||
this->shdr.sh_type = SHT_ARM_EXIDX;
|
||||
this->shdr.sh_flags = SHF_ALLOC;
|
||||
this->shdr.sh_addralign = 4;
|
||||
}
|
||||
|
||||
void compute_section_size(Context<E> &ctx) override;
|
||||
void update_shdr(Context<E> &ctx) override;
|
||||
void remove_duplicate_entries(Context<E> &ctx);
|
||||
void copy_buf(Context<E> &ctx) override;
|
||||
void compute_section_size(Context<ARM32> &ctx) override;
|
||||
void update_shdr(Context<ARM32> &ctx) override;
|
||||
void remove_duplicate_entries(Context<ARM32> &ctx);
|
||||
void copy_buf(Context<ARM32> &ctx) override;
|
||||
|
||||
private:
|
||||
std::vector<u8> get_contents(Context<E> &ctx);
|
||||
std::vector<u8> get_contents(Context<ARM32> &ctx);
|
||||
|
||||
OutputSection<E> &output_section;
|
||||
OutputSection<ARM32> &output_section;
|
||||
};
|
||||
|
||||
template <is_arm32 E>
|
||||
u64 get_eflags(Context<E> &ctx);
|
||||
|
||||
template <is_arm32 E>
|
||||
void create_arm_exidx_section(Context<E> &ctx);
|
||||
|
||||
void arm32be_swap_bytes(Context<ARM32BE> &ctx);
|
||||
template <> u64 get_eflags(Context<ARM32> &ctx);
|
||||
void create_arm_exidx_section(Context<ARM32> &ctx);
|
||||
|
||||
//
|
||||
// arch-riscv.cc
|
||||
@@ -1824,6 +1808,12 @@ public:
|
||||
|
||||
template <> u64 get_eflags(Context<PPC64V2> &ctx);
|
||||
|
||||
//
|
||||
// arch-arc.cc
|
||||
//
|
||||
|
||||
template <> u64 get_eflags(Context<ARC> &ctx);
|
||||
|
||||
//
|
||||
// main.cc
|
||||
//
|
||||
@@ -1904,9 +1894,9 @@ struct ContextExtras<E> {
|
||||
NotePropertySection<E> *note_property = nullptr;
|
||||
};
|
||||
|
||||
template <is_arm32 E>
|
||||
struct ContextExtras<E> {
|
||||
Arm32ExidxSection<E> *exidx = nullptr;
|
||||
template <>
|
||||
struct ContextExtras<ARM32> {
|
||||
Arm32ExidxSection *exidx = nullptr;
|
||||
};
|
||||
|
||||
template <is_riscv E>
|
||||
@@ -1944,7 +1934,7 @@ struct ContextExtras<SPARC64> {
|
||||
template <typename E>
|
||||
struct Context {
|
||||
Context() {
|
||||
arg.entry = get_symbol(*this, "_start");
|
||||
arg.entry = get_symbol(*this, is_arc<E> ? "__start" : "_start");
|
||||
arg.fini = get_symbol(*this, "_fini");
|
||||
arg.init = get_symbol(*this, "_init");
|
||||
|
||||
@@ -1977,7 +1967,6 @@ struct Context {
|
||||
bool allow_multiple_definition = false;
|
||||
bool allow_shlib_undefined = true;
|
||||
bool apply_dynamic_relocs = true;
|
||||
bool be8 = false;
|
||||
bool color_diagnostics = false;
|
||||
bool default_symver = false;
|
||||
bool demangle = true;
|
||||
@@ -2403,9 +2392,6 @@ public:
|
||||
bool is_traced : 1 = false; // for --trace-symbol
|
||||
bool is_wrapped : 1 = false; // for --wrap
|
||||
|
||||
// For symbols with default symbol version, e.g. foo@@VERSION.
|
||||
bool is_versioned_default : 1 = false;
|
||||
|
||||
// If a symbol can be resolved to a symbol in a different ELF file at
|
||||
// runtime, `is_imported` is true. If a symbol is a dynamic symbol and
|
||||
// can be used by other ELF file at runtime, `is_exported` is true.
|
||||
@@ -2709,8 +2695,8 @@ template <typename E>
|
||||
std::string_view MergeableSection<E>::get_contents(i64 i) {
|
||||
i64 cur = frag_offsets[i];
|
||||
if (i == frag_offsets.size() - 1)
|
||||
return input_section->contents.substr(cur);
|
||||
return input_section->contents.substr(cur, frag_offsets[i + 1] - cur);
|
||||
return section->contents.substr(cur);
|
||||
return section->contents.substr(cur, frag_offsets[i + 1] - cur);
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -2748,11 +2734,6 @@ inline std::string_view InputFile<E>::get_string(Context<E> &ctx, i64 idx) {
|
||||
return this->get_string(ctx, elf_sections[idx]);
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
inline std::span<Symbol<E> *> InputFile<E>::get_local_syms() {
|
||||
return std::span<Symbol<E> *>(this->symbols).subspan(0, this->first_global);
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
inline std::span<Symbol<E> *> InputFile<E>::get_global_syms() {
|
||||
return std::span<Symbol<E> *>(this->symbols).subspan(this->first_global);
|
||||
@@ -3040,16 +3021,12 @@ template <typename E>
|
||||
u64
|
||||
Symbol<E>::get_thunk_addr(Context<E> &ctx, u64 P) const requires needs_thunk<E> {
|
||||
std::span<u64> vec = ctx.symbol_aux[aux_idx].thunk_addrs;
|
||||
|
||||
u64 lo = (P < branch_distance<E>) ? 0 : P - branch_distance<E>;
|
||||
auto it = ranges::lower_bound(vec, lo);
|
||||
if (it == vec.end())
|
||||
Fatal(ctx) << "range extension thunk out of range: " << *this;
|
||||
|
||||
i64 disp = *it - P;
|
||||
u64 val = *ranges::lower_bound(vec, lo);
|
||||
i64 disp = val - P;
|
||||
if (disp < -branch_distance<E> || branch_distance<E> <= disp)
|
||||
Fatal(ctx) << "range extension thunk out of range: " << *this;
|
||||
return *it;
|
||||
return val;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -3059,11 +3036,6 @@ inline bool Symbol<E>::has_plt(Context<E> &ctx) const {
|
||||
|
||||
template <typename E>
|
||||
inline bool Symbol<E>::is_absolute() const {
|
||||
// An unresolved weak symbol acts as if it were an absolute address
|
||||
// at address 0
|
||||
if (is_remaining_undef_weak())
|
||||
return true;
|
||||
|
||||
return !is_imported && !get_frag() && !get_input_section() &&
|
||||
!get_output_section();
|
||||
}
|
||||
|
||||
+44
-62
@@ -834,6 +834,20 @@ void DynamicSection<E>::copy_buf(Context<E> &ctx) {
|
||||
write_vector(ctx.buf + this->shdr.sh_offset, contents);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static std::vector<std::span<T>> split(std::vector<T> &input, i64 unit) {
|
||||
std::span<T> span(input);
|
||||
std::vector<std::span<T>> vec;
|
||||
|
||||
while (span.size() >= unit) {
|
||||
vec.push_back(span.subspan(0, unit));
|
||||
span = span.subspan(unit);
|
||||
}
|
||||
if (!span.empty())
|
||||
vec.push_back(span);
|
||||
return vec;
|
||||
}
|
||||
|
||||
// Assign offsets to OutputSection members
|
||||
template <typename E>
|
||||
void OutputSection<E>::compute_section_size(Context<E> &ctx) {
|
||||
@@ -1162,9 +1176,6 @@ void OutputSection<E>::populate_symtab(Context<E> &ctx) {
|
||||
u8 *strtab_base = ctx.buf + ctx.strtab->shdr.sh_offset;
|
||||
u8 *strtab = strtab_base + this->strtab_offset;
|
||||
|
||||
memset(esym, 0, this->num_local_symtab * sizeof(ElfSym<E>));
|
||||
memset(strtab, 0, this->strtab_size);
|
||||
|
||||
auto write_esym = [&](u64 addr, i64 st_name) {
|
||||
memset(esym, 0, sizeof(*esym));
|
||||
esym->st_name = st_name;
|
||||
@@ -1304,16 +1315,18 @@ static std::vector<GotEntry<E>> get_got_entries(Context<E> &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
// If a symbol is imported, let the dynamic linker to resolve it.
|
||||
if (sym->is_imported) {
|
||||
// If a symbol is imported, let the dynamic linker to resolve it.
|
||||
add({idx, 0, E::R_GLOB_DAT, sym});
|
||||
} else if (ctx.arg.pic && sym->is_relative()) {
|
||||
// We know the symbol's address, but it needs a base relocation.
|
||||
add({idx, sym->get_addr(ctx, NO_PLT), E::R_RELATIVE});
|
||||
} else {
|
||||
// We know the symbol's exact run-time address at link-time.
|
||||
add({idx, sym->get_addr(ctx, NO_PLT)});
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we know an address at link-time, fill that GOT entry now.
|
||||
// It may need a base relocation, though.
|
||||
if (ctx.arg.pic && sym->is_relative())
|
||||
add({idx, sym->get_addr(ctx, NO_PLT), E::R_RELATIVE});
|
||||
else
|
||||
add({idx, sym->get_addr(ctx, NO_PLT)});
|
||||
}
|
||||
|
||||
// Create GOT entries for TLVs.
|
||||
@@ -2093,7 +2106,7 @@ static void add_comment_strings(Context<E> &ctx) {
|
||||
};
|
||||
|
||||
// Add an identification string to .comment.
|
||||
add(mold_version);
|
||||
add(get_mold_version());
|
||||
|
||||
// Embed command line arguments for debugging.
|
||||
char *env = getenv("MOLD_DEBUG");
|
||||
@@ -2131,54 +2144,43 @@ void MergedSection<E>::compute_section_size(Context<E> &ctx) {
|
||||
if (!resolved)
|
||||
resolve(ctx);
|
||||
|
||||
std::vector<i64> sizes(map.NUM_SHARDS * 2);
|
||||
std::vector<i64> sizes(map.NUM_SHARDS);
|
||||
|
||||
tbb::parallel_for((i64)0, map.NUM_SHARDS, [&](i64 i) {
|
||||
using Entry = typename decltype(map)::Entry;
|
||||
std::vector<Entry *> entries = map.get_sorted_entries(i);
|
||||
|
||||
i64 off1 = 0;
|
||||
i64 off2 = 0;
|
||||
i64 offset = 0;
|
||||
|
||||
for (Entry *ent : entries) {
|
||||
SectionFragment<E> &frag = ent->value;
|
||||
if (frag.is_alive) {
|
||||
if (frag.is_32bit) {
|
||||
off1 = align_to(off1, 1 << frag.p2align);
|
||||
frag.offset = off1;
|
||||
off1 += ent->keylen;
|
||||
} else {
|
||||
off2 = align_to(off2, 1 << frag.p2align);
|
||||
frag.offset = off2;
|
||||
off2 += ent->keylen;
|
||||
}
|
||||
offset = align_to(offset, 1 << frag.p2align);
|
||||
frag.offset = offset;
|
||||
offset += ent->keylen;
|
||||
}
|
||||
}
|
||||
|
||||
sizes[i] = off1;
|
||||
sizes[i + map.NUM_SHARDS] = off2;
|
||||
sizes[i] = offset;
|
||||
});
|
||||
|
||||
i64 shard_size = map.nbuckets / map.NUM_SHARDS;
|
||||
shard_offsets.resize(sizes.size() + 1);
|
||||
shard_offsets.resize(map.NUM_SHARDS + 1);
|
||||
|
||||
for (i64 i = 1; i < sizes.size() + 1; i++)
|
||||
for (i64 i = 1; i < map.NUM_SHARDS + 1; i++)
|
||||
shard_offsets[i] =
|
||||
align_to(shard_offsets[i - 1] + sizes[i - 1], this->shdr.sh_addralign);
|
||||
|
||||
this->shdr.sh_size = shard_offsets.back();
|
||||
|
||||
tbb::parallel_for((i64)1, map.NUM_SHARDS, [&](i64 i) {
|
||||
for (i64 j = shard_size * i; j < shard_size * (i + 1); j++) {
|
||||
SectionFragment<E> &frag = map.entries[j].value;
|
||||
if (frag.is_alive) {
|
||||
if (frag.is_32bit)
|
||||
frag.offset += shard_offsets[i];
|
||||
else
|
||||
frag.offset += shard_offsets[i + map.NUM_SHARDS];
|
||||
}
|
||||
if (frag.is_alive)
|
||||
frag.offset += shard_offsets[i];
|
||||
}
|
||||
});
|
||||
|
||||
this->shdr.sh_size = shard_offsets[map.NUM_SHARDS];
|
||||
|
||||
if (this->shdr.sh_size > UINT32_MAX)
|
||||
Fatal(ctx) << this->name << ": output section too large";
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -2194,11 +2196,8 @@ void MergedSection<E>::write_to(Context<E> &ctx, u8 *buf) {
|
||||
// There might be gaps between strings to satisfy alignment requirements.
|
||||
// If that's the case, we need to zero-clear them.
|
||||
if (this->shdr.sh_addralign > 1 &&
|
||||
this->shdr.sh_addralign != this->shdr.sh_entsize) {
|
||||
this->shdr.sh_addralign != this->shdr.sh_entsize)
|
||||
memset(buf + shard_offsets[i], 0, shard_offsets[i + 1] - shard_offsets[i]);
|
||||
i64 j = map.NUM_SHARDS + i;
|
||||
memset(buf + shard_offsets[j], 0, shard_offsets[j + 1] - shard_offsets[j]);
|
||||
}
|
||||
|
||||
// Copy strings
|
||||
for (i64 j = shard_size * i; j < shard_size * (i + 1); j++)
|
||||
@@ -2448,30 +2447,21 @@ void EhFrameRelocSection<E>::copy_buf(Context<E> &ctx) {
|
||||
|
||||
template <typename E>
|
||||
void CopyrelSection<E>::add_symbol(Context<E> &ctx, Symbol<E> *sym) {
|
||||
assert(!ctx.arg.shared);
|
||||
if (sym->has_copyrel)
|
||||
return;
|
||||
|
||||
if (!sym->file->is_dso) {
|
||||
assert(sym->esym().is_undef_weak());
|
||||
Error(ctx) << *sym->file << ": cannot create a copy relocation for "
|
||||
<< *sym <<"; recompile with -fPIE or -fPIC";
|
||||
return;
|
||||
}
|
||||
assert(!ctx.arg.shared);
|
||||
assert(sym->file->is_dso);
|
||||
|
||||
if (sym->esym().st_visibility == STV_PROTECTED) {
|
||||
if (sym->esym().st_visibility == STV_PROTECTED)
|
||||
Error(ctx) << *sym->file
|
||||
<< ": cannot create a copy relocation for protected symbol '"
|
||||
<< *sym << "'; recompile with -fPIC";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ctx.arg.z_copyreloc) {
|
||||
if (!ctx.arg.z_copyreloc)
|
||||
Error(ctx) << "-z nocopyreloc: " << *sym->file
|
||||
<< ": cannot create a copy relocation for symbol '" << *sym
|
||||
<< "'; recompile with -fPIC";
|
||||
return;
|
||||
}
|
||||
|
||||
symbols.push_back(sym);
|
||||
|
||||
@@ -2644,14 +2634,6 @@ void VerdefSection<E>::construct(Context<E> &ctx) {
|
||||
if (ctx.arg.version_definitions.empty())
|
||||
return;
|
||||
|
||||
// Handle --default-symver
|
||||
if (ctx.arg.default_symver)
|
||||
for (Symbol<E> *sym : ctx.dynsym->symbols)
|
||||
if (sym && !sym->file->is_dso)
|
||||
if (u16 ver = sym->ver_idx;
|
||||
ver == VER_NDX_GLOBAL || ver == VER_NDX_UNSPECIFIED)
|
||||
sym->ver_idx = VER_NDX_LAST_RESERVED + 1;
|
||||
|
||||
// Resize .gnu.version and write to it
|
||||
ctx.versym->contents.resize(ctx.dynsym->symbols.size(), VER_NDX_GLOBAL);
|
||||
ctx.versym->contents[0] = VER_NDX_LOCAL;
|
||||
|
||||
+135
-292
@@ -26,10 +26,8 @@ int redo_main(Context<E> &ctx, int argc, char **argv) {
|
||||
return mold_main<ARM64LE>(argc, argv);
|
||||
if (target == ARM64BE::name)
|
||||
return mold_main<ARM64BE>(argc, argv);
|
||||
if (target == ARM32LE::name)
|
||||
return mold_main<ARM32LE>(argc, argv);
|
||||
if (target == ARM32BE::name)
|
||||
return mold_main<ARM32BE>(argc, argv);
|
||||
if (target == ARM32::name)
|
||||
return mold_main<ARM32>(argc, argv);
|
||||
if (target == RV64LE::name)
|
||||
return mold_main<RV64LE>(argc, argv);
|
||||
if (target == RV64BE::name)
|
||||
@@ -58,6 +56,8 @@ int redo_main(Context<E> &ctx, int argc, char **argv) {
|
||||
return mold_main<LOONGARCH32>(argc, argv);
|
||||
if (target == LOONGARCH64::name)
|
||||
return mold_main<LOONGARCH64>(argc, argv);
|
||||
if (target == ARC::name)
|
||||
return mold_main<ARC>(argc, argv);
|
||||
abort();
|
||||
}
|
||||
|
||||
@@ -217,14 +217,14 @@ static void mark_live_objects(Context<E> &ctx) {
|
||||
}
|
||||
|
||||
std::vector<InputFile<E> *> roots;
|
||||
append(roots, ctx.objs);
|
||||
append(roots, ctx.dsos);
|
||||
|
||||
for (InputFile<E> *file : roots)
|
||||
if (!file->as_needed)
|
||||
file->is_reachable = true;
|
||||
for (InputFile<E> *file : ctx.objs)
|
||||
if (file->is_reachable)
|
||||
roots.push_back(file);
|
||||
|
||||
std::erase_if(roots, [](InputFile<E> *file) { return !file->is_reachable; });
|
||||
for (InputFile<E> *file : ctx.dsos)
|
||||
if (file->is_reachable)
|
||||
roots.push_back(file);
|
||||
|
||||
tbb::parallel_for_each(roots, [&](InputFile<E> *file,
|
||||
tbb::feeder<InputFile<E> *> &feeder) {
|
||||
@@ -249,15 +249,25 @@ static void mark_live_objects(Context<E> &ctx) {
|
||||
// `VER1`. If it does, that's the symbol we are looking for.
|
||||
template <typename E>
|
||||
static void resolve_default_symver(Context<E> &ctx) {
|
||||
std::vector<InputFile<E> *> files;
|
||||
append(files, ctx.objs);
|
||||
append(files, ctx.dsos);
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
for (i64 i = file->first_global; i < file->elf_syms.size(); i++) {
|
||||
const ElfSym<E> &esym = file->elf_syms[i];
|
||||
Symbol<E> &sym = *file->symbols[i];
|
||||
|
||||
tbb::parallel_for_each(files, [](InputFile<E> *file) {
|
||||
std::span<Symbol<E> *> syms = file->get_global_syms();
|
||||
for (i64 i = 0; i < syms.size(); i++)
|
||||
if (syms[i]->is_versioned_default)
|
||||
syms[i] = (Symbol<E> *)syms[i]->origin;
|
||||
if (!sym.file && esym.is_undef() &&
|
||||
file->has_symver[i - file->first_global]) {
|
||||
std::string_view str = file->symbol_strtab.data() + esym.st_name;
|
||||
i64 pos = str.find('@');
|
||||
assert(pos != str.npos);
|
||||
|
||||
std::string_view name = str.substr(0, pos);
|
||||
std::string_view ver = str.substr(pos + 1);
|
||||
|
||||
Symbol<E> *sym2 = get_symbol(ctx, name);
|
||||
if (sym2->file && sym2->file->is_dso && sym2->get_version() == ver)
|
||||
file->symbols[i] = sym2;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -302,68 +312,69 @@ void resolve_symbols(Context<E> &ctx) {
|
||||
resolve_default_symver(ctx);
|
||||
mark_live_objects(ctx);
|
||||
|
||||
// Now that we know the exact set of input files that are to be
|
||||
// included in the output file, we want to redo symbol resolution.
|
||||
// This is because symbols defined by object files in archive files
|
||||
// may have risen as a result of mark_live_objects().
|
||||
//
|
||||
// To redo symbol resolution, we want to clear the state first.
|
||||
clear_symbols(ctx);
|
||||
|
||||
// COMDAT elimination needs to happen exactly here.
|
||||
//
|
||||
// It needs to be after archive extraction, otherwise we might
|
||||
// assign COMDAT leader to an archive member that is not supposed to
|
||||
// be extracted.
|
||||
//
|
||||
// It needs to happen before the final symbol resolution, otherwise
|
||||
// we could eliminate a symbol that is already resolved to and cause
|
||||
// dangling references.
|
||||
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
|
||||
if (file->is_reachable) {
|
||||
for (ComdatGroupRef<E> &ref : file->comdat_groups)
|
||||
update_minimum(ref.group->owner, file->priority);
|
||||
for (ComdatGroup *g : file->lto_comdat_groups)
|
||||
if (g)
|
||||
update_minimum(g->owner, file->priority);
|
||||
}
|
||||
});
|
||||
|
||||
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
|
||||
if (file->is_reachable)
|
||||
for (ComdatGroupRef<E> &ref : file->comdat_groups)
|
||||
if (ref.group->owner != file->priority)
|
||||
for (u32 i : ref.members)
|
||||
if (InputSection<E> *isec = file->sections[i].get())
|
||||
isec->is_alive = false;
|
||||
});
|
||||
|
||||
// Redo symbol resolution
|
||||
tbb::parallel_for_each(files, [&](InputFile<E> *file) {
|
||||
if (file->is_reachable)
|
||||
file->resolve_symbols(ctx);
|
||||
});
|
||||
|
||||
// Symbols with hidden visibility need to be resolved within the
|
||||
// output file. If a hidden symbol was resolved to a DSO, we'll redo
|
||||
// symbol resolution from scratch with the flag to skip that symbol
|
||||
// next time. This should be rare.
|
||||
std::atomic_bool redo = false;
|
||||
std::atomic_bool flag = false;
|
||||
|
||||
tbb::parallel_for_each(ctx.dsos, [&](SharedFile<E> *file) {
|
||||
if (file->is_reachable) {
|
||||
for (Symbol<E> *sym : file->symbols) {
|
||||
if (sym->file == file && sym->visibility == STV_HIDDEN) {
|
||||
sym->skip_dso = true;
|
||||
redo = true;
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!redo)
|
||||
break;
|
||||
if (!flag)
|
||||
return;
|
||||
clear_symbols(ctx);
|
||||
}
|
||||
|
||||
// Now that we know the exact set of input files that are to be
|
||||
// included in the output file, we want to redo symbol resolution.
|
||||
// This is because symbols defined by object files in archive files
|
||||
// may have risen as a result of mark_live_objects().
|
||||
//
|
||||
// To redo symbol resolution, we want to clear the state first.
|
||||
clear_symbols(ctx);
|
||||
|
||||
// COMDAT elimination needs to happen exactly here.
|
||||
//
|
||||
// It needs to be after archive extraction, otherwise we might
|
||||
// assign COMDAT leader to an archive member that is not supposed to
|
||||
// be extracted.
|
||||
//
|
||||
// It needs to happen before the final symbol resolution, otherwise
|
||||
// we could eliminate a symbol that is already resolved to and cause
|
||||
// dangling references.
|
||||
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
|
||||
if (file->is_reachable) {
|
||||
for (ComdatGroupRef<E> &ref : file->comdat_groups)
|
||||
update_minimum(ref.group->owner, file->priority);
|
||||
for (ComdatGroup *g : file->lto_comdat_groups)
|
||||
if (g)
|
||||
update_minimum(g->owner, file->priority);
|
||||
}
|
||||
});
|
||||
|
||||
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
|
||||
if (file->is_reachable)
|
||||
for (ComdatGroupRef<E> &ref : file->comdat_groups)
|
||||
if (ref.group->owner != file->priority)
|
||||
for (u32 i : ref.members)
|
||||
if (InputSection<E> *isec = file->sections[i].get())
|
||||
isec->is_alive = false;
|
||||
});
|
||||
|
||||
// Redo symbol resolution
|
||||
tbb::parallel_for_each(files, [&](InputFile<E> *file) {
|
||||
if (file->is_reachable)
|
||||
file->resolve_symbols(ctx);
|
||||
});
|
||||
}
|
||||
|
||||
// Do link-time optimization. We pass all IR object files to the compiler
|
||||
@@ -1040,7 +1051,7 @@ void write_repro_file(Context<E> &ctx) {
|
||||
Fatal(ctx) << "cannot open " << path << ": " << errno_string();
|
||||
|
||||
tar->append("response.txt", create_response_file(ctx));
|
||||
tar->append("version.txt", mold_version + "\n");
|
||||
tar->append("version.txt", get_mold_version() + "\n");
|
||||
|
||||
std::unordered_set<std::string_view> seen;
|
||||
|
||||
@@ -1116,41 +1127,35 @@ void check_shlib_undefined(Context<E> &ctx) {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Skip test if we don't have a complete set of shared object files
|
||||
// for the program, because if there's a missing .so, an undefined
|
||||
// symbol might be defined by that library.
|
||||
auto do_test = [&] {
|
||||
std::unordered_set<std::string_view> sonames;
|
||||
for (std::unique_ptr<SharedFile<E>> &file : ctx.dso_pool)
|
||||
sonames.insert(file->soname);
|
||||
// Obtain a list of known shared library names.
|
||||
std::unordered_set<std::string_view> sonames;
|
||||
for (std::unique_ptr<SharedFile<E>> &file : ctx.dso_pool)
|
||||
sonames.insert(file->soname);
|
||||
|
||||
for (SharedFile<E> *file : ctx.dsos)
|
||||
for (std::string_view soname : file->get_dt_needed(ctx))
|
||||
if (!sonames.contains(soname))
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
tbb::parallel_for_each(ctx.dsos, [&](SharedFile<E> *file) {
|
||||
// Skip the file if it depends on a file that we know nothing about.
|
||||
// This is because missing symbols may be provided by that unknown file.
|
||||
for (std::string_view needed : file->get_dt_needed(ctx))
|
||||
if (!sonames.contains(needed))
|
||||
return;
|
||||
|
||||
if (do_test()) {
|
||||
tbb::parallel_for_each(ctx.dsos, [&](SharedFile<E> *file) {
|
||||
// Check if all undefined symbols have been resolved.
|
||||
for (i64 i = 0; i < file->elf_syms.size(); i++) {
|
||||
const ElfSym<E> &esym = file->elf_syms[i];
|
||||
Symbol<E> &sym = *file->symbols[i];
|
||||
if (esym.is_undef() && !esym.is_weak() && !sym.file &&
|
||||
!is_sparc_register(esym))
|
||||
Error(ctx) << *file << ": --no-allow-shlib-undefined: undefined symbol: "
|
||||
<< sym;
|
||||
}
|
||||
});
|
||||
}
|
||||
// Check if all undefined symbols have been resolved.
|
||||
for (i64 i = 0; i < file->elf_syms.size(); i++) {
|
||||
const ElfSym<E> &esym = file->elf_syms[i];
|
||||
Symbol<E> &sym = *file->symbols[i];
|
||||
if (esym.is_undef() && !esym.is_weak() && !sym.file &&
|
||||
!is_sparc_register(esym))
|
||||
Error(ctx) << *file << ": --no-allow-shlib-undefined: undefined symbol: "
|
||||
<< sym;
|
||||
}
|
||||
});
|
||||
|
||||
// Beyond this point, DSOs that are not referenced directly by any
|
||||
// object file are not needed. They were kept by
|
||||
// SharedFile<E>::mark_live_objects just for this pass. Therefore,
|
||||
// remove unneeded DSOs from the list now.
|
||||
for (SharedFile<E> *file : ctx.dsos)
|
||||
file->is_reachable = !file->as_needed;
|
||||
file->is_reachable = false;
|
||||
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
for (Symbol<E> *sym : file->get_global_syms())
|
||||
@@ -1166,6 +1171,10 @@ template <typename E>
|
||||
void check_symbol_types(Context<E> &ctx) {
|
||||
Timer t(ctx, "check_symbol_types");
|
||||
|
||||
std::vector<InputFile<E> *> files;
|
||||
append(files, ctx.objs);
|
||||
append(files, ctx.dsos);
|
||||
|
||||
auto canonicalize = [](u32 ty) -> u32 {
|
||||
if (ty == STT_GNU_IFUNC)
|
||||
return STT_FUNC;
|
||||
@@ -1174,35 +1183,23 @@ void check_symbol_types(Context<E> &ctx) {
|
||||
return ty;
|
||||
};
|
||||
|
||||
auto check = [&](InputFile<E> &file, Symbol<E> &sym,
|
||||
const ElfSym<E> &esym1, const ElfSym<E> &esym2) {
|
||||
if (sym.file && sym.file != &file &&
|
||||
esym1.st_type != STT_NOTYPE && esym2.st_type != STT_NOTYPE &&
|
||||
canonicalize(esym1.st_type) != canonicalize(esym2.st_type)) {
|
||||
Warn(ctx) << "symbol type mismatch: " << sym << '\n'
|
||||
<< ">>> defined in " << *sym.file << " as "
|
||||
<< stt_to_string<E>(esym1.st_type) << '\n'
|
||||
<< ">>> defined in " << file << " as "
|
||||
<< stt_to_string<E>(esym2.st_type);
|
||||
}
|
||||
};
|
||||
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
for (i64 i = file->first_global; i < file->elf_syms.size(); i++)
|
||||
if (Symbol<E> *sym = file->symbols[i];
|
||||
sym->file && sym->file != file)
|
||||
check(*file, *sym, sym->esym(), file->elf_syms[i]);
|
||||
});
|
||||
|
||||
tbb::parallel_for_each(ctx.dsos, [&](SharedFile<E> *file) {
|
||||
tbb::parallel_for_each(files.begin(), files.end(), [&](InputFile<E> *file) {
|
||||
for (i64 i = file->first_global; i < file->elf_syms.size(); i++) {
|
||||
if (Symbol<E> *sym = file->symbols[i];
|
||||
sym->file && sym->file != file)
|
||||
check(*file, *sym, sym->esym(), file->elf_syms[i]);
|
||||
Symbol<E> &sym = *file->symbols[i];
|
||||
if (!sym.file || sym.file == file)
|
||||
continue;
|
||||
|
||||
if (Symbol<E> *sym = file->symbols2[i])
|
||||
if (sym->file && sym->file != file)
|
||||
check(*file, *sym, sym->esym(), file->elf_syms[i]);
|
||||
const ElfSym<E> &esym1 = sym.esym();
|
||||
const ElfSym<E> &esym2 = file->elf_syms[i];
|
||||
|
||||
if (esym1.st_type != STT_NOTYPE && esym2.st_type != STT_NOTYPE &&
|
||||
canonicalize(esym1.st_type) != canonicalize(esym2.st_type)) {
|
||||
Warn(ctx) << "symbol type mismatch: " << sym << '\n'
|
||||
<< ">>> defined in " << *sym.file << " as "
|
||||
<< stt_to_string<E>(esym1.st_type) << '\n'
|
||||
<< ">>> defined in " << *file << " as "
|
||||
<< stt_to_string<E>(esym2.st_type);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1300,142 +1297,6 @@ void sort_ctor_dtor(Context<E> &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if a given section contains a DWARF32 debug record.
|
||||
// `isec` must be a .debug_info section.
|
||||
template <typename E>
|
||||
static bool is_dwarf32(Context<E> &ctx, InputSection<E> *isec) {
|
||||
if (!isec)
|
||||
return true;
|
||||
|
||||
if (isec->sh_size < 12) {
|
||||
// The section is too short. This is a user error, but instead of
|
||||
// being nitpicky about it, we simply handle it on a garbage-in,
|
||||
// garbage-out basis.
|
||||
return true;
|
||||
}
|
||||
|
||||
// A .debug_info section contains compilation units (CUs). A 32-bit CU
|
||||
// starts with a 32-bit size field, while a 64-bit CU starts with a
|
||||
// magic number 0xffff'ffff followed by a 64-bit size field.
|
||||
//
|
||||
// Note that size doesn't take the size field itself into account, so
|
||||
// the actual size of a 64-bit CU including the size field is 12 bytes
|
||||
// larger than the value in the size field.
|
||||
u8 buf[12];
|
||||
isec->copy_contents_to(ctx, buf, 12);
|
||||
if (*(U32<E> *)buf != 0xffff'ffff)
|
||||
return true;
|
||||
if (*(U64<E> *)(buf + 4) + 12 == isec->sh_size)
|
||||
return false;
|
||||
|
||||
// An input .debug_info section usually contains a single CU. However,
|
||||
// if the linker combines multiple object files using `-r`, the
|
||||
// resulting object file may have a .debug_info section with as many CUs
|
||||
// as there are in the input files. Therefore, if the first CU doesn't
|
||||
// cover the entire .debug_info section, we need to keep reading until
|
||||
// the end of the section.
|
||||
//
|
||||
// An input .debug_info section may be compressed using zlib or zstd, so
|
||||
// we need to uncompress it before accessing `isec->contents`.
|
||||
isec->uncompress(ctx);
|
||||
|
||||
u8 *p = (u8 *)isec->contents.data() + *(U64<E> *)(buf + 4) + 12;
|
||||
u8 *end = (u8 *)isec->contents.data() + isec->sh_size;
|
||||
|
||||
while (end - p >= 12) {
|
||||
if (*(U32<E> *)p != 0xffff'ffff)
|
||||
return true;
|
||||
p += *(U64<E> *)(p + 4) + 12;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Debug sections in an input object file refer to other debug sections in
|
||||
// the same file using section offsets. The offsets are 64 bits in DWARF64
|
||||
// and 32 bits in DWARF32.
|
||||
//
|
||||
// GCC and Clang emit DWARF32 debug info by default even for 64-bit code.
|
||||
// That is, 64-bit values are used for addresses, and 32-bit values for
|
||||
// references between debug sections. This makes sense for ordinary programs
|
||||
// because it reduces the size of the debug info sections.
|
||||
//
|
||||
// You can change the format to DWARF64 by passing `-gdwarf64`. Therefore,
|
||||
// the "right" approach to build an extremely large program in debug mode is
|
||||
// to recompile everything with `-gdwarf64`. However, that’s often not
|
||||
// feasiable for various reasons.
|
||||
//
|
||||
// If we don't do anything about it, a relocation overflow could occur if
|
||||
// any output debug section exceeds 4 GiB in size, making it almost
|
||||
// impossible for users to link an object file compiled without `-gdwarf64`
|
||||
// to an extremely large program.
|
||||
//
|
||||
// This function works around the issue by sorting output debug section
|
||||
// contents so that DWARF32 input sections are at the start of the output
|
||||
// section followed by DWARF64 input sections. By doing this, we can avoid
|
||||
// relocation overflow until the total size of DWARF32 input sections alone
|
||||
// exceeds 4 GiB.
|
||||
template <typename E>
|
||||
void sort_debug_info_sections(Context<E> &ctx) {
|
||||
Timer t(ctx, "sort_debug_info_sections");
|
||||
|
||||
// True if mold is running under ctest
|
||||
bool is_in_test = false;
|
||||
if (char *env = getenv("MOLD_DEBUG"); env && env[0])
|
||||
is_in_test = true;
|
||||
|
||||
// Get lists of output debug sections that need sorting
|
||||
std::vector<OutputSection<E> *> vec1;
|
||||
std::vector<MergedSection<E> *> vec2;
|
||||
|
||||
for (Chunk<E> *chunk : ctx.chunks)
|
||||
if (OutputSection<E> *osec = chunk->to_osec())
|
||||
if (!(osec->shdr.sh_flags & SHF_ALLOC) && osec->name.starts_with(".debug_"))
|
||||
if (osec->shdr.sh_size >= UINT32_MAX || is_in_test)
|
||||
vec1.push_back(osec);
|
||||
|
||||
for (std::unique_ptr<MergedSection<E>> &osec : ctx.merged_sections)
|
||||
if (!(osec->shdr.sh_flags & SHF_ALLOC) && osec->name.starts_with(".debug_"))
|
||||
if (osec->shdr.sh_size >= UINT32_MAX || is_in_test)
|
||||
vec2.push_back(osec.get());
|
||||
|
||||
if (vec1.empty() && vec2.empty())
|
||||
return;
|
||||
|
||||
// Read each input file's .debug_info to record whether the file contains
|
||||
// DWARF32 or DWARF64
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
file->is_dwarf32 = is_dwarf32(ctx, file->debug_info);
|
||||
});
|
||||
|
||||
// Unless we have a mix of DWARF32 and DWARF64, it doesn't make sense to
|
||||
// sort sections.
|
||||
if (ranges::all_of(ctx.objs, &ObjectFile<E>::is_dwarf32) ||
|
||||
ranges::none_of(ctx.objs, &ObjectFile<E>::is_dwarf32))
|
||||
return;
|
||||
|
||||
// Reorder input sections in the output section so that DWARF32
|
||||
// precededs DWARF64
|
||||
tbb::parallel_for_each(vec1, [&](OutputSection<E> *osec) {
|
||||
ranges::stable_partition(osec->members, [](InputSection<E> *isec) {
|
||||
return isec->file.is_dwarf32;
|
||||
});
|
||||
osec->compute_section_size(ctx);
|
||||
});
|
||||
|
||||
// Reorder strings in .debug_str and the like
|
||||
tbb::parallel_for_each(vec2, [&](MergedSection<E> *osec) {
|
||||
tbb::parallel_for_each(osec->members, [&](MergeableSection<E> *m) {
|
||||
if (m->input_section->file.is_dwarf32)
|
||||
for (SectionFragment<E> *frag : m->fragments)
|
||||
frag->is_32bit = true;
|
||||
});
|
||||
});
|
||||
|
||||
tbb::parallel_for_each(vec2, [&](MergedSection<E> *osec) {
|
||||
osec->compute_section_size(ctx);
|
||||
});
|
||||
}
|
||||
|
||||
// .ctors/.dtors serves the same purpose as .init_array/.fini_array,
|
||||
// albeit with very subtly differences. Both contain pointers to
|
||||
// initializer/finalizer functions. The runtime executes them one by one
|
||||
@@ -1510,8 +1371,7 @@ void shuffle_sections(Context<E> &ctx) {
|
||||
auto is_eligible = [](OutputSection<E> *osec) {
|
||||
if (osec) {
|
||||
std::string_view name = osec->name;
|
||||
return (osec->shdr.sh_flags & SHF_ALLOC) &&
|
||||
name != ".init" && name != ".fini" &&
|
||||
return name != ".init" && name != ".fini" &&
|
||||
name != ".ctors" && name != ".dtors" &&
|
||||
name != ".init_array" && name != ".preinit_array" &&
|
||||
name != ".fini_array";
|
||||
@@ -1583,7 +1443,7 @@ void compute_section_sizes(Context<E> &ctx) {
|
||||
|
||||
// create_range_extension_thunks is not thread-safe
|
||||
for (Chunk<E> *chunk : std::span(vec.begin(), tail.begin()))
|
||||
chunk->to_osec()->create_range_extension_thunks(ctx);
|
||||
chunk->to_osec()->create_range_extension_thunks(ctx, true);
|
||||
|
||||
tbb::parallel_for_each(tail, [&](Chunk<E> *chunk) {
|
||||
chunk->compute_section_size(ctx);
|
||||
@@ -1758,8 +1618,7 @@ void scan_relocations(Context<E> &ctx) {
|
||||
ctx.got->add_tlsdesc_symbol(ctx, sym);
|
||||
|
||||
if (sym->flags & NEEDS_COPYREL) {
|
||||
if (ctx.arg.z_relro && sym->file->is_dso &&
|
||||
((SharedFile<E> *)sym->file)->is_readonly(sym))
|
||||
if (ctx.arg.z_relro && ((SharedFile<E> *)sym->file)->is_readonly(sym))
|
||||
ctx.copyrel_relro->add_symbol(ctx, sym);
|
||||
else
|
||||
ctx.copyrel->add_symbol(ctx, sym);
|
||||
@@ -2555,15 +2414,6 @@ void sort_output_sections(Context<E> &ctx) {
|
||||
sort_output_sections_by_order(ctx);
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
static i64 get_tls_segment_alignment(Context<E> &ctx) {
|
||||
i64 val = 1;
|
||||
for (Chunk<E> *chunk : ctx.chunks)
|
||||
if (chunk->shdr.sh_flags & SHF_TLS)
|
||||
val = std::max<i64>(val, chunk->shdr.sh_addralign);
|
||||
return val;
|
||||
}
|
||||
|
||||
// This function assigns virtual addresses to output sections. Assigning
|
||||
// addresses is a bit tricky because we want to pack sections as tightly
|
||||
// as possible while not violating the constraints imposed by the hardware
|
||||
@@ -2608,12 +2458,8 @@ static void set_virtual_addresses_regular(Context<E> &ctx) {
|
||||
std::vector<Chunk<E> *> &chunks = ctx.chunks;
|
||||
u64 addr = ctx.arg.image_base;
|
||||
|
||||
auto is_tls = [](Chunk<E> *chunk) {
|
||||
return chunk->shdr.sh_flags & SHF_TLS;
|
||||
};
|
||||
|
||||
auto is_tbss = [](Chunk<E> *chunk) {
|
||||
return (chunk->shdr.sh_flags & SHF_TLS) && (chunk->shdr.sh_type == SHT_NOBITS);
|
||||
return (chunk->shdr.sh_type == SHT_NOBITS) && (chunk->shdr.sh_flags & SHF_TLS);
|
||||
};
|
||||
|
||||
for (i64 i = 0; i < chunks.size(); i++) {
|
||||
@@ -2670,12 +2516,6 @@ static void set_virtual_addresses_regular(Context<E> &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
// TLS sections are included only in PT_LOAD but also in PT_TLS.
|
||||
// We align the first TLS section so that the PT_TLS segment starts
|
||||
// at an address that meets the segment's alignment requirement.
|
||||
if (is_tls(chunks[i]) && (i == 0 || !is_tls(chunks[i - 1])))
|
||||
addr = align_to(addr, get_tls_segment_alignment(ctx));
|
||||
|
||||
// TLS BSS sections are laid out so that they overlap with the
|
||||
// subsequent non-tbss sections. Overlapping is fine because a STT_TLS
|
||||
// segment contains an initialization image for newly-created threads,
|
||||
@@ -2926,7 +2766,7 @@ i64 set_osec_offsets(Context<E> &ctx) {
|
||||
if (ctx.phdr) {
|
||||
i64 sz = ctx.phdr->shdr.sh_size;
|
||||
ctx.phdr->update_shdr(ctx);
|
||||
if (sz < ctx.phdr->shdr.sh_size)
|
||||
if (sz != ctx.phdr->shdr.sh_size)
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -2961,10 +2801,10 @@ void fix_synthetic_symbols(Context<E> &ctx) {
|
||||
}
|
||||
};
|
||||
|
||||
auto stop = [](Symbol<E> *sym, auto &chunk, i64 bias = 0) {
|
||||
auto stop = [](Symbol<E> *sym, auto &chunk) {
|
||||
if (sym && chunk) {
|
||||
sym->set_output_section(chunk);
|
||||
sym->value = chunk->shdr.sh_addr + chunk->shdr.sh_size + bias;
|
||||
sym->value = chunk->shdr.sh_addr + chunk->shdr.sh_size;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3001,9 +2841,10 @@ void fix_synthetic_symbols(Context<E> &ctx) {
|
||||
// such executable lacks the .dynamic section and thus there's no way
|
||||
// to find ifunc relocations other than these symbols.
|
||||
if (ctx.reldyn && ctx.arg.static_ && !ctx.arg.pie) {
|
||||
stop(ctx.__rel_iplt_start, ctx.reldyn,
|
||||
-get_num_irelative_relocs(ctx) * sizeof(ElfRel<E>));
|
||||
stop(ctx.__rel_iplt_start, ctx.reldyn);
|
||||
stop(ctx.__rel_iplt_end, ctx.reldyn);
|
||||
ctx.__rel_iplt_start->value -=
|
||||
get_num_irelative_relocs(ctx) * sizeof(ElfRel<E>);
|
||||
} else {
|
||||
// If the symbols are not ncessary, we turn them to absolute
|
||||
// symbols at address 0.
|
||||
@@ -3105,13 +2946,17 @@ void fix_synthetic_symbols(Context<E> &ctx) {
|
||||
}
|
||||
|
||||
// PPC64's _{save,rest}gpr{0,1}_{14,15,16,...,31} symbols
|
||||
if constexpr (is_ppc64v2<E>)
|
||||
for (i64 i = 0; i < ppc64_save_restore_insns.size(); i++)
|
||||
if (std::string_view label = ppc64_save_restore_insns[i].first;
|
||||
!label.empty())
|
||||
if constexpr (is_ppc64v2<E>) {
|
||||
i64 offset = 0;
|
||||
for (std::pair<std::string_view, u32> p : ppc64_save_restore_insns) {
|
||||
std::string_view label = p.first;
|
||||
if (!label.empty())
|
||||
if (Symbol<E> *sym = get_symbol(ctx, label);
|
||||
sym->file == ctx.internal_obj)
|
||||
start(sym, ctx.extra.save_restore, i * 4);
|
||||
start(sym, ctx.extra.save_restore, offset);
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
|
||||
// __start_ and __stop_ symbols
|
||||
for (Chunk<E> *chunk : sections) {
|
||||
@@ -3366,7 +3211,6 @@ void write_separate_debug_file(Context<E> &ctx) {
|
||||
});
|
||||
|
||||
append(ctx.chunks, ctx.debug_chunks);
|
||||
sort_debug_info_sections(ctx);
|
||||
|
||||
// Handle --compress-debug-info
|
||||
if (ctx.arg.compress_debug_sections != COMPRESS_NONE)
|
||||
@@ -3546,7 +3390,6 @@ template void create_reloc_sections(Context<E> &);
|
||||
template void copy_chunks(Context<E> &);
|
||||
template void construct_relr(Context<E> &);
|
||||
template void sort_dynsyms(Context<E> &);
|
||||
template void sort_debug_info_sections(Context<E> &);
|
||||
template void create_output_symtab(Context<E> &);
|
||||
template void apply_version_script(Context<E> &);
|
||||
template void parse_symbol_version(Context<E> &);
|
||||
|
||||
@@ -122,6 +122,10 @@ i64 compute_distance(Context<E> &ctx, Symbol<E> &sym,
|
||||
if (sym.is_absolute())
|
||||
return INT64_MAX;
|
||||
|
||||
// Likewise, relocations against weak undefined symbols won't be relaxed.
|
||||
if (sym.esym().is_undef_weak())
|
||||
return INT64_MAX;
|
||||
|
||||
// Compute a distance between the relocated place and the symbol.
|
||||
i64 S = sym.get_addr(ctx);
|
||||
i64 A = rel.r_addend;
|
||||
|
||||
@@ -104,7 +104,7 @@ void process_run_subcommand(Context<E> &ctx, int argc, char **argv) {
|
||||
setenv("LD_PRELOAD", dso_path.c_str(), 1);
|
||||
setenv("MOLD_PATH", self.c_str(), 1);
|
||||
|
||||
// If ld, ld.lld or ld.gold is specified, run mold instead
|
||||
// If ld, ld.lld or ld.gold is specified, run mold itself
|
||||
if (std::string cmd = path_filename(argv[2]);
|
||||
cmd == "ld" || cmd == "ld.lld" || cmd == "ld.gold") {
|
||||
std::vector<char *> args;
|
||||
|
||||
+68
-117
@@ -20,8 +20,8 @@
|
||||
// we don't need to try too hard to reduce thunk size to the absolute
|
||||
// minimum.
|
||||
|
||||
#if MOLD_ARM32LE || MOLD_ARM32BE || MOLD_ARM64LE || MOLD_ARM64BE || \
|
||||
MOLD_PPC32 || MOLD_PPC64V1 || MOLD_PPC64V2
|
||||
#if MOLD_ARM32 || MOLD_ARM64LE || MOLD_ARM64BE || MOLD_PPC32 || \
|
||||
MOLD_PPC64V1 || MOLD_PPC64V2
|
||||
|
||||
#include "mold.h"
|
||||
|
||||
@@ -45,67 +45,68 @@ static constexpr i64 max_thunk_size = 1024 * 1024;
|
||||
static constexpr i64 thunk_align = 16;
|
||||
|
||||
template <typename E>
|
||||
static bool
|
||||
requires_thunk(Context<E> &ctx, InputSection<E> &isec, const ElfRel<E> &rel,
|
||||
bool first_pass) {
|
||||
if (!is_func_call_rel(rel))
|
||||
static bool is_reachable(Context<E> &ctx, bool first_pass, InputSection<E> &isec,
|
||||
Symbol<E> &sym, const ElfRel<E> &rel) {
|
||||
// If the target section is in the same output section but
|
||||
// hasn't got any address yet, that's unreacahble.
|
||||
InputSection<E> *isec2 = sym.get_input_section();
|
||||
if (isec2 && isec.output_section == isec2->output_section &&
|
||||
isec2->offset == -1)
|
||||
return false;
|
||||
Symbol<E> &sym = *isec.file.symbols[rel.r_sym];
|
||||
|
||||
// We don't know about the final file layout on the first pass, so
|
||||
// we assume pessimistically that all out-of-section relocations are
|
||||
// out-of-range. Excessive thunks will be removed on the second pass.
|
||||
if (first_pass) {
|
||||
// On the first pass, we pessimistically assume that all out-of-section
|
||||
// relocations are out of range.
|
||||
InputSection<E> *isec2 = sym.get_input_section();
|
||||
if (!isec2 || isec.output_section != isec2->output_section)
|
||||
return true;
|
||||
|
||||
// If the target section is in the same output section but
|
||||
// hasn't got any address yet, that's unreacahble.
|
||||
if (isec2->offset == -1)
|
||||
return true;
|
||||
return false;
|
||||
|
||||
// Even if the target is the same section, we branch to its PLT
|
||||
// if it has one. So a symbol with a PLT is also considered an
|
||||
// out-of-section reference.
|
||||
if (sym.has_plt(ctx))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Thumb and ARM B instructions cannot be converted to BX, so we
|
||||
// always have to make them jump to a thunk to switch processor mode
|
||||
// even if their destinations are reachable.
|
||||
if constexpr (is_arm32<E>)
|
||||
if (bool is_thumb = sym.get_addr(ctx) & 1;
|
||||
(rel.r_type == R_ARM_JUMP24 && is_thumb) ||
|
||||
(rel.r_type == R_ARM_PLT32 && is_thumb) ||
|
||||
(rel.r_type == R_ARM_THM_JUMP24 && !is_thumb))
|
||||
return true;
|
||||
|
||||
// On PowerPC, all PLT calls go through range extension thunks.
|
||||
if constexpr (is_ppc<E>)
|
||||
if (sym.has_plt(ctx))
|
||||
return true;
|
||||
|
||||
// PowerPC before Power9 lacks PC-relative load/store instructions.
|
||||
// Functions compiled for Power9 or earlier assume that r2 points to
|
||||
// GOT+0x8000, while those for Power10 uses r2 as a scratch register.
|
||||
// We need a thunk to recompute r2 for interworking.
|
||||
if constexpr (is_ppc64v2<E>)
|
||||
if ((rel.r_type == R_PPC64_REL24 && !sym.esym().ppc64_preserves_r2()) ||
|
||||
(rel.r_type == R_PPC64_REL24_NOTOC && sym.esym().ppc64_uses_toc()))
|
||||
return true;
|
||||
|
||||
// Compute a distance between the relocated place and the symbol
|
||||
// and check if they are within reach.
|
||||
i64 S = sym.get_addr(ctx, NO_OPD);
|
||||
i64 A = get_addend(isec, rel);
|
||||
i64 P = isec.get_addr() + rel.r_offset;
|
||||
i64 val = S + A - P;
|
||||
return val < -branch_distance<E> || branch_distance<E> <= val;
|
||||
return -branch_distance<E> <= val && val < branch_distance<E>;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
static bool needs_shim(Context<E> &ctx, Symbol<E> &sym, const ElfRel<E> &rel) {
|
||||
// Thumb and ARM B instructions cannot be converted to BX, so we
|
||||
// always have to make them jump to a thunk to switch processor mode
|
||||
// even if their destinations are within their ranges.
|
||||
if constexpr (is_arm32<E>) {
|
||||
bool is_thumb = sym.get_addr(ctx) & 1;
|
||||
return (rel.r_type == R_ARM_THM_JUMP24 && !is_thumb) ||
|
||||
(rel.r_type == R_ARM_JUMP24 && is_thumb) ||
|
||||
(rel.r_type == R_ARM_PLT32 && is_thumb);
|
||||
}
|
||||
|
||||
// On PowerPC, all PLT calls go through range extension thunks.
|
||||
if constexpr (is_ppc32<E> || is_ppc64v1<E>)
|
||||
return sym.has_plt(ctx);
|
||||
|
||||
// PowerPC before Power9 lacks PC-relative load/store instructions.
|
||||
// Functions compiled for Power9 or earlier assume that r2 points to
|
||||
// GOT+0x8000, while those for Power10 uses r2 as a scratch register.
|
||||
// We need to a thunk to recompute r2 for interworking.
|
||||
if constexpr (is_ppc64v2<E>)
|
||||
return sym.has_plt(ctx) ||
|
||||
(rel.r_type == R_PPC64_REL24 && !sym.esym().ppc64_preserves_r2()) ||
|
||||
(rel.r_type == R_PPC64_REL24_NOTOC && sym.esym().ppc64_uses_toc());
|
||||
return false;
|
||||
}
|
||||
|
||||
template <>
|
||||
void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
|
||||
void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx,
|
||||
bool first_pass) {
|
||||
std::span<InputSection<E> *> m = members;
|
||||
if (m.empty())
|
||||
return;
|
||||
@@ -121,7 +122,7 @@ void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
|
||||
// We manage progress using four offsets which increase monotonically.
|
||||
// The locations they point to are always A <= B <= C <= D.
|
||||
//
|
||||
// Input sections between B and C are the current batch.
|
||||
// Input sections between B and C are in the current batch.
|
||||
//
|
||||
// A is the input section with the smallest address than can reach
|
||||
// from the current batch.
|
||||
@@ -180,21 +181,33 @@ void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
|
||||
// Create a new thunk and place it at D.
|
||||
offset = align_to(offset, thunk_align);
|
||||
thunks.emplace_back(std::make_unique<Thunk<E>>(*this, offset));
|
||||
|
||||
Thunk<E> &thunk = *thunks.back();
|
||||
std::mutex mu;
|
||||
|
||||
// Scan relocations between B and C to collect symbols that need
|
||||
// entries in the new thunk.
|
||||
std::mutex mu;
|
||||
|
||||
tbb::parallel_for(b, c, [&](i64 i) {
|
||||
InputSection<E> &isec = *m[i];
|
||||
|
||||
for (const ElfRel<E> &rel : isec.get_rels(ctx)) {
|
||||
if (requires_thunk(ctx, isec, rel, true)) {
|
||||
if (Symbol<E> &sym = *isec.file.symbols[rel.r_sym];
|
||||
!sym.flags.test_and_set()) {
|
||||
std::scoped_lock lock(mu);
|
||||
thunk.symbols.push_back(&sym);
|
||||
}
|
||||
if (!is_func_call_rel(rel))
|
||||
continue;
|
||||
|
||||
// Skip if the symbol is undefined. apply_reloc() will report an error.
|
||||
Symbol<E> &sym = *isec.file.symbols[rel.r_sym];
|
||||
if (!sym.file)
|
||||
continue;
|
||||
|
||||
// Skip if we can directly branch to the destination.
|
||||
if (is_reachable(ctx, first_pass, isec, sym, rel) &&
|
||||
!needs_shim(ctx, sym, rel))
|
||||
continue;
|
||||
|
||||
// Add the symbol to the current thunk if it's not added already.
|
||||
if (!sym.flags.test_and_set()) {
|
||||
std::scoped_lock lock(mu);
|
||||
thunk.symbols.push_back(&sym);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -213,7 +226,6 @@ void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
|
||||
b = c;
|
||||
}
|
||||
|
||||
// Reset flags for future use
|
||||
for (; t < thunks.size(); t++)
|
||||
for (Symbol<E> *sym : thunks[t]->symbols)
|
||||
sym->flags = 0;
|
||||
@@ -221,75 +233,15 @@ void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
|
||||
this->shdr.sh_size = offset;
|
||||
}
|
||||
|
||||
// create_range_extension_thunks() creates thunks with a pessimistic
|
||||
// assumption that all out-of-section references are out of range.
|
||||
// After computing output section addresses, we revisit all thunks to
|
||||
// remove unneeded entries from them.
|
||||
//
|
||||
// We create more thunks than necessary and then eliminate some of
|
||||
// them later, instead of just creating thunks at this stage. This is
|
||||
// because we can safely shrink sections after assigning addresses to
|
||||
// them without worrying about making existing references to thunks go
|
||||
// out of range. On the other hand, if we insert thunks after
|
||||
// assigning addresses to sections, references to thunks could become
|
||||
// out of range due to the new extra gaps for thunks. Thus, the
|
||||
// creation of thunks is a two-pass process.
|
||||
template <>
|
||||
void remove_redundant_thunks(Context<E> &ctx) {
|
||||
Timer t(ctx, "remove_redundant_thunks");
|
||||
set_osec_offsets(ctx);
|
||||
|
||||
// Gather output executable sections
|
||||
std::vector<OutputSection<E> *> sections;
|
||||
for (Chunk<E> *chunk : ctx.chunks)
|
||||
if (OutputSection<E> *osec = chunk->to_osec())
|
||||
if (osec->shdr.sh_flags & SHF_EXECINSTR)
|
||||
sections.push_back(osec);
|
||||
|
||||
// Mark all symbols that actually need range extension thunks
|
||||
for (OutputSection<E> *osec : sections) {
|
||||
tbb::parallel_for_each(osec->members, [&](InputSection<E> *isec) {
|
||||
for (const ElfRel<E> &rel : isec->get_rels(ctx))
|
||||
if (requires_thunk(ctx, *isec, rel, false))
|
||||
isec->file.symbols[rel.r_sym]->flags.test_and_set();
|
||||
});
|
||||
}
|
||||
|
||||
// Remove symbols from thunks if they don't actually need range
|
||||
// extension thunks
|
||||
std::vector<Symbol<E> *> syms;
|
||||
|
||||
for (OutputSection<E> *osec : sections) {
|
||||
for (std::unique_ptr<Thunk<E>> &thunk : osec->thunks) {
|
||||
append(syms, thunk->symbols);
|
||||
std::erase_if(thunk->symbols, [&](Symbol<E> *sym) { return !sym->flags; });
|
||||
}
|
||||
}
|
||||
|
||||
// Reset flags for future use
|
||||
for (Symbol<E> *sym : syms)
|
||||
sym->flags = 0;
|
||||
|
||||
// Recompute section sizes
|
||||
tbb::parallel_for_each(sections, [&](OutputSection<E> *osec) {
|
||||
std::span<InputSection<E> *> m = osec->members;
|
||||
std::span<std::unique_ptr<Thunk<E>>> t = osec->thunks;
|
||||
i64 offset = 0;
|
||||
|
||||
while (!m.empty() || !t.empty()) {
|
||||
if (!m.empty() && (t.empty() || m[0]->offset < t[0]->offset)) {
|
||||
offset = align_to(offset, 1 << m[0]->p2align);
|
||||
m[0]->offset = offset;
|
||||
offset += m[0]->sh_size;
|
||||
m = m.subspan(1);
|
||||
} else {
|
||||
offset = align_to(offset, thunk_align);
|
||||
t[0]->offset = offset;
|
||||
offset += t[0]->size();
|
||||
t = t.subspan(1);
|
||||
}
|
||||
}
|
||||
osec->shdr.sh_size = offset;
|
||||
});
|
||||
osec->create_range_extension_thunks(ctx, false);
|
||||
}
|
||||
|
||||
// When applying relocations, we want to know the address in a reachable
|
||||
@@ -307,8 +259,7 @@ void gather_thunk_addresses(Context<E> &ctx) {
|
||||
std::vector<OutputSection<E> *> sections;
|
||||
for (Chunk<E> *chunk : ctx.chunks)
|
||||
if (OutputSection<E> *osec = chunk->to_osec())
|
||||
if (osec->shdr.sh_flags & SHF_EXECINSTR)
|
||||
sections.push_back(osec);
|
||||
sections.push_back(osec);
|
||||
|
||||
ranges::stable_sort(sections, {}, [](OutputSection<E> *x) {
|
||||
return x->shdr.sh_addr;
|
||||
|
||||
+1
-1
@@ -162,7 +162,7 @@ u64 get_tp_addr(const ElfPhdr<E> &phdr) {
|
||||
// and %a0/%a1 on s390x) refers to past the end of the TLS block for
|
||||
// historical reasons. TLVs are accessed with negative offsets from TP.
|
||||
return align_to(phdr.p_vaddr + phdr.p_memsz, phdr.p_align);
|
||||
} else if constexpr (is_arm<E> || is_sh4<E>) {
|
||||
} else if constexpr (is_arm<E> || is_sh4<E> || is_arc<E>) {
|
||||
// On ARM and SH4, the runtime appends two words at the beginning
|
||||
// of TLV template image when copying TLVs to the TLS block, so we need
|
||||
// to offset it.
|
||||
|
||||
+8
-8
@@ -30,8 +30,8 @@ endif()
|
||||
|
||||
if(MOLD_ENABLE_QEMU_TESTS)
|
||||
list(APPEND QEMU_ARCHS
|
||||
x86_64 i386 arm armeb aarch64 aarch64_be ppc ppc64 ppc64le sparc64
|
||||
sh4 sh4eb s390x riscv64 riscv32 m68k loongarch64)
|
||||
x86_64 i386 arm aarch64 aarch64_be ppc ppc64 ppc64le sparc64 sh4 sh4eb
|
||||
s390x riscv64 riscv32 m68k loongarch64 arc)
|
||||
|
||||
LIST(APPEND TRIPLES
|
||||
x86_64-linux-gnu
|
||||
@@ -39,7 +39,6 @@ if(MOLD_ENABLE_QEMU_TESTS)
|
||||
aarch64-linux-gnu
|
||||
aarch64_be-linux-gnu
|
||||
arm-linux-gnueabihf
|
||||
armeb-linux-gnueabihf
|
||||
riscv64-linux-gnu
|
||||
powerpc-linux-gnu
|
||||
powerpc64-linux-gnu
|
||||
@@ -50,7 +49,8 @@ if(MOLD_ENABLE_QEMU_TESTS)
|
||||
sh4aeb-linux-gnu
|
||||
riscv32-linux-gnu
|
||||
m68k-linux-gnu
|
||||
loongarch64-linux-gnu)
|
||||
loongarch64-linux-gnu
|
||||
arc-linux-gnu)
|
||||
|
||||
foreach(ARCH IN LISTS QEMU_ARCHS)
|
||||
find_program(HAS_qemu-${ARCH} qemu-${ARCH})
|
||||
@@ -122,10 +122,6 @@ if(${MACHINE} STREQUAL "arm" OR (HAS_qemu-arm AND HAS_arm-linux-gnueabihf-gcc))
|
||||
add_target(arm arm-linux-gnueabihf)
|
||||
endif()
|
||||
|
||||
if(${MACHINE} STREQUAL "armeb" OR (HAS_qemu-armeb AND HAS_armeb-linux-gnueabihf-gcc))
|
||||
add_target(armeb armeb-linux-gnueabihf)
|
||||
endif()
|
||||
|
||||
if(${MACHINE} STREQUAL "riscv64" OR (HAS_qemu-riscv64 AND HAS_riscv64-linux-gnu-gcc))
|
||||
add_target(riscv64 riscv64-linux-gnu)
|
||||
endif()
|
||||
@@ -189,3 +185,7 @@ endif()
|
||||
if(${MACHINE} STREQUAL "loongarch64" OR (HAS_qemu-loongarch64 AND HAS_loongarch64-linux-gnu-gcc))
|
||||
add_target(loongarch64 loongarch64-linux-gnu)
|
||||
endif()
|
||||
|
||||
if(${MACHINE} STREQUAL "arc" OR (HAS_qemu-loongarch64 AND HAS_arc-linux-gnu-gcc))
|
||||
add_target(arc arc-linux-gnu)
|
||||
endif()
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc -
|
||||
int main() {}
|
||||
EOF
|
||||
|
||||
not $CC -B. -o $t/exe $t/a.o -mbe32 |& grep 'be32 is not supported'
|
||||
+23
-18
@@ -1,28 +1,33 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -o $t/libfoo.so -shared -fPIC -Wl,-soname,libfoo.so -xc -
|
||||
int fn1() { return 42; }
|
||||
cat <<EOF | $CC -c -fPIC -o $t/a.o -xc -
|
||||
int foo() {
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -o $t/libbar.so -shared -fPIC -Wl,-soname,libbar.so -xc - -L$t -lfoo
|
||||
int fn1();
|
||||
int fn2() { return fn1(); }
|
||||
cat <<EOF | $CC -c -fPIC -o $t/b.o -xc -
|
||||
__attribute__((weak)) int foo();
|
||||
|
||||
int bar() {
|
||||
if (foo) return foo();
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -o $t/libbaz.so -shared -fPIC -Wl,-soname,libbaz.so -xc - -L$t -lbar
|
||||
int fn2();
|
||||
int fn3() { return fn2(); }
|
||||
cat <<EOF | $CC -xc -c -o $t/c.o -
|
||||
int bar();
|
||||
|
||||
int main() {
|
||||
return bar();
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc -
|
||||
int fn3();
|
||||
int main() { fn3(); }
|
||||
EOF
|
||||
$CC -B. -shared -o $t/libfoo.so $t/a.o
|
||||
$CC -B. -shared -o $t/libbar.so $t/b.o
|
||||
$CC -B. -o $t/exe $t/c.o -L$t -Wl,--as-needed -lfoo -lbar
|
||||
|
||||
$CC -B. -o $t/exe $t/a.o -L$t -Wl,--as-needed -lbaz -lbar
|
||||
readelf -W --dynamic $t/exe > $t/log2
|
||||
|
||||
not grep libfoo $t/log2
|
||||
not grep libbar $t/log2
|
||||
grep libbaz $t/log2
|
||||
readelf --dynamic $t/exe > $t/log
|
||||
not grep libfoo.so $t/log
|
||||
grep libbar.so $t/log
|
||||
+2
-3
@@ -7,7 +7,6 @@ canonical_name() {
|
||||
case $1 in
|
||||
i?86) echo i686 ;;
|
||||
amd64) echo x86_64 ;;
|
||||
armeb*) echo armeb ;;
|
||||
arm*) echo arm ;;
|
||||
powerpc) echo ppc ;;
|
||||
powerpc64) echo ppc64 ;;
|
||||
@@ -65,7 +64,7 @@ t=$TESTDIR/$testname
|
||||
mkdir -p $t
|
||||
|
||||
case $MACHINE in
|
||||
x86_64 | i686 | arm | armeb)
|
||||
x86_64 | i686 | arm)
|
||||
tlsdesc_opt=-mtls-dialect=gnu2 ;;
|
||||
aarch64 | riscv* | loongarch*)
|
||||
tlsdesc_opt=-mtls-dialect=desc ;;
|
||||
@@ -102,7 +101,7 @@ supports_ifunc() {
|
||||
|
||||
supports_tlsdesc() {
|
||||
# musl's tlsdesc on arm32 seems to be broken
|
||||
[[ $MACHINE = arm* ]] && is_musl && return 1
|
||||
[ $MACHINE = arm ] && is_musl && return 1
|
||||
|
||||
# FreeBSD's loader doesn't seem to support TLSDESC relocs in an executable
|
||||
[ "$(uname)" = FreeBSD ] && return 1
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
# arm-linux-gnueabihf-objcopy crashes on x86-64
|
||||
[[ $MACHINE = arm* ]] && skip
|
||||
[ $MACHINE = arm ] && skip
|
||||
[ $MACHINE = riscv32 ] && skip
|
||||
|
||||
command -v zstdcat >& /dev/null || skip
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
# Currently, CREL is not supported on REL-type targets
|
||||
[[ $MACHINE = arm* ]] && skip
|
||||
[ $MACHINE = arm ] && skip
|
||||
[ $MACHINE = i686 ] && skip
|
||||
|
||||
[ "$CC" = cc ] || skip
|
||||
|
||||
@@ -6,16 +6,16 @@ cat <<EOF > $t/a.h
|
||||
#define B 99
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -o $t/b.o -c -xc - -I$t -g3
|
||||
cat <<EOF | $GCC -o $t/b.o -c -xc - -I$t -g3
|
||||
#include "a.h"
|
||||
extern int z();
|
||||
int main () { return z() - 122; }
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -o $t/c.o -c -xc - -I$t -g3
|
||||
cat <<EOF | $GCC -o $t/c.o -c -xc - -I$t -g3
|
||||
#include "a.h"
|
||||
int z() { return A + B; }
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe $t/b.o $t/c.o
|
||||
$GCC -B. -o $t/exe $t/b.o $t/c.o
|
||||
$OBJDUMP --dwarf=macro $t/exe | not grep 'DW_MACRO_import -.* 0x0$'
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc -
|
||||
void foo() {}
|
||||
EOF
|
||||
|
||||
echo '{ global: foo; local: *; };' > $t/b.ver
|
||||
|
||||
$CC -B. -o $t/c.so -shared $t/a.o -Wl,--default-symver -Wl,--version-script=$t/b.ver
|
||||
readelf --dyn-syms $t/c.so | grep -F ' foo@@c.so'
|
||||
+2
-2
@@ -22,8 +22,8 @@ void bar();
|
||||
int main() { bar(); }
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe1 $t/c.o $t/b.so -Wl,--allow-shlib-undefined
|
||||
$CC -B. -o $t/exe1 $t/c.o $t/b.so
|
||||
readelf --dynamic $t/exe1 | grep 'Dependency audit library:..*/a.so'
|
||||
|
||||
$CC -B. -o $t/exe2 $t/c.o $t/b.so -Wl,--depaudit=foo -Wl,--allow-shlib-undefined
|
||||
$CC -B. -o $t/exe2 $t/c.o $t/b.so -Wl,--depaudit=foo
|
||||
readelf --dynamic $t/exe2 | grep 'Dependency audit library:..*foo:.*/a.so'
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
|
||||
# musl libc does not support init/fini on ARM
|
||||
# https://github.com/rui314/mold/issues/951
|
||||
[[ $MACHINE = arm* -o $MACHINE = aarch64 ]] && is_musl && skip
|
||||
[ $MACHINE = arm -o $MACHINE = aarch64 ] && is_musl && skip
|
||||
|
||||
cat <<EOF | $CC -c -fPIC -o $t/a.o -xc -
|
||||
void keep();
|
||||
|
||||
@@ -6,11 +6,8 @@ cat <<EOF | $CC -c -o $t/a.o -x assembler -
|
||||
_start:
|
||||
EOF
|
||||
|
||||
./mold -o $t/exe1 $t/a.o
|
||||
readelf -WS $t/exe1 | not grep -F .interp
|
||||
./mold -o $t/exe $t/a.o
|
||||
readelf --sections $t/exe | not grep -F .interp
|
||||
|
||||
./mold -o $t/exe2 $t/a.o --dynamic-linker=/foo/bar
|
||||
readelf -WS $t/exe2 | grep -F .interp
|
||||
|
||||
./mold -o $t/exe3 $t/a.o --dynamic-linker=/foo/bar -static
|
||||
readelf -WS $t/exe3 | not grep -F .interp
|
||||
./mold -o $t/exe $t/a.o --dynamic-linker=/foo/bar
|
||||
readelf --sections $t/exe | grep -F .interp
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
[[ $MACHINE = arm* ]] && skip
|
||||
[ $MACHINE = arm ] && skip
|
||||
|
||||
cat <<EOF | $CXX -o $t/a.o -c -fPIC -xc++ -
|
||||
#include <cstdio>
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -c -o $t/a.o -xc -
|
||||
cat <<EOF | $GCC -c -o $t/a.o -xc -
|
||||
#include <stdio.h>
|
||||
|
||||
__attribute__((section(".gnu.warning.foo")))
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
# ARM32's strip command crashes on the output of this test for some reason.
|
||||
[[ $MACHINE = arm* ]] && skip
|
||||
[ $MACHINE = arm ] && skip
|
||||
|
||||
strip=$STRIP
|
||||
command -v $strip >& /dev/null || skip
|
||||
|
||||
@@ -9,4 +9,4 @@ echo 'int main() {}' | clang -c -o $t/a.o -xc -
|
||||
echo 'void foo() {}' | clang -c -o $t/b.o -xc - -flto
|
||||
|
||||
not ./mold -o /dev/null $t/a.o $t/b.o |&
|
||||
grep "b.o: unable to handle this LTO object file"
|
||||
grep "b.o: don't know how to handle this LTO object file"
|
||||
@@ -1,26 +0,0 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
[ "$(uname)" = FreeBSD ] && skip
|
||||
|
||||
cat <<EOF | $CC -B. -shared -fPIC -o $t/libfoo.so -xc -
|
||||
void foo() {}
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -B. -shared -fPIC -o $t/libbar.so -xc - -L$t -lfoo
|
||||
void foo();
|
||||
void bar() { foo(); }
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -B. -shared -fPIC -o $t/libfoo.so -xc - -L$t -lbar
|
||||
void baz();
|
||||
void foo() { baz(); }
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -c -o $t/a.o -c -xc -
|
||||
int bar();
|
||||
int main() { bar(); }
|
||||
EOF
|
||||
|
||||
not $CC -B. -o $t/exe1 $t/a.o -Wl,--no-allow-shlib-undefined -L$t -lfoo -lbar |&
|
||||
grep 'libfoo.so: --no-allow-shlib-undefined: undefined symbol: baz'
|
||||
@@ -1,8 +1,6 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
[ "$(uname)" = FreeBSD ] && skip
|
||||
|
||||
cat <<EOF | $CC -B. -shared -fPIC -o $t/libfoo.so -xc -
|
||||
void foo() {}
|
||||
EOF
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
[ "$(uname)" = FreeBSD ] && skip
|
||||
|
||||
cat <<EOF | $CC -B. -shared -fPIC -o $t/libfoo.so -xc -
|
||||
void foo() {}
|
||||
EOF
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
[ "$(uname)" = FreeBSD ] && skip
|
||||
|
||||
cat <<EOF | $CC -B. -shared -fPIC -o $t/libfoo.so -xc -
|
||||
void foo() {}
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -B. -shared -fPIC -o $t/libbar.so -xc - -L$t -lfoo
|
||||
void foo();
|
||||
void bar() { foo(); }
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -B. -shared -fPIC -o $t/libbaz.so -xc - -L$t -lbar
|
||||
void bar();
|
||||
void baz() { bar(); }
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -c -o $t/a.o -c -xc -
|
||||
int baz();
|
||||
int main() { baz(); }
|
||||
EOF
|
||||
|
||||
mv $t/libfoo.so $t/libfoo.so.bak
|
||||
echo | $CC -B. -shared -fPIC -o $t/libfoo.so -xc -
|
||||
|
||||
$CC -B. -o $t/exe1 $t/a.o -Wl,--no-allow-shlib-undefined -L$t -lbar -lbaz
|
||||
|
||||
not $CC -B. -o $t/exe2 $t/a.o -Wl,--no-allow-shlib-undefined \
|
||||
-L$t -lfoo -lbar -lbaz |& grep 'no-allow-shlib-undefined: undefined symbol: foo'
|
||||
@@ -1,31 +0,0 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
[ "$(uname)" = FreeBSD ] && skip
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc - -fPIC
|
||||
void f1() {}
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -o $t/b.o -c -xc - -fPIC
|
||||
void f1();
|
||||
void f2() { f1(); }
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -o $t/c.o -c -xc - -fPIC
|
||||
void f1();
|
||||
void f3() { f1(); }
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/libfoo.so -shared $t/a.o
|
||||
$CC -B. -o $t/libbar.so -shared $t/b.o -L$t -lfoo
|
||||
$CC -B. -o $t/libbaz.so -shared $t/c.o
|
||||
|
||||
|
||||
cat <<EOF | $CC -o $t/d.o -c -xc - -fPIC
|
||||
void f2();
|
||||
void f3();
|
||||
int main() { f2(); f3(); }
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe $t/d.o -L$t -lbar -lbaz
|
||||
+1
-1
@@ -3,10 +3,10 @@
|
||||
|
||||
[ $MACHINE = i686 ] && skip
|
||||
[ $MACHINE = m68k ] && skip
|
||||
[ $MACHINE = arm ] && skip
|
||||
[ $MACHINE = ppc64 ] && skip
|
||||
[ $MACHINE = ppc64le ] && skip
|
||||
[ $MACHINE = sh4 ] && skip
|
||||
[[ $MACHINE = arm* ]] && skip
|
||||
[[ $MACHINE = loongarch* ]] && skip
|
||||
|
||||
cat <<EOF | $CC -shared -o $t/a.so -xc -
|
||||
|
||||
Executable
BIN
Binary file not shown.
@@ -6,12 +6,12 @@ nm mold | grep '__tsan_init' && skip
|
||||
|
||||
test_cflags -g3 -gz || skip
|
||||
|
||||
cat <<EOF | $CC -c -o $t/a.o -xc - -g3 -gz
|
||||
cat <<EOF | $GCC -c -o $t/a.o -xc - -g3 -gz
|
||||
#include <stdio.h>
|
||||
void hello() { printf("Hello world\n"); }
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -c -o $t/b.o -xc - -g3 -gz
|
||||
cat <<EOF | $GCC -c -o $t/b.o -xc - -g3 -gz
|
||||
void hello();
|
||||
int main() { hello(); }
|
||||
EOF
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
# ARM assembler has a differnet grammar than the others.
|
||||
# Concretely speaking, ARM as uses "@" as a start of a comment.
|
||||
[[ $MACHINE = arm* ]] && skip
|
||||
[ $MACHINE = arm ] && skip
|
||||
|
||||
# All data symbols need to be aligned to 2 byte boundaries on s390x,
|
||||
# so rodata.str1.1 in this file is invalid on s390x.
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
nm mold | grep '__tsan_init' && skip
|
||||
command -v flock >& /dev/null || skip
|
||||
|
||||
cat <<EOF > $t/a.c
|
||||
int x = 1;
|
||||
void foo() {}
|
||||
EOF
|
||||
|
||||
$CC -o $t/a.o -c -g -gdwarf32 $t/a.c || skip
|
||||
|
||||
cat <<EOF > $t/b.c
|
||||
int y = 1;
|
||||
int main() {}
|
||||
EOF
|
||||
|
||||
$CC -o $t/b.o -c -g -gdwarf64 $t/b.c
|
||||
|
||||
MOLD_DEBUG=1 $CC -B. -o $t/exe $t/a.o $t/b.o -g -Wl,--separate-debug-file
|
||||
flock $t/exe.dbg true
|
||||
readelf -p .debug_line_str $t/exe.dbg > $t/str
|
||||
|
||||
if grep -Fw a.c $t/str; then
|
||||
grep -A10 -Fw a.c $t/str | grep -Fw b.c
|
||||
fi
|
||||
@@ -17,7 +17,7 @@ $CC -c -o $t/a.o $t/a.c -g
|
||||
$CC -B. -o $t/exe1 $t/a.o -Wl,--separate-debug-file
|
||||
readelf -SW $t/exe1 | grep -F .gnu_debuglink
|
||||
|
||||
flock $t/exe1.dbg true
|
||||
flock $t/exe1 true
|
||||
gdb $t/exe1 -ex 'list main' -ex 'quit' | grep -F printf
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ $CC -c -o $t/a.o $t/a.c -g
|
||||
$CC -B. -o $t/exe2 $t/a.o -Wl,--separate-debug-file,--no-build-id
|
||||
readelf -SW $t/exe2 | grep -F .gnu_debuglink
|
||||
|
||||
flock $t/exe2.dbg true
|
||||
flock $t/exe2 true
|
||||
gdb $t/exe2 -ex 'list main' -ex 'quit' | grep -F printf
|
||||
|
||||
|
||||
@@ -33,6 +33,6 @@ $CC -c -o $t/a.o $t/a.c -g
|
||||
$CC -B. -o $t/exe3 $t/a.o -Wl,--separate-debug-file,--compress-debug-sections=zlib
|
||||
readelf -SW $t/exe3 | grep -F .gnu_debuglink
|
||||
|
||||
flock $t/exe3.dbg true
|
||||
flock $t/exe3 true
|
||||
readelf -W --sections $t/exe3.dbg | grep '\.debug_info .*C'
|
||||
gdb $t/exe3 -ex 'list main' -ex 'quit' | grep -F printf
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc - -g -gdwarf32 -Wl,--compress-debug-sections=zlib || skip
|
||||
void foo() {}
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -o $t/b.o -c -xc - -g -gdwarf64 -Wl,--compress-debug-sections=zstd || skip
|
||||
int main() {}
|
||||
EOF
|
||||
|
||||
# Test if DWARF32 precedes DWARF64 in the output .debug_info
|
||||
MOLD_DEBUG=1 $CC -B. -o $t/exe1 $t/a.o $t/b.o -g -Wl,-Map=$t/map1
|
||||
grep -A10 -F '/a.o:(.debug_info)' $t/map1 | grep -F '/b.o:(.debug_info)'
|
||||
|
||||
MOLD_DEBUG=1 $CC -B. -o $t/exe2 $t/b.o $t/a.o -g -Wl,-Map=$t/map2
|
||||
grep -A10 -F '/a.o:(.debug_info)' $t/map2 | grep -F '/b.o:(.debug_info)'
|
||||
@@ -1,24 +0,0 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
nm mold | grep '__tsan_init' && skip
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc - -g -gdwarf64 || skip
|
||||
void foo() {}
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -o $t/b.o -c -xc - -g -gdwarf32
|
||||
void bar() {}
|
||||
EOF
|
||||
|
||||
./mold -r -o $t/c.o $t/a.o $t/b.o
|
||||
|
||||
cat <<EOF | $CC -o $t/d.o -c -xc - -g -gdwarf64
|
||||
int main() {}
|
||||
EOF
|
||||
|
||||
MOLD_DEBUG=1 $CC -B. -o $t/exe1 $t/c.o $t/d.o -g -Wl,-Map=$t/map1
|
||||
grep -A10 -F '/c.o:(.debug_info)' $t/map1 | grep -F '/d.o:(.debug_info)'
|
||||
|
||||
MOLD_DEBUG=1 $CC -B. -o $t/exe2 $t/d.o $t/c.o -g -Wl,-Map=$t/map2
|
||||
grep -A10 -F '/c.o:(.debug_info)' $t/map2 | grep -F '/d.o:(.debug_info)'
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF > $t/a.c
|
||||
int x = 1;
|
||||
void foo() {}
|
||||
EOF
|
||||
|
||||
$CC -o $t/a.o -c -g -gdwarf32 $t/a.c || skip
|
||||
|
||||
cat <<EOF > $t/b.c
|
||||
int y = 1;
|
||||
int main() {}
|
||||
EOF
|
||||
|
||||
$CC -o $t/b.o -c -g -gdwarf64 $t/b.c
|
||||
|
||||
# Test if DWARF32 precedes DWARF64 in the output .debug_info
|
||||
|
||||
MOLD_DEBUG=1 $CC -B. -o $t/exe1 $t/a.o $t/b.o -g -Wl,-Map=$t/map1
|
||||
grep -A10 -F '/a.o:(.debug_info)' $t/map1 | grep -F '/b.o:(.debug_info)'
|
||||
readelf -p .debug_line_str $t/exe1 > $t/str1
|
||||
|
||||
if grep -Fw a.c $t/str1; then
|
||||
grep -A10 -Fw a.c $t/str1 | grep -Fw b.c
|
||||
fi
|
||||
|
||||
|
||||
MOLD_DEBUG=1 $CC -B. -o $t/exe2 $t/b.o $t/a.o -g -Wl,-Map=$t/map2
|
||||
grep -A10 -F '/a.o:(.debug_info)' $t/map2 | grep -F '/b.o:(.debug_info)'
|
||||
readelf -p .debug_line_str $t/exe2 > $t/str2
|
||||
|
||||
if grep -Fw a.c $t/str2; then
|
||||
grep -A10 -Fw a.c $t/str2 | grep -Fw b.c
|
||||
fi
|
||||
|
||||
|
||||
if [ -z "$QEMU" ] && command -v gdb; then
|
||||
gdb $t/exe1 -ex 'list main' -ex 'quit' | grep 'int y = 1'
|
||||
gdb $t/exe2 -ex 'list main' -ex 'quit' | grep 'int y = 1'
|
||||
fi
|
||||
@@ -1,26 +0,0 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc - -fPIC
|
||||
void foo() {}
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -o $t/b.o -c -xc - -fPIC
|
||||
__asm__(".symver foo_,foo@@VERSION");
|
||||
void foo_() {}
|
||||
EOF
|
||||
|
||||
cat <<EOF > $t/c.version
|
||||
VERSION { global: foo; local: *; };
|
||||
EOF
|
||||
|
||||
$CC -B. -shared -o $t/libfoo.so $t/a.o
|
||||
$CC -B. -shared -o $t/libbar.so $t/b.o -Wl,--version-script=$t/c.version
|
||||
|
||||
cat <<EOF | $CC -o $t/d.o -c -xc -
|
||||
void foo();
|
||||
int main() { foo(); }
|
||||
__asm__(".symver foo,foo@VERSION");
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe $t/d.o -L$t -lfoo -lbar
|
||||
@@ -1,7 +1,6 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
[ $MACHINE = armeb ] && skip
|
||||
[[ $MACHINE = loongarch* ]] && skip
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc -
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
[ $MACHINE = armeb ] && skip
|
||||
[[ $MACHINE = loongarch* ]] && skip
|
||||
|
||||
cat <<EOF | $CC -c -o $t/a.o -xc -
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
[ $MACHINE = armeb ] && skip
|
||||
[[ $MACHINE = loongarch* ]] && skip
|
||||
|
||||
mkdir -p $t/bin $t/sysroot/foo
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xassembler - || skip
|
||||
cat <<EOF | $GCC -o $t/a.o -c -xassembler -
|
||||
.globl foo
|
||||
.tls_common foo,4,4
|
||||
EOF
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
|
||||
cat <<EOF | $GCC -fPIC -c -o $t/a.o -xc -
|
||||
static _Thread_local int foo[10000] = { 3, [9999] = 5 };
|
||||
|
||||
int get_foo(int idx) { return foo[idx]; }
|
||||
@@ -9,7 +9,7 @@ EOF
|
||||
|
||||
$CC -B. -shared -o $t/b.so $t/a.o
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/c.o -xc -
|
||||
cat <<EOF | $GCC -fPIC -c -o $t/c.o -xc -
|
||||
#include <assert.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -fPIC -fno-plt -c -o $t/a.o -xc -
|
||||
cat <<EOF | $GCC -fPIC -fno-plt -c -o $t/a.o -xc -
|
||||
#include <stdio.h>
|
||||
|
||||
__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1;
|
||||
@@ -19,13 +19,13 @@ int main() {
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -fPIC -fno-plt -c -o $t/b.o -xc -
|
||||
cat <<EOF | $GCC -fPIC -fno-plt -c -o $t/b.o -xc -
|
||||
__attribute__((tls_model("global-dynamic"))) _Thread_local int x3 = 3;
|
||||
__attribute__((tls_model("global-dynamic"))) static _Thread_local int x5 = 5;
|
||||
int get_x5() { return x5; }
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -fPIC -fno-plt -c -o $t/c.o -xc -
|
||||
cat <<EOF | $GCC -fPIC -fno-plt -c -o $t/c.o -xc -
|
||||
__attribute__((tls_model("global-dynamic"))) _Thread_local int x4 = 4;
|
||||
__attribute__((tls_model("global-dynamic"))) static _Thread_local int x6 = 6;
|
||||
int get_x6() { return x6; }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
|
||||
cat <<EOF | $GCC -fPIC -c -o $t/a.o -xc -
|
||||
#include <stdio.h>
|
||||
|
||||
__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1;
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
|
||||
cat <<EOF | $GCC -fPIC -c -o $t/a.o -xc -
|
||||
#include <stdio.h>
|
||||
|
||||
__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1;
|
||||
@@ -20,13 +20,13 @@ int main() {
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/b.o -xc -
|
||||
cat <<EOF | $GCC -fPIC -c -o $t/b.o -xc -
|
||||
__attribute__((tls_model("global-dynamic"))) _Thread_local int x3 = 3;
|
||||
__attribute__((tls_model("global-dynamic"))) static _Thread_local int x5 = 5;
|
||||
int get_x5() { return x5; }
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/c.o -xc -
|
||||
cat <<EOF | $GCC -fPIC -c -o $t/c.o -xc -
|
||||
__attribute__((tls_model("global-dynamic"))) _Thread_local int x4 = 4;
|
||||
__attribute__((tls_model("global-dynamic"))) static _Thread_local int x6 = 6;
|
||||
int get_x6() { return x6; }
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
|
||||
cat <<EOF | $GCC -fPIC -c -o $t/a.o -xc -
|
||||
#include <stdio.h>
|
||||
|
||||
__attribute__((tls_model("initial-exec"))) static _Thread_local int foo;
|
||||
@@ -19,7 +19,7 @@ EOF
|
||||
|
||||
$CC -B. -shared -o $t/b.so $t/a.o
|
||||
|
||||
cat <<EOF | $CC -c -o $t/c.o -xc - -fPIC
|
||||
cat <<EOF | $GCC -c -o $t/c.o -xc - -fPIC
|
||||
#include <stdio.h>
|
||||
|
||||
_Thread_local int baz;
|
||||
|
||||
@@ -18,14 +18,14 @@ extern _Thread_local int x;
|
||||
extern _Thread_local int y[];
|
||||
|
||||
int main() {
|
||||
printf("%lu %d %d %d %d\n", (unsigned long)&x % 256, x, y[0], y[1], y[2]);
|
||||
printf("%d %d %d %d\n", x, y[0], y[1], y[2]);
|
||||
}
|
||||
EOF
|
||||
|
||||
$CC -B. -shared -o $t/d.so $t/a.o $t/b.o
|
||||
|
||||
$CC -B. -o $t/exe1 $t/a.o $t/b.o $t/c.o
|
||||
$QEMU $t/exe1 | grep '^0 42 1 2 3$'
|
||||
$QEMU $t/exe1 | grep '^42 1 2 3$'
|
||||
|
||||
$CC -B. -o $t/exe2 $t/c.o $t/d.so
|
||||
$QEMU $t/exe2 | grep '^0 42 1 2 3$'
|
||||
$QEMU $t/exe2 | grep '^42 1 2 3$'
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -fPIC -ftls-model=local-dynamic -fno-plt -c -o $t/a.o -xc -
|
||||
cat <<EOF | $GCC -fPIC -ftls-model=local-dynamic -fno-plt -c -o $t/a.o -xc -
|
||||
#include <stdio.h>
|
||||
|
||||
extern _Thread_local int foo;
|
||||
@@ -18,7 +18,7 @@ int main() {
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -fPIC -ftls-model=local-dynamic -fno-plt -c -o $t/b.o -xc -
|
||||
cat <<EOF | $GCC -fPIC -ftls-model=local-dynamic -fno-plt -c -o $t/b.o -xc -
|
||||
_Thread_local int foo = 3;
|
||||
EOF
|
||||
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ int main() {
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -fPIC -ftls-model=local-dynamic -c -o $t/b.o -xc -
|
||||
cat <<EOF | $GCC -fPIC -ftls-model=local-dynamic -c -o $t/b.o -xc -
|
||||
_Thread_local int foo = 3;
|
||||
EOF
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
|
||||
cat <<EOF | $GCC -fPIC -c -o $t/a.o -xc -
|
||||
__attribute__((tls_model("local-exec"))) static _Thread_local int foo = 5;
|
||||
int bar() { return foo; }
|
||||
EOF
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
|
||||
cat <<EOF | $GCC -fPIC -c -o $t/a.o -xc -
|
||||
#include <stdio.h>
|
||||
|
||||
__attribute__((tls_model("local-exec"))) extern _Thread_local int foo;
|
||||
@@ -18,7 +18,7 @@ int main() {
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/b.o -xc -
|
||||
cat <<EOF | $GCC -fPIC -c -o $t/b.o -xc -
|
||||
__attribute__((tls_model("local-exec"))) _Thread_local int foo = 3;
|
||||
EOF
|
||||
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -c -o $t/a.o -xc -
|
||||
cat <<EOF | $GCC -c -o $t/a.o -xc -
|
||||
#include <stdio.h>
|
||||
|
||||
__attribute__((tls_model("global-dynamic"))) extern _Thread_local int foo;
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
|
||||
cat <<EOF | $GCC -fPIC -c -o $t/a.o -xc -
|
||||
#include <stdio.h>
|
||||
|
||||
__attribute__((tls_model("global-dynamic"))) extern _Thread_local int foo;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
supports_tlsdesc || skip
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc - $tlsdesc_opt
|
||||
cat <<EOF | $GCC -fPIC -c -o $t/a.o -xc - $tlsdesc_opt
|
||||
static _Thread_local int foo[10000] = { 3, [9999] = 5 };
|
||||
|
||||
int get_foo(int idx) { return foo[idx]; }
|
||||
@@ -11,7 +11,7 @@ EOF
|
||||
|
||||
$CC -B. -shared -o $t/b.so $t/a.o
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/c.o -xc - $tlsdesc_opt
|
||||
cat <<EOF | $GCC -fPIC -c -o $t/c.o -xc - $tlsdesc_opt
|
||||
#include <assert.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
supports_tlsdesc || skip
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc - $tlsdesc_opt
|
||||
cat <<EOF | $GCC -fPIC -c -o $t/a.o -xc - $tlsdesc_opt
|
||||
#include <stdio.h>
|
||||
|
||||
extern _Thread_local int foo;
|
||||
@@ -15,7 +15,7 @@ int main() {
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -fPIC -shared -o $t/b.so -xc - $tlsdesc_opt
|
||||
cat <<EOF | $GCC -fPIC -shared -o $t/b.so -xc - $tlsdesc_opt
|
||||
_Thread_local int foo = 5;
|
||||
_Thread_local int bar;
|
||||
EOF
|
||||
|
||||
@@ -3,18 +3,18 @@
|
||||
|
||||
supports_tlsdesc || skip
|
||||
|
||||
cat <<EOF | $CC -fPIC -fPIC -c -o $t/a.o -xc - $tlsdesc_opt
|
||||
cat <<EOF | $GCC -fPIC -fPIC -c -o $t/a.o -xc - $tlsdesc_opt
|
||||
_Thread_local int foo = 5;
|
||||
EOF
|
||||
|
||||
$CC -B. -shared -o $t/b.so $t/a.o
|
||||
|
||||
cat <<EOF | $CC -fPIC -fPIC -c -o $t/c.o -xc - $tlsdesc_opt
|
||||
cat <<EOF | $GCC -fPIC -fPIC -c -o $t/c.o -xc - $tlsdesc_opt
|
||||
extern _Thread_local int foo;
|
||||
int get_foo1() { return foo; }
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -fPIC -fPIE -c -o $t/d.o -xc - $tlsdesc_opt
|
||||
cat <<EOF | $GCC -fPIC -fPIE -c -o $t/d.o -xc - $tlsdesc_opt
|
||||
#include <stdio.h>
|
||||
|
||||
extern _Thread_local int foo;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
supports_tlsdesc || skip
|
||||
|
||||
cat <<EOF | $CC -fPIC -ftls-model=local-dynamic -c -o $t/a.o -xc - $tlsdesc_opt
|
||||
cat <<EOF | $GCC -fPIC -ftls-model=local-dynamic -c -o $t/a.o -xc - $tlsdesc_opt
|
||||
extern _Thread_local int foo;
|
||||
|
||||
int get_foo() {
|
||||
@@ -17,7 +17,7 @@ int get_bar() {
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -fPIC -ftls-model=local-dynamic -c -o $t/b.o -xc - $tlsdesc_opt
|
||||
cat <<EOF | $GCC -fPIC -ftls-model=local-dynamic -c -o $t/b.o -xc - $tlsdesc_opt
|
||||
#include <stdio.h>
|
||||
|
||||
_Thread_local int foo;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
supports_tlsdesc || skip
|
||||
test_cflags -static || skip
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc - $tlsdesc_opt
|
||||
cat <<EOF | $GCC -fPIC -c -o $t/a.o -xc - $tlsdesc_opt
|
||||
#include <stdio.h>
|
||||
|
||||
extern _Thread_local int foo;
|
||||
@@ -15,7 +15,7 @@ int main() {
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/b.o -xc - $tlsdesc_opt
|
||||
cat <<EOF | $GCC -fPIC -c -o $t/b.o -xc - $tlsdesc_opt
|
||||
_Thread_local int foo;
|
||||
EOF
|
||||
|
||||
|
||||
+2
-2
@@ -3,7 +3,7 @@
|
||||
|
||||
supports_tlsdesc || skip
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc - $tlsdesc_opt
|
||||
cat <<EOF | $GCC -fPIC -c -o $t/a.o -xc - $tlsdesc_opt
|
||||
extern _Thread_local int foo;
|
||||
_Thread_local int bar = 3;
|
||||
|
||||
@@ -18,7 +18,7 @@ int get_baz() {
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/b.o -xc - $tlsdesc_opt
|
||||
cat <<EOF | $GCC -fPIC -c -o $t/b.o -xc - $tlsdesc_opt
|
||||
#include <stdio.h>
|
||||
|
||||
_Thread_local int foo;
|
||||
|
||||
@@ -9,4 +9,5 @@ int main() {
|
||||
}
|
||||
EOF
|
||||
|
||||
$CC -B. -shared -o $t/b.so $t/a.o |& grep 'warning: symbol type mismatch: times'
|
||||
$CC -B. -shared -o $t/a.so $t/a.o |&
|
||||
grep 'warning: symbol type mismatch: times'
|
||||
@@ -1,14 +0,0 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc - -fno-PIE
|
||||
#include <stdio.h>
|
||||
__attribute__((weak)) extern int foo;
|
||||
int main() { printf("%p\n", &foo); }
|
||||
EOF
|
||||
|
||||
if $CC -B. -o $t/exe $t/a.o -no-pie -Wl,-z,dynamic-undefined-weak >& $t/log; then
|
||||
$QEMU $t/exe | grep -F '(nil)'
|
||||
else
|
||||
grep 'recompile with -fPIE or -fPIC' $t/log
|
||||
fi
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
Vendored
+4
-3
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2023-2024 Intel Corporation
|
||||
# Copyright (c) 2023 Intel Corporation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -13,5 +13,6 @@
|
||||
# limitations under the License.
|
||||
|
||||
allocator:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: ['src/tbbmalloc/**/*', 'src/tbbmalloc_proxy/**/*', 'test/tbbmalloc/**/*']
|
||||
- 'src/tbbmalloc/**/*'
|
||||
- 'src/tbbmalloc_proxy/**/*'
|
||||
- 'test/tbbmalloc/**/*'
|
||||
+4
-4
@@ -75,7 +75,7 @@ jobs:
|
||||
export BUILD_TYPE=${BUILD_TYPE} && sphinx-build doc html
|
||||
tar -czvf html.tar.gz html/
|
||||
- name: Save docs
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v2.2.1
|
||||
with:
|
||||
name: oneTBB-html-docs-${{ env.GITHUB_SHA_SHORT }}
|
||||
path: html.tar.gz
|
||||
@@ -97,7 +97,7 @@ jobs:
|
||||
- name: Set env
|
||||
run: echo GITHUB_SHA_SHORT=${GITHUB_SHA::8} >> $GITHUB_ENV
|
||||
- name: Download documetation
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: oneTBB-html-docs-${{ env.GITHUB_SHA_SHORT }}
|
||||
- name: Publish to github pages
|
||||
@@ -198,7 +198,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: macos-14
|
||||
- os: macos-12
|
||||
c_compiler: clang
|
||||
cxx_compiler: clang++
|
||||
std: 14
|
||||
@@ -314,7 +314,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: macos-15
|
||||
- os: macos-12
|
||||
c_compiler: clang
|
||||
cxx_compiler: clang++
|
||||
std: 14
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2024-2025 Intel Corporation
|
||||
# Copyright (c) 2024 Intel Corporation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@v2.10.4
|
||||
uses: step-security/harden-runner@v2.6.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
# Copyright (c) 2024-2025 Intel Corporation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: Coverity Scan
|
||||
|
||||
on:
|
||||
|
||||
# Only run on push to master branch
|
||||
push:
|
||||
branches: [master]
|
||||
|
||||
permissions: read-all
|
||||
|
||||
env:
|
||||
BUILD_CONCURRENCY: 4
|
||||
COVERITY_PROJECT: oneapi-src%2FoneTBB
|
||||
|
||||
jobs:
|
||||
coverity_linux:
|
||||
name: Coverity Linux
|
||||
if: github.repository == 'uxlfoundation/oneTBB'
|
||||
runs-on: [ubuntu-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download Linux 64 Coverity Tool
|
||||
run: |
|
||||
curl https://scan.coverity.com/download/cxx/linux64 --output ${GITHUB_WORKSPACE}/cov-linux64-tool.tar.gz \
|
||||
--data "token=${{secrets.COVERITY_TOKEN}}&project=${{env.COVERITY_PROJECT}}"
|
||||
mkdir cov-linux64-tool
|
||||
tar -xzf cov-linux64-tool.tar.gz --strip 1 -C cov-linux64-tool
|
||||
- name: Build with cov-build
|
||||
run: |
|
||||
export PATH="${PWD}/cov-linux64-tool/bin:${PATH}"
|
||||
mkdir build && cd build
|
||||
cmake -DCMAKE_CXX_STANDARD=20 -DCMAKE_BUILD_TYPE=relwithdebinfo \
|
||||
-DCMAKE_CXX_COMPILER=g++ -DCMAKE_C_COMPILER=gcc -DTBB_CPF=ON ..
|
||||
cov-build --dir cov-int make VERBOSE=1 -j${{env.BUILD_CONCURRENCY}}
|
||||
- name: Archive Coverity build results
|
||||
run: |
|
||||
cd build
|
||||
tar -czvf cov-int.tgz cov-int
|
||||
- name: Submit Coverity results for analysis
|
||||
run: |
|
||||
cd build
|
||||
curl \
|
||||
--form token="${{ secrets.COVERITY_TOKEN }}" \
|
||||
--form email="${{ secrets.COVERITY_EMAIL }}" \
|
||||
--form [email protected] \
|
||||
--form version="${GITHUB_SHA}" \
|
||||
--form description="" \
|
||||
"https://scan.coverity.com/builds?project=${{env.COVERITY_PROJECT}}"
|
||||
+2
-1
@@ -29,8 +29,9 @@ jobs:
|
||||
issues: write
|
||||
contents: read
|
||||
steps:
|
||||
- uses: github/issue-labeler@v3.4 #May not be the latest version
|
||||
- uses: github/issue-labeler@v3.2 #May not be the latest version
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
configuration-path: .github/issue_labeler.yml
|
||||
enable-versioned-regex: 0
|
||||
sync-labels: 1
|
||||
+2
-4
@@ -24,7 +24,5 @@ jobs:
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/labeler@v5
|
||||
with:
|
||||
configuration-path: .github/labeler.yml
|
||||
- uses: actions/labeler@v4
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Run analysis"
|
||||
uses: ossf/scorecard-action@v2.4.0
|
||||
uses: ossf/scorecard-action@v2.3.1
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
|
||||
Vendored
-1
@@ -37,7 +37,6 @@ cc_library(
|
||||
]),
|
||||
copts = ["-w"] + select({
|
||||
"@platforms//os:windows": [""],
|
||||
"@platforms//cpu:arm64": [""],
|
||||
"//conditions:default": ["-mwaitpkg"],
|
||||
}),
|
||||
defines =
|
||||
|
||||
Vendored
+1
-1
@@ -40,7 +40,7 @@ load("@platforms//tools/build_defs/repo:git.bzl", "git_repository")
|
||||
git_repository(
|
||||
name = "oneTBB",
|
||||
branch = "master",
|
||||
remote = "https://github.com/uxlfoundation/oneTBB/",
|
||||
remote = "https://github.com/oneapi-src/oneTBB/",
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
Vendored
+2
-32
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2020-2025 Intel Corporation
|
||||
# Copyright (c) 2020-2024 Intel Corporation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -12,18 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# If the running version of CMake is older than 3.12, the extra ... dots will be seen as version component separators,
|
||||
# resulting in the ...<max> part being ignored and preserving the pre-3.12 behavior of basing policies on <min>.
|
||||
cmake_minimum_required(VERSION 3.5.0...3.31.3)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# Enable CMake policies
|
||||
|
||||
if (POLICY CMP0063)
|
||||
# The NEW behavior for this policy is to honor the visibility properties for all target types.
|
||||
cmake_policy(SET CMP0063 NEW)
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0063 NEW)
|
||||
endif()
|
||||
|
||||
if (POLICY CMP0068)
|
||||
# RPATH settings do not affect install_name on macOS since CMake 3.9
|
||||
cmake_policy(SET CMP0068 NEW)
|
||||
@@ -37,11 +29,6 @@ elseif (DEFINED CMAKE_MSVC_RUNTIME_LIBRARY)
|
||||
message(FATAL_ERROR "CMAKE_MSVC_RUNTIME_LIBRARY was defined while policy CMP0091 is not available. Use CMake 3.15 or newer.")
|
||||
endif()
|
||||
|
||||
if (POLICY CMP0148)
|
||||
# CMake 3.27: The FindPythonInterp and FindPythonLibs modules are removed
|
||||
cmake_policy(SET CMP0148 OLD)
|
||||
endif()
|
||||
|
||||
if (TBB_WINDOWS_DRIVER AND (NOT ("${CMAKE_MSVC_RUNTIME_LIBRARY}" STREQUAL MultiThreaded OR "${CMAKE_MSVC_RUNTIME_LIBRARY}" STREQUAL MultiThreadedDebug)))
|
||||
message(FATAL_ERROR "Enabled TBB_WINDOWS_DRIVER requires CMAKE_MSVC_RUNTIME_LIBRARY to be set to MultiThreaded or MultiThreadedDebug.")
|
||||
endif()
|
||||
@@ -97,11 +84,6 @@ endif()
|
||||
|
||||
set(CMAKE_CXX_EXTENSIONS OFF) # use -std=c++... instead of -std=gnu++...
|
||||
# ---------------------------------------------------------------------------------------------------------
|
||||
# Setup symbol visibility properties.
|
||||
|
||||
set(CMAKE_VISIBILITY_INLINES_HIDDEN TRUE)
|
||||
set(CMAKE_CXX_VISIBILITY_PRESET "hidden")
|
||||
# ---------------------------------------------------------------------------------------------------------
|
||||
|
||||
# Detect architecture (bitness).
|
||||
if (CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
@@ -123,10 +105,8 @@ option(TBB_CPF "Enable preview features of the library" OFF)
|
||||
option(TBB_FIND_PACKAGE "Enable search for external oneTBB using find_package instead of build from sources" OFF)
|
||||
option(TBB_DISABLE_HWLOC_AUTOMATIC_SEARCH "Disable HWLOC automatic search by pkg-config tool" ${CMAKE_CROSSCOMPILING})
|
||||
option(TBB_ENABLE_IPO "Enable Interprocedural Optimization (IPO) during the compilation" ON)
|
||||
option(TBB_CONTROL_FLOW_GUARD "Enable Control Flow Guard (CFG) during the compilation" OFF)
|
||||
option(TBB_FUZZ_TESTING "Enable fuzz testing" OFF)
|
||||
option(TBB_INSTALL "Enable installation" ON)
|
||||
option(TBB_FILE_TRIM "Enable __FILE__ trim" ON)
|
||||
if(LINUX)
|
||||
option(TBB_LINUX_SEPARATE_DBG "Enable separation of the debug symbols during the build" OFF)
|
||||
endif()
|
||||
@@ -249,16 +229,6 @@ if (TBB_ENABLE_IPO AND BUILD_SHARED_LIBS AND NOT ANDROID_PLATFORM AND NOT TBB_SA
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (TBB_FILE_TRIM)
|
||||
file(RELATIVE_PATH TBB_RELATIVE_BIN_PATH ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR})
|
||||
file(TO_NATIVE_PATH ${CMAKE_SOURCE_DIR} NATIVE_TBB_PROJECT_ROOT_DIR)
|
||||
file(TO_NATIVE_PATH ${TBB_RELATIVE_BIN_PATH} NATIVE_TBB_RELATIVE_BIN_PATH)
|
||||
endif ()
|
||||
|
||||
if (TBB_CONTROL_FLOW_GUARD)
|
||||
message(STATUS "Control Flow Guard (CFG) enabled")
|
||||
endif()
|
||||
|
||||
set(TBB_COMPILER_SETTINGS_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cmake/compilers/${CMAKE_CXX_COMPILER_ID}.cmake)
|
||||
if (EXISTS ${TBB_COMPILER_SETTINGS_FILE})
|
||||
include(${TBB_COMPILER_SETTINGS_FILE})
|
||||
|
||||
Vendored
+1
@@ -2,6 +2,7 @@
|
||||
|
||||
/oneTBB/src/tbb/ @pavelkumbrasev
|
||||
/oneTBB/src/tbb/ @dnmokhov
|
||||
/oneTBB/src/tbb/ @JhaShweta1
|
||||
/oneTBB/src/tbb/ @sarathnandu
|
||||
/oneTBB/include/oneapi/tbb/parallel_* @pavelkumbrasev
|
||||
/oneTBB/include/oneapi/tbb/concurrent_* @kboyarinov
|
||||
|
||||
Vendored
+4
-4
@@ -19,17 +19,17 @@ As an open source project, we welcome community contributions to oneAPI Threadin
|
||||
|
||||
## Licensing
|
||||
|
||||
Licensing is very important to open source projects. It helps ensure the software continues to be available under the terms that the author desired. The oneTBB project uses the [Apache 2.0 License](https://github.com/uxlfoundation/oneTBB/blob/master/LICENSE.txt), a permissive open source license that allows you to freely use, modify, and distribute your own products that include Apache 2.0 licensed software. By contributing to the oneTBB project, you agree to the license and copyright terms therein and release your own contributions under these terms.
|
||||
Licensing is very important to open source projects. It helps ensure the software continues to be available under the terms that the author desired. The oneTBB project uses the [Apache 2.0 License](https://github.com/oneapi-src/oneTBB/blob/master/LICENSE.txt), a permissive open source license that allows you to freely use, modify, and distribute your own products that include Apache 2.0 licensed software. By contributing to the oneTBB project, you agree to the license and copyright terms therein and release your own contributions under these terms.
|
||||
|
||||
Some imported or reused components within oneTBB use other licenses, as described in [third-party-programs.txt](https://github.com/uxlfoundation/oneTBB/blob/master/third-party-programs.txt). By carefully reviewing potential contributions, we can ensure that the community can develop products with oneTBB without concerns over patent or copyright issues.
|
||||
Some imported or reused components within oneTBB use other licenses, as described in [third-party-programs.txt](https://github.com/oneapi-src/oneTBB/blob/master/third-party-programs.txt). By carefully reviewing potential contributions, we can ensure that the community can develop products with oneTBB without concerns over patent or copyright issues.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
As a contributor, you'll want to be familiar with the oneTBB project and the repository layout. You should also know how to use it as explained in the [oneTBB documentation](https://uxlfoundation.github.io/oneTBB/) and how to set up your build development environment to configure, build, and test oneTBB as explained in the [oneTBB Build System Description](cmake/README.md).
|
||||
As a contributor, you’ll want to be familiar with the oneTBB project and the repository layout. You should also know how to use it as explained in the [oneTBB documentation](https://oneapi-src.github.io/oneTBB/) and how to set up your build development environment to configure, build, and test oneTBB as explained in the [oneTBB Build System Description](cmake/README.md).
|
||||
|
||||
## Pull Requests
|
||||
|
||||
You can find all [open oneTBB pull requests](https://github.com/uxlfoundation/oneTBB/pulls) on GitHub.
|
||||
You can find all [open oneTBB pull requests](https://github.com/oneapi-src/oneTBB/pulls) on GitHub.
|
||||
|
||||
### Before contributing changes directly to the oneTBB repository
|
||||
|
||||
|
||||
Vendored
+3
-3
@@ -61,7 +61,7 @@ You can use the ``install`` components for partial installation.
|
||||
The following install components are supported:
|
||||
- `runtime` - oneTBB runtime package (core shared libraries and `.dll` files on Windows* OS).
|
||||
- `devel` - oneTBB development package (header files, CMake integration files, library symbolic links, and `.lib` files on Windows* OS).
|
||||
- `tbb4py` - [oneTBB Module for Python](https://github.com/uxlfoundation/oneTBB/blob/master/python/README.md).
|
||||
- `tbb4py` - [oneTBB Module for Python](https://github.com/oneapi-src/oneTBB/blob/master/python/README.md).
|
||||
|
||||
If you want to install specific components after configuration and build, run:
|
||||
|
||||
@@ -99,7 +99,7 @@ The following example demonstrates how to install oneTBB for single-configuratio
|
||||
# Do our experiments in /tmp
|
||||
cd /tmp
|
||||
# Clone oneTBB repository
|
||||
git clone https://github.com/uxlfoundation/oneTBB.git
|
||||
git clone https://github.com/oneapi-src/oneTBB.git
|
||||
cd oneTBB
|
||||
# Create binary directory for out-of-source build
|
||||
mkdir build && cd build
|
||||
@@ -121,7 +121,7 @@ Choose the configuration during the build and install steps:
|
||||
REM Do our experiments in %TMP%
|
||||
cd %TMP%
|
||||
REM Clone oneTBB repository
|
||||
git clone https://github.com/uxlfoundation/oneTBB.git
|
||||
git clone https://github.com/oneapi-src/oneTBB.git
|
||||
cd oneTBB
|
||||
REM Create binary directory for out-of-source build
|
||||
mkdir build && cd build
|
||||
|
||||
Loaded 100 of 316 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user