mirror of
https://github.com/rui314/mold.git
synced 2026-11-13 06:21:25 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d9b4d5c05 | ||
|
|
d06bfe0ac3 | ||
|
|
6540294a81 | ||
|
|
02a7640047 | ||
|
|
f4c5a8a42e | ||
|
|
0fdbace9b2 | ||
|
|
cab6014154 | ||
|
|
000ce0ee52 | ||
|
|
d720a2011c | ||
|
|
c91055271b | ||
|
|
1478acd551 | ||
|
|
b3404c07d7 | ||
|
|
94767dc0e7 | ||
|
|
f957acfe11 | ||
|
|
7fc43ccc3a | ||
|
|
dc5b9508d9 | ||
|
|
ef056d8ed1 | ||
|
|
6a63671ef5 | ||
|
|
d3867766af | ||
|
|
1ae4b9571b | ||
|
|
3fd1cec20e | ||
|
|
28b16eea19 | ||
|
|
37f32a9008 | ||
|
|
8415252322 | ||
|
|
b3984c9580 | ||
|
|
6616995804 | ||
|
|
de7d37e1fc | ||
|
|
3cb8a528a5 | ||
|
|
8bb01e1a0e | ||
|
|
0416d7ed80 | ||
|
|
c02c04f08a | ||
|
|
efb952ef12 | ||
|
|
04b145d795 | ||
|
|
1830fa1d84 | ||
|
|
a2b300b863 | ||
|
|
2d8297a562 | ||
|
|
deb7728b46 | ||
|
|
bbd6d86af4 | ||
|
|
b6361b8cad | ||
|
|
04fdae124f | ||
|
|
29df7093a8 | ||
|
|
18212a21ab | ||
|
|
e13d28bfe6 | ||
|
|
667204ac7c | ||
|
|
76ea83e791 | ||
|
|
55daf7385d | ||
|
|
0e66e4b7e9 | ||
|
|
fe485db9b4 | ||
|
|
b3dc4879fd | ||
|
|
25ce3738b9 | ||
|
|
22b8ac8470 | ||
|
|
6a4e128ef8 | ||
|
|
66d68c8a98 | ||
|
|
76867af090 | ||
|
|
5d02248a0b | ||
|
|
eb6c213f2a | ||
|
|
efdac9afdd | ||
|
|
c6bfff2a04 |
No files matched your search
@@ -0,0 +1,45 @@
|
||||
name: Build all tarballs
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
release:
|
||||
types: [ published ]
|
||||
|
||||
jobs:
|
||||
build-tarballs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
target: [ x86_64, aarch64, arm, riscv64, ppc64le, s390x ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build a tarball
|
||||
run: ./dist.sh ${{ matrix.target }}
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: tarballs
|
||||
path: mold-*.tar.gz
|
||||
@@ -0,0 +1,36 @@
|
||||
name: Build x86 tarball
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
build-tarball:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build a tarball
|
||||
run: ./dist.sh
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: tarball
|
||||
path: mold-*.tar.gz
|
||||
+17
-20
@@ -18,12 +18,12 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: rui314/setup-mold@staging
|
||||
- uses: rui314/setup-mold@v1
|
||||
- name: install-build-deps
|
||||
run: sudo ./install-build-deps.sh update
|
||||
run: sudo ./install-build-deps.sh
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
- name: build and test
|
||||
- name: build
|
||||
run: |
|
||||
echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
|
||||
sudo apt-get install -y clang++-12
|
||||
@@ -31,7 +31,10 @@ jobs:
|
||||
cd build
|
||||
cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++-12 ${{ matrix.target }} ..
|
||||
cmake --build . -j$(nproc)
|
||||
ctest . -j$(nproc)
|
||||
- name: test
|
||||
run: |
|
||||
cd build
|
||||
ctest -j$(nproc)
|
||||
- name: archive test results
|
||||
uses: actions/upload-artifact@v3
|
||||
if: failure()
|
||||
@@ -47,16 +50,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: install-build-deps
|
||||
shell: bash
|
||||
run: |
|
||||
# Install cross toolchains
|
||||
dpkg --add-architecture i386
|
||||
./install-build-deps.sh update
|
||||
apt-get install -y sudo qemu-user gdb zstd dwarfdump xz-utils {gcc,g++}-10-{i686,aarch64,riscv64,powerpc,powerpc64,powerpc64le,s390x,sparc64,m68k,sh4,alpha}-linux-gnu {gcc,g++}-10-arm-linux-gnueabihf
|
||||
|
||||
for i in {i686,aarch64,riscv64,powerpc,powerpc64,powerpc64le,s390x,sparc64,m68k,sh4,alpha}-linux-gnu arm-linux-gnueabihf; do
|
||||
ln -sf /usr/bin/$i-gcc-10 /usr/bin/$i-gcc
|
||||
ln -sf /usr/bin/$i-g++-10 /usr/bin/$i-g++
|
||||
done
|
||||
./install-build-deps.sh
|
||||
./install-cross-tools.sh
|
||||
|
||||
# Install a RV32 toolchain from third party since it's not available
|
||||
# as an Ubuntu package.
|
||||
@@ -89,26 +87,25 @@ jobs:
|
||||
|
||||
wget -O/usr/local/bin/qemu-loongarch64 -q https://github.com/loongson/build-tools/releases/download/2023.08.08/qemu-loongarch64
|
||||
chmod 755 /usr/local/bin/qemu-loongarch64
|
||||
|
||||
shell: bash
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
- name: build and test
|
||||
- name: build
|
||||
run: |
|
||||
echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
cmake --build . -j$(nproc)
|
||||
ctest . -j$(nproc)
|
||||
- name: test
|
||||
run: |
|
||||
cd build
|
||||
ctest -j$(nproc)
|
||||
- name: archive test results
|
||||
uses: actions/upload-artifact@v3
|
||||
if: failure()
|
||||
with:
|
||||
name: test-results-gcc
|
||||
path: |
|
||||
build/out
|
||||
build/Testing
|
||||
path: b/out b/Testing
|
||||
|
||||
build-macos:
|
||||
runs-on: macos-11
|
||||
@@ -122,7 +119,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
- name: build and test
|
||||
- name: build
|
||||
run: |
|
||||
echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
|
||||
mkdir build
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
name: linux-packages
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: build
|
||||
run: ./dist.sh x86_64
|
||||
- name: upload
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: mold-*-linux.tar.gz
|
||||
@@ -0,0 +1,32 @@
|
||||
# This file is generated by ChatGPT
|
||||
|
||||
name: Update manpage
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'docs/mold.md'
|
||||
|
||||
jobs:
|
||||
update-manpage:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install ronn
|
||||
run: sudo apt-get install -y ronn
|
||||
|
||||
- name: Generate mold.1 from mold.md
|
||||
run: ronn --roff docs/mold.md
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config --global user.name 'Rui Ueyama'
|
||||
git config --global user.email '[email protected]'
|
||||
|
||||
- name: Commit and push if mold.1 is updated
|
||||
run: |
|
||||
git add docs/mold.1
|
||||
git diff --staged --quiet || (git commit -m "Update mold.1 (automated commit)" && git push)
|
||||
+1
-1
@@ -44,7 +44,7 @@
|
||||
# features and behave exactly the same.
|
||||
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(mold VERSION 2.3.3)
|
||||
project(mold VERSION 2.4.0)
|
||||
|
||||
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.3
|
||||
git checkout v2.4.0
|
||||
../install-build-deps.sh
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=c++ ..
|
||||
cmake --build . -j $(nproc)
|
||||
@@ -255,13 +255,14 @@ following people and organizations who have sponsored $128/month or more:
|
||||
|
||||
<a href="https://www.emergetools.com"><img src="docs/emerge-tools-logo.png" align=center height=120 width=240 alt="Emerge Tools"></a><br>
|
||||
|
||||
- [Uber](https://uber.com)
|
||||
- [G-Research](https://www.gresearch.co.uk)
|
||||
- [Signal Slot Inc.](https://github.com/signal-slot)
|
||||
- [GlareDB](https://github.com/GlareDB)
|
||||
|
||||
### Individual sponsors
|
||||
|
||||
- [300baud](https://github.com/300baud)
|
||||
- [Johan Andersson](https://github.com/repi)
|
||||
- [Wei Wu](https://github.com/lazyparser)
|
||||
- [kyle-elliott](https://github.com/kyle-elliott)
|
||||
- [Bryant Biggs](https://github.com/bryantbiggs)
|
||||
- [kraptor23](https://github.com/kraptor23)
|
||||
- [Jinkyu Yi](https://github.com/jincreator)
|
||||
+9
-12
@@ -565,7 +565,7 @@ public:
|
||||
}
|
||||
|
||||
~ConcurrentMap() {
|
||||
free(entries);
|
||||
free(entries_buf);
|
||||
}
|
||||
|
||||
// In order to avoid unnecessary cache-line false sharing, we want
|
||||
@@ -579,19 +579,15 @@ public:
|
||||
|
||||
void resize(i64 nbuckets) {
|
||||
this->nbuckets = std::max<i64>(MIN_NBUCKETS, bit_ceil(nbuckets));
|
||||
free(entries_buf);
|
||||
|
||||
i64 sz = sizeof(Entry) * this->nbuckets;
|
||||
free(entries);
|
||||
|
||||
#if _WIN32
|
||||
// Even though std::aligned_alloc is defined in C++17, MSVC doesn't
|
||||
// seem to provide that function.
|
||||
entries = (Entry *)_aligned_malloc(sz, alignof(Entry));
|
||||
#else
|
||||
entries = (Entry *)std::aligned_alloc(alignof(Entry), sz);
|
||||
#endif
|
||||
|
||||
memset(entries, 0, sz);
|
||||
// seem to provide that function. C11's aligned_alloc may not be always
|
||||
// avialalbe. Therefore, we'll align the buffer ourselves.
|
||||
i64 size = sizeof(Entry) * this->nbuckets;
|
||||
entries_buf = malloc(size + alignof(Entry) - 1);
|
||||
entries = (Entry *)align_to((u64)entries_buf, alignof(Entry));
|
||||
memset(entries, 0, size);
|
||||
}
|
||||
|
||||
std::pair<T *, bool> insert(std::string_view key, u64 hash, const T &val) {
|
||||
@@ -699,6 +695,7 @@ public:
|
||||
static constexpr i64 NUM_SHARDS = 16;
|
||||
static constexpr i64 MAX_RETRY = 128;
|
||||
|
||||
void *entries_buf = nullptr;
|
||||
Entry *entries = nullptr;
|
||||
i64 nbuckets = 0;
|
||||
|
||||
|
||||
+5
-6
@@ -11,16 +11,15 @@
|
||||
namespace mold {
|
||||
|
||||
std::string_view demangle(std::string_view name) {
|
||||
static thread_local char *p;
|
||||
if (p)
|
||||
free(p);
|
||||
static thread_local char *buf;
|
||||
free(buf);
|
||||
|
||||
// Try to demangle as a Rust symbol. Since legacy-style Rust symbols
|
||||
// are also valid as a C++ mangled name, we need to call this before
|
||||
// cpp_demangle.
|
||||
p = rust_demangle(std::string(name).c_str(), 0);
|
||||
if (p)
|
||||
return p;
|
||||
buf = rust_demangle(std::string(name).c_str(), 0);
|
||||
if (buf)
|
||||
return buf;
|
||||
|
||||
// Try to demangle as a C++ symbol.
|
||||
if (std::optional<std::string_view> s = cpp_demangle(name))
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
# $ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||
|
||||
set -e -x
|
||||
cd "$(dirname $0)"
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 [ x86_64 | aarch64 | arm | riscv64 | ppc64le | s390x ]"
|
||||
@@ -42,7 +43,11 @@ usage() {
|
||||
case $# in
|
||||
0)
|
||||
arch=$(uname -m)
|
||||
[[ $arch = arm* ]] && arch=arm
|
||||
if [ $arch = arm64 ]; then
|
||||
arch=aarch64
|
||||
elif [[ $arch = arm* ]]; then
|
||||
arch=arm
|
||||
fi
|
||||
;;
|
||||
1)
|
||||
arch="$1"
|
||||
@@ -53,29 +58,38 @@ esac
|
||||
|
||||
echo "$arch" | grep -Eq '^(x86_64|aarch64|arm|riscv64|ppc64le|s390x)$' || usage
|
||||
|
||||
image=mold-builder-$arch
|
||||
version=$(sed -n 's/^project(mold VERSION \(.*\))/\1/p' $(dirname $0)/CMakeLists.txt)
|
||||
version=$(sed -n 's/^project(mold VERSION \(.*\))/\1/p' CMakeLists.txt)
|
||||
dest=mold-$version-$arch-linux
|
||||
|
||||
if [ "$GITHUB_REPOSITORY" = '' ]; then
|
||||
image=mold-builder-$arch
|
||||
docker_build="docker build --platform linux/$arch -t $image -"
|
||||
else
|
||||
# If this script is running on GitHub Actions, we want to cache
|
||||
# the created Docker image in GitHub's Docker repostiory.
|
||||
image=ghcr.io/$GITHUB_REPOSITORY/mold-builder-$arch
|
||||
docker_build="docker buildx build --platform linux/$arch -t $image --push --cache-to type=inline --cache-from type=registry,ref=ghcr.io/$GITHUB_REPOSITORY/mold-builder-$arch -"
|
||||
fi
|
||||
|
||||
# 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
|
||||
cat <<EOF | $docker_build
|
||||
FROM debian:jessie-20210326@sha256:32ad5050caffb2c7e969dac873bce2c370015c2256ff984b70c1c08b3a2816a0
|
||||
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 && \
|
||||
RUN sed -i -e '/^deb/d' -e 's/^# deb /deb [trusted=yes] /g' /etc/apt/sources.list && \
|
||||
echo 'Acquire::Retries "10"; Acquire::http::timeout "10"; Acquire::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 && \
|
||||
apt-get install -y --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) && \
|
||||
./bootstrap --parallel=\$(nproc) && \
|
||||
make -j\$(nproc) && \
|
||||
make install && \
|
||||
rm -rf /build
|
||||
|
||||
@@ -89,7 +103,7 @@ RUN mkdir -p /build/gcc && \
|
||||
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 -j\$(nproc) && \
|
||||
make install && \
|
||||
ln -sf /usr/lib64/libstdc++.so.6 /usr/lib/x86_64-linux-gnu/libstdc++.so.6 && \
|
||||
rm -rf /build
|
||||
@@ -97,11 +111,20 @@ EOF
|
||||
;;
|
||||
aarch64 | arm | ppc64le | s390x)
|
||||
# Debian 10 (Bullseye) released in July 2019
|
||||
cat <<EOF | docker build --platform $arch -t $image -
|
||||
FROM debian:bullseye-20231030
|
||||
#
|
||||
# We don't want to build GCC for these targets with Qemu becuase
|
||||
# that'd take extremely long time. Also I believe old build machines
|
||||
# are usually x86-64.
|
||||
[ $arch = aarch64 ] && digest=d5ed76c5265576982e6599b6f12392290d9b52b315b19b28b640aaba6e8af002
|
||||
[ $arch = arm ] && digest=bede2623dae269454c5b6dd4af15a10810a5f4ef75963d4eb6531628f98bd633
|
||||
[ $arch = ppc64le ] && digest=255f385e735469493b3465befad59a16f9d46f41d0b50e4fa6d5928c5ee7702a
|
||||
[ $arch = s390x ] && digest=96fb9ce5d3ce7f3dab7c34c18edfee093904cbc7fc19162dbcca22b2cc273b9d
|
||||
|
||||
cat <<EOF | $docker_build
|
||||
FROM debian:bullseye-20231030@sha256:$digest
|
||||
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 && \
|
||||
RUN sed -i -e '/^deb/d' -e 's/^# deb /deb [trusted=yes] /g' /etc/apt/sources.list && \
|
||||
echo 'Acquire::Retries "10"; Acquire::http::timeout "10"; Acquire::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 && \
|
||||
@@ -111,8 +134,8 @@ EOF
|
||||
;;
|
||||
riscv64)
|
||||
# snapshot.debian.org is not available for RISC-V binaries
|
||||
cat <<EOF | docker build --platform $arch -t $image -
|
||||
FROM riscv64/debian:unstable-20231030
|
||||
cat <<EOF | $docker_build
|
||||
FROM riscv64/debian:unstable-20231030@sha256:be1882409392c1f68f23e1e04bd965c49398f4a358b1a15f7b1820c0b39ede5b
|
||||
ENV DEBIAN_FRONTEND=noninteractive TZ=UTC
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends build-essential gcc-12 g++-12 cmake && \
|
||||
@@ -123,18 +146,24 @@ EOF
|
||||
;;
|
||||
esac
|
||||
|
||||
# Source tarballs available on GitHub don't contain .git history.
|
||||
# Clone the repo if missing.
|
||||
[ -d .git ] || git clone --branch v$version --depth 1 --bare https://github.com/rui314/mold .git
|
||||
|
||||
# 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" $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 &&
|
||||
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"
|
||||
docker run --platform linux/$arch -i --rm -v "$(pwd):/mold" $image bash -c "
|
||||
set -e
|
||||
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
|
||||
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
|
||||
"
|
||||
+11
-5
@@ -1,6 +1,6 @@
|
||||
.\" generated with Ronn-NG/v0.9.1
|
||||
.\" http://github.com/apjanke/ronn-ng/tree/0.9.1
|
||||
.TH "MOLD" "1" "July 2023" ""
|
||||
.TH "MOLD" "1" "November 2023" ""
|
||||
.SH "NAME"
|
||||
\fBmold\fR \- a modern linker
|
||||
.SH "SYNOPSIS"
|
||||
@@ -179,6 +179,9 @@ Omit \fB\.symtab\fR section from the output file\.
|
||||
\fB\-u\fR \fIsymbol\fR, \fB\-\-undefined\fR=\fIsymbol\fR
|
||||
If \fIsymbol\fR remains as an undefined symbol after reading all object files, and if there is a static archive that contains an object file defining \fIsymbol\fR, pull out the object file and link it so that the output file contains a definition of \fIsymbol\fR\.
|
||||
.TP
|
||||
\fB\-y\fR \fIsymbol\fR, \fB\-\-trace\-symbol\fR=\fIsymbol\fR
|
||||
Trace references to \fIsymbol\fR\.
|
||||
.TP
|
||||
\fB\-\-Bdynamic\fR
|
||||
Link against shared libraries\.
|
||||
.TP
|
||||
@@ -354,6 +357,9 @@ Create a share library\.
|
||||
\fB\-\-spare\-dynamic\-tags\fR=\fInumber\fR
|
||||
Reserve the given \fInumber\fR of tags in \fB\.dynamic\fR section\.
|
||||
.TP
|
||||
\fB\-\-spare\-program\-headers\fR=\fInumber\fR
|
||||
Reserve the given \fInumber\fR of entries in the program header\.
|
||||
.TP
|
||||
\fB\-\-start\-lib\fR, \fB\-\-end\-lib\fR
|
||||
Handle object files between \fB\-\-start\-lib\fR and \fB\-\-end\-lib\fR as if they were in an archive file\. That means object files between them are linked only when they are needed to resolve undefined symbols\. The options are useful if you want to link object files only when they are needed but want to avoid the overhead of running ar(3)\.
|
||||
.TP
|
||||
@@ -475,13 +481,13 @@ Ignored
|
||||
.SH "ENVIRONMENT VARIABLES"
|
||||
.TP
|
||||
\fBMOLD_JOBS\fR
|
||||
If this variable is set to \fB1\fR, only one process of \fBmold\fR runs actively\. A mold process invoked while another active mold process is running will wait before doing anything until the active process exits\.
|
||||
If this variable is set to \fB1\fR, only one \fBmold\fR process will run at a time\. If a new mold process is initiated while another is already active, the new process will wait until the active one completes before starting\.
|
||||
.IP
|
||||
The purpose of this environment variable is to reduce peak memory usage\. Since mold is highly parallelized, there's no point in running it simultaneously\. If you run N instances of mold in parallel, it would take N times more time and N times more memory\. If you run them serially, it would still take N times more to finish, but their peak memory usage is reduced to normal\.
|
||||
The primary reason for this environment variable is to minimize peak memory usage\. Since mold is designed to operate with high parallelism, running multiple mold instances simultaneously may not be beneficial\. If you execute N instances of mold concurrently, it could require N times the time and N times the memory\. On the other hand, running them one after the other might still take N times longer, but the peak memory usage would be the same as running just a single instance\.
|
||||
.IP
|
||||
If your build system tends to invoke multiple linker processes simultaneously, you may want to try to set this environment variable to \fB1\fR to see if it could improve overall performance\.
|
||||
If your build system invokes multiple linker processes simultaneously and some of them often get killed due to out\-of\-memory errors, you might consider setting this environment variable to \fB1\fR to see if it addresses the OOM issue\.
|
||||
.IP
|
||||
Currently, any value other than 1 is silently ignored\.
|
||||
Currently, any value other than \fB1\fR is silently ignored\.
|
||||
.TP
|
||||
\fBMOLD_DEBUG\fR
|
||||
If this variable is set to a non\-empty string, \fBmold\fR embeds its command\-line options in the output file's \fB\.comment\fR section\.
|
||||
|
||||
@@ -321,6 +321,9 @@ arguments.
|
||||
_symbol_, pull out the object file and link it so that the output file
|
||||
contains a definition of _symbol_.
|
||||
|
||||
* `-y` _symbol_, `--trace-symbol`=_symbol_:
|
||||
Trace references to _symbol_.
|
||||
|
||||
* `--Bdynamic`:
|
||||
Link against shared libraries.
|
||||
|
||||
@@ -581,6 +584,9 @@ arguments.
|
||||
* `--spare-dynamic-tags`=_number_:
|
||||
Reserve the given _number_ of tags in `.dynamic` section.
|
||||
|
||||
* `--spare-program-headers`=_number_:
|
||||
Reserve the given _number_ of entries in the program header.
|
||||
|
||||
* `--start-lib`, `--end-lib`:
|
||||
Handle object files between `--start-lib` and `--end-lib` as if they were in
|
||||
an archive file. That means object files between them are linked only when
|
||||
|
||||
+1
-1
@@ -647,7 +647,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
}
|
||||
|
||||
template <>
|
||||
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
|
||||
void Thunk<E>::copy_buf(Context<E> &ctx) {
|
||||
// TLS trampoline code. ARM32's TLSDESC is designed so that this
|
||||
// common piece of code is factored out from object files to reduce
|
||||
// output size. Since no one provide, the linker has to synthesize it.
|
||||
|
||||
+1
-1
@@ -602,7 +602,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
}
|
||||
|
||||
template <>
|
||||
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
|
||||
void Thunk<E>::copy_buf(Context<E> &ctx) {
|
||||
static const ul32 insn[] = {
|
||||
0x9000'0010, // adrp x16, 0 # R_AARCH64_ADR_PREL_PG_HI21
|
||||
0x9100'0210, // add x16, x16 # R_AARCH64_ADD_ABS_LO12_NC
|
||||
|
||||
+47
-4
@@ -254,6 +254,34 @@ static void relax_ld_to_le(u8 *loc, ElfRel<E> rel, u64 val) {
|
||||
}
|
||||
}
|
||||
|
||||
static u32 relax_tlsdesc_to_ie(u8 *loc) {
|
||||
switch ((loc[0] << 8) | loc[1]) {
|
||||
case 0x8d83: return 0x8b83; // lea 0(%ebx), %eax -> mov 0(%ebx), %eax
|
||||
case 0x8d9b: return 0x8b9b; // lea 0(%ebx), %ebx -> mov 0(%ebx), %ebx
|
||||
case 0x8d8b: return 0x8b8b; // lea 0(%ebx), %ecx -> mov 0(%ebx), %ecx
|
||||
case 0x8d93: return 0x8b93; // lea 0(%ebx), %edx -> mov 0(%ebx), %edx
|
||||
case 0x8db3: return 0x8bb3; // lea 0(%ebx), %esi -> mov 0(%ebx), %esi
|
||||
case 0x8dbb: return 0x8bbb; // lea 0(%ebx), %edi -> mov 0(%ebx), %edi
|
||||
case 0x8da3: return 0x8ba3; // lea 0(%ebx), %esp -> mov 0(%ebx), %esp
|
||||
case 0x8dab: return 0x8bab; // lea 0(%ebx), %ebp -> mov 0(%ebx), %ebp
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 relax_tlsdesc_to_le(u8 *loc) {
|
||||
switch ((loc[0] << 8) | loc[1]) {
|
||||
case 0x8d83: return 0x90b8; // lea 0(%ebx), %eax -> mov $0, %eax
|
||||
case 0x8d9b: return 0x90bb; // lea 0(%ebx), %ebx -> mov $0, %ebx
|
||||
case 0x8d8b: return 0x90b9; // lea 0(%ebx), %ecx -> mov $0, %ecx
|
||||
case 0x8d93: return 0x90ba; // lea 0(%ebx), %edx -> mov $0, %edx
|
||||
case 0x8db3: return 0x90be; // lea 0(%ebx), %esi -> mov $0, %esi
|
||||
case 0x8dbb: return 0x90bf; // lea 0(%ebx), %edi -> mov $0, %edi
|
||||
case 0x8da3: return 0x90bc; // lea 0(%ebx), %esp -> mov $0, %esp
|
||||
case 0x8dab: return 0x90bd; // lea 0(%ebx), %ebp -> mov $0, %ebp
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
@@ -373,15 +401,30 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
//
|
||||
// mov $foo@TPOFF, %eax
|
||||
// nop
|
||||
//
|
||||
// We allow the following alternative code sequence too because
|
||||
// LLVM emits such code.
|
||||
//
|
||||
// lea 0(%ebx), %reg
|
||||
// R_386_TLS_GOTDESC foo
|
||||
// mov %reg, %eax
|
||||
// call *(%eax)
|
||||
// R_386_TLS_DESC_CALL foo
|
||||
if (sym.has_tlsdesc(ctx)) {
|
||||
*(ul32 *)loc = sym.get_tlsdesc_addr(ctx) + A - GOT;
|
||||
} else if (sym.has_gottp(ctx)) {
|
||||
loc[-2] = 0x8b;
|
||||
loc[-1] = 0x83;
|
||||
u32 insn = relax_tlsdesc_to_ie(loc - 2);
|
||||
if (!insn)
|
||||
Fatal(ctx) << *this << ": illegal instruction sequence for TLSDESC";
|
||||
loc[-2] = insn >> 8;
|
||||
loc[-1] = insn;
|
||||
*(ul32 *)loc = sym.get_gottp_addr(ctx) + A - GOT;
|
||||
} else {
|
||||
loc[-2] = 0x90;
|
||||
loc[-1] = 0xb8;
|
||||
u32 insn = relax_tlsdesc_to_le(loc - 2);
|
||||
if (!insn)
|
||||
Fatal(ctx) << *this << ": illegal instruction sequence for TLSDESC";
|
||||
loc[-2] = insn >> 8;
|
||||
loc[-1] = insn;
|
||||
*(ul32 *)loc = S + A - ctx.tp_addr;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -653,7 +653,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
}
|
||||
|
||||
template <>
|
||||
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
|
||||
void Thunk<E>::copy_buf(Context<E> &ctx) {
|
||||
static const ul32 insn[] = {
|
||||
0x1e00'000c, // pcaddu18i $t0, 0
|
||||
0x4c00'0180, // jirl $zero, $t0, 0
|
||||
|
||||
+1
-1
@@ -408,7 +408,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
}
|
||||
|
||||
template <>
|
||||
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
|
||||
void Thunk<E>::copy_buf(Context<E> &ctx) {
|
||||
static const ub32 local_thunk[] = {
|
||||
// Get this thunk's address
|
||||
0x7c08'02a6, // mflr r0
|
||||
|
||||
+8
-9
@@ -409,7 +409,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
}
|
||||
|
||||
template <>
|
||||
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
|
||||
void Thunk<E>::copy_buf(Context<E> &ctx) {
|
||||
// If the destination is .plt.got, we save the current r2, read an
|
||||
// address of a function descriptor from .got, restore %r2 and jump
|
||||
// to the function.
|
||||
@@ -638,15 +638,14 @@ void ppc64v1_scan_symbols(Context<E> &ctx) {
|
||||
});
|
||||
|
||||
// Functions referenced by the ELF header also have to have .opd entries.
|
||||
auto mark = [&](std::string_view name) {
|
||||
if (!name.empty())
|
||||
if (Symbol<E> &sym = *get_symbol(ctx, name); !sym.is_imported)
|
||||
sym.flags |= NEEDS_PPC_OPD;
|
||||
};
|
||||
if (!ctx.arg.entry->is_imported)
|
||||
ctx.arg.entry->flags |= NEEDS_PPC_OPD;
|
||||
|
||||
mark(ctx.arg.entry);
|
||||
mark(ctx.arg.init);
|
||||
mark(ctx.arg.fini);
|
||||
if (!ctx.arg.init->is_imported)
|
||||
ctx.arg.init->flags |= NEEDS_PPC_OPD;
|
||||
|
||||
if (!ctx.arg.fini->is_imported)
|
||||
ctx.arg.fini->flags |= NEEDS_PPC_OPD;
|
||||
}
|
||||
|
||||
void PPC64OpdSection::add_symbol(Context<E> &ctx, Symbol<E> *sym) {
|
||||
|
||||
+1
-1
@@ -484,7 +484,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
}
|
||||
|
||||
template <>
|
||||
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
|
||||
void Thunk<E>::copy_buf(Context<E> &ctx) {
|
||||
// If the destination is PLT, we read an address from .got.plt or .got
|
||||
// and jump there.
|
||||
static const ul32 plt_thunk[] = {
|
||||
|
||||
+65
-8
@@ -178,6 +178,50 @@ static u32 relax_gottpoff(u8 *loc) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 relax_tlsdesc_to_ie(u8 *loc) {
|
||||
switch ((loc[0] << 16) | (loc[1] << 8) | loc[2]) {
|
||||
case 0x488d05: return 0x488b05; // lea 0(%rip), %rax -> mov 0(%rip), %rax
|
||||
case 0x488d0d: return 0x488b0d; // lea 0(%rip), %rcx -> mov 0(%rip), %rcx
|
||||
case 0x488d15: return 0x488b15; // lea 0(%rip), %rdx -> mov 0(%rip), %rdx
|
||||
case 0x488d1d: return 0x488b1d; // lea 0(%rip), %rbx -> mov 0(%rip), %rbx
|
||||
case 0x488d25: return 0x488b25; // lea 0(%rip), %rsp -> mov 0(%rip), %rsp
|
||||
case 0x488d2d: return 0x488b2d; // lea 0(%rip), %rbp -> mov 0(%rip), %rbp
|
||||
case 0x488d35: return 0x488b35; // lea 0(%rip), %rsi -> mov 0(%rip), %rsi
|
||||
case 0x488d3d: return 0x488b3d; // lea 0(%rip), %rdi -> mov 0(%rip), %rdi
|
||||
case 0x4c8d05: return 0x4c8b05; // lea 0(%rip), %r8 -> mov 0(%rip), %r8
|
||||
case 0x4c8d0d: return 0x4c8b0d; // lea 0(%rip), %r9 -> mov 0(%rip), %r9
|
||||
case 0x4c8d15: return 0x4c8b15; // lea 0(%rip), %r10 -> mov 0(%rip), %r10
|
||||
case 0x4c8d1d: return 0x4c8b1d; // lea 0(%rip), %r11 -> mov 0(%rip), %r11
|
||||
case 0x4c8d25: return 0x4c8b25; // lea 0(%rip), %r12 -> mov 0(%rip), %r12
|
||||
case 0x4c8d2d: return 0x4c8b2d; // lea 0(%rip), %r13 -> mov 0(%rip), %r13
|
||||
case 0x4c8d35: return 0x4c8b35; // lea 0(%rip), %r14 -> mov 0(%rip), %r14
|
||||
case 0x4c8d3d: return 0x4c8b3d; // lea 0(%rip), %r15 -> mov 0(%rip), %r15
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 relax_tlsdesc_to_le(u8 *loc) {
|
||||
switch ((loc[0] << 16) | (loc[1] << 8) | loc[2]) {
|
||||
case 0x488d05: return 0x48c7c0; // lea 0(%rip), %rax -> mov $0, %rax
|
||||
case 0x488d0d: return 0x48c7c1; // lea 0(%rip), %rcx -> mov $0, %rcx
|
||||
case 0x488d15: return 0x48c7c2; // lea 0(%rip), %rdx -> mov $0, %rdx
|
||||
case 0x488d1d: return 0x48c7c3; // lea 0(%rip), %rbx -> mov $0, %rbx
|
||||
case 0x488d25: return 0x48c7c4; // lea 0(%rip), %rsp -> mov $0, %rsp
|
||||
case 0x488d2d: return 0x48c7c5; // lea 0(%rip), %rbp -> mov $0, %rbp
|
||||
case 0x488d35: return 0x48c7c6; // lea 0(%rip), %rsi -> mov $0, %rsi
|
||||
case 0x488d3d: return 0x48c7c7; // lea 0(%rip), %rdi -> mov $0, %rdi
|
||||
case 0x4c8d05: return 0x49c7c0; // lea 0(%rip), %r8 -> mov $0, %r8
|
||||
case 0x4c8d0d: return 0x49c7c1; // lea 0(%rip), %r9 -> mov $0, %r9
|
||||
case 0x4c8d15: return 0x49c7c2; // lea 0(%rip), %r10 -> mov $0, %r10
|
||||
case 0x4c8d1d: return 0x49c7c3; // lea 0(%rip), %r11 -> mov $0, %r11
|
||||
case 0x4c8d25: return 0x49c7c4; // lea 0(%rip), %r12 -> mov $0, %r12
|
||||
case 0x4c8d2d: return 0x49c7c5; // lea 0(%rip), %r13 -> mov $0, %r13
|
||||
case 0x4c8d35: return 0x49c7c6; // lea 0(%rip), %r14 -> mov $0, %r14
|
||||
case 0x4c8d3d: return 0x49c7c7; // lea 0(%rip), %r15 -> mov $0, %r15
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Rewrite a function call to __tls_get_addr to a cheaper instruction
|
||||
// sequence. We can do this when we know the thread-local variable's TP-
|
||||
// relative address at link-time.
|
||||
@@ -501,19 +545,32 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
//
|
||||
// mov $foo@TPOFF, %rax
|
||||
// nop
|
||||
//
|
||||
// We allow the following alternative code sequence too because
|
||||
// LLVM emits such code.
|
||||
//
|
||||
// lea 0(%rip), %reg
|
||||
// R_X86_64_GOTPC32_TLSDESC foo
|
||||
// mov %reg, %rax
|
||||
// call *(%rax)
|
||||
// R_X86_64_TLSDESC_CALL foo
|
||||
if (sym.has_tlsdesc(ctx)) {
|
||||
write32s(sym.get_tlsdesc_addr(ctx) + A - P);
|
||||
} else if (sym.has_gottp(ctx)) {
|
||||
// mov foo@gottpoff(%rip), %rax
|
||||
loc[-3] = 0x48;
|
||||
loc[-2] = 0x8b;
|
||||
loc[-1] = 0x05;
|
||||
u32 insn = relax_tlsdesc_to_ie(loc - 3);
|
||||
if (!insn)
|
||||
Fatal(ctx) << *this << ": illegal instruction sequence for TLSDESC";
|
||||
loc[-3] = insn >> 16;
|
||||
loc[-2] = insn >> 8;
|
||||
loc[-1] = insn;
|
||||
write32s(sym.get_gottp_addr(ctx) + A - P);
|
||||
} else {
|
||||
// mov $foo@tpoff, %rax
|
||||
loc[-3] = 0x48;
|
||||
loc[-2] = 0xc7;
|
||||
loc[-1] = 0xc0;
|
||||
u32 insn = relax_tlsdesc_to_le(loc - 3);
|
||||
if (!insn)
|
||||
Fatal(ctx) << *this << ": illegal instruction sequence for TLSDESC";
|
||||
loc[-3] = insn >> 16;
|
||||
loc[-2] = insn >> 8;
|
||||
loc[-1] = insn;
|
||||
write32s(S - ctx.tp_addr);
|
||||
}
|
||||
break;
|
||||
|
||||
+21
-5
@@ -52,6 +52,8 @@ Options:
|
||||
-s, --strip-all Strip .symtab section
|
||||
-u SYMBOL, --undefined SYMBOL
|
||||
Force to resolve SYMBOL
|
||||
-y SYMBOL, --trace-symbol SYMBOL
|
||||
Trace references to SYMBOL
|
||||
--Bdynamic, --dy Link against shared libraries (default)
|
||||
--Bstatic, --dn, --static Do not link against shared libraries
|
||||
--Bsymbolic Bind global symbols locally
|
||||
@@ -107,6 +109,8 @@ Options:
|
||||
Allow merging non-executable sections with --icf
|
||||
--image-base ADDR Set the base address to a given value
|
||||
--init SYMBOL Call SYMBOL at load-time
|
||||
--nmagic Do not page align sections
|
||||
--no-nmagic
|
||||
--no-undefined Report undefined symbols (even with --shared)
|
||||
--noinhibit-exec Create an output file even if errors occur
|
||||
--oformat=binary Omit ELF, section and program headers
|
||||
@@ -140,7 +144,9 @@ Options:
|
||||
--shuffle-sections[=SEED] Randomize the output by shuffling input sections
|
||||
--sort-common Ignored
|
||||
--sort-section Ignored
|
||||
--spare-dynamic-tags NUMBER Reserve give number of tags in .dynamic section
|
||||
--spare-dynamic-tags NUMBER Reserve given number of tags in .dynamic section
|
||||
--spare-program-headers NUMBER
|
||||
Reserve given number of slots in the program header
|
||||
--start-lib Give following object files in-archive-file semantics
|
||||
--end-lib End the effect of --start-lib
|
||||
--stats Print input statistics
|
||||
@@ -586,7 +592,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
ctx.arg.emit_relocs = true;
|
||||
ctx.arg.discard_locals = false;
|
||||
} else if (read_arg("e") || read_arg("entry")) {
|
||||
ctx.arg.entry = arg;
|
||||
ctx.arg.entry = get_symbol(ctx, arg);
|
||||
} else if (read_arg("Map")) {
|
||||
ctx.arg.Map = arg;
|
||||
ctx.arg.print_map = true;
|
||||
@@ -604,6 +610,9 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
ctx.arg.shared = true;
|
||||
} else if (read_arg("spare-dynamic-tags")) {
|
||||
ctx.arg.spare_dynamic_tags = parse_number(ctx, "spare-dynamic-tags", arg);
|
||||
} else if (read_arg("spare-program-headers")) {
|
||||
ctx.arg.spare_program_headers
|
||||
= parse_number(ctx, "spare-program-headers", arg);
|
||||
} else if (read_flag("start-lib")) {
|
||||
remaining.push_back("--start-lib");
|
||||
} else if (read_flag("start-stop")) {
|
||||
@@ -664,9 +673,9 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
} else if (read_arg("require-defined")) {
|
||||
ctx.arg.require_defined.push_back(get_symbol(ctx, arg));
|
||||
} else if (read_arg("init")) {
|
||||
ctx.arg.init = arg;
|
||||
ctx.arg.init = get_symbol(ctx, arg);
|
||||
} else if (read_arg("fini")) {
|
||||
ctx.arg.fini = arg;
|
||||
ctx.arg.fini = get_symbol(ctx, arg);
|
||||
} else if (read_arg("hash-style")) {
|
||||
if (arg == "sysv") {
|
||||
ctx.arg.hash_style_sysv = true;
|
||||
@@ -887,6 +896,10 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
ctx.arg.z_rewrite_endbr = true;
|
||||
} else if (read_flag("no-undefined")) {
|
||||
ctx.arg.z_defs = true;
|
||||
} else if (read_flag("nmagic")) {
|
||||
ctx.arg.nmagic = true;
|
||||
} else if (read_flag("no-nmagic")) {
|
||||
ctx.arg.nmagic = false;
|
||||
} else if (read_flag("fatal-warnings")) {
|
||||
ctx.arg.fatal_warnings = true;
|
||||
} else if (read_flag("no-fatal-warnings")) {
|
||||
@@ -1200,6 +1213,9 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
else if (!ctx.arg.section_order.empty())
|
||||
ctx.arg.z_relro = false;
|
||||
|
||||
if (ctx.arg.nmagic)
|
||||
ctx.arg.z_relro = false;
|
||||
|
||||
if (!ctx.arg.shared) {
|
||||
if (!ctx.arg.filter.empty())
|
||||
Fatal(ctx) << "-filter may not be used without -shared";
|
||||
@@ -1238,7 +1254,7 @@ 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(get_symbol(ctx, ctx.arg.entry));
|
||||
ctx.arg.undefined.push_back(ctx.arg.entry);
|
||||
|
||||
for (i64 i = 0; i < ctx.arg.defsyms.size(); i++) {
|
||||
std::variant<Symbol<E> *, u64> &val = ctx.arg.defsyms[i].second;
|
||||
|
||||
@@ -1869,7 +1869,7 @@ struct X86_64 {
|
||||
static constexpr u32 plt_hdr_size = 32;
|
||||
static constexpr u32 plt_size = 16;
|
||||
static constexpr u32 pltgot_size = 8;
|
||||
static constexpr u8 filler[] = { 0xcc };
|
||||
static constexpr u8 filler[] = { 0xcc }; // int3
|
||||
|
||||
static constexpr u32 R_COPY = R_X86_64_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_X86_64_GLOB_DAT;
|
||||
@@ -1894,7 +1894,7 @@ struct I386 {
|
||||
static constexpr u32 plt_hdr_size = 16;
|
||||
static constexpr u32 plt_size = 16;
|
||||
static constexpr u32 pltgot_size = 16;
|
||||
static constexpr u8 filler[] = { 0xcc };
|
||||
static constexpr u8 filler[] = { 0xcc }; // int3
|
||||
|
||||
static constexpr u32 R_COPY = R_386_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_386_GLOB_DAT;
|
||||
@@ -1921,7 +1921,7 @@ struct ARM64 {
|
||||
static constexpr u32 pltgot_size = 16;
|
||||
static constexpr u32 thunk_hdr_size = 0;
|
||||
static constexpr u32 thunk_size = 16;
|
||||
static constexpr u8 filler[] = { 0x00, 0x7d, 0x20, 0xd4 };
|
||||
static constexpr u8 filler[] = { 0x00, 0x7d, 0x20, 0xd4 }; // brk
|
||||
|
||||
static constexpr u32 R_COPY = R_AARCH64_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_AARCH64_GLOB_DAT;
|
||||
@@ -1948,7 +1948,7 @@ struct ARM32 {
|
||||
static constexpr u32 pltgot_size = 16;
|
||||
static constexpr u32 thunk_hdr_size = 16;
|
||||
static constexpr u32 thunk_size = 16;
|
||||
static constexpr u8 filler[] = { 0xff, 0xde };
|
||||
static constexpr u8 filler[] = { 0xff, 0xde }; // udf
|
||||
|
||||
static constexpr u32 R_COPY = R_ARM_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_ARM_GLOB_DAT;
|
||||
@@ -1974,7 +1974,7 @@ struct RV64 {
|
||||
static constexpr u32 plt_hdr_size = 32;
|
||||
static constexpr u32 plt_size = 16;
|
||||
static constexpr u32 pltgot_size = 16;
|
||||
static constexpr u8 filler[] = { 0x02, 0x90 };
|
||||
static constexpr u8 filler[] = { 0x02, 0x90 }; // ebreak
|
||||
|
||||
static constexpr u32 R_COPY = R_RISCV_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_RISCV_64;
|
||||
@@ -2007,7 +2007,7 @@ struct RV32 {
|
||||
static constexpr u32 plt_hdr_size = 32;
|
||||
static constexpr u32 plt_size = 16;
|
||||
static constexpr u32 pltgot_size = 16;
|
||||
static constexpr u8 filler[] = { 0x02, 0x90 };
|
||||
static constexpr u8 filler[] = { 0x02, 0x90 }; // ebreak
|
||||
|
||||
static constexpr u32 R_COPY = R_RISCV_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_RISCV_32;
|
||||
@@ -2044,7 +2044,7 @@ struct PPC32 {
|
||||
static constexpr u32 pltgot_size = 36;
|
||||
static constexpr u32 thunk_hdr_size = 0;
|
||||
static constexpr u32 thunk_size = 36;
|
||||
static constexpr u8 filler[] = { 0x7f, 0xe0, 0x00, 0x08 };
|
||||
static constexpr u8 filler[] = { 0x7f, 0xe0, 0x00, 0x08 }; // trap
|
||||
|
||||
static constexpr u32 R_COPY = R_PPC_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_PPC_GLOB_DAT;
|
||||
@@ -2086,7 +2086,7 @@ struct PPC64V1 : PPC64 {
|
||||
static constexpr u32 pltgot_size = 0;
|
||||
static constexpr u32 thunk_hdr_size = 0;
|
||||
static constexpr u32 thunk_size = 28;
|
||||
static constexpr u8 filler[] = { 0x7f, 0xe0, 0x00, 0x08 };
|
||||
static constexpr u8 filler[] = { 0x7f, 0xe0, 0x00, 0x08 }; // trap
|
||||
};
|
||||
|
||||
struct PPC64V2 : PPC64 {
|
||||
@@ -2097,7 +2097,7 @@ struct PPC64V2 : PPC64 {
|
||||
static constexpr u32 pltgot_size = 0;
|
||||
static constexpr u32 thunk_hdr_size = 0;
|
||||
static constexpr u32 thunk_size = 24;
|
||||
static constexpr u8 filler[] = { 0x08, 0x00, 0xe0, 0x7f };
|
||||
static constexpr u8 filler[] = { 0x08, 0x00, 0xe0, 0x7f }; // trap
|
||||
};
|
||||
|
||||
struct S390X {
|
||||
@@ -2110,7 +2110,7 @@ struct S390X {
|
||||
static constexpr u32 plt_hdr_size = 48;
|
||||
static constexpr u32 plt_size = 16;
|
||||
static constexpr u32 pltgot_size = 16;
|
||||
static constexpr u8 filler[] = { 0x07, 0x00 };
|
||||
static constexpr u8 filler[] = { 0x07, 0x00 }; // nop
|
||||
|
||||
static constexpr u32 R_COPY = R_390_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_390_GLOB_DAT;
|
||||
@@ -2134,7 +2134,7 @@ struct SPARC64 {
|
||||
static constexpr u32 plt_hdr_size = 128;
|
||||
static constexpr u32 plt_size = 32;
|
||||
static constexpr u32 pltgot_size = 32;
|
||||
static constexpr u8 filler[] = { 0x91, 0xd0, 0x20, 0x05 };
|
||||
static constexpr u8 filler[] = { 0x91, 0xd0, 0x20, 0x05 }; // ta 5
|
||||
|
||||
static constexpr u32 R_COPY = R_SPARC_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_SPARC_GLOB_DAT;
|
||||
@@ -2158,7 +2158,7 @@ struct M68K {
|
||||
static constexpr u32 plt_hdr_size = 18;
|
||||
static constexpr u32 plt_size = 14;
|
||||
static constexpr u32 pltgot_size = 8;
|
||||
static constexpr u8 filler[] = { 0x4a, 0xfc };
|
||||
static constexpr u8 filler[] = { 0x4a, 0xfc }; // illegal
|
||||
|
||||
static constexpr u32 R_COPY = R_68K_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_68K_GLOB_DAT;
|
||||
@@ -2181,7 +2181,7 @@ struct SH4 {
|
||||
static constexpr u32 plt_hdr_size = 16;
|
||||
static constexpr u32 plt_size = 16;
|
||||
static constexpr u32 pltgot_size = 12;
|
||||
static constexpr u8 filler[] = { 0x00, 0x00 };
|
||||
static constexpr u8 filler[] = { 0x00, 0x90 }; // nop
|
||||
|
||||
static constexpr u32 R_COPY = R_SH_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_SH_GLOB_DAT;
|
||||
@@ -2204,7 +2204,7 @@ struct ALPHA {
|
||||
static constexpr u32 plt_hdr_size = 0;
|
||||
static constexpr u32 plt_size = 0;
|
||||
static constexpr u32 pltgot_size = 0;
|
||||
static constexpr u8 filler[] = { 0x81, 0x00, 0x00, 0x00 };
|
||||
static constexpr u8 filler[] = { 0x81, 0x00, 0x00, 0x00 }; // bugchk
|
||||
|
||||
static constexpr u32 R_COPY = R_ALPHA_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_ALPHA_GLOB_DAT;
|
||||
@@ -2229,7 +2229,7 @@ struct LOONGARCH64 {
|
||||
static constexpr u32 pltgot_size = 16;
|
||||
static constexpr u32 thunk_hdr_size = 0;
|
||||
static constexpr u32 thunk_size = 8;
|
||||
static constexpr u8 filler[] = { 0x00, 0x00, 0x2a, 0x00 };
|
||||
static constexpr u8 filler[] = { 0x00, 0x00, 0x2a, 0x00 }; // break 0
|
||||
|
||||
static constexpr u32 R_COPY = R_LARCH_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_LARCH_64;
|
||||
@@ -2255,7 +2255,7 @@ struct LOONGARCH32 {
|
||||
static constexpr u32 pltgot_size = 16;
|
||||
static constexpr u32 thunk_hdr_size = 0;
|
||||
static constexpr u32 thunk_size = 8;
|
||||
static constexpr u8 filler[] = { 0x00, 0x00, 0x2a, 0x00 };
|
||||
static constexpr u8 filler[] = { 0x00, 0x00, 0x2a, 0x00 }; // break 0
|
||||
|
||||
static constexpr u32 R_COPY = R_LARCH_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_LARCH_32;
|
||||
|
||||
@@ -114,8 +114,6 @@ static void collect_root_set(Context<E> &ctx,
|
||||
});
|
||||
|
||||
// Add sections referenced by root symbols.
|
||||
enqueue_symbol(get_symbol(ctx, ctx.arg.entry));
|
||||
|
||||
for (Symbol<E> *sym : ctx.arg.undefined)
|
||||
enqueue_symbol(sym);
|
||||
|
||||
|
||||
+50
-53
@@ -324,7 +324,7 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
|
||||
ctx.has_ctors = true;
|
||||
|
||||
if (name == ".eh_frame")
|
||||
eh_frame_section = this->sections[i].get();
|
||||
eh_frame_sections.push_back(this->sections[i].get());
|
||||
|
||||
if constexpr (is_ppc32<E>)
|
||||
if (name == ".got2")
|
||||
@@ -416,78 +416,75 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
|
||||
// This function parses an input .eh_frame section.
|
||||
template <typename E>
|
||||
void ObjectFile<E>::parse_ehframe(Context<E> &ctx) {
|
||||
if (!eh_frame_section)
|
||||
return;
|
||||
for (InputSection<E> *isec : eh_frame_sections) {
|
||||
std::span<ElfRel<E>> rels = isec->get_rels(ctx);
|
||||
i64 cies_begin = cies.size();
|
||||
i64 fdes_begin = fdes.size();
|
||||
|
||||
InputSection<E> &isec = *eh_frame_section;
|
||||
std::span<ElfRel<E>> rels = isec.get_rels(ctx);
|
||||
i64 cies_begin = cies.size();
|
||||
i64 fdes_begin = fdes.size();
|
||||
// Read CIEs and FDEs until empty.
|
||||
std::string_view contents = this->get_string(ctx, isec->shdr());
|
||||
i64 rel_idx = 0;
|
||||
|
||||
// Read CIEs and FDEs until empty.
|
||||
std::string_view contents = this->get_string(ctx, isec.shdr());
|
||||
i64 rel_idx = 0;
|
||||
for (std::string_view data = contents; !data.empty();) {
|
||||
i64 size = *(U32<E> *)data.data();
|
||||
if (size == 0)
|
||||
break;
|
||||
|
||||
for (std::string_view data = contents; !data.empty();) {
|
||||
i64 size = *(U32<E> *)data.data();
|
||||
if (size == 0)
|
||||
break;
|
||||
i64 begin_offset = data.data() - contents.data();
|
||||
i64 end_offset = begin_offset + size + 4;
|
||||
i64 id = *(U32<E> *)(data.data() + 4);
|
||||
data = data.substr(size + 4);
|
||||
|
||||
i64 begin_offset = data.data() - contents.data();
|
||||
i64 end_offset = begin_offset + size + 4;
|
||||
i64 id = *(U32<E> *)(data.data() + 4);
|
||||
data = data.substr(size + 4);
|
||||
i64 rel_begin = rel_idx;
|
||||
while (rel_idx < rels.size() && rels[rel_idx].r_offset < end_offset)
|
||||
rel_idx++;
|
||||
assert(rel_idx == rels.size() || begin_offset <= rels[rel_begin].r_offset);
|
||||
|
||||
i64 rel_begin = rel_idx;
|
||||
while (rel_idx < rels.size() && rels[rel_idx].r_offset < end_offset)
|
||||
rel_idx++;
|
||||
assert(rel_idx == rels.size() || begin_offset <= rels[rel_begin].r_offset);
|
||||
if (id == 0) {
|
||||
// This is CIE.
|
||||
cies.emplace_back(ctx, *this, *isec, begin_offset, rels, rel_begin);
|
||||
} else {
|
||||
// This is FDE.
|
||||
if (rel_begin == rel_idx || rels[rel_begin].r_sym == 0) {
|
||||
// FDE has no valid relocation, which means FDE is dead from
|
||||
// the beginning. Compilers usually don't create such FDE, but
|
||||
// `ld -r` tend to generate such dead FDEs.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (id == 0) {
|
||||
// This is CIE.
|
||||
cies.emplace_back(ctx, *this, isec, begin_offset, rels, rel_begin);
|
||||
} else {
|
||||
// This is FDE.
|
||||
if (rel_begin == rel_idx || rels[rel_begin].r_sym == 0) {
|
||||
// FDE has no valid relocation, which means FDE is dead from
|
||||
// the beginning. Compilers usually don't create such FDE, but
|
||||
// `ld -r` tend to generate such dead FDEs.
|
||||
continue;
|
||||
if (rels[rel_begin].r_offset - begin_offset != 8)
|
||||
Fatal(ctx) << *isec << ": FDE's first relocation should have offset 8";
|
||||
|
||||
fdes.emplace_back(begin_offset, rel_begin);
|
||||
}
|
||||
}
|
||||
|
||||
if (rels[rel_begin].r_offset - begin_offset != 8)
|
||||
Fatal(ctx) << isec << ": FDE's first relocation should have offset 8";
|
||||
// Associate CIEs to FDEs.
|
||||
auto find_cie = [&](i64 offset) {
|
||||
for (i64 i = cies_begin; i < cies.size(); i++)
|
||||
if (cies[i].input_offset == offset)
|
||||
return i;
|
||||
Fatal(ctx) << *isec << ": bad FDE pointer";
|
||||
};
|
||||
|
||||
fdes.emplace_back(begin_offset, rel_begin);
|
||||
for (i64 i = fdes_begin; i < fdes.size(); i++) {
|
||||
i64 cie_offset = *(I32<E> *)(contents.data() + fdes[i].input_offset + 4);
|
||||
fdes[i].cie_idx = find_cie(fdes[i].input_offset + 4 - cie_offset);
|
||||
}
|
||||
}
|
||||
|
||||
// Associate CIEs to FDEs.
|
||||
auto find_cie = [&](i64 offset) {
|
||||
for (i64 i = cies_begin; i < cies.size(); i++)
|
||||
if (cies[i].input_offset == offset)
|
||||
return i;
|
||||
Fatal(ctx) << isec << ": bad FDE pointer";
|
||||
};
|
||||
|
||||
for (i64 i = fdes_begin; i < fdes.size(); i++) {
|
||||
i64 cie_offset = *(I32<E> *)(contents.data() + fdes[i].input_offset + 4);
|
||||
fdes[i].cie_idx = find_cie(fdes[i].input_offset + 4 - cie_offset);
|
||||
}
|
||||
|
||||
auto get_isec = [&](const FdeRecord<E> &fde) -> InputSection<E> * {
|
||||
return get_section(this->elf_syms[rels[fde.rel_idx].r_sym]);
|
||||
auto get_isec = [&](const FdeRecord<E> &fde) {
|
||||
return get_section(this->elf_syms[fde.get_rels(*this)[0].r_sym]);
|
||||
};
|
||||
|
||||
// We assume that FDEs for the same input sections are contiguous
|
||||
// in `fdes` vector.
|
||||
std::stable_sort(fdes.begin() + fdes_begin, fdes.end(),
|
||||
[&](const FdeRecord<E> &a, const FdeRecord<E> &b) {
|
||||
sort(fdes, [&](const FdeRecord<E> &a, const FdeRecord<E> &b) {
|
||||
return get_isec(a)->get_priority() < get_isec(b)->get_priority();
|
||||
});
|
||||
|
||||
// Associate FDEs to input sections.
|
||||
for (i64 i = fdes_begin; i < fdes.size();) {
|
||||
for (i64 i = 0; i < fdes.size();) {
|
||||
InputSection<E> *isec = get_isec(fdes[i]);
|
||||
assert(isec->fde_begin == -1);
|
||||
isec->fde_begin = i++;
|
||||
|
||||
@@ -457,18 +457,6 @@ void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
|
||||
apply_reloc_alloc(ctx, buf);
|
||||
else
|
||||
apply_reloc_nonalloc(ctx, buf);
|
||||
|
||||
if constexpr (is_x86_64<E>) {
|
||||
u8 endbr[] = {0xf3, 0x0f, 0x1e, 0xfa};
|
||||
u8 nop[] = {0x0f, 0x1f, 0x40, 0x00};
|
||||
|
||||
// Rewrite the leading endbr instruction with a nop if the section
|
||||
// is not address-taken.
|
||||
if (ctx.arg.z_rewrite_endbr && (shdr().sh_flags & SHF_EXECINSTR) &&
|
||||
!address_taken && sh_size >= 4 && memcmp(buf, endbr, 4) == 0) {
|
||||
memcpy(buf, nop, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-14
@@ -10,9 +10,6 @@
|
||||
|
||||
namespace mold::elf {
|
||||
|
||||
template <typename E>
|
||||
static thread_local MappedFile<Context<E>> *current_file;
|
||||
|
||||
template <typename E>
|
||||
void read_version_script(Context<E> &ctx, std::span<std::string_view> &tok);
|
||||
|
||||
@@ -37,7 +34,7 @@ template <typename E>
|
||||
class SyntaxError {
|
||||
public:
|
||||
SyntaxError(Context<E> &ctx, std::string_view errpos) : out(ctx) {
|
||||
std::string_view contents = current_file<E>->get_contents();
|
||||
std::string_view contents = ctx.script_file->get_contents();
|
||||
std::string_view line = get_line(contents, errpos.data());
|
||||
|
||||
i64 lineno = 1;
|
||||
@@ -48,7 +45,7 @@ public:
|
||||
i64 column = errpos.data() - line.data();
|
||||
|
||||
std::stringstream ss;
|
||||
ss << current_file<E>->name << ":" << lineno << ": ";
|
||||
ss << ctx.script_file->name << ":" << lineno << ": ";
|
||||
i64 indent = (i64)ss.tellp() + strlen("mold: ");
|
||||
ss << line << "\n" << std::setw(indent + column) << " " << "^ ";
|
||||
out << ss.str();
|
||||
@@ -118,7 +115,7 @@ template <typename E>
|
||||
static std::span<std::string_view>
|
||||
skip(Context<E> &ctx, std::span<std::string_view> tok, std::string_view str) {
|
||||
if (tok.empty())
|
||||
Fatal(ctx) << current_file<E>->name << ": expected '" << str
|
||||
Fatal(ctx) << ctx.script_file->name << ": expected '" << str
|
||||
<< "', but got EOF";
|
||||
if (tok[0] != str)
|
||||
SyntaxError(ctx, tok[0]) << "expected '" << str << "'";
|
||||
@@ -140,7 +137,7 @@ read_output_format(Context<E> &ctx, std::span<std::string_view> tok) {
|
||||
while (!tok.empty() && tok[0] != ")")
|
||||
tok = tok.subspan(1);
|
||||
if (tok.empty())
|
||||
Fatal(ctx) << current_file<E>->name << ": expected ')', but got EOF";
|
||||
Fatal(ctx) << ctx.script_file->name << ": expected ')', but got EOF";
|
||||
return tok.subspan(1);
|
||||
}
|
||||
|
||||
@@ -158,7 +155,7 @@ static MappedFile<Context<E>> *resolve_path(Context<E> &ctx, std::string_view to
|
||||
|
||||
// GNU ld prepends the sysroot if a pathname starts with '/' and the
|
||||
// script being processed is in the sysroot. We do the same.
|
||||
if (str.starts_with('/') && is_in_sysroot(ctx, current_file<E>->name))
|
||||
if (str.starts_with('/') && is_in_sysroot(ctx, ctx.script_file->name))
|
||||
return MappedFile<Context<E>>::must_open(ctx, ctx.arg.sysroot + str);
|
||||
|
||||
if (str.starts_with('=')) {
|
||||
@@ -205,13 +202,13 @@ read_group(Context<E> &ctx, std::span<std::string_view> tok) {
|
||||
}
|
||||
|
||||
if (tok.empty())
|
||||
Fatal(ctx) << current_file<E>->name << ": expected ')', but got EOF";
|
||||
Fatal(ctx) << ctx.script_file->name << ": expected ')', but got EOF";
|
||||
return tok.subspan(1);
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void parse_linker_script(Context<E> &ctx, MappedFile<Context<E>> *mf) {
|
||||
current_file<E> = mf;
|
||||
ctx.script_file = mf;
|
||||
|
||||
std::vector<std::string_view> vec = tokenize(ctx, mf->get_contents());
|
||||
std::span<std::string_view> tok = vec;
|
||||
@@ -241,7 +238,7 @@ void parse_linker_script(Context<E> &ctx, MappedFile<Context<E>> *mf) {
|
||||
template <typename E>
|
||||
std::string_view
|
||||
get_script_output_type(Context<E> &ctx, MappedFile<Context<E>> *mf) {
|
||||
current_file<E> = mf;
|
||||
ctx.script_file = mf;
|
||||
|
||||
std::vector<std::string_view> vec = tokenize(ctx, mf->get_contents());
|
||||
std::span<std::string_view> tok = vec;
|
||||
@@ -313,10 +310,10 @@ 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);
|
||||
} else if (is_global) {
|
||||
ctx.version_patterns.push_back({unquote(tok[0]), current_file<E>->name,
|
||||
ctx.version_patterns.push_back({unquote(tok[0]), ctx.script_file->name,
|
||||
ver_str, ver_idx, is_cpp});
|
||||
} else {
|
||||
ctx.version_patterns.push_back({unquote(tok[0]), current_file<E>->name,
|
||||
ctx.version_patterns.push_back({unquote(tok[0]), ctx.script_file->name,
|
||||
ver_str, VER_NDX_LOCAL, is_cpp});
|
||||
}
|
||||
|
||||
@@ -357,7 +354,7 @@ void read_version_script(Context<E> &ctx, std::span<std::string_view> &tok) {
|
||||
|
||||
template <typename E>
|
||||
void parse_version_script(Context<E> &ctx, MappedFile<Context<E>> *mf) {
|
||||
current_file<E> = mf;
|
||||
ctx.script_file = mf;
|
||||
std::vector<std::string_view> vec = tokenize(ctx, mf->get_contents());
|
||||
std::span<std::string_view> tok = vec;
|
||||
read_version_script(ctx, tok);
|
||||
|
||||
@@ -631,6 +631,9 @@ int elf_main(int argc, char **argv) {
|
||||
// Copy input sections to the output file and apply relocations.
|
||||
copy_chunks(ctx);
|
||||
|
||||
if (ctx.arg.z_rewrite_endbr)
|
||||
rewrite_endbr(ctx);
|
||||
|
||||
// Dynamic linker works better with sorted .rela.dyn section,
|
||||
// so we sort them.
|
||||
ctx.reldyn->sort(ctx);
|
||||
|
||||
@@ -132,3 +132,14 @@ int posix_spawn(pid_t *pid, const char *path,
|
||||
typeof(posix_spawn) *real = dlsym(RTLD_NEXT, "posix_spawn");
|
||||
return real(pid, path, file_actions, attrp, argv, envp);
|
||||
}
|
||||
|
||||
int posix_spawnp(pid_t *pid, const char *file,
|
||||
const posix_spawn_file_actions_t *file_actions,
|
||||
const posix_spawnattr_t *attrp,
|
||||
char *const *argv, char *const *envp) {
|
||||
debug_print("posix_spawnp %s\n", file);
|
||||
if (is_ld(file))
|
||||
file = get_mold_path();
|
||||
typeof(posix_spawnp) *real = dlsym(RTLD_NEXT, "posix_spawnp");
|
||||
return real(pid, file, file_actions, attrp, argv, envp);
|
||||
}
|
||||
+24
-14
@@ -95,12 +95,12 @@ struct SymbolAux<PPC64V1> : SymbolAux<X86_64> {
|
||||
//
|
||||
|
||||
template <typename E>
|
||||
class RangeExtensionThunk {};
|
||||
class Thunk {};
|
||||
|
||||
template <needs_thunk E>
|
||||
class RangeExtensionThunk<E> {
|
||||
class Thunk<E> {
|
||||
public:
|
||||
RangeExtensionThunk(OutputSection<E> &osec, i64 offset)
|
||||
Thunk(OutputSection<E> &osec, i64 offset)
|
||||
: output_section(osec), offset(offset) {}
|
||||
|
||||
i64 size() const { return E::thunk_hdr_size + symbols.size() * E::thunk_size; }
|
||||
@@ -119,7 +119,7 @@ public:
|
||||
std::vector<Symbol<E> *> symbols;
|
||||
};
|
||||
|
||||
struct RangeExtensionRef {
|
||||
struct ThunkRef {
|
||||
i16 thunk_idx = -1;
|
||||
i16 sym_idx = -1;
|
||||
};
|
||||
@@ -212,7 +212,7 @@ struct InputSectionExtras {};
|
||||
|
||||
template <needs_thunk E>
|
||||
struct InputSectionExtras<E> {
|
||||
std::vector<RangeExtensionRef> range_extn;
|
||||
std::vector<ThunkRef> thunk_refs;
|
||||
};
|
||||
|
||||
template <is_riscv E>
|
||||
@@ -462,7 +462,7 @@ public:
|
||||
void create_range_extension_thunks(Context<E> &ctx);
|
||||
|
||||
std::vector<InputSection<E> *> members;
|
||||
std::vector<std::unique_ptr<RangeExtensionThunk<E>>> thunks;
|
||||
std::vector<std::unique_ptr<Thunk<E>>> thunks;
|
||||
std::unique_ptr<RelocSection<E>> reloc_sec;
|
||||
};
|
||||
|
||||
@@ -1183,7 +1183,7 @@ public:
|
||||
std::vector<FdeRecord<E>> fdes;
|
||||
BitVector has_symver;
|
||||
std::vector<ComdatGroupRef<E>> comdat_groups;
|
||||
InputSection<E> *eh_frame_section = nullptr;
|
||||
std::vector<InputSection<E> *> eh_frame_sections;
|
||||
bool exclude_libs = false;
|
||||
std::map<u32, u32> gnu_properties;
|
||||
bool is_lto_obj = false;
|
||||
@@ -1390,6 +1390,7 @@ template <typename E> void create_output_symtab(Context<E> &);
|
||||
template <typename E> void report_undef_errors(Context<E> &);
|
||||
template <typename E> void create_reloc_sections(Context<E> &);
|
||||
template <typename E> void copy_chunks(Context<E> &);
|
||||
template <typename E> void rewrite_endbr(Context<E> &);
|
||||
template <typename E> void apply_version_script(Context<E> &);
|
||||
template <typename E> void parse_symbol_version(Context<E> &);
|
||||
template <typename E> void compute_import_export(Context<E> &);
|
||||
@@ -1607,7 +1608,12 @@ struct ContextExtras<ALPHA> {
|
||||
// resource management, and other miscellaneous objects.
|
||||
template <typename E>
|
||||
struct Context {
|
||||
Context() = default;
|
||||
Context() {
|
||||
arg.entry = get_symbol(*this, "_start");
|
||||
arg.fini = get_symbol(*this, "_fini");
|
||||
arg.init = get_symbol(*this, "_init");
|
||||
}
|
||||
|
||||
Context(const Context<E> &) = delete;
|
||||
|
||||
void checkpoint() {
|
||||
@@ -1624,6 +1630,9 @@ struct Context {
|
||||
CompressKind compress_debug_sections = COMPRESS_NONE;
|
||||
SeparateCodeKind z_separate_code = NOSEPARATE_CODE;
|
||||
ShuffleSectionsKind shuffle_sections = SHUFFLE_SECTIONS_NONE;
|
||||
Symbol<E> *entry = nullptr;
|
||||
Symbol<E> *fini = nullptr;
|
||||
Symbol<E> *init = nullptr;
|
||||
UnresolvedKind unresolved_symbols = UNRESOLVED_ERROR;
|
||||
bool Bsymbolic = false;
|
||||
bool Bsymbolic_functions = false;
|
||||
@@ -1650,6 +1659,7 @@ struct Context {
|
||||
bool ignore_data_address_equality = false;
|
||||
bool is_static = false;
|
||||
bool lto_pass2 = false;
|
||||
bool nmagic = false;
|
||||
bool noinhibit_exec = false;
|
||||
bool oformat_binary = false;
|
||||
bool omagic = false;
|
||||
@@ -1700,6 +1710,7 @@ struct Context {
|
||||
bool z_text = false;
|
||||
i64 filler = -1;
|
||||
i64 spare_dynamic_tags = 5;
|
||||
i64 spare_program_headers = 0;
|
||||
i64 thread_count = 0;
|
||||
i64 z_stack_size = 0;
|
||||
u64 shuffle_sections_seed;
|
||||
@@ -1711,9 +1722,6 @@ struct Context {
|
||||
std::string dependency_file;
|
||||
std::string directory;
|
||||
std::string dynamic_linker;
|
||||
std::string entry = "_start";
|
||||
std::string fini = "_fini";
|
||||
std::string init = "_init";
|
||||
std::string output = "a.out";
|
||||
std::string package_metadata;
|
||||
std::string plugin;
|
||||
@@ -1751,6 +1759,7 @@ struct Context {
|
||||
bool is_static;
|
||||
bool in_lib = false;
|
||||
i64 file_priority = 10000;
|
||||
MappedFile<Context<E>> *script_file = nullptr;
|
||||
std::unordered_set<std::string_view> visited;
|
||||
tbb::task_group tg;
|
||||
|
||||
@@ -2160,6 +2169,9 @@ public:
|
||||
// opposed to IR object).
|
||||
bool referenced_by_regular_obj : 1 = false;
|
||||
|
||||
// For `-z rewrite-endbr`
|
||||
bool address_taken : 1 = false;
|
||||
|
||||
// Target-dependent extra members.
|
||||
[[no_unique_address]] SymbolExtras<E> extra;
|
||||
};
|
||||
@@ -2307,7 +2319,7 @@ InputSection<E>::get_fragment(Context<E> &ctx, const ElfRel<E> &rel) {
|
||||
template <typename E>
|
||||
u64 InputSection<E>::get_thunk_addr(i64 idx) {
|
||||
if constexpr (needs_thunk<E>) {
|
||||
RangeExtensionRef ref = extra.range_extn[idx];
|
||||
ThunkRef ref = extra.thunk_refs[idx];
|
||||
assert(ref.thunk_idx != -1);
|
||||
return output_section->thunks[ref.thunk_idx]->get_addr(ref.sym_idx);
|
||||
}
|
||||
@@ -2405,8 +2417,6 @@ InputFile<E>::get_string(Context<E> &ctx, const ElfShdr<E> &shdr) {
|
||||
|
||||
template <typename E>
|
||||
inline std::string_view InputFile<E>::get_string(Context<E> &ctx, i64 idx) {
|
||||
assert(idx < elf_sections.size());
|
||||
|
||||
if (elf_sections.size() <= idx)
|
||||
Fatal(ctx) << *this << ": invalid section index: " << idx;
|
||||
return this->get_string(ctx, elf_sections[idx]);
|
||||
|
||||
+84
-89
@@ -50,10 +50,9 @@ void OutputEhdr<E>::copy_buf(Context<E> &ctx) {
|
||||
if (ctx.arg.relocatable)
|
||||
return 0;
|
||||
|
||||
if (!ctx.arg.entry.empty())
|
||||
if (Symbol<E> *sym = get_symbol(ctx, ctx.arg.entry);
|
||||
sym->file && !sym->file->is_dso)
|
||||
return sym->get_addr(ctx);
|
||||
if (Symbol<E> &sym = *ctx.arg.entry;
|
||||
sym.file && !sym.file->is_dso)
|
||||
return sym.get_addr(ctx);
|
||||
|
||||
if (OutputSection<E> *osec = find_section(ctx, ".text"))
|
||||
return osec->shdr.sh_addr;
|
||||
@@ -149,11 +148,11 @@ template <typename E>
|
||||
static std::vector<ElfPhdr<E>> create_phdr(Context<E> &ctx) {
|
||||
std::vector<ElfPhdr<E>> vec;
|
||||
|
||||
auto define = [&](u64 type, u64 flags, i64 min_align, Chunk<E> *chunk) {
|
||||
auto define = [&](u64 type, u64 flags, Chunk<E> *chunk) {
|
||||
ElfPhdr<E> phdr = {};
|
||||
phdr.p_type = type;
|
||||
phdr.p_flags = flags;
|
||||
phdr.p_align = std::max<u64>(min_align, chunk->shdr.sh_addralign);
|
||||
phdr.p_align = chunk->shdr.sh_addralign;
|
||||
phdr.p_offset = chunk->shdr.sh_offset;
|
||||
|
||||
if (chunk->shdr.sh_type != SHT_NOBITS)
|
||||
@@ -176,8 +175,7 @@ static std::vector<ElfPhdr<E>> create_phdr(Context<E> &ctx) {
|
||||
};
|
||||
|
||||
auto is_bss = [](Chunk<E> *chunk) {
|
||||
return chunk->shdr.sh_type == SHT_NOBITS &&
|
||||
!(chunk->shdr.sh_flags & SHF_TLS);
|
||||
return chunk->shdr.sh_type == SHT_NOBITS;
|
||||
};
|
||||
|
||||
auto is_tbss = [](Chunk<E> *chunk) {
|
||||
@@ -186,86 +184,86 @@ static std::vector<ElfPhdr<E>> create_phdr(Context<E> &ctx) {
|
||||
};
|
||||
|
||||
auto is_note = [](Chunk<E> *chunk) {
|
||||
ElfShdr<E> &shdr = chunk->shdr;
|
||||
return (shdr.sh_type == SHT_NOTE) && (shdr.sh_flags & SHF_ALLOC);
|
||||
return chunk->shdr.sh_type == SHT_NOTE;
|
||||
};
|
||||
|
||||
// When we are creating PT_LOAD segments, we consider only
|
||||
// the following chunks.
|
||||
std::vector<Chunk<E> *> chunks;
|
||||
for (Chunk<E> *chunk : ctx.chunks)
|
||||
if ((chunk->shdr.sh_flags & SHF_ALLOC) && !is_tbss(chunk))
|
||||
chunks.push_back(chunk);
|
||||
|
||||
// The ELF spec says that "loadable segment entries in the program
|
||||
// header table appear in ascending order, sorted on the p_vaddr
|
||||
// member".
|
||||
sort(chunks, [](Chunk<E> *a, Chunk<E> *b) {
|
||||
return a->shdr.sh_addr < b->shdr.sh_addr;
|
||||
});
|
||||
|
||||
// Create a PT_PHDR for the program header itself.
|
||||
if (ctx.phdr && (ctx.phdr->shdr.sh_flags & SHF_ALLOC))
|
||||
define(PT_PHDR, PF_R, sizeof(Word<E>), ctx.phdr);
|
||||
define(PT_PHDR, PF_R, ctx.phdr);
|
||||
|
||||
// Create a PT_INTERP.
|
||||
if (ctx.interp)
|
||||
define(PT_INTERP, PF_R, 1, ctx.interp);
|
||||
define(PT_INTERP, PF_R, ctx.interp);
|
||||
|
||||
// Create a PT_NOTE for SHF_NOTE sections.
|
||||
for (i64 i = 0, end = ctx.chunks.size(); i < end;) {
|
||||
Chunk<E> *first = ctx.chunks[i++];
|
||||
if (!is_note(first))
|
||||
continue;
|
||||
for (i64 i = 0; i < chunks.size();) {
|
||||
Chunk<E> *first = chunks[i++];
|
||||
if (is_note(first)) {
|
||||
i64 flags = to_phdr_flags(ctx, first);
|
||||
define(PT_NOTE, flags, first);
|
||||
|
||||
i64 flags = to_phdr_flags(ctx, first);
|
||||
i64 alignment = first->shdr.sh_addralign;
|
||||
define(PT_NOTE, flags, alignment, first);
|
||||
|
||||
while (i < end && is_note(ctx.chunks[i]) &&
|
||||
to_phdr_flags(ctx, ctx.chunks[i]) == flags)
|
||||
append(ctx.chunks[i++]);
|
||||
while (i < chunks.size() &&
|
||||
is_note(ctx.chunks[i]) &&
|
||||
to_phdr_flags(ctx, ctx.chunks[i]) == flags)
|
||||
append(ctx.chunks[i++]);
|
||||
}
|
||||
}
|
||||
|
||||
// Create PT_LOAD segments.
|
||||
{
|
||||
i64 idx = vec.size();
|
||||
std::vector<Chunk<E> *> chunks = ctx.chunks;
|
||||
std::erase_if(chunks, is_tbss);
|
||||
for (i64 i = 0; i < chunks.size();) {
|
||||
Chunk<E> *first = chunks[i++];
|
||||
i64 flags = to_phdr_flags(ctx, first);
|
||||
define(PT_LOAD, flags, first);
|
||||
vec.back().p_align = std::max<u64>(ctx.page_size, vec.back().p_align);
|
||||
|
||||
for (i64 i = 0, end = chunks.size(); i < end;) {
|
||||
Chunk<E> *first = chunks[i++];
|
||||
if (!(first->shdr.sh_flags & SHF_ALLOC))
|
||||
continue;
|
||||
|
||||
i64 flags = to_phdr_flags(ctx, first);
|
||||
define(PT_LOAD, flags, ctx.page_size, first);
|
||||
|
||||
// Add contiguous ALLOC sections as long as they have the same
|
||||
// section flags and there's no on-disk gap in between.
|
||||
if (!is_bss(first))
|
||||
while (i < end && !is_bss(chunks[i]) &&
|
||||
to_phdr_flags(ctx, chunks[i]) == flags &&
|
||||
chunks[i]->shdr.sh_offset - first->shdr.sh_offset ==
|
||||
chunks[i]->shdr.sh_addr - first->shdr.sh_addr)
|
||||
append(chunks[i++]);
|
||||
|
||||
while (i < end && is_bss(chunks[i]) &&
|
||||
to_phdr_flags(ctx, chunks[i]) == flags)
|
||||
// Add contiguous ALLOC sections as long as they have the same
|
||||
// section flags and there's no on-disk gap in between.
|
||||
if (!is_bss(first))
|
||||
while (i < chunks.size() &&
|
||||
!is_bss(chunks[i]) &&
|
||||
to_phdr_flags(ctx, chunks[i]) == flags &&
|
||||
chunks[i]->shdr.sh_offset - first->shdr.sh_offset ==
|
||||
chunks[i]->shdr.sh_addr - first->shdr.sh_addr)
|
||||
append(chunks[i++]);
|
||||
}
|
||||
|
||||
// The ELF spec says that "loadable segment entries in the program
|
||||
// header table appear in ascending order, sorted on the p_vaddr
|
||||
// member".
|
||||
std::stable_sort(vec.begin() + idx, vec.end(),
|
||||
[](const ElfPhdr<E> &a, const ElfPhdr<E> &b) {
|
||||
return a.p_vaddr < b.p_vaddr;
|
||||
});
|
||||
while (i < chunks.size() &&
|
||||
is_bss(chunks[i]) &&
|
||||
to_phdr_flags(ctx, chunks[i]) == flags)
|
||||
append(chunks[i++]);
|
||||
}
|
||||
|
||||
// Create a PT_TLS.
|
||||
for (i64 i = 0; i < ctx.chunks.size(); i++) {
|
||||
if (ctx.chunks[i]->shdr.sh_flags & SHF_TLS) {
|
||||
define(PT_TLS, PF_R, 1, ctx.chunks[i++]);
|
||||
while (i < ctx.chunks.size() && (ctx.chunks[i]->shdr.sh_flags & SHF_TLS))
|
||||
for (i64 i = 0; i < ctx.chunks.size();) {
|
||||
Chunk<E> *first = ctx.chunks[i++];
|
||||
if (first->shdr.sh_flags & SHF_TLS) {
|
||||
define(PT_TLS, PF_R, first);
|
||||
while (i < ctx.chunks.size() &&
|
||||
(ctx.chunks[i]->shdr.sh_flags & SHF_TLS))
|
||||
append(ctx.chunks[i++]);
|
||||
}
|
||||
}
|
||||
|
||||
// Add PT_DYNAMIC
|
||||
if (ctx.dynamic && ctx.dynamic->shdr.sh_size)
|
||||
define(PT_DYNAMIC, PF_R | PF_W, 1, ctx.dynamic);
|
||||
define(PT_DYNAMIC, PF_R | PF_W, ctx.dynamic);
|
||||
|
||||
// Add PT_GNU_EH_FRAME
|
||||
if (ctx.eh_frame_hdr)
|
||||
define(PT_GNU_EH_FRAME, PF_R, 1, ctx.eh_frame_hdr);
|
||||
define(PT_GNU_EH_FRAME, PF_R, ctx.eh_frame_hdr);
|
||||
|
||||
// Add PT_GNU_STACK, which is a marker segment that doesn't really
|
||||
// contain any segments. It controls executable bit of stack area.
|
||||
@@ -280,31 +278,31 @@ static std::vector<ElfPhdr<E>> create_phdr(Context<E> &ctx) {
|
||||
|
||||
// Create a PT_GNU_RELRO.
|
||||
if (ctx.arg.z_relro) {
|
||||
for (i64 i = 0; i < ctx.chunks.size(); i++) {
|
||||
if (!ctx.chunks[i]->is_relro)
|
||||
continue;
|
||||
|
||||
define(PT_GNU_RELRO, PF_R, 1, ctx.chunks[i++]);
|
||||
while (i < ctx.chunks.size() && ctx.chunks[i]->is_relro)
|
||||
append(ctx.chunks[i++]);
|
||||
vec.back().p_align = 1;
|
||||
for (i64 i = 0; i < chunks.size();) {
|
||||
Chunk<E> *first = chunks[i++];
|
||||
if (first->is_relro) {
|
||||
define(PT_GNU_RELRO, PF_R, first);
|
||||
while (i < chunks.size() && chunks[i]->is_relro)
|
||||
append(chunks[i++]);
|
||||
vec.back().p_align = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a PT_ARM_EDXIDX
|
||||
if constexpr (is_arm32<E>)
|
||||
if (OutputSection<E> *osec = find_section(ctx, SHT_ARM_EXIDX))
|
||||
define(PT_ARM_EXIDX, PF_R, 4, osec);
|
||||
define(PT_ARM_EXIDX, PF_R, osec);
|
||||
|
||||
// Create a PT_RISCV_ATTRIBUTES
|
||||
if constexpr (is_riscv<E>)
|
||||
if (ctx.extra.riscv_attributes->shdr.sh_size)
|
||||
define(PT_RISCV_ATTRIBUTES, PF_R, 1, ctx.extra.riscv_attributes);
|
||||
define(PT_RISCV_ATTRIBUTES, PF_R, ctx.extra.riscv_attributes);
|
||||
|
||||
// Create a PT_OPENBSD_RANDOMIZE
|
||||
for (Chunk<E> *chunk : ctx.chunks)
|
||||
if (chunk->name == ".openbsd.randomdata")
|
||||
define(PT_OPENBSD_RANDOMIZE, PF_R | PF_W, 1, chunk);
|
||||
define(PT_OPENBSD_RANDOMIZE, PF_R | PF_W, chunk);
|
||||
|
||||
// Set p_paddr if --physical-image-base was given. --physical-image-base
|
||||
// is typically used in embedded programming to specify the base address
|
||||
@@ -352,6 +350,7 @@ static std::vector<ElfPhdr<E>> create_phdr(Context<E> &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
vec.resize(vec.size() + ctx.arg.spare_program_headers);
|
||||
return vec;
|
||||
}
|
||||
|
||||
@@ -763,13 +762,13 @@ static std::vector<Word<E>> create_dynamic_section(Context<E> &ctx) {
|
||||
define(DT_VERDEFNUM, ctx.verdef->shdr.sh_info);
|
||||
}
|
||||
|
||||
if (Symbol<E> *sym = get_symbol(ctx, ctx.arg.init);
|
||||
sym->file && !sym->file->is_dso)
|
||||
define(DT_INIT, sym->get_addr(ctx));
|
||||
if (Symbol<E> &sym = *ctx.arg.init;
|
||||
sym.file && !sym.file->is_dso)
|
||||
define(DT_INIT, sym.get_addr(ctx));
|
||||
|
||||
if (Symbol<E> *sym = get_symbol(ctx, ctx.arg.fini);
|
||||
sym->file && !sym->file->is_dso)
|
||||
define(DT_FINI, sym->get_addr(ctx));
|
||||
if (Symbol<E> &sym = *ctx.arg.fini;
|
||||
sym.file && !sym.file->is_dso)
|
||||
define(DT_FINI, sym.get_addr(ctx));
|
||||
|
||||
if (ctx.hash)
|
||||
define(DT_HASH, ctx.hash->shdr.sh_addr);
|
||||
@@ -925,8 +924,7 @@ void OutputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
|
||||
});
|
||||
|
||||
if constexpr (needs_thunk<E>) {
|
||||
tbb::parallel_for_each(thunks,
|
||||
[&](std::unique_ptr<RangeExtensionThunk<E>> &thunk) {
|
||||
tbb::parallel_for_each(thunks, [&](std::unique_ptr<Thunk<E>> &thunk) {
|
||||
thunk->copy_buf(ctx);
|
||||
});
|
||||
}
|
||||
@@ -1019,7 +1017,7 @@ void OutputSection<E>::compute_symtab_size(Context<E> &ctx) {
|
||||
this->strtab_size = 0;
|
||||
this->num_local_symtab = 0;
|
||||
|
||||
for (std::unique_ptr<RangeExtensionThunk<E>> &thunk : thunks) {
|
||||
for (std::unique_ptr<Thunk<E>> &thunk : thunks) {
|
||||
// For ARM32, we emit additional symbol "$t", "$a" and "$d" for
|
||||
// each thunk to mark the beginning of Thumb code, ARM code and
|
||||
// data, respectively.
|
||||
@@ -1058,7 +1056,7 @@ void OutputSection<E>::populate_symtab(Context<E> &ctx) {
|
||||
esym++;
|
||||
};
|
||||
|
||||
for (std::unique_ptr<RangeExtensionThunk<E>> &thunk : thunks) {
|
||||
for (std::unique_ptr<Thunk<E>> &thunk : thunks) {
|
||||
for (i64 i = 0; i < thunk->symbols.size(); i++) {
|
||||
Symbol<E> &sym = *thunk->symbols[i];
|
||||
u64 addr = thunk->get_addr(i);
|
||||
@@ -1922,14 +1920,11 @@ get_merged_output_name(Context<E> &ctx, std::string_view name, u64 flags,
|
||||
// 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));
|
||||
std::string name2 = ".rodata.str"s + std::to_string(entsize) +
|
||||
"." + std::to_string(addralign);
|
||||
if (name == name2)
|
||||
return name;
|
||||
return save_string(ctx, name2);
|
||||
}
|
||||
|
||||
return name;
|
||||
|
||||
+105
-61
@@ -356,8 +356,8 @@ void kill_eh_frame_sections(Context<E> &ctx) {
|
||||
Timer t(ctx, "kill_eh_frame_sections");
|
||||
|
||||
for (ObjectFile<E> *file : ctx.objs)
|
||||
if (file->eh_frame_section)
|
||||
file->eh_frame_section->is_alive = false;
|
||||
for (InputSection<E> *sec : file->eh_frame_sections)
|
||||
sec->is_alive = false;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -1547,12 +1547,12 @@ void copy_chunks(Context<E> &ctx) {
|
||||
// sections first. This is because REL-type relocation sections (as
|
||||
// opposed to RELA-type) stores relocation addends to target sections.
|
||||
tbb::parallel_for_each(ctx.chunks, [&](Chunk<E> *chunk) {
|
||||
if (chunk->shdr.sh_type != (E::is_rela ? SHT_RELA : SHT_REL))
|
||||
if (chunk->shdr.sh_type != SHT_REL)
|
||||
copy(*chunk);
|
||||
});
|
||||
|
||||
tbb::parallel_for_each(ctx.chunks, [&](Chunk<E> *chunk) {
|
||||
if (chunk->shdr.sh_type == (E::is_rela ? SHT_RELA : SHT_REL))
|
||||
if (chunk->shdr.sh_type == SHT_REL)
|
||||
copy(*chunk);
|
||||
});
|
||||
|
||||
@@ -1566,6 +1566,54 @@ void copy_chunks(Context<E> &ctx) {
|
||||
fixup_arm_exidx_section(ctx);
|
||||
}
|
||||
|
||||
// Rewrite the leading endbr64 instruction with a nop if a function
|
||||
// symbol's address was not taken.
|
||||
template <typename E>
|
||||
void rewrite_endbr(Context<E> &ctx) {
|
||||
Timer t(ctx, "rewrite_endbr");
|
||||
assert(is_x86_64<E>);
|
||||
|
||||
// Compute address-taken bit for each symbol
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
for (std::unique_ptr<InputSection<E>> &isec : file->sections) {
|
||||
if (isec && isec->is_alive && (isec->shdr().sh_flags & SHF_ALLOC)) {
|
||||
for (const ElfRel<E> &rel : isec->get_rels(ctx)) {
|
||||
Symbol<E> &sym = *file->symbols[rel.r_sym];
|
||||
if (!is_func_call_rel(rel) && sym.esym().st_type == STT_FUNC) {
|
||||
std::scoped_lock lock(sym.mu);
|
||||
sym.address_taken = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Some symbols are implicitly address-taken
|
||||
ctx.arg.entry->address_taken = true;
|
||||
ctx.arg.init->address_taken = true;
|
||||
ctx.arg.fini->address_taken = true;
|
||||
|
||||
// Rewrite endbr64 with nop
|
||||
u8 endbr64[] = {0xf3, 0x0f, 0x1e, 0xfa};
|
||||
u8 nop[] = {0x0f, 0x1f, 0x40, 0x00};
|
||||
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
for (Symbol<E> *sym : file->symbols) {
|
||||
if (sym->file == file && sym->esym().st_type == STT_FUNC &&
|
||||
!sym->address_taken) {
|
||||
if (InputSection<E> *isec = sym->get_input_section()) {
|
||||
if (OutputSection<E> *osec = isec->output_section) {
|
||||
u8 *buf = ctx.buf + osec->shdr.sh_offset + isec->offset +
|
||||
sym->value;
|
||||
if (memcmp(buf, endbr64, 4) == 0)
|
||||
memcpy(buf, nop, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void construct_relr(Context<E> &ctx) {
|
||||
Timer t(ctx, "construct_relr");
|
||||
@@ -1598,18 +1646,54 @@ template <typename E>
|
||||
void apply_version_script(Context<E> &ctx) {
|
||||
Timer t(ctx, "apply_version_script");
|
||||
|
||||
auto is_simple = [&] {
|
||||
for (VersionPattern &v : ctx.version_patterns)
|
||||
if (v.is_cpp || v.pattern.find_first_of("*?[") != v.pattern.npos)
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
// Assign versions to symbols specified with `extern "C++"` or
|
||||
// wildcard patterns first.
|
||||
MultiGlob matcher;
|
||||
MultiGlob cpp_matcher;
|
||||
|
||||
// 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) {
|
||||
for (i64 i = 0; i < ctx.version_patterns.size(); i++) {
|
||||
VersionPattern &v = ctx.version_patterns[i];
|
||||
if (v.is_cpp) {
|
||||
if (!cpp_matcher.add(v.pattern, i))
|
||||
Fatal(ctx) << "invalid version pattern: " << v.pattern;
|
||||
} else if (v.pattern.find_first_of("*?[") != v.pattern.npos) {
|
||||
if (!matcher.add(v.pattern, i))
|
||||
Fatal(ctx) << "invalid version pattern: " << v.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;
|
||||
|
||||
std::string_view name = sym->name();
|
||||
i64 match = INT64_MAX;
|
||||
|
||||
if (std::optional<u32> idx = matcher.find(name))
|
||||
match = std::min<i64>(match, *idx);
|
||||
|
||||
// Match non-mangled symbols against the C++ pattern as well.
|
||||
// Weird, but required to match other linkers' behavior.
|
||||
if (!cpp_matcher.empty()) {
|
||||
if (std::optional<std::string_view> s = cpp_demangle(name))
|
||||
name = *s;
|
||||
if (std::optional<u32> idx = cpp_matcher.find(name))
|
||||
match = std::min<i64>(match, *idx);
|
||||
}
|
||||
|
||||
if (match != INT64_MAX)
|
||||
sym->ver_idx = ctx.version_patterns[match].ver_idx;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Next, assign versions to symbols specified by exact name.
|
||||
// In other words, exact matches have higher precedence over
|
||||
// wildcard or `extern "C++"` patterns.
|
||||
for (VersionPattern &v : ctx.version_patterns) {
|
||||
if (!v.is_cpp && v.pattern.find_first_of("*?[") == v.pattern.npos) {
|
||||
Symbol<E> *sym = get_symbol(ctx, v.pattern);
|
||||
|
||||
if (!sym->file && !ctx.arg.undefined_version)
|
||||
@@ -1619,48 +1703,7 @@ void apply_version_script(Context<E> &ctx) {
|
||||
if (sym->file && !sym->file->is_dso)
|
||||
sym->ver_idx = v.ver_idx;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, use glob pattern matchers.
|
||||
MultiGlob matcher;
|
||||
MultiGlob cpp_matcher;
|
||||
|
||||
for (i64 i = 0; i < ctx.version_patterns.size(); i++) {
|
||||
VersionPattern &v = ctx.version_patterns[i];
|
||||
if (v.is_cpp) {
|
||||
if (!cpp_matcher.add(v.pattern, i))
|
||||
Fatal(ctx) << "invalid version pattern: " << v.pattern;
|
||||
} else {
|
||||
if (!matcher.add(v.pattern, i))
|
||||
Fatal(ctx) << "invalid version pattern: " << v.pattern;
|
||||
}
|
||||
}
|
||||
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
for (Symbol<E> *sym : file->get_global_syms()) {
|
||||
if (sym->file != file)
|
||||
continue;
|
||||
|
||||
std::string_view name = sym->name();
|
||||
i64 match = INT64_MAX;
|
||||
|
||||
if (std::optional<u32> idx = matcher.find(name))
|
||||
match = std::min<i64>(match, *idx);
|
||||
|
||||
// Match non-mangled symbols against the C++ pattern as well.
|
||||
// Weird, but required to match other linkers' behavior.
|
||||
if (!cpp_matcher.empty()) {
|
||||
if (std::optional<std::string_view> s = cpp_demangle(name))
|
||||
name = *s;
|
||||
if (std::optional<u32> idx = cpp_matcher.find(name))
|
||||
match = std::min<i64>(match, *idx);
|
||||
}
|
||||
|
||||
if (match != INT64_MAX)
|
||||
sym->ver_idx = ctx.version_patterns[match].ver_idx;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -1907,9 +1950,9 @@ void compute_address_significance(Context<E> &ctx) {
|
||||
};
|
||||
|
||||
// Some symbols' pointer values are leaked to the dynamic section.
|
||||
mark(get_symbol(ctx, ctx.arg.entry));
|
||||
mark(get_symbol(ctx, ctx.arg.init));
|
||||
mark(get_symbol(ctx, ctx.arg.fini));
|
||||
mark(ctx.arg.entry);
|
||||
mark(ctx.arg.init);
|
||||
mark(ctx.arg.fini);
|
||||
|
||||
// Exported symbols are conservatively considered address-taken.
|
||||
if (ctx.dynsym)
|
||||
@@ -2257,7 +2300,7 @@ static void set_virtual_addresses_regular(Context<E> &ctx) {
|
||||
i64 flags1 = get_flags(chunks[i - 1]);
|
||||
i64 flags2 = get_flags(chunks[i]);
|
||||
|
||||
if (flags1 != flags2) {
|
||||
if (!ctx.arg.nmagic && flags1 != flags2) {
|
||||
switch (ctx.arg.z_separate_code) {
|
||||
case SEPARATE_LOADABLE_SEGMENTS:
|
||||
addr = align_to(addr, ctx.page_size);
|
||||
@@ -2844,7 +2887,7 @@ void show_stats(Context<E> &ctx) {
|
||||
static Counter thunk_bytes("thunk_bytes");
|
||||
for (Chunk<E> *chunk : ctx.chunks)
|
||||
if (OutputSection<E> *osec = chunk->to_osec())
|
||||
for (std::unique_ptr<RangeExtensionThunk<E>> &thunk : osec->thunks)
|
||||
for (std::unique_ptr<Thunk<E>> &thunk : osec->thunks)
|
||||
thunk_bytes += thunk->size();
|
||||
}
|
||||
|
||||
@@ -2883,6 +2926,7 @@ template void scan_relocations(Context<E> &);
|
||||
template void report_undef_errors(Context<E> &);
|
||||
template void create_reloc_sections(Context<E> &);
|
||||
template void copy_chunks(Context<E> &);
|
||||
template void rewrite_endbr(Context<E> &);
|
||||
template void construct_relr(Context<E> &);
|
||||
template void create_output_symtab(Context<E> &);
|
||||
template void apply_version_script(Context<E> &);
|
||||
|
||||
+15
-18
@@ -119,7 +119,7 @@ static bool is_reachable(Context<E> &ctx, InputSection<E> &isec,
|
||||
return -max_distance() <= val && val < max_distance();
|
||||
}
|
||||
|
||||
static void reset_thunk(RangeExtensionThunk<E> &thunk) {
|
||||
static void reset_thunk(Thunk<E> &thunk) {
|
||||
for (Symbol<E> *sym : thunk.symbols) {
|
||||
sym->extra.thunk_idx = -1;
|
||||
sym->extra.thunk_sym_idx = -1;
|
||||
@@ -129,10 +129,10 @@ static void reset_thunk(RangeExtensionThunk<E> &thunk) {
|
||||
|
||||
// Scan relocations to collect symbols that need thunks.
|
||||
static void scan_rels(Context<E> &ctx, InputSection<E> &isec,
|
||||
RangeExtensionThunk<E> &thunk, i64 thunk_idx) {
|
||||
Thunk<E> &thunk, i64 thunk_idx) {
|
||||
std::span<const ElfRel<E>> rels = isec.get_rels(ctx);
|
||||
std::vector<RangeExtensionRef> &range_extn = isec.extra.range_extn;
|
||||
range_extn.resize(rels.size());
|
||||
std::vector<ThunkRef> &thunk_refs = isec.extra.thunk_refs;
|
||||
thunk_refs.resize(rels.size());
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
@@ -151,15 +151,15 @@ static void scan_rels(Context<E> &ctx, InputSection<E> &isec,
|
||||
// This relocation needs a thunk. If the symbol is already in a
|
||||
// previous thunk, reuse it.
|
||||
if (sym.extra.thunk_idx != -1) {
|
||||
range_extn[i].thunk_idx = sym.extra.thunk_idx;
|
||||
range_extn[i].sym_idx = sym.extra.thunk_sym_idx;
|
||||
thunk_refs[i].thunk_idx = sym.extra.thunk_idx;
|
||||
thunk_refs[i].sym_idx = sym.extra.thunk_sym_idx;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, add the symbol to the current thunk if it's not
|
||||
// added already.
|
||||
range_extn[i].thunk_idx = thunk_idx;
|
||||
range_extn[i].sym_idx = -1;
|
||||
thunk_refs[i].thunk_idx = thunk_idx;
|
||||
thunk_refs[i].sym_idx = -1;
|
||||
|
||||
if (sym.flags.exchange(-1) == 0) {
|
||||
std::scoped_lock lock(thunk.mu);
|
||||
@@ -170,8 +170,6 @@ static void scan_rels(Context<E> &ctx, InputSection<E> &isec,
|
||||
|
||||
template <>
|
||||
void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
|
||||
using Thunk = RangeExtensionThunk<E>;
|
||||
|
||||
std::span<InputSection<E> *> m = members;
|
||||
if (m.empty())
|
||||
return;
|
||||
@@ -179,9 +177,8 @@ void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
|
||||
// Initialize input sections with a dummy offset so that we can
|
||||
// distinguish sections that have got an address with the one who
|
||||
// haven't.
|
||||
tbb::parallel_for_each(m, [](InputSection<E> *isec) {
|
||||
for (InputSection<E> *isec : m)
|
||||
isec->offset = -1;
|
||||
});
|
||||
|
||||
// We create thunks from the beginning of the section to the end.
|
||||
// We manage progress using four offsets which increase monotonically.
|
||||
@@ -215,7 +212,7 @@ void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
|
||||
// Move D foward as far as we can jump from B to a thunk at D.
|
||||
auto d_thunk_end = [&] {
|
||||
u64 d_end = align_to(offset, 1 << m[d]->p2align) + m[d]->sh_size;
|
||||
return align_to(d_end, Thunk::alignment) + max_thunk_size;
|
||||
return align_to(d_end, Thunk<E>::alignment) + max_thunk_size;
|
||||
};
|
||||
|
||||
while (d < m.size() &&
|
||||
@@ -243,9 +240,9 @@ void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
|
||||
reset_thunk(*thunks[t++]);
|
||||
|
||||
// Create a new thunk and place it at D.
|
||||
offset = align_to(offset, Thunk::alignment);
|
||||
offset = align_to(offset, Thunk<E>::alignment);
|
||||
i64 thunk_idx = thunks.size();
|
||||
Thunk *thunk = new Thunk(*this, offset);
|
||||
Thunk<E> *thunk = new Thunk<E>(*this, offset);
|
||||
thunks.emplace_back(thunk);
|
||||
|
||||
// Scan relocations between B and C to collect symbols that need
|
||||
@@ -278,11 +275,11 @@ void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
|
||||
[&](InputSection<E> *isec) {
|
||||
std::span<Symbol<E> *> syms = isec->file.symbols;
|
||||
std::span<const ElfRel<E>> rels = isec->get_rels(ctx);
|
||||
std::span<RangeExtensionRef> range_extn = isec->extra.range_extn;
|
||||
std::span<ThunkRef> thunk_refs = isec->extra.thunk_refs;
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++)
|
||||
if (range_extn[i].thunk_idx == thunk_idx)
|
||||
range_extn[i].sym_idx = syms[rels[i].r_sym]->extra.thunk_sym_idx;
|
||||
if (thunk_refs[i].thunk_idx == thunk_idx)
|
||||
thunk_refs[i].sym_idx = syms[rels[i].r_sym]->extra.thunk_sym_idx;
|
||||
});
|
||||
|
||||
// Move B forward to point to the begining of the next batch.
|
||||
|
||||
@@ -20,7 +20,7 @@ ubuntu-* | pop-* | linuxmint-* | debian-* | raspbian-*)
|
||||
apt-get install -y cmake gcc g++
|
||||
apt-get install -y file
|
||||
;;
|
||||
fedora-*)
|
||||
fedora-* | amzn-*)
|
||||
dnf install -y gcc-g++ cmake
|
||||
dnf install -y glibc-static file libstdc++-static diffutils util-linux
|
||||
;;
|
||||
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
nm mold | grep -q '__tsan_init' && skip
|
||||
|
||||
[ $MACHINE = m68k ] && skip
|
||||
[ $MACHINE = sh4 ] && skip
|
||||
|
||||
cat <<EOF | $CXX -o $t/a.o -c -xc++ -
|
||||
int foo() {
|
||||
try {
|
||||
throw 1;
|
||||
} catch (int x) {
|
||||
return x;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CXX -o $t/b.o -c -xc++ -
|
||||
int bar() {
|
||||
try {
|
||||
throw 3;
|
||||
} catch (int x) {
|
||||
return x;
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
EOF
|
||||
|
||||
$OBJCOPY --rename-section .eh_frame=.eh_frame2 $t/a.o
|
||||
./mold -r -o $t/c.o $t/a.o $t/b.o
|
||||
$OBJCOPY --rename-section .eh_frame2=.eh_frame $t/c.o
|
||||
|
||||
cat <<EOF | $CXX -o $t/d.o -c -xc++ -
|
||||
#include <stdio.h>
|
||||
|
||||
int foo();
|
||||
int bar();
|
||||
|
||||
int main() {
|
||||
printf("%d %d\n", foo(), bar());
|
||||
}
|
||||
EOF
|
||||
|
||||
$CXX -B. -o $t/exe1 $t/d.o $t/c.o
|
||||
$QEMU $t/exe1
|
||||
$QEMU $t/exe1 | grep -q '^1 3$'
|
||||
File renamed without changes.
Executable
+48
@@ -0,0 +1,48 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
supports_tlsdesc || skip
|
||||
|
||||
cat <<'EOF' | $GCC -c -o $t/a.o -xassembler -
|
||||
.globl get_foo
|
||||
.type get_foo, @function
|
||||
get_foo:
|
||||
pushl %ebx
|
||||
call __x86.get_pc_thunk.bx
|
||||
addl $_GLOBAL_OFFSET_TABLE_, %ebx
|
||||
subl $8, %esp
|
||||
leal foo@TLSDESC(%ebx), %ebx
|
||||
movl %ebx, %eax
|
||||
call *foo@TLSCALL(%eax)
|
||||
movl %gs:(%eax), %eax
|
||||
addl $8, %esp
|
||||
popl %ebx
|
||||
ret
|
||||
EOF
|
||||
|
||||
cat <<EOF | $GCC -fPIC -c -o $t/b.o -xc - $tlsdesc_opt
|
||||
#include <stdio.h>
|
||||
|
||||
_Thread_local int foo;
|
||||
|
||||
int get_foo();
|
||||
|
||||
int main() {
|
||||
foo = 42;
|
||||
printf("%d\n", get_foo());
|
||||
}
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe1 $t/a.o $t/b.o
|
||||
$QEMU $t/exe1 | grep -q 42
|
||||
|
||||
$CC -B. -o $t/exe2 $t/a.o $t/b.o -Wl,-no-relax
|
||||
$QEMU $t/exe2 | grep -q 42
|
||||
|
||||
$CC -B. -shared -o $t/c.so $t/a.o
|
||||
$CC -B. -o $t/exe3 $t/b.o $t/c.so
|
||||
$QEMU $t/exe3 | grep -q 42
|
||||
|
||||
$CC -B. -shared -o $t/c.so $t/a.o -Wl,-no-relax
|
||||
$CC -B. -o $t/exe4 $t/b.o $t/c.so -Wl,-no-relax
|
||||
$QEMU $t/exe4 | grep -q 42
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc -
|
||||
void _start() {}
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe1 $t/a.o -nostdlib -Wl,-nmagic
|
||||
$CC -B. -o $t/exe2 $t/a.o -nostdlib
|
||||
|
||||
end1=$(nm $t/exe1 | grep ' end$' | cut -d' ' -f1)
|
||||
end2=$(nm $t/exe2 | grep ' end$' | cut -d' ' -f1)
|
||||
|
||||
[ $((0x$end1)) -lt $((0x$end2)) ]
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc -
|
||||
#include <stdio.h>
|
||||
int main() {
|
||||
printf("Hello world\n");
|
||||
}
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe1 $t/a.o
|
||||
$QEMU $t/exe1 | grep -q 'Hello world'
|
||||
[ "$(readelf -Wl $t/exe1 | grep NULL | wc -l)" -eq 0 ]
|
||||
|
||||
$CC -B. -o $t/exe2 $t/a.o -Wl,--spare-program-headers=0
|
||||
$QEMU $t/exe2 | grep -q 'Hello world'
|
||||
[ "$(readelf -Wl $t/exe2 | grep NULL | wc -l)" -eq 0 ]
|
||||
|
||||
$CC -B. -o $t/exe3 $t/a.o -Wl,--spare-program-headers=1
|
||||
$QEMU $t/exe3 | grep -q 'Hello world'
|
||||
[ "$(readelf -Wl $t/exe3 | grep NULL | wc -l)" -eq 1 ]
|
||||
|
||||
$CC -B. -o $t/exe4 $t/a.o -Wl,--spare-program-headers=5
|
||||
$QEMU $t/exe4 | grep -q 'Hello world'
|
||||
[ "$(readelf -Wl $t/exe4 | grep NULL | wc -l)" -eq 5 ]
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc -
|
||||
__thread char foo;
|
||||
|
||||
__attribute__((section(".data.rel.ro.bar"), aligned(16*1024)))
|
||||
char bar;
|
||||
|
||||
int main() {}
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe $t/a.o
|
||||
$QEMU $t/exe
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<'EOF' > $t/a.ver
|
||||
VER1 { foo*; };
|
||||
VER2 { foo_x; };
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/b.o -xc -
|
||||
void foo_x() {}
|
||||
void foo_y() {}
|
||||
void foo_z() {}
|
||||
EOF
|
||||
|
||||
$CC -B. -shared -Wl,--version-script=$t/a.ver -o $t/c.so $t/b.o
|
||||
readelf -W --dyn-syms $t/c.so > $t/log
|
||||
grep -Fq 'foo_x@@VER2' $t/log
|
||||
grep -Fq 'foo_y@@VER1' $t/log
|
||||
grep -Fq 'foo_z@@VER1' $t/log
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<'EOF' > $t/a.ver
|
||||
VER1 { foo_x; };
|
||||
VER2 { foo*; };
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/b.o -xc -
|
||||
void foo_x() {}
|
||||
void foo_y() {}
|
||||
void foo_z() {}
|
||||
EOF
|
||||
|
||||
$CC -B. -shared -Wl,--version-script=$t/a.ver -o $t/c.so $t/b.o
|
||||
readelf -W --dyn-syms $t/c.so > $t/log
|
||||
grep -Fq 'foo_x@@VER1' $t/log
|
||||
grep -Fq 'foo_y@@VER2' $t/log
|
||||
grep -Fq 'foo_z@@VER2' $t/log
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
[ $MACHINE = x86_64 ] || skip
|
||||
test_cflags -fcf-protection || skip
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc - -fno-function-sections -O -fcf-protection
|
||||
int foo() { return 3; }
|
||||
int bar() { return foo(); }
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -o $t/b.o -c -xc - -fno-function-sections -O -fcf-protection
|
||||
int main() {}
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe1 $t/a.o $t/b.o
|
||||
$OBJDUMP -dr $t/exe1 > $t/log1
|
||||
|
||||
grep -A1 '<foo>:' $t/log1 | grep -q endbr64
|
||||
grep -A1 '<bar>:' $t/log1 | grep -q endbr64
|
||||
grep -A1 '<main>:' $t/log1 | grep -q endbr64
|
||||
|
||||
$CC -B. -o $t/exe2 $t/a.o $t/b.o -Wl,-z,rewrite-endbr
|
||||
$OBJDUMP -dr $t/exe2 > $t/log2
|
||||
|
||||
grep -A1 '<foo>:' $t/log2 | grep -q nop
|
||||
grep -A1 '<bar>:' $t/log2 | grep -q nop
|
||||
grep -A1 '<main>:' $t/log2 | grep -q endbr64
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
supports_tlsdesc || skip
|
||||
|
||||
cat <<EOF | $GCC -c -o $t/a.o -xassembler -
|
||||
.globl get_foo
|
||||
.type get_foo, @function
|
||||
get_foo:
|
||||
pushq %rbp
|
||||
movq %rsp, %rbp
|
||||
leaq foo@TLSDESC(%rip), %rbx
|
||||
movq %rbx, %rax
|
||||
call *foo@TLSCALL(%rax)
|
||||
movq %fs:0, %rdx
|
||||
addq %rdx, %rax
|
||||
movl (%rax), %eax
|
||||
popq %rbp
|
||||
ret
|
||||
EOF
|
||||
|
||||
cat <<EOF | $GCC -fPIC -c -o $t/b.o -xc - $tlsdesc_opt
|
||||
#include <stdio.h>
|
||||
|
||||
_Thread_local int foo;
|
||||
|
||||
int get_foo();
|
||||
|
||||
int main() {
|
||||
foo = 42;
|
||||
printf("%d\n", get_foo());
|
||||
}
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe1 $t/a.o $t/b.o
|
||||
$QEMU $t/exe1 | grep -q 42
|
||||
|
||||
$CC -B. -o $t/exe2 $t/a.o $t/b.o -Wl,-no-relax
|
||||
$QEMU $t/exe2 | grep -q 42
|
||||
|
||||
$CC -B. -shared -o $t/c.so $t/a.o
|
||||
$CC -B. -o $t/exe3 $t/b.o $t/c.so
|
||||
$QEMU $t/exe3 | grep -q 42
|
||||
|
||||
$CC -B. -shared -o $t/c.so $t/a.o -Wl,-no-relax
|
||||
$CC -B. -o $t/exe4 $t/b.o $t/c.so -Wl,-no-relax
|
||||
$QEMU $t/exe4 | grep -q 42
|
||||
Reference in new issue
Block a user