Compare commits

...
327 Commits
Author SHA1 Message Date
Rui Ueyama edc2f852a1 Bump mold version to 2.3.2 2023-11-04 15:44:32 +09:00
Rui Ueyama 348e44a9c4 Reorganize tests 2023-11-04 14:43:47 +09:00
Rui Ueyama 42f663cac0 Silence TSAN errors that I believe false 2023-11-04 09:07:28 +09:00
Rui Ueyama 4dd5d2f8cb Propagate mergeable sections' sh_entsize and sh_addralign attributes
For --relocatable output, we need to pass through these attributes
for correctness. With this change, we'll have more .rodata sections
such as .rodata.str1.1, rodata.str1.4, etc. in the final executable,
but that should be fine because the runtime doesn't care about
section tables at all. The table isn't even loaded to memory at runtime.
2023-11-03 21:34:17 +09:00
Rui Ueyama 4cdfc7e72c Do not emit text relocations for IFUNC symbols in PDEs
IFUNC symbols are resolved at process startup by executing the function
that the symbol points to. This is used to select the "best" function at
runtime; for instance, the runtime may choose a faster version of memcpy
that uses SIMD instructions if they are available on the current system.

Thus, an IFUNC symbol has two addresses: the initial address (or the
resolver's address) and the resolved address, which is the return value of
the resolver.

In position-independent executables (PIEs), function pointers are loaded
from the GOT indirectly, and symbols are not directly referenced. In such
executables, the initial value of the GOT slot for an IFUNC symbol
contains the resolver address, and this is overwritten at runtime to the
resolved address upon process startup. When user code takes a pointer to
an IFUNC, it always reads the resolved address from GOT.

In contrast, position-dependent executables (PDEs) may have instructions
that directly refer to an IFUNC symbol, such as movabs on x86-64. The GOT
entry for an IFUNC holds the resolved address, so any direct reference
must also produce the resolved address to maintain pointer equality.
(C/C++ standards require that two pointers must be equal if and only if
they are taken for the same symbol.)

Previously, we emitted text relocations to modify instruction operands.
However, text relocations are undesirable and not always reliable. For
example, on ARM64, multiple instructions are used to materialize a
symbol's address, and it's not feasible to issue a dynamic relocation to
alter those instructions since the dynamic loader generally can only
modify 32-bit or 64-bit words.

In this commit, I have adopted a different strategy. An IFUNC symbol now
occupies two consecutive GOT slots in a PDE. The first slot holds the
symbol's PLT address, and the second slot holds the resolved address. The
PLT address is consistently used as the symbol's address throughout the
process, while the second slot is used only by the PLT entry to jump to
the resolved address.

This method ensures pointer equality without the need to emit text
relocations for IFUNC symbols in PDEs.
2023-11-02 12:27:55 +09:00
Rui Ueyama 9516da1751 Simplify 2023-10-31 21:14:48 +09:00
Rui Ueyama f3de82b2a2 Do not add the same set of tests twice 2023-10-31 19:32:46 +09:00
Rui Ueyama c4c506c1cb Make LoongArch range extension thunks shorter 2023-10-31 16:19:20 +09:00
Rui Ueyama 0c62a91ded Enable loongarch64 tests on loongarch64 hosts 2023-10-31 16:15:14 +09:00
Rui Ueyama 2fd0a4c60a Remove a warning because it's a bit too verbose 2023-10-31 16:10:42 +09:00
Rui Ueyama d3188e39df [LoongArch] Implement a workaround for R_LARCH_PCALA_LO12
It seems R_LARCH_PCALA_LO12 is sometimes used against JIRL even though
such use case is incorrect as per the psABI.

At least, `/usr/lib/loongarch64-linux-gnu/libc_nonshared.a(atexit.oS)`
on `cfarm400.cfarm.net` contains such relocation.

We need this patch to self-host mold on that cfarm machine.
2023-10-31 14:11:43 +09:00
Rui Ueyama 3229b5b7e3 Fix test 2023-10-31 12:22:39 +09:00
Rui Ueyama d9ede96aca Fix an incorrect assertion 2023-10-30 13:31:22 +09:00
Rui Ueyama 4b4219fc33 Fix tests for ARM32-on-ARM64 systems 2023-10-30 13:02:36 +09:00
Rui Ueyama 02ead29989 Fix build issue for ARM32-on-ARM64 systems 2023-10-30 13:02:35 +09:00
Rui Ueyama 37919009ab Do not report incompatibe relocation table as an error
Specifically, .llvm.call-graph-profile is always REL even on x86-64.
We can safely ignore the relocation table because it contains only
R_NONE relocations.

Fixes https://github.com/rui314/mold/issues/653
2023-10-28 19:12:20 +09:00
Rui Ueyama 782ff4f582 Fix test 2023-10-27 13:40:09 +09:00
Rui Ueyama 3c468c71a4 Merge pull request #1136 from sicherha/fix_aarch64_variant-pcs_test
Fix `aarch64_variant-pcs.sh` test on Linux
2023-10-27 11:38:54 +09:00
Christoph Erhardt a31679cf04 Fix aarch64_variant-pcs.sh test on Linux
The test expects the output format given by `llvm-readelf`:
```
5: 0000000000010698     0 FUNC    GLOBAL DEFAULT [VARIANT_PCS]   13 foo
```

However, in GNU's `readelf` implementation the symbol name comes first:
```
5: 0000000000010698     0 FUNC    GLOBAL DEFAULT   13 foo  [VARIANT_PCS]
```

Replace the regular expression with two `grep` calls piped together to
support both output formats.

Signed-off-by: Christoph Erhardt <[email protected]>
2023-10-26 23:59:24 +02:00
Rui Ueyama 9cd65ed152 Emit ARM32 mapping symbols for .plt and .plt.got 2023-10-26 13:20:24 +09:00
Rui Ueyama 79ad405319 Emit ARM32 mapping symbol strings only once 2023-10-26 13:20:24 +09:00
Rui Ueyama b60a164fcc Honor the --chroot option when handling --Map or --dependency-file 2023-10-24 12:34:24 +09:00
Rui Ueyama 2d4ba3ac74 Skip a test if musl
musl's tlsdesc implementation for arm32 is buggy.

Fixes https://github.com/rui314/mold/issues/1135
2023-10-23 23:47:13 +09:00
Rui Ueyama 42eabf9f71 Run a test for all targets 2023-10-23 18:39:53 +09:00
Rui Ueyama aa4b3f7d01 Attemp to fix a test
https://github.com/rui314/mold/issues/1134
2023-10-23 17:54:36 +09:00
Rui Ueyama 2d121b8d6d Inline larger relro segments 2023-10-23 16:33:10 +09:00
Rui Ueyama c86a59a511 Fill gaps in executable segments with trap or nop instructions 2023-10-23 12:26:56 +09:00
Rui Ueyama f7dea3a21b Revert "Do not create a PLT for an IFUNC unless necessary"
This reverts commit 651ae8a2a8 because
it broke CI.
2023-10-22 22:35:49 +09:00
Rui Ueyama 651ae8a2a8 Do not create a PLT for an IFUNC unless necessary 2023-10-22 20:31:36 +09:00
Rui Ueyama 5f7aca6276 Revert "Make container interruptible with Ctrl-C"
This reverts commit 5c4955763b because
it broke CI.
2023-10-22 15:53:50 +09:00
Rui Ueyama 39cdf61695 Create a lockfile in $XDG_RUNTIME_DIR instead of $HOME
https://github.com/rui314/mold/issues/117
2023-10-22 13:28:13 +09:00
Rui Ueyama 5c4955763b Make container interruptible with Ctrl-C 2023-10-22 13:04:56 +09:00
Rui Ueyama dae070283b Do not depend on the current path 2023-10-22 12:56:48 +09:00
Rui Ueyama 403655904b Merge two scripts into one 2023-10-21 23:30:12 +09:00
Rui Ueyama 44942b638b Do not copy mold and mold-wrapper.so to the top source directory 2023-10-21 20:49:43 +09:00
Rui Ueyama ceffd4679b Simplify 2023-10-21 17:50:36 +09:00
Rui Ueyama 6fa742be67 Improve data locality
It looks like this change makes the linker slightly faster.
2023-10-21 16:52:38 +09:00
Rui Ueyama 484e357971 Simplify 2023-10-21 13:13:56 +09:00
Rui Ueyama 53a5a37370 Refactor 2023-10-20 22:33:39 +09:00
Rui Ueyama 41579888b6 Add a timer prober 2023-10-20 20:25:07 +09:00
Rui Ueyama 1e2d84038e Simplify 2023-10-20 18:50:10 +09:00
Rui Ueyama 6c7700b051 Update the manual 2023-10-20 16:45:48 +09:00
Rui Ueyama 34c53777fb Bump mold version to 2.3.1 2023-10-20 13:15:31 +09:00
Rui Ueyama 7be1b66791 Fix range extension thunks
Fixes https://github.com/rui314/mold/issues/1132
2023-10-20 10:33:57 +09:00
Rui Ueyama 4bd80ec21c [LoongArch] Remove relocation overflow checks
These relocations may overflow with -mcmodel=extreme.
2023-10-19 20:44:23 +09:00
Rui Ueyama a9ab0ff452 [LoongArch] Fix -mcmodel=extreme
Based on MQ-mengqing's patch.

Fixes https://github.com/rui314/mold/issues/1131
2023-10-19 19:46:32 +09:00
Rui Ueyama 206bf65c50 Fix test
Fixes https://github.com/rui314/mold/issues/1130
2023-10-19 17:36:37 +09:00
Rui Ueyama 2b3e8eec05 Merge pull request #1129 from vihu/rg/artix 2023-10-19 15:08:51 +09:00
Rahul Garg 9eac818254 Add support for artix
Summary
----
This PR adds support for [Artix OS](https://artixlinux.org/).
2023-10-18 23:56:42 -06:00
Rui Ueyama 6fe0cadfa1 Enable building of RV64 distribution file 2023-10-19 14:12:54 +09:00
Rui Ueyama 1a2baee096 Fix test
`test_cflags` uses $CC to test if a given flag is supported, so
we should use $CC instead of $GCC for the rest of the file.

Fixes https://github.com/rui314/mold/issues/1128
2023-10-19 11:44:41 +09:00
Rui Ueyama 728b265cbd Fix -Wc++11-narrowing on 32-bit machines
Fixes https://github.com/rui314/mold/issues/1127
2023-10-19 11:33:54 +09:00
Rui Ueyama 9fa61b9298 Update third-party/rust-demangle
Updated to 4283d46e4064a7e1c54bc9918a07b066cb43fca3.

Fixed https://github.com/rui314/mold/issues/1127
2023-10-19 11:33:50 +09:00
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
946 changed files with 78626 additions and 18776 deletions

No files matched your search

+30 -11
View File
@@ -51,26 +51,45 @@ jobs:
# Install cross toolchains
dpkg --add-architecture i386
./install-build-deps.sh update
apt-get install -y sudo qemu-user gdb zstd dwarfdump {gcc,g++}-10-{i686,aarch64,riscv64,powerpc,powerpc64,powerpc64le,s390x,sparc64,m68k,sh4,alpha,mips,mipsel}-linux-gnu {gcc,g++}-10-arm-linux-gnueabihf {gcc,g++}-10-{mips64,mips64el}-linux-gnuabi64
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; do
ln -sf /usr/bin/$i-linux-gnu-gcc-10 /usr/bin/$i-linux-gnu-gcc
ln -sf /usr/bin/$i-linux-gnu-g++-10 /usr/bin/$i-linux-gnu-g++
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
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++
# 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/2022.08.26/riscv32-glibc-ubuntu-20.04-nightly-2022.08.26-nightly.tar.gz | tar -C /usr/local/rv32 --strip-components=1 -xzf -
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
ln -sf /usr/local/rv32/bin/riscv32-unknown-linux-gnu-objdump /usr/bin/riscv32-linux-gnu-objdump
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 /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
@@ -79,7 +98,7 @@ jobs:
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 ..
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
-8
View File
@@ -3,15 +3,7 @@
*~
**/.DS_Store
.cache
compile_commands.json
/mold
/test/tmp
options.inc
core
ld
ld64
ld64.mold
gentoo
out
/mold-*.tar.gz
/build*
+35 -23
View File
@@ -43,8 +43,8 @@
# 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.13)
project(mold VERSION 2.0.0)
cmake_minimum_required(VERSION 3.14)
project(mold VERSION 2.3.2)
include(CMakeDependentOption)
include(GNUInstallDirs)
@@ -64,8 +64,12 @@ if(NOT "${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "MSVC")
-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()
@@ -97,7 +101,7 @@ if(MOLD_USE_TSAN)
target_link_options(mold PRIVATE -fsanitize=thread)
endif()
# Statically-link libstdc++ and libcrypto if -DMOLD_MOSTLY_STATIC=ON.
# 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
@@ -105,13 +109,12 @@ endif()
option(MOLD_MOSTLY_STATIC "Statically link libstdc++ and libcrypto" OFF)
if(MOLD_MOSTLY_STATIC)
target_link_options(mold PRIVATE -static-libstdc++)
target_link_libraries(mold PRIVATE libcrypto.a)
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)
if(ZLIB_FOUND AND NOT MOLD_MOSTLY_STATIC)
target_link_libraries(mold PRIVATE ZLIB::ZLIB)
else()
add_subdirectory(third-party/zlib EXCLUDE_FROM_ALL)
@@ -120,12 +123,23 @@ else()
target_link_libraries(mold PRIVATE zlibstatic)
endif()
# Find zstd compression library. Just like zlib, if libzstd.so is not
# found, we compile a bundled one and statically-link it to mold.
# 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)
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)
@@ -146,12 +160,10 @@ endif()
#
# 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
"CMAKE_SIZEOF_VOID_P EQUAL 8; NOT APPLE; NOT ANDROID; NOT OPENBSD" OFF)
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)
@@ -236,11 +248,6 @@ if(NOT APPLE AND NOT WIN32)
target_sources(mold-wrapper PRIVATE elf/mold-wrapper.c)
endif()
if(NOT APPLE AND NOT WIN32 AND NOT MOLD_MOSTLY_STATIC)
find_package(OpenSSL REQUIRED COMPONENTS Crypto)
target_link_libraries(mold PRIVATE OpenSSL::Crypto)
endif()
# If atomics doesn't work by default, add -latomic.
# We need the flag on riscv, armv6 and m68k.
include(CheckCXXSourceCompiles)
@@ -289,12 +296,14 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
# 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 MIPS64LE MIPS64BE)
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/dwarf.cc
elf/gc-sections.cc
elf/gdb-index.cc
elf/icf.cc
elf/input-files.cc
elf/input-sections.cc
@@ -327,10 +336,12 @@ list(APPEND MOLD_MACHO_TEMPLATE_FILES
function(mold_instantiate_templates SOURCE TARGET)
set(PATH ${CMAKE_BINARY_DIR}/${SOURCE}.${TARGET}.cc)
file(WRITE ${PATH} "#define MOLD_${TARGET} 1
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()
@@ -365,11 +376,9 @@ target_sources(mold PRIVATE
elf/arch-arm64.cc
elf/arch-i386.cc
elf/arch-m68k.cc
elf/arch-mips64.cc
elf/arch-ppc32.cc
elf/arch-ppc64v1.cc
elf/arch-ppc64v2.cc
elf/arch-riscv.cc
elf/arch-s390x.cc
elf/arch-sh4.cc
elf/arch-sparc64.cc
@@ -442,6 +451,9 @@ if(NOT CMAKE_SKIP_INSTALL_RULES)
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 "
+9 -8
View File
@@ -1,8 +1,5 @@
# mold: A Modern Linker
[![CI](https://github.com/rui314/mold/actions/workflows/ci.yml/badge.svg)](https://github.com/rui314/mold/actions/workflows/ci.yml)
[![build result](https://build.opensuse.org/projects/home:marxin:mold/packages/mold/badge.svg?type=default)](https://build.opensuse.org/package/show/home:marxin:mold/mold)
<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
@@ -32,7 +29,8 @@ free to [file a bug report](https://github.com/rui314/mold/issues).
mold supports x86-64, i386, ARM64, ARM32, 64-bit/32-bit little/big-endian
RISC-V, 32-bit PowerPC, 64-bit big-endian PowerPC ELFv1, 64-bit little-endian
PowerPC ELFv2, s390x, SPARC64, m68k, SH-4, and DEC Alpha.
PowerPC ELFv2, s390x, 64-bit/32-bit LoongArch, SPARC64, m68k, SH-4, and DEC
Alpha.
mold/macOS is commercial software. For mold/macOS, please visit
https://github.com/bluewhalesystems/sold.
@@ -73,11 +71,11 @@ necessary packages. You may need to run it as root.
git clone https://github.com/rui314/mold.git
mkdir mold/build
cd mold/build
git checkout v2.0.0
git checkout v2.3.2
../install-build-deps.sh
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=c++ ..
cmake --build . -j $(nproc)
sudo cmake --install .
sudo cmake --build . --target install
```
You might need to pass a C++20 compiler command name to `cmake`. In the
@@ -251,8 +249,11 @@ 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://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)
-23
View File
@@ -1,23 +0,0 @@
# 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 && \
\
mkdir /openssl && cd /openssl && \
wget -O- -q https://www.openssl.org/source/openssl-3.0.7.tar.gz | tar --strip-components=1 -xzf - && \
./Configure --prefix=/usr/local --libdir=lib && \
make -j$(nproc) && \
make -j$(nproc) install && \
ldconfig && \
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 /openssl
+57 -46
View File
@@ -6,58 +6,69 @@ namespace mold {
template <typename Context>
std::vector<std::string_view>
read_response_file(Context &ctx, std::string_view path) {
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));
u8 *data = mf->data;
std::string_view data((char *)mf->data, mf->size);
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) {
if (data[i] == '\\' && i + 1 < mf->size) {
buf.append(1, data[i + 1]);
i += 2;
continue;
}
if (!isspace(data[i])) {
buf.append(1, data[i++]);
continue;
}
break;
while (!data.empty()) {
if (isspace(data[0])) {
data = data.substr(1);
continue;
}
vec.push_back(save_string(ctx, buf));
return i;
};
auto read_quoted = [&]() {
char quote = data[0];
data = data.substr(1);
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, '\"');
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
i = read_unquoted(i);
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;
}
@@ -68,7 +79,7 @@ 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));
append(vec, read_response_file(ctx, argv[i] + 1, 1));
else
vec.push_back(argv[i]);
}
+259 -133
View File
@@ -8,6 +8,7 @@
#include <bitset>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <filesystem>
@@ -22,6 +23,7 @@
#include <sys/types.h>
#include <tbb/concurrent_vector.h>
#include <tbb/enumerable_thread_specific.h>
#include <tbb/parallel_for.h>
#include <vector>
#ifdef _WIN32
@@ -224,8 +226,14 @@ struct Atomic : std::atomic<T> {
void operator=(T val) { store(val); }
operator T() const { return load(); }
void store(T val) { std::atomic<T>::store(val, relaxed); }
T load() const { return std::atomic<T>::load(relaxed); }
void store(T val, std::memory_order order = relaxed) {
std::atomic<T>::store(val, order);
}
T load(std::memory_order order = relaxed) const {
return std::atomic<T>::load(order);
}
T exchange(T val) { return std::atomic<T>::exchange(val, relaxed); }
T operator|=(T val) { return std::atomic<T>::fetch_or(val, relaxed); }
T operator++() { return std::atomic<T>::fetch_add(1, relaxed) + 1; }
@@ -242,6 +250,85 @@ struct Atomic : std::atomic<T> {
}
};
//
// perf.cc
//
// Counter is used to collect statistics numbers.
class Counter {
public:
Counter(std::string_view name, i64 value = 0) : name(name), values(value) {
static std::mutex mu;
std::scoped_lock lock(mu);
instances.push_back(this);
}
Counter &operator++(int) {
if (enabled) [[unlikely]]
values.local()++;
return *this;
}
Counter &operator+=(int delta) {
if (enabled) [[unlikely]]
values.local() += delta;
return *this;
}
static void print();
static inline bool enabled = false;
private:
i64 get_value();
std::string_view name;
tbb::enumerable_thread_specific<i64> values;
static inline std::vector<Counter *> instances;
};
// Timer and TimeRecord records elapsed time (wall clock time)
// used by each pass of the linker.
struct TimerRecord {
TimerRecord(std::string name, TimerRecord *parent = nullptr);
void stop();
std::string name;
TimerRecord *parent;
tbb::concurrent_vector<TimerRecord *> children;
i64 start;
i64 end;
i64 user;
i64 sys;
bool stopped = false;
};
void
print_timer_records(tbb::concurrent_vector<std::unique_ptr<TimerRecord>> &);
template <typename Context>
class Timer {
public:
Timer(Context &ctx, std::string name, Timer *parent = nullptr) {
record = new TimerRecord(name, parent ? parent->record : nullptr);
ctx.timer_records.push_back(std::unique_ptr<TimerRecord>(record));
}
Timer(const Timer &) = delete;
~Timer() {
record->stop();
}
void stop() {
record->stop();
}
private:
TimerRecord *record;
};
//
// Bit vector
//
@@ -298,7 +385,7 @@ inline u64 bits(u64 val, u64 hi, u64 lo) {
inline i64 sign_extend(u64 val, i64 size) {
return (i64)(val << (63 - size)) >> (63 - size);
};
}
template <typename T, typename Compare = std::less<T>>
void update_minimum(std::atomic<T> &atomic, u64 new_val, Compare cmp = {}) {
@@ -394,30 +481,36 @@ inline i64 write_uleb(u8 *buf, u64 val) {
return i;
}
inline u64 read_uleb(u8 *&buf) {
inline u64 read_uleb(u8 **buf) {
u64 val = 0;
u8 shift = 0;
u8 byte;
do {
byte = *buf++;
byte = *(*buf)++;
val |= (byte & 0x7f) << shift;
shift += 7;
} while (byte & 0x80);
return val;
}
inline u64 read_uleb(u8 const*&buf) {
return read_uleb(const_cast<u8 *&>(buf));
inline u64 read_uleb(u8 *buf) {
u8 *tmp = buf;
return read_uleb(&tmp);
}
inline u64 read_uleb(std::string_view &str) {
u8 *start = (u8 *)&str[0];
inline u64 read_uleb(std::string_view *str) {
u8 *start = (u8 *)str->data();
u8 *ptr = start;
u64 val = read_uleb(ptr);
str = str.substr(ptr - start);
u64 val = read_uleb(&ptr);
*str = str->substr(ptr - start);
return val;
}
inline u64 read_uleb(std::string_view str) {
std::string_view tmp = str;
return read_uleb(&tmp);
}
inline i64 uleb_size(u64 val) {
#if __GNUC__
#pragma GCC unroll 8
@@ -428,6 +521,14 @@ inline i64 uleb_size(u64 val) {
return 9;
}
inline void overwrite_uleb(u8 *loc, u64 val) {
while (*loc & 0b1000'0000) {
*loc++ = 0b1000'0000 | (val & 0b0111'1111);
val >>= 7;
}
*loc = val & 0b0111'1111;
}
template <typename Context>
std::string_view save_string(Context &ctx, const std::string &str) {
u8 *buf = new u8[str.size() + 1];
@@ -464,53 +565,72 @@ public:
}
~ConcurrentMap() {
if (keys) {
free((void *)keys);
free((void *)key_sizes);
free((void *)values);
}
free(entries);
}
// In order to avoid unnecessary cache-line false sharing, we want
// to make this object to be aligned to a reasonably large
// power-of-two address.
struct alignas(32) Entry {
Atomic<const char *> key;
T value;
u32 keylen;
};
void resize(i64 nbuckets) {
this->~ConcurrentMap();
this->nbuckets = std::max<i64>(MIN_NBUCKETS, bit_ceil(nbuckets));
nbuckets = std::max<i64>(MIN_NBUCKETS, bit_ceil(nbuckets));
i64 sz = sizeof(Entry) * this->nbuckets;
free(entries);
this->nbuckets = nbuckets;
keys = (std::atomic<const char *> *)calloc(nbuckets, sizeof(char *));
key_sizes = (u32 *)malloc(nbuckets * sizeof(u32));
values = (T *)malloc(nbuckets * sizeof(T));
#if _WIN32
// Even though std::aligned_alloc is defined in C++17, MSVC doesn't
// seem to provide that function.
entries = (Entry *)_aligned_malloc(sz, alignof(Entry));
#else
entries = (Entry *)std::aligned_alloc(alignof(Entry), sz);
#endif
memset(entries, 0, sz);
}
std::pair<T *, bool> insert(std::string_view key, u64 hash, const T &val) {
if (!keys)
return {nullptr, false};
assert(has_single_bit(nbuckets));
i64 idx = hash & (nbuckets - 1);
i64 retry = 0;
while (retry < MAX_RETRY) {
const char *ptr = keys[idx].load(std::memory_order_acquire);
if (ptr == marker) {
pause();
Entry &ent = entries[idx];
const char *ptr = nullptr;
bool claimed = ent.key.compare_exchange_weak(ptr, (char *)-1,
std::memory_order_acquire);
// If we successfully claimed the ownership of an unused slot,
// copy values to it.
if (claimed) {
new (&ent.value) T(val);
ent.keylen = key.size();
ent.key.store(key.data(), std::memory_order_release);
return {&ent.value, true};
}
// Loop on a spurious failure.
if (ptr == nullptr)
continue;
// If someone is copying values to the slot, do busy wait.
while (ptr == (char *)-1) {
pause();
ptr = ent.key.load(std::memory_order_acquire);
}
if (ptr == nullptr) {
if (!keys[idx].compare_exchange_weak(ptr, marker,
std::memory_order_acquire))
continue;
new (values + idx) T(val);
key_sizes[idx] = key.size();
keys[idx].store(key.data(), std::memory_order_release);
return {values + idx, true};
}
if (key.size() == key_sizes[idx] &&
memcmp(ptr, key.data(), key_sizes[idx]) == 0)
return {values + idx, false};
// If the same key is already present, this is the slot we are
// looking for.
if (key == std::string_view(ptr, ent.keylen))
return {&ent.value, false};
// Otherwise, move on to the next slot.
u64 mask = nbuckets / NUM_SHARDS - 1;
idx = (idx & ~mask) | ((idx + 1) & mask);
retry++;
@@ -520,17 +640,67 @@ public:
return {nullptr, false};
}
const char *get_key(i64 idx) {
return keys[idx].load(std::memory_order_relaxed);
i64 get_idx(T *value) const {
uintptr_t addr = (uintptr_t)value - (uintptr_t)value % sizeof(Entry);
return (Entry *)addr - entries;
}
// Return a list of map entries sorted in a deterministic order.
std::vector<Entry *> get_sorted_entries(i64 shard_idx) {
if (nbuckets == 0)
return {};
i64 shard_size = nbuckets / NUM_SHARDS;
i64 begin = shard_idx * shard_size;
i64 end = begin + shard_size;
i64 sz = 0;
for (i64 i = begin; i < end; i++)
if (entries[i].key)
sz++;
std::vector<Entry *> vec;
vec.reserve(sz);
// Since the shard is circular, we need to handle the last entries
// as if they were next to the first entries.
while (entries[end - 1].key)
vec.push_back(entries + --end);
// Find entries contiguous in the buckets and sort them.
i64 last = 0;
for (i64 i = begin; i < end;) {
while (i < end && entries[i].key)
vec.push_back(entries + i++);
std::sort(vec.begin() + last, vec.end(), [](Entry *a, Entry *b) {
if (a->keylen != b->keylen)
return a->keylen < b->keylen;
return memcmp(a->key, b->key, a->keylen) < 0;
});
last = vec.size();
while (i < end && !entries[i].key)
i++;
}
return vec;
}
std::vector<Entry *> get_sorted_entries_all() {
std::vector<std::vector<Entry *>> vec(NUM_SHARDS);
tbb::parallel_for((i64)0, NUM_SHARDS, [&](i64 i) {
vec[i] = get_sorted_entries(i);
});
return flatten(vec);
}
static constexpr i64 MIN_NBUCKETS = 2048;
static constexpr i64 NUM_SHARDS = 16;
static constexpr i64 MAX_RETRY = 128;
Entry *entries = nullptr;
i64 nbuckets = 0;
u32 *key_sizes = nullptr;
T *values = nullptr;
private:
static void pause() {
@@ -540,10 +710,6 @@ private:
asm volatile("yield");
#endif
}
private:
std::atomic<const char *> *keys = nullptr;
static constexpr const char *marker = "marker";
};
//
@@ -560,9 +726,11 @@ public:
virtual ~OutputFile() = default;
u8 *buf = nullptr;
std::vector<u8> buf2;
std::string path;
i64 filesize;
bool is_mmapped;
i64 fd = -1;
i64 filesize = 0;
bool is_mmapped = false;
bool is_unmapped = false;
protected:
@@ -570,6 +738,43 @@ protected:
: path(path), filesize(filesize), is_mmapped(is_mmapped) {}
};
template <typename Context>
class MallocOutputFile : public OutputFile<Context> {
public:
MallocOutputFile(Context &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<Context>(path, filesize, false), perm(perm) {
this->buf = (u8 *)malloc(filesize);
if (!this->buf)
Fatal(ctx) << "malloc failed";
}
~MallocOutputFile() {
free(this->buf);
}
void close(Context &ctx) override {
Timer t(ctx, "close_file");
FILE *fp;
if (this->path == "-") {
fp = stdout;
} else {
i64 fd = ::open(this->path.c_str(), O_RDWR | O_CREAT, perm);
if (fd == -1)
Fatal(ctx) << "cannot open " << this->path << ": " << errno_string();
fp = fdopen(fd, "w");
}
fwrite(this->buf, this->filesize, 1, fp);
if (!this->buf2.empty())
fwrite(this->buf2.data(), this->buf2.size(), 1, fp);
fclose(fp);
}
private:
i64 perm;
};
//
// hyperloglog.cc
//
@@ -705,85 +910,6 @@ private:
std::vector<std::vector<u8>> shards;
};
//
// perf.cc
//
// Counter is used to collect statistics numbers.
class Counter {
public:
Counter(std::string_view name, i64 value = 0) : name(name), values(value) {
static std::mutex mu;
std::scoped_lock lock(mu);
instances.push_back(this);
}
Counter &operator++(int) {
if (enabled) [[unlikely]]
values.local()++;
return *this;
}
Counter &operator+=(int delta) {
if (enabled) [[unlikely]]
values.local() += delta;
return *this;
}
static void print();
static inline bool enabled = false;
private:
i64 get_value();
std::string_view name;
tbb::enumerable_thread_specific<i64> values;
static inline std::vector<Counter *> instances;
};
// Timer and TimeRecord records elapsed time (wall clock time)
// used by each pass of the linker.
struct TimerRecord {
TimerRecord(std::string name, TimerRecord *parent = nullptr);
void stop();
std::string name;
TimerRecord *parent;
tbb::concurrent_vector<TimerRecord *> children;
i64 start;
i64 end;
i64 user;
i64 sys;
bool stopped = false;
};
void
print_timer_records(tbb::concurrent_vector<std::unique_ptr<TimerRecord>> &);
template <typename Context>
class Timer {
public:
Timer(Context &ctx, std::string name, Timer *parent = nullptr) {
record = new TimerRecord(name, parent ? parent->record : nullptr);
ctx.timer_records.push_back(std::unique_ptr<TimerRecord>(record));
}
Timer(const Timer &) = delete;
~Timer() {
record->stop();
}
void stop() {
record->stop();
}
private:
TimerRecord *record;
};
//
// tar.cc
//
@@ -871,9 +997,9 @@ MappedFile<Context> *MappedFile<Context>::open(Context &ctx, std::string path) {
i64 fd;
#ifdef _WIN32
fd = ::_open(path.c_str(), O_RDONLY);
fd = ::_open(path.c_str(), O_RDONLY);
#else
fd = ::open(path.c_str(), O_RDONLY);
fd = ::open(path.c_str(), O_RDONLY);
#endif
if (fd == -1) {
@@ -909,7 +1035,7 @@ MappedFile<Context> *MappedFile<Context>::open(Context &ctx, std::string path) {
if (mf->data == MAP_FAILED)
Fatal(ctx) << path << ": mmap failed: " << errno_string();
#endif
}
}
close(fd);
return mf;
+12 -37
View File
@@ -52,14 +52,13 @@ class MemoryMappedOutputFile : public OutputFile<Context> {
public:
MemoryMappedOutputFile(Context &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<Context>(path, filesize, true) {
i64 fd;
std::tie(fd, output_tmpfile) = open_or_create_file(ctx, path, filesize, perm);
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, fd, 0);
MAP_SHARED, this->fd, 0);
if (this->buf == MAP_FAILED)
Fatal(ctx) << path << ": mmap failed: " << errno_string();
::close(fd);
mold::output_buffer_start = this->buf;
mold::output_buffer_end = this->buf + filesize;
@@ -76,6 +75,15 @@ public:
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.
@@ -92,39 +100,6 @@ private:
int fd2 = -1;
};
template <typename Context>
class MallocOutputFile : public OutputFile<Context> {
public:
MallocOutputFile(Context &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<Context>(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 &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 Context>
std::unique_ptr<OutputFile<Context>>
OutputFile<Context>::open(Context &ctx, std::string path, i64 filesize, i64 perm) {
-33
View File
@@ -6,39 +6,6 @@
namespace mold {
template <typename Context>
class MallocOutputFile : public OutputFile<Context> {
public:
MallocOutputFile(Context &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<Context>(path, filesize, false), perm(perm) {
this->buf = (u8 *)malloc(filesize);
if (!this->buf)
Fatal(ctx) << "malloc failed";
}
void close(Context &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);
free(this->buf);
}
private:
i64 perm;
};
template <typename Context>
std::unique_ptr<OutputFile<Context>>
OutputFile<Context>::open(Context &ctx, std::string path, i64 filesize, i64 perm) {
-81
View File
@@ -1,81 +0,0 @@
#pragma once
#include <cstdint>
typedef uint8_t u8;
static constexpr int64_t SHA256_SIZE = 32;
#ifdef _WIN32
// On Windows, we use Microsoft CNG.
#include <Windows.h>
#include <bcrypt.h>
#include <ntstatus.h>
inline static BCRYPT_ALG_HANDLE get_sha256_handle() {
static std::once_flag once;
static BCRYPT_ALG_HANDLE alg;
std::call_once(once, [&] {
BCryptOpenAlgorithmProvider(&alg, BCRYPT_SHA256_ALGORITHM, nullptr, 0);
});
return alg;
}
inline void sha256_hash(u8 *in, size_t len, u8 *out) {
BCryptHash(get_sha256_handle(), nullptr, 0, in, len, out, SHA256_SIZE);
}
class SHA256Hash {
public:
SHA256Hash() {
BCryptCreateHash(get_sha256_handle(), &handle, nullptr, 0, nullptr, 0, 0);
}
void update(u8 *data, size_t len) {
BCryptHashData(handle, data, len, 0);
}
void finish(u8 *out) {
BCryptFinishHash(handle, out, SHA256_SIZE, 0);
}
private:
BCRYPT_HASH_HANDLE handle;
};
#else
// On Unix, we use OpenSSL or the Apple's OpenSSL-compatible API.
#ifdef __APPLE__
# define COMMON_DIGEST_FOR_OPENSSL
# include <CommonCrypto/CommonDigest.h>
# define SHA256(data, len, md) CC_SHA256(data, len, md)
#else
# define OPENSSL_SUPPRESS_DEPRECATED 1
# include <openssl/sha.h>
#endif
inline void sha256_hash(u8 *in, size_t len, u8 *out) {
SHA256(in, len, out);
}
class SHA256Hash {
public:
SHA256Hash() {
SHA256_Init(&ctx);
}
void update(u8 *data, size_t len) {
SHA256_Update(&ctx, data, len);
}
void finish(u8 *out) {
SHA256_Final(out, &ctx);
}
private:
SHA256_CTX ctx;
};
#endif
+3
View File
@@ -50,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);
@@ -81,6 +83,7 @@ void TarWriter::append(std::string path, std::string_view data) {
std::string attr = encode_path(basedir, path);
snprintf(pax.size, sizeof(pax.size), "%011zo", attr.size());
pax.name[0] = '/';
pax.typeflag[0] = 'x';
pax.finalize();
fwrite(&pax, sizeof(pax), 1, out);
+48 -10
View File
@@ -1,4 +1,5 @@
#!/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,6 +8,8 @@
# libstdc++ and libcrypto but dynamically-linked to libc, libm, libz
# and librt, as they almost always exist on any Linux systems.
set -e -x
case $# in
0)
arch=$(uname -m)
@@ -16,25 +19,60 @@ case $# in
arch="$1"
;;
*)
echo "Usage: $0 [ x86_64 | aarch64 | arm | ppc64le | s390x ]"
echo "Usage: $0 [ x86_64 | aarch64 | arm | riscv64 | ppc64le | s390x ]"
exit 1
esac
echo "$arch" | grep -Eq '^(x86_64|aarch64|arm|ppc64le|s390x)$' || \
echo "$arch" | grep -Eq '^(x86_64|aarch64|arm|riscv64|ppc64le|s390x)$' || \
{ echo "Error: no docker image for $arch"; exit 1; }
image=mold-builder-$arch:1
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 &&
# Create a Docker image if not exists.
#
# We want to use a reasonably old Linux distro because we'll dynamically
# link glibc to mold, and a binary linked against a newer version of
# glibc won't work on a system with a older version of glibc.
#
# We might want to instead statically-link everything, but that'll
# disable dlopen(), which means we can't use the linker plugin for LTO.
# So we don't want to do that.
if ! docker image ls $image | grep -q $image; then
if [ $arch = riscv64 ]; then
# Ubuntu 18 didn't support RISC-V, so use Ubuntu 20 instead
cat <<EOF | docker build --platform $arch -t $image -
FROM riscv64/ubuntu:20.04
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive TZ=UTC apt-get install -y --no-install-recommends build-essential gcc-10 g++-10 cmake && \
rm -rf /var/lib/apt/lists
EOF
else
cat <<EOF | docker build --platform $arch -t $image -
FROM ubuntu:18.04
RUN apt-get update && \
apt-get install -y --no-install-recommends software-properties-common && \
add-apt-repository -y ppa:ubuntu-toolchain-r/test && \
apt-get update && \
DEBIAN_FRONTEND=noninteractive TZ=UTC apt-get install -y --no-install-recommends build-essential wget libstdc++-11-dev gcc-10 g++-10 python3 libssl-dev && \
\
mkdir /cmake && cd /cmake && \
wget -O- -q https://github.com/Kitware/CMake/releases/download/v3.24.2/cmake-3.24.2.tar.gz | tar --strip-components=1 -xzf - && \
./bootstrap --parallel=$(nproc) && make -j$(nproc) && make -j$(nproc) install && \
rm -rf /var/lib/apt/lists /cmake
EOF
fi
fi
# Build mold in a container.
docker run --platform linux/$arch -i --rm -v "$(realpath $(dirname $0)):/mold" \
-e "OWNER=$(id -u):$(id -g)" $image \
bash -c "mkdir /build &&
cd /build &&
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 -DMOLD_MOSTLY_STATIC=On /mold &&
cmake --build . -j\$(nproc) &&
[ $arch = arm ] || ctest -j\$(nproc) &&
cmake --install . --prefix $dest --strip &&
tar czf /mold/$dest.tar.gz $dest &&
cp mold mold-wrapper.so /mold &&
chown \$OWNER /mold/mold /mold/mold-wrapper.so /mold/$dest.tar.gz"
chown \$OWNER /mold/$dest.tar.gz"
Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

+4 -1
View File
@@ -1,6 +1,6 @@
.\" generated with Ronn-NG/v0.9.1
.\" http://github.com/apjanke/ronn-ng/tree/0.9.1
.TH "MOLD" "1" "March 2023" ""
.TH "MOLD" "1" "July 2023" ""
.SH "NAME"
\fBmold\fR \- a modern linker
.SH "SYNOPSIS"
@@ -425,6 +425,9 @@ Some sections such as \fB\.dynamic\fR have to be writable only during an executa
.IP
By default, \fBmold\fR generates a relro segment\. \fB\-z norelro\fR disables the feature\.
.TP
\fB\-z sectionheader\fR, \fB\-z nosectionheader\fR
\fB\-z nosectionheader\fR tell the linker to omit the section header\. By default, the linker does not omit the section header\.
.TP
\fB\-z separate\-loadable\-segments\fR, \fB\-z separate\-code\fR, \fB\-z noseparate\-code\fR
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\.
.IP
+19 -13
View File
@@ -697,6 +697,10 @@ arguments.
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
@@ -760,22 +764,24 @@ arguments.
## 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.
If this variable is set to `1`, only one `mold` process will run at a time.
If a new mold process is initiated while another is already active, the new
process will wait until the active one completes before starting.
The purpose of this environment variable is to reduce peak memory usage.
Since mold is highly parallelized, there's no point in running it
simultaneously. If you run N instances of mold in parallel, it would take N
times more time and N times more memory. If you run them serially, it would
still take N times more to finish, but their peak memory usage is reduced to
normal.
The primary reason for this environment variable is to minimize peak memory
usage. Since mold is designed to operate with high parallelism, running
multiple mold instances simultaneously may not be beneficial. If you execute
N instances of mold concurrently, it could require N times the time and N
times the memory. On the other hand, running them one after the other might
still take N times longer, but the peak memory usage would be the same as
running just a single instance.
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.
If your build system invokes multiple linker processes simultaneously and
some of them often get killed due to out-of-memory errors, you might
consider setting this environment variable to `1` to see if it addresses the
OOM issue.
Currently, any value other than 1 is silently ignored.
Currently, any value other than `1` is silently ignored.
* `MOLD_DEBUG`:
If this variable is set to a non-empty string, `mold` embeds its
+3 -3
View File
@@ -62,8 +62,8 @@ template <>
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, const 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) {
@@ -103,7 +103,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
switch (rel.r_type) {
case R_ALPHA_REFQUAD:
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, dynrel);
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_ALPHA_GPREL32:
*(ul32 *)loc = S + A - GP;
+73 -42
View File
@@ -195,14 +195,14 @@ void write_addend(u8 *loc, i64 val, const ElfRel<E> &rel) {
template <>
void write_plt_header(Context<E> &ctx, u8 *buf) {
static const ul32 insn[] = {
0xe52d'e004, // push {lr}
0xe59f'e004, // ldr lr, 2f
0xe52d'e004, // push {lr}
0xe59f'e004, // ldr lr, 2f
0xe08f'e00e, // 1: add lr, pc, lr
0xe5be'f008, // ldr pc, [lr, #8]!
0xe5be'f008, // ldr pc, [lr, #8]!
0x0000'0000, // 2: .word .got.plt - 1b - 8
0xe320'f000, // nop
0xe320'f000, // nop
0xe320'f000, // nop
0x0000'0000, // (padding)
0x0000'0000, // (padding)
0x0000'0000, // (padding)
};
memcpy(buf, insn, sizeof(insn));
@@ -211,8 +211,8 @@ void write_plt_header(Context<E> &ctx, u8 *buf) {
static const ul32 plt_entry[] = {
0xe59f'c004, // 1: ldr ip, 2f
0xe08c'c00f, // add ip, ip, pc
0xe59c'f000, // ldr pc, [ip]
0xe08c'c00f, // add ip, ip, pc
0xe59c'f000, // ldr pc, [ip]
0x0000'0000, // 2: .word sym@GOT - 1b
};
@@ -225,14 +225,14 @@ 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) {
memcpy(buf, plt_entry, sizeof(plt_entry));
*(ul32 *)(buf + 12) = sym.get_got_addr(ctx) - sym.get_plt_addr(ctx) - 12;
*(ul32 *)(buf + 12) = sym.get_got_pltgot_addr(ctx) - sym.get_plt_addr(ctx) - 12;
}
// ARM does not use .eh_frame for exception handling. Instead, it uses
// .ARM.exidx and .ARM.extab. So this function is empty.
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {}
void EhFrameSection<E>::apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {}
// ARM and Thumb branch instructions can jump within ±16 MiB.
static bool is_jump_reachable(i64 val) {
@@ -286,7 +286,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
switch (rel.r_type) {
case R_ARM_ABS32:
case R_ARM_TARGET1:
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, dynrel);
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_ARM_REL32:
*(ul32 *)loc = S + A - P;
@@ -336,12 +336,12 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
break;
}
// Just like THM_CALL, ARM_CALL relocation refers either BL or
// Just like THM_CALL, ARM_CALL relocation refers to either BL or
// BLX instruction. We may need to rewrite BL → BLX or BLX → BL.
bool is_bl = ((*(ul32 *)loc & 0xff00'0000) == 0xeb00'0000);
bool is_blx = ((*(ul32 *)loc & 0xfe00'0000) == 0xfa00'0000);
if (!is_bl && !is_blx)
Fatal(ctx) << *this << ": R_ARM_CALL refers neither BL nor BLX";
Fatal(ctx) << *this << ": R_ARM_CALL refers to neither BL nor BLX";
u64 val = S + A - P;
if (is_jump_reachable(val)) {
@@ -364,7 +364,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
break;
}
// These relocs refers a B (unconditional branch) instruction.
// These relocs refers to a B (unconditional branch) instruction.
// Unlike BL or BLX, we can't rewrite B to BX in place when the
// processor mode switch is required because BX doesn't takes an
// immediate; it takes only a register. So if mode switch is
@@ -467,12 +467,33 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
*(ul32 *)loc = S + A - ctx.tp_addr;
break;
case R_ARM_TLS_GOTDESC:
// ARM32 TLSDESC uses the following code sequence to materialize
// a TP-relative address in r0.
//
// ldr r0, .L2
// .L1: bl foo
// R_ARM_TLS_CALL
// .L2: .word foo + . - .L1
// R_ARM_TLS_GOTDESC
//
// We may relax the instructions to the following for non-dlopen'd DSO
//
// ldr r0, .L2
// .L1: ldr r0, [pc, r0]
// ...
// .L2: .word foo(gottpoff) + . - .L1
//
// or to the following for executable.
//
// ldr r0, .L2
// .L1: nop
// ...
// .L2: .word foo(tpoff)
if (sym.has_tlsdesc(ctx)) {
// A is odd if the corresponding TLS_CALL is Thumb.
if (A & 1)
*(ul32 *)loc = sym.get_tlsdesc_addr(ctx) - P + A - 6;
else
*(ul32 *)loc = sym.get_tlsdesc_addr(ctx) - P + A - 4;
*(ul32 *)loc = sym.get_tlsdesc_addr(ctx) - P + A - ((A & 1) ? 6 : 4);
} else if (sym.has_gottp(ctx)) {
*(ul32 *)loc = sym.get_gottp_addr(ctx) - P + A - ((A & 1) ? 5 : 8);
} else {
*(ul32 *)loc = S - ctx.tp_addr;
}
@@ -481,9 +502,10 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
if (sym.has_tlsdesc(ctx)) {
// BL <tls_trampoline>
*(ul32 *)loc = 0xeb00'0000 | bits(get_tls_trampoline_addr(P + 8), 25, 2);
} else if (sym.has_gottp(ctx)) {
*(ul32 *)loc = 0xe79f'0000; // ldr r0, [pc, r0]
} else {
// BL -> NOP
*(ul32 *)loc = 0xe320'f000;
*(ul32 *)loc = 0xe320'f000; // nop
}
break;
case R_ARM_THM_TLS_CALL:
@@ -491,9 +513,13 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
u64 val = align_to(get_tls_trampoline_addr(P + 4), 4);
write_thm_b_imm(loc, val);
*(ul16 *)(loc + 2) &= ~0x1000; // rewrite BL with BLX
} else if (sym.has_gottp(ctx)) {
// Since `ldr r0, [pc, r0]` is not representable in Thumb,
// we use two instructions instead.
*(ul16 *)loc = 0x4478; // add r0, pc
*(ul16 *)(loc + 2) = 0x6800; // ldr r0, [r0]
} else {
// BL -> NOP.W
*(ul32 *)loc = 0x8000'f3af;
*(ul32 *)loc = 0x8000'f3af; // nop.w
}
break;
default:
@@ -594,9 +620,9 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
case R_ARM_TLS_IE32:
sym.flags |= NEEDS_GOTTP;
break;
case R_ARM_TLS_GOTDESC:
if (!relax_tlsdesc(ctx, sym))
sym.flags |= NEEDS_TLSDESC;
case R_ARM_TLS_CALL:
case R_ARM_THM_TLS_CALL:
scan_tlsdesc(ctx, sym);
break;
case R_ARM_TLS_LE32:
check_tlsle(ctx, sym, rel);
@@ -611,9 +637,8 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
case R_ARM_THM_MOVW_PREL_NC:
case R_ARM_THM_MOVW_ABS_NC:
case R_ARM_TLS_LDO32:
case R_ARM_TLS_CALL:
case R_ARM_THM_TLS_CALL:
case R_ARM_V4BX:
case R_ARM_TLS_GOTDESC:
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
@@ -623,8 +648,6 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
template <>
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + output_section.shdr.sh_offset + offset;
// TLS trampoline code. ARM32's TLSDESC is designed so that this
// common piece of code is factored out from object files to reduce
// output size. Since no one provide, the linker has to synthesize it.
@@ -632,36 +655,44 @@ void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
0xe08e'0000, // add r0, lr, r0
0xe590'1004, // ldr r1, [r0, #4]
0xe12f'ff11, // bx r1
0xe320'f000, // nop
};
// This is a range extension and mode switch thunk.
// It has two entry points: +0 for Thumb and +4 for ARM.
const u8 entry[] = {
// .thumb
0xfc, 0x46, // mov ip, pc
0x60, 0x47, // bx ip # jumps to the following `ldr` insn
0x78, 0x47, // bx pc # jumps to 1f
0xc0, 0x46, // nop
// .arm
0x04, 0xc0, 0x9f, 0xe5, // ldr ip, 2f
0x0f, 0xc0, 0x8c, 0xe0, // 1: add ip, ip, pc
0x1c, 0xff, 0x2f, 0xe1, // bx ip
0x00, 0x00, 0x00, 0x00, // 2: .word sym - 1b
0x00, 0xc0, 0x9f, 0xe5, // 1: ldr ip, 3f
0x0f, 0xf0, 0x8c, 0xe0, // 2: add pc, ip, pc
0x00, 0x00, 0x00, 0x00, // 3: .word sym - 2b
};
static_assert(E::thunk_hdr_size == sizeof(hdr));
static_assert(E::thunk_size == sizeof(entry));
u8 *buf = ctx.buf + output_section.shdr.sh_offset + offset;
memcpy(buf, hdr, sizeof(hdr));
buf += sizeof(hdr);
for (i64 i = 0; i < symbols.size(); i++) {
u8 *loc = buf + sizeof(hdr) + i * sizeof(entry);
memcpy(loc, entry, sizeof(entry));
u64 P = output_section.shdr.sh_addr + offset + sizeof(hdr);
u64 S = symbols[i]->get_addr(ctx);
u64 P = output_section.shdr.sh_addr + offset + sizeof(hdr) + i * sizeof(entry);
*(ul32 *)(loc + 16) = S - P - 16;
for (Symbol<E> *sym : symbols) {
memcpy(buf, entry, sizeof(entry));
*(ul32 *)(buf + 12) = sym->get_addr(ctx) - P - 16;
buf += sizeof(entry);
P += sizeof(entry);
}
}
template <>
u64 get_eflags(Context<E> &ctx) {
return EF_ARM_EABI_VER5;
}
// ARM executables use an .ARM.exidx section to look up an exception
// handling record for the current instruction pointer. The table needs
// to be sorted by their addresses.
+85 -50
View File
@@ -93,7 +93,7 @@ void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
0xd503'201f, // nop
};
u64 got = sym.get_got_addr(ctx);
u64 got = sym.get_got_pltgot_addr(ctx);
u64 plt = sym.get_plt_addr(ctx);
memcpy(buf, insn, sizeof(insn));
@@ -102,8 +102,8 @@ void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, const 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) {
@@ -173,7 +173,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
switch (rel.r_type) {
case R_AARCH64_ABS64:
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, dynrel);
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_AARCH64_LDST8_ABS_LO12_NC:
case R_AARCH64_ADD_ABS_LO12_NC:
@@ -232,15 +232,18 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
i++;
}
break;
case R_AARCH64_ADR_PREL_PG_HI21: {
case R_AARCH64_ADR_PREL_PG_HI21:
case R_AARCH64_ADR_PREL_PG_HI21_NC: {
// The ARM64 psABI defines that an `ADRP x0, foo` and `ADD x0, x0,
// :lo12: foo` instruction pair to materialize a PC-relative address
// in a register can be relaxed to `NOP` followed by `ADR x0, foo`
// if foo is in PC ± 1 MiB.
if (ctx.arg.relax && i + 1 < rels.size() &&
sign_extend(S + A - P - 4, 20) == S + A - P - 4) {
if (ctx.arg.relax && sym.is_pcrel_linktime_const(ctx) &&
i + 1 < rels.size()) {
i64 val = S + A - P - 4;
const ElfRel<E> &rel2 = rels[i + 1];
if (rel2.r_type == R_AARCH64_ADD_ABS_LO12_NC &&
if (sign_extend(val, 20) == val &&
rel2.r_type == R_AARCH64_ADD_ABS_LO12_NC &&
rel2.r_sym == rel.r_sym &&
rel2.r_offset == rel.r_offset + 4 &&
rel2.r_addend == rel.r_addend &&
@@ -251,7 +254,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
if (reg1 == reg2) {
*(ul32 *)loc = 0xd503'201f; // nop
*(ul32 *)(loc + 4) = 0x1000'0000 | reg1; // adr
write_adr(loc + 4, S + A - P - 4);
write_adr(loc + 4, val);
i++;
break;
}
@@ -259,7 +262,8 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
}
i64 val = page(S + A) - page(P);
check(val, -(1LL << 32), 1LL << 32);
if (rel.r_type == R_AARCH64_ADR_PREL_PG_HI21)
check(val, -(1LL << 32), 1LL << 32);
write_adrp(loc, val);
break;
}
@@ -367,38 +371,65 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
*(ul32 *)loc |= bits(sym.get_tlsgd_addr(ctx) + A, 11, 0) << 10;
break;
case R_AARCH64_TLSDESC_ADR_PAGE21:
// ARM64 TLSDESC uses the following code sequence to materialize
// a TP-relative address in x0.
//
// adrp x0, 0
// R_AARCH64_TLSDESC_ADR_PAGE21 foo
// ldr x1, [x0]
// R_AARCH64_TLSDESC_LD64_LO12 foo
// add x0, x0, #0
// R_AARCH64_TLSDESC_ADD_LO12 foo
// blr x1
// R_AARCH64_TLSDESC_CALL foo
//
// We may relax the instructions to the following for non-dlopen'd DSO
//
// nop
// nop
// adrp x0, :gottprel:foo
// ldr x0, [x0, :gottprel_lo12:foo]
//
// or to the following for executable.
//
// nop
// nop
// movz x0, :tls_offset_hi:foo, lsl #16
// movk x0, :tls_offset_lo:foo
if (sym.has_tlsdesc(ctx)) {
i64 val = page(sym.get_tlsdesc_addr(ctx) + A) - page(P);
check(val, -(1LL << 32), 1LL << 32);
write_adrp(loc, val);
} else {
// adrp x0, 0 -> movz x0, #tls_ofset_hi, lsl #16
i64 val = (S + A - ctx.tp_addr);
check(val, -(1LL << 32), 1LL << 32);
*(ul32 *)loc = 0xd2a0'0000 | (bits(val, 32, 16) << 5);
*(ul32 *)loc = 0xd503'201f; // nop
}
break;
case R_AARCH64_TLSDESC_LD64_LO12:
if (sym.has_tlsdesc(ctx)) {
if (sym.has_tlsdesc(ctx))
*(ul32 *)loc |= bits(sym.get_tlsdesc_addr(ctx) + A, 11, 3) << 10;
} else {
// ldr x2, [x0] -> movk x0, #tls_ofset_lo
u32 offset_lo = (S + A - ctx.tp_addr) & 0xffff;
*(ul32 *)loc = 0xf280'0000 | (offset_lo << 5);
}
else
*(ul32 *)loc = 0xd503'201f; // nop
break;
case R_AARCH64_TLSDESC_ADD_LO12:
if (sym.has_tlsdesc(ctx)) {
*(ul32 *)loc |= bits(sym.get_tlsdesc_addr(ctx) + A, 11, 0) << 10;
} else if (sym.has_gottp(ctx)) {
*(ul32 *)loc = 0x9000'0000; // adrp x0, 0
write_adrp(loc, page(sym.get_gottp_addr(ctx) + A) - page(P));
} else {
// add x0, x0, #0 -> nop
*(ul32 *)loc = 0xd503'201f;
*(ul32 *)loc = 0xd2a0'0000; // movz x0, 0, lsl #16
*(ul32 *)loc |= bits(S + A - ctx.tp_addr, 32, 16) << 5;
}
break;
case R_AARCH64_TLSDESC_CALL:
if (!sym.has_tlsdesc(ctx)) {
// blr x2 -> nop
*(ul32 *)loc = 0xd503'201f;
if (sym.has_tlsdesc(ctx)) {
// Do nothing
} else if (sym.has_gottp(ctx)) {
*(ul32 *)loc = 0xf940'0000; // ldr x0, [x0, 0]
*(ul32 *)loc |= bits(sym.get_gottp_addr(ctx) + A, 11, 3) << 10;
} else {
*(ul32 *)loc = 0xf280'0000; // movk x0, 0
*(ul32 *)loc |= bits(S + A - ctx.tp_addr, 15, 0) << 5;
}
break;
default:
@@ -477,13 +508,16 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
case R_AARCH64_ABS64:
scan_dyn_absrel(ctx, sym, rel);
break;
case R_AARCH64_MOVW_UABS_G3:
scan_absrel(ctx, sym, rel);
break;
case R_AARCH64_ADR_GOT_PAGE:
// An ADR_GOT_PAGE and GOT_LO12_NC relocation pair is used to load a
// symbol's address from GOT. If the GOT value is a link-time
// constant, we may be able to rewrite the ADRP+LDR instruction pair
// with an ADRP+ADD, eliminating a GOT memory load.
if (ctx.arg.relax && sym.is_relative() && !sym.is_imported &&
!sym.is_ifunc() && i + 1 < rels.size()) {
if (ctx.arg.relax && sym.is_pcrel_linktime_const(ctx) &&
i + 1 < rels.size()) {
// ADRP+LDR must be consecutive and use the same register to relax.
const ElfRel<E> &rel2 = rels[i + 1];
if (rel2.r_type == R_AARCH64_LD64_GOT_LO12_NC &&
@@ -519,23 +553,16 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
sym.flags |= NEEDS_GOTTP;
break;
case R_AARCH64_ADR_PREL_PG_HI21:
case R_AARCH64_ADR_PREL_PG_HI21_NC:
scan_pcrel(ctx, sym, rel);
break;
case R_AARCH64_TLSGD_ADR_PAGE21:
sym.flags |= NEEDS_TLSGD;
break;
case R_AARCH64_TLSDESC_ADR_PAGE21:
case R_AARCH64_TLSDESC_LD64_LO12:
case R_AARCH64_TLSDESC_ADD_LO12:
if (!relax_tlsdesc(ctx, sym))
sym.flags |= NEEDS_TLSDESC;
case R_AARCH64_TLSDESC_CALL:
scan_tlsdesc(ctx, sym);
break;
case R_AARCH64_TLSLE_MOVW_TPREL_G0:
case R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
case R_AARCH64_TLSLE_MOVW_TPREL_G1:
case R_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
case R_AARCH64_TLSLE_MOVW_TPREL_G2:
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
case R_AARCH64_TLSLE_ADD_TPREL_LO12:
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
check_tlsle(ctx, sym, rel);
@@ -555,12 +582,18 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
case R_AARCH64_MOVW_UABS_G1_NC:
case R_AARCH64_MOVW_UABS_G2:
case R_AARCH64_MOVW_UABS_G2_NC:
case R_AARCH64_MOVW_UABS_G3:
case R_AARCH64_PREL16:
case R_AARCH64_PREL32:
case R_AARCH64_PREL64:
case R_AARCH64_TLSGD_ADD_LO12_NC:
case R_AARCH64_TLSDESC_CALL:
case R_AARCH64_TLSLE_MOVW_TPREL_G0:
case R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
case R_AARCH64_TLSLE_MOVW_TPREL_G1:
case R_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
case R_AARCH64_TLSDESC_ADR_PAGE21:
case R_AARCH64_TLSDESC_LD64_LO12:
case R_AARCH64_TLSDESC_ADD_LO12:
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
@@ -570,24 +603,26 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
template <>
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + output_section.shdr.sh_offset + offset;
static const ul32 data[] = {
static const ul32 insn[] = {
0x9000'0010, // adrp x16, 0 # R_AARCH64_ADR_PREL_PG_HI21
0x9100'0210, // add x16, x16 # R_AARCH64_ADD_ABS_LO12_NC
0xd61f'0200, // br x16
0xd503'201f, // nop
};
static_assert(E::thunk_size == sizeof(data));
static_assert(E::thunk_size == sizeof(insn));
for (i64 i = 0; i < symbols.size(); i++) {
u64 S = symbols[i]->get_addr(ctx);
u64 P = output_section.shdr.sh_addr + offset + i * E::thunk_size;
u8 *buf = ctx.buf + output_section.shdr.sh_offset + offset;
u64 P = output_section.shdr.sh_addr + offset;
u8 *loc = buf + i * E::thunk_size;
memcpy(loc , data, sizeof(data));
write_adrp(loc, page(S) - page(P));
*(ul32 *)(loc + 4) |= bits(S, 11, 0) << 10;
for (Symbol<E> *sym : symbols) {
u64 S = sym->get_addr(ctx);
memcpy(buf, insn, E::thunk_size);
write_adrp(buf, page(S) - page(P));
*(ul32 *)(buf + 4) |= bits(S, 11, 0) << 10;
buf += E::thunk_size;
P += E::thunk_size;
}
}
+54 -46
View File
@@ -164,7 +164,7 @@ void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // (padding)
};
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 6) = sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr;
*(ul32 *)(buf + 6) = sym.get_got_pltgot_addr(ctx) - ctx.got->shdr.sh_addr;
} else {
static const u8 insn[] = {
0xf3, 0x0f, 0x1e, 0xfb, // endbr32
@@ -172,13 +172,13 @@ void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // (padding)
};
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 6) = sym.get_got_addr(ctx);
*(ul32 *)(buf + 6) = sym.get_got_pltgot_addr(ctx);
}
}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, const 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) {
@@ -294,7 +294,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
*(ul16 *)loc = S + A;
break;
case R_386_32:
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, dynrel);
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_386_PC8:
check(S + A - P, -(1 << 7), 1 << 7);
@@ -338,20 +338,16 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
*(ul32 *)loc = sym.get_gottp_addr(ctx) + A;
break;
case R_386_TLS_GD:
if (sym.has_tlsgd(ctx)) {
if (sym.has_tlsgd(ctx))
*(ul32 *)loc = sym.get_tlsgd_addr(ctx) + A - GOT;
} else {
relax_gd_to_le(loc, rels[i + 1], S - ctx.tp_addr);
i++;
}
else
relax_gd_to_le(loc, rels[++i], S - ctx.tp_addr);
break;
case R_386_TLS_LDM:
if (ctx.got->has_tlsld(ctx)) {
if (ctx.got->has_tlsld(ctx))
*(ul32 *)loc = ctx.got->get_tlsld_addr(ctx) + A - GOT;
} else {
relax_ld_to_le(loc, rels[i + 1], ctx.tp_addr - ctx.tls_begin);
i++;
}
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;
@@ -360,13 +356,32 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
*(ul32 *)loc = sym.esym().st_size + A;
break;
case R_386_TLS_GOTDESC:
// 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 {
static const u8 insn[] = {
0x8d, 0x05, 0, 0, 0, 0, // lea 0, %eax
};
memcpy(loc - 2, insn, sizeof(insn));
loc[-2] = 0x90;
loc[-1] = 0xb8;
*(ul32 *)loc = S + A - ctx.tp_addr;
}
break;
@@ -476,6 +491,16 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
if (sym.is_ifunc())
sym.flags |= NEEDS_GOT | 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:
@@ -493,15 +518,15 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
case R_386_GOTPC:
sym.flags |= NEEDS_GOT;
break;
case R_386_GOT32X: {
// We always want to relax GOT32X because static PIE doesn't
// work without it.
bool do_relax = !sym.is_imported && sym.is_relative() &&
relax_got32x(loc - 2);
if (!do_relax)
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;
@@ -511,31 +536,15 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
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_PC32 &&
ty != R_386_GOT32 && ty != R_386_GOT32X)
Fatal(ctx) << *this << ": TLS_GD reloc must be followed by PLT or GOT32";
// 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 && !sym.is_imported))
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_PC32 &&
ty != R_386_GOT32 && ty != R_386_GOT32X)
Fatal(ctx) << *this << ": TLS_LDM reloc must be followed by PLT or GOT32";
// 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))
@@ -544,8 +553,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
ctx.needs_tlsld = true;
break;
case R_386_TLS_GOTDESC:
if (!relax_tlsdesc(ctx, sym))
sym.flags |= NEEDS_TLSDESC;
scan_tlsdesc(ctx, sym);
break;
case R_386_TLS_LE:
check_tlsle(ctx, sym, rel);
+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.
u64 x = page(val) - page(pc);
if (val & 0x800)
x += 0x1000 - 0x1'0000'0000;
if (x & 0x8000'0000)
x += 0x1'0000'0000;
return x;
}
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_pltgot_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:
// It looks like R_LARCH_PCALA_LO12 is sometimes used for JIRL even
// though the instruction takes a 16 bit immediate rather than 12 bits.
// It is contrary to the psABI document, but GNU ld has special
// code to handle it, so we accept it too.
if ((*(ul32 *)loc & 0xfc00'0000) == 0x4c00'0000)
write_k16(loc, sign_extend(S + A, 11) >> 2);
else
write_k12(loc, S + A);
break;
case R_LARCH_PCALA_HI20:
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:
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:
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:
case R_LARCH_PCALA_HI20:
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_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[] = {
0x1e00'000c, // pcaddu18i $t0, 0
0x4c00'0180, // jirl $zero, $t0, 0
};
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, (S - P + 0x20000) >> 18);
write_k16(buf + 4, (S - P) >> 2);
buf += sizeof(insn);
P += sizeof(insn);
}
}
} // namespace mold::elf
#endif
+4 -4
View File
@@ -52,12 +52,12 @@ void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
};
memcpy(buf, insn, sizeof(insn));
*(ub32 *)(buf + 4) = sym.get_got_addr(ctx) - sym.get_plt_addr(ctx) - 2;
*(ub32 *)(buf + 4) = sym.get_got_pltgot_addr(ctx) - sym.get_plt_addr(ctx) - 2;
}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, const 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) {
@@ -126,7 +126,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
switch (rel.r_type) {
case R_68K_32:
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, dynrel);
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_68K_16:
write16(S + A);
-470
View File
@@ -1,470 +0,0 @@
#include "mold.h"
namespace mold::elf {
static constexpr i64 BIAS = 0x8000;
template <typename E>
void write_plt_header(Context<E> &ctx, u8 *buf) {}
template <typename E>
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
static const U32<E> insn[] = {
0xdf99'0000, // ld t9, 0(gp)
0x03e0'7825, // move t3, ra
0x0320'f809, // jalr t9
0x6418'0000, // daddiu t8, zero, 0
};
memcpy(buf, insn, sizeof(insn));
*(U32<E> *)buf |= sym.get_gotplt_addr(ctx) - sym.get_plt_addr(ctx);
*(U32<E> *)(buf + 12) |= sym.get_dynsym_idx(ctx);
}
template <typename E>
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {}
template <typename E>
void EhFrameSection<E>::apply_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_MIPS_64:
*(U64<E> *)loc = val;
break;
default:
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
}
template <typename E>
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 GP = ctx._gp->get_addr(ctx);
u64 GP0 = file.mips_gp0;
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 write_combined = [&](u64 val) {
if (rel.r_type2 == R_MIPS_64 && rel.r_type3 == R_NONE) {
*(U64<E> *)loc = val;
} else if (rel.r_type2 == R_MIPS_SUB && rel.r_type3 == R_MIPS_HI16) {
*(U32<E> *)loc |= ((-val + BIAS) >> 16) & 0xffff;
} else if (rel.r_type2 == R_MIPS_SUB && rel.r_type3 == R_MIPS_LO16) {
*(U32<E> *)loc |= -val & 0xffff;
} else {
Error(ctx) << *this << ": unsupported relocation combination: "
<< rel_to_string<E>(rel.r_type) << " "
<< rel_to_string<E>(rel.r_type2) << " "
<< rel_to_string<E>(rel.r_type3);
}
};
auto write32 = [&](u64 val) {
if (rel.r_type2 == R_NONE && rel.r_type3 == R_NONE)
*(U32<E> *)loc = val;
else
write_combined(val);
};
auto write_hi16 = [&](u64 val) {
if (rel.r_type2 == R_NONE && rel.r_type3 == R_NONE)
*(U32<E> *)loc |= ((val + BIAS) >> 16) & 0xffff;
else
write_combined(val);
};
auto write_lo16 = [&](u64 val) {
if (rel.r_type2 == R_NONE && rel.r_type3 == R_NONE)
*(U32<E> *)loc |= val & 0xffff;
else
write_combined(val);
};
u64 S = sym.get_addr(ctx);
u64 A = rel.r_addend;
u64 P = get_addr() + rel.r_offset;
switch (rel.r_type) {
case R_MIPS_64:
*(U64<E> *)loc = S + A;
break;
case R_MIPS_GPREL16:
if (sym.is_local(ctx))
write_lo16(S + A + GP0 - GP);
else
write_lo16(S + A - GP);
break;
case R_MIPS_GPREL32:
write32(S + A + GP0 - GP);
break;
case R_MIPS_GOT_DISP:
case R_MIPS_CALL16:
write_lo16(ctx.extra.got->get_got_addr(ctx, sym, A) - GP);
break;
case R_MIPS_GOT_PAGE:
write_lo16(ctx.extra.got->get_gotpage_got_addr(ctx, sym, A) - GP);
break;
case R_MIPS_GOT_OFST:
write_lo16(S + A - ctx.extra.got->get_gotpage_page_addr(ctx, sym, A));
break;
case R_MIPS_JALR:
break;
case R_MIPS_TLS_TPREL_HI16:
write_hi16(S + A - ctx.tp_addr);
break;
case R_MIPS_TLS_TPREL_LO16:
write_lo16(S + A - ctx.tp_addr);
break;
case R_MIPS_TLS_GOTTPREL:
write_lo16(ctx.extra.got->get_gottp_addr(ctx, sym) - GP);
break;
case R_MIPS_TLS_DTPREL_HI16:
write_hi16(S + A - ctx.dtp_addr);
break;
case R_MIPS_TLS_DTPREL_LO16:
write_lo16(S + A - ctx.dtp_addr);
break;
case R_MIPS_TLS_GD:
write_lo16(ctx.extra.got->get_tlsgd_addr(ctx, sym) + A - GP);
break;
case R_MIPS_TLS_LDM:
write_lo16(ctx.extra.got->get_tlsld_addr(ctx) + A - GP);
break;
default:
unreachable();
}
}
}
template <typename E>
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_MIPS_32:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(U32<E> *)loc = *val;
else
*(U32<E> *)loc = S + A;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
<< rel;
}
}
}
template <typename E>
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];
switch (rel.r_type) {
case R_MIPS_GOT_DISP:
case R_MIPS_CALL16: {
std::scoped_lock lock(ctx.extra.got->mu);
ctx.extra.got->got_syms.push_back({&sym, rel.r_addend});
break;
}
case R_MIPS_GOT_PAGE: {
std::scoped_lock lock(ctx.extra.got->mu);
ctx.extra.got->gotpage_syms.push_back({&sym, rel.r_addend});
break;
}
case R_MIPS_TLS_GOTTPREL: {
std::scoped_lock lock(ctx.extra.got->mu);
assert(rel.r_addend == 0);
ctx.extra.got->gottp_syms.push_back(&sym);
break;
}
case R_MIPS_TLS_TPREL_HI16:
case R_MIPS_TLS_TPREL_LO16:
check_tlsle(ctx, sym, rel);
break;
case R_MIPS_TLS_GD: {
std::scoped_lock lock(ctx.extra.got->mu);
assert(rel.r_addend == 0);
ctx.extra.got->tlsgd_syms.push_back(&sym);
break;
}
case R_MIPS_TLS_LDM:
ctx.extra.got->needs_tlsld = true;
break;
case R_MIPS_64:
case R_MIPS_GPREL16:
case R_MIPS_GPREL32:
case R_MIPS_GOT_OFST:
case R_MIPS_JALR:
case R_MIPS_TLS_DTPREL_HI16:
case R_MIPS_TLS_DTPREL_LO16:
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
template <typename E>
bool MipsGotSection<E>::SymbolAddend::operator<(const SymbolAddend &other) const {
return std::tuple(sym->file->priority, sym->sym_idx, addend) <
std::tuple(other.sym->file->priority, other.sym->sym_idx, other.addend);
};
template <typename E>
static bool compare(const Symbol<E> *a, const Symbol<E> *b) {
return std::tuple(a->file->priority, a->sym_idx) <
std::tuple(b->file->priority, b->sym_idx);
};
template <typename E>
u64 MipsGotSection<E>::SymbolAddend::get_addr(Context<E> &ctx, i64 flags) const {
return sym->get_addr(ctx, flags) + addend;
}
template <typename E>
u64
MipsGotSection<E>::get_got_addr(Context<E> &ctx, Symbol<E> &sym, i64 addend) const {
auto it = std::lower_bound(got_syms.begin(), got_syms.end(),
SymbolAddend{&sym, addend});
assert(it != got_syms.end());
i64 idx = it - got_syms.begin();
return this->shdr.sh_addr + idx * sizeof(Word<E>);
}
template <typename E>
u64 MipsGotSection<E>::get_gotpage_got_addr(Context<E> &ctx, Symbol<E> &sym,
i64 addend) const {
auto it = std::lower_bound(gotpage_syms.begin(), gotpage_syms.end(),
SymbolAddend{&sym, addend});
assert(it != gotpage_syms.end());
i64 idx = got_syms.size() + (it - gotpage_syms.begin());
return this->shdr.sh_addr + idx * sizeof(Word<E>);
}
template <typename E>
u64 MipsGotSection<E>::get_gotpage_page_addr(Context<E> &ctx, Symbol<E> &sym,
i64 addend) const {
auto it = std::lower_bound(gotpage_syms.begin(), gotpage_syms.end(),
SymbolAddend{&sym, addend});
assert(it != gotpage_syms.end());
return it->get_addr(ctx);
}
template <typename E>
u64 MipsGotSection<E>::get_tlsgd_addr(Context<E> &ctx, Symbol<E> &sym) const {
auto it = std::lower_bound(tlsgd_syms.begin(), tlsgd_syms.end(),
&sym, compare<E>);
assert(it != tlsgd_syms.end());
i64 idx = got_syms.size() + gotpage_syms.size() + (it - tlsgd_syms.begin()) * 2;
return this->shdr.sh_addr + idx * sizeof(Word<E>);
}
template <typename E>
u64 MipsGotSection<E>::get_gottp_addr(Context<E> &ctx, Symbol<E> &sym) const {
auto it = std::lower_bound(gottp_syms.begin(), gottp_syms.end(),
&sym, compare<E>);
assert(it != gottp_syms.end());
i64 idx = got_syms.size() + gotpage_syms.size() + tlsgd_syms.size() * 2 +
(it - gottp_syms.begin());
return this->shdr.sh_addr + idx * sizeof(Word<E>);
}
template <typename E>
u64 MipsGotSection<E>::get_tlsld_addr(Context<E> &ctx) const {
assert(needs_tlsld);
i64 idx = got_syms.size() + gotpage_syms.size() + tlsgd_syms.size() * 2 +
gottp_syms.size();
return this->shdr.sh_addr + idx * sizeof(Word<E>);
}
template <typename E>
std::vector<typename MipsGotSection<E>::GotEntry>
MipsGotSection<E>::get_got_entries(Context<E> &ctx) const {
std::vector<GotEntry> entries;
auto add = [&](GotEntry ent) { entries.push_back(ent); };
// Create GOT entries for ordinary symbols
for (const SymbolAddend &ent : got_syms) {
// If a symbol is imported, let the dynamic linker to resolve it.
if (ent.sym->is_imported) {
add({0, E::R_GLOB_DAT, ent.sym});
continue;
}
// If we know an address at link-time, fill that GOT entry now.
// It may need a base relocation, though.
if (ctx.arg.pic && ent.sym->is_relative())
add({ent.get_addr(ctx, NO_PLT), E::R_RELATIVE});
else
add({ent.get_addr(ctx, NO_PLT)});
}
// Create GOT entries for GOT_PAGE and GOT_OFST relocs
for (const SymbolAddend &ent : gotpage_syms) {
if (ctx.arg.pic && ent.sym->is_relative())
add({ent.get_addr(ctx), E::R_RELATIVE});
else
add({ent.get_addr(ctx)});
}
// Create GOT entries for TLVs
for (Symbol<E> *sym : tlsgd_syms) {
if (sym->is_imported) {
// If a symbol is imported, let the dynamic linker to resolve it.
add({0, E::R_DTPMOD, sym});
add({0, E::R_DTPOFF, sym});
} else if (ctx.arg.shared) {
// If we are creating a shared library, we know the TLV's offset
// within the current TLS block. We don't know the module ID though.
add({0, E::R_DTPMOD});
add({sym->get_addr(ctx) - ctx.dtp_addr});
} else {
// If we are creating an executable, we know both the module ID and the
// offset. Module ID 1 indicates the main executable.
add({1});
add({sym->get_addr(ctx) - ctx.dtp_addr});
}
}
for (Symbol<E> *sym : gottp_syms) {
if (sym->is_imported) {
// If we know nothing about the symbol, let the dynamic linker
// to fill the GOT entry.
add({0, E::R_TPOFF, sym});
} else if (ctx.arg.shared) {
// If we know the offset within the current thread vector,
// let the dynamic linker to adjust it.
add({sym->get_addr(ctx) - ctx.tls_begin, E::R_TPOFF});
} else {
// Otherwise, we know the offset from the thread pointer (TP) at
// link-time, so we can fill the GOT entry directly.
add({sym->get_addr(ctx) - ctx.tp_addr});
}
}
if (needs_tlsld) {
if (ctx.arg.shared)
add({0, E::R_DTPMOD});
else
add({1}); // 1 means the main executable
add({0});
}
return entries;
}
template <typename E>
void MipsGotSection<E>::update_shdr(Context<E> &ctx) {
// Finalize got_syms
sort(got_syms);
remove_duplicates(got_syms);
this->shdr.sh_size = got_syms.size() * sizeof(Word<E>);
// Finalize gotpage_syms
sort(gotpage_syms);
remove_duplicates(gotpage_syms);
this->shdr.sh_size += gotpage_syms.size() * sizeof(Word<E>);
// Finalize tlsgd_syms
sort(tlsgd_syms, compare<E>);
remove_duplicates(tlsgd_syms);
this->shdr.sh_size += tlsgd_syms.size() * 2 * sizeof(Word<E>);
// Finalize gottp_syms
sort(gottp_syms, compare<E>);
remove_duplicates(gottp_syms);
this->shdr.sh_size += gottp_syms.size() * sizeof(Word<E>);
if (needs_tlsld)
this->shdr.sh_size += 2 * sizeof(Word<E>);
}
template <typename E>
i64 MipsGotSection<E>::get_reldyn_size(Context<E> &ctx) const {
i64 n = 0;
for (GotEntry &ent : get_got_entries(ctx))
if (ent.r_type != R_NONE)
n++;
return n;
}
template <typename E>
void MipsGotSection<E>::copy_buf(Context<E> &ctx) {
U64<E> *buf = (U64<E> *)(ctx.buf + this->shdr.sh_offset);
memset(buf, 0, this->shdr.sh_size);
ElfRel<E> *dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
this->reldyn_offset);
for (i64 i = 0; GotEntry &ent : get_got_entries(ctx)) {
if (ent.r_type != R_NONE)
*dynrel++ = ElfRel<E>(this->shdr.sh_addr + i * sizeof(Word<E>),
ent.r_type,
ent.sym ? ent.sym->get_dynsym_idx(ctx) : 0,
ent.val);
buf[i++] = ent.val;
}
}
#define INSTANTIATE(E) \
template void write_plt_header(Context<E> &, u8 *); \
template void write_plt_entry(Context<E> &, u8 *, Symbol<E> &); \
template void write_pltgot_entry(Context<E> &, u8 *, Symbol<E> &); \
template void \
EhFrameSection<E>::apply_reloc(Context<E> &, const ElfRel<E> &, u64, u64); \
template void InputSection<E>::apply_reloc_alloc(Context<E> &, u8 *); \
template void InputSection<E>::apply_reloc_nonalloc(Context<E> &, u8 *); \
template void InputSection<E>::scan_relocations(Context<E> &); \
template class MipsGotSection<E>;
INSTANTIATE(MIPS64LE);
INSTANTIATE(MIPS64BE);
} // namespace mold::elf
+22 -20
View File
@@ -120,14 +120,14 @@ void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
memcpy(buf, plt_entry, sizeof(plt_entry));
ub32 *loc = (ub32 *)buf;
i64 offset = sym.get_got_addr(ctx) - sym.get_plt_addr(ctx) - 8;
i64 offset = sym.get_got_pltgot_addr(ctx) - sym.get_plt_addr(ctx) - 8;
loc[4] |= higha(offset);
loc[5] |= lo(offset);
}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, const 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) {
@@ -153,7 +153,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
file.reldyn_offset + this->reldyn_offset);
u64 GOT2 = file.ppc32_got2 ? file.ppc32_got2->get_addr() : 0;
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];
@@ -172,7 +172,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
switch (rel.r_type) {
case R_PPC_ADDR32:
case R_PPC_UADDR32:
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, dynrel);
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_PPC_ADDR14:
*(ub32 *)loc |= bits(S + A, 15, 2) << 2;
@@ -409,8 +409,6 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
template <>
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + output_section.shdr.sh_offset + offset;
static const ub32 local_thunk[] = {
// Get this thunk's address
0x7c08'02a6, // mflr r0
@@ -429,22 +427,26 @@ void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
static_assert(E::thunk_size == sizeof(plt_entry));
static_assert(E::thunk_size == sizeof(local_thunk));
for (i64 i = 0; i < symbols.size(); i++) {
ub32 *loc = (ub32 *)(buf + i * E::thunk_size);
Symbol<E> &sym = *symbols[i];
u8 *buf = ctx.buf + output_section.shdr.sh_offset + offset;
u64 P = output_section.shdr.sh_addr + offset;
if (sym.has_plt(ctx)) {
memcpy(loc, plt_entry, sizeof(plt_entry));
u64 got = sym.has_got(ctx) ? sym.get_got_addr(ctx) : sym.get_gotplt_addr(ctx);
i64 val = got - get_addr(i) - 8;
loc[4] |= higha(val);
loc[5] |= lo(val);
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 {
memcpy(loc, local_thunk, sizeof(local_thunk));
i64 val = sym.get_addr(ctx) - get_addr(i) - 8;
loc[4] |= higha(val);
loc[5] |= lo(val);
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;
}
}
+30 -31
View File
@@ -72,22 +72,22 @@ void write_plt_header(Context<E> &ctx, u8 *buf) {
0x7d88'02a6, // mflr r12
0x429f'0005, // bcl 20, 31, 4 // obtain PC
0x7d68'02a6, // mflr r11
0xe84b'0024, // ld r2,36(r11)
0x7d88'03a6, // mtlr r12
0x7d62'5a14, // add r11,r2,r11
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
// .quad .got.plt - .plt - 8
0x0000'0000,
0x0000'0000,
};
static_assert(sizeof(insn) == E::plt_hdr_size);
memcpy(buf, insn, sizeof(insn));
*(ub64 *)(buf + 44) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 8;
i64 val = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 8;
*(ub32 *)(buf + 16) |= higha(val);
*(ub32 *)(buf + 20) |= lo(val);
}
template <>
@@ -129,8 +129,8 @@ template <>
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, const 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) {
@@ -183,10 +183,10 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
switch (rel.r_type) {
case R_PPC64_ADDR64:
apply_toc_rel(ctx, sym, rel, loc, S, A, P, dynrel);
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);
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);
@@ -410,8 +410,6 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
template <>
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + output_section.shdr.sh_offset + offset;
// 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.
@@ -466,26 +464,27 @@ void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
static_assert(E::thunk_size == sizeof(plt_thunk));
static_assert(E::thunk_size == sizeof(local_thunk));
for (i64 i = 0; i < symbols.size(); i++) {
Symbol<E> &sym = *symbols[i];
ub32 *loc = (ub32 *)(buf + i * E::thunk_size);
u8 *buf = ctx.buf + output_section.shdr.sh_offset + offset;
if (sym.has_got(ctx)) {
memcpy(loc, pltgot_thunk, sizeof(pltgot_thunk));
i64 val = sym.get_got_addr(ctx) - ctx.extra.TOC->value;
loc[1] |= higha(val);
loc[2] |= lo(val);
} else if(sym.has_plt(ctx)) {
memcpy(loc, plt_thunk, sizeof(plt_thunk));
i64 val = sym.get_gotplt_addr(ctx) - ctx.extra.TOC->value;
loc[1] |= higha(val);
loc[2] |= lo(val);
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 {
memcpy(loc, local_thunk, sizeof(local_thunk));
i64 val = sym.get_addr(ctx, NO_OPD) - ctx.extra.TOC->value;
loc[0] |= higha(val);
loc[1] |= lo(val);
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;
}
}
@@ -520,7 +519,7 @@ struct OpdSymbol {
};
static Symbol<E> *
get_opd_sym_at(Context<E> &ctx, std::span<OpdSymbol> syms, u64 offset) {
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;
@@ -615,7 +614,7 @@ void ppc64v1_rewrite_opd(Context<E> &ctx) {
if (sym.get_input_section() != opd)
continue;
Symbol<E> *real_sym = get_opd_sym_at(ctx, opd_syms, r.r_addend);
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;
+59 -41
View File
@@ -92,8 +92,10 @@ 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 u64 prefix34(u64 x) {
return bits(x, 33, 16) | (bits(x, 15, 0) << 32);
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
@@ -112,25 +114,26 @@ void write_plt_header(Context<E> &ctx, u8 *buf) {
0x7c08'03a6, // mtlr r0
// Compute the PLT entry index
0xe80b'002c, // ld r0, 44(r11)
0x7d8b'6050, // subf r12, r11, r12
0x7d60'5a14, // add r11, r0, r11
0x380c'ffcc, // addi r0, r12, -52
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
// .quad .got.plt - .plt - 8
0x0000'0000,
0x0000'0000,
};
memcpy(buf, insn, sizeof(insn));
*(ul64 *)(buf + 52) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 8;
i64 val = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 8;
*(ul32 *)(buf + 28) |= higha(val);
*(ul32 *)(buf + 32) |= lo(val);
}
template <>
@@ -147,8 +150,8 @@ template <>
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, const 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) {
@@ -204,14 +207,14 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
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) + 4; };
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);
apply_toc_rel(ctx, sym, rel, loc, S, A, P, &dynrel);
else
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, dynrel);
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_PPC64_TOC16_HA:
*(ul16 *)loc = ha(S + A - TOC);
@@ -278,10 +281,10 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
case R_PPC64_PLT_PCREL34:
case R_PPC64_PLT_PCREL34_NOTOC:
case R_PPC64_GOT_PCREL34:
*(ul64 *)loc |= prefix34(G + GOT - P);
write34(loc, G + GOT - P);
break;
case R_PPC64_PCREL34:
*(ul64 *)loc |= prefix34(S + A - P);
write34(loc, S + A - P);
break;
case R_PPC64_GOT_TPREL16_HA:
*(ul16 *)loc = ha(sym.get_gottp_addr(ctx) - TOC);
@@ -290,7 +293,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
*(ul16 *)loc |= (sym.get_gottp_addr(ctx) - TOC) & 0xfffc;
break;
case R_PPC64_GOT_TPREL_PCREL34:
*(ul64 *)loc |= prefix34(sym.get_gottp_addr(ctx) - P);
write34(loc, sym.get_gottp_addr(ctx) - P);
break;
case R_PPC64_GOT_TLSGD16_HA:
*(ul16 *)loc = ha(sym.get_tlsgd_addr(ctx) - TOC);
@@ -299,7 +302,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
*(ul16 *)loc = lo(sym.get_tlsgd_addr(ctx) - TOC);
break;
case R_PPC64_GOT_TLSGD_PCREL34:
*(ul64 *)loc |= prefix34(sym.get_tlsgd_addr(ctx) - P);
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);
@@ -308,7 +311,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
*(ul16 *)loc = lo(ctx.got->get_tlsld_addr(ctx) - TOC);
break;
case R_PPC64_GOT_TLSLD_PCREL34:
*(ul64 *)loc |= prefix34(ctx.got->get_tlsld_addr(ctx) - P);
write34(loc, ctx.got->get_tlsld_addr(ctx) - P);
break;
case R_PPC64_DTPREL16_HA:
*(ul16 *)loc = ha(S + A - ctx.dtp_addr);
@@ -317,7 +320,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
*(ul16 *)loc = lo(S + A - ctx.dtp_addr);
break;
case R_PPC64_DTPREL34:
*(ul64 *)loc |= prefix34(S + A - ctx.dtp_addr);
write34(loc, S + A - ctx.dtp_addr);
break;
case R_PPC64_TPREL16_HA:
*(ul16 *)loc = ha(S + A - ctx.tp_addr);
@@ -325,6 +328,9 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
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:
@@ -442,6 +448,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
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:
@@ -478,12 +485,11 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
template <>
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + output_section.shdr.sh_offset + offset;
// 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
@@ -492,6 +498,7 @@ void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
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
@@ -502,6 +509,7 @@ void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
// 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
@@ -510,6 +518,7 @@ void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
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
@@ -521,34 +530,43 @@ void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
static_assert(E::thunk_size == sizeof(local_thunk));
static_assert(E::thunk_size == sizeof(local_thunk_power10));
for (i64 i = 0; i < symbols.size(); i++) {
Symbol<E> &sym = *symbols[i];
ul32 *loc = (ul32 *)(buf + i * E::thunk_size);
u8 *buf = ctx.buf + output_section.shdr.sh_offset + offset;
u64 P = output_section.shdr.sh_addr + offset;
u64 TOC = ctx.extra.TOC->value;
if (sym.has_plt(ctx)) {
u64 got = sym.has_got(ctx) ? sym.get_got_addr(ctx) : sym.get_gotplt_addr(ctx);
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(loc, plt_thunk_power10, E::thunk_size);
*(ul64 *)(loc + 1) |= prefix34(got - get_addr(i) - 4);
memcpy(buf, plt_thunk_power10, E::thunk_size);
write34(buf + 8, got - P - 8);
} else {
i64 val = got - ctx.extra.TOC->value;
memcpy(loc, plt_thunk, E::thunk_size);
loc[1] |= higha(val);
loc[2] |= lo(val);
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(loc, local_thunk_power10, E::thunk_size);
*(ul64 *)(loc + 1) |= prefix34(sym.get_addr(ctx) - get_addr(i) - 4);
memcpy(buf, local_thunk_power10, E::thunk_size);
write34(buf + 8, S - P - 8);
} else {
i64 val = sym.get_addr(ctx) - ctx.extra.TOC->value;
memcpy(loc, local_thunk, E::thunk_size);
loc[1] |= higha(val);
loc[2] |= lo(val);
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
+483 -124
View File
@@ -67,13 +67,19 @@
//
// https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc
#if MOLD_RV64LE || MOLD_RV64BE || MOLD_RV32LE || MOLD_RV32BE
#include "elf.h"
#include "mold.h"
#include <regex>
#include <tbb/parallel_for.h>
#include <tbb/parallel_for_each.h>
namespace mold::elf {
using E = MOLD_TARGET;
static void write_itype(u8 *loc, u32 val) {
*(ul32 *)loc &= 0b000000'00000'11111'111'11111'1111111;
*(ul32 *)loc |= bits(val, 11, 0) << 20;
@@ -107,6 +113,11 @@ static void write_jtype(u8 *loc, u32 val) {
bit(val, 11) << 20 | bits(val, 19, 12) << 12;
}
static void write_citype(u8 *loc, u32 val) {
*(ul16 *)loc &= 0b111'0'11111'00000'11;
*(ul16 *)loc |= bit(val, 5) << 12 | bits(val, 4, 0) << 2;
}
static void write_cbtype(u8 *loc, u32 val) {
*(ul16 *)loc &= 0b111'000'111'00000'11;
*(ul16 *)loc |= bit(val, 8) << 12 | bit(val, 4) << 11 | bit(val, 3) << 10 |
@@ -122,27 +133,14 @@ static void write_cjtype(u8 *loc, u32 val) {
bit(val, 1) << 3 | bit(val, 5) << 2;
}
static void overwrite_uleb(u8 *loc, u64 val) {
while (*loc & 0b1000'0000) {
*loc++ = 0b1000'0000 | (val & 0b0111'1111);
val >>= 7;
}
*loc = val & 0b0111'1111;
}
// Returns the rd register of an R/I/U/J-type instruction.
static u32 get_rd(u32 val) {
return bits(val, 11, 7);
}
static void set_rs1(u8 *loc, u32 rs1) {
assert(rs1 < 32);
*(ul32 *)loc &= 0b111111'11111'00000'111'11111'1111111;
*(ul32 *)loc |= rs1 << 15;
}
template <typename E>
void write_plt_header(Context<E> &ctx, u8 *buf) {
template <>
void write_plt_header<E>(Context<E> &ctx, u8 *buf) {
static const ul32 insn_64[] = {
0x0000'0397, // auipc t2, %pcrel_hi(.got.plt)
0x41c3'0333, // sub t1, t1, t3 # .plt entry + hdr + 12
@@ -165,13 +163,10 @@ void write_plt_header(Context<E> &ctx, u8 *buf) {
0x000e'0067, // jr t3
};
if constexpr (E::is_64)
memcpy(buf, insn_64, sizeof(insn_64));
else
memcpy(buf, insn_32, sizeof(insn_32));
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_utype(buf, gotplt - plt);
write_itype(buf + 8, gotplt - plt);
write_itype(buf + 16, gotplt - plt);
@@ -191,35 +186,29 @@ static const ul32 plt_entry_32[] = {
0x0000'0013, // nop
};
template <typename E>
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
if constexpr (E::is_64)
memcpy(buf, plt_entry_64, sizeof(plt_entry_64));
else
memcpy(buf, plt_entry_32, sizeof(plt_entry_32));
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_utype(buf, gotplt - plt);
write_itype(buf + 4, gotplt - plt);
}
template <typename E>
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
if constexpr (E::is_64)
memcpy(buf, plt_entry_64, sizeof(plt_entry_64));
else
memcpy(buf, plt_entry_32, sizeof(plt_entry_32));
u64 got = sym.get_got_addr(ctx);
template <>
void write_pltgot_entry<E>(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
u64 got = sym.get_got_pltgot_addr(ctx);
u64 plt = sym.get_plt_addr(ctx);
memcpy(buf, E::is_64 ? plt_entry_64 : plt_entry_32, E::plt_size);
write_utype(buf, got - plt);
write_itype(buf + 4, got - plt);
}
template <typename E>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {
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) {
@@ -260,9 +249,17 @@ void EhFrameSection<E>::apply_reloc(Context<E> &ctx, const ElfRel<E> &rel,
}
}
template <typename E>
static inline bool is_hi20(const ElfRel<E> &rel) {
u32 ty = rel.r_type;
return ty == R_RISCV_GOT_HI20 || ty == R_RISCV_TLS_GOT_HI20 ||
ty == R_RISCV_TLS_GD_HI20 || ty == R_RISCV_PCREL_HI20 ||
ty == R_RISCV_TLSDESC_HI20;
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
u64 GP = ctx.__global_pointer ? ctx.__global_pointer->get_addr(ctx) : 0;
ElfRel<E> *dynrel = nullptr;
if (ctx.reldyn)
@@ -291,28 +288,24 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
};
auto find_paired_reloc = [&] {
Symbol<E> &sym = *file.symbols[rels[i].r_sym];
assert(sym.get_input_section() == this);
if (sym.value < r_offset) {
if (sym.value <= rels[i].r_offset - get_r_delta(i)) {
for (i64 j = i - 1; j >= 0; j--)
if (u32 ty = rels[j].r_type;
ty == R_RISCV_GOT_HI20 || ty == R_RISCV_TLS_GOT_HI20 ||
ty == R_RISCV_TLS_GD_HI20 || ty == R_RISCV_PCREL_HI20)
if (sym.value == rels[j].r_offset - get_r_delta(j))
return j;
if (is_hi20(rels[j]) && sym.value == rels[j].r_offset - get_r_delta(j))
return j;
} else {
for (i64 j = i + 1; j < rels.size(); j++)
if (u32 ty = rels[j].r_type;
ty == R_RISCV_GOT_HI20 || ty == R_RISCV_TLS_GOT_HI20 ||
ty == R_RISCV_TLS_GD_HI20 || ty == R_RISCV_PCREL_HI20)
if (sym.value == rels[j].r_offset - get_r_delta(j))
return j;
if (is_hi20(rels[j]) && sym.value == rels[j].r_offset - get_r_delta(j))
return j;
}
Fatal(ctx) << *this << ": paired relocation is missing: " << i;
};
auto get_rd = [&](i64 offset) {
// Returns the rd register of an R/I/U/J-type instruction.
return bits(*(ul32 *)(contents.data() + offset), 11, 7);
};
u64 S = sym.get_addr(ctx);
u64 A = rel.r_addend;
u64 P = get_addr() + r_offset;
@@ -324,11 +317,11 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
if constexpr (E::is_64)
*(U32<E> *)loc = S + A;
else
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, dynrel);
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_RISCV_64:
assert(E::is_64);
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, dynrel);
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_RISCV_BRANCH:
check(S + A - P, -(1 << 12), 1 << 12);
@@ -340,27 +333,30 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
break;
case R_RISCV_CALL:
case R_RISCV_CALL_PLT: {
u32 rd = get_rd(*(ul32 *)(contents.data() + rel.r_offset + 4));
i64 val = S + A - P;
i64 rd = get_rd(rel.r_offset + 4);
// Calling an undefined weak symbol does not make sense.
// We make such call into an infinite loop. This should
// help debugging of a faulty program.
if (sym.esym().is_undef_weak())
val = 0;
if (removed_bytes == 4) {
// auipc + jalr -> jal
*(ul32 *)loc = (rd << 7) | 0b1101111;
write_jtype(loc, S + A - P);
write_jtype(loc, val);
} else if (removed_bytes == 6 && rd == 0) {
// auipc + jalr -> c.j
*(ul16 *)loc = 0b101'00000000000'01;
write_cjtype(loc, S + A - P);
write_cjtype(loc, val);
} else if (removed_bytes == 6 && rd == 1) {
// auipc + jalr -> c.jal
assert(!E::is_64);
*(ul16 *)loc = 0b001'00000000000'01;
write_cjtype(loc, S + A - P);
write_cjtype(loc, val);
} else {
assert(removed_bytes == 0);
// 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.
u64 val = sym.esym().is_undef_weak() ? 0 : S + A - P;
check(val, -(1LL << 31), 1LL << 31);
write_utype(loc, val);
write_itype(loc + 4, val);
@@ -380,7 +376,9 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
write_utype(loc, S + A - P);
break;
case R_RISCV_PCREL_LO12_I:
case R_RISCV_PCREL_LO12_S: {
case R_RISCV_PCREL_LO12_S:
case R_RISCV_GPREL_LO12_I:
case R_RISCV_GPREL_LO12_S: {
i64 idx2 = find_paired_reloc();
const ElfRel<E> &rel2 = rels[idx2];
Symbol<E> &sym2 = *file.symbols[rel2.r_sym];
@@ -404,19 +402,27 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
case R_RISCV_PCREL_HI20:
val = S + A - P;
break;
case R_RISCV_GPREL_HI20:
val = S + A - GP;
break;
default:
unreachable();
}
if (rel.r_type == R_RISCV_PCREL_LO12_I)
if (rel.r_type == R_RISCV_PCREL_LO12_I ||
rel.r_type == R_RISCV_GPREL_LO12_I)
write_itype(loc, val);
else
write_stype(loc, val);
break;
}
case R_RISCV_HI20:
assert(removed_bytes == 0 || removed_bytes == 4);
if (removed_bytes == 0) {
if (removed_bytes == 2) {
// Rewrite LUI with C.LUI
i64 rd = get_rd(rel.r_offset);
*(ul16 *)loc = 0b011'0'00000'00000'01 | (rd << 7);
write_citype(loc, (S + A + 0x800) >> 12);
} else if (removed_bytes == 0) {
check(S + A, -(1LL << 31), 1LL << 31);
write_utype(loc, S + A);
}
@@ -429,9 +435,9 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
write_stype(loc, S + A);
// Rewrite `lw t1, 0(t0)` with `lw t1, 0(x0)` if the address is
// accessible relative to the zero register. If the upper 20 bits
// are all zero, the corresponding LUI might have been removed.
if (bits(S + A, 31, 12) == 0)
// accessible relative to the zero register because if that's the
// case, corresponding LUI might have been removed by relaxation.
if (sign_extend(S + A, 11) == S + A)
set_rs1(loc, 0);
break;
case R_RISCV_TPREL_HI20:
@@ -459,6 +465,58 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
set_rs1(loc, 4);
break;
}
case R_RISCV_TLSDESC_HI20:
if (removed_bytes == 0)
write_utype(loc, sym.get_tlsdesc_addr(ctx) + A - P);
break;
case R_RISCV_TLSDESC_LOAD_LO12:
case R_RISCV_TLSDESC_ADD_LO12:
case R_RISCV_TLSDESC_CALL: {
i64 idx2 = find_paired_reloc();
const ElfRel<E> &rel2 = rels[idx2];
Symbol<E> &sym2 = *file.symbols[rel2.r_sym];
u64 S = sym2.get_addr(ctx);
u64 A = rel2.r_addend;
u64 P = get_addr() + rel2.r_offset - get_r_delta(idx2);
switch (rel.r_type) {
case R_RISCV_TLSDESC_LOAD_LO12:
if (sym2.has_tlsdesc(ctx))
write_itype(loc, sym2.get_tlsdesc_addr(ctx) + A - P);
break;
case R_RISCV_TLSDESC_ADD_LO12:
if (sym2.has_tlsdesc(ctx)) {
write_itype(loc, sym2.get_tlsdesc_addr(ctx) + A - P);
} else if (sym2.has_gottp(ctx)) {
*(ul32 *)loc = 0x517; // auipc a0,<hi20>
write_utype(loc, sym2.get_gottp_addr(ctx) + A - P);
} else {
if (removed_bytes == 0) {
*(ul32 *)loc = 0x537; // lui a0,<hi20>
write_utype(loc, S + A - ctx.tp_addr);
}
}
break;
case R_RISCV_TLSDESC_CALL:
if (sym2.has_tlsdesc(ctx)) {
// Do nothing
} else if (sym2.has_gottp(ctx)) {
// {ld,lw} a0, <lo12>(a0)
*(ul32 *)loc = E::is_64 ? 0x53503 : 0x52503;
write_itype(loc, sym2.get_gottp_addr(ctx) + A - P);
} else {
i64 val = S + A - ctx.tp_addr;
if (sign_extend(val, 11) == val)
*(ul32 *)loc = 0x513; // addi a0,zero,<lo12>
else
*(ul32 *)loc = 0x50513; // addi a0,a0,<lo12>
write_itype(loc, val);
}
break;
}
break;
}
case R_RISCV_ADD8:
loc += S + A;
break;
@@ -509,6 +567,10 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
check(S + A - P, -(1 << 11), 1 << 11);
write_cjtype(loc, S + A - P);
break;
case R_RISCV_GPREL_HI20:
check(S + A - GP, -(1LL << 31), 1LL << 31);
write_utype(loc, S + A - GP);
break;
case R_RISCV_SUB6:
*loc = (*loc & 0b1100'0000) | ((*loc - S - A) & 0b0011'1111);
break;
@@ -531,19 +593,16 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
case R_RISCV_SET_ULEB128:
overwrite_uleb(loc, S + A);
break;
case R_RISCV_SUB_ULEB128: {
u8 *p = loc;
u64 val = read_uleb(p);
overwrite_uleb(loc, val - (S + A));
case R_RISCV_SUB_ULEB128:
overwrite_uleb(loc, read_uleb(loc) - S - A);
break;
}
default:
unreachable();
}
}
}
template <typename E>
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<const ElfRel<E>> rels = get_rels(ctx);
@@ -614,12 +673,9 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
case R_RISCV_SET_ULEB128:
overwrite_uleb(loc, S + A);
break;
case R_RISCV_SUB_ULEB128: {
u8 *p = loc;
u64 val = read_uleb(p);
overwrite_uleb(loc, val - S - A);
case R_RISCV_SUB_ULEB128:
overwrite_uleb(loc, read_uleb(loc) - S - A);
break;
}
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
<< rel;
@@ -628,7 +684,7 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
}
}
template <typename E>
template <>
void InputSection<E>::copy_contents_riscv(Context<E> &ctx, u8 *buf) {
// If a section is not relaxed, we can copy it as a one big chunk.
if (extra.r_deltas.empty()) {
@@ -655,7 +711,7 @@ void InputSection<E>::copy_contents_riscv(Context<E> &ctx, u8 *buf) {
memcpy(buf, contents.data() + pos, contents.size() - pos);
}
template <typename E>
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
@@ -703,7 +759,11 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
case R_RISCV_TLS_GD_HI20:
sym.flags |= NEEDS_TLSGD;
break;
case R_RISCV_TLSDESC_HI20:
scan_tlsdesc(ctx, sym);
break;
case R_RISCV_32_PCREL:
case R_RISCV_PCREL_HI20:
scan_pcrel(ctx, sym, rel);
break;
case R_RISCV_TPREL_HI20:
@@ -712,13 +772,19 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
case R_RISCV_TPREL_ADD:
check_tlsle(ctx, sym, rel);
break;
case R_RISCV_GPREL_HI20:
if (ctx.arg.shared)
Fatal(ctx) << *this << ": R_RISCV_GPREL_HI20 may not be used with -shared";
break;
case R_RISCV_BRANCH:
case R_RISCV_JAL:
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_TLSDESC_LOAD_LO12:
case R_RISCV_TLSDESC_ADD_LO12:
case R_RISCV_TLSDESC_CALL:
case R_RISCV_ADD8:
case R_RISCV_ADD16:
case R_RISCV_ADD32:
@@ -730,6 +796,8 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
case R_RISCV_ALIGN:
case R_RISCV_RVC_BRANCH:
case R_RISCV_RVC_JUMP:
case R_RISCV_GPREL_LO12_I:
case R_RISCV_GPREL_LO12_S:
case R_RISCV_RELAX:
case R_RISCV_SUB6:
case R_RISCV_SET6:
@@ -745,14 +813,37 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
}
}
template <typename E>
static bool is_resizable(Context<E> &ctx, InputSection<E> *isec) {
template <>
u64 get_eflags(Context<E> &ctx) {
std::vector<ObjectFile<E> *> objs = ctx.objs;
std::erase(objs, ctx.internal_obj);
if (objs.empty())
return 0;
u32 ret = objs[0]->get_ehdr().e_flags;
for (i64 i = 1; i < objs.size(); i++) {
u32 flags = objs[i]->get_ehdr().e_flags;
if (flags & EF_RISCV_RVC)
ret |= EF_RISCV_RVC;
if ((flags & EF_RISCV_FLOAT_ABI) != (ret & EF_RISCV_FLOAT_ABI))
Error(ctx) << *objs[i] << ": cannot link object files with different"
<< " floating-point ABI from " << *objs[0];
if ((flags & EF_RISCV_RVE) != (ret & EF_RISCV_RVE))
Error(ctx) << *objs[i] << ": cannot link object files with different"
<< " EF_RISCV_RVE from " << *objs[0];
}
return ret;
}
static bool is_resizable(InputSection<E> *isec) {
return isec && isec->is_alive && (isec->shdr().sh_flags & SHF_ALLOC) &&
(isec->shdr().sh_flags & SHF_EXECINSTR);
}
// Returns the distance between a relocated place and a symbol.
template <typename E>
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
@@ -774,11 +865,14 @@ static i64 compute_distance(Context<E> &ctx, Symbol<E> &sym,
}
// Scan relocations to shrink sections.
template <typename E>
static void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc) {
std::span<const ElfRel<E>> rels = isec.get_rels(ctx);
isec.extra.r_deltas.resize(rels.size() + 1);
auto get_rd = [&](i64 offset) {
return bits(*(ul32 *)(isec.contents.data() + offset), 11, 7);
};
i64 delta = 0;
for (i64 i = 0; i < rels.size(); i++) {
@@ -815,6 +909,20 @@ static void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc)
if (sym.file == ctx.internal_obj)
continue;
auto find_paired_reloc = [&] {
if (sym.value <= rels[i].r_offset) {
for (i64 j = i - 1; j >= 0; j--)
if (is_hi20(rels[j]) && sym.value == rels[j].r_offset)
return j;
} else {
for (i64 j = i + 1; j < rels.size(); j++)
if (is_hi20(rels[j]) && sym.value == rels[j].r_offset)
return j;
}
Fatal(ctx) << isec << ": paired relocation is missing: " << i;
};
switch (r.r_type) {
case R_RISCV_CALL:
case R_RISCV_CALL_PLT: {
@@ -825,13 +933,13 @@ static void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc)
if (dist & 1)
break;
i64 rd = get_rd(*(ul32 *)(isec.contents.data() + r.r_offset + 4));
i64 rd = get_rd(r.r_offset + 4);
if (rd == 0 && sign_extend(dist, 11) == dist && use_rvc) {
if (use_rvc && rd == 0 && sign_extend(dist, 11) == dist) {
// If rd is x0 and the jump target is within ±2 KiB, we can use
// C.J, saving 6 bytes.
delta += 6;
} else if (rd == 1 && sign_extend(dist, 11) == dist && use_rvc && !E::is_64) {
} else if (use_rvc && !E::is_64 && rd == 1 && sign_extend(dist, 11) == dist) {
// If rd is x1 and the jump target is within ±2 KiB, we can use
// C.JAL. This is RV32 only because C.JAL is RV32-only instruction.
delta += 6;
@@ -841,38 +949,68 @@ static void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc)
}
break;
}
case R_RISCV_HI20:
// If the upper 20 bits are all zero, we can remove LUI.
// The corresponding instructions referred to by LO12_I/LO12_S
// relocations will use the zero register instead.
if (bits(sym.get_addr(ctx), 31, 12) == 0)
case R_RISCV_HI20: {
u64 val = sym.get_addr(ctx) + r.r_addend;
i64 rd = get_rd(r.r_offset);
if (sign_extend(val, 11) == val) {
// We can replace `lui t0, %hi(foo)` and `add t0, t0, %lo(foo)`
// instruction pair with `add t0, x0, %lo(foo)` if foo's bits
// [32:11] are all one or all zero.
delta += 4;
} else if (use_rvc && rd != 0 && rd != 2 && sign_extend(val, 17) == val) {
// If the upper 20 bits can actually be represented in 6 bits,
// we can use C.LUI instead of LUI.
delta += 2;
}
break;
}
case R_RISCV_TPREL_HI20:
case R_RISCV_TPREL_ADD:
// These relocations are used to add a high 20-bit value to the
// thread pointer. The following two instructions materializes
// TP + HI20(foo) in %r5, for example.
// TP + %tprel_hi20(foo) in %t0, for example.
//
// lui a5,%tprel_hi(foo) # R_RISCV_TPREL_HI20 (symbol)
// add a5,a5,tp,%tprel_add(foo) # R_RISCV_TPREL_ADD (symbol)
// lui t0, %tprel_hi(foo) # R_RISCV_TPREL_HI20
// add t0, t0, tp # R_RISCV_TPREL_ADD
//
// Then thread-local variable `foo` is accessed with a low 12-bit
// offset like this:
// Then thread-local variable `foo` is accessed with the low
// 12-bit offset like this:
//
// sw t0,%tprel_lo(foo)(a5) # R_RISCV_TPREL_LO12_S (symbol)
// sw t0, %tprel_lo(foo)(t0) # R_RISCV_TPREL_LO12_S
//
// However, if the variable is at TP ±2 KiB, TP + HI20(foo) is the
// same as TP, so we can instead access the thread-local variable
// directly using TP like this:
// However, if the variable is at TP ± 2 KiB, TP + %tprel_hi20(foo)
// is the same as TP, so we can instead access the thread-local
// variable directly using TP like this:
//
// sw t0,%tprel_lo(foo)(tp)
// sw t0, %tprel_lo(foo)(tp)
//
// Here, we remove `lui` and `add` if the offset is within ±2 KiB.
if (i64 val = sym.get_addr(ctx) + r.r_addend - ctx.tp_addr;
sign_extend(val, 11) == val)
delta += 4;
break;
case R_RISCV_TLSDESC_HI20:
if (!sym.has_tlsdesc(ctx))
delta += 4;
break;
case R_RISCV_TLSDESC_LOAD_LO12:
case R_RISCV_TLSDESC_ADD_LO12: {
const ElfRel<E> &rel2 = rels[find_paired_reloc()];
Symbol<E> &sym2 = *isec.file.symbols[rel2.r_sym];
if (r.r_type == R_RISCV_TLSDESC_LOAD_LO12) {
if (!sym2.has_tlsdesc(ctx))
delta += 4;
} else {
assert(r.r_type == R_RISCV_TLSDESC_ADD_LO12);
if (!sym2.has_tlsdesc(ctx) && !sym2.has_gottp(ctx))
if (i64 val = sym2.get_addr(ctx) + rel2.r_addend - ctx.tp_addr;
sign_extend(val, 11) == val)
delta += 4;
}
break;
}
}
}
@@ -888,8 +1026,8 @@ static void shrink_section(Context<E> &ctx, InputSection<E> &isec, bool use_rvc)
// linker to align the location referred to by the relocation to a
// specified byte boundary. We at least have to interpret them to satisfy
// the alignment constraints.
template <typename E>
i64 riscv_resize_sections(Context<E> &ctx) {
template <>
i64 riscv_resize_sections<E>(Context<E> &ctx) {
Timer t(ctx, "riscv_resize_sections");
// True if we can use the 2-byte instructions. This is usually true on
@@ -900,7 +1038,7 @@ i64 riscv_resize_sections(Context<E> &ctx) {
// 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()))
if (is_resizable(isec.get()))
shrink_section(ctx, *isec, use_rvc);
});
@@ -929,21 +1067,242 @@ i64 riscv_resize_sections(Context<E> &ctx) {
return set_osec_offsets(ctx);
}
#define INSTANTIATE(E) \
template void write_plt_header(Context<E> &, u8 *); \
template void write_plt_entry(Context<E> &, u8 *, Symbol<E> &); \
template void write_pltgot_entry(Context<E> &, u8 *, Symbol<E> &); \
template void \
EhFrameSection<E>::apply_reloc(Context<E> &, const ElfRel<E> &, u64, u64); \
template void InputSection<E>::apply_reloc_alloc(Context<E> &, u8 *); \
template void InputSection<E>::apply_reloc_nonalloc(Context<E> &, u8 *); \
template void InputSection<E>::copy_contents_riscv(Context<E> &, u8 *); \
template void InputSection<E>::scan_relocations(Context<E> &); \
template i64 riscv_resize_sections(Context<E> &);
// ISA name handlers
//
// An example of ISA name is "rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0".
// An ISA name starts with the base name (e.g. "rv64i2p1") followed by
// ISA extensions separated by underscores.
//
// There are lots of ISA extensions defined for RISC-V, and they are
// identified by name. Some extensions are of single-letter alphabet such
// as "m" or "q". Newer extension names start with "z" followed by one or
// more alphabets (i.e. "zicsr"). "s" and "x" prefixes are reserved
// for supervisor-level extensions and private extensions, respectively.
//
// Each extension consists of a name, a major version and a minor version.
// For example, "m2p0" indicates the "m" extension of version 2.0. "p" is
// just a separator. Versions are often omitted in documents, but they are
// mandatory in .riscv.attributes. Likewise, abbreviations as "g" (which
// is short for "IMAFD") are not allowed in .riscv.attributes.
//
// Each RISC-V object file contains an ISA string enumerating extensions
// used by the object file. We need to merge input objects' ISA strings
// into a single ISA string.
//
// In order to guarantee string uniqueness, extensions have to be ordered
// in a specific manner. The exact rule is unfortunately a bit complicated.
//
// The following functions takes care of ISA strings.
INSTANTIATE(RV64LE);
INSTANTIATE(RV64BE);
INSTANTIATE(RV32LE);
INSTANTIATE(RV32BE);
namespace {
struct Extn {
std::string name;
i64 major;
i64 minor;
};
}
// As per the RISC-V spec, the extension names must be sorted in a very
// specific way, and unfortunately that's not just an alphabetical order.
// For example, rv64imafd is a legal ISA string, whereas rv64iafdm is not.
// The exact rule is somewhat arbitrary.
//
// This function returns true if the first extension name should precede
// the second one as per the rule.
static bool extn_name_less(const Extn &e1, const Extn &e2) {
auto get_single_letter_rank = [](char c) -> i64 {
std::string_view exts = "iemafdqlcbkjtpvnh";
size_t pos = exts.find_first_of(c);
if (pos != exts.npos)
return pos;
return c - 'a' + exts.size();
};
auto get_rank = [&](std::string_view str) -> i64 {
switch (str[0]) {
case 'x':
return 1 << 20;
case 's':
return 1 << 19;
case 'z':
return (1 << 18) + get_single_letter_rank(str[1]);
default:
return get_single_letter_rank(str[0]);
}
};
return std::tuple{get_rank(e1.name), e1.name} <
std::tuple{get_rank(e2.name), e2.name};
}
static bool extn_version_less(const Extn &e1, const Extn &e2) {
return std::tuple{e1.major, e1.minor} <
std::tuple{e2.major, e2.minor};
}
static std::optional<Extn> read_extn_string(std::string_view &str) {
auto flags = std::regex_constants::optimize | std::regex_constants::ECMAScript;
static std::regex re(R"(^([a-z]+)(\d+)p(\d+))", flags);
std::cmatch m;
if (std::regex_search(str.data(), str.data() + str.size(), m, re)) {
str = str.substr(m.length());
return Extn{m[1], (i64)std::stoul(m[2]), (i64)std::stoul(m[3])};
}
return {};
}
static std::vector<Extn> parse_arch_string(std::string_view str) {
if (str.size() < 5)
return {};
// Parse the base part
std::string_view base = str.substr(0, 5);
if (base != "rv32i" && base != "rv32e" && base != "rv64i" && base != "rv64e")
return {};
str = str.substr(4);
std::optional<Extn> extn = read_extn_string(str);
if (!extn)
return {};
std::vector<Extn> vec;
extn->name = base;
vec.push_back(*extn);
// Parse extensions
while (!str.empty()) {
if (str[0] != '_')
return {};
str = str.substr(1);
std::optional<Extn> extn = read_extn_string(str);
if (!extn)
return {};
vec.push_back(*extn);
}
return vec;
}
static std::vector<Extn> merge_extensions(std::span<Extn> x, std::span<Extn> y) {
std::vector<Extn> vec;
// The base part (i.e. "rv64i" or "rv32i") must match.
if (x[0].name != y[0].name)
return {};
// Merge ISA extension strings
while (!x.empty() && !y.empty()) {
if (x[0].name == y[0].name) {
vec.push_back(extn_version_less(x[0], y[0]) ? y[0] : x[0]);
x = x.subspan(1);
y = y.subspan(1);
} else if (extn_name_less(x[0], y[0])) {
vec.push_back(x[0]);
x = x.subspan(1);
} else {
vec.push_back(y[0]);
y = y.subspan(1);
}
}
vec.insert(vec.end(), x.begin(), x.end());
vec.insert(vec.end(), y.begin(), y.end());
return vec;
}
static std::string to_string(std::span<Extn> v) {
std::string str = v[0].name + std::to_string(v[0].major) + "p" +
std::to_string(v[0].minor);
for (i64 i = 1; i < v.size(); i++)
str += "_" + v[i].name + std::to_string(v[i].major) + "p" +
std::to_string(v[i].minor);
return str;
}
//
// Output .riscv.attributes class
//
template <>
void RiscvAttributesSection<E>::update_shdr(Context<E> &ctx) {
if (!contents.empty())
return;
i64 stack = -1;
std::vector<Extn> arch;
bool unaligned = false;
for (ObjectFile<E> *file : ctx.objs) {
if (file->extra.stack_align) {
i64 val = *file->extra.stack_align;
if (stack != -1 && stack != val)
Error(ctx) << *file << ": stack alignment requirement mistmatch";
stack = val;
}
if (file->extra.arch) {
std::vector<Extn> arch2 = parse_arch_string(*file->extra.arch);
if (arch2.empty())
Error(ctx) << *file << ": corrupted .riscv.attributes ISA string: "
<< *file->extra.arch;
if (arch.empty()) {
arch = arch2;
} else {
arch = merge_extensions(arch, arch2);
if (arch.empty())
Error(ctx) << *file << ": incompatible .riscv.attributes ISA string: "
<< *file->extra.arch;
}
}
if (file->extra.unaligned_access)
unaligned = true;
}
if (arch.empty())
return;
std::string arch_str = to_string(arch);
contents.resize(arch_str.size() + 100);
u8 *p = (u8 *)contents.data();
*p++ = 'A'; // Format version
U32<E> *sub_sz = (U32<E> *)p; // Sub-section length
p += 4;
p += write_string(p, "riscv"); // Vendor name
u8 *sub_sub_start = p;
*p++ = ELF_TAG_FILE; // Sub-section tag
U32<E> *sub_sub_sz = (U32<E> *)p; // Sub-sub-section length
p += 4;
if (stack != -1) {
p += write_uleb(p, ELF_TAG_RISCV_STACK_ALIGN);
p += write_uleb(p, stack);
}
p += write_uleb(p, ELF_TAG_RISCV_ARCH);
p += write_string(p, arch_str);
if (unaligned) {
p += write_uleb(p, ELF_TAG_RISCV_UNALIGNED_ACCESS);
p += write_uleb(p, 1);
}
i64 sz = p - (u8 *)contents.data();
*sub_sz = sz - 1;
*sub_sub_sz = p - sub_sub_start;
contents.resize(sz);
this->shdr.sh_size = sz;
}
template <>
void RiscvAttributesSection<E>::copy_buf(Context<E> &ctx) {
memcpy(ctx.buf + this->shdr.sh_offset, contents.data(), contents.size());
}
} // namespace mold::elf
#endif
+28 -26
View File
@@ -48,16 +48,23 @@ static void write_mid20(u8 *loc, u64 val) {
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, 0x07, 0x00, // nopr; nopr; nopr
0x07, 0x00, 0x07, 0x00, // nopr; nopr
};
memcpy(buf, insn, sizeof(insn));
*(ub32 *)(buf + 8) = (ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 6) >> 1;
*(ub32 *)(buf + 26) =
(ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 24) >> 1;
}
template <>
@@ -65,15 +72,12 @@ 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)
0xc0, 0x01, 0, 0, 0, 0, // lgfi %r0, PLT_INDEX
0x07, 0xf1, // br %r1
0x07, 0x00, 0x07, 0x00, 0x07, 0x00, // nopr; nopr; nopr
0x07, 0x00, 0x07, 0x00, 0x07, 0x00, // nopr; nopr; nopr
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;
*(ub32 *)(buf + 14) = sym.get_plt_idx(ctx) * sizeof(ElfRel<E>);
}
template <>
@@ -86,12 +90,12 @@ void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
};
memcpy(buf, insn, sizeof(insn));
*(ub32 *)(buf + 2) = (sym.get_got_addr(ctx) - sym.get_plt_addr(ctx)) >> 1;
*(ub32 *)(buf + 2) = (sym.get_got_pltgot_addr(ctx) - sym.get_plt_addr(ctx)) >> 1;
}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, const 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) {
@@ -149,7 +153,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
switch (rel.r_type) {
case R_390_64:
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, dynrel);
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_390_8:
check(S + A, 0, 1 << 8);
@@ -356,12 +360,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
u64 A = frag ? frag_addend : (i64)rel.r_addend;
switch (rel.r_type) {
case R_390_32: {
i64 val = S + A;
check(val, 0, 1LL << 32);
*(ub32 *)loc = val;
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;
@@ -455,23 +457,23 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
// We always want to relax calls to __tls_get_offset() in statically-
// linked executables because __tls_get_offset() in libc.a just calls
// abort().
if (ctx.arg.is_static ||
(ctx.arg.relax && !sym.is_imported && !ctx.arg.shared)) {
// do nothing
} else if (ctx.arg.relax && !sym.is_imported && ctx.arg.shared &&
!ctx.arg.z_dlopen) {
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: {
bool do_relax = ctx.arg.is_static || (ctx.arg.relax && !ctx.arg.shared);
if (!do_relax)
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);
@@ -482,7 +484,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
case R_390_TLS_LDCALL:
break;
default:
Fatal(ctx) << *this << ": scan_relocations: " << rel;
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
+5 -5
View File
@@ -169,7 +169,7 @@ void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
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;
*(ul32 *)(buf + 8) = sym.get_got_pltgot_addr(ctx) - ctx.got->shdr.sh_addr;
} else {
static const u8 insn[] = {
0x01, 0xd0, // mov.l 1f, r0
@@ -181,13 +181,13 @@ void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
static_assert(sizeof(insn) == E::pltgot_size);
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 8) = sym.get_got_addr(ctx);
*(ul32 *)(buf + 8) = sym.get_got_pltgot_addr(ctx);
}
}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, const 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) {
@@ -229,7 +229,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
switch (rel.r_type) {
case R_SH_DIR32:
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, dynrel);
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_SH_REL32:
case R_SH_PLT32:
+24 -27
View File
@@ -115,12 +115,12 @@ void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
};
memcpy(buf, entry, sizeof(entry));
*(ub64 *)(buf + 24) = sym.get_got_addr(ctx) - sym.get_plt_addr(ctx) - 4;
*(ub64 *)(buf + 24) = sym.get_got_pltgot_addr(ctx) - sym.get_plt_addr(ctx) - 4;
}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, const 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) {
@@ -170,7 +170,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
switch (rel.r_type) {
case R_SPARC_64:
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, dynrel);
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_SPARC_5:
check(S + A, 0, 1 << 5);
@@ -288,36 +288,32 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
// 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_imported || sym.is_ifunc()) {
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);
} else if (sym.is_absolute()) {
i64 val = S + A;
*(ub32 *)loc |= bits(val < 0 ? ~val : val, 31, 10);
} else {
i64 val = S + A - GOT;
*(ub32 *)loc |= bits(val < 0 ? ~val : val, 31, 10);
}
break;
case R_SPARC_GOTDATA_OP_LOX10: {
if (sym.is_imported || sym.is_ifunc()) {
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);
} else if (sym.is_absolute()) {
i64 val = S + A;
*(ub32 *)loc |= bits(val, 9, 0) | (val < 0 ? 0b1'1100'0000'0000 : 0);
} else {
i64 val = S + A - GOT;
*(ub32 *)loc |= bits(val, 9, 0) | (val < 0 ? 0b1'1100'0000'0000 : 0);
}
break;
}
case R_SPARC_GOTDATA_OP:
if (sym.is_imported || sym.is_ifunc())
break;
if (sym.is_absolute()) {
// ldx [ %g2 + %g1 ], %g1 → nop
*(ub32 *)loc = 0x0100'0000;
} else {
} 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;
@@ -568,8 +564,9 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
break;
case R_SPARC_TLS_GD_CALL:
case R_SPARC_TLS_LDM_CALL:
if (!ctx.arg.is_static && ctx.extra.tls_get_addr_sym->is_imported)
ctx.extra.tls_get_addr_sym->flags |= NEEDS_PLT;
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:
@@ -592,7 +589,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
case R_SPARC_SIZE32:
break;
default:
Fatal(ctx) << *this << ": scan_relocations: " << rel;
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
+119 -129
View File
@@ -64,33 +64,46 @@ void write_plt_header(Context<E> &ctx, u8 *buf) {
template <>
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
static const u8 insn[] = {
0xf3, 0x0f, 0x1e, 0xfa, // endbr64
0x41, 0xbb, 0, 0, 0, 0, // mov $index_in_relplt, %r11d
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOTPLT
};
// Only a canonical PLT can be address-taken; there's no way to take
// an address of a non-canonical PLT. Therefore, a non-canonical PLT
// doesn't have to start with an endbr64.
if (sym.is_canonical) {
static const u8 insn[] = {
0xf3, 0x0f, 0x1e, 0xfa, // endbr64
0x41, 0xbb, 0, 0, 0, 0, // mov $index_in_relplt, %r11d
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOTPLT
};
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 6) = sym.get_plt_idx(ctx);
*(ul32 *)(buf + 12) = sym.get_gotplt_addr(ctx) - sym.get_plt_addr(ctx) - 16;
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 6) = sym.get_plt_idx(ctx);
*(ul32 *)(buf + 12) = sym.get_gotplt_addr(ctx) - sym.get_plt_addr(ctx) - 16;
} else {
static const u8 insn[] = {
0x41, 0xbb, 0, 0, 0, 0, // mov $index_in_relplt, %r11d
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOTPLT
0xcc, 0xcc, 0xcc, 0xcc, // (padding)
};
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 2) = sym.get_plt_idx(ctx);
*(ul32 *)(buf + 8) = sym.get_gotplt_addr(ctx) - sym.get_plt_addr(ctx) - 12;
}
}
template <>
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
static const u8 insn[] = {
0xf3, 0x0f, 0x1e, 0xfa, // endbr64
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
0xcc, 0xcc, 0xcc, 0xcc, // (padding)
0xcc, 0xcc, // (padding)
};
memcpy(buf, insn, sizeof(insn));
*(ul32 *)(buf + 6) = sym.get_got_addr(ctx) - sym.get_plt_addr(ctx) - 10;
*(ul32 *)(buf + 2) = sym.get_got_pltgot_addr(ctx) - sym.get_plt_addr(ctx) - 6;
}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, const 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) {
@@ -165,28 +178,6 @@ static u32 relax_gottpoff(u8 *loc) {
return 0;
}
static u32 relax_gotpc32_tlsdesc(u8 *loc) {
switch ((loc[0] << 16) | (loc[1] << 8) | loc[2]) {
case 0x488d05: return 0x48c7c0; // lea 0(%rip), %rax -> mov $0, %rax
case 0x488d0d: return 0x48c7c1; // lea 0(%rip), %rcx -> mov $0, %rcx
case 0x488d15: return 0x48c7c2; // lea 0(%rip), %rdx -> mov $0, %rdx
case 0x488d1d: return 0x48c7c3; // lea 0(%rip), %rbx -> mov $0, %rbx
case 0x488d25: return 0x48c7c4; // lea 0(%rip), %rsp -> mov $0, %rsp
case 0x488d2d: return 0x48c7c5; // lea 0(%rip), %rbp -> mov $0, %rbp
case 0x488d35: return 0x48c7c6; // lea 0(%rip), %rsi -> mov $0, %rsi
case 0x488d3d: return 0x48c7c7; // lea 0(%rip), %rdi -> mov $0, %rdi
case 0x4c8d05: return 0x49c7c0; // lea 0(%rip), %r8 -> mov $0, %r8
case 0x4c8d0d: return 0x49c7c1; // lea 0(%rip), %r9 -> mov $0, %r9
case 0x4c8d15: return 0x49c7c2; // lea 0(%rip), %r10 -> mov $0, %r10
case 0x4c8d1d: return 0x49c7c3; // lea 0(%rip), %r11 -> mov $0, %r11
case 0x4c8d25: return 0x49c7c4; // lea 0(%rip), %r12 -> mov $0, %r12
case 0x4c8d2d: return 0x49c7c5; // lea 0(%rip), %r13 -> mov $0, %r13
case 0x4c8d35: return 0x49c7c6; // lea 0(%rip), %r14 -> mov $0, %r14
case 0x4c8d3d: return 0x49c7c7; // lea 0(%rip), %r15 -> mov $0, %r15
}
return 0;
}
// Rewrite a function call to __tls_get_addr to a cheaper instruction
// sequence. We can do this when we know the thread-local variable's TP-
// relative address at link-time.
@@ -267,7 +258,7 @@ static void relax_gd_to_ie(u8 *loc, ElfRel<E> rel, u64 val) {
// sequence. The difference from relax_gd_to_le is that we are
// materializing a Dynamic Thread Pointer for the current ELF module
// instead of an address for a particular thread-local variable.
static void relax_ld_to_le(u8 *loc, ElfRel<E> rel, u64 val) {
static void relax_ld_to_le(u8 *loc, ElfRel<E> rel, u64 tls_size) {
switch (rel.r_type) {
case R_X86_64_PLT32:
case R_X86_64_PC32: {
@@ -275,13 +266,18 @@ static void relax_ld_to_le(u8 *loc, ElfRel<E> rel, u64 val) {
//
// 48 8d 3d 00 00 00 00 lea foo@tlsld(%rip), %rdi
// e8 00 00 00 00 call __tls_get_addr
//
// The instructions are so short that we cannot rewrite them with
// "mov %fs:0, %rax" which is 9 bytes long. We use a shorter code
// sequence instead. Since "xor %eax, %eax" zero-clears %rax, the
// meaning is equivalent.
static const u8 insn[] = {
0x31, 0xc0, // xor %eax, %eax
0x64, 0x48, 0x8b, 0x00, // mov %fs:(%rax), %rax
0x48, 0x2d, 0, 0, 0, 0, // sub $tls_size, %rax
};
memcpy(loc - 3, insn, sizeof(insn));
*(ul32 *)(loc + 5) = val;
*(ul32 *)(loc + 5) = tls_size;
break;
}
case R_X86_64_GOTPCREL:
@@ -297,7 +293,7 @@ static void relax_ld_to_le(u8 *loc, ElfRel<E> rel, u64 val) {
0x90, // nop
};
memcpy(loc - 3, insn, sizeof(insn));
*(ul32 *)(loc + 5) = val;
*(ul32 *)(loc + 5) = tls_size;
break;
}
case R_X86_64_PLTOFF64: {
@@ -308,14 +304,12 @@ static void relax_ld_to_le(u8 *loc, ElfRel<E> rel, u64 val) {
// 48 01 d8 add %rbx, %rax
// ff d0 call *%rax
static const u8 insn[] = {
0x31, 0xc0, // xor %eax, %eax
0x64, 0x48, 0x8b, 0x00, // mov %fs:(%rax), %rax
0x48, 0x2d, 0, 0, 0, 0, // sub $tls_size, %rax
0x0f, 0x1f, 0x44, 0x00, 0x00, // nop
0x0f, 0x1f, 0x44, 0x00, 0x00, // nop
0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0, // mov %fs:0, %rax
0x48, 0x2d, 0, 0, 0, 0, // sub $tls_size, %rax
0x0f, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00, // nop
};
memcpy(loc - 3, insn, sizeof(insn));
*(ul32 *)(loc + 5) = val;
*(ul32 *)(loc + 8) = tls_size;
break;
}
default:
@@ -364,7 +358,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
u64 A = rel.r_addend;
u64 P = get_addr() + rel.r_offset;
u64 G = sym.get_got_addr(ctx) - ctx.gotplt->shdr.sh_addr;
u64 GOTPLT = ctx.gotplt->shdr.sh_addr;
u64 GOT = ctx.gotplt->shdr.sh_addr;
switch (rel.r_type) {
case R_X86_64_8:
@@ -382,7 +376,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
write32s(S + A);
break;
case R_X86_64_64:
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, dynrel);
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_X86_64_PC8:
check(S + A - P, -(1 << 7), 1 << 7);
@@ -400,32 +394,32 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
*(ul64 *)loc = S + A - P;
break;
case R_X86_64_GOT32:
write32s(G + A);
write32(G + A);
break;
case R_X86_64_GOT64:
*(ul64 *)loc = G + A;
break;
case R_X86_64_GOTOFF64:
case R_X86_64_PLTOFF64:
*(ul64 *)loc = S + A - GOTPLT;
*(ul64 *)loc = S + A - GOT;
break;
case R_X86_64_GOTPC32:
write32s(GOTPLT + A - P);
write32s(GOT + A - P);
break;
case R_X86_64_GOTPC64:
*(ul64 *)loc = GOTPLT + A - P;
*(ul64 *)loc = GOT + A - P;
break;
case R_X86_64_GOTPCREL:
write32s(G + GOTPLT + A - P);
write32s(G + GOT + A - P);
break;
case R_X86_64_GOTPCREL64:
*(ul64 *)loc = G + GOTPLT + A - P;
*(ul64 *)loc = G + GOT + A - P;
break;
case R_X86_64_GOTPCRELX:
// We always want to relax GOTPCRELX relocs even if --no-relax
// was given because some static PIE runtime code depends on these
// relaxations.
if (!sym.is_imported && !sym.is_ifunc() && sym.is_relative()) {
if (sym.is_pcrel_linktime_const(ctx)) {
u32 insn = relax_gotpcrelx(loc - 2);
i64 val = S + A - P;
if (insn && (i32)val == val) {
@@ -435,10 +429,10 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
break;
}
}
write32s(G + GOTPLT + A - P);
write32s(G + GOT + A - P);
break;
case R_X86_64_REX_GOTPCRELX:
if (!sym.is_imported && !sym.is_ifunc() && sym.is_relative()) {
if (sym.is_pcrel_linktime_const(ctx)) {
u32 insn = relax_rex_gotpcrelx(loc - 3);
i64 val = S + A - P;
if (insn && (i32)val == val) {
@@ -449,26 +443,21 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
break;
}
}
write32s(G + GOTPLT + A - P);
write32s(G + GOT + A - P);
break;
case R_X86_64_TLSGD:
if (sym.has_tlsgd(ctx)) {
if (sym.has_tlsgd(ctx))
write32s(sym.get_tlsgd_addr(ctx) + A - P);
} else if (sym.has_gottp(ctx)) {
relax_gd_to_ie(loc, rels[i + 1], sym.get_gottp_addr(ctx) - P);
i++;
} else {
relax_gd_to_le(loc, rels[i + 1], S - ctx.tp_addr);
i++;
}
else if (sym.has_gottp(ctx))
relax_gd_to_ie(loc, rels[++i], sym.get_gottp_addr(ctx) - P);
else
relax_gd_to_le(loc, rels[++i], S - ctx.tp_addr);
break;
case R_X86_64_TLSLD:
if (ctx.got->has_tlsld(ctx)) {
if (ctx.got->has_tlsld(ctx))
write32s(ctx.got->get_tlsld_addr(ctx) + A - P);
} else {
relax_ld_to_le(loc, rels[i + 1], ctx.tp_addr - ctx.tls_begin);
i++;
}
else
relax_ld_to_le(loc, rels[++i], ctx.tp_addr - ctx.tls_begin);
break;
case R_X86_64_DTPOFF32:
write32s(S + A - ctx.dtp_addr);
@@ -495,15 +484,46 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
}
break;
case R_X86_64_GOTPC32_TLSDESC:
// x86-64 TLSDESC uses the following code sequence to materialize
// a TP-relative address in %rax.
//
// lea 0(%rip), %rax
// R_X86_64_GOTPC32_TLSDESC foo
// call *(%rax)
// R_X86_64_TLSDESC_CALL foo
//
// We may relax the instructions to the following for non-dlopen'd DSO
//
// mov foo@GOTTPOFF(%rip), %rax
// nop
//
// or to the following for executable.
//
// mov $foo@TPOFF, %rax
// nop
if (sym.has_tlsdesc(ctx)) {
write32s(sym.get_tlsdesc_addr(ctx) + A - P);
} else if (sym.has_gottp(ctx)) {
// mov foo@gottpoff(%rip), %rax
loc[-3] = 0x48;
loc[-2] = 0x8b;
loc[-1] = 0x05;
write32s(sym.get_gottp_addr(ctx) + A - P);
} else {
u32 insn = relax_gotpc32_tlsdesc(loc - 3);
loc[-3] = insn >> 16;
loc[-2] = insn >> 8;
loc[-1] = insn;
// mov $foo@tpoff, %rax
loc[-3] = 0x48;
loc[-2] = 0xc7;
loc[-1] = 0xc0;
write32s(S - ctx.tp_addr);
assert(A == -4);
}
break;
case R_X86_64_TLSDESC_CALL:
if (sym.has_tlsdesc(ctx)) {
// Do nothing
} else {
// call *(%rax) -> nop
loc[0] = 0x66;
loc[1] = 0x90;
}
break;
case R_X86_64_SIZE32:
@@ -512,13 +532,6 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
case R_X86_64_SIZE64:
*(ul64 *)loc = sym.esym().st_size + A;
break;
case R_X86_64_TLSDESC_CALL:
if (!sym.has_tlsdesc(ctx)) {
// call *(%rax) -> nop
loc[0] = 0x66;
loc[1] = 0x90;
}
break;
default:
unreachable();
}
@@ -653,6 +666,19 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
if (sym.is_ifunc())
sym.flags |= NEEDS_GOT | NEEDS_PLT;
if (rel.r_type == R_X86_64_TLSGD || rel.r_type == R_X86_64_TLSLD) {
if (i + 1 == rels.size())
Fatal(ctx) << *this << ": " << rel
<< " must be followed by PLT or GOTPCREL";
if (u32 ty = rels[i + 1].r_type;
ty != R_X86_64_PLT32 && ty != R_X86_64_PC32 &&
ty != R_X86_64_PLTOFF64 && ty != R_X86_64_GOTPCREL &&
ty != R_X86_64_GOTPCRELX)
Fatal(ctx) << *this << ": " << rel
<< " must be followed by PLT or GOTPCREL";
}
switch (rel.r_type) {
case R_X86_64_8:
case R_X86_64_16:
@@ -685,25 +711,12 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
sym.flags |= NEEDS_PLT;
break;
case R_X86_64_TLSGD:
if (rel.r_addend != -4)
Fatal(ctx) << *this << ": bad r_addend for R_X86_64_TLSGD";
if (i + 1 == rels.size())
Fatal(ctx) << *this << ": TLSGD reloc must be followed by PLT or GOTPCREL";
if (u32 ty = rels[i + 1].r_type;
ty != R_X86_64_PLT32 && ty != R_X86_64_PC32 &&
ty != R_X86_64_PLTOFF64 && ty != R_X86_64_GOTPCREL &&
ty != R_X86_64_GOTPCRELX)
Fatal(ctx) << *this << ": TLSGD reloc must be followed by PLT or GOTPCREL";
if (ctx.arg.is_static ||
(ctx.arg.relax && !sym.is_imported && !ctx.arg.shared)) {
if ((ctx.arg.relax && sym.is_tprel_linktime_const(ctx)) ||
ctx.arg.is_static) {
// We always relax if -static because libc.a doesn't contain
// __tls_get_addr().
i++;
} else if (ctx.arg.relax && !sym.is_imported && ctx.arg.shared &&
!ctx.arg.z_dlopen) {
} else if (ctx.arg.relax && sym.is_tprel_runtime_const(ctx)) {
sym.flags |= NEEDS_GOTTP;
i++;
} else {
@@ -711,18 +724,6 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
}
break;
case R_X86_64_TLSLD:
if (rel.r_addend != -4)
Fatal(ctx) << *this << ": bad r_addend for R_X86_64_TLSLD";
if (i + 1 == rels.size())
Fatal(ctx) << *this << ": TLSLD reloc must be followed by PLT or GOTPCREL";
if (u32 ty = rels[i + 1].r_type;
ty != R_X86_64_PLT32 && ty != R_X86_64_PC32 &&
ty != R_X86_64_PLTOFF64 && ty != R_X86_64_GOTPCREL &&
ty != R_X86_64_GOTPCRELX)
Fatal(ctx) << *this << ": TLSLD reloc must be followed by PLT or GOTPCREL";
// 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))
@@ -730,28 +731,17 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
else
ctx.needs_tlsld = true;
break;
case R_X86_64_GOTTPOFF: {
if (rel.r_addend != -4)
Fatal(ctx) << *this << ": bad r_addend for R_X86_64_GOTTPOFF";
bool do_relax = ctx.arg.relax && !ctx.arg.shared &&
!sym.is_imported && relax_gottpoff(loc - 3);
if (!do_relax)
case R_X86_64_GOTTPOFF:
if (ctx.arg.relax && relax_gottpoff(loc - 3) &&
sym.is_tprel_linktime_const(ctx)) {
// do nothing
} else {
sym.flags |= NEEDS_GOTTP;
}
break;
}
case R_X86_64_GOTPC32_TLSDESC: {
if (rel.r_addend != -4)
Fatal(ctx) << *this << ": bad r_addend for R_X86_64_GOTPC32_TLSDESC";
if (relax_gotpc32_tlsdesc(loc - 3) == 0)
Fatal(ctx) << *this << ": GOTPC32_TLSDESC relocation is used"
<< " against an invalid code sequence";
if (!relax_tlsdesc(ctx, sym))
sym.flags |= NEEDS_TLSDESC;
case R_X86_64_TLSDESC_CALL:
scan_tlsdesc(ctx, sym);
break;
}
case R_X86_64_TPOFF32:
case R_X86_64_TPOFF64:
check_tlsle(ctx, sym, rel);
@@ -761,7 +751,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
case R_X86_64_DTPOFF64:
case R_X86_64_SIZE32:
case R_X86_64_SIZE64:
case R_X86_64_TLSDESC_CALL:
case R_X86_64_GOTPC32_TLSDESC:
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
+51 -34
View File
@@ -1,6 +1,7 @@
#include "mold.h"
#include "../common/cmdline.h"
#include <random>
#include <regex>
#include <sstream>
#include <sys/stat.h>
@@ -187,6 +188,8 @@ Options:
-z origin Mark object requiring immediate $ORIGIN processing at runtime
-z pack-relative-relocs Alias for --pack-dyn-relocs=relr
-z nopack-relative-relocs
-z sectionheader Do not omit section header (default)
-z nosectionheader Omit section header
-z separate-loadable-segments
Separate all loadable segments to different pages
-z separate-code Separate code and data into different pages
@@ -198,8 +201,8 @@ Options:
-z notext
-z textoff
mold: supported targets: elf32-i386 elf64-x86-64 elf32-littlearm elf64-littleaarch64 elf32-littleriscv elf32-bigriscv elf64-littleriscv elf64-bigriscv elf32-powerpc elf64-powerpc elf64-powerpc elf64-powerpcle elf64-s390 elf64-sparc elf32-m68k elf32-sh-linux elf64-alpha
mold: supported emulations: elf_i386 elf_x86_64 armelf_linux_eabi aarch64linux aarch64elf elf32lriscv elf32briscv elf64lriscv elf64briscv elf32ppc elf32ppclinux elf64ppc elf64lppc elf64_s390 elf64_sparc m68kelf shlelf_linux elf64alpha)";
mold: supported targets: elf32-i386 elf64-x86-64 elf32-littlearm elf64-littleaarch64 elf32-littleriscv elf32-bigriscv elf64-littleriscv elf64-bigriscv elf32-powerpc elf64-powerpc elf64-powerpc elf64-powerpcle elf64-s390 elf64-sparc elf32-m68k elf32-sh-linux elf64-alpha elf64-loongarch elf32-loongarch
mold: supported emulations: elf_i386 elf_x86_64 armelf_linux_eabi aarch64linux aarch64elf elf32lriscv elf32briscv elf64lriscv elf64briscv elf32ppc elf32ppclinux elf64ppc elf64lppc elf64_s390 elf64_sparc m68kelf shlelf_linux elf64alpha elf64loongarch elf32loongarch)";
static std::vector<std::string> add_dashes(std::string name) {
// Single-letter option
@@ -399,13 +402,13 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
std::vector<std::string> remaining;
std::string_view arg;
ctx.page_size = E::page_size;
ctx.arg.color_diagnostics = isatty(STDERR_FILENO);
bool version_shown = false;
bool warn_shared_textrel = false;
std::optional<SeparateCodeKind> z_separate_code;
std::optional<bool> z_relro;
std::optional<u64> shuffle_sections_seed;
std::unordered_set<std::string_view> rpaths;
auto add_rpath = [&](std::string_view arg) {
@@ -424,7 +427,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
// It looks like the SPARC's dynamic linker takes both RELA's r_addend
// and the value at the relocated place. So we don't want to write
// values to relocated places.
if (is_sparc<E>)
if constexpr (is_sparc<E>)
ctx.arg.apply_dynamic_relocs = false;
auto read_arg = [&](std::string name) {
@@ -521,15 +524,9 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
<< " aarch64linux\n armelf_linux_eabi\n elf64lriscv\n"
<< " elf64briscv\n elf32lriscv\n elf32briscv\n"
<< " elf32ppc\n elf64ppc\n elf64lppc\n elf64_s390\n"
<< " elf64_sparc\n m68kelf\n shlelf_linux\n elf64alpha";
<< " elf64_sparc\n m68kelf\n shlelf_linux\n"
<< " elf64alpha\n elf64loongarch\n elf32loongarch";
version_shown = true;
} else if (read_flag("mips32") || read_flag("mips32r2") ||
read_flag("mips32r3") || read_flag("mips32r4") ||
read_flag("mips32r5") || read_flag("mips32r6") ||
read_flag("mips64") || read_flag("mips64r2") ||
read_flag("mips64r3") || read_flag("mips64r4") ||
read_flag("mips64r5") || read_flag("mips64r6")) {
// Ignore useless MIPS-specific flags
} else if (read_arg("m")) {
if (arg == "elf_x86_64") {
ctx.arg.emulation = X86_64::target_name;
@@ -563,10 +560,10 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
ctx.arg.emulation = SH4::target_name;
} else if (arg == "elf64alpha") {
ctx.arg.emulation = ALPHA::target_name;
} else if (arg == "elf64ltsmip") {
ctx.arg.emulation = MIPS64LE::target_name;
} else if (arg == "elf64btsmip") {
ctx.arg.emulation = MIPS64BE::target_name;
} else if (arg == "elf64loongarch") {
ctx.arg.emulation = LOONGARCH64::target_name;
} else if (arg == "elf32loongarch") {
ctx.arg.emulation = LOONGARCH32::target_name;
} else {
Fatal(ctx) << "unknown -m argument: " << arg;
}
@@ -635,7 +632,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
ctx.arg.shuffle_sections = SHUFFLE_SECTIONS_SHUFFLE;
} else if (read_eq("shuffle-sections")) {
ctx.arg.shuffle_sections = SHUFFLE_SECTIONS_SHUFFLE;
ctx.arg.shuffle_sections_seed = parse_number(ctx, "shuffle-sections", arg);
shuffle_sections_seed = parse_number(ctx, "shuffle-sections", arg);
} else if (read_flag("reverse-sections")) {
ctx.arg.shuffle_sections = SHUFFLE_SECTIONS_REVERSE;
} else if (read_flag("rosegment")) {
@@ -724,9 +721,11 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
ctx.arg.relocatable_merge_sections = true;
} else if (read_flag("perf")) {
ctx.arg.perf = true;
} else if (read_flag("pack-dyn-relocs=relr")) {
} else if (read_flag("pack-dyn-relocs=relr") ||
read_z_flag("pack-relative-relocs")) {
ctx.arg.pack_dyn_relocs_relr = true;
} else if (read_flag("pack-dyn-relocs=none")) {
} else if (read_flag("pack-dyn-relocs=none") ||
read_z_flag("nopack-relative-relocs")) {
ctx.arg.pack_dyn_relocs_relr = false;
} else if (read_arg("package-metadata")) {
ctx.arg.package_metadata = arg;
@@ -868,10 +867,6 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
ctx.arg.z_origin = true;
} else if (read_z_flag("nodefaultlib")) {
ctx.arg.z_nodefaultlib = true;
} else if (read_z_flag("pack-relative-relocs")) {
ctx.arg.pack_dyn_relocs_relr = true;
} else if (read_z_flag("nopack-relative-relocs")) {
ctx.arg.pack_dyn_relocs_relr = false;
} else if (read_z_flag("separate-loadable-segments")) {
z_separate_code = SEPARATE_LOADABLE_SEGMENTS;
} else if (read_z_flag("separate-code")) {
@@ -884,6 +879,12 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
ctx.arg.z_dynamic_undefined_weak = true;
} else if (read_z_flag("nodynamic-undefined-weak")) {
ctx.arg.z_dynamic_undefined_weak = false;
} else if (read_z_flag("sectionheader")) {
ctx.arg.z_sectionheader = true;
} else if (read_z_flag("nosectionheader")) {
ctx.arg.z_sectionheader = false;
} else if (read_z_flag("rewrite-endbr")) {
ctx.arg.z_rewrite_endbr = true;
} else if (read_flag("no-undefined")) {
ctx.arg.z_defs = true;
} else if (read_flag("fatal-warnings")) {
@@ -1009,7 +1010,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
} else if (read_flag("strip-all") || read_flag("s")) {
ctx.arg.strip_all = true;
} else if (read_flag("strip-debug") || read_flag("S")) {
ctx.arg.strip_all = true;
ctx.arg.strip_debug = true;
} else if (read_flag("warn-unresolved-symbols")) {
ctx.arg.unresolved_symbols = UNRESOLVED_WARN;
} else if (read_flag("error-unresolved-symbols")) {
@@ -1104,7 +1105,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
} else if (read_arg("max-cache-size")) {
} else if (read_arg("version-script")) {
// --version-script, --dynamic-list and --export-dynamic-symbol[-list]
// are treated as positional arguments even if they are actually not
// are treated as positional arguments even though they are actually not
// positional. This is because linker scripts (a positional argument)
// can also specify a version script, and it's better to consolidate
// parsing in read_input_files. In particular, version scripts can
@@ -1141,10 +1142,8 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
Warn(ctx) << "unknown command line option: -z " << args[1];
args = args.subspan(2);
} else if (args[0] == "-dynamic") {
Fatal(ctx) << "unknown command line option: -dynamic;"
<< " -dynamic is a macOS linker's option. If you are trying"
<< " to build a binary for an Apple platform, you need to use"
<< " ld64.mold instead of mold or ld.mold.";
Fatal(ctx) << "unknown command line option: -dynamic; -dynamic is a "
<< "macOS linker's option. mold does not support macOS.";
} else {
if (args[0][0] == '-')
Fatal(ctx) << "unknown command line option: " << args[0];
@@ -1181,6 +1180,14 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
if (ctx.arg.relocatable)
ctx.arg.is_static = true;
if (ctx.arg.shuffle_sections == SHUFFLE_SECTIONS_SHUFFLE) {
if (shuffle_sections_seed)
ctx.arg.shuffle_sections_seed = *shuffle_sections_seed;
else
ctx.arg.shuffle_sections_seed =
((u64)std::random_device()() << 32) | std::random_device()();
}
// --section-order implies `-z separate-loadable-segments`
if (z_separate_code)
ctx.arg.z_separate_code = *z_separate_code;
@@ -1200,12 +1207,14 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
Fatal(ctx) << "-auxiliary may not be used without -shared";
}
if (!ctx.arg.apply_dynamic_relocs && !E::is_rela)
Fatal(ctx) << "--no-apply-dynamic-relocs may not be used on "
<< E::target_name;
if constexpr (!E::is_rela)
if (!ctx.arg.apply_dynamic_relocs)
Fatal(ctx) << "--no-apply-dynamic-relocs may not be used on "
<< E::target_name;
if (is_sparc<E> && ctx.arg.apply_dynamic_relocs)
Fatal(ctx) << "--apply-dynamic-relocs may not be used on SPARC64";
if constexpr (is_sparc<E>)
if (ctx.arg.apply_dynamic_relocs)
Fatal(ctx) << "--apply-dynamic-relocs may not be used on SPARC64";
if (!ctx.arg.section_start.empty() && !ctx.arg.section_order.empty())
Fatal(ctx) << "--section-start may not be used with --section-order";
@@ -1255,6 +1264,14 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
if (ctx.arg.shared)
ctx.overwrite_output_file = false;
if (!ctx.arg.chroot.empty()) {
if (!ctx.arg.Map.empty())
ctx.arg.Map = ctx.arg.chroot + "/" + ctx.arg.Map;
if (!ctx.arg.dependency_file.empty())
ctx.arg.dependency_file = ctx.arg.chroot + "/" + ctx.arg.dependency_file;
}
if (version_shown && remaining.empty())
exit(0);
return remaining;
-554
View File
@@ -1,554 +0,0 @@
// 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, which 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 `gdb-add-index`
// post-processing tool that comes with gdb.
//
// 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 (with the same definition), 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 contains 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 -fucntion-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/5 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"
namespace mold::elf {
// 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;
}
// Split .debug_info into so-called "compilation units". A .debug_info
// section usually contains one compunit unless it was created by `ld -r`.
// This is for --gdb-index.
template <typename E>
std::vector<std::string_view>
read_compunits(Context<E> &ctx, ObjectFile<E> &file) {
file.debug_info->uncompress(ctx);
std::string_view data = file.debug_info->contents;
std::vector<std::string_view> vec;
while (!data.empty()) {
if (data.size() < 4)
Fatal(ctx) << *file.debug_info << ": corrupted .debug_info";
if (*(U32<E> *)data.data() == 0xffff'ffff)
Fatal(ctx) << *file.debug_info << ": --gdb-index: DWARF64 not supported";
i64 len = *(U32<E> *)data.data() + 4;
vec.push_back(data.substr(0, len));
data = data.substr(len);
}
return vec;
}
// 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>
std::vector<GdbIndexName> read_pubnames(Context<E> &ctx, ObjectFile<E> &file) {
std::vector<GdbIndexName> vec;
auto get_cu_idx = [&](InputSection<E> &isec, i64 offset) {
i64 off = 0;
for (i64 i = 0; i < file.compunits.size(); i++) {
if (offset == off)
return file.compunits_idx + i;
off += file.compunits[i].size();
}
Fatal(ctx) << isec << ": corrupted debug_info_offset";
};
auto read = [&](InputSection<E> &isec) {
isec.uncompress(ctx);
std::string_view contents = isec.contents;
while (!contents.empty()) {
if (contents.size() < 14)
Fatal(ctx) << isec << ": corrupted header";
u32 len = *(U32<E> *)contents.data() + 4;
u32 debug_info_offset = *(U32<E> *)(contents.data() + 6);
u32 cu_idx = get_cu_idx(isec, debug_info_offset);
std::string_view data = contents.substr(14, len - 14);
contents = contents.substr(len);
while (!data.empty()) {
u32 offset = *(U32<E> *)data.data();
data = data.substr(4);
if (offset == 0)
break;
u8 type = data[0];
data = data.substr(1);
std::string_view name = data.data();
data = data.substr(name.size() + 1);
vec.push_back({name, gdb_hash(name), (type << 24) | cu_idx});
}
}
};
if (file.debug_pubnames)
read(*file.debug_pubnames);
if (file.debug_pubtypes)
read(*file.debug_pubtypes);
// Uniquify elements because GCC 11 seems to emit one record for each
// comdat group which results in having a lot of duplicate records.
auto less = [](const GdbIndexName &a, const GdbIndexName &b) {
return std::tuple{a.hash, a.attr, a.name} <
std::tuple{b.hash, b.attr, b.name};
};
auto equal = [](const GdbIndexName &a, const GdbIndexName &b) {
return std::tuple{a.hash, a.attr, a.name} ==
std::tuple{b.hash, b.attr, b.name};
};
std::sort(vec.begin(), vec.end(), less);
vec.erase(std::unique(vec.begin(), vec.end(), equal), vec.end());
return vec;
}
template <typename E>
static u8 *get_buffer(Context<E> &ctx, Chunk<E> *chunk) {
if (u8 *buf = chunk->get_uncompressed_data())
return buf;
return ctx.buf + chunk->shdr.sh_offset;
}
// Try to find a compilation unit from .debug_info and its
// corresponding record from .debug_abbrev and returns them.
template <typename E>
static std::tuple<u8 *, u8 *, u32>
find_compunit(Context<E> &ctx, ObjectFile<E> &file, i64 offset) {
// Read .debug_info to find the record at a given offset.
u8 *cu = get_buffer(ctx, ctx.debug_info) + offset;
u32 dwarf_version = *(U16<E> *)(cu + 4);
u32 abbrev_offset;
// Skip a header.
switch (dwarf_version) {
case 2:
case 3:
case 4:
abbrev_offset = *(U32<E> *)(cu + 6);
if (u32 address_size = cu[10]; address_size != sizeof(Word<E>))
Fatal(ctx) << file << ": --gdb-index: unsupported address size "
<< address_size;
cu += 11;
break;
case 5: {
abbrev_offset = *(U32<E> *)(cu + 8);
if (u32 address_size = cu[7]; address_size != sizeof(Word<E>))
Fatal(ctx) << file << ": --gdb-index: unsupported address size "
<< address_size;
switch (u32 unit_type = cu[6]; unit_type) {
case DW_UT_compile:
case DW_UT_partial:
cu += 12;
break;
case DW_UT_skeleton:
case DW_UT_split_compile:
cu += 20;
break;
default:
Fatal(ctx) << file << ": --gdb-index: unknown DW_UT_* value: 0x"
<< std::hex << unit_type;
}
break;
}
default:
Fatal(ctx) << file << ": --gdb-index: unknown DWARF version: "
<< dwarf_version;
}
u32 abbrev_code = read_uleb(cu);
// 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 = get_buffer(ctx, ctx.debug_abbrev) + abbrev_offset;
for (;;) {
u32 code = read_uleb(abbrev);
if (code == 0)
Fatal(ctx) << file << ": --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) << file << ": --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 {cu, abbrev, dwarf_version};
}
// Estimate the number of address ranges contained in a given file.
// It may over-estimate but never under-estimate.
template <typename E>
i64 estimate_address_areas(Context<E> &ctx, ObjectFile<E> &file) {
// Each CU contains zero or one address area.
i64 ret = file.compunits.size();
// In DWARF 4, a CU can refer address ranges in .debug_ranges.
// .debug_ranges contains a vector of [begin, end) address pairs.
// The last entry must be a null terminator, so we do -1.
if (file.debug_ranges)
ret += file.debug_ranges->sh_size / sizeof(Word<E>) / 2 - 1;
// In DWARF 5, a CU can refer address ranges in .debug_rnglists, which
// contains variable-length entries. The smallest possible range entry
// is one byte for the code and two ULEB128 values (each can be as
// small as one byte), so 3 bytes.
if (file.debug_rnglists)
ret += file.debug_rnglists->sh_size / 3;
return ret;
}
// .debug_info contains variable-length fields. This class reads them.
template <typename E>
class DebugInfoReader {
public:
DebugInfoReader(Context<E> &ctx, ObjectFile<E> &file, u8 *cu)
: ctx(ctx), file(file), cu(cu) {}
u64 read(u64 form);
Context<E> &ctx;
ObjectFile<E> &file;
u8 *cu;
};
// Read value of the given DW_FORM_* form. If a value is not scalar,
// returns a dummy value 0.
template <typename E>
u64 DebugInfoReader<E>::read(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 *cu++;
case DW_FORM_data2:
case DW_FORM_strx2:
case DW_FORM_addrx2:
case DW_FORM_ref2: {
u64 val = *(U16<E> *)cu;
cu += 2;
return val;
}
case DW_FORM_strx3:
case DW_FORM_addrx3: {
u64 val = *(U24<E> *)cu;
cu += 3;
return val;
}
case DW_FORM_data4:
case DW_FORM_strp:
case DW_FORM_sec_offset:
case DW_FORM_line_strp:
case DW_FORM_strx4:
case DW_FORM_addrx4:
case DW_FORM_ref4: {
u64 val = *(U32<E> *)cu;
cu += 4;
return val;
}
case DW_FORM_data8:
case DW_FORM_ref8: {
u64 val = *(U64<E> *)cu;
cu += 8;
return val;
}
case DW_FORM_addr:
case DW_FORM_ref_addr: {
u64 val = *(Word<E> *)cu;
cu += 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(cu);
case DW_FORM_string:
cu += strlen((char *)cu) + 1;
return 0;
default:
Fatal(ctx) << file << ": --gdb-index: unhandled debug info form: 0x"
<< std::hex << form;
return 0;
}
}
// Read a range list from .debug_ranges starting at the given offset.
template <typename E>
static std::vector<u64>
read_debug_range(Context<E> &ctx, ObjectFile<E> &file, Word<E> *range) {
std::vector<u64> vec;
u64 base = 0;
for (i64 i = 0; range[i] || range[i + 1]; i += 2) {
if (range[i] + 1 == 0) {
// base address selection entry
base = range[i + 1];
} else {
vec.push_back(range[i] + base);
vec.push_back(range[i + 1] + base);
}
}
return vec;
}
// Read a range list from .debug_rnglists starting at the given offset.
template <typename E>
static std::vector<u64>
read_rnglist_range(Context<E> &ctx, ObjectFile<E> &file, u8 *rnglist,
Word<E> *addrx) {
std::vector<u64> vec;
u64 base = 0;
for (;;) {
switch (*rnglist++) {
case DW_RLE_end_of_list:
return vec;
case DW_RLE_base_addressx:
base = addrx[read_uleb(rnglist)];
break;
case DW_RLE_startx_endx:
vec.push_back(addrx[read_uleb(rnglist)]);
vec.push_back(addrx[read_uleb(rnglist)]);
break;
case DW_RLE_startx_length:
vec.push_back(addrx[read_uleb(rnglist)]);
vec.push_back(vec.back() + read_uleb(rnglist));
break;
case DW_RLE_offset_pair:
vec.push_back(base + read_uleb(rnglist));
vec.push_back(base + read_uleb(rnglist));
break;
case DW_RLE_base_address:
base = *(Word<E> *)rnglist;
rnglist += sizeof(Word<E>);
break;
case DW_RLE_start_end:
vec.push_back(*(Word<E> *)rnglist);
rnglist += sizeof(Word<E>);
vec.push_back(*(Word<E> *)rnglist);
rnglist += sizeof(Word<E>);
break;
case DW_RLE_start_length:
vec.push_back(*(Word<E> *)rnglist);
rnglist += sizeof(Word<E>);
vec.push_back(vec.back() + read_uleb(rnglist));
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>
std::vector<u64>
read_address_areas(Context<E> &ctx, ObjectFile<E> &file, i64 offset) {
u8 *cu;
u8 *abbrev;
u32 dwarf_version;
std::tie(cu, abbrev, dwarf_version) = find_compunit(ctx, file, offset);
DebugInfoReader<E> reader{ctx, file, cu};
struct Record {
u64 form = 0;
u64 value = 0;
};
Record low_pc;
Record high_pc;
Record ranges;
std::optional<u64> rnglists_base;
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 = reader.read(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> *)(get_buffer(ctx, ctx.debug_addr) + val);
break;
case DW_AT_ranges:
ranges = {form, val};
break;
}
}
// Handle non-contiguous address ranges.
if (ranges.form) {
if (dwarf_version <= 4) {
Word<E> *range_begin =
(Word<E> *)(get_buffer(ctx, ctx.debug_ranges) + ranges.value);
return read_debug_range(ctx, file, range_begin);
}
assert(dwarf_version == 5);
u8 *buf = get_buffer(ctx, ctx.debug_rnglists);
if (ranges.form == DW_FORM_sec_offset)
return read_rnglist_range(ctx, file, buf + ranges.value, addrx);
if (!rnglists_base)
Fatal(ctx) << file << ": --gdb-index: missing DW_AT_rnglists_base";
u8 *base = buf + *rnglists_base;
return read_rnglist_range(ctx, file, base + *(U32<E> *)base, addrx);
}
// 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) << file << ": --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) << file << ": --gdb-index: unhandled form for DW_AT_high_pc: 0x"
<< std::hex << high_pc.form;
}
}
return {};
}
using E = MOLD_TARGET;
template std::vector<std::string_view> read_compunits(Context<E> &, ObjectFile<E> &);
template std::vector<GdbIndexName> read_pubnames(Context<E> &, ObjectFile<E> &);
template i64 estimate_address_areas(Context<E> &, ObjectFile<E> &);
template std::vector<u64> read_address_areas(Context<E> &, ObjectFile<E> &, i64);
} // namespace mold::elf
+938 -904
View File
File diff suppressed because it is too large. Load diff
+335 -311
View File
@@ -2,6 +2,7 @@
#include "../common/integers.h"
#include <concepts>
#include <ostream>
#include <string>
#include <type_traits>
@@ -24,8 +25,8 @@ struct SPARC64;
struct M68K;
struct SH4;
struct ALPHA;
struct MIPS64LE;
struct MIPS64BE;
struct LOONGARCH64;
struct LOONGARCH32;
template <typename E> struct ElfSym;
template <typename E> struct ElfShdr;
@@ -55,9 +56,9 @@ enum : u32 {
enum : u32 {
SHN_UNDEF = 0,
SHN_LORESERVE = 0xff00,
SHN_ABS = 0xfff1,
SHN_COMMON = 0xfff2,
SHN_LORESERVE = 0xff00,
SHN_XINDEX = 0xffff,
};
@@ -88,6 +89,7 @@ enum : u32 {
SHT_X86_64_UNWIND = 0x70000001,
SHT_ARM_EXIDX = 0x70000001,
SHT_ARM_ATTRIBUTES = 0x70000003,
SHT_RISCV_ATTRIBUTES = 0x70000003,
};
enum : u32 {
@@ -102,7 +104,6 @@ enum : u32 {
SHF_TLS = 0x400,
SHF_COMPRESSED = 0x800,
SHF_GNU_RETAIN = 0x200000,
SHF_MIPS_GPREL = 0x10000000,
SHF_EXCLUDE = 0x80000000,
};
@@ -186,6 +187,7 @@ enum : u32 {
PT_GNU_RELRO = 0x6474e552,
PT_OPENBSD_RANDOMIZE = 0x65a3dbe6,
PT_ARM_EXIDX = 0x70000001,
PT_RISCV_ATTRIBUTES = 0x70000003,
};
enum : u32 {
@@ -220,7 +222,6 @@ enum : u32 {
EM_NONE = 0,
EM_386 = 3,
EM_68K = 4,
EM_MIPS = 8,
EM_PPC = 20,
EM_PPC64 = 21,
EM_S390X = 22,
@@ -230,6 +231,7 @@ enum : u32 {
EM_X86_64 = 62,
EM_AARCH64 = 183,
EM_RISCV = 243,
EM_LOONGARCH = 258,
EM_ALPHA = 0x9026,
};
@@ -289,13 +291,7 @@ enum : u32 {
DT_VERNEEDNUM = 0x6fffffff,
DT_PPC_GOT = 0x70000000,
DT_PPC64_GLINK = 0x70000000,
DT_MIPS_RLD_VERSION = 0x70000001,
DT_MIPS_FLAGS = 0x70000005,
DT_MIPS_BASE_ADDRESS = 0x70000006,
DT_MIPS_LOCAL_GOTNO = 0x7000000a,
DT_MIPS_SYMTABNO = 0x70000011,
DT_MIPS_GOTSYM = 0x70000013,
DT_MIPS_OPTIONS = 0x70000029,
DT_AARCH64_VARIANT_PCS = 0x70000005,
DT_AUXILIARY = 0x7ffffffd,
DT_FILTER = 0x7fffffff,
};
@@ -381,6 +377,24 @@ enum : u32 {
STO_ALPHA_STD_GPLOAD = 0x22,
};
enum : u32 {
ELF_TAG_FILE = 1,
ELF_TAG_SECTION = 2,
ELF_TAG_SYMBOL = 3,
ELF_TAG_RISCV_STACK_ALIGN = 4,
ELF_TAG_RISCV_ARCH = 5,
ELF_TAG_RISCV_UNALIGNED_ACCESS = 6,
};
enum : u32 {
EF_LOONGARCH_ABI_SOFT_FLOAT = 0x1,
EF_LOONGARCH_ABI_SINGLE_FLOAT = 0x2,
EF_LOONGARCH_ABI_DOUBLE_FLOAT = 0x3,
EF_LOONGARCH_ABI_MODIFIER_MASK = 0x7,
EF_LOONGARCH_OBJABI_V1 = 0x40,
EF_LOONGARCH_OBJABI_MASK = 0xC0,
};
//
// Relocation types
//
@@ -731,6 +745,7 @@ enum : u32 {
R_RISCV_TLS_DTPREL64 = 9,
R_RISCV_TLS_TPREL32 = 10,
R_RISCV_TLS_TPREL64 = 11,
R_RISCV_TLSDESC = 12,
R_RISCV_BRANCH = 16,
R_RISCV_JAL = 17,
R_RISCV_CALL = 18,
@@ -760,6 +775,9 @@ enum : u32 {
R_RISCV_RVC_BRANCH = 44,
R_RISCV_RVC_JUMP = 45,
R_RISCV_RVC_LUI = 46,
R_RISCV_GPREL_LO12_I = 47,
R_RISCV_GPREL_LO12_S = 48,
R_RISCV_GPREL_HI20 = 49,
R_RISCV_RELAX = 51,
R_RISCV_SUB6 = 52,
R_RISCV_SET6 = 53,
@@ -771,6 +789,10 @@ enum : u32 {
R_RISCV_PLT32 = 59,
R_RISCV_SET_ULEB128 = 60,
R_RISCV_SUB_ULEB128 = 61,
R_RISCV_TLSDESC_HI20 = 62,
R_RISCV_TLSDESC_LOAD_LO12 = 63,
R_RISCV_TLSDESC_ADD_LO12 = 64,
R_RISCV_TLSDESC_CALL = 65,
};
enum : u32 {
@@ -1236,82 +1258,104 @@ enum : u32 {
};
enum : u32 {
R_MIPS_NONE = 0,
R_MIPS_16 = 1,
R_MIPS_32 = 2,
R_MIPS_REL32 = 3,
R_MIPS_26 = 4,
R_MIPS_HI16 = 5,
R_MIPS_LO16 = 6,
R_MIPS_GPREL16 = 7,
R_MIPS_LITERAL = 8,
R_MIPS_GOT16 = 9,
R_MIPS_PC16 = 10,
R_MIPS_CALL16 = 11,
R_MIPS_GPREL32 = 12,
R_MIPS_UNUSED1 = 13,
R_MIPS_UNUSED2 = 14,
R_MIPS_UNUSED3 = 15,
R_MIPS_SHIFT5 = 16,
R_MIPS_SHIFT6 = 17,
R_MIPS_64 = 18,
R_MIPS_GOT_DISP = 19,
R_MIPS_GOT_PAGE = 20,
R_MIPS_GOT_OFST = 21,
R_MIPS_GOT_HI16 = 22,
R_MIPS_GOT_LO16 = 23,
R_MIPS_SUB = 24,
R_MIPS_INSERT_A = 25,
R_MIPS_INSERT_B = 26,
R_MIPS_DELETE = 27,
R_MIPS_HIGHER = 28,
R_MIPS_HIGHEST = 29,
R_MIPS_CALL_HI16 = 30,
R_MIPS_CALL_LO16 = 31,
R_MIPS_SCN_DISP = 32,
R_MIPS_REL16 = 33,
R_MIPS_ADD_IMMEDIATE = 34,
R_MIPS_PJUMP = 35,
R_MIPS_RELGOT = 36,
R_MIPS_JALR = 37,
R_MIPS_TLS_DTPMOD32 = 38,
R_MIPS_TLS_DTPREL32 = 39,
R_MIPS_TLS_DTPMOD64 = 40,
R_MIPS_TLS_DTPREL64 = 41,
R_MIPS_TLS_GD = 42,
R_MIPS_TLS_LDM = 43,
R_MIPS_TLS_DTPREL_HI16 = 44,
R_MIPS_TLS_DTPREL_LO16 = 45,
R_MIPS_TLS_GOTTPREL = 46,
R_MIPS_TLS_TPREL32 = 47,
R_MIPS_TLS_TPREL64 = 48,
R_MIPS_TLS_TPREL_HI16 = 49,
R_MIPS_TLS_TPREL_LO16 = 50,
R_MIPS_GLOB_DAT = 51,
R_MIPS_PC21_S2 = 60,
R_MIPS_PC26_S2 = 61,
R_MIPS_PC18_S3 = 62,
R_MIPS_PC19_S2 = 63,
R_MIPS_PCHI16 = 64,
R_MIPS_PCLO16 = 65,
R_MIPS16_26 = 100,
R_MIPS16_GPREL = 101,
R_MIPS16_GOT16 = 102,
R_MIPS16_CALL16 = 103,
R_MIPS16_HI16 = 104,
R_MIPS16_LO16 = 105,
R_MIPS16_TLS_GD = 106,
R_MIPS16_TLS_LDM = 107,
R_MIPS16_TLS_DTPREL_HI16 = 108,
R_MIPS16_TLS_DTPREL_LO16 = 109,
R_MIPS16_TLS_GOTTPREL = 110,
R_MIPS16_TLS_TPREL_HI16 = 111,
R_MIPS16_TLS_TPREL_LO16 = 112,
R_MIPS_COPY = 126,
R_MIPS_JUMP_SLOT = 127,
R_MIPS_NUM = 218,
R_MIPS_PC32 = 248,
R_MIPS_EH = 249,
R_LARCH_NONE = 0,
R_LARCH_32 = 1,
R_LARCH_64 = 2,
R_LARCH_RELATIVE = 3,
R_LARCH_COPY = 4,
R_LARCH_JUMP_SLOT = 5,
R_LARCH_TLS_DTPMOD32 = 6,
R_LARCH_TLS_DTPMOD64 = 7,
R_LARCH_TLS_DTPREL32 = 8,
R_LARCH_TLS_DTPREL64 = 9,
R_LARCH_TLS_TPREL32 = 10,
R_LARCH_TLS_TPREL64 = 11,
R_LARCH_IRELATIVE = 12,
R_LARCH_MARK_LA = 20,
R_LARCH_MARK_PCREL = 21,
R_LARCH_SOP_PUSH_PCREL = 22,
R_LARCH_SOP_PUSH_ABSOLUTE = 23,
R_LARCH_SOP_PUSH_DUP = 24,
R_LARCH_SOP_PUSH_GPREL = 25,
R_LARCH_SOP_PUSH_TLS_TPREL = 26,
R_LARCH_SOP_PUSH_TLS_GOT = 27,
R_LARCH_SOP_PUSH_TLS_GD = 28,
R_LARCH_SOP_PUSH_PLT_PCREL = 29,
R_LARCH_SOP_ASSERT = 30,
R_LARCH_SOP_NOT = 31,
R_LARCH_SOP_SUB = 32,
R_LARCH_SOP_SL = 33,
R_LARCH_SOP_SR = 34,
R_LARCH_SOP_ADD = 35,
R_LARCH_SOP_AND = 36,
R_LARCH_SOP_IF_ELSE = 37,
R_LARCH_SOP_POP_32_S_10_5 = 38,
R_LARCH_SOP_POP_32_U_10_12 = 39,
R_LARCH_SOP_POP_32_S_10_12 = 40,
R_LARCH_SOP_POP_32_S_10_16 = 41,
R_LARCH_SOP_POP_32_S_10_16_S2 = 42,
R_LARCH_SOP_POP_32_S_5_20 = 43,
R_LARCH_SOP_POP_32_S_0_5_10_16_S2 = 44,
R_LARCH_SOP_POP_32_S_0_10_10_16_S2 = 45,
R_LARCH_SOP_POP_32_U = 46,
R_LARCH_ADD8 = 47,
R_LARCH_ADD16 = 48,
R_LARCH_ADD24 = 49,
R_LARCH_ADD32 = 50,
R_LARCH_ADD64 = 51,
R_LARCH_SUB8 = 52,
R_LARCH_SUB16 = 53,
R_LARCH_SUB24 = 54,
R_LARCH_SUB32 = 55,
R_LARCH_SUB64 = 56,
R_LARCH_GNU_VTINHERIT = 57,
R_LARCH_GNU_VTENTRY = 58,
R_LARCH_B16 = 64,
R_LARCH_B21 = 65,
R_LARCH_B26 = 66,
R_LARCH_ABS_HI20 = 67,
R_LARCH_ABS_LO12 = 68,
R_LARCH_ABS64_LO20 = 69,
R_LARCH_ABS64_HI12 = 70,
R_LARCH_PCALA_HI20 = 71,
R_LARCH_PCALA_LO12 = 72,
R_LARCH_PCALA64_LO20 = 73,
R_LARCH_PCALA64_HI12 = 74,
R_LARCH_GOT_PC_HI20 = 75,
R_LARCH_GOT_PC_LO12 = 76,
R_LARCH_GOT64_PC_LO20 = 77,
R_LARCH_GOT64_PC_HI12 = 78,
R_LARCH_GOT_HI20 = 79,
R_LARCH_GOT_LO12 = 80,
R_LARCH_GOT64_LO20 = 81,
R_LARCH_GOT64_HI12 = 82,
R_LARCH_TLS_LE_HI20 = 83,
R_LARCH_TLS_LE_LO12 = 84,
R_LARCH_TLS_LE64_LO20 = 85,
R_LARCH_TLS_LE64_HI12 = 86,
R_LARCH_TLS_IE_PC_HI20 = 87,
R_LARCH_TLS_IE_PC_LO12 = 88,
R_LARCH_TLS_IE64_PC_LO20 = 89,
R_LARCH_TLS_IE64_PC_HI12 = 90,
R_LARCH_TLS_IE_HI20 = 91,
R_LARCH_TLS_IE_LO12 = 92,
R_LARCH_TLS_IE64_LO20 = 93,
R_LARCH_TLS_IE64_HI12 = 94,
R_LARCH_TLS_LD_PC_HI20 = 95,
R_LARCH_TLS_LD_HI20 = 96,
R_LARCH_TLS_GD_PC_HI20 = 97,
R_LARCH_TLS_GD_HI20 = 98,
R_LARCH_32_PCREL = 99,
R_LARCH_RELAX = 100,
R_LARCH_DELETE = 101,
R_LARCH_ALIGN = 102,
R_LARCH_PCREL20_S2 = 103,
R_LARCH_CFA = 104,
R_LARCH_ADD6 = 105,
R_LARCH_SUB6 = 106,
R_LARCH_ADD_ULEB128 = 107,
R_LARCH_SUB_ULEB128 = 108,
R_LARCH_64_PCREL = 109,
};
//
@@ -1598,6 +1642,16 @@ struct ElfRel<E> {
std::conditional_t<E::is_64, U32<E>, u8> r_type;
};
// Returns true if a given relocation is of type used for direct
// function call.
template <typename E>
inline bool is_func_call_rel(const ElfRel<E> &r) {
for (u32 r_type : E::R_FUNCALL)
if (r.r_type == r_type)
return true;
return false;
}
template <typename E>
struct ElfDyn {
Word<E> d_tag;
@@ -1665,6 +1719,35 @@ struct ElfNhdr {
// Target-specific ELF data types
//
template <>
struct ElfSym<ARM64> {
bool is_undef() const { return st_shndx == SHN_UNDEF; }
bool is_abs() const { return st_shndx == SHN_ABS; }
bool is_common() const { return st_shndx == SHN_COMMON; }
bool is_weak() const { return st_bind == STB_WEAK; }
bool is_undef_weak() const { return is_undef() && is_weak(); }
ul32 st_name;
#ifdef __LITTLE_ENDIAN__
u8 st_type : 4;
u8 st_bind : 4;
u8 st_visibility : 2;
u8 : 5;
u8 arm64_variant_pcs : 1; // ARM64-specific
#else
u8 st_bind : 4;
u8 st_type : 4;
u8 arm64_variant_pcs : 1;
u8 : 6;
u8 st_visibility : 2;
#endif
ul16 st_shndx;
ul64 st_value;
ul64 st_size;
};
template <>
struct ElfSym<PPC64V2> {
bool is_undef() const { return st_shndx == SHN_UNDEF; }
@@ -1738,107 +1821,42 @@ struct ElfRel<SPARC64> {
ib64 r_addend;
};
static inline u8 get_mips_r_type2(u8 r_type) {
if (r_type == R_MIPS_REL32 || r_type == R_MIPS_GLOB_DAT)
return R_MIPS_64;
return R_NONE;
}
template <>
struct ElfRel<MIPS64LE> {
ElfRel() = default;
ElfRel(u64 offset, u32 type, u32 sym, i64 addend)
: r_offset(offset), r_sym(sym), r_type(type), r_type2(get_mips_r_type2(type)),
r_type3(0), r_padding(0), r_addend(addend) {}
ul64 r_offset;
ul32 r_sym;
u8 r_padding;
u8 r_type3;
u8 r_type2;
u8 r_type;
il64 r_addend;
};
template <>
struct ElfRel<MIPS64BE> {
ElfRel() = default;
ElfRel(u64 offset, u32 type, u32 sym, i64 addend)
: r_offset(offset), r_sym(sym), r_padding(0), r_type3(0),
r_type2(get_mips_r_type2(type)), r_type(type), r_addend(addend) {}
ub64 r_offset;
ub32 r_sym;
u8 r_padding;
u8 r_type3;
u8 r_type2;
u8 r_type;
ib64 r_addend;
};
// .MIPS.options section
template <typename E>
struct MipsOptions {
u8 kind;
u8 size;
U16<E> section;
U32<E> info;
};
template <typename E>
struct MipsRegInfo {
U32<E> ri_gprmask;
U32<E> ri_pad;
U32<E> ri_cprmask[4];
U64<E> ri_gp_value;
};
enum : u32 {
ODK_REGINFO = 1,
};
//
// Machine descriptions
//
template <typename E>
static constexpr bool supports_ifunc = requires { E::R_IRELATIVE; };
template <typename E> concept supports_ifunc = requires { E::R_IRELATIVE; };
template <typename E> concept supports_tlsdesc = requires { E::R_TLSDESC; };
template <typename E> concept needs_thunk = requires { E::thunk_size; };
template <typename E>
static constexpr bool supports_tlsdesc = requires { E::R_TLSDESC; };
template <typename E> concept is_x86_64 = std::same_as<E, X86_64>;
template <typename E> concept is_i386 = std::same_as<E, I386>;
template <typename E> concept is_arm64 = std::same_as<E, ARM64>;
template <typename E> concept is_arm32 = std::same_as<E, ARM32>;
template <typename E> concept is_rv64le = std::same_as<E, RV64LE>;
template <typename E> concept is_rv64be = std::same_as<E, RV64BE>;
template <typename E> concept is_rv32le = std::same_as<E, RV32LE>;
template <typename E> concept is_rv32be = std::same_as<E, RV32BE>;
template <typename E> concept is_ppc32 = std::same_as<E, PPC32>;
template <typename E> concept is_ppc64v1 = std::same_as<E, PPC64V1>;
template <typename E> concept is_ppc64v2 = std::same_as<E, PPC64V2>;
template <typename E> concept is_s390x = std::same_as<E, S390X>;
template <typename E> concept is_sparc64 = std::same_as<E, SPARC64>;
template <typename E> concept is_m68k = std::same_as<E, M68K>;
template <typename E> concept is_sh4 = std::same_as<E, SH4>;
template <typename E> concept is_alpha = std::same_as<E, ALPHA>;
template <typename E> concept is_loongarch64 = std::same_as<E, LOONGARCH64>;
template <typename E> concept is_loongarch32 = std::same_as<E, LOONGARCH32>;
template <typename E>
static constexpr bool needs_thunk = requires { E::thunk_size; };
template <typename E> static constexpr bool is_x86_64 = std::is_same_v<E, X86_64>;
template <typename E> static constexpr bool is_i386 = std::is_same_v<E, I386>;
template <typename E> static constexpr bool is_arm64 = std::is_same_v<E, ARM64>;
template <typename E> static constexpr bool is_arm32 = std::is_same_v<E, ARM32>;
template <typename E> static constexpr bool is_rv64le = std::is_same_v<E, RV64LE>;
template <typename E> static constexpr bool is_rv64be = std::is_same_v<E, RV64BE>;
template <typename E> static constexpr bool is_rv32le = std::is_same_v<E, RV32LE>;
template <typename E> static constexpr bool is_rv32be = std::is_same_v<E, RV32BE>;
template <typename E> static constexpr bool is_ppc32 = std::is_same_v<E, PPC32>;
template <typename E> static constexpr bool is_ppc64v1 = std::is_same_v<E, PPC64V1>;
template <typename E> static constexpr bool is_ppc64v2 = std::is_same_v<E, PPC64V2>;
template <typename E> static constexpr bool is_s390x = std::is_same_v<E, S390X>;
template <typename E> static constexpr bool is_sparc64 = std::is_same_v<E, SPARC64>;
template <typename E> static constexpr bool is_m68k = std::is_same_v<E, M68K>;
template <typename E> static constexpr bool is_sh4 = std::is_same_v<E, SH4>;
template <typename E> static constexpr bool is_alpha = std::is_same_v<E, ALPHA>;
template <typename E> static constexpr bool is_mips64le = std::is_same_v<E, MIPS64LE>;
template <typename E> static constexpr bool is_mips64be = std::is_same_v<E, MIPS64BE>;
template <typename E> static constexpr bool is_x86 = is_x86_64<E> || is_i386<E>;
template <typename E> static constexpr bool is_arm = is_arm64<E> || is_arm32<E>;
template <typename E> static constexpr bool is_rv64 = is_rv64le<E> || is_rv64be<E>;
template <typename E> static constexpr bool is_rv32 = is_rv32le<E> || is_rv32be<E>;
template <typename E> static constexpr bool is_riscv = is_rv64<E> || is_rv32<E>;
template <typename E> static constexpr bool is_ppc64 = is_ppc64v1<E> || is_ppc64v2<E>;
template <typename E> static constexpr bool is_ppc = is_ppc64<E> || is_ppc32<E>;
template <typename E> static constexpr bool is_sparc = is_sparc64<E>;
template <typename E> static constexpr bool is_mips64 = is_mips64le<E> || is_mips64be<E>;
template <typename E> static constexpr bool is_mips = is_mips64<E>;
template <typename E> concept is_x86 = is_x86_64<E> || is_i386<E>;
template <typename E> concept is_arm = is_arm64<E> || is_arm32<E>;
template <typename E> concept is_rv64 = is_rv64le<E> || is_rv64be<E>;
template <typename E> concept is_rv32 = is_rv32le<E> || is_rv32be<E>;
template <typename E> concept is_riscv = is_rv64<E> || is_rv32<E>;
template <typename E> concept is_ppc64 = is_ppc64v1<E> || is_ppc64v2<E>;
template <typename E> concept is_ppc = is_ppc64<E> || is_ppc32<E>;
template <typename E> concept is_sparc = is_sparc64<E>;
template <typename E> concept is_loongarch = is_loongarch64<E> || is_loongarch32<E>;
struct X86_64 {
static constexpr std::string_view target_name = "x86_64";
@@ -1849,7 +1867,8 @@ struct X86_64 {
static constexpr u32 e_machine = EM_X86_64;
static constexpr u32 plt_hdr_size = 32;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 16;
static constexpr u32 pltgot_size = 8;
static constexpr u8 filler[] = { 0xcc };
static constexpr u32 R_COPY = R_X86_64_COPY;
static constexpr u32 R_GLOB_DAT = R_X86_64_GLOB_DAT;
@@ -1861,6 +1880,7 @@ struct X86_64 {
static constexpr u32 R_TPOFF = R_X86_64_TPOFF64;
static constexpr u32 R_DTPMOD = R_X86_64_DTPMOD64;
static constexpr u32 R_TLSDESC = R_X86_64_TLSDESC;
static constexpr u32 R_FUNCALL[] = { R_X86_64_PLT32, R_X86_64_PLTOFF64 };
};
struct I386 {
@@ -1873,6 +1893,7 @@ struct I386 {
static constexpr u32 plt_hdr_size = 16;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 16;
static constexpr u8 filler[] = { 0xcc };
static constexpr u32 R_COPY = R_386_COPY;
static constexpr u32 R_GLOB_DAT = R_386_GLOB_DAT;
@@ -1884,6 +1905,7 @@ struct I386 {
static constexpr u32 R_TPOFF = R_386_TLS_TPOFF;
static constexpr u32 R_DTPMOD = R_386_TLS_DTPMOD32;
static constexpr u32 R_TLSDESC = R_386_TLS_DESC;
static constexpr u32 R_FUNCALL[] = { R_386_PLT32 };
};
struct ARM64 {
@@ -1897,7 +1919,8 @@ struct ARM64 {
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 16;
static constexpr u32 thunk_hdr_size = 0;
static constexpr u32 thunk_size = 12;
static constexpr u32 thunk_size = 16;
static constexpr u8 filler[] = { 0x00, 0x7d, 0x20, 0xd4 };
static constexpr u32 R_COPY = R_AARCH64_COPY;
static constexpr u32 R_GLOB_DAT = R_AARCH64_GLOB_DAT;
@@ -1909,6 +1932,7 @@ struct ARM64 {
static constexpr u32 R_TPOFF = R_AARCH64_TLS_TPREL64;
static constexpr u32 R_DTPMOD = R_AARCH64_TLS_DTPMOD64;
static constexpr u32 R_TLSDESC = R_AARCH64_TLSDESC;
static constexpr u32 R_FUNCALL[] = { R_AARCH64_JUMP26, R_AARCH64_CALL26 };
};
struct ARM32 {
@@ -1921,8 +1945,9 @@ struct ARM32 {
static constexpr u32 plt_hdr_size = 32;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 16;
static constexpr u32 thunk_hdr_size = 12;
static constexpr u32 thunk_size = 20;
static constexpr u32 thunk_hdr_size = 16;
static constexpr u32 thunk_size = 16;
static constexpr u8 filler[] = { 0xff, 0xde };
static constexpr u32 R_COPY = R_ARM_COPY;
static constexpr u32 R_GLOB_DAT = R_ARM_GLOB_DAT;
@@ -1934,94 +1959,76 @@ struct ARM32 {
static constexpr u32 R_TPOFF = R_ARM_TLS_TPOFF32;
static constexpr u32 R_DTPMOD = R_ARM_TLS_DTPMOD32;
static constexpr u32 R_TLSDESC = R_ARM_TLS_DESC;
static constexpr u32 R_FUNCALL[] = {
R_ARM_JUMP24, R_ARM_THM_JUMP24, R_ARM_CALL, R_ARM_THM_CALL, R_ARM_PLT32,
};
};
struct RV64LE {
struct RV64 {
static constexpr bool is_64 = true;
static constexpr bool is_rela = true;
static constexpr u32 page_size = 4096;
static constexpr u32 e_machine = EM_RISCV;
static constexpr u32 plt_hdr_size = 32;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 16;
static constexpr u8 filler[] = { 0x02, 0x90 };
static constexpr u32 R_COPY = R_RISCV_COPY;
static constexpr u32 R_GLOB_DAT = R_RISCV_64;
static constexpr u32 R_JUMP_SLOT = R_RISCV_JUMP_SLOT;
static constexpr u32 R_ABS = R_RISCV_64;
static constexpr u32 R_RELATIVE = R_RISCV_RELATIVE;
static constexpr u32 R_IRELATIVE = R_RISCV_IRELATIVE;
static constexpr u32 R_DTPOFF = R_RISCV_TLS_DTPREL64;
static constexpr u32 R_TPOFF = R_RISCV_TLS_TPREL64;
static constexpr u32 R_DTPMOD = R_RISCV_TLS_DTPMOD64;
static constexpr u32 R_TLSDESC = R_RISCV_TLSDESC;
static constexpr u32 R_FUNCALL[] = { R_RISCV_CALL, R_RISCV_CALL_PLT };
};
struct RV64LE : RV64 {
static constexpr std::string_view target_name = "riscv64";
static constexpr bool is_64 = true;
static constexpr bool is_le = true;
static constexpr bool is_rela = true;
static constexpr u32 page_size = 4096;
static constexpr u32 e_machine = EM_RISCV;
static constexpr u32 plt_hdr_size = 32;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 16;
static constexpr u32 R_COPY = R_RISCV_COPY;
static constexpr u32 R_GLOB_DAT = R_RISCV_64;
static constexpr u32 R_JUMP_SLOT = R_RISCV_JUMP_SLOT;
static constexpr u32 R_ABS = R_RISCV_64;
static constexpr u32 R_RELATIVE = R_RISCV_RELATIVE;
static constexpr u32 R_IRELATIVE = R_RISCV_IRELATIVE;
static constexpr u32 R_DTPOFF = R_RISCV_TLS_DTPREL64;
static constexpr u32 R_TPOFF = R_RISCV_TLS_TPREL64;
static constexpr u32 R_DTPMOD = R_RISCV_TLS_DTPMOD64;
};
struct RV64BE {
struct RV64BE : RV64 {
static constexpr std::string_view target_name = "riscv64be";
static constexpr bool is_64 = true;
static constexpr bool is_le = false;
};
struct RV32 {
static constexpr bool is_64 = false;
static constexpr bool is_rela = true;
static constexpr u32 page_size = 4096;
static constexpr u32 e_machine = EM_RISCV;
static constexpr u32 plt_hdr_size = 32;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 16;
static constexpr u8 filler[] = { 0x02, 0x90 };
static constexpr u32 R_COPY = R_RISCV_COPY;
static constexpr u32 R_GLOB_DAT = R_RISCV_64;
static constexpr u32 R_GLOB_DAT = R_RISCV_32;
static constexpr u32 R_JUMP_SLOT = R_RISCV_JUMP_SLOT;
static constexpr u32 R_ABS = R_RISCV_64;
static constexpr u32 R_ABS = R_RISCV_32;
static constexpr u32 R_RELATIVE = R_RISCV_RELATIVE;
static constexpr u32 R_IRELATIVE = R_RISCV_IRELATIVE;
static constexpr u32 R_DTPOFF = R_RISCV_TLS_DTPREL64;
static constexpr u32 R_TPOFF = R_RISCV_TLS_TPREL64;
static constexpr u32 R_DTPMOD = R_RISCV_TLS_DTPMOD64;
static constexpr u32 R_DTPOFF = R_RISCV_TLS_DTPREL32;
static constexpr u32 R_TPOFF = R_RISCV_TLS_TPREL32;
static constexpr u32 R_DTPMOD = R_RISCV_TLS_DTPMOD32;
static constexpr u32 R_TLSDESC = R_RISCV_TLSDESC;
static constexpr u32 R_FUNCALL[] = { R_RISCV_CALL, R_RISCV_CALL_PLT };
};
struct RV32LE {
struct RV32LE : RV32 {
static constexpr std::string_view target_name = "riscv32";
static constexpr bool is_64 = false;
static constexpr bool is_le = true;
static constexpr bool is_rela = true;
static constexpr u32 page_size = 4096;
static constexpr u32 e_machine = EM_RISCV;
static constexpr u32 plt_hdr_size = 32;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 16;
static constexpr u32 R_COPY = R_RISCV_COPY;
static constexpr u32 R_GLOB_DAT = R_RISCV_32;
static constexpr u32 R_JUMP_SLOT = R_RISCV_JUMP_SLOT;
static constexpr u32 R_ABS = R_RISCV_32;
static constexpr u32 R_RELATIVE = R_RISCV_RELATIVE;
static constexpr u32 R_IRELATIVE = R_RISCV_IRELATIVE;
static constexpr u32 R_DTPOFF = R_RISCV_TLS_DTPREL32;
static constexpr u32 R_TPOFF = R_RISCV_TLS_TPREL32;
static constexpr u32 R_DTPMOD = R_RISCV_TLS_DTPMOD32;
};
struct RV32BE {
struct RV32BE : RV32 {
static constexpr std::string_view target_name = "riscv32be";
static constexpr bool is_64 = false;
static constexpr bool is_le = false;
static constexpr bool is_rela = true;
static constexpr u32 page_size = 4096;
static constexpr u32 e_machine = EM_RISCV;
static constexpr u32 plt_hdr_size = 32;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 16;
static constexpr u32 R_COPY = R_RISCV_COPY;
static constexpr u32 R_GLOB_DAT = R_RISCV_32;
static constexpr u32 R_JUMP_SLOT = R_RISCV_JUMP_SLOT;
static constexpr u32 R_ABS = R_RISCV_32;
static constexpr u32 R_RELATIVE = R_RISCV_RELATIVE;
static constexpr u32 R_IRELATIVE = R_RISCV_IRELATIVE;
static constexpr u32 R_DTPOFF = R_RISCV_TLS_DTPREL32;
static constexpr u32 R_TPOFF = R_RISCV_TLS_TPREL32;
static constexpr u32 R_DTPMOD = R_RISCV_TLS_DTPMOD32;
};
struct PPC32 {
@@ -2036,6 +2043,7 @@ struct PPC32 {
static constexpr u32 pltgot_size = 36;
static constexpr u32 thunk_hdr_size = 0;
static constexpr u32 thunk_size = 36;
static constexpr u8 filler[] = { 0x7f, 0xe0, 0x00, 0x08 };
static constexpr u32 R_COPY = R_PPC_COPY;
static constexpr u32 R_GLOB_DAT = R_PPC_GLOB_DAT;
@@ -2046,53 +2054,49 @@ struct PPC32 {
static constexpr u32 R_DTPOFF = R_PPC_DTPREL32;
static constexpr u32 R_TPOFF = R_PPC_TPREL32;
static constexpr u32 R_DTPMOD = R_PPC_DTPMOD32;
static constexpr u32 R_FUNCALL[] = {
R_PPC_REL24, R_PPC_PLTREL24, R_PPC_LOCAL24PC,
};
};
struct PPC64V1 {
static constexpr std::string_view target_name = "ppc64v1";
struct PPC64 {
static constexpr bool is_64 = true;
static constexpr bool is_le = false;
static constexpr bool is_rela = true;
static constexpr u32 page_size = 65536;
static constexpr u32 e_machine = EM_PPC64;
static constexpr u32 plt_hdr_size = 52;
static constexpr u32 R_COPY = R_PPC64_COPY;
static constexpr u32 R_GLOB_DAT = R_PPC64_GLOB_DAT;
static constexpr u32 R_JUMP_SLOT = R_PPC64_JMP_SLOT;
static constexpr u32 R_ABS = R_PPC64_ADDR64;
static constexpr u32 R_RELATIVE = R_PPC64_RELATIVE;
static constexpr u32 R_IRELATIVE = R_PPC64_IRELATIVE;
static constexpr u32 R_DTPOFF = R_PPC64_DTPREL64;
static constexpr u32 R_TPOFF = R_PPC64_TPREL64;
static constexpr u32 R_DTPMOD = R_PPC64_DTPMOD64;
static constexpr u32 R_FUNCALL[] = { R_PPC64_REL24, R_PPC64_REL24_NOTOC };
};
struct PPC64V1 : PPC64 {
static constexpr std::string_view target_name = "ppc64v1";
static constexpr bool is_le = false;
static constexpr u32 plt_hdr_size = 44;
static constexpr u32 pltgot_size = 0;
static constexpr u32 thunk_hdr_size = 0;
static constexpr u32 thunk_size = 28;
static constexpr u32 R_COPY = R_PPC64_COPY;
static constexpr u32 R_GLOB_DAT = R_PPC64_GLOB_DAT;
static constexpr u32 R_JUMP_SLOT = R_PPC64_JMP_SLOT;
static constexpr u32 R_ABS = R_PPC64_ADDR64;
static constexpr u32 R_RELATIVE = R_PPC64_RELATIVE;
static constexpr u32 R_IRELATIVE = R_PPC64_IRELATIVE;
static constexpr u32 R_DTPOFF = R_PPC64_DTPREL64;
static constexpr u32 R_TPOFF = R_PPC64_TPREL64;
static constexpr u32 R_DTPMOD = R_PPC64_DTPMOD64;
static constexpr u8 filler[] = { 0x7f, 0xe0, 0x00, 0x08 };
};
struct PPC64V2 {
struct PPC64V2 : PPC64 {
static constexpr std::string_view target_name = "ppc64v2";
static constexpr bool is_64 = true;
static constexpr bool is_le = true;
static constexpr bool is_rela = true;
static constexpr u32 page_size = 65536;
static constexpr u32 e_machine = EM_PPC64;
static constexpr u32 plt_hdr_size = 60;
static constexpr u32 plt_hdr_size = 52;
static constexpr u32 plt_size = 4;
static constexpr u32 pltgot_size = 0;
static constexpr u32 thunk_hdr_size = 0;
static constexpr u32 thunk_size = 20;
static constexpr u32 R_COPY = R_PPC64_COPY;
static constexpr u32 R_GLOB_DAT = R_PPC64_GLOB_DAT;
static constexpr u32 R_JUMP_SLOT = R_PPC64_JMP_SLOT;
static constexpr u32 R_ABS = R_PPC64_ADDR64;
static constexpr u32 R_RELATIVE = R_PPC64_RELATIVE;
static constexpr u32 R_IRELATIVE = R_PPC64_IRELATIVE;
static constexpr u32 R_DTPOFF = R_PPC64_DTPREL64;
static constexpr u32 R_TPOFF = R_PPC64_TPREL64;
static constexpr u32 R_DTPMOD = R_PPC64_DTPMOD64;
static constexpr u32 thunk_size = 24;
static constexpr u8 filler[] = { 0x08, 0x00, 0xe0, 0x7f };
};
struct S390X {
@@ -2102,9 +2106,10 @@ struct S390X {
static constexpr bool is_rela = true;
static constexpr u32 page_size = 4096;
static constexpr u32 e_machine = EM_S390X;
static constexpr u32 plt_hdr_size = 32;
static constexpr u32 plt_size = 32;
static constexpr u32 plt_hdr_size = 48;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 16;
static constexpr u8 filler[] = { 0x07, 0x00 };
static constexpr u32 R_COPY = R_390_COPY;
static constexpr u32 R_GLOB_DAT = R_390_GLOB_DAT;
@@ -2115,6 +2120,7 @@ struct S390X {
static constexpr u32 R_DTPOFF = R_390_TLS_DTPOFF;
static constexpr u32 R_TPOFF = R_390_TLS_TPOFF;
static constexpr u32 R_DTPMOD = R_390_TLS_DTPMOD;
static constexpr u32 R_FUNCALL[] = { R_390_PLT32DBL };
};
struct SPARC64 {
@@ -2127,6 +2133,7 @@ struct SPARC64 {
static constexpr u32 plt_hdr_size = 128;
static constexpr u32 plt_size = 32;
static constexpr u32 pltgot_size = 32;
static constexpr u8 filler[] = { 0x91, 0xd0, 0x20, 0x05 };
static constexpr u32 R_COPY = R_SPARC_COPY;
static constexpr u32 R_GLOB_DAT = R_SPARC_GLOB_DAT;
@@ -2137,6 +2144,7 @@ struct SPARC64 {
static constexpr u32 R_DTPOFF = R_SPARC_TLS_DTPOFF64;
static constexpr u32 R_TPOFF = R_SPARC_TLS_TPOFF64;
static constexpr u32 R_DTPMOD = R_SPARC_TLS_DTPMOD64;
static constexpr u32 R_FUNCALL[] = { R_SPARC_WPLT30, R_SPARC_WDISP30 };
};
struct M68K {
@@ -2149,6 +2157,7 @@ struct M68K {
static constexpr u32 plt_hdr_size = 18;
static constexpr u32 plt_size = 14;
static constexpr u32 pltgot_size = 8;
static constexpr u8 filler[] = { 0x4a, 0xfc };
static constexpr u32 R_COPY = R_68K_COPY;
static constexpr u32 R_GLOB_DAT = R_68K_GLOB_DAT;
@@ -2158,6 +2167,7 @@ struct M68K {
static constexpr u32 R_DTPOFF = R_68K_TLS_DTPREL32;
static constexpr u32 R_TPOFF = R_68K_TLS_TPREL32;
static constexpr u32 R_DTPMOD = R_68K_TLS_DTPMOD32;
static constexpr u32 R_FUNCALL[] = { R_68K_PLT32 };
};
struct SH4 {
@@ -2170,6 +2180,7 @@ struct SH4 {
static constexpr u32 plt_hdr_size = 16;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 12;
static constexpr u8 filler[] = { 0x00, 0x00 };
static constexpr u32 R_COPY = R_SH_COPY;
static constexpr u32 R_GLOB_DAT = R_SH_GLOB_DAT;
@@ -2179,6 +2190,7 @@ struct SH4 {
static constexpr u32 R_DTPOFF = R_SH_TLS_DTPOFF32;
static constexpr u32 R_TPOFF = R_SH_TLS_TPOFF32;
static constexpr u32 R_DTPMOD = R_SH_TLS_DTPMOD32;
static constexpr u32 R_FUNCALL[] = { R_SH_PLT32 };
};
struct ALPHA {
@@ -2191,6 +2203,7 @@ struct ALPHA {
static constexpr u32 plt_hdr_size = 0;
static constexpr u32 plt_size = 0;
static constexpr u32 pltgot_size = 0;
static constexpr u8 filler[] = { 0x81, 0x00, 0x00, 0x00 };
static constexpr u32 R_COPY = R_ALPHA_COPY;
static constexpr u32 R_GLOB_DAT = R_ALPHA_GLOB_DAT;
@@ -2200,48 +2213,59 @@ struct ALPHA {
static constexpr u32 R_DTPOFF = R_ALPHA_DTPREL64;
static constexpr u32 R_TPOFF = R_ALPHA_TPREL64;
static constexpr u32 R_DTPMOD = R_ALPHA_DTPMOD64;
static constexpr u32 R_FUNCALL[] = {};
};
struct MIPS64LE {
static constexpr std::string_view target_name = "mips64le";
struct LOONGARCH64 {
static constexpr std::string_view target_name = "loongarch64";
static constexpr bool is_64 = true;
static constexpr bool is_le = true;
static constexpr bool is_rela = true;
static constexpr u32 page_size = 4096;
static constexpr u32 e_machine = EM_MIPS;
static constexpr u32 plt_hdr_size = 0;
static constexpr u32 page_size = 16384;
static constexpr u32 e_machine = EM_LOONGARCH;
static constexpr u32 plt_hdr_size = 32;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 0;
static constexpr u32 pltgot_size = 16;
static constexpr u32 thunk_hdr_size = 0;
static constexpr u32 thunk_size = 8;
static constexpr u8 filler[] = { 0x00, 0x00, 0x2a, 0x00 };
static constexpr u32 R_COPY = R_MIPS_COPY;
static constexpr u32 R_GLOB_DAT = R_MIPS_GLOB_DAT;
static constexpr u32 R_JUMP_SLOT = R_MIPS_JUMP_SLOT;
static constexpr u32 R_ABS = R_MIPS_64;
static constexpr u32 R_RELATIVE = R_MIPS_REL32;
static constexpr u32 R_DTPOFF = R_MIPS_TLS_DTPREL64;
static constexpr u32 R_TPOFF = R_MIPS_TLS_TPREL64;
static constexpr u32 R_DTPMOD = R_MIPS_TLS_DTPMOD64;
static constexpr u32 R_COPY = R_LARCH_COPY;
static constexpr u32 R_GLOB_DAT = R_LARCH_64;
static constexpr u32 R_JUMP_SLOT = R_LARCH_JUMP_SLOT;
static constexpr u32 R_ABS = R_LARCH_64;
static constexpr u32 R_RELATIVE = R_LARCH_RELATIVE;
static constexpr u32 R_IRELATIVE = R_LARCH_IRELATIVE;
static constexpr u32 R_DTPOFF = R_LARCH_TLS_DTPREL64;
static constexpr u32 R_TPOFF = R_LARCH_TLS_TPREL64;
static constexpr u32 R_DTPMOD = R_LARCH_TLS_DTPMOD64;
static constexpr u32 R_FUNCALL[] = { R_LARCH_B26 };
};
struct MIPS64BE {
static constexpr std::string_view target_name = "mips64";
static constexpr bool is_64 = true;
static constexpr bool is_le = false;
struct LOONGARCH32 {
static constexpr std::string_view target_name = "loongarch32";
static constexpr bool is_64 = false;
static constexpr bool is_le = true;
static constexpr bool is_rela = true;
static constexpr u32 page_size = 4096;
static constexpr u32 e_machine = EM_MIPS;
static constexpr u32 plt_hdr_size = 0;
static constexpr u32 page_size = 16384;
static constexpr u32 e_machine = EM_LOONGARCH;
static constexpr u32 plt_hdr_size = 32;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 0;
static constexpr u32 pltgot_size = 16;
static constexpr u32 thunk_hdr_size = 0;
static constexpr u32 thunk_size = 8;
static constexpr u8 filler[] = { 0x00, 0x00, 0x2a, 0x00 };
static constexpr u32 R_COPY = R_MIPS_COPY;
static constexpr u32 R_GLOB_DAT = R_MIPS_GLOB_DAT;
static constexpr u32 R_JUMP_SLOT = R_MIPS_JUMP_SLOT;
static constexpr u32 R_ABS = R_MIPS_64;
static constexpr u32 R_RELATIVE = R_MIPS_REL32;
static constexpr u32 R_DTPOFF = R_MIPS_TLS_DTPREL64;
static constexpr u32 R_TPOFF = R_MIPS_TLS_TPREL64;
static constexpr u32 R_DTPMOD = R_MIPS_TLS_DTPMOD64;
static constexpr u32 R_COPY = R_LARCH_COPY;
static constexpr u32 R_GLOB_DAT = R_LARCH_32;
static constexpr u32 R_JUMP_SLOT = R_LARCH_JUMP_SLOT;
static constexpr u32 R_ABS = R_LARCH_32;
static constexpr u32 R_RELATIVE = R_LARCH_RELATIVE;
static constexpr u32 R_IRELATIVE = R_LARCH_IRELATIVE;
static constexpr u32 R_DTPOFF = R_LARCH_TLS_DTPREL32;
static constexpr u32 R_TPOFF = R_LARCH_TLS_TPREL32;
static constexpr u32 R_DTPMOD = R_LARCH_TLS_DTPMOD32;
static constexpr u32 R_FUNCALL[] = { R_LARCH_B26 };
};
} // namespace mold::elf
+793
View File
@@ -0,0 +1,793 @@
// 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 {
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, (size_t)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);
tbb::parallel_for_each(cus, [&](Compunit &cu) {
cu.entries.reserve(cu.nametypes.size());
for (NameType &nt : cu.nametypes) {
MapValue *ent;
bool inserted;
std::tie(ent, inserted) = map.insert(nt.name, nt.hash, MapValue{nt.hash});
ent->count++;
cu.entries.push_back(ent);
}
});
// Sort symbols for build reproducibility
using Entry = typename decltype(map)::Entry;
std::vector<Entry *> entries = map.get_sorted_entries_all();
// 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(entries.size() * 5 / 4 + 1);
hdr.const_pool_offset = hdr.symtab_offset + ht_size * 8;
i64 offset = 0;
for (Entry *ent : entries) {
ent->value.type_offset = offset;
offset += ent->value.count * 4 + 4;
}
for (Entry *ent : entries) {
ent->value.name_offset = offset;
offset += ent->keylen + 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 (Entry *ent : entries) {
u32 hash = ent->value.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->value.name_offset;
ht[j * 2 + 1] = ent->value.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, [&](Entry *ent) {
memcpy(buf + hdr.const_pool_offset + ent->value.name_offset,
ent->key, ent->keylen);
});
// 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
+30 -28
View File
@@ -65,7 +65,7 @@
// conditions.
#include "mold.h"
#include "../common/sha.h"
#include "blake3.h"
#include <array>
#include <cstdio>
@@ -81,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];
}
@@ -115,26 +115,26 @@ 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_exec = (shdr.sh_flags & SHF_EXECINSTR) ||
ctx.arg.ignore_data_address_equality;
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);
bool is_addr_taken = !ctx.arg.icf_all && isec.address_significant;
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_exec && is_readonly && !is_bss && !is_empty &&
!is_init && !is_fini && !is_enumerable && !is_addr_taken;
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(SHA256Hash &sha) {
u8 buf[SHA256_SIZE];
sha.finish(buf);
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);
@@ -234,15 +234,16 @@ static void merge_leaf_nodes(Context<E> &ctx) {
template <typename E>
static Digest compute_digest(Context<E> &ctx, InputSection<E> &isec) {
SHA256Hash sha;
blake3_hasher hasher;
blake3_hasher_init(&hasher);
auto hash = [&](auto val) {
sha.update((u8 *)&val, sizeof(val));
blake3_hasher_update(&hasher, (u8 *)&val, sizeof(val));
};
auto hash_string = [&](std::string_view str) {
hash(str.size());
sha.update((u8 *)str.data(), str.size());
blake3_hasher_update(&hasher, (u8 *)str.data(), str.size());
};
auto hash_symbol = [&](Symbol<E> &sym) {
@@ -298,7 +299,7 @@ static Digest compute_digest(Context<E> &ctx, InputSection<E> &isec) {
hash_symbol(*isec.file.symbols[rel.r_sym]);
}
return digest_final(sha);
return digest_final(&hasher);
}
template <typename E>
@@ -411,16 +412,17 @@ static i64 propagate(std::span<std::vector<Digest>> digests,
if (converged.get(i))
return;
SHA256Hash sha;
sha.update(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))
sha.update(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]) {
// This node has converged. Skip further iterations as it will
@@ -563,7 +565,7 @@ void icf_sections(Context<E> &ctx) {
}
}
// Group sections by SHA digest.
// Group sections by BLAKE3 digest.
{
Timer t(ctx, "group");
+122 -129
View File
@@ -104,7 +104,7 @@ static bool is_debug_section(const ElfShdr<E> &shdr, std::string_view name) {
template <typename E>
void
ObjectFile<E>::read_note_gnu_property(Context<E> &ctx, const ElfShdr<E> &shdr) {
ObjectFile<E>::parse_note_gnu_property(Context<E> &ctx, const ElfShdr<E> &shdr) {
std::string_view data = this->get_string(ctx, shdr);
while (!data.empty()) {
@@ -140,24 +140,55 @@ ObjectFile<E>::read_note_gnu_property(Context<E> &ctx, const ElfShdr<E> &shdr) {
}
}
// <format-version>
// [ <section-length> "vendor-name" <file-tag> <size> <attribute>*]+ ]*
template <typename E>
static u64 read_mips_gp0(Context<E> &ctx, InputSection<E> &isec) {
std::string_view data = isec.contents;
static void read_riscv_attributes(Context<E> &ctx, ObjectFile<E> &file,
std::string_view data) {
if (data.empty())
Fatal(ctx) << file << ": corrupted .riscv.attributes section";
if (u8 format_version = data[0]; format_version != 'A')
return;
data = data.substr(1);
while (!data.empty()) {
if (data.size() < sizeof(MipsOptions<E>))
Fatal(ctx) << isec << ": corrupted .MIPS.options section";
i64 sz = *(U32<E> *)data.data();
if (data.size() < sz)
Fatal(ctx) << file << ": corrupted .riscv.attributes section";
MipsOptions<E> *opt = (MipsOptions<E> *)data.data();
if (opt->kind == ODK_REGINFO) {
if (data.size() < sizeof(MipsOptions<E>) + sizeof(MipsRegInfo<E>))
Fatal(ctx) << isec << ": corrupted .MIPS.options section";
MipsRegInfo<E> *info = (MipsRegInfo<E> *)(opt + 1);
return info->ri_gp_value;
std::string_view p(data.data() + 4, sz - 4);
data = data.substr(sz);
if (!p.starts_with("riscv\0"sv))
continue;
p = p.substr(6);
if (!p.starts_with(ELF_TAG_FILE))
Fatal(ctx) << file << ": corrupted .riscv.attributes section";
p = p.substr(5); // skip the tag and the sub-sub-section size
while (!p.empty()) {
i64 tag = read_uleb(&p);
switch (tag) {
case ELF_TAG_RISCV_STACK_ALIGN:
file.extra.stack_align = read_uleb(&p);
break;
case ELF_TAG_RISCV_ARCH: {
i64 pos = p.find_first_of('\0');
file.extra.arch = p.substr(0, pos);
p = p.substr(pos + 1);
break;
}
case ELF_TAG_RISCV_UNALIGNED_ACCESS:
file.extra.unaligned_access = read_uleb(&p);
break;
default:
break;
}
}
data = data.substr(opt->size);
}
return 0;
}
template <typename E>
@@ -170,6 +201,17 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
shdr.sh_type != SHT_LLVM_ADDRSIG && !ctx.arg.relocatable)
continue;
if constexpr (is_arm<E>)
if (shdr.sh_type == SHT_ARM_ATTRIBUTES)
continue;
if constexpr (is_riscv<E>) {
if (shdr.sh_type == SHT_RISCV_ATTRIBUTES) {
read_riscv_attributes(ctx, *this, this->get_string(ctx, shdr));
continue;
}
}
switch (shdr.sh_type) {
case SHT_GROUP: {
// Get the signature of this section group.
@@ -206,13 +248,12 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
comdat_groups.push_back({group, (u32)i, entries.subspan(1)});
break;
}
case SHT_REL:
case SHT_RELA:
case SHT_SYMTAB:
case SHT_SYMTAB_SHNDX:
case SHT_STRTAB:
case SHT_REL:
case SHT_RELA:
case SHT_NULL:
case SHT_ARM_ATTRIBUTES:
break;
default: {
std::string_view name = this->shstrtab.data() + shdr.sh_name;
@@ -221,11 +262,8 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
// area in GNU linkers. We ignore that section because silently
// making the stack area executable is too dangerous. Tell our
// users about the difference if that matters.
//
// MIPS object files don't contain .note.GNU-stack for some reason,
// so ignore this error on MIPS.
if (name == ".note.GNU-stack" && !ctx.arg.relocatable) {
if ((shdr.sh_flags & SHF_EXECINSTR) && !is_mips<E>) {
if (shdr.sh_flags & SHF_EXECINSTR) {
if (!ctx.arg.z_execstack && !ctx.arg.z_execstack_if_needed)
Warn(ctx) << *this << ": this file may cause a segmentation"
" fault because it requires an executable stack. See"
@@ -237,7 +275,7 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
}
if (name == ".note.gnu.property") {
read_note_gnu_property(ctx, shdr);
parse_note_gnu_property(ctx, shdr);
continue;
}
@@ -261,12 +299,6 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
is_debug_section(shdr, name))
continue;
// Save .llvm_addrsig for --icf=safe.
if (shdr.sh_type == SHT_LLVM_ADDRSIG && !ctx.arg.relocatable) {
llvm_addrsig = std::make_unique<InputSection<E>>(ctx, *this, name, i);
continue;
}
// If an output file doesn't have a section header (i.e.
// --oformat=binary is given), we discard all non-memory-allocated
// sections. This is because without a section header, we can't find
@@ -274,15 +306,20 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
if (ctx.arg.oformat_binary && !(shdr.sh_flags & SHF_ALLOC))
continue;
this->sections[i] = std::make_unique<InputSection<E>>(ctx, *this, name, i);
this->sections[i] = std::make_unique<InputSection<E>>(ctx, *this, i);
// Save .llvm_addrsig for --icf=safe.
if (shdr.sh_type == SHT_LLVM_ADDRSIG && !ctx.arg.relocatable) {
llvm_addrsig = std::move(this->sections[i]);
continue;
}
if (name == ".eh_frame")
eh_frame_section = this->sections[i].get();
if constexpr (is_ppc32<E>)
if (name == ".got2")
ppc32_got2 = this->sections[i].get();
if constexpr (is_mips<E>)
if (name == ".MIPS.options")
mips_gp0 = read_mips_gp0(ctx, *this->sections[i]);
extra.got2 = this->sections[i].get();
// Save debug sections for --gdb-index.
if (ctx.arg.gdb_index) {
@@ -290,10 +327,6 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
if (name == ".debug_info")
debug_info = isec;
if (name == ".debug_ranges")
debug_ranges = isec;
if (name == ".debug_rnglists")
debug_rnglists = isec;
// If --gdb-index is given, contents of .debug_gnu_pubnames and
// .debug_gnu_pubtypes are copied to .gdb_index, so keeping them
@@ -343,16 +376,6 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
}
}
template <typename E>
void ObjectFile<E>::initialize_ehframe_sections(Context<E> &ctx) {
for (i64 i = 0; i < sections.size(); i++) {
std::unique_ptr<InputSection<E>> &isec = sections[i];
if (isec && isec->is_alive && isec->name() == ".eh_frame") {
read_ehframe(ctx, *isec);
}
}
}
// .eh_frame contains data records explaining how to handle exceptions.
// When an exception is thrown, the runtime searches a record from
// .eh_frame with the current program counter as a key. A record that
@@ -383,7 +406,11 @@ void ObjectFile<E>::initialize_ehframe_sections(Context<E> &ctx) {
//
// This function parses an input .eh_frame section.
template <typename E>
void ObjectFile<E>::read_ehframe(Context<E> &ctx, InputSection<E> &isec) {
void ObjectFile<E>::parse_ehframe(Context<E> &ctx) {
if (!eh_frame_section)
return;
InputSection<E> &isec = *eh_frame_section;
std::span<ElfRel<E>> rels = isec.get_rels(ctx);
i64 cies_begin = cies.size();
i64 fdes_begin = fdes.size();
@@ -555,7 +582,7 @@ void ObjectFile<E>::initialize_symbols(Context<E> &ctx) {
// We expect them to be sorted, so sort them if necessary.
template <typename E>
void ObjectFile<E>::sort_relocations(Context<E> &ctx) {
if constexpr (is_riscv<E>) {
if constexpr (is_riscv<E> || is_loongarch<E>) {
auto less = [&](const ElfRel<E> &a, const ElfRel<E> &b) {
return a.r_offset < b.r_offset;
};
@@ -610,9 +637,16 @@ split_section(Context<E> &ctx, InputSection<E> &sec) {
if (!(shdr.sh_flags & SHF_MERGE))
return nullptr;
i64 entsize = shdr.sh_entsize;
if (entsize == 0)
entsize = (shdr.sh_flags & SHF_STRINGS) ? 1 : (int)shdr.sh_addralign;
if (entsize == 0)
return nullptr;
std::unique_ptr<MergeableSection<E>> rec(new MergeableSection<E>);
rec->parent = MergedSection<E>::get_instance(ctx, sec.name(), shdr.sh_type,
shdr.sh_flags);
shdr.sh_flags, entsize);
rec->p2align = sec.p2align;
if (sec.sh_size == 0)
@@ -623,18 +657,10 @@ split_section(Context<E> &ctx, InputSection<E> &sec) {
std::string_view data = sec.contents;
const char *begin = data.data();
u64 entsize = shdr.sh_entsize;
HyperLogLog estimator;
// Split sections
if (shdr.sh_flags & SHF_STRINGS) {
if (entsize == 0) {
// GHC (Glasgow Haskell Compiler) sometimes creates a mergeable
// string section with entsize of 0 instead of 1, though such
// entsize is technically wrong. This is a workaround for the issue.
entsize = 1;
}
while (!data.empty()) {
size_t end = find_null(data, entsize);
if (end == data.npos)
@@ -651,11 +677,6 @@ split_section(Context<E> &ctx, InputSection<E> &sec) {
estimator.insert(hash);
}
} else {
// OCaml compiler seems to create a mergeable non-string section with
// entisze of 0. Such section is malformed. We do not split such section.
if (entsize == 0)
return nullptr;
if (data.size() % entsize)
Fatal(ctx) << sec << ": section size is not multiple of sh_entsize";
@@ -687,13 +708,14 @@ split_section(Context<E> &ctx, InputSection<E> &sec) {
// atomic unit of mergeable section "section pieces".
//
// This feature is typically used for string literals. String literals
// are usually put into a mergeable section by a compiler. If the same
// string literal happen to occur in two different translation units,
// a linker merges them into a single instance of a string, so that
// a linker's output doesn't contain duplicate string literals.
// are usually put into a mergeable section by the compiler. If the same
// string literal happens to occur in two different translation units,
// the linker merges them into a single instance of a string, so that
// the linker's output doesn't contain duplicate string literals.
//
// Handling symbols in mergeable sections is a bit tricky. Assume that
// we have a mergeable section with the following contents and symbols:
// Handling symbols in the mergeable sections is a bit tricky. Assume
// that we have a mergeable section with the following contents and
// symbols:
//
// Hello world\0foo bar\0
// ^ ^
@@ -702,23 +724,23 @@ split_section(Context<E> &ctx, InputSection<E> &sec) {
//
// '\0' represents a NUL byte. This mergeable section contains two
// section pieces, "Hello world" and "foo bar". The first string is
// referred by two symbols, .rodata and .L.str0, and the second by
// referred to by two symbols, .rodata and .L.str0, and the second by
// .L.str1. .rodata is a section symbol and therefore a local symbol
// and refers the begining of the section.
// and refers to the beginning of the section.
//
// In this example, there are actually two different ways to point to
// string "foo bar", because .rodata+12 and .L.str1+0 refer the same
// string "foo bar", because .rodata+12 and .L.str1+0 refer to the same
// place in the section. This kind of "out-of-bound" reference occurs
// only when a symbol is a section symbol. In other words, compiler
// may use an offset from the beginning of a section to refer any
// only when a symbol is a section symbol. In other words, the compiler
// may use an offset from the beginning of a section to refer to any
// section piece in a section, but it doesn't do for any other types
// of symbols.
//
// In mold, we attach section pieces symbols. If a relocation refers a
// section symbol whose section is a mergeable section, we create a
// new dummy symbol with a section piece and redirect the relocation
// to the symbol. If a non-section symbol refers a section piece, the
// section piece is attached to the symbol.
// In mold, we attach symbols to section pieces. If a relocation refers
// to a section symbol, and that symbol's section is a mergeable one,
// we create a new dummy symbol for a section piece and redirect the
// relocation to this new symbol. If a non-section symbol refers to a
// section piece, the section piece is attached to the symbol.
template <typename E>
void ObjectFile<E>::initialize_mergeable_sections(Context<E> &ctx) {
mergeable_sections.resize(sections.size());
@@ -826,33 +848,6 @@ void ObjectFile<E>::resolve_section_pieces(Context<E> &ctx) {
this->symbols.push_back(&sym);
}
template <typename E>
void ObjectFile<E>::mark_addrsig(Context<E> &ctx) {
// Parse a .llvm_addrsig section.
if (llvm_addrsig) {
u8 *cur = (u8 *)llvm_addrsig->contents.data();
u8 *end = cur + llvm_addrsig->contents.size();
while (cur != end) {
Symbol<E> &sym = *this->symbols[read_uleb(cur)];
if (sym.file == this)
if (InputSection<E> *isec = sym.get_input_section())
isec->address_significant = true;
}
}
// We treat a symbol's address as significant if
//
// 1. we have no address significance information for the symbol, or
// 2. the symbol can be referenced from the outside in an address-
// significant manner.
for (Symbol<E> *sym : this->symbols)
if (sym->file == this)
if (InputSection<E> *isec = sym->get_input_section())
if (!llvm_addrsig || sym->is_exported)
isec->address_significant = true;
}
template <typename E>
void ObjectFile<E>::parse(Context<E> &ctx) {
sections.resize(this->elf_sections.size());
@@ -872,7 +867,7 @@ void ObjectFile<E>::parse(Context<E> &ctx) {
initialize_sections(ctx);
initialize_symbols(ctx);
sort_relocations(ctx);
initialize_ehframe_sections(ctx);
parse_ehframe(ctx);
}
// Symbols with higher priorities overwrites symbols with lower priorities.
@@ -1028,6 +1023,11 @@ void ObjectFile<E>::scan_relocations(Context<E> &ctx) {
for (ElfRel<E> &rel : cie.get_rels()) {
Symbol<E> &sym = *this->symbols[rel.r_sym];
if (ctx.arg.pic && rel.r_type == E::R_ABS)
Error(ctx) << *this << ": relocation " << rel << " in .eh_frame can"
<< " not be used when making a position-independent output;"
<< " recompile with -fPIE or -fPIC";
if (sym.is_imported) {
if (sym.get_type() != STT_FUNC)
Fatal(ctx) << *this << ": " << sym
@@ -1079,13 +1079,9 @@ void ObjectFile<E>::convert_common_symbols(Context<E> &ctx) {
ElfShdr<E> &shdr = elf_sections2.back();
memset(&shdr, 0, sizeof(shdr));
std::string_view name;
if (sym.get_type() == STT_TLS) {
name = ".tls_common";
shdr.sh_flags = SHF_ALLOC | SHF_WRITE | SHF_TLS;
} else {
name = ".common";
shdr.sh_flags = SHF_ALLOC | SHF_WRITE;
}
@@ -1095,7 +1091,7 @@ void ObjectFile<E>::convert_common_symbols(Context<E> &ctx) {
i64 idx = this->elf_sections.size() + elf_sections2.size() - 1;
std::unique_ptr<InputSection<E>> isec =
std::make_unique<InputSection<E>>(ctx, *this, name, idx);
std::make_unique<InputSection<E>>(ctx, *this, idx);
sym.file = this;
sym.set_input_section(isec.get());
@@ -1139,9 +1135,6 @@ void ObjectFile<E>::compute_symtab_size(Context<E> &ctx) {
this->output_sym_indices.resize(this->elf_syms.size(), -1);
auto is_alive = [&](Symbol<E> &sym) -> bool {
if (!ctx.arg.gc_sections)
return true;
if (SectionFragment<E> *frag = sym.get_frag())
return frag->is_alive;
if (InputSection<E> *isec = sym.get_input_section())
@@ -1187,30 +1180,29 @@ void ObjectFile<E>::populate_symtab(Context<E> &ctx) {
u8 *strtab_base = ctx.buf + ctx.strtab->shdr.sh_offset;
i64 strtab_off = this->strtab_offset;
auto write_sym = [&](Symbol<E> &sym, i64 &symtab_idx) {
auto write_sym = [&](Symbol<E> &sym, i64 idx) {
U32<E> *xindex = nullptr;
if (ctx.symtab_shndx)
xindex = (U32<E> *)(ctx.buf + ctx.symtab_shndx->shdr.sh_offset) + symtab_idx;
xindex = (U32<E> *)(ctx.buf + ctx.symtab_shndx->shdr.sh_offset) + idx;
symtab_base[symtab_idx++] = to_output_esym(ctx, sym, strtab_off, xindex);
symtab_base[idx] = to_output_esym(ctx, sym, strtab_off, xindex);
strtab_off += write_string(strtab_base + strtab_off, sym.name());
};
i64 local_symtab_idx = this->local_symtab_idx;
i64 global_symtab_idx = this->global_symtab_idx;
for (i64 i = 1; i < this->first_global; i++) {
Symbol<E> &sym = *this->symbols[i];
if (sym.write_to_symtab)
write_sym(sym, local_symtab_idx);
}
for (i64 i = 1; i < this->first_global; i++)
if (Symbol<E> &sym = *this->symbols[i]; sym.write_to_symtab)
write_sym(sym, local_symtab_idx++);
for (i64 i = this->first_global; i < this->elf_syms.size(); i++) {
Symbol<E> &sym = *this->symbols[i];
if (sym.file == this && sym.write_to_symtab) {
if (sym.is_local(ctx))
write_sym(sym, local_symtab_idx);
write_sym(sym, local_symtab_idx++);
else
write_sym(sym, global_symtab_idx);
write_sym(sym, global_symtab_idx++);
}
}
}
@@ -1241,7 +1233,6 @@ SharedFile<E>::create(Context<E> &ctx, MappedFile<Context<E>> *mf) {
template <typename E>
SharedFile<E>::SharedFile(Context<E> &ctx, MappedFile<Context<E>> *mf)
: InputFile<E>(ctx, mf) {
this->is_needed = ctx.as_needed;
this->is_alive = !ctx.as_needed;
}
@@ -1396,7 +1387,8 @@ SharedFile<E>::mark_live_objects(Context<E> &ctx,
if (sym.is_traced)
print_trace_symbol(ctx, *this, esym, sym);
if (esym.is_undef() && sym.file && !sym.file->is_alive.test_and_set()) {
if (esym.is_undef() && !esym.is_weak() && sym.file && !sym.file->is_dso &&
!sym.file->is_alive.test_and_set()) {
feeder(sym.file);
if (sym.is_traced)
@@ -1452,7 +1444,8 @@ bool SharedFile<E>::is_readonly(Symbol<E> *sym) {
u64 val = sym->esym().st_value;
for (ElfPhdr<E> &phdr : this->get_phdrs())
if (phdr.p_type == PT_LOAD && !(phdr.p_flags & PF_W) &&
if ((phdr.p_type == PT_LOAD || phdr.p_type == PT_GNU_RELRO) &&
!(phdr.p_flags & PF_W) &&
phdr.p_vaddr <= val && val < phdr.p_vaddr + phdr.p_memsz)
return true;
return false;
+108 -50
View File
@@ -7,7 +7,8 @@
namespace mold::elf {
typedef enum {
NONE, ERROR, COPYREL, DYN_COPYREL, PLT, CPLT, DYN_CPLT, DYNREL, BASEREL, IFUNC,
NONE, ERROR, COPYREL, DYN_COPYREL, PLT, CPLT, DYN_CPLT, DYNREL,
BASEREL, IFUNC_DYNREL,
} Action;
template <typename E>
@@ -37,8 +38,7 @@ static i64 to_p2align(u64 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, (size_t)shdr().sh_size};
@@ -107,30 +107,6 @@ void InputSection<E>::uncompress_to(Context<E> &ctx, u8 *buf) {
}
}
template <typename E>
static Action get_rel_action(Context<E> &ctx, Symbol<E> &sym,
const Action table[3][4]) {
auto get_output_type = [&] {
if (ctx.arg.shared)
return 0;
if (ctx.arg.pie)
return 1;
return 2;
};
auto get_sym_type = [&] {
if (sym.is_absolute())
return 0;
if (!sym.is_imported)
return 1;
if (sym.get_type() != STT_FUNC)
return 2;
return 3;
};
return table[get_output_type()][get_sym_type()];
}
template <typename E>
static void scan_rel(Context<E> &ctx, InputSection<E> &isec, Symbol<E> &sym,
const ElfRel<E> &rel, Action action) {
@@ -174,40 +150,67 @@ static void scan_rel(Context<E> &ctx, InputSection<E> &isec, Symbol<E> &sym,
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:
case IFUNC_DYNREL:
// Create an IRELATIVE relocation for a GNU ifunc symbol.
//
// We usually create an IRELATIVE relocation in .got for each ifunc.
// However, if a statically-initialized pointer is initialized to an
// ifunc's address, we have no choice other than emitting an IRELATIVE
// relocation for each such pointer.
dynrel();
ctx.num_ifunc_dynrels++;
break;
@@ -216,73 +219,93 @@ static void scan_rel(Context<E> &ctx, InputSection<E> &isec, Symbol<E> &sym,
}
}
template <typename E>
static inline i64 get_output_type(Context<E> &ctx) {
if (ctx.arg.shared)
return 0;
if (ctx.arg.pie)
return 1;
return 2;
}
template <typename E>
static inline i64 get_sym_type(Symbol<E> &sym) {
if (sym.is_absolute())
return 0;
if (!sym.is_imported)
return 1;
if (sym.get_type() != STT_FUNC)
return 2;
return 3;
}
template <typename E>
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.
constexpr static Action table[3][4] = {
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
};
return get_rel_action(ctx, sym, table);
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 word size (e.g. R_X86_64_32). Since the dynamic linker
// than the pointer size (e.g. R_X86_64_32). Since the dynamic linker
// generally does not support dynamic relocations smaller than the
// word size, we need to report an error if a relocation cannot be
// pointer size, we need to report an error if a relocation cannot be
// resolved at link-time.
constexpr static Action table[3][4] = {
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 get_rel_action(ctx, sym, table);
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;
return ctx.arg.pic ? IFUNC_DYNREL : NONE;
// This is a decision table for absolute relocations for the word
// 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.
constexpr static Action table[3][4] = {
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 get_rel_action(ctx, sym, table);
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;
return IFUNC_DYNREL;
// As a special case, we do not create copy relocations nor canonical
// PLTs for .toc sections. PPC64's .toc is a compiler-generated
// GOT-like section, and no user-generated code directly uses values
// in it.
constexpr static Action table[3][4] = {
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 get_rel_action(ctx, sym, table);
return table[get_output_type(ctx)][get_sym_type(sym)];
}
template <typename E>
@@ -309,6 +332,29 @@ void InputSection<E>::scan_toc_rel(Context<E> &ctx, Symbol<E> &sym,
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) {
@@ -325,7 +371,7 @@ static void apply_absrel(Context<E> &ctx, InputSection<E> &isec,
Action action) {
bool writable = (isec.shdr().sh_flags & SHF_WRITE);
auto apply_dynrel = [&] {
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;
@@ -348,20 +394,20 @@ static void apply_absrel(Context<E> &ctx, InputSection<E> &isec,
break;
case DYN_COPYREL:
if (writable || !ctx.arg.z_copyreloc)
apply_dynrel();
emit_abs_dynrel();
else
*(Word<E> *)loc = S + A;
break;
case DYN_CPLT:
if (writable)
apply_dynrel();
emit_abs_dynrel();
else
*(Word<E> *)loc = S + A;
break;
case DYNREL:
apply_dynrel();
emit_abs_dynrel();
break;
case IFUNC:
case IFUNC_DYNREL:
if constexpr (supports_ifunc<E>) {
u64 addr = sym.get_addr(ctx, NO_PLT) + A;
*dynrel++ = ElfRel<E>(P, E::R_IRELATIVE, 0, addr);
@@ -380,8 +426,8 @@ 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,
ElfRel<E> **dynrel) {
apply_absrel(ctx, *this, sym, rel, loc, S, A, P, *dynrel,
get_dyn_absrel_action(ctx, sym));
}
@@ -389,8 +435,8 @@ 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,
ElfRel<E> **dynrel) {
apply_absrel(ctx, *this, sym, rel, loc, S, A, P, *dynrel,
get_ppc64_toc_action(ctx, sym));
}
@@ -411,6 +457,18 @@ void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
apply_reloc_alloc(ctx, buf);
else
apply_reloc_nonalloc(ctx, buf);
if constexpr (is_x86_64<E>) {
u8 endbr[] = {0xf3, 0x0f, 0x1e, 0xfa};
u8 nop[] = {0x0f, 0x1f, 0x40, 0x00};
// Rewrite the leading endbr instruction with a nop if the section
// is not address-taken.
if (ctx.arg.z_rewrite_endbr && (shdr().sh_flags & SHF_EXECINSTR) &&
!address_taken && sh_size >= 4 && memcmp(buf, endbr, 4) == 0) {
memcpy(buf, nop, 4);
}
}
}
}
+17 -5
View File
@@ -1,3 +1,14 @@
// Many build systems attempt to invoke as many linker processes as there
// are cores, based on the assumption that the linker is single-threaded.
// However, since mold is multi-threaded, such build systems' behavior is
// not beneficial and just increases the overall peak memory usage.
// On machines with limited memory, this could lead to an out-of-memory
// error.
//
// This file implements a feature that limits the number of concurrent
// mold processes to just 1 for each user. It is intended to be used as
// `MOLD_JOBS=1 ninja` or `MOLD_JOBS=1 make -j$(nproc)`.
#include "mold.h"
#ifndef _WIN32
@@ -14,14 +25,15 @@ template <typename E>
void acquire_global_lock(Context<E> &ctx) {
#ifndef _WIN32
char *jobs = getenv("MOLD_JOBS");
if (!jobs || std::string(jobs) != "1")
if (!jobs || jobs != "1"s)
return;
char *home = getenv("HOME");
if (!home)
home = getpwuid(getuid())->pw_dir;
std::string path;
if (char *dir = getenv("XDG_RUNTIME_DIR"))
path = dir + "/mold-lock"s;
else
path = "/tmp/mold-lock-"s + getpwuid(getuid())->pw_name;
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;
+5 -6
View File
@@ -279,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,
std::string_view ver_str, 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;
@@ -299,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_str, 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_str, ver_idx, true);
read_version_script_commands(ctx, tok, ver_str, ver_idx, is_global, true);
}
tok = skip(ctx, tok, "}");
@@ -349,7 +348,7 @@ void read_version_script(Context<E> &ctx, std::span<std::string_view> &tok) {
}
tok = skip(ctx, tok, "{");
read_version_script_commands(ctx, tok, ver_str, 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);
+3
View File
@@ -709,6 +709,9 @@ std::vector<ObjectFile<E> *> do_lto(Context<E> &ctx) {
get_symbol(ctx, y)->referenced_by_regular_obj = true;
}
// Handle --entry
get_symbol(ctx, ctx.arg.entry)->referenced_by_regular_obj = true;
// 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)
+1 -3
View File
@@ -55,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 {
+16 -90
View File
@@ -70,10 +70,8 @@ std::string_view get_machine_type(Context<E> &ctx, MappedFile<Context<E>> *mf) {
return SH4::target_name;
case EM_ALPHA:
return ALPHA::target_name;
case EM_MIPS:
if (is_64)
return is_le ? MIPS64LE::target_name : MIPS64BE::target_name;
return "";
case EM_LOONGARCH:
return is_64 ? LOONGARCH64::target_name : LOONGARCH32::target_name;
default:
return "";
}
@@ -193,7 +191,8 @@ void read_file(Context<E> &ctx, MappedFile<Context<E>> *mf) {
break;
}
}
ctx.visited.insert(mf->name);
if (!ctx.whole_archive)
ctx.visited.insert(mf->name);
return;
case FileType::TEXT:
parse_linker_script(ctx, mf);
@@ -340,48 +339,6 @@ static void read_input_files(Context<E> &ctx, std::span<std::string> args) {
ctx.tg.wait();
}
// Since elf_main is a template, we can't run it without a type parameter.
// We speculatively run elf_main with X86_64, and if the speculation was
// wrong, re-run it with an actual machine type.
template <typename E>
static int redo_main(int argc, char **argv, std::string_view target) {
if (target == I386::target_name)
return elf_main<I386>(argc, argv);
if (target == ARM64::target_name)
return elf_main<ARM64>(argc, argv);
if (target == ARM32::target_name)
return elf_main<ARM32>(argc, argv);
if (target == RV64LE::target_name)
return elf_main<RV64LE>(argc, argv);
if (target == RV64BE::target_name)
return elf_main<RV64BE>(argc, argv);
if (target == RV32LE::target_name)
return elf_main<RV32LE>(argc, argv);
if (target == RV32BE::target_name)
return elf_main<RV32BE>(argc, argv);
if (target == PPC32::target_name)
return elf_main<PPC32>(argc, argv);
if (target == PPC64V1::target_name)
return elf_main<PPC64V1>(argc, argv);
if (target == PPC64V2::target_name)
return elf_main<PPC64V2>(argc, argv);
if (target == S390X::target_name)
return elf_main<S390X>(argc, argv);
if (target == SPARC64::target_name)
return elf_main<SPARC64>(argc, argv);
if (target == M68K::target_name)
return elf_main<M68K>(argc, argv);
if (target == SH4::target_name)
return elf_main<SH4>(argc, argv);
if (target == ALPHA::target_name)
return elf_main<ALPHA>(argc, argv);
if (target == MIPS64LE::target_name)
return elf_main<MIPS64LE>(argc, argv);
if (target == MIPS64BE::target_name)
return elf_main<MIPS64BE>(argc, argv);
unreachable();
}
template <typename E>
int elf_main(int argc, char **argv) {
Context<E> ctx;
@@ -405,7 +362,7 @@ int elf_main(int argc, char **argv) {
// Redo if -m is not x86-64.
if constexpr (is_x86_64<E>)
if (ctx.arg.emulation != X86_64::target_name)
return redo_main<E>(argc, argv, ctx.arg.emulation);
return redo_main(ctx, argc, argv);
Timer t_all(ctx, "all");
@@ -452,7 +409,6 @@ int elf_main(int argc, char **argv) {
});
}
Timer t_total(ctx, "total");
Timer t_before_copy(ctx, "before_copy");
// Apply -exclude-libs
@@ -500,9 +456,9 @@ int elf_main(int argc, char **argv) {
// Set is_imported and is_exported bits for each symbol.
compute_import_export(ctx);
// Read address-significant section information.
if (ctx.arg.icf && !ctx.arg.icf_all)
mark_addrsig(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)
@@ -641,10 +597,6 @@ 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)
@@ -690,12 +642,6 @@ int elf_main(int argc, char **argv) {
// Copy input sections to the output file and apply relocations.
copy_chunks(ctx);
// Some part of .gdb_index couldn't be computed until other debug
// sections are complete. We have complete debug sections now, so
// write the rest of .gdb_index.
if (ctx.gdb_index)
ctx.gdb_index->write_address_areas(ctx);
// Dynamic linker works better with sorted .rela.dyn section,
// so we sort them.
ctx.reldyn->sort(ctx);
@@ -703,6 +649,12 @@ int elf_main(int argc, char **argv) {
// Zero-clear paddings between sections
clear_padding(ctx);
// .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.
@@ -722,7 +674,6 @@ int elf_main(int argc, char **argv) {
if (ctx.has_lto_object)
lto_cleanup(ctx);
t_total.stop();
t_all.stop();
if (ctx.arg.print_map)
@@ -751,39 +702,14 @@ int elf_main(int argc, char **argv) {
return 0;
}
using E = MOLD_TARGET;
template void read_file(Context<E> &, MappedFile<Context<E>> *);
template MappedFile<Context<E>> *open_library(Context<E> &, std::string);
#ifdef MOLD_X86_64
extern template int elf_main<I386>(int, char **);
extern template int elf_main<ARM32>(int, char **);
extern template int elf_main<ARM64>(int, char **);
extern template int elf_main<RV32BE>(int, char **);
extern template int elf_main<RV32LE>(int, char **);
extern template int elf_main<RV64LE>(int, char **);
extern template int elf_main<RV64BE>(int, char **);
extern template int elf_main<PPC32>(int, char **);
extern template int elf_main<PPC64V1>(int, char **);
extern template int elf_main<PPC64V2>(int, char **);
extern template int elf_main<S390X>(int, char **);
extern template int elf_main<SPARC64>(int, char **);
extern template int elf_main<M68K>(int, char **);
extern template int elf_main<SH4>(int, char **);
extern template int elf_main<ALPHA>(int, char **);
extern template int elf_main<MIPS64LE>(int, char **);
extern template int elf_main<MIPS64BE>(int, char **);
int main(int argc, char **argv) {
return elf_main<X86_64>(argc, argv);
}
#endif
#else
using E = MOLD_TARGET;
template int elf_main<E>(int, char **);
#endif
} // namespace mold::elf
+198 -229
View File
@@ -36,8 +36,6 @@
namespace mold::elf {
static constexpr i32 SHA256_SIZE = 32;
template <typename E> class InputFile;
template <typename E> class InputSection;
template <typename E> class MergedSection;
@@ -99,11 +97,11 @@ struct SymbolAux<PPC64V1> : SymbolAux<X86_64> {
template <typename E>
class RangeExtensionThunk {};
template <typename E> requires needs_thunk<E>
template <needs_thunk E>
class RangeExtensionThunk<E> {
public:
RangeExtensionThunk(OutputSection<E> &osec, i64 thunk_idx, i64 offset)
: output_section(osec), thunk_idx(thunk_idx), offset(offset) {}
RangeExtensionThunk(OutputSection<E> &osec, i64 offset)
: output_section(osec), offset(offset) {}
i64 size() const { return E::thunk_hdr_size + symbols.size() * E::thunk_size; }
void copy_buf(Context<E> &ctx);
@@ -113,10 +111,9 @@ public:
idx * E::thunk_size;
}
static constexpr i64 alignment = 4;
static constexpr i64 alignment = 16;
OutputSection<E> &output_section;
i64 thunk_idx;
i64 offset;
std::mutex mu;
std::vector<Symbol<E> *> symbols;
@@ -127,9 +124,6 @@ struct RangeExtensionRef {
i16 sym_idx = -1;
};
template <typename E>
void create_range_extension_thunks(Context<E> &ctx, OutputSection<E> &osec);
//
// input-sections.cc
//
@@ -216,12 +210,12 @@ struct FdeRecord {
template <typename E>
struct InputSectionExtras {};
template <typename E> requires needs_thunk<E>
template <needs_thunk E>
struct InputSectionExtras<E> {
std::vector<RangeExtensionRef> range_extn;
};
template <typename E> requires is_riscv<E>
template <is_riscv E>
struct InputSectionExtras<E> {
std::vector<i32> r_deltas;
};
@@ -230,8 +224,7 @@ struct InputSectionExtras<E> {
template <typename E>
class InputSection {
public:
InputSection(Context<E> &ctx, ObjectFile<E> &file, std::string_view name,
i64 shndx);
InputSection(Context<E> &ctx, ObjectFile<E> &file, i64 shndx);
void uncompress(Context<E> &ctx);
void uncompress_to(Context<E> &ctx, u8 *buf);
@@ -264,17 +257,19 @@ public:
i32 fde_begin = -1;
i32 fde_end = -1;
u32 offset = -1;
u64 offset = -1;
u32 shndx = -1;
u32 relsec_idx = -1;
u32 reldyn_offset = 0;
bool uncompressed = false;
// For COMDAT de-duplication and garbage collection
std::atomic_bool is_alive = true;
u8 p2align = 0;
bool address_significant : 1 = false;
bool uncompressed : 1 = false;
// For ICF
Atomic<bool> address_taken = false;
// For garbage collection
Atomic<bool> is_visited = false;
@@ -296,14 +291,14 @@ private:
void scan_absrel(Context<E> &ctx, Symbol<E> &sym, const ElfRel<E> &rel);
void scan_dyn_absrel(Context<E> &ctx, Symbol<E> &sym, const ElfRel<E> &rel);
void scan_toc_rel(Context<E> &ctx, Symbol<E> &sym, const ElfRel<E> &rel);
void scan_tlsdesc(Context<E> &ctx, Symbol<E> &sym);
void check_tlsle(Context<E> &ctx, Symbol<E> &sym, const ElfRel<E> &rel);
void apply_dyn_absrel(Context<E> &ctx, Symbol<E> &sym, const ElfRel<E> &rel,
u8 *loc, u64 S, i64 A, u64 P, ElfRel<E> *&dynrel);
u8 *loc, u64 S, i64 A, u64 P, ElfRel<E> **dynrel);
void apply_toc_rel(Context<E> &ctx, Symbol<E> &sym, const ElfRel<E> &rel,
u8 *loc, u64 S, i64 A, u64 P, ElfRel<E> *&dynrel);
u8 *loc, u64 S, i64 A, u64 P, ElfRel<E> **dynrel);
void copy_contents_riscv(Context<E> &ctx, u8 *buf);
@@ -319,9 +314,9 @@ private:
// tls.cc
//
template<typename E> u64 get_tls_begin(Context<E> &);
template<typename E> u64 get_tp_addr(Context<E> &);
template<typename E> u64 get_dtp_addr(Context<E> &);
template <typename E> u64 get_tls_begin(Context<E> &);
template <typename E> u64 get_tp_addr(Context<E> &);
template <typename E> u64 get_dtp_addr(Context<E> &);
//
// output-chunks.cc
@@ -333,9 +328,6 @@ u64 get_eflags(Context<E> &ctx);
template <typename E>
i64 to_phdr_flags(Context<E> &ctx, Chunk<E> *chunk);
template <typename E>
bool is_relro(Context<E> &ctx, Chunk<E> *chunk);
template <typename E>
std::string_view get_output_name(Context<E> &ctx, std::string_view name, u64 flags);
@@ -358,16 +350,19 @@ public:
virtual ChunkKind kind() { return SYNTHETIC; }
virtual OutputSection<E> *to_osec() { return nullptr; }
virtual i64 get_reldyn_size(Context<E> &ctx) const { return 0; }
virtual void construct_relr(Context<E> &ctx) {}
virtual void copy_buf(Context<E> &ctx) {}
virtual void write_to(Context<E> &ctx, u8 *buf) { unreachable(); }
virtual void update_shdr(Context<E> &ctx) {}
// For --gdb-index
virtual u8 *get_uncompressed_data() { return nullptr; }
std::string_view name;
ElfShdr<E> shdr = { .sh_addralign = 1 };
i64 shndx = 0;
bool is_relro = false;
// For --gdb-index
bool is_compressed = false;
std::vector<u8> uncompressed_data;
// Some synethetic sections add local symbols to the output.
// For example, range extension thunks adds function_name@thunk
@@ -386,6 +381,9 @@ public:
// For --section-order
i64 sect_order = 0;
// For --pack-dyn-relocs=relr
std::vector<u64> relr;
};
// ELF header
@@ -451,25 +449,19 @@ public:
template <typename E>
class OutputSection : public Chunk<E> {
public:
OutputSection(std::string_view name, u32 type, u64 flags) {
this->name = name;
this->shdr.sh_type = type;
this->shdr.sh_flags = flags;
}
OutputSection(Context<E> &ctx, std::string_view name, u32 type, u64 flags);
ChunkKind kind() override { return OUTPUT_SECTION; }
OutputSection<E> *to_osec() override { return this; }
void construct_relr(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
void write_to(Context<E> &ctx, u8 *buf) override;
void compute_symtab_size(Context<E> &ctx) override;
void populate_symtab(Context<E> &ctx) override;
void create_range_extension_thunks(Context<E> &ctx);
std::vector<InputSection<E> *> members;
void construct_relr(Context<E> &ctx);
std::vector<u64> relr;
std::vector<std::unique_ptr<RangeExtensionThunk<E>>> thunks;
std::unique_ptr<RelocSection<E>> reloc_sec;
};
@@ -479,15 +471,15 @@ class GotSection : public Chunk<E> {
public:
GotSection() {
this->name = ".got";
this->is_relro = true;
this->shdr.sh_type = SHT_PROGBITS;
this->shdr.sh_flags = SHF_ALLOC | SHF_WRITE;
this->shdr.sh_addralign = sizeof(Word<E>);
// We always create a .got so that _GLOBAL_OFFSET_TABLE_ has
// something to point to. s390x/MIPS psABIs define GOT[1] as a
// something to point to. s390x psABI define GOT[1] as a
// reserved slot, so we allocate one more for them.
i64 n = (is_s390x<E> || is_mips<E>) ? 2 : 1;
this->shdr.sh_size = n * sizeof(Word<E>);
this->shdr.sh_size = (is_s390x<E> ? 2 : 1) * sizeof(Word<E>);
}
void add_got_symbol(Context<E> &ctx, Symbol<E> *sym);
@@ -501,6 +493,7 @@ public:
i64 get_reldyn_size(Context<E> &ctx) const override;
void copy_buf(Context<E> &ctx) override;
void construct_relr(Context<E> &ctx) override;
void compute_symtab_size(Context<E> &ctx) override;
void populate_symtab(Context<E> &ctx) override;
@@ -509,16 +502,14 @@ public:
std::vector<Symbol<E> *> tlsdesc_syms;
std::vector<Symbol<E> *> gottp_syms;
u32 tlsld_idx = -1;
void construct_relr(Context<E> &ctx);
std::vector<u64> relr;
};
template <typename E>
class GotPltSection : public Chunk<E> {
public:
GotPltSection() {
GotPltSection(Context<E> &ctx) {
this->name = ".got.plt";
this->is_relro = ctx.arg.z_now;
this->shdr.sh_type = is_ppc64<E> ? SHT_NOBITS : SHT_PROGBITS;
this->shdr.sh_flags = SHF_ALLOC | SHF_WRITE;
this->shdr.sh_addralign = sizeof(Word<E>);
@@ -631,6 +622,12 @@ public:
}
void update_shdr(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
// Offsets in .strtab for ARM32 mapping symbols
static constexpr i64 ARM = 1;
static constexpr i64 THUMB = 4;
static constexpr i64 DATA = 7;
};
template <typename E>
@@ -654,7 +651,6 @@ public:
this->shdr.sh_flags = SHF_ALLOC;
}
void keep() { this->shdr.sh_size = 1; }
i64 add_string(std::string_view str);
i64 find_string(std::string_view str);
void copy_buf(Context<E> &ctx) override;
@@ -670,6 +666,7 @@ class DynamicSection : public Chunk<E> {
public:
DynamicSection() {
this->name = ".dynamic";
this->is_relro = true;
this->shdr.sh_type = SHT_DYNAMIC;
this->shdr.sh_flags = SHF_ALLOC | SHF_WRITE;
this->shdr.sh_addralign = sizeof(Word<E>);
@@ -680,7 +677,7 @@ public:
void copy_buf(Context<E> &ctx) override;
};
template<typename E>
template <typename E>
ElfSym<E> to_output_esym(Context<E> &ctx, Symbol<E> &sym, u32 st_name,
U32<E> *shndx);
@@ -720,13 +717,13 @@ public:
this->shdr.sh_addralign = sizeof(Word<E>);
}
void keep() { this->symbols.resize(1); }
void add_symbol(Context<E> &ctx, Symbol<E> *sym);
void finalize(Context<E> &ctx);
void update_shdr(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
std::vector<Symbol<E> *> symbols;
bool finalized = false;
};
template <typename E>
@@ -770,7 +767,8 @@ template <typename E>
class MergedSection : public Chunk<E> {
public:
static MergedSection<E> *
get_instance(Context<E> &ctx, std::string_view name, u64 type, u64 flags);
get_instance(Context<E> &ctx, std::string_view name, i64 type, i64 flags,
i64 entsize);
SectionFragment<E> *insert(Context<E> &ctx, std::string_view data,
u64 hash, i64 p2align);
@@ -783,7 +781,7 @@ public:
HyperLogLog estimator;
private:
MergedSection(std::string_view name, u64 flags, u32 type);
MergedSection(std::string_view name, i64 flags, i64 type, i64 entsize);
ConcurrentMap<SectionFragment<E>> map;
std::vector<i64> shard_offsets;
@@ -801,7 +799,7 @@ public:
}
void construct(Context<E> &ctx);
void apply_reloc(Context<E> &ctx, const ElfRel<E> &rel, u64 offset, u64 val);
void apply_eh_reloc(Context<E> &ctx, const ElfRel<E> &rel, u64 offset, u64 val);
void copy_buf(Context<E> &ctx) override;
};
@@ -842,8 +840,9 @@ public:
template <typename E>
class CopyrelSection : public Chunk<E> {
public:
CopyrelSection(bool is_relro) : is_relro(is_relro) {
CopyrelSection(bool is_relro) {
this->name = is_relro ? ".copyrel.rel.ro" : ".copyrel";
this->is_relro = is_relro;
this->shdr.sh_type = SHT_NOBITS;
this->shdr.sh_flags = SHF_ALLOC | SHF_WRITE;
}
@@ -853,7 +852,6 @@ public:
i64 get_reldyn_size(Context<E> &ctx) const override { return symbols.size(); }
void copy_buf(Context<E> &ctx) override;
bool is_relro;
std::vector<Symbol<E> *> symbols;
};
@@ -959,13 +957,6 @@ private:
std::map<u32, u32> properties;
};
struct GdbIndexName {
std::string_view name;
u32 hash = 0;
u32 attr = 0;
u32 entry_idx = 0;
};
template <typename E>
class GdbIndexSection : public Chunk<E> {
public:
@@ -974,38 +965,6 @@ public:
this->shdr.sh_type = SHT_PROGBITS;
this->shdr.sh_addralign = 4;
}
void construct(Context<E> &ctx);
void copy_buf(Context<E> &ctx) override;
void write_address_areas(Context<E> &ctx);
private:
struct SectionHeader {
ul32 version = 7;
ul32 cu_list_offset = 0;
ul32 cu_types_offset = 0;
ul32 areas_offset = 0;
ul32 symtab_offset = 0;
ul32 const_pool_offset = 0;
};
struct MapEntry {
MapEntry(ObjectFile<E> *owner, u32 hash) : owner(owner), hash(hash) {}
MapEntry(const MapEntry &other)
: owner(other.owner.load()), num_attrs(other.num_attrs.load()),
hash(other.hash), name_offset(other.name_offset),
attr_offset(other.attr_offset) {}
std::atomic<ObjectFile<E> *> owner;
std::atomic_uint32_t num_attrs = 0;
u32 hash = 0;
u32 name_offset = -1;
u32 attr_offset = -1;
};
SectionHeader header;
ConcurrentMap<MapEntry> map;
};
template <typename E>
@@ -1013,12 +972,10 @@ class CompressedSection : public Chunk<E> {
public:
CompressedSection(Context<E> &ctx, Chunk<E> &chunk);
void copy_buf(Context<E> &ctx) override;
u8 *get_uncompressed_data() override { return uncompressed.get(); }
private:
ElfChdr<E> chdr = {};
std::unique_ptr<Compressor> compressed;
std::unique_ptr<u8[]> uncompressed;
};
template <typename E>
@@ -1041,6 +998,7 @@ class RelroPaddingSection : public Chunk<E> {
public:
RelroPaddingSection() {
this->name = ".relro_padding";
this->is_relro = true;
this->shdr.sh_type = SHT_NOBITS;
this->shdr.sh_flags = SHF_ALLOC | SHF_WRITE;
this->shdr.sh_addralign = 1;
@@ -1069,22 +1027,10 @@ private:
};
//
// dwarf.cc
// gdb-index.cc
//
template <typename E>
std::vector<std::string_view>
read_compunits(Context<E> &ctx, ObjectFile<E> &file);
template <typename E>
std::vector<GdbIndexName> read_pubnames(Context<E> &ctx, ObjectFile<E> &file);
template <typename E>
i64 estimate_address_areas(Context<E> &ctx, ObjectFile<E> &file);
template <typename E>
std::vector<u64>
read_address_areas(Context<E> &ctx, ObjectFile<E> &file, i64 offset);
template <typename E> void write_gdb_index(Context<E> &ctx);
//
// input-files.cc
@@ -1137,11 +1083,11 @@ public:
virtual ~InputFile() = default;
template<typename T> std::span<T>
get_data(Context<E> &ctx, const ElfShdr<E> &shdr);
template <typename T>
std::span<T> get_data(Context<E> &ctx, const ElfShdr<E> &shdr);
template<typename T> std::span<T>
get_data(Context<E> &ctx, i64 idx);
template <typename T>
std::span<T> get_data(Context<E> &ctx, i64 idx);
std::string_view get_string(Context<E> &ctx, const ElfShdr<E> &shdr);
std::string_view get_string(Context<E> &ctx, i64 idx);
@@ -1190,6 +1136,20 @@ protected:
std::vector<Symbol<E>> frag_syms;
};
template <typename E> struct ObjectFileExtras {};
template <is_riscv E>
struct ObjectFileExtras<E> {
std::optional<i64> stack_align;
std::optional<std::string_view> arch;
bool unaligned_access = false;
};
template <>
struct ObjectFileExtras<PPC32> {
InputSection<PPC32> *got2 = nullptr;
};
// ObjectFile represents an input .o file.
template <typename E>
class ObjectFile : public InputFile<E> {
@@ -1206,7 +1166,6 @@ public:
void mark_live_objects(Context<E> &ctx,
std::function<void(InputFile<E> *)> feeder) override;
void convert_undefined_weak_symbols(Context<E> &ctx);
void mark_addrsig(Context<E> &ctx);
void scan_relocations(Context<E> &ctx);
void convert_common_symbols(Context<E> &ctx);
void compute_symtab_size(Context<E> &ctx);
@@ -1224,6 +1183,7 @@ public:
std::vector<FdeRecord<E>> fdes;
BitVector has_symver;
std::vector<ComdatGroupRef<E>> comdat_groups;
InputSection<E> *eh_frame_section = nullptr;
bool exclude_libs = false;
std::map<u32, u32> gnu_properties;
bool is_lto_obj = false;
@@ -1241,28 +1201,14 @@ public:
// For .gdb_index
InputSection<E> *debug_info = nullptr;
InputSection<E> *debug_ranges = nullptr;
InputSection<E> *debug_rnglists = nullptr;
InputSection<E> *debug_pubnames = nullptr;
InputSection<E> *debug_pubtypes = nullptr;
std::vector<std::string_view> compunits;
std::vector<GdbIndexName> gdb_names;
i64 compunits_idx = 0;
i64 attrs_size = 0;
i64 attrs_offset = 0;
i64 names_size = 0;
i64 names_offset = 0;
i64 num_areas = 0;
i64 area_offset = 0;
// For LTO
std::vector<std::string_view> lto_symbol_versions;
// For PPC32
InputSection<E> *ppc32_got2 = nullptr;
// For MIPS
u64 mips_gp0 = 0;
// Target-specific member
[[no_unique_address]] ObjectFileExtras<E> extra;
private:
ObjectFile(Context<E> &ctx, MappedFile<Context<E>> *mf,
@@ -1272,8 +1218,8 @@ private:
void initialize_symbols(Context<E> &ctx);
void sort_relocations(Context<E> &ctx);
void initialize_ehframe_sections(Context<E> &ctx);
void read_note_gnu_property(Context <E> &ctx, const ElfShdr <E> &shdr);
void read_ehframe(Context<E> &ctx, InputSection<E> &isec);
void parse_note_gnu_property(Context <E> &ctx, const ElfShdr <E> &shdr);
void parse_ehframe(Context<E> &ctx);
void override_symbol(Context<E> &ctx, Symbol<E> &sym,
const ElfSym<E> &esym, i64 symidx);
void merge_visibility(Context<E> &ctx, Symbol<E> &sym, u8 visibility);
@@ -1302,7 +1248,6 @@ public:
void compute_symtab_size(Context<E> &ctx);
void populate_symtab(Context<E> &ctx);
bool is_needed = false;
std::string soname;
std::vector<std::string_view> version_strings;
std::vector<ElfSym<E>> elf_syms2;
@@ -1398,7 +1343,7 @@ template <typename E> void acquire_global_lock(Context<E> &ctx);
template <typename E> void release_global_lock(Context<E> &ctx);
//
// commandline.cc
// cmdline.cc
//
template <typename E>
@@ -1408,6 +1353,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx);
// passes.cc
//
template <typename E> int redo_main(Context<E> &, int argc, char **argv);
template <typename E> void create_internal_file(Context<E> &);
template <typename E> void apply_exclude_libs(Context<E> &);
template <typename E> void create_synthetic_sections(Context<E> &);
@@ -1439,7 +1385,7 @@ template <typename E> void copy_chunks(Context<E> &);
template <typename E> void apply_version_script(Context<E> &);
template <typename E> void parse_symbol_version(Context<E> &);
template <typename E> void compute_import_export(Context<E> &);
template <typename E> void mark_addrsig(Context<E> &);
template <typename E> void compute_address_significance(Context<E> &);
template <typename E> void clear_padding(Context<E> &);
template <typename E> void compute_section_headers(Context<E> &);
template <typename E> i64 set_osec_offsets(Context<E> &);
@@ -1452,13 +1398,31 @@ template <typename E> void show_stats(Context<E> &);
// arch-arm32.cc
//
template <> u64 get_eflags(Context<ARM32> &ctx);
void fixup_arm_exidx_section(Context<ARM32> &ctx);
//
// arch-riscv64.cc
// arch-riscv.cc
//
template <typename E>
template <is_riscv E>
class RiscvAttributesSection : public Chunk<E> {
public:
RiscvAttributesSection() {
this->name = ".riscv.attributes";
this->shdr.sh_type = SHT_RISCV_ATTRIBUTES;
}
void update_shdr(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
std::vector<u8> contents;
};
template <is_riscv E>
u64 get_eflags(Context<E> &ctx);
template <is_riscv E>
i64 riscv_resize_sections(Context<E> &ctx);
//
@@ -1486,6 +1450,12 @@ public:
std::vector<Symbol<PPC64V1> *> symbols;
};
//
// arch-ppc64v2.cc
//
template <> u64 get_eflags(Context<PPC64V2> &ctx);
//
// arch-sparc.cc
//
@@ -1511,6 +1481,7 @@ class AlphaGotSection : public Chunk<ALPHA> {
public:
AlphaGotSection() {
this->name = ".alpha_got";
this->is_relro = true;
this->shdr.sh_type = SHT_PROGBITS;
this->shdr.sh_flags = SHF_ALLOC | SHF_WRITE;
this->shdr.sh_addralign = 8;
@@ -1533,57 +1504,6 @@ private:
std::mutex mu;
};
//
// arch-mips64.cc
//
template <typename E>
class MipsGotSection : public Chunk<E> {
public:
MipsGotSection() {
this->name = ".mips_got";
this->shdr.sh_type = SHT_PROGBITS;
this->shdr.sh_flags = SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL;
this->shdr.sh_addralign = 8;
this->shdr.sh_size = 8;
}
u64 get_got_addr(Context<E> &ctx, Symbol<E> &sym, i64 addend) const;
u64 get_gotpage_got_addr(Context<E> &ctx, Symbol<E> &sym, i64 addend) const;
u64 get_gotpage_page_addr(Context<E> &ctx, Symbol<E> &sym, i64 addend) const;
u64 get_gottp_addr(Context<E> &ctx, Symbol<E> &sym) const;
u64 get_tlsgd_addr(Context<E> &ctx, Symbol<E> &sym) const;
u64 get_tlsld_addr(Context<E> &ctx) const;
void update_shdr(Context<E> &ctx) override;
i64 get_reldyn_size(Context<E> &ctx) const override;
void copy_buf(Context<E> &ctx) override;
struct GotEntry {
u64 val = 0;
i64 r_type = R_NONE;
Symbol<E> *sym = nullptr;
};
struct SymbolAddend {
bool operator==(const SymbolAddend &) const = default;
bool operator<(const SymbolAddend &) const;
u64 get_addr(Context<E> &ctx, i64 flags = 0) const;
Symbol<E> *sym;
i64 addend;
};
std::vector<GotEntry> get_got_entries(Context<E> &ctx) const;
std::vector<SymbolAddend> got_syms;
std::vector<SymbolAddend> gotpage_syms;
std::vector<Symbol<E> *> tlsgd_syms;
std::vector<Symbol<E> *> gottp_syms;
std::atomic_bool needs_tlsld = false;
std::mutex mu;
};
//
// main.cc
//
@@ -1637,36 +1557,42 @@ struct SectionOrder {
};
// Target-specific context members
template <typename E> struct ContextExtras {};
template <typename E>
struct ContextExtras {};
template <> struct ContextExtras<PPC32> {
template <is_riscv E>
struct ContextExtras<E> {
RiscvAttributesSection<E> *riscv_attributes = nullptr;
};
template <>
struct ContextExtras<PPC32> {
Symbol<PPC32> *_SDA_BASE_ = nullptr;
};
template <> struct ContextExtras<PPC64V1> {
template <>
struct ContextExtras<PPC64V1> {
PPC64OpdSection *opd = nullptr;
Symbol<PPC64V1> *TOC = nullptr;
};
template <> struct ContextExtras<PPC64V2> {
template <>
struct ContextExtras<PPC64V2> {
Symbol<PPC64V2> *TOC = nullptr;
Atomic<bool> is_power10 = false;
};
template <> struct ContextExtras<SPARC64> {
template <>
struct ContextExtras<SPARC64> {
SparcTlsGetAddrSection *tls_get_addr_sec = nullptr;
Symbol<SPARC64> *tls_get_addr_sym = nullptr;
};
template <> struct ContextExtras<ALPHA> {
template <>
struct ContextExtras<ALPHA> {
AlphaGotSection *got = nullptr;
};
template <typename E> requires is_mips<E>
struct ContextExtras<E> {
MipsGotSection<E> *got = nullptr;
};
// Context represents a context object for each invocation of the linker.
// It contains command line flags, pointers to singleton objects
// (such as linker-synthesized output sections), unique_ptrs for
@@ -1709,7 +1635,7 @@ struct Context {
bool fork = true;
bool gc_sections = false;
bool gdb_index = false;
bool hash_style_gnu = !is_mips<E>;
bool hash_style_gnu = true;
bool hash_style_sysv = true;
bool icf = false;
bool icf_all = false;
@@ -1760,22 +1686,24 @@ struct Context {
bool z_now = false;
bool z_origin = false;
bool z_relro = true;
bool z_rewrite_endbr = false;
bool z_sectionheader = true;
bool z_shstk = false;
bool z_text = false;
i64 filler = -1;
i64 spare_dynamic_tags = 5;
i64 thread_count = 0;
i64 z_stack_size = 0;
u64 shuffle_sections_seed;
std::string_view emulation;
std::optional<Glob> unique;
std::optional<u64> physical_image_base;
std::optional<u64> shuffle_sections_seed;
std::string Map;
std::string chroot;
std::string dependency_file;
std::string directory;
std::string dynamic_linker;
std::string entry = is_mips<E> ? "__start" : "_start";
std::string entry = "_start";
std::string fini = "_fini";
std::string init = "_init";
std::string output = "a.out";
@@ -1805,7 +1733,7 @@ struct Context {
std::vector<VersionPattern> version_patterns;
u16 default_version = VER_NDX_GLOBAL;
i64 page_size = -1;
i64 page_size = E::page_size;
std::optional<int> global_lock_fd;
// true if default_version is set by a wildcard in version script.
@@ -1857,9 +1785,9 @@ struct Context {
bool overwrite_output_file = true;
std::vector<Chunk<E> *> chunks;
std::atomic_bool needs_tlsld = false;
std::atomic_bool has_textrel = false;
std::atomic_uint32_t num_ifunc_dynrels = 0;
Atomic<bool> needs_tlsld = false;
Atomic<bool> has_textrel = false;
Atomic<u32> num_ifunc_dynrels = 0;
tbb::concurrent_hash_map<std::string_view, std::vector<std::string>> undef_errors;
@@ -1901,11 +1829,11 @@ struct Context {
[[no_unique_address]] ContextExtras<E> extra;
// For --gdb-index
Chunk<E> *debug_info = nullptr;
Chunk<E> *debug_abbrev = nullptr;
Chunk<E> *debug_ranges = nullptr;
Chunk<E> *debug_addr = nullptr;
Chunk<E> *debug_rnglists = nullptr;
std::span<u8> debug_info;
std::span<u8> debug_abbrev;
std::span<u8> debug_ranges;
std::span<u8> debug_addr;
std::span<u8> debug_rnglists;
// For thread-local variables
u64 tls_begin = 0;
@@ -1936,7 +1864,6 @@ struct Context {
Symbol<E> *_edata = nullptr;
Symbol<E> *_end = nullptr;
Symbol<E> *_etext = nullptr;
Symbol<E> *_gp = nullptr;
Symbol<E> *edata = nullptr;
Symbol<E> *end = nullptr;
Symbol<E> *etext = nullptr;
@@ -1981,7 +1908,7 @@ enum {
template <typename E>
struct SymbolExtras {};
template <typename E> requires needs_thunk<E>
template <needs_thunk E>
struct SymbolExtras<E> {
// For range extension thunks
i16 thunk_idx = -1;
@@ -2014,6 +1941,7 @@ public:
u64 get_tlsdesc_addr(Context<E> &ctx) const;
u64 get_plt_addr(Context<E> &ctx) const;
u64 get_opd_addr(Context<E> &ctx) const;
u64 get_got_pltgot_addr(Context<E> &ctx) const;
void set_got_idx(Context<E> &ctx, i32 idx);
void set_gottp_idx(Context<E> &ctx, i32 idx);
@@ -2047,8 +1975,13 @@ public:
bool is_relative() const { return !is_absolute(); }
bool is_local(Context<E> &ctx) const;
bool is_ifunc() const { return get_type() == STT_GNU_IFUNC; }
bool is_pde_ifunc(Context<E> &ctx) const;
bool is_remaining_undef_weak() const;
bool is_pcrel_linktime_const(Context<E> &ctx) const;
bool is_tprel_linktime_const(Context<E> &ctx) const;
bool is_tprel_runtime_const(Context<E> &ctx) const;
InputSection<E> *get_input_section() const;
Chunk<E> *get_output_section() const;
SectionFragment<E> *get_frag() const;
@@ -2420,7 +2353,7 @@ InputSection<E>::is_relr_reloc(Context<E> &ctx, const ElfRel<E> &rel) const {
(rel.r_offset % sizeof(Word<E>)) == 0;
}
template<typename E>
template <typename E>
inline bool InputSection<E>::is_killed_by_icf() const {
return this->leader && this->leader != this;
}
@@ -2644,6 +2577,25 @@ inline u64 Symbol<E>::get_opd_addr(Context<E> &ctx) const {
get_opd_idx(ctx) * PPC64OpdSection::ENTRY_SIZE;
}
template <typename E>
inline u64 Symbol<E>::get_got_pltgot_addr(Context<E> &ctx) const {
// An ifunc symbol occupies two consecutive GOT slots in a
// position-dependent executable (PDE). The first slot contains the
// symbol's PLT address, and the second slot holds the resolved
// address. A PDE uses the ifunc symbol's PLT entry as the address
// for the symbol, akin to a canonical PLT.
//
// This function returns the address that the PLT entry should use
// to jump to the resolved address.
//
// Note that we don't use this function for PPC64. In PPC64, symbols
// are always accessed through the TOC table regardless of the
// -fno-PIE setting. We don't need canonical PLTs on the psABIs too.
if (is_pde_ifunc(ctx))
return get_got_addr(ctx) + sizeof(Word<E>);
return get_got_addr(ctx);
}
template <typename E>
inline void Symbol<E>::set_got_idx(Context<E> &ctx, i32 idx) {
assert(aux_idx != -1);
@@ -2772,14 +2724,43 @@ inline bool Symbol<E>::is_local(Context<E> &ctx) const {
return !is_imported && !is_exported;
}
template <typename E>
inline bool Symbol<E>::is_pde_ifunc(Context<E> &ctx) const {
// Returns true if this is an ifunc tha uses two GOT slots
return is_ifunc() && !ctx.arg.pic && !is_ppc64<E>;
}
// A remaining weak undefined symbol is promoted to a dynamic symbol
// in DSO and resolved to 0 in an executable. This function returns
// true if it's latter.
template<typename E>
template <typename E>
inline bool Symbol<E>::is_remaining_undef_weak() const {
return !is_imported && esym().is_undef_weak();
}
// Returns true if the symbol's PC-relative address is known at link-time.
template <typename E>
inline bool Symbol<E>::is_pcrel_linktime_const(Context<E> &ctx) const {
return !is_imported && !is_ifunc() && (is_relative() || !ctx.arg.pic);
}
// Returns true if the symbol's Thread Pointer-relative address is
// known at link-time.
template <typename E>
inline bool Symbol<E>::is_tprel_linktime_const(Context<E> &ctx) const {
assert(get_type() == STT_TLS);
return !ctx.arg.shared && !is_imported;
}
// Returns true if the symbol's Thread Pointer-relative address is
// known at load-time.
template <typename E>
inline bool Symbol<E>::is_tprel_runtime_const(Context<E> &ctx) const {
// Returns true unless we are creating a dlopen'able DSO.
assert(get_type() == STT_TLS);
return !(ctx.arg.shared && ctx.arg.z_dlopen);
}
template <typename E>
inline InputSection<E> *Symbol<E>::get_input_section() const {
if ((origin & TAG_MASK) == TAG_ISEC)
@@ -2864,9 +2845,8 @@ inline std::string_view Symbol<E>::name() const {
template <typename E>
inline void Symbol<E>::add_aux(Context<E> &ctx) {
if (aux_idx == -1) {
i64 sz = ctx.symbol_aux.size();
aux_idx = sz;
ctx.symbol_aux.resize(sz + 1);
aux_idx = ctx.symbol_aux.size();
ctx.symbol_aux.resize(aux_idx + 1);
}
}
@@ -2890,15 +2870,4 @@ inline bool is_c_identifier(std::string_view s) {
return true;
}
template <typename E>
inline bool relax_tlsdesc(Context<E> &ctx, Symbol<E> &sym) {
// TLSDESC relocs must be always relaxed for statically-linked
// executables even if -no-relax is given. It is because a
// statically-linked executable doesn't contain a tranpoline
// function needed for TLSDESC.
if (ctx.arg.is_static)
return true;
return ctx.arg.relax && !ctx.arg.shared && !sym.is_imported;
}
} // namespace mold::elf
+417 -624
View File
File diff suppressed because it is too large. Load diff
+258 -150
View File
@@ -4,7 +4,6 @@
#include <functional>
#include <map>
#include <optional>
#include <random>
#include <regex>
#include <shared_mutex>
#include <tbb/parallel_for_each.h>
@@ -14,6 +13,50 @@
namespace mold::elf {
// Since elf_main is a template, we can't run it without a type parameter.
// We speculatively run elf_main with X86_64, and if the speculation was
// wrong, re-run it with an actual machine type.
template <typename E>
int redo_main(Context<E> &ctx, int argc, char **argv) {
std::string_view target = ctx.arg.emulation;
if (target == I386::target_name)
return elf_main<I386>(argc, argv);
if (target == ARM64::target_name)
return elf_main<ARM64>(argc, argv);
if (target == ARM32::target_name)
return elf_main<ARM32>(argc, argv);
if (target == RV64LE::target_name)
return elf_main<RV64LE>(argc, argv);
if (target == RV64BE::target_name)
return elf_main<RV64BE>(argc, argv);
if (target == RV32LE::target_name)
return elf_main<RV32LE>(argc, argv);
if (target == RV32BE::target_name)
return elf_main<RV32BE>(argc, argv);
if (target == PPC32::target_name)
return elf_main<PPC32>(argc, argv);
if (target == PPC64V1::target_name)
return elf_main<PPC64V1>(argc, argv);
if (target == PPC64V2::target_name)
return elf_main<PPC64V2>(argc, argv);
if (target == S390X::target_name)
return elf_main<S390X>(argc, argv);
if (target == SPARC64::target_name)
return elf_main<SPARC64>(argc, argv);
if (target == M68K::target_name)
return elf_main<M68K>(argc, argv);
if (target == SH4::target_name)
return elf_main<SH4>(argc, argv);
if (target == ALPHA::target_name)
return elf_main<ALPHA>(argc, argv);
if (target == LOONGARCH32::target_name)
return elf_main<LOONGARCH32>(argc, argv);
if (target == LOONGARCH64::target_name)
return elf_main<LOONGARCH64>(argc, argv);
unreachable();
}
template <typename E>
void apply_exclude_libs(Context<E> &ctx) {
Timer t(ctx, "apply_exclude_libs");
@@ -32,9 +75,17 @@ void apply_exclude_libs(Context<E> &ctx) {
}
}
template <typename E>
static bool has_debug_info_section(Context<E> &ctx) {
for (ObjectFile<E> *file : ctx.objs)
if (file->debug_info)
return true;
return false;
}
template <typename E>
void create_synthetic_sections(Context<E> &ctx) {
auto push = [&]<typename T>(T *x) {
auto push = [&](auto *x) {
ctx.chunks.push_back(x);
ctx.chunk_pool.emplace_back(x);
return x;
@@ -58,13 +109,14 @@ void create_synthetic_sections(Context<E> &ctx) {
else
ctx.phdr = push(new OutputPhdr<E>(0));
ctx.shdr = push(new OutputShdr<E>);
if (ctx.arg.z_sectionheader)
ctx.shdr = push(new OutputShdr<E>);
}
ctx.got = push(new GotSection<E>);
if constexpr (!is_sparc<E>)
ctx.gotplt = push(new GotPltSection<E>);
ctx.gotplt = push(new GotPltSection<E>(ctx));
ctx.reldyn = push(new RelDynSection<E>);
ctx.relplt = push(new RelPltSection<E>);
@@ -82,7 +134,7 @@ void create_synthetic_sections(Context<E> &ctx) {
ctx.copyrel = push(new CopyrelSection<E>(false));
ctx.copyrel_relro = push(new CopyrelSection<E>(true));
if (!ctx.arg.oformat_binary)
if (ctx.shdr)
ctx.shstrtab = push(new ShstrtabSection<E>);
if (!ctx.arg.dynamic_linker.empty())
@@ -91,7 +143,7 @@ void create_synthetic_sections(Context<E> &ctx) {
ctx.buildid = push(new BuildIdSection<E>);
if (ctx.arg.eh_frame_hdr)
ctx.eh_frame_hdr = push(new EhFrameHdrSection<E>);
if (ctx.arg.gdb_index)
if (ctx.arg.gdb_index && has_debug_info_section(ctx))
ctx.gdb_index = push(new GdbIndexSection<E>);
if (ctx.arg.z_relro && ctx.arg.section_order.empty() &&
ctx.arg.z_separate_code != SEPARATE_LOADABLE_SEGMENTS)
@@ -105,14 +157,22 @@ void create_synthetic_sections(Context<E> &ctx) {
if (ctx.arg.emit_relocs)
ctx.eh_frame_reloc = push(new EhFrameRelocSection<E>);
if (ctx.arg.shared || !ctx.dsos.empty() || ctx.arg.pie)
if (ctx.arg.shared || !ctx.dsos.empty() || ctx.arg.pie) {
ctx.dynamic = push(new DynamicSection<E>);
// If .dynamic exists, .dynsym and .dynstr must exist as well
// since .dynamic refers to them.
ctx.dynstr->shdr.sh_size = 1;
ctx.dynsym->symbols.resize(1);
}
ctx.versym = push(new VersymSection<E>);
ctx.verneed = push(new VerneedSection<E>);
ctx.note_package = push(new NotePackageSection<E>);
ctx.note_property = push(new NotePropertySection<E>);
if constexpr (is_riscv<E>)
ctx.extra.riscv_attributes = push(new RiscvAttributesSection<E>);
if constexpr (is_ppc64v1<E>)
ctx.extra.opd = push(new PPC64OpdSection);
@@ -125,16 +185,6 @@ void create_synthetic_sections(Context<E> &ctx) {
if constexpr (is_alpha<E>)
ctx.extra.got = push(new AlphaGotSection);
if constexpr (is_mips<E>)
ctx.extra.got = push(new MipsGotSection<E>);
// If .dynamic exists, .dynsym and .dynstr must exist as well
// since .dynamic refers them.
if (ctx.dynamic) {
ctx.dynstr->keep();
ctx.dynsym->keep();
}
}
template <typename E>
@@ -297,6 +347,7 @@ void resolve_symbols(Context<E> &ctx) {
// .eh_frame sections are parsed and regenerated by the linker for the purpose
// of deduplication and garbage collection. As such, the input sections should
// not be copied over.
//
// However, in very rare cases (e.g. GCC CRT compiled with LTO) we might need
// to resolve cross-object .eh_frame section references (they only point to
// begin or end and don't depend on the actual section contents).
@@ -306,15 +357,9 @@ template <typename E>
void kill_eh_frame_sections(Context<E> &ctx) {
Timer t(ctx, "kill_eh_frame_sections");
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (i64 i = 0; i < file->sections.size(); i++) {
if (std::unique_ptr<InputSection<E>> &isec = file->sections[i]) {
if (isec && isec->is_alive && isec->name() == ".eh_frame") {
isec->is_alive = false;
}
}
}
});
for (ObjectFile<E> *file : ctx.objs)
if (file->eh_frame_section)
file->eh_frame_section->is_alive = false;
}
template <typename E>
@@ -352,7 +397,7 @@ template <typename E>
void add_comment_string(Context<E> &ctx, std::string str) {
MergedSection<E> *sec =
MergedSection<E>::get_instance(ctx, ".comment", SHT_PROGBITS,
SHF_MERGE | SHF_STRINGS);
SHF_MERGE | SHF_STRINGS, 1);
std::string_view buf = save_string(ctx, str);
std::string_view data(buf.data(), buf.size() + 1);
@@ -407,13 +452,10 @@ static u64 canonicalize_type(std::string_view name, u64 type) {
}
struct OutputSectionKey {
bool operator==(const OutputSectionKey &) const = default;
std::string_view name;
u64 type;
u64 flags;
bool operator==(const OutputSectionKey &other) const {
return name == other.name && type == other.type && flags == other.flags;
}
};
template <typename E>
@@ -474,10 +516,7 @@ get_output_section_key(Context<E> &ctx, InputSection<E> &isec) {
const ElfShdr<E> &shdr = isec.shdr();
std::string_view name = get_output_name(ctx, isec.name(), shdr.sh_flags);
u64 type = canonicalize_type<E>(name, shdr.sh_type);
u64 flags = shdr.sh_flags & ~(u64)SHF_COMPRESSED;
if (!ctx.arg.relocatable)
flags &= ~(u64)SHF_GROUP & ~(u64)SHF_GNU_RETAIN;
u64 flags = shdr.sh_flags & ~(u64)(SHF_COMPRESSED | SHF_GROUP | SHF_GNU_RETAIN);
// .init_array is usually writable. We don't want to create multiple
// .init_array output sections, so make it always writable.
@@ -504,6 +543,8 @@ void create_output_sections(Context<E> &ctx) {
std::unordered_map<OutputSectionKey, OutputSection<E> *, Hash> map;
std::shared_mutex mu;
i64 size = ctx.osec_pool.size();
// Instantiate output sections
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
// Make a per-thread cache of the main map to avoid lock contention.
@@ -518,6 +559,15 @@ void create_output_sections(Context<E> &ctx) {
if (!isec || !isec->is_alive)
continue;
const ElfShdr<E> &shdr = isec->shdr();
if (ctx.arg.relocatable && (shdr.sh_flags & SHF_GROUP)) {
OutputSection<E> *osec =
new OutputSection<E>(ctx, isec->name(), shdr.sh_type, shdr.sh_flags);
isec->output_section = osec;
ctx.osec_pool.emplace_back(osec);
continue;
}
OutputSectionKey key = get_output_section_key(ctx, *isec);
if (auto it = cache.find(key); it != cache.end()) {
@@ -533,12 +583,11 @@ void create_output_sections(Context<E> &ctx) {
}
std::unique_ptr<OutputSection<E>> osec =
std::make_unique<OutputSection<E>>(key.name, key.type, key.flags);
std::make_unique<OutputSection<E>>(ctx, key.name, key.type, key.flags);
std::unique_lock lock(mu);
auto [it, inserted] = map.insert({key, osec.get()});
OutputSection<E> *ret = it->second;
lock.unlock();
if (inserted)
ctx.osec_pool.emplace_back(std::move(osec));
@@ -552,29 +601,29 @@ void create_output_sections(Context<E> &ctx) {
});
// Add input sections to output sections
std::vector<Chunk<E> *> chunks;
for (i64 i = size; i < ctx.osec_pool.size(); i++)
chunks.push_back(ctx.osec_pool[i].get());
for (ObjectFile<E> *file : ctx.objs)
for (std::unique_ptr<InputSection<E>> &isec : file->sections)
if (isec && isec->is_alive)
isec->output_section->members.push_back(isec.get());
// Add output sections and mergeable sections to ctx.chunks
std::vector<Chunk<E> *> vec;
for (std::pair<const OutputSectionKey, OutputSection<E> *> &kv : map)
vec.push_back(kv.second);
for (std::unique_ptr<MergedSection<E>> &osec : ctx.merged_sections)
if (osec->shdr.sh_size)
vec.push_back(osec.get());
chunks.push_back(osec.get());
// Sections are added to the section lists in an arbitrary order
// because they are created in parallel. Sort them to to make the
// output deterministic.
tbb::parallel_sort(vec.begin(), vec.end(), [](Chunk<E> *x, Chunk<E> *y) {
tbb::parallel_sort(chunks.begin(), chunks.end(), [](Chunk<E> *x, Chunk<E> *y) {
return std::tuple(x->name, x->shdr.sh_type, x->shdr.sh_flags) <
std::tuple(y->name, y->shdr.sh_type, y->shdr.sh_flags);
});
append(ctx.chunks, vec);
append(ctx.chunks, chunks);
}
// Create a dummy object file containing linker-synthesized
@@ -681,10 +730,10 @@ template <typename E>
void add_synthetic_symbols(Context<E> &ctx) {
ObjectFile<E> &obj = *ctx.internal_obj;
auto add = [&](std::string_view name) {
auto add = [&](std::string_view name, u32 type = STT_NOTYPE) {
ElfSym<E> esym;
memset(&esym, 0, sizeof(esym));
esym.st_type = STT_NOTYPE;
esym.st_type = type;
esym.st_shndx = SHN_ABS;
esym.st_bind = STB_GLOBAL;
esym.st_visibility = STV_HIDDEN;
@@ -730,7 +779,7 @@ void add_synthetic_symbols(Context<E> &ctx) {
ctx.__dso_handle = add("__dso_handle");
if constexpr (supports_tlsdesc<E>)
ctx._TLS_MODULE_BASE_ = add("_TLS_MODULE_BASE_");
ctx._TLS_MODULE_BASE_ = add("_TLS_MODULE_BASE_", STT_TLS);
if constexpr (is_riscv<E>)
if (!ctx.arg.shared)
@@ -747,9 +796,6 @@ void add_synthetic_symbols(Context<E> &ctx) {
if constexpr (is_ppc32<E>)
ctx.extra._SDA_BASE_ = add("_SDA_BASE_");
if constexpr (is_mips<E>)
ctx._gp = add("_gp");
for (Chunk<E> *chunk : ctx.chunks) {
if (std::optional<std::string> name = get_start_stop_name(ctx, *chunk)) {
add(save_string(ctx, "__start_" + *name));
@@ -975,33 +1021,30 @@ template <typename E>
void check_symbol_types(Context<E> &ctx) {
Timer t(ctx, "check_symbol_types");
auto check = [&](InputFile<E> *file) {
for (i64 i = file->first_global; i < file->elf_syms.size(); i++) {
const ElfSym<E> &esym = file->elf_syms[i];
Symbol<E> &sym = *file->symbols[i];
std::vector<InputFile<E> *> files;
append(files, ctx.objs);
append(files, ctx.dsos);
if (!sym.file)
tbb::parallel_for_each(files.begin(), files.end(), [&](InputFile<E> *file) {
for (i64 i = file->first_global; i < file->elf_syms.size(); i++) {
Symbol<E> &sym = *file->symbols[i];
if (!sym.file || sym.file == file)
continue;
u32 x = sym.esym().st_type;
if (x == STT_GNU_IFUNC)
x = STT_FUNC;
const ElfSym<E> &esym1 = sym.esym();
const ElfSym<E> &esym2 = file->elf_syms[i];
u32 y = esym.st_type;
if (y == STT_GNU_IFUNC)
y = STT_FUNC;
u32 ty1 = (esym1.st_type == STT_GNU_IFUNC) ? (u32)STT_FUNC : esym1.st_type;
u32 ty2 = (esym2.st_type == STT_GNU_IFUNC) ? (u32)STT_FUNC : esym2.st_type;
if (x != STT_NOTYPE && y != STT_NOTYPE && x != y)
if (ty1 != STT_NOTYPE && ty2 != STT_NOTYPE && ty1 != ty2)
Warn(ctx) << "symbol type mismatch: " << sym << '\n'
<< ">>> defined in " << *sym.file << " as "
<< stt_to_string<E>(sym.esym().st_type) << '\n'
<< stt_to_string<E>(esym1.st_type) << '\n'
<< ">>> defined in " << *file << " as "
<< stt_to_string<E>(esym.st_type);
<< stt_to_string<E>(esym2.st_type);
}
};
tbb::parallel_for_each(ctx.objs, check);
tbb::parallel_for_each(ctx.dsos, check);
});
}
template <typename E>
@@ -1109,37 +1152,35 @@ template <typename E>
void shuffle_sections(Context<E> &ctx) {
Timer t(ctx, "shuffle_sections");
auto is_eligible = [](OutputSection<E> &osec) {
return osec.name != ".init" && osec.name != ".fini" &&
osec.name != ".ctors" && osec.name != ".dtors" &&
osec.name != ".init_array" && osec.name != ".preinit_array" &&
osec.name != ".fini_array";
auto is_eligible = [](OutputSection<E> *osec) {
if (osec) {
std::string_view name = osec->name;
return name != ".init" && name != ".fini" &&
name != ".ctors" && name != ".dtors" &&
name != ".init_array" && name != ".preinit_array" &&
name != ".fini_array";
}
return false;
};
switch (ctx.arg.shuffle_sections) {
case SHUFFLE_SECTIONS_NONE:
unreachable();
case SHUFFLE_SECTIONS_SHUFFLE: {
u64 seed;
if (ctx.arg.shuffle_sections_seed)
seed = *ctx.arg.shuffle_sections_seed;
else
seed = ((u64)std::random_device()() << 32) | std::random_device()();
tbb::parallel_for_each(ctx.chunks, [&](Chunk<E> *chunk) {
if (OutputSection<E> *osec = chunk->to_osec())
if (is_eligible(*osec))
shuffle(osec->members, seed + hash_string(osec->name));
if (OutputSection<E> *osec = chunk->to_osec(); is_eligible(osec)) {
u64 seed = ctx.arg.shuffle_sections_seed + hash_string(osec->name);
shuffle(osec->members, seed);
}
});
break;
}
case SHUFFLE_SECTIONS_REVERSE:
tbb::parallel_for_each(ctx.chunks, [&](Chunk<E> *chunk) {
if (OutputSection<E> *osec = chunk->to_osec())
if (is_eligible(*osec))
std::reverse(osec->members.begin(), osec->members.end());
if (OutputSection<E> *osec = chunk->to_osec(); is_eligible(osec))
std::reverse(osec->members.begin(), osec->members.end());
});
break;
default:
unreachable();
}
}
@@ -1180,19 +1221,16 @@ void compute_section_sizes(Context<E> &ctx) {
}
});
i64 offset = 0;
i64 p2align = 0;
ElfShdr<E> &shdr = osec->shdr;
shdr.sh_size = 0;
for (i64 i = 0; i < groups.size(); i++) {
offset = align_to(offset, 1 << groups[i].p2align);
groups[i].offset = offset;
offset += groups[i].size;
p2align = std::max(p2align, groups[i].p2align);
shdr.sh_size = align_to(shdr.sh_size, 1 << groups[i].p2align);
groups[i].offset = shdr.sh_size;
shdr.sh_size += groups[i].size;
shdr.sh_addralign = std::max<u32>(shdr.sh_addralign, 1 << groups[i].p2align);
}
osec->shdr.sh_size = offset;
osec->shdr.sh_addralign = 1 << p2align;
// Assign offsets to input sections.
tbb::parallel_for_each(groups, [](Group &group) {
i64 offset = group.offset;
@@ -1213,22 +1251,14 @@ void compute_section_sizes(Context<E> &ctx) {
// create_range_extension_thunks is parallelized internally, but the
// function itself is not thread-safe.
if constexpr (needs_thunk<E>) {
for (Chunk<E> *chunk : ctx.chunks) {
OutputSection<E> *osec = chunk->to_osec();
if (osec && (osec->shdr.sh_flags & SHF_EXECINSTR) && !ctx.arg.relocatable) {
create_range_extension_thunks(ctx, *osec);
Timer t2(ctx, "create_range_extension_thunks");
for (InputSection<E> *isec : osec->members)
osec->shdr.sh_addralign =
std::max<u32>(osec->shdr.sh_addralign, 1 << isec->p2align);
}
}
if (!ctx.arg.relocatable)
for (Chunk<E> *chunk : ctx.chunks)
if (OutputSection<E> *osec = chunk->to_osec())
if (osec->shdr.sh_flags & SHF_EXECINSTR)
osec->create_range_extension_thunks(ctx);
}
for (Chunk<E> *chunk : ctx.chunks)
if (OutputSection<E> *osec = chunk->to_osec())
if (u32 align = ctx.arg.section_align[osec->name])
osec->shdr.sh_addralign = std::max<u32>(osec->shdr.sh_addralign, align);
}
// Find all unresolved symbols and attach them to the most appropriate files.
@@ -1352,6 +1382,9 @@ void scan_relocations(Context<E> &ctx) {
std::vector<Symbol<E> *> syms = flatten(vec);
ctx.symbol_aux.reserve(syms.size());
if (ctx.needs_tlsld)
ctx.got->add_tlsld(ctx);
// Assign offsets in additional tables for each dynamic symbol.
for (Symbol<E> *sym : syms) {
sym->add_aux(ctx);
@@ -1402,9 +1435,6 @@ void scan_relocations(Context<E> &ctx) {
sym->flags = 0;
}
if (ctx.needs_tlsld)
ctx.got->add_tlsld(ctx);
if constexpr (is_alpha<E>)
ctx.extra.got->finalize();
@@ -1497,20 +1527,19 @@ void construct_relr(Context<E> &ctx) {
Timer t(ctx, "construct_relr");
tbb::parallel_for_each(ctx.chunks, [&](Chunk<E> *chunk) {
if (OutputSection<E> *osec = chunk->to_osec())
osec->construct_relr(ctx);
chunk->construct_relr(ctx);
});
ctx.got->construct_relr(ctx);
}
template <typename E>
void create_output_symtab(Context<E> &ctx) {
Timer t(ctx, "compute_symtab_size");
tbb::parallel_for_each(ctx.chunks, [&](Chunk<E> *chunk) {
chunk->compute_symtab_size(ctx);
});
if (!ctx.arg.strip_all && !ctx.arg.retain_symbols_file) {
tbb::parallel_for_each(ctx.chunks, [&](Chunk<E> *chunk) {
chunk->compute_symtab_size(ctx);
});
}
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
file->compute_symtab_size(ctx);
@@ -1700,12 +1729,85 @@ void compute_import_export(Context<E> &ctx) {
});
}
// Compute the "address-taken" bit for each input section.
//
// As a space-saving optimization, we want to merge two read-only objects
// into a single object if their contents are equivalent. That
// optimization is called the Identical Code Folding or ICF.
//
// A catch is that comparing object contents is not enough to determine if
// two objects can be merged safely; we need to take care of pointer
// equivalence.
//
// In C/C++, two pointers are equivalent if and only if they are taken for
// the same object. Merging two objects into a single object can break
// this assumption because two distinctive pointers would become
// equivalent as a result of merging. We can still merge one object with
// another if no pointer to the object was taken in code, because without
// a pointer, comparing its address becomes moot.
//
// In mold, each input section has an "address-taken" bit. If there is a
// pointer-taking reference to the object, it's set to true. At the ICF
// stage, we merge only objects whose addresses were not taken.
//
// For functions, address-taking relocations are separated from
// non-address-taking ones. For example, x86-64 uses R_X86_64_PLT32 for
// direct function calls (e.g., "call foo" to call the function foo) while
// R_X86_64_PC32 or R_X86_64_GOT32 are used for pointer-taking operations.
//
// Unfortunately, for data, we can't distinguish between address-taking
// relocations and non-address-taking ones. LLVM generates an "address
// significance" table in the ".llvm_addrsig" section to mark symbols
// whose addresses are taken in code. If that table is available, we use
// that information in this function. Otherwise, we conservatively assume
// that all data items are address-taken.
template <typename E>
void mark_addrsig(Context<E> &ctx) {
Timer t(ctx, "mark_addrsig");
void compute_address_significance(Context<E> &ctx) {
Timer t(ctx, "compute_address_significance");
// Flip address-taken bit for executable sections first.
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
file->mark_addrsig(ctx);
for (std::unique_ptr<InputSection<E>> &src : file->sections)
if (src && src->is_alive && (src->shdr().sh_flags & SHF_ALLOC))
for (const ElfRel<E> &r : src->get_rels(ctx))
if (!is_func_call_rel(r))
if (InputSection<E> *dst = file->symbols[r.r_sym]->get_input_section())
if (dst->shdr().sh_flags & SHF_EXECINSTR)
dst->address_taken = true;
});
auto mark = [](Symbol<E> *sym) {
if (sym)
if (InputSection<E> *isec = sym->get_input_section())
isec->address_taken = true;
};
// Some symbols' pointer values are leaked to the dynamic section.
mark(get_symbol(ctx, ctx.arg.entry));
mark(get_symbol(ctx, ctx.arg.init));
mark(get_symbol(ctx, ctx.arg.fini));
// Exported symbols are conservatively considered address-taken.
if (ctx.dynsym)
for (Symbol<E> *sym : ctx.dynsym->symbols)
if (sym->is_exported)
mark(sym);
// Handle data objects.
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
if (InputSection<E> *sec = file->llvm_addrsig.get()) {
u8 *p = (u8 *)sec->contents.data();
u8 *end = p + sec->contents.size();
while (p != end) {
Symbol<E> *sym = file->symbols[read_uleb(&p)];
if (InputSection<E> *isec = sym->get_input_section())
isec->address_taken = true;
}
} else {
for (std::unique_ptr<InputSection<E>> &isec : file->sections)
if (isec && !(isec->shdr().sh_flags & SHF_EXECINSTR))
isec->address_taken = true;
}
});
}
@@ -1751,13 +1853,13 @@ void clear_padding(Context<E> &ctx) {
// .got
// .toc
// .alpha_got
// .mips_got
// <writable RELRO bss>
// .relro_padding
// <writable non-RELRO data>
// <writable non-RELRO bss>
// <non-memory-allocated sections>
// <section header>
// .gdb_index
//
// .interp and some other linker-synthesized sections are placed at the
// beginning of a file because they are needed by loader. Especially on
@@ -1769,6 +1871,10 @@ void clear_padding(Context<E> &ctx) {
// particular, if .note.gnu.build-id is in a truncated core file, you
// can at least identify which executable has crashed.
//
// .gdb_index cannot be constructed before applying relocations to
// other debug sections, so we create it after completing other part
// of the output file and append it to the very end of the file.
//
// A PT_NOTE segment will contain multiple .note sections if exists,
// but there's no way to represent a gap between .note sections.
// Therefore, we sort .note sections by decreasing alignment
@@ -1814,45 +1920,48 @@ void sort_output_sections_regular(Context<E> &ctx) {
if (chunk == ctx.relplt)
return 11;
if (chunk == ctx.shdr)
return INT32_MAX - 1;
if (chunk == ctx.gdb_index)
return INT32_MAX;
bool alloc = (flags & SHF_ALLOC);
bool writable = (flags & SHF_WRITE);
bool exec = (flags & SHF_EXECINSTR);
bool tls = (flags & SHF_TLS);
bool relro = is_relro(ctx, chunk);
bool relro = chunk->is_relro;
bool is_bss = (type == SHT_NOBITS);
return (1 << 10) | (!alloc << 9) | (writable << 8) | (exec << 7) |
(!tls << 6) | (!relro << 5) | (is_bss << 4);
};
// Ties are broken by additional rules
auto get_rank2 = [&](Chunk<E> *chunk) -> i64 {
if (chunk->shdr.sh_type == SHT_NOTE)
return -chunk->shdr.sh_addralign;
ElfShdr<E> &shdr = chunk->shdr;
if (shdr.sh_type == SHT_NOTE)
return -shdr.sh_addralign;
if (chunk == ctx.got)
return 1;
if (chunk->name == ".toc")
return 2;
if (chunk->name == ".alpha_got")
if (chunk->name == ".toc")
return 3;
if (chunk->name == ".mips_got")
if (chunk->name == ".alpha_got")
return 4;
if (shdr.sh_flags & SHF_MERGE) {
if (shdr.sh_flags & SHF_STRINGS)
return (5LL << 32) | shdr.sh_entsize;
return (6LL << 32) | shdr.sh_entsize;
}
if (chunk == ctx.relro_padding)
return INT_MAX;
return INT64_MAX;
return 0;
};
sort(ctx.chunks, [&](Chunk<E> *a, Chunk<E> *b) {
// Sort sections by segments
i64 x = get_rank1(a);
i64 y = get_rank1(b);
if (x != y)
return x < y;
// Ties are broken by additional rules
return get_rank2(a) < get_rank2(b);
return std::tuple{get_rank1(a), get_rank2(a), a->name} <
std::tuple{get_rank1(b), get_rank2(b), b->name};
});
}
@@ -1961,7 +2070,7 @@ static void set_virtual_addresses_regular(Context<E> &ctx) {
auto get_flags = [&](Chunk<E> *chunk) {
i64 flags = to_phdr_flags(ctx, chunk);
if (is_relro(ctx, chunk))
if (chunk->is_relro)
return flags | RELRO;
return flags;
};
@@ -2224,8 +2333,9 @@ void compute_section_headers(Context<E> &ctx) {
chunk->update_shdr(ctx);
// Remove empty chunks.
std::erase_if(ctx.chunks, [](Chunk<E> *chunk) {
return chunk->kind() != OUTPUT_SECTION && chunk->shdr.sh_size == 0;
std::erase_if(ctx.chunks, [&](Chunk<E> *chunk) {
return chunk->kind() != OUTPUT_SECTION && chunk != ctx.gdb_index &&
chunk->shdr.sh_size == 0;
});
// Set section indices.
@@ -2418,7 +2528,7 @@ void fix_synthetic_symbols(Context<E> &ctx) {
// this symbol.
if (ctx._TLS_MODULE_BASE_) {
ctx._TLS_MODULE_BASE_->set_output_section(sections[0]);
ctx._TLS_MODULE_BASE_->value = ctx.tls_begin;
ctx._TLS_MODULE_BASE_->value = ctx.dtp_addr;
}
// __GNU_EH_FRAME_HDR
@@ -2454,10 +2564,6 @@ void fix_synthetic_symbols(Context<E> &ctx) {
}
}
// MIPS' _gp symbol.
if (ctx._gp)
start(ctx._gp, ctx.got, 0x7ff0);
// __start_ and __stop_ symbols
for (Chunk<E> *chunk : sections) {
if (std::optional<std::string> name = get_start_stop_name(ctx, *chunk)) {
@@ -2523,7 +2629,8 @@ i64 compress_debug_sections(Context<E> &ctx) {
ctx.chunks[i] = comp;
});
ctx.shstrtab->update_shdr(ctx);
if (ctx.shstrtab)
ctx.shstrtab->update_shdr(ctx);
if (ctx.ehdr)
ctx.ehdr->update_shdr(ctx);
@@ -2631,6 +2738,7 @@ void show_stats(Context<E> &ctx) {
using E = MOLD_TARGET;
template int redo_main(Context<E> &, int, char **);
template void create_internal_file(Context<E> &);
template void apply_exclude_libs(Context<E> &);
template void create_synthetic_sections(Context<E> &);
@@ -2661,7 +2769,7 @@ template void create_output_symtab(Context<E> &);
template void apply_version_script(Context<E> &);
template void parse_symbol_version(Context<E> &);
template void compute_import_export(Context<E> &);
template void mark_addrsig(Context<E> &);
template void compute_address_significance(Context<E> &);
template void clear_padding(Context<E> &);
template void compute_section_headers(Context<E> &);
template i64 set_osec_offsets(Context<E> &);
+4 -1
View File
@@ -40,7 +40,7 @@ namespace mold::elf {
// Create linker-synthesized sections
template <typename E>
static void r_create_synthetic_sections(Context<E> &ctx) {
auto push = [&]<typename T>(T *x) {
auto push = [&](auto *x) {
ctx.chunks.push_back(x);
ctx.chunk_pool.emplace_back(x);
return x;
@@ -54,6 +54,9 @@ static void r_create_synthetic_sections(Context<E> &ctx) {
ctx.symtab = push(new SymtabSection<E>);
ctx.shstrtab = push(new ShstrtabSection<E>);
ctx.note_property = push(new NotePropertySection<E>);
if constexpr (is_riscv<E>)
ctx.extra.riscv_attributes = push(new RiscvAttributesSection<E>);
}
// Create SHT_GROUP (i.e. comdat group) sections. We uniquify comdat
+56 -69
View File
@@ -20,7 +20,8 @@
// 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
#if MOLD_ARM32 || MOLD_ARM64 || MOLD_PPC32 || MOLD_PPC64V1 || MOLD_PPC64V2 || \
MOLD_LOONGARCH64 || MOLD_LOONGARCH32
#include "mold.h"
@@ -29,15 +30,18 @@
namespace mold::elf {
using E = MOLD_TARGET;
// Returns a branch reach in bytes for a given target.
template <typename E>
static consteval i64 max_distance() {
// ARM64's branch has 26 bits immediate. The immediate is padded with
// implicit two-bit zeros because all instructions are 4 bytes aligned
// and therefore the least two bits are always zero. So the branch
// operand is effectively 28 bits long. That means the branch range is
// [-2^27, 2^27) or PC ± 128 MiB.
if (is_arm64<E>)
//
// 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
@@ -57,30 +61,12 @@ static consteval i64 max_distance() {
// We create thunks for each 12.8/1.6/3.2 MiB code block for
// ARM64/ARM32/PPC, respectively.
template <typename E>
static constexpr i64 batch_size = max_distance<E>() / 10;
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;
// Returns true if a given relocation is of type used for function calls.
template <typename E>
static bool needs_thunk_rel(const ElfRel<E> &r) {
u32 ty = r.r_type;
if constexpr (is_arm64<E>) {
return ty == R_AARCH64_JUMP26 || ty == R_AARCH64_CALL26;
} else if constexpr (is_arm32<E>) {
return ty == R_ARM_JUMP24 || ty == R_ARM_THM_JUMP24 ||
ty == R_ARM_CALL || ty == R_ARM_THM_CALL ||
ty == R_ARM_PLT32;
} else if constexpr (is_ppc32<E>) {
return ty == R_PPC_REL24 || ty == R_PPC_PLTREL24 || ty == R_PPC_LOCAL24PC;
} else {
static_assert(is_ppc64<E>);
return ty == R_PPC64_REL24 || ty == R_PPC64_REL24_NOTOC;
}
}
static_assert(max_thunk_size / E::thunk_size < INT16_MAX);
template <typename E>
static bool is_reachable(Context<E> &ctx, InputSection<E> &isec,
@@ -130,10 +116,9 @@ static bool is_reachable(Context<E> &ctx, InputSection<E> &isec,
i64 A = get_addend(isec, rel);
i64 P = isec.get_addr() + rel.r_offset;
i64 val = S + A - P;
return -max_distance<E>() <= val && val < max_distance<E>();
return -max_distance() <= val && val < max_distance();
}
template <typename E>
static void reset_thunk(RangeExtensionThunk<E> &thunk) {
for (Symbol<E> *sym : thunk.symbols) {
sym->extra.thunk_idx = -1;
@@ -143,16 +128,15 @@ static void reset_thunk(RangeExtensionThunk<E> &thunk) {
}
// Scan relocations to collect symbols that need thunks.
template <typename E>
static void scan_rels(Context<E> &ctx, InputSection<E> &isec,
RangeExtensionThunk<E> &thunk) {
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 (!needs_thunk_rel(rel))
if (!is_func_call_rel(rel))
continue;
// Skip if the symbol is undefined. apply_reloc() will report an error.
@@ -174,7 +158,7 @@ static void scan_rels(Context<E> &ctx, InputSection<E> &isec,
// Otherwise, add the symbol to the current thunk if it's not
// added already.
range_extn[i].thunk_idx = thunk.thunk_idx;
range_extn[i].thunk_idx = thunk_idx;
range_extn[i].sym_idx = -1;
if (sym.flags.exchange(-1) == 0) {
@@ -184,19 +168,19 @@ static void scan_rels(Context<E> &ctx, InputSection<E> &isec,
}
}
template <typename E>
void create_range_extension_thunks(Context<E> &ctx, OutputSection<E> &osec) {
std::span<InputSection<E> *> m = osec.members;
template <>
void OutputSection<E>::create_range_extension_thunks(Context<E> &ctx) {
using Thunk = RangeExtensionThunk<E>;
std::span<InputSection<E> *> m = members;
if (m.empty())
return;
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;
tbb::parallel_for_each(m, [](InputSection<E> *isec) {
isec->offset = -1;
});
// We create thunks from the beginning of the section to the end.
@@ -206,7 +190,7 @@ void create_range_extension_thunks(Context<E> &ctx, OutputSection<E> &osec) {
// 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.
// 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.
@@ -214,22 +198,28 @@ void create_range_extension_thunks(Context<E> &ctx, OutputSection<E> &osec) {
// ................................ <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
// <---> 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;
i64 thunk_idx = 0;
// The smallest thunk index that is reachable from the current batch.
i64 t = 0;
while (b < m.size()) {
// Move D foward as far as we can jump from B to anywhere in a thunk at D.
// Move D foward as far as we can jump from B to a thunk at D.
auto d_thunk_end = [&] {
u64 d_end = align_to(offset, 1 << m[d]->p2align) + m[d]->sh_size;
return align_to(d_end, Thunk::alignment) + max_thunk_size;
};
while (d < m.size() &&
align_to(offset, 1 << m[d]->p2align) + m[d]->sh_size + max_thunk_size <
m[b]->offset + max_distance<E>()) {
(b == d || d_thunk_end() <= m[b]->offset + max_distance())) {
offset = align_to(offset, 1 << m[d]->p2align);
m[d]->offset = offset;
offset += m[d]->sh_size;
@@ -240,34 +230,33 @@ void create_range_extension_thunks(Context<E> &ctx, OutputSection<E> &osec) {
// 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<E>)
while (c < d && m[c]->offset + m[c]->sh_size < m[b]->offset + batch_size)
c++;
// Move A forward so that A is reachable from C.
i64 c_offset = (c == m.size()) ? offset : m[c]->offset;
while (a < m.size() && m[a]->offset + max_distance<E>() < c_offset)
while (a < b && m[a]->offset + max_distance() < c_offset)
a++;
// Erase references to out-of-range thunks.
while (thunk_idx < osec.thunks.size() &&
osec.thunks[thunk_idx]->offset < m[a]->offset)
reset_thunk(*osec.thunks[thunk_idx++]);
while (t < thunks.size() && thunks[t]->offset < m[a]->offset)
reset_thunk(*thunks[t++]);
// Create a thunk for input sections between B and C and place it at D.
offset = align_to(offset, RangeExtensionThunk<E>::alignment);
RangeExtensionThunk<E> *thunk =
new RangeExtensionThunk<E>(osec, osec.thunks.size(), offset);
osec.thunks.emplace_back(thunk);
// Create a new thunk and place it at D.
offset = align_to(offset, Thunk::alignment);
i64 thunk_idx = thunks.size();
Thunk *thunk = new Thunk(*this, offset);
thunks.emplace_back(thunk);
// Scan relocations between B and C to collect symbols that need thunks.
// 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);
scan_rels(ctx, *isec, *thunk, thunk_idx);
});
// Now that we know the number of symbols in the thunk, we can compute
// its size.
// the thunk's size.
assert(thunk->size() < max_thunk_size);
offset += thunk->size();
@@ -280,7 +269,7 @@ void create_range_extension_thunks(Context<E> &ctx, OutputSection<E> &osec) {
// 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->thunk_idx;
sym.extra.thunk_idx = thunk_idx;
sym.extra.thunk_sym_idx = i;
}
@@ -292,7 +281,7 @@ void create_range_extension_thunks(Context<E> &ctx, OutputSection<E> &osec) {
std::span<RangeExtensionRef> range_extn = isec->extra.range_extn;
for (i64 i = 0; i < rels.size(); i++)
if (range_extn[i].thunk_idx == thunk->thunk_idx)
if (range_extn[i].thunk_idx == thunk_idx)
range_extn[i].sym_idx = syms[rels[i].r_sym]->extra.thunk_sym_idx;
});
@@ -300,18 +289,16 @@ void create_range_extension_thunks(Context<E> &ctx, OutputSection<E> &osec) {
b = c;
}
while (thunk_idx < osec.thunks.size())
reset_thunk(*osec.thunks[thunk_idx++]);
while (t < thunks.size())
reset_thunk(*thunks[t++]);
osec.shdr.sh_size = offset;
this->shdr.sh_size = offset;
for (InputSection<E> *isec : members)
this->shdr.sh_addralign =
std::max<u32>(this->shdr.sh_addralign, 1 << isec->p2align);
}
using E = MOLD_TARGET;
static_assert(max_thunk_size / E::thunk_size < INT16_MAX);
template void create_range_extension_thunks(Context<E> &, OutputSection<E> &);
} // namespace mold::elf
#endif
+29 -28
View File
@@ -19,10 +19,10 @@
// 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 "TLS block". After allocating and
// initializing a TLS block, the runtime sets a register to refer to the TLS
// block, so that the thread-local variables are accessible relative to the
// register.
// 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
@@ -37,8 +37,8 @@
// 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 TLS block. Therefore,
// 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>`.
@@ -50,9 +50,9 @@
// runtime knows the exact TP-relative address.
//
// We can solve the problem with an indirection. Specifically, for
// each TLV whose TP-relative address is only known at process startup
// 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
// also emit a dynamic relocation to let the runtime to fill the GOT
// 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
@@ -60,10 +60,10 @@
// 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 has to be allocated separately from
// the initial TLS block, so we now 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 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
@@ -160,19 +160,20 @@ u64 get_tp_addr(Context<E> &ctx) {
// 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> || is_mips<E>) {
// On PPC and m68k, TP is 0x7000 (28 KiB) past the beginning of the TLV
// block to maximize the addressable range for 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 TLV block, and the runtime wants to access them efficiently too.
} 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 just uses the beginning of the main executable's TLV block as
// TP. RISC-V 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>);
// 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;
}
}
@@ -185,12 +186,12 @@ u64 get_dtp_addr(Context<E> &ctx) {
if (!phdr)
return 0;
if constexpr (is_ppc<E> || is_m68k<E> || is_mips<E>) {
// On PPC64 and m68k, R_DTPOFF is resolved to the address 0x8000
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 for 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
// 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>) {
+13 -9
View File
@@ -12,39 +12,43 @@ set -x
case "$ID-$VERSION_ID" in
ubuntu-20.* | pop-20.*)
apt-get update
apt-get install -y cmake libssl-dev zlib1g-dev gcc g++ g++-10
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 libssl-dev zlib1g-dev gcc g++
apt-get install -y cmake gcc g++
apt-get install -y file
;;
fedora-*)
dnf install -y gcc-g++ cmake openssl-devel zlib-devel
dnf install -y gcc-g++ cmake
dnf install -y glibc-static file libstdc++-static diffutils util-linux
;;
opensuse-leap-*)
zypper install -y make cmake zlib-devel libopenssl-devel gcc-c++ gcc11-c++
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 zlib-devel libopenssl-devel gcc-c++
zypper install -y make cmake gcc-c++
zypper install -y glibc-devel-static tar diffutils util-linux
;;
gentoo-*)
emerge-webrsync
emerge dev-util/cmake sys-libs/zlib
emerge dev-util/cmake
;;
arch-*)
arch-* | archarm-* | artix-*)
pacman -Sy
pacman -S --needed --noconfirm base-devel zlib openssl cmake util-linux
pacman -S --needed --noconfirm base-devel cmake util-linux
;;
void-*)
xbps-install -Sy xbps
xbps-install -Sy bash make cmake openssl-devel zlib-devel gcc
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
+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
+58 -40
View File
@@ -1,73 +1,91 @@
option(MOLD_ENABLE_QEMU_TESTS "Enable tests on non-native targets" OFF)
option(MOLD_ENABLE_QEMU_TESTS_RV32 "Enable tests for RV32" OFF)
option(MOLD_ENABLE_QEMU_TESTS_POWER10 "Enable tests for Power10" OFF)
function(add_target TRIPLE)
set(HOST ${CMAKE_HOST_SYSTEM_PROCESSOR})
set(HOST ${CMAKE_HOST_SYSTEM_PROCESSOR})
if(${HOST} MATCHES "amd64")
set(HOST x86_64)
elseif(${HOST} MATCHES "arm.*")
set(HOST arm)
elseif(${HOST} STREQUAL "ppc64")
set(HOST powerpc64)
endif()
# CMAKE_HOST_SYSTEM_PROCESSOR returns "aarch64" on ARM32 userland
# with ARM64 kernel. Here, we run `cc -dumpmachine` to determine the
# compiler's default target.
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpmachine
RESULT_VARIABLE EXIT_CODE
OUTPUT_VARIABLE ARCH
ERROR_QUIET)
if(NOT EXIT_CODE AND ARCH MATCHES "([^-]+).*")
set(HOST ${CMAKE_MATCH_1})
endif()
if(${TRIPLE} MATCHES "${HOST}-.*")
if(${HOST} MATCHES "amd64")
set(HOST x86_64)
elseif(${HOST} MATCHES "i386")
set(HOST i686)
elseif(${HOST} MATCHES "arm.*")
set(HOST arm)
elseif(${HOST} STREQUAL "powerpc64")
set(HOST ppc64)
elseif(${HOST} STREQUAL "powerpc64le")
set(HOST ppc64le)
endif()
function(add_target ARCH TRIPLE)
if(${ARCH} STREQUAL ${HOST})
set(IS_NATIVE 1)
endif()
if(${TRIPLE} MATCHES "([^-]+)-.")
set(MACHINE ${CMAKE_MATCH_1})
endif()
if(IS_NATIVE OR MOLD_ENABLE_QEMU_TESTS)
file(GLOB ALL_TESTS RELATIVE ${CMAKE_CURRENT_LIST_DIR} CONFIGURE_DEPENDS
"*.sh")
file(GLOB ALL_TESTS RELATIVE ${CMAKE_CURRENT_LIST_DIR} CONFIGURE_DEPENDS "*.sh")
list(FILTER ALL_TESTS EXCLUDE REGEX "_")
file(GLOB TESTS RELATIVE ${CMAKE_CURRENT_LIST_DIR} CONFIGURE_DEPENDS
"${MACHINE}_*.sh")
file(GLOB TESTS RELATIVE ${CMAKE_CURRENT_LIST_DIR} CONFIGURE_DEPENDS "${ARCH}_*.sh")
list(APPEND TESTS ${ALL_TESTS})
foreach(TEST IN LISTS TESTS)
string(REGEX REPLACE "\\.sh$" "" TESTNAME "${MACHINE}-${TEST}")
string(REGEX REPLACE "\\.sh$" "" TESTNAME "${ARCH}-${TEST}")
add_test(NAME ${TESTNAME}
COMMAND bash -x ${CMAKE_CURRENT_LIST_DIR}/${TEST}
WORKING_DIRECTORY ${mold_BINARY_DIR})
set_property(TEST ${TESTNAME} APPEND PROPERTY ENVIRONMENT "HOST=${HOST}")
if(IS_NATIVE)
set_tests_properties(${TESTNAME} PROPERTIES
SKIP_REGULAR_EXPRESSION "skipped")
set_property(TEST ${TESTNAME} APPEND PROPERTY SKIP_REGULAR_EXPRESSION "skipped")
else()
set_tests_properties(${TESTNAME} PROPERTIES
ENVIRONMENT "TRIPLE=${TRIPLE}")
set_property(TEST ${TESTNAME} APPEND PROPERTY ENVIRONMENT "TRIPLE=${TRIPLE}")
endif()
endforeach()
endif()
endfunction()
add_target(x86_64-linux-gnu)
add_target(i686-linux-gnu)
add_target(aarch64-linux-gnu)
add_target(arm-linux-gnueabihf)
add_target(riscv64-linux-gnu)
add_target(powerpc-linux-gnu)
add_target(powerpc64-linux-gnu)
add_target(powerpc64le-linux-gnu)
add_target(sparc64-linux-gnu)
add_target(s390x-linux-gnu)
add_target(m68k-linux-gnu)
add_target(sh4-linux-gnu)
add_target(alpha-linux-gnu)
add_target(x86_64 x86_64-linux-gnu)
add_target(i686 i686-linux-gnu)
add_target(aarch64 aarch64-linux-gnu)
add_target(arm arm-linux-gnueabihf)
add_target(riscv64 riscv64-linux-gnu)
add_target(ppc powerpc-linux-gnu)
add_target(ppc64 powerpc64-linux-gnu)
add_target(ppc64le powerpc64le-linux-gnu)
add_target(sparc64 sparc64-linux-gnu)
add_target(s390x s390x-linux-gnu)
add_target(sh4 sh4-linux-gnu)
add_target(alpha alpha-linux-gnu)
option(MOLD_ENABLE_QEMU_TESTS_RV32 "Enable tests for RV32" OFF)
if(MOLD_ENABLE_QEMU_TESTS_RV32)
add_target(riscv32-linux-gnu)
add_target(riscv32 riscv32-linux-gnu)
endif()
option(MOLD_ENABLE_QEMU_TESTS_POWER10 "Enable tests for Power10" OFF)
if(MOLD_ENABLE_QEMU_TESTS_POWER10)
add_target(powerpc64le_power10-linux-gnu)
add_target(ppc64le powerpc64le_power10-linux-gnu)
endif()
option(MOLD_ENABLE_QEMU_TESTS_M68K "Enable tests for m68k" OFF)
if(MOLD_ENABLE_QEMU_TESTS_M68K)
add_target(m68k m68k-linux-gnu)
endif()
option(MOLD_ENABLE_QEMU_TESTS_LOONGARCH "Enable tests for LoongArch" OFF)
if(MOLD_ENABLE_QEMU_TESTS_LOONGARCH OR ${HOST} STREQUAL loongarch64)
add_target(loongarch64 loongarch64-linux-gnu)
endif()
+23
View File
@@ -0,0 +1,23 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = aarch64 ] || skip
cat <<EOF | $CC -c -o $t/a.o -fPIC -xassembler - 2> /dev/null || skip
.global foo
.type foo, %function
.variant_pcs foo
foo:
ret
EOF
$CC -B. -shared -o $t/b.so $t/a.o
readelf -W --dyn-syms $t/b.so | grep foo | grep -q '[VARIANT_PCS]'
cat <<EOF | $CC -c -o $t/c.o -xc -
void foo();
int main() { foo(); }
EOF
$CC -B. -o $t/exe $t/c.o $t/b.so
readelf -W --dynamic $t/exe | grep -q AARCH64_VARIANT_PCS
+1 -2
View File
@@ -6,8 +6,7 @@
[ $MACHINE = ppc64le ] && skip
[ $MACHINE = s390x ] && skip
[ $MACHINE = alpha ] && skip
[ $MACHINE = mips64el ] && skip
[ $MACHINE = mips64 ] && skip
[[ $MACHINE = loongarch* ]] && skip
cat <<EOF | $CC -fPIC -c -o $t/a.o -xassembler -
.globl foo
@@ -22,6 +22,5 @@ $CC -B. -o $t/exe $t/a.o \
$OBJDUMP -dr $t/exe | grep -F -A7 '<fn1$thunk>:' > $t/log
grep -Eq 'mov\s+ip, pc' $t/log
grep -Eq 'bx\s+ip' $t/log
grep -Eq 'add\s+ip, ip, pc' $t/log
grep -Eq 'bx\s+pc' $t/log
grep -Eq 'add\s+pc, ip, pc' $t/log
+1
View File
@@ -2,6 +2,7 @@
. $(dirname $0)/common.inc
[ $MACHINE = arm ] || skip
is_musl && skip
echo 'int main() {}' | $GCC -c -o /dev/null -xc - -O0 -mthumb >& /dev/null \
|| skip
+4 -7
View File
@@ -15,10 +15,7 @@ int fn2();
int main() { fn2(); }
EOF
$CC -B. -o $t/exe1 $t/a.o -L$t -Wl,--as-needed -lbar -Wl,--allow-shlib-undefined
readelf -W --dynamic $t/exe1 > $t/log1
! grep -q libfoo $t/log1 || false
$CC -B. -o $t/exe2 $t/a.o -L$t -Wl,--as-needed -lbar -lfoo
readelf -W --dynamic $t/exe2 > $t/log2
grep -q libfoo $t/log2
$CC -B. -o $t/exe $t/a.o -L$t -Wl,--as-needed -lbar -lfoo
readelf -W --dynamic $t/exe > $t/log2
grep -q libbar $t/log2
! grep -q libfoo $t/log2 || false
+33
View File
@@ -0,0 +1,33 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -c -fPIC -o $t/a.o -xc -
int foo() {
return 0;
}
EOF
cat <<EOF | $CC -c -fPIC -o $t/b.o -xc -
__attribute__((weak)) int foo();
int bar() {
if (foo) return foo();
return 0;
}
EOF
cat <<EOF | $CC -xc -c -o $t/c.o -
int bar();
int main() {
return bar();
}
EOF
$CC -B. -shared -o $t/libfoo.so $t/a.o
$CC -B. -shared -o $t/libbar.so $t/b.o
$CC -B. -o $t/exe $t/c.o -L$t -Wl,--as-needed -lfoo -lbar
readelf --dynamic $t/exe > $t/log
! grep libfoo.so $t/log || false
grep -q libbar.so $t/log
-32
View File
@@ -1,32 +0,0 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -shared -fPIC -o $t/libfoo.so -Wl,--soname,libfoo.so -xc -
int foo() { return 3; }
EOF
cat <<EOF | $CC -shared -fPIC -o $t/libbar.so -Wl,--soname,libbar.so -xc -
int bar() { return 3; }
EOF
cat <<EOF | $CC -fPIC -c -o $t/a.o -xc -
int foo();
int baz() { return foo(); }
EOF
$CC -B. -shared -o $t/libbaz.so -Wl,--soname,libbaz.so -L$t $t/a.o -lfoo
cat <<EOF | $CC -c -o $t/b.o -xc -
#include <stdio.h>
int baz();
int main() {
printf("%d\n", baz());
}
EOF
$CC -B. -o $t/exe $t/b.o -L$t -Wl,--as-needed -lbaz -lbar -lfoo
readelf --dynamic $t/exe > $t/log
grep -q libbaz $t/log || false
! grep -q libbar $t/log || false
grep -q libfoo $t/log || false
+28 -2
View File
@@ -14,10 +14,12 @@ canonical_name() {
esac
}
HOST=$(canonical_name $(uname -m))
if [ -z "$HOST" ]; then
HOST=$(canonical_name $(uname -m))
fi
# Set tool names
if [ "$TRIPLE" = "" ]; then
if [ -z "$TRIPLE" ]; then
MACHINE=$HOST
TESTDIR=out/test/elf/$HOST
CC="${TEST_CC:-cc}"
@@ -53,6 +55,19 @@ else
QEMU="qemu-$MACHINE -L /usr/$TRIPLE"
fi
if [ $MACHINE = x86_64 -o $MACHINE = i386 -o $MACHINE = arm ]; then
tlsdesc_opt=-mtls-dialect=gnu2
elif [ $MACHINE = aarch64 ]; then
tlsdesc_opt=-mtls-dialect=desc
fi
if [ $MACHINE = ppc64le -a -e /proc/cpuinfo ] && grep -q POWER10 /proc/cpuinfo; then
CC="$CC -mcpu=power10"
CXX="$CXX -mcpu=power10"
GCC="$GCC -mcpu=power10"
GXX="$GXX -mcpu=power10"
fi
# Common functions
test_cflags() {
echo 'int main() {}' | $CC "$@" -o /dev/null -xc - >& /dev/null
@@ -63,6 +78,17 @@ supports_ifunc() {
$CC -c -o /dev/null -xc - >& /dev/null
}
is_musl() {
ldd --help 2>&1 | grep -q musl
}
supports_tlsdesc() {
# musl's tlsdesc on arm32 seems to be broken
[ $MACHINE = arm ] && is_musl && return 1
[ -n "$tlsdesc_opt" ]
}
skip() {
echo skipped
trap - EXIT
+1
View File
@@ -4,6 +4,7 @@
[ $MACHINE = ppc64 ] && skip
[ $MACHINE = ppc64le ] && skip
[ $MACHINE = alpha ] && skip
[[ $MACHINE = loongarch* ]] && skip
cat <<EOF | $CC -fPIC -shared -o $t/a.so -xc -
__attribute__((aligned(32))) int foo = 5;
+1
View File
@@ -4,6 +4,7 @@
[ $MACHINE = ppc64 ] && skip
[ $MACHINE = ppc64le ] && skip
[ $MACHINE = alpha ] && skip
[[ $MACHINE = loongarch* ]] && skip
cat <<EOF | $CC -o $t/a.o -c -xc -fno-PIE -
extern int foo;
+41
View File
@@ -0,0 +1,41 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc -fno-PIE -
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
extern char readonly[100];
extern char readwrite[100];
static int segv = 0;
static jmp_buf buf;
void handler(int sig) {
segv = 1;
longjmp(buf, 1);
}
int main() {
signal(SIGSEGV, handler);
readwrite[0] = 5;
int x = segv;
if (setjmp(buf) == 0)
*(char *)readonly = 5;
int y = segv;
printf("sigsegv %d %d\n", x, y);
}
EOF
cat <<EOF | $CC -fPIC -shared -o $t/b.so -xc -
__attribute__((section (".data.rel.ro"))) char readonly[100] = "abc";
char readwrite[100] = "abc";
EOF
$CC -B. $t/a.o $t/b.so -o $t/exe -no-pie
$QEMU $t/exe | grep -q '^sigsegv 0 1$'
+2 -2
View File
@@ -6,8 +6,8 @@ void foo() {}
EOF
$CC -B. -o $t/b.so -shared $t/a.o -Wl,-default-symver
readelf --dyn-syms $t/b.so | grep -q ' foo@@b\.so$'
readelf --dyn-syms $t/b.so | grep -q ' foo@@b\.so'
$CC -B. -o $t/b.so -shared $t/a.o \
-Wl,--soname=bar -Wl,-default-symver
readelf --dyn-syms $t/b.so | grep -q ' foo@@bar$'
readelf --dyn-syms $t/b.so | grep -q ' foo@@bar'
+2 -1
View File
@@ -2,10 +2,11 @@
. $(dirname $0)/common.inc
[ $MACHINE = riscv64 -o $MACHINE = riscv32 ] && skip
[[ $MACHINE = loongarch* ]] && skip
# musl libc does not support init/fini on ARM
# https://github.com/rui314/mold/issues/951
[ $MACHINE = arm -o $MACHINE = aarch64 ] && ldd --help 2>&1 | grep -q musl && skip
[ $MACHINE = arm -o $MACHINE = aarch64 ] && is_musl && skip
cat <<EOF | $CC -c -fPIC -o $t/a.o -xc -
void keep();
+21
View File
@@ -0,0 +1,21 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc -
void foo() {}
EOF
rm -f $t/b.a
ar rcs $t/b.a $t/a.o
cat <<EOF | $CC -o $t/c.o -c -xc -
void foo();
int main() { foo(); }
EOF
$CC -B. -o $t/exe $t/c.o $t/b.a $t/b.a
! $CC -B. -o $t/exe $t/c.o -Wl,--push-state,--whole-archive \
$t/b.a $t/b.a -Wl,--pop-state 2> $t/log || false
grep -q 'duplicate symbol:.* foo' $t/log
+8 -8
View File
@@ -10,8 +10,8 @@ EOF
$CC -B. -o $t/exe $t/a.o
readelf --dyn-syms $t/exe > $t/log
! grep -q ' foo$' $t/log || false
! grep -q ' bar$' $t/log || false
! grep -q ' foo' $t/log || false
! grep -q ' bar' $t/log || false
cat <<EOF > $t/dyn
{ foo; bar; };
@@ -20,17 +20,17 @@ EOF
$CC -B. -o $t/exe1 $t/a.o -Wl,-dynamic-list=$t/dyn
readelf --dyn-syms $t/exe1 > $t/log1
grep -q ' foo$' $t/log1
grep -q ' bar$' $t/log1
grep -q ' foo' $t/log1
grep -q ' bar' $t/log1
$CC -B. -o $t/exe2 $t/a.o -Wl,--export-dynamic-symbol-list=$t/dyn
readelf --dyn-syms $t/exe2 > $t/log2
grep -q ' foo$' $t/log2
grep -q ' bar$' $t/log2
grep -q ' foo' $t/log2
grep -q ' bar' $t/log2
$CC -B. -o $t/exe3 $t/a.o -Wl,--export-dynamic-symbol=foo,--export-dynamic-symbol=bar
readelf --dyn-syms $t/exe3 > $t/log3
grep -q ' foo$' $t/log3
grep -q ' bar$' $t/log3
grep -q ' foo' $t/log3
grep -q ' bar' $t/log3
+8 -8
View File
@@ -14,8 +14,8 @@ EOF
$CXX -B. -o $t/exe $t/a.o $t/b.o
readelf --dyn-syms $t/exe > $t/log
! grep -q ' foo$' $t/log || false
! grep -q ' bar$' $t/log || false
! grep -q ' foo' $t/log || false
! grep -q ' bar' $t/log || false
cat <<EOF > $t/dyn
{ foo; extern "C++" { "baz(int)"; }; };
@@ -24,13 +24,13 @@ EOF
$CC -B. -o $t/exe1 $t/a.o $t/b.o -Wl,-dynamic-list=$t/dyn
readelf --dyn-syms $t/exe1 > $t/log1
grep -q ' foo$' $t/log1
! grep -q ' bar$' $t/log1 || false
grep -q ' _Z3bazi$' $t/log1
grep -q ' foo' $t/log1
! grep -q ' bar' $t/log1 || false
grep -q ' _Z3bazi' $t/log1
$CC -B. -o $t/exe2 $t/a.o $t/b.o -Wl,--export-dynamic-symbol-list=$t/dyn
readelf --dyn-syms $t/exe2 > $t/log2
grep -q ' foo$' $t/log2
! grep -q ' bar$' $t/log2 || false
grep -q ' _Z3bazi$' $t/log2
grep -q ' foo' $t/log2
! grep -q ' bar' $t/log2 || false
grep -q ' _Z3bazi' $t/log2
+12 -12
View File
@@ -21,19 +21,19 @@ EOF
$CC -B. -Wl,--dynamic-list=$t/dyn -o $t/exe1 $t/b.o
readelf --dyn-syms $t/exe1 > $t/log1
grep -q ' xyz$' $t/log1
! grep -q ' foobarzx$' $t/log1 || false
grep -q ' foobarcx$' $t/log1
grep -q ' foo123bar456bx$' $t/log1
! grep -q ' foo123bar456c$' $t/log1 || false
! grep -q ' foo123bar456x$' $t/log1 || false
grep -q ' xyz' $t/log1
! grep -q ' foobarzx' $t/log1 || false
grep -q ' foobarcx' $t/log1
grep -q ' foo123bar456bx' $t/log1
! grep -q ' foo123bar456c' $t/log1 || false
! grep -q ' foo123bar456x' $t/log1 || false
$CC -B. -Wl,--export-dynamic-symbol-list=$t/dyn -o $t/exe2 $t/b.o
readelf --dyn-syms $t/exe2 > $t/log2
grep -q ' xyz$' $t/log2
! grep -q ' foobarzx$' $t/log2 || false
grep -q ' foobarcx$' $t/log2
grep -q ' foo123bar456bx$' $t/log2
! grep -q ' foo123bar456c$' $t/log2 || false
! grep -q ' foo123bar456x$' $t/log2 || false
grep -q ' xyz' $t/log2
! grep -q ' foobarzx' $t/log2 || false
grep -q ' foobarcx' $t/log2
grep -q ' foo123bar456bx' $t/log2
! grep -q ' foo123bar456c' $t/log2 || false
! grep -q ' foo123bar456x' $t/log2 || false
+2 -2
View File
@@ -11,5 +11,5 @@ EOF
$CC -B. -shared -o $t/b.so $t/a.o
readelf --dyn-syms $t/b.so | grep -q 'bar1$'
readelf --dyn-syms $t/b.so | grep -q 'bar2$'
readelf --dyn-syms $t/b.so | grep -q ' bar1'
readelf --dyn-syms $t/b.so | grep -q ' bar2'
+3 -3
View File
@@ -26,7 +26,7 @@ $CXX -c -o $t/c.o $t/a.cc -fno-PIC
$CXX -B. -o $t/exe1 $t/b.o $static
$QEMU $t/exe1
$CXX -B. -o $t/exe2 $t/c.o $static
$CXX -B. -o $t/exe2 $t/c.o -no-pie $static
$QEMU $t/exe2
$CXX -B. -o $t/exe3 $t/b.o -pie
@@ -38,7 +38,7 @@ $QEMU $t/exe4
$CXX -B. -o $t/exe5 $t/b.o -pie -Wl,--gc-sections
$QEMU $t/exe5
$CXX -B. -o $t/exe6 $t/c.o $static -Wl,--gc-sections
$CXX -B. -o $t/exe6 $t/c.o -no-pie $static -Wl,--gc-sections
$QEMU $t/exe6
if [ $MACHINE = x86_64 ]; then
@@ -54,7 +54,7 @@ fi
if [ $MACHINE = x86_64 -o $MACHINE = aarch64 ]; then
$CXX -c -o $t/e.o $t/a.cc -mcmodel=large -fno-PIC
$CXX -B. -o $t/exe9 $t/e.o $static
$CXX -B. -o $t/exe9 $t/e.o -no-pie $static
$QEMU $t/exe9
$CXX -B. -o $t/exe10 $t/e.o -no-pie
+101
View File
@@ -0,0 +1,101 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = $HOST ] || skip
[ $MACHINE = riscv64 -o $MACHINE = riscv32 -o $MACHINE = sparc64 ] && skip
command -v gdb >& /dev/null || skip
test_cflags -gdwarf-5 -g -gdwarf64 || skip
cat <<EOF > $t/a.c
void fn3();
static void fn2() {
fn3();
}
void fn1() {
fn2();
}
EOF
cat <<EOF > $t/b.c
void fn5();
static void fn4() {
fn5();
}
void fn3() {
fn4();
}
EOF
cat <<EOF > $t/c.c
void fn7();
static void fn6() {
fn7();
}
void fn5() {
fn6();
}
EOF
cat <<EOF > $t/d.c
#include <stdio.h>
void trap() {}
static void fn8() {
printf("Hello world\n");
trap();
}
void fn7() {
fn8();
}
EOF
$CC -c -o $t/a.o $t/a.c -fPIC -g -ggnu-pubnames -gdwarf-5 -gdwarf64 -ffunction-sections
$CC -c -o $t/b.o $t/b.c -fPIC -g -ggnu-pubnames -gdwarf-4 -gdwarf64 -ffunction-sections
$CC -c -o $t/c.o $t/c.c -fPIC -g -ggnu-pubnames -gdwarf-5 -gdwarf64
$CC -c -o $t/d.o $t/d.c -fPIC -g -ggnu-pubnames -gdwarf-5 -gdwarf64 -ffunction-sections
$CC -B. -shared -o $t/e.so $t/a.o $t/b.o $t/c.o $t/d.o
$CC -B. -shared -o $t/f.so $t/a.o $t/b.o $t/c.o $t/d.o -Wl,--gdb-index
readelf -WS $t/f.so 2> /dev/null | grep -Fq .gdb_index
cat <<EOF | $CC -c -o $t/g.o -fPIC -g -ggnu-pubnames -gdwarf-5 -xc - -gz
void fn1();
int main() {
fn1();
}
EOF
# Older versions of gdb are buggy that they complain DWARF64 debug sections
# even without .gdb_index. Skip if such version.
$CC -B. -o $t/exe1 $t/e.so $t/g.o
DEBUGINFOD_URLS= gdb $t/exe1 -nx -batch -ex 'b main' -ex r -ex quit >& $t/log1
grep -q 'DW_FORM_line_strp pointing outside of .debug_line_str' $t/log1 && skip
# We are using a recent version of gdb.
$CC -B. -o $t/exe2 $t/f.so $t/g.o -Wl,--gdb-index
readelf -WS $t/exe2 2> /dev/null | grep -Fq .gdb_index
$QEMU $t/exe2 | grep -q 'Hello world'
DEBUGINFOD_URLS= gdb $t/exe2 -nx -batch -ex 'b main' -ex r -ex 'b trap' \
-ex c -ex bt -ex quit >& $t/log2
grep -q 'fn8 () at .*/d.c:6' $t/log2
grep -q 'fn7 () at .*/d.c:10' $t/log2
grep -q 'fn6 () at .*/c.c:4' $t/log2
grep -q 'fn5 () at .*/c.c:8' $t/log2
grep -q 'fn4 () at .*/b.c:4' $t/log2
grep -q 'fn3 () at .*/b.c:8' $t/log2
grep -q 'fn2 () at .*/a.c:4' $t/log2
grep -q 'fn1 () at .*/a.c:8' $t/log2
+2
View File
@@ -1,6 +1,8 @@
#!/bin/bash
. $(dirname $0)/common.inc
# MIPS ABIs are not compatible with .gnu.hash
cat <<EOF | $CC -o $t/a.o -c -xc -
void foo() {}
EOF
+59
View File
@@ -0,0 +1,59 @@
#!/bin/bash
. $(dirname $0)/common.inc
# GCC 11 or older uses R_390_PLT32DBL to take an address of a function
# instead of R_390_PC32DBL
if [ $MACHINE = s390x ]; then
echo 'void *foo() { return foo; }' | $CC -c -o $t/a.o -xc -
readelf -r $t/a.o | grep -q R_390_PLT32DBL && skip
fi
cat <<EOF | $CC -c -o $t/a.o -ffunction-sections -fdata-sections -xc -
int bar() {
return 5;
}
int foo1(int x) {
return bar() + x;
}
int foo2(int x) {
return bar() + x;
}
int foo3(int x) {
return bar() + x;
}
EOF
cat <<EOF | $CC -c -o $t/b.o -ffunction-sections -fdata-sections -xc -
#include <stdio.h>
int foo1();
int foo2();
int foo3();
int main() {
printf("%d %d\n", foo1 == foo2, foo2 == foo3);
}
EOF
$CC -B. -o $t/exe1 -Wl,-icf=safe $t/a.o $t/b.o
$QEMU $t/exe1 | grep -q '^0 0$'
cat <<EOF | $CC -c -o $t/c.o -ffunction-sections -fdata-sections -xc -
int foo1();
int foo2();
int foo3();
int main() {
foo1();
foo2();
foo3();
}
EOF
$CC -B. -o $t/exe2 -Wl,-icf=safe $t/a.o $t/c.o
$QEMU $t/exe2 > $t/log2
! grep foo2 $t/log2 || false
+33
View File
@@ -0,0 +1,33 @@
#!/bin/bash
. $(dirname $0)/common.inc
supports_ifunc || skip
cat <<EOF | $CC -c -fPIC -o $t/a.o -xc -
typedef void Func();
void foo();
Func *get_foo() { return foo; }
EOF
$CC -B. -shared -o $t/b.so $t/a.o
cat <<EOF | $CC -c -fno-PIE -o $t/c.o -xc -
#include <stdio.h>
typedef void Func();
__attribute__((ifunc("resolve_foo"))) void foo(void);
void real_foo(void) { printf("foo "); }
Func *resolve_foo() { return real_foo; }
Func *get_foo();
int main() {
printf("%p %p\n", foo, get_foo());
foo();
printf("\n");
}
EOF
$CC -B. -o $t/exe1 $t/c.o $t/b.so -no-pie
$QEMU $t/exe1 | grep -Eq '^(\S+) \1'
+52
View File
@@ -0,0 +1,52 @@
#!/bin/bash
. $(dirname $0)/common.inc
supports_ifunc || skip
cat <<EOF | $CC -c -fno-PIE -o $t/a.o -xc -
#include <stdio.h>
typedef void Func();
__attribute__((ifunc("resolve_foo"))) void foo(void);
void real_foo(void) { printf("foo "); }
Func *resolve_foo() { return real_foo; }
__attribute__((ifunc("resolve_bar"))) void bar(void);
void real_bar(void) { printf("bar "); }
Func *resolve_bar() { return real_bar; }
EOF
cat <<EOF | $CC -c -fPIC -o $t/b.o -xc -
typedef void Func();
void foo();
void bar();
Func *get_foo() { return foo; }
Func *get_bar() { return bar; }
EOF
cat <<EOF | $CC -c -fno-PIE -o $t/c.o -xc -
#include <stdio.h>
typedef void Func();
void foo();
void bar();
Func *get_foo();
Func *get_bar();
int main() {
printf("%p %p %p %p\n", foo, get_foo(), bar, get_bar());
foo();
bar();
printf("\n");
}
EOF
$CC -B. -o $t/exe1 $t/a.o $t/b.o $t/c.o -no-pie
$QEMU $t/exe1 | grep -Eq '^(\S+) \1 (\S+) \2'
readelf --dynamic $t/exe1 > $t/log1
! grep -q TEXTREL $t/log1 || false
+1 -1
View File
@@ -2,7 +2,7 @@
. $(dirname $0)/common.inc
# musl does not support GNU-style init/fini priorities
ldd --help 2>&1 | grep -q musl && skip
is_musl && skip
cat <<'EOF' | $CC -c -o $t/a.o -xc -
#include <stdio.h>
+11
View File
@@ -0,0 +1,11 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc - -mcmodel=extreme
#include <stdio.h>
char msg[] = "Hello world\n";
int main() { printf(msg); }
EOF
$CC -B. -o $t/exe1 $t/a.o
$QEMU $t/exe1 | grep -q 'Hello world'
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -flto -c -o $t/a.o -xc -
void _start() {}
EOF
$CC -B. -o $t/exe -flto $t/a.o -nostdlib
readelf -Ws $t/exe | grep -Eq ' _start'
+19
View File
@@ -0,0 +1,19 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc - -fPIC
#include <stdio.h>
int main() {
printf("Hello world\n");
}
EOF
$CC -B. -shared -o $t/b.so $t/a.o
$CC -o $t/c.o -c -xc /dev/null -fPIC
$CC -B. -o $t/exe1 $t/c.o $t/b.so -pie
$QEMU $t/exe1 | grep -q 'Hello world'
$CC -o $t/c.o -c -xc /dev/null -fno-PIC
$CC -B. -o $t/exe2 $t/c.o $t/b.so -no-pie
$QEMU $t/exe2 | grep -q 'Hello world'
+11 -4
View File
@@ -4,9 +4,16 @@
# OneTBB isn't tsan-clean
nm mold | grep -q '__tsan_init' && skip
seq 1 100000 | sed 's/.*/.section .data.\0,"aw"\n.globl x\0\nx\0: .word 0\n/g' | \
$CC -c -xassembler -o $t/a.o -
echo 'foo = 0x1000' > $t/a.s
seq 1 100000 | sed 's/.*/.section .data.\0,"aw"\n.globl x\0\nx\0: .word 0\n/g' >> $t/a.s
$CC -c -xassembler -o $t/a.o $t/a.s
./mold --relocatable -o $t/b.o $t/a.o
readelf -WS $t/b.o | grep -Fq .data.100000
readelf -Ws $t/b.o | grep -Fq 'GLOBAL DEFAULT 100000'
readelf -WS $t/b.o > $t/log1
grep -Fq .data.100000 $t/log1
readelf -Ws $t/b.o > $t/log2
grep -Fq 'GLOBAL DEFAULT 100000' $t/log2
grep -Fq 'ABS foo' $t/log2
! grep -Fq 'ABS x68966' $t/log2 || false
+1
View File
@@ -8,6 +8,7 @@
[ $MACHINE = ppc64le ] && skip
[ $MACHINE = sh4 ] && skip
[ $MACHINE = alpha ] && skip
[[ $MACHINE = loongarch* ]] && skip
cat <<EOF | $CC -shared -o $t/a.so -xc -
int foo = 3;
+3
View File
@@ -3,6 +3,9 @@
[ $MACHINE = ppc64 ] && skip
# BusyBox's grep can't handle capture groups (e.g. \1, \2 ...)
grep --version 2>&1 | grep BusyBox && skip
cat <<EOF | $CC -o $t/a.o -c -xc -
#include <stdio.h>
+95
View File
@@ -0,0 +1,95 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc - -ffunction-sections
#include <stdio.h>
void f0();
void f1();
void f2();
void f3();
void f4();
void f5();
void f6();
void f7();
void f8();
void f9();
void f10();
void f11();
void f12();
void f13();
void f14();
void f15();
void f16();
void f17();
void f18();
void f19();
void f0(int x) { printf("0 "); if (!x) f9(); }
void space0() { __asm__(".space 1024*1024"); }
void f1(int x) { printf("1 "); f8(x); }
void space1() { __asm__(".space 1024*1024"); }
void f2(int x) { printf("2 "); f7(x); }
void space2() { __asm__(".space 1024*1024"); }
void f3(int x) { printf("3 "); f6(x); }
void space3() { __asm__(".space 1024*1024"); }
void f4(int x) { printf("4 "); f5(x); }
void space4() { __asm__(".space 1024*1024"); }
void f5(int x) { printf("5 "); f10(x); }
void space5() { __asm__(".space 1024*1024"); }
void f6(int x) { printf("6 "); f4(x); }
void space6() { __asm__(".space 1024*1024"); }
void f7(int x) { printf("7 "); f3(x); }
void space7() { __asm__(".space 1024*1024"); }
void f8(int x) { printf("8 "); f2(x); }
void space8() { __asm__(".space 1024*1024"); }
void f9(int x) { printf("9 "); f1(x); }
void space9() { __asm__(".space 1024*1024"); }
void f10(int x) { printf("10 "); f19(x); }
void space10() { __asm__(".space 8*1024*1024"); }
void f11(int x) { printf("11 "); f18(x); }
void space11() { __asm__(".space 8*1024*1024"); }
void f12(int x) { printf("12 "); f17(x); }
void space12() { __asm__(".space 8*1024*1024"); }
void f13(int x) { printf("13 "); f16(x); }
void space13() { __asm__(".space 8*1024*1024"); }
void f14(int x) { printf("14 "); f15(x); }
void space14() { __asm__(".space 8*1024*1024"); }
void f15(int x) { printf("15 "); f0(x + 1); }
void space15() { __asm__(".space 8*1024*1024"); }
void f16(int x) { printf("16 "); f14(x); }
void space16() { __asm__(".space 8*1024*1024"); }
void f17(int x) { printf("17 "); f13(x); }
void space17() { __asm__(".space 8*1024*1024"); }
void f18(int x) { printf("18 "); f12(x); }
void space18() { __asm__(".space 8*1024*1024"); }
void f19(int x) { printf("19 "); f11(x); }
void space19() { __asm__(".space 8*1024*1024"); }
int main() {
f0(0);
printf("\n");
}
EOF
$CC -B. -o $t/exe $t/a.o
$QEMU $t/exe | grep -Eq '^0 9 1 8 2 7 3 6 4 5 10 19 11 18 12 17 13 16 14 15 0 $'
+46
View File
@@ -0,0 +1,46 @@
#!/bin/bash
. $(dirname $0)/common.inc
# OneTBB isn't tsan-clean
nm mold | grep -q '__tsan_init' && skip
# Ubuntu 22.04 SH4 GCC is broken
[ $MACHINE = sh4 ] && skip
cat <<EOF | $CXX -c -o $t/a.o -xc++ -
void hello();
void world();
template <typename T>
struct Foo {
Foo() { hello(); }
};
template <typename T>
struct Bar {
Bar() { world(); }
};
void baz() {
Foo<int> foo;
Bar<int> bar;
}
EOF
cat <<EOF | $CXX -c -o $t/b.o -xc++ -
#include <iostream>
void hello() { std::cout << "Hello "; }
void world() { std::cout << "world\n"; }
void baz();
int main() {
baz();
}
EOF
./mold --relocatable -o $t/c.o $t/a.o
./mold --relocatable -o $t/d.o $t/b.o
$CXX -B. -o $t/exe $t/c.o $t/d.o
$QEMU $t/exe | grep -q 'Hello world'
+20
View File
@@ -0,0 +1,20 @@
#!/bin/bash
. $(dirname $0)/common.inc
# OneTBB isn't tsan-clean
nm mold | grep -q '__tsan_init' && skip
cat <<EOF | $CC -c -o $t/a.o -xassembler -
.section .rodata.str1.1,"aMS",@progbits,1
val1:
.ascii "Hello \0"
.section .rodata.str1.1,"aMS",@progbits,1
val5:
.ascii "World \0"
EOF
./mold --relocatable -o $t/b.o $t/a.o
readelf -W -p .rodata.str1.1 $t/b.o | grep -Eq '\b0\b.*Hello'
readelf -W -p .rodata.str1.1 $t/b.o | grep -Eq '\b7\b.*World'
+1 -1
View File
@@ -17,4 +17,4 @@ EOF
echo "'$t/b.o' '$t/c.o'" > $t/rsp
$CC -o $t/exe $t/a.o -Wl,@$t/rsp
$CC -B. -o $t/exe $t/a.o -Wl,@$t/rsp
+16
View File
@@ -0,0 +1,16 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -c -o $t/a.o -xc -
void foo();
int main() { foo(); }
EOF
cat <<EOF | $CC -c -o $t/b.o -xc -
void foo() {}
EOF
echo "'$t/b.o'" > $t/rsp1
echo "@$t/rsp1" > $t/rsp2
$CC -B. -o $t/exe $t/a.o -Wl,@$t/rsp2
+23
View File
@@ -0,0 +1,23 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -march=rv64imafd_xtheadba1p5 -o $t/a.o -c -xc -
void foo() {}
EOF
# The compiler might not create .riscv.attributes
readelf --sections $t/a.o | grep -Fq .riscv.attributes || skip
cat <<EOF | $CC -march=rv64imafd_xtheadba2p0 -o $t/b.o -c -xc -
void bar() {}
EOF
cat <<EOF | $CC -o $t/c.o -c -xc -
void _start() {}
EOF
$CC -B. -nostdlib -o $t/exe $t/a.o $t/c.o
readelf -A $t/exe | grep -q '_xtheadba1p5"'
$CC -B. -nostdlib -o $t/exe $t/a.o $t/b.o $t/c.o
readelf -A $t/exe | grep -q '_xtheadba2p0"'
+14
View File
@@ -0,0 +1,14 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc -
void _start() {}
EOF
$STRIP --remove-section=.riscv.attributes $t/a.o
$CC -B. -nostdlib -o $t/exe $t/a.o
readelf -W --segments --sections $t/exe > $t/log
! grep -F .riscv.attributes $t/log || false
! grep -F RISCV_ATTR $t/log || false
+50
View File
@@ -0,0 +1,50 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = riscv64 -o $MACHINE = riscv32 ] || skip
cat <<EOF | $CC -o $t/a.o -c -xassembler -
.globl get_foo, get_foo2, get_bar, get_baz
get_foo:
lui a0, %hi(foo)
add a0, a0, %lo(foo)
ret
get_foo2:
lui a0, %hi(foo+0x10000000)
add a0, a0, %lo(foo)
ret
get_bar:
lui a0, %hi(bar)
add a0, a0, %lo(bar)
ret
get_baz:
lui a0, %hi(baz)
add a0, a0, %lo(baz)
ret
EOF
cat <<EOF | $CC -o $t/b.o -c -xassembler -
.globl foo, bar, baz
foo = 0xf00
bar = 0xba
baz = 0x11beef
EOF
cat <<EOF | $CC -o $t/c.o -c -xc -
#include <stdio.h>
int get_foo();
int get_foo2();
int get_bar();
int get_baz();
int main() {
printf("%x %x %x %x\n", get_foo(), get_foo2(), get_bar(), get_baz());
}
EOF
$CC -B. -o $t/exe1 $t/a.o $t/b.o $t/c.o -Wl,--no-relax
$QEMU $t/exe1 | grep -q 'f00 10000f00 ba 11beef'
$CC -B. -o $t/exe2 $t/a.o $t/b.o $t/c.o
$QEMU $t/exe2 | grep -q 'f00 10000f00 ba 11beef'
+2
View File
@@ -1,6 +1,8 @@
#!/bin/bash
. $(dirname $0)/common.inc
test_cflags -static || skip
cat <<EOF | $CC -c -o $t/a.o -xassembler -
.globl foo
.weak bar
-50
View File
@@ -1,50 +0,0 @@
#!/bin/bash
. $(dirname $0)/common.inc
# ARM assembler has a differnet grammar than the others.
# Concretely speaking, ARM as uses "@" as a start of a comment.
[ $MACHINE = arm ] && skip
cat <<'EOF' | $CC -c -o $t/a.o -x assembler -
.globl val1, val2, val3, val4, val5
.section .rodata.str1.1,"aMS",@progbits,1
val1:
.ascii "Hello \0"
.section .rodata.str4.4,"aMS",@progbits,4
.align 4
val2:
.ascii "world \0\0\0\0"
.section .rodata.foo,"aMS",@progbits,1
val3:
.ascii "foobar\0"
.section .rodata.cst8,"aM",@progbits,8
.align 8
val4:
.ascii "abcdefgh"
.section .rodatabar,"aMS",@progbits,1
val5:
.ascii "bar\0"
EOF
cat <<'EOF' | $CC -c -o $t/b.o -xc -
#include <stdio.h>
extern char val1, val2, val3, val4, val5;
int main() {
printf("%p %p %p %p %p\n", &val1, &val2, &val3, &val4, &val5);
}
EOF
$CC -B. -o $t/exe $t/a.o $t/b.o
readelf -p .rodata.str $t/exe | grep -q Hello
readelf -p .rodata.str $t/exe | grep -q world
readelf -p .rodata.str $t/exe | grep -q foobar
readelf -p .rodata.cst $t/exe | grep -q abcdefgh
readelf -p .rodatabar $t/exe | grep -q bar
+1 -1
View File
@@ -4,7 +4,7 @@
[ "$CC" = cc ] || skip
# ASAN doesn't work with LD_PRELOAD
nm mold-wrapper.so | grep -q '__[at]san_init' && skip
nm mold | grep -q '__[at]san_init' && skip
cat <<'EOF' | $CC -xc -c -o $t/a.o -
#include <stdio.h>
+3
View File
@@ -5,6 +5,9 @@
# but instead refers "function descriptors" in .opd.
[ $MACHINE = ppc64 ] && skip
# The crt*.o compiled with B26 caused far form GOT.
[[ $MACHINE = loongarch* ]] && skip
[ $MACHINE = arm ] && flags=-marm
cat <<EOF | $CC -o $t/a.o -c -xc -fno-PIC $flags -
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
. $(dirname $0)/common.inc
cat <<EOF | $CC -o $t/a.o -c -xc - -g
static void foo() {}
void bar() {}
int main() {}
EOF
$CC -B. -o $t/exe $t/a.o -Wl,--strip-debug
readelf -W --sections $t/exe > $t/log
! grep -Fq .debug_info $t/log || false
readelf -W --symbols $t/exe | grep -q ' bar'
+4 -4
View File
@@ -16,7 +16,7 @@ EOF
$CC -B. -o $t/c.so -shared $t/a.o -Wl,--version-script=$t/b.version
readelf -W --dyn-syms $t/c.so > $t/log
grep -q ' foo@TEST$' $t/log
grep -q ' bar@TEST$' $t/log
grep -q ' bar1$' $t/log
! grep -q ' foo@@TEST$' $t/log || false
grep -q ' foo@TEST' $t/log
grep -q ' bar@TEST' $t/log
grep -q ' bar1' $t/log
! grep -q ' foo@@TEST' $t/log || false
+3 -3
View File
@@ -19,7 +19,7 @@ EOF
$CC -B. -o $t/c.so -shared $t/a.o -Wl,--version-script=$t/b.version
readelf -W --dyn-syms $t/c.so > $t/log
grep -q ' foo@@TEST1$' $t/log
grep -q ' foo@TEST2$' $t/log
grep -q ' foo@TEST3$' $t/log
grep -q ' foo@@TEST1' $t/log
grep -q ' foo@TEST2' $t/log
grep -q ' foo@TEST3' $t/log
! grep -q ' foo$' $t/log || false
+2
View File
@@ -1,6 +1,8 @@
#!/bin/bash
. $(dirname $0)/common.inc
[[ $MACHINE = loongarch* ]] && skip
cat <<EOF | $CC -o $t/a.o -c -xc -
void foo() {}
EOF
+2
View File
@@ -1,6 +1,8 @@
#!/bin/bash
. $(dirname $0)/common.inc
[[ $MACHINE = loongarch* ]] && skip
cat <<EOF | $CC -c -o $t/a.o -xc -
void foo() {}
EOF
Loaded 100 of 946 files, more files were not shown because too many files have changed in this diff. Show more