Compare commits

...
168 Commits
Author SHA1 Message Date
Rui Ueyama fb3c166f80 Bump mold version to 2.4.1 2024-03-01 11:30:05 +09:00
Rui Ueyama 39643aaf94 Fix GCC warning
Fixes https://github.com/rui314/mold/issues/1208
2024-03-01 10:44:35 +09:00
Rui Ueyama 50bdf39ba5 Mark shared symbols as weak if all references are weak
Previously, mold marked an imported symbol as a strong one if the
symbol came from a DSO and was exported as a strong symbol by the DSO.
This logic resulted in a miscomputation of the weakness bit, causing a
compatibility issue with other linkers.

Now, an imported symbol is marked as strong only when there's at least
one strong reference to it. In other words, if all references to an
imported symbol are weak, the symbol will be imported as a weak one.

Fixes https://github.com/llvm/llvm-project/issues/83080
2024-02-29 12:57:11 +09:00
Rui Ueyama 1e413d4121 Fix GCC warning
Fixes https://github.com/rui314/mold/issues/1205
2024-02-28 14:03:41 +09:00
Rui Ueyama fcda712238 Attempt to fix GCC warnings
Fixes https://github.com/rui314/mold/issues/1204
2024-02-27 16:38:45 +09:00
Rui Ueyama d4ff48a07e [PPC64V2] Emit _savegpr0_*, _restgpr0_*, _savegpr1_* and _restgpr1_* symbols
Fixes https://github.com/rui314/mold/issues/1203
2024-02-27 14:35:19 +09:00
Rui Ueyama 21f25545a8 Update README 2024-02-27 12:02:44 +09:00
Rui Ueyama 58558e319c RISC-V: Define __global_pointer$ for DSO
Fixes https://github.com/rui314/mold/issues/1182
2024-02-26 14:38:38 +09:00
Rui Ueyama 48f6a08352 Refactor 2024-02-26 12:29:27 +09:00
Rui Ueyama 01e9f42c10 Merge pull request #1198 from nga888/win-build-tidy
Minor fixes for the Windows build
2024-02-26 12:12:11 +09:00
Rui Ueyama 8c3967886c Remove code for the sold linker 2024-02-25 14:29:53 +09:00
Rui Ueyama 8a4fb3b445 Merge pull request #1199 from nga888/win-mem-map-file-handling
Fixes and improvements to Windows file handling and memory mapping
2024-02-25 12:58:08 +09:00
Rui Ueyama 04c9b62660 Update README 2024-02-24 15:08:05 +09:00
Rui Ueyama 4790349f79 Update README 2024-02-24 15:07:25 +09:00
Rui Ueyama 3e6005a1e7 Update benchmark results 2024-02-24 15:04:34 +09:00
Andrew Ng 9e3565027a Fixes and improvements to Windows file handling and memory mapping
Prior to this change, links on Windows were failing with:

  mold: fatal: <input file>: CreateFileMapping failed: 5

"5" is the Windows error code for ERROR_ACCESS_DENIED.

Use native Windows file API in MappedFile. Create read only mapping with
FILE_MAP_COPY view because the file mapping contents may be modified in
process but should not be written back.

On Windows, it is important that files are opened in "binary" mode to
avoid automatic "text" mode conversions which can result in "corrupted"
input/output.
2024-02-23 14:40:39 +00:00
Rui Ueyama 6d8e6afbf2 Fix R_PPC64_DTPREL16_LO_DS
Fixes https://github.com/rui314/mold/issues/1192
2024-02-23 17:14:25 +09:00
Rui Ueyama 5b5f431717 Fix CI 2024-02-22 09:13:26 +09:00
Andrew Ng 796e2556b6 Minor fixes for the Windows build
Removes some Windows build warnings.

Fixes conversion of Windows FILETIME to nsec in get_usage() used by
TimerRecord.
2024-02-21 15:24:07 +00:00
Rui Ueyama 8090737d56 Merge sections with different flags into a single section
Previously, mold didn't merge sections with the same name but different
section flags into a single section. This behavior is different from other
linkers and caused a compatibility issue.

Now, mold merges input sections by their names and types.

Fixes https://github.com/rui314/mold/issues/1196
2024-02-21 18:50:47 +09:00
Rui Ueyama d39a0c7035 Refactor 2024-02-21 18:21:03 +09:00
Rui Ueyama de696ba5a4 --repro: Do not try to reopen files with different paths
Fixes https://github.com/rui314/mold/issues/1197
2024-02-21 17:11:04 +09:00
Rui Ueyama cefa2d9100 Add Red Hat Enterprise Linux
Fixes https://github.com/rui314/mold/issues/1191
2024-02-20 15:11:42 +09:00
Rui Ueyama 9f07fd035c Support R_PPC64_DTPREL16_LO_DS
Fixes https://github.com/rui314/mold/issues/1192
2024-02-20 13:07:02 +09:00
Rui Ueyama 7ead7231c9 Fix CI 2024-02-19 16:50:23 +09:00
Rui Ueyama b0a3a5ce62 Drop SHF_COMPRESSED when creating a relocatable file
We always uncompress compressed sections when copying, so we should
not propagate the flag.

Fixes https://github.com/rui314/mold/issues/1195
2024-02-19 16:22:39 +09:00
Rui Ueyama 64c2084c8a Pass GCC offload data to the LTO backend
Fixes https://github.com/rui314/mold/issues/1190
2024-02-19 15:12:43 +09:00
Rui Ueyama 4c355239ad Refactor 2024-02-19 15:12:43 +09:00
Rui Ueyama 79169ea315 Format 2024-02-19 15:12:43 +09:00
Rui Ueyama 4db866d5c3 Merge pull request #1193 from gruenich/feature/range-loop-reference 2024-02-11 03:11:50 +09:00
Christoph Grüninger 83a5bfbfb1 elf/cmdline.cc: Add missing references in range-for loops
std::string is a non-trivial type, this change ensures its
copy-ctor and dtor don't get called.
Found by Clazy (range-loop-reference)


Signed-off-by: Christoph Grüninger <[email protected]>
2024-02-10 17:42:07 +01:00
Rui Ueyama c9651d9b2b Merge pull request #1187 from aokblast/fix/fileoffset 2024-02-04 00:37:32 +09:00
aokblast 8c99dd0f2c Feat: use accumulate address on sections with SHT_ALLOC 2024-02-03 22:03:55 +08:00
Rui Ueyama 25351379ab Fix CI 2024-02-02 02:59:50 +09:00
Rui Ueyama ba67c5d977 Fix CI 2024-02-02 02:35:12 +09:00
Rui Ueyama 7f8d77d84e Do not optimize absolute symbols in DSOs 2024-02-02 01:28:09 +09:00
Rui Ueyama 91a88ee80f Merge pull request #1186 from dancrossnyc/upstream
illumos: Treat absolute symbols specially
2024-02-02 01:25:18 +09:00
Rui Ueyama 206fe8527f Do not try to use /proc/self/exe on macOS
That path does not exist on macOS.

Fixes https://github.com/rui314/mold/issues/1185
2024-02-01 16:23:24 +01:00
Dan Cross bed5b1731b illumos: Treat absolute symbols specially
illumos is a descendent of Solaris/OpenSolaris, and treats
symbols with shndx=SHN_ABS and st_value=0 specially, such that
they should be marked imported.  This change adds a special
case for this behavior, wrapped up in a function that #ifdef's
out on other platforms.  With this patch, executables linked
with mold on illumos run; without it, they do not.

The original behavior was spotted by Luqman Aden (@luqmana).
This also patches a small bogon in the third-party `mimalloc`
code.  I've sent that same fix upstream, but so far the
maintainer has not resonded, so replicate here.

Fixes #1183

Signed-off-by: Dan Cross <[email protected]>
2024-02-01 13:55:05 +00:00
Rui Ueyama c36b83fdea Merge pull request #1177 from ishitatsuyuki/icf-workaround
Ignore addrsig sections corrupted by strip
2024-01-20 20:59:56 +09:00
Tatsuyuki Ishi fede4a8102 Ignore addrsig sections corrupted by strip
When removing symbols, the indices in .llvm_addrsig also needs to be
updated, but binutils is not aware of this and leaves the table in a
corrupt state.

Use the same test as what LLD uses to reject potentially corrupted addrsig
sections.

Unfortunately, testing this seems tricky. While producing a file with OOB
addrsig index is not hard, it's hard to consistently trigger a segfault
with that access.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2024-01-20 17:28:51 +09:00
Rui Ueyama 29c0baa151 Merge pull request #1175 from filipnavara/aadwarf 2024-01-19 18:57:18 +09:00
Rui Ueyama 5d04d9c6cd Merge pull request #1176 from sicherha/fix_unit_tests_on_s390x 2024-01-19 17:58:52 +09:00
Christoph Erhardt 2415b3e0bb [ELF][S390X] Skip tests that still fail with GCC 14
Signed-off-by: Christoph Erhardt <[email protected]>
2024-01-18 21:53:12 +01:00
Filip Navara d832547ba2 Add support for .eh_frame section on arm32 2024-01-18 18:28:26 +01:00
Rui Ueyama fe118f6347 Fix CI 2023-12-24 14:42:01 +09:00
Rui Ueyama 9554b92c86 Allow escaped meta-characters in version script patterns 2023-12-24 14:04:51 +09:00
Rui Ueyama 669ada024e Simplify 2023-12-24 13:53:02 +09:00
Rui Ueyama e1e16bf85a Fix version script priority
Previously, if two or more VERSION clauses match to the same symbol,
the first one took precedence. This was incompatible with GNU ld, which
gives the last one the highesth priority.

This change inverted the priority so that the last one will take
precedence other the others.

