mirror of
https://github.com/rui314/mold.git
synced 2026-11-09 04:49:33 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49066ea329 | ||
|
|
25ceab07f7 | ||
|
|
6e97393740 | ||
|
|
ff3d54d26f | ||
|
|
25c9cf3976 | ||
|
|
5f2fb08cfd | ||
|
|
60bc56848f | ||
|
|
600ca381fd | ||
|
|
da3f5dd4ec | ||
|
|
b59911b0c1 | ||
|
|
8131528305 | ||
|
|
6a71e7dd4b | ||
|
|
a63b2f70b2 | ||
|
|
3f88964527 | ||
|
|
e18dc70af0 | ||
|
|
0c6daad215 | ||
|
|
8c35c80bb7 | ||
|
|
d42ba3bae4 | ||
|
|
8c337b25df | ||
|
|
8d1c08bd03 | ||
|
|
5599a6f24f | ||
|
|
ef9a610dd1 | ||
|
|
31c3b5397b | ||
|
|
e11d64b5c7 | ||
|
|
9d91a2bc23 | ||
|
|
8c41f21d3c | ||
|
|
5c7a75b3e4 | ||
|
|
0804fbd095 | ||
|
|
0f78fa9c43 | ||
|
|
d30e970eb8 |
No files matched your search
@@ -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
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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()
|
||||
@@ -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
@@ -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,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
|
||||
|
||||
Executable
+55
@@ -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'
|
||||
Executable
+9
@@ -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
|
||||
Executable
+44
@@ -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,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,6 +1,7 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
[ $MACHINE = m68k ] && skip
|
||||
[ $MACHINE = sh4 ] && skip
|
||||
|
||||
static=
|
||||
|
||||
@@ -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 "
|
||||
|
||||
@@ -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'
|
||||
@@ -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,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
|
||||
|
||||
@@ -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'
|
||||
@@ -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$'
|
||||
@@ -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$'
|
||||
@@ -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,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,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,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,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
[ $MACHINE = i386 ] && skip
|
||||
[ $MACHINE = i686 ] && skip
|
||||
[ $MACHINE = m68k ] && skip
|
||||
[ $MACHINE = arm ] && skip
|
||||
[ $MACHINE = ppc64 ] && skip
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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,6 +1,7 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
[ $MACHINE = m68k ] && skip
|
||||
[ $MACHINE = sh4 ] && skip
|
||||
|
||||
# OneTBB isn't tsan-clean
|
||||
|
||||
Executable
+50
@@ -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
@@ -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;
|
||||
|
||||
@@ -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'
|
||||
@@ -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; *; };
|
||||
|
||||
@@ -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'
|
||||
@@ -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 -
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user