mirror of
https://github.com/rui314/mold.git
synced 2026-11-09 04:49:33 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f86868cb38 | ||
|
|
421289445a | ||
|
|
100922b650 | ||
|
|
0daf6233fb | ||
|
|
d0e4eee204 | ||
|
|
a7475dd5e3 | ||
|
|
c4af13889d | ||
|
|
7aaa4afce7 | ||
|
|
2fb96ad83f | ||
|
|
a2fa4e5ecc | ||
|
|
68877ab6ca | ||
|
|
f62777e606 | ||
|
|
c80e316649 | ||
|
|
d1b9549817 | ||
|
|
01d2dd8dd9 | ||
|
|
6109154a07 | ||
|
|
49d4dac7e6 | ||
|
|
87a5f238dd | ||
|
|
e448e0eb2b | ||
|
|
4df5d6497a | ||
|
|
e90ad9be16 | ||
|
|
b5788e0caa | ||
|
|
9178e43681 | ||
|
|
5feab826de | ||
|
|
2fc0231348 | ||
|
|
804ae412ed | ||
|
|
f0a030cbcd | ||
|
|
ba6347996b | ||
|
|
a151f4180f | ||
|
|
2c947ce2a9 | ||
|
|
63b7102a4c | ||
|
|
731dd0ed25 | ||
|
|
3105195364 | ||
|
|
23045c6f95 | ||
|
|
2d29f234e2 | ||
|
|
4edaf82691 | ||
|
|
49aa10318c | ||
|
|
2c03ed5673 | ||
|
|
e0bc74ad8a | ||
|
|
3768ca612e | ||
|
|
86d59c29eb | ||
|
|
96a4a1b635 | ||
|
|
006b13aec1 | ||
|
|
91947405fe | ||
|
|
b7123bded5 | ||
|
|
0884f279e0 | ||
|
|
615a24d1dd | ||
|
|
e1e4e9fd9e | ||
|
|
ceed69ff49 | ||
|
|
d90f7e162b | ||
|
|
340528bdc3 | ||
|
|
392781a0e2 | ||
|
|
4e10ee7e50 | ||
|
|
8d94fd6ca3 | ||
|
|
358f244247 | ||
|
|
7a1cbb9b34 | ||
|
|
e6d4154688 | ||
|
|
60eae9ed20 | ||
|
|
7fb1b1b9ec | ||
|
|
0ecd72900e | ||
|
|
9eb4e76daf | ||
|
|
b6876b5640 | ||
|
|
f4753b34bf | ||
|
|
8196199cac | ||
|
|
2a3ee11b7d | ||
|
|
68f2a26987 | ||
|
|
227a6c8a88 | ||
|
|
fe617c3618 | ||
|
|
e3a7c9e017 | ||
|
|
5ee6f89544 | ||
|
|
40c2ad7839 | ||
|
|
5edd07efd9 | ||
|
|
8e5def28ce | ||
|
|
b534d5af27 | ||
|
|
673f531adb | ||
|
|
66d3de438c | ||
|
|
d9ea875677 | ||
|
|
577361478a | ||
|
|
408e2bfee8 | ||
|
|
9ac23730b7 | ||
|
|
bf26753bbd | ||
|
|
8e3cee958b | ||
|
|
05f4a43c8f | ||
|
|
ca227fb8da | ||
|
|
78e4b06cb8 | ||
|
|
94cace7fc1 | ||
|
|
812c59fd5c | ||
|
|
8770bd775b | ||
|
|
89d43f2673 | ||
|
|
632a2ec1b4 | ||
|
|
6af78bf17b | ||
|
|
9360267cc3 | ||
|
|
66b995b5a9 | ||
|
|
94e2d7d6dc | ||
|
|
759aa95618 | ||
|
|
575a604d5d | ||
|
|
ac9a1c4048 | ||
|
|
d5cf590498 | ||
|
|
18656091f6 | ||
|
|
ead32811b3 | ||
|
|
7cdfa31e6c | ||
|
|
a6e4676a71 | ||
|
|
0c88d323c8 | ||
|
|
326150a182 | ||
|
|
339816702d | ||
|
|
146ddd711c | ||
|
|
32d1006e29 | ||
|
|
66a057cce7 | ||
|
|
619ff869e4 | ||
|
|
e7c071ea36 | ||
|
|
3d68824dea | ||
|
|
c98d276916 | ||
|
|
446c461201 | ||
|
|
98edd441ce | ||
|
|
37f6ef2081 | ||
|
|
41cf6d7814 | ||
|
|
bb5a596bc5 | ||
|
|
1b6f301644 | ||
|
|
8a089fee45 | ||
|
|
82e80721ac | ||
|
|
d3dea2f435 | ||
|
|
a20e3b8004 | ||
|
|
c1af9c97e7 | ||
|
|
3999aa8b18 | ||
|
|
b4d9db9db8 | ||
|
|
95c7871c6e | ||
|
|
3f7bffe93c | ||
|
|
ec473c74d7 | ||
|
|
9e11b40a99 | ||
|
|
a4fd7e3819 | ||
|
|
aae3dc3c1e | ||
|
|
ba7967152d | ||
|
|
d6dae3be6d | ||
|
|
83aaa63a49 | ||
|
|
b0d91b9967 | ||
|
|
4ea052591a | ||
|
|
2f03e46bb3 | ||
|
|
6205a1edc3 | ||
|
|
4dea134949 | ||
|
|
eb79859ea2 | ||
|
|
8714b78d48 | ||
|
|
86b142e555 | ||
|
|
e5549c73f0 | ||
|
|
4dae896726 | ||
|
|
83e05dae28 | ||
|
|
99a0aaf133 | ||
|
|
7068c0cd0f | ||
|
|
f49e4024e9 | ||
|
|
567a78dbd5 | ||
|
|
f2d27d8d24 | ||
|
|
dc9573989c | ||
|
|
e469b04f0d | ||
|
|
32e158d340 | ||
|
|
18b6f96756 | ||
|
|
2b7dbb01c4 | ||
|
|
c1c9bdf363 | ||
|
|
d4bbf31db2 | ||
|
|
9b5d404d1e | ||
|
|
4caadefac6 | ||
|
|
8f21cc386e | ||
|
|
f7de9571f8 | ||
|
|
e413433752 | ||
|
|
e67f460bfd | ||
|
|
e281ed88bb | ||
|
|
41986279de | ||
|
|
480efdedf0 | ||
|
|
99e3f959fc | ||
|
|
4ec0bbf048 | ||
|
|
f4c0e321db | ||
|
|
31a5cdf86f | ||
|
|
a44de89991 | ||
|
|
30683645c2 | ||
|
|
ac3ae60184 | ||
|
|
9f34830882 | ||
|
|
4aa4bfa4a6 | ||
|
|
fb6967c0cf | ||
|
|
385c1c494d | ||
|
|
4667be6134 | ||
|
|
3f9868238b | ||
|
|
3e8c519d4e | ||
|
|
aa27d674c1 | ||
|
|
3cbc57f9af | ||
|
|
a1e6d8e8df | ||
|
|
a054bcd4ff | ||
|
|
e47c67727c | ||
|
|
16bdf4009a | ||
|
|
c173f9c0ab |
No files matched your search
+21
-21
@@ -9,35 +9,39 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: rui314/setup-mold@v1
|
||||
with:
|
||||
make-default: false
|
||||
- name: apt-get
|
||||
run: sudo dpkg --add-architecture i386 && sudo apt-get update && sudo apt-get install build-essential libstdc++-10-dev cmake clang libssl-dev zlib1g-dev libxxhash-dev libtbb-dev git bsdmainutils dwarfdump libc6-dev:i386 lib32gcc-10-dev libstdc++-10-dev-arm64-cross gcc-aarch64-linux-gnu g++-aarch64-linux-gnu qemu-user
|
||||
run: sudo dpkg --add-architecture i386 && sudo apt-get update && sudo apt-get install git build-essential libstdc++-10-dev cmake clang libssl-dev zlib1g-dev libxxhash-dev libtbb-dev git bsdmainutils dwarfdump pkg-config
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
- name: make
|
||||
run: |
|
||||
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
|
||||
make CC=clang CXX=clang++ DEBUG=1 -j$(nproc)
|
||||
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make CC=clang CXX=clang++ LDFLAGS=-fuse-ld=mold -j$(nproc)
|
||||
- name: make test
|
||||
run: |
|
||||
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
|
||||
make test -j$(nproc)
|
||||
build-gcc11:
|
||||
run: make -j$(nproc) test
|
||||
build-gcc:
|
||||
runs-on: ubuntu-20.04
|
||||
container: gcc:11.1.0
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: apt-get
|
||||
run: dpkg --add-architecture i386 && apt-get update && apt-get install -y sudo libssl-dev zlib1g-dev libxxhash-dev cmake clang dwarfdump bsdextrautils libgcc-9-dev gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libc6-dev:i386 lib32gcc-10-dev qemu-user
|
||||
run: |
|
||||
dpkg --add-architecture i386 && apt-get update && apt-get install -y sudo libssl-dev zlib1g-dev libxxhash-dev cmake clang bsdmainutils dwarfdump qemu-user gcc-10-i686-linux-gnu gcc-10-aarch64-linux-gnu gcc-10-riscv64-linux-gnu gcc-10-arm-linux-gnueabihf g++-10-i686-linux-gnu g++-10-aarch64-linux-gnu g++-10-riscv64-linux-gnu g++-10-arm-linux-gnueabihf
|
||||
ln -sf /usr/bin/i686-linux-gnu-gcc-10 /usr/bin/i686-linux-gnu-gcc
|
||||
ln -sf /usr/bin/i686-linux-gnu-g++-10 /usr/bin/i686-linux-gnu-g++
|
||||
ln -sf /usr/bin/aarch64-linux-gnu-gcc-10 /usr/bin/aarch64-linux-gnu-gcc
|
||||
ln -sf /usr/bin/aarch64-linux-gnu-g++-10 /usr/bin/aarch64-linux-gnu-g++
|
||||
ln -sf /usr/bin/arm-linux-gnueabihf-gcc-10 /usr/bin/arm-linux-gnueabihf-gcc
|
||||
ln -sf /usr/bin/arm-linux-gnueabihf-g++-10 /usr/bin/arm-linux-gnueabihf-g++
|
||||
ln -sf /usr/bin/riscv64-linux-gnu-gcc-10 /usr/bin/riscv64-linux-gnu-gcc
|
||||
ln -sf /usr/bin/riscv64-linux-gnu-g++-10 /usr/bin/riscv64-linux-gnu-g++
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
- name: make
|
||||
run: |
|
||||
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
|
||||
make CC=gcc CXX=g++ DEBUG=1 -j$(nproc)
|
||||
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make CC=gcc CXX=g++ -j$(nproc)
|
||||
- name: make test
|
||||
run: |
|
||||
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
|
||||
make test -j$(nproc)
|
||||
run: make -j$(nproc) test-all
|
||||
build-macos:
|
||||
runs-on: macos-11
|
||||
steps:
|
||||
@@ -47,10 +51,6 @@ jobs:
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
- name: make
|
||||
run: |
|
||||
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
|
||||
make DEBUG=1 -j$(sysctl -n hw.logicalcpu)
|
||||
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make -j$(sysctl -n hw.logicalcpu)
|
||||
- name: make test
|
||||
run: |
|
||||
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
|
||||
make test -j$(sysctl -n hw.logicalcpu)
|
||||
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make -j$(sysctl -n hw.logicalcpu) test
|
||||
@@ -11,3 +11,4 @@ ld
|
||||
ld64.mold
|
||||
gentoo
|
||||
out
|
||||
/mold-*.tar.gz
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
# This dockerfile creates a reproducible build environment for mold.
|
||||
#
|
||||
# $ docker build -t rui314/mold-builder:v1-$(uname -m) .
|
||||
# $ docker push rui314/mold-builder:v1-$(uname -m)
|
||||
|
||||
FROM ubuntu:18.04
|
||||
RUN apt-get update && \
|
||||
TZ=Europe/London apt-get install -y tzdata && \
|
||||
apt-get install -y --no-install-recommends software-properties-common && \
|
||||
add-apt-repository -y ppa:ubuntu-toolchain-r/test && \
|
||||
apt-get install -y --no-install-recommends build-essential git \
|
||||
wget pkg-config cmake libstdc++-11-dev zlib1g-dev gpg gpg-agent && \
|
||||
bash -c "$(wget -O- https://apt.llvm.org/llvm.sh)" && \
|
||||
apt-get install -y --no-install-recommends clang-14 && \
|
||||
apt clean && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
cd / && \
|
||||
wget -O- https://www.openssl.org/source/openssl-3.0.2.tar.gz | tar xzf - && \
|
||||
mv openssl-3.0.2 openssl && \
|
||||
cd openssl && \
|
||||
./Configure --prefix=/openssl && \
|
||||
make -j$(nproc)
|
||||
@@ -1,8 +1,8 @@
|
||||
# If you want to enable ASAN, run `make` with the following options:
|
||||
#
|
||||
# make CXXFLAGS=-fsanitize=address LDFLAGS=-fsanitize=address USE_MIMALLOC=0
|
||||
# make CXXFLAGS='-fsanitize=address -g' LDFLAGS=-fsanitize=address USE_MIMALLOC=0
|
||||
|
||||
VERSION = 1.1
|
||||
VERSION = 1.2.0
|
||||
|
||||
PREFIX = /usr/local
|
||||
BINDIR = $(PREFIX)/bin
|
||||
@@ -161,13 +161,40 @@ ifeq ($(OS), Darwin)
|
||||
else
|
||||
$(MAKE) -C test -f Makefile.linux --no-print-directory --output-sync
|
||||
endif
|
||||
|
||||
@if test -t 1; then \
|
||||
printf '\e[32mPassed all tests\e[0m\n'; \
|
||||
else \
|
||||
echo 'Passed all tests'; \
|
||||
fi
|
||||
|
||||
test-x86-64: all
|
||||
@echo x86_64
|
||||
CC=x86_64-linux-gnu-gcc CXX=x86_64-linux-gnu-g++ GCC=x86_64-linux-gnu-gcc GXX=x86_64-linux-gnu-g++ OBJDUMP=x86_64-linux-gnu-objdump MACHINE=x86_64 QEMU='qemu-x86_64 -L /usr/x86_64-linux-gnu' $(MAKE) test
|
||||
|
||||
test-i386: all
|
||||
@echo i386
|
||||
CC=i686-linux-gnu-gcc CXX=i686-linux-gnu-g++ GCC=i686-linux-gnu-gcc GXX=i686-linux-gnu-g++ OBJDUMP=x86_64-linux-gnu-objdump MACHINE=i386 QEMU='qemu-i386 -L /usr/i686-linux-gnu' $(MAKE) test
|
||||
|
||||
test-arm64: all
|
||||
@echo arm64
|
||||
CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ GCC=aarch64-linux-gnu-gcc GXX=aarch64-linux-gnu-g++ OBJDUMP=aarch64-linux-gnu-objdump MACHINE=aarch64 QEMU='qemu-aarch64 -L /usr/aarch64-linux-gnu' $(MAKE) test
|
||||
|
||||
test-arm32: all
|
||||
@echo arm
|
||||
CC=arm-linux-gnueabihf-gcc CXX=arm-linux-gnueabihf-g++ GCC=arm-linux-gnueabihf-gcc GXX=arm-linux-gnueabihf-g++ OBJDUMP=arm-linux-gnueabihf-objdump MACHINE=arm QEMU='qemu-arm -L /usr/arm-linux-gnueabihf' $(MAKE) test
|
||||
|
||||
test-riscv64: all
|
||||
@echo riscv64
|
||||
CC=riscv64-linux-gnu-gcc CXX=riscv64-linux-gnu-g++ GCC=riscv64-linux-gnu-gcc GXX=riscv64-linux-gnu-g++ OBJDUMP=riscv64-linux-gnu-objdump MACHINE=riscv64 QEMU='qemu-riscv64 -L /usr/riscv64-linux-gnu' $(MAKE) test
|
||||
|
||||
|
||||
test-all:
|
||||
$(MAKE) test-x86-64
|
||||
$(MAKE) test-i386
|
||||
$(MAKE) test-arm64
|
||||
$(MAKE) test-arm32
|
||||
$(MAKE) test-riscv64
|
||||
|
||||
install: all
|
||||
$(INSTALL) -d $D$(BINDIR)
|
||||
$(INSTALL_PROGRAM) mold $D$(BINDIR)
|
||||
@@ -192,6 +219,6 @@ uninstall:
|
||||
rm -rf $D$(LIBDIR)/mold
|
||||
|
||||
clean:
|
||||
rm -rf *~ mold mold-wrapper.so out ld ld64.mold
|
||||
rm -rf *~ mold mold-wrapper.so out ld ld64.mold mold-*-linux.tar.gz
|
||||
|
||||
.PHONY: all test tests check clean
|
||||
.PHONY: all test tests check clean test-x86-64 test-i386 test-arm64 test-arm32 test-riscv64 test-all
|
||||
@@ -22,7 +22,7 @@ mold is so fast that it is only 2x _slower_ than `cp` on the same
|
||||
machine. Feel free to [file a bug](https://github.com/rui314/mold/issues)
|
||||
if you find mold is not faster than other linkers.
|
||||
|
||||
mold currently supports x86-64, i386, ARM64 and 64-bit RISC-V.
|
||||
mold currently supports x86-64, i386, ARM32, ARM64 and 64-bit RISC-V.
|
||||
|
||||
## Why does the speed of linking matter?
|
||||
|
||||
@@ -72,7 +72,7 @@ sudo dnf install -y git clang cmake openssl-devel xxhash-devel zlib-devel libstd
|
||||
```shell
|
||||
git clone https://github.com/rui314/mold.git
|
||||
cd mold
|
||||
git checkout v1.1
|
||||
git checkout v1.2.0
|
||||
make -j$(nproc) CXX=clang++
|
||||
sudo make install
|
||||
```
|
||||
@@ -170,6 +170,14 @@ replace `argv[0]` with `mold` if it is `ld`, `ld.gold` or `ld.lld`.
|
||||
|
||||
</details>
|
||||
|
||||
<details><summary>GitHub Action</summary>
|
||||
If you want to use mold in your GitHub-hosted CI to speed up continuous
|
||||
build, you can use <a href=https://github.com/rui314/setup-mold>setup-mold</a>
|
||||
GitHub Action. GitHub runs a CI on a two-core machine, but mold is
|
||||
still significantly faster than the default GNU linker there
|
||||
especially when a program being linked is large.
|
||||
</details>
|
||||
|
||||
<details><summary>Verify that you are using mold</summary>
|
||||
|
||||
mold leaves its identification string in `.comment` section in an output
|
||||
|
||||
+20
-14
@@ -1,31 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
#error "mold does not support big-endian hosts"
|
||||
#endif
|
||||
|
||||
namespace mold {
|
||||
|
||||
template <typename T>
|
||||
class BigEndian {
|
||||
public:
|
||||
BigEndian() : BigEndian(0) {}
|
||||
|
||||
BigEndian(T x) {
|
||||
*this = x;
|
||||
}
|
||||
BigEndian() = delete;
|
||||
|
||||
operator T() const {
|
||||
// We don't need to optimize this code because compilers are
|
||||
// usually smart enough to compile this loop into a single
|
||||
// byte-swap instruction such as x86's bswap.
|
||||
T ret = 0;
|
||||
for (int i = 0; i < sizeof(T); i++)
|
||||
ret = (ret << 8) | val[i];
|
||||
return ret;
|
||||
T x;
|
||||
memcpy(&x, val, sizeof(T));
|
||||
return bswap(x);
|
||||
}
|
||||
|
||||
BigEndian &operator=(T x) {
|
||||
for (int i = 0; i < sizeof(T); i++)
|
||||
val[sizeof(T) - 1 - i] = x >> (i * 8);
|
||||
x = bswap(x);
|
||||
memcpy(&val, &x, sizeof(T));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -63,6 +60,15 @@ public:
|
||||
|
||||
private:
|
||||
uint8_t val[sizeof(T)];
|
||||
|
||||
static T bswap(T x) {
|
||||
if constexpr (sizeof(T) == 2)
|
||||
return __builtin_bswap16(x);
|
||||
else if constexpr (sizeof(T) == 4)
|
||||
return __builtin_bswap32(x);
|
||||
else
|
||||
return __builtin_bswap64(x);
|
||||
}
|
||||
};
|
||||
|
||||
using ibig16 = BigEndian<int16_t>;
|
||||
+30
-5
@@ -17,6 +17,8 @@
|
||||
#include <tbb/parallel_for_each.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#define CHECK(fn) do { int r = (fn); assert(r == Z_OK); } while (0)
|
||||
|
||||
namespace mold {
|
||||
|
||||
static constexpr i64 SHARD_SIZE = 1024 * 1024;
|
||||
@@ -41,8 +43,8 @@ static std::vector<u8> do_compress(std::string_view input) {
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
int r = deflateInit2(&strm, 1, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
|
||||
assert(r == Z_OK);
|
||||
|
||||
CHECK(deflateInit2(&strm, 1, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY));
|
||||
|
||||
// Set an input buffer
|
||||
strm.avail_in = input.size();
|
||||
@@ -52,13 +54,36 @@ static std::vector<u8> do_compress(std::string_view input) {
|
||||
// on the compression size. +16 for Z_SYNC_FLUSH.
|
||||
std::vector<u8> buf(deflateBound(&strm, strm.avail_in) + 16);
|
||||
|
||||
// Compress data. It writes all compressed bytes except the last
|
||||
// partial byte, so up to 7 bits can be held to be written to the
|
||||
// buffer.
|
||||
strm.avail_out = buf.size();
|
||||
strm.next_out = buf.data();
|
||||
CHECK(deflate(&strm, Z_BLOCK));
|
||||
|
||||
// This is a workaround for libbacktrace before 2022-04-06.
|
||||
//
|
||||
// Zlib is a bit stream, and what Z_SYNC_FLUSH does is to write a
|
||||
// three bit value indicating the start of an uncompressed data
|
||||
// block followed by four byte data 00 00 ff ff which indicates that
|
||||
// the length of the block is zero. libbacktrace uses its own zlib
|
||||
// inflate routine, and it had a bug that if that particular three
|
||||
// bit value happens to end at a byte boundary, it accidentally
|
||||
// skipped the next byte.
|
||||
//
|
||||
// In order to avoid triggering that bug, we should avoid calling
|
||||
// deflate() with Z_SYNC_FLUSH if the current bit position is 5.
|
||||
// If it's 5, we insert an empty block consisting of 10 bits so
|
||||
// that the bit position is 7 in the next byte.
|
||||
//
|
||||
// https://github.com/ianlancetaylor/libbacktrace/pull/87
|
||||
int nbits;
|
||||
deflatePending(&strm, Z_NULL, &nbits);
|
||||
if (nbits == 5)
|
||||
CHECK(deflatePrime(&strm, 10, 2));
|
||||
CHECK(deflate(&strm, Z_SYNC_FLUSH));
|
||||
|
||||
r = deflate(&strm, Z_SYNC_FLUSH);
|
||||
assert(r == Z_OK);
|
||||
assert(strm.avail_out > 0);
|
||||
|
||||
buf.resize(buf.size() - strm.avail_out);
|
||||
deflateEnd(&strm);
|
||||
return buf;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash -x
|
||||
# This script creates a mold executable suitable for binary distribution.
|
||||
# The output is written in this directory as `mold-$version-$arch-linux.tar.gz`
|
||||
# This script creates a mold binary distribution. The output is
|
||||
# written in this directory as `mold-$version-$arch-linux.tar.gz`
|
||||
# (e.g. `mold-1.0.3-x86_64-linux.tar.gz`).
|
||||
#
|
||||
# The mold executable created by this script is statically-linked to
|
||||
@@ -9,34 +9,26 @@
|
||||
|
||||
set -e
|
||||
|
||||
ver=$(grep '^VERSION =' $(dirname $0)/Makefile | sed 's/.* = //')
|
||||
dest=mold-$ver-$(uname -m)-linux
|
||||
version=$(grep '^VERSION =' $(dirname $0)/Makefile | sed 's/.* = //')
|
||||
arch=$(uname -m)
|
||||
dest=mold-$version-$arch-linux
|
||||
|
||||
cat <<'EOF' | docker build -t mold-build-ubuntu18 -
|
||||
FROM ubuntu:18.04
|
||||
RUN apt-get update && \
|
||||
TZ=Europe/London apt-get install -y tzdata && \
|
||||
apt-get install -y --no-install-recommends software-properties-common && \
|
||||
add-apt-repository -y ppa:ubuntu-toolchain-r/test && \
|
||||
apt-get install -y --no-install-recommends build-essential git \
|
||||
wget pkg-config cmake libstdc++-11-dev zlib1g-dev gpg gpg-agent && \
|
||||
bash -c "$(wget -O- https://apt.llvm.org/llvm.sh)" && \
|
||||
apt-get install -y --no-install-recommends clang-14 && \
|
||||
apt clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
EOF
|
||||
if [ $arch = x86_64 ]; then
|
||||
image=rui314/mold-builder:v1-x86_64
|
||||
elif [ $arch = aarch64 -o $arch = arm64 ]; then
|
||||
image=rui314/mold-builder:v1-aarch64
|
||||
else
|
||||
echo "Error: no docker image for $arch"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
docker run -it --rm -v "$(pwd):/mold:Z" -u "$(id -u):$(id -g)" \
|
||||
mold-build-ubuntu18 \
|
||||
bash -c "cd /tmp &&
|
||||
wget -O- https://www.openssl.org/source/openssl-3.0.1.tar.gz | tar xzf - &&
|
||||
cd openssl-3.0.1 &&
|
||||
./Configure &&
|
||||
make -j$(nproc) &&
|
||||
cp -r /mold /tmp/mold &&
|
||||
docker images -q $image 2> /dev/null || docker pull $image
|
||||
|
||||
docker run -it --rm -v "$(pwd):/mold:Z" -u "$(id -u):$(id -g)" $image \
|
||||
bash -c "cp -r /mold /tmp/mold &&
|
||||
cd /tmp/mold &&
|
||||
make clean &&
|
||||
make -j$(nproc) CXX=clang++-14 CXXFLAGS=-I/tmp/openssl-3.0.1/include LDFLAGS='-static-libstdc++ /tmp/openssl-3.0.1/libcrypto.a' NEEDS_LIBCRYPTO=0 &&
|
||||
make -j\$(nproc) CXX=clang++-14 CXXFLAGS='-I/openssl/include -O2 $CXXFLAGS' LDFLAGS='-static-libstdc++ /openssl/libcrypto.a' NEEDS_LIBCRYPTO=0 LTO=${LTO:-0} &&
|
||||
make install PREFIX=/ DESTDIR=$dest &&
|
||||
ln -sfr $dest/bin/mold $dest/libexec/mold/ld &&
|
||||
tar czf /mold/$dest.tar.gz $dest &&
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
This page explains the following warning message and how to fix it.
|
||||
mold emits this message when it sees an object file that may not be
|
||||
compatible with mold.
|
||||
|
||||
|
||||
```
|
||||
mold: warning: foo.o: this file may cause a segmentation fault because it requires an executable stack. See https://github.com/rui314/mold/tree/main/docs/execstack.md for more info.
|
||||
```
|
||||
|
||||
# Background
|
||||
|
||||
On modern computers, the stack area (to which local variables are
|
||||
stored) cannot contain executable code. If the control reaches the
|
||||
stack area, the CPU refuses to execute any code there and the program
|
||||
is usually terminated due to segmentation fault.
|
||||
|
||||
This is a security measure. The stack area used to be executable (old
|
||||
CPUs generally execute any code as long as it is in a readable memory
|
||||
region), but that provided an easy attack vector to a malicious user.
|
||||
They wrote executable code to the stack area using some buffer
|
||||
overflow bug and jumped there to run arbitrary code in a remote server
|
||||
process.
|
||||
|
||||
To prevent this type of attack, the stack area is no longer executable
|
||||
since the early 2000s. On Linux, the stack's executable-ness is
|
||||
controlled by a bit in an executable, and the loader respect that
|
||||
bit. The bit is set by the linker.
|
||||
|
||||
GCC had (and still have) a feature that depends on the executable
|
||||
stack, so they invented a way to tell the linker to mark the stack
|
||||
executable. Specifically, if an object file contains a
|
||||
`.note.GNU-stack` section with the `SHF_EXECSTACK` bit, GNU linker
|
||||
silently makes the stack of an output file executable.
|
||||
|
||||
But the GNU linker's behavior is dangerous. If you accidentally link
|
||||
an object file that has that marker section, the entire stack area
|
||||
silently becomes executable, disabling the security mechanism.
|
||||
|
||||
Therefore, mold simply ignores that marker section. If you are using
|
||||
mold, you need to explicitly pass `-z execstack` to the linker to make
|
||||
the stack executable.
|
||||
|
||||
# What caused this issue?
|
||||
|
||||
You are likely to use GCC's [Nested
|
||||
Functions](https://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html)
|
||||
feature which still depends on the executable stack.
|
||||
|
||||
# How to fix it?
|
||||
|
||||
If you know what you are doing, pass `-z execstack` to mold. Beaware
|
||||
that this will significantly weaken your program's security.
|
||||
|
||||
If you don't want to pass `-z execstack`, rewrite your code so that
|
||||
your code does not depends on the executable stack.
|
||||
@@ -0,0 +1,158 @@
|
||||
The very concept of linking is simple: a compiler compiles a piece of
|
||||
source code into an object file (a file containing machine code), and
|
||||
a linker combines object files into a single executable or a shared
|
||||
library file. However, the actual implementation of the linker for
|
||||
modern systems is much more complicated because hardware, operating
|
||||
system, compiler and linker all have many more features.
|
||||
|
||||
In this file, I'll explain random topics in the glossary format that
|
||||
you need to understand to read mold code.
|
||||
|
||||
## DSO
|
||||
|
||||
A .so file. Short for Dynamic Shared Object. Often called as a
|
||||
shared library, a dynamic libray or a shared object as well.
|
||||
|
||||
An DSO contains common functions and data that are used by multiple
|
||||
executables and/or other DSOs. At runtime, a DSO is loaded to a
|
||||
contiguous region in the virtual address.
|
||||
|
||||
## Object file
|
||||
|
||||
A .o file. An object file contains machine code and data, but it
|
||||
cannot be executed because it's not self-contained. For example,
|
||||
if you compile a C source file containing a call of `printf`,
|
||||
the actual function code of `printf` is not included in the resulting
|
||||
object file. You include `stdio.h`, but that teaches the compiler
|
||||
only about `printf`'s type, and the compiler still don't know what
|
||||
`printf` actually does. Therefore, it cannot emit code for `printf`.
|
||||
|
||||
You need to link an object file with other object file or a shared
|
||||
library to make it exectuable.
|
||||
|
||||
## Virtual address space
|
||||
|
||||
A pointer has a value like 0x803020 which is an address of the
|
||||
pointee. But it doesn't mean that the pointee resides at the
|
||||
physical memory address 0x803020 on the computer. Modern CPUs
|
||||
contains so-called Mmeory Management Unit (MMU), and all access to
|
||||
the memory are first translated by MMU to the physical address.
|
||||
The address before translation is called the "virtual address".
|
||||
Unless you are doing the kernel programming, all addresses you
|
||||
handle are virtual addresses.
|
||||
|
||||
The OS kernel controls the MMU so that each process owns the entire
|
||||
virtual address space. So, even if two process uses the same virtual
|
||||
address, they don't conflict. They are mapped to different physical
|
||||
addresses.
|
||||
|
||||
The existence of MMU has several implications to the linker. First,
|
||||
we can link the main executable to a specific address. On process
|
||||
startup, there's no code or data in the virtual address space, so
|
||||
the mapping of the main executable always succeed. However, it's not
|
||||
true to DSOs because they are loaded after the main executable and
|
||||
possibly other DSOs. Therefore, shared libraries must be linked in a
|
||||
way that they can be loaded to any address in the virtual address
|
||||
space.
|
||||
|
||||
## Relocation
|
||||
|
||||
A piece of information for the linker as to how to link object files
|
||||
or a dynamic objects.
|
||||
|
||||
Object files can refer functions or data in other object files. For
|
||||
example, if you compile a function which calls a non-local function
|
||||
`foo`, the resulting code contains something like this:
|
||||
|
||||
```
|
||||
26: e8 00 00 00 00 callq 2b <bar+0xb>
|
||||
27: R_X86_64_PLT32 foo-0x4
|
||||
```
|
||||
|
||||
The above `callq` is the instruction to call a function at the
|
||||
machine code level. It's opcode is `0xe8` in x86-64, so the
|
||||
instruction begins with `0xe8`. The following four bytes are
|
||||
displacement; that is, the address of the branch target relative to
|
||||
the end of this `callq` instruction. Notice that the displacement is
|
||||
0. The compiler couldn't fill the displacement because it has no
|
||||
idea as to where `foo` will be at runtime. So, the compiler write 0
|
||||
as a placeholder and instead write a relocation `R_X86_64_PLT32`
|
||||
with `foo` as its associated symbol. The linker reads this
|
||||
relocation, computes the offsets between this call instruction and
|
||||
function `foo` and overwrite the placeholder value 0 with an actual
|
||||
displacement.
|
||||
|
||||
There are many different types of relocations. For example, if you
|
||||
want to fix up not with a displacement but with an absolute address
|
||||
of a symbol, you need to use `R_X86_64_ABS64` instead.
|
||||
|
||||
## Static library
|
||||
|
||||
A .a file. Often called as an archive file or just archive as well.
|
||||
|
||||
A static library is a container just like tar or zip. Actually,
|
||||
there's no technical reason to not use tar or (uncompressed) zip,
|
||||
but traditionally the .a file format is used by the linker.
|
||||
|
||||
A static library contains object files and can be passed to the
|
||||
linker along with other object files and/or archives.
|
||||
|
||||
A linker pulls out object files from an archive only if it is needed
|
||||
to resolve undefined symbols. In other words, object files in an
|
||||
archive are not linked by default and used as a complement to supply
|
||||
missing definitions. This is ideal for a library because you don't
|
||||
want to link library functions unless you are actually using them.
|
||||
|
||||
Contrary to archive files, object files directly given to a linker
|
||||
are always linked to the output.
|
||||
|
||||
To maximize the benefit of archive files, a library often used as a
|
||||
static library is broken down to small files to separate each
|
||||
function individually (for example, look at
|
||||
https://git.musl-libc.org/cgit/musl/tree/src/stdio). By doing this,
|
||||
you import only used functions.
|
||||
|
||||
A static file is created by `ar`, whose command line arguments are
|
||||
similar to `tar`. A static library contains the symbol table which
|
||||
offers a quick way to look up an object file for a defined symbol,
|
||||
but mold does not use the static library's symbol table. mold
|
||||
doesdn't need a symbol table to exist in an archive, and if exists,
|
||||
mold just ignores it.
|
||||
|
||||
See also: DSO (dynamic library)
|
||||
|
||||
## Symbol
|
||||
|
||||
A symbol is a label assigned to a specific location in an input file
|
||||
or an output file. For example, if you define function `foo` and
|
||||
compile it, the resulting object file contains a symbol `foo`
|
||||
pointing to the beginning of the machine code for `foo`.
|
||||
|
||||
Usually, a symbol name is a function or a variable name. If an
|
||||
object is anonymous (such the one for a string literal), a compiler
|
||||
generated a unique symbol, which often starts with `.` to avoid
|
||||
conflict with user-defined symbols.
|
||||
|
||||
For C++, symbol name is a complex "mangled" name. We need to mangle
|
||||
identifiers because a simple name such as `foo` cannot be uniquely
|
||||
identify a function or a data in C++, because for example `foo` may
|
||||
be in a namespace or defined as a static member in some class. If
|
||||
`foo` is an overloaded function, we need to distinguish different
|
||||
`foo`s by its type. Therefore, C++ compiler mangles an identifier by
|
||||
appending nmaepsace names, type information and such so that
|
||||
different things get different names.
|
||||
|
||||
For example, a function `int foo(int)` in a namespace `bar` is
|
||||
mangled as `_ZN3bar3fooEi`.
|
||||
|
||||
A symbol can be either defined or undefined. A defined symbol points
|
||||
to some location in a file which may contain the function's machine
|
||||
code or the variable's initial value. An undefined symbol does not
|
||||
point to anywhere. It needs to be merged with a defined symbol with
|
||||
the same name at link-time. This merging process is called "name
|
||||
resolution".
|
||||
|
||||
For example, if your program is using `printf`, it usually contains
|
||||
`printf` as an undefined symbol. You need to link it with `libc.a`
|
||||
or `libc.so`, which contain a defined symbol of `printf`, to make a
|
||||
complete program.
|
||||
+62
-2
@@ -412,6 +412,18 @@ and
|
||||
Write map file to
|
||||
.Ar file .
|
||||
.Pp
|
||||
.It Fl -Tbss Ns = Ns Ar address
|
||||
Alias for
|
||||
.Fl -section-start=.bss Ns = Ns Ar address .
|
||||
.Pp
|
||||
.It Fl -Tdata Ns = Ns Ar address
|
||||
Alias for
|
||||
.Fl -section-start=.data Ns = Ns Ar address .
|
||||
.Pp
|
||||
.It Fl -Ttext Ns = Ns Ar address
|
||||
Alias for
|
||||
.Fl -section-start=.text Ns = Ns Ar address .
|
||||
.Pp
|
||||
.It Fl -allow-multiple-definition
|
||||
Normally, the linker reports an error if there are more than one \
|
||||
definition of a symbol.
|
||||
@@ -488,6 +500,18 @@ Use soname as a symbol version and append that version to all symbols.
|
||||
.It Fl -no-demangle
|
||||
Demangle C++ symbols in log messages.
|
||||
.Pp
|
||||
.It Fl -dependency-file Ns = Ns Ar file
|
||||
Write a dependency file to
|
||||
.Ar file .
|
||||
The contents of the written file is readable by
|
||||
.Cm make ,
|
||||
which defines only one rule with the linker's output file as a target \
|
||||
and all input fiels as its prerequisite. Users are expected to include \
|
||||
the generated dependency file into a Makefile to automate the \
|
||||
dependency management. This option is analogous to the compiler's
|
||||
.Fl MM Fl MF
|
||||
options.
|
||||
.Pp
|
||||
.It Fl -dynamic-list Ns = Ns Ar file
|
||||
Read a list of dynamic symbols from
|
||||
.Ar file .
|
||||
@@ -513,6 +537,19 @@ need them.
|
||||
always creates RELA-type relocation sections even if the native \
|
||||
ELF format is REL-type so that it is easy to read addends.
|
||||
.Pp
|
||||
.It Fl -enable-new-dtags
|
||||
.It Fl -disable-new-dtags
|
||||
By default,
|
||||
.Nm
|
||||
emits DT_RUNPATH for
|
||||
.Fl -rpath .
|
||||
If you pass
|
||||
.Fl -disable-new-dtags,
|
||||
mold emits DT_RPATH for
|
||||
.Fl -rpath
|
||||
instead.
|
||||
.Pp
|
||||
.Pp
|
||||
.It Fl -exclude-libs Ns = Ns Ar libraries Ns ...
|
||||
Mark all symbols in the given
|
||||
.Ar libraries
|
||||
@@ -557,6 +594,9 @@ at load-time.
|
||||
Report undefined symbols (even with
|
||||
.Fl -shared ) .
|
||||
.Pp
|
||||
.It Fl -noinhibit-exec
|
||||
Create an output file even if errors occur.
|
||||
.Pp
|
||||
.It Fl -pack-dyn-relocs Ns = Ns Op Sy none | relr
|
||||
If
|
||||
.Sy relr
|
||||
@@ -661,6 +701,10 @@ section and does not affect
|
||||
.Dv .dynsym
|
||||
section, which is used for dynamic linking.
|
||||
.Pp
|
||||
.It Fl -reverse-sections
|
||||
Reverses the order of input sections before assigning them the offsets \
|
||||
in the output file.
|
||||
.Pp
|
||||
.It Fl -rpath Ns = Ns Ar dir
|
||||
Add
|
||||
.Ar dir
|
||||
@@ -674,9 +718,27 @@ with
|
||||
as
|
||||
.Pa /usr/bin/ld .
|
||||
.Pp
|
||||
.It Fl -section-start Ns = Ns Ar section Ns = Ns Ar address
|
||||
Set
|
||||
.Ar address
|
||||
to
|
||||
.Ar section .
|
||||
.Ar address
|
||||
is a hexadecimal number that may start with an optional
|
||||
.Sq 0x .
|
||||
.Pp
|
||||
.It Fl -shared , -Bshareable
|
||||
Create a share library.
|
||||
.Pp
|
||||
.It Fl -shuffle-sections
|
||||
.It Fl -shuffle-sections Ns = Ns Ar number
|
||||
Randomizes the output by shuffleing the order of input sections before \
|
||||
assigning them the offsets in the output file. If
|
||||
.Ar number
|
||||
is given, it's used as a seed for the random number generator, so that \
|
||||
the linker produces the same output as for the same seed. If no seed \
|
||||
is given, it uses a random number as a seed.
|
||||
.Pp
|
||||
.It Fl -spare-dynamic-tags Ns = Ns Ar number
|
||||
Reserve given
|
||||
.Ar number
|
||||
@@ -984,9 +1046,7 @@ Mark object to interpose all DSOs but executable.
|
||||
.It Fl -allow-shlib-undefined
|
||||
.It Fl -color-diagnostics
|
||||
.It Fl -dc
|
||||
.It Fl -disable-new-dtags
|
||||
.It Fl -dp
|
||||
.It Fl -enable-new-dtags
|
||||
.It Fl -end-group
|
||||
.It Fl -fatal-warnings
|
||||
.It Fl -gdb-index
|
||||
|
||||
@@ -0,0 +1,600 @@
|
||||
// ARM32 is a bit special from the linker's viewpoint because ARM
|
||||
// processors support two different instruction encodings: Thumb and
|
||||
// ARM (in a narrower sense). Thumb instructions are either 16 bits or
|
||||
// 32 bits, while ARM instructions are all 32 bits. Feature-wise,
|
||||
// thumb is a subset of ARM, so not all ARM instructions are
|
||||
// representable in Thumb.
|
||||
//
|
||||
// ARM processors originally supported only ARM instructions. Thumb
|
||||
// instructions were later added to increase code density.
|
||||
//
|
||||
// ARM processors runs in either ARM mode or Thumb mode. The mode can
|
||||
// be switched using BX (branch and mode exchange)-family instructions.
|
||||
// We need to use such instructions to, for example, call a function
|
||||
// encoded in Thumb from a function encoded in ARM. Sometimes, the
|
||||
// linker even has to emit an interworking thunk code to switch from
|
||||
// Thumb to ARM.
|
||||
//
|
||||
// ARM instructions are aligned to 4 byte boundaries. Thumb are to 2
|
||||
// byte boundaries.
|
||||
//
|
||||
// You can distinguish Thumb functions from ARM functions by looking
|
||||
// at the least significant bit (LSB) of its "address". If LSB is 0,
|
||||
// it's ARM; otherwise, Thumb. LSB is not a part of its real address.
|
||||
// For example, if a symbol `foo` is of type STT_FUNC and has value
|
||||
// 0x2001, then `foo` is a function using Thumb instructions whose
|
||||
// address is 0x2000 (not 0x2001).
|
||||
|
||||
#include "mold.h"
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/parallel_for_each.h>
|
||||
#include <tbb/parallel_sort.h>
|
||||
|
||||
namespace mold::elf {
|
||||
|
||||
using E = ARM32;
|
||||
|
||||
static u32 bit(u32 val, i64 pos) {
|
||||
return (val >> pos) & 1;
|
||||
}
|
||||
|
||||
// Returns [hi:lo] bits of val.
|
||||
static u32 bits(u32 val, i64 hi, i64 lo) {
|
||||
return (val >> lo) & ((1LL << (hi - lo + 1)) - 1);
|
||||
}
|
||||
|
||||
static i32 sign_extend(u32 val, u32 size) {
|
||||
return (i64)(val << (31 - size)) >> (31 - size);
|
||||
};
|
||||
|
||||
static void write_mov_imm(u8 *loc, u32 val) {
|
||||
u32 imm12 = bits(val, 11, 0);
|
||||
u32 imm4 = bits(val, 15, 12);
|
||||
*(u32 *)loc = (*(u32 *)loc & 0xfff0f000) | (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);
|
||||
u32 I1 = bit(val, 23);
|
||||
u32 I2 = bit(val, 22);
|
||||
u32 J1 = !I1 ^ sign;
|
||||
u32 J2 = !I2 ^ sign;
|
||||
u32 imm10 = bits(val, 21, 12);
|
||||
u32 imm11 = bits(val, 11, 1);
|
||||
|
||||
*(u16 *)loc = (*(u16 *)loc & 0xf800) | (sign << 10) | imm10;
|
||||
*(u16 *)(loc + 2) =
|
||||
(*(u16 *)(loc + 2) & 0xd000) | (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
|
||||
u32 imm4 = bits(val, 15, 12);
|
||||
u32 i = bit(val, 11);
|
||||
u32 imm3 = bits(val, 10, 8);
|
||||
u32 imm8 = bits(val, 7, 0);
|
||||
*(u16 *)loc = (*(u16 *)loc & 0b1111'1011'1111'0000) | (i << 10) | imm4;
|
||||
*(u16 *)(loc + 2) =
|
||||
((*(u16 *)(loc + 2)) & 0b1000'1111'0000'0000) | (imm3 << 12) | imm8;
|
||||
}
|
||||
|
||||
template <>
|
||||
void PltSection<E>::copy_buf(Context<E> &ctx) {
|
||||
u8 *buf = ctx.buf + this->shdr.sh_offset;
|
||||
|
||||
static const u32 plt0[] = {
|
||||
0xe52de004, // 1: push {lr}
|
||||
0xe59fe004, // ldr lr, [pc, #4]
|
||||
0xe08fe00e, // add lr, pc, lr
|
||||
0xe5bef008, // ldr pc, [lr, #8]!
|
||||
0x00000000, // .word .got.plt - 1b - 16
|
||||
};
|
||||
|
||||
memcpy(buf, plt0, sizeof(plt0));
|
||||
*(u32 *)(buf + 16) = ctx.gotplt->shdr.sh_addr - this->shdr.sh_addr - 16;
|
||||
|
||||
for (Symbol<E> *sym : symbols) {
|
||||
static const u32 plt[] = {
|
||||
0xe59fc004, // 1: ldr ip, 2f
|
||||
0xe08cc00f, // add ip, ip, pc
|
||||
0xe59cf000, // ldr pc, [ip]
|
||||
0x00000000, // 2: .word sym@PLTGOT - 1b
|
||||
};
|
||||
|
||||
u8 *ent = buf + sizeof(plt0) + sym->get_plt_idx(ctx) * sizeof(plt);
|
||||
memcpy(ent, plt, sizeof(plt));
|
||||
*(u32 *)(ent + 12) = sym->get_gotplt_addr(ctx) - sym->get_plt_addr(ctx) - 12;
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void PltGotSection<E>::copy_buf(Context<E> &ctx) {
|
||||
u8 *buf = ctx.buf + this->shdr.sh_offset;
|
||||
|
||||
for (Symbol<E> *sym : symbols) {
|
||||
static const u32 plt[] = {
|
||||
0xe59fc004, // 1: ldr ip, 2f
|
||||
0xe08cc00f, // add ip, ip, pc
|
||||
0xe59cf000, // ldr pc, [ip]
|
||||
0x00000000, // 2: .word sym@GOT - 1b
|
||||
};
|
||||
|
||||
u8 *ent = buf + sym->get_pltgot_idx(ctx) * sizeof(plt);
|
||||
memcpy(ent, plt, sizeof(plt));
|
||||
*(u32 *)(ent + 12) = sym->get_got_addr(ctx) - sym->get_plt_addr(ctx) - 12;
|
||||
}
|
||||
}
|
||||
|
||||
// ARM does not use .eh_frame for exception handling. Instead, it uses
|
||||
// .ARM.exidx and .ARM.extab. So this function is empty.
|
||||
template <>
|
||||
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
|
||||
u64 offset, u64 val) {}
|
||||
|
||||
template <>
|
||||
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
ElfRel<E> *dynrel = nullptr;
|
||||
std::span<ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
i64 frag_idx = 0;
|
||||
|
||||
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_ARM_NONE)
|
||||
continue;
|
||||
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
u8 *loc = base + rel.r_offset;
|
||||
|
||||
const SectionFragmentRef<E> *frag_ref = nullptr;
|
||||
if (rel_fragments && rel_fragments[frag_idx].idx == i)
|
||||
frag_ref = &rel_fragments[frag_idx++];
|
||||
|
||||
#define S (frag_ref ? frag_ref->frag->get_addr(ctx) : sym.get_addr(ctx))
|
||||
#define A (frag_ref ? frag_ref->addend : this->get_addend(rel))
|
||||
#define P (output_section->shdr.sh_addr + offset + rel.r_offset)
|
||||
#define T (sym.get_addr(ctx) & 1)
|
||||
#define G (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
|
||||
#define GOT ctx.got->shdr.sh_addr
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_ARM_ABS32:
|
||||
case R_ARM_TARGET1:
|
||||
if (sym.is_absolute() || !ctx.arg.pic) {
|
||||
*(u32 *)loc = S + A;
|
||||
} else if (sym.is_imported) {
|
||||
*dynrel++ = {P, R_ARM_ABS32, (u32)sym.get_dynsym_idx(ctx)};
|
||||
} else {
|
||||
if (!is_relr_reloc(ctx, rel))
|
||||
*dynrel++ = {P, R_ARM_RELATIVE, 0};
|
||||
*(u32 *)loc = S + A;
|
||||
}
|
||||
continue;
|
||||
case R_ARM_REL32:
|
||||
*(u32 *)loc = S + A - P;
|
||||
continue;
|
||||
case R_ARM_THM_CALL:
|
||||
// THM_CALL relocation refers 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.
|
||||
if (sym.esym().is_undef_weak()) {
|
||||
// On ARM, calling an weak undefined symbol jumps to the
|
||||
// next instruction.
|
||||
write_thm_b_imm(loc, 4);
|
||||
*(u16 *)(loc + 2) |= (1 << 12); // rewrite with BL
|
||||
} else if (T) {
|
||||
write_thm_b_imm(loc, S + A - P);
|
||||
*(u16 *)(loc + 2) |= (1 << 12); // rewrite with BL
|
||||
} else {
|
||||
write_thm_b_imm(loc, align_to(S + A - P, 4));
|
||||
*(u16 *)(loc + 2) &= ~(1 << 12); // rewrite with BLX
|
||||
}
|
||||
continue;
|
||||
case R_ARM_BASE_PREL:
|
||||
*(u32 *)loc = GOT + A - P;
|
||||
continue;
|
||||
case R_ARM_GOT_PREL:
|
||||
*(u32 *)loc = G + A - P;
|
||||
continue;
|
||||
case R_ARM_GOT_BREL:
|
||||
*(u32 *)loc = G + A;
|
||||
continue;
|
||||
case R_ARM_TARGET2:
|
||||
*(u32 *)loc = GOT + G + A - P;
|
||||
continue;
|
||||
case R_ARM_CALL:
|
||||
case R_ARM_JUMP24: {
|
||||
u32 val;
|
||||
|
||||
if (sym.esym().is_undef_weak()) {
|
||||
// On ARM, calling an weak undefined symbol jumps to the
|
||||
// next instruction.
|
||||
val = 4;
|
||||
} else {
|
||||
val = S + A - P;
|
||||
}
|
||||
|
||||
*(u32 *)loc = (*(u32 *)loc & 0xff00'0000) | ((val >> 2) & 0x00ff'ffff);
|
||||
continue;
|
||||
}
|
||||
case R_ARM_THM_JUMP11: {
|
||||
assert(T);
|
||||
u32 val = (S + A - P) >> 1;
|
||||
*(u16 *)loc = (*(u16 *)loc & 0xf800) | (val & 0x07ff);
|
||||
continue;
|
||||
}
|
||||
case R_ARM_THM_JUMP24:
|
||||
if (T) {
|
||||
write_thm_b_imm(loc, S + A - P);
|
||||
} else {
|
||||
assert(sym.extra.thumb_to_arm_thunk_idx != -1);
|
||||
u64 thunk_addr =
|
||||
ctx.thumb_to_arm->shdr.sh_addr +
|
||||
sym.extra.thumb_to_arm_thunk_idx * ThumbToArmSection::ENTRY_SIZE;
|
||||
write_thm_b_imm(loc, thunk_addr - P - 4);
|
||||
}
|
||||
continue;
|
||||
case R_ARM_MOVW_PREL_NC:
|
||||
write_mov_imm(loc, ((S + A) | T) - P);
|
||||
continue;
|
||||
case R_ARM_MOVW_ABS_NC:
|
||||
write_mov_imm(loc, (S + A) | T);
|
||||
continue;
|
||||
case R_ARM_THM_MOVW_PREL_NC:
|
||||
write_thm_mov_imm(loc, ((S + A) | T) - P);
|
||||
continue;
|
||||
case R_ARM_PREL31: {
|
||||
u32 val = S + A - P;
|
||||
*(u32 *)loc = (*(u32 *)loc & 0x8000'0000) | (val & 0x7fff'ffff);
|
||||
continue;
|
||||
}
|
||||
case R_ARM_THM_MOVW_ABS_NC:
|
||||
write_thm_mov_imm(loc, (S + A) | T);
|
||||
continue;
|
||||
case R_ARM_MOVT_PREL:
|
||||
write_mov_imm(loc, (S + A - P) >> 16);
|
||||
continue;
|
||||
case R_ARM_THM_MOVT_PREL:
|
||||
write_thm_mov_imm(loc, (S + A - P) >> 16);
|
||||
continue;
|
||||
case R_ARM_MOVT_ABS:
|
||||
write_mov_imm(loc, (S + A) >> 16);
|
||||
continue;
|
||||
case R_ARM_THM_MOVT_ABS:
|
||||
write_thm_mov_imm(loc, (S + A) >> 16);
|
||||
continue;
|
||||
case R_ARM_TLS_GD32:
|
||||
*(u32 *)loc = sym.get_tlsgd_addr(ctx) + A - P;
|
||||
continue;
|
||||
case R_ARM_TLS_LDM32:
|
||||
*(u32 *)loc = ctx.got->get_tlsld_addr(ctx) + A - P;
|
||||
continue;
|
||||
case R_ARM_TLS_LDO32:
|
||||
*(u32 *)loc = S + A - ctx.tls_begin;
|
||||
continue;
|
||||
case R_ARM_TLS_IE32:
|
||||
*(u32 *)loc = sym.get_gottp_addr(ctx) + A - P;
|
||||
continue;
|
||||
case R_ARM_TLS_LE32:
|
||||
*(u32 *)loc = S + A - ctx.tls_begin + 8;
|
||||
continue;
|
||||
case R_ARM_TLS_GOTDESC:
|
||||
if (sym.get_tlsdesc_idx(ctx) == -1)
|
||||
*(u32 *)loc = S - ctx.tls_begin + 8;
|
||||
else
|
||||
*(u32 *)loc = sym.get_tlsdesc_addr(ctx) + A - P - 6;
|
||||
continue;
|
||||
case R_ARM_THM_TLS_CALL:
|
||||
if (sym.get_tlsdesc_idx(ctx) == -1) {
|
||||
// BL -> NOP
|
||||
*(u32 *)loc = 0x8000f3af;
|
||||
} else {
|
||||
u64 addr = ctx.tls_trampoline->shdr.sh_addr;
|
||||
write_thm_b_imm(loc, align_to(addr - P - 4, 4));
|
||||
*(u16 *)(loc + 2) &= ~(1 << 12); // rewrite BL with BLX
|
||||
}
|
||||
continue;
|
||||
default:
|
||||
Error(ctx) << *this << ": unknown relocation: " << rel;
|
||||
}
|
||||
|
||||
#undef S
|
||||
#undef A
|
||||
#undef P
|
||||
#undef T
|
||||
#undef G
|
||||
#undef GOT
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
std::span<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_ARM_NONE)
|
||||
continue;
|
||||
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
u8 *loc = base + rel.r_offset;
|
||||
|
||||
if (!sym.file) {
|
||||
report_undef(ctx, file, sym);
|
||||
continue;
|
||||
}
|
||||
|
||||
SectionFragment<E> *frag;
|
||||
i64 addend;
|
||||
std::tie(frag, addend) = get_fragment(ctx, rel);
|
||||
|
||||
#define S (frag ? frag->get_addr(ctx) : sym.get_addr(ctx))
|
||||
#define A (frag ? addend : this->get_addend(rel))
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_ARM_ABS32:
|
||||
if (std::optional<u64> val = get_tombstone(sym))
|
||||
*(u32 *)loc = *val;
|
||||
else
|
||||
*(u32 *)loc = S + A;
|
||||
break;
|
||||
case R_ARM_TLS_LDO32:
|
||||
if (std::optional<u64> val = get_tombstone(sym))
|
||||
*(u32 *)loc = *val;
|
||||
else
|
||||
*(u32 *)loc = S + A - ctx.tls_begin;
|
||||
break;
|
||||
default:
|
||||
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
|
||||
<< rel;
|
||||
break;
|
||||
}
|
||||
|
||||
#undef S
|
||||
#undef A
|
||||
}
|
||||
}
|
||||
|
||||
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<ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
// Scan relocations
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
if (rel.r_type == R_ARM_NONE)
|
||||
continue;
|
||||
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
|
||||
if (!sym.file) {
|
||||
report_undef(ctx, file, sym);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sym.get_type() == STT_GNU_IFUNC) {
|
||||
sym.flags |= NEEDS_GOT;
|
||||
sym.flags |= 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: {
|
||||
Action table[][4] = {
|
||||
// Absolute Local Imported data Imported code
|
||||
{ NONE, BASEREL, DYNREL, DYNREL }, // DSO
|
||||
{ NONE, BASEREL, DYNREL, DYNREL }, // PIE
|
||||
{ NONE, NONE, COPYREL, CPLT }, // PDE
|
||||
};
|
||||
dispatch(ctx, table, i, rel, sym);
|
||||
break;
|
||||
}
|
||||
case R_ARM_REL32:
|
||||
case R_ARM_BASE_PREL:
|
||||
break;
|
||||
case R_ARM_THM_CALL: {
|
||||
Action table[][4] = {
|
||||
// Absolute Local Imported data Imported code
|
||||
{ NONE, NONE, PLT, PLT }, // DSO
|
||||
{ NONE, NONE, PLT, PLT }, // PIE
|
||||
{ NONE, NONE, PLT, PLT }, // PDE
|
||||
};
|
||||
dispatch(ctx, table, i, rel, sym);
|
||||
break;
|
||||
}
|
||||
case R_ARM_GOT_PREL:
|
||||
case R_ARM_GOT_BREL:
|
||||
case R_ARM_TARGET2:
|
||||
sym.flags |= NEEDS_GOT;
|
||||
break;
|
||||
case R_ARM_CALL:
|
||||
case R_ARM_JUMP24:
|
||||
if (sym.is_imported)
|
||||
sym.flags |= NEEDS_PLT;
|
||||
break;
|
||||
case R_ARM_THM_JUMP24:
|
||||
if (sym.is_imported || sym.get_type() == STT_GNU_IFUNC)
|
||||
sym.flags |= NEEDS_PLT | NEEDS_THUMB_TO_ARM_THUNK;
|
||||
else if (sym.esym().st_value % 2 == 0)
|
||||
sym.flags |= NEEDS_THUMB_TO_ARM_THUNK;
|
||||
break;
|
||||
case R_ARM_MOVT_PREL:
|
||||
case R_ARM_THM_MOVT_PREL:
|
||||
case R_ARM_PREL31: {
|
||||
Action table[][4] = {
|
||||
// Absolute Local Imported data Imported code
|
||||
{ ERROR, NONE, ERROR, ERROR }, // DSO
|
||||
{ ERROR, NONE, COPYREL, PLT }, // PIE
|
||||
{ NONE, NONE, COPYREL, PLT }, // PDE
|
||||
};
|
||||
dispatch(ctx, table, i, rel, sym);
|
||||
break;
|
||||
}
|
||||
case R_ARM_TLS_GD32:
|
||||
sym.flags |= NEEDS_TLSGD;
|
||||
break;
|
||||
case R_ARM_TLS_LDM32:
|
||||
ctx.needs_tlsld = true;
|
||||
break;
|
||||
case R_ARM_TLS_IE32:
|
||||
sym.flags |= NEEDS_GOTTP;
|
||||
break;
|
||||
case R_ARM_TLS_GOTDESC:
|
||||
if (!ctx.relax_tlsdesc || sym.is_imported)
|
||||
sym.flags |= NEEDS_TLSDESC;
|
||||
break;
|
||||
case R_ARM_THM_JUMP11:
|
||||
case R_ARM_MOVW_PREL_NC:
|
||||
case R_ARM_MOVW_ABS_NC:
|
||||
case R_ARM_THM_MOVW_PREL_NC:
|
||||
case R_ARM_THM_MOVW_ABS_NC:
|
||||
case R_ARM_TLS_LDO32:
|
||||
case R_ARM_TLS_LE32:
|
||||
case R_ARM_THM_TLS_CALL:
|
||||
break;
|
||||
default:
|
||||
Error(ctx) << *this << ": unknown relocation: " << rel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If a function referenced by a Thumb B (branch) instruction is
|
||||
// resovled to a non-thumb function, we can't directly jump from the
|
||||
// thumb function to the ARM function. We can't rewrite B with BX
|
||||
// because there's no such BX instruction that takes an immediate.
|
||||
// BX takes only a register.
|
||||
//
|
||||
// In order to support such branch, we insert a small piece of code to
|
||||
// the resulting executable which switches the processor mode from
|
||||
// Thumb to ARM. This section contains such code.
|
||||
void ThumbToArmSection::add_symbol(Context<E> &ctx, Symbol<E> *sym) {
|
||||
if (sym->extra.thumb_to_arm_thunk_idx == -1) {
|
||||
sym->extra.thumb_to_arm_thunk_idx = symbols.size();
|
||||
symbols.push_back(sym);
|
||||
}
|
||||
}
|
||||
|
||||
void ThumbToArmSection::update_shdr(Context<E> &ctx) {
|
||||
this->shdr.sh_size = symbols.size() * ENTRY_SIZE;
|
||||
}
|
||||
|
||||
void ThumbToArmSection::copy_buf(Context<E> &ctx) {
|
||||
u8 *buf = ctx.buf + this->shdr.sh_offset;
|
||||
i64 offset = 0;
|
||||
|
||||
static u16 insn[] = {
|
||||
0x4778, // bx pc
|
||||
0x46c0, // nop
|
||||
0, 0, // b <imm24>
|
||||
};
|
||||
|
||||
static_assert(sizeof(insn) == ENTRY_SIZE);
|
||||
|
||||
for (Symbol<E> *sym : symbols) {
|
||||
memcpy(buf + offset, insn, sizeof(insn));
|
||||
|
||||
u32 val = sym->get_addr(ctx) - this->shdr.sh_addr - offset - 12;
|
||||
*(u32 *)(buf + offset + 4) = 0xea00'0000 | (0x00ff'ffff & (val >> 2));
|
||||
offset += sizeof(insn);
|
||||
}
|
||||
}
|
||||
|
||||
void TlsTrampolineSection::copy_buf(Context<E> &ctx) {
|
||||
// Trampoline code for TLSDESC
|
||||
static u32 insn[] = {
|
||||
0xe08e0000, // add r0, lr, r0
|
||||
0xe5901004, // ldr r1, [r0, #4]
|
||||
0xe12fff11, // bx r1
|
||||
};
|
||||
memcpy(ctx.buf + this->shdr.sh_offset, insn, sizeof(insn));
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Other target uses .eh_frame_hdr instead for the same purpose.
|
||||
// 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 sort_arm_exidx(Context<E> &ctx) {
|
||||
Timer t(ctx, "sort_arm_exidx");
|
||||
|
||||
auto find_exidx = [&]() -> OutputSection<E> * {
|
||||
for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections)
|
||||
if (osec->shdr.sh_type == SHT_ARM_EXIDX)
|
||||
return osec.get();
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
OutputSection<E> *osec = find_exidx();
|
||||
if (!osec)
|
||||
return;
|
||||
|
||||
// .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
|
||||
// the followings:
|
||||
//
|
||||
// 1. CANTUNWIND indicating that there's no unwinding info for the function,
|
||||
// 2. a compact unwinding record encoded into a 32-bit value, or
|
||||
// 3. a 31-bit relative address which points to a larger record in
|
||||
// the .ARM.extab section.
|
||||
//
|
||||
// CANTUNWIND is value 1. The most significant is set in (2) but not
|
||||
// in (3). So they can be distinguished just by looking at a value.
|
||||
struct Entry {
|
||||
u32 addr;
|
||||
u32 val;
|
||||
};
|
||||
|
||||
if (osec->shdr.sh_size % sizeof(Entry))
|
||||
Fatal(ctx) << "invalid .ARM.exidx section size";
|
||||
|
||||
Entry *begin = (Entry *)(ctx.buf + osec->shdr.sh_offset);
|
||||
Entry *end = (Entry *)(ctx.buf + osec->shdr.sh_offset + osec->shdr.sh_size);
|
||||
|
||||
struct Entry2 {
|
||||
u32 addr;
|
||||
u32 val;
|
||||
u32 idx;
|
||||
};
|
||||
|
||||
// Read section contents
|
||||
std::vector<Entry2> vec;
|
||||
vec.reserve(end - begin);
|
||||
for (Entry *it = begin; it < end; it++)
|
||||
vec.push_back({it->addr, it->val, (u32)(it - begin)});
|
||||
|
||||
// Sort the records
|
||||
tbb::parallel_sort(vec.begin(), vec.end(), [](const Entry2 &a, const Entry2 &b) {
|
||||
return sign_extend(a.addr, 30) + a.idx * sizeof(Entry) <
|
||||
sign_extend(b.addr, 30) + b.idx * sizeof(Entry);
|
||||
});
|
||||
|
||||
// Write back the sorted records while adjusting relative addresses
|
||||
for (i64 i = 0; i < vec.size(); i++) {
|
||||
u32 offset = (vec[i].idx - i) * sizeof(Entry);
|
||||
begin[i].addr = 0x7fff'ffff & (sign_extend(vec[i].addr, 30) + offset);
|
||||
|
||||
const u32 EXIDX_CANTUNWIND = 1;
|
||||
if (vec[i].val == EXIDX_CANTUNWIND || (vec[i].val & 0x8000'0000))
|
||||
begin[i].val = vec[i].val;
|
||||
else
|
||||
begin[i].val = 0x7fff'ffff & (sign_extend(vec[i].val, 30) + offset);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mold::elf
|
||||
+37
-22
@@ -150,18 +150,18 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
#define G (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
|
||||
#define GOT ctx.got->shdr.sh_addr
|
||||
|
||||
if (needs_dynrel[i]) {
|
||||
*dynrel++ = {P, R_AARCH64_ABS64, (u32)sym.get_dynsym_idx(ctx), A};
|
||||
*(u64 *)loc = A;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (needs_baserel[i] && !is_relr_reloc(ctx, rel))
|
||||
*dynrel++ = {P, R_AARCH64_RELATIVE, 0, (i64)(S + A)};
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_AARCH64_ABS64:
|
||||
*(u64 *)loc = S + A;
|
||||
if (sym.is_absolute() || !ctx.arg.pic) {
|
||||
*(u64 *)loc = S + A;
|
||||
} else if (sym.is_imported) {
|
||||
*dynrel++ = {P, R_AARCH64_ABS64, (u32)sym.get_dynsym_idx(ctx), A};
|
||||
*(u64 *)loc = A;
|
||||
} else {
|
||||
if (!is_relr_reloc(ctx, rel))
|
||||
*dynrel++ = {P, R_AARCH64_RELATIVE, 0, (i64)(S + A)};
|
||||
*(u64 *)loc = S + A;
|
||||
}
|
||||
continue;
|
||||
case R_AARCH64_LDST8_ABS_LO12_NC:
|
||||
*(u32 *)loc |= bits(S + A, 11, 0) << 10;
|
||||
@@ -181,12 +181,15 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
case R_AARCH64_ADD_ABS_LO12_NC:
|
||||
*(u32 *)loc |= bits(S + A, 11, 0) << 10;
|
||||
continue;
|
||||
case R_AARCH64_MOVW_UABS_G0:
|
||||
case R_AARCH64_MOVW_UABS_G0_NC:
|
||||
*(u32 *)loc |= bits(S + A, 15, 0) << 5;
|
||||
continue;
|
||||
case R_AARCH64_MOVW_UABS_G1:
|
||||
case R_AARCH64_MOVW_UABS_G1_NC:
|
||||
*(u32 *)loc |= bits(S + A, 31, 16) << 5;
|
||||
continue;
|
||||
case R_AARCH64_MOVW_UABS_G2:
|
||||
case R_AARCH64_MOVW_UABS_G2_NC:
|
||||
*(u32 *)loc |= bits(S + A, 47, 32) << 5;
|
||||
continue;
|
||||
@@ -233,7 +236,8 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
*(u32 *)loc |= (val >> 2) & 0x3ffffff;
|
||||
continue;
|
||||
}
|
||||
case R_AARCH64_CONDBR19: {
|
||||
case R_AARCH64_CONDBR19:
|
||||
case R_AARCH64_LD_PREL_LO19: {
|
||||
i64 val = S + A - P;
|
||||
overflow_check(val, -((i64)1 << 20), (i64)1 << 20);
|
||||
*(u32 *)loc |= bits(val, 20, 2) << 5;
|
||||
@@ -278,6 +282,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
*(u32 *)loc |= bits(val, 23, 12) << 10;
|
||||
continue;
|
||||
}
|
||||
case R_AARCH64_TLSLE_ADD_TPREL_LO12:
|
||||
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
|
||||
*(u32 *)loc |= bits(S + A - ctx.tls_begin + 16, 11, 0) << 10;
|
||||
continue;
|
||||
@@ -364,7 +369,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_AARCH64_ABS64:
|
||||
*(u64 *)loc = S + A;
|
||||
if (std::optional<u64> val = get_tombstone(sym))
|
||||
*(u64 *)loc = *val;
|
||||
else
|
||||
*(u64 *)loc = S + A;
|
||||
continue;
|
||||
case R_AARCH64_ABS32:
|
||||
*(u32 *)loc = S + A;
|
||||
@@ -411,7 +419,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
// Absolute Local Imported data Imported code
|
||||
{ NONE, BASEREL, DYNREL, DYNREL }, // DSO
|
||||
{ NONE, BASEREL, DYNREL, DYNREL }, // PIE
|
||||
{ NONE, NONE, COPYREL, PLT }, // PDE
|
||||
{ NONE, NONE, COPYREL, CPLT }, // PDE
|
||||
};
|
||||
dispatch(ctx, table, i, rel, sym);
|
||||
break;
|
||||
@@ -433,9 +441,9 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_AARCH64_ADR_PREL_PG_HI21: {
|
||||
Action table[][4] = {
|
||||
// Absolute Local Imported data Imported code
|
||||
{ NONE, NONE, ERROR, ERROR }, // DSO
|
||||
{ NONE, NONE, COPYREL, PLT }, // PIE
|
||||
{ NONE, NONE, COPYREL, PLT }, // PDE
|
||||
{ ERROR, NONE, ERROR, ERROR }, // DSO
|
||||
{ ERROR, NONE, COPYREL, PLT }, // PIE
|
||||
{ NONE, NONE, COPYREL, CPLT }, // PDE
|
||||
};
|
||||
dispatch(ctx, table, i, rel, sym);
|
||||
break;
|
||||
@@ -452,19 +460,24 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_AARCH64_ADD_ABS_LO12_NC:
|
||||
case R_AARCH64_ADR_PREL_LO21:
|
||||
case R_AARCH64_CONDBR19:
|
||||
case R_AARCH64_LD_PREL_LO19:
|
||||
case R_AARCH64_LDST16_ABS_LO12_NC:
|
||||
case R_AARCH64_LDST32_ABS_LO12_NC:
|
||||
case R_AARCH64_LDST64_ABS_LO12_NC:
|
||||
case R_AARCH64_LDST128_ABS_LO12_NC:
|
||||
case R_AARCH64_LDST8_ABS_LO12_NC:
|
||||
case R_AARCH64_MOVW_UABS_G0:
|
||||
case R_AARCH64_MOVW_UABS_G0_NC:
|
||||
case R_AARCH64_MOVW_UABS_G1:
|
||||
case R_AARCH64_MOVW_UABS_G1_NC:
|
||||
case R_AARCH64_MOVW_UABS_G2:
|
||||
case R_AARCH64_MOVW_UABS_G2_NC:
|
||||
case R_AARCH64_MOVW_UABS_G3:
|
||||
case R_AARCH64_PREL16:
|
||||
case R_AARCH64_PREL32:
|
||||
case R_AARCH64_PREL64:
|
||||
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
|
||||
case R_AARCH64_TLSLE_ADD_TPREL_LO12:
|
||||
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
|
||||
case R_AARCH64_TLSGD_ADD_LO12_NC:
|
||||
case R_AARCH64_TLSDESC_CALL:
|
||||
@@ -479,7 +492,7 @@ static void reset_thunk(RangeExtensionThunk<E> &thunk) {
|
||||
for (Symbol<E> *sym : thunk.symbols) {
|
||||
sym->extra.thunk_idx = -1;
|
||||
sym->extra.thunk_sym_idx = -1;
|
||||
sym->flags &= (u8)~NEEDS_THUNK;
|
||||
sym->flags &= (u8)~NEEDS_RANGE_EXTN_THUNK;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -502,7 +515,7 @@ static bool is_reachable(Context<E> &ctx, Symbol<E> &sym,
|
||||
}
|
||||
|
||||
// We create a thunk no further than 100 MiB from any section.
|
||||
constexpr i64 MAX_DISTANCE = 100 * 1024 * 1024;
|
||||
static constexpr i64 MAX_DISTANCE = 100 * 1024 * 1024;
|
||||
|
||||
// We create a thunk for each 10 MiB input sections.
|
||||
static constexpr i64 GROUP_SIZE = 10 * 1024 * 1024;
|
||||
@@ -583,7 +596,8 @@ static void create_thunks(Context<E> &ctx, OutputSection<E> &osec) {
|
||||
// Otherwise, add the symbol to this thunk if it's not added already.
|
||||
range_extn[i] = {thunk.thunk_idx, -1};
|
||||
|
||||
if (!(sym.flags.fetch_or(NEEDS_THUNK) & NEEDS_THUNK)) {
|
||||
if (!(sym.flags.fetch_or(NEEDS_RANGE_EXTN_THUNK) &
|
||||
NEEDS_RANGE_EXTN_THUNK)) {
|
||||
std::scoped_lock lock(thunk.mu);
|
||||
thunk.symbols.push_back(&sym);
|
||||
}
|
||||
@@ -627,7 +641,7 @@ static void create_thunks(Context<E> &ctx, OutputSection<E> &osec) {
|
||||
osec.shdr.sh_size = offset;
|
||||
}
|
||||
|
||||
static void mark_thunk_symbols(Context<E> &ctx, OutputSection<E> &osec) {
|
||||
static void gc_thunk_symbols(Context<E> &ctx, OutputSection<E> &osec) {
|
||||
for (std::unique_ptr<RangeExtensionThunk<E>> &thunk : osec.thunks) {
|
||||
i64 sz = thunk->symbols.size();
|
||||
thunk->symbol_map.resize(sz);
|
||||
@@ -716,7 +730,8 @@ i64 create_range_extension_thunks(Context<E> &ctx) {
|
||||
// with an initial layout in which output sections are separated far
|
||||
// apart.
|
||||
for (i64 i = 0; Chunk<E> *chunk : ctx.chunks)
|
||||
chunk->shdr.sh_addr = i++ << 31;
|
||||
if (chunk->shdr.sh_flags & SHF_ALLOC)
|
||||
chunk->shdr.sh_addr = i++ << 31;
|
||||
|
||||
std::vector<OutputSection<E> *> sections;
|
||||
for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections)
|
||||
@@ -732,7 +747,7 @@ i64 create_range_extension_thunks(Context<E> &ctx) {
|
||||
// Based on the current file layout, remove thunk symbols that turned
|
||||
// out to be unnecessary.
|
||||
tbb::parallel_for_each(sections, [&](OutputSection<E> *osec) {
|
||||
mark_thunk_symbols(ctx, *osec);
|
||||
gc_thunk_symbols(ctx, *osec);
|
||||
});
|
||||
|
||||
// Recompute output section sizes that contain thunks. New section
|
||||
|
||||
+138
-44
@@ -7,15 +7,17 @@ using E = I386;
|
||||
static void write_plt_header(Context<E> &ctx, u8 *buf) {
|
||||
if (ctx.arg.pic) {
|
||||
static const u8 plt0[] = {
|
||||
0xff, 0xb3, 0x04, 0, 0, 0, // pushl 4(%ebx)
|
||||
0xff, 0xa3, 0x08, 0, 0, 0, // jmp *8(%ebx)
|
||||
0x90, 0x90, 0x90, 0x90, // nop
|
||||
0xff, 0xb3, 0, 0, 0, 0, // pushl GOTPLT+4(%ebx)
|
||||
0xff, 0xa3, 0, 0, 0, 0, // jmp *GOTPLT+8(%ebx)
|
||||
0x90, 0x90, 0x90, 0x90, // nop
|
||||
};
|
||||
memcpy(buf, plt0, sizeof(plt0));
|
||||
*(u32 *)(buf + 2) = ctx.gotplt->shdr.sh_addr - ctx.got->shdr.sh_addr + 4;
|
||||
*(u32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr - ctx.got->shdr.sh_addr + 8;
|
||||
} else {
|
||||
static const u8 plt0[] = {
|
||||
0xff, 0x35, 0, 0, 0, 0, // pushl (GOTPLT+4)
|
||||
0xff, 0x25, 0, 0, 0, 0, // jmp *(GOTPLT+8)
|
||||
0xff, 0x35, 0, 0, 0, 0, // pushl GOTPLT+4
|
||||
0xff, 0x25, 0, 0, 0, 0, // jmp *GOTPLT+8
|
||||
0x90, 0x90, 0x90, 0x90, // nop
|
||||
};
|
||||
memcpy(buf, plt0, sizeof(plt0));
|
||||
@@ -35,7 +37,7 @@ static void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym,
|
||||
0xe9, 0, 0, 0, 0, // jmp .PLT0@PC
|
||||
};
|
||||
memcpy(ent, data, sizeof(data));
|
||||
*(u32 *)(ent + 2) = sym.get_gotplt_addr(ctx) - ctx.gotplt->shdr.sh_addr;
|
||||
*(u32 *)(ent + 2) = sym.get_gotplt_addr(ctx) - ctx.got->shdr.sh_addr;
|
||||
} else {
|
||||
static const u8 data[] = {
|
||||
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
|
||||
@@ -72,7 +74,7 @@ void PltGotSection<E>::copy_buf(Context<E> &ctx) {
|
||||
for (i64 i = 0; i < symbols.size(); i++) {
|
||||
u8 *ent = buf + i * sizeof(data);
|
||||
memcpy(ent, data, sizeof(data));
|
||||
*(u32 *)(ent + 2) = symbols[i]->get_got_addr(ctx) - ctx.gotplt->shdr.sh_addr;
|
||||
*(u32 *)(ent + 2) = symbols[i]->get_got_addr(ctx) - ctx.got->shdr.sh_addr;
|
||||
}
|
||||
} else {
|
||||
static const u8 data[] = {
|
||||
@@ -159,20 +161,11 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
*(u32 *)loc = val;
|
||||
};
|
||||
|
||||
#define S (frag_ref ? frag_ref->frag->get_addr(ctx) : sym.get_addr(ctx))
|
||||
#define A (frag_ref ? frag_ref->addend : this->get_addend(rel))
|
||||
#define P (output_section->shdr.sh_addr + offset + rel.r_offset)
|
||||
#define G (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
|
||||
#define GOTPLT ctx.gotplt->shdr.sh_addr
|
||||
|
||||
if (needs_dynrel[i]) {
|
||||
*dynrel++ = {P, R_386_32, (u32)sym.get_dynsym_idx(ctx)};
|
||||
write32(A);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (needs_baserel[i] && !is_relr_reloc(ctx, rel))
|
||||
*dynrel++ = {P, R_386_RELATIVE, 0};
|
||||
#define S (frag_ref ? frag_ref->frag->get_addr(ctx) : sym.get_addr(ctx))
|
||||
#define A (frag_ref ? frag_ref->addend : this->get_addend(rel))
|
||||
#define P (output_section->shdr.sh_addr + offset + rel.r_offset)
|
||||
#define G (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
|
||||
#define GOT ctx.got->shdr.sh_addr
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_386_8:
|
||||
@@ -182,7 +175,16 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
write16(S + A);
|
||||
continue;
|
||||
case R_386_32:
|
||||
write32(S + A);
|
||||
if (sym.is_absolute() || !ctx.arg.pic) {
|
||||
write32(S + A);
|
||||
} else if (sym.is_imported) {
|
||||
*dynrel++ = {P, R_386_32, (u32)sym.get_dynsym_idx(ctx)};
|
||||
write32(A);
|
||||
} else {
|
||||
if (!is_relr_reloc(ctx, rel))
|
||||
*dynrel++ = {P, R_386_RELATIVE, 0};
|
||||
write32(S + A);
|
||||
}
|
||||
continue;
|
||||
case R_386_PC8:
|
||||
write8s(S + A);
|
||||
@@ -191,21 +193,28 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
write16s(S + A);
|
||||
continue;
|
||||
case R_386_PC32:
|
||||
if (sym.is_absolute() || !sym.is_imported || !ctx.arg.shared) {
|
||||
write32(S + A - P);
|
||||
} else {
|
||||
*dynrel++ = {P, R_386_32, (u32)sym.get_dynsym_idx(ctx)};
|
||||
write32(A);
|
||||
}
|
||||
continue;
|
||||
case R_386_PLT32:
|
||||
write32(S + A - P);
|
||||
continue;
|
||||
case R_386_GOT32:
|
||||
case R_386_GOT32X:
|
||||
write32(sym.get_got_addr(ctx) + A - GOTPLT);
|
||||
write32(G + A);
|
||||
continue;
|
||||
case R_386_GOTOFF:
|
||||
write32(S + A - GOTPLT);
|
||||
write32(S + A - GOT);
|
||||
continue;
|
||||
case R_386_GOTPC:
|
||||
write32(GOTPLT + A - P);
|
||||
write32(GOT + A - P);
|
||||
continue;
|
||||
case R_386_TLS_GOTIE:
|
||||
write32(sym.get_gottp_addr(ctx) + A - GOTPLT);
|
||||
write32(sym.get_gottp_addr(ctx) + A - GOT);
|
||||
continue;
|
||||
case R_386_TLS_LE:
|
||||
write32(S + A - ctx.tls_end);
|
||||
@@ -214,13 +223,72 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
write32(sym.get_gottp_addr(ctx) + A);
|
||||
continue;
|
||||
case R_386_TLS_GD:
|
||||
write32(sym.get_tlsgd_addr(ctx) + A - GOTPLT);
|
||||
if (sym.get_tlsgd_idx(ctx) == -1) {
|
||||
// Relax GD to LE
|
||||
switch (rels[i + 1].r_type) {
|
||||
case R_386_PLT32: {
|
||||
static const u8 insn[] = {
|
||||
0x65, 0xa1, 0, 0, 0, 0, // mov %gs:0, %rax
|
||||
0x81, 0xe8, 0, 0, 0, 0, // add $0, %rax
|
||||
};
|
||||
memcpy(loc - 3, insn, sizeof(insn));
|
||||
*(u32 *)(loc + 5) = ctx.tls_end - S - A;
|
||||
break;
|
||||
}
|
||||
case R_386_GOT32: {
|
||||
static const u8 insn[] = {
|
||||
0x65, 0xa1, 0, 0, 0, 0, // mov %gs:0, %rax
|
||||
0x81, 0xe8, 0, 0, 0, 0, // add $0, %rax
|
||||
};
|
||||
memcpy(loc - 2, insn, sizeof(insn));
|
||||
*(u32 *)(loc + 6) = ctx.tls_end - S - A;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
|
||||
i++;
|
||||
} else {
|
||||
write32(sym.get_tlsgd_addr(ctx) + A - GOT);
|
||||
}
|
||||
continue;
|
||||
case R_386_TLS_LDM:
|
||||
write32(ctx.got->get_tlsld_addr(ctx) + A - GOTPLT);
|
||||
if (ctx.got->tlsld_idx == -1) {
|
||||
// Relax LD to LE
|
||||
switch (rels[i + 1].r_type) {
|
||||
case R_386_PLT32: {
|
||||
static const u8 insn[] = {
|
||||
0x65, 0xa1, 0, 0, 0, 0, // mov %gs:0, %eax
|
||||
0x8d, 0x74, 0x26, 0x00, // lea (%esi,1), %esi
|
||||
0x90, // nop
|
||||
};
|
||||
memcpy(loc - 2, insn, sizeof(insn));
|
||||
break;
|
||||
}
|
||||
case R_386_GOT32: {
|
||||
static const u8 insn[] = {
|
||||
0x65, 0xa1, 0, 0, 0, 0, // mov %gs:0, %eax
|
||||
0x8d, 0x74, 0x26, 0x00, // lea (%esi,1), %esi
|
||||
0x66, 0x90,
|
||||
};
|
||||
memcpy(loc - 2, insn, sizeof(insn));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
|
||||
i++;
|
||||
} else {
|
||||
write32(ctx.got->get_tlsld_addr(ctx) + A - GOT);
|
||||
}
|
||||
continue;
|
||||
case R_386_TLS_LDO_32:
|
||||
write32(S + A - ctx.tls_begin);
|
||||
if (ctx.got->tlsld_idx == -1)
|
||||
write32(S + A - ctx.tls_end);
|
||||
else
|
||||
write32(S + A - ctx.tls_begin);
|
||||
continue;
|
||||
case R_386_SIZE32:
|
||||
write32(sym.esym().st_size + A);
|
||||
@@ -233,7 +301,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
memcpy(loc - 2, insn, sizeof(insn));
|
||||
write32(S + A - ctx.tls_end);
|
||||
} else {
|
||||
write32(sym.get_tlsdesc_addr(ctx) + A - GOTPLT);
|
||||
write32(sym.get_tlsdesc_addr(ctx) + A - GOT);
|
||||
}
|
||||
continue;
|
||||
case R_386_TLS_DESC_CALL:
|
||||
@@ -251,7 +319,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
#undef A
|
||||
#undef P
|
||||
#undef G
|
||||
#undef GOTPLT
|
||||
#undef GOT
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,10 +371,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
*(u16 *)loc = val;
|
||||
};
|
||||
|
||||
#define S (frag ? frag->get_addr(ctx) : sym.get_addr(ctx))
|
||||
#define A (frag ? addend : this->get_addend(rel))
|
||||
#define G (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
|
||||
#define GOTPLT ctx.gotplt->shdr.sh_addr
|
||||
#define S (frag ? frag->get_addr(ctx) : sym.get_addr(ctx))
|
||||
#define A (frag ? addend : this->get_addend(rel))
|
||||
#define G (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
|
||||
#define GOT ctx.got->shdr.sh_addr
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_386_8:
|
||||
@@ -316,7 +384,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
write16(S + A);
|
||||
continue;
|
||||
case R_386_32:
|
||||
*(u32 *)loc = S + A;
|
||||
if (std::optional<u64> val = get_tombstone(sym))
|
||||
*(u32 *)loc = *val;
|
||||
else
|
||||
*(u32 *)loc = S + A;
|
||||
continue;
|
||||
case R_386_PC8:
|
||||
write8s(S + A);
|
||||
@@ -328,13 +399,16 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
*(u32 *)loc = S + A;
|
||||
continue;
|
||||
case R_386_GOTPC:
|
||||
*(u32 *)loc = GOTPLT + A;
|
||||
*(u32 *)loc = GOT + A;
|
||||
continue;
|
||||
case R_386_GOTOFF:
|
||||
*(u32 *)loc = S + A - GOTPLT;
|
||||
*(u32 *)loc = S + A - GOT;
|
||||
continue;
|
||||
case R_386_TLS_LDO_32:
|
||||
*(u32 *)loc = S + A - ctx.tls_begin;
|
||||
if (std::optional<u64> val = get_tombstone(sym))
|
||||
*(u32 *)loc = *val;
|
||||
else
|
||||
*(u32 *)loc = S + A - ctx.tls_begin;
|
||||
continue;
|
||||
case R_386_SIZE32:
|
||||
*(u32 *)loc = sym.esym().st_size + A;
|
||||
@@ -345,7 +419,7 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
|
||||
#undef S
|
||||
#undef A
|
||||
#undef GOTPLT
|
||||
#undef GOT
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,7 +455,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
// Absolute Local Imported data Imported code
|
||||
{ NONE, ERROR, ERROR, ERROR }, // DSO
|
||||
{ NONE, ERROR, ERROR, ERROR }, // PIE
|
||||
{ NONE, NONE, COPYREL, PLT }, // PDE
|
||||
{ NONE, NONE, COPYREL, CPLT }, // PDE
|
||||
};
|
||||
dispatch(ctx, table, i, rel, sym);
|
||||
break;
|
||||
@@ -391,7 +465,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
// Absolute Local Imported data Imported code
|
||||
{ NONE, BASEREL, DYNREL, DYNREL }, // DSO
|
||||
{ NONE, BASEREL, DYNREL, DYNREL }, // PIE
|
||||
{ NONE, NONE, COPYREL, PLT }, // PDE
|
||||
{ NONE, NONE, COPYREL, CPLT }, // PDE
|
||||
};
|
||||
dispatch(ctx, table, i, rel, sym);
|
||||
break;
|
||||
@@ -438,10 +512,30 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
sym.flags |= NEEDS_GOTTP;
|
||||
break;
|
||||
case R_386_TLS_GD:
|
||||
sym.flags |= NEEDS_TLSGD;
|
||||
if (i + 1 == rels.size())
|
||||
Fatal(ctx) << *this << ": TLS_GD reloc must be followed by PLT or GOT32";
|
||||
|
||||
if (u32 ty = rels[i + 1].r_type;
|
||||
ty != R_386_PLT32 && ty != R_386_GOT32)
|
||||
Fatal(ctx) << *this << ": TLS_GD reloc must be followed by PLT or GOT32";
|
||||
|
||||
if (ctx.arg.relax && !ctx.arg.shared && !sym.is_imported)
|
||||
i++;
|
||||
else
|
||||
sym.flags |= NEEDS_TLSGD;
|
||||
break;
|
||||
case R_386_TLS_LDM:
|
||||
ctx.needs_tlsld = true;
|
||||
if (i + 1 == rels.size())
|
||||
Fatal(ctx) << *this << ": TLS_LDM reloc must be followed by PLT or GOT32";
|
||||
|
||||
if (u32 ty = rels[i + 1].r_type;
|
||||
ty != R_386_PLT32 && ty != R_386_GOT32)
|
||||
Fatal(ctx) << *this << ": TLS_LDM reloc must be followed by PLT or GOT32";
|
||||
|
||||
if (ctx.arg.relax && !ctx.arg.shared)
|
||||
i++;
|
||||
else
|
||||
ctx.needs_tlsld = true;
|
||||
break;
|
||||
case R_386_TLS_GOTDESC:
|
||||
if (!ctx.arg.relax || ctx.arg.shared)
|
||||
|
||||
+64
-108
@@ -213,7 +213,8 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
if (rel.r_type == R_RISCV_NONE || rel.r_type == R_RISCV_RELAX)
|
||||
if (rel.r_type == R_RISCV_NONE || rel.r_type == R_RISCV_RELAX ||
|
||||
rel.r_type == R_RISCV_ALIGN)
|
||||
continue;
|
||||
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
@@ -230,21 +231,21 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
#define G (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
|
||||
#define GOT ctx.got->shdr.sh_addr
|
||||
|
||||
if (needs_dynrel[i]) {
|
||||
*dynrel++ = {P, R_RISCV_64, (u32)sym.get_dynsym_idx(ctx), A};
|
||||
*(u64 *)loc = A;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (needs_baserel[i] && !is_relr_reloc(ctx, rel))
|
||||
*dynrel++ = {P, R_RISCV_RELATIVE, 0, (i64)(S + A)};
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_RISCV_32:
|
||||
*(u32 *)loc = S + A;
|
||||
break;
|
||||
case R_RISCV_64:
|
||||
*(u64 *)loc = S + A;
|
||||
if (sym.is_absolute() || !ctx.arg.pic) {
|
||||
*(u64 *)loc = S + A;
|
||||
} else if (sym.is_imported) {
|
||||
*dynrel++ = {P, R_RISCV_64, (u32)sym.get_dynsym_idx(ctx), A};
|
||||
*(u64 *)loc = A;
|
||||
} else {
|
||||
if (!is_relr_reloc(ctx, rel))
|
||||
*dynrel++ = {P, R_RISCV_RELATIVE, 0, (i64)(S + A)};
|
||||
*(u64 *)loc = S + A;
|
||||
}
|
||||
break;
|
||||
case R_RISCV_TLS_DTPMOD32:
|
||||
case R_RISCV_TLS_DTPMOD64:
|
||||
@@ -295,7 +296,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
}
|
||||
break;
|
||||
case R_RISCV_PCREL_LO12_I:
|
||||
assert(sym.input_section == this);
|
||||
assert(sym.get_input_section() == this);
|
||||
assert(sym.value < r_offset);
|
||||
write_itype((u32 *)loc, *(u32 *)(base + sym.value));
|
||||
break;
|
||||
@@ -304,7 +305,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
write_itype((u32 *)loc, S + A);
|
||||
break;
|
||||
case R_RISCV_PCREL_LO12_S:
|
||||
assert(sym.input_section == this);
|
||||
assert(sym.get_input_section() == this);
|
||||
assert(sym.value < r_offset);
|
||||
write_stype((u32 *)loc, *(u32 *)(base + sym.value));
|
||||
break;
|
||||
@@ -344,8 +345,6 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
case R_RISCV_SUB64:
|
||||
*(u64 *)loc -= S + A;
|
||||
break;
|
||||
case R_RISCV_ALIGN:
|
||||
break;
|
||||
case R_RISCV_RVC_BRANCH:
|
||||
write_cbtype((u16 *)loc, S + A - P);
|
||||
break;
|
||||
@@ -355,8 +354,6 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
case R_RISCV_RVC_LUI:
|
||||
Error(ctx) << *this << ": unsupported relocation: " << rel;
|
||||
break;
|
||||
case R_RISCV_RELAX:
|
||||
break;
|
||||
case R_RISCV_SUB6:
|
||||
*loc = (*loc & 0b1100'0000) | ((*loc - (S + A)) & 0b0011'1111);
|
||||
break;
|
||||
@@ -434,7 +431,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
*(u32 *)loc = S + A;
|
||||
break;
|
||||
case R_RISCV_64:
|
||||
*(u64 *)loc = S + A;
|
||||
if (std::optional<u64> val = get_tombstone(sym))
|
||||
*(u64 *)loc = *val;
|
||||
else
|
||||
*(u64 *)loc = S + A;
|
||||
break;
|
||||
case R_RISCV_ADD8:
|
||||
*loc += S + A;
|
||||
@@ -507,18 +507,12 @@ void InputSection<E>::copy_contents_riscv(Context<E> &ctx, u8 *buf) {
|
||||
i64 delta = r_deltas[i + 1] - r_deltas[i];
|
||||
if (delta == 0)
|
||||
continue;
|
||||
assert(delta < 0);
|
||||
|
||||
const ElfRel<E> &r = rels[i];
|
||||
memcpy(buf, contents.data() + pos, r.r_offset - pos);
|
||||
buf += r.r_offset - pos;
|
||||
pos = r.r_offset;
|
||||
|
||||
if (delta < 0) {
|
||||
pos -= delta;
|
||||
} else {
|
||||
memset(buf, 0, delta);
|
||||
buf += delta;
|
||||
}
|
||||
pos = r.r_offset - delta;
|
||||
}
|
||||
|
||||
memcpy(buf, contents.data() + pos, contents.size() - pos);
|
||||
@@ -554,9 +548,9 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
case R_RISCV_HI20: {
|
||||
Action table[][4] = {
|
||||
// Absolute Local Imported data Imported code
|
||||
{ NONE, NONE, ERROR, ERROR }, // DSO
|
||||
{ NONE, NONE, COPYREL, PLT }, // PIE
|
||||
{ NONE, NONE, COPYREL, PLT }, // PDE
|
||||
{ NONE, ERROR, ERROR, ERROR }, // DSO
|
||||
{ NONE, ERROR, ERROR, ERROR }, // PIE
|
||||
{ NONE, NONE, COPYREL, CPLT }, // PDE
|
||||
};
|
||||
dispatch(ctx, table, i, rel, sym);
|
||||
break;
|
||||
@@ -566,7 +560,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
// Absolute Local Imported data Imported code
|
||||
{ NONE, BASEREL, DYNREL, DYNREL }, // DSO
|
||||
{ NONE, BASEREL, DYNREL, DYNREL }, // PIE
|
||||
{ NONE, NONE, COPYREL, PLT }, // PDE
|
||||
{ NONE, NONE, COPYREL, CPLT }, // PDE
|
||||
};
|
||||
dispatch(ctx, table, i, rel, sym);
|
||||
break;
|
||||
@@ -661,54 +655,19 @@ static void initialize_storage(Context<E> &ctx) {
|
||||
|
||||
for (Symbol<E> *sym : file->symbols)
|
||||
if (sym->file == file)
|
||||
if (InputSection<E> *isec = sym->input_section)
|
||||
file->sorted_symbols[isec->section_idx].push_back(sym);
|
||||
if (InputSection<E> *isec = sym->get_input_section())
|
||||
file->sorted_symbols[isec->shndx].push_back(sym);
|
||||
|
||||
for (std::vector<Symbol<E> *> &vec : file->sorted_symbols)
|
||||
sort(vec, [](Symbol<E> *a, Symbol<E> *b) { return a->value < b->value; });
|
||||
});
|
||||
}
|
||||
|
||||
// Interpret R_RISCV_ALIGN relocations and align them if necessary.
|
||||
// This function may enlarge input sections but never shrinks.
|
||||
static void align_contents(Context<E> &ctx, InputSection<E> &isec) {
|
||||
std::span<i32> r_deltas = isec.get_r_deltas();
|
||||
std::span<Symbol<E> *> syms = isec.get_sorted_symbols();
|
||||
i64 delta = 0;
|
||||
|
||||
std::span<ElfRel<E>> rels = isec.get_rels(ctx);
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &r = rels[i];
|
||||
r_deltas[i] = delta;
|
||||
|
||||
if (r.r_type != R_RISCV_ALIGN)
|
||||
continue;
|
||||
|
||||
i64 delta2 = align_to(r.r_offset, r.r_addend) - r.r_offset;
|
||||
if (delta2 == 0)
|
||||
continue;
|
||||
|
||||
while (!syms.empty() && syms[0]->value <= r.r_offset) {
|
||||
syms[0]->value += delta;
|
||||
syms = syms.subspan(1);
|
||||
}
|
||||
|
||||
delta += delta2;
|
||||
}
|
||||
|
||||
for (Symbol<E> *sym : syms)
|
||||
sym->value += delta;
|
||||
r_deltas[rels.size()] = delta;
|
||||
|
||||
isec.sh_size += delta;
|
||||
}
|
||||
|
||||
// Returns the distance between a relocated place and a symbol.
|
||||
static 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 `relax_call` may increase a distance between a branch
|
||||
// because `relax_section` may increase a distance between a branch
|
||||
// instruction and an absolute symbol. Branching to an absolute
|
||||
// location is extremely rare in real code, though.
|
||||
if (sym.is_absolute())
|
||||
@@ -726,7 +685,7 @@ static i64 compute_distance(Context<E> &ctx, Symbol<E> &sym,
|
||||
}
|
||||
|
||||
// Relax R_RISCV_CALL and R_RISCV_CALL_PLT relocations.
|
||||
static void relax_call(Context<E> &ctx, InputSection<E> &isec) {
|
||||
static void relax_section(Context<E> &ctx, InputSection<E> &isec) {
|
||||
std::span<i32> r_deltas = isec.get_r_deltas();
|
||||
std::span<Symbol<E> *> syms = isec.get_sorted_symbols();
|
||||
i64 delta = 0;
|
||||
@@ -740,21 +699,37 @@ static void relax_call(Context<E> &ctx, InputSection<E> &isec) {
|
||||
r_deltas[i] += delta;
|
||||
|
||||
switch (r.r_type) {
|
||||
case R_RISCV_ALIGN:
|
||||
delta2 = align_to(r.r_offset, r.r_addend) - r.r_offset;
|
||||
break;
|
||||
case R_RISCV_CALL:
|
||||
case R_RISCV_CALL_PLT: {
|
||||
if (i == rels.size() - 1 || rels[i + 1].r_type != R_RISCV_RELAX)
|
||||
break;
|
||||
case R_RISCV_ALIGN: {
|
||||
// R_RISCV_ALIGN refers NOP instructions. We need to eliminate
|
||||
// some or all of the instructions so that the instruction that
|
||||
// immediately follows the NOPs is aligned to a specified
|
||||
// alignment boundary.
|
||||
u64 loc = isec.get_addr() + r.r_offset + delta;
|
||||
|
||||
// If the jump target is within ±1 MiB, we can replace AUIPC+JALR
|
||||
// with JAL, saving 4 bytes.
|
||||
Symbol<E> &sym = *isec.file.symbols[r.r_sym];
|
||||
i64 dist = compute_distance(ctx, sym, isec, r);
|
||||
if (dist % 2 == 0 && -(1 << 20) <= dist && dist < (1 << 20))
|
||||
delta2 = -4;
|
||||
// The total bytes of NOPs is stored to r_addend, so the next
|
||||
// instruction is r_addend away.
|
||||
u64 next_loc = loc + r.r_addend;
|
||||
|
||||
u64 alignment = (std::popcount<u64>(r.r_addend) == 1)
|
||||
? r.r_addend : next_power_of_two(r.r_addend);
|
||||
|
||||
if (next_loc % alignment)
|
||||
delta2 = align_to(loc, alignment) - next_loc;
|
||||
break;
|
||||
}
|
||||
case R_RISCV_CALL:
|
||||
case R_RISCV_CALL_PLT:
|
||||
if (ctx.arg.relax) {
|
||||
if (i == rels.size() - 1 || rels[i + 1].r_type != R_RISCV_RELAX)
|
||||
break;
|
||||
|
||||
// If the jump target is within ±1 MiB, we can replace AUIPC+JALR
|
||||
// with JAL, saving 4 bytes.
|
||||
Symbol<E> &sym = *isec.file.symbols[r.r_sym];
|
||||
i64 dist = compute_distance(ctx, sym, isec, r);
|
||||
if (dist % 2 == 0 && -(1 << 20) <= dist && dist < (1 << 20))
|
||||
delta2 = -4;
|
||||
}
|
||||
}
|
||||
|
||||
if (delta2 == 0)
|
||||
@@ -816,38 +791,19 @@ static void relax_call(Context<E> &ctx, InputSection<E> &isec) {
|
||||
// mandatory because of R_RISCV_ALIGN. R_RISCV_ALIGN relocation is a
|
||||
// directive to the linker to align the location referred to by the
|
||||
// relocation to a specified byte boundary. We at least have to
|
||||
// interpret them satisfy the constraints imposed by R_RISCV_ALIGN,
|
||||
// and that means we may change section sizes anyway.
|
||||
// interpret them satisfy the constraints imposed by R_RISCV_ALIGN
|
||||
// relocations.
|
||||
i64 riscv_resize_sections(Context<E> &ctx) {
|
||||
Timer t(ctx, "riscv_resize_sections");
|
||||
|
||||
initialize_storage(ctx);
|
||||
|
||||
// First, interpret R_RISCV_ALIGN relocations. This may enlarge
|
||||
// sections.
|
||||
{
|
||||
Timer t(ctx, "align_contents");
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
for (std::unique_ptr<InputSection<E>> &isec : file->sections)
|
||||
if (is_resizable(ctx, isec.get()))
|
||||
align_contents(ctx, *isec);
|
||||
});
|
||||
}
|
||||
|
||||
// Re-compute section offset.
|
||||
compute_section_sizes(ctx);
|
||||
set_osec_offsets(ctx);
|
||||
|
||||
// Find R_RISCV_CALL AND R_RISCV_CALL_PLT that can be relaxed.
|
||||
// This step should only shrink sections.
|
||||
{
|
||||
Timer t(ctx, "relax_call");
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
for (std::unique_ptr<InputSection<E>> &isec : file->sections)
|
||||
if (is_resizable(ctx, isec.get()))
|
||||
relax_call(ctx, *isec);
|
||||
});
|
||||
}
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
for (std::unique_ptr<InputSection<E>> &isec : file->sections)
|
||||
if (is_resizable(ctx, isec.get()))
|
||||
relax_section(ctx, *isec);
|
||||
});
|
||||
|
||||
// Re-compute section offset again to finalize them.
|
||||
compute_section_sizes(ctx);
|
||||
|
||||
+148
-50
@@ -24,44 +24,49 @@ static void write_compact_plt(Context<E> &ctx) {
|
||||
|
||||
// The IBTPLT is a security-enhanced version of the regular PLT.
|
||||
// It uses Indirect Branch Tracking (IBT) feature which is part of
|
||||
// Intel Control-Flow Enforcement (CET). IBTPLT is slightly larger
|
||||
// than the regular PLT (24 bytes vs 16 bytes for each entry).
|
||||
// Intel Control-Flow Enforcement (CET).
|
||||
//
|
||||
// Note that our IBTPLT instruction sequence is different from the one
|
||||
// used in GNU ld. GNU's IBTPLT implementation uses two separate
|
||||
// sections (.plt and .plt.sec) in which one PLT entry takes 32 bytes
|
||||
// in total.
|
||||
// in total. Our PLT consists of just .plt and each entry is 16 bytes
|
||||
// long.
|
||||
//
|
||||
// Our PLT entry clobbers r11, but that's fine because the resolver
|
||||
// function (_dl_runtime_resolve) does not preserve r11 anyway.
|
||||
static void write_ibtplt(Context<E> &ctx) {
|
||||
u8 *buf = ctx.buf + ctx.plt->shdr.sh_offset;
|
||||
|
||||
// Write PLT header
|
||||
static const u8 plt0[] = {
|
||||
0xf3, 0x0f, 0x1e, 0xfa, // endbr64
|
||||
0x41, 0x53, // push %r11
|
||||
0xff, 0x35, 0, 0, 0, 0, // push GOTPLT+8(%rip)
|
||||
0xff, 0x25, 0, 0, 0, 0, // jmp *GOTPLT+16(%rip)
|
||||
0x0f, 0x1f, 0x40, 0x00, // nop
|
||||
0x0f, 0x1f, 0x40, 0x00, // nop
|
||||
0x0f, 0x1f, 0x40, 0x00, // nop
|
||||
0x66, 0x90, // nop
|
||||
};
|
||||
|
||||
memcpy(buf, plt0, sizeof(plt0));
|
||||
*(u32 *)(buf + 2) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr + 2;
|
||||
*(u32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr + 4;
|
||||
*(u32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 4;
|
||||
*(u32 *)(buf + 14) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 2;
|
||||
|
||||
// Write PLT entries
|
||||
i64 relplt_idx = 0;
|
||||
|
||||
static const u8 data[] = {
|
||||
0xf3, 0x0f, 0x1e, 0xfa, // endbr64
|
||||
0x41, 0xbb, 0, 0, 0, 0, // mov $index_in_relplt, %r11d
|
||||
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOTPLT
|
||||
0x68, 0, 0, 0, 0, // push $index_in_relplt
|
||||
0xf2, 0xe9, 0, 0, 0, 0, // jmp PLT[0]
|
||||
0x0f, 0x1f, 0x00, // nop
|
||||
};
|
||||
|
||||
for (Symbol<E> *sym : ctx.plt->symbols) {
|
||||
u8 *ent = buf + ctx.plt_hdr_size + sym->get_plt_idx(ctx) * ctx.plt_size;
|
||||
memcpy(ent, data, sizeof(data));
|
||||
*(u32 *)(ent + 6) = sym->get_gotplt_addr(ctx) - sym->get_plt_addr(ctx) - 10;
|
||||
*(u32 *)(ent + 11) = relplt_idx++;
|
||||
*(u32 *)(ent + 17) = ctx.plt->shdr.sh_addr - sym->get_plt_addr(ctx) - 21;
|
||||
*(u32 *)(ent + 6) = relplt_idx++;
|
||||
*(u32 *)(ent + 12) = sym->get_gotplt_addr(ctx) - sym->get_plt_addr(ctx) - 16;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,6 +205,28 @@ static u32 relax_gottpoff(u8 *loc) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 relax_gotpc32_tlsdesc(u8 *loc) {
|
||||
switch ((loc[0] << 16) | (loc[1] << 8) | loc[2]) {
|
||||
case 0x488d05: return 0x48c7c0; // lea 0(%rip), %rax -> mov $0, %rax
|
||||
case 0x488d0d: return 0x48c7c1; // lea 0(%rip), %rcx -> mov $0, %rcx
|
||||
case 0x488d15: return 0x48c7c2; // lea 0(%rip), %rdx -> mov $0, %rdx
|
||||
case 0x488d1d: return 0x48c7c3; // lea 0(%rip), %rbx -> mov $0, %rbx
|
||||
case 0x488d25: return 0x48c7c4; // lea 0(%rip), %rsp -> mov $0, %rsp
|
||||
case 0x488d2d: return 0x48c7c5; // lea 0(%rip), %rbp -> mov $0, %rbp
|
||||
case 0x488d35: return 0x48c7c6; // lea 0(%rip), %rsi -> mov $0, %rsi
|
||||
case 0x488d3d: return 0x48c7c7; // lea 0(%rip), %rdi -> mov $0, %rdi
|
||||
case 0x4c8d05: return 0x49c7c0; // lea 0(%rip), %r8 -> mov $0, %r8
|
||||
case 0x4c8d0d: return 0x49c7c1; // lea 0(%rip), %r9 -> mov $0, %r9
|
||||
case 0x4c8d15: return 0x49c7c2; // lea 0(%rip), %r10 -> mov $0, %r10
|
||||
case 0x4c8d1d: return 0x49c7c3; // lea 0(%rip), %r11 -> mov $0, %r11
|
||||
case 0x4c8d25: return 0x49c7c4; // lea 0(%rip), %r12 -> mov $0, %r12
|
||||
case 0x4c8d2d: return 0x49c7c5; // lea 0(%rip), %r13 -> mov $0, %r13
|
||||
case 0x4c8d35: return 0x49c7c6; // lea 0(%rip), %r14 -> mov $0, %r14
|
||||
case 0x4c8d3d: return 0x49c7c7; // lea 0(%rip), %r15 -> mov $0, %r15
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Apply relocations to SHF_ALLOC sections (i.e. sections that are
|
||||
// mapped to memory at runtime) based on the result of
|
||||
// scan_relocations().
|
||||
@@ -272,15 +299,6 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
#define G (sym.get_got_addr(ctx) - ctx.gotplt->shdr.sh_addr)
|
||||
#define GOT ctx.gotplt->shdr.sh_addr
|
||||
|
||||
if (needs_dynrel[i]) {
|
||||
*dynrel++ = {P, R_X86_64_64, (u32)sym.get_dynsym_idx(ctx), A};
|
||||
write64(A);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (needs_baserel[i] && !is_relr_reloc(ctx, rel))
|
||||
*dynrel++ = {P, R_X86_64_RELATIVE, 0, (i64)(S + A)};
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_X86_64_8:
|
||||
write8(S + A);
|
||||
@@ -295,7 +313,16 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
write32s(S + A);
|
||||
continue;
|
||||
case R_X86_64_64:
|
||||
write64(S + A);
|
||||
if (sym.is_absolute() || !ctx.arg.pic) {
|
||||
write64(S + A);
|
||||
} else if (sym.is_imported) {
|
||||
*dynrel++ = {P, R_X86_64_64, (u32)sym.get_dynsym_idx(ctx), A};
|
||||
write64(A);
|
||||
} else {
|
||||
if (!is_relr_reloc(ctx, rel))
|
||||
*dynrel++ = {P, R_X86_64_RELATIVE, 0, (i64)(S + A)};
|
||||
write64(S + A);
|
||||
}
|
||||
continue;
|
||||
case R_X86_64_PC8:
|
||||
write8s(S + A - P);
|
||||
@@ -307,7 +334,12 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
write32s(S + A - P);
|
||||
continue;
|
||||
case R_X86_64_PC64:
|
||||
write64(S + A - P);
|
||||
if (sym.is_absolute() || !sym.is_imported || !ctx.arg.shared) {
|
||||
write64(S + A - P);
|
||||
} else {
|
||||
*dynrel++ = {P, R_X86_64_64, (u32)sym.get_dynsym_idx(ctx), A};
|
||||
write64(A);
|
||||
}
|
||||
continue;
|
||||
case R_X86_64_PLT32:
|
||||
write32s(S + A - P);
|
||||
@@ -360,15 +392,35 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
case R_X86_64_TLSGD:
|
||||
if (sym.get_tlsgd_idx(ctx) == -1) {
|
||||
// Relax GD to LE
|
||||
static const u8 insn[] = {
|
||||
0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0, // mov %fs:0, %rax
|
||||
0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea 0(%rax), %rax
|
||||
};
|
||||
memcpy(loc - 4, insn, sizeof(insn));
|
||||
|
||||
i64 val = S - ctx.tls_end + A + 4;
|
||||
overflow_check(val, -((i64)1 << 31), (i64)1 << 31);
|
||||
*(u32 *)(loc + 8) = val;
|
||||
|
||||
switch (rels[i + 1].r_type) {
|
||||
case R_X86_64_PLT32:
|
||||
case R_X86_64_GOTPCREL:
|
||||
case R_X86_64_GOTPCRELX: {
|
||||
static const u8 insn[] = {
|
||||
0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0, // mov %fs:0, %rax
|
||||
0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea 0(%rax), %rax
|
||||
};
|
||||
memcpy(loc - 4, insn, sizeof(insn));
|
||||
*(u32 *)(loc + 8) = val;
|
||||
break;
|
||||
}
|
||||
case R_X86_64_PLTOFF64: {
|
||||
static const u8 insn[] = {
|
||||
0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0, // mov %fs:0, %rax
|
||||
0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea 0(%rax), %rax
|
||||
0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00, // nop
|
||||
};
|
||||
memcpy(loc - 3, insn, sizeof(insn));
|
||||
*(u32 *)(loc + 9) = val;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
|
||||
i++;
|
||||
} else {
|
||||
write32s(sym.get_tlsgd_addr(ctx) + A - P);
|
||||
@@ -377,11 +429,38 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
case R_X86_64_TLSLD:
|
||||
if (ctx.got->tlsld_idx == -1) {
|
||||
// Relax LD to LE
|
||||
static const u8 insn[] = {
|
||||
0x66, 0x66, 0x66, // (padding)
|
||||
0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0, // mov %fs:0, %rax
|
||||
};
|
||||
memcpy(loc - 3, insn, sizeof(insn));
|
||||
switch (rels[i + 1].r_type) {
|
||||
case R_X86_64_PLT32: {
|
||||
static const u8 insn[] = {
|
||||
0x66, 0x66, 0x66, // (padding)
|
||||
0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0, // mov %fs:0, %rax
|
||||
};
|
||||
memcpy(loc - 3, insn, sizeof(insn));
|
||||
break;
|
||||
}
|
||||
case R_X86_64_GOTPCREL:
|
||||
case R_X86_64_GOTPCRELX: {
|
||||
static const u8 insn[] = {
|
||||
0x66, 0x66, 0x66, // (padding)
|
||||
0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0, // mov %fs:0, %rax
|
||||
0x90, // nop
|
||||
};
|
||||
memcpy(loc - 3, insn, sizeof(insn));
|
||||
break;
|
||||
}
|
||||
case R_X86_64_PLTOFF64: {
|
||||
static const u8 insn[] = {
|
||||
0x66, 0x66, 0x66, // (padding)
|
||||
0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0, // mov %fs:0, %rax
|
||||
0x66, 0x66, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, // nop
|
||||
};
|
||||
memcpy(loc - 3, insn, sizeof(insn));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
|
||||
i++;
|
||||
} else {
|
||||
write32s(ctx.got->get_tlsld_addr(ctx) + A - P);
|
||||
@@ -418,10 +497,10 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
continue;
|
||||
case R_X86_64_GOTPC32_TLSDESC:
|
||||
if (sym.get_tlsdesc_idx(ctx) == -1) {
|
||||
static const u8 insn[] = {
|
||||
0x48, 0xc7, 0xc0, 0, 0, 0, 0, // mov $0, %rax
|
||||
};
|
||||
memcpy(loc - 3, insn, sizeof(insn));
|
||||
u32 insn = relax_gotpc32_tlsdesc(loc - 3);
|
||||
loc[-3] = insn >> 16;
|
||||
loc[-2] = insn >> 8;
|
||||
loc[-1] = insn;
|
||||
write32s(S + A - ctx.tls_end + 4);
|
||||
} else {
|
||||
write32s(sym.get_tlsdesc_addr(ctx) + A - P);
|
||||
@@ -529,13 +608,22 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
write32s(S + A);
|
||||
break;
|
||||
case R_X86_64_64:
|
||||
*(u64 *)loc = S + A;
|
||||
if (std::optional<u64> val = get_tombstone(sym))
|
||||
*(u64 *)loc = *val;
|
||||
else
|
||||
*(u64 *)loc = S + A;
|
||||
break;
|
||||
case R_X86_64_DTPOFF32:
|
||||
write32s(S + A - ctx.tls_begin);
|
||||
if (std::optional<u64> val = get_tombstone(sym))
|
||||
*(u32 *)loc = *val;
|
||||
else
|
||||
write32s(S + A - ctx.tls_begin);
|
||||
break;
|
||||
case R_X86_64_DTPOFF64:
|
||||
*(u64 *)loc = S + A - ctx.tls_begin;
|
||||
if (std::optional<u64> val = get_tombstone(sym))
|
||||
*(u64 *)loc = *val;
|
||||
else
|
||||
*(u64 *)loc = S + A - ctx.tls_begin;
|
||||
break;
|
||||
case R_X86_64_SIZE32:
|
||||
write32(sym.esym().st_size + A);
|
||||
@@ -597,7 +685,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
// Absolute Local Imported data Imported code
|
||||
{ NONE, ERROR, ERROR, ERROR }, // DSO
|
||||
{ NONE, ERROR, ERROR, ERROR }, // PIE
|
||||
{ NONE, NONE, COPYREL, PLT }, // PDE
|
||||
{ NONE, NONE, COPYREL, CPLT }, // PDE
|
||||
};
|
||||
dispatch(ctx, table, i, rel, sym);
|
||||
break;
|
||||
@@ -609,7 +697,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
// Absolute Local Imported data Imported code
|
||||
{ NONE, BASEREL, DYNREL, DYNREL }, // DSO
|
||||
{ NONE, BASEREL, DYNREL, DYNREL }, // PIE
|
||||
{ NONE, NONE, COPYREL, PLT }, // PDE
|
||||
{ NONE, NONE, COPYREL, CPLT }, // PDE
|
||||
};
|
||||
dispatch(ctx, table, i, rel, sym);
|
||||
break;
|
||||
@@ -675,26 +763,36 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
dispatch(ctx, table, i, rel, sym);
|
||||
break;
|
||||
}
|
||||
case R_X86_64_TLSGD:
|
||||
case R_X86_64_TLSGD: {
|
||||
if (i + 1 == rels.size())
|
||||
Fatal(ctx) << *this
|
||||
<< ": TLSGD reloc must be followed by PLT32 or GOTPCREL";
|
||||
Fatal(ctx) << *this << ": TLSGD reloc must be followed by PLT or GOTPCREL";
|
||||
|
||||
if (u32 ty = rels[i + 1].r_type;
|
||||
ty != R_X86_64_PLT32 && ty != R_X86_64_PLTOFF64 &&
|
||||
ty != R_X86_64_GOTPCREL && ty != R_X86_64_GOTPCRELX)
|
||||
Fatal(ctx) << *this << ": TLSGD reloc must be followed by PLT or GOTPCREL";
|
||||
|
||||
if (ctx.arg.relax && !ctx.arg.shared && !sym.is_imported)
|
||||
i++;
|
||||
else
|
||||
sym.flags |= NEEDS_TLSGD;
|
||||
break;
|
||||
case R_X86_64_TLSLD:
|
||||
}
|
||||
case R_X86_64_TLSLD: {
|
||||
if (i + 1 == rels.size())
|
||||
Fatal(ctx) << *this
|
||||
<< ": TLSLD reloc must be followed by PLT32 or GOTPCREL";
|
||||
Fatal(ctx) << *this << ": TLSLD reloc must be followed by PLT or GOTPCREL";
|
||||
|
||||
if (u32 ty = rels[i + 1].r_type;
|
||||
ty != R_X86_64_PLT32 && ty != R_X86_64_PLTOFF64 &&
|
||||
ty != R_X86_64_GOTPCREL && ty != R_X86_64_GOTPCRELX)
|
||||
Fatal(ctx) << *this << ": TLSLD reloc must be followed by PLT or GOTPCREL";
|
||||
|
||||
if (ctx.arg.relax && !ctx.arg.shared)
|
||||
i++;
|
||||
else
|
||||
ctx.needs_tlsld = true;
|
||||
break;
|
||||
}
|
||||
case R_X86_64_GOTTPOFF: {
|
||||
ctx.has_gottp_rel = true;
|
||||
|
||||
@@ -705,7 +803,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
break;
|
||||
}
|
||||
case R_X86_64_GOTPC32_TLSDESC: {
|
||||
if (memcmp(loc - 3, "\x48\x8d\x05", 3))
|
||||
if (relax_gotpc32_tlsdesc(loc - 3) == 0)
|
||||
Fatal(ctx) << *this << ": GOTPC32_TLSDESC relocation is used"
|
||||
<< " against an invalid code sequence";
|
||||
|
||||
|
||||
+180
-39
@@ -43,12 +43,15 @@ Options:
|
||||
-s, --strip-all Strip .symtab section
|
||||
-u SYMBOL, --undefined SYMBOL
|
||||
Force to resolve SYMBOL
|
||||
--Bdynamic Link against shared libraries (default)
|
||||
--Bstatic Do not link against shared libraries
|
||||
--Bdynamic, --dy Link against shared libraries (default)
|
||||
--Bstatic, --dn, --static Do not link against shared libraries
|
||||
--Bsymbolic Bind global symbols locally
|
||||
--Bsymbolic-functions Bind global functions locally
|
||||
--Bno-symbolic Cancel --Bsymbolic and --Bsymbolic-functions
|
||||
--Map FILE Write map file to a given file
|
||||
--Tbss=ADDR Set address to .bss
|
||||
--Tdata Set address to .data
|
||||
--Ttext Set address to .text
|
||||
--allow-multiple-definition Allow multiple definitions
|
||||
--as-needed Only set DT_NEEDED if used
|
||||
--no-as-needed
|
||||
@@ -62,10 +65,12 @@ Options:
|
||||
--compress-debug-sections [none,zlib,zlib-gabi,zlib-gnu]
|
||||
Compress .debug_* sections
|
||||
--dc Ignored
|
||||
--dependency-file=FILE Write Makefile-style dependency rules to FILE
|
||||
--defsym=SYMBOL=VALUE Define a symbol alias
|
||||
--demangle Demangle C++ symbols in log messages (default)
|
||||
--no-demangle
|
||||
--disable-new-dtags Ignored
|
||||
--enable-new-dtags Emit DT_RUNPATH for --rpath (default)
|
||||
--disable-new-dtags Emit DT_RPATH for --rpath
|
||||
--dp Ignored
|
||||
--dynamic-list Read a list of dynamic symbols
|
||||
--eh-frame-hdr Create .eh_frame_hdr section
|
||||
@@ -79,21 +84,21 @@ Options:
|
||||
--no-fork
|
||||
--gc-sections Remove unreferenced sections
|
||||
--no-gc-sections
|
||||
--gdb-index Ignored
|
||||
--gdb-index Create .gdb_index for faster gdb startup
|
||||
--hash-style [sysv,gnu,both]
|
||||
Set hash style
|
||||
--icf Fold identical code
|
||||
--icf=[all,none] Fold identical code
|
||||
--no-icf
|
||||
--image-base ADDR Set the base address to a given value
|
||||
--init SYMBOL Call SYMBOl at load-time
|
||||
--no-undefined Report undefined symbols (even with --shared)
|
||||
--noinhibit-exec Create an output file even if errors occur
|
||||
--oformat=binary Omit ELF, section and program headers
|
||||
--pack-dyn-relocs=[relr,none]
|
||||
Pack dynamic relocations
|
||||
--perf Print performance statistics
|
||||
--pie, --pic-executable Create a position independent executable
|
||||
--no-pie, --no-pic-executable
|
||||
--plugin Ignored
|
||||
--plugin-opt Ignored
|
||||
--pop-state Pop state of flags governing input file handling
|
||||
--preload
|
||||
--no-preload
|
||||
@@ -109,9 +114,11 @@ Options:
|
||||
--repro Embed input files to .repro section
|
||||
--require-defined SYMBOL Require SYMBOL be defined in the final output
|
||||
--retain-symbols-file FILE Keep only symbols listed in FILE
|
||||
--reverse-sections Reverses input sections in the output file
|
||||
--rpath DIR Add DIR to runtime search path
|
||||
--rpath-link DIR Ignored
|
||||
--run COMMAND ARG... Run COMMAND with mold as /usr/bin/ld
|
||||
--section-start=SECTION=ADDR Set address to section
|
||||
--shared, --Bshareable Create a share library
|
||||
--shuffle-sections[=SEED] Randomize the output by shuffling input sections
|
||||
--sort-common Ignored
|
||||
@@ -119,7 +126,6 @@ Options:
|
||||
--spare-dynamic-tags NUMBER Reserve give number of tags in .dynamic section
|
||||
--start-lib Give following object files in-archive-file semantics
|
||||
--end-lib End the effect of --start-lib
|
||||
--static Do not link against shared libraries
|
||||
--stats Print input statistics
|
||||
--sysroot DIR Set target system root directory
|
||||
--thread-count COUNT, --threads=COUNT
|
||||
@@ -134,6 +140,7 @@ Options:
|
||||
--warn-common Warn about common symbols
|
||||
--no-warn-common
|
||||
--warn-once Only warn once for each undefined symbol
|
||||
--warn-shared-textrel Warn if the output .so needs text relocations
|
||||
--warn-textrel Warn if the output file needs text relocations
|
||||
--warn-unresolved-symbols Report unresolved symbols as warnings
|
||||
--error-unresolved-symbols
|
||||
@@ -221,6 +228,19 @@ bool read_arg(Context<E> &ctx, std::span<std::string_view> &args,
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
bool read_eq(Context<E> &ctx, std::span<std::string_view> &args,
|
||||
std::string_view &arg, std::string name) {
|
||||
for (std::string opt : add_dashes(name)) {
|
||||
if (args[0].starts_with(opt + "=")) {
|
||||
arg = args[0].substr(opt.size() + 1);
|
||||
args = args.subspan(1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool read_flag(std::span<std::string_view> &args, std::string name) {
|
||||
for (std::string opt : add_dashes(name)) {
|
||||
if (args[0] == opt) {
|
||||
@@ -265,19 +285,26 @@ bool read_z_arg(Context<E> &ctx, std::span<std::string_view> &args,
|
||||
|
||||
template <typename E>
|
||||
static i64 parse_hex(Context<E> &ctx, std::string opt, std::string_view value) {
|
||||
if (!value.starts_with("0x") && !value.starts_with("0X"))
|
||||
Fatal(ctx) << "option -" << opt << ": not a hexadecimal number";
|
||||
value = value.substr(2);
|
||||
if (value.starts_with("0x") || value.starts_with("0X"))
|
||||
value = value.substr(2);
|
||||
if (value.find_first_not_of("0123456789abcdefABCDEF") != value.npos)
|
||||
Fatal(ctx) << "option -" << opt << ": not a hexadecimal number";
|
||||
return std::stol(std::string(value), nullptr, 16);
|
||||
return std::stoul(std::string(value), nullptr, 16);
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
static i64 parse_number(Context<E> &ctx, std::string opt,
|
||||
std::string_view value) {
|
||||
size_t nread;
|
||||
i64 ret = std::stol(std::string(value), &nread, 0);
|
||||
|
||||
if (value.starts_with('-')) {
|
||||
i64 ret = std::stoul(std::string(value.substr(1)), &nread, 0);
|
||||
if (value.size() - 1 != nread)
|
||||
Fatal(ctx) << "option -" << opt << ": not a number: " << value;
|
||||
return -ret;
|
||||
}
|
||||
|
||||
i64 ret = std::stoul(std::string(value), &nread, 0);
|
||||
if (value.size() != nread)
|
||||
Fatal(ctx) << "option -" << opt << ": not a number: " << value;
|
||||
return ret;
|
||||
@@ -370,23 +397,41 @@ static bool is_file(std::string_view path) {
|
||||
(st.st_mode & S_IFMT) != S_IFDIR;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
static std::variant<Symbol<E> *, u64>
|
||||
parse_defsym_value(Context<E> &ctx, std::string_view s) {
|
||||
if (s.starts_with("0x") || s.starts_with("0X")) {
|
||||
size_t nread;
|
||||
u64 addr = std::stoull(std::string(s), &nread, 16);
|
||||
if (s.size() != nread)
|
||||
return {};
|
||||
return addr;
|
||||
}
|
||||
|
||||
if (s.find_first_not_of("0123456789") == s.npos)
|
||||
return (u64)std::stoull(std::string(s), nullptr, 10);
|
||||
return get_symbol(ctx, s);
|
||||
}
|
||||
|
||||
// Returns a PLT header size and a PLT entry size.
|
||||
template <typename E>
|
||||
static std::pair<i64, i64> get_plt_size(Context<E> &ctx) {
|
||||
switch (E::e_machine) {
|
||||
case EM_X86_64:
|
||||
if constexpr (std::is_same_v<E, X86_64>) {
|
||||
if (ctx.arg.z_now)
|
||||
return {0, 8};
|
||||
if (ctx.arg.z_ibtplt)
|
||||
return {16, 24};
|
||||
return {32, 16};
|
||||
return {16, 16};
|
||||
case EM_386:
|
||||
return {16, 16};
|
||||
case EM_AARCH64:
|
||||
return {32, 16};
|
||||
case EM_RISCV:
|
||||
return {32, 16};
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<E, I386>)
|
||||
return {16, 16};
|
||||
if constexpr (std::is_same_v<E, ARM64>)
|
||||
return {32, 16};
|
||||
if constexpr (std::is_same_v<E, ARM32>)
|
||||
return {20, 16};
|
||||
if constexpr (std::is_same_v<E, RISCV64>)
|
||||
return {32, 16};
|
||||
unreachable();
|
||||
}
|
||||
|
||||
@@ -400,10 +445,11 @@ void parse_nonpositional_args(Context<E> &ctx,
|
||||
ctx.page_size = E::page_size;
|
||||
|
||||
bool version_shown = false;
|
||||
bool warn_shared_textrel = false;
|
||||
|
||||
// RISC-V object files contains lots of local symbols, so by default
|
||||
// we discard them. This is compatible with GNU ld.
|
||||
if (E::e_machine == EM_RISCV)
|
||||
if constexpr (std::is_same_v<E, RISCV64>)
|
||||
ctx.arg.discard_locals = true;
|
||||
|
||||
while (!args.empty()) {
|
||||
@@ -439,6 +485,8 @@ void parse_nonpositional_args(Context<E> &ctx,
|
||||
ctx.arg.emulation = EM_386;
|
||||
} else if (arg == "aarch64linux") {
|
||||
ctx.arg.emulation = EM_AARCH64;
|
||||
} else if (arg == "armelf_linux_eabi") {
|
||||
ctx.arg.emulation = EM_ARM;
|
||||
} else if (arg == "elf64lriscv") {
|
||||
ctx.arg.emulation = EM_RISCV;
|
||||
} else {
|
||||
@@ -473,10 +521,10 @@ void parse_nonpositional_args(Context<E> &ctx,
|
||||
ctx.arg.print_dependencies = 2;
|
||||
} else if (read_flag(args, "print-map") || read_flag(args, "M")) {
|
||||
ctx.arg.print_map = true;
|
||||
} else if (read_flag(args, "static") || read_flag(args, "Bstatic")) {
|
||||
} else if (read_flag(args, "Bstatic") || read_flag(args, "dn") || read_flag(args, "static")) {
|
||||
ctx.arg.is_static = true;
|
||||
remaining.push_back("-Bstatic");
|
||||
} else if (read_flag(args, "Bdynamic")) {
|
||||
} else if (read_flag(args, "Bdynamic") || read_flag(args, "dy")) {
|
||||
ctx.arg.is_static = false;
|
||||
remaining.push_back("-Bdynamic");
|
||||
} else if (read_flag(args, "shared") || read_flag(args, "Bshareable")) {
|
||||
@@ -485,11 +533,14 @@ void parse_nonpositional_args(Context<E> &ctx,
|
||||
ctx.arg.spare_dynamic_tags = parse_number(ctx, "spare-dynamic-tags", arg);
|
||||
} else if (read_flag(args, "start-lib")) {
|
||||
remaining.push_back("-start-lib");
|
||||
} else if (read_arg(ctx, args, arg, "dependency-file")) {
|
||||
ctx.arg.dependency_file = arg;
|
||||
} else if (read_arg(ctx, args, arg, "defsym")) {
|
||||
size_t pos = arg.find('=');
|
||||
if (pos == arg.npos || pos == arg.size() - 1)
|
||||
Fatal(ctx) << "-defsym: syntax error: " << arg;
|
||||
ctx.arg.defsyms.push_back({arg.substr(0, pos), arg.substr(pos + 1)});
|
||||
ctx.arg.defsyms.emplace_back(get_symbol(ctx, arg.substr(0, pos)),
|
||||
parse_defsym_value(ctx, arg.substr(pos + 1)));
|
||||
} else if (read_flag(args, ":lto-pass2")) {
|
||||
ctx.arg.lto_pass2 = true;
|
||||
} else if (read_arg(ctx, args, arg, ":ignore-ir-file")) {
|
||||
@@ -500,8 +551,15 @@ void parse_nonpositional_args(Context<E> &ctx,
|
||||
ctx.arg.demangle = false;
|
||||
} else if (read_flag(args, "default-symver")) {
|
||||
ctx.arg.default_symver = true;
|
||||
} else if (read_flag(args, "noinhibit-exec")) {
|
||||
ctx.arg.noinhibit_exec = true;
|
||||
} else if (read_flag(args, "shuffle-sections")) {
|
||||
ctx.arg.shuffle_sections = true;
|
||||
ctx.arg.shuffle_sections = SHUFFLE_SECTIONS_SHUFFLE;
|
||||
} else if (read_eq(ctx, args, arg, "shuffle-sections")) {
|
||||
ctx.arg.shuffle_sections = SHUFFLE_SECTIONS_SHUFFLE;
|
||||
ctx.arg.shuffle_sections_seed = parse_number(ctx, "shuffle-sections", arg);
|
||||
} else if (read_flag(args, "reverse-sections")) {
|
||||
ctx.arg.shuffle_sections = SHUFFLE_SECTIONS_REVERSE;
|
||||
} else if (read_arg(ctx, args, arg, "y") ||
|
||||
read_arg(ctx, args, arg, "trace-symbol")) {
|
||||
ctx.arg.trace_symbol.push_back(arg);
|
||||
@@ -568,6 +626,10 @@ void parse_nonpositional_args(Context<E> &ctx,
|
||||
ctx.arg.relax = true;
|
||||
} else if (read_flag(args, "no-relax")) {
|
||||
ctx.arg.relax = false;
|
||||
} else if (read_flag(args, "gdb-index")) {
|
||||
ctx.arg.gdb_index = true;
|
||||
} else if (read_flag(args, "no-gdb-index")) {
|
||||
ctx.arg.gdb_index = false;
|
||||
} else if (read_flag(args, "r") || read_flag(args, "relocatable")) {
|
||||
ctx.arg.relocatable = true;
|
||||
} else if (read_flag(args, "perf")) {
|
||||
@@ -604,8 +666,14 @@ void parse_nonpositional_args(Context<E> &ctx,
|
||||
ctx.arg.warn_common = false;
|
||||
} else if (read_flag(args, "warn-once")) {
|
||||
ctx.arg.warn_once = true;
|
||||
} else if (read_flag(args, "warn-shared-textrel")) {
|
||||
warn_shared_textrel = true;
|
||||
} else if (read_flag(args, "warn-textrel")) {
|
||||
ctx.arg.warn_textrel = true;
|
||||
} else if (read_flag(args, "enable-new-dtags")) {
|
||||
ctx.arg.enable_new_dtags = true;
|
||||
} else if (read_flag(args, "disable-new-dtags")) {
|
||||
ctx.arg.enable_new_dtags = false;
|
||||
} else if (read_arg(ctx, args, arg, "compress-debug-sections")) {
|
||||
if (arg == "zlib" || arg == "zlib-gabi")
|
||||
ctx.arg.compress_debug_sections = COMPRESS_GABI;
|
||||
@@ -622,8 +690,24 @@ void parse_nonpositional_args(Context<E> &ctx,
|
||||
ctx.arg.is_static = true;
|
||||
} else if (read_flag(args, "no-omagic")) {
|
||||
ctx.arg.omagic = false;
|
||||
} else if (read_arg(ctx, args, arg, "oformat")) {
|
||||
if (arg != "binary")
|
||||
Fatal(ctx) << "-oformat: " << arg << " is not supported";
|
||||
ctx.arg.oformat_binary = true;
|
||||
} else if (read_arg(ctx, args, arg, "retain-symbols-file")) {
|
||||
read_retain_symbols_file(ctx, arg);
|
||||
} else if (read_arg(ctx, args, arg, "section-start")) {
|
||||
size_t pos = arg.find('=');
|
||||
if (pos == arg.npos || pos == arg.size() - 1)
|
||||
Fatal(ctx) << "-section-start: syntax error: " << arg;
|
||||
ctx.arg.section_start[arg.substr(0, pos)] =
|
||||
parse_hex(ctx, "section-start", arg.substr(pos + 1));
|
||||
} else if (read_arg(ctx, args, arg, "Tbss")) {
|
||||
ctx.arg.section_start[".bss"] = parse_hex(ctx, "Tbss", arg);
|
||||
} else if (read_arg(ctx, args, arg, "Tdata")) {
|
||||
ctx.arg.section_start[".data"] = parse_hex(ctx, "Tdata", arg);
|
||||
} else if (read_arg(ctx, args, arg, "Ttext")) {
|
||||
ctx.arg.section_start[".text"] = parse_hex(ctx, "Ttext", arg);
|
||||
} else if (read_flag(args, "repro")) {
|
||||
ctx.arg.repro = true;
|
||||
} else if (read_z_flag(args, "now")) {
|
||||
@@ -731,19 +815,69 @@ void parse_nonpositional_args(Context<E> &ctx,
|
||||
} else if (read_arg(ctx, args, arg, "plugin")) {
|
||||
ctx.arg.plugin = arg;
|
||||
} else if (read_arg(ctx, args, arg, "plugin-opt")) {
|
||||
ctx.arg.plugin_opt.push_back(arg);
|
||||
ctx.arg.plugin_opt.push_back(std::string(arg));
|
||||
} else if (read_flag(args, "lto-cs-profile-generate")) {
|
||||
ctx.arg.plugin_opt.push_back("cs-profile-generate");
|
||||
} else if (read_arg(ctx, args, arg, "lto-cs-profile-file")) {
|
||||
ctx.arg.plugin_opt.push_back("cs-profile-path=" + std::string(arg));
|
||||
} else if (read_flag(args, "lto-debug-pass-manager")) {
|
||||
ctx.arg.plugin_opt.push_back("debug-pass-manager");
|
||||
} else if (read_flag(args, "disable-verify")) {
|
||||
ctx.arg.plugin_opt.push_back("disable-verify");
|
||||
} else if (read_flag(args, "lto-emit-asm")) {
|
||||
ctx.arg.plugin_opt.push_back("emit-asm");
|
||||
} else if (read_arg(ctx, args, arg, "thinlto-jobs")) {
|
||||
ctx.arg.plugin_opt.push_back("jobs=" + std::string(arg));
|
||||
} else if (read_flag(args, "no-legacy-pass-manager")) {
|
||||
ctx.arg.plugin_opt.push_back("legacy-pass-manager");
|
||||
} else if (read_arg(ctx, args, arg, "lto-partitions")) {
|
||||
ctx.arg.plugin_opt.push_back("lto-partitions=" + std::string(arg));
|
||||
} else if (read_flag(args, "no-lto-legacy-pass-manager")) {
|
||||
ctx.arg.plugin_opt.push_back("new-pass-manager");
|
||||
} else if (read_arg(ctx, args, arg, "lto-obj-path")) {
|
||||
ctx.arg.plugin_opt.push_back("obj-path=" + std::string(arg));
|
||||
} else if (read_arg(ctx, args, arg, "opt-remarks-filename")) {
|
||||
ctx.arg.plugin_opt.push_back("opt-remarks-filename=" + std::string(arg));
|
||||
} else if (read_arg(ctx, args, arg, "opt-remarks-format")) {
|
||||
ctx.arg.plugin_opt.push_back("opt-remarks-format=" + std::string(arg));
|
||||
} else if (read_arg(ctx, args, arg, "opt-remarks-hotness-threshold")) {
|
||||
ctx.arg.plugin_opt.push_back("opt-remarks-hotness-threshold=" + std::string(arg));
|
||||
} else if (read_arg(ctx, args, arg, "opt-remarks-passes")) {
|
||||
ctx.arg.plugin_opt.push_back("opt-remarks-passes=" + std::string(arg));
|
||||
} else if (read_flag(args, "opt-remarks-with_hotness")) {
|
||||
ctx.arg.plugin_opt.push_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);
|
||||
} else if (args[0].starts_with("--lto-O")) {
|
||||
ctx.arg.plugin_opt.push_back("O" + std::string(args[0].substr(7)));
|
||||
args = args.subspan(1);
|
||||
} else if (read_arg(ctx, args, arg, "lto-pseudo-probe-for-profiling")) {
|
||||
ctx.arg.plugin_opt.push_back("pseudo-probe-for-profiling=" + std::string(arg));
|
||||
} else if (read_arg(ctx, args, arg, "lto-sample-profile")) {
|
||||
ctx.arg.plugin_opt.push_back("sample-profile=" + std::string(arg));
|
||||
} else if (read_flag(args, "save-temps")) {
|
||||
ctx.arg.plugin_opt.push_back("save-temps");
|
||||
} else if (read_flag(args, "thinlto-emit-imports-files")) {
|
||||
ctx.arg.plugin_opt.push_back("thinlto-emit-imports-files");
|
||||
} else if (read_arg(ctx, args, arg, "thinlto-index-only")) {
|
||||
ctx.arg.plugin_opt.push_back("thinlto-index-only=" + std::string(arg));
|
||||
} else if (read_flag(args, "thinlto-index-only")) {
|
||||
ctx.arg.plugin_opt.push_back("thinlto-index-only");
|
||||
} else if (read_arg(ctx, args, arg, "thinlto-object-suffix-replace")) {
|
||||
ctx.arg.plugin_opt.push_back("thinlto-object-suffix-replace=" + std::string(arg));
|
||||
} else if (read_arg(ctx, args, arg, "thinlto-prefix-replace")) {
|
||||
ctx.arg.plugin_opt.push_back("thinlto-prefix-replace=" + std::string(arg));
|
||||
} else if (read_arg(ctx, args, arg, "thinlto-jobs")) {
|
||||
ctx.arg.plugin_opt.push_back("jobs=" + std::string(arg));
|
||||
} else if (read_arg(ctx, args, arg, "thread-count")) {
|
||||
ctx.arg.thread_count = parse_number(ctx, "thread-count", arg);
|
||||
} else if (read_flag(args, "threads")) {
|
||||
ctx.arg.thread_count = 0;
|
||||
} else if (read_flag(args, "no-threads")) {
|
||||
ctx.arg.thread_count = 1;
|
||||
} else if (args[0].starts_with("-threads=")) {
|
||||
ctx.arg.thread_count = parse_number(ctx, "threads=", args[0].substr(9));
|
||||
args = args.subspan(1);
|
||||
} else if (args[0].starts_with("--threads=")) {
|
||||
ctx.arg.thread_count = parse_number(ctx, "threads=", args[0].substr(10));
|
||||
args = args.subspan(1);
|
||||
} else if (read_eq(ctx, args, arg, "threads")) {
|
||||
ctx.arg.thread_count = parse_number(ctx, "threads", arg);
|
||||
} else if (read_flag(args, "discard-all") || read_flag(args, "x")) {
|
||||
ctx.arg.discard_all = true;
|
||||
} else if (read_flag(args, "discard-locals") || read_flag(args, "X")) {
|
||||
@@ -808,6 +942,8 @@ void parse_nonpositional_args(Context<E> &ctx,
|
||||
read_arg(ctx, args, arg, "F")) {
|
||||
ctx.arg.filter.push_back(arg);
|
||||
} else if (read_flag(args, "preload")) {
|
||||
Warn(ctx) << "--preload is deprecated and will be removed in a"
|
||||
<< " future version";
|
||||
ctx.arg.preload = true;
|
||||
} else if (read_flag(args, "no-preload")) {
|
||||
ctx.arg.preload = false;
|
||||
@@ -818,7 +954,6 @@ void parse_nonpositional_args(Context<E> &ctx,
|
||||
} else if (read_flag(args, "O2")) {
|
||||
} else if (read_flag(args, "verbose")) {
|
||||
} else if (read_flag(args, "color-diagnostics")) {
|
||||
} else if (read_flag(args, "gdb-index")) {
|
||||
} else if (read_flag(args, "eh-frame-hdr")) {
|
||||
} else if (read_flag(args, "start-group")) {
|
||||
} else if (read_flag(args, "end-group")) {
|
||||
@@ -897,10 +1032,12 @@ void parse_nonpositional_args(Context<E> &ctx,
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.arg.shared) {
|
||||
// Remove redundant `/..` or `/.` from library paths.
|
||||
for (std::string &path : ctx.arg.library_paths)
|
||||
path = path_clean(path);
|
||||
|
||||
if (ctx.arg.shared)
|
||||
ctx.arg.pic = true;
|
||||
ctx.arg.dynamic_linker = "";
|
||||
}
|
||||
|
||||
if (ctx.arg.pic)
|
||||
ctx.arg.image_base = 0;
|
||||
@@ -921,7 +1058,7 @@ void parse_nonpositional_args(Context<E> &ctx,
|
||||
}
|
||||
|
||||
if (ctx.arg.image_base % ctx.page_size)
|
||||
Fatal(ctx) << "-image-base msut be a multiple of -max-page-size";
|
||||
Fatal(ctx) << "-image-base must be a multiple of -max-page-size";
|
||||
|
||||
if (char *env = getenv("MOLD_REPRO"); env && env[0])
|
||||
ctx.arg.repro = true;
|
||||
@@ -941,6 +1078,9 @@ void parse_nonpositional_args(Context<E> &ctx,
|
||||
ctx.default_version = VER_NDX_LAST_RESERVED + 1;
|
||||
}
|
||||
|
||||
if (ctx.arg.shared && warn_shared_textrel)
|
||||
ctx.arg.warn_textrel = true;
|
||||
|
||||
std::tie(ctx.plt_hdr_size, ctx.plt_size) = get_plt_size(ctx);
|
||||
|
||||
ctx.arg.undefined.push_back(ctx.arg.entry);
|
||||
@@ -968,6 +1108,7 @@ void parse_nonpositional_args(Context<E> &ctx,
|
||||
INSTANTIATE(X86_64);
|
||||
INSTANTIATE(I386);
|
||||
INSTANTIATE(ARM64);
|
||||
INSTANTIATE(ARM32);
|
||||
INSTANTIATE(RISCV64);
|
||||
|
||||
} // namespace mold::elf
|
||||
@@ -4,10 +4,6 @@
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
#error "mold does not support big-endian hosts"
|
||||
#endif
|
||||
|
||||
namespace mold::elf {
|
||||
|
||||
typedef uint8_t u8;
|
||||
@@ -23,6 +19,7 @@ typedef int64_t i64;
|
||||
struct X86_64;
|
||||
struct I386;
|
||||
struct ARM64;
|
||||
struct ARM32;
|
||||
struct RISCV64;
|
||||
|
||||
template <typename E> struct ElfSym;
|
||||
@@ -70,6 +67,8 @@ static constexpr u32 SHT_GNU_VERDEF = 0x6ffffffd;
|
||||
static constexpr u32 SHT_GNU_VERNEED = 0x6ffffffe;
|
||||
static constexpr u32 SHT_GNU_VERSYM = 0x6fffffff;
|
||||
static constexpr u32 SHT_X86_64_UNWIND = 0x70000001;
|
||||
static constexpr u32 SHT_ARM_EXIDX = 0x70000001;
|
||||
static constexpr u32 SHT_ARM_ATTRIBUTES = 0x70000003;
|
||||
|
||||
static constexpr u32 SHF_WRITE = 0x1;
|
||||
static constexpr u32 SHF_ALLOC = 0x2;
|
||||
@@ -125,6 +124,7 @@ static constexpr u32 PT_TLS = 7;
|
||||
static constexpr u32 PT_GNU_EH_FRAME = 0x6474e550;
|
||||
static constexpr u32 PT_GNU_STACK = 0x6474e551;
|
||||
static constexpr u32 PT_GNU_RELRO = 0x6474e552;
|
||||
static constexpr u32 PT_ARM_EXIDX = 0x70000001;
|
||||
|
||||
static constexpr u32 PF_X = 1;
|
||||
static constexpr u32 PF_W = 2;
|
||||
@@ -144,6 +144,7 @@ static constexpr u32 ELFCLASS64 = 2;
|
||||
static constexpr u32 EV_CURRENT = 1;
|
||||
|
||||
static constexpr u32 EM_386 = 3;
|
||||
static constexpr u32 EM_ARM = 40;
|
||||
static constexpr u32 EM_X86_64 = 62;
|
||||
static constexpr u32 EM_AARCH64 = 183;
|
||||
static constexpr u32 EM_RISCV = 243;
|
||||
@@ -233,6 +234,10 @@ static constexpr u32 GNU_PROPERTY_X86_FEATURE_1_SHSTK = 2;
|
||||
|
||||
static constexpr u32 ELFCOMPRESS_ZLIB = 1;
|
||||
|
||||
static constexpr u32 EF_ARM_ABI_FLOAT_SOFT = 0x00000200;
|
||||
static constexpr u32 EF_ARM_ABI_FLOAT_HARD = 0x00000400;
|
||||
static constexpr u32 EF_ARM_EABI_VER5 = 0x05000000;
|
||||
|
||||
static constexpr u32 EF_RISCV_RVC = 1;
|
||||
static constexpr u32 EF_RISCV_FLOAT_ABI = 6;
|
||||
static constexpr u32 EF_RISCV_FLOAT_ABI_SOFT = 0;
|
||||
@@ -640,6 +645,284 @@ inline std::string rel_to_string<ARM64>(u32 r_type) {
|
||||
return "unknown (" + std::to_string(r_type) + ")";
|
||||
}
|
||||
|
||||
static constexpr u32 R_ARM_NONE = 0x0;
|
||||
static constexpr u32 R_ARM_PC24 = 0x1;
|
||||
static constexpr u32 R_ARM_ABS32 = 0x2;
|
||||
static constexpr u32 R_ARM_REL32 = 0x3;
|
||||
static constexpr u32 R_ARM_LDR_PC_G0 = 0x4;
|
||||
static constexpr u32 R_ARM_ABS16 = 0x5;
|
||||
static constexpr u32 R_ARM_ABS12 = 0x6;
|
||||
static constexpr u32 R_ARM_THM_ABS5 = 0x7;
|
||||
static constexpr u32 R_ARM_ABS8 = 0x8;
|
||||
static constexpr u32 R_ARM_SBREL32 = 0x9;
|
||||
static constexpr u32 R_ARM_THM_CALL = 0xa;
|
||||
static constexpr u32 R_ARM_THM_PC8 = 0xb;
|
||||
static constexpr u32 R_ARM_BREL_ADJ = 0xc;
|
||||
static constexpr u32 R_ARM_TLS_DESC = 0xd;
|
||||
static constexpr u32 R_ARM_THM_SWI8 = 0xe;
|
||||
static constexpr u32 R_ARM_XPC25 = 0xf;
|
||||
static constexpr u32 R_ARM_THM_XPC22 = 0x10;
|
||||
static constexpr u32 R_ARM_TLS_DTPMOD32 = 0x11;
|
||||
static constexpr u32 R_ARM_TLS_DTPOFF32 = 0x12;
|
||||
static constexpr u32 R_ARM_TLS_TPOFF32 = 0x13;
|
||||
static constexpr u32 R_ARM_COPY = 0x14;
|
||||
static constexpr u32 R_ARM_GLOB_DAT = 0x15;
|
||||
static constexpr u32 R_ARM_JUMP_SLOT = 0x16;
|
||||
static constexpr u32 R_ARM_RELATIVE = 0x17;
|
||||
static constexpr u32 R_ARM_GOTOFF32 = 0x18;
|
||||
static constexpr u32 R_ARM_BASE_PREL = 0x19;
|
||||
static constexpr u32 R_ARM_GOT_BREL = 0x1a;
|
||||
static constexpr u32 R_ARM_PLT32 = 0x1b;
|
||||
static constexpr u32 R_ARM_CALL = 0x1c;
|
||||
static constexpr u32 R_ARM_JUMP24 = 0x1d;
|
||||
static constexpr u32 R_ARM_THM_JUMP24 = 0x1e;
|
||||
static constexpr u32 R_ARM_BASE_ABS = 0x1f;
|
||||
static constexpr u32 R_ARM_ALU_PCREL_7_0 = 0x20;
|
||||
static constexpr u32 R_ARM_ALU_PCREL_15_8 = 0x21;
|
||||
static constexpr u32 R_ARM_ALU_PCREL_23_15 = 0x22;
|
||||
static constexpr u32 R_ARM_LDR_SBREL_11_0_NC = 0x23;
|
||||
static constexpr u32 R_ARM_ALU_SBREL_19_12_NC = 0x24;
|
||||
static constexpr u32 R_ARM_ALU_SBREL_27_20_CK = 0x25;
|
||||
static constexpr u32 R_ARM_TARGET1 = 0x26;
|
||||
static constexpr u32 R_ARM_SBREL31 = 0x27;
|
||||
static constexpr u32 R_ARM_V4BX = 0x28;
|
||||
static constexpr u32 R_ARM_TARGET2 = 0x29;
|
||||
static constexpr u32 R_ARM_PREL31 = 0x2a;
|
||||
static constexpr u32 R_ARM_MOVW_ABS_NC = 0x2b;
|
||||
static constexpr u32 R_ARM_MOVT_ABS = 0x2c;
|
||||
static constexpr u32 R_ARM_MOVW_PREL_NC = 0x2d;
|
||||
static constexpr u32 R_ARM_MOVT_PREL = 0x2e;
|
||||
static constexpr u32 R_ARM_THM_MOVW_ABS_NC = 0x2f;
|
||||
static constexpr u32 R_ARM_THM_MOVT_ABS = 0x30;
|
||||
static constexpr u32 R_ARM_THM_MOVW_PREL_NC = 0x31;
|
||||
static constexpr u32 R_ARM_THM_MOVT_PREL = 0x32;
|
||||
static constexpr u32 R_ARM_THM_JUMP19 = 0x33;
|
||||
static constexpr u32 R_ARM_THM_JUMP6 = 0x34;
|
||||
static constexpr u32 R_ARM_THM_ALU_PREL_11_0 = 0x35;
|
||||
static constexpr u32 R_ARM_THM_PC12 = 0x36;
|
||||
static constexpr u32 R_ARM_ABS32_NOI = 0x37;
|
||||
static constexpr u32 R_ARM_REL32_NOI = 0x38;
|
||||
static constexpr u32 R_ARM_ALU_PC_G0_NC = 0x39;
|
||||
static constexpr u32 R_ARM_ALU_PC_G0 = 0x3a;
|
||||
static constexpr u32 R_ARM_ALU_PC_G1_NC = 0x3b;
|
||||
static constexpr u32 R_ARM_ALU_PC_G1 = 0x3c;
|
||||
static constexpr u32 R_ARM_ALU_PC_G2 = 0x3d;
|
||||
static constexpr u32 R_ARM_LDR_PC_G1 = 0x3e;
|
||||
static constexpr u32 R_ARM_LDR_PC_G2 = 0x3f;
|
||||
static constexpr u32 R_ARM_LDRS_PC_G0 = 0x40;
|
||||
static constexpr u32 R_ARM_LDRS_PC_G1 = 0x41;
|
||||
static constexpr u32 R_ARM_LDRS_PC_G2 = 0x42;
|
||||
static constexpr u32 R_ARM_LDC_PC_G0 = 0x43;
|
||||
static constexpr u32 R_ARM_LDC_PC_G1 = 0x44;
|
||||
static constexpr u32 R_ARM_LDC_PC_G2 = 0x45;
|
||||
static constexpr u32 R_ARM_ALU_SB_G0_NC = 0x46;
|
||||
static constexpr u32 R_ARM_ALU_SB_G0 = 0x47;
|
||||
static constexpr u32 R_ARM_ALU_SB_G1_NC = 0x48;
|
||||
static constexpr u32 R_ARM_ALU_SB_G1 = 0x49;
|
||||
static constexpr u32 R_ARM_ALU_SB_G2 = 0x4a;
|
||||
static constexpr u32 R_ARM_LDR_SB_G0 = 0x4b;
|
||||
static constexpr u32 R_ARM_LDR_SB_G1 = 0x4c;
|
||||
static constexpr u32 R_ARM_LDR_SB_G2 = 0x4d;
|
||||
static constexpr u32 R_ARM_LDRS_SB_G0 = 0x4e;
|
||||
static constexpr u32 R_ARM_LDRS_SB_G1 = 0x4f;
|
||||
static constexpr u32 R_ARM_LDRS_SB_G2 = 0x50;
|
||||
static constexpr u32 R_ARM_LDC_SB_G0 = 0x51;
|
||||
static constexpr u32 R_ARM_LDC_SB_G1 = 0x52;
|
||||
static constexpr u32 R_ARM_LDC_SB_G2 = 0x53;
|
||||
static constexpr u32 R_ARM_MOVW_BREL_NC = 0x54;
|
||||
static constexpr u32 R_ARM_MOVT_BREL = 0x55;
|
||||
static constexpr u32 R_ARM_MOVW_BREL = 0x56;
|
||||
static constexpr u32 R_ARM_THM_MOVW_BREL_NC = 0x57;
|
||||
static constexpr u32 R_ARM_THM_MOVT_BREL = 0x58;
|
||||
static constexpr u32 R_ARM_THM_MOVW_BREL = 0x59;
|
||||
static constexpr u32 R_ARM_TLS_GOTDESC = 0x5a;
|
||||
static constexpr u32 R_ARM_TLS_CALL = 0x5b;
|
||||
static constexpr u32 R_ARM_TLS_DESCSEQ = 0x5c;
|
||||
static constexpr u32 R_ARM_THM_TLS_CALL = 0x5d;
|
||||
static constexpr u32 R_ARM_PLT32_ABS = 0x5e;
|
||||
static constexpr u32 R_ARM_GOT_ABS = 0x5f;
|
||||
static constexpr u32 R_ARM_GOT_PREL = 0x60;
|
||||
static constexpr u32 R_ARM_GOT_BREL12 = 0x61;
|
||||
static constexpr u32 R_ARM_GOTOFF12 = 0x62;
|
||||
static constexpr u32 R_ARM_GOTRELAX = 0x63;
|
||||
static constexpr u32 R_ARM_GNU_VTENTRY = 0x64;
|
||||
static constexpr u32 R_ARM_GNU_VTINHERIT = 0x65;
|
||||
static constexpr u32 R_ARM_THM_JUMP11 = 0x66;
|
||||
static constexpr u32 R_ARM_THM_JUMP8 = 0x67;
|
||||
static constexpr u32 R_ARM_TLS_GD32 = 0x68;
|
||||
static constexpr u32 R_ARM_TLS_LDM32 = 0x69;
|
||||
static constexpr u32 R_ARM_TLS_LDO32 = 0x6a;
|
||||
static constexpr u32 R_ARM_TLS_IE32 = 0x6b;
|
||||
static constexpr u32 R_ARM_TLS_LE32 = 0x6c;
|
||||
static constexpr u32 R_ARM_TLS_LDO12 = 0x6d;
|
||||
static constexpr u32 R_ARM_TLS_LE12 = 0x6e;
|
||||
static constexpr u32 R_ARM_TLS_IE12GP = 0x6f;
|
||||
static constexpr u32 R_ARM_PRIVATE_0 = 0x70;
|
||||
static constexpr u32 R_ARM_PRIVATE_1 = 0x71;
|
||||
static constexpr u32 R_ARM_PRIVATE_2 = 0x72;
|
||||
static constexpr u32 R_ARM_PRIVATE_3 = 0x73;
|
||||
static constexpr u32 R_ARM_PRIVATE_4 = 0x74;
|
||||
static constexpr u32 R_ARM_PRIVATE_5 = 0x75;
|
||||
static constexpr u32 R_ARM_PRIVATE_6 = 0x76;
|
||||
static constexpr u32 R_ARM_PRIVATE_7 = 0x77;
|
||||
static constexpr u32 R_ARM_PRIVATE_8 = 0x78;
|
||||
static constexpr u32 R_ARM_PRIVATE_9 = 0x79;
|
||||
static constexpr u32 R_ARM_PRIVATE_10 = 0x7a;
|
||||
static constexpr u32 R_ARM_PRIVATE_11 = 0x7b;
|
||||
static constexpr u32 R_ARM_PRIVATE_12 = 0x7c;
|
||||
static constexpr u32 R_ARM_PRIVATE_13 = 0x7d;
|
||||
static constexpr u32 R_ARM_PRIVATE_14 = 0x7e;
|
||||
static constexpr u32 R_ARM_PRIVATE_15 = 0x7f;
|
||||
static constexpr u32 R_ARM_ME_TOO = 0x80;
|
||||
static constexpr u32 R_ARM_THM_TLS_DESCSEQ16 = 0x81;
|
||||
static constexpr u32 R_ARM_THM_TLS_DESCSEQ32 = 0x82;
|
||||
static constexpr u32 R_ARM_THM_BF16 = 0x88;
|
||||
static constexpr u32 R_ARM_THM_BF12 = 0x89;
|
||||
static constexpr u32 R_ARM_THM_BF18 = 0x8a;
|
||||
static constexpr u32 R_ARM_IRELATIVE = 0xa0;
|
||||
|
||||
template <>
|
||||
inline std::string rel_to_string<ARM32>(u32 r_type) {
|
||||
switch (r_type) {
|
||||
case R_ARM_NONE: return "R_ARM_NONE";
|
||||
case R_ARM_PC24: return "R_ARM_PC24";
|
||||
case R_ARM_ABS32: return "R_ARM_ABS32";
|
||||
case R_ARM_REL32: return "R_ARM_REL32";
|
||||
case R_ARM_LDR_PC_G0: return "R_ARM_LDR_PC_G0";
|
||||
case R_ARM_ABS16: return "R_ARM_ABS16";
|
||||
case R_ARM_ABS12: return "R_ARM_ABS12";
|
||||
case R_ARM_THM_ABS5: return "R_ARM_THM_ABS5";
|
||||
case R_ARM_ABS8: return "R_ARM_ABS8";
|
||||
case R_ARM_SBREL32: return "R_ARM_SBREL32";
|
||||
case R_ARM_THM_CALL: return "R_ARM_THM_CALL";
|
||||
case R_ARM_THM_PC8: return "R_ARM_THM_PC8";
|
||||
case R_ARM_BREL_ADJ: return "R_ARM_BREL_ADJ";
|
||||
case R_ARM_TLS_DESC: return "R_ARM_TLS_DESC";
|
||||
case R_ARM_THM_SWI8: return "R_ARM_THM_SWI8";
|
||||
case R_ARM_XPC25: return "R_ARM_XPC25";
|
||||
case R_ARM_THM_XPC22: return "R_ARM_THM_XPC22";
|
||||
case R_ARM_TLS_DTPMOD32: return "R_ARM_TLS_DTPMOD32";
|
||||
case R_ARM_TLS_DTPOFF32: return "R_ARM_TLS_DTPOFF32";
|
||||
case R_ARM_TLS_TPOFF32: return "R_ARM_TLS_TPOFF32";
|
||||
case R_ARM_COPY: return "R_ARM_COPY";
|
||||
case R_ARM_GLOB_DAT: return "R_ARM_GLOB_DAT";
|
||||
case R_ARM_JUMP_SLOT: return "R_ARM_JUMP_SLOT";
|
||||
case R_ARM_RELATIVE: return "R_ARM_RELATIVE";
|
||||
case R_ARM_GOTOFF32: return "R_ARM_GOTOFF32";
|
||||
case R_ARM_BASE_PREL: return "R_ARM_BASE_PREL";
|
||||
case R_ARM_GOT_BREL: return "R_ARM_GOT_BREL";
|
||||
case R_ARM_PLT32: return "R_ARM_PLT32";
|
||||
case R_ARM_CALL: return "R_ARM_CALL";
|
||||
case R_ARM_JUMP24: return "R_ARM_JUMP24";
|
||||
case R_ARM_THM_JUMP24: return "R_ARM_THM_JUMP24";
|
||||
case R_ARM_BASE_ABS: return "R_ARM_BASE_ABS";
|
||||
case R_ARM_ALU_PCREL_7_0: return "R_ARM_ALU_PCREL_7_0";
|
||||
case R_ARM_ALU_PCREL_15_8: return "R_ARM_ALU_PCREL_15_8";
|
||||
case R_ARM_ALU_PCREL_23_15: return "R_ARM_ALU_PCREL_23_15";
|
||||
case R_ARM_LDR_SBREL_11_0_NC: return "R_ARM_LDR_SBREL_11_0_NC";
|
||||
case R_ARM_ALU_SBREL_19_12_NC: return "R_ARM_ALU_SBREL_19_12_NC";
|
||||
case R_ARM_ALU_SBREL_27_20_CK: return "R_ARM_ALU_SBREL_27_20_CK";
|
||||
case R_ARM_TARGET1: return "R_ARM_TARGET1";
|
||||
case R_ARM_SBREL31: return "R_ARM_SBREL31";
|
||||
case R_ARM_V4BX: return "R_ARM_V4BX";
|
||||
case R_ARM_TARGET2: return "R_ARM_TARGET2";
|
||||
case R_ARM_PREL31: return "R_ARM_PREL31";
|
||||
case R_ARM_MOVW_ABS_NC: return "R_ARM_MOVW_ABS_NC";
|
||||
case R_ARM_MOVT_ABS: return "R_ARM_MOVT_ABS";
|
||||
case R_ARM_MOVW_PREL_NC: return "R_ARM_MOVW_PREL_NC";
|
||||
case R_ARM_MOVT_PREL: return "R_ARM_MOVT_PREL";
|
||||
case R_ARM_THM_MOVW_ABS_NC: return "R_ARM_THM_MOVW_ABS_NC";
|
||||
case R_ARM_THM_MOVT_ABS: return "R_ARM_THM_MOVT_ABS";
|
||||
case R_ARM_THM_MOVW_PREL_NC: return "R_ARM_THM_MOVW_PREL_NC";
|
||||
case R_ARM_THM_MOVT_PREL: return "R_ARM_THM_MOVT_PREL";
|
||||
case R_ARM_THM_JUMP19: return "R_ARM_THM_JUMP19";
|
||||
case R_ARM_THM_JUMP6: return "R_ARM_THM_JUMP6";
|
||||
case R_ARM_THM_ALU_PREL_11_0: return "R_ARM_THM_ALU_PREL_11_0";
|
||||
case R_ARM_THM_PC12: return "R_ARM_THM_PC12";
|
||||
case R_ARM_ABS32_NOI: return "R_ARM_ABS32_NOI";
|
||||
case R_ARM_REL32_NOI: return "R_ARM_REL32_NOI";
|
||||
case R_ARM_ALU_PC_G0_NC: return "R_ARM_ALU_PC_G0_NC";
|
||||
case R_ARM_ALU_PC_G0: return "R_ARM_ALU_PC_G0";
|
||||
case R_ARM_ALU_PC_G1_NC: return "R_ARM_ALU_PC_G1_NC";
|
||||
case R_ARM_ALU_PC_G1: return "R_ARM_ALU_PC_G1";
|
||||
case R_ARM_ALU_PC_G2: return "R_ARM_ALU_PC_G2";
|
||||
case R_ARM_LDR_PC_G1: return "R_ARM_LDR_PC_G1";
|
||||
case R_ARM_LDR_PC_G2: return "R_ARM_LDR_PC_G2";
|
||||
case R_ARM_LDRS_PC_G0: return "R_ARM_LDRS_PC_G0";
|
||||
case R_ARM_LDRS_PC_G1: return "R_ARM_LDRS_PC_G1";
|
||||
case R_ARM_LDRS_PC_G2: return "R_ARM_LDRS_PC_G2";
|
||||
case R_ARM_LDC_PC_G0: return "R_ARM_LDC_PC_G0";
|
||||
case R_ARM_LDC_PC_G1: return "R_ARM_LDC_PC_G1";
|
||||
case R_ARM_LDC_PC_G2: return "R_ARM_LDC_PC_G2";
|
||||
case R_ARM_ALU_SB_G0_NC: return "R_ARM_ALU_SB_G0_NC";
|
||||
case R_ARM_ALU_SB_G0: return "R_ARM_ALU_SB_G0";
|
||||
case R_ARM_ALU_SB_G1_NC: return "R_ARM_ALU_SB_G1_NC";
|
||||
case R_ARM_ALU_SB_G1: return "R_ARM_ALU_SB_G1";
|
||||
case R_ARM_ALU_SB_G2: return "R_ARM_ALU_SB_G2";
|
||||
case R_ARM_LDR_SB_G0: return "R_ARM_LDR_SB_G0";
|
||||
case R_ARM_LDR_SB_G1: return "R_ARM_LDR_SB_G1";
|
||||
case R_ARM_LDR_SB_G2: return "R_ARM_LDR_SB_G2";
|
||||
case R_ARM_LDRS_SB_G0: return "R_ARM_LDRS_SB_G0";
|
||||
case R_ARM_LDRS_SB_G1: return "R_ARM_LDRS_SB_G1";
|
||||
case R_ARM_LDRS_SB_G2: return "R_ARM_LDRS_SB_G2";
|
||||
case R_ARM_LDC_SB_G0: return "R_ARM_LDC_SB_G0";
|
||||
case R_ARM_LDC_SB_G1: return "R_ARM_LDC_SB_G1";
|
||||
case R_ARM_LDC_SB_G2: return "R_ARM_LDC_SB_G2";
|
||||
case R_ARM_MOVW_BREL_NC: return "R_ARM_MOVW_BREL_NC";
|
||||
case R_ARM_MOVT_BREL: return "R_ARM_MOVT_BREL";
|
||||
case R_ARM_MOVW_BREL: return "R_ARM_MOVW_BREL";
|
||||
case R_ARM_THM_MOVW_BREL_NC: return "R_ARM_THM_MOVW_BREL_NC";
|
||||
case R_ARM_THM_MOVT_BREL: return "R_ARM_THM_MOVT_BREL";
|
||||
case R_ARM_THM_MOVW_BREL: return "R_ARM_THM_MOVW_BREL";
|
||||
case R_ARM_TLS_GOTDESC: return "R_ARM_TLS_GOTDESC";
|
||||
case R_ARM_TLS_CALL: return "R_ARM_TLS_CALL";
|
||||
case R_ARM_TLS_DESCSEQ: return "R_ARM_TLS_DESCSEQ";
|
||||
case R_ARM_THM_TLS_CALL: return "R_ARM_THM_TLS_CALL";
|
||||
case R_ARM_PLT32_ABS: return "R_ARM_PLT32_ABS";
|
||||
case R_ARM_GOT_ABS: return "R_ARM_GOT_ABS";
|
||||
case R_ARM_GOT_PREL: return "R_ARM_GOT_PREL";
|
||||
case R_ARM_GOT_BREL12: return "R_ARM_GOT_BREL12";
|
||||
case R_ARM_GOTOFF12: return "R_ARM_GOTOFF12";
|
||||
case R_ARM_GOTRELAX: return "R_ARM_GOTRELAX";
|
||||
case R_ARM_GNU_VTENTRY: return "R_ARM_GNU_VTENTRY";
|
||||
case R_ARM_GNU_VTINHERIT: return "R_ARM_GNU_VTINHERIT";
|
||||
case R_ARM_THM_JUMP11: return "R_ARM_THM_JUMP11";
|
||||
case R_ARM_THM_JUMP8: return "R_ARM_THM_JUMP8";
|
||||
case R_ARM_TLS_GD32: return "R_ARM_TLS_GD32";
|
||||
case R_ARM_TLS_LDM32: return "R_ARM_TLS_LDM32";
|
||||
case R_ARM_TLS_LDO32: return "R_ARM_TLS_LDO32";
|
||||
case R_ARM_TLS_IE32: return "R_ARM_TLS_IE32";
|
||||
case R_ARM_TLS_LE32: return "R_ARM_TLS_LE32";
|
||||
case R_ARM_TLS_LDO12: return "R_ARM_TLS_LDO12";
|
||||
case R_ARM_TLS_LE12: return "R_ARM_TLS_LE12";
|
||||
case R_ARM_TLS_IE12GP: return "R_ARM_TLS_IE12GP";
|
||||
case R_ARM_PRIVATE_0: return "R_ARM_PRIVATE_0";
|
||||
case R_ARM_PRIVATE_1: return "R_ARM_PRIVATE_1";
|
||||
case R_ARM_PRIVATE_2: return "R_ARM_PRIVATE_2";
|
||||
case R_ARM_PRIVATE_3: return "R_ARM_PRIVATE_3";
|
||||
case R_ARM_PRIVATE_4: return "R_ARM_PRIVATE_4";
|
||||
case R_ARM_PRIVATE_5: return "R_ARM_PRIVATE_5";
|
||||
case R_ARM_PRIVATE_6: return "R_ARM_PRIVATE_6";
|
||||
case R_ARM_PRIVATE_7: return "R_ARM_PRIVATE_7";
|
||||
case R_ARM_PRIVATE_8: return "R_ARM_PRIVATE_8";
|
||||
case R_ARM_PRIVATE_9: return "R_ARM_PRIVATE_9";
|
||||
case R_ARM_PRIVATE_10: return "R_ARM_PRIVATE_10";
|
||||
case R_ARM_PRIVATE_11: return "R_ARM_PRIVATE_11";
|
||||
case R_ARM_PRIVATE_12: return "R_ARM_PRIVATE_12";
|
||||
case R_ARM_PRIVATE_13: return "R_ARM_PRIVATE_13";
|
||||
case R_ARM_PRIVATE_14: return "R_ARM_PRIVATE_14";
|
||||
case R_ARM_PRIVATE_15: return "R_ARM_PRIVATE_15";
|
||||
case R_ARM_ME_TOO: return "R_ARM_ME_TOO";
|
||||
case R_ARM_THM_TLS_DESCSEQ16: return "R_ARM_THM_TLS_DESCSEQ16";
|
||||
case R_ARM_THM_TLS_DESCSEQ32: return "R_ARM_THM_TLS_DESCSEQ32";
|
||||
case R_ARM_THM_BF16: return "R_ARM_THM_BF16";
|
||||
case R_ARM_THM_BF12: return "R_ARM_THM_BF12";
|
||||
case R_ARM_THM_BF18: return "R_ARM_THM_BF18";
|
||||
case R_ARM_IRELATIVE: return "R_ARM_IRELATIVE";
|
||||
}
|
||||
return "unknown (" + std::to_string(r_type) + ")";
|
||||
}
|
||||
|
||||
static constexpr u32 R_RISCV_NONE = 0;
|
||||
static constexpr u32 R_RISCV_32 = 1;
|
||||
static constexpr u32 R_RISCV_64 = 2;
|
||||
@@ -767,6 +1050,38 @@ static constexpr u32 DW_EH_PE_datarel = 0x30;
|
||||
static constexpr u32 DW_EH_PE_funcrel = 0x40;
|
||||
static constexpr u32 DW_EH_PE_aligned = 0x50;
|
||||
|
||||
static constexpr u32 DW_AT_low_pc = 0x11;
|
||||
static constexpr u32 DW_AT_high_pc = 0x12;
|
||||
static constexpr u32 DW_AT_producer = 0x25;
|
||||
static constexpr u32 DW_AT_ranges = 0x55;
|
||||
|
||||
static constexpr u32 DW_TAG_compile_unit = 0x11;
|
||||
|
||||
static constexpr u32 DW_FORM_addr = 0x01;
|
||||
static constexpr u32 DW_FORM_block2 = 0x03;
|
||||
static constexpr u32 DW_FORM_block4 = 0x04;
|
||||
static constexpr u32 DW_FORM_data2 = 0x05;
|
||||
static constexpr u32 DW_FORM_data4 = 0x06;
|
||||
static constexpr u32 DW_FORM_data8 = 0x07;
|
||||
static constexpr u32 DW_FORM_string = 0x08;
|
||||
static constexpr u32 DW_FORM_block = 0x09;
|
||||
static constexpr u32 DW_FORM_block1 = 0x0a;
|
||||
static constexpr u32 DW_FORM_data1 = 0x0b;
|
||||
static constexpr u32 DW_FORM_flag = 0x0c;
|
||||
static constexpr u32 DW_FORM_sdata = 0x0d;
|
||||
static constexpr u32 DW_FORM_strp = 0x0e;
|
||||
static constexpr u32 DW_FORM_udata = 0x0f;
|
||||
static constexpr u32 DW_FORM_ref_addr = 0x10;
|
||||
static constexpr u32 DW_FORM_ref1 = 0x11;
|
||||
static constexpr u32 DW_FORM_ref2 = 0x12;
|
||||
static constexpr u32 DW_FORM_ref4 = 0x13;
|
||||
static constexpr u32 DW_FORM_ref8 = 0x14;
|
||||
static constexpr u32 DW_FORM_ref_udata = 0x15;
|
||||
static constexpr u32 DW_FORM_indirect = 0x16;
|
||||
static constexpr u32 DW_FORM_sec_offset = 0x17;
|
||||
static constexpr u32 DW_FORM_exprloc = 0x18;
|
||||
static constexpr u32 DW_FORM_flag_present = 0x19;
|
||||
|
||||
struct Elf64Sym {
|
||||
bool is_defined() const { return !is_undef(); }
|
||||
bool is_undef() const { return st_shndx == SHN_UNDEF; }
|
||||
@@ -990,6 +1305,7 @@ struct X86_64 {
|
||||
static constexpr u32 R_COPY = R_X86_64_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_X86_64_GLOB_DAT;
|
||||
static constexpr u32 R_JUMP_SLOT = R_X86_64_JUMP_SLOT;
|
||||
static constexpr u32 R_ABS = R_X86_64_64;
|
||||
static constexpr u32 R_RELATIVE = R_X86_64_RELATIVE;
|
||||
static constexpr u32 R_IRELATIVE = R_X86_64_IRELATIVE;
|
||||
static constexpr u32 R_DTPOFF = R_X86_64_DTPOFF64;
|
||||
@@ -1001,9 +1317,9 @@ struct X86_64 {
|
||||
static constexpr u32 page_size = 4096;
|
||||
static constexpr u32 e_machine = EM_X86_64;
|
||||
static constexpr u32 pltgot_size = 8;
|
||||
static constexpr u32 tls_offset = -1;
|
||||
static constexpr bool is_rel = false;
|
||||
static constexpr bool is_le = true;
|
||||
static constexpr bool supports_tlsdesc = true;
|
||||
};
|
||||
|
||||
template <> struct ElfSym<X86_64> : public Elf64Sym {};
|
||||
@@ -1021,6 +1337,7 @@ struct I386 {
|
||||
static constexpr u32 R_COPY = R_386_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_386_GLOB_DAT;
|
||||
static constexpr u32 R_JUMP_SLOT = R_386_JUMP_SLOT;
|
||||
static constexpr u32 R_ABS = R_386_32;
|
||||
static constexpr u32 R_RELATIVE = R_386_RELATIVE;
|
||||
static constexpr u32 R_IRELATIVE = R_386_IRELATIVE;
|
||||
static constexpr u32 R_DTPOFF = R_386_TLS_DTPOFF32;
|
||||
@@ -1031,10 +1348,10 @@ struct I386 {
|
||||
static constexpr u32 word_size = 4;
|
||||
static constexpr u32 page_size = 4096;
|
||||
static constexpr u32 e_machine = EM_386;
|
||||
static constexpr u32 tls_offset = -1;
|
||||
static constexpr u32 pltgot_size = 8;
|
||||
static constexpr bool is_rel = true;
|
||||
static constexpr bool is_le = true;
|
||||
static constexpr bool supports_tlsdesc = true;
|
||||
};
|
||||
|
||||
template <> struct ElfSym<I386> : public Elf32Sym {};
|
||||
@@ -1052,6 +1369,7 @@ struct ARM64 {
|
||||
static constexpr u32 R_COPY = R_AARCH64_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_AARCH64_GLOB_DAT;
|
||||
static constexpr u32 R_JUMP_SLOT = R_AARCH64_JUMP_SLOT;
|
||||
static constexpr u32 R_ABS = R_AARCH64_ABS64;
|
||||
static constexpr u32 R_RELATIVE = R_AARCH64_RELATIVE;
|
||||
static constexpr u32 R_IRELATIVE = R_AARCH64_IRELATIVE;
|
||||
static constexpr u32 R_DTPOFF = R_AARCH64_TLS_DTPREL64;
|
||||
@@ -1062,10 +1380,10 @@ struct ARM64 {
|
||||
static constexpr u32 word_size = 8;
|
||||
static constexpr u32 page_size = 65536;
|
||||
static constexpr u32 e_machine = EM_AARCH64;
|
||||
static constexpr u32 tls_offset = 16;
|
||||
static constexpr u32 pltgot_size = 16;
|
||||
static constexpr bool is_rel = false;
|
||||
static constexpr bool is_le = true;
|
||||
static constexpr bool supports_tlsdesc = true;
|
||||
};
|
||||
|
||||
template <> struct ElfSym<ARM64> : public Elf64Sym {};
|
||||
@@ -1076,6 +1394,38 @@ template <> struct ElfRel<ARM64> : public Elf64Rela {};
|
||||
template <> struct ElfDyn<ARM64> : public Elf64Dyn {};
|
||||
template <> struct ElfChdr<ARM64> : public Elf64Chdr {};
|
||||
|
||||
struct ARM32 {
|
||||
using WordTy = u32;
|
||||
|
||||
static constexpr u32 R_NONE = R_ARM_NONE;
|
||||
static constexpr u32 R_COPY = R_ARM_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_ARM_GLOB_DAT;
|
||||
static constexpr u32 R_JUMP_SLOT = R_ARM_JUMP_SLOT;
|
||||
static constexpr u32 R_ABS = R_ARM_ABS32;
|
||||
static constexpr u32 R_RELATIVE = R_ARM_RELATIVE;
|
||||
static constexpr u32 R_IRELATIVE = R_ARM_IRELATIVE;
|
||||
static constexpr u32 R_DTPOFF = R_ARM_TLS_DTPOFF32;
|
||||
static constexpr u32 R_TPOFF = R_ARM_TLS_TPOFF32;
|
||||
static constexpr u32 R_DTPMOD = R_ARM_TLS_DTPMOD32;
|
||||
static constexpr u32 R_TLSDESC = R_ARM_TLS_DESC;
|
||||
|
||||
static constexpr u32 word_size = 4;
|
||||
static constexpr u32 page_size = 4096;
|
||||
static constexpr u32 e_machine = EM_ARM;
|
||||
static constexpr u32 pltgot_size = 16;
|
||||
static constexpr bool is_rel = true;
|
||||
static constexpr bool is_le = true;
|
||||
static constexpr bool supports_tlsdesc = true;
|
||||
};
|
||||
|
||||
template <> struct ElfSym<ARM32> : public Elf32Sym {};
|
||||
template <> struct ElfShdr<ARM32> : public Elf32Shdr {};
|
||||
template <> struct ElfEhdr<ARM32> : public Elf32Ehdr {};
|
||||
template <> struct ElfPhdr<ARM32> : public Elf32Phdr {};
|
||||
template <> struct ElfRel<ARM32> : public Elf32Rel {};
|
||||
template <> struct ElfDyn<ARM32> : public Elf32Dyn {};
|
||||
template <> struct ElfChdr<ARM32> : public Elf32Chdr {};
|
||||
|
||||
struct RISCV64 {
|
||||
using WordTy = u64;
|
||||
|
||||
@@ -1083,6 +1433,7 @@ struct RISCV64 {
|
||||
static constexpr u32 R_COPY = R_RISCV_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_RISCV_64;
|
||||
static constexpr u32 R_JUMP_SLOT = R_RISCV_JUMP_SLOT;
|
||||
static constexpr u32 R_ABS = R_RISCV_64;
|
||||
static constexpr u32 R_RELATIVE = R_RISCV_RELATIVE;
|
||||
static constexpr u32 R_IRELATIVE = R_RISCV_IRELATIVE;
|
||||
static constexpr u32 R_DTPOFF = R_RISCV_TLS_DTPREL64;
|
||||
@@ -1093,9 +1444,9 @@ struct RISCV64 {
|
||||
static constexpr u32 page_size = 4096;
|
||||
static constexpr u32 e_machine = EM_RISCV;
|
||||
static constexpr u32 pltgot_size = 16;
|
||||
static constexpr u32 tls_offset = 0;
|
||||
static constexpr bool is_rel = false;
|
||||
static constexpr bool is_le = true;
|
||||
static constexpr bool supports_tlsdesc = false;
|
||||
};
|
||||
|
||||
template <> struct ElfSym<RISCV64> : public Elf64Sym {};
|
||||
|
||||
+26
-18
@@ -11,24 +11,28 @@ namespace mold::elf {
|
||||
|
||||
template <typename E>
|
||||
static bool is_init_fini(const InputSection<E> &isec) {
|
||||
return isec.shdr().sh_type == SHT_INIT_ARRAY ||
|
||||
isec.shdr().sh_type == SHT_FINI_ARRAY ||
|
||||
isec.shdr().sh_type == SHT_PREINIT_ARRAY ||
|
||||
isec.name().starts_with(".ctors") ||
|
||||
isec.name().starts_with(".dtors") ||
|
||||
isec.name().starts_with(".init") ||
|
||||
isec.name().starts_with(".fini");
|
||||
u32 type = isec.shdr().sh_type;
|
||||
std::string_view name = isec.name();
|
||||
|
||||
return type == SHT_INIT_ARRAY ||
|
||||
type == SHT_FINI_ARRAY ||
|
||||
type == SHT_PREINIT_ARRAY ||
|
||||
(std::is_same_v<E, ARM32> && type == SHT_ARM_EXIDX) ||
|
||||
name.starts_with(".ctors") ||
|
||||
name.starts_with(".dtors") ||
|
||||
name.starts_with(".init") ||
|
||||
name.starts_with(".fini");
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
static bool mark_section(InputSection<E> *isec) {
|
||||
return isec && isec->is_alive && !isec->is_visited.exchange(true);
|
||||
return isec && isec->is_alive && !isec->extra().is_visited.exchange(true);
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
static void visit(Context<E> &ctx, InputSection<E> *isec,
|
||||
tbb::feeder<InputSection<E> *> &feeder, i64 depth) {
|
||||
assert(isec->is_visited);
|
||||
assert(isec->extra().is_visited);
|
||||
|
||||
// A relocation can refer either a section fragment (i.e. a piece of
|
||||
// string in a mergeable string section) or a symbol. Mark all
|
||||
@@ -41,10 +45,10 @@ static void visit(Context<E> &ctx, InputSection<E> *isec,
|
||||
// describing how to handle exceptions for that function.
|
||||
// We want to keep associated .eh_frame records.
|
||||
for (FdeRecord<E> &fde : isec->get_fdes())
|
||||
for (ElfRel<E> &rel : fde.get_rels().subspan(1))
|
||||
for (ElfRel<E> &rel : fde.get_rels(isec->file).subspan(1))
|
||||
if (Symbol<E> *sym = isec->file.symbols[rel.r_sym])
|
||||
if (mark_section(sym->input_section))
|
||||
feeder.add(sym->input_section);
|
||||
if (mark_section(sym->get_input_section()))
|
||||
feeder.add(sym->get_input_section());
|
||||
|
||||
for (ElfRel<E> &rel : isec->get_rels(ctx)) {
|
||||
Symbol<E> &sym = *isec->file.symbols[rel.r_sym];
|
||||
@@ -56,15 +60,15 @@ static void visit(Context<E> &ctx, InputSection<E> *isec,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!mark_section(sym.input_section))
|
||||
if (!mark_section(sym.get_input_section()))
|
||||
continue;
|
||||
|
||||
// Mark a section alive. For better performacne, we don't call
|
||||
// `feeder.add` too often.
|
||||
if (depth < 3)
|
||||
visit(ctx, sym.input_section, feeder, depth + 1);
|
||||
visit(ctx, sym.get_input_section(), feeder, depth + 1);
|
||||
else
|
||||
feeder.add(sym.input_section);
|
||||
feeder.add(sym.get_input_section());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +88,7 @@ collect_root_set(Context<E> &ctx) {
|
||||
if (SectionFragment<E> *frag = sym->get_frag())
|
||||
frag->is_alive.store(true, std::memory_order_relaxed);
|
||||
else
|
||||
enqueue_section(sym->input_section);
|
||||
enqueue_section(sym->get_input_section());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -99,7 +103,7 @@ collect_root_set(Context<E> &ctx) {
|
||||
// use `strip` command, compile without debug info or use
|
||||
// -strip-all linker option.
|
||||
if (!(isec->shdr().sh_flags & SHF_ALLOC))
|
||||
isec->is_visited = true;
|
||||
isec->extra().is_visited = true;
|
||||
|
||||
if (is_init_fini(*isec) || is_c_identifier(isec->name()) ||
|
||||
isec->shdr().sh_type == SHT_NOTE)
|
||||
@@ -155,7 +159,7 @@ static void sweep(Context<E> &ctx) {
|
||||
|
||||
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_visited) {
|
||||
if (isec && isec->is_alive && !isec->extra().is_visited) {
|
||||
if (ctx.arg.print_gc_sections)
|
||||
SyncOut(ctx) << "removing unused section " << *isec;
|
||||
isec->kill();
|
||||
@@ -184,6 +188,9 @@ template <typename E>
|
||||
void gc_sections(Context<E> &ctx) {
|
||||
Timer t(ctx, "gc");
|
||||
|
||||
for (ObjectFile<E> *file : ctx.objs)
|
||||
file->extras.resize(file->sections.size());
|
||||
|
||||
mark_nonalloc_fragments(ctx);
|
||||
|
||||
tbb::concurrent_vector<InputSection<E> *> rootset = collect_root_set(ctx);
|
||||
@@ -197,6 +204,7 @@ void gc_sections(Context<E> &ctx) {
|
||||
INSTANTIATE(X86_64);
|
||||
INSTANTIATE(I386);
|
||||
INSTANTIATE(ARM64);
|
||||
INSTANTIATE(ARM32);
|
||||
INSTANTIATE(RISCV64);
|
||||
|
||||
} // namespace mold::elf
|
||||
+45
-38
@@ -133,7 +133,7 @@ static bool is_leaf(Context<E> &ctx, InputSection<E> &isec) {
|
||||
return false;
|
||||
|
||||
for (FdeRecord<E> &fde : isec.get_fdes())
|
||||
if (fde.get_rels().size() > 1)
|
||||
if (fde.get_rels(isec.file).size() > 1)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@@ -145,10 +145,10 @@ static u64 combine_hash(u64 a, u64 b) {
|
||||
|
||||
template <typename E>
|
||||
struct LeafHasher {
|
||||
size_t operator()(const InputSection<E> *isec) const {
|
||||
size_t operator()(InputSection<E> *isec) const {
|
||||
u64 h = hash_string(isec->contents);
|
||||
for (FdeRecord<E> &fde : isec->get_fdes()) {
|
||||
u64 h2 = hash_string(fde.get_contents().substr(8));
|
||||
u64 h2 = hash_string(fde.get_contents(isec->file).substr(8));
|
||||
h = combine_hash(h, h2);
|
||||
}
|
||||
return h;
|
||||
@@ -157,7 +157,7 @@ struct LeafHasher {
|
||||
|
||||
template <typename E>
|
||||
struct LeafEq {
|
||||
bool operator()(const InputSection<E> *a, const InputSection<E> *b) const {
|
||||
bool operator()(InputSection<E> *a, InputSection<E> *b) const {
|
||||
if (a->contents != b->contents)
|
||||
return false;
|
||||
|
||||
@@ -168,7 +168,8 @@ struct LeafEq {
|
||||
return false;
|
||||
|
||||
for (i64 i = 0; i < x.size(); i++)
|
||||
if (x[i].get_contents().substr(8) != y[i].get_contents().substr(8))
|
||||
if (x[i].get_contents(a->file).substr(8) !=
|
||||
y[i].get_contents(b->file).substr(8))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@@ -197,23 +198,23 @@ static void merge_leaf_nodes(Context<E> &ctx) {
|
||||
|
||||
if (is_leaf(ctx, *isec)) {
|
||||
leaf++;
|
||||
isec->icf_leaf = true;
|
||||
isec->extra().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();
|
||||
} else {
|
||||
eligible++;
|
||||
isec->icf_eligible = true;
|
||||
isec->extra().icf_eligible = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
|
||||
for (std::unique_ptr<InputSection<E>> &isec : ctx.objs[i]->sections) {
|
||||
if (isec && isec->is_alive && isec->icf_leaf) {
|
||||
if (isec && isec->is_alive && isec->extra().icf_leaf) {
|
||||
auto it = map.find(isec.get());
|
||||
assert(it != map.end());
|
||||
isec->leader = it->second;
|
||||
isec->extra().leader = it->second;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -234,7 +235,7 @@ static Digest compute_digest(Context<E> &ctx, InputSection<E> &isec) {
|
||||
};
|
||||
|
||||
auto hash_symbol = [&](Symbol<E> &sym) {
|
||||
InputSection<E> *isec = sym.input_section;
|
||||
InputSection<E> *isec = sym.get_input_section();
|
||||
|
||||
if (!sym.file) {
|
||||
hash('1');
|
||||
@@ -244,10 +245,10 @@ static Digest compute_digest(Context<E> &ctx, InputSection<E> &isec) {
|
||||
hash((u64)frag);
|
||||
} else if (!isec) {
|
||||
hash('3');
|
||||
} else if (isec->leader) {
|
||||
} else if (isec->extra().leader) {
|
||||
hash('4');
|
||||
hash((u64)isec->leader);
|
||||
} else if (isec->icf_eligible) {
|
||||
hash((u64)isec->extra().leader);
|
||||
} else if (isec->extra().icf_eligible) {
|
||||
hash('5');
|
||||
} else {
|
||||
hash('6');
|
||||
@@ -262,19 +263,19 @@ static Digest compute_digest(Context<E> &ctx, InputSection<E> &isec) {
|
||||
hash(isec.get_rels(ctx).size());
|
||||
|
||||
for (FdeRecord<E> &fde : isec.get_fdes()) {
|
||||
hash(fde.cie->icf_idx);
|
||||
hash(isec.file.cies[fde.cie_idx].icf_idx);
|
||||
|
||||
// Bytes 0 to 4 contain the length of this record, and
|
||||
// bytes 4 to 8 contain an offset to CIE.
|
||||
hash_string(fde.get_contents().substr(8));
|
||||
hash_string(fde.get_contents(isec.file).substr(8));
|
||||
|
||||
hash(fde.get_rels().size());
|
||||
hash(fde.get_rels(isec.file).size());
|
||||
|
||||
for (ElfRel<E> &rel : fde.get_rels().subspan(1)) {
|
||||
for (ElfRel<E> &rel : fde.get_rels(isec.file).subspan(1)) {
|
||||
hash_symbol(*isec.file.symbols[rel.r_sym]);
|
||||
hash(rel.r_type);
|
||||
hash(rel.r_offset - fde.input_offset);
|
||||
hash(fde.cie->input_section.get_addend(rel));
|
||||
hash(isec.file.cies[fde.cie_idx].input_section.get_addend(rel));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,7 +309,7 @@ static std::vector<InputSection<E> *> gather_sections(Context<E> &ctx) {
|
||||
|
||||
tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
|
||||
for (std::unique_ptr<InputSection<E>> &isec : ctx.objs[i]->sections)
|
||||
if (isec && isec->is_alive && isec->icf_eligible)
|
||||
if (isec && isec->is_alive && isec->extra().icf_eligible)
|
||||
num_sections[i]++;
|
||||
});
|
||||
|
||||
@@ -323,12 +324,12 @@ static std::vector<InputSection<E> *> gather_sections(Context<E> &ctx) {
|
||||
tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
|
||||
i64 idx = section_indices[i];
|
||||
for (std::unique_ptr<InputSection<E>> &isec : ctx.objs[i]->sections)
|
||||
if (isec && isec->is_alive && isec->icf_eligible)
|
||||
if (isec && isec->is_alive && isec->extra().icf_eligible)
|
||||
sections[idx++] = isec.get();
|
||||
});
|
||||
|
||||
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
|
||||
sections[i]->icf_idx = i;
|
||||
sections[i]->extra().icf_idx = i;
|
||||
});
|
||||
|
||||
return sections;
|
||||
@@ -361,7 +362,7 @@ static void gather_edges(Context<E> &ctx,
|
||||
|
||||
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
|
||||
InputSection<E> &isec = *sections[i];
|
||||
assert(isec.icf_eligible);
|
||||
assert(isec.extra().icf_eligible);
|
||||
i64 frag_idx = 0;
|
||||
|
||||
for (i64 j = 0; j < isec.get_rels(ctx).size(); j++) {
|
||||
@@ -370,9 +371,10 @@ static void gather_edges(Context<E> &ctx,
|
||||
} else {
|
||||
ElfRel<E> &rel = isec.get_rels(ctx)[j];
|
||||
Symbol<E> &sym = *isec.file.symbols[rel.r_sym];
|
||||
if (!sym.get_frag() && sym.input_section &&
|
||||
sym.input_section->icf_eligible)
|
||||
num_edges[i]++;
|
||||
if (!sym.get_frag())
|
||||
if (InputSection<E> *isec = sym.get_input_section())
|
||||
if (isec->extra().icf_eligible)
|
||||
num_edges[i]++;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -392,9 +394,10 @@ static void gather_edges(Context<E> &ctx,
|
||||
frag_idx++;
|
||||
ElfRel<E> &rel = isec.get_rels(ctx)[j];
|
||||
Symbol<E> &sym = *isec.file.symbols[rel.r_sym];
|
||||
if (!sym.get_frag() && sym.input_section &&
|
||||
sym.input_section->icf_eligible)
|
||||
edges[idx++] = sym.input_section->icf_idx;
|
||||
if (!sym.get_frag())
|
||||
if (InputSection<E> *isec = sym.get_input_section())
|
||||
if (isec->extra().icf_eligible)
|
||||
edges[idx++] = isec->extra().icf_idx;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -455,11 +458,11 @@ static void print_icf_sections(Context<E> &ctx) {
|
||||
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
for (std::unique_ptr<InputSection<E>> &isec : file->sections) {
|
||||
if (isec && isec->is_alive && isec->leader) {
|
||||
if (isec.get() == isec->leader)
|
||||
if (isec && isec->is_alive && isec->extra().leader) {
|
||||
if (isec.get() == isec->extra().leader)
|
||||
leaders.push_back(isec.get());
|
||||
else
|
||||
map.insert({isec->leader, isec.get()});
|
||||
map.insert({isec->extra().leader, isec.get()});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -493,6 +496,9 @@ template <typename E>
|
||||
void icf_sections(Context<E> &ctx) {
|
||||
Timer t(ctx, "icf");
|
||||
|
||||
for (ObjectFile<E> *file : ctx.objs)
|
||||
file->extras.resize(file->sections.size());
|
||||
|
||||
uniquify_cies(ctx);
|
||||
merge_leaf_nodes(ctx);
|
||||
|
||||
@@ -552,7 +558,7 @@ void icf_sections(Context<E> &ctx) {
|
||||
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
|
||||
auto it = map->find(digest[i]);
|
||||
assert(it != map->end());
|
||||
sections[i]->leader = it->second;
|
||||
sections[i]->extra().leader = it->second;
|
||||
});
|
||||
|
||||
// Since free'ing the map is slow, postpone it.
|
||||
@@ -567,12 +573,12 @@ void icf_sections(Context<E> &ctx) {
|
||||
Timer t(ctx, "reassign");
|
||||
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
|
||||
for (Symbol<E> *sym : file->symbols) {
|
||||
if (sym->file != file)
|
||||
continue;
|
||||
InputSection<E> *isec = sym->input_section;
|
||||
if (isec && isec->leader && isec->leader != isec) {
|
||||
sym->input_section = isec->leader;
|
||||
isec->kill();
|
||||
if (sym->file == file) {
|
||||
InputSection<E> *isec = sym->get_input_section();
|
||||
if (isec && isec->extra().leader && isec->extra().leader != isec) {
|
||||
isec->kill();
|
||||
isec->killed_by_icf = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -585,6 +591,7 @@ void icf_sections(Context<E> &ctx) {
|
||||
INSTANTIATE(X86_64);
|
||||
INSTANTIATE(I386);
|
||||
INSTANTIATE(ARM64);
|
||||
INSTANTIATE(ARM32);
|
||||
INSTANTIATE(RISCV64);
|
||||
|
||||
} // namespace mold::elf
|
||||
+112
-38
@@ -47,11 +47,12 @@ ElfShdr<E> *InputFile<E>::find_section(i64 type) {
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void InputFile<E>::clear_symbols(Context<E> &ctx) {
|
||||
void InputFile<E>::clear_symbols() {
|
||||
for (Symbol<E> *sym : get_global_syms()) {
|
||||
std::scoped_lock lock(sym->mu);
|
||||
if (sym->file == this) {
|
||||
sym->file = nullptr;
|
||||
sym->input_section = nullptr;
|
||||
sym->shndx = 0;
|
||||
sym->value = -1;
|
||||
sym->sym_idx = -1;
|
||||
sym->ver_idx = 0;
|
||||
@@ -74,7 +75,7 @@ ObjectFile<E> *
|
||||
ObjectFile<E>::create(Context<E> &ctx, MappedFile<Context<E>> *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.push_back(std::unique_ptr<ObjectFile<E>>(obj));
|
||||
ctx.obj_pool.emplace_back(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -156,10 +157,25 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
|
||||
case SHT_REL:
|
||||
case SHT_RELA:
|
||||
case SHT_NULL:
|
||||
case SHT_ARM_ATTRIBUTES:
|
||||
break;
|
||||
default: {
|
||||
std::string_view name = this->shstrtab.data() + shdr.sh_name;
|
||||
if (name == ".note.GNU-stack" || name.starts_with(".gnu.warning."))
|
||||
|
||||
// .note.GNU-stack section controls executable-ness of the stack
|
||||
// area in GNU linkers. We ignore that section because silently
|
||||
// making the stack area executable is too dangerous. Tell our
|
||||
// users about the difference if that matters.
|
||||
if (name == ".note.GNU-stack") {
|
||||
if (shdr.sh_flags & SHF_EXECINSTR)
|
||||
Warn(ctx) << *this << ": this file may cause a segmentation"
|
||||
" fault because it requires an executable stack. See"
|
||||
" https://github.com/rui314/mold/tree/main/docs/execstack.md"
|
||||
" for more info.";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (name.starts_with(".gnu.warning."))
|
||||
continue;
|
||||
|
||||
if (name == ".note.gnu.property") {
|
||||
@@ -176,12 +192,38 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
|
||||
if (name == ".gnu.linkonce.d.DW.ref.__gxx_personality_v0")
|
||||
continue;
|
||||
|
||||
// Ignore debug sections if --strip-all or --strip-debug is given.
|
||||
if ((ctx.arg.strip_all || ctx.arg.strip_debug) &&
|
||||
is_debug_section(shdr, name))
|
||||
continue;
|
||||
|
||||
this->sections[i] = std::make_unique<InputSection<E>>(ctx, *this, name, i);
|
||||
|
||||
// Save debug sections for --gdb-index.
|
||||
if (ctx.arg.gdb_index) {
|
||||
InputSection<E> *isec = this->sections[i].get();
|
||||
|
||||
if (name == ".debug_info")
|
||||
debug_info = isec;
|
||||
if (name == ".debug_abbrev")
|
||||
debug_abbrev = isec;
|
||||
if (name == ".debug_ranges")
|
||||
debug_ranges = isec;
|
||||
|
||||
// If --gdb-index is given, contents of .debug_gnu_pubnames and
|
||||
// .debug_gnu_pubtypes are copied to .gdb_index, so keeping them
|
||||
// in an output file is just a waste of space.
|
||||
if (name == ".debug_gnu_pubnames") {
|
||||
debug_pubnames = isec;
|
||||
isec->is_alive = false;
|
||||
}
|
||||
|
||||
if (name == ".debug_gnu_pubtypes") {
|
||||
debug_pubtypes = isec;
|
||||
isec->is_alive = false;
|
||||
}
|
||||
}
|
||||
|
||||
static Counter counter("regular_sections");
|
||||
counter++;
|
||||
break;
|
||||
@@ -202,12 +244,6 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
|
||||
if (std::unique_ptr<InputSection<E>> &target = sections[shdr.sh_info]) {
|
||||
assert(target->relsec_idx == -1);
|
||||
target->relsec_idx = i;
|
||||
|
||||
if (target->shdr().sh_flags & SHF_ALLOC) {
|
||||
i64 size = shdr.sh_size / sizeof(ElfRel<E>);
|
||||
target->needs_dynrel.resize(size);
|
||||
target->needs_baserel.resize(size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -218,13 +254,9 @@ void ObjectFile<E>::initialize_ehframe_sections(Context<E> &ctx) {
|
||||
std::unique_ptr<InputSection<E>> &isec = sections[i];
|
||||
if (isec && isec->is_alive && isec->name() == ".eh_frame") {
|
||||
read_ehframe(ctx, *isec);
|
||||
isec->is_ehframe = true;
|
||||
isec->is_alive = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (FdeRecord<E> &fde : fdes)
|
||||
fde.cie = &cies[fde.cie_idx];
|
||||
}
|
||||
|
||||
// .eh_frame contains data records explaining how to handle exceptions.
|
||||
@@ -373,8 +405,10 @@ void ObjectFile<E>::initialize_symbols(Context<E> &ctx) {
|
||||
|
||||
for (i64 i = 1; i < this->first_global; i++) {
|
||||
const ElfSym<E> &esym = this->elf_syms[i];
|
||||
std::string_view name = symbol_strtab.data() + esym.st_name;
|
||||
if (esym.is_common())
|
||||
Fatal(ctx) << *this << ": common local symbol?";
|
||||
|
||||
std::string_view name = symbol_strtab.data() + esym.st_name;
|
||||
if (name.empty() && esym.st_type == STT_SECTION)
|
||||
if (InputSection<E> *sec = get_section(esym))
|
||||
name = sec->name();
|
||||
@@ -385,11 +419,8 @@ void ObjectFile<E>::initialize_symbols(Context<E> &ctx) {
|
||||
sym.value = esym.st_value;
|
||||
sym.sym_idx = i;
|
||||
|
||||
if (!esym.is_abs()) {
|
||||
if (esym.is_common())
|
||||
Fatal(ctx) << *this << ": common local symbol?";
|
||||
sym.input_section = get_section(esym);
|
||||
}
|
||||
if (!esym.is_abs())
|
||||
sym.shndx = esym.is_abs() ? 0 : get_shndx(esym);
|
||||
}
|
||||
|
||||
this->symbols.resize(this->elf_syms.size());
|
||||
@@ -433,7 +464,7 @@ void ObjectFile<E>::initialize_symbols(Context<E> &ctx) {
|
||||
// We expect them to be sorted, so sort them if necessary.
|
||||
template <typename E>
|
||||
void ObjectFile<E>::sort_relocations(Context<E> &ctx) {
|
||||
if (E::e_machine != EM_RISCV)
|
||||
if (!std::is_same_v<E, RISCV64>)
|
||||
return;
|
||||
|
||||
auto less = [&](const ElfRel<E> &a, const ElfRel<E> &b) {
|
||||
@@ -452,8 +483,8 @@ void ObjectFile<E>::sort_relocations(Context<E> &ctx) {
|
||||
if (std::is_sorted(rels.begin(), rels.end(), less))
|
||||
continue;
|
||||
|
||||
sorted_rels[isec->section_idx] = {rels.begin(), rels.end()};
|
||||
sort(sorted_rels[isec->section_idx], less);
|
||||
sorted_rels[isec->shndx] = {rels.begin(), rels.end()};
|
||||
sort(sorted_rels[isec->shndx], less);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -607,12 +638,19 @@ void ObjectFile<E>::initialize_mergeable_sections(Context<E> &ctx) {
|
||||
|
||||
template <typename E>
|
||||
void ObjectFile<E>::register_section_pieces(Context<E> &ctx) {
|
||||
for (std::unique_ptr<MergeableSection<E>> &m : mergeable_sections)
|
||||
if (m)
|
||||
for (std::unique_ptr<MergeableSection<E>> &m : mergeable_sections) {
|
||||
if (m) {
|
||||
m->fragments.reserve(m->strings.size());
|
||||
for (i64 i = 0; i < m->strings.size(); i++)
|
||||
m->fragments.push_back(m->parent->insert(m->strings[i], m->hashes[i],
|
||||
m->p2align));
|
||||
|
||||
// Shrink vectors that we will never use again to reclaim memory.
|
||||
m->strings.clear();
|
||||
m->hashes.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize rel_fragments
|
||||
for (std::unique_ptr<InputSection<E>> &isec : sections) {
|
||||
if (!isec || !isec->is_alive || !(isec->shdr().sh_flags & SHF_ALLOC))
|
||||
@@ -756,6 +794,11 @@ static u64 get_rank(const Symbol<E> &sym) {
|
||||
return get_rank(sym.file, sym.esym(), !sym.file->is_alive);
|
||||
}
|
||||
|
||||
// Symbol's visibility is set to the most restrictive one. For example,
|
||||
// if one input file has a defined symbol `foo` with the default
|
||||
// visibility and the other input file has an undefined symbol `foo`
|
||||
// with the hidden visibility, the resulting symbol is a hidden defined
|
||||
// symbol.
|
||||
template <typename E>
|
||||
void ObjectFile<E>::merge_visibility(Context<E> &ctx, Symbol<E> &sym,
|
||||
u8 visibility) {
|
||||
@@ -810,7 +853,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)) {
|
||||
sym.file = this;
|
||||
sym.input_section = isec;
|
||||
sym.shndx = isec ? isec->shndx : 0;
|
||||
sym.value = esym.st_value;
|
||||
sym.sym_idx = i;
|
||||
sym.ver_idx = ctx.default_version;
|
||||
@@ -857,6 +900,16 @@ ObjectFile<E>::mark_live_objects(Context<E> &ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// Comdat groups are used to de-duplicate functions and data that may
|
||||
// be included into multiple object files. C++ compiler uses comdat
|
||||
// groups to de-duplicate instantiated templates.
|
||||
//
|
||||
// For example, if a compiler decides to instantiate `std::vector<int>`,
|
||||
// it generates code and data for `std::vector<int>` and put them into a
|
||||
// comdat group whose name is the mangled name of `std::vector<int>`.
|
||||
// The instantiation may happen multiple times for different translation
|
||||
// units. Then linker de-duplicates them so that the resulting executable
|
||||
// contains only a single copy of `std::vector<int>`.
|
||||
template <typename E>
|
||||
void ObjectFile<E>::resolve_comdat_groups() {
|
||||
for (auto &pair : comdat_groups) {
|
||||
@@ -918,7 +971,7 @@ void ObjectFile<E>::claim_unresolved_symbols(Context<E> &ctx) {
|
||||
|
||||
auto claim = [&] {
|
||||
sym.file = this;
|
||||
sym.input_section = nullptr;
|
||||
sym.shndx = 0;
|
||||
sym.value = 0;
|
||||
sym.sym_idx = i;
|
||||
sym.is_weak = false;
|
||||
@@ -956,7 +1009,7 @@ void ObjectFile<E>::claim_unresolved_symbols(Context<E> &ctx) {
|
||||
|
||||
// Convert remaining undefined symbols to absolute symbols with value 0.
|
||||
if (ctx.arg.unresolved_symbols != UNRESOLVED_ERROR ||
|
||||
esym.is_undef_weak()) {
|
||||
ctx.arg.noinhibit_exec || esym.is_undef_weak()) {
|
||||
claim();
|
||||
sym.ver_idx = ctx.default_version;
|
||||
sym.is_imported = false;
|
||||
@@ -987,6 +1040,24 @@ void ObjectFile<E>::scan_relocations(Context<E> &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
// Common symbols are used by C's tantative definitions. Tentative
|
||||
// definition is an obscure C feature which allows users to omit `extern`
|
||||
// from global variable declarations in a header file. For example, if you
|
||||
// have a tentative definition `int foo;` in a header which is included
|
||||
// into multiple translation units, `foo` will be included into multiple
|
||||
// object files, but it won't cause the duplicate symbol error. Instead,
|
||||
// the linker will merge them into a single instance of `foo`.
|
||||
//
|
||||
// If a header file contains a tentative definition `int foo;` and one of
|
||||
// a C file contains a definition with initial value such as `int foo = 5;`,
|
||||
// then the "real" definition wins. The symbol for the tentative definition
|
||||
// will be resolved to the real definition. If there is no "real"
|
||||
// definition, the tentative definition gets the default initial value 0.
|
||||
//
|
||||
// Tentative definitions are represented as "common symbols" in an object
|
||||
// file. In this function, we allocate spaces in .bss for remaining common
|
||||
// symbols that were not resolved to usual defined symbols in previous
|
||||
// passes.
|
||||
template <typename E>
|
||||
void ObjectFile<E>::convert_common_symbols(Context<E> &ctx) {
|
||||
if (!has_common_symbol)
|
||||
@@ -1001,6 +1072,8 @@ void ObjectFile<E>::convert_common_symbols(Context<E> &ctx) {
|
||||
continue;
|
||||
|
||||
Symbol<E> &sym = *this->symbols[i];
|
||||
std::scoped_lock lock(sym.mu);
|
||||
|
||||
if (sym.file != this) {
|
||||
if (ctx.arg.warn_common)
|
||||
Warn(ctx) << *this << ": multiple common symbols: " << sym;
|
||||
@@ -1022,7 +1095,7 @@ void ObjectFile<E>::convert_common_symbols(Context<E> &ctx) {
|
||||
isec->output_section = osec;
|
||||
|
||||
sym.file = this;
|
||||
sym.input_section = isec.get();
|
||||
sym.shndx = idx;
|
||||
sym.value = 0;
|
||||
sym.sym_idx = i;
|
||||
sym.ver_idx = ctx.default_version;
|
||||
@@ -1049,7 +1122,7 @@ static bool should_write_to_local_symtab(Context<E> &ctx, Symbol<E> &sym) {
|
||||
if (ctx.arg.discard_locals)
|
||||
return false;
|
||||
|
||||
if (InputSection<E> *isec = sym.input_section)
|
||||
if (InputSection<E> *isec = sym.get_input_section())
|
||||
if (isec->shdr().sh_flags & SHF_MERGE)
|
||||
return false;
|
||||
}
|
||||
@@ -1068,8 +1141,8 @@ void ObjectFile<E>::compute_symtab(Context<E> &ctx) {
|
||||
|
||||
if (SectionFragment<E> *frag = sym.get_frag())
|
||||
return frag->is_alive;
|
||||
if (sym.input_section)
|
||||
return sym.input_section->is_alive;
|
||||
if (InputSection<E> *isec = sym.get_input_section())
|
||||
return isec->is_alive;
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -1118,10 +1191,10 @@ void ObjectFile<E>::write_symtab(Context<E> &ctx) {
|
||||
else
|
||||
esym.st_value = sym.get_addr(ctx);
|
||||
|
||||
if (sym.input_section)
|
||||
esym.st_shndx = sym.input_section->output_section->shndx;
|
||||
else if (sym.shndx)
|
||||
esym.st_shndx = sym.shndx;
|
||||
if (InputSection<E> *isec = sym.get_input_section())
|
||||
esym.st_shndx = isec->output_section->shndx;
|
||||
else if (sym.shndx < 0)
|
||||
esym.st_shndx = -sym.shndx;
|
||||
else if (esym.is_undef())
|
||||
esym.st_shndx = SHN_UNDEF;
|
||||
else
|
||||
@@ -1171,7 +1244,7 @@ template <typename E>
|
||||
SharedFile<E> *
|
||||
SharedFile<E>::create(Context<E> &ctx, MappedFile<Context<E>> *mf) {
|
||||
SharedFile<E> *obj = new SharedFile(ctx, mf);
|
||||
ctx.dso_pool.push_back(std::unique_ptr<SharedFile<E>>(obj));
|
||||
ctx.dso_pool.emplace_back(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -1282,7 +1355,7 @@ void SharedFile<E>::resolve_symbols(Context<E> &ctx) {
|
||||
|
||||
if (get_rank(this, esym, false) < get_rank(sym)) {
|
||||
sym.file = this;
|
||||
sym.input_section = nullptr;
|
||||
sym.shndx = 0;
|
||||
sym.value = esym.st_value;
|
||||
sym.sym_idx = i;
|
||||
sym.ver_idx = versyms[i];
|
||||
@@ -1390,6 +1463,7 @@ void SharedFile<E>::write_symtab(Context<E> &ctx) {
|
||||
INSTANTIATE(X86_64);
|
||||
INSTANTIATE(I386);
|
||||
INSTANTIATE(ARM64);
|
||||
INSTANTIATE(ARM32);
|
||||
INSTANTIATE(RISCV64);
|
||||
|
||||
} // namespace mold::elf
|
||||
+35
-18
@@ -33,22 +33,36 @@ static inline i64 to_p2align(u64 alignment) {
|
||||
|
||||
template <typename E>
|
||||
InputSection<E>::InputSection(Context<E> &ctx, ObjectFile<E> &file,
|
||||
std::string_view name, i64 section_idx)
|
||||
: file(file), nameptr(name.data()), namelen(name.size()),
|
||||
section_idx(section_idx) {
|
||||
if (section_idx < file.elf_sections.size())
|
||||
std::string_view name, i64 shndx)
|
||||
: file(file), shndx(shndx) {
|
||||
if (shndx < file.elf_sections.size())
|
||||
contents = {(char *)file.mf->data + shdr().sh_offset, shdr().sh_size};
|
||||
|
||||
bool compressed;
|
||||
|
||||
if (name.starts_with(".zdebug")) {
|
||||
sh_size = *(ubig64 *)&contents[4];
|
||||
p2align = to_p2align(shdr().sh_addralign);
|
||||
compressed = true;
|
||||
} else if (shdr().sh_flags & SHF_COMPRESSED) {
|
||||
ElfChdr<E> &chdr = *(ElfChdr<E> *)&contents[0];
|
||||
sh_size = chdr.ch_size;
|
||||
p2align = to_p2align(chdr.ch_addralign);
|
||||
compressed = true;
|
||||
} else {
|
||||
sh_size = shdr().sh_size;
|
||||
p2align = to_p2align(shdr().sh_addralign);
|
||||
compressed = false;
|
||||
}
|
||||
|
||||
// Uncompress early if the relocation is REL-type so that we can read
|
||||
// addends from section contents. If RELA-type, we don't need to do this
|
||||
// because addends are in relocations.
|
||||
if (compressed && E::is_rel) {
|
||||
u8 *buf = new u8[sh_size];
|
||||
uncompress(ctx, buf);
|
||||
contents = {(char *)buf, sh_size};
|
||||
ctx.string_pool.emplace_back(buf);
|
||||
}
|
||||
|
||||
output_section =
|
||||
@@ -57,7 +71,8 @@ InputSection<E>::InputSection(Context<E> &ctx, ObjectFile<E> &file,
|
||||
|
||||
template <typename E>
|
||||
bool InputSection<E>::is_compressed() {
|
||||
return name().starts_with(".zdebug") || (shdr().sh_flags & SHF_COMPRESSED);
|
||||
return !E::is_rel &&
|
||||
(name().starts_with(".zdebug") || (shdr().sh_flags & SHF_COMPRESSED));
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -116,12 +131,10 @@ void InputSection<E>::dispatch(Context<E> &ctx, Action table[3][4], i64 i,
|
||||
bool is_writable = (shdr().sh_flags & SHF_WRITE);
|
||||
|
||||
auto error = [&] {
|
||||
if (sym.is_absolute())
|
||||
Error(ctx) << *this << ": " << rel << " relocation against symbol `"
|
||||
<< sym << "' can not be used; recompile with -fno-PIC";
|
||||
else
|
||||
Error(ctx) << *this << ": " << rel << " relocation against symbol `"
|
||||
<< sym << "' can not be used; recompile with -fPIC";
|
||||
std::string msg = sym.is_absolute() ? "-fno-PIC" : "-fPIC";
|
||||
Error(ctx) << *this << ": " << rel << " relocation at offset 0x"
|
||||
<< std::hex << rel.r_offset << " against symbol `"
|
||||
<< sym << "' can not be used; recompile with " << msg;
|
||||
};
|
||||
|
||||
auto warn_textrel = [&] {
|
||||
@@ -155,6 +168,12 @@ void InputSection<E>::dispatch(Context<E> &ctx, Action table[3][4], i64 i,
|
||||
case PLT:
|
||||
sym.flags |= NEEDS_PLT;
|
||||
return;
|
||||
case CPLT: {
|
||||
std::scoped_lock lock(sym.mu);
|
||||
sym.flags |= NEEDS_PLT;
|
||||
sym.is_canonical = true;
|
||||
return;
|
||||
}
|
||||
case DYNREL:
|
||||
if (!is_writable) {
|
||||
if (ctx.arg.z_text) {
|
||||
@@ -166,7 +185,6 @@ void InputSection<E>::dispatch(Context<E> &ctx, Action table[3][4], i64 i,
|
||||
}
|
||||
|
||||
assert(sym.is_imported);
|
||||
needs_dynrel[i] = true;
|
||||
file.num_dynrel++;
|
||||
return;
|
||||
case BASEREL:
|
||||
@@ -179,7 +197,6 @@ void InputSection<E>::dispatch(Context<E> &ctx, Action table[3][4], i64 i,
|
||||
ctx.has_textrel = true;
|
||||
}
|
||||
|
||||
needs_baserel[i] = true;
|
||||
if (!is_relr_reloc(ctx, rel))
|
||||
file.num_dynrel++;
|
||||
return;
|
||||
@@ -194,13 +211,12 @@ void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
|
||||
return;
|
||||
|
||||
// Copy data
|
||||
if constexpr (E::e_machine == EM_RISCV) {
|
||||
if constexpr (std::is_same_v<E, RISCV64>) {
|
||||
copy_contents_riscv(ctx, buf);
|
||||
} else if (is_compressed()) {
|
||||
uncompress(ctx, buf);
|
||||
} else {
|
||||
if (is_compressed())
|
||||
uncompress(ctx, buf);
|
||||
else
|
||||
memcpy(buf, contents.data(), contents.size());
|
||||
memcpy(buf, contents.data(), contents.size());
|
||||
}
|
||||
|
||||
// Apply relocations
|
||||
@@ -236,6 +252,7 @@ void report_undef(Context<E> &ctx, InputFile<E> &file, Symbol<E> &sym) {
|
||||
INSTANTIATE(X86_64);
|
||||
INSTANTIATE(I386);
|
||||
INSTANTIATE(ARM64);
|
||||
INSTANTIATE(ARM32);
|
||||
INSTANTIATE(RISCV64);
|
||||
|
||||
} // namespace mold::elf
|
||||
@@ -408,6 +408,7 @@ void parse_dynamic_list(Context<E> &ctx, std::string path) {
|
||||
INSTANTIATE(X86_64);
|
||||
INSTANTIATE(I386);
|
||||
INSTANTIATE(ARM64);
|
||||
INSTANTIATE(ARM32);
|
||||
INSTANTIATE(RISCV64);
|
||||
|
||||
} // namespace mold::elf
|
||||
+43
-25
@@ -104,7 +104,6 @@ template <typename E> static Context<E> *gctx;
|
||||
template <typename E> static std::vector<ObjectFile<E> *> lto_objects;
|
||||
|
||||
static int phase = 0;
|
||||
static void *dlopen_handle;
|
||||
static std::vector<PluginSymbol> plugin_symbols;
|
||||
static ClaimFileHandler *claim_file_hook;
|
||||
static AllSymbolsReadHandler *all_symbols_read_hook;
|
||||
@@ -116,6 +115,7 @@ static PluginStatus message(int level, const char *fmt, ...) {
|
||||
LOG << "message\n";
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr, "mold: ");
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
return LDPS_OK;
|
||||
@@ -161,7 +161,7 @@ static PluginStatus add_input_file(const char *path) {
|
||||
MappedFile<Context<E>> *mf = MappedFile<Context<E>>::must_open(ctx, path);
|
||||
|
||||
ObjectFile<E> *file = ObjectFile<E>::create(ctx, mf, "", false);
|
||||
ctx.obj_pool.push_back(std::unique_ptr<ObjectFile<E>>(file));
|
||||
ctx.obj_pool.emplace_back(file);
|
||||
lto_objects<E>.push_back(file);
|
||||
|
||||
file->priority = file_priority++;
|
||||
@@ -365,11 +365,11 @@ static void load_plugin(Context<E> &ctx) {
|
||||
phase = 1;
|
||||
gctx<E> = &ctx;
|
||||
|
||||
dlopen_handle = dlopen(ctx.arg.plugin.c_str(), RTLD_NOW | RTLD_GLOBAL);
|
||||
if (!dlopen_handle)
|
||||
void *handle = dlopen(ctx.arg.plugin.c_str(), RTLD_NOW | RTLD_GLOBAL);
|
||||
if (!handle)
|
||||
Fatal(ctx) << "could not open plugin file: " << dlerror();
|
||||
|
||||
OnloadFn *onload = (OnloadFn *)dlsym(dlopen_handle, "onload");
|
||||
OnloadFn *onload = (OnloadFn *)dlsym(handle, "onload");
|
||||
if (!onload)
|
||||
Fatal(ctx) << "failed to load plugin " << ctx.arg.plugin << ": "
|
||||
<< dlerror();
|
||||
@@ -477,13 +477,29 @@ static ElfSym<E> to_elf_sym(PluginSymbol &psym) {
|
||||
return esym;
|
||||
}
|
||||
|
||||
// Returns true if a given linker plugin looks like LLVM's one.
|
||||
// Returns false if it's GCC.
|
||||
template <typename E>
|
||||
static bool is_llvm(Context<E> &ctx) {
|
||||
return ctx.arg.plugin.ends_with("LLVMgold.so");
|
||||
}
|
||||
|
||||
// Returns true if a given linker plugin supports the get_symbols_v3 API.
|
||||
// Currently, we simply assume that LLVM supports it and GCC does not.
|
||||
template <typename E>
|
||||
static bool suppots_v3_api(Context<E> &ctx) {
|
||||
return is_llvm(ctx);
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
|
||||
LOG << "read_lto_object: " << mf->name << "\n";
|
||||
|
||||
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";
|
||||
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.";
|
||||
|
||||
// dlopen the linker plugin file
|
||||
static std::once_flag flag;
|
||||
@@ -491,7 +507,7 @@ ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
|
||||
|
||||
// Create mold's object instance
|
||||
ObjectFile<E> *obj = new ObjectFile<E>;
|
||||
ctx.obj_pool.push_back(std::unique_ptr<ObjectFile<E>>(obj));
|
||||
ctx.obj_pool.emplace_back(obj);
|
||||
|
||||
obj->filename = mf->name;
|
||||
obj->symbols.push_back(new Symbol<E>);
|
||||
@@ -500,31 +516,39 @@ ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
|
||||
obj->mf = mf;
|
||||
|
||||
// Create plugin's object instance
|
||||
PluginInputFile *file = new PluginInputFile;
|
||||
PluginInputFile file = {};
|
||||
|
||||
MappedFile<Context<E>> *mf2 = mf->parent ? mf->parent : mf;
|
||||
file->name = save_string(ctx, mf2->name).data();
|
||||
file.name = save_string(ctx, mf2->name).data();
|
||||
if (mf2->fd == -1)
|
||||
mf2->fd = open(file->name, O_RDONLY);
|
||||
file->fd = mf2->fd;
|
||||
if (file->fd == -1)
|
||||
Fatal(ctx) << "cannot open " << file->name << ": " << errno_string();
|
||||
mf2->fd = open(file.name, O_RDONLY);
|
||||
file.fd = mf2->fd;
|
||||
if (file.fd == -1)
|
||||
Fatal(ctx) << "cannot open " << file.name << ": " << errno_string();
|
||||
|
||||
if (mf->parent)
|
||||
obj->archive_name = mf->parent->name;
|
||||
|
||||
file->offset = mf->get_offset();
|
||||
file->filesize = mf->size;
|
||||
file->handle = (void *)obj;
|
||||
file.offset = mf->get_offset();
|
||||
file.filesize = mf->size;
|
||||
file.handle = (void *)obj;
|
||||
|
||||
LOG << "read_lto_symbols: "<< mf->name << "\n";
|
||||
|
||||
// claim_file_hook() calls add_symbols() which initializes `plugin_symbols`
|
||||
int claimed = false;
|
||||
claim_file_hook(file, &claimed);
|
||||
claim_file_hook(&file, &claimed);
|
||||
if (!claimed)
|
||||
Fatal(ctx) << mf->name << ": not claimed by the LTO plugin";
|
||||
|
||||
// It looks like GCC doesn't need fd after claim_file_hook() while
|
||||
// LLVM needs it and takes the ownership of fd. To prevent "too many
|
||||
// open files" issue, we close fd only for GCC. This is ugly, though.
|
||||
if (!is_llvm(ctx)) {
|
||||
close(mf2->fd);
|
||||
mf2->fd = -1;
|
||||
}
|
||||
|
||||
// Initialize object symbols
|
||||
std::vector<ElfSym<E>> *esyms = new std::vector<ElfSym<E>>(1);
|
||||
|
||||
@@ -540,13 +564,6 @@ ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Returns true if a given linker plugin supports the get_symbols_v3 API.
|
||||
// Currently, we simply assume that LLVM supports it and GCC does not.
|
||||
template <typename E>
|
||||
static bool suppots_v3_api(Context<E> &ctx) {
|
||||
return ctx.arg.plugin.ends_with("LLVMgold.so");
|
||||
}
|
||||
|
||||
// This function restarts mold itself with `--:lto-pass2` and
|
||||
// `--:ignore-ir-file` flags. We do this as a workaround for the old
|
||||
// linker plugins that do not support the get_symbols_v3 API.
|
||||
@@ -638,6 +655,7 @@ void lto_cleanup(Context<E> &ctx) {
|
||||
INSTANTIATE(X86_64);
|
||||
INSTANTIATE(I386);
|
||||
INSTANTIATE(ARM64);
|
||||
INSTANTIATE(ARM32);
|
||||
INSTANTIATE(RISCV64);
|
||||
|
||||
} // namespace mold::elf
|
||||
+51
-47
@@ -342,7 +342,7 @@ static void show_stats(Context<E> &ctx) {
|
||||
static Counter num_objs("num_objs", ctx.objs.size());
|
||||
static Counter num_dsos("num_dsos", ctx.dsos.size());
|
||||
|
||||
if constexpr (E::e_machine == EM_AARCH64) {
|
||||
if constexpr (std::is_same_v<E, ARM64>) {
|
||||
static Counter num_thunks("num_thunks");
|
||||
for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections)
|
||||
for (std::unique_ptr<RangeExtensionThunk<E>> &thunk : osec->thunks)
|
||||
@@ -387,6 +387,8 @@ static int elf_main(int argc, char **argv) {
|
||||
return elf_main<I386>(argc, argv);
|
||||
case EM_AARCH64:
|
||||
return elf_main<ARM64>(argc, argv);
|
||||
case EM_ARM:
|
||||
return elf_main<ARM32>(argc, argv);
|
||||
case EM_RISCV:
|
||||
return elf_main<RISCV64>(argc, argv);
|
||||
}
|
||||
@@ -464,10 +466,6 @@ static int elf_main(int argc, char **argv) {
|
||||
// Apply -exclude-libs
|
||||
apply_exclude_libs(ctx);
|
||||
|
||||
// Create instances of linker-synthesized sections such as
|
||||
// .got or .plt.
|
||||
create_synthetic_sections(ctx);
|
||||
|
||||
// Resolve symbols and fix the set of object files that are
|
||||
// included to the final output.
|
||||
resolve_symbols(ctx);
|
||||
@@ -501,6 +499,10 @@ static int elf_main(int argc, char **argv) {
|
||||
// Compute sizes of sections containing mergeable strings.
|
||||
compute_merged_section_sizes(ctx);
|
||||
|
||||
// Create instances of linker-synthesized sections such as
|
||||
// .got or .plt.
|
||||
create_synthetic_sections(ctx);
|
||||
|
||||
// Bin input sections into output sections.
|
||||
bin_sections(ctx);
|
||||
|
||||
@@ -510,7 +512,6 @@ static int elf_main(int argc, char **argv) {
|
||||
// Create a dummy file containing linker-synthesized symbols
|
||||
// (e.g. `__bss_start`).
|
||||
ctx.internal_obj = create_internal_file(ctx);
|
||||
ctx.internal_obj->resolve_symbols(ctx);
|
||||
ctx.objs.push_back(ctx.internal_obj);
|
||||
|
||||
// Beyond this point, no new files will be added to ctx.objs
|
||||
@@ -520,12 +521,6 @@ static int elf_main(int argc, char **argv) {
|
||||
if (ctx.arg.z_cet_report != CET_REPORT_NONE)
|
||||
check_cet_errors(ctx);
|
||||
|
||||
// Handle --print-dependencies
|
||||
if (ctx.arg.print_dependencies == 1)
|
||||
print_dependencies(ctx);
|
||||
else if (ctx.arg.print_dependencies == 2)
|
||||
print_dependencies_full(ctx);
|
||||
|
||||
// If we are linking a .so file, remaining undefined symbols does
|
||||
// not cause a linker error. Instead, they are treated as if they
|
||||
// were imported symbols.
|
||||
@@ -554,8 +549,12 @@ static int elf_main(int argc, char **argv) {
|
||||
// a special rule. Sort them.
|
||||
sort_init_fini(ctx);
|
||||
|
||||
// Likewise, .ctors and .dtors have to be sorted. They are rare
|
||||
// because they are superceded by .init_array/.fini_array, though.
|
||||
sort_ctor_dtor(ctx);
|
||||
|
||||
// Handle --shuffle-sections
|
||||
if (ctx.arg.shuffle_sections)
|
||||
if (ctx.arg.shuffle_sections != SHUFFLE_SECTIONS_NONE)
|
||||
shuffle_sections(ctx);
|
||||
|
||||
// Compute sizes of output sections while assigning offsets
|
||||
@@ -596,7 +595,8 @@ static int elf_main(int argc, char **argv) {
|
||||
ctx.dynsym->finalize(ctx);
|
||||
|
||||
// Fill .gnu.version_d section contents.
|
||||
ctx.verdef->construct(ctx);
|
||||
if (ctx.verdef)
|
||||
ctx.verdef->construct(ctx);
|
||||
|
||||
// Fill .gnu.version_r section contents.
|
||||
ctx.verneed->construct(ctx);
|
||||
@@ -607,15 +607,12 @@ static int elf_main(int argc, char **argv) {
|
||||
// .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.
|
||||
// Here, we transplant .eh_frame sections from a regular output
|
||||
// section to the special EHFrameSection.
|
||||
{
|
||||
Timer t(ctx, "eh_frame");
|
||||
std::erase_if(ctx.chunks, [](Chunk<E> *chunk) {
|
||||
return chunk->is_output_section() && chunk->name == ".eh_frame";
|
||||
});
|
||||
ctx.eh_frame->construct(ctx);
|
||||
}
|
||||
// Here, we construct output .eh_frame contents.
|
||||
ctx.eh_frame->construct(ctx);
|
||||
|
||||
// Handle --gdb-index.
|
||||
if (ctx.arg.gdb_index)
|
||||
ctx.gdb_index->construct(ctx);
|
||||
|
||||
// If --emit-relocs is given, we'll copy relocation sections from input
|
||||
// files to an output file.
|
||||
@@ -646,39 +643,28 @@ static int elf_main(int argc, char **argv) {
|
||||
// On ARM64, we may need to create so-called "range extension thunks"
|
||||
// to extend branch instructions reach, as they can jump only to
|
||||
// ±128 MiB.
|
||||
if constexpr (E::e_machine == EM_AARCH64)
|
||||
if constexpr (std::is_same_v<E, ARM64>)
|
||||
filesize = create_range_extension_thunks(ctx);
|
||||
|
||||
// On RISC-V, branches are encode using multiple instructions so
|
||||
// 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 (E::e_machine == EM_RISCV)
|
||||
if constexpr (std::is_same_v<E, RISCV64>)
|
||||
filesize = riscv_resize_sections(ctx);
|
||||
|
||||
// Fix linker-synthesized symbol addresses.
|
||||
fix_synthetic_symbols(ctx);
|
||||
|
||||
// If --compress-debug-sections is given, compress .debug_* sections
|
||||
// using zlib.
|
||||
if (ctx.arg.compress_debug_sections != COMPRESS_NONE) {
|
||||
compress_debug_sections(ctx);
|
||||
filesize = set_osec_offsets(ctx);
|
||||
}
|
||||
|
||||
// At this point, file layout is fixed.
|
||||
// Beyond this, you can assume that symbol addresses including their
|
||||
// GOT or PLT addresses have a correct final value.
|
||||
|
||||
// Some types of TLS relocations are defined relative to the beginning
|
||||
// or the end of the TLS segment address. Find these addresses now.
|
||||
for (ElfPhdr<E> phdr : create_phdr(ctx)) {
|
||||
if (phdr.p_type == PT_TLS) {
|
||||
ctx.tls_begin = phdr.p_vaddr;
|
||||
ctx.tls_end = align_to(phdr.p_vaddr + phdr.p_memsz, phdr.p_align);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If --compress-debug-sections is given, compress .debug_* sections
|
||||
// using zlib.
|
||||
if (ctx.arg.compress_debug_sections != COMPRESS_NONE)
|
||||
filesize = compress_debug_sections(ctx);
|
||||
|
||||
// At this point, file layout is fixed.
|
||||
|
||||
t_before_copy.stop();
|
||||
|
||||
@@ -693,20 +679,27 @@ static int elf_main(int argc, char **argv) {
|
||||
Timer t(ctx, "copy_buf");
|
||||
|
||||
tbb::parallel_for_each(ctx.chunks, [&](Chunk<E> *chunk) {
|
||||
std::string name(chunk->name);
|
||||
if (name.empty())
|
||||
name = "(header)";
|
||||
std::string name =
|
||||
chunk->name.empty() ? "(header)" : std::string(chunk->name);
|
||||
Timer t2(ctx, name, &t);
|
||||
|
||||
chunk->copy_buf(ctx);
|
||||
});
|
||||
|
||||
ctx.checkpoint();
|
||||
}
|
||||
|
||||
if constexpr (E::e_machine == EM_AARCH64)
|
||||
if constexpr (std::is_same_v<E, ARM64>)
|
||||
write_thunks(ctx);
|
||||
|
||||
if constexpr (std::is_same_v<E, ARM32>)
|
||||
sort_arm_exidx(ctx);
|
||||
|
||||
// Some part of .gdb_index couldn't be computed until other debug
|
||||
// sections are complete. We have complete debug sections now, so
|
||||
// write the rest of .gdb_index.
|
||||
if (ctx.gdb_index)
|
||||
ctx.gdb_index->write_address_areas(ctx);
|
||||
|
||||
// Dynamic linker works better with sorted .rela.dyn section,
|
||||
// so we sort them.
|
||||
ctx.reldyn->sort(ctx);
|
||||
@@ -724,6 +717,16 @@ static int elf_main(int argc, char **argv) {
|
||||
// Close the output file. This is the end of the linker's main job.
|
||||
ctx.output_file->close(ctx);
|
||||
|
||||
// Handle --dependency-file
|
||||
if (!ctx.arg.dependency_file.empty())
|
||||
write_dependency_file(ctx);
|
||||
|
||||
// Handle --print-dependencies
|
||||
if (ctx.arg.print_dependencies == 1)
|
||||
print_dependencies(ctx);
|
||||
else if (ctx.arg.print_dependencies == 2)
|
||||
print_dependencies_full(ctx);
|
||||
|
||||
if (ctx.has_lto_object)
|
||||
lto_cleanup(ctx);
|
||||
|
||||
@@ -763,6 +766,7 @@ int main(int argc, char **argv) {
|
||||
INSTANTIATE(X86_64);
|
||||
INSTANTIATE(I386);
|
||||
INSTANTIATE(ARM64);
|
||||
INSTANTIATE(ARM32);
|
||||
INSTANTIATE(RISCV64);
|
||||
|
||||
} // namespace mold::elf
|
||||
+14
-8
@@ -28,12 +28,13 @@ static Map<E> get_map(Context<E> &ctx) {
|
||||
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
for (Symbol<E> *sym : file->symbols) {
|
||||
if (sym->file == file && sym->input_section &&
|
||||
sym->get_type() != STT_SECTION) {
|
||||
assert(file == &sym->input_section->file);
|
||||
if (sym->file != file || sym->get_type() == STT_SECTION)
|
||||
continue;
|
||||
|
||||
if (InputSection<E> *isec = sym->get_input_section()) {
|
||||
assert(file == &isec->file);
|
||||
typename Map<E>::accessor acc;
|
||||
map.insert(acc, {sym->input_section, {}});
|
||||
map.insert(acc, {isec, {}});
|
||||
acc->second.push_back(sym);
|
||||
}
|
||||
}
|
||||
@@ -62,10 +63,11 @@ void print_map(Context<E> &ctx) {
|
||||
Map<E> map = get_map(ctx);
|
||||
|
||||
// Print a mapfile.
|
||||
*out << " VMA Size Align Out In Symbol\n";
|
||||
*out << " VMA Size Align Out In Symbol\n";
|
||||
|
||||
for (Chunk<E> *osec : ctx.chunks) {
|
||||
*out << std::setw(16) << (u64)osec->shdr.sh_addr
|
||||
*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";
|
||||
@@ -80,8 +82,10 @@ void print_map(Context<E> &ctx) {
|
||||
InputSection<E> *mem = members[i];
|
||||
std::ostringstream ss;
|
||||
opt_demangle = ctx.arg.demangle;
|
||||
u64 addr = osec->shdr.sh_addr + mem->offset;
|
||||
|
||||
ss << std::setw(16) << (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)
|
||||
<< " " << *mem << "\n";
|
||||
@@ -89,7 +93,8 @@ void print_map(Context<E> &ctx) {
|
||||
typename Map<E>::const_accessor acc;
|
||||
if (map.find(acc, mem))
|
||||
for (Symbol<E> *sym : acc->second)
|
||||
ss << std::setw(16) << sym->get_addr(ctx)
|
||||
ss << std::showbase
|
||||
<< std::setw(18) << std::hex << sym->get_addr(ctx) << std::dec
|
||||
<< " 0 0 "
|
||||
<< *sym << "\n";
|
||||
|
||||
@@ -107,6 +112,7 @@ void print_map(Context<E> &ctx) {
|
||||
INSTANTIATE(X86_64);
|
||||
INSTANTIATE(I386);
|
||||
INSTANTIATE(ARM64);
|
||||
INSTANTIATE(ARM32);
|
||||
INSTANTIATE(RISCV64);
|
||||
|
||||
} // namespace mold::elf
|
||||
+506
-133
File diff suppressed because it is too large.
Load diff
+805
-207
File diff suppressed because it is too large.
Load diff
@@ -138,6 +138,7 @@ OutputFile<E>::open(Context<E> &ctx, std::string path, i64 filesize, i64 perm) {
|
||||
INSTANTIATE(X86_64);
|
||||
INSTANTIATE(I386);
|
||||
INSTANTIATE(ARM64);
|
||||
INSTANTIATE(ARM32);
|
||||
INSTANTIATE(RISCV64);
|
||||
|
||||
} // namespace mold::elf
|
||||
+415
-161
@@ -1,5 +1,6 @@
|
||||
#include "mold.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
@@ -31,49 +32,69 @@ void apply_exclude_libs(Context<E> &ctx) {
|
||||
|
||||
template <typename E>
|
||||
void create_synthetic_sections(Context<E> &ctx) {
|
||||
auto add = [&](auto &chunk) {
|
||||
ctx.chunks.push_back(chunk.get());
|
||||
auto push = [&]<typename T>(T *x) {
|
||||
ctx.chunks.push_back(x);
|
||||
return std::unique_ptr<T>(x);
|
||||
};
|
||||
|
||||
add(ctx.ehdr = std::make_unique<OutputEhdr<E>>());
|
||||
add(ctx.phdr = std::make_unique<OutputPhdr<E>>());
|
||||
add(ctx.shdr = std::make_unique<OutputShdr<E>>());
|
||||
add(ctx.got = std::make_unique<GotSection<E>>());
|
||||
add(ctx.gotplt = std::make_unique<GotPltSection<E>>());
|
||||
add(ctx.reldyn = std::make_unique<RelDynSection<E>>());
|
||||
add(ctx.relplt = std::make_unique<RelPltSection<E>>());
|
||||
if (!ctx.arg.oformat_binary) {
|
||||
ctx.ehdr = push(new OutputEhdr<E>);
|
||||
ctx.phdr = push(new OutputPhdr<E>);
|
||||
ctx.shdr = push(new OutputShdr<E>);
|
||||
}
|
||||
|
||||
ctx.got = push(new GotSection<E>);
|
||||
ctx.gotplt = push(new GotPltSection<E>);
|
||||
ctx.reldyn = push(new RelDynSection<E>);
|
||||
ctx.relplt = push(new RelPltSection<E>);
|
||||
|
||||
if (ctx.arg.pack_dyn_relocs_relr)
|
||||
add(ctx.relrdyn = std::make_unique<RelrDynSection<E>>());
|
||||
ctx.relrdyn = push(new RelrDynSection<E>);
|
||||
|
||||
add(ctx.strtab = std::make_unique<StrtabSection<E>>());
|
||||
add(ctx.shstrtab = std::make_unique<ShstrtabSection<E>>());
|
||||
add(ctx.plt = std::make_unique<PltSection<E>>());
|
||||
add(ctx.pltgot = std::make_unique<PltGotSection<E>>());
|
||||
add(ctx.symtab = std::make_unique<SymtabSection<E>>());
|
||||
add(ctx.dynsym = std::make_unique<DynsymSection<E>>());
|
||||
add(ctx.dynstr = std::make_unique<DynstrSection<E>>());
|
||||
add(ctx.eh_frame = std::make_unique<EhFrameSection<E>>());
|
||||
add(ctx.copyrel = std::make_unique<CopyrelSection<E>>(false));
|
||||
add(ctx.copyrel_relro = std::make_unique<CopyrelSection<E>>(true));
|
||||
ctx.strtab = push(new StrtabSection<E>);
|
||||
ctx.shstrtab = push(new ShstrtabSection<E>);
|
||||
ctx.plt = push(new PltSection<E>);
|
||||
ctx.pltgot = push(new PltGotSection<E>);
|
||||
ctx.symtab = push(new SymtabSection<E>);
|
||||
ctx.dynsym = push(new DynsymSection<E>);
|
||||
ctx.dynstr = push(new DynstrSection<E>);
|
||||
ctx.eh_frame = push(new EhFrameSection<E>);
|
||||
ctx.copyrel = push(new CopyrelSection<E>(false));
|
||||
ctx.copyrel_relro = push(new CopyrelSection<E>(true));
|
||||
|
||||
if (!ctx.arg.dynamic_linker.empty())
|
||||
add(ctx.interp = std::make_unique<InterpSection<E>>());
|
||||
ctx.interp = push(new InterpSection<E>);
|
||||
if (ctx.arg.build_id.kind != BuildId::NONE)
|
||||
add(ctx.buildid = std::make_unique<BuildIdSection<E>>());
|
||||
ctx.buildid = push(new BuildIdSection<E>);
|
||||
if (ctx.arg.eh_frame_hdr)
|
||||
add(ctx.eh_frame_hdr = std::make_unique<EhFrameHdrSection<E>>());
|
||||
ctx.eh_frame_hdr = push(new EhFrameHdrSection<E>);
|
||||
if (ctx.arg.gdb_index)
|
||||
ctx.gdb_index = push(new GdbIndexSection<E>);
|
||||
if (ctx.arg.hash_style_sysv)
|
||||
add(ctx.hash = std::make_unique<HashSection<E>>());
|
||||
ctx.hash = push(new HashSection<E>);
|
||||
if (ctx.arg.hash_style_gnu)
|
||||
add(ctx.gnu_hash = std::make_unique<GnuHashSection<E>>());
|
||||
ctx.gnu_hash = push(new GnuHashSection<E>);
|
||||
if (!ctx.arg.version_definitions.empty())
|
||||
add(ctx.verdef = std::make_unique<VerdefSection<E>>());
|
||||
ctx.verdef = push(new VerdefSection<E>);
|
||||
|
||||
add(ctx.dynamic = std::make_unique<DynamicSection<E>>());
|
||||
add(ctx.versym = std::make_unique<VersymSection<E>>());
|
||||
add(ctx.verneed = std::make_unique<VerneedSection<E>>());
|
||||
add(ctx.note_property = std::make_unique<NotePropertySection<E>>());
|
||||
if (ctx.arg.shared || !ctx.dsos.empty() || ctx.arg.pie)
|
||||
ctx.dynamic = push(new DynamicSection<E>);
|
||||
|
||||
ctx.versym = push(new VersymSection<E>);
|
||||
ctx.verneed = push(new VerneedSection<E>);
|
||||
ctx.note_property = push(new NotePropertySection<E>);
|
||||
|
||||
if constexpr (std::is_same_v<E, ARM32>) {
|
||||
ctx.thumb_to_arm = push(new ThumbToArmSection);
|
||||
ctx.tls_trampoline = push(new TlsTrampolineSection);
|
||||
}
|
||||
|
||||
// If .dynamic exists, .dynsym and .dynstr must exist as well
|
||||
// since .dynamic refers them.
|
||||
if (ctx.dynamic) {
|
||||
ctx.dynstr->keep();
|
||||
ctx.dynsym->keep();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -122,9 +143,9 @@ void do_resolve_symbols(Context<E> &ctx) {
|
||||
mark_live_objects(ctx);
|
||||
|
||||
// Remove symbols of eliminated files.
|
||||
for_each_file([&](InputFile<E> *file) {
|
||||
for_each_file([](InputFile<E> *file) {
|
||||
if (!file->is_alive)
|
||||
file->clear_symbols(ctx);
|
||||
file->clear_symbols();
|
||||
});
|
||||
|
||||
// Since we have turned on object files live bits, their symbols
|
||||
@@ -180,12 +201,12 @@ void resolve_symbols(Context<E> &ctx) {
|
||||
|
||||
// Redo name resolution from scratch.
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
file->clear_symbols(ctx);
|
||||
file->clear_symbols();
|
||||
file->is_alive = !file->is_in_lib;
|
||||
});
|
||||
|
||||
tbb::parallel_for_each(ctx.dsos, [&](SharedFile<E> *file) {
|
||||
file->clear_symbols(ctx);
|
||||
file->clear_symbols();
|
||||
file->is_alive = true;
|
||||
});
|
||||
|
||||
@@ -328,26 +349,12 @@ void bin_sections(Context<E> &ctx) {
|
||||
});
|
||||
}
|
||||
|
||||
static std::optional<u64> parse_defsym_addr(std::string_view s) {
|
||||
if (s.starts_with("0x") || s.starts_with("0X")) {
|
||||
size_t nread;
|
||||
u64 addr = std::stoull(std::string(s), &nread, 16);
|
||||
if (s.size() != nread)
|
||||
return {};
|
||||
return addr;
|
||||
}
|
||||
|
||||
if (s.find_first_not_of("0123456789") == s.npos)
|
||||
return std::stoull(std::string(s), nullptr, 10);
|
||||
return {};
|
||||
}
|
||||
|
||||
// Create a dummy object file containing linker-synthesized
|
||||
// symbols.
|
||||
template <typename E>
|
||||
ObjectFile<E> *create_internal_file(Context<E> &ctx) {
|
||||
ObjectFile<E> *obj = new ObjectFile<E>;
|
||||
ctx.obj_pool.push_back(std::unique_ptr<ObjectFile<E>>(obj));
|
||||
ctx.obj_pool.emplace_back(obj);
|
||||
|
||||
// Create linker-synthesized symbols.
|
||||
auto *esyms = new std::vector<ElfSym<E>>(1);
|
||||
@@ -357,16 +364,15 @@ ObjectFile<E> *create_internal_file(Context<E> &ctx) {
|
||||
obj->features = -1;
|
||||
obj->priority = 1;
|
||||
|
||||
auto add = [&](std::string_view name) {
|
||||
auto add = [&](std::string_view name, u8 st_type = STT_NOTYPE) {
|
||||
ElfSym<E> esym = {};
|
||||
esym.st_type = STT_NOTYPE;
|
||||
esym.st_type = st_type;
|
||||
esym.st_shndx = SHN_ABS;
|
||||
esym.st_bind = STB_GLOBAL;
|
||||
esym.st_visibility = STV_HIDDEN;
|
||||
esyms->push_back(esym);
|
||||
|
||||
Symbol<E> *sym = get_symbol(ctx, name);
|
||||
sym->shndx = 1; // dummy value to make it a relative symbol
|
||||
obj->symbols.push_back(sym);
|
||||
return sym;
|
||||
};
|
||||
@@ -401,8 +407,17 @@ ObjectFile<E> *create_internal_file(Context<E> &ctx) {
|
||||
if (!get_symbol(ctx, "edata")->file)
|
||||
ctx.edata = add("edata");
|
||||
|
||||
if (E::e_machine == EM_RISCV && !ctx.arg.shared)
|
||||
ctx.__global_pointer = add("__global_pointer$");
|
||||
if constexpr (E::supports_tlsdesc)
|
||||
ctx._TLS_MODULE_BASE_ = add("_TLS_MODULE_BASE_");
|
||||
|
||||
if constexpr (std::is_same_v<E, RISCV64>)
|
||||
if (!ctx.arg.shared)
|
||||
ctx.__global_pointer = add("__global_pointer$");
|
||||
|
||||
if constexpr (std::is_same_v<E, ARM32>) {
|
||||
ctx.__exidx_start = add("__exidx_start");
|
||||
ctx.__exidx_end = add("__exidx_end");
|
||||
}
|
||||
|
||||
for (Chunk<E> *chunk : ctx.chunks) {
|
||||
if (!is_c_identifier(chunk->name))
|
||||
@@ -412,17 +427,16 @@ ObjectFile<E> *create_internal_file(Context<E> &ctx) {
|
||||
add(save_string(ctx, "__stop_" + std::string(chunk->name)));
|
||||
}
|
||||
|
||||
for (std::pair<std::string_view, std::string_view> pair : ctx.arg.defsyms) {
|
||||
i64 first_defsym = obj->symbols.size();
|
||||
|
||||
for (i64 i = 0; i < ctx.arg.defsyms.size(); i++) {
|
||||
Symbol<E> *sym = ctx.arg.defsyms[i].first;
|
||||
ElfSym<E> esym = {};
|
||||
esym.st_type = STT_NOTYPE;
|
||||
esym.st_shndx = SHN_ABS;
|
||||
esym.st_bind = STB_GLOBAL;
|
||||
esym.st_visibility = STV_DEFAULT;
|
||||
esyms->push_back(esym);
|
||||
|
||||
Symbol<E> *sym = get_symbol(ctx, pair.first);
|
||||
if (!parse_defsym_addr(pair.second))
|
||||
sym->shndx = 1; // dummy value to make it a relative symbol
|
||||
obj->symbols.push_back(sym);
|
||||
};
|
||||
|
||||
@@ -432,6 +446,20 @@ ObjectFile<E> *create_internal_file(Context<E> &ctx) {
|
||||
i64 num_globals = obj->elf_syms.size() - obj->first_global;
|
||||
obj->symvers.resize(num_globals);
|
||||
|
||||
obj->resolve_symbols(ctx);
|
||||
|
||||
for (i64 i = obj->first_global; i < first_defsym; i++)
|
||||
obj->symbols[i]->shndx = -1; // dummy value to make it a relative symbol
|
||||
|
||||
for (i64 i = 0; i < ctx.arg.defsyms.size(); i++) {
|
||||
Symbol<E> *sym = ctx.arg.defsyms[i].first;
|
||||
std::variant<Symbol<E> *, u64> val = ctx.arg.defsyms[i].second;
|
||||
|
||||
if (Symbol<E> **sym2 = std::get_if<Symbol<E> *>(&val))
|
||||
if ((*sym2)->is_relative())
|
||||
sym->shndx = -1; // dummy value to make it a relative symbol
|
||||
}
|
||||
|
||||
ctx.on_exit.push_back([=] {
|
||||
delete esyms;
|
||||
delete obj->symbols[0];
|
||||
@@ -472,9 +500,9 @@ void print_dependencies(Context<E> &ctx) {
|
||||
SyncOut(ctx) <<
|
||||
R"(# This is an output of the mold linker's --print-dependencies option.
|
||||
#
|
||||
# Each line consists of three fields, <input-file>, <output-file> and
|
||||
# <symbol> separated by tab characters. It indicates that <input-file>
|
||||
# depends on <output-file> to use <symbol>.)";
|
||||
# Each line consists of three fields, <file1>, <file2> and <symbol>
|
||||
# separated by tab characters. It indicates that <file1> depends on
|
||||
# <file2> to use <symbol>.)";
|
||||
|
||||
auto print = [&](InputFile<E> *file) {
|
||||
for (i64 i = file->first_global; i < file->symbols.size(); i++) {
|
||||
@@ -496,17 +524,17 @@ void print_dependencies_full(Context<E> &ctx) {
|
||||
SyncOut(ctx) <<
|
||||
R"(# This is an output of the mold linker's --print-dependencies=full option.
|
||||
#
|
||||
# Each line consists of 4 fields, <input-section>, <output-section>,
|
||||
# <symbol-type> and <symbol>, separated by tab characters. It indicates that
|
||||
# <input-section> depends on <output-section> to use <symbol>. <symbol-type>
|
||||
# is either "u" or "w" for regular or weak undefined, respectively.
|
||||
# Each line consists of 4 fields, <section1>, <section2>, <symbol-type> and
|
||||
# <symbol>, separated by tab characters. It indicates that <section1> depends
|
||||
# on <section2> to use <symbol>. <symbol-type> is either "u" or "w" for
|
||||
# regular undefined or weak undefined, respectively.
|
||||
#
|
||||
# If you want to obtain dependency information per function granularity,
|
||||
# compile source files with the -ffunction-sections compiler flag.)";
|
||||
|
||||
auto println = [&](auto &src, Symbol<E> &sym, ElfSym<E> &esym) {
|
||||
if (sym.input_section)
|
||||
SyncOut(ctx) << src << "\t" << *sym.input_section
|
||||
if (InputSection<E> *isec = sym.get_input_section())
|
||||
SyncOut(ctx) << src << "\t" << *isec
|
||||
<< "\t" << (esym.is_weak() ? 'w' : 'u')
|
||||
<< "\t" << sym;
|
||||
else
|
||||
@@ -624,7 +652,7 @@ void sort_init_fini(Context<E> &ctx) {
|
||||
Timer t(ctx, "sort_init_fini");
|
||||
|
||||
auto get_priority = [](InputSection<E> *isec) {
|
||||
static std::regex re(R"(_array\.(\d+)$)", std::regex_constants::optimize);
|
||||
static std::regex re(R"(\.(\d+)$)", std::regex_constants::optimize);
|
||||
std::string name = isec->name().begin();
|
||||
std::smatch m;
|
||||
if (std::regex_search(name, m, re))
|
||||
@@ -634,6 +662,46 @@ void sort_init_fini(Context<E> &ctx) {
|
||||
|
||||
for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections) {
|
||||
if (osec->name == ".init_array" || osec->name == ".fini_array") {
|
||||
if (ctx.arg.shuffle_sections == SHUFFLE_SECTIONS_REVERSE)
|
||||
std::reverse(osec->members.begin(), osec->members.end());
|
||||
|
||||
sort(osec->members, [&](InputSection<E> *a, InputSection<E> *b) {
|
||||
return get_priority(a) < get_priority(b);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void sort_ctor_dtor(Context<E> &ctx) {
|
||||
Timer t(ctx, "sort_ctor_dtor");
|
||||
|
||||
auto get_priority = [](InputSection<E> *isec) {
|
||||
auto opts = std::regex_constants::optimize | std::regex_constants::ECMAScript;
|
||||
static std::regex re1(R"((?:clang_rt\.)?crtbegin)", opts);
|
||||
static std::regex re2(R"((?:clang_rt\.)?crtend)", opts);
|
||||
static std::regex re3(R"(\.(\d+)$)", opts);
|
||||
|
||||
// crtbegin.o and crtend.o contain marker symbols such as
|
||||
// __CTOR_LIST__ or __DTOR_LIST__. So they have to be at the
|
||||
// beginning or end of the section.
|
||||
std::smatch m;
|
||||
if (std::regex_search(isec->file.filename, m, re1))
|
||||
return -2;
|
||||
if (std::regex_search(isec->file.filename, m, re2))
|
||||
return 65536;
|
||||
|
||||
std::string name(isec->name());
|
||||
if (std::regex_search(name, m, re3))
|
||||
return std::stoi(m[1]);
|
||||
return -1;
|
||||
};
|
||||
|
||||
for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections) {
|
||||
if (osec->name == ".ctors" || osec->name == ".dtors") {
|
||||
if (ctx.arg.shuffle_sections != SHUFFLE_SECTIONS_REVERSE)
|
||||
std::reverse(osec->members.begin(), osec->members.end());
|
||||
|
||||
sort(osec->members, [&](InputSection<E> *a, InputSection<E> *b) {
|
||||
return get_priority(a) < get_priority(b);
|
||||
});
|
||||
@@ -671,14 +739,37 @@ template <typename E>
|
||||
void shuffle_sections(Context<E> &ctx) {
|
||||
Timer t(ctx, "shuffle_sections");
|
||||
|
||||
u64 seed = std::random_device()();
|
||||
auto is_eligible = [](OutputSection<E> &osec) {
|
||||
return osec.name != ".init" && osec.name != ".fini" &&
|
||||
osec.name != ".init_array" && osec.name != ".fini_array" &&
|
||||
osec.name != ".ctors" && osec.name != ".dtors";
|
||||
};
|
||||
|
||||
tbb::parallel_for_each(ctx.output_sections,
|
||||
[&](std::unique_ptr<OutputSection<E>> &osec) {
|
||||
if (osec->name != ".init" && osec->name != ".fini" &&
|
||||
osec->name != ".init_array" && osec->name != ".fini_array")
|
||||
shuffle(osec->members, seed + hash_string(osec->name));
|
||||
});
|
||||
switch (ctx.arg.shuffle_sections) {
|
||||
case SHUFFLE_SECTIONS_NONE:
|
||||
unreachable();
|
||||
case SHUFFLE_SECTIONS_SHUFFLE: {
|
||||
u64 seed;
|
||||
if (ctx.arg.shuffle_sections_seed)
|
||||
seed = *ctx.arg.shuffle_sections_seed;
|
||||
else
|
||||
seed = std::random_device()();
|
||||
|
||||
tbb::parallel_for_each(ctx.output_sections,
|
||||
[&](std::unique_ptr<OutputSection<E>> &osec) {
|
||||
if (is_eligible(*osec))
|
||||
shuffle(osec->members, seed + hash_string(osec->name));
|
||||
});
|
||||
break;
|
||||
}
|
||||
case SHUFFLE_SECTIONS_REVERSE:
|
||||
tbb::parallel_for_each(ctx.output_sections,
|
||||
[&](std::unique_ptr<OutputSection<E>> &osec) {
|
||||
if (is_eligible(*osec))
|
||||
std::reverse(osec->members.begin(), osec->members.end());
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -812,20 +903,19 @@ void scan_rels(Context<E> &ctx) {
|
||||
ctx.got->add_got_symbol(ctx, sym);
|
||||
|
||||
if (sym->flags & NEEDS_PLT) {
|
||||
bool is_canonical = (!ctx.arg.pic && sym->is_imported);
|
||||
|
||||
// If a symbol needs a canonical PLT, it is considered both
|
||||
// imported and exported.
|
||||
if (is_canonical)
|
||||
if (sym->is_canonical) {
|
||||
// A canonical PLT needs to be visible from DSOs.
|
||||
sym->is_exported = true;
|
||||
|
||||
if ((sym->flags & NEEDS_GOT) && !is_canonical) {
|
||||
ctx.pltgot->add_symbol(ctx, sym);
|
||||
} else{
|
||||
// If we need to create a canonical PLT, we can't use .plt.got
|
||||
// because otherwise .plt.got and .got would refer each other,
|
||||
// resulting in an infinite loop at runtime.
|
||||
// We can't use .plt.got for a canonical PLT because otherwise
|
||||
// .plt.got and .got would refer each other, resulting in an
|
||||
// infinite loop at runtime.
|
||||
ctx.plt->add_symbol(ctx, sym);
|
||||
} else {
|
||||
if (sym->flags & NEEDS_GOT)
|
||||
ctx.pltgot->add_symbol(ctx, sym);
|
||||
else
|
||||
ctx.plt->add_symbol(ctx, sym);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -866,6 +956,10 @@ void scan_rels(Context<E> &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<E, ARM32>)
|
||||
if (sym->flags & NEEDS_THUMB_TO_ARM_THUNK)
|
||||
ctx.thumb_to_arm->add_symbol(ctx, sym);
|
||||
|
||||
sym->flags = 0;
|
||||
}
|
||||
|
||||
@@ -884,7 +978,7 @@ void create_reloc_sections(Context<E> &ctx) {
|
||||
for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections) {
|
||||
RelocSection<E> *r = new RelocSection<E>(ctx, *osec);
|
||||
ctx.chunks.push_back(r);
|
||||
ctx.output_chunks.push_back(std::unique_ptr<Chunk<E>>(r));
|
||||
ctx.output_chunks.emplace_back(r);
|
||||
}
|
||||
|
||||
// Create a table to map input symbol indices to output symbol indices
|
||||
@@ -1051,11 +1145,13 @@ void compute_import_export(Context<E> &ctx) {
|
||||
continue;
|
||||
|
||||
if (sym->file != file && sym->file->is_dso) {
|
||||
std::scoped_lock lock(sym->mu);
|
||||
sym->is_imported = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sym->file == file) {
|
||||
std::scoped_lock lock(sym->mu);
|
||||
sym->is_exported = true;
|
||||
|
||||
if (ctx.arg.shared && sym->visibility != STV_PROTECTED &&
|
||||
@@ -1093,8 +1189,16 @@ void clear_padding(Context<E> &ctx) {
|
||||
// program header
|
||||
// .interp
|
||||
// alloc note
|
||||
// alloc readonly data
|
||||
// .hash
|
||||
// .gnu.hash
|
||||
// .dynsym
|
||||
// .dynstr
|
||||
// .gnu.version
|
||||
// .gnu.version_r
|
||||
// .rela.dyn
|
||||
// .rela.plt
|
||||
// alloc readonly code
|
||||
// alloc readonly data
|
||||
// alloc writable tdata
|
||||
// alloc writable tbss
|
||||
// alloc writable RELRO data
|
||||
@@ -1104,7 +1208,18 @@ void clear_padding(Context<E> &ctx) {
|
||||
// nonalloc
|
||||
// section header
|
||||
//
|
||||
// .note sections are sorted by their alignments.
|
||||
// The reason to place .interp and other sections at the beginning of
|
||||
// a file is because they are needed by the loader. Especially on a
|
||||
// hard drive with spinnning disks, it is important to read these
|
||||
// sections in a single seek.
|
||||
//
|
||||
// .note sections are also placed at the beginning so that they are
|
||||
// included in a core crash dump even if it's truncated by ulimit. In
|
||||
// particular, if .note.gnu.build-id is in a truncated core file, you
|
||||
// can at least identify which executable has crashed.
|
||||
//
|
||||
// Other file layouts are possible, but this layout is chosen to keep
|
||||
// the number of segments as few as possible.
|
||||
template <typename E>
|
||||
i64 get_section_rank(Context<E> &ctx, Chunk<E> *chunk) {
|
||||
u64 type = chunk->shdr.sh_type;
|
||||
@@ -1116,8 +1231,27 @@ i64 get_section_rank(Context<E> &ctx, Chunk<E> *chunk) {
|
||||
return 1;
|
||||
if (chunk == ctx.interp.get())
|
||||
return 2;
|
||||
|
||||
if (type == SHT_NOTE && (flags & SHF_ALLOC))
|
||||
return (1 << 10) + chunk->shdr.sh_addralign;
|
||||
|
||||
if (chunk == ctx.hash.get())
|
||||
return (1 << 11) + 0;
|
||||
if (chunk == ctx.gnu_hash.get())
|
||||
return (1 << 11) + 1;
|
||||
if (chunk == ctx.dynsym.get())
|
||||
return (1 << 11) + 2;
|
||||
if (chunk == ctx.dynstr.get())
|
||||
return (1 << 11) + 3;
|
||||
if (chunk == ctx.versym.get())
|
||||
return (1 << 11) + 4;
|
||||
if (chunk == ctx.verneed.get())
|
||||
return (1 << 11) + 5;
|
||||
if (chunk == ctx.reldyn.get())
|
||||
return (1 << 11) + 6;
|
||||
if (chunk == ctx.relplt.get())
|
||||
return (1 << 11) + 7;
|
||||
|
||||
if (chunk == ctx.shdr.get())
|
||||
return 1 << 30;
|
||||
if (!(flags & SHF_ALLOC))
|
||||
@@ -1129,19 +1263,10 @@ i64 get_section_rank(Context<E> &ctx, Chunk<E> *chunk) {
|
||||
bool relro = is_relro(ctx, chunk);
|
||||
bool is_bss = (type == SHT_NOBITS);
|
||||
|
||||
return (1 << 20) | (writable << 19) | (exec << 18) | (!tls << 17) |
|
||||
return (1 << 20) | (writable << 19) | (!exec << 18) | (!tls << 17) |
|
||||
(!relro << 16) | (is_bss << 15);
|
||||
}
|
||||
|
||||
// Returns the smallest number n such that
|
||||
// val <= n and n % align == skew % align.
|
||||
inline u64 align_with_skew(u64 val, u64 align, u64 skew) {
|
||||
skew = skew % align;
|
||||
u64 n = align_to(val + align - skew, align) - align + skew;
|
||||
assert(val <= n && n < val + align && n % align == skew % align);
|
||||
return n;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
static bool is_tbss(Chunk<E> *chunk) {
|
||||
return (chunk->shdr.sh_type == SHT_NOBITS) && (chunk->shdr.sh_flags & SHF_TLS);
|
||||
@@ -1149,26 +1274,29 @@ static bool is_tbss(Chunk<E> *chunk) {
|
||||
|
||||
// Assign virtual addresses and file offsets to output sections.
|
||||
template <typename E>
|
||||
i64 set_osec_offsets(Context<E> &ctx) {
|
||||
Timer t(ctx, "set_osec_offsets");
|
||||
|
||||
i64 do_set_osec_offsets(Context<E> &ctx) {
|
||||
std::vector<Chunk<E> *> &chunks = ctx.chunks;
|
||||
|
||||
auto alignment = [](Chunk<E> *chunk) {
|
||||
return std::max<i64>(chunk->extra_addralign, chunk->shdr.sh_addralign);
|
||||
};
|
||||
|
||||
// Assign virtual addresses
|
||||
u64 addr = ctx.arg.image_base;
|
||||
for (i64 i = 0; i < chunks.size(); i++) {
|
||||
if (!(chunks[i]->shdr.sh_flags & SHF_ALLOC))
|
||||
continue;
|
||||
|
||||
if (i > 0 && separate_page(ctx, chunks[i - 1], chunks[i]))
|
||||
addr = align_to(addr, ctx.page_size);
|
||||
if (auto it = ctx.arg.section_start.find(chunks[i]->name);
|
||||
it != ctx.arg.section_start.end())
|
||||
addr = it->second;
|
||||
|
||||
if (is_tbss(chunks[i])) {
|
||||
chunks[i]->shdr.sh_addr = addr;
|
||||
continue;
|
||||
}
|
||||
|
||||
addr = align_to(addr, chunks[i]->shdr.sh_addralign);
|
||||
addr = align_to(addr, alignment(chunks[i]));
|
||||
chunks[i]->shdr.sh_addr = addr;
|
||||
addr += chunks[i]->shdr.sh_size;
|
||||
}
|
||||
@@ -1185,7 +1313,7 @@ i64 set_osec_offsets(Context<E> &ctx) {
|
||||
if (is_tbss(chunks[i])) {
|
||||
u64 addr = chunks[i]->shdr.sh_addr;
|
||||
for (; i < chunks.size() && is_tbss(chunks[i]); i++) {
|
||||
addr = align_to(addr, chunks[i]->shdr.sh_addralign);
|
||||
addr = align_to(addr, alignment(chunks[i]));
|
||||
chunks[i]->shdr.sh_addr = addr;
|
||||
addr += chunks[i]->shdr.sh_size;
|
||||
}
|
||||
@@ -1194,20 +1322,66 @@ i64 set_osec_offsets(Context<E> &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
// Assign file offsets
|
||||
// Assign file offsets to memory-allocated sections.
|
||||
u64 fileoff = 0;
|
||||
for (Chunk<E> *chunk : chunks) {
|
||||
if (chunk->shdr.sh_type == SHT_NOBITS) {
|
||||
chunk->shdr.sh_offset = fileoff;
|
||||
} else {
|
||||
fileoff = align_with_skew(fileoff, ctx.page_size, chunk->shdr.sh_addr);
|
||||
chunk->shdr.sh_offset = fileoff;
|
||||
fileoff += chunk->shdr.sh_size;
|
||||
i64 i = 0;
|
||||
|
||||
while (i < chunks.size() && (chunks[i]->shdr.sh_flags & SHF_ALLOC)) {
|
||||
Chunk<E> &first = *chunks[i];
|
||||
assert(first.shdr.sh_type != SHT_NOBITS);
|
||||
|
||||
fileoff = align_to(fileoff, alignment(&first));
|
||||
|
||||
u64 end = fileoff;
|
||||
while (i < chunks.size() && (chunks[i]->shdr.sh_flags & SHF_ALLOC) &&
|
||||
chunks[i]->shdr.sh_type != SHT_NOBITS) {
|
||||
// The addresses may not increase monotonically if a user uses
|
||||
// --start-sections.
|
||||
if (chunks[i]->shdr.sh_addr < first.shdr.sh_addr)
|
||||
break;
|
||||
|
||||
chunks[i]->shdr.sh_offset =
|
||||
fileoff + chunks[i]->shdr.sh_addr - first.shdr.sh_addr;
|
||||
end = chunks[i]->shdr.sh_offset + chunks[i]->shdr.sh_size;
|
||||
i++;
|
||||
}
|
||||
|
||||
fileoff = end;
|
||||
|
||||
while (i < chunks.size() && (chunks[i]->shdr.sh_flags & SHF_ALLOC) &&
|
||||
chunks[i]->shdr.sh_type == SHT_NOBITS)
|
||||
i++;
|
||||
}
|
||||
|
||||
// Assign file offsets to non-memory-allocated sections.
|
||||
for (; i < chunks.size(); i++) {
|
||||
fileoff = align_to(fileoff, chunks[i]->shdr.sh_addralign);
|
||||
chunks[i]->shdr.sh_offset = fileoff;
|
||||
fileoff += chunks[i]->shdr.sh_size;
|
||||
}
|
||||
return fileoff;
|
||||
}
|
||||
|
||||
// Assign virtual addresses and file offsets to output sections.
|
||||
template <typename E>
|
||||
i64 set_osec_offsets(Context<E> &ctx) {
|
||||
Timer t(ctx, "set_osec_offsets");
|
||||
|
||||
for (;;) {
|
||||
i64 fileoff = do_set_osec_offsets(ctx);
|
||||
|
||||
// Assigning new offsets may change the contents and the length
|
||||
// of the program header, so repeat it until converge.
|
||||
if (!ctx.phdr)
|
||||
return fileoff;
|
||||
|
||||
i64 sz = ctx.phdr->shdr.sh_size;
|
||||
ctx.phdr->update_shdr(ctx);
|
||||
if (sz == ctx.phdr->shdr.sh_size)
|
||||
return fileoff;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
static i64 get_num_irelative_relocs(Context<E> &ctx) {
|
||||
return std::count_if(
|
||||
@@ -1219,45 +1393,59 @@ template <typename E>
|
||||
void fix_synthetic_symbols(Context<E> &ctx) {
|
||||
auto start = [](Symbol<E> *sym, auto &chunk) {
|
||||
if (sym && chunk) {
|
||||
sym->shndx = chunk->shndx;
|
||||
sym->shndx = -chunk->shndx;
|
||||
sym->value = chunk->shdr.sh_addr;
|
||||
}
|
||||
};
|
||||
|
||||
auto stop = [](Symbol<E> *sym, auto &chunk) {
|
||||
if (sym && chunk) {
|
||||
sym->shndx = chunk->shndx;
|
||||
sym->shndx = -chunk->shndx;
|
||||
sym->value = chunk->shdr.sh_addr + chunk->shdr.sh_size;
|
||||
}
|
||||
};
|
||||
|
||||
auto find = [&](std::string name) -> Chunk<E> * {
|
||||
for (Chunk<E> *chunk : ctx.chunks)
|
||||
if (chunk->name == name)
|
||||
return chunk;
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
// __bss_start
|
||||
for (Chunk<E> *chunk : ctx.chunks) {
|
||||
if (chunk->is_output_section() && chunk->name == ".bss") {
|
||||
start(ctx.__bss_start, chunk);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Chunk<E> *chunk = find(".bss"))
|
||||
start(ctx.__bss_start, chunk);
|
||||
|
||||
// __ehdr_start and __executable_start
|
||||
for (Chunk<E> *chunk : ctx.chunks) {
|
||||
if (chunk->shndx == 1) {
|
||||
ctx.__ehdr_start->shndx = 1;
|
||||
ctx.__ehdr_start->value = ctx.ehdr->shdr.sh_addr;
|
||||
|
||||
ctx.__executable_start->shndx = 1;
|
||||
ctx.__executable_start->value = ctx.ehdr->shdr.sh_addr;
|
||||
break;
|
||||
if (ctx.ehdr) {
|
||||
for (Chunk<E> *chunk : ctx.chunks) {
|
||||
if (chunk->shndx == 1) {
|
||||
ctx.__ehdr_start->shndx = -1;
|
||||
ctx.__ehdr_start->value = ctx.ehdr->shdr.sh_addr;
|
||||
ctx.__executable_start->shndx = -1;
|
||||
ctx.__executable_start->value = ctx.ehdr->shdr.sh_addr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// __rel_iplt_start
|
||||
start(ctx.__rel_iplt_start, ctx.reldyn);
|
||||
// __rel_iplt_start and __rel_iplt_end. These symbols need to be
|
||||
// defined in a statically-linked non-relocatable executable because
|
||||
// such executable lacks the .dynamic section and thus there's no way
|
||||
// to find ifunc relocations other than these symbols.
|
||||
//
|
||||
// We don't want to set values to these symbols if we are creating a
|
||||
// static PIE due to a glibc bug. If we set values to these symbols in
|
||||
// a static PIE, glibc attempts to run ifunc initializers twice, with
|
||||
// the second attempt with wrong function addresses, causing a
|
||||
// segmentation fault.
|
||||
if (ctx.reldyn && ctx.arg.is_static && !ctx.arg.pie) {
|
||||
stop(ctx.__rel_iplt_start, ctx.reldyn);
|
||||
stop(ctx.__rel_iplt_end, ctx.reldyn);
|
||||
|
||||
// __rel_iplt_end
|
||||
ctx.__rel_iplt_end->shndx = ctx.reldyn->shndx;
|
||||
ctx.__rel_iplt_end->value = ctx.reldyn->shdr.sh_addr +
|
||||
get_num_irelative_relocs(ctx) * sizeof(ElfRel<E>);
|
||||
ctx.__rel_iplt_start->value -=
|
||||
get_num_irelative_relocs(ctx) * sizeof(ElfRel<E>);
|
||||
}
|
||||
|
||||
// __{init,fini}_array_{start,end}
|
||||
for (Chunk<E> *chunk : ctx.chunks) {
|
||||
@@ -1299,26 +1487,54 @@ void fix_synthetic_symbols(Context<E> &ctx) {
|
||||
start(ctx._DYNAMIC, ctx.dynamic);
|
||||
|
||||
// _GLOBAL_OFFSET_TABLE_
|
||||
if (E::e_machine == EM_X86_64 || E::e_machine == EM_386)
|
||||
if constexpr (std::is_same_v<E, X86_64> || std::is_same_v<E, I386>)
|
||||
start(ctx._GLOBAL_OFFSET_TABLE_, ctx.gotplt);
|
||||
else if (E::e_machine == EM_AARCH64 || E::e_machine == EM_RISCV)
|
||||
start(ctx._GLOBAL_OFFSET_TABLE_, ctx.got);
|
||||
else
|
||||
unreachable();
|
||||
start(ctx._GLOBAL_OFFSET_TABLE_, ctx.got);
|
||||
|
||||
// _TLS_MODULE_BASE_
|
||||
if constexpr (E::supports_tlsdesc) {
|
||||
// _TLS_MODULE_BASE_ is used for Local Dynamic model for TLSDESC.
|
||||
// I believe GCC and Clang don't create a reference to it, but Intel
|
||||
// compiler seems to be using this symbol.
|
||||
//
|
||||
// The symbol is usually resolved to the beginning of the TLS section.
|
||||
// But if DTPOFF is relaxed, it must point the end of the TLS section
|
||||
// so that a GOTPC_TLSDESC's relaxed value is consistent with a
|
||||
// DTPOFF's relaxed value.
|
||||
u64 addr;
|
||||
if constexpr (std::is_same_v<E, X86_64> || std::is_same_v<E, I386>) {
|
||||
if (ctx.arg.relax && !ctx.arg.shared)
|
||||
addr = ctx.tls_end;
|
||||
else
|
||||
addr = ctx.tls_begin;
|
||||
} else {
|
||||
addr = ctx.tls_begin;
|
||||
}
|
||||
|
||||
ctx._TLS_MODULE_BASE_->shndx = -1;
|
||||
ctx._TLS_MODULE_BASE_->value = addr;
|
||||
}
|
||||
|
||||
// __GNU_EH_FRAME_HDR
|
||||
start(ctx.__GNU_EH_FRAME_HDR, ctx.eh_frame_hdr);
|
||||
|
||||
// RISC-V's __global_pointer$
|
||||
if (E::e_machine == EM_RISCV && !ctx.arg.shared) {
|
||||
ctx.__global_pointer->shndx = 1;
|
||||
ctx.__global_pointer->value = 0x800;
|
||||
if constexpr (std::is_same_v<E, RISCV64>) {
|
||||
if (!ctx.arg.shared) {
|
||||
if (Chunk<E> *chunk = find(".sdata"))
|
||||
ctx.__global_pointer->shndx = -chunk->shndx;
|
||||
else
|
||||
ctx.__global_pointer->shndx = -1;
|
||||
ctx.__global_pointer->value = 0x800;
|
||||
}
|
||||
}
|
||||
|
||||
for (Chunk<E> *chunk : ctx.chunks) {
|
||||
if (chunk->name == ".sdata") {
|
||||
ctx.__global_pointer->shndx = chunk->shndx;
|
||||
break;
|
||||
}
|
||||
// ARM32's __exidx_{start,end}
|
||||
if constexpr (std::is_same_v<E, ARM32>) {
|
||||
if (Chunk<E> *chunk = find(".ARM.exidx")) {
|
||||
start(ctx.__exidx_start, chunk);
|
||||
stop(ctx.__exidx_end, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1336,16 +1552,18 @@ void fix_synthetic_symbols(Context<E> &ctx) {
|
||||
}
|
||||
|
||||
// --defsym=sym=value symbols
|
||||
for (std::pair<std::string_view, std::string_view> defsym : ctx.arg.defsyms) {
|
||||
Symbol<E> *sym = get_symbol(ctx, defsym.first);
|
||||
sym->input_section = nullptr;
|
||||
for (i64 i = 0; i < ctx.arg.defsyms.size(); i++) {
|
||||
Symbol<E> *sym = ctx.arg.defsyms[i].first;
|
||||
std::variant<Symbol<E> *, u64> val = ctx.arg.defsyms[i].second;
|
||||
|
||||
if (std::optional<u64> addr = parse_defsym_addr(defsym.second)) {
|
||||
sym->shndx = 0;
|
||||
|
||||
if (u64 *addr = std::get_if<u64>(&val)) {
|
||||
sym->value = *addr;
|
||||
continue;
|
||||
}
|
||||
|
||||
Symbol<E> *sym2 = get_symbol(ctx, defsym.second);
|
||||
Symbol<E> *sym2 = std::get<Symbol<E> *>(val);
|
||||
if (!sym2->file) {
|
||||
Error(ctx) << "--defsym: undefined symbol: " << *sym2;
|
||||
continue;
|
||||
@@ -1354,13 +1572,13 @@ void fix_synthetic_symbols(Context<E> &ctx) {
|
||||
sym->value = sym2->get_addr(ctx);
|
||||
sym->visibility = sym2->visibility.load();
|
||||
|
||||
if (InputSection<E> *isec = sym2->input_section)
|
||||
sym->shndx = isec->output_section->shndx;
|
||||
if (InputSection<E> *isec = sym2->get_input_section())
|
||||
sym->shndx = -isec->output_section->shndx;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
void compress_debug_sections(Context<E> &ctx) {
|
||||
i64 compress_debug_sections(Context<E> &ctx) {
|
||||
Timer t(ctx, "compress_debug_sections");
|
||||
|
||||
tbb::parallel_for((i64)0, (i64)ctx.chunks.size(), [&](i64 i) {
|
||||
@@ -1377,13 +1595,46 @@ void compress_debug_sections(Context<E> &ctx) {
|
||||
comp = new GnuCompressedSection<E>(ctx, chunk);
|
||||
assert(comp);
|
||||
|
||||
ctx.output_chunks.push_back(std::unique_ptr<Chunk<E>>(comp));
|
||||
ctx.output_chunks.emplace_back(comp);
|
||||
ctx.chunks[i] = comp;
|
||||
});
|
||||
|
||||
ctx.shstrtab->update_shdr(ctx);
|
||||
ctx.ehdr->update_shdr(ctx);
|
||||
ctx.shdr->update_shdr(ctx);
|
||||
|
||||
if (ctx.ehdr)
|
||||
ctx.ehdr->update_shdr(ctx);
|
||||
if (ctx.shdr)
|
||||
ctx.shdr->update_shdr(ctx);
|
||||
|
||||
return set_osec_offsets(ctx);
|
||||
}
|
||||
|
||||
// Write Makefile-style dependency rules to a file specified by
|
||||
// --dependency-file. This is analogous to the compiler's -M flag.
|
||||
template <typename E>
|
||||
void write_dependency_file(Context<E> &ctx) {
|
||||
std::vector<std::string> deps;
|
||||
std::unordered_set<std::string> seen;
|
||||
|
||||
for (std::unique_ptr<MappedFile<Context<E>>> &mf : ctx.mf_pool)
|
||||
if (!mf->parent)
|
||||
if (std::string path = path_clean(mf->name); seen.insert(path).second)
|
||||
deps.push_back(path);
|
||||
|
||||
std::ofstream out;
|
||||
out.open(ctx.arg.dependency_file);
|
||||
if (out.fail())
|
||||
Fatal(ctx) << "--dependency-file: cannot open " << ctx.arg.dependency_file
|
||||
<< ": " << errno_string();
|
||||
|
||||
out << ctx.arg.output << ":";
|
||||
for (std::string &s : deps)
|
||||
out << " " << s;
|
||||
out << "\n";
|
||||
|
||||
for (std::string &s : deps)
|
||||
out << "\n" << s << ":\n";
|
||||
out.close();
|
||||
}
|
||||
|
||||
#define INSTANTIATE(E) \
|
||||
@@ -1402,6 +1653,7 @@ void compress_debug_sections(Context<E> &ctx) {
|
||||
template void write_repro_file(Context<E> &); \
|
||||
template void check_duplicate_symbols(Context<E> &); \
|
||||
template void sort_init_fini(Context<E> &); \
|
||||
template void sort_ctor_dtor(Context<E> &); \
|
||||
template void shuffle_sections(Context<E> &); \
|
||||
template std::vector<Chunk<E> *> collect_output_sections(Context<E> &); \
|
||||
template void compute_section_sizes(Context<E> &); \
|
||||
@@ -1417,11 +1669,13 @@ void compress_debug_sections(Context<E> &ctx) {
|
||||
template i64 get_section_rank(Context<E> &, Chunk<E> *); \
|
||||
template i64 set_osec_offsets(Context<E> &); \
|
||||
template void fix_synthetic_symbols(Context<E> &); \
|
||||
template void compress_debug_sections(Context<E> &);
|
||||
template i64 compress_debug_sections(Context<E> &); \
|
||||
template void write_dependency_file(Context<E> &);
|
||||
|
||||
INSTANTIATE(X86_64);
|
||||
INSTANTIATE(I386);
|
||||
INSTANTIATE(ARM64);
|
||||
INSTANTIATE(ARM32);
|
||||
INSTANTIATE(RISCV64);
|
||||
|
||||
} // namespace mold::elf
|
||||
@@ -591,6 +591,7 @@ void combine_objects(Context<E> &ctx, std::span<std::string_view> file_args) {
|
||||
INSTANTIATE(X86_64);
|
||||
INSTANTIATE(I386);
|
||||
INSTANTIATE(ARM64);
|
||||
INSTANTIATE(ARM32);
|
||||
INSTANTIATE(RISCV64);
|
||||
|
||||
} // namespace mold::elf
|
||||
@@ -315,6 +315,7 @@ void process_run_subcommand(Context<E> &ctx, int argc, char **argv) {
|
||||
INSTANTIATE(X86_64);
|
||||
INSTANTIATE(I386);
|
||||
INSTANTIATE(ARM64);
|
||||
INSTANTIATE(ARM32);
|
||||
INSTANTIATE(RISCV64);
|
||||
|
||||
} // namespace mold::elf
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../byteorder.h"
|
||||
#include "../big-endian.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ split_string(std::string_view str, char sep) {
|
||||
template <typename E>
|
||||
static void create_internal_file(Context<E> &ctx) {
|
||||
ObjectFile<E> *obj = new ObjectFile<E>;
|
||||
ctx.obj_pool.push_back(std::unique_ptr<ObjectFile<E>>(obj));
|
||||
ctx.obj_pool.emplace_back(obj);
|
||||
ctx.objs.push_back(obj);
|
||||
|
||||
auto add = [&](std::string_view name) {
|
||||
|
||||
@@ -779,6 +779,7 @@ struct Context {
|
||||
bool dylib = false;
|
||||
bool dynamic = true;
|
||||
bool fatal_warnings = false;
|
||||
bool noinhibit_exec = false;
|
||||
bool trace = false;
|
||||
i64 arch = CPU_TYPE_ARM64;
|
||||
i64 headerpad = 256;
|
||||
|
||||
@@ -21,7 +21,7 @@ ObjectFile<E>::create(Context<E> &ctx, MappedFile<Context<E>> *mf,
|
||||
obj->mf = mf;
|
||||
obj->archive_name = archive_name;
|
||||
obj->is_alive = archive_name.empty();
|
||||
ctx.obj_pool.push_back(std::unique_ptr<ObjectFile<E>>(obj));
|
||||
ctx.obj_pool.emplace_back(obj);
|
||||
return obj;
|
||||
};
|
||||
|
||||
@@ -186,7 +186,7 @@ void ObjectFile<E>::split_subsections(Context<E> &ctx) {
|
||||
.input_addr = (u32)(isec.hdr.addr + r.offset),
|
||||
.p2align = (u8)isec.hdr.p2align,
|
||||
};
|
||||
subsections.push_back(std::unique_ptr<Subsection<E>>(subsec));
|
||||
subsections.emplace_back(subsec);
|
||||
}
|
||||
|
||||
if (r.symidx != -1)
|
||||
@@ -482,7 +482,7 @@ void ObjectFile<E>::convert_common_symbols(Context<E> &ctx) {
|
||||
.p2align = (u8)msym.p2align,
|
||||
};
|
||||
|
||||
subsections.push_back(std::unique_ptr<Subsection<E>>(subsec));
|
||||
subsections.emplace_back(subsec);
|
||||
|
||||
sym.subsec = subsec;
|
||||
sym.value = 0;
|
||||
@@ -514,7 +514,7 @@ InputSection<E> *ObjectFile<E>::get_common_sec(Context<E> &ctx) {
|
||||
hdr->type = S_ZEROFILL;
|
||||
|
||||
common_sec = new InputSection<E>(ctx, *this, *hdr);
|
||||
sections.push_back(std::unique_ptr<InputSection<E>>(common_sec));
|
||||
sections.emplace_back(common_sec);
|
||||
}
|
||||
return common_sec;
|
||||
}
|
||||
@@ -523,7 +523,7 @@ template <typename E>
|
||||
DylibFile<E> *DylibFile<E>::create(Context<E> &ctx, MappedFile<Context<E>> *mf) {
|
||||
DylibFile<E> *dylib = new DylibFile<E>;
|
||||
dylib->mf = mf;
|
||||
ctx.dylib_pool.push_back(std::unique_ptr<DylibFile<E>>(dylib));
|
||||
ctx.dylib_pool.emplace_back(dylib);
|
||||
return dylib;
|
||||
};
|
||||
|
||||
|
||||
@@ -368,7 +368,7 @@ OutputSection<E>::get_instance(Context<E> &ctx, std::string_view segname,
|
||||
return osec;
|
||||
|
||||
OutputSection<E> *osec = new OutputSection<E>(ctx, segname, sectname);
|
||||
ctx.osec_pool.push_back(std::unique_ptr<OutputSection<E>>(osec));
|
||||
ctx.osec_pool.emplace_back(osec);
|
||||
return osec;
|
||||
}
|
||||
|
||||
@@ -426,7 +426,7 @@ OutputSegment<E>::get_instance(Context<E> &ctx, std::string_view name) {
|
||||
return seg;
|
||||
|
||||
OutputSegment<E> *seg = new OutputSegment<E>(name);
|
||||
ctx.segments.push_back(std::unique_ptr<OutputSegment<E>>(seg));
|
||||
ctx.segments.emplace_back(seg);
|
||||
return seg;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "byteorder.h"
|
||||
#include "big-endian.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
@@ -84,11 +84,18 @@ private:
|
||||
std::stringstream ss;
|
||||
};
|
||||
|
||||
template <typename C>
|
||||
static std::string add_color(C &ctx, std::string msg) {
|
||||
if (ctx.arg.color_diagnostics)
|
||||
return "mold: \033[0;1;31m" + msg + ":\033[0m ";
|
||||
return "mold: " + msg + ": ";
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
class Fatal {
|
||||
public:
|
||||
Fatal(C &ctx) : out(ctx, std::cerr) {
|
||||
out << "mold: ";
|
||||
out << add_color(ctx, "fatal");
|
||||
}
|
||||
|
||||
[[noreturn]] ~Fatal() {
|
||||
@@ -110,12 +117,12 @@ template <typename C>
|
||||
class Error {
|
||||
public:
|
||||
Error(C &ctx) : out(ctx, std::cerr) {
|
||||
if (ctx.arg.color_diagnostics)
|
||||
out << "mold: \033[0;1;31merror:\033[0m ";
|
||||
else
|
||||
out << "mold: error: ";
|
||||
|
||||
ctx.has_error = true;
|
||||
if (ctx.arg.noinhibit_exec) {
|
||||
out << add_color(ctx, "warning");
|
||||
} else {
|
||||
out << add_color(ctx, "error");
|
||||
ctx.has_error = true;
|
||||
}
|
||||
}
|
||||
|
||||
template <class T> Error &operator<<(T &&val) {
|
||||
@@ -132,16 +139,10 @@ class Warn {
|
||||
public:
|
||||
Warn(C &ctx) : out(ctx, std::cerr) {
|
||||
if (ctx.arg.fatal_warnings) {
|
||||
if (ctx.arg.color_diagnostics)
|
||||
out << "mold: \033[0;1;31merror:\033[0m ";
|
||||
else
|
||||
out << "mold: error: ";
|
||||
out << add_color(ctx, "error");
|
||||
ctx.has_error = true;
|
||||
} else {
|
||||
if (ctx.arg.color_diagnostics)
|
||||
out << "mold: \033[0;1;35mwarning:\033[0m ";
|
||||
else
|
||||
out << "mold: warning: ";
|
||||
out << add_color(ctx, "warning");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,7 +351,7 @@ public:
|
||||
}
|
||||
|
||||
bool has_key(i64 idx) {
|
||||
return keys[idx];
|
||||
return keys[idx].load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
static constexpr i64 MIN_NBUCKETS = 2048;
|
||||
@@ -374,51 +375,6 @@ private:
|
||||
static constexpr const char *marker = "marker";
|
||||
};
|
||||
|
||||
//
|
||||
// Bit vector
|
||||
//
|
||||
|
||||
class BitVector {
|
||||
class BitRef {
|
||||
public:
|
||||
BitRef(u8 &byte, u8 bitpos) : byte(byte), bitpos(bitpos) {}
|
||||
|
||||
BitRef &operator=(bool val) {
|
||||
if (val)
|
||||
byte |= (1 << bitpos);
|
||||
else
|
||||
byte &= ~(1 << bitpos);
|
||||
return *this;
|
||||
}
|
||||
|
||||
BitRef &operator=(const BitRef &other) {
|
||||
*this = (bool)other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return byte & (1 << bitpos);
|
||||
}
|
||||
|
||||
private:
|
||||
u8 &byte;
|
||||
u8 bitpos;
|
||||
};
|
||||
|
||||
public:
|
||||
void resize(i64 size) {
|
||||
vec.reset(new u8[(size + 7) / 8]);
|
||||
memset(vec.get(), 0, (size + 7) / 8);
|
||||
}
|
||||
|
||||
BitRef operator[](i64 i) {
|
||||
return BitRef(vec[i / 8], i % 8);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<u8[]> vec = nullptr;
|
||||
};
|
||||
|
||||
//
|
||||
// threads.cc
|
||||
//
|
||||
@@ -648,9 +604,6 @@ public:
|
||||
|
||||
template <typename C>
|
||||
MappedFile<C> *MappedFile<C>::open(C &ctx, std::string path) {
|
||||
MappedFile *mf = new MappedFile;
|
||||
mf->name = path;
|
||||
|
||||
if (path.starts_with('/') && !ctx.arg.chroot.empty())
|
||||
path = ctx.arg.chroot + "/" + path_clean(path);
|
||||
|
||||
@@ -658,12 +611,14 @@ MappedFile<C> *MappedFile<C>::open(C &ctx, std::string path) {
|
||||
if (fd == -1)
|
||||
return nullptr;
|
||||
|
||||
ctx.mf_pool.push_back(std::unique_ptr<MappedFile>(mf));
|
||||
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) == -1)
|
||||
Fatal(ctx) << path << ": fstat failed: " << errno_string();
|
||||
|
||||
MappedFile *mf = new MappedFile;
|
||||
ctx.mf_pool.push_back(std::unique_ptr<MappedFile>(mf));
|
||||
|
||||
mf->name = path;
|
||||
mf->size = st.st_size;
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
mold="$(pwd)/mold"
|
||||
t=out/test/elf/$testname
|
||||
mkdir -p $t
|
||||
|
||||
[ "$(uname -m)" = x86_64 ] || { echo skipped; exit; }
|
||||
|
||||
echo 'int main() {}' | aarch64-linux-gnu-gcc -o $t/exe -xc - >& /dev/null \
|
||||
|| { echo skipped; exit; }
|
||||
|
||||
cat <<EOF | aarch64-linux-gnu-gcc -o $t/a.o -c -g -xc -
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
printf("Hello world\n");
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
|
||||
aarch64-linux-gnu-gcc -B. -o $t/exe $t/a.o -static
|
||||
|
||||
readelf -p .comment $t/exe | grep -qw mold
|
||||
|
||||
readelf -a $t/exe > $t/log
|
||||
grep -Eq 'Machine:\s+AArch64' $t/log
|
||||
qemu-aarch64 -L /usr/aarch64-linux-gnu $t/exe | grep -q 'Hello world'
|
||||
|
||||
echo OK
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -10,7 +14,7 @@ mold="$(pwd)/mold"
|
||||
t=out/test/elf/$testname
|
||||
mkdir -p $t
|
||||
|
||||
[ "$(uname -m)" = x86_64 ] || { echo skipped; exit; }
|
||||
[ $MACHINE = x86_64 ] || { echo skipped; exit; }
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -x assembler -
|
||||
.globl foo
|
||||
@@ -43,6 +47,6 @@ int main() {
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe -no-pie $t/a.o $t/b.o
|
||||
$t/exe | grep -q '^ip=0x800000$'
|
||||
$QEMU $t/exe | grep -q '^ip=0x800000$'
|
||||
|
||||
echo OK
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
+7
-3
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -10,12 +14,12 @@ mold="$(pwd)/mold"
|
||||
t=out/test/elf/$testname
|
||||
mkdir -p $t
|
||||
|
||||
[ "$(uname -m)" = x86_64 ] || { echo skipped; exit; }
|
||||
[ $MACHINE = x86_64 ] || { echo skipped; exit; }
|
||||
|
||||
echo '.globl _start; _start: jmp loop' | $CC -o $t/a.o -c -x assembler -
|
||||
echo '.globl loop; loop: jmp loop' | $CC -o $t/b.o -c -x assembler -
|
||||
"$mold" -static -o $t/exe $t/a.o $t/b.o
|
||||
objdump -d $t/exe > /dev/null
|
||||
$OBJDUMP -d $t/exe > /dev/null
|
||||
file $t/exe | grep -q ELF
|
||||
|
||||
echo OK
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -38,6 +42,6 @@ int main() {
|
||||
EOF
|
||||
|
||||
$CC -B. -no-pie -o $t/exe $t/c.o $t/b.so
|
||||
$t/exe | grep -q '3 3 1'
|
||||
$QEMU $t/exe | grep -q '3 3 1'
|
||||
|
||||
echo OK
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -38,6 +42,6 @@ int main() {
|
||||
EOF
|
||||
|
||||
$CC -B. -no-pie -o $t/exe $t/c.o $t/b.so
|
||||
$t/exe | grep -q '3 3 0'
|
||||
$QEMU $t/exe | grep -q '3 3 0'
|
||||
|
||||
echo OK
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -38,6 +42,6 @@ int main() {
|
||||
EOF
|
||||
|
||||
$CC -B. -no-pie -o $t/exe $t/c.o $t/b.so
|
||||
$t/exe | grep -q '3 4 0'
|
||||
$QEMU $t/exe | grep -q '3 4 0'
|
||||
|
||||
echo OK
|
||||
+5
-1
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -28,7 +32,7 @@ readelf -n $t/exe | grep -q 'GNU.*0x00000014.*NT_GNU_BUILD_ID'
|
||||
$CC -B. -o $t/exe $t/a.c -Wl,-build-id=sha256
|
||||
readelf -n $t/exe | grep -q 'GNU.*0x00000020.*NT_GNU_BUILD_ID'
|
||||
|
||||
$CC -B. -o $t/exe $t/a.c -Wl,-build-id=0xdeadbeef
|
||||
readelf -n $t/exe | grep -q 'Build ID: deadbeef'
|
||||
$CC -B. -o $t/exe $t/a.c -Wl,-build-id=0xdeadbeefdeadbeef
|
||||
readelf -n $t/exe | grep -q 'Build ID: deadbeefdeadbeef'
|
||||
|
||||
echo OK
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -41,6 +45,6 @@ int main() {
|
||||
EOF
|
||||
|
||||
$CC -B. -no-pie -o $t/exe $t/a.so $t/b.o $t/c.o
|
||||
$t/exe | grep -q '^1 1 1$'
|
||||
$QEMU $t/exe | grep -q '^1 1 1$'
|
||||
|
||||
echo OK
|
||||
+5
-1
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
+5
-1
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -40,7 +44,7 @@ rm -f $t/e.a
|
||||
ar rcs $t/e.a $t/b.o $t/c.o $t/d.o
|
||||
|
||||
$CC -B. -o $t/exe $t/a.o $t/e.a
|
||||
$t/exe | grep -q '5 0 0 -1'
|
||||
$QEMU $t/exe | grep -q '5 0 0 -1'
|
||||
|
||||
cat <<EOF | $CC -fcommon -xc -c -o $t/f.o -
|
||||
int bar = 0;
|
||||
@@ -52,6 +56,6 @@ rm -f $t/f.a
|
||||
ar rcs $t/f.a $t/b.o $t/f.o
|
||||
|
||||
$CC -B. -o $t/exe $t/a.o $t/f.a
|
||||
$t/exe | grep -q '5 0 7 2'
|
||||
$QEMU $t/exe | grep -q '5 0 7 2'
|
||||
|
||||
echo OK
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -37,6 +41,6 @@ rm -f $t/e.a
|
||||
ar rcs $t/e.a $t/d.o
|
||||
|
||||
$CC -B. -o $t/exe $t/a.o $t/c.a $t/e.a
|
||||
$t/exe | grep -q 5
|
||||
$QEMU $t/exe | grep -q 5
|
||||
|
||||
echo OK
|
||||
+6
-2
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -29,7 +33,7 @@ int main() {
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe $t/a.o $t/b.o
|
||||
$t/exe | grep -q '0 5 42'
|
||||
$QEMU $t/exe | grep -q '0 5 42'
|
||||
|
||||
readelf --sections $t/exe > $t/log
|
||||
grep -q '.common .*NOBITS' $t/log
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -12,13 +16,13 @@ mkdir -p $t
|
||||
|
||||
which dwarfdump >& /dev/null || { echo skipped; exit; }
|
||||
|
||||
cat <<EOF | g++ -c -o $t/a.o -g -gz=zlib-gnu -xc++ -
|
||||
cat <<EOF | $CXX -c -o $t/a.o -g -gz=zlib-gnu -xc++ -
|
||||
int main() {
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF | g++ -c -o $t/b.o -g -gz=zlib -xc++ -
|
||||
cat <<EOF | $CXX -c -o $t/b.o -g -gz=zlib -xc++ -
|
||||
int foo() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -22,7 +26,7 @@ cat <<EOF | $CC -shared -o $t/b.so -xc -
|
||||
__attribute__((visibility("protected"))) int foo;
|
||||
EOF
|
||||
|
||||
! $CC -B. $t/a.o $t/b.so -o $t/exe >& $t/log || false
|
||||
! $CC -B. $t/a.o $t/b.so -o $t/exe >& $t/log -no-pie || false
|
||||
fgrep -q 'cannot make copy relocation for protected symbol' $t/log
|
||||
|
||||
echo OK
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -46,6 +50,6 @@ char readwrite[100] = "abc";
|
||||
EOF
|
||||
|
||||
$CC -B. $t/a.o $t/b.so -o $t/exe -no-pie
|
||||
$t/exe | grep -q '^sigsegv 0 1$'
|
||||
$QEMU $t/exe | grep -q '^sigsegv 0 1$'
|
||||
|
||||
echo OK
|
||||
+6
-2
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -35,6 +39,6 @@ EOF
|
||||
|
||||
$CC -B. -shared -o $t/c.so $t/c.o
|
||||
$CC -B. -no-pie -o $t/exe $t/a.o $t/b.o $t/c.so
|
||||
$t/exe | grep -q '42 42 1'
|
||||
$QEMU $t/exe | grep -q '42 42 1'
|
||||
|
||||
echo OK
|
||||
Executable
+42
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
mold="$(pwd)/mold"
|
||||
t=out/test/elf/$testname
|
||||
mkdir -p $t
|
||||
|
||||
cat <<EOF | $CXX -c -o $t/a.o -g -xc++ -
|
||||
#include <iostream>
|
||||
struct Foo {
|
||||
Foo() { std::cout << "Hello world\n"; }
|
||||
};
|
||||
Foo x;
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CXX -c -o $t/b.o -g -xc++ -
|
||||
#include <iostream>
|
||||
struct Foo {
|
||||
Foo() { std::cout << "Hello world\n"; }
|
||||
};
|
||||
Foo y;
|
||||
EOF
|
||||
|
||||
cat <<EOF | $CXX -o $t/c.o -c -xc++ -g -
|
||||
int main() {}
|
||||
EOF
|
||||
|
||||
$CXX -B. -o $t/exe $t/a.o $t/b.o $t/c.o -g
|
||||
$QEMU $t/exe | grep -q 'Hello world'
|
||||
|
||||
dwarfdump $t/exe > /dev/null
|
||||
|
||||
echo OK
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
+6
-2
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -28,6 +32,6 @@ EOF
|
||||
$CC -B. -o $t/exe $t/a.o -pie -Wl,-defsym=foo=16 \
|
||||
-Wl,-defsym=bar=0x2000 -Wl,-defsym=baz=print
|
||||
|
||||
$t/exe | grep -q '^Hello 0x10 0x2000$'
|
||||
$QEMU $t/exe | grep -q '^Hello 0x10 0x2000$'
|
||||
|
||||
echo OK
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
mold="$(pwd)/mold"
|
||||
t=out/test/elf/$testname
|
||||
mkdir -p $t
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc -
|
||||
#include <stdio.h>
|
||||
int main() {
|
||||
printf("Hello world\n");
|
||||
}
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe $t/a.o -Wl,-dependency-file=$t/dep
|
||||
|
||||
grep -q "dependency-file/exe:.*/a.o " $t/dep
|
||||
grep -q ".*/a.o:$" $t/dep
|
||||
|
||||
echo OK
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
mold="$(pwd)/mold"
|
||||
t=out/test/elf/$testname
|
||||
mkdir -p $t
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc -fPIC -
|
||||
void foo() {}
|
||||
EOF
|
||||
|
||||
$CC -B. -shared -o $t/b.so $t/a.o -Wl,-rpath=/foo
|
||||
readelf --dynamic $t/b.so | grep -q 'RUNPATH.*/foo'
|
||||
|
||||
$CC -B. -shared -o $t/b.so $t/a.o -Wl,-rpath=/foo -Wl,-enable-new-dtags
|
||||
readelf --dynamic $t/b.so | grep -q 'RUNPATH.*/foo'
|
||||
|
||||
$CC -B. -shared -o $t/b.so $t/a.o -Wl,-rpath=/foo -Wl,-disable-new-dtags
|
||||
readelf --dynamic $t/b.so | grep -q 'RPATH.*/foo'
|
||||
|
||||
echo OK
|
||||
+7
-1
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -10,6 +14,8 @@ mold="$(pwd)/mold"
|
||||
t=out/test/elf/$testname
|
||||
mkdir -p $t
|
||||
|
||||
[ $MACHINE = riscv64 ] && { echo skipped; exit; }
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -x assembler -Wa,-L -
|
||||
.text
|
||||
.globl _start
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
+8
-4
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -10,7 +14,7 @@ mold="$(pwd)/mold"
|
||||
t=out/test/elf/$testname
|
||||
mkdir -p $t
|
||||
|
||||
[ "$(uname -m)" = riscv64 ] && { echo skipped; exit; }
|
||||
[ $MACHINE = riscv64 ] && { echo skipped; exit; }
|
||||
|
||||
cat <<EOF | $CC -c -fPIC -o $t/a.o -xc -
|
||||
void keep();
|
||||
@@ -40,8 +44,8 @@ $CC -B. -o $t/d.so -shared $t/b.o -Wl,-init,init -Wl,-fini,fini
|
||||
$CC -o $t/exe1 $t/a.o $t/c.so
|
||||
$CC -o $t/exe2 $t/a.o $t/d.so
|
||||
|
||||
$t/exe1 > $t/log1
|
||||
$t/exe2 > $t/log2
|
||||
$QEMU $t/exe1 > $t/log1
|
||||
$QEMU $t/exe2 > $t/log2
|
||||
|
||||
! grep -q init $t/log1 || false
|
||||
! grep -q fini $t/log1 || false
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -13,7 +17,6 @@ mkdir -p $t
|
||||
cat <<EOF | $CC -c -o $t/a.o -x assembler -
|
||||
.globl _start
|
||||
_start:
|
||||
ret
|
||||
EOF
|
||||
|
||||
"$mold" -o $t/exe $t/a.o
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
+5
-5
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -28,8 +32,4 @@ int main() {
|
||||
}
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe -pie $t/b.o
|
||||
count=$(readelf -W --relocs $t/exe | grep -E 'R_[a-zA-Z0-9_]+_RELATIVE' | wc -l)
|
||||
readelf -W --dynamic $t/exe | grep -q "RELACOUNT.*\b$count\b"
|
||||
|
||||
echo OK
|
||||
+17
-10
@@ -1,24 +1,31 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
cd $(dirname $0)
|
||||
mold=`pwd`/../../mold
|
||||
echo -n "Testing $(basename -s .sh $0) ... "
|
||||
t=$(pwd)/../../out/test/elf/$(basename -s .sh $0)
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
mold="$(pwd)/mold"
|
||||
t=out/test/elf/$testname
|
||||
mkdir -p $t
|
||||
|
||||
[ "$(uname -m)" = x86_64 ] || { echo skipped; exit; }
|
||||
[ $MACHINE = x86_64 ] || { echo skipped; exit; }
|
||||
|
||||
cat <<EOF | cc -o $t/a.o -c -fPIC -xc -
|
||||
cat <<EOF | $CC -o $t/a.o -c -fPIC -xc -
|
||||
#include <stdio.h>
|
||||
int main() {
|
||||
puts("Hello world");
|
||||
}
|
||||
EOF
|
||||
|
||||
clang -fuse-ld=$mold -o $t/exe $t/a.o -Wl,-emit-relocs
|
||||
$t/exe | grep -q 'Hello world'
|
||||
$CC -B. -o $t/exe $t/a.o -Wl,-emit-relocs
|
||||
$QEMU $t/exe | grep -q 'Hello world'
|
||||
|
||||
readelf -r $t/exe | grep -q 'R_X86_64_PLT32.* puts - 4'
|
||||
readelf -S $t/exe | grep -Pq 'rela?\.text'
|
||||
|
||||
echo OK
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -21,6 +25,6 @@ rm -f $t/b.script
|
||||
touch $t/b.script
|
||||
|
||||
$CC -B. -o $t/exe $t/a.o -Wl,--version-script,$t/b.script
|
||||
$t/exe | grep -q 'Hello world'
|
||||
$QEMU $t/exe | grep -q 'Hello world'
|
||||
|
||||
echo OK
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -11,7 +15,7 @@ t=out/test/elf/$testname
|
||||
mkdir -p $t
|
||||
|
||||
# Skip if target is not x86-64
|
||||
[ "$(uname -m)" = x86_64 ] || { echo skipped; exit; }
|
||||
[ $MACHINE = x86_64 ] || { echo skipped; exit; }
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc -
|
||||
void _start() {}
|
||||
|
||||
+9
-16
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -10,18 +14,7 @@ mold="$(pwd)/mold"
|
||||
t=out/test/elf/$testname
|
||||
mkdir -p $t
|
||||
|
||||
case "$(uname -m)" in
|
||||
i386 | i686 | x86_64)
|
||||
base=0x201000
|
||||
;;
|
||||
aarch64)
|
||||
base=0x210000
|
||||
;;
|
||||
*)
|
||||
echo skipped
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
[ $MACHINE = x86_64 ] || { echo skipped; exit; }
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -x assembler -
|
||||
.globl foo, bar
|
||||
@@ -33,14 +26,14 @@ EOF
|
||||
|
||||
"$mold" -e foo -static -o $t/exe $t/a.o
|
||||
readelf -e $t/exe > $t/log
|
||||
grep -q "Entry point address:.*$base" $t/log
|
||||
grep -q "Entry point address:.*0x201000" $t/log
|
||||
|
||||
"$mold" -e bar -static -o $t/exe $t/a.o
|
||||
readelf -e $t/exe > $t/log
|
||||
grep -q "$(printf 'Entry point address:.*0x%x' $((base + 8)))" $t/log
|
||||
grep -q "Entry point address:.*0x201008" $t/log
|
||||
|
||||
"$mold" -static -o $t/exe $t/a.o
|
||||
readelf -e $t/exe > $t/log
|
||||
grep -q "Entry point address:.*$base" $t/log
|
||||
grep -q "Entry point address:.*0x201000" $t/log
|
||||
|
||||
echo OK
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
mold="$(pwd)/mold"
|
||||
t=out/test/elf/$testname
|
||||
mkdir -p $t
|
||||
|
||||
[ $MACHINE = x86_64 ] || { echo skipped; exit; }
|
||||
|
||||
cat <<EOF | $CXX -c -o $t/a.o -xc++ -fPIC -
|
||||
int main() {
|
||||
try {
|
||||
throw 0;
|
||||
} catch (int x) {
|
||||
return x;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
EOF
|
||||
|
||||
$CXX -B. -o $t/exe $t/a.o -mcmodel=large
|
||||
$QEMU $t/exe
|
||||
|
||||
$CXX -B. -o $t/exe $t/a.o -static -mcmodel=large
|
||||
$QEMU $t/exe
|
||||
|
||||
echo OK
|
||||
+17
-24
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -10,7 +14,7 @@ mold="$(pwd)/mold"
|
||||
t=out/test/elf/$testname
|
||||
mkdir -p $t
|
||||
|
||||
cat <<EOF > $t/a.cc
|
||||
cat <<EOF | $CXX -c -o $t/a.o -xc++ -fPIC -
|
||||
int main() {
|
||||
try {
|
||||
throw 0;
|
||||
@@ -21,32 +25,21 @@ int main() {
|
||||
}
|
||||
EOF
|
||||
|
||||
$CXX -B. -o $t/exe $t/a.cc -static
|
||||
$t/exe
|
||||
$CXX -B. -o $t/exe $t/a.o -static
|
||||
$QEMU $t/exe
|
||||
|
||||
$CXX -B. -o $t/exe $t/a.cc
|
||||
$t/exe
|
||||
$CXX -B. -o $t/exe $t/a.o
|
||||
$QEMU $t/exe
|
||||
|
||||
$CXX -B. -o $t/exe $t/a.cc -Wl,--gc-sections
|
||||
$t/exe
|
||||
$CXX -B. -o $t/exe $t/a.o -Wl,--gc-sections
|
||||
$QEMU $t/exe
|
||||
|
||||
$CXX -B. -o $t/exe $t/a.cc -static -Wl,--gc-sections
|
||||
$t/exe
|
||||
$CXX -B. -o $t/exe $t/a.o -static -Wl,--gc-sections
|
||||
$QEMU $t/exe
|
||||
|
||||
if [ "$(uname -m)" = x86_64 ]; then
|
||||
$CXX -B. -o $t/exe $t/a.cc -mcmodel=large
|
||||
$t/exe
|
||||
|
||||
$CXX -B. -o $t/exe $t/a.cc -static -mcmodel=large
|
||||
$t/exe
|
||||
elif [ "$(uname -m)" = aarch64 ]; then
|
||||
# The -mcmodel=large option is incompatible with -fPIC on aarch64, see
|
||||
# https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html#index-mcmodel_003dlarge
|
||||
$CXX -B. -o $t/exe $t/a.cc -mcmodel=large -fno-PIC
|
||||
$t/exe
|
||||
|
||||
$CXX -B. -o $t/exe $t/a.cc -static -mcmodel=large -fno-PIC
|
||||
$t/exe
|
||||
if [ $MACHINE = x86_64 -o $MACHINE = aarch64 ]; then
|
||||
$CXX -B. -o $t/exe $t/a.o -mcmodel=large -fno-PIC
|
||||
$QEMU $t/exe
|
||||
fi
|
||||
|
||||
echo OK
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -13,7 +17,6 @@ mkdir -p $t
|
||||
cat <<EOF | $CC -x assembler -c -o $t/a.o -
|
||||
.globl foo
|
||||
foo:
|
||||
ret
|
||||
EOF
|
||||
|
||||
rm -f $t/b.a
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
mold="$(pwd)/mold"
|
||||
t=out/test/elf/$testname
|
||||
mkdir -p $t
|
||||
|
||||
echo 'int main() {}' | $CC -o $t/exe -xc -
|
||||
dyld=$(readelf -Wl $t/exe | grep 'program interpreter:' | sed -e 's!.*: \(.*\)\]!\1!')
|
||||
|
||||
cat <<EOF | $CC -o $t/a.o -c -xc -fPIC -
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void hello() {
|
||||
puts("Hello world");
|
||||
}
|
||||
|
||||
void _start() {
|
||||
hello();
|
||||
exit(0);
|
||||
}
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/b.so $t/a.o -shared -Wl,--dynamic-linker=$dyld
|
||||
$QEMU $t/b.so | grep -q 'Hello world'
|
||||
|
||||
cat <<EOF | $CC -o $t/c.o -c -xc -
|
||||
void hello();
|
||||
int main() { hello(); }
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe $t/c.o $t/b.so
|
||||
$QEMU $t/exe | grep -q 'Hello world'
|
||||
|
||||
echo OK
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -28,7 +32,7 @@ $CC -shared -fPIC -o $t/b.so -xc /dev/null
|
||||
"$mold" -o $t/exe $t/a.o $t/b.so --export-dynamic
|
||||
|
||||
readelf --dyn-syms $t/exe > $t/log
|
||||
fgrep -q 'NOTYPE GLOBAL DEFAULT 6 bar' $t/log
|
||||
fgrep -q 'NOTYPE GLOBAL DEFAULT 6 _start' $t/log
|
||||
grep -Pq 'NOTYPE GLOBAL DEFAULT \d+ bar' $t/log
|
||||
grep -Pq 'NOTYPE GLOBAL DEFAULT \d+ _start' $t/log
|
||||
|
||||
echo OK
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
+5
-1
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
+5
-1
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -28,6 +32,6 @@ int main() {
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe -no-pie $t/b.o $t/a.so
|
||||
$t/exe | grep -q 1
|
||||
$QEMU $t/exe | grep -q 1
|
||||
|
||||
echo OK
|
||||
@@ -1,8 +1,12 @@
|
||||
#!/bin/bash
|
||||
export LANG=
|
||||
export LC_ALL=C
|
||||
set -e
|
||||
CC="${CC:-cc}"
|
||||
CXX="${CXX:-c++}"
|
||||
GCC="${GCC:-gcc}"
|
||||
GXX="${GXX:-g++}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
MACHINE="${MACHINE:-$(uname -m)}"
|
||||
testname=$(basename "$0" .sh)
|
||||
echo -n "Testing $testname ... "
|
||||
cd "$(dirname "$0")"/../..
|
||||
@@ -42,7 +46,7 @@ grep -qv live_var1 $t/log.1
|
||||
grep -qv live_var2 $t/log.1
|
||||
grep -qv dead_var1 $t/log.1
|
||||
grep -qv dead_var2 $t/log.1
|
||||
$t/exe1 | grep -q '1 2'
|
||||
$QEMU $t/exe1 | grep -q '1 2'
|
||||
|
||||
$CXX -B. -o $t/exe2 $t/a.cc -ffunction-sections -fdata-sections -Wl,-gc-sections
|
||||
|
||||
@@ -55,6 +59,6 @@ grep -q live_var1 $t/log.2
|
||||
grep -q live_var2 $t/log.2
|
||||
grep -qv dead_var1 $t/log.2
|
||||
grep -qv dead_var2 $t/log.2
|
||||
$t/exe2 | grep -q '1 2'
|
||||
$QEMU $t/exe2 | grep -q '1 2'
|
||||
|
||||
echo OK
|
||||
Loaded 100 of 480 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user