mirror of
https://github.com/rui314/mold.git
synced 2026-11-09 04:49:33 -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 | ||
|
|
34c53777fb | ||
|
|
7be1b66791 | ||
|
|
4bd80ec21c | ||
|
|
a9ab0ff452 | ||
|
|
206bf65c50 | ||
|
|
2b3e8eec05 | ||
|
|
9eac818254 | ||
|
|
6fe0cadfa1 | ||
|
|
1a2baee096 | ||
|
|
728b265cbd | ||
|
|
9fa61b9298 |
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.0)
|
||||
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.0
|
||||
git checkout v2.3.2
|
||||
../install-build-deps.sh
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=c++ ..
|
||||
cmake --build . -j $(nproc)
|
||||
|
||||
@@ -1,17 +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 .
|
||||
|
||||
FROM ubuntu:18.04
|
||||
ENV TZ=Europe/London
|
||||
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 && \
|
||||
apt-get install -y --no-install-recommends build-essential wget libstdc++-11-dev zlib1g-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
|
||||
+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)
|
||||
@@ -16,25 +19,60 @@ case $# in
|
||||
arch="$1"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 [ x86_64 | aarch64 | arm | ppc64le | s390x ]"
|
||||
echo "Usage: $0 [ x86_64 | aarch64 | arm | riscv64 | ppc64le | s390x ]"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
echo "$arch" | grep -Eq '^(x86_64|aarch64|arm|ppc64le|s390x)$' || \
|
||||
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
|
||||
|
||||
docker run --platform linux/$arch -i --rm -v "$(pwd):/mold" \
|
||||
-e "OWNER=$(id -u):$(id -g)" rui314/mold-builder:latest \
|
||||
bash -c "mkdir /tmp/build &&
|
||||
cd /tmp/build &&
|
||||
cmake -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 -DMOLD_MOSTLY_STATIC=On -DCMAKE_BUILD_TYPE=Release /mold &&
|
||||
# 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
|
||||
|
||||
# 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 &&
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+19
-19
@@ -65,14 +65,12 @@ static u64 hi64(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.
|
||||
bool x = val & 0x800;
|
||||
bool y = (page(val + 0x800) - page(pc)) & 0x8000'0000;
|
||||
|
||||
if (x && !y)
|
||||
return val - 0x1'0000'0000;
|
||||
if (!x && y)
|
||||
return val + 0x1'0000'0000;
|
||||
return val;
|
||||
u64 x = page(val) - page(pc);
|
||||
if (val & 0x800)
|
||||
x += 0x1000 - 0x1'0000'0000;
|
||||
if (x & 0x8000'0000)
|
||||
x += 0x1'0000'0000;
|
||||
return x;
|
||||
}
|
||||
|
||||
static u64 higher20(u64 val, u64 pc) {
|
||||
@@ -174,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);
|
||||
@@ -325,10 +323,16 @@ 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:
|
||||
check(S + A - P, -(1LL << 31), 1LL << 31);
|
||||
write_j20(loc, hi20(S + A, P));
|
||||
break;
|
||||
case R_LARCH_PCALA64_LO20:
|
||||
@@ -341,7 +345,6 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
write_k12(loc, GOT + G + A);
|
||||
break;
|
||||
case R_LARCH_GOT_PC_HI20:
|
||||
check(GOT + G + A - P, -(1LL << 31), 1LL << 31);
|
||||
write_j20(loc, hi20(GOT + G + A, P));
|
||||
break;
|
||||
case R_LARCH_GOT64_PC_LO20:
|
||||
@@ -378,7 +381,6 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
write_k12(loc, sym.get_gottp_addr(ctx) + A);
|
||||
break;
|
||||
case R_LARCH_TLS_IE_PC_HI20:
|
||||
check(sym.get_gottp_addr(ctx) + A - P, -(1LL << 31), 1LL << 31);
|
||||
write_j20(loc, hi20(sym.get_gottp_addr(ctx) + A, P));
|
||||
break;
|
||||
case R_LARCH_TLS_IE64_PC_LO20:
|
||||
@@ -587,6 +589,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
scan_dyn_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_LARCH_B26:
|
||||
case R_LARCH_PCALA_HI20:
|
||||
if (sym.is_imported)
|
||||
sym.flags |= NEEDS_PLT;
|
||||
break;
|
||||
@@ -620,7 +623,6 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_LARCH_ABS_LO12:
|
||||
case R_LARCH_ABS64_LO20:
|
||||
case R_LARCH_ABS64_HI12:
|
||||
case R_LARCH_PCALA_HI20:
|
||||
case R_LARCH_PCALA_LO12:
|
||||
case R_LARCH_PCALA64_LO20:
|
||||
case R_LARCH_PCALA64_HI12:
|
||||
@@ -658,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));
|
||||
@@ -673,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;
|
||||
|
||||
+18
-33
@@ -140,7 +140,6 @@ struct NameType {
|
||||
};
|
||||
|
||||
struct MapValue {
|
||||
std::string_view name;
|
||||
u32 hash = 0;
|
||||
Atomic<u32> count = 0;
|
||||
u32 name_offset = 0;
|
||||
@@ -639,7 +638,7 @@ template <typename E>
|
||||
std::span<u8> get_buffer(Context<E> &ctx, Chunk<E> *chunk) {
|
||||
if (chunk->is_compressed)
|
||||
return chunk->uncompressed_data;
|
||||
return {ctx.buf + chunk->shdr.sh_offset, chunk->shdr.sh_size};
|
||||
return {ctx.buf + chunk->shdr.sh_offset, (size_t)chunk->shdr.sh_size};
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -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};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+26
-27
@@ -170,17 +170,17 @@ static void scan_rels(Context<E> &ctx, InputSection<E> &isec,
|
||||
|
||||
template <>
|
||||
void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
|
||||
using Thunk = RangeExtensionThunk<E>;
|
||||
|
||||
std::span<InputSection<E> *> m = members;
|
||||
if (m.empty())
|
||||
return;
|
||||
|
||||
m[0]->offset = 0;
|
||||
|
||||
// Initialize input sections with a dummy offset so that we can
|
||||
// distinguish sections that have got an address with the one who
|
||||
// haven't.
|
||||
tbb::parallel_for((i64)1, (i64)m.size(), [&](i64 i) {
|
||||
m[i]->offset = -1;
|
||||
tbb::parallel_for_each(m, [](InputSection<E> *isec) {
|
||||
isec->offset = -1;
|
||||
});
|
||||
|
||||
// We create thunks from the beginning of the section to the end.
|
||||
@@ -190,7 +190,7 @@ void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
|
||||
// Input sections between B and C are in the current batch.
|
||||
//
|
||||
// A is the input section with the smallest address than can reach
|
||||
// anywhere from the current batch.
|
||||
// from the current batch.
|
||||
//
|
||||
// D is the input section with the largest address such that the thunk
|
||||
// is reachable from the current batch if it's inserted right before D.
|
||||
@@ -198,9 +198,9 @@ void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
|
||||
// ................................ <input sections> ............
|
||||
// A B C D
|
||||
// ^ We insert a thunk for the current batch just before D
|
||||
// <---> The current batch, which is smaller than batch_size
|
||||
// <--------> Smaller than max_distance
|
||||
// <--------> Smaller than max_distance
|
||||
// <---> The current batch, which is smaller than BATCH_SIZE
|
||||
// <--------> Smaller than MAX_DISTANCE
|
||||
// <--------> Smaller than MAX_DISTANCE
|
||||
// <-------------> Reachable from the current batch
|
||||
i64 a = 0;
|
||||
i64 b = 0;
|
||||
@@ -212,41 +212,40 @@ void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
|
||||
i64 t = 0;
|
||||
|
||||
while (b < m.size()) {
|
||||
// Move D foward as far as we can jump from B to a thunk at D.
|
||||
auto d_thunk_end = [&] {
|
||||
u64 d_end = align_to(offset, 1 << m[d]->p2align) + m[d]->sh_size;
|
||||
return align_to(d_end, Thunk::alignment) + max_thunk_size;
|
||||
};
|
||||
|
||||
while (d < m.size() &&
|
||||
(b == d || d_thunk_end() <= m[b]->offset + max_distance())) {
|
||||
offset = align_to(offset, 1 << m[d]->p2align);
|
||||
m[d]->offset = offset;
|
||||
offset += m[d]->sh_size;
|
||||
d++;
|
||||
}
|
||||
|
||||
// Move C forward so that C is apart from B by BATCH_SIZE. We want
|
||||
// to make sure that there's at least one section between B and C
|
||||
// to ensure progress.
|
||||
c = b + 1;
|
||||
while (c < m.size() &&
|
||||
m[c]->offset + m[c]->sh_size < m[b]->offset + batch_size)
|
||||
while (c < d && m[c]->offset + m[c]->sh_size < m[b]->offset + batch_size)
|
||||
c++;
|
||||
|
||||
// Move D foward as far as we can jump from B to anywhere in a thunk at D.
|
||||
d = c;
|
||||
while (d < m.size() &&
|
||||
align_to(offset, 1 << m[d]->p2align) + m[d]->sh_size + max_thunk_size <
|
||||
m[b]->offset + max_distance())
|
||||
d++;
|
||||
|
||||
// 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++;
|
||||
|
||||
// Assign offsets to all sections before D.
|
||||
for (i64 i = b; i < d; i++) {
|
||||
offset = align_to(offset, 1 << m[i]->p2align);
|
||||
m[i]->offset = offset;
|
||||
offset += m[i]->sh_size;
|
||||
}
|
||||
|
||||
// Erase references to out-of-range thunks.
|
||||
while (t < thunks.size() && thunks[t]->offset < m[a]->offset)
|
||||
reset_thunk(*thunks[t++]);
|
||||
|
||||
// Create a new thunk and place it at D.
|
||||
offset = align_to(offset, RangeExtensionThunk<E>::alignment);
|
||||
offset = align_to(offset, Thunk::alignment);
|
||||
i64 thunk_idx = thunks.size();
|
||||
RangeExtensionThunk<E> *thunk = new RangeExtensionThunk<E>(*this, offset);
|
||||
Thunk *thunk = new Thunk(*this, offset);
|
||||
thunks.emplace_back(thunk);
|
||||
|
||||
// Scan relocations between B and C to collect symbols that need
|
||||
|
||||
@@ -36,7 +36,7 @@ gentoo-*)
|
||||
emerge-webrsync
|
||||
emerge dev-util/cmake
|
||||
;;
|
||||
arch-* | archarm-*)
|
||||
arch-* | archarm-* | artix-*)
|
||||
pacman -Sy
|
||||
pacman -S --needed --noconfirm base-devel cmake util-linux
|
||||
;;
|
||||
|
||||
+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();
|
||||
|
||||
@@ -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.
@@ -1,7 +1,14 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $GCC -c -o $t/a.o -ffunction-sections -fdata-sections -xc -
|
||||
# GCC 11 or older uses R_390_PLT32DBL to take an address of a function
|
||||
# instead of R_390_PC32DBL
|
||||
if [ $MACHINE = s390x ]; then
|
||||
echo 'void *foo() { return foo; }' | $CC -c -o $t/a.o -xc -
|
||||
readelf -r $t/a.o | grep -q R_390_PLT32DBL && skip
|
||||
fi
|
||||
|
||||
cat <<EOF | $CC -c -o $t/a.o -ffunction-sections -fdata-sections -xc -
|
||||
int bar() {
|
||||
return 5;
|
||||
}
|
||||
@@ -34,7 +41,7 @@ EOF
|
||||
$CC -B. -o $t/exe1 -Wl,-icf=safe $t/a.o $t/b.o
|
||||
$QEMU $t/exe1 | grep -q '^0 0$'
|
||||
|
||||
cat <<EOF | $GCC -c -o $t/c.o -ffunction-sections -fdata-sections -xc -
|
||||
cat <<EOF | $CC -c -o $t/c.o -ffunction-sections -fdata-sections -xc -
|
||||
int foo1();
|
||||
int foo2();
|
||||
int foo3();
|
||||
|
||||
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
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc - -mcmodel=extreme
|
||||
#include <stdio.h>
|
||||
char msg[] = "Hello world\n";
|
||||
int main() { printf(msg); }
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe1 $t/a.o
|
||||
$QEMU $t/exe1 | 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
|
||||
@@ -4,12 +4,12 @@
|
||||
[ $MACHINE = x86_64 ] || skip
|
||||
test_cflags -fcf-protection || skip
|
||||
|
||||
cat <<EOF | $GCC -o $t/a.o -c -xc - -ffunction-sections -O -fcf-protection
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc - -ffunction-sections -O -fcf-protection
|
||||
int foo() { return 3; }
|
||||
int bar() { return foo(); }
|
||||
EOF
|
||||
|
||||
cat <<EOF | $GCC -o $t/b.o -c -xc - -ffunction-sections -O -fcf-protection
|
||||
cat <<EOF | $CC -o $t/b.o -c -xc - -ffunction-sections -O -fcf-protection
|
||||
int main() {}
|
||||
EOF
|
||||
|
||||
|
||||
@@ -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}")
|
||||
|
||||
Vendored
+8
-7
@@ -18,21 +18,22 @@ As this C port was originally (see the [History](#history) section) *only* for t
|
||||
it may lag behind `rustc-demangle` in functionality, for now.
|
||||
|
||||
The current port status by category is:
|
||||
* **(UNPORTED)** `legacy` (pre-RFC2603 Rust symbols) demangling
|
||||
* **ported** `legacy` (pre-RFC2603 Rust symbols) demangling
|
||||
* `v0` ([RFC2603](https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html) Rust symbols) demangling
|
||||
* **ported** PRs:
|
||||
* [[#23] Support demangling the new Rust mangling scheme (v0).](https://github.com/rust-lang/rustc-demangle/pull/23)
|
||||
* [[#26] v0: allow identifiers to start with a digit.](https://github.com/rust-lang/rustc-demangle/pull/26)
|
||||
* [[#53] v0: replace `skip_*` methods with `print_*` methods in a "skip printing" mode.](https://github.com/rust-lang/rustc-demangle/pull/53)
|
||||
* arguably backported to Rust, as the C port always took this approach
|
||||
* **(UNPORTED)** symbol prefix flexibility (`__R` and `R`, instead of `_R`)
|
||||
* **(UNPORTED)** `min_const_generics` constants (`bool`, `char`, negative signed integers)
|
||||
* this arguably also includes `p` as an *untyped* placeholder constant
|
||||
* **(UNPORTED)** [`str` and structural constants](https://github.com/rust-lang/rfcs/pull/3161)
|
||||
(only usable in `const` generics on unstable Rust)
|
||||
* symbol prefix flexibility (`__R` and `R`, instead of `_R`)
|
||||
* [[#39] Add support for `min_const_generics` constants](https://github.com/rust-lang/rustc-demangle/pull/39)
|
||||
* [[#40] Elide the type when the const value is a placeholder `p`](https://github.com/rust-lang/rustc-demangle/pull/40)
|
||||
* [[#55] v0: demangle structural constants and &str.](https://github.com/rust-lang/rustc-demangle/pull/55)
|
||||
(only usable in `const` generics on unstable Rust)
|
||||
* **(UNPORTED)** recursion limits
|
||||
* miscellaneous
|
||||
* **(UNPORTED)** extraneous symbol suffix (e.g. `.llvm.*`) removal
|
||||
* **ported** PRs:
|
||||
* [[#30] v0: also support preserving extra suffixes found after mangled symbol.](https://github.com/rust-lang/rustc-demangle/pull/30)
|
||||
* **(UNPORTED)** output size limits
|
||||
|
||||
Notable differences (intentionally) introduced by porting:
|
||||
|
||||
+644
-80
@@ -70,6 +70,54 @@ static char next(struct rust_demangler *rdm) {
|
||||
return c;
|
||||
}
|
||||
|
||||
struct hex_nibbles {
|
||||
const char *nibbles;
|
||||
size_t nibbles_len;
|
||||
};
|
||||
|
||||
static struct hex_nibbles parse_hex_nibbles(struct rust_demangler *rdm) {
|
||||
struct hex_nibbles hex;
|
||||
|
||||
hex.nibbles = NULL;
|
||||
hex.nibbles_len = 0;
|
||||
|
||||
size_t start = rdm->next, hex_len = 0;
|
||||
while (!eat(rdm, '_')) {
|
||||
char c = next(rdm);
|
||||
CHECK_OR(IS_DIGIT(c) || (c >= 'a' && c <= 'f'), return hex);
|
||||
hex_len++;
|
||||
}
|
||||
|
||||
hex.nibbles = rdm->sym + start;
|
||||
hex.nibbles_len = hex_len;
|
||||
return hex;
|
||||
}
|
||||
|
||||
static struct hex_nibbles
|
||||
parse_hex_nibbles_for_const_uint(struct rust_demangler *rdm) {
|
||||
struct hex_nibbles hex = parse_hex_nibbles(rdm);
|
||||
CHECK_OR(!rdm->errored, return hex);
|
||||
|
||||
// Trim leading `0`s.
|
||||
while (hex.nibbles_len > 0 && *hex.nibbles == '0') {
|
||||
hex.nibbles++;
|
||||
hex.nibbles_len--;
|
||||
}
|
||||
|
||||
return hex;
|
||||
}
|
||||
|
||||
static struct hex_nibbles
|
||||
parse_hex_nibbles_for_const_bytes(struct rust_demangler *rdm) {
|
||||
struct hex_nibbles hex = parse_hex_nibbles(rdm);
|
||||
CHECK_OR(!rdm->errored && (hex.nibbles_len % 2 == 0), return hex);
|
||||
return hex;
|
||||
}
|
||||
|
||||
static uint8_t decode_hex_nibble(char nibble) {
|
||||
return nibble >= 'a' ? 10 + (nibble - 'a') : nibble - '0';
|
||||
}
|
||||
|
||||
static uint64_t parse_integer_62(struct rust_demangler *rdm) {
|
||||
if (eat(rdm, '_'))
|
||||
return 0;
|
||||
@@ -118,7 +166,10 @@ static struct rust_mangled_ident parse_ident(struct rust_demangler *rdm) {
|
||||
ident.punycode = NULL;
|
||||
ident.punycode_len = 0;
|
||||
|
||||
bool is_punycode = eat(rdm, 'u');
|
||||
bool is_punycode = false;
|
||||
if (rdm->version != -1) {
|
||||
is_punycode = eat(rdm, 'u');
|
||||
}
|
||||
|
||||
char c = next(rdm);
|
||||
CHECK_OR(IS_DIGIT(c), return ident);
|
||||
@@ -128,8 +179,10 @@ static struct rust_mangled_ident parse_ident(struct rust_demangler *rdm) {
|
||||
while (IS_DIGIT(peek(rdm)))
|
||||
len = len * 10 + (next(rdm) - '0');
|
||||
|
||||
// Skip past the optional `_` separator.
|
||||
eat(rdm, '_');
|
||||
if (rdm->version != -1) {
|
||||
// Skip past the optional `_` separator.
|
||||
eat(rdm, '_');
|
||||
}
|
||||
|
||||
size_t start = rdm->next;
|
||||
rdm->next += len;
|
||||
@@ -182,6 +235,63 @@ static void print_uint64_hex(struct rust_demangler *rdm, uint64_t x) {
|
||||
PRINT(s);
|
||||
}
|
||||
|
||||
static void
|
||||
print_quoted_escaped_char(struct rust_demangler *rdm, char quote, uint32_t c) {
|
||||
CHECK_OR(c < 0xd800 || (c > 0xdfff && c < 0x10ffff), return);
|
||||
|
||||
switch (c) {
|
||||
case '\0':
|
||||
PRINT("\\0");
|
||||
break;
|
||||
|
||||
case '\t':
|
||||
PRINT("\\t");
|
||||
break;
|
||||
|
||||
case '\r':
|
||||
PRINT("\\r");
|
||||
break;
|
||||
|
||||
case '\n':
|
||||
PRINT("\\n");
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
PRINT("\\\\");
|
||||
break;
|
||||
|
||||
case '"':
|
||||
if (quote == '"') {
|
||||
PRINT("\\\"");
|
||||
} else {
|
||||
PRINT("\"");
|
||||
}
|
||||
break;
|
||||
|
||||
case '\'':
|
||||
if (quote == '\'') {
|
||||
PRINT("\\'");
|
||||
} else {
|
||||
PRINT("'");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (c >= 0x20 && c <= 0x7e) {
|
||||
// Printable ASCII
|
||||
char v = (char)c;
|
||||
print_str(rdm, &v, 1);
|
||||
} else {
|
||||
// FIXME show printable unicode characters without hex encoding
|
||||
PRINT("\\u{");
|
||||
char s[9] = {0};
|
||||
sprintf(s, "%" PRIx32, c);
|
||||
PRINT(s);
|
||||
PRINT("}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_ident(struct rust_demangler *rdm, struct rust_mangled_ident ident) {
|
||||
if (rdm->errored || rdm->skipping_printing)
|
||||
@@ -197,12 +307,12 @@ print_ident(struct rust_demangler *rdm, struct rust_mangled_ident ident) {
|
||||
while (cap < ident.ascii_len) {
|
||||
cap *= 2;
|
||||
// Check for overflows.
|
||||
CHECK_OR((cap * 4) / 4 == cap, return );
|
||||
CHECK_OR((cap * 4) / 4 == cap, return);
|
||||
}
|
||||
|
||||
// Store the output codepoints as groups of 4 UTF-8 bytes.
|
||||
uint8_t *out = (uint8_t *)malloc(cap * 4);
|
||||
CHECK_OR(out, return );
|
||||
CHECK_OR(out, return);
|
||||
|
||||
// Populate initial output from ASCII fragment.
|
||||
for (len = 0; len < ident.ascii_len; len++) {
|
||||
@@ -342,14 +452,15 @@ static void demangle_generic_arg(struct rust_demangler *rdm);
|
||||
static void demangle_type(struct rust_demangler *rdm);
|
||||
static bool demangle_path_maybe_open_generics(struct rust_demangler *rdm);
|
||||
static void demangle_dyn_trait(struct rust_demangler *rdm);
|
||||
static void demangle_const(struct rust_demangler *rdm);
|
||||
static void demangle_const_uint(struct rust_demangler *rdm);
|
||||
static void demangle_const(struct rust_demangler *rdm, bool in_value);
|
||||
static void demangle_const_uint(struct rust_demangler *rdm, char ty_tag);
|
||||
static void demangle_const_str_literal(struct rust_demangler *rdm);
|
||||
|
||||
/// Optionally enter a binder ('G') for late-bound lifetimes,
|
||||
/// printing e.g. `for<'a, 'b> `, and make those lifetimes visible
|
||||
/// to the caller (via depth level, which the caller should reset).
|
||||
static void demangle_binder(struct rust_demangler *rdm) {
|
||||
CHECK_OR(!rdm->errored, return );
|
||||
CHECK_OR(!rdm->errored, return);
|
||||
|
||||
uint64_t bound_lifetimes = parse_opt_integer_62(rdm, 'G');
|
||||
if (bound_lifetimes > 0) {
|
||||
@@ -365,7 +476,7 @@ static void demangle_binder(struct rust_demangler *rdm) {
|
||||
}
|
||||
|
||||
static void demangle_path(struct rust_demangler *rdm, bool in_value) {
|
||||
CHECK_OR(!rdm->errored, return );
|
||||
CHECK_OR(!rdm->errored, return);
|
||||
|
||||
char tag = next(rdm);
|
||||
switch (tag) {
|
||||
@@ -383,7 +494,7 @@ static void demangle_path(struct rust_demangler *rdm, bool in_value) {
|
||||
}
|
||||
case 'N': {
|
||||
char ns = next(rdm);
|
||||
CHECK_OR(IS_LOWER(ns) || IS_UPPER(ns), return );
|
||||
CHECK_OR(IS_LOWER(ns) || IS_UPPER(ns), return);
|
||||
|
||||
demangle_path(rdm, in_value);
|
||||
|
||||
@@ -428,6 +539,7 @@ static void demangle_path(struct rust_demangler *rdm, bool in_value) {
|
||||
rdm->skipping_printing = true;
|
||||
demangle_path(rdm, in_value);
|
||||
rdm->skipping_printing = was_skipping_printing;
|
||||
__attribute__((fallthrough));
|
||||
case 'Y':
|
||||
PRINT("<");
|
||||
demangle_type(rdm);
|
||||
@@ -460,7 +572,7 @@ static void demangle_path(struct rust_demangler *rdm, bool in_value) {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ERROR_AND(return );
|
||||
ERROR_AND(return);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -469,7 +581,7 @@ static void demangle_generic_arg(struct rust_demangler *rdm) {
|
||||
uint64_t lt = parse_integer_62(rdm);
|
||||
print_lifetime_from_index(rdm, lt);
|
||||
} else if (eat(rdm, 'K'))
|
||||
demangle_const(rdm);
|
||||
demangle_const(rdm, false);
|
||||
else
|
||||
demangle_type(rdm);
|
||||
}
|
||||
@@ -525,7 +637,7 @@ static const char *basic_type(char tag) {
|
||||
}
|
||||
|
||||
static void demangle_type(struct rust_demangler *rdm) {
|
||||
CHECK_OR(!rdm->errored, return );
|
||||
CHECK_OR(!rdm->errored, return);
|
||||
|
||||
char tag = next(rdm);
|
||||
|
||||
@@ -565,11 +677,11 @@ static void demangle_type(struct rust_demangler *rdm) {
|
||||
demangle_type(rdm);
|
||||
if (tag == 'A') {
|
||||
PRINT("; ");
|
||||
demangle_const(rdm);
|
||||
demangle_const(rdm, true);
|
||||
}
|
||||
PRINT("]");
|
||||
break;
|
||||
case 'T':
|
||||
case 'T': {
|
||||
PRINT("(");
|
||||
size_t i;
|
||||
for (i = 0; !rdm->errored && !eat(rdm, 'E'); i++) {
|
||||
@@ -581,6 +693,7 @@ static void demangle_type(struct rust_demangler *rdm) {
|
||||
PRINT(",");
|
||||
PRINT(")");
|
||||
break;
|
||||
}
|
||||
case 'F': {
|
||||
uint64_t old_bound_lifetime_depth = rdm->bound_lifetime_depth;
|
||||
demangle_binder(rdm);
|
||||
@@ -652,7 +765,7 @@ static void demangle_type(struct rust_demangler *rdm) {
|
||||
// Restore `bound_lifetime_depth` to outside the binder.
|
||||
rdm->bound_lifetime_depth = old_bound_lifetime_depth;
|
||||
|
||||
CHECK_OR(eat(rdm, 'L'), return );
|
||||
CHECK_OR(eat(rdm, 'L'), return);
|
||||
uint64_t lt = parse_integer_62(rdm);
|
||||
if (lt) {
|
||||
PRINT(" + ");
|
||||
@@ -709,7 +822,7 @@ static bool demangle_path_maybe_open_generics(struct rust_demangler *rdm) {
|
||||
}
|
||||
|
||||
static void demangle_dyn_trait(struct rust_demangler *rdm) {
|
||||
CHECK_OR(!rdm->errored, return );
|
||||
CHECK_OR(!rdm->errored, return);
|
||||
|
||||
bool open = demangle_path_maybe_open_generics(rdm);
|
||||
|
||||
@@ -730,22 +843,17 @@ static void demangle_dyn_trait(struct rust_demangler *rdm) {
|
||||
PRINT(">");
|
||||
}
|
||||
|
||||
static void demangle_const(struct rust_demangler *rdm) {
|
||||
CHECK_OR(!rdm->errored, return );
|
||||
static void demangle_const(struct rust_demangler *rdm, bool in_value) {
|
||||
CHECK_OR(!rdm->errored, return);
|
||||
|
||||
if (eat(rdm, 'B')) {
|
||||
size_t backref = parse_integer_62(rdm);
|
||||
if (!rdm->skipping_printing) {
|
||||
size_t old_next = rdm->next;
|
||||
rdm->next = backref;
|
||||
demangle_const(rdm);
|
||||
rdm->next = old_next;
|
||||
}
|
||||
return;
|
||||
}
|
||||
bool opened_brace = false;
|
||||
|
||||
char ty_tag = next(rdm);
|
||||
switch (ty_tag) {
|
||||
case 'p':
|
||||
PRINT("_");
|
||||
break;
|
||||
|
||||
// Unsigned integer types.
|
||||
case 'h':
|
||||
case 't':
|
||||
@@ -753,66 +861,464 @@ static void demangle_const(struct rust_demangler *rdm) {
|
||||
case 'y':
|
||||
case 'o':
|
||||
case 'j':
|
||||
demangle_const_uint(rdm, ty_tag);
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_AND(return );
|
||||
case 'a':
|
||||
case 's':
|
||||
case 'l':
|
||||
case 'x':
|
||||
case 'n':
|
||||
case 'i':
|
||||
if (eat(rdm, 'n')) {
|
||||
PRINT("-");
|
||||
}
|
||||
demangle_const_uint(rdm, ty_tag);
|
||||
break;
|
||||
|
||||
case 'b': {
|
||||
struct hex_nibbles hex = parse_hex_nibbles_for_const_uint(rdm);
|
||||
CHECK_OR(!rdm->errored && hex.nibbles_len <= 1, return);
|
||||
uint8_t v = hex.nibbles_len > 0 ? decode_hex_nibble(hex.nibbles[0]) : 0;
|
||||
CHECK_OR(v <= 1, return);
|
||||
PRINT(v == 1 ? "true" : "false");
|
||||
break;
|
||||
}
|
||||
|
||||
if (eat(rdm, 'p'))
|
||||
PRINT("_");
|
||||
else {
|
||||
demangle_const_uint(rdm);
|
||||
if (rdm->verbose)
|
||||
PRINT(basic_type(ty_tag));
|
||||
case 'c': {
|
||||
struct hex_nibbles hex = parse_hex_nibbles_for_const_uint(rdm);
|
||||
CHECK_OR(!rdm->errored && hex.nibbles_len <= 6, return);
|
||||
|
||||
uint32_t c = 0;
|
||||
for (size_t i = 0; i < hex.nibbles_len; i++)
|
||||
c = (c << 4) | decode_hex_nibble(hex.nibbles[i]);
|
||||
|
||||
PRINT("'");
|
||||
print_quoted_escaped_char(rdm, '\'', c);
|
||||
PRINT("'");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'e':
|
||||
// NOTE(eddyb) a string literal `"..."` has type `&str`, so
|
||||
// to get back the type `str`, `*"..."` syntax is needed
|
||||
// (even if that may not be valid in Rust itself).
|
||||
if (!in_value) {
|
||||
opened_brace = true;
|
||||
PRINT("{");
|
||||
}
|
||||
PRINT("*");
|
||||
|
||||
demangle_const_str_literal(rdm);
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
case 'Q':
|
||||
if (ty_tag == 'R' && eat(rdm, 'e')) {
|
||||
// NOTE(eddyb) this prints `"..."` instead of `&*"..."`, which
|
||||
// is what `Re..._` would imply (see comment for `str` above).
|
||||
demangle_const_str_literal(rdm);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!in_value) {
|
||||
opened_brace = true;
|
||||
PRINT("{");
|
||||
}
|
||||
|
||||
PRINT("&");
|
||||
if (ty_tag != 'R') {
|
||||
PRINT("mut ");
|
||||
}
|
||||
|
||||
demangle_const(rdm, true);
|
||||
break;
|
||||
|
||||
case 'A': {
|
||||
if (!in_value) {
|
||||
opened_brace = true;
|
||||
PRINT("{");
|
||||
}
|
||||
|
||||
PRINT("[");
|
||||
|
||||
size_t i = 0;
|
||||
while (!eat(rdm, 'E')) {
|
||||
CHECK_OR(!rdm->errored, return);
|
||||
|
||||
if (i > 0)
|
||||
PRINT(", ");
|
||||
|
||||
demangle_const(rdm, true);
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
PRINT("]");
|
||||
break;
|
||||
}
|
||||
|
||||
case 'T': {
|
||||
if (!in_value) {
|
||||
opened_brace = true;
|
||||
PRINT("{");
|
||||
}
|
||||
|
||||
PRINT("(");
|
||||
|
||||
size_t i = 0;
|
||||
while (!eat(rdm, 'E')) {
|
||||
CHECK_OR(!rdm->errored, return);
|
||||
|
||||
if (i > 0)
|
||||
PRINT(", ");
|
||||
|
||||
demangle_const(rdm, true);
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if (i == 1)
|
||||
PRINT(",");
|
||||
|
||||
PRINT(")");
|
||||
break;
|
||||
}
|
||||
|
||||
case 'V':
|
||||
if (!in_value) {
|
||||
opened_brace = true;
|
||||
PRINT("{");
|
||||
}
|
||||
|
||||
demangle_path(rdm, true);
|
||||
|
||||
switch (next(rdm)) {
|
||||
case 'U':
|
||||
break;
|
||||
|
||||
case 'T': {
|
||||
PRINT("(");
|
||||
|
||||
size_t i = 0;
|
||||
while (!eat(rdm, 'E')) {
|
||||
CHECK_OR(!rdm->errored, return);
|
||||
|
||||
if (i > 0)
|
||||
PRINT(", ");
|
||||
|
||||
demangle_const(rdm, true);
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
PRINT(")");
|
||||
break;
|
||||
}
|
||||
|
||||
case 'S': {
|
||||
PRINT(" { ");
|
||||
|
||||
size_t i = 0;
|
||||
while (!eat(rdm, 'E')) {
|
||||
CHECK_OR(!rdm->errored, return);
|
||||
|
||||
if (i > 0)
|
||||
PRINT(", ");
|
||||
|
||||
parse_disambiguator(rdm);
|
||||
|
||||
struct rust_mangled_ident name = parse_ident(rdm);
|
||||
print_ident(rdm, name);
|
||||
|
||||
PRINT(": ");
|
||||
|
||||
demangle_const(rdm, true);
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
PRINT(" }");
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ERROR_AND(return);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'B': {
|
||||
size_t backref = parse_integer_62(rdm);
|
||||
if (!rdm->skipping_printing) {
|
||||
size_t old_next = rdm->next;
|
||||
rdm->next = backref;
|
||||
demangle_const(rdm, in_value);
|
||||
rdm->next = old_next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ERROR_AND(return);
|
||||
}
|
||||
|
||||
if (opened_brace) {
|
||||
PRINT("}");
|
||||
}
|
||||
}
|
||||
|
||||
static void demangle_const_uint(struct rust_demangler *rdm) {
|
||||
CHECK_OR(!rdm->errored, return );
|
||||
static void demangle_const_uint(struct rust_demangler *rdm, char ty_tag) {
|
||||
CHECK_OR(!rdm->errored, return);
|
||||
|
||||
uint64_t value = 0;
|
||||
size_t hex_len = 0;
|
||||
while (!eat(rdm, '_')) {
|
||||
value <<= 4;
|
||||
|
||||
char c = next(rdm);
|
||||
if (IS_DIGIT(c))
|
||||
value |= c - '0';
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
value |= 10 + (c - 'a');
|
||||
else
|
||||
ERROR_AND(return );
|
||||
hex_len++;
|
||||
}
|
||||
struct hex_nibbles hex = parse_hex_nibbles_for_const_uint(rdm);
|
||||
CHECK_OR(!rdm->errored, return);
|
||||
|
||||
// Print anything that doesn't fit in `uint64_t` verbatim.
|
||||
if (hex_len > 16) {
|
||||
if (hex.nibbles_len > 16) {
|
||||
PRINT("0x");
|
||||
print_str(rdm, rdm->sym + (rdm->next - hex_len), hex_len);
|
||||
return;
|
||||
print_str(rdm, hex.nibbles, hex.nibbles_len);
|
||||
} else {
|
||||
uint64_t v = 0;
|
||||
for (size_t i = 0; i < hex.nibbles_len; i++)
|
||||
v = (v << 4) | decode_hex_nibble(hex.nibbles[i]);
|
||||
print_uint64(rdm, v);
|
||||
}
|
||||
|
||||
print_uint64(rdm, value);
|
||||
if (rdm->verbose)
|
||||
PRINT(basic_type(ty_tag));
|
||||
}
|
||||
|
||||
// UTF-8 uses an unary encoding for its "length" field (`1`s followed by a `0`).
|
||||
struct utf8_byte {
|
||||
// Decoded "length" field of an UTF-8 byte, including the special cases:
|
||||
// - `0` indicates this is a lone ASCII byte
|
||||
// - `1` indicates a continuation byte (cannot start an UTF-8 sequence)
|
||||
size_t seq_len;
|
||||
|
||||
// Remaining (`payload_width`) bits in the UTF-8 byte, contributing to
|
||||
// the Unicode scalar value being encoded in the UTF-8 sequence.
|
||||
uint8_t payload;
|
||||
size_t payload_width;
|
||||
};
|
||||
static struct utf8_byte utf8_decode(uint8_t byte) {
|
||||
struct utf8_byte utf8;
|
||||
|
||||
utf8.seq_len = 0;
|
||||
utf8.payload = byte;
|
||||
utf8.payload_width = 8;
|
||||
|
||||
// FIXME(eddyb) figure out if using "count leading ones/zeros" is an option.
|
||||
while (utf8.seq_len <= 6) {
|
||||
uint8_t msb = 0x80 >> utf8.seq_len;
|
||||
utf8.payload &= ~msb;
|
||||
utf8.payload_width--;
|
||||
if ((byte & msb) == 0)
|
||||
break;
|
||||
utf8.seq_len++;
|
||||
}
|
||||
|
||||
return utf8;
|
||||
}
|
||||
|
||||
static void demangle_const_str_literal(struct rust_demangler *rdm) {
|
||||
CHECK_OR(!rdm->errored, return);
|
||||
|
||||
struct hex_nibbles hex = parse_hex_nibbles_for_const_bytes(rdm);
|
||||
CHECK_OR(!rdm->errored, return);
|
||||
|
||||
PRINT("\"");
|
||||
for (size_t i = 0; i < hex.nibbles_len; i += 2) {
|
||||
struct utf8_byte utf8 = utf8_decode(
|
||||
(decode_hex_nibble(hex.nibbles[i]) << 4) |
|
||||
decode_hex_nibble(hex.nibbles[i + 1])
|
||||
);
|
||||
uint32_t c = utf8.payload;
|
||||
if (utf8.seq_len > 0) {
|
||||
CHECK_OR(utf8.seq_len >= 2 && utf8.seq_len <= 4, return);
|
||||
for (size_t extra = utf8.seq_len - 1; extra > 0; extra--) {
|
||||
i += 2;
|
||||
utf8 = utf8_decode(
|
||||
(decode_hex_nibble(hex.nibbles[i]) << 4) |
|
||||
decode_hex_nibble(hex.nibbles[i + 1])
|
||||
);
|
||||
CHECK_OR(utf8.seq_len == 1, return);
|
||||
c = (c << utf8.payload_width) | utf8.payload;
|
||||
}
|
||||
}
|
||||
print_quoted_escaped_char(rdm, '"', c);
|
||||
}
|
||||
PRINT("\"");
|
||||
}
|
||||
|
||||
static bool is_rust_hash(struct rust_mangled_ident name) {
|
||||
if (name.ascii[0] != 'h') {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 1; i < name.ascii_len; i++) {
|
||||
if (!IS_DIGIT(name.ascii[i]) &&
|
||||
!(name.ascii[i] >= 'a' && name.ascii[i] <= 'f')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void print_legacy_ident(
|
||||
struct rust_demangler *rdm, struct rust_mangled_ident ident
|
||||
) {
|
||||
if (rdm->errored || rdm->skipping_printing)
|
||||
return;
|
||||
|
||||
CHECK_OR(!ident.punycode, return);
|
||||
|
||||
if (ident.ascii[0] == '_' && ident.ascii[1] == '$') {
|
||||
ident.ascii += 1;
|
||||
ident.ascii_len -= 1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (ident.ascii_len == 0) {
|
||||
break;
|
||||
} else if (ident.ascii[0] == '.') {
|
||||
if (ident.ascii_len >= 2 && ident.ascii[1] == '.') {
|
||||
PRINT("::");
|
||||
ident.ascii += 2;
|
||||
ident.ascii_len -= 2;
|
||||
} else {
|
||||
PRINT(".");
|
||||
ident.ascii += 1;
|
||||
ident.ascii_len -= 1;
|
||||
}
|
||||
} else if (ident.ascii[0] == '$') {
|
||||
const char *end_ptr =
|
||||
(const char *)memchr(&ident.ascii[1], '$', ident.ascii_len - 1);
|
||||
if (!end_ptr)
|
||||
break;
|
||||
const char *escape = &ident.ascii[1];
|
||||
size_t escape_len = end_ptr - escape;
|
||||
|
||||
if (strncmp(escape, "SP", 2) == 0) {
|
||||
PRINT("@");
|
||||
} else if (strncmp(escape, "BP", 2) == 0) {
|
||||
PRINT("*");
|
||||
} else if (strncmp(escape, "RF", 2) == 0) {
|
||||
PRINT("&");
|
||||
} else if (strncmp(escape, "LT", 2) == 0) {
|
||||
PRINT("<");
|
||||
} else if (strncmp(escape, "GT", 2) == 0) {
|
||||
PRINT(">");
|
||||
} else if (strncmp(escape, "LP", 2) == 0) {
|
||||
PRINT("(");
|
||||
} else if (strncmp(escape, "RP", 2) == 0) {
|
||||
PRINT(")");
|
||||
} else if (strncmp(escape, "C", 1) == 0) {
|
||||
PRINT(",");
|
||||
} else {
|
||||
if (escape[0] != 'u') {
|
||||
break;
|
||||
}
|
||||
|
||||
const char *digits = &escape[1];
|
||||
size_t digits_len = escape_len - 1;
|
||||
|
||||
bool invalid = false;
|
||||
for (size_t i = 1; i < digits_len; i++) {
|
||||
if (!IS_DIGIT(digits[i]) &&
|
||||
!(digits[i] >= 'a' && digits[i] <= 'f')) {
|
||||
invalid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (invalid)
|
||||
break;
|
||||
|
||||
struct hex_nibbles hex;
|
||||
|
||||
hex.nibbles = digits;
|
||||
hex.nibbles_len = digits_len;
|
||||
|
||||
uint32_t c = 0;
|
||||
for (size_t i = 0; i < hex.nibbles_len; i++)
|
||||
c = (c << 4) | decode_hex_nibble(hex.nibbles[i]);
|
||||
|
||||
if (!(c < 0xd800 || (c > 0xdfff && c < 0x10ffff))) {
|
||||
break; // Not a valid unicode scalar
|
||||
}
|
||||
|
||||
if (c >= 0x20 && c <= 0x7e) {
|
||||
// Printable ASCII
|
||||
char v = (char)c;
|
||||
print_str(rdm, &v, 1);
|
||||
} else {
|
||||
// FIXME show printable unicode characters without hex
|
||||
// encoding
|
||||
PRINT("\\u{");
|
||||
char s[9] = {0};
|
||||
sprintf(s, "%" PRIx32, c);
|
||||
PRINT(s);
|
||||
PRINT("}");
|
||||
}
|
||||
}
|
||||
|
||||
ident.ascii += escape_len + 2;
|
||||
ident.ascii_len -= escape_len + 2;
|
||||
} else {
|
||||
bool found = false;
|
||||
for (size_t i = 0; i < ident.ascii_len; i++) {
|
||||
if (ident.ascii[i] == '$' || ident.ascii[i] == '.') {
|
||||
print_str(rdm, ident.ascii, i);
|
||||
ident.ascii += i;
|
||||
ident.ascii_len -= i;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print_str(rdm, ident.ascii, ident.ascii_len);
|
||||
}
|
||||
|
||||
static void demangle_legacy_path(struct rust_demangler *rdm) {
|
||||
bool first = true;
|
||||
|
||||
while (1) {
|
||||
if (eat(rdm, 'E')) {
|
||||
// FIXME Maybe check if at end of symbol?
|
||||
return;
|
||||
}
|
||||
|
||||
struct rust_mangled_ident name = parse_ident(rdm);
|
||||
|
||||
if (!rdm->verbose && peek(rdm) == 'E' && is_rust_hash(name)) {
|
||||
// Skip printing the hash if verbose mode is disabled.
|
||||
eat(rdm, 'E');
|
||||
break;
|
||||
}
|
||||
|
||||
if (!first) {
|
||||
PRINT("::");
|
||||
}
|
||||
first = false;
|
||||
|
||||
print_legacy_ident(rdm, name);
|
||||
|
||||
CHECK_OR(!rdm->errored, return);
|
||||
}
|
||||
}
|
||||
|
||||
bool rust_demangle_with_callback(
|
||||
const char *mangled, int flags,
|
||||
const char *whole_mangled_symbol, int flags,
|
||||
void (*callback)(const char *data, size_t len, void *opaque), void *opaque
|
||||
) {
|
||||
// Rust symbols always start with _R.
|
||||
if (mangled[0] == '_' && mangled[1] == 'R')
|
||||
mangled += 2;
|
||||
else
|
||||
return false;
|
||||
|
||||
// Paths always start with uppercase characters.
|
||||
if (!IS_UPPER(mangled[0]))
|
||||
return false;
|
||||
|
||||
struct rust_demangler rdm;
|
||||
|
||||
rdm.sym = mangled;
|
||||
rdm.sym = whole_mangled_symbol;
|
||||
rdm.sym_len = 0;
|
||||
|
||||
rdm.callback_opaque = opaque;
|
||||
@@ -822,26 +1328,84 @@ bool rust_demangle_with_callback(
|
||||
rdm.errored = false;
|
||||
rdm.skipping_printing = false;
|
||||
rdm.verbose = (flags & RUST_DEMANGLE_FLAG_VERBOSE) != 0;
|
||||
rdm.version = 0;
|
||||
rdm.version = -2; // Invalid version
|
||||
rdm.bound_lifetime_depth = 0;
|
||||
|
||||
// Rust symbols use only [_0-9a-zA-Z] characters.
|
||||
for (const char *p = mangled; *p; p++) {
|
||||
if (!(*p == '_' || IS_DIGIT(*p) || IS_LOWER(*p) || IS_UPPER(*p)))
|
||||
// Rust symbols always start with R, _R or __R for the v0 scheme or ZN, _ZN
|
||||
// or __ZN for the legacy scheme.
|
||||
if (strncmp(rdm.sym, "_R", 2) == 0) {
|
||||
rdm.sym += 2;
|
||||
rdm.version = 0; // v0
|
||||
} else if (rdm.sym[0] == 'R') {
|
||||
// On Windows, dbghelp strips leading underscores, so we accept "R..."
|
||||
// form too.
|
||||
rdm.sym += 1;
|
||||
rdm.version = 0; // v0
|
||||
} else if (strncmp(rdm.sym, "__R", 3) == 0) {
|
||||
// On OSX, symbols are prefixed with an extra _
|
||||
rdm.sym += 3;
|
||||
rdm.version = 0; // v0
|
||||
} else if (strncmp(rdm.sym, "_ZN", 3) == 0) {
|
||||
rdm.sym += 3;
|
||||
rdm.version = -1; // legacy
|
||||
} else if (strncmp(rdm.sym, "ZN", 2) == 0) {
|
||||
// On Windows, dbghelp strips leading underscores, so we accept "R..."
|
||||
// form too.
|
||||
rdm.sym += 2;
|
||||
rdm.version = -1; // legacy
|
||||
} else if (strncmp(rdm.sym, "__ZN", 4) == 0) {
|
||||
// On OSX, symbols are prefixed with an extra _
|
||||
rdm.sym += 4;
|
||||
rdm.version = -1; // legacy
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rdm.version != -1) {
|
||||
// Paths always start with uppercase characters.
|
||||
if (!IS_UPPER(rdm.sym[0]))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Rust symbols only use ASCII characters.
|
||||
for (const char *p = rdm.sym; *p; p++) {
|
||||
if ((*p & 0x80) != 0)
|
||||
return false;
|
||||
|
||||
if (*p == '.' && strncmp(p, ".llvm.", 6) == 0) {
|
||||
// Ignore .llvm.<hash> suffixes
|
||||
break;
|
||||
}
|
||||
|
||||
rdm.sym_len++;
|
||||
}
|
||||
|
||||
demangle_path(&rdm, true);
|
||||
if (rdm.version == -1) {
|
||||
demangle_legacy_path(&rdm);
|
||||
} else {
|
||||
demangle_path(&rdm, true);
|
||||
|
||||
// Skip instantiating crate.
|
||||
if (!rdm.errored && rdm.next < rdm.sym_len) {
|
||||
rdm.skipping_printing = true;
|
||||
demangle_path(&rdm, false);
|
||||
// Skip instantiating crate.
|
||||
if (!rdm.errored && rdm.next < rdm.sym_len && peek(&rdm) >= 'A' &&
|
||||
peek(&rdm) <= 'Z') {
|
||||
rdm.skipping_printing = true;
|
||||
demangle_path(&rdm, false);
|
||||
}
|
||||
}
|
||||
|
||||
// It's an error to not reach the end.
|
||||
rdm.errored |= rdm.next != rdm.sym_len;
|
||||
if (!rdm.errored && (rdm.sym_len - rdm.next > 0)) {
|
||||
for (const char *p = rdm.sym + rdm.next; *p; p++) {
|
||||
// FIXME match is_symbol_like from rustc-demangle
|
||||
if (!((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') ||
|
||||
(*p >= '0' && *p <= '9') || *p == '.')) {
|
||||
// Suffix is not a symbol like string
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Print LLVM produced suffix
|
||||
print_str(&rdm, rdm.sym + rdm.next, rdm.sym_len - rdm.next);
|
||||
}
|
||||
|
||||
return !rdm.errored;
|
||||
}
|
||||
|
||||
+40
-8
@@ -54,8 +54,7 @@ pub fn try_demangle(s: &str) -> Result<Demangle<'_>, rustc_demangle::TryDemangle
|
||||
rustc_demangle: d,
|
||||
}),
|
||||
Err(e) => {
|
||||
// HACK(eddyb) ensure the C port also errors.
|
||||
assert_eq!(demangle_via_c(s, false), "");
|
||||
assert!(demangle_via_c(s, false).is_err());
|
||||
|
||||
Err(e)
|
||||
}
|
||||
@@ -68,6 +67,36 @@ impl fmt::Display for Demangle<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
// HACK(eddyb) the C port doesn't have the "is printable Unicode" heuristic,
|
||||
// to avoid having to include the non-trivial amount of data that requires,
|
||||
// so instead we allow mismatches when `b` has more `\u{...}` escapes than `a`.
|
||||
fn equal_modulo_unicode_escapes(a: &str, b: &str) -> bool {
|
||||
let mut a_chars = a.chars();
|
||||
let mut a_active_escape: Option<std::char::EscapeUnicode> = None;
|
||||
let mut b_chars = b.chars();
|
||||
loop {
|
||||
let a_ch = a_active_escape
|
||||
.as_mut()
|
||||
.and_then(|escape| escape.next())
|
||||
.or_else(|| {
|
||||
a_active_escape = None;
|
||||
a_chars.next()
|
||||
});
|
||||
let b_ch = b_chars.next();
|
||||
match (a_ch, b_ch) {
|
||||
(Some(a_ch), Some(b_ch)) if a_ch == b_ch => {}
|
||||
// Compare with the `\u{...}` escape instead, if possible.
|
||||
(Some(a_ch), Some('\\')) if a_active_escape.is_none() => {
|
||||
let mut escape = a_ch.escape_unicode();
|
||||
assert_eq!(escape.next(), Some('\\'));
|
||||
a_active_escape = Some(escape);
|
||||
}
|
||||
(None, None) => return true,
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Demangle<'_> {
|
||||
pub fn to_string_maybe_verbose(&self, verbose: bool) -> String {
|
||||
let rust = if verbose {
|
||||
@@ -75,8 +104,8 @@ impl Demangle<'_> {
|
||||
} else {
|
||||
format!("{:#}", self.rustc_demangle)
|
||||
};
|
||||
let c = demangle_via_c(self.original, verbose);
|
||||
if rust != c {
|
||||
let c = demangle_via_c(self.original, verbose).unwrap_or_else(|_| self.original.to_owned());
|
||||
if rust != c && !equal_modulo_unicode_escapes(&rust, &c) {
|
||||
panic!(
|
||||
"Rust vs C demangling difference:\
|
||||
\n mangled: {mangled:?}\
|
||||
@@ -91,7 +120,7 @@ impl Demangle<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn demangle_via_c(mangled: &str, verbose: bool) -> String {
|
||||
fn demangle_via_c(mangled: &str, verbose: bool) -> Result<String, ()> {
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::c_char;
|
||||
|
||||
@@ -101,15 +130,18 @@ fn demangle_via_c(mangled: &str, verbose: bool) -> String {
|
||||
}
|
||||
|
||||
let flags = if verbose { 1 } else { 0 };
|
||||
let mangled = CString::new(mangled).unwrap();
|
||||
let Ok(mangled) = CString::new(mangled) else {
|
||||
// C can't handle strings containing nul bytes
|
||||
return Err(());
|
||||
};
|
||||
let out = unsafe { rust_demangle(mangled.as_ptr(), flags) };
|
||||
if out.is_null() {
|
||||
String::new()
|
||||
Err(())
|
||||
} else {
|
||||
unsafe {
|
||||
let s = CStr::from_ptr(out).to_string_lossy().into_owned();
|
||||
free(out);
|
||||
s
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,8 +53,6 @@ fn ok_err(sym: &str) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle() {
|
||||
t_err!("test");
|
||||
@@ -63,8 +61,6 @@ fn demangle() {
|
||||
t!("_ZN4test1a2bcE", "test::a::bc");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_dollars() {
|
||||
t!("_ZN4$RP$E", ")");
|
||||
@@ -74,16 +70,12 @@ fn demangle_dollars() {
|
||||
t!("_ZN35Bar$LT$$u5b$u32$u3b$$u20$4$u5d$$GT$E", "Bar<[u32; 4]>");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_many_dollars() {
|
||||
t!("_ZN13test$u20$test4foobE", "test test::foob");
|
||||
t!("_ZN12test$BP$test4foobE", "test*test::foob");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_osx() {
|
||||
t!(
|
||||
@@ -94,8 +86,6 @@ fn demangle_osx() {
|
||||
t!("__ZN4core5slice89_$LT$impl$u20$core..iter..traits..IntoIterator$u20$for$u20$$RF$$u27$a$u20$$u5b$T$u5d$$GT$9into_iter17h450e234d27262170E", "core::slice::<impl core::iter::traits::IntoIterator for &'a [T]>::into_iter::h450e234d27262170");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_windows() {
|
||||
t!("ZN4testE", "test");
|
||||
@@ -103,8 +93,6 @@ fn demangle_windows() {
|
||||
t!("ZN12test$RF$test4foobE", "test&test::foob");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_elements_beginning_with_underscore() {
|
||||
t!("_ZN13_$LT$test$GT$E", "<test>");
|
||||
@@ -112,8 +100,6 @@ fn demangle_elements_beginning_with_underscore() {
|
||||
t!("_ZN15__STATIC_FMTSTRE", "__STATIC_FMTSTR");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_trait_impls() {
|
||||
t!(
|
||||
@@ -122,8 +108,6 @@ fn demangle_trait_impls() {
|
||||
);
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_without_hash() {
|
||||
let s = "_ZN3foo17h05af221e174051e9E";
|
||||
@@ -131,8 +115,6 @@ fn demangle_without_hash() {
|
||||
t_nohash!(s, "foo");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_without_hash_edgecases() {
|
||||
// One element, no hash.
|
||||
@@ -151,8 +133,6 @@ fn demangle_without_hash_edgecases() {
|
||||
t_nohash!("_ZN3foo17hg5af221e174051e9E", "foo::hg5af221e174051e9");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_thinlto() {
|
||||
// One element, no hash.
|
||||
@@ -164,23 +144,17 @@ fn demangle_thinlto() {
|
||||
);
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_llvm_ir_branch_labels() {
|
||||
t!("_ZN4core5slice77_$LT$impl$u20$core..ops..index..IndexMut$LT$I$GT$$u20$for$u20$$u5b$T$u5d$$GT$9index_mut17haf9727c2edfbc47bE.exit.i.i", "core::slice::<impl core::ops::index::IndexMut<I> for [T]>::index_mut::haf9727c2edfbc47b.exit.i.i");
|
||||
t_nohash!("_ZN4core5slice77_$LT$impl$u20$core..ops..index..IndexMut$LT$I$GT$$u20$for$u20$$u5b$T$u5d$$GT$9index_mut17haf9727c2edfbc47bE.exit.i.i", "core::slice::<impl core::ops::index::IndexMut<I> for [T]>::index_mut.exit.i.i");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_ignores_suffix_that_doesnt_look_like_a_symbol() {
|
||||
t_err!("_ZN3fooE.llvm moocow");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn dont_panic() {
|
||||
rust_demangle_c_test_harness::demangle("_ZN2222222222222222222222EE").to_string();
|
||||
@@ -196,22 +170,16 @@ fn dont_panic() {
|
||||
.to_string();
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn invalid_no_chop() {
|
||||
t_err!("_ZNfooE");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn handle_assoc_types() {
|
||||
t!("_ZN151_$LT$alloc..boxed..Box$LT$alloc..boxed..FnBox$LT$A$C$$u20$Output$u3d$R$GT$$u20$$u2b$$u20$$u27$a$GT$$u20$as$u20$core..ops..function..FnOnce$LT$A$GT$$GT$9call_once17h69e8f44b3723e1caE", "<alloc::boxed::Box<alloc::boxed::FnBox<A, Output=R> + 'a> as core::ops::function::FnOnce<A>>::call_once::h69e8f44b3723e1ca");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn handle_bang() {
|
||||
t!(
|
||||
@@ -220,8 +188,6 @@ fn handle_bang() {
|
||||
);
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_utf8_idents() {
|
||||
t_nohash!(
|
||||
@@ -230,8 +196,6 @@ fn demangle_utf8_idents() {
|
||||
);
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_issue_60925() {
|
||||
t_nohash!(
|
||||
|
||||
+8
-34
@@ -56,8 +56,6 @@ fn ok_err(sym: &str) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle() {
|
||||
t_err!("test");
|
||||
@@ -66,8 +64,6 @@ fn demangle() {
|
||||
t!("_ZN4test1a2bcE", "test::a::bc");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_dollars() {
|
||||
t!("_ZN4$RP$E", ")");
|
||||
@@ -77,16 +73,12 @@ fn demangle_dollars() {
|
||||
t!("_ZN35Bar$LT$$u5b$u32$u3b$$u20$4$u5d$$GT$E", "Bar<[u32; 4]>");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_many_dollars() {
|
||||
t!("_ZN13test$u20$test4foobE", "test test::foob");
|
||||
t!("_ZN12test$BP$test4foobE", "test*test::foob");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_osx() {
|
||||
t!(
|
||||
@@ -97,8 +89,6 @@ fn demangle_osx() {
|
||||
t!("__ZN4core5slice89_$LT$impl$u20$core..iter..traits..IntoIterator$u20$for$u20$$RF$$u27$a$u20$$u5b$T$u5d$$GT$9into_iter17h450e234d27262170E", "core::slice::<impl core::iter::traits::IntoIterator for &'a [T]>::into_iter::h450e234d27262170");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_windows() {
|
||||
t!("ZN4testE", "test");
|
||||
@@ -106,8 +96,6 @@ fn demangle_windows() {
|
||||
t!("ZN12test$RF$test4foobE", "test&test::foob");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_elements_beginning_with_underscore() {
|
||||
t!("_ZN13_$LT$test$GT$E", "<test>");
|
||||
@@ -115,8 +103,6 @@ fn demangle_elements_beginning_with_underscore() {
|
||||
t!("_ZN15__STATIC_FMTSTRE", "__STATIC_FMTSTR");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_trait_impls() {
|
||||
t!(
|
||||
@@ -125,8 +111,6 @@ fn demangle_trait_impls() {
|
||||
);
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_without_hash() {
|
||||
let s = "_ZN3foo17h05af221e174051e9E";
|
||||
@@ -134,8 +118,6 @@ fn demangle_without_hash() {
|
||||
t_nohash!(s, "foo");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_without_hash_edgecases() {
|
||||
// One element, no hash.
|
||||
@@ -154,8 +136,6 @@ fn demangle_without_hash_edgecases() {
|
||||
t_nohash!("_ZN3foo17hg5af221e174051e9E", "foo::hg5af221e174051e9");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_thinlto() {
|
||||
// One element, no hash.
|
||||
@@ -167,23 +147,17 @@ fn demangle_thinlto() {
|
||||
);
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_llvm_ir_branch_labels() {
|
||||
t!("_ZN4core5slice77_$LT$impl$u20$core..ops..index..IndexMut$LT$I$GT$$u20$for$u20$$u5b$T$u5d$$GT$9index_mut17haf9727c2edfbc47bE.exit.i.i", "core::slice::<impl core::ops::index::IndexMut<I> for [T]>::index_mut::haf9727c2edfbc47b.exit.i.i");
|
||||
t_nohash!("_ZN4core5slice77_$LT$impl$u20$core..ops..index..IndexMut$LT$I$GT$$u20$for$u20$$u5b$T$u5d$$GT$9index_mut17haf9727c2edfbc47bE.exit.i.i", "core::slice::<impl core::ops::index::IndexMut<I> for [T]>::index_mut.exit.i.i");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_ignores_suffix_that_doesnt_look_like_a_symbol() {
|
||||
t_err!("_ZN3fooE.llvm moocow");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn dont_panic() {
|
||||
rust_demangle_c_test_harness::demangle("_ZN2222222222222222222222EE").to_string();
|
||||
@@ -199,22 +173,16 @@ fn dont_panic() {
|
||||
.to_string();
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn invalid_no_chop() {
|
||||
t_err!("_ZNfooE");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn handle_assoc_types() {
|
||||
t!("_ZN151_$LT$alloc..boxed..Box$LT$alloc..boxed..FnBox$LT$A$C$$u20$Output$u3d$R$GT$$u20$$u2b$$u20$$u27$a$GT$$u20$as$u20$core..ops..function..FnOnce$LT$A$GT$$GT$9call_once17h69e8f44b3723e1caE", "<alloc::boxed::Box<alloc::boxed::FnBox<A, Output=R> + 'a> as core::ops::function::FnOnce<A>>::call_once::h69e8f44b3723e1ca");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn handle_bang() {
|
||||
t!(
|
||||
@@ -238,14 +206,20 @@ fn limit_recursion() {
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[ignore = "would slowly use up all RAM before being OOM-killed"]
|
||||
#[test]
|
||||
fn limit_output() {
|
||||
fn limit_output_oom_hazard() {
|
||||
assert_ends_with!(
|
||||
rust_demangle_c_test_harness::demangle("RYFG_FGyyEvRYFF_EvRYFFEvERLB_B_B_ERLRjB_B_B_")
|
||||
.to_string(),
|
||||
"{size limit reached}"
|
||||
);
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn limit_output() {
|
||||
// NOTE(eddyb) somewhat reduced version of the above, effectively
|
||||
// `<for<...> fn()>` with a larger number of lifetimes in `...`.
|
||||
assert_ends_with!(
|
||||
|
||||
@@ -92,8 +92,6 @@ fn demangle_const_generics_preview() {
|
||||
t_const_suffixed!("j7b_", "123", "usize");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_min_const_generics() {
|
||||
t_const!("p", "_");
|
||||
@@ -109,8 +107,6 @@ fn demangle_min_const_generics() {
|
||||
t_const!("c2202_", "'∂'");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_const_str() {
|
||||
t_const!("e616263_", "{*\"abc\"}");
|
||||
@@ -129,8 +125,6 @@ fn demangle_const_str() {
|
||||
);
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
// NOTE(eddyb) this uses the same strings as `demangle_const_str` and should
|
||||
// be kept in sync with it - while a macro could be used to generate both
|
||||
// `str` and `&str` tests, from a single list of strings, this seems clearer.
|
||||
@@ -152,8 +146,6 @@ fn demangle_const_ref_str() {
|
||||
);
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_const_ref() {
|
||||
t_const!("Rp", "{&_}");
|
||||
@@ -165,8 +157,6 @@ fn demangle_const_ref() {
|
||||
t_const!("QAE", "{&mut []}");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_const_array() {
|
||||
t_const!("AE", "{[]}");
|
||||
@@ -176,8 +166,6 @@ fn demangle_const_array() {
|
||||
t_const!("AAh1_h2_EAh3_h4_EE", "{[[1, 2], [3, 4]]}");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_const_tuple() {
|
||||
t_const!("TE", "{()}");
|
||||
@@ -189,8 +177,6 @@ fn demangle_const_tuple() {
|
||||
);
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_const_adt() {
|
||||
t_const!(
|
||||
@@ -223,8 +209,6 @@ fn demangle_exponential_explosion() {
|
||||
);
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_thinlto() {
|
||||
t_nohash!("_RC3foo.llvm.9D1C9369", "foo");
|
||||
@@ -232,8 +216,6 @@ fn demangle_thinlto() {
|
||||
t_nohash!("_RNvC9backtrace3foo.llvm.A5310EB9", "backtrace::foo");
|
||||
}
|
||||
|
||||
// FIXME(eddyb) port the relevant functionality to C.
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn demangle_extra_suffix() {
|
||||
// From alexcrichton/rustc-demangle#27:
|
||||
|
||||
Reference in new issue
Block a user