Compare commits

..
79 Commits
Author SHA1 Message Date
Rui Ueyama b53197300b Bump mold version to 2.40.0 2025-05-26 11:30:00 +09:00
Rui Ueyama 3e9321b9a0 Fix test flakiness 2025-05-26 11:30:00 +09:00
Rui Ueyama 142eeaba86 Fix CI 2025-05-25 11:42:00 +09:00
Rui Ueyama 15f6b9f966 Refactor 2025-05-24 13:43:17 +09:00
Rui Ueyama a310cd27d4 Refactor 2025-05-23 18:33:04 +09:00
Rui Ueyama 2d178eb1ed Fix ASAN failure on an invalid downcast 2025-05-22 20:35:04 +09:00
Rui Ueyama 0fdffad57f Fix an assertion failure when a word-size weak abs reloc is promoted to dynamic
Fixes https://github.com/rui314/mold/issues/1462
2025-05-22 19:53:19 +09:00
Rui Ueyama d05272c4ee Fix CI 2025-05-22 12:11:22 +09:00
Rui Ueyama 1683826f2f Fix tests
Closes https://github.com/rui314/mold/issues/1461
2025-05-22 10:34:29 +09:00
Rui Ueyama 9ddec785b1 Do not sort debug info sections unless we have a mix of DWARF32 and DWARF64 2025-05-21 21:38:55 +09:00
Rui Ueyama e4f867ad77 Stabilize test 2025-05-21 21:38:55 +09:00
Rui Ueyama b7ce902e52 Add a test 2025-05-20 22:23:10 +09:00
Rui Ueyama 133613a493 Refactor 2025-05-20 21:29:37 +09:00
Rui Ueyama b026d7119b Sort debug sections in a separate debug file 2025-05-20 18:27:56 +09:00
Rui Ueyama fc0dd85d87 Add a comment 2025-05-20 18:01:27 +09:00
Rui Ueyama 6577232458 Rename a variable 2025-05-20 16:22:19 +09:00
Rui Ueyama b504cacf25 Fix CI 2025-05-20 15:45:20 +09:00
Rui Ueyama 159ce3ba67 Place strings referenced by DWARF32 at the beginning of .debug_str 2025-05-20 15:29:50 +09:00
Rui Ueyama 4c2dae0a2d Fix CI 2025-05-20 14:11:42 +09:00
Rui Ueyama 275c53d615 Merge pull request #1459 from el-remph/static-ldso
Fix linked executable segfaulting if -I and -static are both passed
2025-05-20 11:43:36 +09:00
Rui Ueyama fdd959880a Sort not only .debug_info but also other debug info sections
I believe it is unlikely that sections such as .debug_line grow beyond
4 GiB to cause relocation overflows, but doing some extra work is
trivial, so just do it.
2025-05-20 11:35:07 +09:00
Rui Ueyama 26d827d6e7 Read not only one CU but all CUs in .debug_info 2025-05-19 23:37:58 +09:00
Rui Ueyama 858fd8c495 Refactor 2025-05-19 23:35:06 +09:00
remph c13ecc99eb Fix linked executable segfaulting if -I and -static are both passed 2025-05-19 15:27:06 +01:00
Rui Ueyama 122268d147 Add a test 2025-05-19 16:22:07 +09:00
Rui Ueyama b990467c16 Upgrade bundled OneTBB to 2022.1.0
Specifically, we need https://github.com/uxlfoundation/oneTBB/commit/3c85ad77664f6ec1d50d9aebbd2d2f21adae0e21
to fix our ASAN bot.
2025-05-19 14:58:05 +09:00
Rui Ueyama 3d866bea61 Fix CI 2025-05-19 14:26:09 +09:00
Rui Ueyama 19a1bc651f Layout DWARF32 before DWARF64 to mitigate relocation overflow issues
Previously, if the combined size of DWARF32 and DWARF64 debug
information exceeded 2^32 bytes, a relocation overflow could occur.

Since not all static libraries are built with -gdwarf64, this overflow
issue prevented some very large programs from using static libraries
built without -gdwarf64. Recompiling everything with -gdwarf64 is the
"right" way to fix it, but that's not always feasible for various
reasons.

This commit mitigates the issue.

By sorting the input .debug_info sections so that DWARF32 debug info
comes before DWARF64, a relocation overflow can occur only if the size
of DWARF32 debug info alone exceeds 2^32 bytes.
2025-05-19 14:12:39 +09:00
Rui Ueyama 18c813a21c Make all tests pass on FreeBSD/ARM64 2025-05-16 18:06:21 +09:00
Rui Ueyama af36625a02 Fix bug where --as-needed did not work if --allow-shlib-undefined is given 2025-05-16 15:40:21 +09:00
Rui Ueyama 53c1758503 Align TLS segment correctly
This effectively reverts 5249edc57f.
2025-05-16 11:42:26 +09:00
Rui Ueyama 67ceb41b79 Refactor 2025-05-15 16:08:50 +09:00
Rui Ueyama 7d99824c3d Bump mold version to 2.39.1 2025-05-12 11:26:07 +09:00
Rui Ueyama 344dcff8e8 Change an error mesasge 2025-05-12 10:51:51 +09:00
Rui Ueyama d0dffd5985 Attempt to fix a use-after-free
https://github.com/rui314/mold/issues/1456
2025-05-11 11:12:57 +09:00
Rui Ueyama 251041e371 Fix comment 2025-05-06 11:13:42 +09:00
Rui Ueyama 4dd872ee8d Do not make a call to an unresolved weak symbol into an infinite loop
We don't do that other than RISC-V, so let's stop doing it entirely.
2025-05-06 10:41:39 +09:00
Rui Ueyama bb1b9dda64 Refactor 2025-05-06 10:26:15 +09:00
Rui Ueyama 78ec306cf4 Merge pull request #1453 from ziyao233/riscv/relax-undef-weak-sym
shrink-sections: Relax weak undef symbols if promotion is possible
2025-05-06 08:41:41 +09:00
Yao Zi 163c62f491 shrink-sections: Relax weak undef symbols if promotion is possible
A call to weak undefined symbol could be relaxed as long as the symbol
could be promoted to a dynamic symbol, where the relaxation is done
against the known PLT address and the address of the symbol itself
doesn't really matter.

Signed-off-by: Yao Zi <[email protected]>
2025-05-05 07:01:21 -04:00
Rui Ueyama 026fb54b9d Bump mold version to 2.39.0 2025-05-05 12:27:19 +09:00
Rui Ueyama cd162ba9f6 Remove an unnecessary constraint 2025-05-05 11:47:36 +09:00
Rui Ueyama e08e7f6aa4 Do not turn a call to a resolved weak undefined symbol into an infinite loop
Fixes https://github.com/rui314/mold/issues/1451
2025-05-05 11:35:03 +09:00
Rui Ueyama 6be2e676d2 Refactor 2025-05-04 20:07:24 +09:00
Rui Ueyama b6565025a3 Merge pull request #1450 from pcc/cargo
Update instructions to enable mold in Cargo on all architectures
2025-05-04 18:01:12 +09:00
Peter Collingbourne 118dc5de8e Update instructions to enable mold in Cargo on all architectures
Make it so that the Cargo instructions enable mold for Linux on all
architectures not just x86_64.
2025-05-04 01:21:20 -07:00
Rui Ueyama f3475d9f09 Refactor 2025-05-04 16:04:33 +09:00
Rui Ueyama 299904e56f Give the lowest priority to gentoo-test script 2025-05-04 14:21:55 +09:00
Rui Ueyama fded2d8a77 Remove redundant thunks after computing section addresses
Previously, we might mistakenly remove necessary symbols from thunks,
causing the linker to crash.

This fixes https://github.com/rui314/mold/issues/1449
2025-05-04 13:36:23 +09:00
Rui Ueyama f0e726024e Remove an unused function 2025-05-03 16:27:00 +09:00
Rui Ueyama 3d29f1678d Refactor 2025-05-03 11:44:50 +09:00
Rui Ueyama 559f23ff81 Give the lowest priority to gentoo-test script 2025-05-03 11:43:53 +09:00
Rui Ueyama 3274bcbdf0 Fix spurious --no-allow-shlib-undefined error
Fixes https://github.com/rui314/mold/issues/1446
2025-05-02 21:34:01 +09:00
Rui Ueyama 83dd353979 Prevent oscillation when computing the size of the phdr
Fixes https://github.com/rui314/mold/issues/1447
2025-05-01 09:34:27 +09:00
Rui Ueyama 8bae43b842 Make --default-symver work with version scripts
Previously, symbols specified with the `global:` label were unversioned
even if --default-symver was given. Now, such symbols are versioned with
the output file's soname.

Fixes https://github.com/rui314/mold/issues/1448
2025-04-30 20:45:09 +09:00
Rui Ueyama 4038cfd64d Refactor 2025-04-30 16:20:36 +09:00
Rui Ueyama 41079c64f5 [arm32be] Report error if --be8 is missing 2025-04-30 08:24:49 +09:00
Rui Ueyama 02e410133b Refactor 2025-04-29 14:34:25 +09:00
Rui Ueyama e70236a45a Refactor 2025-04-29 13:03:28 +09:00
Rui Ueyama 285c5bb509 Refactor 2025-04-29 09:12:43 +09:00
Rui Ueyama 975da12333 Fix CI 2025-04-28 22:12:05 +09:00
Rui Ueyama 157b16adb5 Support big-endian ARM32 target 2025-04-28 21:09:59 +09:00
Rui Ueyama 564757a0f5 [RV32] Do not report error on R_RISCV_32 2025-04-28 21:09:59 +09:00
Rui Ueyama 216b4a2325 Refactor 2025-04-28 15:01:56 +09:00
Rui Ueyama 954107e29c Bump mold version to 2.38.1 2025-04-28 11:10:38 +09:00
Rui Ueyama 21e20e01da Make --no-allow-shlib-undefined not report an error on circular dependencies
Fixes https://github.com/rui314/mold/issues/1445
2025-04-28 10:50:48 +09:00
Rui Ueyama 161cb6c99c Fix CI 2025-04-26 11:40:07 +09:00
Rui Ueyama 21cfee556a Transitively test library dependencies for --no-allow-shlib-undefined
This change is needed to build sci-libs/spqr-2.0.9-r4::gentoo
2025-04-26 11:30:38 +09:00
Rui Ueyama 06203c4a72 Make dist.sh reproducible for ARM32 on ARM64 2025-04-23 15:36:06 +09:00
Rui Ueyama 232067634f Simplify 2025-04-23 13:25:15 +09:00
Rui Ueyama 3f9aab7066 Build with -Wl,--gdb-index only for Debug build 2025-04-23 12:42:27 +09:00
Rui Ueyama 8b88595f26 Make dist.sh's output deterministic
It looks like `podman build` command does not push the newly created
image with the `latest` tag to ghcr.io, so the following `podman run`
pulled an older image. That caused an issue that the scirpt created
a different tarball than the one on my local machine.

I don't know what is the "proper" fix, but it looks like adding
`--pull=never` to `podman run` forbids it from pulling an older image
from ghcr.io, solving the issue.
2025-04-23 12:31:28 +09:00
Rui Ueyama fa09c562c9 Simplify 2025-04-23 11:53:24 +09:00
Rui Ueyama 9457d993c4 Upgrade Clang 2025-04-22 18:10:19 +09:00
Rui Ueyama 43810dfecd Make --no-allow-shlib-undefined on by default for executables
Fixes https://github.com/rui314/mold/issues/1443
2025-04-22 13:20:13 +09:00
Rui Ueyama 2d6061a233 Change how we resolve versioned symbols with default version
If a versioned symbol is a default one, it can be referred to with two
names, e.g. `foo` and `foo@VERSION`. Previously, we forward
`foo@VERSION` to `foo` if it is undefined, but it didn't always work
correctly. Now, we resolve both `foo` and `foo@VERSION` as usual and
then forward `foo@VERSION` to `foo` afterwards.

Fixes https://github.com/rui314/mold/issues/1169
2025-04-22 12:21:50 +09:00
Rui Ueyama 3d6aad65b0 Simplify 2025-04-21 13:55:31 +09:00
Rui Ueyama f7b1920f84 Make dist.sh reproducible again
mimalloc 2.2.2 started using __DATE__ and __TIME__ macros which make
the output non-reproducible. We can set the SOURCE_DATE_EPOCH environment
variable to fix the "current" timestamp.
2025-04-16 16:17:28 +09:00
Rui Ueyama b42f978212 Bump mold version to 2.38.0 2025-04-16 11:57:48 +09:00
316 changed files with 6576 additions and 3794 deletions

No files matched your search

