Compare commits

...
30 Commits
Author SHA1 Message Date
Rui Ueyama 49066ea329 Bump mold version to 2.3.3 2023-11-14 14:41:42 +09:00
Rui Ueyama 25ceab07f7 Build mold on Debian 8 (Jessie)
Debian 8 was released in April 2015, which is old enough for
binary distribution. Previously, we used to use Debian 10 (Bullseye),
which was released in July 2019.
2023-11-14 12:44:02 +09:00
Rui Ueyama 6e97393740 Fix tests 2023-11-14 12:44:02 +09:00
Rui Ueyama ff3d54d26f Fix a crash bug in --defsym
Fixes https://github.com/rui314/mold/issues/1108
2023-11-13 20:09:25 +09:00
Rui Ueyama 25c9cf3976 Refactor 2023-11-13 19:57:50 +09:00
Rui Ueyama 5f2fb08cfd Make dist.sh reproducible
dist.sh script should now produce bit-by-bit identical output files now.

Fixes https://github.com/rui314/mold/issues/1146
2023-11-13 17:14:37 +09:00
Rui Ueyama 60bc56848f Fix tests for Debian 9 2023-11-13 13:37:35 +09:00
Rui Ueyama 600ca381fd Reduce the size of Symbol by 2 bytes 2023-11-12 13:39:44 +09:00
Rui Ueyama da3f5dd4ec Fix --dynamic-list for DSOs
--dynamic-list, --export-dynamic-symbol and --export-dynamic-symbol-list
have different semantics for executables and DSOs. If the output is an
executable, they specify a list of symbols that are to be exported.
If the output is a shared object, they specify the list of symbols that
are to be interposable.

mold havne't implemented the latter semantics. This commit fixes that
issue.

Fixes https://github.com/rui314/mold/issues/1071
2023-11-12 13:00:24 +09:00
Rui Ueyama b59911b0c1 Refactor 2023-11-11 15:09:51 +09:00
Rui Ueyama 8131528305 Rename long .rodata section names 2023-11-10 15:07:19 +09:00
Rui Ueyama 6a71e7dd4b Refactor 2023-11-09 18:57:13 +09:00
Rui Ueyama a63b2f70b2 Fix CI 2023-11-09 15:47:25 +09:00
Rui Ueyama 3f88964527 Map .ctors/.dtors into .init_array/.fini_array
.init_array/.fini_array haven't completely replace .ctors/.dtors, so
we need to convert .ctors/.dtors to .init_array/.fini_array.

Fixes https://github.com/rui314/mold/issues/1113
2023-11-09 12:43:58 +09:00
Rui Ueyama e18dc70af0 Refactor 2023-11-08 22:04:14 +09:00
Rui Ueyama 0c6daad215 Add more tests 2023-11-07 15:20:31 +09:00
Rui Ueyama 8c35c80bb7 Enable qemu tests by default 2023-11-07 13:55:56 +09:00
Rui Ueyama d42ba3bae4 Fix CI 2023-11-07 13:48:48 +09:00
Rui Ueyama 8c337b25df Run power10 tests on qemu only if qemu and cross GCC suports power10 2023-11-07 12:04:22 +09:00
Rui Ueyama 8d1c08bd03 Simplify 2023-11-07 11:25:03 +09:00
Rui Ueyama 5599a6f24f Skip test only on qemu 2023-11-07 11:06:13 +09:00
Rui Ueyama ef9a610dd1 Run tests as long as qemu and cross compiler are available
So that we can catch bugs like e11d64b5c7
earlier.
2023-11-07 11:00:26 +09:00
Rui Ueyama 31c3b5397b [POWER10] Emit dynamic relocation for ifunc
Previously, we didn't emit a dynamic relocation for a statically-
initialized function pointer pointing to an ifunc on POWER10, e.g.

  int fn() __attribute__((ifunc("resolve_fn")));
  void *ptr = fn;

Fixes https://github.com/rui314/mold/issues/1142
2023-11-06 13:27:05 +09:00
Rui Ueyama e11d64b5c7 Do not run tests under qemu-i386 on i686 native host
Fixes https://github.com/rui314/mold/issues/1140
2023-11-06 12:44:28 +09:00
Rui Ueyama 9d91a2bc23 Revert "Do not crash if defsym'ed symbol didn't exist"
This reverts commit 5c7a75b3e4 and
8c41f21d3c because they broke CI.
2023-11-06 12:43:20 +09:00
Rui Ueyama 8c41f21d3c Fix CI 2023-11-05 22:30:43 +09:00
Rui Ueyama 5c7a75b3e4 Do not crash if defsym'ed symbol didn't exist
Fixes https://github.com/rui314/mold/issues/1108
2023-11-05 16:37:09 +09:00
Rui Ueyama 0804fbd095 Refactor 2023-11-05 16:11:27 +09:00
Rui Ueyama 0f78fa9c43 Attempt to fix tests on 32-bit x86
Fixes https://github.com/rui314/mold/issues/1140
2023-11-05 11:01:31 +09:00
Rui Ueyama d30e970eb8 Remove useless function call 2023-11-05 10:57:25 +09:00
49 changed files with 813 additions and 329 deletions

No files matched your search

