mirror of
https://github.com/rui314/mold.git
synced 2026-11-08 17:48:41 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57f5c77174 | ||
|
|
ac6f1ec18e | ||
|
|
d19f822ec0 | ||
|
|
a97a628ee8 | ||
|
|
2888aade4c | ||
|
|
0172f22ab5 | ||
|
|
418d4ff62a | ||
|
|
5249edc57f | ||
|
|
1870845f88 | ||
|
|
d15c41a8dd | ||
|
|
b7c8a843b2 | ||
|
|
9b77a7ea86 | ||
|
|
af396ad5d9 | ||
|
|
a2ee7371d5 | ||
|
|
c9812cf09f | ||
|
|
e9c070a3c2 | ||
|
|
02e75be9fa | ||
|
|
d5609a9bb8 | ||
|
|
5d24db5a64 | ||
|
|
aeec8baee5 | ||
|
|
b74a771886 | ||
|
|
0124058ef7 | ||
|
|
e302a2509c | ||
|
|
f70db66373 | ||
|
|
9c0cbc8e6c | ||
|
|
a9acbd6b6f | ||
|
|
b9d65a5787 | ||
|
|
50766f1ad4 | ||
|
|
3496ad1291 | ||
|
|
8a3d374251 | ||
|
|
96b2710849 | ||
|
|
a43f3953f0 | ||
|
|
2bc6ddc41c | ||
|
|
f6575d53c1 | ||
|
|
bf1ec5ac8f | ||
|
|
b291100cfe | ||
|
|
c43a859a90 | ||
|
|
6e3d0e3a3c | ||
|
|
790ce3f64d | ||
|
|
cbd7f74c2e | ||
|
|
3fe25fc69d | ||
|
|
823ef09585 | ||
|
|
f20f57060a | ||
|
|
0061d53409 | ||
|
|
82affb94d8 | ||
|
|
4f782fb7dc | ||
|
|
e9eb6020a6 | ||
|
|
23a662cbb9 | ||
|
|
32c8f6dd61 | ||
|
|
c95476d2fe | ||
|
|
3981f4f3d4 | ||
|
|
d4ad627919 | ||
|
|
befe780cf8 | ||
|
|
879fe682ee | ||
|
|
805a8b2b62 | ||
|
|
ec360760d6 | ||
|
|
30541fc1e8 | ||
|
|
9ebd889a73 | ||
|
|
f15bd7537e | ||
|
|
f2ac49824c | ||
|
|
716fb9c06d | ||
|
|
50231607f9 | ||
|
|
fb7a650186 | ||
|
|
a399548dfa | ||
|
|
11a62121e3 | ||
|
|
a067b0106b | ||
|
|
7607ad6371 | ||
|
|
eab8217962 | ||
|
|
f46959819b | ||
|
|
a17202d2b5 | ||
|
|
83152acc8b | ||
|
|
88ea3a1aa5 | ||
|
|
0302affc11 | ||
|
|
34986d26df | ||
|
|
1fbbcec3da | ||
|
|
9ae7e700fa | ||
|
|
f496c31008 | ||
|
|
48a84176ac | ||
|
|
7fe4bee499 | ||
|
|
015d5388a0 | ||
|
|
9306f69a61 | ||
|
|
b82028d45d | ||
|
|
123faa82dc | ||
|
|
93f540a10a | ||
|
|
143d447a66 | ||
|
|
8b089859ee | ||
|
|
4b754fd944 | ||
|
|
b9f938e801 | ||
|
|
6e50f3a5e7 | ||
|
|
c2df589e05 | ||
|
|
247ae9500c | ||
|
|
85e8d4179e | ||
|
|
9185ee4b05 | ||
|
|
16f75990f9 | ||
|
|
34053cc52e | ||
|
|
810946130d | ||
|
|
33b6f3dee9 | ||
|
|
14a276ecde | ||
|
|
245d265b4b | ||
|
|
0619541234 | ||
|
|
8f57d2affd | ||
|
|
501f66b24c | ||
|
|
6ef9ffb81a | ||
|
|
9ab2c31086 | ||
|
|
182bd7b68c | ||
|
|
09e205d11e | ||
|
|
7e5e396aae | ||
|
|
b8914d0a94 | ||
|
|
359e5134c9 | ||
|
|
4ed6f48f55 | ||
|
|
48beed9fdf | ||
|
|
42cc55e6d9 | ||
|
|
04103ae60a | ||
|
|
0966f1858c | ||
|
|
56748ed199 | ||
|
|
11387b3c19 | ||
|
|
04d71affd1 | ||
|
|
6cb2f31236 | ||
|
|
01392bd53a | ||
|
|
7cb0b817f5 | ||
|
|
ea38c1544f | ||
|
|
80a2d24948 | ||
|
|
ded9292ba9 | ||
|
|
6d1c96f34b | ||
|
|
0849789fd5 | ||
|
|
7f771d6ed9 | ||
|
|
b8a46ede02 | ||
|
|
bab7dd1b2e | ||
|
|
ebe887f0ae | ||
|
|
df30516630 | ||
|
|
e8101a4d8a | ||
|
|
6291f1b86c | ||
|
|
a570e8953c | ||
|
|
5cc0e9d0a1 | ||
|
|
4bb8e03b24 | ||
|
|
bcadb4e30f | ||
|
|
a2eda6acc4 | ||
|
|
e60e04feb2 | ||
|
|
3651dba6ef | ||
|
|
c20d027441 | ||
|
|
66b5ed66c6 | ||
|
|
0b747d8334 | ||
|
|
73f86aeadd | ||
|
|
ceee495584 | ||
|
|
3d89c1181b | ||
|
|
f014a1d015 | ||
|
|
f25e450f2b | ||
|
|
1fbc6f3dec | ||
|
|
4679573654 | ||
|
|
af608f5651 | ||
|
|
5a9c465210 | ||
|
|
1822e47a43 | ||
|
|
06f20e9d69 | ||
|
|
8929bd4a8d | ||
|
|
3b1e2a9aa3 | ||
|
|
721eafc7dd | ||
|
|
4de8daf34f | ||
|
|
f7624ee1f9 | ||
|
|
500416f8f6 | ||
|
|
c6c12d43f5 | ||
|
|
7fc2a6e935 | ||
|
|
9fe8831db3 | ||
|
|
2bb42badde | ||
|
|
48077dd91a | ||
|
|
cf0d750734 | ||
|
|
48dd767ad3 | ||
|
|
3c89ad66a2 | ||
|
|
8cd5574bfb | ||
|
|
0ca543e104 | ||
|
|
e5f80fd822 | ||
|
|
fe8e061427 | ||
|
|
05ceb7da0d | ||
|
|
3cec2e68be | ||
|
|
12a141b03b | ||
|
|
562361345b | ||
|
|
432ffeabf5 | ||
|
|
be0870719f | ||
|
|
a66750b1ec | ||
|
|
c3a957c4dc | ||
|
|
f193d2b603 | ||
|
|
d2859f597c | ||
|
|
06a21efbdb | ||
|
|
13aa1685c9 | ||
|
|
55fcd3facf | ||
|
|
5d454308f2 | ||
|
|
a01fe171d0 | ||
|
|
207c4e99f0 | ||
|
|
0dfdc014e2 | ||
|
|
eec3f6b716 | ||
|
|
a483530a3c | ||
|
|
0d1068e460 | ||
|
|
fd0fc9a3cf | ||
|
|
40b881b7bb | ||
|
|
35cde381b7 | ||
|
|
6d8c5ca90b | ||
|
|
aa4d065fde | ||
|
|
0af930ae28 | ||
|
|
d29fabf084 | ||
|
|
2ee1d47ca8 | ||
|
|
8e27616934 | ||
|
|
a7b8c6061a | ||
|
|
aef5f3231c | ||
|
|
38ae2990a0 | ||
|
|
e6345d5b14 | ||
|
|
ba35344bd0 | ||
|
|
c33ca0b001 | ||
|
|
77935f1807 | ||
|
|
3234d88600 | ||
|
|
1e8cee85bb | ||
|
|
2352ec647d | ||
|
|
88d5e74825 | ||
|
|
d884620f9d | ||
|
|
e55363ec8a | ||
|
|
a297859c53 | ||
|
|
0a112d0aa9 | ||
|
|
6a342f2c64 | ||
|
|
d0bcd35bf5 | ||
|
|
f609510e3c | ||
|
|
9fc0ace817 | ||
|
|
89f3ba89da | ||
|
|
09e7616186 | ||
|
|
2342938cea | ||
|
|
047675b762 | ||
|
|
531e43eddf | ||
|
|
50a75c25e3 | ||
|
|
7a69bd45ac | ||
|
|
006de74706 | ||
|
|
952fdde7ae | ||
|
|
dfce2fc8fa | ||
|
|
50fd4beb3e | ||
|
|
0b8090994b | ||
|
|
37e6d2f4cb | ||
|
|
0c06bef82b | ||
|
|
53dbfd96de | ||
|
|
938f145150 | ||
|
|
930ba5a7f1 | ||
|
|
4107c336a8 | ||
|
|
d4db31ceaf | ||
|
|
30f68106db | ||
|
|
e56fc35820 | ||
|
|
b8fa19bf24 | ||
|
|
d9bd1468d0 | ||
|
|
b5dfab17da | ||
|
|
7d1c970a39 | ||
|
|
e6e1cfac31 | ||
|
|
38bca63747 | ||
|
|
606ce43c5b | ||
|
|
41413f9d7c | ||
|
|
ec1d7120aa | ||
|
|
bcce7091e7 | ||
|
|
e3cb2aa09e | ||
|
|
7e7d75fb4b | ||
|
|
e333e435bd | ||
|
|
afd2fab630 | ||
|
|
26493c0ca8 | ||
|
|
9402ca357c | ||
|
|
5572679c2d | ||
|
|
179d5d4b5c | ||
|
|
f169f1c45c | ||
|
|
e05f3b5362 | ||
|
|
a5b3a22517 | ||
|
|
82263dce4d | ||
|
|
1a012dda4d | ||
|
|
dfe02f41cb | ||
|
|
b582ad3589 | ||
|
|
2d7b6b23e3 | ||
|
|
7ddc8f4654 | ||
|
|
4bbd93f06f | ||
|
|
a76d259c0b | ||
|
|
a557ea1012 | ||
|
|
06f6a56043 | ||
|
|
ac9568f17b | ||
|
|
9c9145eb6e | ||
|
|
cd7287f0de |
No files matched your search
@@ -9,11 +9,18 @@ on:
|
||||
|
||||
jobs:
|
||||
build-tarballs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
target: [ x86_64, aarch64, arm, riscv64, ppc64le, s390x ]
|
||||
include:
|
||||
- { target: x86_64, os: ubuntu-24.04 }
|
||||
- { target: aarch64, os: ubuntu-24.04-arm }
|
||||
- { target: arm, os: ubuntu-24.04-arm }
|
||||
- { target: riscv64, os: ubuntu-24.04 }
|
||||
- { target: ppc64le, os: ubuntu-24.04 }
|
||||
- { target: s390x, os: ubuntu-24.04 }
|
||||
- { target: loongarch64, os: ubuntu-24.04 }
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -21,21 +28,18 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Podman
|
||||
run: sudo apt-get update && sudo apt-get install -y podman qemu-user-static
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v1
|
||||
uses: redhat-actions/podman-login@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 }}
|
||||
|
||||
@@ -43,4 +47,5 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.target }}
|
||||
path: mold-*.tar.gz
|
||||
path: dist/mold-*.tar.gz
|
||||
compression-level: 0
|
||||
@@ -0,0 +1,44 @@
|
||||
name: Build native tarballs
|
||||
|
||||
on:
|
||||
push:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-tarballs:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- { target: x86_64, os: ubuntu-24.04 }
|
||||
- { target: aarch64, os: ubuntu-24.04-arm }
|
||||
- { target: arm, os: ubuntu-24.04-arm }
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Podman
|
||||
run: sudo apt-get update && sudo apt-get install -y podman qemu-user-static
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: redhat-actions/podman-login@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build a tarball
|
||||
run: ./dist.sh ${{ matrix.target }}
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.target }}
|
||||
path: dist/mold-*.tar.gz
|
||||
compression-level: 0
|
||||
@@ -1,37 +0,0 @@
|
||||
name: Build x86 tarball
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
workflow_dispatch:
|
||||
|
||||
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@v4
|
||||
with:
|
||||
name: tarball
|
||||
path: mold-*.tar.gz
|
||||
+28
-21
@@ -14,7 +14,7 @@ jobs:
|
||||
- '-DMOLD_USE_TSAN=On'
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: rui314/setup-mold@staging
|
||||
- run: sudo ./install-build-deps.sh
|
||||
- name: build
|
||||
@@ -24,9 +24,9 @@ jobs:
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=clang-18 -DCMAKE_CXX_COMPILER=clang++-18 ${{ matrix.target }} ..
|
||||
cmake --build . -j$(nproc)
|
||||
- run: cd build; ctest -j$(nproc)
|
||||
- run: cd build; ctest --output-on-failure -j$(nproc)
|
||||
- name: archive test results
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: test-results-clang
|
||||
@@ -35,29 +35,26 @@ jobs:
|
||||
!build/CMakeFiles
|
||||
|
||||
build-multi-archs:
|
||||
runs-on: ubuntu-latest
|
||||
container: gcc:11.1.0
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: install-build-deps
|
||||
shell: bash
|
||||
run: |
|
||||
# Install cross toolchains
|
||||
./install-build-deps.sh
|
||||
./install-cross-tools.sh
|
||||
.github/workflows/install-extras.sh
|
||||
sudo ./install-build-deps.sh
|
||||
sudo ./install-cross-tools.sh
|
||||
sudo .github/workflows/install-extras.sh
|
||||
- name: build
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
cmake --build . -j$(nproc)
|
||||
- run: cd build; ctest -j$(nproc)
|
||||
- run: cd build; ctest --output-on-failure -j$(nproc)
|
||||
- name: archive test results
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: test-results-gcc
|
||||
name: test-results-multi-archs
|
||||
path: |
|
||||
build
|
||||
!build/CMakeFiles
|
||||
@@ -68,14 +65,16 @@ jobs:
|
||||
distro:
|
||||
- alpine
|
||||
- archlinux
|
||||
- debian:11 # GCC 10 and CMake 3.18 - the minimum supported by mold
|
||||
- fedora
|
||||
- gentoo/stage3
|
||||
- opensuse/tumbleweed
|
||||
- ubuntu:22.04
|
||||
- ubuntu:25.04
|
||||
runs-on: ubuntu-latest
|
||||
container: ${{ matrix.distro }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- run: ./install-build-deps.sh
|
||||
- name: build
|
||||
run: |
|
||||
@@ -83,12 +82,20 @@ jobs:
|
||||
cd build
|
||||
cmake ..
|
||||
cmake --build . -j$(nproc)
|
||||
- run: cd build; ctest -j$(nproc)
|
||||
- run: cd build; ctest --output-on-failure -j$(nproc)
|
||||
- name: archive test results
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: test-results-${{ matrix.distro }}
|
||||
path: |
|
||||
build
|
||||
!build/CMakeFiles
|
||||
|
||||
build-macos:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: build
|
||||
run: |
|
||||
mkdir build
|
||||
@@ -99,7 +106,7 @@ jobs:
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: build
|
||||
run: |
|
||||
mkdir build
|
||||
@@ -110,7 +117,7 @@ jobs:
|
||||
build-msys:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup MSYS2
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
@@ -128,7 +135,7 @@ jobs:
|
||||
build-freebsd:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build and test
|
||||
uses: vmactions/freebsd-vm@v1
|
||||
with:
|
||||
@@ -139,4 +146,4 @@ jobs:
|
||||
cd build
|
||||
cmake ..
|
||||
cmake --build . -j$(nproc)
|
||||
ctest -j$(nproc)
|
||||
ctest --output-on-failure -j$(nproc)
|
||||
@@ -5,7 +5,7 @@ apt-get install -y wget xz-utils
|
||||
|
||||
# Install a 32-bit RISC-V toolchain
|
||||
mkdir /rv32
|
||||
wget -O- -q https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2023.07.07/riscv32-glibc-ubuntu-20.04-gcc-nightly-2023.07.07-nightly.tar.gz | tar -C /rv32 --strip-components=1 -xzf -
|
||||
wget -O- -q https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2025.01.20/riscv32-glibc-ubuntu-22.04-gcc-nightly-2025.01.20-nightly.tar.xz | tar -C /rv32 --strip-components=1 --xz -xf -
|
||||
|
||||
ln -sf /rv32/sysroot /usr/riscv32-linux-gnu
|
||||
echo '/rv32/bin/riscv32-unknown-linux-gnu-gcc -L/usr/riscv32-linux-gnu "$@"' > /usr/bin/riscv32-linux-gnu-gcc
|
||||
@@ -18,7 +18,7 @@ done
|
||||
|
||||
# Install a LoongArch toolchain
|
||||
mkdir /larch
|
||||
wget -O- -q https://github.com/loongson/build-tools/releases/download/2024.11.01/x86_64-cross-tools-loongarch64-binutils_2.43.1-gcc_14.2.0-glibc_2.40.tar.xz | tar -C /larch --strip-components=1 --xz -xf -
|
||||
wget -O- -q https://github.com/loongson/build-tools/releases/download/2025.02.21/x86_64-cross-tools-loongarch64-binutils_2.44-gcc_14.2.0-glibc_2.41.tar.xz | tar -C /larch --strip-components=1 --xz -xf -
|
||||
|
||||
cp -r /larch/loongarch64-unknown-linux-gnu/lib/* /larch/target/lib64
|
||||
ln -sf /larch/target /usr/loongarch64-linux-gnu
|
||||
@@ -57,5 +57,5 @@ done
|
||||
|
||||
# Install Intel SDE CPU emulator for CET-related tests
|
||||
mkdir /sde
|
||||
wget -O- -q https://downloadmirror.intel.com/831748/sde-external-9.44.0-2024-08-22-lin.tar.xz | tar -C /sde --strip-components=1 --xz -xf -
|
||||
wget -O- -q https://downloadmirror.intel.com/843185/sde-external-9.48.0-2024-11-25-lin.tar.xz | tar -C /sde --strip-components=1 --xz -xf -
|
||||
ln -s /sde/sde64 /usr/bin
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install ronn
|
||||
run: sudo apt-get update && sudo apt-get install -y ronn
|
||||
|
||||
@@ -8,3 +8,4 @@ gentoo
|
||||
/mold-*.tar.gz
|
||||
/build*
|
||||
/mold
|
||||
/dist
|
||||
+4
-3
@@ -44,7 +44,7 @@
|
||||
# features and behave exactly the same.
|
||||
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(mold VERSION 2.35.1)
|
||||
project(mold VERSION 2.37.1)
|
||||
|
||||
include(CMakeDependentOption)
|
||||
include(CheckSymbolExists)
|
||||
@@ -180,7 +180,7 @@ endif()
|
||||
# be stable on 32-bit targets.
|
||||
cmake_dependent_option(
|
||||
MOLD_USE_MIMALLOC "Use mimalloc" ON
|
||||
"CMAKE_SIZEOF_VOID_P EQUAL 8; NOT APPLE; NOT ANDROID; NOT OPENBSD" OFF)
|
||||
"CMAKE_SIZEOF_VOID_P EQUAL 8; NOT APPLE; NOT ANDROID; NOT OPENBSD; NOT MOLD_USE_ASAN; NOT MOLD_USE_TSAN" OFF)
|
||||
|
||||
cmake_dependent_option(
|
||||
MOLD_USE_SYSTEM_MIMALLOC "Use system or vendored mimalloc" OFF
|
||||
@@ -311,7 +311,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
# on a multicore machine.
|
||||
list(APPEND MOLD_ELF_TARGETS
|
||||
X86_64 I386 ARM64LE ARM64BE ARM32 RV32LE RV32BE RV64LE RV64BE PPC32
|
||||
PPC64V1 PPC64V2 S390X SPARC64 M68K SH4LE SH4BE LOONGARCH32 LOONGARCH64)
|
||||
PPC64V1 PPC64V2 S390X SPARC64 M68K SH4LE SH4BE LOONGARCH32 LOONGARCH64 ARC)
|
||||
|
||||
list(APPEND MOLD_ELF_TEMPLATE_FILES
|
||||
src/arch-arm64.cc
|
||||
@@ -385,6 +385,7 @@ target_sources(mold PRIVATE
|
||||
lib/perf.cc
|
||||
lib/random.cc
|
||||
lib/tar.cc
|
||||
src/arch-arc.cc
|
||||
src/arch-arm32.cc
|
||||
src/arch-i386.cc
|
||||
src/arch-m68k.cc
|
||||
|
||||
@@ -76,12 +76,12 @@ installation location by passing `-DCMAKE_INSTALL_PREFIX=<directory>`.
|
||||
For other cmake options, see the comments in `CMakeLists.txt`.
|
||||
|
||||
If you are not using a recent enough Linux distribution, or if `cmake` does
|
||||
not work for you for any reason, you can use Docker to build mold in a Docker
|
||||
environment. To do so, run `./dist.sh` in this directory instead of using
|
||||
`cmake`. The shell script will pull a Docker image, build mold and auxiliary
|
||||
not work for you for any reason, you can use Podman to build mold in a
|
||||
container. To do so, run `./dist.sh` in this directory instead of using
|
||||
`cmake`. The shell script will pull a container image, build mold and auxiliary
|
||||
files inside it, and package them into a single tar file named
|
||||
`mold-$version-$arch-linux.tar.gz`. You can extract the tar file anywhere and
|
||||
use the mold executable within it.
|
||||
`dist/mold-$version-$arch-linux.tar.gz`. You can extract the tar file anywhere
|
||||
and use the mold executable in it.
|
||||
|
||||
## How to use
|
||||
|
||||
@@ -158,6 +158,14 @@ If you want to use mold for all projects, add the above snippet to
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>If you are using Conan package manager</summary>
|
||||
|
||||
You can configure [Conan](https://github.com/conan-io) to download the latest
|
||||
version of `mold` and use it as the linker when building your dependencies and
|
||||
projects from source. Please see the instructions [here](https://conan.io/center/recipes/mold).
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>mold -run</summary>
|
||||
|
||||
It is sometimes very hard to pass an appropriate command line option to `cc`
|
||||
@@ -264,3 +272,4 @@ the following people and organizations who have sponsored $128/month or more:
|
||||
- [Bryant Biggs](https://github.com/bryantbiggs)
|
||||
- [kraptor23](https://github.com/kraptor23)
|
||||
- [Jinkyu Yi](https://github.com/jincreator)
|
||||
- [Pedro Navarro](https://github.com/pedronavf)
|
||||
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash -x
|
||||
set -e
|
||||
apt-get install -y git bzip2 ninja-build pkg-config libglib2.0-dev python3-distutils-extra libpixman-1-dev
|
||||
|
||||
cd /
|
||||
git clone --depth=1 https://github.com/foss-for-synopsys-dwc-arc-processors/qemu.git
|
||||
mkdir /qemu/build
|
||||
cd /qemu/build
|
||||
../configure --static --extra-cflags=-Wno-error
|
||||
ninja
|
||||
ln -s /qemu/build/qemu-arc /qemu/build/qemu-arc64 /usr/local/bin
|
||||
|
||||
|
||||
apt-get install -y libgmp-dev libmpfr-dev texinfo bison yacc flex
|
||||
cd /
|
||||
git clone --depth=1 https://github.com/foss-for-synopsys-dwc-arc-processors/binutils-gdb.git
|
||||
cd /binutils-gdb
|
||||
mkdir build
|
||||
cd build
|
||||
../configure --target=arc-linux
|
||||
make -j$(nproc)
|
||||
|
||||
|
||||
apt-get install -y mold
|
||||
mkdir /build
|
||||
cd /build
|
||||
cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DMOLD_USE_MOLD=1 /mold
|
||||
ninja
|
||||
TRIPLE=arc-linux-gnu bash -x /mold/test/hello-dynamic.sh
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# This script creates a mold binary distribution. The output is written in
|
||||
# this directory as `mold-$version-$arch-linux.tar.gz` (e.g.,
|
||||
# `dist` directory as `mold-$version-$arch-linux.tar.gz` (e.g.,
|
||||
# `mold-1.0.3-x86_64-linux.tar.gz`).
|
||||
#
|
||||
# The mold executable created by this script is statically linked to
|
||||
@@ -18,7 +18,7 @@
|
||||
# commit with release tags by rebuilding the binaries yourself.
|
||||
#
|
||||
# Debian provides snapshot.debian.org to host all historical binary
|
||||
# packages. We use it to construct Docker images pinned to a
|
||||
# packages. We use it to construct Podman images pinned to a
|
||||
# particular timestamp.
|
||||
#
|
||||
# We aim to use a reasonably old Debian version because we'll dynamically
|
||||
@@ -33,15 +33,14 @@
|
||||
# We need a recent version of Clang to build mold. If it's not available
|
||||
# via apt-get, we'll build it ourselves.
|
||||
#
|
||||
# You may need to run the following command to use Docker with Qemu:
|
||||
#
|
||||
# $ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||
# You may need to install qemu-user-static package to build non-native
|
||||
# binaries.
|
||||
|
||||
set -e -x
|
||||
cd "$(dirname $0)"
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 [ x86_64 | aarch64 | arm | riscv64 | ppc64le | s390x ]"
|
||||
echo "Usage: $0 [ x86_64 | aarch64 | arm | riscv64 | ppc64le | s390x | loongarch64 ]"
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -61,52 +60,48 @@ case $# in
|
||||
usage
|
||||
esac
|
||||
|
||||
echo "$arch" | grep -Eq '^(x86_64|aarch64|arm|riscv64|ppc64le|s390x)$' || usage
|
||||
|
||||
version=$(sed -n 's/^project(mold VERSION \(.*\))/\1/p' CMakeLists.txt)
|
||||
dest=mold-$version-$arch-linux
|
||||
|
||||
# Create a Podman image.
|
||||
if [ "$GITHUB_REPOSITORY" = '' ]; then
|
||||
image=mold-builder-$arch
|
||||
docker_build="docker build --platform linux/$arch -t $image -"
|
||||
image_build="podman build --arch $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.
|
||||
# the created container image in GitHub's container 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 -"
|
||||
image_build="podman build --arch $arch -t $image --output=type=registry --layers --cache-to $image --cache-from $image -"
|
||||
fi
|
||||
|
||||
# Create a Docker image.
|
||||
case $arch in
|
||||
x86_64)
|
||||
# Debian 8 (Jessie) released in April 2015
|
||||
cat <<EOF | $docker_build
|
||||
FROM debian:jessie-20210326@sha256:32ad5050caffb2c7e969dac873bce2c370015c2256ff984b70c1c08b3a2816a0
|
||||
# Debian 9 (Stretch) released in June 2017.
|
||||
# We use a Google-provided mirror (gcr.io) of the official Docker hub
|
||||
# (docker.io) because docker.io has a strict rate limit policy.
|
||||
cat <<EOF | $image_build
|
||||
FROM mirror.gcr.io/library/debian:stretch@sha256:c5c5200ff1e9c73ffbf188b4a67eb1c91531b644856b4aefe86a58d2f0cb05be
|
||||
ENV DEBIAN_FRONTEND=noninteractive TZ=UTC
|
||||
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 wget bzip2 file make autoconf gcc g++ libssl-dev && \
|
||||
apt-get install -y --no-install-recommends wget file make gcc g++ zlib1g-dev libssl-dev && \
|
||||
rm -rf /var/lib/apt/lists
|
||||
|
||||
# Build CMake 3.27
|
||||
RUN mkdir /build && \
|
||||
cd /build && \
|
||||
wget -O- --no-check-certificate https://cmake.org/files/v3.27/cmake-3.27.7.tar.gz | tar xzf - --strip-components=1 && \
|
||||
wget -O- --no-check-certificate --progress=dot:mega https://cmake.org/files/v3.27/cmake-3.27.7.tar.gz | tar xzf - --strip-components=1 && \
|
||||
./bootstrap --parallel=\$(nproc) && \
|
||||
make -j\$(nproc) && \
|
||||
make install && \
|
||||
rm -rf /build
|
||||
|
||||
# Build GCC 10
|
||||
# Build GCC 14
|
||||
RUN mkdir /build && \
|
||||
cd /build && \
|
||||
wget -O- --no-check-certificate https://ftpmirror.gnu.org/gnu/gcc/gcc-10.5.0/gcc-10.5.0.tar.gz | tar xzf - --strip-components=1 && \
|
||||
mkdir isl gmp mpc mpfr && \
|
||||
wget -O- --no-check-certificate https://gcc.gnu.org/pub/gcc/infrastructure/isl-0.18.tar.bz2 | tar xjf - --strip-components=1 -C isl && \
|
||||
wget -O- --no-check-certificate https://ftpmirror.gnu.org/gnu/gmp/gmp-6.1.2.tar.bz2 | tar xjf - --strip-components=1 -C gmp && \
|
||||
wget -O- --no-check-certificate https://ftpmirror.gnu.org/gnu/mpc/mpc-1.2.1.tar.gz | tar xzf - --strip-components=1 -C mpc && \
|
||||
wget -O- --no-check-certificate https://ftpmirror.gnu.org/gnu/mpfr/mpfr-4.1.0.tar.gz | tar xzf - --strip-components=1 -C mpfr && \
|
||||
wget -O- --no-check-certificate --progress=dot:mega https://ftpmirror.gnu.org/gcc/gcc-14.2.0/gcc-14.2.0.tar.gz | tar xzf - --strip-components=1 && \
|
||||
mkdir gmp mpc mpfr && \
|
||||
wget -O- --no-check-certificate --progress=dot:mega https://ftpmirror.gnu.org/gmp/gmp-6.3.0.tar.gz | tar xzf - --strip-components=1 -C gmp && \
|
||||
wget -O- --no-check-certificate --progress=dot:mega https://ftpmirror.gnu.org/mpc/mpc-1.3.1.tar.gz | tar xzf - --strip-components=1 -C mpc && \
|
||||
wget -O- --no-check-certificate --progress=dot:mega https://ftpmirror.gnu.org/mpfr/mpfr-4.2.1.tar.gz | tar xzf - --strip-components=1 -C mpfr && \
|
||||
./configure --prefix=/usr --enable-languages=c,c++ --disable-bootstrap --disable-multilib && \
|
||||
make -j\$(nproc) && \
|
||||
make install && \
|
||||
@@ -116,7 +111,7 @@ RUN mkdir /build && \
|
||||
# Build GNU binutils 2.43
|
||||
RUN mkdir /build && \
|
||||
cd /build && \
|
||||
wget -O- --no-check-certificate https://ftp.gnu.org/gnu/binutils/binutils-2.43.tar.gz | tar xzf - --strip-components=1 && \
|
||||
wget -O- --no-check-certificate --progress=dot:mega https://ftpmirror.gnu.org/binutils/binutils-2.43.tar.gz | tar xzf - --strip-components=1 && \
|
||||
./configure --prefix=/usr && \
|
||||
make -j\$(nproc) && \
|
||||
make install && \
|
||||
@@ -125,17 +120,16 @@ RUN mkdir /build && \
|
||||
# Build Python 3.12.7
|
||||
RUN mkdir /build && \
|
||||
cd /build && \
|
||||
wget -O- --no-check-certificate https://www.python.org/ftp/python/3.12.7/Python-3.12.7.tgz | tar xzf - --strip-components=1 && \
|
||||
wget -O- --no-check-certificate --progress=dot:mega https://www.python.org/ftp/python/3.12.7/Python-3.12.7.tgz | tar xzf - --strip-components=1 && \
|
||||
./configure && \
|
||||
make -j\$(nproc) && \
|
||||
make install && \
|
||||
ln -sf /usr/local/bin/python3 /usr/local/bin/python && \
|
||||
rm -rf /build
|
||||
|
||||
# Build LLVM 18.1.8
|
||||
# Build LLVM 19
|
||||
RUN mkdir /build && \
|
||||
cd /build && \
|
||||
wget -O- --no-check-certificate https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-18.1.8.tar.gz | tar xzf - --strip-components=1 && \
|
||||
wget -O- --no-check-certificate --progress=dot:mega https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-19.1.7.tar.gz | tar xzf - --strip-components=1 && \
|
||||
mkdir b && \
|
||||
cd b && \
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS=clang ../llvm && \
|
||||
@@ -145,13 +139,13 @@ RUN mkdir /build && \
|
||||
EOF
|
||||
;;
|
||||
aarch64 | arm | ppc64le | s390x)
|
||||
# Debian 10 (Bullseye) released in July 2019
|
||||
# Debian 11 (Bullseye) released in August 2021
|
||||
#
|
||||
# We don't want to build Clang for these targets with Qemu becuase
|
||||
# that'd take extremely long time. Also I believe old build machines
|
||||
# are usually x86-64.
|
||||
cat <<EOF | $docker_build
|
||||
FROM debian:bullseye-20240904@sha256:8ccc486c29a3ad02ad5af7f1156e2152dff3ba5634eec9be375269ef123457d8
|
||||
cat <<EOF | $image_build
|
||||
FROM mirror.gcr.io/library/debian:bullseye-20240904@sha256:8ccc486c29a3ad02ad5af7f1156e2152dff3ba5634eec9be375269ef123457d8
|
||||
ENV DEBIAN_FRONTEND=noninteractive TZ=UTC
|
||||
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 && \
|
||||
@@ -163,8 +157,8 @@ RUN sed -i -e '/^deb/d' -e 's/^# deb /deb [trusted=yes] /g' /etc/apt/sources.lis
|
||||
EOF
|
||||
;;
|
||||
riscv64)
|
||||
cat <<EOF | $docker_build
|
||||
FROM riscv64/debian:unstable-20240926@sha256:25654919c2926f38952cdd14b3300d83d13f2d820715f78c9f4b7a1d9399bf48
|
||||
cat <<EOF | $image_build
|
||||
FROM mirror.gcr.io/riscv64/debian:unstable-20240926@sha256:25654919c2926f38952cdd14b3300d83d13f2d820715f78c9f4b7a1d9399bf48
|
||||
ENV DEBIAN_FRONTEND=noninteractive TZ=UTC
|
||||
RUN sed -i -e '/^URIs/d' -e 's/^# http/URIs: http/' /etc/apt/sources.list.d/debian.sources && \
|
||||
echo 'Acquire::Retries "10"; Acquire::http::timeout "10"; Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/80-retries && \
|
||||
@@ -175,8 +169,26 @@ RUN sed -i -e '/^URIs/d' -e 's/^# http/URIs: http/' /etc/apt/sources.list.d/debi
|
||||
rm -rf /var/lib/apt/lists
|
||||
EOF
|
||||
;;
|
||||
loongarch64)
|
||||
# LoongArch build is not reproducible yet
|
||||
cat <<EOF | $image_build
|
||||
FROM mirror.gcr.io/loongarch64/debian:sid
|
||||
ENV DEBIAN_FRONTEND=noninteractive TZ=UTC
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends build-essential gcc-14 g++-14 clang-19 cmake && \
|
||||
ln -sf /usr/bin/clang-19 /usr/bin/clang && \
|
||||
ln -sf /usr/bin/clang++-19 /usr/bin/clang++ && \
|
||||
rm -rf /var/lib/apt/lists
|
||||
EOF
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
|
||||
version=$(sed -n 's/^project(mold VERSION \(.*\))/\1/p' CMakeLists.txt)
|
||||
dest=mold-$version-$arch-linux
|
||||
|
||||
# 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
|
||||
@@ -186,7 +198,10 @@ esac
|
||||
timestamp="$(git log -1 --format=%ci)"
|
||||
|
||||
# Build mold in a container.
|
||||
docker run --platform linux/$arch -i --rm -v "$(pwd):/mold" $image bash -c "
|
||||
mkdir -p dist
|
||||
|
||||
podman run --arch $arch -it --rm --userns=host --pids-limit=-1 --network=none \
|
||||
-v "$(pwd):/mold:ro" -v "$(pwd)/dist:/dist" $image bash -c "
|
||||
set -e
|
||||
mkdir /build
|
||||
cd /build
|
||||
@@ -195,11 +210,10 @@ cmake --build . -j\$(nproc)
|
||||
cmake --install .
|
||||
cmake -DMOLD_USE_MOLD=1 .
|
||||
cmake --build . -j\$(nproc)
|
||||
ctest -j\$(nproc)
|
||||
ctest --output-on-failure -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
|
||||
cp mold /mold
|
||||
chown $(id -u):$(id -g) /mold/$dest.tar.gz /mold/mold
|
||||
sha256sum /mold/$dest.tar.gz
|
||||
find $dest -print | sort | tar -cf - --no-recursion --files-from=- | gzip -9nc > /dist/$dest.tar.gz
|
||||
cp mold /dist
|
||||
sha256sum /dist/$dest.tar.gz
|
||||
"
|
||||
+6
-1
@@ -1,6 +1,6 @@
|
||||
.\" generated with Ronn-NG/v0.9.1
|
||||
.\" http://github.com/apjanke/ronn-ng/tree/0.9.1
|
||||
.TH "MOLD" "1" "October 2024" ""
|
||||
.TH "MOLD" "1" "February 2025" ""
|
||||
.SH "NAME"
|
||||
\fBmold\fR \- a modern linker
|
||||
.SH "SYNOPSIS"
|
||||
@@ -353,6 +353,11 @@ Report undefined symbols (even with \fB\-\-shared\fR)\.
|
||||
\fB\-\-noinhibit\-exec\fR
|
||||
Create an output file even if errors occur\.
|
||||
.TP
|
||||
\fB\-\-package\-metadata\fR=\fIpercent\-encoded\-string\fR
|
||||
Embed a specified string into the \fB\.note\.package\fR section\. This option is designed for build scripts that generate binary packages, such as \fB\.rpm\fR or \fB\.deb\fR, to include package metadata in each executable\. It simplifies the process of identifying the corresponding package for a given executable or core file\.
|
||||
.IP
|
||||
An argument to this option is treated as percent\-encoded and decoded before being inserted into the section, allowing you to avoid the use of the comma (\fB,\fR) character in the argument\. This is useful because the compiler replaces all occurrences of commas in \fB\-Wl,\fR with spaces before forwarding them to the linker\. Note that \fBmold\fR always interprets the argument as percent\-encoded, so you also need to escape all occurrences of \fB%\fR as \fB%25\fR\.
|
||||
.TP
|
||||
\fB\-\-pack\-dyn\-relocs\fR=[ \fBrelr\fR | \fBnone\fR ]
|
||||
If \fBrelr\fR is specified, all \fBR_*_RELATIVE\fR relocations are put into \fB\.relr\.dyn\fR section instead of \fB\.rel\.dyn\fR or \fB\.rela\.dyn\fR section\. Since \fB\.relr\.dyn\fR section uses a space\-efficient encoding scheme, specifying this flag can reduce the size of the output\. This is typically most effective for position\-independent executable\.
|
||||
.IP
|
||||
|
||||
@@ -617,6 +617,21 @@ but as `-o magic`.
|
||||
* `--noinhibit-exec`:
|
||||
Create an output file even if errors occur.
|
||||
|
||||
* `--package-metadata`=_percent-encoded-string_:
|
||||
Embed a specified string into the `.note.package` section. This option
|
||||
is designed for build scripts that generate binary packages, such as
|
||||
`.rpm` or `.deb`, to include package metadata in each executable. It
|
||||
simplifies the process of identifying the corresponding package for a
|
||||
given executable or core file.
|
||||
|
||||
An argument to this option is treated as percent-encoded and decoded
|
||||
before being inserted into the section, allowing you to avoid the use of
|
||||
the comma (`,`) character in the argument. This is useful because the
|
||||
compiler replaces all occurrences of commas in `-Wl,` with spaces before
|
||||
forwarding them to the linker. Note that `mold` always interprets the
|
||||
argument as percent-encoded, so you also need to escape all occurrences
|
||||
of `%` as `%25`.
|
||||
|
||||
* `--pack-dyn-relocs`=[ `relr` | `none` ]:
|
||||
If `relr` is specified, all `R_*_RELATIVE` relocations are put into
|
||||
`.relr.dyn` section instead of `.rel.dyn` or `.rela.dyn` section. Since
|
||||
|
||||
@@ -10,11 +10,13 @@ set -x
|
||||
case "$ID" in
|
||||
ubuntu | pop | linuxmint | debian | raspbian | neon)
|
||||
apt-get update
|
||||
apt-get install -y cmake gcc g++
|
||||
if [ "$ID-$VERSION_ID" = ubuntu-20.04 ]; then apt-get install -y g++-10; fi
|
||||
apt-get install -y cmake gcc g++ clang gdb
|
||||
;;
|
||||
fedora | amzn | rhel)
|
||||
dnf install -y gcc-g++ cmake glibc-static libstdc++-static diffutils util-linux
|
||||
fedora | amzn | rhel | centos)
|
||||
dnf install -y gcc-g++ cmake glibc-static libstdc++-static diffutils util-linux tar
|
||||
;;
|
||||
rocky)
|
||||
dnf install -y gcc-g++ cmake diffutils util-linux
|
||||
;;
|
||||
opensuse-*)
|
||||
zypper install -y make cmake gcc-c++ glibc-devel-static tar diffutils util-linux
|
||||
|
||||
@@ -11,7 +11,7 @@ set -x
|
||||
|
||||
case "$ID" in
|
||||
ubuntu | pop | linuxmint | debian | raspbian)
|
||||
apt-get install -y qemu-user {gcc,g++}-{i686,aarch64,riscv64,powerpc,powerpc64,powerpc64le,s390x,sparc64,m68k,sh4}-linux-gnu {gcc,g++}-arm-linux-gnueabihf
|
||||
apt-get install -y qemu-user {gcc,g++}-{i686,aarch64,riscv64,powerpc,powerpc64,powerpc64le,s390x,sparc64,m68k,sh4,arc}-linux-gnu {gcc,g++}-arm-linux-gnueabihf
|
||||
;;
|
||||
*)
|
||||
echo "Error: don't know anything about build dependencies on $ID-$VERSION_ID"
|
||||
|
||||
+29
-30
@@ -60,6 +60,7 @@ public:
|
||||
|
||||
namespace mold {
|
||||
|
||||
namespace ranges = std::ranges;
|
||||
using namespace std::literals::string_literals;
|
||||
using namespace std::literals::string_view_literals;
|
||||
|
||||
@@ -318,7 +319,7 @@ class Timer {
|
||||
public:
|
||||
Timer(Context &ctx, std::string name, Timer *parent = nullptr) {
|
||||
record = new TimerRecord(name, parent ? parent->record : nullptr);
|
||||
ctx.timer_records.push_back(std::unique_ptr<TimerRecord>(record));
|
||||
ctx.timer_records.emplace_back(record);
|
||||
}
|
||||
|
||||
Timer(const Timer &) = delete;
|
||||
@@ -335,23 +336,6 @@ private:
|
||||
TimerRecord *record;
|
||||
};
|
||||
|
||||
//
|
||||
// Bit vector
|
||||
//
|
||||
|
||||
class BitVector {
|
||||
public:
|
||||
BitVector() = default;
|
||||
BitVector(u32 size) : vec((size + 7) / 8) {}
|
||||
|
||||
void resize(u32 size) { vec.resize((size + 7) / 8); }
|
||||
bool get(u32 idx) const { return vec[idx / 8] & (1 << (idx % 8)); }
|
||||
void set(u32 idx) { vec[idx / 8] |= 1 << (idx % 8); }
|
||||
|
||||
private:
|
||||
std::vector<u8> vec;
|
||||
};
|
||||
|
||||
//
|
||||
// Utility functions
|
||||
//
|
||||
@@ -389,8 +373,14 @@ inline u64 bits(u64 val, u64 hi, u64 lo) {
|
||||
return (val >> lo) & ((1LL << (hi - lo + 1)) - 1);
|
||||
}
|
||||
|
||||
inline i64 sign_extend(u64 val, i64 size) {
|
||||
return (i64)(val << (63 - size)) >> (63 - size);
|
||||
// Cast val to a signed N bit integer.
|
||||
// For example, sign_extend(x, 32) == (i32)x for any integer x.
|
||||
inline i64 sign_extend(u64 val, i64 n) {
|
||||
return (i64)(val << (64 - n)) >> (64 - n);
|
||||
}
|
||||
|
||||
inline bool is_int(u64 val, i64 n) {
|
||||
return sign_extend(val, n) == val;
|
||||
}
|
||||
|
||||
template <typename T, typename Compare = std::less<T>>
|
||||
@@ -427,14 +417,6 @@ inline std::vector<T> flatten(std::vector<std::vector<T>> &vec) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline void sort(auto &vec) {
|
||||
std::stable_sort(vec.begin(), vec.end());
|
||||
}
|
||||
|
||||
inline void sort(auto &vec, auto less) {
|
||||
std::stable_sort(vec.begin(), vec.end(), less);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void remove_duplicates(std::vector<T> &vec) {
|
||||
vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
|
||||
@@ -501,6 +483,23 @@ inline u64 read_uleb(u8 *buf) {
|
||||
return read_uleb(&tmp);
|
||||
}
|
||||
|
||||
inline i64 read_sleb(u8 **buf) {
|
||||
u64 val = 0;
|
||||
u8 shift = 0;
|
||||
u8 byte;
|
||||
do {
|
||||
byte = *(*buf)++;
|
||||
val |= (byte & 0x7f) << shift;
|
||||
shift += 7;
|
||||
} while (byte & 0x80);
|
||||
return sign_extend(val, shift);
|
||||
}
|
||||
|
||||
inline i64 read_sleb(u8 *buf) {
|
||||
u8 *tmp = buf;
|
||||
return read_sleb(&tmp);
|
||||
}
|
||||
|
||||
inline u64 read_uleb(std::string_view *str) {
|
||||
u8 *start = (u8 *)str->data();
|
||||
u8 *ptr = start;
|
||||
@@ -906,7 +905,7 @@ public:
|
||||
mf->size = size;
|
||||
mf->parent = this;
|
||||
|
||||
ctx.mf_pool.push_back(std::unique_ptr<MappedFile>(mf));
|
||||
ctx.mf_pool.emplace_back(mf);
|
||||
return mf;
|
||||
}
|
||||
|
||||
@@ -965,7 +964,7 @@ MappedFile *open_file(Context &ctx, std::string path) {
|
||||
Fatal(ctx) << error;
|
||||
|
||||
if (mf)
|
||||
ctx.mf_pool.push_back(std::unique_ptr<MappedFile>(mf));
|
||||
ctx.mf_pool.emplace_back(mf);
|
||||
return mf;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ u32 compute_crc32(u32 crc, u8 *buf, i64 len) {
|
||||
len -= sz;
|
||||
}
|
||||
|
||||
tbb::parallel_for_each(shards.begin(), shards.end(), [](Shard &shard) {
|
||||
tbb::parallel_for_each(shards, [](Shard &shard) {
|
||||
shard.crc = crc32(0, shard.buf, shard.len);
|
||||
});
|
||||
|
||||
|
||||
+13
-13
@@ -1,14 +1,14 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# This test script takes a Gentoo package name and tries to build it
|
||||
# using mold in a Docker environment. We chose Gentoo Linux as a test
|
||||
# using mold in a Podman environment. We chose Gentoo Linux as a test
|
||||
# target, because its source-based package allows us to build programs
|
||||
# locally and run their test suites without any hassle.
|
||||
#
|
||||
# You can get a complete list of Gentoo packages availalbe for testing
|
||||
# with the following command:
|
||||
#
|
||||
# docker run --rm mold-gentoo emerge --color n -s '' | \
|
||||
# podman run --rm mold-gentoo emerge --color n -s '' | \
|
||||
# perl -ne 'next unless m!^\*\s+(\S+/\S+)!; print "$1\n"'
|
||||
|
||||
package="$1"
|
||||
@@ -20,48 +20,48 @@ fi
|
||||
|
||||
set -x
|
||||
|
||||
# Create a Docker image
|
||||
if ! docker image ls mold-gentoo | grep -q mold-gentoo; then
|
||||
# Create a Podman image
|
||||
if ! podman image ls mold-gentoo | grep -q mold-gentoo; then
|
||||
set -e
|
||||
cat <<EOF | docker build -t mold-gentoo -
|
||||
FROM gentoo/stage3
|
||||
cat <<EOF | podman build -t mold-gentoo -
|
||||
FROM docker.io/gentoo/stage3
|
||||
RUN emerge-webrsync
|
||||
RUN echo 'USE="X ssl elogind -systemd corefonts truetype jpeg jpeg2k tiff zstd static-libs binary -perl"' >> /etc/portage/make.conf && \
|
||||
echo 'ACCEPT_KEYWORDS="~amd64"' >> /etc/portage/make.conf && \
|
||||
echo 'ACCEPT_LICENSE="* -@EULA"' >> /etc/portage/make.conf && \
|
||||
echo 'FEATURES="\${FEATURE} noclean nostrip ccache -ipc-sandbox -network-sandbox -pid-sandbox -sandbox"' >> /etc/portage/make.conf && \
|
||||
echo 'CCACHE_DIR="/ccache"' >> /etc/portage/make.conf && \
|
||||
emerge gdb lld clang vim emacs strace ccache xeyes dev-build/cmake dev-vcs/git && \
|
||||
FEATURES='getbinpkg binpkg-request-signature' emerge gdb lld llvm-core/clang vim emacs strace ccache xeyes dev-build/cmake dev-vcs/git && \
|
||||
rm -rf /var/tmp/portage
|
||||
EOF
|
||||
set +e
|
||||
fi
|
||||
|
||||
git_hash=$(./mold --version | perl -ne '/\((\w+)/; print $1;')
|
||||
git_hash=$(./dist/mold --version | perl -ne '/\((\w+)/; print $1;')
|
||||
|
||||
if [ "$package" = dev-libs/concurrencykit ]; then
|
||||
echo "Skipping known broken package: $package"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Build a given package in Docker
|
||||
cmd1='(cd /usr/bin; ln -sf /mold/mold $(realpath ld))'
|
||||
# Build a given package in Podman
|
||||
cmd1='(cd /usr/bin; ln -sf /mold/dist/mold $(realpath ld))'
|
||||
cmd2="MAKEOPTS=-'j$(nproc) --load-average=100' emerge --onlydeps $package"
|
||||
cmd3="MAKEOPTS='-j$(nproc) --load-average=100' FEATURES=test emerge $package"
|
||||
filename=`echo "$package" | sed 's!/!_!g'`
|
||||
docker="docker run --rm --cap-add=SYS_PTRACE -v `pwd`:/mold -v /var/cache/ccache-gentoo:/ccache mold-gentoo timeout -v -k 15s 3h"
|
||||
podman="podman run --rm --pids-limit=-1 --cap-add=SYS_PTRACE -v `pwd`:/mold:ro -v /var/cache/ccache-gentoo:/ccache mold-gentoo timeout -v -k 15s 3h"
|
||||
dir=gentoo/$git_hash
|
||||
|
||||
mkdir -p "$dir"/success "$dir"/failure
|
||||
|
||||
$docker nice -n 19 bash -c "$cmd1 && $cmd2 && $cmd3" >& "$dir"/"$filename".mold
|
||||
$podman nice -n 19 bash -c "$cmd1 && $cmd2 && $cmd3" >& "$dir"/"$filename".mold
|
||||
if [ $? = 0 ]; then
|
||||
mv "$dir"/"$filename".mold "$dir"/success
|
||||
else
|
||||
mv "$dir"/"$filename".mold "$dir"/failure
|
||||
fi
|
||||
|
||||
$docker nice -n 19 bash -c "$cmd2 && $cmd3" >& "$dir"/"$filename".ld
|
||||
$podman nice -n 19 bash -c "$cmd2 && $cmd3" >& "$dir"/"$filename".ld
|
||||
if [ $? = 0 ]; then
|
||||
mv "$dir"/"$filename".ld "$dir"/success
|
||||
else
|
||||
|
||||
+5
-5
@@ -19,7 +19,7 @@ std::optional<Glob> Glob::compile(std::string_view pat) {
|
||||
// [$\]!]: $, ] or !
|
||||
// [a-czg-i]: a, b, c, z, g, h, or i
|
||||
// [^a-z]: Any character except lowercase letters
|
||||
vec.push_back({BRACKET});
|
||||
vec.emplace_back(BRACKET);
|
||||
std::bitset<256> &bitset = vec.back().bitset;
|
||||
|
||||
bool negate = false;
|
||||
@@ -74,22 +74,22 @@ std::optional<Glob> Glob::compile(std::string_view pat) {
|
||||
break;
|
||||
}
|
||||
case '?':
|
||||
vec.push_back({QUESTION});
|
||||
vec.emplace_back(QUESTION);
|
||||
break;
|
||||
case '*':
|
||||
vec.push_back({STAR});
|
||||
vec.emplace_back(STAR);
|
||||
break;
|
||||
case '\\':
|
||||
if (pat.empty())
|
||||
return {};
|
||||
if (vec.empty() || vec.back().kind != STRING)
|
||||
vec.push_back({STRING});
|
||||
vec.emplace_back(STRING);
|
||||
vec.back().str += pat[0];
|
||||
pat = pat.substr(1);
|
||||
break;
|
||||
default:
|
||||
if (vec.empty() || vec.back().kind != STRING)
|
||||
vec.push_back({STRING});
|
||||
vec.emplace_back(STRING);
|
||||
vec.back().str += c;
|
||||
break;
|
||||
}
|
||||
|
||||
+1
-1
@@ -11,9 +11,9 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include "common.h"
|
||||
|
||||
// Including mimalloc-new-delete.h overrides new/delete operators.
|
||||
// We need it only when we are using mimalloc as a dynamic library.
|
||||
#if MOLD_USE_SYSTEM_MIMALLOC
|
||||
|
||||
+2
-2
@@ -96,12 +96,12 @@ bool MultiGlob::add(std::string_view pat, i64 val) {
|
||||
assert(!is_compiled);
|
||||
assert(!pat.empty());
|
||||
|
||||
strings.push_back(std::string(pat));
|
||||
strings.emplace_back(pat);
|
||||
|
||||
// Complex glob pattern
|
||||
if (!is_simple_pattern(pat)) {
|
||||
if (std::optional<Glob> glob = Glob::compile(pat)) {
|
||||
globs.push_back({std::move(*glob), val});
|
||||
globs.emplace_back(std::move(*glob), val);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
+3
-6
@@ -3,6 +3,7 @@
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <ios>
|
||||
#include <tbb/concurrent_vector.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/resource.h>
|
||||
@@ -16,9 +17,7 @@ i64 Counter::get_value() {
|
||||
}
|
||||
|
||||
void Counter::print() {
|
||||
sort(instances, [](Counter *a, Counter *b) {
|
||||
return a->get_value() > b->get_value();
|
||||
});
|
||||
ranges::stable_sort(instances, {}, [](Counter *x) { return x->get_value(); });
|
||||
|
||||
for (Counter *c : instances)
|
||||
std::cout << std::setw(20) << std::right << c->name
|
||||
@@ -79,9 +78,7 @@ static void print_rec(TimerRecord &rec, i64 indent) {
|
||||
std::string(indent * 2, ' ').c_str(),
|
||||
rec.name.c_str());
|
||||
|
||||
sort(rec.children, [](TimerRecord *a, TimerRecord *b) {
|
||||
return a->start < b->start;
|
||||
});
|
||||
ranges::stable_sort(rec.children, {}, &TimerRecord::start);
|
||||
|
||||
for (TimerRecord *child : rec.children)
|
||||
print_rec(*child, indent + 1);
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
#include "common.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <csignal>
|
||||
#include <tbb/version.h>
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
// r31 is blink. r30 is I believe a scratch register.
|
||||
//
|
||||
// Programs might use one of several branch, jump, and link instructions
|
||||
// to control execution flow through direct and indirect function calls
|
||||
// and branching. For function calling, the conditional branch-and-link
|
||||
// instruction has a maximum branch range of ±1 MiB, and the target
|
||||
// address is 32-bit-aligned. The unconditional branch-and-link format has
|
||||
// a maximum branch range of ±16 MiB.
|
||||
//
|
||||
// https://github.com/foss-for-synopsys-dwc-arc-processors/arc-ABI-manual/blob/master/ARCv2_ABI.pdf
|
||||
|
||||
#include "mold.h"
|
||||
|
||||
namespace mold {
|
||||
|
||||
using E = ARC;
|
||||
|
||||
// An integer-like class for the mixed-endian encoding
|
||||
class M32 {
|
||||
public:
|
||||
operator u32() const {
|
||||
return (buf[1] << 24) | (buf[0] << 16) | (buf[3] << 8) | buf[2];
|
||||
}
|
||||
|
||||
M32 &operator=(u32 x) {
|
||||
buf[0] = x >> 16;
|
||||
buf[1] = x >> 24;
|
||||
buf[2] = x;
|
||||
buf[3] = x >> 8;
|
||||
return *this;
|
||||
}
|
||||
|
||||
M32 &operator&=(u32 x) { return *this = *this & x; }
|
||||
M32 &operator|=(u32 x) { return *this = *this | x; }
|
||||
u8 buf[4];
|
||||
};
|
||||
|
||||
static void write_disp7u(u8 *loc, u32 val) {
|
||||
*(ul16 *)loc |= (bits(val, 6, 3) << 4) | bits(val, 2, 0);
|
||||
}
|
||||
|
||||
static void write_disp9(u8 *loc, u32 val) {
|
||||
*(M32 *)loc |= bits(val, 8, 0);
|
||||
}
|
||||
|
||||
static void write_disp9ls(u8 *loc, u32 val) {
|
||||
*(M32 *)loc |= (bits(val, 7, 0) << 16) | (bit(val, 8) << 15);
|
||||
}
|
||||
|
||||
static void write_disp9s(u8 *loc, u32 val) {
|
||||
*(ul16 *)loc |= bits(val, 8, 0);
|
||||
}
|
||||
|
||||
static void write_disp10u(u8 *loc, u32 val) {
|
||||
*(ul16 *)loc |= bits(val, 9, 0);
|
||||
}
|
||||
|
||||
static void write_disp13s(u8 *loc, u32 val) {
|
||||
*(ul16 *)loc |= bits(val, 12, 0);
|
||||
}
|
||||
|
||||
static void write_disp21h(u8 *loc, u32 val) {
|
||||
*(M32 *)loc |= (bits(val, 10, 1) << 17) | (bits(val, 20, 11) << 6);
|
||||
}
|
||||
|
||||
static void write_disp21w(u8 *loc, u32 val) {
|
||||
*(M32 *)loc |= (bits(val, 10, 2) << 18) | (bits(val, 20, 11) << 6);
|
||||
}
|
||||
|
||||
static void write_disp25h(u8 *loc, u32 val) {
|
||||
*(M32 *)loc |= (bits(val, 10, 1) << 17) | (bits(val, 20, 11) << 6) |
|
||||
bits(val, 24, 21);
|
||||
}
|
||||
|
||||
static void write_disp25w(u8 *loc, u32 val) {
|
||||
*(M32 *)loc |= (bits(val, 10, 2) << 18) | (bits(val, 20, 11) << 6) |
|
||||
bits(val, 24, 21);
|
||||
}
|
||||
|
||||
static void write_disps9(u8 *loc, u32 val) {
|
||||
*(ul16 *)loc |= bits(val, 10, 2);
|
||||
}
|
||||
|
||||
static void write_disps12(u8 *loc, u32 val) {
|
||||
*(M32 *)loc |= (bits(val, 5, 0) << 6) | bits(val, 11, 6);
|
||||
}
|
||||
|
||||
template <>
|
||||
void write_plt_header(Context<E> &ctx, u8 *buf) {
|
||||
static const ul16 insn[] = {
|
||||
0x2730, 0x7f8b, 0, 0, // ld r11, [pcl,0]
|
||||
0x2730, 0x7f8a, 0, 0, // ld r10, [pcl,0]
|
||||
0x2020, 0x0280, // j [r10]
|
||||
0, 0, // (address of GOTPLT)
|
||||
};
|
||||
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(M32 *)(buf + 4) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr + 4;
|
||||
*(M32 *)(buf + 12) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr;
|
||||
*(M32 *)(buf + 20) = ctx.gotplt->shdr.sh_addr;
|
||||
}
|
||||
|
||||
template <>
|
||||
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
static const ul16 insn[] = {
|
||||
0x2730, 0x7f8c, 0, 0, // ld r12, [pcl,0]
|
||||
0x2021, 0x0300, // j.d [r12]
|
||||
0x240a, 0x1fc0, // mov r12,pcl
|
||||
};
|
||||
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(M32 *)(buf + 4) = sym.get_gotplt_addr(ctx) - sym.get_plt_addr(ctx);
|
||||
}
|
||||
|
||||
template <>
|
||||
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
static const ul16 insn[] = {
|
||||
0x2730, 0x7f8c, 0, 0, // ld r12, [pcl,0]
|
||||
0x2021, 0x0300, // j.d [r12]
|
||||
0x240a, 0x1fc0, // mov r12,pcl
|
||||
};
|
||||
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(M32 *)(buf + 4) = sym.get_got_pltgot_addr(ctx) - sym.get_plt_addr(ctx);
|
||||
}
|
||||
|
||||
template <>
|
||||
void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
|
||||
u64 offset, u64 val) {
|
||||
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_NONE:
|
||||
break;
|
||||
case R_ARC_32:
|
||||
*(ul32 *)loc = val;
|
||||
break;
|
||||
case R_ARC_32_PCREL:
|
||||
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
|
||||
break;
|
||||
default:
|
||||
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
if (rel.r_type == R_NONE)
|
||||
continue;
|
||||
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
u8 *loc = base + rel.r_offset;
|
||||
|
||||
u64 S = sym.get_addr(ctx);
|
||||
u64 A = rel.r_addend;
|
||||
u64 P = get_addr() + rel.r_offset;
|
||||
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
|
||||
u64 GOT = ctx.got->shdr.sh_addr;
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_ARC_32:
|
||||
*(ul32 *)loc = S + A;
|
||||
break;
|
||||
case R_ARC_32_ME:
|
||||
*(M32 *)loc = S + A;
|
||||
break;
|
||||
case R_ARC_S25H_PCREL:
|
||||
case R_ARC_S25H_PCREL_PLT:
|
||||
write_disp25h(loc, align_to(S + A - P, 2));
|
||||
break;
|
||||
case R_ARC_S25W_PCREL:
|
||||
case R_ARC_S25W_PCREL_PLT:
|
||||
write_disp25w(loc, align_to(S + A - P, 4));
|
||||
break;
|
||||
case R_ARC_PC32:
|
||||
*(M32 *)loc = S + A - P;
|
||||
break;
|
||||
case R_ARC_32_PCREL:
|
||||
*(ul32 *)loc = S + A - P;
|
||||
break;
|
||||
case R_ARC_GOTPC32:
|
||||
*(M32 *)loc = GOT + G + A - P;
|
||||
break;
|
||||
case R_ARC_TLS_LE_32:
|
||||
*(M32 *)loc = S + A - ctx.tp_addr;
|
||||
break;
|
||||
case R_ARC_TLS_IE_GOT:
|
||||
*(M32 *)loc = sym.get_gottp_addr(ctx) + A - P;
|
||||
break;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
|
||||
continue;
|
||||
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
u8 *loc = base + rel.r_offset;
|
||||
|
||||
SectionFragment<E> *frag;
|
||||
i64 frag_addend;
|
||||
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
|
||||
|
||||
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
|
||||
u64 A = frag ? frag_addend : (i64)rel.r_addend;
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_ARC_32:
|
||||
if (std::optional<u64> val = get_tombstone(sym, frag))
|
||||
*(ul32 *)loc = *val;
|
||||
else
|
||||
*(ul32 *)loc = S + A;
|
||||
break;
|
||||
default:
|
||||
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
|
||||
<< rel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
assert(shdr().sh_flags & SHF_ALLOC);
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
|
||||
continue;
|
||||
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
|
||||
if (sym.is_ifunc())
|
||||
Error(ctx) << sym << ": GNU ifunc symbol is not supported on m68k";
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_ARC_32:
|
||||
break;
|
||||
case R_ARC_32_ME:
|
||||
scan_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_ARC_S25H_PCREL:
|
||||
case R_ARC_S25W_PCREL:
|
||||
case R_ARC_32_PCREL:
|
||||
case R_ARC_PC32:
|
||||
scan_pcrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_ARC_S25H_PCREL_PLT:
|
||||
case R_ARC_S25W_PCREL_PLT:
|
||||
if (sym.is_imported)
|
||||
sym.flags |= NEEDS_PLT;
|
||||
break;
|
||||
case R_ARC_GOTPC32:
|
||||
sym.flags |= NEEDS_GOT;
|
||||
break;
|
||||
case R_ARC_TLS_LE_32:
|
||||
check_tlsle(ctx, sym, rel);
|
||||
break;
|
||||
case R_ARC_TLS_IE_GOT:
|
||||
sym.flags |= NEEDS_GOTTP;
|
||||
break;
|
||||
default:
|
||||
Error(ctx) << *this << ": unknown relocation: " << rel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
u64 get_eflags(Context<E> &ctx) {
|
||||
return 0x406;
|
||||
}
|
||||
|
||||
} // namespace mold
|
||||
+148
-117
@@ -34,8 +34,6 @@
|
||||
#include "mold.h"
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/parallel_for_each.h>
|
||||
#include <tbb/parallel_sort.h>
|
||||
|
||||
namespace mold {
|
||||
|
||||
@@ -43,6 +41,9 @@ using E = ARM32;
|
||||
|
||||
template <>
|
||||
i64 get_addend(u8 *loc, const ElfRel<E> &rel) {
|
||||
ul32 *arm = (ul32 *)loc;
|
||||
ul16 *thm = (ul16 *)loc;
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_ARM_ABS32:
|
||||
case R_ARM_REL32:
|
||||
@@ -56,77 +57,104 @@ i64 get_addend(u8 *loc, const ElfRel<E> &rel) {
|
||||
case R_ARM_TLS_IE32:
|
||||
case R_ARM_TLS_LE32:
|
||||
case R_ARM_TLS_GOTDESC:
|
||||
case R_ARM_TARGET1:
|
||||
case R_ARM_TARGET2:
|
||||
return *(il32 *)loc;
|
||||
return (il32)*arm;
|
||||
case R_ARM_THM_JUMP8:
|
||||
return sign_extend(thm[0], 8) << 1;
|
||||
case R_ARM_THM_JUMP11:
|
||||
return sign_extend(*(ul16 *)loc, 10) << 1;
|
||||
return sign_extend(thm[0], 11) << 1;
|
||||
case R_ARM_THM_JUMP19: {
|
||||
// https://developer.arm.com/documentation/ddi0597/2024-12/Base-Instructions/B--Branch-
|
||||
u32 S = bit(thm[0], 10);
|
||||
u32 J2 = bit(thm[1], 11);
|
||||
u32 J1 = bit(thm[1], 13);
|
||||
u32 imm6 = bits(thm[0], 5, 0);
|
||||
u32 imm11 = bits(thm[1], 10, 0);
|
||||
u32 val = (S << 20) | (J2 << 19) | (J1 << 18) | (imm6 << 12) | (imm11 << 1);
|
||||
return sign_extend(val, 21);
|
||||
}
|
||||
case R_ARM_THM_CALL:
|
||||
case R_ARM_THM_JUMP24:
|
||||
case R_ARM_THM_TLS_CALL: {
|
||||
u32 S = bit(*(ul16 *)loc, 10);
|
||||
u32 J1 = bit(*(ul16 *)(loc + 2), 13);
|
||||
u32 J2 = bit(*(ul16 *)(loc + 2), 11);
|
||||
// https://developer.arm.com/documentation/ddi0597/2024-12/Base-Instructions/BL--BLX--immediate---Branch-with-Link-and-optional-Exchange--immediate--
|
||||
u32 S = bit(thm[0], 10);
|
||||
u32 J1 = bit(thm[1], 13);
|
||||
u32 J2 = bit(thm[1], 11);
|
||||
u32 I1 = !(J1 ^ S);
|
||||
u32 I2 = !(J2 ^ S);
|
||||
u32 imm10 = bits(*(ul16 *)loc, 9, 0);
|
||||
u32 imm11 = bits(*(ul16 *)(loc + 2), 10, 0);
|
||||
u32 imm10 = bits(thm[0], 9, 0);
|
||||
u32 imm11 = bits(thm[1], 10, 0);
|
||||
u32 val = (S << 24) | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1);
|
||||
return sign_extend(val, 24);
|
||||
return sign_extend(val, 25);
|
||||
}
|
||||
case R_ARM_CALL:
|
||||
case R_ARM_JUMP24:
|
||||
case R_ARM_PLT32:
|
||||
case R_ARM_TLS_CALL:
|
||||
return sign_extend(*(ul32 *)loc, 23) << 2;
|
||||
return sign_extend(*arm, 24) << 2;
|
||||
case R_ARM_MOVW_PREL_NC:
|
||||
case R_ARM_MOVW_ABS_NC:
|
||||
case R_ARM_MOVT_PREL:
|
||||
case R_ARM_MOVT_ABS: {
|
||||
u32 imm12 = bits(*(ul32 *)loc, 11, 0);
|
||||
u32 imm4 = bits(*(ul32 *)loc, 19, 16);
|
||||
return sign_extend((imm4 << 12) | imm12, 15);
|
||||
// https://developer.arm.com/documentation/ddi0597/2024-12/Base-Instructions/MOV--MOVS--immediate---Move--immediate--
|
||||
u32 imm4 = bits(*arm, 19, 16);
|
||||
u32 imm12 = bits(*arm, 11, 0);
|
||||
u32 val = (imm4 << 12) | imm12;
|
||||
return sign_extend(val, 16);
|
||||
}
|
||||
case R_ARM_PREL31:
|
||||
return sign_extend(*(ul32 *)loc, 30);
|
||||
return sign_extend(*arm, 31);
|
||||
case R_ARM_THM_MOVW_PREL_NC:
|
||||
case R_ARM_THM_MOVW_ABS_NC:
|
||||
case R_ARM_THM_MOVT_PREL:
|
||||
case R_ARM_THM_MOVT_ABS: {
|
||||
u32 imm4 = bits(*(ul16 *)loc, 3, 0);
|
||||
u32 i = bit(*(ul16 *)loc, 10);
|
||||
u32 imm3 = bits(*(ul16 *)(loc + 2), 14, 12);
|
||||
u32 imm8 = bits(*(ul16 *)(loc + 2), 7, 0);
|
||||
// https://developer.arm.com/documentation/ddi0597/2024-12/Base-Instructions/MOVT--Move-Top-
|
||||
u32 imm4 = bits(thm[0], 3, 0);
|
||||
u32 i = bit(thm[0], 10);
|
||||
u32 imm3 = bits(thm[1], 14, 12);
|
||||
u32 imm8 = bits(thm[1], 7, 0);
|
||||
u32 val = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8;
|
||||
return sign_extend(val, 15);
|
||||
return sign_extend(val, 16);
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void write_mov_imm(u8 *loc, u32 val) {
|
||||
static void write_arm_mov(u8 *loc, u32 val) {
|
||||
u32 imm12 = bits(val, 11, 0);
|
||||
u32 imm4 = bits(val, 15, 12);
|
||||
*(ul32 *)loc = (*(ul32 *)loc & 0xfff0f000) | (imm4 << 16) | imm12;
|
||||
*(ul32 *)loc = (*(ul32 *)loc & 0xfff0'f000) | (imm4 << 16) | imm12;
|
||||
}
|
||||
|
||||
static void write_thm_b_imm(u8 *loc, u32 val) {
|
||||
// https://developer.arm.com/documentation/ddi0406/cb/Application-Level-Architecture/Instruction-Details/Alphabetical-list-of-instructions/BL--BLX--immediate-
|
||||
u32 sign = bit(val, 24);
|
||||
static void write_thm_b21(u8 *loc, u32 val) {
|
||||
u32 S = bit(val, 20);
|
||||
u32 J2 = bit(val, 19);
|
||||
u32 J1 = bit(val, 18);
|
||||
u32 imm6 = bits(val, 17, 12);
|
||||
u32 imm11 = bits(val, 11, 1);
|
||||
|
||||
ul16 *buf = (ul16 *)loc;
|
||||
buf[0] = (buf[0] & 0b1111'1011'1100'0000) | (S << 10) | imm6;
|
||||
buf[1] = (buf[1] & 0b1101'0000'0000'0000) | (J1 << 13) | (J2 << 11) | imm11;
|
||||
}
|
||||
|
||||
static void write_thm_b25(u8 *loc, u32 val) {
|
||||
u32 S = bit(val, 24);
|
||||
u32 I1 = bit(val, 23);
|
||||
u32 I2 = bit(val, 22);
|
||||
u32 J1 = !I1 ^ sign;
|
||||
u32 J2 = !I2 ^ sign;
|
||||
u32 J1 = !I1 ^ S;
|
||||
u32 J2 = !I2 ^ S;
|
||||
u32 imm10 = bits(val, 21, 12);
|
||||
u32 imm11 = bits(val, 11, 1);
|
||||
|
||||
ul16 *buf = (ul16 *)loc;
|
||||
buf[0] = (buf[0] & 0b1111'1000'0000'0000) | (sign << 10) | imm10;
|
||||
buf[0] = (buf[0] & 0b1111'1000'0000'0000) | (S << 10) | imm10;
|
||||
buf[1] = (buf[1] & 0b1101'0000'0000'0000) | (J1 << 13) | (J2 << 11) | imm11;
|
||||
}
|
||||
|
||||
static void write_thm_mov_imm(u8 *loc, u32 val) {
|
||||
// https://developer.arm.com/documentation/ddi0406/cb/Application-Level-Architecture/Instruction-Details/Alphabetical-list-of-instructions/MOVT
|
||||
static void write_thm_mov(u8 *loc, u32 val) {
|
||||
u32 imm4 = bits(val, 15, 12);
|
||||
u32 i = bit(val, 11);
|
||||
u32 imm3 = bits(val, 10, 8);
|
||||
@@ -154,16 +182,20 @@ void write_addend(u8 *loc, i64 val, const ElfRel<E> &rel) {
|
||||
case R_ARM_TLS_IE32:
|
||||
case R_ARM_TLS_LE32:
|
||||
case R_ARM_TLS_GOTDESC:
|
||||
case R_ARM_TARGET1:
|
||||
case R_ARM_TARGET2:
|
||||
*(ul32 *)loc = val;
|
||||
break;
|
||||
case R_ARM_THM_JUMP8:
|
||||
*(ul16 *)loc = (*(ul16 *)loc & 0xff00) | bits(val, 8, 1);
|
||||
break;
|
||||
case R_ARM_THM_JUMP11:
|
||||
*(ul16 *)loc = (*(ul16 *)loc & 0xf800) | bits(val, 11, 1);
|
||||
break;
|
||||
case R_ARM_THM_CALL:
|
||||
case R_ARM_THM_JUMP24:
|
||||
case R_ARM_THM_TLS_CALL:
|
||||
write_thm_b_imm(loc, val);
|
||||
write_thm_b25(loc, val);
|
||||
break;
|
||||
case R_ARM_CALL:
|
||||
case R_ARM_JUMP24:
|
||||
@@ -174,7 +206,7 @@ void write_addend(u8 *loc, i64 val, const ElfRel<E> &rel) {
|
||||
case R_ARM_MOVW_ABS_NC:
|
||||
case R_ARM_MOVT_PREL:
|
||||
case R_ARM_MOVT_ABS:
|
||||
write_mov_imm(loc, val);
|
||||
write_arm_mov(loc, val);
|
||||
break;
|
||||
case R_ARM_PREL31:
|
||||
*(ul32 *)loc = (*(ul32 *)loc & 0x8000'0000) | (val & 0x7fff'ffff);
|
||||
@@ -183,7 +215,7 @@ void write_addend(u8 *loc, i64 val, const ElfRel<E> &rel) {
|
||||
case R_ARM_THM_MOVW_ABS_NC:
|
||||
case R_ARM_THM_MOVT_PREL:
|
||||
case R_ARM_THM_MOVT_ABS:
|
||||
write_thm_mov_imm(loc, val);
|
||||
write_thm_mov(loc, val);
|
||||
break;
|
||||
default:
|
||||
unreachable();
|
||||
@@ -207,7 +239,7 @@ void write_plt_header(Context<E> &ctx, u8 *buf) {
|
||||
*(ul32 *)(buf + 16) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 16;
|
||||
}
|
||||
|
||||
constexpr ul32 plt_entry[] = {
|
||||
static constexpr ul32 plt_entry[] = {
|
||||
0xe59f'c004, // 1: ldr ip, 2f
|
||||
0xe08c'c00f, // add ip, ip, pc
|
||||
0xe59c'f000, // ldr pc, [ip]
|
||||
@@ -245,25 +277,18 @@ void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
|
||||
}
|
||||
}
|
||||
|
||||
// ARM and Thumb branch instructions can jump within ±16 MiB.
|
||||
static bool is_jump_reachable(i64 val) {
|
||||
return sign_extend(val, 24) == val;
|
||||
static Thunk<E> &get_reachable_thunk(OutputSection<E> &osec, u64 addr) {
|
||||
auto it = std::upper_bound(osec.thunks.begin(), osec.thunks.end(), addr,
|
||||
[](u64 addr, std::unique_ptr<Thunk<E>> &thunk) {
|
||||
return addr < thunk->get_addr();
|
||||
});
|
||||
return **it;
|
||||
}
|
||||
|
||||
template <>
|
||||
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
auto get_tls_trampoline_addr = [&, i = 0](u64 addr) mutable {
|
||||
for (; i < output_section->thunks.size(); i++) {
|
||||
i64 disp = output_section->shdr.sh_addr + output_section->thunks[i]->offset -
|
||||
addr;
|
||||
if (is_jump_reachable(disp))
|
||||
return disp;
|
||||
}
|
||||
unreachable();
|
||||
};
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
if (rel.r_type == R_NONE || rel.r_type == R_ARM_V4BX)
|
||||
@@ -272,13 +297,6 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
u8 *loc = base + rel.r_offset;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
if (val < lo || hi <= val)
|
||||
Error(ctx) << *this << ": relocation " << rel << " against "
|
||||
<< sym << " out of range: " << val << " is not in ["
|
||||
<< lo << ", " << hi << ")";
|
||||
};
|
||||
|
||||
u64 S = sym.get_addr(ctx);
|
||||
u64 A = get_addend(*this, rel);
|
||||
u64 P = get_addr() + rel.r_offset;
|
||||
@@ -286,11 +304,20 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
|
||||
u64 GOT = ctx.got->shdr.sh_addr;
|
||||
|
||||
auto get_thumb_thunk_addr = [&] { return get_thunk_addr(i); };
|
||||
auto get_arm_thunk_addr = [&] { return get_thunk_addr(i) + 4; };
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
check_range(ctx, i, val, lo, hi);
|
||||
};
|
||||
|
||||
auto get_thumb_thunk_addr = [&] { return sym.get_thunk_addr(ctx, P); };
|
||||
auto get_arm_thunk_addr = [&] { return sym.get_thunk_addr(ctx, P) + 4; };
|
||||
|
||||
auto get_tlsdesc_trampoline_addr = [&] {
|
||||
return get_reachable_thunk(*output_section, P).get_addr();
|
||||
};
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_ARM_ABS32:
|
||||
case R_ARM_TARGET1:
|
||||
break;
|
||||
case R_ARM_REL32:
|
||||
*(ul32 *)loc = S + A - P;
|
||||
@@ -303,21 +330,21 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
break;
|
||||
}
|
||||
|
||||
// THM_CALL relocation refers either BL or BLX instruction.
|
||||
// THM_CALL relocation refers to either BL or BLX instruction.
|
||||
// They are different in only one bit. We need to use BL if
|
||||
// the jump target is Thumb. Otherwise, use BLX.
|
||||
i64 val = S + A - P;
|
||||
if (is_jump_reachable(val)) {
|
||||
if (T) {
|
||||
write_thm_b_imm(loc, val);
|
||||
*(ul16 *)(loc + 2) |= 0x1000; // rewrite to BL
|
||||
} else {
|
||||
write_thm_b_imm(loc, align_to(val, 4));
|
||||
*(ul16 *)(loc + 2) &= ~0x1000; // rewrite to BLX
|
||||
}
|
||||
i64 val1 = S + A - P;
|
||||
i64 val2 = align_to(S + A - P, 4);
|
||||
|
||||
if (T && is_int(val1, 25)) {
|
||||
*(ul16 *)(loc + 2) |= 0x1000; // BL
|
||||
write_thm_b25(loc, val1);
|
||||
} else if (!T && is_int(val2, 25)) {
|
||||
*(ul16 *)(loc + 2) &= ~0x1000; // BLX
|
||||
write_thm_b25(loc, val2);
|
||||
} else {
|
||||
write_thm_b_imm(loc, align_to(get_arm_thunk_addr() + A - P, 4));
|
||||
*(ul16 *)(loc + 2) &= ~0x1000; // rewrite to BLX
|
||||
*(ul16 *)(loc + 2) |= 0x1000; // BL
|
||||
write_thm_b25(loc, get_thumb_thunk_addr() + A - P);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -347,8 +374,8 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
if (!is_bl && !is_blx)
|
||||
Fatal(ctx) << *this << ": R_ARM_CALL refers to neither BL nor BLX";
|
||||
|
||||
u64 val = S + A - P;
|
||||
if (is_jump_reachable(val)) {
|
||||
i64 val = S + A - P;
|
||||
if (is_int(val, 26)) {
|
||||
if (T) {
|
||||
*(ul32 *)loc = 0xfa00'0000; // BLX
|
||||
*(ul32 *)loc |= (bit(val, 1) << 24) | bits(val, 25, 2);
|
||||
@@ -374,8 +401,8 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
// immediate; it takes only a register. So if mode switch is
|
||||
// required, we jump to a linker-synthesized thunk which does the
|
||||
// job with a longer code sequence.
|
||||
u64 val = S + A - P;
|
||||
if (!is_jump_reachable(val) || T)
|
||||
i64 val = S + A - P;
|
||||
if (T || !is_int(val, 26))
|
||||
val = get_arm_thunk_addr() + A - P;
|
||||
*(ul32 *)loc = (*(ul32 *)loc & 0xff00'0000) | bits(val, 25, 2);
|
||||
break;
|
||||
@@ -388,30 +415,20 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
*(ul32 *)loc = (*(ul32 *)loc & 0xff00'0000) | bits(val, 25, 2);
|
||||
}
|
||||
break;
|
||||
case R_ARM_THM_JUMP8:
|
||||
check(S + A - P, -(1 << 8), 1 << 8);
|
||||
*(ul16 *)loc &= 0xff00;
|
||||
*(ul16 *)loc |= bits(S + A - P, 8, 1);
|
||||
break;
|
||||
case R_ARM_THM_JUMP11:
|
||||
assert(T);
|
||||
check(S + A - P, -(1 << 11), 1 << 11);
|
||||
*(ul16 *)loc &= 0xf800;
|
||||
*(ul16 *)loc |= bits(S + A - P, 11, 1);
|
||||
break;
|
||||
case R_ARM_THM_JUMP19: {
|
||||
i64 val = S + A - P;
|
||||
check(val, -(1 << 19), 1 << 19);
|
||||
|
||||
// sign:J2:J1:imm6:imm11:'0'
|
||||
u32 sign = bit(val, 20);
|
||||
u32 J2 = bit(val, 19);
|
||||
u32 J1 = bit(val, 18);
|
||||
u32 imm6 = bits(val, 17, 12);
|
||||
u32 imm11 = bits(val, 11, 1);
|
||||
|
||||
*(ul16 *)loc &= 0b1111'1011'1100'0000;
|
||||
*(ul16 *)loc |= (sign << 10) | imm6;
|
||||
|
||||
*(ul16 *)(loc + 2) &= 0b1101'0000'0000'0000;
|
||||
*(ul16 *)(loc + 2) |= (J2 << 13) | (J1 << 11) | imm11;
|
||||
case R_ARM_THM_JUMP19:
|
||||
check(S + A - P, -(1 << 20), 1 << 20);
|
||||
write_thm_b21(loc, S + A - P);
|
||||
break;
|
||||
}
|
||||
case R_ARM_THM_JUMP24: {
|
||||
if (sym.is_remaining_undef_weak()) {
|
||||
*(ul32 *)loc = 0x8000'f3af; // NOP
|
||||
@@ -420,20 +437,20 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
|
||||
// Just like R_ARM_JUMP24, we need to jump to a thunk if we need to
|
||||
// switch processor mode.
|
||||
u64 val = S + A - P;
|
||||
if (!is_jump_reachable(val) || !T)
|
||||
i64 val = S + A - P;
|
||||
if (!T || !is_int(val, 25))
|
||||
val = get_thumb_thunk_addr() + A - P;
|
||||
write_thm_b_imm(loc, val);
|
||||
write_thm_b25(loc, val);
|
||||
break;
|
||||
}
|
||||
case R_ARM_MOVW_PREL_NC:
|
||||
write_mov_imm(loc, ((S + A) | T) - P);
|
||||
write_arm_mov(loc, ((S + A) | T) - P);
|
||||
break;
|
||||
case R_ARM_MOVW_ABS_NC:
|
||||
write_mov_imm(loc, (S + A) | T);
|
||||
write_arm_mov(loc, (S + A) | T);
|
||||
break;
|
||||
case R_ARM_THM_MOVW_PREL_NC:
|
||||
write_thm_mov_imm(loc, ((S + A) | T) - P);
|
||||
write_thm_mov(loc, ((S + A) | T) - P);
|
||||
break;
|
||||
case R_ARM_PREL31:
|
||||
check(S + A - P, -(1LL << 30), 1LL << 30);
|
||||
@@ -441,19 +458,19 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
*(ul32 *)loc |= (S + A - P) & 0x7fff'ffff;
|
||||
break;
|
||||
case R_ARM_THM_MOVW_ABS_NC:
|
||||
write_thm_mov_imm(loc, (S + A) | T);
|
||||
write_thm_mov(loc, (S + A) | T);
|
||||
break;
|
||||
case R_ARM_MOVT_PREL:
|
||||
write_mov_imm(loc, (S + A - P) >> 16);
|
||||
write_arm_mov(loc, (S + A - P) >> 16);
|
||||
break;
|
||||
case R_ARM_THM_MOVT_PREL:
|
||||
write_thm_mov_imm(loc, (S + A - P) >> 16);
|
||||
write_thm_mov(loc, (S + A - P) >> 16);
|
||||
break;
|
||||
case R_ARM_MOVT_ABS:
|
||||
write_mov_imm(loc, (S + A) >> 16);
|
||||
write_arm_mov(loc, (S + A) >> 16);
|
||||
break;
|
||||
case R_ARM_THM_MOVT_ABS:
|
||||
write_thm_mov_imm(loc, (S + A) >> 16);
|
||||
write_thm_mov(loc, (S + A) >> 16);
|
||||
break;
|
||||
case R_ARM_TLS_GD32:
|
||||
*(ul32 *)loc = sym.get_tlsgd_addr(ctx) + A - P;
|
||||
@@ -506,8 +523,8 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
break;
|
||||
case R_ARM_TLS_CALL:
|
||||
if (sym.has_tlsdesc(ctx)) {
|
||||
// BL <tls_trampoline>
|
||||
*(ul32 *)loc = 0xeb00'0000 | bits(get_tls_trampoline_addr(P + 8), 25, 2);
|
||||
*(ul32 *)loc = 0xeb00'0000; // bl 0
|
||||
*(ul32 *)loc |= bits(get_tlsdesc_trampoline_addr() - P - 8, 25, 2);
|
||||
} else if (sym.has_gottp(ctx)) {
|
||||
*(ul32 *)loc = 0xe79f'0000; // ldr r0, [pc, r0]
|
||||
} else {
|
||||
@@ -516,8 +533,8 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
break;
|
||||
case R_ARM_THM_TLS_CALL:
|
||||
if (sym.has_tlsdesc(ctx)) {
|
||||
u64 val = align_to(get_tls_trampoline_addr(P + 4), 4);
|
||||
write_thm_b_imm(loc, val);
|
||||
u64 val = align_to(get_tlsdesc_trampoline_addr() - P - 4, 4);
|
||||
write_thm_b25(loc, val);
|
||||
*(ul16 *)(loc + 2) &= ~0x1000; // rewrite BL with BLX
|
||||
} else if (sym.has_gottp(ctx)) {
|
||||
// Since `ldr r0, [pc, r0]` is not representable in Thumb,
|
||||
@@ -630,11 +647,13 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
check_tlsle(ctx, sym, rel);
|
||||
break;
|
||||
case R_ARM_ABS32:
|
||||
case R_ARM_TARGET1:
|
||||
case R_ARM_MOVT_ABS:
|
||||
case R_ARM_THM_MOVT_ABS:
|
||||
case R_ARM_REL32:
|
||||
case R_ARM_BASE_PREL:
|
||||
case R_ARM_GOTOFF32:
|
||||
case R_ARM_THM_JUMP8:
|
||||
case R_ARM_THM_JUMP11:
|
||||
case R_ARM_THM_JUMP19:
|
||||
case R_ARM_MOVW_PREL_NC:
|
||||
@@ -663,7 +682,7 @@ void Thunk<E>::copy_buf(Context<E> &ctx) {
|
||||
|
||||
// This is a range extension and mode switch thunk.
|
||||
// It has two entry points: +0 for Thumb and +4 for ARM.
|
||||
static const u8 entry[] = {
|
||||
constexpr u8 entry[] = {
|
||||
// .thumb
|
||||
0x78, 0x47, // bx pc # jumps to 1f
|
||||
0xc0, 0x46, // nop
|
||||
@@ -715,7 +734,7 @@ void create_arm_exidx_section(Context<E> &ctx) {
|
||||
|
||||
void Arm32ExidxSection::compute_section_size(Context<E> &ctx) {
|
||||
output_section.compute_section_size(ctx);
|
||||
this->shdr.sh_size = output_section.shdr.sh_size;
|
||||
this->shdr.sh_size = output_section.shdr.sh_size + 8; // +8 for sentinel
|
||||
}
|
||||
|
||||
void Arm32ExidxSection::update_shdr(Context<E> &ctx) {
|
||||
@@ -735,6 +754,15 @@ void Arm32ExidxSection::copy_buf(Context<E> &ctx) {
|
||||
write_vector(ctx.buf + this->shdr.sh_offset, contents);
|
||||
}
|
||||
|
||||
// Returns the end of the text segment
|
||||
static u64 get_text_end(Context<E> &ctx) {
|
||||
u64 ret = 0;
|
||||
for (Chunk<E> *chunk : ctx.chunks)
|
||||
if (chunk->shdr.sh_flags & SHF_EXECINSTR)
|
||||
ret = std::max<u64>(ret, chunk->shdr.sh_addr + chunk->shdr.sh_size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// ARM executables use an .ARM.exidx section to look up an exception
|
||||
// handling record for the current instruction pointer. The table needs
|
||||
// to be sorted by their addresses.
|
||||
@@ -745,11 +773,6 @@ void Arm32ExidxSection::copy_buf(Context<E> &ctx) {
|
||||
//
|
||||
// This function returns contents of .ARM.exidx.
|
||||
std::vector<u8> Arm32ExidxSection::get_contents(Context<E> &ctx) {
|
||||
std::vector<u8> buf(output_section.shdr.sh_size);
|
||||
|
||||
output_section.shdr.sh_addr = this->shdr.sh_addr;
|
||||
output_section.write_to(ctx, buf.data(), nullptr);
|
||||
|
||||
// .ARM.exidx records consists of a signed 31-bit relative address
|
||||
// and a 32-bit value. The relative address indicates the start
|
||||
// address of a function that the record covers. The value is one of
|
||||
@@ -769,11 +792,19 @@ std::vector<u8> Arm32ExidxSection::get_contents(Context<E> &ctx) {
|
||||
ul32 val;
|
||||
};
|
||||
|
||||
if (buf.size() % sizeof(Entry))
|
||||
Fatal(ctx) << "invalid .ARM.exidx section size";
|
||||
|
||||
// We reserve one extra slot for the sentinel
|
||||
i64 num_entries = output_section.shdr.sh_size / sizeof(Entry) + 1;
|
||||
std::vector<u8> buf(num_entries * sizeof(Entry));
|
||||
Entry *ent = (Entry *)buf.data();
|
||||
i64 num_entries = buf.size() / sizeof(Entry);
|
||||
|
||||
// Write section contents to the buffer
|
||||
output_section.shdr.sh_addr = this->shdr.sh_addr;
|
||||
output_section.write_to(ctx, buf.data());
|
||||
|
||||
// Fill in sentinel fields
|
||||
u64 sentinel_addr = this->shdr.sh_addr + sizeof(Entry) * (num_entries - 1);
|
||||
ent[num_entries - 1].addr = get_text_end(ctx) - sentinel_addr;
|
||||
ent[num_entries - 1].val = CANTUNWIND;
|
||||
|
||||
// Entry's addresses are relative to themselves. In order to sort
|
||||
// records by address, we first translate them so that the addresses
|
||||
@@ -784,7 +815,7 @@ std::vector<u8> Arm32ExidxSection::get_contents(Context<E> &ctx) {
|
||||
|
||||
tbb::parallel_for((i64)0, num_entries, [&](i64 i) {
|
||||
i64 offset = sizeof(Entry) * i;
|
||||
ent[i].addr = sign_extend(ent[i].addr, 30) + offset;
|
||||
ent[i].addr = sign_extend(ent[i].addr, 31) + offset;
|
||||
if (is_relative(ent[i].val))
|
||||
ent[i].val = 0x7fff'ffff & (ent[i].val + offset);
|
||||
});
|
||||
|
||||
+15
-23
@@ -155,19 +155,16 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
u8 *loc = base + rel.r_offset;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
if (val < lo || hi <= val)
|
||||
Error(ctx) << *this << ": relocation " << rel << " against "
|
||||
<< sym << " out of range: " << val << " is not in ["
|
||||
<< lo << ", " << hi << ")";
|
||||
};
|
||||
|
||||
u64 S = sym.get_addr(ctx);
|
||||
u64 A = rel.r_addend;
|
||||
u64 P = get_addr() + rel.r_offset;
|
||||
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
|
||||
u64 GOT = ctx.got->shdr.sh_addr;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
check_range(ctx, i, val, lo, hi);
|
||||
};
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_AARCH64_ABS64:
|
||||
break;
|
||||
@@ -238,7 +235,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
i + 1 < rels.size()) {
|
||||
i64 val = S + A - P - 4;
|
||||
const ElfRel<E> &rel2 = rels[i + 1];
|
||||
if (sign_extend(val, 20) == val &&
|
||||
if (is_int(val, 21) &&
|
||||
rel2.r_type == R_AARCH64_ADD_ABS_LO12_NC &&
|
||||
rel2.r_sym == rel.r_sym &&
|
||||
rel2.r_offset == rel.r_offset + 4 &&
|
||||
@@ -277,8 +274,8 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
}
|
||||
|
||||
i64 val = S + A - P;
|
||||
if (val < -(1 << 27) || (1 << 27) <= val)
|
||||
val = get_thunk_addr(i) + A - P;
|
||||
if (!is_int(val, 28))
|
||||
val = sym.get_thunk_addr(ctx, P) + A - P;
|
||||
*(ul32 *)loc |= bits(val, 27, 2);
|
||||
break;
|
||||
}
|
||||
@@ -292,7 +289,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
*(ul32 *)loc |= bits(S + A - P, 20, 2) << 5;
|
||||
break;
|
||||
case R_AARCH64_PREL16:
|
||||
check(S + A - P, -(1LL << 15), 1LL << 15);
|
||||
check(S + A - P, -(1LL << 15), 1LL << 16);
|
||||
*(U16<E> *)loc = S + A - P;
|
||||
break;
|
||||
case R_AARCH64_PREL32:
|
||||
@@ -448,13 +445,6 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
u8 *loc = base + rel.r_offset;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
if (val < lo || hi <= val)
|
||||
Error(ctx) << *this << ": relocation " << rel << " against "
|
||||
<< sym << " out of range: " << val << " is not in ["
|
||||
<< lo << ", " << hi << ")";
|
||||
};
|
||||
|
||||
SectionFragment<E> *frag;
|
||||
i64 frag_addend;
|
||||
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
|
||||
@@ -462,6 +452,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
|
||||
u64 A = frag ? frag_addend : (i64)rel.r_addend;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
check_range(ctx, val, i, lo, hi);
|
||||
};
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_AARCH64_ABS64:
|
||||
if (std::optional<u64> val = get_tombstone(sym, frag))
|
||||
@@ -469,12 +463,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
else
|
||||
*(U64<E> *)loc = S + A;
|
||||
break;
|
||||
case R_AARCH64_ABS32: {
|
||||
i64 val = S + A;
|
||||
check(val, 0, 1LL << 32);
|
||||
*(U32<E> *)loc = val;
|
||||
case R_AARCH64_ABS32:
|
||||
check(S + A, 0, 1LL << 32);
|
||||
*(U32<E> *)loc = S + A;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
|
||||
<< rel;
|
||||
|
||||
+38
-46
@@ -108,25 +108,24 @@ template <>
|
||||
void write_plt_header(Context<E> &ctx, u8 *buf) {
|
||||
if (ctx.arg.pic) {
|
||||
static const u8 insn[] = {
|
||||
0xf3, 0x0f, 0x1e, 0xfb, // endbr32
|
||||
0x51, // push %ecx
|
||||
0x8d, 0x8b, 0, 0, 0, 0, // lea GOTPLT+4(%ebx), %ecx
|
||||
0xff, 0x31, // push (%ecx)
|
||||
0xff, 0x61, 0x04, // jmp *0x4(%ecx)
|
||||
0xcc, 0xcc, 0xcc, 0xcc, // (padding)
|
||||
};
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(ul32 *)(buf + 7) = ctx.gotplt->shdr.sh_addr - ctx.got->shdr.sh_addr + 4;
|
||||
*(ul32 *)(buf + 3) = ctx.gotplt->shdr.sh_addr - ctx.got->shdr.sh_addr + 4;
|
||||
} else {
|
||||
static const u8 insn[] = {
|
||||
0xf3, 0x0f, 0x1e, 0xfb, // endbr32
|
||||
0x51, // push %ecx
|
||||
0xb9, 0, 0, 0, 0, // mov GOTPLT+4, %ecx
|
||||
0xff, 0x31, // push (%ecx)
|
||||
0xff, 0x61, 0x04, // jmp *0x4(%ecx)
|
||||
0xcc, // (padding)
|
||||
0x51, // push %ecx
|
||||
0xb9, 0, 0, 0, 0, // mov GOTPLT+4, %ecx
|
||||
0xff, 0x31, // push (%ecx)
|
||||
0xff, 0x61, 0x04, // jmp *0x4(%ecx)
|
||||
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // (padding)
|
||||
};
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(ul32 *)(buf + 6) = ctx.gotplt->shdr.sh_addr + 4;
|
||||
*(ul32 *)(buf + 2) = ctx.gotplt->shdr.sh_addr + 4;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,24 +133,22 @@ template <>
|
||||
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
if (ctx.arg.pic) {
|
||||
static const u8 insn[] = {
|
||||
0xf3, 0x0f, 0x1e, 0xfb, // endbr32
|
||||
0xb9, 0, 0, 0, 0, // mov $reloc_offset, %ecx
|
||||
0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx)
|
||||
0xcc, // (padding)
|
||||
0xb9, 0, 0, 0, 0, // mov $reloc_offset, %ecx
|
||||
0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx)
|
||||
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // (padding)
|
||||
};
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(ul32 *)(buf + 5) = sym.get_plt_idx(ctx) * sizeof(ElfRel<E>);
|
||||
*(ul32 *)(buf + 11) = sym.get_gotplt_addr(ctx) - ctx.got->shdr.sh_addr;
|
||||
*(ul32 *)(buf + 1) = sym.get_plt_idx(ctx) * sizeof(ElfRel<E>);
|
||||
*(ul32 *)(buf + 7) = sym.get_gotplt_addr(ctx) - ctx.got->shdr.sh_addr;
|
||||
} else {
|
||||
static const u8 insn[] = {
|
||||
0xf3, 0x0f, 0x1e, 0xfb, // endbr32
|
||||
0xb9, 0, 0, 0, 0, // mov $reloc_offset, %ecx
|
||||
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
|
||||
0xcc, // (padding)
|
||||
0xb9, 0, 0, 0, 0, // mov $reloc_offset, %ecx
|
||||
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
|
||||
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // (padding)
|
||||
};
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(ul32 *)(buf + 5) = sym.get_plt_idx(ctx) * sizeof(ElfRel<E>);
|
||||
*(ul32 *)(buf + 11) = sym.get_gotplt_addr(ctx);
|
||||
*(ul32 *)(buf + 1) = sym.get_plt_idx(ctx) * sizeof(ElfRel<E>);
|
||||
*(ul32 *)(buf + 7) = sym.get_gotplt_addr(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,20 +156,18 @@ template <>
|
||||
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
if (ctx.arg.pic) {
|
||||
static const u8 insn[] = {
|
||||
0xf3, 0x0f, 0x1e, 0xfb, // endbr32
|
||||
0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx)
|
||||
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // (padding)
|
||||
0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx)
|
||||
0xcc, 0xcc, // (padding)
|
||||
};
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(ul32 *)(buf + 6) = sym.get_got_pltgot_addr(ctx) - ctx.got->shdr.sh_addr;
|
||||
*(ul32 *)(buf + 2) = sym.get_got_pltgot_addr(ctx) - ctx.got->shdr.sh_addr;
|
||||
} else {
|
||||
static const u8 insn[] = {
|
||||
0xf3, 0x0f, 0x1e, 0xfb, // endbr32
|
||||
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
|
||||
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // (padding)
|
||||
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
|
||||
0xcc, 0xcc, // (padding)
|
||||
};
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(ul32 *)(buf + 6) = sym.get_got_pltgot_addr(ctx);
|
||||
*(ul32 *)(buf + 2) = sym.get_got_pltgot_addr(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,11 +237,10 @@ static void relax_ld_to_le(u8 *loc, ElfRel<E> rel, u64 tls_size) {
|
||||
case R_386_GOT32X: {
|
||||
static const u8 insn[] = {
|
||||
0x65, 0xa1, 0, 0, 0, 0, // mov %gs:0, %eax
|
||||
0x2d, 0, 0, 0, 0, // sub $tls_size, %eax
|
||||
0x90, // nop
|
||||
0x81, 0xe8, 0, 0, 0, 0, // sub $tls_size, %eax
|
||||
};
|
||||
memcpy(loc - 2, insn, sizeof(insn));
|
||||
*(ul32 *)(loc + 5) = tls_size;
|
||||
*(ul32 *)(loc + 6) = tls_size;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -294,19 +288,16 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
u8 *loc = base + rel.r_offset;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
if (val < lo || hi <= val)
|
||||
Error(ctx) << *this << ": relocation " << rel << " against "
|
||||
<< sym << " out of range: " << val << " is not in ["
|
||||
<< lo << ", " << hi << ")";
|
||||
};
|
||||
|
||||
u64 S = sym.get_addr(ctx);
|
||||
u64 A = get_addend(*this, rel);
|
||||
u64 P = get_addr() + rel.r_offset;
|
||||
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
|
||||
u64 GOT = ctx.got->shdr.sh_addr;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
check_range(ctx, i, val, lo, hi);
|
||||
};
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_386_8:
|
||||
check(S + A, 0, 1 << 8);
|
||||
@@ -406,6 +397,10 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
// mov %reg, %eax
|
||||
// call *(%eax)
|
||||
// R_386_TLS_DESC_CALL foo
|
||||
//
|
||||
// Note that the compiler always uses the local-exec TLS model
|
||||
// for -fno-pic, so TLSDESC code is always PIC (i.e. uses %ebx to
|
||||
// store the address of GOT.)
|
||||
if (sym.has_tlsdesc(ctx)) {
|
||||
*(ul32 *)loc = sym.get_tlsdesc_addr(ctx) + A - GOT;
|
||||
} else if (sym.has_gottp(ctx)) {
|
||||
@@ -449,13 +444,6 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
u8 *loc = base + rel.r_offset;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
if (val < lo || hi <= val)
|
||||
Error(ctx) << *this << ": relocation " << rel << " against "
|
||||
<< sym << " out of range: " << val << " is not in ["
|
||||
<< lo << ", " << hi << ")";
|
||||
};
|
||||
|
||||
SectionFragment<E> *frag;
|
||||
i64 frag_addend;
|
||||
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
|
||||
@@ -464,6 +452,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
u64 A = frag ? frag_addend : get_addend(*this, rel);
|
||||
u64 GOT = ctx.got->shdr.sh_addr;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
check_range(ctx, val, i, lo, hi);
|
||||
};
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_386_8:
|
||||
check(S + A, 0, 1 << 8);
|
||||
|
||||
+105
-104
@@ -51,7 +51,7 @@ static u64 hi20(u64 val, u64 pc) {
|
||||
return bits(page(val + 0x800) - page(pc), 31, 12);
|
||||
}
|
||||
|
||||
static u64 higher20(u64 val, u64 pc) {
|
||||
static u64 higher(u64 val, u64 pc) {
|
||||
// A PC-relative 64-bit address is materialized with the following
|
||||
// instructions for the large code model:
|
||||
//
|
||||
@@ -68,12 +68,15 @@ static u64 higher20(u64 val, u64 pc) {
|
||||
// Compensating all the sign-extensions is a bit complicated. The
|
||||
// psABI gave the following formula.
|
||||
val = val + 0x8000'0000 + ((val & 0x800) ? (0x1000 - 0x1'0000'0000) : 0);
|
||||
return bits(page(val) - page(pc - 8), 51, 32);
|
||||
return page(val) - page(pc - 8);
|
||||
}
|
||||
|
||||
static u64 higher20(u64 val, u64 pc) {
|
||||
return bits(higher(val, pc), 51, 32);
|
||||
}
|
||||
|
||||
static u64 highest12(u64 val, u64 pc) {
|
||||
val = val + 0x8000'0000 + ((val & 0x800) ? (0x1000 - 0x1'0000'0000) : 0);
|
||||
return bits(page(val) - page(pc - 12), 63, 52);
|
||||
return bits(higher(val, pc), 63, 52);
|
||||
}
|
||||
|
||||
static void write_k12(u8 *loc, u32 val) {
|
||||
@@ -125,20 +128,22 @@ static void set_rj(u8 *loc, u32 rj) {
|
||||
// Returns true if isec's i'th relocation refers to the following
|
||||
// relaxable instructioon pair.
|
||||
//
|
||||
// pcalau12i $t0, 0 # R_LARCH_GOT_PC_HI20
|
||||
// ld.d $t0, $t0, 0 # R_LARCH_GOT_PC_LO12
|
||||
// pcalau12i $t0, 0 # R_LARCH_GOT_PC_HI20, R_LARCH_RELAX
|
||||
// ld.d $t0, $t0, 0 # R_LARCH_GOT_PC_LO12, R_LARCH_RELAX
|
||||
static bool is_relaxable_got_load(Context<E> &ctx, InputSection<E> &isec, i64 i) {
|
||||
std::span<const ElfRel<E>> rels = isec.get_rels(ctx);
|
||||
Symbol<E> &sym = *isec.file.symbols[rels[i].r_sym];
|
||||
u8 *buf = (u8 *)isec.contents.data();
|
||||
|
||||
if (ctx.arg.relax &&
|
||||
sym.is_pcrel_linktime_const(ctx) &&
|
||||
i + 3 < rels.size() &&
|
||||
rels[i + 1].r_type == R_LARCH_RELAX &&
|
||||
rels[i + 2].r_type == R_LARCH_GOT_PC_LO12 &&
|
||||
rels[i + 2].r_offset == rels[i].r_offset + 4 &&
|
||||
rels[i + 3].r_type == R_LARCH_RELAX) {
|
||||
u32 insn1 = *(ul32 *)(isec.contents.data() + rels[i].r_offset);
|
||||
u32 insn2 = *(ul32 *)(isec.contents.data() + rels[i].r_offset + 4);
|
||||
u32 insn1 = *(ul32 *)(buf + rels[i].r_offset);
|
||||
u32 insn2 = *(ul32 *)(buf + rels[i].r_offset + 4);
|
||||
bool is_ld_d = (insn2 & 0xffc0'0000) == 0x28c0'0000;
|
||||
return get_rd(insn1) == get_rd(insn2) && get_rd(insn2) == get_rj(insn2) &&
|
||||
is_ld_d;
|
||||
@@ -179,14 +184,14 @@ void write_plt_header<E>(Context<E> &ctx, u8 *buf) {
|
||||
write_k12(buf + 16, gotplt);
|
||||
}
|
||||
|
||||
constexpr ul32 plt_entry_64[] = {
|
||||
static constexpr ul32 plt_entry_64[] = {
|
||||
0x1a00'000f, // pcalau12i $t3, %pc_hi20([email protected])
|
||||
0x28c0'01ef, // ld.d $t3, $t3, %lo12([email protected])
|
||||
0x4c00'01ed, // jirl $t1, $t3, 0
|
||||
0x002a'0000, // break
|
||||
};
|
||||
|
||||
constexpr ul32 plt_entry_32[] = {
|
||||
static constexpr ul32 plt_entry_32[] = {
|
||||
0x1a00'000f, // pcalau12i $t3, %pc_hi20([email protected])
|
||||
0x2880'01ef, // ld.w $t3, $t3, %lo12([email protected])
|
||||
0x4c00'01ed, // jirl $t1, $t3, 0
|
||||
@@ -265,10 +270,9 @@ void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
|
||||
template <>
|
||||
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
auto get_r_delta = [&](i64 idx) {
|
||||
return extra.r_deltas.empty() ? 0 : extra.r_deltas[idx];
|
||||
};
|
||||
std::span<RelocDelta> deltas = extra.r_deltas;
|
||||
i64 k = 0;
|
||||
u8 *buf = (u8 *)contents.data();
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
@@ -278,25 +282,22 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
rel.r_type == R_LARCH_ALIGN)
|
||||
continue;
|
||||
|
||||
i64 removed_bytes = 0;
|
||||
i64 r_delta = 0;
|
||||
|
||||
if (!deltas.empty()) {
|
||||
while (k < deltas.size() && deltas[k].offset < rel.r_offset)
|
||||
k++;
|
||||
if (k < deltas.size() && deltas[k].offset == rel.r_offset)
|
||||
removed_bytes = get_removed_bytes(deltas, k);
|
||||
if (k > 0)
|
||||
r_delta = deltas[k - 1].delta;
|
||||
}
|
||||
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
i64 r_offset = rel.r_offset - get_r_delta(i);
|
||||
i64 removed_bytes = get_r_delta(i + 1) - get_r_delta(i);
|
||||
i64 r_offset = rel.r_offset - r_delta;
|
||||
u8 *loc = base + r_offset;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
if (val < lo || hi <= val)
|
||||
Error(ctx) << *this << ": relocation " << rel << " against "
|
||||
<< sym << " out of range: " << val << " is not in ["
|
||||
<< lo << ", " << hi << ")";
|
||||
};
|
||||
|
||||
auto check_branch = [&](i64 val, i64 lo, i64 hi) {
|
||||
if (val & 0b11)
|
||||
Error(ctx) << *this << ": misaligned symbol " << sym
|
||||
<< " for relocation " << rel;
|
||||
check(val, lo, hi);
|
||||
};
|
||||
|
||||
// Unlike other psABIs, the LoongArch ABI uses the same relocation
|
||||
// types to refer to GOT entries for thread-local symbols and regular
|
||||
// ones. Therefore, G may refer to a TLSGD or a regular GOT slot
|
||||
@@ -320,10 +321,21 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
u64 G = got_idx * sizeof(Word<E>);
|
||||
u64 GOT = ctx.got->shdr.sh_addr;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
check_range(ctx, i, val, lo, hi);
|
||||
};
|
||||
|
||||
auto check_branch = [&](i64 val, i64 lo, i64 hi) {
|
||||
check(val, lo, hi);
|
||||
if (val & 0b11)
|
||||
Error(ctx) << *this << ": misaligned symbol " << sym
|
||||
<< " for relocation " << rel;
|
||||
};
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_LARCH_32:
|
||||
if constexpr (E::is_64)
|
||||
*(ul32 *)loc = S + A;
|
||||
assert(E::is_64);
|
||||
*(ul32 *)loc = S + A;
|
||||
break;
|
||||
case R_LARCH_B16:
|
||||
check_branch(S + A - P, -(1 << 17), 1 << 17);
|
||||
@@ -355,7 +367,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
// It is contrary to the psABI document, but GNU ld has special
|
||||
// code to handle it, so we accept it too.
|
||||
if ((*(ul32 *)loc & 0xfc00'0000) == 0x4c00'0000)
|
||||
write_k16(loc, sign_extend(S + A, 11) >> 2);
|
||||
write_k16(loc, sign_extend(S + A, 12) >> 2);
|
||||
else
|
||||
write_k12(loc, S + A);
|
||||
break;
|
||||
@@ -393,7 +405,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
// addi.d $t0, $t0, 0
|
||||
if (is_relaxable_got_load(ctx, *this, i)) {
|
||||
i64 dist = compute_distance(ctx, sym, *this, rel);
|
||||
if (-(1LL << 31) <= dist && dist < (1LL << 31)) {
|
||||
if (is_int(dist, 32)) {
|
||||
u32 rd = get_rd(*(ul32 *)loc);
|
||||
*(ul32 *)(loc + 4) = 0x02c0'0000 | (rd << 5) | rd; // addi.d
|
||||
|
||||
@@ -513,12 +525,14 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
break;
|
||||
case R_LARCH_CALL36:
|
||||
if (removed_bytes == 0) {
|
||||
write_j20(loc, (S + A - P + 0x20000) >> 18);
|
||||
write_k16(loc + 4, (S + A - P) >> 2);
|
||||
i64 val = S + A - P;
|
||||
check_branch(val, -(1LL << 37) - 0x20000, (1LL << 37) - 0x20000);
|
||||
write_j20(loc, (val + 0x20000) >> 18);
|
||||
write_k16(loc + 4, val >> 2);
|
||||
} else {
|
||||
// Rewrite PCADDU18I + JIRL to B or BL
|
||||
assert(removed_bytes == 4);
|
||||
if (get_rd(*(ul32 *)(contents.data() + rel.r_offset + 4)) == 0)
|
||||
if (get_rd(*(ul32 *)(buf + rel.r_offset + 4)) == 0)
|
||||
*(ul32 *)loc = 0x5000'0000; // B
|
||||
else
|
||||
*(ul32 *)loc = 0x5400'0000; // BL
|
||||
@@ -547,63 +561,44 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
// We may relax the instructions to the following if its TP-relative
|
||||
// address is known at link-time
|
||||
//
|
||||
// <nop>
|
||||
// <nop>
|
||||
// <deleted>
|
||||
// <deleted>
|
||||
// lu12i.w $a0, foo@TPOFF
|
||||
// addi.w $a0, $a0, foo@TPOFF
|
||||
//
|
||||
// or to the following if the TP offset is small enough.
|
||||
//
|
||||
// <nop>
|
||||
// <nop>
|
||||
// <nop>
|
||||
// <deleted>
|
||||
// <deleted>
|
||||
// <deleted>
|
||||
// ori $a0, $zero, foo@TPOFF
|
||||
//
|
||||
// If the TP-relative address is known at process startup time, we
|
||||
// may relax the instructions to the following.
|
||||
//
|
||||
// <nop>
|
||||
// <nop>
|
||||
// <deleted>
|
||||
// <deleted>
|
||||
// pcalau12i $a0, foo@GOTTP
|
||||
// ld.[dw] $a0, $a0, foo@GOTTP
|
||||
//
|
||||
// If we don't know anything about the symbol, we can still relax
|
||||
// the first two instructions to a single pcaddi as shown below.
|
||||
//
|
||||
// <nop>
|
||||
// <deleted>
|
||||
// pcaddi $a0, foo@GOTDESC
|
||||
// ld.d $ra, $a0, 0
|
||||
// jirl $ra, $ra, 0
|
||||
//
|
||||
// Note that if section-shrinking relaxation is enabled, nop may be
|
||||
// completely deleted.
|
||||
if (removed_bytes == 0) {
|
||||
if (sym.has_tlsdesc(ctx)) {
|
||||
i64 dist = sym.get_tlsdesc_addr(ctx) + A - P;
|
||||
if (ctx.arg.relax && -(1 << 21) <= dist && dist < (1 << 21)) {
|
||||
*(ul32 *)loc = 0x0340'0000; // nop
|
||||
} else {
|
||||
write_j20(loc, hi20(sym.get_tlsdesc_addr(ctx) + A, P));
|
||||
}
|
||||
} else {
|
||||
*(ul32 *)loc = 0x0340'0000; // nop
|
||||
}
|
||||
}
|
||||
if (sym.has_tlsdesc(ctx) && removed_bytes == 0)
|
||||
write_j20(loc, hi20(sym.get_tlsdesc_addr(ctx) + A, P));
|
||||
break;
|
||||
case R_LARCH_TLS_DESC_PC_LO12:
|
||||
if (removed_bytes == 0) {
|
||||
if (sym.has_tlsdesc(ctx)) {
|
||||
i64 dist = sym.get_tlsdesc_addr(ctx) + A - P;
|
||||
if (ctx.arg.relax && -(1 << 21) <= dist && dist < (1 << 21)) {
|
||||
// If we can directly materialize the PC-relative address
|
||||
// with pcaddi, do that.
|
||||
*(ul32 *)loc = 0x1800'0000 | get_rd(*(ul32 *)loc); // pcaddi
|
||||
write_j20(loc, dist >> 2);
|
||||
} else {
|
||||
write_k12(loc, sym.get_tlsdesc_addr(ctx) + A);
|
||||
}
|
||||
if (sym.has_tlsdesc(ctx) && removed_bytes == 0) {
|
||||
i64 dist = sym.get_tlsdesc_addr(ctx) + A - P;
|
||||
if (is_int(dist, 22)) {
|
||||
*(ul32 *)loc = 0x1800'0000 | get_rd(*(ul32 *)loc); // pcaddi
|
||||
write_j20(loc, dist >> 2);
|
||||
} else {
|
||||
*(ul32 *)loc = 0x0340'0000; // nop
|
||||
write_k12(loc, sym.get_tlsdesc_addr(ctx) + A);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -629,7 +624,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
write_k12(loc, sym.get_gottp_addr(ctx) + A);
|
||||
} else {
|
||||
i64 val = S + A - ctx.tp_addr;
|
||||
if (val < 0x1000)
|
||||
if (0 <= val && val < 0x1000)
|
||||
*(ul32 *)loc = 0x0380'0004; // ori $a0, $zero, 0
|
||||
else
|
||||
*(ul32 *)loc = 0x0280'0084; // addi.w $a0, $a0, 0
|
||||
@@ -646,7 +641,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
|
||||
// Rewrite `addi.d $t0, $t0, <offset>` with `addi.d $t0, $tp, <offset>`
|
||||
// if the offset is directly accessible using tp. tp is r2.
|
||||
if (sign_extend(val, 11) == val)
|
||||
if (is_int(val, 12))
|
||||
set_rj(loc, 2);
|
||||
break;
|
||||
}
|
||||
@@ -857,15 +852,20 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
}
|
||||
|
||||
template <>
|
||||
void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc) {
|
||||
void shrink_section(Context<E> &ctx, InputSection<E> &isec) {
|
||||
std::span<const ElfRel<E>> rels = isec.get_rels(ctx);
|
||||
isec.extra.r_deltas.resize(rels.size() + 1);
|
||||
i64 delta = 0;
|
||||
std::vector<RelocDelta> &deltas = isec.extra.r_deltas;
|
||||
i64 r_delta = 0;
|
||||
u8 *buf = (u8 *)isec.contents.data();
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &r = rels[i];
|
||||
Symbol<E> &sym = *isec.file.symbols[r.r_sym];
|
||||
isec.extra.r_deltas[i] = delta;
|
||||
|
||||
auto remove = [&](i64 d) {
|
||||
r_delta += d;
|
||||
deltas.push_back(RelocDelta{r.r_offset, r_delta});
|
||||
};
|
||||
|
||||
// A R_LARCH_ALIGN relocation refers to the beginning of a nop
|
||||
// sequence. We need to remove some or all of them so that the
|
||||
@@ -890,9 +890,11 @@ void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc) {
|
||||
alignment = r.r_addend + 4;
|
||||
}
|
||||
|
||||
u64 loc = isec.get_addr() + r.r_offset - delta;
|
||||
u64 next_loc = loc + alignment - 4;
|
||||
delta += next_loc - align_to(loc, alignment);
|
||||
u64 P = isec.get_addr() + r.r_offset - r_delta;
|
||||
u64 desired = align_to(P, alignment);
|
||||
u64 actual = P + alignment - 4;
|
||||
if (desired != actual)
|
||||
remove(actual - desired);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -921,8 +923,8 @@ void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc) {
|
||||
//
|
||||
// addi.d $t0, $tp, <tp-offset>
|
||||
if (i64 val = sym.get_addr(ctx) + r.r_addend - ctx.tp_addr;
|
||||
sign_extend(val, 11) == val)
|
||||
delta += 4;
|
||||
is_int(val, 12))
|
||||
remove(4);
|
||||
break;
|
||||
case R_LARCH_PCALA_HI20:
|
||||
// The following two instructions are used to materialize a
|
||||
@@ -940,14 +942,14 @@ void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc) {
|
||||
rels[i + 2].r_offset == rels[i].r_offset + 4 &&
|
||||
rels[i + 3].r_type == R_LARCH_RELAX) {
|
||||
i64 dist = compute_distance(ctx, sym, isec, r);
|
||||
u32 insn1 = *(ul32 *)(isec.contents.data() + rels[i].r_offset);
|
||||
u32 insn2 = *(ul32 *)(isec.contents.data() + rels[i].r_offset + 4);
|
||||
u32 insn1 = *(ul32 *)(buf + rels[i].r_offset);
|
||||
u32 insn2 = *(ul32 *)(buf + rels[i].r_offset + 4);
|
||||
bool is_addi_d = (insn2 & 0xffc0'0000) == 0x02c0'0000;
|
||||
|
||||
if (dist % 4 == 0 && -(1 << 21) <= dist && dist < (1 << 21) &&
|
||||
if ((dist & 0b11) == 0 && is_int(dist, 22) &&
|
||||
is_addi_d && get_rd(insn1) == get_rd(insn2) &&
|
||||
get_rd(insn2) == get_rj(insn2))
|
||||
delta += 4;
|
||||
remove(4);
|
||||
}
|
||||
break;
|
||||
case R_LARCH_CALL36:
|
||||
@@ -960,10 +962,10 @@ void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc) {
|
||||
// If the displacement is PC ± 128 MiB, we can use B or BL instead.
|
||||
// Note that $zero is $r0 and $ra is $r1.
|
||||
if (i64 dist = compute_distance(ctx, sym, isec, r);
|
||||
-(1 << 27) <= dist && dist < (1 << 27))
|
||||
if (u32 jirl = *(ul32 *)(isec.contents.data() + rels[i].r_offset + 4);
|
||||
is_int(dist, 28))
|
||||
if (u32 jirl = *(ul32 *)(buf + rels[i].r_offset + 4);
|
||||
get_rd(jirl) == 0 || get_rd(jirl) == 1)
|
||||
delta += 4;
|
||||
remove(4);
|
||||
break;
|
||||
case R_LARCH_GOT_PC_HI20:
|
||||
// The following two instructions are used to load a symbol address
|
||||
@@ -976,36 +978,35 @@ void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc) {
|
||||
// relax them to the following instruction.
|
||||
//
|
||||
// pcaddi $t0, <offset>
|
||||
if (is_relaxable_got_load(ctx, isec, i)) {
|
||||
i64 dist = compute_distance(ctx, sym, isec, r);
|
||||
if (dist % 4 == 0 && -(1 << 21) <= dist && dist < (1 << 21))
|
||||
delta += 4;
|
||||
}
|
||||
if (is_relaxable_got_load(ctx, isec, i))
|
||||
if (i64 dist = compute_distance(ctx, sym, isec, r);
|
||||
is_int(dist, 22))
|
||||
remove(4);
|
||||
break;
|
||||
case R_LARCH_TLS_DESC_PC_HI20:
|
||||
if (sym.has_tlsdesc(ctx)) {
|
||||
u64 P = isec.get_addr() + r.r_offset;
|
||||
i64 dist = sym.get_tlsdesc_addr(ctx) + r.r_addend - P;
|
||||
if (-(1 << 21) <= dist && dist < (1 << 21))
|
||||
delta += 4;
|
||||
if (is_int(dist, 22))
|
||||
remove(4);
|
||||
} else {
|
||||
delta += 4;
|
||||
remove(4);
|
||||
}
|
||||
break;
|
||||
case R_LARCH_TLS_DESC_PC_LO12:
|
||||
if (!sym.has_tlsdesc(ctx))
|
||||
delta += 4;
|
||||
remove(4);
|
||||
break;
|
||||
case R_LARCH_TLS_DESC_LD:
|
||||
if (!sym.has_tlsdesc(ctx) && !sym.has_gottp(ctx) &&
|
||||
sym.get_addr(ctx) + r.r_addend - ctx.tp_addr < 0x1000)
|
||||
delta += 4;
|
||||
if (!sym.has_tlsdesc(ctx) && !sym.has_gottp(ctx))
|
||||
if (i64 val = sym.get_addr(ctx) + r.r_addend - ctx.tp_addr;
|
||||
0 <= val && val < 0x1000)
|
||||
remove(4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
isec.extra.r_deltas[rels.size()] = delta;
|
||||
isec.sh_size -= delta;
|
||||
isec.sh_size -= r_delta;
|
||||
}
|
||||
|
||||
} // namespace mold
|
||||
|
||||
+12
-15
@@ -1,10 +1,10 @@
|
||||
// This file contains code for the Motorola 68000 series microprocessors,
|
||||
// which is often abbreviated as m68k. Running a Unix-like system on a
|
||||
// m68k-based machine today is probably a retro-computing hobby activity,
|
||||
// but the processor was a popular choice to build Unix computers during
|
||||
// '80s. Early Sun workstations for example used m68k. Macintosh until
|
||||
// 1994 were based on m68k as well until they switched to PowerPC (and
|
||||
// then to x86 and to ARM.)
|
||||
// m68k-based machine today is a retro-computing hobby activity, but the
|
||||
// processor was a popular choice to build Unix computers during '80s.
|
||||
// Early Sun workstations for example used m68k. Macintosh until 1994 were
|
||||
// based on m68k as well until they switched to PowerPC (and then to x86
|
||||
// and to ARM.)
|
||||
//
|
||||
// From the linker's point of view, it is not hard to support m68k. It's
|
||||
// just a 32-bit big-endian CISC ISA. Compared to comtemporary i386,
|
||||
@@ -86,11 +86,14 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
u8 *loc = base + rel.r_offset;
|
||||
|
||||
u64 S = sym.get_addr(ctx);
|
||||
u64 A = rel.r_addend;
|
||||
u64 P = get_addr() + rel.r_offset;
|
||||
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
|
||||
u64 GOT = ctx.got->shdr.sh_addr;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
if (val < lo || hi <= val)
|
||||
Error(ctx) << *this << ": relocation " << rel << " against "
|
||||
<< sym << " out of range: " << val << " is not in ["
|
||||
<< lo << ", " << hi << ")";
|
||||
check_range(ctx, i, val, lo, hi);
|
||||
};
|
||||
|
||||
auto write16 = [&](u64 val) {
|
||||
@@ -113,12 +116,6 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
*loc = val;
|
||||
};
|
||||
|
||||
u64 S = sym.get_addr(ctx);
|
||||
u64 A = rel.r_addend;
|
||||
u64 P = get_addr() + rel.r_offset;
|
||||
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
|
||||
u64 GOT = ctx.got->shdr.sh_addr;
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_68K_32:
|
||||
break;
|
||||
|
||||
+5
-5
@@ -88,7 +88,7 @@ void write_plt_header(Context<E> &ctx, u8 *buf) {
|
||||
loc[5] |= lo(ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr + 4);
|
||||
}
|
||||
|
||||
constexpr ub32 plt_entry[] = {
|
||||
static constexpr ub32 plt_entry[] = {
|
||||
// Get the address of this PLT entry
|
||||
0x7c08'02a6, // mflr r0
|
||||
0x429f'0005, // bcl 20, 31, 4
|
||||
@@ -213,15 +213,15 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
case R_PPC_REL24:
|
||||
case R_PPC_LOCAL24PC: {
|
||||
i64 val = S + A - P;
|
||||
if (sign_extend(val, 25) != val)
|
||||
val = get_thunk_addr(i) - P;
|
||||
if (!is_int(val, 26))
|
||||
val = sym.get_thunk_addr(ctx, P) - P;
|
||||
*(ub32 *)loc |= bits(val, 25, 2) << 2;
|
||||
break;
|
||||
}
|
||||
case R_PPC_PLTREL24: {
|
||||
i64 val = S - P;
|
||||
if (sym.has_plt(ctx) || sign_extend(val, 25) != val)
|
||||
val = get_thunk_addr(i) - P;
|
||||
if (sym.has_plt(ctx) || !is_int(val, 26))
|
||||
val = sym.get_thunk_addr(ctx, P) - P;
|
||||
*(ub32 *)loc |= bits(val, 25, 2) << 2;
|
||||
break;
|
||||
}
|
||||
|
||||
+18
-26
@@ -162,13 +162,6 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
u8 *loc = base + rel.r_offset;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
if (val < lo || hi <= val)
|
||||
Error(ctx) << *this << ": relocation " << rel << " against "
|
||||
<< sym << " out of range: " << val << " is not in ["
|
||||
<< lo << ", " << hi << ")";
|
||||
};
|
||||
|
||||
u64 S = sym.get_addr(ctx);
|
||||
u64 A = rel.r_addend;
|
||||
u64 P = get_addr() + rel.r_offset;
|
||||
@@ -176,6 +169,10 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
u64 GOT = ctx.got->shdr.sh_addr;
|
||||
u64 TOC = ctx.extra.TOC->value;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
check_range(ctx, i, val, lo, hi);
|
||||
};
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_PPC64_TOC:
|
||||
break;
|
||||
@@ -194,8 +191,8 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
break;
|
||||
case R_PPC64_REL24: {
|
||||
i64 val = sym.get_addr(ctx, NO_OPD) + A - P;
|
||||
if (sym.has_plt(ctx) || sign_extend(val, 25) != val)
|
||||
val = get_thunk_addr(i) + A - P;
|
||||
if (sym.has_plt(ctx) || !is_int(val, 26))
|
||||
val = sym.get_thunk_addr(ctx, P) + A - P;
|
||||
|
||||
check(val, -(1 << 25), 1 << 25);
|
||||
*(ub32 *)loc |= bits(val, 25, 2) << 2;
|
||||
@@ -293,13 +290,6 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
u8 *loc = base + rel.r_offset;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
if (val < lo || hi <= val)
|
||||
Error(ctx) << *this << ": relocation " << rel << " against "
|
||||
<< sym << " out of range: " << val << " is not in ["
|
||||
<< lo << ", " << hi << ")";
|
||||
};
|
||||
|
||||
SectionFragment<E> *frag;
|
||||
i64 frag_addend;
|
||||
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
|
||||
@@ -307,6 +297,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
|
||||
u64 A = frag ? frag_addend : (i64)rel.r_addend;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
check_range(ctx, val, i, lo, hi);
|
||||
};
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_PPC64_ADDR64:
|
||||
if (std::optional<u64> val = get_tombstone(sym, frag))
|
||||
@@ -314,12 +308,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
else
|
||||
*(ub64 *)loc = S + A;
|
||||
break;
|
||||
case R_PPC64_ADDR32: {
|
||||
i64 val = S + A;
|
||||
check(val, 0, 1LL << 32);
|
||||
*(ub32 *)loc = val;
|
||||
case R_PPC64_ADDR32:
|
||||
check(S + A, 0, 1LL << 32);
|
||||
*(ub32 *)loc = S + A;
|
||||
break;
|
||||
}
|
||||
case R_PPC64_DTPREL64:
|
||||
*(ub64 *)loc = S + A - ctx.dtp_addr;
|
||||
break;
|
||||
@@ -507,16 +499,16 @@ get_relocation_at(Context<E> &ctx, InputSection<E> &isec, i64 offset) {
|
||||
return &*it;
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct OpdSymbol {
|
||||
bool operator<(const OpdSymbol &x) const { return r_offset < x.r_offset; }
|
||||
|
||||
u64 r_offset = 0;
|
||||
Symbol<E> *sym = nullptr;
|
||||
};
|
||||
}
|
||||
|
||||
static Symbol<E> *
|
||||
get_opd_sym_at(std::span<OpdSymbol> syms, u64 offset) {
|
||||
auto it = std::lower_bound(syms.begin(), syms.end(), OpdSymbol{offset});
|
||||
auto it = ranges::lower_bound(syms, offset, {}, &OpdSymbol::r_offset);
|
||||
if (it == syms.end())
|
||||
return nullptr;
|
||||
if (it->r_offset != offset)
|
||||
@@ -589,7 +581,7 @@ void ppc64v1_rewrite_opd(Context<E> &ctx) {
|
||||
|
||||
Symbol<E> *sym2 = file->symbols[rel->r_sym];
|
||||
if (sym2->get_type() != STT_SECTION)
|
||||
Fatal(ctx) << *file << ": bad relocation in .opd referring " << *sym2;
|
||||
Fatal(ctx) << *file << ": bad relocation in .opd referring to " << *sym2;
|
||||
|
||||
opd_syms.push_back({sym->value, sym});
|
||||
|
||||
@@ -598,7 +590,7 @@ void ppc64v1_rewrite_opd(Context<E> &ctx) {
|
||||
}
|
||||
|
||||
// Sort symbols so that get_opd_sym_at() can do binary search.
|
||||
sort(opd_syms);
|
||||
ranges::stable_sort(opd_syms, {}, &OpdSymbol::r_offset);
|
||||
|
||||
// Rewrite relocations so that they directly refer to .opd.
|
||||
for (std::unique_ptr<InputSection<E>> &isec : file->sections) {
|
||||
|
||||
+13
-16
@@ -201,8 +201,8 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
u64 GOT = ctx.got->shdr.sh_addr;
|
||||
u64 TOC = ctx.extra.TOC->value;
|
||||
|
||||
auto r2save_thunk_addr = [&] { return get_thunk_addr(i); };
|
||||
auto no_r2save_thunk_addr = [&] { return get_thunk_addr(i) + 8; };
|
||||
auto r2save_thunk_addr = [&] { return sym.get_thunk_addr(ctx, P); };
|
||||
auto no_r2save_thunk_addr = [&] { return sym.get_thunk_addr(ctx, P) + 8; };
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_PPC64_TOC16_HA:
|
||||
@@ -227,7 +227,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
*(ul32 *)(loc + 4) = 0xe841'0018; // ld r2, 24(r1)
|
||||
} else {
|
||||
i64 val = S + get_local_entry_offset(ctx, sym) + A - P;
|
||||
if (sign_extend(val, 25) != val)
|
||||
if (!is_int(val, 26))
|
||||
val = no_r2save_thunk_addr() + A - P;
|
||||
*(ul32 *)loc |= bits(val, 25, 2) << 2;
|
||||
}
|
||||
@@ -238,7 +238,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
*(ul32 *)loc |= bits(val, 25, 2) << 2;
|
||||
} else {
|
||||
i64 val = S + A - P;
|
||||
if (sign_extend(val, 25) != val)
|
||||
if (!is_int(val, 26))
|
||||
val = no_r2save_thunk_addr() + A - P;
|
||||
*(ul32 *)loc |= bits(val, 25, 2) << 2;
|
||||
}
|
||||
@@ -334,6 +334,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
case R_PPC64_TLS:
|
||||
case R_PPC64_TLSGD:
|
||||
case R_PPC64_TLSLD:
|
||||
case R_PPC64_ENTRY:
|
||||
break;
|
||||
default:
|
||||
unreachable();
|
||||
@@ -353,13 +354,6 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
u8 *loc = base + rel.r_offset;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
if (val < lo || hi <= val)
|
||||
Error(ctx) << *this << ": relocation " << rel << " against "
|
||||
<< sym << " out of range: " << val << " is not in ["
|
||||
<< lo << ", " << hi << ")";
|
||||
};
|
||||
|
||||
SectionFragment<E> *frag;
|
||||
i64 frag_addend;
|
||||
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
|
||||
@@ -367,6 +361,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
|
||||
u64 A = frag ? frag_addend : (i64)rel.r_addend;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
check_range(ctx, i, val, lo, hi);
|
||||
};
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_PPC64_ADDR64:
|
||||
if (std::optional<u64> val = get_tombstone(sym, frag))
|
||||
@@ -374,12 +372,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
else
|
||||
*(ul64 *)loc = S + A;
|
||||
break;
|
||||
case R_PPC64_ADDR32: {
|
||||
i64 val = S + A;
|
||||
check(val, 0, 1LL << 32);
|
||||
*(ul32 *)loc = val;
|
||||
case R_PPC64_ADDR32:
|
||||
check(S + A, 0, 1LL << 32);
|
||||
*(ul32 *)loc = S + A;
|
||||
break;
|
||||
}
|
||||
case R_PPC64_DTPREL64:
|
||||
*(ul64 *)loc = S + A - ctx.dtp_addr;
|
||||
break;
|
||||
@@ -467,6 +463,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_PPC64_DTPREL16_LO:
|
||||
case R_PPC64_DTPREL16_LO_DS:
|
||||
case R_PPC64_DTPREL34:
|
||||
case R_PPC64_ENTRY:
|
||||
break;
|
||||
default:
|
||||
Error(ctx) << *this << ": unknown relocation: " << rel;
|
||||
|
||||
+177
-165
@@ -22,8 +22,6 @@
|
||||
#include "mold.h"
|
||||
|
||||
#include <regex>
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/parallel_for_each.h>
|
||||
|
||||
namespace mold {
|
||||
|
||||
@@ -88,6 +86,10 @@ static void set_rs1(u8 *loc, u32 rs1) {
|
||||
*(ul32 *)loc |= rs1 << 15;
|
||||
}
|
||||
|
||||
static u32 get_rd(u8 *loc) {
|
||||
return bits(*(u32 *)loc, 11, 7);
|
||||
};
|
||||
|
||||
template <>
|
||||
void write_plt_header<E>(Context<E> &ctx, u8 *buf) {
|
||||
constexpr ul32 insn_64[] = {
|
||||
@@ -121,14 +123,14 @@ void write_plt_header<E>(Context<E> &ctx, u8 *buf) {
|
||||
write_itype(buf + 16, gotplt - plt);
|
||||
}
|
||||
|
||||
constexpr ul32 plt_entry_64[] = {
|
||||
static constexpr ul32 plt_entry_64[] = {
|
||||
0x0000'0e17, // auipc t3, %pcrel_hi([email protected])
|
||||
0x000e'3e03, // ld t3, %pcrel_lo(1b)(t3)
|
||||
0x000e'0367, // jalr t1, t3
|
||||
0x0010'0073, // ebreak
|
||||
};
|
||||
|
||||
constexpr ul32 plt_entry_32[] = {
|
||||
static constexpr ul32 plt_entry_32[] = {
|
||||
0x0000'0e17, // auipc t3, %pcrel_hi([email protected])
|
||||
0x000e'2e03, // lw t3, %pcrel_lo(1b)(t3)
|
||||
0x000e'0367, // jalr t1, t3
|
||||
@@ -198,67 +200,111 @@ void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool is_hi20(const ElfRel<E> &rel) {
|
||||
u32 ty = rel.r_type;
|
||||
return ty == R_RISCV_GOT_HI20 || ty == R_RISCV_TLS_GOT_HI20 ||
|
||||
ty == R_RISCV_TLS_GD_HI20 || ty == R_RISCV_PCREL_HI20 ||
|
||||
ty == R_RISCV_TLSDESC_HI20;
|
||||
// RISC-V generally uses the AUIPC + ADDI/LW/SW/etc instruction pair
|
||||
// to access the AUIPC's address ± 2 GiB. AUIPC materializes the most
|
||||
// significant 52 bits in a PC-relative manner, and the following
|
||||
// instruction specifies the remaining least significant 12 bits.
|
||||
// There are several HI20 and LO12 relocation types for them.
|
||||
//
|
||||
// LO12 relocations need to materialize an address relative to AUIPC's
|
||||
// address, not relative to the instruction that the relocation
|
||||
// directly refers to.
|
||||
//
|
||||
// The problem here is that the instruction pair may not always be
|
||||
// adjacent. We need a mechanism to find a paired AUIPC for a given
|
||||
// LO12 relocation. For this purpose, the compiler creates a local
|
||||
// symbol for each location to which HI20 refers, and the LO12
|
||||
// relocation refers to that symbol.
|
||||
//
|
||||
// This function returns a paired HI20 relocation for a given LO12.
|
||||
// Since the instructions are typically adjacent, we do a linear
|
||||
// search.
|
||||
static const ElfRel<E> &
|
||||
find_paired_reloc(Context<E> &ctx, InputSection<E> &isec,
|
||||
std::span<const ElfRel<E>> rels,
|
||||
Symbol<E> &sym, i64 i) {
|
||||
auto is_hi20 = [](u32 ty) {
|
||||
return ty == R_RISCV_GOT_HI20 || ty == R_RISCV_TLS_GOT_HI20 ||
|
||||
ty == R_RISCV_TLS_GD_HI20 || ty == R_RISCV_PCREL_HI20 ||
|
||||
ty == R_RISCV_TLSDESC_HI20;
|
||||
};
|
||||
|
||||
u64 value = sym.esym().st_value;
|
||||
|
||||
if (value <= rels[i].r_offset) {
|
||||
for (i64 j = i - 1; j >= 0; j--)
|
||||
if (is_hi20(rels[j].r_type) && value == rels[j].r_offset)
|
||||
return rels[j];
|
||||
} else {
|
||||
for (i64 j = i + 1; j < rels.size(); j++)
|
||||
if (is_hi20(rels[j].r_type) && value == rels[j].r_offset)
|
||||
return rels[j];
|
||||
}
|
||||
Fatal(ctx) << isec << ": paired relocation is missing: " << i;
|
||||
}
|
||||
|
||||
// Returns true if isec's i'th relocation refers to the following
|
||||
// GOT-load instructioon pair, which is an expeanded form of
|
||||
// `la t0, foo` pseudo assembly instruction.
|
||||
//
|
||||
// .L0
|
||||
// auipc t0, 0 # R_RISCV_GOT_HI20(foo), R_RISCV_RELAX
|
||||
// ld t0, 0(t0) # R_RISCV_PCREL_LO12_I(.L0), R_RISCV_RELAX
|
||||
static bool is_got_load_pair(Context<E> &ctx, InputSection<E> &isec,
|
||||
std::span<const ElfRel<E>> rels, i64 i) {
|
||||
u8 *buf = (u8 *)isec.contents.data();
|
||||
return i + 3 < rels.size() &&
|
||||
rels[i].r_type == R_RISCV_GOT_HI20 &&
|
||||
rels[i + 1].r_type == R_RISCV_RELAX &&
|
||||
rels[i + 2].r_type == R_RISCV_PCREL_LO12_I &&
|
||||
rels[i + 3].r_type == R_RISCV_RELAX &&
|
||||
rels[i].r_offset == rels[i + 2].r_offset - 4 &&
|
||||
rels[i].r_offset == isec.file.symbols[rels[i + 2].r_sym]->value &&
|
||||
get_rd(buf + rels[i].r_offset) == get_rd(buf + rels[i + 2].r_offset);
|
||||
}
|
||||
|
||||
template <>
|
||||
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
auto get_r_delta = [&](i64 idx) {
|
||||
return extra.r_deltas.empty() ? 0 : extra.r_deltas[idx];
|
||||
};
|
||||
std::span<RelocDelta> deltas = extra.r_deltas;
|
||||
i64 k = 0;
|
||||
u8 *buf = (u8 *)contents.data();
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
if (rel.r_type == R_NONE || rel.r_type == R_RISCV_RELAX)
|
||||
continue;
|
||||
|
||||
i64 removed_bytes = 0;
|
||||
i64 r_delta = 0;
|
||||
|
||||
if (!deltas.empty()) {
|
||||
while (k < deltas.size() && deltas[k].offset < rel.r_offset)
|
||||
k++;
|
||||
if (k < deltas.size() && deltas[k].offset == rel.r_offset)
|
||||
removed_bytes = get_removed_bytes(deltas, k);
|
||||
if (k > 0)
|
||||
r_delta = deltas[k - 1].delta;
|
||||
}
|
||||
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
i64 r_offset = rel.r_offset - get_r_delta(i);
|
||||
i64 removed_bytes = get_r_delta(i + 1) - get_r_delta(i);
|
||||
i64 r_offset = rel.r_offset - r_delta;
|
||||
u8 *loc = base + r_offset;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
if (val < lo || hi <= val)
|
||||
Error(ctx) << *this << ": relocation " << rel << " against "
|
||||
<< sym << " out of range: " << val << " is not in ["
|
||||
<< lo << ", " << hi << ")";
|
||||
};
|
||||
|
||||
auto find_paired_reloc = [&] {
|
||||
if (sym.value <= rels[i].r_offset - get_r_delta(i)) {
|
||||
for (i64 j = i - 1; j >= 0; j--)
|
||||
if (is_hi20(rels[j]) && sym.value == rels[j].r_offset - get_r_delta(j))
|
||||
return j;
|
||||
} else {
|
||||
for (i64 j = i + 1; j < rels.size(); j++)
|
||||
if (is_hi20(rels[j]) && sym.value == rels[j].r_offset - get_r_delta(j))
|
||||
return j;
|
||||
}
|
||||
|
||||
Fatal(ctx) << *this << ": paired relocation is missing: " << i;
|
||||
};
|
||||
|
||||
auto get_rd = [&](i64 offset) {
|
||||
// Returns the rd register of an R/I/U/J-type instruction.
|
||||
return bits(*(ul32 *)(contents.data() + offset), 11, 7);
|
||||
};
|
||||
|
||||
u64 S = sym.get_addr(ctx);
|
||||
u64 A = rel.r_addend;
|
||||
u64 P = get_addr() + r_offset;
|
||||
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
|
||||
u64 GOT = ctx.got->shdr.sh_addr;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
check_range(ctx, i, val, lo, hi);
|
||||
};
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_RISCV_32:
|
||||
if constexpr (E::is_64)
|
||||
*(U32<E> *)loc = S + A;
|
||||
assert(E::is_64);
|
||||
*(U32<E> *)loc = S + A;
|
||||
break;
|
||||
case R_RISCV_64:
|
||||
break;
|
||||
@@ -273,7 +319,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
case R_RISCV_CALL:
|
||||
case R_RISCV_CALL_PLT: {
|
||||
i64 val = S + A - P;
|
||||
i64 rd = get_rd(rel.r_offset + 4);
|
||||
i64 rd = get_rd(buf + rel.r_offset + 4);
|
||||
|
||||
// Calling an undefined weak symbol does not make sense.
|
||||
// We make such call into an infinite loop. This should
|
||||
@@ -307,47 +353,34 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
// pair to load a symbol value from the GOT. If the symbol value
|
||||
// is actually a link-time constant, we can materialize the value
|
||||
// directly into a register to eliminate a memory load.
|
||||
i64 rd = get_rd(rel.r_offset);
|
||||
i64 rd = get_rd(buf + rel.r_offset);
|
||||
|
||||
switch (removed_bytes) {
|
||||
case 6:
|
||||
if (removed_bytes == 6) {
|
||||
// c.li <rd>, val
|
||||
*(ul16 *)loc = 0b010'0'00000'00000'01 | (rd << 7);
|
||||
write_citype(loc, sym.get_addr(ctx));
|
||||
i += 3;
|
||||
break;
|
||||
case 4:
|
||||
} else if (removed_bytes == 4) {
|
||||
// addi <rd>, zero, val
|
||||
*(ul32 *)loc = 0b0010011 | (rd << 7);
|
||||
write_itype(loc, sym.get_addr(ctx));
|
||||
i += 3;
|
||||
break;
|
||||
case 0:
|
||||
if (ctx.arg.relax &&
|
||||
sym.is_pcrel_linktime_const(ctx) &&
|
||||
i + 3 < rels.size() &&
|
||||
rels[i + 1].r_type == R_RISCV_RELAX &&
|
||||
rels[i + 2].r_type == R_RISCV_PCREL_LO12_I &&
|
||||
rels[i + 2].r_offset == rels[i].r_offset + 4 &&
|
||||
file.symbols[rels[i + 2].r_sym]->value == r_offset &&
|
||||
rels[i + 3].r_type == R_RISCV_RELAX) {
|
||||
i64 val = S + A - P;
|
||||
if (rd == get_rd(rel.r_offset + 4) && (i32)val == val) {
|
||||
// auipc <rd>, %hi20(val)
|
||||
write_utype(loc, val);
|
||||
} else {
|
||||
assert(removed_bytes == 0);
|
||||
|
||||
// addi <rd>, <rd>, %lo12(val)
|
||||
*(ul32 *)(loc + 4) = 0b0010011 | (rd << 15) | (rd << 7);
|
||||
write_itype(loc + 4, val);
|
||||
i += 3;
|
||||
break;
|
||||
}
|
||||
i64 val = S + A - P;
|
||||
if (ctx.arg.relax && sym.is_pcrel_linktime_const(ctx) &&
|
||||
is_got_load_pair(ctx, *this, rels, i) && is_int(val, 32)) {
|
||||
// auipc <rd>, %hi20(val)
|
||||
write_utype(loc, val);
|
||||
|
||||
// addi <rd>, <rd>, %lo12(val)
|
||||
*(ul32 *)(loc + 4) = 0b0010011 | (rd << 15) | (rd << 7);
|
||||
write_itype(loc + 4, val);
|
||||
i += 3;
|
||||
} else {
|
||||
write_utype(loc, G + GOT + A - P);
|
||||
}
|
||||
|
||||
write_utype(loc, G + GOT + A - P);
|
||||
break;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -362,8 +395,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
break;
|
||||
case R_RISCV_PCREL_LO12_I:
|
||||
case R_RISCV_PCREL_LO12_S: {
|
||||
i64 idx2 = find_paired_reloc();
|
||||
const ElfRel<E> &rel2 = rels[idx2];
|
||||
const ElfRel<E> &rel2 = find_paired_reloc(ctx, *this, rels, sym, i);
|
||||
Symbol<E> &sym2 = *file.symbols[rel2.r_sym];
|
||||
|
||||
auto write =
|
||||
@@ -371,7 +403,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
|
||||
u64 S = sym2.get_addr(ctx);
|
||||
u64 A = rel2.r_addend;
|
||||
u64 P = get_addr() + rel2.r_offset - get_r_delta(idx2);
|
||||
u64 P = get_addr() + rel2.r_offset - get_r_delta(*this, rel2.r_offset);
|
||||
u64 G = sym2.get_got_idx(ctx) * sizeof(Word<E>);
|
||||
|
||||
switch (rel2.r_type) {
|
||||
@@ -393,7 +425,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
case R_RISCV_HI20:
|
||||
if (removed_bytes == 2) {
|
||||
// Rewrite LUI with C.LUI
|
||||
i64 rd = get_rd(rel.r_offset);
|
||||
i64 rd = get_rd(buf + rel.r_offset);
|
||||
*(ul16 *)loc = 0b011'0'00000'00000'01 | (rd << 7);
|
||||
write_citype(loc, (S + A + 0x800) >> 12);
|
||||
} else if (removed_bytes == 0) {
|
||||
@@ -411,7 +443,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
// Rewrite `lw t1, 0(t0)` with `lw t1, 0(x0)` if the address is
|
||||
// accessible relative to the zero register because if that's the
|
||||
// case, corresponding LUI might have been removed by relaxation.
|
||||
if (sign_extend(S + A, 11) == S + A)
|
||||
if (is_int(S + A, 12))
|
||||
set_rs1(loc, 0);
|
||||
break;
|
||||
case R_RISCV_TPREL_HI20:
|
||||
@@ -435,7 +467,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
|
||||
// Rewrite `lw t1, 0(t0)` with `lw t1, 0(tp)` if the address is
|
||||
// directly accessible using tp. tp is x4.
|
||||
if (sign_extend(val, 11) == val)
|
||||
if (is_int(val, 12))
|
||||
set_rs1(loc, 4);
|
||||
break;
|
||||
}
|
||||
@@ -474,7 +506,11 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
// <deleted>
|
||||
// lui a0, %tpoff_hi(a0)
|
||||
// addi a0, a0, %tpoff_lo(a0)
|
||||
if (removed_bytes == 0)
|
||||
//
|
||||
// If the code-shrinking relaxation is disabled, we may leave
|
||||
// original useless instructions instead of deleting them, but we
|
||||
// accept that because relaxations are enabled by default.
|
||||
if (sym.has_tlsdesc(ctx) && removed_bytes == 0)
|
||||
write_utype(loc, sym.get_tlsdesc_addr(ctx) + A - P);
|
||||
break;
|
||||
case R_RISCV_TLSDESC_LOAD_LO12:
|
||||
@@ -483,17 +519,19 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
if (removed_bytes == 4)
|
||||
break;
|
||||
|
||||
i64 idx2 = find_paired_reloc();
|
||||
const ElfRel<E> &rel2 = rels[idx2];
|
||||
const ElfRel<E> &rel2 = find_paired_reloc(ctx, *this, rels, sym, i);
|
||||
Symbol<E> &sym2 = *file.symbols[rel2.r_sym];
|
||||
|
||||
u64 S = sym2.get_addr(ctx);
|
||||
u64 A = rel2.r_addend;
|
||||
u64 P = get_addr() + rel2.r_offset - get_r_delta(idx2);
|
||||
u64 P = get_addr() + rel2.r_offset - get_r_delta(*this, rel2.r_offset);
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_RISCV_TLSDESC_LOAD_LO12:
|
||||
write_itype(loc, sym2.get_tlsdesc_addr(ctx) + A - P);
|
||||
if (sym2.has_tlsdesc(ctx))
|
||||
write_itype(loc, sym2.get_tlsdesc_addr(ctx) + A - P);
|
||||
else
|
||||
*(ul32 *)loc = 0x13; // nop
|
||||
break;
|
||||
case R_RISCV_TLSDESC_ADD_LO12:
|
||||
if (sym2.has_tlsdesc(ctx)) {
|
||||
@@ -515,7 +553,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
write_itype(loc, sym2.get_gottp_addr(ctx) + A - P);
|
||||
} else {
|
||||
i64 val = S + A - ctx.tp_addr;
|
||||
if (sign_extend(val, 11) == val)
|
||||
if (is_int(val, 12))
|
||||
*(ul32 *)loc = 0x513; // addi a0,zero,<lo12>
|
||||
else
|
||||
*(ul32 *)loc = 0x50513; // addi a0,a0,<lo12>
|
||||
@@ -784,9 +822,9 @@ u64 get_eflags(Context<E> &ctx) {
|
||||
if (objs.empty())
|
||||
return 0;
|
||||
|
||||
u32 ret = objs[0]->get_ehdr().e_flags;
|
||||
u32 ret = objs[0]->get_eflags();
|
||||
for (i64 i = 1; i < objs.size(); i++) {
|
||||
u32 flags = objs[i]->get_ehdr().e_flags;
|
||||
u32 flags = objs[i]->get_eflags();
|
||||
if (flags & EF_RISCV_RVC)
|
||||
ret |= EF_RISCV_RVC;
|
||||
|
||||
@@ -803,20 +841,24 @@ u64 get_eflags(Context<E> &ctx) {
|
||||
|
||||
// Scan relocations to shrink a given section.
|
||||
template <>
|
||||
void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc) {
|
||||
void shrink_section(Context<E> &ctx, InputSection<E> &isec) {
|
||||
std::span<const ElfRel<E>> rels = isec.get_rels(ctx);
|
||||
isec.extra.r_deltas.resize(rels.size() + 1);
|
||||
std::vector<RelocDelta> &deltas = isec.extra.r_deltas;
|
||||
i64 r_delta = 0;
|
||||
u8 *buf = (u8 *)isec.contents.data();
|
||||
|
||||
auto get_rd = [&](i64 offset) {
|
||||
return bits(*(ul32 *)(isec.contents.data() + offset), 11, 7);
|
||||
};
|
||||
|
||||
i64 delta = 0;
|
||||
// True if we can use 2-byte instructions. This is usually true on
|
||||
// Unix because RV64GC is generally considered the baseline hardware.
|
||||
bool use_rvc = isec.file.get_eflags() & EF_RISCV_RVC;
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &r = rels[i];
|
||||
Symbol<E> &sym = *isec.file.symbols[r.r_sym];
|
||||
isec.extra.r_deltas[i] = delta;
|
||||
|
||||
auto remove = [&](i64 d) {
|
||||
r_delta += d;
|
||||
deltas.push_back(RelocDelta{r.r_offset, r_delta});
|
||||
};
|
||||
|
||||
// Handling R_RISCV_ALIGN is mandatory.
|
||||
//
|
||||
@@ -826,11 +868,11 @@ void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc) {
|
||||
if (r.r_type == R_RISCV_ALIGN) {
|
||||
// The total bytes of NOPs is stored to r_addend, so the next
|
||||
// instruction is r_addend away.
|
||||
u64 loc = isec.get_addr() + r.r_offset - delta;
|
||||
u64 next_loc = loc + r.r_addend;
|
||||
u64 alignment = bit_ceil(r.r_addend + 1);
|
||||
assert(alignment <= (1 << isec.p2align));
|
||||
delta += next_loc - align_to(loc, alignment);
|
||||
u64 P = isec.get_addr() + r.r_offset - r_delta;
|
||||
u64 desired = align_to(P, bit_ceil(r.r_addend));
|
||||
u64 actual = P + r.r_addend;
|
||||
if (desired != actual)
|
||||
remove(actual - desired);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -847,20 +889,6 @@ void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc) {
|
||||
if (sym.file == ctx.internal_obj)
|
||||
continue;
|
||||
|
||||
auto find_paired_reloc = [&] {
|
||||
if (sym.value <= rels[i].r_offset) {
|
||||
for (i64 j = i - 1; j >= 0; j--)
|
||||
if (is_hi20(rels[j]) && sym.value == rels[j].r_offset)
|
||||
return j;
|
||||
} else {
|
||||
for (i64 j = i + 1; j < rels.size(); j++)
|
||||
if (is_hi20(rels[j]) && sym.value == rels[j].r_offset)
|
||||
return j;
|
||||
}
|
||||
|
||||
Fatal(ctx) << isec << ": paired relocation is missing: " << i;
|
||||
};
|
||||
|
||||
switch (r.r_type) {
|
||||
case R_RISCV_CALL:
|
||||
case R_RISCV_CALL_PLT: {
|
||||
@@ -871,63 +899,51 @@ void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc) {
|
||||
if (dist & 1)
|
||||
break;
|
||||
|
||||
i64 rd = get_rd(r.r_offset + 4);
|
||||
i64 rd = get_rd(buf + r.r_offset + 4);
|
||||
|
||||
if (use_rvc && rd == 0 && sign_extend(dist, 11) == dist) {
|
||||
if (use_rvc && rd == 0 && is_int(dist, 12)) {
|
||||
// If rd is x0 and the jump target is within ±2 KiB, we can use
|
||||
// C.J, saving 6 bytes.
|
||||
delta += 6;
|
||||
} else if (use_rvc && !E::is_64 && rd == 1 && sign_extend(dist, 11) == dist) {
|
||||
remove(6);
|
||||
} else if (use_rvc && !E::is_64 && rd == 1 && is_int(dist, 12)) {
|
||||
// If rd is x1 and the jump target is within ±2 KiB, we can use
|
||||
// C.JAL. This is RV32 only because C.JAL is RV32-only instruction.
|
||||
delta += 6;
|
||||
} else if (sign_extend(dist, 20) == dist) {
|
||||
remove(6);
|
||||
} else if (is_int(dist, 21)) {
|
||||
// If the jump target is within ±1 MiB, we can use JAL.
|
||||
delta += 4;
|
||||
remove(4);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case R_RISCV_GOT_HI20: {
|
||||
case R_RISCV_GOT_HI20:
|
||||
// A GOT_HI20 followed by a PCREL_LO12_I is used to load a value from
|
||||
// GOT. If the loaded value is a link-time constant, we can rewrite
|
||||
// the instructions to directly materialize the value, eliminating a
|
||||
// memory load.
|
||||
if (sym.is_absolute() &&
|
||||
i + 3 < rels.size() &&
|
||||
rels[i + 1].r_type == R_RISCV_RELAX &&
|
||||
rels[i + 2].r_type == R_RISCV_PCREL_LO12_I &&
|
||||
rels[i + 2].r_offset == rels[i].r_offset + 4 &&
|
||||
isec.file.symbols[rels[i + 2].r_sym]->value == rels[i].r_offset &&
|
||||
rels[i + 3].r_type == R_RISCV_RELAX) {
|
||||
i64 rd = get_rd(r.r_offset);
|
||||
|
||||
if (rd == get_rd(r.r_offset + 4)) {
|
||||
u64 val = sym.get_addr(ctx) + r.r_addend;
|
||||
|
||||
if (use_rvc && rd != 0 && sign_extend(val, 5) == val) {
|
||||
// Replace AUIPC + LD with C.LI.
|
||||
delta += 6;
|
||||
} else if (sign_extend(val, 11) == val) {
|
||||
// Replace AUIPC + LD with ADDI.
|
||||
delta += 4;
|
||||
}
|
||||
if (sym.is_absolute() && is_got_load_pair(ctx, isec, rels, i)) {
|
||||
u64 val = sym.get_addr(ctx) + r.r_addend;
|
||||
if (use_rvc && is_int(val, 6) && get_rd(buf + r.r_offset) != 0) {
|
||||
// Replace AUIPC + LD with C.LI.
|
||||
remove(6);
|
||||
} else if (is_int(val, 12)) {
|
||||
// Replace AUIPC + LD with ADDI.
|
||||
remove(4);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case R_RISCV_HI20: {
|
||||
u64 val = sym.get_addr(ctx) + r.r_addend;
|
||||
i64 rd = get_rd(r.r_offset);
|
||||
i64 rd = get_rd(buf + r.r_offset);
|
||||
|
||||
if (sign_extend(val, 11) == val) {
|
||||
if (is_int(val, 12)) {
|
||||
// We can replace `lui t0, %hi(foo)` and `add t0, t0, %lo(foo)`
|
||||
// instruction pair with `add t0, x0, %lo(foo)` if foo's bits
|
||||
// [32:11] are all one or all zero.
|
||||
delta += 4;
|
||||
} else if (use_rvc && rd != 0 && rd != 2 && sign_extend(val, 17) == val) {
|
||||
remove(4);
|
||||
} else if (use_rvc && rd != 0 && rd != 2 && is_int(val + 0x800, 18)) {
|
||||
// If the upper 20 bits can actually be represented in 6 bits,
|
||||
// we can use C.LUI instead of LUI.
|
||||
delta += 2;
|
||||
remove(2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -953,35 +969,34 @@ void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc) {
|
||||
//
|
||||
// Here, we remove `lui` and `add` if the offset is within ±2 KiB.
|
||||
if (i64 val = sym.get_addr(ctx) + r.r_addend - ctx.tp_addr;
|
||||
sign_extend(val, 11) == val)
|
||||
delta += 4;
|
||||
is_int(val, 12))
|
||||
remove(4);
|
||||
break;
|
||||
case R_RISCV_TLSDESC_HI20:
|
||||
if (!sym.has_tlsdesc(ctx))
|
||||
delta += 4;
|
||||
remove(4);
|
||||
break;
|
||||
case R_RISCV_TLSDESC_LOAD_LO12:
|
||||
case R_RISCV_TLSDESC_ADD_LO12: {
|
||||
const ElfRel<E> &rel2 = rels[find_paired_reloc()];
|
||||
const ElfRel<E> &rel2 = find_paired_reloc(ctx, isec, rels, sym, i);
|
||||
Symbol<E> &sym2 = *isec.file.symbols[rel2.r_sym];
|
||||
|
||||
if (r.r_type == R_RISCV_TLSDESC_LOAD_LO12) {
|
||||
if (!sym2.has_tlsdesc(ctx))
|
||||
delta += 4;
|
||||
remove(4);
|
||||
} else {
|
||||
assert(r.r_type == R_RISCV_TLSDESC_ADD_LO12);
|
||||
if (!sym2.has_tlsdesc(ctx) && !sym2.has_gottp(ctx))
|
||||
if (i64 val = sym2.get_addr(ctx) + rel2.r_addend - ctx.tp_addr;
|
||||
sign_extend(val, 11) == val)
|
||||
delta += 4;
|
||||
is_int(val, 12))
|
||||
remove(4);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isec.extra.r_deltas[rels.size()] = delta;
|
||||
isec.sh_size -= delta;
|
||||
isec.sh_size -= r_delta;
|
||||
}
|
||||
|
||||
// ISA name handlers
|
||||
@@ -1100,15 +1115,12 @@ static std::vector<Extn> merge_extensions(std::span<Extn> x, std::span<Extn> y)
|
||||
return vec;
|
||||
}
|
||||
|
||||
static std::string to_string(const Extn &e) {
|
||||
return e.name + std::to_string(e.major) + "p" + std::to_string(e.minor);
|
||||
}
|
||||
|
||||
static std::string to_string(std::span<Extn> v) {
|
||||
std::string str = to_string(v[0]);
|
||||
for (i64 i = 1; i < v.size(); i++)
|
||||
str += "_" + to_string(v[i]);
|
||||
return str;
|
||||
std::ostringstream os;
|
||||
os << v[0].name << v[0].major << 'p' << v[0].minor;
|
||||
for (Extn &e : v.subspan(1))
|
||||
os << '_' << e.name << e.major << 'p' << e.minor;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
+33
-27
@@ -33,7 +33,7 @@
|
||||
// we need to add TP to a return value before use. I don't know why it is
|
||||
// different, but that is the way it is.
|
||||
//
|
||||
// https://github.com/rui314/psabi/blob/main/s390x.pdf
|
||||
// https://github.com/IBM/s390x-abi/releases/download/v1.6.1/lzsabi_s390x.pdf
|
||||
|
||||
#include "mold.h"
|
||||
|
||||
@@ -124,28 +124,24 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
u8 *loc = base + rel.r_offset;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
if (val < lo || hi <= val)
|
||||
Error(ctx) << *this << ": relocation " << rel << " against "
|
||||
<< sym << " out of range: " << val << " is not in ["
|
||||
<< lo << ", " << hi << ")";
|
||||
};
|
||||
|
||||
auto check_dbl = [&](i64 val, i64 lo, i64 hi) {
|
||||
check(val, lo, hi);
|
||||
|
||||
// R_390_*DBL relocs should never refer a symbol at an odd address
|
||||
if (val & 1)
|
||||
Error(ctx) << *this << ": misaligned symbol " << sym
|
||||
<< " for relocation " << rel;
|
||||
};
|
||||
|
||||
u64 S = sym.get_addr(ctx);
|
||||
u64 A = rel.r_addend;
|
||||
u64 P = get_addr() + rel.r_offset;
|
||||
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
|
||||
u64 GOT = ctx.got->shdr.sh_addr;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
check_range(ctx, i, val, lo, hi);
|
||||
};
|
||||
|
||||
auto check_dbl = [&](i64 val, i64 lo, i64 hi) {
|
||||
// R_390_*DBL relocs should never refer to a symbol at an odd address
|
||||
check(val, lo, hi);
|
||||
if (val & 1)
|
||||
Error(ctx) << *this << ": misaligned symbol " << sym
|
||||
<< " for relocation " << rel;
|
||||
};
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_390_64:
|
||||
break;
|
||||
@@ -170,9 +166,6 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
check(S + A, 0, 1LL << 32);
|
||||
*(ub32 *)loc = S + A;
|
||||
break;
|
||||
case R_390_PLT64:
|
||||
*(ub64 *)loc = S + A;
|
||||
break;
|
||||
case R_390_PC12DBL:
|
||||
case R_390_PLT12DBL:
|
||||
check_dbl(S + A - P, -(1 << 12), 1 << 12);
|
||||
@@ -187,6 +180,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
*(ub32 *)loc = S + A - P;
|
||||
break;
|
||||
case R_390_PC64:
|
||||
case R_390_PLT64:
|
||||
*(ub64 *)loc = S + A - P;
|
||||
break;
|
||||
case R_390_PC16DBL:
|
||||
@@ -250,6 +244,19 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
*(ub32 *)loc = (GOT + A - P) >> 1;
|
||||
break;
|
||||
case R_390_GOTENT:
|
||||
// If we can relax a GOT-loading LGRL to an address-materializing
|
||||
// LARL, do that. The format of LGRL is 0xc 0x4 <reg> 0x8 followed
|
||||
// by a 32-bit offset. LARL is 0xc 0x0 <reg> 0x0.
|
||||
if (ctx.arg.relax && sym.is_pcrel_linktime_const(ctx)) {
|
||||
u64 op = *(ub16 *)(loc - 2);
|
||||
u64 val = S + A - P;
|
||||
if ((op & 0xff0f) == 0xc408 && A == 2 && (val & 1) == 0 &&
|
||||
is_int(val, 33)) {
|
||||
*(ub16 *)(loc - 2) = 0xc000 | (op & 0x00f0);
|
||||
*(ub32 *)loc = val >> 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
check_dbl(GOT + G + A - P, -(1LL << 32), 1LL << 32);
|
||||
*(ub32 *)loc = (GOT + G + A - P) >> 1;
|
||||
break;
|
||||
@@ -337,13 +344,6 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
u8 *loc = base + rel.r_offset;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
if (val < lo || hi <= val)
|
||||
Error(ctx) << *this << ": relocation " << rel << " against "
|
||||
<< sym << " out of range: " << val << " is not in ["
|
||||
<< lo << ", " << hi << ")";
|
||||
};
|
||||
|
||||
SectionFragment<E> *frag;
|
||||
i64 frag_addend;
|
||||
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
|
||||
@@ -351,6 +351,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
|
||||
u64 A = frag ? frag_addend : (i64)rel.r_addend;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
check_range(ctx, val, i, lo, hi);
|
||||
};
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_390_32:
|
||||
check(S + A, 0, 1LL << 32);
|
||||
@@ -398,8 +402,10 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_390_32:
|
||||
scan_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_390_PC12DBL:
|
||||
case R_390_PC16:
|
||||
case R_390_PC16DBL:
|
||||
case R_390_PC24DBL:
|
||||
case R_390_PC32:
|
||||
case R_390_PC32DBL:
|
||||
case R_390_PC64:
|
||||
|
||||
+13
-24
@@ -51,9 +51,6 @@
|
||||
// address. That also means each function needs to execute the above piece
|
||||
// of code to become position-independent.
|
||||
//
|
||||
// This scheme is very similar to i386. That may not be a coincidence
|
||||
// because the i386 ELF psABI is created by Sun Microsystems too.
|
||||
//
|
||||
// https://github.com/rui314/psabi/blob/main/sparc.pdf
|
||||
|
||||
#include "mold.h"
|
||||
@@ -150,19 +147,16 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
u8 *loc = base + rel.r_offset;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
if (val < lo || hi <= val)
|
||||
Error(ctx) << *this << ": relocation " << rel << " against "
|
||||
<< sym << " out of range: " << val << " is not in ["
|
||||
<< lo << ", " << hi << ")";
|
||||
};
|
||||
|
||||
u64 S = sym.get_addr(ctx);
|
||||
u64 A = rel.r_addend;
|
||||
u64 P = (get_addr() + rel.r_offset);
|
||||
u64 G = (sym.get_got_idx(ctx) * sizeof(Word<E>));
|
||||
u64 P = get_addr() + rel.r_offset;
|
||||
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
|
||||
u64 GOT = ctx.got->shdr.sh_addr;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
check_range(ctx, i, val, lo, hi);
|
||||
};
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_SPARC_5:
|
||||
check(S + A, 0, 1 << 5);
|
||||
@@ -466,13 +460,6 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
u8 *loc = base + rel.r_offset;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
if (val < lo || hi <= val)
|
||||
Error(ctx) << *this << ": relocation " << rel << " against "
|
||||
<< sym << " out of range: " << val << " is not in ["
|
||||
<< lo << ", " << hi << ")";
|
||||
};
|
||||
|
||||
SectionFragment<E> *frag;
|
||||
i64 frag_addend;
|
||||
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
|
||||
@@ -480,6 +467,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
|
||||
u64 A = frag ? frag_addend : (i64)rel.r_addend;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
check_range(ctx, val, i, lo, hi);
|
||||
};
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_SPARC_64:
|
||||
case R_SPARC_UA64:
|
||||
@@ -489,12 +480,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
*(ub64 *)loc = S + A;
|
||||
break;
|
||||
case R_SPARC_32:
|
||||
case R_SPARC_UA32: {
|
||||
i64 val = S + A;
|
||||
check(val, 0, 1LL << 32);
|
||||
*(ub32 *)loc = val;
|
||||
case R_SPARC_UA32:
|
||||
check(S + A, 0, 1LL << 32);
|
||||
*(ub32 *)loc = S + A;
|
||||
break;
|
||||
}
|
||||
case R_SPARC_TLS_DTPOFF32:
|
||||
*(ub32 *)loc = S + A - ctx.dtp_addr;
|
||||
break;
|
||||
|
||||
+190
-139
@@ -23,8 +23,7 @@
|
||||
// TLS block as TP (with some addend). As a result, offsets from TP to
|
||||
// thread-local variables (TLVs) in the main executable are all negative.
|
||||
//
|
||||
// https://github.com/rui314/psabi/blob/main/x86-64.pdf
|
||||
// https://github.com/rui314/psabi/blob/main/i386.pdf
|
||||
// https://gitlab.com/x86-psABIs/x86-64-ABI
|
||||
|
||||
#include "mold.h"
|
||||
|
||||
@@ -128,98 +127,165 @@ void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
|
||||
}
|
||||
}
|
||||
|
||||
static u32 relax_gotpcrelx(u8 *loc) {
|
||||
switch ((loc[0] << 8) | loc[1]) {
|
||||
case 0xff15: return 0x90e8; // call *0(%rip) -> call 0
|
||||
case 0xff25: return 0x90e9; // jmp *0(%rip) -> jmp 0
|
||||
static u32 relax_gotpcrelx(u8 *loc, const ElfRel<E> &rel) {
|
||||
if (rel.r_type == R_X86_64_GOTPCRELX) {
|
||||
switch ((loc[-2] << 8) | loc[-1]) {
|
||||
case 0xff15: return 0x40e8; // call *0(%rip) -> call 0
|
||||
case 0xff25: return 0x40e9; // jmp *0(%rip) -> jmp 0
|
||||
}
|
||||
} else {
|
||||
assert(rel.r_type == R_X86_64_REX_GOTPCRELX ||
|
||||
rel.r_type == R_X86_64_CODE_4_GOTPCRELX);
|
||||
switch ((loc[-3] << 16) | (loc[-2] << 8) | loc[-1]) {
|
||||
case 0x488b05: return 0x8d05; // mov 0(%rip), %rax -> lea 0(%rip), %rax
|
||||
case 0x488b0d: return 0x8d0d; // mov 0(%rip), %rcx -> lea 0(%rip), %rcx
|
||||
case 0x488b15: return 0x8d15; // mov 0(%rip), %rdx -> lea 0(%rip), %rdx
|
||||
case 0x488b1d: return 0x8d1d; // mov 0(%rip), %rbx -> lea 0(%rip), %rbx
|
||||
case 0x488b25: return 0x8d25; // mov 0(%rip), %rsp -> lea 0(%rip), %rsp
|
||||
case 0x488b2d: return 0x8d2d; // mov 0(%rip), %rbp -> lea 0(%rip), %rbp
|
||||
case 0x488b35: return 0x8d35; // mov 0(%rip), %rsi -> lea 0(%rip), %rsi
|
||||
case 0x488b3d: return 0x8d3d; // mov 0(%rip), %rdi -> lea 0(%rip), %rdi
|
||||
case 0x4c8b05: return 0x8d05; // mov 0(%rip), %r8 -> lea 0(%rip), %r8
|
||||
case 0x4c8b0d: return 0x8d0d; // mov 0(%rip), %r9 -> lea 0(%rip), %r9
|
||||
case 0x4c8b15: return 0x8d15; // mov 0(%rip), %r10 -> lea 0(%rip), %r10
|
||||
case 0x4c8b1d: return 0x8d1d; // mov 0(%rip), %r11 -> lea 0(%rip), %r11
|
||||
case 0x4c8b25: return 0x8d25; // mov 0(%rip), %r12 -> lea 0(%rip), %r12
|
||||
case 0x4c8b2d: return 0x8d2d; // mov 0(%rip), %r13 -> lea 0(%rip), %r13
|
||||
case 0x4c8b35: return 0x8d35; // mov 0(%rip), %r14 -> lea 0(%rip), %r14
|
||||
case 0x4c8b3d: return 0x8d3d; // mov 0(%rip), %r15 -> lea 0(%rip), %r15
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 relax_rex_gotpcrelx(u8 *loc) {
|
||||
switch ((loc[0] << 16) | (loc[1] << 8) | loc[2]) {
|
||||
case 0x488b05: return 0x488d05; // mov 0(%rip), %rax -> lea 0(%rip), %rax
|
||||
case 0x488b0d: return 0x488d0d; // mov 0(%rip), %rcx -> lea 0(%rip), %rcx
|
||||
case 0x488b15: return 0x488d15; // mov 0(%rip), %rdx -> lea 0(%rip), %rdx
|
||||
case 0x488b1d: return 0x488d1d; // mov 0(%rip), %rbx -> lea 0(%rip), %rbx
|
||||
case 0x488b25: return 0x488d25; // mov 0(%rip), %rsp -> lea 0(%rip), %rsp
|
||||
case 0x488b2d: return 0x488d2d; // mov 0(%rip), %rbp -> lea 0(%rip), %rbp
|
||||
case 0x488b35: return 0x488d35; // mov 0(%rip), %rsi -> lea 0(%rip), %rsi
|
||||
case 0x488b3d: return 0x488d3d; // mov 0(%rip), %rdi -> lea 0(%rip), %rdi
|
||||
case 0x4c8b05: return 0x4c8d05; // mov 0(%rip), %r8 -> lea 0(%rip), %r8
|
||||
case 0x4c8b0d: return 0x4c8d0d; // mov 0(%rip), %r9 -> lea 0(%rip), %r9
|
||||
case 0x4c8b15: return 0x4c8d15; // mov 0(%rip), %r10 -> lea 0(%rip), %r10
|
||||
case 0x4c8b1d: return 0x4c8d1d; // mov 0(%rip), %r11 -> lea 0(%rip), %r11
|
||||
case 0x4c8b25: return 0x4c8d25; // mov 0(%rip), %r12 -> lea 0(%rip), %r12
|
||||
case 0x4c8b2d: return 0x4c8d2d; // mov 0(%rip), %r13 -> lea 0(%rip), %r13
|
||||
case 0x4c8b35: return 0x4c8d35; // mov 0(%rip), %r14 -> lea 0(%rip), %r14
|
||||
case 0x4c8b3d: return 0x4c8d3d; // mov 0(%rip), %r15 -> lea 0(%rip), %r15
|
||||
static u32 relax_gottpoff(u8 *loc, const ElfRel<E> &rel) {
|
||||
if (rel.r_type == R_X86_64_GOTTPOFF) {
|
||||
switch ((loc[-3] << 16) | (loc[-2] << 8) | loc[-1]) {
|
||||
case 0x488b05: return 0x48c7c0; // mov 0(%rip), %rax -> mov $0, %rax
|
||||
case 0x488b0d: return 0x48c7c1; // mov 0(%rip), %rcx -> mov $0, %rcx
|
||||
case 0x488b15: return 0x48c7c2; // mov 0(%rip), %rdx -> mov $0, %rdx
|
||||
case 0x488b1d: return 0x48c7c3; // mov 0(%rip), %rbx -> mov $0, %rbx
|
||||
case 0x488b25: return 0x48c7c4; // mov 0(%rip), %rsp -> mov $0, %rsp
|
||||
case 0x488b2d: return 0x48c7c5; // mov 0(%rip), %rbp -> mov $0, %rbp
|
||||
case 0x488b35: return 0x48c7c6; // mov 0(%rip), %rsi -> mov $0, %rsi
|
||||
case 0x488b3d: return 0x48c7c7; // mov 0(%rip), %rdi -> mov $0, %rdi
|
||||
case 0x4c8b05: return 0x49c7c0; // mov 0(%rip), %r8 -> mov $0, %r8
|
||||
case 0x4c8b0d: return 0x49c7c1; // mov 0(%rip), %r9 -> mov $0, %r9
|
||||
case 0x4c8b15: return 0x49c7c2; // mov 0(%rip), %r10 -> mov $0, %r10
|
||||
case 0x4c8b1d: return 0x49c7c3; // mov 0(%rip), %r11 -> mov $0, %r11
|
||||
case 0x4c8b25: return 0x49c7c4; // mov 0(%rip), %r12 -> mov $0, %r12
|
||||
case 0x4c8b2d: return 0x49c7c5; // mov 0(%rip), %r13 -> mov $0, %r13
|
||||
case 0x4c8b35: return 0x49c7c6; // mov 0(%rip), %r14 -> mov $0, %r14
|
||||
case 0x4c8b3d: return 0x49c7c7; // mov 0(%rip), %r15 -> mov $0, %r15
|
||||
}
|
||||
} else {
|
||||
assert(rel.r_type == R_X86_64_CODE_4_GOTTPOFF);
|
||||
switch ((loc[-3] << 16) | (loc[-2] << 8) | loc[-1]) {
|
||||
case 0x488b05: return 0x18c7c0; // mov 0(%rip), %r16 -> mov $0, %r16
|
||||
case 0x488b0d: return 0x18c7c1; // mov 0(%rip), %r17 -> mov $0, %r17
|
||||
case 0x488b15: return 0x18c7c2; // mov 0(%rip), %r18 -> mov $0, %r18
|
||||
case 0x488b1d: return 0x18c7c3; // mov 0(%rip), %r19 -> mov $0, %r19
|
||||
case 0x488b25: return 0x18c7c4; // mov 0(%rip), %r20 -> mov $0, %r20
|
||||
case 0x488b2d: return 0x18c7c5; // mov 0(%rip), %r21 -> mov $0, %r21
|
||||
case 0x488b35: return 0x18c7c6; // mov 0(%rip), %r22 -> mov $0, %r22
|
||||
case 0x488b3d: return 0x18c7c7; // mov 0(%rip), %r23 -> mov $0, %r23
|
||||
case 0x4c8b05: return 0x19c7c0; // mov 0(%rip), %r24 -> mov $0, %r24
|
||||
case 0x4c8b0d: return 0x19c7c1; // mov 0(%rip), %r25 -> mov $0, %r25
|
||||
case 0x4c8b15: return 0x19c7c2; // mov 0(%rip), %r26 -> mov $0, %r26
|
||||
case 0x4c8b1d: return 0x19c7c3; // mov 0(%rip), %r27 -> mov $0, %r27
|
||||
case 0x4c8b25: return 0x19c7c4; // mov 0(%rip), %r28 -> mov $0, %r28
|
||||
case 0x4c8b2d: return 0x19c7c5; // mov 0(%rip), %r29 -> mov $0, %r29
|
||||
case 0x4c8b35: return 0x19c7c6; // mov 0(%rip), %r30 -> mov $0, %r30
|
||||
case 0x4c8b3d: return 0x19c7c7; // mov 0(%rip), %r31 -> mov $0, %r31
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 relax_gottpoff(u8 *loc) {
|
||||
switch ((loc[0] << 16) | (loc[1] << 8) | loc[2]) {
|
||||
case 0x488b05: return 0x48c7c0; // mov 0(%rip), %rax -> mov $0, %rax
|
||||
case 0x488b0d: return 0x48c7c1; // mov 0(%rip), %rcx -> mov $0, %rcx
|
||||
case 0x488b15: return 0x48c7c2; // mov 0(%rip), %rdx -> mov $0, %rdx
|
||||
case 0x488b1d: return 0x48c7c3; // mov 0(%rip), %rbx -> mov $0, %rbx
|
||||
case 0x488b25: return 0x48c7c4; // mov 0(%rip), %rsp -> mov $0, %rsp
|
||||
case 0x488b2d: return 0x48c7c5; // mov 0(%rip), %rbp -> mov $0, %rbp
|
||||
case 0x488b35: return 0x48c7c6; // mov 0(%rip), %rsi -> mov $0, %rsi
|
||||
case 0x488b3d: return 0x48c7c7; // mov 0(%rip), %rdi -> mov $0, %rdi
|
||||
case 0x4c8b05: return 0x49c7c0; // mov 0(%rip), %r8 -> mov $0, %r8
|
||||
case 0x4c8b0d: return 0x49c7c1; // mov 0(%rip), %r9 -> mov $0, %r9
|
||||
case 0x4c8b15: return 0x49c7c2; // mov 0(%rip), %r10 -> mov $0, %r10
|
||||
case 0x4c8b1d: return 0x49c7c3; // mov 0(%rip), %r11 -> mov $0, %r11
|
||||
case 0x4c8b25: return 0x49c7c4; // mov 0(%rip), %r12 -> mov $0, %r12
|
||||
case 0x4c8b2d: return 0x49c7c5; // mov 0(%rip), %r13 -> mov $0, %r13
|
||||
case 0x4c8b35: return 0x49c7c6; // mov 0(%rip), %r14 -> mov $0, %r14
|
||||
case 0x4c8b3d: return 0x49c7c7; // mov 0(%rip), %r15 -> mov $0, %r15
|
||||
static u32 relax_tlsdesc_to_ie(u8 *loc, const ElfRel<E> &rel) {
|
||||
if (rel.r_type == R_X86_64_GOTPC32_TLSDESC) {
|
||||
switch ((loc[-3] << 16) | (loc[-2] << 8) | loc[-1]) {
|
||||
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
|
||||
}
|
||||
} else {
|
||||
assert(rel.r_type == R_X86_64_CODE_4_GOTPC32_TLSDESC);
|
||||
switch ((loc[-3] << 16) | (loc[-2] << 8) | loc[-1]) {
|
||||
case 0x488d05: return 0x488b05; // lea 0(%rip), %r16 -> mov 0(%rip), %r16
|
||||
case 0x488d0d: return 0x488b0d; // lea 0(%rip), %r17 -> mov 0(%rip), %r17
|
||||
case 0x488d15: return 0x488b15; // lea 0(%rip), %r18 -> mov 0(%rip), %r18
|
||||
case 0x488d1d: return 0x488b1d; // lea 0(%rip), %r19 -> mov 0(%rip), %r19
|
||||
case 0x488d25: return 0x488b25; // lea 0(%rip), %r20 -> mov 0(%rip), %r20
|
||||
case 0x488d2d: return 0x488b2d; // lea 0(%rip), %r21 -> mov 0(%rip), %r21
|
||||
case 0x488d35: return 0x488b35; // lea 0(%rip), %r22 -> mov 0(%rip), %r22
|
||||
case 0x488d3d: return 0x488b3d; // lea 0(%rip), %r23 -> mov 0(%rip), %r23
|
||||
case 0x4c8d05: return 0x4c8b05; // lea 0(%rip), %r24 -> mov 0(%rip), %r24
|
||||
case 0x4c8d0d: return 0x4c8b0d; // lea 0(%rip), %r25 -> mov 0(%rip), %r25
|
||||
case 0x4c8d15: return 0x4c8b15; // lea 0(%rip), %r26 -> mov 0(%rip), %r26
|
||||
case 0x4c8d1d: return 0x4c8b1d; // lea 0(%rip), %r27 -> mov 0(%rip), %r27
|
||||
case 0x4c8d25: return 0x4c8b25; // lea 0(%rip), %r28 -> mov 0(%rip), %r28
|
||||
case 0x4c8d2d: return 0x4c8b2d; // lea 0(%rip), %r29 -> mov 0(%rip), %r29
|
||||
case 0x4c8d35: return 0x4c8b35; // lea 0(%rip), %r30 -> mov 0(%rip), %r30
|
||||
case 0x4c8d3d: return 0x4c8b3d; // lea 0(%rip), %r31 -> mov 0(%rip), %r31
|
||||
}
|
||||
}
|
||||
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
|
||||
static u32 relax_tlsdesc_to_le(u8 *loc, const ElfRel<E> &rel) {
|
||||
if (rel.r_type == R_X86_64_GOTPC32_TLSDESC) {
|
||||
switch ((loc[-3] << 16) | (loc[-2] << 8) | loc[-1]) {
|
||||
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
|
||||
}
|
||||
} else {
|
||||
assert(rel.r_type == R_X86_64_CODE_4_GOTPC32_TLSDESC);
|
||||
switch ((loc[-3] << 16) | (loc[-2] << 8) | loc[-1]) {
|
||||
case 0x488d05: return 0x18c7c0; // lea 0(%rip), %r16 -> mov $0, %r16
|
||||
case 0x488d0d: return 0x18c7c1; // lea 0(%rip), %r17 -> mov $0, %r17
|
||||
case 0x488d15: return 0x18c7c2; // lea 0(%rip), %r18 -> mov $0, %r18
|
||||
case 0x488d1d: return 0x18c7c3; // lea 0(%rip), %r19 -> mov $0, %r19
|
||||
case 0x488d25: return 0x18c7c4; // lea 0(%rip), %r20 -> mov $0, %r20
|
||||
case 0x488d2d: return 0x18c7c5; // lea 0(%rip), %r21 -> mov $0, %r21
|
||||
case 0x488d35: return 0x18c7c6; // lea 0(%rip), %r22 -> mov $0, %r22
|
||||
case 0x488d3d: return 0x18c7c7; // lea 0(%rip), %r23 -> mov $0, %r23
|
||||
case 0x4c8d05: return 0x19c7c0; // lea 0(%rip), %r24 -> mov $0, %r24
|
||||
case 0x4c8d0d: return 0x19c7c1; // lea 0(%rip), %r25 -> mov $0, %r25
|
||||
case 0x4c8d15: return 0x19c7c2; // lea 0(%rip), %r26 -> mov $0, %r26
|
||||
case 0x4c8d1d: return 0x19c7c3; // lea 0(%rip), %r27 -> mov $0, %r27
|
||||
case 0x4c8d25: return 0x19c7c4; // lea 0(%rip), %r28 -> mov $0, %r28
|
||||
case 0x4c8d2d: return 0x19c7c5; // lea 0(%rip), %r29 -> mov $0, %r29
|
||||
case 0x4c8d35: return 0x19c7c6; // lea 0(%rip), %r30 -> mov $0, %r30
|
||||
case 0x4c8d3d: return 0x19c7c7; // lea 0(%rip), %r31 -> mov $0, %r31
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -227,7 +293,7 @@ static u32 relax_tlsdesc_to_le(u8 *loc) {
|
||||
// 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.
|
||||
static void relax_gd_to_le(u8 *loc, ElfRel<E> rel, u64 val) {
|
||||
static void relax_gd_to_le(u8 *loc, const ElfRel<E> &rel, u64 val) {
|
||||
switch (rel.r_type) {
|
||||
case R_X86_64_PLT32:
|
||||
case R_X86_64_PC32:
|
||||
@@ -271,7 +337,7 @@ static void relax_gd_to_le(u8 *loc, ElfRel<E> rel, u64 val) {
|
||||
}
|
||||
}
|
||||
|
||||
static void relax_gd_to_ie(u8 *loc, ElfRel<E> rel, u64 val) {
|
||||
static void relax_gd_to_ie(u8 *loc, const ElfRel<E> &rel, u64 val) {
|
||||
switch (rel.r_type) {
|
||||
case R_X86_64_PLT32:
|
||||
case R_X86_64_PC32:
|
||||
@@ -304,7 +370,7 @@ static void relax_gd_to_ie(u8 *loc, ElfRel<E> rel, u64 val) {
|
||||
// sequence. The difference from relax_gd_to_le is that we are materializing
|
||||
// the address of the beginning of TLS block instead of an address of a
|
||||
// particular thread-local variable.
|
||||
static void relax_ld_to_le(u8 *loc, ElfRel<E> rel, i64 tls_size) {
|
||||
static void relax_ld_to_le(u8 *loc, const ElfRel<E> &rel, i64 tls_size) {
|
||||
switch (rel.r_type) {
|
||||
case R_X86_64_PLT32:
|
||||
case R_X86_64_PC32: {
|
||||
@@ -376,11 +442,14 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
u8 *loc = base + rel.r_offset;
|
||||
|
||||
u64 S = sym.get_addr(ctx);
|
||||
u64 A = rel.r_addend;
|
||||
u64 P = get_addr() + rel.r_offset;
|
||||
u64 G = sym.get_got_addr(ctx) - ctx.gotplt->shdr.sh_addr;
|
||||
u64 GOT = ctx.gotplt->shdr.sh_addr;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
if (val < lo || hi <= val)
|
||||
Error(ctx) << *this << ": relocation " << rel << " against "
|
||||
<< sym << " out of range: " << val << " is not in ["
|
||||
<< lo << ", " << hi << ")";
|
||||
check_range(ctx, i, val, lo, hi);
|
||||
};
|
||||
|
||||
auto write32 = [&](u64 val) {
|
||||
@@ -393,12 +462,6 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
*(ul32 *)loc = val;
|
||||
};
|
||||
|
||||
u64 S = sym.get_addr(ctx);
|
||||
u64 A = rel.r_addend;
|
||||
u64 P = get_addr() + rel.r_offset;
|
||||
u64 G = sym.get_got_addr(ctx) - ctx.gotplt->shdr.sh_addr;
|
||||
u64 GOT = ctx.gotplt->shdr.sh_addr;
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_X86_64_8:
|
||||
check(S + A, 0, 1 << 8);
|
||||
@@ -454,30 +517,16 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
*(ul64 *)loc = G + GOT + A - P;
|
||||
break;
|
||||
case R_X86_64_GOTPCRELX:
|
||||
case R_X86_64_REX_GOTPCRELX:
|
||||
case R_X86_64_CODE_4_GOTPCRELX:
|
||||
// We always want to relax GOTPCRELX relocs even if --no-relax
|
||||
// was given because some static PIE runtime code depends on these
|
||||
// relaxations.
|
||||
if (sym.is_pcrel_linktime_const(ctx)) {
|
||||
u32 insn = relax_gotpcrelx(loc - 2);
|
||||
i64 val = S + A - P;
|
||||
if (insn && (i32)val == val) {
|
||||
if (sym.is_pcrel_linktime_const(ctx) && is_int(S + A - P, 32)) {
|
||||
if (u32 insn = relax_gotpcrelx(loc, rel)) {
|
||||
loc[-2] = insn >> 8;
|
||||
loc[-1] = insn;
|
||||
*(ul32 *)loc = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
write32s(G + GOT + A - P);
|
||||
break;
|
||||
case R_X86_64_REX_GOTPCRELX:
|
||||
if (sym.is_pcrel_linktime_const(ctx)) {
|
||||
u32 insn = relax_rex_gotpcrelx(loc - 3);
|
||||
i64 val = S + A - P;
|
||||
if (insn && (i32)val == val) {
|
||||
loc[-3] = insn >> 16;
|
||||
loc[-2] = insn >> 8;
|
||||
loc[-1] = insn;
|
||||
*(ul32 *)loc = val;
|
||||
*(ul32 *)loc = S + A - P;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -510,18 +559,22 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
*(ul64 *)loc = S + A - ctx.tp_addr;
|
||||
break;
|
||||
case R_X86_64_GOTTPOFF:
|
||||
case R_X86_64_CODE_4_GOTTPOFF:
|
||||
if (sym.has_gottp(ctx)) {
|
||||
write32s(sym.get_gottp_addr(ctx) + A - P);
|
||||
} else {
|
||||
u32 insn = relax_gottpoff(loc - 3);
|
||||
u32 insn = relax_gottpoff(loc, rel);
|
||||
loc[-3] = insn >> 16;
|
||||
loc[-2] = insn >> 8;
|
||||
loc[-1] = insn;
|
||||
write32s(S - ctx.tp_addr);
|
||||
assert(A == -4);
|
||||
}
|
||||
break;
|
||||
case R_X86_64_CODE_6_GOTTPOFF:
|
||||
write32s(sym.get_gottp_addr(ctx) + A - P);
|
||||
break;
|
||||
case R_X86_64_GOTPC32_TLSDESC:
|
||||
case R_X86_64_CODE_4_GOTPC32_TLSDESC:
|
||||
// x86-64 TLSDESC uses the following code sequence to materialize
|
||||
// a TP-relative address in %rax.
|
||||
//
|
||||
@@ -553,17 +606,17 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
if (sym.has_tlsdesc(ctx)) {
|
||||
write32s(sym.get_tlsdesc_addr(ctx) + A - P);
|
||||
} else if (sym.has_gottp(ctx)) {
|
||||
u32 insn = relax_tlsdesc_to_ie(loc - 3);
|
||||
u32 insn = relax_tlsdesc_to_ie(loc, rel);
|
||||
if (!insn)
|
||||
Fatal(ctx) << *this << ": illegal instruction sequence for TLSDESC";
|
||||
Fatal(ctx) << *this << ": illegal instruction sequence for " << rel;
|
||||
loc[-3] = insn >> 16;
|
||||
loc[-2] = insn >> 8;
|
||||
loc[-1] = insn;
|
||||
write32s(sym.get_gottp_addr(ctx) + A - P);
|
||||
} else {
|
||||
u32 insn = relax_tlsdesc_to_le(loc - 3);
|
||||
u32 insn = relax_tlsdesc_to_le(loc, rel);
|
||||
if (!insn)
|
||||
Fatal(ctx) << *this << ": illegal instruction sequence for TLSDESC";
|
||||
Fatal(ctx) << *this << ": illegal instruction sequence for " << rel;
|
||||
loc[-3] = insn >> 16;
|
||||
loc[-2] = insn >> 8;
|
||||
loc[-1] = insn;
|
||||
@@ -571,9 +624,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
}
|
||||
break;
|
||||
case R_X86_64_TLSDESC_CALL:
|
||||
if (sym.has_tlsdesc(ctx)) {
|
||||
// Do nothing
|
||||
} else {
|
||||
if (!sym.has_tlsdesc(ctx)) {
|
||||
// call *(%rax) -> nop
|
||||
loc[0] = 0x66;
|
||||
loc[1] = 0x90;
|
||||
@@ -615,11 +666,15 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
u8 *loc = base + rel.r_offset;
|
||||
|
||||
SectionFragment<E> *frag;
|
||||
i64 frag_addend;
|
||||
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
|
||||
|
||||
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
|
||||
u64 A = frag ? frag_addend : (i64)rel.r_addend;
|
||||
|
||||
auto check = [&](i64 val, i64 lo, i64 hi) {
|
||||
if (val < lo || hi <= val)
|
||||
Error(ctx) << *this << ": relocation " << rel << " against "
|
||||
<< sym << " out of range: " << val << " is not in ["
|
||||
<< lo << ", " << hi << ")";
|
||||
check_range(ctx, i, val, lo, hi);
|
||||
};
|
||||
|
||||
auto write32 = [&](u64 val) {
|
||||
@@ -632,13 +687,6 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
*(ul32 *)loc = val;
|
||||
};
|
||||
|
||||
SectionFragment<E> *frag;
|
||||
i64 frag_addend;
|
||||
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
|
||||
|
||||
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
|
||||
u64 A = frag ? frag_addend : (i64)rel.r_addend;
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_X86_64_8:
|
||||
check(S + A, 0, 1 << 8);
|
||||
@@ -751,6 +799,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_X86_64_GOTPCREL64:
|
||||
case R_X86_64_GOTPCRELX:
|
||||
case R_X86_64_REX_GOTPCRELX:
|
||||
case R_X86_64_CODE_4_GOTPCRELX:
|
||||
sym.flags |= NEEDS_GOT;
|
||||
break;
|
||||
case R_X86_64_PLT32:
|
||||
@@ -779,12 +828,13 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
ctx.needs_tlsld = true;
|
||||
break;
|
||||
case R_X86_64_GOTTPOFF:
|
||||
if (ctx.arg.relax && relax_gottpoff(loc - 3) &&
|
||||
sym.is_tprel_linktime_const(ctx)) {
|
||||
// do nothing
|
||||
} else {
|
||||
case R_X86_64_CODE_4_GOTTPOFF:
|
||||
if (!ctx.arg.relax || !sym.is_tprel_linktime_const(ctx) ||
|
||||
!relax_gottpoff(loc, rel))
|
||||
sym.flags |= NEEDS_GOTTP;
|
||||
}
|
||||
break;
|
||||
case R_X86_64_CODE_6_GOTTPOFF:
|
||||
sym.flags |= NEEDS_GOTTP;
|
||||
break;
|
||||
case R_X86_64_TLSDESC_CALL:
|
||||
scan_tlsdesc(ctx, sym);
|
||||
@@ -800,6 +850,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_X86_64_SIZE32:
|
||||
case R_X86_64_SIZE64:
|
||||
case R_X86_64_GOTPC32_TLSDESC:
|
||||
case R_X86_64_CODE_4_GOTPC32_TLSDESC:
|
||||
break;
|
||||
default:
|
||||
Error(ctx) << *this << ": unknown relocation: " << rel;
|
||||
|
||||
+76
-51
@@ -1,10 +1,10 @@
|
||||
#include "mold.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <random>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <system_error>
|
||||
#include <tbb/global_control.h>
|
||||
#include <unordered_set>
|
||||
|
||||
@@ -41,6 +41,8 @@ Options:
|
||||
-N, --omagic Do not page align data; do not make text readonly
|
||||
--no-omagic
|
||||
-O NUMBER Ignored
|
||||
-P AUDITLIB, --depaudit AUDITLIB
|
||||
Set DT_DEPAUDIT to the specified value
|
||||
-S, --strip-debug Strip .debug_* sections
|
||||
-T FILE, --script FILE Read linker script
|
||||
-X, --discard-locals Discard temporary local symbols
|
||||
@@ -76,6 +78,7 @@ Options:
|
||||
--no-apply-dynamic-relocs
|
||||
--as-needed Only set DT_NEEDED if used
|
||||
--no-as-needed
|
||||
--audit LIBNAME Set DT_AUDIT to the specified value
|
||||
--build-id [none,md5,sha1,sha256,fast,uuid,HEXSTRING]
|
||||
Generate build ID
|
||||
--no-build-id
|
||||
@@ -127,7 +130,8 @@ Options:
|
||||
--oformat=binary Omit ELF, section, and program headers
|
||||
--pack-dyn-relocs=[relr,none]
|
||||
Pack dynamic relocations
|
||||
--package-metadata=STRING Set a given string to .note.package
|
||||
--package-metadata=PERCENT_ENCODED_STRING
|
||||
Set a given string to .note.package
|
||||
--perf Print performance statistics
|
||||
--pie, --pic-executable Create a position-independent executable
|
||||
--no-pie, --no-pic-executable
|
||||
@@ -402,12 +406,12 @@ static std::vector<u8> parse_hex_build_id(Context<E> &ctx, std::string_view arg)
|
||||
|
||||
template <typename E>
|
||||
static std::string
|
||||
parse_encoded_package_metadata(Context<E> &ctx, std::string_view arg) {
|
||||
parse_package_metadata(Context<E> &ctx, std::string_view arg) {
|
||||
auto flags = std::regex_constants::optimize | std::regex_constants::ECMAScript;
|
||||
static std::regex re(R"(([^%]|%[0-9a-fA-F][0-9a-fA-F])*)", flags);
|
||||
|
||||
if (!std::regex_match(arg.begin(), arg.end(), re))
|
||||
Fatal(ctx) << "--encoded-package-metadata: invalid string: " << arg;
|
||||
Fatal(ctx) << "--package-metadata: invalid string: " << arg;
|
||||
|
||||
std::ostringstream out;
|
||||
while (!arg.empty()) {
|
||||
@@ -464,10 +468,9 @@ static void read_retain_symbols_file(Context<E> &ctx, std::string_view path) {
|
||||
ctx.arg.retain_symbols_file = std::move(vec);
|
||||
}
|
||||
|
||||
static bool is_file(std::string_view path) {
|
||||
struct stat st;
|
||||
return stat(std::string(path).c_str(), &st) == 0 &&
|
||||
(st.st_mode & S_IFMT) != S_IFDIR;
|
||||
static bool is_file(const std::filesystem::path& path) {
|
||||
std::error_code error;
|
||||
return !std::filesystem::is_directory(path, error) && !error;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -555,18 +558,19 @@ parse_kernel_version(std::string str) {
|
||||
return {std::stoi(m[1]), std::stoi(m[2]), std::stoi(m[3])};
|
||||
}
|
||||
|
||||
// Since 6.11.0, the Linux kernel no longer returns ETXTBSY for open(2)
|
||||
// on an executable file that is currently running. This function
|
||||
// returns true if we are running on a Linux kernel older than 6.11.0.
|
||||
// Version 6.11 and 6.12 of the Linux kernel does not return ETXTBSY for
|
||||
// open(2) on an executable file that is currently running. This function
|
||||
// returns true if we are running on a Linux kernel older than 6.11 or newer
|
||||
// than 6.12.
|
||||
static bool returns_etxtbsy() {
|
||||
#if HAVE_UNAME
|
||||
struct utsname buf;
|
||||
return uname(&buf) == 0 &&
|
||||
strcmp(buf.sysname, "Linux") == 0 &&
|
||||
parse_kernel_version(buf.release) < std::tuple{6, 11, 0};
|
||||
#else
|
||||
return false;
|
||||
if (uname(&buf) == 0 && strcmp(buf.sysname, "Linux") == 0) {
|
||||
std::tuple<int, int, int> ver = parse_kernel_version(buf.release);
|
||||
return ver < std::tuple{6, 11, 0} || std::tuple{6, 13, 0} <= ver;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -585,6 +589,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
std::optional<SeparateCodeKind> z_separate_code;
|
||||
std::optional<bool> report_undefined;
|
||||
std::optional<bool> z_relro;
|
||||
std::optional<bool> z_dynamic_undefined_weak;
|
||||
std::optional<std::string> separate_debug_file;
|
||||
std::optional<u64> shuffle_sections_seed;
|
||||
std::unordered_set<std::string_view> rpaths;
|
||||
@@ -713,8 +718,11 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
<< " elf64briscv\n elf32lriscv\n elf32briscv\n"
|
||||
<< " elf32ppc\n elf64ppc\n elf64lppc\n elf64_s390\n"
|
||||
<< " elf64_sparc\n m68kelf\n shlelf_linux\n"
|
||||
<< " shelf_linux\n elf64loongarch\n elf32loongarch";
|
||||
<< " shelf_linux\n elf64loongarch\n elf32loongarch\n"
|
||||
<< " arclinux";
|
||||
version_shown = true;
|
||||
} else if (read_arg("mllvm")) {
|
||||
ctx.arg.plugin_opt.emplace_back(arg);
|
||||
} else if (read_arg("m")) {
|
||||
if (arg == "elf_x86_64") {
|
||||
ctx.arg.emulation = X86_64::name;
|
||||
@@ -754,11 +762,13 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
ctx.arg.emulation = LOONGARCH64::name;
|
||||
} else if (arg == "elf32loongarch") {
|
||||
ctx.arg.emulation = LOONGARCH32::name;
|
||||
} else if (arg == "arclinux") {
|
||||
ctx.arg.emulation = ARC::name;
|
||||
} else {
|
||||
Fatal(ctx) << "unknown -m argument: " << arg;
|
||||
}
|
||||
} else if (read_flag("end-lib")) {
|
||||
remaining.push_back("--end-lib");
|
||||
remaining.emplace_back("--end-lib");
|
||||
} else if (read_flag("export-dynamic") || read_flag("E")) {
|
||||
ctx.arg.export_dynamic = true;
|
||||
} else if (read_flag("no-export-dynamic")) {
|
||||
@@ -789,10 +799,10 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
ctx.arg.print_map = true;
|
||||
} else if (read_flag("Bstatic") || read_flag("dn") || read_flag("static")) {
|
||||
ctx.arg.static_ = true;
|
||||
remaining.push_back("--Bstatic");
|
||||
remaining.emplace_back("--Bstatic");
|
||||
} else if (read_flag("Bdynamic") || read_flag("dy")) {
|
||||
ctx.arg.static_ = false;
|
||||
remaining.push_back("--Bdynamic");
|
||||
remaining.emplace_back("--Bdynamic");
|
||||
} else if (read_flag("shared") || read_flag("Bshareable")) {
|
||||
ctx.arg.shared = true;
|
||||
} else if (read_arg("spare-dynamic-tags")) {
|
||||
@@ -801,7 +811,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
ctx.arg.spare_program_headers
|
||||
= parse_number(ctx, "spare-program-headers", arg);
|
||||
} else if (read_flag("start-lib")) {
|
||||
remaining.push_back("--start-lib");
|
||||
remaining.emplace_back("--start-lib");
|
||||
} else if (read_flag("start-stop")) {
|
||||
ctx.arg.start_stop = true;
|
||||
} else if (read_arg("dependency-file")) {
|
||||
@@ -844,7 +854,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
} else if (read_arg("filler")) {
|
||||
ctx.arg.filler = parse_hex(ctx, "filler", arg);
|
||||
} else if (read_arg("L") || read_arg("library-path")) {
|
||||
ctx.arg.library_paths.push_back(std::string(arg));
|
||||
ctx.arg.library_paths.emplace_back(arg);
|
||||
} else if (read_arg("sysroot")) {
|
||||
ctx.arg.sysroot = arg;
|
||||
} else if (read_arg("unique")) {
|
||||
@@ -886,9 +896,16 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
} else {
|
||||
Fatal(ctx) << "invalid --hash-style argument: " << arg;
|
||||
}
|
||||
} else if (read_arg("soname") ||
|
||||
read_arg("h")) {
|
||||
} else if (read_arg("soname") || read_arg("h")) {
|
||||
ctx.arg.soname = arg;
|
||||
} else if (read_arg("audit")) {
|
||||
if (!ctx.arg.audit.empty())
|
||||
ctx.arg.audit += ':';
|
||||
ctx.arg.audit += std::string(arg);
|
||||
} else if (read_arg("depaudit") || read_arg("P")) {
|
||||
if (!ctx.arg.depaudit.empty())
|
||||
ctx.arg.depaudit += ':';
|
||||
ctx.arg.depaudit += std::string(arg);
|
||||
} else if (read_flag("allow-multiple-definition")) {
|
||||
ctx.arg.allow_multiple_definition = true;
|
||||
} else if (read_flag("apply-dynamic-relocs")) {
|
||||
@@ -930,10 +947,8 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
} else if (read_flag("pack-dyn-relocs=none") ||
|
||||
read_z_flag("nopack-relative-relocs")) {
|
||||
ctx.arg.pack_dyn_relocs_relr = false;
|
||||
} else if (read_arg("encoded-package-metadata")) {
|
||||
ctx.arg.package_metadata = parse_encoded_package_metadata(ctx, arg);
|
||||
} else if (read_arg("package-metadata")) {
|
||||
ctx.arg.package_metadata = arg;
|
||||
ctx.arg.package_metadata = parse_package_metadata(ctx, arg);
|
||||
} else if (read_flag("stats")) {
|
||||
ctx.arg.stats = true;
|
||||
Counter::enabled = true;
|
||||
@@ -1088,9 +1103,9 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
} else if (read_z_arg("stack-size")) {
|
||||
ctx.arg.z_stack_size = parse_number(ctx, "-z stack-size", arg);
|
||||
} else if (read_z_flag("dynamic-undefined-weak")) {
|
||||
ctx.arg.z_dynamic_undefined_weak = true;
|
||||
z_dynamic_undefined_weak = true;
|
||||
} else if (read_z_flag("nodynamic-undefined-weak")) {
|
||||
ctx.arg.z_dynamic_undefined_weak = false;
|
||||
z_dynamic_undefined_weak = false;
|
||||
} else if (read_z_flag("sectionheader")) {
|
||||
ctx.arg.z_sectionheader = true;
|
||||
} else if (read_z_flag("nosectionheader")) {
|
||||
@@ -1129,6 +1144,10 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
ctx.arg.print_gc_sections = true;
|
||||
} else if (read_flag("no-print-gc-sections")) {
|
||||
ctx.arg.print_gc_sections = false;
|
||||
} else if (read_arg("discard-section")) {
|
||||
ctx.arg.discard_section.insert(arg);
|
||||
} else if (read_arg("no-discard-section")) {
|
||||
ctx.arg.discard_section.erase(arg);
|
||||
} else if (read_arg("icf")) {
|
||||
if (arg == "all") {
|
||||
ctx.arg.icf = true;
|
||||
@@ -1159,25 +1178,23 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
} else if (read_arg("plugin")) {
|
||||
ctx.arg.plugin = arg;
|
||||
} else if (read_arg("plugin-opt")) {
|
||||
ctx.arg.plugin_opt.push_back(std::string(arg));
|
||||
ctx.arg.plugin_opt.emplace_back(arg);
|
||||
} else if (read_flag("lto-cs-profile-generate")) {
|
||||
ctx.arg.plugin_opt.push_back("cs-profile-generate");
|
||||
ctx.arg.plugin_opt.emplace_back("cs-profile-generate");
|
||||
} else if (read_arg("lto-cs-profile-file")) {
|
||||
ctx.arg.plugin_opt.push_back("cs-profile-path=" + std::string(arg));
|
||||
} else if (read_flag("lto-debug-pass-manager")) {
|
||||
ctx.arg.plugin_opt.push_back("debug-pass-manager");
|
||||
ctx.arg.plugin_opt.emplace_back("debug-pass-manager");
|
||||
} else if (read_flag("disable-verify")) {
|
||||
ctx.arg.plugin_opt.push_back("disable-verify");
|
||||
ctx.arg.plugin_opt.emplace_back("disable-verify");
|
||||
} else if (read_flag("lto-emit-asm")) {
|
||||
ctx.arg.plugin_opt.push_back("emit-asm");
|
||||
} else if (read_arg("thinlto-jobs")) {
|
||||
ctx.arg.plugin_opt.push_back("jobs=" + std::string(arg));
|
||||
ctx.arg.plugin_opt.emplace_back("emit-asm");
|
||||
} else if (read_flag("no-legacy-pass-manager")) {
|
||||
ctx.arg.plugin_opt.push_back("legacy-pass-manager");
|
||||
ctx.arg.plugin_opt.emplace_back("legacy-pass-manager");
|
||||
} else if (read_arg("lto-partitions")) {
|
||||
ctx.arg.plugin_opt.push_back("lto-partitions=" + std::string(arg));
|
||||
} else if (read_flag("no-lto-legacy-pass-manager")) {
|
||||
ctx.arg.plugin_opt.push_back("new-pass-manager");
|
||||
ctx.arg.plugin_opt.emplace_back("new-pass-manager");
|
||||
} else if (read_arg("lto-obj-path")) {
|
||||
ctx.arg.plugin_opt.push_back("obj-path=" + std::string(arg));
|
||||
} else if (read_arg("opt-remarks-filename")) {
|
||||
@@ -1190,7 +1207,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
} else if (read_arg("opt-remarks-passes")) {
|
||||
ctx.arg.plugin_opt.push_back("opt-remarks-passes=" + std::string(arg));
|
||||
} else if (read_flag("opt-remarks-with_hotness")) {
|
||||
ctx.arg.plugin_opt.push_back("opt-remarks-with-hotness");
|
||||
ctx.arg.plugin_opt.emplace_back("opt-remarks-with-hotness");
|
||||
} else if (args[0].starts_with("-lto-O")) {
|
||||
ctx.arg.plugin_opt.push_back("O" + std::string(args[0].substr(6)));
|
||||
args = args.subspan(1);
|
||||
@@ -1203,13 +1220,13 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
} else if (read_arg("lto-sample-profile")) {
|
||||
ctx.arg.plugin_opt.push_back("sample-profile=" + std::string(arg));
|
||||
} else if (read_flag("save-temps")) {
|
||||
ctx.arg.plugin_opt.push_back("save-temps");
|
||||
ctx.arg.plugin_opt.emplace_back("save-temps");
|
||||
} else if (read_flag("thinlto-emit-imports-files")) {
|
||||
ctx.arg.plugin_opt.push_back("thinlto-emit-imports-files");
|
||||
ctx.arg.plugin_opt.emplace_back("thinlto-emit-imports-files");
|
||||
} else if (read_arg("thinlto-index-only")) {
|
||||
ctx.arg.plugin_opt.push_back("thinlto-index-only=" + std::string(arg));
|
||||
} else if (read_flag("thinlto-index-only")) {
|
||||
ctx.arg.plugin_opt.push_back("thinlto-index-only");
|
||||
ctx.arg.plugin_opt.emplace_back("thinlto-index-only");
|
||||
} else if (read_arg("thinlto-object-suffix-replace")) {
|
||||
ctx.arg.plugin_opt.push_back("thinlto-object-suffix-replace=" +
|
||||
std::string(arg));
|
||||
@@ -1332,6 +1349,8 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
} else if (read_z_arg("common-page-size")) {
|
||||
} else if (read_flag("no-keep-memory")) {
|
||||
} else if (read_arg("max-cache-size")) {
|
||||
} else if (read_flag("mmap-output-file")) {
|
||||
} else if (read_flag("no-mmap-output-file")) {
|
||||
} else if (read_arg("version-script")) {
|
||||
version_scripts.push_back(arg);
|
||||
} else if (read_arg("dynamic-list")) {
|
||||
@@ -1344,21 +1363,21 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
} else if (read_arg("export-dynamic-symbol-list")) {
|
||||
append(ctx.dynamic_list_patterns, parse_dynamic_list(ctx, arg));
|
||||
} else if (read_flag("as-needed")) {
|
||||
remaining.push_back("--as-needed");
|
||||
remaining.emplace_back("--as-needed");
|
||||
} else if (read_flag("no-as-needed")) {
|
||||
remaining.push_back("--no-as-needed");
|
||||
remaining.emplace_back("--no-as-needed");
|
||||
} else if (read_flag("whole-archive")) {
|
||||
remaining.push_back("--whole-archive");
|
||||
remaining.emplace_back("--whole-archive");
|
||||
} else if (read_flag("no-whole-archive")) {
|
||||
remaining.push_back("--no-whole-archive");
|
||||
remaining.emplace_back("--no-whole-archive");
|
||||
} else if (read_arg("l") || read_arg("library")) {
|
||||
remaining.push_back("-l" + std::string(arg));
|
||||
} else if (read_arg("script") || read_arg("T")) {
|
||||
remaining.push_back(std::string(arg));
|
||||
remaining.emplace_back(arg);
|
||||
} else if (read_flag("push-state")) {
|
||||
remaining.push_back("--push-state");
|
||||
remaining.emplace_back("--push-state");
|
||||
} else if (read_flag("pop-state")) {
|
||||
remaining.push_back("--pop-state");
|
||||
remaining.emplace_back("--pop-state");
|
||||
} else if (args[0].starts_with("-z") && args[0].size() > 2) {
|
||||
Warn(ctx) << "unknown command line option: " << args[0];
|
||||
args = args.subspan(1);
|
||||
@@ -1371,7 +1390,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
} else {
|
||||
if (args[0].starts_with('-'))
|
||||
Fatal(ctx) << "unknown command line option: " << args[0];
|
||||
remaining.push_back(std::string(args[0]));
|
||||
remaining.emplace_back(args[0]);
|
||||
args = args.subspan(1);
|
||||
}
|
||||
}
|
||||
@@ -1430,6 +1449,12 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
else if (!ctx.arg.section_order.empty())
|
||||
ctx.arg.z_separate_code = SEPARATE_LOADABLE_SEGMENTS;
|
||||
|
||||
// `-z dynamic-undefined-weak` is enabled by default for DSOs.
|
||||
if (z_dynamic_undefined_weak)
|
||||
ctx.arg.z_dynamic_undefined_weak = *z_dynamic_undefined_weak;
|
||||
else
|
||||
ctx.arg.z_dynamic_undefined_weak = ctx.arg.shared;
|
||||
|
||||
// --section-order implies `-z norelro`
|
||||
if (z_relro)
|
||||
ctx.arg.z_relro = *z_relro;
|
||||
|
||||
+79
@@ -53,6 +53,15 @@ std::string rel_to_string<X86_64>(u32 r_type) {
|
||||
CASE(R_X86_64_IRELATIVE);
|
||||
CASE(R_X86_64_GOTPCRELX);
|
||||
CASE(R_X86_64_REX_GOTPCRELX);
|
||||
CASE(R_X86_64_CODE_4_GOTPCRELX);
|
||||
CASE(R_X86_64_CODE_4_GOTTPOFF);
|
||||
CASE(R_X86_64_CODE_4_GOTPC32_TLSDESC);
|
||||
CASE(R_X86_64_CODE_5_GOTPCRELX);
|
||||
CASE(R_X86_64_CODE_5_GOTTPOFF);
|
||||
CASE(R_X86_64_CODE_5_GOTPC32_TLSDESC);
|
||||
CASE(R_X86_64_CODE_6_GOTPCRELX);
|
||||
CASE(R_X86_64_CODE_6_GOTTPOFF);
|
||||
CASE(R_X86_64_CODE_6_GOTPC32_TLSDESC);
|
||||
}
|
||||
return unknown_type(r_type);
|
||||
}
|
||||
@@ -619,6 +628,7 @@ std::string rel_to_string<PPC64V1>(u32 r_type) {
|
||||
CASE(R_PPC64_DTPREL16_HIGH);
|
||||
CASE(R_PPC64_DTPREL16_HIGHA);
|
||||
CASE(R_PPC64_REL24_NOTOC);
|
||||
CASE(R_PPC64_ENTRY);
|
||||
CASE(R_PPC64_PLTSEQ);
|
||||
CASE(R_PPC64_PLTCALL);
|
||||
CASE(R_PPC64_PLTSEQ_NOTOC);
|
||||
@@ -1028,4 +1038,73 @@ std::string rel_to_string<LOONGARCH32>(u32 r_type) {
|
||||
return rel_to_string<LOONGARCH64>(r_type);
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string rel_to_string<ARC>(u32 r_type) {
|
||||
switch (r_type) {
|
||||
CASE(R_ARC_8);
|
||||
CASE(R_ARC_16);
|
||||
CASE(R_ARC_24);
|
||||
CASE(R_ARC_32);
|
||||
CASE(R_ARC_N8);
|
||||
CASE(R_ARC_N16);
|
||||
CASE(R_ARC_N24);
|
||||
CASE(R_ARC_N32);
|
||||
CASE(R_ARC_SDA);
|
||||
CASE(R_ARC_SECTOFF);
|
||||
CASE(R_ARC_S21H_PCREL);
|
||||
CASE(R_ARC_S21W_PCREL);
|
||||
CASE(R_ARC_S25H_PCREL);
|
||||
CASE(R_ARC_S25W_PCREL);
|
||||
CASE(R_ARC_SDA32);
|
||||
CASE(R_ARC_SDA_LDST);
|
||||
CASE(R_ARC_SDA_LDST1);
|
||||
CASE(R_ARC_SDA_LDST2);
|
||||
CASE(R_ARC_SDA16_LD);
|
||||
CASE(R_ARC_SDA16_LD1);
|
||||
CASE(R_ARC_SDA16_LD2);
|
||||
CASE(R_ARC_S13_PCREL);
|
||||
CASE(R_ARC_W);
|
||||
CASE(R_ARC_32_ME);
|
||||
CASE(R_ARC_32_ME_S);
|
||||
CASE(R_ARC_N32_ME);
|
||||
CASE(R_ARC_SECTOFF_ME);
|
||||
CASE(R_ARC_SDA32_ME);
|
||||
CASE(R_ARC_W_ME);
|
||||
CASE(R_ARC_SECTOFF_ME_1);
|
||||
CASE(R_ARC_SECTOFF_ME_2);
|
||||
CASE(R_ARC_SECTOFF_1);
|
||||
CASE(R_ARC_SECTOFF_2);
|
||||
CASE(R_ARC_SDA_12);
|
||||
CASE(R_ARC_SDA16_ST2);
|
||||
CASE(R_ARC_32_PCREL);
|
||||
CASE(R_ARC_PC32);
|
||||
CASE(R_ARC_GOT32);
|
||||
CASE(R_ARC_GOTPC32);
|
||||
CASE(R_ARC_PLT32);
|
||||
CASE(R_ARC_COPY);
|
||||
CASE(R_ARC_GLOB_DAT);
|
||||
CASE(R_ARC_JMP_SLOT);
|
||||
CASE(R_ARC_RELATIVE);
|
||||
CASE(R_ARC_GOTOFF);
|
||||
CASE(R_ARC_GOTPC);
|
||||
CASE(R_ARC_S21W_PCREL_PLT);
|
||||
CASE(R_ARC_S25H_PCREL_PLT);
|
||||
CASE(R_ARC_JLI_SECTOFF);
|
||||
CASE(R_ARC_TLS_DTPMOD);
|
||||
CASE(R_ARC_TLS_TPOFF);
|
||||
CASE(R_ARC_TLS_GD_GOT);
|
||||
CASE(R_ARC_TLS_GD_LD);
|
||||
CASE(R_ARC_TLS_GD_CALL);
|
||||
CASE(R_ARC_TLS_IE_GOT);
|
||||
CASE(R_ARC_TLS_DTPOFF);
|
||||
CASE(R_ARC_TLS_DTPOFF_S9);
|
||||
CASE(R_ARC_TLS_LE_S9);
|
||||
CASE(R_ARC_TLS_LE_32);
|
||||
CASE(R_ARC_S25W_PCREL_PLT);
|
||||
CASE(R_ARC_S21H_PCREL_PLT);
|
||||
CASE(R_ARC_NPS_CMEM16);
|
||||
}
|
||||
return unknown_type(r_type);
|
||||
}
|
||||
|
||||
} // namespace mold
|
||||
@@ -28,6 +28,7 @@ struct SH4LE;
|
||||
struct SH4BE;
|
||||
struct LOONGARCH64;
|
||||
struct LOONGARCH32;
|
||||
struct ARC;
|
||||
|
||||
template <typename E> struct ElfSym;
|
||||
template <typename E> struct ElfShdr;
|
||||
@@ -78,6 +79,7 @@ enum : u32 {
|
||||
SHT_GROUP = 17,
|
||||
SHT_SYMTAB_SHNDX = 18,
|
||||
SHT_RELR = 19,
|
||||
SHT_CREL = 0x40000014,
|
||||
SHT_LOOS = 0x60000000,
|
||||
SHT_LLVM_ADDRSIG = 0x6fff4c03,
|
||||
SHT_GNU_HASH = 0x6ffffff6,
|
||||
@@ -87,6 +89,7 @@ enum : u32 {
|
||||
SHT_HIOS = 0x6fffffff,
|
||||
SHT_X86_64_UNWIND = 0x70000001,
|
||||
SHT_ARM_EXIDX = 0x70000001,
|
||||
SHT_ARC_ATTRIBUTES = 0x70000001,
|
||||
SHT_ARM_ATTRIBUTES = 0x70000003,
|
||||
SHT_RISCV_ATTRIBUTES = 0x70000003,
|
||||
SHT_LOUSER = 0x80000000,
|
||||
@@ -234,6 +237,7 @@ enum : u32 {
|
||||
EM_SPARC64 = 43,
|
||||
EM_X86_64 = 62,
|
||||
EM_AARCH64 = 183,
|
||||
EM_ARC_COMPACT2 = 195,
|
||||
EM_RISCV = 243,
|
||||
EM_LOONGARCH = 258,
|
||||
};
|
||||
@@ -284,6 +288,8 @@ enum : u32 {
|
||||
DT_RELR = 36,
|
||||
DT_RELRENT = 37,
|
||||
DT_GNU_HASH = 0x6ffffef5,
|
||||
DT_DEPAUDIT = 0x6ffffefb,
|
||||
DT_AUDIT = 0x6ffffefc,
|
||||
DT_VERSYM = 0x6ffffff0,
|
||||
DT_RELACOUNT = 0x6ffffff9,
|
||||
DT_RELCOUNT = 0x6ffffffa,
|
||||
@@ -452,6 +458,15 @@ enum : u32 {
|
||||
R_X86_64_IRELATIVE = 37,
|
||||
R_X86_64_GOTPCRELX = 41,
|
||||
R_X86_64_REX_GOTPCRELX = 42,
|
||||
R_X86_64_CODE_4_GOTPCRELX = 43,
|
||||
R_X86_64_CODE_4_GOTTPOFF = 44,
|
||||
R_X86_64_CODE_4_GOTPC32_TLSDESC = 45,
|
||||
R_X86_64_CODE_5_GOTPCRELX = 46,
|
||||
R_X86_64_CODE_5_GOTTPOFF = 47,
|
||||
R_X86_64_CODE_5_GOTPC32_TLSDESC = 48,
|
||||
R_X86_64_CODE_6_GOTPCRELX = 49,
|
||||
R_X86_64_CODE_6_GOTTPOFF = 50,
|
||||
R_X86_64_CODE_6_GOTPC32_TLSDESC = 51,
|
||||
};
|
||||
|
||||
enum : u32 {
|
||||
@@ -975,6 +990,7 @@ enum : u32 {
|
||||
R_PPC64_DTPREL16_HIGH = 114,
|
||||
R_PPC64_DTPREL16_HIGHA = 115,
|
||||
R_PPC64_REL24_NOTOC = 116,
|
||||
R_PPC64_ENTRY = 118,
|
||||
R_PPC64_PLTSEQ = 119,
|
||||
R_PPC64_PLTCALL = 120,
|
||||
R_PPC64_PLTSEQ_NOTOC = 121,
|
||||
@@ -1349,6 +1365,71 @@ enum : u32 {
|
||||
R_LARCH_TLS_DESC_PCREL20_S2 = 126,
|
||||
};
|
||||
|
||||
enum : u32 {
|
||||
R_ARC_8 = 1,
|
||||
R_ARC_16 = 2,
|
||||
R_ARC_24 = 3,
|
||||
R_ARC_32 = 4,
|
||||
R_ARC_N8 = 8,
|
||||
R_ARC_N16 = 9,
|
||||
R_ARC_N24 = 10,
|
||||
R_ARC_N32 = 11,
|
||||
R_ARC_SDA = 12,
|
||||
R_ARC_SECTOFF = 13,
|
||||
R_ARC_S21H_PCREL = 14,
|
||||
R_ARC_S21W_PCREL = 15,
|
||||
R_ARC_S25H_PCREL = 16,
|
||||
R_ARC_S25W_PCREL = 17,
|
||||
R_ARC_SDA32 = 18,
|
||||
R_ARC_SDA_LDST = 19,
|
||||
R_ARC_SDA_LDST1 = 20,
|
||||
R_ARC_SDA_LDST2 = 21,
|
||||
R_ARC_SDA16_LD = 22,
|
||||
R_ARC_SDA16_LD1 = 23,
|
||||
R_ARC_SDA16_LD2 = 24,
|
||||
R_ARC_S13_PCREL = 25,
|
||||
R_ARC_W = 26,
|
||||
R_ARC_32_ME = 27,
|
||||
R_ARC_32_ME_S = 105,
|
||||
R_ARC_N32_ME = 28,
|
||||
R_ARC_SECTOFF_ME = 29,
|
||||
R_ARC_SDA32_ME = 30,
|
||||
R_ARC_W_ME = 31,
|
||||
R_ARC_SECTOFF_ME_1 = 41,
|
||||
R_ARC_SECTOFF_ME_2 = 42,
|
||||
R_ARC_SECTOFF_1 = 43,
|
||||
R_ARC_SECTOFF_2 = 44,
|
||||
R_ARC_SDA_12 = 45,
|
||||
R_ARC_SDA16_ST2 = 48,
|
||||
R_ARC_32_PCREL = 49,
|
||||
R_ARC_PC32 = 50,
|
||||
R_ARC_GOT32 = 59,
|
||||
R_ARC_GOTPC32 = 51,
|
||||
R_ARC_PLT32 = 52,
|
||||
R_ARC_COPY = 53,
|
||||
R_ARC_GLOB_DAT = 54,
|
||||
R_ARC_JMP_SLOT = 55,
|
||||
R_ARC_RELATIVE = 56,
|
||||
R_ARC_GOTOFF = 57,
|
||||
R_ARC_GOTPC = 58,
|
||||
R_ARC_S21W_PCREL_PLT = 60,
|
||||
R_ARC_S25H_PCREL_PLT = 61,
|
||||
R_ARC_JLI_SECTOFF = 63,
|
||||
R_ARC_TLS_DTPMOD = 66,
|
||||
R_ARC_TLS_TPOFF = 68,
|
||||
R_ARC_TLS_GD_GOT = 69,
|
||||
R_ARC_TLS_GD_LD = 70,
|
||||
R_ARC_TLS_GD_CALL = 71,
|
||||
R_ARC_TLS_IE_GOT = 72,
|
||||
R_ARC_TLS_DTPOFF = 67,
|
||||
R_ARC_TLS_DTPOFF_S9 = 73,
|
||||
R_ARC_TLS_LE_S9 = 74,
|
||||
R_ARC_TLS_LE_32 = 75,
|
||||
R_ARC_S25W_PCREL_PLT = 76,
|
||||
R_ARC_S21H_PCREL_PLT = 77,
|
||||
R_ARC_NPS_CMEM16 = 78,
|
||||
};
|
||||
|
||||
// Returns true if a given relocation is of type used for direct
|
||||
// function call.
|
||||
template <typename E>
|
||||
@@ -1812,6 +1893,7 @@ template <typename E> concept is_sh4le = std::same_as<E, SH4LE>;
|
||||
template <typename E> concept is_sh4be = std::same_as<E, SH4BE>;
|
||||
template <typename E> concept is_loongarch64 = std::same_as<E, LOONGARCH64>;
|
||||
template <typename E> concept is_loongarch32 = std::same_as<E, LOONGARCH32>;
|
||||
template <typename E> concept is_arc = std::same_as<E, ARC>;
|
||||
|
||||
template <typename E> concept is_x86 = is_x86_64<E> || is_i386<E>;
|
||||
template <typename E> concept is_arm64 = is_arm64le<E> || is_arm64be<E>;
|
||||
@@ -1859,7 +1941,7 @@ struct I386 {
|
||||
static constexpr u32 e_machine = EM_386;
|
||||
static constexpr u32 plt_hdr_size = 16;
|
||||
static constexpr u32 plt_size = 16;
|
||||
static constexpr u32 pltgot_size = 16;
|
||||
static constexpr u32 pltgot_size = 8;
|
||||
static constexpr u8 filler[] = { 0xcc }; // int3
|
||||
|
||||
static constexpr u32 R_COPY = R_386_COPY;
|
||||
@@ -1912,7 +1994,7 @@ struct ARM32 {
|
||||
static constexpr bool is_64 = false;
|
||||
static constexpr bool is_le = true;
|
||||
static constexpr bool is_rela = false;
|
||||
static constexpr u32 page_size = 4096;
|
||||
static constexpr u32 page_size = 65536;
|
||||
static constexpr u32 e_machine = EM_ARM;
|
||||
static constexpr u32 plt_hdr_size = 32;
|
||||
static constexpr u32 plt_size = 16;
|
||||
@@ -2170,7 +2252,7 @@ struct LOONGARCH64 {
|
||||
static constexpr bool is_64 = true;
|
||||
static constexpr bool is_le = true;
|
||||
static constexpr bool is_rela = true;
|
||||
static constexpr u32 page_size = 16384;
|
||||
static constexpr u32 page_size = 65536;
|
||||
static constexpr u32 e_machine = EM_LOONGARCH;
|
||||
static constexpr u32 plt_hdr_size = 32;
|
||||
static constexpr u32 plt_size = 16;
|
||||
@@ -2195,7 +2277,7 @@ struct LOONGARCH32 {
|
||||
static constexpr bool is_64 = false;
|
||||
static constexpr bool is_le = true;
|
||||
static constexpr bool is_rela = true;
|
||||
static constexpr u32 page_size = 16384;
|
||||
static constexpr u32 page_size = 65536;
|
||||
static constexpr u32 e_machine = EM_LOONGARCH;
|
||||
static constexpr u32 plt_hdr_size = 32;
|
||||
static constexpr u32 plt_size = 16;
|
||||
@@ -2215,4 +2297,27 @@ struct LOONGARCH32 {
|
||||
static constexpr u32 R_FUNCALL[] = { R_LARCH_B26, R_LARCH_CALL36 };
|
||||
};
|
||||
|
||||
struct ARC {
|
||||
static constexpr std::string_view name = "arc";
|
||||
static constexpr bool is_64 = false;
|
||||
static constexpr bool is_le = true;
|
||||
static constexpr bool is_rela = true;
|
||||
static constexpr u32 page_size = 8192;
|
||||
static constexpr u32 e_machine = EM_ARC_COMPACT2;
|
||||
static constexpr u32 plt_hdr_size = 24;
|
||||
static constexpr u32 plt_size = 16;
|
||||
static constexpr u32 pltgot_size = 16;
|
||||
static constexpr u8 filler[] = { 0, 0, 0, 0 };
|
||||
|
||||
static constexpr u32 R_COPY = R_ARC_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_ARC_GLOB_DAT;
|
||||
static constexpr u32 R_JUMP_SLOT = R_ARC_JMP_SLOT;
|
||||
static constexpr u32 R_ABS = R_ARC_32;
|
||||
static constexpr u32 R_RELATIVE = R_ARC_RELATIVE;
|
||||
static constexpr u32 R_DTPOFF = R_ARC_TLS_DTPOFF;
|
||||
static constexpr u32 R_TPOFF = R_ARC_TLS_TPOFF;
|
||||
static constexpr u32 R_DTPMOD = R_ARC_TLS_DTPMOD;
|
||||
static constexpr u32 R_FUNCALL[] = { R_ARC_S25W_PCREL_PLT };
|
||||
};
|
||||
|
||||
} // namespace mold
|
||||
@@ -160,6 +160,8 @@ static std::string_view get_elf_type(u8 *buf) {
|
||||
return is_le ? SH4LE::name : SH4BE::name;
|
||||
case EM_LOONGARCH:
|
||||
return is_64 ? LOONGARCH64::name : LOONGARCH32::name;
|
||||
case EM_ARC_COMPACT2:
|
||||
return ARC::name;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
||||
+19
-18
@@ -24,7 +24,6 @@ static bool should_keep(const InputSection<E> &isec) {
|
||||
type == SHT_INIT_ARRAY ||
|
||||
type == SHT_FINI_ARRAY ||
|
||||
type == SHT_PREINIT_ARRAY ||
|
||||
(is_arm32<E> && type == SHT_ARM_EXIDX) ||
|
||||
name.starts_with(".ctors") ||
|
||||
name.starts_with(".dtors") ||
|
||||
name.starts_with(".init") ||
|
||||
@@ -100,34 +99,36 @@ static void visit(Context<E> &ctx, InputSection<E> *isec,
|
||||
tbb::feeder<InputSection<E> *> &feeder, i64 depth) {
|
||||
assert(isec->is_visited);
|
||||
|
||||
// Mark a section alive. For better performacne, we don't call
|
||||
// `feeder.add` too often.
|
||||
auto mark = [&](InputSection<E> *sec) {
|
||||
if (mark_section(sec)) {
|
||||
if (depth < 3)
|
||||
visit(ctx, sec, feeder, depth + 1);
|
||||
else
|
||||
feeder.add(sec);
|
||||
}
|
||||
};
|
||||
|
||||
// If this is a text section, .eh_frame may contain records
|
||||
// describing how to handle exceptions for that function.
|
||||
// We want to keep associated .eh_frame records.
|
||||
for (FdeRecord<E> &fde : isec->get_fdes())
|
||||
for (const ElfRel<E> &rel : fde.get_rels(isec->file).subspan(1))
|
||||
if (Symbol<E> *sym = isec->file.symbols[rel.r_sym])
|
||||
if (mark_section(sym->get_input_section()))
|
||||
feeder.add(sym->get_input_section());
|
||||
mark(sym->get_input_section());
|
||||
|
||||
for (const ElfRel<E> &rel : isec->get_rels(ctx)) {
|
||||
Symbol<E> &sym = *isec->file.symbols[rel.r_sym];
|
||||
|
||||
// Symbol can refer to either a section fragment or an input section.
|
||||
// Mark a fragment as alive.
|
||||
if (SectionFragment<E> *frag = sym.get_frag()) {
|
||||
Symbol<E> &sym = *isec->file.symbols[rel.r_sym];
|
||||
if (SectionFragment<E> *frag = sym.get_frag())
|
||||
frag->is_alive = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Mark a section alive. For better performacne, we don't call
|
||||
// `feeder.add` too often.
|
||||
if (mark_section(sym.get_input_section())) {
|
||||
if (depth < 3)
|
||||
visit(ctx, sym.get_input_section(), feeder, depth + 1);
|
||||
else
|
||||
feeder.add(sym.get_input_section());
|
||||
}
|
||||
else
|
||||
mark(sym.get_input_section());
|
||||
}
|
||||
|
||||
if constexpr (is_arm32<E>)
|
||||
mark(isec->extra.exidx);
|
||||
}
|
||||
|
||||
// Mark all reachable sections
|
||||
|
||||
+2
-3
@@ -58,7 +58,6 @@
|
||||
|
||||
#include "mold.h"
|
||||
#include <tbb/parallel_for_each.h>
|
||||
#include <tbb/parallel_sort.h>
|
||||
|
||||
namespace mold {
|
||||
|
||||
@@ -620,8 +619,8 @@ static std::vector<Compunit> read_compunits(Context<E> &ctx) {
|
||||
|
||||
// Uniquify elements because GCC 11 seems to emit one record for each
|
||||
// comdat group which results in having a lot of duplicate records.
|
||||
tbb::parallel_for_each(cus, [&](Compunit &cu) {
|
||||
sort(cu.nametypes);
|
||||
tbb::parallel_for_each(cus, [](Compunit &cu) {
|
||||
ranges::stable_sort(cu.nametypes);
|
||||
remove_duplicates(cu.nametypes);
|
||||
});
|
||||
|
||||
|
||||
+1
-1
@@ -468,7 +468,7 @@ static void print_icf_sections(Context<E> &ctx) {
|
||||
});
|
||||
|
||||
tbb::parallel_sort(leaders.begin(), leaders.end(),
|
||||
[&](InputSection<E> *a, InputSection<E> *b) {
|
||||
[](InputSection<E> *a, InputSection<E> *b) {
|
||||
return a->get_priority() < b->get_priority();
|
||||
});
|
||||
|
||||
|
||||
+130
-79
@@ -93,12 +93,6 @@ InputFile<E>::InputFile(Context<E> &ctx, MappedFile *mf)
|
||||
shstrtab = this->get_string(ctx, shstrtab_idx);
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
std::span<ElfPhdr<E>> InputFile<E>::get_phdrs() {
|
||||
ElfEhdr<E> &ehdr = get_ehdr();
|
||||
return {(ElfPhdr<E> *)(mf->data + ehdr.e_phoff), ehdr.e_phnum};
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
ElfShdr<E> *InputFile<E>::find_section(i64 type) {
|
||||
for (ElfShdr<E> &sec : elf_sections)
|
||||
@@ -116,13 +110,6 @@ std::string_view InputFile<E>::get_source_name() const {
|
||||
return "";
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
ObjectFile<E>::ObjectFile(Context<E> &ctx, MappedFile *mf,
|
||||
std::string archive_name, bool is_in_lib)
|
||||
: InputFile<E>(ctx, mf), archive_name(archive_name), is_in_lib(is_in_lib) {
|
||||
this->is_alive = !is_in_lib;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
static bool is_debug_section(const ElfShdr<E> &shdr, std::string_view name) {
|
||||
return !(shdr.sh_flags & SHF_ALLOC) && name.starts_with(".debug");
|
||||
@@ -168,7 +155,7 @@ ObjectFile<E>::parse_note_gnu_property(Context<E> &ctx, const ElfShdr<E> &shdr)
|
||||
|
||||
// <format-version>
|
||||
// [ <section-length> "vendor-name" <file-tag> <size> <attribute>*]+ ]*
|
||||
template <typename E>
|
||||
template <is_riscv E>
|
||||
static void read_riscv_attributes(Context<E> &ctx, ObjectFile<E> &file,
|
||||
std::string_view data) {
|
||||
if (data.empty())
|
||||
@@ -243,6 +230,65 @@ static bool is_known_section_type(const ElfShdr<E> &shdr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// SHT_CREL is an experimental alternative relocation table format
|
||||
// designed to reduce the size of the table. Only LLVM supports it
|
||||
// at the moment.
|
||||
//
|
||||
// This function converts a CREL relocation table to a regular one.
|
||||
template <typename E>
|
||||
std::vector<ElfRel<E>> decode_crel(Context<E> &ctx, ObjectFile<E> &file,
|
||||
const ElfShdr<E> &shdr) {
|
||||
u8 *p = (u8 *)file.get_string(ctx, shdr).data();
|
||||
u64 hdr = read_uleb(&p);
|
||||
i64 nrels = hdr >> 3;
|
||||
bool is_rela = hdr & 0b100;
|
||||
i64 scale = hdr & 0b11;
|
||||
|
||||
if (is_rela && !E::is_rela)
|
||||
Fatal(ctx) << file << ": CREL with addends is not supported for " << E::name;
|
||||
|
||||
u64 offset = 0;
|
||||
i64 type = 0;
|
||||
i64 symidx = 0;
|
||||
i64 addend = 0;
|
||||
|
||||
std::vector<ElfRel<E>> vec;
|
||||
vec.reserve(nrels);
|
||||
|
||||
while (vec.size() < nrels) {
|
||||
u8 flags = *p++;
|
||||
i64 nflags = is_rela ? 3 : 2;
|
||||
|
||||
// The first ULEB-128 encoded value is a concatenation of bit flags and
|
||||
// an offset delta. The delta may be very large to decrease the
|
||||
// current offset value by wrapping around. Combined, the encoded value
|
||||
// can be up to 67 bit long. Thus we can't simply use read_uleb() which
|
||||
// returns a u64.
|
||||
u64 delta;
|
||||
if (flags & 0x80)
|
||||
delta = (read_uleb(&p) << (7 - nflags)) | ((flags & 0x7f) >> nflags);
|
||||
else
|
||||
delta = flags >> nflags;
|
||||
offset += delta << scale;
|
||||
|
||||
if (flags & 1)
|
||||
symidx += read_sleb(&p);
|
||||
if (flags & 2)
|
||||
type += read_sleb(&p);
|
||||
if (is_rela && (flags & 4))
|
||||
addend += read_sleb(&p);
|
||||
vec.emplace_back(offset, type, symidx, addend);
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
ComdatGroup *insert_comdat_group(Context<E> &ctx, std::string_view name) {
|
||||
typename decltype(ctx.comdat_groups)::const_accessor acc;
|
||||
ctx.comdat_groups.insert(acc, {name, ComdatGroup()});
|
||||
return const_cast<ComdatGroup *>(&acc->second);
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
|
||||
// Read sections
|
||||
@@ -271,6 +317,10 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (is_arc<E>)
|
||||
if (shdr.sh_type == SHT_ARC_ATTRIBUTES)
|
||||
continue;
|
||||
|
||||
switch (shdr.sh_type) {
|
||||
case SHT_GROUP: {
|
||||
// Get the signature of this section group.
|
||||
@@ -301,12 +351,14 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
|
||||
if (entries[0] != GRP_COMDAT)
|
||||
Fatal(ctx) << *this << ": unsupported SHT_GROUP format";
|
||||
|
||||
typename decltype(ctx.comdat_groups)::const_accessor acc;
|
||||
ctx.comdat_groups.insert(acc, {signature, ComdatGroup()});
|
||||
ComdatGroup *group = const_cast<ComdatGroup *>(&acc->second);
|
||||
ComdatGroup *group = insert_comdat_group(ctx, signature);
|
||||
comdat_groups.push_back({group, (i32)i, entries.subspan(1)});
|
||||
break;
|
||||
}
|
||||
case SHT_CREL:
|
||||
decoded_crel.resize(i + 1);
|
||||
decoded_crel[i] = decode_crel(ctx, *this, shdr);
|
||||
break;
|
||||
case SHT_REL:
|
||||
case SHT_RELA:
|
||||
case SHT_SYMTAB:
|
||||
@@ -360,6 +412,11 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
|
||||
is_debug_section(shdr, name))
|
||||
continue;
|
||||
|
||||
// Ignore section is specified by --discard-section.
|
||||
if (!ctx.arg.discard_section.empty() &&
|
||||
ctx.arg.discard_section.contains(name))
|
||||
continue;
|
||||
|
||||
if (name == ".comment" &&
|
||||
this->get_string(ctx, shdr).starts_with("rustc "))
|
||||
this->is_rust_obj = true;
|
||||
@@ -439,18 +496,21 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
|
||||
// Attach relocation sections to their target sections.
|
||||
for (i64 i = 0; i < this->elf_sections.size(); i++) {
|
||||
const ElfShdr<E> &shdr = this->elf_sections[i];
|
||||
if (shdr.sh_type != (E::is_rela ? SHT_RELA : SHT_REL))
|
||||
continue;
|
||||
|
||||
if (shdr.sh_info >= sections.size())
|
||||
Fatal(ctx) << *this << ": invalid relocated section index: "
|
||||
<< (u32)shdr.sh_info;
|
||||
|
||||
if (std::unique_ptr<InputSection<E>> &target = sections[shdr.sh_info]) {
|
||||
assert(target->relsec_idx == -1);
|
||||
target->relsec_idx = i;
|
||||
if (shdr.sh_type == (E::is_rela ? SHT_RELA : SHT_REL) ||
|
||||
shdr.sh_type == SHT_CREL) {
|
||||
if (std::unique_ptr<InputSection<E>> &target = sections[shdr.sh_info]) {
|
||||
assert(target->relsec_idx == -1);
|
||||
target->relsec_idx = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Attach .arm.exidx sections to their corresponding sections
|
||||
if constexpr (is_arm32<E>)
|
||||
for (std::unique_ptr<InputSection<E>> &isec : this->sections)
|
||||
if (isec && isec->shdr().sh_type == SHT_ARM_EXIDX)
|
||||
if (InputSection<E> *target = sections[isec->shdr().sh_link].get())
|
||||
target->extra.exidx = isec.get();
|
||||
}
|
||||
|
||||
// .eh_frame contains data records explaining how to handle exceptions.
|
||||
@@ -539,6 +599,8 @@ void ObjectFile<E>::parse_ehframe(Context<E> &ctx) {
|
||||
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);
|
||||
}
|
||||
|
||||
isec->is_alive = false;
|
||||
}
|
||||
|
||||
auto get_isec = [&](const FdeRecord<E> &fde) {
|
||||
@@ -547,8 +609,8 @@ void ObjectFile<E>::parse_ehframe(Context<E> &ctx) {
|
||||
|
||||
// We assume that FDEs for the same input sections are contiguous
|
||||
// in `fdes` vector.
|
||||
sort(fdes, [&](const FdeRecord<E> &a, const FdeRecord<E> &b) {
|
||||
return get_isec(a)->get_priority() < get_isec(b)->get_priority();
|
||||
ranges::stable_sort(fdes, {}, [&](const FdeRecord<E> &x) {
|
||||
return get_isec(x)->get_priority();
|
||||
});
|
||||
|
||||
// Associate FDEs to input sections.
|
||||
@@ -628,7 +690,7 @@ void ObjectFile<E>::initialize_symbols(Context<E> &ctx) {
|
||||
if (ver != "@" && ver != "@@") {
|
||||
if (ver.starts_with("@@"))
|
||||
key = name;
|
||||
has_symver.set(i - this->first_global);
|
||||
has_symver[i - this->first_global] = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -656,18 +718,14 @@ void ObjectFile<E>::initialize_symbols(Context<E> &ctx) {
|
||||
template <typename E>
|
||||
void ObjectFile<E>::sort_relocations(Context<E> &ctx) {
|
||||
if constexpr (is_riscv<E> || is_loongarch<E>) {
|
||||
auto less = [&](const ElfRel<E> &a, const ElfRel<E> &b) {
|
||||
return a.r_offset < b.r_offset;
|
||||
};
|
||||
|
||||
for (i64 i = 1; i < sections.size(); i++) {
|
||||
std::unique_ptr<InputSection<E>> &isec = sections[i];
|
||||
if (!isec || !isec->is_alive || !(isec->shdr().sh_flags & SHF_ALLOC))
|
||||
continue;
|
||||
|
||||
std::span<ElfRel<E>> rels = isec->get_rels(ctx);
|
||||
if (!std::is_sorted(rels.begin(), rels.end(), less))
|
||||
sort(rels, less);
|
||||
if (!ranges::is_sorted(rels, {}, &ElfRel<E>::r_offset))
|
||||
ranges::stable_sort(rels, {}, &ElfRel<E>::r_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -844,16 +902,6 @@ void ObjectFile<E>::parse(Context<E> &ctx) {
|
||||
initialize_sections(ctx);
|
||||
initialize_symbols(ctx);
|
||||
sort_relocations(ctx);
|
||||
|
||||
// R_ARM_TARGET1 is typically used for entries in .init_array and may
|
||||
// be interpreted as REL32 or ABS32 depending on the target.
|
||||
// All targets we support handle it as if it were a ABS32.
|
||||
if constexpr (is_arm32<E>)
|
||||
for (std::unique_ptr<InputSection<E>> &isec : sections)
|
||||
if (isec && isec->is_alive)
|
||||
for (ElfRel<E> &r : isec->get_rels(ctx))
|
||||
if (r.r_type == R_ARM_TARGET1)
|
||||
r.r_type = R_ARM_ABS32;
|
||||
}
|
||||
|
||||
// Symbols with higher priorities overwrites symbols with lower priorities.
|
||||
@@ -868,6 +916,10 @@ void ObjectFile<E>::parse(Context<E> &ctx) {
|
||||
// 7. Unclaimed (nonexistent) symbol
|
||||
//
|
||||
// Ties are broken by file priority.
|
||||
//
|
||||
// Note that the above priorities are based on heuristics and not on exact
|
||||
// science. We tried several different orders and settled on the current
|
||||
// one just because it avoids link errors in all programs we've tested.
|
||||
template <typename E>
|
||||
static u64 get_rank(InputFile<E> *file, const ElfSym<E> &esym, bool is_in_archive) {
|
||||
auto get_sym_rank = [&] {
|
||||
@@ -891,7 +943,7 @@ template <typename E>
|
||||
static u64 get_rank(const Symbol<E> &sym) {
|
||||
if (!sym.file)
|
||||
return 7 << 24;
|
||||
return get_rank(sym.file, sym.esym(), !sym.file->is_alive);
|
||||
return get_rank(sym.file, sym.esym(), !sym.file->is_reachable);
|
||||
}
|
||||
|
||||
// Symbol's visibility is set to the most restrictive one. For example,
|
||||
@@ -951,7 +1003,7 @@ void ObjectFile<E>::resolve_symbols(Context<E> &ctx) {
|
||||
}
|
||||
|
||||
std::scoped_lock lock(sym.mu);
|
||||
if (get_rank(this, esym, !this->is_alive) < get_rank(sym)) {
|
||||
if (get_rank(this, esym, !this->is_reachable) < get_rank(sym)) {
|
||||
sym.file = this;
|
||||
sym.set_input_section(isec);
|
||||
sym.value = esym.st_value;
|
||||
@@ -966,7 +1018,7 @@ template <typename E>
|
||||
void
|
||||
ObjectFile<E>::mark_live_objects(Context<E> &ctx,
|
||||
std::function<void(InputFile<E> *)> feeder) {
|
||||
assert(this->is_alive);
|
||||
assert(this->is_reachable);
|
||||
|
||||
for (i64 i = this->first_global; i < this->elf_syms.size(); i++) {
|
||||
const ElfSym<E> &esym = this->elf_syms[i];
|
||||
@@ -984,7 +1036,7 @@ ObjectFile<E>::mark_live_objects(Context<E> &ctx,
|
||||
bool undef_ref = esym.is_undef() && (!esym.is_weak() || sym.file->is_dso);
|
||||
bool common_ref = esym.is_common() && !sym.esym().is_common();
|
||||
|
||||
if ((undef_ref || common_ref) && !sym.file->is_alive.test_and_set()) {
|
||||
if ((undef_ref || common_ref) && !sym.file->is_reachable.test_and_set()) {
|
||||
feeder(sym.file);
|
||||
if (sym.is_traced)
|
||||
Out(ctx) << "trace-symbol: " << *this << " keeps " << *sym.file
|
||||
@@ -1090,7 +1142,7 @@ static bool should_write_to_local_symtab(Context<E> &ctx, Symbol<E> &sym) {
|
||||
// merged, so their origins shouldn't matter, but I don't really
|
||||
// know the rationale. Anyway, this is the behavior of the
|
||||
// traditional linkers.
|
||||
if (sym.name().starts_with(".L")) {
|
||||
if (sym.name().starts_with(".L") || sym.name() == "L0\001") {
|
||||
if (ctx.arg.discard_locals)
|
||||
return false;
|
||||
|
||||
@@ -1106,7 +1158,7 @@ template <typename E>
|
||||
void ObjectFile<E>::compute_symtab_size(Context<E> &ctx) {
|
||||
this->output_sym_indices.resize(this->elf_syms.size(), -1);
|
||||
|
||||
auto is_alive = [&](Symbol<E> &sym) -> bool {
|
||||
auto is_alive = [](Symbol<E> &sym) -> bool {
|
||||
if (SectionFragment<E> *frag = sym.get_frag())
|
||||
return frag->is_alive;
|
||||
if (InputSection<E> *isec = sym.get_input_section())
|
||||
@@ -1195,7 +1247,7 @@ std::string SharedFile<E>::get_soname(Context<E> &ctx) {
|
||||
if (ElfShdr<E> *sec = this->find_section(SHT_DYNAMIC))
|
||||
for (ElfDyn<E> &dyn : this->template get_data<ElfDyn<E>>(ctx, *sec))
|
||||
if (dyn.d_tag == DT_SONAME)
|
||||
return this->symbol_strtab.data() + dyn.d_val;
|
||||
return this->get_string(ctx, sec->sh_link).data() + dyn.d_val;
|
||||
|
||||
if (this->mf->given_fullpath)
|
||||
return this->filename;
|
||||
@@ -1254,30 +1306,23 @@ void SharedFile<E>::parse(Context<E> &ctx) {
|
||||
|
||||
template <typename E>
|
||||
std::vector<std::string_view> SharedFile<E>::get_dt_needed(Context<E> &ctx) {
|
||||
// Get the contents of the dynamic segment
|
||||
std::span<ElfDyn<E>> dynamic;
|
||||
for (ElfPhdr<E> &phdr : this->get_phdrs())
|
||||
if (phdr.p_type == PT_DYNAMIC)
|
||||
dynamic = {(ElfDyn<E> *)(this->mf->data + phdr.p_offset),
|
||||
(size_t)phdr.p_memsz / sizeof(ElfDyn<E>)};
|
||||
|
||||
// Find a string table
|
||||
char *strtab = nullptr;
|
||||
for (ElfDyn<E> &dyn : dynamic)
|
||||
if (dyn.d_tag == DT_STRTAB)
|
||||
strtab = (char *)this->mf->data + dyn.d_val;
|
||||
|
||||
if (!strtab)
|
||||
return {};
|
||||
|
||||
// Find all DT_NEEDED entries
|
||||
std::vector<std::string_view> vec;
|
||||
for (ElfDyn<E> &dyn : dynamic)
|
||||
if (dyn.d_tag == DT_NEEDED)
|
||||
vec.push_back(strtab + dyn.d_val);
|
||||
if (ElfShdr<E> *sec = this->find_section(SHT_DYNAMIC))
|
||||
for (ElfDyn<E> &dyn : this->template get_data<ElfDyn<E>>(ctx, *sec))
|
||||
if (dyn.d_tag == DT_NEEDED)
|
||||
vec.push_back(this->get_string(ctx, sec->sh_link).data() + dyn.d_val);
|
||||
return vec;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
std::string_view SharedFile<E>::get_dt_audit(Context<E> &ctx) {
|
||||
if (ElfShdr<E> *sec = this->find_section(SHT_DYNAMIC))
|
||||
for (ElfDyn<E> &dyn : this->template get_data<ElfDyn<E>>(ctx, *sec))
|
||||
if (dyn.d_tag == DT_AUDIT)
|
||||
return this->get_string(ctx, sec->sh_link).data() + dyn.d_val;
|
||||
return "";
|
||||
}
|
||||
|
||||
// Symbol versioning is a GNU extension to the ELF file format. I don't
|
||||
// particularly like the feature as it complicates the semantics of
|
||||
// dynamic linking, but we need to support it anyway because it is
|
||||
@@ -1370,8 +1415,11 @@ SharedFile<E>::mark_live_objects(Context<E> &ctx,
|
||||
if (sym.is_traced)
|
||||
print_trace_symbol(ctx, *this, esym, sym);
|
||||
|
||||
// We follow undefined symbols in a DSO only to handle
|
||||
// --no-allow-shlib-undefined.
|
||||
if (esym.is_undef() && !esym.is_weak() && sym.file &&
|
||||
!sym.file->is_alive.test_and_set()) {
|
||||
(!sym.file->is_dso || !ctx.arg.allow_shlib_undefined) &&
|
||||
!sym.file->is_reachable.test_and_set()) {
|
||||
feeder(sym.file);
|
||||
|
||||
if (sym.is_traced)
|
||||
@@ -1398,11 +1446,10 @@ std::span<Symbol<E> *> SharedFile<E>::get_symbols_at(Symbol<E> *sym) {
|
||||
});
|
||||
});
|
||||
|
||||
auto [begin, end] = std::equal_range(sorted_syms.begin(), sorted_syms.end(),
|
||||
sym, [&](Symbol<E> *a, Symbol<E> *b) {
|
||||
return a->esym().st_value < b->esym().st_value;
|
||||
auto [begin, end] = ranges::equal_range(sorted_syms, sym->esym().st_value, {},
|
||||
[](Symbol<E> *x) {
|
||||
return x->esym().st_value;
|
||||
});
|
||||
|
||||
return {&*begin, (size_t)(end - begin)};
|
||||
}
|
||||
|
||||
@@ -1424,9 +1471,12 @@ i64 SharedFile<E>::get_alignment(Symbol<E> *sym) {
|
||||
|
||||
template <typename E>
|
||||
bool SharedFile<E>::is_readonly(Symbol<E> *sym) {
|
||||
ElfEhdr<E> &ehdr = *(ElfEhdr<E> *)this->mf->data;
|
||||
std::span<ElfPhdr<E>> phdrs((ElfPhdr<E> *)(this->mf->data + ehdr.e_phoff),
|
||||
ehdr.e_phnum);
|
||||
u64 val = sym->esym().st_value;
|
||||
|
||||
for (ElfPhdr<E> &phdr : this->get_phdrs())
|
||||
for (ElfPhdr<E> &phdr : phdrs)
|
||||
if ((phdr.p_type == PT_LOAD || phdr.p_type == PT_GNU_RELRO) &&
|
||||
!(phdr.p_flags & PF_W) &&
|
||||
phdr.p_vaddr <= val && val < phdr.p_vaddr + phdr.p_memsz)
|
||||
@@ -1482,6 +1532,7 @@ template class SharedFile<E>;
|
||||
template Symbol<E> *get_symbol(Context<E> &, std::string_view, std::string_view);
|
||||
template Symbol<E> *get_symbol(Context<E> &, std::string_view);
|
||||
template std::string_view demangle(const Symbol<E> &);
|
||||
template ComdatGroup *insert_comdat_group(Context<E> &, std::string_view);
|
||||
template std::ostream &operator<<(std::ostream &, const Symbol<E> &);
|
||||
template std::ostream &operator<<(std::ostream &, const InputFile<E> &);
|
||||
|
||||
|
||||
+19
-19
@@ -1,6 +1,5 @@
|
||||
#include "mold.h"
|
||||
|
||||
#include <limits>
|
||||
#include <zlib.h>
|
||||
#include <zstd.h>
|
||||
|
||||
@@ -116,6 +115,11 @@ static void do_action(Context<E> &ctx, Action action, InputSection<E> &isec,
|
||||
break;
|
||||
case COPYREL:
|
||||
// Create a copy relocation
|
||||
if (!sym.file->is_dso) {
|
||||
assert(sym.esym().is_undef_weak());
|
||||
Error(ctx) << isec << ": cannot create a copy relocation for "
|
||||
<< sym <<"; recompile with -fPIE or -fPIC";
|
||||
}
|
||||
sym.flags |= NEEDS_COPYREL;
|
||||
break;
|
||||
case PLT:
|
||||
@@ -226,27 +230,24 @@ void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
|
||||
// relocations are allowed to remove bytes from the middle of a
|
||||
// section and shrink the overall size of it.
|
||||
if constexpr (is_riscv<E> || is_loongarch<E>) {
|
||||
if (extra.r_deltas.empty()) {
|
||||
std::span<RelocDelta> deltas = extra.r_deltas;
|
||||
|
||||
if (deltas.empty()) {
|
||||
// If a section is not relaxed, we can copy it as a one big chunk.
|
||||
copy_contents(ctx, buf);
|
||||
} else {
|
||||
// A relaxed section is copied piece-wise.
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
u8 *buf2 = buf;
|
||||
i64 pos = 0;
|
||||
memcpy(buf, contents.data(), deltas[0].offset);
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
i64 delta = extra.r_deltas[i + 1] - extra.r_deltas[i];
|
||||
if (delta == 0)
|
||||
continue;
|
||||
assert(delta > 0);
|
||||
|
||||
const ElfRel<E> &r = rels[i];
|
||||
memcpy(buf2, contents.data() + pos, r.r_offset - pos);
|
||||
buf2 += r.r_offset - pos;
|
||||
pos = r.r_offset + delta;
|
||||
for (i64 i = 0; i < deltas.size(); i++) {
|
||||
i64 offset = deltas[i].offset;
|
||||
i64 delta = deltas[i].delta;
|
||||
i64 end = (i + 1 == deltas.size()) ? contents.size() : deltas[i + 1].offset;
|
||||
i64 removed_bytes = get_removed_bytes(deltas, i);
|
||||
memcpy(buf + offset + removed_bytes - delta,
|
||||
contents.data() + offset + removed_bytes,
|
||||
end - offset - removed_bytes);
|
||||
}
|
||||
memcpy(buf2, contents.data() + pos, contents.size() - pos);
|
||||
}
|
||||
} else {
|
||||
copy_contents(ctx, buf);
|
||||
@@ -342,7 +343,7 @@ bool InputSection<E>::record_undef_error(Context<E> &ctx, const ElfRel<E> &rel)
|
||||
template <typename E>
|
||||
MergeableSection<E>::MergeableSection(Context<E> &ctx, MergedSection<E> &parent,
|
||||
std::unique_ptr<InputSection<E>> &isec)
|
||||
: parent(parent), section(std::move(isec)), p2align(section->p2align) {
|
||||
: parent(parent), p2align(isec->p2align), section(std::move(isec)) {
|
||||
section->uncompress(ctx);
|
||||
|
||||
std::scoped_lock lock(parent.mu);
|
||||
@@ -381,8 +382,7 @@ template <typename E>
|
||||
void MergeableSection<E>::split_contents(Context<E> &ctx) {
|
||||
std::string_view data = section->contents;
|
||||
if (data.size() > UINT32_MAX)
|
||||
Fatal(ctx) << *section
|
||||
<< ": mergeable section too large";
|
||||
Fatal(ctx) << *section << ": mergeable section too large";
|
||||
|
||||
i64 entsize = parent.shdr.sh_entsize;
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "mold.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <iomanip>
|
||||
|
||||
namespace mold {
|
||||
|
||||
@@ -268,8 +267,8 @@ static bool read_label(std::span<std::string_view> &tok, std::string label) {
|
||||
template <typename E>
|
||||
std::span<std::string_view>
|
||||
Script<E>::read_version_script_commands(std::span<std::string_view> tok,
|
||||
std::string_view ver_str, u16 ver_idx,
|
||||
bool is_global, bool is_cpp) {
|
||||
std::string_view ver_str, u16 ver_idx,
|
||||
bool is_global, bool is_cpp) {
|
||||
while (!tok.empty() && tok[0] != "}") {
|
||||
if (read_label(tok, "global")) {
|
||||
is_global = true;
|
||||
@@ -333,7 +332,7 @@ Script<E>::read_version_script(std::span<std::string_view> tok) {
|
||||
} else {
|
||||
ver_str = tok[0];
|
||||
ver_idx = next_ver++;
|
||||
ctx.arg.version_definitions.push_back(std::string(tok[0]));
|
||||
ctx.arg.version_definitions.emplace_back(tok[0]);
|
||||
tok = tok.subspan(1);
|
||||
}
|
||||
|
||||
@@ -359,8 +358,8 @@ void Script<E>::parse_version_script() {
|
||||
template <typename E>
|
||||
std::span<std::string_view>
|
||||
Script<E>::read_dynamic_list_commands(std::span<std::string_view> tok,
|
||||
std::vector<DynamicPattern> &result,
|
||||
bool is_cpp) {
|
||||
std::vector<DynamicPattern> &result,
|
||||
bool is_cpp) {
|
||||
while (!tok.empty() && tok[0] != "}") {
|
||||
if (tok[0] == "extern") {
|
||||
tok = tok.subspan(1);
|
||||
|
||||
+21
-12
@@ -177,13 +177,14 @@ static PluginStatus add_input_file(const char *path) {
|
||||
static i64 file_priority = 100;
|
||||
|
||||
MappedFile *mf = must_open_file(ctx, path);
|
||||
mf->is_dependency = false;
|
||||
|
||||
ObjectFile<E> *file = new ObjectFile<E>(ctx, mf, "", false);
|
||||
ObjectFile<E> *file = new ObjectFile<E>(ctx, mf, "");
|
||||
ctx.obj_pool.emplace_back(file);
|
||||
lto_objects<E>.push_back(file);
|
||||
|
||||
file->priority = file_priority++;
|
||||
file->is_alive = true;
|
||||
file->is_reachable = true;
|
||||
file->parse(ctx);
|
||||
file->resolve_symbols(ctx);
|
||||
return LDPS_OK;
|
||||
@@ -284,7 +285,7 @@ get_symbols(const void *handle, int nsyms, PluginSymbol *psyms, bool is_v2) {
|
||||
// If file is an archive member which was not chose to be included in
|
||||
// to the final result, we need to make the plugin to ignore all
|
||||
// symbols.
|
||||
if (!file.is_alive) {
|
||||
if (!file.is_reachable) {
|
||||
assert(!is_v2);
|
||||
for (int i = 0; i < nsyms; i++)
|
||||
psyms[i].resolution = LDPR_PREEMPTED_REG;
|
||||
@@ -342,7 +343,7 @@ static void restart_process(Context<E> &ctx) {
|
||||
args.push_back(strdup(std::string(arg).c_str()));
|
||||
|
||||
for (std::unique_ptr<ObjectFile<E>> &file : ctx.obj_pool)
|
||||
if (file->is_lto_obj && !file->is_alive)
|
||||
if (file->is_lto_obj && !file->is_reachable)
|
||||
args.push_back(strdup(("--:ignore-ir-file=" +
|
||||
file->mf->get_identifier()).c_str()));
|
||||
|
||||
@@ -598,6 +599,12 @@ create_plugin_input_file(Context<E> &ctx, MappedFile *mf) {
|
||||
|
||||
template <typename E>
|
||||
ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile *mf) {
|
||||
if (ctx.arg.plugin.empty())
|
||||
Fatal(ctx) << mf->name << ": don't know how to handle this LTO object file "
|
||||
<< "because no -plugin option was given. Please make sure you "
|
||||
<< "added -flto not only for creating object files but also for "
|
||||
<< "creating the final executable.";
|
||||
|
||||
load_lto_plugin(ctx);
|
||||
|
||||
// V0 API's claim_file is not thread-safe.
|
||||
@@ -606,12 +613,6 @@ ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile *mf) {
|
||||
if (!is_gcc_linker_api_v1)
|
||||
lock.lock();
|
||||
|
||||
if (ctx.arg.plugin.empty())
|
||||
Fatal(ctx) << mf->name << ": don't know how to handle this LTO object file "
|
||||
<< "because no -plugin option was given. Please make sure you "
|
||||
<< "added -flto not only for creating object files but also for "
|
||||
<< "creating the final executable.";
|
||||
|
||||
// Create mold's object instance
|
||||
ObjectFile<E> *obj = new ObjectFile<E>;
|
||||
ctx.obj_pool.emplace_back(obj);
|
||||
@@ -653,6 +654,7 @@ ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile *mf) {
|
||||
|
||||
// Initialize esyms
|
||||
obj->lto_elf_syms.resize(plugin_symbols.size() + 1);
|
||||
obj->lto_comdat_groups.resize(plugin_symbols.size() + 1);
|
||||
i64 strtab_offset = 1;
|
||||
|
||||
for (i64 i = 0; i < plugin_symbols.size(); i++) {
|
||||
@@ -663,6 +665,13 @@ ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile *mf) {
|
||||
i64 len = strlen(psym.name);
|
||||
memcpy(strtab.data() + strtab_offset, psym.name, len);
|
||||
strtab_offset += len + 1;
|
||||
|
||||
// comdat_key is non-null if the symbol is defined in a comdat member
|
||||
// section. We handle such symbols differently than comdat symbols in
|
||||
// a regular file because, unlike regular object files, IR files don't
|
||||
// have input sections.
|
||||
if (psym.comdat_key)
|
||||
obj->lto_comdat_groups[i + 1] = insert_comdat_group(ctx, psym.comdat_key);
|
||||
}
|
||||
|
||||
obj->symbol_strtab = save_string(ctx, strtab);
|
||||
@@ -685,7 +694,7 @@ std::vector<ObjectFile<E> *> run_lto_plugin(Context<E> &ctx) {
|
||||
phase = 2;
|
||||
|
||||
// Set `referenced_by_regular_obj` bit.
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
|
||||
if (!file->is_lto_obj) {
|
||||
for (Symbol<E> *sym : file->get_global_syms()) {
|
||||
if (sym->file && !sym->file->is_dso &&
|
||||
@@ -717,7 +726,7 @@ std::vector<ObjectFile<E> *> run_lto_plugin(Context<E> &ctx) {
|
||||
// given to the LTO backend. Such sections contains code and data for
|
||||
// peripherails (typically GPUs).
|
||||
for (ObjectFile<E> *file : ctx.objs) {
|
||||
if (file->is_alive && !file->is_lto_obj && file->is_gcc_offload_obj) {
|
||||
if (file->is_reachable && !file->is_lto_obj && file->is_gcc_offload_obj) {
|
||||
PluginInputFile pfile = create_plugin_input_file(ctx, file->mf);
|
||||
int claimed = false;
|
||||
claim_file_hook(&pfile, &claimed);
|
||||
|
||||
+40
-40
@@ -3,14 +3,9 @@
|
||||
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <map>
|
||||
#include <regex>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <tbb/global_control.h>
|
||||
#include <tbb/parallel_for_each.h>
|
||||
#include <unordered_set>
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -46,11 +41,11 @@ static ObjectFile<E> *new_object_file(Context<E> &ctx, ReaderContext &rctx,
|
||||
|
||||
check_file_compatibility(ctx, rctx, mf);
|
||||
|
||||
bool in_lib = rctx.in_lib || (!archive_name.empty() && !rctx.whole_archive);
|
||||
|
||||
ObjectFile<E> *file = new ObjectFile<E>(ctx, mf, archive_name, in_lib);
|
||||
ObjectFile<E> *file = new ObjectFile<E>(ctx, mf, archive_name);
|
||||
ctx.obj_pool.emplace_back(file);
|
||||
file->priority = ctx.file_priority++;
|
||||
file->is_reachable =
|
||||
!rctx.in_lib && (archive_name.empty() || rctx.whole_archive);
|
||||
|
||||
rctx.tg->run([file, &ctx] { file->parse(ctx); });
|
||||
if (ctx.arg.trace)
|
||||
@@ -70,8 +65,9 @@ static ObjectFile<E> *new_lto_obj(Context<E> &ctx, ReaderContext &rctx,
|
||||
ObjectFile<E> *file = read_lto_object(ctx, mf);
|
||||
file->priority = ctx.file_priority++;
|
||||
file->archive_name = archive_name;
|
||||
file->is_in_lib = rctx.in_lib || (!archive_name.empty() && !rctx.whole_archive);
|
||||
file->is_alive = !file->is_in_lib;
|
||||
file->is_reachable =
|
||||
!rctx.in_lib && (archive_name.empty() || rctx.whole_archive);
|
||||
|
||||
if (ctx.arg.trace)
|
||||
Out(ctx) << "trace: " << *file;
|
||||
return file;
|
||||
@@ -85,7 +81,7 @@ new_shared_file(Context<E> &ctx, ReaderContext &rctx, MappedFile *mf) {
|
||||
SharedFile<E> *file = new SharedFile<E>(ctx, mf);
|
||||
ctx.dso_pool.emplace_back(file);
|
||||
file->priority = ctx.file_priority++;
|
||||
file->is_alive = !rctx.as_needed;
|
||||
file->is_reachable = !rctx.as_needed;
|
||||
|
||||
rctx.tg->run([file, &ctx] { file->parse(ctx); });
|
||||
if (ctx.arg.trace)
|
||||
@@ -269,7 +265,7 @@ static void read_input_files(Context<E> &ctx, std::span<std::string> args) {
|
||||
template <typename E>
|
||||
static bool has_lto_obj(Context<E> &ctx) {
|
||||
for (ObjectFile<E> *file : ctx.objs)
|
||||
if (file->is_alive && (file->is_lto_obj || file->is_gcc_offload_obj))
|
||||
if (file->is_reachable && (file->is_lto_obj || file->is_gcc_offload_obj))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -336,6 +332,10 @@ int mold_main(int argc, char **argv) {
|
||||
});
|
||||
}
|
||||
|
||||
// Handle -repro
|
||||
if (ctx.arg.repro)
|
||||
write_repro_file(ctx);
|
||||
|
||||
Timer t_before_copy(ctx, "before_copy");
|
||||
|
||||
// Apply -exclude-libs
|
||||
@@ -357,8 +357,8 @@ int mold_main(int argc, char **argv) {
|
||||
|
||||
// Now that we know which object files are to be included to the
|
||||
// final output, we can remove unnecessary files.
|
||||
std::erase_if(ctx.objs, [](InputFile<E> *file) { return !file->is_alive; });
|
||||
std::erase_if(ctx.dsos, [](InputFile<E> *file) { return !file->is_alive; });
|
||||
std::erase_if(ctx.objs, [](InputFile<E> *file) { return !file->is_reachable; });
|
||||
std::erase_if(ctx.dsos, [](InputFile<E> *file) { return !file->is_reachable; });
|
||||
|
||||
// Parse .eh_frame section contents.
|
||||
parse_eh_frame_sections(ctx);
|
||||
@@ -386,10 +386,18 @@ int mold_main(int argc, char **argv) {
|
||||
// Set is_imported and is_exported bits for each symbol.
|
||||
compute_import_export(ctx);
|
||||
|
||||
// Make sure that there's no duplicate symbol
|
||||
if (!ctx.arg.allow_multiple_definition)
|
||||
check_duplicate_symbols(ctx);
|
||||
|
||||
// Set "address-taken" bits for input sections.
|
||||
if (ctx.arg.icf)
|
||||
compute_address_significance(ctx);
|
||||
|
||||
// Handle PPC64-specific .opd sections.
|
||||
if constexpr (is_ppc64v1<E>)
|
||||
ppc64v1_rewrite_opd(ctx);
|
||||
|
||||
// Garbage-collect unreachable sections.
|
||||
if (ctx.arg.gc_sections)
|
||||
gc_sections(ctx);
|
||||
@@ -401,19 +409,13 @@ int mold_main(int argc, char **argv) {
|
||||
// Create linker-synthesized sections such as .got or .plt.
|
||||
create_synthetic_sections(ctx);
|
||||
|
||||
// Make sure that there's no duplicate symbol
|
||||
if (!ctx.arg.allow_multiple_definition)
|
||||
check_duplicate_symbols(ctx);
|
||||
|
||||
// Handle --no-allow-shlib-undefined
|
||||
if (!ctx.arg.allow_shlib_undefined)
|
||||
check_shlib_undefined(ctx);
|
||||
|
||||
// Warn if symbols with different types are defined under the same name.
|
||||
check_symbol_types(ctx);
|
||||
|
||||
if constexpr (is_ppc64v1<E>)
|
||||
ppc64v1_rewrite_opd(ctx);
|
||||
|
||||
// Bin input sections into output sections.
|
||||
create_output_sections(ctx);
|
||||
|
||||
@@ -456,10 +458,6 @@ int mold_main(int argc, char **argv) {
|
||||
if (ctx.arg.print_dependencies)
|
||||
print_dependencies(ctx);
|
||||
|
||||
// Handle -repro
|
||||
if (ctx.arg.repro)
|
||||
write_repro_file(ctx);
|
||||
|
||||
// Handle --require-defined
|
||||
for (Symbol<E> *sym : ctx.arg.require_defined)
|
||||
if (!sym->file)
|
||||
@@ -482,16 +480,7 @@ int mold_main(int argc, char **argv) {
|
||||
shuffle_sections(ctx);
|
||||
|
||||
// Copy string referred by .dynamic to .dynstr.
|
||||
for (SharedFile<E> *file : ctx.dsos)
|
||||
ctx.dynstr->add_string(file->soname);
|
||||
for (std::string_view str : ctx.arg.auxiliary)
|
||||
ctx.dynstr->add_string(str);
|
||||
for (std::string_view str : ctx.arg.filter)
|
||||
ctx.dynstr->add_string(str);
|
||||
if (!ctx.arg.rpaths.empty())
|
||||
ctx.dynstr->add_string(ctx.arg.rpaths);
|
||||
if (!ctx.arg.soname.empty())
|
||||
ctx.dynstr->add_string(ctx.arg.soname);
|
||||
add_dynamic_strings(ctx);
|
||||
|
||||
if constexpr (is_ppc64v1<E>)
|
||||
ppc64v1_scan_symbols(ctx);
|
||||
@@ -536,10 +525,6 @@ int mold_main(int argc, char **argv) {
|
||||
// Fill .gnu.version_r section contents.
|
||||
ctx.verneed->construct(ctx);
|
||||
|
||||
// Compute .symtab and .strtab sizes for each file.
|
||||
if (!ctx.arg.strip_all)
|
||||
create_output_symtab(ctx);
|
||||
|
||||
// .eh_frame is a special section from the linker's point of view,
|
||||
// as its contents are parsed and reconstructed by the linker,
|
||||
// unlike other sections that are regarded as opaque bytes.
|
||||
@@ -551,6 +536,17 @@ int mold_main(int argc, char **argv) {
|
||||
if (ctx.arg.emit_relocs)
|
||||
create_reloc_sections(ctx);
|
||||
|
||||
// We've created range extension thunks with a pessimistive assumption
|
||||
// that all out-of-section references are out of range. Now that we are
|
||||
// able to assign addresses to all SHF_ALLOC output sections, we can
|
||||
// eliminate excessive thunks.
|
||||
if constexpr (needs_thunk<E>)
|
||||
remove_redundant_thunks(ctx);
|
||||
|
||||
// Compute .symtab and .strtab sizes for each file.
|
||||
if (!ctx.arg.strip_all)
|
||||
create_output_symtab(ctx);
|
||||
|
||||
// Compute the section header values for all sections.
|
||||
compute_section_headers(ctx);
|
||||
|
||||
@@ -590,6 +586,10 @@ int mold_main(int argc, char **argv) {
|
||||
|
||||
// At this point, both memory and file layouts are fixed.
|
||||
|
||||
// Gather thunk symbols and attach them to themselves.
|
||||
if constexpr (needs_thunk<E>)
|
||||
gather_thunk_addresses(ctx);
|
||||
|
||||
t_before_copy.stop();
|
||||
|
||||
// Create an output file
|
||||
@@ -607,7 +607,7 @@ int mold_main(int argc, char **argv) {
|
||||
|
||||
// Dynamic linker works better with sorted .rela.dyn section,
|
||||
// so we sort them.
|
||||
ctx.reldyn->sort(ctx);
|
||||
sort_reldyn(ctx);
|
||||
|
||||
// .gdb_index's contents cannot be constructed before applying
|
||||
// relocations to other debug sections. We have relocated debug
|
||||
|
||||
+3
-5
@@ -4,8 +4,8 @@
|
||||
#include <iomanip>
|
||||
#include <ios>
|
||||
#include <sstream>
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
#include <tbb/parallel_for_each.h>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mold {
|
||||
|
||||
@@ -35,10 +35,8 @@ static Map<E> get_map(Context<E> &ctx) {
|
||||
return map;
|
||||
|
||||
tbb::parallel_for(map.range(), [](const typename Map<E>::range_type &range) {
|
||||
for (auto it = range.begin(); it != range.end(); it++) {
|
||||
std::vector<Symbol<E> *> &vec = it->second;
|
||||
sort(vec, [](Symbol<E> *a, Symbol<E> *b) { return a->value < b->value; });
|
||||
}
|
||||
for (auto &[k, v] : range)
|
||||
ranges::stable_sort(v, {}, &Symbol<E>::value);
|
||||
});
|
||||
return map;
|
||||
}
|
||||
|
||||
+154
-78
@@ -3,11 +3,8 @@
|
||||
#include "../lib/common.h"
|
||||
#include "elf.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <bitset>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
@@ -15,13 +12,10 @@
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
#include <tbb/concurrent_unordered_map.h>
|
||||
#include <tbb/concurrent_vector.h>
|
||||
#include <tbb/enumerable_thread_specific.h>
|
||||
#include <tbb/spin_mutex.h>
|
||||
#include <tbb/task_group.h>
|
||||
#include <type_traits>
|
||||
@@ -89,12 +83,11 @@ struct SymbolAux {
|
||||
i32 plt_idx = -1;
|
||||
i32 pltgot_idx = -1;
|
||||
i32 dynsym_idx = -1;
|
||||
u32 djb_hash = 0;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SymbolAux<PPC64V1> : SymbolAux<X86_64> {
|
||||
i32 opd_idx = -1;
|
||||
u32 djb_hash = 0;
|
||||
|
||||
// For range extension thunks
|
||||
std::vector<u64> thunk_addrs;
|
||||
};
|
||||
|
||||
//
|
||||
@@ -113,25 +106,60 @@ public:
|
||||
i64 size() const { return E::thunk_hdr_size + symbols.size() * E::thunk_size; }
|
||||
void copy_buf(Context<E> &ctx);
|
||||
|
||||
u64 get_addr(i64 idx) const {
|
||||
return output_section.shdr.sh_addr + offset + E::thunk_hdr_size +
|
||||
idx * E::thunk_size;
|
||||
u64 get_addr() const {
|
||||
return output_section.shdr.sh_addr + offset;
|
||||
}
|
||||
|
||||
static constexpr i64 alignment = 16;
|
||||
u64 get_addr(i64 i) const {
|
||||
return get_addr() + E::thunk_hdr_size + E::thunk_size * i;
|
||||
}
|
||||
|
||||
OutputSection<E> &output_section;
|
||||
i64 offset;
|
||||
std::mutex mu;
|
||||
std::vector<Symbol<E> *> symbols;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
struct ThunkRef {
|
||||
static constexpr i64 MAX_SYM_IDX = (1 << 17) - 1;
|
||||
template <needs_thunk E>
|
||||
static consteval i64 get_branch_distance() {
|
||||
// ARM64's branch has 26 bits immediate. The immediate is padded with
|
||||
// implicit two-bit zeros because all instructions are 4 bytes aligned
|
||||
// and therefore the least two bits are always zero. So the branch
|
||||
// operand is effectively 28 bits long. That means the branch range is
|
||||
// [-2^27, 2^27) or PC ± 128 MiB.
|
||||
if (is_arm64<E>)
|
||||
return 1 << 27;
|
||||
|
||||
i32 thunk_idx : 14 = -1;
|
||||
i32 sym_idx : 18 = -1;
|
||||
};
|
||||
// ARM32's Thumb branch has 24 bits immediate, and the instructions are
|
||||
// aligned to 2, so it's effectively 25 bits. It's [-2^24, 2^24) or PC ±
|
||||
// 16 MiB.
|
||||
//
|
||||
// ARM32's non-Thumb branches have twice longer range than its Thumb
|
||||
// counterparts, but we conservatively use the Thumb's limitation.
|
||||
if (is_arm32<E>)
|
||||
return 1 << 24;
|
||||
|
||||
// PPC's branch has 24 bits immediate, and the instructions are aligned
|
||||
// to 4, therefore the reach is [-2^25, 2^25) or PC ± 32 MiB.
|
||||
assert(is_ppc<E>);
|
||||
return 1 << 25;
|
||||
}
|
||||
|
||||
// The maximum distance of branch instructions used for function calls.
|
||||
//
|
||||
// The exact origin for computing a destination varies slightly depending
|
||||
// on the target architecture. For example, ARM32's B instruction jumps to
|
||||
// the branch's address + immediate + 4 (i.e., B with offset 0 jumps to
|
||||
// the next instruction), while RISC-V has no such implicit bias. Here, we
|
||||
// subtract 32 as a safety margin that is large enough for all targets.
|
||||
template <needs_thunk E>
|
||||
static constexpr i64 branch_distance = get_branch_distance<E>() - 32;
|
||||
|
||||
template <needs_thunk E>
|
||||
void remove_redundant_thunks(Context<E> &ctx);
|
||||
|
||||
template <needs_thunk E>
|
||||
void gather_thunk_addresses(Context<E> &ctx);
|
||||
|
||||
//
|
||||
// input-sections.cc
|
||||
@@ -234,14 +262,29 @@ struct FdeRecord {
|
||||
template <typename E>
|
||||
struct InputSectionExtras {};
|
||||
|
||||
template <needs_thunk E>
|
||||
struct InputSectionExtras<E> {
|
||||
std::vector<ThunkRef> thunk_refs;
|
||||
template <>
|
||||
struct InputSectionExtras<ARM32> {
|
||||
InputSection<ARM32> *exidx = nullptr;
|
||||
};
|
||||
|
||||
struct RelocDelta {
|
||||
u64 offset;
|
||||
i64 delta;
|
||||
};
|
||||
|
||||
// RISC-V and LoongArch support code-shrinking linker relaxation.
|
||||
//
|
||||
// r_deltas is used to manage the locations where instructions are removed
|
||||
// from a section. r_deltas is sorted by offset. Each RelocDelta indicates
|
||||
// that the contents at and after `offset` and up to the next RelocDelta
|
||||
// offset need to be shifted towards the beginning of the section by
|
||||
// `delta` bytes when copying section contents to the output buffer.
|
||||
//
|
||||
// Since code-shrinking relaxation never bloats section contents, `delta`
|
||||
// increases monotonically within the vector as well.
|
||||
template <typename E> requires is_riscv<E> || is_loongarch<E>
|
||||
struct InputSectionExtras<E> {
|
||||
std::vector<i32> r_deltas;
|
||||
std::vector<RelocDelta> r_deltas;
|
||||
};
|
||||
|
||||
// InputSection represents a section in an input object file.
|
||||
@@ -268,6 +311,7 @@ public:
|
||||
bool is_relr_reloc(Context<E> &ctx, const ElfRel<E> &rel) const;
|
||||
bool icf_removed() const;
|
||||
bool record_undef_error(Context<E> &ctx, const ElfRel<E> &rel);
|
||||
void check_range(Context<E> &ctx, i64 i, i64 val, i64 lo, i64 hi);
|
||||
|
||||
std::pair<SectionFragment<E> *, i64>
|
||||
get_fragment(Context<E> &ctx, const ElfRel<E> &rel);
|
||||
@@ -324,8 +368,6 @@ private:
|
||||
void apply_toc_rel(Context<E> &ctx, Symbol<E> &sym, const ElfRel<E> &rel,
|
||||
u8 *loc, u64 S, i64 A, u64 P, ElfRel<E> **dynrel);
|
||||
|
||||
u64 get_thunk_addr(i64 idx) requires needs_thunk<E>;
|
||||
|
||||
std::optional<u64> get_tombstone(Symbol<E> &sym, SectionFragment<E> *frag);
|
||||
};
|
||||
|
||||
@@ -397,7 +439,7 @@ public:
|
||||
virtual i64 get_reldyn_size(Context<E> &ctx) const { return 0; }
|
||||
virtual void construct_relr(Context<E> &ctx) {}
|
||||
virtual void copy_buf(Context<E> &ctx) {}
|
||||
virtual void write_to(Context<E> &ctx, u8 *buf, ElfRel<E> *rel) { unreachable(); }
|
||||
virtual void write_to(Context<E> &ctx, u8 *buf) { unreachable(); }
|
||||
virtual void update_shdr(Context<E> &ctx) {}
|
||||
|
||||
std::string_view name;
|
||||
@@ -522,19 +564,22 @@ public:
|
||||
i64 get_reldyn_size(Context<E> &ctx) const override;
|
||||
void construct_relr(Context<E> &ctx) override;
|
||||
void copy_buf(Context<E> &ctx) override;
|
||||
void write_to(Context<E> &ctx, u8 *buf, ElfRel<E> *rel) override;
|
||||
void write_to(Context<E> &ctx, u8 *buf) override;
|
||||
|
||||
void compute_symtab_size(Context<E> &ctx) override;
|
||||
void populate_symtab(Context<E> &ctx) override;
|
||||
|
||||
void scan_abs_relocations(Context<E> &ctx);
|
||||
void create_range_extension_thunks(Context<E> &ctx);
|
||||
void create_range_extension_thunks(Context<E> &ctx, bool first_pass);
|
||||
|
||||
std::vector<InputSection<E> *> members;
|
||||
std::vector<std::unique_ptr<Thunk<E>>> thunks;
|
||||
std::unique_ptr<RelocSection<E>> reloc_sec;
|
||||
std::vector<AbsRel<E>> abs_rels;
|
||||
Atomic<u32> sh_flags;
|
||||
|
||||
// Used only by create_output_sections()
|
||||
std::vector<std::vector<InputSection<E> *>> members_vec;
|
||||
};
|
||||
|
||||
template <typename E>
|
||||
@@ -548,9 +593,9 @@ public:
|
||||
this->shdr.sh_addralign = sizeof(Word<E>);
|
||||
|
||||
// We always create a .got so that _GLOBAL_OFFSET_TABLE_ has
|
||||
// something to point to. s390x psABI define GOT[1] as a
|
||||
// reserved slot, so we allocate one more for them.
|
||||
this->shdr.sh_size = (is_s390x<E> ? 2 : 1) * sizeof(Word<E>);
|
||||
// something to point to. s390x psABI define GOT[1] and GOT[2]
|
||||
// as reserved slots, so we allocate two more for them.
|
||||
this->shdr.sh_size = (is_s390x<E> ? 3 : 1) * sizeof(Word<E>);
|
||||
}
|
||||
|
||||
void add_got_symbol(Context<E> &ctx, Symbol<E> *sym);
|
||||
@@ -666,7 +711,6 @@ public:
|
||||
}
|
||||
|
||||
void update_shdr(Context<E> &ctx) override;
|
||||
void sort(Context<E> &ctx);
|
||||
};
|
||||
|
||||
template <typename E>
|
||||
@@ -726,8 +770,6 @@ public:
|
||||
i64 find_string(std::string_view str);
|
||||
void copy_buf(Context<E> &ctx) override;
|
||||
|
||||
i64 dynsym_offset = -1;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string_view, i64> strings;
|
||||
};
|
||||
@@ -799,6 +841,7 @@ public:
|
||||
void copy_buf(Context<E> &ctx) override;
|
||||
|
||||
std::vector<Symbol<E> *> symbols;
|
||||
i64 dynstr_offset = -1;
|
||||
};
|
||||
|
||||
template <typename E>
|
||||
@@ -856,7 +899,7 @@ public:
|
||||
void resolve(Context<E> &ctx);
|
||||
void compute_section_size(Context<E> &ctx) override;
|
||||
void copy_buf(Context<E> &ctx) override;
|
||||
void write_to(Context<E> &ctx, u8 *buf, ElfRel<E> *rel) override;
|
||||
void write_to(Context<E> &ctx, u8 *buf) override;
|
||||
void print_stats(Context<E> &ctx);
|
||||
|
||||
std::vector<MergeableSection<E> *> members;
|
||||
@@ -1259,12 +1302,12 @@ public:
|
||||
|
||||
MergedSection<E> &parent;
|
||||
std::vector<SectionFragment<E> *> fragments;
|
||||
u8 p2align = 0;
|
||||
|
||||
private:
|
||||
std::unique_ptr<InputSection<E>> section;
|
||||
std::vector<u32> frag_offsets;
|
||||
std::vector<u32> hashes;
|
||||
u8 p2align = 0;
|
||||
};
|
||||
|
||||
// InputFile is the base class of ObjectFile and SharedFile.
|
||||
@@ -1284,9 +1327,7 @@ public:
|
||||
|
||||
std::string_view get_string(Context<E> &ctx, const ElfShdr<E> &shdr);
|
||||
std::string_view get_string(Context<E> &ctx, i64 idx);
|
||||
|
||||
ElfEhdr<E> &get_ehdr() { return *(ElfEhdr<E> *)mf->data; }
|
||||
std::span<ElfPhdr<E>> get_phdrs();
|
||||
u32 get_eflags() { return ((ElfEhdr<E> *)mf->data)->e_flags; }
|
||||
|
||||
ElfShdr<E> *find_section(i64 type);
|
||||
|
||||
@@ -1308,7 +1349,7 @@ public:
|
||||
std::string filename;
|
||||
bool is_dso = false;
|
||||
i64 priority;
|
||||
Atomic<bool> is_alive = false;
|
||||
Atomic<bool> is_reachable = false;
|
||||
std::string_view shstrtab;
|
||||
std::string_view symbol_strtab;
|
||||
|
||||
@@ -1331,7 +1372,8 @@ protected:
|
||||
std::vector<Symbol<E>> frag_syms;
|
||||
};
|
||||
|
||||
template <typename E> struct ObjectFileExtras {};
|
||||
template <typename E>
|
||||
struct ObjectFileExtras {};
|
||||
|
||||
template <is_riscv E>
|
||||
struct ObjectFileExtras<E> {
|
||||
@@ -1351,8 +1393,8 @@ class ObjectFile : public InputFile<E> {
|
||||
public:
|
||||
ObjectFile() = default;
|
||||
|
||||
ObjectFile(Context<E> &ctx, MappedFile *mf,
|
||||
std::string archive_name, bool is_in_lib);
|
||||
ObjectFile(Context<E> &ctx, MappedFile *mf, std::string archive_name)
|
||||
: InputFile<E>(ctx, mf), archive_name(archive_name) {}
|
||||
|
||||
void parse(Context<E> &ctx);
|
||||
void initialize_symbols(Context<E> &ctx);
|
||||
@@ -1374,13 +1416,13 @@ public:
|
||||
std::string archive_name;
|
||||
std::vector<std::unique_ptr<InputSection<E>>> sections;
|
||||
std::vector<std::unique_ptr<MergeableSection<E>>> mergeable_sections;
|
||||
bool is_in_lib = false;
|
||||
std::vector<ElfShdr<E>> elf_sections2;
|
||||
std::vector<CieRecord<E>> cies;
|
||||
std::vector<FdeRecord<E>> fdes;
|
||||
BitVector has_symver;
|
||||
std::vector<bool> has_symver;
|
||||
std::vector<ComdatGroupRef<E>> comdat_groups;
|
||||
std::vector<InputSection<E> *> eh_frame_sections;
|
||||
std::vector<std::vector<ElfRel<E>>> decoded_crel;
|
||||
bool exclude_libs = false;
|
||||
std::map<u32, u32> gnu_properties;
|
||||
bool needs_executable_stack = false;
|
||||
@@ -1402,6 +1444,7 @@ public:
|
||||
|
||||
// For LTO
|
||||
std::vector<ElfSym<E>> lto_elf_syms;
|
||||
std::vector<ComdatGroup *> lto_comdat_groups;
|
||||
|
||||
private:
|
||||
void initialize_sections(Context<E> &ctx);
|
||||
@@ -1433,6 +1476,7 @@ public:
|
||||
std::span<Symbol<E> *> get_symbols_at(Symbol<E> *sym);
|
||||
i64 get_alignment(Symbol<E> *sym);
|
||||
std::vector<std::string_view> get_dt_needed(Context<E> &ctx);
|
||||
std::string_view get_dt_audit(Context<E> &ctx);
|
||||
bool is_readonly(Symbol<E> *sym);
|
||||
|
||||
void mark_live_objects(Context<E> &ctx,
|
||||
@@ -1540,11 +1584,20 @@ void lto_cleanup(Context<E> &ctx);
|
||||
// shrink-sections.cc
|
||||
//
|
||||
|
||||
inline i64 get_removed_bytes(std::span<RelocDelta> deltas, i64 i) {
|
||||
if (i == 0)
|
||||
return deltas[i].delta;
|
||||
return deltas[i].delta - deltas[i - 1].delta;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void shrink_sections(Context<E> &ctx);
|
||||
|
||||
template <typename E>
|
||||
void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc);
|
||||
void shrink_section(Context<E> &ctx, InputSection<E> &isec);
|
||||
|
||||
template <typename E>
|
||||
i64 get_r_delta(InputSection<E> &isec, u64 offset);
|
||||
|
||||
template <typename E>
|
||||
i64 compute_distance(Context<E> &ctx, Symbol<E> &sym,
|
||||
@@ -1626,6 +1679,7 @@ template <typename E> void sort_init_fini(Context<E> &);
|
||||
template <typename E> void sort_ctor_dtor(Context<E> &);
|
||||
template <typename E> void fixup_ctors_in_init_array(Context<E> &);
|
||||
template <typename E> void shuffle_sections(Context<E> &);
|
||||
template <typename E> void add_dynamic_strings(Context<E> &);
|
||||
template <typename E> void compute_section_sizes(Context<E> &);
|
||||
template <typename E> void sort_output_sections(Context<E> &);
|
||||
template <typename E> void claim_unresolved_symbols(Context<E> &);
|
||||
@@ -1646,6 +1700,7 @@ template <typename E> void compute_section_headers(Context<E> &);
|
||||
template <typename E> i64 set_osec_offsets(Context<E> &);
|
||||
template <typename E> void fix_synthetic_symbols(Context<E> &);
|
||||
template <typename E> void compress_debug_sections(Context<E> &);
|
||||
template <typename E> void sort_reldyn(Context<E> &);
|
||||
template <typename E> void write_build_id(Context<E> &);
|
||||
template <typename E> void write_gnu_debuglink(Context<E> &);
|
||||
template <typename E> void write_separate_debug_file(Context<E> &ctx);
|
||||
@@ -1753,6 +1808,12 @@ public:
|
||||
|
||||
template <> u64 get_eflags(Context<PPC64V2> &ctx);
|
||||
|
||||
//
|
||||
// arch-arc.cc
|
||||
//
|
||||
|
||||
template <> u64 get_eflags(Context<ARC> &ctx);
|
||||
|
||||
//
|
||||
// main.cc
|
||||
//
|
||||
@@ -1873,7 +1934,7 @@ struct ContextExtras<SPARC64> {
|
||||
template <typename E>
|
||||
struct Context {
|
||||
Context() {
|
||||
arg.entry = get_symbol(*this, "_start");
|
||||
arg.entry = get_symbol(*this, is_arc<E> ? "__start" : "_start");
|
||||
arg.fini = get_symbol(*this, "_fini");
|
||||
arg.init = get_symbol(*this, "_init");
|
||||
|
||||
@@ -1986,8 +2047,11 @@ struct Context {
|
||||
i64 z_stack_size = 0;
|
||||
std::optional<Glob> unique;
|
||||
std::optional<u64> physical_image_base;
|
||||
std::optional<std::vector<Symbol<E> *>> retain_symbols_file;
|
||||
std::string Map;
|
||||
std::string audit;
|
||||
std::string chroot;
|
||||
std::string depaudit;
|
||||
std::string dependency_file;
|
||||
std::string directory;
|
||||
std::string dynamic_linker;
|
||||
@@ -1999,9 +2063,9 @@ struct Context {
|
||||
std::string soname;
|
||||
std::string sysroot;
|
||||
std::string_view emulation;
|
||||
std::optional<std::vector<Symbol<E> *>> retain_symbols_file;
|
||||
std::unordered_map<std::string_view, u64> section_align;
|
||||
std::unordered_map<std::string_view, u64> section_start;
|
||||
std::unordered_set<std::string_view> discard_section;
|
||||
std::unordered_set<std::string_view> ignore_ir_file;
|
||||
std::unordered_set<std::string_view> wrap;
|
||||
std::vector<SectionOrder> section_order;
|
||||
@@ -2185,24 +2249,14 @@ enum {
|
||||
NEEDS_PPC_OPD = 1 << 7, // for PPCv1
|
||||
};
|
||||
|
||||
// A struct to hold target-dependent symbol members.
|
||||
template <typename E>
|
||||
struct SymbolExtras {};
|
||||
|
||||
template <needs_thunk E>
|
||||
struct SymbolExtras<E> {
|
||||
// For range extension thunks
|
||||
i16 thunk_idx = -1;
|
||||
i16 thunk_sym_idx = -1;
|
||||
};
|
||||
|
||||
// Flags for Symbol<E>::get_addr()
|
||||
enum {
|
||||
NO_PLT = 1 << 0, // Request an address other than .plt
|
||||
NO_OPD = 1 << 1, // Request an address other than .opd (PPC64V1 only)
|
||||
};
|
||||
|
||||
// Symbol class represents a defined symbol.
|
||||
// Symbol class represents a symbol. For each unique symbol name, we
|
||||
// create one instance of Symbol.
|
||||
//
|
||||
// A symbol has not only one but several different addresses if it
|
||||
// has PLT or GOT entries. This class provides various functions to
|
||||
@@ -2255,6 +2309,8 @@ public:
|
||||
u32 get_djb_hash(Context<E> &ctx) const;
|
||||
void set_djb_hash(Context<E> &ctx, u32 hash);
|
||||
|
||||
u64 get_thunk_addr(Context<E> &ctx, u64 P) const requires needs_thunk<E>;
|
||||
|
||||
bool is_absolute() const;
|
||||
bool is_relative() const { return !is_absolute(); }
|
||||
bool is_local(Context<E> &ctx) const;
|
||||
@@ -2453,9 +2509,6 @@ public:
|
||||
|
||||
// If true, we try to dmenagle the sybmol when printing.
|
||||
bool demangle : 1 = false;
|
||||
|
||||
// Target-dependent extra members.
|
||||
[[no_unique_address]] SymbolExtras<E> extra;
|
||||
};
|
||||
|
||||
template <typename E>
|
||||
@@ -2465,6 +2518,9 @@ Symbol<E> *get_symbol(Context<E> &ctx, std::string_view key,
|
||||
template <typename E>
|
||||
Symbol<E> *get_symbol(Context<E> &ctx, std::string_view name);
|
||||
|
||||
template <typename E>
|
||||
ComdatGroup *insert_comdat_group(Context<E> &ctx, std::string_view name);
|
||||
|
||||
template <typename E>
|
||||
std::string_view demangle(const Symbol<E> &sym);
|
||||
|
||||
@@ -2536,7 +2592,11 @@ template <typename E>
|
||||
inline std::span<ElfRel<E>> InputSection<E>::get_rels(Context<E> &ctx) const {
|
||||
if (relsec_idx == -1)
|
||||
return {};
|
||||
return file.template get_data<ElfRel<E>>(ctx, file.elf_sections[relsec_idx]);
|
||||
|
||||
ElfShdr<E> &shdr = file.elf_sections[relsec_idx];
|
||||
if (shdr.sh_type == SHT_CREL)
|
||||
return file.decoded_crel[relsec_idx];
|
||||
return file.template get_data<ElfRel<E>>(ctx, shdr);
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -2568,13 +2628,6 @@ InputSection<E>::get_fragment(Context<E> &ctx, const ElfRel<E> &rel) {
|
||||
return {p.first, p.second + get_addend(*this, rel)};
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
u64 InputSection<E>::get_thunk_addr(i64 idx) requires needs_thunk<E> {
|
||||
ThunkRef ref = extra.thunk_refs[idx];
|
||||
assert(ref.thunk_idx != -1);
|
||||
return output_section->thunks[ref.thunk_idx]->get_addr(ref.sym_idx);
|
||||
}
|
||||
|
||||
// Input object files may contain duplicate code for inline functions
|
||||
// and such. Linkers de-duplicate them at link-time. However, linkers
|
||||
// generaly don't remove debug info for de-duplicated functions because
|
||||
@@ -2618,13 +2671,24 @@ inline bool InputSection<E>::icf_removed() const {
|
||||
return this->leader && this->leader != this;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
inline void
|
||||
InputSection<E>::check_range(Context<E> &ctx, i64 i, i64 val, i64 lo, i64 hi) {
|
||||
if (val < lo || hi <= val) {
|
||||
const ElfRel<E> &rel = get_rels(ctx)[i];
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
Error(ctx) << *this << ": relocation " << rel << " against "
|
||||
<< sym << " out of range: " << val << " is not in ["
|
||||
<< lo << ", " << hi << ")";
|
||||
}
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
std::pair<SectionFragment<E> *, i64>
|
||||
MergeableSection<E>::get_fragment(i64 offset) {
|
||||
std::span<u32> vec = frag_offsets;
|
||||
auto it = std::upper_bound(vec.begin(), vec.end(), offset);
|
||||
i64 idx = it - 1 - vec.begin();
|
||||
return {fragments[idx], offset - vec[idx]};
|
||||
auto it = ranges::upper_bound(frag_offsets, offset);
|
||||
i64 idx = it - 1 - frag_offsets.begin();
|
||||
return {fragments[idx], offset - frag_offsets[idx]};
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -2953,6 +3017,18 @@ inline void Symbol<E>::set_djb_hash(Context<E> &ctx, u32 hash) {
|
||||
ctx.symbol_aux[aux_idx].djb_hash = hash;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
u64
|
||||
Symbol<E>::get_thunk_addr(Context<E> &ctx, u64 P) const requires needs_thunk<E> {
|
||||
std::span<u64> vec = ctx.symbol_aux[aux_idx].thunk_addrs;
|
||||
u64 lo = (P < branch_distance<E>) ? 0 : P - branch_distance<E>;
|
||||
u64 val = *ranges::lower_bound(vec, lo);
|
||||
i64 disp = val - P;
|
||||
if (disp < -branch_distance<E> || branch_distance<E> <= disp)
|
||||
Fatal(ctx) << "range extension thunk out of range: " << *this;
|
||||
return val;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
inline bool Symbol<E>::has_plt(Context<E> &ctx) const {
|
||||
return get_plt_idx(ctx) != -1 || get_pltgot_idx(ctx) != -1;
|
||||
@@ -3125,7 +3201,7 @@ std::string_view save_string(Context<E> &ctx, const std::string &str) {
|
||||
u8 *buf = new u8[str.size() + 1];
|
||||
memcpy(buf, str.data(), str.size());
|
||||
buf[str.size()] = '\0';
|
||||
ctx.string_pool.push_back(std::unique_ptr<u8[]>(buf));
|
||||
ctx.string_pool.emplace_back(buf);
|
||||
return {(char *)buf, str.size()};
|
||||
}
|
||||
|
||||
|
||||
+235
-258
@@ -203,9 +203,7 @@ static std::vector<ElfPhdr<E>> create_phdr(Context<E> &ctx) {
|
||||
// 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;
|
||||
});
|
||||
ranges::stable_sort(chunks, {}, [](Chunk<E> *x) { return x->shdr.sh_addr; });
|
||||
|
||||
// Create a PT_PHDR for the program header itself.
|
||||
if (ctx.phdr && (ctx.phdr->shdr.sh_flags & SHF_ALLOC))
|
||||
@@ -407,47 +405,6 @@ void RelDynSection<E>::update_shdr(Context<E> &ctx) {
|
||||
this->shdr.sh_link = ctx.dynsym->shndx;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void RelDynSection<E>::sort(Context<E> &ctx) {
|
||||
Timer t(ctx, "sort_dynamic_relocs");
|
||||
|
||||
ElfRel<E> *begin = (ElfRel<E> *)(ctx.buf + this->shdr.sh_offset);
|
||||
ElfRel<E> *end = begin + this->shdr.sh_size / sizeof(ElfRel<E>);
|
||||
|
||||
auto get_rank = [](u32 r_type) {
|
||||
if (r_type == E::R_RELATIVE)
|
||||
return 0;
|
||||
if constexpr (supports_ifunc<E>)
|
||||
if (r_type == E::R_IRELATIVE)
|
||||
return 2;
|
||||
return 1;
|
||||
};
|
||||
|
||||
// This is the reason why we sort dynamic relocations. Quote from
|
||||
// https://www.airs.com/blog/archives/186:
|
||||
//
|
||||
// The dynamic linker in glibc uses a one element cache when processing
|
||||
// relocs: if a relocation refers to the same symbol as the previous
|
||||
// relocation, then the dynamic linker reuses the value rather than
|
||||
// looking up the symbol again. Thus the dynamic linker gets the best
|
||||
// results if the dynamic relocations are sorted so that all dynamic
|
||||
// relocations for a given dynamic symbol are adjacent.
|
||||
//
|
||||
// Other than that, the linker sorts together all relative relocations,
|
||||
// which don't have symbols. Two relative relocations, or two relocations
|
||||
// against the same symbol, are sorted by the address in the output
|
||||
// file. This tends to optimize paging and caching when there are two
|
||||
// references from the same page.
|
||||
//
|
||||
// We group IFUNC relocations at the end of .rel.dyn because we want to
|
||||
// apply all the other relocations before running user-supplied ifunc
|
||||
// resolver functions.
|
||||
tbb::parallel_sort(begin, end, [&](const ElfRel<E> &a, const ElfRel<E> &b) {
|
||||
return std::tuple(get_rank(a.r_type), a.r_sym, a.r_offset) <
|
||||
std::tuple(get_rank(b.r_type), b.r_sym, b.r_offset);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void RelrDynSection<E>::update_shdr(Context<E> &ctx) {
|
||||
i64 n = 0;
|
||||
@@ -534,11 +491,10 @@ void ShstrtabSection<E>::copy_buf(Context<E> &ctx) {
|
||||
|
||||
template <typename E>
|
||||
i64 DynstrSection<E>::add_string(std::string_view str) {
|
||||
if (this->shdr.sh_size == 0)
|
||||
if (this->shdr.sh_size == 0) {
|
||||
strings.insert({"", 0});
|
||||
this->shdr.sh_size = 1;
|
||||
|
||||
if (str.empty())
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto [it, inserted] = strings.insert({str, this->shdr.sh_size});
|
||||
if (inserted)
|
||||
@@ -548,9 +504,6 @@ i64 DynstrSection<E>::add_string(std::string_view str) {
|
||||
|
||||
template <typename E>
|
||||
i64 DynstrSection<E>::find_string(std::string_view str) {
|
||||
if (str.empty())
|
||||
return 0;
|
||||
|
||||
auto it = strings.find(str);
|
||||
assert(it != strings.end());
|
||||
return it->second;
|
||||
@@ -559,12 +512,11 @@ i64 DynstrSection<E>::find_string(std::string_view str) {
|
||||
template <typename E>
|
||||
void DynstrSection<E>::copy_buf(Context<E> &ctx) {
|
||||
u8 *base = ctx.buf + this->shdr.sh_offset;
|
||||
base[0] = '\0';
|
||||
|
||||
for (std::pair<std::string_view, i64> p : strings)
|
||||
write_string(base + p.second, p.first);
|
||||
|
||||
i64 off = dynsym_offset;
|
||||
i64 off = ctx.dynsym->dynstr_offset;
|
||||
for (Symbol<E> *sym : ctx.dynsym->symbols)
|
||||
if (sym)
|
||||
off += write_string(base + off, sym->name());
|
||||
@@ -672,7 +624,7 @@ static bool contains_variant_pcs(Context<E> &ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// RISC-V has the same feature but with different names.
|
||||
// RISC-V has the same feature but with a different name.
|
||||
template <is_riscv E>
|
||||
static bool contains_variant_cc(Context<E> &ctx) {
|
||||
for (Symbol<E> *sym : ctx.plt->symbols)
|
||||
@@ -703,6 +655,12 @@ static std::vector<Word<E>> create_dynamic_section(Context<E> &ctx) {
|
||||
for (std::string_view str : ctx.arg.auxiliary)
|
||||
define(DT_AUXILIARY, ctx.dynstr->find_string(str));
|
||||
|
||||
if (!ctx.arg.audit.empty())
|
||||
define(DT_AUDIT, ctx.dynstr->find_string(ctx.arg.audit));
|
||||
|
||||
if (!ctx.arg.depaudit.empty())
|
||||
define(DT_DEPAUDIT, ctx.dynstr->find_string(ctx.arg.depaudit));
|
||||
|
||||
for (std::string_view str : ctx.arg.filter)
|
||||
define(DT_FILTER, ctx.dynstr->find_string(str));
|
||||
|
||||
@@ -842,7 +800,7 @@ static std::vector<Word<E>> create_dynamic_section(Context<E> &ctx) {
|
||||
define(DT_PPC_GOT, ctx.gotplt->shdr.sh_addr);
|
||||
|
||||
if constexpr (is_ppc64<E>) {
|
||||
// PPC64_GLINK is defined by the psABI to refer 32 bytes before
|
||||
// PPC64_GLINK is defined by the psABI to refer to 32 bytes before
|
||||
// the first PLT entry. I don't know why it's 32 bytes off, but
|
||||
// it's what it is.
|
||||
define(DT_PPC64_GLINK, ctx.plt->shdr.sh_addr + to_plt_offset<E>(0) - 32);
|
||||
@@ -857,7 +815,6 @@ static std::vector<Word<E>> create_dynamic_section(Context<E> &ctx) {
|
||||
|
||||
for (i64 i = 0; i < ctx.arg.spare_dynamic_tags; i++)
|
||||
define(DT_NULL, 0);
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
@@ -891,22 +848,15 @@ static std::vector<std::span<T>> split(std::vector<T> &input, i64 unit) {
|
||||
return vec;
|
||||
}
|
||||
|
||||
|
||||
// Assign offsets to OutputSection members
|
||||
template <typename E>
|
||||
void OutputSection<E>::compute_section_size(Context<E> &ctx) {
|
||||
ElfShdr<E> &shdr = this->shdr;
|
||||
|
||||
// On most RISC systems, we need to create so-called "range extension
|
||||
// thunks" to extend branch instructions reach, as their jump
|
||||
// instructions' reach is limited. create_range_extension_thunks()
|
||||
// computes the size of the section while inserting thunks.
|
||||
if constexpr (needs_thunk<E>) {
|
||||
if ((shdr.sh_flags & SHF_EXECINSTR) && !ctx.arg.relocatable) {
|
||||
create_range_extension_thunks(ctx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Text sections must to be handled by create_range_extension_thunks()
|
||||
// if they may need range extension thunks.
|
||||
assert(!needs_thunk<E> || !(shdr.sh_flags & SHF_EXECINSTR) ||
|
||||
ctx.arg.relocatable);
|
||||
|
||||
// Since one output section may contain millions of input sections,
|
||||
// we first split input sections into groups and assign offsets to
|
||||
@@ -914,61 +864,104 @@ void OutputSection<E>::compute_section_size(Context<E> &ctx) {
|
||||
struct Group {
|
||||
std::span<InputSection<E> *> members;
|
||||
i64 size = 0;
|
||||
i64 p2align = 0;
|
||||
i64 offset = 0;
|
||||
i64 align = 1;
|
||||
};
|
||||
|
||||
std::span<InputSection<E> *> mem = members;
|
||||
std::vector<Group> groups;
|
||||
constexpr i64 group_size = 10000;
|
||||
|
||||
while (!mem.empty()) {
|
||||
i64 sz = std::min<i64>(group_size, mem.size());
|
||||
groups.push_back({mem.subspan(0, sz)});
|
||||
mem = mem.subspan(sz);
|
||||
for (std::span<InputSection<E> *> m = members; !m.empty();) {
|
||||
i64 sz = std::min<i64>(group_size, m.size());
|
||||
groups.push_back({m.subspan(0, sz)});
|
||||
m = m.subspan(sz);
|
||||
}
|
||||
|
||||
tbb::parallel_for_each(groups, [](Group &group) {
|
||||
for (InputSection<E> *isec : group.members) {
|
||||
group.size = align_to(group.size, 1 << isec->p2align) + isec->sh_size;
|
||||
group.p2align = std::max<i64>(group.p2align, isec->p2align);
|
||||
tbb::parallel_for_each(groups, [](Group &g) {
|
||||
i64 off = 0;
|
||||
for (InputSection<E> *isec : g.members) {
|
||||
off = align_to(off, 1 << isec->p2align) + isec->sh_size;
|
||||
g.align = std::max<i64>(g.align, 1 << isec->p2align);
|
||||
}
|
||||
g.size = off;
|
||||
});
|
||||
|
||||
shdr.sh_size = 0;
|
||||
|
||||
for (i64 i = 0; i < groups.size(); i++) {
|
||||
shdr.sh_size = align_to(shdr.sh_size, 1 << groups[i].p2align);
|
||||
groups[i].offset = shdr.sh_size;
|
||||
shdr.sh_size += groups[i].size;
|
||||
shdr.sh_addralign = std::max<u32>(shdr.sh_addralign, 1 << groups[i].p2align);
|
||||
i64 off = 0;
|
||||
for (Group &g : groups) {
|
||||
off = align_to(off, g.align);
|
||||
g.offset = off;
|
||||
off += g.size;
|
||||
}
|
||||
|
||||
shdr.sh_size = off;
|
||||
|
||||
// Assign offsets to input sections.
|
||||
tbb::parallel_for_each(groups, [](Group &group) {
|
||||
i64 offset = group.offset;
|
||||
for (InputSection<E> *isec : group.members) {
|
||||
offset = align_to(offset, 1 << isec->p2align);
|
||||
isec->offset = offset;
|
||||
offset += isec->sh_size;
|
||||
tbb::parallel_for_each(groups, [](Group &g) {
|
||||
i64 off = g.offset;
|
||||
for (InputSection<E> *isec : g.members) {
|
||||
off = align_to(off, 1 << isec->p2align);
|
||||
isec->offset = off;
|
||||
off += isec->sh_size;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void OutputSection<E>::copy_buf(Context<E> &ctx) {
|
||||
if (this->shdr.sh_type != SHT_NOBITS) {
|
||||
ElfRel<E> *rel = nullptr;
|
||||
if (ctx.reldyn)
|
||||
rel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
|
||||
this->reldyn_offset);
|
||||
if (this->shdr.sh_type == SHT_NOBITS)
|
||||
return;
|
||||
|
||||
write_to(ctx, ctx.buf + this->shdr.sh_offset, rel);
|
||||
// Copy section contents
|
||||
u8 *buf = ctx.buf + this->shdr.sh_offset;
|
||||
write_to(ctx, buf);
|
||||
|
||||
// Emit dynamic relocations
|
||||
if (!ctx.reldyn)
|
||||
return;
|
||||
|
||||
ElfRel<E> *rel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
|
||||
this->reldyn_offset);
|
||||
|
||||
for (AbsRel<E> &r : abs_rels) {
|
||||
Symbol<E> &sym = *r.sym;
|
||||
u8 *loc = buf + r.isec->offset + r.offset;
|
||||
u64 S = sym.get_addr(ctx);
|
||||
u64 A = r.addend;
|
||||
u64 P = this->shdr.sh_addr + r.isec->offset + r.offset;
|
||||
|
||||
if constexpr (is_riscv<E> || is_loongarch<E>) {
|
||||
i64 delta = get_r_delta(*r.isec, r.offset);
|
||||
loc -= delta;
|
||||
P -= delta;
|
||||
}
|
||||
|
||||
auto write = [&](i64 ty, i64 idx, u64 val) {
|
||||
*rel++ = ElfRel<E>(P, ty, idx, val);
|
||||
if (ctx.arg.apply_dynamic_relocs)
|
||||
*(Word<E> *)loc = val;
|
||||
};
|
||||
|
||||
switch (r.kind) {
|
||||
case ABS_REL_NONE:
|
||||
case ABS_REL_RELR:
|
||||
*(Word<E> *)loc = S + A;
|
||||
break;
|
||||
case ABS_REL_BASEREL:
|
||||
write(E::R_RELATIVE, 0, S + A);
|
||||
break;
|
||||
case ABS_REL_IFUNC:
|
||||
if constexpr (supports_ifunc<E>)
|
||||
write(E::R_IRELATIVE, 0, sym.get_addr(ctx, NO_PLT) + A);
|
||||
break;
|
||||
case ABS_REL_DYNREL:
|
||||
write(E::R_ABS, sym.get_dynsym_idx(ctx), A);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void OutputSection<E>::write_to(Context<E> &ctx, u8 *buf, ElfRel<E> *rel) {
|
||||
void OutputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
|
||||
// Copy section contents to an output file.
|
||||
tbb::parallel_for((i64)0, (i64)members.size(), [&](i64 i) {
|
||||
InputSection<E> &isec = *members[i];
|
||||
@@ -1001,40 +994,6 @@ void OutputSection<E>::write_to(Context<E> &ctx, u8 *buf, ElfRel<E> *rel) {
|
||||
thunk->copy_buf(ctx);
|
||||
});
|
||||
}
|
||||
|
||||
// Emit dynamic relocations.
|
||||
for (AbsRel<E> &r : abs_rels) {
|
||||
Word<E> *loc = (Word<E> *)(buf + r.isec->offset + r.offset);
|
||||
u64 addr = this->shdr.sh_addr + r.isec->offset + r.offset;
|
||||
Symbol<E> &sym = *r.sym;
|
||||
|
||||
switch (r.kind) {
|
||||
case ABS_REL_NONE:
|
||||
case ABS_REL_RELR:
|
||||
*loc = sym.get_addr(ctx) + r.addend;
|
||||
break;
|
||||
case ABS_REL_BASEREL: {
|
||||
u64 val = sym.get_addr(ctx) + r.addend;
|
||||
*rel++ = ElfRel<E>(addr, E::R_RELATIVE, 0, val);
|
||||
if (ctx.arg.apply_dynamic_relocs)
|
||||
*loc = val;
|
||||
break;
|
||||
}
|
||||
case ABS_REL_IFUNC:
|
||||
if constexpr (supports_ifunc<E>) {
|
||||
u64 val = sym.get_addr(ctx, NO_PLT) + r.addend;
|
||||
*rel++ = ElfRel<E>(addr, E::R_IRELATIVE, 0, val);
|
||||
if (ctx.arg.apply_dynamic_relocs)
|
||||
*loc = val;
|
||||
}
|
||||
break;
|
||||
case ABS_REL_DYNREL:
|
||||
*rel++ = ElfRel<E>(addr, E::R_ABS, sym.get_dynsym_idx(ctx), r.addend);
|
||||
if (ctx.arg.apply_dynamic_relocs)
|
||||
*loc = r.addend;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// .relr.dyn contains base relocations encoded in a space-efficient form.
|
||||
@@ -1056,10 +1015,8 @@ void OutputSection<E>::write_to(Context<E> &ctx, u8 *buf, ElfRel<E> *rel) {
|
||||
// the .rel.dyn section). A bitmap has LSB 1.
|
||||
template <typename E>
|
||||
static std::vector<u64> encode_relr(std::span<u64> pos) {
|
||||
for (i64 i = 0; i < pos.size(); i++) {
|
||||
assert(pos[i] % sizeof(Word<E>) == 0);
|
||||
assert(i == 0 || pos[i - 1] < pos[i]);
|
||||
}
|
||||
assert(ranges::all_of(pos, [](u64 x) { return x % sizeof(Word<E>) == 0; }));
|
||||
assert(ranges::is_sorted(pos));
|
||||
|
||||
std::vector<u64> vec;
|
||||
i64 num_bits = E::is_64 ? 63 : 31;
|
||||
@@ -1100,6 +1057,16 @@ static AbsRelKind get_abs_rel_kind(Context<E> &ctx, Symbol<E> &sym) {
|
||||
return ABS_REL_DYNREL;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
static bool is_absrel(const ElfRel<E> &r) {
|
||||
// On ARM32, R_ARM_TARGET1 is typically used for entries in .init_array
|
||||
// and is interpreted as either ABS32 or REL32 depending on the target.
|
||||
// All targets we support handle it as if it were a ABS32.
|
||||
if constexpr (is_arm32<E>)
|
||||
return r.r_type == E::R_ABS || r.r_type == R_ARM_TARGET1;
|
||||
return r.r_type == E::R_ABS;
|
||||
}
|
||||
|
||||
// Scan word-size absolute relocations (e.g. R_X86_64_64). This is
|
||||
// separated from scan_relocations() because only such relocations can
|
||||
// be promoted to dynamic relocations.
|
||||
@@ -1111,7 +1078,7 @@ void OutputSection<E>::scan_abs_relocations(Context<E> &ctx) {
|
||||
tbb::parallel_for((i64)0, (i64)members.size(), [&](i64 i) {
|
||||
InputSection<E> *isec = members[i];
|
||||
for (const ElfRel<E> &r : isec->get_rels(ctx))
|
||||
if (r.r_type == E::R_ABS)
|
||||
if (is_absrel(r))
|
||||
shards[i].push_back(AbsRel<E>{isec, r.r_offset, isec->file.symbols[r.r_sym],
|
||||
get_addend(*isec, r)});
|
||||
});
|
||||
@@ -1149,7 +1116,7 @@ void OutputSection<E>::scan_abs_relocations(Context<E> &ctx) {
|
||||
|
||||
// If --pack-dyn-relocs=relr is enabled, base relocations are put into
|
||||
// .relr.dyn.
|
||||
if (ctx.arg.pack_dyn_relocs_relr)
|
||||
if (ctx.arg.pack_dyn_relocs_relr && !(this->shdr.sh_flags & SHF_EXECINSTR))
|
||||
for (AbsRel<E> &r : abs_rels)
|
||||
if (r.kind == ABS_REL_BASEREL &&
|
||||
r.isec->shdr().sh_addralign % sizeof(Word<E>) == 0 &&
|
||||
@@ -1192,7 +1159,7 @@ void OutputSection<E>::compute_symtab_size(Context<E> &ctx) {
|
||||
this->num_local_symtab += thunk->symbols.size();
|
||||
|
||||
for (Symbol<E> *sym : thunk->symbols)
|
||||
this->strtab_size += sym->name().size() + sizeof("$thunk");
|
||||
this->strtab_size += sym->name().size() + thunk->name.size() + 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1226,7 +1193,8 @@ void OutputSection<E>::populate_symtab(Context<E> &ctx) {
|
||||
write_esym(addr, strtab - strtab_base);
|
||||
|
||||
strtab += write_string(strtab, sym.name()) - 1;
|
||||
strtab += write_string(strtab, "$thunk");
|
||||
*strtab++ = '$';
|
||||
strtab += write_string(strtab, thunk->name);
|
||||
|
||||
// Emit "$t", "$a" and "$d" if ARM32.
|
||||
if constexpr (is_arm32<E>) {
|
||||
@@ -1334,12 +1302,6 @@ static std::vector<GotEntry<E>> get_got_entries(Context<E> &ctx) {
|
||||
for (Symbol<E> *sym : ctx.got->got_syms) {
|
||||
i64 idx = sym->get_got_idx(ctx);
|
||||
|
||||
// If a symbol is imported, let the dynamic linker to resolve it.
|
||||
if (sym->is_imported) {
|
||||
add({idx, 0, E::R_GLOB_DAT, sym});
|
||||
continue;
|
||||
}
|
||||
|
||||
// IFUNC always needs to be fixed up by the dynamic linker.
|
||||
if constexpr (supports_ifunc<E>) {
|
||||
if (sym->is_ifunc()) {
|
||||
@@ -1353,6 +1315,12 @@ static std::vector<GotEntry<E>> get_got_entries(Context<E> &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
// If a symbol is imported, let the dynamic linker to resolve it.
|
||||
if (sym->is_imported) {
|
||||
add({idx, 0, E::R_GLOB_DAT, sym});
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we know an address at link-time, fill that GOT entry now.
|
||||
// It may need a base relocation, though.
|
||||
if (ctx.arg.pic && sym->is_relative())
|
||||
@@ -1446,7 +1414,7 @@ void GotSection<E>::copy_buf(Context<E> &ctx) {
|
||||
if (ctx.dynamic)
|
||||
buf[0] = ctx.dynamic->shdr.sh_addr;
|
||||
|
||||
// arm64 psABI doesn't say anything about GOT[0], but glibc/arm64's code
|
||||
// ARM64 psABI doesn't say anything about GOT[0], but glibc/arm64's code
|
||||
// path for -static-pie wrongly assumed that GOT[0] refers to _DYNAMIC.
|
||||
//
|
||||
// https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=43d06ed218fc8be5
|
||||
@@ -1468,24 +1436,20 @@ void GotSection<E>::copy_buf(Context<E> &ctx) {
|
||||
ent.sym ? ent.sym->get_dynsym_idx(ctx) : 0,
|
||||
ent.val);
|
||||
|
||||
bool is_tlsdesc = false;
|
||||
if constexpr (supports_tlsdesc<E>)
|
||||
is_tlsdesc = (ent.r_type == E::R_TLSDESC);
|
||||
|
||||
if (ctx.arg.apply_dynamic_relocs) {
|
||||
if (is_tlsdesc && !is_arm32<E>) {
|
||||
// A single TLSDESC relocation fixes two consecutive GOT slots
|
||||
// where one slot holds a function pointer and the other an
|
||||
// argument to the function. An addend should be applied not to
|
||||
// the function pointer but to the function argument, which is
|
||||
// usually stored to the second slot.
|
||||
//
|
||||
// ARM32 employs the inverted layout for some reason, so an
|
||||
// addend is applied to the first slot.
|
||||
buf[ent.idx + 1] = ent.val;
|
||||
} else {
|
||||
buf[ent.idx] = ent.val;
|
||||
}
|
||||
// A single TLSDESC relocation fixes two consecutive GOT slots
|
||||
// where one slot holds a function pointer and the other an
|
||||
// argument to the function. An addend should be applied not to
|
||||
// the function pointer but to the function argument, which is
|
||||
// usually stored to the second slot.
|
||||
//
|
||||
// ARM32 employs the inverted layout for some reason, so an
|
||||
// addend is applied to the first slot.
|
||||
i64 i = ent.idx;
|
||||
if constexpr (supports_tlsdesc<E> && !is_arm32<E>)
|
||||
if (ent.r_type == E::R_TLSDESC)
|
||||
i = ent.idx + 1;
|
||||
buf[i] = ent.val;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1647,6 +1611,7 @@ void PltSection<E>::populate_symtab(Context<E> &ctx) {
|
||||
memset(esym, 0, sizeof(*esym));
|
||||
esym->st_name = st_name;
|
||||
esym->st_type = STT_FUNC;
|
||||
esym->st_bind = STB_LOCAL;
|
||||
esym->st_shndx = this->shndx;
|
||||
esym->st_value = addr;
|
||||
esym++;
|
||||
@@ -1757,6 +1722,21 @@ void RelPltSection<E>::copy_buf(Context<E> &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
// RISC-V and LoongArch have code-shrinking linker relaxation. If we
|
||||
// have removed instructions from a function, we need to update its
|
||||
// size as well.
|
||||
template <typename E>
|
||||
static u64 get_symbol_size(Symbol<E> &sym) {
|
||||
const ElfSym<E> &esym = sym.esym();
|
||||
if constexpr (is_riscv<E> || is_loongarch<E>)
|
||||
if (esym.st_size)
|
||||
if (InputSection<E> *isec = sym.get_input_section())
|
||||
if (isec->shdr().sh_flags & SHF_EXECINSTR)
|
||||
return esym.st_size + esym.st_value - sym.value -
|
||||
get_r_delta(*isec, esym.st_value + esym.st_size);
|
||||
return esym.st_size;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
ElfSym<E> to_output_esym(Context<E> &ctx, Symbol<E> &sym, u32 st_name,
|
||||
U32<E> *shn_xindex) {
|
||||
@@ -1765,7 +1745,7 @@ ElfSym<E> to_output_esym(Context<E> &ctx, Symbol<E> &sym, u32 st_name,
|
||||
|
||||
esym.st_name = st_name;
|
||||
esym.st_type = sym.get_type();
|
||||
esym.st_size = sym.esym().st_size;
|
||||
esym.st_size = get_symbol_size(sym);
|
||||
|
||||
if (sym.is_local(ctx))
|
||||
esym.st_bind = STB_LOCAL;
|
||||
@@ -1896,14 +1876,14 @@ void DynsymSection<E>::update_shdr(Context<E> &ctx) {
|
||||
template <typename E>
|
||||
void DynsymSection<E>::copy_buf(Context<E> &ctx) {
|
||||
ElfSym<E> *buf = (ElfSym<E> *)(ctx.buf + this->shdr.sh_offset);
|
||||
i64 name_offset = ctx.dynstr->dynsym_offset;
|
||||
i64 offset = dynstr_offset;
|
||||
|
||||
memset(buf, 0, sizeof(ElfSym<E>));
|
||||
|
||||
for (i64 i = 1; i < symbols.size(); i++) {
|
||||
Symbol<E> &sym = *symbols[i];
|
||||
buf[sym.get_dynsym_idx(ctx)] = to_output_esym(ctx, sym, name_offset, nullptr);
|
||||
name_offset += sym.name().size() + 1;
|
||||
buf[sym.get_dynsym_idx(ctx)] = to_output_esym(ctx, sym, offset, nullptr);
|
||||
offset += sym.name().size() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1962,18 +1942,20 @@ void GnuHashSection<E>::copy_buf(Context<E> &ctx) {
|
||||
|
||||
i64 first_exported = ctx.dynsym->symbols.size() - num_exported;
|
||||
|
||||
std::span<Symbol<E> *> syms = ctx.dynsym->symbols;
|
||||
syms = syms.subspan(first_exported);
|
||||
|
||||
std::vector<u32> indices(num_exported);
|
||||
|
||||
*(U32<E> *)base = num_buckets;
|
||||
*(U32<E> *)(base + 4) = first_exported;
|
||||
*(U32<E> *)(base + 8) = num_bloom;
|
||||
*(U32<E> *)(base + 12) = BLOOM_SHIFT;
|
||||
|
||||
std::span<Symbol<E> *> syms = ctx.dynsym->symbols;
|
||||
syms = syms.subspan(first_exported);
|
||||
|
||||
if (syms.empty())
|
||||
return;
|
||||
|
||||
// Write a bloom filter
|
||||
Word<E> *bloom = (Word<E> *)(base + HEADER_SIZE);
|
||||
std::vector<u32> indices(num_exported);
|
||||
|
||||
for (i64 i = 0; i < syms.size(); i++) {
|
||||
constexpr i64 word_bits = sizeof(Word<E>) * 8;
|
||||
@@ -1989,9 +1971,8 @@ void GnuHashSection<E>::copy_buf(Context<E> &ctx) {
|
||||
// Write hash bucket indices
|
||||
U32<E> *buckets = (U32<E> *)(bloom + num_bloom);
|
||||
|
||||
for (i64 i = 0; i < syms.size(); i++)
|
||||
if (!buckets[indices[i]])
|
||||
buckets[indices[i]] = i + first_exported;
|
||||
for (i64 i = syms.size() - 1; i >= 0; i--)
|
||||
buckets[indices[i]] = first_exported + i;
|
||||
|
||||
// Write a hash table
|
||||
U32<E> *table = buckets + num_buckets;
|
||||
@@ -2016,14 +1997,22 @@ get_merged_output_name(Context<E> &ctx, std::string_view name, u64 flags,
|
||||
if (ctx.arg.unique && ctx.arg.unique->match(name))
|
||||
return name;
|
||||
|
||||
// GCC seems to create sections named ".rodata.strN.<mangled-symbol-name>.M".
|
||||
// We want to eliminate the symbol name part from the section name.
|
||||
if ((flags & SHF_STRINGS) && name.starts_with(".rodata.")) {
|
||||
std::string name2 = ".rodata.str"s + std::to_string(entsize) +
|
||||
"." + std::to_string(addralign);
|
||||
if (name == name2)
|
||||
return name;
|
||||
return save_string(ctx, name2);
|
||||
// GCC seems to create sections named ".rodata.strN.<mangled-symbol-name>.M"
|
||||
// or ".rodata.cst.<mangled-symbol-name.cstN". We want to eliminate the
|
||||
// symbol name part from the section name.
|
||||
if (name.starts_with(".rodata.")) {
|
||||
if (flags & SHF_STRINGS) {
|
||||
std::string name2 = ".rodata.str"s + std::to_string(entsize) +
|
||||
"." + std::to_string(addralign);
|
||||
if (name == name2)
|
||||
return name;
|
||||
return save_string(ctx, name2);
|
||||
} else {
|
||||
std::string name2 = ".rodata.cst"s + std::to_string(entsize);
|
||||
if (name == name2)
|
||||
return name;
|
||||
return save_string(ctx, name2);
|
||||
}
|
||||
}
|
||||
|
||||
return name;
|
||||
@@ -2140,6 +2129,13 @@ void MergedSection<E>::resolve(Context<E> &ctx) {
|
||||
|
||||
if (this == ctx.comment)
|
||||
add_comment_strings(ctx);
|
||||
|
||||
// Compute section alignment
|
||||
u32 p2align = 0;
|
||||
for (MergeableSection<E> *sec : members)
|
||||
p2align = std::max<u32>(p2align, sec->p2align);
|
||||
this->shdr.sh_addralign = 1 << p2align;
|
||||
|
||||
resolved = true;
|
||||
}
|
||||
|
||||
@@ -2149,14 +2145,11 @@ void MergedSection<E>::compute_section_size(Context<E> &ctx) {
|
||||
resolve(ctx);
|
||||
|
||||
std::vector<i64> sizes(map.NUM_SHARDS);
|
||||
Atomic<i64> alignment = 1;
|
||||
|
||||
tbb::parallel_for((i64)0, map.NUM_SHARDS, [&](i64 i) {
|
||||
using Entry = typename decltype(map)::Entry;
|
||||
std::vector<Entry *> entries = map.get_sorted_entries(i);
|
||||
|
||||
i64 offset = 0;
|
||||
i64 p2align = 0;
|
||||
|
||||
for (Entry *ent : entries) {
|
||||
SectionFragment<E> &frag = ent->value;
|
||||
@@ -2164,15 +2157,9 @@ void MergedSection<E>::compute_section_size(Context<E> &ctx) {
|
||||
offset = align_to(offset, 1 << frag.p2align);
|
||||
frag.offset = offset;
|
||||
offset += ent->keylen;
|
||||
p2align = std::max<i64>(p2align, frag.p2align);
|
||||
}
|
||||
}
|
||||
|
||||
sizes[i] = offset;
|
||||
update_maximum(alignment, 1 << p2align);
|
||||
|
||||
static Counter merged_strings("merged_strings");
|
||||
merged_strings += entries.size();
|
||||
});
|
||||
|
||||
i64 shard_size = map.nbuckets / map.NUM_SHARDS;
|
||||
@@ -2180,7 +2167,7 @@ void MergedSection<E>::compute_section_size(Context<E> &ctx) {
|
||||
|
||||
for (i64 i = 1; i < map.NUM_SHARDS + 1; i++)
|
||||
shard_offsets[i] =
|
||||
align_to(shard_offsets[i - 1] + sizes[i - 1], alignment);
|
||||
align_to(shard_offsets[i - 1] + sizes[i - 1], this->shdr.sh_addralign);
|
||||
|
||||
tbb::parallel_for((i64)1, map.NUM_SHARDS, [&](i64 i) {
|
||||
for (i64 j = shard_size * i; j < shard_size * (i + 1); j++) {
|
||||
@@ -2191,7 +2178,6 @@ void MergedSection<E>::compute_section_size(Context<E> &ctx) {
|
||||
});
|
||||
|
||||
this->shdr.sh_size = shard_offsets[map.NUM_SHARDS];
|
||||
this->shdr.sh_addralign = alignment;
|
||||
|
||||
if (this->shdr.sh_size > UINT32_MAX)
|
||||
Fatal(ctx) << this->name << ": output section too large";
|
||||
@@ -2199,17 +2185,18 @@ void MergedSection<E>::compute_section_size(Context<E> &ctx) {
|
||||
|
||||
template <typename E>
|
||||
void MergedSection<E>::copy_buf(Context<E> &ctx) {
|
||||
write_to(ctx, ctx.buf + this->shdr.sh_offset, nullptr);
|
||||
write_to(ctx, ctx.buf + this->shdr.sh_offset);
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void MergedSection<E>::write_to(Context<E> &ctx, u8 *buf, ElfRel<E> *rel) {
|
||||
void MergedSection<E>::write_to(Context<E> &ctx, u8 *buf) {
|
||||
i64 shard_size = map.nbuckets / map.NUM_SHARDS;
|
||||
|
||||
tbb::parallel_for((i64)0, map.NUM_SHARDS, [&](i64 i) {
|
||||
// There might be gaps between strings to satisfy alignment requirements.
|
||||
// If that's the case, we need to zero-clear them.
|
||||
if (this->shdr.sh_addralign > 1)
|
||||
if (this->shdr.sh_addralign > 1 &&
|
||||
this->shdr.sh_addralign != this->shdr.sh_entsize)
|
||||
memset(buf + shard_offsets[i], 0, shard_offsets[i + 1] - shard_offsets[i]);
|
||||
|
||||
// Copy strings
|
||||
@@ -2238,7 +2225,7 @@ void EhFrameSection<E>::construct(Context<E> &ctx) {
|
||||
|
||||
// Remove dead FDEs and assign them offsets within their corresponding
|
||||
// CIE group.
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
|
||||
std::erase_if(file->fdes, [](FdeRecord<E> &fde) { return !fde.is_alive; });
|
||||
|
||||
i64 offset = 0;
|
||||
@@ -2299,7 +2286,7 @@ void EhFrameSection<E>::copy_buf(Context<E> &ctx) {
|
||||
HdrEntry *eh_hdr = nullptr;
|
||||
if (ctx.eh_frame_hdr)
|
||||
eh_hdr = (HdrEntry *)(ctx.buf + ctx.eh_frame_hdr->shdr.sh_offset +
|
||||
EhFrameHdrSection<E>::HEADER_SIZE);
|
||||
EhFrameHdrSection<E>::HEADER_SIZE);
|
||||
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
// Copy CIEs.
|
||||
@@ -2338,24 +2325,22 @@ void EhFrameSection<E>::copy_buf(Context<E> &ctx) {
|
||||
if (ctx.arg.relocatable)
|
||||
continue;
|
||||
|
||||
for (const ElfRel<E> &rel : rels) {
|
||||
for (i64 j = 0; j < rels.size(); j++) {
|
||||
const ElfRel<E> &rel = rels[j];
|
||||
assert(rel.r_offset - fde.input_offset < contents.size());
|
||||
|
||||
Symbol<E> &sym = *file->symbols[rel.r_sym];
|
||||
u64 loc = offset + rel.r_offset - fde.input_offset;
|
||||
u64 val = sym.get_addr(ctx) + get_addend(cie.input_section, rel);
|
||||
apply_eh_reloc(ctx, rel, loc, val);
|
||||
}
|
||||
|
||||
if (eh_hdr) {
|
||||
// Write to .eh_frame_hdr
|
||||
Symbol<E> &sym = *file->symbols[rels[0].r_sym];
|
||||
u64 val = sym.get_addr(ctx) + get_addend(cie.input_section, rels[0]);
|
||||
u64 sh_addr = ctx.eh_frame_hdr->shdr.sh_addr;
|
||||
|
||||
HdrEntry &ent = eh_hdr[file->fde_idx + i];
|
||||
ent.init_addr = val - sh_addr;
|
||||
ent.fde_addr = this->shdr.sh_addr + offset - sh_addr;
|
||||
if (j == 0 && eh_hdr) {
|
||||
// Write to .eh_frame_hdr
|
||||
HdrEntry &ent = eh_hdr[file->fde_idx + i];
|
||||
u64 origin = ctx.eh_frame_hdr->shdr.sh_addr;
|
||||
ent.init_addr = val - origin;
|
||||
ent.fde_addr = this->shdr.sh_addr + offset - origin;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -2384,7 +2369,7 @@ template <typename E>
|
||||
void EhFrameHdrSection<E>::copy_buf(Context<E> &ctx) {
|
||||
u8 *base = ctx.buf + this->shdr.sh_offset;
|
||||
|
||||
// Write a header. The actual table is written by EhFrameHdr<E>::copy_buf.
|
||||
// Write a header. The actual table is written by EhFrameSection::copy_buf.
|
||||
base[0] = 1;
|
||||
base[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4;
|
||||
base[2] = DW_EH_PE_udata4;
|
||||
@@ -2543,13 +2528,12 @@ void VersymSection<E>::copy_buf(Context<E> &ctx) {
|
||||
// `GLIBC_ABI_DT_RELR' not found" error message. glibc 2.38 or later knows
|
||||
// about this dummy version name and simply ignores it.
|
||||
template <typename E>
|
||||
static InputFile<E> *find_glibc2(Context<E> &ctx) {
|
||||
for (Symbol<E> *sym : ctx.dynsym->symbols)
|
||||
if (sym && sym->file->is_dso &&
|
||||
((SharedFile<E> *)sym->file)->soname.starts_with("libc.so.") &&
|
||||
sym->get_version().starts_with("GLIBC_2."))
|
||||
return sym->file;
|
||||
return nullptr;
|
||||
static bool is_glibc2(SharedFile<E> &file) {
|
||||
if (file.soname.starts_with("libc.so."))
|
||||
for (std::string_view str : file.version_strings)
|
||||
if (str.starts_with("GLIBC_2."))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -2568,9 +2552,8 @@ void VerneedSection<E>::construct(Context<E> &ctx) {
|
||||
if (syms.empty())
|
||||
return;
|
||||
|
||||
sort(syms, [](Symbol<E> *a, Symbol<E> *b) {
|
||||
return std::tuple(((SharedFile<E> *)a->file)->soname, a->ver_idx) <
|
||||
std::tuple(((SharedFile<E> *)b->file)->soname, b->ver_idx);
|
||||
ranges::stable_sort(syms, {}, [](Symbol<E> *x) {
|
||||
return std::tuple{((SharedFile<E> *)x->file)->soname, x->ver_idx};
|
||||
});
|
||||
|
||||
// Resize .gnu.version
|
||||
@@ -2589,19 +2572,6 @@ void VerneedSection<E>::construct(Context<E> &ctx) {
|
||||
|
||||
i64 veridx = VER_NDX_LAST_RESERVED + ctx.arg.version_definitions.size();
|
||||
|
||||
auto start_group = [&](InputFile<E> *file) {
|
||||
this->shdr.sh_info++;
|
||||
if (verneed)
|
||||
verneed->vn_next = ptr - (u8 *)verneed;
|
||||
|
||||
verneed = (ElfVerneed<E> *)ptr;
|
||||
ptr += sizeof(ElfVerneed<E>);
|
||||
verneed->vn_version = 1;
|
||||
verneed->vn_file = ctx.dynstr->find_string(((SharedFile<E> *)file)->soname);
|
||||
verneed->vn_aux = sizeof(ElfVerneed<E>);
|
||||
aux = nullptr;
|
||||
};
|
||||
|
||||
auto add_entry = [&](std::string_view verstr) {
|
||||
verneed->vn_cnt++;
|
||||
|
||||
@@ -2615,25 +2585,33 @@ void VerneedSection<E>::construct(Context<E> &ctx) {
|
||||
aux->vna_name = ctx.dynstr->add_string(verstr);
|
||||
};
|
||||
|
||||
auto start_group = [&](SharedFile<E> &file) {
|
||||
this->shdr.sh_info++;
|
||||
if (verneed)
|
||||
verneed->vn_next = ptr - (u8 *)verneed;
|
||||
|
||||
verneed = (ElfVerneed<E> *)ptr;
|
||||
ptr += sizeof(ElfVerneed<E>);
|
||||
verneed->vn_version = 1;
|
||||
verneed->vn_file = ctx.dynstr->find_string(file.soname);
|
||||
verneed->vn_aux = sizeof(ElfVerneed<E>);
|
||||
aux = nullptr;
|
||||
|
||||
if (ctx.arg.pack_dyn_relocs_relr && is_glibc2(file))
|
||||
add_entry("GLIBC_ABI_DT_RELR");
|
||||
};
|
||||
|
||||
// Create version entries.
|
||||
for (i64 i = 0; i < syms.size(); i++) {
|
||||
if (i == 0 || syms[i - 1]->file != syms[i]->file) {
|
||||
start_group(syms[i]->file);
|
||||
start_group(*(SharedFile<E> *)syms[i]->file);
|
||||
add_entry(syms[i]->get_version());
|
||||
} else if (syms[i - 1]->ver_idx != syms[i]->ver_idx) {
|
||||
add_entry(syms[i]->get_version());
|
||||
}
|
||||
|
||||
ctx.versym->contents[syms[i]->get_dynsym_idx(ctx)] = veridx;
|
||||
}
|
||||
|
||||
if (ctx.arg.pack_dyn_relocs_relr) {
|
||||
if (InputFile<E> *file = find_glibc2(ctx)) {
|
||||
start_group(file);
|
||||
add_entry("GLIBC_ABI_DT_RELR");
|
||||
}
|
||||
}
|
||||
|
||||
// Resize .gnu.version_r to fit to its contents.
|
||||
contents.resize(ptr - buf);
|
||||
}
|
||||
@@ -2660,10 +2638,9 @@ void VerdefSection<E>::construct(Context<E> &ctx) {
|
||||
ctx.versym->contents.resize(ctx.dynsym->symbols.size(), VER_NDX_GLOBAL);
|
||||
ctx.versym->contents[0] = VER_NDX_LOCAL;
|
||||
|
||||
for (i64 i = 1; i < ctx.dynsym->symbols.size(); i++)
|
||||
if (Symbol<E> &sym = *ctx.dynsym->symbols[i];
|
||||
!sym.file->is_dso && sym.ver_idx != VER_NDX_UNSPECIFIED)
|
||||
ctx.versym->contents[sym.get_dynsym_idx(ctx)] = sym.ver_idx;
|
||||
for (Symbol<E> *sym : ctx.dynsym->symbols)
|
||||
if (sym && !sym->file->is_dso && sym->ver_idx != VER_NDX_UNSPECIFIED)
|
||||
ctx.versym->contents[sym->get_dynsym_idx(ctx)] = sym->ver_idx;
|
||||
|
||||
// Allocate a buffer for .gnu.version_d and write to it
|
||||
contents.resize((sizeof(ElfVerdef<E>) + sizeof(ElfVerdaux<E>)) *
|
||||
@@ -2692,10 +2669,10 @@ void VerdefSection<E>::construct(Context<E> &ctx) {
|
||||
aux->vda_name = ctx.dynstr->add_string(verstr);
|
||||
};
|
||||
|
||||
if (!ctx.arg.soname.empty())
|
||||
write(ctx.arg.soname, 1, VER_FLG_BASE);
|
||||
else
|
||||
write(ctx.arg.output, 1, VER_FLG_BASE);
|
||||
std::string_view soname = ctx.arg.soname;
|
||||
if (soname.empty())
|
||||
soname = save_string(ctx, path_filename(ctx.arg.output));
|
||||
write(soname, 1, VER_FLG_BASE);
|
||||
|
||||
i64 idx = VER_NDX_LAST_RESERVED + 1;
|
||||
for (std::string_view verstr : ctx.arg.version_definitions)
|
||||
@@ -2773,10 +2750,6 @@ void NotePropertySection<E>::update_shdr(Context<E> &ctx) {
|
||||
std::map<u32, u32> map;
|
||||
|
||||
for (u32 key : keys) {
|
||||
auto has_key = [&](ObjectFile<E> *file) {
|
||||
return file->gnu_properties.contains(key);
|
||||
};
|
||||
|
||||
if (GNU_PROPERTY_X86_UINT32_AND_LO <= key &&
|
||||
key <= GNU_PROPERTY_X86_UINT32_AND_HI) {
|
||||
// An AND feature is set if all input objects have the property and
|
||||
@@ -2793,7 +2766,11 @@ void NotePropertySection<E>::update_shdr(Context<E> &ctx) {
|
||||
key <= GNU_PROPERTY_X86_UINT32_OR_AND_HI) {
|
||||
// An OR-AND feature is set if all input object files have the property
|
||||
// and some of them has the feature.
|
||||
if (std::all_of(files.begin(), files.end(), has_key))
|
||||
auto has_key = [&](ObjectFile<E> *file) {
|
||||
return file->gnu_properties.contains(key);
|
||||
};
|
||||
|
||||
if (ranges::all_of(files, has_key))
|
||||
for (ObjectFile<E> *file : files)
|
||||
map[key] |= get_value(file, key);
|
||||
}
|
||||
@@ -2839,7 +2816,7 @@ CompressedSection<E>::CompressedSection(Context<E> &ctx, Chunk<E> &chunk) {
|
||||
this->uncompressed_data.resize(chunk.shdr.sh_size);
|
||||
u8 *buf = this->uncompressed_data.data();
|
||||
|
||||
chunk.write_to(ctx, buf, nullptr);
|
||||
chunk.write_to(ctx, buf);
|
||||
|
||||
switch (ctx.arg.compress_debug_sections) {
|
||||
case COMPRESS_ZLIB:
|
||||
@@ -2995,7 +2972,7 @@ void ComdatGroupSection<E>::copy_buf(Context<E> &ctx) {
|
||||
|
||||
template <typename E>
|
||||
void GnuDebuglinkSection<E>::update_shdr(Context<E> &ctx) {
|
||||
filename = std::filesystem::path(ctx.arg.separate_debug_file).filename().string();
|
||||
filename = path_filename(ctx.arg.separate_debug_file);
|
||||
this->shdr.sh_size = align_to(filename.size() + 1, 4) + 4;
|
||||
}
|
||||
|
||||
|
||||
+4
-10
@@ -4,8 +4,7 @@
|
||||
#include <filesystem>
|
||||
#include <sys/file.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <system_error>
|
||||
|
||||
namespace mold {
|
||||
|
||||
@@ -111,14 +110,9 @@ OutputFile<E>::open(Context<E> &ctx, std::string path, i64 filesize, int perm) {
|
||||
if (path.starts_with('/') && !ctx.arg.chroot.empty())
|
||||
path = ctx.arg.chroot + "/" + path_clean(path);
|
||||
|
||||
bool is_special = false;
|
||||
if (path == "-") {
|
||||
is_special = true;
|
||||
} else {
|
||||
struct stat st;
|
||||
if (stat(path.c_str(), &st) == 0 && (st.st_mode & S_IFMT) != S_IFREG)
|
||||
is_special = true;
|
||||
}
|
||||
std::error_code error;
|
||||
bool is_special = path == "-" ||
|
||||
(!std::filesystem::is_regular_file(path, error) && !error);
|
||||
|
||||
OutputFile<E> *file;
|
||||
if (is_special)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "mold.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <filesystem>
|
||||
#include <windows.h>
|
||||
|
||||
namespace mold {
|
||||
|
||||
+314
-180
@@ -3,13 +3,12 @@
|
||||
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <regex>
|
||||
#include <shared_mutex>
|
||||
#include <tbb/enumerable_thread_specific.h>
|
||||
#include <tbb/parallel_for_each.h>
|
||||
#include <tbb/parallel_sort.h>
|
||||
#include <tbb/partitioner.h>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace mold {
|
||||
@@ -57,7 +56,9 @@ int redo_main(Context<E> &ctx, int argc, char **argv) {
|
||||
return mold_main<LOONGARCH32>(argc, argv);
|
||||
if (target == LOONGARCH64::name)
|
||||
return mold_main<LOONGARCH64>(argc, argv);
|
||||
unreachable();
|
||||
if (target == ARC::name)
|
||||
return mold_main<ARC>(argc, argv);
|
||||
abort();
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -67,17 +68,11 @@ void apply_exclude_libs(Context<E> &ctx) {
|
||||
std::unordered_set<std::string_view> set(ctx.arg.exclude_libs.begin(),
|
||||
ctx.arg.exclude_libs.end());
|
||||
|
||||
if (set.contains("ALL")) {
|
||||
if (!set.empty())
|
||||
for (ObjectFile<E> *file : ctx.objs)
|
||||
if (!file->archive_name.empty())
|
||||
file->exclude_libs = true;
|
||||
return;
|
||||
}
|
||||
|
||||
for (ObjectFile<E> *file : ctx.objs)
|
||||
if (!file->archive_name.empty())
|
||||
if (set.contains(path_filename(file->archive_name)))
|
||||
file->exclude_libs = true;
|
||||
if (set.contains(path_filename(file->archive_name)) || set.contains("ALL"))
|
||||
file->exclude_libs = true;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -169,7 +164,7 @@ void create_synthetic_sections(Context<E> &ctx) {
|
||||
|
||||
// If .dynamic exists, .dynsym and .dynstr must exist as well
|
||||
// since .dynamic refers to them.
|
||||
ctx.dynstr->shdr.sh_size = 1;
|
||||
ctx.dynstr->add_string("");
|
||||
ctx.dynsym->symbols.resize(1);
|
||||
}
|
||||
|
||||
@@ -201,18 +196,18 @@ template <typename E>
|
||||
static void mark_live_objects(Context<E> &ctx) {
|
||||
for (Symbol<E> *sym : ctx.arg.undefined)
|
||||
if (sym->file)
|
||||
sym->file->is_alive = true;
|
||||
sym->file->is_reachable = true;
|
||||
|
||||
for (Symbol<E> *sym : ctx.arg.require_defined)
|
||||
if (sym->file)
|
||||
sym->file->is_alive = true;
|
||||
sym->file->is_reachable = true;
|
||||
|
||||
if (!ctx.arg.undefined_glob.empty()) {
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
if (!file->is_alive) {
|
||||
if (!file->is_reachable) {
|
||||
for (Symbol<E> *sym : file->get_global_syms()) {
|
||||
if (sym->file == file && ctx.arg.undefined_glob.find(sym->name())) {
|
||||
file->is_alive = true;
|
||||
file->is_reachable = true;
|
||||
sym->gc_root = true;
|
||||
break;
|
||||
}
|
||||
@@ -224,11 +219,11 @@ static void mark_live_objects(Context<E> &ctx) {
|
||||
std::vector<InputFile<E> *> roots;
|
||||
|
||||
for (InputFile<E> *file : ctx.objs)
|
||||
if (file->is_alive)
|
||||
if (file->is_reachable)
|
||||
roots.push_back(file);
|
||||
|
||||
for (InputFile<E> *file : ctx.dsos)
|
||||
if (file->is_alive)
|
||||
if (file->is_reachable)
|
||||
roots.push_back(file);
|
||||
|
||||
tbb::parallel_for_each(roots, [&](InputFile<E> *file,
|
||||
@@ -237,6 +232,45 @@ static void mark_live_objects(Context<E> &ctx) {
|
||||
});
|
||||
}
|
||||
|
||||
// Symbol resolution involving a default symbol version is tricky because
|
||||
// a symbol that provides the default version has two names by which it
|
||||
// can be referred. Specifically, a symbol `foo` with the default version
|
||||
// `VER1` can be referred to either as `foo` or `foo@VER1`. No other
|
||||
// symbols have two names like that.
|
||||
//
|
||||
// By default, we insert symbols with a default version without an at-sign
|
||||
// (i.e. `foo` instead of `foo@VER1`) into our internal symbol table.
|
||||
// Therefore, if the symbol is referenced with an at-sign (i.e.
|
||||
// `foo@VER1`), the reference fails to resolve. This function corrects
|
||||
// that error.
|
||||
//
|
||||
// In this function, we check all unresolved versioned symbols of the form
|
||||
// `foo@VER1` by removing the version part and see if `foo` has version
|
||||
// `VER1`. If it does, that's the symbol we are looking for.
|
||||
template <typename E>
|
||||
static void resolve_default_symver(Context<E> &ctx) {
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
for (i64 i = file->first_global; i < file->elf_syms.size(); i++) {
|
||||
const ElfSym<E> &esym = file->elf_syms[i];
|
||||
Symbol<E> &sym = *file->symbols[i];
|
||||
|
||||
if (!sym.file && esym.is_undef() &&
|
||||
file->has_symver[i - file->first_global]) {
|
||||
std::string_view str = file->symbol_strtab.data() + esym.st_name;
|
||||
i64 pos = str.find('@');
|
||||
assert(pos != str.npos);
|
||||
|
||||
std::string_view name = str.substr(0, pos);
|
||||
std::string_view ver = str.substr(pos + 1);
|
||||
|
||||
Symbol<E> *sym2 = get_symbol(ctx, name);
|
||||
if (sym2->file && sym2->file->is_dso && sym2->get_version() == ver)
|
||||
file->symbols[i] = sym2;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
static void clear_symbols(Context<E> &ctx) {
|
||||
std::vector<InputFile<E> *> files;
|
||||
@@ -275,6 +309,7 @@ void resolve_symbols(Context<E> &ctx) {
|
||||
file->resolve_symbols(ctx);
|
||||
});
|
||||
|
||||
resolve_default_symver(ctx);
|
||||
mark_live_objects(ctx);
|
||||
|
||||
// Now that we know the exact set of input files that are to be
|
||||
@@ -295,13 +330,17 @@ void resolve_symbols(Context<E> &ctx) {
|
||||
// we could eliminate a symbol that is already resolved to and cause
|
||||
// dangling references.
|
||||
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
|
||||
if (file->is_alive)
|
||||
if (file->is_reachable) {
|
||||
for (ComdatGroupRef<E> &ref : file->comdat_groups)
|
||||
update_minimum(ref.group->owner, file->priority);
|
||||
for (ComdatGroup *g : file->lto_comdat_groups)
|
||||
if (g)
|
||||
update_minimum(g->owner, file->priority);
|
||||
}
|
||||
});
|
||||
|
||||
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
|
||||
if (file->is_alive)
|
||||
if (file->is_reachable)
|
||||
for (ComdatGroupRef<E> &ref : file->comdat_groups)
|
||||
if (ref.group->owner != file->priority)
|
||||
for (u32 i : ref.members)
|
||||
@@ -311,7 +350,7 @@ void resolve_symbols(Context<E> &ctx) {
|
||||
|
||||
// Redo symbol resolution
|
||||
tbb::parallel_for_each(files, [&](InputFile<E> *file) {
|
||||
if (file->is_alive)
|
||||
if (file->is_reachable)
|
||||
file->resolve_symbols(ctx);
|
||||
});
|
||||
|
||||
@@ -322,7 +361,7 @@ void resolve_symbols(Context<E> &ctx) {
|
||||
std::atomic_bool flag = false;
|
||||
|
||||
tbb::parallel_for_each(ctx.dsos, [&](SharedFile<E> *file) {
|
||||
if (file->is_alive) {
|
||||
if (file->is_reachable) {
|
||||
for (Symbol<E> *sym : file->symbols) {
|
||||
if (sym->file == file && sym->visibility == STV_HIDDEN) {
|
||||
sym->skip_dso = true;
|
||||
@@ -334,9 +373,7 @@ void resolve_symbols(Context<E> &ctx) {
|
||||
|
||||
if (!flag)
|
||||
return;
|
||||
|
||||
clear_symbols(ctx);
|
||||
resolve_symbols(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,11 +385,16 @@ void do_lto(Context<E> &ctx) {
|
||||
|
||||
// The compiler backend needs to know how symbols are resolved, so
|
||||
// compute symbol visibility, import/export bits, etc early.
|
||||
mark_live_objects(ctx);
|
||||
apply_version_script(ctx);
|
||||
parse_symbol_version(ctx);
|
||||
compute_import_export(ctx);
|
||||
|
||||
// If multiple IR object files define the same symbol, the LTO backend
|
||||
// would choose one of them randomly instead of reporting an error.
|
||||
// So we need to check for symbol duplication error before doing an LTO.
|
||||
if (!ctx.arg.allow_multiple_definition)
|
||||
check_duplicate_symbols(ctx);
|
||||
|
||||
// Invoke the LTO plugin. This step compiles IR object files into a few
|
||||
// big ELF files.
|
||||
std::vector<ObjectFile<E> *> lto_objs = run_lto_plugin(ctx);
|
||||
@@ -364,7 +406,7 @@ void do_lto(Context<E> &ctx) {
|
||||
// Remove IR object files.
|
||||
for (ObjectFile<E> *file : ctx.objs)
|
||||
if (file->is_lto_obj)
|
||||
file->is_alive = false;
|
||||
file->is_reachable = false;
|
||||
|
||||
std::erase_if(ctx.objs, [](ObjectFile<E> *file) { return file->is_lto_obj; });
|
||||
|
||||
@@ -377,9 +419,6 @@ void parse_eh_frame_sections(Context<E> &ctx) {
|
||||
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
file->parse_ehframe(ctx);
|
||||
|
||||
for (InputSection<E> *isec : file->eh_frame_sections)
|
||||
isec->is_alive = false;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -414,13 +453,9 @@ void convert_common_symbols(Context<E> &ctx) {
|
||||
|
||||
template <typename E>
|
||||
static bool has_ctors_and_init_array(Context<E> &ctx) {
|
||||
bool x = false;
|
||||
bool y = false;
|
||||
for (ObjectFile<E> *file : ctx.objs) {
|
||||
x |= file->has_ctors;
|
||||
y |= file->has_init_array;
|
||||
}
|
||||
return x && y;
|
||||
return
|
||||
ranges::any_of(ctx.objs, [](ObjectFile<E> *x) { return x->has_ctors; }) &&
|
||||
ranges::any_of(ctx.objs, [](ObjectFile<E> *x) { return x->has_init_array; });
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -443,7 +478,6 @@ static u64 canonicalize_type(std::string_view name, u64 type) {
|
||||
if constexpr (is_x86_64<E>)
|
||||
if (type == SHT_X86_64_UNWIND)
|
||||
return SHT_PROGBITS;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
@@ -556,6 +590,9 @@ static bool is_relro(OutputSection<E> &osec) {
|
||||
}
|
||||
|
||||
// Create output sections for input sections.
|
||||
//
|
||||
// Since one output section could contain millions of input sections,
|
||||
// we need to do it efficiently.
|
||||
template <typename E>
|
||||
void create_output_sections(Context<E> &ctx) {
|
||||
Timer t(ctx, "create_output_sections");
|
||||
@@ -564,18 +601,14 @@ void create_output_sections(Context<E> &ctx) {
|
||||
OutputSectionKey::Hash>;
|
||||
MapType map;
|
||||
std::shared_mutex mu;
|
||||
i64 size = ctx.osec_pool.size();
|
||||
bool ctors_in_init_array = has_ctors_and_init_array(ctx);
|
||||
tbb::enumerable_thread_specific<MapType> caches;
|
||||
|
||||
// Instantiate output sections
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
// Make a per-thread cache of the main map to avoid lock contention.
|
||||
// It makes a noticeable difference if we have millions of input sections.
|
||||
MapType cache;
|
||||
{
|
||||
std::shared_lock lock(mu);
|
||||
cache = map;
|
||||
}
|
||||
MapType &cache = caches.local();
|
||||
|
||||
for (std::unique_ptr<InputSection<E>> &isec : file->sections) {
|
||||
if (!isec || !isec->is_alive)
|
||||
@@ -628,22 +661,40 @@ void create_output_sections(Context<E> &ctx) {
|
||||
}
|
||||
});
|
||||
|
||||
// Add input sections to output sections
|
||||
for (std::unique_ptr<OutputSection<E>> &osec : ctx.osec_pool)
|
||||
osec->members_vec.resize(ctx.objs.size());
|
||||
|
||||
tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
|
||||
for (std::unique_ptr<InputSection<E>> &isec : ctx.objs[i]->sections)
|
||||
if (isec && isec->output_section)
|
||||
isec->output_section->members_vec[i].push_back(isec.get());
|
||||
});
|
||||
|
||||
// Compute section alignment
|
||||
for (std::unique_ptr<OutputSection<E>> &osec : ctx.osec_pool) {
|
||||
Atomic<u32> p2align;
|
||||
tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
|
||||
u32 x = 0;
|
||||
for (InputSection<E> *isec : osec->members_vec[i])
|
||||
x = std::max<u32>(x, isec->p2align);
|
||||
update_maximum(p2align, x);
|
||||
});
|
||||
osec->shdr.sh_addralign = 1 << p2align;
|
||||
}
|
||||
|
||||
for (std::unique_ptr<OutputSection<E>> &osec : ctx.osec_pool) {
|
||||
osec->shdr.sh_flags = osec->sh_flags;
|
||||
osec->is_relro = is_relro(*osec);
|
||||
osec->members = flatten(osec->members_vec);
|
||||
osec->members_vec.clear();
|
||||
osec->members_vec.shrink_to_fit();
|
||||
}
|
||||
|
||||
// Add input sections to output sections
|
||||
std::vector<Chunk<E> *> chunks;
|
||||
for (i64 i = size; i < ctx.osec_pool.size(); i++)
|
||||
chunks.push_back(ctx.osec_pool[i].get());
|
||||
|
||||
for (ObjectFile<E> *file : ctx.objs)
|
||||
for (std::unique_ptr<InputSection<E>> &isec : file->sections)
|
||||
if (isec && isec->is_alive)
|
||||
isec->output_section->members.push_back(isec.get());
|
||||
|
||||
// Add output sections and mergeable sections to ctx.chunks
|
||||
std::vector<Chunk<E> *> chunks;
|
||||
for (std::unique_ptr<OutputSection<E>> &osec : ctx.osec_pool)
|
||||
chunks.push_back(osec.get());
|
||||
for (std::unique_ptr<MergedSection<E>> &osec : ctx.merged_sections)
|
||||
chunks.push_back(osec.get());
|
||||
|
||||
@@ -672,7 +723,7 @@ void create_internal_file(Context<E> &ctx) {
|
||||
|
||||
obj->symbols.push_back(new Symbol<E>);
|
||||
obj->first_global = 1;
|
||||
obj->is_alive = true;
|
||||
obj->is_reachable = true;
|
||||
obj->priority = 1;
|
||||
|
||||
auto add = [&](Symbol<E> *sym) {
|
||||
@@ -703,7 +754,6 @@ void create_internal_file(Context<E> &ctx) {
|
||||
add(get_symbol(ctx, ord.name));
|
||||
|
||||
obj->elf_syms = ctx.internal_esyms;
|
||||
obj->has_symver.resize(ctx.internal_esyms.size() - 1);
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -829,8 +879,6 @@ void add_synthetic_symbols(Context<E> &ctx) {
|
||||
add(label);
|
||||
|
||||
obj.elf_syms = ctx.internal_esyms;
|
||||
obj.has_symver.resize(ctx.internal_esyms.size() - 1);
|
||||
|
||||
obj.resolve_symbols(ctx);
|
||||
|
||||
// Make all synthetic symbols relative ones by associating them to
|
||||
@@ -869,11 +917,12 @@ void add_synthetic_symbols(Context<E> &ctx) {
|
||||
|
||||
template <typename E>
|
||||
void apply_section_align(Context<E> &ctx) {
|
||||
for (Chunk<E> *chunk : ctx.chunks)
|
||||
if (OutputSection<E> *osec = chunk->to_osec())
|
||||
if (auto it = ctx.arg.section_align.find(osec->name);
|
||||
it != ctx.arg.section_align.end())
|
||||
osec->shdr.sh_addralign = it->second;
|
||||
std::unordered_map<std::string_view, u64> &map = ctx.arg.section_align;
|
||||
if (!map.empty())
|
||||
for (Chunk<E> *chunk : ctx.chunks)
|
||||
if (OutputSection<E> *osec = chunk->to_osec())
|
||||
if (auto it = map.find(osec->name); it != map.end())
|
||||
osec->shdr.sh_addralign = it->second;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -882,8 +931,7 @@ void check_cet_errors(Context<E> &ctx) {
|
||||
assert(warning || ctx.arg.z_cet_report == CET_REPORT_ERROR);
|
||||
|
||||
auto has_feature = [](ObjectFile<E> *file, u32 feature) {
|
||||
return std::any_of(file->gnu_properties.begin(), file->gnu_properties.end(),
|
||||
[&](std::pair<u32, u32> kv) {
|
||||
return ranges::any_of(file->gnu_properties, [&](std::pair<u32, u32> kv) {
|
||||
return kv.first == GNU_PROPERTY_X86_FEATURE_1_AND &&
|
||||
(kv.second & feature);
|
||||
});
|
||||
@@ -979,7 +1027,7 @@ static std::string create_response_file(Context<E> &ctx) {
|
||||
|
||||
if (cwd != "/") {
|
||||
out << "--chroot ..";
|
||||
i64 depth = std::count(cwd.begin(), cwd.end(), '/');
|
||||
i64 depth = ranges::count(cwd, '/');
|
||||
for (i64 i = 1; i < depth; i++)
|
||||
out << "/..";
|
||||
out << "\n";
|
||||
@@ -1024,12 +1072,15 @@ void check_duplicate_symbols(Context<E> &ctx) {
|
||||
Timer t(ctx, "check_duplicate_symbols");
|
||||
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
if (!file->is_reachable)
|
||||
return;
|
||||
|
||||
for (i64 i = file->first_global; i < file->elf_syms.size(); i++) {
|
||||
const ElfSym<E> &esym = file->elf_syms[i];
|
||||
Symbol<E> &sym = *file->symbols[i];
|
||||
|
||||
// Skip if our symbol is undef or weak
|
||||
if (sym.file == file || sym.file == ctx.internal_obj ||
|
||||
if (!sym.file || sym.file == file || sym.file == ctx.internal_obj ||
|
||||
esym.is_undef() || esym.is_common() || (esym.st_bind == STB_WEAK))
|
||||
continue;
|
||||
|
||||
@@ -1041,6 +1092,13 @@ void check_duplicate_symbols(Context<E> &ctx) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if the symbol is a deduplicated comdat symbol that is in
|
||||
// an IR file.
|
||||
if (file->is_lto_obj)
|
||||
if (ComdatGroup *g = file->lto_comdat_groups[i])
|
||||
if (g->owner != file->priority)
|
||||
continue;
|
||||
|
||||
Error(ctx) << "duplicate symbol: " << *file << ": " << *sym.file
|
||||
<< ": " << sym;
|
||||
}
|
||||
@@ -1071,14 +1129,14 @@ void check_shlib_undefined(Context<E> &ctx) {
|
||||
|
||||
// Obtain a list of known shared library names.
|
||||
std::unordered_set<std::string_view> sonames;
|
||||
for (SharedFile<E> *file : ctx.dsos)
|
||||
for (std::unique_ptr<SharedFile<E>> &file : ctx.dso_pool)
|
||||
sonames.insert(file->soname);
|
||||
|
||||
tbb::parallel_for_each(ctx.dsos, [&](SharedFile<E> *file) {
|
||||
// Skip the file if it depends on a file that we know nothing about.
|
||||
// This is because missing symbols may be provided by that unknown file.
|
||||
for (std::string_view needed : file->get_dt_needed(ctx))
|
||||
if (sonames.count(needed) == 0)
|
||||
if (!sonames.contains(needed))
|
||||
return;
|
||||
|
||||
// Check if all undefined symbols have been resolved.
|
||||
@@ -1091,6 +1149,22 @@ void check_shlib_undefined(Context<E> &ctx) {
|
||||
<< sym;
|
||||
}
|
||||
});
|
||||
|
||||
// Beyond this point, DSOs that are not referenced directly by any
|
||||
// object file are not needed. They were kept by
|
||||
// SharedFile<E>::mark_live_objects just for this pass. Therefore,
|
||||
// remove unneeded DSOs from the list now.
|
||||
for (SharedFile<E> *file : ctx.dsos)
|
||||
file->is_reachable = false;
|
||||
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
for (Symbol<E> *sym : file->get_global_syms())
|
||||
if (InputFile<E> *file = sym->file)
|
||||
if (file->is_dso)
|
||||
file->is_reachable.test_and_set();
|
||||
});
|
||||
|
||||
std::erase_if(ctx.dsos, [](SharedFile<E> *file) { return !file->is_reachable; });
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -1176,7 +1250,7 @@ void sort_init_fini(Context<E> &ctx) {
|
||||
if (osec->name == ".init_array" || osec->name == ".preinit_array" ||
|
||||
osec->name == ".fini_array") {
|
||||
if (ctx.arg.shuffle_sections == SHUFFLE_SECTIONS_REVERSE)
|
||||
std::reverse(osec->members.begin(), osec->members.end());
|
||||
ranges::reverse(osec->members);
|
||||
|
||||
std::vector<Entry> vec;
|
||||
|
||||
@@ -1187,8 +1261,7 @@ void sort_init_fini(Context<E> &ctx) {
|
||||
else
|
||||
vec.push_back({isec, get_init_fini_priority(isec)});
|
||||
}
|
||||
|
||||
sort(vec, [&](const Entry &a, const Entry &b) { return a.prio < b.prio; });
|
||||
ranges::stable_sort(vec, {}, &Entry::prio);
|
||||
|
||||
for (i64 i = 0; i < vec.size(); i++)
|
||||
osec->members[i] = vec[i].sect;
|
||||
@@ -1210,13 +1283,12 @@ void sort_ctor_dtor(Context<E> &ctx) {
|
||||
if (OutputSection<E> *osec = chunk->to_osec()) {
|
||||
if (osec->name == ".ctors" || osec->name == ".dtors") {
|
||||
if (ctx.arg.shuffle_sections != SHUFFLE_SECTIONS_REVERSE)
|
||||
std::reverse(osec->members.begin(), osec->members.end());
|
||||
ranges::reverse(osec->members);
|
||||
|
||||
std::vector<Entry> vec;
|
||||
for (InputSection<E> *isec : osec->members)
|
||||
vec.push_back({isec, get_ctor_dtor_priority(isec)});
|
||||
|
||||
sort(vec, [&](const Entry &a, const Entry &b) { return a.prio < b.prio; });
|
||||
ranges::stable_sort(vec, {}, &Entry::prio);
|
||||
|
||||
for (i64 i = 0; i < vec.size(); i++)
|
||||
osec->members[i] = vec[i].sect;
|
||||
@@ -1250,9 +1322,7 @@ void fixup_ctors_in_init_array(Context<E> &ctx) {
|
||||
for (ElfRel<E> &r : rels)
|
||||
r.r_offset = isec.sh_size - r.r_offset - sizeof(Word<E>);
|
||||
|
||||
sort(rels, [](const ElfRel<E> &a, const ElfRel<E> &b) {
|
||||
return a.r_offset < b.r_offset;
|
||||
});
|
||||
ranges::stable_sort(rels, {}, &ElfRel<E>::r_offset);
|
||||
};
|
||||
|
||||
if (Chunk<E> *chunk = find_chunk(ctx, ".init_array"))
|
||||
@@ -1268,8 +1338,8 @@ void fixup_ctors_in_init_array(Context<E> &ctx) {
|
||||
reverse_contents(*isec);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void shuffle(std::vector<T> &vec, u64 seed) {
|
||||
template <typename E>
|
||||
static void shuffle(std::vector<InputSection<E> *> &vec, u64 seed) {
|
||||
if (vec.empty())
|
||||
return;
|
||||
|
||||
@@ -1322,7 +1392,7 @@ void shuffle_sections(Context<E> &ctx) {
|
||||
case SHUFFLE_SECTIONS_REVERSE:
|
||||
tbb::parallel_for_each(ctx.chunks, [&](Chunk<E> *chunk) {
|
||||
if (OutputSection<E> *osec = chunk->to_osec(); is_eligible(osec))
|
||||
std::reverse(osec->members.begin(), osec->members.end());
|
||||
ranges::reverse(osec->members);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
@@ -1330,21 +1400,53 @@ void shuffle_sections(Context<E> &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void add_dynamic_strings(Context<E> &ctx) {
|
||||
for (SharedFile<E> *file : ctx.dsos) {
|
||||
std::string_view s = file->get_dt_audit(ctx);
|
||||
if (!s.empty()) {
|
||||
if (!ctx.arg.depaudit.empty())
|
||||
ctx.arg.depaudit += ':';
|
||||
ctx.arg.depaudit += std::string(s);
|
||||
}
|
||||
}
|
||||
|
||||
auto add = [&](std::string_view s) {
|
||||
if (!s.empty())
|
||||
ctx.dynstr->add_string(s);
|
||||
};
|
||||
|
||||
for (SharedFile<E> *file : ctx.dsos)
|
||||
add(file->soname);
|
||||
for (std::string_view str : ctx.arg.auxiliary)
|
||||
add(str);
|
||||
for (std::string_view str : ctx.arg.filter)
|
||||
add(str);
|
||||
|
||||
add(ctx.arg.audit);
|
||||
add(ctx.arg.depaudit);
|
||||
add(ctx.arg.rpaths);
|
||||
add(ctx.arg.soname);
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void compute_section_sizes(Context<E> &ctx) {
|
||||
Timer t(ctx, "compute_section_sizes");
|
||||
|
||||
if constexpr (needs_thunk<E>) {
|
||||
// We cannot use parallel-for for compute_section_size() which may
|
||||
// call create_range_extension_thunks() because that function is
|
||||
// not thread-safe.
|
||||
for (Chunk<E> *chunk : ctx.chunks)
|
||||
if (chunk->shdr.sh_flags & SHF_EXECINSTR)
|
||||
chunk->compute_section_size(ctx);
|
||||
std::vector<Chunk<E> *> vec = ctx.chunks;
|
||||
|
||||
tbb::parallel_for_each(ctx.chunks, [&](Chunk<E> *chunk) {
|
||||
if (!(chunk->shdr.sh_flags & SHF_EXECINSTR))
|
||||
chunk->compute_section_size(ctx);
|
||||
auto tail = ranges::partition(vec, [&](Chunk<E> *chunk) {
|
||||
return chunk->to_osec() && (chunk->shdr.sh_flags & SHF_EXECINSTR) &&
|
||||
!ctx.arg.relocatable;
|
||||
});
|
||||
|
||||
// create_range_extension_thunks is not thread-safe
|
||||
for (Chunk<E> *chunk : std::span(vec.begin(), tail.begin()))
|
||||
chunk->to_osec()->create_range_extension_thunks(ctx, true);
|
||||
|
||||
tbb::parallel_for_each(tail, [&](Chunk<E> *chunk) {
|
||||
chunk->compute_section_size(ctx);
|
||||
});
|
||||
} else {
|
||||
tbb::parallel_for_each(ctx.chunks, [&](Chunk<E> *chunk) {
|
||||
@@ -1364,7 +1466,7 @@ void claim_unresolved_symbols(Context<E> &ctx) {
|
||||
Timer t(ctx, "claim_unresolved_symbols");
|
||||
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
if (!file->is_alive)
|
||||
if (file == ctx.internal_obj)
|
||||
return;
|
||||
|
||||
for (i64 i = file->first_global; i < file->elf_syms.size(); i++) {
|
||||
@@ -1379,24 +1481,6 @@ void claim_unresolved_symbols(Context<E> &ctx) {
|
||||
if (!sym.esym().is_undef() || sym.file->priority <= file->priority)
|
||||
continue;
|
||||
|
||||
// If a symbol name is in the form of "foo@version", search for
|
||||
// symbol "foo" and check if the symbol has version "version".
|
||||
if (file->has_symver.get(i - file->first_global)) {
|
||||
std::string_view str = file->symbol_strtab.data() + esym.st_name;
|
||||
i64 pos = str.find('@');
|
||||
assert(pos != str.npos);
|
||||
|
||||
std::string_view name = str.substr(0, pos);
|
||||
std::string_view ver = str.substr(pos + 1);
|
||||
|
||||
Symbol<E> *sym2 = get_symbol(ctx, name);
|
||||
if (sym2->file && sym2->file->is_dso && sym2->get_version() == ver) {
|
||||
file->symbols[i] = sym2;
|
||||
sym2->is_imported = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
auto claim = [&](bool is_imported) {
|
||||
if (sym.is_traced)
|
||||
Out(ctx) << "trace-symbol: " << *file << ": unresolved"
|
||||
@@ -1414,10 +1498,19 @@ void claim_unresolved_symbols(Context<E> &ctx) {
|
||||
};
|
||||
|
||||
if (esym.is_undef_weak()) {
|
||||
if (ctx.arg.shared && sym.visibility != STV_HIDDEN &&
|
||||
ctx.arg.z_dynamic_undefined_weak) {
|
||||
if (ctx.arg.z_dynamic_undefined_weak && sym.visibility != STV_HIDDEN) {
|
||||
// Global weak undefined symbols are promoted to dynamic symbols
|
||||
// when linking a DSO unless `-z nodynamic_undefined_weak` was given.
|
||||
// by default only when linking a DSO. We generally cannot do that
|
||||
// for executables because we may need to create a copy relocation
|
||||
// for a data symbol, but the symbol size is not available for an
|
||||
// unclaimed weak symbol.
|
||||
//
|
||||
// In contrast, GNU ld promotes weak symbols to dynamic ones even
|
||||
// for an executable as long as they don't need copy relocations
|
||||
// (i.e. they need only PLT entries.) That may result in an
|
||||
// inconsistent behavior of a linked program depending on whether
|
||||
// whether its object files were compiled with -fPIC or not. I think
|
||||
// that's bad semantics, so we don't do that.
|
||||
claim(true);
|
||||
} else {
|
||||
// Otherwise, weak undefs are converted to absolute symbols with value 0.
|
||||
@@ -1550,7 +1643,7 @@ template <typename E>
|
||||
void compute_imported_symbol_weakness(Context<E> &ctx) {
|
||||
Timer t(ctx, "compute_imported_symbol_weakness");
|
||||
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
|
||||
for (i64 i = file->first_global; i < file->elf_syms.size(); i++) {
|
||||
const ElfSym<E> &esym = file->elf_syms[i];
|
||||
Symbol<E> &sym = *file->symbols[i];
|
||||
@@ -1696,29 +1789,26 @@ void sort_dynsyms(Context<E> &ctx) {
|
||||
return;
|
||||
|
||||
// In any symtab, local symbols must precede global symbols.
|
||||
auto first_global = std::stable_partition(syms.begin() + 1, syms.end(),
|
||||
[&](Symbol<E> *sym) {
|
||||
auto globals = ranges::stable_partition(syms.subspan(1), [&](Symbol<E> *sym) {
|
||||
return sym->is_local(ctx);
|
||||
});
|
||||
|
||||
// .gnu.hash imposes more restrictions on the order of the symbols in
|
||||
// .dynsym.
|
||||
if (ctx.gnu_hash) {
|
||||
auto first_exported = std::stable_partition(first_global, syms.end(),
|
||||
[&](Symbol<E> *sym) {
|
||||
auto exported_syms = ranges::stable_partition(globals, [](Symbol<E> *sym) {
|
||||
return !sym->is_exported;
|
||||
});
|
||||
|
||||
// Count the number of exported symbols to compute the size of .gnu.hash.
|
||||
i64 num_exported = syms.end() - first_exported;
|
||||
i64 num_exported = exported_syms.size();
|
||||
u32 num_buckets = num_exported / ctx.gnu_hash->LOAD_FACTOR + 1;
|
||||
|
||||
tbb::parallel_for_each(first_exported, syms.end(), [&](Symbol<E> *sym) {
|
||||
tbb::parallel_for_each(exported_syms, [&](Symbol<E> *sym) {
|
||||
sym->set_djb_hash(ctx, djb_hash(sym->name()));
|
||||
});
|
||||
|
||||
tbb::parallel_sort(first_exported, syms.end(),
|
||||
[&](Symbol<E> *a, Symbol<E> *b) {
|
||||
tbb::parallel_sort(exported_syms, [&](Symbol<E> *a, Symbol<E> *b) {
|
||||
return std::tuple(a->get_djb_hash(ctx) % num_buckets, a->name()) <
|
||||
std::tuple(b->get_djb_hash(ctx) % num_buckets, b->name());
|
||||
});
|
||||
@@ -1728,7 +1818,7 @@ void sort_dynsyms(Context<E> &ctx) {
|
||||
}
|
||||
|
||||
// Compute .dynstr size
|
||||
ctx.dynstr->dynsym_offset = ctx.dynstr->shdr.sh_size;
|
||||
ctx.dynsym->dynstr_offset = ctx.dynstr->shdr.sh_size;
|
||||
|
||||
tbb::enumerable_thread_specific<i64> size;
|
||||
tbb::parallel_for((i64)1, (i64)syms.size(), [&](i64 i) {
|
||||
@@ -1739,13 +1829,21 @@ void sort_dynsyms(Context<E> &ctx) {
|
||||
ctx.dynstr->shdr.sh_size += size.combine(std::plus());
|
||||
|
||||
// ELF's symbol table sh_info holds the offset of the first global symbol.
|
||||
ctx.dynsym->shdr.sh_info = first_global - syms.begin();
|
||||
ctx.dynsym->shdr.sh_info = globals.begin() - syms.begin();
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void create_output_symtab(Context<E> &ctx) {
|
||||
Timer t(ctx, "compute_symtab_size");
|
||||
|
||||
if constexpr (needs_thunk<E>) {
|
||||
i64 n = 0;
|
||||
for (Chunk<E> *chunk : ctx.chunks)
|
||||
if (OutputSection<E> *osec = chunk->to_osec())
|
||||
for (std::unique_ptr<Thunk<E>> &thunk : osec->thunks)
|
||||
thunk->name = "thunk" + std::to_string(n++);
|
||||
}
|
||||
|
||||
tbb::parallel_for_each(ctx.chunks, [&](Chunk<E> *chunk) {
|
||||
chunk->compute_symtab_size(ctx);
|
||||
});
|
||||
@@ -1777,8 +1875,7 @@ void apply_version_script(Context<E> &ctx) {
|
||||
// last one takes precedence.
|
||||
std::vector<VersionPattern> patterns = ctx.version_patterns;
|
||||
|
||||
std::stable_partition(patterns.begin(), patterns.end(),
|
||||
[](const VersionPattern &pat) {
|
||||
ranges::stable_partition(patterns, [](const VersionPattern &pat) {
|
||||
return pat.ver_idx == VER_NDX_LOCAL;
|
||||
});
|
||||
|
||||
@@ -1858,7 +1955,7 @@ void parse_symbol_version(Context<E> &ctx) {
|
||||
|
||||
for (i64 i = file->first_global; i < file->elf_syms.size(); i++) {
|
||||
// Match VERSION part of symbol foo@VERSION with version definitions.
|
||||
if (!file->has_symver.get(i - file->first_global))
|
||||
if (!file->has_symver[i - file->first_global])
|
||||
continue;
|
||||
|
||||
Symbol<E> *sym = file->symbols[i];
|
||||
@@ -1890,8 +1987,8 @@ void parse_symbol_version(Context<E> &ctx) {
|
||||
// versioned symbol. Likewise, if `foo@VERSION` and `foo@@VERSION` are
|
||||
// defined, the default one takes precedence.
|
||||
Symbol<E> *sym2 = get_symbol(ctx, sym->name());
|
||||
if (sym2->file == file &&
|
||||
!file->has_symver.get(sym2->sym_idx - file->first_global))
|
||||
if (sym2 != sym && sym2->file == file &&
|
||||
!file->has_symver[sym2->sym_idx - file->first_global])
|
||||
if (sym2->ver_idx == ctx.default_version ||
|
||||
(sym2->ver_idx & ~VERSYM_HIDDEN) == (sym->ver_idx & ~VERSYM_HIDDEN))
|
||||
sym2->ver_idx = VER_NDX_LOCAL;
|
||||
@@ -1947,7 +2044,7 @@ void compute_import_export(Context<E> &ctx) {
|
||||
// If we are creating an executable, we want to export symbols referenced
|
||||
// by DSOs unless they are explicitly marked as local by a version script.
|
||||
if (!ctx.arg.shared) {
|
||||
tbb::parallel_for_each(ctx.dsos, [&](SharedFile<E> *file) {
|
||||
tbb::parallel_for_each(ctx.dsos, [](SharedFile<E> *file) {
|
||||
for (Symbol<E> *sym : file->symbols) {
|
||||
if (sym->file && !sym->file->is_dso && sym->visibility != STV_HIDDEN &&
|
||||
sym->ver_idx != VER_NDX_LOCAL) {
|
||||
@@ -2249,9 +2346,8 @@ void sort_output_sections_regular(Context<E> &ctx) {
|
||||
return 0;
|
||||
};
|
||||
|
||||
sort(ctx.chunks, [&](Chunk<E> *a, Chunk<E> *b) {
|
||||
return std::tuple{get_rank1(a), get_rank2(a), a->name} <
|
||||
std::tuple{get_rank1(b), get_rank2(b), b->name};
|
||||
ranges::stable_sort(ctx.chunks, {}, [&](Chunk<E> *x) {
|
||||
return std::tuple{get_rank1(x), get_rank2(x), x->name};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2307,9 +2403,7 @@ void sort_output_sections_by_order(Context<E> &ctx) {
|
||||
chunk->sect_order = get_rank(chunk);
|
||||
|
||||
// Sort output sections by --section-order
|
||||
sort(ctx.chunks, [&](Chunk<E> *a, Chunk<E> *b) {
|
||||
return a->sect_order < b->sect_order;
|
||||
});
|
||||
ranges::stable_sort(ctx.chunks, {}, &Chunk<E>::sect_order);
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -2364,27 +2458,6 @@ static void set_virtual_addresses_regular(Context<E> &ctx) {
|
||||
std::vector<Chunk<E> *> &chunks = ctx.chunks;
|
||||
u64 addr = ctx.arg.image_base;
|
||||
|
||||
// TLS chunks alignments are special: in addition to having their virtual
|
||||
// addresses aligned, they also have to be aligned when the region of
|
||||
// tls_begin is copied to a new thread's storage area. In other words, their
|
||||
// offset against tls_begin also has to be aligned.
|
||||
//
|
||||
// A good way to achieve this is to take the largest alignment requirement
|
||||
// of all TLS sections and make tls_begin also aligned to that.
|
||||
Chunk<E> *first_tls_chunk = nullptr;
|
||||
u64 tls_alignment = 1;
|
||||
for (Chunk<E> *chunk : chunks) {
|
||||
if (chunk->shdr.sh_flags & SHF_TLS) {
|
||||
if (!first_tls_chunk)
|
||||
first_tls_chunk = chunk;
|
||||
tls_alignment = std::max(tls_alignment, (u64)chunk->shdr.sh_addralign);
|
||||
}
|
||||
}
|
||||
|
||||
auto alignment = [&](Chunk<E> *chunk) {
|
||||
return chunk == first_tls_chunk ? tls_alignment : (u64)chunk->shdr.sh_addralign;
|
||||
};
|
||||
|
||||
auto is_tbss = [](Chunk<E> *chunk) {
|
||||
return (chunk->shdr.sh_type == SHT_NOBITS) && (chunk->shdr.sh_flags & SHF_TLS);
|
||||
};
|
||||
@@ -2456,7 +2529,7 @@ static void set_virtual_addresses_regular(Context<E> &ctx) {
|
||||
if (is_tbss(chunks[i])) {
|
||||
u64 addr2 = addr;
|
||||
for (;;) {
|
||||
addr2 = align_to(addr2, alignment(chunks[i]));
|
||||
addr2 = align_to(addr2, chunks[i]->shdr.sh_addralign);
|
||||
chunks[i]->shdr.sh_addr = addr2;
|
||||
addr2 += chunks[i]->shdr.sh_size;
|
||||
if (i + 2 == chunks.size() || !is_tbss(chunks[i + 1]))
|
||||
@@ -2466,7 +2539,7 @@ static void set_virtual_addresses_regular(Context<E> &ctx) {
|
||||
continue;
|
||||
}
|
||||
|
||||
addr = align_to(addr, alignment(chunks[i]));
|
||||
addr = align_to(addr, chunks[i]->shdr.sh_addralign);
|
||||
chunks[i]->shdr.sh_addr = addr;
|
||||
addr += chunks[i]->shdr.sh_size;
|
||||
}
|
||||
@@ -2540,13 +2613,12 @@ static void set_virtual_addresses_by_order(Context<E> &ctx) {
|
||||
}
|
||||
|
||||
// Returns the smallest integer N that satisfies N >= val and
|
||||
// N mod align == skew mod align.
|
||||
// N % align == skew % align.
|
||||
//
|
||||
// Section's file offset must be congruent to its virtual address modulo
|
||||
// the page size. We use this function to satisfy that requirement.
|
||||
static u64 align_with_skew(u64 val, u64 align, u64 skew) {
|
||||
u64 x = align_down(val, align) + skew % align;
|
||||
return (val <= x) ? x : x + align;
|
||||
return val + ((skew - val) & (align - 1));
|
||||
}
|
||||
|
||||
// Assign file offsets to output sections.
|
||||
@@ -2629,11 +2701,10 @@ void separate_debug_sections(Context<E> &ctx) {
|
||||
chunk->name.starts_with(".debug_");
|
||||
};
|
||||
|
||||
auto mid = std::stable_partition(ctx.chunks.begin(), ctx.chunks.end(),
|
||||
is_debug_section);
|
||||
auto tail = ranges::stable_partition(ctx.chunks, std::not_fn(is_debug_section));
|
||||
|
||||
ctx.debug_chunks = {ctx.chunks.begin(), mid};
|
||||
ctx.chunks.erase(ctx.chunks.begin(), mid);
|
||||
ctx.debug_chunks = {tail.begin(), tail.end()};
|
||||
ctx.chunks.erase(tail.begin(), tail.end());
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -2650,9 +2721,9 @@ void compute_section_headers(Context<E> &ctx) {
|
||||
|
||||
// Set section indices.
|
||||
i64 shndx = 1;
|
||||
for (i64 i = 0; i < ctx.chunks.size(); i++)
|
||||
if (!ctx.chunks[i]->is_header())
|
||||
ctx.chunks[i]->shndx = shndx++;
|
||||
for (Chunk<E> *chunk : ctx.chunks)
|
||||
if (!chunk->is_header())
|
||||
chunk->shndx = shndx++;
|
||||
|
||||
if (ctx.symtab && SHN_LORESERVE <= shndx) {
|
||||
SymtabShndxSection<E> *sec = new SymtabShndxSection<E>;
|
||||
@@ -2666,8 +2737,8 @@ void compute_section_headers(Context<E> &ctx) {
|
||||
if (ctx.shdr)
|
||||
ctx.shdr->shdr.sh_size = shndx * sizeof(ElfShdr<E>);
|
||||
|
||||
// Some types of section header refer other section by index.
|
||||
// Recompute the section header to fill such fields with correct values.
|
||||
// Some types of section header refer to other section by index.
|
||||
// Recompute all section headers to fill such fields with correct values.
|
||||
for (Chunk<E> *chunk : ctx.chunks)
|
||||
chunk->update_shdr(ctx);
|
||||
|
||||
@@ -2705,9 +2776,11 @@ i64 set_osec_offsets(Context<E> &ctx) {
|
||||
|
||||
template <typename E>
|
||||
static i64 get_num_irelative_relocs(Context<E> &ctx) {
|
||||
i64 n = std::count_if(ctx.got->got_syms.begin(), ctx.got->got_syms.end(),
|
||||
[](Symbol<E> *sym) { return sym->is_ifunc(); });
|
||||
return n + ctx.num_ifunc_dynrels;
|
||||
i64 n = ctx.num_ifunc_dynrels;
|
||||
for (Symbol<E> *sym : ctx.got->got_syms)
|
||||
if (sym->is_ifunc())
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -2767,18 +2840,16 @@ void fix_synthetic_symbols(Context<E> &ctx) {
|
||||
// defined in a statically-linked non-relocatable executable because
|
||||
// such executable lacks the .dynamic section and thus there's no way
|
||||
// to find ifunc relocations other than these symbols.
|
||||
//
|
||||
// We don't want to set values to these symbols if we are creating a
|
||||
// static PIE due to a glibc bug. Static PIE has a dynamic section.
|
||||
// If we set values to these symbols in a static PIE, glibc attempts
|
||||
// to run ifunc initializers twice, with the second attempt with wrong
|
||||
// function addresses, causing a segmentation fault.
|
||||
if (ctx.reldyn && ctx.arg.static_ && !ctx.arg.pie) {
|
||||
stop(ctx.__rel_iplt_start, ctx.reldyn);
|
||||
stop(ctx.__rel_iplt_end, ctx.reldyn);
|
||||
|
||||
ctx.__rel_iplt_start->value -=
|
||||
get_num_irelative_relocs(ctx) * sizeof(ElfRel<E>);
|
||||
} else {
|
||||
// If the symbols are not ncessary, we turn them to absolute
|
||||
// symbols at address 0.
|
||||
ctx.__rel_iplt_start->origin = 0;
|
||||
ctx.__rel_iplt_end->origin = 0;
|
||||
}
|
||||
|
||||
// __{init,fini}_array_{start,end}
|
||||
@@ -2977,6 +3048,46 @@ std::vector<std::span<u8>> get_shards(Context<E> &ctx) {
|
||||
return vec;
|
||||
}
|
||||
|
||||
// Sort dynamic relocations. This is the reason why we do it.
|
||||
// Quote from https://www.airs.com/blog/archives/186
|
||||
//
|
||||
// The dynamic linker in glibc uses a one element cache when processing
|
||||
// relocs: if a relocation refers to the same symbol as the previous
|
||||
// relocation, then the dynamic linker reuses the value rather than
|
||||
// looking up the symbol again. Thus the dynamic linker gets the best
|
||||
// results if the dynamic relocations are sorted so that all dynamic
|
||||
// relocations for a given dynamic symbol are adjacent.
|
||||
//
|
||||
// Other than that, the linker sorts together all relative relocations,
|
||||
// which don't have symbols. Two relative relocations, or two relocations
|
||||
// against the same symbol, are sorted by the address in the output
|
||||
// file. This tends to optimize paging and caching when there are two
|
||||
// references from the same page.
|
||||
template <typename E>
|
||||
void sort_reldyn(Context<E> &ctx) {
|
||||
Timer t(ctx, "sort_reldyn");
|
||||
|
||||
ElfRel<E> *begin = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset);
|
||||
ElfRel<E> *end = begin + ctx.reldyn->shdr.sh_size / sizeof(ElfRel<E>);
|
||||
|
||||
// We group IFUNC relocations at the end of .rel.dyn because we want to
|
||||
// apply all the other relocations before running user-supplied IFUNC
|
||||
// resolvers.
|
||||
auto get_rank = [](u32 r_type) {
|
||||
if (r_type == E::R_RELATIVE)
|
||||
return 0;
|
||||
if constexpr (supports_ifunc<E>)
|
||||
if (r_type == E::R_IRELATIVE)
|
||||
return 2;
|
||||
return 1;
|
||||
};
|
||||
|
||||
tbb::parallel_sort(begin, end, [&](const ElfRel<E> &a, const ElfRel<E> &b) {
|
||||
return std::tuple(get_rank(a.r_type), a.r_sym, a.r_offset) <
|
||||
std::tuple(get_rank(b.r_type), b.r_sym, b.r_offset);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void write_build_id(Context<E> &ctx) {
|
||||
Timer t(ctx, "write_build_id");
|
||||
@@ -3101,6 +3212,10 @@ void write_separate_debug_file(Context<E> &ctx) {
|
||||
|
||||
append(ctx.chunks, ctx.debug_chunks);
|
||||
|
||||
// Handle --compress-debug-info
|
||||
if (ctx.arg.compress_debug_sections != COMPRESS_NONE)
|
||||
compress_debug_sections(ctx);
|
||||
|
||||
// Write to the debug info file as if it were a regular output file.
|
||||
compute_section_headers(ctx);
|
||||
file->resize(ctx, set_osec_offsets(ctx));
|
||||
@@ -3209,6 +3324,14 @@ void show_stats(Context<E> &ctx) {
|
||||
static Counter num_objs("num_objs", ctx.objs.size());
|
||||
static Counter num_dsos("num_dsos", ctx.dsos.size());
|
||||
|
||||
using Entry = typename ConcurrentMap<SectionFragment<E>>::Entry;
|
||||
|
||||
static Counter merged_strings("merged_strings");
|
||||
for (std::unique_ptr<MergedSection<E>> &sec : ctx.merged_sections)
|
||||
for (Entry &ent : std::span(sec->map.entries, sec->map.nbuckets))
|
||||
if (ent.key)
|
||||
merged_strings++;
|
||||
|
||||
if constexpr (needs_thunk<E>) {
|
||||
static Counter thunk_bytes("thunk_bytes");
|
||||
for (Chunk<E> *chunk : ctx.chunks)
|
||||
@@ -3217,6 +3340,15 @@ void show_stats(Context<E> &ctx) {
|
||||
thunk_bytes += thunk->size();
|
||||
}
|
||||
|
||||
if constexpr (is_riscv<E> || is_loongarch<E>) {
|
||||
static Counter num_rels("shrunk_relocs");
|
||||
for (Chunk<E> *chunk : ctx.chunks)
|
||||
if (OutputSection<E> *osec = chunk->to_osec())
|
||||
if (osec->shdr.sh_flags & SHF_EXECINSTR)
|
||||
for (InputSection<E> *isec : osec->members)
|
||||
num_rels += isec->extra.r_deltas.size();
|
||||
}
|
||||
|
||||
Counter::print();
|
||||
|
||||
for (std::unique_ptr<MergedSection<E>> &sec : ctx.merged_sections)
|
||||
@@ -3247,6 +3379,7 @@ template void sort_init_fini(Context<E> &);
|
||||
template void sort_ctor_dtor(Context<E> &);
|
||||
template void fixup_ctors_in_init_array(Context<E> &);
|
||||
template void shuffle_sections(Context<E> &);
|
||||
template void add_dynamic_strings(Context<E> &);
|
||||
template void compute_section_sizes(Context<E> &);
|
||||
template void sort_output_sections(Context<E> &);
|
||||
template void claim_unresolved_symbols(Context<E> &);
|
||||
@@ -3267,6 +3400,7 @@ template void compute_section_headers(Context<E> &);
|
||||
template i64 set_osec_offsets(Context<E> &);
|
||||
template void fix_synthetic_symbols(Context<E> &);
|
||||
template void compress_debug_sections(Context<E> &);
|
||||
template void sort_reldyn(Context<E> &);
|
||||
template void write_build_id(Context<E> &);
|
||||
template void write_gnu_debuglink(Context<E> &);
|
||||
template void write_separate_debug_file(Context<E> &);
|
||||
|
||||
+1
-4
@@ -111,10 +111,7 @@ template <typename E>
|
||||
static void r_claim_unresolved_symbols(Context<E> &ctx) {
|
||||
Timer t(ctx, "r_claim_unresolved_symbols");
|
||||
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
if (!file->is_alive)
|
||||
return;
|
||||
|
||||
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
|
||||
for (i64 i = file->first_global; i < file->elf_syms.size(); i++) {
|
||||
const ElfSym<E> &esym = file->elf_syms[i];
|
||||
Symbol<E> &sym = *file->symbols[i];
|
||||
|
||||
+17
-30
@@ -66,21 +66,18 @@ namespace mold {
|
||||
|
||||
using E = MOLD_TARGET;
|
||||
|
||||
static bool is_resizable(InputSection<E> *isec) {
|
||||
return isec && isec->is_alive && (isec->shdr().sh_flags & SHF_ALLOC) &&
|
||||
(isec->shdr().sh_flags & SHF_EXECINSTR);
|
||||
template <>
|
||||
i64 get_r_delta(InputSection<E> &isec, u64 offset) {
|
||||
std::span<RelocDelta> deltas = isec.extra.r_deltas;
|
||||
auto it = ranges::upper_bound(deltas, offset, std::less_equal<>{},
|
||||
&RelocDelta::offset);
|
||||
return (it == deltas.begin()) ? 0 : (it - 1)->delta;
|
||||
}
|
||||
|
||||
template <>
|
||||
void shrink_sections<E>(Context<E> &ctx) {
|
||||
void shrink_sections(Context<E> &ctx) {
|
||||
Timer t(ctx, "shrink_sections");
|
||||
|
||||
// True if we can use the 2-byte instructions. This is usually true on
|
||||
// Unix because RV64GC is generally considered the baseline hardware.
|
||||
bool use_rvc = false;
|
||||
if constexpr (is_riscv<E>)
|
||||
use_rvc = get_eflags(ctx) & EF_RISCV_RVC;
|
||||
|
||||
// Find all relaxable relocations and record how many bytes we can save
|
||||
// into r_deltas.
|
||||
//
|
||||
@@ -93,28 +90,18 @@ void shrink_sections<E>(Context<E> &ctx) {
|
||||
// them. We scan relocations only once here.
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
for (std::unique_ptr<InputSection<E>> &isec : file->sections)
|
||||
if (is_resizable(isec.get()))
|
||||
shrink_section(ctx, *isec, use_rvc);
|
||||
if (isec && isec->is_alive && (isec->shdr().sh_flags & SHF_EXECINSTR))
|
||||
shrink_section(ctx, *isec);
|
||||
});
|
||||
|
||||
// Fix symbol values.
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
for (Symbol<E> *sym : file->symbols) {
|
||||
if (sym->file != file)
|
||||
continue;
|
||||
|
||||
InputSection<E> *isec = sym->get_input_section();
|
||||
if (!isec || isec->extra.r_deltas.empty())
|
||||
continue;
|
||||
|
||||
std::span<const ElfRel<E>> rels = isec->get_rels(ctx);
|
||||
auto it = std::lower_bound(rels.begin(), rels.end(), sym->value,
|
||||
[&](const ElfRel<E> &r, u64 val) {
|
||||
return r.r_offset < val;
|
||||
});
|
||||
|
||||
sym->value -= isec->extra.r_deltas[it - rels.begin()];
|
||||
}
|
||||
for (Symbol<E> *sym : file->symbols)
|
||||
if (sym->file == file)
|
||||
if (InputSection<E> *isec = sym->get_input_section())
|
||||
if (isec->shdr().sh_flags & SHF_EXECINSTR)
|
||||
if (i64 delta = get_r_delta(*isec, sym->value))
|
||||
sym->value -= delta;
|
||||
});
|
||||
|
||||
// Recompute sizes of executable sections
|
||||
@@ -126,8 +113,8 @@ void shrink_sections<E>(Context<E> &ctx) {
|
||||
|
||||
// Returns the distance between a relocated place and a symbol.
|
||||
template <>
|
||||
i64 compute_distance<E>(Context<E> &ctx, Symbol<E> &sym,
|
||||
InputSection<E> &isec, const ElfRel<E> &rel) {
|
||||
i64 compute_distance(Context<E> &ctx, Symbol<E> &sym,
|
||||
InputSection<E> &isec, const ElfRel<E> &rel) {
|
||||
// We handle absolute symbols as if they were infinitely far away
|
||||
// because `shrink_section` may increase a distance between a branch
|
||||
// instruction and an absolute symbol. Branching to an absolute
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "mold.h"
|
||||
|
||||
#include <csignal>
|
||||
#include <filesystem>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
@@ -101,8 +101,8 @@ void process_run_subcommand(Context<E> &ctx, int argc, char **argv) {
|
||||
std::string dso_path = find_dso(ctx, self);
|
||||
|
||||
// Set environment variables
|
||||
putenv(strdup(("LD_PRELOAD=" + dso_path).c_str()));
|
||||
putenv(strdup(("MOLD_PATH=" + self).c_str()));
|
||||
setenv("LD_PRELOAD", dso_path.c_str(), 1);
|
||||
setenv("MOLD_PATH", self.c_str(), 1);
|
||||
|
||||
// If ld, ld.lld or ld.gold is specified, run mold itself
|
||||
if (std::string cmd = path_filename(argv[2]);
|
||||
|
||||
+137
-152
@@ -32,79 +32,39 @@ namespace mold {
|
||||
|
||||
using E = MOLD_TARGET;
|
||||
|
||||
// Returns a branch reach in bytes for a given target.
|
||||
static consteval i64 max_distance() {
|
||||
// ARM64's branch has 26 bits immediate. The immediate is padded with
|
||||
// implicit two-bit zeros because all instructions are 4 bytes aligned
|
||||
// and therefore the least two bits are always zero. So the branch
|
||||
// operand is effectively 28 bits long. That means the branch range is
|
||||
// [-2^27, 2^27) or PC ± 128 MiB.
|
||||
if (is_arm64<E>)
|
||||
return 1 << 27;
|
||||
|
||||
// ARM32's Thumb branch has 24 bits immediate, and the instructions are
|
||||
// aligned to 2, so it's effectively 25 bits. It's [-2^24, 2^24) or PC ±
|
||||
// 16 MiB.
|
||||
//
|
||||
// ARM32's non-Thumb branches have twice longer range than its Thumb
|
||||
// counterparts, but we conservatively use the Thumb's limitation.
|
||||
if (is_arm32<E>)
|
||||
return 1 << 24;
|
||||
|
||||
// PPC's branch has 24 bits immediate, and the instructions are aligned
|
||||
// to 4, therefore the reach is [-2^25, 2^25) or PC ± 32 MiB.
|
||||
assert(is_ppc<E>);
|
||||
return 1 << 25;
|
||||
}
|
||||
|
||||
// We create thunks for each 12.8/1.6/3.2 MiB code block for
|
||||
// We create thunks for each 25.6/3.2/6.4 MiB code block for
|
||||
// ARM64/ARM32/PPC, respectively.
|
||||
static constexpr i64 batch_size = max_distance() / 10;
|
||||
static constexpr i64 batch_size = branch_distance<E> / 5;
|
||||
|
||||
// We assume that a single thunk group is smaller than 900 KiB.
|
||||
static constexpr i64 max_thunk_size = 900 * 1024;
|
||||
// We assume that a single thunk group is smaller than 1 MiB.
|
||||
static constexpr i64 max_thunk_size = 1024 * 1024;
|
||||
|
||||
static_assert(max_thunk_size / E::thunk_size < ThunkRef::MAX_SYM_IDX);
|
||||
// We align thunks to 16 byte boundaries because many processor vendors
|
||||
// recommend we align branch targets to 16 byte boundaries for performance
|
||||
// reasons.
|
||||
static constexpr i64 thunk_align = 16;
|
||||
|
||||
template <typename E>
|
||||
static bool is_reachable(Context<E> &ctx, InputSection<E> &isec,
|
||||
static bool is_reachable(Context<E> &ctx, bool first_pass, InputSection<E> &isec,
|
||||
Symbol<E> &sym, const ElfRel<E> &rel) {
|
||||
// We create thunks with a pessimistic assumption that all
|
||||
// out-of-section relocations would be out-of-range.
|
||||
InputSection<E> *isec2 = sym.get_input_section();
|
||||
if (!isec2 || isec.output_section != isec2->output_section)
|
||||
return false;
|
||||
|
||||
// Even if the target is the same section, we branch to its PLT
|
||||
// if it has one. So a symbol with a PLT is also considered an
|
||||
// out-of-section reference.
|
||||
if (sym.has_plt(ctx))
|
||||
return false;
|
||||
|
||||
// If the target section is in the same output section but
|
||||
// hasn't got any address yet, that's unreacahble.
|
||||
if (isec2->offset == -1)
|
||||
InputSection<E> *isec2 = sym.get_input_section();
|
||||
if (isec2 && isec.output_section == isec2->output_section &&
|
||||
isec2->offset == -1)
|
||||
return false;
|
||||
|
||||
// Thumb and ARM B instructions cannot be converted to BX, so we
|
||||
// always have to make them jump to a thunk to switch processor mode
|
||||
// even if their destinations are within their ranges.
|
||||
if constexpr (is_arm32<E>) {
|
||||
bool is_thumb = sym.get_addr(ctx) & 1;
|
||||
if ((rel.r_type == R_ARM_THM_JUMP24 && !is_thumb) ||
|
||||
(rel.r_type == R_ARM_JUMP24 && is_thumb) ||
|
||||
(rel.r_type == R_ARM_PLT32 && is_thumb))
|
||||
// We don't know about the final file layout on the first pass, so
|
||||
// we assume pessimistically that all out-of-section relocations are
|
||||
// out-of-range. Excessive thunks will be removed on the second pass.
|
||||
if (first_pass) {
|
||||
if (!isec2 || isec.output_section != isec2->output_section)
|
||||
return false;
|
||||
}
|
||||
|
||||
// PowerPC before Power9 lacks PC-relative load/store instructions.
|
||||
// Functions compiled for Power9 or earlier assume that r2 points to
|
||||
// GOT+0x8000, while those for Power10 uses r2 as a scratch register.
|
||||
// We need to a thunk to recompute r2 for interworking.
|
||||
if constexpr (is_ppc64v2<E>) {
|
||||
if (rel.r_type == R_PPC64_REL24 && !sym.esym().ppc64_preserves_r2())
|
||||
return false;
|
||||
if (rel.r_type == R_PPC64_REL24_NOTOC && sym.esym().ppc64_uses_toc())
|
||||
// Even if the target is the same section, we branch to its PLT
|
||||
// if it has one. So a symbol with a PLT is also considered an
|
||||
// out-of-section reference.
|
||||
if (sym.has_plt(ctx))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -114,60 +74,39 @@ static bool is_reachable(Context<E> &ctx, InputSection<E> &isec,
|
||||
i64 A = get_addend(isec, rel);
|
||||
i64 P = isec.get_addr() + rel.r_offset;
|
||||
i64 val = S + A - P;
|
||||
return -max_distance() <= val && val < max_distance();
|
||||
return -branch_distance<E> <= val && val < branch_distance<E>;
|
||||
}
|
||||
|
||||
static void reset_thunk(Thunk<E> &thunk) {
|
||||
for (Symbol<E> *sym : thunk.symbols) {
|
||||
sym->extra.thunk_idx = -1;
|
||||
sym->extra.thunk_sym_idx = -1;
|
||||
sym->flags = 0;
|
||||
template <typename E>
|
||||
static bool needs_shim(Context<E> &ctx, Symbol<E> &sym, const ElfRel<E> &rel) {
|
||||
// Thumb and ARM B instructions cannot be converted to BX, so we
|
||||
// always have to make them jump to a thunk to switch processor mode
|
||||
// even if their destinations are within their ranges.
|
||||
if constexpr (is_arm32<E>) {
|
||||
bool is_thumb = sym.get_addr(ctx) & 1;
|
||||
return (rel.r_type == R_ARM_THM_JUMP24 && !is_thumb) ||
|
||||
(rel.r_type == R_ARM_JUMP24 && is_thumb) ||
|
||||
(rel.r_type == R_ARM_PLT32 && is_thumb);
|
||||
}
|
||||
}
|
||||
|
||||
// Scan relocations to collect symbols that need thunks.
|
||||
static void scan_rels(Context<E> &ctx, InputSection<E> &isec,
|
||||
Thunk<E> &thunk, i64 thunk_idx) {
|
||||
std::span<const ElfRel<E>> rels = isec.get_rels(ctx);
|
||||
std::vector<ThunkRef> &thunk_refs = isec.extra.thunk_refs;
|
||||
thunk_refs.resize(rels.size());
|
||||
// On PowerPC, all PLT calls go through range extension thunks.
|
||||
if constexpr (is_ppc32<E> || is_ppc64v1<E>)
|
||||
return sym.has_plt(ctx);
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
if (!is_func_call_rel(rel))
|
||||
continue;
|
||||
|
||||
// Skip if the symbol is undefined. apply_reloc() will report an error.
|
||||
Symbol<E> &sym = *isec.file.symbols[rel.r_sym];
|
||||
if (!sym.file)
|
||||
continue;
|
||||
|
||||
// Skip if the destination is within reach.
|
||||
if (is_reachable(ctx, isec, sym, rel))
|
||||
continue;
|
||||
|
||||
// This relocation needs a thunk. If the symbol is already in a
|
||||
// previous thunk, reuse it.
|
||||
if (sym.extra.thunk_idx != -1) {
|
||||
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.
|
||||
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);
|
||||
thunk.symbols.push_back(&sym);
|
||||
}
|
||||
}
|
||||
// PowerPC before Power9 lacks PC-relative load/store instructions.
|
||||
// Functions compiled for Power9 or earlier assume that r2 points to
|
||||
// GOT+0x8000, while those for Power10 uses r2 as a scratch register.
|
||||
// We need to a thunk to recompute r2 for interworking.
|
||||
if constexpr (is_ppc64v2<E>)
|
||||
return sym.has_plt(ctx) ||
|
||||
(rel.r_type == R_PPC64_REL24 && !sym.esym().ppc64_preserves_r2()) ||
|
||||
(rel.r_type == R_PPC64_REL24_NOTOC && sym.esym().ppc64_uses_toc());
|
||||
return false;
|
||||
}
|
||||
|
||||
template <>
|
||||
void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
|
||||
void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx,
|
||||
bool first_pass) {
|
||||
std::span<InputSection<E> *> m = members;
|
||||
if (m.empty())
|
||||
return;
|
||||
@@ -189,14 +128,14 @@ void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
|
||||
// from the current batch.
|
||||
//
|
||||
// D is the input section with the largest address such that the thunk
|
||||
// is reachable from the current batch if it's inserted right before D.
|
||||
// is reachable from the current batch if it's inserted at D.
|
||||
//
|
||||
// ................................ <input sections> ............
|
||||
// A B C D
|
||||
// ^ We insert a thunk for the current batch just before D
|
||||
// <---> The current batch, which is smaller than BATCH_SIZE
|
||||
// <--------> Smaller than MAX_DISTANCE
|
||||
// <--------> Smaller than MAX_DISTANCE
|
||||
// <--------> Smaller than BRANCH_DISTANCE
|
||||
// <--------> Smaller than BRANCH_DISTANCE
|
||||
// <-------------> Reachable from the current batch
|
||||
i64 a = 0;
|
||||
i64 b = 0;
|
||||
@@ -211,11 +150,11 @@ 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<E>::alignment) + max_thunk_size;
|
||||
return align_to(d_end, thunk_align) + max_thunk_size;
|
||||
};
|
||||
|
||||
while (d < m.size() &&
|
||||
(b == d || d_thunk_end() <= m[b]->offset + max_distance())) {
|
||||
(b == d || d_thunk_end() <= m[b]->offset + branch_distance<E>)) {
|
||||
offset = align_to(offset, 1 << m[d]->p2align);
|
||||
m[d]->offset = offset;
|
||||
offset += m[d]->sh_size;
|
||||
@@ -230,65 +169,111 @@ void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
|
||||
c++;
|
||||
|
||||
// Move A forward so that A is reachable from C.
|
||||
i64 c_offset = (c == m.size()) ? offset : m[c]->offset;
|
||||
while (a < b && m[a]->offset + max_distance() < c_offset)
|
||||
i64 c_offset = (c == d) ? offset : m[c]->offset;
|
||||
while (a < b && m[a]->offset + branch_distance<E> < c_offset)
|
||||
a++;
|
||||
|
||||
// Erase references to out-of-range thunks.
|
||||
while (t < thunks.size() && thunks[t]->offset < m[a]->offset)
|
||||
reset_thunk(*thunks[t++]);
|
||||
for (; t < thunks.size() && thunks[t]->offset < m[a]->offset; t++)
|
||||
for (Symbol<E> *sym : thunks[t]->symbols)
|
||||
sym->flags = 0;
|
||||
|
||||
// Create a new thunk and place it at D.
|
||||
offset = align_to(offset, Thunk<E>::alignment);
|
||||
i64 thunk_idx = thunks.size();
|
||||
Thunk<E> *thunk = new Thunk<E>(*this, offset);
|
||||
thunks.emplace_back(thunk);
|
||||
offset = align_to(offset, thunk_align);
|
||||
thunks.emplace_back(std::make_unique<Thunk<E>>(*this, offset));
|
||||
Thunk<E> &thunk = *thunks.back();
|
||||
|
||||
// Scan relocations between B and C to collect symbols that need
|
||||
// entries in the new thunk.
|
||||
for (i64 i = b; i < c; i++)
|
||||
scan_rels(ctx, *m[i], *thunk, thunk_idx);
|
||||
std::mutex mu;
|
||||
|
||||
tbb::parallel_for(b, c, [&](i64 i) {
|
||||
InputSection<E> &isec = *m[i];
|
||||
|
||||
for (const ElfRel<E> &rel : isec.get_rels(ctx)) {
|
||||
if (!is_func_call_rel(rel))
|
||||
continue;
|
||||
|
||||
// Skip if the symbol is undefined. apply_reloc() will report an error.
|
||||
Symbol<E> &sym = *isec.file.symbols[rel.r_sym];
|
||||
if (!sym.file)
|
||||
continue;
|
||||
|
||||
// Skip if we can directly branch to the destination.
|
||||
if (is_reachable(ctx, first_pass, isec, sym, rel) &&
|
||||
!needs_shim(ctx, sym, rel))
|
||||
continue;
|
||||
|
||||
// Add the symbol to the current thunk if it's not added already.
|
||||
if (!sym.flags.test_and_set()) {
|
||||
std::scoped_lock lock(mu);
|
||||
thunk.symbols.push_back(&sym);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Sort symbols added to the thunk to make the output deterministic.
|
||||
ranges::sort(thunk.symbols, {}, [](Symbol<E> *x) {
|
||||
return std::tuple{x->file->priority, x->sym_idx};
|
||||
});
|
||||
|
||||
// Now that we know the number of symbols in the thunk, we can compute
|
||||
// the thunk's size.
|
||||
assert(thunk->size() < max_thunk_size);
|
||||
offset += thunk->size();
|
||||
|
||||
// Sort symbols added to the thunk to make the output deterministic.
|
||||
sort(thunk->symbols, [](Symbol<E> *a, Symbol<E> *b) {
|
||||
return std::tuple{a->file->priority, a->sym_idx} <
|
||||
std::tuple{b->file->priority, b->sym_idx};
|
||||
});
|
||||
|
||||
// Assign offsets within the thunk to the symbols.
|
||||
for (i64 i = 0; Symbol<E> *sym : thunk->symbols) {
|
||||
sym->extra.thunk_idx = thunk_idx;
|
||||
sym->extra.thunk_sym_idx = i++;
|
||||
}
|
||||
|
||||
// Scan relocations again to fix symbol offsets in the last thunk.
|
||||
for (i64 i = b; i < c; i++) {
|
||||
std::span<Symbol<E> *> syms = m[i]->file.symbols;
|
||||
std::span<const ElfRel<E>> rels = m[i]->get_rels(ctx);
|
||||
std::span<ThunkRef> thunk_refs = m[i]->extra.thunk_refs;
|
||||
|
||||
for (i64 j = 0; j < rels.size(); j++)
|
||||
if (thunk_refs[j].thunk_idx == thunk_idx)
|
||||
thunk_refs[j].sym_idx = syms[rels[j].r_sym]->extra.thunk_sym_idx;
|
||||
}
|
||||
assert(thunk.size() < max_thunk_size);
|
||||
offset += thunk.size();
|
||||
|
||||
// Move B forward to point to the begining of the next batch.
|
||||
b = c;
|
||||
}
|
||||
|
||||
while (t < thunks.size())
|
||||
reset_thunk(*thunks[t++]);
|
||||
for (; t < thunks.size(); t++)
|
||||
for (Symbol<E> *sym : thunks[t]->symbols)
|
||||
sym->flags = 0;
|
||||
|
||||
this->shdr.sh_size = offset;
|
||||
}
|
||||
|
||||
for (InputSection<E> *isec : members)
|
||||
this->shdr.sh_addralign =
|
||||
std::max<u32>(this->shdr.sh_addralign, 1 << isec->p2align);
|
||||
template <>
|
||||
void remove_redundant_thunks(Context<E> &ctx) {
|
||||
Timer t(ctx, "remove_redundant_thunks");
|
||||
set_osec_offsets(ctx);
|
||||
|
||||
for (Chunk<E> *chunk : ctx.chunks)
|
||||
if (OutputSection<E> *osec = chunk->to_osec())
|
||||
if (osec->shdr.sh_flags & SHF_EXECINSTR)
|
||||
osec->create_range_extension_thunks(ctx, false);
|
||||
}
|
||||
|
||||
// When applying relocations, we want to know the address in a reachable
|
||||
// range extension thunk for a given symbol. Doing it by scanning all
|
||||
// reachable range extension thunks is too expensive.
|
||||
//
|
||||
// In this function, we create a list of all addresses in range extension
|
||||
// thunks for each symbol, so that it is easy to find one.
|
||||
//
|
||||
// Note that thunk_addrs must be sorted for binary search.
|
||||
template <>
|
||||
void gather_thunk_addresses(Context<E> &ctx) {
|
||||
Timer t(ctx, "gather_thunk_addresses");
|
||||
|
||||
std::vector<OutputSection<E> *> sections;
|
||||
for (Chunk<E> *chunk : ctx.chunks)
|
||||
if (OutputSection<E> *osec = chunk->to_osec())
|
||||
sections.push_back(osec);
|
||||
|
||||
ranges::stable_sort(sections, {}, [](OutputSection<E> *x) {
|
||||
return x->shdr.sh_addr;
|
||||
});
|
||||
|
||||
for (OutputSection<E> *osec : sections) {
|
||||
for (std::unique_ptr<Thunk<E>> &thunk : osec->thunks) {
|
||||
for (i64 i = 0; i < thunk->symbols.size(); i++) {
|
||||
Symbol<E> &sym = *thunk->symbols[i];
|
||||
sym.add_aux(ctx);
|
||||
ctx.symbol_aux[sym.aux_idx].thunk_addrs.push_back(thunk->get_addr(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mold
|
||||
|
||||
+36
-11
@@ -72,21 +72,46 @@
|
||||
// TLV is sometimes compiled to a function call! The module ID and the
|
||||
// offset are usually stored to GOT as two consecutive words.
|
||||
//
|
||||
// The last access method is the most generic, so the compiler emits such
|
||||
// code by default. But that's the most expensive one, so the linker
|
||||
// rewrites instructions if possible so that 3) is relaxed to 2) or even
|
||||
// to 1).
|
||||
//
|
||||
// 1) is called the Local Exec access model. 2) is Initial Exec, and 3) is
|
||||
// General Dynamic.
|
||||
//
|
||||
// There's another little trick that the compiler can use if it knows two
|
||||
// TLVs are in the same ELF file (usually in the same file as the code is).
|
||||
// In this case, we can call __tls_get_addr() only once with a module ID and
|
||||
// the offset 0 to obtain the base address of the ELF file's TLS block. The
|
||||
// base address obtained this way is sometimes called Dynamic Thread Pointer
|
||||
// or DTP. We can then compute TLVs' addresses by adding their DTP-relative
|
||||
// addresses to DTP. This access model is called the Local Dynamic.
|
||||
// In this case, we can call __tls_get_addr() only once with the module ID
|
||||
// and the offset 0 to obtain the base address of the ELF file's TLS block.
|
||||
// The base address obtained this way is sometimes called Dynamic Thread
|
||||
// Pointer or DTP. We can then compute TLVs' addresses by adding their
|
||||
// DTP-relative addresses to DTP. This access model is called the Local
|
||||
// Dynamic.
|
||||
//
|
||||
// The compiler tries to emit the most efficient code for a TLV access based
|
||||
// on compiler command line options and TLV properties as follows:
|
||||
//
|
||||
// 1. If -fno-PIC or -fPIE is given, and a TLV is defined as a file-local
|
||||
// variable, the compiler knows that it is compiling code for an
|
||||
// executable (i.e. not for a shared library) and the TLV is defined
|
||||
// within the executable. In this case, Local Exec is used to access the
|
||||
// variable.
|
||||
//
|
||||
// 2. If -fno-PIC or -fPIE is given (i.e. the compiler is compiling code for
|
||||
// a main executable), but a TLV is defined in another translation unit,
|
||||
// then the variable may be defined not in the main executable but in a
|
||||
// shared library. In this case, Initial Exec is used to access the
|
||||
// variable.
|
||||
//
|
||||
// 3. If -fPIC is given, it may be compiling code for dlopen'able shared
|
||||
// library. In this case, Local Dynamic or General Dynamic is used to
|
||||
// access TLVs.
|
||||
//
|
||||
// You can also manually control how the compiler emits TLV access code
|
||||
// globally with `-ftls-model=<model-name>` or per-variable basis with
|
||||
// `__attribute__((tls_model(<model-name>)))`. For example, if you are
|
||||
// building a shared library that you have no plan to use with dlopen(), you
|
||||
// may want to compile the source files with `-ftls-model=initial-exec` to
|
||||
// avoid the cost associated with the General Dynamic access model.
|
||||
//
|
||||
// The linker may rewrite instructions with a code sequence for a cheaper
|
||||
// access model at link-time.
|
||||
//
|
||||
// === TLS Descriptor access model ===
|
||||
//
|
||||
@@ -137,7 +162,7 @@ u64 get_tp_addr(const ElfPhdr<E> &phdr) {
|
||||
// and %a0/%a1 on s390x) refers to past the end of the TLS block for
|
||||
// historical reasons. TLVs are accessed with negative offsets from TP.
|
||||
return align_to(phdr.p_vaddr + phdr.p_memsz, phdr.p_align);
|
||||
} else if constexpr (is_arm<E> || is_sh4<E>) {
|
||||
} else if constexpr (is_arm<E> || is_sh4<E> || is_arc<E>) {
|
||||
// On ARM and SH4, the runtime appends two words at the beginning
|
||||
// of TLV template image when copying TLVs to the TLS block, so we need
|
||||
// to offset it.
|
||||
|
||||
+7
-2
@@ -31,7 +31,7 @@ endif()
|
||||
if(MOLD_ENABLE_QEMU_TESTS)
|
||||
list(APPEND QEMU_ARCHS
|
||||
x86_64 i386 arm aarch64 aarch64_be ppc ppc64 ppc64le sparc64 sh4 sh4eb
|
||||
s390x riscv64 riscv32 m68k loongarch64)
|
||||
s390x riscv64 riscv32 m68k loongarch64 arc)
|
||||
|
||||
LIST(APPEND TRIPLES
|
||||
x86_64-linux-gnu
|
||||
@@ -49,7 +49,8 @@ if(MOLD_ENABLE_QEMU_TESTS)
|
||||
sh4aeb-linux-gnu
|
||||
riscv32-linux-gnu
|
||||
m68k-linux-gnu
|
||||
loongarch64-linux-gnu)
|
||||
loongarch64-linux-gnu
|
||||
arc-linux-gnu)
|
||||
|
||||
foreach(ARCH IN LISTS QEMU_ARCHS)
|
||||
find_program(HAS_qemu-${ARCH} qemu-${ARCH})
|
||||
@@ -184,3 +185,7 @@ endif()
|
||||
if(${MACHINE} STREQUAL "loongarch64" OR (HAS_qemu-loongarch64 AND HAS_loongarch64-linux-gnu-gcc))
|
||||
add_target(loongarch64 loongarch64-linux-gnu)
|
||||
endif()
|
||||
|
||||
if(${MACHINE} STREQUAL "arc" OR (HAS_qemu-loongarch64 AND HAS_arc-linux-gnu-gcc))
|
||||
add_target(arc arc-linux-gnu)
|
||||
endif()
|
||||
+2
-2
@@ -18,5 +18,5 @@ extern char foo;
|
||||
int main() { printf("foo=%p\n", &foo); }
|
||||
EOF
|
||||
|
||||
! $CC -B. -o $t/exe -pie $t/a.o $t/b.o -Wl,-z,text >& $t/log
|
||||
grep -q 'recompile with -fPIC' $t/log
|
||||
not $CC -B. -o $t/exe -pie $t/a.o $t/b.o -Wl,-z,text |&
|
||||
grep 'recompile with -fPIC'
|
||||
@@ -36,4 +36,4 @@ int main() {
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe -no-pie $t/a.o $t/b.o
|
||||
$QEMU $t/exe | grep -q '^ip=0xa0000.$'
|
||||
$QEMU $t/exe | grep '^ip=0xa0000.$'
|
||||
@@ -4,6 +4,6 @@
|
||||
echo 'int main() { return 0; }' | $CC -c -o $t/a.o -xc -
|
||||
echo 'int main() { return 1; }' | $CC -c -o $t/b.o -xc -
|
||||
|
||||
! $CC -B. -o $t/exe $t/a.o $t/b.o 2> /dev/null || false
|
||||
not $CC -B. -o $t/exe $t/a.o $t/b.o 2> /dev/null
|
||||
$CC -B. -o $t/exe $t/a.o $t/b.o -Wl,-allow-multiple-definition
|
||||
$CC -B. -o $t/exe $t/a.o $t/b.o -Wl,-z,muldefs
|
||||
@@ -18,4 +18,4 @@ EOF
|
||||
$CC -B. -o $t/exe $t/a.o \
|
||||
-Wl,--section-start=.low=0x10000000,--section-start=.high=0x20000000
|
||||
|
||||
$OBJDUMP -dr $t/exe | grep -Fq '<fn1$thunk>:'
|
||||
$OBJDUMP -dr $t/exe | grep -E '<fn1\$thunk[0-9]+>:'
|
||||
@@ -10,7 +10,7 @@ foo:
|
||||
EOF
|
||||
|
||||
$CC -B. -shared -o $t/b.so $t/a.o
|
||||
readelf -W --dyn-syms $t/b.so | grep foo | grep -Fq '[VARIANT_PCS]'
|
||||
readelf -W --dyn-syms $t/b.so | grep foo | grep -F '[VARIANT_PCS]'
|
||||
|
||||
cat <<EOF | $CC -c -o $t/c.o -xc -
|
||||
void foo();
|
||||
@@ -18,4 +18,4 @@ int main() { foo(); }
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe $t/c.o $t/b.so
|
||||
readelf -W --dynamic $t/exe | grep -q AARCH64_VARIANT_PCS
|
||||
readelf -W --dynamic $t/exe | grep AARCH64_VARIANT_PCS
|
||||
@@ -14,5 +14,5 @@ EOF
|
||||
|
||||
$CC -o $t/exe -pie $t/a.o $t/b.o >& /dev/null && skip
|
||||
|
||||
! $CC -B. -o $t/exe -pie $t/a.o $t/b.o >& $t/log
|
||||
grep -q 'recompile with -fPIC' $t/log
|
||||
not $CC -B. -o $t/exe -pie $t/a.o $t/b.o |&
|
||||
grep 'recompile with -fPIC'
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CXX -o $t/a.o -c -xc++ - -fno-PIC
|
||||
#include <stdio.h>
|
||||
extern char _etext[];
|
||||
int main() {
|
||||
printf("%p\n", _etext);
|
||||
}
|
||||
EOF
|
||||
|
||||
$CXX -B. -o $t/exe $t/a.o -no-pie
|
||||
readelf --unwind $t/exe | grep "$($QEMU $t/exe) .*cantunwind"
|
||||
@@ -18,7 +18,7 @@ EOF
|
||||
$CC -B. -o $t/exe $t/a.o \
|
||||
-Wl,--section-start=.low=0x10000000,--section-start=.high=0x20000000
|
||||
|
||||
$OBJDUMP -dr $t/exe | grep -F -A7 '<fn1$thunk>:' > $t/log
|
||||
$OBJDUMP -dr $t/exe | grep -E -A7 '<fn1\$thunk[0-9]+>:' > $t/log
|
||||
|
||||
grep -Eq 'bx\s+pc' $t/log
|
||||
grep -Eq 'add\s+pc, ip, pc' $t/log
|
||||
grep -E 'bx\s+pc' $t/log
|
||||
grep -E 'add\s+pc, ip, pc' $t/log
|
||||
@@ -1,8 +1,7 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
echo 'int main() {}' | $CC -c -o /dev/null -xc - -O0 -mthumb >& /dev/null \
|
||||
|| skip
|
||||
test_cflags -mthumb || skip
|
||||
|
||||
cat <<EOF > $t/a.c
|
||||
#include <stdio.h>
|
||||
@@ -35,11 +34,11 @@ $CC -c -o $t/d.o $t/b.c -O0 -marm
|
||||
|
||||
$CC -B. -o $t/exe $t/c.o $t/d.o \
|
||||
-Wl,--section-start=.low=0x10000000,--section-start=.high=0x20000000
|
||||
$QEMU $t/exe | grep -q 'main fn1 fn3 fn2 fn4'
|
||||
$QEMU $t/exe | grep 'main fn1 fn3 fn2 fn4'
|
||||
|
||||
$CC -c -o $t/e.o $t/a.c -O2 -mthumb
|
||||
$CC -c -o $t/f.o $t/b.c -O2 -marm
|
||||
|
||||
$CC -B. -o $t/exe $t/e.o $t/f.o \
|
||||
-Wl,--section-start=.low=0x10000000,--section-start=.high=0x20000000
|
||||
$QEMU $t/exe | grep -q 'main fn1 fn3 fn2 fn4'
|
||||
$QEMU $t/exe | grep 'main fn1 fn3 fn2 fn4'
|
||||
@@ -16,4 +16,4 @@ int main() { printf("%s\n", foo); }
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe -pie $t/a.o $t/b.o
|
||||
$QEMU $t/exe | grep -q 'Hello world'
|
||||
$QEMU $t/exe | grep 'Hello world'
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xassembler -
|
||||
.syntax unified
|
||||
.globl bar
|
||||
.thumb
|
||||
bar:
|
||||
beq foo-2
|
||||
EOF
|
||||
|
||||
$OBJDUMP -d $t/a.o | grep -E 'beq\.w' || skip
|
||||
|
||||
cat <<EOF | $CC -o $t/b.o -c -xassembler -
|
||||
.globl foo, baz
|
||||
.thumb
|
||||
baz:
|
||||
nop
|
||||
foo:
|
||||
nop
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -o $t/c.o -c -xc -
|
||||
void bar();
|
||||
int main() { bar(); }
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe $t/a.o $t/b.o $t/c.o
|
||||
$OBJDUMP -d $t/exe | grep -E 'beq\.w.*<baz>'
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xassembler -
|
||||
.syntax unified
|
||||
.globl bar
|
||||
.thumb
|
||||
bar:
|
||||
beq foo-2
|
||||
EOF
|
||||
|
||||
$OBJDUMP -d $t/a.o | grep -E 'beq\.n' || skip
|
||||
|
||||
cat <<EOF | $CC -o $t/b.o -c -xassembler -
|
||||
.globl foo, baz
|
||||
.thumb
|
||||
baz:
|
||||
nop
|
||||
foo:
|
||||
nop
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -o $t/c.o -c -xc -
|
||||
void bar();
|
||||
int main() { bar(); }
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe $t/a.o $t/b.o $t/c.o
|
||||
$OBJDUMP -d $t/exe | grep -E 'beq\.n.*<baz>'
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
echo 'int foo() { return 0; }' | $CC -o /dev/null -c -xc - -mthumb 2> /dev/null || skip
|
||||
test_cflags -mthumb || skip
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc - -mthumb
|
||||
#include <stdio.h>
|
||||
@@ -27,4 +27,4 @@ int main() {
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe $t/a.o $t/b.o
|
||||
$QEMU $t/exe | grep -q 'main foo bar'
|
||||
$QEMU $t/exe | grep 'main foo bar'
|
||||
+19
-12
@@ -2,9 +2,7 @@
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
is_musl && skip
|
||||
|
||||
echo 'int main() {}' | $GCC -c -o /dev/null -xc - -O0 -mthumb >& /dev/null \
|
||||
|| skip
|
||||
test_cflags -mthumb || skip
|
||||
|
||||
cat <<EOF > $t/a.c
|
||||
extern _Thread_local int foo;
|
||||
@@ -42,24 +40,33 @@ $GCC -fPIC -mtls-dialect=gnu2 -c -o $t/c.o $t/a.c -marm
|
||||
$GCC -fPIC -mtls-dialect=gnu2 -c -o $t/d.o $t/b.c -marm
|
||||
|
||||
$CC -B. -o $t/exe1 $t/c.o $t/d.o
|
||||
$QEMU $t/exe1 | grep -q '42 5'
|
||||
$QEMU $t/exe1 | grep '42 5'
|
||||
|
||||
$CC -B. -o $t/exe2 $t/c.o $t/d.o -Wl,-no-relax
|
||||
$QEMU $t/exe2 | grep -q '42 5'
|
||||
$QEMU $t/exe2 | grep '42 5'
|
||||
|
||||
$CC -B. -o $t/exe3 $t/c.o $t/d.o -Wl,-no-relax \
|
||||
-Wl,--section-start=.low=0x10000000,--section-start=.high=0x20000000
|
||||
$QEMU $t/exe3 | grep -q '42 5'
|
||||
$QEMU $t/exe3 | grep '42 5'
|
||||
|
||||
$GCC -B. -shared -o $t/c.so $t/c.o -Wl,-z,nodlopen
|
||||
$CC -B. -o $t/exe4 $t/c.so $t/d.o
|
||||
$QEMU $t/exe4 | grep '42 5'
|
||||
|
||||
|
||||
$GCC -fPIC -mtls-dialect=gnu2 -c -o $t/e.o $t/a.c -mthumb
|
||||
$GCC -fPIC -mtls-dialect=gnu2 -c -o $t/f.o $t/b.c -mthumb
|
||||
|
||||
$CC -B. -o $t/exe4 $t/e.o $t/f.o
|
||||
$QEMU $t/exe4 | grep -q '42 5'
|
||||
$CC -B. -o $t/exe5 $t/e.o $t/f.o
|
||||
$QEMU $t/exe5 | grep '42 5'
|
||||
|
||||
$CC -B. -o $t/exe5 $t/e.o $t/f.o -Wl,-no-relax
|
||||
$QEMU $t/exe5 | grep -q '42 5'
|
||||
$CC -B. -o $t/exe6 $t/e.o $t/f.o -Wl,-no-relax
|
||||
$QEMU $t/exe6 | grep '42 5'
|
||||
|
||||
$CC -B. -o $t/exe6 $t/e.o $t/f.o -Wl,-no-relax \
|
||||
$CC -B. -o $t/exe7 $t/e.o $t/f.o -Wl,-no-relax \
|
||||
-Wl,--section-start=.low=0x10000000,--section-start=.high=0x20000000
|
||||
$QEMU $t/exe6 | grep -q '42 5'
|
||||
$QEMU $t/exe7 | grep '42 5'
|
||||
|
||||
$GCC -B. -shared -o $t/e.so $t/e.o -Wl,-z,nodlopen
|
||||
$CC -B. -o $t/exe8 $t/e.so $t/f.o
|
||||
$QEMU $t/exe8 | grep '42 5'
|
||||
@@ -37,11 +37,11 @@ int main() {
|
||||
EOF
|
||||
|
||||
$CC -o $t/exe1 $t/a.o $t/b.o $t/c.o -pie
|
||||
$QEMU $t/exe1 | grep -q '^20 3$'
|
||||
$QEMU $t/exe1 | grep '^20 3$'
|
||||
|
||||
$CC -o $t/exe2 $t/a.o $t/b.o $t/c.o -Wl,-no-relax -pie
|
||||
$QEMU $t/exe2 | grep -q '^20 3$'
|
||||
$QEMU $t/exe2 | grep '^20 3$'
|
||||
|
||||
$CC -o $t/d.so $t/a.o -shared
|
||||
$CC -o $t/exe3 $t/b.o $t/c.o $t/d.so -pie
|
||||
$QEMU $t/exe3 | grep -q '^20 3$'
|
||||
$QEMU $t/exe3 | grep '^20 3$'
|
||||
@@ -34,15 +34,15 @@ int main() {
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe1 $t/a.o $t/b.o
|
||||
$QEMU $t/exe1 | grep -q 42
|
||||
$QEMU $t/exe1 | grep 42
|
||||
|
||||
$CC -B. -o $t/exe2 $t/a.o $t/b.o -Wl,-no-relax
|
||||
$QEMU $t/exe2 | grep -q 42
|
||||
$QEMU $t/exe2 | grep 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
|
||||
$QEMU $t/exe3 | grep 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
|
||||
$QEMU $t/exe4 | grep 42
|
||||
@@ -8,4 +8,4 @@ int main() { printf(msg); }
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe1 $t/a.o
|
||||
$QEMU $t/exe1 | grep -q 'Hello world'
|
||||
$QEMU $t/exe1 | grep 'Hello world'
|
||||
@@ -38,15 +38,15 @@ int main() {
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe1 $t/a.o $t/b.o -Wl,--no-relax
|
||||
$QEMU $t/exe1 | grep -q foofoo
|
||||
$QEMU $t/exe1 | grep foofoo
|
||||
|
||||
$OBJDUMP -d $t/exe1 > $t/exe1.objdump
|
||||
grep -A2 '<foo>:' $t/exe1.objdump | grep -wq pcaddu18i
|
||||
grep -A2 '<bar>:' $t/exe1.objdump | grep -wq pcaddu18i
|
||||
grep -A2 '<foo>:' $t/exe1.objdump | grep -w pcaddu18i
|
||||
grep -A2 '<bar>:' $t/exe1.objdump | grep -w pcaddu18i
|
||||
|
||||
$CC -B. -o $t/exe2 $t/a.o $t/b.o -Wl,--relax
|
||||
$QEMU $t/exe2 | grep -q foofoo
|
||||
$QEMU $t/exe2 | grep foofoo
|
||||
|
||||
$OBJDUMP -d $t/exe2 > $t/exe2.objdump
|
||||
grep -A2 '<foo>:' $t/exe2.objdump | grep -wq bl
|
||||
grep -A2 '<bar>:' $t/exe2.objdump | grep -wq b
|
||||
grep -A2 '<foo>:' $t/exe2.objdump | grep -w bl
|
||||
grep -A2 '<bar>:' $t/exe2.objdump | grep -w b
|
||||
@@ -17,17 +17,17 @@ int main() { printf("%d\n", get_foo()); }
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe1 $t/a.o $t/b.o $t/c.o -pie -Wl,--no-relax
|
||||
$QEMU $t/exe1 | grep -q '^3$'
|
||||
$OBJDUMP -d $t/exe1 | grep -A2 '<get_foo>:' | grep -Fqw pcalau12i
|
||||
$OBJDUMP -d $t/exe1 | grep -A2 '<get_foo>:' | grep -Fqw ld.d
|
||||
$QEMU $t/exe1 | grep '^3$'
|
||||
$OBJDUMP -d $t/exe1 | grep -A2 '<get_foo>:' | grep -Fw pcalau12i
|
||||
$OBJDUMP -d $t/exe1 | grep -A2 '<get_foo>:' | grep -Fw ld.d
|
||||
|
||||
$CC -B. -o $t/exe2 $t/a.o $t/b.o $t/c.o -pie -Wl,--relax
|
||||
$QEMU $t/exe2 | grep -q '^3$'
|
||||
$OBJDUMP -d $t/exe2 | grep -A1 '<get_foo>:' | grep -Fqw pcaddi
|
||||
$QEMU $t/exe2 | grep '^3$'
|
||||
$OBJDUMP -d $t/exe2 | grep -A1 '<get_foo>:' | grep -Fw pcaddi
|
||||
|
||||
$CC -B. -o $t/exe3 $t/a.o $t/b.o $t/c.o -pie -Wl,--relax \
|
||||
-Wl,-Ttext=0x1000000,-Tdata=0x2000000
|
||||
|
||||
$QEMU $t/exe3 | grep -q '^3$'
|
||||
$OBJDUMP -d $t/exe3 | grep -A2 '<get_foo>:' | grep -Fqw pcalau12i
|
||||
$OBJDUMP -d $t/exe3 | grep -A2 '<get_foo>:' | grep -Fqw addi.d
|
||||
$QEMU $t/exe3 | grep '^3$'
|
||||
$OBJDUMP -d $t/exe3 | grep -A2 '<get_foo>:' | grep -Fw pcalau12i
|
||||
$OBJDUMP -d $t/exe3 | grep -A2 '<get_foo>:' | grep -Fw addi.d
|
||||
@@ -42,17 +42,17 @@ int main() {
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe1 $t/a.o $t/b.o $t/c.o -Wl,--no-relax
|
||||
$QEMU $t/exe1 | grep -q '^1 2 3$'
|
||||
$QEMU $t/exe1 | grep '^1 2 3$'
|
||||
|
||||
$OBJDUMP -d $t/exe1 > $t/exe1.objdump
|
||||
grep -A1 '<get_sym1>:' $t/exe1.objdump | grep -q pcalau12i
|
||||
grep -A1 '<get_sym2>:' $t/exe1.objdump | grep -q pcalau12i
|
||||
grep -A1 '<get_sym3>:' $t/exe1.objdump | grep -q pcalau12i
|
||||
grep -A1 '<get_sym1>:' $t/exe1.objdump | grep pcalau12i
|
||||
grep -A1 '<get_sym2>:' $t/exe1.objdump | grep pcalau12i
|
||||
grep -A1 '<get_sym3>:' $t/exe1.objdump | grep pcalau12i
|
||||
|
||||
$CC -B. -o $t/exe2 $t/a.o $t/b.o $t/c.o -Wl,--relax
|
||||
$QEMU $t/exe2 | grep -q '^1 2 3$'
|
||||
$QEMU $t/exe2 | grep '^1 2 3$'
|
||||
|
||||
$OBJDUMP -d $t/exe2 > $t/exe2.objdump
|
||||
grep -A1 '<get_sym1>:' $t/exe2.objdump | grep -q pcaddi
|
||||
grep -A1 '<get_sym2>:' $t/exe2.objdump | grep -q pcaddi
|
||||
grep -A1 '<get_sym3>:' $t/exe2.objdump | grep -q pcalau12i
|
||||
grep -A1 '<get_sym1>:' $t/exe2.objdump | grep pcaddi
|
||||
grep -A1 '<get_sym2>:' $t/exe2.objdump | grep pcaddi
|
||||
grep -A1 '<get_sym3>:' $t/exe2.objdump | grep pcalau12i
|
||||
@@ -29,15 +29,16 @@ int main() {
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe1 $t/a.o $t/b.o $t/c.o $t/d.o -Wl,--no-relax
|
||||
$QEMU $t/exe1 | grep -q 'foo bar'
|
||||
{ $QEMU $t/exe1; true; } |& grep 'unexpected reloc type' && skip
|
||||
$QEMU $t/exe1 | grep 'foo bar'
|
||||
|
||||
$OBJDUMP -d $t/exe1 > $t/exe1.objdump
|
||||
grep -A6 '<get_foo>:' $t/exe1.objdump | grep -Fq pcalau12i
|
||||
grep -A6 '<get_bar>:' $t/exe1.objdump | grep -Fq pcalau12i
|
||||
grep -A6 '<get_foo>:' $t/exe1.objdump | grep -F pcalau12i
|
||||
grep -A6 '<get_bar>:' $t/exe1.objdump | grep -F pcalau12i
|
||||
|
||||
$CC -B. -o $t/exe2 $t/a.o $t/b.o $t/c.o $t/d.o -Wl,--relax
|
||||
$QEMU $t/exe2 | grep -q 'foo bar'
|
||||
$QEMU $t/exe2 | grep 'foo bar'
|
||||
|
||||
$OBJDUMP -d $t/exe2 > $t/exe2.objdump
|
||||
grep -A6 '<get_foo>:' $t/exe2.objdump | grep -Fq li.w
|
||||
grep -A6 '<get_bar>:' $t/exe2.objdump | grep -Fq lu12i.w
|
||||
grep -A6 '<get_foo>:' $t/exe2.objdump | grep -F li.w
|
||||
grep -A6 '<get_bar>:' $t/exe2.objdump | grep -F lu12i.w
|
||||
@@ -9,4 +9,4 @@ int main() {
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe $t/a.o
|
||||
$OBJDUMP -d $t/exe | grep -q '<_savegpr0_14>'
|
||||
$OBJDUMP -d $t/exe | grep '<_savegpr0_14>'
|
||||
@@ -6,7 +6,7 @@ void foo() {}
|
||||
EOF
|
||||
|
||||
# The compiler might not create .riscv.attributes
|
||||
readelf --sections $t/a.o | grep -Fq .riscv.attributes || skip
|
||||
readelf --sections $t/a.o | grep -F .riscv.attributes || skip
|
||||
|
||||
cat <<EOF | $CC -march=rv64imafd_xtheadba2p0 -o $t/b.o -c -xc -
|
||||
void bar() {}
|
||||
@@ -17,7 +17,7 @@ void _start() {}
|
||||
EOF
|
||||
|
||||
$CC -B. -nostdlib -o $t/exe $t/a.o $t/c.o
|
||||
readelf -A $t/exe | grep -q '_xtheadba1p5"'
|
||||
readelf -A $t/exe | grep '_xtheadba1p5"'
|
||||
|
||||
$CC -B. -nostdlib -o $t/exe $t/a.o $t/b.o $t/c.o
|
||||
readelf -A $t/exe | grep -q '_xtheadba2p0"'
|
||||
readelf -A $t/exe | grep '_xtheadba2p0"'
|
||||
@@ -10,5 +10,5 @@ $STRIP --remove-section=.riscv.attributes $t/a.o
|
||||
$CC -B. -nostdlib -o $t/exe $t/a.o
|
||||
|
||||
readelf -W --segments --sections $t/exe > $t/log
|
||||
! grep -F .riscv.attributes $t/log || false
|
||||
! grep -F RISCV_ATTR $t/log || false
|
||||
not grep -F .riscv.attributes $t/log
|
||||
not grep -F RISCV_ATTR $t/log
|
||||
@@ -24,4 +24,4 @@ int main() {
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe $t/b.so $t/c.o $t/d.o
|
||||
$QEMU $t/exe | grep -q 'Hello world'
|
||||
$QEMU $t/exe | grep 'Hello world'
|
||||
@@ -9,10 +9,10 @@ int main() {
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe1 $t/a.o -fno-PIE
|
||||
readelf -W --dyn-syms $t/exe1 | grep -Fq '__global_pointer$'
|
||||
readelf -W --dyn-syms $t/exe1 | grep -F '__global_pointer$'
|
||||
|
||||
$CC -B. -o $t/exe2 $t/a.o -fPIE
|
||||
readelf -W --dyn-syms $t/exe2 | grep -Fq '__global_pointer$'
|
||||
readelf -W --dyn-syms $t/exe2 | grep -F '__global_pointer$'
|
||||
|
||||
cat <<EOF | $CC -o $t/b.o -c -xc - -fPIC
|
||||
#include <stdio.h>
|
||||
@@ -22,5 +22,4 @@ int hello() {
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/c.so $t/b.o -shared
|
||||
readelf -W --dyn-syms $t/c.so > $t/log1
|
||||
! grep -Fq '__global_pointer$' $t/log1 || false
|
||||
readelf -W --dyn-syms $t/c.so | not grep -F '__global_pointer$'
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
# Disable C extension
|
||||
if [ $MACHINE = riscv32 ]; then
|
||||
ISA=rv32g
|
||||
else
|
||||
ISA=rv64g
|
||||
fi
|
||||
|
||||
cat <<EOF | $CC -march=$ISA -O2 -o $t/a.o -c -xc -
|
||||
int add1(int n) { return n + 1; }
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -march=$ISA -O2 -o $t/b.o -c -xc -
|
||||
int add1(int n);
|
||||
int add2(int n) { n += 1; return add1(n); }
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -march=$ISA -O2 -o $t/c.o -c -xc -
|
||||
int add2(int n);
|
||||
int main() {
|
||||
add2(0);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
|
||||
$CC -march=$ISA -B. -nostdlib -O2 -o $t/exe $t/a.o $t/b.o $t/c.o
|
||||
|
||||
$OBJDUMP -d $t/exe | grep -q ff5ff06f # j pc - 0xc
|
||||
@@ -18,5 +18,5 @@ int main() {
|
||||
}
|
||||
EOF
|
||||
|
||||
! $CC -B. -o $t/exe $t/a.o $t/b.o 2> $t/log || false
|
||||
grep -q 'cannot link object files with different floating-point ABI' $t/log
|
||||
not $CC -B. -o $t/exe $t/a.o $t/b.o |&
|
||||
grep 'cannot link object files with different floating-point ABI'
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xassembler -
|
||||
.globl x1
|
||||
.text
|
||||
.p2align 5
|
||||
x1:
|
||||
ret
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -o $t/b.o -c -xassembler -
|
||||
.globl x2
|
||||
.text
|
||||
.p2align 5
|
||||
x2:
|
||||
ret
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -o $t/c.o -c -xc -
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
extern char x1, x2;
|
||||
int main() {
|
||||
printf("%lu %lu %lu\n",
|
||||
(uintptr_t)&x1 % 32,
|
||||
(uintptr_t)&x2 % 32,
|
||||
&x2 - &x1);
|
||||
}
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe $t/a.o $t/b.o $t/c.o
|
||||
$QEMU $t/exe | grep '0 0 32'
|
||||
@@ -71,9 +71,9 @@ int main() {
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe1 $t/a.o $t/b.o $t/c.o -Wl,--no-relax
|
||||
$QEMU $t/exe1 | grep -Eq '^0 ba beef 11beef deadbeef$'
|
||||
$QEMU $t/exe1 | grep -E '^0 ba beef 11beef deadbeef$'
|
||||
|
||||
$CC -B. -o $t/exe2 $t/a.o $t/b.o $t/c.o
|
||||
$QEMU $t/exe2 | grep -Eq '^0 ba beef 11beef deadbeef$'
|
||||
$QEMU $t/exe2 | grep -E '^0 ba beef 11beef deadbeef$'
|
||||
|
||||
$OBJDUMP -d $t/exe2 | grep -A2 '<get_sym2>:' | grep -Eq $'li[ \t]+a0,186$'
|
||||
$OBJDUMP -d $t/exe2 | grep -A2 '<get_sym2>:' | grep -E $'li[ \t]+a0,186$'
|
||||
@@ -2,29 +2,38 @@
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xassembler -
|
||||
.globl get_foo, get_foo2, get_bar, get_baz
|
||||
.globl get_foo, get_foo2, get_bar, get_bar2, get_baz
|
||||
get_foo:
|
||||
lui a0, %hi(foo)
|
||||
add a0, a0, %lo(foo)
|
||||
ret
|
||||
.size get_foo, .-get_foo
|
||||
get_foo2:
|
||||
lui a0, %hi(foo+0x10000000)
|
||||
add a0, a0, %lo(foo)
|
||||
ret
|
||||
.size get_foo2, .-get_foo2
|
||||
get_bar:
|
||||
lui a0, %hi(bar)
|
||||
add a0, a0, %lo(bar)
|
||||
ret
|
||||
.size get_bar, .-get_bar
|
||||
get_bar2:
|
||||
lui a0, %hi(bar+0x1ffff)
|
||||
add a0, a0, %lo(bar+0x1ffff)
|
||||
ret
|
||||
.size get_bar2, .-get_bar2
|
||||
get_baz:
|
||||
lui a0, %hi(baz)
|
||||
add a0, a0, %lo(baz)
|
||||
ret
|
||||
.size get_baz, .-get_baz
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -o $t/b.o -c -xassembler -
|
||||
.globl foo, bar, baz
|
||||
foo = 0xf00
|
||||
bar = 0xba
|
||||
bar = 0
|
||||
baz = 0x11beef
|
||||
EOF
|
||||
|
||||
@@ -34,17 +43,31 @@ cat <<EOF | $CC -o $t/c.o -c -xc -
|
||||
int get_foo();
|
||||
int get_foo2();
|
||||
int get_bar();
|
||||
int get_bar2();
|
||||
int get_baz();
|
||||
|
||||
int main() {
|
||||
printf("%x %x %x %x\n", get_foo(), get_foo2(), get_bar(), get_baz());
|
||||
printf("%x %x %x %x %x\n",
|
||||
get_foo(), get_foo2(), get_bar(), get_bar2(), get_baz());
|
||||
}
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe1 $t/a.o $t/b.o $t/c.o -Wl,--no-relax
|
||||
$QEMU $t/exe1 | grep -q 'f00 10000f00 ba 11beef'
|
||||
$QEMU $t/exe1 | grep 'f00 10000f00 0 1ffff 11beef'
|
||||
|
||||
$CC -B. -o $t/exe2 $t/a.o $t/b.o $t/c.o
|
||||
$QEMU $t/exe2 | grep -q 'f00 10000f00 ba 11beef'
|
||||
$QEMU $t/exe2 | grep 'f00 10000f00 0 1ffff 11beef'
|
||||
|
||||
[ $(stat --format='%s' $t/exe1) -gt $(stat --format='%s' $t/exe2) ]
|
||||
readelf --syms $t/exe1 > $t/log1
|
||||
grep -E ' 10 NOTYPE .* get_foo$' $t/log1
|
||||
grep -E ' 10 NOTYPE .* get_foo2$' $t/log1
|
||||
grep -E ' 10 NOTYPE .* get_bar$' $t/log1
|
||||
grep -E ' 10 NOTYPE .* get_bar2$' $t/log1
|
||||
grep -E ' 10 NOTYPE .* get_baz$' $t/log1
|
||||
|
||||
readelf --syms $t/exe2 > $t/log2
|
||||
grep -E ' 8 NOTYPE .* get_foo$' $t/log2
|
||||
grep -E ' 10 NOTYPE .* get_foo2$' $t/log2
|
||||
grep -E ' 6 NOTYPE .* get_bar$' $t/log2
|
||||
grep -E ' 10 NOTYPE .* get_bar2$' $t/log2
|
||||
grep -E ' 10 NOTYPE .* get_baz$' $t/log2
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc - -O2 -march=rv64g
|
||||
void g() {}
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -o $t/b.o -c -xc - -O2 -march=rv64g
|
||||
void g();
|
||||
void f() { g(); }
|
||||
int main() { f(); }
|
||||
EOF
|
||||
|
||||
$CC -B. -march=rv64g -o $t/exe1 $t/a.o $t/b.o
|
||||
$QEMU $t/exe1
|
||||
$OBJDUMP -d $t/exe1 | grep -E '\bj\b.*<g>'
|
||||
|
||||
|
||||
cat <<EOF | $CC -o $t/c.o -c -xc - -O2 -march=rv64gc
|
||||
void g() {}
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -o $t/d.o -c -xc - -O2 -march=rv64gc
|
||||
void g();
|
||||
void f() { g(); }
|
||||
int main() { f(); }
|
||||
EOF
|
||||
|
||||
$CC -B. -march=rv64g -o $t/exe2 $t/c.o $t/d.o
|
||||
$QEMU $t/exe2
|
||||
$OBJDUMP -d $t/exe2 | grep -E '\bj\b.*<g>'
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xassembler -
|
||||
.globl get_foo, get_bar
|
||||
.type get_foo @function
|
||||
get_foo:
|
||||
lui a0, %hi(foo)
|
||||
add a0, a0, %lo(foo)
|
||||
ret
|
||||
.size get_foo, .-get_foo
|
||||
|
||||
.type get_bar @function
|
||||
get_bar:
|
||||
lui a0, %hi(bar)
|
||||
add a0, a0, %lo(bar)
|
||||
ret
|
||||
.size get_bar, .-get_bar
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -o $t/b.o -c -xassembler -
|
||||
.globl foo, bar
|
||||
foo = 0xf00
|
||||
bar = 0xf00
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -o $t/c.o -c -xc -
|
||||
#include <stdio.h>
|
||||
int get_foo();
|
||||
int get_bar();
|
||||
int main() { printf("%x %x\n", get_foo(), get_bar()); }
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe $t/a.o $t/b.o $t/c.o
|
||||
|
||||
readelf --syms $t/a.o | grep -E ' 10 FUNC .* get_foo$'
|
||||
readelf --syms $t/a.o | grep -E ' 10 FUNC .* get_bar$'
|
||||
|
||||
readelf --syms $t/exe | grep -E ' 8 FUNC .* get_foo$'
|
||||
readelf --syms $t/exe | grep -E ' 8 FUNC .* get_bar$'
|
||||
@@ -10,7 +10,7 @@ foo:
|
||||
EOF
|
||||
|
||||
$CC -B. -shared -o $t/b.so $t/a.o
|
||||
readelf -W --dyn-syms $t/b.so | grep foo | grep -Fq '[VARIANT_CC]'
|
||||
readelf -W --dyn-syms $t/b.so | grep foo | grep -F '[VARIANT_CC]'
|
||||
|
||||
cat <<EOF | $CC -c -o $t/c.o -xc -
|
||||
void foo();
|
||||
@@ -18,4 +18,4 @@ int main() { foo(); }
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe $t/c.o $t/b.so
|
||||
readelf -W --dynamic $t/exe | grep -q RISCV_VARIANT_CC
|
||||
readelf -W --dynamic $t/exe | grep RISCV_VARIANT_CC
|
||||
@@ -18,4 +18,4 @@ int main() { printf("%ld\n", foo()); }
|
||||
EOF
|
||||
|
||||
$CC -B. -static -o $t/exe $t/a.o $t/b.o
|
||||
$QEMU $t/exe | grep -q '^0$'
|
||||
$QEMU $t/exe | grep '^0$'
|
||||
@@ -15,4 +15,4 @@ int main() {
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe $t/a.o -Wl,-defsym=got=_GLOBAL_OFFSET_TABLE_ -no-pie
|
||||
$QEMU $t/exe | grep -Eq '^1'
|
||||
$QEMU $t/exe | grep -E '^1'
|
||||
@@ -22,7 +22,7 @@ EOF
|
||||
$CC -B. -shared -o $t/d.so $t/c.o
|
||||
|
||||
$CC -B. -o $t/exe1 $t/a.o $t/b.o $t/d.so -pie
|
||||
$QEMU $t/exe1 | grep -q '^1 '
|
||||
$QEMU $t/exe1 | grep '^1 '
|
||||
|
||||
$CC -B. -o $t/exe2 $t/a.o $t/b.o $t/d.so -pie -Wl,-no-relax
|
||||
$QEMU $t/exe2 | grep -q '^1 '
|
||||
$QEMU $t/exe2 | grep '^1 '
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
# We use Intel SDE to run programs compiled for APX
|
||||
command -v sde64 >& /dev/null || skip
|
||||
{ sde64 -help; true; } | grep 'Diamond Rapids' || skip
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xassembler - || skip
|
||||
.globl get_foo_addr
|
||||
get_foo_addr:
|
||||
mov foo@GOTPCREL(%rip), %r16
|
||||
mov %r16, %rax
|
||||
ret
|
||||
EOF
|
||||
|
||||
$OBJDUMP -r $t/a.o | grep -w R_X86_64_CODE_4_GOTPCRELX
|
||||
|
||||
cat <<EOF | $CC -o $t/b.o -c -xc - -fPIC
|
||||
void foo() {}
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/c.so -shared $t/b.o
|
||||
|
||||
cat <<EOF | $CC -o $t/d.o -c -xc -
|
||||
#include <stdio.h>
|
||||
|
||||
void foo();
|
||||
void *get_foo_addr();
|
||||
|
||||
int main() {
|
||||
printf("%d %p %p\n", foo == get_foo_addr(), foo, get_foo_addr());
|
||||
}
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe1 $t/a.o $t/b.o $t/d.o
|
||||
sde64 -dmr -- $t/exe1 | grep -E '^1 '
|
||||
$OBJDUMP -d $t/exe1 | grep -A1 '<get_foo_addr>:' | grep -w lea
|
||||
|
||||
$CC -B. -o $t/exe2 $t/a.o $t/c.so $t/d.o
|
||||
sde64 -dmr -- $t/exe2 | grep -E '^1 '
|
||||
$OBJDUMP -d $t/exe2 | grep -A1 '<get_foo_addr>:' | grep -w mov
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
# We use Intel SDE to run programs compiled for APX
|
||||
command -v sde64 >& /dev/null || skip
|
||||
{ sde64 -help; true; } | grep 'Diamond Rapids' || skip
|
||||
|
||||
cat <<'EOF' | $CC -o $t/a.o -c -xassembler - || skip
|
||||
.globl get_foo
|
||||
get_foo:
|
||||
mov foo@gottpoff(%rip), %r16
|
||||
mov %fs:(%r16), %eax
|
||||
ret
|
||||
EOF
|
||||
|
||||
$OBJDUMP -r $t/a.o | grep -w R_X86_64_CODE_4_GOTTPOFF
|
||||
|
||||
cat <<EOF | $CC -o $t/b.o -c -xc -
|
||||
#include <stdio.h>
|
||||
|
||||
_Thread_local int foo = 3;
|
||||
int get_foo();
|
||||
|
||||
int main() {
|
||||
printf("%d %d\n", foo, get_foo());
|
||||
}
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe1 $t/a.o $t/b.o
|
||||
sde64 -dmr -- $t/exe1 | grep -E '^3 3$'
|
||||
|
||||
$CC -B. -o $t/exe2 $t/a.o $t/b.o -Wl,-no-relax
|
||||
sde64 -dmr -- $t/exe2 | grep -E '^3 3$'
|
||||
Loaded 100 of 801 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user