+24 -10
View File
@@ -5,7 +5,7 @@ apt-get install -y wget xz-utils
# Install a 32-bit RISC-V toolchain
mkdir /rv32
wget -O- -q https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2025.01.20/riscv32-glibc-ubuntu-22.04-gcc-nightly-2025.01.20-nightly.tar.xz | tar -C /rv32 --strip-components=1 --xz -xf -
wget -O- --progress=dot:mega https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2025.01.20/riscv32-glibc-ubuntu-22.04-gcc-nightly-2025.01.20-nightly.tar.xz | tar -C /rv32 --strip-components=1 --xz -xf -
ln -sf /rv32/sysroot /usr/riscv32-linux-gnu
echo '/rv32/bin/riscv32-unknown-linux-gnu-gcc -L/usr/riscv32-linux-gnu "$@"' > /usr/bin/riscv32-linux-gnu-gcc
@@ -18,7 +18,7 @@ done
# Install a LoongArch toolchain
mkdir /larch
wget -O- -q https://github.com/loongson/build-tools/releases/download/2025.02.21/x86_64-cross-tools-loongarch64-binutils_2.44-gcc_14.2.0-glibc_2.41.tar.xz | tar -C /larch --strip-components=1 --xz -xf -
wget -O- --progress=dot:mega https://github.com/loongson/build-tools/releases/download/2025.02.21/x86_64-cross-tools-loongarch64-binutils_2.44-gcc_14.2.0-glibc_2.41.tar.xz | tar -C /larch --strip-components=1 --xz -xf -
cp -r /larch/loongarch64-unknown-linux-gnu/lib/* /larch/target/lib64
ln -sf /larch/target /usr/loongarch64-linux-gnu
@@ -27,25 +27,39 @@ for i in gcc g++ objdump objcopy strip; do
ln -sf /larch/bin/loongarch64-unknown-linux-gnu-$i /usr/bin/loongarch64-linux-gnu-$i
done
wget -O /usr/local/bin/qemu-loongarch64 -q https://github.com/loongson/build-tools/releases/download/2024.11.01/qemu-loongarch64
wget -O /usr/local/bin/qemu-loongarch64 --progress=dot:mega https://github.com/loongson/build-tools/releases/download/2024.11.01/qemu-loongarch64
chmod 755 /usr/local/bin/qemu-loongarch64
# Install ARM32 big-endian toolchain
mkdir /armeb
wget -O- --progress=dot:mega https://toolchains.bootlin.com/downloads/releases/toolchains/armebv7-eabihf/tarballs/armebv7-eabihf--glibc--stable-2024.05-1.tar.xz | tar -C /armeb --strip-components=1 --xz -xf -
ln -sf /armeb/armeb-buildroot-linux-gnueabihf/sysroot /usr/armeb-linux-gnueabihf
echo '/armeb/bin/armeb-linux-gcc -L/usr/armeb-linux-gnu "$@"' > /usr/bin/armeb-linux-gnueabihf-gcc
echo '/armeb/bin/armeb-linux-g++ -L/usr/armeb-linux-gnu "$@"' > /usr/bin/armeb-linux-gnueabihf-g++
chmod 755 /usr/bin/armeb-linux-gnueabihf-{gcc,g++}
for i in objdump objcopy strip; do
ln -sf /armeb/bin/armeb-linux-$i /usr/bin/armeb-linux-gnueabihf-$i
done
# Install ARM64 big-endian toolchain
mkdir /aarch64be
wget -O- -q https://sources.buildroot.net/toolchain-external-arm-aarch64-be/arm-gnu-toolchain-13.3.rel1-x86_64-aarch64_be-none-linux-gnu.tar.xz | tar -C /aarch64be --strip-components=1 --xz -xf -
wget -O- --progress=dot:mega https://toolchains.bootlin.com/downloads/releases/toolchains/aarch64be/tarballs/aarch64be--glibc--stable-2024.05-1.tar.xz | tar -C /aarch64be --strip-components=1 --xz -xf -
ln -sf /aarch64be/aarch64_be-none-linux-gnu/libc /usr/aarch64_be-linux-gnu
echo '/aarch64be/bin/aarch64_be-none-linux-gnu-gcc -L/usr/aarch64_be-linux-gnu "$@"' > /usr/bin/aarch64_be-linux-gnu-gcc
echo '/aarch64be/bin/aarch64_be-none-linux-gnu-g++ -L/usr/aarch64_be-linux-gnu "$@"' > /usr/bin/aarch64_be-linux-gnu-g++
ln -sf /aarch64be/aarch64_be-buildroot-linux-gnu/sysroot /usr/aarch64_be-linux-gnu
echo '/aarch64be/bin/aarch64_be-linux-gcc -L/usr/aarch64_be-linux-gnu "$@"' > /usr/bin/aarch64_be-linux-gnu-gcc
echo '/aarch64be/bin/aarch64_be-linux-g++ -L/usr/aarch64_be-linux-gnu "$@"' > /usr/bin/aarch64_be-linux-gnu-g++
chmod 755 /usr/bin/aarch64_be-linux-gnu-{gcc,g++}
for i in objdump objcopy strip; do
ln -sf /aarch64be/bin/aarch64_be-none-linux-gnu-$i /usr/bin/aarch64_be-linux-gnu-$i
ln -sf /aarch64be/bin/aarch64_be-linux-$i /usr/bin/aarch64_be-linux-gnu-$i
done
# Install SH4 big-endian toolchain
mkdir /sh4aeb
wget -O- -q https://toolchains.bootlin.com/downloads/releases/toolchains/sh-sh4aeb/tarballs/sh-sh4aeb--glibc--stable-2024.05-1.tar.xz | tar -C /sh4aeb --strip-components=1 --xz -xf -
wget -O- --progress=dot:mega https://toolchains.bootlin.com/downloads/releases/toolchains/sh-sh4aeb/tarballs/sh-sh4aeb--glibc--stable-2024.05-1.tar.xz | tar -C /sh4aeb --strip-components=1 --xz -xf -
ln -sf /sh4aeb/sh4aeb-buildroot-linux-gnu/sysroot /usr/sh4aeb-linux-gnu
echo '/sh4aeb/bin/sh4aeb-linux-gcc -L/usr/sh4aeb-linux-gnu "$@"' > /usr/bin/sh4aeb-linux-gnu-gcc
echo '/sh4aeb/bin/sh4aeb-linux-g++ -L/usr/sh4aeb-linux-gnu "$@"' > /usr/bin/sh4aeb-linux-gnu-g++
@@ -57,5 +71,5 @@ done
# Install Intel SDE CPU emulator for CET-related tests
mkdir /sde
wget -O- -q https://downloadmirror.intel.com/843185/sde-external-9.48.0-2024-11-25-lin.tar.xz | tar -C /sde --strip-components=1 --xz -xf -
wget -O- --progress=dot:mega https://downloadmirror.intel.com/843185/sde-external-9.48.0-2024-11-25-lin.tar.xz | tar -C /sde --strip-components=1 --xz -xf -
ln -s /sde/sde64 /usr/bin
+13 -10
View File
@@ -44,7 +44,7 @@
# features and behave exactly the same.
cmake_minimum_required(VERSION 3.14)
project(mold VERSION 2.37.1)
project(mold VERSION 2.40.0)
include(CMakeDependentOption)
include(CheckSymbolExists)
@@ -62,7 +62,11 @@ endif()
# Build mold itself using mold if -DMOLD_USE_MOLD=ON
option(MOLD_USE_MOLD "Use mold to build mold" OFF)
if(MOLD_USE_MOLD)
target_link_options(mold PRIVATE -fuse-ld=mold -Wl,--gdb-index)
target_link_options(mold PRIVATE -fuse-ld=mold)
if(CMAKE_BUILD_TYPE MATCHES "Deb")
target_link_options(mold PRIVATE -Wl,--gdb-index)
endif()
if(CMAKE_BUILD_TYPE MATCHES "^Rel")
target_link_options(mold PRIVATE -Wl,--gc-sections,--icf=safe)
@@ -289,10 +293,10 @@ check_symbol_exists(uname sys/utsname.h HAVE_UNAME)
add_custom_target(git_hash
COMMAND ${CMAKE_COMMAND}
-DSOURCE_DIR=${CMAKE_SOURCE_DIR}
-DOUTPUT_FILE=${CMAKE_BINARY_DIR}/git-hash.cc
-DOUTPUT_FILE=${CMAKE_BINARY_DIR}/mold-git-hash.h
-P ${CMAKE_SOURCE_DIR}/lib/update-git-hash.cmake
DEPENDS lib/update-git-hash.cmake
BYPRODUCTS git-hash.cc
BYPRODUCTS mold-git-hash.h
VERBATIM)
add_dependencies(mold git_hash)
@@ -310,10 +314,12 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
# compiler instances. This is hacky but greatly reduces compile time
# on a multicore machine.
list(APPEND MOLD_ELF_TARGETS
X86_64 I386 ARM64LE ARM64BE ARM32 RV32LE RV32BE RV64LE RV64BE PPC32
PPC64V1 PPC64V2 S390X SPARC64 M68K SH4LE SH4BE LOONGARCH32 LOONGARCH64 ARC)
X86_64 I386 ARM64LE ARM64BE ARM32LE ARM32BE RV32LE RV32BE RV64LE
RV64BE PPC32 PPC64V1 PPC64V2 S390X SPARC64 M68K SH4LE SH4BE
LOONGARCH32 LOONGARCH64)
list(APPEND MOLD_ELF_TEMPLATE_FILES
src/arch-arm32.cc
src/arch-arm64.cc
src/arch-loongarch.cc
src/arch-riscv.cc
@@ -373,7 +379,6 @@ endforeach()
# Add other non-template source files.
target_sources(mold PRIVATE
git-hash.cc
lib/compress.cc
lib/crc32.cc
lib/demangle.cc
@@ -385,8 +390,6 @@ target_sources(mold PRIVATE
lib/perf.cc
lib/random.cc
lib/tar.cc
src/arch-arc.cc
src/arch-arm32.cc
src/arch-i386.cc
src/arch-m68k.cc
src/arch-ppc32.cc
@@ -395,8 +398,8 @@ target_sources(mold PRIVATE
src/arch-s390x.cc
src/arch-sparc64.cc
src/arch-x86-64.cc
src/config.cc
src/elf.cc
src/entry.cc
third-party/rust-demangle/rust-demangle.c
)
+2 -2
View File
@@ -120,7 +120,7 @@ accept an absolute path as an argument for `-fuse-ld`.
Create `.cargo/config.toml` in your project directory with the following:
```toml
[target.x86_64-unknown-linux-gnu]
[target.'cfg(target_os = "linux")']
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=/path/to/mold"]
```
@@ -131,7 +131,7 @@ example above, we use `clang` as a linker driver since it always accepts the
may be able to remove the `linker = "clang"` line.
```toml
[target.x86_64-unknown-linux-gnu]
[target.'cfg(target_os = "linux")']
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
```
-29
View File
@@ -1,29 +0,0 @@
#!/bin/bash -x
set -e
apt-get install -y git bzip2 ninja-build pkg-config libglib2.0-dev python3-distutils-extra libpixman-1-dev
cd /
git clone --depth=1 https://github.com/foss-for-synopsys-dwc-arc-processors/qemu.git
mkdir /qemu/build
cd /qemu/build
../configure --static --extra-cflags=-Wno-error
ninja
ln -s /qemu/build/qemu-arc /qemu/build/qemu-arc64 /usr/local/bin
apt-get install -y libgmp-dev libmpfr-dev texinfo bison yacc flex
cd /
git clone --depth=1 https://github.com/foss-for-synopsys-dwc-arc-processors/binutils-gdb.git
cd /binutils-gdb
mkdir build
cd build
../configure --target=arc-linux
make -j$(nproc)
apt-get install -y mold
mkdir /build
cd /build
cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DMOLD_USE_MOLD=1 /mold
ninja
TRIPLE=arc-linux-gnu bash -x /mold/test/hello-dynamic.sh
+13 -5
View File
@@ -126,10 +126,10 @@ RUN mkdir /build && \
make install && \
rm -rf /build
# Build LLVM 19
# Build LLVM 20
RUN mkdir /build && \
cd /build && \
wget -O- --no-check-certificate --progress=dot:mega https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-19.1.7.tar.gz | tar xzf - --strip-components=1 && \
wget -O- --no-check-certificate --progress=dot:mega https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-20.1.3.tar.gz | tar xzf - --strip-components=1 && \
mkdir b && \
cd b && \
cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS=clang ../llvm && \
@@ -195,14 +195,22 @@ dest=mold-$version-$arch-linux
# We use the timestamp of the last Git commit as the file timestamp
# for build artifacts.
timestamp="$(git log -1 --format=%ci)"
timestamp=$(git log -1 --format=%ct)
# Build mold in a container.
mkdir -p dist
# If the host is ARM64, `uname -m` in an ARM32 container running on it
# reports it not as ARM32 but as ARM64, which confuses BLAKE3's cmake script
# and erroneously enables NEON SIMD instructions. `setarch` can be used
# to change the output of `uname -m`.
[ $arch = arm ] && setarch='setarch linux32'
podman run --arch $arch -it --rm --userns=host --pids-limit=-1 --network=none \
-v "$(pwd):/mold:ro" -v "$(pwd)/dist:/dist" $image bash -c "
--pull=never -v "$(pwd):/mold:ro" -v "$(pwd)/dist:/dist" $image \
$setarch bash -c "
set -e
export SOURCE_DATE_EPOCH=$timestamp
mkdir /build
cd /build
cmake -DCMAKE_BUILD_TYPE=Release -DMOLD_MOSTLY_STATIC=1 -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ /mold
@@ -212,7 +220,7 @@ cmake -DMOLD_USE_MOLD=1 .
cmake --build . -j\$(nproc)
ctest --output-on-failure -j\$(nproc)
cmake --install . --prefix $dest --strip
find $dest -print | xargs touch --no-dereference --date='$timestamp'
find $dest -print | xargs touch --no-dereference --date=@$timestamp
find $dest -print | sort | tar -cf - --no-recursion --files-from=- | gzip -9nc > /dist/$dest.tar.gz
cp mold /dist
sha256sum /dist/$dest.tar.gz
+1 -1
View File
@@ -11,7 +11,7 @@ set -x
case "$ID" in
ubuntu | pop | linuxmint | debian | raspbian)
apt-get install -y qemu-user {gcc,g++}-{i686,aarch64,riscv64,powerpc,powerpc64,powerpc64le,s390x,sparc64,m68k,sh4,arc}-linux-gnu {gcc,g++}-arm-linux-gnueabihf
apt-get install -y qemu-user {gcc,g++}-{i686,aarch64,riscv64,powerpc,powerpc64,powerpc64le,s390x,sparc64,m68k,sh4}-linux-gnu {gcc,g++}-arm-linux-gnueabihf
;;
*)
echo "Error: don't know anything about build dependencies on $ID-$VERSION_ID"
+1 -3
View File
@@ -69,8 +69,6 @@ inline char *output_tmpfile;
inline u8 *output_buffer_start = nullptr;
inline u8 *output_buffer_end = nullptr;
extern std::string mold_git_hash;
std::string errno_string();
void cleanup();
void install_signal_handler();
@@ -667,7 +665,7 @@ public:
// Since the shard is circular, we need to handle the last entries
// as if they were next to the first entries.
while (entries[end - 1].key)
while (begin < end && entries[end - 1].key)
vec.push_back(entries + --end);
// Find entries contiguous in the buckets and sort them.
+2 -2
View File
@@ -54,14 +54,14 @@ dir=gentoo/$git_hash
mkdir -p "$dir"/success "$dir"/failure
$podman nice -n 19 bash -c "$cmd1 && $cmd2 && $cmd3" >& "$dir"/"$filename".mold
$podman chrt --idle 0 nice -n 19 bash -c "$cmd1 && $cmd2 && $cmd3" >& "$dir"/"$filename".mold
if [ $? = 0 ]; then
mv "$dir"/"$filename".mold "$dir"/success
else
mv "$dir"/"$filename".mold "$dir"/failure
fi
$podman nice -n 19 bash -c "$cmd2 && $cmd3" >& "$dir"/"$filename".ld
$podman chrt --idle 0 nice -n 19 bash -c "$cmd2 && $cmd3" >& "$dir"/"$filename".ld
if [ $? = 0 ]; then
mv "$dir"/"$filename".ld "$dir"/success
else
+5 -5
View File
@@ -17,11 +17,11 @@ if(EXISTS "${SOURCE_DIR}/.git/HEAD")
endif()
# Create new file contents and update a given file if necessary.
set(NEW_CONTENTS "#include <string>
namespace mold {
std::string mold_git_hash = \"${HASH}\";
}
")
if("${HASH}" STREQUAL "")
set(NEW_CONTENTS "")
else()
set(NEW_CONTENTS "#define MOLD_GIT_HASH \"${HASH}\"\n")
endif()
if(EXISTS "${OUTPUT_FILE}")
file(READ "${OUTPUT_FILE}" OLD_CONTENTS)
-285
View File
@@ -1,285 +0,0 @@
// r31 is blink. r30 is I believe a scratch register.
//
// Programs might use one of several branch, jump, and link instructions
// to control execution flow through direct and indirect function calls
// and branching. For function calling, the conditional branch-and-link
// instruction has a maximum branch range of ±1 MiB, and the target
// address is 32-bit-aligned. The unconditional branch-and-link format has
// a maximum branch range of ±16 MiB.
//
// https://github.com/foss-for-synopsys-dwc-arc-processors/arc-ABI-manual/blob/master/ARCv2_ABI.pdf
#include "mold.h"
namespace mold {
using E = ARC;
// An integer-like class for the mixed-endian encoding
class M32 {
public:
operator u32() const {
return (buf[1] << 24) | (buf[0] << 16) | (buf[3] << 8) | buf[2];
}
M32 &operator=(u32 x) {
buf[0] = x >> 16;
buf[1] = x >> 24;
buf[2] = x;
buf[3] = x >> 8;
return *this;
}
M32 &operator&=(u32 x) { return *this = *this & x; }
M32 &operator|=(u32 x) { return *this = *this | x; }
u8 buf[4];
};
static void write_disp7u(u8 *loc, u32 val) {
*(ul16 *)loc |= (bits(val, 6, 3) << 4) | bits(val, 2, 0);
}
static void write_disp9(u8 *loc, u32 val) {
*(M32 *)loc |= bits(val, 8, 0);
}
static void write_disp9ls(u8 *loc, u32 val) {
*(M32 *)loc |= (bits(val, 7, 0) << 16) | (bit(val, 8) << 15);
}
static void write_disp9s(u8 *loc, u32 val) {
*(ul16 *)loc |= bits(val, 8, 0);
}
static void write_disp10u(u8 *loc, u32 val) {
*(ul16 *)loc |= bits(val, 9, 0);
}
static void write_disp13s(u8 *loc, u32 val) {
*(ul16 *)loc |= bits(val, 12, 0);
}
static void write_disp21h(u8 *loc, u32 val) {
*(M32 *)loc |= (bits(val, 10, 1) << 17) | (bits(val, 20, 11) << 6);
}
static void write_disp21w(u8 *loc, u32 val) {
*(M32 *)loc |= (bits(val, 10, 2) << 18) | (bits(val, 20, 11) << 6);
}
static void write_disp25h(u8 *loc, u32 val) {
*(M32 *)loc |= (bits(val, 10, 1) << 17) | (bits(val, 20, 11) << 6) |
bits(val, 24, 21);
}
static void write_disp25w(u8 *loc, u32 val) {
*(M32 *)loc |= (bits(val, 10, 2) << 18) | (bits(val, 20, 11) << 6) |
bits(val, 24, 21);
}
static void write_disps9(u8 *loc, u32 val) {
*(ul16 *)loc |= bits(val, 10, 2);
}
static void write_disps12(u8 *loc, u32 val) {
*(M32 *)loc |= (bits(val, 5, 0) << 6) | bits(val, 11, 6);
}
template <>
void write_plt_header(Context<E> &ctx, u8 *buf) {
static const ul16 insn[] = {
0x2730, 0x7f8b, 0, 0, // ld r11, [pcl,0]
0x2730, 0x7f8a, 0, 0, // ld r10, [pcl,0]
0x2020, 0x0280, // j [r10]
0, 0, // (address of GOTPLT)
};
memcpy(buf, insn, sizeof(insn));
*(M32 *)(buf + 4) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr + 4;
*(M32 *)(buf + 12) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr;
*(M32 *)(buf + 20) = ctx.gotplt->shdr.sh_addr;
}
template <>
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
static const ul16 insn[] = {
0x2730, 0x7f8c, 0, 0, // ld r12, [pcl,0]
0x2021, 0x0300, // j.d [r12]
0x240a, 0x1fc0, // mov r12,pcl
};
memcpy(buf, insn, sizeof(insn));
*(M32 *)(buf + 4) = sym.get_gotplt_addr(ctx) - sym.get_plt_addr(ctx);
}
template <>
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
static const ul16 insn[] = {
0x2730, 0x7f8c, 0, 0, // ld r12, [pcl,0]
0x2021, 0x0300, // j.d [r12]
0x240a, 0x1fc0, // mov r12,pcl
};
memcpy(buf, insn, sizeof(insn));
*(M32 *)(buf + 4) = sym.get_got_pltgot_addr(ctx) - sym.get_plt_addr(ctx);
}
template <>
void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_NONE:
break;
case R_ARC_32:
*(ul32 *)loc = val;
break;
case R_ARC_32_PCREL:
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
break;
default:
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
u64 S = sym.get_addr(ctx);
u64 A = rel.r_addend;
u64 P = get_addr() + rel.r_offset;
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
u64 GOT = ctx.got->shdr.sh_addr;
switch (rel.r_type) {
case R_ARC_32:
*(ul32 *)loc = S + A;
break;
case R_ARC_32_ME:
*(M32 *)loc = S + A;
break;
case R_ARC_S25H_PCREL:
case R_ARC_S25H_PCREL_PLT:
write_disp25h(loc, align_to(S + A - P, 2));
break;
case R_ARC_S25W_PCREL:
case R_ARC_S25W_PCREL_PLT:
write_disp25w(loc, align_to(S + A - P, 4));
break;
case R_ARC_PC32:
*(M32 *)loc = S + A - P;
break;
case R_ARC_32_PCREL:
*(ul32 *)loc = S + A - P;
break;
case R_ARC_GOTPC32:
*(M32 *)loc = GOT + G + A - P;
break;
case R_ARC_TLS_LE_32:
*(M32 *)loc = S + A - ctx.tp_addr;
break;
case R_ARC_TLS_IE_GOT:
*(M32 *)loc = sym.get_gottp_addr(ctx) + A - P;
break;
default:
unreachable();
}
}
}
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
SectionFragment<E> *frag;
i64 frag_addend;
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
u64 A = frag ? frag_addend : (i64)rel.r_addend;
switch (rel.r_type) {
case R_ARC_32:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ul32 *)loc = *val;
else
*(ul32 *)loc = S + A;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
<< rel;
}
}
}
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (sym.is_ifunc())
Error(ctx) << sym << ": GNU ifunc symbol is not supported on m68k";
switch (rel.r_type) {
case R_ARC_32:
break;
case R_ARC_32_ME:
scan_absrel(ctx, sym, rel);
break;
case R_ARC_S25H_PCREL:
case R_ARC_S25W_PCREL:
case R_ARC_32_PCREL:
case R_ARC_PC32:
scan_pcrel(ctx, sym, rel);
break;
case R_ARC_S25H_PCREL_PLT:
case R_ARC_S25W_PCREL_PLT:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_ARC_GOTPC32:
sym.flags |= NEEDS_GOT;
break;
case R_ARC_TLS_LE_32:
check_tlsle(ctx, sym, rel);
break;
case R_ARC_TLS_IE_GOT:
sym.flags |= NEEDS_GOTTP;
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
template <>
u64 get_eflags(Context<E> &ctx) {
return 0x406;
}
} // namespace mold
+173 -103
View File
@@ -31,18 +31,21 @@
//
// https://github.com/ARM-software/abi-aa/blob/main/aaelf32/aaelf32.rst
#if MOLD_ARM32LE || MOLD_ARM32BE
#include "mold.h"
#include <tbb/parallel_for.h>
#include <tbb/parallel_for_each.h>
namespace mold {
using E = ARM32;
using E = MOLD_TARGET;
template <>
i64 get_addend(u8 *loc, const ElfRel<E> &rel) {
ul32 *arm = (ul32 *)loc;
ul16 *thm = (ul16 *)loc;
U32<E> *arm = (U32<E> *)loc;
U16<E> *thm = (U16<E> *)loc;
switch (rel.r_type) {
case R_ARM_ABS32:
@@ -59,7 +62,7 @@ i64 get_addend(u8 *loc, const ElfRel<E> &rel) {
case R_ARM_TLS_GOTDESC:
case R_ARM_TARGET1:
case R_ARM_TARGET2:
return (il32)*arm;
return (I32<E>)*arm;
case R_ARM_THM_JUMP8:
return sign_extend(thm[0], 8) << 1;
case R_ARM_THM_JUMP11:
@@ -125,7 +128,7 @@ i64 get_addend(u8 *loc, const ElfRel<E> &rel) {
static void write_arm_mov(u8 *loc, u32 val) {
u32 imm12 = bits(val, 11, 0);
u32 imm4 = bits(val, 15, 12);
*(ul32 *)loc = (*(ul32 *)loc & 0xfff0'f000) | (imm4 << 16) | imm12;
*(U32<E> *)loc = (*(U32<E> *)loc & 0xfff0'f000) | (imm4 << 16) | imm12;
}
static void write_thm_b21(u8 *loc, u32 val) {
@@ -135,7 +138,7 @@ static void write_thm_b21(u8 *loc, u32 val) {
u32 imm6 = bits(val, 17, 12);
u32 imm11 = bits(val, 11, 1);
ul16 *buf = (ul16 *)loc;
U16<E> *buf = (U16<E> *)loc;
buf[0] = (buf[0] & 0b1111'1011'1100'0000) | (S << 10) | imm6;
buf[1] = (buf[1] & 0b1101'0000'0000'0000) | (J1 << 13) | (J2 << 11) | imm11;
}
@@ -149,7 +152,7 @@ static void write_thm_b25(u8 *loc, u32 val) {
u32 imm10 = bits(val, 21, 12);
u32 imm11 = bits(val, 11, 1);
ul16 *buf = (ul16 *)loc;
U16<E> *buf = (U16<E> *)loc;
buf[0] = (buf[0] & 0b1111'1000'0000'0000) | (S << 10) | imm10;
buf[1] = (buf[1] & 0b1101'0000'0000'0000) | (J1 << 13) | (J2 << 11) | imm11;
}
@@ -160,7 +163,7 @@ static void write_thm_mov(u8 *loc, u32 val) {
u32 imm3 = bits(val, 10, 8);
u32 imm8 = bits(val, 7, 0);
ul16 *buf = (ul16 *)loc;
U16<E> *buf = (U16<E> *)loc;
buf[0] = (buf[0] & 0b1111'1011'1111'0000) | (i << 10) | imm4;
buf[1] = (buf[1] & 0b1000'1111'0000'0000) | (imm3 << 12) | imm8;
}
@@ -184,13 +187,13 @@ void write_addend(u8 *loc, i64 val, const ElfRel<E> &rel) {
case R_ARM_TLS_GOTDESC:
case R_ARM_TARGET1:
case R_ARM_TARGET2:
*(ul32 *)loc = val;
*(U32<E> *)loc = val;
break;
case R_ARM_THM_JUMP8:
*(ul16 *)loc = (*(ul16 *)loc & 0xff00) | bits(val, 8, 1);
*(U16<E> *)loc = (*(U16<E> *)loc & 0xff00) | bits(val, 8, 1);
break;
case R_ARM_THM_JUMP11:
*(ul16 *)loc = (*(ul16 *)loc & 0xf800) | bits(val, 11, 1);
*(U16<E> *)loc = (*(U16<E> *)loc & 0xf800) | bits(val, 11, 1);
break;
case R_ARM_THM_CALL:
case R_ARM_THM_JUMP24:
@@ -200,7 +203,7 @@ void write_addend(u8 *loc, i64 val, const ElfRel<E> &rel) {
case R_ARM_CALL:
case R_ARM_JUMP24:
case R_ARM_PLT32:
*(ul32 *)loc = (*(ul32 *)loc & 0xff00'0000) | bits(val, 25, 2);
*(U32<E> *)loc = (*(U32<E> *)loc & 0xff00'0000) | bits(val, 25, 2);
break;
case R_ARM_MOVW_PREL_NC:
case R_ARM_MOVW_ABS_NC:
@@ -209,7 +212,7 @@ void write_addend(u8 *loc, i64 val, const ElfRel<E> &rel) {
write_arm_mov(loc, val);
break;
case R_ARM_PREL31:
*(ul32 *)loc = (*(ul32 *)loc & 0x8000'0000) | (val & 0x7fff'ffff);
*(U32<E> *)loc = (*(U32<E> *)loc & 0x8000'0000) | (val & 0x7fff'ffff);
break;
case R_ARM_THM_MOVW_PREL_NC:
case R_ARM_THM_MOVW_ABS_NC:
@@ -236,7 +239,7 @@ void write_plt_header(Context<E> &ctx, u8 *buf) {
};
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 16) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 16;
*(U32<E> *)(buf + 16) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 16;
}
static constexpr ul32 plt_entry[] = {
@@ -249,13 +252,13 @@ static constexpr ul32 plt_entry[] = {
template <>
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
memcpy(buf, plt_entry, sizeof(plt_entry));
*(ul32 *)(buf + 12) = sym.get_gotplt_addr(ctx) - sym.get_plt_addr(ctx) - 12;
*(U32<E> *)(buf + 12) = sym.get_gotplt_addr(ctx) - sym.get_plt_addr(ctx) - 12;
}
template <>
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
memcpy(buf, plt_entry, sizeof(plt_entry));
*(ul32 *)(buf + 12) = sym.get_got_pltgot_addr(ctx) - sym.get_plt_addr(ctx) - 12;
*(U32<E> *)(buf + 12) = sym.get_got_pltgot_addr(ctx) - sym.get_plt_addr(ctx) - 12;
}
template <>
@@ -267,24 +270,16 @@ void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
case R_NONE:
break;
case R_ARM_ABS32:
*(ul32 *)loc = val;
*(U32<E> *)loc = val;
break;
case R_ARM_REL32:
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
*(U32<E> *)loc = val - this->shdr.sh_addr - offset;
break;
default:
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
}
static Thunk<E> &get_reachable_thunk(OutputSection<E> &osec, u64 addr) {
auto it = std::upper_bound(osec.thunks.begin(), osec.thunks.end(), addr,
[](u64 addr, std::unique_ptr<Thunk<E>> &thunk) {
return addr < thunk->get_addr();
});
return **it;
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
@@ -312,7 +307,11 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
auto get_arm_thunk_addr = [&] { return sym.get_thunk_addr(ctx, P) + 4; };
auto get_tlsdesc_trampoline_addr = [&] {
return get_reachable_thunk(*output_section, P).get_addr();
auto it = ranges::upper_bound(output_section->thunks, P, {},
[](std::unique_ptr<Thunk<E>> &thunk) {
return thunk->get_addr();
});
return (*it)->get_addr();
};
switch (rel.r_type) {
@@ -320,13 +319,13 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
case R_ARM_TARGET1:
break;
case R_ARM_REL32:
*(ul32 *)loc = S + A - P;
*(U32<E> *)loc = S + A - P;
break;
case R_ARM_THM_CALL: {
if (sym.is_remaining_undef_weak()) {
// On ARM, calling an weak undefined symbol jumps to the
// next instruction.
*(ul32 *)loc = 0x8000'f3af; // NOP.W
*(U32<E> *)loc = 0x8000'f3af; // NOP.W
break;
}
@@ -337,61 +336,61 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
i64 val2 = align_to(S + A - P, 4);
if (T && is_int(val1, 25)) {
*(ul16 *)(loc + 2) |= 0x1000; // BL
*(U16<E> *)(loc + 2) |= 0x1000; // BL
write_thm_b25(loc, val1);
} else if (!T && is_int(val2, 25)) {
*(ul16 *)(loc + 2) &= ~0x1000; // BLX
*(U16<E> *)(loc + 2) &= ~0x1000; // BLX
write_thm_b25(loc, val2);
} else {
*(ul16 *)(loc + 2) |= 0x1000; // BL
*(U16<E> *)(loc + 2) |= 0x1000; // BL
write_thm_b25(loc, get_thumb_thunk_addr() + A - P);
}
break;
}
case R_ARM_BASE_PREL:
*(ul32 *)loc = GOT + A - P;
*(U32<E> *)loc = GOT + A - P;
break;
case R_ARM_GOTOFF32:
*(ul32 *)loc = ((S + A) | T) - GOT;
*(U32<E> *)loc = ((S + A) | T) - GOT;
break;
case R_ARM_GOT_PREL:
case R_ARM_TARGET2:
*(ul32 *)loc = GOT + G + A - P;
*(U32<E> *)loc = GOT + G + A - P;
break;
case R_ARM_GOT_BREL:
*(ul32 *)loc = G + A;
*(U32<E> *)loc = G + A;
break;
case R_ARM_CALL: {
if (sym.is_remaining_undef_weak()) {
*(ul32 *)loc = 0xe320'f000; // NOP
*(U32<E> *)loc = 0xe320'f000; // NOP
break;
}
// Just like THM_CALL, ARM_CALL relocation refers to either BL or
// BLX instruction. We may need to rewrite BL → BLX or BLX → BL.
bool is_bl = ((*(ul32 *)loc & 0xff00'0000) == 0xeb00'0000);
bool is_blx = ((*(ul32 *)loc & 0xfe00'0000) == 0xfa00'0000);
bool is_bl = ((*(U32<E> *)loc & 0xff00'0000) == 0xeb00'0000);
bool is_blx = ((*(U32<E> *)loc & 0xfe00'0000) == 0xfa00'0000);
if (!is_bl && !is_blx)
Fatal(ctx) << *this << ": R_ARM_CALL refers to neither BL nor BLX";
i64 val = S + A - P;
if (is_int(val, 26)) {
if (T) {
*(ul32 *)loc = 0xfa00'0000; // BLX
*(ul32 *)loc |= (bit(val, 1) << 24) | bits(val, 25, 2);
*(U32<E> *)loc = 0xfa00'0000; // BLX
*(U32<E> *)loc |= (bit(val, 1) << 24) | bits(val, 25, 2);
} else {
*(ul32 *)loc = 0xeb00'0000; // BL
*(ul32 *)loc |= bits(val, 25, 2);
*(U32<E> *)loc = 0xeb00'0000; // BL
*(U32<E> *)loc |= bits(val, 25, 2);
}
} else {
*(ul32 *)loc = 0xeb00'0000; // BL
*(ul32 *)loc |= bits(get_arm_thunk_addr() + A - P, 25, 2);
*(U32<E> *)loc = 0xeb00'0000; // BL
*(U32<E> *)loc |= bits(get_arm_thunk_addr() + A - P, 25, 2);
}
break;
}
case R_ARM_JUMP24: {
if (sym.is_remaining_undef_weak()) {
*(ul32 *)loc = 0xe320'f000; // NOP
*(U32<E> *)loc = 0xe320'f000; // NOP
break;
}
@@ -404,26 +403,26 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
i64 val = S + A - P;
if (T || !is_int(val, 26))
val = get_arm_thunk_addr() + A - P;
*(ul32 *)loc = (*(ul32 *)loc & 0xff00'0000) | bits(val, 25, 2);
*(U32<E> *)loc = (*(U32<E> *)loc & 0xff00'0000) | bits(val, 25, 2);
break;
}
case R_ARM_PLT32:
if (sym.is_remaining_undef_weak()) {
*(ul32 *)loc = 0xe320'f000; // NOP
*(U32<E> *)loc = 0xe320'f000; // NOP
} else {
u64 val = (T ? get_arm_thunk_addr() : S) + A - P;
*(ul32 *)loc = (*(ul32 *)loc & 0xff00'0000) | bits(val, 25, 2);
*(U32<E> *)loc = (*(U32<E> *)loc & 0xff00'0000) | bits(val, 25, 2);
}
break;
case R_ARM_THM_JUMP8:
check(S + A - P, -(1 << 8), 1 << 8);
*(ul16 *)loc &= 0xff00;
*(ul16 *)loc |= bits(S + A - P, 8, 1);
*(U16<E> *)loc &= 0xff00;
*(U16<E> *)loc |= bits(S + A - P, 8, 1);
break;
case R_ARM_THM_JUMP11:
check(S + A - P, -(1 << 11), 1 << 11);
*(ul16 *)loc &= 0xf800;
*(ul16 *)loc |= bits(S + A - P, 11, 1);
*(U16<E> *)loc &= 0xf800;
*(U16<E> *)loc |= bits(S + A - P, 11, 1);
break;
case R_ARM_THM_JUMP19:
check(S + A - P, -(1 << 20), 1 << 20);
@@ -431,7 +430,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
break;
case R_ARM_THM_JUMP24: {
if (sym.is_remaining_undef_weak()) {
*(ul32 *)loc = 0x8000'f3af; // NOP
*(U32<E> *)loc = 0x8000'f3af; // NOP
break;
}
@@ -454,8 +453,8 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
break;
case R_ARM_PREL31:
check(S + A - P, -(1LL << 30), 1LL << 30);
*(ul32 *)loc &= 0x8000'0000;
*(ul32 *)loc |= (S + A - P) & 0x7fff'ffff;
*(U32<E> *)loc &= 0x8000'0000;
*(U32<E> *)loc |= (S + A - P) & 0x7fff'ffff;
break;
case R_ARM_THM_MOVW_ABS_NC:
write_thm_mov(loc, (S + A) | T);
@@ -473,19 +472,19 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
write_thm_mov(loc, (S + A) >> 16);
break;
case R_ARM_TLS_GD32:
*(ul32 *)loc = sym.get_tlsgd_addr(ctx) + A - P;
*(U32<E> *)loc = sym.get_tlsgd_addr(ctx) + A - P;
break;
case R_ARM_TLS_LDM32:
*(ul32 *)loc = ctx.got->get_tlsld_addr(ctx) + A - P;
*(U32<E> *)loc = ctx.got->get_tlsld_addr(ctx) + A - P;
break;
case R_ARM_TLS_LDO32:
*(ul32 *)loc = S + A - ctx.dtp_addr;
*(U32<E> *)loc = S + A - ctx.dtp_addr;
break;
case R_ARM_TLS_IE32:
*(ul32 *)loc = sym.get_gottp_addr(ctx) + A - P;
*(U32<E> *)loc = sym.get_gottp_addr(ctx) + A - P;
break;
case R_ARM_TLS_LE32:
*(ul32 *)loc = S + A - ctx.tp_addr;
*(U32<E> *)loc = S + A - ctx.tp_addr;
break;
case R_ARM_TLS_GOTDESC:
// ARM32 TLSDESC uses the following code sequence to materialize
@@ -514,35 +513,35 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
// .L2: .word foo(gottpoff) + . - .L1
if (sym.has_tlsdesc(ctx)) {
// A is odd if the corresponding TLS_CALL is Thumb.
*(ul32 *)loc = sym.get_tlsdesc_addr(ctx) - P + A - ((A & 1) ? 6 : 4);
*(U32<E> *)loc = sym.get_tlsdesc_addr(ctx) - P + A - ((A & 1) ? 6 : 4);
} else if (sym.has_gottp(ctx)) {
*(ul32 *)loc = sym.get_gottp_addr(ctx) - P + A - ((A & 1) ? 5 : 8);
*(U32<E> *)loc = sym.get_gottp_addr(ctx) - P + A - ((A & 1) ? 5 : 8);
} else {
*(ul32 *)loc = S - ctx.tp_addr;
*(U32<E> *)loc = S - ctx.tp_addr;
}
break;
case R_ARM_TLS_CALL:
if (sym.has_tlsdesc(ctx)) {
*(ul32 *)loc = 0xeb00'0000; // bl 0
*(ul32 *)loc |= bits(get_tlsdesc_trampoline_addr() - P - 8, 25, 2);
*(U32<E> *)loc = 0xeb00'0000; // bl 0
*(U32<E> *)loc |= bits(get_tlsdesc_trampoline_addr() - P - 8, 25, 2);
} else if (sym.has_gottp(ctx)) {
*(ul32 *)loc = 0xe79f'0000; // ldr r0, [pc, r0]
*(U32<E> *)loc = 0xe79f'0000; // ldr r0, [pc, r0]
} else {
*(ul32 *)loc = 0xe320'f000; // nop
*(U32<E> *)loc = 0xe320'f000; // nop
}
break;
case R_ARM_THM_TLS_CALL:
if (sym.has_tlsdesc(ctx)) {
u64 val = align_to(get_tlsdesc_trampoline_addr() - P - 4, 4);
write_thm_b25(loc, val);
*(ul16 *)(loc + 2) &= ~0x1000; // rewrite BL with BLX
*(U16<E> *)(loc + 2) &= ~0x1000; // rewrite BL with BLX
} else if (sym.has_gottp(ctx)) {
// Since `ldr r0, [pc, r0]` is not representable in Thumb,
// we use two instructions instead.
*(ul16 *)loc = 0x4478; // add r0, pc
*(ul16 *)(loc + 2) = 0x6800; // ldr r0, [r0]
*(U16<E> *)loc = 0x4478; // add r0, pc
*(U16<E> *)(loc + 2) = 0x6800; // ldr r0, [r0]
} else {
*(ul32 *)loc = 0x8000'f3af; // nop.w
*(U32<E> *)loc = 0x8000'f3af; // nop.w
}
break;
default:
@@ -573,15 +572,15 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
switch (rel.r_type) {
case R_ARM_ABS32:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ul32 *)loc = *val;
*(U32<E> *)loc = *val;
else
*(ul32 *)loc = S + A;
*(U32<E> *)loc = S + A;
break;
case R_ARM_TLS_LDO32:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ul32 *)loc = *val;
*(U32<E> *)loc = *val;
else
*(ul32 *)loc = S + A - ctx.dtp_addr;
*(U32<E> *)loc = S + A - ctx.dtp_addr;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
@@ -703,7 +702,7 @@ void Thunk<E>::copy_buf(Context<E> &ctx) {
for (Symbol<E> *sym : symbols) {
memcpy(buf, entry, sizeof(entry));
*(ul32 *)(buf + 12) = sym->get_addr(ctx) - P - 16;
*(U32<E> *)(buf + 12) = sym->get_addr(ctx) - P - 16;
buf += sizeof(entry);
P += sizeof(entry);
@@ -712,10 +711,14 @@ void Thunk<E>::copy_buf(Context<E> &ctx) {
template <>
u64 get_eflags(Context<E> &ctx) {
return EF_ARM_EABI_VER5;
if constexpr (E::is_le)
return EF_ARM_EABI_VER5;
else
return EF_ARM_EABI_VER5 | EF_ARM_BE8;
}
void create_arm_exidx_section(Context<E> &ctx) {
template <>
void create_arm_exidx_section<E>(Context<E> &ctx) {
for (i64 i = 0; i < ctx.chunks.size(); i++) {
OutputSection<E> *osec = ctx.chunks[i]->to_osec();
@@ -732,28 +735,20 @@ void create_arm_exidx_section(Context<E> &ctx) {
}
}
void Arm32ExidxSection::compute_section_size(Context<E> &ctx) {
template <>
void Arm32ExidxSection<E>::compute_section_size(Context<E> &ctx) {
output_section.compute_section_size(ctx);
this->shdr.sh_size = output_section.shdr.sh_size + 8; // +8 for sentinel
}
void Arm32ExidxSection::update_shdr(Context<E> &ctx) {
template <>
void Arm32ExidxSection<E>::update_shdr(Context<E> &ctx) {
// .ARM.exidx's sh_link should be set to the .text section index.
// Runtime doesn't care about it, but the binutils's strip command does.
if (Chunk<E> *chunk = find_chunk(ctx, ".text"))
this->shdr.sh_link = chunk->shndx;
}
void Arm32ExidxSection::remove_duplicate_entries(Context<E> &ctx) {
this->shdr.sh_size = get_contents(ctx).size();
}
void Arm32ExidxSection::copy_buf(Context<E> &ctx) {
std::vector<u8> contents = get_contents(ctx);
assert(this->shdr.sh_size == contents.size());
write_vector(ctx.buf + this->shdr.sh_offset, contents);
}
// Returns the end of the text segment
static u64 get_text_end(Context<E> &ctx) {
u64 ret = 0;
@@ -772,7 +767,8 @@ static u64 get_text_end(Context<E> &ctx) {
// likely that it's due to some historical reason.
//
// This function returns contents of .ARM.exidx.
std::vector<u8> Arm32ExidxSection::get_contents(Context<E> &ctx) {
template <>
std::vector<u8> Arm32ExidxSection<E>::get_contents(Context<E> &ctx) {
// .ARM.exidx records consists of a signed 31-bit relative address
// and a 32-bit value. The relative address indicates the start
// address of a function that the record covers. The value is one of
@@ -788,8 +784,8 @@ std::vector<u8> Arm32ExidxSection::get_contents(Context<E> &ctx) {
const u32 CANTUNWIND = 1;
struct Entry {
ul32 addr;
ul32 val;
U32<E> addr;
U32<E> val;
};
// We reserve one extra slot for the sentinel
@@ -820,19 +816,13 @@ std::vector<u8> Arm32ExidxSection::get_contents(Context<E> &ctx) {
ent[i].val = 0x7fff'ffff & (ent[i].val + offset);
});
std::sort(ent, ent + num_entries, [](const Entry &a, const Entry &b) {
return a.addr < b.addr;
});
ranges::sort(ent, ent + num_entries, {}, &Entry::addr);
// Remove duplicate adjacent entries. That is, if two adjacent functions
// have the same compact unwind info or are both CANTUNWIND, we can
// merge them into a single address range.
auto it = std::unique(ent, ent + num_entries,
[](const Entry &a, const Entry &b) {
return a.val == b.val;
});
num_entries = it - ent;
auto tail = ranges::unique(ent, ent + num_entries, {}, &Entry::val);
num_entries -= tail.size();
buf.resize(num_entries * sizeof(Entry));
// Make addresses relative to themselves.
@@ -846,4 +836,84 @@ std::vector<u8> Arm32ExidxSection::get_contents(Context<E> &ctx) {
return buf;
}
template <>
void Arm32ExidxSection<E>::remove_duplicate_entries(Context<E> &ctx) {
this->shdr.sh_size = get_contents(ctx).size();
}
template <>
void Arm32ExidxSection<E>::copy_buf(Context<E> &ctx) {
std::vector<u8> contents = get_contents(ctx);
assert(this->shdr.sh_size == contents.size());
write_vector(ctx.buf + this->shdr.sh_offset, contents);
}
#if MOLD_ARM32BE
// Even though using ARM32 in big-endian mode is very rare, the processor
// technically supports both little- and big-endian modes. There are two
// variants of big-endian mode: BE32 and BE8. In BE32, instructions and
// data are encoded in big-endian. In BE8, instructions are encoded in
// little-endian, and only data is in big-endian. BE8 is the de facto
// standard for ARMv6 or later. We support only BE8.
//
// A tricky thing is that instructions in an object file are always
// big-endian if the file is compiled for big-endian mode. In other words,
// the compiler always emit code in BE32 if -mbig-endian is specified. It
// is the linker's responsibility to rewrite instructions from big-endian
// to little-endian for an BE8 output. This function does that.
//
// The text section may contain a mix of 32-bit ARM instructions, 16-bit
// Thumb instructions, and data. We need to distinguish them to swap 4
// bytes, 2 bytes, or not swap bytes, respectively. The beginning of ARM
// code, Thumb code, and data is labeled with a mapping symbol of $a, $t,
// and $d, respectively. We use mapping symbols to determine what to do
// with the text section.
//
// This function is called after we copy the input section contents to the
// output file. We rewrite instructions in the output buffer in place.
void arm32be_swap_bytes(Context<E> &ctx) {
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
// Collect mapping symbols
std::vector<Symbol<E> *> syms;
for (Symbol<E> *sym : file->get_local_syms())
if (InputSection<E> *isec = sym->get_input_section())
if (isec->is_alive && (isec->shdr().sh_flags & SHF_EXECINSTR))
if (std::string_view x = sym->name();
x == "$a" || x.starts_with("$a.") ||
x == "$t" || x.starts_with("$t.") ||
x == "$d" || x.starts_with("$d."))
syms.push_back(sym);
// Group mapping symbols by input section and sort by address
ranges::stable_sort(syms, {}, [](const Symbol<E> *sym) {
return std::tuple{(uintptr_t)sym->get_input_section(), sym->value};
});
// Swap bytes
for (i64 i = 0; i < syms.size(); i++) {
Symbol<E> &sym = *syms[i];
if (sym.name().starts_with("$d"))
continue;
InputSection<E> &isec = *sym.get_input_section();
u8 *base = ctx.buf + isec.output_section->shdr.sh_offset + isec.offset;
u8 *start = base + sym.value;
u8 *end;
if (i + 1 < syms.size() && syms[i + 1]->get_input_section() == &isec)
end = base + syms[i + 1]->value;
else
end = base + isec.sh_size;
i64 sz = sym.name().starts_with("$a") ? 4 : 2;
for (u8 *p = start; p < end; p += sz)
std::reverse(p, p + sz);
}
});
}
#endif
} // namespace mold
#endif
+1 -1
View File
@@ -66,7 +66,7 @@ static u64 higher(u64 val, u64 pc) {
// LU52I.D simply set bits to [51:31] and to [63:53], respectively.
//
// Compensating all the sign-extensions is a bit complicated. The
// psABI gave the following formula.
// psABI gives the following formula.
val = val + 0x8000'0000 + ((val & 0x800) ? (0x1000 - 0x1'0000'0000) : 0);
return page(val) - page(pc - 8);
}
+5 -12
View File
@@ -484,17 +484,12 @@ static InputSection<E> *get_opd_section(ObjectFile<E> &file) {
}
static ElfRel<E> *
get_relocation_at(Context<E> &ctx, InputSection<E> &isec, i64 offset) {
get_relocation_at(Context<E> &ctx, InputSection<E> &isec, u64 offset) {
std::span<ElfRel<E>> rels = isec.get_rels(ctx);
auto it = std::lower_bound(rels.begin(), rels.end(), offset,
[](const ElfRel<E> &r, i64 offset) {
return r.r_offset < offset;
auto it = ranges::lower_bound(rels, offset, {}, [](const ElfRel<E> &r) {
return (u64)r.r_offset;
});
if (it == rels.end())
return nullptr;
if (it->r_offset != offset)
if (it == rels.end() || it->r_offset != offset)
return nullptr;
return &*it;
}
@@ -509,9 +504,7 @@ struct OpdSymbol {
static Symbol<E> *
get_opd_sym_at(std::span<OpdSymbol> syms, u64 offset) {
auto it = ranges::lower_bound(syms, offset, {}, &OpdSymbol::r_offset);
if (it == syms.end())
return nullptr;
if (it->r_offset != offset)
if (it == syms.end() || it->r_offset != offset)
return nullptr;
return it->sym;
}
+3 -9
View File
@@ -303,8 +303,8 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
switch (rel.r_type) {
case R_RISCV_32:
assert(E::is_64);
*(U32<E> *)loc = S + A;
if (E::is_64)
*(U32<E> *)loc = S + A;
break;
case R_RISCV_64:
break;
@@ -321,12 +321,6 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
i64 val = S + A - P;
i64 rd = get_rd(buf + rel.r_offset + 4);
// Calling an undefined weak symbol does not make sense.
// We make such call into an infinite loop. This should
// help debugging of a faulty program.
if (sym.esym().is_undef_weak())
val = 0;
if (removed_bytes == 4) {
// auipc + jalr -> jal
*(ul32 *)loc = (rd << 7) | 0b1101111;
@@ -473,7 +467,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
}
case R_RISCV_TLSDESC_HI20:
// RISC-V TLSDESC uses the following code sequence to materialize
// a TP-relative address in x0.
// a TP-relative address in a0.
//
// .L0:
// auipc tX, 0
+25 -12
View File
@@ -587,6 +587,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
bool warn_shared_textrel = false;
bool error_unresolved_symbols = true;
std::optional<SeparateCodeKind> z_separate_code;
std::optional<bool> allow_shlib_undefined;
std::optional<bool> report_undefined;
std::optional<bool> z_relro;
std::optional<bool> z_dynamic_undefined_weak;
@@ -705,21 +706,20 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
} else if (read_flag("no-dynamic-linker")) {
ctx.arg.dynamic_linker = "";
} else if (read_flag("v")) {
Out(ctx) << get_mold_version();
Out(ctx) << mold_version;
version_shown = true;
} else if (read_flag("version")) {
Out(ctx) << get_mold_version();
Out(ctx) << mold_version;
exit(0);
} else if (read_flag("V")) {
Out(ctx) << get_mold_version()
Out(ctx) << mold_version
<< "\n Supported emulations:\n elf_x86_64\n elf_i386\n"
<< " aarch64elf\n aarch64linux\n aarch64elfb\n"
<< " aarch64linuxb\n armelf_linux_eabi\n elf64lriscv\n"
<< " elf64briscv\n elf32lriscv\n elf32briscv\n"
<< " elf32ppc\n elf64ppc\n elf64lppc\n elf64_s390\n"
<< " elf64_sparc\n m68kelf\n shlelf_linux\n"
<< " shelf_linux\n elf64loongarch\n elf32loongarch\n"
<< " arclinux";
<< " shelf_linux\n elf64loongarch\n elf32loongarch";
version_shown = true;
} else if (read_arg("mllvm")) {
ctx.arg.plugin_opt.emplace_back(arg);
@@ -733,7 +733,9 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
} else if (arg == "aarch64elfb" || arg == "aarch64linuxb") {
ctx.arg.emulation = ARM64BE::name;
} else if (arg == "armelf_linux_eabi") {
ctx.arg.emulation = ARM32::name;
ctx.arg.emulation = ARM32LE::name;
} else if (arg == "armelfb_linux_eabi") {
ctx.arg.emulation = ARM32BE::name;
} else if (arg == "elf64lriscv") {
ctx.arg.emulation = RV64LE::name;
} else if (arg == "elf64briscv") {
@@ -762,8 +764,6 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
ctx.arg.emulation = LOONGARCH64::name;
} else if (arg == "elf32loongarch") {
ctx.arg.emulation = LOONGARCH32::name;
} else if (arg == "arclinux") {
ctx.arg.emulation = ARC::name;
} else {
Fatal(ctx) << "unknown -m argument: " << arg;
}
@@ -1294,6 +1294,10 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
}
} else if (read_flag("no-build-id")) {
ctx.arg.build_id.kind = BuildId::NONE;
} else if (read_flag("be8")) {
ctx.arg.be8 = true;
} else if (read_flag("be32")) {
ctx.arg.be8 = false;
} else if (read_arg("format") || read_arg("b")) {
if (arg == "binary")
Fatal(ctx)
@@ -1306,9 +1310,9 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
} else if (read_arg("filter") || read_arg("F")) {
ctx.arg.filter.push_back(arg);
} else if (read_flag("allow-shlib-undefined")) {
ctx.arg.allow_shlib_undefined = true;
allow_shlib_undefined = true;
} else if (read_flag("no-allow-shlib-undefined")) {
ctx.arg.allow_shlib_undefined = false;
allow_shlib_undefined = false;
} else if (read_arg("O")) {
} else if (read_flag("EB")) {
} else if (read_flag("EL")) {
@@ -1415,6 +1419,11 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
if (ctx.arg.pic)
ctx.arg.image_base = 0;
if (allow_shlib_undefined)
ctx.arg.allow_shlib_undefined = *allow_shlib_undefined;
else
ctx.arg.allow_shlib_undefined = ctx.arg.shared;
if (!report_undefined)
report_undefined = !ctx.arg.shared;
@@ -1435,6 +1444,9 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
if (ctx.arg.relocatable)
ctx.arg.static_ = true;
if (ctx.arg.static_)
ctx.arg.dynamic_linker = "";
if (ctx.arg.shuffle_sections == SHUFFLE_SECTIONS_SHUFFLE) {
if (shuffle_sections_seed)
ctx.arg.shuffle_sections_seed = *shuffle_sections_seed;
@@ -1488,6 +1500,9 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
if (ctx.arg.image_base % ctx.page_size)
Fatal(ctx) << "-image-base must be a multiple of -max-page-size";
if (ctx.arg.emulation == ARM32BE::name && !ctx.arg.be8)
Fatal(ctx) << "--be32 is not supported";
if (ctx.arg.thread_count == 0)
ctx.arg.thread_count = get_default_thread_count();
@@ -1498,9 +1513,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
std::string ver = ctx.arg.soname;
if (ver.empty())
ver = path_filename(ctx.arg.output);
ctx.arg.version_definitions.push_back(ver);
ctx.default_version = VER_NDX_LAST_RESERVED + 1;
}
for (std::string_view path : version_scripts) {
-12
View File
@@ -1,12 +0,0 @@
#include "mold.h"
namespace mold {
std::string get_mold_version() {
if (mold_git_hash.empty())
return "mold "s + MOLD_VERSION + " (compatible with GNU ld)";
return "mold "s + MOLD_VERSION + " (" + mold_git_hash +
"; compatible with GNU ld)";
}
} // namespace mold
+10 -73
View File
@@ -1,11 +1,12 @@
#include "elf.h"
#include <sstream>
namespace mold {
static std::string unknown_type(u32 r_type) {
char buf[50];
snprintf(buf, sizeof(buf), "unknown (0x%x)", r_type);
return buf;
std::stringstream ss;
ss << "unknown (0x" << std::hex << r_type << ")";
return ss.str();
}
#define CASE(x) case x: return #x
@@ -232,7 +233,7 @@ std::string rel_to_string<ARM64BE>(u32 r_type) {
}
template <>
std::string rel_to_string<ARM32>(u32 r_type) {
std::string rel_to_string<ARM32LE>(u32 r_type) {
switch (r_type) {
CASE(R_ARM_NONE);
CASE(R_ARM_PC24);
@@ -373,6 +374,11 @@ std::string rel_to_string<ARM32>(u32 r_type) {
return unknown_type(r_type);
}
template <>
std::string rel_to_string<ARM32BE>(u32 r_type) {
return rel_to_string<ARM32LE>(r_type);
}
template <>
std::string rel_to_string<RV64LE>(u32 r_type) {
switch (r_type) {
@@ -1038,73 +1044,4 @@ std::string rel_to_string<LOONGARCH32>(u32 r_type) {
return rel_to_string<LOONGARCH64>(r_type);
}
template <>
std::string rel_to_string<ARC>(u32 r_type) {
switch (r_type) {
CASE(R_ARC_8);
CASE(R_ARC_16);
CASE(R_ARC_24);
CASE(R_ARC_32);
CASE(R_ARC_N8);
CASE(R_ARC_N16);
CASE(R_ARC_N24);
CASE(R_ARC_N32);
CASE(R_ARC_SDA);
CASE(R_ARC_SECTOFF);
CASE(R_ARC_S21H_PCREL);
CASE(R_ARC_S21W_PCREL);
CASE(R_ARC_S25H_PCREL);
CASE(R_ARC_S25W_PCREL);
CASE(R_ARC_SDA32);
CASE(R_ARC_SDA_LDST);
CASE(R_ARC_SDA_LDST1);
CASE(R_ARC_SDA_LDST2);
CASE(R_ARC_SDA16_LD);
CASE(R_ARC_SDA16_LD1);
CASE(R_ARC_SDA16_LD2);
CASE(R_ARC_S13_PCREL);
CASE(R_ARC_W);
CASE(R_ARC_32_ME);
CASE(R_ARC_32_ME_S);
CASE(R_ARC_N32_ME);
CASE(R_ARC_SECTOFF_ME);
CASE(R_ARC_SDA32_ME);
CASE(R_ARC_W_ME);
CASE(R_ARC_SECTOFF_ME_1);
CASE(R_ARC_SECTOFF_ME_2);
CASE(R_ARC_SECTOFF_1);
CASE(R_ARC_SECTOFF_2);
CASE(R_ARC_SDA_12);
CASE(R_ARC_SDA16_ST2);
CASE(R_ARC_32_PCREL);
CASE(R_ARC_PC32);
CASE(R_ARC_GOT32);
CASE(R_ARC_GOTPC32);
CASE(R_ARC_PLT32);
CASE(R_ARC_COPY);
CASE(R_ARC_GLOB_DAT);
CASE(R_ARC_JMP_SLOT);
CASE(R_ARC_RELATIVE);
CASE(R_ARC_GOTOFF);
CASE(R_ARC_GOTPC);
CASE(R_ARC_S21W_PCREL_PLT);
CASE(R_ARC_S25H_PCREL_PLT);
CASE(R_ARC_JLI_SECTOFF);
CASE(R_ARC_TLS_DTPMOD);
CASE(R_ARC_TLS_TPOFF);
CASE(R_ARC_TLS_GD_GOT);
CASE(R_ARC_TLS_GD_LD);
CASE(R_ARC_TLS_GD_CALL);
CASE(R_ARC_TLS_IE_GOT);
CASE(R_ARC_TLS_DTPOFF);
CASE(R_ARC_TLS_DTPOFF_S9);
CASE(R_ARC_TLS_LE_S9);
CASE(R_ARC_TLS_LE_32);
CASE(R_ARC_S25W_PCREL_PLT);
CASE(R_ARC_S21H_PCREL_PLT);
CASE(R_ARC_NPS_CMEM16);
}
return unknown_type(r_type);
}
} // namespace mold
+15 -98
View File
@@ -13,7 +13,8 @@ struct X86_64;
struct I386;
struct ARM64LE;
struct ARM64BE;
struct ARM32;
struct ARM32LE;
struct ARM32BE;
struct RV64LE;
struct RV64BE;
struct RV32LE;
@@ -28,7 +29,6 @@ struct SH4LE;
struct SH4BE;
struct LOONGARCH64;
struct LOONGARCH32;
struct ARC;
template <typename E> struct ElfSym;
template <typename E> struct ElfShdr;
@@ -89,7 +89,6 @@ enum : u32 {
SHT_HIOS = 0x6fffffff,
SHT_X86_64_UNWIND = 0x70000001,
SHT_ARM_EXIDX = 0x70000001,
SHT_ARC_ATTRIBUTES = 0x70000001,
SHT_ARM_ATTRIBUTES = 0x70000003,
SHT_RISCV_ATTRIBUTES = 0x70000003,
SHT_LOUSER = 0x80000000,
@@ -237,7 +236,6 @@ enum : u32 {
EM_SPARC64 = 43,
EM_X86_64 = 62,
EM_AARCH64 = 183,
EM_ARC_COMPACT2 = 195,
EM_RISCV = 243,
EM_LOONGARCH = 258,
};
@@ -360,9 +358,10 @@ enum : u32 {
};
enum : u32 {
EF_ARM_ABI_FLOAT_SOFT = 0x00000200,
EF_ARM_ABI_FLOAT_HARD = 0x00000400,
EF_ARM_EABI_VER5 = 0x05000000,
EF_ARM_ABI_FLOAT_SOFT = 0x200,
EF_ARM_ABI_FLOAT_HARD = 0x400,
EF_ARM_BE8 = 0x800000,
EF_ARM_EABI_VER5 = 0x5000000,
};
enum : u32 {
@@ -1365,71 +1364,6 @@ enum : u32 {
R_LARCH_TLS_DESC_PCREL20_S2 = 126,
};
enum : u32 {
R_ARC_8 = 1,
R_ARC_16 = 2,
R_ARC_24 = 3,
R_ARC_32 = 4,
R_ARC_N8 = 8,
R_ARC_N16 = 9,
R_ARC_N24 = 10,
R_ARC_N32 = 11,
R_ARC_SDA = 12,
R_ARC_SECTOFF = 13,
R_ARC_S21H_PCREL = 14,
R_ARC_S21W_PCREL = 15,
R_ARC_S25H_PCREL = 16,
R_ARC_S25W_PCREL = 17,
R_ARC_SDA32 = 18,
R_ARC_SDA_LDST = 19,
R_ARC_SDA_LDST1 = 20,
R_ARC_SDA_LDST2 = 21,
R_ARC_SDA16_LD = 22,
R_ARC_SDA16_LD1 = 23,
R_ARC_SDA16_LD2 = 24,
R_ARC_S13_PCREL = 25,
R_ARC_W = 26,
R_ARC_32_ME = 27,
R_ARC_32_ME_S = 105,
R_ARC_N32_ME = 28,
R_ARC_SECTOFF_ME = 29,
R_ARC_SDA32_ME = 30,
R_ARC_W_ME = 31,
R_ARC_SECTOFF_ME_1 = 41,
R_ARC_SECTOFF_ME_2 = 42,
R_ARC_SECTOFF_1 = 43,
R_ARC_SECTOFF_2 = 44,
R_ARC_SDA_12 = 45,
R_ARC_SDA16_ST2 = 48,
R_ARC_32_PCREL = 49,
R_ARC_PC32 = 50,
R_ARC_GOT32 = 59,
R_ARC_GOTPC32 = 51,
R_ARC_PLT32 = 52,
R_ARC_COPY = 53,
R_ARC_GLOB_DAT = 54,
R_ARC_JMP_SLOT = 55,
R_ARC_RELATIVE = 56,
R_ARC_GOTOFF = 57,
R_ARC_GOTPC = 58,
R_ARC_S21W_PCREL_PLT = 60,
R_ARC_S25H_PCREL_PLT = 61,
R_ARC_JLI_SECTOFF = 63,
R_ARC_TLS_DTPMOD = 66,
R_ARC_TLS_TPOFF = 68,
R_ARC_TLS_GD_GOT = 69,
R_ARC_TLS_GD_LD = 70,
R_ARC_TLS_GD_CALL = 71,
R_ARC_TLS_IE_GOT = 72,
R_ARC_TLS_DTPOFF = 67,
R_ARC_TLS_DTPOFF_S9 = 73,
R_ARC_TLS_LE_S9 = 74,
R_ARC_TLS_LE_32 = 75,
R_ARC_S25W_PCREL_PLT = 76,
R_ARC_S21H_PCREL_PLT = 77,
R_ARC_NPS_CMEM16 = 78,
};
// Returns true if a given relocation is of type used for direct
// function call.
template <typename E>
@@ -1878,7 +1812,8 @@ template <typename E> concept is_x86_64 = std::same_as<E, X86_64>;
template <typename E> concept is_i386 = std::same_as<E, I386>;
template <typename E> concept is_arm64le = std::same_as<E, ARM64LE>;
template <typename E> concept is_arm64be = std::same_as<E, ARM64BE>;
template <typename E> concept is_arm32 = std::same_as<E, ARM32>;
template <typename E> concept is_arm32le = std::same_as<E, ARM32LE>;
template <typename E> concept is_arm32be = std::same_as<E, ARM32BE>;
template <typename E> concept is_rv64le = std::same_as<E, RV64LE>;
template <typename E> concept is_rv64be = std::same_as<E, RV64BE>;
template <typename E> concept is_rv32le = std::same_as<E, RV32LE>;
@@ -1893,9 +1828,9 @@ template <typename E> concept is_sh4le = std::same_as<E, SH4LE>;
template <typename E> concept is_sh4be = std::same_as<E, SH4BE>;
template <typename E> concept is_loongarch64 = std::same_as<E, LOONGARCH64>;
template <typename E> concept is_loongarch32 = std::same_as<E, LOONGARCH32>;
template <typename E> concept is_arc = std::same_as<E, ARC>;
template <typename E> concept is_x86 = is_x86_64<E> || is_i386<E>;
template <typename E> concept is_arm32 = is_arm32le<E> || is_arm32be<E>;
template <typename E> concept is_arm64 = is_arm64le<E> || is_arm64be<E>;
template <typename E> concept is_arm = is_arm64<E> || is_arm32<E>;
template <typename E> concept is_rv64 = is_rv64le<E> || is_rv64be<E>;
@@ -1989,7 +1924,7 @@ struct ARM64BE : ARM64LE {
static constexpr bool is_le = false;
};
struct ARM32 {
struct ARM32LE {
static constexpr std::string_view name = "arm32";
static constexpr bool is_64 = false;
static constexpr bool is_le = true;
@@ -2019,6 +1954,11 @@ struct ARM32 {
};
};
struct ARM32BE : ARM32LE {
static constexpr std::string_view name = "arm32be";
static constexpr bool is_le = false;
};
struct RV64LE {
static constexpr std::string_view name = "riscv64";
static constexpr bool is_64 = true;
@@ -2297,27 +2237,4 @@ struct LOONGARCH32 {
static constexpr u32 R_FUNCALL[] = { R_LARCH_B26, R_LARCH_CALL36 };
};
struct ARC {
static constexpr std::string_view name = "arc";
static constexpr bool is_64 = false;
static constexpr bool is_le = true;
static constexpr bool is_rela = true;
static constexpr u32 page_size = 8192;
static constexpr u32 e_machine = EM_ARC_COMPACT2;
static constexpr u32 plt_hdr_size = 24;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 16;
static constexpr u8 filler[] = { 0, 0, 0, 0 };
static constexpr u32 R_COPY = R_ARC_COPY;
static constexpr u32 R_GLOB_DAT = R_ARC_GLOB_DAT;
static constexpr u32 R_JUMP_SLOT = R_ARC_JMP_SLOT;
static constexpr u32 R_ABS = R_ARC_32;
static constexpr u32 R_RELATIVE = R_ARC_RELATIVE;
static constexpr u32 R_DTPOFF = R_ARC_TLS_DTPOFF;
static constexpr u32 R_TPOFF = R_ARC_TLS_TPOFF;
static constexpr u32 R_DTPMOD = R_ARC_TLS_DTPMOD;
static constexpr u32 R_FUNCALL[] = { R_ARC_S25W_PCREL_PLT };
};
} // namespace mold
+14
View File
@@ -0,0 +1,14 @@
#include "mold.h"
#include "mold-git-hash.h"
std::string mold::mold_version =
#ifdef MOLD_GIT_HASH
"mold " MOLD_VERSION " (" MOLD_GIT_HASH "; compatible with GNU ld)";
#else
"mold " MOLD_VERSION " (compatible with GNU ld)";
#endif
int main(int argc, char **argv) {
mold::set_mimalloc_options();
return mold::mold_main<mold::X86_64>(argc, argv);
}
+1 -3
View File
@@ -139,7 +139,7 @@ static std::string_view get_elf_type(u8 *buf) {
case EM_X86_64:
return X86_64::name;
case EM_ARM:
return ARM32::name;
return is_le ? ARM32LE::name : ARM32BE::name;
case EM_AARCH64:
return is_le ? ARM64LE::name : ARM64BE::name;
case EM_RISCV:
@@ -160,8 +160,6 @@ static std::string_view get_elf_type(u8 *buf) {
return is_le ? SH4LE::name : SH4BE::name;
case EM_LOONGARCH:
return is_64 ? LOONGARCH64::name : LOONGARCH32::name;
case EM_ARC_COMPACT2:
return ARC::name;
default:
return "";
}
+58 -20
View File
@@ -317,10 +317,6 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
}
}
if constexpr (is_arc<E>)
if (shdr.sh_type == SHT_ARC_ATTRIBUTES)
continue;
switch (shdr.sh_type) {
case SHT_GROUP: {
// Get the signature of this section group.
@@ -429,6 +425,7 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
continue;
this->sections[i] = std::make_unique<InputSection<E>>(ctx, *this, i);
InputSection<E> *isec = this->sections[i].get();
// Save .llvm_addrsig for --icf=safe.
if (shdr.sh_type == SHT_LLVM_ADDRSIG && !ctx.arg.relocatable) {
@@ -450,19 +447,17 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
this->has_ctors = true;
if (name == ".eh_frame")
eh_frame_sections.push_back(this->sections[i].get());
eh_frame_sections.push_back(isec);
if (name == ".debug_info" && !(shdr.sh_flags & SHF_ALLOC))
debug_info = isec;
if constexpr (is_ppc32<E>)
if (name == ".got2")
extra.got2 = this->sections[i].get();
extra.got2 = isec;
// 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 --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.
@@ -1003,6 +998,7 @@ void ObjectFile<E>::resolve_symbols(Context<E> &ctx) {
}
std::scoped_lock lock(sym.mu);
if (get_rank(this, esym, !this->is_reachable) < get_rank(sym)) {
sym.file = this;
sym.set_input_section(isec);
@@ -1010,6 +1006,7 @@ void ObjectFile<E>::resolve_symbols(Context<E> &ctx) {
sym.sym_idx = i;
sym.ver_idx = ctx.default_version;
sym.is_weak = esym.is_weak();
sym.is_versioned_default = false;
}
}
}
@@ -1277,23 +1274,48 @@ void SharedFile<E>::parse(Context<E> &ctx) {
if (vers.empty() || esyms[i].is_undef())
ver = VER_NDX_GLOBAL;
else
ver = (vers[i] & ~VERSYM_HIDDEN);
ver = vers[i] & ~VERSYM_HIDDEN;
if (ver == VER_NDX_LOCAL)
continue;
std::string_view name = this->symbol_strtab.data() + esyms[i].st_name;
bool is_default = vers.empty() || !(vers[i] & VERSYM_HIDDEN);
this->elf_syms2.push_back(esyms[i]);
this->versyms.push_back(ver);
if (is_default) {
this->symbols.push_back(get_symbol(ctx, name));
} else {
std::string_view mangled_name = save_string(
std::string_view name = this->symbol_strtab.data() + esyms[i].st_name;
auto get_versioned_sym = [&] {
std::string_view key = save_string(
ctx, std::string(name) + "@" + std::string(version_strings[ver]));
this->symbols.push_back(get_symbol(ctx, mangled_name, name));
return get_symbol(ctx, key, name);
};
// Symbol resolution involving symbol versioning is tricky because one
// symbol can be resolved with two different identifiers. Among
// symbols with the same name but different versions, one of them is
// always marked as the "default" one. This symbol is often denoted
// with two atsigns as `foo@@VERSION` and can be referred to either
// as `foo` or `foo@VERSION`. No other symbols have two names like that.
//
// On contrary, a versioned non-default symbol can be referred only
// with an explicit version suffix, e.g., `foo@VERSION`.
//
// Here is how we resolve versioned default symbols. We resolve `foo`
// and `foo@VERSION` as usual, but with information to forward
// references to `foo@VERSION` to `foo`. After name resolution, we
// visit all symbol references to redirect `foo@VERSION` to `foo`.
if (vers.empty() || ver == VER_NDX_GLOBAL) {
// Unversioned symbol
this->symbols.push_back(get_symbol(ctx, name));
this->symbols2.push_back(nullptr);
} else if (vers[i] & VERSYM_HIDDEN) {
// Versioned non-default symbol
this->symbols.push_back(get_versioned_sym());
this->symbols2.push_back(nullptr);
} else {
// Versioned default symbol
this->symbols.push_back(get_symbol(ctx, name));
this->symbols2.push_back(get_versioned_sym());
}
}
@@ -1400,6 +1422,22 @@ void SharedFile<E>::resolve_symbols(Context<E> &ctx) {
sym.sym_idx = i;
sym.ver_idx = versyms[i];
sym.is_weak = true;
sym.is_versioned_default = false;
}
// A symbol with the default version is a special case because, unlike
// other symbols, the symbol can be referred by two names, `foo` and
// `foo@VERSION`. Here, we resolve `foo@VERSOIN` as a proxy of `foo`.
Symbol<E> *sym2 = this->symbols2[i];
if (sym2 && sym2 != &sym) {
std::scoped_lock lock2(sym2->mu);
if (get_rank(this, esym, false) < get_rank(*sym2)) {
sym2->file = this;
sym2->origin = (uintptr_t)&sym;
sym2->sym_idx = i;
sym2->is_versioned_default = true;
}
}
}
}
+38 -24
View File
@@ -63,16 +63,16 @@ void InputSection<E>::uncompress(Context<E> &ctx) {
return;
u8 *buf = new u8[sh_size];
copy_contents(ctx, buf);
copy_contents_to(ctx, buf, sh_size);
contents = std::string_view((char *)buf, sh_size);
ctx.string_pool.emplace_back(buf);
uncompressed = true;
}
template <typename E>
void InputSection<E>::copy_contents(Context<E> &ctx, u8 *buf) {
void InputSection<E>::copy_contents_to(Context<E> &ctx, u8 *buf, i64 sz) {
if (!(shdr().sh_flags & SHF_COMPRESSED) || uncompressed) {
memcpy(buf, contents.data(), contents.size());
memcpy(buf, contents.data(), sz);
return;
}
@@ -84,16 +84,35 @@ void InputSection<E>::copy_contents(Context<E> &ctx, u8 *buf) {
switch (hdr.ch_type) {
case ELFCOMPRESS_ZLIB: {
unsigned long size = sh_size;
if (::uncompress(buf, &size, (u8 *)data.data(), data.size()) != Z_OK)
Fatal(ctx) << *this << ": uncompress failed";
assert(size == sh_size);
z_stream s = {};
inflateInit(&s);
s.next_in = (u8 *)data.data();
s.avail_in = data.size();
s.next_out = buf;
s.avail_out = sz;
int r;
while (s.total_out < sz && (r = inflate(&s, Z_NO_FLUSH)) == Z_OK);
if (s.total_out < sz && r != Z_STREAM_END)
Fatal(ctx) << *this << ": uncompress failed: " << s.msg;
inflateEnd(&s);
break;
}
case ELFCOMPRESS_ZSTD:
if (ZSTD_decompress(buf, sh_size, (u8 *)data.data(), data.size()) != sh_size)
Fatal(ctx) << *this << ": ZSTD_decompress failed";
case ELFCOMPRESS_ZSTD: {
ZSTD_DCtx *dctx = ZSTD_createDCtx();
ZSTD_inBuffer in = { data.data(), data.size() };
ZSTD_outBuffer out = { buf, (size_t)sz };
while (out.pos < out.size) {
size_t r = ZSTD_decompressStream(dctx, &out, &in);
if (ZSTD_isError(r))
Fatal(ctx) << *this << ": uncompress failed: " << ZSTD_getErrorName(r);
if (r == 0 && out.pos < out.size)
Fatal(ctx) << *this << ": uncompress failed: premature end of input";
}
ZSTD_freeDCtx(dctx);
break;
}
default:
Fatal(ctx) << *this << ": unsupported compression type: 0x"
<< std::hex << hdr.ch_type;
@@ -114,12 +133,6 @@ static void do_action(Context<E> &ctx, Action action, InputSection<E> &isec,
<< sym << "' can not be used; recompile with -fPIC";
break;
case COPYREL:
// Create a copy relocation
if (!sym.file->is_dso) {
assert(sym.esym().is_undef_weak());
Error(ctx) << isec << ": cannot create a copy relocation for "
<< sym <<"; recompile with -fPIE or -fPIC";
}
sym.flags |= NEEDS_COPYREL;
break;
case PLT:
@@ -234,7 +247,7 @@ void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
if (deltas.empty()) {
// If a section is not relaxed, we can copy it as a one big chunk.
copy_contents(ctx, buf);
copy_contents_to(ctx, buf, sh_size);
} else {
// A relaxed section is copied piece-wise.
memcpy(buf, contents.data(), deltas[0].offset);
@@ -250,7 +263,7 @@ void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
}
}
} else {
copy_contents(ctx, buf);
copy_contents_to(ctx, buf, sh_size);
}
// Apply relocations
@@ -343,8 +356,8 @@ bool InputSection<E>::record_undef_error(Context<E> &ctx, const ElfRel<E> &rel)
template <typename E>
MergeableSection<E>::MergeableSection(Context<E> &ctx, MergedSection<E> &parent,
std::unique_ptr<InputSection<E>> &isec)
: parent(parent), p2align(isec->p2align), section(std::move(isec)) {
section->uncompress(ctx);
: parent(parent), p2align(isec->p2align), input_section(std::move(isec)) {
input_section->uncompress(ctx);
std::scoped_lock lock(parent.mu);
parent.members.push_back(this);
@@ -380,9 +393,9 @@ static size_t find_null(std::string_view data, i64 pos, i64 entsize) {
// We do not support mergeable sections that have relocations.
template <typename E>
void MergeableSection<E>::split_contents(Context<E> &ctx) {
std::string_view data = section->contents;
std::string_view data = input_section->contents;
if (data.size() > UINT32_MAX)
Fatal(ctx) << *section << ": mergeable section too large";
Fatal(ctx) << *input_section << ": mergeable section too large";
i64 entsize = parent.shdr.sh_entsize;
@@ -392,12 +405,13 @@ void MergeableSection<E>::split_contents(Context<E> &ctx) {
frag_offsets.push_back(pos);
size_t end = find_null(data, pos, entsize);
if (end == data.npos)
Fatal(ctx) << *section << ": string is not null terminated";
Fatal(ctx) << *input_section << ": string is not null terminated";
pos = end + entsize;
}
} else {
if (data.size() % entsize)
Fatal(ctx) << *section << ": section size is not multiple of sh_entsize";
Fatal(ctx) << *input_section
<< ": section size is not multiple of sh_entsize";
frag_offsets.reserve(data.size() / entsize);
for (i64 pos = 0; pos < data.size(); pos += entsize)
+11 -9
View File
@@ -29,8 +29,8 @@
//
// 4. Continue the rest of the linking process as usual.
//
// When gcc or clang inovkes ld, they pass `-plugin linker-plugin.so` to
// the linker. The given .so file provides a way to call the compiler
// When gcc or clang inovkes ld, they pass `-plugin /path/to/linker-plugin.so`
// to the linker. The given .so file provides a way to call the compiler
// backend.
//
// The linker plugin API is documented at
@@ -425,7 +425,7 @@ get_api_version(const char *plugin_identifier,
if (LAPI_V1 < minimal_api_supported)
Fatal(*gctx<E>) << "LTO plugin does not support V0 or V1 API";
std::string version = get_mold_version() + "\0"s;
std::string version = mold_version + "\0"s;
*linker_identifier = "mold";
*linker_version = version.data();
@@ -600,10 +600,10 @@ create_plugin_input_file(Context<E> &ctx, MappedFile *mf) {
template <typename E>
ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile *mf) {
if (ctx.arg.plugin.empty())
Fatal(ctx) << mf->name << ": don't know how to handle this LTO object file "
<< "because no -plugin option was given. Please make sure you "
<< "added -flto not only for creating object files but also for "
<< "creating the final executable.";
Fatal(ctx) << mf->name << ": unable to handle this LTO object file because "
<< "the -plugin option was not provided. Please make sure you "
<< "added -flto not only when creating object files but also "
<< "when linking the final executable.";
load_lto_plugin(ctx);
@@ -670,8 +670,10 @@ ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile *mf) {
// section. We handle such symbols differently than comdat symbols in
// a regular file because, unlike regular object files, IR files don't
// have input sections.
if (psym.comdat_key)
obj->lto_comdat_groups[i + 1] = insert_comdat_group(ctx, psym.comdat_key);
if (psym.comdat_key) {
std::string_view key = save_string(ctx, psym.comdat_key);
obj->lto_comdat_groups[i + 1] = insert_comdat_group(ctx, key);
}
}
obj->symbol_strtab = save_string(ctx, strtab);
+25 -23
View File
@@ -15,13 +15,6 @@
# include <unistd.h>
#endif
#ifdef MOLD_X86_64
int main(int argc, char **argv) {
mold::set_mimalloc_options();
return mold::mold_main<mold::X86_64>(argc, argv);
}
#endif
namespace mold {
template <typename E>
@@ -44,8 +37,8 @@ static ObjectFile<E> *new_object_file(Context<E> &ctx, ReaderContext &rctx,
ObjectFile<E> *file = new ObjectFile<E>(ctx, mf, archive_name);
ctx.obj_pool.emplace_back(file);
file->priority = ctx.file_priority++;
file->is_reachable =
!rctx.in_lib && (archive_name.empty() || rctx.whole_archive);
file->as_needed =
rctx.in_lib || (!archive_name.empty() && !rctx.whole_archive);
rctx.tg->run([file, &ctx] { file->parse(ctx); });
if (ctx.arg.trace)
@@ -65,8 +58,8 @@ static ObjectFile<E> *new_lto_obj(Context<E> &ctx, ReaderContext &rctx,
ObjectFile<E> *file = read_lto_object(ctx, mf);
file->priority = ctx.file_priority++;
file->archive_name = archive_name;
file->is_reachable =
!rctx.in_lib && (archive_name.empty() || rctx.whole_archive);
file->as_needed =
rctx.in_lib || (!archive_name.empty() && !rctx.whole_archive);
if (ctx.arg.trace)
Out(ctx) << "trace: " << *file;
@@ -81,7 +74,7 @@ new_shared_file(Context<E> &ctx, ReaderContext &rctx, MappedFile *mf) {
SharedFile<E> *file = new SharedFile<E>(ctx, mf);
ctx.dso_pool.emplace_back(file);
file->priority = ctx.file_priority++;
file->is_reachable = !rctx.as_needed;
file->as_needed = rctx.as_needed;
rctx.tg->run([file, &ctx] { file->parse(ctx); });
if (ctx.arg.trace)
@@ -496,7 +489,8 @@ int mold_main(int argc, char **argv) {
// create as few segments as possible.
sort_output_sections(ctx);
if (!ctx.arg.separate_debug_file.empty())
// Handle --separate-debug-file.
if (ctx.gnu_debuglink)
separate_debug_sections(ctx);
// Compute sizes of output sections while assigning offsets
@@ -514,6 +508,10 @@ int mold_main(int argc, char **argv) {
// be added to .dynsym.
sort_dynsyms(ctx);
// Sort .debug_info contents so that DWARF32 debug info precedes that of
// DWARF64. This is to mitigate the possibility of a relocation overflow.
sort_debug_info_sections(ctx);
// Print reports about undefined symbols, if needed.
if (ctx.arg.unresolved_symbols == UNRESOLVED_ERROR)
report_undef_errors(ctx);
@@ -536,13 +534,6 @@ int mold_main(int argc, char **argv) {
if (ctx.arg.emit_relocs)
create_reloc_sections(ctx);
// We've created range extension thunks with a pessimistive assumption
// that all out-of-section references are out of range. Now that we are
// able to assign addresses to all SHF_ALLOC output sections, we can
// eliminate excessive thunks.
if constexpr (needs_thunk<E>)
remove_redundant_thunks(ctx);
// Compute .symtab and .strtab sizes for each file.
if (!ctx.arg.strip_all)
create_output_symtab(ctx);
@@ -562,6 +553,14 @@ int mold_main(int argc, char **argv) {
filesize = set_osec_offsets(ctx);
}
// We've created range extension thunks with a pessimistive assumption
// that all out-of-section references are out of range. Now that we know
// the addresses of all sections,, we can eliminate excessive thunks.
if constexpr (needs_thunk<E>) {
remove_redundant_thunks(ctx);
filesize = set_osec_offsets(ctx);
}
if constexpr (is_arm32<E>) {
if (ctx.extra.exidx) {
ctx.extra.exidx->remove_duplicate_entries(ctx);
@@ -601,6 +600,9 @@ int mold_main(int argc, char **argv) {
// Copy input sections to the output file and apply relocations.
copy_chunks(ctx);
if constexpr (is_arm32be<E>)
arm32be_swap_bytes(ctx);
if constexpr (is_x86_64<E>)
if (ctx.arg.z_rewrite_endbr)
rewrite_endbr(ctx);
@@ -612,7 +614,7 @@ int mold_main(int argc, char **argv) {
// .gdb_index's contents cannot be constructed before applying
// relocations to other debug sections. We have relocated debug
// sections now, so write the .gdb_index section.
if (ctx.gdb_index && ctx.arg.separate_debug_file.empty())
if (ctx.gdb_index && !ctx.gnu_debuglink)
write_gdb_index(ctx);
// .note.gnu.build-id section contains a cryptographic hash of the
@@ -621,7 +623,7 @@ int mold_main(int argc, char **argv) {
if (ctx.buildid)
write_build_id(ctx);
if (!ctx.arg.separate_debug_file.empty())
if (ctx.gnu_debuglink)
write_gnu_debuglink(ctx);
t_copy.stop();
@@ -642,7 +644,7 @@ int mold_main(int argc, char **argv) {
if (ctx.arg.print_map)
print_map(ctx);
if (!ctx.arg.separate_debug_file.empty())
if (ctx.gnu_debuglink)
write_separate_debug_file(ctx);
// Show stats numbers
+6 -8
View File
@@ -19,14 +19,12 @@ 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->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, {isec, {}});
acc->second.push_back(sym);
if (sym->file == file && sym->get_type() != STT_SECTION) {
if (InputSection<E> *isec = sym->get_input_section()) {
typename Map<E>::accessor acc;
map.insert(acc, {isec, {}});
acc->second.push_back(sym);
}
}
}
});
+64 -36
View File
@@ -2,6 +2,7 @@
#include "../lib/common.h"
#include "elf.h"
#include "mold-git-hash.h"
#include <cassert>
#include <cstdint>
@@ -49,7 +50,7 @@ struct ReaderContext;
template <typename E>
std::ostream &operator<<(std::ostream &out, const Symbol<E> &sym);
std::string get_mold_version();
extern std::string mold_version;
//
// Mergeable section fragments
@@ -65,9 +66,13 @@ struct __attribute__((aligned(4))) SectionFragment {
}
MergedSection<E> &output_section;
u32 offset = -1;
i64 offset = -1;
Atomic<u8> p2align = 0;
Atomic<bool> is_alive = false;
// True if this fragment must be placed within 2^32 bytes from the
// start of the output section.
Atomic<bool> is_32bit = false;
};
// Additional class members for dynamic symbols. Because most symbols
@@ -262,9 +267,9 @@ struct FdeRecord {
template <typename E>
struct InputSectionExtras {};
template <>
struct InputSectionExtras<ARM32> {
InputSection<ARM32> *exidx = nullptr;
template <is_arm32 E>
struct InputSectionExtras<E> {
InputSection<E> *exidx = nullptr;
};
struct RelocDelta {
@@ -294,7 +299,7 @@ public:
InputSection(Context<E> &ctx, ObjectFile<E> &file, i64 shndx);
void uncompress(Context<E> &ctx);
void copy_contents(Context<E> &ctx, u8 *buf);
void copy_contents_to(Context<E> &ctx, u8 *buf, i64 sz);
void scan_relocations(Context<E> &ctx);
void write_to(Context<E> &ctx, u8 *buf);
void apply_reloc_alloc(Context<E> &ctx, u8 *base);
@@ -455,8 +460,8 @@ public:
// For example, range extension thunks adds function_name@thunk
// symbol for each thunk entry. The following members are used
// for such synthesizing symbols.
virtual void compute_symtab_size(Context<E> &ctx) {};
virtual void populate_symtab(Context<E> &ctx) {};
virtual void compute_symtab_size(Context<E> &ctx) {}
virtual void populate_symtab(Context<E> &ctx) {}
i64 local_symtab_idx = 0;
i64 num_local_symtab = 0;
@@ -570,7 +575,7 @@ public:
void populate_symtab(Context<E> &ctx) override;
void scan_abs_relocations(Context<E> &ctx);
void create_range_extension_thunks(Context<E> &ctx, bool first_pass);
void create_range_extension_thunks(Context<E> &ctx);
std::vector<InputSection<E> *> members;
std::vector<std::unique_ptr<Thunk<E>>> thunks;
@@ -1301,11 +1306,11 @@ public:
std::string_view get_contents(i64 idx);
MergedSection<E> &parent;
std::vector<SectionFragment<E> *> fragments;
u8 p2align = 0;
std::unique_ptr<InputSection<E>> input_section;
std::vector<SectionFragment<E> *> fragments;
private:
std::unique_ptr<InputSection<E>> section;
std::vector<u32> frag_offsets;
std::vector<u32> hashes;
};
@@ -1337,6 +1342,7 @@ public:
mark_live_objects(Context<E> &ctx,
std::function<void(InputFile<E> *)> feeder) = 0;
std::span<Symbol<E> *> get_local_syms();
std::span<Symbol<E> *> get_global_syms();
std::string_view get_source_name() const;
@@ -1353,6 +1359,7 @@ public:
std::string_view shstrtab;
std::string_view symbol_strtab;
bool as_needed = false;
bool has_init_array = false;
bool has_ctors = false;
@@ -1429,6 +1436,7 @@ public:
bool is_lto_obj = false;
bool is_gcc_offload_obj = false;
bool is_rust_obj = false;
bool is_dwarf32 = false;
i64 fde_idx = 0;
i64 fde_offset = 0;
@@ -1487,6 +1495,7 @@ public:
std::string soname;
std::vector<std::string_view> version_strings;
std::vector<Symbol<E> *> symbols2;
std::vector<ElfSym<E>> elf_syms2;
private:
@@ -1687,6 +1696,7 @@ template <typename E> void scan_relocations(Context<E> &);
template <typename E> void compute_imported_symbol_weakness(Context<E> &);
template <typename E> void construct_relr(Context<E> &);
template <typename E> void sort_dynsyms(Context<E> &);
template <typename E> void sort_debug_info_sections(Context<E> &);
template <typename E> void create_output_symtab(Context<E> &);
template <typename E> void report_undef_errors(Context<E> &);
template <typename E> void create_reloc_sections(Context<E> &);
@@ -1717,28 +1727,34 @@ void rewrite_endbr(Context<X86_64> &ctx);
// arch-arm32.cc
//
class Arm32ExidxSection : public Chunk<ARM32> {
template <is_arm32 E>
class Arm32ExidxSection : public Chunk<E> {
public:
Arm32ExidxSection(OutputSection<ARM32> &osec) : output_section(osec) {
Arm32ExidxSection(OutputSection<E> &osec) : output_section(osec) {
this->name = ".ARM.exidx";
this->shdr.sh_type = SHT_ARM_EXIDX;
this->shdr.sh_flags = SHF_ALLOC;
this->shdr.sh_addralign = 4;
}
void compute_section_size(Context<ARM32> &ctx) override;
void update_shdr(Context<ARM32> &ctx) override;
void remove_duplicate_entries(Context<ARM32> &ctx);
void copy_buf(Context<ARM32> &ctx) override;
void compute_section_size(Context<E> &ctx) override;
void update_shdr(Context<E> &ctx) override;
void remove_duplicate_entries(Context<E> &ctx);
void copy_buf(Context<E> &ctx) override;
private:
std::vector<u8> get_contents(Context<ARM32> &ctx);
std::vector<u8> get_contents(Context<E> &ctx);
OutputSection<ARM32> &output_section;
OutputSection<E> &output_section;
};
template <> u64 get_eflags(Context<ARM32> &ctx);
void create_arm_exidx_section(Context<ARM32> &ctx);
template <is_arm32 E>
u64 get_eflags(Context<E> &ctx);
template <is_arm32 E>
void create_arm_exidx_section(Context<E> &ctx);
void arm32be_swap_bytes(Context<ARM32BE> &ctx);
//
// arch-riscv.cc
@@ -1808,12 +1824,6 @@ public:
template <> u64 get_eflags(Context<PPC64V2> &ctx);
//
// arch-arc.cc
//
template <> u64 get_eflags(Context<ARC> &ctx);
//
// main.cc
//
@@ -1894,9 +1904,9 @@ struct ContextExtras<E> {
NotePropertySection<E> *note_property = nullptr;
};
template <>
struct ContextExtras<ARM32> {
Arm32ExidxSection *exidx = nullptr;
template <is_arm32 E>
struct ContextExtras<E> {
Arm32ExidxSection<E> *exidx = nullptr;
};
template <is_riscv E>
@@ -1934,7 +1944,7 @@ struct ContextExtras<SPARC64> {
template <typename E>
struct Context {
Context() {
arg.entry = get_symbol(*this, is_arc<E> ? "__start" : "_start");
arg.entry = get_symbol(*this, "_start");
arg.fini = get_symbol(*this, "_fini");
arg.init = get_symbol(*this, "_init");
@@ -1967,6 +1977,7 @@ struct Context {
bool allow_multiple_definition = false;
bool allow_shlib_undefined = true;
bool apply_dynamic_relocs = true;
bool be8 = false;
bool color_diagnostics = false;
bool default_symver = false;
bool demangle = true;
@@ -2392,6 +2403,9 @@ public:
bool is_traced : 1 = false; // for --trace-symbol
bool is_wrapped : 1 = false; // for --wrap
// For symbols with default symbol version, e.g. foo@@VERSION.
bool is_versioned_default : 1 = false;
// If a symbol can be resolved to a symbol in a different ELF file at
// runtime, `is_imported` is true. If a symbol is a dynamic symbol and
// can be used by other ELF file at runtime, `is_exported` is true.
@@ -2695,8 +2709,8 @@ template <typename E>
std::string_view MergeableSection<E>::get_contents(i64 i) {
i64 cur = frag_offsets[i];
if (i == frag_offsets.size() - 1)
return section->contents.substr(cur);
return section->contents.substr(cur, frag_offsets[i + 1] - cur);
return input_section->contents.substr(cur);
return input_section->contents.substr(cur, frag_offsets[i + 1] - cur);
}
template <typename E>
@@ -2734,6 +2748,11 @@ inline std::string_view InputFile<E>::get_string(Context<E> &ctx, i64 idx) {
return this->get_string(ctx, elf_sections[idx]);
}
template <typename E>
inline std::span<Symbol<E> *> InputFile<E>::get_local_syms() {
return std::span<Symbol<E> *>(this->symbols).subspan(0, this->first_global);
}
template <typename E>
inline std::span<Symbol<E> *> InputFile<E>::get_global_syms() {
return std::span<Symbol<E> *>(this->symbols).subspan(this->first_global);
@@ -3021,12 +3040,16 @@ template <typename E>
u64
Symbol<E>::get_thunk_addr(Context<E> &ctx, u64 P) const requires needs_thunk<E> {
std::span<u64> vec = ctx.symbol_aux[aux_idx].thunk_addrs;
u64 lo = (P < branch_distance<E>) ? 0 : P - branch_distance<E>;
u64 val = *ranges::lower_bound(vec, lo);
i64 disp = val - P;
auto it = ranges::lower_bound(vec, lo);
if (it == vec.end())
Fatal(ctx) << "range extension thunk out of range: " << *this;
i64 disp = *it - P;
if (disp < -branch_distance<E> || branch_distance<E> <= disp)
Fatal(ctx) << "range extension thunk out of range: " << *this;
return val;
return *it;
}
template <typename E>
@@ -3036,6 +3059,11 @@ inline bool Symbol<E>::has_plt(Context<E> &ctx) const {
template <typename E>
inline bool Symbol<E>::is_absolute() const {
// An unresolved weak symbol acts as if it were an absolute address
// at address 0
if (is_remaining_undef_weak())
return true;
return !is_imported && !get_frag() && !get_input_section() &&
!get_output_section();
}
+61 -43
View File
@@ -834,20 +834,6 @@ void DynamicSection<E>::copy_buf(Context<E> &ctx) {
write_vector(ctx.buf + this->shdr.sh_offset, contents);
}
template <typename T>
static std::vector<std::span<T>> split(std::vector<T> &input, i64 unit) {
std::span<T> span(input);
std::vector<std::span<T>> vec;
while (span.size() >= unit) {
vec.push_back(span.subspan(0, unit));
span = span.subspan(unit);
}
if (!span.empty())
vec.push_back(span);
return vec;
}
// Assign offsets to OutputSection members
template <typename E>
void OutputSection<E>::compute_section_size(Context<E> &ctx) {
@@ -1176,6 +1162,9 @@ void OutputSection<E>::populate_symtab(Context<E> &ctx) {
u8 *strtab_base = ctx.buf + ctx.strtab->shdr.sh_offset;
u8 *strtab = strtab_base + this->strtab_offset;
memset(esym, 0, this->num_local_symtab * sizeof(ElfSym<E>));
memset(strtab, 0, this->strtab_size);
auto write_esym = [&](u64 addr, i64 st_name) {
memset(esym, 0, sizeof(*esym));
esym->st_name = st_name;
@@ -1315,18 +1304,16 @@ static std::vector<GotEntry<E>> get_got_entries(Context<E> &ctx) {
}
}
// If a symbol is imported, let the dynamic linker to resolve it.
if (sym->is_imported) {
// If a symbol is imported, let the dynamic linker to resolve it.
add({idx, 0, E::R_GLOB_DAT, sym});
continue;
}
// If we know an address at link-time, fill that GOT entry now.
// It may need a base relocation, though.
if (ctx.arg.pic && sym->is_relative())
} else if (ctx.arg.pic && sym->is_relative()) {
// We know the symbol's address, but it needs a base relocation.
add({idx, sym->get_addr(ctx, NO_PLT), E::R_RELATIVE});
else
} else {
// We know the symbol's exact run-time address at link-time.
add({idx, sym->get_addr(ctx, NO_PLT)});
}
}
// Create GOT entries for TLVs.
@@ -2106,7 +2093,7 @@ static void add_comment_strings(Context<E> &ctx) {
};
// Add an identification string to .comment.
add(get_mold_version());
add(mold_version);
// Embed command line arguments for debugging.
char *env = getenv("MOLD_DEBUG");
@@ -2144,43 +2131,54 @@ void MergedSection<E>::compute_section_size(Context<E> &ctx) {
if (!resolved)
resolve(ctx);
std::vector<i64> sizes(map.NUM_SHARDS);
std::vector<i64> sizes(map.NUM_SHARDS * 2);
tbb::parallel_for((i64)0, map.NUM_SHARDS, [&](i64 i) {
using Entry = typename decltype(map)::Entry;
std::vector<Entry *> entries = map.get_sorted_entries(i);
i64 offset = 0;
i64 off1 = 0;
i64 off2 = 0;
for (Entry *ent : entries) {
SectionFragment<E> &frag = ent->value;
if (frag.is_alive) {
offset = align_to(offset, 1 << frag.p2align);
frag.offset = offset;
offset += ent->keylen;
if (frag.is_32bit) {
off1 = align_to(off1, 1 << frag.p2align);
frag.offset = off1;
off1 += ent->keylen;
} else {
off2 = align_to(off2, 1 << frag.p2align);
frag.offset = off2;
off2 += ent->keylen;
}
}
}
sizes[i] = offset;
sizes[i] = off1;
sizes[i + map.NUM_SHARDS] = off2;
});
i64 shard_size = map.nbuckets / map.NUM_SHARDS;
shard_offsets.resize(map.NUM_SHARDS + 1);
shard_offsets.resize(sizes.size() + 1);
for (i64 i = 1; i < map.NUM_SHARDS + 1; i++)
for (i64 i = 1; i < sizes.size() + 1; i++)
shard_offsets[i] =
align_to(shard_offsets[i - 1] + sizes[i - 1], this->shdr.sh_addralign);
this->shdr.sh_size = shard_offsets.back();
tbb::parallel_for((i64)1, map.NUM_SHARDS, [&](i64 i) {
for (i64 j = shard_size * i; j < shard_size * (i + 1); j++) {
SectionFragment<E> &frag = map.entries[j].value;
if (frag.is_alive)
frag.offset += shard_offsets[i];
if (frag.is_alive) {
if (frag.is_32bit)
frag.offset += shard_offsets[i];
else
frag.offset += shard_offsets[i + map.NUM_SHARDS];
}
}
});
this->shdr.sh_size = shard_offsets[map.NUM_SHARDS];
if (this->shdr.sh_size > UINT32_MAX)
Fatal(ctx) << this->name << ": output section too large";
}
template <typename E>
@@ -2196,8 +2194,11 @@ void MergedSection<E>::write_to(Context<E> &ctx, u8 *buf) {
// There might be gaps between strings to satisfy alignment requirements.
// If that's the case, we need to zero-clear them.
if (this->shdr.sh_addralign > 1 &&
this->shdr.sh_addralign != this->shdr.sh_entsize)
this->shdr.sh_addralign != this->shdr.sh_entsize) {
memset(buf + shard_offsets[i], 0, shard_offsets[i + 1] - shard_offsets[i]);
i64 j = map.NUM_SHARDS + i;
memset(buf + shard_offsets[j], 0, shard_offsets[j + 1] - shard_offsets[j]);
}
// Copy strings
for (i64 j = shard_size * i; j < shard_size * (i + 1); j++)
@@ -2447,21 +2448,30 @@ void EhFrameRelocSection<E>::copy_buf(Context<E> &ctx) {
template <typename E>
void CopyrelSection<E>::add_symbol(Context<E> &ctx, Symbol<E> *sym) {
assert(!ctx.arg.shared);
if (sym->has_copyrel)
return;
assert(!ctx.arg.shared);
assert(sym->file->is_dso);
if (!sym->file->is_dso) {
assert(sym->esym().is_undef_weak());
Error(ctx) << *sym->file << ": cannot create a copy relocation for "
<< *sym <<"; recompile with -fPIE or -fPIC";
return;
}
if (sym->esym().st_visibility == STV_PROTECTED)
if (sym->esym().st_visibility == STV_PROTECTED) {
Error(ctx) << *sym->file
<< ": cannot create a copy relocation for protected symbol '"
<< *sym << "'; recompile with -fPIC";
return;
}
if (!ctx.arg.z_copyreloc)
if (!ctx.arg.z_copyreloc) {
Error(ctx) << "-z nocopyreloc: " << *sym->file
<< ": cannot create a copy relocation for symbol '" << *sym
<< "'; recompile with -fPIC";
return;
}
symbols.push_back(sym);
@@ -2634,6 +2644,14 @@ void VerdefSection<E>::construct(Context<E> &ctx) {
if (ctx.arg.version_definitions.empty())
return;
// Handle --default-symver
if (ctx.arg.default_symver)
for (Symbol<E> *sym : ctx.dynsym->symbols)
if (sym && !sym->file->is_dso)
if (u16 ver = sym->ver_idx;
ver == VER_NDX_GLOBAL || ver == VER_NDX_UNSPECIFIED)
sym->ver_idx = VER_NDX_LAST_RESERVED + 1;
// Resize .gnu.version and write to it
ctx.versym->contents.resize(ctx.dynsym->symbols.size(), VER_NDX_GLOBAL);
ctx.versym->contents[0] = VER_NDX_LOCAL;
+292 -135
View File
@@ -26,8 +26,10 @@ int redo_main(Context<E> &ctx, int argc, char **argv) {
return mold_main<ARM64LE>(argc, argv);
if (target == ARM64BE::name)
return mold_main<ARM64BE>(argc, argv);
if (target == ARM32::name)
return mold_main<ARM32>(argc, argv);
if (target == ARM32LE::name)
return mold_main<ARM32LE>(argc, argv);
if (target == ARM32BE::name)
return mold_main<ARM32BE>(argc, argv);
if (target == RV64LE::name)
return mold_main<RV64LE>(argc, argv);
if (target == RV64BE::name)
@@ -56,8 +58,6 @@ int redo_main(Context<E> &ctx, int argc, char **argv) {
return mold_main<LOONGARCH32>(argc, argv);
if (target == LOONGARCH64::name)
return mold_main<LOONGARCH64>(argc, argv);
if (target == ARC::name)
return mold_main<ARC>(argc, argv);
abort();
}
@@ -217,14 +217,14 @@ static void mark_live_objects(Context<E> &ctx) {
}
std::vector<InputFile<E> *> roots;
append(roots, ctx.objs);
append(roots, ctx.dsos);
for (InputFile<E> *file : ctx.objs)
if (file->is_reachable)
roots.push_back(file);
for (InputFile<E> *file : roots)
if (!file->as_needed)
file->is_reachable = true;
for (InputFile<E> *file : ctx.dsos)
if (file->is_reachable)
roots.push_back(file);
std::erase_if(roots, [](InputFile<E> *file) { return !file->is_reachable; });
tbb::parallel_for_each(roots, [&](InputFile<E> *file,
tbb::feeder<InputFile<E> *> &feeder) {
@@ -249,25 +249,15 @@ static void mark_live_objects(Context<E> &ctx) {
// `VER1`. If it does, that's the symbol we are looking for.
template <typename E>
static void resolve_default_symver(Context<E> &ctx) {
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (i64 i = file->first_global; i < file->elf_syms.size(); i++) {
const ElfSym<E> &esym = file->elf_syms[i];
Symbol<E> &sym = *file->symbols[i];
std::vector<InputFile<E> *> files;
append(files, ctx.objs);
append(files, ctx.dsos);
if (!sym.file && esym.is_undef() &&
file->has_symver[i - file->first_global]) {
std::string_view str = file->symbol_strtab.data() + esym.st_name;
i64 pos = str.find('@');
assert(pos != str.npos);
std::string_view name = str.substr(0, pos);
std::string_view ver = str.substr(pos + 1);
Symbol<E> *sym2 = get_symbol(ctx, name);
if (sym2->file && sym2->file->is_dso && sym2->get_version() == ver)
file->symbols[i] = sym2;
}
}
tbb::parallel_for_each(files, [](InputFile<E> *file) {
std::span<Symbol<E> *> syms = file->get_global_syms();
for (i64 i = 0; i < syms.size(); i++)
if (syms[i]->is_versioned_default)
syms[i] = (Symbol<E> *)syms[i]->origin;
});
}
@@ -312,69 +302,68 @@ void resolve_symbols(Context<E> &ctx) {
resolve_default_symver(ctx);
mark_live_objects(ctx);
// Now that we know the exact set of input files that are to be
// included in the output file, we want to redo symbol resolution.
// This is because symbols defined by object files in archive files
// may have risen as a result of mark_live_objects().
//
// To redo symbol resolution, we want to clear the state first.
clear_symbols(ctx);
// COMDAT elimination needs to happen exactly here.
//
// It needs to be after archive extraction, otherwise we might
// assign COMDAT leader to an archive member that is not supposed to
// be extracted.
//
// It needs to happen before the final symbol resolution, otherwise
// we could eliminate a symbol that is already resolved to and cause
// dangling references.
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
if (file->is_reachable) {
for (ComdatGroupRef<E> &ref : file->comdat_groups)
update_minimum(ref.group->owner, file->priority);
for (ComdatGroup *g : file->lto_comdat_groups)
if (g)
update_minimum(g->owner, file->priority);
}
});
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
if (file->is_reachable)
for (ComdatGroupRef<E> &ref : file->comdat_groups)
if (ref.group->owner != file->priority)
for (u32 i : ref.members)
if (InputSection<E> *isec = file->sections[i].get())
isec->is_alive = false;
});
// Redo symbol resolution
tbb::parallel_for_each(files, [&](InputFile<E> *file) {
if (file->is_reachable)
file->resolve_symbols(ctx);
});
// Symbols with hidden visibility need to be resolved within the
// output file. If a hidden symbol was resolved to a DSO, we'll redo
// symbol resolution from scratch with the flag to skip that symbol
// next time. This should be rare.
std::atomic_bool flag = false;
std::atomic_bool redo = false;
tbb::parallel_for_each(ctx.dsos, [&](SharedFile<E> *file) {
if (file->is_reachable) {
for (Symbol<E> *sym : file->symbols) {
if (sym->file == file && sym->visibility == STV_HIDDEN) {
sym->skip_dso = true;
flag = true;
redo = true;
}
}
}
});
if (!flag)
return;
if (!redo)
break;
clear_symbols(ctx);
}
// Now that we know the exact set of input files that are to be
// included in the output file, we want to redo symbol resolution.
// This is because symbols defined by object files in archive files
// may have risen as a result of mark_live_objects().
//
// To redo symbol resolution, we want to clear the state first.
clear_symbols(ctx);
// COMDAT elimination needs to happen exactly here.
//
// It needs to be after archive extraction, otherwise we might
// assign COMDAT leader to an archive member that is not supposed to
// be extracted.
//
// It needs to happen before the final symbol resolution, otherwise
// we could eliminate a symbol that is already resolved to and cause
// dangling references.
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
if (file->is_reachable) {
for (ComdatGroupRef<E> &ref : file->comdat_groups)
update_minimum(ref.group->owner, file->priority);
for (ComdatGroup *g : file->lto_comdat_groups)
if (g)
update_minimum(g->owner, file->priority);
}
});
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
if (file->is_reachable)
for (ComdatGroupRef<E> &ref : file->comdat_groups)
if (ref.group->owner != file->priority)
for (u32 i : ref.members)
if (InputSection<E> *isec = file->sections[i].get())
isec->is_alive = false;
});
// Redo symbol resolution
tbb::parallel_for_each(files, [&](InputFile<E> *file) {
if (file->is_reachable)
file->resolve_symbols(ctx);
});
}
// Do link-time optimization. We pass all IR object files to the compiler
@@ -1051,7 +1040,7 @@ void write_repro_file(Context<E> &ctx) {
Fatal(ctx) << "cannot open " << path << ": " << errno_string();
tar->append("response.txt", create_response_file(ctx));
tar->append("version.txt", get_mold_version() + "\n");
tar->append("version.txt", mold_version + "\n");
std::unordered_set<std::string_view> seen;
@@ -1127,35 +1116,41 @@ void check_shlib_undefined(Context<E> &ctx) {
return false;
};
// Obtain a list of known shared library names.
std::unordered_set<std::string_view> sonames;
for (std::unique_ptr<SharedFile<E>> &file : ctx.dso_pool)
sonames.insert(file->soname);
// Skip test if we don't have a complete set of shared object files
// for the program, because if there's a missing .so, an undefined
// symbol might be defined by that library.
auto do_test = [&] {
std::unordered_set<std::string_view> sonames;
for (std::unique_ptr<SharedFile<E>> &file : ctx.dso_pool)
sonames.insert(file->soname);
tbb::parallel_for_each(ctx.dsos, [&](SharedFile<E> *file) {
// Skip the file if it depends on a file that we know nothing about.
// This is because missing symbols may be provided by that unknown file.
for (std::string_view needed : file->get_dt_needed(ctx))
if (!sonames.contains(needed))
return;
for (SharedFile<E> *file : ctx.dsos)
for (std::string_view soname : file->get_dt_needed(ctx))
if (!sonames.contains(soname))
return false;
return true;
};
// Check if all undefined symbols have been resolved.
for (i64 i = 0; i < file->elf_syms.size(); i++) {
const ElfSym<E> &esym = file->elf_syms[i];
Symbol<E> &sym = *file->symbols[i];
if (esym.is_undef() && !esym.is_weak() && !sym.file &&
!is_sparc_register(esym))
Error(ctx) << *file << ": --no-allow-shlib-undefined: undefined symbol: "
<< sym;
}
});
if (do_test()) {
tbb::parallel_for_each(ctx.dsos, [&](SharedFile<E> *file) {
// Check if all undefined symbols have been resolved.
for (i64 i = 0; i < file->elf_syms.size(); i++) {
const ElfSym<E> &esym = file->elf_syms[i];
Symbol<E> &sym = *file->symbols[i];
if (esym.is_undef() && !esym.is_weak() && !sym.file &&
!is_sparc_register(esym))
Error(ctx) << *file << ": --no-allow-shlib-undefined: undefined symbol: "
<< sym;
}
});
}
// Beyond this point, DSOs that are not referenced directly by any
// object file are not needed. They were kept by
// SharedFile<E>::mark_live_objects just for this pass. Therefore,
// remove unneeded DSOs from the list now.
for (SharedFile<E> *file : ctx.dsos)
file->is_reachable = false;
file->is_reachable = !file->as_needed;
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (Symbol<E> *sym : file->get_global_syms())
@@ -1171,10 +1166,6 @@ template <typename E>
void check_symbol_types(Context<E> &ctx) {
Timer t(ctx, "check_symbol_types");
std::vector<InputFile<E> *> files;
append(files, ctx.objs);
append(files, ctx.dsos);
auto canonicalize = [](u32 ty) -> u32 {
if (ty == STT_GNU_IFUNC)
return STT_FUNC;
@@ -1183,23 +1174,35 @@ void check_symbol_types(Context<E> &ctx) {
return ty;
};
tbb::parallel_for_each(files.begin(), files.end(), [&](InputFile<E> *file) {
auto check = [&](InputFile<E> &file, Symbol<E> &sym,
const ElfSym<E> &esym1, const ElfSym<E> &esym2) {
if (sym.file && sym.file != &file &&
esym1.st_type != STT_NOTYPE && esym2.st_type != STT_NOTYPE &&
canonicalize(esym1.st_type) != canonicalize(esym2.st_type)) {
Warn(ctx) << "symbol type mismatch: " << sym << '\n'
<< ">>> defined in " << *sym.file << " as "
<< stt_to_string<E>(esym1.st_type) << '\n'
<< ">>> defined in " << file << " as "
<< stt_to_string<E>(esym2.st_type);
}
};
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (i64 i = file->first_global; i < file->elf_syms.size(); i++)
if (Symbol<E> *sym = file->symbols[i];
sym->file && sym->file != file)
check(*file, *sym, sym->esym(), file->elf_syms[i]);
});
tbb::parallel_for_each(ctx.dsos, [&](SharedFile<E> *file) {
for (i64 i = file->first_global; i < file->elf_syms.size(); i++) {
Symbol<E> &sym = *file->symbols[i];
if (!sym.file || sym.file == file)
continue;
if (Symbol<E> *sym = file->symbols[i];
sym->file && sym->file != file)
check(*file, *sym, sym->esym(), file->elf_syms[i]);
const ElfSym<E> &esym1 = sym.esym();
const ElfSym<E> &esym2 = file->elf_syms[i];
if (esym1.st_type != STT_NOTYPE && esym2.st_type != STT_NOTYPE &&
canonicalize(esym1.st_type) != canonicalize(esym2.st_type)) {
Warn(ctx) << "symbol type mismatch: " << sym << '\n'
<< ">>> defined in " << *sym.file << " as "
<< stt_to_string<E>(esym1.st_type) << '\n'
<< ">>> defined in " << *file << " as "
<< stt_to_string<E>(esym2.st_type);
}
if (Symbol<E> *sym = file->symbols2[i])
if (sym->file && sym->file != file)
check(*file, *sym, sym->esym(), file->elf_syms[i]);
}
});
}
@@ -1297,6 +1300,142 @@ void sort_ctor_dtor(Context<E> &ctx) {
}
}
// Returns true if a given section contains a DWARF32 debug record.
// `isec` must be a .debug_info section.
template <typename E>
static bool is_dwarf32(Context<E> &ctx, InputSection<E> *isec) {
if (!isec)
return true;
if (isec->sh_size < 12) {
// The section is too short. This is a user error, but instead of
// being nitpicky about it, we simply handle it on a garbage-in,
// garbage-out basis.
return true;
}
// A .debug_info section contains compilation units (CUs). A 32-bit CU
// starts with a 32-bit size field, while a 64-bit CU starts with a
// magic number 0xffff'ffff followed by a 64-bit size field.
//
// Note that size doesn't take the size field itself into account, so
// the actual size of a 64-bit CU including the size field is 12 bytes
// larger than the value in the size field.
u8 buf[12];
isec->copy_contents_to(ctx, buf, 12);
if (*(U32<E> *)buf != 0xffff'ffff)
return true;
if (*(U64<E> *)(buf + 4) + 12 == isec->sh_size)
return false;
// An input .debug_info section usually contains a single CU. However,
// if the linker combines multiple object files using `-r`, the
// resulting object file may have a .debug_info section with as many CUs
// as there are in the input files. Therefore, if the first CU doesn't
// cover the entire .debug_info section, we need to keep reading until
// the end of the section.
//
// An input .debug_info section may be compressed using zlib or zstd, so
// we need to uncompress it before accessing `isec->contents`.
isec->uncompress(ctx);
u8 *p = (u8 *)isec->contents.data() + *(U64<E> *)(buf + 4) + 12;
u8 *end = (u8 *)isec->contents.data() + isec->sh_size;
while (end - p >= 12) {
if (*(U32<E> *)p != 0xffff'ffff)
return true;
p += *(U64<E> *)(p + 4) + 12;
}
return false;
}
// Debug sections in an input object file refer to other debug sections in
// the same file using section offsets. The offsets are 64 bits in DWARF64
// and 32 bits in DWARF32.
//
// GCC and Clang emit DWARF32 debug info by default even for 64-bit code.
// That is, 64-bit values are used for addresses, and 32-bit values for
// references between debug sections. This makes sense for ordinary programs
// because it reduces the size of the debug info sections.
//
// You can change the format to DWARF64 by passing `-gdwarf64`. Therefore,
// the "right" approach to build an extremely large program in debug mode is
// to recompile everything with `-gdwarf64`. However, that’s often not
// feasiable for various reasons.
//
// If we don't do anything about it, a relocation overflow could occur if
// any output debug section exceeds 4 GiB in size, making it almost
// impossible for users to link an object file compiled without `-gdwarf64`
// to an extremely large program.
//
// This function works around the issue by sorting output debug section
// contents so that DWARF32 input sections are at the start of the output
// section followed by DWARF64 input sections. By doing this, we can avoid
// relocation overflow until the total size of DWARF32 input sections alone
// exceeds 4 GiB.
template <typename E>
void sort_debug_info_sections(Context<E> &ctx) {
Timer t(ctx, "sort_debug_info_sections");
// True if mold is running under ctest
bool is_in_test = false;
if (char *env = getenv("MOLD_DEBUG"); env && env[0])
is_in_test = true;
// Get lists of output debug sections that need sorting
std::vector<OutputSection<E> *> vec1;
std::vector<MergedSection<E> *> vec2;
for (Chunk<E> *chunk : ctx.chunks)
if (OutputSection<E> *osec = chunk->to_osec())
if (!(osec->shdr.sh_flags & SHF_ALLOC) && osec->name.starts_with(".debug_"))
if (osec->shdr.sh_size >= UINT32_MAX || is_in_test)
vec1.push_back(osec);
for (std::unique_ptr<MergedSection<E>> &osec : ctx.merged_sections)
if (!(osec->shdr.sh_flags & SHF_ALLOC) && osec->name.starts_with(".debug_"))
if (osec->shdr.sh_size >= UINT32_MAX || is_in_test)
vec2.push_back(osec.get());
if (vec1.empty() && vec2.empty())
return;
// Read each input file's .debug_info to record whether the file contains
// DWARF32 or DWARF64
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
file->is_dwarf32 = is_dwarf32(ctx, file->debug_info);
});
// Unless we have a mix of DWARF32 and DWARF64, it doesn't make sense to
// sort sections.
if (ranges::all_of(ctx.objs, &ObjectFile<E>::is_dwarf32) ||
ranges::none_of(ctx.objs, &ObjectFile<E>::is_dwarf32))
return;
// Reorder input sections in the output section so that DWARF32
// precededs DWARF64
tbb::parallel_for_each(vec1, [&](OutputSection<E> *osec) {
ranges::stable_partition(osec->members, [](InputSection<E> *isec) {
return isec->file.is_dwarf32;
});
osec->compute_section_size(ctx);
});
// Reorder strings in .debug_str and the like
tbb::parallel_for_each(vec2, [&](MergedSection<E> *osec) {
tbb::parallel_for_each(osec->members, [&](MergeableSection<E> *m) {
if (m->input_section->file.is_dwarf32)
for (SectionFragment<E> *frag : m->fragments)
frag->is_32bit = true;
});
});
tbb::parallel_for_each(vec2, [&](MergedSection<E> *osec) {
osec->compute_section_size(ctx);
});
}
// .ctors/.dtors serves the same purpose as .init_array/.fini_array,
// albeit with very subtly differences. Both contain pointers to
// initializer/finalizer functions. The runtime executes them one by one
@@ -1371,7 +1510,8 @@ void shuffle_sections(Context<E> &ctx) {
auto is_eligible = [](OutputSection<E> *osec) {
if (osec) {
std::string_view name = osec->name;
return name != ".init" && name != ".fini" &&
return (osec->shdr.sh_flags & SHF_ALLOC) &&
name != ".init" && name != ".fini" &&
name != ".ctors" && name != ".dtors" &&
name != ".init_array" && name != ".preinit_array" &&
name != ".fini_array";
@@ -1443,7 +1583,7 @@ void compute_section_sizes(Context<E> &ctx) {
// create_range_extension_thunks is not thread-safe
for (Chunk<E> *chunk : std::span(vec.begin(), tail.begin()))
chunk->to_osec()->create_range_extension_thunks(ctx, true);
chunk->to_osec()->create_range_extension_thunks(ctx);
tbb::parallel_for_each(tail, [&](Chunk<E> *chunk) {
chunk->compute_section_size(ctx);
@@ -1618,7 +1758,8 @@ void scan_relocations(Context<E> &ctx) {
ctx.got->add_tlsdesc_symbol(ctx, sym);
if (sym->flags & NEEDS_COPYREL) {
if (ctx.arg.z_relro && ((SharedFile<E> *)sym->file)->is_readonly(sym))
if (ctx.arg.z_relro && sym->file->is_dso &&
((SharedFile<E> *)sym->file)->is_readonly(sym))
ctx.copyrel_relro->add_symbol(ctx, sym);
else
ctx.copyrel->add_symbol(ctx, sym);
@@ -2414,6 +2555,15 @@ void sort_output_sections(Context<E> &ctx) {
sort_output_sections_by_order(ctx);
}
template <typename E>
static i64 get_tls_segment_alignment(Context<E> &ctx) {
i64 val = 1;
for (Chunk<E> *chunk : ctx.chunks)
if (chunk->shdr.sh_flags & SHF_TLS)
val = std::max<i64>(val, chunk->shdr.sh_addralign);
return val;
}
// This function assigns virtual addresses to output sections. Assigning
// addresses is a bit tricky because we want to pack sections as tightly
// as possible while not violating the constraints imposed by the hardware
@@ -2458,8 +2608,12 @@ static void set_virtual_addresses_regular(Context<E> &ctx) {
std::vector<Chunk<E> *> &chunks = ctx.chunks;
u64 addr = ctx.arg.image_base;
auto is_tls = [](Chunk<E> *chunk) {
return chunk->shdr.sh_flags & SHF_TLS;
};
auto is_tbss = [](Chunk<E> *chunk) {
return (chunk->shdr.sh_type == SHT_NOBITS) && (chunk->shdr.sh_flags & SHF_TLS);
return (chunk->shdr.sh_flags & SHF_TLS) && (chunk->shdr.sh_type == SHT_NOBITS);
};
for (i64 i = 0; i < chunks.size(); i++) {
@@ -2516,6 +2670,12 @@ static void set_virtual_addresses_regular(Context<E> &ctx) {
}
}
// TLS sections are included only in PT_LOAD but also in PT_TLS.
// We align the first TLS section so that the PT_TLS segment starts
// at an address that meets the segment's alignment requirement.
if (is_tls(chunks[i]) && (i == 0 || !is_tls(chunks[i - 1])))
addr = align_to(addr, get_tls_segment_alignment(ctx));
// TLS BSS sections are laid out so that they overlap with the
// subsequent non-tbss sections. Overlapping is fine because a STT_TLS
// segment contains an initialization image for newly-created threads,
@@ -2766,7 +2926,7 @@ i64 set_osec_offsets(Context<E> &ctx) {
if (ctx.phdr) {
i64 sz = ctx.phdr->shdr.sh_size;
ctx.phdr->update_shdr(ctx);
if (sz != ctx.phdr->shdr.sh_size)
if (sz < ctx.phdr->shdr.sh_size)
continue;
}
@@ -2801,10 +2961,10 @@ void fix_synthetic_symbols(Context<E> &ctx) {
}
};
auto stop = [](Symbol<E> *sym, auto &chunk) {
auto stop = [](Symbol<E> *sym, auto &chunk, i64 bias = 0) {
if (sym && chunk) {
sym->set_output_section(chunk);
sym->value = chunk->shdr.sh_addr + chunk->shdr.sh_size;
sym->value = chunk->shdr.sh_addr + chunk->shdr.sh_size + bias;
}
};
@@ -2841,10 +3001,9 @@ void fix_synthetic_symbols(Context<E> &ctx) {
// such executable lacks the .dynamic section and thus there's no way
// to find ifunc relocations other than these symbols.
if (ctx.reldyn && ctx.arg.static_ && !ctx.arg.pie) {
stop(ctx.__rel_iplt_start, ctx.reldyn);
stop(ctx.__rel_iplt_start, ctx.reldyn,
-get_num_irelative_relocs(ctx) * sizeof(ElfRel<E>));
stop(ctx.__rel_iplt_end, ctx.reldyn);
ctx.__rel_iplt_start->value -=
get_num_irelative_relocs(ctx) * sizeof(ElfRel<E>);
} else {
// If the symbols are not ncessary, we turn them to absolute
// symbols at address 0.
@@ -2946,17 +3105,13 @@ void fix_synthetic_symbols(Context<E> &ctx) {
}
// PPC64's _{save,rest}gpr{0,1}_{14,15,16,...,31} symbols
if constexpr (is_ppc64v2<E>) {
i64 offset = 0;
for (std::pair<std::string_view, u32> p : ppc64_save_restore_insns) {
std::string_view label = p.first;
if (!label.empty())
if constexpr (is_ppc64v2<E>)
for (i64 i = 0; i < ppc64_save_restore_insns.size(); i++)
if (std::string_view label = ppc64_save_restore_insns[i].first;
!label.empty())
if (Symbol<E> *sym = get_symbol(ctx, label);
sym->file == ctx.internal_obj)
start(sym, ctx.extra.save_restore, offset);
offset += 4;
}
}
start(sym, ctx.extra.save_restore, i * 4);
// __start_ and __stop_ symbols
for (Chunk<E> *chunk : sections) {
@@ -3211,6 +3366,7 @@ void write_separate_debug_file(Context<E> &ctx) {
});
append(ctx.chunks, ctx.debug_chunks);
sort_debug_info_sections(ctx);
// Handle --compress-debug-info
if (ctx.arg.compress_debug_sections != COMPRESS_NONE)
@@ -3390,6 +3546,7 @@ template void create_reloc_sections(Context<E> &);
template void copy_chunks(Context<E> &);
template void construct_relr(Context<E> &);
template void sort_dynsyms(Context<E> &);
template void sort_debug_info_sections(Context<E> &);
template void create_output_symtab(Context<E> &);
template void apply_version_script(Context<E> &);
template void parse_symbol_version(Context<E> &);
-4
View File
@@ -122,10 +122,6 @@ i64 compute_distance(Context<E> &ctx, Symbol<E> &sym,
if (sym.is_absolute())
return INT64_MAX;
// Likewise, relocations against weak undefined symbols won't be relaxed.
if (sym.esym().is_undef_weak())
return INT64_MAX;
// Compute a distance between the relocated place and the symbol.
i64 S = sym.get_addr(ctx);
i64 A = rel.r_addend;
+1 -1
View File
@@ -104,7 +104,7 @@ void process_run_subcommand(Context<E> &ctx, int argc, char **argv) {
setenv("LD_PRELOAD", dso_path.c_str(), 1);
setenv("MOLD_PATH", self.c_str(), 1);
// If ld, ld.lld or ld.gold is specified, run mold itself
// If ld, ld.lld or ld.gold is specified, run mold instead
if (std::string cmd = path_filename(argv[2]);
cmd == "ld" || cmd == "ld.lld" || cmd == "ld.gold") {
std::vector<char *> args;
+117 -68
View File
@@ -20,8 +20,8 @@
// we don't need to try too hard to reduce thunk size to the absolute
// minimum.
#if MOLD_ARM32 || MOLD_ARM64LE || MOLD_ARM64BE || MOLD_PPC32 || \
MOLD_PPC64V1 || MOLD_PPC64V2
#if MOLD_ARM32LE || MOLD_ARM32BE || MOLD_ARM64LE || MOLD_ARM64BE || \
MOLD_PPC32 || MOLD_PPC64V1 || MOLD_PPC64V2
#include "mold.h"
@@ -45,68 +45,67 @@ static constexpr i64 max_thunk_size = 1024 * 1024;
static constexpr i64 thunk_align = 16;
template <typename E>
static bool is_reachable(Context<E> &ctx, bool first_pass, InputSection<E> &isec,
Symbol<E> &sym, const ElfRel<E> &rel) {
// If the target section is in the same output section but
// hasn't got any address yet, that's unreacahble.
InputSection<E> *isec2 = sym.get_input_section();
if (isec2 && isec.output_section == isec2->output_section &&
isec2->offset == -1)
static bool
requires_thunk(Context<E> &ctx, InputSection<E> &isec, const ElfRel<E> &rel,
bool first_pass) {
if (!is_func_call_rel(rel))
return false;
Symbol<E> &sym = *isec.file.symbols[rel.r_sym];
// We don't know about the final file layout on the first pass, so
// we assume pessimistically that all out-of-section relocations are
// out-of-range. Excessive thunks will be removed on the second pass.
if (first_pass) {
// On the first pass, we pessimistically assume that all out-of-section
// relocations are out of range.
InputSection<E> *isec2 = sym.get_input_section();
if (!isec2 || isec.output_section != isec2->output_section)
return false;
return true;
// If the target section is in the same output section but
// hasn't got any address yet, that's unreacahble.
if (isec2->offset == -1)
return true;
// Even if the target is the same section, we branch to its PLT
// if it has one. So a symbol with a PLT is also considered an
// out-of-section reference.
if (sym.has_plt(ctx))
return false;
return true;
}
// Thumb and ARM B instructions cannot be converted to BX, so we
// always have to make them jump to a thunk to switch processor mode
// even if their destinations are reachable.
if constexpr (is_arm32<E>)
if (bool is_thumb = sym.get_addr(ctx) & 1;
(rel.r_type == R_ARM_JUMP24 && is_thumb) ||
(rel.r_type == R_ARM_PLT32 && is_thumb) ||
(rel.r_type == R_ARM_THM_JUMP24 && !is_thumb))
return true;
// On PowerPC, all PLT calls go through range extension thunks.
if constexpr (is_ppc<E>)
if (sym.has_plt(ctx))
return true;
// PowerPC before Power9 lacks PC-relative load/store instructions.
// Functions compiled for Power9 or earlier assume that r2 points to
// GOT+0x8000, while those for Power10 uses r2 as a scratch register.
// We need a thunk to recompute r2 for interworking.
if constexpr (is_ppc64v2<E>)
if ((rel.r_type == R_PPC64_REL24 && !sym.esym().ppc64_preserves_r2()) ||
(rel.r_type == R_PPC64_REL24_NOTOC && sym.esym().ppc64_uses_toc()))
return true;
// Compute a distance between the relocated place and the symbol
// and check if they are within reach.
i64 S = sym.get_addr(ctx, NO_OPD);
i64 A = get_addend(isec, rel);
i64 P = isec.get_addr() + rel.r_offset;
i64 val = S + A - P;
return -branch_distance<E> <= val && val < branch_distance<E>;
}
template <typename E>
static bool needs_shim(Context<E> &ctx, Symbol<E> &sym, const ElfRel<E> &rel) {
// Thumb and ARM B instructions cannot be converted to BX, so we
// always have to make them jump to a thunk to switch processor mode
// even if their destinations are within their ranges.
if constexpr (is_arm32<E>) {
bool is_thumb = sym.get_addr(ctx) & 1;
return (rel.r_type == R_ARM_THM_JUMP24 && !is_thumb) ||
(rel.r_type == R_ARM_JUMP24 && is_thumb) ||
(rel.r_type == R_ARM_PLT32 && is_thumb);
}
// On PowerPC, all PLT calls go through range extension thunks.
if constexpr (is_ppc32<E> || is_ppc64v1<E>)
return sym.has_plt(ctx);
// PowerPC before Power9 lacks PC-relative load/store instructions.
// Functions compiled for Power9 or earlier assume that r2 points to
// GOT+0x8000, while those for Power10 uses r2 as a scratch register.
// We need to a thunk to recompute r2 for interworking.
if constexpr (is_ppc64v2<E>)
return sym.has_plt(ctx) ||
(rel.r_type == R_PPC64_REL24 && !sym.esym().ppc64_preserves_r2()) ||
(rel.r_type == R_PPC64_REL24_NOTOC && sym.esym().ppc64_uses_toc());
return false;
return val < -branch_distance<E> || branch_distance<E> <= val;
}
template <>
void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx,
bool first_pass) {
void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
std::span<InputSection<E> *> m = members;
if (m.empty())
return;
@@ -122,7 +121,7 @@ void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx,
// We manage progress using four offsets which increase monotonically.
// The locations they point to are always A <= B <= C <= D.
//
// Input sections between B and C are in the current batch.
// Input sections between B and C are the current batch.
//
// A is the input section with the smallest address than can reach
// from the current batch.
@@ -181,33 +180,21 @@ void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx,
// Create a new thunk and place it at D.
offset = align_to(offset, thunk_align);
thunks.emplace_back(std::make_unique<Thunk<E>>(*this, offset));
Thunk<E> &thunk = *thunks.back();
std::mutex mu;
// Scan relocations between B and C to collect symbols that need
// entries in the new thunk.
std::mutex mu;
tbb::parallel_for(b, c, [&](i64 i) {
InputSection<E> &isec = *m[i];
for (const ElfRel<E> &rel : isec.get_rels(ctx)) {
if (!is_func_call_rel(rel))
continue;
// Skip if the symbol is undefined. apply_reloc() will report an error.
Symbol<E> &sym = *isec.file.symbols[rel.r_sym];
if (!sym.file)
continue;
// Skip if we can directly branch to the destination.
if (is_reachable(ctx, first_pass, isec, sym, rel) &&
!needs_shim(ctx, sym, rel))
continue;
// Add the symbol to the current thunk if it's not added already.
if (!sym.flags.test_and_set()) {
std::scoped_lock lock(mu);
thunk.symbols.push_back(&sym);
if (requires_thunk(ctx, isec, rel, true)) {
if (Symbol<E> &sym = *isec.file.symbols[rel.r_sym];
!sym.flags.test_and_set()) {
std::scoped_lock lock(mu);
thunk.symbols.push_back(&sym);
}
}
}
});
@@ -226,6 +213,7 @@ void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx,
b = c;
}
// Reset flags for future use
for (; t < thunks.size(); t++)
for (Symbol<E> *sym : thunks[t]->symbols)
sym->flags = 0;
@@ -233,15 +221,75 @@ void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx,
this->shdr.sh_size = offset;
}
// create_range_extension_thunks() creates thunks with a pessimistic
// assumption that all out-of-section references are out of range.
// After computing output section addresses, we revisit all thunks to
// remove unneeded entries from them.
//
// We create more thunks than necessary and then eliminate some of
// them later, instead of just creating thunks at this stage. This is
// because we can safely shrink sections after assigning addresses to
// them without worrying about making existing references to thunks go
// out of range. On the other hand, if we insert thunks after
// assigning addresses to sections, references to thunks could become
// out of range due to the new extra gaps for thunks. Thus, the
// creation of thunks is a two-pass process.
template <>
void remove_redundant_thunks(Context<E> &ctx) {
Timer t(ctx, "remove_redundant_thunks");
set_osec_offsets(ctx);
// Gather output executable sections
std::vector<OutputSection<E> *> sections;
for (Chunk<E> *chunk : ctx.chunks)
if (OutputSection<E> *osec = chunk->to_osec())
if (osec->shdr.sh_flags & SHF_EXECINSTR)
osec->create_range_extension_thunks(ctx, false);
sections.push_back(osec);
// Mark all symbols that actually need range extension thunks
for (OutputSection<E> *osec : sections) {
tbb::parallel_for_each(osec->members, [&](InputSection<E> *isec) {
for (const ElfRel<E> &rel : isec->get_rels(ctx))
if (requires_thunk(ctx, *isec, rel, false))
isec->file.symbols[rel.r_sym]->flags.test_and_set();
});
}
// Remove symbols from thunks if they don't actually need range
// extension thunks
std::vector<Symbol<E> *> syms;
for (OutputSection<E> *osec : sections) {
for (std::unique_ptr<Thunk<E>> &thunk : osec->thunks) {
append(syms, thunk->symbols);
std::erase_if(thunk->symbols, [&](Symbol<E> *sym) { return !sym->flags; });
}
}
// Reset flags for future use
for (Symbol<E> *sym : syms)
sym->flags = 0;
// Recompute section sizes
tbb::parallel_for_each(sections, [&](OutputSection<E> *osec) {
std::span<InputSection<E> *> m = osec->members;
std::span<std::unique_ptr<Thunk<E>>> t = osec->thunks;
i64 offset = 0;
while (!m.empty() || !t.empty()) {
if (!m.empty() && (t.empty() || m[0]->offset < t[0]->offset)) {
offset = align_to(offset, 1 << m[0]->p2align);
m[0]->offset = offset;
offset += m[0]->sh_size;
m = m.subspan(1);
} else {
offset = align_to(offset, thunk_align);
t[0]->offset = offset;
offset += t[0]->size();
t = t.subspan(1);
}
}
osec->shdr.sh_size = offset;
});
}
// When applying relocations, we want to know the address in a reachable
@@ -259,7 +307,8 @@ void gather_thunk_addresses(Context<E> &ctx) {
std::vector<OutputSection<E> *> sections;
for (Chunk<E> *chunk : ctx.chunks)
if (OutputSection<E> *osec = chunk->to_osec())
sections.push_back(osec);
if (osec->shdr.sh_flags & SHF_EXECINSTR)
sections.push_back(osec);
ranges::stable_sort(sections, {}, [](OutputSection<E> *x) {
return x->shdr.sh_addr;
+1 -1
View File
@@ -162,7 +162,7 @@ u64 get_tp_addr(const ElfPhdr<E> &phdr) {
// and %a0/%a1 on s390x) refers to past the end of the TLS block for
// historical reasons. TLVs are accessed with negative offsets from TP.
return align_to(phdr.p_vaddr + phdr.p_memsz, phdr.p_align);
} else if constexpr (is_arm<E> || is_sh4<E> || is_arc<E>) {
} else if constexpr (is_arm<E> || is_sh4<E>) {
// On ARM and SH4, the runtime appends two words at the beginning
// of TLV template image when copying TLVs to the TLS block, so we need
// to offset it.
+8 -8
View File
@@ -30,8 +30,8 @@ endif()
if(MOLD_ENABLE_QEMU_TESTS)
list(APPEND QEMU_ARCHS
x86_64 i386 arm aarch64 aarch64_be ppc ppc64 ppc64le sparc64 sh4 sh4eb
s390x riscv64 riscv32 m68k loongarch64 arc)
x86_64 i386 arm armeb aarch64 aarch64_be ppc ppc64 ppc64le sparc64
sh4 sh4eb s390x riscv64 riscv32 m68k loongarch64)
LIST(APPEND TRIPLES
x86_64-linux-gnu
@@ -39,6 +39,7 @@ if(MOLD_ENABLE_QEMU_TESTS)
aarch64-linux-gnu
aarch64_be-linux-gnu
arm-linux-gnueabihf
armeb-linux-gnueabihf
riscv64-linux-gnu
powerpc-linux-gnu
powerpc64-linux-gnu
@@ -49,8 +50,7 @@ if(MOLD_ENABLE_QEMU_TESTS)
sh4aeb-linux-gnu
riscv32-linux-gnu
m68k-linux-gnu
loongarch64-linux-gnu
arc-linux-gnu)
loongarch64-linux-gnu)
foreach(ARCH IN LISTS QEMU_ARCHS)
find_program(HAS_qemu-${ARCH} qemu-${ARCH})
@@ -122,6 +122,10 @@ if(${MACHINE} STREQUAL "arm" OR (HAS_qemu-arm AND HAS_arm-linux-gnueabihf-gcc))
add_target(arm arm-linux-gnueabihf)
endif()
if(${MACHINE} STREQUAL "armeb" OR (HAS_qemu-armeb AND HAS_armeb-linux-gnueabihf-gcc))
add_target(armeb armeb-linux-gnueabihf)
endif()
if(${MACHINE} STREQUAL "riscv64" OR (HAS_qemu-riscv64 AND HAS_riscv64-linux-gnu-gcc))
add_target(riscv64 riscv64-linux-gnu)
endif()
@@ -185,7 +189,3 @@ endif()
if(${MACHINE} STREQUAL "loongarch64" OR (HAS_qemu-loongarch64 AND HAS_loongarch64-linux-gnu-gcc))
add_target(loongarch64 loongarch64-linux-gnu)
endif()
if(${MACHINE} STREQUAL "arc" OR (HAS_qemu-loongarch64 AND HAS_arc-linux-gnu-gcc))
add_target(arc arc-linux-gnu)
endif()
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc -
int main() {}
EOF
not $CC -B. -o $t/exe $t/a.o -mbe32 |& grep 'be32 is not supported'
+18 -23
View File
@@ -1,33 +1,28 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -c -fPIC -o $t/a.o -xc -
int foo() {
return 0;
}
cat <<EOF | $CC -o $t/libfoo.so -shared -fPIC -Wl,-soname,libfoo.so -xc -
int fn1() { return 42; }
EOF
cat <<EOF | $CC -c -fPIC -o $t/b.o -xc -
__attribute__((weak)) int foo();
int bar() {
if (foo) return foo();
return 0;
}
cat <<EOF | $CC -o $t/libbar.so -shared -fPIC -Wl,-soname,libbar.so -xc - -L$t -lfoo
int fn1();
int fn2() { return fn1(); }
EOF
cat <<EOF | $CC -xc -c -o $t/c.o -
int bar();
int main() {
return bar();
}
cat <<EOF | $CC -o $t/libbaz.so -shared -fPIC -Wl,-soname,libbaz.so -xc - -L$t -lbar
int fn2();
int fn3() { return fn2(); }
EOF
$CC -B. -shared -o $t/libfoo.so $t/a.o
$CC -B. -shared -o $t/libbar.so $t/b.o
$CC -B. -o $t/exe $t/c.o -L$t -Wl,--as-needed -lfoo -lbar
cat <<EOF | $CC -o $t/a.o -c -xc -
int fn3();
int main() { fn3(); }
EOF
readelf --dynamic $t/exe > $t/log
not grep libfoo.so $t/log
grep libbar.so $t/log
$CC -B. -o $t/exe $t/a.o -L$t -Wl,--as-needed -lbaz -lbar
readelf -W --dynamic $t/exe > $t/log2
not grep libfoo $t/log2
not grep libbar $t/log2
grep libbaz $t/log2
+3 -2
View File
@@ -7,6 +7,7 @@ canonical_name() {
case $1 in
i?86) echo i686 ;;
amd64) echo x86_64 ;;
armeb*) echo armeb ;;
arm*) echo arm ;;
powerpc) echo ppc ;;
powerpc64) echo ppc64 ;;
@@ -64,7 +65,7 @@ t=$TESTDIR/$testname
mkdir -p $t
case $MACHINE in
x86_64 | i686 | arm)
x86_64 | i686 | arm | armeb)
tlsdesc_opt=-mtls-dialect=gnu2 ;;
aarch64 | riscv* | loongarch*)
tlsdesc_opt=-mtls-dialect=desc ;;
@@ -101,7 +102,7 @@ supports_ifunc() {
supports_tlsdesc() {
# musl's tlsdesc on arm32 seems to be broken
[ $MACHINE = arm ] && is_musl && return 1
[[ $MACHINE = arm* ]] && is_musl && return 1
# FreeBSD's loader doesn't seem to support TLSDESC relocs in an executable
[ "$(uname)" = FreeBSD ] && return 1
+1 -1
View File
@@ -2,7 +2,7 @@
. $(dirname $0)/common.inc
# arm-linux-gnueabihf-objcopy crashes on x86-64
[ $MACHINE = arm ] && skip
[[ $MACHINE = arm* ]] && skip
[ $MACHINE = riscv32 ] && skip
command -v zstdcat >& /dev/null || skip
+1 -1
View File
@@ -2,7 +2,7 @@
. $(dirname $0)/common.inc
# Currently, CREL is not supported on REL-type targets
[ $MACHINE = arm ] && skip
[[ $MACHINE = arm* ]] && skip
[ $MACHINE = i686 ] && skip
[ "$CC" = cc ] || skip
+3 -3
View File
@@ -6,16 +6,16 @@ cat <<EOF > $t/a.h
#define B 99
EOF
cat <<EOF | $GCC -o $t/b.o -c -xc - -I$t -g3
cat <<EOF | $CC -o $t/b.o -c -xc - -I$t -g3
#include "a.h"
extern int z();
int main () { return z() - 122; }
EOF
cat <<EOF | $GCC -o $t/c.o -c -xc - -I$t -g3
cat <<EOF | $CC -o $t/c.o -c -xc - -I$t -g3
#include "a.h"
int z() { return A + B; }
EOF
$GCC -B. -o $t/exe $t/b.o $t/c.o
$CC -B. -o $t/exe $t/b.o $t/c.o
$OBJDUMP --dwarf=macro $t/exe | not grep 'DW_MACRO_import -.* 0x0$'
+11
View File
@@ -0,0 +1,11 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc -
void foo() {}
EOF
echo '{ global: foo; local: *; };' > $t/b.ver
$CC -B. -o $t/c.so -shared $t/a.o -Wl,--default-symver -Wl,--version-script=$t/b.ver
readelf --dyn-syms $t/c.so | grep -F ' foo@@c.so'
+2 -2
View File
@@ -22,8 +22,8 @@ void bar();
int main() { bar(); }
EOF
$CC -B. -o $t/exe1 $t/c.o $t/b.so
$CC -B. -o $t/exe1 $t/c.o $t/b.so -Wl,--allow-shlib-undefined
readelf --dynamic $t/exe1 | grep 'Dependency audit library:..*/a.so'
$CC -B. -o $t/exe2 $t/c.o $t/b.so -Wl,--depaudit=foo
$CC -B. -o $t/exe2 $t/c.o $t/b.so -Wl,--depaudit=foo -Wl,--allow-shlib-undefined
readelf --dynamic $t/exe2 | grep 'Dependency audit library:..*foo:.*/a.so'
+1 -1
View File
@@ -6,7 +6,7 @@
# musl libc does not support init/fini on ARM
# https://github.com/rui314/mold/issues/951
[ $MACHINE = arm -o $MACHINE = aarch64 ] && is_musl && skip
[[ $MACHINE = arm* -o $MACHINE = aarch64 ]] && is_musl && skip
cat <<EOF | $CC -c -fPIC -o $t/a.o -xc -
void keep();
+7 -4
View File
@@ -6,8 +6,11 @@ cat <<EOF | $CC -c -o $t/a.o -x assembler -
_start:
EOF
./mold -o $t/exe $t/a.o
readelf --sections $t/exe | not grep -F .interp
./mold -o $t/exe1 $t/a.o
readelf -WS $t/exe1 | not grep -F .interp
./mold -o $t/exe $t/a.o --dynamic-linker=/foo/bar
readelf --sections $t/exe | grep -F .interp
./mold -o $t/exe2 $t/a.o --dynamic-linker=/foo/bar
readelf -WS $t/exe2 | grep -F .interp
./mold -o $t/exe3 $t/a.o --dynamic-linker=/foo/bar -static
readelf -WS $t/exe3 | not grep -F .interp
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = arm ] && skip
[[ $MACHINE = arm* ]] && skip
cat <<EOF | $CXX -o $t/a.o -c -fPIC -xc++ -
#include <cstdio>
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $GCC -c -o $t/a.o -xc -
cat <<EOF | $CC -c -o $t/a.o -xc -
#include <stdio.h>
__attribute__((section(".gnu.warning.foo")))
+1 -1
View File
@@ -2,7 +2,7 @@
. $(dirname $0)/common.inc
# ARM32's strip command crashes on the output of this test for some reason.
[ $MACHINE = arm ] && skip
[[ $MACHINE = arm* ]] && skip
strip=$STRIP
command -v $strip >& /dev/null || skip
+1 -1
View File
@@ -9,4 +9,4 @@ echo 'int main() {}' | clang -c -o $t/a.o -xc -
echo 'void foo() {}' | clang -c -o $t/b.o -xc - -flto
not ./mold -o /dev/null $t/a.o $t/b.o |&
grep "b.o: don't know how to handle this LTO object file"
grep "b.o: unable to handle this LTO object file"
+26
View File
@@ -0,0 +1,26 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ "$(uname)" = FreeBSD ] && skip
cat <<EOF | $CC -B. -shared -fPIC -o $t/libfoo.so -xc -
void foo() {}
EOF
cat <<EOF | $CC -B. -shared -fPIC -o $t/libbar.so -xc - -L$t -lfoo
void foo();
void bar() { foo(); }
EOF
cat <<EOF | $CC -B. -shared -fPIC -o $t/libfoo.so -xc - -L$t -lbar
void baz();
void foo() { baz(); }
EOF
cat <<EOF | $CC -c -o $t/a.o -c -xc -
int bar();
int main() { bar(); }
EOF
not $CC -B. -o $t/exe1 $t/a.o -Wl,--no-allow-shlib-undefined -L$t -lfoo -lbar |&
grep 'libfoo.so: --no-allow-shlib-undefined: undefined symbol: baz'
+2
View File
@@ -1,6 +1,8 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ "$(uname)" = FreeBSD ] && skip
cat <<EOF | $CC -B. -shared -fPIC -o $t/libfoo.so -xc -
void foo() {}
EOF
+2
View File
@@ -1,6 +1,8 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ "$(uname)" = FreeBSD ] && skip
cat <<EOF | $CC -B. -shared -fPIC -o $t/libfoo.so -xc -
void foo() {}
EOF
+31
View File
@@ -0,0 +1,31 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ "$(uname)" = FreeBSD ] && skip
cat <<EOF | $CC -B. -shared -fPIC -o $t/libfoo.so -xc -
void foo() {}
EOF
cat <<EOF | $CC -B. -shared -fPIC -o $t/libbar.so -xc - -L$t -lfoo
void foo();
void bar() { foo(); }
EOF
cat <<EOF | $CC -B. -shared -fPIC -o $t/libbaz.so -xc - -L$t -lbar
void bar();
void baz() { bar(); }
EOF
cat <<EOF | $CC -c -o $t/a.o -c -xc -
int baz();
int main() { baz(); }
EOF
mv $t/libfoo.so $t/libfoo.so.bak
echo | $CC -B. -shared -fPIC -o $t/libfoo.so -xc -
$CC -B. -o $t/exe1 $t/a.o -Wl,--no-allow-shlib-undefined -L$t -lbar -lbaz
not $CC -B. -o $t/exe2 $t/a.o -Wl,--no-allow-shlib-undefined \
-L$t -lfoo -lbar -lbaz |& grep 'no-allow-shlib-undefined: undefined symbol: foo'
+31
View File
@@ -0,0 +1,31 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ "$(uname)" = FreeBSD ] && skip
cat <<EOF | $CC -o $t/a.o -c -xc - -fPIC
void f1() {}
EOF
cat <<EOF | $CC -o $t/b.o -c -xc - -fPIC
void f1();
void f2() { f1(); }
EOF
cat <<EOF | $CC -o $t/c.o -c -xc - -fPIC
void f1();
void f3() { f1(); }
EOF
$CC -B. -o $t/libfoo.so -shared $t/a.o
$CC -B. -o $t/libbar.so -shared $t/b.o -L$t -lfoo
$CC -B. -o $t/libbaz.so -shared $t/c.o
cat <<EOF | $CC -o $t/d.o -c -xc - -fPIC
void f2();
void f3();
int main() { f2(); f3(); }
EOF
$CC -B. -o $t/exe $t/d.o -L$t -lbar -lbaz
+1 -1
View File
@@ -3,10 +3,10 @@
[ $MACHINE = i686 ] && skip
[ $MACHINE = m68k ] && skip
[ $MACHINE = arm ] && skip
[ $MACHINE = ppc64 ] && skip
[ $MACHINE = ppc64le ] && skip
[ $MACHINE = sh4 ] && skip
[[ $MACHINE = arm* ]] && skip
[[ $MACHINE = loongarch* ]] && skip
cat <<EOF | $CC -shared -o $t/a.so -xc -
Binary file not shown.
+2 -2
View File
@@ -6,12 +6,12 @@ nm mold | grep '__tsan_init' && skip
test_cflags -g3 -gz || skip
cat <<EOF | $GCC -c -o $t/a.o -xc - -g3 -gz
cat <<EOF | $CC -c -o $t/a.o -xc - -g3 -gz
#include <stdio.h>
void hello() { printf("Hello world\n"); }
EOF
cat <<EOF | $GCC -c -o $t/b.o -xc - -g3 -gz
cat <<EOF | $CC -c -o $t/b.o -xc - -g3 -gz
void hello();
int main() { hello(); }
EOF
+1 -1
View File
@@ -3,7 +3,7 @@
# ARM assembler has a differnet grammar than the others.
# Concretely speaking, ARM as uses "@" as a start of a comment.
[ $MACHINE = arm ] && skip
[[ $MACHINE = arm* ]] && skip
# All data symbols need to be aligned to 2 byte boundaries on s390x,
# so rodata.str1.1 in this file is invalid on s390x.
+27
View File
@@ -0,0 +1,27 @@
#!/bin/bash
. $(dirname $0)/common.inc
nm mold | grep '__tsan_init' && skip
command -v flock >& /dev/null || skip
cat <<EOF > $t/a.c
int x = 1;
void foo() {}
EOF
$CC -o $t/a.o -c -g -gdwarf32 $t/a.c || skip
cat <<EOF > $t/b.c
int y = 1;
int main() {}
EOF
$CC -o $t/b.o -c -g -gdwarf64 $t/b.c
MOLD_DEBUG=1 $CC -B. -o $t/exe $t/a.o $t/b.o -g -Wl,--separate-debug-file
flock $t/exe.dbg true
readelf -p .debug_line_str $t/exe.dbg > $t/str
if grep -Fw a.c $t/str; then
grep -A10 -Fw a.c $t/str | grep -Fw b.c
fi
+3 -3
View File
@@ -17,7 +17,7 @@ $CC -c -o $t/a.o $t/a.c -g
$CC -B. -o $t/exe1 $t/a.o -Wl,--separate-debug-file
readelf -SW $t/exe1 | grep -F .gnu_debuglink
flock $t/exe1 true
flock $t/exe1.dbg true
gdb $t/exe1 -ex 'list main' -ex 'quit' | grep -F printf
@@ -25,7 +25,7 @@ $CC -c -o $t/a.o $t/a.c -g
$CC -B. -o $t/exe2 $t/a.o -Wl,--separate-debug-file,--no-build-id
readelf -SW $t/exe2 | grep -F .gnu_debuglink
flock $t/exe2 true
flock $t/exe2.dbg true
gdb $t/exe2 -ex 'list main' -ex 'quit' | grep -F printf
@@ -33,6 +33,6 @@ $CC -c -o $t/a.o $t/a.c -g
$CC -B. -o $t/exe3 $t/a.o -Wl,--separate-debug-file,--compress-debug-sections=zlib
readelf -SW $t/exe3 | grep -F .gnu_debuglink
flock $t/exe3 true
flock $t/exe3.dbg true
readelf -W --sections $t/exe3.dbg | grep '\.debug_info .*C'
gdb $t/exe3 -ex 'list main' -ex 'quit' | grep -F printf
+17
View File
@@ -0,0 +1,17 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc - -g -gdwarf32 -Wl,--compress-debug-sections=zlib || skip
void foo() {}
EOF
cat <<EOF | $CC -o $t/b.o -c -xc - -g -gdwarf64 -Wl,--compress-debug-sections=zstd || skip
int main() {}
EOF
# Test if DWARF32 precedes DWARF64 in the output .debug_info
MOLD_DEBUG=1 $CC -B. -o $t/exe1 $t/a.o $t/b.o -g -Wl,-Map=$t/map1
grep -A10 -F '/a.o:(.debug_info)' $t/map1 | grep -F '/b.o:(.debug_info)'
MOLD_DEBUG=1 $CC -B. -o $t/exe2 $t/b.o $t/a.o -g -Wl,-Map=$t/map2
grep -A10 -F '/a.o:(.debug_info)' $t/map2 | grep -F '/b.o:(.debug_info)'
+24
View File
@@ -0,0 +1,24 @@
#!/bin/bash
. $(dirname $0)/common.inc
nm mold | grep '__tsan_init' && skip
cat <<EOF | $CC -o $t/a.o -c -xc - -g -gdwarf64 || skip
void foo() {}
EOF
cat <<EOF | $CC -o $t/b.o -c -xc - -g -gdwarf32
void bar() {}
EOF
./mold -r -o $t/c.o $t/a.o $t/b.o
cat <<EOF | $CC -o $t/d.o -c -xc - -g -gdwarf64
int main() {}
EOF
MOLD_DEBUG=1 $CC -B. -o $t/exe1 $t/c.o $t/d.o -g -Wl,-Map=$t/map1
grep -A10 -F '/c.o:(.debug_info)' $t/map1 | grep -F '/d.o:(.debug_info)'
MOLD_DEBUG=1 $CC -B. -o $t/exe2 $t/d.o $t/c.o -g -Wl,-Map=$t/map2
grep -A10 -F '/c.o:(.debug_info)' $t/map2 | grep -F '/d.o:(.debug_info)'
+41
View File
@@ -0,0 +1,41 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF > $t/a.c
int x = 1;
void foo() {}
EOF
$CC -o $t/a.o -c -g -gdwarf32 $t/a.c || skip
cat <<EOF > $t/b.c
int y = 1;
int main() {}
EOF
$CC -o $t/b.o -c -g -gdwarf64 $t/b.c
# Test if DWARF32 precedes DWARF64 in the output .debug_info
MOLD_DEBUG=1 $CC -B. -o $t/exe1 $t/a.o $t/b.o -g -Wl,-Map=$t/map1
grep -A10 -F '/a.o:(.debug_info)' $t/map1 | grep -F '/b.o:(.debug_info)'
readelf -p .debug_line_str $t/exe1 > $t/str1
if grep -Fw a.c $t/str1; then
grep -A10 -Fw a.c $t/str1 | grep -Fw b.c
fi
MOLD_DEBUG=1 $CC -B. -o $t/exe2 $t/b.o $t/a.o -g -Wl,-Map=$t/map2
grep -A10 -F '/a.o:(.debug_info)' $t/map2 | grep -F '/b.o:(.debug_info)'
readelf -p .debug_line_str $t/exe2 > $t/str2
if grep -Fw a.c $t/str2; then
grep -A10 -Fw a.c $t/str2 | grep -Fw b.c
fi
if [ -z "$QEMU" ] && command -v gdb; then
gdb $t/exe1 -ex 'list main' -ex 'quit' | grep 'int y = 1'
gdb $t/exe2 -ex 'list main' -ex 'quit' | grep 'int y = 1'
fi
+26
View File
@@ -0,0 +1,26 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc - -fPIC
void foo() {}
EOF
cat <<EOF | $CC -o $t/b.o -c -xc - -fPIC
__asm__(".symver foo_,foo@@VERSION");
void foo_() {}
EOF
cat <<EOF > $t/c.version
VERSION { global: foo; local: *; };
EOF
$CC -B. -shared -o $t/libfoo.so $t/a.o
$CC -B. -shared -o $t/libbar.so $t/b.o -Wl,--version-script=$t/c.version
cat <<EOF | $CC -o $t/d.o -c -xc -
void foo();
int main() { foo(); }
__asm__(".symver foo,foo@VERSION");
EOF
$CC -B. -o $t/exe $t/d.o -L$t -lfoo -lbar
+1
View File
@@ -1,6 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = armeb ] && skip
[[ $MACHINE = loongarch* ]] && skip
cat <<EOF | $CC -o $t/a.o -c -xc -
+1
View File
@@ -1,6 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = armeb ] && skip
[[ $MACHINE = loongarch* ]] && skip
cat <<EOF | $CC -c -o $t/a.o -xc -
+1
View File
@@ -1,6 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = armeb ] && skip
[[ $MACHINE = loongarch* ]] && skip
mkdir -p $t/bin $t/sysroot/foo
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $GCC -o $t/a.o -c -xassembler -
cat <<EOF | $CC -o $t/a.o -c -xassembler - || skip
.globl foo
.tls_common foo,4,4
EOF
+2 -2
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $GCC -fPIC -c -o $t/a.o -xc -
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
static _Thread_local int foo[10000] = { 3, [9999] = 5 };
int get_foo(int idx) { return foo[idx]; }
@@ -9,7 +9,7 @@ EOF
$CC -B. -shared -o $t/b.so $t/a.o
cat <<EOF | $GCC -fPIC -c -o $t/c.o -xc -
cat <<EOF | $CC -fPIC -c -o $t/c.o -xc -
#include <assert.h>
#include <dlfcn.h>
#include <stdio.h>
+3 -3
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $GCC -fPIC -fno-plt -c -o $t/a.o -xc -
cat <<EOF | $CC -fPIC -fno-plt -c -o $t/a.o -xc -
#include <stdio.h>
__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1;
@@ -19,13 +19,13 @@ int main() {
}
EOF
cat <<EOF | $GCC -fPIC -fno-plt -c -o $t/b.o -xc -
cat <<EOF | $CC -fPIC -fno-plt -c -o $t/b.o -xc -
__attribute__((tls_model("global-dynamic"))) _Thread_local int x3 = 3;
__attribute__((tls_model("global-dynamic"))) static _Thread_local int x5 = 5;
int get_x5() { return x5; }
EOF
cat <<EOF | $GCC -fPIC -fno-plt -c -o $t/c.o -xc -
cat <<EOF | $CC -fPIC -fno-plt -c -o $t/c.o -xc -
__attribute__((tls_model("global-dynamic"))) _Thread_local int x4 = 4;
__attribute__((tls_model("global-dynamic"))) static _Thread_local int x6 = 6;
int get_x6() { return x6; }
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $GCC -fPIC -c -o $t/a.o -xc -
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
#include <stdio.h>
__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1;
+3 -3
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $GCC -fPIC -c -o $t/a.o -xc -
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
#include <stdio.h>
__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1;
@@ -20,13 +20,13 @@ int main() {
}
EOF
cat <<EOF | $GCC -fPIC -c -o $t/b.o -xc -
cat <<EOF | $CC -fPIC -c -o $t/b.o -xc -
__attribute__((tls_model("global-dynamic"))) _Thread_local int x3 = 3;
__attribute__((tls_model("global-dynamic"))) static _Thread_local int x5 = 5;
int get_x5() { return x5; }
EOF
cat <<EOF | $GCC -fPIC -c -o $t/c.o -xc -
cat <<EOF | $CC -fPIC -c -o $t/c.o -xc -
__attribute__((tls_model("global-dynamic"))) _Thread_local int x4 = 4;
__attribute__((tls_model("global-dynamic"))) static _Thread_local int x6 = 6;
int get_x6() { return x6; }
+2 -2
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $GCC -fPIC -c -o $t/a.o -xc -
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
#include <stdio.h>
__attribute__((tls_model("initial-exec"))) static _Thread_local int foo;
@@ -19,7 +19,7 @@ EOF
$CC -B. -shared -o $t/b.so $t/a.o
cat <<EOF | $GCC -c -o $t/c.o -xc - -fPIC
cat <<EOF | $CC -c -o $t/c.o -xc - -fPIC
#include <stdio.h>
_Thread_local int baz;
+3 -3
View File
@@ -18,14 +18,14 @@ extern _Thread_local int x;
extern _Thread_local int y[];
int main() {
printf("%d %d %d %d\n", x, y[0], y[1], y[2]);
printf("%lu %d %d %d %d\n", (unsigned long)&x % 256, x, y[0], y[1], y[2]);
}
EOF
$CC -B. -shared -o $t/d.so $t/a.o $t/b.o
$CC -B. -o $t/exe1 $t/a.o $t/b.o $t/c.o
$QEMU $t/exe1 | grep '^42 1 2 3$'
$QEMU $t/exe1 | grep '^0 42 1 2 3$'
$CC -B. -o $t/exe2 $t/c.o $t/d.so
$QEMU $t/exe2 | grep '^42 1 2 3$'
$QEMU $t/exe2 | grep '^0 42 1 2 3$'
+2 -2
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $GCC -fPIC -ftls-model=local-dynamic -fno-plt -c -o $t/a.o -xc -
cat <<EOF | $CC -fPIC -ftls-model=local-dynamic -fno-plt -c -o $t/a.o -xc -
#include <stdio.h>
extern _Thread_local int foo;
@@ -18,7 +18,7 @@ int main() {
}
EOF
cat <<EOF | $GCC -fPIC -ftls-model=local-dynamic -fno-plt -c -o $t/b.o -xc -
cat <<EOF | $CC -fPIC -ftls-model=local-dynamic -fno-plt -c -o $t/b.o -xc -
_Thread_local int foo = 3;
EOF
+1 -1
View File
@@ -18,7 +18,7 @@ int main() {
}
EOF
cat <<EOF | $GCC -fPIC -ftls-model=local-dynamic -c -o $t/b.o -xc -
cat <<EOF | $CC -fPIC -ftls-model=local-dynamic -c -o $t/b.o -xc -
_Thread_local int foo = 3;
EOF
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $GCC -fPIC -c -o $t/a.o -xc -
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
__attribute__((tls_model("local-exec"))) static _Thread_local int foo = 5;
int bar() { return foo; }
EOF
+2 -2
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $GCC -fPIC -c -o $t/a.o -xc -
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
#include <stdio.h>
__attribute__((tls_model("local-exec"))) extern _Thread_local int foo;
@@ -18,7 +18,7 @@ int main() {
}
EOF
cat <<EOF | $GCC -fPIC -c -o $t/b.o -xc -
cat <<EOF | $CC -fPIC -c -o $t/b.o -xc -
__attribute__((tls_model("local-exec"))) _Thread_local int foo = 3;
EOF
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $GCC -c -o $t/a.o -xc -
cat <<EOF | $CC -c -o $t/a.o -xc -
#include <stdio.h>
__attribute__((tls_model("global-dynamic"))) extern _Thread_local int foo;
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $GCC -fPIC -c -o $t/a.o -xc -
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
#include <stdio.h>
__attribute__((tls_model("global-dynamic"))) extern _Thread_local int foo;
+2 -2
View File
@@ -3,7 +3,7 @@
supports_tlsdesc || skip
cat <<EOF | $GCC -fPIC -c -o $t/a.o -xc - $tlsdesc_opt
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc - $tlsdesc_opt
static _Thread_local int foo[10000] = { 3, [9999] = 5 };
int get_foo(int idx) { return foo[idx]; }
@@ -11,7 +11,7 @@ EOF
$CC -B. -shared -o $t/b.so $t/a.o
cat <<EOF | $GCC -fPIC -c -o $t/c.o -xc - $tlsdesc_opt
cat <<EOF | $CC -fPIC -c -o $t/c.o -xc - $tlsdesc_opt
#include <assert.h>
#include <dlfcn.h>
#include <stdio.h>
+2 -2
View File
@@ -3,7 +3,7 @@
supports_tlsdesc || skip
cat <<EOF | $GCC -fPIC -c -o $t/a.o -xc - $tlsdesc_opt
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc - $tlsdesc_opt
#include <stdio.h>
extern _Thread_local int foo;
@@ -15,7 +15,7 @@ int main() {
}
EOF
cat <<EOF | $GCC -fPIC -shared -o $t/b.so -xc - $tlsdesc_opt
cat <<EOF | $CC -fPIC -shared -o $t/b.so -xc - $tlsdesc_opt
_Thread_local int foo = 5;
_Thread_local int bar;
EOF
+3 -3
View File
@@ -3,18 +3,18 @@
supports_tlsdesc || skip
cat <<EOF | $GCC -fPIC -fPIC -c -o $t/a.o -xc - $tlsdesc_opt
cat <<EOF | $CC -fPIC -fPIC -c -o $t/a.o -xc - $tlsdesc_opt
_Thread_local int foo = 5;
EOF
$CC -B. -shared -o $t/b.so $t/a.o
cat <<EOF | $GCC -fPIC -fPIC -c -o $t/c.o -xc - $tlsdesc_opt
cat <<EOF | $CC -fPIC -fPIC -c -o $t/c.o -xc - $tlsdesc_opt
extern _Thread_local int foo;
int get_foo1() { return foo; }
EOF
cat <<EOF | $GCC -fPIC -fPIE -c -o $t/d.o -xc - $tlsdesc_opt
cat <<EOF | $CC -fPIC -fPIE -c -o $t/d.o -xc - $tlsdesc_opt
#include <stdio.h>
extern _Thread_local int foo;
+2 -2
View File
@@ -3,7 +3,7 @@
supports_tlsdesc || skip
cat <<EOF | $GCC -fPIC -ftls-model=local-dynamic -c -o $t/a.o -xc - $tlsdesc_opt
cat <<EOF | $CC -fPIC -ftls-model=local-dynamic -c -o $t/a.o -xc - $tlsdesc_opt
extern _Thread_local int foo;
int get_foo() {
@@ -17,7 +17,7 @@ int get_bar() {
}
EOF
cat <<EOF | $GCC -fPIC -ftls-model=local-dynamic -c -o $t/b.o -xc - $tlsdesc_opt
cat <<EOF | $CC -fPIC -ftls-model=local-dynamic -c -o $t/b.o -xc - $tlsdesc_opt
#include <stdio.h>
_Thread_local int foo;
+2 -2
View File
@@ -4,7 +4,7 @@
supports_tlsdesc || skip
test_cflags -static || skip
cat <<EOF | $GCC -fPIC -c -o $t/a.o -xc - $tlsdesc_opt
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc - $tlsdesc_opt
#include <stdio.h>
extern _Thread_local int foo;
@@ -15,7 +15,7 @@ int main() {
}
EOF
cat <<EOF | $GCC -fPIC -c -o $t/b.o -xc - $tlsdesc_opt
cat <<EOF | $CC -fPIC -c -o $t/b.o -xc - $tlsdesc_opt
_Thread_local int foo;
EOF
+2 -2
View File
@@ -3,7 +3,7 @@
supports_tlsdesc || skip
cat <<EOF | $GCC -fPIC -c -o $t/a.o -xc - $tlsdesc_opt
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc - $tlsdesc_opt
extern _Thread_local int foo;
_Thread_local int bar = 3;
@@ -18,7 +18,7 @@ int get_baz() {
}
EOF
cat <<EOF | $GCC -fPIC -c -o $t/b.o -xc - $tlsdesc_opt
cat <<EOF | $CC -fPIC -c -o $t/b.o -xc - $tlsdesc_opt
#include <stdio.h>
_Thread_local int foo;
+1 -2
View File
@@ -9,5 +9,4 @@ int main() {
}
EOF
$CC -B. -shared -o $t/a.so $t/a.o |&
grep 'warning: symbol type mismatch: times'
$CC -B. -shared -o $t/b.so $t/a.o |& grep 'warning: symbol type mismatch: times'
+14
View File
@@ -0,0 +1,14 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc - -fno-PIE
#include <stdio.h>
__attribute__((weak)) extern int foo;
int main() { printf("%p\n", &foo); }
EOF
if $CC -B. -o $t/exe $t/a.o -no-pie -Wl,-z,dynamic-undefined-weak >& $t/log; then
$QEMU $t/exe | grep -F '(nil)'
else
grep 'recompile with -fPIE or -fPIC' $t/log
fi
+6
View File
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
+3 -4
View File
@@ -1,4 +1,4 @@
# Copyright (c) 2023 Intel Corporation
# Copyright (c) 2023-2024 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -13,6 +13,5 @@
# limitations under the License.
allocator:
- 'src/tbbmalloc/**/*'
- 'src/tbbmalloc_proxy/**/*'
- 'test/tbbmalloc/**/*'
- changed-files:
- any-glob-to-any-file: ['src/tbbmalloc/**/*', 'src/tbbmalloc_proxy/**/*', 'test/tbbmalloc/**/*']
+4 -4
View File
@@ -75,7 +75,7 @@ jobs:
export BUILD_TYPE=${BUILD_TYPE} && sphinx-build doc html
tar -czvf html.tar.gz html/
- name: Save docs
uses: actions/upload-artifact@v2.2.1
uses: actions/upload-artifact@v4
with:
name: oneTBB-html-docs-${{ env.GITHUB_SHA_SHORT }}
path: html.tar.gz
@@ -97,7 +97,7 @@ jobs:
- name: Set env
run: echo GITHUB_SHA_SHORT=${GITHUB_SHA::8} >> $GITHUB_ENV
- name: Download documetation
uses: actions/download-artifact@v2
uses: actions/download-artifact@v4
with:
name: oneTBB-html-docs-${{ env.GITHUB_SHA_SHORT }}
- name: Publish to github pages
@@ -198,7 +198,7 @@ jobs:
fail-fast: false
matrix:
include:
- os: macos-12
- os: macos-14
c_compiler: clang
cxx_compiler: clang++
std: 14
@@ -314,7 +314,7 @@ jobs:
fail-fast: false
matrix:
include:
- os: macos-12
- os: macos-15
c_compiler: clang
cxx_compiler: clang++
std: 14
+2 -2
View File
@@ -1,4 +1,4 @@
# Copyright (c) 2024 Intel Corporation
# Copyright (c) 2024-2025 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -46,7 +46,7 @@ jobs:
steps:
- name: Harden Runner
uses: step-security/harden-runner@v2.6.1
uses: step-security/harden-runner@v2.10.4
with:
egress-policy: audit
+62
View File
@@ -0,0 +1,62 @@
# Copyright (c) 2024-2025 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: Coverity Scan
on:
# Only run on push to master branch
push:
branches: [master]
permissions: read-all
env:
BUILD_CONCURRENCY: 4
COVERITY_PROJECT: oneapi-src%2FoneTBB
jobs:
coverity_linux:
name: Coverity Linux
if: github.repository == 'uxlfoundation/oneTBB'
runs-on: [ubuntu-latest]
steps:
- uses: actions/checkout@v4
- name: Download Linux 64 Coverity Tool
run: |
curl https://scan.coverity.com/download/cxx/linux64 --output ${GITHUB_WORKSPACE}/cov-linux64-tool.tar.gz \
--data "token=${{secrets.COVERITY_TOKEN}}&project=${{env.COVERITY_PROJECT}}"
mkdir cov-linux64-tool
tar -xzf cov-linux64-tool.tar.gz --strip 1 -C cov-linux64-tool
- name: Build with cov-build
run: |
export PATH="${PWD}/cov-linux64-tool/bin:${PATH}"
mkdir build && cd build
cmake -DCMAKE_CXX_STANDARD=20 -DCMAKE_BUILD_TYPE=relwithdebinfo \
-DCMAKE_CXX_COMPILER=g++ -DCMAKE_C_COMPILER=gcc -DTBB_CPF=ON ..
cov-build --dir cov-int make VERBOSE=1 -j${{env.BUILD_CONCURRENCY}}
- name: Archive Coverity build results
run: |
cd build
tar -czvf cov-int.tgz cov-int
- name: Submit Coverity results for analysis
run: |
cd build
curl \
--form token="${{ secrets.COVERITY_TOKEN }}" \
--form email="${{ secrets.COVERITY_EMAIL }}" \
--form [email protected] \
--form version="${GITHUB_SHA}" \
--form description="" \
"https://scan.coverity.com/builds?project=${{env.COVERITY_PROJECT}}"
+1 -2
View File
@@ -29,9 +29,8 @@ jobs:
issues: write
contents: read
steps:
- uses: github/issue-labeler@v3.2 #May not be the latest version
- uses: github/issue-labeler@v3.4 #May not be the latest version
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
configuration-path: .github/issue_labeler.yml
enable-versioned-regex: 0
sync-labels: 1
+4 -2
View File
@@ -24,5 +24,7 @@ jobs:
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v4
- uses: actions/checkout@v4
- uses: actions/labeler@v5
with:
configuration-path: .github/labeler.yml
+1 -1
View File
@@ -47,7 +47,7 @@ jobs:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@v2.3.1
uses: ossf/scorecard-action@v2.4.0
with:
results_file: results.sarif
results_format: sarif
+1
View File
@@ -37,6 +37,7 @@ cc_library(
]),
copts = ["-w"] + select({
"@platforms//os:windows": [""],
"@platforms//cpu:arm64": [""],
"//conditions:default": ["-mwaitpkg"],
}),
defines =
+1 -1
View File
@@ -40,7 +40,7 @@ load("@platforms//tools/build_defs/repo:git.bzl", "git_repository")
git_repository(
name = "oneTBB",
branch = "master",
remote = "https://github.com/oneapi-src/oneTBB/",
remote = "https://github.com/uxlfoundation/oneTBB/",
)
```
+32 -2
View File
@@ -1,4 +1,4 @@
# Copyright (c) 2020-2024 Intel Corporation
# Copyright (c) 2020-2025 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -12,10 +12,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
cmake_minimum_required(VERSION 3.5)
# If the running version of CMake is older than 3.12, the extra ... dots will be seen as version component separators,
# resulting in the ...<max> part being ignored and preserving the pre-3.12 behavior of basing policies on <min>.
cmake_minimum_required(VERSION 3.5.0...3.31.3)
# Enable CMake policies
if (POLICY CMP0063)
# The NEW behavior for this policy is to honor the visibility properties for all target types.
cmake_policy(SET CMP0063 NEW)
set(CMAKE_POLICY_DEFAULT_CMP0063 NEW)
endif()
if (POLICY CMP0068)
# RPATH settings do not affect install_name on macOS since CMake 3.9
cmake_policy(SET CMP0068 NEW)
@@ -29,6 +37,11 @@ elseif (DEFINED CMAKE_MSVC_RUNTIME_LIBRARY)
message(FATAL_ERROR "CMAKE_MSVC_RUNTIME_LIBRARY was defined while policy CMP0091 is not available. Use CMake 3.15 or newer.")
endif()
if (POLICY CMP0148)
# CMake 3.27: The FindPythonInterp and FindPythonLibs modules are removed
cmake_policy(SET CMP0148 OLD)
endif()
if (TBB_WINDOWS_DRIVER AND (NOT ("${CMAKE_MSVC_RUNTIME_LIBRARY}" STREQUAL MultiThreaded OR "${CMAKE_MSVC_RUNTIME_LIBRARY}" STREQUAL MultiThreadedDebug)))
message(FATAL_ERROR "Enabled TBB_WINDOWS_DRIVER requires CMAKE_MSVC_RUNTIME_LIBRARY to be set to MultiThreaded or MultiThreadedDebug.")
endif()
@@ -84,6 +97,11 @@ endif()
set(CMAKE_CXX_EXTENSIONS OFF) # use -std=c++... instead of -std=gnu++...
# ---------------------------------------------------------------------------------------------------------
# Setup symbol visibility properties.
set(CMAKE_VISIBILITY_INLINES_HIDDEN TRUE)
set(CMAKE_CXX_VISIBILITY_PRESET "hidden")
# ---------------------------------------------------------------------------------------------------------
# Detect architecture (bitness).
if (CMAKE_SIZEOF_VOID_P EQUAL 4)
@@ -105,8 +123,10 @@ option(TBB_CPF "Enable preview features of the library" OFF)
option(TBB_FIND_PACKAGE "Enable search for external oneTBB using find_package instead of build from sources" OFF)
option(TBB_DISABLE_HWLOC_AUTOMATIC_SEARCH "Disable HWLOC automatic search by pkg-config tool" ${CMAKE_CROSSCOMPILING})
option(TBB_ENABLE_IPO "Enable Interprocedural Optimization (IPO) during the compilation" ON)
option(TBB_CONTROL_FLOW_GUARD "Enable Control Flow Guard (CFG) during the compilation" OFF)
option(TBB_FUZZ_TESTING "Enable fuzz testing" OFF)
option(TBB_INSTALL "Enable installation" ON)
option(TBB_FILE_TRIM "Enable __FILE__ trim" ON)
if(LINUX)
option(TBB_LINUX_SEPARATE_DBG "Enable separation of the debug symbols during the build" OFF)
endif()
@@ -229,6 +249,16 @@ if (TBB_ENABLE_IPO AND BUILD_SHARED_LIBS AND NOT ANDROID_PLATFORM AND NOT TBB_SA
endif()
endif()
if (TBB_FILE_TRIM)
file(RELATIVE_PATH TBB_RELATIVE_BIN_PATH ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR})
file(TO_NATIVE_PATH ${CMAKE_SOURCE_DIR} NATIVE_TBB_PROJECT_ROOT_DIR)
file(TO_NATIVE_PATH ${TBB_RELATIVE_BIN_PATH} NATIVE_TBB_RELATIVE_BIN_PATH)
endif ()
if (TBB_CONTROL_FLOW_GUARD)
message(STATUS "Control Flow Guard (CFG) enabled")
endif()
set(TBB_COMPILER_SETTINGS_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cmake/compilers/${CMAKE_CXX_COMPILER_ID}.cmake)
if (EXISTS ${TBB_COMPILER_SETTINGS_FILE})
include(${TBB_COMPILER_SETTINGS_FILE})
-1
View File
@@ -2,7 +2,6 @@
/oneTBB/src/tbb/ @pavelkumbrasev
/oneTBB/src/tbb/ @dnmokhov
/oneTBB/src/tbb/ @JhaShweta1
/oneTBB/src/tbb/ @sarathnandu
/oneTBB/include/oneapi/tbb/parallel_* @pavelkumbrasev
/oneTBB/include/oneapi/tbb/concurrent_* @kboyarinov
+4 -4
View File
@@ -19,17 +19,17 @@ As an open source project, we welcome community contributions to oneAPI Threadin
## Licensing
Licensing is very important to open source projects. It helps ensure the software continues to be available under the terms that the author desired. The oneTBB project uses the [Apache 2.0 License](https://github.com/oneapi-src/oneTBB/blob/master/LICENSE.txt), a permissive open source license that allows you to freely use, modify, and distribute your own products that include Apache 2.0 licensed software. By contributing to the oneTBB project, you agree to the license and copyright terms therein and release your own contributions under these terms.
Licensing is very important to open source projects. It helps ensure the software continues to be available under the terms that the author desired. The oneTBB project uses the [Apache 2.0 License](https://github.com/uxlfoundation/oneTBB/blob/master/LICENSE.txt), a permissive open source license that allows you to freely use, modify, and distribute your own products that include Apache 2.0 licensed software. By contributing to the oneTBB project, you agree to the license and copyright terms therein and release your own contributions under these terms.
Some imported or reused components within oneTBB use other licenses, as described in [third-party-programs.txt](https://github.com/oneapi-src/oneTBB/blob/master/third-party-programs.txt). By carefully reviewing potential contributions, we can ensure that the community can develop products with oneTBB without concerns over patent or copyright issues.
Some imported or reused components within oneTBB use other licenses, as described in [third-party-programs.txt](https://github.com/uxlfoundation/oneTBB/blob/master/third-party-programs.txt). By carefully reviewing potential contributions, we can ensure that the community can develop products with oneTBB without concerns over patent or copyright issues.
## Prerequisites
As a contributor, you’ll want to be familiar with the oneTBB project and the repository layout. You should also know how to use it as explained in the [oneTBB documentation](https://oneapi-src.github.io/oneTBB/) and how to set up your build development environment to configure, build, and test oneTBB as explained in the [oneTBB Build System Description](cmake/README.md).
As a contributor, you'll want to be familiar with the oneTBB project and the repository layout. You should also know how to use it as explained in the [oneTBB documentation](https://uxlfoundation.github.io/oneTBB/) and how to set up your build development environment to configure, build, and test oneTBB as explained in the [oneTBB Build System Description](cmake/README.md).
## Pull Requests
You can find all [open oneTBB pull requests](https://github.com/oneapi-src/oneTBB/pulls) on GitHub.
You can find all [open oneTBB pull requests](https://github.com/uxlfoundation/oneTBB/pulls) on GitHub.
### Before contributing changes directly to the oneTBB repository
+3 -3
View File
@@ -61,7 +61,7 @@ You can use the ``install`` components for partial installation.
The following install components are supported:
- `runtime` - oneTBB runtime package (core shared libraries and `.dll` files on Windows* OS).
- `devel` - oneTBB development package (header files, CMake integration files, library symbolic links, and `.lib` files on Windows* OS).
- `tbb4py` - [oneTBB Module for Python](https://github.com/oneapi-src/oneTBB/blob/master/python/README.md).
- `tbb4py` - [oneTBB Module for Python](https://github.com/uxlfoundation/oneTBB/blob/master/python/README.md).
If you want to install specific components after configuration and build, run:
@@ -99,7 +99,7 @@ The following example demonstrates how to install oneTBB for single-configuratio
# Do our experiments in /tmp
cd /tmp
# Clone oneTBB repository
git clone https://github.com/oneapi-src/oneTBB.git
git clone https://github.com/uxlfoundation/oneTBB.git
cd oneTBB
# Create binary directory for out-of-source build
mkdir build && cd build
@@ -121,7 +121,7 @@ Choose the configuration during the build and install steps:
REM Do our experiments in %TMP%
cd %TMP%
REM Clone oneTBB repository
git clone https://github.com/oneapi-src/oneTBB.git
git clone https://github.com/uxlfoundation/oneTBB.git
cd oneTBB
REM Create binary directory for out-of-source build
mkdir build && cd build
Loaded 100 of 316 files, more files were not shown because too many files have changed in this diff. Show more