+1 -1
View File
@@ -98,7 +98,7 @@ jobs:
echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
mkdir build
cd build
cmake -DMOLD_ENABLE_QEMU_TESTS=On -DMOLD_ENABLE_QEMU_TESTS_RV32=On -DMOLD_ENABLE_QEMU_TESTS_LOONGARCH=On ..
cmake ..
cmake --build . -j$(nproc)
ctest . -j$(nproc)
- name: archive test results
+1 -1
View File
@@ -44,7 +44,7 @@
# features and behave exactly the same.
cmake_minimum_required(VERSION 3.14)
project(mold VERSION 2.3.2)
project(mold VERSION 2.3.3)
include(CMakeDependentOption)
include(GNUInstallDirs)
+1 -1
View File
@@ -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.2
git checkout v2.3.3
../install-build-deps.sh
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=c++ ..
cmake --build . -j $(nproc)
+112 -50
View File
@@ -1,15 +1,44 @@
#!/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`).
# 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`).
#
# The mold executable created by this script is statically-linked to
# libstdc++ and libcrypto but dynamically-linked to libc, libm, libz
# and librt, as they almost always exist on any Linux systems.
# The mold executable created by this script is statically linked to
# libstdc++ but dynamically linked to libc, libm, libz, and librt, as
# these libraries almost always exist on any Linux system. We can't
# statically link libc because doing so would disable dlopen(), which is
# necessary to open the LTO linker plugin.
#
# This script aims to produce reproducible outputs. That means if you run
# the script twice on the same git commit, it should produce bit-by-bit
# identical binary files. This property is crucial as a countermeasure
# against supply chain attacks. With this, you can verify that the binary
# files distributed on the GitHub release pages are created from the
# commit with release tags by rebuilding the binaries yourself.
#
# Debian provides snapshot.debian.org to host all historical binary
# packages. We use it to construct Docker images pinned to a
# particular timestamp.
#
# We aim to use a reasonably old Debian version 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 an older version of glibc.
#
# We need GCC 10 or newer to build mold. If GCC 10 is not available on an
# old Debian version, we'll build it ourselves.
#
# You may need to run the following command to use Docker with Qemu:
#
# $ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
set -e -x
usage() {
echo "Usage: $0 [ x86_64 | aarch64 | arm | riscv64 | ppc64le | s390x ]"
exit 1
}
case $# in
0)
arch=$(uname -m)
@@ -19,60 +48,93 @@ case $# in
arch="$1"
;;
*)
echo "Usage: $0 [ x86_64 | aarch64 | arm | riscv64 | ppc64le | s390x ]"
exit 1
usage
esac
echo "$arch" | grep -Eq '^(x86_64|aarch64|arm|riscv64|ppc64le|s390x)$' || \
{ echo "Error: no docker image for $arch"; exit 1; }
echo "$arch" | grep -Eq '^(x86_64|aarch64|arm|riscv64|ppc64le|s390x)$' || usage
image=mold-builder-$arch:1
image=mold-builder-$arch
version=$(sed -n 's/^project(mold VERSION \(.*\))/\1/p' $(dirname $0)/CMakeLists.txt)
dest=mold-$version-$arch-linux
# 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 && \
# Create a Docker image.
case $arch in
x86_64)
# Debian 8 (Jessie) released in April 2015
cat <<EOF | docker build --platform $arch -t $image -
FROM debian:jessie-20210326
ENV DEBIAN_FRONTEND=noninteractive TZ=UTC
RUN sed -i -e '/^deb/d' -e 's/^# //g' /etc/apt/sources.list && \
echo 'Acquire { Retries "10"; http::timeout "10"; Check-Valid-Until "false"; };' > /etc/apt/apt.conf.d/80-retries && \
apt-get update && \
apt-get install -y --force-yes --no-install-recommends wget bzip2 file make autoconf gcc g++ libssl-dev && \
rm -rf /var/lib/apt/lists
# Build CMake 3.27
RUN mkdir -p /build/cmake && \
cd /build/cmake && \
wget -O- --no-check-certificate https://cmake.org/files/v3.27/cmake-3.27.7.tar.gz | tar xzf - --strip-components=1 && \
./bootstrap --parallel=$(nproc) && \
make -j$(nproc) && \
make install && \
rm -rf /build
# Build GCC 10
RUN mkdir -p /build/gcc && \
cd /build/gcc && \
wget -O- http://ftp.gnu.org/gnu/gcc/gcc-10.5.0/gcc-10.5.0.tar.gz | tar xzf - --strip-components=1 && \
mkdir isl gmp mpc mpfr && \
wget -O- http://gcc.gnu.org/pub/gcc/infrastructure/isl-0.18.tar.bz2 | tar xjf - --strip-components=1 -C isl && \
wget -O- http://ftp.gnu.org/gnu/gmp/gmp-6.1.2.tar.bz2 | tar xjf - --strip-components=1 -C gmp && \
wget -O- http://ftp.gnu.org/gnu/mpc/mpc-1.2.1.tar.gz | tar xzf - --strip-components=1 -C mpc && \
wget -O- http://ftp.gnu.org/gnu/mpfr/mpfr-4.1.0.tar.gz | tar xzf - --strip-components=1 -C mpfr && \
./configure --prefix=/usr --enable-languages=c,c++ --disable-bootstrap --disable-multilib && \
make -j$(nproc) && \
make install && \
ln -sf /usr/lib64/libstdc++.so.6 /usr/lib/x86_64-linux-gnu/libstdc++.so.6 && \
rm -rf /build
EOF
;;
aarch64 | arm | ppc64le | s390x)
# Debian 10 (Bullseye) released in July 2019
cat <<EOF | docker build --platform $arch -t $image -
FROM debian:bullseye-20231030
ENV DEBIAN_FRONTEND=noninteractive TZ=UTC
RUN sed -i -e '/^deb/d' -e 's/^# //g' /etc/apt/sources.list && \
echo 'Acquire { Retries "10"; http::timeout "10"; Check-Valid-Until "false"; };' > /etc/apt/apt.conf.d/80-retries && \
apt-get update && \
apt-get install -y --no-install-recommends build-essential gcc-10 g++-10 cmake && \
ln -sf /usr/bin/gcc-10 /usr/bin/cc && \
ln -sf /usr/bin/g++-10 /usr/bin/c++ && \
rm -rf /var/lib/apt/lists
EOF
else
cat <<EOF | docker build --platform $arch -t $image -
FROM ubuntu:18.04
;;
riscv64)
# snapshot.debian.org is not available for RISC-V binaries
cat <<EOF | docker build --platform $arch -t $image -
FROM riscv64/debian:unstable-20231030
ENV DEBIAN_FRONTEND=noninteractive TZ=UTC
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
apt-get install -y --no-install-recommends build-essential gcc-12 g++-12 cmake && \
ln -sf /usr/bin/gcc-12 /usr/bin/cc && \
ln -sf /usr/bin/g++-12 /usr/bin/c++ && \
rm -rf /var/lib/apt/lists
EOF
fi
fi
;;
esac
# We use the timestamp of the last Git commit as the file timestamp
# for build artifacts.
timestamp="$(git log -1 --format=%ci)"
# 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) &&
docker run --platform linux/$arch -i --rm -v "$(realpath $(dirname $0)):/mold" $image \
bash -c "mkdir -p /build/mold &&
cd /build/mold &&
cmake -DCMAKE_BUILD_TYPE=Release -DMOLD_MOSTLY_STATIC=On /mold &&
cmake --build . -j$(nproc) &&
ctest -j$(nproc) &&
cmake --install . --prefix $dest --strip &&
tar czf /mold/$dest.tar.gz $dest &&
chown \$OWNER /mold/$dest.tar.gz"
find $dest -print | xargs touch --no-dereference --date='$timestamp' &&
find $dest -print | sort | tar -cf - --no-recursion --files-from=- | gzip -9nc > /mold/$dest.tar.gz &&
chown $(id -u):$(id -g) /mold/$dest.tar.gz"
-5
View File
@@ -569,11 +569,6 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (!sym.file) {
record_undef_error(ctx, rel);
continue;
}
if (sym.is_ifunc())
sym.flags |= NEEDS_GOT | NEEDS_PLT;
+19 -18
View File
@@ -660,9 +660,9 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
else
Fatal(ctx) << "unknown --unresolved-symbols argument: " << arg;
} else if (read_arg("undefined") || read_arg("u")) {
ctx.arg.undefined.push_back(arg);
ctx.arg.undefined.push_back(get_symbol(ctx, arg));
} else if (read_arg("require-defined")) {
ctx.arg.require_defined.push_back(arg);
ctx.arg.require_defined.push_back(get_symbol(ctx, arg));
} else if (read_arg("init")) {
ctx.arg.init = arg;
} else if (read_arg("fini")) {
@@ -1104,21 +1104,21 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
} else if (read_flag("no-keep-memory")) {
} else if (read_arg("max-cache-size")) {
} else if (read_arg("version-script")) {
// --version-script, --dynamic-list and --export-dynamic-symbol[-list]
// are treated as positional arguments even though they are actually not
// positional. This is because linker scripts (a positional argument)
// can also specify a version script, and it's better to consolidate
// parsing in read_input_files. In particular, version scripts can
// modify ctx.default_version which we initialize *after* parsing
// non-positional args, so the parsing cannot be done right here.
// --version-script is treated as positional arguments even though
// they are actually not positional. This is because linker scripts
// (a positional argument) can also specify a version script, and
// it's better to consolidate parsing in read_input_files. In
// particular, version scripts can modify ctx.default_version which
// we initialize *after* parsing non-positional args, so the parsing
// cannot be done right here.
remaining.push_back("--version-script=" + std::string(arg));
} else if (read_arg("dynamic-list")) {
ctx.arg.Bsymbolic = true;
remaining.push_back("--dynamic-list=" + std::string(arg));
append(ctx.dynamic_list_patterns, parse_dynamic_list(ctx, arg));
} else if (read_arg("export-dynamic-symbol")) {
remaining.push_back("--export-dynamic-symbol=" + std::string(arg));
ctx.dynamic_list_patterns.push_back({arg, "<command line>"});
} else if (read_arg("export-dynamic-symbol-list")) {
remaining.push_back("--export-dynamic-symbol-list=" + std::string(arg));
append(ctx.dynamic_list_patterns, parse_dynamic_list(ctx, arg));
} else if (read_flag("as-needed")) {
remaining.push_back("--as-needed");
} else if (read_flag("no-as-needed")) {
@@ -1228,11 +1228,6 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
if (char *env = getenv("MOLD_REPRO"); env && env[0])
ctx.arg.repro = true;
if (ctx.arg.shared || ctx.arg.export_dynamic)
ctx.default_version = VER_NDX_GLOBAL;
else
ctx.default_version = VER_NDX_LOCAL;
if (ctx.arg.default_symver) {
std::string ver = ctx.arg.soname.empty() ?
filepath(ctx.arg.output).filename().string() : std::string(ctx.arg.soname);
@@ -1243,7 +1238,13 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
if (ctx.arg.shared && warn_shared_textrel)
ctx.arg.warn_textrel = true;
ctx.arg.undefined.push_back(ctx.arg.entry);
ctx.arg.undefined.push_back(get_symbol(ctx, ctx.arg.entry));
for (i64 i = 0; i < ctx.arg.defsyms.size(); i++) {
std::variant<Symbol<E> *, u64> &val = ctx.arg.defsyms[i].second;
if (Symbol<E> **sym = std::get_if<Symbol<E> *>(&val))
ctx.arg.undefined.push_back(*sym);
}
// --oformat=binary implies --strip-all because without a section
// header, there's no way to identify the locations of a symbol
+1
View File
@@ -161,6 +161,7 @@ enum : u32 {
VER_NDX_LOCAL = 0,
VER_NDX_GLOBAL = 1,
VER_NDX_LAST_RESERVED = 1,
VER_NDX_UNSPECIFIED = 0xffff,
};
enum : u32 {
+4 -4
View File
@@ -116,11 +116,11 @@ static void collect_root_set(Context<E> &ctx,
// Add sections referenced by root symbols.
enqueue_symbol(get_symbol(ctx, ctx.arg.entry));
for (std::string_view name : ctx.arg.undefined)
enqueue_symbol(get_symbol(ctx, name));
for (Symbol<E> *sym : ctx.arg.undefined)
enqueue_symbol(sym);
for (std::string_view name : ctx.arg.require_defined)
enqueue_symbol(get_symbol(ctx, name));
for (Symbol<E> *sym : ctx.arg.require_defined)
enqueue_symbol(sym);
// .eh_frame consists of variable-length records called CIE and FDE
// records, and they are a unit of inclusion or exclusion.
+22 -6
View File
@@ -63,7 +63,7 @@ void InputFile<E>::clear_symbols() {
sym->origin = 0;
sym->value = -1;
sym->sym_idx = -1;
sym->ver_idx = 0;
sym->ver_idx = VER_NDX_UNSPECIFIED;
sym->is_weak = false;
sym->is_imported = false;
sym->is_exported = false;
@@ -314,6 +314,15 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
continue;
}
if (shdr.sh_type == SHT_INIT_ARRAY ||
shdr.sh_type == SHT_FINI_ARRAY ||
shdr.sh_type == SHT_PREINIT_ARRAY)
ctx.has_init_array = true;
if (name == ".ctors" || name.starts_with(".ctors.") ||
name == ".dtors" || name.starts_with(".dtors."))
ctx.has_ctors = true;
if (name == ".eh_frame")
eh_frame_section = this->sections[i].get();
@@ -644,9 +653,13 @@ split_section(Context<E> &ctx, InputSection<E> &sec) {
if (entsize == 0)
return nullptr;
i64 addralign = shdr.sh_addralign;
if (addralign == 0)
addralign = 1;
std::unique_ptr<MergeableSection<E>> rec(new MergeableSection<E>);
rec->parent = MergedSection<E>::get_instance(ctx, sec.name(), shdr.sh_type,
shdr.sh_flags, entsize);
shdr.sh_flags, entsize, addralign);
rec->p2align = sec.p2align;
if (sec.sh_size == 0)
@@ -1277,17 +1290,17 @@ void SharedFile<E>::parse(Context<E> &ctx) {
continue;
std::string_view name = this->symbol_strtab.data() + esyms[i].st_name;
bool is_hidden = (!vers.empty() && (vers[i] & VERSYM_HIDDEN));
bool is_default = vers.empty() || !(vers[i] & VERSYM_HIDDEN);
this->elf_syms2.push_back(esyms[i]);
this->versyms.push_back(ver);
if (is_hidden) {
if (is_default) {
this->symbols.push_back(get_symbol(ctx, name));
} else {
std::string_view mangled_name = save_string(
ctx, std::string(name) + "@" + std::string(version_strings[ver]));
this->symbols.push_back(get_symbol(ctx, mangled_name, name));
} else {
this->symbols.push_back(get_symbol(ctx, name));
}
}
@@ -1342,6 +1355,9 @@ std::vector<std::string_view> SharedFile<E>::read_verdef(Context<E> &ctx) {
ElfVerdef<E> *ver = (ElfVerdef<E> *)verdef.data();
for (;;) {
if (ver->vd_ndx == VER_NDX_UNSPECIFIED)
Fatal(ctx) << *this << ": symbol version too large";
if (ret.size() <= ver->vd_ndx)
ret.resize(ver->vd_ndx + 1);
+1 -1
View File
@@ -274,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 ctx.arg.pic ? IFUNC_DYNREL : NONE;
return sym.is_pde_ifunc(ctx) ? NONE : IFUNC_DYNREL;
// 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
+20 -15
View File
@@ -312,7 +312,6 @@ read_version_script_commands(Context<E> &ctx, std::span<std::string_view> &tok,
if (tok[0] == "*") {
ctx.default_version = (is_global ? ver_idx : (u32)VER_NDX_LOCAL);
ctx.default_version_from_version_script = true;
} else if (is_global) {
ctx.version_patterns.push_back({unquote(tok[0]), current_file<E>->name,
ver_str, ver_idx, is_cpp});
@@ -367,7 +366,9 @@ void parse_version_script(Context<E> &ctx, MappedFile<Context<E>> *mf) {
}
template <typename E>
void read_dynamic_list_commands(Context<E> &ctx, std::span<std::string_view> &tok,
void read_dynamic_list_commands(Context<E> &ctx,
std::vector<DynamicPattern> &result,
std::span<std::string_view> &tok,
bool is_cpp) {
while (!tok.empty() && tok[0] != "}") {
if (tok[0] == "extern") {
@@ -376,11 +377,11 @@ void read_dynamic_list_commands(Context<E> &ctx, std::span<std::string_view> &to
if (!tok.empty() && tok[0] == "\"C\"") {
tok = tok.subspan(1);
tok = skip(ctx, tok, "{");
read_dynamic_list_commands(ctx, tok, false);
read_dynamic_list_commands(ctx, result, tok, false);
} else {
tok = skip(ctx, tok, "\"C++\"");
tok = skip(ctx, tok, "{");
read_dynamic_list_commands(ctx, tok, true);
read_dynamic_list_commands(ctx, result, tok, true);
}
tok = skip(ctx, tok, "}");
@@ -388,29 +389,32 @@ void read_dynamic_list_commands(Context<E> &ctx, std::span<std::string_view> &to
continue;
}
if (tok[0] == "*")
ctx.default_version = VER_NDX_GLOBAL;
else
ctx.version_patterns.push_back({unquote(tok[0]), current_file<E>->name,
"global", VER_NDX_GLOBAL, is_cpp});
result.push_back({unquote(tok[0]), "", is_cpp});
tok = skip(ctx, tok.subspan(1), ";");
}
}
template <typename E>
void parse_dynamic_list(Context<E> &ctx, MappedFile<Context<E>> *mf) {
current_file<E> = mf;
std::vector<std::string_view> vec = tokenize(ctx, mf->get_contents());
std::vector<DynamicPattern>
parse_dynamic_list(Context<E> &ctx, std::string_view path) {
std::string_view contents =
MappedFile<Context<E>>::must_open(ctx, std::string(path))->get_contents();
std::vector<std::string_view> vec = tokenize(ctx, contents);
std::span<std::string_view> tok = vec;
std::vector<DynamicPattern> result;
tok = skip(ctx, tok, "{");
read_dynamic_list_commands(ctx, tok, false);
read_dynamic_list_commands(ctx, result, tok, false);
tok = skip(ctx, tok, "}");
tok = skip(ctx, tok, ";");
if (!tok.empty())
SyntaxError(ctx, tok[0]) << "trailing garbage token";
for (DynamicPattern &p : result)
p.source = path;
return result;
}
using E = MOLD_TARGET;
@@ -418,6 +422,7 @@ using E = MOLD_TARGET;
template void parse_linker_script(Context<E> &, MappedFile<Context<E>> *);
template std::string_view get_script_output_type(Context<E> &, MappedFile<Context<E>> *);
template void parse_version_script(Context<E> &, MappedFile<Context<E>> *);
template void parse_dynamic_list(Context<E> &, MappedFile<Context<E>> *);
template std::vector<DynamicPattern> parse_dynamic_list(Context<E> &, std::string_view);
} // namespace mold::elf
+3 -2
View File
@@ -709,8 +709,9 @@ std::vector<ObjectFile<E> *> do_lto(Context<E> &ctx) {
get_symbol(ctx, y)->referenced_by_regular_obj = true;
}
// Handle --entry
get_symbol(ctx, ctx.arg.entry)->referenced_by_regular_obj = true;
// Keep some symbols
for (Symbol<E> *sym : ctx.arg.undefined)
sym->referenced_by_regular_obj = true;
// all_symbols_read_hook() calls add_input_file() and add_input_library()
LOG << "all symbols read\n";
+8 -19
View File
@@ -299,22 +299,6 @@ static void read_input_files(Context<E> &ctx, std::span<std::string> args) {
if (!mf)
Fatal(ctx) << "--version-script: file not found: " << arg;
parse_version_script(ctx, mf);
} else if (remove_prefix(arg, "--dynamic-list=")) {
MappedFile<Context<E>> *mf = find_from_search_paths(ctx, std::string(arg));
if (!mf)
Fatal(ctx) << "--dynamic-list: file not found: " << arg;
parse_dynamic_list(ctx, mf);
} else if (remove_prefix(arg, "--export-dynamic-symbol=")) {
if (arg == "*")
ctx.default_version = VER_NDX_GLOBAL;
else
ctx.version_patterns.push_back({arg, "--export-dynamic-symbol",
"global", VER_NDX_GLOBAL, false});
} else if (remove_prefix(arg, "--export-dynamic-symbol-list=")) {
MappedFile<Context<E>> *mf = find_from_search_paths(ctx, std::string(arg));
if (!mf)
Fatal(ctx) << "--export-dynamic-symbol-list: file not found: " << arg;
parse_dynamic_list(ctx, mf);
} else if (arg == "--push-state") {
state.push_back({ctx.as_needed, ctx.whole_archive, ctx.is_static,
ctx.in_lib});
@@ -523,9 +507,9 @@ int elf_main(int argc, char **argv) {
write_repro_file(ctx);
// Handle --require-defined
for (std::string_view name : ctx.arg.require_defined)
if (!get_symbol(ctx, name)->file)
Error(ctx) << "--require-defined: undefined symbol: " << name;
for (Symbol<E> *sym : ctx.arg.require_defined)
if (!sym->file)
Error(ctx) << "--require-defined: undefined symbol: " << *sym;
// .init_array and .fini_array contents have to be sorted by
// a special rule. Sort them.
@@ -535,6 +519,11 @@ int elf_main(int argc, char **argv) {
// because they are superceded by .init_array/.fini_array, though.
sort_ctor_dtor(ctx);
// If .ctors/.dtors are to be placed to .init_array/.fini_array,
// we need to reverse their contents.
if (ctx.has_init_array && ctx.has_ctors)
fixup_ctors_in_init_array(ctx);
// Handle --shuffle-sections
if (ctx.arg.shuffle_sections != SHUFFLE_SECTIONS_NONE)
shuffle_sections(ctx);
+17 -9
View File
@@ -768,7 +768,7 @@ class MergedSection : public Chunk<E> {
public:
static MergedSection<E> *
get_instance(Context<E> &ctx, std::string_view name, i64 type, i64 flags,
i64 entsize);
i64 entsize, i64 addralign);
SectionFragment<E> *insert(Context<E> &ctx, std::string_view data,
u64 hash, i64 p2align);
@@ -1281,8 +1281,15 @@ get_script_output_type(Context<E> &ctx, MappedFile<Context<E>> *mf);
template <typename E>
void parse_version_script(Context<E> &ctx, MappedFile<Context<E>> *mf);
struct DynamicPattern {
std::string_view pattern;
std::string_view source;
bool is_cpp = false;
};
template <typename E>
void parse_dynamic_list(Context<E> &ctx, MappedFile<Context<E>> *mf);
std::vector<DynamicPattern>
parse_dynamic_list(Context<E> &ctx, std::string_view path);
//
// lto.cc
@@ -1372,6 +1379,7 @@ template <typename E> void check_duplicate_symbols(Context<E> &);
template <typename E> void check_symbol_types(Context<E> &);
template <typename E> void sort_init_fini(Context<E> &);
template <typename E> void sort_ctor_dtor(Context<E> &);
template <typename E> void fixup_ctors_in_init_array(Context<E> &);
template <typename E> void shuffle_sections(Context<E> &);
template <typename E> void compute_section_sizes(Context<E> &);
template <typename E> void sort_output_sections(Context<E> &);
@@ -1718,6 +1726,8 @@ struct Context {
std::unordered_set<std::string_view> ignore_ir_file;
std::unordered_set<std::string_view> wrap;
std::vector<SectionOrder> section_order;
std::vector<Symbol<E> *> require_defined;
std::vector<Symbol<E> *> undefined;
std::vector<std::pair<Symbol<E> *, std::variant<Symbol<E> *, u64>>> defsyms;
std::vector<std::string> library_paths;
std::vector<std::string> plugin_opt;
@@ -1725,20 +1735,16 @@ struct Context {
std::vector<std::string_view> auxiliary;
std::vector<std::string_view> exclude_libs;
std::vector<std::string_view> filter;
std::vector<std::string_view> require_defined;
std::vector<std::string_view> trace_symbol;
std::vector<std::string_view> undefined;
u64 image_base = 0x200000;
} arg;
std::vector<VersionPattern> version_patterns;
u16 default_version = VER_NDX_GLOBAL;
std::vector<DynamicPattern> dynamic_list_patterns;
i64 default_version = VER_NDX_UNSPECIFIED;
i64 page_size = E::page_size;
std::optional<int> global_lock_fd;
// true if default_version is set by a wildcard in version script.
bool default_version_from_version_script = false;
// Reader context
bool as_needed = false;
bool whole_archive = false;
@@ -1750,6 +1756,8 @@ struct Context {
bool has_error = false;
bool has_lto_object = false;
Atomic<bool> has_init_array = false;
Atomic<bool> has_ctors = false;
// Symbol table
tbb::concurrent_hash_map<std::string_view, Symbol<E>, HashCmp> symbol_map;
@@ -2031,7 +2039,7 @@ public:
i32 sym_idx = -1;
i32 aux_idx = -1;
u16 ver_idx = 0;
u16 ver_idx = VER_NDX_UNSPECIFIED;
// `flags` has NEEDS_ flags.
Atomic<u8> flags = 0;
+25 -5
View File
@@ -1912,11 +1912,26 @@ void GnuHashSection<E>::copy_buf(Context<E> &ctx) {
template <typename E>
std::string_view
get_merged_output_name(Context<E> &ctx, std::string_view name, u64 flags) {
get_merged_output_name(Context<E> &ctx, std::string_view name, u64 flags,
i64 entsize, i64 addralign) {
if (ctx.arg.relocatable && !ctx.arg.relocatable_merge_sections)
return name;
if (ctx.arg.unique && ctx.arg.unique->match(name))
return name;
// GCC seems to create sections named ".rodata.strN.<mangled-symbol-name>.M".
// We want to eliminate the symbol name part from the section name.
if ((flags & SHF_STRINGS) && name.starts_with(".rodata.")) {
if (entsize == 1 && addralign == 1)
return ".rodata.str1.1";
if (entsize == 2 && addralign == 2)
return ".rodata.str2.2";
if (entsize == 4 && addralign == 4)
return ".rodata.str4.4";
return save_string(ctx,".rodata.str"s + std::to_string(entsize) + "." +
std::to_string(addralign));
}
return name;
}
@@ -1932,8 +1947,9 @@ MergedSection<E>::MergedSection(std::string_view name, i64 flags, i64 type,
template <typename E>
MergedSection<E> *
MergedSection<E>::get_instance(Context<E> &ctx, std::string_view name,
i64 type, i64 flags, i64 entsize) {
name = get_merged_output_name(ctx, name, flags);
i64 type, i64 flags,
i64 entsize, i64 addralign) {
name = get_merged_output_name(ctx, name, flags, entsize, addralign);
flags = flags & ~(u64)SHF_GROUP & ~(u64)SHF_COMPRESSED;
auto find = [&]() -> MergedSection * {
@@ -2534,8 +2550,12 @@ void VerdefSection<E>::construct(Context<E> &ctx) {
for (std::string_view verstr : ctx.arg.version_definitions)
write(verstr, idx++, 0);
for (Symbol<E> *sym : std::span<Symbol<E> *>(ctx.dynsym->symbols).subspan(1))
ctx.versym->contents[sym->get_dynsym_idx(ctx)] = sym->ver_idx;
for (Symbol<E> *sym : std::span<Symbol<E> *>(ctx.dynsym->symbols).subspan(1)) {
i64 ver = sym->ver_idx;
if (ver == VER_NDX_UNSPECIFIED)
ver = VER_NDX_GLOBAL;
ctx.versym->contents[sym->get_dynsym_idx(ctx)] = ver;
}
}
template <typename E>
+234 -115
View File
@@ -189,15 +189,13 @@ void create_synthetic_sections(Context<E> &ctx) {
template <typename E>
static void mark_live_objects(Context<E> &ctx) {
auto mark_symbol = [&](std::string_view name) {
if (InputFile<E> *file = get_symbol(ctx, name)->file)
file->is_alive = true;
};
for (Symbol<E> *sym : ctx.arg.undefined)
if (sym->file)
sym->file->is_alive = true;
for (std::string_view name : ctx.arg.undefined)
mark_symbol(name);
for (std::string_view name : ctx.arg.require_defined)
mark_symbol(name);
for (Symbol<E> *sym : ctx.arg.require_defined)
if (sym->file)
sym->file->is_alive = true;
std::vector<InputFile<E> *> roots;
@@ -397,7 +395,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, 1);
SHF_MERGE | SHF_STRINGS, 1, 1);
std::string_view buf = save_string(ctx, str);
std::string_view data(buf.data(), buf.size() + 1);
@@ -510,9 +508,31 @@ get_output_name(Context<E> &ctx, std::string_view name, u64 flags) {
return name;
}
// Returns true if a given input section is a .ctors/.dtors that
// should be put into .init_array/.fini_array.
//
// CRT object files contain .ctors/.dtors sections without any
// relocations. They contain sentinel values, 0 and -1, to mark the
// beginning and the end of the initializer/finalizer pointer arrays. We
// do not place them into .init_array/.fini_array because such invalid
// pointer values would simply make the program to crash.
template <typename E>
static bool is_ctors_in_init_array(Context<E> &ctx, InputSection<E> &isec) {
std::string_view name = isec.name();
return ctx.has_init_array && !isec.get_rels(ctx).empty() &&
(name == ".ctors" || name.starts_with(".ctors.") ||
name == ".dtors" || name.starts_with(".dtors."));
}
template <typename E>
static OutputSectionKey
get_output_section_key(Context<E> &ctx, InputSection<E> &isec) {
if (is_ctors_in_init_array(ctx, isec)) {
if (isec.name().starts_with(".ctors"))
return {".init_array", SHT_INIT_ARRAY, SHF_ALLOC | SHF_WRITE};
return {".fini_array", SHT_FINI_ARRAY, SHF_ALLOC | SHF_WRITE};
}
const ElfShdr<E> &shdr = isec.shdr();
std::string_view name = get_output_name(ctx, isec.name(), shdr.sh_flags);
u64 type = canonicalize_type<E>(name, shdr.sh_type);
@@ -523,6 +543,7 @@ get_output_section_key(Context<E> &ctx, InputSection<E> &isec) {
// So is .fini_array.
if (type == SHT_INIT_ARRAY || type == SHT_FINI_ARRAY)
flags |= SHF_WRITE;
return {name, type, flags};
}
@@ -661,34 +682,9 @@ void create_internal_file(Context<E> &ctx) {
ctx.internal_esyms.push_back(esym);
};
auto add_undef = [&](Symbol<E> *sym) {
obj->symbols.push_back(sym);
sym->value = 0xdeadbeef;
ElfSym<E> esym;
memset(&esym, 0, sizeof(esym));
esym.st_type = STT_NOTYPE;
esym.st_shndx = SHN_UNDEF;
esym.st_bind = STB_GLOBAL;
esym.st_visibility = STV_DEFAULT;
ctx.internal_esyms.push_back(esym);
};
// Add --defsym symbols
for (i64 i = 0; i < ctx.arg.defsyms.size(); i++) {
Symbol<E> *sym = ctx.arg.defsyms[i].first;
std::variant<Symbol<E> *, u64> val = ctx.arg.defsyms[i].second;
add(sym);
if (Symbol<E> **ref = std::get_if<Symbol<E> *>(&val)) {
// Add an undefined symbol to keep a reference to the defsym target.
// This prevents elimination by e.g. LTO or gc-sections.
// The undefined symbol will never make to the final object file; we
// double-check that the defsym target is not undefined in
// fix_synthetic_symbols.
add_undef(*ref);
}
}
// Add --defsym'd symbols
for (i64 i = 0; i < ctx.arg.defsyms.size(); i++)
add(ctx.arg.defsyms[i].first);
// Add --section-order symbols
for (SectionOrder &ord : ctx.arg.section_order)
@@ -821,26 +817,27 @@ void add_synthetic_symbols(Context<E> &ctx) {
// Handle --defsym symbols.
for (i64 i = 0; i < ctx.arg.defsyms.size(); i++) {
Symbol<E> *sym = ctx.arg.defsyms[i].first;
Symbol<E> *sym1 = ctx.arg.defsyms[i].first;
std::variant<Symbol<E> *, u64> val = ctx.arg.defsyms[i].second;
Symbol<E> *target = nullptr;
if (Symbol<E> **ref = std::get_if<Symbol<E> *>(&val))
target = *ref;
if (Symbol<E> **ref = std::get_if<Symbol<E> *>(&val)) {
Symbol<E> *sym2 = *ref;
if (!sym2->file) {
Error(ctx) << "--defsym: undefined symbol: " << *sym2;
continue;
}
// If the alias refers another symobl, copy ELF symbol attributes.
if (target) {
ElfSym<E> &esym = obj.elf_syms[i + 1];
esym.st_type = target->esym().st_type;
esym.st_type = sym2->esym().st_type;
if constexpr (is_ppc64v2<E>)
esym.ppc_local_entry = target->esym().ppc_local_entry;
esym.ppc_local_entry = sym2->esym().ppc_local_entry;
if (sym2->is_absolute())
sym1->origin = 0;
} else {
sym1->origin = 0;
}
// Make the target absolute if necessary.
if (!target || target->is_absolute())
sym->origin = 0;
}
}
template <typename E>
@@ -1047,19 +1044,42 @@ void check_symbol_types(Context<E> &ctx) {
});
}
template <typename E>
static i64 get_init_fini_priority(InputSection<E> *isec) {
static std::regex re(R"(\.(\d+)$)", std::regex_constants::optimize);
std::string_view name = isec->name();
std::cmatch m;
if (std::regex_search(name.data(), name.data() + name.size(), m, re))
return std::stoi(m[1]);
return 65536;
}
template <typename E>
static i64 get_ctor_dtor_priority(InputSection<E> *isec) {
auto opts = std::regex_constants::optimize | std::regex_constants::ECMAScript;
static std::regex re1(R"((?:clang_rt\.)?crtbegin)", opts);
static std::regex re2(R"((?:clang_rt\.)?crtend)", opts);
static std::regex re3(R"(\.(\d+)$)", opts);
// crtbegin.o and crtend.o contain marker symbols such as
// __CTOR_LIST__ or __DTOR_LIST__. So they have to be at the
// beginning or end of the section.
std::smatch m;
if (std::regex_search(isec->file.filename, m, re1))
return -2;
if (std::regex_search(isec->file.filename, m, re2))
return 65536;
std::string name(isec->name());
if (std::regex_search(name, m, re3))
return std::stoi(m[1]);
return -1;
}
template <typename E>
void sort_init_fini(Context<E> &ctx) {
Timer t(ctx, "sort_init_fini");
auto get_priority = [](InputSection<E> *isec) {
static std::regex re(R"(\.(\d+)$)", std::regex_constants::optimize);
std::string_view name = isec->name();
std::cmatch m;
if (std::regex_search(name.data(), name.data() + name.size(), m, re))
return std::stoi(m[1]);
return 65536;
};
for (Chunk<E> *chunk : ctx.chunks) {
if (OutputSection<E> *osec = chunk->to_osec()) {
if (osec->name == ".init_array" || osec->name == ".preinit_array" ||
@@ -1068,8 +1088,14 @@ void sort_init_fini(Context<E> &ctx) {
std::reverse(osec->members.begin(), osec->members.end());
std::unordered_map<InputSection<E> *, i64> map;
for (InputSection<E> *isec : osec->members)
map.insert({isec, get_priority(isec)});
for (InputSection<E> *isec : osec->members) {
std::string_view name = isec->name();
if (name.starts_with(".ctors") || name.starts_with(".dtors"))
map.insert({isec, 65535 - get_ctor_dtor_priority(isec)});
else
map.insert({isec, get_init_fini_priority(isec)});
}
sort(osec->members, [&](InputSection<E> *a, InputSection<E> *b) {
return map[a] < map[b];
@@ -1083,27 +1109,6 @@ template <typename E>
void sort_ctor_dtor(Context<E> &ctx) {
Timer t(ctx, "sort_ctor_dtor");
auto get_priority = [](InputSection<E> *isec) {
auto opts = std::regex_constants::optimize | std::regex_constants::ECMAScript;
static std::regex re1(R"((?:clang_rt\.)?crtbegin)", opts);
static std::regex re2(R"((?:clang_rt\.)?crtend)", opts);
static std::regex re3(R"(\.(\d+)$)", opts);
// crtbegin.o and crtend.o contain marker symbols such as
// __CTOR_LIST__ or __DTOR_LIST__. So they have to be at the
// beginning or end of the section.
std::smatch m;
if (std::regex_search(isec->file.filename, m, re1))
return -2;
if (std::regex_search(isec->file.filename, m, re2))
return 65536;
std::string name(isec->name());
if (std::regex_search(name, m, re3))
return std::stoi(m[1]);
return -1;
};
for (Chunk<E> *chunk : ctx.chunks) {
if (OutputSection<E> *osec = chunk->to_osec()) {
if (osec->name == ".ctors" || osec->name == ".dtors") {
@@ -1112,7 +1117,7 @@ void sort_ctor_dtor(Context<E> &ctx) {
std::unordered_map<InputSection<E> *, i64> map;
for (InputSection<E> *isec : osec->members)
map.insert({isec, get_priority(isec)});
map.insert({isec, get_ctor_dtor_priority(isec)});
sort(osec->members, [&](InputSection<E> *a, InputSection<E> *b) {
return map[a] < map[b];
@@ -1122,6 +1127,45 @@ void sort_ctor_dtor(Context<E> &ctx) {
}
}
// .ctors/.dtors serves the same purpose as .init_array/.fini_array,
// albeit with very subtly differences. Both contain pointers to
// initializer/finalizer functions. The runtime executes them one by one
// but in the exact opposite order to one another. Therefore, if we are to
// place the contents of .ctors/.dtors into .init_array/.fini_array, we
// need to reverse them.
//
// It's unfortunate that we have both .ctors/.dtors and
// .init_array/.fini_array in ELF for historical reasons, but that's
// the reality we need to deal with.
template <typename E>
void fixup_ctors_in_init_array(Context<E> &ctx) {
Timer t(ctx, "fixup_ctors_in_init_array");
for (Chunk<E> *chunk : ctx.chunks) {
if (OutputSection<E> *osec = chunk->to_osec()) {
if (osec->name == ".init_array" || osec->name == ".fini_array") {
for (InputSection<E> *isec : osec->members) {
if (isec->name().starts_with(".ctors") ||
isec->name().starts_with(".dtors")) {
if (isec->sh_size % sizeof(Word<E>)) {
Error(ctx) << *isec << ": section corrupted";
continue;
}
u8 *buf = (u8 *)isec->contents.data();
std::reverse((Word<E> *)buf, (Word<E> *)(buf + isec->sh_size));
std::span<ElfRel<E>> rels = isec->get_rels(ctx);
for (ElfRel<E> &r : rels)
r.r_offset = isec->sh_size - r.r_offset - sizeof(Word<E>);
std::reverse(rels.begin(), rels.end());
}
}
}
}
}
}
template <typename T>
static void shuffle(std::vector<T> &vec, u64 seed) {
if (vec.empty())
@@ -1554,9 +1598,6 @@ template <typename E>
void apply_version_script(Context<E> &ctx) {
Timer t(ctx, "apply_version_script");
// If all patterns are simple (i.e. not containing any meta-
// characters and is not a C++ name), we can simply look up
// symbols.
auto is_simple = [&] {
for (VersionPattern &v : ctx.version_patterns)
if (v.is_cpp || v.pattern.find_first_of("*?[") != v.pattern.npos)
@@ -1564,6 +1605,9 @@ void apply_version_script(Context<E> &ctx) {
return true;
};
// If all patterns are simple (i.e. not containing any meta-
// characters and is not a C++ name), we can simply look up
// symbols.
if (is_simple()) {
for (VersionPattern &v : ctx.version_patterns) {
Symbol<E> *sym = get_symbol(ctx, v.pattern);
@@ -1689,44 +1733,124 @@ void compute_import_export(Context<E> &ctx) {
if (!ctx.arg.shared) {
tbb::parallel_for_each(ctx.dsos, [&](SharedFile<E> *file) {
for (Symbol<E> *sym : file->symbols) {
if (sym->file && !sym->file->is_dso && sym->visibility != STV_HIDDEN) {
if (sym->ver_idx != VER_NDX_LOCAL ||
!ctx.default_version_from_version_script) {
std::scoped_lock lock(sym->mu);
sym->is_exported = true;
}
if (sym->file && !sym->file->is_dso && sym->visibility != STV_HIDDEN &&
sym->ver_idx != VER_NDX_LOCAL) {
std::scoped_lock lock(sym->mu);
sym->is_exported = true;
}
}
});
}
auto should_export = [&](Symbol<E> &sym) {
if (sym.visibility == STV_HIDDEN)
return false;
switch (sym.ver_idx) {
case VER_NDX_UNSPECIFIED:
if (ctx.arg.shared)
return !((ObjectFile<E> *)sym.file)->exclude_libs;
return ctx.arg.export_dynamic;
case VER_NDX_LOCAL:
return false;
default:
return true;
}
};
// Export symbols that are not hidden or marked as local.
// We also want to mark imported symbols as such.
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (Symbol<E> *sym : file->get_global_syms()) {
if (!sym->file || sym->visibility == STV_HIDDEN ||
sym->ver_idx == VER_NDX_LOCAL)
continue;
// If we are using a symbol in a DSO, we need to import it at runtime.
if (sym->file != file && sym->file->is_dso && !sym->is_absolute()) {
std::scoped_lock lock(sym->mu);
sym->is_imported = true;
// If we are using a symbol in a DSO, we need to import it.
if (sym->file && sym->file->is_dso) {
if (!sym->is_absolute()) {
std::scoped_lock lock(sym->mu);
sym->is_imported = true;
}
continue;
}
// If we are creating a DSO, all global symbols are exported by default.
if (sym->file == file) {
std::scoped_lock lock(sym->mu);
// If we have a definition of a symbol, we may want to export it.
if (sym->file == file && should_export(*sym)) {
sym->is_exported = true;
if (ctx.arg.shared && sym->visibility != STV_PROTECTED &&
// Exported symbols are marked as imported as well by default
// for DSOs.
if (ctx.arg.shared &&
sym->visibility != STV_PROTECTED &&
!ctx.arg.Bsymbolic &&
!(ctx.arg.Bsymbolic_functions && sym->get_type() == STT_FUNC))
sym->is_imported = true;
}
}
});
// Apply --dynamic-list, --export-dynamic-symbol and
// --export-dynamic-symbol-list options.
//
// The semantics of these options vary depending on whether we are
// creating an executalbe or a shared object.
//
// For executable, matched symbols are exported.
//
// For shared objects, matched symbols are imported if it is already
// exported so that they are interposable. In other words, symbols
// that did not match will be bound locally within the output file,
// effectively turning them into protected symbols.
MultiGlob matcher;
MultiGlob cpp_matcher;
auto handle_match = [&](Symbol<E> *sym) {
if (ctx.arg.shared) {
if (sym->is_exported)
sym->is_imported = true;
} else {
if (sym->file && !sym->file->is_dso && sym->visibility != STV_HIDDEN)
sym->is_exported = true;
}
};
for (DynamicPattern &p : ctx.dynamic_list_patterns) {
if (p.is_cpp) {
if (!cpp_matcher.add(p.pattern, 1))
Fatal(ctx) << p.source << ": invalid dynamic list entry: "
<< p.pattern;
continue;
}
if (p.pattern.find_first_of("*?[") != p.pattern.npos) {
if (!matcher.add(p.pattern, 1))
Fatal(ctx) << p.source << ": invalid dynamic list entry: "
<< p.pattern;
continue;
}
handle_match(get_symbol(ctx, p.pattern));
}
if (!matcher.empty() || !cpp_matcher.empty()) {
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (Symbol<E> *sym : file->get_global_syms()) {
if (sym->file != file)
continue;
if (ctx.arg.shared && !sym->is_exported)
continue;
std::string_view name = sym->name();
if (matcher.find(name)) {
handle_match(sym);
} else if (!cpp_matcher.empty()) {
if (std::optional<std::string_view> s = cpp_demangle(name))
name = *s;
if (cpp_matcher.find(name))
handle_match(sym);
}
}
});
}
}
// Compute the "address-taken" bit for each input section.
@@ -2592,18 +2716,12 @@ void fix_synthetic_symbols(Context<E> &ctx) {
if (u64 *addr = std::get_if<u64>(&val)) {
sym->origin = 0;
sym->value = *addr;
continue;
} else {
Symbol<E> *sym2 = std::get<Symbol<E> *>(val);
sym->value = sym2->value;
sym->origin = sym2->origin;
sym->visibility = sym2->visibility.load();
}
Symbol<E> *sym2 = std::get<Symbol<E> *>(val);
if (!sym2->file) {
Error(ctx) << "--defsym: undefined symbol: " << *sym2;
continue;
}
sym->value = sym2->value;
sym->origin = sym2->origin;
sym->visibility = sym2->visibility.load();
}
@@ -2756,6 +2874,7 @@ template void check_duplicate_symbols(Context<E> &);
template void check_symbol_types(Context<E> &);
template void sort_init_fini(Context<E> &);
template void sort_ctor_dtor(Context<E> &);
template void fixup_ctors_in_init_array(Context<E> &);
template void shuffle_sections(Context<E> &);
template void compute_section_sizes(Context<E> &);
template void sort_output_sections(Context<E> &);
+130 -40
View File
@@ -1,7 +1,10 @@
option(MOLD_ENABLE_QEMU_TESTS "Enable tests on non-native targets" OFF)
option(MOLD_ENABLE_QEMU_TESTS "Enable tests on non-native targets" ON)
set(HOST ${CMAKE_HOST_SYSTEM_PROCESSOR})
if(EXISTS "/proc/cpuinfo")
file(READ "/proc/cpuinfo" CPUINFO)
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.
@@ -25,67 +28,154 @@ elseif(${HOST} STREQUAL "powerpc64le")
set(HOST ppc64le)
endif()
if(MOLD_ENABLE_QEMU_TESTS)
list(APPEND QEMU_ARCHS
x86_64 i386 arm aarch64 ppc ppc64 ppc64le sparc64 sh4 s390x
alpha riscv64 riscv32 m68k loongarch64)
LIST(APPEND TRIPLES
x86_64-linux-gnu
i686-linux-gnu
aarch64-linux-gnu
arm-linux-gnueabihf
riscv64-linux-gnu
powerpc-linux-gnu
powerpc64-linux-gnu
powerpc64le-linux-gnu
sparc64-linux-gnu
s390x-linux-gnu
sh4-linux-gnu
alpha-linux-gnu
riscv32-linux-gnu
m68k-linux-gnu
loongarch64-linux-gnu)
foreach(ARCH IN LISTS QEMU_ARCHS)
find_program(HAS_qemu-${ARCH} qemu-${ARCH})
endforeach()
foreach(TRIPLE IN LISTS TRIPLES)
find_program(HAS_${TRIPLE}-gcc ${TRIPLE}-gcc)
endforeach()
endif()
function(add_target ARCH TRIPLE)
set(CPU ${ARGV2})
if(${ARCH} STREQUAL ${HOST})
set(IS_NATIVE 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 "_")
list(FILTER ALL_TESTS EXCLUDE REGEX "_")
file(GLOB TESTS RELATIVE ${CMAKE_CURRENT_LIST_DIR} CONFIGURE_DEPENDS "${ARCH}_*.sh")
file(GLOB TESTS RELATIVE ${CMAKE_CURRENT_LIST_DIR} CONFIGURE_DEPENDS
"${ARCH}_*.sh")
list(APPEND TESTS ${ALL_TESTS})
list(APPEND TESTS ${ALL_TESTS})
foreach(TEST IN LISTS TESTS)
foreach(TEST IN LISTS TESTS)
if(CPU)
string(REGEX REPLACE "\\.sh$" "" TESTNAME "${ARCH}_${CPU}-${TEST}")
else()
string(REGEX REPLACE "\\.sh$" "" TESTNAME "${ARCH}-${TEST}")
endif()
add_test(NAME ${TESTNAME}
COMMAND bash -x ${CMAKE_CURRENT_LIST_DIR}/${TEST}
WORKING_DIRECTORY ${mold_BINARY_DIR})
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}")
set_property(TEST ${TESTNAME} APPEND PROPERTY ENVIRONMENT
"HOST=${HOST};CPU=${CPU}")
if(IS_NATIVE)
set_property(TEST ${TESTNAME} APPEND PROPERTY SKIP_REGULAR_EXPRESSION "skipped")
else()
set_property(TEST ${TESTNAME} APPEND PROPERTY ENVIRONMENT "TRIPLE=${TRIPLE}")
endif()
endforeach()
endif()
if(IS_NATIVE)
set_property(TEST ${TESTNAME} APPEND PROPERTY SKIP_REGULAR_EXPRESSION
"skipped")
else()
set_property(TEST ${TESTNAME} APPEND PROPERTY ENVIRONMENT
"TRIPLE=${TRIPLE}")
endif()
endforeach()
endfunction()
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)
if(${HOST} STREQUAL "x86_64" OR (HAS_qemu-x86_64 AND HAS_x86_64-linux-gnu-gcc))
add_target(x86_64 x86_64-linux-gnu)
endif()
option(MOLD_ENABLE_QEMU_TESTS_RV32 "Enable tests for RV32" OFF)
if(MOLD_ENABLE_QEMU_TESTS_RV32)
if(${HOST} STREQUAL "i686" OR (HAS_qemu-i386 AND HAS_i686-linux-gnu-gcc))
add_target(i686 i686-linux-gnu)
endif()
if(${HOST} STREQUAL "aarch64" OR (HAS_qemu-aarch64 AND HAS_aarch64-linux-gnu-gcc))
add_target(aarch64 aarch64-linux-gnu)
endif()
if(${HOST} STREQUAL "arm" OR (HAS_qemu-arm AND HAS_arm-linux-gnueabihf-gcc))
add_target(arm arm-linux-gnueabihf)
endif()
if(${HOST} STREQUAL "riscv64" OR (HAS_qemu-riscv64 AND HAS_riscv64-linux-gnu-gcc))
add_target(riscv64 riscv64-linux-gnu)
endif()
if(${HOST} STREQUAL "riscv32" OR (HAS_qemu-riscv32 AND HAS_riscv32-linux-gnu-gcc))
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(ppc64le powerpc64le_power10-linux-gnu)
if(${HOST} STREQUAL "ppc" OR (HAS_qemu-ppc AND HAS_powerpc-linux-gnu-gcc))
add_target(ppc powerpc-linux-gnu)
endif()
option(MOLD_ENABLE_QEMU_TESTS_M68K "Enable tests for m68k" OFF)
if(MOLD_ENABLE_QEMU_TESTS_M68K)
if(${HOST} STREQUAL "ppc64" OR (HAS_qemu-ppc64 AND HAS_powerpc64-linux-gnu-gcc))
add_target(ppc64 powerpc64-linux-gnu)
endif()
if(${HOST} STREQUAL "ppc64le" OR (HAS_qemu-ppc64le AND HAS_powerpc64le-linux-gnu-gcc))
add_target(ppc64le powerpc64le-linux-gnu)
endif()
if(${HOST} STREQUAL "ppc64le" AND "${CPUINFO}" MATCHES "POWER10")
add_target(ppc64le powerpc64le-linux-gnu power10)
elseif(HAS_qemu-ppc64le AND HAS_powerpc64le-linux-gnu-gcc)
file(WRITE "${CMAKE_BINARY_DIR}/empty.c" "")
execute_process(
COMMAND powerpc64le-linux-gnu-gcc -mcpu=power10 -E
"${CMAKE_BINARY_DIR}/empty.c"
RESULT_VARIABLE GCC_EXIT_CODE
OUTPUT_QUIET ERROR_QUIET)
execute_process(
COMMAND qemu-ppc64le -cpu help
OUTPUT_VARIABLE QEMU_CPUS)
if("${GCC_EXIT_CODE}" EQUAL "0" AND "${QEMU_CPUS}" MATCHES power10_v2.0)
add_target(ppc64le powerpc64le-linux-gnu power10)
endif()
endif()
if(${HOST} STREQUAL "sparc64" OR (HAS_qemu-sparc64 AND HAS_sparc64-linux-gnu-gcc))
add_target(sparc64 sparc64-linux-gnu)
endif()
if(${HOST} STREQUAL "s390x" OR (HAS_qemu-s390x AND HAS_s390x-linux-gnu-gcc))
add_target(s390x s390x-linux-gnu)
endif()
if(${HOST} STREQUAL "sh4" OR (HAS_qemu-sh4 AND HAS_sh4-linux-gnu-gcc))
add_target(sh4 sh4-linux-gnu)
endif()
if(${HOST} STREQUAL "alpha" OR (HAS_qemu-alpha AND HAS_alpha-linux-gnu-gcc))
add_target(alpha alpha-linux-gnu)
endif()
if(${HOST} STREQUAL "m68k" OR (HAS_qemu-m68k AND HAS_m68k-linux-gnu-gcc))
add_target(m68k m68k-linux-gnu)
endif()
option(MOLD_ENABLE_QEMU_TESTS_LOONGARCH "Enable tests for LoongArch" OFF)
if(MOLD_ENABLE_QEMU_TESTS_LOONGARCH OR ${HOST} STREQUAL loongarch64)
if(${HOST} STREQUAL "loongarch64" OR (HAS_qemu-loongarch64 AND HAS_loongarch64-linux-gnu-gcc))
add_target(loongarch64 loongarch64-linux-gnu)
endif()
+1 -2
View File
@@ -7,8 +7,7 @@ $CC -B. -o $t/exe $t/a.c -Wl,-build-id
readelf -n $t/exe | grep -qv 'GNU.*0x00000010.*NT_GNU_BUILD_ID'
$CC -B. -o $t/exe $t/a.c -Wl,-build-id=uuid
readelf -nW $t/exe |
grep -Eq 'GNU.*0x00000010.*NT_GNU_BUILD_ID.*Build ID: ............4...[89abcdef]'
readelf -nW $t/exe | grep -Eq 'Build ID: ............4...[89abcdef]'
$CC -B. -o $t/exe $t/a.c -Wl,-build-id=md5
readelf -n $t/exe | grep -q 'GNU.*0x00000010.*NT_GNU_BUILD_ID'
+9 -12
View File
@@ -5,7 +5,7 @@ export LC_ALL=C
canonical_name() {
case $1 in
i?86) echo i386 ;;
i?86) echo i686 ;;
arm*) echo arm ;;
powerpc) echo ppc ;;
powerpc64) echo ppc64 ;;
@@ -30,8 +30,7 @@ if [ -z "$TRIPLE" ]; then
OBJCOPY=objcopy
STRIP=strip
QEMU=
elif [ "$TRIPLE" = powerpc64le_power10-linux-gnu ]; then
TRIPLE=powerpc64le-linux-gnu
elif [ "$TRIPLE" = powerpc64le-linux-gnu -a "$CPU" = power10 ]; then
MACHINE=ppc64le
TESTDIR=out/test/elf/ppc64le-power10
CC="${TEST_CC:-$TRIPLE-gcc} -mcpu=power10"
@@ -52,22 +51,20 @@ else
OBJDUMP="$TRIPLE-objdump"
OBJCOPY="$TRIPLE-objcopy"
STRIP="$TRIPLE-strip"
QEMU="qemu-$MACHINE -L /usr/$TRIPLE"
if [ $MACHINE = i686 ]; then
QEMU="qemu-i386 -L /usr/$TRIPLE"
else
QEMU="qemu-$MACHINE -L /usr/$TRIPLE"
fi
fi
if [ $MACHINE = x86_64 -o $MACHINE = i386 -o $MACHINE = arm ]; then
if [ $MACHINE = x86_64 -o $MACHINE = i686 -o $MACHINE = arm ]; then
tlsdesc_opt=-mtls-dialect=gnu2
elif [ $MACHINE = aarch64 ]; then
tlsdesc_opt=-mtls-dialect=desc
fi
if [ $MACHINE = ppc64le -a -e /proc/cpuinfo ] && grep -q POWER10 /proc/cpuinfo; then
CC="$CC -mcpu=power10"
CXX="$CXX -mcpu=power10"
GCC="$GCC -mcpu=power10"
GXX="$GXX -mcpu=power10"
fi
# Common functions
test_cflags() {
echo 'int main() {}' | $CC "$@" -o /dev/null -xc - >& /dev/null
+1 -2
View File
@@ -1,7 +1,6 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc -fno-PIE -
#include <setjmp.h>
#include <signal.h>
@@ -32,7 +31,7 @@ int main() {
}
EOF
cat <<EOF | $CC -fPIC -shared -o $t/b.so -xc -
cat <<EOF | $CC -B. -fPIC -shared -o $t/b.so -xc -
__attribute__((section (".data.rel.ro"))) char readonly[100] = "abc";
char readwrite[100] = "abc";
EOF
+55
View File
@@ -0,0 +1,55 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc -
#include <stdio.h>
static void ctor1() { printf("ctor1 "); }
static void ctor2() { printf("ctor2 "); }
static void ctor3() { printf("ctor3 "); }
static void ctor4() { printf("ctor4 "); }
static void dtor1() { printf("dtor1 "); }
static void dtor2() { printf("dtor2 "); }
static void dtor3() { printf("dtor3 "); }
static void dtor4() { printf("dtor4 "); }
__attribute__((aligned(sizeof(void *)), section(".ctors.65435")))
void (*ctors65435[])() = { ctor1 };
__attribute__((aligned(sizeof(void *)), section(".ctors.65433")))
void (*ctors65433[])() = { ctor2 };
__attribute__((aligned(sizeof(void *)), section(".ctors")))
void (*ctors[])() = { ctor4, ctor3 };
__attribute__((aligned(sizeof(void *)), section(".dtors")))
void (*dtors[])() = { dtor1, dtor2 };
__attribute__((aligned(sizeof(void *)), section(".dtors.65433")))
void (*dtors65433[])() = { dtor3 };
__attribute__((aligned(sizeof(void *)), section(".dtors.65435")))
void (*dtors65435[])() = { dtor4 };
EOF
cat <<EOF | $CC -o $t/b.o -c -xc -
#include <stdio.h>
__attribute__((constructor(101)))
static void init1() { printf("init1 "); }
__attribute__((constructor))
static void init2() { printf("init2 "); }
__attribute__((destructor(101)))
static void fini1() { printf("fini1 "); }
__attribute__((destructor))
static void fini2() { printf("fini2 "); }
int main() {}
EOF
$CC -B. -o $t/exe $t/a.o $t/b.o
$QEMU $t/exe | grep -q 'ctor1 init1 ctor2 ctor3 ctor4 init2 fini2 dtor1 dtor2 dtor3 fini1 dtor4'
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc -
int main() {}
EOF
! $CC -B. -o $t/exe $t/a.o -Wl,-defsym=foo=bar 2> $t/log
grep -q 'undefined symbol: bar' $t/log
+44
View File
@@ -0,0 +1,44 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc - -fPIC
#include <stdio.h>
void foo() { printf("foo1 "); }
void bar() { printf("bar1 "); }
void baz() { printf("baz1 "); }
void print() {
foo();
bar();
baz();
printf("\n");
}
EOF
cat <<EOF > $t/dyn
{ foo; bar; };
EOF
$CC -B. -shared -o $t/b.so $t/a.o -Wl,--dynamic-list=$t/dyn
cat <<EOF | $CC -o $t/c.o -c -xc - -fPIC
#include <stdio.h>
void foo() { printf("foo2 "); }
void bar() { printf("bar2 "); }
void baz() { printf("baz2 "); }
EOF
$CC -B. -shared -o $t/d.so $t/c.o
cat <<EOF | $CC -o $t/e.o -c -xc -
#include <stdio.h>
void print();
int main() { print(); }
EOF
$CC -B. -o $t/exe1 $t/e.o -Wl,-push-state,-no-as-needed $t/b.so -Wl,-pop-state
$QEMU $t/exe1 | grep -q 'foo1 bar1 baz1'
$CC -B. -o $t/exe2 $t/e.o -Wl,-push-state,-no-as-needed $t/d.so $t/b.so -Wl,-pop-state
$QEMU $t/exe2 | grep -q 'foo2 bar2 baz1'
+1
View File
@@ -1,6 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = m68k ] && skip
[ $MACHINE = sh4 ] && skip
cat <<EOF | $CXX -o $t/a.o -c -fPIC -xc++ -g -ffunction-sections -
+1
View File
@@ -1,6 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = m68k ] && skip
[ $MACHINE = sh4 ] && skip
static=
+1 -1
View File
@@ -20,7 +20,7 @@ GOT_ADDR=$($QEMU $t/exe)
# _GLOBAL_OFFSET_TABLE_ refers the end of .got only on x86.
# We assume .got is followed by .gotplt.
if [ $MACHINE = x86_64 -o $MACHINE = i386 ]; then
if [ $MACHINE = x86_64 -o $MACHINE = i686 ]; then
readelf -WS $t/exe | grep -q "\.got\.plt .*$GOT_ADDR "
else
readelf -WS $t/exe | grep -q "\.got .*$GOT_ADDR "
+1 -1
View File
@@ -18,5 +18,5 @@ int main() {
}
EOF
$CC -B. -o $t/exe $t/a.o $t/b.o
$CC -B. -o $t/exe $t/a.o $t/b.o -no-pie
$QEMU $t/exe | grep -q 'foo=5'
+1 -1
View File
@@ -16,5 +16,5 @@ int main() {
EOF
# Make sure that we do not copy .gnu.warning.* sections.
$CC -B. -o $t/exe $t/a.o
$CC -B. -o $t/exe $t/a.o -no-pie
$QEMU $t/exe | grep -q 'Hello world'
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = i386 ] || skip
[ $MACHINE = i686 ] || skip
cat <<'EOF' | $CC -fPIC -o $t/a.o -c -xassembler -
.globl get_foo
+2 -2
View File
@@ -29,5 +29,5 @@ int main() {
}
EOF
$CC -B. -o $t/exe1 $t/c.o $t/b.so -no-pie
$QEMU $t/exe1 | grep -Eq '^(\S+) \1'
$CC -B. -o $t/exe $t/c.o $t/b.so -no-pie
$QEMU $t/exe | grep -Eq '^(\S+) \1'
+5 -2
View File
@@ -16,5 +16,8 @@ int main() {
}
EOF
$CC -B. -o $t/exe $t/a.o
$QEMU $t/exe | grep -Eq '^(\S+) \1$'
$CC -B. -o $t/exe1 $t/a.o -pie
$QEMU $t/exe1 | grep -Eq '^(\S+) \1$'
$CC -B. -o $t/exe2 $t/a.o -no-pie
$QEMU $t/exe2 | grep -Eq '^(\S+) \1$'
+5 -2
View File
@@ -33,5 +33,8 @@ int main() {
}
EOF
$CC -B. -o $t/exe $t/a.o $t/b.o $t/c.o
$QEMU $t/exe | grep -q '^3$'
$CC -B. -o $t/exe1 $t/a.o $t/b.o $t/c.o -pie
$QEMU $t/exe1 | grep -q '^3$'
$CC -B. -o $t/exe2 $t/a.o $t/b.o $t/c.o -no-pie
$QEMU $t/exe2 | grep -q '^3$'
+5 -2
View File
@@ -24,5 +24,8 @@ int main() {
}
EOF
$CC -B. -o $t/exe $t/a.o
$QEMU $t/exe | grep -q 'Hello world'
$CC -B. -o $t/exe1 $t/a.o -pie
$QEMU $t/exe1 | grep -q 'Hello world'
$CC -B. -o $t/exe2 $t/a.o -no-pie
$QEMU $t/exe2 | grep -q 'Hello world'
+1
View File
@@ -1,6 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = m68k ] && skip
[ $MACHINE = sh4 ] && skip
cat <<EOF | $CXX -o $t/a.o -c -xc++ -
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = i386 ] && skip
[ $MACHINE = i686 ] && skip
cat <<EOF | $CC -o $t/a.o -c -xc - -ffunction-sections -fPIC
#include <stdio.h>
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = i386 ] && skip
[ $MACHINE = i686 ] && skip
cat <<EOF | $CC -o $t/a.o -c -xc - -ffunction-sections
#include <stdio.h>
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = i386 ] && skip
[ $MACHINE = i686 ] && skip
[ $MACHINE = m68k ] && skip
[ $MACHINE = arm ] && skip
[ $MACHINE = ppc64 ] && skip
+5 -1
View File
@@ -2,12 +2,16 @@
. $(dirname $0)/common.inc
# Skip if 32 bits as we use very large addresses in this test.
[ $MACHINE = i386 ] && skip
[ $MACHINE = i686 ] && skip
[ $MACHINE = riscv32 ] && skip
# It looks like SPARC's runtime can't handle PLT if it's too far from GOT.
[ $MACHINE = sparc64 ] && skip
# qemu aborts with the "Unknown exception 0x5" error, although this
# test passes on a real POWER10 machine.
[ -n "$QEMU" -a "$CPU" = power10 ] && skip
cat <<EOF > $t/a.c
#include <stdio.h>
+2 -1
View File
@@ -4,7 +4,8 @@
# OneTBB isn't tsan-clean
nm mold | grep -q '__tsan_init' && skip
# Ubuntu 22.04 SH4 GCC is broken
# Ubuntu 22.04 GCC is broken
[ $MACHINE = m68k ] && skip
[ $MACHINE = sh4 ] && skip
cat <<EOF | $CXX -c -o $t/a.o -xc++ -
+1
View File
@@ -1,6 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = m68k ] && skip
[ $MACHINE = sh4 ] && skip
# OneTBB isn't tsan-clean
+50
View File
@@ -0,0 +1,50 @@
#!/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.foo,"aMS",@progbits,4
.p2align 2
val2:
.ascii "world \0\0\0\0"
.section .rodata.x,"aMS",@progbits,1
val3:
.ascii "foobar\0"
.section .rodata.cst8,"aM",@progbits,8
.p2align 3
val4:
.ascii "abcdefgh"
.section .rodatabaz,"aMS",@progbits,1
val5:
.ascii "baz\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.str1.1 $t/exe | grep -q Hello
readelf -p .rodata.str4.4 $t/exe | grep -q world
readelf -p .rodata.str1.1 $t/exe | grep -q foobar
readelf -p .rodata.cst8 $t/exe | grep -q abcdefgh
readelf -p .rodatabaz $t/exe | grep -q baz
+1 -1
View File
@@ -19,7 +19,7 @@ EOF
$CC -B. -shared -o $t/b.so $t/a.o
cat <<EOF | $GCC -c -o $t/c.o -xc -
cat <<EOF | $GCC -c -o $t/c.o -xc - -fPIC
#include <stdio.h>
_Thread_local int baz;
+1 -1
View File
@@ -23,5 +23,5 @@ cat <<EOF | $CC -xc -c -o $t/b.o -
__attribute__((tls_model("global-dynamic"))) _Thread_local int foo;
EOF
$CC -B. -o $t/exe $t/a.o $t/b.o
$CC -B. -o $t/exe $t/a.o $t/b.o -no-pie
$QEMU $t/exe | grep -q '3 5 3 5'
+2 -2
View File
@@ -9,10 +9,10 @@ EOF
cat <<EOF | $CXX -fPIC -c -o $t/b.o -xc -
int foo = 5;
int bar = 6;
int quux = 100;
EOF
$CC -B. -shared -Wl,--version-script=$t/a.ver \
-o $t/c.so $t/b.o
$CC -B. -shared -Wl,--version-script=$t/a.ver -o $t/c.so $t/b.o
cat <<'EOF' > $t/d.ver
VER_Y1 { local; *; };
+1
View File
@@ -22,5 +22,6 @@ int main() {}
EOF
$CC -B. -o $t/exe $t/a.o $t/b.o $t/c.o
$OBJDUMP -d $t/exe >& /dev/null || skip
$OBJDUMP -d $t/exe | grep -A1 '<__x86.get_pc_thunk.bx>:' | \
grep -Fq 'puts$plt'
+2
View File
@@ -4,6 +4,8 @@
# Skip if target is not x86-64
[ $MACHINE = x86_64 ] || skip
echo endbr64 | $CC -o /dev/null -c -xassembler - 2> /dev/null || skip
$CC -fcf-protection=branch -c /dev/null -o /dev/null -xc 2> /dev/null || skip
cat <<EOF | $CC -fcf-protection=branch -c -o $t/a.o -xc -
+1
View File
@@ -3,6 +3,7 @@
# Skip if target is not x86-64
[ $MACHINE = x86_64 ] || skip
echo ret | cc -c -o /dev/null -xassembler -Wa,-mrelax-relocations=yes - 2> /dev/null || skip
cat <<EOF | $CC -o $t/a.o -c -x assembler -Wa,-mrelax-relocations=yes -
.globl bar
+1
View File
@@ -2,6 +2,7 @@
. $(dirname $0)/common.inc
[ $MACHINE = x86_64 ] || skip
echo endbr64 | $CC -o /dev/null -c -xassembler - 2> /dev/null || skip
cat <<EOF | $CC -o $t/a.o -c -x assembler -
.globl main
+1
View File
@@ -2,6 +2,7 @@
. $(dirname $0)/common.inc
[ $MACHINE = x86_64 ] || skip
echo endbr64 | $CC -o /dev/null -c -xassembler - 2> /dev/null || skip
cat <<EOF | $CC -o $t/a.o -c -x assembler -
.globl main