mirror of
https://github.com/rui314/mold.git
synced 2026-11-09 13:01:26 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edc2f852a1 | ||
|
|
348e44a9c4 | ||
|
|
42f663cac0 | ||
|
|
4dd5d2f8cb | ||
|
|
4cdfc7e72c | ||
|
|
9516da1751 | ||
|
|
f3de82b2a2 | ||
|
|
c4c506c1cb | ||
|
|
0c62a91ded | ||
|
|
2fd0a4c60a | ||
|
|
d3188e39df | ||
|
|
3229b5b7e3 | ||
|
|
d9ede96aca | ||
|
|
4b4219fc33 | ||
|
|
02ead29989 | ||
|
|
37919009ab | ||
|
|
782ff4f582 | ||
|
|
3c468c71a4 | ||
|
|
a31679cf04 | ||
|
|
9cd65ed152 | ||
|
|
79ad405319 | ||
|
|
b60a164fcc | ||
|
|
2d4ba3ac74 | ||
|
|
42eabf9f71 | ||
|
|
aa4b3f7d01 | ||
|
|
2d121b8d6d | ||
|
|
c86a59a511 | ||
|
|
f7dea3a21b | ||
|
|
651ae8a2a8 | ||
|
|
5f7aca6276 | ||
|
|
39cdf61695 | ||
|
|
5c4955763b | ||
|
|
dae070283b | ||
|
|
403655904b | ||
|
|
44942b638b | ||
|
|
ceffd4679b | ||
|
|
6fa742be67 | ||
|
|
484e357971 | ||
|
|
53a5a37370 | ||
|
|
41579888b6 | ||
|
|
1e2d84038e | ||
|
|
6c7700b051 |
No files matched your search
@@ -3,15 +3,7 @@
|
||||
*~
|
||||
**/.DS_Store
|
||||
.cache
|
||||
compile_commands.json
|
||||
/mold
|
||||
/test/tmp
|
||||
options.inc
|
||||
core
|
||||
ld
|
||||
ld64
|
||||
ld64.mold
|
||||
gentoo
|
||||
out
|
||||
/mold-*.tar.gz
|
||||
/build*
|
||||
+4
-6
@@ -44,7 +44,7 @@
|
||||
# features and behave exactly the same.
|
||||
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(mold VERSION 2.3.1)
|
||||
project(mold VERSION 2.3.2)
|
||||
|
||||
include(CMakeDependentOption)
|
||||
include(GNUInstallDirs)
|
||||
@@ -160,12 +160,10 @@ endif()
|
||||
#
|
||||
# We enable mimalloc by default for 64-bit targets. It doesn't seem to
|
||||
# be stable on 32-bit targets.
|
||||
include(CheckCXXSourceCompiles)
|
||||
check_cxx_source_compiles(
|
||||
"static_assert(sizeof(void *) == 4); int main() {}" P32)
|
||||
cmake_dependent_option(
|
||||
MOLD_USE_MIMALLOC "Use mimalloc" ON
|
||||
"CMAKE_SIZEOF_VOID_P EQUAL 8; NOT APPLE; NOT ANDROID; NOT OPENBSD" OFF)
|
||||
|
||||
cmake_dependent_option(MOLD_USE_MIMALLOC "Use mimalloc" ON
|
||||
"NOT APPLE; NOT ANDROID; NOT OPENBSD; NOT P32" OFF)
|
||||
cmake_dependent_option(
|
||||
MOLD_USE_SYSTEM_MIMALLOC "Use system or vendored mimalloc" OFF
|
||||
MOLD_USE_MIMALLOC OFF)
|
||||
|
||||
@@ -71,7 +71,7 @@ necessary packages. You may need to run it as root.
|
||||
git clone https://github.com/rui314/mold.git
|
||||
mkdir mold/build
|
||||
cd mold/build
|
||||
git checkout v2.3.1
|
||||
git checkout v2.3.2
|
||||
../install-build-deps.sh
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=c++ ..
|
||||
cmake --build . -j $(nproc)
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
# This dockerfile creates a reproducible build environment for mold.
|
||||
#
|
||||
# $ docker buildx create --use
|
||||
# $ docker buildx build --platform x86_64,aarch64,arm,ppc64le,s390x -t rui314/mold-builder:latest --push - < common/Dockerfile
|
||||
|
||||
FROM ubuntu:18.04
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends software-properties-common && \
|
||||
add-apt-repository -y ppa:ubuntu-toolchain-r/test && \
|
||||
apt-get update && \
|
||||
DEBIAN_FRONTEND=noninteractive TZ=UTC apt-get install -y --no-install-recommends build-essential wget libstdc++-11-dev gcc-10 g++-10 python3 libssl-dev && \
|
||||
\
|
||||
mkdir /cmake && cd /cmake && \
|
||||
wget -O- -q https://github.com/Kitware/CMake/releases/download/v3.24.2/cmake-3.24.2.tar.gz | tar --strip-components=1 -xzf - && \
|
||||
./bootstrap --parallel=$(nproc) && make -j$(nproc) && make -j$(nproc) install && \
|
||||
rm -rf /var/lib/apt/lists /cmake
|
||||
@@ -1,8 +0,0 @@
|
||||
# This dockerfile creates a reproducible build environment for mold.
|
||||
#
|
||||
# $ docker build --platform riscv64 -t rui314/mold-builder-riscv64:latest --push - < common/Dockerfile.riscv64
|
||||
|
||||
FROM riscv64/ubuntu:20.04
|
||||
RUN apt-get update && \
|
||||
DEBIAN_FRONTEND=noninteractive TZ=UTC apt-get install -y --no-install-recommends build-essential gcc-10 g++-10 cmake && \
|
||||
rm -rf /var/lib/apt/lists
|
||||
+51
-4
@@ -23,6 +23,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <tbb/concurrent_vector.h>
|
||||
#include <tbb/enumerable_thread_specific.h>
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -639,15 +640,61 @@ public:
|
||||
return {nullptr, false};
|
||||
}
|
||||
|
||||
const char *get_key(i64 idx) {
|
||||
return entries[idx].key;
|
||||
}
|
||||
|
||||
i64 get_idx(T *value) const {
|
||||
uintptr_t addr = (uintptr_t)value - (uintptr_t)value % sizeof(Entry);
|
||||
return (Entry *)addr - entries;
|
||||
}
|
||||
|
||||
// Return a list of map entries sorted in a deterministic order.
|
||||
std::vector<Entry *> get_sorted_entries(i64 shard_idx) {
|
||||
if (nbuckets == 0)
|
||||
return {};
|
||||
|
||||
i64 shard_size = nbuckets / NUM_SHARDS;
|
||||
i64 begin = shard_idx * shard_size;
|
||||
i64 end = begin + shard_size;
|
||||
|
||||
i64 sz = 0;
|
||||
for (i64 i = begin; i < end; i++)
|
||||
if (entries[i].key)
|
||||
sz++;
|
||||
|
||||
std::vector<Entry *> vec;
|
||||
vec.reserve(sz);
|
||||
|
||||
// Since the shard is circular, we need to handle the last entries
|
||||
// as if they were next to the first entries.
|
||||
while (entries[end - 1].key)
|
||||
vec.push_back(entries + --end);
|
||||
|
||||
// Find entries contiguous in the buckets and sort them.
|
||||
i64 last = 0;
|
||||
for (i64 i = begin; i < end;) {
|
||||
while (i < end && entries[i].key)
|
||||
vec.push_back(entries + i++);
|
||||
|
||||
std::sort(vec.begin() + last, vec.end(), [](Entry *a, Entry *b) {
|
||||
if (a->keylen != b->keylen)
|
||||
return a->keylen < b->keylen;
|
||||
return memcmp(a->key, b->key, a->keylen) < 0;
|
||||
});
|
||||
|
||||
last = vec.size();
|
||||
|
||||
while (i < end && !entries[i].key)
|
||||
i++;
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
std::vector<Entry *> get_sorted_entries_all() {
|
||||
std::vector<std::vector<Entry *>> vec(NUM_SHARDS);
|
||||
tbb::parallel_for((i64)0, NUM_SHARDS, [&](i64 i) {
|
||||
vec[i] = get_sorted_entries(i);
|
||||
});
|
||||
return flatten(vec);
|
||||
}
|
||||
|
||||
static constexpr i64 MIN_NBUCKETS = 2048;
|
||||
static constexpr i64 NUM_SHARDS = 16;
|
||||
static constexpr i64 MAX_RETRY = 128;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# This script creates a mold binary distribution. The output is
|
||||
# written in this directory as `mold-$version-$arch-linux.tar.gz`
|
||||
# (e.g. `mold-1.0.3-x86_64-linux.tar.gz`).
|
||||
@@ -7,6 +8,8 @@
|
||||
# libstdc++ and libcrypto but dynamically-linked to libc, libm, libz
|
||||
# and librt, as they almost always exist on any Linux systems.
|
||||
|
||||
set -e -x
|
||||
|
||||
case $# in
|
||||
0)
|
||||
arch=$(uname -m)
|
||||
@@ -23,18 +26,48 @@ esac
|
||||
echo "$arch" | grep -Eq '^(x86_64|aarch64|arm|riscv64|ppc64le|s390x)$' || \
|
||||
{ echo "Error: no docker image for $arch"; exit 1; }
|
||||
|
||||
image=mold-builder-$arch:1
|
||||
version=$(sed -n 's/^project(mold VERSION \(.*\))/\1/p' $(dirname $0)/CMakeLists.txt)
|
||||
dest=mold-$version-$arch-linux
|
||||
set -e -x
|
||||
|
||||
if [ $arch = riscv64 ]; then
|
||||
image=rui314/mold-builder-riscv64
|
||||
else
|
||||
image=rui314/mold-builder
|
||||
# Create a Docker image if not exists.
|
||||
#
|
||||
# We want to use a reasonably old Linux distro because we'll dynamically
|
||||
# link glibc to mold, and a binary linked against a newer version of
|
||||
# glibc won't work on a system with a older version of glibc.
|
||||
#
|
||||
# We might want to instead statically-link everything, but that'll
|
||||
# disable dlopen(), which means we can't use the linker plugin for LTO.
|
||||
# So we don't want to do that.
|
||||
if ! docker image ls $image | grep -q $image; then
|
||||
if [ $arch = riscv64 ]; then
|
||||
# Ubuntu 18 didn't support RISC-V, so use Ubuntu 20 instead
|
||||
cat <<EOF | docker build --platform $arch -t $image -
|
||||
FROM riscv64/ubuntu:20.04
|
||||
RUN apt-get update && \
|
||||
DEBIAN_FRONTEND=noninteractive TZ=UTC apt-get install -y --no-install-recommends build-essential gcc-10 g++-10 cmake && \
|
||||
rm -rf /var/lib/apt/lists
|
||||
EOF
|
||||
else
|
||||
cat <<EOF | docker build --platform $arch -t $image -
|
||||
FROM ubuntu:18.04
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends software-properties-common && \
|
||||
add-apt-repository -y ppa:ubuntu-toolchain-r/test && \
|
||||
apt-get update && \
|
||||
DEBIAN_FRONTEND=noninteractive TZ=UTC apt-get install -y --no-install-recommends build-essential wget libstdc++-11-dev gcc-10 g++-10 python3 libssl-dev && \
|
||||
\
|
||||
mkdir /cmake && cd /cmake && \
|
||||
wget -O- -q https://github.com/Kitware/CMake/releases/download/v3.24.2/cmake-3.24.2.tar.gz | tar --strip-components=1 -xzf - && \
|
||||
./bootstrap --parallel=$(nproc) && make -j$(nproc) && make -j$(nproc) install && \
|
||||
rm -rf /var/lib/apt/lists /cmake
|
||||
EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
docker run --platform linux/$arch -i --rm -v "$(pwd):/mold" \
|
||||
-e "OWNER=$(id -u):$(id -g)" $image:latest \
|
||||
# Build mold in a container.
|
||||
docker run --platform linux/$arch -i --rm -v "$(realpath $(dirname $0)):/mold" \
|
||||
-e "OWNER=$(id -u):$(id -g)" $image \
|
||||
bash -c "mkdir /build &&
|
||||
cd /build &&
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 -DMOLD_MOSTLY_STATIC=On /mold &&
|
||||
@@ -42,5 +75,4 @@ cmake --build . -j\$(nproc) &&
|
||||
[ $arch = arm ] || ctest -j\$(nproc) &&
|
||||
cmake --install . --prefix $dest --strip &&
|
||||
tar czf /mold/$dest.tar.gz $dest &&
|
||||
cp mold mold-wrapper.so /mold &&
|
||||
chown \$OWNER /mold/mold /mold/mold-wrapper.so /mold/$dest.tar.gz"
|
||||
chown \$OWNER /mold/$dest.tar.gz"
|
||||
+15
-13
@@ -764,22 +764,24 @@ arguments.
|
||||
## ENVIRONMENT VARIABLES
|
||||
|
||||
* `MOLD_JOBS`:
|
||||
If this variable is set to `1`, only one process of `mold` runs actively. A
|
||||
mold process invoked while another active mold process is running will wait
|
||||
before doing anything until the active process exits.
|
||||
If this variable is set to `1`, only one `mold` process will run at a time.
|
||||
If a new mold process is initiated while another is already active, the new
|
||||
process will wait until the active one completes before starting.
|
||||
|
||||
The purpose of this environment variable is to reduce peak memory usage.
|
||||
Since mold is highly parallelized, there's no point in running it
|
||||
simultaneously. If you run N instances of mold in parallel, it would take N
|
||||
times more time and N times more memory. If you run them serially, it would
|
||||
still take N times more to finish, but their peak memory usage is reduced to
|
||||
normal.
|
||||
The primary reason for this environment variable is to minimize peak memory
|
||||
usage. Since mold is designed to operate with high parallelism, running
|
||||
multiple mold instances simultaneously may not be beneficial. If you execute
|
||||
N instances of mold concurrently, it could require N times the time and N
|
||||
times the memory. On the other hand, running them one after the other might
|
||||
still take N times longer, but the peak memory usage would be the same as
|
||||
running just a single instance.
|
||||
|
||||
If your build system tends to invoke multiple linker processes
|
||||
simultaneously, you may want to try to set this environment variable to
|
||||
`1` to see if it could improve overall performance.
|
||||
If your build system invokes multiple linker processes simultaneously and
|
||||
some of them often get killed due to out-of-memory errors, you might
|
||||
consider setting this environment variable to `1` to see if it addresses the
|
||||
OOM issue.
|
||||
|
||||
Currently, any value other than 1 is silently ignored.
|
||||
Currently, any value other than `1` is silently ignored.
|
||||
|
||||
* `MOLD_DEBUG`:
|
||||
If this variable is set to a non-empty string, `mold` embeds its
|
||||
|
||||
+4
-4
@@ -200,9 +200,9 @@ void write_plt_header(Context<E> &ctx, u8 *buf) {
|
||||
0xe08f'e00e, // 1: add lr, pc, lr
|
||||
0xe5be'f008, // ldr pc, [lr, #8]!
|
||||
0x0000'0000, // 2: .word .got.plt - 1b - 8
|
||||
0xe320'f000, // nop
|
||||
0xe320'f000, // nop
|
||||
0xe320'f000, // nop
|
||||
0x0000'0000, // (padding)
|
||||
0x0000'0000, // (padding)
|
||||
0x0000'0000, // (padding)
|
||||
};
|
||||
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
@@ -225,7 +225,7 @@ void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
template <>
|
||||
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
memcpy(buf, plt_entry, sizeof(plt_entry));
|
||||
*(ul32 *)(buf + 12) = sym.get_got_addr(ctx) - sym.get_plt_addr(ctx) - 12;
|
||||
*(ul32 *)(buf + 12) = sym.get_got_pltgot_addr(ctx) - sym.get_plt_addr(ctx) - 12;
|
||||
}
|
||||
|
||||
// ARM does not use .eh_frame for exception handling. Instead, it uses
|
||||
|
||||
+1
-1
@@ -93,7 +93,7 @@ void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
0xd503'201f, // nop
|
||||
};
|
||||
|
||||
u64 got = sym.get_got_addr(ctx);
|
||||
u64 got = sym.get_got_pltgot_addr(ctx);
|
||||
u64 plt = sym.get_plt_addr(ctx);
|
||||
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
|
||||
+2
-2
@@ -164,7 +164,7 @@ void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // (padding)
|
||||
};
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(ul32 *)(buf + 6) = sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr;
|
||||
*(ul32 *)(buf + 6) = sym.get_got_pltgot_addr(ctx) - ctx.got->shdr.sh_addr;
|
||||
} else {
|
||||
static const u8 insn[] = {
|
||||
0xf3, 0x0f, 0x1e, 0xfb, // endbr32
|
||||
@@ -172,7 +172,7 @@ void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // (padding)
|
||||
};
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(ul32 *)(buf + 6) = sym.get_got_addr(ctx);
|
||||
*(ul32 *)(buf + 6) = sym.get_got_pltgot_addr(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-7
@@ -172,7 +172,7 @@ void write_plt_entry<E>(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
|
||||
template <>
|
||||
void write_pltgot_entry<E>(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
u64 got = sym.get_got_addr(ctx);
|
||||
u64 got = sym.get_got_pltgot_addr(ctx);
|
||||
u64 plt = sym.get_plt_addr(ctx);
|
||||
|
||||
memcpy(buf, E::is_64 ? plt_entry_64 : plt_entry_32, E::plt_size);
|
||||
@@ -323,7 +323,14 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
write_k12(loc, (S + A) >> 52);
|
||||
break;
|
||||
case R_LARCH_PCALA_LO12:
|
||||
write_k12(loc, S + A);
|
||||
// It looks like R_LARCH_PCALA_LO12 is sometimes used for JIRL even
|
||||
// though the instruction takes a 16 bit immediate rather than 12 bits.
|
||||
// It is contrary to the psABI document, but GNU ld has special
|
||||
// code to handle it, so we accept it too.
|
||||
if ((*(ul32 *)loc & 0xfc00'0000) == 0x4c00'0000)
|
||||
write_k16(loc, sign_extend(S + A, 11) >> 2);
|
||||
else
|
||||
write_k12(loc, S + A);
|
||||
break;
|
||||
case R_LARCH_PCALA_HI20:
|
||||
write_j20(loc, hi20(S + A, P));
|
||||
@@ -653,10 +660,8 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
template <>
|
||||
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
|
||||
static const ul32 insn[] = {
|
||||
0x1a00'000c, // pcalau12i $t0, 0
|
||||
0x02c0'018c, // addi.d $t0, $t0, 0
|
||||
0x1e00'000c, // pcaddu18i $t0, 0
|
||||
0x4c00'0180, // jirl $zero, $t0, 0
|
||||
0x0340'0000, // nop
|
||||
};
|
||||
|
||||
static_assert(E::thunk_size == sizeof(insn));
|
||||
@@ -668,8 +673,8 @@ void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
|
||||
u64 S = sym->get_addr(ctx);
|
||||
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
write_j20(buf, hi20(S, P));
|
||||
write_k12(buf + 4, S);
|
||||
write_j20(buf, (S - P + 0x20000) >> 18);
|
||||
write_k16(buf + 4, (S - P) >> 2);
|
||||
|
||||
buf += sizeof(insn);
|
||||
P += sizeof(insn);
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
};
|
||||
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(ub32 *)(buf + 4) = sym.get_got_addr(ctx) - sym.get_plt_addr(ctx) - 2;
|
||||
*(ub32 *)(buf + 4) = sym.get_got_pltgot_addr(ctx) - sym.get_plt_addr(ctx) - 2;
|
||||
}
|
||||
|
||||
template <>
|
||||
|
||||
+1
-1
@@ -120,7 +120,7 @@ void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
memcpy(buf, plt_entry, sizeof(plt_entry));
|
||||
|
||||
ub32 *loc = (ub32 *)buf;
|
||||
i64 offset = sym.get_got_addr(ctx) - sym.get_plt_addr(ctx) - 8;
|
||||
i64 offset = sym.get_got_pltgot_addr(ctx) - sym.get_plt_addr(ctx) - 8;
|
||||
loc[4] |= higha(offset);
|
||||
loc[5] |= lo(offset);
|
||||
}
|
||||
|
||||
+1
-1
@@ -198,7 +198,7 @@ void write_plt_entry<E>(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
|
||||
template <>
|
||||
void write_pltgot_entry<E>(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
u64 got = sym.get_got_addr(ctx);
|
||||
u64 got = sym.get_got_pltgot_addr(ctx);
|
||||
u64 plt = sym.get_plt_addr(ctx);
|
||||
|
||||
memcpy(buf, E::is_64 ? plt_entry_64 : plt_entry_32, E::plt_size);
|
||||
|
||||
+1
-1
@@ -90,7 +90,7 @@ void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
};
|
||||
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(ub32 *)(buf + 2) = (sym.get_got_addr(ctx) - sym.get_plt_addr(ctx)) >> 1;
|
||||
*(ub32 *)(buf + 2) = (sym.get_got_pltgot_addr(ctx) - sym.get_plt_addr(ctx)) >> 1;
|
||||
}
|
||||
|
||||
template <>
|
||||
|
||||
+2
-2
@@ -169,7 +169,7 @@ void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
|
||||
static_assert(sizeof(insn) == E::pltgot_size);
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(ul32 *)(buf + 8) = sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr;
|
||||
*(ul32 *)(buf + 8) = sym.get_got_pltgot_addr(ctx) - ctx.got->shdr.sh_addr;
|
||||
} else {
|
||||
static const u8 insn[] = {
|
||||
0x01, 0xd0, // mov.l 1f, r0
|
||||
@@ -181,7 +181,7 @@ void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
|
||||
static_assert(sizeof(insn) == E::pltgot_size);
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(ul32 *)(buf + 8) = sym.get_got_addr(ctx);
|
||||
*(ul32 *)(buf + 8) = sym.get_got_pltgot_addr(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -115,7 +115,7 @@ void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
};
|
||||
|
||||
memcpy(buf, entry, sizeof(entry));
|
||||
*(ub64 *)(buf + 24) = sym.get_got_addr(ctx) - sym.get_plt_addr(ctx) - 4;
|
||||
*(ub64 *)(buf + 24) = sym.get_got_pltgot_addr(ctx) - sym.get_plt_addr(ctx) - 4;
|
||||
}
|
||||
|
||||
template <>
|
||||
|
||||
+1
-1
@@ -98,7 +98,7 @@ void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
};
|
||||
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(ul32 *)(buf + 2) = sym.get_got_addr(ctx) - sym.get_plt_addr(ctx) - 6;
|
||||
*(ul32 *)(buf + 2) = sym.get_got_pltgot_addr(ctx) - sym.get_plt_addr(ctx) - 6;
|
||||
}
|
||||
|
||||
template <>
|
||||
|
||||
@@ -1264,6 +1264,14 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
if (ctx.arg.shared)
|
||||
ctx.overwrite_output_file = false;
|
||||
|
||||
if (!ctx.arg.chroot.empty()) {
|
||||
if (!ctx.arg.Map.empty())
|
||||
ctx.arg.Map = ctx.arg.chroot + "/" + ctx.arg.Map;
|
||||
|
||||
if (!ctx.arg.dependency_file.empty())
|
||||
ctx.arg.dependency_file = ctx.arg.chroot + "/" + ctx.arg.dependency_file;
|
||||
}
|
||||
|
||||
if (version_shown && remaining.empty())
|
||||
exit(0);
|
||||
return remaining;
|
||||
|
||||
@@ -1868,6 +1868,7 @@ struct X86_64 {
|
||||
static constexpr u32 plt_hdr_size = 32;
|
||||
static constexpr u32 plt_size = 16;
|
||||
static constexpr u32 pltgot_size = 8;
|
||||
static constexpr u8 filler[] = { 0xcc };
|
||||
|
||||
static constexpr u32 R_COPY = R_X86_64_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_X86_64_GLOB_DAT;
|
||||
@@ -1892,6 +1893,7 @@ struct I386 {
|
||||
static constexpr u32 plt_hdr_size = 16;
|
||||
static constexpr u32 plt_size = 16;
|
||||
static constexpr u32 pltgot_size = 16;
|
||||
static constexpr u8 filler[] = { 0xcc };
|
||||
|
||||
static constexpr u32 R_COPY = R_386_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_386_GLOB_DAT;
|
||||
@@ -1918,6 +1920,7 @@ struct ARM64 {
|
||||
static constexpr u32 pltgot_size = 16;
|
||||
static constexpr u32 thunk_hdr_size = 0;
|
||||
static constexpr u32 thunk_size = 16;
|
||||
static constexpr u8 filler[] = { 0x00, 0x7d, 0x20, 0xd4 };
|
||||
|
||||
static constexpr u32 R_COPY = R_AARCH64_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_AARCH64_GLOB_DAT;
|
||||
@@ -1944,6 +1947,7 @@ struct ARM32 {
|
||||
static constexpr u32 pltgot_size = 16;
|
||||
static constexpr u32 thunk_hdr_size = 16;
|
||||
static constexpr u32 thunk_size = 16;
|
||||
static constexpr u8 filler[] = { 0xff, 0xde };
|
||||
|
||||
static constexpr u32 R_COPY = R_ARM_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_ARM_GLOB_DAT;
|
||||
@@ -1969,6 +1973,7 @@ struct RV64 {
|
||||
static constexpr u32 plt_hdr_size = 32;
|
||||
static constexpr u32 plt_size = 16;
|
||||
static constexpr u32 pltgot_size = 16;
|
||||
static constexpr u8 filler[] = { 0x02, 0x90 };
|
||||
|
||||
static constexpr u32 R_COPY = R_RISCV_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_RISCV_64;
|
||||
@@ -2001,6 +2006,7 @@ struct RV32 {
|
||||
static constexpr u32 plt_hdr_size = 32;
|
||||
static constexpr u32 plt_size = 16;
|
||||
static constexpr u32 pltgot_size = 16;
|
||||
static constexpr u8 filler[] = { 0x02, 0x90 };
|
||||
|
||||
static constexpr u32 R_COPY = R_RISCV_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_RISCV_32;
|
||||
@@ -2037,7 +2043,7 @@ struct PPC32 {
|
||||
static constexpr u32 pltgot_size = 36;
|
||||
static constexpr u32 thunk_hdr_size = 0;
|
||||
static constexpr u32 thunk_size = 36;
|
||||
|
||||
static constexpr u8 filler[] = { 0x7f, 0xe0, 0x00, 0x08 };
|
||||
|
||||
static constexpr u32 R_COPY = R_PPC_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_PPC_GLOB_DAT;
|
||||
@@ -2079,6 +2085,7 @@ struct PPC64V1 : PPC64 {
|
||||
static constexpr u32 pltgot_size = 0;
|
||||
static constexpr u32 thunk_hdr_size = 0;
|
||||
static constexpr u32 thunk_size = 28;
|
||||
static constexpr u8 filler[] = { 0x7f, 0xe0, 0x00, 0x08 };
|
||||
};
|
||||
|
||||
struct PPC64V2 : PPC64 {
|
||||
@@ -2089,6 +2096,7 @@ struct PPC64V2 : PPC64 {
|
||||
static constexpr u32 pltgot_size = 0;
|
||||
static constexpr u32 thunk_hdr_size = 0;
|
||||
static constexpr u32 thunk_size = 24;
|
||||
static constexpr u8 filler[] = { 0x08, 0x00, 0xe0, 0x7f };
|
||||
};
|
||||
|
||||
struct S390X {
|
||||
@@ -2101,6 +2109,7 @@ struct S390X {
|
||||
static constexpr u32 plt_hdr_size = 48;
|
||||
static constexpr u32 plt_size = 16;
|
||||
static constexpr u32 pltgot_size = 16;
|
||||
static constexpr u8 filler[] = { 0x07, 0x00 };
|
||||
|
||||
static constexpr u32 R_COPY = R_390_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_390_GLOB_DAT;
|
||||
@@ -2124,6 +2133,7 @@ struct SPARC64 {
|
||||
static constexpr u32 plt_hdr_size = 128;
|
||||
static constexpr u32 plt_size = 32;
|
||||
static constexpr u32 pltgot_size = 32;
|
||||
static constexpr u8 filler[] = { 0x91, 0xd0, 0x20, 0x05 };
|
||||
|
||||
static constexpr u32 R_COPY = R_SPARC_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_SPARC_GLOB_DAT;
|
||||
@@ -2147,6 +2157,7 @@ struct M68K {
|
||||
static constexpr u32 plt_hdr_size = 18;
|
||||
static constexpr u32 plt_size = 14;
|
||||
static constexpr u32 pltgot_size = 8;
|
||||
static constexpr u8 filler[] = { 0x4a, 0xfc };
|
||||
|
||||
static constexpr u32 R_COPY = R_68K_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_68K_GLOB_DAT;
|
||||
@@ -2169,6 +2180,7 @@ struct SH4 {
|
||||
static constexpr u32 plt_hdr_size = 16;
|
||||
static constexpr u32 plt_size = 16;
|
||||
static constexpr u32 pltgot_size = 12;
|
||||
static constexpr u8 filler[] = { 0x00, 0x00 };
|
||||
|
||||
static constexpr u32 R_COPY = R_SH_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_SH_GLOB_DAT;
|
||||
@@ -2191,6 +2203,7 @@ struct ALPHA {
|
||||
static constexpr u32 plt_hdr_size = 0;
|
||||
static constexpr u32 plt_size = 0;
|
||||
static constexpr u32 pltgot_size = 0;
|
||||
static constexpr u8 filler[] = { 0x81, 0x00, 0x00, 0x00 };
|
||||
|
||||
static constexpr u32 R_COPY = R_ALPHA_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_ALPHA_GLOB_DAT;
|
||||
@@ -2214,7 +2227,8 @@ struct LOONGARCH64 {
|
||||
static constexpr u32 plt_size = 16;
|
||||
static constexpr u32 pltgot_size = 16;
|
||||
static constexpr u32 thunk_hdr_size = 0;
|
||||
static constexpr u32 thunk_size = 16;
|
||||
static constexpr u32 thunk_size = 8;
|
||||
static constexpr u8 filler[] = { 0x00, 0x00, 0x2a, 0x00 };
|
||||
|
||||
static constexpr u32 R_COPY = R_LARCH_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_LARCH_64;
|
||||
@@ -2239,7 +2253,8 @@ struct LOONGARCH32 {
|
||||
static constexpr u32 plt_size = 16;
|
||||
static constexpr u32 pltgot_size = 16;
|
||||
static constexpr u32 thunk_hdr_size = 0;
|
||||
static constexpr u32 thunk_size = 16;
|
||||
static constexpr u32 thunk_size = 8;
|
||||
static constexpr u8 filler[] = { 0x00, 0x00, 0x2a, 0x00 };
|
||||
|
||||
static constexpr u32 R_COPY = R_LARCH_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_LARCH_32;
|
||||
|
||||
+17
-32
@@ -140,7 +140,6 @@ struct NameType {
|
||||
};
|
||||
|
||||
struct MapValue {
|
||||
std::string_view name;
|
||||
u32 hash = 0;
|
||||
Atomic<u32> count = 0;
|
||||
u32 name_offset = 0;
|
||||
@@ -678,36 +677,21 @@ void write_gdb_index(Context<E> &ctx) {
|
||||
});
|
||||
|
||||
ConcurrentMap<MapValue> map(estimator.get_cardinality() * 3 / 2);
|
||||
Atomic<i64> num_entries;
|
||||
|
||||
tbb::parallel_for_each(cus, [&](Compunit &cu) {
|
||||
cu.entries.reserve(cu.nametypes.size());
|
||||
i64 count = 0;
|
||||
|
||||
for (NameType &nt : cu.nametypes) {
|
||||
MapValue *ent;
|
||||
bool inserted;
|
||||
MapValue value = {nt.name, nt.hash};
|
||||
std::tie(ent, inserted) = map.insert(nt.name, nt.hash, value);
|
||||
std::tie(ent, inserted) = map.insert(nt.name, nt.hash, MapValue{nt.hash});
|
||||
ent->count++;
|
||||
if (inserted)
|
||||
count++;
|
||||
cu.entries.push_back(ent);
|
||||
}
|
||||
num_entries += count;
|
||||
});
|
||||
|
||||
// Sort symbols for build reproducibility
|
||||
std::vector<MapValue *> entries;
|
||||
entries.reserve(num_entries);
|
||||
|
||||
for (i64 i = 0; i < map.nbuckets; i++)
|
||||
if (map.entries[i].key)
|
||||
entries.push_back(&map.entries[i].value);
|
||||
|
||||
tbb::parallel_sort(entries, [](MapValue *a, MapValue *b) {
|
||||
return std::tuple(a->hash, a->name) < std::tuple(b->hash, b->name);
|
||||
});
|
||||
using Entry = typename decltype(map)::Entry;
|
||||
std::vector<Entry *> entries = map.get_sorted_entries_all();
|
||||
|
||||
// Compute sizes of each components
|
||||
SectionHeader hdr;
|
||||
@@ -719,18 +703,18 @@ void write_gdb_index(Context<E> &ctx) {
|
||||
for (Compunit &cu : cus)
|
||||
hdr.symtab_offset += cu.ranges.size() * 20;
|
||||
|
||||
i64 ht_size = bit_ceil(num_entries * 5 / 4 + 1);
|
||||
i64 ht_size = bit_ceil(entries.size() * 5 / 4 + 1);
|
||||
hdr.const_pool_offset = hdr.symtab_offset + ht_size * 8;
|
||||
|
||||
i64 offset = 0;
|
||||
for (MapValue *ent : entries) {
|
||||
ent->type_offset = offset;
|
||||
offset += ent->count * 4 + 4;
|
||||
for (Entry *ent : entries) {
|
||||
ent->value.type_offset = offset;
|
||||
offset += ent->value.count * 4 + 4;
|
||||
}
|
||||
|
||||
for (MapValue *ent : entries) {
|
||||
ent->name_offset = offset;
|
||||
offset += ent->name.size() + 1;
|
||||
for (Entry *ent : entries) {
|
||||
ent->value.name_offset = offset;
|
||||
offset += ent->keylen + 1;
|
||||
}
|
||||
|
||||
i64 bufsize = hdr.const_pool_offset + offset;
|
||||
@@ -765,16 +749,16 @@ void write_gdb_index(Context<E> &ctx) {
|
||||
u32 mask = ht_size - 1;
|
||||
ul32 *ht = (ul32 *)(buf + hdr.symtab_offset);
|
||||
|
||||
for (MapValue *ent : entries) {
|
||||
u32 hash = ent->hash;
|
||||
for (Entry *ent : entries) {
|
||||
u32 hash = ent->value.hash;
|
||||
u32 step = ((hash * 17) & mask) | 1;
|
||||
u32 j = hash & mask;
|
||||
|
||||
while (ht[j * 2] || ht[j * 2 + 1])
|
||||
j = (j + step) & mask;
|
||||
|
||||
ht[j * 2] = ent->name_offset;
|
||||
ht[j * 2 + 1] = ent->type_offset;
|
||||
ht[j * 2] = ent->value.name_offset;
|
||||
ht[j * 2 + 1] = ent->value.type_offset;
|
||||
}
|
||||
|
||||
// Write types
|
||||
@@ -790,8 +774,9 @@ void write_gdb_index(Context<E> &ctx) {
|
||||
}
|
||||
|
||||
// Write names
|
||||
tbb::parallel_for_each(entries, [&](MapValue *ent) {
|
||||
write_string(buf + hdr.const_pool_offset + ent->name_offset, ent->name);
|
||||
tbb::parallel_for_each(entries, [&](Entry *ent) {
|
||||
memcpy(buf + hdr.const_pool_offset + ent->value.name_offset,
|
||||
ent->key, ent->keylen);
|
||||
});
|
||||
|
||||
// Update the section size and rewrite the section header
|
||||
|
||||
+8
-22
@@ -249,15 +249,7 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
|
||||
break;
|
||||
}
|
||||
case SHT_REL:
|
||||
if constexpr (E::is_rela)
|
||||
Fatal(ctx) << *this << ": REL-type relocation table is not supported"
|
||||
<< " for this target";
|
||||
break;
|
||||
case SHT_RELA:
|
||||
if constexpr (!E::is_rela)
|
||||
Fatal(ctx) << *this <<": RELA-type relocation table is not supported"
|
||||
<< " for this target";
|
||||
break;
|
||||
case SHT_SYMTAB:
|
||||
case SHT_SYMTAB_SHNDX:
|
||||
case SHT_STRTAB:
|
||||
@@ -645,9 +637,16 @@ split_section(Context<E> &ctx, InputSection<E> &sec) {
|
||||
if (!(shdr.sh_flags & SHF_MERGE))
|
||||
return nullptr;
|
||||
|
||||
i64 entsize = shdr.sh_entsize;
|
||||
if (entsize == 0)
|
||||
entsize = (shdr.sh_flags & SHF_STRINGS) ? 1 : (int)shdr.sh_addralign;
|
||||
|
||||
if (entsize == 0)
|
||||
return nullptr;
|
||||
|
||||
std::unique_ptr<MergeableSection<E>> rec(new MergeableSection<E>);
|
||||
rec->parent = MergedSection<E>::get_instance(ctx, sec.name(), shdr.sh_type,
|
||||
shdr.sh_flags);
|
||||
shdr.sh_flags, entsize);
|
||||
rec->p2align = sec.p2align;
|
||||
|
||||
if (sec.sh_size == 0)
|
||||
@@ -658,18 +657,10 @@ split_section(Context<E> &ctx, InputSection<E> &sec) {
|
||||
|
||||
std::string_view data = sec.contents;
|
||||
const char *begin = data.data();
|
||||
u64 entsize = shdr.sh_entsize;
|
||||
HyperLogLog estimator;
|
||||
|
||||
// Split sections
|
||||
if (shdr.sh_flags & SHF_STRINGS) {
|
||||
if (entsize == 0) {
|
||||
// GHC (Glasgow Haskell Compiler) sometimes creates a mergeable
|
||||
// string section with entsize of 0 instead of 1, though such
|
||||
// entsize is technically wrong. This is a workaround for the issue.
|
||||
entsize = 1;
|
||||
}
|
||||
|
||||
while (!data.empty()) {
|
||||
size_t end = find_null(data, entsize);
|
||||
if (end == data.npos)
|
||||
@@ -686,11 +677,6 @@ split_section(Context<E> &ctx, InputSection<E> &sec) {
|
||||
estimator.insert(hash);
|
||||
}
|
||||
} else {
|
||||
// OCaml compiler seems to create a mergeable non-string section with
|
||||
// entisze of 0. Such section is malformed. We do not split such section.
|
||||
if (entsize == 0)
|
||||
return nullptr;
|
||||
|
||||
if (data.size() % entsize)
|
||||
Fatal(ctx) << sec << ": section size is not multiple of sh_entsize";
|
||||
|
||||
|
||||
+14
-13
@@ -7,7 +7,8 @@
|
||||
namespace mold::elf {
|
||||
|
||||
typedef enum {
|
||||
NONE, ERROR, COPYREL, DYN_COPYREL, PLT, CPLT, DYN_CPLT, DYNREL, BASEREL, IFUNC,
|
||||
NONE, ERROR, COPYREL, DYN_COPYREL, PLT, CPLT, DYN_CPLT, DYNREL,
|
||||
BASEREL, IFUNC_DYNREL,
|
||||
} Action;
|
||||
|
||||
template <typename E>
|
||||
@@ -203,7 +204,7 @@ static void scan_rel(Context<E> &ctx, InputSection<E> &isec, Symbol<E> &sym,
|
||||
if (!isec.is_relr_reloc(ctx, rel))
|
||||
isec.file.num_dynrel++;
|
||||
break;
|
||||
case IFUNC:
|
||||
case IFUNC_DYNREL:
|
||||
// Create an IRELATIVE relocation for a GNU ifunc symbol.
|
||||
//
|
||||
// We usually create an IRELATIVE relocation in .got for each ifunc.
|
||||
@@ -273,7 +274,7 @@ static Action get_absrel_action(Context<E> &ctx, Symbol<E> &sym) {
|
||||
template <typename E>
|
||||
static Action get_dyn_absrel_action(Context<E> &ctx, Symbol<E> &sym) {
|
||||
if (sym.is_ifunc())
|
||||
return IFUNC;
|
||||
return ctx.arg.pic ? IFUNC_DYNREL : NONE;
|
||||
|
||||
// This is a decision table for absolute relocations for the pointer
|
||||
// size data (e.g. R_X86_64_64). Unlike the absrel_table, we can emit
|
||||
@@ -291,7 +292,7 @@ static Action get_dyn_absrel_action(Context<E> &ctx, Symbol<E> &sym) {
|
||||
template <typename E>
|
||||
static Action get_ppc64_toc_action(Context<E> &ctx, Symbol<E> &sym) {
|
||||
if (sym.is_ifunc())
|
||||
return IFUNC;
|
||||
return IFUNC_DYNREL;
|
||||
|
||||
// As a special case, we do not create copy relocations nor canonical
|
||||
// PLTs for .toc sections. PPC64's .toc is a compiler-generated
|
||||
@@ -406,7 +407,7 @@ static void apply_absrel(Context<E> &ctx, InputSection<E> &isec,
|
||||
case DYNREL:
|
||||
emit_abs_dynrel();
|
||||
break;
|
||||
case IFUNC:
|
||||
case IFUNC_DYNREL:
|
||||
if constexpr (supports_ifunc<E>) {
|
||||
u64 addr = sym.get_addr(ctx, NO_PLT) + A;
|
||||
*dynrel++ = ElfRel<E>(P, E::R_IRELATIVE, 0, addr);
|
||||
@@ -458,14 +459,14 @@ void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
|
||||
apply_reloc_nonalloc(ctx, buf);
|
||||
|
||||
if constexpr (is_x86_64<E>) {
|
||||
if (ctx.arg.z_rewrite_endbr) {
|
||||
// Rewrite the leading endbr instruction with a nop if the section
|
||||
// is not address-taken.
|
||||
u8 endbr[] = {0xf3, 0x0f, 0x1e, 0xfa};
|
||||
u8 nop[] = {0x0f, 0x1f, 0x40, 0x00};
|
||||
if (!address_taken && (shdr().sh_flags & SHF_EXECINSTR) &&
|
||||
sh_size >= 4 && memcmp(buf, endbr, 4) == 0)
|
||||
memcpy(buf, nop, 4);
|
||||
u8 endbr[] = {0xf3, 0x0f, 0x1e, 0xfa};
|
||||
u8 nop[] = {0x0f, 0x1f, 0x40, 0x00};
|
||||
|
||||
// Rewrite the leading endbr instruction with a nop if the section
|
||||
// is not address-taken.
|
||||
if (ctx.arg.z_rewrite_endbr && (shdr().sh_flags & SHF_EXECINSTR) &&
|
||||
!address_taken && sh_size >= 4 && memcmp(buf, endbr, 4) == 0) {
|
||||
memcpy(buf, nop, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+17
-5
@@ -1,3 +1,14 @@
|
||||
// Many build systems attempt to invoke as many linker processes as there
|
||||
// are cores, based on the assumption that the linker is single-threaded.
|
||||
// However, since mold is multi-threaded, such build systems' behavior is
|
||||
// not beneficial and just increases the overall peak memory usage.
|
||||
// On machines with limited memory, this could lead to an out-of-memory
|
||||
// error.
|
||||
//
|
||||
// This file implements a feature that limits the number of concurrent
|
||||
// mold processes to just 1 for each user. It is intended to be used as
|
||||
// `MOLD_JOBS=1 ninja` or `MOLD_JOBS=1 make -j$(nproc)`.
|
||||
|
||||
#include "mold.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
@@ -14,14 +25,15 @@ template <typename E>
|
||||
void acquire_global_lock(Context<E> &ctx) {
|
||||
#ifndef _WIN32
|
||||
char *jobs = getenv("MOLD_JOBS");
|
||||
if (!jobs || std::string(jobs) != "1")
|
||||
if (!jobs || jobs != "1"s)
|
||||
return;
|
||||
|
||||
char *home = getenv("HOME");
|
||||
if (!home)
|
||||
home = getpwuid(getuid())->pw_dir;
|
||||
std::string path;
|
||||
if (char *dir = getenv("XDG_RUNTIME_DIR"))
|
||||
path = dir + "/mold-lock"s;
|
||||
else
|
||||
path = "/tmp/mold-lock-"s + getpwuid(getuid())->pw_name;
|
||||
|
||||
std::string path = std::string(home) + "/.mold-lock";
|
||||
int fd = open(path.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, 0600);
|
||||
if (fd == -1)
|
||||
return;
|
||||
|
||||
+36
-2
@@ -622,6 +622,12 @@ public:
|
||||
}
|
||||
|
||||
void update_shdr(Context<E> &ctx) override;
|
||||
void copy_buf(Context<E> &ctx) override;
|
||||
|
||||
// Offsets in .strtab for ARM32 mapping symbols
|
||||
static constexpr i64 ARM = 1;
|
||||
static constexpr i64 THUMB = 4;
|
||||
static constexpr i64 DATA = 7;
|
||||
};
|
||||
|
||||
template <typename E>
|
||||
@@ -761,7 +767,8 @@ template <typename E>
|
||||
class MergedSection : public Chunk<E> {
|
||||
public:
|
||||
static MergedSection<E> *
|
||||
get_instance(Context<E> &ctx, std::string_view name, u64 type, u64 flags);
|
||||
get_instance(Context<E> &ctx, std::string_view name, i64 type, i64 flags,
|
||||
i64 entsize);
|
||||
|
||||
SectionFragment<E> *insert(Context<E> &ctx, std::string_view data,
|
||||
u64 hash, i64 p2align);
|
||||
@@ -774,7 +781,7 @@ public:
|
||||
HyperLogLog estimator;
|
||||
|
||||
private:
|
||||
MergedSection(std::string_view name, u64 flags, u32 type);
|
||||
MergedSection(std::string_view name, i64 flags, i64 type, i64 entsize);
|
||||
|
||||
ConcurrentMap<SectionFragment<E>> map;
|
||||
std::vector<i64> shard_offsets;
|
||||
@@ -1934,6 +1941,7 @@ public:
|
||||
u64 get_tlsdesc_addr(Context<E> &ctx) const;
|
||||
u64 get_plt_addr(Context<E> &ctx) const;
|
||||
u64 get_opd_addr(Context<E> &ctx) const;
|
||||
u64 get_got_pltgot_addr(Context<E> &ctx) const;
|
||||
|
||||
void set_got_idx(Context<E> &ctx, i32 idx);
|
||||
void set_gottp_idx(Context<E> &ctx, i32 idx);
|
||||
@@ -1967,6 +1975,7 @@ public:
|
||||
bool is_relative() const { return !is_absolute(); }
|
||||
bool is_local(Context<E> &ctx) const;
|
||||
bool is_ifunc() const { return get_type() == STT_GNU_IFUNC; }
|
||||
bool is_pde_ifunc(Context<E> &ctx) const;
|
||||
bool is_remaining_undef_weak() const;
|
||||
|
||||
bool is_pcrel_linktime_const(Context<E> &ctx) const;
|
||||
@@ -2568,6 +2577,25 @@ inline u64 Symbol<E>::get_opd_addr(Context<E> &ctx) const {
|
||||
get_opd_idx(ctx) * PPC64OpdSection::ENTRY_SIZE;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
inline u64 Symbol<E>::get_got_pltgot_addr(Context<E> &ctx) const {
|
||||
// An ifunc symbol occupies two consecutive GOT slots in a
|
||||
// position-dependent executable (PDE). The first slot contains the
|
||||
// symbol's PLT address, and the second slot holds the resolved
|
||||
// address. A PDE uses the ifunc symbol's PLT entry as the address
|
||||
// for the symbol, akin to a canonical PLT.
|
||||
//
|
||||
// This function returns the address that the PLT entry should use
|
||||
// to jump to the resolved address.
|
||||
//
|
||||
// Note that we don't use this function for PPC64. In PPC64, symbols
|
||||
// are always accessed through the TOC table regardless of the
|
||||
// -fno-PIE setting. We don't need canonical PLTs on the psABIs too.
|
||||
if (is_pde_ifunc(ctx))
|
||||
return get_got_addr(ctx) + sizeof(Word<E>);
|
||||
return get_got_addr(ctx);
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
inline void Symbol<E>::set_got_idx(Context<E> &ctx, i32 idx) {
|
||||
assert(aux_idx != -1);
|
||||
@@ -2696,6 +2724,12 @@ inline bool Symbol<E>::is_local(Context<E> &ctx) const {
|
||||
return !is_imported && !is_exported;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
inline bool Symbol<E>::is_pde_ifunc(Context<E> &ctx) const {
|
||||
// Returns true if this is an ifunc tha uses two GOT slots
|
||||
return is_ifunc() && !ctx.arg.pic && !is_ppc64<E>;
|
||||
}
|
||||
|
||||
// A remaining weak undefined symbol is promoted to a dynamic symbol
|
||||
// in DSO and resolved to 0 in an executable. This function returns
|
||||
// true if it's latter.
|
||||
|
||||
+149
-125
@@ -462,6 +462,14 @@ template <typename E>
|
||||
void StrtabSection<E>::update_shdr(Context<E> &ctx) {
|
||||
i64 offset = 1;
|
||||
|
||||
// ARM32 uses $a, $t and $t mapping symbols to mark the beginning of
|
||||
// ARM, Thumb and data in text, respectively. These symbols don't
|
||||
// affect correctness of the program but helps disassembler to
|
||||
// disassemble machine code appropriately.
|
||||
if constexpr (is_arm32<E>)
|
||||
if (!ctx.arg.strip_all && !ctx.arg.retain_symbols_file)
|
||||
offset += sizeof("$a\0$t\0$d");
|
||||
|
||||
for (Chunk<E> *chunk : ctx.chunks) {
|
||||
chunk->strtab_offset = offset;
|
||||
offset += chunk->strtab_size;
|
||||
@@ -480,6 +488,16 @@ void StrtabSection<E>::update_shdr(Context<E> &ctx) {
|
||||
this->shdr.sh_size = (offset == 1) ? 0 : offset;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void StrtabSection<E>::copy_buf(Context<E> &ctx) {
|
||||
u8 *buf = ctx.buf + this->shdr.sh_offset;
|
||||
buf[0] = '\0';
|
||||
|
||||
if constexpr (is_arm32<E>)
|
||||
if (!ctx.arg.strip_all && !ctx.arg.retain_symbols_file)
|
||||
memcpy(buf + 1, "$a\0$t\0$d", 9);
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void ShstrtabSection<E>::update_shdr(Context<E> &ctx) {
|
||||
std::unordered_map<std::string_view, i64> map;
|
||||
@@ -591,9 +609,6 @@ void SymtabSection<E>::copy_buf(Context<E> &ctx) {
|
||||
ElfSym<E> *buf = (ElfSym<E> *)(ctx.buf + this->shdr.sh_offset);
|
||||
memset(buf, 0, sizeof(ElfSym<E>));
|
||||
|
||||
// Write the initial NUL byte to .strtab.
|
||||
ctx.buf[ctx.strtab->shdr.sh_offset] = '\0';
|
||||
|
||||
if (ctx.symtab_shndx) {
|
||||
ElfShdr<E> &shdr = ctx.symtab_shndx->shdr;
|
||||
memset(ctx.buf + shdr.sh_offset, 0, shdr.sh_size);
|
||||
@@ -850,7 +865,7 @@ OutputSection<E>::OutputSection(Context<E> &ctx, std::string_view name,
|
||||
u32 type, u64 flags) {
|
||||
this->name = name;
|
||||
this->shdr.sh_type = type;
|
||||
this->shdr.sh_flags = flags;
|
||||
this->shdr.sh_flags = flags & ~SHF_MERGE & ~SHF_STRINGS;
|
||||
|
||||
if (auto it = ctx.arg.section_align.find(name);
|
||||
it != ctx.arg.section_align.end())
|
||||
@@ -883,31 +898,30 @@ void OutputSection<E>::copy_buf(Context<E> &ctx) {
|
||||
|
||||
template <typename E>
|
||||
void OutputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
|
||||
auto clear = [&](u8 *loc, i64 size) {
|
||||
// As a special case, .init and .fini are filled with NOPs for s390x
|
||||
// because the runtime executes the sections as if they were a single
|
||||
// function. .init and .fini are superceded by .init_array and
|
||||
// .fini_array but being actively used only on s390x.
|
||||
if constexpr (is_s390x<E>) {
|
||||
if (this->name == ".init" || this->name == ".fini") {
|
||||
for (i64 i = 0; i < size; i += 2)
|
||||
*(ub16 *)(loc + i) = 0x0700; // nop
|
||||
return;
|
||||
}
|
||||
}
|
||||
memset(loc, 0, size);
|
||||
};
|
||||
|
||||
tbb::parallel_for((i64)0, (i64)members.size(), [&](i64 i) {
|
||||
// Copy section contents to an output file
|
||||
// Copy section contents to an output file.
|
||||
InputSection<E> &isec = *members[i];
|
||||
isec.write_to(ctx, buf + isec.offset);
|
||||
|
||||
// Clear trailing padding
|
||||
// Clear trailing padding. We write trap or nop instructions for
|
||||
// an executable segment so that a disassembler wouldn't try to
|
||||
// disassemble garbage as instructions.
|
||||
u64 this_end = isec.offset + isec.sh_size;
|
||||
u64 next_start = (i == members.size() - 1) ?
|
||||
(u64)this->shdr.sh_size : members[i + 1]->offset;
|
||||
clear(buf + this_end, next_start - this_end);
|
||||
u64 next_start;
|
||||
if (i + 1 < members.size())
|
||||
next_start = members[i + 1]->offset;
|
||||
else
|
||||
next_start = this->shdr.sh_size;
|
||||
|
||||
u8 *loc = buf + this_end;
|
||||
i64 size = next_start - this_end;
|
||||
|
||||
if (this->shdr.sh_flags & SHF_EXECINSTR) {
|
||||
for (i64 i = 0; i + sizeof(E::filler) <= size; i += sizeof(E::filler))
|
||||
memcpy(loc + i, E::filler, sizeof(E::filler));
|
||||
} else {
|
||||
memset(loc, 0, size);
|
||||
}
|
||||
});
|
||||
|
||||
if constexpr (needs_thunk<E>) {
|
||||
@@ -1001,16 +1015,10 @@ void OutputSection<E>::construct_relr(Context<E> &ctx) {
|
||||
// Compute spaces needed for thunk symbols
|
||||
template <typename E>
|
||||
void OutputSection<E>::compute_symtab_size(Context<E> &ctx) {
|
||||
if (ctx.arg.strip_all || ctx.arg.retain_symbols_file || ctx.arg.relocatable)
|
||||
return;
|
||||
|
||||
if constexpr (needs_thunk<E>) {
|
||||
this->strtab_size = 0;
|
||||
this->num_local_symtab = 0;
|
||||
|
||||
if constexpr (is_arm32<E>)
|
||||
this->strtab_size = 9; // for "$t", "$a" and "$d" symbols
|
||||
|
||||
for (std::unique_ptr<RangeExtensionThunk<E>> &thunk : thunks) {
|
||||
// For ARM32, we emit additional symbol "$t", "$a" and "$d" for
|
||||
// each thunk to mark the beginning of Thumb code, ARM code and
|
||||
@@ -1031,7 +1039,7 @@ void OutputSection<E>::compute_symtab_size(Context<E> &ctx) {
|
||||
// disassembling and/or debugging our output.
|
||||
template <typename E>
|
||||
void OutputSection<E>::populate_symtab(Context<E> &ctx) {
|
||||
if (this->strtab_size == 0)
|
||||
if (this->num_local_symtab == 0)
|
||||
return;
|
||||
|
||||
if constexpr (needs_thunk<E>) {
|
||||
@@ -1041,37 +1049,30 @@ 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;
|
||||
|
||||
if constexpr (is_arm32<E>) {
|
||||
// ARM uses these symbols to mark the begining of Thumb code, ARM
|
||||
// code and data, respectively. Our thunk contains all of them.
|
||||
strtab += write_string(strtab, "$t");
|
||||
strtab += write_string(strtab, "$a");
|
||||
strtab += write_string(strtab, "$d");
|
||||
}
|
||||
auto write_esym = [&](u64 addr, i64 st_name) {
|
||||
memset(esym, 0, sizeof(*esym));
|
||||
esym->st_name = st_name;
|
||||
esym->st_type = STT_FUNC;
|
||||
esym->st_shndx = this->shndx;
|
||||
esym->st_value = addr;
|
||||
esym++;
|
||||
};
|
||||
|
||||
for (std::unique_ptr<RangeExtensionThunk<E>> &thunk : thunks) {
|
||||
for (i64 i = 0; i < thunk->symbols.size(); i++) {
|
||||
Symbol<E> &sym = *thunk->symbols[i];
|
||||
u64 addr = thunk->get_addr(i);
|
||||
|
||||
auto write_esym = [&](i64 st_name, i64 off) {
|
||||
memset(esym, 0, sizeof(*esym));
|
||||
esym->st_name = st_name;
|
||||
esym->st_type = STT_FUNC;
|
||||
esym->st_shndx = this->shndx;
|
||||
esym->st_value = thunk->get_addr(i) + off;
|
||||
esym++;
|
||||
};
|
||||
|
||||
write_esym(strtab - strtab_base, 0);
|
||||
write_esym(addr, strtab - strtab_base);
|
||||
|
||||
strtab += write_string(strtab, sym.name()) - 1;
|
||||
strtab += write_string(strtab, "$thunk");
|
||||
|
||||
// Emit "$t", "$a" and "$d" if ARM32.
|
||||
if constexpr (is_arm32<E>) {
|
||||
write_esym(this->strtab_offset, 0);
|
||||
write_esym(this->strtab_offset + 3, 4);
|
||||
write_esym(this->strtab_offset + 6, 12);
|
||||
write_esym(addr, ctx.strtab->THUMB);
|
||||
write_esym(addr + 4, ctx.strtab->ARM);
|
||||
write_esym(addr + 12, ctx.strtab->DATA);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1081,7 +1082,14 @@ void OutputSection<E>::populate_symtab(Context<E> &ctx) {
|
||||
template <typename E>
|
||||
void GotSection<E>::add_got_symbol(Context<E> &ctx, Symbol<E> *sym) {
|
||||
sym->set_got_idx(ctx, this->shdr.sh_size / sizeof(Word<E>));
|
||||
this->shdr.sh_size += sizeof(Word<E>);
|
||||
|
||||
// An IFUNC symbol uses two GOT slots in a position-dependent
|
||||
// executable.
|
||||
if (sym->is_pde_ifunc(ctx))
|
||||
this->shdr.sh_size += sizeof(Word<E>) * 2;
|
||||
else
|
||||
this->shdr.sh_size += sizeof(Word<E>);
|
||||
|
||||
got_syms.push_back(sym);
|
||||
}
|
||||
|
||||
@@ -1105,10 +1113,10 @@ void GotSection<E>::add_tlsdesc_symbol(Context<E> &ctx, Symbol<E> *sym) {
|
||||
// always emit a dynamic relocation for each TLSDESC entry.
|
||||
//
|
||||
// If dynamic relocation is not available (i.e. if we are creating a
|
||||
// non-PIC executable), we always relax TLSDESC relocations so that
|
||||
// no TLSDESC relocation exist at runtime.
|
||||
// statically-linked executable), we always relax TLSDESC relocations
|
||||
// so that no TLSDESC relocation exist at runtime.
|
||||
assert(supports_tlsdesc<E>);
|
||||
assert(ctx.arg.pic);
|
||||
assert(!ctx.arg.is_static);
|
||||
|
||||
sym->set_tlsdesc_idx(ctx, this->shdr.sh_size / sizeof(Word<E>));
|
||||
this->shdr.sh_size += sizeof(Word<E>) * 2;
|
||||
@@ -1175,7 +1183,12 @@ static std::vector<GotEntry<E>> get_got_entries(Context<E> &ctx) {
|
||||
// IFUNC always needs to be fixed up by the dynamic linker.
|
||||
if constexpr (supports_ifunc<E>) {
|
||||
if (sym->is_ifunc()) {
|
||||
add({idx, sym->get_addr(ctx, NO_PLT), E::R_IRELATIVE});
|
||||
if (sym->is_pde_ifunc(ctx)) {
|
||||
add({idx, sym->get_plt_addr(ctx)});
|
||||
add({idx + 1, sym->get_addr(ctx, NO_PLT), E::R_IRELATIVE});
|
||||
} else {
|
||||
add({idx, sym->get_addr(ctx, NO_PLT), E::R_IRELATIVE});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -1330,9 +1343,6 @@ void GotSection<E>::construct_relr(Context<E> &ctx) {
|
||||
|
||||
template <typename E>
|
||||
void GotSection<E>::compute_symtab_size(Context<E> &ctx) {
|
||||
if (ctx.arg.strip_all || ctx.arg.retain_symbols_file)
|
||||
return;
|
||||
|
||||
this->strtab_size = 0;
|
||||
this->num_local_symtab = 0;
|
||||
|
||||
@@ -1364,7 +1374,7 @@ void GotSection<E>::compute_symtab_size(Context<E> &ctx) {
|
||||
|
||||
template <typename E>
|
||||
void GotSection<E>::populate_symtab(Context<E> &ctx) {
|
||||
if (this->strtab_size == 0)
|
||||
if (this->num_local_symtab == 0)
|
||||
return;
|
||||
|
||||
ElfSym<E> *esym =
|
||||
@@ -1455,19 +1465,19 @@ void PltSection<E>::copy_buf(Context<E> &ctx) {
|
||||
|
||||
template <typename E>
|
||||
void PltSection<E>::compute_symtab_size(Context<E> &ctx) {
|
||||
if (ctx.arg.strip_all || ctx.arg.retain_symbols_file)
|
||||
return;
|
||||
|
||||
this->num_local_symtab = symbols.size();
|
||||
this->strtab_size = 0;
|
||||
|
||||
for (Symbol<E> *sym : symbols)
|
||||
this->strtab_size += sym->name().size() + sizeof("$plt");
|
||||
|
||||
if constexpr (is_arm32<E>)
|
||||
this->num_local_symtab += symbols.size() * 2 + 2;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void PltSection<E>::populate_symtab(Context<E> &ctx) {
|
||||
if (this->strtab_size == 0)
|
||||
if (this->num_local_symtab == 0)
|
||||
return;
|
||||
|
||||
ElfSym<E> *esym =
|
||||
@@ -1476,16 +1486,30 @@ void PltSection<E>::populate_symtab(Context<E> &ctx) {
|
||||
u8 *strtab_base = ctx.buf + ctx.strtab->shdr.sh_offset;
|
||||
u8 *strtab = strtab_base + this->strtab_offset;
|
||||
|
||||
for (Symbol<E> *sym : symbols) {
|
||||
auto write_esym = [&](u64 addr, i64 st_name) {
|
||||
memset(esym, 0, sizeof(*esym));
|
||||
esym->st_name = strtab - strtab_base;
|
||||
esym->st_name = st_name;
|
||||
esym->st_type = STT_FUNC;
|
||||
esym->st_shndx = this->shndx;
|
||||
esym->st_value = sym->get_plt_addr(ctx);
|
||||
esym->st_value = addr;
|
||||
esym++;
|
||||
};
|
||||
|
||||
if constexpr (is_arm32<E>) {
|
||||
write_esym(this->shdr.sh_addr, ctx.strtab->ARM);
|
||||
write_esym(this->shdr.sh_addr + 16, ctx.strtab->DATA);
|
||||
}
|
||||
|
||||
for (Symbol<E> *sym : symbols) {
|
||||
u64 addr = sym->get_plt_addr(ctx);
|
||||
write_esym(addr, strtab - strtab_base);
|
||||
strtab += write_string(strtab, sym->name()) - 1;
|
||||
strtab += write_string(strtab, "$plt");
|
||||
|
||||
if constexpr (is_arm32<E>) {
|
||||
write_esym(addr, ctx.strtab->ARM);
|
||||
write_esym(addr + 12, ctx.strtab->DATA);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1508,19 +1532,19 @@ void PltGotSection<E>::copy_buf(Context<E> &ctx) {
|
||||
|
||||
template <typename E>
|
||||
void PltGotSection<E>::compute_symtab_size(Context<E> &ctx) {
|
||||
if (ctx.arg.strip_all || ctx.arg.retain_symbols_file)
|
||||
return;
|
||||
|
||||
this->num_local_symtab = symbols.size();
|
||||
this->strtab_size = 0;
|
||||
|
||||
for (Symbol<E> *sym : symbols)
|
||||
this->strtab_size += sym->name().size() + sizeof("$pltgot");
|
||||
|
||||
if constexpr (is_arm32<E>)
|
||||
this->num_local_symtab += symbols.size() * 2;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void PltGotSection<E>::populate_symtab(Context<E> &ctx) {
|
||||
if (this->strtab_size == 0)
|
||||
if (this->num_local_symtab == 0)
|
||||
return;
|
||||
|
||||
ElfSym<E> *esym =
|
||||
@@ -1529,16 +1553,25 @@ void PltGotSection<E>::populate_symtab(Context<E> &ctx) {
|
||||
u8 *strtab_base = ctx.buf + ctx.strtab->shdr.sh_offset;
|
||||
u8 *strtab = strtab_base + this->strtab_offset;
|
||||
|
||||
for (Symbol<E> *sym : symbols) {
|
||||
auto write_esym = [&](u64 addr, i64 st_name) {
|
||||
memset(esym, 0, sizeof(*esym));
|
||||
esym->st_name = strtab - strtab_base;
|
||||
esym->st_name = st_name;
|
||||
esym->st_type = STT_FUNC;
|
||||
esym->st_shndx = this->shndx;
|
||||
esym->st_value = sym->get_plt_addr(ctx);
|
||||
esym->st_value = addr;
|
||||
esym++;
|
||||
};
|
||||
|
||||
for (Symbol<E> *sym : symbols) {
|
||||
u64 addr = sym->get_plt_addr(ctx);
|
||||
write_esym(addr, strtab - strtab_base);
|
||||
strtab += write_string(strtab, sym->name()) - 1;
|
||||
strtab += write_string(strtab, "$pltgot");
|
||||
|
||||
if constexpr (is_arm32<E>) {
|
||||
write_esym(addr, ctx.strtab->ARM);
|
||||
write_esym(addr + 12, ctx.strtab->DATA);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1635,8 +1668,15 @@ ElfSym<E> to_output_esym(Context<E> &ctx, Symbol<E> &sym, u32 st_name,
|
||||
esym.st_shndx = SHN_ABS;
|
||||
esym.st_value = sym.get_addr(ctx);
|
||||
} else if (sym.get_type() == STT_TLS) {
|
||||
// TLS symbol
|
||||
shndx = get_st_shndx(sym);
|
||||
esym.st_value = sym.get_addr(ctx) - ctx.tls_begin;
|
||||
} else if (sym.is_pde_ifunc(ctx)) {
|
||||
// IFUNC symbol in PDE that uses two GOT slots
|
||||
shndx = get_st_shndx(sym);
|
||||
esym.st_type = STT_FUNC;
|
||||
esym.st_visibility = sym.visibility;
|
||||
esym.st_value = sym.get_addr(ctx);
|
||||
} else {
|
||||
shndx = get_st_shndx(sym);
|
||||
esym.st_visibility = sym.visibility;
|
||||
@@ -1877,29 +1917,30 @@ get_merged_output_name(Context<E> &ctx, std::string_view name, u64 flags) {
|
||||
return name;
|
||||
if (ctx.arg.unique && ctx.arg.unique->match(name))
|
||||
return name;
|
||||
if (name == ".rodata" || name.starts_with(".rodata."))
|
||||
return (flags & SHF_STRINGS) ? ".rodata.str" : ".rodata.cst";
|
||||
return name;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
MergedSection<E>::MergedSection(std::string_view name, u64 flags, u32 type) {
|
||||
MergedSection<E>::MergedSection(std::string_view name, i64 flags, i64 type,
|
||||
i64 entsize) {
|
||||
this->name = name;
|
||||
this->shdr.sh_flags = flags;
|
||||
this->shdr.sh_type = type;
|
||||
this->shdr.sh_entsize = entsize;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
MergedSection<E> *
|
||||
MergedSection<E>::get_instance(Context<E> &ctx, std::string_view name,
|
||||
u64 type, u64 flags) {
|
||||
i64 type, i64 flags, i64 entsize) {
|
||||
name = get_merged_output_name(ctx, name, flags);
|
||||
flags = flags & ~(u64)SHF_GROUP & ~(u64)SHF_COMPRESSED;
|
||||
|
||||
auto find = [&]() -> MergedSection * {
|
||||
for (std::unique_ptr<MergedSection<E>> &osec : ctx.merged_sections)
|
||||
if (std::tuple(name, flags, type) ==
|
||||
std::tuple(osec->name, osec->shdr.sh_flags, osec->shdr.sh_type))
|
||||
if (std::tuple(name, flags, type, entsize) ==
|
||||
std::tuple(osec->name, osec->shdr.sh_flags, osec->shdr.sh_type,
|
||||
osec->shdr.sh_entsize))
|
||||
return osec.get();
|
||||
return nullptr;
|
||||
};
|
||||
@@ -1917,7 +1958,7 @@ MergedSection<E>::get_instance(Context<E> &ctx, std::string_view name,
|
||||
if (MergedSection *osec = find())
|
||||
return osec;
|
||||
|
||||
MergedSection *osec = new MergedSection(name, flags, type);
|
||||
MergedSection *osec = new MergedSection(name, flags, type, entsize);
|
||||
ctx.merged_sections.emplace_back(osec);
|
||||
return osec;
|
||||
}
|
||||
@@ -1947,67 +1988,49 @@ MergedSection<E>::insert(Context<E> &ctx, std::string_view data, u64 hash,
|
||||
template <typename E>
|
||||
void MergedSection<E>::assign_offsets(Context<E> &ctx) {
|
||||
std::vector<i64> sizes(map.NUM_SHARDS);
|
||||
std::vector<i64> max_p2aligns(map.NUM_SHARDS);
|
||||
shard_offsets.resize(map.NUM_SHARDS + 1);
|
||||
|
||||
i64 shard_size = map.nbuckets / map.NUM_SHARDS;
|
||||
Atomic<i64> alignment = 1;
|
||||
|
||||
tbb::parallel_for((i64)0, map.NUM_SHARDS, [&](i64 i) {
|
||||
struct KeyVal {
|
||||
std::string_view key;
|
||||
SectionFragment<E> *val;
|
||||
};
|
||||
using Entry = typename decltype(map)::Entry;
|
||||
std::vector<Entry *> entries = map.get_sorted_entries(i);
|
||||
|
||||
std::vector<KeyVal> fragments;
|
||||
fragments.reserve(shard_size);
|
||||
|
||||
for (i64 j = shard_size * i; j < shard_size * (i + 1); j++)
|
||||
if (const char *key = map.get_key(j))
|
||||
if (SectionFragment<E> &frag = map.entries[j].value; frag.is_alive)
|
||||
fragments.push_back({{key, map.entries[j].keylen}, &frag});
|
||||
|
||||
// Sort fragments to make output deterministic.
|
||||
tbb::parallel_sort(fragments.begin(), fragments.end(),
|
||||
[](const KeyVal &a, const KeyVal &b) {
|
||||
return std::tuple{(u32)a.val->p2align, a.key.size(), a.key} <
|
||||
std::tuple{(u32)b.val->p2align, b.key.size(), b.key};
|
||||
});
|
||||
|
||||
// Assign offsets.
|
||||
i64 offset = 0;
|
||||
i64 p2align = 0;
|
||||
|
||||
for (KeyVal &kv : fragments) {
|
||||
SectionFragment<E> &frag = *kv.val;
|
||||
offset = align_to(offset, 1 << frag.p2align);
|
||||
frag.offset = offset;
|
||||
offset += kv.key.size();
|
||||
p2align = std::max<i64>(p2align, frag.p2align);
|
||||
for (Entry *ent : entries) {
|
||||
SectionFragment<E> &frag = ent->value;
|
||||
if (frag.is_alive) {
|
||||
offset = align_to(offset, 1 << frag.p2align);
|
||||
frag.offset = offset;
|
||||
offset += ent->keylen;
|
||||
p2align = std::max<i64>(p2align, frag.p2align);
|
||||
}
|
||||
}
|
||||
|
||||
sizes[i] = offset;
|
||||
max_p2aligns[i] = p2align;
|
||||
update_maximum(alignment, 1 << p2align);
|
||||
|
||||
static Counter merged_strings("merged_strings");
|
||||
merged_strings += fragments.size();
|
||||
merged_strings += entries.size();
|
||||
});
|
||||
|
||||
i64 p2align = 0;
|
||||
for (i64 x : max_p2aligns)
|
||||
p2align = std::max(p2align, x);
|
||||
i64 shard_size = map.nbuckets / map.NUM_SHARDS;
|
||||
shard_offsets.resize(map.NUM_SHARDS + 1);
|
||||
|
||||
for (i64 i = 1; i < map.NUM_SHARDS + 1; i++)
|
||||
shard_offsets[i] =
|
||||
align_to(shard_offsets[i - 1] + sizes[i - 1], 1 << p2align);
|
||||
align_to(shard_offsets[i - 1] + sizes[i - 1], alignment);
|
||||
|
||||
tbb::parallel_for((i64)1, map.NUM_SHARDS, [&](i64 i) {
|
||||
for (i64 j = shard_size * i; j < shard_size * (i + 1); j++)
|
||||
if (SectionFragment<E> &frag = map.entries[j].value; frag.is_alive)
|
||||
for (i64 j = shard_size * i; j < shard_size * (i + 1); j++) {
|
||||
SectionFragment<E> &frag = map.entries[j].value;
|
||||
if (frag.is_alive)
|
||||
frag.offset += shard_offsets[i];
|
||||
}
|
||||
});
|
||||
|
||||
this->shdr.sh_size = shard_offsets[map.NUM_SHARDS];
|
||||
this->shdr.sh_addralign = 1 << p2align;
|
||||
this->shdr.sh_addralign = alignment;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -2023,9 +2046,11 @@ void MergedSection<E>::write_to(Context<E> &ctx, u8 *buf) {
|
||||
memset(buf + shard_offsets[i], 0, shard_offsets[i + 1] - shard_offsets[i]);
|
||||
|
||||
for (i64 j = shard_size * i; j < shard_size * (i + 1); j++)
|
||||
if (const char *key = map.get_key(j))
|
||||
if (SectionFragment<E> &frag = map.entries[j].value; frag.is_alive)
|
||||
if (const char *key = map.entries[j].key) {
|
||||
SectionFragment<E> &frag = map.entries[j].value;
|
||||
if (frag.is_alive)
|
||||
memcpy(buf + frag.offset, key, map.entries[j].keylen);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2033,7 +2058,7 @@ template <typename E>
|
||||
void MergedSection<E>::print_stats(Context<E> &ctx) {
|
||||
i64 used = 0;
|
||||
for (i64 i = 0; i < map.nbuckets; i++)
|
||||
if (map.get_key(i))
|
||||
if (map.entries[i].key)
|
||||
used++;
|
||||
|
||||
SyncOut(ctx) << this->name
|
||||
@@ -2315,8 +2340,7 @@ void CopyrelSection<E>::update_shdr(Context<E> &ctx) {
|
||||
// a segment, so a .copyrel.rel.ro usually requires one extra
|
||||
// segment for it. We turn a .copyrel.rel.ro into a regular section
|
||||
// if it is very small to avoid the cost of the extra segment.
|
||||
constexpr i64 threshold = 4096;
|
||||
if (this->is_relro && ctx.arg.z_relro && this->shdr.sh_size < threshold)
|
||||
if (this->is_relro && ctx.arg.z_relro && this->shdr.sh_size < E::page_size)
|
||||
this->shdr.sh_type = SHT_PROGBITS;
|
||||
}
|
||||
|
||||
|
||||
+26
-13
@@ -397,7 +397,7 @@ template <typename E>
|
||||
void add_comment_string(Context<E> &ctx, std::string str) {
|
||||
MergedSection<E> *sec =
|
||||
MergedSection<E>::get_instance(ctx, ".comment", SHT_PROGBITS,
|
||||
SHF_MERGE | SHF_STRINGS);
|
||||
SHF_MERGE | SHF_STRINGS, 1);
|
||||
|
||||
std::string_view buf = save_string(ctx, str);
|
||||
std::string_view data(buf.data(), buf.size() + 1);
|
||||
@@ -1250,12 +1250,15 @@ void compute_section_sizes(Context<E> &ctx) {
|
||||
// inserting thunks. This pass cannot be parallelized. That is,
|
||||
// create_range_extension_thunks is parallelized internally, but the
|
||||
// function itself is not thread-safe.
|
||||
if constexpr (needs_thunk<E>)
|
||||
if constexpr (needs_thunk<E>) {
|
||||
Timer t2(ctx, "create_range_extension_thunks");
|
||||
|
||||
if (!ctx.arg.relocatable)
|
||||
for (Chunk<E> *chunk : ctx.chunks)
|
||||
if (OutputSection<E> *osec = chunk->to_osec())
|
||||
if (osec->shdr.sh_flags & SHF_EXECINSTR)
|
||||
osec->create_range_extension_thunks(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// Find all unresolved symbols and attach them to the most appropriate files.
|
||||
@@ -1532,9 +1535,11 @@ template <typename E>
|
||||
void create_output_symtab(Context<E> &ctx) {
|
||||
Timer t(ctx, "compute_symtab_size");
|
||||
|
||||
tbb::parallel_for_each(ctx.chunks, [&](Chunk<E> *chunk) {
|
||||
chunk->compute_symtab_size(ctx);
|
||||
});
|
||||
if (!ctx.arg.strip_all && !ctx.arg.retain_symbols_file) {
|
||||
tbb::parallel_for_each(ctx.chunks, [&](Chunk<E> *chunk) {
|
||||
chunk->compute_symtab_size(ctx);
|
||||
});
|
||||
}
|
||||
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
file->compute_symtab_size(ctx);
|
||||
@@ -1932,23 +1937,31 @@ void sort_output_sections_regular(Context<E> &ctx) {
|
||||
|
||||
// Ties are broken by additional rules
|
||||
auto get_rank2 = [&](Chunk<E> *chunk) -> i64 {
|
||||
if (chunk->shdr.sh_type == SHT_NOTE)
|
||||
return -chunk->shdr.sh_addralign;
|
||||
ElfShdr<E> &shdr = chunk->shdr;
|
||||
if (shdr.sh_type == SHT_NOTE)
|
||||
return -shdr.sh_addralign;
|
||||
|
||||
if (chunk == ctx.got)
|
||||
return 1;
|
||||
if (chunk->name == ".toc")
|
||||
return 2;
|
||||
if (chunk->name == ".alpha_got")
|
||||
if (chunk->name == ".toc")
|
||||
return 3;
|
||||
if (chunk->name == ".alpha_got")
|
||||
return 4;
|
||||
|
||||
if (shdr.sh_flags & SHF_MERGE) {
|
||||
if (shdr.sh_flags & SHF_STRINGS)
|
||||
return (5LL << 32) | shdr.sh_entsize;
|
||||
return (6LL << 32) | shdr.sh_entsize;
|
||||
}
|
||||
|
||||
if (chunk == ctx.relro_padding)
|
||||
return INT_MAX;
|
||||
return INT64_MAX;
|
||||
return 0;
|
||||
};
|
||||
|
||||
sort(ctx.chunks, [&](Chunk<E> *a, Chunk<E> *b) {
|
||||
return std::tuple{get_rank1(a), get_rank2(a)} <
|
||||
std::tuple{get_rank1(b), get_rank2(b)};
|
||||
return std::tuple{get_rank1(a), get_rank2(a), a->name} <
|
||||
std::tuple{get_rank1(b), get_rank2(b), b->name};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -235,7 +235,7 @@ void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
|
||||
|
||||
// Move A forward so that A is reachable from C.
|
||||
i64 c_offset = (c == m.size()) ? offset : m[c]->offset;
|
||||
while (a < b && a < m.size() && m[a]->offset + max_distance() < c_offset)
|
||||
while (a < b && m[a]->offset + max_distance() < c_offset)
|
||||
a++;
|
||||
|
||||
// Erase references to out-of-range thunks.
|
||||
|
||||
+49
-42
@@ -1,84 +1,91 @@
|
||||
option(MOLD_ENABLE_QEMU_TESTS "Enable tests on non-native targets" OFF)
|
||||
|
||||
function(add_target TRIPLE)
|
||||
set(HOST ${CMAKE_HOST_SYSTEM_PROCESSOR})
|
||||
set(HOST ${CMAKE_HOST_SYSTEM_PROCESSOR})
|
||||
|
||||
if(${HOST} MATCHES "amd64")
|
||||
set(HOST x86_64)
|
||||
elseif(${HOST} MATCHES "arm.*")
|
||||
set(HOST arm)
|
||||
elseif(${HOST} STREQUAL "ppc64")
|
||||
set(HOST powerpc64)
|
||||
elseif(${HOST} STREQUAL "ppc64le")
|
||||
set(HOST powerpc64le)
|
||||
endif()
|
||||
# CMAKE_HOST_SYSTEM_PROCESSOR returns "aarch64" on ARM32 userland
|
||||
# with ARM64 kernel. Here, we run `cc -dumpmachine` to determine the
|
||||
# compiler's default target.
|
||||
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpmachine
|
||||
RESULT_VARIABLE EXIT_CODE
|
||||
OUTPUT_VARIABLE ARCH
|
||||
ERROR_QUIET)
|
||||
if(NOT EXIT_CODE AND ARCH MATCHES "([^-]+).*")
|
||||
set(HOST ${CMAKE_MATCH_1})
|
||||
endif()
|
||||
|
||||
if(${TRIPLE} MATCHES "${HOST}-.*")
|
||||
if(${HOST} MATCHES "amd64")
|
||||
set(HOST x86_64)
|
||||
elseif(${HOST} MATCHES "i386")
|
||||
set(HOST i686)
|
||||
elseif(${HOST} MATCHES "arm.*")
|
||||
set(HOST arm)
|
||||
elseif(${HOST} STREQUAL "powerpc64")
|
||||
set(HOST ppc64)
|
||||
elseif(${HOST} STREQUAL "powerpc64le")
|
||||
set(HOST ppc64le)
|
||||
endif()
|
||||
|
||||
function(add_target ARCH TRIPLE)
|
||||
if(${ARCH} STREQUAL ${HOST})
|
||||
set(IS_NATIVE 1)
|
||||
endif()
|
||||
|
||||
if(${TRIPLE} MATCHES "([^-]+)-.")
|
||||
set(MACHINE ${CMAKE_MATCH_1})
|
||||
endif()
|
||||
|
||||
if(IS_NATIVE OR MOLD_ENABLE_QEMU_TESTS)
|
||||
file(GLOB ALL_TESTS RELATIVE ${CMAKE_CURRENT_LIST_DIR} CONFIGURE_DEPENDS
|
||||
"*.sh")
|
||||
file(GLOB ALL_TESTS RELATIVE ${CMAKE_CURRENT_LIST_DIR} CONFIGURE_DEPENDS "*.sh")
|
||||
|
||||
list(FILTER ALL_TESTS EXCLUDE REGEX "_")
|
||||
|
||||
file(GLOB TESTS RELATIVE ${CMAKE_CURRENT_LIST_DIR} CONFIGURE_DEPENDS
|
||||
"${MACHINE}_*.sh")
|
||||
file(GLOB TESTS RELATIVE ${CMAKE_CURRENT_LIST_DIR} CONFIGURE_DEPENDS "${ARCH}_*.sh")
|
||||
|
||||
list(APPEND TESTS ${ALL_TESTS})
|
||||
|
||||
foreach(TEST IN LISTS TESTS)
|
||||
string(REGEX REPLACE "\\.sh$" "" TESTNAME "${MACHINE}-${TEST}")
|
||||
string(REGEX REPLACE "\\.sh$" "" TESTNAME "${ARCH}-${TEST}")
|
||||
|
||||
add_test(NAME ${TESTNAME}
|
||||
COMMAND bash -x ${CMAKE_CURRENT_LIST_DIR}/${TEST}
|
||||
WORKING_DIRECTORY ${mold_BINARY_DIR})
|
||||
|
||||
set_property(TEST ${TESTNAME} APPEND PROPERTY ENVIRONMENT "HOST=${HOST}")
|
||||
|
||||
if(IS_NATIVE)
|
||||
set_tests_properties(${TESTNAME} PROPERTIES
|
||||
SKIP_REGULAR_EXPRESSION "skipped")
|
||||
set_property(TEST ${TESTNAME} APPEND PROPERTY SKIP_REGULAR_EXPRESSION "skipped")
|
||||
else()
|
||||
set_tests_properties(${TESTNAME} PROPERTIES
|
||||
ENVIRONMENT "TRIPLE=${TRIPLE}")
|
||||
set_property(TEST ${TESTNAME} APPEND PROPERTY ENVIRONMENT "TRIPLE=${TRIPLE}")
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
add_target(x86_64-linux-gnu)
|
||||
add_target(i686-linux-gnu)
|
||||
add_target(aarch64-linux-gnu)
|
||||
add_target(arm-linux-gnueabihf)
|
||||
add_target(riscv64-linux-gnu)
|
||||
add_target(powerpc-linux-gnu)
|
||||
add_target(powerpc64-linux-gnu)
|
||||
add_target(powerpc64le-linux-gnu)
|
||||
add_target(sparc64-linux-gnu)
|
||||
add_target(s390x-linux-gnu)
|
||||
add_target(sh4-linux-gnu)
|
||||
add_target(alpha-linux-gnu)
|
||||
add_target(x86_64 x86_64-linux-gnu)
|
||||
add_target(i686 i686-linux-gnu)
|
||||
add_target(aarch64 aarch64-linux-gnu)
|
||||
add_target(arm arm-linux-gnueabihf)
|
||||
add_target(riscv64 riscv64-linux-gnu)
|
||||
add_target(ppc powerpc-linux-gnu)
|
||||
add_target(ppc64 powerpc64-linux-gnu)
|
||||
add_target(ppc64le powerpc64le-linux-gnu)
|
||||
add_target(sparc64 sparc64-linux-gnu)
|
||||
add_target(s390x s390x-linux-gnu)
|
||||
add_target(sh4 sh4-linux-gnu)
|
||||
add_target(alpha alpha-linux-gnu)
|
||||
|
||||
option(MOLD_ENABLE_QEMU_TESTS_RV32 "Enable tests for RV32" OFF)
|
||||
if(MOLD_ENABLE_QEMU_TESTS_RV32)
|
||||
add_target(riscv32-linux-gnu)
|
||||
add_target(riscv32 riscv32-linux-gnu)
|
||||
endif()
|
||||
|
||||
option(MOLD_ENABLE_QEMU_TESTS_POWER10 "Enable tests for Power10" OFF)
|
||||
if(MOLD_ENABLE_QEMU_TESTS_POWER10)
|
||||
add_target(powerpc64le_power10-linux-gnu)
|
||||
add_target(ppc64le powerpc64le_power10-linux-gnu)
|
||||
endif()
|
||||
|
||||
option(MOLD_ENABLE_QEMU_TESTS_M68K "Enable tests for m68k" OFF)
|
||||
if(MOLD_ENABLE_QEMU_TESTS_M68K)
|
||||
add_target(m68k-linux-gnu)
|
||||
add_target(m68k m68k-linux-gnu)
|
||||
endif()
|
||||
|
||||
option(MOLD_ENABLE_QEMU_TESTS_LOONGARCH "Enable tests for LoongArch" OFF)
|
||||
if(MOLD_ENABLE_QEMU_TESTS_LOONGARCH)
|
||||
add_target(loongarch64-linux-gnu)
|
||||
if(MOLD_ENABLE_QEMU_TESTS_LOONGARCH OR ${HOST} STREQUAL loongarch64)
|
||||
add_target(loongarch64 loongarch64-linux-gnu)
|
||||
endif()
|
||||
@@ -12,7 +12,7 @@ foo:
|
||||
EOF
|
||||
|
||||
$CC -B. -shared -o $t/b.so $t/a.o
|
||||
readelf -W --dyn-syms $t/b.so | grep -Eq '\[VARIANT_PCS\].* foo$'
|
||||
readelf -W --dyn-syms $t/b.so | grep foo | grep -q '[VARIANT_PCS]'
|
||||
|
||||
cat <<EOF | $CC -c -o $t/c.o -xc -
|
||||
void foo();
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc - -ffunction-sections
|
||||
#include <stdio.h>
|
||||
|
||||
void f0();
|
||||
void f1();
|
||||
void f2();
|
||||
void f3();
|
||||
void f4();
|
||||
void f5();
|
||||
void f6();
|
||||
void f7();
|
||||
void f8();
|
||||
void f9();
|
||||
void f10();
|
||||
void f11();
|
||||
void f12();
|
||||
void f13();
|
||||
void f14();
|
||||
void f15();
|
||||
void f16();
|
||||
void f17();
|
||||
void f18();
|
||||
void f19();
|
||||
|
||||
__attribute__((aligned(1024*1024))) void f0(int x) { printf("0 "); if (!x) f9(); }
|
||||
__attribute__((aligned(1024*1024))) void f1(int x) { printf("1 "); f8(x); }
|
||||
__attribute__((aligned(1024*1024))) void f2(int x) { printf("2 "); f7(x); }
|
||||
__attribute__((aligned(1024*1024))) void f3(int x) { printf("3 "); f6(x); }
|
||||
__attribute__((aligned(1024*1024))) void f4(int x) { printf("4 "); f5(x); }
|
||||
__attribute__((aligned(1024*1024))) void f5(int x) { printf("5 "); f10(x); }
|
||||
__attribute__((aligned(1024*1024))) void f6(int x) { printf("6 "); f4(x); }
|
||||
__attribute__((aligned(1024*1024))) void f7(int x) { printf("7 "); f3(x); }
|
||||
__attribute__((aligned(1024*1024))) void f8(int x) { printf("8 "); f2(x); }
|
||||
__attribute__((aligned(1024*1024))) void f9(int x) { printf("9 "); f1(x); }
|
||||
|
||||
__attribute__((aligned(8*1024*1024))) void f10(int x) { printf("10 "); f19(x); }
|
||||
__attribute__((aligned(8*1024*1024))) void f11(int x) { printf("11 "); f18(x); }
|
||||
__attribute__((aligned(8*1024*1024))) void f12(int x) { printf("12 "); f17(x); }
|
||||
__attribute__((aligned(8*1024*1024))) void f13(int x) { printf("13 "); f16(x); }
|
||||
__attribute__((aligned(8*1024*1024))) void f14(int x) { printf("14 "); f15(x); }
|
||||
__attribute__((aligned(8*1024*1024))) void f15(int x) { printf("15 "); f0(x + 1); }
|
||||
__attribute__((aligned(8*1024*1024))) void f16(int x) { printf("16 "); f14(x); }
|
||||
__attribute__((aligned(8*1024*1024))) void f17(int x) { printf("17 "); f13(x); }
|
||||
__attribute__((aligned(8*1024*1024))) void f18(int x) { printf("18 "); f12(x); }
|
||||
__attribute__((aligned(8*1024*1024))) void f19(int x) { printf("19 "); f11(x); }
|
||||
|
||||
int main() {
|
||||
f0(0);
|
||||
printf("\n");
|
||||
}
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe $t/a.o
|
||||
$QEMU $t/exe | grep -Eq '^0 9 1 8 2 7 3 6 4 5 10 19 11 18 12 17 13 16 14 15 0 $'
|
||||
@@ -2,6 +2,7 @@
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
[ $MACHINE = arm ] || skip
|
||||
is_musl && skip
|
||||
|
||||
echo 'int main() {}' | $GCC -c -o /dev/null -xc - -O0 -mthumb >& /dev/null \
|
||||
|| skip
|
||||
|
||||
+4
-2
@@ -14,10 +14,12 @@ canonical_name() {
|
||||
esac
|
||||
}
|
||||
|
||||
HOST=$(canonical_name $(uname -m))
|
||||
if [ -z "$HOST" ]; then
|
||||
HOST=$(canonical_name $(uname -m))
|
||||
fi
|
||||
|
||||
# Set tool names
|
||||
if [ "$TRIPLE" = "" ]; then
|
||||
if [ -z "$TRIPLE" ]; then
|
||||
MACHINE=$HOST
|
||||
TESTDIR=out/test/elf/$HOST
|
||||
CC="${TEST_CC:-cc}"
|
||||
|
||||
Regular → Executable
File mode changed.
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
supports_ifunc || skip
|
||||
|
||||
cat <<EOF | $CC -c -fPIC -o $t/a.o -xc -
|
||||
typedef void Func();
|
||||
void foo();
|
||||
Func *get_foo() { return foo; }
|
||||
EOF
|
||||
|
||||
$CC -B. -shared -o $t/b.so $t/a.o
|
||||
|
||||
cat <<EOF | $CC -c -fno-PIE -o $t/c.o -xc -
|
||||
#include <stdio.h>
|
||||
|
||||
typedef void Func();
|
||||
|
||||
__attribute__((ifunc("resolve_foo"))) void foo(void);
|
||||
void real_foo(void) { printf("foo "); }
|
||||
Func *resolve_foo() { return real_foo; }
|
||||
|
||||
Func *get_foo();
|
||||
|
||||
int main() {
|
||||
printf("%p %p\n", foo, get_foo());
|
||||
foo();
|
||||
printf("\n");
|
||||
}
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe1 $t/c.o $t/b.so -no-pie
|
||||
$QEMU $t/exe1 | grep -Eq '^(\S+) \1'
|
||||
Executable
+52
@@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
supports_ifunc || skip
|
||||
|
||||
cat <<EOF | $CC -c -fno-PIE -o $t/a.o -xc -
|
||||
#include <stdio.h>
|
||||
|
||||
typedef void Func();
|
||||
|
||||
__attribute__((ifunc("resolve_foo"))) void foo(void);
|
||||
void real_foo(void) { printf("foo "); }
|
||||
Func *resolve_foo() { return real_foo; }
|
||||
|
||||
__attribute__((ifunc("resolve_bar"))) void bar(void);
|
||||
void real_bar(void) { printf("bar "); }
|
||||
Func *resolve_bar() { return real_bar; }
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -c -fPIC -o $t/b.o -xc -
|
||||
typedef void Func();
|
||||
|
||||
void foo();
|
||||
void bar();
|
||||
|
||||
Func *get_foo() { return foo; }
|
||||
Func *get_bar() { return bar; }
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -c -fno-PIE -o $t/c.o -xc -
|
||||
#include <stdio.h>
|
||||
|
||||
typedef void Func();
|
||||
|
||||
void foo();
|
||||
void bar();
|
||||
Func *get_foo();
|
||||
Func *get_bar();
|
||||
|
||||
int main() {
|
||||
printf("%p %p %p %p\n", foo, get_foo(), bar, get_bar());
|
||||
foo();
|
||||
bar();
|
||||
printf("\n");
|
||||
}
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe1 $t/a.o $t/b.o $t/c.o -no-pie
|
||||
$QEMU $t/exe1 | grep -Eq '^(\S+) \1 (\S+) \2'
|
||||
|
||||
readelf --dynamic $t/exe1 > $t/log1
|
||||
! grep -q TEXTREL $t/log1 || false
|
||||
@@ -9,6 +9,3 @@ EOF
|
||||
|
||||
$CC -B. -o $t/exe1 $t/a.o
|
||||
$QEMU $t/exe1 | grep -q 'Hello world'
|
||||
|
||||
$CC -B. -o $t/exe2 $t/a.o -Wl,-Ttext=0x100000000,-Tdata=0x500000000
|
||||
$QEMU $t/exe2 | grep -q 'Hello world'
|
||||
Executable
+95
@@ -0,0 +1,95 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc - -ffunction-sections
|
||||
#include <stdio.h>
|
||||
|
||||
void f0();
|
||||
void f1();
|
||||
void f2();
|
||||
void f3();
|
||||
void f4();
|
||||
void f5();
|
||||
void f6();
|
||||
void f7();
|
||||
void f8();
|
||||
void f9();
|
||||
void f10();
|
||||
void f11();
|
||||
void f12();
|
||||
void f13();
|
||||
void f14();
|
||||
void f15();
|
||||
void f16();
|
||||
void f17();
|
||||
void f18();
|
||||
void f19();
|
||||
|
||||
void f0(int x) { printf("0 "); if (!x) f9(); }
|
||||
void space0() { __asm__(".space 1024*1024"); }
|
||||
|
||||
void f1(int x) { printf("1 "); f8(x); }
|
||||
void space1() { __asm__(".space 1024*1024"); }
|
||||
|
||||
void f2(int x) { printf("2 "); f7(x); }
|
||||
void space2() { __asm__(".space 1024*1024"); }
|
||||
|
||||
void f3(int x) { printf("3 "); f6(x); }
|
||||
void space3() { __asm__(".space 1024*1024"); }
|
||||
|
||||
void f4(int x) { printf("4 "); f5(x); }
|
||||
void space4() { __asm__(".space 1024*1024"); }
|
||||
|
||||
void f5(int x) { printf("5 "); f10(x); }
|
||||
void space5() { __asm__(".space 1024*1024"); }
|
||||
|
||||
void f6(int x) { printf("6 "); f4(x); }
|
||||
void space6() { __asm__(".space 1024*1024"); }
|
||||
|
||||
void f7(int x) { printf("7 "); f3(x); }
|
||||
void space7() { __asm__(".space 1024*1024"); }
|
||||
|
||||
void f8(int x) { printf("8 "); f2(x); }
|
||||
void space8() { __asm__(".space 1024*1024"); }
|
||||
|
||||
void f9(int x) { printf("9 "); f1(x); }
|
||||
void space9() { __asm__(".space 1024*1024"); }
|
||||
|
||||
void f10(int x) { printf("10 "); f19(x); }
|
||||
void space10() { __asm__(".space 8*1024*1024"); }
|
||||
|
||||
void f11(int x) { printf("11 "); f18(x); }
|
||||
void space11() { __asm__(".space 8*1024*1024"); }
|
||||
|
||||
void f12(int x) { printf("12 "); f17(x); }
|
||||
void space12() { __asm__(".space 8*1024*1024"); }
|
||||
|
||||
void f13(int x) { printf("13 "); f16(x); }
|
||||
void space13() { __asm__(".space 8*1024*1024"); }
|
||||
|
||||
void f14(int x) { printf("14 "); f15(x); }
|
||||
void space14() { __asm__(".space 8*1024*1024"); }
|
||||
|
||||
void f15(int x) { printf("15 "); f0(x + 1); }
|
||||
void space15() { __asm__(".space 8*1024*1024"); }
|
||||
|
||||
void f16(int x) { printf("16 "); f14(x); }
|
||||
void space16() { __asm__(".space 8*1024*1024"); }
|
||||
|
||||
void f17(int x) { printf("17 "); f13(x); }
|
||||
void space17() { __asm__(".space 8*1024*1024"); }
|
||||
|
||||
void f18(int x) { printf("18 "); f12(x); }
|
||||
void space18() { __asm__(".space 8*1024*1024"); }
|
||||
|
||||
void f19(int x) { printf("19 "); f11(x); }
|
||||
void space19() { __asm__(".space 8*1024*1024"); }
|
||||
|
||||
int main() {
|
||||
f0(0);
|
||||
printf("\n");
|
||||
}
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe $t/a.o
|
||||
$QEMU $t/exe | grep -Eq '^0 9 1 8 2 7 3 6 4 5 10 19 11 18 12 17 13 16 14 15 0 $'
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
# OneTBB isn't tsan-clean
|
||||
nm mold | grep -q '__tsan_init' && skip
|
||||
|
||||
cat <<EOF | $CC -c -o $t/a.o -xassembler -
|
||||
.section .rodata.str1.1,"aMS",@progbits,1
|
||||
val1:
|
||||
.ascii "Hello \0"
|
||||
|
||||
.section .rodata.str1.1,"aMS",@progbits,1
|
||||
val5:
|
||||
.ascii "World \0"
|
||||
EOF
|
||||
|
||||
./mold --relocatable -o $t/b.o $t/a.o
|
||||
|
||||
readelf -W -p .rodata.str1.1 $t/b.o | grep -Eq '\b0\b.*Hello'
|
||||
readelf -W -p .rodata.str1.1 $t/b.o | grep -Eq '\b7\b.*World'
|
||||
@@ -1,50 +0,0 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
# ARM assembler has a differnet grammar than the others.
|
||||
# Concretely speaking, ARM as uses "@" as a start of a comment.
|
||||
[ $MACHINE = arm ] && skip
|
||||
|
||||
cat <<'EOF' | $CC -c -o $t/a.o -x assembler -
|
||||
.globl val1, val2, val3, val4, val5
|
||||
|
||||
.section .rodata.str1.1,"aMS",@progbits,1
|
||||
val1:
|
||||
.ascii "Hello \0"
|
||||
|
||||
.section .rodata.str4.4,"aMS",@progbits,4
|
||||
.align 4
|
||||
val2:
|
||||
.ascii "world \0\0\0\0"
|
||||
|
||||
.section .rodata.foo,"aMS",@progbits,1
|
||||
val3:
|
||||
.ascii "foobar\0"
|
||||
|
||||
.section .rodata.cst8,"aM",@progbits,8
|
||||
.align 8
|
||||
val4:
|
||||
.ascii "abcdefgh"
|
||||
|
||||
.section .rodatabar,"aMS",@progbits,1
|
||||
val5:
|
||||
.ascii "bar\0"
|
||||
EOF
|
||||
|
||||
cat <<'EOF' | $CC -c -o $t/b.o -xc -
|
||||
#include <stdio.h>
|
||||
|
||||
extern char val1, val2, val3, val4, val5;
|
||||
|
||||
int main() {
|
||||
printf("%p %p %p %p %p\n", &val1, &val2, &val3, &val4, &val5);
|
||||
}
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe $t/a.o $t/b.o
|
||||
|
||||
readelf -p .rodata.str $t/exe | grep -q Hello
|
||||
readelf -p .rodata.str $t/exe | grep -q world
|
||||
readelf -p .rodata.str $t/exe | grep -q foobar
|
||||
readelf -p .rodata.cst $t/exe | grep -q abcdefgh
|
||||
readelf -p .rodatabar $t/exe | grep -q bar
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
[ "$CC" = cc ] || skip
|
||||
|
||||
# ASAN doesn't work with LD_PRELOAD
|
||||
nm mold-wrapper.so | grep -q '__[at]san_init' && skip
|
||||
nm mold | grep -q '__[at]san_init' && skip
|
||||
|
||||
cat <<'EOF' | $CC -xc -c -o $t/a.o -
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xassembler -
|
||||
.section .rodata.str1.1, "aMS", @progbits, 1
|
||||
.section .rodata.foo, "aMS", @progbits, 1
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -o $t/b.o -c -xassembler -
|
||||
@@ -19,4 +20,5 @@ $CC -B. -o $t/exe $t/a.o $t/b.o $t/c.o
|
||||
$t/exe
|
||||
|
||||
readelf -SW $t/exe > $t/log
|
||||
! grep -Fq .rodata.str1.1 $t/log || false
|
||||
grep -q '.rodata.str1.1 .* AMS ' $t/log
|
||||
! grep -q '.rodata.str1.1 .* AM ' $t/log || false
|
||||
@@ -1,6 +1,9 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
# OneTBB isn't tsan-clean
|
||||
nm mold | grep -q '__tsan_init' && skip
|
||||
|
||||
# Skip if target is not x86-64
|
||||
[ $MACHINE = x86_64 ] || skip
|
||||
|
||||
|
||||
Vendored
+7
-1
@@ -6,6 +6,7 @@ project(libblake3
|
||||
LANGUAGES C ASM
|
||||
)
|
||||
|
||||
include(CheckCCompilerFlag)
|
||||
include(FeatureSummary)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
@@ -131,7 +132,12 @@ elseif(CMAKE_SYSTEM_PROCESSOR IN_LIST BLAKE3_ARMv8_NAMES
|
||||
target_sources(blake3 PRIVATE
|
||||
blake3_neon.c
|
||||
)
|
||||
set_source_files_properties(blake3_dispatch.c PROPERTIES COMPILE_DEFINITIONS BLAKE3_USE_NEON=1)
|
||||
target_compile_options(blake3 PRIVATE -DBLAKE3_USE_NEON=1)
|
||||
|
||||
check_c_compiler_flag(-mfpu=neon BLAKE3_MFPU_NEON_SUPPORTED)
|
||||
if (BLAKE3_MFPU_NEON_SUPPORTED)
|
||||
target_compile_options(blake3 PRIVATE -mfpu=neon)
|
||||
endif()
|
||||
|
||||
if (DEFINED BLAKE3_CFLAGS_NEON)
|
||||
set_source_files_properties(blake3_neon.c PROPERTIES COMPILE_FLAGS "${BLAKE3_CFLAGS_NEON}")
|
||||
|
||||
Reference in new issue
Block a user