mirror of
https://github.com/rui314/mold.git
synced 2026-11-09 04:49:33 -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 | ||
|
|
3cb551424b | ||
|
|
ee8e50a3f7 | ||
|
|
4bbf85c2ea | ||
|
|
6257f12c1f | ||
|
|
ea460af09c | ||
|
|
a0f9641ae1 | ||
|
|
9204e1dc12 | ||
|
|
396666fd48 | ||
|
|
74024ea0df | ||
|
|
79aac78581 | ||
|
|
04066d1c1a | ||
|
|
2a78b1b333 | ||
|
|
27162691b7 | ||
|
|
fe6090efff | ||
|
|
c7042f416e | ||
|
|
e56b649968 | ||
|
|
0f56a792c3 | ||
|
|
5bb914553f | ||
|
|
26d898e9bd | ||
|
|
eec3bb4478 | ||
|
|
fa3026611b | ||
|
|
a649156e0f | ||
|
|
e2e1146a0b | ||
|
|
baf4f76f53 | ||
|
|
85be067813 | ||
|
|
8e4f7b5383 | ||
|
|
a066efa995 | ||
|
|
f184021eca | ||
|
|
0841ffcb20 | ||
|
|
9cf1165a34 | ||
|
|
9aa4ded614 | ||
|
|
e7e764ccf0 | ||
|
|
5ba5b4960f | ||
|
|
bb57289385 | ||
|
|
0e28522b5c | ||
|
|
882e7eb15b | ||
|
|
3a0bb87dfe | ||
|
|
832973cef5 | ||
|
|
0cb9fc613c | ||
|
|
8931cf81f4 | ||
|
|
ecdd0f71b6 | ||
|
|
cb9344fab8 | ||
|
|
382e45ea30 | ||
|
|
fdaa13c374 | ||
|
|
fa2411cc08 | ||
|
|
a69b332f86 | ||
|
|
20dccf2b77 | ||
|
|
ffd10dd447 | ||
|
|
ce9cdb1bae | ||
|
|
979084b593 | ||
|
|
c562c733ce | ||
|
|
3c6f57cc27 | ||
|
|
c16d6e8cc0 | ||
|
|
91e2daf3bf | ||
|
|
7204c6dec8 | ||
|
|
3441cbb636 | ||
|
|
3070e798f3 | ||
|
|
754354991b | ||
|
|
86fe020558 | ||
|
|
40e7b983d6 | ||
|
|
f69ecd616d | ||
|
|
d683f62e01 | ||
|
|
205a091924 | ||
|
|
7b4d2e77ee | ||
|
|
2c9ba071bc | ||
|
|
de65f6bd75 | ||
|
|
186272a425 | ||
|
|
5fcbf25260 | ||
|
|
16eb5139f1 | ||
|
|
35be7ba887 | ||
|
|
55f0289a41 | ||
|
|
36dd6babe1 | ||
|
|
91b6818b62 | ||
|
|
9e9539144e | ||
|
|
feb9b3eb4e | ||
|
|
4b3fc4e82a | ||
|
|
ed7cc1b85a | ||
|
|
3711ddb95e | ||
|
|
0ee12e4115 | ||
|
|
87c9929df9 | ||
|
|
f5f14d3faa | ||
|
|
5be5ea9ff5 | ||
|
|
f1739bffd0 | ||
|
|
1efbe3f141 | ||
|
|
5ba1c36fbb | ||
|
|
c217134d97 | ||
|
|
fea866d910 | ||
|
|
3b3bc7b41e | ||
|
|
0612ea41f1 | ||
|
|
940ac8125c | ||
|
|
583ba8ade1 | ||
|
|
82feeed693 | ||
|
|
e7e41329ef | ||
|
|
63d990e49a | ||
|
|
8d5b753337 | ||
|
|
431fc39db0 | ||
|
|
7734a93217 | ||
|
|
806d7409da | ||
|
|
2e326cc006 | ||
|
|
e1ace5ea00 | ||
|
|
8e010c2095 | ||
|
|
6758b53bff | ||
|
|
f6822fb0f3 | ||
|
|
f0d39b2460 | ||
|
|
a3e69502b0 | ||
|
|
4645248106 | ||
|
|
80648584cf | ||
|
|
d4c2073cda | ||
|
|
fe7237e454 | ||
|
|
4da257e6fb | ||
|
|
ed617fef05 | ||
|
|
1c321022d0 | ||
|
|
ed4cae9354 | ||
|
|
2d390565c1 | ||
|
|
5e72d88940 | ||
|
|
213f2ecec5 | ||
|
|
23b3778383 | ||
|
|
b9776af9b2 | ||
|
|
25794c6003 | ||
|
|
7fa561699e | ||
|
|
127ba25dab | ||
|
|
6a1742ea90 | ||
|
|
e013d27b54 | ||
|
|
37e175b4a4 | ||
|
|
982b901077 | ||
|
|
f0797bb8f2 | ||
|
|
18f8c425dc | ||
|
|
194239f8c6 | ||
|
|
401e019b05 | ||
|
|
0c74e8274e | ||
|
|
cf5d054eb4 | ||
|
|
d9bf693fb8 | ||
|
|
cf38f81c39 | ||
|
|
2eeb7b411e | ||
|
|
ecc7ce022b | ||
|
|
a6b182b065 | ||
|
|
e30f01fe93 | ||
|
|
ac84d2cdbb | ||
|
|
742ea87d41 | ||
|
|
fa546965ec | ||
|
|
f68657d201 | ||
|
|
23bc92c66f | ||
|
|
c2956484ca | ||
|
|
3f593c62ae | ||
|
|
5b8594979b | ||
|
|
7571305cd1 | ||
|
|
0631f9750e | ||
|
|
b51d6ab5cb | ||
|
|
93c4f35615 | ||
|
|
510b2d68c0 | ||
|
|
d6d8d2e494 | ||
|
|
9e0c1c81dd | ||
|
|
ac5031198c | ||
|
|
11d5426781 | ||
|
|
afc52eea70 | ||
|
|
6248d964fb | ||
|
|
addb5f1675 | ||
|
|
ed22d76ef0 | ||
|
|
a73e9b2dac | ||
|
|
5dfa1cf07c | ||
|
|
4164285a13 | ||
|
|
1f53bd5bfa | ||
|
|
a32d294df8 | ||
|
|
dbaa6d7341 | ||
|
|
23a9e7261d | ||
|
|
5a9919b433 | ||
|
|
1d24b70996 | ||
|
|
f18d1c1ffc | ||
|
|
cd7287f0de | ||
|
|
f05e32df4a |
No files matched your search
@@ -5,14 +5,22 @@ on:
|
||||
- cron: '0 0 * * *'
|
||||
release:
|
||||
types: [ published ]
|
||||
workflow_dispatch:
|
||||
|
||||
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
|
||||
@@ -20,26 +28,24 @@ 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 }}
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: tarballs
|
||||
path: mold-*.tar.gz
|
||||
name: ${{ matrix.target }}
|
||||
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,36 +0,0 @@
|
||||
name: Build x86 tarball
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
build-tarball:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build a tarball
|
||||
run: ./dist.sh
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: tarball
|
||||
path: mold-*.tar.gz
|
||||
+54
-87
@@ -5,35 +5,28 @@ on:
|
||||
env:
|
||||
UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
|
||||
jobs:
|
||||
build-clang:
|
||||
build-sanitizers:
|
||||
strategy:
|
||||
matrix:
|
||||
target:
|
||||
# Disable PCH for the default configuration. This prevents relying on implicit includes.
|
||||
- '-DCMAKE_DISABLE_PRECOMPILE_HEADERS=On'
|
||||
- ''
|
||||
- '-DMOLD_USE_ASAN=On'
|
||||
- '-DMOLD_USE_TSAN=On'
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: rui314/setup-mold@staging
|
||||
- name: install-build-deps
|
||||
run: sudo ./install-build-deps.sh
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
- run: sudo ./install-build-deps.sh
|
||||
- name: build
|
||||
run: |
|
||||
echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
|
||||
sudo apt-get install -y clang++-12
|
||||
sudo apt-get install -y clang-18 clang gcc-multilib gdb dwarfdump zstd
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++-12 ${{ matrix.target }} ..
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=clang-18 -DCMAKE_CXX_COMPILER=clang++-18 ${{ matrix.target }} ..
|
||||
cmake --build . -j$(nproc)
|
||||
- name: test
|
||||
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
|
||||
@@ -41,68 +34,27 @@ jobs:
|
||||
build
|
||||
!build/CMakeFiles
|
||||
|
||||
build-gcc:
|
||||
runs-on: ubuntu-20.04
|
||||
container: gcc:11.1.0
|
||||
build-multi-archs:
|
||||
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
|
||||
|
||||
# Install a RV32 toolchain from third party since it's not available
|
||||
# as an Ubuntu package.
|
||||
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 -
|
||||
|
||||
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
|
||||
echo '/rv32/bin/riscv32-unknown-linux-gnu-g++ -L/usr/riscv32-linux-gnu "$@"' > /usr/bin/riscv32-linux-gnu-g++
|
||||
chmod 755 /usr/bin/riscv32-linux-gnu-{gcc,g++}
|
||||
|
||||
for i in objdump objcopy strip; do
|
||||
ln -sf /rv32/bin/riscv32-unknown-linux-gnu-$i /usr/bin/riscv32-linux-gnu-$i
|
||||
done
|
||||
|
||||
# Install a LoongArch toolchain
|
||||
mkdir /larch
|
||||
wget -O- -q https://github.com/loongson/build-tools/releases/download/2024.06.01/x86_64-cross-tools-loongarch64-binutils_2.42-gcc_14.1.0-glibc_2.39.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
|
||||
|
||||
for i in gcc g++ objdump objcopy strip; do
|
||||
ln -sf /larch/bin/loongarch64-unknown-linux-gnu-$i /usr/bin/loongarch64-linux-gnu-$i
|
||||
done
|
||||
|
||||
wget -O /usr/local/bin/qemu-loongarch64 -q https://github.com/loongson/build-tools/releases/download/2023.08.08/qemu-loongarch64
|
||||
chmod 755 /usr/local/bin/qemu-loongarch64
|
||||
|
||||
# Install Intel SDE CPU emulator for CET-related tests
|
||||
mkdir /sde
|
||||
wget -O- -q https://downloadmirror.intel.com/813591/sde-external-9.33.0-2024-01-07-lin.tar.xz | tar -C /sde --strip-components=1 --xz -xf -
|
||||
ln -s /sde/sde /usr/bin
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
sudo ./install-build-deps.sh
|
||||
sudo ./install-cross-tools.sh
|
||||
sudo .github/workflows/install-extras.sh
|
||||
- name: build
|
||||
run: |
|
||||
echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
cmake --build . -j$(nproc)
|
||||
- name: test
|
||||
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
|
||||
@@ -113,13 +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: |
|
||||
@@ -127,35 +82,31 @@ jobs:
|
||||
cd build
|
||||
cmake ..
|
||||
cmake --build . -j$(nproc)
|
||||
- name: test
|
||||
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-12
|
||||
strategy:
|
||||
matrix:
|
||||
target:
|
||||
# Disable PCH for the default configuration. This prevents relying on implicit includes.
|
||||
- '-DCMAKE_DISABLE_PRECOMPILE_HEADERS=On'
|
||||
- '-DMOLD_USE_ASAN=On'
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
- uses: actions/checkout@v4
|
||||
- name: build
|
||||
run: |
|
||||
echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ${{ matrix.target }} ..
|
||||
cmake ..
|
||||
cmake --build . -j$(sysctl -n hw.physicalcpu)
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: build
|
||||
run: |
|
||||
mkdir build
|
||||
@@ -166,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:
|
||||
@@ -178,5 +129,21 @@ jobs:
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -GNinja -DMOLD_USE_MIMALLOC=OFF -DMOLD_USE_SYSTEM_TBB=ON -DMOLD_USE_MOLD=OFF ..
|
||||
cmake -GNinja -DMOLD_USE_MIMALLOC=OFF -DMOLD_USE_SYSTEM_TBB=ON ..
|
||||
cmake --build . -j $(nproc)
|
||||
|
||||
build-freebsd:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build and test
|
||||
uses: vmactions/freebsd-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
run: |
|
||||
./install-build-deps.sh
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
cmake --build . -j$(nproc)
|
||||
ctest --output-on-failure -j$(nproc)
|
||||
Executable
+61
@@ -0,0 +1,61 @@
|
||||
#!/bin/bash -x
|
||||
|
||||
apt-get update
|
||||
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/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
|
||||
echo '/rv32/bin/riscv32-unknown-linux-gnu-g++ -L/usr/riscv32-linux-gnu "$@"' > /usr/bin/riscv32-linux-gnu-g++
|
||||
chmod 755 /usr/bin/riscv32-linux-gnu-{gcc,g++}
|
||||
|
||||
for i in objdump objcopy strip; do
|
||||
ln -sf /rv32/bin/riscv32-unknown-linux-gnu-$i /usr/bin/riscv32-linux-gnu-$i
|
||||
done
|
||||
|
||||
# Install a LoongArch toolchain
|
||||
mkdir /larch
|
||||
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
|
||||
|
||||
for i in gcc g++ objdump objcopy strip; do
|
||||
ln -sf /larch/bin/loongarch64-unknown-linux-gnu-$i /usr/bin/loongarch64-linux-gnu-$i
|
||||
done
|
||||
|
||||
wget -O /usr/local/bin/qemu-loongarch64 -q https://github.com/loongson/build-tools/releases/download/2024.11.01/qemu-loongarch64
|
||||
chmod 755 /usr/local/bin/qemu-loongarch64
|
||||
|
||||
# Install ARM64 big-endian toolchain
|
||||
mkdir /aarch64be
|
||||
wget -O- -q https://sources.buildroot.net/toolchain-external-arm-aarch64-be/arm-gnu-toolchain-13.3.rel1-x86_64-aarch64_be-none-linux-gnu.tar.xz | tar -C /aarch64be --strip-components=1 --xz -xf -
|
||||
|
||||
ln -sf /aarch64be/aarch64_be-none-linux-gnu/libc /usr/aarch64_be-linux-gnu
|
||||
echo '/aarch64be/bin/aarch64_be-none-linux-gnu-gcc -L/usr/aarch64_be-linux-gnu "$@"' > /usr/bin/aarch64_be-linux-gnu-gcc
|
||||
echo '/aarch64be/bin/aarch64_be-none-linux-gnu-g++ -L/usr/aarch64_be-linux-gnu "$@"' > /usr/bin/aarch64_be-linux-gnu-g++
|
||||
chmod 755 /usr/bin/aarch64_be-linux-gnu-{gcc,g++}
|
||||
|
||||
for i in objdump objcopy strip; do
|
||||
ln -sf /aarch64be/bin/aarch64_be-none-linux-gnu-$i /usr/bin/aarch64_be-linux-gnu-$i
|
||||
done
|
||||
|
||||
# Install SH4 big-endian toolchain
|
||||
mkdir /sh4aeb
|
||||
wget -O- -q https://toolchains.bootlin.com/downloads/releases/toolchains/sh-sh4aeb/tarballs/sh-sh4aeb--glibc--stable-2024.05-1.tar.xz | tar -C /sh4aeb --strip-components=1 --xz -xf -
|
||||
ln -sf /sh4aeb/sh4aeb-buildroot-linux-gnu/sysroot /usr/sh4aeb-linux-gnu
|
||||
echo '/sh4aeb/bin/sh4aeb-linux-gcc -L/usr/sh4aeb-linux-gnu "$@"' > /usr/bin/sh4aeb-linux-gnu-gcc
|
||||
echo '/sh4aeb/bin/sh4aeb-linux-g++ -L/usr/sh4aeb-linux-gnu "$@"' > /usr/bin/sh4aeb-linux-gnu-g++
|
||||
chmod 755 /usr/bin/sh4aeb-linux-gnu-{gcc,g++}
|
||||
|
||||
for i in objdump objcopy strip; do
|
||||
ln -sf /sh4aeb/bin/sh4aeb-linux-$i /usr/bin/sh4aeb-linux-gnu-$i
|
||||
done
|
||||
|
||||
# Install Intel SDE CPU emulator for CET-related tests
|
||||
mkdir /sde
|
||||
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
|
||||
+81
-79
@@ -44,18 +44,12 @@
|
||||
# features and behave exactly the same.
|
||||
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(mold VERSION 2.33.0)
|
||||
project(mold VERSION 2.37.1)
|
||||
|
||||
include(CMakeDependentOption)
|
||||
include(CheckSymbolExists)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# Build mold itself using mold if -DMOLD_USE_MOLD=ON
|
||||
option(MOLD_USE_MOLD "Use mold to build mold" OFF)
|
||||
if(MOLD_USE_MOLD)
|
||||
add_link_options(-fuse-ld=mold -Wl,--gdb-index)
|
||||
endif()
|
||||
|
||||
add_executable(mold)
|
||||
target_compile_features(mold PRIVATE cxx_std_20)
|
||||
|
||||
@@ -65,11 +59,23 @@ else()
|
||||
target_link_libraries(mold PRIVATE ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
|
||||
# Build mold itself using mold if -DMOLD_USE_MOLD=ON
|
||||
option(MOLD_USE_MOLD "Use mold to build mold" OFF)
|
||||
if(MOLD_USE_MOLD)
|
||||
target_link_options(mold PRIVATE -fuse-ld=mold -Wl,--gdb-index)
|
||||
|
||||
if(CMAKE_BUILD_TYPE MATCHES "^Rel")
|
||||
target_link_options(mold PRIVATE -Wl,--gc-sections,--icf=safe)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT "${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "MSVC")
|
||||
target_compile_options(mold PRIVATE
|
||||
-fno-exceptions
|
||||
-fno-unwind-tables
|
||||
-fno-asynchronous-unwind-tables
|
||||
-ffunction-sections
|
||||
-fdata-sections
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wno-sign-compare
|
||||
@@ -174,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
|
||||
@@ -187,7 +193,6 @@ if(MOLD_USE_MIMALLOC)
|
||||
if(MOLD_USE_SYSTEM_MIMALLOC)
|
||||
find_package(mimalloc REQUIRED)
|
||||
target_link_libraries(mold PRIVATE mimalloc)
|
||||
target_compile_definitions(mold PRIVATE USE_SYSTEM_MIMALLOC)
|
||||
else()
|
||||
function(mold_add_mimalloc)
|
||||
set(MI_BUILD_STATIC ON CACHE INTERNAL "")
|
||||
@@ -252,7 +257,7 @@ if(NOT APPLE AND NOT WIN32)
|
||||
# Remove the default `lib` prefix
|
||||
set_target_properties(mold-wrapper PROPERTIES PREFIX "")
|
||||
target_link_libraries(mold-wrapper PRIVATE ${CMAKE_DL_LIBS})
|
||||
target_sources(mold-wrapper PRIVATE elf/mold-wrapper.c)
|
||||
target_sources(mold-wrapper PRIVATE src/mold-wrapper.c)
|
||||
endif()
|
||||
|
||||
# If atomics doesn't work by default, add -latomic.
|
||||
@@ -278,21 +283,22 @@ if(NOT APPLE AND NOT MSVC)
|
||||
endif()
|
||||
|
||||
check_symbol_exists(madvise sys/mman.h HAVE_MADVISE)
|
||||
check_symbol_exists(uname sys/utsname.h HAVE_UNAME)
|
||||
|
||||
# Create a .cc file containing the current git hash for `mold --version`.
|
||||
add_custom_target(git_hash
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DSOURCE_DIR=${CMAKE_SOURCE_DIR}
|
||||
-DOUTPUT_FILE=${CMAKE_BINARY_DIR}/git-hash.cc
|
||||
-P ${CMAKE_SOURCE_DIR}/common/update-git-hash.cmake
|
||||
DEPENDS common/update-git-hash.cmake
|
||||
-P ${CMAKE_SOURCE_DIR}/lib/update-git-hash.cmake
|
||||
DEPENDS lib/update-git-hash.cmake
|
||||
BYPRODUCTS git-hash.cc
|
||||
VERBATIM)
|
||||
|
||||
add_dependencies(mold git_hash)
|
||||
|
||||
# Create config.h file
|
||||
configure_file(common/config.h.in config.h)
|
||||
configure_file(lib/config.h.in config.h)
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
# Almost all functions are template in mold which take a target type
|
||||
@@ -304,39 +310,48 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
# compiler instances. This is hacky but greatly reduces compile time
|
||||
# on a multicore machine.
|
||||
list(APPEND MOLD_ELF_TARGETS
|
||||
X86_64 I386 ARM64 ARM32 RV32LE RV32BE RV64LE RV64BE PPC32 PPC64V1 PPC64V2
|
||||
S390X SPARC64 M68K SH4 ALPHA LOONGARCH32 LOONGARCH64)
|
||||
X86_64 I386 ARM64LE ARM64BE ARM32 RV32LE RV32BE RV64LE RV64BE PPC32
|
||||
PPC64V1 PPC64V2 S390X SPARC64 M68K SH4LE SH4BE LOONGARCH32 LOONGARCH64 ARC)
|
||||
|
||||
list(APPEND MOLD_ELF_TEMPLATE_FILES
|
||||
elf/arch-loongarch.cc
|
||||
elf/arch-riscv.cc
|
||||
elf/cmdline.cc
|
||||
elf/gc-sections.cc
|
||||
elf/gdb-index.cc
|
||||
elf/icf.cc
|
||||
elf/input-files.cc
|
||||
elf/input-sections.cc
|
||||
elf/linker-script.cc
|
||||
elf/main.cc
|
||||
elf/mapfile.cc
|
||||
elf/output-chunks.cc
|
||||
elf/passes.cc
|
||||
elf/relocatable.cc
|
||||
elf/shrink-sections.cc
|
||||
elf/thunks.cc
|
||||
elf/tls.cc
|
||||
src/arch-arm64.cc
|
||||
src/arch-loongarch.cc
|
||||
src/arch-riscv.cc
|
||||
src/arch-sh4.cc
|
||||
src/cmdline.cc
|
||||
src/filetype.cc
|
||||
src/gc-sections.cc
|
||||
src/gdb-index.cc
|
||||
src/icf.cc
|
||||
src/input-files.cc
|
||||
src/input-sections.cc
|
||||
src/linker-script.cc
|
||||
src/main.cc
|
||||
src/mapfile.cc
|
||||
src/output-chunks.cc
|
||||
src/passes.cc
|
||||
src/relocatable.cc
|
||||
src/shrink-sections.cc
|
||||
src/thunks.cc
|
||||
src/tls.cc
|
||||
)
|
||||
|
||||
if(WIN32 AND NOT MINGW)
|
||||
list(APPEND MOLD_ELF_TEMPLATE_FILES elf/lto-win32.cc)
|
||||
list(APPEND MOLD_ELF_TEMPLATE_FILES src/lto-win32.cc)
|
||||
else()
|
||||
list(APPEND MOLD_ELF_TEMPLATE_FILES elf/lto-unix.cc)
|
||||
list(APPEND MOLD_ELF_TEMPLATE_FILES src/lto-unix.cc)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND MOLD_ELF_TEMPLATE_FILES elf/subprocess-win32.cc)
|
||||
list(APPEND MOLD_ELF_TEMPLATE_FILES
|
||||
src/output-file-win32.cc
|
||||
src/subprocess-win32.cc
|
||||
)
|
||||
else()
|
||||
list(APPEND MOLD_ELF_TEMPLATE_FILES elf/subprocess-unix.cc)
|
||||
list(APPEND MOLD_ELF_TEMPLATE_FILES
|
||||
src/output-file-unix.cc
|
||||
src/subprocess-unix.cc
|
||||
)
|
||||
endif()
|
||||
|
||||
function(mold_instantiate_templates SOURCE TARGET)
|
||||
@@ -358,59 +373,47 @@ endforeach()
|
||||
|
||||
# Add other non-template source files.
|
||||
target_sources(mold PRIVATE
|
||||
common/compress.cc
|
||||
common/crc32.cc
|
||||
common/demangle.cc
|
||||
common/filepath.cc
|
||||
common/glob.cc
|
||||
common/hyperloglog.cc
|
||||
common/malloc.cc
|
||||
common/multi-glob.cc
|
||||
common/perf.cc
|
||||
common/random.cc
|
||||
common/tar.cc
|
||||
elf/arch-alpha.cc
|
||||
elf/arch-arm32.cc
|
||||
elf/arch-arm64.cc
|
||||
elf/arch-i386.cc
|
||||
elf/arch-m68k.cc
|
||||
elf/arch-ppc32.cc
|
||||
elf/arch-ppc64v1.cc
|
||||
elf/arch-ppc64v2.cc
|
||||
elf/arch-s390x.cc
|
||||
elf/arch-sh4.cc
|
||||
elf/arch-sparc64.cc
|
||||
elf/arch-x86-64.cc
|
||||
elf/config.cc
|
||||
elf/elf.cc
|
||||
git-hash.cc
|
||||
lib/compress.cc
|
||||
lib/crc32.cc
|
||||
lib/demangle.cc
|
||||
lib/filepath.cc
|
||||
lib/glob.cc
|
||||
lib/hyperloglog.cc
|
||||
lib/mimalloc.cc
|
||||
lib/multi-glob.cc
|
||||
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
|
||||
src/arch-ppc32.cc
|
||||
src/arch-ppc64v1.cc
|
||||
src/arch-ppc64v2.cc
|
||||
src/arch-s390x.cc
|
||||
src/arch-sparc64.cc
|
||||
src/arch-x86-64.cc
|
||||
src/config.cc
|
||||
src/elf.cc
|
||||
third-party/rust-demangle/rust-demangle.c
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
target_sources(mold PRIVATE
|
||||
common/jobs-win32.cc
|
||||
common/mapped-file-win32.cc
|
||||
common/signal-win32.cc
|
||||
lib/jobs-win32.cc
|
||||
lib/mapped-file-win32.cc
|
||||
lib/signal-win32.cc
|
||||
)
|
||||
else()
|
||||
target_sources(mold PRIVATE
|
||||
common/jobs-unix.cc
|
||||
common/mapped-file-unix.cc
|
||||
common/signal-unix.cc
|
||||
lib/jobs-unix.cc
|
||||
lib/mapped-file-unix.cc
|
||||
lib/signal-unix.cc
|
||||
)
|
||||
endif()
|
||||
|
||||
# Add frequently included header files for pre-compiling.
|
||||
# target_precompile_headers is supported by CMake 3.16.0 or newer.
|
||||
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.16.0")
|
||||
# ccache needs this flag along with `sloppiness = pch_defines,time_macros`
|
||||
# to enable caching
|
||||
if(NOT MSVC)
|
||||
target_compile_options(mold PRIVATE -fpch-preprocess)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(CTest)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
@@ -425,7 +428,7 @@ if(BUILD_TESTING)
|
||||
endif()
|
||||
|
||||
if(${UNIX})
|
||||
add_subdirectory(test/elf)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -433,7 +436,6 @@ if(NOT CMAKE_SKIP_INSTALL_RULES)
|
||||
install(TARGETS mold RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
install(FILES docs/mold.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1/)
|
||||
install(FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR})
|
||||
install(FILES "LICENSE.third-party" DESTINATION "${CMAKE_INSTALL_DOCDIR}")
|
||||
|
||||
function(mold_install_relative_symlink OLD NEW)
|
||||
install(CODE "
|
||||
|
||||
@@ -24,8 +24,7 @@ free to [file a bug report](https://github.com/rui314/mold/issues).
|
||||
|
||||
mold supports x86-64, i386, ARM64, ARM32, 64-bit/32-bit little/big-endian
|
||||
RISC-V, 32-bit PowerPC, 64-bit big-endian PowerPC ELFv1, 64-bit little-endian
|
||||
PowerPC ELFv2, s390x, 64-bit/32-bit LoongArch, SPARC64, m68k, SH-4, and DEC
|
||||
Alpha.
|
||||
PowerPC ELFv2, s390x, 64-bit/32-bit LoongArch, SPARC64, m68k, and SH-4.
|
||||
|
||||
## Why does linking speed matter?
|
||||
|
||||
@@ -77,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
|
||||
|
||||
@@ -133,7 +132,7 @@ may be able to remove the `linker = "clang"` line.
|
||||
|
||||
```toml
|
||||
[target.x86_64-unknown-linux-gnu]
|
||||
rustflags = ["-C", "link-arg=-fuse-ld=/path/to/mold"]
|
||||
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
||||
```
|
||||
|
||||
If you want to use mold for all projects, add the above snippet to
|
||||
@@ -159,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`
|
||||
@@ -265,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,5 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#ifdef USE_SYSTEM_MIMALLOC
|
||||
# include <mimalloc-new-delete.h>
|
||||
#endif
|
||||
@@ -1,5 +0,0 @@
|
||||
#if _WIN32
|
||||
# include "output-file-win32.h"
|
||||
#else
|
||||
# include "output-file-unix.h"
|
||||
#endif
|
||||
@@ -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,25 +18,29 @@
|
||||
# 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
|
||||
# link glibc to mold, and a binary linked against a newer version of glibc
|
||||
# won't work on a system with an older version of glibc.
|
||||
#
|
||||
# We need GCC 10 or newer to build mold. If GCC 10 is not available on an
|
||||
# old Debian version, we'll build it ourselves.
|
||||
# We want to build mold with Clang rather than GCC because mold's
|
||||
# Identical Code Folding works best with the LLVM address significance
|
||||
# table (.llvm_addrsig). Building a release binary with GCC yields a
|
||||
# slightly larger binary than Clang's.
|
||||
#
|
||||
# You may need to run the following command to use Docker with Qemu:
|
||||
# We need a recent version of Clang to build mold. If it's not available
|
||||
# via apt-get, we'll build it ourselves.
|
||||
#
|
||||
# $ 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
|
||||
}
|
||||
|
||||
@@ -56,97 +60,135 @@ 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 && \
|
||||
ln -sf /usr/lib64/libstdc++.so.6 /usr/lib/x86_64-linux-gnu/libstdc++.so.6 && \
|
||||
rm -rf /build
|
||||
|
||||
# Build GNU binutils 2.43
|
||||
RUN mkdir /build && \
|
||||
cd /build && \
|
||||
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 && \
|
||||
rm -fr /build
|
||||
|
||||
# Build Python 3.12.7
|
||||
RUN mkdir /build && \
|
||||
cd /build && \
|
||||
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 && \
|
||||
rm -rf /build
|
||||
|
||||
# Build LLVM 19
|
||||
RUN mkdir /build && \
|
||||
cd /build && \
|
||||
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 && \
|
||||
cmake --build . -j\$(nproc) && \
|
||||
cmake --install . --strip && \
|
||||
rm -rf /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 GCC for these targets with Qemu becuase
|
||||
# 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.
|
||||
[ $arch = aarch64 ] && digest=d5ed76c5265576982e6599b6f12392290d9b52b315b19b28b640aaba6e8af002
|
||||
[ $arch = arm ] && digest=bede2623dae269454c5b6dd4af15a10810a5f4ef75963d4eb6531628f98bd633
|
||||
[ $arch = ppc64le ] && digest=255f385e735469493b3465befad59a16f9d46f41d0b50e4fa6d5928c5ee7702a
|
||||
[ $arch = s390x ] && digest=96fb9ce5d3ce7f3dab7c34c18edfee093904cbc7fc19162dbcca22b2cc273b9d
|
||||
|
||||
cat <<EOF | $docker_build
|
||||
FROM debian:bullseye-20231030@sha256:$digest
|
||||
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 && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends build-essential gcc-10 g++-10 cmake && \
|
||||
ln -sf /usr/bin/gcc-10 /usr/bin/cc && \
|
||||
ln -sf /usr/bin/g++-10 /usr/bin/c++ && \
|
||||
apt-get install -y --no-install-recommends build-essential gcc-10 g++-10 clang-16 cmake && \
|
||||
ln -sf /usr/bin/clang-16 /usr/bin/clang && \
|
||||
ln -sf /usr/bin/clang++-16 /usr/bin/clang++ && \
|
||||
rm -rf /var/lib/apt/lists
|
||||
EOF
|
||||
;;
|
||||
riscv64)
|
||||
cat <<EOF | $docker_build
|
||||
FROM riscv64/debian:sid-20240311@sha256:8c02dbe4faa999b588e873cc1759dd9b340f39daf4d7aabdb2c1a87cdc586459
|
||||
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 && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends build-essential gcc-12 g++-12 cmake && \
|
||||
ln -sf /usr/bin/gcc-12 /usr/bin/cc && \
|
||||
ln -sf /usr/bin/g++-12 /usr/bin/c++ && \
|
||||
apt-get install -y --no-install-recommends build-essential gcc-14 g++-14 clang-18 cmake && \
|
||||
ln -sf /usr/bin/clang-18 /usr/bin/clang && \
|
||||
ln -sf /usr/bin/clang++-18 /usr/bin/clang++ && \
|
||||
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
|
||||
@@ -156,17 +198,22 @@ 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
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DMOLD_MOSTLY_STATIC=On /mold
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DMOLD_MOSTLY_STATIC=1 -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ /mold
|
||||
cmake --build . -j\$(nproc)
|
||||
ctest -j\$(nproc)
|
||||
cmake --install .
|
||||
cmake -DMOLD_USE_MOLD=1 .
|
||||
cmake --build . -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
|
||||
"
|
||||
@@ -1,3 +1,8 @@
|
||||
[This document was written in 2020, and the contents are outdated.
|
||||
Specifically, we no longer believe that object preloading is a good
|
||||
idea. That being said, most of the points in this document still hold
|
||||
even today. Therefore, I'll keep this document as-is.]
|
||||
|
||||
## Design and implementation of mold
|
||||
|
||||
For the rest of this documentation, I'll explain the design and the
|
||||
|
||||
+13
-10
@@ -1,6 +1,6 @@
|
||||
.\" generated with Ronn-NG/v0.9.1
|
||||
.\" http://github.com/apjanke/ronn-ng/tree/0.9.1
|
||||
.TH "MOLD" "1" "August 2024" ""
|
||||
.TH "MOLD" "1" "February 2025" ""
|
||||
.SH "NAME"
|
||||
\fBmold\fR \- a modern linker
|
||||
.SH "SYNOPSIS"
|
||||
@@ -10,9 +10,9 @@
|
||||
.SS "How to use"
|
||||
See \fIhttps://github\.com/rui314/mold#how\-to\-use\fR\.
|
||||
.SS "Compatibility"
|
||||
\fBMold\fR is designed to be a drop\-in replacement for the GNU linkers for linking user\-land programs\. If your user\-land program cannot be built due to missing command\-line options, please file a bug at \fIhttps://github\.com/rui314/mold/issues\fR\.
|
||||
\fBmold\fR is designed to be a drop\-in replacement for the GNU linkers for linking user\-land programs\. If your user\-land program cannot be built due to missing command\-line options, please file a bug at \fIhttps://github\.com/rui314/mold/issues\fR\.
|
||||
.P
|
||||
Mold supports a very limited set of linker script features, which is just sufficient to read \fB/usr/lib/x86_64\-linux\-gnu/libc\.so\fR on Linux systems (on Linux, that file is contrary to its name not a shared library but an ASCII linker script that loads a real \fBlibc\.so\fR file\.)
|
||||
\fBmold\fR supports a very limited set of linker script features, which is just sufficient to read \fB/usr/lib/x86_64\-linux\-gnu/libc\.so\fR on Linux systems (on Linux, that file is contrary to its name not a shared library but an ASCII linker script that loads a real \fBlibc\.so\fR file\.)
|
||||
.P
|
||||
Beyond that, we have no plan to support any additional linker script features\. The linker script is an ad\-hoc, over\-designed, complex language which we believe needs to be replaced by a simpler mechanism\. We have a plan to add a replacement for the linker script to \fBmold\fR instead\.
|
||||
.SS "Archive symbol resolution"
|
||||
@@ -40,13 +40,11 @@ Typically, an ELF module that exports a symbol also imports the same symbol\. Su
|
||||
.P
|
||||
Let's take \fBmalloc\fR as an example\. Assume that you define your version of \fBmalloc\fR in your main executable file\. Then, all \fBmalloc\fR calls from any module are resolved to your function instead of the one in libc, because the executable is always at the beginning of the dynamic symbol search list\. Note that even \fBmalloc\fR calls within libc are resolved to your definition since libc exports and imports \fBmalloc\fR\. Therefore, by defining \fBmalloc\fR yourself, you can overwrite a library function, and the \fBmalloc\fR in libc becomes dead code\.
|
||||
.P
|
||||
These Unix semantics are tricky and sometimes considered harmful\. For example, assume that you accidentally define \fBatoi\fR as a global function in your executable that behaves completely differently from the one in the C standard\. Then, all \fBatoi\fR function calls from any modules (even function calls within libc) are redirected to your function instead of the one in libc, which will very likely cause a problem\.
|
||||
.P
|
||||
That is a somewhat surprising consequence for an accidental name conflict\. On the other hand, this semantic is sometimes useful because it allows users to override library functions without rebuilding modules containing them\.
|
||||
These Unix semantics are tricky and sometimes considered harmful\. For example, assume that you accidentally define \fBatoi\fR as a global function in your executable that behaves completely differently from the one in the C standard\. Then, all \fBatoi\fR function calls from any modules (even function calls within libc) are redirected to your function instead of the one in libc, which will very likely cause a problem\. That is a somewhat surprising consequence for an accidental name conflict\. On the other hand, this semantic is sometimes useful because it allows users to override library functions without rebuilding modules containing them\.
|
||||
.P
|
||||
Whether good or bad, you should keep these semantics in mind to understand Unix linkers' behaviors\.
|
||||
.SS "Build reproducibility"
|
||||
\fBmold\fR's output is deterministic\. That is, if you pass the same object files and the same command\-line options to the same version of \fBmold\fR, it is guaranteed that \fBmold\fR produces the bit\-by\-bit identical output\. The linker's internal randomness, such as the timing of thread scheduling or iteration orders of hash tables, doesn't affect the output\.
|
||||
\fBmold\fR's output is deterministic\. That is, if you pass the same object files and the same command\-line options to the same version of \fBmold\fR, it is guaranteed that \fBmold\fR produces the bit\-for\-bit identical output\. The linker's internal randomness, such as the timing of thread scheduling or iteration orders of hash tables, doesn't affect the output\.
|
||||
.P
|
||||
\fBmold\fR does not have any host\-specific default settings\. This is contrary to the GNU linkers, for which some configurable values, such as system\-dependent library search paths, are hard\-coded\. \fBmold\fR depends only on its command\-line arguments\.
|
||||
.SH "OPTION NOTATIONS"
|
||||
@@ -67,7 +65,7 @@ Synonym for \fB\-\-color\-diagnostics=auto\fR\.
|
||||
\fB\-\-no\-color\-diagnostics\fR
|
||||
Synonym for \fB\-\-color\-diagnostics=never\fR\.
|
||||
.TP
|
||||
\fB\-\-detach\fR, `\-\-no\-detach
|
||||
\fB\-\-detach\fR, \fB\-\-no\-detach\fR
|
||||
Permit or do not permit mold to create a debug info file in the background\.
|
||||
.TP
|
||||
\fB\-\-fork\fR, \fB\-\-no\-fork\fR
|
||||
@@ -262,8 +260,8 @@ By default, shared libraries given to the linker are unconditionally added to th
|
||||
.IP
|
||||
The \fB\-\-no\-as\-needed\fR option restores the default behavior for subsequent files\.
|
||||
.TP
|
||||
\fB\-\-build\-id\fR=[ \fBmd5\fR | \fBsha1\fR | \fBsha256\fR | \fBuuid\fR | \fB0x\fR\fIhexstring\fR | \fBnone\fR ]
|
||||
Create a \fB\.note\.gnu\.build\-id\fR section containing a byte string to uniquely identify an output file\. \fBsha256\fR compute a 256\-bit cryptographic hash of an output file and set it to build\-id\. \fBmd5\fR and \fBsha1\fR compute the same hash but truncate it to 128 and 160 bits, respectively, before setting it to build\-id\. \fBuuid\fR sets a random 128\-bit UUID\. \fB0x\fR\fIhexstring\fR sets \fIhexstring\fR\.
|
||||
\fB\-\-build\-id\fR=[ \fBmd5\fR | \fBsha1\fR | \fBsha256\fR | \fBfast\fR | \fBuuid\fR | \fB0x\fR\fIhexstring\fR | \fBnone\fR ]
|
||||
Create a \fB\.note\.gnu\.build\-id\fR section containing a byte string to uniquely identify an output file\. \fBsha256\fR compute a 256\-bit cryptographic hash of an output file and set it to build\-id\. \fBmd5\fR and \fBsha1\fR compute the same hash but truncate it to 128 and 160 bits, respectively, before setting it to build\-id\. \fBuuid\fR sets a random 128\-bit UUID\. \fB0x\fR\fIhexstring\fR sets \fIhexstring\fR\. \fBfast\fR is a synonym for \fBsha256\fR\.
|
||||
.TP
|
||||
\fB\-\-build\-id\fR
|
||||
Synonym for \fB\-\-build\-id=sha256\fR\.
|
||||
@@ -355,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
|
||||
|
||||
+25
-11
@@ -13,12 +13,12 @@ See <https://github.com/rui314/mold#how-to-use>.
|
||||
|
||||
### Compatibility
|
||||
|
||||
**Mold** is designed to be a drop-in replacement for the GNU linkers for
|
||||
`mold` is designed to be a drop-in replacement for the GNU linkers for
|
||||
linking user-land programs. If your user-land program cannot be built due to
|
||||
missing command-line options, please file a bug at
|
||||
<https://github.com/rui314/mold/issues>.
|
||||
|
||||
Mold supports a very limited set of linker script features, which is just
|
||||
`mold` supports a very limited set of linker script features, which is just
|
||||
sufficient to read `/usr/lib/x86_64-linux-gnu/libc.so` on Linux systems (on
|
||||
Linux, that file is contrary to its name not a shared library but an ASCII
|
||||
linker script that loads a real `libc.so` file.)
|
||||
@@ -104,11 +104,10 @@ assume that you accidentally define `atoi` as a global function in your
|
||||
executable that behaves completely differently from the one in the C standard.
|
||||
Then, all `atoi` function calls from any modules (even function calls within
|
||||
libc) are redirected to your function instead of the one in libc, which will
|
||||
very likely cause a problem.
|
||||
|
||||
That is a somewhat surprising consequence for an accidental name conflict. On
|
||||
the other hand, this semantic is sometimes useful because it allows users to
|
||||
override library functions without rebuilding modules containing them.
|
||||
very likely cause a problem. That is a somewhat surprising consequence for an
|
||||
accidental name conflict. On the other hand, this semantic is sometimes useful
|
||||
because it allows users to override library functions without rebuilding
|
||||
modules containing them.
|
||||
|
||||
Whether good or bad, you should keep these semantics in mind to understand
|
||||
Unix linkers' behaviors.
|
||||
@@ -117,7 +116,7 @@ Unix linkers' behaviors.
|
||||
|
||||
`mold`'s output is deterministic. That is, if you pass the same object files
|
||||
and the same command-line options to the same version of `mold`, it is
|
||||
guaranteed that `mold` produces the bit-by-bit identical output. The linker's
|
||||
guaranteed that `mold` produces the bit-for-bit identical output. The linker's
|
||||
internal randomness, such as the timing of thread scheduling or iteration
|
||||
orders of hash tables, doesn't affect the output.
|
||||
|
||||
@@ -152,7 +151,7 @@ but as `-o magic`.
|
||||
* `--no-color-diagnostics`:
|
||||
Synonym for `--color-diagnostics=never`.
|
||||
|
||||
* `--detach`, `--no-detach:
|
||||
* `--detach`, `--no-detach`:
|
||||
Permit or do not permit mold to create a debug info file in the background.
|
||||
|
||||
* `--fork`, `--no-fork`:
|
||||
@@ -467,13 +466,13 @@ but as `-o magic`.
|
||||
The `--no-as-needed` option restores the default behavior for subsequent
|
||||
files.
|
||||
|
||||
* `--build-id`=[ `md5` | `sha1` | `sha256` | `uuid` | `0x`_hexstring_ | `none` ]:
|
||||
* `--build-id`=[ `md5` | `sha1` | `sha256` | `fast` | `uuid` | `0x`_hexstring_ | `none` ]:
|
||||
Create a `.note.gnu.build-id` section containing a byte string to uniquely
|
||||
identify an output file. `sha256` compute a 256-bit cryptographic hash of an
|
||||
output file and set it to build-id. `md5` and `sha1` compute the same hash
|
||||
but truncate it to 128 and 160 bits, respectively, before setting it to
|
||||
build-id. `uuid` sets a random 128-bit UUID. `0x`_hexstring_ sets
|
||||
_hexstring_.
|
||||
_hexstring_. `fast` is a synonym for `sha256`.
|
||||
|
||||
* `--build-id`:
|
||||
Synonym for `--build-id=sha256`.
|
||||
@@ -618,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
|
||||
|
||||
@@ -1,330 +0,0 @@
|
||||
// Alpha is a 64-bit RISC ISA developed by DEC (Digital Equipment
|
||||
// Corporation) in the early '90s. It aimed to be an ISA that would last
|
||||
// 25 years. DEC expected Alpha would become 1000x faster during that time
|
||||
// span. Since the ISA was developed from scratch for future machines,
|
||||
// it's 64-bit from the beginning. There's no 32-bit variant.
|
||||
//
|
||||
// DEC ported its own Unix (Tru64) to Alpha. Microsoft also ported Windows
|
||||
// NT to it. But it wasn't a huge commercial success.
|
||||
//
|
||||
// DEC was acquired by Compaq in 1997. In the late '90s, Intel and
|
||||
// Hewlett-Packard were advertising that their upcoming Itanium processor
|
||||
// would achieve significantly better performance than RISC processors, so
|
||||
// Compaq decided to discontinue the Alpha processor line to switch to
|
||||
// Itanium. Itanium resulted in a miserable failure, but it still suceeded
|
||||
// to wipe out several RISC processors just by promising overly optimistic
|
||||
// perf numbers. Alpha as an ISA would probably have been fine after 25
|
||||
// years since its introduction (which is 1992 + 25 = 2017), but the
|
||||
// company and its market didn't last that long.
|
||||
//
|
||||
// From the linker's point of view, there are a few peculiarities in its
|
||||
// psABI as shown below:
|
||||
//
|
||||
// - Alpha lacks PC-relative memory load/store instructions, so it uses
|
||||
// register-relative load/store instructions in position-independent
|
||||
// code. Specifically, GP (which is an alias for $r29) is always
|
||||
// maintained to refer to .got+0x8000, and global variables' addresses
|
||||
// are loaded in a GP-relative manner.
|
||||
//
|
||||
// - It looks like even function addresses are first loaded to register
|
||||
// in a GP-relative manner before calling it. We can relax it to
|
||||
// convert the instruction sequence with a direct branch instruction,
|
||||
// but by default, object files don't use a direct branch to call a
|
||||
// function. Therefore, by default, we don't need to create a PLT.
|
||||
// Any function call is made by first reading its address from GOT and
|
||||
// jump to the address.
|
||||
|
||||
#include "mold.h"
|
||||
|
||||
namespace mold::elf {
|
||||
|
||||
using E = ALPHA;
|
||||
|
||||
// A 32-bit immediate can be materialized in a register with a "load high"
|
||||
// and a "load low" instruction sequence. The first instruction sets the
|
||||
// upper 16 bits in a register, and the second one set the lower 16
|
||||
// bits. When doing so, they sign-extend an immediate. Therefore, if the
|
||||
// 15th bit of an immediate happens to be 1, setting a "low half" value
|
||||
// negates the upper 16 bit values that has already been set in a
|
||||
// register. To compensate that, we need to add 0x8000 when setting the
|
||||
// upper 16 bits.
|
||||
static u32 hi(u32 val) {
|
||||
return bits(val + 0x8000, 31, 16);
|
||||
}
|
||||
|
||||
template <>
|
||||
void write_plt_header(Context<E> &ctx, u8 *buf) {}
|
||||
|
||||
template <>
|
||||
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {}
|
||||
|
||||
template <>
|
||||
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {}
|
||||
|
||||
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_ALPHA_SREL32:
|
||||
*(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);
|
||||
|
||||
ElfRel<E> *dynrel = nullptr;
|
||||
if (ctx.reldyn)
|
||||
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
|
||||
file.reldyn_offset + this->reldyn_offset);
|
||||
|
||||
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;
|
||||
u64 GP = ctx.got->shdr.sh_addr + 0x8000;
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_ALPHA_REFQUAD:
|
||||
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
|
||||
break;
|
||||
case R_ALPHA_GPREL32:
|
||||
*(ul32 *)loc = S + A - GP;
|
||||
break;
|
||||
case R_ALPHA_LITERAL:
|
||||
if (A)
|
||||
*(ul16 *)loc = ctx.extra.got->get_addr(sym, A) - GP;
|
||||
else
|
||||
*(ul16 *)loc = GOT + G - GP;
|
||||
break;
|
||||
case R_ALPHA_BRSGP:
|
||||
*(ul32 *)loc |= bits(S + A - P - 4, 22, 0);
|
||||
break;
|
||||
case R_ALPHA_GPDISP:
|
||||
*(ul16 *)loc = hi(GP - P);
|
||||
*(ul16 *)(loc + A) = GP - P;
|
||||
break;
|
||||
case R_ALPHA_SREL32:
|
||||
*(ul32 *)loc = S + A - P;
|
||||
break;
|
||||
case R_ALPHA_GPRELHIGH:
|
||||
*(ul16 *)loc = hi(S + A - GP);
|
||||
break;
|
||||
case R_ALPHA_GPRELLOW:
|
||||
*(ul16 *)loc = S + A - GP;
|
||||
break;
|
||||
case R_ALPHA_TLSGD:
|
||||
*(ul16 *)loc = sym.get_tlsgd_addr(ctx) - GP;
|
||||
break;
|
||||
case R_ALPHA_TLSLDM:
|
||||
*(ul16 *)loc = ctx.got->get_tlsld_addr(ctx) - GP;
|
||||
break;
|
||||
case R_ALPHA_DTPRELHI:
|
||||
*(ul16 *)loc = hi(S + A - ctx.dtp_addr);
|
||||
break;
|
||||
case R_ALPHA_DTPRELLO:
|
||||
*(ul16 *)loc = S + A - ctx.dtp_addr;
|
||||
break;
|
||||
case R_ALPHA_GOTTPREL:
|
||||
*(ul16 *)loc = sym.get_gottp_addr(ctx) + A - GP;
|
||||
break;
|
||||
case R_ALPHA_TPRELHI:
|
||||
*(ul16 *)loc = hi(S + A - ctx.tp_addr);
|
||||
break;
|
||||
case R_ALPHA_TPRELLO:
|
||||
*(ul16 *)loc = S + A - ctx.tp_addr;
|
||||
break;
|
||||
case R_ALPHA_LITUSE:
|
||||
case R_ALPHA_HINT:
|
||||
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_ALPHA_REFLONG:
|
||||
if (std::optional<u64> val = get_tombstone(sym, frag))
|
||||
*(ul32 *)loc = *val;
|
||||
else
|
||||
*(ul32 *)loc = S + A;
|
||||
break;
|
||||
case R_ALPHA_REFQUAD:
|
||||
if (std::optional<u64> val = get_tombstone(sym, frag))
|
||||
*(ul64 *)loc = *val;
|
||||
else
|
||||
*(ul64 *)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);
|
||||
|
||||
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
|
||||
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 Alpha";
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_ALPHA_REFQUAD:
|
||||
scan_dyn_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_ALPHA_LITERAL:
|
||||
if (rel.r_addend)
|
||||
ctx.extra.got->add_symbol(sym, rel.r_addend);
|
||||
else
|
||||
sym.flags |= NEEDS_GOT;
|
||||
break;
|
||||
case R_ALPHA_SREL32:
|
||||
scan_pcrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_ALPHA_BRSGP:
|
||||
if (sym.is_imported)
|
||||
sym.flags |= NEEDS_PLT;
|
||||
break;
|
||||
case R_ALPHA_TLSGD:
|
||||
sym.flags |= NEEDS_TLSGD;
|
||||
break;
|
||||
case R_ALPHA_TLSLDM:
|
||||
ctx.needs_tlsld = true;
|
||||
break;
|
||||
case R_ALPHA_GOTTPREL:
|
||||
sym.flags |= NEEDS_GOTTP;
|
||||
break;
|
||||
case R_ALPHA_TPRELHI:
|
||||
case R_ALPHA_TPRELLO:
|
||||
check_tlsle(ctx, sym, rel);
|
||||
break;
|
||||
case R_ALPHA_GPREL32:
|
||||
case R_ALPHA_LITUSE:
|
||||
case R_ALPHA_GPDISP:
|
||||
case R_ALPHA_HINT:
|
||||
case R_ALPHA_GPRELHIGH:
|
||||
case R_ALPHA_GPRELLOW:
|
||||
case R_ALPHA_DTPRELHI:
|
||||
case R_ALPHA_DTPRELLO:
|
||||
break;
|
||||
default:
|
||||
Fatal(ctx) << *this << ": unknown relocation: " << rel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// An R_ALPHA_LITERAL relocation may request the linker to create a GOT
|
||||
// entry for an external symbol with a non-zero addend. This is an unusual
|
||||
// request which is not found in any other targets.
|
||||
//
|
||||
// Referring an external symbol with a non-zero addend is a bad practice
|
||||
// because we need to create as many dynamic relocations as the number of
|
||||
// distinctive addends for the same symbol.
|
||||
//
|
||||
// We don't want to mess up the implementation of the common GOT section
|
||||
// for Alpha. So we create another GOT-like section, .alpha_got. Any GOT
|
||||
// entry for an R_ALPHA_LITERAL reloc with a non-zero addend is created
|
||||
// not in .got but in .alpha_got.
|
||||
//
|
||||
// Since .alpha_got entries are accessed relative to GP, .alpha_got
|
||||
// needs to be close enough to .got. It's actually placed next to .got.
|
||||
void AlphaGotSection::add_symbol(Symbol<E> &sym, i64 addend) {
|
||||
assert(addend);
|
||||
std::scoped_lock lock(mu);
|
||||
entries.push_back({&sym, addend});
|
||||
}
|
||||
|
||||
bool operator<(const AlphaGotSection::Entry &a, const AlphaGotSection::Entry &b) {
|
||||
return std::tuple(a.sym->file->priority, a.sym->sym_idx, a.addend) <
|
||||
std::tuple(b.sym->file->priority, b.sym->sym_idx, b.addend);
|
||||
};
|
||||
|
||||
u64 AlphaGotSection::get_addr(Symbol<E> &sym, i64 addend) {
|
||||
auto it = std::lower_bound(entries.begin(), entries.end(), Entry{&sym, addend});
|
||||
assert(it != entries.end());
|
||||
return this->shdr.sh_addr + (it - entries.begin()) * sizeof(Word<E>);
|
||||
}
|
||||
|
||||
i64 AlphaGotSection::get_reldyn_size(Context<E> &ctx) const {
|
||||
i64 n = 0;
|
||||
for (const Entry &e : entries)
|
||||
if (e.sym->is_imported || (ctx.arg.pic && !e.sym->is_absolute()))
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
void AlphaGotSection::finalize() {
|
||||
sort(entries);
|
||||
remove_duplicates(entries);
|
||||
shdr.sh_size = entries.size() * sizeof(Word<E>);
|
||||
}
|
||||
|
||||
void AlphaGotSection::copy_buf(Context<E> &ctx) {
|
||||
ElfRel<E> *dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
|
||||
reldyn_offset);
|
||||
|
||||
for (i64 i = 0; i < entries.size(); i++) {
|
||||
Entry &e = entries[i];
|
||||
u64 P = this->shdr.sh_addr + sizeof(Word<E>) * i;
|
||||
ul64 *buf = (ul64 *)(ctx.buf + this->shdr.sh_offset + sizeof(Word<E>) * i);
|
||||
|
||||
if (e.sym->is_imported) {
|
||||
*buf = ctx.arg.apply_dynamic_relocs ? e.addend : 0;
|
||||
*dynrel++ = ElfRel<E>(P, E::R_ABS, e.sym->get_dynsym_idx(ctx), e.addend);
|
||||
} else {
|
||||
*buf = e.sym->get_addr(ctx) + e.addend;
|
||||
if (ctx.arg.pic && !e.sym->is_absolute())
|
||||
*dynrel++ = ElfRel<E>(P, E::R_RELATIVE, 0, *buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mold::elf
|
||||
-299
@@ -1,299 +0,0 @@
|
||||
// RISC instructions are usually up to 4 bytes long, so the immediates of
|
||||
// their branch instructions are naturally smaller than 32 bits. This is
|
||||
// contrary to x86-64 on which branch instructions take 4 bytes immediates
|
||||
// and can jump to anywhere within PC ± 2 GiB.
|
||||
//
|
||||
// In fact, ARM32's branch instructions can jump only within ±16 MiB and
|
||||
// ARM64's ±128 MiB, for example. If a branch target is further than that,
|
||||
// we need to let it branch to a linker-synthesized code sequence that
|
||||
// construct a full 32 bit address in a register and jump there. That
|
||||
// linker-synthesized code is called "thunk".
|
||||
//
|
||||
// The function in this file creates thunks.
|
||||
//
|
||||
// Note that although thunks play an important role in an executable, they
|
||||
// don't take up too much space in it. For example, among the clang-16's
|
||||
// text segment whose size is ~300 MiB on ARM64, thunks in total occupy
|
||||
// only ~30 KiB or 0.01%. Of course the number depends on an ISA; we would
|
||||
// need more thunks on ARM32 whose branch range is shorter than ARM64.
|
||||
// That said, the total size of thunks still isn't that much. Therefore,
|
||||
// we don't need to try too hard to reduce thunk size to the absolute
|
||||
// minimum.
|
||||
|
||||
#if MOLD_ARM32 || MOLD_ARM64 || MOLD_PPC32 || MOLD_PPC64V1 || MOLD_PPC64V2
|
||||
|
||||
#include "mold.h"
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/parallel_for_each.h>
|
||||
|
||||
namespace mold::elf {
|
||||
|
||||
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
|
||||
// ARM64/ARM32/PPC, respectively.
|
||||
static constexpr i64 batch_size = max_distance() / 10;
|
||||
|
||||
// We assume that a single thunk group is smaller than 900 KiB.
|
||||
static constexpr i64 max_thunk_size = 900 * 1024;
|
||||
|
||||
static_assert(max_thunk_size / E::thunk_size < ThunkRef::MAX_SYM_IDX);
|
||||
|
||||
template <typename E>
|
||||
static bool is_reachable(Context<E> &ctx, 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)
|
||||
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))
|
||||
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().preserves_r2())
|
||||
return false;
|
||||
if (rel.r_type == R_PPC64_REL24_NOTOC && sym.esym().uses_toc())
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compute a distance between the relocated place and the symbol
|
||||
// and check if they are within reach.
|
||||
i64 S = sym.get_addr(ctx, NO_OPD);
|
||||
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();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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());
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
|
||||
// This function is not thread-safe because it mutates symbols' members
|
||||
static std::mutex mu;
|
||||
std::scoped_lock lock(mu);
|
||||
|
||||
std::span<InputSection<E> *> m = members;
|
||||
if (m.empty())
|
||||
return;
|
||||
|
||||
// Initialize input sections with a dummy offset so that we can
|
||||
// distinguish sections that have got an address with the one who
|
||||
// haven't.
|
||||
for (InputSection<E> *isec : m)
|
||||
isec->offset = -1;
|
||||
thunks.clear();
|
||||
|
||||
// We create thunks from the beginning of the section to the end.
|
||||
// We manage progress using four offsets which increase monotonically.
|
||||
// The locations they point to are always A <= B <= C <= D.
|
||||
//
|
||||
// Input sections between B and C are in the current batch.
|
||||
//
|
||||
// A is the input section with the smallest address than can reach
|
||||
// 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.
|
||||
//
|
||||
// ................................ <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
|
||||
// <-------------> Reachable from the current batch
|
||||
i64 a = 0;
|
||||
i64 b = 0;
|
||||
i64 c = 0;
|
||||
i64 d = 0;
|
||||
i64 offset = 0;
|
||||
|
||||
// The smallest thunk index that is reachable from the current batch.
|
||||
i64 t = 0;
|
||||
|
||||
while (b < m.size()) {
|
||||
// Move D foward as far as we can jump from B to a thunk at D.
|
||||
auto d_thunk_end = [&] {
|
||||
u64 d_end = align_to(offset, 1 << m[d]->p2align) + m[d]->sh_size;
|
||||
return align_to(d_end, Thunk<E>::alignment) + max_thunk_size;
|
||||
};
|
||||
|
||||
while (d < m.size() &&
|
||||
(b == d || d_thunk_end() <= m[b]->offset + max_distance())) {
|
||||
offset = align_to(offset, 1 << m[d]->p2align);
|
||||
m[d]->offset = offset;
|
||||
offset += m[d]->sh_size;
|
||||
d++;
|
||||
}
|
||||
|
||||
// Move C forward so that C is apart from B by BATCH_SIZE. We want
|
||||
// to make sure that there's at least one section between B and C
|
||||
// to ensure progress.
|
||||
c = b + 1;
|
||||
while (c < d && m[c]->offset + m[c]->sh_size < m[b]->offset + batch_size)
|
||||
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)
|
||||
a++;
|
||||
|
||||
// Erase references to out-of-range thunks.
|
||||
while (t < thunks.size() && thunks[t]->offset < m[a]->offset)
|
||||
reset_thunk(*thunks[t++]);
|
||||
|
||||
// Create a new thunk and place it at D.
|
||||
offset = align_to(offset, Thunk<E>::alignment);
|
||||
i64 thunk_idx = thunks.size();
|
||||
Thunk<E> *thunk = new Thunk<E>(*this, offset);
|
||||
thunks.emplace_back(thunk);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Move B forward to point to the begining of the next batch.
|
||||
b = c;
|
||||
}
|
||||
|
||||
while (t < thunks.size())
|
||||
reset_thunk(*thunks[t++]);
|
||||
|
||||
this->shdr.sh_size = offset;
|
||||
|
||||
for (InputSection<E> *isec : members)
|
||||
this->shdr.sh_addralign =
|
||||
std::max<u32>(this->shdr.sh_addralign, 1 << isec->p2align);
|
||||
}
|
||||
|
||||
} // namespace mold::elf
|
||||
|
||||
#endif
|
||||
+18
-18
@@ -7,45 +7,45 @@ set -e
|
||||
|
||||
set -x
|
||||
|
||||
case "$ID-$VERSION_ID" in
|
||||
ubuntu-20.* | pop-20.*)
|
||||
case "$ID" in
|
||||
ubuntu | pop | linuxmint | debian | raspbian | neon)
|
||||
apt-get update
|
||||
apt-get install -y cmake gcc g++ g++-10
|
||||
apt-get install -y cmake gcc g++ clang gdb
|
||||
;;
|
||||
ubuntu-* | pop-* | linuxmint-* | debian-* | raspbian-*)
|
||||
apt-get update
|
||||
apt-get install -y cmake gcc g++
|
||||
fedora | amzn | rhel | centos)
|
||||
dnf install -y gcc-g++ cmake glibc-static libstdc++-static diffutils util-linux tar
|
||||
;;
|
||||
fedora-* | amzn-* | rhel-*)
|
||||
dnf install -y gcc-g++ cmake glibc-static libstdc++-static diffutils util-linux
|
||||
rocky)
|
||||
dnf install -y gcc-g++ cmake diffutils util-linux
|
||||
;;
|
||||
opensuse-leap-*)
|
||||
zypper install -y make cmake gcc-c++ gcc11-c++ glibc-devel-static tar diffutils util-linux
|
||||
;;
|
||||
opensuse-tumbleweed-*)
|
||||
opensuse-*)
|
||||
zypper install -y make cmake gcc-c++ glibc-devel-static tar diffutils util-linux
|
||||
;;
|
||||
gentoo-*)
|
||||
gentoo)
|
||||
emerge-webrsync
|
||||
FEATURES='getbinpkg binpkg-request-signature' emerge dev-build/cmake
|
||||
;;
|
||||
arch-* | archarm-* | artix-* | endeavouros-*)
|
||||
arch | archarm | artix | endeavouros)
|
||||
pacman -Sy --needed --noconfirm base-devel cmake util-linux
|
||||
;;
|
||||
void-*)
|
||||
void)
|
||||
xbps-install -Sy xbps bash make cmake gcc tar diffutils util-linux
|
||||
;;
|
||||
alpine-*)
|
||||
alpine)
|
||||
apk update
|
||||
apk add bash make linux-headers cmake gcc g++
|
||||
;;
|
||||
clear-linux-*)
|
||||
clear-linux-os)
|
||||
swupd update
|
||||
swupd bundle-add c-basic diffutils
|
||||
;;
|
||||
almalinux-*)
|
||||
almalinux)
|
||||
dnf install -y gcc-toolset-13-gcc-c++ gcc-toolset-13-libstdc++-devel cmake diffutils
|
||||
;;
|
||||
freebsd)
|
||||
pkg update
|
||||
pkg install -y cmake bash binutils gcc
|
||||
;;
|
||||
*)
|
||||
echo "Error: don't know anything about build dependencies on $ID-$VERSION_ID"
|
||||
exit 1
|
||||
|
||||
@@ -9,9 +9,9 @@ set -x
|
||||
#
|
||||
# Feel free to send me a PR if your OS is not on this list.
|
||||
|
||||
case "$ID-$VERSION_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,alpha}-linux-gnu {gcc,g++}-arm-linux-gnueabihf
|
||||
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,arc}-linux-gnu {gcc,g++}-arm-linux-gnueabihf
|
||||
;;
|
||||
*)
|
||||
echo "Error: don't know anything about build dependencies on $ID-$VERSION_ID"
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "filetype.h"
|
||||
|
||||
namespace mold {
|
||||
|
||||
@@ -76,7 +75,7 @@ struct ArHdr {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Context, typename MappedFile>
|
||||
template <typename Context>
|
||||
std::vector<MappedFile *>
|
||||
read_thin_archive_members(Context &ctx, MappedFile *mf) {
|
||||
u8 *begin = mf->data;
|
||||
@@ -116,7 +115,7 @@ read_thin_archive_members(Context &ctx, MappedFile *mf) {
|
||||
continue;
|
||||
|
||||
std::string path = name.starts_with('/') ?
|
||||
name : (filepath(mf->name).parent_path() / name).string();
|
||||
name : (path_dirname(mf->name) / name).string();
|
||||
vec.push_back(must_open_file(ctx, path));
|
||||
vec.back()->thin_parent = mf;
|
||||
data = body;
|
||||
@@ -124,7 +123,7 @@ read_thin_archive_members(Context &ctx, MappedFile *mf) {
|
||||
return vec;
|
||||
}
|
||||
|
||||
template <typename Context, typename MappedFile>
|
||||
template <typename Context>
|
||||
std::vector<MappedFile *> read_fat_archive_members(Context &ctx, MappedFile *mf) {
|
||||
u8 *begin = mf->data;
|
||||
u8 *data = begin + 8;
|
||||
@@ -162,16 +161,13 @@ std::vector<MappedFile *> read_fat_archive_members(Context &ctx, MappedFile *mf)
|
||||
return vec;
|
||||
}
|
||||
|
||||
template <typename Context, typename MappedFile>
|
||||
template <typename Context>
|
||||
std::vector<MappedFile *> read_archive_members(Context &ctx, MappedFile *mf) {
|
||||
switch (get_file_type(ctx, mf)) {
|
||||
case FileType::AR:
|
||||
std::string_view str = mf->get_contents();
|
||||
if (str.starts_with("!<arch>\n"))
|
||||
return read_fat_archive_members(ctx, mf);
|
||||
case FileType::THIN_AR:
|
||||
return read_thin_archive_members(ctx, mf);
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
assert(str.starts_with("!<thin>\n"));
|
||||
return read_thin_archive_members(ctx, mf);
|
||||
}
|
||||
|
||||
} // namespace mold
|
||||
+59
-136
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
#include "integers.h"
|
||||
|
||||
#include <array>
|
||||
@@ -59,11 +60,10 @@ public:
|
||||
|
||||
namespace mold {
|
||||
|
||||
namespace ranges = std::ranges;
|
||||
using namespace std::literals::string_literals;
|
||||
using namespace std::literals::string_view_literals;
|
||||
|
||||
template <typename Context> class OutputFile;
|
||||
|
||||
inline char *output_tmpfile;
|
||||
|
||||
inline u8 *output_buffer_start = nullptr;
|
||||
@@ -72,11 +72,10 @@ inline u8 *output_buffer_end = nullptr;
|
||||
extern std::string mold_git_hash;
|
||||
|
||||
std::string errno_string();
|
||||
std::string get_self_path();
|
||||
void cleanup();
|
||||
void install_signal_handler();
|
||||
|
||||
static u64 combine_hash(u64 a, u64 b) {
|
||||
inline u64 combine_hash(u64 a, u64 b) {
|
||||
return a ^ (b + 0x9e3779b9 + (a << 6) + (a >> 2));
|
||||
}
|
||||
|
||||
@@ -252,6 +251,12 @@ struct Atomic : std::atomic<T> {
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// mimalloc.cc
|
||||
//
|
||||
|
||||
void set_mimalloc_options();
|
||||
|
||||
//
|
||||
// perf.cc
|
||||
//
|
||||
@@ -314,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;
|
||||
@@ -331,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
|
||||
//
|
||||
@@ -385,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>>
|
||||
@@ -423,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());
|
||||
@@ -443,10 +429,9 @@ inline i64 write_string(void *buf, std::string_view str) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline i64 write_vector(void *buf, const std::vector<T> &vec) {
|
||||
i64 sz = vec.size() * sizeof(T);
|
||||
memcpy(buf, vec.data(), sz);
|
||||
return sz;
|
||||
inline void write_vector(void *buf, const std::vector<T> &vec) {
|
||||
if (!vec.empty())
|
||||
memcpy(buf, vec.data(), vec.size() * sizeof(T));
|
||||
}
|
||||
|
||||
inline void encode_uleb(std::vector<u8> &vec, u64 val) {
|
||||
@@ -498,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;
|
||||
@@ -526,15 +528,6 @@ inline void overwrite_uleb(u8 *loc, u64 val) {
|
||||
*loc = val & 0b0111'1111;
|
||||
}
|
||||
|
||||
template <typename Context>
|
||||
std::string_view save_string(Context &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));
|
||||
return {(char *)buf, str.size()};
|
||||
}
|
||||
|
||||
static inline void pause() {
|
||||
#if defined(__x86_64__)
|
||||
asm volatile("pause");
|
||||
@@ -601,14 +594,14 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
std::pair<T *, bool> insert(std::string_view key, u32 hash, const T &val) {
|
||||
std::pair<T *, bool> insert(std::string_view key, u64 hash, const T &val) {
|
||||
assert(has_single_bit(nbuckets));
|
||||
|
||||
i64 begin = hash & (nbuckets - 1);
|
||||
u64 begin = hash & (nbuckets - 1);
|
||||
u64 mask = nbuckets / NUM_SHARDS - 1;
|
||||
|
||||
for (i64 i = 0; i < MAX_RETRY; i++) {
|
||||
i64 idx = (begin & ~mask) | ((begin + i) & mask);
|
||||
u64 idx = (begin & ~mask) | ((begin + i) & mask);
|
||||
Entry &ent = entries[idx];
|
||||
|
||||
// It seems avoiding compare-and-swap is faster overall at least
|
||||
@@ -646,8 +639,8 @@ public:
|
||||
return {&ent.value, false};
|
||||
}
|
||||
|
||||
assert(false && "ConcurrentMap is full");
|
||||
return {nullptr, false};
|
||||
std::cerr << "ConcurrentMap is full\n";
|
||||
abort();
|
||||
}
|
||||
|
||||
i64 get_idx(T *value) const {
|
||||
@@ -705,12 +698,12 @@ public:
|
||||
return flatten(vec);
|
||||
}
|
||||
|
||||
static constexpr i64 MIN_NBUCKETS = 2048;
|
||||
static constexpr i64 MIN_NBUCKETS = 4096;
|
||||
static constexpr i64 NUM_SHARDS = 16;
|
||||
static constexpr i64 MAX_RETRY = 128;
|
||||
static constexpr i64 MAX_RETRY = 256;
|
||||
|
||||
Entry *entries = nullptr;
|
||||
i64 nbuckets = 0;
|
||||
u64 nbuckets = 0;
|
||||
};
|
||||
|
||||
//
|
||||
@@ -719,82 +712,6 @@ public:
|
||||
|
||||
void get_random_bytes(u8 *buf, i64 size);
|
||||
|
||||
//
|
||||
// output-file.h
|
||||
//
|
||||
|
||||
template <typename Context>
|
||||
class OutputFile {
|
||||
public:
|
||||
static std::unique_ptr<OutputFile<Context>>
|
||||
open(Context &ctx, std::string path, i64 filesize, int perm);
|
||||
|
||||
virtual void close(Context &ctx) = 0;
|
||||
virtual ~OutputFile() = default;
|
||||
|
||||
u8 *buf = nullptr;
|
||||
std::vector<u8> buf2;
|
||||
std::string path;
|
||||
int fd = -1;
|
||||
i64 filesize = 0;
|
||||
bool is_mmapped = false;
|
||||
bool is_unmapped = false;
|
||||
|
||||
protected:
|
||||
OutputFile(std::string path, i64 filesize, bool is_mmapped)
|
||||
: path(path), filesize(filesize), is_mmapped(is_mmapped) {}
|
||||
};
|
||||
|
||||
template <typename Context>
|
||||
class MallocOutputFile : public OutputFile<Context> {
|
||||
public:
|
||||
MallocOutputFile(Context &ctx, std::string path, i64 filesize, int perm)
|
||||
: OutputFile<Context>(path, filesize, false), ptr(new u8[filesize]),
|
||||
perm(perm) {
|
||||
this->buf = ptr.get();
|
||||
}
|
||||
|
||||
void close(Context &ctx) override {
|
||||
Timer t(ctx, "close_file");
|
||||
FILE *fp;
|
||||
|
||||
if (this->path == "-") {
|
||||
fp = stdout;
|
||||
} else {
|
||||
#ifdef _WIN32
|
||||
int pmode = (perm & 0200) ? (_S_IREAD | _S_IWRITE) : _S_IREAD;
|
||||
i64 fd = _open(this->path.c_str(), _O_RDWR | _O_CREAT | _O_BINARY, pmode);
|
||||
#else
|
||||
i64 fd = ::open(this->path.c_str(), O_RDWR | O_CREAT, perm);
|
||||
#endif
|
||||
if (fd == -1)
|
||||
Fatal(ctx) << "cannot open " << this->path << ": " << errno_string();
|
||||
#ifdef _WIN32
|
||||
fp = _fdopen(fd, "wb");
|
||||
#else
|
||||
fp = fdopen(fd, "w");
|
||||
#endif
|
||||
}
|
||||
|
||||
fwrite(this->buf, this->filesize, 1, fp);
|
||||
if (!this->buf2.empty())
|
||||
fwrite(this->buf2.data(), this->buf2.size(), 1, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<u8[]> ptr;
|
||||
int perm;
|
||||
};
|
||||
|
||||
template <typename Context>
|
||||
class LockingOutputFile : public OutputFile<Context> {
|
||||
public:
|
||||
LockingOutputFile(Context &ctx, std::string path, int perm);
|
||||
void resize(Context &ctx, i64 filesize);
|
||||
void close(Context &ctx) override;
|
||||
};
|
||||
|
||||
//
|
||||
// hyperloglog.cc
|
||||
//
|
||||
@@ -878,13 +795,19 @@ private:
|
||||
// filepath.cc
|
||||
//
|
||||
|
||||
std::filesystem::path filepath(const auto &path) {
|
||||
return {path, std::filesystem::path::format::generic_format};
|
||||
inline std::filesystem::path path_dirname(std::string_view path) {
|
||||
return std::filesystem::path(path).parent_path();
|
||||
}
|
||||
|
||||
std::string get_realpath(std::string_view path);
|
||||
std::string path_clean(std::string_view path);
|
||||
std::filesystem::path to_abs_path(std::filesystem::path path);
|
||||
inline std::string path_filename(std::string_view path) {
|
||||
return std::filesystem::path(path).filename().string();
|
||||
}
|
||||
|
||||
inline std::string path_clean(std::string_view path) {
|
||||
return std::filesystem::path(path).lexically_normal().string();
|
||||
}
|
||||
|
||||
std::string get_self_path();
|
||||
|
||||
//
|
||||
// demangle.cc
|
||||
@@ -982,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;
|
||||
}
|
||||
|
||||
@@ -1041,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;
|
||||
}
|
||||
|
||||
File renamed without changes.
@@ -2,3 +2,6 @@
|
||||
#define MOLD_LIBDIR "@CMAKE_INSTALL_FULL_LIBDIR@"
|
||||
|
||||
#cmakedefine HAVE_MADVISE 1
|
||||
#cmakedefine HAVE_UNAME 1
|
||||
#cmakedefine MOLD_USE_MIMALLOC 1
|
||||
#cmakedefine MOLD_USE_SYSTEM_MIMALLOC 1
|
||||
@@ -48,8 +48,8 @@ u32 compute_crc32(u32 crc, u8 *buf, i64 len) {
|
||||
len -= sz;
|
||||
}
|
||||
|
||||
tbb::parallel_for_each(shards.begin(), shards.end(), [](Shard &shard) {
|
||||
shard.crc = crc32_z(0, shard.buf, shard.len);
|
||||
tbb::parallel_for_each(shards, [](Shard &shard) {
|
||||
shard.crc = crc32(0, shard.buf, shard.len);
|
||||
});
|
||||
|
||||
for (Shard &shard : shards)
|
||||
File renamed without changes.
@@ -13,27 +13,6 @@
|
||||
|
||||
namespace mold {
|
||||
|
||||
std::string get_realpath(std::string_view path) {
|
||||
std::error_code ec;
|
||||
std::filesystem::path link = std::filesystem::read_symlink(path, ec);
|
||||
if (ec)
|
||||
return std::string(path);
|
||||
return (filepath(path) / ".." / link).lexically_normal().string();
|
||||
}
|
||||
|
||||
// Removes redundant '/..' or '/.' from a given path.
|
||||
// The transformation is done purely by lexical processing.
|
||||
// This function does not access file system.
|
||||
std::string path_clean(std::string_view path) {
|
||||
return filepath(path).lexically_normal().string();
|
||||
}
|
||||
|
||||
std::filesystem::path to_abs_path(std::filesystem::path path) {
|
||||
if (path.is_absolute())
|
||||
return path.lexically_normal();
|
||||
return (std::filesystem::current_path() / path).lexically_normal();
|
||||
}
|
||||
|
||||
// Returns the path of the mold executable itself
|
||||
std::string get_self_path() {
|
||||
#if __APPLE__ || _WIN32
|
||||
@@ -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,47 +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"' >> /etc/portage/make.conf && \
|
||||
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
|
||||
RUN emerge gdb lld clang vim emacs strace ccache xeyes dev-build/cmake dev-vcs/git && rm -rf /var/tmp/portage
|
||||
echo 'CCACHE_DIR="/ccache"' >> /etc/portage/make.conf && \
|
||||
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
|
||||
@@ -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;
|
||||
}
|
||||
File renamed without changes.
@@ -8,21 +8,21 @@
|
||||
// big-endian SPARC machine to create a little-endian RV64 binary.
|
||||
//
|
||||
// 2. Even though data members in all ELF data strucutres are naturally
|
||||
// aligned, they are not guaranteed to be aligned on memory. Because
|
||||
// archive file (.a file) aligns each member only to a 2 byte boundary,
|
||||
// anything larger than 2 bytes may be unaligned in an mmap'ed memory.
|
||||
// Unaligned access is an undefined behavior in C/C++, so we shouldn't
|
||||
// cast an arbitrary pointer to a uint32_t, for example, to read a
|
||||
// 32 bit value.
|
||||
// aligned, they are not guaranteed to be aligned on memory because of
|
||||
// archive files. Archive files (.a files) align each member only to a
|
||||
// 2 byte boundary, so anything larger than 2 bytes may be misaligned
|
||||
// in an mmap'ed memory. Misaligned access is an undefined behavior in
|
||||
// C/C++, so we shouldn't cast an arbitrary pointer to a uint32_t, for
|
||||
// example, to read a 32 bit value.
|
||||
//
|
||||
// The data types defined in this file don't depend on host byte order and
|
||||
// don't do unaligned access.
|
||||
// don't do unaligned access. Note that modern compilers are smart enough
|
||||
// to recognize shift and bitwise OR patterns and compile them into a
|
||||
// single load instruction.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bit>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)
|
||||
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
@@ -46,44 +46,39 @@ typedef int16_t i16;
|
||||
typedef int32_t i32;
|
||||
typedef int64_t i64;
|
||||
|
||||
template <typename T, std::endian endian, int size = sizeof(T)>
|
||||
template <typename T, bool is_le, int size = sizeof(T)>
|
||||
class Integer {
|
||||
public:
|
||||
constexpr Integer() = default;
|
||||
|
||||
constexpr Integer(T x) requires (endian == std::endian::little && size == 2)
|
||||
constexpr Integer(T x) requires (is_le && size == 2)
|
||||
: buf{(u8)x, (u8)(x >> 8)} {}
|
||||
|
||||
constexpr Integer(T x) requires (endian == std::endian::little && size == 3)
|
||||
constexpr Integer(T x) requires (is_le && size == 3)
|
||||
: buf{(u8)x, (u8)(x >> 8), (u8)(x >> 16)} {}
|
||||
|
||||
constexpr Integer(T x) requires (endian == std::endian::little && size == 4)
|
||||
constexpr Integer(T x) requires (is_le && size == 4)
|
||||
: buf{(u8)x, (u8)(x >> 8), (u8)(x >> 16), (u8)(x >> 24)} {}
|
||||
|
||||
constexpr Integer(T x) requires (endian == std::endian::little && size == 8)
|
||||
constexpr Integer(T x) requires (is_le && size == 8)
|
||||
: buf{(u8)x, (u8)(x >> 8), (u8)(x >> 16), (u8)(x >> 24),
|
||||
(u8)(x >> 32), (u8)(x >> 40), (u8)(x >> 48), (u8)(x >> 56)} {}
|
||||
|
||||
constexpr Integer(T x) requires (endian == std::endian::big && size == 2)
|
||||
constexpr Integer(T x) requires (!is_le && size == 2)
|
||||
: buf{(u8)(x >> 8), (u8)x} {}
|
||||
|
||||
constexpr Integer(T x) requires (endian == std::endian::big && size == 3)
|
||||
constexpr Integer(T x) requires (!is_le && size == 3)
|
||||
: buf{(u8)(x >> 16), (u8)(x >> 8), (u8)x} {}
|
||||
|
||||
constexpr Integer(T x) requires (endian == std::endian::big && size == 4)
|
||||
constexpr Integer(T x) requires (!is_le && size == 4)
|
||||
: buf{(u8)(x >> 24), (u8)(x >> 16), (u8)(x >> 8), (u8)x} {}
|
||||
|
||||
constexpr Integer(T x) requires (endian == std::endian::big && size == 8)
|
||||
constexpr Integer(T x) requires (!is_le && size == 8)
|
||||
: buf{(u8)(x >> 56), (u8)(x >> 48), (u8)(x >> 40), (u8)(x >> 32),
|
||||
(u8)(x >> 24), (u8)(x >> 16), (u8)(x >> 8), (u8)x} {}
|
||||
|
||||
Integer &operator=(T x) {
|
||||
new (this) Integer(x);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator T() const {
|
||||
if constexpr (endian == std::endian::little) {
|
||||
if constexpr (is_le) {
|
||||
if constexpr (size == 2)
|
||||
return buf[1] << 8 | buf[0];
|
||||
else if constexpr (size == 3)
|
||||
@@ -110,6 +105,11 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
Integer &operator=(T x) {
|
||||
new (this) Integer(x);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Integer &operator++() { return *this = *this + 1; }
|
||||
Integer operator++(int) { return ++*this - 1; }
|
||||
Integer &operator--() { return *this = *this - 1; }
|
||||
@@ -121,24 +121,24 @@ public:
|
||||
|
||||
private:
|
||||
u8 buf[size];
|
||||
};;
|
||||
};
|
||||
|
||||
using il16 = Integer<i16, std::endian::little>;
|
||||
using il32 = Integer<i32, std::endian::little>;
|
||||
using il64 = Integer<i64, std::endian::little>;
|
||||
using il16 = Integer<i16, true>;
|
||||
using il32 = Integer<i32, true>;
|
||||
using il64 = Integer<i64, true>;
|
||||
|
||||
using ul16 = Integer<u16, std::endian::little>;
|
||||
using ul24 = Integer<u32, std::endian::little, 3>;
|
||||
using ul32 = Integer<u32, std::endian::little>;
|
||||
using ul64 = Integer<u64, std::endian::little>;
|
||||
using ul16 = Integer<u16, true>;
|
||||
using ul24 = Integer<u32, true, 3>;
|
||||
using ul32 = Integer<u32, true>;
|
||||
using ul64 = Integer<u64, true>;
|
||||
|
||||
using ib16 = Integer<i16, std::endian::big>;
|
||||
using ib32 = Integer<i32, std::endian::big>;
|
||||
using ib64 = Integer<i64, std::endian::big>;
|
||||
using ib16 = Integer<i16, false>;
|
||||
using ib32 = Integer<i32, false>;
|
||||
using ib64 = Integer<i64, false>;
|
||||
|
||||
using ub16 = Integer<u16, std::endian::big>;
|
||||
using ub24 = Integer<u32, std::endian::big, 3>;
|
||||
using ub32 = Integer<u32, std::endian::big>;
|
||||
using ub64 = Integer<u64, std::endian::big>;
|
||||
using ub16 = Integer<u16, false>;
|
||||
using ub24 = Integer<u32, false, 3>;
|
||||
using ub32 = Integer<u32, false>;
|
||||
using ub64 = Integer<u64, false>;
|
||||
|
||||
} // namespace mold
|
||||
@@ -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>
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,24 @@
|
||||
#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
|
||||
# include <mimalloc-new-delete.h>
|
||||
#endif
|
||||
|
||||
// Silence mimalloc warning messages that users can just ignore.
|
||||
#if MOLD_USE_MIMALLOC
|
||||
# include <mimalloc.h>
|
||||
|
||||
namespace mold {
|
||||
void set_mimalloc_options() {
|
||||
mi_option_disable(mi_option_verbose);
|
||||
mi_option_disable(mi_option_show_errors);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
namespace mold {
|
||||
void set_mimalloc_options() {}
|
||||
}
|
||||
#endif
|
||||
@@ -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,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);
|
||||
File renamed without changes.
@@ -1,6 +1,6 @@
|
||||
#include "common.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <csignal>
|
||||
#include <tbb/version.h>
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
File renamed without changes.
File renamed without changes.
@@ -1,3 +1,5 @@
|
||||
// This file contains functions to create a tar file.
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
File renamed without changes.
+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
|
||||
@@ -34,19 +34,19 @@
|
||||
#include "mold.h"
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/parallel_for_each.h>
|
||||
#include <tbb/parallel_sort.h>
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
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:
|
||||
case R_ARM_TARGET1:
|
||||
case R_ARM_BASE_PREL:
|
||||
case R_ARM_GOTOFF32:
|
||||
case R_ARM_GOT_PREL:
|
||||
@@ -57,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);
|
||||
@@ -145,7 +172,6 @@ void write_addend(u8 *loc, i64 val, const ElfRel<E> &rel) {
|
||||
break;
|
||||
case R_ARM_ABS32:
|
||||
case R_ARM_REL32:
|
||||
case R_ARM_TARGET1:
|
||||
case R_ARM_BASE_PREL:
|
||||
case R_ARM_GOTOFF32:
|
||||
case R_ARM_GOT_PREL:
|
||||
@@ -156,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:
|
||||
@@ -176,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);
|
||||
@@ -185,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();
|
||||
@@ -209,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]
|
||||
@@ -247,30 +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);
|
||||
|
||||
ElfRel<E> *dynrel = nullptr;
|
||||
if (ctx.reldyn)
|
||||
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
|
||||
file.reldyn_offset + this->reldyn_offset);
|
||||
|
||||
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)
|
||||
@@ -279,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;
|
||||
@@ -293,13 +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:
|
||||
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
|
||||
break;
|
||||
case R_ARM_REL32:
|
||||
*(ul32 *)loc = S + A - P;
|
||||
@@ -312,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;
|
||||
}
|
||||
@@ -356,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);
|
||||
@@ -383,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;
|
||||
@@ -397,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
|
||||
@@ -429,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);
|
||||
@@ -450,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;
|
||||
@@ -489,19 +497,21 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
// .L2: .word foo + . - .L1
|
||||
// R_ARM_TLS_GOTDESC
|
||||
//
|
||||
// We may relax the instructions to the following for non-dlopen'd DSO
|
||||
//
|
||||
// ldr r0, .L2
|
||||
// .L1: ldr r0, [pc, r0]
|
||||
// ...
|
||||
// .L2: .word foo(gottpoff) + . - .L1
|
||||
//
|
||||
// or to the following for executable.
|
||||
// We may relax the instructions to the following if its TP-relative
|
||||
// address is known at link-time
|
||||
//
|
||||
// ldr r0, .L2
|
||||
// .L1: nop
|
||||
// ...
|
||||
// .L2: .word foo(tpoff)
|
||||
//
|
||||
// or to the following if the TP-relative address is known at
|
||||
// process startup time.
|
||||
//
|
||||
// ldr r0, .L2
|
||||
// .L1: ldr r0, [pc, r0]
|
||||
// ...
|
||||
// .L2: .word foo(gottpoff) + . - .L1
|
||||
if (sym.has_tlsdesc(ctx)) {
|
||||
// A is odd if the corresponding TLS_CALL is Thumb.
|
||||
*(ul32 *)loc = sym.get_tlsdesc_addr(ctx) - P + A - ((A & 1) ? 6 : 4);
|
||||
@@ -513,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 {
|
||||
@@ -523,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,
|
||||
@@ -584,8 +594,6 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
template <>
|
||||
void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
assert(shdr().sh_flags & SHF_ALLOC);
|
||||
|
||||
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
// Scan relocations
|
||||
@@ -600,12 +608,6 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
sym.flags |= NEEDS_GOT | NEEDS_PLT;
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_ARM_ABS32:
|
||||
case R_ARM_MOVT_ABS:
|
||||
case R_ARM_THM_MOVT_ABS:
|
||||
case R_ARM_TARGET1:
|
||||
scan_dyn_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_ARM_MOVW_ABS_NC:
|
||||
case R_ARM_THM_MOVW_ABS_NC:
|
||||
scan_absrel(ctx, sym, rel);
|
||||
@@ -644,9 +646,14 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_ARM_TLS_LE32:
|
||||
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:
|
||||
@@ -675,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
|
||||
@@ -708,6 +715,54 @@ u64 get_eflags(Context<E> &ctx) {
|
||||
return EF_ARM_EABI_VER5;
|
||||
}
|
||||
|
||||
void create_arm_exidx_section(Context<E> &ctx) {
|
||||
for (i64 i = 0; i < ctx.chunks.size(); i++) {
|
||||
OutputSection<E> *osec = ctx.chunks[i]->to_osec();
|
||||
|
||||
if (osec && osec->shdr.sh_type == SHT_ARM_EXIDX) {
|
||||
auto *sec = new Arm32ExidxSection(*osec);
|
||||
ctx.extra.exidx = sec;
|
||||
ctx.chunks[i] = sec;
|
||||
ctx.chunk_pool.emplace_back(sec);
|
||||
|
||||
for (InputSection<E> *isec : osec->members)
|
||||
isec->is_alive = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Arm32ExidxSection::compute_section_size(Context<E> &ctx) {
|
||||
output_section.compute_section_size(ctx);
|
||||
this->shdr.sh_size = output_section.shdr.sh_size + 8; // +8 for sentinel
|
||||
}
|
||||
|
||||
void Arm32ExidxSection::update_shdr(Context<E> &ctx) {
|
||||
// .ARM.exidx's sh_link should be set to the .text section index.
|
||||
// Runtime doesn't care about it, but the binutils's strip command does.
|
||||
if (Chunk<E> *chunk = find_chunk(ctx, ".text"))
|
||||
this->shdr.sh_link = chunk->shndx;
|
||||
}
|
||||
|
||||
void Arm32ExidxSection::remove_duplicate_entries(Context<E> &ctx) {
|
||||
this->shdr.sh_size = get_contents(ctx).size();
|
||||
}
|
||||
|
||||
void Arm32ExidxSection::copy_buf(Context<E> &ctx) {
|
||||
std::vector<u8> contents = get_contents(ctx);
|
||||
assert(this->shdr.sh_size == contents.size());
|
||||
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.
|
||||
@@ -716,14 +771,8 @@ u64 get_eflags(Context<E> &ctx) {
|
||||
// I don't know why only ARM uses the different mechanism, but it's
|
||||
// likely that it's due to some historical reason.
|
||||
//
|
||||
// This function sorts .ARM.exidx records.
|
||||
void fixup_arm_exidx_section(Context<E> &ctx) {
|
||||
Timer t(ctx, "fixup_arm_exidx_section");
|
||||
|
||||
OutputSection<E> *osec = find_section(ctx, SHT_ARM_EXIDX);
|
||||
if (!osec)
|
||||
return;
|
||||
|
||||
// This function returns contents of .ARM.exidx.
|
||||
std::vector<u8> Arm32ExidxSection::get_contents(Context<E> &ctx) {
|
||||
// .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
|
||||
@@ -736,37 +785,56 @@ void fixup_arm_exidx_section(Context<E> &ctx) {
|
||||
//
|
||||
// CANTUNWIND is value 1. The most significant bit is set in (2) but
|
||||
// not in (3). So we can distinguished them just by looking at a value.
|
||||
const u32 EXIDX_CANTUNWIND = 1;
|
||||
const u32 CANTUNWIND = 1;
|
||||
|
||||
struct Entry {
|
||||
ul32 addr;
|
||||
ul32 val;
|
||||
};
|
||||
|
||||
if (osec->shdr.sh_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();
|
||||
|
||||
Entry *ent = (Entry *)(ctx.buf + osec->shdr.sh_offset);
|
||||
i64 num_entries = osec->shdr.sh_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 addresses, we first translate them so that the addresses
|
||||
// records by address, we first translate them so that the addresses
|
||||
// are relative to the beginning of the section.
|
||||
auto is_relative = [](u32 val) {
|
||||
return val != EXIDX_CANTUNWIND && !(val & 0x8000'0000);
|
||||
return val != CANTUNWIND && !(val & 0x8000'0000);
|
||||
};
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
tbb::parallel_sort(ent, ent + num_entries, [](const Entry &a, const Entry &b) {
|
||||
std::sort(ent, ent + num_entries, [](const Entry &a, const Entry &b) {
|
||||
return a.addr < b.addr;
|
||||
});
|
||||
|
||||
// Remove duplicate adjacent entries. That is, if two adjacent functions
|
||||
// have the same compact unwind info or are both CANTUNWIND, we can
|
||||
// merge them into a single address range.
|
||||
auto it = std::unique(ent, ent + num_entries,
|
||||
[](const Entry &a, const Entry &b) {
|
||||
return a.val == b.val;
|
||||
});
|
||||
|
||||
num_entries = it - ent;
|
||||
buf.resize(num_entries * sizeof(Entry));
|
||||
|
||||
// Make addresses relative to themselves.
|
||||
tbb::parallel_for((i64)0, num_entries, [&](i64 i) {
|
||||
i64 offset = sizeof(Entry) * i;
|
||||
@@ -775,14 +843,7 @@ void fixup_arm_exidx_section(Context<E> &ctx) {
|
||||
ent[i].val = 0x7fff'ffff & (ent[i].val - offset);
|
||||
});
|
||||
|
||||
// .ARM.exidx's sh_link should be set to the .text section index.
|
||||
// Runtime doesn't care about it, but the binutils's strip command does.
|
||||
if (ctx.shdr) {
|
||||
if (Chunk<E> *text = find_section(ctx, ".text")) {
|
||||
osec->shdr.sh_link = text->shndx;
|
||||
ctx.shdr->copy_buf(ctx);
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
@@ -17,11 +17,13 @@
|
||||
//
|
||||
// https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst
|
||||
|
||||
#if MOLD_ARM64LE || MOLD_ARM64BE
|
||||
|
||||
#include "mold.h"
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
using E = ARM64;
|
||||
using E = MOLD_TARGET;
|
||||
|
||||
static void write_adrp(u8 *buf, u64 val) {
|
||||
*(ul32 *)buf |= (bits(val, 13, 12) << 29) | (bits(val, 32, 14) << 5);
|
||||
@@ -110,13 +112,13 @@ void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
|
||||
case R_NONE:
|
||||
break;
|
||||
case R_AARCH64_ABS64:
|
||||
*(ul64 *)loc = val;
|
||||
*(U64<E> *)loc = val;
|
||||
break;
|
||||
case R_AARCH64_PREL32:
|
||||
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
|
||||
*(U32<E> *)loc = val - this->shdr.sh_addr - offset;
|
||||
break;
|
||||
case R_AARCH64_PREL64:
|
||||
*(ul64 *)loc = val - this->shdr.sh_addr - offset;
|
||||
*(U64<E> *)loc = val - this->shdr.sh_addr - offset;
|
||||
break;
|
||||
default:
|
||||
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
|
||||
@@ -145,11 +147,6 @@ template <>
|
||||
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
ElfRel<E> *dynrel = nullptr;
|
||||
if (ctx.reldyn)
|
||||
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
|
||||
file.reldyn_offset + this->reldyn_offset);
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
if (rel.r_type == R_NONE)
|
||||
@@ -158,22 +155,18 @@ 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:
|
||||
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
|
||||
break;
|
||||
case R_AARCH64_LDST8_ABS_LO12_NC:
|
||||
case R_AARCH64_ADD_ABS_LO12_NC:
|
||||
@@ -242,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 &&
|
||||
@@ -281,14 +274,14 @@ 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;
|
||||
}
|
||||
case R_AARCH64_PLT32:
|
||||
check(S + A - P, -(1LL << 31), 1LL << 31);
|
||||
*(ul32 *)loc = S + A - P;
|
||||
*(U32<E> *)loc = S + A - P;
|
||||
break;
|
||||
case R_AARCH64_CONDBR19:
|
||||
case R_AARCH64_LD_PREL_LO19:
|
||||
@@ -296,15 +289,15 @@ 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);
|
||||
*(ul16 *)loc = S + A - P;
|
||||
check(S + A - P, -(1LL << 15), 1LL << 16);
|
||||
*(U16<E> *)loc = S + A - P;
|
||||
break;
|
||||
case R_AARCH64_PREL32:
|
||||
check(S + A - P, -(1LL << 31), 1LL << 32);
|
||||
*(ul32 *)loc = S + A - P;
|
||||
*(U32<E> *)loc = S + A - P;
|
||||
break;
|
||||
case R_AARCH64_PREL64:
|
||||
*(ul64 *)loc = S + A - P;
|
||||
*(U64<E> *)loc = S + A - P;
|
||||
break;
|
||||
case R_AARCH64_LD64_GOT_LO12_NC:
|
||||
*(ul32 *)loc |= bits(G + GOT + A, 11, 3) << 10;
|
||||
@@ -383,19 +376,21 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
// blr x1
|
||||
// R_AARCH64_TLSDESC_CALL foo
|
||||
//
|
||||
// We may relax the instructions to the following for non-dlopen'd DSO
|
||||
//
|
||||
// nop
|
||||
// nop
|
||||
// adrp x0, :gottprel:foo
|
||||
// ldr x0, [x0, :gottprel_lo12:foo]
|
||||
//
|
||||
// or to the following for executable.
|
||||
// We may relax the instructions to the following if its TP-relative
|
||||
// address is known at link-time
|
||||
//
|
||||
// nop
|
||||
// nop
|
||||
// movz x0, :tls_offset_hi:foo, lsl #16
|
||||
// movk x0, :tls_offset_lo:foo
|
||||
//
|
||||
// or to the following if the TP-relative address is known at
|
||||
// process startup time.
|
||||
//
|
||||
// nop
|
||||
// nop
|
||||
// adrp x0, :gottprel:foo
|
||||
// ldr x0, [x0, :gottprel_lo12:foo]
|
||||
if (sym.has_tlsdesc(ctx)) {
|
||||
i64 val = page(sym.get_tlsdesc_addr(ctx) + A) - page(P);
|
||||
check(val, -(1LL << 32), 1LL << 32);
|
||||
@@ -450,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);
|
||||
@@ -464,19 +452,21 @@ 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))
|
||||
*(ul64 *)loc = *val;
|
||||
*(U64<E> *)loc = *val;
|
||||
else
|
||||
*(ul64 *)loc = S + A;
|
||||
*(U64<E> *)loc = S + A;
|
||||
break;
|
||||
case R_AARCH64_ABS32: {
|
||||
i64 val = S + A;
|
||||
check(val, 0, 1LL << 32);
|
||||
*(ul32 *)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;
|
||||
@@ -488,8 +478,6 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
template <>
|
||||
void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
assert(shdr().sh_flags & SHF_ALLOC);
|
||||
|
||||
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
// Scan relocations
|
||||
@@ -505,9 +493,6 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
sym.flags |= NEEDS_GOT | NEEDS_PLT;
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_AARCH64_ABS64:
|
||||
scan_dyn_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_AARCH64_MOVW_UABS_G3:
|
||||
scan_absrel(ctx, sym, rel);
|
||||
break;
|
||||
@@ -567,6 +552,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
|
||||
check_tlsle(ctx, sym, rel);
|
||||
break;
|
||||
case R_AARCH64_ABS64:
|
||||
case R_AARCH64_ADD_ABS_LO12_NC:
|
||||
case R_AARCH64_ADR_PREL_LO21:
|
||||
case R_AARCH64_CONDBR19:
|
||||
@@ -626,4 +612,6 @@ void Thunk<E>::copy_buf(Context<E> &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
|
||||
#endif
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
#include "mold.h"
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
using E = I386;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,7 +221,7 @@ static void relax_gd_to_le(u8 *loc, ElfRel<E> rel, u64 val) {
|
||||
}
|
||||
|
||||
// Relax LD to LE
|
||||
static void relax_ld_to_le(u8 *loc, ElfRel<E> rel, u64 val) {
|
||||
static void relax_ld_to_le(u8 *loc, ElfRel<E> rel, u64 tls_size) {
|
||||
switch (rel.r_type) {
|
||||
case R_386_PLT32:
|
||||
case R_386_PC32: {
|
||||
@@ -235,18 +230,17 @@ static void relax_ld_to_le(u8 *loc, ElfRel<E> rel, u64 val) {
|
||||
0x2d, 0, 0, 0, 0, // sub $tls_size, %eax
|
||||
};
|
||||
memcpy(loc - 2, insn, sizeof(insn));
|
||||
*(ul32 *)(loc + 5) = val;
|
||||
*(ul32 *)(loc + 5) = tls_size;
|
||||
break;
|
||||
}
|
||||
case R_386_GOT32:
|
||||
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) = val;
|
||||
*(ul32 *)(loc + 6) = tls_size;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -286,11 +280,6 @@ template <>
|
||||
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
ElfRel<E> *dynrel = nullptr;
|
||||
if (ctx.reldyn)
|
||||
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
|
||||
file.reldyn_offset + this->reldyn_offset);
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
if (rel.r_type == R_NONE)
|
||||
@@ -299,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);
|
||||
@@ -322,7 +308,6 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
*(ul16 *)loc = S + A;
|
||||
break;
|
||||
case R_386_32:
|
||||
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
|
||||
break;
|
||||
case R_386_PC8:
|
||||
check(S + A - P, -(1 << 7), 1 << 7);
|
||||
@@ -374,7 +359,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
case R_386_TLS_LDM:
|
||||
if (ctx.got->has_tlsld(ctx))
|
||||
*(ul32 *)loc = ctx.got->get_tlsld_addr(ctx) + A - GOT;
|
||||
else
|
||||
else
|
||||
relax_ld_to_le(loc, rels[++i], ctx.tp_addr - ctx.tls_begin);
|
||||
break;
|
||||
case R_386_TLS_LDO_32:
|
||||
@@ -392,16 +377,18 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
// call *(%eax)
|
||||
// R_386_TLS_DESC_CALL foo
|
||||
//
|
||||
// We may relax the instructions to the following for non-dlopen'd DSO
|
||||
//
|
||||
// mov foo@GOTTPOFF(%ebx), %eax
|
||||
// nop
|
||||
//
|
||||
// or to the following for executable.
|
||||
// We may relax the instructions to the following if its TP-relative
|
||||
// address is known at link-time
|
||||
//
|
||||
// mov $foo@TPOFF, %eax
|
||||
// nop
|
||||
//
|
||||
// or to the following if the TP-relative address is known at
|
||||
// process startup time.
|
||||
//
|
||||
// mov foo@GOTTPOFF(%ebx), %eax
|
||||
// nop
|
||||
//
|
||||
// We allow the following alternative code sequence too because
|
||||
// LLVM emits such code.
|
||||
//
|
||||
@@ -410,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)) {
|
||||
@@ -453,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);
|
||||
@@ -468,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);
|
||||
@@ -518,8 +506,6 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
template <>
|
||||
void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
assert(shdr().sh_flags & SHF_ALLOC);
|
||||
|
||||
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
// Scan relocations
|
||||
@@ -549,9 +535,6 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_386_16:
|
||||
scan_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_386_32:
|
||||
scan_dyn_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_386_PC8:
|
||||
case R_386_PC16:
|
||||
case R_386_PC32:
|
||||
@@ -581,7 +564,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_386_TLS_GD:
|
||||
// We always relax if -static because libc.a doesn't contain
|
||||
// __tls_get_addr().
|
||||
if ((ctx.arg.relax && sym.is_tprel_linktime_const(ctx)) || ctx.arg.static_)
|
||||
if (ctx.arg.static_ || (ctx.arg.relax && sym.is_tprel_linktime_const(ctx)))
|
||||
i++;
|
||||
else
|
||||
sym.flags |= NEEDS_TLSGD;
|
||||
@@ -600,6 +583,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_386_TLS_LE:
|
||||
check_tlsle(ctx, sym, rel);
|
||||
break;
|
||||
case R_386_32:
|
||||
case R_386_GOTOFF:
|
||||
case R_386_TLS_LDO_32:
|
||||
case R_386_SIZE32:
|
||||
@@ -611,4 +595,4 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
#include "mold.h"
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
using E = MOLD_TARGET;
|
||||
|
||||
@@ -51,7 +51,7 @@ static u64 hi20(u64 val, u64 pc) {
|
||||
return bits(page(val + 0x800) - page(pc), 31, 12);
|
||||
}
|
||||
|
||||
static u64 hi64(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:
|
||||
//
|
||||
@@ -65,21 +65,18 @@ static u64 hi64(u64 val, u64 pc) {
|
||||
// ADDI.D adds a sign-extended 12 bit value to a register. LU32I.D and
|
||||
// LU52I.D simply set bits to [51:31] and to [63:53], respectively.
|
||||
//
|
||||
// Compensating all the sign-extensions is a bit complicated.
|
||||
u64 x = page(val) - page(pc);
|
||||
if (val & 0x800)
|
||||
x += 0x1000 - 0x1'0000'0000;
|
||||
if (x & 0x8000'0000)
|
||||
x += 0x1'0000'0000;
|
||||
return x;
|
||||
// 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 page(val) - page(pc - 8);
|
||||
}
|
||||
|
||||
static u64 higher20(u64 val, u64 pc) {
|
||||
return bits(hi64(val, pc), 51, 32);
|
||||
return bits(higher(val, pc), 51, 32);
|
||||
}
|
||||
|
||||
static u64 highest12(u64 val, u64 pc) {
|
||||
return bits(hi64(val, pc), 63, 52);
|
||||
return bits(higher(val, pc), 63, 52);
|
||||
}
|
||||
|
||||
static void write_k12(u8 *loc, u32 val) {
|
||||
@@ -131,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;
|
||||
@@ -185,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
|
||||
@@ -271,15 +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);
|
||||
|
||||
ElfRel<E> *dynrel = nullptr;
|
||||
if (ctx.reldyn)
|
||||
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
|
||||
file.reldyn_offset + this->reldyn_offset);
|
||||
|
||||
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];
|
||||
@@ -289,56 +282,60 @@ 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
|
||||
// depending on the symbol type.
|
||||
//
|
||||
// Note that as of August 2023, both GCC and Clang treat TLSLD relocs
|
||||
// as if they were TLSGD relocs for LoongArch, which is a clear bug.
|
||||
// We need to handle TLSLD relocs as synonyms for TLSGD relocs for the
|
||||
// sake of bug compatibility.
|
||||
auto get_got_idx = [&] {
|
||||
if (sym.has_tlsgd(ctx))
|
||||
return sym.get_tlsgd_idx(ctx);
|
||||
return sym.get_got_idx(ctx);
|
||||
};
|
||||
// Note that even though LoongArch defines relocations for TLSLD, TLSLD
|
||||
// is not actually supported on it. GCC and LLVM emit identical machine
|
||||
// code for -ftls-model=global-dynamic and -ftls-model=local-dynamic,
|
||||
// and we need to handle TLSLD relocations as equivalent to TLSGD
|
||||
// relocations. This is clearly a compiler bug, but it's too late to
|
||||
// fix. The only way to fix it would be to define a new set of
|
||||
// relocations for true TLSLD and deprecate the current ones. But it
|
||||
// appears that migrating to TLSDESC is a better choice, so it's
|
||||
// unlikely to happen.
|
||||
i64 got_idx =
|
||||
sym.has_tlsgd(ctx) ? sym.get_tlsgd_idx(ctx) : sym.get_got_idx(ctx);
|
||||
|
||||
u64 S = sym.get_addr(ctx);
|
||||
u64 A = rel.r_addend;
|
||||
u64 P = get_addr() + r_offset;
|
||||
u64 G = get_got_idx() * sizeof(Word<E>);
|
||||
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;
|
||||
else
|
||||
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
|
||||
break;
|
||||
case R_LARCH_64:
|
||||
assert(E::is_64);
|
||||
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
|
||||
*(ul32 *)loc = S + A;
|
||||
break;
|
||||
case R_LARCH_B16:
|
||||
check_branch(S + A - P, -(1 << 17), 1 << 17);
|
||||
@@ -370,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;
|
||||
@@ -408,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
|
||||
|
||||
@@ -481,13 +478,13 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
case R_LARCH_TLS_IE64_HI12:
|
||||
write_k12(loc, (sym.get_gottp_addr(ctx) + A) >> 52);
|
||||
break;
|
||||
case R_LARCH_TLS_LD_PC_HI20:
|
||||
case R_LARCH_TLS_GD_PC_HI20:
|
||||
case R_LARCH_TLS_LD_PC_HI20:
|
||||
check(sym.get_tlsgd_addr(ctx) + A - P, -(1LL << 31), 1LL << 31);
|
||||
write_j20(loc, hi20(sym.get_tlsgd_addr(ctx) + A, P));
|
||||
break;
|
||||
case R_LARCH_TLS_LD_HI20:
|
||||
case R_LARCH_TLS_GD_HI20:
|
||||
case R_LARCH_TLS_LD_HI20:
|
||||
write_j20(loc, (sym.get_tlsgd_addr(ctx) + A) >> 12);
|
||||
break;
|
||||
case R_LARCH_ADD6:
|
||||
@@ -528,16 +525,18 @@ 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
|
||||
write_k16(loc, (S + A - P) >> 2);
|
||||
write_d10k16(loc, (S + A - P) >> 2);
|
||||
}
|
||||
break;
|
||||
case R_LARCH_ADD_ULEB128:
|
||||
@@ -546,6 +545,92 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
case R_LARCH_SUB_ULEB128:
|
||||
overwrite_uleb(loc, read_uleb(loc) - S - A);
|
||||
break;
|
||||
case R_LARCH_TLS_DESC_PC_HI20:
|
||||
// LoongArch TLSDESC uses the following code sequence to materialize
|
||||
// a TP-relative address in a0.
|
||||
//
|
||||
// pcalau12i $a0, 0
|
||||
// R_LARCH_TLS_DESC_PC_HI20 foo
|
||||
// addi.[dw] $a0, $a0, 0
|
||||
// R_LARCH_TLS_DESC_PC_LO12 foo
|
||||
// ld.d $ra, $a0, 0
|
||||
// R_LARCH_TLS_DESC_LD foo
|
||||
// jirl $ra, $ra, 0
|
||||
// R_LARCH_TLS_DESC_CALL foo
|
||||
//
|
||||
// We may relax the instructions to the following if its TP-relative
|
||||
// address is known at link-time
|
||||
//
|
||||
// <deleted>
|
||||
// <deleted>
|
||||
// lu12i.w $a0, foo@TPOFF
|
||||
// addi.w $a0, $a0, foo@TPOFF
|
||||
//
|
||||
// or to the following if the TP offset is small enough.
|
||||
//
|
||||
// <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.
|
||||
//
|
||||
// <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.
|
||||
//
|
||||
// <deleted>
|
||||
// pcaddi $a0, foo@GOTDESC
|
||||
// ld.d $ra, $a0, 0
|
||||
// jirl $ra, $ra, 0
|
||||
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 (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 {
|
||||
write_k12(loc, sym.get_tlsdesc_addr(ctx) + A);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case R_LARCH_TLS_DESC_LD:
|
||||
if (sym.has_tlsdesc(ctx) || removed_bytes == 4) {
|
||||
// Do nothing
|
||||
} else if (sym.has_gottp(ctx)) {
|
||||
*(ul32 *)loc = 0x1a00'0004; // pcalau12i $a0, 0
|
||||
write_j20(loc, hi20(sym.get_gottp_addr(ctx) + A, P));
|
||||
} else {
|
||||
*(ul32 *)loc = 0x1400'0004; // lu12i.w $a0, 0
|
||||
write_j20(loc, (S + A + 0x800 - ctx.tp_addr) >> 12);
|
||||
}
|
||||
break;
|
||||
case R_LARCH_TLS_DESC_CALL:
|
||||
if (sym.has_tlsdesc(ctx)) {
|
||||
// Do nothing
|
||||
} else if (sym.has_gottp(ctx)) {
|
||||
if (E::is_64)
|
||||
*(ul32 *)loc = 0x28c0'0084; // ld.d $a0, $a0, 0
|
||||
else
|
||||
*(ul32 *)loc = 0x2880'0084; // ld.w $a0, $a0, 0
|
||||
write_k12(loc, sym.get_gottp_addr(ctx) + A);
|
||||
} else {
|
||||
i64 val = S + A - ctx.tp_addr;
|
||||
if (0 <= val && val < 0x1000)
|
||||
*(ul32 *)loc = 0x0380'0004; // ori $a0, $zero, 0
|
||||
else
|
||||
*(ul32 *)loc = 0x0280'0084; // addi.w $a0, $a0, 0
|
||||
write_k12(loc, val);
|
||||
}
|
||||
break;
|
||||
case R_LARCH_TLS_LE_HI20_R:
|
||||
if (removed_bytes == 0)
|
||||
write_j20(loc, (S + A + 0x800 - ctx.tp_addr) >> 12);
|
||||
@@ -556,10 +641,11 @@ 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;
|
||||
}
|
||||
case R_LARCH_64:
|
||||
case R_LARCH_TLS_LE_ADD_R:
|
||||
break;
|
||||
default:
|
||||
@@ -661,8 +747,6 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
template <>
|
||||
void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
assert(shdr().sh_flags & SHF_ALLOC);
|
||||
|
||||
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
// Scan relocations
|
||||
@@ -686,12 +770,6 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_LARCH_32:
|
||||
if constexpr (E::is_64)
|
||||
scan_absrel(ctx, sym, rel);
|
||||
else
|
||||
scan_dyn_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_LARCH_64:
|
||||
assert(E::is_64);
|
||||
scan_dyn_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_LARCH_B26:
|
||||
case R_LARCH_PCALA_HI20:
|
||||
@@ -707,10 +785,10 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_LARCH_TLS_IE_PC_HI20:
|
||||
sym.flags |= NEEDS_GOTTP;
|
||||
break;
|
||||
case R_LARCH_TLS_LD_PC_HI20:
|
||||
case R_LARCH_TLS_GD_PC_HI20:
|
||||
case R_LARCH_TLS_LD_HI20:
|
||||
case R_LARCH_TLS_LD_PC_HI20:
|
||||
case R_LARCH_TLS_GD_HI20:
|
||||
case R_LARCH_TLS_LD_HI20:
|
||||
sym.flags |= NEEDS_TLSGD;
|
||||
break;
|
||||
case R_LARCH_32_PCREL:
|
||||
@@ -725,6 +803,10 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_LARCH_TLS_LE_LO12_R:
|
||||
check_tlsle(ctx, sym, rel);
|
||||
break;
|
||||
case R_LARCH_TLS_DESC_CALL:
|
||||
scan_tlsdesc(ctx, sym);
|
||||
break;
|
||||
case R_LARCH_64:
|
||||
case R_LARCH_B16:
|
||||
case R_LARCH_B21:
|
||||
case R_LARCH_ABS_HI20:
|
||||
@@ -758,6 +840,9 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_LARCH_SUB64:
|
||||
case R_LARCH_ADD_ULEB128:
|
||||
case R_LARCH_SUB_ULEB128:
|
||||
case R_LARCH_TLS_DESC_PC_HI20:
|
||||
case R_LARCH_TLS_DESC_PC_LO12:
|
||||
case R_LARCH_TLS_DESC_LD:
|
||||
case R_LARCH_TLS_LE_ADD_R:
|
||||
break;
|
||||
default:
|
||||
@@ -767,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
|
||||
@@ -800,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;
|
||||
}
|
||||
|
||||
@@ -831,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
|
||||
@@ -850,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:
|
||||
@@ -870,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
|
||||
@@ -886,19 +978,37 @@ 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 (is_int(dist, 22))
|
||||
remove(4);
|
||||
} else {
|
||||
remove(4);
|
||||
}
|
||||
break;
|
||||
case R_LARCH_TLS_DESC_PC_LO12:
|
||||
if (!sym.has_tlsdesc(ctx))
|
||||
remove(4);
|
||||
break;
|
||||
case R_LARCH_TLS_DESC_LD:
|
||||
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::elf
|
||||
} // namespace mold
|
||||
|
||||
#endif
|
||||
@@ -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,
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#include "mold.h"
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
using E = M68K;
|
||||
|
||||
@@ -78,11 +78,6 @@ template <>
|
||||
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
ElfRel<E> *dynrel = nullptr;
|
||||
if (ctx.reldyn)
|
||||
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
|
||||
file.reldyn_offset + this->reldyn_offset);
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
if (rel.r_type == R_NONE)
|
||||
@@ -91,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) {
|
||||
@@ -118,15 +116,8 @@ 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:
|
||||
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
|
||||
break;
|
||||
case R_68K_16:
|
||||
write16(S + A);
|
||||
@@ -251,8 +242,6 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
template <>
|
||||
void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
assert(shdr().sh_flags & SHF_ALLOC);
|
||||
|
||||
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
@@ -266,9 +255,6 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
Error(ctx) << sym << ": GNU ifunc symbol is not supported on m68k";
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_68K_32:
|
||||
scan_dyn_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_68K_16:
|
||||
case R_68K_8:
|
||||
scan_absrel(ctx, sym, rel);
|
||||
@@ -312,6 +298,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_68K_TLS_LE8:
|
||||
check_tlsle(ctx, sym, rel);
|
||||
break;
|
||||
case R_68K_32:
|
||||
case R_68K_TLS_LDO32:
|
||||
case R_68K_TLS_LDO16:
|
||||
case R_68K_TLS_LDO8:
|
||||
@@ -322,4 +309,4 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
#include "mold.h"
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
using E = PPC32;
|
||||
|
||||
@@ -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
|
||||
@@ -148,11 +148,6 @@ template <>
|
||||
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
ElfRel<E> *dynrel = nullptr;
|
||||
if (ctx.reldyn)
|
||||
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
|
||||
file.reldyn_offset + this->reldyn_offset);
|
||||
|
||||
u64 GOT2 = file.extra.got2 ? file.extra.got2->get_addr() : 0;
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
@@ -170,10 +165,6 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
u64 GOT = ctx.got->shdr.sh_addr;
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_PPC_ADDR32:
|
||||
case R_PPC_UADDR32:
|
||||
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
|
||||
break;
|
||||
case R_PPC_ADDR14:
|
||||
*(ub32 *)loc |= bits(S + A, 15, 2) << 2;
|
||||
break;
|
||||
@@ -222,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;
|
||||
}
|
||||
@@ -275,6 +266,8 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
case R_PPC_GOT_TPREL16:
|
||||
*(ub16 *)loc = sym.get_gottp_addr(ctx) - GOT;
|
||||
break;
|
||||
case R_PPC_ADDR32:
|
||||
case R_PPC_UADDR32:
|
||||
case R_PPC_TLS:
|
||||
case R_PPC_TLSGD:
|
||||
case R_PPC_TLSLD:
|
||||
@@ -323,8 +316,6 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
template <>
|
||||
void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
assert(shdr().sh_flags & SHF_ALLOC);
|
||||
|
||||
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
// Scan relocations
|
||||
@@ -339,10 +330,6 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
sym.flags |= NEEDS_GOT | NEEDS_PLT;
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_PPC_ADDR32:
|
||||
case R_PPC_UADDR32:
|
||||
scan_dyn_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_PPC_ADDR14:
|
||||
case R_PPC_ADDR16:
|
||||
case R_PPC_UADDR16:
|
||||
@@ -391,6 +378,8 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_PPC_TPREL16_HA:
|
||||
check_tlsle(ctx, sym, rel);
|
||||
break;
|
||||
case R_PPC_ADDR32:
|
||||
case R_PPC_UADDR32:
|
||||
case R_PPC_LOCAL24PC:
|
||||
case R_PPC_TLS:
|
||||
case R_PPC_TLSGD:
|
||||
@@ -450,4 +439,4 @@ void Thunk<E>::copy_buf(Context<E> &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
@@ -50,7 +50,7 @@
|
||||
#include <algorithm>
|
||||
#include <tbb/parallel_for_each.h>
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
using E = PPC64V1;
|
||||
|
||||
@@ -154,11 +154,6 @@ template <>
|
||||
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
ElfRel<E> *dynrel = nullptr;
|
||||
if (ctx.reldyn)
|
||||
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
|
||||
file.reldyn_offset + this->reldyn_offset);
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
if (rel.r_type == R_NONE)
|
||||
@@ -167,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;
|
||||
@@ -181,12 +169,12 @@ 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_ADDR64:
|
||||
apply_toc_rel(ctx, sym, rel, loc, S, A, P, &dynrel);
|
||||
break;
|
||||
case R_PPC64_TOC:
|
||||
apply_toc_rel(ctx, *ctx.extra.TOC, rel, loc, TOC, A, P, &dynrel);
|
||||
break;
|
||||
case R_PPC64_TOC16_HA:
|
||||
*(ub16 *)loc = ha(S + A - TOC);
|
||||
@@ -203,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;
|
||||
@@ -277,6 +265,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
case R_PPC64_GOT_TPREL16_LO_DS:
|
||||
*(ub16 *)loc |= (sym.get_gottp_addr(ctx) - TOC) & 0xfffc;
|
||||
break;
|
||||
case R_PPC64_ADDR64:
|
||||
case R_PPC64_PLTSEQ:
|
||||
case R_PPC64_PLTCALL:
|
||||
case R_PPC64_TLS:
|
||||
@@ -301,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);
|
||||
@@ -315,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))
|
||||
@@ -322,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;
|
||||
@@ -341,8 +325,6 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
template <>
|
||||
void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
assert(shdr().sh_flags & SHF_ALLOC);
|
||||
|
||||
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
// Scan relocations
|
||||
@@ -362,10 +344,6 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
sym.flags |= NEEDS_PPC_OPD;
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_PPC64_ADDR64:
|
||||
case R_PPC64_TOC:
|
||||
scan_toc_rel(ctx, sym, rel);
|
||||
break;
|
||||
case R_PPC64_GOT_TPREL16_HA:
|
||||
sym.flags |= NEEDS_GOTTP;
|
||||
break;
|
||||
@@ -387,6 +365,8 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_PPC64_TPREL16_LO_DS:
|
||||
check_tlsle(ctx, sym, rel);
|
||||
break;
|
||||
case R_PPC64_ADDR64:
|
||||
case R_PPC64_TOC:
|
||||
case R_PPC64_REL32:
|
||||
case R_PPC64_REL64:
|
||||
case R_PPC64_TOC16_HA:
|
||||
@@ -519,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)
|
||||
@@ -601,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});
|
||||
|
||||
@@ -610,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) {
|
||||
@@ -689,4 +669,4 @@ void PPC64OpdSection::copy_buf(Context<E> &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
@@ -82,7 +82,7 @@
|
||||
|
||||
#include "mold.h"
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
using E = PPC64V2;
|
||||
|
||||
@@ -172,7 +172,7 @@ void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
|
||||
}
|
||||
|
||||
static u64 get_local_entry_offset(Context<E> &ctx, Symbol<E> &sym) {
|
||||
i64 val = sym.esym().ppc_local_entry;
|
||||
i64 val = sym.esym().ppc64_local_entry;
|
||||
assert(val <= 7);
|
||||
if (val == 7)
|
||||
Fatal(ctx) << sym << ": local entry offset 7 is reserved";
|
||||
@@ -186,11 +186,6 @@ template <>
|
||||
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
ElfRel<E> *dynrel = nullptr;
|
||||
if (ctx.reldyn)
|
||||
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
|
||||
file.reldyn_offset + this->reldyn_offset);
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
if (rel.r_type == R_NONE)
|
||||
@@ -206,16 +201,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 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_ADDR64:
|
||||
if (name() == ".toc")
|
||||
apply_toc_rel(ctx, sym, rel, loc, S, A, P, &dynrel);
|
||||
else
|
||||
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
|
||||
break;
|
||||
case R_PPC64_TOC16_HA:
|
||||
*(ul16 *)loc = ha(S + A - TOC);
|
||||
break;
|
||||
@@ -227,7 +216,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
*(ul16 *)loc |= (S + A - TOC) & 0xfffc;
|
||||
break;
|
||||
case R_PPC64_REL24:
|
||||
if (sym.has_plt(ctx) || !sym.esym().preserves_r2()) {
|
||||
if (sym.has_plt(ctx) || !sym.esym().ppc64_preserves_r2()) {
|
||||
i64 val = r2save_thunk_addr() + A - P;
|
||||
*(ul32 *)loc |= bits(val, 25, 2) << 2;
|
||||
|
||||
@@ -238,18 +227,18 @@ 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;
|
||||
}
|
||||
break;
|
||||
case R_PPC64_REL24_NOTOC:
|
||||
if (sym.has_plt(ctx) || sym.esym().uses_toc()) {
|
||||
if (sym.has_plt(ctx) || sym.esym().ppc64_uses_toc()) {
|
||||
i64 val = no_r2save_thunk_addr() + A - P;
|
||||
*(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;
|
||||
}
|
||||
@@ -337,6 +326,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
case R_PPC64_TPREL34:
|
||||
write34(loc, S + A - ctx.tp_addr);
|
||||
break;
|
||||
case R_PPC64_ADDR64:
|
||||
case R_PPC64_PLTSEQ:
|
||||
case R_PPC64_PLTSEQ_NOTOC:
|
||||
case R_PPC64_PLTCALL:
|
||||
@@ -344,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();
|
||||
@@ -363,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);
|
||||
@@ -377,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))
|
||||
@@ -384,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;
|
||||
@@ -403,8 +389,6 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
template <>
|
||||
void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
assert(shdr().sh_flags & SHF_ALLOC);
|
||||
|
||||
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
// Scan relocations
|
||||
@@ -419,12 +403,6 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
sym.flags |= NEEDS_GOT | NEEDS_PLT;
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_PPC64_ADDR64:
|
||||
if (name() == ".toc")
|
||||
scan_toc_rel(ctx, sym, rel);
|
||||
else
|
||||
scan_dyn_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_PPC64_GOT_TPREL16_HA:
|
||||
case R_PPC64_GOT_TPREL_PCREL34:
|
||||
sym.flags |= NEEDS_GOTTP;
|
||||
@@ -458,6 +436,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_PPC64_TPREL34:
|
||||
check_tlsle(ctx, sym, rel);
|
||||
break;
|
||||
case R_PPC64_ADDR64:
|
||||
case R_PPC64_REL32:
|
||||
case R_PPC64_REL64:
|
||||
case R_PPC64_TOC16_HA:
|
||||
@@ -484,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;
|
||||
@@ -677,4 +657,4 @@ u64 get_eflags(Context<E> &ctx) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
@@ -19,14 +19,11 @@
|
||||
|
||||
#if MOLD_RV64LE || MOLD_RV64BE || MOLD_RV32LE || MOLD_RV32BE
|
||||
|
||||
#include "elf.h"
|
||||
#include "mold.h"
|
||||
|
||||
#include <regex>
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/parallel_for_each.h>
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
using E = MOLD_TARGET;
|
||||
|
||||
@@ -89,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[] = {
|
||||
@@ -122,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
|
||||
@@ -199,79 +200,113 @@ 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);
|
||||
u64 GP = ctx.__global_pointer ? ctx.__global_pointer->get_addr(ctx) : 0;
|
||||
|
||||
ElfRel<E> *dynrel = nullptr;
|
||||
if (ctx.reldyn)
|
||||
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
|
||||
file.reldyn_offset + this->reldyn_offset);
|
||||
|
||||
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;
|
||||
else
|
||||
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
|
||||
assert(E::is_64);
|
||||
*(U32<E> *)loc = S + A;
|
||||
break;
|
||||
case R_RISCV_64:
|
||||
assert(E::is_64);
|
||||
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
|
||||
break;
|
||||
case R_RISCV_BRANCH:
|
||||
check(S + A - P, -(1 << 12), 1 << 12);
|
||||
@@ -284,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
|
||||
@@ -318,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;
|
||||
}
|
||||
@@ -372,50 +394,38 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
write_utype(loc, S + A - P);
|
||||
break;
|
||||
case R_RISCV_PCREL_LO12_I:
|
||||
case R_RISCV_PCREL_LO12_S:
|
||||
case R_RISCV_GPREL_LO12_I:
|
||||
case R_RISCV_GPREL_LO12_S: {
|
||||
i64 idx2 = find_paired_reloc();
|
||||
const ElfRel<E> &rel2 = rels[idx2];
|
||||
case R_RISCV_PCREL_LO12_S: {
|
||||
const ElfRel<E> &rel2 = find_paired_reloc(ctx, *this, rels, sym, i);
|
||||
Symbol<E> &sym2 = *file.symbols[rel2.r_sym];
|
||||
|
||||
auto write =
|
||||
(rel.r_type == R_RISCV_PCREL_LO12_I) ? write_itype : write_stype;
|
||||
|
||||
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>);
|
||||
u64 val;
|
||||
|
||||
switch (rel2.r_type) {
|
||||
case R_RISCV_GOT_HI20:
|
||||
val = G + GOT + A - P;
|
||||
write(loc, G + GOT + A - P);
|
||||
break;
|
||||
case R_RISCV_TLS_GOT_HI20:
|
||||
val = sym2.get_gottp_addr(ctx) + A - P;
|
||||
write(loc, sym2.get_gottp_addr(ctx) + A - P);
|
||||
break;
|
||||
case R_RISCV_TLS_GD_HI20:
|
||||
val = sym2.get_tlsgd_addr(ctx) + A - P;
|
||||
write(loc, sym2.get_tlsgd_addr(ctx) + A - P);
|
||||
break;
|
||||
case R_RISCV_PCREL_HI20:
|
||||
val = S + A - P;
|
||||
write(loc, S + A - P);
|
||||
break;
|
||||
case R_RISCV_GPREL_HI20:
|
||||
val = S + A - GP;
|
||||
break;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
|
||||
if (rel.r_type == R_RISCV_PCREL_LO12_I ||
|
||||
rel.r_type == R_RISCV_GPREL_LO12_I)
|
||||
write_itype(loc, val);
|
||||
else
|
||||
write_stype(loc, val);
|
||||
break;
|
||||
}
|
||||
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) {
|
||||
@@ -433,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:
|
||||
@@ -457,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;
|
||||
}
|
||||
@@ -496,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:
|
||||
@@ -505,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)) {
|
||||
@@ -537,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>
|
||||
@@ -597,10 +613,6 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
check(S + A - P, -(1 << 11), 1 << 11);
|
||||
write_cjtype(loc, S + A - P);
|
||||
break;
|
||||
case R_RISCV_GPREL_HI20:
|
||||
check(S + A - GP, -(1LL << 31), 1LL << 31);
|
||||
write_utype(loc, S + A - GP);
|
||||
break;
|
||||
case R_RISCV_SUB6:
|
||||
*loc = (*loc & 0b1100'0000) | ((*loc - S - A) & 0b0011'1111);
|
||||
break;
|
||||
@@ -717,8 +729,6 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
template <>
|
||||
void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
assert(shdr().sh_flags & SHF_ALLOC);
|
||||
|
||||
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
// Scan relocations
|
||||
@@ -736,17 +746,10 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_RISCV_32:
|
||||
if constexpr (E::is_64)
|
||||
scan_absrel(ctx, sym, rel);
|
||||
else
|
||||
scan_dyn_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_RISCV_HI20:
|
||||
scan_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_RISCV_64:
|
||||
if constexpr (!E::is_64)
|
||||
Error(ctx) << *this << ": R_RISCV_64 cannot be used on RV32";
|
||||
scan_dyn_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_RISCV_CALL:
|
||||
case R_RISCV_CALL_PLT:
|
||||
case R_RISCV_PLT32:
|
||||
@@ -775,10 +778,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_RISCV_TPREL_ADD:
|
||||
check_tlsle(ctx, sym, rel);
|
||||
break;
|
||||
case R_RISCV_GPREL_HI20:
|
||||
if (ctx.arg.shared)
|
||||
Error(ctx) << *this << ": R_RISCV_GPREL_HI20 may not be used with -shared";
|
||||
break;
|
||||
case R_RISCV_64:
|
||||
case R_RISCV_BRANCH:
|
||||
case R_RISCV_JAL:
|
||||
case R_RISCV_PCREL_LO12_I:
|
||||
@@ -799,8 +799,6 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_RISCV_ALIGN:
|
||||
case R_RISCV_RVC_BRANCH:
|
||||
case R_RISCV_RVC_JUMP:
|
||||
case R_RISCV_GPREL_LO12_I:
|
||||
case R_RISCV_GPREL_LO12_S:
|
||||
case R_RISCV_RELAX:
|
||||
case R_RISCV_SUB6:
|
||||
case R_RISCV_SET6:
|
||||
@@ -824,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;
|
||||
|
||||
@@ -841,22 +839,26 @@ u64 get_eflags(Context<E> &ctx) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Scan relocations to a given shrink section.
|
||||
// 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.
|
||||
//
|
||||
@@ -866,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;
|
||||
}
|
||||
|
||||
@@ -887,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: {
|
||||
@@ -911,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;
|
||||
}
|
||||
@@ -993,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
|
||||
@@ -1140,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();
|
||||
}
|
||||
|
||||
//
|
||||
@@ -1233,6 +1205,6 @@ void RiscvAttributesSection<E>::copy_buf(Context<E> &ctx) {
|
||||
write_vector(ctx.buf + this->shdr.sh_offset, contents);
|
||||
}
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
|
||||
#endif
|
||||
@@ -33,11 +33,11 @@
|
||||
// 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"
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
using E = S390X;
|
||||
|
||||
@@ -116,11 +116,6 @@ template <>
|
||||
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
ElfRel<E> *dynrel = nullptr;
|
||||
if (ctx.reldyn)
|
||||
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
|
||||
file.reldyn_offset + this->reldyn_offset);
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
if (rel.r_type == R_NONE)
|
||||
@@ -129,31 +124,26 @@ 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:
|
||||
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
|
||||
break;
|
||||
case R_390_8:
|
||||
check(S + A, 0, 1 << 8);
|
||||
@@ -176,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);
|
||||
@@ -193,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:
|
||||
@@ -256,7 +244,20 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
*(ub32 *)loc = (GOT + A - P) >> 1;
|
||||
break;
|
||||
case R_390_GOTENT:
|
||||
check(GOT + G + A - P, -(1LL << 32), 1LL << 32);
|
||||
// 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;
|
||||
case R_390_TLS_LE32:
|
||||
@@ -303,22 +304,14 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
case R_390_TLS_LDM32:
|
||||
if (ctx.got->has_tlsld(ctx))
|
||||
*(ub32 *)loc = ctx.got->get_tlsld_addr(ctx) + A - GOT;
|
||||
else
|
||||
*(ub32 *)loc = ctx.dtp_addr - ctx.tp_addr;
|
||||
break;
|
||||
case R_390_TLS_LDM64:
|
||||
if (ctx.got->has_tlsld(ctx))
|
||||
*(ub64 *)loc = ctx.got->get_tlsld_addr(ctx) + A - GOT;
|
||||
break;
|
||||
case R_390_TLS_LDO32:
|
||||
if (ctx.got->has_tlsld(ctx))
|
||||
*(ub32 *)loc = S + A - ctx.dtp_addr;
|
||||
else
|
||||
*(ub32 *)loc = S + A - ctx.tp_addr;
|
||||
break;
|
||||
case R_390_TLS_LDO64:
|
||||
if (ctx.got->has_tlsld(ctx))
|
||||
*(ub64 *)loc = S + A - ctx.dtp_addr;
|
||||
else
|
||||
*(ub64 *)loc = S + A - ctx.tp_addr;
|
||||
*(ub64 *)loc = ctx.dtp_addr - ctx.tp_addr;
|
||||
break;
|
||||
case R_390_TLS_LDCALL:
|
||||
if (!ctx.got->has_tlsld(ctx)) {
|
||||
@@ -327,6 +320,12 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
memcpy(loc, insn, sizeof(insn));
|
||||
}
|
||||
break;
|
||||
case R_390_TLS_LDO32:
|
||||
*(ub32 *)loc = S + A - ctx.dtp_addr;
|
||||
break;
|
||||
case R_390_TLS_LDO64:
|
||||
*(ub64 *)loc = S + A - ctx.dtp_addr;
|
||||
break;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
@@ -345,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);
|
||||
@@ -359,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);
|
||||
@@ -385,8 +381,6 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
template <>
|
||||
void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
assert(shdr().sh_flags & SHF_ALLOC);
|
||||
|
||||
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
// Scan relocations
|
||||
@@ -401,9 +395,6 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
sym.flags |= NEEDS_GOT | NEEDS_PLT;
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_390_64:
|
||||
scan_dyn_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_390_8:
|
||||
case R_390_12:
|
||||
case R_390_16:
|
||||
@@ -411,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:
|
||||
@@ -457,7 +450,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
// We always want to relax calls to __tls_get_offset() in statically-
|
||||
// linked executables because __tls_get_offset() in libc.a just calls
|
||||
// abort().
|
||||
if ((ctx.arg.relax && sym.is_tprel_linktime_const(ctx)) || ctx.arg.static_) {
|
||||
if (ctx.arg.static_ || (ctx.arg.relax && sym.is_tprel_linktime_const(ctx))) {
|
||||
// Do nothing
|
||||
} else if (ctx.arg.relax && sym.is_tprel_runtime_const(ctx)) {
|
||||
sym.flags |= NEEDS_GOTTP;
|
||||
@@ -477,6 +470,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_390_TLS_LE64:
|
||||
check_tlsle(ctx, sym, rel);
|
||||
break;
|
||||
case R_390_64:
|
||||
case R_390_TLS_LDO32:
|
||||
case R_390_TLS_LDO64:
|
||||
case R_390_TLS_GDCALL:
|
||||
@@ -488,4 +482,4 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
@@ -58,11 +58,13 @@
|
||||
// output from the linker contains lots of text relocations. That's not
|
||||
// a problem with embedded programming, I guess.
|
||||
|
||||
#if MOLD_SH4LE || MOLD_SH4BE
|
||||
|
||||
#include "mold.h"
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
using E = SH4;
|
||||
using E = MOLD_TARGET;
|
||||
|
||||
// Even though SH-4 uses RELA-type relocations, addends are stored to
|
||||
// relocated places for some reason.
|
||||
@@ -84,7 +86,7 @@ i64 get_addend(u8 *loc, const ElfRel<E> &rel) {
|
||||
case R_SH_GOTOFF:
|
||||
case R_SH_GOTPC:
|
||||
case R_SH_GOTPLT32:
|
||||
return *(ul32 *)loc;
|
||||
return *(U32<E> *)loc;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@@ -108,102 +110,102 @@ void write_addend(u8 *loc, i64 val, const ElfRel<E> &rel) {
|
||||
case R_SH_GOTOFF:
|
||||
case R_SH_GOTPC:
|
||||
case R_SH_GOTPLT32:
|
||||
*(ul32 *)loc = val;
|
||||
*(U32<E> *)loc = val;
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void write_plt_header(Context<E> &ctx, u8 *buf) {
|
||||
if (ctx.arg.pic) {
|
||||
static const u8 insn[] = {
|
||||
0x02, 0xd2, // mov.l 1f, r2
|
||||
0xcc, 0x32, // add r12, r2
|
||||
0x22, 0x50, // mov.l @(8, r2), r0
|
||||
0x21, 0x52, // mov.l @(4, r2), r2
|
||||
0x2b, 0x40, // jmp @r0
|
||||
0x00, 0xe0, // mov #0, r0
|
||||
0, 0, 0, 0, // 1: .long GOTPLT
|
||||
constexpr U16<E> insn[] = {
|
||||
0xd202, // mov.l 1f, r2
|
||||
0x32cc, // add r12, r2
|
||||
0x5022, // mov.l @(8, r2), r0
|
||||
0x5221, // mov.l @(4, r2), r2
|
||||
0x402b, // jmp @r0
|
||||
0xe000, // mov #0, r0
|
||||
0, 0, // 1: .long GOTPLT
|
||||
};
|
||||
|
||||
static_assert(sizeof(insn) == E::plt_hdr_size);
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(ul32 *)(buf + 12) = ctx.gotplt->shdr.sh_addr - ctx.got->shdr.sh_addr;
|
||||
*(U32<E> *)(buf + 12) = ctx.gotplt->shdr.sh_addr - ctx.got->shdr.sh_addr;
|
||||
} else {
|
||||
static const u8 insn[] = {
|
||||
0x02, 0xd2, // mov.l 1f, r2
|
||||
0x22, 0x50, // mov.l @(8, r2), r0
|
||||
0x21, 0x52, // mov.l @(4, r2), r2
|
||||
0x2b, 0x40, // jmp @r0
|
||||
0x00, 0xe0, // mov #0, r0
|
||||
0x09, 0x00, // nop
|
||||
0, 0, 0, 0, // 1: .long GOTPLT
|
||||
constexpr U16<E> insn[] = {
|
||||
0xd202, // mov.l 1f, r2
|
||||
0x5022, // mov.l @(8, r2), r0
|
||||
0x5221, // mov.l @(4, r2), r2
|
||||
0x402b, // jmp @r0
|
||||
0xe000, // mov #0, r0
|
||||
0x0009, // nop
|
||||
0, 0, // 1: .long GOTPLT
|
||||
};
|
||||
|
||||
static_assert(sizeof(insn) == E::plt_hdr_size);
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(ul32 *)(buf + 12) = ctx.gotplt->shdr.sh_addr;
|
||||
*(U32<E> *)(buf + 12) = ctx.gotplt->shdr.sh_addr;
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
if (ctx.arg.pic) {
|
||||
static const u8 insn[] = {
|
||||
0x01, 0xd0, // mov.l 1f, r0
|
||||
0xce, 0x00, // mov.l @(r0, r12), r0
|
||||
0x2b, 0x40, // jmp @r0
|
||||
0x01, 0xd1, // mov.l 2f, r1
|
||||
0, 0, 0, 0, // 1: .long GOTPLT_ENTRY
|
||||
0, 0, 0, 0, // 2: .long INDEX_IN_RELPLT
|
||||
constexpr U16<E> insn[] = {
|
||||
0xd001, // mov.l 1f, r0
|
||||
0x00ce, // mov.l @(r0, r12), r0
|
||||
0x402b, // jmp @r0
|
||||
0xd101, // mov.l 2f, r1
|
||||
0, 0, // 1: .long GOTPLT_ENTRY
|
||||
0, 0, // 2: .long INDEX_IN_RELPLT
|
||||
};
|
||||
|
||||
static_assert(sizeof(insn) == E::plt_size);
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(ul32 *)(buf + 8) = sym.get_gotplt_addr(ctx) - ctx.got->shdr.sh_addr;
|
||||
*(ul32 *)(buf + 12) = sym.get_plt_idx(ctx) * sizeof(ElfRel<E>);
|
||||
*(U32<E> *)(buf + 8) = sym.get_gotplt_addr(ctx) - ctx.got->shdr.sh_addr;
|
||||
*(U32<E> *)(buf + 12) = sym.get_plt_idx(ctx) * sizeof(ElfRel<E>);
|
||||
} else {
|
||||
static const u8 insn[] = {
|
||||
0x01, 0xd0, // mov.l 1f, r0
|
||||
0x02, 0x60, // mov.l @r0, r0
|
||||
0x2b, 0x40, // jmp @r0
|
||||
0x01, 0xd1, // mov.l 2f, r1
|
||||
0, 0, 0, 0, // 1: .long GOTPLT_ENTRY
|
||||
0, 0, 0, 0, // 2: .long INDEX_IN_RELPLT
|
||||
constexpr U16<E> insn[] = {
|
||||
0xd001, // mov.l 1f, r0
|
||||
0x6002, // mov.l @r0, r0
|
||||
0x402b, // jmp @r0
|
||||
0xd101, // mov.l 2f, r1
|
||||
0, 0, // 1: .long GOTPLT_ENTRY
|
||||
0, 0, // 2: .long INDEX_IN_RELPLT
|
||||
};
|
||||
|
||||
static_assert(sizeof(insn) == E::plt_size);
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(ul32 *)(buf + 8) = sym.get_gotplt_addr(ctx);
|
||||
*(ul32 *)(buf + 12) = sym.get_plt_idx(ctx) * sizeof(ElfRel<E>);
|
||||
*(U32<E> *)(buf + 8) = sym.get_gotplt_addr(ctx);
|
||||
*(U32<E> *)(buf + 12) = sym.get_plt_idx(ctx) * sizeof(ElfRel<E>);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
|
||||
if (ctx.arg.pic) {
|
||||
static const u8 insn[] = {
|
||||
0x01, 0xd0, // mov.l 1f, r0
|
||||
0xce, 0x00, // mov.l @(r0, r12), r0
|
||||
0x2b, 0x40, // jmp @r0
|
||||
0x09, 0x00, // nop
|
||||
0, 0, 0, 0, // 1: .long GOT_ENTRY
|
||||
constexpr U16<E> insn[] = {
|
||||
0xd001, // mov.l 1f, r0
|
||||
0x00ce, // mov.l @(r0, r12), r0
|
||||
0x402b, // jmp @r0
|
||||
0x0009, // nop
|
||||
0, 0, // 1: .long GOT_ENTRY
|
||||
};
|
||||
|
||||
static_assert(sizeof(insn) == E::pltgot_size);
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(ul32 *)(buf + 8) = sym.get_got_pltgot_addr(ctx) - ctx.got->shdr.sh_addr;
|
||||
*(U32<E> *)(buf + 8) = sym.get_got_pltgot_addr(ctx) - ctx.got->shdr.sh_addr;
|
||||
} else {
|
||||
static const u8 insn[] = {
|
||||
0x01, 0xd0, // mov.l 1f, r0
|
||||
0x02, 0x60, // mov.l @r0, r0
|
||||
0x2b, 0x40, // jmp @r0
|
||||
0x09, 0x00, // nop
|
||||
0, 0, 0, 0, // 1: .long GOT_ENTRY
|
||||
constexpr U16<E> insn[] = {
|
||||
0xd001, // mov.l 1f, r0
|
||||
0x6002, // mov.l @r0, r0
|
||||
0x402b, // jmp @r0
|
||||
0x0009, // nop
|
||||
0, 0, // 1: .long GOT_ENTRY
|
||||
};
|
||||
|
||||
static_assert(sizeof(insn) == E::pltgot_size);
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
*(ul32 *)(buf + 8) = sym.get_got_pltgot_addr(ctx);
|
||||
*(U32<E> *)(buf + 8) = sym.get_got_pltgot_addr(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,10 +218,10 @@ void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
|
||||
case R_NONE:
|
||||
break;
|
||||
case R_SH_DIR32:
|
||||
*(ul32 *)loc = val;
|
||||
*(U32<E> *)loc = val;
|
||||
break;
|
||||
case R_SH_REL32:
|
||||
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
|
||||
*(U32<E> *)loc = val - this->shdr.sh_addr - offset;
|
||||
break;
|
||||
default:
|
||||
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
|
||||
@@ -230,11 +232,6 @@ template <>
|
||||
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
ElfRel<E> *dynrel = nullptr;
|
||||
if (ctx.reldyn)
|
||||
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
|
||||
file.reldyn_offset + this->reldyn_offset);
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
if (rel.r_type == R_NONE)
|
||||
@@ -251,35 +248,34 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_SH_DIR32:
|
||||
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
|
||||
break;
|
||||
case R_SH_REL32:
|
||||
case R_SH_PLT32:
|
||||
*(ul32 *)loc = S + A - P;
|
||||
*(U32<E> *)loc = S + A - P;
|
||||
break;
|
||||
case R_SH_GOT32:
|
||||
*(ul32 *)loc = G;
|
||||
*(U32<E> *)loc = G;
|
||||
break;
|
||||
case R_SH_GOTPC:
|
||||
*(ul32 *)loc = GOT + A - P;
|
||||
*(U32<E> *)loc = GOT + A - P;
|
||||
break;
|
||||
case R_SH_GOTOFF:
|
||||
*(ul32 *)loc = S + A - GOT;
|
||||
*(U32<E> *)loc = S + A - GOT;
|
||||
break;
|
||||
case R_SH_TLS_GD_32:
|
||||
*(ul32 *)loc = sym.get_tlsgd_addr(ctx) + A - GOT;
|
||||
*(U32<E> *)loc = sym.get_tlsgd_addr(ctx) + A - GOT;
|
||||
break;
|
||||
case R_SH_TLS_LD_32:
|
||||
*(ul32 *)loc = ctx.got->get_tlsld_addr(ctx) + A - GOT;
|
||||
*(U32<E> *)loc = ctx.got->get_tlsld_addr(ctx) + A - GOT;
|
||||
break;
|
||||
case R_SH_TLS_LDO_32:
|
||||
*(ul32 *)loc = S + A - ctx.dtp_addr;
|
||||
*(U32<E> *)loc = S + A - ctx.dtp_addr;
|
||||
break;
|
||||
case R_SH_TLS_IE_32:
|
||||
*(ul32 *)loc = sym.get_gottp_addr(ctx) + A - GOT;
|
||||
*(U32<E> *)loc = sym.get_gottp_addr(ctx) + A - GOT;
|
||||
break;
|
||||
case R_SH_TLS_LE_32:
|
||||
*(ul32 *)loc = S + A - ctx.tp_addr;
|
||||
*(U32<E> *)loc = S + A - ctx.tp_addr;
|
||||
break;
|
||||
default:
|
||||
unreachable();
|
||||
@@ -309,9 +305,9 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
switch (rel.r_type) {
|
||||
case R_SH_DIR32:
|
||||
if (std::optional<u64> val = get_tombstone(sym, frag))
|
||||
*(ul32 *)loc = *val;
|
||||
*(U32<E> *)loc = *val;
|
||||
else
|
||||
*(ul32 *)loc = S + A;
|
||||
*(U32<E> *)loc = S + A;
|
||||
break;
|
||||
default:
|
||||
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
|
||||
@@ -323,8 +319,6 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
template <>
|
||||
void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
assert(shdr().sh_flags & SHF_ALLOC);
|
||||
|
||||
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
@@ -338,9 +332,6 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
Error(ctx) << sym << ": GNU ifunc symbol is not supported on sh4";
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_SH_DIR32:
|
||||
scan_dyn_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_SH_REL32:
|
||||
scan_pcrel(ctx, sym, rel);
|
||||
break;
|
||||
@@ -363,6 +354,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_SH_TLS_LE_32:
|
||||
check_tlsle(ctx, sym, rel);
|
||||
break;
|
||||
case R_SH_DIR32:
|
||||
case R_SH_GOTPC:
|
||||
case R_SH_GOTOFF:
|
||||
case R_SH_TLS_LDO_32:
|
||||
@@ -373,4 +365,6 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
|
||||
#endif
|
||||
@@ -51,14 +51,11 @@
|
||||
// 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"
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
using E = SPARC64;
|
||||
|
||||
@@ -142,11 +139,6 @@ template <>
|
||||
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
ElfRel<E> *dynrel = nullptr;
|
||||
if (ctx.reldyn)
|
||||
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
|
||||
file.reldyn_offset + this->reldyn_offset);
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
if (rel.r_type == R_NONE)
|
||||
@@ -155,23 +147,17 @@ 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_64:
|
||||
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
|
||||
break;
|
||||
case R_SPARC_5:
|
||||
check(S + A, 0, 1 << 5);
|
||||
*(ub32 *)loc |= bits(S + A, 4, 0);
|
||||
@@ -359,27 +345,75 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
*(ub32 *)loc |= bits(S + A, 11, 0);
|
||||
break;
|
||||
case R_SPARC_TLS_GD_HI22:
|
||||
*(ub32 *)loc |= bits(sym.get_tlsgd_addr(ctx) + A - GOT, 31, 10);
|
||||
if (sym.has_tlsgd(ctx)) {
|
||||
*(ub32 *)loc |= bits(sym.get_tlsgd_addr(ctx) + A - GOT, 31, 10);
|
||||
} else if (sym.has_gottp(ctx)) {
|
||||
*(ub32 *)loc |= bits(sym.get_gottp_addr(ctx) + A - GOT, 31, 10);
|
||||
} else {
|
||||
*(ub32 *)loc |= bits(~(S + A - ctx.tp_addr), 31, 10);
|
||||
}
|
||||
break;
|
||||
case R_SPARC_TLS_GD_LO10:
|
||||
*(ub32 *)loc |= bits(sym.get_tlsgd_addr(ctx) + A - GOT, 9, 0);
|
||||
if (sym.has_tlsgd(ctx)) {
|
||||
*(ub32 *)loc |= bits(sym.get_tlsgd_addr(ctx) + A - GOT, 9, 0);
|
||||
} else if (sym.has_gottp(ctx)) {
|
||||
u32 rd = bits(*(ub32 *)loc, 29, 25);
|
||||
*(ub32 *)loc = 0x8010'2000 | (rd << 25) | (rd << 14); // or %reg, $0, %reg
|
||||
*(ub32 *)loc |= bits(sym.get_gottp_addr(ctx) + A - GOT, 9, 0);
|
||||
} else {
|
||||
u32 rd = bits(*(ub32 *)loc, 29, 25);
|
||||
*(ub32 *)loc = 0x8018'2000 | (rd << 25) | (rd << 14); // xor %reg, $0, %reg
|
||||
*(ub32 *)loc |= bits(S + A - ctx.tp_addr, 9, 0) | 0b1'1100'0000'0000;
|
||||
}
|
||||
break;
|
||||
case R_SPARC_TLS_GD_ADD:
|
||||
if (sym.has_tlsgd(ctx)) {
|
||||
// do nothing
|
||||
} else if (sym.has_gottp(ctx)) {
|
||||
u32 rs2 = bits(*(ub32 *)loc, 4, 0);
|
||||
*(ub32 *)loc = 0xd05d'c000 | rs2; // ldx [ %l7 + %reg ], %o0
|
||||
} else {
|
||||
u32 rs2 = bits(*(ub32 *)loc, 4, 0);
|
||||
*(ub32 *)loc = 0x9001'c000 | rs2; // add %g7, %reg, %o0
|
||||
}
|
||||
break;
|
||||
case R_SPARC_TLS_GD_CALL:
|
||||
case R_SPARC_TLS_LDM_CALL: {
|
||||
u64 addr;
|
||||
if (ctx.arg.static_)
|
||||
addr = ctx.extra.tls_get_addr_sec->shdr.sh_addr;
|
||||
else
|
||||
addr = ctx.extra.tls_get_addr_sym->get_addr(ctx);
|
||||
|
||||
*(ub32 *)loc |= bits(addr + A - P, 31, 2);
|
||||
if (sym.has_tlsgd(ctx)) {
|
||||
u64 addr = ctx.extra.tls_get_addr->get_addr(ctx);
|
||||
*(ub32 *)loc |= bits(addr + A - P, 31, 2);
|
||||
} else if (sym.has_gottp(ctx)) {
|
||||
*(ub32 *)loc = 0x9001'c008; // add %g7, %o0, %o0
|
||||
} else {
|
||||
*(ub32 *)loc = 0x0100'0000; // nop
|
||||
}
|
||||
break;
|
||||
}
|
||||
case R_SPARC_TLS_LDM_HI22:
|
||||
*(ub32 *)loc |= bits(ctx.got->get_tlsld_addr(ctx) + A - GOT, 31, 10);
|
||||
if (ctx.got->has_tlsld(ctx))
|
||||
*(ub32 *)loc |= bits(ctx.got->get_tlsld_addr(ctx) + A - GOT, 31, 10);
|
||||
else
|
||||
*(ub32 *)loc |= bits(ctx.tp_addr - ctx.tls_begin, 31, 10);
|
||||
break;
|
||||
case R_SPARC_TLS_LDM_LO10:
|
||||
*(ub32 *)loc |= bits(ctx.got->get_tlsld_addr(ctx) + A - GOT, 9, 0);
|
||||
if (ctx.got->has_tlsld(ctx))
|
||||
*(ub32 *)loc |= bits(ctx.got->get_tlsld_addr(ctx) + A - GOT, 9, 0);
|
||||
else
|
||||
*(ub32 *)loc |= bits(ctx.tp_addr - ctx.tls_begin, 9, 0);
|
||||
break;
|
||||
case R_SPARC_TLS_LDM_ADD:
|
||||
if (ctx.got->has_tlsld(ctx)) {
|
||||
// do nothing
|
||||
} else {
|
||||
u32 rs2 = bits(*(ub32 *)loc, 4, 0);
|
||||
*(ub32 *)loc = 0x9021'c000 | rs2; // sub %g7, %reg, %o0
|
||||
}
|
||||
break;
|
||||
case R_SPARC_TLS_LDM_CALL:
|
||||
if (ctx.got->has_tlsld(ctx)) {
|
||||
u64 addr = ctx.extra.tls_get_addr->get_addr(ctx);
|
||||
*(ub32 *)loc |= bits(addr + A - P, 31, 2);
|
||||
} else {
|
||||
*(ub32 *)loc = 0x0100'0000; // nop
|
||||
}
|
||||
break;
|
||||
case R_SPARC_TLS_LDO_HIX22:
|
||||
*(ub32 *)loc |= bits(S + A - ctx.dtp_addr, 31, 10);
|
||||
@@ -402,8 +436,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
case R_SPARC_SIZE32:
|
||||
*(ub32 *)loc = sym.esym().st_size + A;
|
||||
break;
|
||||
case R_SPARC_TLS_GD_ADD:
|
||||
case R_SPARC_TLS_LDM_ADD:
|
||||
case R_SPARC_64:
|
||||
case R_SPARC_TLS_LDO_ADD:
|
||||
case R_SPARC_TLS_IE_LD:
|
||||
case R_SPARC_TLS_IE_LDX:
|
||||
@@ -427,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);
|
||||
@@ -441,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:
|
||||
@@ -450,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;
|
||||
@@ -471,8 +499,6 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
template <>
|
||||
void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
assert(shdr().sh_flags & SHF_ALLOC);
|
||||
|
||||
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
// Scan relocations
|
||||
@@ -487,9 +513,6 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
sym.flags |= NEEDS_GOT | NEEDS_PLT;
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_SPARC_64:
|
||||
scan_dyn_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_SPARC_8:
|
||||
case R_SPARC_5:
|
||||
case R_SPARC_6:
|
||||
@@ -554,24 +577,36 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
scan_pcrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_SPARC_TLS_GD_HI22:
|
||||
sym.flags |= NEEDS_TLSGD;
|
||||
if (ctx.arg.static_ || (ctx.arg.relax && sym.is_tprel_linktime_const(ctx))) {
|
||||
// We always relax if -static because libc.a doesn't contain
|
||||
// __tls_get_addr().
|
||||
} else if (ctx.arg.relax && sym.is_tprel_runtime_const(ctx)) {
|
||||
sym.flags |= NEEDS_GOTTP;
|
||||
} else {
|
||||
sym.flags |= NEEDS_TLSGD;
|
||||
}
|
||||
break;
|
||||
case R_SPARC_TLS_LDM_HI22:
|
||||
ctx.needs_tlsld = true;
|
||||
if (ctx.arg.static_ || (ctx.arg.relax && !ctx.arg.shared)) {
|
||||
// We always relax if -static because libc.a doesn't contain
|
||||
// __tls_get_addr().
|
||||
} else {
|
||||
ctx.needs_tlsld = true;
|
||||
}
|
||||
break;
|
||||
case R_SPARC_TLS_IE_HI22:
|
||||
sym.flags |= NEEDS_GOTTP;
|
||||
break;
|
||||
case R_SPARC_TLS_GD_CALL:
|
||||
case R_SPARC_TLS_LDM_CALL:
|
||||
if (!ctx.arg.static_)
|
||||
if (Symbol<E> &sym = *ctx.extra.tls_get_addr_sym; sym.is_imported)
|
||||
sym.flags |= NEEDS_PLT;
|
||||
if (Symbol<E> *sym = ctx.extra.tls_get_addr; sym->is_imported)
|
||||
sym->flags |= NEEDS_PLT;
|
||||
break;
|
||||
case R_SPARC_TLS_LE_HIX22:
|
||||
case R_SPARC_TLS_LE_LOX10:
|
||||
check_tlsle(ctx, sym, rel);
|
||||
break;
|
||||
case R_SPARC_64:
|
||||
case R_SPARC_GOTDATA_OP_LOX10:
|
||||
case R_SPARC_GOTDATA_OP:
|
||||
case R_SPARC_GOTDATA_LOX10:
|
||||
@@ -594,25 +629,4 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
// __tls_get_addr is not defined by libc.a, so we can't use that function
|
||||
// in statically-linked executables. This section provides a replacement.
|
||||
void SparcTlsGetAddrSection::copy_buf(Context<E> &ctx) {
|
||||
ub32 *buf = (ub32 *)(ctx.buf + this->shdr.sh_offset);
|
||||
|
||||
constexpr ub32 insn[] = {
|
||||
0x0300'0000, // sethi %hi(TP_SIZE), %g1
|
||||
0x8210'6000, // or %g1, %lo(TP_SIZE), %g1
|
||||
0x8221'c001, // sub %g7, %g1, %g1
|
||||
0xd05a'2008, // ldx [ %o0 + 8 ], %o0
|
||||
0x81c3'e008, // retl
|
||||
0x9000'4008, // add %g1, %o0, %o0
|
||||
};
|
||||
|
||||
assert(this->shdr.sh_size == sizeof(insn));
|
||||
memcpy(buf, insn, sizeof(insn));
|
||||
|
||||
buf[0] |= bits(ctx.tp_addr - ctx.tls_begin, 31, 10);
|
||||
buf[1] |= bits(ctx.tp_addr - ctx.tls_begin, 9, 0);
|
||||
}
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
@@ -23,14 +23,13 @@
|
||||
// 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"
|
||||
|
||||
#include <tbb/parallel_for_each.h>
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
using E = X86_64;
|
||||
|
||||
@@ -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, u64 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: {
|
||||
@@ -315,7 +381,7 @@ static void relax_ld_to_le(u8 *loc, ElfRel<E> rel, u64 tls_size) {
|
||||
//
|
||||
// Because the original instruction sequence is so short that we need a
|
||||
// little bit of code golfing here. "mov %fs:0, %rax" is 9 byte long, so
|
||||
// xor and mov is shorter. Note that `xor %eax, %eax` zero-clears %eax.
|
||||
// xor + mov is shorter. Note that `xor %eax, %eax` zero-clears %eax.
|
||||
static const u8 insn[] = {
|
||||
0x31, 0xc0, // xor %eax, %eax
|
||||
0x64, 0x48, 0x8b, 0x00, // mov %fs:(%rax), %rax
|
||||
@@ -332,13 +398,12 @@ static void relax_ld_to_le(u8 *loc, ElfRel<E> rel, u64 tls_size) {
|
||||
// 48 8d 3d 00 00 00 00 lea foo@tlsld(%rip), %rdi
|
||||
// ff 15 00 00 00 00 call *__tls_get_addr@GOT(%rip)
|
||||
static const u8 insn[] = {
|
||||
0x31, 0xc0, // xor %eax, %eax
|
||||
0x48, 0x31, 0xc0, // xor %rax, %rax
|
||||
0x64, 0x48, 0x8b, 0x00, // mov %fs:(%rax), %rax
|
||||
0x48, 0x2d, 0, 0, 0, 0, // sub $tls_size, %rax
|
||||
0x90, // nop
|
||||
};
|
||||
memcpy(loc - 3, insn, sizeof(insn));
|
||||
*(ul32 *)(loc + 5) = tls_size;
|
||||
*(ul32 *)(loc + 6) = tls_size;
|
||||
break;
|
||||
}
|
||||
case R_X86_64_PLTOFF64: {
|
||||
@@ -369,11 +434,6 @@ template <>
|
||||
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
ElfRel<E> *dynrel = nullptr;
|
||||
if (ctx.reldyn)
|
||||
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
|
||||
file.reldyn_offset + this->reldyn_offset);
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
if (rel.r_type == R_NONE)
|
||||
@@ -382,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) {
|
||||
@@ -399,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);
|
||||
@@ -421,7 +478,6 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
write32s(S + A);
|
||||
break;
|
||||
case R_X86_64_64:
|
||||
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
|
||||
break;
|
||||
case R_X86_64_PC8:
|
||||
check(S + A - P, -(1 << 7), 1 << 7);
|
||||
@@ -461,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;
|
||||
}
|
||||
}
|
||||
@@ -517,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.
|
||||
//
|
||||
@@ -537,16 +583,18 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
// call *(%rax)
|
||||
// R_X86_64_TLSDESC_CALL foo
|
||||
//
|
||||
// We may relax the instructions to the following for non-dlopen'd DSO
|
||||
//
|
||||
// mov foo@GOTTPOFF(%rip), %rax
|
||||
// nop
|
||||
//
|
||||
// or to the following for executable.
|
||||
// We may relax the instructions to the following if its TP-relative
|
||||
// address is known at link-time
|
||||
//
|
||||
// mov $foo@TPOFF, %rax
|
||||
// nop
|
||||
//
|
||||
// or to the following if the TP-relative address is known at
|
||||
// process startup time.
|
||||
//
|
||||
// mov foo@GOTTPOFF(%rip), %rax
|
||||
// nop
|
||||
//
|
||||
// We allow the following alternative code sequence too because
|
||||
// LLVM emits such code.
|
||||
//
|
||||
@@ -558,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;
|
||||
@@ -576,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;
|
||||
@@ -620,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) {
|
||||
@@ -637,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);
|
||||
@@ -708,8 +751,6 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
template <>
|
||||
void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
assert(shdr().sh_flags & SHF_ALLOC);
|
||||
|
||||
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
// Scan relocations
|
||||
@@ -744,9 +785,6 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_X86_64_32S:
|
||||
scan_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_X86_64_64:
|
||||
scan_dyn_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_X86_64_PC8:
|
||||
case R_X86_64_PC16:
|
||||
case R_X86_64_PC32:
|
||||
@@ -761,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:
|
||||
@@ -769,7 +808,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
sym.flags |= NEEDS_PLT;
|
||||
break;
|
||||
case R_X86_64_TLSGD:
|
||||
if ((ctx.arg.relax && sym.is_tprel_linktime_const(ctx)) || ctx.arg.static_) {
|
||||
if (ctx.arg.static_ || (ctx.arg.relax && sym.is_tprel_linktime_const(ctx))) {
|
||||
// We always relax if -static because libc.a doesn't contain
|
||||
// __tls_get_addr().
|
||||
i++;
|
||||
@@ -789,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);
|
||||
@@ -803,12 +843,14 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_X86_64_TPOFF64:
|
||||
check_tlsle(ctx, sym, rel);
|
||||
break;
|
||||
case R_X86_64_64:
|
||||
case R_X86_64_GOTOFF64:
|
||||
case R_X86_64_DTPOFF32:
|
||||
case R_X86_64_DTPOFF64:
|
||||
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;
|
||||
@@ -907,4 +949,4 @@ void rewrite_endbr(Context<E> &ctx) {
|
||||
keep(sym);
|
||||
}
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
+145
-86
@@ -1,13 +1,17 @@
|
||||
#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>
|
||||
|
||||
#if __has_include(<sys/utsname.h>)
|
||||
# include <sys/utsname.h>
|
||||
#endif
|
||||
|
||||
#if __has_include(<unistd.h>)
|
||||
# include <unistd.h>
|
||||
#else
|
||||
@@ -15,9 +19,9 @@
|
||||
# define STDERR_FILENO (_fileno(stderr))
|
||||
#endif
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
inline const char helpmsg[] = R"(
|
||||
static const char helpmsg[] = R"(
|
||||
Options:
|
||||
--help Report usage information
|
||||
-v, --version Report version information
|
||||
@@ -37,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
|
||||
@@ -72,7 +78,8 @@ Options:
|
||||
--no-apply-dynamic-relocs
|
||||
--as-needed Only set DT_NEEDED if used
|
||||
--no-as-needed
|
||||
--build-id [none,md5,sha1,sha256,uuid,HEXSTRING]
|
||||
--audit LIBNAME Set DT_AUDIT to the specified value
|
||||
--build-id [none,md5,sha1,sha256,fast,uuid,HEXSTRING]
|
||||
Generate build ID
|
||||
--no-build-id
|
||||
--chroot DIR Set a given path to the root directory
|
||||
@@ -123,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
|
||||
@@ -222,8 +230,8 @@ Options:
|
||||
-z notext
|
||||
-z textoff
|
||||
|
||||
mold: supported targets: elf32-i386 elf64-x86-64 elf32-littlearm elf64-littleaarch64 elf32-littleriscv elf32-bigriscv elf64-littleriscv elf64-bigriscv elf32-powerpc elf64-powerpc elf64-powerpc elf64-powerpcle elf64-s390 elf64-sparc elf32-m68k elf32-sh-linux elf64-alpha elf64-loongarch elf32-loongarch
|
||||
mold: supported emulations: elf_i386 elf_x86_64 armelf_linux_eabi aarch64linux aarch64elf elf32lriscv elf32briscv elf64lriscv elf64briscv elf32ppc elf32ppclinux elf64ppc elf64lppc elf64_s390 elf64_sparc m68kelf shlelf_linux elf64alpha elf64loongarch elf32loongarch)";
|
||||
mold: supported targets: elf32-i386 elf64-x86-64 elf32-littlearm elf64-littleaarch64 elf64-bigaarch64 elf32-littleriscv elf32-bigriscv elf64-littleriscv elf64-bigriscv elf32-powerpc elf64-powerpc elf64-powerpc elf64-powerpcle elf64-s390 elf64-sparc elf32-m68k elf32-sh-linux elf64-loongarch elf32-loongarch
|
||||
mold: supported emulations: elf_i386 elf_x86_64 armelf_linux_eabi aarch64elf aarch64linux aarch64elfb aarch64linuxb elf32lriscv elf32briscv elf64lriscv elf64briscv elf32ppc elf32ppclinux elf64ppc elf64lppc elf64_s390 elf64_sparc m68kelf shlelf_linux shelf_linux elf64loongarch elf32loongarch)";
|
||||
|
||||
template <typename E>
|
||||
static std::vector<std::string_view>
|
||||
@@ -317,7 +325,7 @@ static i64 get_default_thread_count() {
|
||||
return std::min(n, 32);
|
||||
}
|
||||
|
||||
static inline std::string_view string_trim(std::string_view str) {
|
||||
static std::string_view string_trim(std::string_view str) {
|
||||
size_t pos = str.find_first_not_of(" \t");
|
||||
if (pos == str.npos)
|
||||
return "";
|
||||
@@ -398,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()) {
|
||||
@@ -429,7 +437,7 @@ split_by_comma_or_colon(std::string_view str) {
|
||||
break;
|
||||
}
|
||||
vec.push_back(str.substr(0, pos));
|
||||
str = str.substr(pos);
|
||||
str = str.substr(pos + 1);
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
@@ -438,8 +446,7 @@ template <typename E>
|
||||
static void read_retain_symbols_file(Context<E> &ctx, std::string_view path) {
|
||||
MappedFile *mf = must_open_file(ctx, std::string(path));
|
||||
std::string_view data((char *)mf->data, mf->size);
|
||||
|
||||
ctx.arg.retain_symbols_file.reset(new std::unordered_set<std::string_view>);
|
||||
std::vector<Symbol<E> *> vec;
|
||||
|
||||
while (!data.empty()) {
|
||||
size_t pos = data.find('\n');
|
||||
@@ -455,14 +462,15 @@ static void read_retain_symbols_file(Context<E> &ctx, std::string_view path) {
|
||||
|
||||
name = string_trim(name);
|
||||
if (!name.empty())
|
||||
ctx.arg.retain_symbols_file->insert(name);
|
||||
vec.push_back(get_symbol(ctx, name));
|
||||
}
|
||||
|
||||
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>
|
||||
@@ -538,6 +546,33 @@ parse_defsym_value(Context<E> &ctx, std::string_view s) {
|
||||
return get_symbol(ctx, s);
|
||||
}
|
||||
|
||||
// Parses a kernel version string, e.g. "6.8.0-47-generic".
|
||||
static std::tuple<int, int, int>
|
||||
parse_kernel_version(std::string str) {
|
||||
auto flags = std::regex_constants::optimize | std::regex_constants::ECMAScript;
|
||||
static std::regex re(R"(^(\d+)\.(\d+)\.(\d+))", flags);
|
||||
std::smatch m;
|
||||
|
||||
if (!std::regex_search(str, m, re))
|
||||
return {0, 0, 0};
|
||||
return {std::stoi(m[1]), std::stoi(m[2]), std::stoi(m[3])};
|
||||
}
|
||||
|
||||
// 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;
|
||||
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>
|
||||
std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
std::span<std::string_view> args = ctx.cmdline_args;
|
||||
@@ -554,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;
|
||||
@@ -567,9 +603,9 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
}
|
||||
};
|
||||
|
||||
// RISC-V object files contains lots of local symbols, so by default
|
||||
// we discard them. This is compatible with GNU ld.
|
||||
if constexpr (is_riscv<E>)
|
||||
// RISC-V and LoongArch object files contains lots of local symbols,
|
||||
// so by default we discard them. This is compatible with GNU ld.
|
||||
if constexpr (is_riscv<E> || is_loongarch<E>)
|
||||
ctx.arg.discard_locals = true;
|
||||
|
||||
// We generally don't need to write addends to relocated places if the
|
||||
@@ -677,54 +713,62 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
} else if (read_flag("V")) {
|
||||
Out(ctx) << get_mold_version()
|
||||
<< "\n Supported emulations:\n elf_x86_64\n elf_i386\n"
|
||||
<< " aarch64linux\n armelf_linux_eabi\n elf64lriscv\n"
|
||||
<< " aarch64elf\n aarch64linux\n aarch64elfb\n"
|
||||
<< " aarch64linuxb\n armelf_linux_eabi\n elf64lriscv\n"
|
||||
<< " elf64briscv\n elf32lriscv\n elf32briscv\n"
|
||||
<< " elf32ppc\n elf64ppc\n elf64lppc\n elf64_s390\n"
|
||||
<< " elf64_sparc\n m68kelf\n shlelf_linux\n"
|
||||
<< " elf64alpha\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::target_name;
|
||||
ctx.arg.emulation = X86_64::name;
|
||||
} else if (arg == "elf_i386") {
|
||||
ctx.arg.emulation = I386::target_name;
|
||||
} else if (arg == "aarch64linux") {
|
||||
ctx.arg.emulation = ARM64::target_name;
|
||||
ctx.arg.emulation = I386::name;
|
||||
} else if (arg == "aarch64elf" || arg == "aarch64linux") {
|
||||
ctx.arg.emulation = ARM64LE::name;
|
||||
} else if (arg == "aarch64elfb" || arg == "aarch64linuxb") {
|
||||
ctx.arg.emulation = ARM64BE::name;
|
||||
} else if (arg == "armelf_linux_eabi") {
|
||||
ctx.arg.emulation = ARM32::target_name;
|
||||
ctx.arg.emulation = ARM32::name;
|
||||
} else if (arg == "elf64lriscv") {
|
||||
ctx.arg.emulation = RV64LE::target_name;
|
||||
ctx.arg.emulation = RV64LE::name;
|
||||
} else if (arg == "elf64briscv") {
|
||||
ctx.arg.emulation = RV64BE::target_name;
|
||||
ctx.arg.emulation = RV64BE::name;
|
||||
} else if (arg == "elf32lriscv") {
|
||||
ctx.arg.emulation = RV32LE::target_name;
|
||||
ctx.arg.emulation = RV32LE::name;
|
||||
} else if (arg == "elf32briscv") {
|
||||
ctx.arg.emulation = RV32BE::target_name;
|
||||
ctx.arg.emulation = RV32BE::name;
|
||||
} else if (arg == "elf32ppc" || arg == "elf32ppclinux") {
|
||||
ctx.arg.emulation = PPC32::target_name;
|
||||
ctx.arg.emulation = PPC32::name;
|
||||
} else if (arg == "elf64ppc") {
|
||||
ctx.arg.emulation = PPC64V1::target_name;
|
||||
ctx.arg.emulation = PPC64V1::name;
|
||||
} else if (arg == "elf64lppc") {
|
||||
ctx.arg.emulation = PPC64V2::target_name;
|
||||
ctx.arg.emulation = PPC64V2::name;
|
||||
} else if (arg == "elf64_s390") {
|
||||
ctx.arg.emulation = S390X::target_name;
|
||||
ctx.arg.emulation = S390X::name;
|
||||
} else if (arg == "elf64_sparc") {
|
||||
ctx.arg.emulation = SPARC64::target_name;
|
||||
ctx.arg.emulation = SPARC64::name;
|
||||
} else if (arg == "m68kelf") {
|
||||
ctx.arg.emulation = M68K::target_name;
|
||||
} else if (arg == "shlelf_linux") {
|
||||
ctx.arg.emulation = SH4::target_name;
|
||||
} else if (arg == "elf64alpha") {
|
||||
ctx.arg.emulation = ALPHA::target_name;
|
||||
ctx.arg.emulation = M68K::name;
|
||||
} else if (arg == "shlelf" || arg == "shlelf_linux") {
|
||||
ctx.arg.emulation = SH4LE::name;
|
||||
} else if (arg == "shelf" || arg == "shelf_linux") {
|
||||
ctx.arg.emulation = SH4BE::name;
|
||||
} else if (arg == "elf64loongarch") {
|
||||
ctx.arg.emulation = LOONGARCH64::target_name;
|
||||
ctx.arg.emulation = LOONGARCH64::name;
|
||||
} else if (arg == "elf32loongarch") {
|
||||
ctx.arg.emulation = LOONGARCH32::target_name;
|
||||
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")) {
|
||||
@@ -755,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")) {
|
||||
@@ -767,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")) {
|
||||
@@ -810,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")) {
|
||||
@@ -852,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")) {
|
||||
@@ -896,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;
|
||||
@@ -1054,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")) {
|
||||
@@ -1095,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;
|
||||
@@ -1125,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")) {
|
||||
@@ -1156,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);
|
||||
@@ -1169,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));
|
||||
@@ -1232,7 +1283,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
} else if (arg == "sha1") {
|
||||
ctx.arg.build_id.kind = BuildId::HASH;
|
||||
ctx.arg.build_id.hash_size = 20;
|
||||
} else if (arg == "sha256") {
|
||||
} else if (arg == "sha256" || arg == "fast") {
|
||||
ctx.arg.build_id.kind = BuildId::HASH;
|
||||
ctx.arg.build_id.hash_size = 32;
|
||||
} else if (arg.starts_with("0x") || arg.starts_with("0X")) {
|
||||
@@ -1298,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")) {
|
||||
@@ -1310,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);
|
||||
@@ -1337,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);
|
||||
}
|
||||
}
|
||||
@@ -1396,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;
|
||||
@@ -1417,8 +1476,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
// to handle it as an exception.
|
||||
if constexpr (!E::is_rela || is_sh4<E>)
|
||||
if (!ctx.arg.apply_dynamic_relocs)
|
||||
Fatal(ctx) << "--no-apply-dynamic-relocs may not be used on "
|
||||
<< E::target_name;
|
||||
Fatal(ctx) << "--no-apply-dynamic-relocs may not be used on " << E::name;
|
||||
|
||||
if constexpr (is_sparc<E>)
|
||||
if (ctx.arg.apply_dynamic_relocs)
|
||||
@@ -1437,8 +1495,10 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
ctx.arg.repro = true;
|
||||
|
||||
if (ctx.arg.default_symver) {
|
||||
std::string ver = ctx.arg.soname.empty() ?
|
||||
filepath(ctx.arg.output).filename().string() : std::string(ctx.arg.soname);
|
||||
std::string ver = ctx.arg.soname;
|
||||
if (ver.empty())
|
||||
ver = path_filename(ctx.arg.output);
|
||||
|
||||
ctx.arg.version_definitions.push_back(ver);
|
||||
ctx.default_version = VER_NDX_LAST_RESERVED + 1;
|
||||
}
|
||||
@@ -1496,8 +1556,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
// However, that mechanism doesn't protect .so files. Therefore, we
|
||||
// want to disable this optimization if we are creating a shared
|
||||
// object file.
|
||||
if (ctx.arg.shared)
|
||||
ctx.overwrite_output_file = false;
|
||||
ctx.overwrite_output_file = (!ctx.arg.shared && returns_etxtbsy());
|
||||
|
||||
if (!ctx.arg.chroot.empty()) {
|
||||
if (!ctx.arg.Map.empty())
|
||||
@@ -1524,4 +1583,4 @@ using E = MOLD_TARGET;
|
||||
template std::vector<std::string_view> expand_response_files(Context<E> &, char **);
|
||||
template std::vector<std::string> parse_nonpositional_args(Context<E> &ctx);
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "mold.h"
|
||||
#include "config.h"
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
std::string get_mold_version() {
|
||||
if (mold_git_hash.empty())
|
||||
@@ -10,4 +9,4 @@ std::string get_mold_version() {
|
||||
"; compatible with GNU ld)";
|
||||
}
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
+93
-43
@@ -1,6 +1,6 @@
|
||||
#include "mold.h"
|
||||
#include "elf.h"
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
static std::string unknown_type(u32 r_type) {
|
||||
char buf[50];
|
||||
@@ -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);
|
||||
}
|
||||
@@ -107,7 +116,7 @@ std::string rel_to_string<I386>(u32 r_type) {
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string rel_to_string<ARM64>(u32 r_type) {
|
||||
std::string rel_to_string<ARM64LE>(u32 r_type) {
|
||||
switch (r_type) {
|
||||
CASE(R_AARCH64_NONE);
|
||||
CASE(R_AARCH64_ABS64);
|
||||
@@ -217,6 +226,11 @@ std::string rel_to_string<ARM64>(u32 r_type) {
|
||||
return unknown_type(r_type);
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string rel_to_string<ARM64BE>(u32 r_type) {
|
||||
return rel_to_string<ARM64LE>(r_type);
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string rel_to_string<ARM32>(u32 r_type) {
|
||||
switch (r_type) {
|
||||
@@ -402,7 +416,6 @@ std::string rel_to_string<RV64LE>(u32 r_type) {
|
||||
CASE(R_RISCV_ALIGN);
|
||||
CASE(R_RISCV_RVC_BRANCH);
|
||||
CASE(R_RISCV_RVC_JUMP);
|
||||
CASE(R_RISCV_RVC_LUI);
|
||||
CASE(R_RISCV_RELAX);
|
||||
CASE(R_RISCV_SUB6);
|
||||
CASE(R_RISCV_SET6);
|
||||
@@ -615,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);
|
||||
@@ -857,7 +871,7 @@ std::string rel_to_string<M68K>(u32 r_type) {
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string rel_to_string<SH4>(u32 r_type) {
|
||||
std::string rel_to_string<SH4LE>(u32 r_type) {
|
||||
switch (r_type) {
|
||||
CASE(R_SH_NONE);
|
||||
CASE(R_SH_DIR32);
|
||||
@@ -891,43 +905,8 @@ std::string rel_to_string<SH4>(u32 r_type) {
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string rel_to_string<ALPHA>(u32 r_type) {
|
||||
switch (r_type) {
|
||||
CASE(R_ALPHA_NONE);
|
||||
CASE(R_ALPHA_REFLONG);
|
||||
CASE(R_ALPHA_REFQUAD);
|
||||
CASE(R_ALPHA_GPREL32);
|
||||
CASE(R_ALPHA_LITERAL);
|
||||
CASE(R_ALPHA_LITUSE);
|
||||
CASE(R_ALPHA_GPDISP);
|
||||
CASE(R_ALPHA_BRADDR);
|
||||
CASE(R_ALPHA_HINT);
|
||||
CASE(R_ALPHA_SREL16);
|
||||
CASE(R_ALPHA_SREL32);
|
||||
CASE(R_ALPHA_SREL64);
|
||||
CASE(R_ALPHA_GPRELHIGH);
|
||||
CASE(R_ALPHA_GPRELLOW);
|
||||
CASE(R_ALPHA_GPREL16);
|
||||
CASE(R_ALPHA_COPY);
|
||||
CASE(R_ALPHA_GLOB_DAT);
|
||||
CASE(R_ALPHA_JMP_SLOT);
|
||||
CASE(R_ALPHA_RELATIVE);
|
||||
CASE(R_ALPHA_BRSGP);
|
||||
CASE(R_ALPHA_TLSGD);
|
||||
CASE(R_ALPHA_TLSLDM);
|
||||
CASE(R_ALPHA_DTPMOD64);
|
||||
CASE(R_ALPHA_GOTDTPREL);
|
||||
CASE(R_ALPHA_DTPREL64);
|
||||
CASE(R_ALPHA_DTPRELHI);
|
||||
CASE(R_ALPHA_DTPRELLO);
|
||||
CASE(R_ALPHA_DTPREL16);
|
||||
CASE(R_ALPHA_GOTTPREL);
|
||||
CASE(R_ALPHA_TPREL64);
|
||||
CASE(R_ALPHA_TPRELHI);
|
||||
CASE(R_ALPHA_TPRELLO);
|
||||
CASE(R_ALPHA_TPREL16);
|
||||
}
|
||||
return unknown_type(r_type);
|
||||
std::string rel_to_string<SH4BE>(u32 r_type) {
|
||||
return rel_to_string<SH4LE>(r_type);
|
||||
}
|
||||
|
||||
template <>
|
||||
@@ -946,6 +925,8 @@ std::string rel_to_string<LOONGARCH64>(u32 r_type) {
|
||||
CASE(R_LARCH_TLS_TPREL32);
|
||||
CASE(R_LARCH_TLS_TPREL64);
|
||||
CASE(R_LARCH_IRELATIVE);
|
||||
CASE(R_LARCH_TLS_DESC32);
|
||||
CASE(R_LARCH_TLS_DESC64);
|
||||
CASE(R_LARCH_MARK_LA);
|
||||
CASE(R_LARCH_MARK_PCREL);
|
||||
CASE(R_LARCH_SOP_PUSH_PCREL);
|
||||
@@ -1057,4 +1038,73 @@ std::string rel_to_string<LOONGARCH32>(u32 r_type) {
|
||||
return rel_to_string<LOONGARCH64>(r_type);
|
||||
}
|
||||
|
||||
} // namespace mold::elf
|
||||
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
|
||||
+240
-229
@@ -1,17 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common/integers.h"
|
||||
#include "../lib/integers.h"
|
||||
|
||||
#include <concepts>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
struct X86_64;
|
||||
struct I386;
|
||||
struct ARM64;
|
||||
struct ARM64LE;
|
||||
struct ARM64BE;
|
||||
struct ARM32;
|
||||
struct RV64LE;
|
||||
struct RV64BE;
|
||||
@@ -23,10 +24,11 @@ struct PPC64V2;
|
||||
struct S390X;
|
||||
struct SPARC64;
|
||||
struct M68K;
|
||||
struct SH4;
|
||||
struct ALPHA;
|
||||
struct SH4LE;
|
||||
struct SH4BE;
|
||||
struct LOONGARCH64;
|
||||
struct LOONGARCH32;
|
||||
struct ARC;
|
||||
|
||||
template <typename E> struct ElfSym;
|
||||
template <typename E> struct ElfShdr;
|
||||
@@ -50,10 +52,6 @@ std::ostream &operator<<(std::ostream &out, const ElfRel<E> &rel) {
|
||||
return out;
|
||||
}
|
||||
|
||||
enum : u32 {
|
||||
R_NONE = 0,
|
||||
};
|
||||
|
||||
enum : u32 {
|
||||
SHN_UNDEF = 0,
|
||||
SHN_LORESERVE = 0xff00,
|
||||
@@ -81,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,
|
||||
@@ -90,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,
|
||||
@@ -191,6 +191,7 @@ enum : u32 {
|
||||
PT_GNU_EH_FRAME = 0x6474e550,
|
||||
PT_GNU_STACK = 0x6474e551,
|
||||
PT_GNU_RELRO = 0x6474e552,
|
||||
PT_GNU_PROPERTY = 0x6474e553,
|
||||
PT_OPENBSD_RANDOMIZE = 0x65a3dbe6,
|
||||
PT_ARM_EXIDX = 0x70000001,
|
||||
PT_RISCV_ATTRIBUTES = 0x70000003,
|
||||
@@ -236,9 +237,9 @@ enum : u32 {
|
||||
EM_SPARC64 = 43,
|
||||
EM_X86_64 = 62,
|
||||
EM_AARCH64 = 183,
|
||||
EM_ARC_COMPACT2 = 195,
|
||||
EM_RISCV = 243,
|
||||
EM_LOONGARCH = 258,
|
||||
EM_ALPHA = 0x9026,
|
||||
};
|
||||
|
||||
enum : u32 {
|
||||
@@ -287,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,
|
||||
@@ -297,6 +300,7 @@ enum : u32 {
|
||||
DT_VERNEEDNUM = 0x6fffffff,
|
||||
DT_PPC_GOT = 0x70000000,
|
||||
DT_PPC64_GLINK = 0x70000000,
|
||||
DT_RISCV_VARIANT_CC = 0x70000001,
|
||||
DT_AARCH64_VARIANT_PCS = 0x70000005,
|
||||
DT_AUXILIARY = 0x7ffffffd,
|
||||
DT_FILTER = 0x7fffffff,
|
||||
@@ -385,8 +389,6 @@ enum : u32 {
|
||||
|
||||
enum : u32 {
|
||||
STO_RISCV_VARIANT_CC = 0x80,
|
||||
STO_ALPHA_NOPV = 0x20,
|
||||
STO_ALPHA_STD_GPLOAD = 0x22,
|
||||
};
|
||||
|
||||
enum : u32 {
|
||||
@@ -411,6 +413,10 @@ enum : u32 {
|
||||
// Relocation types
|
||||
//
|
||||
|
||||
enum : u32 {
|
||||
R_NONE = 0,
|
||||
};
|
||||
|
||||
enum : u32 {
|
||||
R_X86_64_NONE = 0,
|
||||
R_X86_64_64 = 1,
|
||||
@@ -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 {
|
||||
@@ -786,10 +801,6 @@ enum : u32 {
|
||||
R_RISCV_ALIGN = 43,
|
||||
R_RISCV_RVC_BRANCH = 44,
|
||||
R_RISCV_RVC_JUMP = 45,
|
||||
R_RISCV_RVC_LUI = 46,
|
||||
R_RISCV_GPREL_LO12_I = 47,
|
||||
R_RISCV_GPREL_LO12_S = 48,
|
||||
R_RISCV_GPREL_HI20 = 49,
|
||||
R_RISCV_RELAX = 51,
|
||||
R_RISCV_SUB6 = 52,
|
||||
R_RISCV_SET6 = 53,
|
||||
@@ -979,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,
|
||||
@@ -1233,42 +1245,6 @@ enum : u32 {
|
||||
R_SH_GOTPLT32 = 168,
|
||||
};
|
||||
|
||||
enum : u32 {
|
||||
R_ALPHA_NONE = 0,
|
||||
R_ALPHA_REFLONG = 1,
|
||||
R_ALPHA_REFQUAD = 2,
|
||||
R_ALPHA_GPREL32 = 3,
|
||||
R_ALPHA_LITERAL = 4,
|
||||
R_ALPHA_LITUSE = 5,
|
||||
R_ALPHA_GPDISP = 6,
|
||||
R_ALPHA_BRADDR = 7,
|
||||
R_ALPHA_HINT = 8,
|
||||
R_ALPHA_SREL16 = 9,
|
||||
R_ALPHA_SREL32 = 10,
|
||||
R_ALPHA_SREL64 = 11,
|
||||
R_ALPHA_GPRELHIGH = 17,
|
||||
R_ALPHA_GPRELLOW = 18,
|
||||
R_ALPHA_GPREL16 = 19,
|
||||
R_ALPHA_COPY = 24,
|
||||
R_ALPHA_GLOB_DAT = 25,
|
||||
R_ALPHA_JMP_SLOT = 26,
|
||||
R_ALPHA_RELATIVE = 27,
|
||||
R_ALPHA_BRSGP = 28,
|
||||
R_ALPHA_TLSGD = 29,
|
||||
R_ALPHA_TLSLDM = 30,
|
||||
R_ALPHA_DTPMOD64 = 31,
|
||||
R_ALPHA_GOTDTPREL = 32,
|
||||
R_ALPHA_DTPREL64 = 33,
|
||||
R_ALPHA_DTPRELHI = 34,
|
||||
R_ALPHA_DTPRELLO = 35,
|
||||
R_ALPHA_DTPREL16 = 36,
|
||||
R_ALPHA_GOTTPREL = 37,
|
||||
R_ALPHA_TPREL64 = 38,
|
||||
R_ALPHA_TPRELHI = 39,
|
||||
R_ALPHA_TPRELLO = 40,
|
||||
R_ALPHA_TPREL16 = 41,
|
||||
};
|
||||
|
||||
enum : u32 {
|
||||
R_LARCH_NONE = 0,
|
||||
R_LARCH_32 = 1,
|
||||
@@ -1283,6 +1259,8 @@ enum : u32 {
|
||||
R_LARCH_TLS_TPREL32 = 10,
|
||||
R_LARCH_TLS_TPREL64 = 11,
|
||||
R_LARCH_IRELATIVE = 12,
|
||||
R_LARCH_TLS_DESC32 = 13,
|
||||
R_LARCH_TLS_DESC64 = 14,
|
||||
R_LARCH_MARK_LA = 20,
|
||||
R_LARCH_MARK_PCREL = 21,
|
||||
R_LARCH_SOP_PUSH_PCREL = 22,
|
||||
@@ -1387,6 +1365,81 @@ 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>
|
||||
inline bool is_func_call_rel(const ElfRel<E> &r) {
|
||||
for (u32 r_type : E::R_FUNCALL)
|
||||
if (r.r_type == r_type)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// DWARF data types
|
||||
//
|
||||
@@ -1501,7 +1554,7 @@ template <typename E> using U32 = std::conditional_t<E::is_le, ul32, ub32>;
|
||||
template <typename E> using U64 = std::conditional_t<E::is_le, ul64, ub64>;
|
||||
|
||||
template <typename E> using Word = std::conditional_t<E::is_64, U64<E>, U32<E>>;
|
||||
template <typename E> using IWord = std::conditional_t<E::is_64, I64<E>, I32<E>>;
|
||||
template <typename E> using SWord = std::conditional_t<E::is_64, I64<E>, I32<E>>;
|
||||
|
||||
template <typename E> requires E::is_64
|
||||
struct ElfSym<E> {
|
||||
@@ -1511,18 +1564,41 @@ struct ElfSym<E> {
|
||||
bool is_weak() const { return st_bind == STB_WEAK; }
|
||||
bool is_undef_weak() const { return is_undef() && is_weak(); }
|
||||
|
||||
bool ppc64_preserves_r2() const { return ppc64_local_entry != 1; }
|
||||
bool ppc64_uses_toc() const { return ppc64_local_entry > 1; }
|
||||
|
||||
U32<E> st_name;
|
||||
|
||||
#ifdef __LITTLE_ENDIAN__
|
||||
u8 st_type : 4;
|
||||
u8 st_bind : 4;
|
||||
u8 st_visibility : 2;
|
||||
u8 : 6;
|
||||
union {
|
||||
u8 st_visibility : 2;
|
||||
struct {
|
||||
u8 : 7;
|
||||
u8 arm64_variant_pcs : 1;
|
||||
};
|
||||
struct {
|
||||
u8 : 7;
|
||||
u8 riscv_variant_cc : 1;
|
||||
};
|
||||
struct {
|
||||
u8 : 5;
|
||||
u8 ppc64_local_entry : 3;
|
||||
};
|
||||
};
|
||||
#else
|
||||
u8 st_bind : 4;
|
||||
u8 st_type : 4;
|
||||
u8 : 6;
|
||||
u8 st_visibility : 2;
|
||||
union {
|
||||
struct {
|
||||
u8 : 6;
|
||||
u8 st_visibility : 2;
|
||||
};
|
||||
u8 arm64_variant_pcs : 1;
|
||||
u8 riscv_variant_cc : 1;
|
||||
u8 ppc64_local_entry : 3;
|
||||
};
|
||||
#endif
|
||||
|
||||
U16<E> st_shndx;
|
||||
@@ -1545,13 +1621,23 @@ struct ElfSym<E> {
|
||||
#ifdef __LITTLE_ENDIAN__
|
||||
u8 st_type : 4;
|
||||
u8 st_bind : 4;
|
||||
u8 st_visibility : 2;
|
||||
u8 : 6;
|
||||
union {
|
||||
u8 st_visibility : 2;
|
||||
struct {
|
||||
u8 : 7;
|
||||
u8 riscv_variant_cc : 1;
|
||||
};
|
||||
};
|
||||
#else
|
||||
u8 st_bind : 4;
|
||||
u8 st_type : 4;
|
||||
u8 : 6;
|
||||
u8 st_visibility : 2;
|
||||
union {
|
||||
struct {
|
||||
u8 : 6;
|
||||
u8 st_visibility : 2;
|
||||
};
|
||||
u8 riscv_variant_cc : 1;
|
||||
};
|
||||
#endif
|
||||
|
||||
U16<E> st_shndx;
|
||||
@@ -1625,7 +1711,7 @@ struct ElfPhdr<E> {
|
||||
// We don't want to have too many `if (REL)`s and `if (RELA)`s in our
|
||||
// codebase, so ElfRel always takes r_addend as a constructor argument.
|
||||
// If it's REL, the argument will simply be ignored.
|
||||
template <typename E> requires E::is_le && E::is_rela
|
||||
template <typename E> requires (E::is_le && E::is_rela)
|
||||
struct ElfRel<E> {
|
||||
ElfRel() = default;
|
||||
ElfRel(u64 offset, u32 type, u32 sym, i64 addend)
|
||||
@@ -1634,10 +1720,10 @@ struct ElfRel<E> {
|
||||
Word<E> r_offset;
|
||||
std::conditional_t<E::is_64, U32<E>, u8> r_type;
|
||||
std::conditional_t<E::is_64, U32<E>, U24<E>> r_sym;
|
||||
IWord<E> r_addend;
|
||||
SWord<E> r_addend;
|
||||
};
|
||||
|
||||
template <typename E> requires (!E::is_le) && E::is_rela
|
||||
template <typename E> requires (!E::is_le && E::is_rela)
|
||||
struct ElfRel<E> {
|
||||
ElfRel() = default;
|
||||
ElfRel(u64 offset, u32 type, u32 sym, i64 addend)
|
||||
@@ -1646,10 +1732,10 @@ struct ElfRel<E> {
|
||||
Word<E> r_offset;
|
||||
std::conditional_t<E::is_64, U32<E>, U24<E>> r_sym;
|
||||
std::conditional_t<E::is_64, U32<E>, u8> r_type;
|
||||
IWord<E> r_addend;
|
||||
SWord<E> r_addend;
|
||||
};
|
||||
|
||||
template <typename E> requires E::is_le && (!E::is_rela)
|
||||
template <typename E> requires (E::is_le && !E::is_rela)
|
||||
struct ElfRel<E> {
|
||||
ElfRel() = default;
|
||||
ElfRel(u64 offset, u32 type, u32 sym, i64 addend = 0)
|
||||
@@ -1660,7 +1746,7 @@ struct ElfRel<E> {
|
||||
std::conditional_t<E::is_64, U32<E>, U24<E>> r_sym;
|
||||
};
|
||||
|
||||
template <typename E> requires (!E::is_le) && (!E::is_rela)
|
||||
template <typename E> requires (!E::is_le && !E::is_rela)
|
||||
struct ElfRel<E> {
|
||||
ElfRel() = default;
|
||||
ElfRel(u64 offset, u32 type, u32 sym, i64 addend = 0)
|
||||
@@ -1671,16 +1757,6 @@ struct ElfRel<E> {
|
||||
std::conditional_t<E::is_64, U32<E>, u8> r_type;
|
||||
};
|
||||
|
||||
// Returns true if a given relocation is of type used for direct
|
||||
// function call.
|
||||
template <typename E>
|
||||
inline bool is_func_call_rel(const ElfRel<E> &r) {
|
||||
for (u32 r_type : E::R_FUNCALL)
|
||||
if (r.r_type == r_type)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
struct ElfDyn {
|
||||
Word<E> d_tag;
|
||||
@@ -1748,94 +1824,6 @@ struct ElfNhdr {
|
||||
// Target-specific ELF data types
|
||||
//
|
||||
|
||||
template <>
|
||||
struct ElfSym<ARM64> {
|
||||
bool is_undef() const { return st_shndx == SHN_UNDEF; }
|
||||
bool is_abs() const { return st_shndx == SHN_ABS; }
|
||||
bool is_common() const { return st_shndx == SHN_COMMON; }
|
||||
bool is_weak() const { return st_bind == STB_WEAK; }
|
||||
bool is_undef_weak() const { return is_undef() && is_weak(); }
|
||||
|
||||
ul32 st_name;
|
||||
|
||||
#ifdef __LITTLE_ENDIAN__
|
||||
u8 st_type : 4;
|
||||
u8 st_bind : 4;
|
||||
u8 st_visibility : 2;
|
||||
u8 : 5;
|
||||
u8 arm64_variant_pcs : 1; // ARM64-specific
|
||||
#else
|
||||
u8 st_bind : 4;
|
||||
u8 st_type : 4;
|
||||
u8 arm64_variant_pcs : 1;
|
||||
u8 : 5;
|
||||
u8 st_visibility : 2;
|
||||
#endif
|
||||
|
||||
ul16 st_shndx;
|
||||
ul64 st_value;
|
||||
ul64 st_size;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ElfSym<PPC64V2> {
|
||||
bool is_undef() const { return st_shndx == SHN_UNDEF; }
|
||||
bool is_abs() const { return st_shndx == SHN_ABS; }
|
||||
bool is_common() const { return st_shndx == SHN_COMMON; }
|
||||
bool is_weak() const { return st_bind == STB_WEAK; }
|
||||
bool is_undef_weak() const { return is_undef() && is_weak(); }
|
||||
|
||||
bool preserves_r2() const { return ppc_local_entry != 1; }
|
||||
bool uses_toc() const { return ppc_local_entry > 1; }
|
||||
|
||||
ul32 st_name;
|
||||
|
||||
#ifdef __LITTLE_ENDIAN__
|
||||
u8 st_type : 4;
|
||||
u8 st_bind : 4;
|
||||
u8 st_visibility : 2;
|
||||
u8 : 3;
|
||||
u8 ppc_local_entry : 3; // This is PPC64V2-specific
|
||||
#else
|
||||
u8 st_bind : 4;
|
||||
u8 st_type : 4;
|
||||
u8 ppc_local_entry : 3;
|
||||
u8 : 3;
|
||||
u8 st_visibility : 2;
|
||||
#endif
|
||||
|
||||
ul16 st_shndx;
|
||||
ul64 st_value;
|
||||
ul64 st_size;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ElfSym<ALPHA> {
|
||||
bool is_undef() const { return st_shndx == SHN_UNDEF; }
|
||||
bool is_abs() const { return st_shndx == SHN_ABS; }
|
||||
bool is_common() const { return st_shndx == SHN_COMMON; }
|
||||
bool is_weak() const { return st_bind == STB_WEAK; }
|
||||
bool is_undef_weak() const { return is_undef() && is_weak(); }
|
||||
|
||||
ul32 st_name;
|
||||
|
||||
#ifdef __LITTLE_ENDIAN__
|
||||
u8 st_type : 4;
|
||||
u8 st_bind : 4;
|
||||
u8 st_visibility : 2;
|
||||
u8 alpha_st_other : 6; // contains STO_ALPHA_NOPV, STO_ALPHA_STD_GPLOAD or 0
|
||||
#else
|
||||
u8 st_bind : 4;
|
||||
u8 st_type : 4;
|
||||
u8 alpha_st_other : 6;
|
||||
u8 st_visibility : 2;
|
||||
#endif
|
||||
|
||||
ul16 st_shndx;
|
||||
ul64 st_value;
|
||||
ul64 st_size;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ElfRel<SPARC64> {
|
||||
ElfRel() = default;
|
||||
@@ -1851,10 +1839,10 @@ struct ElfRel<SPARC64> {
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ElfRel<SH4> {
|
||||
struct ElfRel<SH4LE> {
|
||||
ElfRel() = default;
|
||||
|
||||
// Addend is ignored except for base relocations because even though
|
||||
// Addend is ignored except for base relocations because even though
|
||||
// SH4 is RELA, r_addend is ignored in most cases and works as if it
|
||||
// were REL.
|
||||
ElfRel(u64 offset, u32 type, u32 sym, i64 addend)
|
||||
@@ -1866,6 +1854,18 @@ struct ElfRel<SH4> {
|
||||
il32 r_addend;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ElfRel<SH4BE> {
|
||||
ElfRel() = default;
|
||||
ElfRel(u64 offset, u32 type, u32 sym, i64 addend)
|
||||
: r_offset(offset), r_sym(sym), r_type(type), r_addend(sym ? 0 : addend) {}
|
||||
|
||||
ub32 r_offset;
|
||||
ub24 r_sym;
|
||||
u8 r_type;
|
||||
ib32 r_addend;
|
||||
};
|
||||
|
||||
//
|
||||
// Machine descriptions
|
||||
//
|
||||
@@ -1876,7 +1876,8 @@ template <typename E> concept needs_thunk = requires { E::thunk_size; };
|
||||
|
||||
template <typename E> concept is_x86_64 = std::same_as<E, X86_64>;
|
||||
template <typename E> concept is_i386 = std::same_as<E, I386>;
|
||||
template <typename E> concept is_arm64 = std::same_as<E, ARM64>;
|
||||
template <typename E> concept is_arm64le = std::same_as<E, ARM64LE>;
|
||||
template <typename E> concept is_arm64be = std::same_as<E, ARM64BE>;
|
||||
template <typename E> concept is_arm32 = std::same_as<E, ARM32>;
|
||||
template <typename E> concept is_rv64le = std::same_as<E, RV64LE>;
|
||||
template <typename E> concept is_rv64be = std::same_as<E, RV64BE>;
|
||||
@@ -1888,12 +1889,14 @@ template <typename E> concept is_ppc64v2 = std::same_as<E, PPC64V2>;
|
||||
template <typename E> concept is_s390x = std::same_as<E, S390X>;
|
||||
template <typename E> concept is_sparc64 = std::same_as<E, SPARC64>;
|
||||
template <typename E> concept is_m68k = std::same_as<E, M68K>;
|
||||
template <typename E> concept is_sh4 = std::same_as<E, SH4>;
|
||||
template <typename E> concept is_alpha = std::same_as<E, ALPHA>;
|
||||
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>;
|
||||
template <typename E> concept is_arm = is_arm64<E> || is_arm32<E>;
|
||||
template <typename E> concept is_rv64 = is_rv64le<E> || is_rv64be<E>;
|
||||
template <typename E> concept is_rv32 = is_rv32le<E> || is_rv32be<E>;
|
||||
@@ -1901,10 +1904,11 @@ template <typename E> concept is_riscv = is_rv64<E> || is_rv32<E>;
|
||||
template <typename E> concept is_ppc64 = is_ppc64v1<E> || is_ppc64v2<E>;
|
||||
template <typename E> concept is_ppc = is_ppc64<E> || is_ppc32<E>;
|
||||
template <typename E> concept is_sparc = is_sparc64<E>;
|
||||
template <typename E> concept is_sh4 = is_sh4le<E> || is_sh4be<E>;
|
||||
template <typename E> concept is_loongarch = is_loongarch64<E> || is_loongarch32<E>;
|
||||
|
||||
struct X86_64 {
|
||||
static constexpr std::string_view target_name = "x86_64";
|
||||
static constexpr std::string_view name = "x86_64";
|
||||
static constexpr bool is_64 = true;
|
||||
static constexpr bool is_le = true;
|
||||
static constexpr bool is_rela = true;
|
||||
@@ -1929,7 +1933,7 @@ struct X86_64 {
|
||||
};
|
||||
|
||||
struct I386 {
|
||||
static constexpr std::string_view target_name = "i386";
|
||||
static constexpr std::string_view name = "i386";
|
||||
static constexpr bool is_64 = false;
|
||||
static constexpr bool is_le = true;
|
||||
static constexpr bool is_rela = false;
|
||||
@@ -1937,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;
|
||||
@@ -1953,8 +1957,8 @@ struct I386 {
|
||||
static constexpr u32 R_FUNCALL[] = { R_386_PLT32 };
|
||||
};
|
||||
|
||||
struct ARM64 {
|
||||
static constexpr std::string_view target_name = "arm64";
|
||||
struct ARM64LE {
|
||||
static constexpr std::string_view name = "arm64";
|
||||
static constexpr bool is_64 = true;
|
||||
static constexpr bool is_le = true;
|
||||
static constexpr bool is_rela = true;
|
||||
@@ -1980,12 +1984,17 @@ struct ARM64 {
|
||||
static constexpr u32 R_FUNCALL[] = { R_AARCH64_JUMP26, R_AARCH64_CALL26 };
|
||||
};
|
||||
|
||||
struct ARM64BE : ARM64LE {
|
||||
static constexpr std::string_view name = "arm64be";
|
||||
static constexpr bool is_le = false;
|
||||
};
|
||||
|
||||
struct ARM32 {
|
||||
static constexpr std::string_view target_name = "arm32";
|
||||
static constexpr std::string_view name = "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;
|
||||
@@ -2010,8 +2019,10 @@ struct ARM32 {
|
||||
};
|
||||
};
|
||||
|
||||
struct RV64 {
|
||||
struct RV64LE {
|
||||
static constexpr std::string_view name = "riscv64";
|
||||
static constexpr bool is_64 = true;
|
||||
static constexpr bool is_le = true;
|
||||
static constexpr bool is_rela = true;
|
||||
static constexpr u32 page_size = 4096;
|
||||
static constexpr u32 e_machine = EM_RISCV;
|
||||
@@ -2033,18 +2044,15 @@ struct RV64 {
|
||||
static constexpr u32 R_FUNCALL[] = { R_RISCV_CALL, R_RISCV_CALL_PLT };
|
||||
};
|
||||
|
||||
struct RV64LE : RV64 {
|
||||
static constexpr std::string_view target_name = "riscv64";
|
||||
static constexpr bool is_le = true;
|
||||
};
|
||||
|
||||
struct RV64BE : RV64 {
|
||||
static constexpr std::string_view target_name = "riscv64be";
|
||||
struct RV64BE : RV64LE {
|
||||
static constexpr std::string_view name = "riscv64be";
|
||||
static constexpr bool is_le = false;
|
||||
};
|
||||
|
||||
struct RV32 {
|
||||
struct RV32LE {
|
||||
static constexpr std::string_view name = "riscv32";
|
||||
static constexpr bool is_64 = false;
|
||||
static constexpr bool is_le = true;
|
||||
static constexpr bool is_rela = true;
|
||||
static constexpr u32 page_size = 4096;
|
||||
static constexpr u32 e_machine = EM_RISCV;
|
||||
@@ -2066,18 +2074,13 @@ struct RV32 {
|
||||
static constexpr u32 R_FUNCALL[] = { R_RISCV_CALL, R_RISCV_CALL_PLT };
|
||||
};
|
||||
|
||||
struct RV32LE : RV32 {
|
||||
static constexpr std::string_view target_name = "riscv32";
|
||||
static constexpr bool is_le = true;
|
||||
};
|
||||
|
||||
struct RV32BE : RV32 {
|
||||
static constexpr std::string_view target_name = "riscv32be";
|
||||
struct RV32BE : RV32LE {
|
||||
static constexpr std::string_view name = "riscv32be";
|
||||
static constexpr bool is_le = false;
|
||||
};
|
||||
|
||||
struct PPC32 {
|
||||
static constexpr std::string_view target_name = "ppc32";
|
||||
static constexpr std::string_view name = "ppc32";
|
||||
static constexpr bool is_64 = false;
|
||||
static constexpr bool is_le = false;
|
||||
static constexpr bool is_rela = true;
|
||||
@@ -2124,7 +2127,7 @@ struct PPC64 {
|
||||
};
|
||||
|
||||
struct PPC64V1 : PPC64 {
|
||||
static constexpr std::string_view target_name = "ppc64v1";
|
||||
static constexpr std::string_view name = "ppc64v1";
|
||||
static constexpr bool is_le = false;
|
||||
static constexpr u32 plt_hdr_size = 44;
|
||||
static constexpr u32 pltgot_size = 0;
|
||||
@@ -2134,7 +2137,7 @@ struct PPC64V1 : PPC64 {
|
||||
};
|
||||
|
||||
struct PPC64V2 : PPC64 {
|
||||
static constexpr std::string_view target_name = "ppc64v2";
|
||||
static constexpr std::string_view name = "ppc64v2";
|
||||
static constexpr bool is_le = true;
|
||||
static constexpr u32 plt_hdr_size = 52;
|
||||
static constexpr u32 plt_size = 4;
|
||||
@@ -2145,7 +2148,7 @@ struct PPC64V2 : PPC64 {
|
||||
};
|
||||
|
||||
struct S390X {
|
||||
static constexpr std::string_view target_name = "s390x";
|
||||
static constexpr std::string_view name = "s390x";
|
||||
static constexpr bool is_64 = true;
|
||||
static constexpr bool is_le = false;
|
||||
static constexpr bool is_rela = true;
|
||||
@@ -2169,7 +2172,7 @@ struct S390X {
|
||||
};
|
||||
|
||||
struct SPARC64 {
|
||||
static constexpr std::string_view target_name = "sparc64";
|
||||
static constexpr std::string_view name = "sparc64";
|
||||
static constexpr bool is_64 = true;
|
||||
static constexpr bool is_le = false;
|
||||
static constexpr bool is_rela = true;
|
||||
@@ -2193,7 +2196,7 @@ struct SPARC64 {
|
||||
};
|
||||
|
||||
struct M68K {
|
||||
static constexpr std::string_view target_name = "m68k";
|
||||
static constexpr std::string_view name = "m68k";
|
||||
static constexpr bool is_64 = false;
|
||||
static constexpr bool is_le = false;
|
||||
static constexpr bool is_rela = true;
|
||||
@@ -2215,8 +2218,8 @@ struct M68K {
|
||||
static constexpr u32 R_FUNCALL[] = { R_68K_PLT32 };
|
||||
};
|
||||
|
||||
struct SH4 {
|
||||
static constexpr std::string_view target_name = "sh4";
|
||||
struct SH4LE {
|
||||
static constexpr std::string_view name = "sh4";
|
||||
static constexpr bool is_64 = false;
|
||||
static constexpr bool is_le = true;
|
||||
static constexpr bool is_rela = true;
|
||||
@@ -2238,35 +2241,18 @@ struct SH4 {
|
||||
static constexpr u32 R_FUNCALL[] = { R_SH_PLT32 };
|
||||
};
|
||||
|
||||
struct ALPHA {
|
||||
static constexpr std::string_view target_name = "alpha";
|
||||
struct SH4BE : SH4LE {
|
||||
static constexpr std::string_view name = "sh4be";
|
||||
static constexpr bool is_le = false;
|
||||
static constexpr u8 filler[] = { 0x90, 0x00 }; // nop
|
||||
};
|
||||
|
||||
struct LOONGARCH64 {
|
||||
static constexpr std::string_view name = "loongarch64";
|
||||
static constexpr bool is_64 = true;
|
||||
static constexpr bool is_le = true;
|
||||
static constexpr bool is_rela = true;
|
||||
static constexpr u32 page_size = 65536;
|
||||
static constexpr u32 e_machine = EM_ALPHA;
|
||||
static constexpr u32 plt_hdr_size = 0;
|
||||
static constexpr u32 plt_size = 0;
|
||||
static constexpr u32 pltgot_size = 0;
|
||||
static constexpr u8 filler[] = { 0x81, 0x00, 0x00, 0x00 }; // bugchk
|
||||
|
||||
static constexpr u32 R_COPY = R_ALPHA_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_ALPHA_GLOB_DAT;
|
||||
static constexpr u32 R_JUMP_SLOT = R_ALPHA_JMP_SLOT;
|
||||
static constexpr u32 R_ABS = R_ALPHA_REFQUAD;
|
||||
static constexpr u32 R_RELATIVE = R_ALPHA_RELATIVE;
|
||||
static constexpr u32 R_DTPOFF = R_ALPHA_DTPREL64;
|
||||
static constexpr u32 R_TPOFF = R_ALPHA_TPREL64;
|
||||
static constexpr u32 R_DTPMOD = R_ALPHA_DTPMOD64;
|
||||
static constexpr u32 R_FUNCALL[] = {};
|
||||
};
|
||||
|
||||
struct LOONGARCH64 {
|
||||
static constexpr std::string_view target_name = "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 e_machine = EM_LOONGARCH;
|
||||
static constexpr u32 plt_hdr_size = 32;
|
||||
static constexpr u32 plt_size = 16;
|
||||
@@ -2282,15 +2268,16 @@ struct LOONGARCH64 {
|
||||
static constexpr u32 R_DTPOFF = R_LARCH_TLS_DTPREL64;
|
||||
static constexpr u32 R_TPOFF = R_LARCH_TLS_TPREL64;
|
||||
static constexpr u32 R_DTPMOD = R_LARCH_TLS_DTPMOD64;
|
||||
static constexpr u32 R_TLSDESC = R_LARCH_TLS_DESC64;
|
||||
static constexpr u32 R_FUNCALL[] = { R_LARCH_B26, R_LARCH_CALL36 };
|
||||
};
|
||||
|
||||
struct LOONGARCH32 {
|
||||
static constexpr std::string_view target_name = "loongarch32";
|
||||
static constexpr std::string_view name = "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;
|
||||
@@ -2306,7 +2293,31 @@ struct LOONGARCH32 {
|
||||
static constexpr u32 R_DTPOFF = R_LARCH_TLS_DTPREL32;
|
||||
static constexpr u32 R_TPOFF = R_LARCH_TLS_TPREL32;
|
||||
static constexpr u32 R_DTPMOD = R_LARCH_TLS_DTPMOD32;
|
||||
static constexpr u32 R_TLSDESC = R_LARCH_TLS_DESC32;
|
||||
static constexpr u32 R_FUNCALL[] = { R_LARCH_B26, R_LARCH_CALL36 };
|
||||
};
|
||||
|
||||
} // namespace mold::elf
|
||||
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
|
||||
@@ -1,30 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "../elf/elf.h"
|
||||
#include "mold.h"
|
||||
#include "../lib/archive-file.h"
|
||||
|
||||
namespace mold {
|
||||
|
||||
enum class FileType {
|
||||
UNKNOWN,
|
||||
EMPTY,
|
||||
ELF_OBJ,
|
||||
ELF_DSO,
|
||||
MACH_OBJ,
|
||||
MACH_EXE,
|
||||
MACH_DYLIB,
|
||||
MACH_BUNDLE,
|
||||
MACH_UNIVERSAL,
|
||||
AR,
|
||||
THIN_AR,
|
||||
TAPI,
|
||||
TEXT,
|
||||
GCC_LTO_OBJ,
|
||||
LLVM_BITCODE,
|
||||
};
|
||||
|
||||
template <typename MappedFile>
|
||||
bool is_text_file(MappedFile *mf) {
|
||||
static bool is_text_file(MappedFile *mf) {
|
||||
auto istext = [](char c) {
|
||||
return isprint(c) || c == '\n' || c == '\t';
|
||||
};
|
||||
@@ -34,10 +13,8 @@ bool is_text_file(MappedFile *mf) {
|
||||
istext(data[2]) && istext(data[3]);
|
||||
}
|
||||
|
||||
template <typename E, typename Context, typename MappedFile>
|
||||
inline bool is_gcc_lto_obj(Context &ctx, MappedFile *mf) {
|
||||
using namespace mold::elf;
|
||||
|
||||
template <typename E>
|
||||
static bool is_gcc_lto_obj(MappedFile *mf, bool has_plugin) {
|
||||
const char *data = mf->get_contents().data();
|
||||
ElfEhdr<E> &ehdr = *(ElfEhdr<E> *)data;
|
||||
ElfShdr<E> *sh_begin = (ElfShdr<E> *)(data + ehdr.e_shoff);
|
||||
@@ -54,7 +31,7 @@ inline bool is_gcc_lto_obj(Context &ctx, MappedFile *mf) {
|
||||
// the LTO linker plugin is available and falls back as regular
|
||||
// objects otherwise. GCC FAT LTO object can be identified by the
|
||||
// presence of `.gcc.lto_.symtab` section.
|
||||
if (!ctx.arg.plugin.empty()) {
|
||||
if (has_plugin) {
|
||||
std::string_view name = data + shdrs[shstrtab_idx].sh_offset + sec.sh_name;
|
||||
if (name.starts_with(".gnu.lto_.symtab."))
|
||||
return true;
|
||||
@@ -89,11 +66,10 @@ inline bool is_gcc_lto_obj(Context &ctx, MappedFile *mf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Context, typename MappedFile>
|
||||
FileType get_file_type(Context &ctx, MappedFile *mf) {
|
||||
using namespace elf;
|
||||
|
||||
template <typename E>
|
||||
FileType get_file_type(Context<E> &ctx, MappedFile *mf) {
|
||||
std::string_view data = mf->get_contents();
|
||||
bool has_plugin = !ctx.arg.plugin.empty();
|
||||
|
||||
if (data.empty())
|
||||
return FileType::EMPTY;
|
||||
@@ -106,10 +82,10 @@ FileType get_file_type(Context &ctx, MappedFile *mf) {
|
||||
|
||||
if (ehdr.e_type == ET_REL) {
|
||||
if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) {
|
||||
if (is_gcc_lto_obj<I386>(ctx, mf))
|
||||
if (is_gcc_lto_obj<I386>(mf, has_plugin))
|
||||
return FileType::GCC_LTO_OBJ;
|
||||
} else {
|
||||
if (is_gcc_lto_obj<X86_64>(ctx, mf))
|
||||
if (is_gcc_lto_obj<X86_64>(mf, has_plugin))
|
||||
return FileType::GCC_LTO_OBJ;
|
||||
}
|
||||
return FileType::ELF_OBJ;
|
||||
@@ -122,10 +98,10 @@ FileType get_file_type(Context &ctx, MappedFile *mf) {
|
||||
|
||||
if (ehdr.e_type == ET_REL) {
|
||||
if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) {
|
||||
if (is_gcc_lto_obj<M68K>(ctx, mf))
|
||||
if (is_gcc_lto_obj<M68K>(mf, has_plugin))
|
||||
return FileType::GCC_LTO_OBJ;
|
||||
} else {
|
||||
if (is_gcc_lto_obj<SPARC64>(ctx, mf))
|
||||
if (is_gcc_lto_obj<SPARC64>(mf, has_plugin))
|
||||
return FileType::GCC_LTO_OBJ;
|
||||
}
|
||||
return FileType::ELF_OBJ;
|
||||
@@ -137,28 +113,10 @@ FileType get_file_type(Context &ctx, MappedFile *mf) {
|
||||
return FileType::UNKNOWN;
|
||||
}
|
||||
|
||||
if (data.starts_with("\xcf\xfa\xed\xfe")) {
|
||||
switch (*(ul32 *)(data.data() + 12)) {
|
||||
case 1: // MH_OBJECT
|
||||
return FileType::MACH_OBJ;
|
||||
case 2: // MH_EXECUTE
|
||||
return FileType::MACH_EXE;
|
||||
case 6: // MH_DYLIB
|
||||
return FileType::MACH_DYLIB;
|
||||
case 8: // MH_BUNDLE
|
||||
return FileType::MACH_BUNDLE;
|
||||
}
|
||||
return FileType::UNKNOWN;
|
||||
}
|
||||
|
||||
if (data.starts_with("!<arch>\n"))
|
||||
return FileType::AR;
|
||||
if (data.starts_with("!<thin>\n"))
|
||||
return FileType::THIN_AR;
|
||||
if (data.starts_with("--- !tapi-tbd"))
|
||||
return FileType::TAPI;
|
||||
if (data.starts_with("\xca\xfe\xba\xbe"))
|
||||
return FileType::MACH_UNIVERSAL;
|
||||
if (is_text_file(mf))
|
||||
return FileType::TEXT;
|
||||
if (data.starts_with("\xde\xc0\x17\x0b"))
|
||||
@@ -168,30 +126,81 @@ FileType get_file_type(Context &ctx, MappedFile *mf) {
|
||||
return FileType::UNKNOWN;
|
||||
}
|
||||
|
||||
inline std::string filetype_to_string(FileType type) {
|
||||
switch (type) {
|
||||
case FileType::UNKNOWN: return "UNKNOWN";
|
||||
case FileType::EMPTY: return "EMPTY";
|
||||
case FileType::ELF_OBJ: return "ELF_OBJ";
|
||||
case FileType::ELF_DSO: return "ELF_DSO";
|
||||
case FileType::MACH_EXE: return "MACH_EXE";
|
||||
case FileType::MACH_OBJ: return "MACH_OBJ";
|
||||
case FileType::MACH_DYLIB: return "MACH_DYLIB";
|
||||
case FileType::MACH_BUNDLE: return "MACH_BUNDLE";
|
||||
case FileType::MACH_UNIVERSAL: return "MACH_UNIVERSAL";
|
||||
case FileType::AR: return "AR";
|
||||
case FileType::THIN_AR: return "THIN_AR";
|
||||
case FileType::TAPI: return "TAPI";
|
||||
case FileType::TEXT: return "TEXT";
|
||||
case FileType::GCC_LTO_OBJ: return "GCC_LTO_OBJ";
|
||||
case FileType::LLVM_BITCODE: return "LLVM_BITCODE";
|
||||
static std::string_view get_elf_type(u8 *buf) {
|
||||
bool is_le = (buf[EI_DATA] == ELFDATA2LSB);
|
||||
bool is_64 = (buf[EI_CLASS] == ELFCLASS64);
|
||||
|
||||
auto *ehdr_le = (ElfEhdr<I386> *)buf;
|
||||
auto *ehdr_be = (ElfEhdr<M68K> *)buf;
|
||||
|
||||
switch (is_le ? ehdr_le->e_machine : ehdr_be->e_machine) {
|
||||
case EM_386:
|
||||
return I386::name;
|
||||
case EM_X86_64:
|
||||
return X86_64::name;
|
||||
case EM_ARM:
|
||||
return ARM32::name;
|
||||
case EM_AARCH64:
|
||||
return is_le ? ARM64LE::name : ARM64BE::name;
|
||||
case EM_RISCV:
|
||||
if (is_le)
|
||||
return is_64 ? RV64LE::name : RV32LE::name;
|
||||
return is_64 ? RV64BE::name : RV32BE::name;
|
||||
case EM_PPC:
|
||||
return PPC32::name;
|
||||
case EM_PPC64:
|
||||
return is_le ? PPC64V2::name : PPC64V1::name;
|
||||
case EM_S390X:
|
||||
return S390X::name;
|
||||
case EM_SPARC64:
|
||||
return SPARC64::name;
|
||||
case EM_68K:
|
||||
return M68K::name;
|
||||
case EM_SH:
|
||||
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 "";
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &out, FileType type) {
|
||||
out << filetype_to_string(type);
|
||||
return out;
|
||||
// Read the beginning of a given file and returns its machine type
|
||||
// (e.g. EM_X86_64 or EM_386).
|
||||
template <typename E>
|
||||
std::string_view
|
||||
get_machine_type(Context<E> &ctx, ReaderContext &rctx, MappedFile *mf) {
|
||||
switch (get_file_type(ctx, mf)) {
|
||||
case FileType::ELF_OBJ:
|
||||
case FileType::ELF_DSO:
|
||||
case FileType::GCC_LTO_OBJ:
|
||||
return get_elf_type(mf->data);
|
||||
case FileType::AR:
|
||||
for (MappedFile *child : read_fat_archive_members(ctx, mf))
|
||||
if (FileType ty = get_file_type(ctx, child);
|
||||
ty == FileType::ELF_OBJ || ty == FileType::GCC_LTO_OBJ)
|
||||
return get_elf_type(child->data);
|
||||
return "";
|
||||
case FileType::THIN_AR:
|
||||
for (MappedFile *child : read_thin_archive_members(ctx, mf))
|
||||
if (FileType ty = get_file_type(ctx, child);
|
||||
ty == FileType::ELF_OBJ || ty == FileType::GCC_LTO_OBJ)
|
||||
return get_elf_type(child->data);
|
||||
return "";
|
||||
case FileType::TEXT:
|
||||
return Script(ctx, rctx, mf).get_script_output_type();
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
using E = MOLD_TARGET;
|
||||
|
||||
template FileType get_file_type(Context<E> &, MappedFile *);
|
||||
|
||||
template std::string_view
|
||||
get_machine_type(Context<E> &, ReaderContext &, MappedFile *);
|
||||
|
||||
} // namespace mold
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <tbb/concurrent_vector.h>
|
||||
#include <tbb/parallel_for_each.h>
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
template <typename E>
|
||||
static bool should_keep(const InputSection<E> &isec) {
|
||||
@@ -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
|
||||
@@ -172,4 +173,4 @@ using E = MOLD_TARGET;
|
||||
|
||||
template void gc_sections(Context<E> &ctx);
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
@@ -58,9 +58,8 @@
|
||||
|
||||
#include "mold.h"
|
||||
#include <tbb/parallel_for_each.h>
|
||||
#include <tbb/parallel_sort.h>
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
enum DwarfKind { DWARF2_32, DWARF5_32, DWARF2_64, DWARF5_64 };
|
||||
|
||||
@@ -127,16 +126,10 @@ struct SectionHeader {
|
||||
};
|
||||
|
||||
struct NameType {
|
||||
bool operator==(const NameType &) const = default;
|
||||
|
||||
bool operator<(const NameType &other) const {
|
||||
return std::tuple(hash, type, name) <
|
||||
std::tuple(other.hash, other.type, other.name);
|
||||
}
|
||||
|
||||
std::string_view name;
|
||||
auto operator<=>(const NameType &) const = default;
|
||||
u64 hash;
|
||||
u8 type;
|
||||
std::string_view name;
|
||||
};
|
||||
|
||||
struct MapValue {
|
||||
@@ -539,7 +532,7 @@ static i64 read_pubnames_cu(Context<E> &ctx, const PubnamesHdr &hdr,
|
||||
u8 type = *p++;
|
||||
std::string_view name = (char *)p;
|
||||
p += name.size() + 1;
|
||||
cu->nametypes.push_back({name, hash_string(name), type});
|
||||
cu->nametypes.push_back(NameType{hash_string(name), type, name});
|
||||
}
|
||||
|
||||
return size;
|
||||
@@ -626,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);
|
||||
});
|
||||
|
||||
@@ -791,4 +784,4 @@ using E = MOLD_TARGET;
|
||||
|
||||
template void write_gdb_index(Context<E> &);
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
+20
-12
@@ -65,7 +65,7 @@
|
||||
// conditions.
|
||||
|
||||
#include "mold.h"
|
||||
#include "../common/siphash.h"
|
||||
#include "../lib/siphash.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
@@ -91,7 +91,7 @@ template <> struct hash<Digest> {
|
||||
};
|
||||
}
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
static u8 hmac_key[16];
|
||||
|
||||
@@ -133,7 +133,7 @@ static bool is_eligible(Context<E> &ctx, InputSection<E> &isec) {
|
||||
name != ".init" && name != ".fini";
|
||||
|
||||
bool is_readonly = !(shdr.sh_flags & SHF_WRITE);
|
||||
bool is_relro = isec.output_section && isec.output_section->is_relro;
|
||||
bool is_relro = isec.name().starts_with(".data.rel.ro");
|
||||
return (ctx.arg.ignore_data_address_equality || !isec.address_taken) &&
|
||||
(is_readonly || is_relro);
|
||||
}
|
||||
@@ -192,7 +192,7 @@ static void merge_leaf_nodes(Context<E> &ctx) {
|
||||
static Counter non_eligible("icf_non_eligibles");
|
||||
static Counter leaf("icf_leaf_nodes");
|
||||
|
||||
tbb::concurrent_unordered_map<InputSection<E> *, InputSection<E> *,
|
||||
tbb::concurrent_unordered_map<InputSection<E> *, Atomic<InputSection<E> *>,
|
||||
LeafHasher<E>, LeafEq<E>> map;
|
||||
|
||||
tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
|
||||
@@ -209,8 +209,11 @@ static void merge_leaf_nodes(Context<E> &ctx) {
|
||||
leaf++;
|
||||
isec->icf_leaf = true;
|
||||
auto [it, inserted] = map.insert({isec.get(), isec.get()});
|
||||
if (!inserted && isec->get_priority() < it->second->get_priority())
|
||||
it->second = isec.get();
|
||||
if (!inserted) {
|
||||
InputSection<E> *isec2 = it->second.load();
|
||||
while (isec->get_priority() < isec2->get_priority() &&
|
||||
!it->second.compare_exchange_strong(isec2, isec.get()));
|
||||
}
|
||||
} else {
|
||||
eligible++;
|
||||
isec->icf_eligible = true;
|
||||
@@ -465,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();
|
||||
});
|
||||
|
||||
@@ -568,14 +571,19 @@ void icf_sections(Context<E> &ctx) {
|
||||
{
|
||||
Timer t(ctx, "group");
|
||||
|
||||
auto *map = new tbb::concurrent_unordered_map<Digest, InputSection<E> *>;
|
||||
auto *map =
|
||||
new tbb::concurrent_unordered_map<Digest, Atomic<InputSection<E> *>>;
|
||||
|
||||
std::span<Digest> digest = digests[slot];
|
||||
|
||||
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
|
||||
InputSection<E> *isec = sections[i];
|
||||
auto [it, inserted] = map->insert({digest[i], isec});
|
||||
if (!inserted && isec->get_priority() < it->second->get_priority())
|
||||
it->second = isec;
|
||||
if (!inserted) {
|
||||
InputSection<E> *isec2 = it->second.load();
|
||||
while (isec->get_priority() < isec2->get_priority() &&
|
||||
!it->second.compare_exchange_strong(isec2, isec));
|
||||
}
|
||||
});
|
||||
|
||||
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
|
||||
@@ -599,7 +607,7 @@ void icf_sections(Context<E> &ctx) {
|
||||
static Counter eliminated("icf_eliminated");
|
||||
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
|
||||
for (std::unique_ptr<InputSection<E>> &isec : file->sections) {
|
||||
if (isec && isec->is_alive && isec->is_killed_by_icf()) {
|
||||
if (isec && isec->is_alive && isec->icf_removed()) {
|
||||
isec->kill();
|
||||
eliminated++;
|
||||
}
|
||||
@@ -612,4 +620,4 @@ using E = MOLD_TARGET;
|
||||
|
||||
template void icf_sections(Context<E> &ctx);
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
@@ -8,7 +8,7 @@
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
// If we haven't seen the same `key` before, create a new instance
|
||||
// of Symbol and returns it. Otherwise, returns the previously-
|
||||
@@ -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,22 +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>
|
||||
ObjectFile<E> *
|
||||
ObjectFile<E>::create(Context<E> &ctx, MappedFile *mf,
|
||||
std::string archive_name, bool is_in_lib) {
|
||||
ObjectFile<E> *obj = new ObjectFile<E>(ctx, mf, archive_name, is_in_lib);
|
||||
ctx.obj_pool.emplace_back(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
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");
|
||||
@@ -177,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,7 +221,7 @@ static bool is_known_section_type(const ElfShdr<E> &shdr) {
|
||||
return true;
|
||||
if (SHT_LOOS <= ty && ty <= SHT_HIOS && !(flags & SHF_OS_NONCONFORMING))
|
||||
return true;
|
||||
if (is_x86<E> && ty == SHT_X86_64_UNWIND)
|
||||
if (is_x86_64<E> && ty == SHT_X86_64_UNWIND)
|
||||
return true;
|
||||
if (is_arm32<E> && (ty == SHT_ARM_EXIDX || ty == SHT_ARM_ATTRIBUTES))
|
||||
return true;
|
||||
@@ -252,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
|
||||
@@ -280,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.
|
||||
@@ -310,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:
|
||||
@@ -369,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;
|
||||
@@ -448,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.
|
||||
@@ -548,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) {
|
||||
@@ -556,19 +609,23 @@ 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.
|
||||
for (i64 i = 0; i < fdes.size();) {
|
||||
InputSection<E> *isec = get_isec(fdes[i]);
|
||||
assert(isec->fde_begin == -1);
|
||||
isec->fde_begin = i++;
|
||||
|
||||
while (i < fdes.size() && isec == get_isec(fdes[i]))
|
||||
i++;
|
||||
isec->fde_end = i;
|
||||
if (isec->is_alive) {
|
||||
isec->fde_begin = i++;
|
||||
while (i < fdes.size() && isec == get_isec(fdes[i]))
|
||||
i++;
|
||||
isec->fde_end = i;
|
||||
} else {
|
||||
fdes[i++].is_alive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -633,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -661,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -849,7 +902,6 @@ void ObjectFile<E>::parse(Context<E> &ctx) {
|
||||
initialize_sections(ctx);
|
||||
initialize_symbols(ctx);
|
||||
sort_relocations(ctx);
|
||||
parse_ehframe(ctx);
|
||||
}
|
||||
|
||||
// Symbols with higher priorities overwrites symbols with lower priorities.
|
||||
@@ -864,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 = [&] {
|
||||
@@ -887,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,
|
||||
@@ -947,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;
|
||||
@@ -962,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];
|
||||
@@ -980,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
|
||||
@@ -1086,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;
|
||||
|
||||
@@ -1100,12 +1156,9 @@ static bool should_write_to_local_symtab(Context<E> &ctx, Symbol<E> &sym) {
|
||||
|
||||
template <typename E>
|
||||
void ObjectFile<E>::compute_symtab_size(Context<E> &ctx) {
|
||||
if (ctx.arg.strip_all)
|
||||
return;
|
||||
|
||||
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())
|
||||
@@ -1160,21 +1213,17 @@ void ObjectFile<E>::populate_symtab(Context<E> &ctx) {
|
||||
strtab_off += write_string(strtab_base + strtab_off, sym.name());
|
||||
};
|
||||
|
||||
i64 local_symtab_idx = this->local_symtab_idx;
|
||||
i64 global_symtab_idx = this->global_symtab_idx;
|
||||
i64 local_idx = this->local_symtab_idx;
|
||||
i64 global_idx = this->global_symtab_idx;
|
||||
|
||||
for (i64 i = 1; i < this->first_global; i++)
|
||||
if (Symbol<E> &sym = *this->symbols[i]; sym.write_to_symtab)
|
||||
write_sym(sym, local_symtab_idx++);
|
||||
write_sym(sym, local_idx++);
|
||||
|
||||
for (i64 i = this->first_global; i < this->elf_syms.size(); i++) {
|
||||
Symbol<E> &sym = *this->symbols[i];
|
||||
if (sym.file == this && sym.write_to_symtab) {
|
||||
if (sym.is_local(ctx))
|
||||
write_sym(sym, local_symtab_idx++);
|
||||
else
|
||||
write_sym(sym, global_symtab_idx++);
|
||||
}
|
||||
if (sym.file == this && sym.write_to_symtab)
|
||||
write_sym(sym, sym.is_local(ctx) ? local_idx++ : global_idx++);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1193,24 +1242,17 @@ std::ostream &operator<<(std::ostream &out, const InputFile<E> &file) {
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
SharedFile<E> *SharedFile<E>::create(Context<E> &ctx, MappedFile *mf) {
|
||||
SharedFile<E> *obj = new SharedFile(ctx, mf);
|
||||
ctx.dso_pool.emplace_back(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
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;
|
||||
|
||||
return filepath(this->filename).filename().string();
|
||||
return path_filename(this->filename);
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -1264,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<Word<E>> dynamic;
|
||||
for (ElfPhdr<E> &phdr : this->get_phdrs())
|
||||
if (phdr.p_type == PT_DYNAMIC)
|
||||
dynamic = {(Word<E> *)(this->mf->data + phdr.p_offset),
|
||||
phdr.p_memsz / sizeof(Word<E>)};
|
||||
|
||||
// Find a string table
|
||||
char *strtab = nullptr;
|
||||
for (i64 i = 0; i < dynamic.size(); i += 2)
|
||||
if (dynamic[i] == DT_STRTAB)
|
||||
strtab = (char *)this->mf->data + dynamic[i + 1];
|
||||
|
||||
if (!strtab)
|
||||
return {};
|
||||
|
||||
// Find all DT_NEEDED entries
|
||||
std::vector<std::string_view> vec;
|
||||
for (i64 i = 0; i < dynamic.size(); i += 2)
|
||||
if (dynamic[i] == DT_NEEDED)
|
||||
vec.push_back(strtab + dynamic[i + 1]);
|
||||
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
|
||||
@@ -1352,7 +1387,8 @@ void SharedFile<E>::resolve_symbols(Context<E> &ctx) {
|
||||
for (i64 i = 0; i < this->symbols.size(); i++) {
|
||||
Symbol<E> &sym = *this->symbols[i];
|
||||
const ElfSym<E> &esym = this->elf_syms[i];
|
||||
if (esym.is_undef())
|
||||
|
||||
if (esym.is_undef() || sym.skip_dso)
|
||||
continue;
|
||||
|
||||
std::scoped_lock lock(sym.mu);
|
||||
@@ -1379,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)
|
||||
@@ -1407,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)};
|
||||
}
|
||||
|
||||
@@ -1433,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)
|
||||
@@ -1445,9 +1486,6 @@ bool SharedFile<E>::is_readonly(Symbol<E> *sym) {
|
||||
|
||||
template <typename E>
|
||||
void SharedFile<E>::compute_symtab_size(Context<E> &ctx) {
|
||||
if (ctx.arg.strip_all)
|
||||
return;
|
||||
|
||||
this->output_sym_indices.resize(this->elf_syms.size(), -1);
|
||||
|
||||
// Compute the size of global symbols.
|
||||
@@ -1494,7 +1532,8 @@ 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> &);
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
@@ -1,15 +1,9 @@
|
||||
#include "mold.h"
|
||||
|
||||
#include <limits>
|
||||
#include <zlib.h>
|
||||
#include <zstd.h>
|
||||
|
||||
namespace mold::elf {
|
||||
|
||||
typedef enum {
|
||||
NONE, ERROR, COPYREL, DYN_COPYREL, PLT, CPLT, DYN_CPLT, DYNREL,
|
||||
BASEREL, IFUNC_DYNREL,
|
||||
} Action;
|
||||
namespace mold {
|
||||
|
||||
static i64 to_p2align(u64 alignment) {
|
||||
if (alignment == 0)
|
||||
@@ -106,125 +100,36 @@ void InputSection<E>::copy_contents(Context<E> &ctx, u8 *buf) {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
static bool
|
||||
is_relr_reloc(Context<E> &ctx, InputSection<E> &isec, const ElfRel<E> &rel) {
|
||||
ElfShdr<E> shdr = isec.shdr();
|
||||
return ctx.arg.pack_dyn_relocs_relr &&
|
||||
!(shdr.sh_flags & SHF_EXECINSTR) &&
|
||||
shdr.sh_addralign % sizeof(Word<E>) == 0 &&
|
||||
rel.r_offset % sizeof(Word<E>) == 0;
|
||||
}
|
||||
typedef enum : u8 { NONE, ERROR, COPYREL, PLT, CPLT } Action;
|
||||
|
||||
template <typename E>
|
||||
static void scan_rel(Context<E> &ctx, InputSection<E> &isec, Symbol<E> &sym,
|
||||
const ElfRel<E> &rel, Action action) {
|
||||
bool writable = (isec.shdr().sh_flags & SHF_WRITE);
|
||||
|
||||
auto error = [&] {
|
||||
std::string msg = sym.is_absolute() ? "-fno-PIC" : "-fPIC";
|
||||
Error(ctx) << isec << ": " << rel << " relocation at offset 0x"
|
||||
<< std::hex << rel.r_offset << " against symbol `"
|
||||
<< sym << "' can not be used; recompile with " << msg;
|
||||
};
|
||||
|
||||
auto check_textrel = [&] {
|
||||
if (!writable) {
|
||||
if (ctx.arg.z_text) {
|
||||
error();
|
||||
} else if (ctx.arg.warn_textrel) {
|
||||
Warn(ctx) << isec << ": relocation against symbol `" << sym
|
||||
<< "' in read-only section";
|
||||
}
|
||||
ctx.has_textrel = true;
|
||||
}
|
||||
};
|
||||
|
||||
auto copyrel = [&] {
|
||||
assert(sym.is_imported);
|
||||
if (sym.esym().st_visibility == STV_PROTECTED) {
|
||||
Error(ctx) << isec
|
||||
<< ": cannot make copy relocation for protected symbol '" << sym
|
||||
<< "', defined in " << *sym.file << "; recompile with -fPIC";
|
||||
}
|
||||
sym.flags |= NEEDS_COPYREL;
|
||||
};
|
||||
|
||||
auto dynrel = [&] {
|
||||
check_textrel();
|
||||
isec.file.num_dynrel++;
|
||||
};
|
||||
|
||||
static void do_action(Context<E> &ctx, Action action, InputSection<E> &isec,
|
||||
Symbol<E> &sym, const ElfRel<E> &rel) {
|
||||
switch (action) {
|
||||
case NONE:
|
||||
break;
|
||||
case ERROR:
|
||||
// Print out the "recompile with -fPIC" error message.
|
||||
error();
|
||||
Error(ctx) << isec << ": " << rel << " relocation at offset 0x"
|
||||
<< std::hex << rel.r_offset << " against symbol `"
|
||||
<< sym << "' can not be used; recompile with -fPIC";
|
||||
break;
|
||||
case COPYREL:
|
||||
// Create a copy relocation.
|
||||
if (!ctx.arg.z_copyreloc)
|
||||
error();
|
||||
copyrel();
|
||||
break;
|
||||
case DYN_COPYREL:
|
||||
// Same as COPYREL but try to avoid creating a copy relocation by
|
||||
// creating a dynamic relocation instead if the relocation is in
|
||||
// a writable section.
|
||||
//
|
||||
// GHC (Glasgow Haskell Compiler) places a small amount of data in
|
||||
// .text before each function and access that data with a fixed
|
||||
// offset. The function breaks if we copy-relocate the data. For such
|
||||
// programs, we should avoid copy relocations if possible.
|
||||
//
|
||||
// Besides GHC, copy relocation is a hacky solution, so if we can
|
||||
// represent a relocation either with copyrel or dynrel, we prefer
|
||||
// dynamic relocation.
|
||||
if (writable || !ctx.arg.z_copyreloc)
|
||||
dynrel();
|
||||
else
|
||||
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:
|
||||
// Create a PLT entry.
|
||||
// Create a PLT entry
|
||||
sym.flags |= NEEDS_PLT;
|
||||
break;
|
||||
case CPLT:
|
||||
// Create a canonical PLT entry.
|
||||
// Create a canonical PLT entry
|
||||
sym.flags |= NEEDS_CPLT;
|
||||
break;
|
||||
case DYN_CPLT:
|
||||
// Same as CPLT but try to avoid creating a canonical PLT creating by
|
||||
// creating a dynamic relocation instead if the relocation is in a
|
||||
// writable section. The motivation behind it is hte same as DYN_COPYREL.
|
||||
if (writable)
|
||||
dynrel();
|
||||
else
|
||||
sym.flags |= NEEDS_CPLT;
|
||||
break;
|
||||
case DYNREL:
|
||||
// Create a dynamic relocation.
|
||||
dynrel();
|
||||
break;
|
||||
case BASEREL:
|
||||
// Create a base relocation.
|
||||
check_textrel();
|
||||
if (!is_relr_reloc(ctx, isec, rel))
|
||||
isec.file.num_dynrel++;
|
||||
break;
|
||||
case IFUNC_DYNREL:
|
||||
// Create an IRELATIVE relocation for a GNU ifunc symbol.
|
||||
//
|
||||
// We usually create an IRELATIVE relocation in .got for each ifunc.
|
||||
// However, if a statically-initialized pointer is initialized to an
|
||||
// ifunc's address, we have no choice other than emitting an IRELATIVE
|
||||
// relocation for each such pointer.
|
||||
dynrel();
|
||||
ctx.num_ifunc_dynrels++;
|
||||
break;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,96 +154,39 @@ static inline i64 get_sym_type(Symbol<E> &sym) {
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
static Action get_pcrel_action(Context<E> &ctx, Symbol<E> &sym) {
|
||||
void InputSection<E>::scan_pcrel(Context<E> &ctx, Symbol<E> &sym,
|
||||
const ElfRel<E> &rel) {
|
||||
// This is for PC-relative relocations (e.g. R_X86_64_PC32).
|
||||
// We cannot promote them to dynamic relocations because the dynamic
|
||||
// linker generally does not support PC-relative relocations.
|
||||
static Action table[3][4] = {
|
||||
static Action table[][4] = {
|
||||
// Absolute Local Imported data Imported code
|
||||
{ ERROR, NONE, ERROR, PLT }, // Shared object
|
||||
{ ERROR, NONE, COPYREL, CPLT }, // Position-independent exec
|
||||
{ NONE, NONE, COPYREL, CPLT }, // Position-dependent exec
|
||||
};
|
||||
|
||||
return table[get_output_type(ctx)][get_sym_type(sym)];
|
||||
Action action = table[get_output_type(ctx)][get_sym_type(sym)];
|
||||
do_action(ctx, action, *this, sym, rel);
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
static Action get_absrel_action(Context<E> &ctx, Symbol<E> &sym) {
|
||||
void InputSection<E>::scan_absrel(Context<E> &ctx, Symbol<E> &sym,
|
||||
const ElfRel<E> &rel) {
|
||||
// This is a decision table for absolute relocations that is smaller
|
||||
// than the pointer size (e.g. R_X86_64_32). Since the dynamic linker
|
||||
// generally does not support dynamic relocations smaller than the
|
||||
// pointer size, we need to report an error if a relocation cannot be
|
||||
// resolved at link-time.
|
||||
static Action table[3][4] = {
|
||||
static Action table[][4] = {
|
||||
// Absolute Local Imported data Imported code
|
||||
{ NONE, ERROR, ERROR, ERROR }, // Shared object
|
||||
{ NONE, ERROR, ERROR, ERROR }, // Position-independent exec
|
||||
{ NONE, NONE, COPYREL, CPLT }, // Position-dependent exec
|
||||
};
|
||||
|
||||
return table[get_output_type(ctx)][get_sym_type(sym)];
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
static Action get_dyn_absrel_action(Context<E> &ctx, Symbol<E> &sym) {
|
||||
if (sym.is_ifunc())
|
||||
return sym.is_pde_ifunc(ctx) ? NONE : IFUNC_DYNREL;
|
||||
|
||||
// This is a decision table for absolute relocations for the pointer
|
||||
// size data (e.g. R_X86_64_64). Unlike the absrel_table, we can emit
|
||||
// a dynamic relocation if we cannot resolve an address at link-time.
|
||||
static Action table[3][4] = {
|
||||
// Absolute Local Imported data Imported code
|
||||
{ NONE, BASEREL, DYNREL, DYNREL }, // Shared object
|
||||
{ NONE, BASEREL, DYNREL, DYNREL }, // Position-independent exec
|
||||
{ NONE, NONE, DYN_COPYREL, DYN_CPLT }, // Position-dependent exec
|
||||
};
|
||||
|
||||
return table[get_output_type(ctx)][get_sym_type(sym)];
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
static Action get_ppc64_toc_action(Context<E> &ctx, Symbol<E> &sym) {
|
||||
if (sym.is_ifunc())
|
||||
return IFUNC_DYNREL;
|
||||
|
||||
// As a special case, we do not create copy relocations nor canonical
|
||||
// PLTs for .toc sections. PPC64's .toc is a compiler-generated
|
||||
// GOT-like section, and no user-generated code directly uses values
|
||||
// in it.
|
||||
static Action table[3][4] = {
|
||||
// Absolute Local Imported data Imported code
|
||||
{ NONE, BASEREL, DYNREL, DYNREL }, // Shared object
|
||||
{ NONE, BASEREL, DYNREL, DYNREL }, // Position-independent exec
|
||||
{ NONE, NONE, DYNREL, DYNREL }, // Position-dependent exec
|
||||
};
|
||||
|
||||
return table[get_output_type(ctx)][get_sym_type(sym)];
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void InputSection<E>::scan_pcrel(Context<E> &ctx, Symbol<E> &sym,
|
||||
const ElfRel<E> &rel) {
|
||||
scan_rel(ctx, *this, sym, rel, get_pcrel_action(ctx, sym));
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void InputSection<E>::scan_absrel(Context<E> &ctx, Symbol<E> &sym,
|
||||
const ElfRel<E> &rel) {
|
||||
scan_rel(ctx, *this, sym, rel, get_absrel_action(ctx, sym));
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void InputSection<E>::scan_dyn_absrel(Context<E> &ctx, Symbol<E> &sym,
|
||||
const ElfRel<E> &rel) {
|
||||
scan_rel(ctx, *this, sym, rel, get_dyn_absrel_action(ctx, sym));
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void InputSection<E>::scan_toc_rel(Context<E> &ctx, Symbol<E> &sym,
|
||||
const ElfRel<E> &rel) {
|
||||
scan_rel(ctx, *this, sym, rel, get_ppc64_toc_action(ctx, sym));
|
||||
Action action = table[get_output_type(ctx)][get_sym_type(sym)];
|
||||
do_action(ctx, action, *this, sym, rel);
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -372,113 +220,34 @@ void InputSection<E>::check_tlsle(Context<E> &ctx, Symbol<E> &sym,
|
||||
<< " recompile with -fPIC";
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
static void apply_absrel(Context<E> &ctx, InputSection<E> &isec,
|
||||
Symbol<E> &sym, const ElfRel<E> &rel, u8 *loc,
|
||||
u64 S, i64 A, u64 P, ElfRel<E> *&dynrel,
|
||||
Action action) {
|
||||
bool writable = (isec.shdr().sh_flags & SHF_WRITE);
|
||||
|
||||
auto emit_abs_dynrel = [&] {
|
||||
*dynrel++ = ElfRel<E>(P, E::R_ABS, sym.get_dynsym_idx(ctx), A);
|
||||
if (ctx.arg.apply_dynamic_relocs)
|
||||
*(Word<E> *)loc = A;
|
||||
};
|
||||
|
||||
switch (action) {
|
||||
case COPYREL:
|
||||
case CPLT:
|
||||
case NONE:
|
||||
*(Word<E> *)loc = S + A;
|
||||
break;
|
||||
case BASEREL:
|
||||
if (is_relr_reloc(ctx, isec, rel)) {
|
||||
*(Word<E> *)loc = S + A;
|
||||
} else {
|
||||
*dynrel++ = ElfRel<E>(P, E::R_RELATIVE, 0, S + A);
|
||||
if (ctx.arg.apply_dynamic_relocs)
|
||||
*(Word<E> *)loc = S + A;
|
||||
}
|
||||
break;
|
||||
case DYN_COPYREL:
|
||||
if (writable || !ctx.arg.z_copyreloc)
|
||||
emit_abs_dynrel();
|
||||
else
|
||||
*(Word<E> *)loc = S + A;
|
||||
break;
|
||||
case DYN_CPLT:
|
||||
if (writable)
|
||||
emit_abs_dynrel();
|
||||
else
|
||||
*(Word<E> *)loc = S + A;
|
||||
break;
|
||||
case DYNREL:
|
||||
emit_abs_dynrel();
|
||||
break;
|
||||
case IFUNC_DYNREL:
|
||||
if constexpr (supports_ifunc<E>) {
|
||||
u64 addr = sym.get_addr(ctx, NO_PLT) + A;
|
||||
*dynrel++ = ElfRel<E>(P, E::R_IRELATIVE, 0, addr);
|
||||
if (ctx.arg.apply_dynamic_relocs)
|
||||
*(Word<E> *)loc = addr;
|
||||
} else {
|
||||
unreachable();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void InputSection<E>::apply_dyn_absrel(Context<E> &ctx, Symbol<E> &sym,
|
||||
const ElfRel<E> &rel, u8 *loc,
|
||||
u64 S, i64 A, u64 P,
|
||||
ElfRel<E> **dynrel) {
|
||||
apply_absrel(ctx, *this, sym, rel, loc, S, A, P, *dynrel,
|
||||
get_dyn_absrel_action(ctx, sym));
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void InputSection<E>::apply_toc_rel(Context<E> &ctx, Symbol<E> &sym,
|
||||
const ElfRel<E> &rel, u8 *loc,
|
||||
u64 S, i64 A, u64 P,
|
||||
ElfRel<E> **dynrel) {
|
||||
apply_absrel(ctx, *this, sym, rel, loc, S, A, P, *dynrel,
|
||||
get_ppc64_toc_action(ctx, sym));
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
|
||||
if (shdr().sh_type == SHT_NOBITS || sh_size == 0)
|
||||
return;
|
||||
|
||||
// Copy data. In RISC-V and LoongArch object files, sections are not
|
||||
// an atomic unit of copying because of relaxation. That is, some
|
||||
// atomic unit of copying because of relaxation. That is, some
|
||||
// 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);
|
||||
@@ -498,12 +267,14 @@ template <typename E>
|
||||
std::string_view
|
||||
InputSection<E>::get_func_name(Context<E> &ctx, i64 offset) const {
|
||||
for (Symbol<E> *sym : file.symbols) {
|
||||
const ElfSym<E> &esym = sym->esym();
|
||||
if (esym.st_shndx == shndx && esym.st_type == STT_FUNC &&
|
||||
esym.st_value <= offset && offset < esym.st_value + esym.st_size) {
|
||||
if (ctx.arg.demangle)
|
||||
return demangle(*sym);
|
||||
return sym->name();
|
||||
if (sym->file == &file) {
|
||||
const ElfSym<E> &esym = sym->esym();
|
||||
if (esym.st_shndx == shndx && esym.st_type == STT_FUNC &&
|
||||
esym.st_value <= offset && offset < esym.st_value + esym.st_size) {
|
||||
if (ctx.arg.demangle)
|
||||
return demangle(*sym);
|
||||
return sym->name();
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
@@ -554,6 +325,7 @@ bool InputSection<E>::record_undef_error(Context<E> &ctx, const ElfRel<E> &rel)
|
||||
// Every ELF file has an absolute local symbol as its first symbol.
|
||||
// Referring to that symbol is always valid.
|
||||
bool is_undef = esym.is_undef() && !esym.is_weak() && sym.sym_idx;
|
||||
|
||||
if (is_undef && sym.esym().is_undef()) {
|
||||
if (ctx.arg.unresolved_symbols == UNRESOLVED_ERROR && !sym.is_imported) {
|
||||
record();
|
||||
@@ -565,21 +337,13 @@ bool InputSection<E>::record_undef_error(Context<E> &ctx, const ElfRel<E> &rel)
|
||||
}
|
||||
}
|
||||
|
||||
// If a protected/hidden undefined symbol is resolved to other .so,
|
||||
// it's handled as if no symbols were found.
|
||||
if (sym.file->is_dso &&
|
||||
(sym.visibility == STV_PROTECTED || sym.visibility == STV_HIDDEN)) {
|
||||
record();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -618,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;
|
||||
|
||||
@@ -674,4 +437,4 @@ template bool cie_equals(const CieRecord<E> &, const CieRecord<E> &);
|
||||
template class InputSection<E>;
|
||||
template class MergeableSection<E>;
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
@@ -6,9 +6,8 @@
|
||||
#include "mold.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <iomanip>
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
static std::string_view get_line(std::string_view input, const char *pos) {
|
||||
assert(input.data() <= pos);
|
||||
@@ -125,8 +124,7 @@ Script<E>::read_output_format(std::span<std::string_view> tok) {
|
||||
|
||||
template <typename E>
|
||||
static bool is_in_sysroot(Context<E> &ctx, std::string path) {
|
||||
std::filesystem::path sysroot = to_abs_path(ctx.arg.sysroot);
|
||||
std::string rel = to_abs_path(path).lexically_relative(sysroot).string();
|
||||
std::string rel = std::filesystem::relative(path, ctx.arg.sysroot).string();
|
||||
return rel != "." && !rel.starts_with("../");
|
||||
}
|
||||
|
||||
@@ -141,7 +139,7 @@ MappedFile *Script<E>::resolve_path(std::string_view tok, bool check_target) {
|
||||
|
||||
if (check_target) {
|
||||
std::string_view target = get_machine_type(ctx, rctx, mf);
|
||||
if (!target.empty() && target != E::target_name) {
|
||||
if (!target.empty() && target != E::name) {
|
||||
Warn(ctx) << path << ": skipping incompatible file: " << target
|
||||
<< " (e_machine " << (int)E::e_machine << ")";
|
||||
return nullptr;
|
||||
@@ -241,9 +239,9 @@ std::string_view Script<E>::get_script_output_type() {
|
||||
|
||||
if (tok.size() >= 3 && tok[0] == "OUTPUT_FORMAT" && tok[1] == "(") {
|
||||
if (tok[2] == "elf64-x86-64")
|
||||
return X86_64::target_name;
|
||||
return X86_64::name;
|
||||
if (tok[2] == "elf32-i386")
|
||||
return I386::target_name;
|
||||
return I386::name;
|
||||
}
|
||||
|
||||
if (tok.size() >= 3 && (tok[0] == "INPUT" || tok[0] == "GROUP") &&
|
||||
@@ -269,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;
|
||||
@@ -334,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);
|
||||
}
|
||||
|
||||
@@ -360,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);
|
||||
@@ -421,4 +419,4 @@ template class Script<E>;
|
||||
template
|
||||
std::vector<DynamicPattern> parse_dynamic_list(Context<E> &, std::string_view);
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
@@ -95,7 +95,7 @@
|
||||
# define LOG std::ostringstream()
|
||||
#endif
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
// Global variables
|
||||
// We store LTO-related information to global variables,
|
||||
@@ -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 = ObjectFile<E>::create(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);
|
||||
@@ -674,8 +683,8 @@ ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile *mf) {
|
||||
|
||||
// Entry point
|
||||
template <typename E>
|
||||
std::vector<ObjectFile<E> *> do_lto(Context<E> &ctx) {
|
||||
Timer t(ctx, "do_lto");
|
||||
std::vector<ObjectFile<E> *> run_lto_plugin(Context<E> &ctx) {
|
||||
Timer t(ctx, "run_lto_plugin");
|
||||
load_lto_plugin(ctx);
|
||||
|
||||
if (!ctx.arg.lto_pass2 && !supports_v3_api(ctx))
|
||||
@@ -685,7 +694,7 @@ std::vector<ObjectFile<E> *> do_lto(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> *> do_lto(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);
|
||||
@@ -743,7 +752,7 @@ void lto_cleanup(Context<E> &ctx) {
|
||||
using E = MOLD_TARGET;
|
||||
|
||||
template ObjectFile<E> *read_lto_object(Context<E> &, MappedFile *);
|
||||
template std::vector<ObjectFile<E> *> do_lto(Context<E> &);
|
||||
template std::vector<ObjectFile<E> *> run_lto_plugin(Context<E> &);
|
||||
template void lto_cleanup(Context<E> &);
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "mold.h"
|
||||
#include "lto.h"
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
template <typename E>
|
||||
ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile *mf) {
|
||||
@@ -9,7 +9,7 @@ ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile *mf) {
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
std::vector<ObjectFile<E> *> do_lto(Context<E> &ctx) {
|
||||
std::vector<ObjectFile<E> *> run_lto_plugin(Context<E> &ctx) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ void lto_cleanup(Context<E> &ctx) {}
|
||||
using E = MOLD_TARGET;
|
||||
|
||||
template ObjectFile<E> *read_lto_object(Context<E> &, MappedFile *);
|
||||
template std::vector<ObjectFile<E> *> do_lto(Context<E> &);
|
||||
template std::vector<ObjectFile<E> *> run_lto_plugin(Context<E> &);
|
||||
template void lto_cleanup(Context<E> &);
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common/integers.h"
|
||||
#include "../lib/integers.h"
|
||||
|
||||
namespace mold {
|
||||
|
||||
+102
-149
@@ -1,17 +1,11 @@
|
||||
#include "mold.h"
|
||||
#include "../common/archive-file.h"
|
||||
#include "../common/output-file.h"
|
||||
#include "../lib/archive-file.h"
|
||||
|
||||
#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
|
||||
@@ -23,89 +17,12 @@
|
||||
|
||||
#ifdef MOLD_X86_64
|
||||
int main(int argc, char **argv) {
|
||||
return mold::elf::elf_main<mold::elf::X86_64>(argc, argv);
|
||||
mold::set_mimalloc_options();
|
||||
return mold::mold_main<mold::X86_64>(argc, argv);
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace mold::elf {
|
||||
|
||||
// Read the beginning of a given file and returns its machine type
|
||||
// (e.g. EM_X86_64 or EM_386).
|
||||
template <typename E>
|
||||
std::string_view
|
||||
get_machine_type(Context<E> &ctx, ReaderContext &rctx, MappedFile *mf) {
|
||||
auto get_elf_type = [&](u8 *buf) -> std::string_view {
|
||||
bool is_le = (((ElfEhdr<I386> *)buf)->e_ident[EI_DATA] == ELFDATA2LSB);
|
||||
bool is_64;
|
||||
u32 e_machine;
|
||||
|
||||
if (is_le) {
|
||||
auto &ehdr = *(ElfEhdr<I386> *)buf;
|
||||
is_64 = (ehdr.e_ident[EI_CLASS] == ELFCLASS64);
|
||||
e_machine = ehdr.e_machine;
|
||||
} else {
|
||||
auto &ehdr = *(ElfEhdr<M68K> *)buf;
|
||||
is_64 = (ehdr.e_ident[EI_CLASS] == ELFCLASS64);
|
||||
e_machine = ehdr.e_machine;
|
||||
}
|
||||
|
||||
switch (e_machine) {
|
||||
case EM_386:
|
||||
return I386::target_name;
|
||||
case EM_X86_64:
|
||||
return X86_64::target_name;
|
||||
case EM_ARM:
|
||||
return ARM32::target_name;
|
||||
case EM_AARCH64:
|
||||
return ARM64::target_name;
|
||||
case EM_RISCV:
|
||||
if (is_le)
|
||||
return is_64 ? RV64LE::target_name : RV32LE::target_name;
|
||||
return is_64 ? RV64BE::target_name : RV32BE::target_name;
|
||||
case EM_PPC:
|
||||
return PPC32::target_name;
|
||||
case EM_PPC64:
|
||||
return is_le ? PPC64V2::target_name : PPC64V1::target_name;
|
||||
case EM_S390X:
|
||||
return S390X::target_name;
|
||||
case EM_SPARC64:
|
||||
return SPARC64::target_name;
|
||||
case EM_68K:
|
||||
return M68K::target_name;
|
||||
case EM_SH:
|
||||
return SH4::target_name;
|
||||
case EM_ALPHA:
|
||||
return ALPHA::target_name;
|
||||
case EM_LOONGARCH:
|
||||
return is_64 ? LOONGARCH64::target_name : LOONGARCH32::target_name;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
switch (get_file_type(ctx, mf)) {
|
||||
case FileType::ELF_OBJ:
|
||||
case FileType::ELF_DSO:
|
||||
case FileType::GCC_LTO_OBJ:
|
||||
return get_elf_type(mf->data);
|
||||
case FileType::AR:
|
||||
for (MappedFile *child : read_fat_archive_members(ctx, mf))
|
||||
if (FileType ty = get_file_type(ctx, child);
|
||||
ty == FileType::ELF_OBJ || ty == FileType::GCC_LTO_OBJ)
|
||||
return get_elf_type(child->data);
|
||||
return "";
|
||||
case FileType::THIN_AR:
|
||||
for (MappedFile *child : read_thin_archive_members(ctx, mf))
|
||||
if (FileType ty = get_file_type(ctx, child);
|
||||
ty == FileType::ELF_OBJ || ty == FileType::GCC_LTO_OBJ)
|
||||
return get_elf_type(child->data);
|
||||
return "";
|
||||
case FileType::TEXT:
|
||||
return Script(ctx, rctx, mf).get_script_output_type();
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
namespace mold {
|
||||
|
||||
template <typename E>
|
||||
static void
|
||||
@@ -124,9 +41,12 @@ 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 = ObjectFile<E>::create(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)
|
||||
Out(ctx) << "trace: " << *file;
|
||||
@@ -145,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;
|
||||
@@ -157,9 +78,11 @@ static SharedFile<E> *
|
||||
new_shared_file(Context<E> &ctx, ReaderContext &rctx, MappedFile *mf) {
|
||||
check_file_compatibility(ctx, rctx, mf);
|
||||
|
||||
SharedFile<E> *file = SharedFile<E>::create(ctx, 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)
|
||||
Out(ctx) << "trace: " << *file;
|
||||
@@ -251,7 +174,7 @@ MappedFile *open_library(Context<E> &ctx, ReaderContext &rctx, std::string path)
|
||||
return nullptr;
|
||||
|
||||
std::string_view target = get_machine_type(ctx, rctx, mf);
|
||||
if (!target.empty() && target != E::target_name) {
|
||||
if (!target.empty() && target != E::name) {
|
||||
Warn(ctx) << path << ": skipping incompatible file: " << target
|
||||
<< " (e_machine " << (int)E::e_machine << ")";
|
||||
return nullptr;
|
||||
@@ -340,7 +263,15 @@ static void read_input_files(Context<E> &ctx, std::span<std::string> args) {
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
int elf_main(int argc, char **argv) {
|
||||
static bool has_lto_obj(Context<E> &ctx) {
|
||||
for (ObjectFile<E> *file : ctx.objs)
|
||||
if (file->is_reachable && (file->is_lto_obj || file->is_gcc_offload_obj))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
int mold_main(int argc, char **argv) {
|
||||
Context<E> ctx;
|
||||
|
||||
// Process -run option first. process_run_subcommand() does not return.
|
||||
@@ -357,7 +288,7 @@ int elf_main(int argc, char **argv) {
|
||||
|
||||
// Redo if -m is not x86-64.
|
||||
if constexpr (is_x86_64<E>)
|
||||
if (ctx.arg.emulation != X86_64::target_name)
|
||||
if (ctx.arg.emulation != X86_64::name)
|
||||
return redo_main(ctx, argc, argv);
|
||||
|
||||
Timer t_all(ctx, "all");
|
||||
@@ -384,8 +315,8 @@ int elf_main(int argc, char **argv) {
|
||||
|
||||
// Handle --retain-symbols-file options if any.
|
||||
if (ctx.arg.retain_symbols_file)
|
||||
for (std::string_view name : *ctx.arg.retain_symbols_file)
|
||||
get_symbol(ctx, name)->write_to_symtab = true;
|
||||
for (Symbol<E> *sym : *ctx.arg.retain_symbols_file)
|
||||
sym->write_to_symtab = true;
|
||||
|
||||
for (std::string_view arg : ctx.arg.trace_symbol)
|
||||
get_symbol(ctx, arg)->is_traced = true;
|
||||
@@ -401,6 +332,10 @@ int elf_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
|
||||
@@ -410,20 +345,23 @@ int elf_main(int argc, char **argv) {
|
||||
if (!ctx.arg.relocatable)
|
||||
create_internal_file(ctx);
|
||||
|
||||
// resolve_symbols is 4 things in 1 phase:
|
||||
//
|
||||
// - Determine the set of object files to extract from archives.
|
||||
// - Remove redundant COMDAT sections (e.g. duplicate inline functions).
|
||||
// - Finally, the actual symbol resolution.
|
||||
// - LTO, which requires preliminary symbol resolution before running
|
||||
// and a follow-up re-resolution after the LTO objects are emitted.
|
||||
//
|
||||
// These passes have complex interactions, and unfortunately has to be
|
||||
// put together in a single phase.
|
||||
// Resolve symbols by choosing the most appropriate file for each
|
||||
// symbol. This pass also removes redundant comdat sections (e.g.
|
||||
// duplicate inline functions).
|
||||
resolve_symbols(ctx);
|
||||
|
||||
// "Kill" .eh_frame input sections after symbol resolution.
|
||||
kill_eh_frame_sections(ctx);
|
||||
// If there's an object file compiled with -flto, do link-time
|
||||
// optimization.
|
||||
if (has_lto_obj(ctx))
|
||||
do_lto(ctx);
|
||||
|
||||
// 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_reachable; });
|
||||
std::erase_if(ctx.dsos, [](InputFile<E> *file) { return !file->is_reachable; });
|
||||
|
||||
// Parse .eh_frame section contents.
|
||||
parse_eh_frame_sections(ctx);
|
||||
|
||||
// Split mergeable section contents into section pieces.
|
||||
create_merged_sections(ctx);
|
||||
@@ -448,10 +386,18 @@ int elf_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);
|
||||
@@ -463,22 +409,20 @@ int elf_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);
|
||||
|
||||
// Convert an .ARM.exidx to a synthetic section.
|
||||
if constexpr (is_arm32<E>)
|
||||
create_arm_exidx_section(ctx);
|
||||
|
||||
// Handle --section-align options.
|
||||
if (!ctx.arg.section_align.empty())
|
||||
apply_section_align(ctx);
|
||||
@@ -514,10 +458,6 @@ int elf_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)
|
||||
@@ -540,16 +480,7 @@ int elf_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);
|
||||
@@ -594,9 +525,6 @@ int elf_main(int argc, char **argv) {
|
||||
// Fill .gnu.version_r section contents.
|
||||
ctx.verneed->construct(ctx);
|
||||
|
||||
// Compute .symtab and .strtab sizes for each file.
|
||||
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.
|
||||
@@ -608,6 +536,17 @@ int elf_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);
|
||||
|
||||
@@ -618,8 +557,17 @@ int elf_main(int argc, char **argv) {
|
||||
// that they can jump to anywhere in ±2 GiB by default. They may
|
||||
// be replaced with shorter instruction sequences if destinations
|
||||
// are close enough. Do this optimization.
|
||||
if constexpr (is_riscv<E> || is_loongarch<E>)
|
||||
filesize = shrink_sections(ctx);
|
||||
if constexpr (is_riscv<E> || is_loongarch<E>) {
|
||||
shrink_sections(ctx);
|
||||
filesize = set_osec_offsets(ctx);
|
||||
}
|
||||
|
||||
if constexpr (is_arm32<E>) {
|
||||
if (ctx.extra.exidx) {
|
||||
ctx.extra.exidx->remove_duplicate_entries(ctx);
|
||||
filesize = set_osec_offsets(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, memory layout is fixed.
|
||||
|
||||
@@ -631,16 +579,21 @@ int elf_main(int argc, char **argv) {
|
||||
|
||||
// If --compress-debug-sections is given, compress .debug_* sections
|
||||
// using zlib.
|
||||
if (ctx.arg.compress_debug_sections != COMPRESS_NONE)
|
||||
filesize = compress_debug_sections(ctx);
|
||||
if (ctx.arg.compress_debug_sections != COMPRESS_NONE) {
|
||||
compress_debug_sections(ctx);
|
||||
filesize = set_osec_offsets(ctx);
|
||||
}
|
||||
|
||||
// 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
|
||||
ctx.output_file =
|
||||
OutputFile<Context<E>>::open(ctx, ctx.arg.output, filesize, 0777);
|
||||
ctx.output_file = OutputFile<E>::open(ctx, ctx.arg.output, filesize, 0777);
|
||||
ctx.buf = ctx.output_file->buf;
|
||||
|
||||
Timer t_copy(ctx, "copy");
|
||||
@@ -654,13 +607,7 @@ int elf_main(int argc, char **argv) {
|
||||
|
||||
// Dynamic linker works better with sorted .rela.dyn section,
|
||||
// so we sort them.
|
||||
ctx.reldyn->sort(ctx);
|
||||
|
||||
// .note.gnu.build-id section contains a cryptographic hash of the
|
||||
// entire output file. Now that we wrote everything except build-id,
|
||||
// we can compute it.
|
||||
if (ctx.buildid)
|
||||
write_build_id(ctx);
|
||||
sort_reldyn(ctx);
|
||||
|
||||
// .gdb_index's contents cannot be constructed before applying
|
||||
// relocations to other debug sections. We have relocated debug
|
||||
@@ -668,6 +615,12 @@ int elf_main(int argc, char **argv) {
|
||||
if (ctx.gdb_index && ctx.arg.separate_debug_file.empty())
|
||||
write_gdb_index(ctx);
|
||||
|
||||
// .note.gnu.build-id section contains a cryptographic hash of the
|
||||
// entire output file. Now that we wrote everything except build-id,
|
||||
// we can compute it.
|
||||
if (ctx.buildid)
|
||||
write_build_id(ctx);
|
||||
|
||||
if (!ctx.arg.separate_debug_file.empty())
|
||||
write_gnu_debuglink(ctx);
|
||||
|
||||
@@ -716,6 +669,6 @@ int elf_main(int argc, char **argv) {
|
||||
|
||||
using E = MOLD_TARGET;
|
||||
|
||||
template int elf_main<E>(int, char **);
|
||||
template int mold_main<E>(int, char **);
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
@@ -4,24 +4,15 @@
|
||||
#include <iomanip>
|
||||
#include <ios>
|
||||
#include <sstream>
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
#include <tbb/parallel_for_each.h>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
template <typename E>
|
||||
using Map =
|
||||
tbb::concurrent_hash_map<InputSection<E> *, std::vector<Symbol<E> *>>;
|
||||
|
||||
template <typename E>
|
||||
static std::unique_ptr<std::ofstream> open_output_file(Context<E> &ctx) {
|
||||
std::unique_ptr<std::ofstream> file(new std::ofstream);
|
||||
file->open(ctx.arg.Map.c_str());
|
||||
if (!file->is_open())
|
||||
Fatal(ctx) << "cannot open " << ctx.arg.Map << ": " << errno_string();
|
||||
return file;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
static Map<E> get_map(Context<E> &ctx) {
|
||||
Map<E> map;
|
||||
@@ -44,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;
|
||||
}
|
||||
@@ -57,11 +46,13 @@ void print_map(Context<E> &ctx) {
|
||||
Timer t(ctx, "print_map");
|
||||
|
||||
std::ostream *out = &std::cout;
|
||||
std::unique_ptr<std::ofstream> file;
|
||||
std::ofstream file;
|
||||
|
||||
if (!ctx.arg.Map.empty()) {
|
||||
file = open_output_file(ctx);
|
||||
out = file.get();
|
||||
file.open(ctx.arg.Map);
|
||||
if (!file.is_open())
|
||||
Fatal(ctx) << "cannot open " << ctx.arg.Map << ": " << errno_string();
|
||||
out = &file;
|
||||
}
|
||||
|
||||
// Construct a section-to-symbol map.
|
||||
@@ -70,28 +61,32 @@ void print_map(Context<E> &ctx) {
|
||||
// Print a mapfile.
|
||||
*out << " VMA Size Align Out In Symbol\n";
|
||||
|
||||
for (Chunk<E> *osec : ctx.chunks) {
|
||||
for (Chunk<E> *chunk : ctx.chunks) {
|
||||
*out << std::showbase
|
||||
<< std::setw(18) << std::hex << (u64)osec->shdr.sh_addr << std::dec
|
||||
<< std::setw(11) << (u64)osec->shdr.sh_size
|
||||
<< std::setw(6) << (u64)osec->shdr.sh_addralign
|
||||
<< " " << osec->name << "\n";
|
||||
<< std::setw(18) << std::hex << (u64)chunk->shdr.sh_addr << std::dec
|
||||
<< std::setw(11) << (u64)chunk->shdr.sh_size
|
||||
<< std::setw(6) << (u64)chunk->shdr.sh_addralign
|
||||
<< " " << chunk->name << "\n";
|
||||
|
||||
if (!osec->to_osec())
|
||||
OutputSection<E> *osec = chunk->to_osec();
|
||||
if (!osec)
|
||||
continue;
|
||||
|
||||
std::span<InputSection<E> *> members = ((OutputSection<E> *)osec)->members;
|
||||
std::span<InputSection<E> *> members = osec->members;
|
||||
std::vector<std::string> bufs(members.size());
|
||||
|
||||
tbb::parallel_for((i64)0, (i64)members.size(), [&](i64 i) {
|
||||
InputSection<E> *mem = members[i];
|
||||
std::ostringstream ss;
|
||||
u64 addr = osec->shdr.sh_addr + mem->offset;
|
||||
|
||||
u64 addr = 0;
|
||||
if (osec->shdr.sh_flags & SHF_ALLOC)
|
||||
addr = osec->shdr.sh_addr + mem->offset;
|
||||
|
||||
ss << std::showbase
|
||||
<< std::setw(18) << std::hex << addr << std::dec
|
||||
<< std::setw(11) << (u64)mem->sh_size
|
||||
<< std::setw(6) << (1 << (u64)mem->p2align)
|
||||
<< std::setw(6) << (1 << mem->p2align)
|
||||
<< " " << *mem << "\n";
|
||||
|
||||
typename Map<E>::const_accessor acc;
|
||||
@@ -114,4 +109,4 @@ using E = MOLD_TARGET;
|
||||
|
||||
template void print_map(Context<E> &ctx);
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if !defined(__OpenBSD__) && !defined(__FreeBSD__)
|
||||
#if __has_include(<alloca.h>)
|
||||
# include <alloca.h>
|
||||
#endif
|
||||
|
||||
+378
-183
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,23 +1,22 @@
|
||||
#include "common.h"
|
||||
#include "mold.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <filesystem>
|
||||
#include <sys/file.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <system_error>
|
||||
|
||||
namespace mold {
|
||||
|
||||
inline u32 get_umask() {
|
||||
static u32 get_umask() {
|
||||
u32 orig_umask = umask(0);
|
||||
umask(orig_umask);
|
||||
return orig_umask;
|
||||
}
|
||||
|
||||
template <typename Context>
|
||||
template <typename E>
|
||||
static int
|
||||
open_or_create_file(Context &ctx, std::string path, std::string tmpfile,
|
||||
open_or_create_file(Context<E> &ctx, std::string path, std::string tmpfile,
|
||||
int perm) {
|
||||
// Reuse an existing file if exists and writable because on Linux,
|
||||
// writing to an existing file is much faster than creating a fresh
|
||||
@@ -35,14 +34,14 @@ open_or_create_file(Context &ctx, std::string path, std::string tmpfile,
|
||||
return fd;
|
||||
}
|
||||
|
||||
template <typename Context>
|
||||
class MemoryMappedOutputFile : public OutputFile<Context> {
|
||||
template <typename E>
|
||||
class MemoryMappedOutputFile : public OutputFile<E> {
|
||||
public:
|
||||
MemoryMappedOutputFile(Context &ctx, std::string path, i64 filesize, int perm)
|
||||
: OutputFile<Context>(path, filesize, true) {
|
||||
std::filesystem::path dir = filepath(path).parent_path();
|
||||
std::string filename = filepath(path).filename().string();
|
||||
std::string tmpfile = dir / ("." + filename + "." + std::to_string(getpid()));
|
||||
MemoryMappedOutputFile(Context<E> &ctx, std::string path, i64 filesize, int perm)
|
||||
: OutputFile<E>(path, filesize, true) {
|
||||
std::string pid = std::to_string(getpid());
|
||||
std::string tmpfile =
|
||||
path_dirname(path) / ("." + path_filename(path) + "." + pid);
|
||||
|
||||
this->fd = open_or_create_file(ctx, path, tmpfile, perm);
|
||||
|
||||
@@ -72,7 +71,7 @@ public:
|
||||
::close(fd2);
|
||||
}
|
||||
|
||||
void close(Context &ctx) override {
|
||||
void close(Context<E> &ctx) override {
|
||||
Timer t(ctx, "close_file");
|
||||
|
||||
if (!this->is_unmapped)
|
||||
@@ -103,24 +102,19 @@ private:
|
||||
int fd2 = -1;
|
||||
};
|
||||
|
||||
template <typename Context>
|
||||
std::unique_ptr<OutputFile<Context>>
|
||||
OutputFile<Context>::open(Context &ctx, std::string path, i64 filesize, int perm) {
|
||||
template <typename E>
|
||||
std::unique_ptr<OutputFile<E>>
|
||||
OutputFile<E>::open(Context<E> &ctx, std::string path, i64 filesize, int perm) {
|
||||
Timer t(ctx, "open_file");
|
||||
|
||||
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<Context> *file;
|
||||
OutputFile<E> *file;
|
||||
if (is_special)
|
||||
file = new MallocOutputFile(ctx, path, filesize, perm);
|
||||
else
|
||||
@@ -146,10 +140,10 @@ OutputFile<Context>::open(Context &ctx, std::string path, i64 filesize, int perm
|
||||
|
||||
// LockingOutputFile is similar to MemoryMappedOutputFile, but it doesn't
|
||||
// rename output files and instead acquires file lock using flock().
|
||||
template <typename Context>
|
||||
LockingOutputFile<Context>::LockingOutputFile(Context &ctx, std::string path,
|
||||
int perm)
|
||||
: OutputFile<Context>(path, 0, true) {
|
||||
template <typename E>
|
||||
LockingOutputFile<E>::LockingOutputFile(Context<E> &ctx, std::string path,
|
||||
int perm)
|
||||
: OutputFile<E>(path, 0, true) {
|
||||
this->fd = ::open(path.c_str(), O_RDWR | O_CREAT, perm);
|
||||
if (this->fd == -1)
|
||||
Fatal(ctx) << "cannot open " << path << ": " << errno_string();
|
||||
@@ -162,8 +156,8 @@ LockingOutputFile<Context>::LockingOutputFile(Context &ctx, std::string path,
|
||||
(void)!!write(this->fd, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
template <typename Context>
|
||||
void LockingOutputFile<Context>::resize(Context &ctx, i64 filesize) {
|
||||
template <typename E>
|
||||
void LockingOutputFile<E>::resize(Context<E> &ctx, i64 filesize) {
|
||||
if (ftruncate(this->fd, filesize) == -1)
|
||||
Fatal(ctx) << "ftruncate failed: " << errno_string();
|
||||
|
||||
@@ -177,8 +171,8 @@ void LockingOutputFile<Context>::resize(Context &ctx, i64 filesize) {
|
||||
mold::output_buffer_end = this->buf + filesize;
|
||||
}
|
||||
|
||||
template <typename Context>
|
||||
void LockingOutputFile<Context>::close(Context &ctx) {
|
||||
template <typename E>
|
||||
void LockingOutputFile<E>::close(Context<E> &ctx) {
|
||||
if (!this->is_unmapped)
|
||||
munmap(this->buf, this->filesize);
|
||||
|
||||
@@ -192,4 +186,9 @@ void LockingOutputFile<Context>::close(Context &ctx) {
|
||||
::close(this->fd);
|
||||
}
|
||||
|
||||
using E = MOLD_TARGET;
|
||||
|
||||
template class OutputFile<E>;
|
||||
template class LockingOutputFile<E>;
|
||||
|
||||
} // namespace mold
|
||||
@@ -1,16 +1,15 @@
|
||||
#include "common.h"
|
||||
#include "mold.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <filesystem>
|
||||
#include <windows.h>
|
||||
|
||||
namespace mold {
|
||||
|
||||
template <typename Context>
|
||||
class MemoryMappedOutputFile : public OutputFile<Context> {
|
||||
template <typename E>
|
||||
class MemoryMappedOutputFile : public OutputFile<E> {
|
||||
public:
|
||||
MemoryMappedOutputFile(Context &ctx, std::string path, i64 filesize, int perm)
|
||||
: OutputFile<Context>(path, filesize, true) {
|
||||
MemoryMappedOutputFile(Context<E> &ctx, std::string path, i64 filesize, int perm)
|
||||
: OutputFile<E>(path, filesize, true) {
|
||||
// TODO: use intermediate temporary file for output.
|
||||
DWORD attrs = (perm & 0200) ? FILE_ATTRIBUTE_NORMAL : FILE_ATTRIBUTE_READONLY;
|
||||
|
||||
@@ -40,7 +39,7 @@ public:
|
||||
CloseHandle(handle);
|
||||
}
|
||||
|
||||
void close(Context &ctx) override {
|
||||
void close(Context<E> &ctx) override {
|
||||
Timer t(ctx, "close_file");
|
||||
|
||||
UnmapViewOfFile(this->buf);
|
||||
@@ -64,9 +63,9 @@ private:
|
||||
HANDLE handle;
|
||||
};
|
||||
|
||||
template <typename Context>
|
||||
std::unique_ptr<OutputFile<Context>>
|
||||
OutputFile<Context>::open(Context &ctx, std::string path, i64 filesize, int perm) {
|
||||
template <typename E>
|
||||
std::unique_ptr<OutputFile<E>>
|
||||
OutputFile<E>::open(Context<E> &ctx, std::string path, i64 filesize, int perm) {
|
||||
Timer t(ctx, "open_file");
|
||||
|
||||
if (path.starts_with('/') && !ctx.arg.chroot.empty())
|
||||
@@ -86,7 +85,7 @@ OutputFile<Context>::open(Context &ctx, std::string path, i64 filesize, int perm
|
||||
}
|
||||
}
|
||||
|
||||
OutputFile<Context> *file;
|
||||
OutputFile<E> *file;
|
||||
if (is_special)
|
||||
file = new MallocOutputFile(ctx, path, filesize, perm);
|
||||
else
|
||||
@@ -94,20 +93,25 @@ OutputFile<Context>::open(Context &ctx, std::string path, i64 filesize, int perm
|
||||
|
||||
if (ctx.arg.filler != -1)
|
||||
memset(file->buf, ctx.arg.filler, filesize);
|
||||
return std::unique_ptr<OutputFile<Context>>(file);
|
||||
return std::unique_ptr<OutputFile<E>>(file);
|
||||
}
|
||||
|
||||
template <typename Context>
|
||||
LockingOutputFile<Context>::LockingOutputFile(Context &ctx, std::string path,
|
||||
int perm)
|
||||
: OutputFile<Context>(path, 0, true) {
|
||||
template <typename E>
|
||||
LockingOutputFile<E>::LockingOutputFile(Context<E> &ctx, std::string path,
|
||||
int perm)
|
||||
: OutputFile<E>(path, 0, true) {
|
||||
Fatal(ctx) << "LockingOutputFile is not supported on Windows";
|
||||
}
|
||||
|
||||
template <typename Context>
|
||||
void LockingOutputFile<Context>::resize(Context &ctx, i64 filesize) {}
|
||||
template <typename E>
|
||||
void LockingOutputFile<E>::resize(Context<E> &ctx, i64 filesize) {}
|
||||
|
||||
template <typename Context>
|
||||
void LockingOutputFile<Context>::close(Context &ctx) {}
|
||||
template <typename E>
|
||||
void LockingOutputFile<E>::close(Context<E> &ctx) {}
|
||||
|
||||
using E = MOLD_TARGET;
|
||||
|
||||
template class OutputFile<E>;
|
||||
template class LockingOutputFile<E>;
|
||||
|
||||
} // namespace mold
|
||||
+539
-421
File diff suppressed because it is too large.
Load diff
@@ -35,7 +35,7 @@
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/parallel_for_each.h>
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
// Create linker-synthesized sections
|
||||
template <typename E>
|
||||
@@ -53,7 +53,9 @@ static void r_create_synthetic_sections(Context<E> &ctx) {
|
||||
ctx.strtab = push(new StrtabSection<E>);
|
||||
ctx.symtab = push(new SymtabSection<E>);
|
||||
ctx.shstrtab = push(new ShstrtabSection<E>);
|
||||
ctx.note_property = push(new NotePropertySection<E>);
|
||||
|
||||
if constexpr (is_x86<E>)
|
||||
ctx.extra.note_property = push(new NotePropertySection<E>);
|
||||
|
||||
if constexpr (is_riscv<E>)
|
||||
ctx.extra.riscv_attributes = push(new RiscvAttributesSection<E>);
|
||||
@@ -109,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];
|
||||
@@ -169,8 +168,7 @@ void combine_objects(Context<E> &ctx) {
|
||||
compute_section_headers(ctx);
|
||||
|
||||
i64 filesize = r_set_osec_offsets(ctx);
|
||||
ctx.output_file =
|
||||
OutputFile<Context<E>>::open(ctx, ctx.arg.output, filesize, 0666);
|
||||
ctx.output_file = OutputFile<E>::open(ctx, ctx.arg.output, filesize, 0666);
|
||||
ctx.buf = ctx.output_file->buf;
|
||||
|
||||
copy_chunks(ctx);
|
||||
@@ -194,4 +192,4 @@ using E = MOLD_TARGET;
|
||||
|
||||
template void combine_objects(Context<E> &);
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
@@ -1,12 +1,13 @@
|
||||
// Since RISC instructions are generally up to 32 bit long, there's no way
|
||||
// to embed very large immediates into their branch instructions. For
|
||||
// Since RISC instructions are generally up to 32 bits long, there's no
|
||||
// way to embed very large immediates into their branch instructions. For
|
||||
// example, RISC-V's JAL (jump and link) instruction can jump to only
|
||||
// within PC ± 1 MiB because its immediate is 21 bits long. If the
|
||||
// destination is further away, we need to use two instructions instead;
|
||||
// the first instruction being AUIPC which sets upper 20 bit of a
|
||||
// displacement to a register, and the second being JALR which specifies
|
||||
// the lower 12 bits and the register. Combined, they specify a 32 bit
|
||||
// displacement, which is sufficient to support the medium code model.
|
||||
// destination is further than that, we need to use two instructions
|
||||
// instead; the first instruction being AUIPC, which sets the upper 20
|
||||
// bits of a displacement to a register, and the second being JALR, which
|
||||
// specifies the lower 12 bits and the register. Combined, they specify a
|
||||
// 32-bit displacement, which is sufficient to support the medium code
|
||||
// model.
|
||||
//
|
||||
// However, always using two or more instructions for function calls is a
|
||||
// waste of time and space if the branch target is within a single
|
||||
@@ -15,7 +16,7 @@
|
||||
//
|
||||
// 1. The compiler optimistically emits a single branch instruction for
|
||||
// all function calls. The linker then checks if the branch target is
|
||||
// reachable, and if not, redirect the branch to a linker-synthesized
|
||||
// reachable, and if not, redirects the branch to a linker-synthesized
|
||||
// code sequence that uses two or more instructions to branch further.
|
||||
// That linker-synthesized code is called a "thunk". All RISC psABIs
|
||||
// except RISC-V and LoongArch take this approach.
|
||||
@@ -28,31 +29,31 @@
|
||||
// This file contains functions to support (2). For (1), see thunks.cc.
|
||||
//
|
||||
// With the presence of this code-shrinking relaxation, sections can no
|
||||
// longer be considered as an atomic unit. If we delete an instruction
|
||||
// from the middle of a section, section contents after that point needs
|
||||
// to be shifted by the size of the instruction. Symbols values and
|
||||
// relocations offsets have to be shifted too if they refers to past the
|
||||
// deleted bytes.
|
||||
// longer be considered as atomic units. If we delete an instruction from
|
||||
// the middle of a section, the section contents after that point need to
|
||||
// be shifted by the size of the instruction. Symbol values and relocation
|
||||
// offsets have to be shifted too if they refer to bytes past the deleted
|
||||
// ones.
|
||||
//
|
||||
// In mold, we use `r_deltas` to memorize how many bytes have be shifted
|
||||
// In mold, we use `r_deltas` to memorize how many bytes have been shifted
|
||||
// for relocations. For symbols, we directly mutate their `value` member.
|
||||
//
|
||||
// RISC-V and LoongArch object files tend to have way more relocations
|
||||
// than those for other targets. This is because all branches, including
|
||||
// ones that jump within the same section, are explicitly expressed with
|
||||
// relocations. Here is why we need them: all control-flow statements such
|
||||
// as `if` or `for` are implemented using branch instructions. For other
|
||||
// targets, the compiler doesn't emit relocations for such branches
|
||||
// because they know at compile-time exactly how many bytes has to be
|
||||
// those that jump within the same section, are explicitly expressed with
|
||||
// relocations. Here is why we need them: all control-flow statements,
|
||||
// such as `if` or `for`, are implemented using branch instructions. For
|
||||
// other targets, the compiler doesn't emit relocations for such branches
|
||||
// because it knows at compile-time exactly how many bytes have to be
|
||||
// skipped. That's not true in RISC-V and LoongArch because the linker may
|
||||
// delete bytes between a branch and its target. Therefore, all branches
|
||||
// including in-section ones have to be explicitly expressed with
|
||||
// delete bytes between a branch and its target. Therefore, all branches,
|
||||
// including in-section ones, have to be explicitly expressed with
|
||||
// relocations.
|
||||
//
|
||||
// Note that this mechanism only shrink sections and never enlarge, as
|
||||
// the compiler always emits the longest instruction sequence. This
|
||||
// makes the linker implementation a bit simpler because we don't need
|
||||
// to worry about oscillation.
|
||||
// Note that this mechanism only shrinks sections and never enlarges them,
|
||||
// as the compiler always emits the longest instruction sequence. This
|
||||
// makes the linker implementation a bit simpler because we don't need to
|
||||
// worry about oscillation.
|
||||
|
||||
#if MOLD_RV64LE || MOLD_RV64BE || MOLD_RV32LE || MOLD_RV32BE || \
|
||||
MOLD_LOONGARCH64 || MOLD_LOONGARCH32
|
||||
@@ -61,68 +62,59 @@
|
||||
|
||||
#include <tbb/parallel_for_each.h>
|
||||
|
||||
namespace mold::elf {
|
||||
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 <>
|
||||
i64 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.
|
||||
//
|
||||
// Technically speaking, relaxing relocations may allow more relocations
|
||||
// to be relaxed because the distance between a branch instruction and
|
||||
// its target may decrease as a result of relaxation. That said, the
|
||||
// number of such relocations is negligible in practice, so don't bother
|
||||
// to handle them. We scan relocations only once here.
|
||||
// number of such relocations is negligible (I tried to self-host mold
|
||||
// on RISC-V as an experiment and found that the mold-built .text is
|
||||
// only ~0.04% larger than that of GNU ld), so we don't bother to handle
|
||||
// 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;
|
||||
});
|
||||
|
||||
// Re-compute section offset again to finalize them.
|
||||
compute_section_sizes(ctx);
|
||||
return set_osec_offsets(ctx);
|
||||
// Recompute sizes of executable sections
|
||||
tbb::parallel_for_each(ctx.chunks, [&](Chunk<E> *chunk) {
|
||||
if (chunk->to_osec() && (chunk->shdr.sh_flags & SHF_EXECINSTR))
|
||||
chunk->compute_section_size(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
|
||||
@@ -141,6 +133,6 @@ i64 compute_distance<E>(Context<E> &ctx, Symbol<E> &sym,
|
||||
return S + A - P;
|
||||
}
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
|
||||
#endif
|
||||
@@ -1,15 +1,14 @@
|
||||
#include "mold.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <csignal>
|
||||
#include <filesystem>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
#ifdef MOLD_X86_64
|
||||
static int pipe_write_fd = -1;
|
||||
@@ -102,11 +101,11 @@ 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 = filepath(argv[2]).filename();
|
||||
if (std::string cmd = path_filename(argv[2]);
|
||||
cmd == "ld" || cmd == "ld.lld" || cmd == "ld.gold") {
|
||||
std::vector<char *> args;
|
||||
args.push_back(argv[0]);
|
||||
@@ -126,4 +125,4 @@ using E = MOLD_TARGET;
|
||||
|
||||
template void process_run_subcommand(Context<E> &, int, char **);
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "mold.h"
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
#ifdef MOLD_X86_64
|
||||
void fork_child() {}
|
||||
@@ -17,4 +17,4 @@ using E = MOLD_TARGET;
|
||||
|
||||
template void process_run_subcommand(Context<E> &, int, char **);
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
+281
@@ -0,0 +1,281 @@
|
||||
// RISC instructions are usually up to 4 bytes long, so the immediates of
|
||||
// their branch instructions are naturally smaller than 32 bits. This is
|
||||
// contrary to x86-64 on which branch instructions take 4 bytes immediates
|
||||
// and can jump to anywhere within PC ± 2 GiB.
|
||||
//
|
||||
// In fact, ARM32's branch instructions can jump only within ±16 MiB and
|
||||
// ARM64's ±128 MiB, for example. If a branch target is further than that,
|
||||
// we need to let it branch to a linker-synthesized code sequence that
|
||||
// construct a full 32 bit address in a register and jump there. That
|
||||
// linker-synthesized code is called "thunk".
|
||||
//
|
||||
// The function in this file creates thunks.
|
||||
//
|
||||
// Note that although thunks play an important role in an executable, they
|
||||
// don't take up too much space in it. For example, among the clang-16's
|
||||
// text segment whose size is ~300 MiB on ARM64, thunks in total occupy
|
||||
// only ~30 KiB or 0.01%. Of course the number depends on an ISA; we would
|
||||
// need more thunks on ARM32 whose branch range is shorter than ARM64.
|
||||
// That said, the total size of thunks still isn't that much. Therefore,
|
||||
// we don't need to try too hard to reduce thunk size to the absolute
|
||||
// minimum.
|
||||
|
||||
#if MOLD_ARM32 || MOLD_ARM64LE || MOLD_ARM64BE || MOLD_PPC32 || \
|
||||
MOLD_PPC64V1 || MOLD_PPC64V2
|
||||
|
||||
#include "mold.h"
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/parallel_for_each.h>
|
||||
|
||||
namespace mold {
|
||||
|
||||
using E = MOLD_TARGET;
|
||||
|
||||
// We create thunks for each 25.6/3.2/6.4 MiB code block for
|
||||
// ARM64/ARM32/PPC, respectively.
|
||||
static constexpr i64 batch_size = branch_distance<E> / 5;
|
||||
|
||||
// We assume that a single thunk group is smaller than 1 MiB.
|
||||
static constexpr i64 max_thunk_size = 1024 * 1024;
|
||||
|
||||
// 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, bool first_pass, InputSection<E> &isec,
|
||||
Symbol<E> &sym, const ElfRel<E> &rel) {
|
||||
// If the target section is in the same output section but
|
||||
// hasn't got any address yet, that's unreacahble.
|
||||
InputSection<E> *isec2 = sym.get_input_section();
|
||||
if (isec2 && isec.output_section == isec2->output_section &&
|
||||
isec2->offset == -1)
|
||||
return false;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Compute a distance between the relocated place and the symbol
|
||||
// and check if they are within reach.
|
||||
i64 S = sym.get_addr(ctx, NO_OPD);
|
||||
i64 A = get_addend(isec, rel);
|
||||
i64 P = isec.get_addr() + rel.r_offset;
|
||||
i64 val = S + A - P;
|
||||
return -branch_distance<E> <= val && val < branch_distance<E>;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// On PowerPC, all PLT calls go through range extension thunks.
|
||||
if constexpr (is_ppc32<E> || is_ppc64v1<E>)
|
||||
return sym.has_plt(ctx);
|
||||
|
||||
// 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,
|
||||
bool first_pass) {
|
||||
std::span<InputSection<E> *> m = members;
|
||||
if (m.empty())
|
||||
return;
|
||||
|
||||
// Initialize input sections with a dummy offset so that we can
|
||||
// distinguish sections that have got an address with the one who
|
||||
// haven't.
|
||||
for (InputSection<E> *isec : m)
|
||||
isec->offset = -1;
|
||||
thunks.clear();
|
||||
|
||||
// We create thunks from the beginning of the section to the end.
|
||||
// We manage progress using four offsets which increase monotonically.
|
||||
// The locations they point to are always A <= B <= C <= D.
|
||||
//
|
||||
// Input sections between B and C are in the current batch.
|
||||
//
|
||||
// A is the input section with the smallest address than can reach
|
||||
// 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 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 BRANCH_DISTANCE
|
||||
// <--------> Smaller than BRANCH_DISTANCE
|
||||
// <-------------> Reachable from the current batch
|
||||
i64 a = 0;
|
||||
i64 b = 0;
|
||||
i64 c = 0;
|
||||
i64 d = 0;
|
||||
i64 offset = 0;
|
||||
|
||||
// The smallest thunk index that is reachable from the current batch.
|
||||
i64 t = 0;
|
||||
|
||||
while (b < m.size()) {
|
||||
// Move D foward as far as we can jump from B to a thunk at D.
|
||||
auto d_thunk_end = [&] {
|
||||
u64 d_end = align_to(offset, 1 << m[d]->p2align) + m[d]->sh_size;
|
||||
return align_to(d_end, thunk_align) + max_thunk_size;
|
||||
};
|
||||
|
||||
while (d < m.size() &&
|
||||
(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;
|
||||
d++;
|
||||
}
|
||||
|
||||
// Move C forward so that C is apart from B by BATCH_SIZE. We want
|
||||
// to make sure that there's at least one section between B and C
|
||||
// to ensure progress.
|
||||
c = b + 1;
|
||||
while (c < d && m[c]->offset + m[c]->sh_size < m[b]->offset + batch_size)
|
||||
c++;
|
||||
|
||||
// Move A forward so that A is reachable from C.
|
||||
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.
|
||||
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_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.
|
||||
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();
|
||||
|
||||
// Move B forward to point to the begining of the next batch.
|
||||
b = c;
|
||||
}
|
||||
|
||||
for (; t < thunks.size(); t++)
|
||||
for (Symbol<E> *sym : thunks[t]->symbols)
|
||||
sym->flags = 0;
|
||||
|
||||
this->shdr.sh_size = offset;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#endif
|
||||
+41
-16
@@ -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 ===
|
||||
//
|
||||
@@ -122,7 +147,7 @@
|
||||
|
||||
#include "mold.h"
|
||||
|
||||
namespace mold::elf {
|
||||
namespace mold {
|
||||
|
||||
// Returns the TP address which can be used for efficient TLV accesses in
|
||||
// the main executable. TP at runtime refers to a per-process TLS block
|
||||
@@ -137,8 +162,8 @@ 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> || is_alpha<E>) {
|
||||
// On ARM, SH4 and Alpha, the runtime appends two words at the beginning
|
||||
} 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.
|
||||
return align_down(phdr.p_vaddr - sizeof(Word<E>) * 2, phdr.p_align);
|
||||
@@ -186,7 +211,7 @@ u64 get_dtp_addr(const ElfPhdr<E> &phdr) {
|
||||
|
||||
using E = MOLD_TARGET;
|
||||
|
||||
template u64 get_tp_addr<E>(const ElfPhdr<E> &);
|
||||
template u64 get_dtp_addr<E>(const ElfPhdr<E> &);
|
||||
template u64 get_tp_addr(const ElfPhdr<E> &);
|
||||
template u64 get_dtp_addr(const ElfPhdr<E> &);
|
||||
|
||||
} // namespace mold::elf
|
||||
} // namespace mold
|
||||
@@ -18,7 +18,7 @@ endif()
|
||||
|
||||
if(${MACHINE} MATCHES "amd64")
|
||||
set(MACHINE x86_64)
|
||||
elseif(${MACHINE} MATCHES "i386")
|
||||
elseif(${MACHINE} MATCHES "i.86")
|
||||
set(MACHINE i686)
|
||||
elseif(${MACHINE} MATCHES "arm.*")
|
||||
set(MACHINE arm)
|
||||
@@ -30,13 +30,14 @@ endif()
|
||||
|
||||
if(MOLD_ENABLE_QEMU_TESTS)
|
||||
list(APPEND QEMU_ARCHS
|
||||
x86_64 i386 arm aarch64 ppc ppc64 ppc64le sparc64 sh4 s390x
|
||||
alpha riscv64 riscv32 m68k loongarch64)
|
||||
x86_64 i386 arm aarch64 aarch64_be ppc ppc64 ppc64le sparc64 sh4 sh4eb
|
||||
s390x riscv64 riscv32 m68k loongarch64 arc)
|
||||
|
||||
LIST(APPEND TRIPLES
|
||||
x86_64-linux-gnu
|
||||
i686-linux-gnu
|
||||
aarch64-linux-gnu
|
||||
aarch64_be-linux-gnu
|
||||
arm-linux-gnueabihf
|
||||
riscv64-linux-gnu
|
||||
powerpc-linux-gnu
|
||||
@@ -45,10 +46,11 @@ if(MOLD_ENABLE_QEMU_TESTS)
|
||||
sparc64-linux-gnu
|
||||
s390x-linux-gnu
|
||||
sh4-linux-gnu
|
||||
alpha-linux-gnu
|
||||
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})
|
||||
@@ -69,10 +71,10 @@ function(add_target ARCH TRIPLE)
|
||||
file(GLOB ALL_TESTS RELATIVE ${CMAKE_CURRENT_LIST_DIR} CONFIGURE_DEPENDS
|
||||
"*.sh")
|
||||
|
||||
list(FILTER ALL_TESTS EXCLUDE REGEX "_")
|
||||
list(FILTER ALL_TESTS EXCLUDE REGEX "^arch-")
|
||||
|
||||
file(GLOB TESTS RELATIVE ${CMAKE_CURRENT_LIST_DIR} CONFIGURE_DEPENDS
|
||||
"${ARCH}_*.sh")
|
||||
"arch-${ARCH}-*.sh")
|
||||
|
||||
list(APPEND TESTS ${ALL_TESTS})
|
||||
|
||||
@@ -112,6 +114,10 @@ if(${MACHINE} STREQUAL "aarch64" OR (HAS_qemu-aarch64 AND HAS_aarch64-linux-gnu-
|
||||
add_target(aarch64 aarch64-linux-gnu)
|
||||
endif()
|
||||
|
||||
if(${MACHINE} STREQUAL "aarch64_be" OR (HAS_qemu-aarch64_be AND HAS_aarch64_be-linux-gnu-gcc))
|
||||
add_target(aarch64_be aarch64_be-linux-gnu)
|
||||
endif()
|
||||
|
||||
if(${MACHINE} STREQUAL "arm" OR (HAS_qemu-arm AND HAS_arm-linux-gnueabihf-gcc))
|
||||
add_target(arm arm-linux-gnueabihf)
|
||||
endif()
|
||||
@@ -168,8 +174,8 @@ if(${MACHINE} STREQUAL "sh4" OR (HAS_qemu-sh4 AND HAS_sh4-linux-gnu-gcc))
|
||||
add_target(sh4 sh4-linux-gnu)
|
||||
endif()
|
||||
|
||||
if(${MACHINE} STREQUAL "alpha" OR (HAS_qemu-alpha AND HAS_alpha-linux-gnu-gcc))
|
||||
add_target(alpha alpha-linux-gnu)
|
||||
if(${MACHINE} STREQUAL "sh4aeb" OR (HAS_qemu-sh4eb AND HAS_sh4aeb-linux-gnu-gcc))
|
||||
add_target(sh4aeb sh4aeb-linux-gnu)
|
||||
endif()
|
||||
|
||||
if(${MACHINE} STREQUAL "m68k" OR (HAS_qemu-m68k AND HAS_m68k-linux-gnu-gcc))
|
||||
@@ -179,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()
|
||||
@@ -5,7 +5,6 @@
|
||||
[ $MACHINE = ppc64 ] && skip
|
||||
[ $MACHINE = ppc64le ] && skip
|
||||
[ $MACHINE = s390x ] && skip
|
||||
[ $MACHINE = alpha ] && skip
|
||||
[[ $MACHINE = loongarch* ]] && skip
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/a.o -xassembler -
|
||||
@@ -19,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'
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -x assembler -
|
||||
.globl foo
|
||||
foo = 0x800008
|
||||
foo = 0xa00008
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -o $t/b.o -c -fno-PIC -xc -
|
||||
@@ -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=0x80000.$'
|
||||
$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
|
||||
File renamed without changes.
+1
-3
@@ -1,8 +1,6 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
[ $MACHINE = aarch64 ] || skip
|
||||
|
||||
cat <<EOF | $CC -c -o $t/a.o -fPIC -xc -
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -20,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 -q '[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"
|
||||
+3
-3
@@ -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'
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -c -o $t/a.o -xassembler -
|
||||
.globl foo
|
||||
.data
|
||||
foo:
|
||||
.word bar(TARGET1)
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CC -fPIC -c -o $t/b.o -xc -
|
||||
#include <stdio.h>
|
||||
extern char *foo;
|
||||
char bar[] = "Hello world";
|
||||
int main() { printf("%s\n", foo); }
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe -pie $t/a.o $t/b.o
|
||||
$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>'
|
||||
Loaded 100 of 1047 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user