Compare commits

...
2421 Commits
Author SHA1 Message Date
Rui Ueyama 6ad942cb27 Bump mold version to 2.3.0 2023-10-18 19:24:17 +09:00
Rui Ueyama eed4496af2 Fix test
Older versions of GNU assembler don't know `.variant_pcs` directive.
2023-10-18 19:24:02 +09:00
Rui Ueyama 17c292c2b4 Fix CI
It looks like older versions of gdb falls into an infinite loop
if the gdb hash table is full.
2023-10-18 15:52:14 +09:00
Rui Ueyama ab1ab78f8a Fix gdb-index hash computation 2023-10-18 09:56:32 +09:00
Rui Ueyama 8506065fd2 Refactor 2023-10-17 20:19:07 +09:00
Rui Ueyama a291d0d448 Refactor 2023-10-17 18:41:10 +09:00
Rui Ueyama 690fefb97d Read .debug_gnu_{pubnames,pubtypes} correctly 2023-10-17 17:25:10 +09:00
Rui Ueyama c7c1779ad7 Fix CI 2023-10-17 17:04:53 +09:00
Rui Ueyama ec095e7513 Do not emit address areas for eliminated sections 2023-10-17 16:39:32 +09:00
Rui Ueyama 69a91e8aa0 --gdb-index: Fix address table 2023-10-17 15:53:55 +09:00
Rui Ueyama af46000645 Support DWARF64 for --gdb_index
Fixes https://github.com/rui314/mold/issues/1048
2023-10-17 15:19:35 +09:00
Rui Ueyama b1658c4702 Refactor 2023-10-17 15:19:35 +09:00
Rui Ueyama 9f85267b44 Optimize 2023-10-17 12:01:15 +09:00
Rui Ueyama f8d20cd3ce Fix CI 2023-10-17 10:40:21 +09:00
Rui Ueyama a396fa400f Re-implement --gdb-index
This change reduces the size of the .gdb_index section. It also fixes
an issue with handling of clang-generated object files.
2023-10-17 10:27:30 +09:00
Rui Ueyama d8a4f14fd0 Fix CI 2023-10-16 13:55:54 +09:00
Rui Ueyama c8b5fae2d0 Refactor 2023-10-16 13:15:00 +09:00
Rui Ueyama 0ce32d36d5 Allow <4GB input sections
Fixes https://github.com/rui314/mold/issues/1126
2023-10-16 12:40:52 +09:00
Rui Ueyama 01cd5d1a32 Revert "Do not write to an atomic variable too often"
This reverts commit 39a25e2394.
2023-10-13 15:08:27 +09:00
Rui Ueyama 39a25e2394 Do not write to an atomic variable too often 2023-10-13 14:28:56 +09:00
Rui Ueyama bfdd75ed38 Refactor 2023-10-13 14:24:35 +09:00
Rui Ueyama 5411f4216e Fix CI 2023-10-13 14:18:30 +09:00
Rui Ueyama 3cf00e30d5 Refactor 2023-10-13 13:14:01 +09:00
Rui Ueyama 0f714710f0 Fix relocations for POWER10
With this change, all tests now pass on an actual POWER10 machine
(gcc120.fsffrance.org).
2023-10-13 10:11:52 +09:00
Rui Ueyama 83ec409426 Support R_PPC64_TPREL34 2023-10-13 08:24:34 +09:00
Rui Ueyama e6ae83fbf9 Fix POWER10 issues 2023-10-12 21:04:27 +09:00
Rui Ueyama 00702773f4 Format 2023-10-12 12:04:08 +09:00
Rui Ueyama 1739cfbfb4 Refactor 2023-10-10 12:01:08 +09:00
Rui Ueyama ff689a1253 Allow nested response files
Fixes https://github.com/rui314/mold/issues/1124
2023-10-09 19:12:14 +09:00
Rui Ueyama ae427d133e Refactor 2023-10-07 16:50:00 +09:00
Rui Ueyama d92af2456d Refactor 2023-10-07 16:39:46 +09:00
Rui Ueyama b3e1092879 Refactor 2023-10-07 14:01:24 +09:00
Rui Ueyama 46ef71eb75 Fix CI 2023-10-05 20:00:27 +09:00
Rui Ueyama 871ef8cf14 Refactor 2023-10-05 19:06:33 +09:00
Rui Ueyama c1b1caa854 Fix address-taken bit 2023-10-05 14:10:38 +09:00
Rui Ueyama dcab76697e Refactor 2023-10-05 11:47:00 +09:00
Rui Ueyama cf11efb147 Revert "Fix TLSDESC on musl-libc ARM32 systems"
This reverts commit 967006479f because
it made the linker to crash. TLSDESC doesn't seem to work even with
GNU ld on ARM32 musl systems, so I'll disable the test.

Fixes https://github.com/rui314/mold/issues/1116
2023-10-05 11:45:15 +09:00
Rui Ueyama a688b73f8e Merge pull request #1123 from ishitatsuyuki/main-mono-reland
[ELF] Reland "Move main() and redo_main() into a separate compile unit."
2023-10-04 19:48:55 +09:00
Tatsuyuki Ishi 9bb50ae7a5 [ELF] Move main() and redo_main() into a separate compile unit.
Despite the extern template items Clang 14 seems to insist on instantiating
elf_main(), with the hilarious consequence of the function being duplicated
quadratically to the number of archs.

Move this to a different CU so that the compiler cannot possibly
instantiate it more than once. This significantly speeds up compilation
related to main.cc.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2023-10-04 19:09:26 +09:00
Rui Ueyama ead6235470 Emit an endbr64 only for a canonical PLT 2023-10-04 17:17:07 +09:00
Rui Ueyama 967006479f Fix TLSDESC on musl-libc ARM32 systems
Fixes https://github.com/rui314/mold/issues/1116
2023-10-04 14:54:55 +09:00
Rui Ueyama f843daee9b Revert "[ELF] Move main() and redo_main() into a separate compile unit."
This reverts commit 1d3706285e.

Fixes https://github.com/rui314/mold/issues/1122
2023-10-04 13:07:06 +09:00
Rui Ueyama c7129916ee Merge pull request #1121 from ishitatsuyuki/main-mono
[ELF] Move main() and redo_main() into a separate compile unit.
2023-10-03 19:01:43 +09:00
Rui Ueyama e234ce25b6 Fix CI 2023-10-03 18:58:01 +09:00
Rui Ueyama 02e1b9de10 Remove a useless variable 2023-10-03 18:53:35 +09:00
Rui Ueyama effde5ffa9 Merge pull request #1119 from sicherha/improvement_strict_compiler_warnings
Enable strict compiler warnings by default
2023-10-03 18:50:52 +09:00
Tatsuyuki Ishi 1d3706285e [ELF] Move main() and redo_main() into a separate compile unit.
Despite the extern template items Clang 14 seems to insist on instantiating
elf_main(), with the hilarious consequence of the function being duplicated
quadratically to the number of archs.

Move this to a different CU so that the compiler cannot possibly
instantiate it more than once. This significantly speeds up compilation
related to main.cc.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2023-10-03 17:48:57 +09:00
Rui Ueyama 085a1148fd Report a duplicate symbol error for archives with --whole-archive
Fixes https://github.com/rui314/mold/issues/1120
2023-10-03 17:36:45 +09:00
Christoph Erhardt 5cf3e53ab9 Enable strict compiler warnings
Compiler warnings have the potential to uncover real bugs before they
make it into a commit. For example, the accidental fallthrough
introduced by commit 713d81947a and fixed
by commit 49f38828d1 could have been
detected by GCC.

Remove `-Wunused-variable` since it is implied by `-Wall`.
Add `-Wno-unused-parameter` to suppress warning spam regarding unused
parameters.

Signed-off-by: Christoph Erhardt <[email protected]>
2023-10-03 08:50:44 +02:00
Christoph Erhardt a5aa387cb4 Add cast to ternary-operator expression
This fixes the following compiler warning:
> enumerated and non-enumerated type in conditional expression [-Wextra]

Signed-off-by: Christoph Erhardt <[email protected]>
2023-10-03 08:50:44 +02:00
Christoph Erhardt f99cb33c2d Remove unused parameters
Signed-off-by: Christoph Erhardt <[email protected]>
2023-10-03 08:50:44 +02:00
Rui Ueyama e55703405b Do not call compute_address_significance twice 2023-10-03 14:30:45 +09:00
Rui Ueyama 4c71174d5d Fix CI 2023-10-02 20:03:24 +09:00
Rui Ueyama 2ea08ac4b3 Revert "Enable to download build artifacts"
This reverts commit 82ace062db.
2023-10-02 19:58:37 +09:00
Rui Ueyama 82ace062db Enable to download build artifacts 2023-10-02 19:11:55 +09:00
Rui Ueyama 17f0d85efb Add an experimental -z rewrite-endbr option
endbr64 is a relatively new x86 instruction to mark a location to which an
indirect jump instruction can transfer control. If the control-flow
integrity is turned on (i.e., endbr64 is effective), an indirect jump can
only target an endbr64 or raise a runtime exception. This mechanism makes
some types of control hijacking attacks, such as ROP or JOP, very hard
because an attacker can no longer jump to an arbitrary location.

If `-fcf-protection` is given, GCC emits an endbr64 at the beginning of
each global function conservatively because the address of that function
might be used by other translation units.

In reality, most function addresses aren't taken, so the conservative
assumption ends up emitting lots of unnecessary endbr64 instructions.
This not only bloats the code but also weakens security because an
attacker would have more locations to attack.

The new linker option, `-z rewrite-endbr`, is designed to mitigate this
situation. The linker is in a position to conduct whole-program analysis
on the input files and determine functions whose addresses have never been
taken. If `-z rewrite-endbr` is given, mold performs that analysis and
rewrites the leading endbr64 with a nop if the function's address wasn't
taken.

For evaluation, I built the mold executable itself both with and without
this option. Before using the `-z rewrite-endbr` option, the mold
executable contained 25,994 endbr64 instructions. With the option, the
number becomes 18,035, a 30% reduction.

The new option is exprerimental; we may remove it in a future versions of
the mold linker.
2023-10-02 17:53:03 +09:00
Rui Ueyama a994d71fa1 Use relocations to identify address-taken functions 2023-10-02 17:05:38 +09:00
Rui Ueyama 0eea8f4015 Refactor 2023-09-30 12:04:20 +09:00
Rui Ueyama 2e3b56ee3f Emit DT_AARCH64_VARIANT_PCS if required
Fixes https://github.com/rui314/mold/issues/1115
2023-09-28 20:57:54 +09:00
Rui Ueyama b8bcb8a2a3 Refactor 2023-09-27 18:17:24 +09:00
Rui Ueyama ac3ee91cbd [RISC-V] Support GP-relative relocations
These relocations are use for embedded programming and are probably
not useful for mold, but we want them for the sake of completeness.
2023-09-26 11:30:02 +09:00
Rui Ueyama 18df821625 Install a required package 2023-09-24 17:26:11 +09:00
Rui Ueyama 79b3fc4b6a Bump mold version to 2.2.0 2023-09-24 12:28:19 +09:00
Rui Ueyama 9e09e74db3 Refactor 2023-09-20 16:58:37 +09:00
Rui Ueyama 9ee10ba4bd Fix test
Fixes https://github.com/rui314/mold/issues/1107
2023-09-18 15:03:41 +09:00
Rui Ueyama fe7c8c26b7 Fix for big-endian hosts 2023-09-17 15:28:35 +09:00
Rui Ueyama 23576b6e48 Refactor 2023-09-17 11:16:39 +09:00
Rui Ueyama cfa9e404d1 Keep the entry symbol when doing LTO
Fixes https://github.com/rui314/mold/issues/1105
2023-09-16 14:43:04 +09:00
Rui Ueyama 641360b0aa Update comments 2023-09-16 14:28:39 +09:00
Rui Ueyama 4ccc9eb89a Fix RISC-V's shrink_section()
r_delta is not available when we are shrinking sections.

Address the post review comment
https://github.com/rui314/mold/commit/742cdeec666ea015c26af4f30d01f2fb08fcb7b7#r127431639
2023-09-16 14:01:57 +09:00
Rui Ueyama a801dde186 Use ld for RV64 and lw for RV32 2023-09-14 14:00:55 +09:00
Rui Ueyama e2d53f26e2 Refactor 2023-09-14 14:00:54 +09:00
Rui Ueyama 742cdeec66 Refactor 2023-09-14 14:00:53 +09:00
Rui Ueyama c2116aa7ca Merge pull request #1041 from ishitatsuyuki/rv-tlsdesc
RISC-V TLSDESC support
2023-09-14 12:54:11 +09:00
Tatsuyuki Ishi fed950837a [ELF][RISCV] Add TLSDESC relaxation.
Signed-off-by: Tatsuyuki Ishi <[email protected]>
2023-09-14 02:18:15 +09:00
Tatsuyuki Ishi 508890d33d [ELF][RISCV] Fix finding paired relocations with zero distance.
For example, when the leader's instruction is removed and a follower is right
next to it, the follower's relocation can point to the follower's address
itself. In this case the leader's relocation may lie before or after the
follower, so just search for both.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2023-09-13 13:38:46 +09:00
Tatsuyuki Ishi 141556db44 [ELF] Support RISC-V TLSDESC relocations.
Signed-off-by: Tatsuyuki Ishi <[email protected]>
2023-09-13 13:38:42 +09:00
Tatsuyuki Ishi 8589403fe0 [ELF][RISCV] Move the list of "leader" paired reloc into a helper.
Signed-off-by: Tatsuyuki Ishi <[email protected]>
2023-09-13 13:37:15 +09:00
Tatsuyuki Ishi cd2224742f [ELF] Support RISC-V TLSDESC GOT generation.
Signed-off-by: Tatsuyuki Ishi <[email protected]>
2023-09-13 13:36:55 +09:00
Tatsuyuki Ishi 0b1d11ed01 [ELF] Add RISC-V TLSDESC relocation constants.
Signed-off-by: Tatsuyuki Ishi <[email protected]>
2023-09-13 13:36:16 +09:00
Rui Ueyama 22443b1490 Reduce the number of atomic memory operations 2023-09-08 18:48:25 +09:00
Rui Ueyama a3a53e9a0c Refactor 2023-09-08 17:44:24 +09:00
Rui Ueyama dd3d045396 Discard local symbols in discarded sections 2023-09-06 17:16:22 +09:00
Rui Ueyama 673d790372 Attempt to fix CI 2023-09-06 14:48:10 +09:00
Rui Ueyama 6cac0aba6a Fix CI 2023-09-06 13:59:48 +09:00
Rui Ueyama 1a13c5076e Avoid cache-line false sharing
This change improves the performance of our concurrent hash map on
a multicore machine.
2023-09-06 13:26:49 +09:00
Rui Ueyama c504911bf6 Add a missing #include 2023-09-05 15:39:44 +09:00
Rui Ueyama db5fa8a8cd Remove incomplete MIPS support
I made a fair amount of effort to try to support MIPS, but it turned out
that it is much harder than expected. I punted it instead of making
further efforts.

The problem is the MIPS ABI is hostile to the linker in the modern
environment. MIPS object files are still compiled for the small code
model in which GOT entries and data in the small data area are expected
to be accessible with a single machine instruction with a 16-bit
displacement. In other words, .got/.sdata/.sbss are expected to be
smaller than 64 KiB. This might have been a reasonable assumption in the
90s, but it's not suitable for modern applications that can be 1000x
larger than the binaries in the 90s.

MIPS requires the linker to implement tons of workarounds for its legacy
ABI assumptions. Our incomplete MIPS support can build binaries that
pass all our unit tests. However, it needed more effort to support
real-world programs that are larger than our test cases.

At this point, I don't think it is productive to implement workarounds
for the old ABI that is stuck in the 90s. It is honestly annoying to
think about workarounds for the code that is intentionally compiled to
be hostile to the linker. The situation is unfortunate, but if the
Open Source community is still serious about MIPS, they should improve
the ABI and the compiler instead of asking us to implement the legacy
ABI.

Closes https://github.com/rui314/mold/issues/1040
2023-09-04 13:12:03 +09:00
Rui Ueyama c03400303f Fix --relocatable
Fixes https://github.com/rui314/mold/issues/1096
2023-09-03 20:15:32 +09:00
Rui Ueyama 7f91a134ef Refactor 2023-09-03 12:31:22 +09:00
Rui Ueyama d4891d5ff1 Improve PC-relative relocation relaxation
Absolute symbols are rare, but their PC-relative addresses are known
at link-time if we are creating a position-dependent executable.
2023-09-03 10:50:52 +09:00
Rui Ueyama bdc2336265 Refactor 2023-09-03 10:50:52 +09:00
Rui Ueyama 2a0f65084e Refactor 2023-09-01 17:24:32 +09:00
Rui Ueyama db146cfc37 Fix i386 TLSDESC 2023-09-01 15:30:38 +09:00
Rui Ueyama 49f38828d1 Add a missing break
This error resulted in creating an unnecessary GOT entry.
2023-09-01 13:58:09 +09:00
Rui Ueyama 0c972cf59d Fix a minor bug in R_X86_64_GOT32 handling 2023-09-01 13:54:39 +09:00
Rui Ueyama f057fdadba Fix TLSGD/TLSDDESC to Initial Exec relaxation 2023-09-01 12:39:10 +09:00
Rui Ueyama 3fb2571255 Refactor 2023-09-01 11:13:01 +09:00
Rui Ueyama 619f741ca9 Fix linker script "local" and "global" label handling
Fixes https://github.com/rui314/mold/issues/1098
2023-09-01 10:10:51 +09:00
Rui Ueyama 713d81947a Refactor 2023-09-01 09:57:20 +09:00
Rui Ueyama f76d3e0d0c Fix a unit test
Recent versions of GCC report a compile error if an extension
name is not recognizable.

Fixes https://github.com/rui314/mold/issues/1097
2023-08-31 18:16:32 +09:00
Rui Ueyama 488cd5d933 Add comments 2023-08-29 17:23:21 +09:00
Rui Ueyama 55bcb72be7 Fix a minor relaxation error 2023-08-29 16:12:57 +09:00
Rui Ueyama 7f8872b2f1 Do not update auto-generated files' timestamp on cmake
Fixes https://github.com/rui314/mold/issues/1094
2023-08-28 12:36:44 +09:00
Rui Ueyama 139807080b Refactor 2023-08-27 13:35:15 +09:00
Rui Ueyama 86b37b112f Add comments 2023-08-27 11:51:43 +09:00
Rui Ueyama d56f528c27 Relax TLSDESC to Initial Exec 2023-08-26 20:47:02 +09:00
Rui Ueyama a1b81b249f Add a rule for Asahi Linux 2023-08-26 14:37:48 +09:00
Rui Ueyama fa0de2f03b Make the PLT header a little bit compact 2023-08-25 11:26:43 +09:00
Rui Ueyama a357445654 Refactor 2023-08-24 19:08:10 +09:00
Rui Ueyama 8f5c49c655 Play the code-golf with our s390x PLT header 2023-08-23 22:41:41 +09:00
Rui Ueyama 03b5ce7c6f Reduce the size of PLT entry on s390 2023-08-23 21:45:58 +09:00
Rui Ueyama 63df2a3c8b Add a script to install cross toolchains 2023-08-23 19:45:28 +09:00
Rui Ueyama 9a09ead865 Reduce the size of range extension thunk for ARM 2023-08-23 16:40:48 +09:00
Rui Ueyama c2380ff7bc Do not run the compiler in sudo 2023-08-23 14:38:50 +09:00
Rui Ueyama c49a8b5b07 Merge pull request #1092 from david-perez/main
Update `README.md` installation instructions for CMake 3.14
2023-08-23 14:37:07 +09:00
Rui Ueyama 51fec5f1ff Avoid reading data from .plt 2023-08-23 14:02:03 +09:00
Rui Ueyama ac20d8729b Avoid reading data from .plt
With `--execute-only`, the text segment may not be readable.

Fixes https://github.com/rui314/mold/issues/1087
2023-08-23 12:53:27 +09:00
Rui Ueyama 7e9fedd203 Align range extension thunk entry points to 16 byte boundaries
Quote from Arm Cortex-A75 Software Optimization Guide
(PJDOC-466751330-205) p.59:

  Consider aligning subroutine entry points and branch targets to
  16-byte boundaries, within the bounds of the code-density requirements
  of the program. This ensures that the subsequent fetch can maximize
  bandwidth following the taken branch by bringing in all useful
  instructions.
2023-08-22 16:20:12 +09:00
Rui Ueyama 576cd86ecb Refactor 2023-08-22 15:47:34 +09:00
david-perez bb1db6cf17 Update README.md installation instructions for CMake 3.14
`CMakeLists.txt` declares a minimum CMake dependency on 3.14.

```
cmake_minimum_required(VERSION 3.13)
```

But the `--install` switch is only available since CMake 3.15, see
<https://stackoverflow.com/a/56457739>.

This commit updates the installation instructions in `README.md` so that
they work with CMake 3.14.
2023-08-21 18:52:35 +02:00
Rui Ueyama 3529a2fcc6 Refactor 2023-08-21 12:30:18 +09:00
Rui Ueyama e842bee049 Fix R_RISCV_HI20 relaxation 2023-08-20 17:30:44 +09:00
Rui Ueyama 3c8684c528 Fix R_RISCV_HI20 2023-08-20 17:03:38 +09:00
Rui Ueyama 108815dbe1 Implement LUI -> C.LUI relaxation 2023-08-20 14:29:02 +09:00
Rui Ueyama d75386b545 Bump the required CMake version to 3.14
Because CREATE_LINK requires CMake 3.14.

Fixes https://github.com/rui314/mold/issues/1091
2023-08-20 08:33:41 +09:00
Rui Ueyama bb1934bb9f Refactor 2023-08-19 17:34:38 +09:00
Rui Ueyama f01817f39e Statically link more libraies if -DMOLD_MOSTLY_STATIC=1 2023-08-19 14:08:26 +09:00
Rui Ueyama fad4c1b56c Disable tests in the bundled zlib 2023-08-19 13:55:03 +09:00
Rui Ueyama 3c8868280d Remove unnecessary build dependencies 2023-08-19 13:38:11 +09:00
Rui Ueyama a665ca766d Upgrade bundled zlib to v1.3 2023-08-19 13:30:22 +09:00
Rui Ueyama 0f85278c73 Upgrade bundled zstd to v1.5.5 2023-08-19 13:29:24 +09:00
Rui Ueyama 085727234a Refactor 2023-08-19 13:21:32 +09:00
Rui Ueyama 83fe073e97 Refactor 2023-08-18 17:14:04 +09:00
Rui Ueyama 8a25c1bfc1 Refactor 2023-08-17 18:50:15 +09:00
Rui Ueyama a94cd68ce1 Refactor 2023-08-17 17:45:48 +09:00
Rui Ueyama 68ac03d19c Refactor 2023-08-17 14:48:57 +09:00
Rui Ueyama b79bc35fd9 Fix CI 2023-08-17 12:40:02 +09:00
Rui Ueyama 90839d747f Refactor 2023-08-17 12:18:55 +09:00
Rui Ueyama 1b1ab744e5 Copy RELRO symbols to .copyrel.rel.ro instead of to .copyrel
So that readonly data wouldn't become writable by copy relocations.
2023-08-17 11:55:05 +09:00
Rui Ueyama f02db0fa0f Do not link libraries that are not directly used by the output file
If an as-needed DSO is referenced only by other DSOs and not by
the output file we are creating, we won't mark it as "needed".

Fixes https://github.com/rui314/mold/issues/1036
2023-08-17 11:03:12 +09:00
Rui Ueyama fbcee0b716 Refactor 2023-08-17 10:16:19 +09:00
Rui Ueyama c105cfd35a Fix CI 2023-08-16 23:13:07 +09:00
Rui Ueyama bb402480a5 Refactor 2023-08-16 22:57:16 +09:00
Rui Ueyama c983321fed Upgrade the bundled OneTBB to v2021.10.0 2023-08-16 21:06:41 +09:00
Rui Ueyama 43ebc96bfe Refactor 2023-08-16 20:46:25 +09:00
Rui Ueyama 8ae1ad57a4 Merge pull request #1090 from Romain-Geissler-1A/remove-openssl-mentions
Removing remaining openssl mentions following the move to Blake3.
2023-08-16 20:45:00 +09:00
Romain Geissler bd39bfdc5b Removing remaining openssl mentions following the move to Blake3. 2023-08-16 10:19:38 +00:00
Rui Ueyama a11566795d Update comments 2023-08-16 16:36:26 +09:00
Rui Ueyama 3676c596df Remove CI badge as well
We can see the CI status on the same GitHub homepage.
2023-08-16 14:27:21 +09:00
Rui Ueyama 0947836a44 Refactor 2023-08-16 14:27:09 +09:00
Rui Ueyama 2383e42a5c Merge pull request #1089 from marxin/drop-obs-badge
drop openSUSE OBS badge as the package is unmaintained
2023-08-16 14:26:58 +09:00
Martin Liska 049dbb73fe drop openSUSE OBS badge as the package is unmaintained 2023-08-16 07:19:27 +02:00
Rui Ueyama 95894ef2e2 Attempt to fix Windows CI 2023-08-16 13:36:30 +09:00
Rui Ueyama 7f7a744ebb Use BLAKE3 instead of SHA256 for cryptographic hash computation
Closes https://github.com/rui314/mold/issues/1088
2023-08-16 12:18:10 +09:00
Rui Ueyama 3833a4a457 Add BLAKE3 to third-party/
Since BLAKE3 is available under CC0, we don't need to add its license
terms (or the lack thereof) to our LICENSE.third-party file.
2023-08-16 12:13:53 +09:00
Rui Ueyama a0516a371f Refactor 2023-08-16 10:27:21 +09:00
Rui Ueyama 29491df6f2 Refactor 2023-08-15 18:19:39 +09:00
Rui Ueyama 599ed1dde0 Refactor 2023-08-15 16:56:59 +09:00
Rui Ueyama 143111ae5a Fix CI 2023-08-15 16:45:09 +09:00
Rui Ueyama d753ea5bdb Fix CI 2023-08-15 16:10:35 +09:00
Rui Ueyama b205eb7fa6 Refactor 2023-08-15 15:58:09 +09:00
Rui Ueyama d8baee9605 Refactor 2023-08-15 15:33:28 +09:00
Rui Ueyama 6dacc5192a Refactor 2023-08-15 15:08:34 +09:00
Rui Ueyama 4f5f88258c Fix TLSDESC relocation
Although we do not support TLSDESC on any target on which
ctx.tls_begin != ctx.dtp_addr, my understanding is that the origin
of the TLSDESC base relocation is tls_begin.
2023-08-14 19:55:39 +09:00
Rui Ueyama f5abedad01 Fix _TLS_MODULE_BASE_ address
Since the module base address obtained with `_TLS_MODULE_BASE_` will
be used with foo@dtpoff relocations, it should be the same as the DTP
address.
2023-08-14 19:39:50 +09:00
Rui Ueyama 02aee3b43e Do not put local symbols into .dynsym for TLSDESC
Placing local symbols into .dynsym is not technically incorrect, but
doing so is just a waste of space.
2023-08-14 19:06:35 +09:00
Rui Ueyama c1406860fa Fix -Wunused-variable
Fixes https://github.com/rui314/mold/issues/1086
2023-08-14 11:39:44 +09:00
Rui Ueyama 53759731cf Fix relocation overflow check 2023-08-14 08:39:13 +09:00
Rui Ueyama b018baec8a Refactor 2023-08-13 19:11:38 +09:00
Rui Ueyama 28ddea5371 Revert "Do not create copy relocations for PIE"
This reverts commit 82989a8b7c because
it broke our CI.
2023-08-13 17:55:44 +09:00
Rui Ueyama 82989a8b7c Do not create copy relocations for PIE
Object files have to be compiled with `-fPIE` or `-fPIC` for a
position-independent executable, and such object files shouldn't
contain relocations that directly refers to data objects without
GOT.
2023-08-13 17:02:45 +09:00
Rui Ueyama d78b748696 Bump mold version to 2.1.0 2023-08-13 13:35:43 +09:00
Rui Ueyama 77968a7fc1 Make --pack-dyn-relocs=relr as a synonym for -z pack-relative-relocs 2023-08-13 10:47:50 +09:00
Rui Ueyama 00a68c5fcf Format 2023-08-12 17:47:51 +09:00
Rui Ueyama 6f9752a1bf Remove a useless timer 2023-08-12 16:58:51 +09:00
Rui Ueyama 6a32828dd8 Improve error messages 2023-08-12 14:36:42 +09:00
Rui Ueyama b5e0d7a7c9 Refactor 2023-08-12 13:41:46 +09:00
Rui Ueyama 3b0b28ff97 Update comments 2023-08-12 13:19:11 +09:00
Rui Ueyama 6ce3730083 Use the same error message for s390x 2023-08-11 17:07:10 +09:00
Rui Ueyama d3e7d88753 Remove error checks
We do not have these error checks for other targets.
We want to be consistent across targets.
2023-08-11 16:55:44 +09:00
Rui Ueyama 9dee80eb4f Simplify 2023-08-11 16:55:16 +09:00
Rui Ueyama eb99c87f75 Use PCALAU12I instead of PCADDU12I in PLT
So that the PLT code is more in line with compiler-generated code.
2023-08-11 16:32:55 +09:00
Rui Ueyama e45f2a8efb Fix CMakeLists.txt 2023-08-11 15:07:09 +09:00
Rui Ueyama 86ab3890d6 Upgrade RV32 toolchain in CI 2023-08-11 15:05:51 +09:00
Rui Ueyama 3b5ccb9342 Implement range extension thunks for LoongArch 2023-08-11 10:54:19 +09:00
Rui Ueyama 9441170d39 Refactor 2023-08-11 01:30:43 +09:00
Rui Ueyama ffb83247f4 Fix CI 2023-08-10 17:59:40 +09:00
Rui Ueyama 13f42071b9 Update comments 2023-08-10 17:49:47 +09:00
Rui Ueyama 032931fa3b Enable LoongArch tests on CI 2023-08-10 16:41:08 +09:00
Rui Ueyama 472b325349 Improve Loongarch support 2023-08-10 15:42:54 +09:00
Rui Ueyama b852d925c2 Revert "Fix TLSLD"
This reverts commit 56cdba50cf.
2023-08-10 13:48:12 +09:00
Rui Ueyama 56cdba50cf Fix TLSLD 2023-08-10 13:25:36 +09:00
Rui Ueyama cfec68e054 Refactor 2023-08-10 11:27:11 +09:00
Rui Ueyama d9f850b032 Fix instruction bitmask 2023-08-10 10:36:06 +09:00
Rui Ueyama 1f012337c0 Refactor 2023-08-10 10:09:49 +09:00
Rui Ueyama 90cf642ca7 Fix high 32 bits computation
I believe the correct expression is this one.
2023-08-10 09:56:06 +09:00
Rui Ueyama d12c48ce63 Refactor 2023-08-10 09:56:06 +09:00
Rui Ueyama 67b9d6cb8d Remove unnecessary function 2023-08-10 08:45:24 +09:00
Rui Ueyama 2e12e635a0 Refactor 2023-08-10 08:30:07 +09:00
Rui Ueyama 1b5631990c Simplify 2023-08-10 08:26:24 +09:00
Rui Ueyama acf846acc0 Assume little endian on LooongArch 2023-08-09 21:30:55 +09:00
Rui Ueyama e1347d8f7d Refactor 2023-08-09 21:29:53 +09:00
Rui Ueyama 6d809b56e6 Refactor 2023-08-09 21:13:00 +09:00
Rui Ueyama 4528507155 Handle obsolete relocations as unknown ones
A "unknown relocation" message should be enough to show that the
relocation is not supported by mold.
2023-08-09 20:57:45 +09:00
Rui Ueyama da07871416 Move LoongArch tests behind MOLD_ENABLE_QEMU_TESTS_LOONGARCH 2023-08-09 20:54:29 +09:00
Rui Ueyama 354d0254ee Update comments 2023-08-09 20:53:39 +09:00
Rui Ueyama b711bf2009 Merge pull request #1081 from MQ-mengqing/main
Add loongarch basic support
2023-08-09 20:53:22 +09:00
Rui Ueyama 94e2a71866 Refactor 2023-08-09 17:16:11 +09:00
Rui Ueyama f467ad1add Create a symbol version dependency to GLIBC_ABI_DT_RELR
Fixes https://github.com/rui314/mold/issues/653
2023-08-09 12:02:06 +09:00
Rui Ueyama f631477a68 Set e_flags and .MIPS.abiflags 2023-08-08 19:20:59 +09:00
Rui Ueyama 56479e724f Improve an error message 2023-08-08 18:14:26 +09:00
Rui Ueyama d752bac24a Do not handle --strip-debug as a synonym for --strip-all
Fixes https://github.com/rui314/mold/issues/1080
2023-08-08 17:46:45 +09:00
Rui Ueyama a50258fdf8 Refactor 2023-08-08 11:37:36 +09:00
Rui Ueyama 710dfd01d6 Fix ODR violation
Fixes https://github.com/rui314/mold/issues/1082
2023-08-07 19:15:31 +09:00
Jinyang He 03b1a1cc68 Add loongarch basic support
link: https://reviews.llvm.org/D138135
link: https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html
2023-08-07 10:17:29 +08:00
Rui Ueyama 1c73fd57fc Fix CI 2023-08-06 17:32:36 +09:00
Rui Ueyama 24078a4395 Merge .mips_got sections 2023-08-06 15:09:25 +09:00
Rui Ueyama 270cbf42df Attempt to fix CI 2023-08-06 15:01:42 +09:00
Rui Ueyama ea75dae8ec Refactor 2023-08-06 14:01:11 +09:00
Rui Ueyama 6d290babe2 Refactor 2023-08-06 13:48:29 +09:00
Rui Ueyama 428faa75b2 Create multiple GOT sections for MIPS 2023-08-06 13:36:14 +09:00
Rui Ueyama 829c9a980e Refactor 2023-08-06 13:29:34 +09:00
Rui Ueyama 76af9907fd Fix CI 2023-08-05 18:24:49 +09:00
Rui Ueyama 9893bb331f Update comments 2023-08-05 18:08:34 +09:00
Rui Ueyama 6108e60936 Attempt to fix CI 2023-08-05 17:27:06 +09:00
Rui Ueyama 9e2e376f5a Add MIPS arch string to the -V message 2023-08-05 15:31:04 +09:00
Rui Ueyama 2db8b3fa48 Enable MIPS tests on CI 2023-08-05 15:28:03 +09:00
Rui Ueyama e2c6f0c171 Refactor 2023-08-05 14:46:21 +09:00
Rui Ueyama eba7cdade2 Improve MIPS support
All tests now pass for MIPS64LE and MIPS64BE.
2023-08-05 13:07:37 +09:00
Rui Ueyama ca88e45bf3 Disable tests for MIPS 2023-08-05 09:00:57 +09:00
Rui Ueyama 19776686ec Enable MIPS tests if -DMOLD_ENABLE_QEMU_TESTS_MIPS64=ON
m68k tests are disabled by default because they are broken with
recent updates to Ubuntu 22.04.
2023-08-05 08:42:40 +09:00
Rui Ueyama e0a150a22c Refactor 2023-08-04 19:36:18 +09:00
Rui Ueyama 0df6063d8a Improve MIPS support 2023-08-04 18:48:52 +09:00
Rui Ueyama 5199414933 Refactor 2023-08-03 17:03:52 +09:00
Rui Ueyama 85f1dec81d Improve MIPS support 2023-08-03 16:30:48 +09:00
Rui Ueyama 97c937a56d Refactor 2023-08-02 20:21:11 +09:00
Rui Ueyama f1113f7c76 Refactor 2023-08-02 10:24:26 +09:00
Rui Ueyama 0ced83597f Refactor 2023-08-01 16:27:44 +09:00
Rui Ueyama 1f5fbb808b Do not create dynamic relocations for R_AARCH64_MOVW_UABS_G3 2023-08-01 16:09:40 +09:00
Rui Ueyama 5fb6091fb7 Handle R_RISCV_PCREL_HI20 as a PLT-generating reloc 2023-08-01 15:44:27 +09:00
Rui Ueyama e764557007 Handle R_AARCH64_MOVW_UABS_G3 as a PLT-generating reloc
ARM64's crt1.o may refer to `main` with the following four instructions
instead of with the usual `R_AARCH64_ABS64` relocation:

  18:   d2e00000        movz    x0, #0x0, lsl #48
                        18: R_AARCH64_MOVW_UABS_G3      main
  1c:   f2c00000        movk    x0, #0x0, lsl #32
                        1c: R_AARCH64_MOVW_UABS_G2_NC   main
  20:   f2a00000        movk    x0, #0x0, lsl #16
                        20: R_AARCH64_MOVW_UABS_G1_NC   main
  24:   f2800000        movk    x0, #0x0
                        24: R_AARCH64_MOVW_UABS_G0_NC   main
  28:   d2e00003        movz    x3, #0x0, lsl #48

So we need to create a PLT if a symbol is referred to by this way.

Fixes https://github.com/rui314/mold/issues/1074
2023-08-01 15:02:30 +09:00
Rui Ueyama 961cc10e0d Refactor 2023-08-01 11:04:03 +09:00
Rui Ueyama 49fc7da32e Refactor 2023-08-01 00:39:25 +09:00
Rui Ueyama dd56734fda Fix symbol table for very large section indices 2023-07-31 19:38:04 +09:00
Rui Ueyama 2015347b0c Report an error for incompatible relocation types 2023-07-31 15:44:29 +09:00
Rui Ueyama 4daa9aad36 Do not write special symbol values to .symtab_shndx 2023-07-31 15:10:30 +09:00
Rui Ueyama 084ca55a36 Support -z nosectionheader 2023-07-31 13:50:50 +09:00
Rui Ueyama e06d886252 Report an error if a non-PIC object file is given for an PIC output 2023-07-31 12:44:27 +09:00
Rui Ueyama 9fe3d750cc Fix test
Fixes https://github.com/rui314/mold/issues/1073
2023-07-31 09:26:00 +09:00
Rui Ueyama 13bb00bfa2 Refactor 2023-07-31 09:05:19 +09:00
Rui Ueyama 79a72011b7 Refactor 2023-07-30 23:47:42 +09:00
Rui Ueyama f2b278ce10 Attempt to fix a test for Alt Linux
Fixes https://github.com/rui314/mold/issues/1073
2023-07-30 22:01:31 +09:00
Rui Ueyama ec10452b67 Run tests on ppc64le machines 2023-07-30 13:12:44 +09:00
Rui Ueyama ba3fd65bcc Refactor
Thanks to small string optimization, storing a short string to std::string
is usually more efficient than referring it from std::string_view.
2023-07-30 12:03:58 +09:00
Rui Ueyama f816c45b88 Extend comments 2023-07-30 11:55:06 +09:00
Rui Ueyama ca14cdf9b6 Do not create a PT_RISCV_ATTRIBUTES segment if .riscv.attributes is missing 2023-07-30 11:36:35 +09:00
Rui Ueyama 6d667fad45 Fix CI 2023-07-30 11:25:18 +09:00
Rui Ueyama aa644914fe Merge .riscv.attributes input sections 2023-07-30 00:56:47 +09:00
Rui Ueyama 1cbe4d44ae Fix style 2023-07-29 21:13:50 +09:00
Rui Ueyama 91805d1e26 Add Emerge Tools as our sponsor 2023-07-29 21:07:44 +09:00
Rui Ueyama 9212f2ebd7 Refactor 2023-07-29 12:08:24 +09:00
Rui Ueyama 784fd27e9e Refactor 2023-07-28 17:33:46 +09:00
Rui Ueyama b78f237a68 Fix -Wunused-variable
Fixes https://github.com/rui314/mold/issues/1063
2023-07-28 16:55:18 +09:00
Rui Ueyama 1582b720d5 Weak undefs should not keep DSOs alive
Fixes https://github.com/rui314/mold/issues/1067
2023-07-28 15:12:52 +09:00
Rui Ueyama 710127977e Make --repro output readable by busybox's tar command 2023-07-28 15:12:45 +09:00
Rui Ueyama 8e07496e08 Refactor 2023-07-28 12:55:31 +09:00
Rui Ueyama 163f5db0d1 Simplify 2023-07-28 10:58:28 +09:00
Rui Ueyama be2e1b17cd Skip test if grep is not a "real" grep 2023-07-27 21:48:53 +09:00
Rui Ueyama 5f5e73360b Add build dependencies for Alpine Linux 2023-07-27 21:48:53 +09:00
Rui Ueyama b0b015bc2e Merge pull request #1066 from aloisklink/install-LICENSE.third-party 2023-07-27 14:49:00 +09:00
Rui Ueyama 7f66689e21 Merge pull request #1068 from pcc/adrp 2023-07-27 14:42:12 +09:00
Peter Collingbourne 17a5c3e0ab Support R_AARCH64_ADR_PREL_PG_HI21_NC 2023-07-26 21:33:26 -07:00
Alois Klink 3ed5b8ddf7 Install LICENSE.third-party when using CMake 2023-07-27 01:10:22 +01:00
Rui Ueyama de991b519a Bump mold version to 2.0 2023-07-26 19:40:11 +09:00
Rui Ueyama a066fe25c6 Change the license to the MIT license 2023-07-26 14:49:42 +09:00
Rui Ueyama 6e8a9dfec1 Merge pull request #1058 from andreas-schwab/main 2023-07-24 19:09:17 +09:00
Rui Ueyama 31eeb44d4f Format 2023-07-23 13:23:36 +09:00
Rui Ueyama 44b15cbb35 Update the bundled xxhash to v0.8.2 2023-07-21 19:28:46 +09:00
Rui Ueyama b5b3ffa6fe Merge pull request #1062 from Expander/replace-pow 2023-07-20 19:54:41 +09:00
Rui Ueyama 83a924ddac Fix CI 2023-07-20 17:41:08 +09:00
Rui Ueyama 2e8bd0b7a6 Handle an output relocatable object file with a large number of sections
Fixes https://github.com/rui314/mold/issues/1061
2023-07-20 16:38:54 +09:00
Alexander Voigt b685f3cab7 replace pow(2,x) by ldexp(1.0,x) for performance 2023-07-20 09:28:35 +02:00
Rui Ueyama 2d25a93126 Handle -undefined as a synonym for --undefined instead of -u ndefined
Fixes https://github.com/rui314/mold/discussions/1059
2023-07-18 16:59:44 +09:00
Rui Ueyama a8eb52f61a Format 2023-07-17 16:00:39 +09:00
Rui Ueyama 761c341c19 Improve MIPS64 support 2023-07-17 11:45:55 +09:00
Rui Ueyama 000e395c07 Improve MIPS64 support 2023-07-16 08:01:42 +09:00
Rui Ueyama 4b06f1e9f6 Refactor 2023-07-15 15:01:03 +09:00
Rui Ueyama 29dc0cf53f Initial MIPS64 support
Although incomplete, this implementation is capable of passing the
following tests:

  TRIPLE=mips64el-linux-gnuabi64 bash -x ../test/elf/hello-dynamic.sh
  TRIPLE=mips64-linux-gnuabi64 bash -x ../test/elf/hello-dynamic.sh
2023-07-14 15:11:53 +09:00
Rui Ueyama f8b55680f8 Revert "Merge .alpha_got with .got"
This reverts commit 72671df483.
2023-07-14 14:12:45 +09:00
Rui Ueyama cb08466693 Attempt to fix CI 2023-07-14 10:41:36 +09:00
Rui Ueyama 72671df483 Merge .alpha_got with .got
It's not only Alpha but MIPS relocations also need dynamic symbols
with addends, so we want to use this code not only for Alpha.
2023-07-14 10:06:18 +09:00
Rui Ueyama 83524e3690 Update comments 2023-07-09 11:25:02 +09:00
Rui Ueyama 0125d9c254 Refactor 2023-07-06 17:33:41 +09:00
Andreas Schwab 1ac5fe7c1e Fix support for R_RISCV_SET_ULEB128 and R_RISCV_SUB_ULEB128
Handle allocated sections and write the full value into the location.

Signed-off-by: Andreas Schwab <[email protected]>
2023-07-06 10:23:38 +02:00
Rui Ueyama 4b42f38257 Handle embedded default version strings in LTO symbols
Fixes https://github.com/rui314/mold/issues/1056
2023-07-05 19:07:52 +09:00
Rui Ueyama d2b153c522 Refactor 2023-07-05 18:43:07 +09:00
Rui Ueyama ab126ad1d9 Make sure to import versioned symbols
Undefined versioned symbols are handled specially; they are resolved
at the very end of name resolution stage. When they are resolved, we
need to make sure that they are marked as imported symbols.

Fixes https://github.com/rui314/mold/issues/1057
2023-07-05 11:34:33 +09:00
Rui Ueyama b04aba89d3 Add -z stack-size
https://github.com/rui314/mold/issues/917
2023-06-29 15:58:00 +09:00
Rui Ueyama 2cb41d8a2b Upgrade bundled mimalloc to v2.1.2 2023-06-29 12:23:39 +09:00
Rui Ueyama 1b1a142a89 Upgrade bundled OneTBB to version 2021.9.0 2023-06-29 12:21:58 +09:00
Rui Ueyama dcfe76bc80 Format 2023-06-29 12:17:21 +09:00
Rui Ueyama d4d93d7fb7 Format 2023-06-19 12:35:20 +09:00
Rui Ueyama ebd780e8ba Support R_PPC64_REL32
Fixes https://github.com/rui314/mold/issues/1049
2023-06-17 19:00:55 +09:00
Rui Ueyama 65bd36ba40 Generate a PT_OPENBSD_RANDOMIZE segment 2023-06-14 14:08:40 +09:00
Rui Ueyama bc86ab514f Add -nopie as an alias for -no-pie
OpenBSD seems to be using an alternative spelling for this option.
2023-06-14 09:55:55 +08:00
Rui Ueyama 8ee7773260 Do not use mimalloc on OpenBSD
It seems like mimalloc doesn't work on OpenBSD at all.
2023-06-13 11:10:04 +08:00
Rui Ueyama e011889436 Fix build on systems without ucontext.h in libc
Upstream patch:
https://github.com/oneapi-src/oneTBB/commit/ea4e6156a2cde839fc01b21b76cd9b5743ec0cef
2023-06-05 11:52:56 +09:00
Rui Ueyama 16e22ec067 Handle "amd64" as a synonym for "x86_64"
OpenBSD's `uname` returns `amd64` instead of `x86_64`.
2023-06-04 15:28:44 +09:00
Rui Ueyama d2ee47a4ac Print out a hint about older versions of litbb
https://github.com/rui314/mold/issues/1031
2023-06-04 15:28:44 +09:00
Rui Ueyama dce8ebe526 Merge pull request #1033 from ishitatsuyuki/wrap-mold
mold-wrapper: Wrap ld.mold as well.
2023-04-19 20:10:36 +09:00
Tatsuyuki Ishi 23de4be59f mold-wrapper: Wrap ld.mold as well.
Convenient if you have configured to use mold for a particular project but want
to test a new local build.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2023-04-19 19:35:29 +09:00
Rui Ueyama 76e3f87775 Merge pull request #1034 from ishitatsuyuki/errno 2023-04-19 19:26:54 +09:00
Tatsuyuki Ishi fca6fad720 MappedFile: Fail on open() errors other than ENOENT.
open() can fail with transient reasons, such as running out of file descriptor
limit. Previously, this was treated the same as file not being found, which
caused confusing error messages [1].

With this change anything but ENOENT will be treated as a fatal error. This will
also include e.g. permission errors, but if there's something within the search
path that is not accessible, it's likely better to tell the user instead of
silently skipping it.

[1]: https://github.com/rui314/mold/issues/1026

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2023-04-19 17:29:37 +09:00
Rui Ueyama ceffcfcadd Fix test
Fixes https://github.com/rui314/mold/issues/1030
2023-04-18 16:47:16 +09:00
Rui Ueyama 1ad19a4ce1 Simplify 2023-04-16 14:57:47 +09:00
Rui Ueyama 67a58c01d0 Update comments 2023-04-16 14:41:10 +09:00
Rui Ueyama 186ac191c4 Update comments 2023-04-15 15:15:18 +09:00
Rui Ueyama 34bf04bc41 Improve tests 2023-04-15 12:53:43 +09:00
Rui Ueyama e6db53fadb Do not set STT_GNU_IFUNC to non-exported symbols
STT_GNU_IFUNC is significant only for exported symbols. Setting
non-exported symbol type to STT_GNU_IFUNC actually causes an issue
if the symbol is used as a canonical PLT because the runtime would
try to run the canonical PLT as an IFUNC.

Fixes https://github.com/rui314/mold/issues/1028
2023-04-15 12:32:21 +09:00
Rui Ueyama 461001328b Simplify 2023-04-02 09:09:04 +08:00
Rui Ueyama 4bffe26bc0 Support R_RISCV_SET_ULEB128 and R_RISCV_SUB_ULEB128
https://github.com/riscv-non-isa/riscv-elf-psabi-doc/pull/361
2023-03-31 10:26:50 +08:00
Rui Ueyama 290b4eaa46 Refactor 2023-03-28 17:30:44 +08:00
Rui Ueyama 0f1b828e6a Refactor 2023-03-28 16:57:57 +08:00
Rui Ueyama 46288758a0 Improve README with ChatGPT 2023-03-22 16:48:14 +08:00
Rui Ueyama e8789c71a4 Format 2023-03-22 16:35:22 +08:00
Rui Ueyama f54203d76c Update the auto-generated manpage 2023-03-22 12:55:52 +08:00
Rui Ueyama a5634794b3 Improve the man page
I made this change by asking GPT-4-based ChatGPT to improve this
document with an additional note that this document was written by a
non-native English speaker and may have wrong or unnatural phrases.
2023-03-22 12:40:35 +08:00
Rui Ueyama b403b811cd Report a possible ODR violation
https://github.com/rui314/mold/issues/1019
2023-03-18 16:32:04 +08:00
Rui Ueyama 36fc065548 Add a feature to not run mold simultaneously
https://github.com/rui314/mold/issues/117
2023-03-17 14:58:27 +08:00
Rui Ueyama 39b4548a2a Simplify 2023-03-17 13:58:11 +08:00
Rui Ueyama 52f0c4a06f Fix typos 2023-03-17 13:49:59 +08:00
Rui Ueyama c74fe12dd5 Fix test 2023-03-16 18:24:25 +08:00
Rui Ueyama fe11565110 Improve comments 2023-03-16 18:08:44 +08:00
Rui Ueyama cca255e6be Bump mold version to 1.11.0 2023-03-16 14:01:09 +08:00
Rui Ueyama 325d12d169 Upload test artifacts only on test failure 2023-03-15 08:32:51 +08:00
Rui Ueyama 2a26a0db64 Fix CI 2023-03-15 08:32:51 +08:00
Rui Ueyama 72c9425ae3 Merge pull request #1016 from marxin/fix-Wunused-but-set-variable
Fix warning: variable ‘check’ set but not used [-Wunused-but-set-variable]
2023-03-14 21:44:48 +09:00
Rui Ueyama ff46c33d3f Merge pull request #1015 from marxin/fix-Wint-in-bool-context
fix thunks.cc:81:32: warning: enum constant in boolean context [-Wint…
2023-03-14 21:44:33 +09:00
Martin Liska c59b3f5fa5 Fix warning: variable ‘check’ set but not used [-Wunused-but-set-variable]
Signed-off-by: Martin Liska <[email protected]>
2023-03-14 13:28:45 +01:00
Martin Liska 84a5509da2 fix thunks.cc:81:32: warning: enum constant in boolean context [-Wint-in-bool-context]
Fixes the following error:
/elf/thunks.cc:81:32: warning: enum constant in boolean context [-Wint-in-bool-context]
  return ty == R_PPC64_REL24 || R_PPC64_REL24_NOTOC;

Signed-off-by: Martin Liska <[email protected]>
2023-03-14 13:25:05 +01:00
Rui Ueyama 305153b9c7 Archive test results 2023-03-14 19:22:56 +08:00
Rui Ueyama 1adde7aa51 Make undefined symbol in a DSO to keep other DSOs alive
Fixes https://github.com/rui314/mold/issues/1014
2023-03-14 18:52:03 +08:00
Rui Ueyama 4fa2bc17b8 Allow empty mergeable sections
Fixes https://github.com/rui314/mold/issues/1012
2023-03-14 07:14:18 +08:00
Rui Ueyama 294a467116 Rename files 2023-03-13 16:23:36 +08:00
Rui Ueyama 67eb3069b3 Fix build failure
Fixes https://github.com/rui314/mold/issues/1008
2023-03-07 18:34:59 +08:00
Rui Ueyama ad2694445c Import changes from sold 2023-03-07 15:57:28 +08:00
Rui Ueyama 6a1713e440 Merge pull request #1004 from ishitatsuyuki/lto-eh-frame 2023-03-07 12:01:54 +09:00
Tatsuyuki Ishi 62d65371d7 [ELF] Kill .eh_frame sections after symbol resolution.
Handle .eh_frame in a similar way as mergeable sections.

This seems to be required for GCC CRT compiled with LTO.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2023-03-06 13:22:06 +08:00
Rui Ueyama c4722fe5ae Build Linux binaries on GitHub Actions 2023-03-05 14:46:32 +08:00
Tatsuyuki Ishi 13472be059 [ELF] Resolve .eh_frame symbols renamed by GCC LTO.
When LTO is enabled for CRT it seems symbols like __EH_FRAME_BEGIN__.lto_priv.0
can be created. Treat it as the same as the non-LTO variant.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2023-03-05 13:59:40 +08:00
Rui Ueyama 6d2dbd42a6 Preparation to support Apple ld64's -w option 2023-03-05 08:48:09 +08:00
Rui Ueyama d2970e096d Support R_X86_64_GOTOFF64 in non-memory-allocated sections
Fixes https://github.com/rui314/mold/issues/1003
2023-03-05 08:07:24 +08:00
Rui Ueyama 77c4795139 Workaround for an old GCC issue
Fixes https://github.com/rui314/mold/issues/1003
2023-03-04 17:10:49 +08:00
Rui Ueyama 5434babdf3 Fix typo 2023-03-04 16:53:02 +08:00
Rui Ueyama 530b3ebd88 Enable tests for Power10 if MOLD_ENABLE_QEMU_TESTS_POWER10 is true 2023-03-04 16:13:57 +08:00
Rui Ueyama 33172a7b61 Update comments 2023-03-04 11:01:25 +08:00
Rui Ueyama 5065547c4e Support the Power10 ABI 2023-03-04 09:43:42 +08:00
Rui Ueyama 0870078ed2 Merge pull request #997 from ksco/fix_rv64_test 2023-02-22 18:27:57 +09:00
Yang Liu 992ad0fc08 [RISCV] Fix riscv64_obj-compatible 2023-02-22 16:40:42 +08:00
Rui Ueyama 5ae8ec9669 Merge pull request #993 from ksco/riscv_compatibility 2023-02-21 14:03:08 +09:00
Rui Ueyama 0bd5adde7e Refactor 2023-02-20 18:06:33 +08:00
Yang Liu 7e8a9f230c [RISCV] Add an obj ABI incompatibility tests
Signed-off-by: Yang Liu <[email protected]>
2023-02-19 19:59:29 +08:00
Yang Liu f7bc901c2d [RISCV] Check object files' compatibility
Signed-off-by: Yang Liu <[email protected]>
2023-02-19 19:58:13 +08:00
Rui Ueyama e1a7590db3 Fix LTO symbol resolution instability
Previously, we clear liveness bits for all input files after compiling
IR object files to real object files so that we can do name resolution
completely from scratch. However, that can result in inconsistent name
resolution results before and after LTO.

Consider a weak defined symbol X in an object file. A weak undef doesn't
pull files out of an archive file. However, if symbol X is transformed
to an undefined symbol as a result of LTO, it could pull out a file from
an archive. That can have cascading effects and result in a link failure.

With this fix, all object files that are alive at the time when LTO kicks
in are guaranteed to be kept after LTO.

Fixes https://github.com/rui314/mold/issues/991
2023-02-19 16:26:46 +08:00
Rui Ueyama f592b6e167 Refactor 2023-02-16 15:18:25 +08:00
Rui Ueyama 8c0f455b97 Refactor 2023-02-16 08:58:47 +08:00
Rui Ueyama 0d08c52258 Refactor 2023-02-15 20:46:50 +08:00
Rui Ueyama a42626b975 Skip if ppc64
Fixes https://github.com/rui314/mold/issues/989
2023-02-15 15:51:35 +08:00
Rui Ueyama ec728d0cef Simplify 2023-02-13 10:17:37 +08:00
Rui Ueyama 20bddc75cf Avoid inline assembly 2023-02-12 11:58:47 +08:00
Rui Ueyama 45bece4bd9 Handle ppc64 as a synonym for powerpc64
Fixes https://github.com/rui314/mold/issues/986
2023-02-12 09:06:10 +08:00
Rui Ueyama 445a23fbc0 Merge pull request #985 from ksco/fix_ci_warnings
[CI] Use clang++-12 to suppress warnings
2023-02-12 08:11:18 +09:00
Rui Ueyama 6952a2efa5 Merge pull request #984 from ksco/weak_undef4
Add an another weak undef test
2023-02-12 08:09:44 +09:00
Yang Liu 8c2ccd2f55 [CI] Use clang++-12 to suppress warnings 2023-02-11 15:00:08 +08:00
Yang Liu 9ce963b480 Add an another weak undef test
This test ensures that mold does not extract archive members to resolve
undefined weak symbols.

Signed-off-by: Yang Liu <[email protected]>
2023-02-11 14:51:30 +08:00
Rui Ueyama 35794cfc66 Merge pull request #983 from ksco/weak_undef3 2023-02-11 13:24:06 +09:00
Yang Liu e300c6d9b2 [RISCV] Add a weak undef symbol test case
Signed-off-by: Yang Liu <[email protected]>
2023-02-11 11:43:02 +08:00
Rui Ueyama 8660db16a8 Update man page 2023-02-11 08:51:20 +08:00
Rui Ueyama 25b4f0b054 Merge pull request #980 from ksco/fix_undef_weak
[RISCV] Fix R_RISCV_PCREL_HI20
2023-02-10 10:28:53 +09:00
Yang Liu fc809c6c73 [RISCV] Fix R_RISCV_PCREL_HI20 2023-02-09 14:15:56 +08:00
Rui Ueyama 51845ac5fe Support R_RISCV_PLT32
https://github.com/riscv-non-isa/riscv-elf-psabi-doc/pull/363
2023-02-06 12:48:56 +08:00
Rui Ueyama 8e50fad6b9 Update man page 2023-02-06 12:48:56 +08:00
Rui Ueyama 3e818cd350 Simplify 2023-02-06 08:24:33 +08:00
Rui Ueyama 4327d4547e Fix typo 2023-02-06 08:10:45 +08:00
Rui Ueyama abba8b5864 Update man page 2023-02-05 21:10:39 +08:00
Rui Ueyama 8ede25fa42 Write man page in Markdown instead of mandoc
mandoc is hard to read and hard to write. With `ronn` command, we can
write a man page in Markdown and convert it to roff.
2023-02-05 16:46:46 +08:00
Rui Ueyama 50d4bc04ab Update README 2023-02-05 12:10:09 +08:00
Rui Ueyama 128f8b3b8d Remove mentions to mold/macOS
Fixes https://github.com/rui314/mold/issues/978
2023-02-05 12:04:07 +08:00
Rui Ueyama 5140010a6e Attempt to fix R_ARM_PLT32
https://github.com/rui314/mold/issues/974
2023-02-05 08:06:13 +08:00
Rui Ueyama 24c21d7ed2 Simplify
is_imported and is_exported are computed and overwritten by
compute_import_export(), so these early assignments are redundant.
2023-02-05 07:52:37 +08:00
Rui Ueyama e50590043c Support R_AARCH64_PLT32 2023-02-01 11:36:06 +08:00
Rui Ueyama fdc592b169 Warn on shared object files in archives
https://github.com/rui314/mold/issues/976
2023-01-30 09:38:44 +08:00
Rui Ueyama 82731d62f9 Add a new sponsor name 2023-01-28 09:34:28 +08:00
Rui Ueyama 912a863b13 Refactor 2023-01-28 09:03:24 +08:00
Rui Ueyama 627bf7c8ef Do not relax GOTPCRELX reloc if it's unreachable
Fixes https://github.com/rui314/mold/issues/975
2023-01-27 20:12:40 +08:00
Rui Ueyama beddabe64d Import changes for the sold linker 2023-01-26 08:34:04 +08:00
Rui Ueyama 339b1b4704 Merge pull request #973 from ganta/patch-1
Adjust the image ratio and fix the alt attribute.
2023-01-24 12:47:55 +09:00
Hideki Igarashi 8dff18db0e Adjust the image ratio and fix the alt attribute.
Signed-off-by: Hideki Igarashi <[email protected]>
2023-01-24 12:40:26 +09:00
Rui Ueyama ebb9b6aff8 Add commas 2023-01-23 21:45:23 +08:00
Rui Ueyama 22af2ff328 Add Cybozu as a corporate-tier sponsor 2023-01-23 17:02:23 +08:00
Rui Ueyama d755793345 Remove unused struct member 2023-01-23 09:35:38 +08:00
Rui Ueyama ec756333cb Support --hash-style=none to cancel --hash-style=[sysv|gnu|both]
https://github.com/rui314/mold/issues/971
2023-01-22 20:29:15 +08:00
Rui Ueyama ce3aa9a752 Update an include file path
Pointed out by https://github.com/rui314/mold/pull/972/files
2023-01-22 19:35:45 +08:00
Rui Ueyama e48b90f941 Make Atomic copyable and copy-constructible 2023-01-22 16:25:25 +08:00
Rui Ueyama 7f407fb1ae Refactor 2023-01-22 12:44:59 +08:00
Rui Ueyama ac212c3d00 Improve range extension insertion algorithm
Now the algorithm matches with the code comment.
2023-01-22 12:29:33 +08:00
Rui Ueyama 040180f933 Bump mold version to 1.10.1 2023-01-22 09:57:00 +08:00
Rui Ueyama 7e6554677f Fix out-of-bound memory access
Fixes https://github.com/rui314/mold/issues/969
2023-01-21 12:06:10 +08:00
Rui Ueyama dbeb10c0d2 Recognize ld64.sold as a command name 2023-01-20 16:21:18 +08:00
Rui Ueyama c935d6387a Bump mold version to 1.10.0 2023-01-20 15:03:58 +08:00
Rui Ueyama acdd92e741 Refactor 2023-01-20 15:02:32 +08:00
Rui Ueyama 62660bc02a Refactor 2023-01-20 12:07:03 +08:00
Rui Ueyama 90d9fd06ae Refactor 2023-01-19 20:34:00 +08:00
Rui Ueyama 82d412cbff Report an error for a TLS access model mismatch 2023-01-19 16:59:06 +08:00
Rui Ueyama 85efa3796a Set DF_STATIC_TLS to DSOs that cannot be loaded with dlopen() 2023-01-19 16:37:03 +08:00
Rui Ueyama 3f672919c2 Refactor 2023-01-19 15:01:22 +08:00
Rui Ueyama a2e8f13ddd Refactor 2023-01-19 14:54:48 +08:00
Rui Ueyama 821fb07083 Also fix R_390_{PC,PLT}12DBL 2023-01-19 14:16:22 +08:00
Rui Ueyama b9bd71ecfc Fix R_390_{12,GOT12,GOTPLT12}
When writing a 12 bit value, we need to preserve the most significant 4 bits.
2023-01-19 14:01:05 +08:00
Rui Ueyama 9df4d6e479 Fix test/elf/compressed-debug-info.sh for SH-4 2023-01-19 12:51:51 +08:00
Rui Ueyama 29eb97fda5 Rename a file 2023-01-19 11:43:29 +08:00
Rui Ueyama 67c6255c9a Refactor 2023-01-19 10:31:21 +08:00
Rui Ueyama 2d68cbbfd2 Save 8 byte of memory for each global symbol 2023-01-19 08:54:16 +08:00
Rui Ueyama f98e0ef92a Refactor 2023-01-18 20:57:24 +08:00
Rui Ueyama 83303995c7 Refactor 2023-01-18 20:40:26 +08:00
Rui Ueyama 7d369dd9e8 Refactor 2023-01-18 17:29:17 +08:00
Rui Ueyama 9f66ea7026 Build the native binary by default 2023-01-18 16:29:25 +08:00
Rui Ueyama 67a96ae8e1 Refactor 2023-01-18 16:20:20 +08:00
Rui Ueyama 8936194ba7 Report all undefined symbols correctly
Previously, we failed to report an undefined error if we have two
undefined symbols, a strong and a weak one, for the same symbol.

It is an undefined symbol error if an undefined symbol is resolved
to a weak undefined symbol.

Fixes https://github.com/rui314/mold/issues/967
2023-01-18 16:01:36 +08:00
Rui Ueyama 00af084416 Remove an unnecessary dependency 2023-01-18 12:45:08 +08:00
Rui Ueyama 3829372a42 Refactor 2023-01-18 11:24:15 +08:00
Rui Ueyama 54ab1ec4b4 Silence -Werror=format-truncation
Ideally we wanted to use __builtin_assume() or [[assume]], but the former
is available only in clang and the latter is in C++23.

Fixes https://github.com/rui314/mold/issues/966
2023-01-18 11:00:59 +08:00
Rui Ueyama f4b0596b3a Update the man page 2023-01-18 09:02:55 +08:00
Rui Ueyama 71f6b0bc32 Refactor 2023-01-17 21:05:26 +08:00
Rui Ueyama fb8e63e498 Make some tests faster 2023-01-17 09:19:09 +08:00
Rui Ueyama 1a133a4df5 Upgrade the bundled mimalloc from 2.0.7 to 2.0.9 2023-01-17 08:53:56 +08:00
Rui Ueyama 60fc036bd6 Enabling logging is [[unlikely]] 2023-01-17 07:25:18 +08:00
Rui Ueyama 3d96596485 Add a newline after a comment for --print-dependencies 2023-01-16 18:08:52 +08:00
Rui Ueyama 0a8e90bd84 Rename a file 2023-01-16 16:22:30 +08:00
Rui Ueyama 1e961000e6 Move more files under common/ 2023-01-16 15:35:24 +08:00
Rui Ueyama 3539487471 Remove an unnecessary file 2023-01-16 15:31:33 +08:00
Rui Ueyama 6caf0867b0 Reorganize files 2023-01-16 15:27:10 +08:00
Rui Ueyama 28a6c0e4f0 Update references 2023-01-16 12:34:36 +08:00
Rui Ueyama a4349df0a5 [PPC64v1] Support a PLT section containing more than 32767 entries. 2023-01-16 11:06:34 +08:00
Rui Ueyama d2335707de [x86] Always relax TLSGD/TLSLD if -static 2023-01-14 15:27:28 +08:00
Rui Ueyama 1d2b08ade7 [x86] Always relax GOTX relocations 2023-01-14 15:19:27 +08:00
Rui Ueyama dd93988dc6 Simplify 2023-01-14 13:14:07 +08:00
Rui Ueyama 06ec355e88 Improve instal-build-deps script
voidlinux/voidlinux Docker image doesn't contain bash, so we should
eliminate the dependency to bash from this script.
2023-01-14 12:00:49 +08:00
Rui Ueyama c10f36c71c Merge pull request #963 from mystuffs/main
Add: build deps when compiling mold on Void Linux
2023-01-14 12:37:56 +09:00
Rui Ueyama f32ce333dd [s390x] Implement TLSGD to TLSIE relaxation 2023-01-14 11:33:58 +08:00
rilysh d48c52cc2f Merge branch 'rui314:main' into main 2023-01-14 08:49:54 +05:30
Rui Ueyama 25d02bb261 [x86-64] Implement TLSGD to TLSIE relaxation
If we know that the .so file we are creating will not be dlopen'ed,
we can relax __tls_get_addr function calls to GOT loads.
2023-01-14 10:22:06 +08:00
rilysh fda7657eab add: build deps for Void Linux
Install build dependencies when compiling mold on Void Linux

Signed-off-by: rilysh <[email protected]>
2023-01-14 01:06:06 +00:00
Rui Ueyama 983fe0f10f Refactor 2023-01-13 21:07:30 +08:00
Rui Ueyama 8437097305 Refactor 2023-01-13 20:46:19 +08:00
Rui Ueyama 5a9341659c Remove an unnecessary log message 2023-01-13 17:43:08 +08:00
Rui Ueyama 125af4c855 Refactor 2023-01-13 16:34:50 +08:00
Rui Ueyama fa1db07ec8 Optimize section fragment liveness management 2023-01-13 15:23:09 +08:00
Rui Ueyama f09bf0615b Simplify 2023-01-13 11:36:02 +08:00
Rui Ueyama 534cd392db Do not run if -static is not supported
Fixes https://github.com/rui314/mold/issues/961
2023-01-13 08:48:09 +08:00
Rui Ueyama 5dee5ea49d Refactor 2023-01-12 17:11:18 +08:00
Rui Ueyama 957eaf0f18 Optimize DynamicSection::finalize
Parallelizing this loop makes sense if we have hundreds of thousands
of dynamic symbols.
2023-01-12 13:44:02 +08:00
Rui Ueyama 12fad60eb4 Reduce the number of dynamic symbols for TLS symbols 2023-01-12 11:52:10 +08:00
Rui Ueyama 404077e330 Refactor 2023-01-12 10:49:20 +08:00
Rui Ueyama 73947c56e1 Create section symbols for non-ALLOC sections
Previously, we created section symbols only for memory-allocated secitons.
That resulted in symbols with invalid section indices for the --relocatable
output.

https://github.com/rui314/mold/issues/957
2023-01-12 09:57:24 +08:00
Rui Ueyama 63fae64e77 Refactor 2023-01-12 09:55:53 +08:00
Rui Ueyama 7f27b180ac Optimize 2023-01-11 17:29:39 +08:00
Rui Ueyama 6f4ba44946 Avoid sequentially consistent memory access 2023-01-11 14:16:41 +08:00
Rui Ueyama dcc58e4f7a Refactor 2023-01-11 10:45:45 +08:00
Rui Ueyama a68dfdb2d3 Reduce the sizes of Symbol and Relocation for needs_thunk targets
Since the maximum size of RangeExtensionThunk is 100 KiB, it can't contain
too many entries, so the contents is addressable with a 16-bit value.

We don't create more than 32768 range extension thunks (unless the output
is hundreds gibibytes in size), so we can also use i16 to specify the
index of RangeExtensionThunk.
2023-01-10 17:39:17 +08:00
Rui Ueyama 277dd7710a Refactor 2023-01-10 17:27:35 +08:00
Rui Ueyama d03c9a1654 Fix data race
The iterator obtained by `find()` may be invalidated by other writes
happening concurrently, so we shouldn't release the mutex while we
are using it.
2023-01-10 15:22:07 +08:00
Rui Ueyama 19bb456721 Merge pull request #954 from MehdiChinoune/fix-mingw-w64 2023-01-10 15:34:05 +09:00
مهدي شينون (Mehdi Chinoune) 4e26d7b237 Fix building on MinGW-w64
Signed-off-by: Mehdi Chinoune <[email protected]>
2023-01-10 06:41:46 +01:00
Rui Ueyama 6a797a4fbc Fix CI 2023-01-10 13:41:31 +08:00
Rui Ueyama 14784bbc4e Fix CI 2023-01-10 13:04:12 +08:00
Rui Ueyama 6fd47dbab4 Document the --print-dependencies option
We used to have `--print-dependencies` and `--print-dependencies=full`
options. I think this distinction is necesssary. Now, `=full` is renamed
`--print-dependencies`.
2023-01-10 12:54:00 +08:00
Rui Ueyama e31801f22f Refactor 2023-01-10 12:37:25 +08:00
Rui Ueyama 58e81bb0a0 Cache more results 2023-01-09 20:32:51 +08:00
Rui Ueyama d2cdce4410 Implement another algorithm to optimize create_output_sections
https://github.com/rui314/mold/issues/937
2023-01-09 19:31:22 +08:00
Rui Ueyama 7132822cc7 Mitigate lock contention
This change speeds up the `create_output_sections` pass.

https://github.com/rui314/mold/issues/937
2023-01-09 18:05:18 +08:00
Rui Ueyama ca98fe843d Fix -Werror=implicit-fallthrough
These aren't bug, but I decided to repeat the same code instead of
adding [[fallthrough]] because the code duplication is very small.

Fixes https://github.com/rui314/mold/issues/952
2023-01-09 17:04:51 +08:00
Rui Ueyama bbfed9af3f Do not access uninitialized memory 2023-01-09 16:38:10 +08:00
Rui Ueyama 707ddaa630 Avoid unnecessary memory initializations 2023-01-09 14:44:02 +08:00
Rui Ueyama d127583c86 Refactor 2023-01-09 13:59:35 +08:00
Rui Ueyama 7128ee371d Skip if ARM64 too
https://github.com/rui314/mold/issues/951
2023-01-09 13:21:35 +08:00
Rui Ueyama 321158b99f Skip test if musl libc
Fixes https://github.com/rui314/mold/issues/951
2023-01-09 13:03:49 +08:00
Rui Ueyama d71a672089 Fix an option name
LLVM lld does not accept `-gdb-index` as an option
2023-01-09 12:46:15 +08:00
Rui Ueyama e17d7daee8 Fix a crash bug in --emit-relocs
Fixes https://github.com/rui314/mold/issues/950
2023-01-09 09:31:27 +08:00
Rui Ueyama d4e40a31c1 Do not R_AARCH64_ADR_GOT_PAGE if it refers an IFUNC 2023-01-09 08:56:48 +08:00
Rui Ueyama 915cd23b2f Skip if GNU IFUNC is not supported
Even on x86-64, non-GNU libc may not support the IFUNC.

Fixes https://github.com/rui314/mold/issues/949
2023-01-09 08:26:36 +08:00
Rui Ueyama 994f800f8f Add a missing relocation overflow check 2023-01-09 08:15:18 +08:00
Rui Ueyama 2b3a684869 Refactor 2023-01-09 08:15:03 +08:00
Rui Ueyama 68b2b35148 Refactor 2023-01-08 21:15:53 +08:00
Rui Ueyama da6dd7c0ac [ARM64] Relax R_AARCH64_ADR_PREL_PG_HI21 2023-01-08 20:47:49 +08:00
Rui Ueyama f2311b1efa [ARM64] Relax GOT-loading ADRP+LDR to ADRP+ADD 2023-01-08 16:43:23 +08:00
Rui Ueyama b8bb78fde7 Update comments 2023-01-08 13:58:41 +08:00
Rui Ueyama e4a1a57318 Update comments 2023-01-08 13:21:36 +08:00
Rui Ueyama 1ebaa27fc4 Refactor 2023-01-08 12:46:51 +08:00
Rui Ueyama d6db850121 Refactor 2023-01-08 12:02:40 +08:00
Rui Ueyama ebf732fca9 Update comments 2023-01-08 11:26:21 +08:00
Rui Ueyama eba86d5312 Document TLS 2023-01-08 09:38:55 +08:00
Rui Ueyama 648fcc60ca Refactor 2023-01-08 07:35:14 +08:00
Rui Ueyama 9f5b7e278f Enable -gdb-index when building mold 2023-01-07 17:26:08 +08:00
Rui Ueyama 14fe1ba070 Fix typo 2023-01-07 17:21:14 +08:00
Rui Ueyama 9d3cfd5c7d Refactor 2023-01-07 16:39:13 +08:00
Rui Ueyama c2d5f602e7 Refactor 2023-01-07 15:00:03 +08:00
Rui Ueyama ed42ce0d36 Do not install LICENSE file if it does not exist 2023-01-07 14:28:08 +08:00
Rui Ueyama 8201d0ed2a Add const 2023-01-07 11:59:18 +08:00
Rui Ueyama 779a404950 [RISC-V] Fix wrong R_RISCV_LO12_{I,S} relaxation 2023-01-07 11:27:13 +08:00
Rui Ueyama 52a0638dd2 Simplify
We used to try to avoid using identifiers such as S, A or P more than
once because they were defined as macros expaneded to function calls.
Now they are just variables, so we can use them as many times as we
want without any performance penalty.
2023-01-07 11:09:47 +08:00
Rui Ueyama 73034f94fe Do not trigger undefined behavior for unknown relocations
Unknown relocations will be reported by scan_relocations(). We don't
want to execute unreachable() statement in get_addend() because it
triggers undefined behavior.

https://github.com/rui314/mold/issues/947
2023-01-07 10:23:07 +08:00
Rui Ueyama ad0b6d0ac6 Bump mold version to 1.9.0 2023-01-06 16:13:10 +08:00
Rui Ueyama 6e918eda31 Fix CI 2023-01-06 12:04:47 +08:00
Rui Ueyama 3c968289f8 Fix crash bug for when --gdb-index and -gsplit-dwarf are used together
Fixes https://github.com/rui314/mold/issues/940
2023-01-06 11:09:12 +08:00
Rui Ueyama cb204decf4 Fix R_RISCV_HI20 relaxation
If we compile a program for high address, this code would accidentally
clear high 20 bits.
2023-01-06 10:22:20 +08:00
Rui Ueyama f9fe3d621f Refactor 2023-01-05 21:48:03 +08:00
Rui Ueyama b51d5baae6 Handle R_ARM_PLT32 as an alias for R_ARM_JUMP24 instead of R_ARM_CALL
It looks like R_ARM_PLT32 can be applied to non-BL/non-BLX instructions,
so we can't handle it as an alias for R_ARM_CALL.

https://github.com/rui314/mold/issues/934
2023-01-05 18:07:52 +08:00
Rui Ueyama 747ba1d41e Add a missing relocation type
https://github.com/rui314/mold/issues/934
2023-01-05 17:20:56 +08:00
Rui Ueyama 5c4ed9259e Enable more tests for arm32 2023-01-05 15:20:52 +08:00
Rui Ueyama d3e0ffe0b9 Refactor 2023-01-05 09:49:28 +08:00
Rui Ueyama 17a24cdd7e Fix "error: enumerated and non-enumerated type in conditional expression" warning
Fixes https://github.com/rui314/mold/issues/945
2023-01-04 09:02:16 +08:00
Rui Ueyama 4a66d7a573 Revert "Bit permutation"
This reverts commit cbd88ed3f3.
It was a nice abstraction but wasn't worth its complexity unless we
use it in various places. The original simple code is more in line
with other parts of mold.

Fixes https://github.com/rui314/mold/issues/946
2023-01-04 08:51:20 +08:00
Rui Ueyama cbd88ed3f3 Bit permutation 2023-01-03 18:05:15 +08:00
Rui Ueyama 9157414cae Make ElfRel trivial type
Fixes https://github.com/rui314/mold/issues/944
2023-01-03 16:47:24 +08:00
Rui Ueyama 6663adb700 Refactor 2023-01-03 16:47:16 +08:00
Rui Ueyama 7c64f67499 Merge pull request #943 from marxin/fix-Wunused-variable 2023-01-03 17:35:24 +09:00
Martin Liska 7539c3fd15 Fix warning: unused variable 'G' [-Wunused-variable]
elf/arch-i386.cc:425:9: warning: unused variable 'G' [-Wunused-variable]

Signed-off-by: Martin Liska <[email protected]>
2023-01-03 09:31:28 +01:00
Rui Ueyama 83f6bc18e1 Refactor 2023-01-02 20:52:43 +08:00
Rui Ueyama ea4cb40131 Fix CI 2023-01-02 20:44:45 +08:00
Rui Ueyama bdc3d71a23 Refactor 2023-01-02 19:53:05 +08:00
Rui Ueyama b15ce5203d Refactor 2023-01-02 16:31:14 +08:00
Rui Ueyama 4507e6e51d Do not create thunks for all symbols 2023-01-02 16:13:16 +08:00
Rui Ueyama c86689b227 Do not use C macros
We used to use C macros as a poor-man's way to inline Symbol::get_addr(),
but it looks like inlining that member function isn't an overall win.
This change removes `inline` from that member function and depends on the
compiler's optimizations to optimize function calls.
2023-01-02 15:44:11 +08:00
Rui Ueyama 89156f3fcc Simplify 2023-01-02 09:53:15 +08:00
Rui Ueyama d0abdf3bbe Refactor 2023-01-02 05:43:16 +08:00
Rui Ueyama 9615b8b962 Do not bother to clear existing bits when applying relocations
We were inconsistent as to when to clear existing values when applying
relocations. mold was broken from the point of view that bits must be
cleared before applying relcoations. But at the same time, we had code
to clear bits for some types of relocations.

This patch removes all bit clear code for the sake of simplicity.
2023-01-02 05:17:15 +08:00
Rui Ueyama 608b328cbf Reduce symbol size 2023-01-01 12:12:34 +08:00
Rui Ueyama cbc71ac363 Clear existing values when applying relocations 2023-01-01 12:12:22 +08:00
Rui Ueyama df9e997cc3 Refactor 2023-01-01 07:40:55 +08:00
Rui Ueyama abe7943c06 Refactor 2023-01-01 07:11:23 +08:00
Rui Ueyama 764f802180 Fix CI 2022-12-31 20:28:01 +08:00
Rui Ueyama 651adad5f2 Support 32-bit PowerPC ABI 2022-12-31 17:56:37 +08:00
Rui Ueyama 34bfbc0983 Rename inttypes.h integers.h
`inttypes.h` unintentionally collided with the system's `inttypes.h`.

Fixes https://github.com/rui314/mold/issues/936
2022-12-30 21:46:05 +08:00
Rui Ueyama 9c355b7765 Treat R_ARM_PLT32 as a synonym for R_ARM_CALL
According to the ARM psABI document, R_ARM_PLT32 is deprecated.
It looks like old compilers emit it for function calls.

Fixes https://github.com/rui314/mold/issues/934
2022-12-30 18:14:49 +08:00
Rui Ueyama ccd47db916 Set a correct value to __iplt_start for statically-linked binaries
Fixes https://github.com/rui314/mold/issues/930
2022-12-30 12:26:19 +08:00
Rui Ueyama ad30db1102 Refactor 2022-12-30 12:05:07 +08:00
Rui Ueyama b3db6c9976 Revert "Eliminate uses of <bit>"
This reverts commit 33af22848f.

https://github.com/rui314/mold/issues/936
2022-12-30 11:09:50 +08:00
Rui Ueyama 02ca6cc643 Do not mention --ld-path because -fuse-ld works both for clang and gcc 2022-12-30 10:53:10 +08:00
Rui Ueyama d5a1c7a1b3 Merge pull request #938 from arbipher/patch-2
Add explanation for `-fuse-ld-mold` in README
2022-12-30 11:44:44 +09:00
Weng Shiwei 翁士伟 13a4cf9994 Update README.md 2022-12-29 21:40:07 -05:00
Weng Shiwei 翁士伟 e63bf01704 Update README.md 2022-12-29 21:33:30 -05:00
Rui Ueyama f23c1c9b69 Fix CI 2022-12-30 10:14:24 +08:00
Weng Shiwei 翁士伟 e454d54a8a Update README.md 2022-12-29 20:53:44 -05:00
Rui Ueyama 33af22848f Eliminate uses of <bit>
Some old C++ compilers don't provide that header.

Fixes https://github.com/rui314/mold/issues/936
2022-12-30 09:16:38 +08:00
Rui Ueyama 610d90fa69 Simplify 2022-12-29 17:52:47 +08:00
Rui Ueyama 9c62c55a2c Rename a file 2022-12-29 17:39:39 +08:00
Rui Ueyama 34ca952a9b Refactor 2022-12-29 16:59:08 +08:00
Rui Ueyama 487ef64336 Remove dead code 2022-12-29 16:46:36 +08:00
Rui Ueyama f33968c782 Refactor 2022-12-29 16:20:40 +08:00
Rui Ueyama 7c03f5e4c0 Refactor 2022-12-29 15:49:12 +08:00
Rui Ueyama 6ff6084ab8 Refactor 2022-12-29 11:35:53 +08:00
Rui Ueyama 326e2c6ba1 Copy st_other from input symbols to output symbols 2022-12-29 11:35:53 +08:00
Rui Ueyama efe117d946 Merge pull request #933 from marxin/support-fno-use-linker-plugin
support correctly -fno-use-linker-plugin
2022-12-29 12:35:38 +09:00
Rui Ueyama b39ee6a985 Refactor 2022-12-29 09:30:59 +08:00
Martin Liska 5f99006de3 support correctly -fno-use-linker-plugin
Right now, mold linker does not work with -fno-use-linker-plugin
and thus use assembly for GCC FAT LTO objects. I noticed that
when running GCC test-suite with mold.

Fixes: 804b843983

Signed-off-by: Martin Liska <[email protected]>
2022-12-28 19:46:08 +01:00
Rui Ueyama 033d5f3406 Refactor 2022-12-28 22:35:26 +08:00
Rui Ueyama d96c9b8236 Do not set an undefined value to dynrel 2022-12-28 22:23:50 +08:00
Rui Ueyama a45f97b474 Refactor 2022-12-28 17:53:58 +08:00
Rui Ueyama 10a1ca0d8e Refactor 2022-12-28 17:44:57 +08:00
Rui Ueyama 4362727cc8 Refactor 2022-12-28 17:25:25 +08:00
Rui Ueyama 40ddfa533b Refactor 2022-12-28 15:11:05 +08:00
Rui Ueyama 447bb8b036 Update a comment 2022-12-28 14:05:39 +08:00
Rui Ueyama 6231510794 Support DEC Alpha 2022-12-28 13:57:00 +08:00
Rui Ueyama 352c086047 Refactor 2022-12-28 12:52:41 +08:00
Rui Ueyama fb1ca49d9b Update comments 2022-12-27 22:36:45 +08:00
Rui Ueyama 1b0979c795 Refactor 2022-12-27 21:26:57 +08:00
Rui Ueyama 5a38f70a37 Refactor 2022-12-27 18:16:31 +08:00
Rui Ueyama 4da942723e Refactor 2022-12-27 14:09:30 +08:00
Rui Ueyama e26ea4ba05 Refactor 2022-12-27 12:08:52 +08:00
Rui Ueyama 8cfa64944e Update comments 2022-12-27 11:25:06 +08:00
Rui Ueyama 21855a194c Attempt to fix CI 2022-12-27 10:46:29 +08:00
Rui Ueyama 3411e17d8b [ELF] Support SH-4 ISA 2022-12-27 10:34:20 +08:00
Rui Ueyama e1f587eafb Merge pull request #926 from ZhongRuoyu/test-permission
`warn-symbol-type.sh`: fix file permission
2022-12-26 19:16:55 +09:00
Ruoyu Zhong 215f351dcd warn-symbol-type.sh: fix file permission
Also, end this file with a newline.

Signed-off-by: Ruoyu Zhong <[email protected]>
2022-12-26 16:21:41 +08:00
Rui Ueyama a49a201695 Bump mold version to 1.8.0 2022-12-25 19:55:07 +08:00
Rui Ueyama e7d80bcf33 Remove a redundant if condition 2022-12-25 18:12:14 +08:00
Rui Ueyama feaf2632c5 Fix typo in command line parameter
There's no such option as `-z nodefs` in any linker. The correct option
ame is `-z undefs`.
2022-12-25 16:43:23 +08:00
Rui Ueyama b510588677 Support -z nopack-relative-relocs 2022-12-25 16:39:16 +08:00
Rui Ueyama aa51aea245 Merge pull request #925 from ishitatsuyuki/type-warn
[ELF] Warn on symbol type mismatches.
2022-12-25 16:22:32 +09:00
Tatsuyuki Ishi b70211ec08 [ELF] Warn on symbol type mismatches.
Linking symbols with differing types will almost always lead to a crash; warn to
make it easier for the user to figure out the cause.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-12-25 14:18:41 +08:00
Tatsuyuki Ishi 30f79a0d62 [ELF] Add formatter for st_type.
Will be used to print symbol type mismatch warnings.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-12-25 14:18:41 +08:00
Rui Ueyama a3a98ac5f5 Fix build failure on some targets
Fixes https://github.com/rui314/mold/issues/924
2022-12-25 11:47:31 +08:00
Rui Ueyama ed235f3db6 Add -z [no]dynamic-undefined-weak 2022-12-25 11:46:13 +08:00
Rui Ueyama ad79674ec5 Refactor 2022-12-25 08:31:33 +08:00
Rui Ueyama 2ab4175ac1 Delete an unused variable 2022-12-25 08:31:33 +08:00
Rui Ueyama 408bdc1e87 Refactor 2022-12-24 14:40:10 +08:00
Rui Ueyama 86e6fa17a5 Refactor 2022-12-24 12:30:05 +08:00
Rui Ueyama 57c5b9ea27 Refactor 2022-12-23 20:40:37 +08:00
Rui Ueyama 23fff53e94 Do not crash if there's a SHF_MERGE section with entsize == 0
It looks like the OCaml compiler creates such broken sections.
2022-12-23 20:40:37 +08:00
Rui Ueyama c829aed2df Merge pull request #923 from marxin/badges
Add badges to README.md
2022-12-23 17:27:44 +09:00
Martin Liska e53df1deda Add badges to README.md
Include GitHub CI and openSUSE OBS badges.

Fixes: #922

Signed-off-by: Martin Liska <[email protected]>
2022-12-23 09:23:56 +01:00
Rui Ueyama 1283061d2b Remove CI logging 2022-12-23 14:38:42 +08:00
Rui Ueyama 5e976dbf97 Add logging to CI 2022-12-23 11:36:45 +08:00
Rui Ueyama a86d94cee7 [ELF] Relax sequencial consistency memory stores 2022-12-23 11:22:48 +08:00
Rui Ueyama 365779fbe4 Refactor 2022-12-23 10:54:47 +08:00
Rui Ueyama 0c456efebb [ELF] Fix the --help message
Pointed out by justanotheranonymoususer

https://github.com/rui314/mold/commit/e2d7353213004f5eac0eeda372dac7cbbdb1cb28
2022-12-21 16:12:14 +08:00
Rui Ueyama e2d7353213 [ELF] Support --[no-]undefined-version
Fixes https://github.com/rui314/mold/issues/921
2022-12-21 12:50:45 +08:00
Rui Ueyama 864f9a0490 Deduplicate the help message
Fixes https://github.com/rui314/mold/issues/920
2022-12-20 16:12:27 +08:00
Rui Ueyama 262bf67b20 Make OutputFile::close faster 2022-12-17 12:13:00 +08:00
Rui Ueyama bffc887dae Merge pull request #918 from marxin/fix-typo 2022-12-16 05:59:28 -08:00
Martin Liska 262ef21f32 fix copy&pasted typo in elf/lto-unix.cc
Signed-off-by: Martin Liska <[email protected]>
2022-12-16 13:57:00 +01:00
Rui Ueyama 07d89116a8 [ELF] Make --wrap work with LTO
Fixes https://github.com/rui314/mold/issues/916
2022-12-16 13:06:00 +08:00
Rui Ueyama cc3e5fd1a8 Refactor 2022-12-16 10:38:01 +08:00
Rui Ueyama b2858d2680 [ELF] Allow to initialize a global variable with an ifunc function pointer
This is a tricky case, but if you initialize a global variable with a
pointer to an ifunc function like this:

  int bar() __attribute__((ifunc("resolve_bar")));
  void *bar2 = bar;

then we need to emit two dynamic relocations, one for `bar()` and the other
for `bar2`. This results in calling `resolve_bar` twice at runtime, but
there's no other way to initialize the function and the variable correctly.

Fixes https://github.com/rui314/mold/issues/915
2022-12-15 16:46:44 +08:00
Rui Ueyama 293a4ca425 Refactor 2022-12-15 16:46:24 +08:00
Rui Ueyama 22c9ec8cb6 [ELF] Ignore -z start-stop-visibility=hidden
https://github.com/rui314/mold/issues/826
2022-12-14 17:20:18 +08:00
Rui Ueyama a260630f48 Merge pull request #913 from ZhongRuoyu/openssl-libdir 2022-12-12 20:12:19 -08:00
Rui Ueyama 86d37b4720 Merge pull request #911 from ZhongRuoyu/ld-symlink 2022-12-12 18:19:24 -08:00
Rui Ueyama 099663aaae Merge pull request #912 from marxin/ignore-max-cache-size 2022-12-12 18:18:20 -08:00
Ruoyu Zhong 678ef917cc Dockerfile: fix OpenSSL libdir
In the current version of Docker image, OpenSSL libraries are installed
to `/usr/local/lib64`. It is not present in the default dynamic linker
search path, and thus results in the following error when libcrypto is
not statically-linked:

  mold: error while loading shared libraries: libcrypto.so.3: cannot
  open shared object file: No such file or directory

Fix that by overriding `libdir`, as suggested in
https://github.com/openssl/openssl/blob/master/INSTALL.md#libdir. Also,
run `ldconfig` to update dynamic linker cache.

Signed-off-by: Ruoyu Zhong <[email protected]>
2022-12-12 23:40:02 +08:00
Martin Liska d05e83e40b ignore BFD linker argument --max-cache-size=size
Signed-off-by: Martin Liska <[email protected]>
2022-12-12 15:10:14 +01:00
Ruoyu Zhong a18fe8bccf Install ld64.sold symlink
Signed-off-by: Ruoyu Zhong <[email protected]>
2022-12-12 21:27:25 +08:00
Ruoyu Zhong 56c56cc450 Fix ld.sold symlink installation
Fixes #910.

Signed-off-by: Ruoyu Zhong <[email protected]>
2022-12-12 21:25:32 +08:00
Rui Ueyama 998c7afb67 Do not run tests on macOS 2022-12-12 17:33:23 +08:00
Rui Ueyama a90e7ac782 Remove mold/macOS
mold/macOS will be available at https://github.com/bluewhalesystems/sold
as a source-available commercial product.
2022-12-12 17:05:09 +08:00
Rui Ueyama bd584ac05e Automatically detect if mold is sold 2022-12-12 16:00:59 +08:00
Rui Ueyama 30aecda799 Change the version string for commercial version of mold
Since many programs recognizes `^mold` as a check if a linker is mold,
we want to always print out "mold" as a product name.
2022-12-12 15:19:59 +08:00
Rui Ueyama 05ff76f361 [ELF][ARM32] Support R_ARM_GOTOFF32
Fixes https://github.com/rui314/mold/issues/909
2022-12-12 11:51:08 +08:00
Rui Ueyama 83d8189c51 Refactor 2022-12-11 18:04:16 +08:00
Rui Ueyama 7c56dddc8c [ELF] Cache DJB hash
We used to compute DJB hash twice.
2022-12-11 16:14:16 +08:00
Rui Ueyama ba8a4d9b98 Refactor 2022-12-11 13:44:29 +08:00
Rui Ueyama afe01a1367 Improve a comment 2022-12-10 18:13:47 +08:00
Rui Ueyama bcc74d68ee Simplify 2022-12-10 17:27:00 +08:00
Rui Ueyama a6c4530e43 Fix -Wunused-function 2022-12-10 16:03:25 +08:00
Rui Ueyama f83581880d Upgrade to mimalloc 2.0.7 2022-12-10 15:41:48 +08:00
Rui Ueyama 0d4193f564 Optimize our concurrent map 2022-12-10 14:24:21 +08:00
Rui Ueyama 88b4c1eb81 [ELF] Optimize scan_relocations 2022-12-10 14:06:08 +08:00
Rui Ueyama cd4fb9f26e Update man page
`-preload` has been removed.

https://github.com/rui314/mold/issues/414
2022-12-10 10:31:36 +08:00
Rui Ueyama 3e23192256 [Mach-O] Fix crash bug 2022-12-09 15:47:29 +08:00
Rui Ueyama 589cb55516 Do not create __objc_stubs etc. sections if they already exist 2022-12-09 15:30:24 +08:00
Rui Ueyama b14727149b [ELF] Allow a section symbol as a comdat group name
A symbol with `STT_SECTION` doesn't have name (i.e. its `st_name` is zero.)
If such symbol is used as a comdat group name, we need to use a section name
instead of a symbol name as a group name.
2022-12-09 13:53:26 +08:00
Rui Ueyama df8e2d859c Refactor 2022-12-09 13:04:21 +08:00
Rui Ueyama faa71c6f1c Merge pull request #908 from marxin/i386-plt-use-int3
[ELF][i386] Use int3 as padding in .plt and .plt.got entires
2022-12-08 19:38:47 -08:00
Martin Liska 3486f69fcf [ELF][i386] Use int3 as padding in .plt and .plt.got entires
Similarly to x86_64, use int3 (0xcc) as padding at the end of plt
and pltgot entries in order to increase security.

Signed-off-by: Martin Liska <[email protected]>
2022-12-08 15:01:47 +01:00
Rui Ueyama 0f36a40a44 [Mach-O] Create __objc_{methname,selrefs,stubs} sections
Recent versions of Xcode started creating `_objc_msgSend$foo` symbols,
and we need to synthesize the symbols and place them into `__objc_stubs`
section.
2022-12-08 17:33:37 +08:00
Rui Ueyama 73f32940ab [ELF] Parallelize HashSection::copy_buf and GnuHashSection::copy_buf
If there are lots of exported symbols, their performances matter.
2022-12-08 15:13:44 +08:00
Rui Ueyama 3d255b2cdf Merge pull request #907 from ishitatsuyuki/defsym-lto
[ELF] Keep a reference to the defsym target.
2022-12-07 22:02:55 -08:00
Tatsuyuki Ishi 27b543a725 [ELF] Keep a reference to the defsym target.
Add an undefined symbol to keep a reference to the defsym target. This prevents
elimination by e.g. LTO or gc-sections. A test is added for the case of LTO
dead-code elimination.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-12-08 12:05:09 +08:00
Tatsuyuki Ishi 79483332b7 [ELF] Only assign "owned" synthetic symbols to output section.
In preparation for introducing undefined symbols that references external object
files (and should not get the output section overridden).

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-12-08 11:57:42 +08:00
Rui Ueyama b09be697b5 Revert "Link against libc++ instead of libstdc++ if -DMOLD_MOSTLY_STATIC"
This reverts commit 94122f4ffd.

Fixes https://github.com/rui314/mold/issues/905
2022-12-07 14:39:20 +08:00
Rui Ueyama b7439b5efc [ELF] Do not remove SHF_LINK_ORDER bit
SHF_LINK_ORDER bit doesn't matter in an executable, but it looks like
the `strip` command in binutils 2.35 or earlier on ARM32 complains about
`.ARM.exidx` if we do not preserve that bit.

Fixes https://github.com/rui314/mold/issues/889
2022-12-06 13:52:38 +08:00
Rui Ueyama 8c87f16592 [ELF] Make --dynamic-list to search in -L locations
Fixes https://github.com/rui314/mold/issues/904
2022-12-06 13:35:16 +08:00
Rui Ueyama e16328e790 Refactor 2022-12-03 16:03:00 +08:00
Rui Ueyama bb5d96be6e Allow negative values for R_AARCH64_TLSLE_MOVW_TPREL_G[012] relocs 2022-12-03 11:44:52 +08:00
Rui Ueyama 3c1a0550da Search version script from library search paths
Fixes https://github.com/rui314/mold/issues/822
2022-12-03 10:34:37 +08:00
Rui Ueyama dd90ade29d Fix CI 2022-12-02 16:25:19 +08:00
Rui Ueyama 5866c9e95a [ELF] Do not create copy relocations if dynamic relocations suffice
We used to create copy relocations if we are creating a non-PIC executable
and there's a direct reference to imported data. That works for most
programs but not for GHC (Glasgow Haskell Compiler). GHC places function
machine code and its supplimental data next to each other in the .text
section. If we create a copy relocation for such function, the resulting
executable won't work because (1) copy-relocated data is not executable
and (2) it may separate the function code from its supplemental data.

In this patch, I made a change so that mold creates dynamic relocations
instead of copy relocations if possible.
2022-12-02 16:08:34 +08:00
Rui Ueyama 372f34300e Fix CI 2022-12-02 16:08:23 +08:00
Rui Ueyama 6cb5b53d5d [ELF] Handle escape character in response file
GHC uses backslash escapes instead of double-quoted strings in response files
2022-12-02 15:46:50 +08:00
Rui Ueyama 1d4b2071ca [ELF] Implement R_AARCH64_TLSLE_MOVW_TPREL relocations
Fixes https://github.com/rui314/mold/issues/901
2022-12-02 12:23:31 +08:00
Rui Ueyama f0774d4e24 Merge pull request #893 from ishitatsuyuki/tls
[ELF] TLS alignment fix
2022-11-30 01:16:06 -08:00
Rui Ueyama f007127ad3 Merge pull request #899 from marxin/fix-typos-v2
fix typos in comments and help string
2022-11-30 01:12:27 -08:00
Rui Ueyama 38c1211dba [ELF] Optimize scan_relocations 2022-11-30 17:02:55 +08:00
Tatsuyuki Ishi bd46edf3f0 [ELF] Align the first TLS section with largest required TLS alignment.
TLS chunks alignments are special: in addition to having their virtual
addresses aligned, they also have to be aligned when the region of
tls_begin is copied to a new thread's storage area. In other words, their
offset against tls_begin also has to be aligned.

A good way to achieve this is to take the largest alignment requirement of
all TLS sections and make tls_begin also aligned to that.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-11-30 16:12:49 +08:00
Rui Ueyama 48b2c0f8a3 Make test less verbose 2022-11-29 14:36:57 +08:00
Rui Ueyama 8975fc2ed2 Allow a double-quoted filename 2022-11-29 14:34:31 +08:00
Rui Ueyama 594beb67a2 Merge pull request #900 from marxin/gcc-fat-lto-objects
support -ffat-lto-objects for GCC compiler
2022-11-28 17:53:01 -08:00
Martin Liska 804b843983 support -ffat-lto-objects for GCC compiler
FAT LTO objects are objects that contain both native assembly
and LTO bytecode. Identify such files as GCC_LTO_OBJ.

Signed-off-by: Martin Liska <[email protected]>
2022-11-28 17:00:13 +01:00
Martin Liska 032a44626f fix typos in comments and help string
Signed-off-by: Martin Liska <[email protected]>
2022-11-28 15:24:27 +01:00
Rui Ueyama d2cfffc24d Add a test 2022-11-28 17:39:13 +08:00
Rui Ueyama c2a0ae1c96 Add --relocatable-merge-sections 2022-11-28 17:35:06 +08:00
Rui Ueyama cf8cce6d17 -r: Infer machine type even if all input files are given via linker script 2022-11-28 17:35:06 +08:00
Rui Ueyama 0420a527c0 Add a workaround for a bad GHC output 2022-11-28 14:22:07 +08:00
Rui Ueyama 54e45746cc Merge pull request #896 from hack3ric/main 2022-11-27 04:42:09 -08:00
Eric Long c0c863ff8b Fix atomic checks
Signed-off-by: Eric Long <[email protected]>
2022-11-27 18:13:22 +08:00
Rui Ueyama e08efab1e7 Refactor 2022-11-27 12:39:47 +08:00
Rui Ueyama 41bc68e26d -r: Set SHF_INFO_LINK to relocation sections 2022-11-27 12:31:46 +08:00
Rui Ueyama 8f682da608 Refactor 2022-11-27 12:07:56 +08:00
Rui Ueyama 72ac3f200d Refactor 2022-11-27 12:04:48 +08:00
Rui Ueyama 32dcda5aa9 -r: fix relocation addends for section fragments 2022-11-27 12:04:48 +08:00
Rui Ueyama 26e3203e9a Merge pull request #895 from justanotheranonymoususer/patch-1
Fix typos in execstack.md
2022-11-26 04:26:16 -08:00
Rui Ueyama 1969b52569 Refactor 2022-11-26 20:10:26 +08:00
justanotheranonymoususer 53a84a182c Fix typos in execstack.md 2022-11-26 13:48:17 +02:00
Rui Ueyama c1b57d9977 -r: Do not strip .note.GNU-stack 2022-11-26 16:40:16 +08:00
Rui Ueyama 556a5cabf9 -r: Do not emit .eh_frame if there's no input .eh_frame 2022-11-26 16:33:32 +08:00
Rui Ueyama 3a4166a066 Avoid creating two sections with the same name 2022-11-26 13:43:23 +08:00
Rui Ueyama 85c3467147 Optimize 2022-11-26 12:54:22 +08:00
Rui Ueyama 7f124d4271 Optimize 2022-11-26 12:23:52 +08:00
Rui Ueyama 8d2ddf7038 Optimize 2022-11-26 12:15:34 +08:00
Rui Ueyama 3d0bd4dacf Silence TSAN error 2022-11-26 12:03:12 +08:00
Rui Ueyama d5bbe347a1 Accept -perf with -r 2022-11-26 11:29:39 +08:00
Rui Ueyama f9c721f9a9 Handle an output file with more than 65535 sections
Executables and shared libraries usually don't have too many sections,
but relocatable object files may. So we need this for --relocatable.
2022-11-25 19:38:09 +08:00
Rui Ueyama fc16eb4e34 Remove dead code 2022-11-25 16:45:07 +08:00
Rui Ueyama 683583a8c4 Fix CI 2022-11-25 15:47:39 +08:00
Rui Ueyama 6524389621 Generalize a test 2022-11-25 15:30:19 +08:00
Rui Ueyama 75e059af5b Optimize create_output_sections
It turned out that tbb::concurrent_hash_map is much slower than
std::unordered_map with a reader-writer lock for this function.
2022-11-25 15:17:47 +08:00
Rui Ueyama ecd61a4fed [ELF] Create output sections lazily
Previously, we create output sections when input sections are instantiated.
That is not ideal because input sections may be removed, and if that's the
case, any work that has done for the removed section is wasted.
2022-11-25 13:46:38 +08:00
Rui Ueyama 8614fbbaff Refactor 2022-11-25 11:06:18 +08:00
Rui Ueyama e1445e7a84 Merge pull request #892 from marxin/fix-typos-in-man 2022-11-24 17:22:04 -08:00
Martin Liska be01de4b45 man: fixes a couple of typos
Signed-off-by: Martin Liska <[email protected]>
2022-11-24 15:01:40 +01:00
Rui Ueyama 968a8cf0be Merge pull request #891 from marxin/comment-note.gnu.property
Document unsupported .note.gnu.property properties
2022-11-24 05:24:25 -08:00
Martin Liska e5e3925d5b Document unsupported .note.gnu.property properties
Mention 2 properties that are not suported right now.

Signed-off-by: Martin Liska <[email protected]>
2022-11-24 13:54:13 +01:00
Rui Ueyama 57b09ccfb7 [ELF] Merge .note.gnu.property if --relocatable is given 2022-11-24 17:24:38 +08:00
Rui Ueyama 50d0cc6959 Refactor 2022-11-24 17:16:33 +08:00
Rui Ueyama f43c0fde26 Fix CI 2022-11-24 17:08:45 +08:00
Rui Ueyama 3c4dd61710 Make NotePropertySection::update_shdr idempotent 2022-11-24 16:58:47 +08:00
Rui Ueyama ed8580f86a Refactor 2022-11-24 16:49:39 +08:00
Rui Ueyama b2f44b07fc Refactor 2022-11-24 15:12:18 +08:00
Rui Ueyama 1ccf1f8ffb Relocatable output file shouldn't have an executable bit 2022-11-24 15:06:52 +08:00
Rui Ueyama 2e3e177a6d Refactor 2022-11-24 15:02:02 +08:00
Rui Ueyama 5e345b0f82 Fix CI 2022-11-24 14:36:37 +08:00
Rui Ueyama 6fd2c69926 Add comments 2022-11-24 14:24:50 +08:00
Rui Ueyama f8e7bace50 Fix CI 2022-11-24 14:00:49 +08:00
Rui Ueyama 46def3b49f 80 columns 2022-11-24 13:35:10 +08:00
Rui Ueyama c9a7ae733c [ELF] Re-implement --relocatable
This is an initial patch to reimplement --relocatable. Unlike the previous
implementation, the new one aims to be on par with GNU ld in terms of the
behavior. Previously, we didn't for example merge input sections at all.
Now we do it in the new implementation.

The previous implementation was slow. We also want to fix it in the new one.

The new --relocation feature is not complete yet; there are many rough edges.
But I think this patch is good enough to be submitted.

This work is funded by Mercury (https://mercury.com).
2022-11-24 12:24:57 +08:00
Rui Ueyama 9d437dfd6f Merge pull request #885 from marxin/fix-psabi-link
Add i386-psabi.pdf to arch-x86-64
2022-11-21 22:09:53 -08:00
Rui Ueyama 5d277c1b90 Merge pull request #887 from ZhongRuoyu/cmake-CMP0077
Work around CMake CMP0077 warning
2022-11-21 22:02:31 -08:00
Rui Ueyama a8b070bc56 Update README 2022-11-22 13:06:35 +08:00
Rui Ueyama 6eb6a93ced Merge pull request #886 from CraneDancingShape/pr_nim.md
Add README.md "If you are using Nim" & Fix "If you are using Rust" mi…
2022-11-21 21:05:28 -08:00
Zhong Ruoyu 89ebf2179c Work around CMake CMP0077 warning
As documented by CMake [^1], prior to CMake 3.13, entries set by
`option()` can override a non-cached old value. The `CMakeLists.txt` in
the bundled mimalloc specifies a minimum CMake version of 3.0, so that
old behaviour is expected. That means no matter whatever value of
`MI_BUILD_STATIC` we set in `mold_add_mimalloc` does not really take
effect (this can be seen by adding some debug messages). That didn't
trouble us because it's on by default anyway.

While the documented solution is to set `CMAKE_POLICY_DEFAULT_CMP0077`
to `NEW`, we could actually also work around this by making them cache
variables. This is fine and valid because their values are not meant to
be changed anyway. (And this workaround is already being used in
`mold_add_tbb`, so by doing this, we are also eliminating some
inconcistency.)

Fixes #874.

[^1]: See `cmake --help-policy CMP0077` or
https://cmake.org/cmake/help/latest/policy/CMP0077.html

Signed-off-by: Zhong Ruoyu <[email protected]>
2022-11-22 01:03:08 +08:00
CraneDancingShape 22635e3716 Add README.md "If you are using Nim" & Fix "If you are using Rust" miss-spell. 2022-11-21 23:30:28 +09:00
Martin Liska 2ae49688e8 Add i386-psabi.pdf to arch-x86-64
Some parts (like .note.gnu.property section format)
of the ABI are defined in i386 manual.
2022-11-21 15:18:52 +01:00
Rui Ueyama 988241975f Merge pull request #877 from sicherha/fix_span_out_of_bounds
Fix out-of-bounds error on aarch64 with `_GLIBCXX_ASSERTIONS` enabled…
2022-11-19 20:31:19 -08:00
Rui Ueyama f5fb6b4abb Merge pull request #880 from ZhongRuoyu/dockerfile-cleanup
Dockerfile: perform cleanup at the end
2022-11-19 18:57:01 -08:00
Rui Ueyama ada8dec8da Fix -Wdangling-reference
Fixes https://github.com/rui314/mold/issues/882
2022-11-20 10:55:29 +08:00
Rui Ueyama a18316a83a Merge pull request #881 from tzussman/patch-1
Update CMakeLists.txt to use mold 1.7.1
2022-11-19 18:54:39 -08:00
Tal Zussman 3f81358b54 Update CMakeLists.txt to use mold 1.7.1 2022-11-19 12:55:20 -05:00
Zhong Ruoyu ce3ac5d2f4 Dockerfile: perform cleanup at the end
As recommended in Docker's documentation [^1]:

> In addition, when you clean up the apt cache by removing
> `/var/lib/apt/lists` it reduces the image size, since the apt cache
> is not stored in a layer.

This reduces the image size by ~0.5GiB.

[^1]: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#apt-get

Signed-off-by: Zhong Ruoyu <[email protected]>
2022-11-20 01:29:16 +08:00
Christoph Erhardt a2c7ad822b Fix out-of-bounds error on aarch64 with _GLIBCXX_ASSERTIONS enabled (again)
The square-bracket operator of `std::span` has undefined behaviour for
out-of-bounds element accesses. Resorting to iterators yields defined
behaviour.

This issue (originally described in #298) had been fixed in the past by
commit 03ba8f9325, but it has since
resurfaced with commit 193c245781.

Signed-off-by: Christoph Erhardt <[email protected]>
2022-11-19 14:01:52 +01:00
Rui Ueyama 72fffee54f Merge pull request #875 from Paulo-21/patch-1
Update "compile mold" command to use mold 1.7.1
2022-11-19 03:29:34 -08:00
Pmimo c77c41eaf8 Update compile mold command to 1.7.1 2022-11-19 11:27:50 +01:00
Rui Ueyama d1a0e2c2c5 Refactor 2022-11-19 17:57:12 +08:00
Rui Ueyama bbb03c9096 Update README 2022-11-19 12:27:49 +08:00
Rui Ueyama 0b9f4b0e45 Update README 2022-11-19 12:04:02 +08:00
Rui Ueyama f689d25798 Update sponsor list 2022-11-19 11:34:06 +08:00
Rui Ueyama dae1edc578 [ELF] Remove duplicate rpaths
Fixes https://github.com/rui314/mold/issues/873
2022-11-19 11:08:07 +08:00
Rui Ueyama df00fe8a58 Remove "commercial/free edition" from the version message
Commercial version will get a different brand name ("sold") and
maintained in a separate repository than this repo.
2022-11-18 17:21:46 +08:00
Rui Ueyama 428218ce30 Merge pull request #867 from ishitatsuyuki/property
[ELF] Add support for more .gnu.note.property variants.
2022-11-17 23:04:26 -08:00
Rui Ueyama 20eaa1d19c Revert "[ELF] Do not include non-memory-mapped sections into build-id computation"
This reverts commit e228abfd5a because
it makes the linker to produce different executables with the same
build-id.

Fixes https://github.com/rui314/mold/issues/872
2022-11-18 14:45:16 +08:00
Tatsuyuki Ishi d30d743cb0 [ELF] Add support for more .gnu.note.property variants.
This makes .gnu.note.property handling generic instead of hardcoded against the
GNU_PROPERTY_X86_FEATURE_1_AND value.
The behavior is based on specification inside GNU gold header comments;
unfortunately a formal document does not exist yet and the comments seem to be
the best reference that can be found.

The support for the actual properties in wild seems scarce; neither gcc nor
clang emits the correct property even when passed -march=x86-64-v3 and using
__builtin_popcnt, for example. Therefore, this does not include a test yet for
the new properties.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-11-18 11:18:13 +09:00
Tatsuyuki Ishi 14a6e2012d [ELF] Update .note.gnu.property constants.
Includes value required to handle the "ABI needed" and "ABI used" tags, but
doesn't include the feature values yet (because we don't have a use for it).

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-11-18 10:38:59 +09:00
Tatsuyuki Ishi 94fb470e29 [ELF] Workaround binutils injecting own .note.gnu.property section.
binutils has learned to inject some "x86 ISA needed/used" properties.
Try to disable their emission with -mx86-used-note=no if supported, since
now that mold is gaining support for these properties, they will cause an
mismatch with the expectations.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-11-18 10:38:59 +09:00
Rui Ueyama 968e970adb Merge pull request #868 from ZhongRuoyu/fix-empty-prefix
Fix symlinks when `CMAKE_INSTALL_PREFIX` is `/` or empty
2022-11-17 16:50:41 -08:00
Zhong Ruoyu 2d0a38436c Tweak symlink installation
This removes the extra slash in the symlink
installation output. It works because both
NEW_ABS and NEW_DIR are absolute paths, and
therefore always begin with a slash.

Signed-off-by: Zhong Ruoyu <[email protected]>
2022-11-18 00:42:00 +08:00
Zhong Ruoyu 9513f63a29 Fix symlinks when CMAKE_INSTALL_PREFIX is / or empty
As suggested by https://github.com/rui314/mold/pull/846#issuecomment-1318000326,
if `CMAKE_INSTALL_PREFIX` is set to `/` or empty,
then `mold_install_relative_symlink` would see an
empty `CMAKE_INSTALL_PREFIX` and fail. Appending
an additional slash takes care of that.

See #868.

Signed-off-by: Zhong Ruoyu <[email protected]>
2022-11-18 00:39:56 +08:00
Rui Ueyama 8b373d38ce Emit REL-type relocations for --emit-relocs no i386 and ARM32
Previously, we always emit RELA-type relocations for --emit-relocs.
Now, we emit relocation tables in the target's native relocation format.
2022-11-16 16:39:19 +08:00
Rui Ueyama 456d34bde3 Fix .eh_frame relocations we create for --emit-relocs 2022-11-16 14:22:51 +08:00
Rui Ueyama 622c2d479c [ELF] Make -emit-relocs to create a .rela.eh_frame section 2022-11-16 13:56:34 +08:00
Rui Ueyama d835d016e0 [ELF] Preserve SHF_MERGE and SHF_STRING bits
Even though section flags don't matter at runtime, we want to preserve
these flags for `-r`.
2022-11-16 11:53:39 +08:00
Rui Ueyama 700a475597 Refactor 2022-11-16 11:45:09 +08:00
Rui Ueyama c2a34cc1e3 Refactor 2022-11-15 15:36:15 +08:00
Rui Ueyama 1f67ccfa20 Show license information in the --help message 2022-11-13 20:25:14 +08:00
Rui Ueyama 2b7b45c8d1 Automatically download the latest repo files 2022-11-13 19:17:16 +08:00
Rui Ueyama c9803c8984 Merge pull request #859 from ZhongRuoyu/fix-test-permission
`test/elf/section-order.sh`: fix file permission
2022-11-13 01:24:44 -08:00
Zhong Ruoyu c55931dae5 test/elf/section-order.sh: fix file permission
Signed-off-by: Zhong Ruoyu <[email protected]>
2022-11-13 16:52:59 +08:00
Rui Ueyama 94122f4ffd Link against libc++ instead of libstdc++ if -DMOLD_MOSTLY_STATIC 2022-11-13 16:31:00 +08:00
Rui Ueyama 22f3b7ed24 Bump mold version to 1.7.0 2022-11-13 08:14:59 +08:00
Rui Ueyama a67e9f54f8 Attempt to unbreak CI 2022-11-12 13:51:48 +08:00
Rui Ueyama 63933a9221 Retry if pthread_create fails with spurious error
https://github.com/oneapi-src/oneTBB/commit/137c1a88b690acf3525e0f279720ac489ce66481
is not included in 2021.7.0, so we still need to apply it separately.
2022-11-10 17:54:25 +08:00
Rui Ueyama e25802f84f Upgrade vendored OneTBB to version 2021.7.0
Fixes https://github.com/rui314/mold/issues/666
2022-11-10 17:42:51 +08:00
Rui Ueyama 73a467a3c9 Simplify 2022-11-10 14:16:55 +08:00
Rui Ueyama 91378b9c2f Simplify 2022-11-10 14:05:34 +08:00
Rui Ueyama 7f2a2c4233 Merge pull request #854 from ZhongRuoyu/git-packed-refs
Allow `update-git-hash.cmake` to get packed refs
2022-11-09 04:49:26 -08:00
Zhong Ruoyu ddd5264abd Allow update-git-hash.cmake to get packed refs
`git pack-refs` (often called by `git gc`) may
move refs from `.git/refs` to
`.git/packed-refs`. If that is the case, a lookup
in `.git/refs` will fail.

Signed-off-by: Zhong Ruoyu <[email protected]>
2022-11-09 19:07:30 +08:00
Rui Ueyama 5797a59ab9 Merge pull request #853 from ishitatsuyuki/cpp-version-precedence
[ELF] Fix precedence with C++ version script rules.
2022-11-09 00:52:50 -08:00
Tatsuyuki Ishi 9875150677 [ELF] Fix precedence with C++ version script rules.
Earlier entries in a version scripts always get the precedence. Don't let a
non-C++ symbol rule override a preceding C++ symbol rule.

MultiGlob is modified to return the necessary rule index information to the
caller. Since the rule index might not always be contiguous within a
MultiGlob instance (e.g. interleaving ordinary/C++ rules), the meaning of
MultiGlob's `val` is changed so that the minimum `val` itself is returned,
instead of the `val` with minimum index (the earliest added one).

MultiGlob is also used for the macOS backend, but since it does not care
about order yet the code is left as-is.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-11-09 14:56:06 +09:00
Rui Ueyama c9ff2a21c5 Fix -Wdeprecated-declarations 2022-11-07 15:13:17 +08:00
Rui Ueyama bd555b33ab Do not run tests if ARM32
I don't know why, but ctests fail only on ARM32 on Docker.
2022-11-06 15:55:40 +08:00
Rui Ueyama 51427cd7d2 Do not use mimalloc on 32-bit targets 2022-11-06 15:55:23 +08:00
Rui Ueyama 0e0bd8a4f0 80 columns 2022-11-06 14:40:20 +08:00
Rui Ueyama a8321acb88 Do not try to run executables created with --section-order
We can create peculiar executables with --section-order, and some ELF
loaders don't like some of them. Since the flag is intended to be used
for embedded/kernel programming, we don't need to verify that the
executables work on regular Linux macines.

Fixes https://github.com/rui314/mold/issues/838
2022-11-06 13:05:56 +08:00
Rui Ueyama 3127508040 Refactor 2022-11-05 21:06:52 +08:00
Rui Ueyama 4fc877779b Allow to use ccache 2022-11-05 12:41:21 +08:00
Rui Ueyama dae7dca0b9 80 columns 2022-11-04 17:00:41 +08:00
Rui Ueyama b43b31eae8 [ELF] Add --execute-only 2022-11-03 14:00:06 +08:00
Rui Ueyama bedcb1bf56 Merge pull request #848 from ZhongRuoyu/cmake-absolute-prefix
Fix symlinks for absolute `CMAKE_INSTALL_*`
2022-11-02 21:02:51 -07:00
Zhong Ruoyu 9fdb09ae0e Emit a message on each symlink installation
Signed-off-by: Zhong Ruoyu <[email protected]>
2022-11-02 19:19:00 +08:00
Zhong Ruoyu f1b50695e5 Fix symlinks for absolute CMAKE_INSTALL_*
Related: #738 #846

Signed-off-by: Zhong Ruoyu <[email protected]>
2022-11-02 19:18:59 +08:00
Rui Ueyama 9fcd6e965d Merge pull request #846 from ZhongRuoyu/cmake-install
Respect prefix set by `cmake --install`
2022-11-01 19:28:37 -07:00
Rui Ueyama 228b7464bf Use OpenSSL 3.0.7
We are using OpenSSL only to compute SHA-256 hashes, so we are unaffeted
by CVE-2022-3786 and CVE-2022-3602. But I think we still want to use the
newer version of OpenSSL to avoid unnecessary confusion.
2022-11-02 07:28:37 +08:00
Zhong Ruoyu ce87053c78 Respect prefix set by cmake --install
Currently, `CMAKE_INSTALL_FULL_{LIBEXEC,BIN,MAN}DIR`
are used for symlink creation. The problem with
that is it only works when the prefix is not
overridden by `cmake --install <build-dir> --prefix <prefix>`,
because their values are evaluated and hard-coded
into `cmake_install.cmake` during configuration
time, rather than installation time.

While it would be nice if we can get their
overridden values during installation, only
`CMAKE_INSTALL_PREFIX` reflects the overriden
value. (See "INSTALL A PROJECT", cmake(1)).

Therefore, the logic for symlink creation is
rewritten and extracted into a function named
`mold_install_symlink`. Now only `CMAKE_INSTALL_PREFIX`
is used during installation.

A new problem with this change is, if environment
variable `DESTDIR` is not set and `CMAKE_INSTALL_PREFIX`
is set to a relative path (which is perfectly
valid), a slash `/` would be incorrectly
prepended. To avoid that, and maintain
consistency with CMake's behaviour, the install
prefix is first turned into absolute path, stored
in `PREFIX_ABS`.

By the way, it seems okay to use the
`MAKE_DIRECTORY` and `CREATE_LINK` subcommands
under command `file`. So, it shouldn't be needed
to invoke a subprocess.

Signed-off-by: Zhong Ruoyu <[email protected]>
2022-11-02 02:42:16 +08:00
Rui Ueyama 554caeeb34 Refactor 2022-11-01 14:09:34 +08:00
Rui Ueyama 3bddfc5fd2 Refactor 2022-11-01 14:06:09 +08:00
Rui Ueyama 127758047c Merge pull request #845 from ishitatsuyuki/icf-opt1
[ELF] ICF optimizations and simplifications
2022-10-31 19:43:09 -07:00
Tatsuyuki Ishi 8449ead2a7 [ELF] icf: Add counter for eliminated sections.
We can already print them, but a counter is more handy when the
executable is large and dumping everything would be too much.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-11-01 11:10:36 +09:00
Tatsuyuki Ishi aab34e52cf [ELF] icf: Simplify sweeping logic
This isn't perf intensive at all but sweeping through the sections seems
to be the more obvious way compared to going through the symbol
indirections.

Also, reword the comments and timing name to better reflect what it's
doing.

FWIW, it's slightly (5ms) faster.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-11-01 11:10:36 +09:00
Tatsuyuki Ishi 8186b8a242 [ELF] icf: Use a bitmap to skip converged nodes faster.
A node converges if its descendants contains no cycles and its hash has reached
all its descendants. Previously we checked for convergence on each iteration by
comparing hashes, but this could be tracked more cheaply through a separate
bitset.

This improves cache efficiency and results in a ~50ms speed up for Chromium out
of 310ms for the ICF pass.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-11-01 11:10:36 +09:00
Rui Ueyama bd77bcdab4 Update comments 2022-11-01 09:18:52 +08:00
Rui Ueyama f82c0c0f83 Merge pull request #844 from LinuxUserGD/patch-1 2022-10-31 16:13:29 -07:00
Linux User 7607e3cbc2 Update CMakeLists.txt 2022-10-31 18:50:50 +01:00
Rui Ueyama 00acf3d2f6 Test whether we need -latomic or not
Based on https://github.com/MariaDB/server/commit/7de110af8c25697855ac4925b2c91f757b356655

Fixes https://github.com/rui314/mold/issues/843
2022-10-31 21:21:46 +08:00
Rui Ueyama 2942ba83d4 Merge pull request #841 from glaubitz/m68k-atomic-fix 2022-10-30 12:45:33 -07:00
John Paul Adrian Glaubitz 30bd1c26c8 [ELF][m68k] Linking against libatomic is required on m68k
Signed-off-by: John Paul Adrian Glaubitz <[email protected]>
2022-10-30 18:33:46 +01:00
Rui Ueyama ba640ceac6 [ELF] Add ppc64 to the supported emulations list 2022-10-30 17:07:51 +08:00
Rui Ueyama 097b872536 Add a link to the psABI document 2022-10-30 15:16:07 +08:00
Rui Ueyama ab5d04b670 Update README 2022-10-30 11:52:39 +08:00
Rui Ueyama 5fe60e3b6d [ELF][m68k] Add initial m68k support 2022-10-30 11:10:14 +08:00
Rui Ueyama e0ff005333 Attempt to fix build for SuSE
https://github.com/rui314/mold/issues/838
2022-10-29 19:38:12 +08:00
Rui Ueyama 98df612168 [ELF][x86-64] Allow PC32 after TLSGD/TLSLD
It looks like SuSE Leap's gcc11 produces such relocation.
2022-10-29 15:02:00 +08:00
Rui Ueyama 15979f9a10 [ELF] Change default for --section-order 2022-10-29 14:49:09 +08:00
Rui Ueyama 5218f73773 [ELF] Make it possible to define symbols with --section-order 2022-10-29 14:27:21 +08:00
Rui Ueyama 064ab23511 [ELF] Make it possible to specify an alignment with --section-order 2022-10-29 12:56:32 +08:00
Rui Ueyama 774e5c3bb6 [ELF] Set p_vaddr to non-loadable segments
It looks like the runtime doesn't care the non-loadable segments'
p_addr values, but `strip` does. `strip` corrupts an executable if
no value is set to p_vaddr.

Fixes https://github.com/rui314/mold/issues/839
2022-10-29 12:55:12 +08:00
Rui Ueyama 28598c7703 Merge pull request #837 from marxin/remove-unused-var 2022-10-28 05:11:10 -07:00
Martin Liska 91bcc51e60 fix -Wunused-variable warning
Fixes:
elf/passes.cc:1516:9: warning: unused variable 'type' [-Wunused-variable]
2022-10-28 13:49:46 +02:00
Rui Ueyama b5a4d98ef2 [ELF] Try to keep segment's paddr to be equal to its vaddr 2022-10-28 18:15:09 +08:00
Rui Ueyama b365d6ba1a [ELF] Define -z pack-relative-relocs as an alias for --pack-dyn-relocs=relr
This is for compatibility with GNU ld.
2022-10-28 16:25:33 +08:00
Rui Ueyama 2e9a4ccd10 [ELF] Do not handle .multiboot specially
Now that we have --section-order, we need to handle .multiboot in a
special manner.
2022-10-28 16:25:06 +08:00
Rui Ueyama 2fbdf1f982 Improve --section-order 2022-10-28 16:25:06 +08:00
Rui Ueyama 20173ad233 Merge pull request #835 from hwti/elf-linker-script-relative-sysroot
[ELF] Fix absolute paths handling in linker scripts
2022-10-27 18:37:25 -07:00
Rui Ueyama 602f423241 [ELF] Make --section-roder to recognize #ehdr and #phdr special names 2022-10-28 08:50:36 +08:00
Rui Ueyama 460b443736 [ELF] Add --section-order
This is an experimental flag.

Context:
https://docs.google.com/document/d/1bVRrKsB6iMGmay1odsmS44PyUzPXgQBH83gYGMY6LWM
2022-10-27 21:14:39 +08:00
Rui Ueyama 99b886f9c4 [ELF] Add --start-stop
This is an experimental flag and may be deleted in the future.

If --start-stop is given, mold creates __start_/__stop_ symbols for
sections whose names are not in C identifier style.
2022-10-27 16:14:20 +08:00
Rui Ueyama feb9cd5859 [ELF] Define __phys_{start,stop} symbols if --physical-image-base is given 2022-10-27 11:08:55 +08:00
Rui Ueyama c921959b81 [ELF] Add --physical-image-base
This is an experimental option and may be removed in the future.

Context:
https://docs.google.com/document/d/1bVRrKsB6iMGmay1odsmS44PyUzPXgQBH83gYGMY6LWM
2022-10-27 09:42:16 +08:00
Rui Ueyama 7816fd8249 [ELF] --section-align: validate alignment value 2022-10-27 09:00:54 +08:00
Rui Ueyama 1483ed9ac6 [ELF] Allow a decimal number as an argument for --section-align 2022-10-27 08:52:22 +08:00
Rui Ueyama 0cc131c065 Simplify 2022-10-27 08:44:39 +08:00
Rui Ueyama 2e11c57a6e Simplify 2022-10-27 08:36:51 +08:00
Rui Ueyama 67738238be [ELF] Add --section-align=<sectname>=<value> option
This is an experimental option and may be removed in the future.

Context:
https://docs.google.com/document/d/1bVRrKsB6iMGmay1odsmS44PyUzPXgQBH83gYGMY6LWM
2022-10-27 08:30:35 +08:00
Loïc Yhuel 467b08a782 [ELF] Fix absolute paths handling in linker scripts
With toolchains using a relative sysroot, like crosstool-ng, gcc/clang will call the linker
with something like "--sysroot=/path/bin/../machine/sysroot".
The is_in_sysroot detection wasn't working in this case.

Since to_abs_path() uses lexically_normal() for relative paths, it seems consistent to do
the same for absolute paths instead of modifying is_in_sysroot().
2022-10-26 19:57:28 +02:00
Rui Ueyama 903fff7701 Merge pull request #834 from ishitatsuyuki/dso-output-esym 2022-10-26 03:07:19 -07:00
Tatsuyuki Ishi 430818b366 [ELF] Use to_output_esym for symtab entries from DSOs.
DSO symtab handling was relatively simple, but it led to wrong entries when
copyrel was used.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-10-26 18:03:14 +08:00
Rui Ueyama dda70ce920 Merge pull request #832 from JamesWidman/opt-find_subsection
[Mach-O] Use binary search in find_subsection()
2022-10-25 23:38:14 -07:00
Rui Ueyama f7876a9f2a Merge pull request #833 from ishitatsuyuki/sec-reloc
[ELF] Fix section relocations getting bogus r_type.
2022-10-25 21:59:47 -07:00
Tatsuyuki Ishi 303cba23b5 [ELF] Fix section relocations getting bogus r_type.
They should inherit the original type instead of using a value from the wrong
enum (which showed up as R_X86_64_GOT32).

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-10-26 12:48:28 +08:00
James Widman 13eda8051a [Mach-O] Use binary search in find_subsection()
When using ld64.mold to link clang for Mach-O, most of the time was
spent in the first call to find_subsection() from
parse_compact_unwind(). Changing its linear search to a binary search
cut an 18-second link time down to about 3 seconds.

Signed-off-by: James Widman <[email protected]>
2022-10-25 18:07:25 -07:00
Rui Ueyama 0763dd5639 Improve error check 2022-10-25 16:52:13 +08:00
Rui Ueyama 95276c98fe 80 columns 2022-10-25 16:41:59 +08:00
Rui Ueyama 5a0ad2aa59 Show a warning on an invalid multiboot section 2022-10-25 16:41:14 +08:00
Rui Ueyama 4db7574e84 Place "multiboot" section right after the ELF header
This is an experimental feature to support Multiboot specification.

https://github.com/rui314/mold/issues/563
2022-10-25 10:01:12 +08:00
Rui Ueyama 997e53472d Merge pull request #819 from ishitatsuyuki/tsan-disable
Remove lock from clear_symbols and add a justification for disabling TSan
2022-10-24 03:10:51 -07:00
Tatsuyuki Ishi 3a308270bd Remove lock from clear_symbols and add a justification for disabling TSan
Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-10-24 17:40:06 +08:00
Rui Ueyama af6475caa7 Do not use hexdump command
hexdump is not universally available, while od seems to be defined by POSIX.
2022-10-24 16:19:01 +08:00
Rui Ueyama d0210b85b9 Run tests before install 2022-10-24 16:19:01 +08:00
Rui Ueyama 73cdbb320d Merge pull request #818 from ishitatsuyuki/opt
Some trivial optimizations
2022-10-24 00:37:47 -07:00
Tatsuyuki Ishi 6088d954f6 [ELF] Remove redundant lock in mark_live_objects.
There are no writes to `sym` apart from the fast_mark which is already atomic.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-10-24 14:54:57 +08:00
Tatsuyuki Ishi 20ca4e38f4 Use relaxed for update_{minimum,maximum}.
We only need the RMW to be atomic and doesn't need the ordering guarantee wrt
other memory locations.
Might make ARM a little faster (no machine code change for x86).

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-10-24 14:54:13 +08:00
Rui Ueyama 616f8b2205 Update comments 2022-10-24 13:54:39 +08:00
Rui Ueyama 4379dc4afa Prepare to distribute binary packages for arm, ppc64le and s390x 2022-10-24 13:54:39 +08:00
Rui Ueyama c5106da010 Remove unused file 2022-10-24 11:46:27 +08:00
Rui Ueyama 0d6e373ffc Refactor 2022-10-24 11:33:58 +08:00
Rui Ueyama 6361875a15 Revert "[ELF] Add --start-stop"
This reverts commit 1cfa4a93ae because
it was committed by accident.
2022-10-24 07:29:17 +08:00
Rui Ueyama 9c7970f0af Add a test 2022-10-24 07:16:27 +08:00
Rui Ueyama 59ea52933d [ELF] Honor section alignment when creating copy relocations 2022-10-24 06:58:03 +08:00
Rui Ueyama 4fa41e1ad4 Fix -Wdeprecated-declarations
Fixes https://github.com/rui314/mold/issues/816
2022-10-24 06:28:13 +08:00
Rui Ueyama ce35e19c6d Merge pull request #815 from polluks/patch-1 2022-10-23 12:39:57 -07:00
Stefan 8643520464 Fixed typos 2022-10-23 20:31:29 +02:00
Rui Ueyama d01deafff0 Merge pull request #810 from ishitatsuyuki/comdat-early 2022-10-23 05:24:48 -07:00
Rui Ueyama 8f74e48dfb Merge pull request #814 from ishitatsuyuki/fast-mark
Add an optimized mark function for faster GC passes.
2022-10-23 04:18:35 -07:00
Tatsuyuki Ishi 59a713fb4c Add an optimized mark function for faster GC passes.
Atomic RMW operations can take hundreds of cycles and tends to be massively
slower than a fence-free alternative.

Add a fast_mark function that optimistically tests if the node is already
visited first, which allows avoiding the fence penalty if we can early exit.

When testing on Chromium, this roughly improved the associated pass's runtime
by 20--30%.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-10-23 18:19:32 +08:00
Rui Ueyama 626619a8b0 Enable transparent huge pages for output file if THP is available 2022-10-23 18:17:47 +08:00
Rui Ueyama 585eae2030 Fix CI 2022-10-23 17:29:57 +08:00
Tatsuyuki Ishi 730e970e79 [ELF] Run COMDAT elimination inside resolve_symbols.
We've long had a hack to de-prioritize GNU_UNIQUE symbols, due to an edge case
where these symbols could get higher resolution priority yet eliminated in the
later COMDAT elimination stage. But since GNU_UNIQUE symbols were supposed to
have a resolution as strong as GLOBAL symbols, it created unsoundness in the
resolution and could have cause false duplicate symbol errors in later stages.

The root cause is that COMDAT elimination, unlike ICF and SHF_MERGE, removes
symbols from the candidate pool and can actually affect resolution result. Hence
we ought to do the elimination before the final resolution.

Doing so requires fully wiping the resolution instead of just the ones from
eliminated archive members. This has a performance hit that we need to keep an
eye on.

On another note this wiping of resolution is actually justifiable. The old
algorithm did not handle the case of an archive member overriding a DSO symbol.
So wiping is the correct way even in absence of COMDAT elimination.

One caveat is that currently the symbol resolution stage doesn't actually care
about the is_alive flag which is how COMDAT symbols are killed. There are two
other users of this flag: mergeable sections, and eh_frame. The eh_frame section
is already handled with special logic, and we can treat it as if it had no
symbols to resolve. Mergeable sections needs a bit more careful handling, but a
good way is to just leave them unmerged and update the references in the same
later pass where merge happens. This has been done in a prior commit.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-10-23 14:27:26 +08:00
Tatsuyuki Ishi 26c503ad30 [ELF] Defer initialize_mergeable_sections.
To allow symbol resolution to skip !is_alive sections.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-10-23 14:18:28 +08:00
Tatsuyuki Ishi 1a99724eca [ELF] Split out 1st pass of symbol resolution as finalize_archive_extraction.
This pass is rather different in that it runs the resolve pass with lower
priority for archive symbols in order to determine extraction. Split it out to
better convey its difference.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-10-23 13:53:03 +08:00
Tatsuyuki Ishi 03dad9ba14 [ELF] Simplify do_resolve_symbols.
We can skip the is_alive condition by doing the compaction beforehand.

This also allows splitting this function into two phases clearly which will
happen later.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-10-23 13:05:38 +08:00
Tatsuyuki Ishi e7a7f9e39a [ELF] Ensure all symbols from LTO objects are cleared.
Make sure clear_symbols() are called on IR object files too, so that we'll never
have references to symbols inside IR object files after LTO. Can't really think
of an example that causes problem, but for sanity.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-10-23 13:00:44 +08:00
Rui Ueyama e228abfd5a git [ELF] Do not include non-memory-mapped sections into build-id computation 2022-10-23 12:41:59 +08:00
Rui Ueyama 61f28d270c Fix CI 2022-10-23 12:24:58 +08:00
Rui Ueyama 9535e080e9 Simplify 2022-10-23 09:31:19 +08:00
Rui Ueyama f9750d163d Merge pull request #809 from sicherha/fix_SHN_XINDEX_out_of_bounds
Fix name lookup for section symbols when `st_shndx == SHN_XINDEX`
2022-10-22 18:15:55 -07:00
Christoph Erhardt f8cb32e59a Fix name lookup for section symbols when st_shndx == SHN_XINDEX
When the section-header index has the escape value `SHN_XINDEX`, the
actual index must be looked up in the separate `SHT_SYMTAB_SHNDX` table.
Trying to use `SHN_XINDEX` (= 0xffff) as an index results in an
out-of-bounds read. The error can be observed when running the
`x86_64_many-sections.sh` test on RHEL 8 or 9 (but not on Fedora,
because there the assembler doesn't emit section symbols).

Instead of using `st_shndx` directly, call the pre-existing helper
method `get_shndx()` to get the correct behaviour.

Signed-off-by: Christoph Erhardt <[email protected]>
2022-10-22 14:41:42 +02:00
Rui Ueyama 4508d93d08 Merge pull request #808 from ishitatsuyuki/refactor 2022-10-22 05:21:16 -07:00
Tatsuyuki Ishi fb4b67ae18 [ELF] Move ElfSym memset to inside of loop.
Should eliminate redundant assignments and improve memory locality.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-10-22 20:16:54 +09:00
Rui Ueyama 1e98ec0b38 Merge pull request #807 from ishitatsuyuki/symtab-garbage
[ELF] Fix uninitialized ElfSym paddings
2022-10-22 03:58:07 -07:00
Tatsuyuki Ishi 0b50ebda47 [ELF] Fix uninitialized ElfSym paddings
Designated initializer doesn't initialize the padding; make sure we memset
them by ourselves.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-10-22 19:37:35 +09:00
Rui Ueyama 5469f5ac1b Revert "Always enable assert()"
Turned out that what I wrote as a commit message for
800fcdc5d4 is wrong. mold is 5% slower
for linking clang with assertions enabled.
2022-10-22 16:24:22 +08:00
Rui Ueyama 068ce4654f Add a CI for a non-assert build
Even if we always enable assert(), we want code to be able to compile
with assert() being expanded to a null statement. I.e., we should not
write code that has side effect in assert().

In order to add a test case for it, I added a cmake option to force
disable assert().
2022-10-22 16:03:45 +08:00
Rui Ueyama 800fcdc5d4 Always enable assert()
Our assertions are cheap, and the cost of branch instructions that
are never taken is almost free thanks to hardware branch prediction.
So I don't think it's worth to disable it for release builds.
2022-10-22 15:21:08 +08:00
Rui Ueyama 0d7a5a7284 Create config.h 2022-10-22 14:40:47 +08:00
Rui Ueyama 7db72a871c Remove Makefile
It's been deprecated for some time.
2022-10-22 14:40:47 +08:00
Rui Ueyama 9842388cd0 Do not halt even if there's a big input text section 2022-10-22 13:24:49 +08:00
Rui Ueyama 29c4403ec4 Refactor 2022-10-22 13:03:25 +08:00
Rui Ueyama 74124636dc [ELF] Make less number of range extension thunks 2022-10-22 12:32:32 +08:00
Rui Ueyama 193c245781 Refactor 2022-10-22 12:22:57 +08:00
Rui Ueyama 5cdc761746 Print out the number of bytes occupied by range extension thunks 2022-10-22 11:01:27 +08:00
Rui Ueyama d35d3922f0 Fix typo
We set a value to .ARM.exidx's sh_shlink to appease GNU binutil's
strip command (this value is not used by runtime), but even strip
doesn't care what value is set! We set a wrong value by accident.

Fixes https://github.com/rui314/mold/issues/804
2022-10-22 08:24:00 +08:00
Rui Ueyama c4850273c7 [ELF] Handle GNU-unique symbols as if they were weak
https://github.com/rui314/mold/issues/524
2022-10-21 20:58:17 +08:00
Rui Ueyama 86f3130e44 [ELF] Remove the special logic for STB_GNU_UNIQUE
We've been treating STB_GNU_UNIQUE symbols as if they were weak.
The rationale of doing is this:

1. GCC sometimes creates GNU-unique symbols instead of weak ones
   for comdats, and

2. after COMDAT de-duplication, we used to report a duplicate symbol
   error for GNU-unique symbols

However, (2) is no longer the case because we do not report a symbol
duplication error if its section is dead.

I'm not sure if this new logic will work for all programs, but I want
to give it a shot.

https://github.com/rui314/mold/issues/524
2022-10-21 17:15:51 +08:00
Rui Ueyama 2dc1fecb02 Update comments 2022-10-21 17:15:51 +08:00
Rui Ueyama 33e3a75a79 Merge pull request #802 from ZhongRuoyu/disable-subprocess-cc-on-macos 2022-10-20 23:14:45 -07:00
Zhong Ruoyu 3fe46175f3 Do not build subprocess.cc on macOS
Signed-off-by: Zhong Ruoyu <[email protected]>
2022-10-21 12:44:27 +08:00
Rui Ueyama 861bccce72 [ELF] Do not emit non-memory-allocated sections if --oformat=binary 2022-10-21 08:24:15 +08:00
Rui Ueyama 60a75d0549 Fix CI 2022-10-21 07:46:49 +08:00
Rui Ueyama 610042a8e9 [ELF] Set .ARM.exidx's sh_link to .text
GNU binutil's strip command doesn't like an .ARM.exidx section with
sh_link set to 0. arm-linux-gnueabihf-strip actually crashes on my
machine when the command is run on such a file.

https://github.com/rui314/mold/issues/801
2022-10-21 07:11:52 +08:00
Rui Ueyama 1cfa4a93ae [ELF] Add --start-stop 2022-10-21 07:11:52 +08:00
Rui Ueyama 23905bba19 Merge pull request #800 from ZhongRuoyu/fix-mold-run-output 2022-10-20 11:07:42 -07:00
Zhong Ruoyu eb391f99cf Fix mold -run output on unsupported platforms
Signed-off-by: Zhong Ruoyu <[email protected]>
2022-10-21 00:29:37 +08:00
Rui Ueyama 78713299e3 Merge pull request #799 from ksco/fix-findnull
Fix a bug in find_null
2022-10-20 06:37:06 -07:00
Yang Liu 1f32a6daf6 Fix a bug in find_null
Signed-off-by: Yang Liu <[email protected]>
2022-10-20 21:14:09 +08:00
Rui Ueyama 6b00a6c2ca Merge pull request #798 from ishitatsuyuki/emit-relocs
[ELF] Fix --emit-relocs r_sym indices.
2022-10-20 05:34:58 -07:00
Tatsuyuki Ishi f18af9e2f4 [ELF] Fix --emit-relocs r_sym indices.
536e0e7d ("[elf] Align .symtab's ELF attributes with .dynsym's.") fixed the
issue of .dynsym and .symtab disagreeing with each other, but in the
process introduced change to the symtab index. --emit-relocs was assuming
the old assignment logic, which caused mismatches as soon as any
global-to-local demotion happened.

Fix this to use the demotion-aware assignment logic by directly filling in
the indices as we assign them to symtab, and by making get_output_sym_idx
to test global/local using the new is_local() helper.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-10-20 20:43:29 +09:00
Rui Ueyama 288cd5b400 [ELF][i386] Fix assertion failure
https://github.com/rui314/mold/issues/794
2022-10-19 20:14:58 +08:00
Rui Ueyama 48dd520014 Clarify and simplify the sign-off guideline
The more we write to this file, the more it is unclear what exatly
a contributor is agreeing to.
2022-10-19 20:13:01 +08:00
Rui Ueyama 2aa24949c7 [ELF][i386] Allow R_386_PC32 after R_386_TLS_{GD,LDM}
I don't know why GCC sometimes creates a PC32 relocation instead of
PLT32 after a TLS_GD/TLS_LDM. I believe it's strictly speaking a
violation of the psABI. But we need to handle such input.

Fixes https://github.com/rui314/mold/issues/794
2022-10-19 19:33:18 +08:00
Rui Ueyama 323ad30e25 Bump mold version to 1.6.0 2022-10-19 14:58:59 +08:00
Rui Ueyama 96f7b664e9 Fix CI 2022-10-19 14:38:22 +08:00
Rui Ueyama a137bc4111 Avoid reading /proc/self/exe on FreeBSD
Fixes https://github.com/rui314/mold/issues/792
2022-10-19 13:48:46 +08:00
Rui Ueyama 0f6a03a9ba Merge pull request #791 from ksco/simplify 2022-10-18 13:43:11 -07:00
Yang Liu 17cb7d8dce Simplify
Signed-off-by: Yang Liu <[email protected]>
2022-10-18 21:26:42 +08:00
Rui Ueyama 0df3a2bf7b Fix CI 2022-10-18 18:11:05 +08:00
Rui Ueyama d5c59fc919 Use strerror() instead of its thread-safe versions
Since errno_string is not in a performance-critical path, it's ok for
that function to be a bit inefficient.

https://github.com/rui314/mold/issues/790
2022-10-18 17:59:07 +08:00
Rui Ueyama 0364934636 Fix -Wunused-result
Fixes https://github.com/rui314/mold/issues/790
2022-10-18 17:20:10 +08:00
Rui Ueyama edd10419dd [ELF] Save a little bit space for copy relocations 2022-10-18 17:16:00 +08:00
Rui Ueyama c1c6749940 [ELF] Ignore .note.gnu.build-id in input object files 2022-10-18 16:31:04 +08:00
Rui Ueyama 750d536401 Update comments 2022-10-18 16:30:55 +08:00
Rui Ueyama e56a3751bf [ELF][s390x] Do not allow misaligned R_390_*DBL relocs 2022-10-18 16:28:52 +08:00
Rui Ueyama ad87ac28b7 Simplify 2022-10-18 13:05:35 +08:00
Rui Ueyama 7cb97e153a [ELF] Fix -repro 2022-10-18 12:51:14 +08:00
Rui Ueyama db4bcd4138 Untabify 2022-10-18 12:41:11 +08:00
Rui Ueyama 1493a5cfd3 Merge pull request #788 from akien-mga/armv7-no-latomic 2022-10-17 14:20:47 -07:00
Rémi Verschelde 04033a1b52 CMake: Only add -latomic for armv6, not armv7
This matches what was done in `Makefile` in #561, but the check was extended
in the CMake port.

It builds fine without `-latomic` on Mageia 9 armv7hl nodes, fails with
explicit `-latomic`.

Signed-off-by: Rémi Verschelde <[email protected]>
2022-10-17 18:09:31 +02:00
Rui Ueyama d2b7b04be3 [ELF] Do not allow DSO to keep other DSO
Previously, an undefined symbol in a DSO can mark other DSO as "needed".
But that lead to mark more DSOs needed as necessary. It is also a bit
counter-intuitive.

The new behavior is still slightly different from GNU ld, but I believe
it's now compatible with LLVM lld.

Fixes https://github.com/rui314/mold/issues/786
2022-10-17 20:59:20 +08:00
Rui Ueyama bc22065684 Merge pull request #787 from ishitatsuyuki/pch
Add precompiled headers for CMake builds.
2022-10-17 03:09:26 -07:00
Tatsuyuki Ishi bf2d6bda28 workflows: Disable PCH for the default configuration.
As stated in the comment, have at least one permutation that checks for missing
(implicit) header includes.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-10-17 17:03:32 +08:00
Tatsuyuki Ishi d1cc5844c6 Add precompiled headers for CMake builds.
Since we're having lots of arch-generic permutations, headers seems to be a good
place where potential perf improvement are possible.

The feature is gated against CMake 3.16.0 or later, which is when it was
implemented.

Measured on 3990X, with this change it takes:
ninja  1166.22s user 34.45s system 5378% cpu 22.322 total
While before it took:
ninja  1776.81s user 51.30s system 7172% cpu 25.489 total

So a 10% improvement for workstation users and even more if you don't.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-10-17 16:58:42 +08:00
Rui Ueyama c5e9c30ce8 Fix CI 2022-10-17 14:25:25 +08:00
Rui Ueyama 2fb054d4a5 Refactor 2022-10-17 14:19:48 +08:00
Rui Ueyama 8fb6fb295b Rgefactor 2022-10-17 12:00:09 +08:00
Rui Ueyama 341705e4a2 Merge pull request #783 from ishitatsuyuki/icf-followup 2022-10-16 05:45:14 -07:00
Tatsuyuki Ishi 53848be39f [ELF] Replace killed_by_icf with is_killed_by_icf().
One less bit of state and should be clearer how the property is computed.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-10-16 10:06:13 +08:00
Rui Ueyama b3ed06aa39 Merge pull request #782 from ishitatsuyuki/icf-shndx
[ELF] Assign the correct SHNDX to symbols killed by ICF.
2022-10-15 04:42:29 -07:00
Tatsuyuki Ishi 207d8535f8 [ELF] Clarify ICF related struct fields.
Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-10-15 18:10:29 +08:00
Tatsuyuki Ishi 519595c0c3 [ELF] Assign the correct SHNDX to symbols killed by ICF.
We have been setting the symbol value and size correctly, but the SHNDX was
erroneously set to UND, potentially causing undefined symbol errors. Fix this by
forwarding the appropriate value from the `leader`.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-10-15 18:10:03 +08:00
Rui Ueyama 916f8b196a Merge pull request #781 from marxin/use-checkout-v3
fix Github Actions warning:
2022-10-15 02:07:03 -07:00
Martin Liska 38b2843f84 fix Github Actions warning:
Node.js 12 actions are deprecated. For more information see:
https://github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12/
2022-10-15 10:24:18 +02:00
Rui Ueyama df371c392b [ELF] Add more relocation overflow checks 2022-10-15 14:07:21 +08:00
Rui Ueyama 5171c9164e Refactor 2022-10-14 19:49:41 +08:00
Rui Ueyama c74486172b Make compile faster 2022-10-14 19:25:50 +08:00
Rui Ueyama 8fb9cb8815 Refactor 2022-10-14 18:17:05 +08:00
Rui Ueyama dd4048b7c4 Simplify 2022-10-14 15:27:25 +08:00
Rui Ueyama d3dc5e31eb Fix CI 2022-10-14 15:10:07 +08:00
Rui Ueyama dcc0dff67d Encourage users to migrate to cmake 2022-10-14 14:36:25 +08:00
Rui Ueyama b1382fd5e6 Update documentation 2022-10-14 13:13:37 +08:00
Rui Ueyama f4ce278729 [ELF][S390X] Support more relocation types 2022-10-14 12:59:57 +08:00
Rui Ueyama 30dd63cc0a [ELF] Do not create .plt.got for ppc64/ppc64le 2022-10-14 12:09:24 +08:00
Rui Ueyama 2dbbc54c28 Merge pull request #776 from marxin/fix-2-warnings
fix 2 -Wextra warnings
2022-10-13 19:14:47 -07:00
Rui Ueyama 55552b1481 Update comments 2022-10-13 21:12:29 +08:00
Rui Ueyama 1e24ce1f50 Update man page and comments 2022-10-13 20:53:57 +08:00
Rui Ueyama 687bd8ab65 Update comments 2022-10-13 16:33:15 +08:00
Rui Ueyama 08f34e3d67 [ELF][S390X] Skip tests affected by a GCC bug 2022-10-13 16:22:28 +08:00
Rui Ueyama 83e0632935 Update README 2022-10-13 15:16:38 +08:00
Rui Ueyama 2e7a0ad5f4 Update README 2022-10-13 15:07:36 +08:00
Rui Ueyama 1c63e0577a Document CMake options
Fixes https://github.com/rui314/mold/issues/734
2022-10-13 14:59:02 +08:00
Rui Ueyama c45db5c659 Fix CI 2022-10-13 14:08:47 +08:00
Rui Ueyama 0d205aa5e8 [ELF] Do not create PREINIT .dynamic entries if not necessary
Fixes https://github.com/rui314/mold/issues/773
2022-10-13 12:00:16 +08:00
Rui Ueyama 501a9fb57d [ELF][S390X] Set GOT[0] to _DYNAMIC
s390x psABI requires GOT[0] to be set to the link-time value of _DYNAMIC.
It also defines that GOT[1] as reserved.

https://sourceware.org/bugzilla/show_bug.cgi?id=29662
2022-10-13 09:42:26 +08:00
Rui Ueyama 33d388cc48 [ELF][S390X] Do not set GOT[0] unless -static-pie
It looks like we don't have to conservatively set GOT[0] to .dynamic
on s390x. glibc's -static-pie depends on GOT[0], so we need to set for
it, though.
2022-10-12 16:41:37 +08:00
Rui Ueyama 5010c0e66a Fix SuSE builder 2022-10-12 14:23:35 +08:00
Rui Ueyama a484df6885 Refactor 2022-10-12 12:34:00 +08:00
Rui Ueyama ce12303229 Fix CI 2022-10-12 12:10:14 +08:00
Rui Ueyama fb046cc6ed Refactor 2022-10-12 12:10:14 +08:00
Rui Ueyama f172cada2f Refactor 2022-10-11 15:28:17 +08:00
Rui Ueyama 5a5d25895c [ELF] Attempt to fix CI 2022-10-11 15:03:44 +08:00
Rui Ueyama bdecfeef7f Refactor 2022-10-11 14:43:20 +08:00
Rui Ueyama 86fd0241e0 Update comments 2022-10-11 14:24:27 +08:00
Rui Ueyama c3e8fea746 Fix build and refactor 2022-10-11 14:00:46 +08:00
Rui Ueyama 336a7b89c3 Merge pull request #778 from toppa102/freebsd-fix
Fix /proc/self behaviour on FreeBSD
2022-10-10 22:56:17 -07:00
Rui Ueyama 0d368b44de [ELF] Fix -Wtype-limits
These relocations are defined as follows

  R_SPARC_GOTDATA_OP_HIX22    (G >> 10) ^ (G >> 31)
  R_SPARC_GOTDATA_OP_LOX10    (G & 0x3ff) | ((G >> 31) & 0x1c00)

where G is an offset into .got. Since .got is much smaller than 4 GiB,
(G >> 31) is always zero, so we can ignore that part.

https://docs.oracle.com/cd/E19120-01/open.solaris/819-0690/chapter6-24/index.html
2022-10-11 13:53:15 +08:00
Rui Ueyama 12ff7bb067 [ELF][PPC64V1] Enable QEMU tests on GitHub Actions
All tests now pass on my machine for ppc64v1 except a few disabled ones.
2022-10-11 13:24:24 +08:00
Rui Ueyama 305f0f801e [ELF][PPC64V1] Fix more tests 2022-10-11 12:46:56 +08:00
toppa102 e11aa75543 Fixed /proc/self behaviour on FreeBSD 2022-10-10 22:28:34 +03:00
Martin Liska 2d79d1f65c fix 2 -Wextra warnings
Fixes:
macho/input-files.cc:310:31: warning: missing initializer for member ‘mold::macho::Subsection<mold::macho::ARM64>::replacer’ [-Wmissing-field-initializers]
elf/mold-wrapper.c:68:15: warning: passing argument 1 of ‘debug_print’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]

Signed-off-by: Martin Liska <[email protected]>
2022-10-10 17:42:05 +02:00
Rui Ueyama e4c682b645 [ELF][ARM32] Support R_ARM_THM_JUMP19
Fixes https://github.com/rui314/mold/issues/774
2022-10-10 12:55:06 +08:00
Rui Ueyama cc3d9a274e Merge pull request #772 from ishitatsuyuki/issue-606
[ELF] Rework version_specified handling.
2022-10-09 19:32:33 -07:00
Rui Ueyama 2d5c9e0ff7 Refactor 2022-10-10 10:19:11 +08:00
Rui Ueyama 1a58a0a6d4 Merge pull request #771 from marxin/fix-compiler-warnings
fix compiler warnings
2022-10-09 19:18:03 -07:00
Tatsuyuki Ishi 2e099a12a7 [ELF] Rework version_specified handling.
We should only override the default export behavior in executable if the default
version was actually overridden by a linker script (i.e. local: *;). Other
options, such as --dynamic-list and --export-dynamic-symbol[-list], doesn't
actually change this default, and should not be considered for this override.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-10-09 21:23:30 +08:00
Martin Liska 8352edfe94 fix compiler warnings
Fixes:

```
elf/arch-ppc64v1.cc:278:9: warning: unused variable 'loc' [-Wunused-variable]
elf/arch-ppc64v1.cc:285:10: warning: variable 'check' set but not used [-Wunused-but-set-variable]
elf/output-chunks.cc:1400:32: warning: unused variable 'sym' [-Wunused-variable]
```

Signed-off-by: Martin Liska <[email protected]>
2022-10-09 14:22:47 +02:00
Rui Ueyama 87f0262ce8 Refactor 2022-10-09 12:43:40 +08:00
Rui Ueyama 3b987874f4 Update comments 2022-10-09 12:32:21 +08:00
Rui Ueyama 4d9de802fc Update README 2022-10-09 11:33:17 +08:00
Rui Ueyama 6102ed531c Merge pull request #769 from AlexTMjugador/main
README.md: mention that installing clang is not needed for Rust when using the latest GCC
2022-10-08 20:13:08 -07:00
Rui Ueyama c9ec169d50 Merge pull request #770 from tosch42/spell
Fix spelling of "unknown"
2022-10-08 20:12:16 -07:00
Rui Ueyama 944d0fd3f6 [ELF][PPC64V1] Add initial PPC64 big-endian ELFv1 ABI
Since around 2015, IBM seems to have been encouraging their customers to
use PowerPC systems in little-endian mode. They ratified the little-
endian ABI for PPC64. Since it was completely a binary-compatibility-
breaking change anyway, they didn't only change the endianness but made a
few improvements to the ABI they have been wanting to make. So the new ABI
was born.

The new ABI is called the ELFv2 ABI. The original, big-endian-only one
is retrospectively called ELFv1. Technically, the new ABI supports both
little endiannesses. but effectively all systems that employ ELFv2 are
using the processor in the little-endian mode.

There are still many PowerPC systems that use the original ELF ABI in the
little-endian mode for binary compatibility. This commit is for such systems.

We already support ELFv2. Look at arch-ppc64v2.cc.

Currently, "73% tests passed, 63 tests failed out of 234" for the ppc64v1
target according to ctest.
2022-10-09 11:09:50 +08:00
Tom Schwindl 88e275f2b1 Fix spelling of "unknown"
Signed-off-by: Tom Schwindl <[email protected]>
2022-10-08 20:24:11 +02:00
Alejandro González d91ebb6c0c README.md: mention that installing clang is not needed for Rust when using the latest GCC
As explained in the "A classic way to use mold" section, the latest GCC releases support `mold` out of the box. Rust can take advantage of this to use `mold` too, without going through a system-wide `clang` linker.

I've just tested this on my Linux workstation that runs Debian unstable.
2022-10-08 19:40:30 +02:00
Rui Ueyama e48b7e767b Refactor 2022-10-08 09:49:45 +08:00
Rui Ueyama 8d0f3ee849 [ELF][S390X] Set _DYNAMIC to GOT[0]
https://github.com/rui314/mold/issues/767
2022-10-08 09:34:43 +08:00
Rui Ueyama fc66759444 [ELF] Set the .dynamic address to the first entry of .got
glibc for ARM64 prior to August 2021 arguably wrongly assumes that
the GOT first entry contains the address of the .dynamic section.
That's been removed in the following commit, but without satisfying
that assumption, mold can't create working static position-independent
execuables for ARM64.

https://sourceware.org/git/?p=glibc.git;h=43d06ed218fc8be58987bdfd00e21e5720f0b862
2022-10-08 09:06:28 +08:00
Rui Ueyama 8bedc4d7e7 Refactor 2022-10-08 07:15:21 +08:00
Rui Ueyama c2baf10d6b Disable static PIE tests on ARM64
They are failing only on GitHub Actions. I believe they don't work
with older versions of glibc. Until we fix them, I want to disable
them so that the tests are green.
2022-10-08 06:59:01 +08:00
Rui Ueyama 5dc906dc6b [ELF][S390] TLSLE computes an offset from TP, so we don't need a GOT entry for it 2022-10-08 06:55:53 +08:00
Rui Ueyama 8ea3316c7f Refactor 2022-10-08 06:55:53 +08:00
Rui Ueyama 5ff581b363 Merge pull request #766 from marxin/tests-use-skip
tests: use skip function call when possible
2022-10-06 01:10:46 -07:00
Rui Ueyama 7778ef7fa3 Merge pull request #765 from marxin/machine-test
tests: add missing MACHINE check in x86_64_large-bss.sh
2022-10-06 01:10:20 -07:00
Martin Liska ec4c1ae981 tests: use skip function call when possible
Signed-off-by: Martin Liska <[email protected]>
2022-10-06 09:53:05 +02:00
Martin Liska 1906d210ab tests: add missing MACHINE check in x86_64_large-bss.sh
Signed-off-by: Martin Liska <[email protected]>
2022-10-06 09:49:59 +02:00
Rui Ueyama 864d91569d Enable more -static-pie tests for ARM64 and i386 2022-10-06 15:32:22 +08:00
Rui Ueyama c05e2691c0 Merge pull request #764 from marxin/cleanup-test
remove extra OK message from a test
2022-10-06 00:24:25 -07:00
Rui Ueyama d3ab0c732c Fix CI 2022-10-06 15:23:18 +08:00
Rui Ueyama bd16377bd8 [ELF][i386] Implement R_386_GOT32X relaxation 2022-10-06 15:20:17 +08:00
Martin Liska c38bf979ce remove extra OK message from a test
Signed-off-by: Martin Liska <[email protected]>
2022-10-06 09:12:57 +02:00
Rui Ueyama 8251c0d467 Enable tests that depend on zstdcat, gdb and dwarfdump 2022-10-06 14:06:18 +08:00
Rui Ueyama 52fc5877f7 Refactor 2022-10-06 13:15:55 +08:00
Rui Ueyama e1275942c8 Factor out common piece of code to common.inc 2022-10-06 12:07:31 +08:00
Rui Ueyama 9e4e81b061 Fix -Wnarrowing on i386
FIxes https://github.com/rui314/mold/issues/763
2022-10-06 09:36:56 +08:00
Rui Ueyama c7f181c30b Rename S390 S390X
s390 technically means the 32-bit ESA/390 ISA, so s390x is more precise.
2022-10-06 09:34:30 +08:00
Rui Ueyama 03721f9abe Update comments 2022-10-05 22:31:31 +08:00
Rui Ueyama a79a6059e2 Refactor 2022-10-05 21:22:56 +08:00
Rui Ueyama 1145900c51 Refactor 2022-10-05 21:13:17 +08:00
Rui Ueyama 6bbf82f3b0 Refactor 2022-10-05 20:52:02 +08:00
Rui Ueyama 7aae0bf7ba [ELF][S390] Implement TLS GD -> LE relaxation 2022-10-05 20:31:38 +08:00
Rui Ueyama 2b0ab0e974 [ELF][S390] Fix linker-synthesized __tls_get_offset
__tls_get_offset is supposed to return an offset from TP. Previously,
we instead returned an offset from the beginning of the TLS block.
2022-10-05 18:08:49 +08:00
Rui Ueyama 4bf21122e1 Refactor 2022-10-05 16:21:24 +08:00
Rui Ueyama b6b6c178b4 Fix comments 2022-10-05 15:48:28 +08:00
Rui Ueyama 5eb81b4e00 Refactor 2022-10-05 15:43:56 +08:00
Rui Ueyama f5aeeeb01d Add comments 2022-10-05 15:20:42 +08:00
Rui Ueyama 1efc14d45d [ELF][S390] Allow R_390_TLS_LDO64 in non-memory-allocated sections 2022-10-05 14:27:36 +08:00
Rui Ueyama da5d4380cf Merge pull request #759 from ishitatsuyuki/docs
[elf] Update push/pop-state documentation.
2022-10-04 23:13:43 -07:00
Tatsuyuki Ishi 9d873f555e [elf] Update push/pop-state documentation.
Closes: https://github.com/rui314/mold/issues/755
Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-10-05 14:03:44 +08:00
Rui Ueyama a39086fa3b Fix CI 2022-10-05 13:45:01 +08:00
Rui Ueyama ffac38ae9e [ELF][S390] Support more relocations, enabling tests
It seems all tests pass with this change, so I'm enabling them.
2022-10-05 13:02:47 +08:00
Rui Ueyama 182db15da7 [ELF] Add initial s390x support
The implementation is far from complete, but mold/s390 is already able
to dynamically link a "Hello World" program for s390 which can
successfully run on qemu-s390x.
2022-10-05 10:13:21 +08:00
Rui Ueyama 4801234609 Refactor 2022-10-05 10:06:53 +08:00
Rui Ueyama 6ea03e6433 [ELF] Use $ instead of @ for lihnker-synthesized symbols
gdb recognizes "@" as an operator, so it's better to use a different symbol.
2022-10-04 16:46:21 +08:00
Rui Ueyama 5000a5f05d Merge pull request #754 from ZhongRuoyu/dist-fail-early
dist.sh: do not proceed when `cmake --build` fails
2022-10-04 00:34:51 -07:00
Zhong Ruoyu 89f8990dde dist.sh: do not proceed when cmake --build fails
Signed-off-by: Zhong Ruoyu <[email protected]>
2022-10-04 14:11:49 +08:00
Rui Ueyama e5adf2fe49 [ELF][i386] Make PLT entries to start with ENDBR32
Unlike GNU ld, we do not create .plt.sec section.

%ecx is defined as a scratch register, so we can use it in our PLT entries.
2022-10-04 13:07:36 +08:00
Rui Ueyama ed0996a590 Refactor 2022-10-04 10:04:08 +08:00
Rui Ueyama f19c242901 Fix CI 2022-10-03 23:16:28 +08:00
Rui Ueyama 443a9c6d2d Fix CI 2022-10-03 23:00:15 +08:00
Rui Ueyama d414f8ff6b Attempt to fix CI 2022-10-03 22:29:14 +08:00
Rui Ueyama 1205b1c18b [ELF] Do not truncate section size to 2^32
Fixes https://github.com/rui314/mold/issues/753
2022-10-03 21:55:28 +08:00
Rui Ueyama 1b86708683 [ELF] Resurrect .plt.got
This reverts commit ef04befcba and
6c964df459.

Even though these commits are not technically wrong, they caused a
regression on ARM32 and PPC64LE. It is because glibc cannot handle
IRELATIVE relocations in .rela.plt on these targets. Compare the
following two source source files:

  https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/x86_64/dl-machine.h;h=005d089501fa78654e16103de9ec901af7be4ff2;hb=HEAD#l530

  https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/arm/dl-machine.h;h=b0521c15496b9967a1c00a0ca7214bad2337be14;hb=HEAD#l464

ARM doesn't handle IRELATIVE for some reason, though x86-64 does.
I guess this is glibc's bug, but apparently we can't create IRELATIVE
relocations in .rela.plt. So we need to create it in .rela.dyn.
2022-10-03 21:45:15 +08:00
Rui Ueyama ef04befcba [ELF] Do not emit .plt.got
If a symbol has both PLT and GOT entries, we used to create its PLT
entry in .plt.got so that the runtime needs to look up the symbol
exactly once. This optimization however doesn't seem to be useful
because the number of such symbols is usually very small (<100 for
a large program). So I don't think its worth its complexity.
2022-10-03 17:21:45 +08:00
Rui Ueyama 6c964df459 [ELF] Do not always create a GOT entry for IFUNC 2022-10-03 17:07:43 +08:00
Rui Ueyama c74c001891 Merge pull request #749 from erneestoc/bundle-loader-bug
Makes xcode tests run
2022-10-02 22:52:24 -07:00
Rui Ueyama 324658fe1c Rename a function 2022-10-03 13:04:46 +08:00
Rui Ueyama c89d742be6 Refactor 2022-10-03 13:04:46 +08:00
Rui Ueyama 0f611697e0 Merge pull request #751 from rafalh/patch-1 2022-10-02 08:14:52 -07:00
Rafał Harabień 850627e7c9 Add Linux Mint support in install-build-deps.sh
Should fix following error on Linux Mint 21:
```
Error: don't know anything about build dependencies on linuxmint-21
```
2022-10-02 16:30:30 +02:00
Rui Ueyama c4e8fab106 Fix CI 2022-10-02 17:08:35 +08:00
Rui Ueyama a7f1e20594 [ELF] Emit local symbols for .got, .plt and .plt.got
These symbols are purely informational. They are not used for symbol
resolution. But they are useful for making disassembly readable.
2022-10-02 16:51:45 +08:00
Ernesto Cambuston 48ee718f23 Makes xcode tests run, Signed-off-by: Ernesto Cambuston <[email protected]> 2022-10-02 00:13:47 -07:00
Rui Ueyama ae91e487df Do not include .sh in test name 2022-10-02 14:44:07 +08:00
Rui Ueyama 1709ad5996 [ELF] Do not create a PLT entry for IFUNC if -fno-plt 2022-10-02 14:26:33 +08:00
Rui Ueyama 424f43772a Refactor 2022-10-02 13:59:59 +08:00
Rui Ueyama 3663b60074 [ELF] Place a section header at the very end of a file
Conventionally, a section header is placed at the very end of a file.
It's not technically required to do so, though.
2022-10-02 13:01:25 +08:00
Rui Ueyama 3c123cfe9d [ELF] Always set alignment 1 to PT_GNU_RELRO and PT_GNU_STACK
Alignment values are not significant for these types of segments,
so I don't want to set them to meainingful values.
2022-10-02 12:42:48 +08:00
Rui Ueyama 0fa4cff086 [ELF] Fix thread-pointer calculation
TLS segments were always page-aligned before 9c7b95f108,
so we didn't notice, but our expressions to compute TP and TLS offset
were wrong.
2022-10-02 11:41:04 +08:00
Rui Ueyama 2f5366f764 Refactor 2022-10-01 16:16:28 +08:00
Rui Ueyama 92c2c9bc8f Refactor 2022-10-01 16:13:52 +08:00
Rui Ueyama 19db4bbe13 [ELF] Create symbols for range extension thunks 2022-10-01 15:52:59 +08:00
Rui Ueyama 0929788ccc Refactor 2022-10-01 11:43:26 +08:00
Rui Ueyama fb4c21a65a Raise the support level of SPARC64 to match other targets 2022-10-01 11:22:40 +08:00
Rui Ueyama b36562fc94 Show skipped tests
https://github.com/rui314/mold/pull/745
2022-10-01 10:57:43 +08:00
Rui Ueyama 5cf3ad6fe7 Handle MOLD_ENABLE_QEMU_TESTS 2022-10-01 10:57:04 +08:00
Rui Ueyama 930964ab38 [ELF] Do not run target-specific tests on unintended targets
This commit reduces the number of tests that are invoked only to
print out the "skipped" message.
2022-10-01 10:29:40 +08:00
Rui Ueyama 56de92f65f Fix CI 2022-10-01 10:25:25 +08:00
Rui Ueyama 7d8bf817e9 [ELF] Reorganize test files 2022-09-30 15:59:53 +08:00
Rui Ueyama 438d67bcf5 Refactor 2022-09-30 12:30:32 +08:00
Rui Ueyama 020863f238 Merge pull request #740 from SupervisedThinking/up_mold_binary_install_path
CMakeLists: allow custom mold binary install path
2022-09-29 19:35:10 -07:00
Rui Ueyama 322ce8eb9d [ELF] Do not create an unnecessary gap before an executable segment 2022-09-30 10:24:56 +08:00
Rui Ueyama 57f2099a36 Revert "Detect skipped tests"
This reverts commit 313629e1af because
it made `ctest` to print out too many lines after the test summary
report for skipped test names.
2022-09-30 10:20:50 +08:00
Rui Ueyama 2fcfb9b470 [ELF] Do not allocate disk space for .relro_padding
With this change, there should be no file size penalty on specifying
a large value to `-z max-page-size`.
2022-09-30 10:18:55 +08:00
Rui Ueyama 8ef01f87b1 Merge pull request #743 from sicherha/detect_skipped_tests 2022-09-29 18:23:39 -07:00
Rui Ueyama 5eed32bcf2 [ELF] Do not discard .gnu.warning sections
.gnu.warning* section may contain strings referenced by other sections,
so we can't discard them unconditionally. In this commit, we simply
keep all .gnu.warning* sections.
2022-09-30 08:57:23 +08:00
Rui Ueyama c7767dd36d Merge pull request #744 from lkr0n/fix_gnu_warn_segfault
only ignore .gnu.warning. sections if emit_relocs is true
2022-09-29 16:02:58 -07:00
Louis Kronberg 5b147db113 only ignore .gnu.warning. sections if emit_relocs is true
This attempts to fix Issue #567 where mold would segfault
trying to emit relocations for .gnu.warning. sections.
2022-09-29 16:52:23 +02:00
Christoph Erhardt 313629e1af Detect skipped tests
This allows CTest to differentiate between tests that were executed
successfully and tests that were skipped.

Signed-off-by: Christoph Erhardt <[email protected]>
2022-09-29 16:43:17 +02:00
Rui Ueyama 260182d439 Fix CI 2022-09-29 21:52:32 +08:00
Rui Ueyama 458f88b6cf Fix CI 2022-09-29 21:00:58 +08:00
Rui Ueyama 34ee49f3de [ELF] Add a padding at end of a PT_GNU_RELRO segment
We used to extend just a load segment containing RELRO sections.
strip command shrinks such segment so that it fits its contents.
So we need a dummy section for padding.

https://github.com/rui314/mold/issues/732
2022-09-29 20:02:49 +08:00
Rui Ueyama 5746060cde Refactor 2022-09-29 20:02:33 +08:00
SupervisedThinking f7f2ef6182 CMakeLists: allow custom mold binary install path
- https://cmake.org/cmake/help/latest/command/install.html
- ${CMAKE_INSTALL_BINDIR} defaults to bin if not set
2022-09-29 11:49:57 +02:00
Rui Ueyama 886dbe6b24 Refactor 2022-09-29 16:52:33 +08:00
Rui Ueyama d1d6c00e0c Improve a script 2022-09-29 15:53:08 +08:00
Rui Ueyama 3bf3709799 Add a missing dependency
We need `util-linux` package for `hexdump`
2022-09-29 15:46:06 +08:00
Rui Ueyama d504b24419 [ELF] Fix test for PPC64LE
`readelf --symbols` print out auxiliary information after a symbol name
for PPC64, so something like `foo$` didn't match.

     7: 0000000000000000    80 FUNC    GLOBAL DEFAULT    3 main [<localentry>: 8]
     8: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND .TOC.
     9: 0000000000000000    72 FUNC    GLOBAL DEFAULT   11 foo  [<localentry>: 8]
    10: 0000000000000000    40 FUNC    GLOBAL DEFAULT   19 bar

Fixes https://github.com/rui314/mold/issues/739
2022-09-29 15:43:18 +08:00
Rui Ueyama b8ab2fd016 Refactor 2022-09-29 15:04:23 +08:00
Rui Ueyama a5448f2a48 Merge pull request #738 from jpalus/cmake-absolute-install-paths
cmake: use unambiguously absolute paths for install directories
2022-09-29 00:02:36 -07:00
Rui Ueyama e541ad3811 [ELF] Do not try to merge RELRO with a RO segment
Because of db415be21c, a RELRO segment
no longer follow a read-only data segment. So this optimization doesn't
do anything.
2022-09-29 14:08:58 +08:00
Rui Ueyama db415be21c [ELF] Place readonly data after ELF headers
They are both read-only, so by placing them next to each other, we can
reduce the number of the load segments by one, which translates to one
less mmap(2) at runtime.
2022-09-29 13:36:45 +08:00
Rui Ueyama 33de554f41 Revert "Revert recent memory layout changes"
This reverts commit e62de0b31f so that
we can try this for 1.5.2.
2022-09-29 13:22:03 +08:00
Rui Ueyama 6abf26ef38 [ELF] Sort loadable segments on p_vaddr 2022-09-29 11:47:04 +08:00
Rui Ueyama 1ed941f3f0 Bump mold version to 1.5.1 2022-09-28 17:33:54 +08:00
Rui Ueyama e62de0b31f Revert recent memory layout changes
This commit reverts the following commits:

- 3d33349691 [ELF] Fix -max-page-size
- 14b2ba3b74 Refactor
- 41c762b6c5 Update comments
- d22e85f345 [ELF] Save one memory page for RELRO
- 2941d75c71 [ELF] Do not page-align loadable segments
- a2552b6fd1 Refactor
- 919f58dc5d Fix CI
- ef6e6ecdf4 Refactor
- 9c7b95f108 [ELF] Do not page-align RELRO and TLS segments

I'll revert these changes because they caused a regression on systems
with large page size. Even though I believe most users are not
affected, it's a critical bug as mold creates completely unusable
output files for certain systems. In order to release 1.5.1, I'll
revert all of them.
2022-09-28 17:24:19 +08:00
Rui Ueyama 56b9ad9c62 Merge pull request #736 from sicherha/fix_test_failure_with_clang15
Disable PIE in `run-clang.sh` unit test
2022-09-27 19:57:34 -07:00
Jan Palus 9464a12960 cmake: use unambiguously absolute paths for install directories
as per https://cmake.org/cmake/help/latest/module/GNUInstallDirs.html
CMAKE_INSTALL_<dir> can be both relative and absolute. so far mold made
assumption these are relative only, but handle both cases by using
CMAKE_INSTALL_FULL_<dir> instead.

Signed-off-by: Jan Palus <[email protected]>
2022-09-27 23:48:07 +02:00
Christoph Erhardt 7aa87220c4 Disable PIE in run-clang.sh unit test
This fixes the following error when Clang 15 is installed on the system:
> mold: error: out/test/elf/x86_64/run-clang/a.o:(.text):
> R_X86_64_32 relocation at offset 0x5 against symbol `.rodata' can not
> be used; recompile with -fPIC

The error occurs because Clang 15 assumes PIE to be enabled by default:
https://releases.llvm.org/15.0.0/tools/clang/docs/ReleaseNotes.html#build-system-changes

Attempting to produce a position-independent executable from an object
file that was created by `cc` without `-fPIE` caused the linker to bail
out.

Signed-off-by: Christoph Erhardt <[email protected]>
2022-09-27 15:16:44 +02:00
Rui Ueyama 95ad47ca7d Bump mold version to 1.5.0 2022-09-27 14:23:31 +08:00
Rui Ueyama 9db16a87f5 Improve build script 2022-09-27 11:50:09 +08:00
Rui Ueyama 3d33349691 [ELF] Fix -max-page-size
Fixes https://github.com/rui314/mold/issues/732
2022-09-27 11:19:31 +08:00
Rui Ueyama 5fab142c73 Refactor 2022-09-27 07:44:36 +08:00
Rui Ueyama 0a4ec45949 Merge pull request #731 from marxin/remove-duplicate 2022-09-26 14:28:22 -07:00
Martin Liska c9fbc48a6c remove duplicate in CMakeLists.txt
Signed-off-by: Martin Liska <[email protected]>
2022-09-26 21:22:01 +02:00
Rui Ueyama 14b2ba3b74 Refactor 2022-09-26 22:24:01 +08:00
Rui Ueyama 41c762b6c5 Update comments 2022-09-26 21:26:08 +08:00
Rui Ueyama d22e85f345 [ELF] Save one memory page for RELRO 2022-09-26 21:08:07 +08:00
Rui Ueyama 07ecfbcfe1 Refactor 2022-09-26 17:36:20 +08:00
Rui Ueyama c623b4e3f0 [ELF] Handle .eh_frame_seg as a special symbol for .eh_frame
Looks like Intel C/C++ compiler emits this symbol in .eh_frame.
I don't excatly know how this symbol is being used, but it looks like
this symbol is supposed to refer the beginning of the output .eh_frame.

Fixes https://github.com/rui314/mold/issues/685
2022-09-26 16:15:31 +08:00
Rui Ueyama 747c205773 [ELF] Do not copy .llvm_addrsig to an output file 2022-09-26 14:17:30 +08:00
Rui Ueyama 2941d75c71 [ELF] Do not page-align loadable segments
Previously, we align each loadable segment and the beginning of
readable-writable non-RELRO segment to page boundaries. That's one
posible layout, but it made too many holes in files that resulted
in output file bloat.

That bloat is most visible on targets supporting large pages.
We conservatively link output files for the largest possible page
size so that the output will run both on large pages and usual small
pages. As an example, we use 64 KiB as a page size for ARM64.
So we wasted up to 64 KiB for each loadable segment. A "hello world"
program for exapmle became almost 200 KiB on ARM64 because of that
reason.

This commit changes the file layout. Loadable segments are no longer
aligned to page boundaries. Loadable segments are still placed into
different memory pages. A page at a boundary of two loadable segments
are doubly mapped to memory.

This change reduces output file size. It is most noticeable when an
output is small -- a "hello world" on ARM64 is now smaller than 10
KiB now.
2022-09-26 13:48:03 +08:00
Rui Ueyama a2552b6fd1 Refactor 2022-09-25 20:27:00 +08:00
Rui Ueyama 919f58dc5d Fix CI 2022-09-25 20:27:00 +08:00
Rui Ueyama ef6e6ecdf4 Refactor 2022-09-25 17:53:44 +08:00
Rui Ueyama 9c7b95f108 [ELF] Do not page-align RELRO and TLS segments
Previously, we always aligned RELRO and TLS segments to page boundaries.
Paddings for doing that bloated up resulting output files.
2022-09-25 17:42:59 +08:00
Rui Ueyama 6f6e0a6611 Refactor 2022-09-25 14:19:38 +08:00
Rui Ueyama 3c1425fe88 [ELF] Do not set W bit to PT_TLS
Since the actual writability is controlled by the W bit of the LOAD
segments, I don't think it actually matters. But in the spirit, TLS
segment contains the initial TLS image and should not be writable.
2022-09-24 17:47:54 +08:00
Rui Ueyama 101c64b427 Downgrade required CMake version
CMake 3.13 should be reasonably old enough. Ubuntu 20.04 includes
CMake 3.16.3. I wanted to support Ubuntu 18.04's CMake, but that was
3.10.2, and that's too old.
2022-09-24 13:34:55 +08:00
Rui Ueyama 622e18bd40 Fix CI 2022-09-24 09:18:26 +08:00
Rui Ueyama 652ab0c1da Fix CI 2022-09-24 08:15:25 +08:00
Rui Ueyama 64f6a89a0d Fix CI 2022-09-23 23:06:41 +08:00
Rui Ueyama b93ad52a73 Enable separate compilation for each source file and target pair
Almost all functions and data types are template in mold, which are
parameterized to target type (e.g. X86_64 or ARM32). I'm generally
happy with that design, but there's one drawback; as we add more
targets, compilation gets slower.

Now we support more than 10 targets, so the compiler has to do 10x
more work than it originall did. As a result, compiling output-chunks.cc
(one of our largest file) took more than 30 seconds to compile on a
Threadripper machine. I hasitated to add a support for a new target
because of this problem. We needed to fix it.

This commit tweaks the build system configs so that the build system
generates bunch of intermediate .cc files. Each generated .cc file
instantiates an original .cc file for one target.

The total amount of required computation doesn't change by this hack,
but now we can parallelize the compilation step. That works well on a
modern multi-core machine.

I generally don't like to tweak build system to workaround a tool's
issue (that's why I'm creating mold in the first place), but it looks
like there's no way to speed up compilation other than this. Or,
maybe we can write a faster C++ compiler someday, but that's another
topic.
2022-09-23 22:28:40 +08:00
Rui Ueyama 6b23821276 Fix build failure if MOLD_DEBUG_X86_64_ONLY is set 2022-09-23 11:45:37 +08:00
Rui Ueyama d37bc2eb2d Merge pull request #727 from marxin/enable-compress-debug-sections-zstd-for-GCC
Enable compress-debug-sections-zstd for GCC
2022-09-22 07:09:42 -07:00
Martin Liska b5f9d936da Enable compress-debug-sections-zstd for GCC
The compression option is directly passed to linker,
so GCC can handle it as well.

Signed-off-by: Martin Liska <[email protected]>
2022-09-22 16:06:16 +02:00
Rui Ueyama 97bf734b67 Merge pull request #726 from marxin/mold-V-alignment
fix output of mold -V
2022-09-22 06:27:34 -07:00
Martin Liska 38dfad1c6f fix output of mold -V
Add missing space and remove trailing newline.

Signed-off-by: Martin Liska <[email protected]>
2022-09-22 15:10:49 +02:00
Rui Ueyama 302a9ed8d8 Add missing targets and emulations 2022-09-22 19:53:01 +08:00
Rui Ueyama 264ca5aab4 Bug fix 2022-09-22 15:56:23 +08:00
Rui Ueyama b2ffaf9294 Refactor 2022-09-22 15:08:01 +08:00
Rui Ueyama a1e0ca8a7f Refactor 2022-09-22 14:15:02 +08:00
Rui Ueyama 54f50a7cb4 Refactor 2022-09-22 12:59:41 +08:00
Rui Ueyama ae53c4202a Refactor 2022-09-22 12:44:04 +08:00
Rui Ueyama 1dd2b9e662 Update comments 2022-09-22 12:05:59 +08:00
Rui Ueyama 4d02762fd9 Add a comment 2022-09-22 11:56:17 +08:00
Rui Ueyama 218038c7a4 [ELF][RISCV] Support big-endian RISC-V targets
These are experimental, but gcc already supports them with `-mbig-endian`.
2022-09-22 11:35:37 +08:00
Rui Ueyama c320f546fe Refactor 2022-09-22 09:01:59 +08:00
Rui Ueyama 068e52859b [ELF] Rename PPC64LE PPC64V2
The original PPC64 psABI was big-endian only which is retrospectively
called the ELF v1 ABI. Around 2016, IBM decided to push little-endian
use of PowerPC platform and ratified a new psABI which is called the
ELF v2 ABI.

Changing the endianness inevitably breaks ABI compatibility, so IBM
took that chance to clean up the ABI. So the difference between v1 and
v2 are not just endianness.

However, v2 isn't technically little-endian only. You can still create
an ELFv2-based big-endian system. Such systems are not popular though.

In many systems, the original ELFv1 big-endian system is called
"ppc64" and the v2 little-endian system is called "ppc64le". But these
names are not very accurate. I think ppc64v1 and ppc64v2 (or ppc64v2le
and ppc64v2be) are more accurate.
2022-09-21 21:23:24 +08:00
Rui Ueyama 615978a132 [ELF] Emit addend if both --pack-dyn-relocs and --no-apply-dynamic-relocs are given 2022-09-21 19:48:22 +08:00
Rui Ueyama 867c285332 [ELF] Allow more than 2^11 range extension thunks
It's unlikely that we have so many range extension thunks in a single
binary, but I don't think we need to save 4 bytes here.
2022-09-21 19:27:34 +08:00
Rui Ueyama d92cef9559 Attempt to fix ASAN issue
https://github.com/rui314/mold/issues/723
2022-09-21 18:05:55 +08:00
Rui Ueyama b67335fadd Merge pull request #722 from marxin/fix-Wreorder 2022-09-21 02:01:14 -07:00
Rui Ueyama 18a424dbc9 Merge pull request #724 from marxin/fix-i586-warning 2022-09-21 02:00:48 -07:00
Martin Liska d315ff2eff fix i686 ld warning
ld: warning: out/test/elf/i686/tls-module-base-i386/a.o:
missing .note.GNU-stack section implies executable stack

Signed-off-by: Martin Liska <[email protected]>
2022-09-21 10:17:48 +02:00
Martin Liska 5c275ce843 fix -Wreoder warning
Fixes:

elf/elf.h:2107:6: warning: ‘mold::elf::SparcEB64Rela::r_type’ will be initialized after [-Wreorder]
 2107 |   u8 r_type;
      |      ^~~~~~
elf/elf.h:2106:8: warning:   ‘mold::ub24 mold::elf::SparcEB64Rela::r_type_data’ [-Wreorder]
 2106 |   ub24 r_type_data; // SPARC-specific: used for R_SPARC_OLO10

Signed-off-by: Martin Liska <[email protected]>
2022-09-21 09:57:48 +02:00
Rui Ueyama 46f74fe7ed Refactor 2022-09-21 12:11:17 +08:00
Rui Ueyama 8e922416ee Do not build with mimalloc on i386
mimalloc on i386 seems to cause a runtime error on i386.

Fixes https://github.com/rui314/mold/issues/718
2022-09-21 11:06:32 +08:00
Rui Ueyama 24affd8ed3 Refactor 2022-09-21 10:18:48 +08:00
Rui Ueyama 0e4db53774 Update comments 2022-09-21 09:38:40 +08:00
Rui Ueyama 57442144ef [ELF][SPARC] Handle more relocations 2022-09-21 09:26:33 +08:00
Rui Ueyama 3f45f68a5d Enable SPARC64 tests on GitHub Actions 2022-09-21 08:49:55 +08:00
Rui Ueyama 3625c40f04 [ELF][SPARC] Disable gdb-index tests
These tests don't pass even with GNU ld on SPARC64. gdb doesn't seem
to be able to set a breakpoint to a function in an DSO for some resaon.
2022-09-21 08:06:01 +08:00
Rui Ueyama 9094e2eee1 [ELF][SPARC] Write little-endian values to .gdb_index 2022-09-21 08:04:29 +08:00
Rui Ueyama 5e8e35940a Refactor 2022-09-21 07:34:04 +08:00
Rui Ueyama 3400567fee Update comments 2022-09-20 23:04:07 +08:00
Rui Ueyama 91ce29d30c Update comments 2022-09-20 22:58:31 +08:00
Rui Ueyama 3564b71c4f Update comments 2022-09-20 22:52:15 +08:00
Rui Ueyama e9714be08f Refactor 2022-09-20 22:10:46 +08:00
Rui Ueyama 99d6478b35 Disable on SPARC64 2022-09-20 21:00:51 +08:00
Rui Ueyama 321d8208cb [ELF][SPARC] Make LTO plugin work 2022-09-20 21:00:18 +08:00
Rui Ueyama 76ab25d486 [ELF][SPARC64] Fix struct definition 2022-09-20 18:18:31 +08:00
Rui Ueyama 3b58bf52a7 [ELF][SPARC] Fix R_SPARC_OLO10 2022-09-20 17:44:05 +08:00
Rui Ueyama ce85848867 Add more constants 2022-09-20 17:20:33 +08:00
Rui Ueyama 9784632214 [ELF][SPARC] Fix endianness issue
Bitfields are placed in an address-increasing order on both little
and big-endian hosts. Therefore, the following struct has the same
layout on both endiannesses.

  struct {
    uint32_t x : 8;
    uint32_t y : 16;
  }

The address of `x` is smaller than `y`, though you can't take an
address of a bitfield using the `&` operator in C.

However, if two bitfields are placed the same byte, they are placed in
a reverse order. In LE, bitfields are placed from LSB to MSB, while in
BE, they are placed from MSB to LSB. Therefore, members something like
this

  struct {
    uint32_t x : 4;
    uint32_t y : 4;
  }

needs to be swapped.
2022-09-20 16:04:24 +08:00
Rui Ueyama e6625d0c86 Refactor 2022-09-20 16:03:59 +08:00
Rui Ueyama 1f53fd2adf Refactor 2022-09-20 14:18:29 +08:00
Rui Ueyama c0cd1f5d58 Revert "[ELF][SPARC] Enable SPARC64 tests" to unbreak CI
This reverts commit 8c4d65179f.
2022-09-20 14:09:56 +08:00
Rui Ueyama 8c4d65179f [ELF][SPARC] Enable SPARC64 tests 2022-09-20 13:50:45 +08:00
Rui Ueyama f1ccc05c3f [ELF][SPARC] Simplify TLS handling
__tls_get_addr is not available in statically-linked executables,
so we have been relaxing all TLSGD and TLSLD relocations for static
executables. However, I noticed that it's much easier to provide a
replacement function for __tls_get_addr instead of implementing all
TLS relaxations.

With this change, all mold's unit tests pass on qemu-sparc64.
2022-09-20 13:30:52 +08:00
Rui Ueyama 46c38ca738 [ELF][SPARC] Fix .eh_frame_hdr 2022-09-20 11:32:49 +08:00
Rui Ueyama 546c552644 Fix tests 2022-09-20 11:21:21 +08:00
Rui Ueyama a1a8058b3d [ELF][SPARC] Fix TLSLD relaxation 2022-09-20 11:17:59 +08:00
Rui Ueyama d8fa5343ec [ELF][SPARC] Fix TLSGD relaxation 2022-09-20 09:45:25 +08:00
Rui Ueyama 9dcc70f6ee [ELF][SPARC] Fix more test failures 2022-09-19 21:03:37 +08:00
Rui Ueyama 139aae9444 [ELF][SPARC64] Relax R_SPARC_GOTDATA relocations 2022-09-19 17:13:21 +08:00
Rui Ueyama 956433bd8e Refactor 2022-09-19 15:33:01 +08:00
Rui Ueyama 6952ba3953 [ELF][SPARC64] Relax TLSGD and TLSLD relocations 2022-09-19 15:16:31 +08:00
Rui Ueyama 0788013cd8 [ELF] Create symbols for relocations referring mergeable section symbols
Previously, a relocation is associated either to a symbol or a section
fragment. Now, we synthesize a symbol for each unnamed piece of section
fragment and associate it to a relocation.
2022-09-19 15:16:31 +08:00
Rui Ueyama 39847a4a27 Refactor 2022-09-19 12:07:21 +08:00
Rui Ueyama 6f6a814439 Move a struct member for better packing 2022-09-19 11:10:41 +08:00
Rui Ueyama a789e48452 [ELF] Use a tagged pointer to store the origin of a pointer
A symbol can belong to an input section, an output section or a section
fragment. They are mutually exclusive, and their data structures are
aligned to at least 4 bytes boundaries. So we can use a tagged pointer
to store all of them in a single pointer-size object.
2022-09-19 11:04:00 +08:00
Rui Ueyama 7b6db0308e Merge pull request #715 from marxin/sparc64-unused-code
sparc64: remove unused code
2022-09-18 17:10:32 -07:00
Rui Ueyama 3e185623c1 Merge pull request #716 from marxin/fix-test
fix a test warning
2022-09-18 17:10:10 -07:00
Martin Liska 459cdcb6f1 fix a test warning
Fixes:
./test/elf/tls-gd2.sh: line 15: [: missing `]'

Signed-off-by: Martin Liska <[email protected]>
2022-09-18 19:22:46 +02:00
Martin Liska a9e697660b sparc64: remove unused code
Signed-off-by: Martin Liska <[email protected]>
2022-09-18 19:18:55 +02:00
Rui Ueyama dbfb584ab9 [ELF][SPARC64] Fix more tests 2022-09-18 22:23:30 +08:00
Rui Ueyama 154031af66 Refactor 2022-09-18 21:56:19 +08:00
Rui Ueyama ee11979a76 [ELF] Do not allow --no-apply-dynamic-relocs on REL-type targets 2022-09-18 21:36:50 +08:00
Rui Ueyama b87398be69 Merge pull request #712 from wangp/which
Don't use which(1) or perl
2022-09-18 06:10:54 -07:00
Rui Ueyama 982b06da2d Merge pull request #714 from marxin/fix-Worder-issues 2022-09-18 05:53:34 -07:00
Rui Ueyama 5c8cd76d2e [ELF][SPARC] Support TLS relocations 2022-09-18 19:43:46 +08:00
Rui Ueyama b28e771756 [ELF][SPARC64] Fill .plt.got contents 2022-09-18 17:26:03 +08:00
Martin Liska 1174cf7406 fix -Wreorder warnings
Fixes:

elf/elf.h: In constructor 'mold::elf::EB64Rel::EB64Rel(mold::u64, mold::u32, mold::u32, mold::i64)':
elf/elf.h:1928:8: warning: 'mold::elf::EB64Rel::r_type' will be initialized after [-Wreorder]
 1928 |   ub32 r_type;
      |        ^~~~~~
elf/elf.h:1927:8: warning:   'mold::ub32 mold::elf::EB64Rel::r_sym' [-Wreorder]
 1927 |   ub32 r_sym;
      |        ^~~~~
elf/elf.h:1923:3: warning:   when initialized here [-Wreorder]
 1923 |   EB64Rel(u64 r_offset, u32 r_type, u32 r_sym, i64 r_addend = 0)
      |   ^~~~~~~
elf/elf.h: In constructor 'mold::elf::EB32Rel::EB32Rel(mold::u64, mold::u32, mold::u32, mold::i64)':
elf/elf.h:1938:6: warning: 'mold::elf::EB32Rel::r_type' will be initialized after [-Wreorder]
 1938 |   u8 r_type;
      |      ^~~~~~

Signed-off-by: Martin Liska <[email protected]>
2022-09-18 11:01:34 +02:00
Rui Ueyama 190da9ce92 [ELF][SPARC64] Support non-position-independent executables 2022-09-18 15:26:23 +08:00
Rui Ueyama 874876ab82 Refactor 2022-09-18 13:26:29 +08:00
Rui Ueyama 4698c1364a Refactor 2022-09-18 13:04:56 +08:00
Rui Ueyama adde0130f5 [ELF] Do not promote hidden symbols to dynamic symbols
Fixes https://github.com/rui314/mold/pull/713
2022-09-18 12:32:56 +08:00
Rui Ueyama 562b52906f [ELF][SPARC64] Support more relocations
With this commit, we can now build a dynamically-linked "hello world"
program for SPARC64.
2022-09-18 12:08:26 +08:00
Peter Wang c56429b1d6 Use sed instead of perl 2022-09-17 14:00:17 +10:00
Peter Wang 7cdf0abbb3 Use command -v instead of which 2022-09-17 13:41:02 +10:00
Peter Wang d4004cf054 Directly run clang to check if available
"which clang" may find a symlink from "clang" to ccache,
thus succeeding even when clang is not installed on the system.
2022-09-17 13:40:40 +10:00
Rui Ueyama c1bdbe2327 Add a comment 2022-09-16 21:56:31 +08:00
Rui Ueyama ce78dfd4b2 Simplify 2022-09-16 21:39:01 +08:00
Rui Ueyama 2331492840 Refactor 2022-09-16 21:23:45 +08:00
Rui Ueyama a189dae361 [ELF][SPARC64] Implement features
With this commit, mold/SPARC64 can create an output file for
hello-dynamic.sh test, though the output didn't work correctly.
2022-09-16 18:06:19 +08:00
Rui Ueyama d72e96f02e [ELF] Add a stub for SPARC64 2022-09-16 14:00:21 +08:00
Rui Ueyama 1fdb13c816 [ELF] Add data types for SPARC64 2022-09-16 13:50:02 +08:00
Rui Ueyama 8af8eced16 [ELF] Rename PPC64 PPC64LE 2022-09-16 13:35:35 +08:00
Rui Ueyama d92d6c0cd7 Update comments 2022-09-16 12:36:03 +08:00
Rui Ueyama 20edf1b44b Refactor 2022-09-16 10:29:23 +08:00
Rui Ueyama 441d53c302 Refactor 2022-09-16 09:38:19 +08:00
Rui Ueyama 26168f662a Refactor 2022-09-16 08:51:30 +08:00
Rui Ueyama 2af061f7fd Update comments 2022-09-15 19:53:47 +08:00
Rui Ueyama fea0734c41 Use one more word for each symbol
I don't think saving 32 bits here doesn't worth the cost of worrying
about possible integer overflow.
2022-09-15 18:03:40 +08:00
Rui Ueyama f0f8b30a2d Create an Docker multi-architecture build image
So that we can create binary distribution images on any machine.
2022-09-15 15:30:21 +08:00
Rui Ueyama 9d748ba967 Do not depend on compiler optimization too much
This is a performance critical part, and I started to think that
depending on a smart compiler optimization is a bit too risky.
2022-09-15 12:50:02 +08:00
Rui Ueyama 37863ccbaf Update comments 2022-09-15 11:52:37 +08:00
Rui Ueyama fb5b828996 Simplify 2022-09-15 11:20:57 +08:00
Rui Ueyama 7d36399035 Merge branch 'marxin-use-CC-in-tests' 2022-09-15 10:03:07 +08:00
Martin Liska 0825208628 tests: fix test/elf/exception-mcmodel-large.sh
Fix syntax error:
line 31: int main() {}: command not found

Signed-off-by: Martin Liska <[email protected]>
2022-09-15 10:02:39 +08:00
Rui Ueyama 86d67b1b17 Simplify 2022-09-15 10:02:22 +08:00
Rui Ueyama 8612d411d1 Refactor 2022-09-15 10:02:22 +08:00
Rui Ueyama 3fd9ca553b Merge pull request #710 from marxin/fix-a-test
tests: fix test/elf/exception-mcmodel-large.sh
2022-09-14 18:50:16 -07:00
Rui Ueyama 62c077924a Simplify 2022-09-15 09:14:13 +08:00
Rui Ueyama 3795b71350 Refactor 2022-09-15 08:52:46 +08:00
Martin Liska bcb21edcb1 tests: use $CC instead of cc
Signed-off-by: Martin Liska <[email protected]>
2022-09-14 21:46:41 +02:00
Martin Liska 30617cc632 tests: fix test/elf/exception-mcmodel-large.sh
Fix syntax error:
line 31: int main() {}: command not found

Signed-off-by: Martin Liska <[email protected]>
2022-09-14 21:46:20 +02:00
Rui Ueyama 183e4db696 Support big-endian hosts
Note that we do not support any big-endian targets. This commit enables
users to use mold to cross compile little-endian binaries on big-endian
hosts.
2022-09-14 21:43:29 +08:00
Rui Ueyama 1b9dc06150 Revert "Enable IFUNC tests on RISC-V" to fix CI
This reverts commit 70b69b2698.
2022-09-14 18:24:39 +08:00
Rui Ueyama 34b63e7f92 Fix CI 2022-09-14 17:57:20 +08:00
Rui Ueyama 045bc58da5 Simplify 2022-09-14 17:55:43 +08:00
Rui Ueyama 70b69b2698 Enable IFUNC tests on RISC-V 2022-09-14 17:45:37 +08:00
Rui Ueyama 0856d3f76e Check if $CC accepts -static
Fixes https://github.com/rui314/mold/issues/707
2022-09-14 17:24:48 +08:00
Rui Ueyama f8176051c1 Simplify 2022-09-14 16:26:39 +08:00
Rui Ueyama 80f2c66413 Simplify 2022-09-14 16:19:10 +08:00
Rui Ueyama e00ddceda2 Simplify 2022-09-14 16:08:10 +08:00
Rui Ueyama 9e6e472b2a Attempt to suppress GCC's -Walloc-size-larger-than warnings
https://github.com/rui314/mold/issues/708
2022-09-14 15:20:29 +08:00
Rui Ueyama c55e1216b8 Disable -Wsign-compare and -Wunused-function
https://github.com/rui314/mold/issues/708
2022-09-14 15:12:50 +08:00
Rui Ueyama 07636727c7 Fix -Wunused-variable warnings
https://github.com/rui314/mold/issues/708
2022-09-14 15:00:49 +08:00
Rui Ueyama 75c142f321 Fix GCC's -Wclass-memaccess warnings
GCC prints out bunch of the following warnings when compiling mold.

```
macho/input-files.cc:717:11: warning: 'void* memset(void*, int, size_t)'
  clearing an object of non-trivial type 'struct mold::macho::MachSection';
  use assignment or value-initialization instead [-Wclass-memaccess]

  717 |     memset(hdr, 0, sizeof(*hdr));
      |     ~~~~~~^~~~~~~~~~~~~~~~~~~~~~
```

This commit suppresses it by making our classes trivial.

https://github.com/rui314/mold/issues/708
2022-09-14 14:49:42 +08:00
Rui Ueyama 8e2e6d8603 Set LIBDIR for mold-wrapper.so
Fixes https://github.com/rui314/mold/issues/697
2022-09-14 14:28:17 +08:00
Rui Ueyama fdcb3c2efe Refactor 2022-09-14 14:10:36 +08:00
Rui Ueyama b4c148dd0c Do not over-specify distro version 2022-09-14 11:24:23 +08:00
Rui Ueyama 4e5fe38a2d Simplify 2022-09-14 11:17:07 +08:00
Rui Ueyama 9fd8313657 Simplify 2022-09-13 17:27:37 +08:00
Rui Ueyama 6a71ec6804 Fix CI 2022-09-13 16:21:22 +08:00
Rui Ueyama 896e49cc9e Update the man page 2022-09-13 15:53:03 +08:00
Rui Ueyama 258ec353b0 [ELF] Support reading ZSTD-compressed debug info section 2022-09-13 15:47:59 +08:00
Rui Ueyama 7b9bfe08a6 [ELF] Delete support of .zdebug-style compressed debug sections
lld 15 stopped supporting it since its usage is very low if not none.

https://reviews.llvm.org/D126793
2022-09-13 15:47:59 +08:00
Rui Ueyama a3512e6636 Fix CI 2022-09-13 15:47:59 +08:00
Rui Ueyama 145369e1f1 Fix CI 2022-09-13 15:33:39 +08:00
Rui Ueyama 26591cfd41 Fix CI 2022-09-13 15:09:14 +08:00
Rui Ueyama c8abc0f189 Suppress compiler warnings 2022-09-13 14:55:20 +08:00
Rui Ueyama ede7a5aaa3 [ELF] Support --compress-debug-sections=zstd
Fixes https://github.com/rui314/mold/issues/700
2022-09-13 14:55:20 +08:00
Rui Ueyama 5919914c54 Add libzstd to support --compress-debug-sections=zstd 2022-09-13 14:55:17 +08:00
Rui Ueyama 34bd56e15b Update comments 2022-09-13 13:10:02 +08:00
Rui Ueyama da550493b6 Refactor 2022-09-13 13:04:35 +08:00
Rui Ueyama b1055b42b7 Refactor 2022-09-13 11:58:14 +08:00
Rui Ueyama b941a37b1e Make addresses given to --start-address multiples of 65536
On PPC64, page size is 65536 bytes or 0x10000 bytes.

https://github.com/rui314/mold/issues/554
2022-09-12 19:37:58 +08:00
Rui Ueyama c0ff4a246b [ELF][PPC64] Handle R_PPC64_DTPREL64 against non-alloc sections
https://github.com/rui314/mold/issues/554
2022-09-12 16:27:00 +08:00
Rui Ueyama ed1acd31c8 Do not use fgrep or egrep
fgrep and egrep are deprecated.

Fixes https://github.com/rui314/mold/issues/705
2022-09-12 16:22:55 +08:00
Rui Ueyama cda57cfe4a [ELF] Do not create multiple NOTE segments 2022-09-12 15:58:20 +08:00
Rui Ueyama 428aac7458 Update comments 2022-09-12 15:07:53 +08:00
Rui Ueyama 629f2157dd Update comments 2022-09-12 14:43:32 +08:00
Rui Ueyama 8dc8ddb5d4 Update comments 2022-09-12 14:18:27 +08:00
Rui Ueyama d4c7062bd4 Simplify 2022-09-12 13:11:32 +08:00
Rui Ueyama bbf591288e Add a comment 2022-09-12 13:06:15 +08:00
Rui Ueyama f702c398b2 Update comments 2022-09-12 12:16:45 +08:00
Rui Ueyama a5eb8b9d24 [ELF][PPC64] REL24's range is not 2^23 but 2^25 2022-09-12 10:33:38 +08:00
Rui Ueyama 56c7761485 Simplify 2022-09-12 10:22:04 +08:00
Rui Ueyama 6d588cc3a8 [ELF] Add more error checks 2022-09-11 20:42:47 +08:00
Rui Ueyama e01f8b4993 [ELF] Add more error checks 2022-09-11 19:33:53 +08:00
Rui Ueyama cbf786bb59 Simplify 2022-09-11 18:24:32 +08:00
Rui Ueyama 2ee1b530b6 Update comments 2022-09-11 17:39:16 +08:00
Rui Ueyama b96a4150d9 [ELF][x86] Simplify TLS relaxation
Previously, TLSLD relocation on i386/x86-64 produced different values
than it normally does if it's relaxed. If relaxed, it's evaluated to
TP, which is the end address of the TLS block. If not relaxed, it's
evaluated to the beginning of the TLS block.

As a result, we needed to adjust subsequent DTPOFF relocations so that
it is based on the end of the TLS block only when the corresponding
symbol was relaxed. This is very tricky.

I believe the psABI TLS spec specifies this way because they couldn't
find a replacement instruction sequence that is equal to or shorter
then the replacing instructions. `mov %fs:0, %rax` is 9 bytes long,
so it barely fits the location where a TLSLD refers to.

I found that `xor %eax, %eax; mov %fs(%rax), %rax` is shorter than
`mov %fs:0, %rax` while it yields the same value. The two instructions
are 5 bytes in total. By saving 4 bytes, we can now add another `add`
instruction to adjust the resulting value, so that the result is always
relative to the beginning of the TLS block.
2022-09-11 16:40:02 +08:00
Rui Ueyama a6777e9c50 Refactor 2022-09-11 16:33:19 +08:00
Rui Ueyama 20effdc21c [ELF][i386] Enable more TLS tests 2022-09-11 16:04:42 +08:00
Rui Ueyama c7ad760534 Add comments 2022-09-11 11:49:27 +08:00
Rui Ueyama 7406b18042 Add a test 2022-09-11 11:35:49 +08:00
Rui Ueyama c97d6088e5 Remove dead code
https://github.com/rui314/mold/pull/703
2022-09-11 10:36:20 +08:00
Rui Ueyama 01240db538 Simplify 2022-09-11 10:14:41 +08:00
Rui Ueyama 055e2423e5 Update comments 2022-09-10 21:11:34 +08:00
Rui Ueyama ac7a7cb1f3 Update comments 2022-09-10 20:52:43 +08:00
Rui Ueyama fe59dc9dff [ELF][PPC64] Create range extension thunks for unreachable branches
With this, all tests pass on PPC64. We need to support TOC-less
functions, though.
2022-09-10 19:55:44 +08:00
Rui Ueyama 3bf132f7ed Revert "Improve a test"
This reverts commit 100fcaebf4 to unbreak CI.
2022-09-10 19:54:17 +08:00
Rui Ueyama 100fcaebf4 Improve a test 2022-09-10 19:17:24 +08:00
Rui Ueyama 1b3e0fe0fb Update a test 2022-09-10 18:18:54 +08:00
Rui Ueyama b0776d9496 Style changes 2022-09-10 17:59:13 +08:00
Rui Ueyama 7b6dc763ad Update a help message 2022-09-10 17:44:41 +08:00
Rui Ueyama 43e3aa24ef Merge pull request #701 from rod-salazar/lto_lib_doc_text
Remove 'Ignored' helptext for lto_library and order_file
2022-09-10 02:42:39 -07:00
Rui Ueyama 1cbae48a29 Style changes 2022-09-10 17:34:55 +08:00
Rui Ueyama 623549ff45 Style changes 2022-09-10 17:31:38 +08:00
Rodrigo Salazar 042910a848 Remove 'Ignored' helptext for lto_library and order_file 2022-09-10 02:20:39 -07:00
Rui Ueyama 0f7c87ca03 Add a comment 2022-09-10 16:43:17 +08:00
Rui Ueyama febb696a39 [ELF][PPC64] Enable PPC64 tests if -DMOLD_ENABLE_QEMU_TESTS is specified 2022-09-10 15:59:24 +08:00
Rui Ueyama 594dd50246 Simplify 2022-09-10 15:57:52 +08:00
Rui Ueyama 6aa2c56497 [ELF][PPC64] Enable CI 2022-09-10 15:39:28 +08:00
Rui Ueyama 5ca3d4bf48 [ELF][PPC64] Handle .toc just like .got 2022-09-10 15:35:15 +08:00
Rui Ueyama a3a0582ac4 Fix CI 2022-09-10 14:32:51 +08:00
Rui Ueyama 6b5f0f1c92 [ELF][PPC64] Fix various issues for ppc64 2022-09-10 13:47:28 +08:00
Rui Ueyama 5dcb2b634e Improve error messages 2022-09-09 21:07:22 +08:00
Rui Ueyama aa9cab8323 Simplify 2022-09-09 20:45:19 +08:00
Rui Ueyama d739547feb [ELF][PPC64] Handle relocations against non-alloc sections 2022-09-09 20:02:53 +08:00
Rui Ueyama 20d80b806a [ELF][PPC64] Remove an unnecessary padding from .plt.got 2022-09-09 19:33:33 +08:00
Rui Ueyama 21be1d7d5e Update comments 2022-09-09 19:24:54 +08:00
Rui Ueyama 1227897ab0 Update README 2022-09-09 18:08:02 +08:00
Rui Ueyama 3cd25344a8 Simplify 2022-09-09 17:55:46 +08:00
Rui Ueyama 3e02327858 Fix invalid uses of a relocation 2022-09-09 17:52:42 +08:00
Rui Ueyama c8013ae871 Update comments 2022-09-09 17:49:32 +08:00
Rui Ueyama 98b35d3793 [ELF][PPC64] Add R_PPC64_TLSLD, R_PPC64_DTPREL16_HA and R_PPC64_DTPREL16_LO 2022-09-09 17:23:24 +08:00
Rui Ueyama 5a8181f7ff [ELF][PPC64] Add R_PPC64_GOT_TLSLD16_HA 2022-09-09 17:19:18 +08:00
Rui Ueyama f38e131242 [ELF][PPC64] Add R_PPC64_REL64 2022-09-09 17:15:35 +08:00
Rui Ueyama 2dd6bbbb3d [ELF][PPC64] Restore r2 after function call 2022-09-09 17:09:20 +08:00
Rui Ueyama 87e2e06530 Add links to psABI documents 2022-09-09 14:24:02 +08:00
Rui Ueyama 678dcfb748 Update comments 2022-09-09 12:57:19 +08:00
Rui Ueyama 7b4afc48ff [ELF][PPC64] Fix PLT contents
With this change, tests/elf/hello-dynamic.sh passes.
2022-09-09 12:47:03 +08:00
Rui Ueyama fda84aa648 Simplify 2022-09-09 07:46:04 +08:00
Rui Ueyama 12b5699750 Add a comment 2022-09-08 21:51:11 +08:00
Rui Ueyama aa300ae973 [ELF][PPC64] Remove an unnecessary section 2022-09-08 21:36:57 +08:00
Rui Ueyama c1de2d53dc [ELF][PPC64] Do not disk space to .got.plt 2022-09-08 19:29:17 +08:00
Rui Ueyama da7c3f878f Style changes 2022-09-08 17:31:52 +08:00
Rui Ueyama 5bf05b5f45 Merge pull request #699 from ksco/eh-frame-hdr
Fix --no-eh-frame-hdr option
2022-09-08 02:28:24 -07:00
Yang Liu 410e1633e2 Add a no-eh-frame-header test
Signed-off-by: Yang Liu <[email protected]>
2022-09-08 17:07:12 +08:00
Yang Liu 5871018d10 Fix --no-eh-frame-hdr
Signed-off-by: Yang Liu <[email protected]>
2022-09-08 17:07:12 +08:00
Rui Ueyama 2940d93123 Remove dead code 2022-09-08 17:02:04 +08:00
Rui Ueyama be0131fc63 [ELF] Add initial PPC64LE support 2022-09-08 16:57:59 +08:00
Rui Ueyama 4d8d3fda55 Refactor 2022-09-08 16:57:59 +08:00
Rui Ueyama 28f89b0a4f Merge pull request #698 from ksco/global-pointer
Fix global-pointer value
2022-09-08 00:05:18 -07:00
Yang Liu 906abcedee Fix global-pointer value
Global-pointer's value should be .sdata+0x800.

Signed-off-by: Yang Liu <[email protected]>
2022-09-08 14:24:35 +08:00
Rui Ueyama 1950def5f9 Refactor 2022-09-06 21:19:56 +08:00
Rui Ueyama 692fe9af1e Update comments 2022-09-06 18:15:57 +08:00
Rui Ueyama ff6afd39e0 Simplify 2022-09-06 18:03:03 +08:00
Rui Ueyama a712f91a03 Rename a variable 2022-09-06 13:24:20 +08:00
Rui Ueyama 8c7f31cafa [ELF] Copy ELF st_type when creating symbol aliases for --defsym
Fixes https://github.com/rui314/mold/issues/695
2022-09-06 12:54:34 +08:00
Rui Ueyama eede5a08b0 Update comments 2022-09-06 10:18:39 +08:00
Rui Ueyama ecdb6034af Update comments 2022-09-05 20:50:04 +08:00
Rui Ueyama 85d496dd45 Update a comment 2022-09-05 20:42:29 +08:00
Rui Ueyama 00273f3d0c Simplify 2022-09-05 20:21:43 +08:00
Rui Ueyama 7cad171a34 Rename functions 2022-09-05 20:17:05 +08:00
Rui Ueyama 1d0c1f8f32 [ELF] Add overflow checks 2022-09-05 20:16:00 +08:00
Rui Ueyama 7ffdcd49d8 [ELF] Set a unique dummy value to linker-synthesized symbols 2022-09-05 17:33:34 +08:00
Rui Ueyama bb0e601eaa Avoid GNU assembler's bug
Looks like GNU assembler produces illegal code and relocations for
_GLOBAL_OFFSET_TABLE, so we can't directly use that symbol in our
tests.

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106834
2022-09-05 17:06:19 +08:00
Rui Ueyama f34c345c7c Move code so that the relocations are in the same order in all switch-cases 2022-09-05 14:43:35 +08:00
Rui Ueyama d2bba23541 Update comments 2022-09-05 14:21:54 +08:00
Rui Ueyama fc49faa896 Style changes 2022-09-05 14:19:36 +08:00
Rui Ueyama ddbf501a66 Merge pull request #687 from ksco/relax
[RISCV] Add zero-page relaxation
2022-09-04 22:58:42 -07:00
Rui Ueyama be13b5de4a [ELF] Replace branch instructions with NOPs 2022-09-05 13:44:53 +08:00
Rui Ueyama 72e26d9b69 [ELF] Do not convert a call with a weak undef to a nop in DSO
On ARM, calling an remaining weak undefined symbol is a NOP. But
if such symbol was promoted to a dynamic symbol, we need to execute
that call instruction instead of converting to a NOP because it may
be resolved to a defined symbol at runtime.

Fixes https://github.com/rui314/mold/issues/660
2022-09-05 13:34:59 +08:00
Rui Ueyama 6d51493e46 [ELF] Avoid accidental string prefix matches 2022-09-05 10:10:20 +08:00
Rui Ueyama 25c7aee1d9 [ELF] Merge .rodata mergeable strings to .rodata.str
Fixes https://github.com/rui314/mold/issues/511
2022-09-05 09:46:58 +08:00
Rui Ueyama fafc0d0b94 Simplify 2022-09-04 20:34:44 +08:00
Rui Ueyama 8d16301665 [ELF][RISC-V] Do not allocate r_deltas for non-executable sections
RISC-V relocations are relaxed only for executable code sections,
so we don't need to allocate r_deltas for non-executable sections.
2022-09-04 19:23:02 +08:00
Rui Ueyama 98042443ac Simplify 2022-09-04 19:19:43 +08:00
Rui Ueyama 2c86726c06 Suppress a dynamic linker's warning message
glibc started printing out the following warning message on process
startup, starting from glibc 2.36:

  warning: direct reference to protected function `baz' in
  `out/test/elf/x86_64/protected/b.so' may break pointer equality

It does so if it finds there's a canonical PLT against a protected
symbol. The warning message itself makes sense -- there's no way to
create a canonical PLT against a protected symbol while keeping
pointer equality, and you should recompile object files with `-fPIC`
to suppress this warning.

However, in this test, we intentionally create a canonical PLT against
a protected symbol, so we just want to silence it.

Fixes https://github.com/rui314/mold/issues/689
2022-09-04 17:54:51 +08:00
Rui Ueyama ba43a01c8c Merge pull request #690 from ksco/script-test
Improve linker-script-defsym tests
2022-09-04 02:48:41 -07:00
Yang Liu c1dc0e6c2d Improve linker-script-defsym tests
Signed-off-by: Yang Liu <[email protected]>
2022-09-04 17:42:48 +08:00
Rui Ueyama 3ab548971f [ELF] Make riscv_resize_sections faster
We saw a significant performance improvement with this commit. Here
is before and after numbers when cross-building mold for RV32 on a
simulated 16-core machine:

  $ time ./mold.old @rsp
  real    0m45.216s
  user    0m0.016s
  sys     0m0.000s

  $ time ./mold.new @rsp
  real    0m0.240s
  user    0m0.014s
  sys     0m0.003s

  $ ls -lh mold
  -rwxrwxr-x 1 ruiu ruiu 152M Sep  4 17:16 mold
2022-09-04 17:24:07 +08:00
Yang Liu b6549580be [RISCV] Add zero-page relaxation
Signed-off-by: Yang Liu <[email protected]>
2022-09-04 16:02:53 +08:00
Rui Ueyama 807af97a4b Revert "wip" which was made by mistake 2022-09-04 15:46:46 +08:00
Rui Ueyama 682fc41c0d [ELF] Do not try to relax dead input sections
It's just a waste of time.
2022-09-04 15:31:13 +08:00
Rui Ueyama ff0679f7b8 wip 2022-09-04 15:03:03 +08:00
Rui Ueyama b7bfe418ba Bump mold version to 1.4.2 2022-09-04 12:59:58 +08:00
Rui Ueyama 05f8bcff31 Improve comments 2022-09-04 09:58:04 +08:00
Rui Ueyama d148db3c2d Fix GCC's -Woverflow warnings 2022-09-04 09:16:48 +08:00
Yang Liu e58f55959d Update comments 2022-09-04 02:06:09 +08:00
Yang Liu 9507f11f77 Remove redundant code 2022-09-04 02:06:09 +08:00
Yang Liu 114fb21b8e Update comments
Signed-off-by: Yang Liu <[email protected]>
2022-09-04 02:06:09 +08:00
Rui Ueyama 29f054c49c Fix comment 2022-09-03 20:21:38 +08:00
Rui Ueyama fc38c15897 Update comments 2022-09-03 20:01:07 +08:00
Rui Ueyama 4528abc1c9 [ELF][ARM32] Change the way how we compute TLSDESC trampoline address 2022-09-03 19:55:33 +08:00
Rui Ueyama a83985c3b3 Update comments 2022-09-03 17:09:10 +08:00
Rui Ueyama 0beba29082 Update comments 2022-09-03 16:35:00 +08:00
Rui Ueyama 62ee0b8d72 Remove a redundant command 2022-09-03 16:25:25 +08:00
Rui Ueyama 0b42416868 Fix CI 2022-09-03 16:16:51 +08:00
Rui Ueyama 6cd5eb93b6 Merge pull request #686 from ericriff/eriff/fix-cmake-system-mimalloc
Fix build error while using the system version of mimalloc and CMake
2022-09-03 01:16:43 -07:00
Rui Ueyama ef7c698713 Simplify 2022-09-03 16:04:46 +08:00
Rui Ueyama 97bea18e5a Simplify 2022-09-03 15:33:13 +08:00
Rui Ueyama b8855f0749 Simplify 2022-09-03 15:10:59 +08:00
Rui Ueyama 78d65510ff Fix CI 2022-09-03 13:56:30 +08:00
Rui Ueyama 13808663cc [ELF] Reduce the size of a per-relocation object 2022-09-03 13:49:29 +08:00
Rui Ueyama 645c8f9931 [ELF] Avoid "relocation out of range" error for TLS trampoline 2022-09-03 13:34:53 +08:00
Rui Ueyama 1f7553d53b [ELF] Support R_ARM_TLS_CALL 2022-09-03 12:44:07 +08:00
Rui Ueyama cf208a531e Do not always skip range-extension-thunk-arm32 test 2022-09-03 12:44:06 +08:00
Riff, Eric 2fae57c4d2 Address 'Cannot specify compile definitions for target "mimalloc" which is not built by this project.' CMake error 2022-09-02 13:20:37 -03:00
Rui Ueyama 1408e6ab6c Update comments 2022-09-02 20:04:25 +08:00
Rui Ueyama 994af8db20 Update comments 2022-09-02 19:59:15 +08:00
Rui Ueyama fccf3902e0 Add comments 2022-09-02 19:37:01 +08:00
Rui Ueyama f667b0c4b9 Merge pull request #684 from ksco/tls
[RISCV] Enable tls-gd-noplt tests for RV32 and RV64
2022-09-02 04:09:00 -07:00
Yang Liu 66dfce5d03 [RISCV] Enable tls-gd-noplt tests for RV32 and RV64
Signed-off-by: Yang Liu <[email protected]>
2022-09-02 18:24:11 +08:00
Rui Ueyama 7c32e613e8 Merge pull request #683 from ksco/tls 2022-09-02 03:19:11 -07:00
Yang Liu a0dc74bb87 [RISCV] Enable tls tests for RV32 and RV64
Signed-off-by: Yang Liu <[email protected]>
2022-09-02 18:16:46 +08:00
Rui Ueyama 96524e48fd [ELF] Fix RISC-V TLSGD relocation
For RISC-V, DTPOFF is computed 0x800 off from the start of each TLS block
to maximize the accessible range for memory access instructions with
signed 12-bit immediate. DTPMOD is usually computed by the dynamic loader,
but when we do for static binaries, we need to subtract 0x800 to offset
the offset.

https://github.com/rui314/mold/issues/461
2022-09-02 17:57:40 +08:00
Rui Ueyama b48d3d3da3 Rename a variable 2022-09-02 17:57:40 +08:00
Rui Ueyama 899e6c5e6e Merge pull request #682 from ksco/linker-script
Fix missing parameter in linker script tests
2022-09-02 01:36:45 -07:00
Rui Ueyama 6ae335f379 Refactor 2022-09-02 16:22:30 +08:00
Yang Liu 0d2a258e77 Fix missing parameter in linker script tests
Signed-off-by: Yang Liu <[email protected]>
2022-09-02 16:12:44 +08:00
Rui Ueyama df521f2a04 Refactor 2022-09-02 16:08:50 +08:00
Rui Ueyama fb71467574 Refactor 2022-09-02 15:57:48 +08:00
Rui Ueyama 62511ae2c3 [ELF] Reduce the alignment of thunks 2022-09-02 15:46:00 +08:00
Rui Ueyama b23ae9de2a Fix test for Raspberry Pi 4 2022-09-02 15:19:44 +08:00
Rui Ueyama ceeec0e1da Move an assignmen out of a loop 2022-09-02 14:28:39 +08:00
Rui Ueyama 56bacad887 Improve comments 2022-09-02 13:47:47 +08:00
Rui Ueyama a6c0aba80b Improve test coverage 2022-09-02 13:12:52 +08:00
Rui Ueyama e989b57f8f [ELF] Add tests for range extension thunks 2022-09-02 12:58:26 +08:00
Rui Ueyama 5403df8cfa [ELF] Implement range extension thunks for ARM32
Previously, we couldn't create an executable whose .text is larger
than 16 MiB.
2022-09-02 12:33:04 +08:00
Rui Ueyama 0069fff8a5 [ELF] Do not call get_eflags for each relaxable relocation 2022-09-01 21:50:53 +08:00
Rui Ueyama 4a2f6574f4 Refactor
Make it consistent with arch-x86-64.cc.
2022-09-01 21:50:48 +08:00
Rui Ueyama b6bd7b83f0 Merge pull request #681 from ksco/rvc
[RISCV] Check rvc support when relaxing R_RISCV_CALL_PLT
2022-09-01 06:27:48 -07:00
Rui Ueyama 017f2492bf Merge pull request #680 from ksco/check
[RISCV] Check relocation range
2022-09-01 06:05:27 -07:00
Yang Liu c7534f49f1 [RISCV] Add a no RVC test
Signed-off-by: Yang Liu <[email protected]>
2022-09-01 20:53:45 +08:00
Yang Liu eb2f02c2e2 [RISCV] Check rvc support when relaxing R_RISCV_CALL_PLT
Signed-off-by: Yang Liu <[email protected]>
2022-09-01 20:53:45 +08:00
Yang Liu 9e303fd8ac [RISCV] Check relocation range
Signed-off-by: Yang Liu <[email protected]>
2022-09-01 19:37:24 +08:00
Rui Ueyama df1b8d9f56 Remove dead code 2022-09-01 14:37:04 +08:00
Rui Ueyama ea779c36a4 [ELF] Fix build issue on Raspberry Pi 4
We need -latomic on ARMv7.
2022-09-01 12:18:37 +08:00
Rui Ueyama 1a0a242c45 Add Rasbian OS 2022-09-01 12:02:59 +08:00
Rui Ueyama 2fa850ceeb Simplify 2022-09-01 10:26:24 +08:00
Rui Ueyama 7e56070c77 Update a comment 2022-09-01 10:25:10 +08:00
Rui Ueyama a557877fb5 [ELF] Do not allocate disk space for a virtual address space hole
Previously, if a large address is specified by --start-address, we
created a large file.

Fixes https://github.com/rui314/mold/issues/676
2022-08-31 17:43:55 +08:00
Rui Ueyama 5f2a4a0498 Update comments 2022-08-31 14:36:24 +08:00
Rui Ueyama 099f85a29d [ELF][RISC-V] Relax R_RISCV_TPREL_HI20 and R_RISCV_TPREL_ADD 2022-08-31 14:15:50 +08:00
Rui Ueyama f0d0cd5b1c Revert "[ELF][RISC-V] Implement LUI → C.LUI or C.LI relaxation"
This reverts commit a96657bcac to unbreak CI.
2022-08-31 13:00:43 +08:00
Rui Ueyama a96657bcac [ELF][RISC-V] Implement LUI → C.LUI or C.LI relaxation
This is a re-submission of c625e28e1c.
2022-08-31 12:49:05 +08:00
Rui Ueyama d887569522 Revert "[ELF][RISC-V] Implement LUI → C.LUI or C.LI relaxation"
This reverts commit c625e28e1c to unbreak CI.
2022-08-31 11:44:51 +08:00
Rui Ueyama c625e28e1c [ELF][RISC-V] Implement LUI → C.LUI or C.LI relaxation 2022-08-31 10:54:21 +08:00
Rui Ueyama 8b45ffe17a Simplify 2022-08-31 10:00:05 +08:00
Rui Ueyama c79f58856f Update a comment 2022-08-30 20:38:24 +08:00
Rui Ueyama 78f8832acf Simplify 2022-08-30 20:29:58 +08:00
Rui Ueyama 4c760cd87f Rename functions 2022-08-30 19:43:03 +08:00
Rui Ueyama e06e6b474d Simplify 2022-08-30 14:12:49 +08:00
Rui Ueyama 10a9a6bafb [ELF] C.J and C.JAL can jump within ±2 KiB
The number of bits of the operand is 11 bits, but since the least
significant bit is always zero and omitted, the effective number
of bits is 12.
2022-08-30 14:02:58 +08:00
Rui Ueyama e6b1e6d7ec Refactor
r_deltas contained negative numbers. This commit inverts them.
2022-08-30 13:49:34 +08:00
Rui Ueyama 34bdfeb69f [ELF] Parallelize RISC-V relaxation 2022-08-30 13:49:34 +08:00
Rui Ueyama 4c5cad43fb [ELF] Make the output deterministic
Mutating symbols values in this loop is not safe because we also
read symbol values in the same loop.
2022-08-29 21:29:38 +08:00
Rui Ueyama 09d12639b4 Refactor 2022-08-29 20:07:28 +08:00
Rui Ueyama 2f38ebd1cc Format 2022-08-29 20:02:39 +08:00
Rui Ueyama 136f32ac2e Do not create a circular symlink 2022-08-29 19:24:41 +08:00
Rui Ueyama cddbdba091 Fix CI 2022-08-29 19:02:21 +08:00
Rui Ueyama 99090d0e37 Archive object files for tests 2022-08-29 18:01:53 +08:00
Rui Ueyama 9598ba4d25 Archive test results even if tests failed 2022-08-29 17:36:09 +08:00
Rui Ueyama c43f648fa1 Add a comment 2022-08-29 17:33:07 +08:00
Rui Ueyama 82564cb571 Simplify 2022-08-29 17:06:23 +08:00
Rui Ueyama e791b34d50 Simplify 2022-08-29 16:51:00 +08:00
Rui Ueyama 584837e294 Make test results downloadable 2022-08-29 14:51:09 +08:00
Rui Ueyama 037d6e6d22 Simplify 2022-08-29 14:43:29 +08:00
Rui Ueyama f08bc94618 Fix comment 2022-08-29 13:24:27 +08:00
Rui Ueyama 14bf89918c Remove redundnat chmod 2022-08-29 13:21:20 +08:00
Rui Ueyama 33d8657270 Merge pull request #675 from ksco/tailcall
Add tail call test
2022-08-28 22:20:41 -07:00
Yang Liu 9de22f8945 Add tail call test
Signed-off-by: Yang Liu <[email protected]>
2022-08-29 13:03:00 +08:00
Rui Ueyama bd5fb9ec9b Rename a function 2022-08-29 12:55:47 +08:00
Rui Ueyama e8453216cf Simplify 2022-08-29 12:54:48 +08:00
Rui Ueyama c39b9bb100 Make log message less verbose 2022-08-29 12:47:14 +08:00
Rui Ueyama 6eac1e60ad Fix CI 2022-08-29 12:37:23 +08:00
Rui Ueyama 00aa3124cd [ELF] Detect an error
Distance between two instructions on RISC-V should be a multiple of two
because instructions are aligned to at least two bytes boundaries.
2022-08-29 12:10:11 +08:00
Rui Ueyama 3ad19a7555 Simplify 2022-08-29 11:30:27 +08:00
Rui Ueyama d1babd0682 Style changes 2022-08-29 11:15:27 +08:00
Rui Ueyama 8999732344 Merge pull request #672 from ksco/relax
[RISCV] Relax AUIPC+JALR to C.JAL or C.J
2022-08-28 19:48:49 -07:00
Rui Ueyama 667d1e7d6e Update RV32 tests 2022-08-29 10:39:55 +08:00
Rui Ueyama f189ea805c Remove an unnecessary apt-get line 2022-08-29 10:33:48 +08:00
Rui Ueyama 1fc86d7905 Run RV32 tests on CI
https://github.com/rui314/mold/pull/672
2022-08-29 10:29:07 +08:00
Rui Ueyama e54d7e9761 Merge pull request #673 from ksco/make 2022-08-28 04:07:28 -07:00
Yang Liu 538239e1c2 [RISCV] Relax AUIPC+JALR to C.JAL or C.J
Signed-off-by: Yang Liu <[email protected]>
2022-08-28 18:34:02 +08:00
Yang Liu 3de6eb920c Add MOLD_ENABLE_RV32_QEMU_TESTS option
Signed-off-by: Yang Liu <[email protected]>
2022-08-28 18:25:52 +08:00
Rui Ueyama 91d40e07fc Simplify 2022-08-28 18:20:47 +08:00
Rui Ueyama 70b5a69f31 Simplify 2022-08-28 17:48:27 +08:00
Rui Ueyama 0604f638ea [ELF] R_RISCV_ALIGN should align the next insn to r_addend + 1 2022-08-28 17:46:29 +08:00
Rui Ueyama 00a004d698 Simplify 2022-08-28 15:02:55 +08:00
Rui Ueyama cef99ce48f Merge pull request #671 from ksco/rv64-relax-fix
[RV64] Fix jalr assemble error
2022-08-27 23:46:50 -07:00
Yang Liu 6f78946a15 [RV64] Fix jalr assemble error
Signed-off-by: Yang Liu <[email protected]>
2022-08-28 12:26:55 +08:00
Rui Ueyama 0a4586ab96 Merge pull request #670 from ksco/rv32gc-strip-fix
[RV32] Skip all test cases not applicable to RV32
2022-08-27 18:21:40 -07:00
Yang Liu ec5da29d17 [RV32] Skip all test cases not applicable to RV32
Signed-off-by: Yang Liu <[email protected]>
2022-08-28 08:42:02 +08:00
Rui Ueyama 63115cfa34 Merge pull request #669 from ksco/rv32gc
[RV32] Fix PLT header
2022-08-27 15:46:27 -07:00
Yang Liu ce849a2e5f [RV32] Fix PLT header
According to spec[1], .got.plt offset is lg2(16/PTRSIZE), under RV32 it is 2.

[1] https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#program-linkage-table

Signed-off-by: Yang Liu <[email protected]>
2022-08-27 21:04:25 +08:00
Rui Ueyama bbafdc3d23 Revert "[RISCV32] Fix R_RISCV_HI20 relocations"
This reverts commit e65cc7feea because
I don't think this is a correct fix.

We can't emit a R_RISCV_32 dynamic relocation for R_RISCV_HI20
relocation. If we do, the dynamic loader will override an entire 32
bit word with an address of a specified symbol. HI20 is expected to
set a high 20 bits as an instruction operand, so they are not
compatible.

https://github.com/rui314/mold/pull/668
2022-08-27 18:05:29 +08:00
Rui Ueyama 29c4a9c996 Update comments 2022-08-27 18:00:02 +08:00
Rui Ueyama 42e8f9a439 Merge pull request #668 from ksco/rv32gc 2022-08-27 02:51:43 -07:00
Yang Liu e65cc7feea [RISCV32] Fix R_RISCV_HI20 relocations
Signed-off-by: Yang Liu <[email protected]>
2022-08-27 17:27:44 +08:00
Rui Ueyama 340d8b3742 Improve comments 2022-08-27 09:30:10 +08:00
Rui Ueyama 8a3b291ee7 Move a comment 2022-08-26 15:48:17 +08:00
Rui Ueyama 2d364b0e78 [ELF] R_RISCV_64 should not be available on RV32 2022-08-26 12:45:09 +08:00
Rui Ueyama a0dffce882 Refactor 2022-08-26 12:45:09 +08:00
Rui Ueyama 694f973dc5 [Mach-O] Emit addends for dynamic relocations
Previously, all dynamic symbols had addend 0.

Fixes https://github.com/rui314/mold/issues/657
2022-08-25 19:02:07 +08:00
Rui Ueyama 8c4109389d Improve an error message 2022-08-25 19:02:07 +08:00
Rui Ueyama 800d246ec3 [ELF] Fix i386 PC-relative relocations 2022-08-24 13:48:13 +08:00
Rui Ueyama 2eb0258991 Simplify 2022-08-24 13:41:03 +08:00
Rui Ueyama 70bdc9bc94 [ELF] Do not promote absolute symbols from DSO to dynamic ones 2022-08-24 13:08:46 +08:00
Rui Ueyama a8bd32d2ea [ELF][RV32] Do not emit a dynamic relocation for R_RISCV_64 2022-08-24 11:25:09 +08:00
Rui Ueyama ac705872dd Remove dead code
There's no thing like PC-relative dynamic relocation, so the `else`
part will never be executed.
2022-08-24 10:45:12 +08:00
Rui Ueyama a3da318ff9 Simplify 2022-08-24 09:56:45 +08:00
Rui Ueyama a2c3b6fa1c Merge pull request #662 from ideasman42/patch-1
Fix #661: Restore ld location to $(LIBEXECDIR)/mold/ld
2022-08-23 18:50:19 -07:00
Campbell Barton 5e65a4fa77 Fix #661: Restore ld location to $(LIBEXECDIR)/mold/ld 2022-08-24 09:46:42 +10:00
Rui Ueyama 89038c2c27 Fix typos 2022-08-23 19:49:48 +08:00
Rui Ueyama e2ca959bed Rename a function 2022-08-23 19:48:40 +08:00
Rui Ueyama f787f4bdac Merge pull request #658 from marxin/really-skip-arm32-thumb-interwork.sh
armv6l: properly skip arm32-thumb-interwork.sh
2022-08-23 02:41:21 -07:00
Martin Liska c760c5daf7 armv6l: properly skip arm32-thumb-interwork.sh
I was not correct as the 'sorry, unimplemented: Thumb-1 'hard-float' VFP ABI
really comes late in target register allocator. Thus one needs to
assembly a function.

Fixes: #655
Signed-off-by: Martin Liska <[email protected]>
2022-08-23 11:15:52 +02:00
Rui Ueyama 4cb4a93d3d Fix CI 2022-08-23 16:05:37 +08:00
Rui Ueyama f6051993e4 Style fixes 2022-08-23 15:26:20 +08:00
Rui Ueyama 277d56d6f4 Add a comment 2022-08-23 13:26:54 +08:00
Rui Ueyama a31f95bd9a Simplify 2022-08-23 13:09:03 +08:00
Rui Ueyama b9b000bed0 Simplify 2022-08-23 12:28:11 +08:00
Rui Ueyama 45b3fd5ecf Simplify 2022-08-23 12:00:44 +08:00
Rui Ueyama 1992af8b51 Simplify 2022-08-23 08:40:14 +08:00
Rui Ueyama 159d4401df Remove dead code 2022-08-22 20:24:31 +08:00
Rui Ueyama e6e68945cf Remove unnecessary variables 2022-08-22 19:37:03 +08:00
Rui Ueyama cb6e1a5b9a Fix a requires expression
I don't know if the previous expression was correct, but this new
one should be correct.
2022-08-22 18:16:04 +08:00
Rui Ueyama c794112836 Simplify 2022-08-22 16:33:59 +08:00
Rui Ueyama 7bd86d9c48 [Mach-O] Fix spurious "duplicate symbol" error
Fixes https://github.com/rui314/mold/issues/654
2022-08-22 16:04:34 +08:00
Rui Ueyama a9b34e6f03 Skip a test if the compiler doesn't accept -mthumb
Fixes https://github.com/rui314/mold/issues/655
2022-08-22 15:55:41 +08:00
Rui Ueyama c4b3c28445 Add a test 2022-08-22 15:32:48 +08:00
Rui Ueyama 4682f02a45 Simplify 2022-08-22 15:12:49 +08:00
Rui Ueyama 88edf02912 Simplify 2022-08-22 14:29:22 +08:00
Rui Ueyama b184ebb73d Simplify 2022-08-22 14:19:03 +08:00
Rui Ueyama f8d81f0981 Simplify 2022-08-22 13:55:57 +08:00
Rui Ueyama c945c855ad Simplify
Because we are using C++20, we can use `requires`.
2022-08-22 13:39:33 +08:00
Rui Ueyama 7af3664791 [ELF][RISCV32] Create dynamic relocations for R_RISCV_32 2022-08-22 13:05:54 +08:00
Rui Ueyama ba3903e969 Simplify 2022-08-21 20:30:33 +08:00
Rui Ueyama f26f1e6192 Simplify 2022-08-21 20:04:37 +08:00
Rui Ueyama 29ca848dc9 Refactor 2022-08-21 17:44:11 +08:00
Rui Ueyama abb3ee0a62 Refactor 2022-08-21 16:48:39 +08:00
Rui Ueyama 95863f9ccf Simplify 2022-08-21 16:27:36 +08:00
Rui Ueyama 647395ecff Simplify 2022-08-21 15:09:30 +08:00
Rui Ueyama ab1ee42a9e Simplify 2022-08-21 14:58:02 +08:00
Rui Ueyama 784eaa2d39 Simplify 2022-08-21 14:50:20 +08:00
Rui Ueyama 19a681e06b Simplify 2022-08-21 14:25:22 +08:00
Rui Ueyama a3b9c26657 Remove an unused parameter 2022-08-21 13:42:15 +08:00
Rui Ueyama 9a7b63b55a Handle R_AARCH64_ADR_PREL_PG_HI21 as an absolute relocation
I don't think PIE uses R_AARCH64_ADR_PREL_PG_HI21 relocation because
in PIE, we need to use R_AARCH64_CALL26 or R_AARCH64_JUMP26 for branches
and use GOT to take an address of a global object.
2022-08-21 13:21:49 +08:00
Rui Ueyama 15dbcb6d52 Update the decision table for relocation processing
- PC64 relocations are not representable using DYNREL because
  DYNREL is for an absolute address

- PC-relative relocations should be able to refer PLT entries
  in the same ELF module
2022-08-21 13:03:18 +08:00
Rui Ueyama bdf5373119 Remove code for MSVC
Since we are using clang-cl for Windows, we don't need to write code
for MSVC.
2022-08-21 11:55:22 +08:00
Rui Ueyama 300f051939 Suppress "control reaches end of non-void function" warning 2022-08-21 11:54:27 +08:00
Rui Ueyama 507fb3f445 [ELF] Set st_size to 0 to imported symbols 2022-08-21 11:35:41 +08:00
Rui Ueyama 63552afa91 Simplify 2022-08-21 11:32:01 +08:00
Rui Ueyama d47a18bde2 Simplify 2022-08-21 09:58:45 +08:00
Rui Ueyama 454fd916ff Improve error message 2022-08-21 09:51:59 +08:00
Rui Ueyama 0f3437a0f0 Fix compilation error for the optimized build
Fixes https://github.com/rui314/mold/issues/648
2022-08-21 09:44:12 +08:00
Rui Ueyama 1d09662818 Simplify 2022-08-21 09:30:22 +08:00
Rui Ueyama cbfc042dae Merge pull request #649 from rbartlensky/fix-550
Correctly compute R_ARM_GOT_PREL.
2022-08-20 18:23:41 -07:00
Rui Ueyama 14c8a119f4 Respect DESTDIR when installing symlinks
Fixes https://github.com/rui314/mold/issues/650
2022-08-21 09:18:44 +08:00
Robert Bartlensky 00d9017088 arm32: Correctly compute R_ARM_GOT_PREL.
Fixes #550.

In the Arm32 [spec][spec], R_ARM_GOT_PREL is defined as follows:

R_ARM_GOT_PREL: GOT(S) + A – P

Where:
> GOT(S) is the address of the GOT entry for the symbol S.

If we look at the code a bit, notice that G is actually the _offset_
of S into the GOT, not the symbol's address in GOT. This patch makes
sure that the relocation is computed using the GOT address of the
symbol, thereby fixing the mentioned issue.

[spec]: https://github.com/ARM-software/abi-aa/blob/60a8eb8c55e999d74dac5e368fc9d7e36e38dda4/aaelf32/aaelf32.rst#5612relocation-types

Signed-off-by: Robert Bartlensky <[email protected]>
2022-08-21 02:09:03 +01:00
Rui Ueyama 71daaa22df [ELF] Fix ARM32 exception handling
Fixes https://github.com/rui314/mold/issues/646
2022-08-20 21:23:07 +08:00
Rui Ueyama 7d91c754dd Refactor 2022-08-20 17:30:07 +08:00
Rui Ueyama 6901c1ff45 Refactor 2022-08-20 17:11:09 +08:00
Rui Ueyama 8eaf81b0bd Simplify 2022-08-20 16:47:25 +08:00
Rui Ueyama 112b52c300 Refactor 2022-08-20 16:15:55 +08:00
Rui Ueyama c661a467cb Always create git-hash.cc if missing 2022-08-20 15:59:05 +08:00
Rui Ueyama 9c61fc648d Do not override CMAKE_INSTALL_LIBDIR
Fixes https://github.com/rui314/mold/issues/641
2022-08-20 15:27:58 +08:00
Rui Ueyama e2730c6e0e Improve a test 2022-08-20 15:26:43 +08:00
Rui Ueyama 7890bf36b7 Improve error message
https://github.com/rui314/mold/issues/604
2022-08-20 15:13:52 +08:00
Rui Ueyama 58fd94d96d Merge pull request #645 from imichael2e2/main
Support compilation of mold-wrapper.c on FreeBSD
2022-08-19 23:15:40 -07:00
Michael Lee 5a422bd8a1 Support compilation of mold-wrapper.c in FreeBSD 2022-08-20 13:46:08 +08:00
Rui Ueyama 0ef1e49134 Refactor 2022-08-20 13:32:05 +08:00
Rui Ueyama 49ec4671fb Eliminate all uses of Python3 2022-08-20 12:29:03 +08:00
Rui Ueyama 2406e4cd5f Rewrite update-git-hash.py in CMake 2022-08-20 12:16:25 +08:00
Rui Ueyama 0a80016276 Update README 2022-08-20 09:41:03 +08:00
Rui Ueyama 9005e163bd Merge pull request #639 from nickgerace/docs
Add Rust macOS instructions to README
2022-08-19 18:24:24 -07:00
Rui Ueyama a653478d85 Add CONFIGURE_DEPENDS to CMake file glob
Without `CONFIGURE_DEPENDS`, cmake build file is not automatically
regenerated when a new file is added.
2022-08-20 09:03:53 +08:00
Rui Ueyama a817f8d576 Enable mold to be built under BSDs
This commit is imported from
https://github.com/oneapi-src/oneTBB/commit/c4a42feb5333488360617a9b58f90e479659b744

Fixes https://github.com/rui314/mold/issues/549
2022-08-20 08:54:17 +08:00
Rui Ueyama b613a4192b Merge pull request #637 from nevack/nevack/python-shebang
Use portable shebang for python3 in update-git-hash.py
2022-08-19 17:52:42 -07:00
Rui Ueyama a5dd93ea7b Merge pull request #644 from ltratt/openbsd_support
Enable mold to build under OpenBSD.
2022-08-19 17:52:07 -07:00
Laurence Tratt 35d94cc046 Enable mold to build under OpenBSD. 2022-08-19 20:51:36 +01:00
Rui Ueyama 60070e0c13 Simplify 2022-08-19 20:05:26 +08:00
Rui Ueyama 04d76d7929 Indentation 2022-08-19 19:42:37 +08:00
Rui Ueyama 38d2b22f9b Revert "Simplify" to fix CI
This reverts commit a436ba36e5.
2022-08-19 19:26:06 +08:00
Rui Ueyama a436ba36e5 Simplify 2022-08-19 18:14:01 +08:00
Rui Ueyama 17f0f7a7c2 Simplify 2022-08-19 17:29:43 +08:00
Rui Ueyama 41cb428550 Merge pull request #640 from ZhongRuoyu/fix-macho-version-flag
Fix `ld64.mold` version flag
2022-08-19 00:57:20 -07:00
Rui Ueyama 431211321c Make "make clean" to remove symlinks 2022-08-19 15:15:32 +08:00
Zhong Ruoyu f6fb88a3ec Revert "[Mach-O] Add flag --version"
This reverts commit 156aad6354 and lines in `test/macho/version.sh` that cover this.

Signed-off-by: Zhong Ruoyu <[email protected]>
2022-08-19 15:13:19 +08:00
Rui Ueyama 771a97822b Do not request a sign-off for small patches
Tiny patches are not copyrightable in the first place, so we do not
need a sign-off for such changes.
2022-08-19 15:08:28 +08:00
Rui Ueyama c1bc12b2db Ignore directories commonly used with CMake 2022-08-19 14:33:20 +08:00
Zhong Ruoyu 4636580508 [Mach-O] Add a test to cover version flags
Signed-off-by: Zhong Ruoyu <[email protected]>
2022-08-19 14:22:51 +08:00
Zhong Ruoyu 156aad6354 [Mach-O] Add flag --version
Signed-off-by: Zhong Ruoyu <[email protected]>
2022-08-19 14:20:04 +08:00
Zhong Ruoyu 6d8dcc9cea [Mach-O] Fix nonzero return code with ld64.mold -v
Signed-off-by: Zhong Ruoyu <[email protected]>
2022-08-19 14:19:39 +08:00
Rui Ueyama f341fbfdc9 Fix CI 2022-08-19 11:06:38 +08:00
Rui Ueyama 0515b4b100 Improve error message
https://github.com/rui314/mold/issues/638
2022-08-19 10:36:28 +08:00
Rui Ueyama c8ae70d591 Merge pull request #635 from nevack/nevack/snprintf
remove unused to_string impl for Digest (aka array<uint8_t, ...>)
2022-08-18 18:29:49 -07:00
Rui Ueyama 3a6e1cd79a Refactor 2022-08-19 09:28:53 +08:00
Dzmitry Neviadomski b871a4ff66 remove to_string implementation for Digest (aka array<uint8_t, ...>)
function uses sprintf which is deprecated in latest macOS 13.0 SDK

Signed-off-by: Dzmitry Neviadomski <[email protected]>
2022-08-19 04:03:31 +03:00
Nick Gerace 97737a32ec Add Rust macOS instructions to README
Signed-off-by: Nick Gerace <[email protected]>
2022-08-18 13:45:55 -04:00
Dzmitry Neviadomski dea48143db Use portable shebang for python3 in update-git-hash.py
Signed-off-by: Dzmitry Neviadomski <[email protected]>
2022-08-18 16:59:20 +03:00
Rui Ueyama f57d10dcda Merge pull request #634 from sicherha/arm_testcase_preconditions 2022-08-18 04:57:12 -07:00
Christoph Erhardt a4a0592795 Fix precondition check for ARM architecture
The string `arm` is too restrictive: the architecture can also be
`armv7l`, for example.

Signed-off-by: Christoph Erhardt <[email protected]>
2022-08-18 12:16:25 +02:00
Christoph Erhardt 1bc11579e7 Remove precondition checks for ARM architecture
These two test cases work fine on ARM; there is no need to skip them any
more.

Signed-off-by: Christoph Erhardt <[email protected]>
2022-08-18 12:16:18 +02:00
Rui Ueyama 19d9255d72 Bump mold version to 1.4.1 2022-08-18 16:27:51 +08:00
Rui Ueyama 6d5e3e7d18 Remove a test that uses valgrind
Looks like it is causing a false negative on some ARM32-based systems.

Fixes https://github.com/rui314/mold/issues/442
2022-08-18 16:22:45 +08:00
Rui Ueyama 053b90b489 [ELF] Fix R_ARM_CALL
If a R_ARM_CALL refers an ARM instruction that jumps to Thumb, we
need to rewrite an instruction to BLX. We previously didn't do that.

Fixes https://github.com/rui314/mold/issues/442
2022-08-18 13:13:56 +08:00
Rui Ueyama 64d7532b75 Refactor 2022-08-18 12:52:04 +08:00
Rui Ueyama a8d1e293fe Refactor 2022-08-17 19:34:57 +08:00
Rui Ueyama b6f4e171c5 Merge pull request #633 from marxin/binutils-2-39
Fix warnings seen with binutils 2.39
2022-08-17 04:17:45 -07:00
Martin Liska 980219b435 Fix warnings seen with binutils 2.39
The following warnings are seen:

Testing pltgot ... /usr/lib64/gcc/x86_64-suse-linux/12/../../../../x86_64-suse-linux/bin/ld: warning: /tmp/ccpkwmmy.o: missing .note.GNU-stack section implies executable stack
/usr/lib64/gcc/x86_64-suse-linux/12/../../../../x86_64-suse-linux/bin/ld: NOTE: This behaviour is deprecated and will be removed in a future version of the linker

Testing reloc ... /usr/lib64/gcc/x86_64-suse-linux/12/../../../../x86_64-suse-linux/bin/ld: warning: out/test/elf/x86_64/reloc/a.o: missing .note.GNU-stack section implies executable stack
/usr/lib64/gcc/x86_64-suse-linux/12/../../../../x86_64-suse-linux/bin/ld: NOTE: This behaviour is deprecated and will be removed in a future version of the linker

Signed-off-by: Martin Liska <[email protected]>
2022-08-17 12:29:44 +02:00
Rui Ueyama bfba468ddd Add a description for mold/macOS 2022-08-17 17:41:14 +08:00
Rui Ueyama f9e3a31d25 Always run update-git-hash.py
Previously, the script didn't run unless the script itself is updated.
2022-08-17 16:57:31 +08:00
Rui Ueyama a2c06b2975 Do not update git-hash.cc if .git directory is missing
Fixes https://github.com/rui314/mold/issues/632.
2022-08-17 16:56:13 +08:00
Rui Ueyama a65d1a58be Do not install mold-wrapper.so for macOS
mold-wrapper.so works only on ELF-based systems, so we should not
install that file with `make install`.

https://github.com/rui314/mold/issues/621
2022-08-17 16:56:13 +08:00
Rui Ueyama bbae3116e4 Make --hash-style=both default instead of --hash-style=sysv
It is found by https://blog.hiler.eu/win32-the-only-stable-abi/.
I didn't intend to make mold to be different from GNU ld. It's
simply a compatibility bug.
2022-08-17 12:34:43 +08:00
Rui Ueyama 810eac0ff8 Format 2022-08-17 12:34:43 +08:00
Rui Ueyama d0ec2243b1 Merge pull request #631 from sicherha/fix_test_always_skipped
Fix test case being skipped unconditionally
2022-08-16 06:55:57 -07:00
Christoph Erhardt 9666041de7 Fix test case being skipped unconditionally
The precondition check was supposed to find out whether the compiler
supports `-fcfprotection`, but it always failed complaining about an
undefined reference to `main()`. As a consequence, the test case was
never executed.

Add `-c` to omit the link step in the compiler call.

Signed-off-by: Christoph Erhardt <[email protected]>
2022-08-16 15:44:34 +02:00
Rui Ueyama 6b27a7e9b3 Fix a regression introduced in 0a8431b930
That change made test/macho/all-load.sh to hang up.
2022-08-16 15:44:36 +08:00
Rui Ueyama 000b51a60c [Mach-O] Symbols flags in an export trie as bit flags
Fixes https://github.com/rui314/mold/issues/624
2022-08-16 15:44:36 +08:00
Rui Ueyama 3c236bdcfb Merge pull request #628 from marxin/fix-output-of-defsym2-test 2022-08-15 23:21:11 -07:00
Martin Liska b1242f55e9 tests: grep for output of nm in defsym2.sh test
Right now, the test prints to the output:

Testing defsym2 ...                  w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 U __cxa_finalize
                 w __gmon_start__
00000000000010f9 T bar
00000000000010f9 T foo
OK

which is changed to:

Testing defsym2 ... OK

Signed-off-by: Martin Liska <[email protected]>
2022-08-16 08:00:10 +02:00
Rui Ueyama bce2170cfe Add a test for a595c4874d 2022-08-16 12:53:54 +08:00
Rui Ueyama da4c309071 Remove dead code 2022-08-16 12:26:03 +08:00
Rui Ueyama 47793b0aa4 Let users to use clang-cl to compile mold on Windows 2022-08-16 12:10:24 +08:00
Rui Ueyama 5dd282ded5 Merge pull request #625 from rbartlensky/compute-correct-shndx
Make sure symbol points to correct section.
2022-08-15 20:13:10 -07:00
Robert Bartlensky a595c4874d Make sure symbol points to correct section.
I was debugging #550, and I noticed a symbol was pointing to the
start of a section, but its shndx was set to SHN_UNDEF. Looking over
the code, I realized that for each section you create a corresponding
output section, but if the input section is mergeable, you also create
a fragment for it whose parent is MergeableSection, which in the end
will be copied into the output binary instead of the output section.

Since the symbol was pointing to the start of a mergeable section, I
realized that it is probably wrong to use the shndx of
`isec->output_section`, because that particular Chunk never actually
makes it to the list of final Chunks for mergeable sections.

Before:
```
$ eu-readelf -s ./target/debug/mold_test | grep _rustc
  Num:    Value   Size Type    Bind   Vis          Ndx Name
   77: 000d563c     34 OBJECT  LOCAL  DEFAULT    UNDEF __rustc_debug_gdb_scripts_section__

$ eu-readelf -S ./target/debug/mold_test
[Nr] Name                 Type         Addr     Off    Size   ES Flags Lk Inf Al
...
[22] .debug_gdb_scripts   PROGBITS     000d563c 0d563c 000022  0 A      0   0  1
...
```

After:
```
$ eu-readelf -s ./target/debug/mold_test | grep _rustc
  Num:    Value   Size Type    Bind   Vis          Ndx Name
   77: 000d563c     34 OBJECT  LOCAL  DEFAULT       22 __rustc_debug_gdb_scripts_section__

$ eu-readelf -S ./target/debug/mold_test
[Nr] Name                 Type         Addr     Off    Size   ES Flags Lk Inf Al
...
[22] .debug_gdb_scripts   PROGBITS     000d563c 0d563c 000022  0 A      0   0  1
...
```

Signed-off-by: Robert Bartlensky <[email protected]>
2022-08-15 22:44:41 +01:00
Rui Ueyama 0a8431b930 Fix an assertion failure for -flto and -dead_strip_dylibs
Fixes https://github.com/rui314/mold/issues/620
2022-08-15 20:13:02 +08:00
Rui Ueyama 3fbb604577 Re-submit 9eeae5b295 in a different style
With a comment explaining why we need this macro.
2022-08-15 16:43:46 +08:00
Rui Ueyama 70a64b7391 [Mach-O] Temporarily disable linker optimization hints
It seems buggy and causes subtle linker errors. Disable it for now so that
we can focus on getting the essential parts of the linker right.

Fixes https://github.com/rui314/mold/issues/619
2022-08-15 16:14:17 +08:00
Rui Ueyama 76f6959a8e Revert "Windows: define the version of header files to Windows 10"
This reverts commit 9eeae5b295 because
I didn't actually understand what that meant.
2022-08-15 12:54:08 +08:00
Rui Ueyama 0902dd731f Merge pull request #618 from vtorri/vtorri_win10
Windows: define the version of header files to Windows 10
2022-08-14 21:46:56 -07:00
Rui Ueyama 18ca557fbd Add logging
To debug https://github.com/rui314/mold/issues/617
2022-08-15 08:27:41 +08:00
Vincent Torri 9eeae5b295 Windows: define the version of header files to Windows 10
Signed-off-by: Vincent Torri <[email protected]>
2022-08-14 21:47:50 +02:00
Rui Ueyama 9ea9a3312d [Mach-O] Remove a warning message 2022-08-14 17:40:22 +08:00
Rui Ueyama 59542519eb Update the man page 2022-08-14 16:10:52 +08:00
Rui Ueyama a2262989f9 Update the man page 2022-08-14 15:59:47 +08:00
Rui Ueyama 0bbad93d02 Add a link to the man page 2022-08-14 15:06:52 +08:00
Rui Ueyama d51c5a197d Fix CI 2022-08-14 14:39:02 +08:00
Rui Ueyama 446474e46e Add a missing #include
Fixes https://github.com/rui314/mold/issues/613
2022-08-14 14:33:55 +08:00
Rui Ueyama 9afbd99d88 Sort 2022-08-14 13:46:27 +08:00
Rui Ueyama f5b0c8d47a Do not pass GNU-ish options if we use MSVC-compatible clang-cl 2022-08-14 13:18:03 +08:00
Rui Ueyama 7d5e4bdd48 Tidy up 2022-08-14 13:15:26 +08:00
Rui Ueyama b575d2ba79 [ELF] Add ARM32, RV32 and RV64 to the --help message 2022-08-14 12:56:44 +08:00
Rui Ueyama dcaef43283 Split lines that are too long 2022-08-14 12:51:31 +08:00
Rui Ueyama 1bec466d3b Simplify 2022-08-14 12:45:11 +08:00
Rui Ueyama b439a2ba36 Merge pull request #616 from ADKaster/missing-tests 2022-08-13 19:10:00 -07:00
Andrew Kaster f77220c9fa test: Add missing tests to CMakeLists
These tests were added to the repo, but not the list of tests in the
corresponding CMakeLists. As a result, they are not run in CI when using
CTest.

Signed-off-by: Andrew Kaster <[email protected]>
2022-08-13 11:41:46 -06:00
Rui Ueyama 168cc16de2 Merge pull request #614 from vtorri/vtorri_pow_2
Windows: does not test pow() when compiling also with mingw
2022-08-13 04:58:08 -07:00
Vincent Torri 1df6fa8855 Windows: does not test pow() when compiling also with mingw
Signed-off-by: Vincent Torri <[email protected]>
2022-08-13 13:31:26 +02:00
Rui Ueyama 22c78e112b Use MSVC builtin function for unreachable 2022-08-13 17:57:45 +08:00
Rui Ueyama 6abf01597e Fix CI 2022-08-13 17:38:04 +08:00
Rui Ueyama e8332bb052 Add a Windows builder 2022-08-13 17:16:16 +08:00
Rui Ueyama 01bf7847d3 Fix build failure 2022-08-13 17:14:55 +08:00
Rui Ueyama 94d6edb7eb Fix build errors for Windows
With this change, we can compile mold on Windows using Visual Studio
2022-08-13 01:54:38 -07:00
Rui Ueyama c434c2ce94 Fix CI 2022-08-13 16:11:53 +08:00
Rui Ueyama ed1166a08f Fix more compile errors for Windows 2022-08-13 00:55:47 -07:00
Rui Ueyama 76f4c28a1b Fix typo 2022-08-13 13:49:12 +08:00
Rui Ueyama 9f05c9e26f Update email address 2022-08-13 13:47:13 +08:00
Rui Ueyama 6bbc3a56ca Include the LICENSE file into the binary distribution 2022-08-13 13:29:04 +08:00
Rui Ueyama 5eaeed25aa Remove an unneeded option 2022-08-13 13:22:10 +08:00
Rui Ueyama 93e476796b Add third-party components licenses to the main license file 2022-08-13 13:13:44 +08:00
Rui Ueyama f70a86fe45 Fix compiler warnings on Unix 2022-08-13 13:12:49 +08:00
Rui Ueyama 802aad8891 Fix CI 2022-08-13 12:49:16 +08:00
Rui Ueyama 3b796819e5 Split LTO source files for Windows 2022-08-12 21:47:02 -07:00
Rui Ueyama 4050f5241f Revert "Reduce block nesting level"
This reverts commit 73ee17337c.
We no longer use MSVC cl.exe as a compiler. clang doesn't have the
silly 128-level nesting level restriction.
2022-08-13 12:23:48 +08:00
Rui Ueyama 2ed36aef23 Merge branch 'main' of https://github.com/rui314/mold 2022-08-12 21:22:30 -07:00
Rui Ueyama 10fa6c87ef Use clang-cl instead of MSVC's cl as a compiler on Windows 2022-08-12 21:21:01 -07:00
Rui Ueyama f72b93cc66 Add a comment 2022-08-13 11:04:00 +08:00
Rui Ueyama 9daf9f942c Fix MSVC compile errors 2022-08-12 20:49:16 +08:00
Rui Ueyama 73ee17337c Reduce block nesting level
MSVC has a hard limit of 128 block nesting levels. So you can't write
cascaded "else if"s more than 128. That's a very silly restriction,
but we need to work it around.
2022-08-12 20:15:31 +08:00
Rui Ueyama 7908eae30a Silence MSVC's "unknown pragma" warnings' 2022-08-12 19:57:40 +08:00
Rui Ueyama e79f21d2e3 Fix compilation error with MSVC 2022-08-12 19:50:45 +08:00
Rui Ueyama d35d7bf918 Make it compile with MSVC
Turned out that `if constexpr` causes compilation errors with MSVC.
2022-08-12 19:50:06 +08:00
Rui Ueyama 27ef381242 Simplify 2022-08-12 19:37:57 +08:00
Rui Ueyama 0bf4d90b9b Simplify 2022-08-12 19:28:28 +08:00
Rui Ueyama 340a302e3c Attempt to unbreak CI 2022-08-12 18:18:00 +08:00
Rui Ueyama 6780da9eb0 wip 2022-08-12 18:16:10 +08:00
Rui Ueyama 9aa1eb50f4 Simplify 2022-08-12 18:03:44 +08:00
Rui Ueyama 43231abc5c Do not use fork hack on Windows 2022-08-12 17:52:35 +08:00
Rui Ueyama d5f9ca608a Simplify 2022-08-12 17:49:17 +08:00
Rui Ueyama fbd75e2831 Do not build mold-wrapper on non-Unix systems
mold-wrapper just does not work on non-Unix systems.
2022-08-12 17:43:03 +08:00
Rui Ueyama 688ded77ac Simplify 2022-08-12 17:40:35 +08:00
Rui Ueyama 630acaea46 Simplify 2022-08-12 17:37:03 +08:00
Rui Ueyama 9774ff6134 Simplify 2022-08-12 17:19:59 +08:00
Rui Ueyama c3a901e0f2 Simplify 2022-08-12 16:44:51 +08:00
Rui Ueyama 2721c45b41 Merge pull request #608 from clemenswasser/win
Initial Windows fixes
2022-08-12 01:23:32 -07:00
Rui Ueyama f0e1237b00 [ELF] Allow linker script to define symbol aliases
Fixes https://github.com/rui314/mold/issues/610
2022-08-12 15:32:27 +08:00
Rui Ueyama 5dd122710c [ELF] Export symbols defined by --defsym
Previously, symbols defined by `--defsym` would never be exported
because we created a dynamic symbol table before processing `--defsym`.
2022-08-12 15:30:24 +08:00
Clemens Wasser 71c8e5d8c0 Fix narrowing conversions for Windows
Signed-off-by: Clemens Wasser <[email protected]>
2022-08-11 18:03:31 +02:00
Clemens Wasser cd46476c76 Fix mold-wrapper for Windows
Signed-off-by: Clemens Wasser <[email protected]>
2022-08-11 17:32:07 +02:00
Clemens Wasser 965b562cdf Fix CMake for Windows
Signed-off-by: Clemens Wasser <[email protected]>
2022-08-11 17:32:07 +02:00
Clemens Wasser f7703dacd8 Port errno_string to Windows
Signed-off-by: Clemens Wasser <[email protected]>
2022-08-11 17:32:07 +02:00
Clemens Wasser 1c75580a24 Port TimerRecord to Windows
Signed-off-by: Clemens Wasser <[email protected]>
2022-08-11 17:32:07 +02:00
Clemens Wasser 61dc019c05 Port MappedFile to Windows
Signed-off-by: Clemens Wasser <[email protected]>
2022-08-11 17:32:07 +02:00
Clemens Wasser a681e3add3 Fix stderr fileno for Windows
Signed-off-by: Clemens Wasser <[email protected]>
2022-08-11 17:32:07 +02:00
Clemens Wasser fde9e852f3 Port dynamic loading to Windows
Signed-off-by: Clemens Wasser <[email protected]>
2022-08-11 17:32:07 +02:00
Clemens Wasser e8a915e0dd Port bswap to Windows
Signed-off-by: Clemens Wasser <[email protected]>
2022-08-11 17:32:07 +02:00
Clemens Wasser 996dc7c9a3 Port mmap failure signal handler to Windows
Signed-off-by: Clemens Wasser <[email protected]>
2022-08-11 17:32:07 +02:00
Clemens Wasser 6df1f30750 Port SHA256 to Windows using CNG
Signed-off-by: Clemens Wasser <[email protected]>
2022-08-11 17:32:07 +02:00
Clemens Wasser c8af11588a Disable C++ demangling on Windows
Signed-off-by: Clemens Wasser <[email protected]>
2022-08-11 17:28:11 +02:00
Clemens Wasser 246f1a1b49 Fixes for Windows string_view impl
Signed-off-by: Clemens Wasser <[email protected]>
2022-08-11 17:28:11 +02:00
Clemens Wasser 08a83c7aaa Disable includes unavailable on Windows
Signed-off-by: Clemens Wasser <[email protected]>
2022-08-11 17:28:11 +02:00
Rui Ueyama ee68d1f2ff Add a SHA256 fallback implementation
We are going to use this implementation on Windows.
2022-08-11 21:30:13 +08:00
Rui Ueyama 8524705a48 Remove a disclaimer to support CMake officially 2022-08-11 20:44:44 +08:00
Rui Ueyama 2ade8e950f Change test names 2022-08-11 18:07:56 +08:00
Rui Ueyama daf2a5173c Add more tests for CMake 2022-08-11 17:56:12 +08:00
Rui Ueyama ae2e177cb4 Simplify 2022-08-11 17:34:55 +08:00
Rui Ueyama a7d09b7149 Add a new CMake option to disable tests that use QEMU 2022-08-11 17:16:23 +08:00
Rui Ueyama 051dbc0bef Install ld.mold.1 man page as an alias to mold.1
Fixes https://github.com/rui314/mold/issues/607
2022-08-10 22:26:38 +08:00
Rui Ueyama c3a86f5b24 Remove MOLD_DEBUG_ARM64_ONLY
Because it's a developer option, and I'm not using it.
2022-08-10 16:51:07 +08:00
Rui Ueyama d8ad53cc6d Add developer options for faster compilation 2022-08-10 16:43:58 +08:00
Rui Ueyama b1fff13659 [ELF][RISC-V] As per the psABI, this is the correct alignment requirement 2022-08-10 16:33:36 +08:00
Rui Ueyama 78e676cdd0 Fix for ARM64 2022-08-10 15:08:48 +08:00
Rui Ueyama 46be0e152b Fix a build issue on macOS 2022-08-10 15:08:30 +08:00
Rui Ueyama b55cd9be14 Fix build errors 2022-08-10 14:29:37 +08:00
Rui Ueyama 16752b82a3 Do not always link the main executable 2022-08-10 14:29:04 +08:00
Rui Ueyama 5dbb996b36 Format 2022-08-09 22:25:32 +08:00
Rui Ueyama d0e2437f4a Disable exceptions
We are not using exceptions in mold.
2022-08-09 22:24:59 +08:00
Rui Ueyama b16306806d [ELF] Do not export a local symbol even if referenced by a DSO
Fixes https://github.com/rui314/mold/issues/606
2022-08-09 17:28:59 +08:00
Rui Ueyama 910c82a4f4 Remove dead code 2022-08-09 17:22:49 +08:00
Rui Ueyama 3a1a8b0dc7 Fix indentation 2022-08-08 21:54:50 +08:00
Rui Ueyama 635c017400 Run native tests without Qemu 2022-08-08 21:54:06 +08:00
Rui Ueyama b2c6206aa9 Add a script to create a .cc file containing the current git hash 2022-08-08 20:31:02 +08:00
Rui Ueyama e15cdbfad2 Simplify 2022-08-08 17:42:00 +08:00
Rui Ueyama f7f257a663 Specify target name 2022-08-08 17:30:53 +08:00
Rui Ueyama 415673a49e Simplify 2022-08-08 17:20:24 +08:00
Rui Ueyama 0c1c48c3b2 Use CMake in dist.sh 2022-08-08 17:11:32 +08:00
Rui Ueyama eab4f663bf Fix /usr/libexec/mold/ld symlink 2022-08-08 16:40:21 +08:00
Rui Ueyama 3041714a16 Update to LLVM 15 and OpenSSL 3.0.5 2022-08-08 14:03:56 +08:00
Rui Ueyama c622da4a62 Add MOLD_LTO 2022-08-08 13:35:18 +08:00
Rui Ueyama 6ed017919e Build vendored zlib if zlib is not found 2022-08-08 13:01:52 +08:00
Rui Ueyama eee1888d2c Remove a cmake-generated file 2022-08-08 12:47:04 +08:00
Rui Ueyama b6eaa8505b Add MOLD_USE_ASAN and MOLD_USE_TSAN 2022-08-08 12:33:18 +08:00
Rui Ueyama a998d5e845 Simplify 2022-08-08 12:10:01 +08:00
Rui Ueyama 83e4163ed4 Remove make test as we can just run ctest 2022-08-08 11:23:36 +08:00
Rui Ueyama 6a613a12e2 Simplify 2022-08-08 11:22:57 +08:00
Rui Ueyama 3987ebaf34 Add -DMOLD_USE_MOLD=O~n/OFF 2022-08-08 11:11:26 +08:00
Rui Ueyama f8a1d6b3f1 Change tab width from 4 to 2 2022-08-08 11:04:08 +08:00
Rui Ueyama 863c454979 Add a note about the OneTBB patch
Follow-up for https://github.com/rui314/mold/commit/add94b86266b40bc66789e26358675da9d603919
2022-08-07 19:50:50 +08:00
Rui Ueyama 023f1f3596 Make test scripts to print out command trace 2022-08-07 15:41:12 +08:00
Rui Ueyama 00e7119440 Simplify 2022-08-07 15:34:13 +08:00
Rui Ueyama cdd10d4e2e Add a CI for macOS with cmake 2022-08-07 14:25:15 +08:00
Rui Ueyama a937e8fe55 Fix Mach-O tests 2022-08-07 14:16:17 +08:00
Rui Ueyama 3ee4611640 Refactor 2022-08-07 14:14:40 +08:00
Rui Ueyama 76b1845732 Remove unnecessary commands 2022-08-07 13:52:53 +08:00
Rui Ueyama 1a041ffc62 Add to zlib to third-party
We are going to use it on Windows.
2022-08-07 13:42:06 +08:00
Rui Ueyama b3bc37c479 Fix CI 2022-08-07 13:03:24 +08:00
Rui Ueyama 2ea5bd3b02 Add a CI for cmake build 2022-08-07 12:30:21 +08:00
Rui Ueyama 56e82cfc50 Fix test failures
foo_host and foo_x86_64 tests should run simultaneously as
they are the same.
2022-08-07 12:27:29 +08:00
Rui Ueyama fb91f8dfc2 Do not link against external tbb and mimalloc by default
We do not want to change the default settings when we migrate to
cmake.

Also fixed build failures.
2022-08-07 11:16:56 +08:00
Rui Ueyama d4ccc10ed4 Merge pull request #571 from clemenswasser/cmake
CMake Port
2022-08-07 10:37:44 +08:00
Rui Ueyama add94b8626 Specify relative full paths for rust-demangle.h and xxhash.h
They are always in the same directory relative to the top directory,
so we can just specify their relative paths.
2022-08-06 21:24:30 +08:00
Rui Ueyama 6d4f228238 Merge pull request #601 from friendlyanon/feature/third-party-includes
Include only what is needed from third-party
2022-08-06 21:17:12 +08:00
friendlyanon b42d039bd5 Include only what is needed from third-party
Both rust-demangle and xxhash provide usage instructions with include
paths that do not have an extra directory component. This patch fixes
uses of these libraries in the code and avoids including the third-party
directory wholesale.

Signed-off-by: friendlyanon <[email protected]>
2022-08-06 14:59:40 +02:00
Rui Ueyama 12ec2b8acc [Mach-O] -framework in LC_LINKER_OPTION can be missing 2022-08-06 13:02:37 +08:00
Rui Ueyama 637e05bed3 [Mach-O] Allow past-the-end relocations
Relocation may refer past-the-end location of a section. Therefore,
if section X is [0, 0x1000) and section Y is [0x1000, 0x2000), we
can't determine whether a relocation refers address 0x1000 refers
X or Y. We need to pass a section index.
2022-08-06 11:54:12 +08:00
Rui Ueyama e30e8e363a git is no longer a build-time dependency 2022-08-06 08:57:24 +08:00
Rui Ueyama 4739e5f724 Add Pop!_OS
https://github.com/rui314/mold/pull/599
2022-08-06 08:53:01 +08:00
Rui Ueyama 5f8609fe7a Improve error message 2022-08-05 14:47:30 +08:00
Rui Ueyama 76cb2f917f git is no longer a build dependency 2022-08-05 14:40:41 +08:00
Rui Ueyama 627cb0664c Avoid possible accidental prefix match 2022-08-05 14:40:15 +08:00
Rui Ueyama 9bcf974a11 [ELF][RISC-V] Remove deprecated relocations 2022-08-05 13:50:00 +08:00
Rui Ueyama a8345e83c5 [ELF][RISC-V] Make calling an undefined weak symbol an infinite loop 2022-08-05 12:14:08 +08:00
Rui Ueyama 7177f7442b Bump mold version to 1.4.0 2022-08-04 20:47:21 +08:00
Rui Ueyama da2f3fe12c Revert "Re-submit b23c47ae6dbe285e02627803eac0604cfaaa1e78"
This reverts commit a70b1eb58b because
it's too risky to include it into 1.4.0.
2022-08-04 20:46:09 +08:00
Rui Ueyama 10c211f1ab [ELF] Fix a regression for libprotobuf
The `extern "C"` directive in a version script should match a non-C++
mangled name for the sake of compatibility with other linkers. It
caused a regression for libprotobuf.
2022-08-04 18:15:05 +08:00
Rui Ueyama 135e6465c9 Add sponsor names 2022-08-04 15:22:45 +08:00
Rui Ueyama d3cf7cd239 Fix CI 2022-08-04 14:57:10 +08:00
Rui Ueyama 989dacf6d2 [ELF] Fix st_shndx for linker-synthesized symbols 2022-08-04 13:43:12 +08:00
Rui Ueyama 04e3492d77 [ELF] Define __dso_handle only if undefined
GCC 11's crtbeginS.o contains `__dso_handle` as a word-size data
which cnotains its own address as a value. Overriding that symbol
programs that depend on the value to crash.

This fix is needed for 7zip.
2022-08-04 13:41:18 +08:00
Rui Ueyama 456071dafd [ELF][RISCV] Write nops for R_RISCV_ALIGN 2022-08-03 20:35:11 +08:00
Rui Ueyama ab93c8c1e3 [ELF][RISCV32] Use lw instead of ld for RV32 2022-08-03 17:54:19 +08:00
Rui Ueyama 2a8eb67089 [Mach-O] Support LOH_ARM64_ADRP_ADRP 2022-08-03 14:04:26 +08:00
Rui Ueyama aa78db8193 Refactor 2022-08-03 13:16:17 +08:00
Rui Ueyama 1709fc3ca0 [Mach-O] Support LOH_ARM64_ADRP_LDR 2022-08-03 12:47:20 +08:00
Rui Ueyama 7d91674a98 Refactor 2022-08-03 11:29:16 +08:00
Rui Ueyama 8f2768904f [ELF] Add supportec targets to the -V message 2022-08-03 10:54:31 +08:00
Rui Ueyama 04dc1a74db Style fix 2022-08-02 16:36:28 +08:00
Rui Ueyama d553cea606 Refactor 2022-08-02 15:56:53 +08:00
Rui Ueyama 71886e5e2a Fix CI 2022-08-02 13:23:58 +08:00
Rui Ueyama d9db6bcf49 [ELF] Add an initial 32-bit RISC-V support 2022-08-02 13:12:05 +08:00
Rui Ueyama 123dde1e8f Refactor 2022-08-01 17:13:53 +08:00
Rui Ueyama 172c7f6bb4 Refactor 2022-08-01 17:11:21 +08:00
Rui Ueyama 0ce50e1cf8 Refactor 2022-08-01 16:11:45 +08:00
Rui Ueyama 1121879199 Refactor 2022-08-01 14:09:59 +08:00
Rui Ueyama d2829e1806 Simplify 2022-08-01 11:03:29 +08:00
Rui Ueyama 0988565c2f Simplify 2022-08-01 10:28:30 +08:00
Rui Ueyama a70b1eb58b Re-submit b23c47ae6d
With a fix for a bug in my previous test script.
2022-08-01 09:38:12 +08:00
Rui Ueyama bb999c5e93 Revert "[ELF] Garbage-collect C identifier sections"
This reverts commit b23c47ae6d because
it broke CI.
2022-07-31 20:56:37 +08:00
Rui Ueyama b23c47ae6d [ELF] Garbage-collect C identifier sections
If a section name is valid as a C identifier (i.e. it doesn't start
with '.' and doesn't contain any punctuators), the linker automatically
creates new symbols by prepending `__start_` and `__stop_` to the
section name.

Previously, we conservatively keep such C identifier sections during
garbage collection. However, since `__start_` and `__stop_` symbols
are hidden symbols, if we do not have `__start_foo` or `__stop_foo`,
we can safely discard section `foo`.
2022-07-31 20:26:31 +08:00
Rui Ueyama 8cd699c110 Remove dead code 2022-07-31 17:29:42 +08:00
Rui Ueyama e3495efad7 Refactor 2022-07-31 16:51:06 +08:00
Rui Ueyama 5414ae7a36 Merge pull request #593 from ishitatsuyuki/issue-592
[elf] Align .symtab's ELF attributes with .dynsym's.
2022-07-31 12:13:19 +08:00
Rui Ueyama bc81472adb [ELF] Fix TSAN issue
Fixes https://github.com/rui314/mold/issues/594
2022-07-31 10:59:51 +08:00
Tatsuyuki Ishi b83cb924a5 [elf] Rename write_symtab to export_to_symtab.
write_symtab feels like we're writing to the input file, which isn't the
case. export_to_symtab conveys the meaning clearer.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-07-30 22:30:57 +09:00
Tatsuyuki Ishi 536e0e7dca [elf] Align .symtab's ELF attributes with .dynsym's.
Notably, we previously wrote symbols that have been demoted into STB_LOCAL
as STB_GLOBAL. Fix this by using a logic shared with dynsym to determine
the output attributes.

Also reorder the symbols since the LOCAL ones need to come first.

Found in Mesa test suite.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-07-30 22:30:57 +09:00
Tatsuyuki Ishi b19599f81d [elf] Extract computation of output esym into a helper.
Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-07-30 22:30:57 +09:00
Tatsuyuki Ishi bbbda7925e [elf] Add helper to determine if a symbol is STB_LOCAL in output.
Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-07-30 22:30:57 +09:00
Rui Ueyama 467294eb17 [Mach-O] Allow only one LC_SEGMENT_64 command 2022-07-30 16:44:58 +08:00
Rui Ueyama 13ef968fb1 [Mach-O] Do not read the same library or framework more than once 2022-07-30 14:59:11 +08:00
Rui Ueyama 3deaafca30 [Mach-O] Improve relaxation 2022-07-30 13:03:38 +08:00
Rui Ueyama f998104bb0 [Mach-O] Respect N_NO_DEAD_STRIP for executables 2022-07-30 12:39:54 +08:00
Rui Ueyama 4a73ca28ca Fix CI 2022-07-29 21:37:23 +08:00
Rui Ueyama 070907e367 [Mach-O] Allow LC_LINKER_OPTIONS to refer a missing library 2022-07-29 21:24:54 +08:00
Rui Ueyama ed72ca3509 [Mach-O] Change our tool magic number 2022-07-29 21:24:19 +08:00
Rui Ueyama 5a493d4868 [Mach-O] Use 16 KiB page for code signature on ARM64
Based on https://github.com/rui314/mold/pull/536
2022-07-29 12:51:06 +08:00
Rui Ueyama b2b16e66b4 [Mach-O] Add -no_function_starts 2022-07-29 10:48:12 +08:00
Rui Ueyama 4ed7d72372 Update a comment 2022-07-29 09:50:24 +08:00
Rui Ueyama 8143fe2fcc [Mach-O] Fix a relaxation bug 2022-07-29 09:13:18 +08:00
Rui Ueyama 65b0aec355 Update comments 2022-07-28 23:47:55 +08:00
Rui Ueyama 12f941c58d [Mach-O] Improve LOH_ARM64_ADRP_LDR_GOT_LDR 2022-07-28 17:15:37 +08:00
Rui Ueyama 08cf940a7a Merge pull request #590 from alexfanqi/main
[ELF][RISCV] add R_RISCV_SET32 in EhFrameSection
2022-07-28 15:35:27 +08:00
Rui Ueyama 0e1b60dd6e [Mach-O] Fix mis-optimization bug for LOH_ARM64_ADRP_LDR_GOT_LDR 2022-07-28 15:18:14 +08:00
Alex Fan 68bd00caa7 [ELF][RISCV] add missing R_RISCV_SET32 in EhFrameSection
Signed-off-by: Alex Fan <[email protected]>
2022-07-28 16:36:23 +10:00
Rui Ueyama d10b129b13 Update comments 2022-07-27 20:13:37 +08:00
Rui Ueyama bedbb1f4e5 [Mach-O] Fix LOH_ARM64_ADRP_ADD 2022-07-27 19:51:14 +08:00
Rui Ueyama 59f4197edb [Mach-O] Add asserts 2022-07-27 19:17:42 +08:00
Rui Ueyama 390d0dc634 Refactor 2022-07-27 18:04:01 +08:00
Rui Ueyama 8fb3082529 [Mach-O] Support LOH_ARM64_ADRP_ADD 2022-07-27 17:43:15 +08:00
Rui Ueyama f81337cc8b [ELF][RISC-V] Do not assume HI20 relocs appear before LO12 relocs
This re-commits 990b0ac4cd.

As clarified in
https://github.com/riscv-non-isa/riscv-elf-psabi-doc/issues/303,
the order of HI20 and LO12 relocations can be arbitrary.
2022-07-27 17:41:47 +08:00
Rui Ueyama 62ee59ecc6 [Mach-O] Fix bugs in linker relaxation 2022-07-27 16:24:16 +08:00
Rui Ueyama 54399fe567 [Mach-O] Support LOH_ARM64_ADRP_LDR_GOT_LDR 2022-07-27 14:45:13 +08:00
Rui Ueyama d3d0c81bc6 [Mach-O] Fix typo 2022-07-25 19:54:09 +08:00
Rui Ueyama 18063f3ac9 [Mach-O] Add -mark_dead_strippable_dylib 2022-07-25 18:12:39 +08:00
Rui Ueyama 5b8f9f1a7a [Mach-O] Respect MH_NO_REEXPORTED_DYLIBS 2022-07-25 17:13:47 +08:00
Rui Ueyama a6d9955a08 Add a comment 2022-07-25 17:02:42 +08:00
Rui Ueyama 92cbaada34 [Mach-O] Improve -dead_strip performance 2022-07-25 16:03:40 +08:00
Rui Ueyama e9b07df4fb [Mach-O] Parallelize -dead_strip 2022-07-25 15:49:22 +08:00
Rui Ueyama 04909597e9 Fix CI 2022-07-25 12:11:24 +08:00
Rui Ueyama e2c43dcb5e [Mach-O] Report an error for unknown export types 2022-07-25 11:39:42 +08:00
Rui Ueyama 853d1f8a6f [Mach-O] Do not load the same dylib more than once 2022-07-24 20:22:28 +08:00
Rui Ueyama 339eeef435 Simplify 2022-07-24 18:16:10 +08:00
Rui Ueyama 4a21d5dca3 80 columns 2022-07-24 17:46:31 +08:00
Rui Ueyama b824f73912 [Mach-O] Handle EXPORT_SYMBOL_FLAGS_REEXPORT in an export trie 2022-07-24 13:51:01 +08:00
Rui Ueyama b5ed4a67cb [Mach-O] Support -bundle_loader 2022-07-23 20:22:50 +08:00
Rui Ueyama 0a57aec7bb [Mach-O] Use ctx.output_type instead of ctx.arg.dylib 2022-07-23 20:22:50 +08:00
Rui Ueyama e0906564dc Merge pull request #588 from ishitatsuyuki/dynamic-list
[elf] Implement --export-dynamic-symbol[-list].
2022-07-23 20:20:19 +08:00
Tatsuyuki Ishi 1c8778b6e3 [elf] Add test for globs in --dynamic-list, --export-dynamic-symbol-list.
Glob and function names are inherited from version-script14.sh.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-07-23 19:21:19 +09:00
Tatsuyuki Ishi 58f393ac8b [elf] Add test for --export-dynamic-symbol[-list].
Piggybacked on existing tests for --dynamic-list.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-07-23 19:21:19 +09:00
Tatsuyuki Ishi cd25f6b6c7 [elf] Update man for --dynamic-list, --export-dynamic-symbol[-list].
Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-07-23 19:21:19 +09:00
Tatsuyuki Ishi e115aaef4c [elf] Implement --export-dynamic-symbol[-list].
Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-07-23 19:21:19 +09:00
Tatsuyuki Ishi 85217abc7a [elf] Refactor input file argument parser to use string_view.
parse_dynamic_glob requires the passed string_view to be alive across the
entire linker invocation, so we need to avoid cloning for that.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-07-23 19:21:19 +09:00
Tatsuyuki Ishi b48ff94ad1 [elf] Extract dynamic-list glob handling to a helper function.
To ease handling indivudual globs passed through --export-dynamic-symbol.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-07-23 19:21:19 +09:00
Tatsuyuki Ishi 23b43d29c5 [elf] Assume -Bsymbolic when --dynamic-list is specified.
According to lld, --dynamic-list "implies -Bsymbolic but does not set
DF_SYMBOLIC" when creating shared objects. Follow suit in this patch.

Note that we don't set DF_SYMBOLIC currently, and arg.Bsymbolic has no
effect on non-shared objects.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-07-23 19:21:18 +09:00
Rui Ueyama c0e4de2feb Fix CI 2022-07-23 14:21:36 +08:00
Rui Ueyama 5900ee701c [Mach-O] Minimize alignments 2022-07-23 13:59:43 +08:00
Rui Ueyama fe09596d7c [Mach-O] Merge __TEXT,__StaticInit to __TEXT,__text 2022-07-22 20:47:57 +08:00
Rui Ueyama c1195691f3 [Mach-O] Do not ignore N_NO_DEAD_STRIP for local symbols 2022-07-22 16:19:16 +08:00
Rui Ueyama bb1d2147a7 [Mach-O] Merge __objc_imageinfo sections 2022-07-21 22:29:40 +08:00
Rui Ueyama b80b94bd96 [Mach-O] Do not create empty subsections 2022-07-21 19:38:08 +08:00
Rui Ueyama ff087b26a2 [Mach-O] Rename a variable 2022-07-19 14:48:14 +08:00
Clemens Wasser 0a73cf1bfc Port CTest
Signed-off-by: Clemens Wasser <[email protected]>
2022-07-18 18:58:17 +02:00
Clemens Wasser 3c56ade25f Port CPack
Signed-off-by: Clemens Wasser <[email protected]>
2022-07-18 18:58:17 +02:00
Clemens Wasser e6a0e67a38 Port CMake
Signed-off-by: Clemens Wasser <[email protected]>
2022-07-18 18:58:17 +02:00
Rui Ueyama d7b42f29f2 [Mach-O] Define ${section,segment}${start,end}$ symbols. 2022-07-18 19:39:11 +08:00
Rui Ueyama 852bd457d8 [Mach-O] Fix thread-local variable handling 2022-07-18 19:39:11 +08:00
Rui Ueyama b66e55c7e5 [Mach-O] Do not dead-strip module initializers 2022-07-18 19:39:11 +08:00
Rui Ueyama 3ba30f7f09 Create a test workdir for each machine/test pair 2022-07-18 15:25:56 +08:00
Rui Ueyama f6a85acc07 Revert "[ELF][RISC-V] Do not assume HI20 relocs appear before LO12 relocs"
This reverts commit 990b0ac4cd.

This is what the RISC-V psABI says

> The R_RISCV_HI20 refers to an LUI instruction containing the high
> 20-bits to be relocated to an absolute symbol address. The LUI
> instruction is followed by an I-Type instruction (add immediate or
> load) with an R_RISCV_LO12_I relocation or an S-Type instruction
> (store) and an R_RISCV_LO12_S relocation. The addresses for pair of
> relocations are calculated like this:

I read LO12 is guaranteed to always appear after HI20.
2022-07-18 13:16:47 +08:00
Rui Ueyama 990b0ac4cd [ELF][RISC-V] Do not assume HI20 relocs appear before LO12 relocs 2022-07-16 19:51:46 +08:00
Rui Ueyama 29af9b5177 Unmask amd64 2022-07-16 19:51:46 +08:00
Rui Ueyama a8559be1b1 Merge pull request #586 from ltratt/doc_fix
Fix spelling of "support".
2022-07-16 15:33:48 +08:00
Laurence Tratt c8b0522fa6 Fix spelling of "support".
Signed-off-by: Laurence Tratt <[email protected]>
2022-07-16 07:59:56 +01:00
Rui Ueyama 70d5f60aa1 Make pacman non-interactive 2022-07-14 13:44:40 +08:00
Rui Ueyama 4335732d5d Relax version match conditions 2022-07-14 13:02:46 +08:00
Rui Ueyama fc98d73d7e Add an assertion 2022-07-14 13:01:25 +08:00
Rui Ueyama 1275b80eb8 Merge pull request #585 from aslikr42069/add-arch-linux-support-install-deps
Add support to install deps on Arch
2022-07-14 13:01:19 +08:00
aslikr42069 dabe283e4a Add support to install deps on Arch 2022-07-13 21:54:09 -05:00
Rui Ueyama b8a4df51ca [ELF][LTO] Support GCC's LDPT_GET_API_VERSION 2022-07-13 15:29:15 +08:00
Rui Ueyama 2af7e73258 Fix CI 2022-07-12 21:13:05 +08:00
Rui Ueyama ed7e7c7ae0 Merge makefiles for tests into the main makefile 2022-07-12 21:03:54 +08:00
Rui Ueyama 152ee5a261 Update comments 2022-07-12 16:38:47 +08:00
Rui Ueyama b33c3eb010 Refactor 2022-07-12 16:31:37 +08:00
Rui Ueyama 5a190422cd Add comments 2022-07-12 16:08:27 +08:00
Rui Ueyama c4eefe267d Refactor 2022-07-12 15:52:56 +08:00
Rui Ueyama b8dbaff539 Refactor 2022-07-12 13:11:56 +08:00
Rui Ueyama dd32de31c8 [ELF][ARM32] Ignore R_ARM_V4BX
R_ARM_V4BX is a marker relocation which we can savely ignore.

Fixes https://github.com/rui314/mold/issues/582
2022-07-12 12:33:02 +08:00
Rui Ueyama 9f771dd129 Add comments 2022-07-12 12:33:01 +08:00
Rui Ueyama 764d7575f7 [ELF] Define __dso_handle
`cxa_finalize` takes `__dso_handle` to uniquify identify an ELF module
in memory. Its actual location doesn't matter but needs to be different
for each ELF module.

Fixes https://github.com/rui314/mold/issues/507
2022-07-11 18:01:23 +08:00
Rui Ueyama 04efce2d29 Refactor 2022-07-11 15:52:51 +08:00
Rui Ueyama e3e371dcd3 [ELF][x86-64] Always create PLT entries starting with ENDBR64
This change removes the compact PLT and non-IBT PLT and replaces
them with IBT PLT. The compact PLT wasn't compatible with `-z ibtplt`.

Fixes https://github.com/rui314/mold/issues/581
2022-07-11 13:59:32 +08:00
Rui Ueyama 4f1211f7fb Refactor 2022-07-10 20:17:47 +08:00
Rui Ueyama 4d3e59f716 Refactor 2022-07-10 17:53:09 +08:00
Rui Ueyama 816835e25c Update comments 2022-07-10 17:26:15 +08:00
Rui Ueyama 01f89730e1 Simplify 2022-07-10 16:31:01 +08:00
Rui Ueyama 55379e1142 Fix tests for i686
Fixes https://github.com/rui314/mold/issues/579
2022-07-10 12:40:25 +08:00
Rui Ueyama df3f6999a3 [Mach-O] Support NO_DEAD_STRIP attributes 2022-07-09 19:59:29 +08:00
Rui Ueyama d9cb06c270 Merge pull request #576 from wangp/musl-execvp
Fix run.sh failure on musl
2022-07-09 19:51:53 +08:00
Peter Wang 31d2a902e5 Fix run.sh failure on musl
The command "mold -run foo.ld" can fail when foo.ld is an empty file.
The musl execvp() call fails with "Exec format error" when the script
to be executed is empty. The fix is to create foo.ld with a valid
Unix shebang.

Signed-off-by: Peter Wang <[email protected]>
2022-07-09 17:37:57 +10:00
Rui Ueyama 593f2cfdbc Do not run LTO tests if LTO is not supported
Fixes https://github.com/rui314/mold/issues/577
2022-07-09 15:30:38 +08:00
Rui Ueyama 6a44eadd2f Use install-build-deps.sh to set up CI environments 2022-07-09 12:55:51 +08:00
Rui Ueyama 0cc9715df1 Merge pull request #575 from ZhongRuoyu/fix-ci
Fix double git installation in CI workflow
2022-07-09 12:47:43 +08:00
Zhong Ruoyu 50ccbcb444 Fix double git installation in CI workflow
Signed-off-by: Zhong Ruoyu <[email protected]>
2022-07-09 12:38:55 +08:00
Rui Ueyama 12c9c53b90 Add a test 2022-07-08 22:51:05 +08:00
Rui Ueyama 4a9e1439f6 [Mach-O] Rename more sections for compatibility with Apple's ld 2022-07-07 17:13:43 +08:00
Rui Ueyama 4aca9d471e Fix CI 2022-07-07 14:27:57 +08:00
Rui Ueyama af4ef3c76c [Mach-O] Merge all S_CSTRING_LITERALS sections
__TEXT,__cstring is one of such sections.
2022-07-07 14:01:18 +08:00
Rui Ueyama a4cc7f358a [Mach-O] Do not create a baserel for a subtracted relocation 2022-07-06 20:59:05 +08:00
Rui Ueyama be5fe4c727 [Mach-O] Map __DATA,__const to __DATA_CONST,__const 2022-07-06 17:22:52 +08:00
Rui Ueyama 5634b2287a [Mach-O] -ObjC should force-load any section starting with __swift 2022-07-06 15:27:41 +08:00
Rui Ueyama 5d5f75c41d [Mach-O] Fix undefined behavior
A new item may be added to `ctx.objs` in this loop, so we can't
use the range-based for loop here.
2022-07-05 15:55:28 +08:00
Rui Ueyama 256c467b05 [Mach-O] Mark symbols referenced by compact unwind records 2022-07-05 15:55:28 +08:00
Rui Ueyama 5071c42e72 Add a test for Rust demangler
https://github.com/rui314/mold/issues/371
2022-07-05 15:09:44 +08:00
Rui Ueyama 15f1139c31 [Mach-O] Allow compact unwind entry to refer global symbol 2022-07-05 10:52:15 +08:00
Rui Ueyama d6526ed9da [Mach-O] Allow more than one LC_LINKER_OPTION commands 2022-07-05 10:52:15 +08:00
Rui Ueyama 4928e3276d [Mach-O] Do not report unknown symbol types as errors
There are bunch of symbol types for debug symbols.
2022-07-05 10:52:15 +08:00
Rui Ueyama 2bfc724e42 [Mach-O] Add -dependency_info 2022-07-05 10:52:15 +08:00
Rui Ueyama 8bd705a3cb [Mach-O] Support X86_64_RELOC_SUBTRACTOR 2022-07-05 10:52:15 +08:00
Rui Ueyama 691892f48a Add a comment 2022-07-04 20:47:49 +08:00
Rui Ueyama 22e1bba58e Attempt to demangle as a Rust symbol before as C++
https://github.com/rui314/mold/issues/371
2022-07-04 20:42:06 +08:00
Rui Ueyama 808f1240ac Demangle Rust symbols
https://github.com/rui314/mold/issues/371
2022-07-04 19:28:55 +08:00
Rui Ueyama 0e88fd047e Add extern "C" 2022-07-04 19:28:55 +08:00
Rui Ueyama 80dc5cf1fb Merge commit '53e07d3a89bffe34a8d48136e25514e41d523d27' as 'third-party/rust-demangle' 2022-07-04 19:28:35 +08:00
Rui Ueyama 53e07d3a89 Squashed 'third-party/rust-demangle/' content from commit eed29f577
git-subtree-dir: third-party/rust-demangle
git-subtree-split: eed29f57732ddb2be434ec89f8ede9b695e5e157
2022-07-04 19:28:35 +08:00
Rui Ueyama 5fe5294133 Merge pull request #569 from sicherha/fix_compiler_warnings
Fix more compiler warnings
2022-07-04 15:37:29 +08:00
Christoph Erhardt 4af6ff614c Reorder segment_table member variables in bundled TBB library
This fixes a warning produced by GCC 12 related to the initialisation
order of the class members.

For details, see the discussion at
https://github.com/oneapi-src/oneTBB/issues/823. The concern about
potential ABI incompatibility is not relevant for a private bundled
library.

Signed-off-by: Christoph Erhardt <[email protected]>
2022-07-04 08:05:52 +02:00
Christoph Erhardt eaf52ee844 Use GCC-compatible unroll pragma
This notation is supported by both GCC and Clang, whereas
`#pragma unroll` is only supported by Clang.

Signed-off-by: Christoph Erhardt <[email protected]>
2022-07-04 08:05:51 +02:00
Christoph Erhardt ab81c9e51d Forward-declare ARM64 and X86_64 as struct, not as class
Clang warns that forward-declaring a `struct` as `class`
'is valid, but may result in linker errors under the Microsoft C++ ABI'.

Signed-off-by: Christoph Erhardt <[email protected]>
2022-07-04 08:05:14 +02:00
Christoph Erhardt 95d0af2958 Remove superfluous const qualifier
This fixes a compiler warning that occurs when building with `-Wextra`.

Signed-off-by: Christoph Erhardt <[email protected]>
2022-07-04 08:05:14 +02:00
Christoph Erhardt 9056011f67 Suppress 'unused function' compiler warning
Signed-off-by: Christoph Erhardt <[email protected]>
2022-07-04 08:05:12 +02:00
Rui Ueyama ff45af0f34 Refactor 2022-07-04 13:52:34 +08:00
Christoph Erhardt 4df062c72e Remove unused variable
Signed-off-by: Christoph Erhardt <[email protected]>
2022-07-03 21:58:10 +02:00
Rui Ueyama 721c59aea9 Simplify 2022-07-03 21:54:08 +08:00
Rui Ueyama 9ddbfa863a Refactor 2022-07-03 17:35:26 +08:00
Rui Ueyama cd2b8f93e4 Merge pull request #568 from sicherha/fix_compiler_warnings
Fix a few compiler warnings
2022-07-03 16:57:25 +08:00
Christoph Erhardt e2e025ed29 Remove unused #include and unused variable
This fixes an 'unused function' and an 'unused variable' warning.

Signed-off-by: Christoph Erhardt <[email protected]>
2022-07-03 09:18:59 +02:00
Christoph Erhardt 15441b60d3 Fix typos: missing colon and ampersand
Signed-off-by: Christoph Erhardt <[email protected]>
2022-07-03 09:16:08 +02:00
Rui Ueyama 71c4c90922 Simplify 2022-07-02 21:10:29 +08:00
Rui Ueyama 8d29501887 [Mach-O] Ignore OS name part of targets in TBD files 2022-07-02 13:57:12 +08:00
Rui Ueyama ea308bc77b [Mach-O] Add -add_ast_path 2022-07-02 13:52:19 +08:00
Rui Ueyama ad427f8629 [ELF] Skip one more test if musl
https://github.com/rui314/mold/issues/565
2022-07-01 19:05:32 +08:00
Rui Ueyama 23b90dfc38 [ELF] Detect musl libc in a more reliably way
Fixes https://github.com/rui314/mold/issues/565
2022-07-01 18:00:19 +08:00
Rui Ueyama 7f7d10cc89 Bump mold version to 1.3.1 2022-07-01 16:15:37 +08:00
Rui Ueyama 14392e7720 Fix a test for musl 2022-07-01 09:51:32 +08:00
Rui Ueyama 3b75398786 [ELF] Support .preinit_array
It looks like AddressSanitizer depends on this feature.

Fixes https://github.com/rui314/mold/issues/562
2022-06-30 16:29:50 +08:00
Rui Ueyama 5688115bb6 Merge pull request #561 from jpalus/armv6-libatomic
armv6 needs linking with libatomic as well
2022-06-29 20:58:42 +08:00
Jan Palus b1d951a96f armv6 needs linking with libatomic as well
mold: error: undefined symbol: __atomic_fetch_add_8
>>> referenced by compress.cc
>>>               out/compress.o:(void tbb::detail::d1::fold_tree<tbb::detail::d1::tree_node>(tbb::detail::d1::node*, tbb::detail::d1::execution_data const&))>>> referenced by arch-arm64.cc
>>>               out/elf/arch-arm64.o:(tbb::detail::d2::for_each_root_task_base<__gnu_cxx::__normal_iterator<mold::elf::InputSection<mold::elf::ARM64>**, std::span<mold::elf::InputSection<mold::elf::ARM64>*, 4294967295u> >, mold::elf::create_range_extension_thunks(mold::elf::Context<mold::elf::ARM64>&, mold::elf::OutputSection<mold::elf::ARM64>&)::{lambda(mold::elf::InputSection<mold::elf::ARM64>*)#2}, mold::elf::InputSection<mold::elf::ARM64>*>::cancel(tbb::detail::d1::execution_data&))>>> referenced by arch-arm64.cc
>>>               out/elf/arch-arm64.o:(tbb::detail::d2::for_each_root_task_base<__gnu_cxx::__normal_iterator<mold::elf::InputSection<mold::elf::ARM64>**, std::span<mold::elf::InputSection<mold::elf::ARM64>*, 4294967295u> >, mold::elf::create_range_extension_thunks(mold::elf::Context<mold::elf::ARM64>&, mold::elf::OutputSection<mold::elf::ARM64>&)::{lambda(mold::elf::InputSection<mold::elf::ARM64>*)#4}, mold::elf::InputSection<mold::elf::ARM64>*>::cancel(tbb::detail::d1::execution_data&))>>> referenced 971 more times

mold: error: undefined symbol: __atomic_load_8
>>> referenced by main.cc
>>>               out/elf/main.o:(mold::elf::Context<mold::elf::X86_64>::~Context())>>> referenced by output-chunks.cc
>>>               out/macho/output-chunks.o:(mold::macho::ExportEncoder::finish())>>> referenced by main.cc
>>>               out/elf/main.o:(mold::elf::Context<mold::elf::I386>::~Context())>>> referenced 3 more times

Signed-off-by: Jan Palus <[email protected]>
2022-06-29 14:08:55 +02:00
Rui Ueyama ff1d5cd245 [Mach-O] Add -sectalign 2022-06-28 21:16:13 +08:00
Rui Ueyama 3bd972ad85 [Mach-O] Ignore -debug_variant 2022-06-28 20:09:04 +08:00
Rui Ueyama 575a59ef75 [Mach-O] Ignore -objc_abi_version 2022-06-28 20:06:25 +08:00
Rui Ueyama 9a0b7e826b [Mach-O] Add -dylib_install_name 2022-06-28 19:56:18 +08:00
Rui Ueyama ded9cd4ffa Add -application_extension 2022-06-28 19:53:28 +08:00
Rui Ueyama 556e953045 Fix -Wdeprecated 2022-06-28 16:21:22 +08:00
Rui Ueyama 4a1d0dfc31 [Mach-O] Parallelize UUID computation 2022-06-28 16:13:22 +08:00
Rui Ueyama 73074389ed [Mach-O] Do not write x86-64 NOPs if ARM64 2022-06-28 15:49:29 +08:00
Rui Ueyama b0c6aa9a0f [Mach-O] Add -add_empty_section 2022-06-28 15:20:47 +08:00
Rui Ueyama 883320d5e7 Refactor 2022-06-28 13:07:10 +08:00
Rui Ueyama b81f3c22be Add a test 2022-06-28 11:35:36 +08:00
Rui Ueyama 26d45ed4c1 Fix a test
We passed two input files (/dev/null and "-") to the command which
is plainly wrong.

Fixes https://github.com/rui314/mold/issues/559
2022-06-28 10:42:26 +08:00
Rui Ueyama b5d7335e35 Revert "Implement `--(no-)allow-shlib-undefined."
This reverts the following commits because it causes a regression.

 - 62be1fcdb0
 - a7b8da9345
 - 21a2d6bb6c

Fixes https://github.com/rui314/mold/issues/559
2022-06-27 20:53:26 +08:00
Rui Ueyama bdee09ad02 [Mach-O] Handle LC_REEXPORT_DYLIB 2022-06-27 20:49:02 +08:00
Rui Ueyama a6a03b5851 Refactor 2022-06-27 17:43:53 +08:00
Rui Ueyama 8aa05de5e3 Refactor 2022-06-27 16:22:03 +08:00
Rui Ueyama 0e1226287b Remove a dead variable 2022-06-27 14:57:49 +08:00
Rui Ueyama 685085f6fe Refactor 2022-06-27 12:50:10 +08:00
Rui Ueyama 582350cc81 Simplify 2022-06-27 12:29:20 +08:00
Rui Ueyama 335698851b [Mach-O] Add -reexport-l 2022-06-26 18:27:36 +08:00
Rui Ueyama 7b5b1a340b [Mach-O] Handle weak dynamic symbols 2022-06-26 17:21:26 +08:00
Rui Ueyama 3e6f6fe8b5 Merge pull request #557 from wangp/grep-P
Don't use grep -P in tests
2022-06-26 16:42:20 +08:00
Rui Ueyama c001b75d90 Attempt to fix CI 2022-06-26 16:41:30 +08:00
Rui Ueyama 48b782ac6d Ensure that we build TBB with RelWithDebInfo release type
Fixes https://github.com/rui314/mold/issues/558
2022-06-26 16:23:35 +08:00
Rui Ueyama 0be1318b7e Attempt to fix CI 2022-06-26 16:15:06 +08:00
Rui Ueyama 1524746a80 [Mach-O] Fix weak undef symbols 2022-06-26 15:57:15 +08:00
Peter Wang 2963ab672c Don't use grep -P in tests
The version of grep being used may not support the -P flag
(Perl-compatible regular expressions).
2022-06-26 15:51:25 +10:00
Rui Ueyama fe72463f55 [Mach-O] Set mold's version to LC_BUILD_VERSION
I chose 0x6d6f6c64 as a magic number without coordinating with other
tools or Apple, but it should be fine.
2022-06-26 13:14:07 +08:00
Rui Ueyama 831f872886 [Mach-O] Do not emit corrupted unwind table 2022-06-25 19:22:38 +08:00
Rui Ueyama d9a28221dd [Mach-O] Sort the symtab and fill LC_DYSYMTAB fields 2022-06-24 16:00:50 +08:00
Rui Ueyama 62be1fcdb0 Simplify 2022-06-24 11:41:18 +08:00
Rui Ueyama a7b8da9345 Simplify 2022-06-24 10:44:35 +08:00
Rui Ueyama 9e2e70ce7d Merge pull request #540 from rbartlensky/allow-shlib-undefined
Implement `--(no-)allow-shlib-undefined.
2022-06-24 10:19:07 +08:00
Robert Bartlensky 21a2d6bb6c Implement `--(no-)allow-shlib-undefined.
This also fixes the `note-property` test which was hanging on my
machine. Turns out `$CC` was stuck waiting to read from `stdin`,
and as such I decided to write something into it.

Signed-off-by: Robert Bartlensky <[email protected]>
2022-06-23 23:46:15 +01:00
Rui Ueyama 9a3188bcf5 [Mach-O] Compute LC_UUID using the entire code signature section
We used to compute a file UUID using only the hashes in the code
signature section. So we computes the same UUID even if two files
are differnet only in some field of the code signature section.
2022-06-22 20:29:03 +08:00
Rui Ueyama 9cd811393f Simplify 2022-06-22 14:14:08 +08:00
Rui Ueyama 8b86c49f42 [Mach-O] Make sure -thread_count works properly 2022-06-22 14:14:08 +08:00
Rui Ueyama 52943009b7 [ELF] Handle R_ARM_MOVT_PREL and R_ARM_PREL31 as non-address-taking relocations
This is an attempt to fix https://github.com/rui314/mold/issues/545 though
I don't know if this is a correct fix.
2022-06-19 11:32:08 +08:00
Rui Ueyama 00c16556de Revert "[Mach-O] Do not fill text segment with 0x90"
This reverts commit b8f6fe1a1a as an
attempt to fix CI.
2022-06-18 19:38:12 +08:00
Rui Ueyama b8f6fe1a1a [Mach-O] Do not fill text segment with 0x90
0x90 is a NOP on x86-64 but doesn't make sense on ARM.
2022-06-18 19:21:35 +08:00
Rui Ueyama a9f063a394 [Mach-O] Parallelize claim_unresolved_symbols() 2022-06-18 19:16:28 +08:00
Rui Ueyama b0ad0048b2 Simplify 2022-06-18 16:43:39 +08:00
Rui Ueyama 37d798f064 Simplify 2022-06-18 12:15:04 +08:00
Rui Ueyama a9a8205be2 Bump mold version to 1.3.0 2022-06-18 11:29:18 +08:00
Rui Ueyama 0bbc1808cc Merge pull request #544 from lancethepants/main
Also copy mold-wrapper.so from docker build.
2022-06-17 22:51:42 +08:00
lancethepants cd3d82c89e Also copy mold-wrapper.so from docker build.
Signed-off-by: Lance Fredrickson <[email protected]>
2022-06-17 08:32:31 -06:00
Rui Ueyama 52c8e09ddb [ELF] Explain --icf=safe and --ignore-data-address-equality 2022-06-17 16:57:56 +08:00
Rui Ueyama c214bfc96b [Mach-O] Remove -dump
I added this option for debugging. Since mold is now mature enough,
we no longer need this option.
2022-06-16 20:53:27 +08:00
Rui Ueyama 7b8d7a6417 [Mach-O] Add -order_file 2022-06-16 16:52:25 +08:00
Rui Ueyama cfb7c268f5 [Mach-O] Do not strip __eh_frame 2022-06-16 15:26:06 +08:00
Rui Ueyama 285ea7ba26 [Mach-O] Add -u 2022-06-16 14:24:59 +08:00
Rui Ueyama 603a831f02 [Mach-O] Improve performance of __cstring merging 2022-06-16 13:17:54 +08:00
Rui Ueyama edfe9f30f2 [Mach-O] Do not enable -adhoc_codesign by default on Intel Mac 2022-06-16 12:23:03 +08:00
Rui Ueyama 5228fa2d78 Revert "[Mach-O] Add a hack to speed up code verification"
This reverts commit f3e1f728d3.
I'm not 100% sure why, but now the code signature verification works
way faster than before. Maybe it has improved in macOS 12.4 which was
released last month?
2022-06-16 12:05:22 +08:00
Rui Ueyama e4e267cc8b Simplify 2022-06-16 11:25:30 +08:00
Rui Ueyama 75ac73809b Remove dead code 2022-06-16 10:35:02 +08:00
Rui Ueyama 1b89d52504 [Mach-O] Reduce the number of personality functions 2022-06-15 20:34:00 +08:00
Rui Ueyama b05a671789 [Mach-O] Do not split into subsections if MH_SUBSECTIONS_VIA_SYMBOLS is not set 2022-06-15 17:45:25 +08:00
Rui Ueyama 30222dfb3f Revert "simplify opt_demangle setup"
This reverts commit f6b661a9051f5657ac701ea9c94dd54a85499365.
We want to make it possible to run two or more mold's `main` function
simultaneously in a single process, so we don't want to make `opt_demangle`
a global variable. That's why it was a thread-local variable.
2022-06-15 13:16:50 +08:00
Rui Ueyama 91aca3b7cf Attempt to fix CI 2022-06-15 13:12:18 +08:00
Rui Ueyama 610e839e9e Fix ASAN issue 2022-06-15 13:12:09 +08:00
Rui Ueyama dcbc85b412 Attempt to fix CI 2022-06-15 11:52:45 +08:00
Rui Ueyama 7133f8a547 Attempt to fix CI 2022-06-15 11:41:45 +08:00
Rui Ueyama 792773be20 Simplify 2022-06-15 10:40:38 +08:00
Rui Ueyama 13816a14e1 Merge pull request #538 from llunak/better-undefined
Better reporting of undefined symbols
2022-06-15 10:28:56 +08:00
Rui Ueyama 8f02509ba6 Print out an error message if mold -run is used on macOS 2022-06-09 10:36:02 -07:00
Luboš Luňák 87163c667a fix tests to work with new undefined references reporting
Signed-off-by: Luboš Luňák <[email protected]>
2022-06-09 13:32:23 +02:00
Luboš Luňák e45bebd1c7 group undefined symbols when reporting them (#508)
The output now roughly matches the lld report format, with
each symbol followed by a limited list of places that reference it.
Lld can additionally read debuginfo to report the exact location,
but that would require writing an interpreter for .debug_line.

Signed-off-by: Luboš Luňák <[email protected]>
2022-06-09 12:40:09 +02:00
Luboš Luňák d0cc19735d report also the referencing function for undefined symbols
Signed-off-by: Luboš Luňák <[email protected]>
2022-06-09 12:40:09 +02:00
Luboš Luňák 2c9e97c768 move duplicated symbol_strtab to base class InputFile
Signed-off-by: Luboš Luňák <[email protected]>
2022-06-09 12:40:09 +02:00
Luboš Luňák 114cb8e9c8 report also source file name in undefined symbol reports
Signed-off-by: Luboš Luňák <[email protected]>
2022-06-09 12:40:09 +02:00
Luboš Luňák 2af30acbb2 simplify opt_demangle setup
Setting it on demand is racy, and also not necessary, it's enough
to set it together with ctx.arg.demangle .

Signed-off-by: Luboš Luňák <[email protected]>
2022-06-09 12:40:09 +02:00
Rui Ueyama 6f9eedf98a [Mach-O] Fix an assertion failure 2022-06-07 20:15:47 +08:00
Rui Ueyama e60bf30513 [Mach-O] Do not include duplicated symbols from re-exported libs 2022-06-07 14:46:16 +08:00
Rui Ueyama 610ce3c307 Refactor 2022-06-07 14:46:16 +08:00
Rui Ueyama f3e1f728d3 [Mach-O] Add a hack to speed up code verification 2022-06-07 12:21:50 +08:00
Rui Ueyama 6f04ef9cf2 [Mach-O] Handle $ld$install_name$ symbols 2022-06-06 20:24:52 +08:00
Rui Ueyama 880204ebe5 [Mach-O] Handle $ld$hide$ symbols 2022-06-06 20:20:16 +08:00
Rui Ueyama 406d8786c9 Do not addd an element in a range-based loop
Adding a new element to a vector may invalidate existing iterators,
so it's not safe.
2022-06-06 20:10:30 +08:00
Rui Ueyama e11e68d508 [Mach-O] Handle $ld$add$ symbols 2022-06-06 19:49:58 +08:00
Rui Ueyama 3af44d08db Refactor 2022-06-06 18:52:14 +08:00
Rui Ueyama d5b5b9afe1 [Mach-O] Handle $ld$previous$ symbols 2022-06-06 17:23:10 +08:00
Rui Ueyama 0b537ae54c [Mach-O] Make -weak_framework to make framework weak 2022-06-06 14:19:24 +08:00
Rui Ueyama b364b3f453 [Mach-O] Add -weak-l<LIB> 2022-06-06 14:02:28 +08:00
Rui Ueyama ea31281149 Add tests 2022-06-06 12:03:01 +08:00
Rui Ueyama ce34654924 Refactor 2022-06-06 10:34:20 +08:00
Rui Ueyama 2d34ba13cb [Mach-O] Report errors in -{un,}exported_symbols_list early 2022-06-05 18:02:47 +08:00
Rui Ueyama eee315ea52 [Mach-O] Allow glob patterns in -exported_symbols_list and -unexported_symbols_list 2022-06-05 17:47:42 +08:00
Rui Ueyama d2d73cbcbc Move glob pattern matchers out of elf directory
Because we want to use them in macho too.
2022-06-05 17:31:32 +08:00
Rui Ueyama c786f55370 Style fix 2022-06-05 12:58:11 +08:00
Rui Ueyama 8ff4bd5e0f [Mach-O] Ignore -order_file 2022-06-05 12:51:14 +08:00
Rui Ueyama 169a22d643 [Mach-O] Add -current_versin 2022-06-05 12:50:28 +08:00
Rui Ueyama 4a8cda75ad [Mach-O] Add -compatibility_version 2022-06-05 12:48:54 +08:00
Rui Ueyama 1ebaf5be84 [Mach-O] Do not read the same .a more than once 2022-06-05 12:30:16 +08:00
Rui Ueyama a05aae7d7a Refactor 2022-06-05 11:45:45 +08:00
Rui Ueyama f9ea7890a8 Remove dead code 2022-06-05 11:39:09 +08:00
Rui Ueyama 0488d95f38 Refactor 2022-06-05 11:34:13 +08:00
Rui Ueyama 6d0d19dffb [Mach-O] Remove a hack for Chromium 2022-06-05 10:39:21 +08:00
Rui Ueyama e3e0593162 [Mach-O] Recurse on re-exported libraries 2022-06-04 22:23:41 +08:00
Rui Ueyama 7511aee558 Refactor 2022-06-04 22:23:21 +08:00
Rui Ueyama 6a3c35d97c [Mach-O] Add more hacks to link Chromium 2022-06-04 21:48:53 +08:00
Rui Ueyama 032b4d4cc5 Refactor 2022-06-04 21:10:52 +08:00
Rui Ueyama 3fb8b483b4 Fix CI 2022-06-04 20:31:09 +08:00
Rui Ueyama e14788102c [Mach-O] Fix dylib ordinal for flat-bounded symbols 2022-06-04 20:17:16 +08:00
Rui Ueyama 96d9859f45 [Mach-O] Add a workaround for linking Chrome 2022-06-04 20:17:16 +08:00
Rui Ueyama 56e6c038dc [Mach-O] Fix a constant 2022-06-04 20:17:16 +08:00
Rui Ueyama 1f5da3d313 [Mach-O] Ignore comment lines in --exported_symbols_list file 2022-06-04 20:17:16 +08:00
Rui Ueyama fc1aed3e99 [Mach-O] Load reexported libraries 2022-06-04 20:17:16 +08:00
Rui Ueyama 4b754348f5 [Mach-O] Support a local reference to a personality function 2022-06-04 20:17:16 +08:00
Rui Ueyama eee1a87742 [Mach-O] Define -weak_framework as an alias for -framework for now 2022-06-04 20:17:16 +08:00
Rui Ueyama 1b52d228d4 Add a test 2022-06-04 20:17:16 +08:00
Rui Ueyama 3842222956 Update FUNDING.yml 2022-06-04 21:14:38 +09:00
Rui Ueyama dce00d511b Create FUNDING.yml 2022-06-04 16:01:47 +09:00
Rui Ueyama 0de862191b [Mach-O] Add --unexported_symbol and --unexported_symbols_list 2022-06-03 19:48:57 +08:00
Rui Ueyama 485877a826 Update README 2022-06-03 19:38:35 +08:00
Rui Ueyama 7478c27ce8 [Mach-O] Add --exported_symbol and --exported_symbols_list 2022-06-03 18:55:00 +08:00
Rui Ueyama a5c216310b [Mach-O] Parallelize FunctionStartsSectoin::compute_size() 2022-06-03 17:57:16 +08:00
Rui Ueyama c55c98c4f2 [Mach-O] Parallelize SymtabSection::copy_buf(). 2022-06-03 17:05:41 +08:00
Rui Ueyama 6b3fc59143 [Mach-O] Parallelize SymtabSection::compute_size() 2022-06-03 16:29:27 +08:00
Rui Ueyama 6779b2bdfe [Mach-O] Do not emit __ind_sym_tab
It looks like no one is using __ind_sym_tab section, so I'll remove
it for now. We can add it back later if it turns out necessary.
2022-06-03 16:03:26 +08:00
Rui Ueyama 7097946eee [Mach-O] Merge symbol visibility 2022-06-03 15:03:29 +08:00
Rui Ueyama 67ec8f3376 [Mach-O] Handle symbols with N_WEAK_DEF|N_WEAK_REF as private extern 2022-06-03 13:23:45 +08:00
Rui Ueyama 0723476fa5 Add a test 2022-06-03 11:50:05 +08:00
Rui Ueyama a50173c259 Revert "Refactor"
This reverts commit 280782b3ae because
it broke CI.
2022-06-03 10:41:50 +08:00
Rui Ueyama 280782b3ae Refactor 2022-06-03 10:14:12 +08:00
Rui Ueyama 5621394dc3 Do not use git command in Makefile
Fixes https://github.com/rui314/mold/issues/529
2022-06-02 21:27:26 +08:00
Rui Ueyama fa420c38d4 [Mach-O] Handle private extern symbols 2022-06-02 21:27:10 +08:00
Rui Ueyama ae33fd278f [Mach-O] Rename variables 2022-06-02 18:18:42 +08:00
Rui Ueyama 68a779f76b [Mach-O] Fix a crash bug 2022-06-02 18:05:31 +08:00
Rui Ueyama f730dde6e7 [Mach-O] Add -object_path_lto 2022-06-02 16:29:50 +08:00
Rui Ueyama 8cf514408b [Mach-O] Print more staticstics numbers 2022-06-02 15:51:22 +08:00
Rui Ueyama 72c8446eb7 [Mach-O] Parallelize string merging 2022-06-02 14:25:52 +08:00
Rui Ueyama f5d85e854f wip 2022-06-02 14:03:08 +08:00
Rui Ueyama 4e845f3972 Fix CI 2022-06-02 14:01:15 +08:00
Rui Ueyama 03af112272 [Mach-O] Add -stats option 2022-06-02 13:57:48 +08:00
Rui Ueyama 66c1433be1 [Mach-O] Merge string constants in __TEXT,__cstring 2022-06-02 13:39:51 +08:00
Rui Ueyama 621e2edd65 Refactor 2022-06-02 11:14:30 +08:00
Rui Ueyama 03045cb2d0 Refactor 2022-06-02 10:27:44 +08:00
Rui Ueyama a572b803d2 Refactor 2022-06-02 10:23:32 +08:00
Rui Ueyama 7c52c41d7b Attempt to fix CI 2022-06-01 22:45:24 +08:00
Rui Ueyama 21fea57d99 [Mach-O] Parallelize export table construction 2022-06-01 22:31:26 +08:00
Rui Ueyama 965fa17b7b [Mach-O] Fix thread-local variables 2022-06-01 15:00:17 +08:00
Rui Ueyama 826bb56d59 [Mach-O] Fix thread-local variables 2022-05-31 16:54:05 +08:00
Rui Ueyama 3f12a843a6 Revert "[Mach-O] Merge string constants in __TEXT,__cstring"
This reverts commit e770df42d9 because
it broke many clang tests.
2022-05-30 17:13:09 +08:00
Rui Ueyama e895351481 Use bool bitfields instead of uint8_t
`bool foo : 1` is better `u8 foo : 1` because

  struct {
    bool foo : 1 = 0;
  } bar;

  bar.foo = 0xf000;

sets `bar.foo` to 1 while

  struct {
    u8 foo : 1 = 0;
  } bar;

  bar.foo = 0xf000;

does _not_ set `bar.foo` to 1 because it just truncates a given
integer to one bit and assign it to `bar.foo`.
2022-05-30 16:04:51 +08:00
Rui Ueyama 1eac1e6cb4 [Mach-O] Fix clang build 2022-05-30 14:58:41 +08:00
Rui Ueyama 0fab94639f [Mach-O] Parallelize resolve_symbols() 2022-05-30 13:58:07 +08:00
Rui Ueyama 348f2bb9da Refactor 2022-05-30 13:39:53 +08:00
Rui Ueyama 2f78d2eb94 [Mach-O] Parallelize a part of merge_cstring_sections() 2022-05-30 13:04:37 +08:00
Rui Ueyama 4826d393d1 [Mach-O] Set a LC_UUID even if -no_adhoc_codesign is given 2022-05-30 12:46:00 +08:00
Rui Ueyama b82aa99bca [Mach-O] Do not create LC_CODE_SIGNATURE if -no_adhoc_codesign is given 2022-05-30 12:26:05 +08:00
Rui Ueyama 080ebb9ee4 [Mach-O] Fix timer probers 2022-05-29 20:01:30 +08:00
Rui Ueyama 4f77af8e76 [Mach-O] Make export table size computation a bit faster 2022-05-29 19:51:02 +08:00
Rui Ueyama cffcea8d44 [Mach-O] Parallelize __LINKEDIT segment members size computation 2022-05-29 19:19:18 +08:00
Rui Ueyama 4622b6b14c Revert "[Mach-O] Use SHA1 instead of SHA256 for code signature"
This reverts commit 395b962c3d because
it broke CI.
2022-05-29 16:57:50 +08:00
Rui Ueyama 395b962c3d [Mach-O] Use SHA1 instead of SHA256 for code signature
It looks like SHA1 is slightly faster than SHA256 on a 2021 ARM Mac mini.
SHA1 is not recommended anymore, but for ad-hoc code signing, it should
be enough because anyone can just recompute an ad-hoc code signature for
any arbitrary binary.
2022-05-29 15:10:02 +08:00
Rui Ueyama f0f194e373 [Mach-O] Do not compute code signatures twice 2022-05-29 14:35:00 +08:00
Rui Ueyama 45615c9d81 [Mach-O] Speed up code signature computation
It looks like calling msync with MS_ASYNC can save ~130 ms per 1 GiB
output on an ARM Mac Mini 2021.
2022-05-29 14:29:26 +08:00
Rui Ueyama d9e2669063 [Mach-O] Parallelize copy_buf even more 2022-05-29 12:58:58 +08:00
Rui Ueyama d31b63e48b Refactor 2022-05-28 21:40:41 +08:00
Rui Ueyama 5612636fe4 Refactor 2022-05-28 21:06:28 +08:00
Rui Ueyama be19241630 Revert "[Mach-O] Parallelize resolve_symbols"
This reverts commit 4aeee88ef8 because
it has made mold unstable.
2022-05-28 15:38:35 +08:00
Rui Ueyama 2f243d2f52 Refactor 2022-05-28 15:34:33 +08:00
Rui Ueyama 8911458961 [Mach-O] Add -thread_count 2022-05-28 14:03:06 +08:00
Rui Ueyama 4aeee88ef8 [Mach-O] Parallelize resolve_symbols 2022-05-28 12:54:36 +08:00
Rui Ueyama 936d3b3c30 [Mach-O] Improve export table construction 2022-05-28 12:05:34 +08:00
Rui Ueyama 0a89a07e6d [Mach-O] Parallelize remove_unreferenced_subsections 2022-05-28 10:53:03 +08:00
Rui Ueyama 79654bfde0 [Mach-O] Parallelize file parsing
`make test-tsan` started failing with this change, but I believe it
is an internal failure of Intel TBB itself.
2022-05-28 09:47:36 +08:00
Rui Ueyama 379ff3d781 Refactor 2022-05-27 16:15:09 +08:00
Rui Ueyama c797408167 Refactor 2022-05-27 14:07:20 +08:00
Rui Ueyama 20ad945fbf Fix CI 2022-05-27 14:02:30 +08:00
Rui Ueyama 702ec961ff [Mach-O] Paralelize data copy 2022-05-27 13:25:11 +08:00
Rui Ueyama 5e4f8924fd [Mach-O] Parallelize code signature computation 2022-05-27 12:02:06 +08:00
Rui Ueyama f55fd7d9f2 [Mach-O] Compute LC_UUID as a tree hash of ad-hoc code signatures
We need to compute SHA256 hashes for a __code_signature section,
so recomputing a SHA256 hash of an entire file for an LC_UUID is
a waste of time. We can instead compute it as a tree hash.
2022-05-27 11:57:27 +08:00
Rui Ueyama ae29091aa6 Tidy up a script
Fixes https://github.com/rui314/mold/issues/525
2022-05-26 21:46:18 +08:00
Rui Ueyama e9f6715c6e [ELF] Support --package-metadata
Fixes https://github.com/rui314/mold/issues/505
2022-05-26 21:04:36 +08:00
Rui Ueyama 7730cb3d25 Revert "[Mach-O] Parallelize file parsing and output section writing"
This reverts commit c36291be60 to unbreak CI.
2022-05-26 17:40:51 +08:00
Rui Ueyama c36291be60 [Mach-O] Parallelize file parsing and output section writing 2022-05-26 17:19:12 +08:00
Rui Ueyama c1164cda77 Attempt o fix CI 2022-05-26 15:42:57 +08:00
Rui Ueyama e770df42d9 [Mach-O] Merge string constants in __TEXT,__cstring 2022-05-26 15:30:28 +08:00
Rui Ueyama 14c717d698 Refactor 2022-05-26 10:10:23 +08:00
Rui Ueyama b32bc66901 Revert "Refactor"
This reverts commit 9e09a21eac to unbreak CI.
2022-05-25 18:51:02 +08:00
Rui Ueyama 9e09a21eac Refactor 2022-05-25 18:38:06 +08:00
Rui Ueyama f5c8d7509e Fix CI 2022-05-25 14:10:51 +08:00
Rui Ueyama 95164b3556 Refactor 2022-05-25 14:09:11 +08:00
Rui Ueyama 4d06199e0d [Mach-O] Add time probers 2022-05-25 14:05:32 +08:00
Rui Ueyama bd292ef782 [Mach-O] Support LC_LINKER_OPTION
Fixes https://github.com/rui314/mold/issues/523
2022-05-25 13:50:05 +08:00
Rui Ueyama b626d74a5f Do not crash if a symbol has not been resolved 2022-05-25 13:01:13 +08:00
Rui Ueyama e7d4d2c39b Format 2022-05-25 12:25:59 +08:00
Rui Ueyama 48c9157fb1 Refactor 2022-05-24 20:39:13 +08:00
Rui Ueyama e37fe163b7 Fix CI 2022-05-24 20:04:41 +08:00
Rui Ueyama 4ca2d82da8 Fix CI 2022-05-24 19:56:54 +08:00
Rui Ueyama 2850c5cfe6 Fix CI 2022-05-24 19:48:13 +08:00
Rui Ueyama 75df1c82f1 [Mach-O] Implement range extension thunks
mold can now link LLVM/Clang 15 on macOS.
2022-05-24 18:25:21 +08:00
Rui Ueyama 754b995e1c Refactor 2022-05-24 16:58:19 +08:00
Rui Ueyama bafa43d4c7 Refactor 2022-05-24 16:39:54 +08:00
Rui Ueyama 760f39d129 Refactor 2022-05-24 15:52:33 +08:00
Rui Ueyama 99b622ffd1 Refactor 2022-05-24 15:01:29 +08:00
Rui Ueyama 7b097e51c9 Refactor 2022-05-24 14:19:22 +08:00
Rui Ueyama 16735e1e68 [ELF] Simplify range extension thunk algorithm
We eliminate unneeded symbols at the end of the algorithm, but that
didn't save too much space. In fact, I didn't observe any difference
by eliminating that last step from the algorithm.
2022-05-24 11:19:32 +08:00
Rui Ueyama 18e24a6e4d [ELF][ARM32] Handle a displacement larger than 2^24 2022-05-23 20:47:52 +08:00
Rui Ueyama 29e15384ed [ELF] Use an alternative instructions for PLT0
Previously, .plt must be within 128 MiB of .got.plt. The PLT0
instruction sequence in this commit does not have such limitation.
2022-05-23 19:27:40 +08:00
Rui Ueyama 229f610825 Revert "[Mach-O] Report relocation overflow error"
This reverts commit f844179fd3 because
it broke the build.
2022-05-23 13:48:09 +08:00
Rui Ueyama e9def90e9a Refactor 2022-05-23 13:47:32 +08:00
Rui Ueyama f844179fd3 [Mach-O] Report relocation overflow error 2022-05-22 18:40:23 +08:00
Rui Ueyama ccf6b0aa33 Revert "[Mach-O] Add mach-o symbols to the internal object"
This reverts commit ca3cd7c3fa because
it broke CI.
2022-05-22 18:40:10 +08:00
Rui Ueyama ca3cd7c3fa [Mach-O] Add mach-o symbols to the internal object 2022-05-22 18:36:00 +08:00
Rui Ueyama b5587d930e [Mach-O] Deduplicate comdat sections 2022-05-22 16:37:14 +08:00
Rui Ueyama 4c034da565 Refactor 2022-05-22 13:42:20 +08:00
Rui Ueyama a1711b97b3 [Mach-O] Demangle symbols 2022-05-21 20:31:11 +08:00
Rui Ueyama e293d56cc1 [Mach-O] Add -sectcreate 2022-05-21 15:56:36 +08:00
Rui Ueyama 7f84e4242c [Mach-O] Add -export_dynamic 2022-05-21 15:16:51 +08:00
Rui Ueyama ffb18422c1 Add comments 2022-05-21 14:06:56 +08:00
Rui Ueyama 33e00f2fb5 [Mach-O] Make output reproducible 2022-05-21 12:54:54 +08:00
Rui Ueyama 5c3bbb801d Refactor 2022-05-21 12:36:41 +08:00
Rui Ueyama def3c95b7f Merge pull request #522 from jacob-abraham/ignore-ld-flags
Ignore --no-keep-memory
2022-05-21 13:09:33 +09:00
Jacob Abraham bd3fa9a3db Pass through of -no-keep-memory of ld
Signed-off-by: Jacob Abraham <[email protected]>
2022-05-20 12:19:27 -07:00
Rui Ueyama ef053c41fe Revert "[CI] Always upload test results as artifacts"
This reverts commit 03cda81af6.
2022-05-20 17:07:05 +08:00
Rui Ueyama 03cda81af6 [CI] Always upload test results as artifacts 2022-05-20 16:28:08 +08:00
Rui Ueyama 51f99db269 Attempt to fix CI 2022-05-20 14:52:03 +08:00
Rui Ueyama 94c100c211 Attempt to fix CI 2022-05-20 14:28:56 +08:00
Rui Ueyama 943cb99a84 Attempt to fix CI 2022-05-20 14:23:28 +08:00
Rui Ueyama eb90d1bcbb [Mach-O] Fix x86-64 CI 2022-05-20 14:18:31 +08:00
Rui Ueyama 7156ef7daa Attempt to fix CI 2022-05-20 13:36:12 +08:00
Rui Ueyama fb89e06e2b Upload build artifacts 2022-05-20 13:31:58 +08:00
Rui Ueyama 6b77349b4a [Mach-O] Fix thread-local variables
With this change, mold can now link mold itself on macOS!
2022-05-20 13:12:58 +08:00
Rui Ueyama 2c5582b9e9 [Mach-O] Fix references to dyld_stub_binder 2022-05-19 18:14:34 +08:00
Rui Ueyama 7ce2c7edb5 [Mach-O] Ignore __eh_frame input sections 2022-05-18 17:19:08 +08:00
Rui Ueyama b5bfe4c607 Revert "[Mach-O] Deduplicate comdat sections"
This reverts commit 55eb8dee4d to unbreak CI.
2022-05-18 14:28:31 +08:00
Rui Ueyama b9827f29e7 [Mach-O] Define ___dso_handle symbol 2022-05-18 14:25:55 +08:00
Rui Ueyama 3ec1e8d9df Refactor 2022-05-18 14:04:48 +08:00
Rui Ueyama 55eb8dee4d [Mach-O] Deduplicate comdat sections 2022-05-18 13:39:02 +08:00
Rui Ueyama da22677bf9 Refactor 2022-05-18 13:08:57 +08:00
Rui Ueyama c654d42c14 Merge pull request #520 from tapthaker/macho-tbd-files
[Mach-O] Adds support for objc and weak symbols in TBD files
2022-05-18 12:03:11 +08:00
Rui Ueyama 4b945c839f Extend a test 2022-05-18 11:41:37 +08:00
Rui Ueyama e4b8fa3c26 [ELF] Do not ovewrite .so files
Fixes https://github.com/rui314/mold/issues/188
2022-05-18 11:38:53 +08:00
Tapan Thaker 6d67614004 [Mach-O] Adds support for objc and weak symbols in TBD files
Signed-off-by: Tapan Thaker <[email protected]>
2022-05-17 20:16:27 -07:00
Rui Ueyama 1052b93c4f [Mach-O] Deduplicate comdat symbols 2022-05-17 20:08:29 +08:00
Rui Ueyama 0692db3481 Merge elf/output-file.cc and macho/output-file.cc into a single file 2022-05-17 16:22:17 +08:00
Rui Ueyama 68f4891107 Merge pull request #518 from tapthaker/absolute-type
[macho] Fix checking for absolute address symbol type
2022-05-17 09:48:37 +08:00
Tapan Thaker 37a50f8996 [macho] Fix checking for absolute address symbol type
Signed-off-by: Tapan Thaker <[email protected]>
2022-05-16 14:04:39 -07:00
Rui Ueyama 352ab1a3f9 [Mach-O][ELF] Add a cleanup handler 2022-05-16 20:14:52 +08:00
Rui Ueyama 519de432c7 [Mach-O][LTO] Pass -mllvm to CodeGen 2022-05-16 19:47:25 +08:00
Rui Ueyama 4d380ec645 [Mach-O] Add tests 2022-05-16 19:34:24 +08:00
Rui Ueyama 0326d66b24 Refactor 2022-05-16 19:34:22 +08:00
Rui Ueyama f392b4aeb3 Refactor 2022-05-16 18:57:16 +08:00
Rui Ueyama a21a79e846 Refactor 2022-05-16 18:31:44 +08:00
Rui Ueyama ca6d4a53c4 Update a CI config file 2022-05-16 16:23:03 +08:00
Rui Ueyama 66e585eebe Fix CI 2022-05-16 16:06:28 +08:00
Rui Ueyama deebb497d3 [Mach-O][LTO] Add a basic support of LTO
mold/macOS can now link a "hello world" program with LTO.
2022-05-16 15:07:09 +08:00
Rui Ueyama 466e93ea46 [ELF] Change a variable name for consistency
We use the same spelling for a variable name and a command line option.
2022-05-16 10:20:36 +08:00
Rui Ueyama 3008879d23 Merge pull request #516 from ishitatsuyuki/icf
ICF: explain, implement icf=safe and ignore-data-address-equality.
2022-05-16 10:08:33 +08:00
Tatsuyuki Ishi e8166bcb1b [elf] Implement ignore-data-address-equality.
Closes: https://github.com/rui314/mold/issues/485
Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-05-15 21:54:58 +09:00
Tatsuyuki Ishi 27908af67d [elf] Implement icf=safe.
Closes: https://github.com/rui314/mold/issues/484
Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-05-15 21:35:25 +09:00
Tatsuyuki Ishi c9288c5b92 [elf] icf: explain.
Add comments to give more insights to the design of the algorithm and
optimizations.

Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-05-15 21:35:04 +09:00
Rui Ueyama 188f17524f [Mach-O] Add a header and a stub cc file for LTO 2022-05-15 19:31:21 +08:00
Rui Ueyama 2b86786419 Move a file 2022-05-15 16:43:22 +08:00
Rui Ueyama bcc8375958 Refactor 2022-05-14 17:47:42 +08:00
Rui Ueyama f0bb588e32 Revert "[ELF][LTO] Restart the linker when get_symbols_v2 is called for the first time"
This reverts commit 38f2b965db.

I'd like to wait for GCC to provide some way to identify whether
or not a given LTO plugin supports the v3 API.
2022-05-14 17:47:19 +08:00
Rui Ueyama 3cf8fc4b35 [Mach-O] Add -no_uuid and -uuid_random and -final_output 2022-05-14 15:51:42 +08:00
Rui Ueyama 64f0ba4a9a Update README 2022-05-14 14:47:38 +08:00
Rui Ueyama 611fa3c7a4 Silence a -Wunused-result warning 2022-05-14 14:36:32 +08:00
Rui Ueyama 69d23bc10f Update dependencies 2022-05-14 14:26:41 +08:00
Rui Ueyama a6c3f3c082 Add more dependencies 2022-05-14 14:11:18 +08:00
Rui Ueyama 64b8168916 Fix tests for OpenSUSE Leap
Fixes https://github.com/rui314/mold/issues/515
2022-05-14 13:47:35 +08:00
Rui Ueyama 27409530d7 Add rules for OpenSUSE 2022-05-14 13:47:10 +08:00
Rui Ueyama b1c0a13386 [ELF] Add --rosegment and --no-rosegment
Fixes https://github.com/rui314/mold/issues/514
2022-05-13 19:41:32 +08:00
Rui Ueyama fcb3bedf0d Refactor 2022-05-13 19:28:15 +08:00
Rui Ueyama e68fdc396e Refactor 2022-05-13 19:08:20 +08:00
Rui Ueyama a98a19acfa Refactor 2022-05-13 14:09:41 +08:00
Rui Ueyama 1a8ebf89db Fix CI 2022-05-13 14:07:19 +08:00
Rui Ueyama f086145fc4 Fix CI 2022-05-13 13:16:51 +08:00
Rui Ueyama 8cbcd9777f [ELF] Support SHF_GNU_RETAIN
https://github.com/rui314/mold/issues/511
2022-05-13 13:09:16 +08:00
Rui Ueyama 2e47fe5815 Do not create a .gdb_index section if there's no debug info section 2022-05-13 11:45:32 +08:00
Rui Ueyama 61d19e65e2 Merge pull request #510 from llunak/no-gdb-index-abort
don't abort with --gdb-index if there's no .debug_info
2022-05-13 11:31:03 +08:00
Rui Ueyama c64fc2ea6d Add a rule for Gentoo Linux 2022-05-13 10:50:18 +08:00
Rui Ueyama 5b4c698a9c Refactor 2022-05-12 20:47:12 +08:00
Rui Ueyama 7f3a05ad0f [Mach-O] Add -hidden-l 2022-05-12 20:15:26 +08:00
Rui Ueyama a5c7c98af8 [Mach-O] Add -stack_size 2022-05-12 19:07:04 +08:00
Rui Ueyama 00148f4d5e [Mach-O] Add -install_name 2022-05-12 18:55:56 +08:00
Rui Ueyama 9459b0f4f5 Refactor 2022-05-12 18:05:01 +08:00
Rui Ueyama ee96875fcc [Mach-O] Add -macos_version_min 2022-05-12 17:48:14 +08:00
Rui Ueyama 4bc287ff48 [Mach-O] Add -search_paths_first and -search_dylibs_first 2022-05-12 16:49:10 +08:00
Luboš Luňák 73a9b6ec7d don't abort with --gdb-index if there's no .debug_info
This is an unusual scenario, but it can happen on Arch Linux, where
apparently even crt1.o has no .debug_info . So if one for whatever
reason omits -g for all compilations and then uses --gdb-index,
mold aborts.

Signed-off-by: Luboš Luňák <[email protected]>
2022-05-12 10:37:29 +02:00
Rui Ueyama 5cf365b4f5 Refactor 2022-05-12 16:34:29 +08:00
Rui Ueyama 5a239ef4b2 [Mach-O] Fix ARM64_RELOC_ADDEND and PAGEOFF relocs
With this fix, mold can now link git and successfully run all
git test suites.
2022-05-12 14:28:04 +08:00
Rui Ueyama 344c027a91 Refactor 2022-05-12 09:35:50 +08:00
Rui Ueyama c1409d95a4 Refactor 2022-05-12 09:30:02 +08:00
Rui Ueyama adb1ee4c0a Refactor 2022-05-11 20:17:47 +08:00
Rui Ueyama 5e389f99d4 Use mold to build mold 2022-05-11 17:34:36 +08:00
Rui Ueyama c054e3d197 Fix CI 2022-05-11 17:23:29 +08:00
Rui Ueyama 53fd03e115 Fix a bug found by AddressSanitizer 2022-05-11 16:36:40 +08:00
Rui Ueyama 99155920af Remove unnecessary dependency 2022-05-11 15:54:09 +08:00
Rui Ueyama 05acf9da2c Add test-asan, test-tsan and test-ubsan targets 2022-05-11 15:51:44 +08:00
Rui Ueyama 4ef90d4316 Do not use pkg-config
We are not using pkg-config for zlib, so I think we can live without
it for libcrypto too.
2022-05-11 15:32:51 +08:00
Rui Ueyama 4d61d462ef Refactor 2022-05-11 10:55:51 +08:00
Rui Ueyama d68c7eb5e4 Refactor 2022-05-11 10:32:30 +08:00
Rui Ueyama 3c284f09ab [ELF] Skip a failing test on RV64
https://github.com/rui314/mold/issues/503
2022-05-10 22:20:28 +08:00
Rui Ueyama f59bd5ea23 [ELF] Fix an unsafe memory access
Fixes https://github.com/rui314/mold/issues/506
2022-05-10 21:42:12 +08:00
Rui Ueyama b6e9c6da66 [Mach-O] Add -U 2022-05-10 20:02:48 +08:00
Rui Ueyama f8296ba0fc Disable a few tests for RV64
Fixes https://github.com/rui314/mold/issues/503
2022-05-10 19:23:33 +08:00
Rui Ueyama 90e0228601 Refactor 2022-05-10 17:19:31 +08:00
Rui Ueyama 15eb278ab1 Refactor 2022-05-10 16:35:36 +08:00
Rui Ueyama c1ba1707fc [Mach-O] Add -force_load 2022-05-10 14:17:41 +08:00
Rui Ueyama 4ae6091442 Refactor 2022-05-10 14:15:16 +08:00
Rui Ueyama 5c63d2e031 Remove an unused variable 2022-05-10 14:01:05 +08:00
Rui Ueyama 166a67d5ee Refactor 2022-05-10 13:54:43 +08:00
Rui Ueyama 6b5c47882a [ELF] Fix mold-wrapper's execle
Fixes https://github.com/rui314/mold/issues/501
2022-05-10 12:43:17 +08:00
Rui Ueyama b8b6d0d3ae Allow to take an arbitrary pointer type 2022-05-10 11:34:06 +08:00
Rui Ueyama 144c156dc6 Fix a test
Fixes https://github.com/rui314/mold/issues/502
2022-05-09 19:34:00 +08:00
Rui Ueyama edc1c68efb Relax a test
Fixes https://github.com/rui314/mold/issues/500
2022-05-09 19:20:41 +08:00
Rui Ueyama e6446b3931 Merge pull request #499 from marxin/docs-gcc
README: GCC 12.1 is already released
2022-05-09 17:14:22 +08:00
Martin Liska d1ade871f0 README: GCC 12.1 is already released
Signed-off-by: Martin Liska <[email protected]>
2022-05-09 11:11:08 +02:00
Rui Ueyama 72ef8fc73a [Mach-O] Support -all_load 2022-05-09 16:48:12 +08:00
Rui Ueyama abaceb458f Update comments 2022-05-09 16:09:00 +08:00
Rui Ueyama d22cdf8540 Rename a file 2022-05-09 14:44:11 +08:00
Rui Ueyama 7f9d16e971 [Mach-O] Fix unaligned accesses 2022-05-09 14:15:42 +08:00
Rui Ueyama f48895cbf3 Update .gitignore 2022-05-09 14:13:32 +08:00
Rui Ueyama bd12a98533 Remove duplicate code 2022-05-09 14:13:16 +08:00
Rui Ueyama ab63994c97 Fix symlink name 2022-05-09 14:02:20 +08:00
Rui Ueyama 537a062d25 Remove unnecessary shell variable 2022-05-09 13:56:48 +08:00
Rui Ueyama 5d7064c01e [Mach-O] Remove absolute addresses from tests 2022-05-09 11:57:42 +08:00
Rui Ueyama f822898c2f [Mach-O] Remove a hook to run executables under Qemu
Qemu does not suport user process emulation for macOS, so we'll
never use it.
2022-05-09 11:44:38 +08:00
Rui Ueyama b2cf48de33 [Mach-O] Fix test for GitHub Actions 2022-05-09 11:43:09 +08:00
Rui Ueyama 188a0f1b3c Remove an unused variable 2022-05-09 11:37:10 +08:00
Rui Ueyama e4d6c8c382 [Mach-O] Fix test runner
If one of the test scripts fail, the entire test should fail.
2022-05-09 11:35:18 +08:00
Rui Ueyama f787e99f16 [Mach-O] Suppress pre-alpha warnings on GitHub Actions 2022-05-08 20:27:00 +08:00
Rui Ueyama 128b345b9a [Mach-O] Enable ASan, UBSan and TSan on GitHub Actions 2022-05-08 20:21:27 +08:00
Rui Ueyama 4c758a62b1 [Mach-O] Fix bugs found by ASAN 2022-05-08 20:03:13 +08:00
Rui Ueyama 2a672254a2 [Mach-O] Fix unaligned acccesses 2022-05-08 20:03:13 +08:00
Rui Ueyama 159d89036e [Mach-O] Enable conditional compilation for debugging 2022-05-08 18:38:39 +08:00
Rui Ueyama 407846a8c6 Use staging setup-mold GitHub action 2022-05-08 11:08:31 +08:00
Rui Ueyama d8a8877875 Retry if pthread_create returns EAGAIN
Looks like on many Unix-like systems, `pthread_create` can spuriously
fail for various reasons even if we have enough resources one more
thread. So we need to retry if EAGAIN is returned.

Without this, mold occasionally fails due to a pthread_create failure.

I'll test this logic for a while in this repo and upstream to OneTBB.

This logic is borrowed from Go's this patch:
https://go-review.googlesource.com/c/go/+/33894/

https://github.com/rui314/mold/issues/353
https://github.com/rui314/mold/issues/410
2022-05-07 19:04:48 +08:00
Rui Ueyama 448a9ec4e3 Fix a signal handler
Pointed out in a post-commit review at
https://github.com/rui314/mold/commit/4819d3dc53e057e4cdb345829f5181fcc6d7d0a4
2022-05-07 16:29:33 +08:00
Rui Ueyama 4819d3dc53 Show a disk full error message only when a write failed
Suggested by @X547 in a post-commit review at
https://github.com/rui314/mold/commit/596926008a4042b94a6901e53236ab1856a29e1a

`puts` is replaced with `write` as suggested by @Alcaro.
2022-05-07 13:42:10 +08:00
Rui Ueyama f8361f6389 Update comments 2022-05-06 18:25:17 +08:00
Rui Ueyama 596926008a Print out a user-friendly error on output filesystem full error
Fixes https://github.com/rui314/mold/issues/497
2022-05-06 18:15:32 +08:00
Rui Ueyama 9fbd71ec6b Update README 2022-05-06 12:38:34 +08:00
Rui Ueyama 727d34d727 Do not show "skipped" if valgrind is not available 2022-05-05 18:59:48 +08:00
Rui Ueyama 6d1f67fc59 Merge pull request #496 from marxin/tests-valgrind
tests: print skipped if valgrind is missing
2022-05-05 18:57:23 +08:00
Martin Liska 6910f8e590 tests: print skipped if valgrind is missing
Signed-off-by: Martin Liska <[email protected]>
2022-05-05 12:42:17 +02:00
Rui Ueyama ca25f1fd20 [ELF] Redefine --color-diagnostics as an alias for --color-diagnostics=auto
It was accidentally mapped to --color-diagnostics=always, but it
was a bug. For the sake of compatibility with other tools such as
clang or lld, it should be defined as an alias for `auto`.
2022-05-05 18:01:04 +08:00
Rui Ueyama 0dc5ed38ed Simplify 2022-05-05 18:00:42 +08:00
Rui Ueyama 3694ffe61d Fix execl-family wrappers
Previously, we didn't terminate an argv with a null pointer.

Fixes https://github.com/rui314/mold/issues/495
2022-05-05 17:37:57 +08:00
Rui Ueyama 2d3b410a3c Remove unnecessary packed attribute 2022-05-05 17:17:24 +08:00
Rui Ueyama 38f2b965db [ELF][LTO] Restart the linker when get_symbols_v2 is called for the first time
Previously, we detected if a given linker plugin supports get_symbols_v3
or not and restart the process if only get_symbols_v2 is supported.

Now, mold restart itself wehn get_symbols_v2 is called for the first time,
eliminating the need for the feature detection.

https://github.com/rui314/mold/issues/454
2022-05-04 10:38:02 +08:00
Rui Ueyama cde4b0fb45 Move a function 2022-05-04 10:01:41 +08:00
Rui Ueyama ed9a29b713 Refactor 2022-05-03 20:23:18 +08:00
Rui Ueyama 06cdd7e951 Refactor 2022-05-03 20:04:01 +08:00
Rui Ueyama 232dafa8d0 [ELF] Do not allow -- for a single-letter option 2022-05-03 19:35:48 +08:00
Rui Ueyama 31ba99a7cd Refactor 2022-05-03 19:25:30 +08:00
Rui Ueyama 885b5c0e10 Fix possible unaligned accesses 2022-05-03 18:59:04 +08:00
Rui Ueyama cd54cacb1b Refactor 2022-05-03 18:35:09 +08:00
Rui Ueyama eb7433f3ac Avoid heap allocation in mold-wrapper 2022-05-03 14:48:24 +08:00
Rui Ueyama 86ad2c5b4d Simplify 2022-05-03 14:35:49 +08:00
Rui Ueyama 68bcc8ea40 Remove dependency to /dev/urandom 2022-05-03 13:59:55 +08:00
Rui Ueyama 44228d91b0 Refactor 2022-05-03 13:34:03 +08:00
Rui Ueyama dbf1040245 Fix possible unaligned accesses 2022-05-02 19:54:25 +08:00
Rui Ueyama f223b9c292 Refactor 2022-05-02 19:39:24 +08:00
Rui Ueyama 3e18883b4b Refactor 2022-05-02 19:09:39 +08:00
Rui Ueyama 5090ad6111 Refactor 2022-05-02 19:04:35 +08:00
Rui Ueyama 11cbf9571b Revert "Style fix"
This reverts commit 433a222f907b9d0c7ce5a2d46b84768652017af7 because
it broke non-x86-64 targets.
2022-05-02 18:50:24 +08:00
Rui Ueyama a15f387526 Simplify 2022-05-02 16:12:56 +08:00
Rui Ueyama d56ba46cb2 Remove dead code 2022-05-02 16:10:25 +08:00
Rui Ueyama dd8e15952f [ELF] Fix --omagic 2022-05-02 15:52:29 +08:00
Rui Ueyama 22a8b3095c Fix possible unaligned accesses 2022-05-02 14:56:34 +08:00
Rui Ueyama 5d68c800ed Update comments 2022-05-02 09:03:28 +08:00
Rui Ueyama a5ff3c8287 Fix relocations 2022-05-01 19:31:05 +08:00
Rui Ueyama 06405073a9 Style fix 2022-05-01 17:33:26 +08:00
Rui Ueyama 0abd0a40db [ELF] Do not set wrong tombstone values to debug info sectinos
Previously, if a relocation in an non-alloc section refers a section
fragment, it gets a tombstone value instead of a real value.

Fixes https://github.com/rui314/mold/issues/490
2022-05-01 17:30:58 +08:00
Rui Ueyama 7cfc894914 [ELF] Do not use uninitialized members 2022-05-01 16:11:46 +08:00
Rui Ueyama 230976a139 Avoid casting to E::WordTy 2022-05-01 15:56:38 +08:00
Rui Ueyama 74ad5da477 Update comments 2022-05-01 15:10:14 +08:00
Rui Ueyama d0d7f948f0 Enable UBSan on GitHub CI 2022-05-01 14:32:25 +08:00
Rui Ueyama da7b5db350 Fix all undefined behaviors found by UBsan 2022-05-01 14:27:53 +08:00
Rui Ueyama bf902e1dd2 Fix -Wc++11-narrowing 2022-05-01 14:27:53 +08:00
Rui Ueyama 12cfddc611 Refactor
- Rename data types so that we have LittleEndian and BigEndian types.

- Do not assume that "base" integral types are aligned to 2 bytes
  boundaries, because that doesn't make much difference in terms of
  performance.

- Remove `pu8` type
2022-05-01 14:27:53 +08:00
Rui Ueyama 1d8c0567a8 Merge pull request #486 from yurai007/add-ubsan-support
Add UBSan support
2022-05-01 12:09:49 +08:00
Rui Ueyama a9f2a8ddfe Merge pull request #487 from sicherha/i686-skip-static-pie-tests
Skip `-static-pie` tests on i686, not just on i386
2022-05-01 10:45:02 +08:00
Christoph Erhardt b5a1d3ace2 Skip -static-pie tests on i686, not just on i386
Signed-off-by: Christoph Erhardt <[email protected]>
2022-05-01 00:31:27 +02:00
Dawid Jurczak 3499ee6ac7 Add UBSan support
Core part of this change is cleaning up Mold from UBSan alignment errors.
Following approach inspired by LLVM's ELFTypes is done here:

    1. Introduce generic Packed class acting as proxy for unaligned aggregated value.
       Every access to such value through proxy is secured by memcpy call.
    2. Replace plain struct members with Packed members wherever it matters.
    3. Use explicit castings through Packed type for misaligned loads.

For more background please check: https://github.com/rui314/mold/discussions/477

Signed-off-by: Dawid Jurczak <[email protected]>
2022-04-30 17:37:14 +02:00
Rui Ueyama eae062f815 [ELF] Fix ICF so that all tests pass with ICF 2022-04-30 19:37:29 +08:00
Rui Ueyama 2ce5e22193 Refactor
This commit makes it clear that Header, Output Section and Synthetic
Section are distinctive.
2022-04-30 17:07:39 +08:00
Rui Ueyama c1c20aa427 refactor 2022-04-30 16:40:05 +08:00
Rui Ueyama af592f303c Simplify 2022-04-30 16:09:05 +08:00
Rui Ueyama d0fc8fb005 Simplify 2022-04-30 15:07:29 +08:00
Rui Ueyama a85b1f539a [ELF] Remove --preload 2022-04-30 15:03:24 +08:00
Rui Ueyama d3130f3648 Replace int with int32_t or uint32_t 2022-04-30 12:43:08 +08:00
Rui Ueyama f48825decb Simplify 2022-04-30 12:38:06 +08:00
Rui Ueyama 920266bce4 [ELF][LTO] Fix LTO on 32-bit hosts
`off_t` is either an alias for `off32_t` or for `off64_t` on a 32-bit
machine. `off32_t` is the default. But it looks like LLVM gold plugin
is always compiled with `off64_t`.

Because of this difference, the offset of the `handle` member in
`PluginInputFile` differed between mold and LLVMgold.so. mold thought
that the member was at offset 16 of the struct, while LLVMgold.so
thought that it's at offset 24.

That caused a mysterious crash when LLVMgold tries to access the
`handle` member.

Fixes https://github.com/rui314/mold/issues/483
2022-04-30 12:10:59 +08:00
Rui Ueyama d714819759 Rename a test 2022-04-29 20:06:48 +08:00
Rui Ueyama 517cce0371 Add a tsan test 2022-04-29 19:53:48 +08:00
Rui Ueyama e5bcbdf487 [ELF] Remove extraneous guards 2022-04-29 19:53:48 +08:00
Rui Ueyama 62b3a3dca9 Fix tests for ASAN and TSAN 2022-04-29 19:53:48 +08:00
Rui Ueyama fafb75b0e5 [ELF] Fix race condition found by ThreadSanitizer 2022-04-29 19:53:48 +08:00
Rui Ueyama 43cd7a4035 Add an asan test 2022-04-29 19:37:52 +08:00
Rui Ueyama 708ad63474 [ELF][LTO] Fix non-v2 LTO build
If a given linker LTO plugin does not support the v3 API, we resolve
all symbol names to identify the set of object files that are actually
needed for linking and then restart the linker to do the same thing
again from scratch, excluding the unneeded files from the beginning.

We do this because the v2 API does not provide a way to "unload" an
object file once it is read.

To identify an unneeded object file on the second run, we serialize
its name in the form of `<archive-name>:<offset-in-archive>` or just
a filename. If an object file is in a thin archive, we simply used
its filename.

But if the same file is directly given to a linker as well as as a
thin archive member, the file would accidentally be excluded on the
second run.

This change fixes that issue. With this change, it looks like we can
build GCC 12 with LTO using mold.

Fixes https://github.com/rui314/mold/issues/454
2022-04-29 12:09:25 +08:00
Rui Ueyama 3f545317c9 Update README 2022-04-28 20:23:55 +08:00
Rui Ueyama c8d8f86a52 Bump mold version to 1.2.1 2022-04-28 17:51:29 +08:00
Rui Ueyama f1f2d40469 [ELF] Support object files with more than 2^19 symbols
Previously, Symbol::sym_idx overflows if there are more than 524,288
symbols in a single file. That's a very large number but is not impossible.

Fixes https://github.com/rui314/mold/issues/405
2022-04-28 17:08:42 +08:00
Rui Ueyama a15b73a6ee [ELF] Parallelize a loop 2022-04-28 13:40:11 +08:00
Rui Ueyama bcac1a3c53 [ELF][LTO] Do not internalize weak/common symbols
Previously, if a symbol in a non-IR object is weak or common and
is resolved to an IR symbol, mold internalized that symbol. That
resulted in having two separate definitions for the same symbol
and caused a mysterious error.

Fixed https://github.com/rui314/mold/issues/479
2022-04-28 13:37:16 +08:00
Rui Ueyama a63c25b020 [ELF] Respect --as-needed for LTO
Previously, we accidentally create DT_NEEDED entries for all DSOs
even if it is guaraded by --as-needed and is not used.
2022-04-28 11:40:31 +08:00
Rui Ueyama 668b17b3fa Merge pull request #480 from ishitatsuyuki/thinlto
ThinLTO option fixes
2022-04-28 11:19:06 +08:00
Tatsuyuki Ishi 3b9dc73210 [ELF] Remove support for thinlto-single-module
This is a LLD exclusive option that is not supported by LLVMgold. It
will be broken, so remove it for now.

Fixes: 7ebd0712 ("[ELF] Recognize --thinlto-{single-module,cache-dir,cache-policy}")
Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-04-28 12:02:57 +09:00
Tatsuyuki Ishi 1bd48be605 [ELF] Fix LLVMgold ThinLTO cache option names
These options needs to be passed without the thinlto- prefix.

See also: https://github.com/llvm/llvm-project/blob/6365bde6585651b7813010dc63df7984a5b8ad41/llvm/tools/gold/gold-plugin.cpp#L263-L266

Fixes: 7ebd0712 ("[ELF] Recognize --thinlto-{single-module,cache-dir,cache-policy}")
Signed-off-by: Tatsuyuki Ishi <[email protected]>
2022-04-28 12:02:57 +09:00
Rui Ueyama eca94d9081 [mold-wrapper] Allow arbitrary long argv for execl, execlp and execle 2022-04-28 10:32:25 +08:00
Rui Ueyama 615687b2ed [ELF] Fix crash of programs that runs under mold -run
We cannot always write to argv safely becasue mold-wrapper's functions
run in other program's context, and we have no idea about the assumption
that the underlying program has on this vector.

So, we should not write to argv.

This commit is based on @ishitatsuyuki's patch.

Fixes https://github.com/rui314/mold/issues/478
2022-04-28 09:53:48 +08:00
Rui Ueyama ee95df673b [ELF] Remove a test for a feature that is deprecated 2022-04-27 22:30:00 +08:00
Rui Ueyama e429773794 Update a comment 2022-04-27 21:40:58 +08:00
Rui Ueyama e89ec11427 [ELF] De-duplicate .debug_gnu_pubtypes entries
Fixes https://github.com/rui314/mold/issues/472
2022-04-27 21:39:41 +08:00
Rui Ueyama 43ab68b95d Simplify 2022-04-27 18:11:11 +08:00
Rui Ueyama 3455e8bd82 Include popular large libs to the base Docker image
So that each test doesn't have to compile them individually.
2022-04-27 16:36:45 +08:00
Rui Ueyama 9899769713 Merge pull request #476 from llunak/gdb-index-debug-types
[ELF] the --gdb-index code does not handle .debug_types
2022-04-27 15:04:04 +08:00
Luboš Luňák 8b584f8297 [ELF] the --gdb-index code does not handle .debug_types
And there is probably little need to do so, since it's DWARF4-only
and requires explicit -fdebug-types-section, but at least detect
and abort in this case.
2022-04-27 07:55:48 +02:00
Rui Ueyama e088db72e0 [ELF] Do not set PLT addresses to .symtab
Fixes https://github.com/rui314/mold/issues/473
2022-04-27 13:31:47 +08:00
Rui Ueyama 8298c0ad16 [ELF] Fix symbol resolution for versioned symbols
My last commit d391fd9a59 changes how we
resolve versioned symbols, but that affected too many programs.
In particular it broke GCC build, so reverted in
f378fdc495.

This commit is an attempt to fix the same issue with a minimal change.

Fixes https://github.com/rui314/mold/issues/475
2022-04-27 13:13:14 +08:00
Rui Ueyama f378fdc495 Revert "[ELF] Fix regression in symbol name resolution"
This reverts commit d391fd9a59 because
it broke GCC builds.
2022-04-27 12:57:22 +08:00
Rui Ueyama 897f969017 [ELF] Handle --print-dependencies early
So that the option can be used to investigate why some symbol is
reported as a duplicated one.
2022-04-27 12:11:10 +08:00
Rui Ueyama 17907964ac Merge pull request #474 from llunak/gdb-index-size-3
[ELF] handle also DW_FORM_addrx3 and DW_FORM_strx3
2022-04-26 19:31:51 +08:00
Luboš Luňák 9431704230 [ELF] handle also DW_FORM_addrx3 and DW_FORM_strx3
Apparently these also may get used with large enough binaries,
despite being somewhat tricky with regard to endianess.

Signed-off-by: Luboš Luňák <[email protected]>
2022-04-26 13:23:31 +02:00
Rui Ueyama 1b5735543f Merge pull request #470 from llunak/gdb-index-address-size
Gdb index address size
2022-04-26 18:48:56 +08:00
Luboš Luňák 886695d914 [ELF] check DWARF address size
The current code assumes the debuginfo address size is equal to the
native address size, which should be the case, but check that just
in case it isn't.
2022-04-26 11:17:02 +02:00
Rui Ueyama 0fd6a6ca90 [ELF] Interpret DW_RLE_ DWARF record correctly
Fixes https://github.com/rui314/mold/issues/467
2022-04-26 16:29:20 +08:00
Rui Ueyama d391fd9a59 [ELF] Fix regression in symbol name resolution
This fix is needed to build Gentoo's net-fs/fuse package.
2022-04-26 15:26:52 +08:00
Luboš Luňák face536c39 launch gdb with -nx (ignore all .gdbinit)
Otherwise the test may fail simply because .gdbinit setup does
something strange (I happen to have 'set auto-solib-add off', which
disables loading debug info by default).
2022-04-26 09:12:29 +02:00
Rui Ueyama 3015f5e418 Fix typo
Pointed out by @llunak.
2022-04-25 16:07:05 +08:00
Rui Ueyama fc9f9e570e Simplify 2022-04-25 14:26:22 +08:00
Rui Ueyama f1e925d416 Update comments 2022-04-25 13:31:12 +08:00
Rui Ueyama a761f6a75c Improve documents 2022-04-25 13:14:45 +08:00
Rui Ueyama ea600a752b Do not run ./dist.sh on every invocation 2022-04-24 16:36:17 +08:00
Rui Ueyama fa6d0c33d7 [ELF] Hide an unversioned symbol if a versioned one exists
An object file can contain both unversioned and versioned symbols for
the same symbol. For example, the following piece of code will be
compiled to an object file that will contain `foo` and `foo@VERSION`.

  void foo() {}
  asm(".symver foo, foo@VERSION");

If this object file is given to GNU BFD linker, it exports only
`foo@VERSION` and suppresses `foo`. I believe the rationale for doing
that is, if a symbol is versioned, all of its versions must be
versioned. In other words, `foo@VER1`, `foo@VER2` and `foo@@VER3` can
co-exist, but `foo@VER1` and `foo` can't. In the latter case, if you
want to provide `foo` as the default version, it should be exported
not as `foo` but as `foo@@SOME_VERSION` (with double at-signs).

Fixes https://github.com/rui314/mold/issues/426
2022-04-24 12:09:50 +08:00
Rui Ueyama b516828b37 Add a script to install build dependencies 2022-04-23 18:34:40 +08:00
Rui Ueyama 5a2589bd94 Upgrade the bundled mimalloc to v2.0.6 2022-04-23 17:16:25 +08:00
Rui Ueyama 4f4bfdbba0 [ELF] Fix a regression
`read_pubnames` uses `compunits_idx`, so we can't move this piece of
code above the loop that computes `compunits_idx`.
2022-04-23 17:12:56 +08:00
Rui Ueyama a830fcdb58 Simplify 2022-04-23 17:01:24 +08:00
Rui Ueyama fd8a6a2c5d Simplify 2022-04-23 16:44:47 +08:00
Rui Ueyama ac244ede10 [ELF] Make .gdb_index contents reproducible
Previously, our output for .gdb_index depended on the iteration order of
ConcurrentMap. Since the iteration order varies on every run of the linker
 due to thread scheduling randomness, our .gdb_index were different on
 every lihnker invocation.

In mold, we guarantee that our output is the same as long as the same
command line options and input files are given to the same version of mold.
So the situation clearly violated the rule.

This commit fixes that issue.
2022-04-23 15:36:21 +08:00
Rui Ueyama 5d4e7a9e31 Update comments 2022-04-23 15:36:21 +08:00
Rui Ueyama 1cce39f113 Merge pull request #455 from llunak/gdb-zero-address-crash
[ELF] do not add 0 addresses to .gdb_index
2022-04-23 11:50:12 +08:00
Rui Ueyama f9768c6352 [ELF] Set _TLS_MODULE_BAES_ to 0 if there's no TLS segment 2022-04-22 17:10:23 +08:00
Rui Ueyama fe134ab3ab Simplify 2022-04-22 16:04:45 +08:00
Rui Ueyama f97ff51a72 [ELF] Assume all output is in little-endian
Currently, is_le is always true, so this is essentially dead code.
2022-04-22 15:45:27 +08:00
Rui Ueyama b05bf30e57 Simplify 2022-04-22 15:41:23 +08:00
Rui Ueyama 6baef60a48 [ELF] Fix .debug_rnglists reader
Fixes https://github.com/rui314/mold/issues/453
2022-04-22 15:00:02 +08:00
Rui Ueyama b101bfbf5f Update comments 2022-04-22 15:00:02 +08:00
Rui Ueyama f3adbe7c8f Update comments 2022-04-22 13:42:35 +08:00
Rui Ueyama a8c4c71dd7 Simplify 2022-04-22 13:24:26 +08:00
Rui Ueyama eb5de9c12f [ELF] Recognize DW_FORM_udata
Fixes https://github.com/rui314/mold/issues/453
2022-04-22 13:11:06 +08:00
Rui Ueyama 23218e7ae2 [ELF] Fix output of --perf 2022-04-22 12:35:05 +08:00
Rui Ueyama 8c5e4df741 [ELF] Ignore comdat groups GCC emits for .debug_macro
GCC creates a .debug_macro section if -g3 is given. That section
is in a comdat group, but the section is directly referred by another
section, which is a violation of the ELF spec.

If a section is deduplicated, we handle any references to that section
as if they had value 0. But that causes a mysterious gdb slowdown.
So we can't set 0 for a dead .debug_macro section.

In this commit, we simply stop deduplicating .debug_macro sections.
This will bloat up debug info, but that's better than producing an
effectively non-debuggable binary.

Fixes https://github.com/rui314/mold/issues/357
Fixes https://github.com/rui314/mold/issues/438
2022-04-22 12:24:22 +08:00
Rui Ueyama 92505f4aea [ELF] Do not fail if a debug info record contains only DW_AT_low_pc 2022-04-22 11:40:55 +08:00
Rui Ueyama d1a723a53a Add inline a hot function 2022-04-22 11:37:23 +08:00
Rui Ueyama de45e4af6d Refactor 2022-04-22 11:33:19 +08:00
Rui Ueyama ad3487376c Imrpove tests 2022-04-22 11:10:45 +08:00
Rui Ueyama 3b3333a27f [ELF] Make --gdb-index work with compressed output debug sections
Previously, we read compressed bogus data from the .debug_info section.

Fixes https://github.com/rui314/mold/issues/440
2022-04-22 08:55:16 +08:00
Luboš Luňák 9c865b976b [ELF] do not add 0 addresses to .gdb_index
GDB crashes in that case in dw2_find_pc_sect_compunit_symtab()
for a reason I do not understand. This is presumably what I was
trying to avoid already in e0dd53b4ad
but didn't quite manage (so reverting that part).

Signed-off-by: Luboš Luňák <[email protected]>
2022-04-21 17:52:05 +02:00
Rui Ueyama 494b28cfb3 Merge pull request #452 from llunak/fix-dw-rle-start-length
[ELF] fix DW_RLE_start_length
2022-04-21 21:01:46 +08:00
Rui Ueyama d4d1f2c697 Simplify 2022-04-21 20:59:48 +08:00
Luboš Luňák 00381ea64b [ELF] fix DW_RLE_start_length
Signed-off-by: Luboš Luňák <[email protected]>
2022-04-21 14:53:40 +02:00
Rui Ueyama 9781d35aba Simplify 2022-04-21 20:50:38 +08:00
Rui Ueyama ead0c96187 Style changes 2022-04-21 20:30:15 +08:00
Rui Ueyama eed62bd9d4 Update a test 2022-04-21 20:05:52 +08:00
Rui Ueyama 6d5c44ab58 Merge pull request #451 from llunak/gcc-dwarf5
[ELF] handle DW_FORM_implicit_const in .debug_abbrev
2022-04-21 20:03:28 +08:00
Luboš Luňák 5b28bfd6c8 [ELF] handle DW_FORM_implicit_const in .debug_abbrev
This one form has an extra uleb field directly in .debug_abbrev,
so it needs to be skipped too. This is not a problem with Clang,
as that one seems to always put DW_TAG_compile_unit first
in .debug_abbrev, but GCC with DWARF5 doesn't and this code
didn't find it.
Another stylistic minor problem in this code was that abbrev_code
and has_children weren't skipped over, although that got silently
handled by the first iteration where the 1-byte has_children
flag was read as 1-byte uleb.
2022-04-21 13:49:41 +02:00
Rui Ueyama bad0cbc72a Revert "Remove a test that doesn't work yet"
This reverts commit bd65e0908f
because --gdb-index should now work with DWARF5.
2022-04-21 19:23:51 +08:00
Rui Ueyama b6fb244f14 Merge pull request #448 from llunak/gdb-index-fixes
Gdb index fixes
2022-04-21 19:20:36 +08:00
Rui Ueyama 1c84145810 Remove an unnecessary command
`make install` now creates /usr/local/libexec/ld as a a relative
symbolic link, so this `ln` is not needed.
2022-04-21 19:11:09 +08:00
Rui Ueyama f945077e42 Merge pull request #450 from marxin/gdb-tests-batch-mode
Use -batch mode for gdb tests.
2022-04-21 18:18:40 +08:00
Luboš Luňák e0dd53b4ad [ELF] use better gdb index address dummy values
If start of the previous is used, gdb crashes for me, but end seems
to be fine. I'm not sure why, but I think end is better because
that is an address that is one beyond the end, so it shouldn't match
anything, or maybe end shouldn't be 0.
Also set some CU index, otherwise those random bogus values look
strange when dumping the content.

Signed-off-by: Luboš Luňák <[email protected]>
2022-04-21 11:04:16 +02:00
Luboš Luňák 267a1ff5b7 [ELF] handle properly DW_FORM_rnglistx
The value is an index (offset by DW_AT_rnglists_base) into the first
part of .debug_rnglists, which is a list of offsets into the second
part of .debug_rnglists, which lists ranges, each entry starting
with a value describing how to interpret the data.

Signed-off-by: Luboš Luňák <[email protected]>
2022-04-21 11:04:16 +02:00
Luboš Luňák 93cefc5280 [ELF] handle properly DW_FORM_addrx*
The value is not the address, it's an index into .debug_addr
that's additionally offset by DW_AT_addr_base.

Signed-off-by: Luboš Luňák <[email protected]>
2022-04-21 11:04:16 +02:00
Martin Liska b3e0955d88 Use -batch mode for gdb tests.
Use the mode, otherwise one can be asked things like:
Quit anyway? (y or n)

which make the test stuck.

Signed-off-by: Martin Liska <[email protected]>
2022-04-21 10:15:45 +02:00
Luboš Luňák c7339c33e0 [ELF] separate reading and skipping of DW_FORM_* data
Handling properly e.g. DW_FORM_loclistx is non-trivial and
actually not needed at all for .gdb_index, so it's enough to know
how to skip it. For the reading function it's enough to handle
only those forms that are needed by DW_AT_low_pc, DW_AT_high_pc
and DW_AT_ranges (and AFAICT the function now includes all of those,
although for some of them the implementation is incorrect).

Signed-off-by: Luboš Luňák <[email protected]>
2022-04-21 10:14:38 +02:00
Luboš Luňák 1ac4030556 [ELF] support DWARF5 split debug compile unit info
For DWARF5 split debug .o's get a slightly different .debug_info
entry that's larger, but for the purpose here it's otherwise
the same.

Signed-off-by: Luboš Luňák <[email protected]>
2022-04-21 10:02:47 +02:00
Luboš Luňák 478871c06e [ELF] do not include base selection range entry in gdb index
DWARF4 2.17.3 says that an entry where start address is the max
possible offset is a base selection entry and the end address
is actually a value to add to all addresses. The gdb_index spec
doesn't say if this entry is allowed in the address list, but
gold doesn't include it, so apply and drop it too.

Signed-off-by: Luboš Luňák <[email protected]>
2022-04-21 10:02:43 +02:00
Luboš Luňák d18d971962 [ELF] detect and bail out on DWARF64
It uses different sizes for some fields, and the dwarf mold code
is not ready to read it (it'd need converting those relevant calls
from reading u32 to optionally reading u64 instead).

Signed-off-by: Luboš Luňák <[email protected]>
2022-04-21 10:02:39 +02:00
Rui Ueyama b26e1a3c32 Fix -Wc++-narrowing errors on a 32 bits host 2022-04-21 15:44:29 +08:00
Rui Ueyama f12a9bb5ff Fix possible "recompile with -fPIC" errors
Fixes https://github.com/rui314/mold/issues/449
2022-04-21 15:38:12 +08:00
Rui Ueyama cf850f8993 [ELF] Handle TLS common symbols
Fixes https://github.com/rui314/mold/issues/447
2022-04-21 13:58:43 +08:00
Rui Ueyama ec062f7241 [ELF] Accept DWARF 2 and 3 for --gdb-index 2022-04-21 10:21:53 +08:00
Rui Ueyama fc798bc4ea Refactor 2022-04-21 09:59:36 +08:00
Rui Ueyama bd65e0908f Remove a test that doesn't work yet 2022-04-21 09:59:36 +08:00
Rui Ueyama 8a5a9e1ffe Revert "[ELF] Do not emit address ranges for --gdb-index"
This reverts commit d4cc0ed3fb
because it broke gdb's stacktrace.
2022-04-21 09:59:36 +08:00
Rui Ueyama c6dace2335 Merge pull request #446 from marxin/fix-warning-for-execstack-if-needed
Fix assembler warnings for a testcase.
2022-04-20 17:09:52 +08:00
Martin Liska 32e0863675 Fix assembler warnings for a testcase.
Fixes:

Testing execstack-if-needed ... {standard input}: Assembler messages:
{standard input}:4: Warning: setting incorrect section type for .note.GNU-stack
{standard input}:4: Warning: setting incorrect section attributes for .note.GNU-stack

Signed-off-by: Martin Liska <[email protected]>
2022-04-20 10:56:23 +02:00
Rui Ueyama 8398d592de [ELF] Add -z execstack-if-needed
Fixes https://github.com/rui314/mold/issues/427
2022-04-20 15:17:55 +08:00
Rui Ueyama fa2ff3b9e9 [ELF] Align .gdb_index to a 4 bytes boundary
.gdb_index contains lots of 4 bytes integers, so we should align
them to 4 bytes boundaries.
2022-04-20 13:59:56 +08:00
Rui Ueyama 3fb93c7cc8 Simplify 2022-04-20 10:21:39 +08:00
Rui Ueyama 8dddd98dfd Update README 2022-04-20 10:14:42 +08:00
Rui Ueyama 17f16d016c Update README 2022-04-20 10:00:48 +08:00
Rui Ueyama 21e1a80cec Add a test 2022-04-19 13:51:38 +08:00
Rui Ueyama d4cc0ed3fb [ELF] Do not emit address ranges for --gdb-index
.gdb_index contains two maps: a map from identifiers (type names,
function names or variable names) to compunits, and a map from
function address ranges to compunits. The latter is harder to create
because we needed to parse DWARF debug records to read function
address ranges. We generally don't want to do that.

This commit stops emitting address ranges. Now, .gdb_index sections
created by our linker contains the zero-length map as address ranges.
Though I'm not 100% sure if this is actually OK, it looks like gdb
works fine with that. It's at least worth a try.

https://github.com/rui314/mold/issues/439
https://github.com/rui314/mold/issues/396
2022-04-19 13:38:25 +08:00
Rui Ueyama a4398c7c32 [ELF] Factor out DWARF-related code to dwarf.cc 2022-04-19 12:43:51 +08:00
Rui Ueyama 7ebd071273 [ELF] Recognize --thinlto-{single-module,cache-dir,cache-policy}
They are defined as aliases for
--plugin-opt=thinlto-{single-module,cache-dir,cache-policy}.

https://github.com/rui314/mold/issues/336
2022-04-18 12:53:59 +08:00
Rui Ueyama 369e8a19c5 Simplify 2022-04-17 19:25:22 +08:00
Rui Ueyama d73587033f Simplify 2022-04-17 19:07:17 +08:00
Rui Ueyama 70bd1adbab [ELF] Handle --print-dependencies early
So that the dependency info is printed out even if the link will
eventually fail.
2022-04-17 19:06:58 +08:00
Rui Ueyama f675faade2 Fix typo 2022-04-17 19:06:58 +08:00
Rui Ueyama 3c895abefa Fix -Wc++11-narrowing
Fixes https://github.com/rui314/mold/issues/437
2022-04-17 16:05:28 +08:00
Rui Ueyama ff1b97bcdb [ELF] Recognize more DW_FORM tags
We don't need values of hte se DW_FORM tags, but we have to be
able to skip them in order to read a .debug_info section.
2022-04-17 16:00:15 +08:00
Rui Ueyama 19ad74ac52 Use python3 instead of python
If a system has only Python 3, it may not have `python` command.
2022-04-17 15:12:35 +08:00
Rui Ueyama 45df585268 Remove test for executable DSO
This test fails on some Linux systems, and it is essentially testing
not the mold's capabilities but the running platform's capabilities.
I don't think there's a portable, guaranteed way to create an
executable shared object on Linux, so even if it happens to work,
I don't think we can guarantee that.
2022-04-17 15:01:28 +08:00
Rui Ueyama deb212bbfc Fix for a compiler that does not support -gz=zlib-gnu 2022-04-17 14:51:23 +08:00
Rui Ueyama 1fa6b3cd17 [ELF] Allow to build only for x86-64 or ARM64
mold is usually built for all supported tagets, namely, x86-64, i386,
ARM32, ARM64 and RISCV64. This is a good thing because it makes cross
compilation easy. That is, as long as you have a copy of the mold linker,
it is guaranteed to work as a cross linker.

However, during a quick debug session in which you build mold many
times, you may want to build mold only for your native target. That
greatly reduces build time because it reduces the amount of code
after template instantiation.

Therefore, in this commit, I introduced new macros,
MOLD_DEBUG_X86_64_ONLY and MOLD_DEBUG_ARM64_ONLY, to build mold for
x86-64 and ARM64 only, respectively.

These flags should never be used for production. They are solely for
debugging purpose.
2022-04-17 13:02:19 +08:00
Rui Ueyama b0596ab8fb Run only for native target 2022-04-17 12:32:42 +08:00
Rui Ueyama 1e59bb518a Add comments 2022-04-17 12:01:15 +08:00
Rui Ueyama efcbb02e83 Remove unnecessary build dependency 2022-04-17 11:55:08 +08:00
Rui Ueyama d8a12490f5 [ELF] Do not exit with 0 if errors occured 2022-04-17 11:46:19 +08:00
Rui Ueyama b5a75c4215 [ELF] Support DWARF5 for --gdb-index
mold used to support only DWARF4.

Fixes https://github.com/rui314/mold/issues/436
2022-04-17 10:19:58 +08:00
Rui Ueyama 5803c3c200 Make /usr/local/libexec/ld really relative
Pointed out by Kévin Le Gouguec in https://github.com/rui314/mold/pull/425.
2022-04-16 17:14:55 +08:00
Rui Ueyama c90801e629 [ELF] Make --gdb-index work for compressed input debug sections
Previously, --gdb-index tries to read bogus compressed data from
input sections if input debug sections are compressed.

Fixes https://github.com/rui314/mold/issues/431
2022-04-16 15:00:21 +08:00
Rui Ueyama 8073a92614 Make /usr/local/libexec/ld a relative symlink
I didn't want to write a Python one-liner in the Makefile, but it
looks like it is in practice one of the most portable way to compute a
relative path.

https://github.com/rui314/mold/pull/425
2022-04-16 11:48:35 +08:00
Rui Ueyama c4c7e45886 Remove the logo from README
Sensitivity to this kind of image varies and could be an unneeded
friction to adopt the mold linker.
2022-04-16 11:00:17 +08:00
Rui Ueyama 6a6ae217a3 Merge pull request #433 from sicherha/suppress-debuginfod-prompt
Fix hanging unit test `gdb-index`
2022-04-16 10:38:11 +08:00
Rui Ueyama 30a0fac952 Merge pull request #434 from sicherha/skip-test-if-dwarfdump-unavailable
Skip test if `dwarfdump` is unavailable
2022-04-16 10:34:22 +08:00
Christoph Erhardt 133946af0c Skip test if dwarfdump is unavailable
Fixes #430.

Signed-off-by: Christoph Erhardt <[email protected]>
2022-04-15 20:34:04 +02:00
Christoph Erhardt c6ae4234b8 Fix hanging unit test gdb-index
This affects Fedora >= 35, where debuginfod is enabled by default. On
such systems, gdb shows the following interactive prompt and then waits
forever:
```
This GDB supports auto-downloading debuginfo from the following URLs:
https://debuginfod.fedoraproject.org/
Enable debuginfod for this session? (y or [n])
```

Unsetting the `DEBUGINFOD_URLS` environment variable disables the prompt
as documented here: https://fedoraproject.org/wiki/Debuginfod#Disabling

Signed-off-by: Christoph Erhardt <[email protected]>
2022-04-15 19:08:15 +02:00
Rui Ueyama 1f55e40a8a Canonicalize the tar file name for ARM64 2022-04-15 20:02:37 +08:00
2160 changed files with 399224 additions and 52773 deletions

No files matched your search

+13
View File
@@ -0,0 +1,13 @@
# These are supported funding model platforms
github: [rui314] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
+118 -32
View File
@@ -4,53 +4,139 @@ on:
branches: [ main ]
pull_request:
branches: [ main ]
env:
UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
jobs:
build-clang:
strategy:
matrix:
target:
# Disable PCH for the default configuration. This prevents relying on implicit includes.
- '-DCMAKE_DISABLE_PRECOMPILE_HEADERS=On'
- '-DMOLD_USE_ASAN=On'
- '-DMOLD_USE_TSAN=On'
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: rui314/setup-mold@v1
with:
make-default: false
- name: apt-get
run: sudo dpkg --add-architecture i386 && sudo apt-get update && sudo apt-get install git build-essential libstdc++-10-dev cmake clang libssl-dev zlib1g-dev libxxhash-dev libtbb-dev git bsdmainutils dwarfdump pkg-config
- uses: actions/checkout@v3
- uses: rui314/setup-mold@staging
- name: install-build-deps
run: sudo ./install-build-deps.sh update
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: make
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make CC=clang CXX=clang++ LDFLAGS=-fuse-ld=mold -j$(nproc)
- name: make test
run: make -j$(nproc) test
- name: build and test
run: |
echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
sudo apt-get install -y clang++-12
mkdir build
cd build
cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++-12 ${{ matrix.target }} ..
cmake --build . -j$(nproc)
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-gcc:
runs-on: ubuntu-20.04
container: gcc:11.1.0
steps:
- uses: actions/checkout@v2
- name: apt-get
- uses: actions/checkout@v3
- name: install-build-deps
run: |
dpkg --add-architecture i386 && apt-get update && apt-get install -y sudo libssl-dev zlib1g-dev libxxhash-dev cmake clang bsdmainutils dwarfdump qemu-user gcc-10-i686-linux-gnu gcc-10-aarch64-linux-gnu gcc-10-riscv64-linux-gnu gcc-10-arm-linux-gnueabihf g++-10-i686-linux-gnu g++-10-aarch64-linux-gnu g++-10-riscv64-linux-gnu g++-10-arm-linux-gnueabihf
ln -sf /usr/bin/i686-linux-gnu-gcc-10 /usr/bin/i686-linux-gnu-gcc
ln -sf /usr/bin/i686-linux-gnu-g++-10 /usr/bin/i686-linux-gnu-g++
ln -sf /usr/bin/aarch64-linux-gnu-gcc-10 /usr/bin/aarch64-linux-gnu-gcc
ln -sf /usr/bin/aarch64-linux-gnu-g++-10 /usr/bin/aarch64-linux-gnu-g++
ln -sf /usr/bin/arm-linux-gnueabihf-gcc-10 /usr/bin/arm-linux-gnueabihf-gcc
ln -sf /usr/bin/arm-linux-gnueabihf-g++-10 /usr/bin/arm-linux-gnueabihf-g++
ln -sf /usr/bin/riscv64-linux-gnu-gcc-10 /usr/bin/riscv64-linux-gnu-gcc
ln -sf /usr/bin/riscv64-linux-gnu-g++-10 /usr/bin/riscv64-linux-gnu-g++
# 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 a RV32 toolchain from third party since it's not available
# as an Ubuntu package.
mkdir /usr/local/rv32
wget -O- -q https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2023.07.07/riscv32-glibc-ubuntu-20.04-gcc-nightly-2023.07.07-nightly.tar.gz | tar -C /usr/local/rv32 --strip-components=1 -xzf -
ln -sf /usr/local/rv32/sysroot /usr/riscv32-linux-gnu
echo '/usr/local/rv32/bin/riscv32-unknown-linux-gnu-gcc -L/usr/riscv32-linux-gnu "$@"' > /usr/bin/riscv32-linux-gnu-gcc
echo '/usr/local/rv32/bin/riscv32-unknown-linux-gnu-g++ -L/usr/riscv32-linux-gnu "$@"' > /usr/bin/riscv32-linux-gnu-g++
chmod 755 /usr/bin/riscv32-linux-gnu-{gcc,g++}
for i in objdump objcopy strip; do
ln -sf /usr/local/rv32/bin/riscv32-unknown-linux-gnu-$i /usr/bin/riscv32-linux-gnu-$i
done
# Install a LoongArch toolchain
mkdir /usr/local/larch
wget -O- -q https://github.com/loongson/build-tools/releases/download/2023.08.08/CLFS-loongarch64-8.1-x86_64-cross-tools-gcc-glibc.tar.xz | tar -C /usr/local/larch --strip-components=1 --xz -xf -
ln -sf /usr/local/larch/target /usr/loongarch64-linux-gnu
cp -r /usr/local/larch/loongarch64-unknown-linux-gnu/lib/* /usr/loongarch64-linux-gnu/lib64/
for i in objdump objcopy strip; do
ln -sf /usr/local/larch/bin/loongarch64-unknown-linux-gnu-$i /usr/bin/loongarch64-linux-gnu-$i
done
echo '/usr/local/larch/bin/loongarch64-unknown-linux-gnu-gcc -L/usr/local/larch/loongarch64-unknown-linux-gnu "$@"' > /usr/bin/loongarch64-linux-gnu-gcc
echo '/usr/local/larch/bin/loongarch64-unknown-linux-gnu-g++ -L/usr/local/larch/loongarch64-unknown-linux-gnu "$@"' > /usr/bin/loongarch64-linux-gnu-g++
chmod 755 /usr/bin/loongarch64-linux-gnu-{gcc,g++}
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: make
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make CC=gcc CXX=g++ -j$(nproc)
- name: make test
run: make -j$(nproc) test-all
- name: build and test
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 --build . -j$(nproc)
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-macos:
runs-on: macos-11
strategy:
matrix:
target:
# Disable PCH for the default configuration. This prevents relying on implicit includes.
- '-DCMAKE_DISABLE_PRECOMPILE_HEADERS=On'
- '-DMOLD_USE_ASAN=On'
steps:
- uses: actions/checkout@v2
- name: brew
run: brew install xxhash
- uses: actions/checkout@v3
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: make
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make -j$(sysctl -n hw.logicalcpu)
- name: make test
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make -j$(sysctl -n hw.logicalcpu) test
- name: build and test
run: |
echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
mkdir build
cd build
cmake ${{ matrix.target }} ..
cmake --build . -j$(sysctl -n hw.physicalcpu)
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- name: build
run: |
mkdir build
cd build
cmake -T clangcl ..
cmake --build . -j $Env:NUMBER_OF_PROCESSORS
+15
View File
@@ -0,0 +1,15 @@
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
+3
View File
@@ -1,6 +1,7 @@
*.o
*.so
*~
**/.DS_Store
.cache
compile_commands.json
/mold
@@ -8,7 +9,9 @@ compile_commands.json
options.inc
core
ld
ld64
ld64.mold
gentoo
out
/mold-*.tar.gz
/build*
+487
View File
@@ -0,0 +1,487 @@
# 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.
#
# 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`.
#
# -DCMAKE_CXX_COMPILER=<command-name>
#
# Specifies a 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`.
#
# -DCMAKE_BUILD_TYPE=[Debug | Release | RelWithDebInfo | MinSizeRel]
#
# Specifies a 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.
#
# With cmake, you may run `cmake --install .` instead of `make install` to
# install build artifacts to system directories. If you want to install
# artifacts to a temporary target directory, run `cmake --install . --prefix
# <dir>`.
#
# You can see the current cmake variables and their values by running
# `cmake -N -L .` in a build directory.
#
# Note that in this file, we provide various dials and knobs to configure
# 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
# features and behave exactly the same.
cmake_minimum_required(VERSION 3.14)
project(mold VERSION 2.3.0)
include(CMakeDependentOption)
include(GNUInstallDirs)
# Build mold itself using mold if -DMOLD_USE_MOLD=ON
option(MOLD_USE_MOLD "Use mold to build mold" OFF)
if(MOLD_USE_MOLD)
add_link_options(-fuse-ld=mold -Wl,--gdb-index)
endif()
add_executable(mold)
target_compile_features(mold PRIVATE cxx_std_20)
target_link_libraries(mold PRIVATE ${CMAKE_DL_LIBS})
if(NOT "${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "MSVC")
target_compile_options(mold PRIVATE
-fno-exceptions
-fno-unwind-tables
-fno-asynchronous-unwind-tables
-Wall
-Wextra
-Wno-sign-compare
-Wno-unused-function
-Wno-unused-parameter
-Wno-missing-field-initializers
-ggnu-pubnames)
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_options(mold PRIVATE -D_GLIBCXX_ASSERTIONS)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
set(OPENBSD ON)
endif()
# Build mold with -flto if -DMOLD_LTO=ON
option(MOLD_LTO "Build mold with link-time optimization enabled")
if(MOLD_LTO)
set_property(TARGET mold PROPERTY INTERPROCEDURAL_OPTIMIZATION ON)
endif()
# Enable AddressSanitizer if -DMOLD_USE_ASAN=ON
option(MOLD_USE_ASAN "Build mold with AddressSanitizer" OFF)
if(MOLD_USE_ASAN)
target_compile_options(mold PRIVATE -fsanitize=address -fsanitize=undefined)
target_link_options(mold PRIVATE -fsanitize=address -fsanitize=undefined)
endif()
# Enabled ThreadSanitizer if -DMOLD_USE_TSAN=ON
option(MOLD_USE_TSAN "Build mold with ThreadSanitizer" OFF)
if(MOLD_USE_TSAN)
target_compile_options(mold PRIVATE -fsanitize=thread)
target_link_options(mold PRIVATE -fsanitize=thread)
endif()
# Statically-link libstdc++ if -DMOLD_MOSTLY_STATIC=ON.
#
# This option is intended to be used by `./dist.sh` script to create a
# mold binary that works on various Linux distros. You probably don't
# need nor want to set this to ON.
option(MOLD_MOSTLY_STATIC "Statically link libstdc++ and libcrypto" OFF)
if(MOLD_MOSTLY_STATIC)
target_link_options(mold PRIVATE -static-libstdc++)
endif()
# Find zlib. If libz.so is not found, we compile a bundled one and
# statically-link it to mold.
find_package(ZLIB QUIET)
if(ZLIB_FOUND AND NOT MOLD_MOSTLY_STATIC)
target_link_libraries(mold PRIVATE ZLIB::ZLIB)
else()
add_subdirectory(third-party/zlib EXCLUDE_FROM_ALL)
target_include_directories(zlibstatic INTERFACE third-party/zlib
$<TARGET_PROPERTY:zlibstatic,BINARY_DIR>)
target_link_libraries(mold PRIVATE zlibstatic)
endif()
# Find BLAKE3 cryptographic hash library. Just like zlib, if libblkae3.so
# is not found, we compile a bundled one and statically-link it to mold.
find_package(BLAKE3 QUIET)
if(BLAKE3_FOUND AND NOT MOLD_MOSTLY_STATIC)
target_link_libraries(mold PRIVATE BLAKE3::blake3)
else()
add_subdirectory(third-party/blake3/c EXCLUDE_FROM_ALL)
target_link_libraries(mold PRIVATE blake3)
target_include_directories(mold PUBLIC third-party/blake3/c)
endif()
# Find zstd compression library. If libzstd.so is not found, we compile a
# bundled one and statically-link it to mold.
include(CheckIncludeFile)
check_include_file(zstd.h HAVE_ZSTD_H)
if(HAVE_ZSTD_H AND NOT MOLD_MOSTLY_STATIC)
target_link_libraries(mold PRIVATE zstd)
else()
add_subdirectory(third-party/zstd/build/cmake EXCLUDE_FROM_ALL)
target_compile_definitions(libzstd_static PRIVATE
ZSTD_BUILD_STATIC=1
ZSTD_BUILD_SHARED=0
ZSTD_BUILD_PROGRAMS=0
ZSTD_MULTITHREAD_SUPPORT=0
ZSTD_BUILD_TESTS=0)
target_include_directories(mold PUBLIC third-party/zstd/lib)
target_link_libraries(mold PRIVATE libzstd_static)
endif()
# Find mimalloc. mimalloc is an alternative malloc implementation
# optimized for multi-threaded applications.
#
# If you want to use the usual libc's malloc, pass -DMOLD_USE_MIMALLOC=OFF.
#
# We enable mimalloc by default for 64-bit targets. It doesn't seem to
# be stable on 32-bit targets.
include(CheckCXXSourceCompiles)
check_cxx_source_compiles(
"static_assert(sizeof(void *) == 4); int main() {}" P32)
cmake_dependent_option(MOLD_USE_MIMALLOC "Use mimalloc" ON
"NOT APPLE; NOT ANDROID; NOT OPENBSD; NOT P32" OFF)
cmake_dependent_option(
MOLD_USE_SYSTEM_MIMALLOC "Use system or vendored mimalloc" OFF
MOLD_USE_MIMALLOC OFF)
# By default, we build a bundled mimalloc and statically-link it to
# mold. If you want to dynamically link to the system's
# libmimalloc.so, pass -DMOLD_USE_SYSTEM_MIMALLOC=ON.
if(MOLD_USE_MIMALLOC)
if(MOLD_USE_SYSTEM_MIMALLOC)
find_package(mimalloc REQUIRED)
target_link_libraries(mold PRIVATE mimalloc)
target_compile_definitions(mold PRIVATE USE_SYSTEM_MIMALLOC)
else()
function(mold_add_mimalloc)
set(MI_BUILD_STATIC ON CACHE INTERNAL "")
set(MI_BUILD_TESTS OFF CACHE INTERNAL "")
add_subdirectory(third-party/mimalloc EXCLUDE_FROM_ALL)
target_compile_definitions(mimalloc-static PRIVATE MI_USE_ENVIRON=0)
target_link_libraries(mold PRIVATE mimalloc-static)
endfunction()
mold_add_mimalloc()
endif()
endif()
# Find TBB. TBB (OneTBB or Intel TBB) is a high-level threading library.
# Use of this library is mandatory.
#
# By default, we build a bundled one and statically-link the library
# to mold. If you want to link to the system's libtbb2.so, pass
# -DMOLD_USE_SYSTEM_TBB=ON.
option(MOLD_USE_SYSTEM_TBB "Use system or vendored TBB" OFF)
if(MOLD_USE_SYSTEM_TBB)
find_package(TBB REQUIRED)
target_link_libraries(mold PRIVATE TBB::tbb)
else()
function(mold_add_tbb)
set(BUILD_SHARED_LIBS OFF)
set(TBB_TEST OFF CACHE INTERNAL "")
set(TBB_STRICT OFF CACHE INTERNAL "")
add_subdirectory(third-party/tbb EXCLUDE_FROM_ALL)
target_compile_definitions(tbb PRIVATE __TBB_DYNAMIC_LOAD_ENABLED=0)
target_link_libraries(mold PRIVATE TBB::tbb)
endfunction()
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")
message(FATAL_ERROR
"Your compiler is not supported; install Clang from Visual Studio Installer and re-run cmake with '-T clangcl'")
endif()
target_compile_definitions(mold PRIVATE NOGDI NOMINMAX)
if(MINGW)
target_compile_definitions(mold PRIVATE _WIN32_WINNT=0xA00)
target_link_libraries(mold PRIVATE bcrypt)
endif()
else()
include(CheckLibraryExists)
check_library_exists(m pow "" LIBM_FOUND)
if(LIBM_FOUND)
target_link_libraries(mold PRIVATE m)
endif()
endif()
# Build mold-wrapper.so
if(NOT APPLE AND NOT WIN32)
add_library(mold-wrapper SHARED)
install(TARGETS mold-wrapper DESTINATION ${CMAKE_INSTALL_LIBDIR}/mold)
# Remove the default `lib` prefix
set_target_properties(mold-wrapper PROPERTIES PREFIX "")
target_link_libraries(mold-wrapper PRIVATE ${CMAKE_DL_LIBS})
target_sources(mold-wrapper PRIVATE elf/mold-wrapper.c)
endif()
# If atomics doesn't work by default, add -latomic.
# We need the flag on riscv, armv6 and m68k.
include(CheckCXXSourceCompiles)
check_cxx_source_compiles("#include <atomic>
int main() {
std::atomic_uint8_t a;
std::atomic_uint16_t b;
std::atomic_uint32_t c;
std::atomic_uint64_t d;
return ++a + ++b + ++c + ++d;
}" HAVE_FULL_ATOMIC_SUPPORT)
if(NOT HAVE_FULL_ATOMIC_SUPPORT)
target_link_libraries(mold PRIVATE atomic)
endif()
# Add -pthread
if(NOT APPLE AND NOT MSVC)
target_compile_options(mold PRIVATE -pthread)
target_link_options(mold PRIVATE -pthread)
endif()
# Create a .cc file containing the current git hash for `mold --version`.
add_custom_target(git_hash
COMMAND ${CMAKE_COMMAND}
-DSOURCE_DIR=${CMAKE_SOURCE_DIR}
-DOUTPUT_FILE=${CMAKE_BINARY_DIR}/git-hash.cc
-P ${CMAKE_SOURCE_DIR}/common/update-git-hash.cmake
DEPENDS common/update-git-hash.cmake
BYPRODUCTS git-hash.cc
VERBATIM)
add_dependencies(mold git_hash)
# Create config.h file
configure_file(common/config.h.in config.h)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
# Almost all functions are template in mold which take a target type
# (e.g. X86_64) as its type parameter. Since we suport more than 10
# targets, compiling a single source file for all the targets is very
# slow.
#
# As a workaround, we create a .cc file for each target and spawn many
# compiler instances. This is hacky but greatly reduces compile time
# on a multicore machine.
list(APPEND MOLD_ELF_TARGETS
X86_64 I386 ARM64 ARM32 RV32LE RV32BE RV64LE RV64BE PPC32 PPC64V1 PPC64V2
S390X SPARC64 M68K SH4 ALPHA LOONGARCH32 LOONGARCH64)
list(APPEND MOLD_ELF_TEMPLATE_FILES
elf/arch-loongarch.cc
elf/arch-riscv.cc
elf/cmdline.cc
elf/gc-sections.cc
elf/gdb-index.cc
elf/icf.cc
elf/input-files.cc
elf/input-sections.cc
elf/jobs.cc
elf/linker-script.cc
elf/lto.cc
elf/main.cc
elf/mapfile.cc
elf/output-chunks.cc
elf/passes.cc
elf/relocatable.cc
elf/subprocess.cc
elf/thunks.cc
elf/tls.cc
)
list(APPEND MOLD_MACHO_TARGETS X86_64 ARM64)
list(APPEND MOLD_MACHO_TEMPLATE_FILES
macho/cmdline.cc
macho/dead-strip.cc
macho/input-files.cc
macho/input-sections.cc
macho/lto.cc
macho/main.cc
macho/mapfile.cc
macho/output-chunks.cc
macho/tapi.cc
)
function(mold_instantiate_templates SOURCE TARGET)
set(PATH ${CMAKE_BINARY_DIR}/${SOURCE}.${TARGET}.cc)
if(NOT EXISTS ${PATH})
file(WRITE ${PATH} "#define MOLD_${TARGET} 1
#define MOLD_TARGET ${TARGET}
#include \"${CMAKE_SOURCE_DIR}/${SOURCE}\"
")
endif()
target_sources(mold PRIVATE ${PATH})
endfunction()
foreach (SOURCE IN LISTS MOLD_ELF_TEMPLATE_FILES)
foreach(TARGET IN LISTS MOLD_ELF_TARGETS)
mold_instantiate_templates(${SOURCE} ${TARGET})
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
common/demangle.cc
common/filepath.cc
common/glob.cc
common/hyperloglog.cc
common/main.cc
common/multi-glob.cc
common/perf.cc
common/tar.cc
common/uuid.cc
elf/arch-alpha.cc
elf/arch-arm32.cc
elf/arch-arm64.cc
elf/arch-i386.cc
elf/arch-m68k.cc
elf/arch-ppc32.cc
elf/arch-ppc64v1.cc
elf/arch-ppc64v2.cc
elf/arch-s390x.cc
elf/arch-sh4.cc
elf/arch-sparc64.cc
elf/arch-x86-64.cc
elf/elf.cc
git-hash.cc
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)
endif()
include(CTest)
if(BUILD_TESTING)
# Create the ld and ld64 symlinks required for testing
if(NOT WIN32)
add_custom_command(
TARGET mold POST_BUILD
COMMAND ${CMAKE_COMMAND} -E create_symlink mold ld
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})
add_subdirectory(test/elf)
endif()
endif()
if(NOT CMAKE_SKIP_INSTALL_RULES)
install(TARGETS mold RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES docs/mold.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1/)
if(EXISTS "${CMAKE_SOURCE_DIR}/LICENSE")
install(FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR})
endif()
if(EXISTS "${CMAKE_SOURCE_DIR}/LICENSE.third-party")
install(FILES "LICENSE.third-party" DESTINATION "${CMAKE_INSTALL_DOCDIR}")
endif()
function(mold_install_relative_symlink OLD NEW)
install(CODE "
get_filename_component(PREFIX_ABS \${CMAKE_INSTALL_PREFIX}/ ABSOLUTE)
get_filename_component(OLD_ABS ${OLD} ABSOLUTE BASE_DIR \${PREFIX_ABS})
get_filename_component(NEW_ABS ${NEW} ABSOLUTE BASE_DIR \${PREFIX_ABS})
get_filename_component(NEW_DIR \${NEW_ABS} DIRECTORY)
file(RELATIVE_PATH OLD_REL \${NEW_DIR} \${OLD_ABS})
message(STATUS \"Installing symlink: \$ENV{DESTDIR}\${NEW_ABS} -> \${OLD_REL}\")
file(MAKE_DIRECTORY \$ENV{DESTDIR}\${NEW_DIR})
file(CREATE_LINK \${OLD_REL} \$ENV{DESTDIR}\${NEW_ABS} SYMBOLIC)")
endfunction()
mold_install_relative_symlink(${CMAKE_INSTALL_BINDIR}/mold${CMAKE_EXECUTABLE_SUFFIX}
${CMAKE_INSTALL_LIBEXECDIR}/mold/ld${CMAKE_EXECUTABLE_SUFFIX})
mold_install_relative_symlink(${CMAKE_INSTALL_BINDIR}/mold${CMAKE_EXECUTABLE_SUFFIX}
${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()
-54
View File
@@ -1,54 +0,0 @@
# Contributing to mold
Summary: When you send a patch, you must agree that the patch is
released under the dual license of the GNU AGPLv3 and the MIT license.
As an indication of agreement, please add a `Signed-off-by` line to
your commit message using `git commit -s`.
Here is the long official version:
mold is currently licensed under the GNU AGPLv3, but I want to relicense
it under a more liberal license in the future. To make this possible,
I want all patches to be submitted under the dual license of the GNU
AGPLv3 and the MIT license (the "Dual License") from the beginning.
To make it clear that contributors have agreed to release their
patches under the Dual License, I introduced a sign-off procedure just
like the Linux kernel project did. The sign-off is a simple line at
the end of the git commit message, which certifies that you wrote it
or otherwise have the right to pass it on as an open-source patch. The
rules are pretty simple: if you can certify the below:
> ## Developer's Certificate of Origin
>
> By making a contribution to this project, I certify that:
>
> 1. The contribution was created in whole or in part by me and I
> have the right to submit it under the Dual License; or
>
> 2. The contribution is based upon previous work that, to the best
> of my knowledge, is covered under the the Dual License and I
> have the right under the Dual License to submit that work with
> modifications, whether created in whole or in part by me, under
> the Dual License (unless I am permitted to submit under
> different licenses), as indicated in the file; or
>
> 3. The contribution was provided directly to me by some other
> person who certified (1), (2) or (3) and I have not modified it.
>
> 4. I understand and agree that this project and the contribution
> are public and that a record of the contribution (including all
> personal information I submit with it, including my sign-off) is
> maintained indefinitely and may be redistributed consistent with
> this project or the open source license(s) involved.
then you just add a line saying:
```
Signed-off-by: Random J Developer <[email protected]>
```
using your real name (sorry, no pseudonyms or anonymous contributions.)
This will be done for you automatically if you use ``git commit -s``.
Reverts should also include "Signed-off-by". ``git revert -s`` does that
for you.
-22
View File
@@ -1,22 +0,0 @@
# This dockerfile creates a reproducible build environment for mold.
#
# $ docker build -t rui314/mold-builder:v1-$(uname -m) .
# $ docker push rui314/mold-builder:v1-$(uname -m)
FROM ubuntu:18.04
RUN apt-get update && \
TZ=Europe/London apt-get install -y tzdata && \
apt-get install -y --no-install-recommends software-properties-common && \
add-apt-repository -y ppa:ubuntu-toolchain-r/test && \
apt-get install -y --no-install-recommends build-essential git \
wget pkg-config cmake libstdc++-11-dev zlib1g-dev gpg gpg-agent && \
bash -c "$(wget -O- https://apt.llvm.org/llvm.sh)" && \
apt-get install -y --no-install-recommends clang-14 && \
apt clean && \
rm -rf /var/lib/apt/lists/* && \
cd / && \
wget -O- https://www.openssl.org/source/openssl-3.0.2.tar.gz | tar xzf - && \
mv openssl-3.0.2 openssl && \
cd openssl && \
./Configure --prefix=/openssl && \
make -j$(nproc)
+21 -671
View File
@@ -1,671 +1,21 @@
Note: I'm looking for a sponsor who wants to purchase the copyright
of this work and relicense it under a more liberal license such as
the MIT license. For now, mold is released under the GNU AGPL v3.
If you just want to use mold but do not want to use AGPL software
in your organization, a commercial license is available. Please
contact [email protected].
--
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.
MIT License
Copyright (c) 2023 Rui Ueyama
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+348
View File
@@ -0,0 +1,348 @@
Mold includes a number of subcomponents with separate copyright notices
and license terms. Your use of the source code for the these subcomponents
is subject to the terms and conditions of the following licenses.
=== For the third-party/mimalloc component:
MIT License
Copyright (c) 2018-2021 Microsoft Corporation, Daan Leijen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
=== For the third-party/rust-demangle component:
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
=== For the third-party/tbb component:
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
=== For the third-party/xxhash component:
xxHash Library
Copyright (c) 2012-2020 Yann Collet
All rights reserved.
BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
=== For the third-party/zlib component:
(C) 1995-2022 Jean-loup Gailly and Mark Adler
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Jean-loup Gailly Mark Adler
[email protected] [email protected]
=== For the third-party/zstd component:
BSD License
For Zstandard software
Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name Facebook nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-224
View File
@@ -1,224 +0,0 @@
# If you want to enable ASAN, run `make` with the following options:
#
# make CXXFLAGS='-fsanitize=address -g' LDFLAGS=-fsanitize=address USE_MIMALLOC=0
VERSION = 1.2.0
PREFIX = /usr/local
BINDIR = $(PREFIX)/bin
LIBDIR = $(PREFIX)/lib
LIBEXECDIR = $(PREFIX)/libexec
MANDIR = $(PREFIX)/share/man
INSTALL = install
INSTALL_PROGRAM = $(INSTALL)
INSTALL_DATA = $(INSTALL) -m 644
D = $(DESTDIR)
# CXX defaults to `g++`. Rewrite it with a vendor-neutral compiler
# name `c++`.
ifeq ($(origin CXX), default)
CXX = c++
endif
# Allow overriding pkg-config binary
PKG_CONFIG = pkg-config
# If you want to keep symbols in the installed binary, run make with
# `STRIP=true` to run /bin/true instead of the strip command.
STRIP = strip
SRCS = $(wildcard *.cc elf/*.cc macho/*.cc)
OBJS = $(SRCS:%.cc=out/%.o)
OS := $(shell uname -s)
ARCH := $(shell uname -m)
IS_ANDROID = 0
ifneq ($(findstring -android,$(shell $(CC) -dumpmachine)),)
IS_ANDROID = 1
endif
# If you want to compile mold for debugging, invoke make as
# `make CXXFLAGS=-g`.
CFLAGS = -O2
CXXFLAGS = -O2
MOLD_CXXFLAGS := -std=c++20 -fno-exceptions -fno-unwind-tables \
-fno-asynchronous-unwind-tables -Ithird-party/xxhash \
-DMOLD_VERSION=\"$(VERSION)\" -DLIBDIR="\"$(LIBDIR)\""
MOLD_LDFLAGS := -pthread -lz -lm -ldl
GIT_HASH := $(shell [ -d .git ] && git rev-parse HEAD)
ifneq ($(GIT_HASH),)
MOLD_CXXFLAGS += -DGIT_HASH=\"$(GIT_HASH)\"
endif
LTO = 0
ifeq ($(LTO), 1)
CXXFLAGS += -flto -O3
LDFLAGS += -flto
endif
# By default, we want to use mimalloc as a memory allocator. mimalloc
# is disabled on macOS and Android because it didn't work on those hosts.
USE_MIMALLOC = 1
ifeq ($(OS), Darwin)
USE_MIMALLOC = 0
else ifeq ($(IS_ANDROID), 1)
USE_MIMALLOC = 0
endif
ifeq ($(USE_MIMALLOC), 1)
ifdef SYSTEM_MIMALLOC
MOLD_CXXFLAGS += -DUSE_SYSTEM_MIMALLOC
MOLD_LDFLAGS += -lmimalloc
else
MIMALLOC_LIB = out/mimalloc/libmimalloc.a
MOLD_CXXFLAGS += -Ithird-party/mimalloc/include
MOLD_LDFLAGS += -Wl,-whole-archive $(MIMALLOC_LIB) -Wl,-no-whole-archive
endif
endif
ifdef SYSTEM_TBB
MOLD_LDFLAGS += -ltbb
else
TBB_LIB = out/tbb/libs/libtbb.a
MOLD_LDFLAGS += $(TBB_LIB)
MOLD_CXXFLAGS += -Ithird-party/tbb/include
endif
ifeq ($(OS), Linux)
ifeq ($(IS_ANDROID), 0)
# glibc before 2.17 need librt for clock_gettime
MOLD_LDFLAGS += -Wl,-push-state -Wl,-as-needed -lrt -Wl,-pop-state
endif
endif
NEEDS_LIBCRYPTO = 0
ifneq ($(OS), Darwin)
NEEDS_LIBCRYPTO = 1
endif
ifeq ($(NEEDS_LIBCRYPTO), 1)
MOLD_CXXFLAGS += $(shell $(PKG_CONFIG) --cflags-only-I openssl)
MOLD_LDFLAGS += $(shell $(PKG_CONFIG) --libs-only-L openssl) -lcrypto
endif
# '-latomic' flag is needed building on riscv64 system
# RV32 system doesn't tested yet
# seems like '-atomic' would be better but not working.
ifeq ($(ARCH), riscv64)
MOLD_LDFLAGS += -latomic
endif
# -Wc++11-narrowing is a fatal error on Android, so disable it.
ifeq ($(IS_ANDROID), 1)
MOLD_CXXFLAGS += -Wno-c++11-narrowing
endif
ifeq ($(OS), Linux)
MOLD_WRAPPER_LDFLAGS = -Wl,-push-state -Wl,-no-as-needed -ldl -Wl,-pop-state
endif
DEPFLAGS = -MT $@ -MMD -MP -MF out/$*.d
all: mold mold-wrapper.so
-include $(SRCS:%.cc=out/%.d)
mold: $(OBJS) $(MIMALLOC_LIB) $(TBB_LIB)
$(CXX) $(OBJS) -o $@ $(MOLD_LDFLAGS) $(LDFLAGS)
ln -sf mold ld
ln -sf mold ld64.mold
mold-wrapper.so: elf/mold-wrapper.c
$(CC) $(DEPFLAGS) $(CFLAGS) -fPIC -shared -o $@ $< $(MOLD_WRAPPER_LDFLAGS) $(LDFLAGS)
out/%.o: %.cc out/elf/.keep out/macho/.keep
$(CXX) $(MOLD_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS) -c -o $@ $<
out/elf/.keep out/macho/.keep:
mkdir -p $(@D)
touch $@
$(MIMALLOC_LIB):
mkdir -p out/mimalloc
(cd out/mimalloc; CFLAGS=-DMI_USE_ENVIRON=0 cmake -G'Unix Makefiles' ../../third-party/mimalloc)
$(MAKE) -C out/mimalloc mimalloc-static
$(TBB_LIB):
mkdir -p out/tbb
(cd out/tbb; cmake -G'Unix Makefiles' -DBUILD_SHARED_LIBS=OFF -DTBB_TEST=OFF -DCMAKE_CXX_FLAGS="$(CXXFLAGS) -D__TBB_DYNAMIC_LOAD_ENABLED=0" -DTBB_STRICT=OFF ../../third-party/tbb)
$(MAKE) -C out/tbb tbb
(cd out/tbb; ln -sf *_relwithdebinfo libs)
test tests check: all
ifeq ($(OS), Darwin)
$(MAKE) -C test -f Makefile.darwin --no-print-directory
else
$(MAKE) -C test -f Makefile.linux --no-print-directory --output-sync
endif
@if test -t 1; then \
printf '\e[32mPassed all tests\e[0m\n'; \
else \
echo 'Passed all tests'; \
fi
test-x86-64: all
@echo x86_64
CC=x86_64-linux-gnu-gcc CXX=x86_64-linux-gnu-g++ GCC=x86_64-linux-gnu-gcc GXX=x86_64-linux-gnu-g++ OBJDUMP=x86_64-linux-gnu-objdump MACHINE=x86_64 QEMU='qemu-x86_64 -L /usr/x86_64-linux-gnu' $(MAKE) test
test-i386: all
@echo i386
CC=i686-linux-gnu-gcc CXX=i686-linux-gnu-g++ GCC=i686-linux-gnu-gcc GXX=i686-linux-gnu-g++ OBJDUMP=x86_64-linux-gnu-objdump MACHINE=i386 QEMU='qemu-i386 -L /usr/i686-linux-gnu' $(MAKE) test
test-arm64: all
@echo arm64
CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ GCC=aarch64-linux-gnu-gcc GXX=aarch64-linux-gnu-g++ OBJDUMP=aarch64-linux-gnu-objdump MACHINE=aarch64 QEMU='qemu-aarch64 -L /usr/aarch64-linux-gnu' $(MAKE) test
test-arm32: all
@echo arm
CC=arm-linux-gnueabihf-gcc CXX=arm-linux-gnueabihf-g++ GCC=arm-linux-gnueabihf-gcc GXX=arm-linux-gnueabihf-g++ OBJDUMP=arm-linux-gnueabihf-objdump MACHINE=arm QEMU='qemu-arm -L /usr/arm-linux-gnueabihf' $(MAKE) test
test-riscv64: all
@echo riscv64
CC=riscv64-linux-gnu-gcc CXX=riscv64-linux-gnu-g++ GCC=riscv64-linux-gnu-gcc GXX=riscv64-linux-gnu-g++ OBJDUMP=riscv64-linux-gnu-objdump MACHINE=riscv64 QEMU='qemu-riscv64 -L /usr/riscv64-linux-gnu' $(MAKE) test
test-all:
$(MAKE) test-x86-64
$(MAKE) test-i386
$(MAKE) test-arm64
$(MAKE) test-arm32
$(MAKE) test-riscv64
install: all
$(INSTALL) -d $D$(BINDIR)
$(INSTALL_PROGRAM) mold $D$(BINDIR)
$(STRIP) $D$(BINDIR)/mold
$(INSTALL) -d $D$(LIBDIR)/mold
$(INSTALL_DATA) mold-wrapper.so $D$(LIBDIR)/mold
$(STRIP) $D$(LIBDIR)/mold/mold-wrapper.so
$(INSTALL) -d $D$(LIBEXECDIR)/mold
ln -sf $(BINDIR)/mold $D$(LIBEXECDIR)/mold/ld
$(INSTALL) -d $D$(MANDIR)/man1
$(INSTALL_DATA) docs/mold.1 $D$(MANDIR)/man1
ln -sf mold $D$(BINDIR)/ld.mold
ln -sf mold $D$(BINDIR)/ld64.mold
uninstall:
rm -f $D$(BINDIR)/mold $D$(BINDIR)/ld.mold $D$(BINDIR)/ld64.mold
rm -f $D$(MANDIR)/man1/mold.1
rm -rf $D$(LIBDIR)/mold
clean:
rm -rf *~ mold mold-wrapper.so out ld ld64.mold mold-*-linux.tar.gz
.PHONY: all test tests check clean test-x86-64 test-i386 test-arm64 test-arm32 test-riscv64 test-all
+171 -123
View File
@@ -1,14 +1,19 @@
# mold: A Modern Linker
mold is a faster drop-in replacement for existing Unix linkers.
It is several times faster than the LLVM lld linker, the second-fastest
open-source linker which I originally created a few years ago.
mold is designed to increase developer productivity by reducing
build time, especially in rapid debug-edit-rebuild cycles.
<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>
Here is a performance comparison of GNU gold, LLVM lld, and mold for
linking final debuginfo-enabled executables of major large programs
on a simulated 8-core 16-threads machine.
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.
![Link speed comparison](docs/comparison.png)
@@ -18,118 +23,106 @@ on a simulated 8-core 16-threads machine.
| Clang 13 (3.18 GiB) | 64.12s | 5.82s | 2.90s
| Firefox 89 libxul (1.64 GiB) | 32.95s | 6.80s | 1.42s
mold is so fast that it is only 2x _slower_ than `cp` on the same
machine. Feel free to [file a bug](https://github.com/rui314/mold/issues)
if you find mold is not faster than other linkers.
mold 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
free to [file a bug report](https://github.com/rui314/mold/issues).
mold currently supports x86-64, i386, ARM32, ARM64 and 64-bit RISC-V.
mold supports x86-64, i386, ARM64, ARM32, 64-bit/32-bit little/big-endian
RISC-V, 32-bit PowerPC, 64-bit big-endian PowerPC ELFv1, 64-bit little-endian
PowerPC ELFv2, s390x, 64-bit/32-bit LoongArch, SPARC64, m68k, SH-4, and DEC
Alpha.
## Why does the speed of linking matter?
mold/macOS is commercial software. For mold/macOS, please visit
https://github.com/bluewhalesystems/sold.
If you are using a compiled language such as C, C++ or Rust, a build
consists of two phases. In the first phase, a compiler compiles a
source file into an object file (`.o` files). In the second phase,
a linker takes all object files to combine them into a single executable
or a shared library file.
## Why does linking speed matter?
The second phase takes a long time if your build output is large.
mold can make it faster, saving your time and keeping you from being
distracted while waiting for a long build to finish. The difference is
most noticeable when you are in rapid debug-edit-rebuild cycles.
If you are using a compiled language such as C, C++, or Rust, a build consists
of two phases. In the first phase, a compiler compiles source files into
object files (`.o` files). In the second phase, a linker takes all object
files and combines them into a single executable or shared library file.
## Install
The second phase can be time-consuming if your build output is large. mold can
speed up this process, saving you time and preventing distractions while
waiting for a lengthy build to finish. The difference is most noticeable
during rapid debug-edit-rebuild cycles.
Binary packages for the following distros are currently available.
## Installation
Binary packages for the following systems are currently available:
[![Packaging status](https://repology.org/badge/vertical-allrepos/mold.svg)](https://repology.org/project/mold/versions)
## How to build
## How to Build
mold is written in C++20, so if you build mold yourself, you need a
recent version of a C++ compiler and a C++ standard library. GCC 10.2
or Clang 12.0.0 as well as libstdc++ 10 or libc++ 7 are recommended.
mold is written in C++20, so if you build mold yourself, you will need a
recent version of a C++ compiler and a C++ standard library. We recommend GCC
10.2 or Clang 12.0.0 (or later) and libstdc++ 10 or libc++ 7 (or later).
I'm using Ubuntu 20.04 as a development platform. In that environment,
you can build mold by the following commands.
### Install Dependencies
### Install dependencies
#### Ubuntu 20.04 and later / Debian 11 and later
```shell
sudo apt-get update
sudo apt-get install -y build-essential git clang cmake libstdc++-10-dev libssl-dev libxxhash-dev zlib1g-dev pkg-config
```
#### Fedora 34 and later
```shell
sudo dnf install -y git clang cmake openssl-devel xxhash-devel zlib-devel libstdc++-devel
```
To install build dependencies, run `./install-build-deps.sh` in this
directory. It will detect your Linux distribution and attempt to install the
necessary packages. You may need to run it as root.
### Compile mold
```shell
git clone https://github.com/rui314/mold.git
cd mold
git checkout v1.2.0
make -j$(nproc) CXX=clang++
sudo make install
mkdir mold/build
cd mold/build
git checkout v2.3.0
../install-build-deps.sh
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=c++ ..
cmake --build . -j $(nproc)
sudo cmake --build . --target install
```
You may need to pass a C++20 compiler command name to `make`.
In the above case, `clang++` is passed. If it doesn't work for you,
try a specific version of a compiler such as `g++-10` or `clang++-12`.
You might need to pass a C++20 compiler command name to `cmake`. In the
example above, `c++` is passed. If that doesn't work for you, try a specific
version of a compiler, such as `g++-10` or `clang++-12`.
By default, `mold` is installed to `/usr/local/bin`.
By default, `mold` is installed to `/usr/local/bin`. You can change the
installation location by passing `-DCMAKE_INSTALL_PREFIX=<directory>`.
For other cmake options, see the comments in `CMakeLists.txt`.
If you don't use a recent enough Linux distribution, or if for any reason
`make` in the above commands doesn't work for you, you can use Docker to
build it in a Docker environment. To do so, just run `./dist.sh` in this
directory instead of running `make -j$(nproc)`. The shell script creates a
Ubuntu 18.04 Docker image, installs necessary tools and libraries to it,
builds mold and auxiliary files, and packs them into a single tar file
`mold-$version-$arch-linux.tar.gz`. You can extract the tar file anywhere
and use `mold` executable in it.
`make test` depends on a few more packages. To install, run the following commands:
```shell
sudo dpkg --add-architecture i386
sudo apt update
sudo apt-get install bsdmainutils dwarfdump libc6-dev:i386 lib32gcc-10-dev libstdc++-10-dev-arm64-cross gcc-10-aarch64-linux-gnu g++-10-aarch64-linux-gnu
```
If you are not using a recent enough Linux distribution, or if `cmake` does
not work for you for any reason, you can use Docker to build mold in a Docker
environment. To do so, run `./dist.sh` in this directory instead of using
`cmake`. The shell script will pull a Docker image, build mold and auxiliary
files inside it, and package them into a single tar file named
`mold-$version-$arch-linux.tar.gz`. You can extract the tar file anywhere and
use the mold executable within it.
## How to use
<details><summary>A classic way to use mold</summary>
On Unix, the linker command (which is usually `/usr/bin/ld`) is
invoked indirectly by the compiler driver (which is usually `cc`,
`gcc` or `clang`), which is typically in turn indirectly invoked by
`make` or some other build system command.
On Unix, the linker command (usually `/usr/bin/ld`) is indirectly invoked by
the compiler driver (typically `cc`, `gcc`, or `clang`), which is in turn
indirectly invoked by `make` or another build system command.
If you can specify an additional command line option to your compiler
driver by modifying build system's config files, add one of the
following flags to use `mold` instead of `/usr/bin/ld`:
If you can specify an additional command line option for your compiler driver
by modifying the build system's config files, add one of the following flags
to use mold instead of `/usr/bin/ld`:
- Clang: pass `-fuse-ld=mold`
- For Clang: pass `-fuse-ld=mold`
- GCC 12.1.0 (upcoming version) or later: pass `-fuse-ld=mold`
- For GCC 12.1.0 or later: pass `-fuse-ld=mold`
- GCC before 12.1.0: `-fuse-ld` does not accept `mold` as a valid
argument, so you need to use `-B` option instead. `-B` is an option
to tell GCC where to look for external commands such as `ld`.
- For GCC before 12.1.0: the `-fuse-ld` option does not accept `mold` as a
valid argument, so you need to use the `-B` option instead. The `-B` option
tells GCC where to look for external commands like `ld`.
If you have installed mold with `make install`, there should be a
directory named `/usr/libexec/mold` (or `/usr/local/libexec/mold`,
depending on your $PREFIX), and `ld` command should be there. The
`ld` is actually a symlink to `mold`. So, all you need is to pass
`-B/usr/libexec/mold` (or `-B/usr/local/libexec/mold`) to GCC.
If you have installed mold with `make install`, there should be a directory
named `/usr/libexec/mold` (or `/usr/local/libexec/mold`, depending on your
`$PREFIX`), and the `ld` command should be there. The `ld` is actually a
symlink to `mold`. So, all you need is to pass `-B/usr/libexec/mold` (or
`-B/usr/local/libexec/mold`) to GCC.
If you haven't installed `mold` to any `$PATH`, you can still pass
`-fuse-ld=/absolute/path/to/mold` to clang to use mold. GCC does not
take an absolute path as an argument for `-fuse-ld` though.
If you haven't installed `ld.mold` to any `$PATH`, you can still pass
`-fuse-ld=/absolute/path/to/mold` to clang to use mold. However, GCC does not
accept an absolute path as an argument for `-fuse-ld`.
</details>
@@ -137,83 +130,138 @@ take an absolute path as an argument for `-fuse-ld` though.
Create `.cargo/config.toml` in your project directory with the following:
```
```toml
[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=/path/to/mold"]
```
where `/path/to/mold` is an absolute path to `mold` exectuable.
Please make sure you have installed `clang`.
where `/path/to/mold` is an absolute path to the mold executable. In the
example above, we use `clang` as a linker driver since it always accepts the
`-fuse-ld` option. If your GCC is recent enough to recognize the option, you
may be able to remove the `linker = "clang"` line.
If you want to use mold for all projects, put the above snippet to
```toml
[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "link-arg=-fuse-ld=/path/to/mold"]
```
If you want to use mold for all projects, add the above snippet to
`~/.cargo/config.toml`.
</details>
<details><summary>If you are using Nim</summary>
Create `config.nims` in your project directory with the following:
```nim
when findExe("mold").len > 0 and defined(linux):
switch("passL", "-fuse-ld=mold")
```
where `mold` must be included in the `PATH` environment variable. In this
example, `gcc` is used as the linker driver. Use the `-fuse-ld` option if your
GCC is recent enough to recognize this option.
If you want to use mold for all projects, add the above snippet to
`~/.config/config.nims`.
</details>
<details><summary>mold -run</summary>
It is sometimes very hard to pass an appropriate command line option
to `cc` to specify an alternative linker. To deal with the situation,
mold has a feature to intercept all invocations of `ld`, `ld.lld` or
`ld.gold` and redirect it to itself. To use the feature, run `make`
(or another build command) as a subcommand of mold as follows:
It is sometimes very hard to pass an appropriate command line option to `cc`
to specify an alternative linker. To address this situation, mold has a
feature to intercept all invocations of `ld`, `ld.lld`, or `ld.gold` and
redirect them to itself. To use this feature, run `make` (or another build
command) as a subcommand of mold as follows:
```shell
mold -run make <make-options-if-any>
```
Internally, mold invokes a given command with `LD_PRELOAD` environment
variable set to its companion shared object file. The shared object
file intercepts all function calls to `exec(3)`-family functions to
replace `argv[0]` with `mold` if it is `ld`, `ld.gold` or `ld.lld`.
Internally, mold invokes the given command with the `LD_PRELOAD` environment
variable set to its companion shared object file. The shared object file
intercepts all function calls to `exec(3)`-family functions to replace
`argv[0]` with `mold` if it is `ld`, `ld.gold`, or `ld.lld`.
</details>
<details><summary>GitHub Action</summary>
If you want to use mold in your GitHub-hosted CI to speed up continuous
build, you can use <a href=https://github.com/rui314/setup-mold>setup-mold</a>
GitHub Action. GitHub runs a CI on a two-core machine, but mold is
still significantly faster than the default GNU linker there
especially when a program being linked is large.
<details><summary>GitHub Actions</summary>
You can use our [setup-mold](https://github.com/rui314/setup-mold) GitHub
Action to speed up GitHub-hosted continuous builds. Although GitHub Actions
run on a two-core machine, mold is still significantly faster than the default
GNU linker, especially when linking large programs.
</details>
<details><summary>Verify that you are using mold</summary>
mold leaves its identification string in `.comment` section in an output
mold leaves its identification string in the `.comment` section of an output
file. You can print it out to verify that you are actually using mold.
```shell
readelf -p .comment <executable-file>
$ readelf -p .comment <executable-file>
String dump of section '.comment':
[ 0] GCC: (Ubuntu 10.2.0-5ubuntu1~20.04) 10.2.0
[ 2b] mold 9a1679b47d9b22012ec7dfbda97c8983956716f7
```
If `mold` is in `.comment`, the file is created by mold.
If `mold` is present in the `.comment` section, the file was created by mold.
</details>
<details><summary>Online manual</summary>
Since mold is a drop-in replacement, you should be able to use it without
reading its manual. However, if you need it, [mold's man page](docs/mold.md)
is available. You can read the same manual by running `man mold`.
</details>
## Why is mold so fast?
One reason is because it simply uses faster algorithms and efficient
data structures than other linkers do. The other reason is that the
new linker is highly parallelized.
One reason is that it utilizes faster algorithms and more efficient data
structures compared to other linkers. Another reason is that mold is highly
parallelized.
Here is a side-by-side comparison of per-core CPU usage of lld (left)
and mold (right). They are linking the same program, Chromium
executable.
Here is a side-by-side comparison of per-core CPU usage for lld (left) and
mold (right), linking the same program, a Chromium executable.
![CPU usage comparison in htop animation](docs/htop.gif)
As you can see, mold uses all available cores throughout its execution
and finishes quickly. On the other hand, lld failed to use available
cores most of the time. In this demo, the maximum parallelism is
artificially capped to 16 so that the bars fit in the GIF.
As you can see, mold uses all available cores throughout its execution and
finishes quickly. In contrast, lld fails to utilize available cores most of
the time. In this demo, the maximum parallelism is artificially capped at 16,
so that the bars fit in the GIF.
For details, please read [design notes](docs/design.md).
For details, please see the [design notes](docs/design.md).
# Logo
## Sponsors
-![mold image](docs/mold.jpg)
We accept donations via [GitHub Sponsors](https://github.com/sponsors/rui314)
and [OpenCollective](https://opencollective.com/mold-linker). We thank
everyone who sponsors our project. In particular, we'd like to acknowledge the
following people and organizations who have sponsored $128/month or more:
### Corporate sponsors
<a href="https://mercury.com"><img src="docs/mercury-logo.png" align=center height=120 width=400 alt=Mercury></a>
<a href="https://cybozu-global.com"><img src="docs/cyboze-logo.png" align=center height=120 width=133 alt=Cybozu></a>
<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)
### 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)
-81
View File
@@ -1,81 +0,0 @@
#pragma once
#include <cstdint>
#include <cstring>
#ifdef __BIG_ENDIAN__
#error "mold does not support big-endian hosts"
#endif
namespace mold {
template <typename T>
class BigEndian {
public:
BigEndian() = delete;
operator T() const {
T x;
memcpy(&x, val, sizeof(T));
return bswap(x);
}
BigEndian &operator=(T x) {
x = bswap(x);
memcpy(&val, &x, sizeof(T));
return *this;
}
BigEndian &operator++() {
return *this = *this + 1;
}
BigEndian operator++(int) {
T ret = *this;
*this = *this + 1;
return ret;
}
BigEndian &operator--() {
return *this = *this - 1;
}
BigEndian operator--(int) {
T ret = *this;
*this = *this - 1;
return ret;
}
BigEndian &operator+=(T x) {
return *this = *this + x;
}
BigEndian &operator&=(T x) {
return *this = *this & x;
}
BigEndian &operator|=(T x) {
return *this = *this | x;
}
private:
uint8_t val[sizeof(T)];
static T bswap(T x) {
if constexpr (sizeof(T) == 2)
return __builtin_bswap16(x);
else if constexpr (sizeof(T) == 4)
return __builtin_bswap32(x);
else
return __builtin_bswap64(x);
}
};
using ibig16 = BigEndian<int16_t>;
using ibig32 = BigEndian<int32_t>;
using ibig64 = BigEndian<int64_t>;
using ubig16 = BigEndian<uint16_t>;
using ubig32 = BigEndian<uint32_t>;
using ubig64 = BigEndian<uint64_t>;
} // namespace mold
-63
View File
@@ -1,63 +0,0 @@
#pragma once
#include "mold.h"
namespace mold {
template <typename C>
std::vector<std::string_view>
read_response_file(C &ctx, std::string_view path) {
std::vector<std::string_view> vec;
MappedFile<C> *mf = MappedFile<C>::must_open(ctx, std::string(path));
u8 *data = mf->data;
auto read_quoted = [&](i64 i, char quote) {
std::string buf;
while (i < mf->size && data[i] != quote) {
if (data[i] == '\\') {
buf.append(1, data[i + 1]);
i += 2;
} else {
buf.append(1, data[i++]);
}
}
if (i >= mf->size)
Fatal(ctx) << path << ": premature end of input";
vec.push_back(save_string(ctx, buf));
return i + 1;
};
auto read_unquoted = [&](i64 i) {
std::string buf;
while (i < mf->size && !isspace(data[i]))
buf.append(1, data[i++]);
vec.push_back(save_string(ctx, buf));
return i;
};
for (i64 i = 0; i < mf->size;) {
if (isspace(data[i]))
i++;
else if (data[i] == '\'')
i = read_quoted(i + 1, '\'');
else if (data[i] == '\"')
i = read_quoted(i + 1, '\"');
else
i = read_unquoted(i);
}
return vec;
}
template <typename C>
std::vector<std::string_view> expand_response_files(C &ctx, char **argv) {
std::vector<std::string_view> vec;
for (i64 i = 0; argv[i]; i++) {
if (argv[i][0] == '@')
append(vec, read_response_file(ctx, argv[i] + 1));
else
vec.push_back(argv[i]);
}
return vec;
}
} // namespace mold
+17
View File
@@ -0,0 +1,17 @@
# This dockerfile creates a reproducible build environment for mold.
#
# $ docker buildx create --use
# $ docker buildx build --platform x86_64,aarch64,arm,ppc64le,s390x -t rui314/mold-builder:latest --push .
FROM ubuntu:18.04
ENV TZ=Europe/London
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 && \
apt-get install -y --no-install-recommends build-essential wget libstdc++-11-dev zlib1g-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
+38 -14
View File
@@ -1,6 +1,31 @@
// This file contains functions to read an archive file (.a file).
// An archive file is just a bundle of object files. It's similar to
// tar or zip, but the contents are not compressed.
//
// An archive file is either "regular" or "thin". A regular archive
// contains object files directly, while a thin archive contains only
// pathnames. In the latter case, actual file contents have to be read
// from given pathnames. A regular archive is sometimes called "fat"
// archive as opposed to "thin".
//
// If an archive file is given to the linker, the linker pulls out
// object files that are needed to resolve undefined symbols. So,
// bunding object files as an archive and giving that archive to the
// linker has a different meaning than directly giving the same set of
// object files to the linker. The former links only needed object
// files, while the latter links all the given object files.
//
// Therefore, if you link libc.a for example, not all the libc
// functions are linked to your binary. Instead, only object files
// that provides functions and variables used in your program get
// linked. To make this efficient, static library functions are
// usually separated to each object file in an archive file. You can
// see the contents of libc.a by running `ar t
// /usr/lib/x86_64-linux-gnu/libc.a`.
#pragma once
#include "mold.h"
#include "common.h"
#include "filetype.h"
namespace mold {
@@ -51,12 +76,12 @@ struct ArHdr {
}
};
template <typename C>
std::vector<MappedFile<C> *>
read_thin_archive_members(C &ctx, MappedFile<C> *mf) {
template <typename Context, typename MappedFile>
std::vector<MappedFile *>
read_thin_archive_members(Context &ctx, MappedFile *mf) {
u8 *begin = mf->data;
u8 *data = begin + 8;
std::vector<MappedFile<C> *> vec;
std::vector<MappedFile *> vec;
std::string_view strtab;
while (data < begin + mf->size) {
@@ -92,18 +117,18 @@ read_thin_archive_members(C &ctx, MappedFile<C> *mf) {
std::string path = name.starts_with('/') ?
name : (filepath(mf->name).parent_path() / name).string();
vec.push_back(MappedFile<C>::must_open(ctx, path));
vec.push_back(MappedFile::must_open(ctx, path));
vec.back()->thin_parent = mf;
data = body;
}
return vec;
}
template <typename C>
std::vector<MappedFile<C> *>
read_fat_archive_members(C &ctx, MappedFile<C> *mf) {
template <typename Context, typename MappedFile>
std::vector<MappedFile *> read_fat_archive_members(Context &ctx, MappedFile *mf) {
u8 *begin = mf->data;
u8 *data = begin + 8;
std::vector<MappedFile<C> *> vec;
std::vector<MappedFile *> vec;
std::string_view strtab;
while (begin + mf->size - data >= 2) {
@@ -137,10 +162,9 @@ read_fat_archive_members(C &ctx, MappedFile<C> *mf) {
return vec;
}
template <typename C>
std::vector<MappedFile<C> *>
read_archive_members(C &ctx, MappedFile<C> *mf) {
switch (get_file_type(mf)) {
template <typename Context, typename MappedFile>
std::vector<MappedFile *> read_archive_members(Context &ctx, MappedFile *mf) {
switch (get_file_type(ctx, mf)) {
case FileType::AR:
return read_fat_archive_members(ctx, mf);
case FileType::THIN_AR:
+101
View File
@@ -0,0 +1,101 @@
#pragma once
#include "common.h"
namespace mold {
template <typename Context>
std::vector<std::string_view>
read_response_file(Context &ctx, std::string_view path, i64 depth) {
if (depth > 10)
Fatal(ctx) << path << ": response file nesting too deep";
std::vector<std::string_view> vec;
MappedFile<Context> *mf = MappedFile<Context>::must_open(ctx, std::string(path));
std::string_view data((char *)mf->data, mf->size);
while (!data.empty()) {
if (isspace(data[0])) {
data = data.substr(1);
continue;
}
auto read_quoted = [&]() {
char quote = data[0];
data = data.substr(1);
std::string buf;
while (!data.empty() && data[0] != quote) {
if (data[0] == '\\' && data.size() >= 1) {
buf.append(1, data[1]);
data = data.substr(2);
} else {
buf.append(1, data[0]);
data = data.substr(1);
}
}
if (data.empty())
Fatal(ctx) << path << ": premature end of input";
data = data.substr(1);
return save_string(ctx, buf);
};
auto read_unquoted = [&] {
std::string buf;
while (!data.empty()) {
if (data[0] == '\\' && data.size() >= 1) {
buf.append(1, data[1]);
data = data.substr(2);
continue;
}
if (!isspace(data[0])) {
buf.append(1, data[0]);
data = data.substr(1);
continue;
}
break;
}
return save_string(ctx, buf);
};
std::string_view tok;
if (data[0] == '\'' || data[0] == '\"')
tok = read_quoted();
else
tok = read_unquoted();
if (tok.starts_with('@'))
append(vec, read_response_file(ctx, tok.substr(1), depth + 1));
else
vec.push_back(tok);
}
return vec;
}
// Replace "@path/to/some/text/file" with its file contents.
template <typename Context>
std::vector<std::string_view> expand_response_files(Context &ctx, char **argv) {
std::vector<std::string_view> vec;
for (i64 i = 0; argv[i]; i++) {
if (argv[i][0] == '@')
append(vec, read_response_file(ctx, argv[i] + 1, 1));
else
vec.push_back(argv[i]);
}
return vec;
}
static inline std::string_view string_trim(std::string_view str) {
size_t pos = str.find_first_not_of(" \t");
if (pos == str.npos)
return "";
str = str.substr(pos);
pos = str.find_last_not_of(" \t");
if (pos == str.npos)
return str;
return str.substr(0, pos + 1);
}
} // namespace mold
+1034
View File
File diff suppressed because it is too large. Load diff
+46 -53
View File
@@ -1,23 +1,30 @@
// This file implements a multi-threaded zlib compression routine.
// This file implements a multi-threaded zlib and zstd compression
// routine.
//
// Multiple pieces of raw compressed data in zlib-format can be merged
// just by concatenation as long as each zlib stream is flushed with
// Z_SYNC_FLUSH. In this file, we split input data into multiple
// shards, compress them individually and concatenate them. We then
// append a header, a trailer and a checksum so that the concatenated
// data is valid zlib-format data.
// zlib-compressed data can be merged just by concatenation as long as
// each piece of data is flushed with Z_SYNC_FLUSH. In this file, we
// split input data into multiple shards, compress them individually
// and concatenate them. We then append a header, a trailer and a
// checksum so that the concatenated data is valid zlib-format data.
//
// zstd-compressed data can be merged in the same way.
//
// Using threads to compress data has a downside. Since the dictionary
// is reset on boundaries of shards, compression ratio is sacrificed
// a little bit. However, if a shard size is large enough, that loss
// is negligible in practice.
#include "mold.h"
#include "common.h"
#include <tbb/parallel_for_each.h>
#include <zlib.h>
#include <zstd.h>
#define CHECK(fn) do { int r = (fn); assert(r == Z_OK); } while (0)
#define CHECK(fn) \
do { \
[[maybe_unused]] int r = (fn); \
assert(r == Z_OK); \
} while (0)
namespace mold {
@@ -35,7 +42,7 @@ static std::vector<std::string_view> split(std::string_view input) {
return shards;
}
static std::vector<u8> do_compress(std::string_view input) {
static std::vector<u8> zlib_compress(std::string_view input) {
// Initialize zlib stream. Since debug info is generally compressed
// pretty well with lower compression levels, we chose compression
// level 1.
@@ -85,11 +92,13 @@ static std::vector<u8> do_compress(std::string_view input) {
assert(strm.avail_out > 0);
buf.resize(buf.size() - strm.avail_out);
buf.shrink_to_fit();
deflateEnd(&strm);
return buf;
}
ZlibCompressor::ZlibCompressor(std::string_view input) {
ZlibCompressor::ZlibCompressor(u8 *buf, i64 size) {
std::string_view input{(char *)buf, (size_t)size};
std::vector<std::string_view> inputs = split(input);
std::vector<u64> adlers(inputs.size());
shards.resize(inputs.size());
@@ -97,20 +106,18 @@ ZlibCompressor::ZlibCompressor(std::string_view input) {
// Compress each shard
tbb::parallel_for((i64)0, (i64)inputs.size(), [&](i64 i) {
adlers[i] = adler32(1, (u8 *)inputs[i].data(), inputs[i].size());
shards[i] = do_compress(inputs[i]);
shards[i] = zlib_compress(inputs[i]);
});
// Combine checksums
checksum = adlers[0];
for (i64 i = 1; i < inputs.size(); i++)
checksum = adler32_combine(checksum, adlers[i], inputs[i].size());
}
i64 ZlibCompressor::size() const {
i64 size = 2; // +2 for header
for (const std::vector<u8> &shard : shards)
size += shard.size();
return size + 6; // +6 for trailer and checksum
// Comput the total size
compressed_size = 8; // the header and the trailer
for (std::vector<u8> &shard : shards)
compressed_size += shard.size();
}
void ZlibCompressor::write_to(u8 *buf) {
@@ -129,64 +136,50 @@ void ZlibCompressor::write_to(u8 *buf) {
});
// Write a trailer
u8 *end = buf + size();
u8 *end = buf + compressed_size;
end[-6] = 3;
end[-5] = 0;
// Write a checksum
*(ubig32 *)(end - 4) = checksum;
*(ub32 *)(end - 4) = checksum;
}
GzipCompressor::GzipCompressor(std::string_view input) {
static std::vector<u8> zstd_compress(std::string_view input) {
std::vector<u8> buf(ZSTD_COMPRESSBOUND(input.size()));
constexpr int level = 3; // compression level; must be between 1 to 22
size_t sz = ZSTD_compress(buf.data(), buf.size(), input.data(), input.size(),
level);
assert(!ZSTD_isError(sz));
buf.resize(sz);
buf.shrink_to_fit();
return buf;
}
ZstdCompressor::ZstdCompressor(u8 *buf, i64 size) {
std::string_view input{(char *)buf, (size_t)size};
std::vector<std::string_view> inputs = split(input);
std::vector<u32> crc(inputs.size());
shards.resize(inputs.size());
// Compress each shard
tbb::parallel_for((i64)0, (i64)inputs.size(), [&](i64 i) {
crc[i] = crc32(0, (u8 *)inputs[i].data(), inputs[i].size());
shards[i] = do_compress(inputs[i]);
shards[i] = zstd_compress(inputs[i]);
});
// Combine checksums
checksum = crc[0];
for (i64 i = 1; i < inputs.size(); i++)
checksum = crc32_combine(checksum, crc[i], inputs[i].size());
uncompressed_size = input.size();
compressed_size = 0;
for (std::vector<u8> &shard : shards)
compressed_size += shard.size();
}
i64 GzipCompressor::size() const {
i64 size = 10; // +10 for header
for (const std::vector<u8> &shard : shards)
size += shard.size();
return size + 10; // +10 for trailer and checksum
}
void GzipCompressor::write_to(u8 *buf) {
// Write a zlib-format header
memset(buf, 0, 10);
buf[0] = 0x1f; // magic
buf[1] = 0x8b; // magic
buf[2] = 0x08; // compression method is zlib
buf[9] = 0xff; // made on unknown OS
void ZstdCompressor::write_to(u8 *buf) {
// Copy compressed data
std::vector<i64> offsets(shards.size());
offsets[0] = 10; // +10 for header
for (i64 i = 1; i < shards.size(); i++)
offsets[i] = offsets[i - 1] + shards[i - 1].size();
tbb::parallel_for((i64)0, (i64)shards.size(), [&](i64 i) {
memcpy(&buf[offsets[i]], shards[i].data(), shards[i].size());
});
// Write a trailer
u8 *end = buf + size();
end[-10] = 0x3; // two-byte zlib stream terminator
end[-9] = 0;
*(u32 *)(end - 8) = checksum;
*(u32 *)(end - 4) = uncompressed_size;
}
} // namespace mold
+3
View File
@@ -0,0 +1,3 @@
#define MOLD_VERSION "@mold_VERSION@"
#define MOLD_LIBDIR "@CMAKE_INSTALL_FULL_LIBDIR@"
#cmakedefine01 MOLD_IS_SOLD
+51
View File
@@ -0,0 +1,51 @@
#include "common.h"
#include <cstdlib>
#ifndef _WIN32
#include <cxxabi.h>
#endif
#include "../third-party/rust-demangle/rust-demangle.h"
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) {
static thread_local char *buf;
static thread_local size_t buflen;
// TODO(cwasser): Actually demangle Symbols on Windows using e.g.
// `UnDecorateSymbolName` from Dbghelp, maybe even Itanium symbols?
#ifndef _WIN32
if (name.starts_with("_Z")) {
int status;
char *p = abi::__cxa_demangle(std::string(name).c_str(), buf, &buflen, &status);
if (status == 0) {
buf = p;
return p;
}
}
#endif
return {};
}
} // namespace mold
+7 -5
View File
@@ -1,4 +1,4 @@
#include "mold.h"
#include "common.h"
#include <filesystem>
#include <sys/stat.h>
@@ -7,20 +7,22 @@ namespace mold {
std::string get_realpath(std::string_view path) {
std::error_code ec;
std::string ret = std::filesystem::canonical(path, ec);
return ec ? std::string(path) : ret;
std::filesystem::path link = std::filesystem::read_symlink(path, ec);
if (ec)
return std::string(path);
return (filepath(path) / ".." / link).lexically_normal().string();
}
// Removes redundant '/..' or '/.' from a given path.
// The transformation is done purely by lexical processing.
// This function does not access file system.
std::string path_clean(std::string_view path) {
return filepath(path).lexically_normal();
return filepath(path).lexically_normal().string();
}
std::filesystem::path to_abs_path(std::filesystem::path path) {
if (path.is_absolute())
return path;
return path.lexically_normal();
return (std::filesystem::current_path() / path).lexically_normal();
}
+71 -26
View File
@@ -1,7 +1,7 @@
#pragma once
#include "mold.h"
#include "elf/elf.h"
#include "common.h"
#include "../elf/elf.h"
namespace mold {
@@ -11,7 +11,9 @@ enum class FileType {
ELF_OBJ,
ELF_DSO,
MACH_OBJ,
MACH_EXE,
MACH_DYLIB,
MACH_BUNDLE,
MACH_UNIVERSAL,
AR,
THIN_AR,
@@ -21,34 +23,52 @@ enum class FileType {
LLVM_BITCODE,
};
template <typename C>
bool is_text_file(MappedFile<C> *mf) {
template <typename MappedFile>
bool is_text_file(MappedFile *mf) {
u8 *data = mf->data;
return mf->size >= 4 && isprint(data[0]) && isprint(data[1]) &&
isprint(data[2]) && isprint(data[3]);
}
template <typename E, typename C>
inline bool is_gcc_lto_obj(MappedFile<C> *mf) {
template <typename E, typename Context, typename MappedFile>
inline bool is_gcc_lto_obj(Context &ctx, MappedFile *mf) {
using namespace mold::elf;
const char *data = mf->get_contents().data();
ElfEhdr<E> &ehdr = *(ElfEhdr<E> *)data;
ElfShdr<E> *sh_begin = (ElfShdr<E> *)(data + ehdr.e_shoff);
std::span<ElfShdr<E>> shdrs{(ElfShdr<E> *)(data + ehdr.e_shoff), ehdr.e_shnum};
// e_shstrndx is a 16-bit field. If .shstrtab's section index is
// too large, the actual number is stored to sh_link field.
i64 shstrtab_idx = (ehdr.e_shstrndx == SHN_XINDEX)
? sh_begin->sh_link : ehdr.e_shstrndx;
for (ElfShdr<E> &sec : shdrs) {
// GCC FAT LTO objects contain both regular ELF sections and GCC-
// specific LTO sections, so that they can be linked as LTO objects if
// the LTO linker plugin is available and falls back as regular
// objects otherwise. GCC FAT LTO object can be identified by the
// presence of `.gcc.lto_.symtab` section.
if (!ctx.arg.plugin.empty()) {
std::string_view name = data + shdrs[shstrtab_idx].sh_offset + sec.sh_name;
if (name.starts_with(".gnu.lto_.symtab."))
return true;
}
if (sec.sh_type != SHT_SYMTAB)
continue;
// GCC non-FAT LTO object contains only sections symbols followed by
// a common symbol whose name is `__gnu_lto_slim` (or `__gnu_lto_v1`
// for older GCC releases).
std::span<ElfSym<E>> elf_syms{(ElfSym<E> *)(data + sec.sh_offset),
sec.sh_size / sizeof(ElfSym<E>)};
(size_t)sec.sh_size / sizeof(ElfSym<E>)};
auto skip = [](u8 type) {
return type == STT_NOTYPE || type == STT_FILE || type == STT_SECTION;
};
// GCC LTO object contains only sections symbols followed by a common
// symbol whose name is `__gnu_lto_v1` or `__gnu_lto_slim`.
i64 i = 1;
while (i < elf_syms.size() && skip(elf_syms[i].st_type))
i++;
@@ -65,41 +85,64 @@ inline bool is_gcc_lto_obj(MappedFile<C> *mf) {
return false;
}
template <typename C>
FileType get_file_type(MappedFile<C> *mf) {
template <typename Context, typename MappedFile>
FileType get_file_type(Context &ctx, MappedFile *mf) {
using namespace elf;
std::string_view data = mf->get_contents();
if (data.empty())
return FileType::EMPTY;
if (data.starts_with("\177ELF")) {
switch (*(u16 *)(data.data() + 16)) {
case 1: {
// ET_REL
elf::Elf32Ehdr &ehdr = *(elf::Elf32Ehdr *)data.data();
u8 byte_order = ((ElfEhdr<I386> *)data.data())->e_ident[EI_DATA];
if (ehdr.e_ident[elf::EI_CLASS] == elf::ELFCLASS32) {
if (is_gcc_lto_obj<elf::I386>(mf))
return FileType::GCC_LTO_OBJ;
} else {
if (is_gcc_lto_obj<elf::X86_64>(mf))
return FileType::GCC_LTO_OBJ;
if (byte_order == ELFDATA2LSB) {
auto &ehdr = *(ElfEhdr<I386> *)data.data();
if (ehdr.e_type == ET_REL) {
if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) {
if (is_gcc_lto_obj<I386>(ctx, mf))
return FileType::GCC_LTO_OBJ;
} else {
if (is_gcc_lto_obj<X86_64>(ctx, mf))
return FileType::GCC_LTO_OBJ;
}
return FileType::ELF_OBJ;
}
return FileType::ELF_OBJ;
}
case 3: // ET_DYN
return FileType::ELF_DSO;
if (ehdr.e_type == ET_DYN)
return FileType::ELF_DSO;
} else {
auto &ehdr = *(ElfEhdr<M68K> *)data.data();
if (ehdr.e_type == ET_REL) {
if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) {
if (is_gcc_lto_obj<M68K>(ctx, mf))
return FileType::GCC_LTO_OBJ;
} else {
if (is_gcc_lto_obj<SPARC64>(ctx, mf))
return FileType::GCC_LTO_OBJ;
}
return FileType::ELF_OBJ;
}
if (ehdr.e_type == ET_DYN)
return FileType::ELF_DSO;
}
return FileType::UNKNOWN;
}
if (data.starts_with("\xcf\xfa\xed\xfe")) {
switch (*(u32 *)(data.data() + 12)) {
switch (*(ul32 *)(data.data() + 12)) {
case 1: // MH_OBJECT
return FileType::MACH_OBJ;
case 2: // MH_EXECUTE
return FileType::MACH_EXE;
case 6: // MH_DYLIB
return FileType::MACH_DYLIB;
case 8: // MH_BUNDLE
return FileType::MACH_BUNDLE;
}
return FileType::UNKNOWN;
}
@@ -127,8 +170,10 @@ inline std::string filetype_to_string(FileType type) {
case FileType::EMPTY: return "EMPTY";
case FileType::ELF_OBJ: return "ELF_OBJ";
case FileType::ELF_DSO: return "ELF_DSO";
case FileType::MACH_EXE: return "MACH_EXE";
case FileType::MACH_OBJ: return "MACH_OBJ";
case FileType::MACH_DYLIB: return "MACH_DYLIB";
case FileType::MACH_BUNDLE: return "MACH_BUNDLE";
case FileType::MACH_UNIVERSAL: return "MACH_UNIVERSAL";
case FileType::AR: return "AR";
case FileType::THIN_AR: return "THIN_AR";
+12 -14
View File
@@ -1,10 +1,10 @@
#include "mold.h"
#include "common.h"
#include <cstring>
namespace mold::elf {
namespace mold {
std::optional<GlobPattern> GlobPattern::compile(std::string_view pat) {
std::optional<Glob> Glob::compile(std::string_view pat) {
std::vector<Element> vec;
while (!pat.empty()) {
@@ -20,8 +20,7 @@ std::optional<GlobPattern> GlobPattern::compile(std::string_view pat) {
// [a-czg-i]: a, b, c, z, g, h, or i
// [^a-z]: Any character except lowercase letters
vec.push_back({BRACKET});
std::vector<bool> &bitmap = vec.back().bitmap;
bitmap.resize(256);
std::bitset<256> &bitset = vec.back().bitset;
bool negate = false;
if (!pat.empty() && pat[0] == '^') {
@@ -60,9 +59,9 @@ std::optional<GlobPattern> GlobPattern::compile(std::string_view pat) {
return {};
for (i64 i = start; i <= end; i++)
bitmap[i] = true;
bitset[i] = true;
} else {
bitmap[(u8)pat[0]] = true;
bitset[(u8)pat[0]] = true;
pat = pat.substr(1);
}
}
@@ -71,8 +70,7 @@ std::optional<GlobPattern> GlobPattern::compile(std::string_view pat) {
return {};
if (negate)
for (i64 i = 0; i < 256; i++)
bitmap[i] = !bitmap[i];
bitset.flip();
break;
}
case '?':
@@ -89,14 +87,14 @@ std::optional<GlobPattern> GlobPattern::compile(std::string_view pat) {
}
}
return {GlobPattern{std::move(vec)}};
return {Glob{std::move(vec)}};
}
bool GlobPattern::match(std::string_view str) {
bool Glob::match(std::string_view str) {
return do_match(str, elements);
}
bool GlobPattern::do_match(std::string_view str, std::span<Element> elements) {
bool Glob::do_match(std::string_view str, std::span<Element> elements) {
while (!elements.empty()) {
Element &e = elements[0];
elements = elements.subspan(1);
@@ -138,7 +136,7 @@ bool GlobPattern::do_match(std::string_view str, std::span<Element> elements) {
str = str.substr(1);
break;
case BRACKET:
if (str.empty() || !e.bitmap[str[0]])
if (str.empty() || !e.bitset[str[0]])
return false;
str = str.substr(1);
break;
@@ -148,4 +146,4 @@ bool GlobPattern::do_match(std::string_view str, std::span<Element> elements) {
return str.empty();
}
} // namespace mold::elf
} // namespace mold
+2 -2
View File
@@ -4,7 +4,7 @@
// For more info, read
// https://engineering.fb.com/2018/12/13/data-infrastructure/hyperloglog
#include "mold.h"
#include "common.h"
#include <cmath>
@@ -13,7 +13,7 @@ namespace mold {
i64 HyperLogLog::get_cardinality() const {
double z = 0;
for (i64 val : buckets)
z += pow(2, -val);
z += std::ldexp(1.0, -val);
return ALPHA * NBUCKETS * NBUCKETS / z;
}
+221
View File
@@ -0,0 +1,221 @@
// This file defines integral types for file input/output. We need to use
// these types instead of the plain integers (such as uint32_t or int32_t)
// when reading from/writing to an mmap'ed file area for the following
// reasons:
//
// 1. mold is always a cross linker and should not depend on what host it
// is running on. Users should be able to run mold on a big-endian
// SPARC machine to create a little-endian RV64 binary, for example.
//
// 2. Even though data members in all ELF data strucutres are naturally
// aligned, they are not guaranteed to be aligned on memory. Because
// archive file (.a file) aligns each member only to a 2 byte boundary,
// anything larger than 2 bytes may be unaligned in an mmap'ed memory.
// Unaligned access is an undefined behavior in C/C++, so we shouldn't
// cast an arbitrary pointer to a uint32_t, for example, to read a
// 32-bits value.
//
// The data types defined in this file don't depend on host byte order and
// don't do unaligned access.
#pragma once
#include <bit>
#include <cstdint>
#include <cstring>
#if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
# define __LITTLE_ENDIAN__ 1
# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
# define __BIG_ENDIAN__ 1
# else
# error "unknown host byte order"
# endif
#endif
namespace mold {
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t i8;
typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
template <typename T>
static inline T bswap(T val) {
switch (sizeof(T)) {
case 2: return __builtin_bswap16(val);
case 4: return __builtin_bswap32(val);
case 8: return __builtin_bswap64(val);
default: __builtin_unreachable();
}
}
template <typename T, int SIZE = sizeof(T)>
class LittleEndian {
public:
LittleEndian() = default;
LittleEndian(T x) { *this = x; }
operator T() const {
if constexpr (sizeof(T) == SIZE) {
T x;
memcpy(&x, val, sizeof(T));
if constexpr (std::endian::native == std::endian::big)
x = bswap(x);
return x;
} else {
static_assert(SIZE == 3);
return (val[2] << 16) | (val[1] << 8) | val[0];
}
}
LittleEndian &operator=(T x) {
if constexpr (sizeof(T) == SIZE) {
if constexpr (std::endian::native == std::endian::big)
x = bswap(x);
memcpy(val, &x, sizeof(T));
} else {
static_assert(SIZE == 3);
val[2] = x >> 16;
val[1] = x >> 8;
val[0] = x;
}
return *this;
}
LittleEndian &operator++() {
return *this = *this + 1;
}
LittleEndian operator++(int) {
T ret = *this;
*this = *this + 1;
return ret;
}
LittleEndian &operator--() {
return *this = *this - 1;
}
LittleEndian operator--(int) {
T ret = *this;
*this = *this - 1;
return ret;
}
LittleEndian &operator+=(T x) {
return *this = *this + x;
}
LittleEndian &operator-=(T x) {
return *this = *this - x;
}
LittleEndian &operator&=(T x) {
return *this = *this & x;
}
LittleEndian &operator|=(T x) {
return *this = *this | x;
}
private:
u8 val[SIZE];
};
using il16 = LittleEndian<i16>;
using il32 = LittleEndian<i32>;
using il64 = LittleEndian<i64>;
using ul16 = LittleEndian<u16>;
using ul24 = LittleEndian<u32, 3>;
using ul32 = LittleEndian<u32>;
using ul64 = LittleEndian<u64>;
template <typename T, int SIZE = sizeof(T)>
class BigEndian {
public:
BigEndian() = default;
BigEndian(T x) { *this = x; }
operator T() const {
if constexpr (sizeof(T) == SIZE) {
T x;
memcpy(&x, val, sizeof(T));
if constexpr (std::endian::native == std::endian::little)
x = bswap(x);
return x;
} else {
static_assert(SIZE == 3);
return (val[0] << 16) | (val[1] << 8) | val[2];
}
}
BigEndian &operator=(T x) {
if constexpr (sizeof(T) == SIZE) {
if constexpr (std::endian::native == std::endian::little)
x = bswap(x);
memcpy(val, &x, sizeof(T));
} else {
static_assert(SIZE == 3);
val[0] = x >> 16;
val[1] = x >> 8;
val[2] = x;
}
return *this;
}
BigEndian &operator++() {
return *this = *this + 1;
}
BigEndian operator++(int) {
T ret = *this;
*this = *this + 1;
return ret;
}
BigEndian &operator--() {
return *this = *this - 1;
}
BigEndian operator--(int) {
T ret = *this;
*this = *this - 1;
return ret;
}
BigEndian &operator+=(T x) {
return *this = *this + x;
}
BigEndian &operator-=(T x) {
return *this = *this - x;
}
BigEndian &operator&=(T x) {
return *this = *this & x;
}
BigEndian &operator|=(T x) {
return *this = *this | x;
}
private:
u8 val[SIZE];
};
using ib16 = BigEndian<i16>;
using ib32 = BigEndian<i32>;
using ib64 = BigEndian<i64>;
using ub16 = BigEndian<u16>;
using ub24 = BigEndian<u32, 3>;
using ub32 = BigEndian<u32>;
using ub64 = BigEndian<u64>;
} // namespace mold
+169
View File
@@ -0,0 +1,169 @@
#include "common.h"
#include "config.h"
#include <cstring>
#include <filesystem>
#include <signal.h>
#include <tbb/global_control.h>
#ifdef USE_SYSTEM_MIMALLOC
#include <mimalloc-new-delete.h>
#endif
#ifdef __FreeBSD__
# include <sys/sysctl.h>
# include <unistd.h>
#endif
namespace mold {
std::string mold_version_string = MOLD_VERSION;
namespace elf {
int main(int argc, char **argv);
}
namespace macho {
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)";
}
void cleanup() {
if (output_tmpfile)
unlink(output_tmpfile);
}
std::string errno_string() {
// strerror is not thread-safe, so guard it with a lock.
static std::mutex mu;
std::scoped_lock lock(mu);
return strerror(errno);
}
// Returns the path of the mold executable itself
std::string get_self_path() {
#ifdef __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];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
size_t size;
sysctl(mib, 4, NULL, &size, NULL, 0);
std::string path;
path.resize(size);
sysctl(mib, 4, path.data(), &size, NULL, 0);
return path;
#else
return std::filesystem::read_symlink("/proc/self/exe").string();
#endif
}
// mold mmap's an output file, and the mmap succeeds even if there's
// no enough space left on the filesystem. The actual disk blocks are
// not allocated on the mmap call but when the program writes to it
// for the first time.
//
// If a disk becomes full as a result of a write to an mmap'ed memory
// region, the failure of the write is reported as a SIGBUS or structured
// exeption with code EXCEPTION_IN_PAGE_ERROR on Windows. This
// signal handler catches that signal and prints out a user-friendly
// error message. Without this, it is very hard to realize that the
// disk might be full.
#ifdef _WIN32
static LONG WINAPI vectored_handler(_EXCEPTION_POINTERS *exception_info) {
static std::mutex mu;
std::scoped_lock lock{mu};
PEXCEPTION_RECORD exception_record = exception_info->ExceptionRecord;
ULONG_PTR *exception_information = exception_record->ExceptionInformation;
if (exception_record->ExceptionCode == EXCEPTION_IN_PAGE_ERROR &&
(ULONG_PTR)output_buffer_start <= exception_information[1] &&
exception_information[1] < (ULONG_PTR)output_buffer_end) {
const char msg[] = "mold: failed to write to an output file. Disk full?\n";
(void)!write(_fileno(stderr), msg, sizeof(msg) - 1);
}
cleanup();
_exit(1);
}
void install_signal_handler() {
AddVectoredExceptionHandler(0, vectored_handler);
}
#else
static void sighandler(int signo, siginfo_t *info, void *ucontext) {
static std::mutex mu;
std::scoped_lock lock{mu};
switch (signo) {
case SIGSEGV:
case SIGBUS:
if (output_buffer_start <= info->si_addr &&
info->si_addr < output_buffer_end) {
const char msg[] = "mold: failed to write to an output file. Disk full?\n";
(void)!write(STDERR_FILENO, msg, sizeof(msg) - 1);
}
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);
break;
}
}
_exit(1);
}
void install_signal_handler() {
struct sigaction action;
action.sa_sigaction = sighandler;
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);
}
#endif
i64 get_default_thread_count() {
// mold doesn't scale well above 32 threads.
int n = tbb::global_control::active_value(
tbb::global_control::max_allowed_parallelism);
return std::min(n, 32);
}
} // namespace mold
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);
}
+33 -33
View File
@@ -1,34 +1,34 @@
// This file implements the Aho-Corasick algorithm to match version
// script patterns to symbol strings as quickly as possible.
// This file implements the Aho-Corasick algorithm to match multiple
// glob patterns to symbol strings as quickly as possible.
//
// Here are some examples of version script patterns:
// Here are some examples of glob patterns:
//
// qt_private_api_tag*
// *16QAccessibleCache*
// *32QAbstractFileIconProviderPrivate*
// *17QPixmapIconEngine*
//
// The pattern is a glob pattern, so `*` is a wildcard that matches
// any substring. We sometimes have hundreds of version script
// patterns and have to match them against millions of symbol strings.
// `*` is a wildcard that matches any substring. We sometimes have
// hundreds of glob patterns and have to match them against millions
// of symbol strings.
//
// Aho-Corasick cannot handle complex patterns such as `*foo*bar*`.
// We handle such patterns with GlobPattern. GlobPattern is relatively
// slow, but complex patterns are rare in practice, so it should be OK.
// We handle such patterns with the Glob class. Glob is relatively
// slow, but complex patterns are rare in practice, so it should be
// OK.
#include "mold.h"
#include "common.h"
#include <queue>
#include <regex>
namespace mold::elf {
namespace mold {
std::optional<u16> VersionMatcher::find(std::string_view str) {
u32 idx = UINT32_MAX;
std::optional<u32> MultiGlob::find(std::string_view str) {
std::call_once(once, [&] { compile(); });
u32 val = UINT32_MAX;
if (root) {
std::call_once(once_flag, [&] { compile(); });
// Match against simple glob patterns
TrieNode *node = root.get();
@@ -36,7 +36,7 @@ std::optional<u16> VersionMatcher::find(std::string_view str) {
for (;;) {
if (node->children[c]) {
node = node->children[c].get();
idx = std::min(idx, node->value);
val = std::min(val, node->value);
return;
}
@@ -53,13 +53,13 @@ std::optional<u16> VersionMatcher::find(std::string_view str) {
}
// Match against complex glob patterns
for (std::pair<GlobPattern, u32> &glob : globs)
for (std::pair<Glob, u32> &glob : globs)
if (glob.first.match(str))
idx = std::min(idx, glob.second);
val = std::min(val, glob.second);
if (idx == UINT32_MAX)
if (val == UINT32_MAX)
return {};
return versions[idx];
return val;
}
static bool is_simple_pattern(std::string_view pat) {
@@ -82,18 +82,16 @@ static std::string handle_stars(std::string_view pat) {
return "\0"s + str + "\0"s;
}
bool VersionMatcher::add(std::string_view pat, u16 ver) {
assert(!compiled);
bool MultiGlob::add(std::string_view pat, u32 val) {
assert(!is_compiled);
assert(!pat.empty());
u32 idx = strings.size();
strings.push_back(std::string(pat));
versions.push_back(ver);
// Complex glob pattern
if (!is_simple_pattern(pat)) {
if (std::optional<GlobPattern> glob = GlobPattern::compile(pat)) {
globs.push_back({std::move(*glob), idx});
if (std::optional<Glob> glob = Glob::compile(pat)) {
globs.push_back({std::move(*glob), val});
return true;
}
return false;
@@ -110,17 +108,19 @@ bool VersionMatcher::add(std::string_view pat, u16 ver) {
node = node->children[c].get();
}
node->value = std::min(node->value, idx);
node->value = std::min(node->value, val);
return true;
}
void VersionMatcher::compile() {
fix_suffix_links(*root);
fix_values();
compiled = true;
void MultiGlob::compile() {
is_compiled = true;
if (root) {
fix_suffix_links(*root);
fix_values();
}
}
void VersionMatcher::fix_suffix_links(TrieNode &node) {
void MultiGlob::fix_suffix_links(TrieNode &node) {
for (i64 i = 0; i < 256; i++) {
if (!node.children[i])
continue;
@@ -146,7 +146,7 @@ void VersionMatcher::fix_suffix_links(TrieNode &node) {
}
}
void VersionMatcher::fix_values() {
void MultiGlob::fix_values() {
std::queue<TrieNode *> queue;
queue.push(root.get());
@@ -163,4 +163,4 @@ void VersionMatcher::fix_values() {
} while (!queue.empty());
}
} // namespace mold::elf
} // namespace mold
+144
View File
@@ -0,0 +1,144 @@
#include "common.h"
#include <fcntl.h>
#include <filesystem>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
namespace mold {
inline u32 get_umask() {
u32 orig_umask = umask(0);
umask(orig_umask);
return orig_umask;
}
template <typename Context>
static std::pair<i64, char *>
open_or_create_file(Context &ctx, std::string path, i64 filesize, i64 perm) {
std::string tmpl = filepath(path).parent_path() / ".mold-XXXXXX";
char *path2 = (char *)save_string(ctx, tmpl).data();
i64 fd = mkstemp(path2);
if (fd == -1)
Fatal(ctx) << "cannot open " << path2 << ": " << errno_string();
// Reuse an existing file if exists and writable because on Linux,
// writing to an existing file is much faster than creating a fresh
// file and writing to it.
if (ctx.overwrite_output_file && rename(path.c_str(), path2) == 0) {
::close(fd);
fd = ::open(path2, O_RDWR | O_CREAT, perm);
if (fd != -1 && !ftruncate(fd, filesize) && !fchmod(fd, perm & ~get_umask()))
return {fd, path2};
unlink(path2);
fd = ::open(path2, O_RDWR | O_CREAT, perm);
if (fd == -1)
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();
return {fd, path2};
}
template <typename Context>
class MemoryMappedOutputFile : public OutputFile<Context> {
public:
MemoryMappedOutputFile(Context &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<Context>(path, filesize, true) {
std::tie(this->fd, output_tmpfile) =
open_or_create_file(ctx, path, filesize, perm);
this->buf = (u8 *)mmap(nullptr, filesize, PROT_READ | PROT_WRITE,
MAP_SHARED, this->fd, 0);
if (this->buf == MAP_FAILED)
Fatal(ctx) << path << ": mmap failed: " << errno_string();
mold::output_buffer_start = this->buf;
mold::output_buffer_end = this->buf + filesize;
}
~MemoryMappedOutputFile() {
if (fd2 != -1)
::close(fd2);
}
void close(Context &ctx) override {
Timer t(ctx, "close_file");
if (!this->is_unmapped)
munmap(this->buf, this->filesize);
if (this->buf2.empty()) {
::close(this->fd);
} else {
FILE *out = fdopen(this->fd, "w");
fseek(out, 0, SEEK_END);
fwrite(&this->buf2[0], this->buf2.size(), 1, out);
fclose(out);
}
// If an output file already exists, open a file and then remove it.
// This is the fastest way to unlink a file, as it does not make the
// system to immediately release disk blocks occupied by the file.
fd2 = ::open(this->path.c_str(), O_RDONLY);
if (fd2 != -1)
unlink(this->path.c_str());
if (rename(output_tmpfile, this->path.c_str()) == -1)
Fatal(ctx) << this->path << ": rename failed: " << errno_string();
output_tmpfile = nullptr;
}
private:
int fd2 = -1;
};
template <typename Context>
std::unique_ptr<OutputFile<Context>>
OutputFile<Context>::open(Context &ctx, std::string path, i64 filesize, i64 perm) {
Timer t(ctx, "open_file");
if (path.starts_with('/') && !ctx.arg.chroot.empty())
path = ctx.arg.chroot + "/" + path_clean(path);
bool is_special = false;
if (path == "-") {
is_special = true;
} else {
struct stat st;
if (stat(path.c_str(), &st) == 0 && (st.st_mode & S_IFMT) != S_IFREG)
is_special = true;
}
OutputFile<Context> *file;
if (is_special)
file = new MallocOutputFile(ctx, path, filesize, perm);
else
file = new MemoryMappedOutputFile(ctx, path, filesize, perm);
#ifdef MADV_HUGEPAGE
// Enable transparent huge page for an output memory-mapped file.
// On Linux, it has an effect only on tmpfs mounted with `huge=advise`,
// but it can make the linker ~10% faster. You can try it by creating
// a tmpfs with the following commands
//
// $ mkdir tmp
// $ sudo mount -t tmpfs -o size=2G,huge=advise none tmp
//
// and then specifying a path under the directory as an output file.
madvise(file->buf, filesize, MADV_HUGEPAGE);
#endif
if (ctx.arg.filler != -1)
memset(file->buf, ctx.arg.filler, filesize);
return std::unique_ptr<OutputFile>(file);
}
} // namespace mold
+24
View File
@@ -0,0 +1,24 @@
#include "common.h"
#include <fcntl.h>
#include <filesystem>
#include <windows.h>
namespace mold {
template <typename Context>
std::unique_ptr<OutputFile<Context>>
OutputFile<Context>::open(Context &ctx, std::string path, i64 filesize, i64 perm) {
Timer t(ctx, "open_file");
if (path.starts_with('/') && !ctx.arg.chroot.empty())
path = ctx.arg.chroot + "/" + path_clean(path);
OutputFile<Context> *file = new MallocOutputFile(ctx, path, filesize, perm);
if (ctx.arg.filler != -1)
memset(file->buf, ctx.arg.filler, filesize);
return std::unique_ptr<OutputFile<Context>>(file);
}
} // namespace mold
+5
View File
@@ -0,0 +1,5 @@
#if _WIN32
# include "output-file-win32.h"
#else
# include "output-file-unix.h"
#endif
+36 -17
View File
@@ -1,10 +1,13 @@
#include "mold.h"
#include "common.h"
#include <functional>
#include <iomanip>
#include <ios>
#ifndef _WIN32
#include <sys/resource.h>
#include <sys/time.h>
#endif
namespace mold {
@@ -23,24 +26,39 @@ void Counter::print() {
}
static i64 now_nsec() {
#ifdef _WIN32
return (i64)std::chrono::steady_clock::now().time_since_epoch().count();
#else
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
return (i64)t.tv_sec * 1000000000 + t.tv_nsec;
return (i64)t.tv_sec * 1'000'000'000 + t.tv_nsec;
#endif
}
static i64 to_nsec(struct timeval t) {
return (i64)t.tv_sec * 1000000000 + t.tv_usec * 1000;
static std::pair<i64, i64> get_usage() {
#ifdef _WIN32
auto to_nsec = [](FILETIME t) -> i64 {
return ((u64)t.dwHighDateTime << 32 + (u64)t.dwLowDateTime) * 100;
};
FILETIME creation, exit, kernel, user;
GetProcessTimes(GetCurrentProcess(), &creation, &exit, &kernel, &user);
return {to_nsec(user), to_nsec(kernel)};
#else
auto to_nsec = [](struct timeval t) -> i64 {
return (i64)t.tv_sec * 1'000'000'000 + t.tv_usec * 1'000;
};
struct rusage ru;
getrusage(RUSAGE_SELF, &ru);
return {to_nsec(ru.ru_utime), to_nsec(ru.ru_stime)};
#endif
}
TimerRecord::TimerRecord(std::string name, TimerRecord *parent)
: name(name), parent(parent) {
struct rusage usage;
getrusage(RUSAGE_SELF, &usage);
start = now_nsec();
user = to_nsec(usage.ru_utime);
sys = to_nsec(usage.ru_stime);
std::tie(user, sys) = get_usage();
if (parent)
parent->children.push_back(this);
}
@@ -50,19 +68,20 @@ void TimerRecord::stop() {
return;
stopped = true;
struct rusage usage;
getrusage(RUSAGE_SELF, &usage);
i64 user2;
i64 sys2;
std::tie(user2, sys2) = get_usage();
end = now_nsec();
user = to_nsec(usage.ru_utime) - user;
sys = to_nsec(usage.ru_stime) - sys;
user = user2 - user;
sys = sys2 - sys;
}
static void print_rec(TimerRecord &rec, i64 indent) {
printf(" % 8.3f % 8.3f % 8.3f %s%s\n",
((double)rec.user / 1000000000),
((double)rec.sys / 1000000000),
(((double)rec.end - rec.start) / 1000000000),
((double)rec.user / 1'000'000'000),
((double)rec.sys / 1'000'000'000),
(((double)rec.end - rec.start) / 1'000'000'000),
std::string(indent * 2, ' ').c_str(),
rec.name.c_str());
+11 -5
View File
@@ -1,4 +1,4 @@
#include "mold.h"
#include "common.h"
namespace mold {
@@ -24,8 +24,11 @@ struct UstarHeader {
int sum = 0;
for (i64 i = 0; i < sizeof(*this); i++)
sum += ((u8 *)this)[i];
assert(sum < 1000000);
sprintf(checksum, "%06o", sum);
// We need to convince the compiler that sum isn't too big to silence
// -Werror=format-truncation.
ASSUME(sum < 01'000'000);
snprintf(checksum, sizeof(checksum), "%06o", sum);
}
char name[100];
@@ -47,6 +50,8 @@ struct UstarHeader {
char pad[12];
};
static_assert(sizeof(UstarHeader) == 512);
static std::string encode_path(std::string basedir, std::string path) {
path = path_clean(basedir + "/" + path);
@@ -77,7 +82,8 @@ void TarWriter::append(std::string path, std::string_view data) {
UstarHeader pax;
std::string attr = encode_path(basedir, path);
sprintf(pax.size, "%011zo", attr.size());
snprintf(pax.size, sizeof(pax.size), "%011zo", attr.size());
pax.name[0] = '/';
pax.typeflag[0] = 'x';
pax.finalize();
fwrite(&pax, sizeof(pax), 1, out);
@@ -89,7 +95,7 @@ void TarWriter::append(std::string path, std::string_view data) {
// Write Ustar header
UstarHeader ustar;
memcpy(ustar.mode, "0000664", 8);
sprintf(ustar.size, "%011zo", data.size());
snprintf(ustar.size, sizeof(ustar.size), "%011zo", data.size());
ustar.finalize();
fwrite(&ustar, sizeof(ustar), 1, out);
+33
View File
@@ -0,0 +1,33 @@
# Get a git hash value. We do not want to use git command here
# because we don't want to make git a build-time dependency.
if(EXISTS "${SOURCE_DIR}/.git/HEAD")
file(READ "${SOURCE_DIR}/.git/HEAD" HASH)
string(STRIP "${HASH}" HASH)
if(HASH MATCHES "^ref: (.*)")
set(HEAD "${CMAKE_MATCH_1}")
if(EXISTS "${SOURCE_DIR}/.git/${HEAD}")
file(READ "${SOURCE_DIR}/.git/${HEAD}" HASH)
string(STRIP "${HASH}" HASH)
else()
file(READ "${SOURCE_DIR}/.git/packed-refs" PACKED_REFS)
string(REGEX REPLACE ".*\n([0-9a-f]+) ${HEAD}\n.*" "\\1" HASH "\n${PACKED_REFS}")
endif()
endif()
endif()
# Create new file contents and update a given file if necessary.
set(NEW_CONTENTS "#include <string>
namespace mold {
std::string mold_git_hash = \"${HASH}\";
}
")
if(EXISTS "${OUTPUT_FILE}")
file(READ "${OUTPUT_FILE}" OLD_CONTENTS)
if(NOT "${NEW_CONTENTS}" STREQUAL "${OLD_CONTENTS}")
file(WRITE "${OUTPUT_FILE}" "${NEW_CONTENTS}")
endif()
else()
file(WRITE "${OUTPUT_FILE}" "${NEW_CONTENTS}")
endif()
+20
View File
@@ -0,0 +1,20 @@
#include "common.h"
#include <random>
namespace mold {
std::array<u8, 16> get_uuid_v4() {
std::array<u8, 16> bytes;
std::random_device rand;
u32 buf[4] = { rand(), rand(), rand(), rand() };
memcpy(bytes.data(), buf, 16);
// Indicate that this is UUIDv4 as defined by RFC4122.
bytes[6] = (bytes[6] & 0b00001111) | 0b01000000;
bytes[8] = (bytes[8] & 0b00111111) | 0b10000000;
return bytes;
}
} // namespace mold
-24
View File
@@ -1,24 +0,0 @@
#include "mold.h"
#include <cstdlib>
#include <cxxabi.h>
namespace mold {
std::string_view demangle(std::string_view name) {
if (name.starts_with("_Z")) {
static thread_local char *buf;
static thread_local size_t buflen;
int status;
char *p = abi::__cxa_demangle(std::string(name).c_str(), buf, &buflen, &status);
if (status == 0) {
buf = p;
return p;
}
}
return name;
}
} // namespace mold
+28 -23
View File
@@ -1,4 +1,4 @@
#!/bin/bash -x
#!/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`).
@@ -7,29 +7,34 @@
# libstdc++ and libcrypto but dynamically-linked to libc, libm, libz
# and librt, as they almost always exist on any Linux systems.
set -e
version=$(grep '^VERSION =' $(dirname $0)/Makefile | sed 's/.* = //')
arch=$(uname -m)
dest=mold-$version-$arch-linux
if [ $arch = x86_64 ]; then
image=rui314/mold-builder:v1-x86_64
elif [ $arch = aarch64 -o $arch = arm64 ]; then
image=rui314/mold-builder:v1-aarch64
else
echo "Error: no docker image for $arch"
case $# in
0)
arch=$(uname -m)
[[ $arch = arm* ]] && arch=arm
;;
1)
arch="$1"
;;
*)
echo "Usage: $0 [ x86_64 | aarch64 | arm | ppc64le | s390x ]"
exit 1
fi
esac
docker images -q $image 2> /dev/null || docker pull $image
echo "$arch" | grep -Eq '^(x86_64|aarch64|arm|ppc64le|s390x)$' || \
{ echo "Error: no docker image for $arch"; exit 1; }
docker run -it --rm -v "$(pwd):/mold:Z" -u "$(id -u):$(id -g)" $image \
bash -c "cp -r /mold /tmp/mold &&
cd /tmp/mold &&
make clean &&
make -j\$(nproc) CXX=clang++-14 CXXFLAGS='-I/openssl/include -O2 $CXXFLAGS' LDFLAGS='-static-libstdc++ /openssl/libcrypto.a' NEEDS_LIBCRYPTO=0 LTO=${LTO:-0} &&
make install PREFIX=/ DESTDIR=$dest &&
ln -sfr $dest/bin/mold $dest/libexec/mold/ld &&
version=$(sed -n 's/^project(mold VERSION \(.*\))/\1/p' $(dirname $0)/CMakeLists.txt)
dest=mold-$version-$arch-linux
set -e -x
docker run --platform linux/$arch -i --rm -v "$(pwd):/mold" \
-e "OWNER=$(id -u):$(id -g)" rui314/mold-builder:latest \
bash -c "mkdir /tmp/build &&
cd /tmp/build &&
cmake -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 -DMOLD_MOSTLY_STATIC=On -DCMAKE_BUILD_TYPE=Release /mold &&
cmake --build . -j\$(nproc) &&
[ $arch = arm ] || ctest -j\$(nproc) &&
cmake --install . --prefix $dest --strip &&
tar czf /mold/$dest.tar.gz $dest &&
cp mold /mold"
cp mold mold-wrapper.so /mold &&
chown \$OWNER /mold/mold /mold/mold-wrapper.so /mold/$dest.tar.gz"
Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

+4 -4
View File
@@ -23,10 +23,10 @@ process.
To prevent this type of attack, the stack area is no longer executable
since the early 2000s. On Linux, the stack's executable-ness is
controlled by a bit in an executable, and the loader respect that
controlled by a bit in an executable, and the loader respects that
bit. The bit is set by the linker.
GCC had (and still have) a feature that depends on the executable
GCC had (and still has) a feature that depends on the executable
stack, so they invented a way to tell the linker to mark the stack
executable. Specifically, if an object file contains a
`.note.GNU-stack` section with the `SHF_EXECSTACK` bit, GNU linker
@@ -48,8 +48,8 @@ feature which still depends on the executable stack.
# How to fix it?
If you know what you are doing, pass `-z execstack` to mold. Beaware
If you know what you are doing, pass `-z execstack` to mold. Beware
that this will significantly weaken your program's security.
If you don't want to pass `-z execstack`, rewrite your code so that
your code does not depends on the executable stack.
your code does not depend on the executable stack.
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

+495 -1090
View File
File diff suppressed because it is too large. Load diff
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

+802
View File
@@ -0,0 +1,802 @@
mold(1) -- a modern linker
==========================
## SYNOPSIS
`mold` [_option_...] _file_...
## DESCRIPTION
`mold` is a faster drop-in replacement for the default GNU ld(1).
### How to use
See <https://github.com/rui314/mold#how-to-use>.
### Compatibility
**Mold** is designed to be a drop-in replacement for the GNU linkers for
linking user-land programs. If your user-land program cannot be built due to
missing command-line options, please file a bug at
<https://github.com/rui314/mold/issues>.
Mold supports a very limited set of linker script features, which is just
sufficient to read `/usr/lib/x86_64-linux-gnu/libc.so` on Linux systems (on
Linux, that file is contrary to its name not a shared library but an ASCII
linker script that loads a real `libc.so` file.)
Beyond that, we have no plan to support any additional linker script features.
The linker script is an ad-hoc, over-designed, complex language which we
believe needs to be replaced by a simpler mechanism. We have a plan to add a
replacement for the linker script to `mold` instead.
### Archive symbol resolution
Traditionally, Unix linkers are sensitive to the order in which input files
appear on the command line. They process input files from the first (leftmost)
file to the last (rightmost) file one-by-one. While reading input files, they
maintain sets of defined and undefined symbols. When visiting an archive file
(`.a` files), they pull out object files to resolve as many undefined symbols
as possible and move on to the next input file. Object files that weren't
pulled out will never have a chance for a second look.
Due to this behavior, you usually have to add archive files at the end of a
command line, so that when a linker reaches archive files, it knows what
symbols remain as undefined.
If you put archive files at the beginning of a command line, a linker doesn't
have any undefined symbols, and thus no object files will be pulled out from
archives. You can change the processing order by using the `--start-group` and
`--end-group` options, though they make a linker slower.
`mold`, as well as the LLVM lld(1) linker, takes a different approach. They
remember which symbols can be resolved from archive files instead of
forgetting them after processing each archive. Therefore, `mold` and lld(1)
can "go back" in a command line to pull out object files from archives if they
are needed to resolve remaining undefined symbols. They are not sensitive to
the input file order.
`--start-group` and `--end-group` are still accepted by `mold` and lld(1)
for compatibility with traditional linkers, but they are silently ignored.
### Dynamic symbol resolution
Some Unix linker features are difficult to understand without comprehending
the semantics of dynamic symbol resolution. Therefore, even though it's not
specific to `mold`, we'll explain it here.
We use "ELF module" or just "module" as a collective term to refer to an
executable or a shared library file in the ELF format.
An ELF module may have lists of imported symbols and exported symbols, as well
as a list of shared library names from which imported symbols should be
imported. The point is that imported symbols are not bound to any specific
shared library until runtime.
Here is how the Unix dynamic linker resolves dynamic symbols. Upon the start
of an ELF program, the dynamic linker constructs a list of ELF modules which,
as a whole, consist of a complete program. The executable file is always at
the beginning of the list followed by its dependent shared libraries. An
imported symbol is searched from the beginning of the list to the end. If two
or more modules define the same symbol, the one that appears first in the list
takes precedence over the others.
This Unix semantics are contrary to systems such as Windows that have a
two-level namespace for dynamic symbols. On Windows, for example, dynamic
symbols are represented as a tuple of (`symbol-name`, `shared-library-name`),
so that each dynamic symbol is guaranteed to be resolved from some specific
library.
Typically, an ELF module that exports a symbol also imports the same symbol.
Such a symbol is usually resolved to itself, but that's not the case if a
module that appears before it in the symbol search list provides another
definition of the same symbol.
Let's take `malloc` as an example. Assume that you define your version of
`malloc` in your main executable file. Then, all `malloc` calls from any
module are resolved to your function instead of the one in libc, because the
executable is always at the beginning of the dynamic symbol search list. Note
that even `malloc` calls within libc are resolved to your definition since
libc exports and imports `malloc`. Therefore, by defining `malloc` yourself,
you can overwrite a library function, and the `malloc` in libc becomes dead
code.
These Unix semantics are tricky and sometimes considered harmful. For example,
assume that you accidentally define `atoi` as a global function in your
executable that behaves completely differently from the one in the C standard.
Then, all `atoi` function calls from any modules (even function calls within
libc) are redirected to your function instead of the one in libc, which will
very likely cause a problem.
That is a somewhat surprising consequence for an accidental name conflict. On
the other hand, this semantic is sometimes useful because it allows users to
override library functions without rebuilding modules containing them.
Whether good or bad, you should keep these semantics in mind to understand
Unix linkers' behaviors.
### Build reproducibility
`mold`'s output is deterministic. That is, if you pass the same object files
and the same command-line options to the same version of `mold`, it is
guaranteed that `mold` produces the bit-by-bit identical output. The linker's
internal randomness, such as the timing of thread scheduling or iteration
orders of hash tables, doesn't affect the output.
`mold` does not have any host-specific default settings. This is contrary to the
GNU linkers, for which some configurable values, such as system-dependent
library search paths, are hard-coded. `mold` depends only on its command-line
arguments.
## MOLD-SPECIFIC OPTIONS
* `--chroot`=_dir_:
Set _dir_ as the root directory.
* `--color-diagnostics`=[ _auto_ | _always_ | _never_ ]:
Show diagnostic messages in color using ANSI escape sequences. `auto` means
that `mold` prints out messages in color only if the standard output is
connected to a TTY. Default is `auto`.
* `--color-diagnostics`:
Synonym for `--color-diagnostics=auto`.
* `--no-color-diagnostics`:
Synonym for `--color-diagnostics=never`.
* `--fork`, `--no-fork`:
Spawn a child process and let it do the actual linking. When linking a large
program, the OS kernel can take a few hundred milliseconds to terminate a
`mold` process. `--fork` hides that latency. By default, it does fork.
* `--perf`:
Print performance statistics.
* `--print-dependencies`:
Print out dependency information for input files.
Each line of the output for this option shows which file depends on which
file to use a specific symbol. This option is useful for debugging why some
object file in a static archive got linked or why some shared library is
kept in an output file's dependency list even with `--as-needed`.
* `--repro`:
Archive input files, as well as a text file containing command line options,
in a tar file so that you can run `mold` with the exact same inputs again.
This is useful for reporting a bug with a reproducer. The output filename is
`path/to/output.tar`, where `path/to/output` is an output filename specified
by `-o`.
* `--reverse-sections`:
Reverse the order of input sections before assigning them the offsets in the
output file.
This option is useful for finding bugs that depend on the initialization
order of global objects. In C++, constructors of global objects in a single
source file are guaranteed to be executed in the source order, but there's
no such guarantee across compilation units. Usually, constructors are
executed in the order given to the linker, but depending on it is a mistake.
By reversing the order of input sections using `--reverse-sections`, you can
easily test that your program works in the reversed initialization order.
* `--run` _command_ _arg_...:
Run _command_ with `mold` `/usr/bin/ld`. Specifically, `mold` runs a given
command with the `LD_PRELOAD` environment set to intercept exec(3) family
functions and replaces `argv[0]` with itself if it is `ld`, `ld.gold`, or
`ld.lld`.
* `--shuffle-sections`, `--shuffle-sections`=_number_:
Randomize the output by shuffling the order of input sections before
assigning them the offsets in the output file. If a _number_ is given, it's
used as a seed for the random number generator, so that the linker produces
the same output for the same seed. If no seed is given, a random number is
used as a seed.
This option is useful for benchmarking. Modern CPUs are sensitive to a
program's memory layout. A seemingly benign change in program layout, such
as a small size increase of a function in the middle of a program, can
affect the program's performance. Therefore, even if you write new code and
get a good benchmark result, it is hard to say whether the new code improves
the program's performance; it is possible that the new memory layout happens
to perform better.
By running a benchmark multiple times with randomized memory layouts using
`--shuffle-sections`, you can isolate your program's real performance number
from the randomness caused by memory layout changes.
* `--stats`:
Print input statistics.
* `--thread-count`=_count_:
Use _count_ number of threads.
* `--threads`, `--no-threads`:
Use multiple threads. By default, `mold` uses as many threads as the number of
cores or 32, whichever is smaller. The reason it is capped at 32 is because
`mold` doesn't scale well beyond that point. To use only one thread, pass
`--no-threads` or `--thread-count=1`.
* `--quick-exit`, `--no-quick-exit`:
Use or do not use `quick_exit` to exit.
## GNU-COMPATIBLE OPTIONS
* `--help`:
Report usage information to stdout and exit.
* `-v`, `--version`:
Report version information to stdout.
* `-V`:
Report version and target information to stdout.
* `-E`, `--export-dynamic`, `--no-export-dynamic`:
When creating an executable, using the `-E` option causes all global symbols
to be put into the dynamic symbol table, so that the symbols are visible
from other ELF modules at runtime.
By default, or if `--no-export-dynamic` is given, only symbols that are
referenced by DSOs at link-time are exported from an executable.
* `-F` _libname_, `--filter`=_libname_:
Set the `DT_FILTER` dynamic section field to _libname_.
* `-I` _file_, `--dynamic-linker`=_file_, `--no-dynamic-linker`:
Set the dynamic linker path to _file_. If no `-I` option is given, or if
`--no-dynamic-linker` is given, no dynamic linker path is set to an output
file. This is contrary to the GNU linkers which set a default dynamic linker
path in that case. This difference doesn't usually make any difference
because the compiler driver always passes `-I` to the linker.
* `-L` _dir_, `--library-path`=_dir_:
Add _dir_ to the list of library search paths from which `mold` searches
libraries for the `-l` option.
Unlike the GNU linkers, `mold` does not have default search paths. This
difference doesn't usually make any difference because the compiler driver
always passes all necessary search paths to the linker.
* `-M`, `--print-map`:
Write a map file to stdout.
* `-N`, `--omagic`, `--no-omagic`:
Force `mold` to emit an output file with an old-fashioned memory layout.
First, it makes the first data segment not aligned to a page boundary.
Second, text segments are marked as writable if the option is given.
* `-S`, `--strip-debug`:
Omit `.debug_*` sections from the output file.
* `-T` _file_, `--script`=_file_:
Read linker script from _file_.
* `-X`, `--discard-locals`:
Discard temporary local symbols to reduce the sizes of the symbol table and
the string table. Temporary local symbols are local symbols starting with
`.L`. Compilers usually generate such symbols for unnamed program elements
such as string literals or floating-point literals.
* `-e` _symbol_, `--entry`=_symbol_:
Use _symbol_ as the entry point symbol instead of the default entry
point symbol _start.
* `-f` _shlib_, `--auxiliary`=_shlib_:
Set the `DT_AUXILIARY` dynamic section field to _shlib_.
* `-h` _libname_, `--soname`=_libname_:
Set the `DT_SONAME` dynamic section field to _libname_. This option is used
when creating a shared object file. Typically, when you create `libfoo.so`,
you want to pass `--soname=foo` to a linker.
* `-l` _libname_:
Search for `lib`_libname_`.so` or `lib`_libname_`.a` from library search
paths.
* `-m` _target_:
Choose a _target_.
* `-o` _file_, `--output`=_file_:
Use _file_ as the output file name instead of the default name `a.out`.
* `-r`, `--relocatable`:
Instead of generating an executable or a shared object file, combine input
object files to generate another object file that can be used as an input to
a linker.
* `--relocatable-merge-sections`:
By default, `mold` doesn't merge input sections by name when merging input
object files into a single output object file for `-r`. For example,
`.text.foo` and `.text.bar` aren't merged for `-r` even though they are
merged into `.text` according to the default section merging rules.
This option changes the behavior so that `mold` merges input sections by
name by the default section merging rules.
* `-s`, `--strip-all`:
Omit `.symtab` section from the output file.
* `-u` _symbol_, `--undefined`=_symbol_:
If _symbol_ remains as an undefined symbol after reading all object files,
and if there is a static archive that contains an object file defining
_symbol_, pull out the object file and link it so that the output file
contains a definition of _symbol_.
* `--Bdynamic`:
Link against shared libraries.
* `--Bstatic`:
Do not link against shared libraries.
* `--Bsymbolic`:
When creating a shared library, make global symbols export-only (i.e. do not
import the same symbol). As a result, references within a shared library are
always resolved locally, negating symbol override at runtime. See "Dynamic
symbol resolution" for more information about symbol imports and exports.
* `--Bsymbolic-functions`:
This option has the same effect as `--Bsymbolic` but works only for function
symbols. Data symbols remain being both imported and exported.
* `--Bno-symbolic`:
Cancel `--Bsymbolic` and `--Bsymbolic-functions`.
* `--Map`=_file_:
Write map file to _file_.
* `--Tbss`=_address_:
Alias for `--section-start=.bss=`_address_.
* `--Tdata`=_address_:
Alias for `--section-start=.data=`_address_.
* `--Ttext`=_address_:
Alias for `--section-start=.text=`_address_.
* `--allow-multiple-definition`:
Normally, the linker reports an error if there are more than one definition
of a symbol. This option changes the default behavior so that it doesn't
report an error for duplicate definitions and instead use the first
definition.
* `--as-needed`, `--no-as-needed`:
By default, shared libraries given to the linker are unconditionally added
to the list of required libraries in an output file. However, shared
libraries after `--as-needed` are added to the list only when at least one
symbol is actually used by the output file. In other words, shared libraries
after `--as-needed` are not added to the list of needed libraries if they
are not needed by a program.
The `--no-as-needed` option restores the default behavior for subsequent
files.
* `--build-id`=[ `md5` | `sha1` | `sha256` | `uuid` | `0x`_hexstring_ | `none` ]:
Create a `.note.gnu.build-id` section containing a byte string to uniquely
identify an output file. `sha256` compute a 256-bit cryptographic hash of an
output file and set it to build-id. `md5` and `sha1` compute the same hash
but truncate it to 128 and 160 bits, respectively, before setting it to
build-id. `uuid` sets a random 128-bit UUID. `0x`_hexstring_ sets
_hexstring_.
* `--build-id`:
Synonym for `--build-id=sha256`.
* `--no-build-id`:
Synonym for `--build-id=none`.
* `--compress-debug-sections`=[ `zlib` | `zlib-gabi` | `zstd` | `none` ]:
Compress DWARF debug info (`.debug_*` sections) using the zlib or zstd
compression algorithm. `zlib-gabi` is an alias for `zlib`.
* `--defsym`=_symbol_=_value_:
Define _symbol_ as an alias for _value_.
_value_ is either an integer (in decimal or hexadecimal with `0x` prefix) or
a symbol name. If an integer is given as a value, _symbol_ is defined as an
absolute symbol with the given value.
* `--default-symver`:
Use soname as a symbol version and append that version to all symbols.
* `--demangle`, `--no-demangle`:
Demangle C++ and Rust symbols in log messages.
* `--dependency-file`=_file_:
Write a dependency file to _file_. The contents of the written file is
readable by make(1), which defines only one rule with the linker's output
file as a target and all input files as its prerequisites. Users are
expected to include the generated dependency file into a Makefile to
automate the dependency management. This option is analogous to the
compiler's `-MM -MF` options.
* `--dynamic-list`=_file_:
Read a list of dynamic symbols from _file_. Same as
`--export-dynamic-symbol-list`, except that it implies `--Bsymbolic`. If
_file_ does not exist in the current directory, it is searched from library
search paths for the sake of compatibility with GNU ld.
* `--eh-frame-hdr`, `--no-eh-frame-hdr`:
Create `.eh_frame_hdr` section.
* `--emit-relocs`:
The linker usually "consumes" relocation sections. That is, the linker
applies relocations to other sections, and relocation sections themselves
are discarded.
The `--emit-relocs` instructs the linker to leave relocation sections in the
output file. Some post-link binary analysis or optimization tools such as
LLVM Bolt need them.
* `--enable-new-dtags`, `--disable-new-dtags`:
By default, `mold` emits `DT_RUNPATH` for `--rpath`. If you pass
`--disable-new-dtags`, `mold` emits `DT_RPATH` for `--rpath` instead.
* `--execute-only`:
Traditionally, most processors require both executable and readable bits to
1 to make the page executable, which allows machine code to be read as data
at runtime. This is actually what an attacker often does after gaining a
limited control of a process to find pieces of machine code they can use to
gain the full control of the process. As a mitigation, some recent
processors allows "execute-only" pages. If a page is execute-only, you can
call a function there as long as you know its address but can't read it as
data.
This option marks text segments execute-only. This option currently works
only on some ARM64 processors.
* `--exclude-libs`=_libraries_ ...:
Mark all symbols in the given _libraries_ hidden.
* `--export-dynamic-symbol`=_symbol_:
Put symbols matching _symbol_ in the dynamic symbol table. _symbol_ may be a
glob pattern in the same syntax as for the `--export-dynamic-symbol-list` or
`--version-script` options.
* `--export-dynamic-symbol-list`=_file_:
Read a list of dynamic symbols from _file_.
* `--fatal-warnings`, `--no-fatal-warnings`:
Treat warnings as errors.
* `--fini`=_symbol_:
Call _symbol_ at unload-time.
* `--gc-sections`, `--no-gc-sections`:
Remove unreferenced sections.
* `--gdb-index`:
Create a `.gdb_index` section to speed up GNU debugger. To use this, you
need to compile source files with the `-ggnu-pubnames` compiler flag.
* `--hash-style`=[ `sysv` | `gnu` | `both` | `none` ]:
Set hash style.
* `--icf`=[ `safe` | `all` | `none` ], `--no-icf`:
It is not uncommon for a program to contain many identical functions that
differ only in name. For example, a C++ template `std::vector` is very
likely to be instantiated to the identical code for `std::vector<int>` and
`std::vector<unsigned>` because the container cares only about the size of
the parameter type. Identical Code Folding (ICF) is a size optimization to
identify and merge such identical functions.
If `--icf=all` is given, `mold` tries to merge all identical functions. This
reduces the size of the output most, but it is not a "safe" optimization. It
is guaranteed in C and C++ that two pointers pointing two different
functions will never be equal, but `--icf=all` breaks that assumption as two
identical functions have the same address after merging. So a care must be
taken when you use this flag that your program does not depend on the
function pointer uniqueness.
`--icf=safe` is a flag to merge functions only when it is safe to do so.
That is, if a program does not take an address of a function, it is safe to
merge that function with other function, as you cannot compare a function
pointer with something else without taking an address of a function.
`--icf=safe` needs to be used with a compiler that supports `.llvm_addrsig`
section which contains the information as to what symbols are address-taken.
LLVM/Clang supports that section by default. Since GCC does not support it
yet, you cannot use `--icf=safe` with GCC (it doesn't do any harm but can't
optimize at all.)
`--icf=none` and `--no-icf` disables ICF.
* `--ignore-data-address-equality`:
Make ICF to merge not only functions but also data. This option should be
used in combination with `--icf=all`.
* `--image-base`=_addr_:
Set the base address to _addr_.
* `--init`=_symbol_:
Call _symbol_ at load-time.
* `--no-undefined`:
Report undefined symbols (even with `--shared`).
* `--noinhibit-exec`:
Create an output file even if errors occur.
* `--pack-dyn-relocs`=[ `relr` | `none` ]:
If `relr` is specified, all `R_*_RELATIVE` relocations are put into
`.relr.dyn` section instead of `.rel.dyn` or `.rela.dyn` section. Since
`.relr.dyn` section uses a space-efficient encoding scheme, specifying this
flag can reduce the size of the output. This is typically most effective for
position-independent executable.
Note that a runtime loader has to support `.relr.dyn` to run executables or
shared libraries linked with `--pack-dyn-relocs=relr`. As of 2022, only
ChromeOS, Android and Fuchsia support it.
* `--package-metadata`=_string_:
Embed _string_ to a `.note.package` section. This option is intended to be
used by a package management command such as rpm(8) to embed metadata
regarding a package to each executable file.
* `--pie`, `--pic-executable`, `--no-pie`, `--no-pic-executable`:
Create a position-independent executable.
* `--print-gc-sections`, `--no-print-gc-sections`:
Print removed unreferenced sections.
* `--print-icf-sections`, `--no-print-icf-sections`:
Print folded identical sections.
* `--push-state`, `--pop-state`:
`--push-state` saves the current values of `--as-needed`, `--whole-archive`,
`--static`, and `--start-lib`. The saved values can be restored by
pop-state.
`--push-state` and `--pop-state` pairs can nest.
These options are useful when you want to construct linker command line
options programmatically. For example, if you want to link `libfoo.so` by
as-needed basis but don't want to change the global state of `--as-needed`,
you can append `--push-state --as-needed -lfoo --pop-state` to the linker
command line options.
* `--relax, --no-relax`:
Rewrite machine instructions with more efficient ones for some relocations.
The feature is enabled by default.
* `--require-defined`=_symbol_:
Like `--undefined`, except the new symbol must be defined by the end of the
link.
* `--retain-symbols-file`=_file_:
Keep only symbols listed in _file_. _file_ is a text file containing a
symbol name on each line. `mold` discards all local symbols as well as
global symbol that are not in _file_. Note that this option removes symbols
only from `.symtab` section and does not affect `.dynsym` section, which is
used for dynamic linking.
* `--rpath`=_dir_:
Add _dir_ to runtime search paths.
* `--section-start`=_section_=_address_:
Set _address_ to section. _address_ is a hexadecimal number that may start
with an optional `0x`.
* `--shared`, `--Bshareable`:
Create a share library.
* `--spare-dynamic-tags`=_number_:
Reserve the given _number_ of tags in `.dynamic` section.
* `--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
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).
* `--static`:
Do not link against shared libraries.
* `--sysroot`=_dir_:
Set target system root directory to _dir_.
* `--trace`:
Print name of each input file.
* `--undefined-version`, `--no-undefined-version`:
By default, `mold` warns on a symbol specified by a version script or by
`--export-dynamic-symbol` if it is not defined. You can silence the warning
by `--undefined-version`.
* `--unique`=_pattern_:
Don't merge input sections that match the given glob pattern _pattern_.
* `--unresolved-symbols`=[ `report-all` | `ignore-all` | `ignore-in-object-files` | `ignore-in-shared-libs` ]:
How to handle undefined symbols.
* `--version-script`=_file_:
Read version script from _file_. If _file_ does not exist in the current
directory, it is searched from library search paths for the sake of
compatibility with GNU ld.
* `--warn-common`, `--no-warn-common`:
Warn about common symbols.
* `--warn-once`:
Only warn once for each undefined symbol instead of warn for each relocation
referring an undefined symbol.
* `--warn-unresolved-symbols`, `--error-unresolved-symbols`:
Normally, the linker reports an error for unresolved symbols.
`--warn-unresolved-symbols` option turns it into a warning.
`--error-unresolved-symbols` option restores the default behavior.
* `--whole-archive`, `--no-whole-archive`:
When archive files (`.a` files) are given to the linker, only object files
that are needed to resolve undefined symbols are extracted from them and
linked to an output file. `--whole-archive` changes that behavior for
subsequent archives so that the linker extracts all object files and links
them to an output. For example, if you are creating a shared object file and
you want to include all archive members to the output, you should pass
`--whole-archive`. `--no-whole-archive` restores the default behavior for
subsequent archives.
* `--wrap`=_symbol_:
Make _symbol_ be resolved to `__wrap_`_symbol_. The original symbol can be
resolved as `__real_`_symbol_. This option is typically used for wrapping an
existing function.
* `-z cet-report`=[ `warning` | `error` | `none` ]:
Intel Control-flow Enforcement Technology (CET) is a new x86 feature
available since Tiger Lake which is released in 2020. It defines new
instructions to harden security to protect programs from control hijacking
attacks. You can tell the compiler to use the feature by specifying the
`-fcf-protection` flag.
`-z cet-report` flag is used to make sure that all object files were
compiled with a correct `-fcf-protection` flag. If `warning` or `error` are
given, `mold` prints out a warning or an error message if an object file was
not compiled with the compiler flag.
`mold` looks for `GNU_PROPERTY_X86_FEATURE_1_IBT` bit and
`GNU_PROPERTY_X86_FEATURE_1_SHSTK` bit in `.note.gnu.property` section to
determine whether or not an object file was compiled with `-fcf-protection`.
* `-z now`, `-z lazy`:
By default, functions referring to other ELF modules are resolved by the
dynamic linker when they are called for the first time. `-z now` marks an
executable or a shared library file so that all dynamic symbols are resolved
when a file is loaded to memory. `-z lazy` restores the default behavior.
* `-z origin`:
Mark object requiring immediate `$ORIGIN` processing at runtime.
* `-z ibt`:
Turn on `GNU_PROPERTY_X86_FEATURE_1_IBT` bit in `.note.gnu.property` section
to indicate that the output uses IBT-enabled PLT. This option implies `-z
ibtplt`.
* `-z ibtplt`:
Generate Intel Branch Tracking (IBT)-enabled PLT which is the default on
x86-64. This is the default.
* `-z execstack`, `-z noexecstack`:
By default, the pages for the stack area (i.e. the pages where local
variables reside) are not executable for security reasons. `-z execstack`
makes it executable. `-z noexecstack` restores the default behavior.
* `-z keep-text-section-prefix`, `-z nokeep-text-section-prefix`:
Keep `.text.hot`, `.text.unknown`, `.text.unlikely`, `.text.startup`, and
`.text.exit` as separate sections in the final binary instead of merging
them as `.text`.
* `-z relro`, `-z norelro`:
Some sections such as `.dynamic` have to be writable only during an
executable or a shared library file is being loaded to memory. Once the
dynamic linker finishes its job, such sections won't be mutated by anyone.
As a security mitigation, it is preferred to make such segments read-only
during program execution.
`-z relro` puts such sections into a special segment called `relro`. The
dynamic linker makes a relro segment read-only after it finishes its job.
By default, `mold` generates a relro segment. `-z norelro` disables the
feature.
* `-z sectionheader`, `-z nosectionheader`:
`-z nosectionheader` tell the linker to omit the section header.
By default, the linker does not omit the section header.
* `-z separate-loadable-segments`, `-z separate-code`, `-z noseparate-code`:
If one memory page contains multiple segments, the page protection bits are
set in such a way that the needed attributes (writable or executable) are
satisfied for all segments. This usually happens at a boundary of two
segments with two different attributes.
`separate-loadable-segments` adds paddings between segments with different
attributes so that they do not share the same page. This is the default.
`separate-code` adds paddings only between executable and non-executable
segments.
`noseparate-code` does not add any paddings between segments.
* `-z defs`, `-z nodefs`:
Report undefined symbols (even with `--shared`).
* `-z shstk`:
Enforce shadow stack by turning `GNU_PROPERTY_X86_FEATURE_1_SHSTK` bit in
`.note.gnu.property` output section. Shadow stack is part of Intel
Control-flow Enforcement Technology (CET), which is available since Tiger
Lake (2020).
* `-z text`, `-z notext`, `-z textoff`:
`mold` by default reports an error if dynamic relocations are created in
read-only sections. If `-z notext` or `-z textoff` are given, `mold` creates
such dynamic relocations without reporting an error. `-z text` restores the
default behavior.
* `-z max-page-size`=_number_:
Some CPU ISAs support multiple different memory page sizes. This option
specifies the maximum page size that an output binary can run on. The
default value is 4 KiB for i386, x86-64, and RISC-V, and 64 KiB for ARM64.
* `-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(3). This option makes it possible for the
linker to optimize thread-local variable accesses by rewriting instructions
for some targets.
* `-z nodump`:
Mark DSO not available to dldump(3).
* `-z nocopyreloc`:
Do not create copy relocations.
* `-z initfirst`:
Mark DSO to be initialized first at runtime.
* `-z interpose`:
Mark object to interpose all DSOs but executable.
* `-(`, `-)`, `-EL`, `-O`_number_, `--allow-shlib-undefined`, `--dc`, `--dp`, `--end-group`, `--no-add-needed`, `--no-allow-shlib-undefined`, `--no-copy-dt-needed-entries`, `--no-fatal-warnings`, `--nostdlib`, `--rpath-link=Ar dir`, `--sort-common`, `--sort-section`, `--start-group`, `--warn-constructors`, `--warn-once`, `--fix-cortex-a53-835769`, `--fix-cortex-a53-843419`, `-z combreloc`, `-z common-page-size`, `-z nocombreloc`:
Ignored
## ENVIRONMENT VARIABLES
* `MOLD_JOBS`:
If this variable is set to `1`, only one process of `mold` runs actively. A
mold process invoked while another active mold process is running will wait
before doing anything until the active process exits.
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.
If your build system tends to invoke multiple linker processes
simultaneously, you may want to try to set this environment variable to
`1` to see if it could improve overall performance.
Currently, any value other than 1 is silently ignored.
* `MOLD_DEBUG`:
If this variable is set to a non-empty string, `mold` embeds its
command-line options in the output file's `.comment` section.
* `MOLD_REPRO`:
Setting this variable to a non-empty string has the same effect as passing
the `--repro` option.
## SEE ALSO
gold(1), ld(1), elf(5), ld.so(8)
## AUTHOR
Rui Ueyama <ruiu@cs.stanford.edu>
## BUGS
Report bugs to <https://github.com/rui314/mold/issues>.
+330
View File
@@ -0,0 +1,330 @@
// Alpha is a 64-bit RISC ISA developed by DEC (Digital Equipment
// Corporation) in the early '90s. It aimed to be an ISA that would last
// 25 years. DEC expected Alpha would become 1000x faster during that time
// span. Since the ISA was developed from scratch for future machines,
// it's 64-bit from the beginning. There's no 32-bit variant.
//
// DEC ported its own Unix (Tru64) to Alpha. Microsoft also ported Windows
// NT to it. But it wasn't a huge commercial success.
//
// DEC was acquired by Compaq in 1997. In the late '90s, Intel and
// Hewlett-Packard were advertising that their upcoming Itanium processor
// would achieve significantly better performance than RISC processors, so
// Compaq decided to discontinue the Alpha processor line to switch to
// Itanium. Itanium resulted in a miserable failure, but it still suceeded
// to wipe out several RISC processors just by promising overly optimistic
// perf numbers. Alpha as an ISA would probably have been fine after 25
// years since its introduction (which is 1992 + 25 = 2017), but the
// company and its market didn't last that long.
//
// From the linker's point of view, there are a few peculiarities in its
// psABI as shown below:
//
// - Alpha lacks PC-relative memory load/store instructions, so it uses
// register-relative load/store instructions in position-independent
// code. Specifically, GP (which is an alias for $r29) is always
// maintained to refer to .got+0x8000, and global variables' addresses
// are loaded in a GP-relative manner.
//
// - It looks like even function addresses are first loaded to register
// in a GP-relative manner before calling it. We can relax it to
// convert the instruction sequence with a direct branch instruction,
// but by default, object files don't use a direct branch to call a
// function. Therefore, by default, we don't need to create a PLT.
// Any function call is made by first reading its address from GOT and
// jump to the address.
#include "mold.h"
namespace mold::elf {
using E = ALPHA;
// A 32-bit immediate can be materialized in a register with a "load high"
// and a "load low" instruction sequence. The first instruction sets the
// upper 16 bits in a register, and the second one set the lower 16
// bits. When doing so, they sign-extend an immediate. Therefore, if the
// 15th bit of an immediate happens to be 1, setting a "low half" value
// negates the upper 16 bit values that has already been set in a
// register. To compensate that, we need to add 0x8000 when setting the
// upper 16 bits.
static u32 hi(u32 val) {
return bits(val + 0x8000, 31, 16);
}
template <>
void write_plt_header(Context<E> &ctx, u8 *buf) {}
template <>
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {}
template <>
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {}
template <>
void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_NONE:
break;
case R_ALPHA_SREL32:
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
break;
default:
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
ElfRel<E> *dynrel = nullptr;
if (ctx.reldyn)
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
file.reldyn_offset + this->reldyn_offset);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
u64 S = sym.get_addr(ctx);
u64 A = rel.r_addend;
u64 P = get_addr() + rel.r_offset;
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
u64 GOT = ctx.got->shdr.sh_addr;
u64 GP = ctx.got->shdr.sh_addr + 0x8000;
switch (rel.r_type) {
case R_ALPHA_REFQUAD:
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_ALPHA_GPREL32:
*(ul32 *)loc = S + A - GP;
break;
case R_ALPHA_LITERAL:
if (A)
*(ul16 *)loc = ctx.extra.got->get_addr(sym, A) - GP;
else
*(ul16 *)loc = GOT + G - GP;
break;
case R_ALPHA_BRSGP:
*(ul32 *)loc |= bits(S + A - P - 4, 22, 0);
break;
case R_ALPHA_GPDISP:
*(ul16 *)loc = hi(GP - P);
*(ul16 *)(loc + A) = GP - P;
break;
case R_ALPHA_SREL32:
*(ul32 *)loc = S + A - P;
break;
case R_ALPHA_GPRELHIGH:
*(ul16 *)loc = hi(S + A - GP);
break;
case R_ALPHA_GPRELLOW:
*(ul16 *)loc = S + A - GP;
break;
case R_ALPHA_TLSGD:
*(ul16 *)loc = sym.get_tlsgd_addr(ctx) - GP;
break;
case R_ALPHA_TLSLDM:
*(ul16 *)loc = ctx.got->get_tlsld_addr(ctx) - GP;
break;
case R_ALPHA_DTPRELHI:
*(ul16 *)loc = hi(S + A - ctx.dtp_addr);
break;
case R_ALPHA_DTPRELLO:
*(ul16 *)loc = S + A - ctx.dtp_addr;
break;
case R_ALPHA_GOTTPREL:
*(ul16 *)loc = sym.get_gottp_addr(ctx) + A - GP;
break;
case R_ALPHA_TPRELHI:
*(ul16 *)loc = hi(S + A - ctx.tp_addr);
break;
case R_ALPHA_TPRELLO:
*(ul16 *)loc = S + A - ctx.tp_addr;
break;
case R_ALPHA_LITUSE:
case R_ALPHA_HINT:
break;
default:
unreachable();
}
}
}
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
SectionFragment<E> *frag;
i64 frag_addend;
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
u64 A = frag ? frag_addend : (i64)rel.r_addend;
switch (rel.r_type) {
case R_ALPHA_REFLONG:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ul32 *)loc = *val;
else
*(ul32 *)loc = S + A;
break;
case R_ALPHA_REFQUAD:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ul64 *)loc = *val;
else
*(ul64 *)loc = S + A;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
<< rel;
}
}
}
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (sym.is_ifunc())
Error(ctx) << sym << ": GNU ifunc symbol is not supported on Alpha";
switch (rel.r_type) {
case R_ALPHA_REFQUAD:
scan_dyn_absrel(ctx, sym, rel);
break;
case R_ALPHA_LITERAL:
if (rel.r_addend)
ctx.extra.got->add_symbol(sym, rel.r_addend);
else
sym.flags |= NEEDS_GOT;
break;
case R_ALPHA_SREL32:
scan_pcrel(ctx, sym, rel);
break;
case R_ALPHA_BRSGP:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_ALPHA_TLSGD:
sym.flags |= NEEDS_TLSGD;
break;
case R_ALPHA_TLSLDM:
ctx.needs_tlsld = true;
break;
case R_ALPHA_GOTTPREL:
sym.flags |= NEEDS_GOTTP;
break;
case R_ALPHA_TPRELHI:
case R_ALPHA_TPRELLO:
check_tlsle(ctx, sym, rel);
break;
case R_ALPHA_GPREL32:
case R_ALPHA_LITUSE:
case R_ALPHA_GPDISP:
case R_ALPHA_HINT:
case R_ALPHA_GPRELHIGH:
case R_ALPHA_GPRELLOW:
case R_ALPHA_DTPRELHI:
case R_ALPHA_DTPRELLO:
break;
default:
Fatal(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
// An R_ALPHA_LITERAL relocation may request the linker to create a GOT
// entry for an external symbol with a non-zero addend. This is an unusual
// request which is not found in any other targets.
//
// Referring an external symbol with a non-zero addend is a bad practice
// because we need to create as many dynamic relocations as the number of
// distinctive addends for the same symbol.
//
// We don't want to mess up the implementation of the common GOT section
// for Alpha. So we create another GOT-like section, .alpha_got. Any GOT
// entry for an R_ALPHA_LITERAL reloc with a non-zero addend is created
// not in .got but in .alpha_got.
//
// Since .alpha_got entries are accessed relative to GP, .alpha_got
// needs to be close enough to .got. It's actually placed next to .got.
void AlphaGotSection::add_symbol(Symbol<E> &sym, i64 addend) {
assert(addend);
std::scoped_lock lock(mu);
entries.push_back({&sym, addend});
}
bool operator<(const AlphaGotSection::Entry &a, const AlphaGotSection::Entry &b) {
return std::tuple(a.sym->file->priority, a.sym->sym_idx, a.addend) <
std::tuple(b.sym->file->priority, b.sym->sym_idx, b.addend);
};
u64 AlphaGotSection::get_addr(Symbol<E> &sym, i64 addend) {
auto it = std::lower_bound(entries.begin(), entries.end(), Entry{&sym, addend});
assert(it != entries.end());
return this->shdr.sh_addr + (it - entries.begin()) * sizeof(Word<E>);
}
i64 AlphaGotSection::get_reldyn_size(Context<E> &ctx) const {
i64 n = 0;
for (const Entry &e : entries)
if (e.sym->is_imported || (ctx.arg.pic && !e.sym->is_absolute()))
n++;
return n;
}
void AlphaGotSection::finalize() {
sort(entries);
remove_duplicates(entries);
shdr.sh_size = entries.size() * sizeof(Word<E>);
}
void AlphaGotSection::copy_buf(Context<E> &ctx) {
ElfRel<E> *dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
reldyn_offset);
for (i64 i = 0; i < entries.size(); i++) {
Entry &e = entries[i];
u64 P = this->shdr.sh_addr + sizeof(Word<E>) * i;
ul64 *buf = (ul64 *)(ctx.buf + this->shdr.sh_offset + sizeof(Word<E>) * i);
if (e.sym->is_imported) {
*buf = ctx.arg.apply_dynamic_relocs ? e.addend : 0;
*dynrel++ = ElfRel<E>(P, E::R_ABS, e.sym->get_dynsym_idx(ctx), e.addend);
} else {
*buf = e.sym->get_addr(ctx) + e.addend;
if (ctx.arg.pic && !e.sym->is_absolute())
*dynrel++ = ElfRel<E>(P, E::R_RELATIVE, 0, *buf);
}
}
}
} // namespace mold::elf
+511 -338
View File
File diff suppressed because it is too large. Load diff
+420 -589
View File
File diff suppressed because it is too large. Load diff
+393 -376
View File
@@ -1,425 +1,474 @@
// i386 is similar to x86-64 but lacks PC-relative memory access
// instructions. So it's not straightforward to support position-
// independent code (PIC) on that target.
//
// If an object file is compiled with -fPIC, a function that needs to load
// a value from memory first obtains its own address with the following
// code
//
// call __x86.get_pc_thunk.bx
//
// where __x86.get_pc_thunk.bx is defined as
//
// __x86.get_pc_thunk.bx:
// mov (%esp), %ebx # move the return address to %ebx
// ret
//
// . With the function's own address (or, more precisely, the address
// immediately after the call instruction), the function can compute an
// absolute address of a variable with its address + link-time constant.
//
// Executing call-mov-ret isn't very cheap, and allocating one register to
// store PC isn't cheap too, especially given that i386 has only 8
// general-purpose registers. But that's the cost of PIC on i386. You need
// to pay it when creating a .so and a position-independent executable.
//
// When a position-independent function calls another function, it sets
// %ebx to the address of .got. Position-independent PLT entries use that
// register to load values from .got.plt/.got.
//
// If we are creating a position-dependent executable (PDE), we can't
// assume that %ebx is set to .got. For PDE, we need to create position-
// dependent PLT entries which don't use %ebx.
//
// https://github.com/rui314/psabi/blob/main/i386.pdf
#include "mold.h"
namespace mold::elf {
using E = I386;
static void write_plt_header(Context<E> &ctx, u8 *buf) {
if (ctx.arg.pic) {
static const u8 plt0[] = {
0xff, 0xb3, 0, 0, 0, 0, // pushl GOTPLT+4(%ebx)
0xff, 0xa3, 0, 0, 0, 0, // jmp *GOTPLT+8(%ebx)
0x90, 0x90, 0x90, 0x90, // nop
};
memcpy(buf, plt0, sizeof(plt0));
*(u32 *)(buf + 2) = ctx.gotplt->shdr.sh_addr - ctx.got->shdr.sh_addr + 4;
*(u32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr - ctx.got->shdr.sh_addr + 8;
} else {
static const u8 plt0[] = {
0xff, 0x35, 0, 0, 0, 0, // pushl GOTPLT+4
0xff, 0x25, 0, 0, 0, 0, // jmp *GOTPLT+8
0x90, 0x90, 0x90, 0x90, // nop
};
memcpy(buf, plt0, sizeof(plt0));
*(u32 *)(buf + 2) = ctx.gotplt->shdr.sh_addr + 4;
*(u32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr + 8;
template <>
i64 get_addend(u8 *loc, const ElfRel<E> &rel) {
switch (rel.r_type) {
case R_386_8:
case R_386_PC8:
return *loc;
case R_386_16:
case R_386_PC16:
return *(ul16 *)loc;
case R_386_32:
case R_386_PC32:
case R_386_GOT32:
case R_386_GOT32X:
case R_386_PLT32:
case R_386_GOTOFF:
case R_386_GOTPC:
case R_386_TLS_LDM:
case R_386_TLS_GOTIE:
case R_386_TLS_LE:
case R_386_TLS_IE:
case R_386_TLS_GD:
case R_386_TLS_LDO_32:
case R_386_SIZE32:
case R_386_TLS_GOTDESC:
return *(ul32 *)loc;
default:
return 0;
}
}
static void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym,
i64 idx) {
u8 *ent = buf + ctx.plt_hdr_size + sym.get_plt_idx(ctx) * ctx.plt_size;
template <>
void write_addend(u8 *loc, i64 val, const ElfRel<E> &rel) {
switch (rel.r_type) {
case R_386_NONE:
break;
case R_386_8:
case R_386_PC8:
*loc = val;
break;
case R_386_16:
case R_386_PC16:
*(ul16 *)loc = val;
break;
case R_386_32:
case R_386_PC32:
case R_386_GOT32:
case R_386_GOT32X:
case R_386_PLT32:
case R_386_GOTOFF:
case R_386_GOTPC:
case R_386_TLS_LDM:
case R_386_TLS_GOTIE:
case R_386_TLS_LE:
case R_386_TLS_IE:
case R_386_TLS_GD:
case R_386_TLS_LDO_32:
case R_386_SIZE32:
case R_386_TLS_GOTDESC:
*(ul32 *)loc = val;
break;
default:
unreachable();
}
}
template <>
void write_plt_header(Context<E> &ctx, u8 *buf) {
if (ctx.arg.pic) {
static const u8 data[] = {
static const u8 insn[] = {
0xf3, 0x0f, 0x1e, 0xfb, // endbr32
0x51, // push %ecx
0x8d, 0x8b, 0, 0, 0, 0, // lea GOTPLT+4(%ebx), %ecx
0xff, 0x31, // push (%ecx)
0xff, 0x61, 0x04, // jmp *0x4(%ecx)
};
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 7) = ctx.gotplt->shdr.sh_addr - ctx.got->shdr.sh_addr + 4;
} else {
static const u8 insn[] = {
0xf3, 0x0f, 0x1e, 0xfb, // endbr32
0x51, // push %ecx
0xb9, 0, 0, 0, 0, // mov GOTPLT+4, %ecx
0xff, 0x31, // push (%ecx)
0xff, 0x61, 0x04, // jmp *0x4(%ecx)
0xcc, // (padding)
};
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 6) = ctx.gotplt->shdr.sh_addr + 4;
}
}
template <>
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
if (ctx.arg.pic) {
static const u8 insn[] = {
0xf3, 0x0f, 0x1e, 0xfb, // endbr32
0xb9, 0, 0, 0, 0, // mov $reloc_offset, %ecx
0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx)
0x68, 0, 0, 0, 0, // pushl $reloc_offset
0xe9, 0, 0, 0, 0, // jmp .PLT0@PC
0xcc, // (padding)
};
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = sym.get_gotplt_addr(ctx) - ctx.got->shdr.sh_addr;
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 5) = sym.get_plt_idx(ctx) * sizeof(ElfRel<E>);
*(ul32 *)(buf + 11) = sym.get_gotplt_addr(ctx) - ctx.got->shdr.sh_addr;
} else {
static const u8 data[] = {
static const u8 insn[] = {
0xf3, 0x0f, 0x1e, 0xfb, // endbr32
0xb9, 0, 0, 0, 0, // mov $reloc_offset, %ecx
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
0x68, 0, 0, 0, 0, // pushl $reloc_offset
0xe9, 0, 0, 0, 0, // jmp .PLT0@PC
0xcc, // (padding)
};
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = sym.get_gotplt_addr(ctx);
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 5) = sym.get_plt_idx(ctx) * sizeof(ElfRel<E>);
*(ul32 *)(buf + 11) = sym.get_gotplt_addr(ctx);
}
*(u32 *)(ent + 7) = idx * sizeof(ElfRel<E>);
*(u32 *)(ent + 12) = ctx.plt->shdr.sh_addr - sym.get_plt_addr(ctx) - 16;
}
template <>
void PltSection<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + this->shdr.sh_offset;
write_plt_header(ctx, buf);
for (i64 i = 0; i < symbols.size(); i++)
write_plt_entry(ctx, buf, *symbols[i], i);
}
template <>
void PltGotSection<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + this->shdr.sh_offset;
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
if (ctx.arg.pic) {
static const u8 data[] = {
0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx)
0x66, 0x90, // nop
static const u8 insn[] = {
0xf3, 0x0f, 0x1e, 0xfb, // endbr32
0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx)
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // (padding)
};
for (i64 i = 0; i < symbols.size(); i++) {
u8 *ent = buf + i * sizeof(data);
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = symbols[i]->get_got_addr(ctx) - ctx.got->shdr.sh_addr;
}
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 6) = sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr;
} else {
static const u8 data[] = {
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
0x66, 0x90, // nop
static const u8 insn[] = {
0xf3, 0x0f, 0x1e, 0xfb, // endbr32
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // (padding)
};
for (i64 i = 0; i < symbols.size(); i++) {
u8 *ent = buf + i * sizeof(data);
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = symbols[i]->get_got_addr(ctx);
}
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 6) = sym.get_got_addr(ctx);
}
}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
u64 offset, u64 val) {
void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_386_NONE:
return;
case R_NONE:
break;
case R_386_32:
*(u32 *)loc = val;
return;
*(ul32 *)loc = val;
break;
case R_386_PC32:
*(u32 *)loc = val - this->shdr.sh_addr - offset;
return;
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
break;
default:
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
}
static u32 relax_got32x(u8 *loc) {
// mov imm(%reg1), %reg2 -> lea imm(%reg1), %reg2
if (loc[0] == 0x8b)
return 0x8d00 | loc[1];
return 0;
}
// Relax GD to LE
static void relax_gd_to_le(u8 *loc, ElfRel<E> rel, u64 val) {
static const u8 insn[] = {
0x65, 0xa1, 0, 0, 0, 0, // mov %gs:0, %eax
0x81, 0xc0, 0, 0, 0, 0, // add $tp_offset, %eax
};
switch (rel.r_type) {
case R_386_PLT32:
case R_386_PC32:
memcpy(loc - 3, insn, sizeof(insn));
*(ul32 *)(loc + 5) = val;
break;
case R_386_GOT32:
case R_386_GOT32X:
memcpy(loc - 2, insn, sizeof(insn));
*(ul32 *)(loc + 6) = val;
break;
default:
unreachable();
}
}
// Relax LD to LE
static void relax_ld_to_le(u8 *loc, ElfRel<E> rel, u64 val) {
switch (rel.r_type) {
case R_386_PLT32:
case R_386_PC32: {
static const u8 insn[] = {
0x65, 0xa1, 0, 0, 0, 0, // mov %gs:0, %eax
0x2d, 0, 0, 0, 0, // sub $tls_size, %eax
};
memcpy(loc - 2, insn, sizeof(insn));
*(ul32 *)(loc + 5) = val;
break;
}
case R_386_GOT32:
case R_386_GOT32X: {
static const u8 insn[] = {
0x65, 0xa1, 0, 0, 0, 0, // mov %gs:0, %eax
0x2d, 0, 0, 0, 0, // sub $tls_size, %eax
0x90, // nop
};
memcpy(loc - 2, insn, sizeof(insn));
*(ul32 *)(loc + 5) = val;
break;
}
default:
unreachable();
}
unreachable();
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
ElfRel<E> *dynrel = nullptr;
std::span<ElfRel<E>> rels = get_rels(ctx);
i64 frag_idx = 0;
std::span<const ElfRel<E>> rels = get_rels(ctx);
ElfRel<E> *dynrel = nullptr;
if (ctx.reldyn)
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
file.reldyn_offset + this->reldyn_offset);
file.reldyn_offset + this->reldyn_offset);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_386_NONE)
if (rel.r_type == R_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
const SectionFragmentRef<E> *frag_ref = nullptr;
if (rel_fragments && rel_fragments[frag_idx].idx == i)
frag_ref = &rel_fragments[frag_idx++];
auto overflow_check = [&](i64 val, i64 lo, i64 hi) {
auto check = [&](i64 val, i64 lo, i64 hi) {
if (val < lo || hi <= val)
Error(ctx) << *this << ": relocation " << rel << " against "
<< sym << " out of range: " << val << " is not in ["
<< lo << ", " << hi << ")";
};
auto write8 = [&](u64 val) {
overflow_check(val, 0, 1 << 8);
*loc = val;
};
auto write8s = [&](u64 val) {
overflow_check(val, -(1 << 7), 1 << 7);
*loc = val;
};
auto write16 = [&](u64 val) {
overflow_check(val, 0, 1 << 16);
*(u16 *)loc = val;
};
auto write16s = [&](u64 val) {
overflow_check(val, -(1 << 15), 1 << 15);
*(u16 *)loc = val;
};
auto write32 = [&](u64 val) {
*(u32 *)loc = val;
};
#define S (frag_ref ? frag_ref->frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag_ref ? frag_ref->addend : this->get_addend(rel))
#define P (output_section->shdr.sh_addr + offset + rel.r_offset)
#define G (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
#define GOT ctx.got->shdr.sh_addr
u64 S = sym.get_addr(ctx);
u64 A = get_addend(*this, rel);
u64 P = get_addr() + rel.r_offset;
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
u64 GOT = ctx.got->shdr.sh_addr;
switch (rel.r_type) {
case R_386_8:
write8(S + A);
continue;
check(S + A, 0, 1 << 8);
*loc = S + A;
break;
case R_386_16:
write16(S + A);
continue;
check(S + A, 0, 1 << 16);
*(ul16 *)loc = S + A;
break;
case R_386_32:
if (sym.is_absolute() || !ctx.arg.pic) {
write32(S + A);
} else if (sym.is_imported) {
*dynrel++ = {P, R_386_32, (u32)sym.get_dynsym_idx(ctx)};
write32(A);
} else {
if (!is_relr_reloc(ctx, rel))
*dynrel++ = {P, R_386_RELATIVE, 0};
write32(S + A);
}
continue;
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_386_PC8:
write8s(S + A);
continue;
check(S + A - P, -(1 << 7), 1 << 7);
*loc = S + A - P;
break;
case R_386_PC16:
write16s(S + A);
continue;
check(S + A - P, -(1 << 15), 1 << 15);
*(ul16 *)loc = S + A - P;
break;
case R_386_PC32:
if (sym.is_absolute() || !sym.is_imported || !ctx.arg.shared) {
write32(S + A - P);
} else {
*dynrel++ = {P, R_386_32, (u32)sym.get_dynsym_idx(ctx)};
write32(A);
}
continue;
case R_386_PLT32:
write32(S + A - P);
continue;
*(ul32 *)loc = S + A - P;
break;
case R_386_GOT32:
*(ul32 *)loc = G + A;
break;
case R_386_GOT32X:
write32(G + A);
continue;
if (sym.has_got(ctx)) {
*(ul32 *)loc = G + A;
} else {
u32 insn = relax_got32x(loc - 2);
assert(insn);
loc[-2] = insn >> 8;
loc[-1] = insn;
*(ul32 *)loc = S + A - GOT;
}
break;
case R_386_GOTOFF:
write32(S + A - GOT);
continue;
*(ul32 *)loc = S + A - GOT;
break;
case R_386_GOTPC:
write32(GOT + A - P);
continue;
*(ul32 *)loc = GOT + A - P;
break;
case R_386_TLS_GOTIE:
write32(sym.get_gottp_addr(ctx) + A - GOT);
continue;
*(ul32 *)loc = sym.get_gottp_addr(ctx) + A - GOT;
break;
case R_386_TLS_LE:
write32(S + A - ctx.tls_end);
continue;
*(ul32 *)loc = S + A - ctx.tp_addr;
break;
case R_386_TLS_IE:
write32(sym.get_gottp_addr(ctx) + A);
continue;
*(ul32 *)loc = sym.get_gottp_addr(ctx) + A;
break;
case R_386_TLS_GD:
if (sym.get_tlsgd_idx(ctx) == -1) {
// Relax GD to LE
switch (rels[i + 1].r_type) {
case R_386_PLT32: {
static const u8 insn[] = {
0x65, 0xa1, 0, 0, 0, 0, // mov %gs:0, %rax
0x81, 0xe8, 0, 0, 0, 0, // add $0, %rax
};
memcpy(loc - 3, insn, sizeof(insn));
*(u32 *)(loc + 5) = ctx.tls_end - S - A;
break;
}
case R_386_GOT32: {
static const u8 insn[] = {
0x65, 0xa1, 0, 0, 0, 0, // mov %gs:0, %rax
0x81, 0xe8, 0, 0, 0, 0, // add $0, %rax
};
memcpy(loc - 2, insn, sizeof(insn));
*(u32 *)(loc + 6) = ctx.tls_end - S - A;
break;
}
default:
unreachable();
}
i++;
} else {
write32(sym.get_tlsgd_addr(ctx) + A - GOT);
}
continue;
case R_386_TLS_LDM:
if (ctx.got->tlsld_idx == -1) {
// Relax LD to LE
switch (rels[i + 1].r_type) {
case R_386_PLT32: {
static const u8 insn[] = {
0x65, 0xa1, 0, 0, 0, 0, // mov %gs:0, %eax
0x8d, 0x74, 0x26, 0x00, // lea (%esi,1), %esi
0x90, // nop
};
memcpy(loc - 2, insn, sizeof(insn));
break;
}
case R_386_GOT32: {
static const u8 insn[] = {
0x65, 0xa1, 0, 0, 0, 0, // mov %gs:0, %eax
0x8d, 0x74, 0x26, 0x00, // lea (%esi,1), %esi
0x66, 0x90,
};
memcpy(loc - 2, insn, sizeof(insn));
break;
}
default:
unreachable();
}
i++;
} else {
write32(ctx.got->get_tlsld_addr(ctx) + A - GOT);
}
continue;
case R_386_TLS_LDO_32:
if (ctx.got->tlsld_idx == -1)
write32(S + A - ctx.tls_end);
if (sym.has_tlsgd(ctx))
*(ul32 *)loc = sym.get_tlsgd_addr(ctx) + A - GOT;
else
write32(S + A - ctx.tls_begin);
continue;
relax_gd_to_le(loc, rels[++i], S - ctx.tp_addr);
break;
case R_386_TLS_LDM:
if (ctx.got->has_tlsld(ctx))
*(ul32 *)loc = ctx.got->get_tlsld_addr(ctx) + A - GOT;
else
relax_ld_to_le(loc, rels[++i], ctx.tp_addr - ctx.tls_begin);
break;
case R_386_TLS_LDO_32:
*(ul32 *)loc = S + A - ctx.dtp_addr;
break;
case R_386_SIZE32:
write32(sym.esym().st_size + A);
continue;
*(ul32 *)loc = sym.esym().st_size + A;
break;
case R_386_TLS_GOTDESC:
if (sym.get_tlsdesc_idx(ctx) == -1) {
static const u8 insn[] = {
0x8d, 0x05, 0, 0, 0, 0, // lea 0, %eax
};
memcpy(loc - 2, insn, sizeof(insn));
write32(S + A - ctx.tls_end);
// i386 TLSDESC uses the following code sequence to materialize
// a TP-relative address in %eax.
//
// lea 0(%ebx), %eax
// R_386_TLS_GOTDESC foo
// call *(%eax)
// R_386_TLS_DESC_CALL foo
//
// We may relax the instructions to the following for non-dlopen'd DSO
//
// mov foo@GOTTPOFF(%ebx), %eax
// nop
//
// or to the following for executable.
//
// mov $foo@TPOFF, %eax
// nop
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;
*(ul32 *)loc = sym.get_gottp_addr(ctx) + A - GOT;
} else {
write32(sym.get_tlsdesc_addr(ctx) + A - GOT);
loc[-2] = 0x90;
loc[-1] = 0xb8;
*(ul32 *)loc = S + A - ctx.tp_addr;
}
continue;
break;
case R_386_TLS_DESC_CALL:
if (ctx.arg.relax && !ctx.arg.shared) {
// call *(%rax) -> nop
if (!sym.has_tlsdesc(ctx)) {
// call *(%eax) -> nop
loc[0] = 0x66;
loc[1] = 0x90;
}
continue;
break;
default:
unreachable();
}
#undef S
#undef A
#undef P
#undef G
#undef GOT
}
}
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_386_NONE)
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
if (!sym.file) {
report_undef(ctx, file, sym);
continue;
}
SectionFragment<E> *frag;
i64 addend;
std::tie(frag, addend) = get_fragment(ctx, rel);
auto overflow_check = [&](i64 val, i64 lo, i64 hi) {
auto check = [&](i64 val, i64 lo, i64 hi) {
if (val < lo || hi <= val)
Error(ctx) << *this << ": relocation " << rel << " against "
<< sym << " out of range: " << val << " is not in ["
<< lo << ", " << hi << ")";
};
auto write8 = [&](u64 val) {
overflow_check(val, 0, 1 << 8);
*loc = val;
};
SectionFragment<E> *frag;
i64 frag_addend;
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
auto write8s = [&](u64 val) {
overflow_check(val, -(1 << 7), 1 << 7);
*loc = val;
};
auto write16 = [&](u64 val) {
overflow_check(val, 0, 1 << 16);
*(u16 *)loc = val;
};
auto write16s = [&](u64 val) {
overflow_check(val, -(1 << 15), 1 << 15);
*(u16 *)loc = val;
};
#define S (frag ? frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag ? addend : this->get_addend(rel))
#define G (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
#define GOT ctx.got->shdr.sh_addr
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
u64 A = frag ? frag_addend : get_addend(*this, rel);
u64 GOT = ctx.got->shdr.sh_addr;
switch (rel.r_type) {
case R_386_8:
write8(S + A);
continue;
check(S + A, 0, 1 << 8);
*loc = S + A;
break;
case R_386_16:
write16(S + A);
continue;
check(S + A, 0, 1 << 16);
*(ul16 *)loc = S + A;
break;
case R_386_32:
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ul32 *)loc = *val;
else
*(u32 *)loc = S + A;
continue;
*(ul32 *)loc = S + A;
break;
case R_386_PC8:
write8s(S + A);
continue;
check(S + A, -(1 << 7), 1 << 7);
*loc = S + A;
break;
case R_386_PC16:
write16s(S + A);
continue;
check(S + A, -(1 << 15), 1 << 15);
*(ul16 *)loc = S + A;
break;
case R_386_PC32:
*(u32 *)loc = S + A;
continue;
*(ul32 *)loc = S + A;
break;
case R_386_GOTPC:
*(u32 *)loc = GOT + A;
continue;
*(ul32 *)loc = GOT + A;
break;
case R_386_GOTOFF:
*(u32 *)loc = S + A - GOT;
continue;
*(ul32 *)loc = S + A - GOT;
break;
case R_386_TLS_LDO_32:
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ul32 *)loc = *val;
else
*(u32 *)loc = S + A - ctx.tls_begin;
continue;
*(ul32 *)loc = S + A - ctx.dtp_addr;
break;
case R_386_SIZE32:
*(u32 *)loc = sym.esym().st_size + A;
continue;
*(ul32 *)loc = sym.esym().st_size + A;
break;
default:
unreachable();
}
#undef S
#undef A
#undef GOT
}
}
@@ -428,118 +477,86 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_386_NONE)
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = (u8 *)(contents.data() + rel.r_offset);
if (!sym.file) {
report_undef(ctx, file, sym);
continue;
}
if (sym.is_ifunc())
sym.flags |= NEEDS_GOT | NEEDS_PLT;
if (sym.get_type() == STT_GNU_IFUNC) {
sym.flags |= NEEDS_GOT;
sym.flags |= NEEDS_PLT;
if (rel.r_type == R_386_TLS_GD || rel.r_type == R_386_TLS_LDM) {
if (i + 1 == rels.size())
Fatal(ctx) << *this << ": " << rel << " must be followed by PLT or GOT32";
if (u32 ty = rels[i + 1].r_type;
ty != R_386_PLT32 && ty != R_386_PC32 &&
ty != R_386_GOT32 && ty != R_386_GOT32X)
Fatal(ctx) << *this << ": " << rel << " must be followed by PLT or GOT32";
}
switch (rel.r_type) {
case R_386_8:
case R_386_16: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, ERROR, ERROR, ERROR }, // DSO
{ NONE, ERROR, ERROR, ERROR }, // PIE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
case R_386_16:
scan_absrel(ctx, sym, rel);
break;
}
case R_386_32: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, BASEREL, DYNREL, DYNREL }, // DSO
{ NONE, BASEREL, DYNREL, DYNREL }, // PIE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
case R_386_32:
scan_dyn_absrel(ctx, sym, rel);
break;
}
case R_386_PC8:
case R_386_PC16: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ ERROR, NONE, ERROR, ERROR }, // DSO
{ ERROR, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
case R_386_PC16:
case R_386_PC32:
scan_pcrel(ctx, sym, rel);
break;
}
case R_386_PC32: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ ERROR, NONE, DYNREL, DYNREL }, // DSO
{ ERROR, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
case R_386_GOT32:
case R_386_GOT32X:
case R_386_GOTPC:
sym.flags |= NEEDS_GOT;
break;
case R_386_PLT32: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, NONE, PLT, PLT }, // DSO
{ NONE, NONE, PLT, PLT }, // PIE
{ NONE, NONE, PLT, PLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
case R_386_GOT32X:
// We always want to relax GOT32X even if --no-relax is given
// because static PIE doesn't work without it.
if (sym.is_pcrel_linktime_const(ctx) && relax_got32x(loc - 2)) {
// Do nothing
} else {
sym.flags |= NEEDS_GOT;
}
break;
case R_386_PLT32:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
}
case R_386_TLS_GOTIE:
case R_386_TLS_LE:
case R_386_TLS_IE:
sym.flags |= NEEDS_GOTTP;
break;
case R_386_TLS_GD:
if (i + 1 == rels.size())
Fatal(ctx) << *this << ": TLS_GD reloc must be followed by PLT or GOT32";
if (u32 ty = rels[i + 1].r_type;
ty != R_386_PLT32 && ty != R_386_GOT32)
Fatal(ctx) << *this << ": TLS_GD reloc must be followed by PLT or GOT32";
if (ctx.arg.relax && !ctx.arg.shared && !sym.is_imported)
// We always relax if -static because libc.a doesn't contain
// __tls_get_addr().
if ((ctx.arg.relax && sym.is_tprel_linktime_const(ctx)) ||
ctx.arg.is_static)
i++;
else
sym.flags |= NEEDS_TLSGD;
break;
case R_386_TLS_LDM:
if (i + 1 == rels.size())
Fatal(ctx) << *this << ": TLS_LDM reloc must be followed by PLT or GOT32";
if (u32 ty = rels[i + 1].r_type;
ty != R_386_PLT32 && ty != R_386_GOT32)
Fatal(ctx) << *this << ": TLS_LDM reloc must be followed by PLT or GOT32";
if (ctx.arg.relax && !ctx.arg.shared)
// We always relax if -static because libc.a doesn't contain
// __tls_get_addr().
if (ctx.arg.is_static || (ctx.arg.relax && !ctx.arg.shared))
i++;
else
ctx.needs_tlsld = true;
break;
case R_386_TLS_GOTDESC:
if (!ctx.arg.relax || ctx.arg.shared)
sym.flags |= NEEDS_TLSDESC;
scan_tlsdesc(ctx, sym);
break;
case R_386_TLS_LE:
check_tlsle(ctx, sym, rel);
break;
case R_386_GOTOFF:
case R_386_TLS_LDO_32:
+686
View File
@@ -0,0 +1,686 @@
// LoongArch is a new RISC ISA announced in 2021 by Loongson. The ISA
// feels like a modernized MIPS with a hint of RISC-V flavor, although
// it's not compatible with either one.
//
// While LoongArch is a fresh and clean ISA, its technological advantage
// over other modern RISC ISAs such as RISC-V doesn't seem to be very
// significant. It appears that the real selling point of LoongArch is
// that the ISA is developed and controlled by a Chinese company,
// reflecting a desire for domestic CPUs. Loongson is actively working on
// bootstrapping the entire ecosystem for LoongArch, sending patches to
// Linux, GCC, LLVM, etc.
//
// All instructions are 4 bytes long in LoongArch and aligned to 4-byte
// boundaries. It has 32 general-purpose registers. Among these, $t0 - $t8
// (aliases for $r12 - $r20) are temporary registers that we can use in
// our PLT and range extension thunks.
//
// The psABI defines a few linker relaxations. We haven't supported them
// yet.
//
// https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html
#if MOLD_LOONGARCH64 || MOLD_LOONGARCH32
#include "mold.h"
namespace mold::elf {
using E = MOLD_TARGET;
static u64 page(u64 val) {
return val & 0xffff'ffff'ffff'f000;
}
static u64 hi20(u64 val, u64 pc) {
// A PC-relative address with a 32 bit offset is materialized in a
// register with the following instructions:
//
// pcalau12i $rN, %pc_hi20(sym)
// addi.d $rN, $rN, %lo12(sym)
//
// PCALAU12I materializes bits [63:12] by computing (pc + imm << 12)
// and zero-clear [11:0]. ADDI.D sign-extends its 12 bit immediate and
// add it to the register. To compensate the sign-extension, PCALAU12I
// needs to materialize a 0x1000 larger value than the desired [63:12]
// if [11:0] is sign-extended.
//
// This is similar but different from RISC-V because RISC-V's AUIPC
// doesn't zero-clear [11:0].
return bits(page(val + 0x800) - page(pc), 31, 12);
}
static u64 hi64(u64 val, u64 pc) {
// A PC-relative 64-bit address is materialized with the following
// instructions for the large code model:
//
// pcalau12i $rN, %pc_hi20(sym)
// addi.d $rM, $zero, %lo12(sym)
// lu32i.d $rM, %pc64_lo20(sym)
// lu52i.d $rM, $r12, %pc64_hi12(sym)
// add.d $rN, $rN, $rM
//
// PCALAU12I computes (pc + imm << 12) to materialize a 64-bit value.
// ADDI.D adds a sign-extended 12 bit value to a register. LU32I.D and
// LU52I.D simply set bits to [51:31] and to [63:53], respectively.
//
// Compensating all the sign-extensions is a bit complicated.
bool x = val & 0x800;
bool y = (page(val + 0x800) - page(pc)) & 0x8000'0000;
if (x && !y)
return val - 0x1'0000'0000;
if (!x && y)
return val + 0x1'0000'0000;
return val;
}
static u64 higher20(u64 val, u64 pc) {
return bits(hi64(val, pc), 51, 32);
}
static u64 highest12(u64 val, u64 pc) {
return bits(hi64(val, pc), 63, 52);
}
static void write_k12(u8 *loc, u32 val) {
// opcode, [11:0], rj, rd
*(ul32 *)loc &= 0b1111111111'000000000000'11111'11111;
*(ul32 *)loc |= bits(val, 11, 0) << 10;
}
static void write_k16(u8 *loc, u32 val) {
// opcode, [15:0], rj, rd
*(ul32 *)loc &= 0b111111'0000000000000000'11111'11111;
*(ul32 *)loc |= bits(val, 15, 0) << 10;
}
static void write_j20(u8 *loc, u32 val) {
// opcode, [19:0], rd
*(ul32 *)loc &= 0b1111111'00000000000000000000'11111;
*(ul32 *)loc |= bits(val, 19, 0) << 5;
}
static void write_d5k16(u8 *loc, u32 val) {
// opcode, [15:0], rj, [20:16]
*(ul32 *)loc &= 0b111111'0000000000000000'11111'00000;
*(ul32 *)loc |= bits(val, 15, 0) << 10;
*(ul32 *)loc |= bits(val, 20, 16);
}
static void write_d10k16(u8 *loc, u32 val) {
// opcode, [15:0], [25:16]
*(ul32 *)loc &= 0b111111'0000000000000000'0000000000;
*(ul32 *)loc |= bits(val, 15, 0) << 10;
*(ul32 *)loc |= bits(val, 25, 16);
}
template <>
void write_plt_header<E>(Context<E> &ctx, u8 *buf) {
static const ul32 insn_64[] = {
0x1a00'000e, // pcalau12i $t2, %pc_hi20(.got.plt)
0x0011'bdad, // sub.d $t1, $t1, $t3
0x28c0'01cf, // ld.d $t3, $t2, %lo12(.got.plt) # _dl_runtime_resolve
0x02ff'51ad, // addi.d $t1, $t1, -44 # .plt entry
0x02c0'01cc, // addi.d $t0, $t2, %lo12(.got.plt) # &.got.plt
0x0045'05ad, // srli.d $t1, $t1, 1 # .plt entry offset
0x28c0'218c, // ld.d $t0, $t0, 8 # link map
0x4c00'01e0, // jr $t3
};
static const ul32 insn_32[] = {
0x1a00'000e, // pcalau12i $t2, %pc_hi20(.got.plt)
0x0011'3dad, // sub.w $t1, $t1, $t3
0x2880'01cf, // ld.w $t3, $t2, %lo12(.got.plt) # _dl_runtime_resolve
0x02bf'51ad, // addi.w $t1, $t1, -44 # .plt entry
0x0280'01cc, // addi.w $t0, $t2, %lo12(.got.plt) # &.got.plt
0x0044'89ad, // srli.w $t1, $t1, 2 # .plt entry offset
0x2880'118c, // ld.w $t0, $t0, 4 # link map
0x4c00'01e0, // jr $t3
};
u64 gotplt = ctx.gotplt->shdr.sh_addr;
u64 plt = ctx.plt->shdr.sh_addr;
memcpy(buf, E::is_64 ? insn_64 : insn_32, E::plt_hdr_size);
write_j20(buf, hi20(gotplt, plt));
write_k12(buf + 8, gotplt);
write_k12(buf + 16, gotplt);
}
static const ul32 plt_entry_64[] = {
0x1a00'000f, // pcalau12i $t3, %pc_hi20([email protected])
0x28c0'01ef, // ld.d $t3, $t3, %lo12([email protected])
0x4c00'01ed, // jirl $t1, $t3, 0
0x0340'0000, // nop
};
static const ul32 plt_entry_32[] = {
0x1a00'000f, // pcalau12i $t3, %pc_hi20([email protected])
0x2880'01ef, // ld.w $t3, $t3, %lo12([email protected])
0x4c00'01ed, // jirl $t1, $t3, 0
0x0340'0000, // nop
};
template <>
void write_plt_entry<E>(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
u64 gotplt = sym.get_gotplt_addr(ctx);
u64 plt = sym.get_plt_addr(ctx);
memcpy(buf, E::is_64 ? plt_entry_64 : plt_entry_32, E::plt_size);
write_j20(buf, hi20(gotplt, plt));
write_k12(buf + 4, gotplt);
}
template <>
void write_pltgot_entry<E>(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
u64 got = sym.get_got_addr(ctx);
u64 plt = sym.get_plt_addr(ctx);
memcpy(buf, E::is_64 ? plt_entry_64 : plt_entry_32, E::plt_size);
write_j20(buf, hi20(got, plt));
write_k12(buf + 4, got);
}
template <>
void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_NONE:
break;
case R_LARCH_ADD6:
*loc = (*loc & 0b1100'0000) | ((*loc + val) & 0b0011'1111);
break;
case R_LARCH_ADD8:
*loc += val;
break;
case R_LARCH_ADD16:
*(ul16 *)loc += val;
break;
case R_LARCH_ADD32:
*(ul32 *)loc += val;
break;
case R_LARCH_ADD64:
*(ul64 *)loc += val;
break;
case R_LARCH_SUB6:
*loc = (*loc & 0b1100'0000) | ((*loc - val) & 0b0011'1111);
break;
case R_LARCH_SUB8:
*loc -= val;
break;
case R_LARCH_SUB16:
*(ul16 *)loc -= val;
break;
case R_LARCH_SUB32:
*(ul32 *)loc -= val;
break;
case R_LARCH_SUB64:
*(ul64 *)loc -= val;
break;
case R_LARCH_32_PCREL:
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
break;
case R_LARCH_64_PCREL:
*(ul64 *)loc = val - this->shdr.sh_addr - offset;
break;
default:
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
ElfRel<E> *dynrel = nullptr;
if (ctx.reldyn)
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
file.reldyn_offset + this->reldyn_offset);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || rel.r_type == R_LARCH_RELAX ||
rel.r_type == R_LARCH_MARK_LA || rel.r_type == R_LARCH_MARK_PCREL ||
rel.r_type == R_LARCH_ALIGN)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
auto check = [&](i64 val, i64 lo, i64 hi) {
if (val < lo || hi <= val)
Error(ctx) << *this << ": relocation " << rel << " against "
<< sym << " out of range: " << val << " is not in ["
<< lo << ", " << hi << ")";
};
auto check_branch = [&](i64 val, i64 lo, i64 hi) {
if (val & 0b11)
Error(ctx) << *this << ": misaligned symbol " << sym
<< " for relocation " << rel;
check(val, lo, hi);
};
// Unlike other psABIs, the LoongArch ABI uses the same relocation
// types to refer to GOT entries for thread-local symbols and regular
// ones. Therefore, G may refer to a TLSGD or a regular GOT slot
// depending on the symbol type.
//
// Note that as of August 2023, both GCC and Clang treat TLSLD relocs
// as if they were TLSGD relocs for LoongArch, which is a clear bug.
// We need to handle TLSLD relocs as synonyms for TLSGD relocs for the
// sake of bug compatibility.
auto get_got_idx = [&] {
if (sym.has_tlsgd(ctx))
return sym.get_tlsgd_idx(ctx);
return sym.get_got_idx(ctx);
};
u64 S = sym.get_addr(ctx);
u64 A = rel.r_addend;
u64 P = get_addr() + rel.r_offset;
u64 G = get_got_idx() * sizeof(Word<E>);
u64 GOT = ctx.got->shdr.sh_addr;
switch (rel.r_type) {
case R_LARCH_32:
if constexpr (E::is_64)
*(ul32 *)loc = S + A;
else
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_LARCH_64:
assert(E::is_64);
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_LARCH_B16:
check_branch(S + A - P, -(1 << 17), 1 << 17);
write_k16(loc, (S + A - P) >> 2);
break;
case R_LARCH_B21:
check_branch(S + A - P, -(1 << 22), 1 << 22);
write_d5k16(loc, (S + A - P) >> 2);
break;
case R_LARCH_B26: {
i64 val = S + A - P;
if (val < -(1 << 27) || (1 << 27) <= val)
val = get_thunk_addr(i) + A - P;
write_d10k16(loc, val >> 2);
break;
}
case R_LARCH_ABS_LO12:
write_k12(loc, S + A);
break;
case R_LARCH_ABS_HI20:
write_j20(loc, (S + A) >> 12);
break;
case R_LARCH_ABS64_LO20:
write_j20(loc, (S + A) >> 32);
break;
case R_LARCH_ABS64_HI12:
write_k12(loc, (S + A) >> 52);
break;
case R_LARCH_PCALA_LO12:
write_k12(loc, S + A);
break;
case R_LARCH_PCALA_HI20:
check(S + A - P, -(1LL << 31), 1LL << 31);
write_j20(loc, hi20(S + A, P));
break;
case R_LARCH_PCALA64_LO20:
write_j20(loc, higher20(S + A, P));
break;
case R_LARCH_PCALA64_HI12:
write_k12(loc, highest12(S + A, P));
break;
case R_LARCH_GOT_PC_LO12:
write_k12(loc, GOT + G + A);
break;
case R_LARCH_GOT_PC_HI20:
check(GOT + G + A - P, -(1LL << 31), 1LL << 31);
write_j20(loc, hi20(GOT + G + A, P));
break;
case R_LARCH_GOT64_PC_LO20:
write_j20(loc, higher20(GOT + G + A, P));
break;
case R_LARCH_GOT64_PC_HI12:
write_k12(loc, highest12(GOT + G + A, P));
break;
case R_LARCH_GOT_LO12:
write_k12(loc, GOT + G + A);
break;
case R_LARCH_GOT_HI20:
write_j20(loc, (GOT + G + A) >> 12);
break;
case R_LARCH_GOT64_LO20:
write_j20(loc, (GOT + G + A) >> 32);
break;
case R_LARCH_GOT64_HI12:
write_k12(loc, (GOT + G + A) >> 52);
break;
case R_LARCH_TLS_LE_LO12:
write_k12(loc, S + A - ctx.tp_addr);
break;
case R_LARCH_TLS_LE_HI20:
write_j20(loc, (S + A - ctx.tp_addr) >> 12);
break;
case R_LARCH_TLS_LE64_LO20:
write_j20(loc, (S + A - ctx.tp_addr) >> 32);
break;
case R_LARCH_TLS_LE64_HI12:
write_k12(loc, (S + A - ctx.tp_addr) >> 52);
break;
case R_LARCH_TLS_IE_PC_LO12:
write_k12(loc, sym.get_gottp_addr(ctx) + A);
break;
case R_LARCH_TLS_IE_PC_HI20:
check(sym.get_gottp_addr(ctx) + A - P, -(1LL << 31), 1LL << 31);
write_j20(loc, hi20(sym.get_gottp_addr(ctx) + A, P));
break;
case R_LARCH_TLS_IE64_PC_LO20:
write_j20(loc, higher20(sym.get_gottp_addr(ctx) + A, P));
break;
case R_LARCH_TLS_IE64_PC_HI12:
write_k12(loc, highest12(sym.get_gottp_addr(ctx) + A, P));
break;
case R_LARCH_TLS_IE_LO12:
write_k12(loc, sym.get_gottp_addr(ctx) + A);
break;
case R_LARCH_TLS_IE_HI20:
write_j20(loc, (sym.get_gottp_addr(ctx) + A) >> 12);
break;
case R_LARCH_TLS_IE64_LO20:
write_j20(loc, (sym.get_gottp_addr(ctx) + A) >> 32);
break;
case R_LARCH_TLS_IE64_HI12:
write_k12(loc, (sym.get_gottp_addr(ctx) + A) >> 52);
break;
case R_LARCH_TLS_LD_PC_HI20:
case R_LARCH_TLS_GD_PC_HI20:
check(sym.get_tlsgd_addr(ctx) + A - P, -(1LL << 31), 1LL << 31);
write_j20(loc, hi20(sym.get_tlsgd_addr(ctx) + A, P));
break;
case R_LARCH_TLS_LD_HI20:
case R_LARCH_TLS_GD_HI20:
write_j20(loc, (sym.get_tlsgd_addr(ctx) + A) >> 12);
break;
case R_LARCH_ADD6:
*loc = (*loc & 0b1100'0000) | ((*loc + S + A) & 0b0011'1111);
break;
case R_LARCH_ADD8:
*loc += S + A;
break;
case R_LARCH_ADD16:
*(ul16 *)loc += S + A;
break;
case R_LARCH_ADD32:
*(ul32 *)loc += S + A;
break;
case R_LARCH_ADD64:
*(ul64 *)loc += S + A;
break;
case R_LARCH_SUB6:
*loc = (*loc & 0b1100'0000) | ((*loc - S - A) & 0b0011'1111);
break;
case R_LARCH_SUB8:
*loc -= S + A;
break;
case R_LARCH_SUB16:
*(ul16 *)loc -= S + A;
break;
case R_LARCH_SUB32:
*(ul32 *)loc -= S + A;
break;
case R_LARCH_SUB64:
*(ul64 *)loc -= S + A;
break;
case R_LARCH_32_PCREL:
*(ul32 *)loc = S + A - P;
break;
case R_LARCH_64_PCREL:
*(ul64 *)loc = S + A - P;
break;
case R_LARCH_ADD_ULEB128:
overwrite_uleb(loc, read_uleb(loc) + S + A);
break;
case R_LARCH_SUB_ULEB128:
overwrite_uleb(loc, read_uleb(loc) - S - A);
break;
default:
unreachable();
}
}
}
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
if (!sym.file) {
record_undef_error(ctx, rel);
continue;
}
SectionFragment<E> *frag;
i64 frag_addend;
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
u64 A = frag ? frag_addend : (i64)rel.r_addend;
switch (rel.r_type) {
case R_LARCH_32:
*(ul32 *)loc = S + A;
break;
case R_LARCH_64:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ul64 *)loc = *val;
else
*(ul64 *)loc = S + A;
break;
case R_LARCH_ADD6:
*loc = (*loc & 0b1100'0000) | ((*loc + S + A) & 0b0011'1111);
break;
case R_LARCH_ADD8:
*loc += S + A;
break;
case R_LARCH_ADD16:
*(ul16 *)loc += S + A;
break;
case R_LARCH_ADD32:
*(ul32 *)loc += S + A;
break;
case R_LARCH_ADD64:
*(ul64 *)loc += S + A;
break;
case R_LARCH_SUB6:
*loc = (*loc & 0b1100'0000) | ((*loc - S - A) & 0b0011'1111);
break;
case R_LARCH_SUB8:
*loc -= S + A;
break;
case R_LARCH_SUB16:
*(ul16 *)loc -= S + A;
break;
case R_LARCH_SUB32:
*(ul32 *)loc -= S + A;
break;
case R_LARCH_SUB64:
*(ul64 *)loc -= S + A;
break;
case R_LARCH_TLS_DTPREL32:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ul32 *)loc = *val;
else
*(ul32 *)loc = S + A - ctx.dtp_addr;
break;
case R_LARCH_TLS_DTPREL64:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ul64 *)loc = *val;
else
*(ul64 *)loc = S + A - ctx.dtp_addr;
break;
case R_LARCH_ADD_ULEB128:
overwrite_uleb(loc, read_uleb(loc) + S + A);
break;
case R_LARCH_SUB_ULEB128:
overwrite_uleb(loc, read_uleb(loc) - S - A);
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
<< rel;
break;
}
}
}
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<const ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || rel.r_type == R_LARCH_RELAX ||
rel.r_type == R_LARCH_MARK_LA || rel.r_type == R_LARCH_MARK_PCREL ||
rel.r_type == R_LARCH_ALIGN)
continue;
if (record_undef_error(ctx, rel))
continue;
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;
switch (rel.r_type) {
case R_LARCH_32:
if constexpr (E::is_64)
scan_absrel(ctx, sym, rel);
else
scan_dyn_absrel(ctx, sym, rel);
break;
case R_LARCH_64:
assert(E::is_64);
scan_dyn_absrel(ctx, sym, rel);
break;
case R_LARCH_B26:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_LARCH_GOT_HI20:
case R_LARCH_GOT_PC_HI20:
sym.flags |= NEEDS_GOT;
break;
case R_LARCH_TLS_IE_HI20:
case R_LARCH_TLS_IE_PC_HI20:
sym.flags |= NEEDS_GOTTP;
break;
case R_LARCH_TLS_LD_PC_HI20:
case R_LARCH_TLS_GD_PC_HI20:
case R_LARCH_TLS_LD_HI20:
case R_LARCH_TLS_GD_HI20:
sym.flags |= NEEDS_TLSGD;
break;
case R_LARCH_32_PCREL:
case R_LARCH_64_PCREL:
scan_pcrel(ctx, sym, rel);
break;
case R_LARCH_TLS_LE_HI20:
case R_LARCH_TLS_LE_LO12:
case R_LARCH_TLS_LE64_LO20:
case R_LARCH_TLS_LE64_HI12:
check_tlsle(ctx, sym, rel);
break;
case R_LARCH_B16:
case R_LARCH_B21:
case R_LARCH_ABS_HI20:
case R_LARCH_ABS_LO12:
case R_LARCH_ABS64_LO20:
case R_LARCH_ABS64_HI12:
case R_LARCH_PCALA_HI20:
case R_LARCH_PCALA_LO12:
case R_LARCH_PCALA64_LO20:
case R_LARCH_PCALA64_HI12:
case R_LARCH_GOT_PC_LO12:
case R_LARCH_GOT64_PC_LO20:
case R_LARCH_GOT64_PC_HI12:
case R_LARCH_GOT_LO12:
case R_LARCH_GOT64_LO20:
case R_LARCH_GOT64_HI12:
case R_LARCH_TLS_IE_PC_LO12:
case R_LARCH_TLS_IE64_PC_LO20:
case R_LARCH_TLS_IE64_PC_HI12:
case R_LARCH_TLS_IE_LO12:
case R_LARCH_TLS_IE64_LO20:
case R_LARCH_TLS_IE64_HI12:
case R_LARCH_ADD6:
case R_LARCH_SUB6:
case R_LARCH_ADD8:
case R_LARCH_SUB8:
case R_LARCH_ADD16:
case R_LARCH_SUB16:
case R_LARCH_ADD32:
case R_LARCH_SUB32:
case R_LARCH_ADD64:
case R_LARCH_SUB64:
case R_LARCH_ADD_ULEB128:
case R_LARCH_SUB_ULEB128:
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
template <>
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
static const ul32 insn[] = {
0x1a00'000c, // pcalau12i $t0, 0
0x02c0'018c, // addi.d $t0, $t0, 0
0x4c00'0180, // jirl $zero, $t0, 0
0x0340'0000, // nop
};
static_assert(E::thunk_size == sizeof(insn));
u8 *buf = ctx.buf + output_section.shdr.sh_offset + offset;
u64 P = output_section.shdr.sh_addr + offset;
for (Symbol<E> *sym : symbols) {
u64 S = sym->get_addr(ctx);
memcpy(buf, insn, sizeof(insn));
write_j20(buf, hi20(S, P));
write_k12(buf + 4, S);
buf += sizeof(insn);
P += sizeof(insn);
}
}
} // namespace mold::elf
#endif
+325
View File
@@ -0,0 +1,325 @@
// This file contains code for the Motorola 68000 series microprocessors,
// which is often abbreviated as m68k. Running a Unix-like system on a
// m68k-based machine today is probably a retro-computing hobby activity,
// but the processor was a popular choice to build Unix computers during
// '80s. Early Sun workstations for example used m68k. Macintosh until
// 1994 were based on m68k as well until they switched to PowerPC (and
// then to x86 and to ARM.)
//
// From the linker's point of view, it is not hard to support m68k. It's
// just a 32-bit big-endian CISC ISA. Compared to comtemporary i386,
// m68k's psABI is actually simpler because m68k has PC-relative memory
// access instructions and therefore can support position-independent
// code without too much hassle.
//
// https://github.com/rui314/psabi/blob/main/m68k.pdf
#include "mold.h"
namespace mold::elf {
using E = M68K;
template <>
void write_plt_header(Context<E> &ctx, u8 *buf) {
static const u8 insn[] = {
0x2f, 0x00, // move.l %d0, -(%sp)
0x2f, 0x3b, 0x01, 0x70, 0, 0, 0, 0, // move.l (GOTPLT+4, %pc), -(%sp)
0x4e, 0xfb, 0x01, 0x71, 0, 0, 0, 0, // jmp ([GOTPLT+8, %pc])
};
memcpy(buf, insn, sizeof(insn));
*(ub32 *)(buf + 6) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr;
*(ub32 *)(buf + 14) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 4;
}
template <>
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
static const u8 insn[] = {
0x20, 0x3c, 0, 0, 0, 0, // move.l PLT_OFFSET, %d0
0x4e, 0xfb, 0x01, 0x71, 0, 0, 0, 0, // jmp ([GOTPLT_ENTRY, %pc])
};
memcpy(buf, insn, sizeof(insn));
*(ub32 *)(buf + 2) = sym.get_plt_idx(ctx) * sizeof(ElfRel<E>);
*(ub32 *)(buf + 10) = sym.get_gotplt_addr(ctx) - sym.get_plt_addr(ctx) - 8;
}
template <>
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
static const u8 insn[] = {
0x4e, 0xfb, 0x01, 0x71, 0, 0, 0, 0, // jmp ([GOT_ENTRY, %pc])
};
memcpy(buf, insn, sizeof(insn));
*(ub32 *)(buf + 4) = sym.get_got_addr(ctx) - sym.get_plt_addr(ctx) - 2;
}
template <>
void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_NONE:
break;
case R_68K_32:
*(ub32 *)loc = val;
break;
case R_68K_PC32:
*(ub32 *)loc = val - this->shdr.sh_addr - offset;
break;
default:
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
ElfRel<E> *dynrel = nullptr;
if (ctx.reldyn)
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
file.reldyn_offset + this->reldyn_offset);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
auto check = [&](i64 val, i64 lo, i64 hi) {
if (val < lo || hi <= val)
Error(ctx) << *this << ": relocation " << rel << " against "
<< sym << " out of range: " << val << " is not in ["
<< lo << ", " << hi << ")";
};
auto write16 = [&](u64 val) {
check(val, 0, 1 << 16);
*(ub16 *)loc = val;
};
auto write16s = [&](u64 val) {
check(val, -(1 << 15), 1 << 15);
*(ub16 *)loc = val;
};
auto write8 = [&](u64 val) {
check(val, 0, 1 << 8);
*loc = val;
};
auto write8s = [&](u64 val) {
check(val, -(1 << 7), 1 << 7);
*loc = val;
};
u64 S = sym.get_addr(ctx);
u64 A = rel.r_addend;
u64 P = get_addr() + rel.r_offset;
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
u64 GOT = ctx.got->shdr.sh_addr;
switch (rel.r_type) {
case R_68K_32:
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_68K_16:
write16(S + A);
break;
case R_68K_8:
write8(S + A);
break;
case R_68K_PC32:
case R_68K_PLT32:
*(ub32 *)loc = S + A - P;
break;
case R_68K_PC16:
case R_68K_PLT16:
write16s(S + A - P);
break;
case R_68K_PC8:
case R_68K_PLT8:
write8s(S + A - P);
break;
case R_68K_GOTPCREL32:
*(ub32 *)loc = GOT + A - P;
break;
case R_68K_GOTPCREL16:
write16s(GOT + A - P);
break;
case R_68K_GOTPCREL8:
write8s(GOT + A - P);
break;
case R_68K_GOTOFF32:
*(ub32 *)loc = G + A;
break;
case R_68K_GOTOFF16:
write16(G + A);
break;
case R_68K_GOTOFF8:
write8(G + A);
break;
case R_68K_TLS_GD32:
*(ub32 *)loc = sym.get_tlsgd_addr(ctx) + A - GOT;
break;
case R_68K_TLS_GD16:
write16(sym.get_tlsgd_addr(ctx) + A - GOT);
break;
case R_68K_TLS_GD8:
write8(sym.get_tlsgd_addr(ctx) + A - GOT);
break;
case R_68K_TLS_LDM32:
*(ub32 *)loc = ctx.got->get_tlsld_addr(ctx) + A - GOT;
break;
case R_68K_TLS_LDM16:
write16(ctx.got->get_tlsld_addr(ctx) + A - GOT);
break;
case R_68K_TLS_LDM8:
write8(ctx.got->get_tlsld_addr(ctx) + A - GOT);
break;
case R_68K_TLS_LDO32:
*(ub32 *)loc = S + A - ctx.dtp_addr;
break;
case R_68K_TLS_LDO16:
write16s(S + A - ctx.dtp_addr);
break;
case R_68K_TLS_LDO8:
write8s(S + A - ctx.dtp_addr);
break;
case R_68K_TLS_IE32:
*(ub32 *)loc = sym.get_gottp_addr(ctx) + A - GOT;
break;
case R_68K_TLS_IE16:
write16(sym.get_gottp_addr(ctx) + A - GOT);
break;
case R_68K_TLS_IE8:
write8(sym.get_gottp_addr(ctx) + A - GOT);
break;
case R_68K_TLS_LE32:
*(ub32 *)loc = S + A - ctx.tp_addr;
break;
case R_68K_TLS_LE16:
write16(S + A - ctx.tp_addr);
break;
case R_68K_TLS_LE8:
write8(S + A - ctx.tp_addr);
break;
default:
unreachable();
}
}
}
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
SectionFragment<E> *frag;
i64 frag_addend;
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
u64 A = frag ? frag_addend : (i64)rel.r_addend;
switch (rel.r_type) {
case R_68K_32:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ub32 *)loc = *val;
else
*(ub32 *)loc = S + A;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
<< rel;
}
}
}
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (sym.is_ifunc())
Error(ctx) << sym << ": GNU ifunc symbol is not supported on m68k";
switch (rel.r_type) {
case R_68K_32:
scan_dyn_absrel(ctx, sym, rel);
break;
case R_68K_16:
case R_68K_8:
scan_absrel(ctx, sym, rel);
break;
case R_68K_PC32:
case R_68K_PC16:
case R_68K_PC8:
scan_pcrel(ctx, sym, rel);
break;
case R_68K_GOTPCREL32:
case R_68K_GOTPCREL16:
case R_68K_GOTPCREL8:
case R_68K_GOTOFF32:
case R_68K_GOTOFF16:
case R_68K_GOTOFF8:
sym.flags |= NEEDS_GOT;
break;
case R_68K_PLT32:
case R_68K_PLT16:
case R_68K_PLT8:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_68K_TLS_GD32:
case R_68K_TLS_GD16:
case R_68K_TLS_GD8:
sym.flags |= NEEDS_TLSGD;
break;
case R_68K_TLS_LDM32:
case R_68K_TLS_LDM16:
case R_68K_TLS_LDM8:
ctx.needs_tlsld = true;
break;
case R_68K_TLS_IE32:
case R_68K_TLS_IE16:
case R_68K_TLS_IE8:
sym.flags |= NEEDS_GOTTP;
break;
case R_68K_TLS_LE32:
case R_68K_TLS_LE16:
case R_68K_TLS_LE8:
check_tlsle(ctx, sym, rel);
break;
case R_68K_TLS_LDO32:
case R_68K_TLS_LDO16:
case R_68K_TLS_LDO8:
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
} // namespace mold::elf
+453
View File
@@ -0,0 +1,453 @@
// This file implements the PowerPC 32-bit ISA. For 64-bit PowerPC, see
// arch-ppc64v1.cpp and arch-ppc64v2.cpp.
//
// PPC32 is a RISC ISA. It has 32 general-purpose registers (GPRs).
// r0, r11 and r12 are reserved for static linkers, so we can use these
// registers in PLTs and range extension thunks. In addition to that, it
// has a few special registers. Notable ones are LR which holds a return
// address and CTR which we can use to store a branch target address.
//
// It feels that the PPC32 psABI is unnecessarily complicated at first
// glance, but that is mainly stemmed from the fact that the ISA lacks
// PC-relative load/store instructions. Since machine instructions cannot
// load data relative to its own address, it is not straightforward to
// support position-independent code (PIC) on PPC32.
//
// A position-independent function typically contains the following code
// in the prologue to obtain its own address:
//
// mflr r0 // save the current return address to %r0
// bcl 20, 31, 4 // call the next instruction as if it were a function
// mtlr r12 // save the return address to %r12
// mtlr r0 // restore the original return address
//
// An object file compiled with -fPIC contains a data section named
// `.got2` to store addresses of locally-defined global variables and
// constants. A PIC function usually computes its .got2+0x8000 and set it
// to %r30. This scheme allows the function to access global objects
// defined in the same input file with a single %r30-relative load/store
// instruction with a 16-bit offset, given that .got2 is smaller than
// 0x10000 (or 65536) bytes.
//
// Since each object file has its own .got2, %r30 refers to different
// places in a merged .got2 for two functions that came from different
// input files. Therefore, %r30 makes sense only within a single function.
//
// Technically, we can reuse a %r30 value in our PLT if we create a PLT
// _for each input file_ (that's what GNU ld seems to be doing), but that
// doesn't seems to be worth its complexity. Our PLT simply doesn't rely
// on a %r30 value.
//
// https://github.com/rui314/psabi/blob/main/ppc32.pdf
#include "mold.h"
namespace mold::elf {
using E = PPC32;
static u64 lo(u64 x) { return x & 0xffff; }
static u64 hi(u64 x) { return x >> 16; }
static u64 ha(u64 x) { return (x + 0x8000) >> 16; }
static u64 high(u64 x) { return (x >> 16) & 0xffff; }
static u64 higha(u64 x) { return ((x + 0x8000) >> 16) & 0xffff; }
template <>
void write_plt_header(Context<E> &ctx, u8 *buf) {
static const ub32 insn[] = {
// Get the address of this PLT section
0x7c08'02a6, // mflr r0
0x429f'0005, // bcl 20, 31, 4
0x7d88'02a6, // 1: mflr r12
0x7c08'03a6, // mtlr r0
// Compute the runtime address of GOTPLT+12
0x3d8c'0000, // addis r12, r12, (GOTPLT - 1b)@higha
0x398c'0000, // addi r12, r12, (GOTPLT - 1b)@lo
// Compute the PLT entry offset
0x7d6c'5850, // sub r11, r11, r12
0x1d6b'0003, // mulli r11, r11, 3
// Load GOTPLT[2] and branch to GOTPLT[1]
0x800c'fff8, // lwz r0, -8(r12)
0x7c09'03a6, // mtctr r0
0x818c'fffc, // lwz r12, -4(r12)
0x4e80'0420, // bctr
0x6000'0000, // nop
0x6000'0000, // nop
0x6000'0000, // nop
0x6000'0000, // nop
};
static_assert(sizeof(insn) == E::plt_hdr_size);
memcpy(buf, insn, sizeof(insn));
ub32 *loc = (ub32 *)buf;
loc[4] |= higha(ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr + 4);
loc[5] |= lo(ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr + 4);
}
static const ub32 plt_entry[] = {
// Get the address of this PLT entry
0x7c08'02a6, // mflr r0
0x429f'0005, // bcl 20, 31, 4
0x7d88'02a6, // mflr r12
0x7c08'03a6, // mtlr r0
// Load an address from the GOT/GOTPLT entry and jump to that address
0x3d6c'0000, // addis r11, r12, OFFSET@higha
0x396b'0000, // addi r11, r11, OFFSET@lo
0x818b'0000, // lwz r12, 0(r11)
0x7d89'03a6, // mtctr r12
0x4e80'0420, // bctr
};
template <>
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
static_assert(E::plt_size == sizeof(plt_entry));
memcpy(buf, plt_entry, sizeof(plt_entry));
ub32 *loc = (ub32 *)buf;
i64 offset = sym.get_gotplt_addr(ctx) - sym.get_plt_addr(ctx) - 8;
loc[4] |= higha(offset);
loc[5] |= lo(offset);
}
template <>
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
static_assert(E::pltgot_size == sizeof(plt_entry));
memcpy(buf, plt_entry, sizeof(plt_entry));
ub32 *loc = (ub32 *)buf;
i64 offset = sym.get_got_addr(ctx) - sym.get_plt_addr(ctx) - 8;
loc[4] |= higha(offset);
loc[5] |= lo(offset);
}
template <>
void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_NONE:
break;
case R_PPC_ADDR32:
*(ub32 *)loc = val;
break;
case R_PPC_REL32:
*(ub32 *)loc = val - this->shdr.sh_addr - offset;
break;
default:
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
ElfRel<E> *dynrel = nullptr;
if (ctx.reldyn)
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
file.reldyn_offset + this->reldyn_offset);
u64 GOT2 = file.extra.got2 ? file.extra.got2->get_addr() : 0;
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
u64 S = sym.get_addr(ctx);
u64 A = rel.r_addend;
u64 P = get_addr() + rel.r_offset;
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
u64 GOT = ctx.got->shdr.sh_addr;
switch (rel.r_type) {
case R_PPC_ADDR32:
case R_PPC_UADDR32:
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_PPC_ADDR14:
*(ub32 *)loc |= bits(S + A, 15, 2) << 2;
break;
case R_PPC_ADDR16:
case R_PPC_UADDR16:
case R_PPC_ADDR16_LO:
*(ub16 *)loc = lo(S + A);
break;
case R_PPC_ADDR16_HI:
*(ub16 *)loc = hi(S + A);
break;
case R_PPC_ADDR16_HA:
*(ub16 *)loc = ha(S + A);
break;
case R_PPC_ADDR24:
*(ub32 *)loc |= bits(S + A, 25, 2) << 2;
break;
case R_PPC_ADDR30:
*(ub32 *)loc |= bits(S + A, 31, 2) << 2;
break;
case R_PPC_PLT16_LO:
*(ub16 *)loc = lo(G + GOT - A - GOT2);
break;
case R_PPC_PLT16_HI:
*(ub16 *)loc = hi(G + GOT - A - GOT2);
break;
case R_PPC_PLT16_HA:
*(ub16 *)loc = ha(G + GOT - A - GOT2);
break;
case R_PPC_PLT32:
*(ub32 *)loc = G + GOT - A - GOT2;
break;
case R_PPC_REL14:
*(ub32 *)loc |= bits(S + A - P, 15, 2) << 2;
break;
case R_PPC_REL16:
case R_PPC_REL16_LO:
*(ub16 *)loc = lo(S + A - P);
break;
case R_PPC_REL16_HI:
*(ub16 *)loc = hi(S + A - P);
break;
case R_PPC_REL16_HA:
*(ub16 *)loc = ha(S + A - P);
break;
case R_PPC_REL24:
case R_PPC_LOCAL24PC: {
i64 val = S + A - P;
if (sign_extend(val, 25) != val)
val = get_thunk_addr(i) - P;
*(ub32 *)loc |= bits(val, 25, 2) << 2;
break;
}
case R_PPC_PLTREL24: {
i64 val = S - P;
if (sym.has_plt(ctx) || sign_extend(val, 25) != val)
val = get_thunk_addr(i) - P;
*(ub32 *)loc |= bits(val, 25, 2) << 2;
break;
}
case R_PPC_REL32:
case R_PPC_PLTREL32:
*(ub32 *)loc = S + A - P;
break;
case R_PPC_GOT16:
case R_PPC_GOT16_LO:
*(ub16 *)loc = lo(G + A);
break;
case R_PPC_GOT16_HI:
*(ub16 *)loc = hi(G + A);
break;
case R_PPC_GOT16_HA:
*(ub16 *)loc = ha(G + A);
break;
case R_PPC_TPREL16_LO:
*(ub16 *)loc = lo(S + A - ctx.tp_addr);
break;
case R_PPC_TPREL16_HI:
*(ub16 *)loc = hi(S + A - ctx.tp_addr);
break;
case R_PPC_TPREL16_HA:
*(ub16 *)loc = ha(S + A - ctx.tp_addr);
break;
case R_PPC_DTPREL16_LO:
*(ub16 *)loc = lo(S + A - ctx.dtp_addr);
break;
case R_PPC_DTPREL16_HI:
*(ub16 *)loc = hi(S + A - ctx.dtp_addr);
break;
case R_PPC_DTPREL16_HA:
*(ub16 *)loc = ha(S + A - ctx.dtp_addr);
break;
case R_PPC_GOT_TLSGD16:
*(ub16 *)loc = sym.get_tlsgd_addr(ctx) - GOT;
break;
case R_PPC_GOT_TLSLD16:
*(ub16 *)loc = ctx.got->get_tlsld_addr(ctx) - GOT;
break;
case R_PPC_GOT_TPREL16:
*(ub16 *)loc = sym.get_gottp_addr(ctx) - GOT;
break;
case R_PPC_TLS:
case R_PPC_TLSGD:
case R_PPC_TLSLD:
case R_PPC_PLTSEQ:
case R_PPC_PLTCALL:
break;
default:
unreachable();
}
}
}
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
SectionFragment<E> *frag;
i64 frag_addend;
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
u64 A = frag ? frag_addend : (i64)rel.r_addend;
switch (rel.r_type) {
case R_PPC_ADDR32:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ub32 *)loc = *val;
else
*(ub32 *)loc = S + A;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
<< rel;
}
}
}
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<const ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (sym.is_ifunc())
sym.flags |= NEEDS_GOT | NEEDS_PLT;
switch (rel.r_type) {
case R_PPC_ADDR32:
case R_PPC_UADDR32:
scan_dyn_absrel(ctx, sym, rel);
break;
case R_PPC_ADDR14:
case R_PPC_ADDR16:
case R_PPC_UADDR16:
case R_PPC_ADDR16_LO:
case R_PPC_ADDR16_HI:
case R_PPC_ADDR16_HA:
case R_PPC_ADDR24:
case R_PPC_ADDR30:
scan_absrel(ctx, sym, rel);
break;
case R_PPC_REL14:
case R_PPC_REL16:
case R_PPC_REL16_LO:
case R_PPC_REL16_HI:
case R_PPC_REL16_HA:
case R_PPC_REL32:
scan_pcrel(ctx, sym, rel);
break;
case R_PPC_GOT16:
case R_PPC_GOT16_LO:
case R_PPC_GOT16_HI:
case R_PPC_GOT16_HA:
case R_PPC_PLT16_LO:
case R_PPC_PLT16_HI:
case R_PPC_PLT16_HA:
case R_PPC_PLT32:
sym.flags |= NEEDS_GOT;
break;
case R_PPC_REL24:
case R_PPC_PLTREL24:
case R_PPC_PLTREL32:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_PPC_GOT_TLSGD16:
sym.flags |= NEEDS_TLSGD;
break;
case R_PPC_GOT_TLSLD16:
ctx.needs_tlsld = true;
break;
case R_PPC_GOT_TPREL16:
sym.flags |= NEEDS_GOTTP;
break;
case R_PPC_TPREL16_LO:
case R_PPC_TPREL16_HI:
case R_PPC_TPREL16_HA:
check_tlsle(ctx, sym, rel);
break;
case R_PPC_LOCAL24PC:
case R_PPC_TLS:
case R_PPC_TLSGD:
case R_PPC_TLSLD:
case R_PPC_DTPREL16_LO:
case R_PPC_DTPREL16_HI:
case R_PPC_DTPREL16_HA:
case R_PPC_PLTSEQ:
case R_PPC_PLTCALL:
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
template <>
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
static const ub32 local_thunk[] = {
// Get this thunk's address
0x7c08'02a6, // mflr r0
0x429f'0005, // bcl 20, 31, 4
0x7d88'02a6, // mflr r12
0x7c08'03a6, // mtlr r0
// Materialize the destination's address in %r11 and jump to that address
0x3d6c'0000, // addis r11, r12, OFFSET@higha
0x396b'0000, // addi r11, r11, OFFSET@lo
0x7d69'03a6, // mtctr r11
0x4e80'0420, // bctr
0x6000'0000, // nop
};
static_assert(E::thunk_size == sizeof(plt_entry));
static_assert(E::thunk_size == sizeof(local_thunk));
u8 *buf = ctx.buf + output_section.shdr.sh_offset + offset;
u64 P = output_section.shdr.sh_addr + offset;
for (Symbol<E> *sym : symbols) {
if (sym->has_plt(ctx)) {
u64 got =
sym->has_got(ctx) ? sym->get_got_addr(ctx) : sym->get_gotplt_addr(ctx);
i64 val = got - P - 8;
memcpy(buf, plt_entry, sizeof(plt_entry));
*(ub32 *)(buf + 16) |= higha(val);
*(ub32 *)(buf + 20) |= lo(val);
} else {
i64 val = sym->get_addr(ctx) - P - 8;
memcpy(buf, local_thunk, sizeof(local_thunk));
*(ub32 *)(buf + 16) |= higha(val);
*(ub32 *)(buf + 20) |= lo(val);
}
buf += E::thunk_size;
P += E::thunk_size;
}
}
} // namespace mold::elf
+685
View File
@@ -0,0 +1,685 @@
// This file contains code for the 64-bit PowerPC ELFv1 ABI that is
// commonly used for big-endian PPC systems. Modern PPC systems that use
// the processor in the little-endian mode use the ELFv2 ABI instead. For
// ELFv2, see arch-ppc64v2.cc.
//
// Even though they are similiar, ELFv1 isn't only different from ELFv2 in
// endianness. The most notable difference is, in ELFv1, a function
// pointer doesn't directly refer to the entry point of a function but
// instead refers to a data structure so-called "function descriptor".
//
// The function descriptor is essentially a pair of a function entry point
// address and a value that should be set to %r2 before calling that
// function. There is also a third member for "the environment pointer for
// languages such as Pascal and PL/1" according to the psABI, but it looks
// like no one acutally uses it. In total, the function descriptor is 24
// bytes long. Here is why we need it.
//
// PPC generally lacks PC-relative data access instructions. Position-
// independent code sets GOT + 0x8000 to %r2 and access global variables
// relative to %r2.
//
// Each ELF file has its own GOT. If a function calls another function in
// the same ELF file, it doesn't have to reset %r2. However, if it is in
// other file (e.g. other .so), it has to set a new value to %r2 so that
// the register contains the callee's GOT + 0x8000.
//
// In this way, you can't call a function just by knowing the function's
// entry point address. You also need to know a proper %r2 value for the
// function. This is why a function pointer refers to a tuple of an
// address and a %r2 value.
//
// If a function call is made through PLT, PLT takes care of restoring %r2.
// Therefore, the caller has to restore %r2 only for function calls
// through function pointers.
//
// .opd (short for "official procedure descriptors") contains function
// descriptors.
//
// You can think OPD as this: even in other targets, a function can have a
// few different addresses for different purposes. It may not only have an
// entry point address but may also have PLT and/or GOT addresses.
// In PPCV1, it may have an OPD address in addition to these. OPD address
// is used for relocations that refers to the address of a function as a
// function pointer.
//
// https://github.com/rui314/psabi/blob/main/ppc64v1.pdf
#include "mold.h"
#include <algorithm>
#include <tbb/parallel_for_each.h>
namespace mold::elf {
using E = PPC64V1;
static u64 lo(u64 x) { return x & 0xffff; }
static u64 hi(u64 x) { return x >> 16; }
static u64 ha(u64 x) { return (x + 0x8000) >> 16; }
static u64 high(u64 x) { return (x >> 16) & 0xffff; }
static u64 higha(u64 x) { return ((x + 0x8000) >> 16) & 0xffff; }
// .plt is used only for lazy symbol resolution on PPC64. All PLT
// calls are made via range extension thunks even if they are within
// reach. Thunks read addresses from .got.plt and jump there.
// Therefore, once PLT symbols are resolved and final addresses are
// written to .got.plt, thunks just skip .plt and directly jump to the
// resolved addresses.
template <>
void write_plt_header(Context<E> &ctx, u8 *buf) {
static const ub32 insn[] = {
0x7d88'02a6, // mflr r12
0x429f'0005, // bcl 20, 31, 4 // obtain PC
0x7d68'02a6, // mflr r11
0x7d88'03a6, // mtlr r12
0x3d6b'0000, // addis r11, r11, GOTPLT_OFFSET@ha
0x396b'0000, // addi r11, r11, GOTPLT_OFFSET@lo
0xe98b'0000, // ld r12,0(r11)
0xe84b'0008, // ld r2,8(r11)
0x7d89'03a6, // mtctr r12
0xe96b'0010, // ld r11,16(r11)
0x4e80'0420, // bctr
};
static_assert(sizeof(insn) == E::plt_hdr_size);
memcpy(buf, insn, sizeof(insn));
i64 val = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 8;
*(ub32 *)(buf + 16) |= higha(val);
*(ub32 *)(buf + 20) |= lo(val);
}
template <>
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
ub32 *loc = (ub32 *)buf;
i64 idx = sym.get_plt_idx(ctx);
// The PPC64 ELFv1 ABI requires PLT entries to be vary in size depending
// on their indices. Unlike other targets, .got.plt is filled not by us
// but by the loader, so we don't have a control over where the initial
// call to the PLT entry jumps to. So we need to strictly follow the PLT
// section layout as the loader expect it to be.
if (idx < 0x8000) {
static const ub32 insn[] = {
0x3800'0000, // li r0, PLT_INDEX
0x4b00'0000, // b plt0
};
memcpy(loc, insn, sizeof(insn));
loc[0] |= idx;
loc[1] |= (ctx.plt->shdr.sh_addr - sym.get_plt_addr(ctx) - 4) & 0x00ff'ffff;
} else {
static const ub32 insn[] = {
0x3c00'0000, // lis r0, PLT_INDEX@high
0x6000'0000, // ori r0, r0, PLT_INDEX@lo
0x4b00'0000, // b plt0
};
memcpy(loc, insn, sizeof(insn));
loc[0] |= high(idx);
loc[1] |= lo(idx);
loc[2] |= (ctx.plt->shdr.sh_addr - sym.get_plt_addr(ctx) - 8) & 0x00ff'ffff;
}
}
// .plt.got is not necessary on PPC64 because range extension thunks
// directly read GOT entries and jump there.
template <>
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {}
template <>
void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_NONE:
break;
case R_PPC64_ADDR64:
*(ub64 *)loc = val;
break;
case R_PPC64_REL32:
*(ub32 *)loc = val - this->shdr.sh_addr - offset;
break;
case R_PPC64_REL64:
*(ub64 *)loc = val - this->shdr.sh_addr - offset;
break;
default:
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
ElfRel<E> *dynrel = nullptr;
if (ctx.reldyn)
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
file.reldyn_offset + this->reldyn_offset);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
auto check = [&](i64 val, i64 lo, i64 hi) {
if (val < lo || hi <= val)
Error(ctx) << *this << ": relocation " << rel << " against "
<< sym << " out of range: " << val << " is not in ["
<< lo << ", " << hi << ")";
};
u64 S = sym.get_addr(ctx);
u64 A = rel.r_addend;
u64 P = get_addr() + rel.r_offset;
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
u64 GOT = ctx.got->shdr.sh_addr;
u64 TOC = ctx.extra.TOC->value;
switch (rel.r_type) {
case R_PPC64_ADDR64:
apply_toc_rel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_PPC64_TOC:
apply_toc_rel(ctx, *ctx.extra.TOC, rel, loc, TOC, A, P, &dynrel);
break;
case R_PPC64_TOC16_HA:
*(ub16 *)loc = ha(S + A - TOC);
break;
case R_PPC64_TOC16_LO:
*(ub16 *)loc = lo(S + A - TOC);
break;
case R_PPC64_TOC16_DS:
check(S + A - TOC, -(1 << 15), 1 << 15);
*(ub16 *)loc |= (S + A - TOC) & 0xfffc;
break;
case R_PPC64_TOC16_LO_DS:
*(ub16 *)loc |= (S + A - TOC) & 0xfffc;
break;
case R_PPC64_REL24: {
i64 val = sym.get_addr(ctx, NO_OPD) + A - P;
if (sym.has_plt(ctx) || sign_extend(val, 25) != val)
val = get_thunk_addr(i) + A - P;
check(val, -(1 << 25), 1 << 25);
*(ub32 *)loc |= bits(val, 25, 2) << 2;
// If a callee is an external function, PLT saves %r2 to the
// caller's r2 save slot. We need to restore it after function
// return. To do so, there's usually a NOP as a placeholder
// after a BL. 0x6000'0000 is a NOP.
if (sym.has_plt(ctx) && *(ub32 *)(loc + 4) == 0x6000'0000)
*(ub32 *)(loc + 4) = 0xe841'0028; // ld r2, 40(r1)
break;
}
case R_PPC64_REL32:
*(ub32 *)loc = S + A - P;
break;
case R_PPC64_REL64:
*(ub64 *)loc = S + A - P;
break;
case R_PPC64_REL16_HA:
*(ub16 *)loc = ha(S + A - P);
break;
case R_PPC64_REL16_LO:
*(ub16 *)loc = lo(S + A - P);
break;
case R_PPC64_PLT16_HA:
*(ub16 *)loc = ha(G + GOT - TOC);
break;
case R_PPC64_PLT16_HI:
*(ub16 *)loc = hi(G + GOT - TOC);
break;
case R_PPC64_PLT16_LO:
*(ub16 *)loc = lo(G + GOT - TOC);
break;
case R_PPC64_PLT16_LO_DS:
*(ub16 *)loc |= (G + GOT - TOC) & 0xfffc;
break;
case R_PPC64_GOT_TPREL16_HA:
*(ub16 *)loc = ha(sym.get_gottp_addr(ctx) - TOC);
break;
case R_PPC64_GOT_TLSGD16_HA:
*(ub16 *)loc = ha(sym.get_tlsgd_addr(ctx) - TOC);
break;
case R_PPC64_GOT_TLSGD16_LO:
*(ub16 *)loc = lo(sym.get_tlsgd_addr(ctx) - TOC);
break;
case R_PPC64_GOT_TLSLD16_HA:
*(ub16 *)loc = ha(ctx.got->get_tlsld_addr(ctx) - TOC);
break;
case R_PPC64_GOT_TLSLD16_LO:
*(ub16 *)loc = lo(ctx.got->get_tlsld_addr(ctx) - TOC);
break;
case R_PPC64_DTPREL16_HA:
*(ub16 *)loc = ha(S + A - ctx.dtp_addr);
break;
case R_PPC64_DTPREL16_LO:
*(ub16 *)loc = lo(S + A - ctx.dtp_addr);
break;
case R_PPC64_TPREL16_HA:
*(ub16 *)loc = ha(S + A - ctx.tp_addr);
break;
case R_PPC64_TPREL16_LO:
*(ub16 *)loc = lo(S + A - ctx.tp_addr);
break;
case R_PPC64_GOT_TPREL16_LO_DS:
*(ub16 *)loc |= (sym.get_gottp_addr(ctx) - TOC) & 0xfffc;
break;
case R_PPC64_PLTSEQ:
case R_PPC64_PLTCALL:
case R_PPC64_TLS:
case R_PPC64_TLSGD:
case R_PPC64_TLSLD:
break;
default:
unreachable();
}
}
}
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
auto check = [&](i64 val, i64 lo, i64 hi) {
if (val < lo || hi <= val)
Error(ctx) << *this << ": relocation " << rel << " against "
<< sym << " out of range: " << val << " is not in ["
<< lo << ", " << hi << ")";
};
SectionFragment<E> *frag;
i64 frag_addend;
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
u64 A = frag ? frag_addend : (i64)rel.r_addend;
switch (rel.r_type) {
case R_PPC64_ADDR64:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ub64 *)loc = *val;
else
*(ub64 *)loc = S + A;
break;
case R_PPC64_ADDR32: {
i64 val = S + A;
check(val, 0, 1LL << 32);
*(ub32 *)loc = val;
break;
}
case R_PPC64_DTPREL64:
*(ub64 *)loc = S + A - ctx.dtp_addr;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
<< rel;
}
}
}
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<const ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (sym.is_ifunc())
sym.flags |= NEEDS_GOT | NEEDS_PLT | NEEDS_PPC_OPD;
// Any relocation except R_PPC64_REL24 is considered as an
// address-taking relocation.
if (rel.r_type != R_PPC64_REL24 && sym.get_type() == STT_FUNC)
sym.flags |= NEEDS_PPC_OPD;
switch (rel.r_type) {
case R_PPC64_ADDR64:
case R_PPC64_TOC:
scan_toc_rel(ctx, sym, rel);
break;
case R_PPC64_GOT_TPREL16_HA:
sym.flags |= NEEDS_GOTTP;
break;
case R_PPC64_REL24:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_PPC64_PLT16_HA:
sym.flags |= NEEDS_GOT;
break;
case R_PPC64_GOT_TLSGD16_HA:
sym.flags |= NEEDS_TLSGD;
break;
case R_PPC64_GOT_TLSLD16_HA:
ctx.needs_tlsld = true;
break;
case R_PPC64_TPREL16_HA:
case R_PPC64_TPREL16_LO:
check_tlsle(ctx, sym, rel);
break;
case R_PPC64_REL32:
case R_PPC64_REL64:
case R_PPC64_TOC16_HA:
case R_PPC64_TOC16_LO:
case R_PPC64_TOC16_LO_DS:
case R_PPC64_TOC16_DS:
case R_PPC64_REL16_HA:
case R_PPC64_REL16_LO:
case R_PPC64_PLT16_HI:
case R_PPC64_PLT16_LO:
case R_PPC64_PLT16_LO_DS:
case R_PPC64_PLTSEQ:
case R_PPC64_PLTCALL:
case R_PPC64_GOT_TPREL16_LO_DS:
case R_PPC64_GOT_TLSGD16_LO:
case R_PPC64_GOT_TLSLD16_LO:
case R_PPC64_TLS:
case R_PPC64_TLSGD:
case R_PPC64_TLSLD:
case R_PPC64_DTPREL16_HA:
case R_PPC64_DTPREL16_LO:
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
template <>
void RangeExtensionThunk<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.
static const ub32 pltgot_thunk[] = {
// Store the caller's %r2
0xf841'0028, // std %r2, 40(%r1)
// Load an address of a function descriptor
0x3d82'0000, // addis %r12, %r2, foo@got@toc@ha
0xe98c'0000, // ld %r12, foo@got@toc@lo(%r12)
// Restore the callee's %r2
0xe84c'0008, // ld %r2, 8(%r12)
// Jump to the function
0xe98c'0000, // ld %r12, 0(%r12)
0x7d89'03a6, // mtctr %r12
0x4e80'0420, // bctr
};
// If the destination is .plt, read a function descriptor from .got.plt.
static const ub32 plt_thunk[] = {
// Store the caller's %r2
0xf841'0028, // std %r2, 40(%r1)
// Materialize an address of a function descriptor
0x3d82'0000, // addis %r12, %r2, foo@gotplt@toc@ha
0x398c'0000, // addi %r12, %r12, foo@gotplt@toc@lo
// Restore the callee's %r2
0xe84c'0008, // ld %r2, 8(%r12)
// Jump to the function
0xe98c'0000, // ld %r12, 0(%r12)
0x7d89'03a6, // mtctr %r12
0x4e80'0420, // bctr
};
// If the destination is a non-imported function, we directly jump
// to the function entry address.
static const ub32 local_thunk[] = {
0x3d82'0000, // addis r12, r2, foo@toc@ha
0x398c'0000, // addi r12, r12, foo@toc@lo
0x7d89'03a6, // mtctr r12
0x4e80'0420, // bctr
0x6000'0000, // nop
0x6000'0000, // nop
0x6000'0000, // nop
};
static_assert(E::thunk_size == sizeof(pltgot_thunk));
static_assert(E::thunk_size == sizeof(plt_thunk));
static_assert(E::thunk_size == sizeof(local_thunk));
u8 *buf = ctx.buf + output_section.shdr.sh_offset + offset;
for (Symbol<E> *sym : symbols) {
if (sym->has_got(ctx)) {
i64 val = sym->get_got_addr(ctx) - ctx.extra.TOC->value;
memcpy(buf, pltgot_thunk, sizeof(pltgot_thunk));
*(ub32 *)(buf + 4) |= higha(val);
*(ub32 *)(buf + 8) |= lo(val);
} else if(sym->has_plt(ctx)) {
i64 val = sym->get_gotplt_addr(ctx) - ctx.extra.TOC->value;
memcpy(buf, plt_thunk, sizeof(plt_thunk));
*(ub32 *)(buf + 4) |= higha(val);
*(ub32 *)(buf + 8) |= lo(val);
} else {
i64 val = sym->get_addr(ctx, NO_OPD) - ctx.extra.TOC->value;
memcpy(buf, local_thunk, sizeof(local_thunk));
*(ub32 *)buf |= higha(val);
*(ub32 *)(buf + 4) |= lo(val);
}
buf += E::thunk_size;
}
}
static InputSection<E> *get_opd_section(ObjectFile<E> &file) {
for (std::unique_ptr<InputSection<E>> &isec : file.sections)
if (isec && isec->name() == ".opd")
return isec.get();
return nullptr;
}
static ElfRel<E> *
get_relocation_at(Context<E> &ctx, InputSection<E> &isec, i64 offset) {
std::span<ElfRel<E>> rels = isec.get_rels(ctx);
auto it = std::lower_bound(rels.begin(), rels.end(), offset,
[](const ElfRel<E> &r, i64 offset) {
return r.r_offset < offset;
});
if (it == rels.end())
return nullptr;
if (it->r_offset != offset)
return nullptr;
return &*it;
}
struct OpdSymbol {
bool operator<(const OpdSymbol &x) const { return r_offset < x.r_offset; }
u64 r_offset = 0;
Symbol<E> *sym = nullptr;
};
static Symbol<E> *
get_opd_sym_at(std::span<OpdSymbol> syms, u64 offset) {
auto it = std::lower_bound(syms.begin(), syms.end(), OpdSymbol{offset});
if (it == syms.end())
return nullptr;
if (it->r_offset != offset)
return nullptr;
return it->sym;
}
// Compiler creates an .opd entry for each function symbol. The intention
// is to make it possible to create an output .opd section just by linking
// input .opd sections in the same manner as we do to other normal input
// sections.
//
// However, in reality, .opd isn't a normal input section. It needs many
// special treatments as follows:
//
// 1. A function symbol refers to not a .text but an .opd. Its address
// works fine for address-taking relocations such as R_PPC64_ADDR64.
// However, R_PPC64_REL24 (which is used for branch instruction) needs
// a function's real address instead of the function's .opd address.
// We need to read .opd contents to find out a function entry point
// address to apply R_PPC64_REL24.
//
// 2. Output .opd entries are needed only for functions whose addresses
// are taken. Just copying input .opd sections to an output would
// produces lots of dead .opd entries.
//
// 3. In this design, all function symbols refer to an .opd section, and
// that doesn't work well with graph traversal optimizations such as
// garbage collection or identical comdat folding. For example, garbage
// collector would mark an .opd alive which in turn mark all functions
// thatare referenced by .opd as alive, effectively keeping all
// functions as alive.
//
// The problem is that the compiler creates a half-baked .opd section, and
// the linker has to figure out what all these .opd entries and
// relocations are trying to achieve. It's like the compiler would emit a
// half-baked .plt section in an object file and the linker has to deal
// with that. That's not a good design.
//
// So, in this function, we undo what the compiler did to .opd. We remove
// function symbols from .opd and reattach them to their function entry
// points. We also rewrite relocations that directly refer to an input
// .opd section so that they refer to function symbols instead. We then
// mark input .opd sections as dead.
//
// After this function, we mark symbols with the NEEDS_PPC_OPD flag if the
// symbol needs an .opd entry. We then create an output .opd just like we
// do for .plt or .got.
void ppc64v1_rewrite_opd(Context<E> &ctx) {
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
InputSection<E> *opd = get_opd_section(*file);
if (!opd)
return;
opd->is_alive = false;
// Move symbols from .opd to .text.
std::vector<OpdSymbol> opd_syms;
for (Symbol<E> *sym : file->symbols) {
if (sym->file != file || sym->get_input_section() != opd)
continue;
if (u32 ty = sym->get_type(); ty != STT_FUNC && ty != STT_GNU_IFUNC)
continue;
ElfRel<E> *rel = get_relocation_at(ctx, *opd, sym->value);
if (!rel)
Fatal(ctx) << *file << ": cannot find a relocation in .opd for "
<< *sym << " at offset 0x" << std::hex << (u64)sym->value;
Symbol<E> *sym2 = file->symbols[rel->r_sym];
if (sym2->get_type() != STT_SECTION)
Fatal(ctx) << *file << ": bad relocation in .opd referring " << *sym2;
opd_syms.push_back({sym->value, sym});
sym->set_input_section(sym2->get_input_section());
sym->value = rel->r_addend;
}
// Sort symbols so that get_opd_sym_at() can do binary search.
sort(opd_syms);
// Rewrite relocations so that they directly refer to .opd.
for (std::unique_ptr<InputSection<E>> &isec : file->sections) {
if (!isec || !isec->is_alive || isec.get() == opd)
continue;
for (ElfRel<E> &r : isec->get_rels(ctx)) {
Symbol<E> &sym = *file->symbols[r.r_sym];
if (sym.get_input_section() != opd)
continue;
Symbol<E> *real_sym = get_opd_sym_at(opd_syms, r.r_addend);
if (!real_sym)
Fatal(ctx) << *isec << ": cannot find a symbol in .opd for " << r
<< " at offset 0x" << std::hex << (u64)r.r_addend;
r.r_sym = real_sym->sym_idx;
r.r_addend = 0;
}
}
});
}
// When a function is exported, the dynamic symbol for the function should
// refers to the function's .opd entry. This function marks such symbols
// with NEEDS_PPC_OPD.
void ppc64v1_scan_symbols(Context<E> &ctx) {
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (Symbol<E> *sym : file->symbols)
if (sym->file == file && sym->is_exported)
if (u32 ty = sym->get_type(); ty == STT_FUNC || ty == STT_GNU_IFUNC)
sym->flags |= NEEDS_PPC_OPD;
});
// 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;
};
mark(ctx.arg.entry);
mark(ctx.arg.init);
mark(ctx.arg.fini);
}
void PPC64OpdSection::add_symbol(Context<E> &ctx, Symbol<E> *sym) {
sym->set_opd_idx(ctx, symbols.size());
symbols.push_back(sym);
this->shdr.sh_size += ENTRY_SIZE;
}
i64 PPC64OpdSection::get_reldyn_size(Context<E> &ctx) const {
if (ctx.arg.pic)
return symbols.size() * 2;
return 0;
}
void PPC64OpdSection::copy_buf(Context<E> &ctx) {
ub64 *buf = (ub64 *)(ctx.buf + this->shdr.sh_offset);
ElfRel<E> *rel = nullptr;
if (ctx.arg.pic)
rel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset + reldyn_offset);
for (Symbol<E> *sym : symbols) {
u64 addr = sym->get_addr(ctx, NO_PLT | NO_OPD);
*buf++ = addr;
*buf++ = ctx.extra.TOC->value;
*buf++ = 0;
if (ctx.arg.pic) {
u64 loc = sym->get_opd_addr(ctx);
*rel++ = ElfRel<E>(loc, E::R_RELATIVE, 0, addr);
*rel++ = ElfRel<E>(loc + 8, E::R_RELATIVE, 0, ctx.extra.TOC->value);
}
}
}
} // namespace mold::elf
+572
View File
@@ -0,0 +1,572 @@
// This file implements the PowerPC ELFv2 ABI which was standardized in
// 2014. Modern little-endian PowerPC systems are based on this ABI.
// The ABI is often referred to as "ppc64le". This shouldn't be confused
// with "ppc64" which refers to the original, big-endian PowerPC systems.
//
// PPC64 is a bit tricky to support because PC-relative load/store
// instructions hadn't been available until Power10 which debuted in 2021.
// Prior to Power10, it wasn't trivial for position-independent code (PIC)
// to load a value from, for example, .got, as we can't do that with [PC +
// the offset to the .got entry].
//
// In the following, I'll explain how PIC is supported on pre-Power10
// systems first and then explain what has changed with Power10.
//
//
// Position-independent code on Power9 or earlier:
//
// We can get the program counter on older PPC64 systems with the
// following four instructions
//
// mflr r1 // save the current link register to r1
// bl .+4 // branch to the next instruction as if it were a function
// mflr r0 // copy the return address to r0
// mtlr r1 // restore the original link register value
//
// , but it's too expensive to do if we do this for each load/store.
//
// As a workaround, most functions are compiled in such a way that r2 is
// assumed to always contain the address of .got + 0x8000. With this, we
// can for example load the first entry of .got with a single instruction
// `lw r0, -0x8000(r2)`. r2 is called the TOC pointer.
//
// There's only one .got for each ELF module. Therefore, if a callee is in
// the same ELF module, r2 doesn't have to be recomputed. Most function
// calls are usually within the same ELF module, so this mechanism is
// efficient.
//
// A function compiled for pre-Power10 usually has two entry points,
// global and local. The global entry point usually 8 bytes precedes
// the local entry point. In between is the following instructions:
//
// addis r2, r12, .TOC.@ha
// addi r2, r2, .TOC.@lo + 4;
//
// The global entry point assumes that the address of itself is in r12,
// and it computes its own TOC pointer from r12. It's easy to do so for
// the callee because the offset between its .got + 0x8000 and the
// function is known at link-time. The above code sequence then falls
// through to the local entry point that assumes r2 is .got + 0x8000.
//
// So, if a callee's TOC pointer is different from the current one
// (e.g. calling a function in another .so), we first load the callee's
// address to r12 (e.g. from .got.plt with a r2-relative load) and branch
// to that address. Then the callee computes its own TOC pointer using
// r12.
//
//
// Position-independent code on Power10:
//
// Power10 added 8-bytes-long instructions to the ISA. Some of them are
// PC-relative load/store instructions that take 34 bits offsets.
// Functions compiled with `-mcpu=power10` use these instructions for PIC.
// r2 does not have a special meaning in such fucntions.
//
// When a fucntion compiled for Power10 calls a function that uses the TOC
// pointer, we need to compute a correct value for TOC and set it to r2
// before transferring the control to the callee. Thunks are responsible
// for doing it.
//
// `_NOTOC` relocations such as `R_PPC64_REL24_NOTOC` indicate that the
// callee does not use TOC (i.e. compiled with `-mcpu=power10`). If a
// function using TOC is referenced via a `_NOTOC` relocation, that call
// is made through a range extension thunk.
//
//
// Note on section names: the PPC64 psABI uses a weird naming convention
// which calls .got.plt .plt. We ignored that part because it's just
// confusing. Since the runtime only cares about segments, we should be
// able to name sections whatever we want.
//
// https://github.com/rui314/psabi/blob/main/ppc64v2.pdf
#include "mold.h"
namespace mold::elf {
using E = PPC64V2;
static u64 lo(u64 x) { return x & 0xffff; }
static u64 hi(u64 x) { return x >> 16; }
static u64 ha(u64 x) { return (x + 0x8000) >> 16; }
static u64 high(u64 x) { return (x >> 16) & 0xffff; }
static u64 higha(u64 x) { return ((x + 0x8000) >> 16) & 0xffff; }
static void write34(u8 *loc, u64 x) {
ul32 *buf = (ul32 *)loc;
buf[0] = (buf[0] & 0xfffc'0000) | bits(x, 33, 16);
buf[1] = (buf[1] & 0xffff'0000) | bits(x, 15, 0);
}
// .plt is used only for lazy symbol resolution on PPC64. All PLT
// calls are made via range extension thunks even if they are within
// reach. Thunks read addresses from .got.plt and jump there.
// Therefore, once PLT symbols are resolved and final addresses are
// written to .got.plt, thunks just skip .plt and directly jump to the
// resolved addresses.
template <>
void write_plt_header(Context<E> &ctx, u8 *buf) {
static const ul32 insn[] = {
// Get PC
0x7c08'02a6, // mflr r0
0x429f'0005, // bcl 20, 31, 4 // obtain PC
0x7d68'02a6, // mflr r11
0x7c08'03a6, // mtlr r0
// Compute the PLT entry index
0x398c'ffd4, // addi r12, r12, -44
0x7c0b'6050, // subf r0, r11, r12
0x7800'f082, // rldicl r0, r0, 62, 2
// Compute the address of .got.plt
0x3d6b'0000, // addis r11, r11, GOTPLT_OFFSET@ha
0x396b'0000, // addi r11, r11, GOTPLT_OFFSET@lo
// Load .got.plt[0] and .got.plt[1] and branch to .got.plt[0]
0xe98b'0000, // ld r12, 0(r11)
0x7d89'03a6, // mtctr r12
0xe96b'0008, // ld r11, 8(r11)
0x4e80'0420, // bctr
};
memcpy(buf, insn, sizeof(insn));
i64 val = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 8;
*(ul32 *)(buf + 28) |= higha(val);
*(ul32 *)(buf + 32) |= lo(val);
}
template <>
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
// When the control is transferred to a PLT entry, the PLT entry's
// address is already set to %r12 by the caller.
i64 offset = ctx.plt->shdr.sh_addr - sym.get_plt_addr(ctx);
*(ul32 *)buf = 0x4b00'0000 | (offset & 0x00ff'ffff); // b plt0
}
// .plt.got is not necessary on PPC64 because range extension thunks
// directly read GOT entries and jump there.
template <>
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {}
template <>
void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_NONE:
break;
case R_PPC64_ADDR64:
*(ul64 *)loc = val;
break;
case R_PPC64_REL32:
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
break;
case R_PPC64_REL64:
*(ul64 *)loc = val - this->shdr.sh_addr - offset;
break;
default:
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
}
static u64 get_local_entry_offset(Context<E> &ctx, Symbol<E> &sym) {
i64 val = sym.esym().ppc_local_entry;
assert(val <= 7);
if (val == 7)
Fatal(ctx) << sym << ": local entry offset 7 is reserved";
if (val == 0 || val == 1)
return 0;
return 1 << val;
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
ElfRel<E> *dynrel = nullptr;
if (ctx.reldyn)
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
file.reldyn_offset + this->reldyn_offset);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
u64 S = sym.get_addr(ctx);
u64 A = rel.r_addend;
u64 P = get_addr() + rel.r_offset;
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
u64 GOT = ctx.got->shdr.sh_addr;
u64 TOC = ctx.extra.TOC->value;
auto r2save_thunk_addr = [&] { return get_thunk_addr(i); };
auto no_r2save_thunk_addr = [&] { return get_thunk_addr(i) + 8; };
switch (rel.r_type) {
case R_PPC64_ADDR64:
if (name() == ".toc")
apply_toc_rel(ctx, sym, rel, loc, S, A, P, &dynrel);
else
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_PPC64_TOC16_HA:
*(ul16 *)loc = ha(S + A - TOC);
break;
case R_PPC64_TOC16_LO:
*(ul16 *)loc = lo(S + A - TOC);
break;
case R_PPC64_TOC16_DS:
case R_PPC64_TOC16_LO_DS:
*(ul16 *)loc |= (S + A - TOC) & 0xfffc;
break;
case R_PPC64_REL24:
if (sym.has_plt(ctx) || !sym.esym().preserves_r2()) {
i64 val = r2save_thunk_addr() + A - P;
*(ul32 *)loc |= bits(val, 25, 2) << 2;
// The thunk saves %r2 to the caller's r2 save slot. We need to
// restore it after function return. To do so, there's usually a
// NOP as a placeholder after a BL. 0x6000'0000 is a NOP.
if (*(ul32 *)(loc + 4) == 0x6000'0000)
*(ul32 *)(loc + 4) = 0xe841'0018; // ld r2, 24(r1)
} else {
i64 val = S + get_local_entry_offset(ctx, sym) + A - P;
if (sign_extend(val, 25) != val)
val = no_r2save_thunk_addr() + A - P;
*(ul32 *)loc |= bits(val, 25, 2) << 2;
}
break;
case R_PPC64_REL24_NOTOC:
if (sym.has_plt(ctx) || sym.esym().uses_toc()) {
i64 val = no_r2save_thunk_addr() + A - P;
*(ul32 *)loc |= bits(val, 25, 2) << 2;
} else {
i64 val = S + A - P;
if (sign_extend(val, 25) != val)
val = no_r2save_thunk_addr() + A - P;
*(ul32 *)loc |= bits(val, 25, 2) << 2;
}
break;
case R_PPC64_REL32:
*(ul32 *)loc = S + A - P;
break;
case R_PPC64_REL64:
*(ul64 *)loc = S + A - P;
break;
case R_PPC64_REL16_HA:
*(ul16 *)loc = ha(S + A - P);
break;
case R_PPC64_REL16_LO:
*(ul16 *)loc = lo(S + A - P);
break;
case R_PPC64_PLT16_HA:
*(ul16 *)loc = ha(G + GOT - TOC);
break;
case R_PPC64_PLT16_HI:
*(ul16 *)loc = hi(G + GOT - TOC);
break;
case R_PPC64_PLT16_LO:
*(ul16 *)loc = lo(G + GOT - TOC);
break;
case R_PPC64_PLT16_LO_DS:
*(ul16 *)loc |= (G + GOT - TOC) & 0xfffc;
break;
case R_PPC64_PLT_PCREL34:
case R_PPC64_PLT_PCREL34_NOTOC:
case R_PPC64_GOT_PCREL34:
write34(loc, G + GOT - P);
break;
case R_PPC64_PCREL34:
write34(loc, S + A - P);
break;
case R_PPC64_GOT_TPREL16_HA:
*(ul16 *)loc = ha(sym.get_gottp_addr(ctx) - TOC);
break;
case R_PPC64_GOT_TPREL16_LO_DS:
*(ul16 *)loc |= (sym.get_gottp_addr(ctx) - TOC) & 0xfffc;
break;
case R_PPC64_GOT_TPREL_PCREL34:
write34(loc, sym.get_gottp_addr(ctx) - P);
break;
case R_PPC64_GOT_TLSGD16_HA:
*(ul16 *)loc = ha(sym.get_tlsgd_addr(ctx) - TOC);
break;
case R_PPC64_GOT_TLSGD16_LO:
*(ul16 *)loc = lo(sym.get_tlsgd_addr(ctx) - TOC);
break;
case R_PPC64_GOT_TLSGD_PCREL34:
write34(loc, sym.get_tlsgd_addr(ctx) - P);
break;
case R_PPC64_GOT_TLSLD16_HA:
*(ul16 *)loc = ha(ctx.got->get_tlsld_addr(ctx) - TOC);
break;
case R_PPC64_GOT_TLSLD16_LO:
*(ul16 *)loc = lo(ctx.got->get_tlsld_addr(ctx) - TOC);
break;
case R_PPC64_GOT_TLSLD_PCREL34:
write34(loc, ctx.got->get_tlsld_addr(ctx) - P);
break;
case R_PPC64_DTPREL16_HA:
*(ul16 *)loc = ha(S + A - ctx.dtp_addr);
break;
case R_PPC64_DTPREL16_LO:
*(ul16 *)loc = lo(S + A - ctx.dtp_addr);
break;
case R_PPC64_DTPREL34:
write34(loc, S + A - ctx.dtp_addr);
break;
case R_PPC64_TPREL16_HA:
*(ul16 *)loc = ha(S + A - ctx.tp_addr);
break;
case R_PPC64_TPREL16_LO:
*(ul16 *)loc = lo(S + A - ctx.tp_addr);
break;
case R_PPC64_TPREL34:
write34(loc, S + A - ctx.tp_addr);
break;
case R_PPC64_PLTSEQ:
case R_PPC64_PLTSEQ_NOTOC:
case R_PPC64_PLTCALL:
case R_PPC64_PLTCALL_NOTOC:
case R_PPC64_TLS:
case R_PPC64_TLSGD:
case R_PPC64_TLSLD:
break;
default:
unreachable();
}
}
}
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
auto check = [&](i64 val, i64 lo, i64 hi) {
if (val < lo || hi <= val)
Error(ctx) << *this << ": relocation " << rel << " against "
<< sym << " out of range: " << val << " is not in ["
<< lo << ", " << hi << ")";
};
SectionFragment<E> *frag;
i64 frag_addend;
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
u64 A = frag ? frag_addend : (i64)rel.r_addend;
switch (rel.r_type) {
case R_PPC64_ADDR64:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ul64 *)loc = *val;
else
*(ul64 *)loc = S + A;
break;
case R_PPC64_ADDR32: {
i64 val = S + A;
check(val, 0, 1LL << 32);
*(ul32 *)loc = val;
break;
}
case R_PPC64_DTPREL64:
*(ul64 *)loc = S + A - ctx.dtp_addr;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
<< rel;
}
}
}
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<const ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (sym.is_ifunc())
sym.flags |= NEEDS_GOT | NEEDS_PLT;
switch (rel.r_type) {
case R_PPC64_ADDR64:
if (name() == ".toc")
scan_toc_rel(ctx, sym, rel);
else
scan_dyn_absrel(ctx, sym, rel);
break;
case R_PPC64_GOT_TPREL16_HA:
case R_PPC64_GOT_TPREL_PCREL34:
sym.flags |= NEEDS_GOTTP;
break;
case R_PPC64_REL24:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_PPC64_REL24_NOTOC:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
ctx.extra.is_power10 = true;
break;
case R_PPC64_PLT16_HA:
case R_PPC64_PLT_PCREL34:
case R_PPC64_PLT_PCREL34_NOTOC:
case R_PPC64_GOT_PCREL34:
sym.flags |= NEEDS_GOT;
break;
case R_PPC64_GOT_TLSGD16_HA:
case R_PPC64_GOT_TLSGD_PCREL34:
sym.flags |= NEEDS_TLSGD;
break;
case R_PPC64_GOT_TLSLD16_HA:
case R_PPC64_GOT_TLSLD_PCREL34:
ctx.needs_tlsld = true;
break;
case R_PPC64_TPREL16_HA:
case R_PPC64_TPREL16_LO:
case R_PPC64_TPREL34:
check_tlsle(ctx, sym, rel);
break;
case R_PPC64_REL32:
case R_PPC64_REL64:
case R_PPC64_TOC16_HA:
case R_PPC64_TOC16_LO:
case R_PPC64_TOC16_LO_DS:
case R_PPC64_TOC16_DS:
case R_PPC64_REL16_HA:
case R_PPC64_REL16_LO:
case R_PPC64_PLT16_HI:
case R_PPC64_PLT16_LO:
case R_PPC64_PLT16_LO_DS:
case R_PPC64_PCREL34:
case R_PPC64_PLTSEQ:
case R_PPC64_PLTSEQ_NOTOC:
case R_PPC64_PLTCALL:
case R_PPC64_PLTCALL_NOTOC:
case R_PPC64_GOT_TPREL16_LO_DS:
case R_PPC64_GOT_TLSGD16_LO:
case R_PPC64_GOT_TLSLD16_LO:
case R_PPC64_TLS:
case R_PPC64_TLSGD:
case R_PPC64_TLSLD:
case R_PPC64_DTPREL16_HA:
case R_PPC64_DTPREL16_LO:
case R_PPC64_DTPREL34:
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
template <>
void RangeExtensionThunk<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[] = {
0xf841'0018, // std r2, 24(r1)
0x6000'0000, // nop
0x3d82'0000, // addis r12, r2, foo@gotplt@toc@ha
0xe98c'0000, // ld r12, foo@gotplt@toc@lo(r12)
0x7d89'03a6, // mtctr r12
0x4e80'0420, // bctr
};
static const ul32 plt_thunk_power10[] = {
0xf841'0018, // std r2, 24(r1)
0x6000'0000, // nop
0x0410'0000, // pld r12, foo@gotplt@pcrel
0xe580'0000,
0x7d89'03a6, // mtctr r12
0x4e80'0420, // bctr
};
// If the destination is a non-imported function, we directly jump
// to its local entry point.
static const ul32 local_thunk[] = {
0xf841'0018, // std r2, 24(r1)
0x6000'0000, // nop
0x3d82'0000, // addis r12, r2, foo@toc@ha
0x398c'0000, // addi r12, r12, foo@toc@lo
0x7d89'03a6, // mtctr r12
0x4e80'0420, // bctr
};
static const ul32 local_thunk_power10[] = {
0xf841'0018, // std r2, 24(r1)
0x6000'0000, // nop
0x0610'0000, // pla r12, foo@pcrel
0x3980'0000,
0x7d89'03a6, // mtctr r12
0x4e80'0420, // bctr
};
static_assert(E::thunk_size == sizeof(plt_thunk));
static_assert(E::thunk_size == sizeof(plt_thunk_power10));
static_assert(E::thunk_size == sizeof(local_thunk));
static_assert(E::thunk_size == sizeof(local_thunk_power10));
u8 *buf = ctx.buf + output_section.shdr.sh_offset + offset;
u64 P = output_section.shdr.sh_addr + offset;
u64 TOC = ctx.extra.TOC->value;
for (Symbol<E> *sym : symbols) {
if (sym->has_plt(ctx)) {
u64 got =
sym->has_got(ctx) ? sym->get_got_addr(ctx) : sym->get_gotplt_addr(ctx);
if (ctx.extra.is_power10) {
memcpy(buf, plt_thunk_power10, E::thunk_size);
write34(buf + 8, got - P - 8);
} else {
memcpy(buf, plt_thunk, E::thunk_size);
*(ul32 *)(buf + 8) |= higha(got - TOC);
*(ul32 *)(buf + 12) |= lo(got - TOC);
}
} else {
u64 S = sym->get_addr(ctx);
if (ctx.extra.is_power10) {
memcpy(buf, local_thunk_power10, E::thunk_size);
write34(buf + 8, S - P - 8);
} else {
memcpy(buf, local_thunk, E::thunk_size);
*(ul32 *)(buf + 8) |= higha(S - TOC);
*(ul32 *)(buf + 12) |= lo(S - TOC);
}
}
buf += E::thunk_size;
P += E::thunk_size;
}
}
template <>
u64 get_eflags(Context<E> &ctx) {
return 2;
}
} // namespace mold::elf
+1308
View File
File diff suppressed because it is too large. Load diff
-813
View File
@@ -1,813 +0,0 @@
#include "mold.h"
#include <tbb/parallel_for.h>
#include <tbb/parallel_for_each.h>
namespace mold::elf {
using E = RISCV64;
static u32 bit(u32 val, i64 pos) {
return (val >> pos) & 1;
}
// Returns [hi:lo] bits of val.
static u32 bits(u32 val, i64 hi, i64 lo) {
return (val >> lo) & ((1LL << (hi - lo + 1)) - 1);
}
static u32 itype(u32 val) {
return val << 20;
}
static u32 stype(u32 val) {
return bits(val, 11, 5) << 25 | bits(val, 4, 0) << 7;
}
static u32 btype(u32 val) {
return bit(val, 12) << 31 | bits(val, 10, 5) << 25 |
bits(val, 4, 1) << 8 | bit(val, 11) << 7;
}
static u32 utype(u32 val) {
// U-type instructions are used in combination with I-type
// instructions. U-type insn sets an immediate to the upper 20-bits
// of a register. I-type insn sign-extends a 12-bits immediate and
// add it to a register value to construct a complete value. 0x800
// is added here to compensate for the sign-extension.
return bits(val + 0x800, 31, 12) << 12;
}
static u32 jtype(u32 val) {
return bit(val, 20) << 31 | bits(val, 10, 1) << 21 |
bit(val, 11) << 20 | bits(val, 19, 12) << 12;
}
static u32 cbtype(u32 val) {
return bit(val, 8) << 12 | bit(val, 4) << 11 | bit(val, 3) << 10 |
bit(val, 7) << 6 | bit(val, 6) << 5 | bit(val, 2) << 4 |
bit(val, 1) << 3 | bit(val, 5) << 2;
}
static u32 cjtype(u32 val) {
return bit(val, 11) << 12 | bit(val, 4) << 11 | bit(val, 9) << 10 |
bit(val, 8) << 9 | bit(val, 10) << 8 | bit(val, 6) << 7 |
bit(val, 7) << 6 | bit(val, 3) << 5 | bit(val, 2) << 4 |
bit(val, 1) << 3 | bit(val, 5) << 2;
}
static void write_itype(u32 *loc, u32 val) {
u32 mask = 0b000000'00000'11111'111'11111'1111111;
*loc = (*loc & mask) | itype(val);
}
static void write_stype(u32 *loc, u32 val) {
u32 mask = 0b000000'11111'11111'111'00000'1111111;
*loc = (*loc & mask) | stype(val);
}
static void write_btype(u32 *loc, u32 val) {
u32 mask = 0b000000'11111'11111'111'00000'1111111;
*loc = (*loc & mask) | btype(val);
}
static void write_utype(u32 *loc, u32 val) {
u32 mask = 0b000000'00000'00000'000'11111'1111111;
*loc = (*loc & mask) | utype(val);
}
static void write_jtype(u32 *loc, u32 val) {
u32 mask = 0b000000'00000'00000'000'11111'1111111;
*loc = (*loc & mask) | jtype(val);
}
static void write_cbtype(u16 *loc, u32 val) {
u32 mask = 0b1110001110000011;
*loc = (*loc & mask) | cbtype(val);
}
static void write_cjtype(u16 *loc, u32 val) {
u32 mask = 0b1110000000000011;
*loc = (*loc & mask) | cjtype(val);
}
static void write_plt_header(Context<E> &ctx) {
u32 *buf = (u32 *)(ctx.buf + ctx.plt->shdr.sh_offset);
static const u32 plt0[] = {
0x00000397, // auipc t2, %pcrel_hi(.got.plt)
0x41c30333, // sub t1, t1, t3 # .plt entry + hdr + 12
0x0003be03, // ld t3, %pcrel_lo(1b)(t2) # _dl_runtime_resolve
0xfd430313, // addi t1, t1, -44 # .plt entry
0x00038293, // addi t0, t2, %pcrel_lo(1b) # &.got.plt
0x00135313, // srli t1, t1, 1 # .plt entry offset
0x0082b283, // ld t0, 8(t0) # link map
0x000e0067, // jr t3
};
u64 gotplt = ctx.gotplt->shdr.sh_addr;
u64 plt = ctx.plt->shdr.sh_addr;
memcpy(buf, plt0, sizeof(plt0));
write_utype(buf, gotplt - plt);
write_itype(buf + 2, gotplt - plt);
write_itype(buf + 4, gotplt - plt);
}
static void write_plt_entry(Context<E> &ctx, Symbol<E> &sym) {
u32 *ent = (u32 *)(ctx.buf + ctx.plt->shdr.sh_offset + ctx.plt_hdr_size +
sym.get_plt_idx(ctx) * ctx.plt_size);
static const u32 data[] = {
0x00000e17, // auipc t3, %pcrel_hi([email protected])
0x000e3e03, // ld t3, %pcrel_lo(1b)(t3)
0x000e0367, // jalr t1, t3
0x00000013, // nop
};
u64 gotplt = sym.get_gotplt_addr(ctx);
u64 plt = sym.get_plt_addr(ctx);
memcpy(ent, data, sizeof(data));
write_utype(ent, gotplt - plt);
write_itype(ent + 1, gotplt - plt);
}
template <>
void PltSection<E>::copy_buf(Context<E> &ctx) {
write_plt_header(ctx);
for (Symbol<E> *sym : symbols)
write_plt_entry(ctx, *sym);
}
template <>
void PltGotSection<E>::copy_buf(Context<E> &ctx) {
u32 *buf = (u32 *)(ctx.buf + this->shdr.sh_offset);
static const u32 data[] = {
0x00000e17, // auipc t3, %pcrel_hi([email protected])
0x000e3e03, // ld t3, %pcrel_lo(1b)(t3)
0x000e0367, // jalr t1, t3
0x00000013, // nop
};
for (Symbol<E> *sym : symbols) {
u32 *ent = buf + sym->get_pltgot_idx(ctx) * 4;
u64 got = sym->get_got_addr(ctx);
u64 plt = sym->get_plt_addr(ctx);
memcpy(ent, data, sizeof(data));
write_utype(ent, got - plt);
write_itype(ent + 1, got - plt);
}
}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_RISCV_ADD32:
*(u32 *)loc += val;
return;
case R_RISCV_SUB8:
*loc -= val;
return;
case R_RISCV_SUB16:
*(u16 *)loc -= val;
return;
case R_RISCV_SUB32:
*(u32 *)loc -= val;
return;
case R_RISCV_SUB6:
*loc = (*loc & 0b1100'0000) | ((*loc - val) & 0b0011'1111);
return;
case R_RISCV_SET6:
*loc = (*loc & 0b1100'0000) | (val & 0b0011'1111);
return;
case R_RISCV_SET8:
*loc = val;
return;
case R_RISCV_SET16:
*(u16 *)loc = val;
return;
case R_RISCV_32_PCREL:
*(u32 *)loc = val - this->shdr.sh_addr - offset;
return;
}
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
ElfRel<E> *dynrel = nullptr;
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<i32> r_deltas = get_r_deltas();
i64 frag_idx = 0;
if (ctx.reldyn)
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
file.reldyn_offset + this->reldyn_offset);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_RISCV_NONE || rel.r_type == R_RISCV_RELAX ||
rel.r_type == R_RISCV_ALIGN)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
i64 r_offset = rel.r_offset + r_deltas[i];
u8 *loc = base + r_offset;
const SectionFragmentRef<E> *frag_ref = nullptr;
if (rel_fragments && rel_fragments[frag_idx].idx == i)
frag_ref = &rel_fragments[frag_idx++];
#define S (frag_ref ? frag_ref->frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag_ref ? frag_ref->addend : rel.r_addend)
#define P (output_section->shdr.sh_addr + offset + r_offset)
#define G (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
#define GOT ctx.got->shdr.sh_addr
switch (rel.r_type) {
case R_RISCV_32:
*(u32 *)loc = S + A;
break;
case R_RISCV_64:
if (sym.is_absolute() || !ctx.arg.pic) {
*(u64 *)loc = S + A;
} else if (sym.is_imported) {
*dynrel++ = {P, R_RISCV_64, (u32)sym.get_dynsym_idx(ctx), A};
*(u64 *)loc = A;
} else {
if (!is_relr_reloc(ctx, rel))
*dynrel++ = {P, R_RISCV_RELATIVE, 0, (i64)(S + A)};
*(u64 *)loc = S + A;
}
break;
case R_RISCV_TLS_DTPMOD32:
case R_RISCV_TLS_DTPMOD64:
case R_RISCV_TLS_DTPREL32:
case R_RISCV_TLS_DTPREL64:
case R_RISCV_TLS_TPREL32:
case R_RISCV_TLS_TPREL64:
Error(ctx) << *this << ": unsupported relocation: " << rel;
break;
case R_RISCV_BRANCH:
write_btype((u32 *)loc, S + A - P);
break;
case R_RISCV_JAL:
write_jtype((u32 *)loc, S + A - P);
break;
case R_RISCV_CALL:
case R_RISCV_CALL_PLT: {
if (r_deltas[i + 1] - r_deltas[i] != 0) {
// auipc + jalr -> jal
assert(r_deltas[i + 1] - r_deltas[i] == -4);
u32 jalr = *(u32 *)&contents[rels[i].r_offset + 4];
*(u32 *)loc = (0b11111'000000 & jalr) | 0b101111;
write_jtype((u32 *)loc, S + A - P);
} else {
u64 val = sym.esym().is_undef_weak() ? 0 : S + A - P;
write_utype((u32 *)loc, val);
write_itype((u32 *)(loc + 4), val);
}
break;
}
case R_RISCV_GOT_HI20:
*(u32 *)loc = G + GOT + A - P;
break;
case R_RISCV_TLS_GOT_HI20:
*(u32 *)loc = sym.get_gottp_addr(ctx) + A - P;
break;
case R_RISCV_TLS_GD_HI20:
*(u32 *)loc = sym.get_tlsgd_addr(ctx) + A - P;
break;
case R_RISCV_PCREL_HI20:
if (sym.esym().is_undef_weak()) {
// Calling an undefined weak symbol does not make sense.
// We make such call into an infinite loop. This should
// help debugging of a faulty program.
*(u32 *)loc = P;
} else {
*(u32 *)loc = S + A - P;
}
break;
case R_RISCV_PCREL_LO12_I:
assert(sym.get_input_section() == this);
assert(sym.value < r_offset);
write_itype((u32 *)loc, *(u32 *)(base + sym.value));
break;
case R_RISCV_LO12_I:
case R_RISCV_TPREL_LO12_I:
write_itype((u32 *)loc, S + A);
break;
case R_RISCV_PCREL_LO12_S:
assert(sym.get_input_section() == this);
assert(sym.value < r_offset);
write_stype((u32 *)loc, *(u32 *)(base + sym.value));
break;
case R_RISCV_LO12_S:
case R_RISCV_TPREL_LO12_S:
write_stype((u32 *)loc, S + A);
break;
case R_RISCV_HI20:
write_utype((u32 *)loc, S + A);
break;
case R_RISCV_TPREL_HI20:
write_utype((u32 *)loc, S + A - ctx.tls_begin);
break;
case R_RISCV_TPREL_ADD:
break;
case R_RISCV_ADD8:
loc += S + A;
break;
case R_RISCV_ADD16:
*(u16 *)loc += S + A;
break;
case R_RISCV_ADD32:
*(u32 *)loc += S + A;
break;
case R_RISCV_ADD64:
*(u64 *)loc += S + A;
break;
case R_RISCV_SUB8:
loc -= S + A;
break;
case R_RISCV_SUB16:
*(u16 *)loc -= S + A;
break;
case R_RISCV_SUB32:
*(u32 *)loc -= S + A;
break;
case R_RISCV_SUB64:
*(u64 *)loc -= S + A;
break;
case R_RISCV_RVC_BRANCH:
write_cbtype((u16 *)loc, S + A - P);
break;
case R_RISCV_RVC_JUMP:
write_cjtype((u16 *)loc, S + A - P);
break;
case R_RISCV_RVC_LUI:
Error(ctx) << *this << ": unsupported relocation: " << rel;
break;
case R_RISCV_SUB6:
*loc = (*loc & 0b1100'0000) | ((*loc - (S + A)) & 0b0011'1111);
break;
case R_RISCV_SET6:
*loc = (*loc & 0b1100'0000) | ((S + A) & 0b0011'1111);
break;
case R_RISCV_SET8:
*loc = S + A;
break;
case R_RISCV_SET16:
*(u16 *)loc = S + A;
break;
case R_RISCV_SET32:
*(u32 *)loc = S + A;
break;
case R_RISCV_32_PCREL:
*(u32 *)loc = S + A - P;
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
#undef S
#undef A
#undef P
#undef G
#undef GOT
}
// In the above loop, PC-relative HI20 relocations overwrote
// instructions with full 32-bit values to allow their corresponding
// PCREL_LO12 relocations to read their values. This loop restore
// the original instructions.
for (i64 i = 0; i < rels.size(); i++) {
switch (rels[i].r_type) {
case R_RISCV_GOT_HI20:
case R_RISCV_PCREL_HI20:
case R_RISCV_TLS_GOT_HI20:
case R_RISCV_TLS_GD_HI20: {
u32 *loc = (u32 *)(base + rels[i].r_offset + r_deltas[i]);
u32 val = *loc;
*loc = *(u32 *)&contents[rels[i].r_offset];
write_utype(loc, val);
}
}
}
}
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_RISCV_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
if (!sym.file) {
report_undef(ctx, file, sym);
continue;
}
SectionFragment<E> *frag;
i64 addend;
std::tie(frag, addend) = get_fragment(ctx, rel);
#define S (frag ? frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag ? addend : rel.r_addend)
switch (rel.r_type) {
case R_RISCV_32:
*(u32 *)loc = S + A;
break;
case R_RISCV_64:
if (std::optional<u64> val = get_tombstone(sym))
*(u64 *)loc = *val;
else
*(u64 *)loc = S + A;
break;
case R_RISCV_ADD8:
*loc += S + A;
break;
case R_RISCV_ADD16:
*(u16 *)loc += S + A;
break;
case R_RISCV_ADD32:
*(u32 *)loc += S + A;
break;
case R_RISCV_ADD64:
*(u64 *)loc += S + A;
break;
case R_RISCV_SUB8:
*loc -= S + A;
break;
case R_RISCV_SUB16:
*(u16 *)loc -= S + A;
break;
case R_RISCV_SUB32:
*(u32 *)loc -= S + A;
break;
case R_RISCV_SUB64:
*(u64 *)loc -= S + A;
break;
case R_RISCV_SUB6:
*loc = (*loc & 0b1100'0000) | ((*loc - (S + A)) & 0b0011'1111);
break;
case R_RISCV_SET6:
*loc = (*loc & 0b1100'0000) | ((S + A) & 0b0011'1111);
break;
case R_RISCV_SET8:
*loc = S + A;
break;
case R_RISCV_SET16:
*(u16 *)loc = S + A;
break;
case R_RISCV_SET32:
*(u32 *)loc = S + A;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
<< rel;
break;
}
#undef S
#undef A
}
}
template <>
void InputSection<E>::copy_contents_riscv(Context<E> &ctx, u8 *buf) {
// A non-alloc section isn't relaxed, so just copy it as one big chunk.
if (!(shdr().sh_flags & SHF_ALLOC)) {
if (is_compressed())
uncompress(ctx, buf);
else
memcpy(buf, contents.data(), contents.size());
return;
}
// Memory-allocated sections may be relaxed, so copy each segment
// individually.
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<i32> r_deltas = get_r_deltas();
i64 pos = 0;
for (i64 i = 0; i < rels.size(); i++) {
i64 delta = r_deltas[i + 1] - r_deltas[i];
if (delta == 0)
continue;
assert(delta < 0);
const ElfRel<E> &r = rels[i];
memcpy(buf, contents.data() + pos, r.r_offset - pos);
buf += r.r_offset - pos;
pos = r.r_offset - delta;
}
memcpy(buf, contents.data() + pos, contents.size() - pos);
}
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_RISCV_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (!sym.file) {
report_undef(ctx, file, sym);
continue;
}
if (sym.get_type() == STT_GNU_IFUNC) {
sym.flags |= NEEDS_GOT;
sym.flags |= NEEDS_PLT;
}
switch (rel.r_type) {
case R_RISCV_32:
case R_RISCV_HI20: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, ERROR, ERROR, ERROR }, // DSO
{ NONE, ERROR, ERROR, ERROR }, // PIE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
case R_RISCV_64: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, BASEREL, DYNREL, DYNREL }, // DSO
{ NONE, BASEREL, DYNREL, DYNREL }, // PIE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
case R_RISCV_TLS_DTPMOD32:
case R_RISCV_TLS_DTPMOD64:
case R_RISCV_TLS_DTPREL32:
case R_RISCV_TLS_DTPREL64:
case R_RISCV_TLS_TPREL32:
case R_RISCV_TLS_TPREL64:
Error(ctx) << *this << ": unsupported relocation: " << rel;
break;
case R_RISCV_BRANCH:
case R_RISCV_JAL:
break;
case R_RISCV_CALL:
case R_RISCV_CALL_PLT:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_RISCV_GOT_HI20:
sym.flags |= NEEDS_GOT;
break;
case R_RISCV_TLS_GOT_HI20:
ctx.has_gottp_rel = true;
sym.flags |= NEEDS_GOTTP;
break;
case R_RISCV_TLS_GD_HI20:
sym.flags |= NEEDS_TLSGD;
break;
case R_RISCV_PCREL_HI20:
case R_RISCV_PCREL_LO12_I:
case R_RISCV_PCREL_LO12_S:
case R_RISCV_LO12_I:
case R_RISCV_LO12_S:
case R_RISCV_TPREL_HI20:
case R_RISCV_TPREL_LO12_I:
case R_RISCV_TPREL_LO12_S:
case R_RISCV_TPREL_ADD:
case R_RISCV_ADD8:
case R_RISCV_ADD16:
case R_RISCV_ADD32:
case R_RISCV_ADD64:
case R_RISCV_SUB8:
case R_RISCV_SUB16:
case R_RISCV_SUB32:
case R_RISCV_SUB64:
case R_RISCV_ALIGN:
break;
case R_RISCV_RVC_BRANCH:
case R_RISCV_RVC_JUMP:
break;
case R_RISCV_RVC_LUI:
Error(ctx) << *this << ": unsupported relocation: " << rel;
break;
case R_RISCV_RELAX:
case R_RISCV_SUB6:
case R_RISCV_SET6:
case R_RISCV_SET8:
case R_RISCV_SET16:
case R_RISCV_SET32:
break;
case R_RISCV_32_PCREL: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ ERROR, NONE, ERROR, ERROR }, // DSO
{ ERROR, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
static bool is_resizable(Context<E> &ctx, InputSection<E> *isec) {
return isec && (isec->shdr().sh_flags & SHF_ALLOC);
}
// Initializes r_deltas and sorted_symbols.
static void initialize_storage(Context<E> &ctx) {
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
file->r_deltas.resize(file->sections.size());
for (std::unique_ptr<InputSection<E>> &isec : file->sections)
if (is_resizable(ctx, isec.get()))
isec->get_r_deltas().resize(isec->get_rels(ctx).size() + 1);
file->sorted_symbols.resize(file->sections.size());
for (Symbol<E> *sym : file->symbols)
if (sym->file == file)
if (InputSection<E> *isec = sym->get_input_section())
file->sorted_symbols[isec->shndx].push_back(sym);
for (std::vector<Symbol<E> *> &vec : file->sorted_symbols)
sort(vec, [](Symbol<E> *a, Symbol<E> *b) { return a->value < b->value; });
});
}
// Returns the distance between a relocated place and a symbol.
static i64 compute_distance(Context<E> &ctx, Symbol<E> &sym,
InputSection<E> &isec, const ElfRel<E> &rel) {
// We handle absolute symbols as if they were infinitely far away
// because `relax_section` may increase a distance between a branch
// instruction and an absolute symbol. Branching to an absolute
// location is extremely rare in real code, though.
if (sym.is_absolute())
return INT32_MAX;
// Likewise, relocations against weak undefined symbols won't be relaxed.
if (sym.esym().is_undef_weak())
return INT32_MAX;
// Compute a distance between the relocated place and the symbol.
i64 S = sym.get_addr(ctx);
i64 A = rel.r_addend;
i64 P = isec.get_addr() + rel.r_offset;
return S + A - P;
}
// Relax R_RISCV_CALL and R_RISCV_CALL_PLT relocations.
static void relax_section(Context<E> &ctx, InputSection<E> &isec) {
std::span<i32> r_deltas = isec.get_r_deltas();
std::span<Symbol<E> *> syms = isec.get_sorted_symbols();
i64 delta = 0;
std::span<ElfRel<E>> rels = isec.get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &r = rels[i];
i64 delta2 = 0;
r_deltas[i] += delta;
switch (r.r_type) {
case R_RISCV_ALIGN: {
// R_RISCV_ALIGN refers NOP instructions. We need to eliminate
// some or all of the instructions so that the instruction that
// immediately follows the NOPs is aligned to a specified
// alignment boundary.
u64 loc = isec.get_addr() + r.r_offset + delta;
// The total bytes of NOPs is stored to r_addend, so the next
// instruction is r_addend away.
u64 next_loc = loc + r.r_addend;
u64 alignment = (std::popcount<u64>(r.r_addend) == 1)
? r.r_addend : next_power_of_two(r.r_addend);
if (next_loc % alignment)
delta2 = align_to(loc, alignment) - next_loc;
break;
}
case R_RISCV_CALL:
case R_RISCV_CALL_PLT:
if (ctx.arg.relax) {
if (i == rels.size() - 1 || rels[i + 1].r_type != R_RISCV_RELAX)
break;
// If the jump target is within ±1 MiB, we can replace AUIPC+JALR
// with JAL, saving 4 bytes.
Symbol<E> &sym = *isec.file.symbols[r.r_sym];
i64 dist = compute_distance(ctx, sym, isec, r);
if (dist % 2 == 0 && -(1 << 20) <= dist && dist < (1 << 20))
delta2 = -4;
}
}
if (delta2 == 0)
continue;
while (!syms.empty() && syms[0]->value <= r.r_offset) {
syms[0]->value += delta;
syms = syms.subspan(1);
}
delta += delta2;
}
for (Symbol<E> *sym : syms)
sym->value += delta;
r_deltas[rels.size()] += delta;
isec.sh_size += delta;
}
// RISC-V instructions are 16 or 32 bits long, so immediates encoded
// in instructions can't be 32 bits long. Therefore, branch and load
// instructions can't refer the 4 GiB address space unlike x86-64.
// In fact, JAL (jump and link) instruction can jump to only within
// ±1 MiB as their immediate is only 21 bits long.
//
// If you want to jump to somewhere further than that, you need to
// construct a full 32-bit offset using multiple instruction and
// branch to that place (e.g. AUIPC and JALR instead of JAL).
// In this comment, we refer instructions such as JAL as the short
// encoding and ones such as AUIPC+JALR as the long encoding.
//
// By default, compiler always uses the long encoding so that branch
// targets are always encodable. This is a safe bet for them but
// may result in inefficient code. Therefore, the RISC-V psABI defines
// a mechanism for the linker to replace long encoding instructions
// with short ones, shrinking the section and increasing the code
// density.
//
// This is contrary to the psABIs for the other RISC processors such as
// ARM64. Typically, they use short instructions by default, and a
// linker creates so-called "thunks" to extend ranges of short jumps.
// On RISC-V, instructions are in the long encoding by default, and
// the linker shrinks them if it can.
//
// When we shrink a section, we need to adjust relocation offsets and
// symbol values. For example, if we replace AUIPC+JALR with JAL
// (which saves 4 bytes), all relocations pointing to anywhere after
// that location need to be shifted by 4. In addition to that, any
// symbol that refers anywhere after that locatioin need to be shifted
// by 4 bytes as well.
//
// For relocations, we use `r_deltas` array to memorize how many bytes
// have be adjusted. For symbols, we directly mutate their `value`
// member.
//
// This operation seems to be optional, as by default instructions are
// using the long encoding, but calling this function is actually
// mandatory because of R_RISCV_ALIGN. R_RISCV_ALIGN relocation is a
// directive to the linker to align the location referred to by the
// relocation to a specified byte boundary. We at least have to
// interpret them satisfy the constraints imposed by R_RISCV_ALIGN
// relocations.
i64 riscv_resize_sections(Context<E> &ctx) {
Timer t(ctx, "riscv_resize_sections");
initialize_storage(ctx);
// Find R_RISCV_CALL AND R_RISCV_CALL_PLT that can be relaxed.
// This step should only shrink sections.
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (std::unique_ptr<InputSection<E>> &isec : file->sections)
if (is_resizable(ctx, isec.get()))
relax_section(ctx, *isec);
});
// Re-compute section offset again to finalize them.
compute_section_sizes(ctx);
return set_osec_offsets(ctx);
}
} // namespace mold::elf
+492
View File
@@ -0,0 +1,492 @@
// This file contains code for the IBM z/Architecture 64-bit ISA, which is
// commonly referred to as "s390x" on Linux.
//
// z/Architecture is a 64-bit CISC ISA developed by IBM around 2000 for
// IBM's "big iron" mainframe computers. The computers are direct
// descendents of IBM System/360 all the way back in 1966. I've never
// actually seen a mainframe, and you probaly haven't either, but it looks
// like the mainframe market is still large enough to sustain its ecosystem.
// Ubuntu for example provides the official support for s390x as of 2022.
// Since they are being actively maintained, we need to support them.
//
// As an instruction set, s390x isn't particularly odd. It has 16 general-
// purpose registers. Instructions are 2, 4 or 6 bytes long and always
// aligned to 2 bytes boundaries. Despite unfamiliarty, I found that it
// just feels like an x86-64 in a parallel universe.
//
// Here is the register usage in this ABI:
//
// r0-r1: reserved as scratch registers so we can use them in our PLT
// r2: parameter passing and return values
// r3-r6: parameter passing
// r12: address of GOT if position-independent code
// r14: return address
// r15: stack pointer
// a1: upper 32 bits of TP (thread pointer)
// a2: lower 32 bits of TP (thread pointer)
//
// Thread-local storage (TLS) is supported on s390x in the same way as it
// is on other targets with one exeption. On other targets, __tls_get_addr
// is used to get an address of a thread-local variable. On s390x,
// __tls_get_offset is used instead. The difference is __tls_get_offset
// returns an address of a thread-local variable as an offset from TP. So
// we need to add TP to a return value before use. I don't know why it is
// different, but that is the way it is.
//
// https://github.com/rui314/psabi/blob/main/s390x.pdf
#include "mold.h"
namespace mold::elf {
using E = S390X;
static void write_mid20(u8 *loc, u64 val) {
*(ub32 *)loc |= (bits(val, 11, 0) << 16) | (bits(val, 19, 12) << 8);
}
template <>
void write_plt_header(Context<E> &ctx, u8 *buf) {
static u8 insn[] = {
// Compute PLT_INDEX
0xb9, 0x09, 0x00, 0x01, // sgr %r0, %r1
0xa7, 0x0b, 0xff, 0xc2, // aghi %r0, -62
0xeb, 0x10, 0x00, 0x01, 0x00, 0x0c, // srlg %r1, %r0, 1
0xb9, 0x08, 0x00, 0x01, // agr %r0, %r1
0xe3, 0x00, 0xf0, 0x38, 0x00, 0x24, // stg %r0, 56(%r15)
// Branch to _dl_runtime_resolve
0xc0, 0x10, 0, 0, 0, 0, // larl %r1, GOTPLT_OFFSET
0xd2, 0x07, 0xf0, 0x30, 0x10, 0x08, // mvc 48(8, %r15), 8(%r1)
0xe3, 0x10, 0x10, 0x10, 0x00, 0x04, // lg %r1, 16(%r1)
0x07, 0xf1, // br %r1
0x07, 0x00, 0x07, 0x00, // nopr; nopr
};
memcpy(buf, insn, sizeof(insn));
*(ub32 *)(buf + 26) =
(ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 24) >> 1;
}
template <>
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
static u8 insn[] = {
0xc0, 0x10, 0, 0, 0, 0, // larl %r1, GOTPLT_ENTRY_OFFSET
0xe3, 0x10, 0x10, 0x00, 0x00, 0x04, // lg %r1, (%r1)
0x0d, 0x01, // basr %r0, %r1
0x07, 0x00, // nopr
};
memcpy(buf, insn, sizeof(insn));
*(ub32 *)(buf + 2) = (sym.get_gotplt_addr(ctx) - sym.get_plt_addr(ctx)) >> 1;
}
template <>
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
static u8 insn[] = {
0xc0, 0x10, 0, 0, 0, 0, // larl %r1, GOT_ENTRY_OFFSET
0xe3, 0x10, 0x10, 0x00, 0x00, 0x04, // lg %r1, (%r1)
0x07, 0xf1, // br %r1
0x07, 0x00, // nopr
};
memcpy(buf, insn, sizeof(insn));
*(ub32 *)(buf + 2) = (sym.get_got_addr(ctx) - sym.get_plt_addr(ctx)) >> 1;
}
template <>
void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_NONE:
break;
case R_390_PC32:
*(ub32 *)loc = val - this->shdr.sh_addr - offset;
break;
case R_390_64:
*(ub64 *)loc = val;
break;
default:
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
ElfRel<E> *dynrel = nullptr;
if (ctx.reldyn)
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
file.reldyn_offset + this->reldyn_offset);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
auto check = [&](i64 val, i64 lo, i64 hi) {
if (val < lo || hi <= val)
Error(ctx) << *this << ": relocation " << rel << " against "
<< sym << " out of range: " << val << " is not in ["
<< lo << ", " << hi << ")";
};
auto check_dbl = [&](i64 val, i64 lo, i64 hi) {
check(val, lo, hi);
// R_390_*DBL relocs should never refer a symbol at an odd address
if (val & 1)
Error(ctx) << *this << ": misaligned symbol " << sym
<< " for relocation " << rel;
};
u64 S = sym.get_addr(ctx);
u64 A = rel.r_addend;
u64 P = get_addr() + rel.r_offset;
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
u64 GOT = ctx.got->shdr.sh_addr;
switch (rel.r_type) {
case R_390_64:
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_390_8:
check(S + A, 0, 1 << 8);
*loc = S + A;
break;
case R_390_12:
check(S + A, 0, 1 << 12);
*(ul16 *)loc |= bits(S + A, 11, 0);
break;
case R_390_16:
check(S + A, 0, 1 << 16);
*(ub16 *)loc = S + A;
break;
case R_390_20:
check(S + A, 0, 1 << 20);
write_mid20(loc, S + A);
break;
case R_390_32:
case R_390_PLT32:
check(S + A, 0, 1LL << 32);
*(ub32 *)loc = S + A;
break;
case R_390_PLT64:
*(ub64 *)loc = S + A;
break;
case R_390_PC12DBL:
case R_390_PLT12DBL:
check_dbl(S + A - P, -(1 << 12), 1 << 12);
*(ul16 *)loc |= bits(S + A - P, 12, 1);
break;
case R_390_PC16:
check(S + A - P, -(1 << 15), 1 << 15);
*(ub16 *)loc = S + A - P;
break;
case R_390_PC32:
check(S + A - P, -(1LL << 31), 1LL << 31);
*(ub32 *)loc = S + A - P;
break;
case R_390_PC64:
*(ub64 *)loc = S + A - P;
break;
case R_390_PC16DBL:
case R_390_PLT16DBL:
check_dbl(S + A - P, -(1 << 16), 1 << 16);
*(ub16 *)loc = (S + A - P) >> 1;
break;
case R_390_PC24DBL:
case R_390_PLT24DBL:
check_dbl(S + A - P, -(1 << 24), 1 << 24);
*(ub32 *)loc |= bits(S + A - P, 24, 1);
break;
case R_390_PC32DBL:
case R_390_PLT32DBL:
check_dbl(S + A - P, -(1LL << 32), 1LL << 32);
*(ub32 *)loc = (S + A - P) >> 1;
break;
case R_390_GOT12:
case R_390_GOTPLT12:
check(G + A, 0, 1 << 12);
*(ul16 *)loc |= bits(G + A, 11, 0);
break;
case R_390_GOT16:
case R_390_GOTPLT16:
check(G + A, 0, 1 << 16);
*(ub16 *)loc = G + A;
break;
case R_390_GOT20:
case R_390_GOTPLT20:
check(G + A, 0, 1 << 20);
write_mid20(loc, G + A);
break;
case R_390_GOT32:
case R_390_GOTPLT32:
check(G + A, 0, 1LL << 32);
*(ub32 *)loc = G + A;
break;
case R_390_GOT64:
case R_390_GOTPLT64:
*(ub64 *)loc = G + A;
break;
case R_390_GOTOFF16:
case R_390_PLTOFF16:
check(S + A - GOT, -(1 << 15), 1 << 15);
*(ub16 *)loc = S + A - GOT;
break;
case R_390_GOTOFF32:
case R_390_PLTOFF32:
check(S + A - GOT, -(1LL << 31), 1LL << 31);
*(ub32 *)loc = S + A - GOT;
break;
case R_390_GOTOFF64:
case R_390_PLTOFF64:
*(ub64 *)loc = S + A - GOT;
break;
case R_390_GOTPC:
*(ub64 *)loc = GOT + A - P;
break;
case R_390_GOTPCDBL:
check_dbl(GOT + A - P, -(1LL << 32), 1LL << 32);
*(ub32 *)loc = (GOT + A - P) >> 1;
break;
case R_390_GOTENT:
check(GOT + G + A - P, -(1LL << 32), 1LL << 32);
*(ub32 *)loc = (GOT + G + A - P) >> 1;
break;
case R_390_TLS_LE32:
*(ub32 *)loc = S + A - ctx.tp_addr;
break;
case R_390_TLS_LE64:
*(ub64 *)loc = S + A - ctx.tp_addr;
break;
case R_390_TLS_GOTIE20:
write_mid20(loc, sym.get_gottp_addr(ctx) + A - GOT);
break;
case R_390_TLS_IEENT:
*(ub32 *)loc = (sym.get_gottp_addr(ctx) + A - P) >> 1;
break;
case R_390_TLS_GD32:
if (sym.has_tlsgd(ctx))
*(ub32 *)loc = sym.get_tlsgd_addr(ctx) + A - GOT;
else if (sym.has_gottp(ctx))
*(ub32 *)loc = sym.get_gottp_addr(ctx) + A - GOT;
else
*(ub32 *)loc = S + A - ctx.tp_addr;
break;
case R_390_TLS_GD64:
if (sym.has_tlsgd(ctx))
*(ub64 *)loc = sym.get_tlsgd_addr(ctx) + A - GOT;
else if (sym.has_gottp(ctx))
*(ub64 *)loc = sym.get_gottp_addr(ctx) + A - GOT;
else
*(ub64 *)loc = S + A - ctx.tp_addr;
break;
case R_390_TLS_GDCALL:
if (sym.has_tlsgd(ctx)) {
// do nothing
} else if (sym.has_gottp(ctx)) {
// lg %r2, 0(%r2, %r12)
static u8 insn[] = { 0xe3, 0x22, 0xc0, 0x00, 0x00, 0x04 };
memcpy(loc, insn, sizeof(insn));
} else {
// nop
static u8 insn[] = { 0xc0, 0x04, 0x00, 0x00, 0x00, 0x00 };
memcpy(loc, insn, sizeof(insn));
}
break;
case R_390_TLS_LDM32:
if (ctx.got->has_tlsld(ctx))
*(ub32 *)loc = ctx.got->get_tlsld_addr(ctx) + A - GOT;
break;
case R_390_TLS_LDM64:
if (ctx.got->has_tlsld(ctx))
*(ub64 *)loc = ctx.got->get_tlsld_addr(ctx) + A - GOT;
break;
case R_390_TLS_LDO32:
if (ctx.got->has_tlsld(ctx))
*(ub32 *)loc = S + A - ctx.dtp_addr;
else
*(ub32 *)loc = S + A - ctx.tp_addr;
break;
case R_390_TLS_LDO64:
if (ctx.got->has_tlsld(ctx))
*(ub64 *)loc = S + A - ctx.dtp_addr;
else
*(ub64 *)loc = S + A - ctx.tp_addr;
break;
case R_390_TLS_LDCALL:
if (!ctx.got->has_tlsld(ctx)) {
// nop
static u8 insn[] = { 0xc0, 0x04, 0x00, 0x00, 0x00, 0x00 };
memcpy(loc, insn, sizeof(insn));
}
break;
default:
unreachable();
}
}
}
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
auto check = [&](i64 val, i64 lo, i64 hi) {
if (val < lo || hi <= val)
Error(ctx) << *this << ": relocation " << rel << " against "
<< sym << " out of range: " << val << " is not in ["
<< lo << ", " << hi << ")";
};
SectionFragment<E> *frag;
i64 frag_addend;
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
u64 A = frag ? frag_addend : (i64)rel.r_addend;
switch (rel.r_type) {
case R_390_32:
check(S + A, 0, 1LL << 32);
*(ub32 *)loc = S + A;
break;
case R_390_64:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ub64 *)loc = *val;
else
*(ub64 *)loc = S + A;
break;
case R_390_TLS_LDO64:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ub64 *)loc = *val;
else
*(ub64 *)loc = S + A - ctx.dtp_addr;
break;
default:
Fatal(ctx) << *this << ": apply_reloc_nonalloc: " << rel;
}
}
}
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<const ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (sym.is_ifunc())
sym.flags |= NEEDS_GOT | NEEDS_PLT;
switch (rel.r_type) {
case R_390_64:
scan_dyn_absrel(ctx, sym, rel);
break;
case R_390_8:
case R_390_12:
case R_390_16:
case R_390_20:
case R_390_32:
scan_absrel(ctx, sym, rel);
break;
case R_390_PC16:
case R_390_PC16DBL:
case R_390_PC32:
case R_390_PC32DBL:
case R_390_PC64:
scan_pcrel(ctx, sym, rel);
break;
case R_390_GOT12:
case R_390_GOT16:
case R_390_GOT20:
case R_390_GOT32:
case R_390_GOT64:
case R_390_GOTOFF16:
case R_390_GOTOFF32:
case R_390_GOTOFF64:
case R_390_GOTPLT12:
case R_390_GOTPLT16:
case R_390_GOTPLT20:
case R_390_GOTPLT32:
case R_390_GOTPLT64:
case R_390_GOTPC:
case R_390_GOTPCDBL:
case R_390_GOTENT:
sym.flags |= NEEDS_GOT;
break;
case R_390_PLT12DBL:
case R_390_PLT16DBL:
case R_390_PLT24DBL:
case R_390_PLT32:
case R_390_PLT32DBL:
case R_390_PLT64:
case R_390_PLTOFF16:
case R_390_PLTOFF32:
case R_390_PLTOFF64:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_390_TLS_GOTIE20:
case R_390_TLS_IEENT:
sym.flags |= NEEDS_GOTTP;
break;
case R_390_TLS_GD32:
case R_390_TLS_GD64:
// We always want to relax calls to __tls_get_offset() in statically-
// linked executables because __tls_get_offset() in libc.a just calls
// abort().
if ((ctx.arg.relax && sym.is_tprel_linktime_const(ctx)) ||
ctx.arg.is_static) {
// Do nothing
} else if (ctx.arg.relax && sym.is_tprel_runtime_const(ctx)) {
sym.flags |= NEEDS_GOTTP;
} else {
sym.flags |= NEEDS_TLSGD;
}
break;
case R_390_TLS_LDM32:
case R_390_TLS_LDM64:
if (ctx.arg.is_static || (ctx.arg.relax && !ctx.arg.shared)) {
// Do nothing
} else {
ctx.needs_tlsld = true;
}
break;
case R_390_TLS_LE32:
case R_390_TLS_LE64:
check_tlsle(ctx, sym, rel);
break;
case R_390_TLS_LDO32:
case R_390_TLS_LDO64:
case R_390_TLS_GDCALL:
case R_390_TLS_LDCALL:
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
} // namespace mold::elf
+354
View File
@@ -0,0 +1,354 @@
// SH-4 (SuperH 4) is a 32-bit RISC ISA developed by Hitachi in the early
// '90s. Some relatively powerful systems were developed with SH-4.
// A notable example is Sega's Dreamcast game console which debuted in 1998.
// Hitachi later spun off its semiconductor division as an independent
// company, Renesas, and Renesas is still selling SH-4 processors for the
// embedded market. It has never been as popular as ARM is, and its
// popularity continues to decline though.
//
// SH-4's most distinctive feature compared to other RISC ISAs is that its
// instructions are 16 bits in length instead of more common 32 bits for
// better code density. This difference affects various aspects of its
// instruction set as shown below:
//
// - SH-4 has 16 general-purpose registers (GPRs) instead of the most
// commmon 32 GPR configuration to save one bit to specify a register.
//
// - Binary instructions such as ADD normally take three register in
// RISC ISAs (e.g. x ← y ⊕ z where x, y and z are registers), but
// SH-4's instructions take only two registers. The result of an
// operation is written to one of the source registers (e.g. x ← x ⊕ y).
//
// - Usual RISC ISAs have "load high" and "load low" instructions to set
// an immediate to most significant and least significant bits in a
// register to construct a full 32-bit value in a register. This
// technique is hard to use in SH-4, as 16 bit instructions are too
// small to contain large immediates. On SH-4, large immediates are
// loaded from memory using `mov.l` PC-relative load instruction.
//
// - Many RISC ISAs are, despite their name, actually fairly complex.
// They tend to have hundreds if not thousands of different instructions.
// SH-4 doesn't really have that many instructions because its 16-bit
// machine code simply can't encode many different opcodes. As a
// result, the number of relocations the linker has to support is also
// small.
//
// Beside these, SH-4 has a delay branch slot just like contemporary MIPS
// and SPARC. That is, one instruction after a branch instruction will
// always be executed even if the branch is taken. Delay branch slot allows
// a pipelined CPU to start and finish executing an instruction after a
// branch regardless of the branch's condition, simplifying the processor's
// implementation. It's considered a bad premature optimization nowadays,
// though. Modern RISC processors don't have it.
//
// Here are notes about the SH-4 psABI:
//
// - If a source file is compiled with -fPIC, each function starts
// with a piece of code to store the address of .got to %r12.
// We can use the register in our PLT for position-independent output.
//
// - Even though it uses the RELA-type relocations, relocation addends
// are stored not to the r_addend field but to the relocated section
// contents for some reason. Therefore, it's effectively REL.
//
// - It looks like the ecosystem has bit-rotted. Some tests, especially
// one using C++ exceptions, don't pass even with GNU ld.
//
// - GCC/SH4 tends to write dynamically-relocated data into .text, so the
// output from the linker contains lots of text relocations. That's not
// a problem with embedded programming, I guess.
#include "mold.h"
namespace mold::elf {
using E = SH4;
// Even though SH-4 uses RELA-type relocations, addends are stored to
// relocated places for some reason.
template <>
i64 get_addend(u8 *loc, const ElfRel<E> &rel) {
switch (rel.r_type) {
case R_SH_DIR32:
case R_SH_REL32:
case R_SH_TLS_GD_32:
case R_SH_TLS_LD_32:
case R_SH_TLS_LDO_32:
case R_SH_TLS_IE_32:
case R_SH_TLS_LE_32:
case R_SH_TLS_DTPMOD32:
case R_SH_TLS_DTPOFF32:
case R_SH_TLS_TPOFF32:
case R_SH_GOT32:
case R_SH_PLT32:
case R_SH_GOTOFF:
case R_SH_GOTPC:
case R_SH_GOTPLT32:
return *(ul32 *)loc;
default:
return 0;
}
}
template <>
void write_plt_header(Context<E> &ctx, u8 *buf) {
if (ctx.arg.pic) {
static const u8 insn[] = {
0x02, 0xd2, // mov.l 1f, r2
0xcc, 0x32, // add r12, r2
0x22, 0x50, // mov.l @(8, r2), r0
0x21, 0x52, // mov.l @(4, r2), r2
0x2b, 0x40, // jmp @r0
0x00, 0xe0, // mov #0, r0
0, 0, 0, 0, // 1: .long GOTPLT
};
static_assert(sizeof(insn) == E::plt_hdr_size);
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 12) = ctx.gotplt->shdr.sh_addr - ctx.got->shdr.sh_addr;
} else {
static const u8 insn[] = {
0x02, 0xd2, // mov.l 1f, r2
0x22, 0x50, // mov.l @(8, r2), r0
0x21, 0x52, // mov.l @(4, r2), r2
0x2b, 0x40, // jmp @r0
0x00, 0xe0, // mov #0, r0
0x09, 0x00, // nop
0, 0, 0, 0, // 1: .long GOTPLT
};
static_assert(sizeof(insn) == E::plt_hdr_size);
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 12) = ctx.gotplt->shdr.sh_addr;
}
}
template <>
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
if (ctx.arg.pic) {
static const u8 insn[] = {
0x01, 0xd0, // mov.l 1f, r0
0xce, 0x00, // mov.l @(r0, r12), r0
0x2b, 0x40, // jmp @r0
0x01, 0xd1, // mov.l 2f, r1
0, 0, 0, 0, // 1: .long GOTPLT_ENTRY
0, 0, 0, 0, // 2: .long INDEX_IN_RELPLT
};
static_assert(sizeof(insn) == E::plt_size);
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 8) = sym.get_gotplt_addr(ctx) - ctx.got->shdr.sh_addr;
*(ul32 *)(buf + 12) = sym.get_plt_idx(ctx) * sizeof(ElfRel<E>);
} else {
static const u8 insn[] = {
0x01, 0xd0, // mov.l 1f, r0
0x02, 0x60, // mov.l @r0, r0
0x2b, 0x40, // jmp @r0
0x01, 0xd1, // mov.l 2f, r1
0, 0, 0, 0, // 1: .long GOTPLT_ENTRY
0, 0, 0, 0, // 2: .long INDEX_IN_RELPLT
};
static_assert(sizeof(insn) == E::plt_size);
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 8) = sym.get_gotplt_addr(ctx);
*(ul32 *)(buf + 12) = sym.get_plt_idx(ctx) * sizeof(ElfRel<E>);
}
}
template <>
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
if (ctx.arg.pic) {
static const u8 insn[] = {
0x01, 0xd0, // mov.l 1f, r0
0xce, 0x00, // mov.l @(r0, r12), r0
0x2b, 0x40, // jmp @r0
0x09, 0x00, // nop
0, 0, 0, 0, // 1: .long GOT_ENTRY
};
static_assert(sizeof(insn) == E::pltgot_size);
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 8) = sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr;
} else {
static const u8 insn[] = {
0x01, 0xd0, // mov.l 1f, r0
0x02, 0x60, // mov.l @r0, r0
0x2b, 0x40, // jmp @r0
0x09, 0x00, // nop
0, 0, 0, 0, // 1: .long GOT_ENTRY
};
static_assert(sizeof(insn) == E::pltgot_size);
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 8) = sym.get_got_addr(ctx);
}
}
template <>
void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_NONE:
break;
case R_SH_DIR32:
*(ul32 *)loc = val;
break;
case R_SH_REL32:
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
break;
default:
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
ElfRel<E> *dynrel = nullptr;
if (ctx.reldyn)
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
file.reldyn_offset + this->reldyn_offset);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
u64 S = sym.get_addr(ctx);
u64 A = get_addend(loc, rel);
u64 P = get_addr() + rel.r_offset;
u64 G = sym.get_got_idx(ctx) * sizeof(Word<E>);
u64 GOT = ctx.got->shdr.sh_addr;
switch (rel.r_type) {
case R_SH_DIR32:
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_SH_REL32:
case R_SH_PLT32:
*(ul32 *)loc = S + A - P;
break;
case R_SH_GOT32:
*(ul32 *)loc = G;
break;
case R_SH_GOTPC:
*(ul32 *)loc = GOT + A - P;
break;
case R_SH_GOTOFF:
*(ul32 *)loc = S + A - GOT;
break;
case R_SH_TLS_GD_32:
*(ul32 *)loc = sym.get_tlsgd_addr(ctx) + A - GOT;
break;
case R_SH_TLS_LD_32:
*(ul32 *)loc = ctx.got->get_tlsld_addr(ctx) + A - GOT;
break;
case R_SH_TLS_LDO_32:
*(ul32 *)loc = S + A - ctx.dtp_addr;
break;
case R_SH_TLS_IE_32:
*(ul32 *)loc = sym.get_gottp_addr(ctx) + A - GOT;
break;
case R_SH_TLS_LE_32:
*(ul32 *)loc = S + A - ctx.tp_addr;
break;
default:
unreachable();
}
}
}
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
SectionFragment<E> *frag;
i64 frag_addend;
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
u64 A = frag ? frag_addend : get_addend(loc, rel);
switch (rel.r_type) {
case R_SH_DIR32:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ul32 *)loc = *val;
else
*(ul32 *)loc = S + A;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
<< rel;
}
}
}
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (sym.is_ifunc())
Error(ctx) << sym << ": GNU ifunc symbol is not supported on sh4";
switch (rel.r_type) {
case R_SH_DIR32:
scan_dyn_absrel(ctx, sym, rel);
break;
case R_SH_REL32:
scan_pcrel(ctx, sym, rel);
break;
case R_SH_GOT32:
sym.flags |= NEEDS_GOT;
break;
case R_SH_PLT32:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_SH_TLS_GD_32:
sym.flags |= NEEDS_TLSGD;
break;
case R_SH_TLS_LD_32:
ctx.needs_tlsld = true;
break;
case R_SH_TLS_IE_32:
sym.flags |= NEEDS_GOTTP;
break;
case R_SH_TLS_LE_32:
check_tlsle(ctx, sym, rel);
break;
case R_SH_GOTPC:
case R_SH_GOTOFF:
case R_SH_TLS_LDO_32:
break;
default:
Fatal(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
} // namespace mold::elf
+618
View File
@@ -0,0 +1,618 @@
// SPARC is a RISC ISA developed by Sun Microsystems.
//
// The byte order of the processor is big-endian. Anything larger than a
// byte is stored in the "reverse" order compared to little-endian
// processors such as x86-64.
//
// All instructions are 4 bytes long and aligned to 4 bytes boundaries.
//
// A notable feature of SPARC is that, unlike other RISC ISAs, it doesn't
// need range extension thunks. It is because the SPARC's CALL instruction
// contains a whopping 30 bits immediate. The processor scales it by 4 to
// extend it to 32 bits (this is doable because all instructions are
// aligned to 4 bytes boundaries, so the least significant two bits are
// always zero). That means CALL's reach is PC ± 2 GiB, elinating the
// need of range extension thunks. It comes with the cost that the CALL
// instruction alone takes 1/4th of the instruction encoding space,
// though.
//
// SPARC has 32 general purpose registers. CALL instruction saves a return
// address to %o7, which is an alias for %r15. Thread pointer is stored to
// %g7 which is %r7.
//
// SPARC does not have PC-relative load/store instructions. To access data
// in the position-independent manner, we usually first set the address of
// .got to, for example, %l7, with the following piece of code
//
// sethi %hi(. - _GLOBAL_OFFSET_TABLE_), %l7
// add %l7, %lo(. - _GLOBAL_OFFSET_TABLE_), %l7
// call __sparc_get_pc_thunk.l7
// nop
//
// where __sparc_get_pc_thunk.l7 is defined as
//
// retl
// add %o7, %l7, %l7
//
// . SETHI and the following ADD materialize a 32 bits offset to .got.
// CALL instruction sets a return address to $o7, and the subsequent ADD
// adds it to the GOT offset to materialize the absolute address of .got.
//
// Note that we have a NOP after CALL and an ADD after RETL because of
// SPARC's delay branch slots. That is, the SPARC processor always
// executes one instruction after a branch even if the branch is taken.
// This may seem like an odd behavior, and indeed it is considered as such
// (that's a premature optimization for the early pipelined SPARC
// processors), but that's been a part of the ISA's spec so that's what it
// is.
//
// Note also that the .got address obtained this way is not shared between
// functions, so functions can use an arbitrary register to hold the .got
// address. That also means each function needs to execute the above piece
// of code to become position-independent.
//
// This scheme is very similar to i386. That may not be a coincidence
// because the i386 ELF psABI is created by Sun Microsystems too.
//
// https://github.com/rui314/psabi/blob/main/sparc.pdf
#include "mold.h"
namespace mold::elf {
using E = SPARC64;
// SPARC's PLT section is writable despite containing executable code.
// We don't need to write the PLT header entry because the dynamic loader
// will do that for us.
//
// We also don't need a .got.plt section to store the result of lazy PLT
// symbol resolution because the dynamic symbol resolver directly mutates
// instructions in PLT so that they jump to the right places next time.
// That's why each PLT entry contains lots of NOPs; they are a placeholder
// for the runtime to add more instructions.
//
// Self-modifying code is nowadays considered really bad from the security
// point of view, though.
template <>
void write_plt_header(Context<E> &ctx, u8 *buf) {
memset(buf, 0, E::plt_hdr_size);
}
template <>
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
static ub32 insn[] = {
0x0300'0000, // sethi (. - .PLT0), %g1
0x3068'0000, // ba,a %xcc, .PLT1
0x0100'0000, // nop
0x0100'0000, // nop
0x0100'0000, // nop
0x0100'0000, // nop
0x0100'0000, // nop
0x0100'0000, // nop
};
u64 plt0 = ctx.plt->shdr.sh_addr;
u64 plt1 = ctx.plt->shdr.sh_addr + E::plt_size;
u64 entry = sym.get_plt_addr(ctx);
memcpy(buf, insn, sizeof(insn));
*(ub32 *)buf |= bits(entry - plt0, 21, 0);
*(ub32 *)(buf + 4) |= bits(plt1 - entry - 4, 20, 2);
}
template <>
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
static ub32 entry[] = {
0x8a10'000f, // mov %o7, %g5
0x4000'0002, // call . + 8
0xc25b'e014, // ldx [ %o7 + 20 ], %g1
0xc25b'c001, // ldx [ %o7 + %g1 ], %g1
0x81c0'4000, // jmp %g1
0x9e10'0005, // mov %g5, %o7
0x0000'0000, // .quad $plt_entry - $got_entry
0x0000'0000,
};
memcpy(buf, entry, sizeof(entry));
*(ub64 *)(buf + 24) = sym.get_got_addr(ctx) - sym.get_plt_addr(ctx) - 4;
}
template <>
void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_NONE:
break;
case R_SPARC_64:
case R_SPARC_UA64:
*(ub64 *)loc = val;
break;
case R_SPARC_DISP32:
*(ub32 *)loc = val - this->shdr.sh_addr - offset;
break;
default:
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
ElfRel<E> *dynrel = nullptr;
if (ctx.reldyn)
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
file.reldyn_offset + this->reldyn_offset);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
auto check = [&](i64 val, i64 lo, i64 hi) {
if (val < lo || hi <= val)
Error(ctx) << *this << ": relocation " << rel << " against "
<< sym << " out of range: " << val << " is not in ["
<< lo << ", " << hi << ")";
};
u64 S = sym.get_addr(ctx);
u64 A = rel.r_addend;
u64 P = (get_addr() + rel.r_offset);
u64 G = (sym.get_got_idx(ctx) * sizeof(Word<E>));
u64 GOT = ctx.got->shdr.sh_addr;
switch (rel.r_type) {
case R_SPARC_64:
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_SPARC_5:
check(S + A, 0, 1 << 5);
*(ub32 *)loc |= bits(S + A, 4, 0);
break;
case R_SPARC_6:
check(S + A, 0, 1 << 6);
*(ub32 *)loc |= bits(S + A, 5, 0);
break;
case R_SPARC_7:
check(S + A, 0, 1 << 7);
*(ub32 *)loc |= bits(S + A, 6, 0);
break;
case R_SPARC_8:
check(S + A, 0, 1 << 8);
*(u8 *)loc = S + A;
break;
case R_SPARC_10:
check(S + A, 0, 1 << 10);
*(ub32 *)loc |= bits(S + A, 9, 0);
break;
case R_SPARC_LO10:
case R_SPARC_LOPLT10:
*(ub32 *)loc |= bits(S + A, 9, 0);
break;
case R_SPARC_11:
check(S + A, 0, 1 << 11);
*(ub32 *)loc |= bits(S + A, 10, 0);
break;
case R_SPARC_13:
check(S + A, 0, 1 << 13);
*(ub32 *)loc |= bits(S + A, 12, 0);
break;
case R_SPARC_16:
case R_SPARC_UA16:
check(S + A, 0, 1 << 16);
*(ub16 *)loc = S + A;
break;
case R_SPARC_22:
check(S + A, 0, 1 << 22);
*(ub32 *)loc |= bits(S + A, 21, 0);
break;
case R_SPARC_32:
case R_SPARC_UA32:
case R_SPARC_PLT32:
check(S + A, 0, 1LL << 32);
*(ub32 *)loc = S + A;
break;
case R_SPARC_PLT64:
case R_SPARC_UA64:
case R_SPARC_REGISTER:
*(ub64 *)loc = S + A;
break;
case R_SPARC_DISP8:
check(S + A - P, -(1 << 7), 1 << 7);
*(u8 *)loc = S + A - P;
break;
case R_SPARC_DISP16:
check(S + A - P, -(1 << 15), 1 << 15);
*(ub16 *)loc = S + A - P;
break;
case R_SPARC_DISP32:
case R_SPARC_PCPLT32:
check(S + A - P, -(1LL << 31), 1LL << 31);
*(ub32 *)loc = S + A - P;
break;
case R_SPARC_DISP64:
*(ub64 *)loc = S + A - P;
break;
case R_SPARC_WDISP16: {
i64 val = S + A - P;
check(val, -(1 << 16), 1 << 16);
*(ub16 *)loc |= (bit(val, 16) << 21) | bits(val, 15, 2);
break;
}
case R_SPARC_WDISP19:
check(S + A - P, -(1 << 20), 1 << 20);
*(ub32 *)loc |= bits(S + A - P, 20, 2);
break;
case R_SPARC_WDISP22:
check(S + A - P, -(1 << 23), 1 << 23);
*(ub32 *)loc |= bits(S + A - P, 23, 2);
break;
case R_SPARC_WDISP30:
case R_SPARC_WPLT30:
check(S + A - P, -(1LL << 31), 1LL << 31);
*(ub32 *)loc |= bits(S + A - P, 31, 2);
break;
case R_SPARC_HI22:
case R_SPARC_HIPLT22:
case R_SPARC_LM22:
*(ub32 *)loc |= bits(S + A, 31, 10);
break;
case R_SPARC_GOT10:
*(ub32 *)loc |= bits(G, 9, 0);
break;
case R_SPARC_GOT13:
check(G, 0, 1 << 12);
*(ub32 *)loc |= bits(G, 12, 0);
break;
case R_SPARC_GOT22:
*(ub32 *)loc |= bits(G, 31, 10);
break;
case R_SPARC_GOTDATA_HIX22: {
i64 val = S + A - GOT;
*(ub32 *)loc |= bits(val < 0 ? ~val : val, 31, 10);
break;
}
case R_SPARC_GOTDATA_LOX10: {
i64 val = S + A - GOT;
*(ub32 *)loc |= bits(val, 9, 0) | (val < 0 ? 0b1'1100'0000'0000 : 0);
break;
}
case R_SPARC_GOTDATA_OP_HIX22:
// We always have to relax a GOT load to a load immediate if a
// symbol is local, because R_SPARC_GOTDATA_OP cannot represent
// an addend for a local symbol.
if (sym.is_absolute()) {
i64 val = S + A;
*(ub32 *)loc |= bits(val < 0 ? ~val : val, 31, 10);
} else if (sym.is_pcrel_linktime_const(ctx)) {
i64 val = S + A - GOT;
*(ub32 *)loc |= bits(val < 0 ? ~val : val, 31, 10);
} else {
*(ub32 *)loc |= bits(G, 31, 10);
}
break;
case R_SPARC_GOTDATA_OP_LOX10:
if (sym.is_absolute()) {
i64 val = S + A;
*(ub32 *)loc |= bits(val, 9, 0) | (val < 0 ? 0b1'1100'0000'0000 : 0);
} else if (sym.is_pcrel_linktime_const(ctx)) {
i64 val = S + A - GOT;
*(ub32 *)loc |= bits(val, 9, 0) | (val < 0 ? 0b1'1100'0000'0000 : 0);
} else {
*(ub32 *)loc |= bits(G, 9, 0);
}
break;
case R_SPARC_GOTDATA_OP:
if (sym.is_absolute()) {
// ldx [ %g2 + %g1 ], %g1 → nop
*(ub32 *)loc = 0x0100'0000;
} else if (sym.is_pcrel_linktime_const(ctx)) {
// ldx [ %g2 + %g1 ], %g1 → add %g2, %g1, %g1
*(ub32 *)loc &= 0b00'11111'000000'11111'1'11111111'11111;
*(ub32 *)loc |= 0b10'00000'000000'00000'0'00000000'00000;
}
break;
case R_SPARC_PC10:
case R_SPARC_PCPLT10:
*(ub32 *)loc |= bits(S + A - P, 9, 0);
break;
case R_SPARC_PC22:
case R_SPARC_PCPLT22:
case R_SPARC_PC_LM22:
*(ub32 *)loc |= bits(S + A - P, 31, 10);
break;
case R_SPARC_OLO10:
*(ub32 *)loc |= bits(bits(S + A, 9, 0) + rel.r_type_data, 12, 0);
break;
case R_SPARC_HH22:
*(ub32 *)loc |= bits(S + A, 63, 42);
break;
case R_SPARC_HM10:
*(ub32 *)loc |= bits(S + A, 41, 32);
break;
case R_SPARC_PC_HH22:
*(ub32 *)loc |= bits(S + A - P, 63, 42);
break;
case R_SPARC_PC_HM10:
*(ub32 *)loc |= bits(S + A - P, 41, 32);
break;
case R_SPARC_HIX22:
*(ub32 *)loc |= bits(~(S + A), 31, 10);
break;
case R_SPARC_LOX10:
*(ub32 *)loc |= bits(S + A, 9, 0) | 0b1'1100'0000'0000;
break;
case R_SPARC_H44:
*(ub32 *)loc |= bits(S + A, 43, 22);
break;
case R_SPARC_M44:
*(ub32 *)loc |= bits(S + A, 21, 12);
break;
case R_SPARC_L44:
*(ub32 *)loc |= bits(S + A, 11, 0);
break;
case R_SPARC_TLS_GD_HI22:
*(ub32 *)loc |= bits(sym.get_tlsgd_addr(ctx) + A - GOT, 31, 10);
break;
case R_SPARC_TLS_GD_LO10:
*(ub32 *)loc |= bits(sym.get_tlsgd_addr(ctx) + A - GOT, 9, 0);
break;
case R_SPARC_TLS_GD_CALL:
case R_SPARC_TLS_LDM_CALL: {
u64 addr;
if (ctx.arg.is_static)
addr = ctx.extra.tls_get_addr_sec->shdr.sh_addr;
else
addr = ctx.extra.tls_get_addr_sym->get_addr(ctx);
*(ub32 *)loc |= bits(addr + A - P, 31, 2);
break;
}
case R_SPARC_TLS_LDM_HI22:
*(ub32 *)loc |= bits(ctx.got->get_tlsld_addr(ctx) + A - GOT, 31, 10);
break;
case R_SPARC_TLS_LDM_LO10:
*(ub32 *)loc |= bits(ctx.got->get_tlsld_addr(ctx) + A - GOT, 9, 0);
break;
case R_SPARC_TLS_LDO_HIX22:
*(ub32 *)loc |= bits(S + A - ctx.dtp_addr, 31, 10);
break;
case R_SPARC_TLS_LDO_LOX10:
*(ub32 *)loc |= bits(S + A - ctx.dtp_addr, 9, 0);
break;
case R_SPARC_TLS_IE_HI22:
*(ub32 *)loc |= bits(sym.get_gottp_addr(ctx) + A - GOT, 31, 10);
break;
case R_SPARC_TLS_IE_LO10:
*(ub32 *)loc |= bits(sym.get_gottp_addr(ctx) + A - GOT, 9, 0);
break;
case R_SPARC_TLS_LE_HIX22:
*(ub32 *)loc |= bits(~(S + A - ctx.tp_addr), 31, 10);
break;
case R_SPARC_TLS_LE_LOX10:
*(ub32 *)loc |= bits(S + A - ctx.tp_addr, 9, 0) | 0b1'1100'0000'0000;
break;
case R_SPARC_SIZE32:
*(ub32 *)loc = sym.esym().st_size + A;
break;
case R_SPARC_TLS_GD_ADD:
case R_SPARC_TLS_LDM_ADD:
case R_SPARC_TLS_LDO_ADD:
case R_SPARC_TLS_IE_LD:
case R_SPARC_TLS_IE_LDX:
case R_SPARC_TLS_IE_ADD:
break;
default:
unreachable();
}
}
}
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
auto check = [&](i64 val, i64 lo, i64 hi) {
if (val < lo || hi <= val)
Error(ctx) << *this << ": relocation " << rel << " against "
<< sym << " out of range: " << val << " is not in ["
<< lo << ", " << hi << ")";
};
SectionFragment<E> *frag;
i64 frag_addend;
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
u64 A = frag ? frag_addend : (i64)rel.r_addend;
switch (rel.r_type) {
case R_SPARC_64:
case R_SPARC_UA64:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ub64 *)loc = *val;
else
*(ub64 *)loc = S + A;
break;
case R_SPARC_32:
case R_SPARC_UA32: {
i64 val = S + A;
check(val, 0, 1LL << 32);
*(ub32 *)loc = val;
break;
}
case R_SPARC_TLS_DTPOFF32:
*(ub32 *)loc = S + A - ctx.dtp_addr;
break;
case R_SPARC_TLS_DTPOFF64:
*(ub64 *)loc = S + A - ctx.dtp_addr;
break;
default:
Fatal(ctx) << *this << ": apply_reloc_nonalloc: " << rel;
}
}
}
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<const ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (sym.is_ifunc())
sym.flags |= NEEDS_GOT | NEEDS_PLT;
switch (rel.r_type) {
case R_SPARC_64:
scan_dyn_absrel(ctx, sym, rel);
break;
case R_SPARC_8:
case R_SPARC_5:
case R_SPARC_6:
case R_SPARC_7:
case R_SPARC_10:
case R_SPARC_11:
case R_SPARC_13:
case R_SPARC_16:
case R_SPARC_22:
case R_SPARC_32:
case R_SPARC_REGISTER:
case R_SPARC_UA16:
case R_SPARC_UA32:
case R_SPARC_UA64:
case R_SPARC_PC_HM10:
case R_SPARC_OLO10:
case R_SPARC_LOX10:
case R_SPARC_HM10:
case R_SPARC_M44:
case R_SPARC_HIX22:
case R_SPARC_LO10:
case R_SPARC_L44:
case R_SPARC_LM22:
case R_SPARC_HI22:
case R_SPARC_H44:
case R_SPARC_HH22:
scan_absrel(ctx, sym, rel);
break;
case R_SPARC_PLT32:
case R_SPARC_WPLT30:
case R_SPARC_WDISP30:
case R_SPARC_HIPLT22:
case R_SPARC_LOPLT10:
case R_SPARC_PCPLT32:
case R_SPARC_PCPLT22:
case R_SPARC_PCPLT10:
case R_SPARC_PLT64:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_SPARC_GOT13:
case R_SPARC_GOT10:
case R_SPARC_GOT22:
case R_SPARC_GOTDATA_HIX22:
sym.flags |= NEEDS_GOT;
break;
case R_SPARC_GOTDATA_OP_HIX22:
if (sym.is_imported)
sym.flags |= NEEDS_GOT;
break;
case R_SPARC_DISP16:
case R_SPARC_DISP32:
case R_SPARC_DISP64:
case R_SPARC_DISP8:
case R_SPARC_PC10:
case R_SPARC_PC22:
case R_SPARC_PC_LM22:
case R_SPARC_WDISP16:
case R_SPARC_WDISP19:
case R_SPARC_WDISP22:
case R_SPARC_PC_HH22:
scan_pcrel(ctx, sym, rel);
break;
case R_SPARC_TLS_GD_HI22:
sym.flags |= NEEDS_TLSGD;
break;
case R_SPARC_TLS_LDM_HI22:
ctx.needs_tlsld = true;
break;
case R_SPARC_TLS_IE_HI22:
sym.flags |= NEEDS_GOTTP;
break;
case R_SPARC_TLS_GD_CALL:
case R_SPARC_TLS_LDM_CALL:
if (!ctx.arg.is_static)
if (Symbol<E> &sym = *ctx.extra.tls_get_addr_sym; sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_SPARC_TLS_LE_HIX22:
case R_SPARC_TLS_LE_LOX10:
check_tlsle(ctx, sym, rel);
break;
case R_SPARC_GOTDATA_OP_LOX10:
case R_SPARC_GOTDATA_OP:
case R_SPARC_GOTDATA_LOX10:
case R_SPARC_TLS_GD_LO10:
case R_SPARC_TLS_GD_ADD:
case R_SPARC_TLS_LDM_LO10:
case R_SPARC_TLS_LDM_ADD:
case R_SPARC_TLS_LDO_HIX22:
case R_SPARC_TLS_LDO_LOX10:
case R_SPARC_TLS_LDO_ADD:
case R_SPARC_TLS_IE_ADD:
case R_SPARC_TLS_IE_LD:
case R_SPARC_TLS_IE_LDX:
case R_SPARC_TLS_IE_LO10:
case R_SPARC_SIZE32:
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
// __tls_get_addr is not defined by libc.a, so we can't use that function
// in statically-linked executables. This section provides a replacement.
void SparcTlsGetAddrSection::copy_buf(Context<E> &ctx) {
ub32 *buf = (ub32 *)(ctx.buf + this->shdr.sh_offset);
static const ub32 insn[] = {
0x0300'0000, // sethi %hi(TP_SIZE), %g1
0x8210'6000, // or %g1, %lo(TP_SIZE), %g1
0x8221'c001, // sub %g7, %g1, %g1
0xd05a'2008, // ldx [ %o0 + 8 ], %o0
0x81c3'e008, // retl
0x9000'4008, // add %g1, %o0, %o0
};
assert(this->shdr.sh_size == sizeof(insn));
memcpy(buf, insn, sizeof(insn));
buf[0] |= bits(ctx.tp_addr - ctx.tls_begin, 31, 10);
buf[1] |= bits(ctx.tp_addr - ctx.tls_begin, 9, 0);
}
} // namespace mold::elf
+451 -519
View File
File diff suppressed because it is too large. Load diff
+659 -497
View File
File diff suppressed because it is too large. Load diff
+1043
View File
File diff suppressed because it is too large. Load diff
+2074 -1278
View File
File diff suppressed because it is too large. Load diff
+34 -65
View File
@@ -10,73 +10,68 @@
namespace mold::elf {
template <typename E>
static bool is_init_fini(const InputSection<E> &isec) {
static bool should_keep(const InputSection<E> &isec) {
u32 type = isec.shdr().sh_type;
u32 flags = isec.shdr().sh_flags;
std::string_view name = isec.name();
return type == SHT_INIT_ARRAY ||
return (flags & SHF_GNU_RETAIN) ||
type == SHT_NOTE ||
type == SHT_INIT_ARRAY ||
type == SHT_FINI_ARRAY ||
type == SHT_PREINIT_ARRAY ||
(std::is_same_v<E, ARM32> && type == SHT_ARM_EXIDX) ||
(is_arm32<E> && type == SHT_ARM_EXIDX) ||
name.starts_with(".ctors") ||
name.starts_with(".dtors") ||
name.starts_with(".init") ||
name.starts_with(".fini");
name.starts_with(".fini") ||
is_c_identifier(name);
}
template <typename E>
static bool mark_section(InputSection<E> *isec) {
return isec && isec->is_alive && !isec->extra().is_visited.exchange(true);
return isec && isec->is_alive && !isec->is_visited.test_and_set();
}
template <typename E>
static void visit(Context<E> &ctx, InputSection<E> *isec,
tbb::feeder<InputSection<E> *> &feeder, i64 depth) {
assert(isec->extra().is_visited);
// A relocation can refer either a section fragment (i.e. a piece of
// string in a mergeable string section) or a symbol. Mark all
// section fragments as alive.
if (SectionFragmentRef<E> *refs = isec->rel_fragments.get())
for (i64 i = 0; refs[i].idx >= 0; i++)
refs[i].frag->is_alive.store(true, std::memory_order_relaxed);
assert(isec->is_visited);
// If this is a text section, .eh_frame may contain records
// describing how to handle exceptions for that function.
// We want to keep associated .eh_frame records.
for (FdeRecord<E> &fde : isec->get_fdes())
for (ElfRel<E> &rel : fde.get_rels(isec->file).subspan(1))
for (const ElfRel<E> &rel : fde.get_rels(isec->file).subspan(1))
if (Symbol<E> *sym = isec->file.symbols[rel.r_sym])
if (mark_section(sym->get_input_section()))
feeder.add(sym->get_input_section());
for (ElfRel<E> &rel : isec->get_rels(ctx)) {
for (const ElfRel<E> &rel : isec->get_rels(ctx)) {
Symbol<E> &sym = *isec->file.symbols[rel.r_sym];
// Symbol can refer either a section fragment or an input section.
// Mark a fragment as alive.
if (SectionFragment<E> *frag = sym.get_frag()) {
frag->is_alive.store(true, std::memory_order_relaxed);
frag->is_alive = true;
continue;
}
if (!mark_section(sym.get_input_section()))
continue;
// Mark a section alive. For better performacne, we don't call
// `feeder.add` too often.
if (depth < 3)
visit(ctx, sym.get_input_section(), feeder, depth + 1);
else
feeder.add(sym.get_input_section());
if (mark_section(sym.get_input_section())) {
if (depth < 3)
visit(ctx, sym.get_input_section(), feeder, depth + 1);
else
feeder.add(sym.get_input_section());
}
}
}
template <typename E>
static tbb::concurrent_vector<InputSection<E> *>
collect_root_set(Context<E> &ctx) {
static void collect_root_set(Context<E> &ctx,
tbb::concurrent_vector<InputSection<E> *> &rootset) {
Timer t(ctx, "collect_root_set");
tbb::concurrent_vector<InputSection<E> *> rootset;
auto enqueue_section = [&](InputSection<E> *isec) {
if (mark_section(isec))
@@ -86,7 +81,7 @@ collect_root_set(Context<E> &ctx) {
auto enqueue_symbol = [&](Symbol<E> *sym) {
if (sym) {
if (SectionFragment<E> *frag = sym->get_frag())
frag->is_alive.store(true, std::memory_order_relaxed);
frag->is_alive = true;
else
enqueue_section(sym->get_input_section());
}
@@ -98,15 +93,15 @@ collect_root_set(Context<E> &ctx) {
if (!isec || !isec->is_alive)
continue;
// -gc-sections discards only SHF_ALLOC sections. If you want to
// --gc-sections discards only SHF_ALLOC sections. If you want to
// reduce the amount of non-memory-mapped segments, you should
// use `strip` command, compile without debug info or use
// -strip-all linker option.
if (!(isec->shdr().sh_flags & SHF_ALLOC))
isec->extra().is_visited = true;
// --strip-all linker option.
u32 flags = isec->shdr().sh_flags;
if (!(flags & SHF_ALLOC))
isec->is_visited = true;
if (is_init_fini(*isec) || is_c_identifier(isec->name()) ||
isec->shdr().sh_type == SHT_NOTE)
if (should_keep(*isec))
enqueue_section(isec.get());
}
});
@@ -132,11 +127,9 @@ collect_root_set(Context<E> &ctx) {
// We just keep all CIEs and everything that are referenced by them.
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (CieRecord<E> &cie : file->cies)
for (ElfRel<E> &rel : cie.get_rels())
for (const ElfRel<E> &rel : cie.get_rels())
enqueue_symbol(file->symbols[rel.r_sym]);
});
return rootset;
}
// Mark all reachable sections
@@ -159,7 +152,7 @@ static void sweep(Context<E> &ctx) {
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (std::unique_ptr<InputSection<E>> &isec : file->sections) {
if (isec && isec->is_alive && !isec->extra().is_visited) {
if (isec && isec->is_alive && !isec->is_visited) {
if (ctx.arg.print_gc_sections)
SyncOut(ctx) << "removing unused section " << *isec;
isec->kill();
@@ -169,42 +162,18 @@ static void sweep(Context<E> &ctx) {
});
}
// Non-alloc section fragments are not subject of garbage collection.
// This function marks such fragments.
template <typename E>
static void mark_nonalloc_fragments(Context<E> &ctx) {
Timer t(ctx, "mark_nonalloc_fragments");
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
for (std::unique_ptr<MergeableSection<E>> &m : file->mergeable_sections)
if (m)
for (SectionFragment<E> *frag : m->fragments)
if (!(frag->output_section.shdr.sh_flags & SHF_ALLOC))
frag->is_alive.store(true, std::memory_order_relaxed);
});
}
template <typename E>
void gc_sections(Context<E> &ctx) {
Timer t(ctx, "gc");
for (ObjectFile<E> *file : ctx.objs)
file->extras.resize(file->sections.size());
mark_nonalloc_fragments(ctx);
tbb::concurrent_vector<InputSection<E> *> rootset = collect_root_set(ctx);
tbb::concurrent_vector<InputSection<E> *> rootset;
collect_root_set(ctx, rootset);
mark(ctx, rootset);
sweep(ctx);
}
#define INSTANTIATE(E) \
template void gc_sections(Context<E> &ctx);
using E = MOLD_TARGET;
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
template void gc_sections(Context<E> &ctx);
} // namespace mold::elf
+808
View File
@@ -0,0 +1,808 @@
// This file contains code to read DWARF debug info to create .gdb_index.
//
// .gdb_index is an optional section to speed up GNU debugger. It contains
// two maps: 1) a map from function/variable/type names to compunits, and
// 2) a map from function address ranges to compunits. gdb uses these
// maps to quickly find a compunit given a name or an instruction pointer.
//
// (Terminology: a compilation unit, often abbreviated as compunit or
// CU, is a unit of debug info. An input .debug_info section usually
// contains one compunit, and thus an output .debug_info contains as
// many compunits as the number of input files.)
//
// .gdb_index is not mandatory. All the information in .gdb_index is
// also in other debug info sections. You can actually create an
// executable without .gdb_index and later add it using the
// `gdb-add-index` post-processing tool that comes with gdb.
//
// Post-relocated debug section contents are needed to create a
// .gdb_index. Therefore, we create it after relocating all the other
// sections. The size of the section is also hard to estimate before
// applying relocations to debug info sections, so a .gdb_index is
// placed at the very end of the output file, even after the section
// header.
//
// The mapping from names to compunits is 1:n while the mapping from
// address ranges to compunits is 1:1. That is, two object files may
// define the same type name, while there should be no two functions
// that overlap with each other in memory.
//
// .gdb_index contains an on-disk hash table for names, so gdb can
// lookup names without loading all strings into memory and construct an
// in-memory hash table.
//
// Names are in .debug_gnu_pubnames and .debug_gnu_pubtypes input
// sections. These sections are created if `-ggnu-pubnames` is given.
// Besides names, these sections contain attributes for each name so
// that gdb can distinguish type names from function names, for example.
//
// A compunit contains one or more function address ranges. If an
// object file is compiled without -ffunction-sections, it contains
// only one .text section and therefore contains a single address range.
// Such range is typically stored directly to the compunit.
//
// If an object file is compiled with -ffunction-sections, it contains
// more than one .text section, and it has as many address ranges as
// the number of .text sections. Such discontiguous address ranges are
// stored to .debug_ranges in DWARF 2/3/4 and .debug_rnglists/.debug_addr
// in DWARF 5.
//
// .debug_info section contains DWARF debug info. Although we don't need
// to parse the whole .debug_info section to read address ranges, we
// have to do a little bit. DWARF is complicated and often handled using
// a library such as libdwarf. But we don't use any library because we
// don't want to add an extra run-time dependency just for --gdb-index.
//
// This page explains the format of .gdb_index:
// https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html
#include "mold.h"
#include <tbb/parallel_for_each.h>
#include <tbb/parallel_sort.h>
namespace mold::elf {
enum DwarfKind { DWARF2_32, DWARF5_32, DWARF2_64, DWARF5_64 };
template <typename E>
struct CuHdrDwarf2_32 {
U32<E> size;
U16<E> version;
U32<E> abbrev_offset;
u8 address_size;
};
template <typename E>
struct CuHdrDwarf5_32 {
U32<E> size;
U16<E> version;
u8 unit_type;
u8 address_size;
U32<E> abbrev_offset;
};
template <typename E>
struct CuHdrDwarf2_64 {
U32<E> magic;
U64<E> size;
U16<E> version;
U64<E> abbrev_offset;
u8 address_size;
};
template <typename E>
struct CuHdrDwarf5_64 {
U32<E> magic;
U64<E> size;
U16<E> version;
u8 unit_type;
u8 address_size;
U64<E> abbrev_offset;
};
template <typename E>
struct PubnamesHdr32 {
U32<E> size;
U16<E> version;
U32<E> debug_info_offset;
U32<E> debug_info_size;
};
template <typename E>
struct PubnamesHdr64 {
U32<E> magic;
U64<E> size;
U16<E> version;
U64<E> debug_info_offset;
U64<E> debug_info_size;
};
struct SectionHeader {
ul32 version = 7;
ul32 cu_list_offset = 0;
ul32 cu_types_offset = 0;
ul32 ranges_offset = 0;
ul32 symtab_offset = 0;
ul32 const_pool_offset = 0;
};
struct NameType {
bool operator==(const NameType &) const = default;
bool operator<(const NameType &other) const {
return std::tuple(hash, type, name) <
std::tuple(other.hash, other.type, other.name);
}
std::string_view name;
u32 hash;
u8 type;
};
struct MapValue {
std::string_view name;
u32 hash = 0;
Atomic<u32> count = 0;
u32 name_offset = 0;
u32 type_offset = 0;
};
struct Compunit {
DwarfKind kind;
i64 offset;
i64 size;
std::vector<std::pair<u64, u64>> ranges;
std::vector<NameType> nametypes;
std::vector<MapValue *> entries;
};
// The hash function for .gdb_index.
static u32 gdb_hash(std::string_view name) {
u32 h = 0;
for (u8 c : name) {
if ('A' <= c && c <= 'Z')
c = 'a' + c - 'A';
h = h * 67 + c - 113;
}
return h;
}
template <typename E>
static DwarfKind get_dwarf_kind(Context<E> &ctx, u8 *p) {
if (*(U32<E> *)p == 0xffff'ffff) {
CuHdrDwarf2_64<E> &hdr = *(CuHdrDwarf2_64<E> *)p;
if (hdr.version > 5)
Fatal(ctx) << "--gdb-index: DWARF version " << hdr.version
<< " is not supported";
return (hdr.version == 5) ? DWARF5_64 : DWARF2_64;
}
CuHdrDwarf2_32<E> &hdr = *(CuHdrDwarf2_32<E> *)p;
if (hdr.version > 5)
Fatal(ctx) << "--gdb-index: DWARF version " << hdr.version
<< " is not supported";
return (hdr.version == 5) ? DWARF5_32 : DWARF2_32;
}
template <typename E, typename CuHdr>
u8 *find_cu_abbrev(Context<E> &ctx, u8 **p, const CuHdr &hdr) {
if (hdr.address_size != sizeof(Word<E>))
Fatal(ctx) << "--gdb-index: unsupported address size " << hdr.address_size;
if constexpr (requires { hdr.unit_type; }) {
switch (hdr.unit_type) {
case DW_UT_compile:
case DW_UT_partial:
break;
case DW_UT_skeleton:
case DW_UT_split_compile:
*p += 8;
break;
default:
Fatal(ctx) << "--gdb-index: unknown unit type: 0x"
<< std::hex << hdr.unit_type;
}
}
i64 abbrev_code = read_uleb(p);
// Find a .debug_abbrev record corresponding to the .debug_info record.
// We assume the .debug_info record at a given offset is of
// DW_TAG_compile_unit which describes a compunit.
u8 *abbrev = &ctx.debug_abbrev[0] + hdr.abbrev_offset;
for (;;) {
u32 code = read_uleb(&abbrev);
if (code == 0)
Fatal(ctx) << "--gdb-index: .debug_abbrev does not contain"
<< " a record for the first .debug_info record";
if (code == abbrev_code) {
// Found a record
u64 abbrev_tag = read_uleb(&abbrev);
if (abbrev_tag != DW_TAG_compile_unit && abbrev_tag != DW_TAG_skeleton_unit)
Fatal(ctx) << "--gdb-index: the first entry's tag is not"
<< " DW_TAG_compile_unit/DW_TAG_skeleton_unit but 0x"
<< std::hex << abbrev_tag;
break;
}
// Skip an uninteresting record
read_uleb(&abbrev); // tag
abbrev++; // has_children byte
for (;;) {
u64 name = read_uleb(&abbrev);
u64 form = read_uleb(&abbrev);
if (name == 0 && form == 0)
break;
if (form == DW_FORM_implicit_const)
read_uleb(&abbrev);
}
}
abbrev++; // skip has_children byte
return abbrev;
}
// .debug_info contains variable-length fields.
// This function reads one scalar value from a given location.
template <typename E, typename Offset>
u64 read_scalar(Context<E> &ctx, u8 **p, u64 form) {
switch (form) {
case DW_FORM_flag_present:
return 0;
case DW_FORM_data1:
case DW_FORM_flag:
case DW_FORM_strx1:
case DW_FORM_addrx1:
case DW_FORM_ref1:
return *(*p)++;
case DW_FORM_data2:
case DW_FORM_strx2:
case DW_FORM_addrx2:
case DW_FORM_ref2: {
u64 val = *(U16<E> *)*p;
*p += 2;
return val;
}
case DW_FORM_strx3:
case DW_FORM_addrx3: {
u64 val = *(U24<E> *)*p;
*p += 3;
return val;
}
case DW_FORM_data4:
case DW_FORM_strx4:
case DW_FORM_addrx4:
case DW_FORM_ref4: {
u64 val = *(U32<E> *)*p;
*p += 4;
return val;
}
case DW_FORM_data8:
case DW_FORM_ref8: {
u64 val = *(U64<E> *)*p;
*p += 8;
return val;
}
case DW_FORM_strp:
case DW_FORM_sec_offset:
case DW_FORM_line_strp: {
u64 val = *(Offset *)*p;
*p += sizeof(Offset);
return val;
}
case DW_FORM_addr:
case DW_FORM_ref_addr: {
u64 val = *(Word<E> *)*p;
*p += sizeof(Word<E>);
return val;
}
case DW_FORM_strx:
case DW_FORM_addrx:
case DW_FORM_udata:
case DW_FORM_ref_udata:
case DW_FORM_loclistx:
case DW_FORM_rnglistx:
return read_uleb(p);
case DW_FORM_string:
*p += strlen((char *)*p) + 1;
return 0;
default:
Fatal(ctx) << "--gdb-index: unhandled debug info form: 0x"
<< std::hex << form;
}
}
// Read a range list from .debug_ranges starting at the given offset.
template <typename E>
static std::vector<std::pair<u64, u64>>
read_debug_range(Word<E> *range, u64 base) {
std::vector<std::pair<u64, u64>> vec;
for (i64 i = 0; range[i] || range[i + 1]; i += 2) {
if (range[i] + 1 == 0)
base = range[i + 1];
else
vec.emplace_back(range[i] + base, range[i + 1] + base);
}
return vec;
}
// Read a range list from .debug_rnglists starting at the given offset.
template <typename E>
static void
read_rnglist_range(std::vector<std::pair<u64, u64>> &vec, u8 *p,
Word<E> *addrx, u64 base) {
for (;;) {
switch (*p++) {
case DW_RLE_end_of_list:
return;
case DW_RLE_base_addressx:
base = addrx[read_uleb(&p)];
break;
case DW_RLE_startx_endx: {
u64 val1 = read_uleb(&p);
u64 val2 = read_uleb(&p);
vec.emplace_back(addrx[val1], addrx[val2]);
break;
}
case DW_RLE_startx_length: {
u64 val1 = read_uleb(&p);
u64 val2 = read_uleb(&p);
vec.emplace_back(addrx[val1], addrx[val1] + val2);
break;
}
case DW_RLE_offset_pair: {
u64 val1 = read_uleb(&p);
u64 val2 = read_uleb(&p);
// If the base is 0, this address range is for an eliminated
// section. We only emit it if it's alive.
if (base)
vec.emplace_back(base + val1, base + val2);
break;
}
case DW_RLE_base_address:
base = *(Word<E> *)p;
p += sizeof(Word<E>);
break;
case DW_RLE_start_end: {
u64 val1 = ((Word<E> *)p)[0];
u64 val2 = ((Word<E> *)p)[1];
p += sizeof(Word<E>) * 2;
vec.emplace_back(val1, val2);
break;
}
case DW_RLE_start_length: {
u64 val1 = *(Word<E> *)p;
p += sizeof(Word<E>);
u64 val2 = read_uleb(&p);
vec.emplace_back(val1, val1 + val2);
break;
}
}
}
}
// Returns a list of address ranges explained by a compunit at the
// `offset` in an output .debug_info section.
//
// .debug_info contains DWARF debug info records, so this function
// parses DWARF. If a designated compunit contains multiple ranges, the
// ranges are read from .debug_ranges (or .debug_rnglists for DWARF5).
// Otherwise, a range is read directly from .debug_info (or possibly
// from .debug_addr for DWARF5).
template <typename E, typename CuHdr>
static std::vector<std::pair<u64, u64>>
read_address_ranges(Context<E> &ctx, const Compunit &cu) {
// Read .debug_info to find the record at a given offset.
u8 *p = &ctx.debug_info[0] + cu.offset;
CuHdr &hdr = *(CuHdr *)p;
p += sizeof(hdr);
u8 *abbrev = find_cu_abbrev(ctx, &p, hdr);
// Now, read debug info records.
struct Record {
u64 form = 0;
u64 value = 0;
};
using Offset = decltype(hdr.size);
Record low_pc;
Record high_pc;
Record ranges;
u64 rnglists_base = -1;
Word<E> *addrx = nullptr;
// Read all interesting debug records.
for (;;) {
u64 name = read_uleb(&abbrev);
u64 form = read_uleb(&abbrev);
if (name == 0 && form == 0)
break;
u64 val = read_scalar<E, Offset>(ctx, &p, form);
switch (name) {
case DW_AT_low_pc:
low_pc = {form, val};
break;
case DW_AT_high_pc:
high_pc = {form, val};
break;
case DW_AT_rnglists_base:
rnglists_base = val;
break;
case DW_AT_addr_base:
addrx = (Word<E> *)(&ctx.debug_addr[0] + val);
break;
case DW_AT_ranges:
ranges = {form, val};
break;
}
}
// Handle non-contiguous address ranges.
if (ranges.form) {
if (hdr.version <= 4) {
Word<E> *p = (Word<E> *)(&ctx.debug_ranges[0] + ranges.value);
return read_debug_range<E>(p, low_pc.value);
}
assert(hdr.version == 5);
std::vector<std::pair<u64, u64>> vec;
u8 *buf = &ctx.debug_rnglists[0];
if (ranges.form == DW_FORM_sec_offset) {
read_rnglist_range<E>(vec, buf + ranges.value, addrx, low_pc.value);
} else {
if (rnglists_base == -1)
Fatal(ctx) << "--gdb-index: missing DW_AT_rnglists_base";
u8 *base = buf + rnglists_base;
i64 num_offsets = *(U32<E> *)(base - 4);
Offset *offsets = (Offset *)base;
for (i64 i = 0; i < num_offsets; i++)
read_rnglist_range<E>(vec, base + offsets[i], addrx, low_pc.value);
}
return vec;
}
// Handle a contiguous address range.
if (low_pc.form && high_pc.form) {
u64 lo;
switch (low_pc.form) {
case DW_FORM_addr:
lo = low_pc.value;
break;
case DW_FORM_addrx:
case DW_FORM_addrx1:
case DW_FORM_addrx2:
case DW_FORM_addrx4:
lo = addrx[low_pc.value];
break;
default:
Fatal(ctx) << "--gdb-index: unhandled form for DW_AT_low_pc: 0x"
<< std::hex << high_pc.form;
}
switch (high_pc.form) {
case DW_FORM_addr:
return {{lo, high_pc.value}};
case DW_FORM_addrx:
case DW_FORM_addrx1:
case DW_FORM_addrx2:
case DW_FORM_addrx4:
return {{lo, addrx[high_pc.value]}};
case DW_FORM_udata:
case DW_FORM_data1:
case DW_FORM_data2:
case DW_FORM_data4:
case DW_FORM_data8:
return {{lo, lo + high_pc.value}};
default:
Fatal(ctx) << "--gdb-index: unhandled form for DW_AT_high_pc: 0x"
<< std::hex << high_pc.form;
}
}
return {};
}
template <typename E, typename PubnamesHdr>
static i64 read_pubnames_cu(Context<E> &ctx, const PubnamesHdr &hdr,
std::vector<Compunit> &cus, ObjectFile<E> &file) {
using Offset = decltype(hdr.size);
auto get_cu = [&](i64 offset) {
for (i64 i = 0; i < cus.size(); i++)
if (cus[i].offset == offset)
return &cus[i];
Fatal(ctx) << file << ": corrupted debug_info_offset";
};
Compunit *cu = get_cu(file.debug_info->offset + hdr.debug_info_offset);
i64 size = hdr.size + offsetof(PubnamesHdr, size) + sizeof(hdr.size);
u8 *p = (u8 *)&hdr + sizeof(hdr);
u8 *end = (u8 *)&hdr + size;
while (p < end) {
if (*(Offset *)p == 0)
break;
p += sizeof(Offset);
u8 type = *p++;
std::string_view name = (char *)p;
p += name.size() + 1;
cu->nametypes.push_back({name, gdb_hash(name), type});
}
return size;
}
// Parses .debug_gnu_pubnames and .debug_gnu_pubtypes. These sections
// start with a 14 bytes header followed by (4-byte offset, 1-byte type,
// null-terminated string) tuples.
//
// The 4-byte offset is an offset into .debug_info that contains details
// about the name. The 1-byte type is a type of the corresponding name
// (e.g. function, variable or datatype). The string is a name of a
// function, a variable or a type.
template <typename E>
static void read_pubnames(Context<E> &ctx, std::vector<Compunit> &cus,
ObjectFile<E> &file) {
for (InputSection<E> *isec : { file.debug_pubnames, file.debug_pubtypes }) {
if (!isec)
continue;
isec->uncompress(ctx);
if (isec->contents.empty())
continue;
u8 *p = (u8*)&isec->contents[0];
u8 *end = p + isec->contents.size();
while (p < end) {
if (*(U32<E> *)p == 0xffff'ffff)
p += read_pubnames_cu(ctx, *(PubnamesHdr64<E> *)p, cus, file);
else
p += read_pubnames_cu(ctx, *(PubnamesHdr32<E> *)p, cus, file);
}
};
}
template <typename E>
static std::vector<Compunit> read_compunits(Context<E> &ctx) {
std::vector<Compunit> cus;
// Read compunits from the output .debug_info section.
u8 *begin = &ctx.debug_info[0];
u8 *end = begin + ctx.debug_info.size();
for (u8 *p = begin; p < end;) {
DwarfKind kind = get_dwarf_kind(ctx, p);
i64 size;
if (kind == DWARF2_32 || kind == DWARF5_32)
size = ((CuHdrDwarf2_32<E> *)p)->size + 4;
else
size = ((CuHdrDwarf2_64<E> *)p)->size + 12;
cus.push_back(Compunit{kind, p - begin, size});
p += size;
}
// Read address ranges for each compunit.
tbb::parallel_for_each(cus, [&](Compunit &cu) {
switch (cu.kind) {
case DWARF2_32:
cu.ranges = read_address_ranges<E, CuHdrDwarf2_32<E>>(ctx, cu);
break;
case DWARF5_32:
cu.ranges = read_address_ranges<E, CuHdrDwarf5_32<E>>(ctx, cu);
break;
case DWARF2_64:
cu.ranges = read_address_ranges<E, CuHdrDwarf2_64<E>>(ctx, cu);
break;
case DWARF5_64:
cu.ranges = read_address_ranges<E, CuHdrDwarf5_64<E>>(ctx, cu);
break;
}
// Remove empty ranges
std::erase_if(cu.ranges, [](std::pair<u64, u64> p) {
return p.first == 0 || p.first == p.second;
});
});
// Read symbols from .debug_gnu_pubnames and .debug_gnu_pubtypes.
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
read_pubnames(ctx, cus, *file);
});
// Uniquify elements because GCC 11 seems to emit one record for each
// comdat group which results in having a lot of duplicate records.
tbb::parallel_for_each(cus, [&](Compunit &cu) {
sort(cu.nametypes);
remove_duplicates(cu.nametypes);
});
return cus;
}
template <typename E>
std::span<u8> get_buffer(Context<E> &ctx, Chunk<E> *chunk) {
if (chunk->is_compressed)
return chunk->uncompressed_data;
return {ctx.buf + chunk->shdr.sh_offset, chunk->shdr.sh_size};
}
template <typename E>
void write_gdb_index(Context<E> &ctx) {
Timer t(ctx, "write_gdb_index");
// Find debug info sections
for (Chunk<E> *chunk : ctx.chunks) {
std::string_view name = chunk->name;
if (name == ".debug_info")
ctx.debug_info = get_buffer(ctx, chunk);
if (name == ".debug_abbrev")
ctx.debug_abbrev = get_buffer(ctx, chunk);
if (name == ".debug_ranges")
ctx.debug_ranges = get_buffer(ctx, chunk);
if (name == ".debug_addr")
ctx.debug_addr = get_buffer(ctx, chunk);
if (name == ".debug_rnglists")
ctx.debug_rnglists = get_buffer(ctx, chunk);
}
if (ctx.debug_info.empty())
return;
// Read debug info
std::vector<Compunit> cus = read_compunits(ctx);
// Uniquify symbols
HyperLogLog estimator;
tbb::parallel_for_each(cus, [&](Compunit &cu) {
HyperLogLog e;
for (NameType &nt : cu.nametypes)
e.insert(nt.hash);
estimator.merge(e);
});
ConcurrentMap<MapValue> map(estimator.get_cardinality() * 3 / 2);
Atomic<i64> num_entries;
tbb::parallel_for_each(cus, [&](Compunit &cu) {
cu.entries.reserve(cu.nametypes.size());
i64 count = 0;
for (NameType &nt : cu.nametypes) {
MapValue *ent;
bool inserted;
MapValue value = {nt.name, nt.hash};
std::tie(ent, inserted) = map.insert(nt.name, nt.hash, value);
ent->count++;
if (inserted)
count++;
cu.entries.push_back(ent);
}
num_entries += count;
});
// Sort symbols for build reproducibility
std::vector<MapValue *> entries;
entries.reserve(num_entries);
for (i64 i = 0; i < map.nbuckets; i++)
if (map.entries[i].key)
entries.push_back(&map.entries[i].value);
tbb::parallel_sort(entries, [](MapValue *a, MapValue *b) {
return std::tuple(a->hash, a->name) < std::tuple(b->hash, b->name);
});
// Compute sizes of each components
SectionHeader hdr;
hdr.cu_list_offset = sizeof(hdr);
hdr.cu_types_offset = hdr.cu_list_offset + cus.size() * 16;
hdr.ranges_offset = hdr.cu_types_offset;
hdr.symtab_offset = hdr.ranges_offset;
for (Compunit &cu : cus)
hdr.symtab_offset += cu.ranges.size() * 20;
i64 ht_size = bit_ceil(num_entries * 5 / 4 + 1);
hdr.const_pool_offset = hdr.symtab_offset + ht_size * 8;
i64 offset = 0;
for (MapValue *ent : entries) {
ent->type_offset = offset;
offset += ent->count * 4 + 4;
}
for (MapValue *ent : entries) {
ent->name_offset = offset;
offset += ent->name.size() + 1;
}
i64 bufsize = hdr.const_pool_offset + offset;
// Allocate an output buffer
ctx.output_file->buf2.resize(bufsize);
u8 *buf = ctx.output_file->buf2.data();
// Write a section header
memcpy(buf, &hdr, sizeof(hdr));
// Write a CU list
u8 *p = buf + sizeof(hdr);
for (Compunit &cu : cus) {
*(ul64 *)p = cu.offset;
*(ul64 *)(p + 8) = cu.size;
p += 16;
}
// Write address areas
for (i64 i = 0; i < cus.size(); i++) {
for (std::pair<u64, u64> range : cus[i].ranges) {
*(ul64 *)p = range.first;
*(ul64 *)(p + 8) = range.second;
*(ul32 *)(p + 16) = i;
p += 20;
}
}
// Write a symbol table
u32 mask = ht_size - 1;
ul32 *ht = (ul32 *)(buf + hdr.symtab_offset);
for (MapValue *ent : entries) {
u32 hash = ent->hash;
u32 step = ((hash * 17) & mask) | 1;
u32 j = hash & mask;
while (ht[j * 2] || ht[j * 2 + 1])
j = (j + step) & mask;
ht[j * 2] = ent->name_offset;
ht[j * 2 + 1] = ent->type_offset;
}
// Write types
for (i64 i = 0; i < cus.size(); i++) {
Compunit &cu = cus[i];
u8 *base = buf + hdr.const_pool_offset;
for (i64 j = 0; j < cu.nametypes.size(); j++) {
ul32 *p = (ul32 *)(base + cu.entries[j]->type_offset);
i64 idx = ++p[0];
p[idx] = (cu.nametypes[j].type << 24) | i;
}
}
// Write names
tbb::parallel_for_each(entries, [&](MapValue *ent) {
write_string(buf + hdr.const_pool_offset + ent->name_offset, ent->name);
});
// Update the section size and rewrite the section header
if (ctx.shdr) {
ctx.gdb_index->shdr.sh_size = bufsize;
ctx.shdr->copy_buf(ctx);
}
}
using E = MOLD_TARGET;
template void write_gdb_index(Context<E> &);
} // namespace mold::elf
+141 -122
View File
@@ -40,22 +40,35 @@
// regular trees are equal. Given this formulation, we want to find as
// many identical vertices as possible.
//
// Solving such problem is computationally intensive, but mold is quite fast.
// For Chromium, mold's ICF finishes in less than 1 second with 20 threads.
// This is contrary to lld and gold, which take about 5 and 50 seconds to
// run ICF under the same condition, respectively.
// Just like a lot of problems with graph, this problem doesn't have a
// straightforward "optimal" solution, and we need to resort to heuristics.
//
// mold's ICF is faster because we are using a better algorithm.
// It's actually me who developed and implemented the lld's ICF algorithm,
// and I can say that mold's algorithm is better than that in all aspects.
// It scales better for number of available cores, require less overall
// computation, and has a smaller working set. So, it's better with a single
// thread and even better with multiple threads.
// mold approaches this problem by hashing program trees with increasing depth
// on each iteration.
// For example, when we start, we only hash individual functions with
// their call into other functions omitted. From the second iteration, we
// put the function they call into the hash by appending the hash of those
// functions from the previous iteration. This means that the nth iteration
// hashes call chain up to (n-1) levels deep.
// We use a cryptographic hash function, so the unique number of hashes will
// only monotonically increase as we take into account of deeper trees with
// iterations (otherwise, that means we have found a hash collision). We stop
// when the unique number of hashes stop increasing; this is based on the fact
// that once we observe an iteration with the same amount of unique hashes as
// the previous iteration, it will remain unchanged for further iterations.
// This is provable, but here we omit the proof for brevity.
//
// When compared to other approaches, mold's approach has a relatively cheaper
// cost per iteration, and as a bonus, is highly parallelizable.
// For Chromium, mold's ICF finishes in less than 1 second with 20 threads,
// whereas lld takes 5 seconds and gold takes 50 seconds under the same
// conditions.
#include "mold.h"
#include "../sha.h"
#include "blake3.h"
#include <array>
#include <cstdio>
#include <tbb/concurrent_unordered_map.h>
#include <tbb/concurrent_vector.h>
#include <tbb/enumerable_thread_specific.h>
@@ -68,7 +81,7 @@ static constexpr int64_t HASH_SIZE = 16;
typedef std::array<uint8_t, HASH_SIZE> Digest;
namespace std {
template<> struct hash<Digest> {
template <> struct hash<Digest> {
size_t operator()(const Digest &k) const {
return *(int64_t *)&k[0];
}
@@ -98,29 +111,30 @@ static void uniquify_cies(Context<E> &ctx) {
}
template <typename E>
static bool is_eligible(InputSection<E> &isec) {
static bool is_eligible(Context<E> &ctx, InputSection<E> &isec) {
const ElfShdr<E> &shdr = isec.shdr();
std::string_view name = isec.name();
bool is_alloc = (shdr.sh_flags & SHF_ALLOC);
bool is_executable = (shdr.sh_flags & SHF_EXECINSTR);
bool is_relro = (name == ".data.rel.ro" ||
name.starts_with(".data.rel.ro."));
bool is_readonly = !(shdr.sh_flags & SHF_WRITE) || is_relro;
bool is_bss = (shdr.sh_type == SHT_NOBITS);
bool is_empty = (shdr.sh_size == 0);
bool is_init = (shdr.sh_type == SHT_INIT_ARRAY || name == ".init");
bool is_fini = (shdr.sh_type == SHT_FINI_ARRAY || name == ".fini");
bool is_enumerable = is_c_identifier(name);
if (shdr.sh_size == 0 || !(shdr.sh_flags & SHF_ALLOC) ||
shdr.sh_type == SHT_NOBITS || is_c_identifier(name))
return false;
return is_alloc && is_executable && is_readonly && !is_bss &&
!is_empty && !is_init && !is_fini && !is_enumerable;
if (shdr.sh_flags & SHF_EXECINSTR) {
return (ctx.arg.icf_all || !isec.address_taken) &&
name != ".init" && name != ".fini";
} else {
bool is_readonly = !(shdr.sh_flags & SHF_WRITE);
bool is_relro = isec.output_section && isec.output_section->is_relro;
return (ctx.arg.ignore_data_address_equality || !isec.address_taken) &&
(is_readonly || is_relro);
}
}
static Digest digest_final(SHA256_CTX &sha) {
u8 buf[SHA256_SIZE];
int res = SHA256_Final(buf, &sha);
assert(res == 1);
static Digest digest_final(blake3_hasher *hasher) {
assert(HASH_SIZE <= BLAKE3_OUT_LEN);
u8 buf[BLAKE3_OUT_LEN];
blake3_hasher_finalize(hasher, buf, BLAKE3_OUT_LEN);
Digest digest;
memcpy(digest.data(), buf, HASH_SIZE);
@@ -139,10 +153,6 @@ static bool is_leaf(Context<E> &ctx, InputSection<E> &isec) {
return true;
}
static u64 combine_hash(u64 a, u64 b) {
return a ^ (b + 0x9e3779b9 + (a << 6) + (a >> 2));
}
template <typename E>
struct LeafHasher {
size_t operator()(InputSection<E> *isec) const {
@@ -175,6 +185,8 @@ struct LeafEq {
}
};
// Early merge of leaf nodes, which can be processed without constructing the
// entire graph. This reduces the vertex count and improves memory efficiency.
template <typename E>
static void merge_leaf_nodes(Context<E> &ctx) {
Timer t(ctx, "merge_leaf_nodes");
@@ -191,30 +203,30 @@ static void merge_leaf_nodes(Context<E> &ctx) {
if (!isec || !isec->is_alive)
continue;
if (!is_eligible(*isec)) {
if (!is_eligible(ctx, *isec)) {
non_eligible++;
continue;
}
if (is_leaf(ctx, *isec)) {
leaf++;
isec->extra().icf_leaf = true;
isec->icf_leaf = true;
auto [it, inserted] = map.insert({isec.get(), isec.get()});
if (!inserted && isec->get_priority() < it->second->get_priority())
it->second = isec.get();
} else {
eligible++;
isec->extra().icf_eligible = true;
isec->icf_eligible = true;
}
}
});
tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
for (std::unique_ptr<InputSection<E>> &isec : ctx.objs[i]->sections) {
if (isec && isec->is_alive && isec->extra().icf_leaf) {
if (isec && isec->is_alive && isec->icf_leaf) {
auto it = map.find(isec.get());
assert(it != map.end());
isec->extra().leader = it->second;
isec->leader = it->second;
}
}
});
@@ -222,16 +234,16 @@ static void merge_leaf_nodes(Context<E> &ctx) {
template <typename E>
static Digest compute_digest(Context<E> &ctx, InputSection<E> &isec) {
SHA256_CTX sha;
SHA256_Init(&sha);
blake3_hasher hasher;
blake3_hasher_init(&hasher);
auto hash = [&](auto val) {
SHA256_Update(&sha, &val, sizeof(val));
blake3_hasher_update(&hasher, (u8 *)&val, sizeof(val));
};
auto hash_string = [&](std::string_view str) {
hash(str.size());
SHA256_Update(&sha, str.data(), str.size());
blake3_hasher_update(&hasher, (u8 *)str.data(), str.size());
};
auto hash_symbol = [&](Symbol<E> &sym) {
@@ -245,10 +257,10 @@ static Digest compute_digest(Context<E> &ctx, InputSection<E> &isec) {
hash((u64)frag);
} else if (!isec) {
hash('3');
} else if (isec->extra().leader) {
} else if (isec->leader) {
hash('4');
hash((u64)isec->extra().leader);
} else if (isec->extra().icf_eligible) {
hash((u64)isec->leader);
} else if (isec->icf_eligible) {
hash('5');
} else {
hash('6');
@@ -271,33 +283,23 @@ static Digest compute_digest(Context<E> &ctx, InputSection<E> &isec) {
hash(fde.get_rels(isec.file).size());
for (ElfRel<E> &rel : fde.get_rels(isec.file).subspan(1)) {
for (const ElfRel<E> &rel : fde.get_rels(isec.file).subspan(1)) {
hash_symbol(*isec.file.symbols[rel.r_sym]);
hash(rel.r_type);
hash(rel.r_offset - fde.input_offset);
hash(isec.file.cies[fde.cie_idx].input_section.get_addend(rel));
hash(get_addend(isec.file.cies[fde.cie_idx].input_section, rel));
}
}
i64 frag_idx = 0;
for (i64 i = 0; i < isec.get_rels(ctx).size(); i++) {
ElfRel<E> &rel = isec.get_rels(ctx)[i];
const ElfRel<E> &rel = isec.get_rels(ctx)[i];
hash(rel.r_offset);
hash(rel.r_type);
hash(isec.get_addend(rel));
if (isec.rel_fragments && isec.rel_fragments[frag_idx].idx == i) {
SectionFragmentRef<E> &ref = isec.rel_fragments[frag_idx++];
hash('a');
isec.get_addend(rel);
hash((u64)ref.frag);
} else {
hash_symbol(*isec.file.symbols[rel.r_sym]);
}
hash(get_addend(isec, rel));
hash_symbol(*isec.file.symbols[rel.r_sym]);
}
return digest_final(sha);
return digest_final(&hasher);
}
template <typename E>
@@ -309,7 +311,7 @@ static std::vector<InputSection<E> *> gather_sections(Context<E> &ctx) {
tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
for (std::unique_ptr<InputSection<E>> &isec : ctx.objs[i]->sections)
if (isec && isec->is_alive && isec->extra().icf_eligible)
if (isec && isec->is_alive && isec->icf_eligible)
num_sections[i]++;
});
@@ -324,12 +326,12 @@ static std::vector<InputSection<E> *> gather_sections(Context<E> &ctx) {
tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
i64 idx = section_indices[i];
for (std::unique_ptr<InputSection<E>> &isec : ctx.objs[i]->sections)
if (isec && isec->is_alive && isec->extra().icf_eligible)
if (isec && isec->is_alive && isec->icf_eligible)
sections[idx++] = isec.get();
});
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
sections[i]->extra().icf_idx = i;
sections[i]->icf_idx = i;
});
return sections;
@@ -347,6 +349,9 @@ compute_digests(Context<E> &ctx, std::span<InputSection<E> *> sections) {
return digests;
}
// Build a graph, treating every function as a vertex and every function call
// as an edge. See the description at the top for a more detailed formulation.
// We use u32 indices here to improve cache locality.
template <typename E>
static void gather_edges(Context<E> &ctx,
std::span<InputSection<E> *> sections,
@@ -362,20 +367,15 @@ static void gather_edges(Context<E> &ctx,
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
InputSection<E> &isec = *sections[i];
assert(isec.extra().icf_eligible);
i64 frag_idx = 0;
assert(isec.icf_eligible);
for (i64 j = 0; j < isec.get_rels(ctx).size(); j++) {
if (isec.rel_fragments && isec.rel_fragments[frag_idx].idx == j) {
frag_idx++;
} else {
ElfRel<E> &rel = isec.get_rels(ctx)[j];
Symbol<E> &sym = *isec.file.symbols[rel.r_sym];
if (!sym.get_frag())
if (InputSection<E> *isec = sym.get_input_section())
if (isec->extra().icf_eligible)
num_edges[i]++;
}
const ElfRel<E> &rel = isec.get_rels(ctx)[j];
Symbol<E> &sym = *isec.file.symbols[rel.r_sym];
if (!sym.get_frag())
if (InputSection<E> *isec = sym.get_input_section())
if (isec->icf_eligible)
num_edges[i]++;
}
});
@@ -386,27 +386,22 @@ static void gather_edges(Context<E> &ctx,
tbb::parallel_for((i64)0, (i64)num_edges.size(), [&](i64 i) {
InputSection<E> &isec = *sections[i];
i64 frag_idx = 0;
i64 idx = edge_indices[i];
for (i64 j = 0; j < isec.get_rels(ctx).size(); j++) {
if (isec.rel_fragments && isec.rel_fragments[frag_idx].idx == j) {
frag_idx++;
ElfRel<E> &rel = isec.get_rels(ctx)[j];
Symbol<E> &sym = *isec.file.symbols[rel.r_sym];
if (!sym.get_frag())
if (InputSection<E> *isec = sym.get_input_section())
if (isec->extra().icf_eligible)
edges[idx++] = isec->extra().icf_idx;
}
}
for (ElfRel<E> &rel : isec.get_rels(ctx)) {
Symbol<E> &sym = *isec.file.symbols[rel.r_sym];
if (InputSection<E> *isec = sym.get_input_section())
if (isec->icf_eligible)
edges[idx++] = isec->icf_idx;
}
});
}
template <typename E>
static i64 propagate(std::span<std::vector<Digest>> digests,
std::span<u32> edges, std::span<u32> edge_indices,
bool &slot, tbb::affinity_partitioner &ap) {
bool &slot, BitVector &converged,
tbb::affinity_partitioner &ap) {
static Counter round("icf_round");
round++;
@@ -414,23 +409,28 @@ static i64 propagate(std::span<std::vector<Digest>> digests,
tbb::enumerable_thread_specific<i64> changed;
tbb::parallel_for((i64)0, num_digests, [&](i64 i) {
if (digests[slot][i] == digests[!slot][i])
if (converged.get(i))
return;
SHA256_CTX sha;
SHA256_Init(&sha);
SHA256_Update(&sha, digests[2][i].data(), HASH_SIZE);
blake3_hasher hasher;
blake3_hasher_init(&hasher);
blake3_hasher_update(&hasher, digests[2][i].data(), HASH_SIZE);
i64 begin = edge_indices[i];
i64 end = (i + 1 == num_digests) ? edges.size() : edge_indices[i + 1];
for (i64 j : edges.subspan(begin, end - begin))
SHA256_Update(&sha, digests[slot][j].data(), HASH_SIZE);
blake3_hasher_update(&hasher, digests[slot][j].data(), HASH_SIZE);
digests[!slot][i] = digest_final(sha);
digests[!slot][i] = digest_final(&hasher);
if (digests[slot][i] != digests[!slot][i])
if (digests[slot][i] == digests[!slot][i]) {
// This node has converged. Skip further iterations as it will
// yield the same hash.
converged.set(i);
} else {
changed.local()++;
}
}, ap);
slot = !slot;
@@ -458,11 +458,11 @@ static void print_icf_sections(Context<E> &ctx) {
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (std::unique_ptr<InputSection<E>> &isec : file->sections) {
if (isec && isec->is_alive && isec->extra().leader) {
if (isec.get() == isec->extra().leader)
if (isec && isec->is_alive && isec->leader) {
if (isec.get() == isec->leader)
leaders.push_back(isec.get());
else
map.insert({isec->extra().leader, isec.get()});
map.insert({isec->leader, isec.get()});
}
}
});
@@ -495,9 +495,8 @@ static void print_icf_sections(Context<E> &ctx) {
template <typename E>
void icf_sections(Context<E> &ctx) {
Timer t(ctx, "icf");
for (ObjectFile<E> *file : ctx.objs)
file->extras.resize(file->sections.size());
if (ctx.objs.empty())
return;
uniquify_cies(ctx);
merge_leaf_nodes(ctx);
@@ -505,6 +504,15 @@ void icf_sections(Context<E> &ctx) {
// Prepare for the propagation rounds.
std::vector<InputSection<E> *> sections = gather_sections(ctx);
// We allocate 3 arrays to store hashes for each vertex.
//
// Index 0 and 1 are used for tree hashes from the previous
// iteration and the current iteration. They switch roles every
// iteration. See `slot` below.
//
// Index 2 stores the initial, single-vertex hash. This is combined
// with hashes from the connected vertices to form the tree hash
// described above.
std::vector<std::vector<Digest>> digests(3);
digests[0] = compute_digests<E>(ctx, sections);
digests[1].resize(digests[0].size());
@@ -514,6 +522,7 @@ void icf_sections(Context<E> &ctx) {
std::vector<u32> edge_indices;
gather_edges<E>(ctx, sections, edges, edge_indices);
BitVector converged(digests[0].size());
bool slot = 0;
// Execute the propagation rounds until convergence is obtained.
@@ -521,18 +530,33 @@ void icf_sections(Context<E> &ctx) {
Timer t(ctx, "propagate");
tbb::affinity_partitioner ap;
// A cheap test that the graph hasn't converged yet.
// The loop after this one uses a strict condition, but it's expensive
// as it requires sorting the entire hash collection.
//
// For nodes that have a cycle in downstream (i.e. recursive
// functions and functions that calls recursive functions) will always
// change with the iterations. Nodes that doesn't (i.e. non-recursive
// functions) will stop changing as soon as the propagation depth reaches
// the call tree depth.
// Here, we test whether we have reached sufficient depth for the latter,
// which is a necessary (but not sufficient) condition for convergence.
i64 num_changed = -1;
for (;;) {
i64 n = propagate<E>(digests, edges, edge_indices, slot, ap);
i64 n = propagate<E>(digests, edges, edge_indices, slot, converged, ap);
if (n == num_changed)
break;
num_changed = n;
}
// Run the pass until the unique number of hashes stop increasing, at which
// point we have achieved convergence (proof omitted for brevity).
i64 num_classes = -1;
for (;;) {
// count_num_classes requires sorting which is O(n log n), so do a little
// more work beforehand to amortize that log factor.
for (i64 i = 0; i < 10; i++)
propagate<E>(digests, edges, edge_indices, slot, ap);
propagate<E>(digests, edges, edge_indices, slot, converged, ap);
i64 n = count_num_classes<E>(digests[slot], ap);
if (n == num_classes)
@@ -541,7 +565,7 @@ void icf_sections(Context<E> &ctx) {
}
}
// Group sections by SHA digest.
// Group sections by BLAKE3 digest.
{
Timer t(ctx, "group");
@@ -558,7 +582,7 @@ void icf_sections(Context<E> &ctx) {
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
auto it = map->find(digest[i]);
assert(it != map->end());
sections[i]->extra().leader = it->second;
sections[i]->leader = it->second;
});
// Since free'ing the map is slow, postpone it.
@@ -568,30 +592,25 @@ void icf_sections(Context<E> &ctx) {
if (ctx.arg.print_icf_sections)
print_icf_sections(ctx);
// Re-assign input sections to symbols.
// Eliminate duplicate sections.
// Symbols pointing to eliminated sections will be redirected on the fly when
// exporting to the symtab.
{
Timer t(ctx, "reassign");
Timer t(ctx, "sweep");
static Counter eliminated("icf_eliminated");
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
for (Symbol<E> *sym : file->symbols) {
if (sym->file == file) {
InputSection<E> *isec = sym->get_input_section();
if (isec && isec->extra().leader && isec->extra().leader != isec) {
isec->kill();
isec->killed_by_icf = true;
}
for (std::unique_ptr<InputSection<E>> &isec : file->sections) {
if (isec && isec->is_alive && isec->is_killed_by_icf()) {
isec->kill();
eliminated++;
}
}
});
}
}
#define INSTANTIATE(E) \
template void icf_sections(Context<E> &ctx);
using E = MOLD_TARGET;
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
template void icf_sections(Context<E> &ctx);
} // namespace mold::elf
+524 -475
View File
File diff suppressed because it is too large. Load diff
+449 -153
View File
@@ -2,16 +2,21 @@
#include <limits>
#include <zlib.h>
#include <zstd.h>
namespace mold::elf {
typedef enum {
NONE, ERROR, COPYREL, DYN_COPYREL, PLT, CPLT, DYN_CPLT, DYNREL, BASEREL, IFUNC,
} Action;
template <typename E>
bool CieRecord<E>::equals(const CieRecord<E> &other) const {
if (get_contents() != other.get_contents())
return false;
std::span<ElfRel<E>> x = get_rels();
std::span<ElfRel<E>> y = other.get_rels();
std::span<const ElfRel<E>> x = get_rels();
std::span<const ElfRel<E>> y = other.get_rels();
if (x.size() != y.size())
return false;
@@ -19,93 +24,202 @@ bool CieRecord<E>::equals(const CieRecord<E> &other) const {
if (x[i].r_offset - input_offset != y[i].r_offset - other.input_offset ||
x[i].r_type != y[i].r_type ||
file.symbols[x[i].r_sym] != other.file.symbols[y[i].r_sym] ||
input_section.get_addend(x[i]) != other.input_section.get_addend(y[i]))
get_addend(input_section, x[i]) != get_addend(other.input_section, y[i]))
return false;
}
return true;
}
static inline i64 to_p2align(u64 alignment) {
static i64 to_p2align(u64 alignment) {
if (alignment == 0)
return 0;
return std::countr_zero(alignment);
}
template <typename E>
InputSection<E>::InputSection(Context<E> &ctx, ObjectFile<E> &file,
std::string_view name, i64 shndx)
InputSection<E>::InputSection(Context<E> &ctx, ObjectFile<E> &file, i64 shndx)
: file(file), shndx(shndx) {
if (shndx < file.elf_sections.size())
contents = {(char *)file.mf->data + shdr().sh_offset, shdr().sh_size};
contents = {(char *)file.mf->data + shdr().sh_offset, (size_t)shdr().sh_size};
bool compressed;
if (name.starts_with(".zdebug")) {
sh_size = *(ubig64 *)&contents[4];
p2align = to_p2align(shdr().sh_addralign);
compressed = true;
} else if (shdr().sh_flags & SHF_COMPRESSED) {
if (shdr().sh_flags & SHF_COMPRESSED) {
ElfChdr<E> &chdr = *(ElfChdr<E> *)&contents[0];
sh_size = chdr.ch_size;
p2align = to_p2align(chdr.ch_addralign);
compressed = true;
} else {
sh_size = shdr().sh_size;
p2align = to_p2align(shdr().sh_addralign);
compressed = false;
}
// Uncompress early if the relocation is REL-type so that we can read
// addends from section contents. If RELA-type, we don't need to do this
// because addends are in relocations.
if (compressed && E::is_rel) {
u8 *buf = new u8[sh_size];
uncompress(ctx, buf);
contents = {(char *)buf, sh_size};
ctx.string_pool.emplace_back(buf);
}
output_section =
OutputSection<E>::get_instance(ctx, name, shdr().sh_type, shdr().sh_flags);
// Sections may have been compressed. We usually uncompress them
// directly into the mmap'ed output file, but we want to uncompress
// early for REL-type ELF types to read relocation addends from
// section contents. For RELA-type, we don't need to do this because
// addends are in relocations.
//
// SH-4 stores addends to sections despite being RELA, which is a
// special (and buggy) case.
if constexpr (!E::is_rela || is_sh4<E>)
uncompress(ctx);
}
template <typename E>
bool InputSection<E>::is_compressed() {
return !E::is_rel &&
(name().starts_with(".zdebug") || (shdr().sh_flags & SHF_COMPRESSED));
void InputSection<E>::uncompress(Context<E> &ctx) {
if (!(shdr().sh_flags & SHF_COMPRESSED) || uncompressed)
return;
u8 *buf = new u8[sh_size];
uncompress_to(ctx, buf);
contents = std::string_view((char *)buf, sh_size);
ctx.string_pool.emplace_back(buf);
uncompressed = true;
}
template <typename E>
void InputSection<E>::uncompress(Context<E> &ctx, u8 *buf) {
auto do_uncompress = [&](std::string_view data) {
unsigned long size = sh_size;
if (::uncompress(buf, &size, (u8 *)data.data(), data.size()) != Z_OK)
Fatal(ctx) << *this << ": uncompress failed";
assert(size == sh_size);
};
if (name().starts_with(".zdebug")) {
// Old-style compressed section
if (!contents.starts_with("ZLIB") || contents.size() <= 12)
Fatal(ctx) << *this << ": corrupted compressed section";
do_uncompress(contents.substr(12));
void InputSection<E>::uncompress_to(Context<E> &ctx, u8 *buf) {
if (!(shdr().sh_flags & SHF_COMPRESSED) || uncompressed) {
memcpy(buf, contents.data(), contents.size());
return;
}
assert(shdr().sh_flags & SHF_COMPRESSED);
// New-style compressed section
if (contents.size() < sizeof(ElfChdr<E>))
Fatal(ctx) << *this << ": corrupted compressed section";
ElfChdr<E> &hdr = *(ElfChdr<E> *)&contents[0];
if (hdr.ch_type != ELFCOMPRESS_ZLIB)
Fatal(ctx) << *this << ": unsupported compression type";
do_uncompress(contents.substr(sizeof(ElfChdr<E>)));
std::string_view data = contents.substr(sizeof(ElfChdr<E>));
switch (hdr.ch_type) {
case ELFCOMPRESS_ZLIB: {
unsigned long size = sh_size;
if (::uncompress(buf, &size, (u8 *)data.data(), data.size()) != Z_OK)
Fatal(ctx) << *this << ": uncompress failed";
assert(size == sh_size);
break;
}
case ELFCOMPRESS_ZSTD:
if (ZSTD_decompress(buf, sh_size, (u8 *)data.data(), data.size()) != sh_size)
Fatal(ctx) << *this << ": ZSTD_decompress failed";
break;
default:
Fatal(ctx) << *this << ": unsupported compression type: 0x"
<< std::hex << hdr.ch_type;
}
}
template <typename E>
static i64 get_output_type(Context<E> &ctx) {
static void scan_rel(Context<E> &ctx, InputSection<E> &isec, Symbol<E> &sym,
const ElfRel<E> &rel, Action action) {
bool writable = (isec.shdr().sh_flags & SHF_WRITE);
auto error = [&] {
std::string msg = sym.is_absolute() ? "-fno-PIC" : "-fPIC";
Error(ctx) << isec << ": " << rel << " relocation at offset 0x"
<< std::hex << rel.r_offset << " against symbol `"
<< sym << "' can not be used; recompile with " << msg;
};
auto check_textrel = [&] {
if (!writable) {
if (ctx.arg.z_text) {
error();
} else if (ctx.arg.warn_textrel) {
Warn(ctx) << isec << ": relocation against symbol `" << sym
<< "' in read-only section";
}
ctx.has_textrel = true;
}
};
auto copyrel = [&] {
assert(sym.is_imported);
if (sym.esym().st_visibility == STV_PROTECTED) {
Error(ctx) << isec
<< ": cannot make copy relocation for protected symbol '" << sym
<< "', defined in " << *sym.file << "; recompile with -fPIC";
}
sym.flags |= NEEDS_COPYREL;
};
auto dynrel = [&] {
check_textrel();
isec.file.num_dynrel++;
};
switch (action) {
case NONE:
break;
case ERROR:
// Print out the "recompile with -fPIC" error message.
error();
break;
case COPYREL:
// Create a copy relocation.
if (!ctx.arg.z_copyreloc)
error();
copyrel();
break;
case DYN_COPYREL:
// Same as COPYREL but try to avoid creating a copy relocation by
// creating a dynamic relocation instead if the relocation is in
// a writable section.
//
// GHC (Glasgow Haskell Compiler) places a small amount of data in
// .text before each function and access that data with a fixed
// offset. The function breaks if we copy-relocate the data. For such
// programs, we should avoid copy relocations if possible.
//
// Besides GHC, copy relocation is a hacky solution, so if we can
// represent a relocation either with copyrel or dynrel, we prefer
// dynamic relocation.
if (writable || !ctx.arg.z_copyreloc)
dynrel();
else
copyrel();
break;
case PLT:
// Create a PLT entry.
sym.flags |= NEEDS_PLT;
break;
case CPLT:
// Create a canonical PLT entry.
sym.flags |= NEEDS_CPLT;
break;
case DYN_CPLT:
// Same as CPLT but try to avoid creating a canonical PLT creating by
// creating a dynamic relocation instead if the relocation is in a
// writable section. The motivation behind it is hte same as DYN_COPYREL.
if (writable)
dynrel();
else
sym.flags |= NEEDS_CPLT;
break;
case DYNREL:
// Create a dynamic relocation.
dynrel();
break;
case BASEREL:
// Create a base relocation.
check_textrel();
if (!isec.is_relr_reloc(ctx, rel))
isec.file.num_dynrel++;
break;
case IFUNC:
// Create an IRELATIVE relocation for a GNU ifunc symbol.
//
// We usually create an IRELATIVE relocation in .got for each ifunc.
// However, if a statically-initialized pointer is initialized to an
// ifunc's address, we have no choice other than emitting an IRELATIVE
// relocation for each such pointer.
dynrel();
ctx.num_ifunc_dynrels++;
break;
default:
unreachable();
}
}
template <typename E>
static inline i64 get_output_type(Context<E> &ctx) {
if (ctx.arg.shared)
return 0;
if (ctx.arg.pie)
@@ -114,7 +228,7 @@ static i64 get_output_type(Context<E> &ctx) {
}
template <typename E>
static i64 get_sym_type(Symbol<E> &sym) {
static inline i64 get_sym_type(Symbol<E> &sym) {
if (sym.is_absolute())
return 0;
if (!sym.is_imported)
@@ -125,134 +239,316 @@ static i64 get_sym_type(Symbol<E> &sym) {
}
template <typename E>
void InputSection<E>::dispatch(Context<E> &ctx, Action table[3][4], i64 i,
const ElfRel<E> &rel, Symbol<E> &sym) {
Action action = table[get_output_type(ctx)][get_sym_type(sym)];
bool is_writable = (shdr().sh_flags & SHF_WRITE);
auto error = [&] {
std::string msg = sym.is_absolute() ? "-fno-PIC" : "-fPIC";
Error(ctx) << *this << ": " << rel << " relocation at offset 0x"
<< std::hex << rel.r_offset << " against symbol `"
<< sym << "' can not be used; recompile with " << msg;
static Action get_pcrel_action(Context<E> &ctx, Symbol<E> &sym) {
// This is for PC-relative relocations (e.g. R_X86_64_PC32).
// We cannot promote them to dynamic relocations because the dynamic
// linker generally does not support PC-relative relocations.
static Action table[3][4] = {
// Absolute Local Imported data Imported code
{ ERROR, NONE, ERROR, PLT }, // Shared object
{ ERROR, NONE, COPYREL, PLT }, // Position-independent exec
{ NONE, NONE, COPYREL, CPLT }, // Position-dependent exec
};
auto warn_textrel = [&] {
if (ctx.arg.warn_textrel)
Warn(ctx) << *this << ": relocation against symbol `" << sym
<< "' in read-only section";
return table[get_output_type(ctx)][get_sym_type(sym)];
}
template <typename E>
static Action get_absrel_action(Context<E> &ctx, Symbol<E> &sym) {
// This is a decision table for absolute relocations that is smaller
// than the pointer size (e.g. R_X86_64_32). Since the dynamic linker
// generally does not support dynamic relocations smaller than the
// pointer size, we need to report an error if a relocation cannot be
// resolved at link-time.
static Action table[3][4] = {
// Absolute Local Imported data Imported code
{ NONE, ERROR, ERROR, ERROR }, // Shared object
{ NONE, ERROR, ERROR, ERROR }, // Position-independent exec
{ NONE, NONE, COPYREL, CPLT }, // Position-dependent exec
};
return table[get_output_type(ctx)][get_sym_type(sym)];
}
template <typename E>
static Action get_dyn_absrel_action(Context<E> &ctx, Symbol<E> &sym) {
if (sym.is_ifunc())
return IFUNC;
// This is a decision table for absolute relocations for the pointer
// size data (e.g. R_X86_64_64). Unlike the absrel_table, we can emit
// a dynamic relocation if we cannot resolve an address at link-time.
static Action table[3][4] = {
// Absolute Local Imported data Imported code
{ NONE, BASEREL, DYNREL, DYNREL }, // Shared object
{ NONE, BASEREL, DYNREL, DYNREL }, // Position-independent exec
{ NONE, NONE, DYN_COPYREL, DYN_CPLT }, // Position-dependent exec
};
return table[get_output_type(ctx)][get_sym_type(sym)];
}
template <typename E>
static Action get_ppc64_toc_action(Context<E> &ctx, Symbol<E> &sym) {
if (sym.is_ifunc())
return IFUNC;
// As a special case, we do not create copy relocations nor canonical
// PLTs for .toc sections. PPC64's .toc is a compiler-generated
// GOT-like section, and no user-generated code directly uses values
// in it.
static Action table[3][4] = {
// Absolute Local Imported data Imported code
{ NONE, BASEREL, DYNREL, DYNREL }, // Shared object
{ NONE, BASEREL, DYNREL, DYNREL }, // Position-independent exec
{ NONE, NONE, DYNREL, DYNREL }, // Position-dependent exec
};
return table[get_output_type(ctx)][get_sym_type(sym)];
}
template <typename E>
void InputSection<E>::scan_pcrel(Context<E> &ctx, Symbol<E> &sym,
const ElfRel<E> &rel) {
scan_rel(ctx, *this, sym, rel, get_pcrel_action(ctx, sym));
}
template <typename E>
void InputSection<E>::scan_absrel(Context<E> &ctx, Symbol<E> &sym,
const ElfRel<E> &rel) {
scan_rel(ctx, *this, sym, rel, get_absrel_action(ctx, sym));
}
template <typename E>
void InputSection<E>::scan_dyn_absrel(Context<E> &ctx, Symbol<E> &sym,
const ElfRel<E> &rel) {
scan_rel(ctx, *this, sym, rel, get_dyn_absrel_action(ctx, sym));
}
template <typename E>
void InputSection<E>::scan_toc_rel(Context<E> &ctx, Symbol<E> &sym,
const ElfRel<E> &rel) {
scan_rel(ctx, *this, sym, rel, get_ppc64_toc_action(ctx, sym));
}
template <typename E>
void InputSection<E>::scan_tlsdesc(Context<E> &ctx, Symbol<E> &sym) {
if (ctx.arg.is_static ||
(ctx.arg.relax && sym.is_tprel_linktime_const(ctx))) {
// Relax TLSDESC to Local Exec. In this case, we directly materialize
// a TP-relative offset, so no dynamic relocation is needed.
//
// TLSDESC relocs must always be relaxed for statically-linked
// executables even if -no-relax is given. It is because a
// statically-linked executable doesn't contain a trampoline
// function needed for TLSDESC.
} else if (ctx.arg.relax && sym.is_tprel_runtime_const(ctx)) {
// In this condition, TP-relative offset of a thread-local variable
// is known at process startup time, so we can relax TLSDESC to the
// code that reads the TP-relative offset from GOT and add TP to it.
sym.flags |= NEEDS_GOTTP;
} else {
// If no relaxation is doable, we simply create a TLSDESC dynamic
// relocation.
sym.flags |= NEEDS_TLSDESC;
}
}
template <typename E>
void InputSection<E>::check_tlsle(Context<E> &ctx, Symbol<E> &sym,
const ElfRel<E> &rel) {
if (ctx.arg.shared)
Error(ctx) << *this << ": relocation " << rel << " against `" << sym
<< "` can not be used when making a shared object;"
<< " recompile with -fPIC";
}
template <typename E>
static void apply_absrel(Context<E> &ctx, InputSection<E> &isec,
Symbol<E> &sym, const ElfRel<E> &rel, u8 *loc,
u64 S, i64 A, u64 P, ElfRel<E> *&dynrel,
Action action) {
bool writable = (isec.shdr().sh_flags & SHF_WRITE);
auto emit_abs_dynrel = [&] {
*dynrel++ = ElfRel<E>(P, E::R_ABS, sym.get_dynsym_idx(ctx), A);
if (ctx.arg.apply_dynamic_relocs)
*(Word<E> *)loc = A;
};
switch (action) {
case NONE:
return;
case ERROR:
error();
return;
case COPYREL:
if (!ctx.arg.z_copyreloc) {
error();
return;
}
if (sym.esym().st_visibility == STV_PROTECTED) {
Error(ctx) << *this
<< ": cannot make copy relocation for protected symbol '"
<< sym << "', defined in " << *sym.file
<< "; recompile with -fPIC";
return;
}
sym.flags |= NEEDS_COPYREL;
return;
case PLT:
sym.flags |= NEEDS_PLT;
return;
case CPLT: {
std::scoped_lock lock(sym.mu);
sym.flags |= NEEDS_PLT;
sym.is_canonical = true;
return;
}
case DYNREL:
if (!is_writable) {
if (ctx.arg.z_text) {
error();
return;
}
warn_textrel();
ctx.has_textrel = true;
}
assert(sym.is_imported);
file.num_dynrel++;
return;
case CPLT:
case NONE:
*(Word<E> *)loc = S + A;
break;
case BASEREL:
if (!is_writable) {
if (ctx.arg.z_text) {
error();
return;
}
warn_textrel();
ctx.has_textrel = true;
if (isec.is_relr_reloc(ctx, rel)) {
*(Word<E> *)loc = S + A;
} else {
*dynrel++ = ElfRel<E>(P, E::R_RELATIVE, 0, S + A);
if (ctx.arg.apply_dynamic_relocs)
*(Word<E> *)loc = S + A;
}
if (!is_relr_reloc(ctx, rel))
file.num_dynrel++;
return;
break;
case DYN_COPYREL:
if (writable || !ctx.arg.z_copyreloc)
emit_abs_dynrel();
else
*(Word<E> *)loc = S + A;
break;
case DYN_CPLT:
if (writable)
emit_abs_dynrel();
else
*(Word<E> *)loc = S + A;
break;
case DYNREL:
emit_abs_dynrel();
break;
case IFUNC:
if constexpr (supports_ifunc<E>) {
u64 addr = sym.get_addr(ctx, NO_PLT) + A;
*dynrel++ = ElfRel<E>(P, E::R_IRELATIVE, 0, addr);
if (ctx.arg.apply_dynamic_relocs)
*(Word<E> *)loc = addr;
} else {
unreachable();
}
break;
default:
unreachable();
}
}
template <typename E>
void InputSection<E>::apply_dyn_absrel(Context<E> &ctx, Symbol<E> &sym,
const ElfRel<E> &rel, u8 *loc,
u64 S, i64 A, u64 P,
ElfRel<E> **dynrel) {
apply_absrel(ctx, *this, sym, rel, loc, S, A, P, *dynrel,
get_dyn_absrel_action(ctx, sym));
}
template <typename E>
void InputSection<E>::apply_toc_rel(Context<E> &ctx, Symbol<E> &sym,
const ElfRel<E> &rel, u8 *loc,
u64 S, i64 A, u64 P,
ElfRel<E> **dynrel) {
apply_absrel(ctx, *this, sym, rel, loc, S, A, P, *dynrel,
get_ppc64_toc_action(ctx, sym));
}
template <typename E>
void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
if (shdr().sh_type == SHT_NOBITS || sh_size == 0)
return;
// Copy data
if constexpr (std::is_same_v<E, RISCV64>) {
if constexpr (is_riscv<E>)
copy_contents_riscv(ctx, buf);
} else if (is_compressed()) {
uncompress(ctx, buf);
} else {
memcpy(buf, contents.data(), contents.size());
}
else
uncompress_to(ctx, buf);
// Apply relocations
if (shdr().sh_flags & SHF_ALLOC)
apply_reloc_alloc(ctx, buf);
else
apply_reloc_nonalloc(ctx, buf);
}
if (!ctx.arg.relocatable) {
if (shdr().sh_flags & SHF_ALLOC)
apply_reloc_alloc(ctx, buf);
else
apply_reloc_nonalloc(ctx, buf);
template <typename E>
void report_undef(Context<E> &ctx, InputFile<E> &file, Symbol<E> &sym) {
if (ctx.arg.warn_once && !ctx.warned.insert({(void *)&sym, 1}))
return;
switch (ctx.arg.unresolved_symbols) {
case UNRESOLVED_ERROR:
Error(ctx) << "undefined symbol: " << file << ": " << sym;
break;
case UNRESOLVED_WARN:
Warn(ctx) << "undefined symbol: " << file << ": " << sym;
break;
case UNRESOLVED_IGNORE:
break;
if constexpr (is_x86_64<E>) {
if (ctx.arg.z_rewrite_endbr) {
// Rewrite the leading endbr instruction with a nop if the section
// is not address-taken.
u8 endbr[] = {0xf3, 0x0f, 0x1e, 0xfa};
u8 nop[] = {0x0f, 0x1f, 0x40, 0x00};
if (!address_taken && (shdr().sh_flags & SHF_EXECINSTR) &&
sh_size >= 4 && memcmp(buf, endbr, 4) == 0)
memcpy(buf, nop, 4);
}
}
}
}
#define INSTANTIATE(E) \
template struct CieRecord<E>; \
template class InputSection<E>; \
template void report_undef(Context<E> &, InputFile<E> &, Symbol<E> &)
// 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) {
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 "";
}
// Test if the symbol a given relocation refers to has already been resolved.
// If not, record that error and returns true.
template <typename E>
bool InputSection<E>::record_undef_error(Context<E> &ctx, const ElfRel<E> &rel) {
// If a relocation refers to a linker-synthesized symbol for a
// section fragment, it's always been resolved.
if (file.elf_syms.size() <= rel.r_sym)
return false;
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
Symbol<E> &sym = *file.symbols[rel.r_sym];
const ElfSym<E> &esym = file.elf_syms[rel.r_sym];
// If a symbol is defined in a comdat group, and the comdat group is
// discarded, the symbol may not have an owner. It is technically an
// violation of the One Definition Rule, so it is a programmer's fault.
if (!sym.file) {
Error(ctx) << *this << ": " << sym << " refers to a discarded COMDAT section"
<< " probably due to an ODR violation";
return true;
}
auto record = [&] {
std::stringstream ss;
if (std::string_view source = file.get_source_name(); !source.empty())
ss << ">>> referenced by " << source << "\n";
else
ss << ">>> referenced by " << *this << "\n";
ss << ">>> " << file;
if (std::string_view func = get_func_name(ctx, rel.r_offset); !func.empty())
ss << ":(" << func << ")";
typename decltype(ctx.undef_errors)::accessor acc;
ctx.undef_errors.insert(acc, {sym.name(), {}});
acc->second.push_back(ss.str());
};
// A non-weak undefined symbol must be promoted to an imported
// symbol or resolved to an defined symbol. Otherwise, it's an
// undefined symbol error.
//
// Every ELF file has an absolute local symbol as its first symbol.
// Referring to that symbol is always valid.
bool is_undef = esym.is_undef() && !esym.is_weak() && sym.sym_idx;
if (!sym.is_imported && is_undef && sym.esym().is_undef()) {
record();
return true;
}
// If a protected/hidden undefined symbol is resolved to other .so,
// it's handled as if no symbols were found.
if (sym.file->is_dso &&
(sym.visibility == STV_PROTECTED || sym.visibility == STV_HIDDEN)) {
record();
return true;
}
return false;
}
using E = MOLD_TARGET;
template struct CieRecord<E>;
template class InputSection<E>;
} // namespace mold::elf
+49
View File
@@ -0,0 +1,49 @@
#include "mold.h"
#ifndef _WIN32
# include <fcntl.h>
# include <pwd.h>
# include <sys/stat.h>
# include <sys/types.h>
# include <unistd.h>
#endif
namespace mold::elf {
template <typename E>
void acquire_global_lock(Context<E> &ctx) {
#ifndef _WIN32
char *jobs = getenv("MOLD_JOBS");
if (!jobs || std::string(jobs) != "1")
return;
char *home = getenv("HOME");
if (!home)
home = getpwuid(getuid())->pw_dir;
std::string path = std::string(home) + "/.mold-lock";
int fd = open(path.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, 0600);
if (fd == -1)
return;
if (lockf(fd, F_LOCK, 0) == -1)
return;
ctx.global_lock_fd = fd;
#endif
}
template <typename E>
void release_global_lock(Context<E> &ctx) {
#ifndef _WIN32
if (ctx.global_lock_fd)
close(*ctx.global_lock_fd);
#endif
}
using E = MOLD_TARGET;
template void acquire_global_lock(Context<E> &);
template void release_global_lock(Context<E> &);
} // namespace mold::elf
+48 -39
View File
@@ -146,8 +146,9 @@ read_output_format(Context<E> &ctx, std::span<std::string_view> tok) {
template <typename E>
static bool is_in_sysroot(Context<E> &ctx, std::string path) {
std::string rel =
to_abs_path(path).lexically_relative(to_abs_path(ctx.arg.sysroot));
std::string rel = to_abs_path(path)
.lexically_relative(to_abs_path(ctx.arg.sysroot))
.string();
return rel != "." && !rel.starts_with("../");
}
@@ -225,6 +226,10 @@ void parse_linker_script(Context<E> &ctx, MappedFile<Context<E>> *mf) {
tok = skip(ctx, tok, "{");
read_version_script(ctx, tok);
tok = skip(ctx, tok, "}");
} else if (tok.size() > 3 && tok[1] == "=" && tok[3] == ";") {
ctx.arg.defsyms.emplace_back(get_symbol(ctx, unquote(tok[0])),
get_symbol(ctx, unquote(tok[2])));
tok = tok.subspan(4);
} else if (tok[0] == ";") {
tok = tok.subspan(1);
} else {
@@ -234,7 +239,8 @@ void parse_linker_script(Context<E> &ctx, MappedFile<Context<E>> *mf) {
}
template <typename E>
i64 get_script_output_type(Context<E> &ctx, MappedFile<Context<E>> *mf) {
std::string_view
get_script_output_type(Context<E> &ctx, MappedFile<Context<E>> *mf) {
current_file<E> = mf;
std::vector<std::string_view> vec = tokenize(ctx, mf->get_contents());
@@ -242,11 +248,18 @@ i64 get_script_output_type(Context<E> &ctx, MappedFile<Context<E>> *mf) {
if (tok.size() >= 3 && tok[0] == "OUTPUT_FORMAT" && tok[1] == "(") {
if (tok[2] == "elf64-x86-64")
return EM_X86_64;
return X86_64::target_name;
if (tok[2] == "elf32-i386")
return EM_386;
return I386::target_name;
}
return -1;
if (tok.size() >= 3 && (tok[0] == "INPUT" || tok[0] == "GROUP") &&
tok[1] == "(")
if (MappedFile<Context<E>> *mf =
MappedFile<Context<E>>::open(ctx, std::string(unquote(tok[2]))))
return get_machine_type(ctx, mf);
return "";
}
static bool read_label(std::span<std::string_view> &tok,
@@ -266,9 +279,8 @@ static bool read_label(std::span<std::string_view> &tok,
template <typename E>
static void
read_version_script_commands(Context<E> &ctx, std::span<std::string_view> &tok,
u16 ver_idx, bool is_cpp) {
bool is_global = true;
std::string_view ver_str, u16 ver_idx,
bool is_global, bool is_cpp) {
while (!tok.empty() && tok[0] != "}") {
if (read_label(tok, "global")) {
is_global = true;
@@ -286,11 +298,11 @@ read_version_script_commands(Context<E> &ctx, std::span<std::string_view> &tok,
if (!tok.empty() && tok[0] == "\"C\"") {
tok = tok.subspan(1);
tok = skip(ctx, tok, "{");
read_version_script_commands( ctx, tok, ver_idx, false);
read_version_script_commands( ctx, tok, ver_str, ver_idx, is_global, false);
} else {
tok = skip(ctx, tok, "\"C++\"");
tok = skip(ctx, tok, "{");
read_version_script_commands(ctx, tok, ver_idx, true);
read_version_script_commands(ctx, tok, ver_str, ver_idx, is_global, true);
}
tok = skip(ctx, tok, "}");
@@ -299,11 +311,14 @@ read_version_script_commands(Context<E> &ctx, std::span<std::string_view> &tok,
}
if (tok[0] == "*") {
ctx.default_version = (is_global ? ver_idx : VER_NDX_LOCAL);
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]), ver_idx, is_cpp});
ctx.version_patterns.push_back({unquote(tok[0]), current_file<E>->name,
ver_str, ver_idx, is_cpp});
} else {
ctx.version_patterns.push_back({unquote(tok[0]), VER_NDX_LOCAL, is_cpp});
ctx.version_patterns.push_back({unquote(tok[0]), current_file<E>->name,
ver_str, VER_NDX_LOCAL, is_cpp});
}
tok = tok.subspan(1);
@@ -319,17 +334,21 @@ void read_version_script(Context<E> &ctx, std::span<std::string_view> &tok) {
u16 next_ver = VER_NDX_LAST_RESERVED + ctx.arg.version_definitions.size() + 1;
while (!tok.empty() && tok[0] != "}") {
std::string_view ver_str;
u16 ver_idx;
if (tok[0] == "{") {
ver_str = "global";
ver_idx = VER_NDX_GLOBAL;
} else {
ver_str = tok[0];
ver_idx = next_ver++;
ctx.arg.version_definitions.push_back(std::string(tok[0]));
tok = tok.subspan(1);
}
tok = skip(ctx, tok, "{");
read_version_script_commands(ctx, tok, ver_idx, false);
read_version_script_commands(ctx, tok, ver_str, ver_idx, true, false);
tok = skip(ctx, tok, "}");
if (!tok.empty() && tok[0] != ";")
tok = tok.subspan(1);
@@ -338,10 +357,9 @@ void read_version_script(Context<E> &ctx, std::span<std::string_view> &tok) {
}
template <typename E>
void parse_version_script(Context<E> &ctx, std::string path) {
current_file<E> = MappedFile<Context<E>>::must_open(ctx, path);
std::vector<std::string_view> vec =
tokenize(ctx, current_file<E>->get_contents());
void parse_version_script(Context<E> &ctx, MappedFile<Context<E>> *mf) {
current_file<E> = mf;
std::vector<std::string_view> vec = tokenize(ctx, mf->get_contents());
std::span<std::string_view> tok = vec;
read_version_script(ctx, tok);
if (!tok.empty())
@@ -373,19 +391,19 @@ void read_dynamic_list_commands(Context<E> &ctx, std::span<std::string_view> &to
if (tok[0] == "*")
ctx.default_version = VER_NDX_GLOBAL;
else
ctx.version_patterns.push_back({unquote(tok[0]), VER_NDX_GLOBAL, is_cpp});
ctx.version_patterns.push_back({unquote(tok[0]), current_file<E>->name,
"global", VER_NDX_GLOBAL, is_cpp});
tok = skip(ctx, tok.subspan(1), ";");
}
}
template <typename E>
void parse_dynamic_list(Context<E> &ctx, std::string path) {
current_file<E> = MappedFile<Context<E>>::must_open(ctx, path);
std::vector<std::string_view> vec =
tokenize(ctx, current_file<E>->get_contents());
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::span<std::string_view> tok = vec;
tok = skip(ctx, tok, "{");
read_dynamic_list_commands(ctx, tok, false);
tok = skip(ctx, tok, "}");
@@ -395,20 +413,11 @@ void parse_dynamic_list(Context<E> &ctx, std::string path) {
SyntaxError(ctx, tok[0]) << "trailing garbage token";
}
#define INSTANTIATE(E) \
template \
void parse_linker_script(Context<E> &ctx, MappedFile<Context<E>> *mf); \
template \
i64 get_script_output_type(Context<E> &ctx, MappedFile<Context<E>> *mf); \
template \
void parse_version_script(Context<E> &ctx, std::string path); \
template \
void parse_dynamic_list(Context<E> &ctx, std::string path)
using E = MOLD_TARGET;
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
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>> *);
} // namespace mold::elf
+737
View File
@@ -0,0 +1,737 @@
// This file handles the linker plugin to support LTO (Link-Time
// Optimization).
//
// LTO is a technique to do whole-program optimization to a program. Since
// a linker sees the whole program as opposed to a single compilation
// unit, it in theory can do some optimizations that cannot be done in the
// usual separate compilation model. For example, LTO should be able to
// inline functions that are defined in other compilation unit.
//
// In GCC and Clang, all you have to do to enable LTO is adding the
// `-flto` flag to the compiler and the linker command lines. If `-flto`
// is given, the compiler generates a file that contains not machine code
// but the compiler's IR (intermediate representation). In GCC, the output
// is an ELF file which wraps GCC's IR. In LLVM, it's not even an ELF file
// but just a raw LLVM IR file.
//
// Here is what we have to do if at least one input file is not a usual
// ELF file but an IR object file:
//
// 1. Read symbols both from usual ELF files and from IR object files and
// resolve symbols as usual.
//
// 2. Pass all IR objects to the compiler backend. The compiler backend
// compiles the IRs and returns a few big ELF object files as a
// result.
//
// 3. Parse the returned ELF files and overwrite IR object symbols with
// the returned ones, discarding IR object files.
//
// 4. Continue the rest of the linking process as usual.
//
// When gcc or clang inovkes ld, they pass `-plugin linker-plugin.so` to
// the linker. The given .so file provides a way to call the compiler
// backend.
//
// The linker plugin API is documented at
// https://gcc.gnu.org/wiki/whopr/driver, though the document is a bit
// outdated.
//
// Frankly, the linker plugin API is peculiar and is not very easy to use.
// For some reason, the API functions don't return the result of a
// function call as a return value but instead calls other function with
// the result as its argument to "return" the result.
//
// For example, the first thing you need to do after dlopen()'ing a linker
// plugin .so is to call `onload` function with a list of callback
// functions. `onload` calls callbacks to notify about the pointers to
// other functions the linker plugin provides. I don't know why `onload`
// can't just return a list of functions or why the linker plugin can't
// define not only `onload` but other functions, but that's what it is.
//
// Here is the steps to use the linker plugin:
//
// 1. dlopen() the linker plugin .so and call `onload` to obtain pointers
// to other functions provided by the plugin.
//
// 2. Call `claim_file_hook` with an IR object file to read its symbol
// table. `claim_file_hook` calls the `add_symbols` callback to
// "return" a list of symbols.
//
// 3. `claim_file_hook` returns LDPT_OK only when the plugin wants to
// handle a given file. Since we pass only IR object files to the
// plugin in mold, it always returns LDPT_OK in our case.
//
// 4. Once we made a decision as to which object file to include into the
// output file, we call `all_symbols_read_hook` to compile IR objects
// into a few big ELF files. That function calls the `get_symbols`
// callback to ask us about the symbol resolution results. (The
// compiler backend needs to know whether an undefined symbol in an IR
// object was resolved to a regular object file or a shared object to
// do whole program optimization, for example.)
//
// 5. `all_symbols_read_hook` "returns" the result by calling the
// `add_input_file` callback. The callback is called with a path to an
// LTO'ed ELF file. We parse that ELF file and override symbols
// defined by IR objects with the ELF file's ones.
//
// 6. Lastly, we call `cleanup_hook` to remove temporary files created by
// the compiler backend.
#include "mold.h"
#include "lto.h"
#include <cstdarg>
#include <cstring>
#include <dlfcn.h>
#include <fcntl.h>
#include <sstream>
#include <tbb/parallel_for_each.h>
#include <unistd.h>
#if 0
# define LOG std::cerr
#else
# define LOG std::ostringstream()
#endif
namespace mold::elf {
// Global variables
// We store LTO-related information to global variables,
// as the LTO plugin is not thread-safe by design anyway.
template <typename E> static Context<E> *gctx;
template <typename E> static std::vector<ObjectFile<E> *> lto_objects;
static int phase = 0;
static std::vector<PluginSymbol> plugin_symbols;
static ClaimFileHandler *claim_file_hook;
static AllSymbolsReadHandler *all_symbols_read_hook;
static CleanupHandler *cleanup_hook;
static bool is_gcc_linker_api_v1 = false;
// Event handlers
template <typename E>
static PluginStatus message(PluginLevel level, const char *fmt, ...) {
LOG << "message\n";
Context<E> &ctx = *gctx<E>;
char buf[1000];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
switch (level) {
case LDPL_INFO:
SyncOut(ctx) << buf;
break;
case LDPL_WARNING:
Warn(ctx) << buf;
break;
case LDPL_ERROR:
case LDPL_FATAL:
Fatal(ctx) << buf;
}
return LDPS_OK;
}
template <typename E>
static PluginStatus register_claim_file_hook(ClaimFileHandler fn) {
LOG << "register_claim_file_hook\n";
claim_file_hook = fn;
return LDPS_OK;
}
template <typename E>
static PluginStatus
register_all_symbols_read_hook(AllSymbolsReadHandler fn) {
LOG << "register_all_symbols_read_hook\n";
all_symbols_read_hook = fn;
return LDPS_OK;
}
template <typename E>
static PluginStatus register_cleanup_hook(CleanupHandler fn) {
LOG << "register_cleanup_hook\n";
cleanup_hook = fn;
return LDPS_OK;
}
static PluginStatus
add_symbols(void *handle, int nsyms, const PluginSymbol *psyms) {
LOG << "add_symbols: " << nsyms << "\n";
assert(phase == 1);
plugin_symbols = {psyms, psyms + nsyms};
return LDPS_OK;
}
template <typename E>
static PluginStatus add_input_file(const char *path) {
LOG << "add_input_file: " << path << "\n";
Context<E> &ctx = *gctx<E>;
static i64 file_priority = 100;
MappedFile<Context<E>> *mf = MappedFile<Context<E>>::must_open(ctx, path);
ObjectFile<E> *file = ObjectFile<E>::create(ctx, mf, "", false);
ctx.obj_pool.emplace_back(file);
lto_objects<E>.push_back(file);
file->priority = file_priority++;
file->is_alive = true;
file->parse(ctx);
file->resolve_symbols(ctx);
return LDPS_OK;
}
static PluginStatus
get_input_file(const void *handle, struct PluginInputFile *file) {
LOG << "get_input_file\n";
return LDPS_OK;
}
template <typename E>
static PluginStatus release_input_file(const void *handle) {
LOG << "release_input_file\n";
ObjectFile<E> &file = *(ObjectFile<E> *)handle;
if (file.mf->fd != -1) {
close(file.mf->fd);
file.mf->fd = -1;
}
return LDPS_OK;
}
static PluginStatus add_input_library(const char *path) {
LOG << "add_input_library\n";
return LDPS_OK;
}
static PluginStatus set_extra_library_path(const char *path) {
LOG << "set_extra_library_path\n";
return LDPS_OK;
}
template <typename E>
static PluginStatus get_view(const void *handle, const void **view) {
LOG << "get_view\n";
ObjectFile<E> &file = *(ObjectFile<E> *)handle;
*view = (void *)file.mf->data;
return LDPS_OK;
}
static PluginStatus
get_input_section_count(const void *handle, int *count) {
LOG << "get_input_section_count\n";
return LDPS_OK;
}
static PluginStatus
get_input_section_type(const PluginSection section, int *type) {
LOG << "get_input_section_type\n";
return LDPS_OK;
}
static PluginStatus
get_input_section_name(const PluginSection section,
char **section_name) {
LOG << "get_input_section_name\n";
return LDPS_OK;
}
static PluginStatus
get_input_section_contents(const PluginSection section,
const char **section_contents,
size_t *len) {
LOG << "get_input_section_contents\n";
return LDPS_OK;
}
static PluginStatus
update_section_order(const PluginSection *section_list,
int num_sections) {
LOG << "update_section_order\n";
return LDPS_OK;
}
static PluginStatus allow_section_ordering() {
LOG << "allow_section_ordering\n";
return LDPS_OK;
}
static PluginStatus
get_symbols_v1(const void *handle, int nsyms, PluginSymbol *psyms) {
unreachable();
}
// get_symbols teaches the LTO plugin as to how we have resolved symbols.
// The plugin uses the symbol resolution info to optimize the program.
//
// For example, if a definition in an IR file is not referenced by
// non-IR objects at all, the plugin may choose to completely inline
// that definition within the IR objects and remove the symbol from the
// LTO result. On the other hand, if a definition is referenced by a
// non-IR object, it has to keep the symbol in the LTO result.
template <typename E>
static PluginStatus
get_symbols(const void *handle, int nsyms, PluginSymbol *psyms, bool is_v2) {
ObjectFile<E> &file = *(ObjectFile<E> *)handle;
assert(file.is_lto_obj);
// If file is an archive member which was not chose to be included in
// to the final result, we need to make the plugin to ignore all
// symbols.
if (!file.is_alive) {
assert(!is_v2);
for (int i = 0; i < nsyms; i++)
psyms[i].resolution = LDPR_PREEMPTED_REG;
return LDPS_NO_SYMS;
}
auto get_resolution = [&](ElfSym<E> &esym, Symbol<E> &sym) {
if (!sym.file)
return LDPR_UNDEF;
if (sym.file == &file) {
if (sym.referenced_by_regular_obj)
return LDPR_PREVAILING_DEF;
if (sym.is_exported)
return is_v2 ? LDPR_PREVAILING_DEF : LDPR_PREVAILING_DEF_IRONLY_EXP;
return LDPR_PREVAILING_DEF_IRONLY;
}
if (sym.file->is_dso)
return LDPR_RESOLVED_DYN;
if (((ObjectFile<E> *)sym.file)->is_lto_obj && !sym.is_wrapped)
return esym.is_undef() ? LDPR_RESOLVED_IR : LDPR_PREEMPTED_IR;
return esym.is_undef() ? LDPR_RESOLVED_EXEC : LDPR_PREEMPTED_REG;
};
// Set the symbol resolution results to psyms.
for (i64 i = 0; i < nsyms; i++) {
ElfSym<E> &esym = file.elf_syms[i + 1];
Symbol<E> &sym = *file.symbols[i + 1];
psyms[i].resolution = get_resolution(esym, sym);
}
return LDPS_OK;
}
// This function restarts mold itself with `--:lto-pass2` and
// `--:ignore-ir-file` flags. We do this as a workaround for the old
// linker plugins that do not support the get_symbols_v3 API.
//
// get_symbols_v1 and get_symbols_v2 don't provide a way to ignore an
// object file we previously passed to the linker plugin. So we can't
// "unload" object files in archives that we ended up not choosing to
// include into the final output.
//
// As a workaround, we restart the linker with a list of object files
// the linker has to ignore, so that it won't read the object files
// from archives next time.
//
// This is an ugly hack and should be removed once GCC adopts the v3 API.
template <typename E>
static void restart_process(Context<E> &ctx) {
std::vector<const char *> args;
for (std::string_view arg : ctx.cmdline_args)
args.push_back(strdup(std::string(arg).c_str()));
for (std::unique_ptr<ObjectFile<E>> &file : ctx.obj_pool)
if (file->is_lto_obj && !file->is_alive)
args.push_back(strdup(("--:ignore-ir-file=" +
file->mf->get_identifier()).c_str()));
args.push_back("--:lto-pass2");
args.push_back(nullptr);
std::cout << std::flush;
std::cerr << std::flush;
std::string self = get_self_path();
execv(self.c_str(), (char * const *)args.data());
std::cerr << "execv failed: " << errno_string() << "\n";
_exit(1);
}
template <typename E>
static PluginStatus
get_symbols_v2(const void *handle, int nsyms, PluginSymbol *psyms) {
LOG << "get_symbols_v2\n";
return get_symbols<E>(handle, nsyms, psyms, true);
}
template <typename E>
static PluginStatus
get_symbols_v3(const void *handle, int nsyms, PluginSymbol *psyms) {
LOG << "get_symbols_v3\n";
return get_symbols<E>(handle, nsyms, psyms, false);
}
static PluginStatus allow_unique_segment_for_sections() {
LOG << "allow_unique_segment_for_sections\n";
return LDPS_OK;
}
static PluginStatus
unique_segment_for_sections(const char *segment_name,
uint64_t flags,
uint64_t align,
const PluginSection *section_list,
int num_sections) {
LOG << "unique_segment_for_sections\n";
return LDPS_OK;
}
static PluginStatus
get_input_section_alignment(const PluginSection section,
int *addralign) {
LOG << "get_input_section_alignment\n";
return LDPS_OK;
}
static PluginStatus
get_input_section_size(const PluginSection section, uint64_t *size) {
LOG << "get_input_section_size\n";
return LDPS_OK;
}
template <typename E>
static PluginStatus
register_new_input_hook(NewInputHandler fn) {
LOG << "register_new_input_hook\n";
return LDPS_OK;
}
static PluginStatus
get_wrap_symbols(uint64_t *num_symbols, const char ***wrap_symbols) {
LOG << "get_wrap_symbols\n";
return LDPS_OK;
}
template <typename E>
static PluginLinkerAPIVersion
get_api_version(const char *plugin_identifier,
unsigned plugin_version,
int minimal_api_supported,
int maximal_api_supported,
const char **linker_identifier,
const char **linker_version) {
if (LAPI_V1 < minimal_api_supported)
Fatal(*gctx<E>) << "LTO plugin does not support V0 or V1 API";
std::string version = mold_version + "\0"s;
*linker_identifier = "mold";
*linker_version = version.data();
if (LAPI_V1 <= maximal_api_supported) {
is_gcc_linker_api_v1 = true;
return LAPI_V1;
}
return LAPI_V0;
}
template <typename E>
static void load_plugin(Context<E> &ctx) {
assert(phase == 0);
phase = 1;
gctx<E> = &ctx;
void *handle = dlopen(ctx.arg.plugin.c_str(), RTLD_NOW | RTLD_GLOBAL);
if (!handle)
Fatal(ctx) << "could not open plugin file: " << dlerror();
OnloadFn *onload = (OnloadFn *)dlsym(handle, "onload");
if (!onload)
Fatal(ctx) << "failed to load plugin " << ctx.arg.plugin << ": "
<< dlerror();
auto save = [&](std::string_view str) {
return save_string(ctx, std::string(str).c_str()).data();
};
std::vector<PluginTagValue> tv;
tv.emplace_back(LDPT_MESSAGE, message<E>);
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);
for (std::string_view opt : ctx.arg.plugin_opt)
tv.emplace_back(LDPT_OPTION, save(opt));
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>
static ElfSym<E> to_elf_sym(PluginSymbol &psym) {
ElfSym<E> esym;
memset(&esym, 0, sizeof(esym));
switch (psym.def) {
case LDPK_DEF:
esym.st_shndx = SHN_ABS;
break;
case LDPK_WEAKDEF:
esym.st_shndx = SHN_ABS;
esym.st_bind = STB_WEAK;
break;
case LDPK_UNDEF:
esym.st_shndx = SHN_UNDEF;
break;
case LDPK_WEAKUNDEF:
esym.st_shndx = SHN_UNDEF;
esym.st_bind = STB_WEAK;
break;
case LDPK_COMMON:
esym.st_shndx = SHN_COMMON;
break;
}
switch (psym.symbol_type) {
case LDST_UNKNOWN:
break;
case LDST_FUNCTION:
esym.st_type = STT_FUNC;
break;
case LDST_VARIABLE:
esym.st_type = STT_OBJECT;
break;
};
switch (psym.visibility) {
case LDPV_DEFAULT:
break;
case LDPV_PROTECTED:
esym.st_visibility = STV_PROTECTED;
break;
case LDPV_INTERNAL:
esym.st_visibility = STV_INTERNAL;
break;
case LDPV_HIDDEN:
esym.st_visibility = STV_HIDDEN;
break;
}
esym.st_size = psym.size;
return esym;
}
// Returns true if a given linker plugin looks like LLVM's one.
// Returns false if it's GCC.
template <typename E>
static bool is_llvm(Context<E> &ctx) {
return ctx.arg.plugin.ends_with("LLVMgold.so");
}
// Returns true if a given linker plugin supports the get_symbols_v3 API.
// Any version of LLVM and GCC 12 or newer support it.
template <typename E>
static bool supports_v3_api(Context<E> &ctx) {
return is_gcc_linker_api_v1 || is_llvm(ctx);
}
template <typename E>
ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
// V0 API's claim_file is not thread-safe.
static std::mutex mu;
std::unique_lock lock(mu, std::defer_lock);
if (!is_gcc_linker_api_v1)
lock.lock();
if (ctx.arg.plugin.empty())
Fatal(ctx) << mf->name << ": don't know how to handle this LTO object file "
<< "because no -plugin option was given. Please make sure you "
<< "added -flto not only for creating object files but also for "
<< "creating the final executable.";
// 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);
obj->filename = mf->name;
obj->symbols.push_back(new Symbol<E>);
obj->first_global = 1;
obj->is_lto_obj = true;
obj->mf = mf;
// 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;
file.handle = (void *)obj;
LOG << "read_lto_symbols: "<< mf->name << "\n";
// claim_file_hook() calls add_symbols() which initializes `plugin_symbols`
int claimed = false;
claim_file_hook(&file, &claimed);
if (!claimed)
Fatal(ctx) << mf->name << ": not claimed by the LTO plugin;"
<< " please make sure you are using the same compiler of the"
<< " same version for all object files";
// It looks like GCC doesn't need fd after claim_file_hook() while
// LLVM needs it and takes the ownership of fd. To prevent "too many
// open files" issue, we close fd only for GCC. This is ugly, though.
if (!is_llvm(ctx)) {
close(mf2->fd);
mf2->fd = -1;
}
// Initialize object symbols
std::vector<ElfSym<E>> *esyms = new std::vector<ElfSym<E>>(1);
obj->has_symver.resize(plugin_symbols.size());
obj->lto_symbol_versions.resize(plugin_symbols.size());
for (i64 i = 0; i < plugin_symbols.size(); i++) {
PluginSymbol &psym = plugin_symbols[i];
esyms->push_back(to_elf_sym<E>(psym));
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));
}
obj->elf_syms = *esyms;
obj->has_symver.resize(esyms->size());
plugin_symbols.clear();
return obj;
}
// Entry point
template <typename E>
std::vector<ObjectFile<E> *> do_lto(Context<E> &ctx) {
Timer t(ctx, "do_lto");
if (!ctx.arg.lto_pass2 && !supports_v3_api(ctx))
restart_process(ctx);
assert(phase == 1);
phase = 2;
// Set `referenced_by_regular_obj` bit.
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
if (!file->is_lto_obj) {
for (Symbol<E> *sym : file->get_global_syms()) {
if (sym->file && !sym->file->is_dso &&
((ObjectFile<E> *)sym->file)->is_lto_obj) {
std::scoped_lock lock(sym->mu);
sym->referenced_by_regular_obj = true;
}
}
}
});
// Symbols specified by the --wrap option needs to be visible from
// regular object files.
for (std::string_view name : ctx.arg.wrap) {
get_symbol(ctx, name)->referenced_by_regular_obj = true;
std::string_view x = save_string(ctx, "__wrap_" + std::string(name));
std::string_view y = save_string(ctx, "__real_" + std::string(name));
get_symbol(ctx, x)->referenced_by_regular_obj = true;
get_symbol(ctx, y)->referenced_by_regular_obj = true;
}
// Handle --entry
get_symbol(ctx, ctx.arg.entry)->referenced_by_regular_obj = true;
// all_symbols_read_hook() calls add_input_file() and add_input_library()
LOG << "all symbols read\n";
if (PluginStatus st = all_symbols_read_hook(); st != LDPS_OK)
Fatal(ctx) << "LTO: all_symbols_read_hook returns " << st;
return lto_objects<E>;
}
template <typename E>
void lto_cleanup(Context<E> &ctx) {
Timer t(ctx, "lto_cleanup");
if (cleanup_hook)
cleanup_hook();
}
using E = MOLD_TARGET;
template ObjectFile<E> *read_lto_object(Context<E> &, MappedFile<Context<E>> *);
template std::vector<ObjectFile<E> *> do_lto(Context<E> &);
template void lto_cleanup(Context<E> &);
} // namespace mold::elf
+25
View File
@@ -0,0 +1,25 @@
#include "mold.h"
#include "lto.h"
namespace mold::elf {
template <typename E>
ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
Fatal(ctx) << "LTO is not supported on Windows";
}
template <typename E>
std::vector<ObjectFile<E> *> do_lto(Context<E> &ctx) {
return {};
}
template <typename E>
void lto_cleanup(Context<E> &ctx) {}
using E = MOLD_TARGET;
template ObjectFile<E> *read_lto_object(Context<E> &, MappedFile<Context<E>> *);
template std::vector<ObjectFile<E> *> do_lto(Context<E> &);
template void lto_cleanup(Context<E> &);
} // namespace mold::elf
+3 -659
View File
@@ -1,661 +1,5 @@
// This file handles the linker plugin to support LTO (Link-Time
// Optimization).
//
// LTO is a technique to do whole-program optimization to a program. Since
// a linker sees the whole program as opposed to a single compilation
// unit, it in theory can do some optimizations that cannot be done in the
// usual separate compilation model. For example, LTO should be able to
// inline functions that are defined in other compilation unit.
//
// In GCC and Clang, all you have to do to enable LTO is adding the
// `-flto` flag to the compiler and the linker command lines. If `-flto`
// is given, the compiler generates a file that contains not machine code
// but the compiler's IR (intermediate representation). In GCC, the output
// is an ELF file which wraps GCC's IR. In LLVM, it's not even an ELF file
// but just a raw LLVM IR file.
//
// Here is what we have to do if at least one input file is not a usual
// ELF file but an IR object file:
//
// 1. Read symbols both from usual ELF files and from IR object files and
// resolve symbols as usual.
//
// 2. Pass all IR objects to the compiler backend. The compiler backend
// compiles the IRs and returns a few big ELF object files as a
// result.
//
// 3. Parse the returned ELF files and overwrite IR object symbols with
// the returned ones, discarding IR object files.
//
// 4. Continue the rest of the linking process as usual.
//
// When gcc or clang inovkes ld, they pass `-plugin linker-plugin.so` to
// the linker. The given .so file provides a way to call the compiler
// backend.
//
// The linker plugin API is documented at
// https://gcc.gnu.org/wiki/whopr/driver, though the document is a bit
// outdated.
//
// Frankly, the linker plugin API is peculiar and is not very easy to use.
// For some reason, the API functions don't return the result of a
// function call as a return value but instead calls other function with
// the result as its argument to "return" the result.
//
// For example, the first thing you need to do after dlopen()'ing a linker
// plugin .so is to call `onload` function with a list of callback
// functions. `onload` calls callbacks to notify about the pointers to
// other functions the linker plugin provides. I don't know why `onload`
// can't just return a list of functions or why the linker plugin can't
// define not only `onload` but other functions, but that's how it works.
//
// Here is the steps to use the linker plugin:
//
// 1. dlopen() the linker plugin .so and call `onload` to obtain pointers
// to other functions provided by the plugin.
//
// 2. Call `claim_file_hook` with an IR object file to read its symbol
// table. `claim_file_hook` calls the `add_symbols` callback to
// "return" a list of symbols.
//
// 3. `claim_file_hook` returns LDPT_OK only when the plugin wants to
// handle a given file. Since we pass only IR object files to the
// plugin in mold, it always returns LDPT_OK in our case.
//
// 4. Once we made a decision as to which object file to include into the
// output file, we call `all_symbols_read_hook` to compile IR objects
// into a few big ELF files. That function calls the `get_symbols`
// callback to ask us about the symbol resolution results. (The
// compiler backend needs to know whether an undefined symbol in an IR
// object was resolved to a regular object file or a shared object to
// do whole program optimization, for example.)
//
// 5. `all_symbols_read_hook` "returns" the result by calling the
// `add_input_file` callback. The callback is called with a path to an
// LTO'ed ELF file. We parse that ELF file and override symbols
// defined by IR objects with the ELF file's ones.
//
// 6. Lastly, we call `cleanup_hook` to remove temporary files created by
// the compiler backend.
#include "mold.h"
#include "../lto.h"
#include <cstdarg>
#include <cstring>
#include <dlfcn.h>
#include <fcntl.h>
#include <sstream>
#include <unistd.h>
#if 0
# define LOG std::cerr
#ifdef _WIN32
# include "lto-win32.cc"
#else
# define LOG std::ostringstream()
# include "lto-unix.cc"
#endif
namespace mold::elf {
// Global variables
// We store LTO-related information to global variables,
// as the LTO plugin is not thread-safe by design anyway.
template <typename E> static Context<E> *gctx;
template <typename E> static std::vector<ObjectFile<E> *> lto_objects;
static int phase = 0;
static std::vector<PluginSymbol> plugin_symbols;
static ClaimFileHandler *claim_file_hook;
static AllSymbolsReadHandler *all_symbols_read_hook;
static CleanupHandler *cleanup_hook;
// Event handlers
static PluginStatus message(int level, const char *fmt, ...) {
LOG << "message\n";
va_list ap;
va_start(ap, fmt);
fprintf(stderr, "mold: ");
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
return LDPS_OK;
}
template <typename E>
static PluginStatus register_claim_file_hook(ClaimFileHandler fn) {
LOG << "register_claim_file_hook\n";
claim_file_hook = fn;
return LDPS_OK;
}
template <typename E>
static PluginStatus
register_all_symbols_read_hook(AllSymbolsReadHandler fn) {
LOG << "register_all_symbols_read_hook\n";
all_symbols_read_hook = fn;
return LDPS_OK;
}
template <typename E>
static PluginStatus register_cleanup_hook(CleanupHandler fn) {
LOG << "register_cleanup_hook\n";
cleanup_hook = fn;
return LDPS_OK;
}
static PluginStatus
add_symbols(void *handle, int nsyms, const PluginSymbol *psyms) {
LOG << "add_symbols: " << nsyms << "\n";
assert(phase == 1);
plugin_symbols = {psyms, psyms + nsyms};
return LDPS_OK;
}
template <typename E>
static PluginStatus add_input_file(const char *path) {
LOG << "add_input_file: " << path << "\n";
Context<E> &ctx = *gctx<E>;
static i64 file_priority = 100;
MappedFile<Context<E>> *mf = MappedFile<Context<E>>::must_open(ctx, path);
ObjectFile<E> *file = ObjectFile<E>::create(ctx, mf, "", false);
ctx.obj_pool.emplace_back(file);
lto_objects<E>.push_back(file);
file->priority = file_priority++;
file->is_alive = true;
file->parse(ctx);
file->resolve_symbols(ctx);
return LDPS_OK;
}
static PluginStatus
get_input_file(const void *handle, struct PluginInputFile *file) {
LOG << "get_input_file\n";
return LDPS_OK;
}
template <typename E>
static PluginStatus release_input_file(const void *handle) {
LOG << "release_input_file\n";
ObjectFile<E> &file = *(ObjectFile<E> *)handle;
if (file.mf->fd != -1) {
close(file.mf->fd);
file.mf->fd = -1;
}
return LDPS_OK;
}
static PluginStatus add_input_library(const char *path) {
LOG << "add_input_library\n";
return LDPS_OK;
}
static PluginStatus set_extra_library_path(const char *path) {
LOG << "set_extra_library_path\n";
return LDPS_OK;
}
template <typename E>
static PluginStatus get_view(const void *handle, const void **view) {
LOG << "get_view\n";
ObjectFile<E> &file = *(ObjectFile<E> *)handle;
*view = (void *)file.mf->data;
return LDPS_OK;
}
static PluginStatus
get_input_section_count(const void *handle, int *count) {
LOG << "get_input_section_count\n";
return LDPS_OK;
}
static PluginStatus
get_input_section_type(const PluginSection section, int *type) {
LOG << "get_input_section_type\n";
return LDPS_OK;
}
static PluginStatus
get_input_section_name(const PluginSection section,
char **section_name) {
LOG << "get_input_section_name\n";
return LDPS_OK;
}
static PluginStatus
get_input_section_contents(const PluginSection section,
const char **section_contents,
size_t *len) {
LOG << "get_input_section_contents\n";
return LDPS_OK;
}
static PluginStatus
update_section_order(const PluginSection *section_list,
int num_sections) {
LOG << "update_section_order\n";
return LDPS_OK;
}
static PluginStatus allow_section_ordering() {
LOG << "allow_section_ordering\n";
return LDPS_OK;
}
static PluginStatus
get_symbols_v1(const void *handle, int nsyms, PluginSymbol *psyms) {
unreachable();
}
// get_symbols teaches the LTO plugin as to how we resolved symbols.
// The plugin uses the symbol resolution info to optimizes the program.
//
// For example, if a definition in an IR file is not referenced by
// non-IR objects at all, the plugin may choose to completely inline
// that definition within the IR objects and remove the symbol from the
// LTO result. On the other hand, if a definition is referenced by a
// non-IR object, it has to keep the symbol in the LTO result.
template <typename E>
static PluginStatus
get_symbols(const void *handle, int nsyms, PluginSymbol *psyms, bool is_v2) {
ObjectFile<E> &file = *(ObjectFile<E> *)handle;
assert(file.is_lto_obj);
// If file is an archive member which was not chose to be included in
// to the final result, we need to make the plugin to ignore all
// symbols.
if (!file.is_alive) {
assert(!is_v2);
for (int i = 0; i < nsyms; i++)
psyms[i].resolution = LDPR_PREEMPTED_REG;
return LDPS_NO_SYMS;
}
auto get_resolution = [&](ElfSym<E> &esym, Symbol<E> &sym) {
if (!sym.file)
return LDPR_UNDEF;
if (sym.file == &file) {
if (sym.referenced_by_regular_obj)
return LDPR_PREVAILING_DEF;
if (sym.is_exported)
return is_v2 ? LDPR_PREVAILING_DEF : LDPR_PREVAILING_DEF_IRONLY_EXP;
return LDPR_PREVAILING_DEF_IRONLY;
}
if (sym.file->is_dso)
return LDPR_RESOLVED_DYN;
if (((ObjectFile<E> *)sym.file)->is_lto_obj)
return esym.is_undef() ? LDPR_RESOLVED_IR : LDPR_PREEMPTED_IR;
return esym.is_undef() ? LDPR_RESOLVED_EXEC : LDPR_PREEMPTED_REG;
};
// Set the symbol resolution results to psyms.
for (i64 i = 0; i < nsyms; i++) {
ElfSym<E> &esym = file.elf_syms[i + 1];
Symbol<E> &sym = *file.symbols[i + 1];
psyms[i].resolution = get_resolution(esym, sym);
}
return LDPS_OK;
}
template <typename E>
static PluginStatus
get_symbols_v2(const void *handle, int nsyms, PluginSymbol *psyms) {
LOG << "get_symbols_v2\n";
return get_symbols<E>(handle, nsyms, psyms, true);
}
template <typename E>
static PluginStatus
get_symbols_v3(const void *handle, int nsyms, PluginSymbol *psyms) {
LOG << "get_symbols_v3\n";
return get_symbols<E>(handle, nsyms, psyms, false);
}
static PluginStatus allow_unique_segment_for_sections() {
LOG << "allow_unique_segment_for_sections\n";
return LDPS_OK;
}
static PluginStatus
unique_segment_for_sections(const char *segment_name,
uint64_t flags,
uint64_t align,
const PluginSection *section_list,
int num_sections) {
LOG << "unique_segment_for_sections\n";
return LDPS_OK;
}
static PluginStatus
get_input_section_alignment(const PluginSection section,
int *addralign) {
LOG << "get_input_section_alignment\n";
return LDPS_OK;
}
static PluginStatus
get_input_section_size(const PluginSection section, uint64_t *size) {
LOG << "get_input_section_size\n";
return LDPS_OK;
}
template <typename E>
static PluginStatus
register_new_input_hook(NewInputHandler fn) {
LOG << "register_new_input_hook\n";
return LDPS_OK;
}
static PluginStatus
get_wrap_symbols(uint64_t *num_symbols, const char ***wrap_symbols) {
LOG << "get_wrap_symbols\n";
return LDPS_OK;
}
template <typename E>
static void load_plugin(Context<E> &ctx) {
assert(phase == 0);
phase = 1;
gctx<E> = &ctx;
void *handle = dlopen(ctx.arg.plugin.c_str(), RTLD_NOW | RTLD_GLOBAL);
if (!handle)
Fatal(ctx) << "could not open plugin file: " << dlerror();
OnloadFn *onload = (OnloadFn *)dlsym(handle, "onload");
if (!onload)
Fatal(ctx) << "failed to load plugin " << ctx.arg.plugin << ": "
<< dlerror();
auto save = [&](std::string_view str) {
return save_string(ctx, std::string(str).c_str()).data();
};
std::vector<PluginTagValue> tv;
tv.emplace_back(LDPT_MESSAGE, message);
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);
for (std::string_view opt : ctx.arg.plugin_opt)
tv.emplace_back(LDPT_OPTION, save(opt));
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_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_NULL, 0);
onload(tv.data());
}
template <typename E>
static ElfSym<E> to_elf_sym(PluginSymbol &psym) {
ElfSym<E> esym = {};
switch (psym.def) {
case LDPK_DEF:
esym.st_shndx = SHN_ABS;
break;
case LDPK_WEAKDEF:
esym.st_shndx = SHN_ABS;
esym.st_bind = STB_WEAK;
break;
case LDPK_UNDEF:
esym.st_shndx = SHN_UNDEF;
break;
case LDPK_WEAKUNDEF:
esym.st_shndx = SHN_UNDEF;
esym.st_bind = STB_WEAK;
break;
case LDPK_COMMON:
esym.st_shndx = SHN_COMMON;
break;
}
switch (psym.symbol_type) {
case LDST_UNKNOWN:
break;
case LDST_FUNCTION:
esym.st_type = STT_FUNC;
break;
case LDST_VARIABLE:
esym.st_type = STT_OBJECT;
break;
};
switch (psym.visibility) {
case LDPV_DEFAULT:
break;
case LDPV_PROTECTED:
esym.st_visibility = STV_PROTECTED;
break;
case LDPV_INTERNAL:
esym.st_visibility = STV_INTERNAL;
break;
case LDPV_HIDDEN:
esym.st_visibility = STV_HIDDEN;
break;
}
esym.st_size = psym.size;
return esym;
}
// Returns true if a given linker plugin looks like LLVM's one.
// Returns false if it's GCC.
template <typename E>
static bool is_llvm(Context<E> &ctx) {
return ctx.arg.plugin.ends_with("LLVMgold.so");
}
// Returns true if a given linker plugin supports the get_symbols_v3 API.
// Currently, we simply assume that LLVM supports it and GCC does not.
template <typename E>
static bool suppots_v3_api(Context<E> &ctx) {
return is_llvm(ctx);
}
template <typename E>
ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
LOG << "read_lto_object: " << mf->name << "\n";
if (ctx.arg.plugin.empty())
Fatal(ctx) << mf->name << ": don't know how to handle this LTO object file "
<< "because no -plugin option was given. Please make sure you "
<< "added -flto not only for creating object files but also for "
<< "creating the final executable.";
// dlopen the linker plugin file
static std::once_flag flag;
std::call_once(flag, [&] { load_plugin(ctx); });
// Create mold's object instance
ObjectFile<E> *obj = new ObjectFile<E>;
ctx.obj_pool.emplace_back(obj);
obj->filename = mf->name;
obj->symbols.push_back(new Symbol<E>);
obj->first_global = 1;
obj->is_lto_obj = true;
obj->mf = mf;
// 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;
file.handle = (void *)obj;
LOG << "read_lto_symbols: "<< mf->name << "\n";
// claim_file_hook() calls add_symbols() which initializes `plugin_symbols`
int claimed = false;
claim_file_hook(&file, &claimed);
if (!claimed)
Fatal(ctx) << mf->name << ": not claimed by the LTO plugin";
// It looks like GCC doesn't need fd after claim_file_hook() while
// LLVM needs it and takes the ownership of fd. To prevent "too many
// open files" issue, we close fd only for GCC. This is ugly, though.
if (!is_llvm(ctx)) {
close(mf2->fd);
mf2->fd = -1;
}
// Initialize object symbols
std::vector<ElfSym<E>> *esyms = new std::vector<ElfSym<E>>(1);
for (PluginSymbol &psym : plugin_symbols) {
esyms->push_back(to_elf_sym<E>(psym));
obj->symbols.push_back(get_symbol(ctx, save_string(ctx, psym.name)));
}
obj->elf_syms = *esyms;
obj->sym_fragments.resize(esyms->size());
obj->symvers.resize(esyms->size());
plugin_symbols.clear();
return obj;
}
// This function restarts mold itself with `--:lto-pass2` and
// `--:ignore-ir-file` flags. We do this as a workaround for the old
// linker plugins that do not support the get_symbols_v3 API.
//
// get_symbols_v1 and get_symbols_v2 don't provide a way to ignore an
// object file we previously passed to the linker plugin. So we can't
// "unload" object files in archives that we ended up not choosing to
// include into the final output.
//
// As a workaround, we restart the linker with a list of object files
// the linker has to ignore, so that it won't read the object files
// from archives next time.
//
// This is an ugly hack and should be removed once GCC adopts the v3 API.
template <typename E>
static void restart_process(Context<E> &ctx) {
std::vector<const char *> args;
for (std::string_view arg : ctx.cmdline_args)
args.push_back(strdup(std::string(arg).c_str()));
for (std::unique_ptr<ObjectFile<E>> &file : ctx.obj_pool)
if (file->is_lto_obj && !file->is_alive)
args.push_back(strdup(("--:ignore-ir-file=" +
file->mf->get_identifier()).c_str()));
args.push_back("--:lto-pass2");
args.push_back(nullptr);
std::string self = std::filesystem::read_symlink("/proc/self/exe");
std::cout << std::flush;
std::cerr << std::flush;
execv(self.c_str(), (char * const *)args.data());
std::cerr << "execv failed: " << errno_string() << "\n";
_exit(1);
}
// Entry point
template <typename E>
std::vector<ObjectFile<E> *> do_lto(Context<E> &ctx) {
Timer t(ctx, "do_lto");
if (!ctx.arg.lto_pass2 && !suppots_v3_api(ctx))
restart_process(ctx);
assert(phase == 1);
phase = 2;
// Set `referenced_by_regular_obj` bit.
for (ObjectFile<E> *file : ctx.objs) {
if (file->is_lto_obj)
continue;
for (i64 i = file->first_global; i < file->symbols.size(); i++) {
ElfSym<E> &esym = file->elf_syms[i];
Symbol<E> &sym = *file->symbols[i];
if (esym.is_undef() && sym.file && sym.file != file &&
!sym.file->is_dso && ((ObjectFile<E> *)sym.file)->is_lto_obj) {
std::scoped_lock lock(sym.mu);
sym.referenced_by_regular_obj = true;
}
}
}
// all_symbols_read_hook() calls add_input_file() and add_input_library()
LOG << "all symbols read\n";
if (PluginStatus st = all_symbols_read_hook(); st != LDPS_OK)
Fatal(ctx) << "LTO: all_symbols_read_hook returns " << st;
return lto_objects<E>;
}
template <typename E>
void lto_cleanup(Context<E> &ctx) {
Timer t(ctx, "lto_cleanup");
if (cleanup_hook)
cleanup_hook();
}
#define INSTANTIATE(E) \
template ObjectFile<E> * \
read_lto_object(Context<E> &, MappedFile<Context<E>> *); \
template std::vector<ObjectFile<E> *> do_lto(Context<E> &); \
template void lto_cleanup(Context<E> &)
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+27 -13
View File
@@ -1,5 +1,7 @@
#pragma once
#include "../common/integers.h"
namespace mold {
enum PluginStatus {
@@ -44,6 +46,7 @@ enum PluginTag {
LDPT_REGISTER_NEW_INPUT_HOOK,
LDPT_GET_WRAP_SYMBOLS,
LDPT_ADD_SYMBOLS_V2,
LDPT_GET_API_VERSION,
};
enum PluginApiVersion {
@@ -52,9 +55,7 @@ enum PluginApiVersion {
struct PluginTagValue {
PluginTagValue(PluginTag tag, int val) : tag(tag), val(val) {}
template <typename T>
PluginTagValue(PluginTag tag, T *ptr) : tag(tag), ptr((void *)ptr) {}
PluginTagValue(PluginTag tag, auto *ptr) : tag(tag), ptr((void *)ptr) {}
PluginTag tag;
union {
@@ -72,27 +73,35 @@ enum PluginOutputFileType {
struct PluginInputFile {
const char *name;
int fd;
off_t offset;
off_t filesize;
i32 fd;
u64 offset;
u64 filesize;
void *handle;
};
struct PluginSection {
const void *handle;
int shndx;
u32 shndx;
};
struct PluginSymbol {
char *name;
char *version;
char def;
char symbol_type;
char section_kind;
int visibility;
uint64_t size;
#ifdef __LITTLE_ENDIAN__
u8 def;
u8 symbol_type;
u8 section_kind;
u8 padding;
#else
u8 padding;
u8 section_kind;
u8 symbol_type;
u8 def;
#endif
i32 visibility;
u64 size;
char *comdat_key;
int resolution;
i32 resolution;
};
enum PluginSymbolKind {
@@ -141,6 +150,11 @@ enum PluginLevel {
LDPL_FATAL,
};
enum PluginLinkerAPIVersion {
LAPI_V0,
LAPI_V1,
};
typedef PluginStatus OnloadFn(PluginTagValue *tv);
typedef PluginStatus ClaimFileHandler(const PluginInputFile *, int *);
typedef PluginStatus AllSymbolsReadHandler();
+297 -354
View File
@@ -1,30 +1,121 @@
#include "mold.h"
#include "../archive-file.h"
#include "../cmdline.h"
#include "../common/archive-file.h"
#include "../common/cmdline.h"
#include "../common/output-file.h"
#include <cstring>
#include <functional>
#include <iomanip>
#include <map>
#include <regex>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <tbb/global_control.h>
#include <tbb/parallel_for_each.h>
#include <unistd.h>
#include <unordered_set>
#ifdef _WIN32
# include <direct.h>
# define _chdir chdir
#else
# include <unistd.h>
#endif
namespace mold::elf {
// Read the beginning of a given file and returns its machine type
// (e.g. EM_X86_64 or EM_386).
template <typename E>
std::string_view get_machine_type(Context<E> &ctx, MappedFile<Context<E>> *mf) {
auto get_elf_type = [&](u8 *buf) -> std::string_view {
bool is_le = (((ElfEhdr<I386> *)buf)->e_ident[EI_DATA] == ELFDATA2LSB);
bool is_64;
u32 e_machine;
if (is_le) {
auto &ehdr = *(ElfEhdr<I386> *)buf;
is_64 = (ehdr.e_ident[EI_CLASS] == ELFCLASS64);
e_machine = ehdr.e_machine;
} else {
auto &ehdr = *(ElfEhdr<M68K> *)buf;
is_64 = (ehdr.e_ident[EI_CLASS] == ELFCLASS64);
e_machine = ehdr.e_machine;
}
switch (e_machine) {
case EM_386:
return I386::target_name;
case EM_X86_64:
return X86_64::target_name;
case EM_ARM:
return ARM32::target_name;
case EM_AARCH64:
return ARM64::target_name;
case EM_RISCV:
if (is_le)
return is_64 ? RV64LE::target_name : RV32LE::target_name;
return is_64 ? RV64BE::target_name : RV32BE::target_name;
case EM_PPC:
return PPC32::target_name;
case EM_PPC64:
return is_le ? PPC64V2::target_name : PPC64V1::target_name;
case EM_S390X:
return S390X::target_name;
case EM_SPARC64:
return SPARC64::target_name;
case EM_68K:
return M68K::target_name;
case EM_SH:
return SH4::target_name;
case EM_ALPHA:
return ALPHA::target_name;
case EM_LOONGARCH:
return is_64 ? LOONGARCH64::target_name : LOONGARCH32::target_name;
default:
return "";
}
};
switch (get_file_type(ctx, mf)) {
case FileType::ELF_OBJ:
case FileType::ELF_DSO:
case FileType::GCC_LTO_OBJ:
return get_elf_type(mf->data);
case FileType::AR:
for (MappedFile<Context<E>> *child : read_fat_archive_members(ctx, mf))
if (get_file_type(ctx, child) == FileType::ELF_OBJ)
return get_elf_type(child->data);
return "";
case FileType::THIN_AR:
for (MappedFile<Context<E>> *child : read_thin_archive_members(ctx, mf))
if (get_file_type(ctx, child) == FileType::ELF_OBJ)
return get_elf_type(child->data);
return "";
case FileType::TEXT:
return get_script_output_type(ctx, mf);
default:
return "";
}
}
template <typename E>
static void
check_file_compatibility(Context<E> &ctx, MappedFile<Context<E>> *mf) {
std::string_view target = get_machine_type(ctx, mf);
if (target != ctx.arg.emulation)
Fatal(ctx) << mf->name << ": incompatible file type: "
<< ctx.arg.emulation << " is expected but got " << target;
}
template <typename E>
static ObjectFile<E> *new_object_file(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::string archive_name) {
if (i64 type = ((ElfEhdr<E> *)mf->data)->e_machine; type != E::e_machine)
Fatal(ctx) << mf->name << ": incompatible file type: " << type;
static Counter count("parsed_objs");
count++;
check_file_compatibility(ctx, mf);
bool in_lib = ctx.in_lib || (!archive_name.empty() && !ctx.whole_archive);
ObjectFile<E> *file = ObjectFile<E>::create(ctx, mf, archive_name, in_lib);
file->priority = ctx.file_priority++;
@@ -41,10 +132,11 @@ static ObjectFile<E> *new_lto_obj(Context<E> &ctx, MappedFile<Context<E>> *mf,
count++;
if (ctx.arg.ignore_ir_file.count(mf->get_identifier()))
return new ObjectFile<E>;
return nullptr;
ObjectFile<E> *file = read_lto_object(ctx, mf);
file->priority = ctx.file_priority++;
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;
@@ -56,8 +148,7 @@ static ObjectFile<E> *new_lto_obj(Context<E> &ctx, MappedFile<Context<E>> *mf,
template <typename E>
static SharedFile<E> *
new_shared_file(Context<E> &ctx, MappedFile<Context<E>> *mf) {
if (i64 type = ((ElfEhdr<E> *)mf->data)->e_machine; type != E::e_machine)
Fatal(ctx) << mf->name << ": incompatible file type: " << type;
check_file_compatibility(ctx, mf);
SharedFile<E> *file = SharedFile<E>::create(ctx, mf);
file->priority = ctx.file_priority++;
@@ -72,8 +163,7 @@ void read_file(Context<E> &ctx, MappedFile<Context<E>> *mf) {
if (ctx.visited.contains(mf->name))
return;
FileType type = get_file_type(mf);
switch (type) {
switch (get_file_type(ctx, mf)) {
case FileType::ELF_OBJ:
ctx.objs.push_back(new_object_file(ctx, mf, ""));
return;
@@ -84,66 +174,48 @@ void read_file(Context<E> &ctx, MappedFile<Context<E>> *mf) {
case FileType::AR:
case FileType::THIN_AR:
for (MappedFile<Context<E>> *child : read_archive_members(ctx, mf)) {
switch (get_file_type(child)) {
switch (get_file_type(ctx, child)) {
case FileType::ELF_OBJ:
ctx.objs.push_back(new_object_file(ctx, child, mf->name));
break;
case FileType::GCC_LTO_OBJ:
case FileType::LLVM_BITCODE:
ctx.objs.push_back(new_lto_obj(ctx, child, mf->name));
if (ObjectFile<E> *file = new_lto_obj(ctx, child, mf->name))
ctx.objs.push_back(file);
break;
case FileType::ELF_DSO:
Warn(ctx) << mf->name << "(" << child->name
<< "): shared object file in an archive is ignored";
break;
default:
break;
}
}
ctx.visited.insert(mf->name);
if (!ctx.whole_archive)
ctx.visited.insert(mf->name);
return;
case FileType::TEXT:
parse_linker_script(ctx, mf);
return;
case FileType::GCC_LTO_OBJ:
case FileType::LLVM_BITCODE:
ctx.objs.push_back(new_lto_obj(ctx, mf, ""));
if (ObjectFile<E> *file = new_lto_obj(ctx, mf, ""))
ctx.objs.push_back(file);
return;
default:
Fatal(ctx) << mf->name << ": unknown file type: " << type;
}
}
// Read the beginning of a given file and returns its machine type
// (e.g. EM_X86_64 or EM_386). Return -1 if unknown.
template <typename E>
static i64 get_machine_type(Context<E> &ctx, MappedFile<Context<E>> *mf) {
switch (get_file_type(mf)) {
case FileType::ELF_OBJ:
case FileType::ELF_DSO:
case FileType::GCC_LTO_OBJ:
return ((ElfEhdr<E> *)mf->data)->e_machine;
case FileType::AR:
for (MappedFile<Context<E>> *child : read_fat_archive_members(ctx, mf))
if (get_file_type(child) == FileType::ELF_OBJ)
return ((ElfEhdr<E> *)child->data)->e_machine;
return -1;
case FileType::THIN_AR:
for (MappedFile<Context<E>> *child : read_thin_archive_members(ctx, mf))
if (get_file_type(child) == FileType::ELF_OBJ)
return ((ElfEhdr<E> *)child->data)->e_machine;
return -1;
case FileType::TEXT:
return get_script_output_type(ctx, mf);
default:
return -1;
Fatal(ctx) << mf->name << ": unknown file type";
}
}
template <typename E>
static i64
deduce_machine_type(Context<E> &ctx, std::span<std::string_view> args) {
static std::string_view
deduce_machine_type(Context<E> &ctx, std::span<std::string> args) {
for (std::string_view arg : args)
if (!arg.starts_with('-'))
if (auto *mf = MappedFile<Context<E>>::open(ctx, std::string(arg)))
if (i64 type = get_machine_type(ctx, mf); type != -1)
return type;
if (std::string_view target = get_machine_type(ctx, mf);
!target.empty())
return target;
Fatal(ctx) << "-m option is missing";
}
@@ -153,10 +225,10 @@ MappedFile<Context<E>> *open_library(Context<E> &ctx, std::string path) {
if (!mf)
return nullptr;
i64 type = get_machine_type(ctx, mf);
if (type == -1 || type == E::e_machine)
std::string_view target = get_machine_type(ctx, mf);
if (target.empty() || target == E::target_name)
return mf;
Warn(ctx) << path << ": skipping incompatible file " << (int)type
Warn(ctx) << path << ": skipping incompatible file " << target
<< " " << (int)E::e_machine;
return nullptr;
}
@@ -184,51 +256,80 @@ MappedFile<Context<E>> *find_library(Context<E> &ctx, std::string name) {
}
template <typename E>
static void read_input_files(Context<E> &ctx, std::span<std::string_view> args) {
MappedFile<Context<E>> *find_from_search_paths(Context<E> &ctx, std::string name) {
if (MappedFile<Context<E>> *mf = MappedFile<Context<E>>::open(ctx, name))
return mf;
for (std::string_view dir : ctx.arg.library_paths)
if (MappedFile<Context<E>> *mf =
MappedFile<Context<E>>::open(ctx, std::string(dir) + "/" + name))
return mf;
return nullptr;
}
template <typename E>
static void read_input_files(Context<E> &ctx, std::span<std::string> args) {
Timer t(ctx, "read_input_files");
std::vector<std::tuple<bool, bool, bool, bool>> state;
ctx.is_static = ctx.arg.is_static;
while (!args.empty()) {
std::string_view arg;
std::string_view arg = args[0];
args = args.subspan(1);
if (read_flag(args, "as-needed")) {
if (arg == "--as-needed") {
ctx.as_needed = true;
} else if (read_flag(args, "no-as-needed")) {
} else if (arg == "--no-as-needed") {
ctx.as_needed = false;
} else if (read_flag(args, "whole-archive")) {
} else if (arg == "--whole-archive") {
ctx.whole_archive = true;
} else if (read_flag(args, "no-whole-archive")) {
} else if (arg == "--no-whole-archive") {
ctx.whole_archive = false;
} else if (read_flag(args, "Bstatic")) {
} else if (arg == "--Bstatic") {
ctx.is_static = true;
} else if (read_flag(args, "Bdynamic")) {
} else if (arg == "--Bdynamic") {
ctx.is_static = false;
} else if (read_flag(args, "start-lib")) {
} else if (arg == "--start-lib") {
ctx.in_lib = true;
} else if (read_flag(args, "end-lib")) {
} else if (arg == "--end-lib") {
ctx.in_lib = false;
} else if (read_arg(ctx, args, arg, "version-script")) {
parse_version_script(ctx, std::string(arg));
} else if (read_arg(ctx, args, arg, "dynamic-list")) {
parse_dynamic_list(ctx, std::string(arg));
} else if (read_flag(args, "push-state")) {
} else if (remove_prefix(arg, "--version-script=")) {
MappedFile<Context<E>> *mf = find_from_search_paths(ctx, std::string(arg));
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});
} else if (read_flag(args, "pop-state")) {
} else if (arg == "--pop-state") {
if (state.empty())
Fatal(ctx) << "no state pushed before popping";
std::tie(ctx.as_needed, ctx.whole_archive, ctx.is_static, ctx.in_lib) =
state.back();
state.pop_back();
} else if (read_arg(ctx, args, arg, "l")) {
} else if (remove_prefix(arg, "-l")) {
MappedFile<Context<E>> *mf = find_library(ctx, std::string(arg));
mf->given_fullpath = false;
read_file(ctx, mf);
} else {
read_file(ctx, MappedFile<Context<E>>::must_open(ctx, std::string(args[0])));
args = args.subspan(1);
read_file(ctx, MappedFile<Context<E>>::must_open(ctx, std::string(arg)));
}
}
@@ -239,178 +340,32 @@ static void read_input_files(Context<E> &ctx, std::span<std::string_view> args)
}
template <typename E>
static i64 get_mtime(Context<E> &ctx, std::string path) {
struct stat st;
if (stat(path.c_str(), &st) < 0)
Fatal(ctx) << path << ": stat failed: " << errno_string();
return st.st_mtime;
}
template <typename E>
static bool reload_input_files(Context<E> &ctx) {
Timer t(ctx, "reload_input_files");
std::vector<ObjectFile<E> *> objs;
std::vector<SharedFile<E> *> dsos;
// Reload updated .o files
for (ObjectFile<E> *file : ctx.objs) {
if (file->mf->parent) {
if (get_mtime(ctx, file->mf->parent->name) != file->mf->parent->mtime)
return false;
objs.push_back(file);
continue;
}
if (get_mtime(ctx, file->mf->name) == file->mf->mtime) {
objs.push_back(file);
continue;
}
MappedFile<Context<E>> *mf =
MappedFile<Context<E>>::must_open(ctx, file->mf->name);
objs.push_back(new_object_file(ctx, mf, file->mf->name));
}
// Reload updated .so files
for (SharedFile<E> *file : ctx.dsos) {
if (get_mtime(ctx, file->mf->name) == file->mf->mtime) {
dsos.push_back(file);
} else {
MappedFile<Context<E>> *mf =
MappedFile<Context<E>>::must_open(ctx, file->mf->name);
dsos.push_back(new_shared_file(ctx, mf));
}
}
ctx.objs = objs;
ctx.dsos = dsos;
return true;
}
template <typename E>
static void show_stats(Context<E> &ctx) {
for (ObjectFile<E> *obj : ctx.objs) {
static Counter defined("defined_syms");
defined += obj->first_global - 1;
static Counter undefined("undefined_syms");
undefined += obj->symbols.size() - obj->first_global;
for (std::unique_ptr<InputSection<E>> &sec : obj->sections) {
if (!sec || !sec->is_alive)
continue;
static Counter alloc("reloc_alloc");
static Counter nonalloc("reloc_nonalloc");
if (sec->shdr().sh_flags & SHF_ALLOC)
alloc += sec->get_rels(ctx).size();
else
nonalloc += sec->get_rels(ctx).size();
}
static Counter comdats("comdats");
comdats += obj->comdat_groups.size();
static Counter removed_comdats("removed_comdat_mem");
for (auto &pair : obj->comdat_groups)
if (ComdatGroup *group = pair.first; group->owner != obj->priority)
removed_comdats += pair.second.size();
static Counter num_cies("num_cies");
num_cies += obj->cies.size();
static Counter num_unique_cies("num_unique_cies");
for (CieRecord<E> &cie : obj->cies)
if (cie.is_leader)
num_unique_cies++;
static Counter num_fdes("num_fdes");
num_fdes += obj->fdes.size();
}
static Counter num_bytes("total_input_bytes");
for (std::unique_ptr<MappedFile<Context<E>>> &mf : ctx.mf_pool)
num_bytes += mf->size;
static Counter num_input_sections("input_sections");
for (ObjectFile<E> *file : ctx.objs)
num_input_sections += file->sections.size();
static Counter num_output_chunks("output_chunks", ctx.chunks.size());
static Counter num_objs("num_objs", ctx.objs.size());
static Counter num_dsos("num_dsos", ctx.dsos.size());
if constexpr (std::is_same_v<E, ARM64>) {
static Counter num_thunks("num_thunks");
for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections)
for (std::unique_ptr<RangeExtensionThunk<E>> &thunk : osec->thunks)
num_thunks += thunk->symbols.size();
}
Counter::print();
for (std::unique_ptr<MergedSection<E>> &sec : ctx.merged_sections)
sec->print_stats(ctx);
}
static i64 get_default_thread_count() {
// mold doesn't scale well above 32 threads.
int n = tbb::global_control::active_value(
tbb::global_control::max_allowed_parallelism);
return std::min(n, 32);
}
template <typename E>
static int elf_main(int argc, char **argv) {
int elf_main(int argc, char **argv) {
Context<E> ctx;
// Process -run option first. process_run_subcommand() does not return.
if (argc >= 2)
if (argv[1] == "-run"sv || argv[1] == "--run"sv)
process_run_subcommand(ctx, argc, argv);
if (argc >= 2 && (argv[1] == "-run"sv || argv[1] == "--run"sv)) {
#if defined(_WIN32) || defined(__APPLE__)
Fatal(ctx) << "-run is supported only on Unix";
#endif
process_run_subcommand(ctx, argc, argv);
}
// Parse non-positional command line options
ctx.cmdline_args = expand_response_files(ctx, argv);
std::vector<std::string_view> file_args;
parse_nonpositional_args(ctx, file_args);
std::vector<std::string> file_args = parse_nonpositional_args(ctx);
// If no -m option is given, deduce it from input files.
if (ctx.arg.emulation == -1)
if (ctx.arg.emulation.empty())
ctx.arg.emulation = deduce_machine_type(ctx, file_args);
// Redo if -m is not x86-64.
if (ctx.arg.emulation != E::e_machine) {
switch (ctx.arg.emulation) {
case EM_386:
return elf_main<I386>(argc, argv);
case EM_AARCH64:
return elf_main<ARM64>(argc, argv);
case EM_ARM:
return elf_main<ARM32>(argc, argv);
case EM_RISCV:
return elf_main<RISCV64>(argc, argv);
}
unreachable();
}
if constexpr (is_x86_64<E>)
if (ctx.arg.emulation != X86_64::target_name)
return redo_main(ctx, argc, argv);
Timer t_all(ctx, "all");
if (ctx.arg.relocatable) {
combine_objects(ctx, file_args);
return 0;
}
if (!ctx.arg.preload)
try_resume_daemon(ctx);
i64 thread_count = ctx.arg.thread_count;
if (thread_count == 0)
thread_count = get_default_thread_count();
tbb::global_control tbb_cont(tbb::global_control::max_allowed_parallelism,
thread_count);
install_signal_handler();
if (!ctx.arg.directory.empty())
@@ -418,40 +373,34 @@ static int elf_main(int argc, char **argv) {
Fatal(ctx) << "chdir failed: " << ctx.arg.directory
<< ": " << errno_string();
// Fork a subprocess unless --no-fork is given.
std::function<void()> on_complete;
#if !defined(_WIN32) && !defined(__APPLE__)
if (ctx.arg.fork)
on_complete = fork_child();
#endif
acquire_global_lock(ctx);
tbb::global_control tbb_cont(tbb::global_control::max_allowed_parallelism,
ctx.arg.thread_count);
// Handle --wrap options if any.
for (std::string_view name : ctx.arg.wrap)
get_symbol(ctx, name)->wrap = true;
get_symbol(ctx, name)->is_wrapped = true;
// Handle --retain-symbols-file options if any.
if (ctx.arg.retain_symbols_file)
for (std::string_view name : *ctx.arg.retain_symbols_file)
get_symbol(ctx, name)->write_to_symtab = true;
// Preload input files
std::function<void()> on_complete;
std::function<void()> wait_for_client;
if (ctx.arg.preload)
daemonize(ctx, &wait_for_client, &on_complete);
else if (ctx.arg.fork)
on_complete = fork_child();
for (std::string_view arg : ctx.arg.trace_symbol)
get_symbol(ctx, arg)->traced = true;
get_symbol(ctx, arg)->is_traced = true;
// Parse input files
read_input_files(ctx, file_args);
if (ctx.arg.preload) {
wait_for_client();
if (!reload_input_files(ctx)) {
std::vector<char *> args(argv, argv + argc);
args.push_back((char *)"--no-preload");
args.push_back(nullptr);
return elf_main<E>(argc + 1, args.data());
}
}
// Uniquify shared object files by soname
{
std::unordered_set<std::string_view> seen;
@@ -460,21 +409,40 @@ static int elf_main(int argc, char **argv) {
});
}
Timer t_total(ctx, "total");
Timer t_before_copy(ctx, "before_copy");
// Apply -exclude-libs
apply_exclude_libs(ctx);
// Resolve symbols and fix the set of object files that are
// included to the final output.
// Create a dummy file containing linker-synthesized symbols.
if (!ctx.arg.relocatable)
create_internal_file(ctx);
// resolve_symbols is 4 things in 1 phase:
//
// - Determine the set of object files to extract from archives.
// - Remove redundant COMDAT sections (e.g. duplicate inline functions).
// - Finally, the actual symbol resolution.
// - LTO, which requires preliminary symbol resolution before running
// and a follow-up re-resolution after the LTO objects are emitted.
//
// These passes have complex interactions, and unfortunately has to be
// put together in a single phase.
resolve_symbols(ctx);
// Resolve mergeable section pieces to merge them.
register_section_pieces(ctx);
// "Kill" .eh_frame input sections after symbol resolution.
kill_eh_frame_sections(ctx);
// Remove redundant comdat sections (e.g. duplicate inline functions).
eliminate_comdats(ctx);
// Resolve mergeable section pieces to merge them.
resolve_section_pieces(ctx);
// Handle --relocatable. Since the linker's behavior is quite different
// from the normal one when the option is given, the logic is implemented
// to a separate file.
if (ctx.arg.relocatable) {
combine_objects(ctx);
return 0;
}
// Create .bss sections for common symbols.
convert_common_symbols(ctx);
@@ -485,9 +453,13 @@ static int elf_main(int argc, char **argv) {
// Parse symbol version suffixes (e.g. "foo@ver1").
parse_symbol_version(ctx);
// Set is_import and is_export bits for each symbol.
// Set is_imported and is_exported bits for each symbol.
compute_import_export(ctx);
// Set "address-taken" bits for input sections.
if (ctx.arg.icf || ctx.arg.z_rewrite_endbr)
compute_address_significance(ctx);
// Garbage-collect unreachable sections.
if (ctx.arg.gc_sections)
gc_sections(ctx);
@@ -499,20 +471,24 @@ static int elf_main(int argc, char **argv) {
// Compute sizes of sections containing mergeable strings.
compute_merged_section_sizes(ctx);
// Create instances of linker-synthesized sections such as
// .got or .plt.
// Create linker-synthesized sections such as .got or .plt.
create_synthetic_sections(ctx);
// Make sure that there's no duplicate symbol
if (!ctx.arg.allow_multiple_definition)
check_duplicate_symbols(ctx);
// Warn if symbols with different types are defined under the same name.
check_symbol_types(ctx);
if constexpr (is_ppc64v1<E>)
ppc64v1_rewrite_opd(ctx);
// Bin input sections into output sections.
bin_sections(ctx);
create_output_sections(ctx);
// Get a list of output sections.
append(ctx.chunks, collect_output_sections(ctx));
// Create a dummy file containing linker-synthesized symbols
// (e.g. `__bss_start`).
ctx.internal_obj = create_internal_file(ctx);
ctx.objs.push_back(ctx.internal_obj);
// Add synthetic symbols such as __ehdr_start or __end.
add_synthetic_symbols(ctx);
// Beyond this point, no new files will be added to ctx.objs
// or ctx.dsos.
@@ -521,25 +497,31 @@ static int elf_main(int argc, char **argv) {
if (ctx.arg.z_cet_report != CET_REPORT_NONE)
check_cet_errors(ctx);
// Handle `-z execstack-if-needed`.
if (ctx.arg.z_execstack_if_needed)
for (ObjectFile<E> *file : ctx.objs)
if (file->needs_executable_stack)
ctx.arg.z_execstack = true;
// If we are linking a .so file, remaining undefined symbols does
// not cause a linker error. Instead, they are treated as if they
// were imported symbols.
//
// If we are linking an executable, weak undefs are converted to
// weakly imported symbol so that they'll have another chance to be
// weakly imported symbols so that they'll have another chance to be
// resolved.
claim_unresolved_symbols(ctx);
// Beyond this point, no new symbols will be added to the result.
// Handle --print-dependencies
if (ctx.arg.print_dependencies)
print_dependencies(ctx);
// Handle -repro
if (ctx.arg.repro)
write_repro_file(ctx);
// Make sure that all symbols have been resolved.
if (!ctx.arg.allow_multiple_definition)
check_duplicate_symbols(ctx);
// Handle --require-defined
for (std::string_view name : ctx.arg.require_defined)
if (!get_symbol(ctx, name)->file)
@@ -557,16 +539,6 @@ static int elf_main(int argc, char **argv) {
if (ctx.arg.shuffle_sections != SHUFFLE_SECTIONS_NONE)
shuffle_sections(ctx);
// Compute sizes of output sections while assigning offsets
// within an output section to input sections.
compute_section_sizes(ctx);
// Sort sections by section attributes so that we'll have to
// create as few segments as possible.
sort(ctx.chunks, [&](Chunk<E> *a, Chunk<E> *b) {
return get_section_rank(ctx, a) < get_section_rank(ctx, b);
});
// Copy string referred by .dynamic to .dynstr.
for (SharedFile<E> *file : ctx.dsos)
ctx.dynstr->add_string(file->soname);
@@ -579,9 +551,20 @@ static int elf_main(int argc, char **argv) {
if (!ctx.arg.soname.empty())
ctx.dynstr->add_string(ctx.arg.soname);
if constexpr (is_ppc64v1<E>)
ppc64v1_scan_symbols(ctx);
// Scan relocations to find symbols that need entries in .got, .plt,
// .got.plt, .dynsym, .dynstr, etc.
scan_rels(ctx);
scan_relocations(ctx);
// Compute sizes of output sections while assigning offsets
// within an output section to input sections.
compute_section_sizes(ctx);
// Sort sections by section attributes so that we'll have to
// create as few segments as possible.
sort_output_sections(ctx);
// If --packed_dyn_relocs=relr was given, base relocations are stored
// to a .relr.dyn section in a compressed form. Construct a compressed
@@ -594,6 +577,10 @@ static int elf_main(int argc, char **argv) {
// be added to .dynsym.
ctx.dynsym->finalize(ctx);
// Print reports about undefined symbols, if needed.
if (ctx.arg.unresolved_symbols == UNRESOLVED_ERROR)
report_undef_errors(ctx);
// Fill .gnu.version_d section contents.
if (ctx.verdef)
ctx.verdef->construct(ctx);
@@ -610,50 +597,27 @@ static int elf_main(int argc, char **argv) {
// Here, we construct output .eh_frame contents.
ctx.eh_frame->construct(ctx);
// Handle --gdb-index.
if (ctx.arg.gdb_index)
ctx.gdb_index->construct(ctx);
// If --emit-relocs is given, we'll copy relocation sections from input
// files to an output file.
if (ctx.arg.emit_relocs)
create_reloc_sections(ctx);
// Update sh_size for each chunk and remove empty ones.
for (Chunk<E> *chunk : ctx.chunks)
chunk->update_shdr(ctx);
std::erase_if(ctx.chunks, [](Chunk<E> *chunk) {
return !chunk->is_output_section() && chunk->shdr.sh_size == 0;
});
// Set section indices.
for (i64 i = 0, shndx = 1; i < ctx.chunks.size(); i++)
if (!ctx.chunks[i]->is_header())
ctx.chunks[i]->shndx = shndx++;
// Some types of section header refer other section by index.
// Recompute the section header to fill such fields with correct values.
for (Chunk<E> *chunk : ctx.chunks)
chunk->update_shdr(ctx);
// Compute the section header values for all sections.
compute_section_headers(ctx);
// Assign offsets to output sections
i64 filesize = set_osec_offsets(ctx);
// On ARM64, we may need to create so-called "range extension thunks"
// to extend branch instructions reach, as they can jump only to
// ±128 MiB.
if constexpr (std::is_same_v<E, ARM64>)
filesize = create_range_extension_thunks(ctx);
// On RISC-V, branches are encode using multiple instructions so
// that they can jump to anywhere in ±2 GiB by default. They may
// be replaced with shorter instruction sequences if destinations
// are close enough. Do this optimization.
if constexpr (std::is_same_v<E, RISCV64>)
if constexpr (is_riscv<E>)
filesize = riscv_resize_sections(ctx);
// Fix linker-synthesized symbol addresses.
// At this point, memory layout is fixed.
// Set actual addresses to linker-synthesized symbols.
fix_synthetic_symbols(ctx);
// Beyond this, you can assume that symbol addresses including their
@@ -664,41 +628,19 @@ static int elf_main(int argc, char **argv) {
if (ctx.arg.compress_debug_sections != COMPRESS_NONE)
filesize = compress_debug_sections(ctx);
// At this point, file layout is fixed.
// At this point, both memory and file layouts are fixed.
t_before_copy.stop();
// Create an output file
ctx.output_file = OutputFile<E>::open(ctx, ctx.arg.output, filesize, 0777);
ctx.output_file =
OutputFile<Context<E>>::open(ctx, ctx.arg.output, filesize, 0777);
ctx.buf = ctx.output_file->buf;
Timer t_copy(ctx, "copy");
// Copy input sections to the output file
{
Timer t(ctx, "copy_buf");
tbb::parallel_for_each(ctx.chunks, [&](Chunk<E> *chunk) {
std::string name =
chunk->name.empty() ? "(header)" : std::string(chunk->name);
Timer t2(ctx, name, &t);
chunk->copy_buf(ctx);
});
ctx.checkpoint();
}
if constexpr (std::is_same_v<E, ARM64>)
write_thunks(ctx);
if constexpr (std::is_same_v<E, ARM32>)
sort_arm_exidx(ctx);
// Some part of .gdb_index couldn't be computed until other debug
// sections are complete. We have complete debug sections now, so
// write the rest of .gdb_index.
if (ctx.gdb_index)
ctx.gdb_index->write_address_areas(ctx);
// Copy input sections to the output file and apply relocations.
copy_chunks(ctx);
// Dynamic linker works better with sorted .rela.dyn section,
// so we sort them.
@@ -707,12 +649,20 @@ static int elf_main(int argc, char **argv) {
// Zero-clear paddings between sections
clear_padding(ctx);
if (ctx.buildid) {
Timer t(ctx, "build_id");
// .gdb_index's contents cannot be constructed before applying
// relocations to other debug sections. We have relocated debug
// sections now, so write the .gdb_index section.
if (ctx.gdb_index)
write_gdb_index(ctx);
// .note.gnu.build-id section contains a cryptographic hash of the
// entire output file. Now that we wrote everything except build-id,
// we can compute it.
if (ctx.buildid)
ctx.buildid->write_buildid(ctx);
}
t_copy.stop();
ctx.checkpoint();
// Close the output file. This is the end of the linker's main job.
ctx.output_file->close(ctx);
@@ -721,16 +671,9 @@ static int elf_main(int argc, char **argv) {
if (!ctx.arg.dependency_file.empty())
write_dependency_file(ctx);
// Handle --print-dependencies
if (ctx.arg.print_dependencies == 1)
print_dependencies(ctx);
else if (ctx.arg.print_dependencies == 2)
print_dependencies_full(ctx);
if (ctx.has_lto_object)
lto_cleanup(ctx);
t_total.stop();
t_all.stop();
if (ctx.arg.print_map)
@@ -748,25 +691,25 @@ static int elf_main(int argc, char **argv) {
if (on_complete)
on_complete();
release_global_lock(ctx);
if (ctx.arg.quick_exit)
_exit(0);
for (std::function<void()> &fn : ctx.on_exit)
fn();
ctx.checkpoint();
return 0;
}
#ifdef MOLD_X86_64
int main(int argc, char **argv) {
return elf_main<X86_64>(argc, argv);
}
#endif
#define INSTANTIATE(E) \
template void read_file(Context<E> &, MappedFile<Context<E>> *);
using E = MOLD_TARGET;
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
template int elf_main<E>(int, char **);
} // namespace mold::elf
+6 -8
View File
@@ -40,6 +40,9 @@ static Map<E> get_map(Context<E> &ctx) {
}
});
if (map.size() <= 1)
return map;
tbb::parallel_for(map.range(), [](const typename Map<E>::range_type &range) {
for (auto it = range.begin(); it != range.end(); it++) {
std::vector<Symbol<E> *> &vec = it->second;
@@ -72,7 +75,7 @@ void print_map(Context<E> &ctx) {
<< std::setw(6) << (u64)osec->shdr.sh_addralign
<< " " << osec->name << "\n";
if (!osec->is_output_section())
if (osec->kind() != OUTPUT_SECTION)
continue;
std::span<InputSection<E> *> members = ((OutputSection<E> *)osec)->members;
@@ -106,13 +109,8 @@ void print_map(Context<E> &ctx) {
}
}
#define INSTANTIATE(E) \
template void print_map(Context<E> &ctx);
using E = MOLD_TARGET;
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
template void print_map(Context<E> &ctx);
} // namespace mold::elf
+34 -28
View File
@@ -1,5 +1,8 @@
#define _GNU_SOURCE 1
#if !defined(__OpenBSD__) && !defined(__FreeBSD__)
# include <alloca.h>
#endif
#include <dlfcn.h>
#include <spawn.h>
#include <stdarg.h>
@@ -19,7 +22,7 @@ static char *get_mold_path() {
exit(1);
}
static void debug_print(char *fmt, ...) {
static void debug_print(const char *fmt, ...) {
if (!getenv("MOLD_WRAPPER_DEBUG"))
return;
@@ -31,13 +34,25 @@ static void debug_print(char *fmt, ...) {
va_end(ap);
}
static void get_args(va_list *ap, int argc, char **argv) {
for (int i = 1; i < argc - 1; i++) {
char *arg = va_arg(*ap, char *);
if (!arg)
break;
argv[i] = arg;
}
static int count_args(va_list *ap) {
va_list aq;
va_copy(aq, *ap);
int i = 0;
while (va_arg(aq, char *))
i++;
va_end(aq);
return i;
}
static void copy_args(char **argv, const char *arg0, va_list *ap) {
int i = 1;
char *arg;
while ((arg = va_arg(*ap, char *)))
argv[i++] = arg;
((const char **)argv)[0] = arg0;
((const char **)argv)[i] = NULL;
}
static bool is_ld(const char *path) {
@@ -46,16 +61,15 @@ static bool is_ld(const char *path) {
ptr--;
return !strcmp(ptr, "ld") || !strcmp(ptr, "ld.lld") ||
!strcmp(ptr, "ld.gold") || !strcmp(ptr, "ld.bfd");
!strcmp(ptr, "ld.gold") || !strcmp(ptr, "ld.bfd") ||
!strcmp(ptr, "ld.mold");
}
int execvpe(const char *file, char *const *argv, char *const *envp) {
debug_print("execvpe %s\n", file);
if (!strcmp(file, "ld") || is_ld(file)) {
if (!strcmp(file, "ld") || is_ld(file))
file = get_mold_path();
((const char **)argv)[0] = file;
}
for (int i = 0; envp[i]; i++)
putenv(envp[i]);
@@ -66,12 +80,8 @@ int execvpe(const char *file, char *const *argv, char *const *envp) {
int execve(const char *path, char *const *argv, char *const *envp) {
debug_print("execve %s\n", path);
if (is_ld(path)) {
if (is_ld(path))
path = get_mold_path();
((const char **)argv)[0] = path;
}
typeof(execve) *real = dlsym(RTLD_NEXT, "execve");
return real(path, argv, envp);
}
@@ -79,8 +89,8 @@ int execve(const char *path, char *const *argv, char *const *envp) {
int execl(const char *path, const char *arg0, ...) {
va_list ap;
va_start(ap, arg0);
char *argv[4096] = {(char *)arg0};
get_args(&ap, 4096, argv);
char **argv = alloca((count_args(&ap) + 2) * sizeof(char *));
copy_args(argv, arg0, &ap);
va_end(ap);
return execve(path, argv, environ);
}
@@ -88,8 +98,8 @@ int execl(const char *path, const char *arg0, ...) {
int execlp(const char *file, const char *arg0, ...) {
va_list ap;
va_start(ap, arg0);
char *argv[4096] = {(char *)arg0};
get_args(&ap, 4096, argv);
char **argv = alloca((count_args(&ap) + 2) * sizeof(char *));
copy_args(argv, arg0, &ap);
va_end(ap);
return execvpe(file, argv, environ);
}
@@ -97,8 +107,8 @@ int execlp(const char *file, const char *arg0, ...) {
int execle(const char *path, const char *arg0, ...) {
va_list ap;
va_start(ap, arg0);
char *argv[4096] = {(char *)arg0};
get_args(&ap, 4096, argv);
char **argv = alloca((count_args(&ap) + 2) * sizeof(char *));
copy_args(argv, arg0, &ap);
char **env = va_arg(ap, char **);
va_end(ap);
return execve(path, argv, env);
@@ -117,12 +127,8 @@ int posix_spawn(pid_t *pid, const char *path,
const posix_spawnattr_t *attrp,
char *const *argv, char *const *envp) {
debug_print("posix_spawn %s\n", path);
if (is_ld(path)) {
if (is_ld(path))
path = get_mold_path();
((const char **)argv)[0] = path;
}
typeof(posix_spawn) *real = dlsym(RTLD_NEXT, "posix_spawn");
return real(pid, path, file_actions, attrp, argv, envp);
}
+1046 -859
View File
File diff suppressed because it is too large. Load diff
+1567 -1420
View File
File diff suppressed because it is too large. Load diff
-144
View File
@@ -1,144 +0,0 @@
#include "mold.h"
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
namespace mold::elf {
inline u32 get_umask() {
u32 orig_umask = umask(0);
umask(orig_umask);
return orig_umask;
}
template <typename C>
static std::pair<i64, char *>
open_or_create_file(C &ctx, std::string path, i64 filesize, i64 perm) {
std::string tmpl = filepath(path).parent_path() / ".mold-XXXXXX";
char *path2 = (char *)save_string(ctx, tmpl).data();
i64 fd = mkstemp(path2);
if (fd == -1)
Fatal(ctx) << "cannot open " << path2 << ": " << errno_string();
// Reuse an existing file if exists and writable.
if (rename(path.c_str(), path2) == 0) {
::close(fd);
fd = ::open(path2, O_RDWR | O_CREAT, perm);
if (fd != -1 && !ftruncate(fd, filesize) && !fchmod(fd, perm & ~get_umask()))
return {fd, path2};
unlink(path2);
fd = ::open(path2, O_RDWR | O_CREAT, perm);
if (fd == -1)
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();
return {fd, path2};
}
template <typename E>
class MemoryMappedOutputFile : public OutputFile<E> {
public:
MemoryMappedOutputFile(Context<E> &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<E>(path, filesize, true) {
i64 fd;
std::tie(fd, output_tmpfile) = open_or_create_file(ctx, path, filesize, perm);
this->buf = (u8 *)mmap(nullptr, filesize, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (this->buf == MAP_FAILED)
Fatal(ctx) << path << ": mmap failed: " << errno_string();
::close(fd);
}
void close(Context<E> &ctx) override {
Timer t(ctx, "close_file");
if (!this->is_unmapped)
munmap(this->buf, this->filesize);
if (rename(output_tmpfile, this->path.c_str()) == -1)
Fatal(ctx) << this->path << ": rename failed: " << errno_string();
output_tmpfile = nullptr;
}
};
template <typename E>
class MallocOutputFile : public OutputFile<E> {
public:
MallocOutputFile(Context<E> &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<E>(path, filesize, false), perm(perm) {
this->buf = (u8 *)mmap(NULL, filesize, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (this->buf == MAP_FAILED)
Fatal(ctx) << "mmap failed: " << errno_string();
}
void close(Context<E> &ctx) override {
Timer t(ctx, "close_file");
if (this->path == "-") {
fwrite(this->buf, this->filesize, 1, stdout);
fclose(stdout);
return;
}
i64 fd = ::open(this->path.c_str(), O_RDWR | O_CREAT, perm);
if (fd == -1)
Fatal(ctx) << "cannot open " << this->path << ": " << errno_string();
FILE *fp = fdopen(fd, "w");
fwrite(this->buf, this->filesize, 1, fp);
fclose(fp);
}
private:
i64 perm;
};
template <typename E>
std::unique_ptr<OutputFile<E>>
OutputFile<E>::open(Context<E> &ctx, std::string path, i64 filesize, i64 perm) {
Timer t(ctx, "open_file");
if (path.starts_with('/') && !ctx.arg.chroot.empty())
path = ctx.arg.chroot + "/" + path_clean(path);
bool is_special = false;
if (path == "-") {
is_special = true;
} else {
struct stat st;
if (stat(path.c_str(), &st) == 0 && (st.st_mode & S_IFMT) != S_IFREG)
is_special = true;
}
std::unique_ptr<OutputFile<E>> file;
if (is_special)
file = std::make_unique<MallocOutputFile<E>>(ctx, path, filesize, perm);
else
file = std::make_unique<MemoryMappedOutputFile<E>>(ctx, path, filesize, perm);
if (ctx.arg.filler != -1)
memset(file->buf, ctx.arg.filler, filesize);
return file;
}
#define INSTANTIATE(E) \
template class OutputFile<E>;
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+1677 -590
View File
File diff suppressed because it is too large. Load diff
+143 -540
View File
@@ -1,597 +1,200 @@
// This file implements the -r or ---relocatable option. If the option
// is given to a linker, the linker creates not an executable nor a
// shared library file but instead create another object file by
// combining input object files.
// This file implements -r or --relocatable. That option forces the linker
// to combine input object files into another single large object file.
// Since the behavior of the linker when the option is given is quite
// different from that of the normal execution mode, we separate code for
// the feature into this separate file.
//
// The behavior of a linker with -r is pretty different from the one
// without -r, and adding code for -r to the regular code path could
// severely complicate it. Therefore, we implement -r as an
// independent feature from others. That's why this file share only a
// small amount of code with other files.
// The --relocatable option isn't used very often. After all, if you want
// to combine object files into a single file, you could use `ar`.
// However, some programs use it in a creative manner which is hard to be
// substituted with static archives, so we need to support this option in
// the same way as GNU ld does. A notable example is GHC (Glasgow Haskell
// Compiler). GHC has its own dynamic linker which can load a .o file (as
// opposed to a .so) into memory. GHC's module is not a shared object file
// but a combined object file.
//
// Since the -r option is a minor feature, we don't aim for speed in
// this file. This is a "better than nothing" implementation of the -r
// option.
// There are many different ways to combine object files into a single file.
// The simplest approach would be to just copy all sections from input files
// to an output file as-is with a few exceptions for singleton sections such
// as the symbol table or the string table. That works, but that's not
// compatible with GNU ld.
//
// Here is the strategy as to how to combine multiple object files
// into one:
// To be compatible with GNU ld, we need to do the followings:
//
// - Regular sections containing opaque data (e.g. ".text" or
// ".data") are just copied as-is without merging.
// - Regular sections containing opaque data (e.g. ".text" or ".data")
// are just copied as-is. Two sections with the same name are merged.
//
// - .symtab, .strtab and .shstrtab are merged.
//
// - COMDAT groups are uniquified.
//
// - Relocation sections are copied one by one, but we need to fix
// symbol indices.
// - Relocations are copied, but we need to fix symbol indices.
#include "mold.h"
#include "../archive-file.h"
#include <unordered_map>
#include <unordered_set>
#include <tbb/parallel_for.h>
#include <tbb/parallel_for_each.h>
namespace mold::elf {
template <typename E> class RObjectFile;
// Create linker-synthesized sections
template <typename E>
class RChunk {
public:
RChunk() {
out_shdr.sh_addralign = 1;
}
static void r_create_synthetic_sections(Context<E> &ctx) {
auto push = [&](auto *x) {
ctx.chunks.push_back(x);
ctx.chunk_pool.emplace_back(x);
return x;
};
virtual ~RChunk() = default;
virtual void update_shdr(Context<E> &ctx) {}
virtual void write_to(Context<E> &ctx) = 0;
ctx.ehdr = push(new OutputEhdr<E>(0));
ctx.shdr = push(new OutputShdr<E>);
ctx.eh_frame = push(new EhFrameSection<E>);
ctx.eh_frame_reloc = push(new EhFrameRelocSection<E>);
ctx.strtab = push(new StrtabSection<E>);
ctx.symtab = push(new SymtabSection<E>);
ctx.shstrtab = push(new ShstrtabSection<E>);
ctx.note_property = push(new NotePropertySection<E>);
std::string_view name;
i64 shndx = 0;
ElfShdr<E> in_shdr = {};
ElfShdr<E> out_shdr = {};
};
template <typename E>
class RInputSection : public RChunk<E> {
public:
RInputSection(Context<E> &ctx, RObjectFile<E> &file, const ElfShdr<E> &shdr);
void update_shdr(Context<E> &ctx) override;
void write_to(Context<E> &ctx) override;
RObjectFile<E> &file;
};
template <typename E>
class RSymtabSection : public RChunk<E> {
public:
RSymtabSection() {
this->name = ".symtab";
this->out_shdr.sh_type = SHT_SYMTAB;
this->out_shdr.sh_entsize = sizeof(ElfSym<E>);
this->out_shdr.sh_addralign = E::word_size;
}
void add_local_symbol(Context<E> &ctx, RObjectFile<E> &file, i64 idx);
void add_global_symbol(Context<E> &ctx, RObjectFile<E> &file, i64 idx);
void update_shdr(Context<E> &ctx) override;
void write_to(Context<E> &ctx) override;
std::unordered_map<std::string_view, i64> sym_map;
std::vector<ElfSym<E>> syms{1};
};
template <typename E>
class RStrtabSection : public RChunk<E> {
public:
RStrtabSection(std::string_view name) {
this->name = name;
this->out_shdr.sh_type = SHT_STRTAB;
this->out_shdr.sh_size = 1;
}
i64 add_string(std::string_view str);
void write_to(Context<E> &ctx) override;
std::unordered_map<std::string_view, i64> strings;
};
template <typename E>
class ROutputEhdr : public RChunk<E> {
public:
ROutputEhdr() {
this->out_shdr.sh_size = sizeof(ElfEhdr<E>);
}
void write_to(Context<E> &ctx) override;
};
template <typename E>
class ROutputShdr : public RChunk<E> {
public:
ROutputShdr() {
this->out_shdr.sh_size = sizeof(ElfShdr<E>);
}
void update_shdr(Context<E> &ctx) override;
void write_to(Context<E> &ctx) override;
};
template <typename E>
class RObjectFile {
public:
RObjectFile(Context<E> &ctx, MappedFile<Context<E>> &mf, bool is_alive);
void remove_comdats(Context<E> &ctx,
std::unordered_set<std::string_view> &groups);
template <typename T>
std::span<T> get_data(Context<E> &ctx, const ElfShdr<E> &shdr);
MappedFile<Context<E>> &mf;
std::span<ElfShdr<E>> elf_sections;
std::vector<std::unique_ptr<RInputSection<E>>> sections;
std::span<const ElfSym<E>> syms;
std::vector<i64> symidx;
std::unordered_set<std::string_view> defined_syms;
std::unordered_set<std::string_view> undef_syms;
i64 symtab_shndx = 0;
i64 first_global = 0;
bool is_alive;
const char *strtab = nullptr;
const char *shstrtab = nullptr;
};
template <typename E>
void RSymtabSection<E>::add_local_symbol(Context<E> &ctx, RObjectFile<E> &file,
i64 idx) {
ElfSym<E> sym = file.syms[idx];
assert(sym.st_bind == STB_LOCAL);
if (!sym.is_undef() && !sym.is_abs() && !sym.is_common()) {
if (!file.sections[sym.st_shndx])
return;
sym.st_shndx = file.sections[sym.st_shndx]->shndx;
}
std::string_view name = file.strtab + sym.st_name;
sym.st_name = ctx.r_strtab->add_string(name);
file.symidx[idx] = syms.size();
syms.push_back(sym);
if constexpr (is_riscv<E>)
ctx.extra.riscv_attributes = push(new RiscvAttributesSection<E>);
}
// Create SHT_GROUP (i.e. comdat group) sections. We uniquify comdat
// sections by signature. We want to propagate input comdat groups as
// output comdat groups if they are still alive after uniquification.
template <typename E>
void RSymtabSection<E>::add_global_symbol(Context<E> &ctx, RObjectFile<E> &file,
i64 idx) {
ElfSym<E> sym = file.syms[idx];
assert(sym.st_bind != STB_LOCAL);
static void create_comdat_group_sections(Context<E> &ctx) {
Timer t(ctx, "create_comdat_group_sections");
std::string_view name = file.strtab + sym.st_name;
auto [it, inserted] = sym_map.insert({name, syms.size()});
std::vector<std::vector<Chunk<E> *>> vec{ctx.objs.size()};
if (inserted) {
if (!sym.is_undef() && !sym.is_abs() && !sym.is_common())
sym.st_shndx = file.sections[sym.st_shndx]->shndx;
sym.st_name = ctx.r_strtab->add_string(name);
file.symidx[idx] = syms.size();
syms.push_back(sym);
return;
}
tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
ObjectFile<E> &file = *ctx.objs[i];
file.symidx[idx] = it->second;
ElfSym<E> &existing = syms[it->second];
if (existing.is_undef() && !sym.is_undef()) {
if (!sym.is_abs() && !sym.is_common())
sym.st_shndx = file.sections[sym.st_shndx]->shndx;
sym.st_name = existing.st_name;
existing = sym;
}
}
template <typename E>
void RSymtabSection<E>::update_shdr(Context<E> &ctx) {
this->out_shdr.sh_size = syms.size() * sizeof(ElfSym<E>);
this->out_shdr.sh_link = ctx.r_strtab->shndx;
}
template <typename E>
void RSymtabSection<E>::write_to(Context<E> &ctx) {
ElfSym<E> *buf = (ElfSym<E> *)(ctx.buf + this->out_shdr.sh_offset);
for (i64 i = 1; i < syms.size(); i++)
buf[i] = syms[i];
}
template <typename E>
RInputSection<E>::RInputSection(Context<E> &ctx, RObjectFile<E> &file,
const ElfShdr<E> &shdr)
: file(file) {
this->name = file.shstrtab + shdr.sh_name;
this->in_shdr = shdr;
this->out_shdr = shdr;
}
template <typename E>
void RInputSection<E>::update_shdr(Context<E> &ctx) {
switch (this->in_shdr.sh_type) {
case SHT_GROUP:
this->out_shdr.sh_link = ctx.r_symtab->shndx;
this->out_shdr.sh_info = file.symidx[this->in_shdr.sh_info];
break;
case SHT_REL:
case SHT_RELA:
this->out_shdr.sh_link = ctx.r_symtab->shndx;
this->out_shdr.sh_info = file.sections[this->in_shdr.sh_info]->shndx;
break;
default:
if (this->in_shdr.sh_link) {
std::unique_ptr<RInputSection<E>> &sec =
file.sections[this->in_shdr.sh_info];
if (sec)
this->out_shdr.sh_link = sec->shndx;
else if (this->in_shdr.sh_link == file.symtab_shndx)
this->out_shdr.sh_link = ctx.r_symtab->shndx;
}
}
}
template <typename E>
void RInputSection<E>::write_to(Context<E> &ctx) {
if (this->in_shdr.sh_type == SHT_NOBITS)
return;
std::span<u8> contents = file.template get_data<u8>(ctx, this->in_shdr);
memcpy(ctx.buf + this->out_shdr.sh_offset, contents.data(), contents.size());
switch (this->in_shdr.sh_type) {
case SHT_GROUP: {
u32 *mem = (u32 *)(ctx.buf + this->out_shdr.sh_offset);
for (i64 i = 1; i < this->out_shdr.sh_size / sizeof(u32); i++)
mem[i] = file.sections[mem[i]]->shndx;
break;
}
case SHT_REL:
case SHT_RELA: {
ElfRel<E> *rel = (ElfRel<E> *)(ctx.buf + this->out_shdr.sh_offset);
i64 size = this->out_shdr.sh_size / sizeof(ElfRel<E>);
for (i64 i = 0; i < size; i++) {
const ElfSym<E> &sym = file.syms[rel[i].r_sym];
if (sym.is_undef() || sym.is_abs() || sym.is_common() ||
file.sections[sym.st_shndx])
rel[i].r_sym = file.symidx[rel[i].r_sym];
else
memset(rel + i, 0, sizeof(ElfRel<E>));
}
i64 i = 0;
i64 j = 0;
for (; j < size; j++)
if (rel[j].r_type)
rel[i++] = rel[j];
for (; i < size; i++)
memset(rel + i, 0, sizeof(ElfRel<E>));
break;
}
}
}
template <typename E>
i64 RStrtabSection<E>::add_string(std::string_view str) {
auto [it, inserted] = strings.insert({str, this->out_shdr.sh_size});
if (inserted)
this->out_shdr.sh_size += str.size() + 1;
return it->second;
}
template <typename E>
void RStrtabSection<E>::write_to(Context<E> &ctx) {
for (auto [str, offset] : strings)
memcpy(ctx.buf + this->out_shdr.sh_offset + offset, str.data(), str.size());
}
template <typename E>
void ROutputEhdr<E>::write_to(Context<E> &ctx) {
ElfEhdr<E> &hdr = *(ElfEhdr<E> *)(ctx.buf + this->out_shdr.sh_offset);
memcpy(&hdr.e_ident, "\177ELF", 4);
hdr.e_ident[EI_CLASS] = (E::word_size == 8) ? ELFCLASS64 : ELFCLASS32;
hdr.e_ident[EI_DATA] = E::is_le ? ELFDATA2LSB : ELFDATA2MSB;
hdr.e_ident[EI_VERSION] = EV_CURRENT;
hdr.e_type = ET_REL;
hdr.e_machine = E::e_machine;
hdr.e_version = EV_CURRENT;
hdr.e_shoff = ctx.r_shdr->out_shdr.sh_offset;
hdr.e_ehsize = sizeof(ElfEhdr<E>);
hdr.e_shentsize = sizeof(ElfShdr<E>);
hdr.e_shstrndx = ctx.r_shstrtab->shndx;
hdr.e_shnum = ctx.r_shdr->out_shdr.sh_size / sizeof(ElfShdr<E>);
hdr.e_shstrndx = ctx.r_shstrtab->shndx;
}
template <typename E>
void ROutputShdr<E>::update_shdr(Context<E> &ctx) {
for (RChunk<E> *chunk : ctx.r_chunks)
if (chunk->shndx)
this->out_shdr.sh_size += sizeof(ElfShdr<E>);
}
template <typename E>
void ROutputShdr<E>::write_to(Context<E> &ctx) {
ElfShdr<E> *hdr = (ElfShdr<E> *)(ctx.buf + this->out_shdr.sh_offset);
for (RChunk<E> *chunk : ctx.r_chunks)
if (chunk->shndx)
hdr[chunk->shndx] = chunk->out_shdr;
}
template <typename E>
RObjectFile<E>::RObjectFile(Context<E> &ctx, MappedFile<Context<E>> &mf,
bool is_alive)
: mf(mf), is_alive(is_alive) {
// Read ELF header and section header
ElfEhdr<E> &ehdr = *(ElfEhdr<E> *)mf.data;
ElfShdr<E> *sh_begin = (ElfShdr<E> *)(mf.data + ehdr.e_shoff);
i64 num_sections = (ehdr.e_shnum == 0) ? sh_begin->sh_size : ehdr.e_shnum;
elf_sections = {sh_begin, sh_begin + num_sections};
sections.resize(num_sections);
// Read .shstrtab
i64 shstrtab_idx = (ehdr.e_shstrndx == SHN_XINDEX)
? sh_begin->sh_link : ehdr.e_shstrndx;
shstrtab = (char *)(mf.data + elf_sections[shstrtab_idx].sh_offset);
// Read .symtab
for (i64 i = 1; i < elf_sections.size(); i++) {
ElfShdr<E> &shdr = elf_sections[i];
if (shdr.sh_type == SHT_SYMTAB) {
syms = get_data<const ElfSym<E>>(ctx, shdr);
strtab = (char *)(mf.data + elf_sections[shdr.sh_link].sh_offset);
symtab_shndx = i;
first_global = shdr.sh_info;
break;
}
}
symidx.resize(syms.size());
// Read sections
for (i64 i = 1; i < elf_sections.size(); i++) {
ElfShdr<E> &shdr = elf_sections[i];
switch (shdr.sh_type) {
case SHT_NULL:
case SHT_SYMTAB:
case SHT_STRTAB:
break;
default:
sections[i].reset(new RInputSection(ctx, *this, shdr));
}
}
// Read global symbols
for (i64 i = first_global; i < syms.size(); i++) {
std::string_view name = strtab + syms[i].st_name;
if (syms[i].is_defined())
defined_syms.insert(name);
else
undef_syms.insert(name);
}
}
// Remove duplicate comdat groups
template <typename E>
void RObjectFile<E>::remove_comdats(Context<E> &ctx,
std::unordered_set<std::string_view> &groups) {
for (i64 i = 1; i < sections.size(); i++) {
if (!sections[i])
continue;
ElfShdr<E> &shdr = sections[i]->in_shdr;
if (shdr.sh_type != SHT_GROUP)
continue;
// Get a comdat group signature and insert it into a set.
const ElfSym<E> &sym = syms[shdr.sh_info];
std::string_view signature = strtab + sym.st_name;
if (groups.insert(signature).second)
continue;
// If it is a duplicate, remove it and its members.
for (i64 j : this->template get_data<u32>(ctx, shdr).subspan(1))
sections[j] = nullptr;
sections[i] = nullptr;
}
}
template <typename E>
template <typename T>
std::span<T> RObjectFile<E>::get_data(Context<E> &ctx, const ElfShdr<E> &shdr) {
T *begin = (T *)(mf.data + shdr.sh_offset);
T *end = (T *)(mf.data + shdr.sh_offset + shdr.sh_size);
return {begin, end};
}
template <typename E>
static std::vector<std::unique_ptr<RObjectFile<E>>>
open_files(Context<E> &ctx, std::span<std::string_view> args) {
std::vector<std::unique_ptr<RObjectFile<E>>> files;
bool whole_archive = false;
while (!args.empty()) {
if (read_flag(args, "whole-archive")) {
whole_archive = true;
continue;
}
if (read_flag(args, "no-whole-archive")) {
whole_archive = false;
continue;
}
std::string_view arg;
if (read_arg(ctx, args, arg, "version-script") ||
read_arg(ctx, args, arg, "dynamic-list"))
continue;
MappedFile<Context<E>> *mf = nullptr;
if (read_arg(ctx, args, arg, "l")) {
mf = find_library(ctx, std::string(arg));
} else {
if (arg.starts_with('-'))
for (ComdatGroupRef<E> &ref : file.comdat_groups) {
if (ref.group->owner != file.priority)
continue;
arg = args[0];
args = args.subspan(1);
mf = MappedFile<Context<E>>::must_open(ctx, std::string(arg));
}
switch (get_file_type(mf)) {
case FileType::ELF_OBJ:
files.emplace_back(new RObjectFile<E>(ctx, *mf, true));
break;
case FileType::AR:
case FileType::THIN_AR:
for (MappedFile<Context<E>> *child : read_archive_members(ctx, mf))
if (get_file_type(child) == FileType::ELF_OBJ)
files.emplace_back(new RObjectFile<E>(ctx, *child, whole_archive));
break;
default:
break;
Symbol<E> *sym = file.symbols[file.elf_sections[ref.sect_idx].sh_info];
assert(sym);
std::vector<Chunk<E> *> members;
for (u32 j : ref.members) {
const ElfShdr<E> &shdr = file.elf_sections[j];
if (shdr.sh_type == (E::is_rela ? SHT_RELA : SHT_REL)) {
InputSection<E> &isec = *file.sections[shdr.sh_info];
members.push_back(isec.output_section->reloc_sec.get());
} else {
InputSection<E> &isec = *file.sections[j];
members.push_back(isec.output_section);
}
}
vec[i].push_back(new ComdatGroupSection<E>(*sym, std::move(members)));
}
});
for (std::vector<Chunk<E> *> &vec2 : vec) {
for (Chunk<E> *chunk : vec2) {
ctx.chunks.push_back(chunk);
ctx.chunk_pool.emplace_back(chunk);
}
}
return files;
}
// Unresolved undefined symbols in the -r mode are simply propagated to an
// output file as undefined symbols. This function guarantees that
// unresolved undefined symbols belongs to some input file.
template <typename E>
static i64 assign_offsets(Context<E> &ctx) {
i64 offset = 0;
for (RChunk<E> *chunk : ctx.r_chunks) {
offset = align_to(offset, chunk->out_shdr.sh_addralign);
chunk->out_shdr.sh_offset = offset;
offset += chunk->out_shdr.sh_size;
static void r_claim_unresolved_symbols(Context<E> &ctx) {
Timer t(ctx, "r_claim_unresolved_symbols");
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
if (!file->is_alive)
return;
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())
continue;
std::scoped_lock lock(sym.mu);
if (sym.file &&
(!sym.esym().is_undef() || sym.file->priority <= file->priority))
continue;
sym.file = file;
sym.origin = 0;
sym.value = 0;
sym.sym_idx = i;
}
});
}
// Set output section in-file offsets. Output section memory addresses
// are left as zero.
template <typename E>
static u64 r_set_osec_offsets(Context<E> &ctx) {
u64 offset = 0;
for (Chunk<E> *chunk : ctx.chunks) {
offset = align_to(offset, chunk->shdr.sh_addralign);
chunk->shdr.sh_offset = offset;
offset += chunk->shdr.sh_size;
}
return offset;
}
static bool contains(std::unordered_set<std::string_view> &a,
std::unordered_set<std::string_view> &b) {
for (std::string_view x : b)
if (a.contains(x))
return true;
return false;
}
template <typename E>
void combine_objects(Context<E> &ctx, std::span<std::string_view> file_args) {
// Read object files
std::vector<std::unique_ptr<RObjectFile<E>>> files = open_files(ctx, file_args);
void combine_objects(Context<E> &ctx) {
compute_merged_section_sizes(ctx);
// Identify needed objects
std::unordered_set<std::string_view> undef_syms;
auto add_syms = [&](RObjectFile<E> &file) {
undef_syms.insert(file.undef_syms.begin(), file.undef_syms.end());
for (std::string_view name : file.defined_syms)
undef_syms.erase(name);
file.is_alive = true;
};
create_output_sections(ctx);
for (std::unique_ptr<RObjectFile<E>> &file : files)
if (file->is_alive)
add_syms(*file);
r_create_synthetic_sections(ctx);
for (;;) {
bool added = false;
for (std::unique_ptr<RObjectFile<E>> &file : files) {
if (!file->is_alive && contains(undef_syms, file->defined_syms)) {
add_syms(*file);
added = true;
}
}
if (!added)
break;
}
r_claim_unresolved_symbols(ctx);
std::erase_if(files, [](std::unique_ptr<RObjectFile<E>> &file) {
return !file->is_alive;
});
compute_section_sizes(ctx);
// Remove duplicate comdat groups
std::unordered_set<std::string_view> comdat_groups;
for (std::unique_ptr<RObjectFile<E>> &file : files)
file->remove_comdats(ctx, comdat_groups);
sort_output_sections(ctx);
// Create headers and linker-synthesized sections
ROutputEhdr<E> ehdr;
ROutputShdr<E> shdr;
RSymtabSection<E> symtab;
RStrtabSection<E> shstrtab(".shstrtab");
RStrtabSection<E> strtab(".strtab");
create_output_symtab(ctx);
ctx.r_chunks.push_back(&ehdr);
ctx.r_chunks.push_back(&shstrtab);
ctx.r_chunks.push_back(&strtab);
ctx.eh_frame->construct(ctx);
ctx.r_ehdr = &ehdr;
ctx.r_shdr = &shdr;
ctx.r_shstrtab = &shstrtab;
ctx.r_strtab = &strtab;
ctx.r_symtab = &symtab;
create_reloc_sections(ctx);
// Add input sections to output sections
for (std::unique_ptr<RObjectFile<E>> &file : files)
for (std::unique_ptr<RInputSection<E>> &sec : file->sections)
if (sec)
ctx.r_chunks.push_back(sec.get());
create_comdat_group_sections(ctx);
ctx.r_chunks.push_back(&symtab);
ctx.r_chunks.push_back(&shdr);
compute_section_headers(ctx);
// Assign output section indices
i64 shndx = 1;
for (RChunk<E> *chunk : ctx.r_chunks)
if (chunk != &ehdr && chunk != &shdr)
chunk->shndx = shndx++;
i64 filesize = r_set_osec_offsets(ctx);
ctx.output_file =
OutputFile<Context<E>>::open(ctx, ctx.arg.output, filesize, 0666);
ctx.buf = ctx.output_file->buf;
// Add section names to .shstrtab
for (RChunk<E> *chunk : ctx.r_chunks)
if (chunk->shndx)
chunk->out_shdr.sh_name = shstrtab.add_string(chunk->name);
copy_chunks(ctx);
clear_padding(ctx);
ctx.output_file->close(ctx);
ctx.checkpoint();
// Copy symbols from input objects to an output object
for (std::unique_ptr<RObjectFile<E>> &file : files)
for (i64 i = 1; i < file->first_global; i++)
symtab.add_local_symbol(ctx, *file, i);
if (ctx.arg.print_map)
print_map(ctx);
symtab.out_shdr.sh_info = symtab.syms.size();
if (ctx.arg.stats)
show_stats(ctx);
for (std::unique_ptr<RObjectFile<E>> &file : files)
for (i64 i = file->first_global; i < file->syms.size(); i++)
symtab.add_global_symbol(ctx, *file, i);
if (ctx.arg.perf)
print_timer_records(ctx.timer_records);
// Finalize section header
for (RChunk<E> *chunk : ctx.r_chunks)
chunk->update_shdr(ctx);
// Open an output file
i64 filesize = assign_offsets(ctx);
std::unique_ptr<OutputFile<E>> out =
OutputFile<E>::open(ctx, ctx.arg.output, filesize, 0666);
memset(out->buf, 0, filesize);
ctx.buf = out->buf;
// Write to the output file
for (RChunk<E> *chunk : ctx.r_chunks)
chunk->write_to(ctx);
out->close(ctx);
if (ctx.arg.quick_exit)
_exit(0);
}
#define INSTANTIATE(E) \
template void combine_objects(Context<E> &, std::span<std::string_view>);
using E = MOLD_TARGET;
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
template void combine_objects(Context<E> &);
} // namespace mold::elf
+15 -214
View File
@@ -1,21 +1,19 @@
#if !defined(_WIN32) && !defined(__APPLE__)
#include "mold.h"
#include "../sha.h"
#include "config.h"
#include <filesystem>
#include <signal.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <unistd.h>
#define DAEMON_TIMEOUT 30
namespace mold::elf {
#ifdef MOLD_X86_64
// Exiting from a program with large memory usage is slow --
// it may take a few hundred milliseconds. To hide the latency,
// we fork a child and let it do the actual linking work.
@@ -55,216 +53,25 @@ std::function<void()> fork_child() {
return [=] {
char buf[] = {1};
int n = write(pipefd[1], buf, 1);
[[maybe_unused]] int n = write(pipefd[1], buf, 1);
assert(n == 1);
};
}
static std::string base64(u8 *data, u64 size) {
static const char chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_";
std::ostringstream out;
auto encode = [&](u32 x) {
out << chars[x & 0b111111]
<< chars[(x >> 6) & 0b111111]
<< chars[(x >> 12) & 0b111111]
<< chars[(x >> 18) & 0b111111];
};
i64 i = 0;
for (; i < size - 3; i += 3)
encode((data[i + 2] << 16) | (data[i + 1] << 8) | data[i]);
if (i == size - 1)
encode(data[i]);
else if (i == size - 2)
encode((data[i + 1] << 8) | data[i]);
return out.str();
}
static std::string compute_sha256(std::span<std::string_view> argv) {
SHA256_CTX sha;
SHA256_Init(&sha);
for (std::string_view arg : argv) {
if (arg != "-preload" && arg != "--preload") {
SHA256_Update(&sha, arg.data(), arg.size());
char buf[] = {0};
SHA256_Update(&sha, buf, 1);
}
}
u8 digest[SHA256_SIZE];
SHA256_Final(digest, &sha);
return base64(digest, SHA256_SIZE);
}
#endif
template <typename E>
static void send_fd(Context<E> &ctx, i64 conn, i64 fd) {
struct iovec iov;
char dummy = '1';
iov.iov_base = &dummy;
iov.iov_len = 1;
struct msghdr msg = {};
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
char buf[CMSG_SPACE(sizeof(int))];
msg.msg_control = buf;
msg.msg_controllen = CMSG_LEN(sizeof(int));
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
*(int *)CMSG_DATA(cmsg) = fd;
if (sendmsg(conn, &msg, 0) == -1)
Fatal(ctx) << "sendmsg failed: " << errno_string();
}
template <typename E>
static i64 recv_fd(Context<E> &ctx, i64 conn) {
struct iovec iov;
char buf[1];
iov.iov_base = buf;
iov.iov_len = sizeof(buf);
struct msghdr msg = {};
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
char cmsgbuf[CMSG_SPACE(sizeof(int))];
msg.msg_control = (caddr_t)cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
i64 len = recvmsg(conn, &msg, 0);
if (len <= 0)
Fatal(ctx) << "recvmsg failed: " << errno_string();
struct cmsghdr *cmsg;
cmsg = CMSG_FIRSTHDR(&msg);
return *(int *)CMSG_DATA(cmsg);
}
template <typename E>
void try_resume_daemon(Context<E> &ctx) {
i64 conn = socket(AF_UNIX, SOCK_STREAM, 0);
if (conn == -1)
Fatal(ctx) << "socket failed: " << errno_string();
std::string path = "/tmp/mold-" + compute_sha256(ctx.cmdline_args);
struct sockaddr_un name = {};
name.sun_family = AF_UNIX;
memcpy(name.sun_path, path.data(), path.size());
if (connect(conn, (struct sockaddr *)&name, sizeof(name)) != 0) {
close(conn);
return;
}
send_fd(ctx, conn, STDOUT_FILENO);
send_fd(ctx, conn, STDERR_FILENO);
char buf[1];
i64 r = read(conn, buf, 1);
close(conn);
if (r == 1)
exit(0);
}
template <typename E>
void daemonize(Context<E> &ctx, std::function<void()> *wait_for_client,
std::function<void()> *on_complete) {
if (daemon(1, 0) == -1)
Fatal(ctx) << "daemon failed: " << errno_string();
i64 sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock == -1)
Fatal(ctx) << "socket failed: " << errno_string();
socket_tmpfile =
strdup(("/tmp/mold-" + compute_sha256(ctx.cmdline_args)).c_str());
struct sockaddr_un name = {};
name.sun_family = AF_UNIX;
strcpy(name.sun_path, socket_tmpfile);
u32 orig_mask = umask(0177);
if (bind(sock, (struct sockaddr *)&name, sizeof(name)) == -1) {
if (errno != EADDRINUSE)
Fatal(ctx) << "bind failed: " << errno_string();
unlink(socket_tmpfile);
if (bind(sock, (struct sockaddr *)&name, sizeof(name)) == -1)
Fatal(ctx) << "bind failed: " << errno_string();
}
umask(orig_mask);
if (listen(sock, 0) == -1)
Fatal(ctx) << "listen failed: " << errno_string();
static i64 conn = -1;
*wait_for_client = [=, &ctx] {
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(sock, &rfds);
struct timeval tv;
tv.tv_sec = DAEMON_TIMEOUT;
tv.tv_usec = 0;
i64 res = select(sock + 1, &rfds, NULL, NULL, &tv);
if (res == -1)
Fatal(ctx) << "select failed: " << errno_string();
if (res == 0) {
std::cout << "timeout\n";
exit(0);
}
conn = accept(sock, NULL, NULL);
if (conn == -1)
Fatal(ctx) << "accept failed: " << errno_string();
unlink(socket_tmpfile);
dup2(recv_fd(ctx, conn), STDOUT_FILENO);
dup2(recv_fd(ctx, conn), STDERR_FILENO);
};
*on_complete = [=] {
char buf[] = {1};
int n = write(conn, buf, 1);
assert(n == 1);
};
}
static std::string get_self_path() {
return std::filesystem::read_symlink("/proc/self/exe");
}
template <typename E>
std::string find_dso(Context<E> &ctx, std::filesystem::path self) {
static std::string find_dso(Context<E> &ctx, std::filesystem::path self) {
// Look for mold-wrapper.so from the same directory as the executable is.
std::filesystem::path path = self.parent_path() / "mold-wrapper.so";
std::error_code ec;
if (std::filesystem::is_regular_file(path, ec) && !ec)
return path;
#ifdef LIBDIR
// If not found, search $(LIBDIR)/mold, which is /usr/local/lib/mold
// If not found, search $(MOLD_LIBDIR)/mold, which is /usr/local/lib/mold
// by default.
path = LIBDIR "/mold/mold-wrapper.so";
path = MOLD_LIBDIR "/mold/mold-wrapper.so";
if (std::filesystem::is_regular_file(path, ec) && !ec)
return path;
#endif
// Look for ../lib/mold/mold-wrapper.so
path = self.parent_path() / "../lib/mold/mold-wrapper.so";
@@ -277,8 +84,8 @@ std::string find_dso(Context<E> &ctx, std::filesystem::path self) {
template <typename E>
[[noreturn]]
void process_run_subcommand(Context<E> &ctx, int argc, char **argv) {
std::string_view arg1 = argv[1];
assert(arg1 == "-run" || arg1 == "--run");
assert(argv[1] == "-run"s || argv[1] == "--run"s);
if (!argv[2])
Fatal(ctx) << "-run: argument missing";
@@ -306,16 +113,10 @@ void process_run_subcommand(Context<E> &ctx, int argc, char **argv) {
Fatal(ctx) << "mold -run failed: " << argv[2] << ": " << errno_string();
}
#define INSTANTIATE(E) \
template void try_resume_daemon(Context<E> &); \
template void daemonize(Context<E> &, std::function<void()> *, \
std::function<void()> *); \
template void process_run_subcommand(Context<E> &, int, char **)
using E = MOLD_TARGET;
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
template void process_run_subcommand(Context<E> &, int, char **);
} // namespace mold::elf
#endif
+305
View File
@@ -0,0 +1,305 @@
// RISC instructions are usually up to 4 bytes long, so the immediates of
// their branch instructions are naturally smaller than 32 bits. This is
// contrary to x86-64 on which branch instructions take 4 bytes immediates
// and can jump to anywhere within PC ± 2 GiB.
//
// In fact, ARM32's branch instructions can jump only within ±16 MiB and
// ARM64's ±128 MiB, for example. If a branch target is further than that,
// we need to let it branch to a linker-synthesized code sequence that
// construct a full 32 bit address in a register and jump there. That
// linker-synthesized code is called "thunk".
//
// The function in this file creates thunks.
//
// Note that although thunks play an important role in an executable, they
// don't take up too much space in it. For example, among the clang-16's
// text segment whose size is ~300 MiB on ARM64, thunks in total occupy
// only ~30 KiB or 0.01%. Of course the number depends on an ISA; we would
// need more thunks on ARM32 whose branch range is shorter than ARM64.
// That said, the total size of thunks still isn't that much. Therefore,
// we don't need to try too hard to reduce thunk size to the absolute
// minimum.
#if MOLD_ARM32 || MOLD_ARM64 || MOLD_PPC32 || MOLD_PPC64V1 || MOLD_PPC64V2 || \
MOLD_LOONGARCH64 || MOLD_LOONGARCH32
#include "mold.h"
#include <tbb/parallel_for.h>
#include <tbb/parallel_for_each.h>
namespace mold::elf {
using E = MOLD_TARGET;
// Returns a branch reach in bytes for a given target.
static consteval i64 max_distance() {
// ARM64's branch has 26 bits immediate. The immediate is padded with
// implicit two-bit zeros because all instructions are 4 bytes aligned
// and therefore the least two bits are always zero. So the branch
// operand is effectively 28 bits long. That means the branch range is
// [-2^27, 2^27) or PC ± 128 MiB.
//
// LoongArch's BR instruction also takes a 26 bit immediate.
if (is_arm64<E> || is_loongarch<E>)
return 1 << 27;
// ARM32's Thumb branch has 24 bits immediate, and the instructions are
// aligned to 2, so it's effectively 25 bits. It's [-2^24, 2^24) or PC ±
// 16 MiB.
//
// ARM32's non-Thumb branches have twice longer range than its Thumb
// counterparts, but we conservatively use the Thumb's limitation.
if (is_arm32<E>)
return 1 << 24;
// PPC's branch has 24 bits immediate, and the instructions are aligned
// to 4, therefore the reach is [-2^25, 2^25) or PC ± 32 MiB.
assert(is_ppc<E>);
return 1 << 25;
}
// We create thunks for each 12.8/1.6/3.2 MiB code block for
// ARM64/ARM32/PPC, respectively.
static constexpr i64 batch_size = max_distance() / 10;
// We assume that a single thunk group is smaller than 100 KiB.
static constexpr i64 max_thunk_size = 102400;
static_assert(max_thunk_size / E::thunk_size < INT16_MAX);
template <typename E>
static bool is_reachable(Context<E> &ctx, InputSection<E> &isec,
Symbol<E> &sym, const ElfRel<E> &rel) {
// We create thunks with a pessimistic assumption that all
// out-of-section relocations would be out-of-range.
InputSection<E> *isec2 = sym.get_input_section();
if (!isec2 || isec.output_section != isec2->output_section)
return false;
// Even if the target is the same section, we branch to its PLT
// if it has one. So a symbol with a PLT is also considered an
// out-of-section reference.
if (sym.has_plt(ctx))
return false;
// If the target section is in the same output section but
// hasn't got any address yet, that's unreacahble.
if (isec2->offset == -1)
return false;
// Thumb and ARM B instructions cannot be converted to BX, so we
// always have to make them jump to a thunk to switch processor mode
// even if their destinations are within their ranges.
if constexpr (is_arm32<E>) {
bool is_thumb = sym.get_addr(ctx) & 1;
if ((rel.r_type == R_ARM_THM_JUMP24 && !is_thumb) ||
(rel.r_type == R_ARM_JUMP24 && is_thumb) ||
(rel.r_type == R_ARM_PLT32 && is_thumb))
return false;
}
// PowerPC before Power9 lacks PC-relative load/store instructions.
// Functions compiled for Power9 or earlier assume that r2 points to
// GOT+0x8000, while those for Power10 uses r2 as a scratch register.
// We need to a thunk to recompute r2 for interworking.
if constexpr (is_ppc64v2<E>) {
if (rel.r_type == R_PPC64_REL24 && !sym.esym().preserves_r2())
return false;
if (rel.r_type == R_PPC64_REL24_NOTOC && sym.esym().uses_toc())
return false;
}
// Compute a distance between the relocated place and the symbol
// and check if they are within reach.
i64 S = sym.get_addr(ctx, NO_OPD);
i64 A = get_addend(isec, rel);
i64 P = isec.get_addr() + rel.r_offset;
i64 val = S + A - P;
return -max_distance() <= val && val < max_distance();
}
static void reset_thunk(RangeExtensionThunk<E> &thunk) {
for (Symbol<E> *sym : thunk.symbols) {
sym->extra.thunk_idx = -1;
sym->extra.thunk_sym_idx = -1;
sym->flags = 0;
}
}
// Scan relocations to collect symbols that need thunks.
static void scan_rels(Context<E> &ctx, InputSection<E> &isec,
RangeExtensionThunk<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());
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (!is_func_call_rel(rel))
continue;
// Skip if the symbol is undefined. apply_reloc() will report an error.
Symbol<E> &sym = *isec.file.symbols[rel.r_sym];
if (!sym.file)
continue;
// Skip if the destination is within reach.
if (is_reachable(ctx, isec, sym, rel))
continue;
// This relocation needs a thunk. If the symbol is already in a
// previous thunk, reuse it.
if (sym.extra.thunk_idx != -1) {
range_extn[i].thunk_idx = sym.extra.thunk_idx;
range_extn[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;
if (sym.flags.exchange(-1) == 0) {
std::scoped_lock lock(thunk.mu);
thunk.symbols.push_back(&sym);
}
}
}
template <>
void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
std::span<InputSection<E> *> m = members;
if (m.empty())
return;
m[0]->offset = 0;
// 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((i64)1, (i64)m.size(), [&](i64 i) {
m[i]->offset = -1;
});
// We create thunks from the beginning of the section to the end.
// We manage progress using four offsets which increase monotonically.
// The locations they point to are always A <= B <= C <= D.
//
// Input sections between B and C are in the current batch.
//
// A is the input section with the smallest address than can reach
// anywhere from the current batch.
//
// D is the input section with the largest address such that the thunk
// is reachable from the current batch if it's inserted right before D.
//
// ................................ <input sections> ............
// A B C D
// ^ We insert a thunk for the current batch just before D
// <---> The current batch, which is smaller than batch_size
// <--------> Smaller than max_distance
// <--------> Smaller than max_distance
// <-------------> Reachable from the current batch
i64 a = 0;
i64 b = 0;
i64 c = 0;
i64 d = 0;
i64 offset = 0;
// The smallest thunk index that is reachable from the current batch.
i64 t = 0;
while (b < m.size()) {
// Move C forward so that C is apart from B by BATCH_SIZE. We want
// to make sure that there's at least one section between B and C
// to ensure progress.
c = b + 1;
while (c < m.size() &&
m[c]->offset + m[c]->sh_size < m[b]->offset + batch_size)
c++;
// Move D foward as far as we can jump from B to anywhere in a thunk at D.
d = c;
while (d < m.size() &&
align_to(offset, 1 << m[d]->p2align) + m[d]->sh_size + max_thunk_size <
m[b]->offset + max_distance())
d++;
// Move A forward so that A is reachable from C.
i64 c_offset = (c == m.size()) ? offset : m[c]->offset;
while (a < b && a < m.size() && m[a]->offset + max_distance() < c_offset)
a++;
// Assign offsets to all sections before D.
for (i64 i = b; i < d; i++) {
offset = align_to(offset, 1 << m[i]->p2align);
m[i]->offset = offset;
offset += m[i]->sh_size;
}
// Erase references to out-of-range thunks.
while (t < thunks.size() && thunks[t]->offset < m[a]->offset)
reset_thunk(*thunks[t++]);
// Create a new thunk and place it at D.
offset = align_to(offset, RangeExtensionThunk<E>::alignment);
i64 thunk_idx = thunks.size();
RangeExtensionThunk<E> *thunk = new RangeExtensionThunk<E>(*this, offset);
thunks.emplace_back(thunk);
// Scan relocations between B and C to collect symbols that need
// entries in the new thunk.
tbb::parallel_for_each(m.begin() + b, m.begin() + c,
[&](InputSection<E> *isec) {
scan_rels(ctx, *isec, *thunk, thunk_idx);
});
// Now that we know the number of symbols in the thunk, we can compute
// the thunk's size.
assert(thunk->size() < max_thunk_size);
offset += thunk->size();
// Sort symbols added to the thunk to make the output deterministic.
sort(thunk->symbols, [](Symbol<E> *a, Symbol<E> *b) {
return std::tuple{a->file->priority, a->sym_idx} <
std::tuple{b->file->priority, b->sym_idx};
});
// Assign offsets within the thunk to the symbols.
for (i64 i = 0; i < thunk->symbols.size(); i++) {
Symbol<E> &sym = *thunk->symbols[i];
sym.extra.thunk_idx = thunk_idx;
sym.extra.thunk_sym_idx = i;
}
// Scan relocations again to fix symbol offsets in the last thunk.
tbb::parallel_for_each(m.begin() + b, m.begin() + c,
[&](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;
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;
});
// Move B forward to point to the begining of the next batch.
b = c;
}
while (t < thunks.size())
reset_thunk(*thunks[t++]);
this->shdr.sh_size = offset;
for (InputSection<E> *isec : members)
this->shdr.sh_addralign =
std::max<u32>(this->shdr.sh_addralign, 1 << isec->p2align);
}
} // namespace mold::elf
#endif
+213
View File
@@ -0,0 +1,213 @@
// This file contains helper functions for thread-local storage (TLS).
// TLS is probably the most obscure feature the linker has to support,
// so I'll explain it in detail in this comment.
//
// TLS is a per-thread storage. Thread-local variables (TLVs) are in a TLS
// so that each thread has its own set of thread-local variables. Taking
// an address of a TLV returns a unique value for each thread. For example,
// `&foo` for the following code returns different pointer values for
// different threads.
//
// thread_local int foo;
//
// TLV is a relatively new feature. C for example didn't provide the
// official support for it through the keyword `thread_local` until C11.
// TLV needs a coordination between the compiler, the linker and the
// runtime to work correctly.
//
// An ELF exectuable or a shared library using TLV contains a "TLS template
// image" in the PT_TLS segment. For each newly created thread including the
// initial one, the runtime allocates a contiguous memory for an executable
// and its depending shared libraries and copies template images there. That
// per-thread memory is called the "initial TLS block". After allocating and
// initializing the initial TLS block, the runtime sets a register to refer
// to the initial TLS block, so that the thread-local variables are
// accessible relative to the register.
//
// The register referring to the per-thread storage is called the Thread
// Pointer (TP). TP is part of the thread's context. When the kernel
// scheduler switches threads, TP is saved and restored automatically just
// like other registers are.
//
// The TLS template image is read-only. It contains TLVs' initial values
// for new threads, and no one writes to it at runtime.
//
// Now, let's think about how to access a TLV. We need to know the TLV's
// address to access it which can be done in several different ways as
// follows:
//
// 1. If we are creating an executable, we know the exact size of the TLS
// template image we are creating, and we know where the TP will be set
// to after the template is copied to the initial TLS block. Therefore,
// the TP-relative address of a TLV in the main executable is known at
// link-time. That means, computing a TLV's address can be as easy as
// `add %dst, %tp, <link-time constant>`.
//
// 2. If we are creating a shared library, we don't exactly know where
// its TLS template image will be copied to in terms of the
// TP-relative address, because we don't know how large the main
// executable's and other libraries' TLS template images are. Only the
// runtime knows the exact TP-relative address.
//
// We can solve the problem with an indirection. Specifically, for
// each TLV whose TP-relative address is known only at process startup
// time, we create a GOT entry to store its TP-relative address. We
// then emit a dynamic relocation to let the runtime to fill the GOT
// entry with a TP-relative address.
//
// Computing a TLV address in this scheme needs at least two machine
// instructions in most ISAs; the first instruction loads a value from
// the GOT entry, and the second one adds the loaded value to TP.
//
// 3. Now, think about libraries that are dynamically loaded with dlopen.
// The TLS block for such library may not be allocated next to the
// initial TLS block, so we can have two or more discontiguous TLS
// blocks. There's no easy formula to compute an address of a TLV in a
// separate TLS block.
//
// The address of a TLV in a separate TLS block can be obtained by
// calling the libc-provided function, __tls_get_addr(). The function
// takes two arguments; a module ID to identify the ELF file and the
// TLV's offset within the ELF file's TLS template image. Accessing a
// TLV is sometimes compiled to a function call! The module ID and the
// offset are usually stored to GOT as two consecutive words.
//
// The last access method is the most generic, so the compiler emits such
// code by default. But that's the most expensive one, so the linker
// rewrites instructions if possible so that 3) is relaxed to 2) or even
// to 1).
//
// 1) is called the Local Exec access model. 2) is Initial Exec, and 3) is
// General Dynamic.
//
// There's another little trick that the compiler can use if it knows two
// TLVs are in the same ELF file (usually in the same file as the code is).
// In this case, we can call __tls_get_addr() only once with a module ID and
// the offset 0 to obtain the base address of the ELF file's TLS block. The
// base address obtained this way is sometimes called Dynamic Thread Pointer
// or DTP. We can then compute TLVs' addresses by adding their DTP-relative
// addresses to DTP. This access model is called the Local Dynamic.
//
// === TLS Descriptor access model ===
//
// As described above, there are arguably too many different TLS access
// models from the most generic one you can use in any ELF file to the most
// efficient one you can use only when building a main executable. Compiling
// source code with an appropriate TLS access model is bothersome. To solve
// the problem, a new TLS access model was proposed. That is called the TLS
// Descriptor (TLSDESC) model.
//
// For a TLV compiled with TLSDESC, we allocate two consecutive GOT slots
// and create a TLSDESC dynamic relocation for them. The dynamic linker
// sets a function pointer to the first GOT slot and its argument to the
// second slot.
//
// To access the TLV, we call the function pointer with the argument we
// read from the second GOT slot. The function returns the TLV's
// TP-relative address.
//
// The runtime chooses the best access method depending on the situation
// and sets a pointer to the most efficient code to the first GOT slot.
// For example, if a TLV's TP-relative address is known at process startup
// time, the runtime sets that address to the second GOT slot and set a
// function that just returns its argument to the first GOT slot.
//
// With TLSDECS, the compiler can always emit the same code for TLVs
// without sacrificing runtime performance.
//
// TLSDESC is better than the traditional, non-TLSDESC TLS access models.
// It's the default on ARM64, but on other targets, TLSDESC is
// unfortunately either optional or even not supported at all. So we still
// need to support both the traditional TLS models and the TLSDESC model.
#include "mold.h"
namespace mold::elf {
template <typename E>
static ElfPhdr<E> *get_tls_segment(Context<E> &ctx) {
if (ctx.phdr)
for (ElfPhdr<E> &phdr : ctx.phdr->phdrs)
if (phdr.p_type == PT_TLS)
return &phdr;
return nullptr;
}
template <typename E>
u64 get_tls_begin(Context<E> &ctx) {
if (ElfPhdr<E> *phdr = get_tls_segment(ctx))
return phdr->p_vaddr;
return 0;
}
// Returns the TP address which can be used for efficient TLV accesses in
// the main executable. TP at runtime refers to a per-process TLS block
// whose address is not known at link-time. So the address returned from
// this function is the TP if the TLS template image were a TLS block.
template <typename E>
u64 get_tp_addr(Context<E> &ctx) {
ElfPhdr<E> *phdr = get_tls_segment(ctx);
if (!phdr)
return 0;
if constexpr (is_x86<E> || is_sparc<E> || is_s390x<E>) {
// On x86, SPARC and s390x, TP (%gs on i386, %fs on x86-64, %g7 on SPARC
// and %a0/%a1 on s390x) refers to past the end of the TLS block for
// historical reasons. TLVs are accessed with negative offsets from TP.
return align_to(phdr->p_vaddr + phdr->p_memsz, phdr->p_align);
} else if constexpr (is_arm<E> || is_sh4<E> || is_alpha<E>) {
// On ARM, SH4 and Alpha, the runtime appends two words at the beginning
// of TLV template image when copying TLVs to the TLS block, so we need
// to offset it.
return align_down(phdr->p_vaddr - sizeof(Word<E>) * 2, phdr->p_align);
} else if constexpr (is_ppc<E> || is_m68k<E>) {
// On PowerPC and m68k, TP is 0x7000 (28 KiB) past the beginning
// of the TLV block to maximize the addressable range of load/store
// instructions with 16-bits signed immediates. It's not exactly 0x8000
// (32 KiB) off because there's a small implementation-defined piece of
// data before the initial TLV block, and the runtime wants to access
// them efficiently too.
return phdr->p_vaddr + 0x7000;
} else {
// RISC-V and LoongArch just uses the beginning of the main executable's
// TLV block as TP. Their load/store instructions usually take 12-bits
// signed immediates, so the beginning of the TLS block ± 2 KiB is
// accessible with a single load/store instruction.
static_assert(is_riscv<E> || is_loongarch<E>);
return phdr->p_vaddr;
}
}
// Returns the address __tls_get_addr() would return if it's called
// with offset 0.
template <typename E>
u64 get_dtp_addr(Context<E> &ctx) {
ElfPhdr<E> *phdr = get_tls_segment(ctx);
if (!phdr)
return 0;
if constexpr (is_ppc<E> || is_m68k<E>) {
// On PowerPC and m68k, R_DTPOFF is resolved to the address 0x8000
// (32 KiB) past the start of the TLS block. The bias maximizes the
// accessible range of load/store instructions with 16-bits signed
// immediates. That is, if the offset were right at the beginning of the
// start of the TLS block, the half of addressible space (negative
// immediates) would have been wasted.
return phdr->p_vaddr + 0x8000;
} else if constexpr (is_riscv<E>) {
// On RISC-V, the bias is 0x800 as the load/store instructions in the
// ISA usually have a 12-bit immediate.
return phdr->p_vaddr + 0x800;
} else {
// On other targets, DTP simply refers to the beginning of the TLS block.
return phdr->p_vaddr;
}
}
using E = MOLD_TARGET;
template u64 get_tls_begin<E>(Context<E> &);
template u64 get_tp_addr<E>(Context<E> &);
template u64 get_dtp_addr<E>(Context<E> &);
} // namespace mold::elf
+55
View File
@@ -0,0 +1,55 @@
#!/bin/sh
set -e
. /etc/os-release
set -x
# The first line for each distro installs a build dependency.
# The second line installs extra packages for unittests.
#
# Feel free to send me a PR if your OS is not on this list.
case "$ID-$VERSION_ID" in
ubuntu-20.* | pop-20.*)
apt-get update
apt-get install -y cmake gcc g++ g++-10
apt-get install -y file
;;
ubuntu-* | pop-* | linuxmint-* | debian-* | raspbian-*)
apt-get update
apt-get install -y cmake gcc g++
apt-get install -y file
;;
fedora-*)
dnf install -y gcc-g++ cmake
dnf install -y glibc-static file libstdc++-static diffutils util-linux
;;
opensuse-leap-*)
zypper install -y make cmake gcc-c++ gcc11-c++
zypper install -y glibc-devel-static tar diffutils util-linux
;;
opensuse-tumbleweed-*)
zypper install -y make cmake gcc-c++
zypper install -y glibc-devel-static tar diffutils util-linux
;;
gentoo-*)
emerge-webrsync
emerge dev-util/cmake
;;
arch-* | archarm-*)
pacman -Sy
pacman -S --needed --noconfirm base-devel cmake util-linux
;;
void-*)
xbps-install -Sy xbps
xbps-install -Sy bash make cmake gcc
xbps-install -Sy tar diffutils util-linux
;;
alpine-*)
apk update
apk add bash make linux-headers cmake gcc g++
;;
*)
echo "Error: don't know anything about build dependencies on $ID-$VERSION_ID"
exit 1
esac
+19
View File
@@ -0,0 +1,19 @@
#!/bin/bash
set -e
. /etc/os-release
set -x
# This script install packages for -DMOLD_ENABLE_QEMU_TESTS=1
# to enable cross-target tests.
#
# Feel free to send me a PR if your OS is not on this list.
case "$ID-$VERSION_ID" in
ubuntu-* | pop-* | linuxmint-* | debian-* | raspbian-*)
apt-get install -y qemu-user {gcc,g++}-{i686,aarch64,riscv64,powerpc,powerpc64,powerpc64le,s390x,sparc64,m68k,sh4,alpha}-linux-gnu {gcc,g++}-arm-linux-gnueabihf
;;
*)
echo "Error: don't know anything about build dependencies on $ID-$VERSION_ID"
exit 1
esac
-258
View File
@@ -1,258 +0,0 @@
#include "mold.h"
namespace mold::macho {
// Returns [hi:lo] bits of val.
static u64 bits(u64 val, u64 hi, u64 lo) {
return (val >> lo) & (((u64)1 << (hi - lo + 1)) - 1);
}
static u64 page(u64 val) {
return val & 0xffff'ffff'ffff'f000;
}
static u64 encode_page(u64 val) {
return (bits(val, 13, 12) << 29) | (bits(val, 32, 14) << 5);
}
template <>
void StubsSection<ARM64>::copy_buf(Context<ARM64> &ctx) {
u32 *buf = (u32 *)(ctx.buf + this->hdr.offset);
for (i64 i = 0; i < syms.size(); i++) {
static const u32 insn[] = {
0x90000010, // adrp x16, $ptr@PAGE
0xf9400210, // ldr x16, [x16, $ptr@PAGEOFF]
0xd61f0200, // br x16
};
static_assert(sizeof(insn) == ARM64::stub_size);
u64 la_addr = ctx.lazy_symbol_ptr.hdr.addr + ARM64::word_size * i;
u64 this_addr = this->hdr.addr + ARM64::stub_size * i;
memcpy(buf, insn, sizeof(insn));
buf[0] |= encode_page(page(la_addr) - page(this_addr));
buf[1] |= bits(la_addr, 11, 3) << 10;
buf += 3;
}
}
template <>
void StubHelperSection<ARM64>::copy_buf(Context<ARM64> &ctx) {
u32 *start = (u32 *)(ctx.buf + this->hdr.offset);
u32 *buf = start;
static const u32 insn0[] = {
0x90000011, // adrp x17, $__dyld_private@PAGE
0x91000231, // add x17, x17, $__dyld_private@PAGEOFF
0xa9bf47f0, // stp x16, x17, [sp, #-16]!
0x90000010, // adrp x16, $dyld_stub_binder@PAGE
0xf9400210, // ldr x16, [x16, $dyld_stub_binder@PAGEOFF]
0xd61f0200, // br x16
};
static_assert(sizeof(insn0) == ARM64::stub_helper_hdr_size);
memcpy(buf, insn0, sizeof(insn0));
u64 dyld_private = get_symbol(ctx, "__dyld_private")->get_addr(ctx);
buf[0] |= encode_page(page(dyld_private) - page(this->hdr.addr));
buf[1] |= bits(dyld_private, 11, 0) << 10;
u64 stub_binder = get_symbol(ctx, "dyld_stub_binder")->get_addr(ctx);
buf[3] |= encode_page(page(stub_binder) - page(this->hdr.addr - 12));
buf[4] |= bits(stub_binder, 11, 0) << 10;
buf += 6;
for (i64 i = 0; i < ctx.stubs.syms.size(); i++) {
static const u32 insn[] = {
0x18000050, // ldr w16, addr
0x14000000, // b stubHelperHeader
0x00000000, // addr: .long <idx>
};
static_assert(sizeof(insn) == ARM64::stub_helper_size);
memcpy(buf, insn, sizeof(insn));
buf[1] |= bits((start - buf - 1) * 4, 27, 2);
buf[2] = ctx.stubs.bind_offsets[i];
buf += 3;
}
}
static i64 read_addend(u8 *buf, const MachRel &r) {
if (r.p2size == 2)
return *(i32 *)(buf + r.offset);
if (r.p2size == 3)
return *(i64 *)(buf + r.offset);
unreachable();
}
static Relocation<ARM64>
read_reloc(Context<ARM64> &ctx, ObjectFile<ARM64> &file,
const MachSection &hdr, MachRel *rels, i64 &idx) {
i64 addend = 0;
switch (rels[idx].type) {
case ARM64_RELOC_UNSIGNED:
case ARM64_RELOC_SUBTRACTOR:
addend = read_addend((u8 *)file.mf->data + hdr.offset, rels[idx]);
break;
case ARM64_RELOC_ADDEND:
addend = rels[idx++].offset;
break;
}
MachRel &r = rels[idx];
Relocation<ARM64> rel{r.offset, (u8)r.type, (u8)r.p2size, (bool)r.is_pcrel};
if (r.is_extern) {
rel.sym = file.syms[r.idx];
rel.addend = addend;
return rel;
}
u32 addr;
if (r.is_pcrel)
addr = hdr.addr + r.offset + addend;
else
addr = addend;
Subsection<ARM64> *target = file.find_subsection(ctx, addr);
if (!target)
Fatal(ctx) << file << ": bad relocation: " << r.offset;
rel.subsec = target;
rel.addend = addr - target->input_addr;
return rel;
}
template <>
std::vector<Relocation<ARM64>>
read_relocations(Context<ARM64> &ctx, ObjectFile<ARM64> &file,
const MachSection &hdr) {
std::vector<Relocation<ARM64>> vec;
MachRel *rels = (MachRel *)(file.mf->data + hdr.reloff);
for (i64 i = 0; i < hdr.nreloc; i++)
vec.push_back(read_reloc(ctx, file, hdr, rels, i));
return vec;
}
template <>
void Subsection<ARM64>::scan_relocations(Context<ARM64> &ctx) {
for (Relocation<ARM64> &r : get_rels()) {
Symbol<ARM64> *sym = r.sym;
if (!sym)
continue;
switch (r.type) {
case ARM64_RELOC_GOT_LOAD_PAGE21:
case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
case ARM64_RELOC_POINTER_TO_GOT:
sym->flags |= NEEDS_GOT;
break;
case ARM64_RELOC_TLVP_LOAD_PAGE21:
case ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
sym->flags |= NEEDS_THREAD_PTR;
break;
}
if (sym->file && sym->file->is_dylib()) {
sym->flags |= NEEDS_STUB;
((DylibFile<ARM64> *)sym->file)->is_needed = true;
}
}
}
template <>
void Subsection<ARM64>::apply_reloc(Context<ARM64> &ctx, u8 *buf) {
std::span<Relocation<ARM64>> rels = get_rels();
for (i64 i = 0; i < rels.size(); i++) {
Relocation<ARM64> &r = rels[i];
if (r.sym && !r.sym->file) {
Error(ctx) << "undefined symbol: " << isec.file << ": " << *r.sym;
continue;
}
u64 val = 0;
switch (r.type) {
case ARM64_RELOC_UNSIGNED:
case ARM64_RELOC_BRANCH26:
case ARM64_RELOC_PAGE21:
case ARM64_RELOC_PAGEOFF12:
val = r.sym ? r.sym->get_addr(ctx) : r.subsec->get_addr(ctx);
break;
case ARM64_RELOC_SUBTRACTOR: {
Relocation<ARM64> s = rels[++i];
assert(s.type == ARM64_RELOC_UNSIGNED);
u64 val1 = r.sym ? r.sym->get_addr(ctx) : r.subsec->get_addr(ctx);
u64 val2 = s.sym ? s.sym->get_addr(ctx) : s.subsec->get_addr(ctx);
val = val2 - val1;
break;
}
case ARM64_RELOC_GOT_LOAD_PAGE21:
case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
case ARM64_RELOC_POINTER_TO_GOT:
val = r.sym->get_got_addr(ctx);
break;
case ARM64_RELOC_TLVP_LOAD_PAGE21:
case ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
val = r.sym->get_tlv_addr(ctx);
break;
default:
Fatal(ctx) << isec << ": unknown reloc: " << (int)r.type;
}
val += r.addend;
switch (r.type) {
case ARM64_RELOC_UNSIGNED:
case ARM64_RELOC_SUBTRACTOR:
case ARM64_RELOC_POINTER_TO_GOT:
if (r.is_pcrel)
val -= get_addr(ctx) + r.offset;
if (r.p2size == 2)
*(i32 *)(buf + r.offset) = val;
else if (r.p2size == 3)
*(i64 *)(buf + r.offset) = val;
else
unreachable();
break;
case ARM64_RELOC_BRANCH26:
if (r.is_pcrel)
val -= get_addr(ctx) + r.offset;
*(u32 *)(buf + r.offset) |= bits(val, 27, 2);
break;
case ARM64_RELOC_PAGE21:
case ARM64_RELOC_GOT_LOAD_PAGE21:
case ARM64_RELOC_TLVP_LOAD_PAGE21:
assert(r.is_pcrel);
*(u32 *)(buf + r.offset) |=
encode_page(page(val) - page(get_addr(ctx) + r.offset));
break;
case ARM64_RELOC_PAGEOFF12:
case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: {
if (r.is_pcrel)
val -= get_addr(ctx) + r.offset;
u32 insn = *(u32 *)(buf + r.offset);
i64 scale = 0;
if ((insn & 0x3b000000) == 0x39000000)
scale = insn >> 30;
*(u32 *)(buf + r.offset) |= bits(val, 11, scale) << 10;
break;
}
default:
Fatal(ctx) << isec << ": unknown reloc: " << (int)r.type;
}
}
}
} // namespace mold::macho
-222
View File
@@ -1,222 +0,0 @@
#include "mold.h"
namespace mold::macho {
template <>
void StubsSection<X86_64>::copy_buf(Context<X86_64> &ctx) {
u8 *buf = ctx.buf + this->hdr.offset;
for (i64 i = 0; i < syms.size(); i++) {
// `ff 25 xx xx xx xx` is a RIP-relative indirect jump instruction,
// i.e., `jmp *IMM(%rip)`. It loads an address from la_symbol_ptr
// and jump there.
static_assert(X86_64::stub_size == 6);
buf[i * 6] = 0xff;
buf[i * 6 + 1] = 0x25;
*(u32 *)(buf + i * 6 + 2) =
ctx.lazy_symbol_ptr.hdr.addr + i * X86_64::word_size -
(this->hdr.addr + i * 6 + 6);
}
}
template <>
void StubHelperSection<X86_64>::copy_buf(Context<X86_64> &ctx) {
u8 *start = ctx.buf + this->hdr.offset;
u8 *buf = start;
static const u8 insn0[] = {
0x4c, 0x8d, 0x1d, 0, 0, 0, 0, // lea $__dyld_private(%rip), %r11
0x41, 0x53, // push %r11
0xff, 0x25, 0, 0, 0, 0, // jmp *$dyld_stub_binder@GOT(%rip)
0x90, // nop
};
static_assert(sizeof(insn0) == X86_64::stub_helper_hdr_size);
memcpy(buf, insn0, sizeof(insn0));
*(u32 *)(buf + 3) =
get_symbol(ctx, "__dyld_private")->get_addr(ctx) - this->hdr.addr - 7;
*(u32 *)(buf + 11) =
get_symbol(ctx, "dyld_stub_binder")->get_got_addr(ctx) - this->hdr.addr - 15;
buf += 16;
for (i64 i = 0; i < ctx.stubs.syms.size(); i++) {
u8 insn[] = {
0x68, 0, 0, 0, 0, // push $bind_offset
0xe9, 0, 0, 0, 0, // jmp $__stub_helper
};
static_assert(sizeof(insn) == X86_64::stub_helper_size);
memcpy(buf, insn, sizeof(insn));
*(u32 *)(buf + 1) = ctx.stubs.bind_offsets[i];
*(u32 *)(buf + 6) = start - buf - 10;
buf += 10;
}
}
static i64 get_reloc_addend(u32 type) {
switch (type) {
case X86_64_RELOC_SIGNED_1:
return 1;
case X86_64_RELOC_SIGNED_2:
return 2;
case X86_64_RELOC_SIGNED_4:
return 4;
default:
return 0;
}
}
static i64 read_addend(u8 *buf, const MachRel &r) {
if (r.p2size == 2)
return *(i32 *)(buf + r.offset) + get_reloc_addend(r.type);
if (r.p2size == 3)
return *(i64 *)(buf + r.offset) + get_reloc_addend(r.type);
unreachable();
}
static Relocation<X86_64>
read_reloc(Context<X86_64> &ctx, ObjectFile<X86_64> &file,
const MachSection &hdr, MachRel &r) {
if (r.p2size != 2 && r.p2size != 3)
Fatal(ctx) << file << ": invalid r.p2size: " << (u32)r.p2size;
if (r.is_pcrel) {
if (r.p2size != 2)
Fatal(ctx) << file << ": invalid PC-relative reloc: " << r.offset;
} else {
if (r.p2size != 3)
Fatal(ctx) << file << ": invalid non-PC-relative reloc: " << r.offset;
}
u8 *buf = (u8 *)file.mf->data + hdr.offset;
Relocation<X86_64> rel{r.offset, (u8)r.type, (u8)r.p2size, (bool)r.is_pcrel};
i64 addend = read_addend(buf, r);
if (r.is_extern) {
rel.sym = file.syms[r.idx];
rel.addend = addend;
return rel;
}
u32 addr;
if (r.is_pcrel)
addr = hdr.addr + r.offset + 4 + addend;
else
addr = addend;
Subsection<X86_64> *target = file.find_subsection(ctx, addr);
if (!target)
Fatal(ctx) << file << ": bad relocation: " << r.offset;
rel.subsec = target;
rel.addend = addr - target->input_addr;
return rel;
}
template <>
std::vector<Relocation<X86_64>>
read_relocations(Context<X86_64> &ctx, ObjectFile<X86_64> &file,
const MachSection &hdr) {
std::vector<Relocation<X86_64>> vec;
vec.reserve(hdr.nreloc);
MachRel *rels = (MachRel *)(file.mf->data + hdr.reloff);
for (i64 i = 0; i < hdr.nreloc; i++)
vec.push_back(read_reloc(ctx, file, hdr, rels[i]));
return vec;
}
template <>
void Subsection<X86_64>::scan_relocations(Context<X86_64> &ctx) {
for (Relocation<X86_64> &r : get_rels()) {
Symbol<X86_64> *sym = r.sym;
if (!sym)
continue;
switch (r.type) {
case X86_64_RELOC_GOT_LOAD:
if (sym->file && sym->file->is_dylib())
sym->flags |= NEEDS_GOT;
break;
case X86_64_RELOC_GOT:
sym->flags |= NEEDS_GOT;
break;
case X86_64_RELOC_TLV:
if (sym->file && sym->file->is_dylib())
sym->flags |= NEEDS_THREAD_PTR;
break;
}
if (sym->file && sym->file->is_dylib()) {
sym->flags |= NEEDS_STUB;
((DylibFile<X86_64> *)sym->file)->is_needed = true;
}
}
}
template <>
void Subsection<X86_64>::apply_reloc(Context<X86_64> &ctx, u8 *buf) {
for (const Relocation<X86_64> &r : get_rels()) {
if (r.sym && !r.sym->file) {
Error(ctx) << "undefined symbol: " << isec.file << ": " << *r.sym;
continue;
}
u64 val = 0;
switch (r.type) {
case X86_64_RELOC_UNSIGNED:
case X86_64_RELOC_SIGNED:
case X86_64_RELOC_BRANCH:
case X86_64_RELOC_SIGNED_1:
case X86_64_RELOC_SIGNED_2:
case X86_64_RELOC_SIGNED_4:
val = r.sym ? r.sym->get_addr(ctx) : r.subsec->get_addr(ctx);
break;
case X86_64_RELOC_GOT_LOAD:
if (r.sym->got_idx != -1) {
val = r.sym->get_got_addr(ctx);
} else {
// Relax MOVQ into LEAQ
if (buf[r.offset - 2] != 0x8b)
Error(ctx) << isec << ": invalid GOT_LOAD relocation";
buf[r.offset - 2] = 0x8d;
val = r.sym->get_addr(ctx);
}
break;
case X86_64_RELOC_GOT:
val = r.sym->get_got_addr(ctx);
break;
case X86_64_RELOC_TLV:
if (r.sym->tlv_idx != -1) {
val = r.sym->get_tlv_addr(ctx);
} else {
// Relax MOVQ into LEAQ
if (buf[r.offset - 2] != 0x8b)
Error(ctx) << isec << ": invalid TLV relocation";
buf[r.offset - 2] = 0x8d;
val = r.sym->get_addr(ctx);
}
break;
default:
Fatal(ctx) << isec << ": unknown reloc: " << (int)r.type;
}
val += r.addend;
if (r.is_pcrel)
val -= get_addr(ctx) + r.offset + 4 + get_reloc_addend(r.type);
if (r.p2size == 2)
*(u32 *)(buf + r.offset) = val;
else if (r.p2size == 3)
*(u64 *)(buf + r.offset) = val;
else
unreachable();
}
}
} // namespace mold::macho
-322
View File
@@ -1,322 +0,0 @@
#include "mold.h"
#include "../cmdline.h"
#include <optional>
#include <sstream>
#include <sys/stat.h>
#include <sys/types.h>
#include <regex>
#include <unistd.h>
#include <unordered_set>
namespace mold::macho {
static const char helpmsg[] = R"(
Options:
-F<PATH> Add DIR to framework search path
-L<PATH> Add DIR to library search path
-Z Do not search the standard directories when
searching for libraries and frameworks
-ObjC Load all static archive members that implement
an Objective-C class or category
-adhoc_codesign Add ad-hoc code signature to the output file
-no_adhoc_codesign
-arch <ARCH_NAME> Specify target architecture
-bundle Produce a mach-o bundle
-dead_strip Remove unreachable functions and data
-dead_strip_dylibs Remove unreachable dylibs from dependencies
-demangle Demangle C++ symbols in log messages (default)
-dylib Produce a dynamic library
-dynamic Link against dylibs (default)
-e <SYMBOL> Specify the entry point of a main executable
-execute Produce an executable (default)
-filelist <FILE>[,<DIR>] Specify the list of input file names
-framework <NAME>,[,<SUFFIX>]
Search for a given framework
-headerpad <SIZE> Allocate the size of padding after load commands
-headerpad_max_install_names
Allocate MAXPATHLEN byte padding after load commands
-help Report usage information
-l<LIB> Search for a given library
-lto_library <FILE> Ignored
-map <FILE> Write map file to a given file
-needed-l<LIB> Search for a given library
-needed-framework <NAME>[,<SUFFIX>]
Search for a given framework
-no_deduplicate Ignored
-o <FILE> Set output filename
-pagezero_size <SIZE> Specify the size of the __PAGEZERO segment
-platform_version <PLATFORM> <MIN_VERSION> <SDK_VERSION>
Set platform, platform version and SDK version
-rpath <PATH> Add PATH to the runpath search path list
-syslibroot <DIR> Prepend DIR to library search paths
-t Print out each file the linker loads
-v Report version information)";
template <typename E>
static i64 parse_platform(Context<E> &ctx, std::string_view arg) {
static std::regex re(R"(\d+)", std::regex_constants::ECMAScript);
if (std::regex_match(arg.begin(), arg.end(), re))
return stoi(std::string(arg));
if (arg == "macos")
return PLATFORM_MACOS;
if (arg == "ios")
return PLATFORM_IOS;
if (arg == "tvos")
return PLATFORM_TVOS;
if (arg == "watchos")
return PLATFORM_WATCHOS;
if (arg == "bridgeos")
return PLATFORM_BRIDGEOS;
if (arg == "mac-catalyst")
return PLATFORM_MACCATALYST;
if (arg == "ios-simulator")
return PLATFORM_IOSSIMULATOR;
if (arg == "tvos-simulator")
return PLATFORM_TVOSSIMULATOR;
if (arg == "watchos-simulator")
return PLATFORM_WATCHOSSIMULATOR;
if (arg == "driverkit")
return PLATFORM_DRIVERKIT;
Fatal(ctx) << "unknown -platform_version name: " << arg;
}
template <typename E>
static i64 parse_version(Context<E> &ctx, std::string_view arg) {
static std::regex re(R"((\d+)(?:\.(\d+))?(?:\.(\d+))?)",
std::regex_constants::ECMAScript);
std::cmatch m;
if (!std::regex_match(arg.begin(), arg.end(), m, re))
Fatal(ctx) << "malformed version number: " << arg;
i64 major = (m[1].length() == 0) ? 0 : stoi(m[1]);
i64 minor = (m[2].length() == 0) ? 0 : stoi(m[2]);
i64 patch = (m[3].length() == 0) ? 0 : stoi(m[3]);
return (major << 16) | (minor << 8) | patch;
}
static bool is_directory(std::filesystem::path path) {
std::error_code ec;
return std::filesystem::is_directory(path, ec) && !ec;
}
template <typename E>
void parse_nonpositional_args(Context<E> &ctx,
std::vector<std::string> &remaining) {
std::vector<std::string_view> &args = ctx.cmdline_args;
i64 i = 1;
std::vector<std::string> framework_paths;
std::vector<std::string> library_paths;
bool nostdlib = false;
std::optional<i64> pagezero_size;
while (i < args.size()) {
std::string_view arg;
std::string_view arg2;
std::string_view arg3;
auto read_arg = [&](std::string name) {
if (args[i] == name) {
if (args.size() <= i + 1)
Fatal(ctx) << "option -" << name << ": argument missing";
arg = args[i + 1];
i += 2;
return true;
}
return false;
};
auto read_arg3 = [&](std::string name) {
if (args[i] == name) {
if (args.size() <= i + 3)
Fatal(ctx) << "option -" << name << ": argument missing";
arg = args[i + 1];
arg2 = args[i + 2];
arg3 = args[i + 3];
i += 4;
return true;
}
return false;
};
auto read_joined = [&](std::string name) {
if (read_arg(name))
return true;
if (args[i].starts_with(name)) {
arg = args[i].substr(name.size());
i++;
return true;
}
return false;
};
auto read_flag = [&](std::string name) {
if (args[i] == name) {
i++;
return true;
}
return false;
};
if (args[i].starts_with('@')) {
std::vector<std::string_view> vec =
read_response_file(ctx, args[i].substr(1));
args.erase(args.begin() + i);
args.insert(args.begin() + i, vec.begin(), vec.end());
continue;
}
if (read_flag("-help") || read_flag("--help")) {
SyncOut(ctx) << "Usage: " << ctx.cmdline_args[i]
<< " [options] file...\n" << helpmsg;
exit(0);
}
if (read_joined("-F")) {
framework_paths.push_back(std::string(arg));
} else if (read_joined("-L")) {
library_paths.push_back(std::string(arg));
} else if (read_flag("-Z")) {
nostdlib = true;
} else if (read_flag("-ObjC")) {
ctx.arg.ObjC = true;
} else if (read_flag("-adhoc_codesign")) {
ctx.arg.adhoc_codesign = true;
} else if (read_flag("-no_adhoc_codesign")) {
ctx.arg.adhoc_codesign = false;
} else if (read_arg("-arch")) {
if (arg == "x86_64")
ctx.arg.arch = CPU_TYPE_X86_64;
else if (arg == "arm64")
ctx.arg.arch = CPU_TYPE_ARM64;
else
Fatal(ctx) << "unknown -arch: " << arg;
} else if (read_flag("-bundle")) {
ctx.output_type = MH_BUNDLE;
} else if (read_flag("-color-diagnostics") ||
read_flag("--color-diagnostics")) {
} else if (read_flag("-dead_strip")) {
ctx.arg.dead_strip = true;
} else if (read_flag("-dead_strip_dylibs")) {
ctx.arg.dead_strip_dylibs = true;
} else if (read_flag("-demangle")) {
ctx.arg.demangle = true;
} else if (read_flag("-dylib")) {
ctx.output_type = MH_DYLIB;
} else if (read_arg("-headerpad")) {
size_t pos;
ctx.arg.headerpad = std::stol(std::string(arg), &pos, 16);
if (pos != arg.size())
Fatal(ctx) << "malformed -headerpad: " << arg;
} else if (read_flag("-headerpad_max_install_names")) {
ctx.arg.headerpad = 1024;
} else if (read_flag("-dynamic")) {
ctx.arg.dynamic = true;
} else if (read_arg("-e")) {
ctx.arg.entry = arg;
} else if (read_flag("-execute")) {
ctx.output_type = MH_EXECUTE;
} else if (read_arg("-fatal_warnings")) {
} else if (read_arg("-filelist")) {
remaining.push_back("-filelist");
remaining.push_back(std::string(arg));
} else if (read_arg("-framework")) {
remaining.push_back("-framework");
remaining.push_back(std::string(arg));
} else if (read_arg("-lto_library")) {
} else if (read_joined("-l")) {
remaining.push_back("-l");
remaining.push_back(std::string(arg));
} else if (read_arg("-map")) {
ctx.arg.map = arg;
} else if (read_joined("-needed-l")) {
remaining.push_back("-needed-l");
remaining.push_back(std::string(arg));
} else if (read_arg("-needed_framework")) {
remaining.push_back("-needed_framework");
remaining.push_back(std::string(arg));
} else if (read_flag("-no_deduplicate")) {
} else if (read_arg("-o")) {
ctx.arg.output = arg;
} else if (read_arg("-pagezero_size")) {
size_t pos;
pagezero_size = std::stol(std::string(arg), &pos, 16);
if (pos != arg.size())
Fatal(ctx) << "malformed -pagezero_size: " << arg;
} else if (read_arg3("-platform_version")) {
ctx.arg.platform = parse_platform(ctx, arg);
ctx.arg.platform_min_version = parse_version(ctx, arg2);
ctx.arg.platform_sdk_version = parse_version(ctx, arg3);
} else if (read_arg("-rpath")) {
ctx.arg.rpath.push_back(std::string(arg));
} else if (read_flag("-search_dylibs_first")) {
Fatal(ctx) << "-search_dylibs_first is not supported";
} else if (read_flag("-search_paths_first")) {
} else if (read_arg("-syslibroot")) {
ctx.arg.syslibroot.push_back(std::string(arg));
} else if (read_flag("-t")) {
ctx.arg.trace = true;
} else if (read_flag("-v")) {
SyncOut(ctx) << mold_version;
} else {
if (args[i][0] == '-')
Fatal(ctx) << "unknown command line option: " << args[i];
remaining.push_back(std::string(args[i]));
i++;
}
}
auto add_search_path = [&](std::vector<std::string> &vec, std::string path) {
if (!path.starts_with('/') || ctx.arg.syslibroot.empty()) {
if (is_directory(path))
vec.push_back(path);
return;
}
bool found = false;
for (std::string &dir : ctx.arg.syslibroot) {
std::string str = path_clean(dir + "/" + path);
if (is_directory(str)) {
vec.push_back(str);
found = true;
}
}
if (!found && is_directory(path))
vec.push_back(path);
};
for (std::string &path : library_paths)
add_search_path(ctx.arg.library_paths, path);
if (!nostdlib) {
add_search_path(ctx.arg.library_paths, "/usr/lib");
add_search_path(ctx.arg.library_paths, "/usr/local/lib");
}
for (std::string &path : framework_paths)
add_search_path(ctx.arg.framework_paths, path);
if (!nostdlib) {
add_search_path(ctx.arg.framework_paths, "/Library/Frameworks");
add_search_path(ctx.arg.framework_paths, "/System/Library/Frameworks");
}
if (pagezero_size.has_value()) {
if (ctx.output_type != MH_EXECUTE)
Error(ctx) << "-pagezero_size option can only be used when"
<< " linking a main executable";
ctx.arg.pagezero_size = *pagezero_size;
} else {
ctx.arg.pagezero_size = (ctx.output_type == MH_EXECUTE) ? 0x100000000 : 0;
}
}
#define INSTANTIATE(E) \
template void parse_nonpositional_args(Context<E> &, std::vector<std::string> &)
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
} // namespace mold::macho
-112
View File
@@ -1,112 +0,0 @@
#include "mold.h"
namespace mold::macho {
template <typename E>
static std::vector<Subsection<E> *> collect_root_set(Context<E> &ctx) {
std::vector<Subsection<E> *> rootset;
auto mark = [&](Symbol<E> *sym) {
if (sym && sym->subsec)
rootset.push_back(sym->subsec);
};
mark(get_symbol(ctx, ctx.arg.entry));
if (ctx.output_type == MH_DYLIB || ctx.output_type == MH_BUNDLE)
for (ObjectFile<E> *file : ctx.objs)
for (Symbol<E> *sym : file->syms)
if (sym->file == file && sym->is_extern)
mark(sym);
return rootset;
}
template <typename E>
static void visit(Context<E> &ctx, Subsection<E> &subsec) {
if (subsec.is_alive.exchange(true))
return;
for (Relocation<E> &rel : subsec.get_rels()) {
if (rel.sym) {
if (rel.sym->subsec)
visit(ctx, *rel.sym->subsec);
} else {
visit(ctx, *rel.subsec);
}
}
for (UnwindRecord<E> &rec : subsec.get_unwind_records()) {
rec.is_alive = true;
visit(ctx, *rec.subsec);
if (rec.lsda)
visit(ctx, *rec.lsda);
if (Symbol<E> *sym = rec.personality; sym && sym->subsec)
visit(ctx, *sym->subsec);
}
}
template <typename E>
static bool refers_live_subsection(Subsection<E> &subsec) {
for (Relocation<E> &rel : subsec.get_rels()) {
if (rel.sym) {
if (!rel.sym->subsec || rel.sym->subsec->is_alive)
return true;
} else {
if (rel.subsec->is_alive)
return true;
}
}
return false;
}
template <typename E>
static void mark(Context<E> &ctx, const std::vector<Subsection<E> *> &rootset) {
for (Subsection<E> *subsec : rootset)
visit(ctx, *subsec);
bool repeat;
do {
repeat = false;
for (ObjectFile<E> *file : ctx.objs) {
for (std::unique_ptr<Subsection<E>> &subsec : file->subsections) {
if ((subsec->isec.hdr.attr & S_ATTR_LIVE_SUPPORT) &&
!subsec->is_alive &&
refers_live_subsection(*subsec)) {
visit(ctx, *subsec);
repeat = true;
}
}
}
} while (repeat);
}
template <typename E>
static void sweep(Context<E> &ctx) {
for (ObjectFile<E> *file : ctx.objs) {
std::erase_if(file->subsections,
[](const std::unique_ptr<Subsection<E>> &subsec) {
return !subsec->is_alive;
});
}
for (ObjectFile<E> *file : ctx.objs)
for (Symbol<E> *&sym : file->syms)
if (sym->file == file && sym->subsec && !sym->subsec->is_alive)
sym = nullptr;
}
template <typename E>
void dead_strip(Context<E> &ctx) {
std::vector<Subsection<E> *> rootset = collect_root_set(ctx);
mark(ctx, rootset);
sweep(ctx);
}
#define INSTANTIATE(E) \
template void dead_strip(Context<E> &)
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
} // namespace mold::macho
-511
View File
@@ -1,511 +0,0 @@
#include "mold.h"
#include <cstdlib>
#include <fcntl.h>
#include <iomanip>
#include <iostream>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
namespace mold::macho {
static u8 *open_file(std::string path) {
i64 fd = ::open(path.c_str(), O_RDONLY);
if (fd == -1)
return nullptr;
struct stat st;
if (fstat(fd, &st) == -1) {
std::cerr << path << ": fstat failed: " << errno_string();
exit(1);
}
void *ptr = mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (ptr == MAP_FAILED) {
std::cerr << path << ": mmap failed: " << errno_string();
exit(1);
}
close(fd);
return (u8 *)ptr;
}
static void print_bytes(u8 *buf, i64 size) {
if (size == 0) {
std::cout << "[]\n";
return;
}
std::cout << "[" << std::setw(2) << std::setfill('0') << (u32)buf[0];
for (i64 i = 1; i < size; i++)
std::cout << " " << std::setw(2) << std::setfill('0') << (u32)buf[i];
std::cout << "]\n";
}
struct ExportEntry {
std::string name;
u32 flags;
u64 addr;
};
static void read_trie(std::vector<ExportEntry> &vec, u8 *start, i64 offset = 0,
const std::string &prefix = "") {
u8 *buf = start + offset;
if (read_uleb(buf)) {
ExportEntry ent;
ent.name = prefix;
ent.flags = read_uleb(buf);
ent.addr = read_uleb(buf);
vec.push_back(std::move(ent));
}
for (i64 i = 0, n = read_uleb(buf); i < n; i++) {
std::string suffix((char *)buf);
buf += suffix.size() + 1;
i64 off = read_uleb(buf);
read_trie(vec, start, off, prefix + suffix);
}
}
void dump_unwind_info(u8 *buf, MachSection &sec) {
UnwindSectionHeader &hdr = *(UnwindSectionHeader *)(buf + sec.offset);
std::cout << std::hex << " Unwind info:"
<< "\n version: 0x" << hdr.version
<< "\n encoding_offset: 0x" << hdr.encoding_offset
<< "\n encoding_count: 0x" << hdr.encoding_count
<< "\n personality_offset: 0x" << hdr.personality_offset
<< "\n personality_count: 0x" << hdr.personality_count
<< "\n page_offset: 0x" << hdr.page_offset
<< "\n page_count: 0x" << hdr.page_count;
std::cout << "\n encoding:";
u32 *enc = (u32 *)(buf + sec.offset + hdr.encoding_offset);
for (i64 i = 0; i < hdr.encoding_count; i++)
std::cout << std::hex << "\n 0x" << enc[i];
std::cout << "\n personality:";
u32 *pers = (u32 *)(buf + sec.offset + hdr.personality_offset);
for (i64 i = 0; i < hdr.personality_count; i++)
std::cout << std::hex << "\n 0x" << pers[i];
UnwindFirstLevelPage *ent =
(UnwindFirstLevelPage *)(buf + sec.offset + hdr.page_offset);
for (i64 i = 0; i < hdr.page_count; i++) {
std::cout << std::hex << "\n function:"
<< "\n func_addr: 0x" << ent[i].func_addr
<< "\n page_offset: 0x" << ent[i].page_offset
<< "\n lsda_offset: 0x" << ent[i].lsda_offset;
if (i != hdr.page_count - 1) {
UnwindLsdaEntry *lsda =
(UnwindLsdaEntry *)(buf + sec.offset + ent[i].lsda_offset);
i64 lsda_size = ent[i + 1].lsda_offset - ent[i].lsda_offset;
for (i64 j = 0; j < lsda_size / sizeof(UnwindLsdaEntry); j++)
std::cout << std::hex
<< "\n lsda:"
<< "\n func_addr: 0x" << lsda[j].func_addr
<< "\n lsda_addr: 0x" << lsda[j].lsda_addr;
}
if (ent[i].page_offset == 0)
break;
u8 *addr = buf + sec.offset + ent[i].page_offset;
switch (u32 kind = *addr; kind) {
case UNWIND_SECOND_LEVEL_REGULAR: {
std::cout << "\n UNWIND_SECOND_LEVEL_REGULAR:" ;
break;
}
case UNWIND_SECOND_LEVEL_COMPRESSED: {
std::cout << "\n UNWIND_SECOND_LEVEL_COMPRESSED" ;
UnwindSecondLevelPage &hdr2 = *(UnwindSecondLevelPage *)addr;
std::cout << std::hex
<< "\n page_offset: 0x" << hdr2.page_offset
<< "\n page_count: 0x" << hdr2.page_count
<< "\n encoding_offset: 0x" << hdr2.encoding_offset
<< "\n encoding_count: 0x" << hdr2.encoding_count;
UnwindPageEntry *ent2 = (UnwindPageEntry *)(addr + hdr2.page_offset);
for (i64 j = 0; j < hdr2.page_count; j++)
std::cout << std::hex << "\n ent 0x"
<< (ent[i].func_addr + ent2[j].func_addr)
<< " 0x" << ent2[j].encoding;
u32 *enc = (u32 *)(addr + hdr2.encoding_offset);
for (i64 j = 0; j < hdr2.encoding_count; j++)
std::cout << std::hex << "\n 0x" << enc[j];
break;
}
default:
std::cout << "\n bad 2nd-level unwind info header: " << kind;
}
}
std::cout << "\n";
}
void dump_compact_unwind(u8 *buf, MachSection &sec) {
CompactUnwindEntry *ent = (CompactUnwindEntry *)(buf + sec.offset);
i64 nentry = sec.size / sizeof(CompactUnwindEntry);
std::cout << " Compact unwind:"
<< "\n num_entry: " << nentry;
for (i64 i = 0; i < nentry; i++) {
std::cout << std::hex
<< "\n entry: 0x" << (i * sizeof(CompactUnwindEntry))
<< "\n code_start: 0x" << ent[i].code_start
<< "\n code_len: 0x" << ent[i].code_len
<< "\n encoding: 0x" << ent[i].encoding
<< "\n personality: 0x" << ent[i].personality
<< "\n lsda: 0x" << ent[i].lsda;
}
std::cout << "\n";
}
void dump_file(std::string path) {
u8 *buf = open_file(path);
if (!buf) {
std::cerr << "cannot open " << path << "\n";
exit(1);
}
MachHeader &hdr = *(MachHeader *)buf;
std::cout << "magic: 0x" << std::hex << hdr.magic
<< "\ncputype: 0x" << hdr.cputype
<< "\ncpusubtype: 0x" << hdr.cpusubtype
<< "\nfiletype: 0x" << hdr.filetype
<< "\nncmds: 0x" << hdr.ncmds
<< "\nsizeofcmds: 0x" << hdr.sizeofcmds
<< "\nflags: 0x" << hdr.flags
<< "\n\n";
u8 *p = buf + sizeof(MachHeader);
for (i64 i = 0; i < hdr.ncmds; i++) {
std::cout << "fileoff: 0x" << std::hex << (p - buf) << "\n";
LoadCommand &lc = *(LoadCommand *)p;
p += lc.cmdsize;
auto print_dylib = [&] {
DylibCommand &cmd = *(DylibCommand *)&lc;
std::cout << " cmdsize: 0x" << cmd.cmdsize
<< "\n nameoff: 0x" << cmd.nameoff
<< "\n timestamp: 0x" << cmd.timestamp
<< "\n current_version: 0x" << cmd.current_version
<< "\n compatibility_version: 0x" << cmd.compatibility_version
<< "\n data: " << (char *)((char *)&cmd + cmd.nameoff)
<< "\n";
};
switch (lc.cmd) {
case LC_SYMTAB: {
std::cout << "LC_SYMTAB\n";
SymtabCommand &cmd = *(SymtabCommand *)&lc;
std::cout << " cmdsize: " << cmd.cmdsize
<< "\n symoff: 0x" << std::hex << cmd.symoff
<< "\n nsyms: " << std::dec << cmd.nsyms
<< "\n stroff: 0x" << std::hex << cmd.stroff
<< "\n strsize: 0x" << cmd.strsize
<< "\n symdata: ";
print_bytes(buf + cmd.symoff, cmd.nsyms * sizeof(MachSym));
std::cout << " strdata: ";
print_bytes(buf + cmd.stroff, cmd.strsize);
MachSym *syms = (MachSym *)(buf + cmd.symoff);
for (i64 j = 0; j < cmd.nsyms; j++) {
std::cout << " symbol:"
<< "\n name: " << (char *)(buf + cmd.stroff + syms[j].stroff)
<< "\n stub: " << (u32)syms[j].stub
<< "\n pext: " << (u32)syms[j].pext
<< "\n type: " << (u32)syms[j].type
<< "\n ext: " << (u32)syms[j].ext
<< "\n sect: 0x" << (u32)syms[j].sect
<< "\n desc: 0x" << (u32)syms[j].desc
<< "\n value: 0x" << syms[j].value
<< "\n";
}
break;
}
case LC_DYSYMTAB: {
std::cout << "LC_DYSYMTAB\n";
DysymtabCommand &cmd = *(DysymtabCommand *)&lc;
std::cout << " cmdsize: 0x" << cmd.cmdsize
<< "\n ilocalsym: 0x" << cmd.ilocalsym
<< "\n nlocalsym: 0x" << cmd.nlocalsym
<< "\n iextdefsym: 0x" << cmd.iextdefsym
<< "\n nextdefsym: 0x" << cmd.nextdefsym
<< "\n iundefsym: 0x" << cmd.iundefsym
<< "\n nundefsym: 0x" << cmd.nundefsym
<< "\n tocoff: 0x" << cmd.tocoff
<< "\n ntoc: 0x" << cmd.ntoc
<< "\n modtaboff: 0x" << cmd.modtaboff
<< "\n nmodtab: 0x" << cmd.nmodtab
<< "\n extrefsymoff: 0x" << cmd.extrefsymoff
<< "\n nextrefsyms: 0x" << cmd.nextrefsyms
<< "\n indirectsymoff: 0x" << cmd.indirectsymoff
<< "\n nindirectsyms: 0x" << cmd.nindirectsyms
<< "\n extreloff: 0x" << cmd.extreloff
<< "\n nextrel: 0x" << cmd.nextrel
<< "\n locreloff: 0x" << cmd.locreloff
<< "\n nlocrel: 0x" << cmd.nlocrel
<< "\n";
if (cmd.indirectsymoff) {
std::cout << " indirectsymdata: ";
print_bytes(buf + cmd.indirectsymoff, 4 * cmd.nindirectsyms);
}
break;
}
case LC_LOAD_DYLIB:
std::cout << "LC_LOAD_DYLIB\n";
print_dylib();
break;
case LC_LOAD_WEAK_DYLIB:
std::cout << "LC_LOAD_WEAK_DYLIB\n";
print_dylib();
break;
case LC_ID_DYLIB:
std::cout << "LC_ID_DYLIB\n";
print_dylib();
break;
case LC_LOAD_DYLINKER: {
std::cout << "LC_LOAD_DYLINKER\n";
DylinkerCommand &cmd = *(DylinkerCommand *)&lc;
std::cout << " cmdsize: 0x" << cmd.cmdsize
<< "\n nameoff: 0x" << cmd.nameoff
<< "\n data: " << (char *)((char *)&cmd + cmd.nameoff)
<< "\n";
break;
}
case LC_SEGMENT_64: {
std::cout << "LC_SEGMENT_64\n";
SegmentCommand &cmd = *(SegmentCommand *)&lc;
std::cout << " cmdsize: " << cmd.cmdsize
<< "\n segname: " << cmd.get_segname()
<< "\n vmaddr: 0x" << std::hex << cmd.vmaddr
<< "\n vmsize: 0x" << cmd.vmsize
<< "\n fileoff: 0x" << cmd.fileoff
<< "\n filesize: 0x" << cmd.filesize
<< "\n maxprot: " << std::dec << cmd.maxprot
<< "\n initprot: " << cmd.initprot
<< "\n nsects: " << cmd.nsects
<< "\n flags: 0x" << std::hex << cmd.flags
<< "\n";
MachSection *sec = (MachSection *)((u8 *)&lc + sizeof(cmd));
for (i64 j = 0; j < cmd.nsects; j++) {
std::cout << " section:\n sectname: " << sec[j].get_sectname()
<< "\n segname: " << sec[j].get_segname()
<< "\n addr: 0x" << std::hex << sec[j].addr
<< "\n size: 0x" << sec[j].size
<< "\n offset: 0x" << sec[j].offset
<< "\n p2align: " << std::dec << sec[j].p2align
<< "\n reloff: " << std::hex << sec[j].reloff
<< "\n nreloc: " << std::dec << sec[j].nreloc
<< "\n type: 0x" << std::hex << sec[j].type
<< "\n attr: 0x" << std::hex << sec[j].attr
<< "\n";
if (sec[j].type != S_ZEROFILL) {
std::cout << " contents: ";
print_bytes(buf + sec[j].offset, sec[j].size);
}
if (sec[j].reloff) {
MachRel *rel = (MachRel *)(buf + sec[j].reloff);
for (i64 k = 0; k < sec[j].nreloc; k++) {
std::cout << " reloc: "
<< "\n offset: 0x" << rel[k].offset
<< "\n idx: 0x" << rel[k].idx
<< "\n is_pcrel: " << rel[k].is_pcrel
<< "\n p2size: 0x" << rel[k].p2size
<< "\n is_extern: " << rel[k].is_extern
<< "\n type: " << rel[k].type
<< "\n";
}
}
if (sec[j].match("__TEXT", "__unwind_info"))
dump_unwind_info(buf, sec[j]);
if (sec[j].match("__LD", "__compact_unwind"))
dump_compact_unwind(buf, sec[j]);
}
break;
}
case LC_UUID: {
std::cout << "LC_UUID\n";
UUIDCommand &cmd = *(UUIDCommand *)&lc;
std::cout << " data: ";
print_bytes(cmd.uuid, sizeof(cmd.uuid));
break;
}
case LC_DYLD_INFO_ONLY: {
std::cout << "LC_DYLD_INFO_ONLY\n";
DyldInfoCommand &cmd = *(DyldInfoCommand *)&lc;
if (cmd.rebase_off) {
std::cout << " rebase: ";
print_bytes(buf + cmd.rebase_off, cmd.rebase_size);
std::cout << " rebase_off: 0x" << std::hex << cmd.rebase_off
<< "\n rebase_size: 0x" << cmd.rebase_size
<< "\n";
}
if (cmd.bind_off) {
std::cout << " bind: ";
print_bytes(buf + cmd.bind_off, cmd.bind_size);
std::cout << " bind_off: 0x" << std::hex << cmd.bind_off
<< "\n bind_size: 0x" << cmd.bind_size
<< "\n";
}
if (cmd.weak_bind_off) {
std::cout << " weak_bind: ";
print_bytes(buf + cmd.weak_bind_off, cmd.weak_bind_size);
std::cout << " weak_bind_off: 0x" << std::hex << cmd.weak_bind_off
<< "\n weak_bind_size: 0x" << cmd.weak_bind_size
<< "\n";
}
if (cmd.lazy_bind_off) {
std::cout << " lazy_bind: ";
print_bytes(buf + cmd.lazy_bind_off, cmd.lazy_bind_size);
std::cout << " lazy_bind_off: 0x" << std::hex << cmd.lazy_bind_off
<< "\n lazy_bind_size: 0x" << cmd.lazy_bind_size
<< "\n";
}
if (cmd.export_off) {
std::cout << " export: ";
print_bytes(buf + cmd.export_off, cmd.export_size);
std::cout << " export_off: 0x" << std::hex << cmd.export_off
<< "\n export_size: 0x" << cmd.export_size
<< "\n";
std::vector<ExportEntry> vec;
read_trie(vec, buf + cmd.export_off);
for (ExportEntry &ent : vec)
std::cout << " export_sym: " << ent.name << " 0x" << ent.addr << "\n";
}
break;
}
case LC_FUNCTION_STARTS: {
std::cout << "LC_FUNCTION_STARTS\n";
LinkEditDataCommand &cmd = *(LinkEditDataCommand *)&lc;
std::cout << " dataoff: 0x" << cmd.dataoff
<< "\n datasize: 0x" << cmd.datasize
<< "\n data:";
u8 *p = buf + cmd.dataoff;
u64 addr = 0;
for (;;) {
u64 delta = read_uleb(p);
if (!delta)
break;
addr += delta;
std::cout << std::hex << " 0x" << addr;
}
std::cout << "\n";
break;
}
case LC_MAIN: {
std::cout << "LC_MAIN\n";
EntryPointCommand &cmd = *(EntryPointCommand *)&lc;
std::cout << " cmdsize: 0x" << cmd.cmdsize
<< "\n entryoff: 0x" << cmd.entryoff
<< "\n stacksize: 0x" << cmd.stacksize
<< "\n";
break;
}
case LC_DATA_IN_CODE: {
std::cout << "LC_DATA_IN_CODE\n";
LinkEditDataCommand &cmd = *(LinkEditDataCommand *)&lc;
std::cout << " dataoff: 0x" << cmd.dataoff
<< "\n datasize: 0x" << cmd.datasize
<< "\n";
break;
}
case LC_SOURCE_VERSION: {
std::cout << "LC_SOURCE_VERSION\n";
SourceVersionCommand &cmd = *(SourceVersionCommand *)&lc;
std::cout << " version: 0x" << cmd.version
<< "\n";
break;
}
break;
case LC_BUILD_VERSION: {
std::cout << "LC_BUILD_VERSION\n";
BuildVersionCommand &cmd = *(BuildVersionCommand *)&lc;
std::cout << " cmdsize: 0x" << cmd.cmdsize
<< "\n platform: 0x" << cmd.platform
<< "\n minos: 0x" << cmd.minos
<< "\n sdk: 0x" << cmd.sdk
<< "\n ntools: 0x" << cmd.ntools
<< "\n";
BuildToolVersion *tools =
(BuildToolVersion *)((u8 *)&lc + sizeof(BuildVersionCommand));
for (i64 i = 0; i < cmd.ntools; i++)
std::cout << " tool: 0x" << tools[i].tool
<< "\n version: 0x" << tools[i].version
<< "\n";
break;
}
case LC_VERSION_MIN_MACOSX: {
std::cout << "LC_VERSION_MIN_MACOSX\n";
VersionMinCommand &cmd = *(VersionMinCommand *)&lc;
std::cout << " version: " << (int)cmd.version
<< "\n sdk: " << (int)cmd.sdk
<< "\n";
break;
}
case LC_CODE_SIGNATURE: {
std::cout << "LC_CODE_SIGNATURE\n";
LinkEditDataCommand &cmd = *(LinkEditDataCommand *)&lc;
CodeSignatureHeader &sig = *(CodeSignatureHeader *)(buf + cmd.dataoff);
std::cout << " magic: " << sig.magic
<< "\n length: " << sig.length
<< "\n count: " << sig.count
<< "\n";
for (i64 i = 0; i < sig.count; i++) {
CodeSignatureBlobIndex &idx = ((CodeSignatureBlobIndex *)(&sig + 1))[i];
std::cout << " idx type: " << idx.type
<< "\n idx offset: " << idx.offset
<< "\n";
CodeSignatureDirectory &dir =
*(CodeSignatureDirectory *)((u8 *)&sig + idx.offset);
std::cout << std::hex
<< " magic: 0x" << dir.magic
<< "\n version: 0x" << dir.version
<< "\n flags: 0x" << dir.flags
<< "\n hash_offset: 0x" << dir.hash_offset
<< "\n n_code_slots: 0x" << dir.n_code_slots
<< "\n hash_size: 0x" << (u32)dir.hash_size
<< "\n hash_type: 0x" << (u32)dir.hash_type
<< "\n page_size: 0x" << (1 << dir.page_size)
<< "\n";
}
break;
}
default:
std::cout << "UNKNOWN (0x" << std::hex << lc.cmd << ")\n";
break;
}
}
}
} // namespace mold::macho
-60
View File
@@ -1,60 +0,0 @@
#include "mold.h"
namespace mold::macho {
template <typename E>
InputSection<E>::InputSection(Context<E> &ctx, ObjectFile<E> &file,
const MachSection &hdr)
: file(file), hdr(hdr),
osec(*OutputSection<E>::get_instance(ctx, hdr.get_segname(),
hdr.get_sectname())) {
if (hdr.type != S_ZEROFILL)
contents = file.mf->get_contents().substr(hdr.offset, hdr.size);
}
template <typename E>
void InputSection<E>::parse_relocations(Context<E> &ctx) {
rels.reserve(hdr.nreloc);
// Parse mach-o relocations to fill `rels` vector
rels = read_relocations(ctx, file, hdr);
// Sort `rels` vector
sort(rels, [](const Relocation<E> &a, const Relocation<E> &b) {
return a.offset < b.offset;
});
// Find subsections for this section
auto begin = std::lower_bound(
file.subsections.begin(), file.subsections.end(), hdr.addr,
[](std::unique_ptr<Subsection<E>> &subsec, u32 addr) {
return subsec->input_addr < addr;
});
auto end = std::lower_bound(
begin, file.subsections.end(), hdr.addr + hdr.size,
[](std::unique_ptr<Subsection<E>> &subsec, u32 addr) {
return subsec->input_addr < addr;
});
// Assign each subsection a group of relocations
i64 i = 0;
for (auto it = begin; it < end; it++) {
Subsection<E> &subsec = **it;
subsec.rel_offset = i;
while (i < rels.size() &&
rels[i].offset < subsec.input_offset + subsec.input_size) {
rels[i].offset -= subsec.input_offset;
i++;
}
subsec.nrels = i - subsec.rel_offset;
}
}
#define INSTANTIATE(E) \
template class InputSection<E>
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
} // namespace mold::macho
-693
View File
@@ -1,693 +0,0 @@
#pragma once
#include "../big-endian.h"
#include <cassert>
#include <cstdint>
#include <cstring>
#include <string_view>
namespace mold::macho {
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t i8;
typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
static constexpr u32 FAT_MAGIC = 0xcafebabe;
static constexpr u32 MH_OBJECT = 0x1;
static constexpr u32 MH_EXECUTE = 0x2;
static constexpr u32 MH_FVMLIB = 0x3;
static constexpr u32 MH_CORE = 0x4;
static constexpr u32 MH_PRELOAD = 0x5;
static constexpr u32 MH_DYLIB = 0x6;
static constexpr u32 MH_DYLINKER = 0x7;
static constexpr u32 MH_BUNDLE = 0x8;
static constexpr u32 MH_DYLIB_STUB = 0x9;
static constexpr u32 MH_DSYM = 0xa;
static constexpr u32 MH_KEXT_BUNDLE = 0xb;
static constexpr u32 MH_NOUNDEFS = 0x1;
static constexpr u32 MH_INCRLINK = 0x2;
static constexpr u32 MH_DYLDLINK = 0x4;
static constexpr u32 MH_BINDATLOAD = 0x8;
static constexpr u32 MH_PREBOUND = 0x10;
static constexpr u32 MH_SPLIT_SEGS = 0x20;
static constexpr u32 MH_LAZY_INIT = 0x40;
static constexpr u32 MH_TWOLEVEL = 0x80;
static constexpr u32 MH_FORCE_FLAT = 0x100;
static constexpr u32 MH_NOMULTIDEFS = 0x200;
static constexpr u32 MH_NOFIXPREBINDING = 0x400;
static constexpr u32 MH_PREBINDABLE = 0x800;
static constexpr u32 MH_ALLMODSBOUND = 0x1000;
static constexpr u32 MH_SUBSECTIONS_VIA_SYMBOLS = 0x2000;
static constexpr u32 MH_CANONICAL = 0x4000;
static constexpr u32 MH_WEAK_DEFINES = 0x8000;
static constexpr u32 MH_BINDS_TO_WEAK = 0x10000;
static constexpr u32 MH_ALLOW_STACK_EXECUTION = 0x20000;
static constexpr u32 MH_ROOT_SAFE = 0x40000;
static constexpr u32 MH_SETUID_SAFE = 0x80000;
static constexpr u32 MH_NO_REEXPORTED_DYLIBS = 0x100000;
static constexpr u32 MH_PIE = 0x200000;
static constexpr u32 MH_DEAD_STRIPPABLE_DYLIB = 0x400000;
static constexpr u32 MH_HAS_TLV_DESCRIPTORS = 0x800000;
static constexpr u32 MH_NO_HEAP_EXECUTION = 0x1000000;
static constexpr u32 MH_APP_EXTENSION_SAFE = 0x02000000;
static constexpr u32 MH_NLIST_OUTOFSYNC_WITH_DYLDINFO = 0x04000000;
static constexpr u32 MH_SIM_SUPPORT = 0x08000000;
static constexpr u32 VM_PROT_READ = 0x1;
static constexpr u32 VM_PROT_WRITE = 0x2;
static constexpr u32 VM_PROT_EXECUTE = 0x4;
static constexpr u32 VM_PROT_NO_CHANGE = 0x8;
static constexpr u32 VM_PROT_COPY = 0x10;
static constexpr u32 VM_PROT_WANTS_COPY = 0x10;
static constexpr u32 LC_REQ_DYLD = 0x80000000;
static constexpr u32 LC_SEGMENT = 0x1;
static constexpr u32 LC_SYMTAB = 0x2;
static constexpr u32 LC_SYMSEG = 0x3;
static constexpr u32 LC_THREAD = 0x4;
static constexpr u32 LC_UNIXTHREAD = 0x5;
static constexpr u32 LC_LOADFVMLIB = 0x6;
static constexpr u32 LC_IDFVMLIB = 0x7;
static constexpr u32 LC_IDENT = 0x8;
static constexpr u32 LC_FVMFILE = 0x9;
static constexpr u32 LC_PREPAGE = 0xa;
static constexpr u32 LC_DYSYMTAB = 0xb;
static constexpr u32 LC_LOAD_DYLIB = 0xc;
static constexpr u32 LC_ID_DYLIB = 0xd;
static constexpr u32 LC_LOAD_DYLINKER = 0xe;
static constexpr u32 LC_ID_DYLINKER = 0xf;
static constexpr u32 LC_PREBOUND_DYLIB = 0x10;
static constexpr u32 LC_ROUTINES = 0x11;
static constexpr u32 LC_SUB_FRAMEWORK = 0x12;
static constexpr u32 LC_SUB_UMBRELLA = 0x13;
static constexpr u32 LC_SUB_CLIENT = 0x14;
static constexpr u32 LC_SUB_LIBRARY = 0x15;
static constexpr u32 LC_TWOLEVEL_HINTS = 0x16;
static constexpr u32 LC_PREBIND_CKSUM = 0x17;
static constexpr u32 LC_LOAD_WEAK_DYLIB = (0x18 | LC_REQ_DYLD);
static constexpr u32 LC_SEGMENT_64 = 0x19;
static constexpr u32 LC_ROUTINES_64 = 0x1a;
static constexpr u32 LC_UUID = 0x1b;
static constexpr u32 LC_RPATH = (0x1c | LC_REQ_DYLD);
static constexpr u32 LC_CODE_SIGNATURE = 0x1d;
static constexpr u32 LC_SEGMENT_SPLIT_INFO = 0x1e;
static constexpr u32 LC_REEXPORT_DYLIB = (0x1f | LC_REQ_DYLD);
static constexpr u32 LC_LAZY_LOAD_DYLIB = 0x20;
static constexpr u32 LC_ENCRYPTION_INFO = 0x21;
static constexpr u32 LC_DYLD_INFO = 0x22;
static constexpr u32 LC_DYLD_INFO_ONLY = (0x22 | LC_REQ_DYLD);
static constexpr u32 LC_LOAD_UPWARD_DYLIB = (0x23 | LC_REQ_DYLD);
static constexpr u32 LC_VERSION_MIN_MACOSX = 0x24;
static constexpr u32 LC_VERSION_MIN_IPHONEOS = 0x25;
static constexpr u32 LC_FUNCTION_STARTS = 0x26;
static constexpr u32 LC_DYLD_ENVIRONMENT = 0x27;
static constexpr u32 LC_MAIN = (0x28 | LC_REQ_DYLD);
static constexpr u32 LC_DATA_IN_CODE = 0x29;
static constexpr u32 LC_SOURCE_VERSION = 0x2A;
static constexpr u32 LC_DYLIB_CODE_SIGN_DRS = 0x2B;
static constexpr u32 LC_ENCRYPTION_INFO_64 = 0x2C;
static constexpr u32 LC_LINKER_OPTION = 0x2D;
static constexpr u32 LC_LINKER_OPTIMIZATION_HINT = 0x2E;
static constexpr u32 LC_VERSION_MIN_TVOS = 0x2F;
static constexpr u32 LC_VERSION_MIN_WATCHOS = 0x30;
static constexpr u32 LC_NOTE = 0x31;
static constexpr u32 LC_BUILD_VERSION = 0x32;
static constexpr u32 LC_DYLD_EXPORTS_TRIE = (0x33 | LC_REQ_DYLD);
static constexpr u32 LC_DYLD_CHAINED_FIXUPS = (0x34 | LC_REQ_DYLD);
static constexpr u32 SG_HIGHVM = 0x1;
static constexpr u32 SG_FVMLIB = 0x2;
static constexpr u32 SG_NORELOC = 0x4;
static constexpr u32 SG_PROTECTED_VERSION_1 = 0x8;
static constexpr u32 SG_READ_ONLY = 0x10;
static constexpr u32 S_REGULAR = 0x0;
static constexpr u32 S_ZEROFILL = 0x1;
static constexpr u32 S_CSTRING_LITERALS = 0x2;
static constexpr u32 S_4BYTE_LITERALS = 0x3;
static constexpr u32 S_8BYTE_LITERALS = 0x4;
static constexpr u32 S_LITERAL_POINTERS = 0x5;
static constexpr u32 S_NON_LAZY_SYMBOL_POINTERS = 0x6;
static constexpr u32 S_LAZY_SYMBOL_POINTERS = 0x7;
static constexpr u32 S_SYMBOL_STUBS = 0x8;
static constexpr u32 S_MOD_INIT_FUNC_POINTERS = 0x9;
static constexpr u32 S_MOD_TERM_FUNC_POINTERS = 0xa;
static constexpr u32 S_COALESCED = 0xb;
static constexpr u32 S_GB_ZEROFILL = 0xc;
static constexpr u32 S_INTERPOSING = 0xd;
static constexpr u32 S_16BYTE_LITERALS = 0xe;
static constexpr u32 S_DTRACE_DOF = 0xf;
static constexpr u32 S_LAZY_DYLIB_SYMBOL_POINTERS = 0x10;
static constexpr u32 S_THREAD_LOCAL_REGULAR = 0x11;
static constexpr u32 S_THREAD_LOCAL_ZEROFILL = 0x12;
static constexpr u32 S_THREAD_LOCAL_VARIABLES = 0x13;
static constexpr u32 S_THREAD_LOCAL_VARIABLE_POINTERS = 0x14;
static constexpr u32 S_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15;
static constexpr u32 S_INIT_FUNC_OFFSETS = 0x16;
static constexpr u32 S_ATTR_LOC_RELOC = 0x000001;
static constexpr u32 S_ATTR_EXT_RELOC = 0x000002;
static constexpr u32 S_ATTR_SOME_INSTRUCTIONS = 0x000004;
static constexpr u32 S_ATTR_DEBUG = 0x020000;
static constexpr u32 S_ATTR_SELF_MODIFYING_CODE = 0x040000;
static constexpr u32 S_ATTR_LIVE_SUPPORT = 0x080000;
static constexpr u32 S_ATTR_NO_DEAD_STRIP = 0x100000;
static constexpr u32 S_ATTR_STRIP_STATIC_SYMS = 0x200000;
static constexpr u32 S_ATTR_NO_TOC = 0x400000;
static constexpr u32 S_ATTR_PURE_INSTRUCTIONS = 0x800000;
static constexpr u32 CPU_TYPE_X86_64 = 0x1000007;
static constexpr u32 CPU_TYPE_ARM64 = 0x100000c;
static constexpr u32 CPU_SUBTYPE_X86_64_ALL = 3;
static constexpr u32 CPU_SUBTYPE_ARM64_ALL = 0;
static constexpr u32 REBASE_TYPE_POINTER = 1;
static constexpr u32 REBASE_TYPE_TEXT_ABSOLUTE32 = 2;
static constexpr u32 REBASE_TYPE_TEXT_PCREL32 = 3;
static constexpr u32 REBASE_OPCODE_MASK = 0xf0;
static constexpr u32 REBASE_IMMEDIATE_MASK = 0x0f;
static constexpr u32 REBASE_OPCODE_DONE = 0x00;
static constexpr u32 REBASE_OPCODE_SET_TYPE_IMM = 0x10;
static constexpr u32 REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x20;
static constexpr u32 REBASE_OPCODE_ADD_ADDR_ULEB = 0x30;
static constexpr u32 REBASE_OPCODE_ADD_ADDR_IMM_SCALED = 0x40;
static constexpr u32 REBASE_OPCODE_DO_REBASE_IMM_TIMES = 0x50;
static constexpr u32 REBASE_OPCODE_DO_REBASE_ULEB_TIMES = 0x60;
static constexpr u32 REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB = 0x70;
static constexpr u32 REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB = 0x80;
static constexpr u32 BIND_TYPE_POINTER = 1;
static constexpr u32 BIND_TYPE_TEXT_ABSOLUTE32 = 2;
static constexpr u32 BIND_TYPE_TEXT_PCREL32 = 3;
static constexpr u32 BIND_SPECIAL_DYLIB_SELF = 0;
static constexpr u32 BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE = -1;
static constexpr u32 BIND_SPECIAL_DYLIB_FLAT_LOOKUP = -2;
static constexpr u32 BIND_SPECIAL_DYLIB_WEAK_LOOKUP = -3;
static constexpr u32 BIND_SYMBOL_FLAGS_WEAK_IMPORT = 0x1;
static constexpr u32 BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION = 0x8;
static constexpr u32 BIND_OPCODE_MASK = 0xF0;
static constexpr u32 BIND_IMMEDIATE_MASK = 0x0F;
static constexpr u32 BIND_OPCODE_DONE = 0x00;
static constexpr u32 BIND_OPCODE_SET_DYLIB_ORDINAL_IMM = 0x10;
static constexpr u32 BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB = 0x20;
static constexpr u32 BIND_OPCODE_SET_DYLIB_SPECIAL_IMM = 0x30;
static constexpr u32 BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM = 0x40;
static constexpr u32 BIND_OPCODE_SET_TYPE_IMM = 0x50;
static constexpr u32 BIND_OPCODE_SET_ADDEND_SLEB = 0x60;
static constexpr u32 BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x70;
static constexpr u32 BIND_OPCODE_ADD_ADDR_ULEB = 0x80;
static constexpr u32 BIND_OPCODE_DO_BIND = 0x90;
static constexpr u32 BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB = 0xA0;
static constexpr u32 BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED = 0xB0;
static constexpr u32 BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB = 0xC0;
static constexpr u32 BIND_OPCODE_THREADED = 0xD0;
static constexpr u32 BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB = 0x00;
static constexpr u32 BIND_SUBOPCODE_THREADED_APPLY = 0x01;
static constexpr u32 EXPORT_SYMBOL_FLAGS_KIND_MASK = 0x03;
static constexpr u32 EXPORT_SYMBOL_FLAGS_KIND_REGULAR = 0x00;
static constexpr u32 EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL = 0x01;
static constexpr u32 EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE = 0x02;
static constexpr u32 EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION = 0x04;
static constexpr u32 EXPORT_SYMBOL_FLAGS_REEXPORT = 0x08;
static constexpr u32 EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER = 0x10;
static constexpr u32 DICE_KIND_DATA = 1;
static constexpr u32 DICE_KIND_JUMP_TABLE8 = 2;
static constexpr u32 DICE_KIND_JUMP_TABLE16 = 3;
static constexpr u32 DICE_KIND_JUMP_TABLE32 = 4;
static constexpr u32 DICE_KIND_ABS_JUMP_TABLE32 = 5;
static constexpr u32 N_UNDF = 0;
static constexpr u32 N_ABS = 1;
static constexpr u32 N_INDR = 5;
static constexpr u32 N_PBUD = 6;
static constexpr u32 N_SECT = 7;
static constexpr u32 N_GSYM = 0x20;
static constexpr u32 N_FNAME = 0x22;
static constexpr u32 N_FUN = 0x24;
static constexpr u32 N_STSYM = 0x26;
static constexpr u32 N_LCSYM = 0x28;
static constexpr u32 N_BNSYM = 0x2e;
static constexpr u32 N_AST = 0x32;
static constexpr u32 N_OPT = 0x3c;
static constexpr u32 N_RSYM = 0x40;
static constexpr u32 N_SLINE = 0x44;
static constexpr u32 N_ENSYM = 0x4e;
static constexpr u32 N_SSYM = 0x60;
static constexpr u32 N_SO = 0x64;
static constexpr u32 N_OSO = 0x66;
static constexpr u32 N_LSYM = 0x80;
static constexpr u32 N_BINCL = 0x82;
static constexpr u32 N_SOL = 0x84;
static constexpr u32 N_PARAMS = 0x86;
static constexpr u32 N_VERSION = 0x88;
static constexpr u32 N_OLEVEL = 0x8A;
static constexpr u32 N_PSYM = 0xa0;
static constexpr u32 N_EINCL = 0xa2;
static constexpr u32 N_ENTRY = 0xa4;
static constexpr u32 N_LBRAC = 0xc0;
static constexpr u32 N_EXCL = 0xc2;
static constexpr u32 N_RBRAC = 0xe0;
static constexpr u32 N_BCOMM = 0xe2;
static constexpr u32 N_ECOMM = 0xe4;
static constexpr u32 N_ECOML = 0xe8;
static constexpr u32 N_LENG = 0xfe;
static constexpr u32 N_PC = 0x30;
static constexpr u32 REFERENCE_TYPE = 0xf;
static constexpr u32 REFERENCE_FLAG_UNDEFINED_NON_LAZY = 0;
static constexpr u32 REFERENCE_FLAG_UNDEFINED_LAZY = 1;
static constexpr u32 REFERENCE_FLAG_DEFINED = 2;
static constexpr u32 REFERENCE_FLAG_PRIVATE_DEFINED = 3;
static constexpr u32 REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY = 4;
static constexpr u32 REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY = 5;
static constexpr u32 REFERENCED_DYNAMICALLY = 0x0010;
static constexpr u32 N_NO_DEAD_STRIP = 0x0020;
static constexpr u32 N_DESC_DISCARDED = 0x0020;
static constexpr u32 N_WEAK_REF = 0x0040;
static constexpr u32 N_WEAK_DEF = 0x0080;
static constexpr u32 N_REF_TO_WEAK = 0x0080;
static constexpr u32 N_ARM_THUMB_DEF = 0x0008;
static constexpr u32 N_SYMBOL_RESOLVER = 0x0100;
static constexpr u32 N_ALT_ENTRY = 0x0200;
static constexpr u32 PLATFORM_MACOS = 1;
static constexpr u32 PLATFORM_IOS = 2;
static constexpr u32 PLATFORM_TVOS = 3;
static constexpr u32 PLATFORM_WATCHOS = 4;
static constexpr u32 PLATFORM_BRIDGEOS = 5;
static constexpr u32 PLATFORM_MACCATALYST = 6;
static constexpr u32 PLATFORM_IOSSIMULATOR = 7;
static constexpr u32 PLATFORM_TVOSSIMULATOR = 8;
static constexpr u32 PLATFORM_WATCHOSSIMULATOR = 9;
static constexpr u32 PLATFORM_DRIVERKIT = 10;
static constexpr u32 TOOL_CLANG = 1;
static constexpr u32 TOOL_SWIFT = 2;
static constexpr u32 TOOL_LD = 3;
static constexpr u32 ARM64_RELOC_UNSIGNED = 0;
static constexpr u32 ARM64_RELOC_SUBTRACTOR = 1;
static constexpr u32 ARM64_RELOC_BRANCH26 = 2;
static constexpr u32 ARM64_RELOC_PAGE21 = 3;
static constexpr u32 ARM64_RELOC_PAGEOFF12 = 4;
static constexpr u32 ARM64_RELOC_GOT_LOAD_PAGE21 = 5;
static constexpr u32 ARM64_RELOC_GOT_LOAD_PAGEOFF12 = 6;
static constexpr u32 ARM64_RELOC_POINTER_TO_GOT = 7;
static constexpr u32 ARM64_RELOC_TLVP_LOAD_PAGE21 = 8;
static constexpr u32 ARM64_RELOC_TLVP_LOAD_PAGEOFF12 = 9;
static constexpr u32 ARM64_RELOC_ADDEND = 10;
static constexpr u32 X86_64_RELOC_UNSIGNED = 0;
static constexpr u32 X86_64_RELOC_SIGNED = 1;
static constexpr u32 X86_64_RELOC_BRANCH = 2;
static constexpr u32 X86_64_RELOC_GOT_LOAD = 3;
static constexpr u32 X86_64_RELOC_GOT = 4;
static constexpr u32 X86_64_RELOC_SUBTRACTOR = 5;
static constexpr u32 X86_64_RELOC_SIGNED_1 = 6;
static constexpr u32 X86_64_RELOC_SIGNED_2 = 7;
static constexpr u32 X86_64_RELOC_SIGNED_4 = 8;
static constexpr u32 X86_64_RELOC_TLV = 9;
struct FatHeader {
ubig32 magic;
ubig32 nfat_arch;
};
struct FatArch {
ubig32 cputype;
ubig32 cpusubtype;
ubig32 offset;
ubig32 size;
ubig32 align;
};
struct MachHeader {
u32 magic;
u32 cputype;
u32 cpusubtype;
u32 filetype;
u32 ncmds;
u32 sizeofcmds;
u32 flags;
u32 reserved;
};
struct LoadCommand {
u32 cmd;
u32 cmdsize;
};
struct SegmentCommand {
std::string_view get_segname() const {
return {segname, strnlen(segname, sizeof(segname))};
}
u32 cmd;
u32 cmdsize;
char segname[16];
u64 vmaddr;
u64 vmsize;
u64 fileoff;
u64 filesize;
u32 maxprot;
u32 initprot;
u32 nsects;
u32 flags;
};
struct MachSection {
void set_segname(std::string_view name) {
assert(name.size() <= sizeof(segname));
memcpy(segname, name.data(), name.size());
}
std::string_view get_segname() const {
return {segname, strnlen(segname, sizeof(segname))};
}
void set_sectname(std::string_view name) {
assert(name.size() <= sizeof(sectname));
memcpy(sectname, name.data(), name.size());
}
std::string_view get_sectname() const {
return {sectname, strnlen(sectname, sizeof(sectname))};
}
bool match(std::string_view segname, std::string_view sectname) const {
return get_segname() == segname && get_sectname() == sectname;
}
char sectname[16];
char segname[16];
u64 addr;
u64 size;
u32 offset;
u32 p2align;
u32 reloff;
u32 nreloc;
u32 type : 8;
u32 attr : 24;
u32 reserved1;
u32 reserved2;
u32 reserved3;
};
struct DylibCommand {
u32 cmd;
u32 cmdsize;
u32 nameoff;
u32 timestamp;
u32 current_version;
u32 compatibility_version;
};
struct DylinkerCommand {
u32 cmd;
u32 cmdsize;
u32 nameoff;
};
struct SymtabCommand {
u32 cmd;
u32 cmdsize;
u32 symoff;
u32 nsyms;
u32 stroff;
u32 strsize;
};
struct DysymtabCommand {
u32 cmd;
u32 cmdsize;
u32 ilocalsym;
u32 nlocalsym;
u32 iextdefsym;
u32 nextdefsym;
u32 iundefsym;
u32 nundefsym;
u32 tocoff;
u32 ntoc;
u32 modtaboff;
u32 nmodtab;
u32 extrefsymoff;
u32 nextrefsyms;
u32 indirectsymoff;
u32 nindirectsyms;
u32 extreloff;
u32 nextrel;
u32 locreloff;
u32 nlocrel;
};
struct VersionMinCommand {
u32 cmd;
u32 cmdsize;
u32 version;
u32 sdk;
};
struct DyldInfoCommand {
u32 cmd;
u32 cmdsize;
u32 rebase_off;
u32 rebase_size;
u32 bind_off;
u32 bind_size;
u32 weak_bind_off;
u32 weak_bind_size;
u32 lazy_bind_off;
u32 lazy_bind_size;
u32 export_off;
u32 export_size;
};
struct UUIDCommand {
u32 cmd;
u32 cmdsize;
u8 uuid[16];
};
struct RpathCommand {
u32 cmd;
u32 cmdsize;
u32 path_off;
};
struct LinkEditDataCommand {
u32 cmd;
u32 cmdsize;
u32 dataoff;
u32 datasize;
};
struct BuildToolVersion {
u32 tool;
u32 version;
};
struct BuildVersionCommand {
u32 cmd;
u32 cmdsize;
u32 platform;
u32 minos;
u32 sdk;
u32 ntools;
};
struct EntryPointCommand {
u32 cmd;
u32 cmdsize;
u64 entryoff;
u64 stacksize;
};
struct SourceVersionCommand {
u32 cmd;
u32 cmdsize;
u64 version;
};
struct DataInCodeEntry {
u32 offset;
u16 length;
u16 kind;
};
// This struct is named `n_list` on BSD and macOS.
struct MachSym {
bool is_undef() const {
return type == N_UNDF && !is_common();
}
bool is_common() const {
return type == N_UNDF && ext && value;
}
u32 stroff;
u8 ext : 1;
u8 type : 3;
u8 pext : 1;
u8 stub : 3;
u8 sect;
union {
u16 desc;
struct {
u16 : 8;
u16 p2align : 4;
u16 : 4;
};
};
u64 value;
};
// This struct is named `relocation_info` on BSD and macOS.
struct MachRel {
u32 offset;
u32 idx : 24;
u32 is_pcrel : 1;
u32 p2size : 2;
u32 is_extern : 1;
u32 type : 4;
};
// __TEXT,__unwind_info section contents
static constexpr u32 UNWIND_SECTION_VERSION = 1;
static constexpr u32 UNWIND_SECOND_LEVEL_REGULAR = 2;
static constexpr u32 UNWIND_SECOND_LEVEL_COMPRESSED = 3;
static constexpr u32 UNWIND_PERSONALITY_MASK = 0x30000000;
struct UnwindSectionHeader {
u32 version;
u32 encoding_offset;
u32 encoding_count;
u32 personality_offset;
u32 personality_count;
u32 page_offset;
u32 page_count;
};
struct UnwindFirstLevelPage {
u32 func_addr;
u32 page_offset;
u32 lsda_offset;
};
struct UnwindSecondLevelPage {
u32 kind;
u16 page_offset;
u16 page_count;
u16 encoding_offset;
u16 encoding_count;
};
struct UnwindLsdaEntry {
u32 func_addr;
u32 lsda_addr;
};
struct UnwindPageEntry {
u32 func_addr : 24;
u32 encoding : 8;
};
// __LD,__compact_unwind section contents
struct CompactUnwindEntry {
u64 code_start;
u32 code_len;
u32 encoding;
u64 personality;
u64 lsda;
};
// __LINKEDIT,__code_signature
static constexpr u32 CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0;
static constexpr u32 CS_SUPPORTSEXECSEG = 0x20400;
static constexpr u32 CSMAGIC_CODEDIRECTORY = 0xfade0c02;
static constexpr u32 CSSLOT_CODEDIRECTORY = 0;
static constexpr u32 CS_ADHOC = 0x00000002;
static constexpr u32 CS_LINKER_SIGNED = 0x00020000;
static constexpr u32 CS_EXECSEG_MAIN_BINARY = 1;
static constexpr u32 CS_HASHTYPE_SHA256 = 2;
struct CodeSignatureHeader {
ubig32 magic;
ubig32 length;
ubig32 count;
};
struct CodeSignatureBlobIndex {
ubig32 type;
ubig32 offset;
u32 padding;
};
struct CodeSignatureDirectory {
ubig32 magic;
ubig32 length;
ubig32 version;
ubig32 flags;
ubig32 hash_offset;
ubig32 ident_offset;
ubig32 n_special_slots;
ubig32 n_code_slots;
ubig32 code_limit;
u8 hash_size;
u8 hash_type;
u8 platform;
u8 page_size;
ubig32 spare2;
ubig32 scatter_offset;
ubig32 team_offset;
ubig32 spare3;
ubig64 code_limit64;
ubig64 exec_seg_base;
ubig64 exec_seg_limit;
ubig64 exec_seg_flags;
};
struct ARM64 {
static constexpr u32 cputype = CPU_TYPE_ARM64;
static constexpr u32 cpusubtype = CPU_SUBTYPE_ARM64_ALL;
static constexpr u32 abs_rel = ARM64_RELOC_UNSIGNED;
static constexpr u32 word_size = 8;
static constexpr u32 stub_size = 12;
static constexpr u32 stub_helper_hdr_size = 24;
static constexpr u32 stub_helper_size = 12;
};
struct X86_64 {
static constexpr u32 cputype = CPU_TYPE_X86_64;
static constexpr u32 cpusubtype = CPU_SUBTYPE_X86_64_ALL;
static constexpr u32 abs_rel = X86_64_RELOC_UNSIGNED;
static constexpr u32 word_size = 8;
static constexpr u32 stub_size = 6;
static constexpr u32 stub_helper_hdr_size = 16;
static constexpr u32 stub_helper_size = 10;
};
} // namespace mold::macho
-489
View File
@@ -1,489 +0,0 @@
#include "mold.h"
#include "../archive-file.h"
#include "../cmdline.h"
#include <cstdlib>
#include <fcntl.h>
#include <iomanip>
#include <iostream>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
namespace mold::macho {
static std::pair<std::string_view, std::string_view>
split_string(std::string_view str, char sep) {
size_t pos = str.find(sep);
if (pos == str.npos)
return {str, ""};
return {str.substr(0, pos), str.substr(pos + 1)};
}
template <typename E>
static void create_internal_file(Context<E> &ctx) {
ObjectFile<E> *obj = new ObjectFile<E>;
ctx.obj_pool.emplace_back(obj);
ctx.objs.push_back(obj);
auto add = [&](std::string_view name) {
Symbol<E> *sym = get_symbol(ctx, name);
sym->file = obj;
obj->syms.push_back(sym);
return sym;
};
add("__dyld_private");
switch (ctx.output_type) {
case MH_EXECUTE: {
Symbol<E> *sym = add("__mh_execute_header");
sym->is_extern = true;
sym->referenced_dynamically = true;
sym->value = ctx.arg.pagezero_size;
break;
}
case MH_DYLIB:
add("__mh_dylib_header");
break;
case MH_BUNDLE:
add("__mh_bundle_header");
break;
default:
unreachable();
}
}
template <typename E>
static bool compare_segments(const std::unique_ptr<OutputSegment<E>> &a,
const std::unique_ptr<OutputSegment<E>> &b) {
// We want to sort output segments in the following order:
// __TEXT, __DATA_CONST, __DATA, <other segments>, __LINKEDIT
auto get_rank = [](std::string_view name) {
if (name == "__TEXT")
return 0;
if (name == "__DATA_CONST")
return 1;
if (name == "__DATA")
return 2;
return INT_MAX;
};
std::string_view na = a->cmd.get_segname();
std::string_view nb = b->cmd.get_segname();
i64 ra = get_rank(na);
i64 rb = get_rank(nb);
if (ra != INT_MAX || rb != INT_MAX)
return ra < rb;
if (na == "__LINKEDIT" || nb == "__LINKEDIT")
return na != "__LINKEDIT";
return na < nb;
}
template <typename E>
static bool compare_chunks(const Chunk<E> *a, const Chunk<E> *b) {
assert(a->hdr.get_segname() == b->hdr.get_segname());
if ((a->hdr.type == S_ZEROFILL) != (b->hdr.type == S_ZEROFILL))
return a->hdr.type != S_ZEROFILL;
static const std::string_view rank[] = {
// __TEXT
"__mach_header",
"__text",
"__stubs",
"__stub_helper",
"__gcc_except_tab",
"__cstring",
"__unwind_info",
// __DATA_CONST
"__got",
// __DATA
"__la_symbol_ptr",
"__data",
"__thread_ptr",
"__thread_data",
"__thread_vars",
"__thread_bss",
"__common",
"__bss",
// __LINKEDIT
"__rebase",
"__binding",
"__weak_binding",
"__lazy_binding",
"__export",
"__func_starts",
"__data_in_code",
"__symbol_table",
"__ind_sym_tab",
"__string_table",
"__code_signature",
};
auto get_rank = [](const Chunk<E> *chunk) -> i64 {
std::string_view name = chunk->hdr.get_sectname();
i64 i = 0;
for (; i < sizeof(rank) / sizeof(rank[0]); i++)
if (name == rank[i])
return i;
return INT_MAX;
};
i64 ra = get_rank(a);
i64 rb = get_rank(b);
if (ra == INT_MAX && rb == INT_MAX)
return a->hdr.get_sectname() < b->hdr.get_sectname();
return ra < rb;
}
template <typename E>
static void create_synthetic_chunks(Context<E> &ctx) {
for (ObjectFile<E> *file : ctx.objs)
for (std::unique_ptr<Subsection<E>> &subsec : file->subsections)
subsec->isec.osec.add_subsec(subsec.get());
for (Chunk<E> *chunk : ctx.chunks) {
if (chunk != ctx.data && chunk->is_regular &&
((OutputSection<E> *)chunk)->members.empty())
continue;
OutputSegment<E> *seg =
OutputSegment<E>::get_instance(ctx, chunk->hdr.get_segname());
seg->chunks.push_back(chunk);
}
sort(ctx.segments, compare_segments<E>);
for (std::unique_ptr<OutputSegment<E>> &seg : ctx.segments)
sort(seg->chunks, compare_chunks<E>);
}
template <typename E>
static void scan_unwind_info(Context<E> &ctx) {
for (ObjectFile<E> *file : ctx.objs)
for (UnwindRecord<E> &rec : file->unwind_records)
if (!ctx.arg.dead_strip || rec.is_alive)
if (rec.personality)
rec.personality->flags |= NEEDS_GOT;
}
template <typename E>
static void export_symbols(Context<E> &ctx) {
ctx.got.add(ctx, get_symbol(ctx, "dyld_stub_binder"));
for (ObjectFile<E> *file : ctx.objs) {
for (Symbol<E> *sym : file->syms) {
if (sym && sym->file == file) {
if (sym->flags & NEEDS_GOT)
ctx.got.add(ctx, sym);
if (sym->flags & NEEDS_THREAD_PTR)
ctx.thread_ptrs.add(ctx, sym);
}
}
}
for (DylibFile<E> *file : ctx.dylibs) {
for (Symbol<E> *sym : file->syms) {
if (sym && sym->file == file) {
if (sym->flags & NEEDS_STUB)
ctx.stubs.add(ctx, sym);
if (sym->flags & NEEDS_GOT)
ctx.got.add(ctx, sym);
if (sym->flags & NEEDS_THREAD_PTR)
ctx.thread_ptrs.add(ctx, sym);
}
}
}
}
template <typename E>
static i64 assign_offsets(Context<E> &ctx) {
i64 sect_idx = 1;
for (std::unique_ptr<OutputSegment<E>> &seg : ctx.segments)
for (Chunk<E> *chunk : seg->chunks)
if (!chunk->is_hidden)
chunk->sect_idx = sect_idx++;
i64 fileoff = 0;
i64 vmaddr = ctx.arg.pagezero_size;
for (std::unique_ptr<OutputSegment<E>> &seg : ctx.segments) {
seg->set_offset(ctx, fileoff, vmaddr);
fileoff += seg->cmd.filesize;
vmaddr += seg->cmd.vmsize;
}
return fileoff;
}
template <typename E>
static void fix_synthetic_symbol_values(Context<E> &ctx) {
get_symbol(ctx, "__dyld_private")->value = ctx.data->hdr.addr;
get_symbol(ctx, "__mh_dylib_header")->value = ctx.data->hdr.addr;
get_symbol(ctx, "__mh_bundle_header")->value = ctx.data->hdr.addr;
}
template <typename E>
MappedFile<Context<E>> *find_framework(Context<E> &ctx, std::string name) {
std::string suffix;
std::tie(name, suffix) = split_string(name, ',');
for (std::string path : ctx.arg.framework_paths) {
path = get_realpath(path + "/" + name + ".framework/" + name);
if (!suffix.empty())
if (auto *mf = MappedFile<Context<E>>::open(ctx, path + suffix))
return mf;
if (auto *mf = MappedFile<Context<E>>::open(ctx, path + ".tbd"))
return mf;
if (auto *mf = MappedFile<Context<E>>::open(ctx, path))
return mf;
}
Fatal(ctx) << "-framework not found: " << name;
}
template <typename E>
MappedFile<Context<E>> *find_library(Context<E> &ctx, std::string name) {
for (std::string dir : ctx.arg.library_paths) {
for (std::string ext : {".tbd", ".dylib", ".a"}) {
std::string path = dir + "/lib" + name + ext;
if (MappedFile<Context<E>> *mf = MappedFile<Context<E>>::open(ctx, path))
return mf;
}
}
return nullptr;
}
template <typename E>
static MappedFile<Context<E>> *
strip_universal_header(Context<E> &ctx, MappedFile<Context<E>> *mf) {
FatHeader &hdr = *(FatHeader *)mf->data;
assert(hdr.magic == FAT_MAGIC);
FatArch *arch = (FatArch *)(mf->data + sizeof(hdr));
for (i64 i = 0; i < hdr.nfat_arch; i++)
if (arch[i].cputype == E::cputype)
return mf->slice(ctx, mf->name, arch[i].offset, arch[i].size);
Fatal(ctx) << mf->name << ": fat file contains no matching file";
}
template <typename E>
static void read_file(Context<E> &ctx, MappedFile<Context<E>> *mf,
bool is_needed = false) {
if (get_file_type(mf) == FileType::MACH_UNIVERSAL)
mf = strip_universal_header(ctx, mf);
switch (get_file_type(mf)) {
case FileType::TAPI:
case FileType::MACH_DYLIB:
ctx.dylibs.push_back(DylibFile<E>::create(ctx, mf));
if (is_needed || !ctx.arg.dead_strip_dylibs)
ctx.dylibs.back()->is_needed = true;
break;
case FileType::MACH_OBJ:
ctx.objs.push_back(ObjectFile<E>::create(ctx, mf, ""));
break;
case FileType::AR:
for (MappedFile<Context<E>> *child : read_archive_members(ctx, mf))
if (get_file_type(child) == FileType::MACH_OBJ)
ctx.objs.push_back(ObjectFile<E>::create(ctx, child, mf->name));
break;
default:
break;
// Fatal(ctx) << mf->name << ": unknown file type";
}
}
template <typename E>
static std::vector<std::string>
read_filelist(Context<E> &ctx, std::string arg) {
std::string path;
std::string dir;
if (size_t pos = arg.find(','); pos != arg.npos) {
path = arg.substr(0, pos);
dir = arg.substr(pos + 1) + "/";
} else {
path = arg;
}
MappedFile<Context<E>> *mf = MappedFile<Context<E>>::open(ctx, path);
if (!mf)
Fatal(ctx) << "-filepath: cannot open " << path;
std::vector<std::string> vec;
for (std::string_view str = mf->get_contents(); !str.empty();) {
std::string_view path;
std::tie(path, str) = split_string(str, '\n');
vec.push_back(path_clean(dir + std::string(path)));
}
return vec;
}
template <typename E>
static void read_input_files(Context<E> &ctx, std::span<std::string> args) {
while (!args.empty()) {
if (args[0].starts_with("-filelist")) {
for (std::string &path : read_filelist(ctx, args[1])) {
MappedFile<Context<E>> *mf = MappedFile<Context<E>>::open(ctx, path);
if (!mf)
Fatal(ctx) << "-filepath " << args[1] << ": cannot open file: " << path;
read_file(ctx, mf);
}
args = args.subspan(2);
} else if (args[0] == "-framework" || args[0] == "-needed_framework") {
bool needed = (args[0] == "-needed_framework");
read_file(ctx, find_framework(ctx, args[1]), needed);
args = args.subspan(2);
} else if (args[0] == "-l" || args[0] == "-needed-l") {
MappedFile<Context<E>> *mf = find_library(ctx, args[1]);
if (!mf)
Fatal(ctx) << "library not found: -l" << args[1];
bool needed = (args[0] == "-needed-l");
read_file(ctx, mf, needed);
args = args.subspan(2);
} else {
read_file(ctx, MappedFile<Context<E>>::must_open(ctx, args[0]));
args = args.subspan(1);
}
}
if (ctx.objs.empty())
Fatal(ctx) << "no input files";
}
template <typename E>
static int do_main(int argc, char **argv) {
Context<E> ctx;
if (argc > 1 && std::string_view(argv[1]) == "-dump") {
if (argc != 3)
Fatal(ctx) << "usage: ld64.mold -dump <executable-name>\n";
dump_file(argv[2]);
exit(0);
}
for (i64 i = 0; i < argc; i++)
ctx.cmdline_args.push_back(argv[i]);
std::vector<std::string> file_args;
parse_nonpositional_args(ctx, file_args);
if (ctx.arg.arch != E::cputype) {
if (ctx.arg.arch == CPU_TYPE_X86_64)
return do_main<X86_64>(argc, argv);
}
read_input_files(ctx, file_args);
i64 priority = 1;
for (ObjectFile<E> *file : ctx.objs)
file->priority = priority++;
for (DylibFile<E> *dylib : ctx.dylibs)
dylib->priority = priority++;
for (i64 i = 0; i < ctx.dylibs.size(); i++)
ctx.dylibs[i]->dylib_idx = i + 1;
for (ObjectFile<E> *file : ctx.objs)
file->parse(ctx);
for (DylibFile<E> *dylib : ctx.dylibs)
dylib->parse(ctx);
if (ctx.arg.ObjC)
for (ObjectFile<E> *file : ctx.objs)
if (!file->archive_name.empty() && file->is_objc_object(ctx))
file->is_alive = true;
for (ObjectFile<E> *file : ctx.objs) {
if (file->is_alive)
file->resolve_regular_symbols(ctx);
else
file->resolve_lazy_symbols(ctx);
}
std::vector<ObjectFile<E> *> live_objs;
for (ObjectFile<E> *file : ctx.objs)
if (file->is_alive)
live_objs.push_back(file);
for (i64 i = 0; i < live_objs.size(); i++)
append(live_objs, live_objs[i]->mark_live_objects(ctx));
for (DylibFile<E> *dylib : ctx.dylibs)
dylib->resolve_symbols(ctx);
if (ctx.output_type == MH_EXECUTE && !get_symbol(ctx, ctx.arg.entry)->file)
Error(ctx) << "undefined entry point symbol: " << ctx.arg.entry;
create_internal_file(ctx);
std::erase_if(ctx.objs, [](ObjectFile<E> *file) { return !file->is_alive; });
std::erase_if(ctx.dylibs, [](DylibFile<E> *file) { return !file->is_alive; });
if (ctx.arg.trace) {
for (ObjectFile<E> *file : ctx.objs)
SyncOut(ctx) << *file;
for (DylibFile<E> *file : ctx.dylibs)
SyncOut(ctx) << *file;
}
for (ObjectFile<E> *file : ctx.objs)
file->convert_common_symbols(ctx);
if (ctx.arg.dead_strip)
dead_strip(ctx);
create_synthetic_chunks(ctx);
for (ObjectFile<E> *file : ctx.objs)
file->check_duplicate_symbols(ctx);
for (i64 i = 0; i < ctx.segments.size(); i++)
ctx.segments[i]->seg_idx = i + 1;
for (ObjectFile<E> *file : ctx.objs)
for (std::unique_ptr<Subsection<E> > &subsec : file->subsections)
subsec->scan_relocations(ctx);
scan_unwind_info(ctx);
if (ctx.arg.dead_strip_dylibs)
std::erase_if(ctx.dylibs, [](DylibFile<E> *file) { return !file->is_needed; });
export_symbols(ctx);
i64 output_size = assign_offsets(ctx);
fix_synthetic_symbol_values(ctx);
ctx.output_file = OutputFile<E>::open(ctx, ctx.arg.output, output_size, 0777);
ctx.buf = ctx.output_file->buf;
for (std::unique_ptr<OutputSegment<E>> &seg : ctx.segments)
seg->copy_buf(ctx);
ctx.code_sig.write_signature(ctx);
ctx.output_file->close(ctx);
ctx.checkpoint();
if (!ctx.arg.map.empty())
print_map(ctx);
return 0;
}
int main(int argc, char **argv) {
if (!getenv("MOLD_SUPPRESS_MACHO_WARNING")) {
std::cerr <<
R"(********************************************************************************
mold for macOS is pre-alpha. Do not use unless you know what you are doing.
Do not report bugs because it's too early to manage missing features as bugs.
********************************************************************************
)";
}
return do_main<ARM64>(argc, argv);
}
}
-80
View File
@@ -1,80 +0,0 @@
#include "mold.h"
#include <iomanip>
#include <fstream>
namespace mold::macho {
struct Sym {
u64 addr;
u32 size;
u32 fileidx;
std::string_view name;
};
template <typename E>
void print_map(Context<E> &ctx) {
std::ofstream out(ctx.arg.map.c_str());
if (!out.is_open())
Fatal(ctx) << "cannot open " << ctx.arg.map << ": " << errno_string();
out << "# Path: " << ctx.arg.output << "\n"
<< "# Arch: x86-64\n"
<< "# Object files:\n";
std::vector<Sym> syms;
for (i64 i = 0; i < ctx.objs.size(); i++){
ObjectFile<E> &file = *ctx.objs[i];
if (file.is_alive) {
out << "[" << std::setw(3) << i << "] " << file << "\n";
for (Symbol<E> *sym : file.syms)
if (sym->file == &file)
syms.push_back({sym->get_addr(ctx), 0, (u32)i, sym->name});
}
}
sort(syms, [](const Sym &a, const Sym &b) {
return a.addr < b.addr;
});
out << "# Sections:\n"
<< "# Address Size Segment Section\n";
for (std::unique_ptr<OutputSegment<E>> &seg : ctx.segments) {
for (Chunk<E> *chunk : seg->chunks) {
if (!chunk->is_hidden) {
out << "0x" << std::hex
<< std::setw(8) << std::setfill('0') << chunk->hdr.addr
<< " 0x"
<< std::setw(8) << std::setfill('0') << chunk->hdr.size
<< " " << std::setw(7) << std::left << std::setfill(' ')
<< chunk->hdr.get_segname()
<< " " << chunk->hdr.get_sectname() << "\n";
}
}
}
out << "# Symbols:\n"
<< "# Address Size File Name\n";
for (Sym &sym : syms) {
out << "0x" << std::hex
<< std::setw(8) << std::setfill('0') << sym.addr
<< " 0x"
<< std::setw(8) << std::setfill('0') << 0
<< " ["
<< std::setw(3) << std::right << std::setfill(' ') << std::dec
<< sym.fileidx
<< "] " << sym.name << "\n";
}
}
#define INSTANTIATE(E) \
template void print_map(Context<E> &)
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
} // namespace mold::macho
-920
View File
@@ -1,920 +0,0 @@
#pragma once
#include "macho.h"
#include "../mold.h"
#include <map>
#include <memory>
#include <span>
#include <tbb/concurrent_hash_map.h>
#include <tbb/spin_mutex.h>
#include <unordered_map>
#include <variant>
namespace mold::macho {
static constexpr i64 COMMON_PAGE_SIZE = 0x4000;
static constexpr i64 SHA256_SIZE = 32;
template <typename E> class Chunk;
template <typename E> class InputSection;
template <typename E> class OutputSection;
template <typename E> class Subsection;
template <typename E> struct Context;
template <typename E> struct Symbol;
//
// object-file.cc
//
template <typename E>
struct Relocation {
u32 offset = 0;
u8 type = -1;
u8 p2size = 0;
bool is_pcrel = false;
i64 addend = 0;
Symbol<E> *sym = nullptr;
Subsection<E> *subsec = nullptr;
};
template <typename E>
struct UnwindRecord {
UnwindRecord(u32 len, u32 enc) : code_len(len), encoding(enc) {}
inline u64 get_func_raddr(Context<E> &ctx) const;
Subsection<E> *subsec = nullptr;
u32 offset = 0;
u32 code_len;
u32 encoding;
Symbol<E> *personality = nullptr;
Subsection<E> *lsda = nullptr;
u32 lsda_offset = 0;
bool is_alive = false;
};
template <typename E>
class InputFile {
public:
virtual ~InputFile() = default;
MappedFile<Context<E>> *mf = nullptr;
std::vector<Symbol<E> *> syms;
i64 priority = 0;
std::atomic_bool is_alive = false;
std::string archive_name;
virtual bool is_dylib() const = 0;
protected:
InputFile() = default;
};
template <typename E>
class ObjectFile : public InputFile<E> {
public:
ObjectFile() = default;
static ObjectFile *create(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::string archive_name);
bool is_dylib() const override { return false; }
void parse(Context<E> &ctx);
Subsection<E> *find_subsection(Context<E> &ctx, u32 addr);
void parse_compact_unwind(Context<E> &ctx, MachSection &hdr);
void resolve_regular_symbols(Context<E> &ctx);
void resolve_lazy_symbols(Context<E> &ctx);
bool is_objc_object(Context<E> &ctx);
std::vector<ObjectFile *> mark_live_objects(Context<E> &ctx);
void convert_common_symbols(Context<E> &ctx);
void check_duplicate_symbols(Context<E> &ctx);
Relocation<E> read_reloc(Context<E> &ctx, const MachSection &hdr, MachRel r);
std::vector<std::unique_ptr<InputSection<E>>> sections;
std::vector<std::unique_ptr<Subsection<E>>> subsections;
std::vector<u32> sym_to_subsec;
std::span<MachSym> mach_syms;
std::vector<Symbol<E>> local_syms;
std::vector<UnwindRecord<E>> unwind_records;
std::span<DataInCodeEntry> data_in_code_entries;
private:
void parse_sections(Context<E> &ctx);
void parse_symtab(Context<E> &ctx);
void split_subsections(Context<E> &ctx);
void parse_data_in_code(Context<E> &ctx);
LoadCommand *find_load_command(Context<E> &ctx, u32 type);
i64 find_subsection_idx(Context<E> &ctx, u32 addr);
void override_symbol(Context<E> &ctx, i64 symidx);
InputSection<E> *get_common_sec(Context<E> &ctx);
MachSection *unwind_sec = nullptr;
std::unique_ptr<MachSection> common_hdr;
InputSection<E> *common_sec = nullptr;
};
template <typename E>
class DylibFile : public InputFile<E> {
public:
static DylibFile *create(Context<E> &ctx, MappedFile<Context<E>> *mf);
void parse(Context<E> &ctx);
void resolve_symbols(Context<E> &ctx);
std::string_view install_name;
i64 dylib_idx = 0;
std::atomic_bool is_needed = false;
bool is_dylib() const override { return true; }
private:
void parse_dylib(Context<E> &ctx);
void read_trie(Context<E> &ctx, u8 *start, i64 offset = 0,
const std::string &prefix = "");
DylibFile() {
this->is_alive = true;
}
};
template <typename E>
std::ostream &operator<<(std::ostream &out, const InputFile<E> &file);
//
// input-sections.cc
//
template <typename E>
class InputSection {
public:
InputSection(Context<E> &ctx, ObjectFile<E> &file, const MachSection &hdr);
void parse_relocations(Context<E> &ctx);
ObjectFile<E> &file;
const MachSection &hdr;
OutputSection<E> &osec;
std::string_view contents;
std::vector<Symbol<E> *> syms;
std::vector<Relocation<E>> rels;
};
template <typename E>
std::ostream &operator<<(std::ostream &out, const InputSection<E> &sec);
template <typename E>
class Subsection {
public:
inline u64 get_addr(Context<E> &ctx) const;
std::string_view get_contents() {
assert(isec.hdr.type != S_ZEROFILL);
return isec.contents.substr(input_offset, input_size);
}
std::span<UnwindRecord<E>> get_unwind_records() {
return std::span<UnwindRecord<E>>(isec.file.unwind_records)
.subspan(unwind_offset, nunwind);
}
std::span<Relocation<E>> get_rels() const {
return std::span<Relocation<E>>(isec.rels).subspan(rel_offset, nrels);
}
void scan_relocations(Context<E> &ctx);
void apply_reloc(Context<E> &ctx, u8 *buf);
InputSection<E> &isec;
u32 input_offset = 0;
u32 input_size = 0;
u32 input_addr = 0;
u32 rel_offset = 0;
u32 nrels = 0;
u32 unwind_offset = 0;
u32 nunwind = 0;
u32 raddr = -1;
u16 p2align = 0;
std::atomic_bool is_alive = false;
};
template <typename E>
std::vector<Relocation<E>>
read_relocations(Context<E> &ctx, ObjectFile<E> &file, const MachSection &hdr);
//
// Symbol
//
enum {
NEEDS_GOT = 1 << 0,
NEEDS_STUB = 1 << 1,
NEEDS_THREAD_PTR = 1 << 2,
};
template <typename E>
struct Symbol {
Symbol() = default;
Symbol(std::string_view name) : name(name) {}
Symbol(const Symbol<E> &other) : name(other.name) {}
std::string_view name;
InputFile<E> *file = nullptr;
Subsection<E> *subsec = nullptr;
u64 value = 0;
i32 stub_idx = -1;
i32 got_idx = -1;
i32 tlv_idx = -1;
tbb::spin_mutex mu;
std::atomic_uint8_t flags = 0;
u8 is_extern : 1 = false;
u8 is_lazy : 1 = false;
u8 is_common : 1 = false;
u8 referenced_dynamically : 1 = false;
inline u64 get_addr(Context<E> &ctx) const;
inline u64 get_got_addr(Context<E> &ctx) const;
inline u64 get_tlv_addr(Context<E> &ctx) const;
};
template <typename E>
std::ostream &operator<<(std::ostream &out, const Symbol<E> &sym);
//
// output-chunks.cc
//
template <typename E>
class OutputSegment {
public:
static OutputSegment<E> *
get_instance(Context<E> &ctx, std::string_view name);
void set_offset(Context<E> &ctx, i64 fileoff, u64 vmaddr);
void copy_buf(Context<E> &ctx);
std::string_view name;
SegmentCommand cmd = {};
i32 seg_idx = -1;
std::vector<Chunk<E> *> chunks;
private:
OutputSegment(std::string_view name);
};
template <typename E>
class Chunk {
public:
inline Chunk(Context<E> &ctx, std::string_view segname,
std::string_view sectname);
virtual ~Chunk() = default;
virtual void compute_size(Context<E> &ctx) {};
virtual void copy_buf(Context<E> &ctx) {}
MachSection hdr = {};
u32 sect_idx = 0;
bool is_hidden = false;
bool is_regular = false;
};
template <typename E>
std::ostream &operator<<(std::ostream &out, const Chunk<E> &chunk);
template <typename E>
class OutputMachHeader : public Chunk<E> {
public:
OutputMachHeader(Context<E> &ctx)
: Chunk<E>(ctx, "__TEXT", "__mach_header") {
this->is_hidden = true;
}
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
};
template <typename E>
class OutputSection : public Chunk<E> {
public:
static OutputSection<E> *
get_instance(Context<E> &ctx, std::string_view segname,
std::string_view sectname);
OutputSection(Context<E> &ctx, std::string_view segname,
std::string_view sectname)
: Chunk<E>(ctx, segname, sectname) {
this->is_regular = true;
}
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
void add_subsec(Subsection<E> *subsec) {
members.push_back(subsec);
this->hdr.p2align = std::max<u32>(this->hdr.p2align, subsec->p2align);
this->hdr.attr |= subsec->isec.hdr.attr;
this->hdr.type = subsec->isec.hdr.type;
}
std::vector<Subsection<E> *> members;
};
class RebaseEncoder {
public:
RebaseEncoder();
void add(i64 seg_idx, i64 offset);
void flush();
void finish();
std::vector<u8> buf;
private:
i64 cur_seg = -1;
i64 cur_off = 0;
i64 times = 0;
};
template <typename E>
class OutputRebaseSection : public Chunk<E> {
public:
OutputRebaseSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__rebase") {
this->is_hidden = true;
}
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
std::vector<u8> contents;
};
class BindEncoder {
public:
BindEncoder();
void add(i64 dylib_idx, std::string_view sym, i64 flags, i64 seg_idx,
i64 offset);
void finish();
std::vector<u8> buf;
private:
std::string_view last_sym;
i64 last_flags = -1;
i64 last_dylib = -1;
i64 last_seg = -1;
i64 last_off = -1;
};
template <typename E>
class OutputBindSection : public Chunk<E> {
public:
OutputBindSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__binding") {
this->is_hidden = true;
}
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
std::vector<u8> contents;
};
template <typename E>
class OutputLazyBindSection : public Chunk<E> {
public:
OutputLazyBindSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__lazy_binding") {
this->is_hidden = true;
this->hdr.p2align = std::countr_zero(8U);
}
void add(Context<E> &ctx, Symbol<E> &sym, i64 flags);
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
std::vector<u8> contents;
};
class ExportEncoder {
public:
void add(std::string_view name, u32 flags, u64 addr);
i64 finish();
void write_trie(u8 *buf) {
write_trie(buf, root);
}
private:
struct Entry {
std::string_view name;
u32 flags;
u64 addr;
};
struct TrieNode {
std::string_view prefix;
bool is_leaf = false;
u32 flags = 0;
u64 addr = 0;
u32 offset = -1;
std::vector<TrieNode> children;
};
static i64 common_prefix_len(std::span<Entry> entries, i64 len);
static TrieNode construct_trie(std::span<Entry> entries, i64 len);
static i64 set_offset(TrieNode &node, i64 offset);
void write_trie(u8 *buf, TrieNode &node);
TrieNode root;
std::vector<Entry> entries;
};
template <typename E>
class OutputExportSection : public Chunk<E> {
public:
OutputExportSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__export") {
this->is_hidden = true;
}
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
private:
ExportEncoder enc;
};
template <typename E>
class OutputFunctionStartsSection : public Chunk<E> {
public:
OutputFunctionStartsSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__func_starts") {
this->is_hidden = true;
}
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
std::vector<u8> contents;
};
template <typename E>
class OutputSymtabSection : public Chunk<E> {
public:
OutputSymtabSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__symbol_table") {
this->is_hidden = true;
this->hdr.p2align = std::countr_zero(8U);
}
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
struct Entry {
Symbol<E> *sym;
i64 stroff;
};
std::vector<Entry> locals;
std::vector<Entry> globals;
std::vector<Entry> undefs;
};
template <typename E>
class OutputStrtabSection : public Chunk<E> {
public:
OutputStrtabSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__string_table") {
this->is_hidden = true;
this->hdr.p2align = std::countr_zero(8U);
}
i64 add_string(std::string_view str);
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
std::string contents{1, '\0'};
};
template <typename E>
class OutputIndirectSymtabSection : public Chunk<E> {
public:
OutputIndirectSymtabSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__ind_sym_tab") {
this->is_hidden = true;
}
static constexpr i64 ENTRY_SIZE = 4;
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
struct Entry {
Symbol<E> *sym;
i64 symtab_idx;
};
std::vector<Entry> stubs;
std::vector<Entry> gots;
};
template <typename E>
class CodeSignatureSection : public Chunk<E> {
public:
CodeSignatureSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__code_signature") {
this->is_hidden = true;
this->hdr.p2align = std::countr_zero(16U);
}
void compute_size(Context<E> &ctx) override;
void write_signature(Context<E> &ctx);
static constexpr i64 BLOCK_SIZE = 4096;
};
template <typename E>
class DataInCodeSection : public Chunk<E> {
public:
DataInCodeSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__data_in_code") {
this->is_hidden = true;
this->hdr.p2align = std::countr_zero(alignof(DataInCodeEntry));
}
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
std::vector<DataInCodeEntry> contents;
};
template <typename E>
class StubsSection : public Chunk<E> {
public:
StubsSection(Context<E> &ctx) : Chunk<E>(ctx, "__TEXT", "__stubs") {
this->hdr.p2align = std::countr_zero(2U);
this->hdr.type = S_SYMBOL_STUBS;
this->hdr.attr = S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS;
this->hdr.reserved2 = E::stub_size;
}
void add(Context<E> &ctx, Symbol<E> *sym);
void copy_buf(Context<E> &ctx) override;
std::vector<Symbol<E> *> syms;
std::vector<u32> bind_offsets;
};
template <typename E>
class StubHelperSection : public Chunk<E> {
public:
StubHelperSection(Context<E> &ctx)
: Chunk<E>(ctx, "__TEXT", "__stub_helper") {
this->hdr.p2align = std::countr_zero(4U);
this->hdr.attr = S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS;
}
void copy_buf(Context<E> &ctx) override;
};
template <typename E>
class UnwindEncoder {
public:
std::vector<u8> encode(Context<E> &ctx, std::span<UnwindRecord<E>> records);
private:
u32 encode_personality(Context<E> &ctx, Symbol<E> *sym);
std::vector<std::span<UnwindRecord<E>>>
split_records(Context<E> &ctx, std::span<UnwindRecord<E>>);
std::vector<Symbol<E> *> personalities;
};
template <typename E>
class UnwindInfoSection : public Chunk<E> {
public:
UnwindInfoSection(Context<E> &ctx)
: Chunk<E>(ctx, "__TEXT", "__unwind_info") {
this->hdr.p2align = std::countr_zero(4U);
}
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
std::vector<u8> contents;
};
template <typename E>
class GotSection : public Chunk<E> {
public:
GotSection(Context<E> &ctx) : Chunk<E>(ctx, "__DATA_CONST", "__got") {
this->hdr.p2align = std::countr_zero(8U);
this->hdr.type = S_NON_LAZY_SYMBOL_POINTERS;
}
void add(Context<E> &ctx, Symbol<E> *sym);
void copy_buf(Context<E> &ctx) override;
std::vector<Symbol<E> *> syms;
};
template <typename E>
class LazySymbolPtrSection : public Chunk<E> {
public:
LazySymbolPtrSection(Context<E> &ctx)
: Chunk<E>(ctx, "__DATA", "__la_symbol_ptr") {
this->hdr.p2align = std::countr_zero(8U);
this->hdr.type = S_LAZY_SYMBOL_POINTERS;
}
void copy_buf(Context<E> &ctx) override;
};
template <typename E>
class ThreadPtrsSection : public Chunk<E> {
public:
ThreadPtrsSection(Context<E> &ctx)
: Chunk<E>(ctx, "__DATA", "__thread_ptrs") {
this->hdr.p2align = std::countr_zero(8U);
this->hdr.type = S_THREAD_LOCAL_VARIABLE_POINTERS;
}
void add(Context<E> &ctx, Symbol<E> *sym);
void copy_buf(Context<E> &ctx) override;
std::vector<Symbol<E> *> syms;
};
//
// mapfile.cc
//
template <typename E>
void print_map(Context<E> &ctx);
//
// dumper.cc
//
void dump_file(std::string path);
//
// output-file.cc
//
template <typename E>
class OutputFile {
public:
static std::unique_ptr<OutputFile>
open(Context<E> &ctx, std::string path, i64 filesize, i64 perm);
virtual void close(Context<E> &ctx) = 0;
virtual ~OutputFile() {}
u8 *buf = nullptr;
std::string path;
i64 filesize;
bool is_mmapped;
bool is_unmapped = false;
protected:
OutputFile(std::string path, i64 filesize, bool is_mmapped)
: path(path), filesize(filesize), is_mmapped(is_mmapped) {}
};
//
// yaml.cc
//
struct YamlNode {
std::variant<std::string_view,
std::vector<YamlNode>,
std::map<std::string_view, YamlNode>> data;
};
struct YamlError {
std::string msg;
i64 pos;
};
std::variant<std::vector<YamlNode>, YamlError>
parse_yaml(std::string_view str);
//
// tapi.cc
//
struct TextDylib {
std::string_view uuid;
std::string_view install_name;
std::string_view current_version = "1.0";
std::string_view parent_umbrella;
std::vector<std::string_view> reexported_libs;
std::vector<std::string_view> exports;
};
template <typename E>
TextDylib parse_tbd(Context<E> &ctx, MappedFile<Context<E>> *mf);
//
// cmdline.cc
//
template <typename E>
void parse_nonpositional_args(Context<E> &ctx,
std::vector<std::string> &remaining);
//
// dead-strip.cc
//
template <typename E>
void dead_strip(Context<E> &ctx);
//
// main.cc
//
template <typename E>
struct Context {
Context() {
text_seg = OutputSegment<E>::get_instance(*this, "__TEXT");
data_const_seg = OutputSegment<E>::get_instance(*this, "__DATA_CONST");
data_seg = OutputSegment<E>::get_instance(*this, "__DATA");
linkedit_seg = OutputSegment<E>::get_instance(*this, "__LINKEDIT");
text = OutputSection<E>::get_instance(*this, "__TEXT", "__text");
data = OutputSection<E>::get_instance(*this, "__DATA", "__data");
bss = OutputSection<E>::get_instance(*this, "__DATA", "__bss");
cstring = OutputSection<E>::get_instance(*this, "__TEXT", "__cstring");
common = OutputSection<E>::get_instance(*this, "__DATA", "__common");
bss->hdr.type = S_ZEROFILL;
cstring->hdr.type = S_CSTRING_LITERALS;
common->hdr.type = S_ZEROFILL;
}
Context(const Context<E> &) = delete;
void checkpoint() {
if (has_error) {
cleanup();
_exit(1);
}
}
// Command-line arguments
struct {
bool ObjC = false;
bool adhoc_codesign = true;
bool color_diagnostics = false;
bool dead_strip = true;
bool dead_strip_dylibs = false;
bool deduplicate = true;
bool demangle = false;
bool dylib = false;
bool dynamic = true;
bool fatal_warnings = false;
bool noinhibit_exec = false;
bool trace = false;
i64 arch = CPU_TYPE_ARM64;
i64 headerpad = 256;
i64 pagezero_size = 0;
i64 platform = PLATFORM_MACOS;
i64 platform_min_version = 0;
i64 platform_sdk_version = 0;
std::string chroot;
std::string entry = "_main";
std::string map;
std::string output = "a.out";
std::vector<std::string> framework_paths;
std::vector<std::string> library_paths;
std::vector<std::string> rpath;
std::vector<std::string> syslibroot;
} arg;
std::vector<std::string_view> cmdline_args;
u32 output_type = MH_EXECUTE;
bool has_error = false;
tbb::concurrent_hash_map<std::string_view, Symbol<E>> symbol_map;
std::unique_ptr<OutputFile<E>> output_file;
u8 *buf;
tbb::concurrent_vector<std::unique_ptr<ObjectFile<E>>> obj_pool;
tbb::concurrent_vector<std::unique_ptr<DylibFile<E>>> dylib_pool;
tbb::concurrent_vector<std::unique_ptr<u8[]>> string_pool;
tbb::concurrent_vector<std::unique_ptr<MappedFile<Context<E>>>> mf_pool;
std::vector<std::unique_ptr<OutputSection<E>>> osec_pool;
tbb::concurrent_vector<std::unique_ptr<TimerRecord>> timer_records;
std::vector<ObjectFile<E> *> objs;
std::vector<DylibFile<E> *> dylibs;
OutputSegment<E> *text_seg = nullptr;
OutputSegment<E> *data_const_seg = nullptr;
OutputSegment<E> *data_seg = nullptr;
OutputSegment<E> *linkedit_seg = nullptr;
std::vector<std::unique_ptr<OutputSegment<E>>> segments;
std::vector<Chunk<E> *> chunks;
OutputMachHeader<E> mach_hdr{*this};
StubsSection<E> stubs{*this};
StubHelperSection<E> stub_helper{*this};
UnwindInfoSection<E> unwind_info{*this};
GotSection<E> got{*this};
LazySymbolPtrSection<E> lazy_symbol_ptr{*this};
CodeSignatureSection<E> code_sig{*this};
DataInCodeSection<E> data_in_code{*this};
ThreadPtrsSection<E> thread_ptrs{*this};
OutputRebaseSection<E> rebase{*this};
OutputBindSection<E> bind{*this};
OutputLazyBindSection<E> lazy_bind{*this};
OutputExportSection<E> export_{*this};
OutputFunctionStartsSection<E> function_starts{*this};
OutputSymtabSection<E> symtab{*this};
OutputIndirectSymtabSection<E> indir_symtab{*this};
OutputStrtabSection<E> strtab{*this};
OutputSection<E> *text = nullptr;
OutputSection<E> *data = nullptr;
OutputSection<E> *bss = nullptr;
OutputSection<E> *cstring = nullptr;
OutputSection<E> *common = nullptr;
};
int main(int argc, char **argv);
//
// Inline functions
//
template <typename E>
std::ostream &operator<<(std::ostream &out, const InputSection<E> &sec) {
out << sec.file << "(" << sec.hdr.get_segname() << ","
<< sec.hdr.get_sectname() << ")";
return out;
}
template <typename E>
u64 Subsection<E>::get_addr(Context<E> &ctx) const {
return ctx.arg.pagezero_size + raddr;
}
template <typename E>
u64 Symbol<E>::get_addr(Context<E> &ctx) const {
if (subsec)
return subsec->get_addr(ctx) + value;
if (stub_idx != -1)
return ctx.stubs.hdr.addr + stub_idx * E::stub_size;
return value;
}
template <typename E>
u64 Symbol<E>::get_got_addr(Context<E> &ctx) const {
assert(got_idx != -1);
return ctx.got.hdr.addr + got_idx * E::word_size;
}
template <typename E>
u64 Symbol<E>::get_tlv_addr(Context<E> &ctx) const {
assert(tlv_idx != -1);
return ctx.thread_ptrs.hdr.addr + tlv_idx * E::word_size;
}
template <typename E>
inline Symbol<E> *get_symbol(Context<E> &ctx, std::string_view name) {
typename decltype(ctx.symbol_map)::const_accessor acc;
ctx.symbol_map.insert(acc, {name, Symbol<E>(name)});
return (Symbol<E> *)(&acc->second);
}
template <typename E>
inline std::ostream &operator<<(std::ostream &out, const Symbol<E> &sym) {
out << sym.name;
return out;
}
template <typename E>
Chunk<E>::Chunk(Context<E> &ctx, std::string_view segname,
std::string_view sectname) {
ctx.chunks.push_back(this);
hdr.set_segname(segname);
hdr.set_sectname(sectname);
}
template <typename E>
u64 UnwindRecord<E>::get_func_raddr(Context<E> &ctx) const {
return subsec->raddr + offset;
}
} // namespace mold::macho
-622
View File
@@ -1,622 +0,0 @@
#include "mold.h"
#include "../archive-file.h"
namespace mold::macho {
template <typename E>
std::ostream &operator<<(std::ostream &out, const InputFile<E> &file) {
if (file.archive_name.empty())
out << path_clean(file.mf->name);
else
out << path_clean(file.archive_name) << "(" << path_clean(file.mf->name) + ")";
return out;
}
template <typename E>
ObjectFile<E> *
ObjectFile<E>::create(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::string archive_name) {
ObjectFile<E> *obj = new ObjectFile<E>;
obj->mf = mf;
obj->archive_name = archive_name;
obj->is_alive = archive_name.empty();
ctx.obj_pool.emplace_back(obj);
return obj;
};
template <typename E>
void ObjectFile<E>::parse(Context<E> &ctx) {
parse_sections(ctx);
parse_symtab(ctx);
split_subsections(ctx);
parse_data_in_code(ctx);
for (std::unique_ptr<InputSection<E>> &isec : sections)
if (isec)
isec->parse_relocations(ctx);
if (unwind_sec)
parse_compact_unwind(ctx, *unwind_sec);
}
template <typename E>
void ObjectFile<E>::parse_sections(Context<E> &ctx) {
MachHeader &hdr = *(MachHeader *)this->mf->data;
u8 *p = this->mf->data + sizeof(hdr);
// Read all but a symtab section
for (i64 i = 0; i < hdr.ncmds; i++) {
LoadCommand &lc = *(LoadCommand *)p;
p += lc.cmdsize;
if (lc.cmd != LC_SEGMENT_64)
continue;
SegmentCommand &cmd = *(SegmentCommand *)&lc;
MachSection *mach_sec = (MachSection *)((u8 *)&lc + sizeof(cmd));
for (MachSection &msec : std::span(mach_sec, mach_sec + cmd.nsects)) {
sections.push_back(nullptr);
if (msec.match("__LD", "__compact_unwind")) {
unwind_sec = &msec;
continue;
}
if (msec.attr & S_ATTR_DEBUG)
continue;
sections.back().reset(new InputSection<E>(ctx, *this, msec));
}
}
}
template <typename E>
void ObjectFile<E>::parse_symtab(Context<E> &ctx) {
SymtabCommand *cmd = (SymtabCommand *)find_load_command(ctx, LC_SYMTAB);
if (!cmd)
return;
mach_syms = {(MachSym *)(this->mf->data + cmd->symoff), cmd->nsyms};
this->syms.reserve(mach_syms.size());
i64 nlocal = 0;
for (MachSym &msym : mach_syms)
if (!msym.ext)
nlocal++;
local_syms.reserve(nlocal);
for (MachSym &msym : mach_syms) {
std::string_view name = (char *)(this->mf->data + cmd->stroff + msym.stroff);
if (msym.ext) {
this->syms.push_back(get_symbol(ctx, name));
} else {
local_syms.emplace_back(name);
this->syms.push_back(&local_syms.back());
}
}
}
struct SplitRegion {
u32 offset;
u32 size;
u32 symidx;
bool is_alt_entry;
};
template <typename E>
struct SplitInfo {
SplitInfo(InputSection<E> *isec) : isec(isec) {}
InputSection<E> *isec;
std::vector<SplitRegion> regions;
};
template <typename E>
static std::vector<SplitInfo<E>> split(Context<E> &ctx, ObjectFile<E> &file) {
std::vector<SplitInfo<E>> vec;
for (std::unique_ptr<InputSection<E>> &isec : file.sections)
vec.push_back({isec.get()});
for (i64 i = 0; i < file.mach_syms.size(); i++) {
MachSym &msym = file.mach_syms[i];
if (msym.type == N_SECT && file.sections[msym.sect - 1]) {
SplitRegion r;
r.offset = msym.value - file.sections[msym.sect - 1]->hdr.addr;
r.symidx = i;
r.is_alt_entry = (msym.desc & N_ALT_ENTRY);
vec[msym.sect - 1].regions.push_back(r);
}
}
std::erase_if(vec, [](const SplitInfo<E> &info) { return !info.isec; });
sort(vec, [](const SplitInfo<E> &a, const SplitInfo<E> &b) {
return a.isec->hdr.addr < b.isec->hdr.addr;
});
for (SplitInfo<E> &info : vec) {
std::vector<SplitRegion> &r = info.regions;
if (r.empty()) {
r.push_back({0, (u32)info.isec->hdr.size, (u32)-1, false});
continue;
}
sort(r, [](const SplitRegion &a, const SplitRegion &b) {
return a.offset < b.offset;
});
if (r[0].offset > 0)
r.insert(r.begin(), {0, r[0].offset, (u32)-1, false});
for (i64 i = 1; i < r.size(); i++)
if (r[i - 1].offset == r[i].offset)
r[i++].is_alt_entry = true;
i64 last = -1;
for (i64 i = 0; i < r.size(); i++) {
if (!r[i].is_alt_entry) {
if (last != -1)
r[last].size = r[i].offset - r[last].offset;
last = i;
}
}
if (last != -1)
r[last].size = info.isec->hdr.size - r[last].offset;
}
return vec;
}
template <typename E>
void ObjectFile<E>::split_subsections(Context<E> &ctx) {
sym_to_subsec.resize(mach_syms.size());
for (SplitInfo<E> &info : split(ctx, *this)) {
InputSection<E> &isec = *info.isec;
for (SplitRegion &r : info.regions) {
if (!r.is_alt_entry) {
Subsection<E> *subsec = new Subsection<E>{
.isec = isec,
.input_offset = r.offset,
.input_size = r.size,
.input_addr = (u32)(isec.hdr.addr + r.offset),
.p2align = (u8)isec.hdr.p2align,
};
subsections.emplace_back(subsec);
}
if (r.symidx != -1)
sym_to_subsec[r.symidx] = subsections.size() - 1;
}
}
for (i64 i = 0; i < mach_syms.size(); i++)
if (!mach_syms[i].ext)
override_symbol(ctx, i);
}
template <typename E>
void ObjectFile<E>::parse_data_in_code(Context<E> &ctx) {
if (auto *cmd = (LinkEditDataCommand *)find_load_command(ctx, LC_DATA_IN_CODE))
data_in_code_entries = {
(DataInCodeEntry *)(this->mf->data + cmd->dataoff),
cmd->datasize / sizeof(DataInCodeEntry)
};
}
template <typename E>
LoadCommand *ObjectFile<E>::find_load_command(Context<E> &ctx, u32 type) {
MachHeader &hdr = *(MachHeader *)this->mf->data;
u8 *p = this->mf->data + sizeof(hdr);
for (i64 i = 0; i < hdr.ncmds; i++) {
LoadCommand &lc = *(LoadCommand *)p;
if (lc.cmd == type)
return &lc;
p += lc.cmdsize;
}
return nullptr;
}
template <typename E>
i64 ObjectFile<E>::find_subsection_idx(Context<E> &ctx, u32 addr) {
auto it = std::upper_bound(
subsections.begin(), subsections.end(), addr,
[&](u32 addr, const std::unique_ptr<Subsection<E>> &subsec) {
return addr < subsec->input_addr;
});
if (it == subsections.begin())
return -1;
return it - subsections.begin() - 1;
}
template <typename E>
Subsection<E> *ObjectFile<E>::find_subsection(Context<E> &ctx, u32 addr) {
i64 i = find_subsection_idx(ctx, addr);
return (i == -1) ? nullptr : subsections[i].get();
}
template <typename E>
void ObjectFile<E>::parse_compact_unwind(Context<E> &ctx, MachSection &hdr) {
if (hdr.size % sizeof(CompactUnwindEntry))
Fatal(ctx) << *this << ": invalid __compact_unwind section size";
i64 num_entries = hdr.size / sizeof(CompactUnwindEntry);
unwind_records.reserve(num_entries);
CompactUnwindEntry *src = (CompactUnwindEntry *)(this->mf->data + hdr.offset);
// Read compact unwind entries
for (i64 i = 0; i < num_entries; i++)
unwind_records.emplace_back(src[i].code_len, src[i].encoding);
// Read relocations
MachRel *mach_rels = (MachRel *)(this->mf->data + hdr.reloff);
for (i64 i = 0; i < hdr.nreloc; i++) {
MachRel &r = mach_rels[i];
if (r.offset >= hdr.size)
Fatal(ctx) << *this << ": relocation offset too large: " << i;
i64 idx = r.offset / sizeof(CompactUnwindEntry);
UnwindRecord<E> &dst = unwind_records[idx];
auto error = [&] {
Fatal(ctx) << *this << ": __compact_unwind: unsupported relocation: " << i;
};
switch (r.offset % sizeof(CompactUnwindEntry)) {
case offsetof(CompactUnwindEntry, code_start): {
if (r.is_pcrel || r.p2size != 3 || r.is_extern || r.type)
error();
Subsection<E> *target = find_subsection(ctx, src[idx].code_start);
if (!target)
error();
dst.subsec = target;
dst.offset = src[idx].code_start - target->input_addr;
break;
}
case offsetof(CompactUnwindEntry, personality):
if (r.is_pcrel || r.p2size != 3 || !r.is_extern || r.type)
error();
dst.personality = this->syms[r.idx];
break;
case offsetof(CompactUnwindEntry, lsda): {
if (r.is_pcrel || r.p2size != 3 || r.is_extern || r.type)
error();
i32 addr = *(i32 *)((u8 *)this->mf->data + hdr.offset + r.offset);
Subsection<E> *target = find_subsection(ctx, addr);
if (!target)
error();
dst.lsda = target;
dst.lsda_offset = addr - target->input_addr;
break;
}
default:
Fatal(ctx) << *this << ": __compact_unwind: unsupported relocation: " << i;
}
}
for (i64 i = 0; i < num_entries; i++)
if (!unwind_records[i].subsec)
Fatal(ctx) << ": __compact_unwind: missing relocation at " << i;
// Sort unwind entries by offset
sort(unwind_records, [](const UnwindRecord<E> &a, const UnwindRecord<E> &b) {
return std::tuple(a.subsec->input_addr, a.offset) <
std::tuple(b.subsec->input_addr, b.offset);
});
// Associate unwind entries to subsections
for (i64 i = 0; i < num_entries;) {
Subsection<E> &subsec = *unwind_records[i].subsec;
subsec.unwind_offset = i;
i64 j = i + 1;
while (j < num_entries && unwind_records[j].subsec == &subsec)
j++;
subsec.nunwind = j - i;
i = j;
}
}
// Symbols with higher priorities overwrites symbols with lower priorities.
// Here is the list of priorities, from the highest to the lowest.
//
// 1. Strong defined symbol
// 2. Weak defined symbol
// 3. Strong defined symbol in a DSO
// 4. Weak defined symbol in a DSO
// 5. Strong or weak defined symbol in an archive
// 6. Common symbol
// 7. Unclaimed (nonexistent) symbol
//
// Ties are broken by file priority.
template <typename E>
static u64 get_rank(InputFile<E> *file, MachSym &msym, bool is_lazy) {
if (msym.is_common())
return (6 << 24) + file->priority;
if (is_lazy)
return (5 << 24) + file->priority;
if (file->is_dylib())
return (3 << 24) + file->priority;
return (1 << 24) + file->priority;
}
template <typename E>
static u64 get_rank(Symbol<E> &sym) {
InputFile<E> *file = sym.file;
if (!file)
return 7 << 24;
if (sym.is_common)
return (6 << 24) + file->priority;
if (!file->archive_name.empty())
return (5 << 24) + file->priority;
if (file->is_dylib())
return (3 << 24) + file->priority;
return (1 << 24) + file->priority;
}
template <typename E>
void ObjectFile<E>::override_symbol(Context<E> &ctx, i64 idx) {
Symbol<E> &sym = *this->syms[idx];
MachSym &msym = mach_syms[idx];
sym.file = this;
sym.is_extern = msym.ext;
sym.is_lazy = false;
switch (msym.type) {
case N_UNDF:
assert(msym.is_common());
sym.subsec = nullptr;
sym.value = msym.value;
sym.is_common = true;
break;
case N_ABS:
sym.subsec = nullptr;
sym.value = msym.value;
sym.is_common = false;
break;
case N_SECT:
sym.subsec = subsections[sym_to_subsec[idx]].get();
sym.value = msym.value - sym.subsec->input_addr;
sym.is_common = false;
break;
default:
Fatal(ctx) << sym << ": unknown symbol type: " << (u64)msym.type;
}
}
template <typename E>
void ObjectFile<E>::resolve_regular_symbols(Context<E> &ctx) {
for (i64 i = 0; i < this->syms.size(); i++) {
MachSym &msym = mach_syms[i];
if (!msym.ext || msym.is_undef())
continue;
Symbol<E> &sym = *this->syms[i];
std::scoped_lock lock(sym.mu);
if (get_rank(this, msym, false) < get_rank(sym))
override_symbol(ctx, i);
}
}
template <typename E>
void ObjectFile<E>::resolve_lazy_symbols(Context<E> &ctx) {
for (i64 i = 0; i < this->syms.size(); i++) {
MachSym &msym = mach_syms[i];
if (!msym.ext || msym.is_undef() || msym.is_common())
continue;
Symbol<E> &sym = *this->syms[i];
std::scoped_lock lock(sym.mu);
if (get_rank(this, msym, true) < get_rank(sym)) {
sym.file = this;
sym.subsec = nullptr;
sym.value = 0;
sym.is_extern = false;
sym.is_lazy = true;
sym.is_common = false;
}
}
}
template <typename E>
bool ObjectFile<E>::is_objc_object(Context<E> &ctx) {
for (std::unique_ptr<InputSection<E>> &isec : sections)
if (isec->hdr.match("__DATA", "__objc_catlist") ||
isec->hdr.match("__TEXT", "__swift"))
return true;
for (i64 i = 0; i < this->syms.size(); i++)
if (!mach_syms[i].is_undef() && mach_syms[i].ext &&
this->syms[i]->name.starts_with("_OBJC_CLASS_$_"))
return true;
return false;
}
template <typename E>
std::vector<ObjectFile<E> *> ObjectFile<E>::mark_live_objects(Context<E> &ctx) {
std::vector<ObjectFile<E> *> vec;
assert(this->is_alive);
for (i64 i = 0; i < this->syms.size(); i++) {
MachSym &msym = mach_syms[i];
if (!msym.ext)
continue;
Symbol<E> &sym = *this->syms[i];
std::scoped_lock lock(sym.mu);
if (msym.is_undef()) {
if (sym.file && !sym.file->is_alive.exchange(true))
vec.push_back((ObjectFile<E> *)sym.file);
continue;
}
if (get_rank(this, msym, false) < get_rank(sym))
override_symbol(ctx, i);
}
return vec;
}
template <typename E>
void ObjectFile<E>::convert_common_symbols(Context<E> &ctx) {
for (i64 i = 0; i < this->syms.size(); i++) {
Symbol<E> &sym = *this->syms[i];
MachSym &msym = mach_syms[i];
if (sym.file == this && sym.is_common) {
InputSection<E> *isec = get_common_sec(ctx);
Subsection<E> *subsec = new Subsection<E>{
.isec = *isec,
.input_size = (u32)msym.value,
.p2align = (u8)msym.p2align,
};
subsections.emplace_back(subsec);
sym.subsec = subsec;
sym.value = 0;
sym.is_common = false;
}
}
}
template <typename E>
void ObjectFile<E>::check_duplicate_symbols(Context<E> &ctx) {
for (i64 i = 0; i < this->syms.size(); i++) {
Symbol<E> *sym = this->syms[i];
MachSym &msym = mach_syms[i];
if (sym && !msym.is_undef() && !msym.is_common() && sym->file != this)
Error(ctx) << "duplicate symbol: " << *this << ": " << *sym->file
<< ": " << *sym;
}
}
template <typename E>
InputSection<E> *ObjectFile<E>::get_common_sec(Context<E> &ctx) {
if (!common_sec) {
MachSection *hdr = new MachSection;
common_hdr.reset(hdr);
memset(hdr, 0, sizeof(*hdr));
hdr->set_segname("__DATA");
hdr->set_sectname("__common");
hdr->type = S_ZEROFILL;
common_sec = new InputSection<E>(ctx, *this, *hdr);
sections.emplace_back(common_sec);
}
return common_sec;
}
template <typename E>
DylibFile<E> *DylibFile<E>::create(Context<E> &ctx, MappedFile<Context<E>> *mf) {
DylibFile<E> *dylib = new DylibFile<E>;
dylib->mf = mf;
ctx.dylib_pool.emplace_back(dylib);
return dylib;
};
template <typename E>
void DylibFile<E>::read_trie(Context<E> &ctx, u8 *start, i64 offset,
const std::string &prefix) {
u8 *buf = start + offset;
if (*buf) {
read_uleb(buf); // size
read_uleb(buf); // flags
read_uleb(buf); // addr
this->syms.push_back(get_symbol(ctx, prefix));
} else {
buf++;
}
i64 nchild = *buf++;
for (i64 i = 0; i < nchild; i++) {
std::string suffix((char *)buf);
buf += suffix.size() + 1;
i64 off = read_uleb(buf);
read_trie(ctx, start, off, prefix + suffix);
}
}
template <typename E>
void DylibFile<E>::parse_dylib(Context<E> &ctx) {
MachHeader &hdr = *(MachHeader *)this->mf->data;
u8 *p = this->mf->data + sizeof(hdr);
for (i64 i = 0; i < hdr.ncmds; i++) {
LoadCommand &lc = *(LoadCommand *)p;
switch (lc.cmd) {
case LC_ID_DYLIB: {
DylibCommand &cmd = *(DylibCommand *)p;
install_name = (char *)p + cmd.nameoff;
break;
}
case LC_DYLD_INFO_ONLY: {
DyldInfoCommand &cmd = *(DyldInfoCommand *)p;
if (cmd.export_off)
read_trie(ctx, this->mf->data + cmd.export_off);
break;
}
case LC_DYLD_EXPORTS_TRIE: {
LinkEditDataCommand &cmd = *(LinkEditDataCommand *)p;
read_trie(ctx, this->mf->data + cmd.dataoff);
break;
}
}
p += lc.cmdsize;
}
}
template <typename E>
void DylibFile<E>::parse(Context<E> &ctx) {
switch (get_file_type(this->mf)) {
case FileType::TAPI: {
TextDylib tbd = parse_tbd(ctx, this->mf);
for (std::string_view sym : tbd.exports)
this->syms.push_back(get_symbol(ctx, sym));
install_name = tbd.install_name;
break;
}
case FileType::MACH_DYLIB:
parse_dylib(ctx);
break;
default:
Fatal(ctx) << this->mf->name << ": is not a dylib";
}
}
template <typename E>
void DylibFile<E>::resolve_symbols(Context<E> &ctx) {
for (Symbol<E> *sym : this->syms) {
std::scoped_lock lock(sym->mu);
if (sym->file && sym->file->priority < this->priority)
continue;
sym->file = this;
sym->is_extern = true;
}
}
#define INSTANTIATE(E) \
template class ObjectFile<E>; \
template class DylibFile<E>; \
template std::ostream &operator<<(std::ostream &, const InputFile<E> &)
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
} // namespace mold::macho
File diff suppressed because it is too large. Load diff
-120
View File
@@ -1,120 +0,0 @@
// This file implements the same functionality as elf/output-file.cc.
// We have two different implemetntations for ELF and Mach-O because
// we implemented a special optimization for ELF or Linux.
//
// On Linux, it is faster to write to an existing file than creating a
// new file and writing to it. So, we overwrite an existing executable
// if exists.
//
// On macOS, it looks like the system does not assume that an executable
// is mutated once it is created. Due to some code signing mechanism or
// something, we always have to create a fresh file.
#include "mold.h"
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
namespace mold::macho {
template <typename E>
class MemoryMappedOutputFile : public OutputFile<E> {
public:
MemoryMappedOutputFile(Context<E> &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<E>(path, filesize, true) {
std::string tmp_path = filepath(path).parent_path() / ".mold-XXXXXX";
output_tmpfile = (char *)save_string(ctx, tmp_path).data();
i64 fd = mkstemp(output_tmpfile);
if (fd == -1)
Fatal(ctx) << "cannot open " << output_tmpfile << ": " << errno_string();
if (ftruncate(fd, filesize))
Fatal(ctx) << "ftruncate failed: " << errno_string();
if (fchmod(fd, perm) == -1)
Fatal(ctx) << "fchmod failed: " << errno_string();
this->buf = (u8 *)mmap(nullptr, filesize, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (this->buf == MAP_FAILED)
Fatal(ctx) << path << ": mmap failed: " << errno_string();
::close(fd);
}
void close(Context<E> &ctx) override {
Timer t(ctx, "close_file");
munmap(this->buf, this->filesize);
if (rename(output_tmpfile, this->path.c_str()) == -1)
Fatal(ctx) << this->path << ": rename failed: " << errno_string();
output_tmpfile = nullptr;
}
};
template <typename E>
class MallocOutputFile : public OutputFile<E> {
public:
MallocOutputFile(Context<E> &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<E>(path, filesize, false), perm(perm) {
this->buf = (u8 *)mmap(NULL, filesize, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (this->buf == MAP_FAILED)
Fatal(ctx) << "mmap failed: " << errno_string();
}
void close(Context<E> &ctx) override {
Timer t(ctx, "close_file");
if (this->path == "-") {
fwrite(this->buf, this->filesize, 1, stdout);
fclose(stdout);
return;
}
i64 fd = ::open(this->path.c_str(), O_RDWR | O_CREAT, perm);
if (fd == -1)
Fatal(ctx) << "cannot open " << this->path << ": " << errno_string();
FILE *fp = fdopen(fd, "w");
fwrite(this->buf, this->filesize, 1, fp);
fclose(fp);
}
private:
i64 perm;
};
template <typename E>
std::unique_ptr<OutputFile<E>>
OutputFile<E>::open(Context<E> &ctx, std::string path, i64 filesize, i64 perm) {
Timer t(ctx, "open_file");
if (path.starts_with('/') && !ctx.arg.chroot.empty())
path = ctx.arg.chroot + "/" + path_clean(path);
bool is_special = false;
if (path == "-") {
is_special = true;
} else {
struct stat st;
if (stat(path.c_str(), &st) == 0 && (st.st_mode & S_IFMT) != S_IFREG)
is_special = true;
}
std::unique_ptr<OutputFile> file;
if (is_special)
file = std::make_unique<MallocOutputFile<E>>(ctx, path, filesize, perm);
else
file = std::make_unique<MemoryMappedOutputFile<E>>(ctx, path, filesize, perm);
return file;
}
#define INSTANTIATE(E) \
template class OutputFile<E>
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
} // namespace mold::macho
-127
View File
@@ -1,127 +0,0 @@
#include "mold.h"
#include <optional>
namespace mold::macho {
static std::vector<YamlNode>
get_vector(YamlNode &node, std::string_view key) {
if (auto *map = std::get_if<std::map<std::string_view, YamlNode>>(&node.data))
if (auto it = map->find(key); it != map->end())
if (auto *vec = std::get_if<std::vector<YamlNode>>(&it->second.data))
return *vec;
return {};
}
static std::optional<std::string_view>
get_string(YamlNode &node, std::string_view key) {
if (auto *map = std::get_if<std::map<std::string_view, YamlNode>>(&node.data))
if (auto it = map->find(key); it != map->end())
if (auto *str = std::get_if<std::string_view>(&it->second.data))
return *str;
return {};
}
static bool contains(const std::vector<YamlNode> &vec, std::string_view key) {
for (const YamlNode &mem : vec)
if (const std::string_view *s = std::get_if<std::string_view>(&mem.data))
if (*s == key)
return true;
return false;
}
static std::optional<TextDylib> to_tbd(YamlNode &node, std::string_view arch) {
if (!contains(get_vector(node, "targets"), arch))
return {};
TextDylib tbd;
for (YamlNode &mem : get_vector(node, "uuids"))
if (auto target = get_string(mem, "target"))
if (*target == arch)
if (auto value = get_string(mem, "value"))
tbd.uuid = *value;
if (auto val = get_string(node, "install-name"))
tbd.install_name = *val;
if (auto val = get_string(node, "current-version"))
tbd.current_version = *val;
for (YamlNode &mem : get_vector(node, "parent-umbrella"))
if (contains(get_vector(mem, "targets"), arch))
if (auto val = get_string(mem, "umbrella"))
tbd.parent_umbrella = *val;
for (YamlNode &mem : get_vector(node, "reexported-libraries"))
if (contains(get_vector(mem, "targets"), arch))
for (YamlNode &mem : get_vector(mem, "libraries"))
if (auto *lib = std::get_if<std::string_view>(&mem.data))
tbd.reexported_libs.push_back(*lib);
for (std::string_view key : {"exports", "reexports"})
for (YamlNode &mem : get_vector(node, key))
if (contains(get_vector(mem, "targets"), arch))
for (YamlNode &mem : get_vector(mem, "symbols"))
if (auto *sym = std::get_if<std::string_view>(&mem.data))
tbd.exports.push_back(*sym);
return tbd;
}
template <typename E>
static TextDylib squash(Context<E> &ctx, std::span<TextDylib> tbds) {
std::unordered_map<std::string_view, TextDylib> map;
TextDylib main = std::move(tbds[0]);
for (TextDylib &tbd : tbds.subspan(1))
map[tbd.install_name] = std::move(tbd);
std::vector<std::string_view> libs;
for (std::string_view lib : main.reexported_libs) {
auto it = map.find(lib);
if (it != map.end())
append(main.exports, it->second.exports);
else
libs.push_back(lib);
}
main.reexported_libs = std::move(libs);
return main;
}
template <typename E>
static TextDylib parse(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::string_view arch) {
std::string_view contents = mf->get_contents();
std::variant<std::vector<YamlNode>, YamlError> res = parse_yaml(contents);
if (YamlError *err = std::get_if<YamlError>(&res)) {
i64 lineno = std::count(contents.begin(), contents.begin() + err->pos, '\n');
Fatal(ctx) << mf->name << ":" << (lineno + 1)
<< ": YAML parse error: " << err->msg;
}
std::vector<YamlNode> &nodes = std::get<std::vector<YamlNode>>(res);
if (nodes.empty())
Fatal(ctx) << mf->name << ": malformed TBD file";
std::vector<TextDylib> vec;
for (YamlNode &node : nodes)
if (std::optional<TextDylib> dylib = to_tbd(node, arch))
vec.push_back(*dylib);
return squash(ctx, vec);
}
template <>
TextDylib parse_tbd(Context<ARM64> &ctx, MappedFile<Context<ARM64>> *mf) {
return parse(ctx, mf, "arm64-macos");
}
template <>
TextDylib parse_tbd(Context<X86_64> &ctx, MappedFile<Context<X86_64>> *mf) {
return parse(ctx, mf, "x86_64-macos");
}
} // namespace mold::macho
-360
View File
@@ -1,360 +0,0 @@
#include "mold.h"
#include <optional>
namespace mold::macho {
struct Token {
enum { STRING = 1, INDENT, DEDENT, END };
u8 kind = 0;
std::string_view str;
};
class YamlParser {
public:
YamlParser(std::string_view input) : input(input) {}
std::variant<std::vector<YamlNode>, YamlError> parse();
private:
std::optional<YamlError> tokenize();
void tokenize_bare_string(std::string_view &str);
std::optional<YamlError> tokenize_list(std::string_view &str);
std::optional<YamlError> tokenize_string(std::string_view &str, char end);
std::variant<YamlNode, YamlError> parse_element(std::span<Token> &tok);
std::variant<YamlNode, YamlError> parse_list(std::span<Token> &tok);
std::variant<YamlNode, YamlError> parse_map(std::span<Token> &tok);
std::variant<YamlNode, YamlError> parse_flow_element(std::span<Token> &tok);
std::variant<YamlNode, YamlError> parse_flow_list(std::span<Token> &tok);
std::string_view input;
std::vector<Token> tokens;
};
std::optional<YamlError> YamlParser::tokenize() {
std::vector<i64> indents = {0};
auto indent = [&](std::string_view str, i64 depth) {
tokens.push_back({Token::INDENT, str});
indents.push_back(depth);
};
auto dedent = [&](std::string_view str) {
assert(indents.size() > 1);
tokens.push_back({Token::DEDENT, str});
indents.pop_back();
};
auto skip_line = [](std::string_view &str) {
size_t pos = str.find('\n');
if (pos == str.npos)
str = str.substr(str.size());
else
str = str.substr(pos + 1);
};
auto tokenize_line = [&](std::string_view &str) -> std::optional<YamlError> {
const char *start = str.data();
if (str.starts_with("---")) {
while (indents.size() > 1)
dedent(str);
tokens.push_back({Token::END, str.substr(0, 3)});
skip_line(str);
return {};
}
if (str.starts_with("...")) {
while (indents.size() > 1)
dedent(str);
tokens.push_back({Token::END, str.substr(0, 3)});
str = str.substr(str.size());
return {};
}
size_t pos = str.find_first_not_of(" \t");
if (pos == str.npos || str[pos] == '#' || str[pos] == '\n') {
skip_line(str);
return {};
}
if (indents.back() != pos) {
if (indents.back() < pos) {
indent(str, pos);
} else {
while (indents.back() != pos) {
if (pos < indents.back())
dedent(str);
else
return YamlError{"bad indentation", start - input.data()};
}
}
}
str = str.substr(pos);
while (!str.empty()) {
if (str[0] == '\n') {
str = str.substr(1);
return {};
}
if (str.starts_with("- ")) {
tokens.push_back({'-', str.substr(0, 1)});
size_t pos = str.find_first_not_of(" \t", 1);
if (pos == str.npos || str[pos] == '\n') {
skip_line(str);
return {};
}
str = str.substr(pos);
indent(str, str.data() - start);
continue;
}
if (str.starts_with('['))
return tokenize_list(str);
if (str.starts_with('\'')) {
if (std::optional<YamlError> err = tokenize_string(str, '\''))
return err;
continue;
}
if (str.starts_with('"')) {
if (std::optional<YamlError> err = tokenize_string(str, '"'))
return err;
continue;
}
if (str.starts_with('#')) {
skip_line(str);
return {};
}
if (str.starts_with(':')) {
tokens.push_back({':', str.substr(0, 1)});
size_t pos = str.find_first_not_of(" \t", 1);
if (pos == str.npos || str[pos] == '\n') {
skip_line(str);
return {};
}
str = str.substr(pos);
continue;
}
tokenize_bare_string(str);
}
return {};
};
std::string_view str = input;
while (!str.empty())
if (std::optional<YamlError> err = tokenize_line(str))
return err;
return {};
}
std::optional<YamlError> YamlParser::tokenize_list(std::string_view &str) {
const char *start = str.data();
tokens.push_back({'[', str.substr(0, 1)});
str = str.substr(1);
while (!str.empty() && str[0] != ']') {
if (size_t pos = str.find_first_not_of(" \t\n"); pos) {
str = str.substr(pos);
continue;
}
if (str.starts_with('\'')) {
if (std::optional<YamlError> err = tokenize_string(str, '\''))
return err;
continue;
}
if (str.starts_with('"')) {
if (std::optional<YamlError> err = tokenize_string(str, '"'))
return err;
continue;
}
if (str.starts_with(',')) {
tokens.push_back({',', str.substr(0, 1)});
str = str.substr(1);
continue;
}
tokenize_bare_string(str);
}
if (str.empty())
return YamlError{"unclosed list", start - input.data()};
const char *bracket = str.data();
tokens.push_back({']', str.substr(0, 1)});
str = str.substr(1);
while (!str.empty() && (str[0] == ' ' || str[0] == '\t'))
str = str.substr(1);
if (str.empty() || str[0] != '\n')
return YamlError{"no newline after ']'", bracket - input.data()};
str = str.substr(1);
return {};
}
std::optional<YamlError>
YamlParser::tokenize_string(std::string_view &str, char end) {
const char *start = str.data();
size_t pos = str.find(end, 1);
if (pos == str.npos)
return YamlError{"unterminated string literal", start - input.data()};
tokens.push_back({Token::STRING, str.substr(1, pos - 1)});
str = str.substr(pos + 1);
return {};
}
void
YamlParser::tokenize_bare_string(std::string_view &str) {
size_t pos = str.find_first_not_of(
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-/.");
if (pos == str.npos)
pos = str.size();
tokens.push_back({Token::STRING, str.substr(0, pos)});
str = str.substr(pos);
}
std::variant<std::vector<YamlNode>, YamlError> YamlParser::parse() {
if (std::optional<YamlError> err = tokenize())
return *err;
std::span<Token> tok(tokens);
std::vector<YamlNode> vec;
while (!tok.empty()) {
if (tok[0].kind == Token::END) {
tok = tok.subspan(1);
continue;
}
std::variant<YamlNode, YamlError> elem = parse_element(tok);
if (YamlError *err = std::get_if<YamlError>(&elem))
return *err;
vec.push_back(std::get<YamlNode>(elem));
if (tok[0].kind != Token::END)
return YamlError{"stray token", tok[0].str.data() - input.data()};
}
return vec;
}
std::variant<YamlNode, YamlError>
YamlParser::parse_element(std::span<Token> &tok) {
if (tok[0].kind == Token::INDENT) {
tok = tok.subspan(1);
std::variant<YamlNode, YamlError> elem = parse_element(tok);
assert(tok[0].kind == Token::DEDENT);
tok = tok.subspan(1);
return elem;
}
if (tok[0].kind == '-')
return parse_list(tok);
if (tok.size() > 2 && tok[0].kind == Token::STRING && tok[1].kind == ':')
return parse_map(tok);
return parse_flow_element(tok);
}
std::variant<YamlNode, YamlError>
YamlParser::parse_list(std::span<Token> &tok) {
std::vector<YamlNode> vec;
while (tok[0].kind != Token::END && tok[0].kind != Token::DEDENT) {
if (tok[0].kind != '-')
return YamlError{"list element expected", tok[0].str.data() - input.data()};
tok = tok.subspan(1);
std::variant<YamlNode, YamlError> elem = parse_element(tok);
if (YamlError *err = std::get_if<YamlError>(&elem))
return *err;
vec.push_back(std::get<YamlNode>(elem));
}
return YamlNode{vec};
}
std::variant<YamlNode, YamlError>
YamlParser::parse_map(std::span<Token> &tok) {
std::map<std::string_view, YamlNode> map;
while (tok[0].kind != Token::END && tok[0].kind != Token::DEDENT) {
if (tok.size() < 2 || tok[0].kind != Token::STRING || tok[1].kind != ':')
return YamlError{"map key expected", tok[0].str.data() - input.data()};
std::string_view key = tok[0].str;
tok = tok.subspan(2);
std::variant<YamlNode, YamlError> elem = parse_element(tok);
if (YamlError *err = std::get_if<YamlError>(&elem))
return *err;
map[key] = std::get<YamlNode>(elem);
}
return YamlNode{map};
}
std::variant<YamlNode, YamlError>
YamlParser::parse_flow_element(std::span<Token> &tok) {
if (tok[0].kind == '[') {
tok = tok.subspan(1);
return parse_flow_list(tok);
}
if (tok[0].kind != Token::STRING)
return YamlError{"scalar expected", tok[0].str.data() - input.data()};
std::string_view val = tok[0].str;
tok = tok.subspan(1);
return YamlNode{val};
}
std::variant<YamlNode, YamlError>
YamlParser::parse_flow_list(std::span<Token> &tok) {
std::vector<YamlNode> vec;
const char *start = tok[0].str.data();
while (tok[0].kind != ']' && tok[0].kind != Token::END) {
std::variant<YamlNode, YamlError> elem = parse_flow_element(tok);
if (YamlError *err = std::get_if<YamlError>(&elem))
return *err;
vec.push_back(std::get<YamlNode>(elem));
if (tok[0].kind == ']')
break;
if (tok[0].kind != ',')
return YamlError{"comma expected", tok[0].str.data() - input.data()};
tok = tok.subspan(1);
}
if (tok[0].kind == Token::END)
return YamlError{"unterminated flow list", start - input.data()};
tok = tok.subspan(1);
return YamlNode{vec};
}
std::variant<std::vector<YamlNode>, YamlError>
parse_yaml(std::string_view str) {
return YamlParser(str).parse();
}
} // namespace mold::macho
Loaded 100 of 2160 files, more files were not shown because too many files have changed in this diff. Show more