Fixes https://github.com/rui314/mold/issues/1158
2023-12-23 18:20:22 +09:00
Rui Ueyama 35516a6fc2 Do not include '@' in symbol name
Fixes https://github.com/rui314/mold/issues/1168
2023-12-23 15:37:39 +09:00
Rui Ueyama 51ecb10b01 Refactor 2023-12-23 15:35:43 +09:00
Rui Ueyama 10ce09fe0c Refactor 2023-12-23 15:16:02 +09:00
Rui Ueyama 0702d4185a Refactor 2023-12-19 15:32:43 +09:00
Rui Ueyama afe57e99d1 Update comments 2023-12-15 11:55:53 +09:00
Rui Ueyama e921486008 Fix CI
It seems some versions of objdump cannot disassemble c.li instruction.
2023-12-13 13:26:56 +09:00
Rui Ueyama 70d59ca1cd Improve test 2023-12-12 22:11:35 +09:00
Rui Ueyama 2ccaa81aab Relax a GOT load into a direct address materialization
https://github.com/riscv-non-isa/riscv-elf-psabi-doc/pull/397
2023-12-12 16:41:18 +09:00
Rui Ueyama 4db2eb91e9 Do not rewrite endbr64 of exported functions
Pointed out by @MaskRay at
https://github.com/llvm/llvm-project/issues/74400#issuecomment-1848686317.
2023-12-12 15:36:43 +09:00
Badini Rachid RodrigueandBADINI Rachid Rodrigue a4daff2d83 Add entry for EndeavouOS (#1165)
---------

Co-authored-by: BADINI Rachid Rodrigue <[email protected]>
2023-12-12 04:08:42 +09:00
Rui Ueyama cc884d57ca Fix cross building of ARM64 binaries on big-endian machines
It's probably very rare to do so, but mold couldn't create ARM64
binaries on big-endian machines due to a typo in an ELF struct
definition.
2023-12-10 16:06:52 +09:00
Rui Ueyama 0c8a872c37 Avoid using the HOST environment variable
It seems HOST is a fairly common environment variable. Let's use
MACHINE instead.

Fixes https://github.com/rui314/mold/issues/1163
2023-12-07 09:45:29 +09:00
Rui Ueyama 663ac74610 Simplify 2023-12-06 19:51:22 +09:00
Rui Ueyama f04aebf1e0 Simplify 2023-12-06 13:04:58 +09:00
Rui Ueyama bcf3b48e98 Simplify 2023-12-06 11:33:46 +09:00
Rui Ueyama e7cc9455c9 Merge pull request #1161 from ishitatsuyuki/prealloc
Use fallocate when creating a new output
2023-12-06 11:05:17 +09:00
Tatsuyuki Ishi e909535684 Use fallocate when creating a new output
Using ftruncate() creates holes inside files, that on first (write) page
fault gets allocated an actual disk block.  There are several
disadvantages with this:
- Allocating block one-by-one is slower than doing it all at once.
- As we write to the output in parallel the write order is not exactly
  sequential.  The allocated blocks can also be out-of-order and make the
  on-disk representation fragmented.

Instead, use posix_fallocate() which allocates a contiguous on-disk block
(if possible).  It's arguably a better fit for linker output, since holes
are unlikely to be present after writing all the output (any contiguous
zeroes should just go into .bss).

I benchmarked this by linking mold with debug symbols.  On an 8-core
desktop, it is 5% (1.0s -> 0.95s) faster, but on a 64-core workstation it
is actually 40% slower (0.37s -> 0.52s).  The total amount of System time
is reduced (6.7s -> 5.2s), however.  I suspect that the reduced page fault
work ends up causing more lock contention.

Despite potential regression on high-core systems I still think this
change is justified because of the better on-disk layout.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2023-12-05 16:07:48 +09:00
Rui Ueyama cea54cb865 Check the runtime libtbb version to handle sigabrt 2023-12-05 12:25:52 +09:00
Rui Ueyama 3bc2e9bdb1 Revert "Set DF_SYMBOLIC if -Bsymbolic is given"
This reverts commit 9d35710419 to unbreak CI.
2023-12-04 16:07:56 +09:00
Rui Ueyama 159960c72e Fix errors in the help message; this change was made using ChatGPT 2023-12-04 15:34:56 +09:00
Rui Ueyama 9d35710419 Set DF_SYMBOLIC if -Bsymbolic is given
I don't think this would make any difference, but the ELF spec says
that the flag is set if symbols are bound locally.
2023-12-04 15:10:01 +09:00
Rui Ueyama bdcf448605 Emit TLS common symbols in .tls_common
Section names are not significant in executables or DSOs, so this is
a purely aesthetic change.
2023-12-04 13:13:33 +09:00
Rui Ueyama 72eb180028 Upgrade OneTBB to version 2021.11.0 2023-12-02 12:34:16 +09:00
Rui Ueyama fe427bfe7a Do not compute per-InputSection address-taken bit for -z rewrite-endbr
We now compute per-Symbol address-taken bit in rewrite_endbr,
so this step is not necessary anymore.
2023-12-02 12:34:16 +09:00
Rui Ueyama 348f5c7086 Merge pull request #1160 from tzussman/ci_fix 2023-12-02 07:52:41 +09:00
Tal Zussman e09b877146 Fix typos in ci.yml 2023-12-01 10:01:25 -05:00
Rui Ueyama aeb7ba4731 Add asserts 2023-12-01 21:03:15 +09:00
Rui Ueyama 421cd1c3ca Revert "Attempt to fix CI"
This reverts commit 5e8729bd6f.
2023-12-01 10:26:10 +09:00
Rui Ueyama 5e8729bd6f Attempt to fix CI 2023-12-01 10:19:48 +09:00
Rui Ueyama 76d057a418 Archive entire build directory when CI fails 2023-11-30 19:34:08 +09:00
Rui Ueyama ea9864bbd5 Do not try to demangle C++ symbols as Rust ones
Previously, we always tried to demangle a symbol name as a Rust
symbol first and then attempted to demangle it as a C++ symbol.
This resulted in an incorrect demangled result, as the Rust legacy
mangling scheme is not distinguishable from C++.

This patch fix the issue by adding the "is_rust_obj" flag to the
ObjectFile. We try to demangle a symbol as a Rust one before as a C++
only if the flag is true.

Fixes https://github.com/rui314/mold/issues/1159
2023-11-30 13:53:23 +09:00
Rui Ueyama 3d9b4d5c05 Bump mold version to 2.4.0 2023-11-29 17:58:00 +09:00
Rui Ueyama d06bfe0ac3 Update GitHub sponsor list 2023-11-29 17:48:11 +09:00
Rui Ueyama 6540294a81 Fix CI 2023-11-29 17:15:39 +09:00
Rui Ueyama 02a7640047 Fix build artifact pathnames 2023-11-29 16:59:23 +09:00
Rui Ueyama f4c5a8a42e Allow multiple .eh_frame sections in a single object file
A `.eh_frame` section contains data for exception handling. Usually,
an object file contains only one `.eh_frame` section, which explains
how to handle exceptions for all text sections in the same object file.

However, it appears that, in rare cases, we need to handle object
files containing multiple `.eh_frame` sections. An example of this is
the `/usr/lib/clang/17/lib/x86_64-redhat-linux-gnu/clang_rt.crtbegin.o`
file, which is provided by the `compiler-rt` package of Fedora 39.
Specifically, I'm using the `quay.io/fedora/fedora:39` Docker image.
The file contains two `.eh_frame` sections.

One `.eh_frame` in the file is of type `STT_X86_64_UNWIND` and the
other is of `STT_PROGBITS`. It's possible that the file was created
with `ld -r`, and the linker failed to merge the two incoming
`.eh_frame` sections into one output section due to the difference in
section types.

We did not expect such inputs and consequently produced corrupted
output files.

This commit improves our linker so that mold can handle multiple
`.eh_frame` sections in a single object file.

Fixes https://github.com/rui314/mold/issues/1157
2023-11-29 16:10:36 +09:00
Rui Ueyama 0fdbace9b2 Improve compatibility of precedences in version script patterns
Previously, symbol names or wildcard patterns in version script files
were processed strictly from top to bottom. When a single symbol
matches two or more patterns, the first one was given precedence.

However, GNU ld appears to treat exact matches differently, assigning
them higher precedence over wildcard patterns. This discrepancy led to
programs linked with Qt 6.6.1 using mold failing to launch.

Fixes https://github.com/rui314/mold/issues/1158
2023-11-28 15:43:48 +09:00
Rui Ueyama cab6014154 Merge pull request #1156 from ywgrit/main
Remove assertions to make errors exposed in a customized way
2023-11-26 18:55:40 +09:00
Rui Ueyama 000ce0ee52 Handle LLVM-generated TLSDESC code sequence
It had been assumed that the TLSDESC code seqeunce looks like this:

  lea    0(%rip), %rax
      R_X86_64_GOTPC32_TLSDESC    foo
  call   *(%rax)
      R_X86_64_TLSDESC_CALL       foo

However, LLVM seems to also emit something like this:

  lea    0(%rip), %reg
      R_X86_64_GOTPC32_TLSDESC    foo
  ...
  mov    %reg, %rax
  ...
  call   *(%rax)
      R_X86_64_TLSDESC_CALL       foo

That means when we rewrite the LEA instruction to relax the code
sequence, we need to handle destination registers other than %rax.

The wrong assumption caused a program crash as reported as
https://bugs.gentoo.org/914849.
2023-11-26 18:49:20 +09:00
ywgrit d720a2011c Remove assertions to make errors exposed in a customized way 2023-11-26 17:04:28 +08:00
Rui Ueyama c91055271b Simplify 2023-11-24 16:15:37 +09:00
Rui Ueyama 1478acd551 Do not save strings if not necessary 2023-11-24 11:48:19 +09:00
Rui Ueyama b3404c07d7 Add a timer prober 2023-11-24 10:44:18 +09:00
Rui Ueyama 94767dc0e7 Fix a crash bug 2023-11-24 10:14:00 +09:00
Rui Ueyama f957acfe11 Add an entry for Amazon Linux 2023-11-24 09:30:56 +09:00
Rui Ueyama 7fc43ccc3a Add comments 2023-11-24 08:40:57 +09:00
Rui Ueyama dc5b9508d9 Simplify 2023-11-24 06:39:27 +09:00
Rui Ueyama ef056d8ed1 Refactor 2023-11-24 06:38:29 +09:00
Rui Ueyama 6a63671ef5 Simplify 2023-11-23 12:22:14 +09:00
Rui Ueyama d3867766af Remove redundant check
`is_ld("ld")` returns true
2023-11-23 11:47:33 +09:00
Rui Ueyama 1ae4b9571b Merge pull request #1154 from andreas-schwab/main
mold-wrapper: also wrap posix_spawnp
2023-11-23 11:39:07 +09:00
Andreas Schwab 3fd1cec20e mold-wrapper: also wrap posix_spawnp
Fixes issue 1153.

Signed-off-by: Andreas Schwab <[email protected]>
2023-11-22 14:59:11 +01:00
Rui Ueyama 28b16eea19 Rename classes 2023-11-22 20:56:21 +09:00
Rui Ueyama 37f32a9008 Simplify 2023-11-22 14:12:37 +09:00
Rui Ueyama 8415252322 Do not use std::aligned_alloc
Fixes https://github.com/rui314/mold/issues/1152
2023-11-22 11:52:33 +09:00
Rui Ueyama b3984c9580 Simplify 2023-11-21 21:23:47 +09:00
Rui Ueyama 6616995804 Refactor 2023-11-21 12:23:49 +09:00
Rui Ueyama de7d37e1fc Do not count .tbss into account when creating PT_GNU_RELRO
Fixes https://github.com/rui314/mold/issues/1151
2023-11-21 11:44:52 +09:00
Rui Ueyama 3cb8a528a5 Make -z rewrite-endbr to work without -ffunction-sections
Now we can rewrite an endbr64 instruction even if it is not at the
beginning of a section.
2023-11-20 17:36:23 +09:00
Rui Ueyama 8bb01e1a0e Fix CI 2023-11-20 14:25:03 +09:00
Rui Ueyama 0416d7ed80 Add --nmagic
Fixes https://github.com/rui314/mold/issues/1144
2023-11-20 13:35:04 +09:00
Rui Ueyama c02c04f08a Improve dist.sh 2023-11-20 11:55:14 +09:00
Rui Ueyama efb952ef12 Use the release version of setup-mold GitHub action 2023-11-19 10:32:51 +09:00
Rui Ueyama 04b145d795 Add comments and fix SHA256 digest 2023-11-19 09:36:04 +09:00
Rui Ueyama 1830fa1d84 Pin docker images with sha256 digests for additional security 2023-11-18 12:25:37 +09:00
Rui Ueyama a2b300b863 Fix CI 2023-11-17 19:12:19 +09:00
Rui Ueyama 2d8297a562 Fix CI 2023-11-17 18:22:16 +09:00
Rui Ueyama deb7728b46 Fix CI 2023-11-17 18:08:13 +09:00
Rui Ueyama bbd6d86af4 Fix CI 2023-11-17 17:57:59 +09:00
Rui Ueyama b6361b8cad Refactor 2023-11-17 17:51:25 +09:00
Rui Ueyama 04fdae124f Refactor 2023-11-17 17:41:24 +09:00
Rui Ueyama 29df7093a8 Refactor 2023-11-17 17:18:53 +09:00
Rui Ueyama 18212a21ab Refactor 2023-11-17 17:04:43 +09:00
Rui Ueyama e13d28bfe6 Create non-x86 binaries on daily basis
Building non-native binaries using Qemu is extremely heavy.
It takes ~2 hours on a GitHub Actions runner. We don't want to
run it on each push.
2023-11-17 15:45:39 +09:00
Rui Ueyama 667204ac7c Make dist.sh to work on macOS 2023-11-17 15:09:31 +09:00
Rui Ueyama 76ea83e791 Create binary tarballs for non-x86-46 targets 2023-11-17 14:15:04 +09:00
Rui Ueyama 55daf7385d Cache Docker images for GitHub Actions 2023-11-17 13:42:54 +09:00
Rui Ueyama 0e66e4b7e9 Create distribution tarballs for each commit 2023-11-16 22:54:38 +09:00
Rui Ueyama fe485db9b4 Refactor 2023-11-16 18:30:31 +09:00
Rui Ueyama b3dc4879fd Remove a global variable 2023-11-16 18:00:41 +09:00
Rui Ueyama 25ce3738b9 Update workflow 2023-11-16 16:50:33 +09:00
Rui Ueyama 22b8ac8470 Update mold.1 2023-11-16 07:16:13 +00:00
Rui Ueyama 6a4e128ef8 Document --spare-program-headers 2023-11-16 16:15:28 +09:00
Rui Ueyama 66d68c8a98 Auto-generate docs/mold.1 2023-11-16 16:15:28 +09:00
Rui Ueyama 76867af090 Regenerate mold.1
This file is created by `ronn` command.
2023-11-16 12:54:49 +09:00
Rui Ueyama 5d02248a0b Sort entries and fix typos 2023-11-16 12:54:38 +09:00
Rui Ueyama eb6c213f2a Add --spare-program-headers
This is a new experimental flag to make room at the end of PHDR
so that post-processing tools can add more entries as needed.

Fixes https://github.com/rui314/mold/issues/1148
2023-11-16 12:07:13 +09:00
Rui Ueyama efdac9afdd Merge pull request #1147 from Saldivarcher/add-trace-symbol-help-msg 2023-11-16 05:45:29 +09:00
Miguel Saldivar c6bfff2a04 Add --trace-symbol to the help message 2023-11-14 14:24:41 -06:00
Rui Ueyama 49066ea329 Bump mold version to 2.3.3 2023-11-14 14:41:42 +09:00
Rui Ueyama 25ceab07f7 Build mold on Debian 8 (Jessie)
Debian 8 was released in April 2015, which is old enough for
binary distribution. Previously, we used to use Debian 10 (Bullseye),
which was released in July 2019.
2023-11-14 12:44:02 +09:00
Rui Ueyama 6e97393740 Fix tests 2023-11-14 12:44:02 +09:00
Rui Ueyama ff3d54d26f Fix a crash bug in --defsym
Fixes https://github.com/rui314/mold/issues/1108
2023-11-13 20:09:25 +09:00
Rui Ueyama 25c9cf3976 Refactor 2023-11-13 19:57:50 +09:00
Rui Ueyama 5f2fb08cfd Make dist.sh reproducible
dist.sh script should now produce bit-by-bit identical output files now.

Fixes https://github.com/rui314/mold/issues/1146
2023-11-13 17:14:37 +09:00
Rui Ueyama 60bc56848f Fix tests for Debian 9 2023-11-13 13:37:35 +09:00
Rui Ueyama 600ca381fd Reduce the size of Symbol by 2 bytes 2023-11-12 13:39:44 +09:00
Rui Ueyama da3f5dd4ec Fix --dynamic-list for DSOs
--dynamic-list, --export-dynamic-symbol and --export-dynamic-symbol-list
have different semantics for executables and DSOs. If the output is an
executable, they specify a list of symbols that are to be exported.
If the output is a shared object, they specify the list of symbols that
are to be interposable.

mold havne't implemented the latter semantics. This commit fixes that
issue.

Fixes https://github.com/rui314/mold/issues/1071
2023-11-12 13:00:24 +09:00
Rui Ueyama b59911b0c1 Refactor 2023-11-11 15:09:51 +09:00
Rui Ueyama 8131528305 Rename long .rodata section names 2023-11-10 15:07:19 +09:00
Rui Ueyama 6a71e7dd4b Refactor 2023-11-09 18:57:13 +09:00
Rui Ueyama a63b2f70b2 Fix CI 2023-11-09 15:47:25 +09:00
Rui Ueyama 3f88964527 Map .ctors/.dtors into .init_array/.fini_array
.init_array/.fini_array haven't completely replace .ctors/.dtors, so
we need to convert .ctors/.dtors to .init_array/.fini_array.

Fixes https://github.com/rui314/mold/issues/1113
2023-11-09 12:43:58 +09:00
Rui Ueyama e18dc70af0 Refactor 2023-11-08 22:04:14 +09:00
Rui Ueyama 0c6daad215 Add more tests 2023-11-07 15:20:31 +09:00
Rui Ueyama 8c35c80bb7 Enable qemu tests by default 2023-11-07 13:55:56 +09:00
Rui Ueyama d42ba3bae4 Fix CI 2023-11-07 13:48:48 +09:00
Rui Ueyama 8c337b25df Run power10 tests on qemu only if qemu and cross GCC suports power10 2023-11-07 12:04:22 +09:00
Rui Ueyama 8d1c08bd03 Simplify 2023-11-07 11:25:03 +09:00
Rui Ueyama 5599a6f24f Skip test only on qemu 2023-11-07 11:06:13 +09:00
Rui Ueyama ef9a610dd1 Run tests as long as qemu and cross compiler are available
So that we can catch bugs like e11d64b5c7
earlier.
2023-11-07 11:00:26 +09:00
Rui Ueyama 31c3b5397b [POWER10] Emit dynamic relocation for ifunc
Previously, we didn't emit a dynamic relocation for a statically-
initialized function pointer pointing to an ifunc on POWER10, e.g.

  int fn() __attribute__((ifunc("resolve_fn")));
  void *ptr = fn;

Fixes https://github.com/rui314/mold/issues/1142
2023-11-06 13:27:05 +09:00
Rui Ueyama e11d64b5c7 Do not run tests under qemu-i386 on i686 native host
Fixes https://github.com/rui314/mold/issues/1140
2023-11-06 12:44:28 +09:00
Rui Ueyama 9d91a2bc23 Revert "Do not crash if defsym'ed symbol didn't exist"
This reverts commit 5c7a75b3e4 and
8c41f21d3c because they broke CI.
2023-11-06 12:43:20 +09:00
Rui Ueyama 8c41f21d3c Fix CI 2023-11-05 22:30:43 +09:00
Rui Ueyama 5c7a75b3e4 Do not crash if defsym'ed symbol didn't exist
Fixes https://github.com/rui314/mold/issues/1108
2023-11-05 16:37:09 +09:00
Rui Ueyama 0804fbd095 Refactor 2023-11-05 16:11:27 +09:00
Rui Ueyama 0f78fa9c43 Attempt to fix tests on 32-bit x86
Fixes https://github.com/rui314/mold/issues/1140
2023-11-05 11:01:31 +09:00
Rui Ueyama d30e970eb8 Remove useless function call 2023-11-05 10:57:25 +09:00
246 changed files with 7789 additions and 3147 deletions

No files matched your search

+45
View File
@@ -0,0 +1,45 @@
name: Build all tarballs
on:
schedule:
- cron: '0 0 * * *'
release:
types: [ published ]
jobs:
build-tarballs:
runs-on: ubuntu-latest
strategy:
matrix:
target: [ x86_64, aarch64, arm, riscv64, ppc64le, s390x ]
permissions:
contents: read
packages: write
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build a tarball
run: ./dist.sh ${{ matrix.target }}
- name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: tarballs
path: mold-*.tar.gz
+36
View File
@@ -0,0 +1,36 @@
name: Build x86 tarball
on:
push:
branches: [ main ]
jobs:
build-tarball:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build a tarball
run: ./dist.sh
- name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: tarball
path: mold-*.tar.gz
+21 -22
View File
@@ -18,12 +18,12 @@ jobs:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: rui314/setup-mold@staging
- uses: rui314/setup-mold@v1
- name: install-build-deps
run: sudo ./install-build-deps.sh update
run: sudo ./install-build-deps.sh
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: build and test
- name: build
run: |
echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
sudo apt-get install -y clang++-12
@@ -31,15 +31,18 @@ jobs:
cd build
cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++-12 ${{ matrix.target }} ..
cmake --build . -j$(nproc)
ctest . -j$(nproc)
- name: test
run: |
cd build
ctest -j$(nproc)
- name: archive test results
uses: actions/upload-artifact@v3
if: failure()
with:
name: test-results-clang
path: |
build/out
build/Testing
build
!build/CMakeFiles
build-gcc:
runs-on: ubuntu-20.04
@@ -47,16 +50,11 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: install-build-deps
shell: bash
run: |
# Install cross toolchains
dpkg --add-architecture i386
./install-build-deps.sh update
apt-get install -y sudo qemu-user gdb zstd dwarfdump xz-utils {gcc,g++}-10-{i686,aarch64,riscv64,powerpc,powerpc64,powerpc64le,s390x,sparc64,m68k,sh4,alpha}-linux-gnu {gcc,g++}-10-arm-linux-gnueabihf
for i in {i686,aarch64,riscv64,powerpc,powerpc64,powerpc64le,s390x,sparc64,m68k,sh4,alpha}-linux-gnu arm-linux-gnueabihf; do
ln -sf /usr/bin/$i-gcc-10 /usr/bin/$i-gcc
ln -sf /usr/bin/$i-g++-10 /usr/bin/$i-g++
done
./install-build-deps.sh
./install-cross-tools.sh
# Install a RV32 toolchain from third party since it's not available
# as an Ubuntu package.
@@ -89,26 +87,27 @@ jobs:
wget -O/usr/local/bin/qemu-loongarch64 -q https://github.com/loongson/build-tools/releases/download/2023.08.08/qemu-loongarch64
chmod 755 /usr/local/bin/qemu-loongarch64
shell: bash
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: build and test
- name: build
run: |
echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
mkdir build
cd build
cmake -DMOLD_ENABLE_QEMU_TESTS=On -DMOLD_ENABLE_QEMU_TESTS_RV32=On -DMOLD_ENABLE_QEMU_TESTS_LOONGARCH=On ..
cmake ..
cmake --build . -j$(nproc)
ctest . -j$(nproc)
- name: test
run: |
cd build
ctest -j$(nproc)
- name: archive test results
uses: actions/upload-artifact@v3
if: failure()
with:
name: test-results-gcc
path: |
build/out
build/Testing
build
!build/CMakeFiles
build-macos:
runs-on: macos-11
@@ -122,7 +121,7 @@ jobs:
- uses: actions/checkout@v3
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: build and test
- name: build
run: |
echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
mkdir build
-15
View File
@@ -1,15 +0,0 @@
name: linux-packages
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- name: build
run: ./dist.sh x86_64
- name: upload
uses: actions/upload-artifact@v3
with:
path: mold-*-linux.tar.gz
+32
View File
@@ -0,0 +1,32 @@
# This file is generated by ChatGPT
name: Update manpage
on:
push:
paths:
- 'docs/mold.md'
jobs:
update-manpage:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v2
- name: Install ronn
run: sudo apt-get install -y ronn
- name: Generate mold.1 from mold.md
run: ronn --roff docs/mold.md
- name: Configure Git
run: |
git config --global user.name 'Rui Ueyama'
git config --global user.email '[email protected]'
- name: Commit and push if mold.1 is updated
run: |
git add docs/mold.1
git diff --staged --quiet || (git commit -m "Update mold.1 (automated commit)" && git push)
+14 -64
View File
@@ -1,31 +1,31 @@
# You can customize a build by specifying CMake options. An option may be
# given in the -Dvariable=value form. For a boolean variable, `ON` or `1`
# means true while `OFF` or `0` means false.
# means true, while `OFF` or `0` means false.
#
# Here are a couple of common cmake options:
#
# -DCMAKE_C_COMPILER=<command-name>
#
# Specifies a C compiler name to use. The default value is `cc`.
# Specifies the C compiler name to use. The default value is `cc`.
#
# -DCMAKE_CXX_COMPILER=<command-name>
#
# Specifies a C++ compiler name to use. The default value is `c++`.
# Specifies the C++ compiler name to use. The default value is `c++`.
#
# -DCMAKE_INSTALL_PREFIX=<directory>
#
# Specifies an install target directory. The default value is `/usr/local`.
# Specifies the install target directory. The default value is `/usr/local`.
#
# -DCMAKE_BUILD_TYPE=[Debug | Release | RelWithDebInfo | MinSizeRel]
#
# Specifies a build type. The default is `Release` which is the right
# Specifies the build type. The default is `Release`, which is the right
# option unless you are debugging mold.
#
# An example of a cmake command line is shown below:
#
# $ cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_CXX_COMPILER=g++-12 ..
#
# where `..` refers this directory.
# where `..` refers to this directory.
#
# With cmake, you may run `cmake --install .` instead of `make install` to
# install build artifacts to system directories. If you want to install
@@ -39,12 +39,12 @@
# how to build mold. However, as a policy, we do not provide a way to
# enable/disable any individual mold's feature. In other words, we do not
# provide options like `--enable-foo` or `--disable-foo`. The motivation
# behind it is build reproducibility. We want to guarantees that all builds
# of the mold linker of the same version will have the exactly same set of
# behind this is build reproducibility. We want to guarantee that all builds
# of the mold linker of the same version will have the exact same set of
# features and behave exactly the same.
cmake_minimum_required(VERSION 3.14)
project(mold VERSION 2.3.2)
project(mold VERSION 2.4.1)
include(CMakeDependentOption)
include(GNUInstallDirs)
@@ -134,7 +134,7 @@ else()
target_include_directories(mold PUBLIC third-party/blake3/c)
endif()
# Find zstd compression library. If libzstd.so is not found, we compile a
# Find zstd compression library. If zstd.h is not found, we compile a
# bundled one and statically-link it to mold.
include(CheckIncludeFile)
check_include_file(zstd.h HAVE_ZSTD_H)
@@ -212,11 +212,6 @@ else()
mold_add_tbb()
endif()
# Check if this is a commercial version of mold (a.k.a. "sold")
if(EXISTS "${CMAKE_SOURCE_DIR}/LICENSE.md")
set(MOLD_IS_SOLD ON)
endif()
# We always use Clang to build mold on Windows. MSVC can't compile mold.
if(WIN32)
if(MSVC AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
@@ -351,14 +346,6 @@ foreach (SOURCE IN LISTS MOLD_ELF_TEMPLATE_FILES)
endforeach()
endforeach()
if(MOLD_IS_SOLD)
foreach (SOURCE IN LISTS MOLD_MACHO_TEMPLATE_FILES)
foreach(TARGET IN LISTS MOLD_MACHO_TARGETS)
mold_instantiate_templates(${SOURCE} ${TARGET})
endforeach()
endforeach()
endif()
# Add other non-template source files.
target_sources(mold PRIVATE
common/compress.cc
@@ -388,29 +375,14 @@ target_sources(mold PRIVATE
third-party/rust-demangle/rust-demangle.c
)
if(MOLD_IS_SOLD)
target_sources(mold PRIVATE
macho/arch-arm64.cc
macho/arch-x86-64.cc
macho/yaml.cc
)
endif()
# Add frequently included header files for pre-compiling.
# target_precompile_headers is supported by CMake 3.16.0 or newer.
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.16.0")
if(MOLD_IS_SOLD)
target_precompile_headers(mold PRIVATE
"$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/elf/mold.h>"
"$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/macho/mold.h>")
else()
target_precompile_headers(mold PRIVATE
"$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/elf/mold.h>")
endif()
# ccache needs this flag along with `sloppiness = pch_defines,time_macros`
# to enable caching
target_compile_options(mold PRIVATE -fpch-preprocess)
if(NOT MSVC)
target_compile_options(mold PRIVATE -fpch-preprocess)
endif()
endif()
include(CTest)
@@ -424,22 +396,9 @@ if(BUILD_TESTING)
BYPRODUCTS ld
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
VERBATIM)
if(MOLD_IS_SOLD)
add_custom_command(
TARGET mold POST_BUILD
COMMAND ${CMAKE_COMMAND} -E create_symlink mold ld64
BYPRODUCTS ld64
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
VERBATIM)
endif()
endif()
if(${APPLE})
if(MOLD_IS_SOLD)
add_subdirectory(test/macho)
endif()
elseif(${UNIX})
if(${UNIX})
add_subdirectory(test/elf)
endif()
endif()
@@ -473,13 +432,4 @@ if(NOT CMAKE_SKIP_INSTALL_RULES)
${CMAKE_INSTALL_BINDIR}/ld.mold${CMAKE_EXECUTABLE_SUFFIX})
mold_install_relative_symlink(${CMAKE_INSTALL_MANDIR}/man1/mold.1
${CMAKE_INSTALL_MANDIR}/man1/ld.mold.1)
if(MOLD_IS_SOLD)
mold_install_relative_symlink(${CMAKE_INSTALL_BINDIR}/mold
${CMAKE_INSTALL_BINDIR}/ld64.mold)
mold_install_relative_symlink(${CMAKE_INSTALL_BINDIR}/mold
${CMAKE_INSTALL_BINDIR}/ld.sold)
mold_install_relative_symlink(${CMAKE_INSTALL_BINDIR}/mold
${CMAKE_INSTALL_BINDIR}/ld64.sold)
endif()
endif()
+15 -22
View File
@@ -1,30 +1,25 @@
# mold: A Modern Linker
<i>This repository contains a free version of the mold linker.
If you are looking for a commercial version that supports macOS
please visit the
[repository of the sold linker](https://github.com/bluewhalesystems/sold).</i>
mold is a faster drop-in replacement for existing Unix linkers. It is several
times quicker than the LLVM lld linker, the second-fastest open-source linker,
which I initially developed a few years ago. mold aims to enhance developer
productivity by minimizing build time, particularly in rapid
debug-edit-rebuild cycles.
Here is a performance comparison of GNU gold, LLVM lld, and mold when linking
final debuginfo-enabled executables for major large programs on a simulated
8-core, 16-thread machine.
Here is a performance comparison of GNU ld, GNU gold, LLVM lld, and
mold when linking final debuginfo-enabled executables for major large
programs on a simulated 16-core, 32-thread machine.
![Link speed comparison](docs/comparison.png)
![Link speed comparison](docs/chart.svg)
| Program (linker output size) | GNU gold | LLVM lld | mold
|-------------------------------|----------|----------|--------
| Chrome 96 (1.89 GiB) | 53.86s | 11.74s | 2.21s
| Clang 13 (3.18 GiB) | 64.12s | 5.82s | 2.90s
| Firefox 89 libxul (1.64 GiB) | 32.95s | 6.80s | 1.42s
| Program (linker output size) | GNU ld | GNU gold | LLVM lld | mold
|-------------------------------|--------|----------|----------|------
| MySQL 8.3 (0.47 GiB) | 10.84s | 7.47s | 1.64s | 0.46s
| Clang 19 (1.56 GiB) | 42.07s | 33.13s | 5.20s | 1.35s
| Chromium 124 (1.35 GiB) | N/A | 27.40s | 6.10s | 1.52s
mold is so fast that it is only 2x _slower_ than the `cp` command on the same
machine. If you find that mold is not faster than other linkers, please feel
machine. If you find that mold is not faster than other linkers, feel
free to [file a bug report](https://github.com/rui314/mold/issues).
mold supports x86-64, i386, ARM64, ARM32, 64-bit/32-bit little/big-endian
@@ -32,9 +27,6 @@ RISC-V, 32-bit PowerPC, 64-bit big-endian PowerPC ELFv1, 64-bit little-endian
PowerPC ELFv2, s390x, 64-bit/32-bit LoongArch, SPARC64, m68k, SH-4, and DEC
Alpha.
mold/macOS is commercial software. For mold/macOS, please visit
https://github.com/bluewhalesystems/sold.
## Why does linking speed matter?
If you are using a compiled language such as C, C++, or Rust, a build consists
@@ -71,7 +63,7 @@ necessary packages. You may need to run it as root.
git clone https://github.com/rui314/mold.git
mkdir mold/build
cd mold/build
git checkout v2.3.2
git checkout v2.4.1
../install-build-deps.sh
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=c++ ..
cmake --build . -j $(nproc)
@@ -255,13 +247,14 @@ following people and organizations who have sponsored $128/month or more:
<a href="https://www.emergetools.com"><img src="docs/emerge-tools-logo.png" align=center height=120 width=240 alt="Emerge Tools"></a><br>
- [Uber](https://uber.com)
- [G-Research](https://www.gresearch.co.uk)
- [Signal Slot Inc.](https://github.com/signal-slot)
- [GlareDB](https://github.com/GlareDB)
### Individual sponsors
- [300baud](https://github.com/300baud)
- [Johan Andersson](https://github.com/repi)
- [Wei Wu](https://github.com/lazyparser)
- [kyle-elliott](https://github.com/kyle-elliott)
- [Bryant Biggs](https://github.com/bryantbiggs)
- [kraptor23](https://github.com/kraptor23)
- [Jinkyu Yi](https://github.com/jincreator)
+69 -36
View File
@@ -216,7 +216,7 @@ struct Atomic : std::atomic<T> {
using std::atomic<T>::atomic;
Atomic(const Atomic<T> &other) { store(other.load()); }
Atomic(const Atomic<T> &other) : std::atomic<T>(other.load()) {}
Atomic<T> &operator=(const Atomic<T> &other) {
store(other.load());
@@ -565,7 +565,7 @@ public:
}
~ConcurrentMap() {
free(entries);
free(entries_buf);
}
// In order to avoid unnecessary cache-line false sharing, we want
@@ -579,19 +579,14 @@ public:
void resize(i64 nbuckets) {
this->nbuckets = std::max<i64>(MIN_NBUCKETS, bit_ceil(nbuckets));
free(entries_buf);
i64 sz = sizeof(Entry) * this->nbuckets;
free(entries);
#if _WIN32
// Even though std::aligned_alloc is defined in C++17, MSVC doesn't
// seem to provide that function.
entries = (Entry *)_aligned_malloc(sz, alignof(Entry));
#else
entries = (Entry *)std::aligned_alloc(alignof(Entry), sz);
#endif
memset(entries, 0, sz);
// seem to provide that function. C11's aligned_alloc may not be always
// avialalbe. Therefore, we'll align the buffer ourselves.
i64 size = sizeof(Entry) * this->nbuckets;
entries_buf = calloc(1, size + alignof(Entry) - 1);
entries = (Entry *)align_to((u64)entries_buf, alignof(Entry));
}
std::pair<T *, bool> insert(std::string_view key, u64 hash, const T &val) {
@@ -699,6 +694,7 @@ public:
static constexpr i64 NUM_SHARDS = 16;
static constexpr i64 MAX_RETRY = 128;
void *entries_buf = nullptr;
Entry *entries = nullptr;
i64 nbuckets = 0;
@@ -759,10 +755,19 @@ public:
if (this->path == "-") {
fp = stdout;
} else {
#ifdef _WIN32
int pmode = (perm & 0200) ? (_S_IREAD | _S_IWRITE) : _S_IREAD;
i64 fd = _open(this->path.c_str(), _O_RDWR | _O_CREAT | _O_BINARY, pmode);
#else
i64 fd = ::open(this->path.c_str(), O_RDWR | O_CREAT, perm);
#endif
if (fd == -1)
Fatal(ctx) << "cannot open " << this->path << ": " << errno_string();
#ifdef _WIN32
fp = _fdopen(fd, "wb");
#else
fp = fdopen(fd, "w");
#endif
}
fwrite(this->buf, this->filesize, 1, fp);
@@ -832,13 +837,13 @@ private:
class MultiGlob {
public:
bool add(std::string_view pat, u32 val);
bool add(std::string_view pat, i64 val);
bool empty() const { return strings.empty(); }
std::optional<u32> find(std::string_view str);
std::optional<i64> find(std::string_view str);
private:
struct TrieNode {
u32 value = -1;
i64 value = -1;
TrieNode *suffix_link = nullptr;
std::unique_ptr<TrieNode> children[256];
};
@@ -849,7 +854,7 @@ private:
std::vector<std::string> strings;
std::unique_ptr<TrieNode> root;
std::vector<std::pair<Glob, u32>> globs;
std::vector<std::pair<Glob, i64>> globs;
std::once_flag once;
bool is_compiled = false;
};
@@ -877,8 +882,8 @@ std::filesystem::path to_abs_path(std::filesystem::path path);
// demangle.cc
//
std::string_view demangle(std::string_view name);
std::optional<std::string_view> cpp_demangle(std::string_view name);
std::optional<std::string_view> demangle_cpp(std::string_view name);
std::optional<std::string_view> demangle_rust(std::string_view name);
//
// compress.cc
@@ -995,13 +1000,52 @@ MappedFile<Context> *MappedFile<Context>::open(Context &ctx, std::string path) {
if (path.starts_with('/') && !ctx.arg.chroot.empty())
path = ctx.arg.chroot + "/" + path_clean(path);
i64 fd;
#ifdef _WIN32
fd = ::_open(path.c_str(), O_RDONLY);
#else
fd = ::open(path.c_str(), O_RDONLY);
#endif
HANDLE file_handle =
CreateFileA(path.c_str(), GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (file_handle == INVALID_HANDLE_VALUE) {
auto err = GetLastError();
if (err != ERROR_FILE_NOT_FOUND)
Fatal(ctx) << "opening " << path << " failed: " << err;
return nullptr;
}
if (GetFileType(file_handle) != FILE_TYPE_DISK) {
CloseHandle(file_handle);
return nullptr;
}
DWORD size_hi;
DWORD size_lo = GetFileSize(file_handle, &size_hi);
if (size_lo == INVALID_FILE_SIZE)
Fatal(ctx) << path << ": GetFileSize failed: " << GetLastError();
u64 size = ((u64)size_hi << 32) + size_lo;
MappedFile *mf = new MappedFile;
ctx.mf_pool.push_back(std::unique_ptr<MappedFile>(mf));
mf->name = path;
mf->size = size;
mf->file_handle = file_handle;
if (size > 0) {
HANDLE mapping_handle = CreateFileMapping(file_handle, nullptr,
PAGE_READONLY, 0, size, nullptr);
if (!mapping_handle)
Fatal(ctx) << path << ": CreateFileMapping failed: " << GetLastError();
mf->data = (u8 *)MapViewOfFile(mapping_handle, FILE_MAP_COPY, 0, 0, size);
CloseHandle(mapping_handle);
if (!mf->data)
Fatal(ctx) << path << ": MapViewOfFile failed: " << GetLastError();
}
return mf;
#else
i64 fd = ::open(path.c_str(), O_RDONLY);
if (fd == -1) {
if (errno != ENOENT)
Fatal(ctx) << "opening " << path << " failed: " << errno_string();
@@ -1019,26 +1063,15 @@ MappedFile<Context> *MappedFile<Context>::open(Context &ctx, std::string path) {
mf->size = st.st_size;
if (st.st_size > 0) {
#ifdef _WIN32
HANDLE handle = CreateFileMapping((HANDLE)_get_osfhandle(fd),
nullptr, PAGE_READWRITE, 0,
st.st_size, nullptr);
if (!handle)
Fatal(ctx) << path << ": CreateFileMapping failed: " << GetLastError();
mf->file_handle = handle;
mf->data = (u8 *)MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, st.st_size);
if (!mf->data)
Fatal(ctx) << path << ": MapViewOfFile failed: " << GetLastError();
#else
mf->data = (u8 *)mmap(nullptr, st.st_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE, fd, 0);
if (mf->data == MAP_FAILED)
Fatal(ctx) << path << ": mmap failed: " << errno_string();
#endif
}
close(fd);
return mf;
#endif
}
template <typename Context>
-1
View File
@@ -1,3 +1,2 @@
#define MOLD_VERSION "@mold_VERSION@"
#define MOLD_LIBDIR "@CMAKE_INSTALL_FULL_LIBDIR@"
#cmakedefine01 MOLD_IS_SOLD
+10 -19
View File
@@ -10,25 +10,7 @@
namespace mold {
std::string_view demangle(std::string_view name) {
static thread_local char *p;
if (p)
free(p);
// Try to demangle as a Rust symbol. Since legacy-style Rust symbols
// are also valid as a C++ mangled name, we need to call this before
// cpp_demangle.
p = rust_demangle(std::string(name).c_str(), 0);
if (p)
return p;
// Try to demangle as a C++ symbol.
if (std::optional<std::string_view> s = cpp_demangle(name))
return *s;
return name;
}
std::optional<std::string_view> cpp_demangle(std::string_view name) {
std::optional<std::string_view> demangle_cpp(std::string_view name) {
static thread_local char *buf;
static thread_local size_t buflen;
@@ -48,4 +30,13 @@ std::optional<std::string_view> cpp_demangle(std::string_view name) {
return {};
}
std::optional<std::string_view> demangle_rust(std::string_view name) {
static thread_local char *buf;
free(buf);
buf = rust_demangle(std::string(name).c_str(), 0);
if (buf)
return buf;
return {};
}
} // namespace mold
+1 -1
View File
@@ -53,7 +53,7 @@ inline bool is_gcc_lto_obj(Context &ctx, MappedFile *mf) {
if (!ctx.arg.plugin.empty()) {
std::string_view name = data + shdrs[shstrtab_idx].sh_offset + sec.sh_name;
if (name.starts_with(".gnu.lto_.symtab."))
return true;
return true;
}
if (sec.sh_type != SHT_SYMTAB)
+9 -1
View File
@@ -79,6 +79,14 @@ std::optional<Glob> Glob::compile(std::string_view pat) {
case '*':
vec.push_back({STAR});
break;
case '\\':
if (pat.empty())
return {};
if (vec.empty() || vec.back().kind != STRING)
vec.push_back({STRING});
vec.back().str += pat[0];
pat = pat.substr(1);
break;
default:
if (vec.empty() || vec.back().kind != STRING)
vec.push_back({STRING});
@@ -101,7 +109,7 @@ bool Glob::do_match(std::string_view str, std::span<Element> elements) {
switch (e.kind) {
case STRING:
if (str.empty() || !str.starts_with(e.str))
if (!str.starts_with(e.str))
return false;
str = str.substr(e.str.size());
break;
+36 -18
View File
@@ -5,16 +5,26 @@
#include <filesystem>
#include <signal.h>
#include <tbb/global_control.h>
#include <tbb/version.h>
#ifdef USE_SYSTEM_MIMALLOC
#include <mimalloc-new-delete.h>
#endif
#ifdef __APPLE__
#include <mach-o/dyld.h>
#endif
#ifdef __FreeBSD__
# include <sys/sysctl.h>
# include <unistd.h>
#endif
#ifdef _WIN32
# define unlink _unlink
# define write _write
#endif
namespace mold {
std::string mold_version_string = MOLD_VERSION;
@@ -28,10 +38,10 @@ int main(int argc, char **argv);
}
static std::string get_mold_version() {
std::string name = MOLD_IS_SOLD ? "mold (sold) " : "mold ";
if (mold_git_hash.empty())
return name + MOLD_VERSION + " (compatible with GNU ld)";
return name + MOLD_VERSION + " (" + mold_git_hash + "; compatible with GNU ld)";
return "mold "s + MOLD_VERSION + " (compatible with GNU ld)";
return "mold "s + MOLD_VERSION + " (" + mold_git_hash +
"; compatible with GNU ld)";
}
void cleanup() {
@@ -48,7 +58,15 @@ std::string errno_string() {
// Returns the path of the mold executable itself
std::string get_self_path() {
#ifdef __FreeBSD__
#if __APPLE__
char path[8192];
u32 size = sizeof(path);
if (_NSGetExecutablePath(path, &size)) {
std::cerr << "_NSGetExecutablePath failed\n";
exit(1);
}
return path;
#elif __FreeBSD__
// /proc may not be mounted on FreeBSD. The proper way to get the
// current executable's path is to use sysctl(2).
int mib[4];
@@ -106,6 +124,8 @@ void install_signal_handler() {
#else
static std::string sigabrt_msg;
static void sighandler(int signo, siginfo_t *info, void *ucontext) {
static std::mutex mu;
std::scoped_lock lock{mu};
@@ -120,12 +140,7 @@ static void sighandler(int signo, siginfo_t *info, void *ucontext) {
}
break;
case SIGABRT: {
const char msg[] =
"mold: aborted\n"
"mold: If mold failed due to a spurious failure of pthread_create, "
"it's likely because of https://github.com/oneapi-src/oneTBB/pull/824. "
"You should ensure that you are using 2021.9.0 or newer version of libtbb.\n";
(void)!write(STDERR_FILENO, msg, sizeof(msg) - 1);
(void)!write(STDERR_FILENO, &sigabrt_msg[0], sigabrt_msg.size());
break;
}
}
@@ -139,10 +154,20 @@ void install_signal_handler() {
sigemptyset(&action.sa_mask);
action.sa_flags = SA_SIGINFO;
sigaction(SIGABRT, &action, NULL);
sigaction(SIGINT, &action, NULL);
sigaction(SIGTERM, &action, NULL);
sigaction(SIGBUS, &action, NULL);
// OneTBB 2021.9.0 has the interface version 12090.
if (TBB_runtime_interface_version() < 12090) {
sigabrt_msg = "mold: aborted\n"
"mold: mold with libtbb version 2021.9.0 or older is known to be unstable "
"under heavy load. Your libtbb version is " +
std::string(TBB_runtime_version()) +
". Please upgrade your libtbb library and try again.\n";
sigaction(SIGABRT, &action, NULL);
}
}
#endif
@@ -158,12 +183,5 @@ i64 get_default_thread_count() {
int main(int argc, char **argv) {
mold::mold_version = mold::get_mold_version();
#if MOLD_IS_SOLD
std::string cmd = mold::filepath(argv[0]).filename().string();
if (cmd == "ld64" || cmd.starts_with("ld64."))
return mold::macho::main(argc, argv);
#endif
return mold::elf::main(argc, argv);
}
+9 -9
View File
@@ -24,9 +24,9 @@
namespace mold {
std::optional<u32> MultiGlob::find(std::string_view str) {
std::optional<i64> MultiGlob::find(std::string_view str) {
std::call_once(once, [&] { compile(); });
u32 val = UINT32_MAX;
i64 val = -1;
if (root) {
// Match against simple glob patterns
@@ -36,7 +36,7 @@ std::optional<u32> MultiGlob::find(std::string_view str) {
for (;;) {
if (node->children[c]) {
node = node->children[c].get();
val = std::min(val, node->value);
val = std::max(val, node->value);
return;
}
@@ -53,11 +53,11 @@ std::optional<u32> MultiGlob::find(std::string_view str) {
}
// Match against complex glob patterns
for (std::pair<Glob, u32> &glob : globs)
for (std::pair<Glob, i64> &glob : globs)
if (glob.first.match(str))
val = std::min(val, glob.second);
val = std::max(val, glob.second);
if (val == UINT32_MAX)
if (val == -1)
return {};
return val;
}
@@ -82,7 +82,7 @@ static std::string handle_stars(std::string_view pat) {
return "\0"s + str + "\0"s;
}
bool MultiGlob::add(std::string_view pat, u32 val) {
bool MultiGlob::add(std::string_view pat, i64 val) {
assert(!is_compiled);
assert(!pat.empty());
@@ -108,7 +108,7 @@ bool MultiGlob::add(std::string_view pat, u32 val) {
node = node->children[c].get();
}
node->value = std::min(node->value, val);
node->value = std::max(node->value, val);
return true;
}
@@ -157,7 +157,7 @@ void MultiGlob::fix_values() {
for (std::unique_ptr<TrieNode> &child : node->children) {
if (!child)
continue;
child->value = std::min(child->value, child->suffix_link->value);
child->value = std::max(child->value, child->suffix_link->value);
queue.push(child.get());
}
} while (!queue.empty());
+8 -3
View File
@@ -39,11 +39,16 @@ open_or_create_file(Context &ctx, std::string path, i64 filesize, i64 perm) {
Fatal(ctx) << "cannot open " << path2 << ": " << errno_string();
}
if (ftruncate(fd, filesize))
Fatal(ctx) << "ftruncate failed: " << errno_string();
if (fchmod(fd, (perm & ~get_umask())) == -1)
Fatal(ctx) << "fchmod failed: " << errno_string();
#ifdef __linux__
if (fallocate(fd, 0, 0, filesize) == 0)
return {fd, path2};
#endif
if (ftruncate(fd, filesize) == -1)
Fatal(ctx) << "ftruncate failed: " << errno_string();
return {fd, path2};
}
+1 -1
View File
@@ -38,7 +38,7 @@ static i64 now_nsec() {
static std::pair<i64, i64> get_usage() {
#ifdef _WIN32
auto to_nsec = [](FILETIME t) -> i64 {
return ((u64)t.dwHighDateTime << 32 + (u64)t.dwLowDateTime) * 100;
return (((u64)t.dwHighDateTime << 32) + (u64)t.dwLowDateTime) * 100;
};
FILETIME creation, exit, kernel, user;
+147 -56
View File
@@ -1,78 +1,169 @@
#!/bin/bash
#
# This script creates a mold binary distribution. The output is
# written in this directory as `mold-$version-$arch-linux.tar.gz`
# (e.g. `mold-1.0.3-x86_64-linux.tar.gz`).
# This script creates a mold binary distribution. The output is written in
# this directory as `mold-$version-$arch-linux.tar.gz` (e.g.,
# `mold-1.0.3-x86_64-linux.tar.gz`).
#
# The mold executable created by this script is statically-linked to
# libstdc++ and libcrypto but dynamically-linked to libc, libm, libz
# and librt, as they almost always exist on any Linux systems.
# The mold executable created by this script is statically linked to
# libstdc++ but dynamically linked to libc, libm, libz, and librt, as
# these libraries almost always exist on any Linux system. We can't
# statically link libc because doing so would disable dlopen(), which is
# necessary to open the LTO linker plugin.
#
# This script aims to produce reproducible outputs. That means if you run
# the script twice on the same git commit, it should produce bit-by-bit
# identical binary files. This property is crucial as a countermeasure
# against supply chain attacks. With this, you can verify that the binary
# files distributed on the GitHub release pages are created from the
# commit with release tags by rebuilding the binaries yourself.
#
# Debian provides snapshot.debian.org to host all historical binary
# packages. We use it to construct Docker images pinned to a
# particular timestamp.
#
# We aim to use a reasonably old Debian version because we'll dynamically
# link glibc to mold, and a binary linked against a newer version of glibc
# won't work on a system with an older version of glibc.
#
# We need GCC 10 or newer to build mold. If GCC 10 is not available on an
# old Debian version, we'll build it ourselves.
#
# You may need to run the following command to use Docker with Qemu:
#
# $ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
set -e -x
cd "$(dirname $0)"
usage() {
echo "Usage: $0 [ x86_64 | aarch64 | arm | riscv64 | ppc64le | s390x ]"
exit 1
}
case $# in
0)
arch=$(uname -m)
[[ $arch = arm* ]] && arch=arm
if [ $arch = arm64 ]; then
arch=aarch64
elif [[ $arch = arm* ]]; then
arch=arm
fi
;;
1)
arch="$1"
;;
*)
echo "Usage: $0 [ x86_64 | aarch64 | arm | riscv64 | ppc64le | s390x ]"
exit 1
usage
esac
echo "$arch" | grep -Eq '^(x86_64|aarch64|arm|riscv64|ppc64le|s390x)$' || \
{ echo "Error: no docker image for $arch"; exit 1; }
echo "$arch" | grep -Eq '^(x86_64|aarch64|arm|riscv64|ppc64le|s390x)$' || usage
image=mold-builder-$arch:1
version=$(sed -n 's/^project(mold VERSION \(.*\))/\1/p' $(dirname $0)/CMakeLists.txt)
version=$(sed -n 's/^project(mold VERSION \(.*\))/\1/p' CMakeLists.txt)
dest=mold-$version-$arch-linux
# Create a Docker image if not exists.
#
# We want to use a reasonably old Linux distro because we'll dynamically
# link glibc to mold, and a binary linked against a newer version of
# glibc won't work on a system with a older version of glibc.
#
# We might want to instead statically-link everything, but that'll
# disable dlopen(), which means we can't use the linker plugin for LTO.
# So we don't want to do that.
if ! docker image ls $image | grep -q $image; then
if [ $arch = riscv64 ]; then
# Ubuntu 18 didn't support RISC-V, so use Ubuntu 20 instead
cat <<EOF | docker build --platform $arch -t $image -
FROM riscv64/ubuntu:20.04
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive TZ=UTC apt-get install -y --no-install-recommends build-essential gcc-10 g++-10 cmake && \
rm -rf /var/lib/apt/lists
EOF
else
cat <<EOF | docker build --platform $arch -t $image -
FROM ubuntu:18.04
RUN apt-get update && \
apt-get install -y --no-install-recommends software-properties-common && \
add-apt-repository -y ppa:ubuntu-toolchain-r/test && \
apt-get update && \
DEBIAN_FRONTEND=noninteractive TZ=UTC apt-get install -y --no-install-recommends build-essential wget libstdc++-11-dev gcc-10 g++-10 python3 libssl-dev && \
\
mkdir /cmake && cd /cmake && \
wget -O- -q https://github.com/Kitware/CMake/releases/download/v3.24.2/cmake-3.24.2.tar.gz | tar --strip-components=1 -xzf - && \
./bootstrap --parallel=$(nproc) && make -j$(nproc) && make -j$(nproc) install && \
rm -rf /var/lib/apt/lists /cmake
EOF
fi
if [ "$GITHUB_REPOSITORY" = '' ]; then
image=mold-builder-$arch
docker_build="docker build --platform linux/$arch -t $image -"
else
# If this script is running on GitHub Actions, we want to cache
# the created Docker image in GitHub's Docker repostiory.
image=ghcr.io/$GITHUB_REPOSITORY/mold-builder-$arch
docker_build="docker buildx build --platform linux/$arch -t $image --push --cache-to type=inline --cache-from type=registry,ref=ghcr.io/$GITHUB_REPOSITORY/mold-builder-$arch -"
fi
# Create a Docker image.
case $arch in
x86_64)
# Debian 8 (Jessie) released in April 2015
cat <<EOF | $docker_build
FROM debian:jessie-20210326@sha256:32ad5050caffb2c7e969dac873bce2c370015c2256ff984b70c1c08b3a2816a0
ENV DEBIAN_FRONTEND=noninteractive TZ=UTC
RUN sed -i -e '/^deb/d' -e 's/^# deb /deb [trusted=yes] /g' /etc/apt/sources.list && \
echo 'Acquire::Retries "10"; Acquire::http::timeout "10"; Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/80-retries && \
apt-get update && \
apt-get install -y --no-install-recommends wget bzip2 file make autoconf gcc g++ libssl-dev && \
rm -rf /var/lib/apt/lists
# Build CMake 3.27
RUN mkdir -p /build/cmake && \
cd /build/cmake && \
wget -O- --no-check-certificate https://cmake.org/files/v3.27/cmake-3.27.7.tar.gz | tar xzf - --strip-components=1 && \
./bootstrap --parallel=\$(nproc) && \
make -j\$(nproc) && \
make install && \
rm -rf /build
# Build GCC 10
RUN mkdir -p /build/gcc && \
cd /build/gcc && \
wget -O- http://ftp.gnu.org/gnu/gcc/gcc-10.5.0/gcc-10.5.0.tar.gz | tar xzf - --strip-components=1 && \
mkdir isl gmp mpc mpfr && \
wget -O- http://gcc.gnu.org/pub/gcc/infrastructure/isl-0.18.tar.bz2 | tar xjf - --strip-components=1 -C isl && \
wget -O- http://ftp.gnu.org/gnu/gmp/gmp-6.1.2.tar.bz2 | tar xjf - --strip-components=1 -C gmp && \
wget -O- http://ftp.gnu.org/gnu/mpc/mpc-1.2.1.tar.gz | tar xzf - --strip-components=1 -C mpc && \
wget -O- http://ftp.gnu.org/gnu/mpfr/mpfr-4.1.0.tar.gz | tar xzf - --strip-components=1 -C mpfr && \
./configure --prefix=/usr --enable-languages=c,c++ --disable-bootstrap --disable-multilib && \
make -j\$(nproc) && \
make install && \
ln -sf /usr/lib64/libstdc++.so.6 /usr/lib/x86_64-linux-gnu/libstdc++.so.6 && \
rm -rf /build
EOF
;;
aarch64 | arm | ppc64le | s390x)
# Debian 10 (Bullseye) released in July 2019
#
# We don't want to build GCC for these targets with Qemu becuase
# that'd take extremely long time. Also I believe old build machines
# are usually x86-64.
[ $arch = aarch64 ] && digest=d5ed76c5265576982e6599b6f12392290d9b52b315b19b28b640aaba6e8af002
[ $arch = arm ] && digest=bede2623dae269454c5b6dd4af15a10810a5f4ef75963d4eb6531628f98bd633
[ $arch = ppc64le ] && digest=255f385e735469493b3465befad59a16f9d46f41d0b50e4fa6d5928c5ee7702a
[ $arch = s390x ] && digest=96fb9ce5d3ce7f3dab7c34c18edfee093904cbc7fc19162dbcca22b2cc273b9d
cat <<EOF | $docker_build
FROM debian:bullseye-20231030@sha256:$digest
ENV DEBIAN_FRONTEND=noninteractive TZ=UTC
RUN sed -i -e '/^deb/d' -e 's/^# deb /deb [trusted=yes] /g' /etc/apt/sources.list && \
echo 'Acquire::Retries "10"; Acquire::http::timeout "10"; Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/80-retries && \
apt-get update && \
apt-get install -y --no-install-recommends build-essential gcc-10 g++-10 cmake && \
ln -sf /usr/bin/gcc-10 /usr/bin/cc && \
ln -sf /usr/bin/g++-10 /usr/bin/c++ && \
rm -rf /var/lib/apt/lists
EOF
;;
riscv64)
# snapshot.debian.org is not available for RISC-V binaries
cat <<EOF | $docker_build
FROM riscv64/debian:unstable-20231030@sha256:be1882409392c1f68f23e1e04bd965c49398f4a358b1a15f7b1820c0b39ede5b
ENV DEBIAN_FRONTEND=noninteractive TZ=UTC
RUN apt-get update && \
apt-get install -y --no-install-recommends build-essential gcc-12 g++-12 cmake && \
ln -sf /usr/bin/gcc-12 /usr/bin/cc && \
ln -sf /usr/bin/g++-12 /usr/bin/c++ && \
rm -rf /var/lib/apt/lists
EOF
;;
esac
# Source tarballs available on GitHub don't contain .git history.
# Clone the repo if missing.
[ -d .git ] || git clone --branch v$version --depth 1 --bare https://github.com/rui314/mold .git
# We use the timestamp of the last Git commit as the file timestamp
# for build artifacts.
timestamp="$(git log -1 --format=%ci)"
# Build mold in a container.
docker run --platform linux/$arch -i --rm -v "$(realpath $(dirname $0)):/mold" \
-e "OWNER=$(id -u):$(id -g)" $image \
bash -c "mkdir /build &&
cd /build &&
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 -DMOLD_MOSTLY_STATIC=On /mold &&
cmake --build . -j\$(nproc) &&
[ $arch = arm ] || ctest -j\$(nproc) &&
cmake --install . --prefix $dest --strip &&
tar czf /mold/$dest.tar.gz $dest &&
chown \$OWNER /mold/$dest.tar.gz"
docker run --platform linux/$arch -i --rm -v "$(pwd):/mold" $image bash -c "
set -e
mkdir -p /build/mold
cd /build/mold
cmake -DCMAKE_BUILD_TYPE=Release -DMOLD_MOSTLY_STATIC=On /mold
cmake --build . -j\$(nproc)
ctest -j\$(nproc)
cmake --install . --prefix $dest --strip
find $dest -print | xargs touch --no-dereference --date='$timestamp'
find $dest -print | sort | tar -cf - --no-recursion --files-from=- | gzip -9nc > /mold/$dest.tar.gz
chown $(id -u):$(id -g) /mold/$dest.tar.gz
"
+1
View File
@@ -0,0 +1 @@
<svg version="1.1" viewBox="0.0 0.0 600.0 371.0" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" width="600" height="371" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M0 0L600.0 0L600.0 371.0L0 371.0L0 0Z" fill-rule="nonzero"/><path stroke="#333333" stroke-width="1.0" stroke-linecap="butt" d="M82.5 275.5L581.5 275.5" fill-rule="nonzero"/><path stroke="#cccccc" stroke-width="1.0" stroke-linecap="butt" d="M82.5 231.5L581.5 231.5" fill-rule="nonzero"/><path stroke="#cccccc" stroke-width="1.0" stroke-linecap="butt" d="M82.5 186.5L581.5 186.5" fill-rule="nonzero"/><path stroke="#cccccc" stroke-width="1.0" stroke-linecap="butt" d="M82.5 142.5L581.5 142.5" fill-rule="nonzero"/><path stroke="#cccccc" stroke-width="1.0" stroke-linecap="butt" d="M82.5 98.5L581.5 98.5" fill-rule="nonzero"/><path stroke="#cccccc" stroke-width="1.0" stroke-linecap="butt" d="M82.5 53.5L581.5 53.5" fill-rule="nonzero"/><clipPath id="id_0"><path d="M82.55 53.916668L581.45 53.916668L581.45 275.45L82.55 275.45L82.55 53.916668Z" clip-rule="nonzero"/></clipPath><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" stroke-opacity="0.0" clip-path="url(#id_0)" d="M158.0 275.0L136.0 275.0L136.0 229.0C136.0 227.89543 136.89543 227.0 138.0 227.0L156.0 227.0C157.10457 227.0 158.0 227.89543 158.0 229.0Z" fill-rule="nonzero"/><path fill="#4285f4" clip-path="url(#id_0)" d="M158.0 275.0L136.0 275.0L136.0 229.0C136.0 227.89543 136.89543 227.0 138.0 227.0L156.0 227.0C157.10457 227.0 158.0 227.89543 158.0 229.0Z" fill-rule="nonzero"/><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" stroke-opacity="0.0" clip-path="url(#id_0)" d="M307.0 275.0L285.0 275.0L285.0 91.0C285.0 89.89543 285.89542 89.0 287.0 89.0L305.0 89.0C306.10458 89.0 307.0 89.89543 307.0 91.0Z" fill-rule="nonzero"/><path fill="#4285f4" clip-path="url(#id_0)" d="M307.0 275.0L285.0 275.0L285.0 91.0C285.0 89.89543 285.89542 89.0 287.0 89.0L305.0 89.0C306.10458 89.0 307.0 89.89543 307.0 91.0Z" fill-rule="nonzero"/><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" stroke-opacity="0.0" clip-path="url(#id_0)" d="M182.0 275.0L160.0 275.0L160.0 244.0C160.0 242.89543 160.89543 242.0 162.0 242.0L180.0 242.0C181.10457 242.0 182.0 242.89543 182.0 244.0Z" fill-rule="nonzero"/><path fill="#ea4335" clip-path="url(#id_0)" d="M182.0 275.0L160.0 275.0L160.0 244.0C160.0 242.89543 160.89543 242.0 162.0 242.0L180.0 242.0C181.10457 242.0 182.0 242.89543 182.0 244.0Z" fill-rule="nonzero"/><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" stroke-opacity="0.0" clip-path="url(#id_0)" d="M331.0 275.0L309.0 275.0L309.0 131.0C309.0 129.89543 309.89542 129.0 311.0 129.0L329.0 129.0C330.10458 129.0 331.0 129.89543 331.0 131.0Z" fill-rule="nonzero"/><path fill="#ea4335" clip-path="url(#id_0)" d="M331.0 275.0L309.0 275.0L309.0 131.0C309.0 129.89543 309.89542 129.0 311.0 129.0L329.0 129.0C330.10458 129.0 331.0 129.89543 331.0 131.0Z" fill-rule="nonzero"/><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" stroke-opacity="0.0" clip-path="url(#id_0)" d="M480.0 275.0L458.0 275.0L458.0 156.0C458.0 154.89543 458.89542 154.0 460.0 154.0L478.0 154.0C479.10458 154.0 480.0 154.89543 480.0 156.0Z" fill-rule="nonzero"/><path fill="#ea4335" clip-path="url(#id_0)" d="M480.0 275.0L458.0 275.0L458.0 156.0C458.0 154.89543 458.89542 154.0 460.0 154.0L478.0 154.0C479.10458 154.0 480.0 154.89543 480.0 156.0Z" fill-rule="nonzero"/><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" stroke-opacity="0.0" clip-path="url(#id_0)" d="M206.0 275.0L184.0 275.0L184.0 270.0C184.0 268.89542 184.89543 268.0 186.0 268.0L204.0 268.0C205.10457 268.0 206.0 268.89542 206.0 270.0Z" fill-rule="nonzero"/><path fill="#fbbc04" clip-path="url(#id_0)" d="M206.0 275.0L184.0 275.0L184.0 270.0C184.0 268.89542 184.89543 268.0 186.0 268.0L204.0 268.0C205.10457 268.0 206.0 268.89542 206.0 270.0Z" fill-rule="nonzero"/><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" stroke-opacity="0.0" clip-path="url(#id_0)" d="M355.0 275.0L333.0 275.0L333.0 254.0C333.0 252.89543 333.89542 252.0 335.0 252.0L353.0 252.0C354.10458 252.0 355.0 252.89543 355.0 254.0Z" fill-rule="nonzero"/><path fill="#fbbc04" clip-path="url(#id_0)" d="M355.0 275.0L333.0 275.0L333.0 254.0C333.0 252.89543 333.89542 252.0 335.0 252.0L353.0 252.0C354.10458 252.0 355.0 252.89543 355.0 254.0Z" fill-rule="nonzero"/><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" stroke-opacity="0.0" clip-path="url(#id_0)" d="M504.0 275.0L482.0 275.0L482.0 250.0C482.0 248.89543 482.89542 248.0 484.0 248.0L502.0 248.0C503.10458 248.0 504.0 248.89543 504.0 250.0Z" fill-rule="nonzero"/><path fill="#fbbc04" clip-path="url(#id_0)" d="M504.0 275.0L482.0 275.0L482.0 250.0C482.0 248.89543 482.89542 248.0 484.0 248.0L502.0 248.0C503.10458 248.0 504.0 248.89543 504.0 250.0Z" fill-rule="nonzero"/><path stroke="#000000" stroke-width="2.0" stroke-linecap="butt" stroLine truncated

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

+11 -5
View File
@@ -1,6 +1,6 @@
.\" generated with Ronn-NG/v0.9.1
.\" http://github.com/apjanke/ronn-ng/tree/0.9.1
.TH "MOLD" "1" "July 2023" ""
.TH "MOLD" "1" "November 2023" ""
.SH "NAME"
\fBmold\fR \- a modern linker
.SH "SYNOPSIS"
@@ -179,6 +179,9 @@ Omit \fB\.symtab\fR section from the output file\.
\fB\-u\fR \fIsymbol\fR, \fB\-\-undefined\fR=\fIsymbol\fR
If \fIsymbol\fR remains as an undefined symbol after reading all object files, and if there is a static archive that contains an object file defining \fIsymbol\fR, pull out the object file and link it so that the output file contains a definition of \fIsymbol\fR\.
.TP
\fB\-y\fR \fIsymbol\fR, \fB\-\-trace\-symbol\fR=\fIsymbol\fR
Trace references to \fIsymbol\fR\.
.TP
\fB\-\-Bdynamic\fR
Link against shared libraries\.
.TP
@@ -354,6 +357,9 @@ Create a share library\.
\fB\-\-spare\-dynamic\-tags\fR=\fInumber\fR
Reserve the given \fInumber\fR of tags in \fB\.dynamic\fR section\.
.TP
\fB\-\-spare\-program\-headers\fR=\fInumber\fR
Reserve the given \fInumber\fR of entries in the program header\.
.TP
\fB\-\-start\-lib\fR, \fB\-\-end\-lib\fR
Handle object files between \fB\-\-start\-lib\fR and \fB\-\-end\-lib\fR as if they were in an archive file\. That means object files between them are linked only when they are needed to resolve undefined symbols\. The options are useful if you want to link object files only when they are needed but want to avoid the overhead of running ar(3)\.
.TP
@@ -475,13 +481,13 @@ Ignored
.SH "ENVIRONMENT VARIABLES"
.TP
\fBMOLD_JOBS\fR
If this variable is set to \fB1\fR, only one process of \fBmold\fR runs actively\. A mold process invoked while another active mold process is running will wait before doing anything until the active process exits\.
If this variable is set to \fB1\fR, only one \fBmold\fR process will run at a time\. If a new mold process is initiated while another is already active, the new process will wait until the active one completes before starting\.
.IP
The purpose of this environment variable is to reduce peak memory usage\. Since mold is highly parallelized, there's no point in running it simultaneously\. If you run N instances of mold in parallel, it would take N times more time and N times more memory\. If you run them serially, it would still take N times more to finish, but their peak memory usage is reduced to normal\.
The primary reason for this environment variable is to minimize peak memory usage\. Since mold is designed to operate with high parallelism, running multiple mold instances simultaneously may not be beneficial\. If you execute N instances of mold concurrently, it could require N times the time and N times the memory\. On the other hand, running them one after the other might still take N times longer, but the peak memory usage would be the same as running just a single instance\.
.IP
If your build system tends to invoke multiple linker processes simultaneously, you may want to try to set this environment variable to \fB1\fR to see if it could improve overall performance\.
If your build system invokes multiple linker processes simultaneously and some of them often get killed due to out\-of\-memory errors, you might consider setting this environment variable to \fB1\fR to see if it addresses the OOM issue\.
.IP
Currently, any value other than 1 is silently ignored\.
Currently, any value other than \fB1\fR is silently ignored\.
.TP
\fBMOLD_DEBUG\fR
If this variable is set to a non\-empty string, \fBmold\fR embeds its command\-line options in the output file's \fB\.comment\fR section\.
+6
View File
@@ -321,6 +321,9 @@ arguments.
_symbol_, pull out the object file and link it so that the output file
contains a definition of _symbol_.
* `-y` _symbol_, `--trace-symbol`=_symbol_:
Trace references to _symbol_.
* `--Bdynamic`:
Link against shared libraries.
@@ -581,6 +584,9 @@ arguments.
* `--spare-dynamic-tags`=_number_:
Reserve the given _number_ of tags in `.dynamic` section.
* `--spare-program-headers`=_number_:
Reserve the given _number_ of entries in the program header.
* `--start-lib`, `--end-lib`:
Handle object files between `--start-lib` and `--end-lib` as if they were in
an archive file. That means object files between them are linked only when
+17 -4
View File
@@ -228,11 +228,24 @@ void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
*(ul32 *)(buf + 12) = sym.get_got_pltgot_addr(ctx) - sym.get_plt_addr(ctx) - 12;
}
// ARM does not use .eh_frame for exception handling. Instead, it uses
// .ARM.exidx and .ARM.extab. So this function is empty.
template <>
void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {}
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_NONE:
break;
case R_ARM_ABS32:
*(ul32 *)loc = val;
break;
case R_ARM_REL32:
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
break;
default:
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
}
// ARM and Thumb branch instructions can jump within ±16 MiB.
static bool is_jump_reachable(i64 val) {
@@ -647,7 +660,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
}
template <>
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
void Thunk<E>::copy_buf(Context<E> &ctx) {
// TLS trampoline code. ARM32's TLSDESC is designed so that this
// common piece of code is factored out from object files to reduce
// output size. Since no one provide, the linker has to synthesize it.
+1 -1
View File
@@ -602,7 +602,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
}
template <>
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
void Thunk<E>::copy_buf(Context<E> &ctx) {
static const ul32 insn[] = {
0x9000'0010, // adrp x16, 0 # R_AARCH64_ADR_PREL_PG_HI21
0x9100'0210, // add x16, x16 # R_AARCH64_ADD_ABS_LO12_NC
+47 -4
View File
@@ -254,6 +254,34 @@ static void relax_ld_to_le(u8 *loc, ElfRel<E> rel, u64 val) {
}
}
static u32 relax_tlsdesc_to_ie(u8 *loc) {
switch ((loc[0] << 8) | loc[1]) {
case 0x8d83: return 0x8b83; // lea 0(%ebx), %eax -> mov 0(%ebx), %eax
case 0x8d9b: return 0x8b9b; // lea 0(%ebx), %ebx -> mov 0(%ebx), %ebx
case 0x8d8b: return 0x8b8b; // lea 0(%ebx), %ecx -> mov 0(%ebx), %ecx
case 0x8d93: return 0x8b93; // lea 0(%ebx), %edx -> mov 0(%ebx), %edx
case 0x8db3: return 0x8bb3; // lea 0(%ebx), %esi -> mov 0(%ebx), %esi
case 0x8dbb: return 0x8bbb; // lea 0(%ebx), %edi -> mov 0(%ebx), %edi
case 0x8da3: return 0x8ba3; // lea 0(%ebx), %esp -> mov 0(%ebx), %esp
case 0x8dab: return 0x8bab; // lea 0(%ebx), %ebp -> mov 0(%ebx), %ebp
}
return 0;
}
static u32 relax_tlsdesc_to_le(u8 *loc) {
switch ((loc[0] << 8) | loc[1]) {
case 0x8d83: return 0x90b8; // lea 0(%ebx), %eax -> mov $0, %eax
case 0x8d9b: return 0x90bb; // lea 0(%ebx), %ebx -> mov $0, %ebx
case 0x8d8b: return 0x90b9; // lea 0(%ebx), %ecx -> mov $0, %ecx
case 0x8d93: return 0x90ba; // lea 0(%ebx), %edx -> mov $0, %edx
case 0x8db3: return 0x90be; // lea 0(%ebx), %esi -> mov $0, %esi
case 0x8dbb: return 0x90bf; // lea 0(%ebx), %edi -> mov $0, %edi
case 0x8da3: return 0x90bc; // lea 0(%ebx), %esp -> mov $0, %esp
case 0x8dab: return 0x90bd; // lea 0(%ebx), %ebp -> mov $0, %ebp
}
return 0;
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
@@ -373,15 +401,30 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
//
// mov $foo@TPOFF, %eax
// nop
//
// We allow the following alternative code sequence too because
// LLVM emits such code.
//
// lea 0(%ebx), %reg
// R_386_TLS_GOTDESC foo
// mov %reg, %eax
// call *(%eax)
// R_386_TLS_DESC_CALL foo
if (sym.has_tlsdesc(ctx)) {
*(ul32 *)loc = sym.get_tlsdesc_addr(ctx) + A - GOT;
} else if (sym.has_gottp(ctx)) {
loc[-2] = 0x8b;
loc[-1] = 0x83;
u32 insn = relax_tlsdesc_to_ie(loc - 2);
if (!insn)
Fatal(ctx) << *this << ": illegal instruction sequence for TLSDESC";
loc[-2] = insn >> 8;
loc[-1] = insn;
*(ul32 *)loc = sym.get_gottp_addr(ctx) + A - GOT;
} else {
loc[-2] = 0x90;
loc[-1] = 0xb8;
u32 insn = relax_tlsdesc_to_le(loc - 2);
if (!insn)
Fatal(ctx) << *this << ": illegal instruction sequence for TLSDESC";
loc[-2] = insn >> 8;
loc[-1] = insn;
*(ul32 *)loc = S + A - ctx.tp_addr;
}
break;
+1 -6
View File
@@ -569,11 +569,6 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (!sym.file) {
record_undef_error(ctx, rel);
continue;
}
if (sym.is_ifunc())
sym.flags |= NEEDS_GOT | NEEDS_PLT;
@@ -658,7 +653,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
}
template <>
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
void Thunk<E>::copy_buf(Context<E> &ctx) {
static const ul32 insn[] = {
0x1e00'000c, // pcaddu18i $t0, 0
0x4c00'0180, // jirl $zero, $t0, 0
+1 -1
View File
@@ -408,7 +408,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
}
template <>
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
void Thunk<E>::copy_buf(Context<E> &ctx) {
static const ub32 local_thunk[] = {
// Get this thunk's address
0x7c08'02a6, // mflr r0
+12 -9
View File
@@ -262,6 +262,9 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
case R_PPC64_DTPREL16_LO:
*(ub16 *)loc = lo(S + A - ctx.dtp_addr);
break;
case R_PPC64_DTPREL16_LO_DS:
*(ub16 *)loc |= (S + A - ctx.dtp_addr) & 0xfffc;
break;
case R_PPC64_TPREL16_HA:
*(ub16 *)loc = ha(S + A - ctx.tp_addr);
break;
@@ -401,6 +404,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
case R_PPC64_TLSLD:
case R_PPC64_DTPREL16_HA:
case R_PPC64_DTPREL16_LO:
case R_PPC64_DTPREL16_LO_DS:
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
@@ -409,7 +413,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
}
template <>
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
void Thunk<E>::copy_buf(Context<E> &ctx) {
// If the destination is .plt.got, we save the current r2, read an
// address of a function descriptor from .got, restore %r2 and jump
// to the function.
@@ -638,15 +642,14 @@ void ppc64v1_scan_symbols(Context<E> &ctx) {
});
// Functions referenced by the ELF header also have to have .opd entries.
auto mark = [&](std::string_view name) {
if (!name.empty())
if (Symbol<E> &sym = *get_symbol(ctx, name); !sym.is_imported)
sym.flags |= NEEDS_PPC_OPD;
};
if (!ctx.arg.entry->is_imported)
ctx.arg.entry->flags |= NEEDS_PPC_OPD;
mark(ctx.arg.entry);
mark(ctx.arg.init);
mark(ctx.arg.fini);
if (!ctx.arg.init->is_imported)
ctx.arg.init->flags |= NEEDS_PPC_OPD;
if (!ctx.arg.fini->is_imported)
ctx.arg.fini->flags |= NEEDS_PPC_OPD;
}
void PPC64OpdSection::add_symbol(Context<E> &ctx, Symbol<E> *sym) {
+105 -1
View File
@@ -319,6 +319,9 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
case R_PPC64_DTPREL16_LO:
*(ul16 *)loc = lo(S + A - ctx.dtp_addr);
break;
case R_PPC64_DTPREL16_LO_DS:
*(ul16 *)loc |= (S + A - ctx.dtp_addr) & 0xfffc;
break;
case R_PPC64_DTPREL34:
write34(loc, S + A - ctx.dtp_addr);
break;
@@ -475,6 +478,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
case R_PPC64_TLSLD:
case R_PPC64_DTPREL16_HA:
case R_PPC64_DTPREL16_LO:
case R_PPC64_DTPREL16_LO_DS:
case R_PPC64_DTPREL34:
break;
default:
@@ -484,7 +488,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
}
template <>
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
void Thunk<E>::copy_buf(Context<E> &ctx) {
// If the destination is PLT, we read an address from .got.plt or .got
// and jump there.
static const ul32 plt_thunk[] = {
@@ -564,6 +568,106 @@ void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
}
}
// GCC may emit references to the following functions in function prologue
// and epiilogue if -Os is specified. For some reason, these functions are
// not in libgcc.a and expected to be synthesized by the linker.
const std::vector<std::pair<std::string_view, u32>>
ppc64_save_restore_insns = {
{ "_savegpr0_14", 0xf9c1ff70 }, // std r14,-144(r1)
{ "_savegpr0_15", 0xf9e1ff78 }, // std r15,-136(r1)
{ "_savegpr0_16", 0xfa01ff80 }, // std r16,-128(r1)
{ "_savegpr0_17", 0xfa21ff88 }, // std r17,-120(r1)
{ "_savegpr0_18", 0xfa41ff90 }, // std r18,-112(r1)
{ "_savegpr0_19", 0xfa61ff98 }, // std r19,-104(r1)
{ "_savegpr0_20", 0xfa81ffa0 }, // std r20,-96(r1)
{ "_savegpr0_21", 0xfaa1ffa8 }, // std r21,-88(r1)
{ "_savegpr0_22", 0xfac1ffb0 }, // std r22,-80(r1)
{ "_savegpr0_23", 0xfae1ffb8 }, // std r23,-72(r1)
{ "_savegpr0_24", 0xfb01ffc0 }, // std r24,-64(r1)
{ "_savegpr0_25", 0xfb21ffc8 }, // std r25,-56(r1)
{ "_savegpr0_26", 0xfb41ffd0 }, // std r26,-48(r1)
{ "_savegpr0_27", 0xfb61ffd8 }, // std r27,-40(r1)
{ "_savegpr0_28", 0xfb81ffe0 }, // std r28,-32(r1)
{ "_savegpr0_29", 0xfba1ffe8 }, // std r29,-24(r1)
{ "_savegpr0_30", 0xfbc1fff0 }, // std r30,-16(r1)
{ "_savegpr0_31", 0xfbe1fff8 }, // std r31,-8(r1)
{ "", 0xf8010010 }, // std r0,16(r1)
{ "", 0x4e800020 }, // blr
{ "_restgpr0_14", 0xe9c1ff70 }, // ld r14,-144(r1)
{ "_restgpr0_15", 0xe9e1ff78 }, // ld r15,-136(r1)
{ "_restgpr0_16", 0xea01ff80 }, // ld r16,-128(r1)
{ "_restgpr0_17", 0xea21ff88 }, // ld r17,-120(r1)
{ "_restgpr0_18", 0xea41ff90 }, // ld r18,-112(r1)
{ "_restgpr0_19", 0xea61ff98 }, // ld r19,-104(r1)
{ "_restgpr0_20", 0xea81ffa0 }, // ld r20,-96(r1)
{ "_restgpr0_21", 0xeaa1ffa8 }, // ld r21,-88(r1)
{ "_restgpr0_22", 0xeac1ffb0 }, // ld r22,-80(r1)
{ "_restgpr0_23", 0xeae1ffb8 }, // ld r23,-72(r1)
{ "_restgpr0_24", 0xeb01ffc0 }, // ld r24,-64(r1)
{ "_restgpr0_25", 0xeb21ffc8 }, // ld r25,-56(r1)
{ "_restgpr0_26", 0xeb41ffd0 }, // ld r26,-48(r1)
{ "_restgpr0_27", 0xeb61ffd8 }, // ld r27,-40(r1)
{ "_restgpr0_28", 0xeb81ffe0 }, // ld r28,-32(r1)
{ "_restgpr0_29", 0xe8010010 }, // ld r0,16(r1)
{ "", 0xeba1ffe8 }, // ld r29,-24(r1)
{ "", 0x7c0803a6 }, // mtlr r0
{ "", 0xebc1fff0 }, // ld r30,-16(r1)
{ "", 0xebe1fff8 }, // ld r31,-8(r1)
{ "", 0x4e800020 }, // blr
{ "_restgpr0_30", 0xebc1fff0 }, // ld r30,-16(r1)
{ "_restgpr0_31", 0xe8010010 }, // ld r0,16(r1)
{ "", 0xebe1fff8 }, // ld r31,-8(r1)
{ "", 0x7c0803a6 }, // mtlr r0
{ "", 0x4e800020 }, // blr
{ "_savegpr1_14", 0xf9ccff70 }, // std r14,-144(r12)
{ "_savegpr1_15", 0xf9ecff78 }, // std r15,-136(r12)
{ "_savegpr1_16", 0xfa0cff80 }, // std r16,-128(r12)
{ "_savegpr1_17", 0xfa2cff88 }, // std r17,-120(r12)
{ "_savegpr1_18", 0xfa4cff90 }, // std r18,-112(r12)
{ "_savegpr1_19", 0xfa6cff98 }, // std r19,-104(r12)
{ "_savegpr1_20", 0xfa8cffa0 }, // std r20,-96(r12)
{ "_savegpr1_21", 0xfaacffa8 }, // std r21,-88(r12)
{ "_savegpr1_22", 0xfaccffb0 }, // std r22,-80(r12)
{ "_savegpr1_23", 0xfaecffb8 }, // std r23,-72(r12)
{ "_savegpr1_24", 0xfb0cffc0 }, // std r24,-64(r12)
{ "_savegpr1_25", 0xfb2cffc8 }, // std r25,-56(r12)
{ "_savegpr1_26", 0xfb4cffd0 }, // std r26,-48(r12)
{ "_savegpr1_27", 0xfb6cffd8 }, // std r27,-40(r12)
{ "_savegpr1_28", 0xfb8cffe0 }, // std r28,-32(r12)
{ "_savegpr1_29", 0xfbacffe8 }, // std r29,-24(r12)
{ "_savegpr1_30", 0xfbccfff0 }, // std r30,-16(r12)
{ "_savegpr1_31", 0xfbecfff8 }, // std r31,-8(r12)
{ "", 0x4e800020 }, // blr
{ "_restgpr1_14", 0xe9ccff70 }, // ld r14,-144(r12)
{ "_restgpr1_15", 0xe9ecff78 }, // ld r15,-136(r12)
{ "_restgpr1_16", 0xea0cff80 }, // ld r16,-128(r12)
{ "_restgpr1_17", 0xea2cff88 }, // ld r17,-120(r12)
{ "_restgpr1_18", 0xea4cff90 }, // ld r18,-112(r12)
{ "_restgpr1_19", 0xea6cff98 }, // ld r19,-104(r12)
{ "_restgpr1_20", 0xea8cffa0 }, // ld r20,-96(r12)
{ "_restgpr1_21", 0xeaacffa8 }, // ld r21,-88(r12)
{ "_restgpr1_22", 0xeaccffb0 }, // ld r22,-80(r12)
{ "_restgpr1_23", 0xeaecffb8 }, // ld r23,-72(r12)
{ "_restgpr1_24", 0xeb0cffc0 }, // ld r24,-64(r12)
{ "_restgpr1_25", 0xeb2cffc8 }, // ld r25,-56(r12)
{ "_restgpr1_26", 0xeb4cffd0 }, // ld r26,-48(r12)
{ "_restgpr1_27", 0xeb6cffd8 }, // ld r27,-40(r12)
{ "_restgpr1_28", 0xeb8cffe0 }, // ld r28,-32(r12)
{ "_restgpr1_29", 0xebacffe8 }, // ld r29,-24(r12)
{ "_restgpr1_30", 0xebccfff0 }, // ld r30,-16(r12)
{ "_restgpr1_31", 0xebecfff8 }, // ld r31,-8(r12)
{ "", 0x4e800020 }, // blr
};
void PPC64SaveRestoreSection::copy_buf(Context<E> &ctx) {
ul32 *buf = (ul32 *)(ctx.buf + this->shdr.sh_offset);
for (auto [label, insn] : ppc64_save_restore_insns)
*buf++ = insn;
}
template <>
u64 get_eflags(Context<E> &ctx) {
return 2;
+118 -10
View File
@@ -363,9 +363,55 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
}
break;
}
case R_RISCV_GOT_HI20:
write_utype(loc, G + GOT + A - P);
case R_RISCV_GOT_HI20: {
// This relocation usually refers to an AUIPC + LD instruction
// pair to load a symbol value from the GOT. If the symbol value
// is actually a link-time constant, we can materialize the value
// directly into a register to eliminate a memory load.
i64 rd = get_rd(rel.r_offset);
switch (removed_bytes) {
case 6:
// c.li <rd>, val
*(ul16 *)loc = 0b010'0'00000'00000'01 | (rd << 7);
write_citype(loc, sym.get_addr(ctx));
i += 3;
break;
case 4:
// addi <rd>, zero, val
*(ul32 *)loc = 0b0010011 | (rd << 7);
write_itype(loc, sym.get_addr(ctx));
i += 3;
break;
case 0:
if (ctx.arg.relax &&
sym.is_pcrel_linktime_const(ctx) &&
i + 3 < rels.size() &&
rels[i + 1].r_type == R_RISCV_RELAX &&
rels[i + 2].r_type == R_RISCV_PCREL_LO12_I &&
rels[i + 2].r_offset == rels[i].r_offset + 4 &&
file.symbols[rels[i + 2].r_sym]->value == r_offset &&
rels[i + 3].r_type == R_RISCV_RELAX) {
i64 val = S + A - P;
if (rd == get_rd(rel.r_offset + 4) && (i32)val == val) {
// auipc <rd>, %hi20(val)
write_utype(loc, val);
// addi <rd>, <rd>, %lo12(val)
*(ul32 *)(loc + 4) = 0b0010011 | (rd << 15) | (rd << 7);
write_itype(loc + 4, val);
i += 3;
break;
}
}
write_utype(loc, G + GOT + A - P);
break;
default:
unreachable();
}
break;
}
case R_RISCV_TLS_GOT_HI20:
write_utype(loc, sym.get_gottp_addr(ctx) + A - P);
break;
@@ -466,12 +512,49 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
break;
}
case R_RISCV_TLSDESC_HI20:
// RISC-V TLSDESC uses the following code sequence to materialize
// a TP-relative address in x0.
//
// .L0:
// auipc tX, 0
// R_RISCV_TLSDESC_HI20 foo
// l[d|w] tY, tX, 0
// R_RISCV_TLSDESC_LOAD_LO12_I .L0
// addi a0, tX, 0
// R_RISCV_TLSDESC_ADD_LO12_I .L0
// jalr t0, tY
// R_RISCV_TLSDESC_CALL .L0
//
// For non-dlopen'd DSO, we may relax the instructions to the following:
//
// <deleted>
// <deleted>
// auipc a0, %gottp_hi(a0)
// l[d|w] a0, %gottp_lo(a0)
//
// For executable, if the TP offset is small enough, we'll relax
// it to the following:
//
// <deleted>
// <deleted>
// <deleted>
// addi a0, zero, %tpoff_lo(a0)
//
// Otherwise, the following sequence is used:
//
// <deleted>
// <deleted>
// lui a0, %tpoff_hi(a0)
// addi a0, a0, %tpoff_lo(a0)
if (removed_bytes == 0)
write_utype(loc, sym.get_tlsdesc_addr(ctx) + A - P);
break;
case R_RISCV_TLSDESC_LOAD_LO12:
case R_RISCV_TLSDESC_ADD_LO12:
case R_RISCV_TLSDESC_CALL: {
if (removed_bytes == 4)
break;
i64 idx2 = find_paired_reloc();
const ElfRel<E> &rel2 = rels[idx2];
Symbol<E> &sym2 = *file.symbols[rel2.r_sym];
@@ -482,27 +565,24 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
switch (rel.r_type) {
case R_RISCV_TLSDESC_LOAD_LO12:
if (sym2.has_tlsdesc(ctx))
write_itype(loc, sym2.get_tlsdesc_addr(ctx) + A - P);
write_itype(loc, sym2.get_tlsdesc_addr(ctx) + A - P);
break;
case R_RISCV_TLSDESC_ADD_LO12:
if (sym2.has_tlsdesc(ctx)) {
write_itype(loc, sym2.get_tlsdesc_addr(ctx) + A - P);
} else if (sym2.has_gottp(ctx)) {
*(ul32 *)loc = 0x517; // auipc a0,<hi20>
*(ul32 *)loc = 0x517; // auipc a0,<hi20>
write_utype(loc, sym2.get_gottp_addr(ctx) + A - P);
} else {
if (removed_bytes == 0) {
*(ul32 *)loc = 0x537; // lui a0,<hi20>
write_utype(loc, S + A - ctx.tp_addr);
}
*(ul32 *)loc = 0x537; // lui a0,<hi20>
write_utype(loc, S + A - ctx.tp_addr);
}
break;
case R_RISCV_TLSDESC_CALL:
if (sym2.has_tlsdesc(ctx)) {
// Do nothing
} else if (sym2.has_gottp(ctx)) {
// {ld,lw} a0, <lo12>(a0)
// l[d|w] a0,<lo12>
*(ul32 *)loc = E::is_64 ? 0x53503 : 0x52503;
write_itype(loc, sym2.get_gottp_addr(ctx) + A - P);
} else {
@@ -949,6 +1029,34 @@ static void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc)
}
break;
}
case R_RISCV_GOT_HI20: {
// A GOT_HI20 followed by a PCREL_LO12_I is used to load a value from
// GOT. If the loaded value is a link-time constant, we can rewrite
// the instructions to directly materialize the value, eliminating a
// memory load.
if (sym.is_absolute() &&
i + 3 < rels.size() &&
rels[i + 1].r_type == R_RISCV_RELAX &&
rels[i + 2].r_type == R_RISCV_PCREL_LO12_I &&
rels[i + 2].r_offset == rels[i].r_offset + 4 &&
isec.file.symbols[rels[i + 2].r_sym]->value == rels[i].r_offset &&
rels[i + 3].r_type == R_RISCV_RELAX) {
i64 rd = get_rd(r.r_offset);
if (rd == get_rd(r.r_offset + 4)) {
u64 val = sym.get_addr(ctx) + r.r_addend;
if (use_rvc && rd != 0 && sign_extend(val, 5) == val) {
// Replace AUIPC + LD with C.LI.
delta += 6;
} else if (sign_extend(val, 11) == val) {
// Replace AUIPC + LD with ADDI.
delta += 4;
}
}
}
break;
}
case R_RISCV_HI20: {
u64 val = sym.get_addr(ctx) + r.r_addend;
i64 rd = get_rd(r.r_offset);
+65 -8
View File
@@ -178,6 +178,50 @@ static u32 relax_gottpoff(u8 *loc) {
return 0;
}
static u32 relax_tlsdesc_to_ie(u8 *loc) {
switch ((loc[0] << 16) | (loc[1] << 8) | loc[2]) {
case 0x488d05: return 0x488b05; // lea 0(%rip), %rax -> mov 0(%rip), %rax
case 0x488d0d: return 0x488b0d; // lea 0(%rip), %rcx -> mov 0(%rip), %rcx
case 0x488d15: return 0x488b15; // lea 0(%rip), %rdx -> mov 0(%rip), %rdx
case 0x488d1d: return 0x488b1d; // lea 0(%rip), %rbx -> mov 0(%rip), %rbx
case 0x488d25: return 0x488b25; // lea 0(%rip), %rsp -> mov 0(%rip), %rsp
case 0x488d2d: return 0x488b2d; // lea 0(%rip), %rbp -> mov 0(%rip), %rbp
case 0x488d35: return 0x488b35; // lea 0(%rip), %rsi -> mov 0(%rip), %rsi
case 0x488d3d: return 0x488b3d; // lea 0(%rip), %rdi -> mov 0(%rip), %rdi
case 0x4c8d05: return 0x4c8b05; // lea 0(%rip), %r8 -> mov 0(%rip), %r8
case 0x4c8d0d: return 0x4c8b0d; // lea 0(%rip), %r9 -> mov 0(%rip), %r9
case 0x4c8d15: return 0x4c8b15; // lea 0(%rip), %r10 -> mov 0(%rip), %r10
case 0x4c8d1d: return 0x4c8b1d; // lea 0(%rip), %r11 -> mov 0(%rip), %r11
case 0x4c8d25: return 0x4c8b25; // lea 0(%rip), %r12 -> mov 0(%rip), %r12
case 0x4c8d2d: return 0x4c8b2d; // lea 0(%rip), %r13 -> mov 0(%rip), %r13
case 0x4c8d35: return 0x4c8b35; // lea 0(%rip), %r14 -> mov 0(%rip), %r14
case 0x4c8d3d: return 0x4c8b3d; // lea 0(%rip), %r15 -> mov 0(%rip), %r15
}
return 0;
}
static u32 relax_tlsdesc_to_le(u8 *loc) {
switch ((loc[0] << 16) | (loc[1] << 8) | loc[2]) {
case 0x488d05: return 0x48c7c0; // lea 0(%rip), %rax -> mov $0, %rax
case 0x488d0d: return 0x48c7c1; // lea 0(%rip), %rcx -> mov $0, %rcx
case 0x488d15: return 0x48c7c2; // lea 0(%rip), %rdx -> mov $0, %rdx
case 0x488d1d: return 0x48c7c3; // lea 0(%rip), %rbx -> mov $0, %rbx
case 0x488d25: return 0x48c7c4; // lea 0(%rip), %rsp -> mov $0, %rsp
case 0x488d2d: return 0x48c7c5; // lea 0(%rip), %rbp -> mov $0, %rbp
case 0x488d35: return 0x48c7c6; // lea 0(%rip), %rsi -> mov $0, %rsi
case 0x488d3d: return 0x48c7c7; // lea 0(%rip), %rdi -> mov $0, %rdi
case 0x4c8d05: return 0x49c7c0; // lea 0(%rip), %r8 -> mov $0, %r8
case 0x4c8d0d: return 0x49c7c1; // lea 0(%rip), %r9 -> mov $0, %r9
case 0x4c8d15: return 0x49c7c2; // lea 0(%rip), %r10 -> mov $0, %r10
case 0x4c8d1d: return 0x49c7c3; // lea 0(%rip), %r11 -> mov $0, %r11
case 0x4c8d25: return 0x49c7c4; // lea 0(%rip), %r12 -> mov $0, %r12
case 0x4c8d2d: return 0x49c7c5; // lea 0(%rip), %r13 -> mov $0, %r13
case 0x4c8d35: return 0x49c7c6; // lea 0(%rip), %r14 -> mov $0, %r14
case 0x4c8d3d: return 0x49c7c7; // lea 0(%rip), %r15 -> mov $0, %r15
}
return 0;
}
// Rewrite a function call to __tls_get_addr to a cheaper instruction
// sequence. We can do this when we know the thread-local variable's TP-
// relative address at link-time.
@@ -501,19 +545,32 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
//
// mov $foo@TPOFF, %rax
// nop
//
// We allow the following alternative code sequence too because
// LLVM emits such code.
//
// lea 0(%rip), %reg
// R_X86_64_GOTPC32_TLSDESC foo
// mov %reg, %rax
// call *(%rax)
// R_X86_64_TLSDESC_CALL foo
if (sym.has_tlsdesc(ctx)) {
write32s(sym.get_tlsdesc_addr(ctx) + A - P);
} else if (sym.has_gottp(ctx)) {
// mov foo@gottpoff(%rip), %rax
loc[-3] = 0x48;
loc[-2] = 0x8b;
loc[-1] = 0x05;
u32 insn = relax_tlsdesc_to_ie(loc - 3);
if (!insn)
Fatal(ctx) << *this << ": illegal instruction sequence for TLSDESC";
loc[-3] = insn >> 16;
loc[-2] = insn >> 8;
loc[-1] = insn;
write32s(sym.get_gottp_addr(ctx) + A - P);
} else {
// mov $foo@tpoff, %rax
loc[-3] = 0x48;
loc[-2] = 0xc7;
loc[-1] = 0xc0;
u32 insn = relax_tlsdesc_to_le(loc - 3);
if (!insn)
Fatal(ctx) << *this << ": illegal instruction sequence for TLSDESC";
loc[-3] = insn >> 16;
loc[-2] = insn >> 8;
loc[-1] = insn;
write32s(S - ctx.tp_addr);
}
break;
+72 -55
View File
@@ -9,7 +9,7 @@
#include <unordered_set>
#ifdef _WIN32
# define _isatty isatty
# define isatty _isatty
# define STDERR_FILENO (_fileno(stderr))
#else
# include <unistd.h>
@@ -34,7 +34,7 @@ Options:
--no-dynamic-linker
-L DIR, --library-path DIR Add DIR to library search path
-M, --print-map Write map file to stdout
-N, --omagic Do not page align data, do not make text readonly
-N, --omagic Do not page align data; do not make text readonly
--no-omagic
-O NUMBER Ignored
-S, --strip-debug Strip .debug_* sections
@@ -52,6 +52,8 @@ Options:
-s, --strip-all Strip .symtab section
-u SYMBOL, --undefined SYMBOL
Force to resolve SYMBOL
-y SYMBOL, --trace-symbol SYMBOL
Trace references to SYMBOL
--Bdynamic, --dy Link against shared libraries (default)
--Bstatic, --dn, --static Do not link against shared libraries
--Bsymbolic Bind global symbols locally
@@ -59,17 +61,17 @@ Options:
--Bno-symbolic Cancel --Bsymbolic and --Bsymbolic-functions
--Map FILE Write map file to a given file
--Tbss=ADDR Set address to .bss
--Tdata Set address to .data
--Ttext Set address to .text
--Tdata=ADDR Set address to .data
--Ttext=ADDR Set address to .text
--allow-multiple-definition Allow multiple definitions
--apply-dynamic-relocs Apply link-time values for dynamic relocations (defualt)
--apply-dynamic-relocs Apply link-time values for dynamic relocations (default)
--no-apply-dynamic-relocs
--as-needed Only set DT_NEEDED if used
--no-as-needed
--build-id [none,md5,sha1,sha256,uuid,HEXSTRING]
Generate build ID
--no-build-id
--chroot DIR Set a given path to root directory
--chroot DIR Set a given path to the root directory
--color-diagnostics=[auto,always,never]
Use colors in diagnostics
--color-diagnostics Alias for --color-diagnostics=always
@@ -84,12 +86,12 @@ Options:
--disable-new-dtags Emit DT_RPATH for --rpath
--execute-only Make executable segments unreadable
--dp Ignored
--dynamic-list Read a list of dynamic symbols (implies -Bsymbolic)
--dynamic-list=FILE Read a list of dynamic symbols (implies -Bsymbolic)
--eh-frame-hdr Create .eh_frame_hdr section
--no-eh-frame-hdr
--exclude-libs LIB,LIB,.. Mark all symbols in given libraries hidden
--exclude-libs LIB,LIB,.. Mark all symbols in given libraries as hidden
--export-dynamic-symbol Put symbols matching glob in the dynamic symbol table
--export-dynamic-symbol-list
--export-dynamic-symbol-list=FILE
Read a list of dynamic symbols
--fatal-warnings Treat warnings as errors
--no-fatal-warnings Do not treat warnings as errors (default)
@@ -107,54 +109,58 @@ Options:
Allow merging non-executable sections with --icf
--image-base ADDR Set the base address to a given value
--init SYMBOL Call SYMBOL at load-time
--nmagic Do not page align sections
--no-nmagic
--no-undefined Report undefined symbols (even with --shared)
--noinhibit-exec Create an output file even if errors occur
--oformat=binary Omit ELF, section and program headers
--oformat=binary Omit ELF, section, and program headers
--pack-dyn-relocs=[relr,none]
Pack dynamic relocations
--package-metadata=STRING Set a given string to .note.package
--perf Print performance statistics
--pie, --pic-executable Create a position independent executable
--pie, --pic-executable Create a position-independent executable
--no-pie, --no-pic-executable
--pop-state Restore state of flags governing input file handling
--pop-state Restore the state of flags governing input file handling
--print-gc-sections Print removed unreferenced sections
--no-print-gc-sections
--print-icf-sections Print folded identical sections
--no-print-icf-sections
--push-state Save state of flags governing input file handling
--push-state Save the state of flags governing input file handling
--quick-exit Use quick_exit to exit (default)
--no-quick-exit
--relax Optimize instructions (default)
--no-relax
--repro Embed input files to .repro section
--repro Embed input files in .repro section
--require-defined SYMBOL Require SYMBOL be defined in the final output
--retain-symbols-file FILE Keep only symbols listed in FILE
--reverse-sections Reverses input sections in the output file
--reverse-sections Reverse input sections in the output file
--rosegment Put read-only non-executable sections in their own segment (default)
--no-rosegment Put read-only non-executable sections in an executable segment
--rpath DIR Add DIR to runtime search path
--rpath DIR Add DIR to the runtime search path
--rpath-link DIR Ignored
--run COMMAND ARG... Run COMMAND with mold as /usr/bin/ld
--section-start=SECTION=ADDR Set address to section
--shared, --Bshareable Create a share library
--section-start=SECTION=ADDR Set address for section
--shared, --Bshareable Create a shared library
--shuffle-sections[=SEED] Randomize the output by shuffling input sections
--sort-common Ignored
--sort-section Ignored
--spare-dynamic-tags NUMBER Reserve give number of tags in .dynamic section
--spare-dynamic-tags NUMBER Reserve the given number of tags in the .dynamic section
--spare-program-headers NUMBER
Reserve the given number of slots in the program header
--start-lib Give following object files in-archive-file semantics
--end-lib End the effect of --start-lib
--stats Print input statistics
--sysroot DIR Set target system root directory
--sysroot DIR Set the target system root directory
--thread-count COUNT, --threads=COUNT
Use COUNT number of threads
--threads Use multiple threads (default)
--no-threads
--trace Print name of each input file
--undefined-version Do not report version scripts that refer undefined symbols
--no-undefined-version Report version scripts that refer undefined symbols (default)
--trace Print the name of each input file
--undefined-version Do not report version scripts that refer to undefined symbols
--no-undefined-version Report version scripts that refer to undefined symbols (default)
--unique PATTERN Don't merge input sections that match a given pattern
--unresolved-symbols [report-all,ignore-all,ignore-in-object-files,ignore-in-shared-libs]
How to handle unresolved symbols
Handle unresolved symbols
--version-script FILE Read version script
--warn-common Warn about common symbols
--no-warn-common
@@ -166,21 +172,21 @@ Options:
Report unresolved symbols as errors (default)
--whole-archive Include all objects from static archives
--no-whole-archive
--wrap SYMBOL Use wrapper function for a given symbol
--wrap SYMBOL Use a wrapper function for a given symbol
-z defs Report undefined symbols (even with --shared)
-z nodefs
-z common-page-size=VALUE Ignored
-z execstack Require executable stack
-z execstack Require an executable stack
-z noexecstack
-z execstack-if-needed Make the stack area execuable if an input file explicitly requests it
-z execstack-if-needed Make the stack area executable if an input file explicitly requests it
-z initfirst Mark DSO to be initialized first at runtime
-z interpose Mark object to interpose all DSOs but executable
-z interpose Mark object to interpose all DSOs but the executable
-z keep-text-section-prefix Keep .text.{hot,unknown,unlikely,startup,exit} as separate sections in the final binary
-z nokeep-text-section-prefix
-z lazy Enable lazy function resolution (default)
-z max-page-size=VALUE Use VALUE as the memory page size
-z nocopyreloc Do not create copy relocations
-z nodefaultlib Make the dynamic loader to ignore default search paths
-z nodefaultlib Make the dynamic loader ignore default search paths
-z nodelete Mark DSO non-deletable at runtime
-z nodlopen Mark DSO not available to dlopen
-z nodump Mark DSO not available to dldump
@@ -191,10 +197,10 @@ Options:
-z sectionheader Do not omit section header (default)
-z nosectionheader Omit section header
-z separate-loadable-segments
Separate all loadable segments to different pages
-z separate-code Separate code and data into different pages
Separate all loadable segments onto different pages
-z separate-code Separate code and data onto different pages
-z noseparate-code Allow overlap in pages
-z stack-size=VALUE Set size of stack segment
-z stack-size=VALUE Set the size of the stack segment
-z relro Make some sections read-only after relocation (default)
-z norelro
-z text Report error if DT_TEXTREL is set
@@ -431,7 +437,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
ctx.arg.apply_dynamic_relocs = false;
auto read_arg = [&](std::string name) {
for (std::string opt : add_dashes(name)) {
for (const std::string &opt : add_dashes(name)) {
if (args[0] == opt) {
if (args.size() == 1)
Fatal(ctx) << "option -" << name << ": argument missing";
@@ -451,7 +457,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
};
auto read_eq = [&](std::string name) {
for (std::string opt : add_dashes(name)) {
for (const std::string &opt : add_dashes(name)) {
if (args[0].starts_with(opt + "=")) {
arg = args[0].substr(opt.size() + 1);
args = args.subspan(1);
@@ -462,7 +468,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
};
auto read_flag = [&](std::string name) {
for (std::string opt : add_dashes(name)) {
for (const std::string &opt : add_dashes(name)) {
if (args[0] == opt) {
args = args.subspan(1);
return true;
@@ -586,7 +592,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
ctx.arg.emit_relocs = true;
ctx.arg.discard_locals = false;
} else if (read_arg("e") || read_arg("entry")) {
ctx.arg.entry = arg;
ctx.arg.entry = get_symbol(ctx, arg);
} else if (read_arg("Map")) {
ctx.arg.Map = arg;
ctx.arg.print_map = true;
@@ -604,6 +610,9 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
ctx.arg.shared = true;
} else if (read_arg("spare-dynamic-tags")) {
ctx.arg.spare_dynamic_tags = parse_number(ctx, "spare-dynamic-tags", arg);
} else if (read_arg("spare-program-headers")) {
ctx.arg.spare_program_headers
= parse_number(ctx, "spare-program-headers", arg);
} else if (read_flag("start-lib")) {
remaining.push_back("--start-lib");
} else if (read_flag("start-stop")) {
@@ -660,13 +669,13 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
else
Fatal(ctx) << "unknown --unresolved-symbols argument: " << arg;
} else if (read_arg("undefined") || read_arg("u")) {
ctx.arg.undefined.push_back(arg);
ctx.arg.undefined.push_back(get_symbol(ctx, arg));
} else if (read_arg("require-defined")) {
ctx.arg.require_defined.push_back(arg);
ctx.arg.require_defined.push_back(get_symbol(ctx, arg));
} else if (read_arg("init")) {
ctx.arg.init = arg;
ctx.arg.init = get_symbol(ctx, arg);
} else if (read_arg("fini")) {
ctx.arg.fini = arg;
ctx.arg.fini = get_symbol(ctx, arg);
} else if (read_arg("hash-style")) {
if (arg == "sysv") {
ctx.arg.hash_style_sysv = true;
@@ -887,6 +896,10 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
ctx.arg.z_rewrite_endbr = true;
} else if (read_flag("no-undefined")) {
ctx.arg.z_defs = true;
} else if (read_flag("nmagic")) {
ctx.arg.nmagic = true;
} else if (read_flag("no-nmagic")) {
ctx.arg.nmagic = false;
} else if (read_flag("fatal-warnings")) {
ctx.arg.fatal_warnings = true;
} else if (read_flag("no-fatal-warnings")) {
@@ -1104,21 +1117,21 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
} else if (read_flag("no-keep-memory")) {
} else if (read_arg("max-cache-size")) {
} else if (read_arg("version-script")) {
// --version-script, --dynamic-list and --export-dynamic-symbol[-list]
// are treated as positional arguments even though they are actually not
// positional. This is because linker scripts (a positional argument)
// can also specify a version script, and it's better to consolidate
// parsing in read_input_files. In particular, version scripts can
// modify ctx.default_version which we initialize *after* parsing
// non-positional args, so the parsing cannot be done right here.
// --version-script is treated as positional arguments even though
// they are actually not positional. This is because linker scripts
// (a positional argument) can also specify a version script, and
// it's better to consolidate parsing in read_input_files. In
// particular, version scripts can modify ctx.default_version which
// we initialize *after* parsing non-positional args, so the parsing
// cannot be done right here.
remaining.push_back("--version-script=" + std::string(arg));
} else if (read_arg("dynamic-list")) {
ctx.arg.Bsymbolic = true;
remaining.push_back("--dynamic-list=" + std::string(arg));
append(ctx.dynamic_list_patterns, parse_dynamic_list(ctx, arg));
} else if (read_arg("export-dynamic-symbol")) {
remaining.push_back("--export-dynamic-symbol=" + std::string(arg));
ctx.dynamic_list_patterns.push_back({arg, "<command line>"});
} else if (read_arg("export-dynamic-symbol-list")) {
remaining.push_back("--export-dynamic-symbol-list=" + std::string(arg));
append(ctx.dynamic_list_patterns, parse_dynamic_list(ctx, arg));
} else if (read_flag("as-needed")) {
remaining.push_back("--as-needed");
} else if (read_flag("no-as-needed")) {
@@ -1200,6 +1213,9 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
else if (!ctx.arg.section_order.empty())
ctx.arg.z_relro = false;
if (ctx.arg.nmagic)
ctx.arg.z_relro = false;
if (!ctx.arg.shared) {
if (!ctx.arg.filter.empty())
Fatal(ctx) << "-filter may not be used without -shared";
@@ -1228,11 +1244,6 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
if (char *env = getenv("MOLD_REPRO"); env && env[0])
ctx.arg.repro = true;
if (ctx.arg.shared || ctx.arg.export_dynamic)
ctx.default_version = VER_NDX_GLOBAL;
else
ctx.default_version = VER_NDX_LOCAL;
if (ctx.arg.default_symver) {
std::string ver = ctx.arg.soname.empty() ?
filepath(ctx.arg.output).filename().string() : std::string(ctx.arg.soname);
@@ -1245,6 +1256,12 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
ctx.arg.undefined.push_back(ctx.arg.entry);
for (i64 i = 0; i < ctx.arg.defsyms.size(); i++) {
std::variant<Symbol<E> *, u64> &val = ctx.arg.defsyms[i].second;
if (Symbol<E> **sym = std::get_if<Symbol<E> *>(&val))
ctx.arg.undefined.push_back(*sym);
}
// --oformat=binary implies --strip-all because without a section
// header, there's no way to identify the locations of a symbol
// table in an output file in the first place.
+18 -17
View File
@@ -161,6 +161,7 @@ enum : u32 {
VER_NDX_LOCAL = 0,
VER_NDX_GLOBAL = 1,
VER_NDX_LAST_RESERVED = 1,
VER_NDX_UNSPECIFIED = 0xffff,
};
enum : u32 {
@@ -1739,7 +1740,7 @@ struct ElfSym<ARM64> {
u8 st_bind : 4;
u8 st_type : 4;
u8 arm64_variant_pcs : 1;
u8 : 6;
u8 : 5;
u8 st_visibility : 2;
#endif
@@ -1868,7 +1869,7 @@ struct X86_64 {
static constexpr u32 plt_hdr_size = 32;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 8;
static constexpr u8 filler[] = { 0xcc };
static constexpr u8 filler[] = { 0xcc }; // int3
static constexpr u32 R_COPY = R_X86_64_COPY;
static constexpr u32 R_GLOB_DAT = R_X86_64_GLOB_DAT;
@@ -1893,7 +1894,7 @@ struct I386 {
static constexpr u32 plt_hdr_size = 16;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 16;
static constexpr u8 filler[] = { 0xcc };
static constexpr u8 filler[] = { 0xcc }; // int3
static constexpr u32 R_COPY = R_386_COPY;
static constexpr u32 R_GLOB_DAT = R_386_GLOB_DAT;
@@ -1920,7 +1921,7 @@ struct ARM64 {
static constexpr u32 pltgot_size = 16;
static constexpr u32 thunk_hdr_size = 0;
static constexpr u32 thunk_size = 16;
static constexpr u8 filler[] = { 0x00, 0x7d, 0x20, 0xd4 };
static constexpr u8 filler[] = { 0x00, 0x7d, 0x20, 0xd4 }; // brk
static constexpr u32 R_COPY = R_AARCH64_COPY;
static constexpr u32 R_GLOB_DAT = R_AARCH64_GLOB_DAT;
@@ -1947,7 +1948,7 @@ struct ARM32 {
static constexpr u32 pltgot_size = 16;
static constexpr u32 thunk_hdr_size = 16;
static constexpr u32 thunk_size = 16;
static constexpr u8 filler[] = { 0xff, 0xde };
static constexpr u8 filler[] = { 0xff, 0xde }; // udf
static constexpr u32 R_COPY = R_ARM_COPY;
static constexpr u32 R_GLOB_DAT = R_ARM_GLOB_DAT;
@@ -1973,7 +1974,7 @@ struct RV64 {
static constexpr u32 plt_hdr_size = 32;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 16;
static constexpr u8 filler[] = { 0x02, 0x90 };
static constexpr u8 filler[] = { 0x02, 0x90 }; // ebreak
static constexpr u32 R_COPY = R_RISCV_COPY;
static constexpr u32 R_GLOB_DAT = R_RISCV_64;
@@ -2006,7 +2007,7 @@ struct RV32 {
static constexpr u32 plt_hdr_size = 32;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 16;
static constexpr u8 filler[] = { 0x02, 0x90 };
static constexpr u8 filler[] = { 0x02, 0x90 }; // ebreak
static constexpr u32 R_COPY = R_RISCV_COPY;
static constexpr u32 R_GLOB_DAT = R_RISCV_32;
@@ -2043,7 +2044,7 @@ struct PPC32 {
static constexpr u32 pltgot_size = 36;
static constexpr u32 thunk_hdr_size = 0;
static constexpr u32 thunk_size = 36;
static constexpr u8 filler[] = { 0x7f, 0xe0, 0x00, 0x08 };
static constexpr u8 filler[] = { 0x7f, 0xe0, 0x00, 0x08 }; // trap
static constexpr u32 R_COPY = R_PPC_COPY;
static constexpr u32 R_GLOB_DAT = R_PPC_GLOB_DAT;
@@ -2085,7 +2086,7 @@ struct PPC64V1 : PPC64 {
static constexpr u32 pltgot_size = 0;
static constexpr u32 thunk_hdr_size = 0;
static constexpr u32 thunk_size = 28;
static constexpr u8 filler[] = { 0x7f, 0xe0, 0x00, 0x08 };
static constexpr u8 filler[] = { 0x7f, 0xe0, 0x00, 0x08 }; // trap
};
struct PPC64V2 : PPC64 {
@@ -2096,7 +2097,7 @@ struct PPC64V2 : PPC64 {
static constexpr u32 pltgot_size = 0;
static constexpr u32 thunk_hdr_size = 0;
static constexpr u32 thunk_size = 24;
static constexpr u8 filler[] = { 0x08, 0x00, 0xe0, 0x7f };
static constexpr u8 filler[] = { 0x08, 0x00, 0xe0, 0x7f }; // trap
};
struct S390X {
@@ -2109,7 +2110,7 @@ struct S390X {
static constexpr u32 plt_hdr_size = 48;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 16;
static constexpr u8 filler[] = { 0x07, 0x00 };
static constexpr u8 filler[] = { 0x07, 0x00 }; // nop
static constexpr u32 R_COPY = R_390_COPY;
static constexpr u32 R_GLOB_DAT = R_390_GLOB_DAT;
@@ -2133,7 +2134,7 @@ struct SPARC64 {
static constexpr u32 plt_hdr_size = 128;
static constexpr u32 plt_size = 32;
static constexpr u32 pltgot_size = 32;
static constexpr u8 filler[] = { 0x91, 0xd0, 0x20, 0x05 };
static constexpr u8 filler[] = { 0x91, 0xd0, 0x20, 0x05 }; // ta 5
static constexpr u32 R_COPY = R_SPARC_COPY;
static constexpr u32 R_GLOB_DAT = R_SPARC_GLOB_DAT;
@@ -2157,7 +2158,7 @@ struct M68K {
static constexpr u32 plt_hdr_size = 18;
static constexpr u32 plt_size = 14;
static constexpr u32 pltgot_size = 8;
static constexpr u8 filler[] = { 0x4a, 0xfc };
static constexpr u8 filler[] = { 0x4a, 0xfc }; // illegal
static constexpr u32 R_COPY = R_68K_COPY;
static constexpr u32 R_GLOB_DAT = R_68K_GLOB_DAT;
@@ -2180,7 +2181,7 @@ struct SH4 {
static constexpr u32 plt_hdr_size = 16;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 12;
static constexpr u8 filler[] = { 0x00, 0x00 };
static constexpr u8 filler[] = { 0x00, 0x90 }; // nop
static constexpr u32 R_COPY = R_SH_COPY;
static constexpr u32 R_GLOB_DAT = R_SH_GLOB_DAT;
@@ -2203,7 +2204,7 @@ struct ALPHA {
static constexpr u32 plt_hdr_size = 0;
static constexpr u32 plt_size = 0;
static constexpr u32 pltgot_size = 0;
static constexpr u8 filler[] = { 0x81, 0x00, 0x00, 0x00 };
static constexpr u8 filler[] = { 0x81, 0x00, 0x00, 0x00 }; // bugchk
static constexpr u32 R_COPY = R_ALPHA_COPY;
static constexpr u32 R_GLOB_DAT = R_ALPHA_GLOB_DAT;
@@ -2228,7 +2229,7 @@ struct LOONGARCH64 {
static constexpr u32 pltgot_size = 16;
static constexpr u32 thunk_hdr_size = 0;
static constexpr u32 thunk_size = 8;
static constexpr u8 filler[] = { 0x00, 0x00, 0x2a, 0x00 };
static constexpr u8 filler[] = { 0x00, 0x00, 0x2a, 0x00 }; // break 0
static constexpr u32 R_COPY = R_LARCH_COPY;
static constexpr u32 R_GLOB_DAT = R_LARCH_64;
@@ -2254,7 +2255,7 @@ struct LOONGARCH32 {
static constexpr u32 pltgot_size = 16;
static constexpr u32 thunk_hdr_size = 0;
static constexpr u32 thunk_size = 8;
static constexpr u8 filler[] = { 0x00, 0x00, 0x2a, 0x00 };
static constexpr u8 filler[] = { 0x00, 0x00, 0x2a, 0x00 }; // break 0
static constexpr u32 R_COPY = R_LARCH_COPY;
static constexpr u32 R_GLOB_DAT = R_LARCH_32;
+4 -6
View File
@@ -114,13 +114,11 @@ static void collect_root_set(Context<E> &ctx,
});
// Add sections referenced by root symbols.
enqueue_symbol(get_symbol(ctx, ctx.arg.entry));
for (Symbol<E> *sym : ctx.arg.undefined)
enqueue_symbol(sym);
for (std::string_view name : ctx.arg.undefined)
enqueue_symbol(get_symbol(ctx, name));
for (std::string_view name : ctx.arg.require_defined)
enqueue_symbol(get_symbol(ctx, name));
for (Symbol<E> *sym : ctx.arg.require_defined)
enqueue_symbol(sym);
// .eh_frame consists of variable-length records called CIE and FDE
// records, and they are a unit of inclusion or exclusion.
+1 -1
View File
@@ -141,7 +141,7 @@ struct NameType {
struct MapValue {
u32 hash = 0;
Atomic<u32> count = 0;
Atomic<u32> count;
u32 name_offset = 0;
u32 type_offset = 0;
};
+155 -89
View File
@@ -10,6 +10,48 @@
namespace mold::elf {
// If we haven't seen the same `key` before, create a new instance
// of Symbol and returns it. Otherwise, returns the previously-
// instantiated object. `key` is usually the same as `name`.
template <typename E>
Symbol<E> *get_symbol(Context<E> &ctx, std::string_view key,
std::string_view name) {
typename decltype(ctx.symbol_map)::const_accessor acc;
ctx.symbol_map.insert(acc, {key, Symbol<E>(name)});
return const_cast<Symbol<E> *>(&acc->second);
}
template <typename E>
Symbol<E> *get_symbol(Context<E> &ctx, std::string_view key) {
std::string_view name = key.substr(0, key.find('@'));
return get_symbol(ctx, key, name);
}
template <typename E>
static bool is_rust_symbol(const Symbol<E> &sym) {
// The legacy Rust mangling scheme is indistinguishtable from C++.
// We don't want to accidentally demangle C++ symbols as Rust ones.
// So, the legacy mangling scheme will be demangled only when we
// know the object file was created by rustc.
if (sym.file && !sym.file->is_dso && ((ObjectFile<E> *)sym.file)->is_rust_obj)
return true;
// "_R" is the prefix of the new Rust mangling scheme.
return sym.name().starts_with("_R");
}
template <typename E>
std::string_view demangle(const Symbol<E> &sym) {
if (is_rust_symbol(sym)) {
if (std::optional<std::string_view> s = demangle_rust(sym.name()))
return *s;
} else {
if (std::optional<std::string_view> s = demangle_cpp(sym.name()))
return *s;
}
return sym.name();
}
template <typename E>
InputFile<E>::InputFile(Context<E> &ctx, MappedFile<Context<E>> *mf)
: mf(mf), filename(mf->name) {
@@ -63,7 +105,7 @@ void InputFile<E>::clear_symbols() {
sym->origin = 0;
sym->value = -1;
sym->sym_idx = -1;
sym->ver_idx = 0;
sym->ver_idx = VER_NDX_UNSPECIFIED;
sym->is_weak = false;
sym->is_imported = false;
sym->is_exported = false;
@@ -196,6 +238,13 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
// Read sections
for (i64 i = 0; i < this->elf_sections.size(); i++) {
const ElfShdr<E> &shdr = this->elf_sections[i];
std::string_view name = this->shstrtab.data() + shdr.sh_name;
if ((shdr.sh_flags & SHF_EXCLUDE) &&
name.starts_with(".gnu.offload_lto_.symtab.")) {
this->is_gcc_offload_obj = true;
continue;
}
if ((shdr.sh_flags & SHF_EXCLUDE) && !(shdr.sh_flags & SHF_ALLOC) &&
shdr.sh_type != SHT_LLVM_ADDRSIG && !ctx.arg.relocatable)
@@ -255,9 +304,7 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
case SHT_STRTAB:
case SHT_NULL:
break;
default: {
std::string_view name = this->shstrtab.data() + shdr.sh_name;
default:
// .note.GNU-stack section controls executable-ness of the stack
// area in GNU linkers. We ignore that section because silently
// making the stack area executable is too dangerous. Tell our
@@ -299,6 +346,10 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
is_debug_section(shdr, name))
continue;
if (name == ".comment" &&
this->get_string(ctx, shdr).starts_with("rustc "))
this->is_rust_obj = true;
// If an output file doesn't have a section header (i.e.
// --oformat=binary is given), we discard all non-memory-allocated
// sections. This is because without a section header, we can't find
@@ -310,12 +361,26 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
// Save .llvm_addrsig for --icf=safe.
if (shdr.sh_type == SHT_LLVM_ADDRSIG && !ctx.arg.relocatable) {
llvm_addrsig = std::move(this->sections[i]);
if (shdr.sh_link != 0) {
llvm_addrsig = std::move(this->sections[i]);
} else {
Warn(ctx) << *this << ": ignoring .llvm_addrsig section without"
<< " sh_link; was the file processed by strip or objcopy -r?";
}
continue;
}
if (shdr.sh_type == SHT_INIT_ARRAY ||
shdr.sh_type == SHT_FINI_ARRAY ||
shdr.sh_type == SHT_PREINIT_ARRAY)
ctx.has_init_array = true;
if (name == ".ctors" || name.starts_with(".ctors.") ||
name == ".dtors" || name.starts_with(".dtors."))
ctx.has_ctors = true;
if (name == ".eh_frame")
eh_frame_section = this->sections[i].get();
eh_frame_sections.push_back(this->sections[i].get());
if constexpr (is_ppc32<E>)
if (name == ".got2")
@@ -356,7 +421,6 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
counter++;
break;
}
}
}
// Attach relocation sections to their target sections.
@@ -407,78 +471,75 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
// This function parses an input .eh_frame section.
template <typename E>
void ObjectFile<E>::parse_ehframe(Context<E> &ctx) {
if (!eh_frame_section)
return;
for (InputSection<E> *isec : eh_frame_sections) {
std::span<ElfRel<E>> rels = isec->get_rels(ctx);
i64 cies_begin = cies.size();
i64 fdes_begin = fdes.size();
InputSection<E> &isec = *eh_frame_section;
std::span<ElfRel<E>> rels = isec.get_rels(ctx);
i64 cies_begin = cies.size();
i64 fdes_begin = fdes.size();
// Read CIEs and FDEs until empty.
std::string_view contents = this->get_string(ctx, isec->shdr());
i64 rel_idx = 0;
// Read CIEs and FDEs until empty.
std::string_view contents = this->get_string(ctx, isec.shdr());
i64 rel_idx = 0;
for (std::string_view data = contents; !data.empty();) {
i64 size = *(U32<E> *)data.data();
if (size == 0)
break;
for (std::string_view data = contents; !data.empty();) {
i64 size = *(U32<E> *)data.data();
if (size == 0)
break;
i64 begin_offset = data.data() - contents.data();
i64 end_offset = begin_offset + size + 4;
i64 id = *(U32<E> *)(data.data() + 4);
data = data.substr(size + 4);
i64 begin_offset = data.data() - contents.data();
i64 end_offset = begin_offset + size + 4;
i64 id = *(U32<E> *)(data.data() + 4);
data = data.substr(size + 4);
i64 rel_begin = rel_idx;
while (rel_idx < rels.size() && rels[rel_idx].r_offset < end_offset)
rel_idx++;
assert(rel_idx == rels.size() || begin_offset <= rels[rel_begin].r_offset);
i64 rel_begin = rel_idx;
while (rel_idx < rels.size() && rels[rel_idx].r_offset < end_offset)
rel_idx++;
assert(rel_idx == rels.size() || begin_offset <= rels[rel_begin].r_offset);
if (id == 0) {
// This is CIE.
cies.emplace_back(ctx, *this, *isec, begin_offset, rels, rel_begin);
} else {
// This is FDE.
if (rel_begin == rel_idx || rels[rel_begin].r_sym == 0) {
// FDE has no valid relocation, which means FDE is dead from
// the beginning. Compilers usually don't create such FDE, but
// `ld -r` tend to generate such dead FDEs.
continue;
}
if (id == 0) {
// This is CIE.
cies.emplace_back(ctx, *this, isec, begin_offset, rels, rel_begin);
} else {
// This is FDE.
if (rel_begin == rel_idx || rels[rel_begin].r_sym == 0) {
// FDE has no valid relocation, which means FDE is dead from
// the beginning. Compilers usually don't create such FDE, but
// `ld -r` tend to generate such dead FDEs.
continue;
if (rels[rel_begin].r_offset - begin_offset != 8)
Fatal(ctx) << *isec << ": FDE's first relocation should have offset 8";
fdes.emplace_back(begin_offset, rel_begin);
}
}
if (rels[rel_begin].r_offset - begin_offset != 8)
Fatal(ctx) << isec << ": FDE's first relocation should have offset 8";
// Associate CIEs to FDEs.
auto find_cie = [&](i64 offset) {
for (i64 i = cies_begin; i < cies.size(); i++)
if (cies[i].input_offset == offset)
return i;
Fatal(ctx) << *isec << ": bad FDE pointer";
};
fdes.emplace_back(begin_offset, rel_begin);
for (i64 i = fdes_begin; i < fdes.size(); i++) {
i64 cie_offset = *(I32<E> *)(contents.data() + fdes[i].input_offset + 4);
fdes[i].cie_idx = find_cie(fdes[i].input_offset + 4 - cie_offset);
}
}
// Associate CIEs to FDEs.
auto find_cie = [&](i64 offset) {
for (i64 i = cies_begin; i < cies.size(); i++)
if (cies[i].input_offset == offset)
return i;
Fatal(ctx) << isec << ": bad FDE pointer";
};
for (i64 i = fdes_begin; i < fdes.size(); i++) {
i64 cie_offset = *(I32<E> *)(contents.data() + fdes[i].input_offset + 4);
fdes[i].cie_idx = find_cie(fdes[i].input_offset + 4 - cie_offset);
}
auto get_isec = [&](const FdeRecord<E> &fde) -> InputSection<E> * {
return get_section(this->elf_syms[rels[fde.rel_idx].r_sym]);
auto get_isec = [&](const FdeRecord<E> &fde) {
return get_section(this->elf_syms[fde.get_rels(*this)[0].r_sym]);
};
// We assume that FDEs for the same input sections are contiguous
// in `fdes` vector.
std::stable_sort(fdes.begin() + fdes_begin, fdes.end(),
[&](const FdeRecord<E> &a, const FdeRecord<E> &b) {
sort(fdes, [&](const FdeRecord<E> &a, const FdeRecord<E> &b) {
return get_isec(a)->get_priority() < get_isec(b)->get_priority();
});
// Associate FDEs to input sections.
for (i64 i = fdes_begin; i < fdes.size();) {
for (i64 i = 0; i < fdes.size();) {
InputSection<E> *isec = get_isec(fdes[i]);
assert(isec->fde_begin == -1);
isec->fde_begin = i++;
@@ -489,29 +550,9 @@ void ObjectFile<E>::parse_ehframe(Context<E> &ctx) {
}
}
// Returns a symbol object for a given key. This function handles
// the -wrap option.
template <typename E>
static Symbol<E> *insert_symbol(Context<E> &ctx, const ElfSym<E> &esym,
std::string_view key, std::string_view name) {
if (esym.is_undef() && name.starts_with("__real_") &&
ctx.arg.wrap.contains(name.substr(7))) {
return get_symbol(ctx, key.substr(7), name.substr(7));
}
Symbol<E> *sym = get_symbol(ctx, key, name);
if (esym.is_undef() && sym->is_wrapped) {
key = save_string(ctx, "__wrap_" + std::string(key));
name = save_string(ctx, "__wrap_" + std::string(name));
return get_symbol(ctx, key, name);
}
return sym;
}
template <typename E>
void ObjectFile<E>::initialize_symbols(Context<E> &ctx) {
if (!symtab_sec)
if (this->elf_syms.empty())
return;
static Counter counter("all_syms");
@@ -555,6 +596,9 @@ void ObjectFile<E>::initialize_symbols(Context<E> &ctx) {
for (i64 i = this->first_global; i < this->elf_syms.size(); i++) {
const ElfSym<E> &esym = this->elf_syms[i];
if (esym.is_common())
has_common_symbol = true;
// Get a symbol name
std::string_view key = this->symbol_strtab.data() + esym.st_name;
std::string_view name = key;
@@ -571,9 +615,21 @@ void ObjectFile<E>::initialize_symbols(Context<E> &ctx) {
}
}
this->symbols[i] = insert_symbol(ctx, esym, key, name);
if (esym.is_common())
has_common_symbol = true;
// Handle --wrap option
Symbol<E> *sym;
if (esym.is_undef() && name.starts_with("__real_") &&
ctx.arg.wrap.contains(name.substr(7))) {
sym = get_symbol(ctx, key.substr(7), name.substr(7));
} else {
sym = get_symbol(ctx, key, name);
if (esym.is_undef() && sym->is_wrapped) {
key = save_string(ctx, "__wrap_" + std::string(key));
name = save_string(ctx, "__wrap_" + std::string(name));
sym = get_symbol(ctx, key, name);
}
}
this->symbols[i] = sym;
}
}
@@ -644,9 +700,13 @@ split_section(Context<E> &ctx, InputSection<E> &sec) {
if (entsize == 0)
return nullptr;
i64 addralign = shdr.sh_addralign;
if (addralign == 0)
addralign = 1;
std::unique_ptr<MergeableSection<E>> rec(new MergeableSection<E>);
rec->parent = MergedSection<E>::get_instance(ctx, sec.name(), shdr.sh_type,
shdr.sh_flags, entsize);
shdr.sh_flags, entsize, addralign);
rec->p2align = sec.p2align;
if (sec.sh_size == 0)
@@ -1277,17 +1337,17 @@ void SharedFile<E>::parse(Context<E> &ctx) {
continue;
std::string_view name = this->symbol_strtab.data() + esyms[i].st_name;
bool is_hidden = (!vers.empty() && (vers[i] & VERSYM_HIDDEN));
bool is_default = vers.empty() || !(vers[i] & VERSYM_HIDDEN);
this->elf_syms2.push_back(esyms[i]);
this->versyms.push_back(ver);
if (is_hidden) {
if (is_default) {
this->symbols.push_back(get_symbol(ctx, name));
} else {
std::string_view mangled_name = save_string(
ctx, std::string(name) + "@" + std::string(version_strings[ver]));
this->symbols.push_back(get_symbol(ctx, mangled_name, name));
} else {
this->symbols.push_back(get_symbol(ctx, name));
}
}
@@ -1342,6 +1402,9 @@ std::vector<std::string_view> SharedFile<E>::read_verdef(Context<E> &ctx) {
ElfVerdef<E> *ver = (ElfVerdef<E> *)verdef.data();
for (;;) {
if (ver->vd_ndx == VER_NDX_UNSPECIFIED)
Fatal(ctx) << *this << ": symbol version too large";
if (ret.size() <= ver->vd_ndx)
ret.resize(ver->vd_ndx + 1);
@@ -1371,7 +1434,7 @@ void SharedFile<E>::resolve_symbols(Context<E> &ctx) {
sym.value = esym.st_value;
sym.sym_idx = i;
sym.ver_idx = versyms[i];
sym.is_weak = false;
sym.is_weak = true;
}
}
}
@@ -1499,6 +1562,9 @@ using E = MOLD_TARGET;
template class InputFile<E>;
template class ObjectFile<E>;
template class SharedFile<E>;
template Symbol<E> *get_symbol(Context<E> &, std::string_view, std::string_view);
template Symbol<E> *get_symbol(Context<E> &, std::string_view);
template std::string_view demangle(const Symbol<E> &);
template std::ostream &operator<<(std::ostream &, const InputFile<E> &);
} // namespace mold::elf
+8 -19
View File
@@ -274,7 +274,7 @@ static Action get_absrel_action(Context<E> &ctx, Symbol<E> &sym) {
template <typename E>
static Action get_dyn_absrel_action(Context<E> &ctx, Symbol<E> &sym) {
if (sym.is_ifunc())
return ctx.arg.pic ? IFUNC_DYNREL : NONE;
return sym.is_pde_ifunc(ctx) ? NONE : IFUNC_DYNREL;
// This is a decision table for absolute relocations for the pointer
// size data (e.g. R_X86_64_64). Unlike the absrel_table, we can emit
@@ -457,31 +457,20 @@ void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
apply_reloc_alloc(ctx, buf);
else
apply_reloc_nonalloc(ctx, buf);
if constexpr (is_x86_64<E>) {
u8 endbr[] = {0xf3, 0x0f, 0x1e, 0xfa};
u8 nop[] = {0x0f, 0x1f, 0x40, 0x00};
// Rewrite the leading endbr instruction with a nop if the section
// is not address-taken.
if (ctx.arg.z_rewrite_endbr && (shdr().sh_flags & SHF_EXECINSTR) &&
!address_taken && sh_size >= 4 && memcmp(buf, endbr, 4) == 0) {
memcpy(buf, nop, 4);
}
}
}
}
// Get the name of a function containin a given offset.
template <typename E>
std::string_view InputSection<E>::get_func_name(Context<E> &ctx, i64 offset) const {
for (const ElfSym<E> &esym : file.elf_syms) {
std::string_view
InputSection<E>::get_func_name(Context<E> &ctx, i64 offset) const {
for (Symbol<E> *sym : file.symbols) {
const ElfSym<E> &esym = sym->esym();
if (esym.st_shndx == shndx && esym.st_type == STT_FUNC &&
esym.st_value <= offset && offset < esym.st_value + esym.st_size) {
std::string_view name = file.symbol_strtab.data() + esym.st_name;
if (ctx.arg.demangle)
return demangle(name);
return name;
return demangle(*sym);
return sym->name();
}
}
return "";
@@ -520,7 +509,7 @@ bool InputSection<E>::record_undef_error(Context<E> &ctx, const ElfRel<E> &rel)
ss << ":(" << func << ")";
typename decltype(ctx.undef_errors)::accessor acc;
ctx.undef_errors.insert(acc, {sym.name(), {}});
ctx.undef_errors.insert(acc, {&sym, {}});
acc->second.push_back(ss.str());
};
+31 -29
View File
@@ -10,9 +10,6 @@
namespace mold::elf {
template <typename E>
static thread_local MappedFile<Context<E>> *current_file;
template <typename E>
void read_version_script(Context<E> &ctx, std::span<std::string_view> &tok);
@@ -37,7 +34,7 @@ template <typename E>
class SyntaxError {
public:
SyntaxError(Context<E> &ctx, std::string_view errpos) : out(ctx) {
std::string_view contents = current_file<E>->get_contents();
std::string_view contents = ctx.script_file->get_contents();
std::string_view line = get_line(contents, errpos.data());
i64 lineno = 1;
@@ -48,7 +45,7 @@ public:
i64 column = errpos.data() - line.data();
std::stringstream ss;
ss << current_file<E>->name << ":" << lineno << ": ";
ss << ctx.script_file->name << ":" << lineno << ": ";
i64 indent = (i64)ss.tellp() + strlen("mold: ");
ss << line << "\n" << std::setw(indent + column) << " " << "^ ";
out << ss.str();
@@ -118,7 +115,7 @@ template <typename E>
static std::span<std::string_view>
skip(Context<E> &ctx, std::span<std::string_view> tok, std::string_view str) {
if (tok.empty())
Fatal(ctx) << current_file<E>->name << ": expected '" << str
Fatal(ctx) << ctx.script_file->name << ": expected '" << str
<< "', but got EOF";
if (tok[0] != str)
SyntaxError(ctx, tok[0]) << "expected '" << str << "'";
@@ -140,7 +137,7 @@ read_output_format(Context<E> &ctx, std::span<std::string_view> tok) {
while (!tok.empty() && tok[0] != ")")
tok = tok.subspan(1);
if (tok.empty())
Fatal(ctx) << current_file<E>->name << ": expected ')', but got EOF";
Fatal(ctx) << ctx.script_file->name << ": expected ')', but got EOF";
return tok.subspan(1);
}
@@ -158,7 +155,7 @@ static MappedFile<Context<E>> *resolve_path(Context<E> &ctx, std::string_view to
// GNU ld prepends the sysroot if a pathname starts with '/' and the
// script being processed is in the sysroot. We do the same.
if (str.starts_with('/') && is_in_sysroot(ctx, current_file<E>->name))
if (str.starts_with('/') && is_in_sysroot(ctx, ctx.script_file->name))
return MappedFile<Context<E>>::must_open(ctx, ctx.arg.sysroot + str);
if (str.starts_with('=')) {
@@ -205,13 +202,13 @@ read_group(Context<E> &ctx, std::span<std::string_view> tok) {
}
if (tok.empty())
Fatal(ctx) << current_file<E>->name << ": expected ')', but got EOF";
Fatal(ctx) << ctx.script_file->name << ": expected ')', but got EOF";
return tok.subspan(1);
}
template <typename E>
void parse_linker_script(Context<E> &ctx, MappedFile<Context<E>> *mf) {
current_file<E> = mf;
ctx.script_file = mf;
std::vector<std::string_view> vec = tokenize(ctx, mf->get_contents());
std::span<std::string_view> tok = vec;
@@ -241,7 +238,7 @@ void parse_linker_script(Context<E> &ctx, MappedFile<Context<E>> *mf) {
template <typename E>
std::string_view
get_script_output_type(Context<E> &ctx, MappedFile<Context<E>> *mf) {
current_file<E> = mf;
ctx.script_file = mf;
std::vector<std::string_view> vec = tokenize(ctx, mf->get_contents());
std::span<std::string_view> tok = vec;
@@ -312,12 +309,11 @@ read_version_script_commands(Context<E> &ctx, std::span<std::string_view> &tok,
if (tok[0] == "*") {
ctx.default_version = (is_global ? ver_idx : (u32)VER_NDX_LOCAL);
ctx.default_version_from_version_script = true;
} else if (is_global) {
ctx.version_patterns.push_back({unquote(tok[0]), current_file<E>->name,
ctx.version_patterns.push_back({unquote(tok[0]), ctx.script_file->name,
ver_str, ver_idx, is_cpp});
} else {
ctx.version_patterns.push_back({unquote(tok[0]), current_file<E>->name,
ctx.version_patterns.push_back({unquote(tok[0]), ctx.script_file->name,
ver_str, VER_NDX_LOCAL, is_cpp});
}
@@ -358,7 +354,7 @@ void read_version_script(Context<E> &ctx, std::span<std::string_view> &tok) {
template <typename E>
void parse_version_script(Context<E> &ctx, MappedFile<Context<E>> *mf) {
current_file<E> = mf;
ctx.script_file = mf;
std::vector<std::string_view> vec = tokenize(ctx, mf->get_contents());
std::span<std::string_view> tok = vec;
read_version_script(ctx, tok);
@@ -367,7 +363,9 @@ void parse_version_script(Context<E> &ctx, MappedFile<Context<E>> *mf) {
}
template <typename E>
void read_dynamic_list_commands(Context<E> &ctx, std::span<std::string_view> &tok,
void read_dynamic_list_commands(Context<E> &ctx,
std::vector<DynamicPattern> &result,
std::span<std::string_view> &tok,
bool is_cpp) {
while (!tok.empty() && tok[0] != "}") {
if (tok[0] == "extern") {
@@ -376,11 +374,11 @@ void read_dynamic_list_commands(Context<E> &ctx, std::span<std::string_view> &to
if (!tok.empty() && tok[0] == "\"C\"") {
tok = tok.subspan(1);
tok = skip(ctx, tok, "{");
read_dynamic_list_commands(ctx, tok, false);
read_dynamic_list_commands(ctx, result, tok, false);
} else {
tok = skip(ctx, tok, "\"C++\"");
tok = skip(ctx, tok, "{");
read_dynamic_list_commands(ctx, tok, true);
read_dynamic_list_commands(ctx, result, tok, true);
}
tok = skip(ctx, tok, "}");
@@ -388,29 +386,32 @@ void read_dynamic_list_commands(Context<E> &ctx, std::span<std::string_view> &to
continue;
}
if (tok[0] == "*")
ctx.default_version = VER_NDX_GLOBAL;
else
ctx.version_patterns.push_back({unquote(tok[0]), current_file<E>->name,
"global", VER_NDX_GLOBAL, is_cpp});
result.push_back({unquote(tok[0]), "", is_cpp});
tok = skip(ctx, tok.subspan(1), ";");
}
}
template <typename E>
void parse_dynamic_list(Context<E> &ctx, MappedFile<Context<E>> *mf) {
current_file<E> = mf;
std::vector<std::string_view> vec = tokenize(ctx, mf->get_contents());
std::vector<DynamicPattern>
parse_dynamic_list(Context<E> &ctx, std::string_view path) {
std::string_view contents =
MappedFile<Context<E>>::must_open(ctx, std::string(path))->get_contents();
std::vector<std::string_view> vec = tokenize(ctx, contents);
std::span<std::string_view> tok = vec;
std::vector<DynamicPattern> result;
tok = skip(ctx, tok, "{");
read_dynamic_list_commands(ctx, tok, false);
read_dynamic_list_commands(ctx, result, tok, false);
tok = skip(ctx, tok, "}");
tok = skip(ctx, tok, ";");
if (!tok.empty())
SyntaxError(ctx, tok[0]) << "trailing garbage token";
for (DynamicPattern &p : result)
p.source = path;
return result;
}
using E = MOLD_TARGET;
@@ -418,6 +419,7 @@ using E = MOLD_TARGET;
template void parse_linker_script(Context<E> &, MappedFile<Context<E>> *);
template std::string_view get_script_output_type(Context<E> &, MappedFile<Context<E>> *);
template void parse_version_script(Context<E> &, MappedFile<Context<E>> *);
template void parse_dynamic_list(Context<E> &, MappedFile<Context<E>> *);
template std::vector<DynamicPattern> parse_dynamic_list(Context<E> &, std::string_view);
} // namespace mold::elf
+119 -103
View File
@@ -439,72 +439,77 @@ get_api_version(const char *plugin_identifier,
return LAPI_V0;
}
// dlopen the linker plugin file
template <typename E>
static void load_plugin(Context<E> &ctx) {
assert(phase == 0);
phase = 1;
gctx<E> = &ctx;
static void load_lto_plugin(Context<E> &ctx) {
static std::once_flag flag;
void *handle = dlopen(ctx.arg.plugin.c_str(), RTLD_NOW | RTLD_GLOBAL);
if (!handle)
Fatal(ctx) << "could not open plugin file: " << dlerror();
std::call_once(flag, [&] {
assert(phase == 0);
phase = 1;
gctx<E> = &ctx;
OnloadFn *onload = (OnloadFn *)dlsym(handle, "onload");
if (!onload)
Fatal(ctx) << "failed to load plugin " << ctx.arg.plugin << ": "
<< dlerror();
void *handle = dlopen(ctx.arg.plugin.c_str(), RTLD_NOW | RTLD_GLOBAL);
if (!handle)
Fatal(ctx) << "could not open plugin file: " << dlerror();
auto save = [&](std::string_view str) {
return save_string(ctx, std::string(str).c_str()).data();
};
OnloadFn *onload = (OnloadFn *)dlsym(handle, "onload");
if (!onload)
Fatal(ctx) << "failed to load plugin " << ctx.arg.plugin << ": "
<< dlerror();
std::vector<PluginTagValue> tv;
tv.emplace_back(LDPT_MESSAGE, message<E>);
auto save = [&](std::string_view str) {
return save_string(ctx, std::string(str).c_str()).data();
};
if (ctx.arg.shared)
tv.emplace_back(LDPT_LINKER_OUTPUT, LDPO_DYN);
else if (ctx.arg.pie)
tv.emplace_back(LDPT_LINKER_OUTPUT, LDPO_PIE);
else
tv.emplace_back(LDPT_LINKER_OUTPUT, LDPO_EXEC);
std::vector<PluginTagValue> tv;
tv.emplace_back(LDPT_MESSAGE, message<E>);
for (std::string_view opt : ctx.arg.plugin_opt)
tv.emplace_back(LDPT_OPTION, save(opt));
if (ctx.arg.shared)
tv.emplace_back(LDPT_LINKER_OUTPUT, LDPO_DYN);
else if (ctx.arg.pie)
tv.emplace_back(LDPT_LINKER_OUTPUT, LDPO_PIE);
else
tv.emplace_back(LDPT_LINKER_OUTPUT, LDPO_EXEC);
tv.emplace_back(LDPT_REGISTER_CLAIM_FILE_HOOK, register_claim_file_hook<E>);
tv.emplace_back(LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK,
register_all_symbols_read_hook<E>);
tv.emplace_back(LDPT_REGISTER_CLEANUP_HOOK, register_cleanup_hook<E>);
tv.emplace_back(LDPT_ADD_SYMBOLS, add_symbols);
tv.emplace_back(LDPT_GET_SYMBOLS, get_symbols_v1);
tv.emplace_back(LDPT_ADD_INPUT_FILE, add_input_file<E>);
tv.emplace_back(LDPT_GET_INPUT_FILE, get_input_file);
tv.emplace_back(LDPT_RELEASE_INPUT_FILE, release_input_file<E>);
tv.emplace_back(LDPT_ADD_INPUT_LIBRARY, add_input_library);
tv.emplace_back(LDPT_OUTPUT_NAME, save(ctx.arg.output));
tv.emplace_back(LDPT_SET_EXTRA_LIBRARY_PATH, set_extra_library_path);
tv.emplace_back(LDPT_GET_VIEW, get_view<E>);
tv.emplace_back(LDPT_GET_INPUT_SECTION_COUNT, get_input_section_count);
tv.emplace_back(LDPT_GET_INPUT_SECTION_TYPE, get_input_section_type);
tv.emplace_back(LDPT_GET_INPUT_SECTION_NAME, get_input_section_name);
tv.emplace_back(LDPT_GET_INPUT_SECTION_CONTENTS, get_input_section_contents);
tv.emplace_back(LDPT_UPDATE_SECTION_ORDER, update_section_order);
tv.emplace_back(LDPT_ALLOW_SECTION_ORDERING, allow_section_ordering);
tv.emplace_back(LDPT_ADD_SYMBOLS_V2, add_symbols);
tv.emplace_back(LDPT_GET_SYMBOLS_V2, get_symbols_v2<E>);
tv.emplace_back(LDPT_ALLOW_UNIQUE_SEGMENT_FOR_SECTIONS,
allow_unique_segment_for_sections);
tv.emplace_back(LDPT_UNIQUE_SEGMENT_FOR_SECTIONS, unique_segment_for_sections);
tv.emplace_back(LDPT_GET_SYMBOLS_V3, get_symbols_v3<E>);
tv.emplace_back(LDPT_GET_INPUT_SECTION_ALIGNMENT, get_input_section_alignment);
tv.emplace_back(LDPT_GET_INPUT_SECTION_SIZE, get_input_section_size);
tv.emplace_back(LDPT_REGISTER_NEW_INPUT_HOOK, register_new_input_hook<E>);
tv.emplace_back(LDPT_GET_WRAP_SYMBOLS, get_wrap_symbols);
tv.emplace_back(LDPT_GET_API_VERSION, get_api_version<E>);
tv.emplace_back(LDPT_NULL, 0);
for (std::string_view opt : ctx.arg.plugin_opt)
tv.emplace_back(LDPT_OPTION, save(opt));
[[maybe_unused]] PluginStatus status = onload(tv.data());
assert(status == LDPS_OK);
tv.emplace_back(LDPT_REGISTER_CLAIM_FILE_HOOK, register_claim_file_hook<E>);
tv.emplace_back(LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK,
register_all_symbols_read_hook<E>);
tv.emplace_back(LDPT_REGISTER_CLEANUP_HOOK, register_cleanup_hook<E>);
tv.emplace_back(LDPT_ADD_SYMBOLS, add_symbols);
tv.emplace_back(LDPT_GET_SYMBOLS, get_symbols_v1);
tv.emplace_back(LDPT_ADD_INPUT_FILE, add_input_file<E>);
tv.emplace_back(LDPT_GET_INPUT_FILE, get_input_file);
tv.emplace_back(LDPT_RELEASE_INPUT_FILE, release_input_file<E>);
tv.emplace_back(LDPT_ADD_INPUT_LIBRARY, add_input_library);
tv.emplace_back(LDPT_OUTPUT_NAME, save(ctx.arg.output));
tv.emplace_back(LDPT_SET_EXTRA_LIBRARY_PATH, set_extra_library_path);
tv.emplace_back(LDPT_GET_VIEW, get_view<E>);
tv.emplace_back(LDPT_GET_INPUT_SECTION_COUNT, get_input_section_count);
tv.emplace_back(LDPT_GET_INPUT_SECTION_TYPE, get_input_section_type);
tv.emplace_back(LDPT_GET_INPUT_SECTION_NAME, get_input_section_name);
tv.emplace_back(LDPT_GET_INPUT_SECTION_CONTENTS, get_input_section_contents);
tv.emplace_back(LDPT_UPDATE_SECTION_ORDER, update_section_order);
tv.emplace_back(LDPT_ALLOW_SECTION_ORDERING, allow_section_ordering);
tv.emplace_back(LDPT_ADD_SYMBOLS_V2, add_symbols);
tv.emplace_back(LDPT_GET_SYMBOLS_V2, get_symbols_v2<E>);
tv.emplace_back(LDPT_ALLOW_UNIQUE_SEGMENT_FOR_SECTIONS,
allow_unique_segment_for_sections);
tv.emplace_back(LDPT_UNIQUE_SEGMENT_FOR_SECTIONS, unique_segment_for_sections);
tv.emplace_back(LDPT_GET_SYMBOLS_V3, get_symbols_v3<E>);
tv.emplace_back(LDPT_GET_INPUT_SECTION_ALIGNMENT, get_input_section_alignment);
tv.emplace_back(LDPT_GET_INPUT_SECTION_SIZE, get_input_section_size);
tv.emplace_back(LDPT_REGISTER_NEW_INPUT_HOOK, register_new_input_hook<E>);
tv.emplace_back(LDPT_GET_WRAP_SYMBOLS, get_wrap_symbols);
tv.emplace_back(LDPT_GET_API_VERSION, get_api_version<E>);
tv.emplace_back(LDPT_NULL, 0);
[[maybe_unused]] PluginStatus status = onload(tv.data());
assert(status == LDPS_OK);
});
}
template <typename E>
@@ -575,8 +580,29 @@ static bool supports_v3_api(Context<E> &ctx) {
return is_gcc_linker_api_v1 || is_llvm(ctx);
}
template <typename E>
static PluginInputFile
create_plugin_input_file(Context<E> &ctx, MappedFile<Context<E>> *mf) {
PluginInputFile file;
MappedFile<Context<E>> *mf2 = mf->parent ? mf->parent : mf;
file.name = save_string(ctx, mf2->name).data();
file.offset = mf->get_offset();
file.filesize = mf->size;
if (mf2->fd == -1)
mf2->fd = open(file.name, O_RDONLY);
file.fd = mf2->fd;
if (file.fd == -1)
Fatal(ctx) << "cannot open " << file.name << ": " << errno_string();
return file;
}
template <typename E>
ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
load_lto_plugin(ctx);
// V0 API's claim_file is not thread-safe.
static std::mutex mu;
std::unique_lock lock(mu, std::defer_lock);
@@ -589,10 +615,6 @@ ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
<< "added -flto not only for creating object files but also for "
<< "creating the final executable.";
// dlopen the linker plugin file
static std::once_flag flag;
std::call_once(flag, [&] { load_plugin(ctx); });
// Create mold's object instance
ObjectFile<E> *obj = new ObjectFile<E>;
ctx.obj_pool.emplace_back(obj);
@@ -602,23 +624,10 @@ ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
obj->first_global = 1;
obj->is_lto_obj = true;
obj->mf = mf;
obj->archive_name = mf->parent ? mf->parent->name : "";
// Create plugin's object instance
PluginInputFile file = {};
MappedFile<Context<E>> *mf2 = mf->parent ? mf->parent : mf;
file.name = save_string(ctx, mf2->name).data();
if (mf2->fd == -1)
mf2->fd = open(file.name, O_RDONLY);
file.fd = mf2->fd;
if (file.fd == -1)
Fatal(ctx) << "cannot open " << file.name << ": " << errno_string();
if (mf->parent)
obj->archive_name = mf->parent->name;
file.offset = mf->get_offset();
file.filesize = mf->size;
PluginInputFile file = create_plugin_input_file(ctx, mf);
file.handle = (void *)obj;
LOG << "read_lto_symbols: "<< mf->name << "\n";
@@ -635,40 +644,34 @@ ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
// LLVM needs it and takes the ownership of fd. To prevent "too many
// open files" issue, we close fd only for GCC. This is ugly, though.
if (!is_llvm(ctx)) {
MappedFile<Context<E>> *mf2 = mf->parent ? mf->parent : mf;
close(mf2->fd);
mf2->fd = -1;
}
// Initialize object symbols
std::vector<ElfSym<E>> *esyms = new std::vector<ElfSym<E>>(1);
obj->has_symver.resize(plugin_symbols.size());
obj->lto_symbol_versions.resize(plugin_symbols.size());
// Create a symbol strtab
i64 strtab_size = 1;
for (PluginSymbol &psym : plugin_symbols)
strtab_size += strlen(psym.name) + 1;
std::string strtab(strtab_size, '\0');
// Initialize esyms
obj->lto_elf_syms.resize(plugin_symbols.size() + 1);
i64 strtab_offset = 1;
for (i64 i = 0; i < plugin_symbols.size(); i++) {
PluginSymbol &psym = plugin_symbols[i];
esyms->push_back(to_elf_sym<E>(psym));
obj->lto_elf_syms[i + 1] = to_elf_sym<E>(psym);
obj->lto_elf_syms[i + 1].st_name = strtab_offset;
std::string_view key = save_string(ctx, psym.name);
std::string_view name = key;
// Parse symbol version after atsign
if (i64 pos = name.find('@'); pos != name.npos) {
std::string_view ver = name.substr(pos);
name = name.substr(0, pos);
if (ver != "@" && ver != "@@") {
if (ver.starts_with("@@"))
key = name;
obj->has_symver.set(i);
obj->lto_symbol_versions[i] = ver.substr(1);
}
}
obj->symbols.push_back(get_symbol(ctx, key, name));
i64 len = strlen(psym.name);
memcpy(strtab.data() + strtab_offset, psym.name, len);
strtab_offset += len + 1;
}
obj->elf_syms = *esyms;
obj->has_symver.resize(esyms->size());
obj->symbol_strtab = save_string(ctx, strtab);
obj->elf_syms = obj->lto_elf_syms;
obj->initialize_symbols(ctx);
plugin_symbols.clear();
return obj;
}
@@ -677,6 +680,7 @@ ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
template <typename E>
std::vector<ObjectFile<E> *> do_lto(Context<E> &ctx) {
Timer t(ctx, "do_lto");
load_lto_plugin(ctx);
if (!ctx.arg.lto_pass2 && !supports_v3_api(ctx))
restart_process(ctx);
@@ -709,8 +713,21 @@ std::vector<ObjectFile<E> *> do_lto(Context<E> &ctx) {
get_symbol(ctx, y)->referenced_by_regular_obj = true;
}
// Handle --entry
get_symbol(ctx, ctx.arg.entry)->referenced_by_regular_obj = true;
// Keep some symbols
for (Symbol<E> *sym : ctx.arg.undefined)
sym->referenced_by_regular_obj = true;
// Object files containing .gnu.offload_lto_.* sections need to be
// given to the LTO backend. Such sections contains code and data for
// peripherails (typically GPUs).
for (ObjectFile<E> *file : ctx.objs) {
if (!file->is_lto_obj && file->is_gcc_offload_obj) {
PluginInputFile pfile = create_plugin_input_file(ctx, file->mf);
int claimed = false;
claim_file_hook(&pfile, &claimed);
assert(!claimed);
}
}
// all_symbols_read_hook() calls add_input_file() and add_input_library()
LOG << "all symbols read\n";
@@ -723,7 +740,6 @@ std::vector<ObjectFile<E> *> do_lto(Context<E> &ctx) {
template <typename E>
void lto_cleanup(Context<E> &ctx) {
Timer t(ctx, "lto_cleanup");
if (cleanup_hook)
cleanup_hook();
}
+17 -23
View File
@@ -17,7 +17,7 @@
#ifdef _WIN32
# include <direct.h>
# define _chdir chdir
# define chdir _chdir
#else
# include <unistd.h>
#endif
@@ -139,7 +139,6 @@ static ObjectFile<E> *new_lto_obj(Context<E> &ctx, MappedFile<Context<E>> *mf,
file->archive_name = archive_name;
file->is_in_lib = ctx.in_lib || (!archive_name.empty() && !ctx.whole_archive);
file->is_alive = !file->is_in_lib;
ctx.has_lto_object = true;
if (ctx.arg.trace)
SyncOut(ctx) << "trace: " << *file;
return file;
@@ -299,22 +298,6 @@ static void read_input_files(Context<E> &ctx, std::span<std::string> args) {
if (!mf)
Fatal(ctx) << "--version-script: file not found: " << arg;
parse_version_script(ctx, mf);
} else if (remove_prefix(arg, "--dynamic-list=")) {
MappedFile<Context<E>> *mf = find_from_search_paths(ctx, std::string(arg));
if (!mf)
Fatal(ctx) << "--dynamic-list: file not found: " << arg;
parse_dynamic_list(ctx, mf);
} else if (remove_prefix(arg, "--export-dynamic-symbol=")) {
if (arg == "*")
ctx.default_version = VER_NDX_GLOBAL;
else
ctx.version_patterns.push_back({arg, "--export-dynamic-symbol",
"global", VER_NDX_GLOBAL, false});
} else if (remove_prefix(arg, "--export-dynamic-symbol-list=")) {
MappedFile<Context<E>> *mf = find_from_search_paths(ctx, std::string(arg));
if (!mf)
Fatal(ctx) << "--export-dynamic-symbol-list: file not found: " << arg;
parse_dynamic_list(ctx, mf);
} else if (arg == "--push-state") {
state.push_back({ctx.as_needed, ctx.whole_archive, ctx.is_static,
ctx.in_lib});
@@ -457,7 +440,7 @@ int elf_main(int argc, char **argv) {
compute_import_export(ctx);
// Set "address-taken" bits for input sections.
if (ctx.arg.icf || ctx.arg.z_rewrite_endbr)
if (ctx.arg.icf)
compute_address_significance(ctx);
// Garbage-collect unreachable sections.
@@ -523,9 +506,9 @@ int elf_main(int argc, char **argv) {
write_repro_file(ctx);
// Handle --require-defined
for (std::string_view name : ctx.arg.require_defined)
if (!get_symbol(ctx, name)->file)
Error(ctx) << "--require-defined: undefined symbol: " << name;
for (Symbol<E> *sym : ctx.arg.require_defined)
if (!sym->file)
Error(ctx) << "--require-defined: undefined symbol: " << *sym;
// .init_array and .fini_array contents have to be sorted by
// a special rule. Sort them.
@@ -535,6 +518,11 @@ int elf_main(int argc, char **argv) {
// because they are superceded by .init_array/.fini_array, though.
sort_ctor_dtor(ctx);
// If .ctors/.dtors are to be placed to .init_array/.fini_array,
// we need to reverse their contents.
if (ctx.has_init_array && ctx.has_ctors)
fixup_ctors_in_init_array(ctx);
// Handle --shuffle-sections
if (ctx.arg.shuffle_sections != SHUFFLE_SECTIONS_NONE)
shuffle_sections(ctx);
@@ -558,6 +546,9 @@ int elf_main(int argc, char **argv) {
// .got.plt, .dynsym, .dynstr, etc.
scan_relocations(ctx);
// Compute the is_weak bit for each imported symbol.
compute_imported_symbol_weakness(ctx);
// Compute sizes of output sections while assigning offsets
// within an output section to input sections.
compute_section_sizes(ctx);
@@ -642,6 +633,9 @@ int elf_main(int argc, char **argv) {
// Copy input sections to the output file and apply relocations.
copy_chunks(ctx);
if (ctx.arg.z_rewrite_endbr)
rewrite_endbr(ctx);
// Dynamic linker works better with sorted .rela.dyn section,
// so we sort them.
ctx.reldyn->sort(ctx);
@@ -671,7 +665,7 @@ int elf_main(int argc, char **argv) {
if (!ctx.arg.dependency_file.empty())
write_dependency_file(ctx);
if (ctx.has_lto_object)
if (!ctx.arg.plugin.empty())
lto_cleanup(ctx);
t_all.stop();
+11
View File
@@ -132,3 +132,14 @@ int posix_spawn(pid_t *pid, const char *path,
typeof(posix_spawn) *real = dlsym(RTLD_NEXT, "posix_spawn");
return real(pid, path, file_actions, attrp, argv, envp);
}
int posix_spawnp(pid_t *pid, const char *file,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp,
char *const *argv, char *const *envp) {
debug_print("posix_spawnp %s\n", file);
if (is_ld(file))
file = get_mold_path();
typeof(posix_spawnp) *real = dlsym(RTLD_NEXT, "posix_spawnp");
return real(pid, file, file_actions, attrp, argv, envp);
}
+82 -45
View File
@@ -95,12 +95,12 @@ struct SymbolAux<PPC64V1> : SymbolAux<X86_64> {
//
template <typename E>
class RangeExtensionThunk {};
class Thunk {};
template <needs_thunk E>
class RangeExtensionThunk<E> {
class Thunk<E> {
public:
RangeExtensionThunk(OutputSection<E> &osec, i64 offset)
Thunk(OutputSection<E> &osec, i64 offset)
: output_section(osec), offset(offset) {}
i64 size() const { return E::thunk_hdr_size + symbols.size() * E::thunk_size; }
@@ -119,7 +119,7 @@ public:
std::vector<Symbol<E> *> symbols;
};
struct RangeExtensionRef {
struct ThunkRef {
i16 thunk_idx = -1;
i16 sym_idx = -1;
};
@@ -212,7 +212,7 @@ struct InputSectionExtras {};
template <needs_thunk E>
struct InputSectionExtras<E> {
std::vector<RangeExtensionRef> range_extn;
std::vector<ThunkRef> thunk_refs;
};
template <is_riscv E>
@@ -449,7 +449,11 @@ public:
template <typename E>
class OutputSection : public Chunk<E> {
public:
OutputSection(Context<E> &ctx, std::string_view name, u32 type, u64 flags);
OutputSection(std::string_view name, u32 type) {
this->name = name;
this->shdr.sh_type = type;
}
ChunkKind kind() override { return OUTPUT_SECTION; }
OutputSection<E> *to_osec() override { return this; }
void construct_relr(Context<E> &ctx) override;
@@ -462,8 +466,9 @@ public:
void create_range_extension_thunks(Context<E> &ctx);
std::vector<InputSection<E> *> members;
std::vector<std::unique_ptr<RangeExtensionThunk<E>>> thunks;
std::vector<std::unique_ptr<Thunk<E>>> thunks;
std::unique_ptr<RelocSection<E>> reloc_sec;
Atomic<u32> sh_flags;
};
template <typename E>
@@ -768,7 +773,7 @@ class MergedSection : public Chunk<E> {
public:
static MergedSection<E> *
get_instance(Context<E> &ctx, std::string_view name, i64 type, i64 flags,
i64 entsize);
i64 entsize, i64 addralign);
SectionFragment<E> *insert(Context<E> &ctx, std::string_view data,
u64 hash, i64 p2align);
@@ -1160,6 +1165,7 @@ public:
std::string archive_name, bool is_in_lib);
void parse(Context<E> &ctx);
void initialize_symbols(Context<E> &ctx);
void initialize_mergeable_sections(Context<E> &ctx);
void resolve_section_pieces(Context<E> &ctx);
void resolve_symbols(Context<E> &ctx) override;
@@ -1183,11 +1189,13 @@ public:
std::vector<FdeRecord<E>> fdes;
BitVector has_symver;
std::vector<ComdatGroupRef<E>> comdat_groups;
InputSection<E> *eh_frame_section = nullptr;
std::vector<InputSection<E> *> eh_frame_sections;
bool exclude_libs = false;
std::map<u32, u32> gnu_properties;
bool is_lto_obj = false;
bool needs_executable_stack = false;
bool is_lto_obj = false;
bool is_gcc_offload_obj = false;
bool is_rust_obj = false;
u64 num_dynrel = 0;
u64 reldyn_offset = 0;
@@ -1205,7 +1213,7 @@ public:
InputSection<E> *debug_pubtypes = nullptr;
// For LTO
std::vector<std::string_view> lto_symbol_versions;
std::vector<ElfSym<E>> lto_elf_syms;
// Target-specific member
[[no_unique_address]] ObjectFileExtras<E> extra;
@@ -1215,7 +1223,6 @@ private:
std::string archive_name, bool is_in_lib);
void initialize_sections(Context<E> &ctx);
void initialize_symbols(Context<E> &ctx);
void sort_relocations(Context<E> &ctx);
void initialize_ehframe_sections(Context<E> &ctx);
void parse_note_gnu_property(Context <E> &ctx, const ElfShdr <E> &shdr);
@@ -1281,8 +1288,15 @@ get_script_output_type(Context<E> &ctx, MappedFile<Context<E>> *mf);
template <typename E>
void parse_version_script(Context<E> &ctx, MappedFile<Context<E>> *mf);
struct DynamicPattern {
std::string_view pattern;
std::string_view source;
bool is_cpp = false;
};
template <typename E>
void parse_dynamic_list(Context<E> &ctx, MappedFile<Context<E>> *mf);
std::vector<DynamicPattern>
parse_dynamic_list(Context<E> &ctx, std::string_view path);
//
// lto.cc
@@ -1372,16 +1386,19 @@ template <typename E> void check_duplicate_symbols(Context<E> &);
template <typename E> void check_symbol_types(Context<E> &);
template <typename E> void sort_init_fini(Context<E> &);
template <typename E> void sort_ctor_dtor(Context<E> &);
template <typename E> void fixup_ctors_in_init_array(Context<E> &);
template <typename E> void shuffle_sections(Context<E> &);
template <typename E> void compute_section_sizes(Context<E> &);
template <typename E> void sort_output_sections(Context<E> &);
template <typename E> void claim_unresolved_symbols(Context<E> &);
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 create_output_symtab(Context<E> &);
template <typename E> void report_undef_errors(Context<E> &);
template <typename E> void create_reloc_sections(Context<E> &);
template <typename E> void copy_chunks(Context<E> &);
template <typename E> void rewrite_endbr(Context<E> &);
template <typename E> void apply_version_script(Context<E> &);
template <typename E> void parse_symbol_version(Context<E> &);
template <typename E> void compute_import_export(Context<E> &);
@@ -1454,6 +1471,22 @@ public:
// arch-ppc64v2.cc
//
extern const std::vector<std::pair<std::string_view, u32>>
ppc64_save_restore_insns;
class PPC64SaveRestoreSection : public Chunk<PPC64V2> {
public:
PPC64SaveRestoreSection() {
this->name = ".save_restore_gprs";
this->shdr.sh_type = SHT_PROGBITS;
this->shdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR;
this->shdr.sh_addralign = 16;
this->shdr.sh_size = ppc64_save_restore_insns.size() * 4;
}
void copy_buf(Context<PPC64V2> &ctx) override;
};
template <> u64 get_eflags(Context<PPC64V2> &ctx);
//
@@ -1578,6 +1611,7 @@ struct ContextExtras<PPC64V1> {
template <>
struct ContextExtras<PPC64V2> {
PPC64SaveRestoreSection *save_restore = nullptr;
Symbol<PPC64V2> *TOC = nullptr;
Atomic<bool> is_power10 = false;
};
@@ -1599,7 +1633,12 @@ struct ContextExtras<ALPHA> {
// resource management, and other miscellaneous objects.
template <typename E>
struct Context {
Context() = default;
Context() {
arg.entry = get_symbol(*this, "_start");
arg.fini = get_symbol(*this, "_fini");
arg.init = get_symbol(*this, "_init");
}
Context(const Context<E> &) = delete;
void checkpoint() {
@@ -1616,6 +1655,9 @@ struct Context {
CompressKind compress_debug_sections = COMPRESS_NONE;
SeparateCodeKind z_separate_code = NOSEPARATE_CODE;
ShuffleSectionsKind shuffle_sections = SHUFFLE_SECTIONS_NONE;
Symbol<E> *entry = nullptr;
Symbol<E> *fini = nullptr;
Symbol<E> *init = nullptr;
UnresolvedKind unresolved_symbols = UNRESOLVED_ERROR;
bool Bsymbolic = false;
bool Bsymbolic_functions = false;
@@ -1642,6 +1684,7 @@ struct Context {
bool ignore_data_address_equality = false;
bool is_static = false;
bool lto_pass2 = false;
bool nmagic = false;
bool noinhibit_exec = false;
bool oformat_binary = false;
bool omagic = false;
@@ -1692,6 +1735,7 @@ struct Context {
bool z_text = false;
i64 filler = -1;
i64 spare_dynamic_tags = 5;
i64 spare_program_headers = 0;
i64 thread_count = 0;
i64 z_stack_size = 0;
u64 shuffle_sections_seed;
@@ -1703,9 +1747,6 @@ struct Context {
std::string dependency_file;
std::string directory;
std::string dynamic_linker;
std::string entry = "_start";
std::string fini = "_fini";
std::string init = "_init";
std::string output = "a.out";
std::string package_metadata;
std::string plugin;
@@ -1718,6 +1759,8 @@ struct Context {
std::unordered_set<std::string_view> ignore_ir_file;
std::unordered_set<std::string_view> wrap;
std::vector<SectionOrder> section_order;
std::vector<Symbol<E> *> require_defined;
std::vector<Symbol<E> *> undefined;
std::vector<std::pair<Symbol<E> *, std::variant<Symbol<E> *, u64>>> defsyms;
std::vector<std::string> library_paths;
std::vector<std::string> plugin_opt;
@@ -1725,31 +1768,29 @@ struct Context {
std::vector<std::string_view> auxiliary;
std::vector<std::string_view> exclude_libs;
std::vector<std::string_view> filter;
std::vector<std::string_view> require_defined;
std::vector<std::string_view> trace_symbol;
std::vector<std::string_view> undefined;
u64 image_base = 0x200000;
} arg;
std::vector<VersionPattern> version_patterns;
u16 default_version = VER_NDX_GLOBAL;
std::vector<DynamicPattern> dynamic_list_patterns;
i64 default_version = VER_NDX_UNSPECIFIED;
i64 page_size = E::page_size;
std::optional<int> global_lock_fd;
// true if default_version is set by a wildcard in version script.
bool default_version_from_version_script = false;
// Reader context
bool as_needed = false;
bool whole_archive = false;
bool is_static;
bool in_lib = false;
i64 file_priority = 10000;
MappedFile<Context<E>> *script_file = nullptr;
std::unordered_set<std::string_view> visited;
tbb::task_group tg;
bool has_error = false;
bool has_lto_object = false;
Atomic<bool> has_init_array = false;
Atomic<bool> has_ctors = false;
// Symbol table
tbb::concurrent_hash_map<std::string_view, Symbol<E>, HashCmp> symbol_map;
@@ -1789,7 +1830,7 @@ struct Context {
Atomic<bool> has_textrel = false;
Atomic<u32> num_ifunc_dynrels = 0;
tbb::concurrent_hash_map<std::string_view, std::vector<std::string>> undef_errors;
tbb::concurrent_hash_map<Symbol<E> *, std::vector<std::string>> undef_errors;
// Output chunks
OutputEhdr<E> *ehdr = nullptr;
@@ -2016,6 +2057,10 @@ public:
TAG_MASK = 0b11,
};
static_assert(alignof(InputSection<E>) >= 4);
static_assert(alignof(Chunk<E>) >= 4);
static_assert(alignof(SectionFragment<E>) >= 4);
uintptr_t origin = 0;
// `value` contains symbol value. If it's an absolute symbol, it is
@@ -2031,7 +2076,7 @@ public:
i32 sym_idx = -1;
i32 aux_idx = -1;
u16 ver_idx = 0;
u16 ver_idx = VER_NDX_UNSPECIFIED;
// `flags` has NEEDS_ flags.
Atomic<u8> flags = 0;
@@ -2152,30 +2197,27 @@ public:
// opposed to IR object).
bool referenced_by_regular_obj : 1 = false;
// For `-z rewrite-endbr`
bool address_taken : 1 = false;
// Target-dependent extra members.
[[no_unique_address]] SymbolExtras<E> extra;
};
// If we haven't seen the same `key` before, create a new instance
// of Symbol and returns it. Otherwise, returns the previously-
// instantiated object. `key` is usually the same as `name`.
template <typename E>
Symbol<E> *get_symbol(Context<E> &ctx, std::string_view key,
std::string_view name) {
typename decltype(ctx.symbol_map)::const_accessor acc;
ctx.symbol_map.insert(acc, {key, Symbol<E>(name)});
return const_cast<Symbol<E> *>(&acc->second);
}
std::string_view name);
template <typename E>
Symbol<E> *get_symbol(Context<E> &ctx, std::string_view name) {
return get_symbol(ctx, name, name);
}
Symbol<E> *get_symbol(Context<E> &ctx, std::string_view name);
template <typename E>
std::string_view demangle(const Symbol<E> &sym);
template <typename E>
std::ostream &operator<<(std::ostream &out, const Symbol<E> &sym) {
if (opt_demangle)
out << demangle(sym.name());
out << demangle(sym);
else
out << sym.name();
return out;
@@ -2232,7 +2274,7 @@ inline u64 InputSection<E>::get_addr() const {
template <typename E>
inline std::string_view InputSection<E>::name() const {
if (file.elf_sections.size() <= shndx)
return ".common";
return (shdr().sh_flags & SHF_TLS) ? ".tls_common" : ".common";
return file.shstrtab.data() + file.elf_sections[shndx].sh_name;
}
@@ -2299,7 +2341,7 @@ InputSection<E>::get_fragment(Context<E> &ctx, const ElfRel<E> &rel) {
template <typename E>
u64 InputSection<E>::get_thunk_addr(i64 idx) {
if constexpr (needs_thunk<E>) {
RangeExtensionRef ref = extra.range_extn[idx];
ThunkRef ref = extra.thunk_refs[idx];
assert(ref.thunk_idx != -1);
return output_section->thunks[ref.thunk_idx]->get_addr(ref.sym_idx);
}
@@ -2397,8 +2439,6 @@ InputFile<E>::get_string(Context<E> &ctx, const ElfShdr<E> &shdr) {
template <typename E>
inline std::string_view InputFile<E>::get_string(Context<E> &ctx, i64 idx) {
assert(idx < elf_sections.size());
if (elf_sections.size() <= idx)
Fatal(ctx) << *this << ": invalid section index: " << idx;
return this->get_string(ctx, elf_sections[idx]);
@@ -2710,9 +2750,6 @@ inline bool Symbol<E>::has_plt(Context<E> &ctx) const {
template <typename E>
inline bool Symbol<E>::is_absolute() const {
if (file && file->is_dso)
return esym().is_abs();
return !is_imported && !get_frag() && !get_input_section() &&
!get_output_section();
}
+101 -116
View File
@@ -50,10 +50,9 @@ void OutputEhdr<E>::copy_buf(Context<E> &ctx) {
if (ctx.arg.relocatable)
return 0;
if (!ctx.arg.entry.empty())
if (Symbol<E> *sym = get_symbol(ctx, ctx.arg.entry);
sym->file && !sym->file->is_dso)
return sym->get_addr(ctx);
if (Symbol<E> &sym = *ctx.arg.entry;
sym.file && !sym.file->is_dso)
return sym.get_addr(ctx);
if (OutputSection<E> *osec = find_section(ctx, ".text"))
return osec->shdr.sh_addr;
@@ -149,11 +148,11 @@ template <typename E>
static std::vector<ElfPhdr<E>> create_phdr(Context<E> &ctx) {
std::vector<ElfPhdr<E>> vec;
auto define = [&](u64 type, u64 flags, i64 min_align, Chunk<E> *chunk) {
auto define = [&](u64 type, u64 flags, Chunk<E> *chunk) {
ElfPhdr<E> phdr = {};
phdr.p_type = type;
phdr.p_flags = flags;
phdr.p_align = std::max<u64>(min_align, chunk->shdr.sh_addralign);
phdr.p_align = chunk->shdr.sh_addralign;
phdr.p_offset = chunk->shdr.sh_offset;
if (chunk->shdr.sh_type != SHT_NOBITS)
@@ -176,8 +175,7 @@ static std::vector<ElfPhdr<E>> create_phdr(Context<E> &ctx) {
};
auto is_bss = [](Chunk<E> *chunk) {
return chunk->shdr.sh_type == SHT_NOBITS &&
!(chunk->shdr.sh_flags & SHF_TLS);
return chunk->shdr.sh_type == SHT_NOBITS;
};
auto is_tbss = [](Chunk<E> *chunk) {
@@ -186,86 +184,86 @@ static std::vector<ElfPhdr<E>> create_phdr(Context<E> &ctx) {
};
auto is_note = [](Chunk<E> *chunk) {
ElfShdr<E> &shdr = chunk->shdr;
return (shdr.sh_type == SHT_NOTE) && (shdr.sh_flags & SHF_ALLOC);
return chunk->shdr.sh_type == SHT_NOTE;
};
// When we are creating PT_LOAD segments, we consider only
// the following chunks.
std::vector<Chunk<E> *> chunks;
for (Chunk<E> *chunk : ctx.chunks)
if ((chunk->shdr.sh_flags & SHF_ALLOC) && !is_tbss(chunk))
chunks.push_back(chunk);
// The ELF spec says that "loadable segment entries in the program
// header table appear in ascending order, sorted on the p_vaddr
// member".
sort(chunks, [](Chunk<E> *a, Chunk<E> *b) {
return a->shdr.sh_addr < b->shdr.sh_addr;
});
// Create a PT_PHDR for the program header itself.
if (ctx.phdr && (ctx.phdr->shdr.sh_flags & SHF_ALLOC))
define(PT_PHDR, PF_R, sizeof(Word<E>), ctx.phdr);
define(PT_PHDR, PF_R, ctx.phdr);
// Create a PT_INTERP.
if (ctx.interp)
define(PT_INTERP, PF_R, 1, ctx.interp);
define(PT_INTERP, PF_R, ctx.interp);
// Create a PT_NOTE for SHF_NOTE sections.
for (i64 i = 0, end = ctx.chunks.size(); i < end;) {
Chunk<E> *first = ctx.chunks[i++];
if (!is_note(first))
continue;
for (i64 i = 0; i < chunks.size();) {
Chunk<E> *first = chunks[i++];
if (is_note(first)) {
i64 flags = to_phdr_flags(ctx, first);
define(PT_NOTE, flags, first);
i64 flags = to_phdr_flags(ctx, first);
i64 alignment = first->shdr.sh_addralign;
define(PT_NOTE, flags, alignment, first);
while (i < end && is_note(ctx.chunks[i]) &&
to_phdr_flags(ctx, ctx.chunks[i]) == flags)
append(ctx.chunks[i++]);
while (i < chunks.size() &&
is_note(ctx.chunks[i]) &&
to_phdr_flags(ctx, ctx.chunks[i]) == flags)
append(ctx.chunks[i++]);
}
}
// Create PT_LOAD segments.
{
i64 idx = vec.size();
std::vector<Chunk<E> *> chunks = ctx.chunks;
std::erase_if(chunks, is_tbss);
for (i64 i = 0; i < chunks.size();) {
Chunk<E> *first = chunks[i++];
i64 flags = to_phdr_flags(ctx, first);
define(PT_LOAD, flags, first);
vec.back().p_align = std::max<u64>(ctx.page_size, vec.back().p_align);
for (i64 i = 0, end = chunks.size(); i < end;) {
Chunk<E> *first = chunks[i++];
if (!(first->shdr.sh_flags & SHF_ALLOC))
continue;
i64 flags = to_phdr_flags(ctx, first);
define(PT_LOAD, flags, ctx.page_size, first);
// Add contiguous ALLOC sections as long as they have the same
// section flags and there's no on-disk gap in between.
if (!is_bss(first))
while (i < end && !is_bss(chunks[i]) &&
to_phdr_flags(ctx, chunks[i]) == flags &&
chunks[i]->shdr.sh_offset - first->shdr.sh_offset ==
chunks[i]->shdr.sh_addr - first->shdr.sh_addr)
append(chunks[i++]);
while (i < end && is_bss(chunks[i]) &&
to_phdr_flags(ctx, chunks[i]) == flags)
// Add contiguous ALLOC sections as long as they have the same
// section flags and there's no on-disk gap in between.
if (!is_bss(first))
while (i < chunks.size() &&
!is_bss(chunks[i]) &&
to_phdr_flags(ctx, chunks[i]) == flags &&
chunks[i]->shdr.sh_offset - first->shdr.sh_offset ==
chunks[i]->shdr.sh_addr - first->shdr.sh_addr)
append(chunks[i++]);
}
// The ELF spec says that "loadable segment entries in the program
// header table appear in ascending order, sorted on the p_vaddr
// member".
std::stable_sort(vec.begin() + idx, vec.end(),
[](const ElfPhdr<E> &a, const ElfPhdr<E> &b) {
return a.p_vaddr < b.p_vaddr;
});
while (i < chunks.size() &&
is_bss(chunks[i]) &&
to_phdr_flags(ctx, chunks[i]) == flags)
append(chunks[i++]);
}
// Create a PT_TLS.
for (i64 i = 0; i < ctx.chunks.size(); i++) {
if (ctx.chunks[i]->shdr.sh_flags & SHF_TLS) {
define(PT_TLS, PF_R, 1, ctx.chunks[i++]);
while (i < ctx.chunks.size() && (ctx.chunks[i]->shdr.sh_flags & SHF_TLS))
for (i64 i = 0; i < ctx.chunks.size();) {
Chunk<E> *first = ctx.chunks[i++];
if (first->shdr.sh_flags & SHF_TLS) {
define(PT_TLS, PF_R, first);
while (i < ctx.chunks.size() &&
(ctx.chunks[i]->shdr.sh_flags & SHF_TLS))
append(ctx.chunks[i++]);
}
}
// Add PT_DYNAMIC
if (ctx.dynamic && ctx.dynamic->shdr.sh_size)
define(PT_DYNAMIC, PF_R | PF_W, 1, ctx.dynamic);
define(PT_DYNAMIC, PF_R | PF_W, ctx.dynamic);
// Add PT_GNU_EH_FRAME
if (ctx.eh_frame_hdr)
define(PT_GNU_EH_FRAME, PF_R, 1, ctx.eh_frame_hdr);
define(PT_GNU_EH_FRAME, PF_R, ctx.eh_frame_hdr);
// Add PT_GNU_STACK, which is a marker segment that doesn't really
// contain any segments. It controls executable bit of stack area.
@@ -280,31 +278,31 @@ static std::vector<ElfPhdr<E>> create_phdr(Context<E> &ctx) {
// Create a PT_GNU_RELRO.
if (ctx.arg.z_relro) {
for (i64 i = 0; i < ctx.chunks.size(); i++) {
if (!ctx.chunks[i]->is_relro)
continue;
define(PT_GNU_RELRO, PF_R, 1, ctx.chunks[i++]);
while (i < ctx.chunks.size() && ctx.chunks[i]->is_relro)
append(ctx.chunks[i++]);
vec.back().p_align = 1;
for (i64 i = 0; i < chunks.size();) {
Chunk<E> *first = chunks[i++];
if (first->is_relro) {
define(PT_GNU_RELRO, PF_R, first);
while (i < chunks.size() && chunks[i]->is_relro)
append(chunks[i++]);
vec.back().p_align = 1;
}
}
}
// Create a PT_ARM_EDXIDX
if constexpr (is_arm32<E>)
if (OutputSection<E> *osec = find_section(ctx, SHT_ARM_EXIDX))
define(PT_ARM_EXIDX, PF_R, 4, osec);
define(PT_ARM_EXIDX, PF_R, osec);
// Create a PT_RISCV_ATTRIBUTES
if constexpr (is_riscv<E>)
if (ctx.extra.riscv_attributes->shdr.sh_size)
define(PT_RISCV_ATTRIBUTES, PF_R, 1, ctx.extra.riscv_attributes);
define(PT_RISCV_ATTRIBUTES, PF_R, ctx.extra.riscv_attributes);
// Create a PT_OPENBSD_RANDOMIZE
for (Chunk<E> *chunk : ctx.chunks)
if (chunk->name == ".openbsd.randomdata")
define(PT_OPENBSD_RANDOMIZE, PF_R | PF_W, 1, chunk);
define(PT_OPENBSD_RANDOMIZE, PF_R | PF_W, chunk);
// Set p_paddr if --physical-image-base was given. --physical-image-base
// is typically used in embedded programming to specify the base address
@@ -352,6 +350,7 @@ static std::vector<ElfPhdr<E>> create_phdr(Context<E> &ctx) {
}
}
vec.resize(vec.size() + ctx.arg.spare_program_headers);
return vec;
}
@@ -763,13 +762,13 @@ static std::vector<Word<E>> create_dynamic_section(Context<E> &ctx) {
define(DT_VERDEFNUM, ctx.verdef->shdr.sh_info);
}
if (Symbol<E> *sym = get_symbol(ctx, ctx.arg.init);
sym->file && !sym->file->is_dso)
define(DT_INIT, sym->get_addr(ctx));
if (Symbol<E> &sym = *ctx.arg.init;
sym.file && !sym.file->is_dso)
define(DT_INIT, sym.get_addr(ctx));
if (Symbol<E> *sym = get_symbol(ctx, ctx.arg.fini);
sym->file && !sym->file->is_dso)
define(DT_FINI, sym->get_addr(ctx));
if (Symbol<E> &sym = *ctx.arg.fini;
sym.file && !sym.file->is_dso)
define(DT_FINI, sym.get_addr(ctx));
if (ctx.hash)
define(DT_HASH, ctx.hash->shdr.sh_addr);
@@ -860,36 +859,6 @@ void DynamicSection<E>::copy_buf(Context<E> &ctx) {
write_vector(ctx.buf + this->shdr.sh_offset, contents);
}
template <typename E>
OutputSection<E>::OutputSection(Context<E> &ctx, std::string_view name,
u32 type, u64 flags) {
this->name = name;
this->shdr.sh_type = type;
this->shdr.sh_flags = flags & ~SHF_MERGE & ~SHF_STRINGS;
if (auto it = ctx.arg.section_align.find(name);
it != ctx.arg.section_align.end())
this->shdr.sh_addralign = it->second;
// PT_GNU_RELRO segment is a security mechanism to make more pages
// read-only than we could have done without it.
//
// Traditionally, sections are either read-only or read-write. If a
// section contains dynamic relocations, it must have been put into a
// read-write segment so that the program loader can mutate its
// contents in memory, even if no one will write to it at runtime.
//
// RELRO segment allows us to make such pages writable only when a
// program is being loaded. After that, the page becomes read-only.
//
// Some sections, such as .init, .fini, .got, .dynamic, contain
// dynamic relocations but doesn't have to be writable at runtime,
// so they are put into a RELRO segment.
this->is_relro = (name == ".toc" || name.ends_with(".rel.ro") ||
type == SHT_INIT_ARRAY || type == SHT_FINI_ARRAY ||
type == SHT_PREINIT_ARRAY || (flags & SHF_TLS));
}
template <typename E>
void OutputSection<E>::copy_buf(Context<E> &ctx) {
if (this->shdr.sh_type != SHT_NOBITS)
@@ -925,8 +894,7 @@ void OutputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
});
if constexpr (needs_thunk<E>) {
tbb::parallel_for_each(thunks,
[&](std::unique_ptr<RangeExtensionThunk<E>> &thunk) {
tbb::parallel_for_each(thunks, [&](std::unique_ptr<Thunk<E>> &thunk) {
thunk->copy_buf(ctx);
});
}
@@ -1019,7 +987,7 @@ void OutputSection<E>::compute_symtab_size(Context<E> &ctx) {
this->strtab_size = 0;
this->num_local_symtab = 0;
for (std::unique_ptr<RangeExtensionThunk<E>> &thunk : thunks) {
for (std::unique_ptr<Thunk<E>> &thunk : thunks) {
// For ARM32, we emit additional symbol "$t", "$a" and "$d" for
// each thunk to mark the beginning of Thumb code, ARM code and
// data, respectively.
@@ -1058,7 +1026,7 @@ void OutputSection<E>::populate_symtab(Context<E> &ctx) {
esym++;
};
for (std::unique_ptr<RangeExtensionThunk<E>> &thunk : thunks) {
for (std::unique_ptr<Thunk<E>> &thunk : thunks) {
for (i64 i = 0; i < thunk->symbols.size(); i++) {
Symbol<E> &sym = *thunk->symbols[i];
u64 addr = thunk->get_addr(i);
@@ -1912,11 +1880,23 @@ void GnuHashSection<E>::copy_buf(Context<E> &ctx) {
template <typename E>
std::string_view
get_merged_output_name(Context<E> &ctx, std::string_view name, u64 flags) {
get_merged_output_name(Context<E> &ctx, std::string_view name, u64 flags,
i64 entsize, i64 addralign) {
if (ctx.arg.relocatable && !ctx.arg.relocatable_merge_sections)
return name;
if (ctx.arg.unique && ctx.arg.unique->match(name))
return name;
// GCC seems to create sections named ".rodata.strN.<mangled-symbol-name>.M".
// We want to eliminate the symbol name part from the section name.
if ((flags & SHF_STRINGS) && name.starts_with(".rodata.")) {
std::string name2 = ".rodata.str"s + std::to_string(entsize) +
"." + std::to_string(addralign);
if (name == name2)
return name;
return save_string(ctx, name2);
}
return name;
}
@@ -1932,8 +1912,9 @@ MergedSection<E>::MergedSection(std::string_view name, i64 flags, i64 type,
template <typename E>
MergedSection<E> *
MergedSection<E>::get_instance(Context<E> &ctx, std::string_view name,
i64 type, i64 flags, i64 entsize) {
name = get_merged_output_name(ctx, name, flags);
i64 type, i64 flags,
i64 entsize, i64 addralign) {
name = get_merged_output_name(ctx, name, flags, entsize, addralign);
flags = flags & ~(u64)SHF_GROUP & ~(u64)SHF_COMPRESSED;
auto find = [&]() -> MergedSection * {
@@ -2534,8 +2515,12 @@ void VerdefSection<E>::construct(Context<E> &ctx) {
for (std::string_view verstr : ctx.arg.version_definitions)
write(verstr, idx++, 0);
for (Symbol<E> *sym : std::span<Symbol<E> *>(ctx.dynsym->symbols).subspan(1))
ctx.versym->contents[sym->get_dynsym_idx(ctx)] = sym->ver_idx;
for (Symbol<E> *sym : std::span<Symbol<E> *>(ctx.dynsym->symbols).subspan(1)) {
i64 ver = sym->ver_idx;
if (ver == VER_NDX_UNSPECIFIED)
ver = VER_NDX_GLOBAL;
ctx.versym->contents[sym->get_dynsym_idx(ctx)] = ver;
}
}
template <typename E>
+461 -221
View File
@@ -177,6 +177,9 @@ void create_synthetic_sections(Context<E> &ctx) {
if constexpr (is_ppc64v1<E>)
ctx.extra.opd = push(new PPC64OpdSection);
if constexpr (is_ppc64v2<E>)
ctx.extra.save_restore = push(new PPC64SaveRestoreSection);
if constexpr (is_sparc<E>) {
if (ctx.arg.is_static)
ctx.extra.tls_get_addr_sec = push(new SparcTlsGetAddrSection);
@@ -189,15 +192,13 @@ void create_synthetic_sections(Context<E> &ctx) {
template <typename E>
static void mark_live_objects(Context<E> &ctx) {
auto mark_symbol = [&](std::string_view name) {
if (InputFile<E> *file = get_symbol(ctx, name)->file)
file->is_alive = true;
};
for (Symbol<E> *sym : ctx.arg.undefined)
if (sym->file)
sym->file->is_alive = true;
for (std::string_view name : ctx.arg.undefined)
mark_symbol(name);
for (std::string_view name : ctx.arg.require_defined)
mark_symbol(name);
for (Symbol<E> *sym : ctx.arg.require_defined)
if (sym->file)
sym->file->is_alive = true;
std::vector<InputFile<E> *> roots;
@@ -302,7 +303,12 @@ void resolve_symbols(Context<E> &ctx) {
do_resolve_symbols(ctx);
if (ctx.has_lto_object) {
bool has_lto_obj = false;
for (ObjectFile<E> *file : objs)
if (file->is_lto_obj || file->is_gcc_offload_obj)
has_lto_obj = true;
if (has_lto_obj) {
// Do link-time optimization. We pass all IR object files to the
// compiler backend to compile them into a few ELF object files.
//
@@ -358,8 +364,8 @@ void kill_eh_frame_sections(Context<E> &ctx) {
Timer t(ctx, "kill_eh_frame_sections");
for (ObjectFile<E> *file : ctx.objs)
if (file->eh_frame_section)
file->eh_frame_section->is_alive = false;
for (InputSection<E> *sec : file->eh_frame_sections)
sec->is_alive = false;
}
template <typename E>
@@ -397,7 +403,7 @@ template <typename E>
void add_comment_string(Context<E> &ctx, std::string str) {
MergedSection<E> *sec =
MergedSection<E>::get_instance(ctx, ".comment", SHT_PROGBITS,
SHF_MERGE | SHF_STRINGS, 1);
SHF_MERGE | SHF_STRINGS, 1, 1);
std::string_view buf = save_string(ctx, str);
std::string_view data(buf.data(), buf.size() + 1);
@@ -455,7 +461,6 @@ struct OutputSectionKey {
bool operator==(const OutputSectionKey &) const = default;
std::string_view name;
u64 type;
u64 flags;
};
template <typename E>
@@ -513,17 +518,26 @@ get_output_name(Context<E> &ctx, std::string_view name, u64 flags) {
template <typename E>
static OutputSectionKey
get_output_section_key(Context<E> &ctx, InputSection<E> &isec) {
// If .init_array/.fini_array exist, .ctors/.dtors must be merged
// with them.
//
// CRT object files contain .ctors/.dtors sections without any
// relocations. They contain sentinel values, 0 and -1, to mark the
// beginning and the end of the initializer/finalizer pointer arrays.
// We do not place them into .init_array/.fini_array because such
// invalid pointer values would simply make the program to crash.
if (ctx.has_init_array && !isec.get_rels(ctx).empty()) {
std::string_view name = isec.name();
if (name == ".ctors" || name.starts_with(".ctors."))
return {".init_array", SHT_INIT_ARRAY};
if (name == ".dtors" || name.starts_with(".dtors."))
return {".fini_array", SHT_FINI_ARRAY};
}
const ElfShdr<E> &shdr = isec.shdr();
std::string_view name = get_output_name(ctx, isec.name(), shdr.sh_flags);
u64 type = canonicalize_type<E>(name, shdr.sh_type);
u64 flags = shdr.sh_flags & ~(u64)(SHF_COMPRESSED | SHF_GROUP | SHF_GNU_RETAIN);
// .init_array is usually writable. We don't want to create multiple
// .init_array output sections, so make it always writable.
// So is .fini_array.
if (type == SHT_INIT_ARRAY || type == SHT_FINI_ARRAY)
flags |= SHF_WRITE;
return {name, type, flags};
return {name, type};
}
// Create output sections for input sections.
@@ -533,10 +547,7 @@ void create_output_sections(Context<E> &ctx) {
struct Hash {
size_t operator()(const OutputSectionKey &k) const {
u64 h = hash_string(k.name);
h = combine_hash(h, std::hash<u64>{}(k.type));
h = combine_hash(h, std::hash<u64>{}(k.flags));
return h;
return combine_hash(hash_string(k.name), std::hash<u64>{}(k.type));
}
};
@@ -560,9 +571,12 @@ void create_output_sections(Context<E> &ctx) {
continue;
const ElfShdr<E> &shdr = isec->shdr();
if (ctx.arg.relocatable && (shdr.sh_flags & SHF_GROUP)) {
OutputSection<E> *osec =
new OutputSection<E>(ctx, isec->name(), shdr.sh_type, shdr.sh_flags);
u32 sh_flags = shdr.sh_flags & ~SHF_MERGE & ~SHF_STRINGS &
~SHF_COMPRESSED & ~SHF_GNU_RETAIN;
if (ctx.arg.relocatable && (sh_flags & SHF_GROUP)) {
OutputSection<E> *osec = new OutputSection<E>(isec->name(), shdr.sh_type);
osec->sh_flags = sh_flags;
isec->output_section = osec;
ctx.osec_pool.emplace_back(osec);
continue;
@@ -583,7 +597,7 @@ void create_output_sections(Context<E> &ctx) {
}
std::unique_ptr<OutputSection<E>> osec =
std::make_unique<OutputSection<E>>(ctx, key.name, key.type, key.flags);
std::make_unique<OutputSection<E>>(key.name, key.type);
std::unique_lock lock(mu);
auto [it, inserted] = map.insert({key, osec.get()});
@@ -595,11 +609,42 @@ void create_output_sections(Context<E> &ctx) {
};
OutputSection<E> *osec = get_or_insert();
osec->sh_flags |= sh_flags & ~SHF_GROUP;
isec->output_section = osec;
cache.insert({key, osec});
}
});
for (std::unique_ptr<OutputSection<E>> &osec : ctx.osec_pool) {
osec->shdr.sh_flags = osec->sh_flags;
// Handle --section-align
if (!ctx.arg.section_align.empty())
if (auto it = ctx.arg.section_align.find(osec->name);
it != ctx.arg.section_align.end())
osec->shdr.sh_addralign = it->second;
// PT_GNU_RELRO segment is a security mechanism to make more pages
// read-only than we could have done without it.
//
// Traditionally, sections are either read-only or read-write. If a
// section contains dynamic relocations, it must have been put into a
// read-write segment so that the program loader can mutate its
// contents in memory, even if no one will write to it at runtime.
//
// RELRO segment allows us to make such pages writable only when a
// program is being loaded. After that, the page becomes read-only.
//
// Some sections, such as .init, .fini, .got, .dynamic, contain
// dynamic relocations but doesn't have to be writable at runtime,
// so they are put into a RELRO segment.
u32 type = osec->shdr.sh_type;
u32 flags = osec->shdr.sh_flags;
osec->is_relro = (osec->name == ".toc" || osec->name.ends_with(".rel.ro") ||
type == SHT_INIT_ARRAY || type == SHT_FINI_ARRAY ||
type == SHT_PREINIT_ARRAY || (flags & SHF_TLS));
}
// Add input sections to output sections
std::vector<Chunk<E> *> chunks;
for (i64 i = size; i < ctx.osec_pool.size(); i++)
@@ -661,34 +706,9 @@ void create_internal_file(Context<E> &ctx) {
ctx.internal_esyms.push_back(esym);
};
auto add_undef = [&](Symbol<E> *sym) {
obj->symbols.push_back(sym);
sym->value = 0xdeadbeef;
ElfSym<E> esym;
memset(&esym, 0, sizeof(esym));
esym.st_type = STT_NOTYPE;
esym.st_shndx = SHN_UNDEF;
esym.st_bind = STB_GLOBAL;
esym.st_visibility = STV_DEFAULT;
ctx.internal_esyms.push_back(esym);
};
// Add --defsym symbols
for (i64 i = 0; i < ctx.arg.defsyms.size(); i++) {
Symbol<E> *sym = ctx.arg.defsyms[i].first;
std::variant<Symbol<E> *, u64> val = ctx.arg.defsyms[i].second;
add(sym);
if (Symbol<E> **ref = std::get_if<Symbol<E> *>(&val)) {
// Add an undefined symbol to keep a reference to the defsym target.
// This prevents elimination by e.g. LTO or gc-sections.
// The undefined symbol will never make to the final object file; we
// double-check that the defsym target is not undefined in
// fix_synthetic_symbols.
add_undef(*ref);
}
}
// Add --defsym'd symbols
for (i64 i = 0; i < ctx.arg.defsyms.size(); i++)
add(ctx.arg.defsyms[i].first);
// Add --section-order symbols
for (SectionOrder &ord : ctx.arg.section_order)
@@ -782,8 +802,7 @@ void add_synthetic_symbols(Context<E> &ctx) {
ctx._TLS_MODULE_BASE_ = add("_TLS_MODULE_BASE_", STT_TLS);
if constexpr (is_riscv<E>)
if (!ctx.arg.shared)
ctx.__global_pointer = add("__global_pointer$");
ctx.__global_pointer = add("__global_pointer$");
if constexpr (is_arm32<E>) {
ctx.__exidx_start = add("__exidx_start");
@@ -808,6 +827,11 @@ void add_synthetic_symbols(Context<E> &ctx) {
}
}
if constexpr (is_ppc64v2<E>)
for (auto [label, insn] : ppc64_save_restore_insns)
if (!label.empty())
add(label);
obj.elf_syms = ctx.internal_esyms;
obj.has_symver.resize(ctx.internal_esyms.size() - 1);
@@ -821,26 +845,27 @@ void add_synthetic_symbols(Context<E> &ctx) {
// Handle --defsym symbols.
for (i64 i = 0; i < ctx.arg.defsyms.size(); i++) {
Symbol<E> *sym = ctx.arg.defsyms[i].first;
Symbol<E> *sym1 = ctx.arg.defsyms[i].first;
std::variant<Symbol<E> *, u64> val = ctx.arg.defsyms[i].second;
Symbol<E> *target = nullptr;
if (Symbol<E> **ref = std::get_if<Symbol<E> *>(&val))
target = *ref;
if (Symbol<E> **ref = std::get_if<Symbol<E> *>(&val)) {
Symbol<E> *sym2 = *ref;
if (!sym2->file) {
Error(ctx) << "--defsym: undefined symbol: " << *sym2;
continue;
}
// If the alias refers another symobl, copy ELF symbol attributes.
if (target) {
ElfSym<E> &esym = obj.elf_syms[i + 1];
esym.st_type = target->esym().st_type;
esym.st_type = sym2->esym().st_type;
if constexpr (is_ppc64v2<E>)
esym.ppc_local_entry = target->esym().ppc_local_entry;
esym.ppc_local_entry = sym2->esym().ppc_local_entry;
if (sym2->is_absolute())
sym1->origin = 0;
} else {
sym1->origin = 0;
}
// Make the target absolute if necessary.
if (!target || target->is_absolute())
sym->origin = 0;
}
}
template <typename E>
@@ -972,17 +997,16 @@ void write_repro_file(Context<E> &ctx) {
tar->append("response.txt", save_string(ctx, create_response_file(ctx)));
tar->append("version.txt", save_string(ctx, mold_version + "\n"));
std::unordered_set<std::string> seen;
std::unordered_set<std::string_view> seen;
for (std::unique_ptr<MappedFile<Context<E>>> &mf : ctx.mf_pool) {
if (!mf->parent) {
std::string path = to_abs_path(mf->name).string();
if (seen.insert(path).second) {
// We reopen a file because we may have modified the contents of mf
// in memory, which is mapped with PROT_WRITE and MAP_PRIVATE.
MappedFile<Context<E>> *mf2 = MappedFile<Context<E>>::must_open(ctx, path);
tar->append(path, mf2->get_contents());
mf2->unmap();
}
if (!mf->parent && seen.insert(mf->name).second) {
// We reopen a file because we may have modified the contents of mf
// in memory, which is mapped with PROT_WRITE and MAP_PRIVATE.
MappedFile<Context<E>> *mf2 =
MappedFile<Context<E>>::must_open(ctx, mf->name);
tar->append(path, mf2->get_contents());
mf2->unmap();
}
}
}
@@ -1047,19 +1071,42 @@ void check_symbol_types(Context<E> &ctx) {
});
}
template <typename E>
static i64 get_init_fini_priority(InputSection<E> *isec) {
static std::regex re(R"(\.(\d+)$)", std::regex_constants::optimize);
std::string_view name = isec->name();
std::cmatch m;
if (std::regex_search(name.data(), name.data() + name.size(), m, re))
return std::stoi(m[1]);
return 65536;
}
template <typename E>
static i64 get_ctor_dtor_priority(InputSection<E> *isec) {
auto opts = std::regex_constants::optimize | std::regex_constants::ECMAScript;
static std::regex re1(R"((?:clang_rt\.)?crtbegin)", opts);
static std::regex re2(R"((?:clang_rt\.)?crtend)", opts);
static std::regex re3(R"(\.(\d+)$)", opts);
// crtbegin.o and crtend.o contain marker symbols such as
// __CTOR_LIST__ or __DTOR_LIST__. So they have to be at the
// beginning or end of the section.
std::smatch m;
if (std::regex_search(isec->file.filename, m, re1))
return -2;
if (std::regex_search(isec->file.filename, m, re2))
return 65536;
std::string name(isec->name());
if (std::regex_search(name, m, re3))
return std::stoi(m[1]);
return -1;
}
template <typename E>
void sort_init_fini(Context<E> &ctx) {
Timer t(ctx, "sort_init_fini");
auto get_priority = [](InputSection<E> *isec) {
static std::regex re(R"(\.(\d+)$)", std::regex_constants::optimize);
std::string_view name = isec->name();
std::cmatch m;
if (std::regex_search(name.data(), name.data() + name.size(), m, re))
return std::stoi(m[1]);
return 65536;
};
for (Chunk<E> *chunk : ctx.chunks) {
if (OutputSection<E> *osec = chunk->to_osec()) {
if (osec->name == ".init_array" || osec->name == ".preinit_array" ||
@@ -1068,8 +1115,14 @@ void sort_init_fini(Context<E> &ctx) {
std::reverse(osec->members.begin(), osec->members.end());
std::unordered_map<InputSection<E> *, i64> map;
for (InputSection<E> *isec : osec->members)
map.insert({isec, get_priority(isec)});
for (InputSection<E> *isec : osec->members) {
std::string_view name = isec->name();
if (name.starts_with(".ctors") || name.starts_with(".dtors"))
map.insert({isec, 65535 - get_ctor_dtor_priority(isec)});
else
map.insert({isec, get_init_fini_priority(isec)});
}
sort(osec->members, [&](InputSection<E> *a, InputSection<E> *b) {
return map[a] < map[b];
@@ -1083,27 +1136,6 @@ template <typename E>
void sort_ctor_dtor(Context<E> &ctx) {
Timer t(ctx, "sort_ctor_dtor");
auto get_priority = [](InputSection<E> *isec) {
auto opts = std::regex_constants::optimize | std::regex_constants::ECMAScript;
static std::regex re1(R"((?:clang_rt\.)?crtbegin)", opts);
static std::regex re2(R"((?:clang_rt\.)?crtend)", opts);
static std::regex re3(R"(\.(\d+)$)", opts);
// crtbegin.o and crtend.o contain marker symbols such as
// __CTOR_LIST__ or __DTOR_LIST__. So they have to be at the
// beginning or end of the section.
std::smatch m;
if (std::regex_search(isec->file.filename, m, re1))
return -2;
if (std::regex_search(isec->file.filename, m, re2))
return 65536;
std::string name(isec->name());
if (std::regex_search(name, m, re3))
return std::stoi(m[1]);
return -1;
};
for (Chunk<E> *chunk : ctx.chunks) {
if (OutputSection<E> *osec = chunk->to_osec()) {
if (osec->name == ".ctors" || osec->name == ".dtors") {
@@ -1112,7 +1144,7 @@ void sort_ctor_dtor(Context<E> &ctx) {
std::unordered_map<InputSection<E> *, i64> map;
for (InputSection<E> *isec : osec->members)
map.insert({isec, get_priority(isec)});
map.insert({isec, get_ctor_dtor_priority(isec)});
sort(osec->members, [&](InputSection<E> *a, InputSection<E> *b) {
return map[a] < map[b];
@@ -1122,6 +1154,45 @@ void sort_ctor_dtor(Context<E> &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
// but in the exact opposite order to one another. Therefore, if we are to
// place the contents of .ctors/.dtors into .init_array/.fini_array, we
// need to reverse them.
//
// It's unfortunate that we have both .ctors/.dtors and
// .init_array/.fini_array in ELF for historical reasons, but that's
// the reality we need to deal with.
template <typename E>
void fixup_ctors_in_init_array(Context<E> &ctx) {
Timer t(ctx, "fixup_ctors_in_init_array");
for (Chunk<E> *chunk : ctx.chunks) {
if (OutputSection<E> *osec = chunk->to_osec()) {
if (osec->name == ".init_array" || osec->name == ".fini_array") {
for (InputSection<E> *isec : osec->members) {
if (isec->name().starts_with(".ctors") ||
isec->name().starts_with(".dtors")) {
if (isec->sh_size % sizeof(Word<E>)) {
Error(ctx) << *isec << ": section corrupted";
continue;
}
u8 *buf = (u8 *)isec->contents.data();
std::reverse((Word<E> *)buf, (Word<E> *)(buf + isec->sh_size));
std::span<ElfRel<E>> rels = isec->get_rels(ctx);
for (ElfRel<E> &r : rels)
r.r_offset = isec->sh_size - r.r_offset - sizeof(Word<E>);
std::reverse(rels.begin(), rels.end());
}
}
}
}
}
}
template <typename T>
static void shuffle(std::vector<T> &vec, u64 seed) {
if (vec.empty())
@@ -1442,20 +1513,40 @@ void scan_relocations(Context<E> &ctx) {
Warn(ctx) << "creating a DT_TEXTREL in an output file";
}
// Compute the is_weak bit for each imported symbol.
//
// If all references to a shared symbol is weak, the symbol is marked
// as weak in .dynsym.
template <typename E>
void compute_imported_symbol_weakness(Context<E> &ctx) {
Timer t(ctx, "compute_imported_symbol_weakness");
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (i64 i = file->first_global; i < file->elf_syms.size(); i++) {
const ElfSym<E> &esym = file->elf_syms[i];
Symbol<E> &sym = *file->symbols[i];
if (esym.is_undef() && !esym.is_weak() && sym.file && sym.file->is_dso) {
std::scoped_lock lock(sym.mu);
sym.is_weak = false;
}
}
});
}
// Report all undefined symbols, grouped by symbol.
template <typename E>
void report_undef_errors(Context<E> &ctx) {
constexpr i64 max_errors = 3;
for (auto &pair : ctx.undef_errors) {
std::string_view sym_name = pair.first;
Symbol<E> *sym = pair.first;
std::span<std::string> errors = pair.second;
if (ctx.arg.demangle)
sym_name = demangle(sym_name);
std::stringstream ss;
ss << "undefined symbol: " << sym_name << "\n";
ss << "undefined symbol: "
<< (ctx.arg.demangle ? demangle(*sym) : sym->name())
<< "\n";
for (i64 i = 0; i < errors.size() && i < max_errors; i++)
ss << errors[i];
@@ -1503,12 +1594,12 @@ void copy_chunks(Context<E> &ctx) {
// sections first. This is because REL-type relocation sections (as
// opposed to RELA-type) stores relocation addends to target sections.
tbb::parallel_for_each(ctx.chunks, [&](Chunk<E> *chunk) {
if (chunk->shdr.sh_type != (E::is_rela ? SHT_RELA : SHT_REL))
if (chunk->shdr.sh_type != SHT_REL)
copy(*chunk);
});
tbb::parallel_for_each(ctx.chunks, [&](Chunk<E> *chunk) {
if (chunk->shdr.sh_type == (E::is_rela ? SHT_RELA : SHT_REL))
if (chunk->shdr.sh_type == SHT_REL)
copy(*chunk);
});
@@ -1522,6 +1613,60 @@ void copy_chunks(Context<E> &ctx) {
fixup_arm_exidx_section(ctx);
}
// Rewrite the leading endbr64 instruction with a nop if a function
// symbol's address was not taken.
template <typename E>
void rewrite_endbr(Context<E> &ctx) {
Timer t(ctx, "rewrite_endbr");
assert(is_x86_64<E>);
// Compute address-taken bit for each symbol
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (std::unique_ptr<InputSection<E>> &isec : file->sections) {
if (isec && isec->is_alive && (isec->shdr().sh_flags & SHF_ALLOC)) {
for (const ElfRel<E> &rel : isec->get_rels(ctx)) {
Symbol<E> &sym = *file->symbols[rel.r_sym];
if (!is_func_call_rel(rel) && sym.esym().st_type == STT_FUNC) {
std::scoped_lock lock(sym.mu);
sym.address_taken = true;
}
}
}
}
});
// Exported symbols are conservatively assumed to be address-taken.
if (ctx.dynsym)
for (Symbol<E> *sym : ctx.dynsym->symbols)
if (sym && sym->is_exported)
sym->address_taken = true;
// Some symbols are implicitly address-taken
ctx.arg.entry->address_taken = true;
ctx.arg.init->address_taken = true;
ctx.arg.fini->address_taken = true;
// Rewrite endbr64 with nop
u8 endbr64[] = {0xf3, 0x0f, 0x1e, 0xfa};
u8 nop[] = {0x0f, 0x1f, 0x40, 0x00};
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (Symbol<E> *sym : file->symbols) {
if (sym->file == file && sym->esym().st_type == STT_FUNC &&
!sym->address_taken) {
if (InputSection<E> *isec = sym->get_input_section()) {
if (OutputSection<E> *osec = isec->output_section) {
u8 *buf = ctx.buf + osec->shdr.sh_offset + isec->offset +
sym->value;
if (memcmp(buf, endbr64, 4) == 0)
memcpy(buf, nop, 4);
}
}
}
}
});
}
template <typename E>
void construct_relr(Context<E> &ctx) {
Timer t(ctx, "construct_relr");
@@ -1554,18 +1699,68 @@ template <typename E>
void apply_version_script(Context<E> &ctx) {
Timer t(ctx, "apply_version_script");
// If all patterns are simple (i.e. not containing any meta-
// characters and is not a C++ name), we can simply look up
// symbols.
auto is_simple = [&] {
for (VersionPattern &v : ctx.version_patterns)
if (v.is_cpp || v.pattern.find_first_of("*?[") != v.pattern.npos)
return false;
return true;
};
// Assign versions to symbols specified with `extern "C++"` or
// wildcard patterns first.
MultiGlob matcher;
MultiGlob cpp_matcher;
if (is_simple()) {
for (VersionPattern &v : ctx.version_patterns) {
// The "local:" label has a special meaning in the version script.
// It can appear in any VERSION clause, and it hides matched symbols
// unless other non-local patterns match to them. In other words,
// "local:" has lower precedence than other version definitions.
//
// If two or more non-local patterns match to the same symbol, the
// last one takes precedence.
std::vector<VersionPattern> patterns = ctx.version_patterns;
std::stable_partition(patterns.begin(), patterns.end(),
[](const VersionPattern &pat) {
return pat.ver_idx == VER_NDX_LOCAL;
});
for (i64 i = 0; i < patterns.size(); i++) {
VersionPattern &v = patterns[i];
if (v.is_cpp) {
if (!cpp_matcher.add(v.pattern, i))
Fatal(ctx) << "invalid version pattern: " << v.pattern;
} else if (v.pattern.find_first_of("*?[") != v.pattern.npos) {
if (!matcher.add(v.pattern, i))
Fatal(ctx) << "invalid version pattern: " << v.pattern;
}
}
if (!matcher.empty() || !cpp_matcher.empty()) {
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (Symbol<E> *sym : file->get_global_syms()) {
if (sym->file != file)
continue;
std::string_view name = sym->name();
i64 match = -1;
if (std::optional<i64> idx = matcher.find(name))
match = std::max(match, *idx);
// Match non-mangled symbols against the C++ pattern as well.
// Weird, but required to match other linkers' behavior.
if (!cpp_matcher.empty()) {
if (std::optional<std::string_view> s = demangle_cpp(name))
name = *s;
if (std::optional<i64> idx = cpp_matcher.find(name))
match = std::max(match, *idx);
}
if (match != -1)
sym->ver_idx = patterns[match].ver_idx;
}
});
}
// Next, assign versions to symbols specified by exact name.
// In other words, exact matches have higher precedence over
// wildcard or `extern "C++"` patterns.
for (VersionPattern &v : patterns) {
if (!v.is_cpp && v.pattern.find_first_of("*?[") == v.pattern.npos) {
Symbol<E> *sym = get_symbol(ctx, v.pattern);
if (!sym->file && !ctx.arg.undefined_version)
@@ -1575,48 +1770,7 @@ void apply_version_script(Context<E> &ctx) {
if (sym->file && !sym->file->is_dso)
sym->ver_idx = v.ver_idx;
}
return;
}
// Otherwise, use glob pattern matchers.
MultiGlob matcher;
MultiGlob cpp_matcher;
for (i64 i = 0; i < ctx.version_patterns.size(); i++) {
VersionPattern &v = ctx.version_patterns[i];
if (v.is_cpp) {
if (!cpp_matcher.add(v.pattern, i))
Fatal(ctx) << "invalid version pattern: " << v.pattern;
} else {
if (!matcher.add(v.pattern, i))
Fatal(ctx) << "invalid version pattern: " << v.pattern;
}
}
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (Symbol<E> *sym : file->get_global_syms()) {
if (sym->file != file)
continue;
std::string_view name = sym->name();
i64 match = INT64_MAX;
if (std::optional<u32> idx = matcher.find(name))
match = std::min<i64>(match, *idx);
// Match non-mangled symbols against the C++ pattern as well.
// Weird, but required to match other linkers' behavior.
if (!cpp_matcher.empty()) {
if (std::optional<std::string_view> s = cpp_demangle(name))
name = *s;
if (std::optional<u32> idx = cpp_matcher.find(name))
match = std::min<i64>(match, *idx);
}
if (match != INT64_MAX)
sym->ver_idx = ctx.version_patterns[match].ver_idx;
}
});
}
template <typename E>
@@ -1631,6 +1785,9 @@ void parse_symbol_version(Context<E> &ctx) {
verdefs[ctx.arg.version_definitions[i]] = i + VER_NDX_LAST_RESERVED + 1;
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
if (file == ctx.internal_obj)
return;
for (i64 i = file->first_global; i < file->elf_syms.size(); i++) {
// Match VERSION part of symbol foo@VERSION with version definitions.
if (!file->has_symver.get(i - file->first_global))
@@ -1640,14 +1797,8 @@ void parse_symbol_version(Context<E> &ctx) {
if (sym->file != file)
continue;
std::string_view ver;
if (file->is_lto_obj) {
ver = file->lto_symbol_versions[i - file->first_global];
} else {
const char *name = file->symbol_strtab.data() + file->elf_syms[i].st_name;
ver = strchr(name, '@') + 1;
}
const char *name = file->symbol_strtab.data() + file->elf_syms[i].st_name;
std::string_view ver = strchr(name, '@') + 1;
bool is_default = false;
if (ver.starts_with('@')) {
@@ -1689,44 +1840,122 @@ void compute_import_export(Context<E> &ctx) {
if (!ctx.arg.shared) {
tbb::parallel_for_each(ctx.dsos, [&](SharedFile<E> *file) {
for (Symbol<E> *sym : file->symbols) {
if (sym->file && !sym->file->is_dso && sym->visibility != STV_HIDDEN) {
if (sym->ver_idx != VER_NDX_LOCAL ||
!ctx.default_version_from_version_script) {
std::scoped_lock lock(sym->mu);
sym->is_exported = true;
}
if (sym->file && !sym->file->is_dso && sym->visibility != STV_HIDDEN &&
sym->ver_idx != VER_NDX_LOCAL) {
std::scoped_lock lock(sym->mu);
sym->is_exported = true;
}
}
});
}
auto should_export = [&](Symbol<E> &sym) {
if (sym.visibility == STV_HIDDEN)
return false;
switch (sym.ver_idx) {
case VER_NDX_UNSPECIFIED:
if (ctx.arg.shared)
return !((ObjectFile<E> *)sym.file)->exclude_libs;
return ctx.arg.export_dynamic;
case VER_NDX_LOCAL:
return false;
default:
return true;
}
};
// Export symbols that are not hidden or marked as local.
// We also want to mark imported symbols as such.
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (Symbol<E> *sym : file->get_global_syms()) {
if (!sym->file || sym->visibility == STV_HIDDEN ||
sym->ver_idx == VER_NDX_LOCAL)
continue;
// If we are using a symbol in a DSO, we need to import it at runtime.
if (sym->file != file && sym->file->is_dso && !sym->is_absolute()) {
// If we are using a symbol in a DSO, we need to import it.
if (sym->file && sym->file->is_dso) {
std::scoped_lock lock(sym->mu);
sym->is_imported = true;
continue;
}
// If we are creating a DSO, all global symbols are exported by default.
if (sym->file == file) {
std::scoped_lock lock(sym->mu);
// If we have a definition of a symbol, we may want to export it.
if (sym->file == file && should_export(*sym)) {
sym->is_exported = true;
if (ctx.arg.shared && sym->visibility != STV_PROTECTED &&
// Exported symbols are marked as imported as well by default
// for DSOs.
if (ctx.arg.shared &&
sym->visibility != STV_PROTECTED &&
!ctx.arg.Bsymbolic &&
!(ctx.arg.Bsymbolic_functions && sym->get_type() == STT_FUNC))
sym->is_imported = true;
}
}
});
// Apply --dynamic-list, --export-dynamic-symbol and
// --export-dynamic-symbol-list options.
//
// The semantics of these options vary depending on whether we are
// creating an executalbe or a shared object.
//
// For executable, matched symbols are exported.
//
// For shared objects, matched symbols are imported if it is already
// exported so that they are interposable. In other words, symbols
// that did not match will be bound locally within the output file,
// effectively turning them into protected symbols.
MultiGlob matcher;
MultiGlob cpp_matcher;
auto handle_match = [&](Symbol<E> *sym) {
if (ctx.arg.shared) {
if (sym->is_exported)
sym->is_imported = true;
} else {
if (sym->file && !sym->file->is_dso && sym->visibility != STV_HIDDEN)
sym->is_exported = true;
}
};
for (DynamicPattern &p : ctx.dynamic_list_patterns) {
if (p.is_cpp) {
if (!cpp_matcher.add(p.pattern, 1))
Fatal(ctx) << p.source << ": invalid dynamic list entry: "
<< p.pattern;
continue;
}
if (p.pattern.find_first_of("*?[") != p.pattern.npos) {
if (!matcher.add(p.pattern, 1))
Fatal(ctx) << p.source << ": invalid dynamic list entry: "
<< p.pattern;
continue;
}
handle_match(get_symbol(ctx, p.pattern));
}
if (!matcher.empty() || !cpp_matcher.empty()) {
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (Symbol<E> *sym : file->get_global_syms()) {
if (sym->file != file)
continue;
if (ctx.arg.shared && !sym->is_exported)
continue;
std::string_view name = sym->name();
if (matcher.find(name)) {
handle_match(sym);
} else if (!cpp_matcher.empty()) {
if (std::optional<std::string_view> s = demangle_cpp(name))
name = *s;
if (cpp_matcher.find(name))
handle_match(sym);
}
}
});
}
}
// Compute the "address-taken" bit for each input section.
@@ -1783,14 +2012,14 @@ void compute_address_significance(Context<E> &ctx) {
};
// Some symbols' pointer values are leaked to the dynamic section.
mark(get_symbol(ctx, ctx.arg.entry));
mark(get_symbol(ctx, ctx.arg.init));
mark(get_symbol(ctx, ctx.arg.fini));
mark(ctx.arg.entry);
mark(ctx.arg.init);
mark(ctx.arg.fini);
// Exported symbols are conservatively considered address-taken.
if (ctx.dynsym)
for (Symbol<E> *sym : ctx.dynsym->symbols)
if (sym->is_exported)
if (sym && sym->is_exported)
mark(sym);
// Handle data objects.
@@ -1798,11 +2027,8 @@ void compute_address_significance(Context<E> &ctx) {
if (InputSection<E> *sec = file->llvm_addrsig.get()) {
u8 *p = (u8 *)sec->contents.data();
u8 *end = p + sec->contents.size();
while (p != end) {
Symbol<E> *sym = file->symbols[read_uleb(&p)];
if (InputSection<E> *isec = sym->get_input_section())
isec->address_taken = true;
}
while (p != end)
mark(file->symbols[read_uleb(&p)]);
} else {
for (std::unique_ptr<InputSection<E>> &isec : file->sections)
if (isec && !(isec->shdr().sh_flags & SHF_EXECINSTR))
@@ -2133,7 +2359,7 @@ static void set_virtual_addresses_regular(Context<E> &ctx) {
i64 flags1 = get_flags(chunks[i - 1]);
i64 flags2 = get_flags(chunks[i]);
if (flags1 != flags2) {
if (!ctx.arg.nmagic && flags1 != flags2) {
switch (ctx.arg.z_separate_code) {
case SEPARATE_LOADABLE_SEGMENTS:
addr = align_to(addr, ctx.page_size);
@@ -2279,6 +2505,8 @@ static i64 set_file_offsets(Context<E> &ctx) {
}
if (first.shdr.sh_type == SHT_NOBITS) {
fileoff = align_to(fileoff, first.shdr.sh_addralign);
first.shdr.sh_offset = fileoff;
i++;
continue;
}
@@ -2319,8 +2547,11 @@ static i64 set_file_offsets(Context<E> &ctx) {
while (i < chunks.size() &&
(chunks[i]->shdr.sh_flags & SHF_ALLOC) &&
chunks[i]->shdr.sh_type == SHT_NOBITS)
chunks[i]->shdr.sh_type == SHT_NOBITS) {
fileoff = align_to(fileoff, chunks[i]->shdr.sh_addralign);
chunks[i]->shdr.sh_offset = fileoff;
i++;
}
}
return fileoff;
@@ -2564,6 +2795,18 @@ 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 (auto [label, insn] : ppc64_save_restore_insns) {
if (!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_ and __stop_ symbols
for (Chunk<E> *chunk : sections) {
if (std::optional<std::string> name = get_start_stop_name(ctx, *chunk)) {
@@ -2592,18 +2835,12 @@ void fix_synthetic_symbols(Context<E> &ctx) {
if (u64 *addr = std::get_if<u64>(&val)) {
sym->origin = 0;
sym->value = *addr;
continue;
} else {
Symbol<E> *sym2 = std::get<Symbol<E> *>(val);
sym->value = sym2->value;
sym->origin = sym2->origin;
sym->visibility = sym2->visibility.load();
}
Symbol<E> *sym2 = std::get<Symbol<E> *>(val);
if (!sym2->file) {
Error(ctx) << "--defsym: undefined symbol: " << *sym2;
continue;
}
sym->value = sym2->value;
sym->origin = sym2->origin;
sym->visibility = sym2->visibility.load();
}
@@ -2726,7 +2963,7 @@ void show_stats(Context<E> &ctx) {
static Counter thunk_bytes("thunk_bytes");
for (Chunk<E> *chunk : ctx.chunks)
if (OutputSection<E> *osec = chunk->to_osec())
for (std::unique_ptr<RangeExtensionThunk<E>> &thunk : osec->thunks)
for (std::unique_ptr<Thunk<E>> &thunk : osec->thunks)
thunk_bytes += thunk->size();
}
@@ -2756,14 +2993,17 @@ template void check_duplicate_symbols(Context<E> &);
template void check_symbol_types(Context<E> &);
template void sort_init_fini(Context<E> &);
template void sort_ctor_dtor(Context<E> &);
template void fixup_ctors_in_init_array(Context<E> &);
template void shuffle_sections(Context<E> &);
template void compute_section_sizes(Context<E> &);
template void sort_output_sections(Context<E> &);
template void claim_unresolved_symbols(Context<E> &);
template void compute_imported_symbol_weakness(Context<E> &);
template void scan_relocations(Context<E> &);
template void report_undef_errors(Context<E> &);
template void create_reloc_sections(Context<E> &);
template void copy_chunks(Context<E> &);
template void rewrite_endbr(Context<E> &);
template void construct_relr(Context<E> &);
template void create_output_symtab(Context<E> &);
template void apply_version_script(Context<E> &);
+15 -18
View File
@@ -119,7 +119,7 @@ static bool is_reachable(Context<E> &ctx, InputSection<E> &isec,
return -max_distance() <= val && val < max_distance();
}
static void reset_thunk(RangeExtensionThunk<E> &thunk) {
static void reset_thunk(Thunk<E> &thunk) {
for (Symbol<E> *sym : thunk.symbols) {
sym->extra.thunk_idx = -1;
sym->extra.thunk_sym_idx = -1;
@@ -129,10 +129,10 @@ static void reset_thunk(RangeExtensionThunk<E> &thunk) {
// Scan relocations to collect symbols that need thunks.
static void scan_rels(Context<E> &ctx, InputSection<E> &isec,
RangeExtensionThunk<E> &thunk, i64 thunk_idx) {
Thunk<E> &thunk, i64 thunk_idx) {
std::span<const ElfRel<E>> rels = isec.get_rels(ctx);
std::vector<RangeExtensionRef> &range_extn = isec.extra.range_extn;
range_extn.resize(rels.size());
std::vector<ThunkRef> &thunk_refs = isec.extra.thunk_refs;
thunk_refs.resize(rels.size());
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
@@ -151,15 +151,15 @@ static void scan_rels(Context<E> &ctx, InputSection<E> &isec,
// This relocation needs a thunk. If the symbol is already in a
// previous thunk, reuse it.
if (sym.extra.thunk_idx != -1) {
range_extn[i].thunk_idx = sym.extra.thunk_idx;
range_extn[i].sym_idx = sym.extra.thunk_sym_idx;
thunk_refs[i].thunk_idx = sym.extra.thunk_idx;
thunk_refs[i].sym_idx = sym.extra.thunk_sym_idx;
continue;
}
// Otherwise, add the symbol to the current thunk if it's not
// added already.
range_extn[i].thunk_idx = thunk_idx;
range_extn[i].sym_idx = -1;
thunk_refs[i].thunk_idx = thunk_idx;
thunk_refs[i].sym_idx = -1;
if (sym.flags.exchange(-1) == 0) {
std::scoped_lock lock(thunk.mu);
@@ -170,8 +170,6 @@ static void scan_rels(Context<E> &ctx, InputSection<E> &isec,
template <>
void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
using Thunk = RangeExtensionThunk<E>;
std::span<InputSection<E> *> m = members;
if (m.empty())
return;
@@ -179,9 +177,8 @@ void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
// Initialize input sections with a dummy offset so that we can
// distinguish sections that have got an address with the one who
// haven't.
tbb::parallel_for_each(m, [](InputSection<E> *isec) {
for (InputSection<E> *isec : m)
isec->offset = -1;
});
// We create thunks from the beginning of the section to the end.
// We manage progress using four offsets which increase monotonically.
@@ -215,7 +212,7 @@ void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
// Move D foward as far as we can jump from B to a thunk at D.
auto d_thunk_end = [&] {
u64 d_end = align_to(offset, 1 << m[d]->p2align) + m[d]->sh_size;
return align_to(d_end, Thunk::alignment) + max_thunk_size;
return align_to(d_end, Thunk<E>::alignment) + max_thunk_size;
};
while (d < m.size() &&
@@ -243,9 +240,9 @@ void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
reset_thunk(*thunks[t++]);
// Create a new thunk and place it at D.
offset = align_to(offset, Thunk::alignment);
offset = align_to(offset, Thunk<E>::alignment);
i64 thunk_idx = thunks.size();
Thunk *thunk = new Thunk(*this, offset);
Thunk<E> *thunk = new Thunk<E>(*this, offset);
thunks.emplace_back(thunk);
// Scan relocations between B and C to collect symbols that need
@@ -278,11 +275,11 @@ void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
[&](InputSection<E> *isec) {
std::span<Symbol<E> *> syms = isec->file.symbols;
std::span<const ElfRel<E>> rels = isec->get_rels(ctx);
std::span<RangeExtensionRef> range_extn = isec->extra.range_extn;
std::span<ThunkRef> thunk_refs = isec->extra.thunk_refs;
for (i64 i = 0; i < rels.size(); i++)
if (range_extn[i].thunk_idx == thunk_idx)
range_extn[i].sym_idx = syms[rels[i].r_sym]->extra.thunk_sym_idx;
if (thunk_refs[i].thunk_idx == thunk_idx)
thunk_refs[i].sym_idx = syms[rels[i].r_sym]->extra.thunk_sym_idx;
});
// Move B forward to point to the begining of the next batch.
+2 -2
View File
@@ -20,7 +20,7 @@ ubuntu-* | pop-* | linuxmint-* | debian-* | raspbian-*)
apt-get install -y cmake gcc g++
apt-get install -y file
;;
fedora-*)
fedora-* | amzn-* | rhel-*)
dnf install -y gcc-g++ cmake
dnf install -y glibc-static file libstdc++-static diffutils util-linux
;;
@@ -36,7 +36,7 @@ gentoo-*)
emerge-webrsync
emerge dev-util/cmake
;;
arch-* | archarm-* | artix-*)
arch-* | archarm-* | artix-* | endeavouros-*)
pacman -Sy
pacman -S --needed --noconfirm base-devel cmake util-linux
;;
+142 -52
View File
@@ -1,6 +1,9 @@
option(MOLD_ENABLE_QEMU_TESTS "Enable tests on non-native targets" OFF)
option(MOLD_ENABLE_QEMU_TESTS "Enable tests on non-native targets" ON)
set(MACHINE ${CMAKE_HOST_SYSTEM_PROCESSOR})
set(HOST ${CMAKE_HOST_SYSTEM_PROCESSOR})
if(EXISTS "/proc/cpuinfo")
file(READ "/proc/cpuinfo" CPUINFO)
endif()
# CMAKE_HOST_SYSTEM_PROCESSOR returns "aarch64" on ARM32 userland
# with ARM64 kernel. Here, we run `cc -dumpmachine` to determine the
@@ -10,82 +13,169 @@ execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpmachine
OUTPUT_VARIABLE ARCH
ERROR_QUIET)
if(NOT EXIT_CODE AND ARCH MATCHES "([^-]+).*")
set(HOST ${CMAKE_MATCH_1})
set(MACHINE ${CMAKE_MATCH_1})
endif()
if(${HOST} MATCHES "amd64")
set(HOST x86_64)
elseif(${HOST} MATCHES "i386")
set(HOST i686)
elseif(${HOST} MATCHES "arm.*")
set(HOST arm)
elseif(${HOST} STREQUAL "powerpc64")
set(HOST ppc64)
elseif(${HOST} STREQUAL "powerpc64le")
set(HOST ppc64le)
if(${MACHINE} MATCHES "amd64")
set(MACHINE x86_64)
elseif(${MACHINE} MATCHES "i386")
set(MACHINE i686)
elseif(${MACHINE} MATCHES "arm.*")
set(MACHINE arm)
elseif(${MACHINE} STREQUAL "powerpc64")
set(MACHINE ppc64)
elseif(${MACHINE} STREQUAL "powerpc64le")
set(MACHINE ppc64le)
endif()
if(MOLD_ENABLE_QEMU_TESTS)
list(APPEND QEMU_ARCHS
x86_64 i386 arm aarch64 ppc ppc64 ppc64le sparc64 sh4 s390x
alpha riscv64 riscv32 m68k loongarch64)
LIST(APPEND TRIPLES
x86_64-linux-gnu
i686-linux-gnu
aarch64-linux-gnu
arm-linux-gnueabihf
riscv64-linux-gnu
powerpc-linux-gnu
powerpc64-linux-gnu
powerpc64le-linux-gnu
sparc64-linux-gnu
s390x-linux-gnu
sh4-linux-gnu
alpha-linux-gnu
riscv32-linux-gnu
m68k-linux-gnu
loongarch64-linux-gnu)
foreach(ARCH IN LISTS QEMU_ARCHS)
find_program(HAS_qemu-${ARCH} qemu-${ARCH})
endforeach()
foreach(TRIPLE IN LISTS TRIPLES)
find_program(HAS_${TRIPLE}-gcc ${TRIPLE}-gcc)
endforeach()
endif()
function(add_target ARCH TRIPLE)
if(${ARCH} STREQUAL ${HOST})
set(CPU ${ARGV2})
if(${ARCH} STREQUAL ${MACHINE})
set(IS_NATIVE 1)
endif()
if(IS_NATIVE OR MOLD_ENABLE_QEMU_TESTS)
file(GLOB ALL_TESTS RELATIVE ${CMAKE_CURRENT_LIST_DIR} CONFIGURE_DEPENDS "*.sh")
file(GLOB ALL_TESTS RELATIVE ${CMAKE_CURRENT_LIST_DIR} CONFIGURE_DEPENDS
"*.sh")
list(FILTER ALL_TESTS EXCLUDE REGEX "_")
list(FILTER ALL_TESTS EXCLUDE REGEX "_")
file(GLOB TESTS RELATIVE ${CMAKE_CURRENT_LIST_DIR} CONFIGURE_DEPENDS "${ARCH}_*.sh")
file(GLOB TESTS RELATIVE ${CMAKE_CURRENT_LIST_DIR} CONFIGURE_DEPENDS
"${ARCH}_*.sh")
list(APPEND TESTS ${ALL_TESTS})
list(APPEND TESTS ${ALL_TESTS})
foreach(TEST IN LISTS TESTS)
foreach(TEST IN LISTS TESTS)
if(CPU)
string(REGEX REPLACE "\\.sh$" "" TESTNAME "${ARCH}_${CPU}-${TEST}")
else()
string(REGEX REPLACE "\\.sh$" "" TESTNAME "${ARCH}-${TEST}")
endif()
add_test(NAME ${TESTNAME}
COMMAND bash -x ${CMAKE_CURRENT_LIST_DIR}/${TEST}
WORKING_DIRECTORY ${mold_BINARY_DIR})
add_test(NAME ${TESTNAME}
COMMAND bash -x ${CMAKE_CURRENT_LIST_DIR}/${TEST}
WORKING_DIRECTORY ${mold_BINARY_DIR})
set_property(TEST ${TESTNAME} APPEND PROPERTY ENVIRONMENT "HOST=${HOST}")
set_property(TEST ${TESTNAME} APPEND PROPERTY ENVIRONMENT
"MACHINE=${MACHINE};CPU=${CPU}")
if(IS_NATIVE)
set_property(TEST ${TESTNAME} APPEND PROPERTY SKIP_REGULAR_EXPRESSION "skipped")
else()
set_property(TEST ${TESTNAME} APPEND PROPERTY ENVIRONMENT "TRIPLE=${TRIPLE}")
endif()
endforeach()
endif()
if(IS_NATIVE)
set_property(TEST ${TESTNAME} APPEND PROPERTY SKIP_REGULAR_EXPRESSION
"skipped")
else()
set_property(TEST ${TESTNAME} APPEND PROPERTY ENVIRONMENT
"TRIPLE=${TRIPLE}")
endif()
endforeach()
endfunction()
add_target(x86_64 x86_64-linux-gnu)
add_target(i686 i686-linux-gnu)
add_target(aarch64 aarch64-linux-gnu)
add_target(arm arm-linux-gnueabihf)
add_target(riscv64 riscv64-linux-gnu)
add_target(ppc powerpc-linux-gnu)
add_target(ppc64 powerpc64-linux-gnu)
add_target(ppc64le powerpc64le-linux-gnu)
add_target(sparc64 sparc64-linux-gnu)
add_target(s390x s390x-linux-gnu)
add_target(sh4 sh4-linux-gnu)
add_target(alpha alpha-linux-gnu)
if(${MACHINE} STREQUAL "x86_64" OR (HAS_qemu-x86_64 AND HAS_x86_64-linux-gnu-gcc))
add_target(x86_64 x86_64-linux-gnu)
endif()
option(MOLD_ENABLE_QEMU_TESTS_RV32 "Enable tests for RV32" OFF)
if(MOLD_ENABLE_QEMU_TESTS_RV32)
if(${MACHINE} STREQUAL "i686" OR (HAS_qemu-i386 AND HAS_i686-linux-gnu-gcc))
add_target(i686 i686-linux-gnu)
endif()
if(${MACHINE} STREQUAL "aarch64" OR (HAS_qemu-aarch64 AND HAS_aarch64-linux-gnu-gcc))
add_target(aarch64 aarch64-linux-gnu)
endif()
if(${MACHINE} STREQUAL "arm" OR (HAS_qemu-arm AND HAS_arm-linux-gnueabihf-gcc))
add_target(arm arm-linux-gnueabihf)
endif()
if(${MACHINE} STREQUAL "riscv64" OR (HAS_qemu-riscv64 AND HAS_riscv64-linux-gnu-gcc))
add_target(riscv64 riscv64-linux-gnu)
endif()
if(${MACHINE} STREQUAL "riscv32" OR (HAS_qemu-riscv32 AND HAS_riscv32-linux-gnu-gcc))
add_target(riscv32 riscv32-linux-gnu)
endif()
option(MOLD_ENABLE_QEMU_TESTS_POWER10 "Enable tests for Power10" OFF)
if(MOLD_ENABLE_QEMU_TESTS_POWER10)
add_target(ppc64le powerpc64le_power10-linux-gnu)
if(${MACHINE} STREQUAL "ppc" OR (HAS_qemu-ppc AND HAS_powerpc-linux-gnu-gcc))
add_target(ppc powerpc-linux-gnu)
endif()
option(MOLD_ENABLE_QEMU_TESTS_M68K "Enable tests for m68k" OFF)
if(MOLD_ENABLE_QEMU_TESTS_M68K)
if(${MACHINE} STREQUAL "ppc64" OR (HAS_qemu-ppc64 AND HAS_powerpc64-linux-gnu-gcc))
add_target(ppc64 powerpc64-linux-gnu)
endif()
if(${MACHINE} STREQUAL "ppc64le" OR (HAS_qemu-ppc64le AND HAS_powerpc64le-linux-gnu-gcc))
add_target(ppc64le powerpc64le-linux-gnu)
endif()
if(${MACHINE} STREQUAL "ppc64le" AND "${CPUINFO}" MATCHES "POWER10")
add_target(ppc64le powerpc64le-linux-gnu power10)
elseif(HAS_qemu-ppc64le AND HAS_powerpc64le-linux-gnu-gcc)
file(WRITE "${CMAKE_BINARY_DIR}/empty.c" "")
execute_process(
COMMAND powerpc64le-linux-gnu-gcc -mcpu=power10 -E
"${CMAKE_BINARY_DIR}/empty.c"
RESULT_VARIABLE GCC_EXIT_CODE
OUTPUT_QUIET ERROR_QUIET)
execute_process(
COMMAND qemu-ppc64le -cpu help
OUTPUT_VARIABLE QEMU_CPUS)
if("${GCC_EXIT_CODE}" EQUAL "0" AND "${QEMU_CPUS}" MATCHES power10_v2.0)
add_target(ppc64le powerpc64le-linux-gnu power10)
endif()
endif()
if(${MACHINE} STREQUAL "sparc64" OR (HAS_qemu-sparc64 AND HAS_sparc64-linux-gnu-gcc))
add_target(sparc64 sparc64-linux-gnu)
endif()
if(${MACHINE} STREQUAL "s390x" OR (HAS_qemu-s390x AND HAS_s390x-linux-gnu-gcc))
add_target(s390x s390x-linux-gnu)
endif()
if(${MACHINE} STREQUAL "sh4" OR (HAS_qemu-sh4 AND HAS_sh4-linux-gnu-gcc))
add_target(sh4 sh4-linux-gnu)
endif()
if(${MACHINE} STREQUAL "alpha" OR (HAS_qemu-alpha AND HAS_alpha-linux-gnu-gcc))
add_target(alpha alpha-linux-gnu)
endif()
if(${MACHINE} STREQUAL "m68k" OR (HAS_qemu-m68k AND HAS_m68k-linux-gnu-gcc))
add_target(m68k m68k-linux-gnu)
endif()
option(MOLD_ENABLE_QEMU_TESTS_LOONGARCH "Enable tests for LoongArch" OFF)
if(MOLD_ENABLE_QEMU_TESTS_LOONGARCH OR ${HOST} STREQUAL loongarch64)
if(${MACHINE} STREQUAL "loongarch64" OR (HAS_qemu-loongarch64 AND HAS_loongarch64-linux-gnu-gcc))
add_target(loongarch64 loongarch64-linux-gnu)
endif()
+1 -1
View File
@@ -3,7 +3,7 @@
# GCC produces buggy code for this test case on s390x.
# https://sourceware.org/bugzilla/show_bug.cgi?id=29655
[ $MACHINE = s390x ] && $CC -v 2>&1 | grep -E '^gcc version 1[0-3]\.' && skip
[ $MACHINE = s390x ] && $CC -v 2>&1 | grep -E '^gcc version 1[0-4]\.' && skip
cat <<EOF | $CC -c -fPIC -o$t/a.o -xc -
int foo = 4;
+1 -2
View File
@@ -7,8 +7,7 @@ $CC -B. -o $t/exe $t/a.c -Wl,-build-id
readelf -n $t/exe | grep -qv 'GNU.*0x00000010.*NT_GNU_BUILD_ID'
$CC -B. -o $t/exe $t/a.c -Wl,-build-id=uuid
readelf -nW $t/exe |
grep -Eq 'GNU.*0x00000010.*NT_GNU_BUILD_ID.*Build ID: ............4...[89abcdef]'
readelf -nW $t/exe | grep -Eq 'Build ID: ............4...[89abcdef]'
$CC -B. -o $t/exe $t/a.c -Wl,-build-id=md5
readelf -n $t/exe | grep -q 'GNU.*0x00000010.*NT_GNU_BUILD_ID'
+1 -1
View File
@@ -3,7 +3,7 @@
# GCC produces buggy code for this test case on s390x.
# https://sourceware.org/bugzilla/show_bug.cgi?id=29655
[ $MACHINE = s390x ] && $CC -v 2>&1 | grep -E '^gcc version 1[0-3]\.' && skip
[ $MACHINE = s390x ] && $CC -v 2>&1 | grep -E '^gcc version 1[0-4]\.' && skip
cat <<EOF | $CC -o $t/a.so -fPIC -shared -xc -
void *foo() {
+12 -16
View File
@@ -5,7 +5,7 @@ export LC_ALL=C
canonical_name() {
case $1 in
i?86) echo i386 ;;
i?86) echo i686 ;;
arm*) echo arm ;;
powerpc) echo ppc ;;
powerpc64) echo ppc64 ;;
@@ -14,14 +14,13 @@ canonical_name() {
esac
}
if [ -z "$HOST" ]; then
HOST=$(canonical_name $(uname -m))
if [ -z "$MACHINE" ]; then
MACHINE=$(canonical_name $(uname -m))
fi
# Set tool names
if [ -z "$TRIPLE" ]; then
MACHINE=$HOST
TESTDIR=out/test/elf/$HOST
TESTDIR=out/test/elf/$MACHINE
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
@@ -30,8 +29,7 @@ if [ -z "$TRIPLE" ]; then
OBJCOPY=objcopy
STRIP=strip
QEMU=
elif [ "$TRIPLE" = powerpc64le_power10-linux-gnu ]; then
TRIPLE=powerpc64le-linux-gnu
elif [ "$TRIPLE" = powerpc64le-linux-gnu -a "$CPU" = power10 ]; then
MACHINE=ppc64le
TESTDIR=out/test/elf/ppc64le-power10
CC="${TEST_CC:-$TRIPLE-gcc} -mcpu=power10"
@@ -52,22 +50,20 @@ else
OBJDUMP="$TRIPLE-objdump"
OBJCOPY="$TRIPLE-objcopy"
STRIP="$TRIPLE-strip"
QEMU="qemu-$MACHINE -L /usr/$TRIPLE"
if [ $MACHINE = i686 ]; then
QEMU="qemu-i386 -L /usr/$TRIPLE"
else
QEMU="qemu-$MACHINE -L /usr/$TRIPLE"
fi
fi
if [ $MACHINE = x86_64 -o $MACHINE = i386 -o $MACHINE = arm ]; then
if [ $MACHINE = x86_64 -o $MACHINE = i686 -o $MACHINE = arm ]; then
tlsdesc_opt=-mtls-dialect=gnu2
elif [ $MACHINE = aarch64 ]; then
tlsdesc_opt=-mtls-dialect=desc
fi
if [ $MACHINE = ppc64le -a -e /proc/cpuinfo ] && grep -q POWER10 /proc/cpuinfo; then
CC="$CC -mcpu=power10"
CXX="$CXX -mcpu=power10"
GCC="$GCC -mcpu=power10"
GXX="$GXX -mcpu=power10"
fi
# Common functions
test_cflags() {
echo 'int main() {}' | $CC "$@" -o /dev/null -xc - >& /dev/null
+1 -2
View File
@@ -1,7 +1,6 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc -fno-PIE -
#include <setjmp.h>
#include <signal.h>
@@ -32,7 +31,7 @@ int main() {
}
EOF
cat <<EOF | $CC -fPIC -shared -o $t/b.so -xc -
cat <<EOF | $CC -B. -fPIC -shared -o $t/b.so -xc -
__attribute__((section (".data.rel.ro"))) char readonly[100] = "abc";
char readwrite[100] = "abc";
EOF
+55
View File
@@ -0,0 +1,55 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc -
#include <stdio.h>
static void ctor1() { printf("ctor1 "); }
static void ctor2() { printf("ctor2 "); }
static void ctor3() { printf("ctor3 "); }
static void ctor4() { printf("ctor4 "); }
static void dtor1() { printf("dtor1 "); }
static void dtor2() { printf("dtor2 "); }
static void dtor3() { printf("dtor3 "); }
static void dtor4() { printf("dtor4 "); }
__attribute__((aligned(sizeof(void *)), section(".ctors.65435")))
void (*ctors65435[])() = { ctor1 };
__attribute__((aligned(sizeof(void *)), section(".ctors.65433")))
void (*ctors65433[])() = { ctor2 };
__attribute__((aligned(sizeof(void *)), section(".ctors")))
void (*ctors[])() = { ctor4, ctor3 };
__attribute__((aligned(sizeof(void *)), section(".dtors")))
void (*dtors[])() = { dtor1, dtor2 };
__attribute__((aligned(sizeof(void *)), section(".dtors.65433")))
void (*dtors65433[])() = { dtor3 };
__attribute__((aligned(sizeof(void *)), section(".dtors.65435")))
void (*dtors65435[])() = { dtor4 };
EOF
cat <<EOF | $CC -o $t/b.o -c -xc -
#include <stdio.h>
__attribute__((constructor(101)))
static void init1() { printf("init1 "); }
__attribute__((constructor))
static void init2() { printf("init2 "); }
__attribute__((destructor(101)))
static void fini1() { printf("fini1 "); }
__attribute__((destructor))
static void fini2() { printf("fini2 "); }
int main() {}
EOF
$CC -B. -o $t/exe $t/a.o $t/b.o
$QEMU $t/exe | grep -q 'ctor1 init1 ctor2 ctor3 ctor4 init2 fini2 dtor1 dtor2 dtor3 fini1 dtor4'
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc -
int main() {}
EOF
! $CC -B. -o $t/exe $t/a.o -Wl,-defsym=foo=bar 2> $t/log
grep -q 'undefined symbol: bar' $t/log
+19
View File
@@ -0,0 +1,19 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<'EOF' | $CC -c -o $t/a.o -xc -
void _ZN2ns7versionEv();
int main() { _ZN2ns7versionEv(); }
EOF
! $CC -B. -o $t/exe1 $t/a.o 2> $t/log || false
grep -Fq 'ns::version()' $t/log
cat <<'EOF' | $CC -c -o $t/b.o -xc -
void _ZN2ns7versionEv();
int main() { _ZN2ns7versionEv(); }
__attribute__((section(".comment"))) char str[] = "rustc version x.y.z\n";
EOF
! $CC -B. -o $t/exe2 $t/b.o 2> $t/log || false
grep -Fq 'ns::versionv' $t/log
+44
View File
@@ -0,0 +1,44 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc - -fPIC
#include <stdio.h>
void foo() { printf("foo1 "); }
void bar() { printf("bar1 "); }
void baz() { printf("baz1 "); }
void print() {
foo();
bar();
baz();
printf("\n");
}
EOF
cat <<EOF > $t/dyn
{ foo; bar; };
EOF
$CC -B. -shared -o $t/b.so $t/a.o -Wl,--dynamic-list=$t/dyn
cat <<EOF | $CC -o $t/c.o -c -xc - -fPIC
#include <stdio.h>
void foo() { printf("foo2 "); }
void bar() { printf("bar2 "); }
void baz() { printf("baz2 "); }
EOF
$CC -B. -shared -o $t/d.so $t/c.o
cat <<EOF | $CC -o $t/e.o -c -xc -
#include <stdio.h>
void print();
int main() { print(); }
EOF
$CC -B. -o $t/exe1 $t/e.o -Wl,-push-state,-no-as-needed $t/b.so -Wl,-pop-state
$QEMU $t/exe1 | grep -q 'foo1 bar1 baz1'
$CC -B. -o $t/exe2 $t/e.o -Wl,-push-state,-no-as-needed $t/d.so $t/b.so -Wl,-pop-state
$QEMU $t/exe2 | grep -q 'foo2 bar2 baz1'
+1
View File
@@ -1,6 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = m68k ] && skip
[ $MACHINE = sh4 ] && skip
cat <<EOF | $CXX -o $t/a.o -c -fPIC -xc++ -g -ffunction-sections -
+48
View File
@@ -0,0 +1,48 @@
#!/bin/bash
. $(dirname $0)/common.inc
nm mold | grep -q '__tsan_init' && skip
[ $MACHINE = m68k ] && skip
[ $MACHINE = sh4 ] && skip
cat <<EOF | $CXX -o $t/a.o -c -xc++ -
int foo() {
try {
throw 1;
} catch (int x) {
return x;
}
return 2;
}
EOF
cat <<EOF | $CXX -o $t/b.o -c -xc++ -
int bar() {
try {
throw 3;
} catch (int x) {
return x;
}
return 4;
}
EOF
$OBJCOPY --rename-section .eh_frame=.eh_frame2 $t/a.o
./mold -r -o $t/c.o $t/a.o $t/b.o
$OBJCOPY --rename-section .eh_frame2=.eh_frame $t/c.o
cat <<EOF | $CXX -o $t/d.o -c -xc++ -
#include <stdio.h>
int foo();
int bar();
int main() {
printf("%d %d\n", foo(), bar());
}
EOF
$CXX -B. -o $t/exe1 $t/d.o $t/c.o
$QEMU $t/exe1
$QEMU $t/exe1 | grep -q '^1 3$'
+1
View File
@@ -1,6 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = m68k ] && skip
[ $MACHINE = sh4 ] && skip
static=
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = $HOST ] || skip
[ "$QEMU" = '' ] || skip
[ $MACHINE = riscv64 -o $MACHINE = riscv32 -o $MACHINE = sparc64 ] && skip
command -v gdb >& /dev/null || skip
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = $HOST ] || skip
[ "$QEMU" = '' ] || skip
[ $MACHINE = riscv64 -o $MACHINE = riscv32 -o $MACHINE = sparc64 ] && skip
command -v gdb >& /dev/null || skip
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = $HOST ] || skip
[ "$QEMU" = '' ] || skip
[ $MACHINE = riscv64 -o $MACHINE = riscv32 -o $MACHINE = sparc64 ] && skip
command -v gdb >& /dev/null || skip
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = $HOST ] || skip
[ "$QEMU" = '' ] || skip
[ $MACHINE = riscv64 -o $MACHINE = riscv32 -o $MACHINE = sparc64 ] && skip
command -v gdb >& /dev/null || skip
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = $HOST ] || skip
[ "$QEMU" = '' ] || skip
[ $MACHINE = riscv64 -o $MACHINE = riscv32 -o $MACHINE = sparc64 ] && skip
command -v gdb >& /dev/null || skip
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = $HOST ] || skip
[ "$QEMU" = '' ] || skip
[ $MACHINE = riscv64 -o $MACHINE = riscv32 -o $MACHINE = sparc64 ] && skip
command -v gdb >& /dev/null || skip
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = $HOST ] || skip
[ "$QEMU" = '' ] || skip
[ $MACHINE = riscv64 -o $MACHINE = riscv32 -o $MACHINE = sparc64 ] && skip
command -v gdb >& /dev/null || skip
+1 -1
View File
@@ -20,7 +20,7 @@ GOT_ADDR=$($QEMU $t/exe)
# _GLOBAL_OFFSET_TABLE_ refers the end of .got only on x86.
# We assume .got is followed by .gotplt.
if [ $MACHINE = x86_64 -o $MACHINE = i386 ]; then
if [ $MACHINE = x86_64 -o $MACHINE = i686 ]; then
readelf -WS $t/exe | grep -q "\.got\.plt .*$GOT_ADDR "
else
readelf -WS $t/exe | grep -q "\.got .*$GOT_ADDR "
+1 -1
View File
@@ -18,5 +18,5 @@ int main() {
}
EOF
$CC -B. -o $t/exe $t/a.o $t/b.o
$CC -B. -o $t/exe $t/a.o $t/b.o -no-pie
$QEMU $t/exe | grep -q 'foo=5'
+1 -1
View File
@@ -16,5 +16,5 @@ int main() {
EOF
# Make sure that we do not copy .gnu.warning.* sections.
$CC -B. -o $t/exe $t/a.o
$CC -B. -o $t/exe $t/a.o -no-pie
$QEMU $t/exe | grep -q 'Hello world'
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = i386 ] || skip
[ $MACHINE = i686 ] || skip
cat <<'EOF' | $CC -fPIC -o $t/a.o -c -xassembler -
.globl get_foo
+48
View File
@@ -0,0 +1,48 @@
#!/bin/bash
. $(dirname $0)/common.inc
supports_tlsdesc || skip
cat <<'EOF' | $GCC -c -o $t/a.o -xassembler -
.globl get_foo
.type get_foo, @function
get_foo:
pushl %ebx
call __x86.get_pc_thunk.bx
addl $_GLOBAL_OFFSET_TABLE_, %ebx
subl $8, %esp
leal foo@TLSDESC(%ebx), %ebx
movl %ebx, %eax
call *foo@TLSCALL(%eax)
movl %gs:(%eax), %eax
addl $8, %esp
popl %ebx
ret
EOF
cat <<EOF | $GCC -fPIC -c -o $t/b.o -xc - $tlsdesc_opt
#include <stdio.h>
_Thread_local int foo;
int get_foo();
int main() {
foo = 42;
printf("%d\n", get_foo());
}
EOF
$CC -B. -o $t/exe1 $t/a.o $t/b.o
$QEMU $t/exe1 | grep -q 42
$CC -B. -o $t/exe2 $t/a.o $t/b.o -Wl,-no-relax
$QEMU $t/exe2 | grep -q 42
$CC -B. -shared -o $t/c.so $t/a.o
$CC -B. -o $t/exe3 $t/b.o $t/c.so
$QEMU $t/exe3 | grep -q 42
$CC -B. -shared -o $t/c.so $t/a.o -Wl,-no-relax
$CC -B. -o $t/exe4 $t/b.o $t/c.so -Wl,-no-relax
$QEMU $t/exe4 | grep -q 42
+2 -2
View File
@@ -29,5 +29,5 @@ int main() {
}
EOF
$CC -B. -o $t/exe1 $t/c.o $t/b.so -no-pie
$QEMU $t/exe1 | grep -Eq '^(\S+) \1'
$CC -B. -o $t/exe $t/c.o $t/b.so -no-pie
$QEMU $t/exe | grep -Eq '^(\S+) \1'
+5 -2
View File
@@ -16,5 +16,8 @@ int main() {
}
EOF
$CC -B. -o $t/exe $t/a.o
$QEMU $t/exe | grep -Eq '^(\S+) \1$'
$CC -B. -o $t/exe1 $t/a.o -pie
$QEMU $t/exe1 | grep -Eq '^(\S+) \1$'
$CC -B. -o $t/exe2 $t/a.o -no-pie
$QEMU $t/exe2 | grep -Eq '^(\S+) \1$'
+5 -2
View File
@@ -33,5 +33,8 @@ int main() {
}
EOF
$CC -B. -o $t/exe $t/a.o $t/b.o $t/c.o
$QEMU $t/exe | grep -q '^3$'
$CC -B. -o $t/exe1 $t/a.o $t/b.o $t/c.o -pie
$QEMU $t/exe1 | grep -q '^3$'
$CC -B. -o $t/exe2 $t/a.o $t/b.o $t/c.o -no-pie
$QEMU $t/exe2 | grep -q '^3$'
+5 -2
View File
@@ -24,5 +24,8 @@ int main() {
}
EOF
$CC -B. -o $t/exe $t/a.o
$QEMU $t/exe | grep -q 'Hello world'
$CC -B. -o $t/exe1 $t/a.o -pie
$QEMU $t/exe1 | grep -q 'Hello world'
$CC -B. -o $t/exe2 $t/a.o -no-pie
$QEMU $t/exe2 | grep -q 'Hello world'
+1
View File
@@ -1,6 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = m68k ] && skip
[ $MACHINE = sh4 ] && skip
cat <<EOF | $CXX -o $t/a.o -c -xc++ -
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = i386 ] && skip
[ $MACHINE = i686 ] && skip
cat <<EOF | $CC -o $t/a.o -c -xc - -ffunction-sections -fPIC
#include <stdio.h>
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = i386 ] && skip
[ $MACHINE = i686 ] && skip
cat <<EOF | $CC -o $t/a.o -c -xc - -ffunction-sections
#include <stdio.h>
+14
View File
@@ -0,0 +1,14 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc -
void _start() {}
EOF
$CC -B. -o $t/exe1 $t/a.o -nostdlib -Wl,-nmagic
$CC -B. -o $t/exe2 $t/a.o -nostdlib
end1=$(nm $t/exe1 | grep ' end$' | cut -d' ' -f1)
end2=$(nm $t/exe2 | grep ' end$' | cut -d' ' -f1)
[ $((0x$end1)) -lt $((0x$end2)) ]
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = i386 ] && skip
[ $MACHINE = i686 ] && skip
[ $MACHINE = m68k ] && skip
[ $MACHINE = arm ] && skip
[ $MACHINE = ppc64 ] && skip
+12
View File
@@ -0,0 +1,12 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc -
#include <stdio.h>
int main() {
printf("Hello world\n");
}
EOF
$CC -B. -o $t/exe $t/a.o
$OBJDUMP -d $t/exe | grep -q '<_savegpr0_14>'
+5 -1
View File
@@ -2,12 +2,16 @@
. $(dirname $0)/common.inc
# Skip if 32 bits as we use very large addresses in this test.
[ $MACHINE = i386 ] && skip
[ $MACHINE = i686 ] && skip
[ $MACHINE = riscv32 ] && skip
# It looks like SPARC's runtime can't handle PLT if it's too far from GOT.
[ $MACHINE = sparc64 ] && skip
# qemu aborts with the "Unknown exception 0x5" error, although this
# test passes on a real POWER10 machine.
[ -n "$QEMU" -a "$CPU" = power10 ] && skip
cat <<EOF > $t/a.c
#include <stdio.h>
+2 -1
View File
@@ -4,7 +4,8 @@
# OneTBB isn't tsan-clean
nm mold | grep -q '__tsan_init' && skip
# Ubuntu 22.04 SH4 GCC is broken
# Ubuntu 22.04 GCC is broken
[ $MACHINE = m68k ] && skip
[ $MACHINE = sh4 ] && skip
cat <<EOF | $CXX -c -o $t/a.o -xc++ -
+21
View File
@@ -0,0 +1,21 @@
#!/bin/bash
. $(dirname $0)/common.inc
# OneTBB isn't tsan-clean
nm mold | grep -q '__tsan_init' && skip
echo 'int main() {}' | $GCC -o /dev/null -xc -g3 -gz - >& /dev/null || skip
cat <<EOF | $GCC -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
void hello();
int main() { hello(); }
EOF
./mold --relocatable -o $t/c.o $t/a.o $t/b.o
$CC -B. -o $t/exe $t/c.o
$QEMU $t/exe | grep -q 'Hello world'
+1
View File
@@ -1,6 +1,7 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = m68k ] && skip
[ $MACHINE = sh4 ] && skip
# OneTBB isn't tsan-clean
+81
View File
@@ -0,0 +1,81 @@
#!/bin/bash
. $(dirname $0)/common.inc
[[ $MACHINE = riscv* ]] || skip
cat <<EOF | $CC -o $t/a.o -c -xassembler -
.globl get_sym1, get_sym2, get_sym3, get_sym4, get_sym5
.option norvc
get_sym1:
.reloc ., R_RISCV_GOT_HI20, sym1
.reloc ., R_RISCV_RELAX
auipc a0, 0
.reloc ., R_RISCV_PCREL_LO12_I, get_sym1
.reloc ., R_RISCV_RELAX
ld a0, 0(a0)
ret
get_sym2:
.reloc ., R_RISCV_GOT_HI20, sym2
.reloc ., R_RISCV_RELAX
auipc a0, 0
.reloc ., R_RISCV_PCREL_LO12_I, get_sym2
.reloc ., R_RISCV_RELAX
ld a0, 0(a0)
ret
get_sym3:
.reloc ., R_RISCV_GOT_HI20, sym3
.reloc ., R_RISCV_RELAX
auipc a0, 0
.reloc ., R_RISCV_PCREL_LO12_I, get_sym3
.reloc ., R_RISCV_RELAX
ld a0, 0(a0)
ret
get_sym4:
.reloc ., R_RISCV_GOT_HI20, sym4
.reloc ., R_RISCV_RELAX
auipc a0, 0
.reloc ., R_RISCV_PCREL_LO12_I, get_sym4
.reloc ., R_RISCV_RELAX
ld a0, 0(a0)
ret
get_sym5:
.reloc ., R_RISCV_GOT_HI20, sym5
.reloc ., R_RISCV_RELAX
auipc a0, 0
.reloc ., R_RISCV_PCREL_LO12_I, get_sym5
.reloc ., R_RISCV_RELAX
ld a0, 0(a0)
ret
EOF
cat <<EOF | $CC -o $t/b.o -c -xassembler -
.globl sym1, sym2, sym3, sym4, sym5
sym1 = 0x0
sym2 = 0xba
sym3 = 0xbeef
sym4 = 0x11beef
sym5 = 0xdeadbeef
EOF
cat <<EOF | $CC -o $t/c.o -c -xc -
#include <stdio.h>
int get_sym1();
int get_sym2();
int get_sym3();
int get_sym4();
int get_sym5();
int main() {
printf("%x %x %x %x %x\n",
get_sym1(), get_sym2(), get_sym3(), get_sym4(), get_sym5());
}
EOF
$CC -B. -o $t/exe1 $t/a.o $t/b.o $t/c.o -Wl,--no-relax
$QEMU $t/exe1 | grep -Eq '^0 ba beef 11beef deadbeef$'
$CC -B. -o $t/exe2 $t/a.o $t/b.o $t/c.o
$QEMU $t/exe2 | grep -Eq '^0 ba beef 11beef deadbeef$'
$OBJDUMP -d $t/exe2 | grep -A2 '<get_sym2>:' | grep -Eq $'li[ \t]+a0,186$'
+50
View File
@@ -0,0 +1,50 @@
#!/bin/bash
. $(dirname $0)/common.inc
# ARM assembler has a differnet grammar than the others.
# Concretely speaking, ARM as uses "@" as a start of a comment.
[ $MACHINE = arm ] && skip
cat <<'EOF' | $CC -c -o $t/a.o -x assembler -
.globl val1, val2, val3, val4, val5
.section .rodata.str1.1,"aMS",@progbits,1
val1:
.ascii "Hello \0"
.section .rodata.foo,"aMS",@progbits,4
.p2align 2
val2:
.ascii "world \0\0\0\0"
.section .rodata.x,"aMS",@progbits,1
val3:
.ascii "foobar\0"
.section .rodata.cst8,"aM",@progbits,8
.p2align 3
val4:
.ascii "abcdefgh"
.section .rodatabaz,"aMS",@progbits,1
val5:
.ascii "baz\0"
EOF
cat <<'EOF' | $CC -c -o $t/b.o -xc -
#include <stdio.h>
extern char val1, val2, val3, val4, val5;
int main() {
printf("%p %p %p %p %p\n", &val1, &val2, &val3, &val4, &val5);
}
EOF
$CC -B. -o $t/exe $t/a.o $t/b.o
readelf -p .rodata.str1.1 $t/exe | grep -q Hello
readelf -p .rodata.str4.4 $t/exe | grep -q world
readelf -p .rodata.str1.1 $t/exe | grep -q foobar
readelf -p .rodata.cst8 $t/exe | grep -q abcdefgh
readelf -p .rodatabaz $t/exe | grep -q baz
+26
View File
@@ -0,0 +1,26 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xassembler -
.section .foobar,"aw"
.ascii "foo\0"
EOF
cat <<EOF | $CC -o $t/b.o -c -xassembler -
.section .foobar,"a"
.ascii "bar\0"
EOF
cat <<EOF | $CC -o $t/c.o -c -xassembler -
.section .foobar,"axT"
.ascii "bar\0"
EOF
cat <<EOF | $CC -o $t/d.o -c -xc -
int main() {}
EOF
$CC -B. -o $t/exe $t/a.o $t/b.o $t/c.o $t/d.o
$CC -o $t/exe $t/a.o $t/b.o $t/c.o $t/d.o
readelf -WS $t/exe
# readelf -WS $t/exe | grep -q 'foobar.*WAX'
+6 -8
View File
@@ -15,15 +15,13 @@ int main() { printf("foo=%p\n", &foo); }
EOF
$CC -fPIC -c -o $t/d.o $t/c.c
$CC -B. -o $t/exe1 -pie $t/d.o $t/b.so
$QEMU $t/exe1 | grep -q 'foo=0x3'
nm -D $t/exe1 > $t/log1
! grep -q foo $t/log1 || false
# This test fails with older glibc
$CC -o $t/exe1 -pie $t/d.o $t/b.so 2> /dev/null || skip
$QEMU $t/exe1 | grep -q 'foo=0x3' || skip
$CC -fPIC -c -o $t/e.o $t/c.c
$CC -B. -o $t/exe2 -no-pie $t/e.o $t/b.so
$CC -B. -o $t/exe2 -pie $t/d.o $t/b.so
$QEMU $t/exe2 | grep -q 'foo=0x3'
nm -D $t/exe2 > $t/log2
! grep -q foo $t/log2 || false
$CC -B. -o $t/exe3 -no-pie $t/d.o $t/b.so
$QEMU $t/exe3 | grep -q 'foo=0x3'
+25
View File
@@ -0,0 +1,25 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc -
#include <stdio.h>
int main() {
printf("Hello world\n");
}
EOF
$CC -B. -o $t/exe1 $t/a.o
$QEMU $t/exe1 | grep -q 'Hello world'
[ "$(readelf -Wl $t/exe1 | grep NULL | wc -l)" -eq 0 ]
$CC -B. -o $t/exe2 $t/a.o -Wl,--spare-program-headers=0
$QEMU $t/exe2 | grep -q 'Hello world'
[ "$(readelf -Wl $t/exe2 | grep NULL | wc -l)" -eq 0 ]
$CC -B. -o $t/exe3 $t/a.o -Wl,--spare-program-headers=1
$QEMU $t/exe3 | grep -q 'Hello world'
[ "$(readelf -Wl $t/exe3 | grep NULL | wc -l)" -eq 1 ]
$CC -B. -o $t/exe4 $t/a.o -Wl,--spare-program-headers=5
$QEMU $t/exe4 | grep -q 'Hello world'
[ "$(readelf -Wl $t/exe4 | grep NULL | wc -l)" -eq 5 ]
+14
View File
@@ -0,0 +1,14 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc -
__thread char foo;
__attribute__((section(".data.rel.ro.bar"), aligned(16*1024)))
char bar;
int main() {}
EOF
$CC -B. -o $t/exe $t/a.o
$QEMU $t/exe
+1
View File
@@ -17,4 +17,5 @@ int main() {
EOF
$CC -B. -o $t/exe $t/a.o $t/b.o
readelf -WS $t/exe | grep -Fq .tls_common
$QEMU $t/exe | grep -q '^foo=0$'
+1 -1
View File
@@ -19,7 +19,7 @@ EOF
$CC -B. -shared -o $t/b.so $t/a.o
cat <<EOF | $GCC -c -o $t/c.o -xc -
cat <<EOF | $GCC -c -o $t/c.o -xc - -fPIC
#include <stdio.h>
_Thread_local int baz;
+1 -1
View File
@@ -23,5 +23,5 @@ cat <<EOF | $CC -xc -c -o $t/b.o -
__attribute__((tls_model("global-dynamic"))) _Thread_local int foo;
EOF
$CC -B. -o $t/exe $t/a.o $t/b.o
$CC -B. -o $t/exe $t/a.o $t/b.o -no-pie
$QEMU $t/exe | grep -q '3 5 3 5'
+30
View File
@@ -0,0 +1,30 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -c -o $t/a.o -xc -
void foo1() {}
void foo2() {}
void foo3() {}
__asm__(".symver foo1, foo@VER1");
__asm__(".symver foo2, foo@VER2");
__asm__(".symver foo3, foo@@VER3");
EOF
cat <<EOF > $t/b.version
VER1 { local: *; };
VER2 { local: *; };
VER3 { local: *; };
EOF
$CC -B. -o $t/c.so -shared $t/a.o -Wl,--version-script=$t/b.version \
-Wl,--trace-symbol='foo@VER1' > /dev/null
cat <<EOF | $CC -c -o $t/d.o -xc -
void foo();
__asm__(".symver foo, foo@VER1");
int main() { foo(); }
EOF
$CC -B. -o $t/exe $t/d.o $t/c.so -Wl,--trace-symbol='foo@VER1' > /dev/null
$QEMU $t/exe
+19
View File
@@ -0,0 +1,19 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<'EOF' > $t/a.ver
VER1 { foo*; };
VER2 { foo_x; };
EOF
cat <<EOF | $CC -fPIC -c -o $t/b.o -xc -
void foo_x() {}
void foo_y() {}
void foo_z() {}
EOF
$CC -B. -shared -Wl,--version-script=$t/a.ver -o $t/c.so $t/b.o
readelf -W --dyn-syms $t/c.so > $t/log
grep -Fq 'foo_x@@VER2' $t/log
grep -Fq 'foo_y@@VER1' $t/log
grep -Fq 'foo_z@@VER1' $t/log
+19
View File
@@ -0,0 +1,19 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<'EOF' > $t/a.ver
VER1 { foo_x; };
VER2 { foo*; };
EOF
cat <<EOF | $CC -fPIC -c -o $t/b.o -xc -
void foo_x() {}
void foo_y() {}
void foo_z() {}
EOF
$CC -B. -shared -Wl,--version-script=$t/a.ver -o $t/c.so $t/b.o
readelf -W --dyn-syms $t/c.so > $t/log
grep -Fq 'foo_x@@VER1' $t/log
grep -Fq 'foo_y@@VER2' $t/log
grep -Fq 'foo_z@@VER2' $t/log
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<'EOF' > $t/a.ver
VER1 { foo*; };
VER2 { foo*bar*; };
EOF
cat <<EOF | $CC -fPIC -c -o $t/b.o -xc -
void foo_bar() {}
EOF
$CC -B. -shared -Wl,--version-script=$t/a.ver -o $t/c.so $t/b.o
readelf -W --dyn-syms $t/c.so > $t/log
grep -Fq 'foo_bar@@VER2' $t/log
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<'EOF' > $t/a.ver
VER1 { foo\?; };
EOF
cat <<EOF | $CC -c -o $t/b.o -xassembler - >& /dev/null || skip
.globl "foo?"
"foo?":
EOF
$CC -B. -shared -Wl,--version-script=$t/a.ver -o $t/c.so $t/b.o
readelf -W --dyn-syms $t/c.so > $t/log
grep -Fq 'foo?@@VER1' $t/log
+2 -2
View File
@@ -9,10 +9,10 @@ EOF
cat <<EOF | $CXX -fPIC -c -o $t/b.o -xc -
int foo = 5;
int bar = 6;
int quux = 100;
EOF
$CC -B. -shared -Wl,--version-script=$t/a.ver \
-o $t/c.so $t/b.o
$CC -B. -shared -Wl,--version-script=$t/a.ver -o $t/c.so $t/b.o
cat <<'EOF' > $t/d.ver
VER_Y1 { local; *; };
+21
View File
@@ -0,0 +1,21 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
int foo() { return 0; }
EOF
$CC -B. -o $t/b.so $t/a.o -shared
cat <<EOF | $CC -fPIC -c -o $t/c.o -xc -
#include <stdio.h>
__attribute__((weak)) int foo();
int main() {
printf("%d\n", foo ? foo() : 3);
}
EOF
$CC -B. -o $t/d.so $t/c.o $t/b.so -shared
readelf -W --dyn-syms $t/d.so | grep -q 'WEAK DEFAULT UND foo'
Loaded 100 of 246 files, more files were not shown because too many files have changed in this diff. Show more