Compare commits

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

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

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

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

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

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

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

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

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

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

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

R_ARM_GOT_PREL: GOT(S) + A – P

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

which is changed to:

Testing defsym2 ... OK

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Found in Mesa test suite.

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

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

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

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

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

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

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

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

This is what the RISC-V psABI says

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 - 62be1fcdb0
 - a7b8da9345
 - 21a2d6bb6c

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

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

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

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

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

  bar.foo = 0xf000;

sets `bar.foo` to 1 while

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

  bar.foo = 0xf000;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

So, we should not write to argv.

This commit is based on @ishitatsuyuki's patch.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

which make the test stuck.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

No files matched your search

+13
View File
@@ -0,0 +1,13 @@
# These are supported funding model platforms
github: [rui314] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
+143 -22
View File
@@ -4,30 +4,65 @@ on:
branches: [ main ]
pull_request:
branches: [ main ]
env:
UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
MOLD_SUPPRESS_MACHO_WARNING: 1
jobs:
build-clang:
build-clang-make:
strategy:
matrix:
target:
- test
- test-asan
- test-tsan
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: rui314/setup-mold@v1
with:
make-default: false
- name: apt-get
run: sudo dpkg --add-architecture i386 && sudo apt-get update && sudo apt-get install git build-essential libstdc++-10-dev cmake clang libssl-dev zlib1g-dev libxxhash-dev libtbb-dev git bsdmainutils dwarfdump pkg-config
- uses: rui314/setup-mold@staging
- name: install-build-deps
run: sudo apt-get update && sudo ./install-build-deps.sh
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: make
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make CC=clang CXX=clang++ LDFLAGS=-fuse-ld=mold -j$(nproc)
- name: make test
run: make -j$(nproc) test
build-gcc:
run: |
echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
make -j$(nproc) CC=clang CXX=clang++ ${{ matrix.target }}
build-clang-cmake:
strategy:
matrix:
target:
- ''
- '-DMOLD_USE_ASAN=On'
- '-DMOLD_USE_TSAN=On'
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: rui314/setup-mold@staging
- name: install-build-deps
run: sudo apt-get update && sudo ./install-build-deps.sh
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: build and test
run: |
echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
mkdir build
cd build
cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ ${{ matrix.target }} ..
cmake --build . -j$(nproc)
ctest . -j$(nproc)
build-gcc-make:
runs-on: ubuntu-20.04
container: gcc:11.1.0
steps:
- uses: actions/checkout@v2
- name: apt-get
- name: install-build-deps
run: |
dpkg --add-architecture i386 && apt-get update && apt-get install -y sudo libssl-dev zlib1g-dev libxxhash-dev cmake clang bsdmainutils dwarfdump qemu-user gcc-10-i686-linux-gnu gcc-10-aarch64-linux-gnu gcc-10-riscv64-linux-gnu gcc-10-arm-linux-gnueabihf g++-10-i686-linux-gnu g++-10-aarch64-linux-gnu g++-10-riscv64-linux-gnu g++-10-arm-linux-gnueabihf
dpkg --add-architecture i386
apt-get update
./install-build-deps.sh
apt-get install -y sudo qemu-user gcc-10-i686-linux-gnu gcc-10-aarch64-linux-gnu gcc-10-riscv64-linux-gnu gcc-10-arm-linux-gnueabihf g++-10-i686-linux-gnu g++-10-aarch64-linux-gnu g++-10-riscv64-linux-gnu g++-10-arm-linux-gnueabihf
ln -sf /usr/bin/i686-linux-gnu-gcc-10 /usr/bin/i686-linux-gnu-gcc
ln -sf /usr/bin/i686-linux-gnu-g++-10 /usr/bin/i686-linux-gnu-g++
ln -sf /usr/bin/aarch64-linux-gnu-gcc-10 /usr/bin/aarch64-linux-gnu-gcc
@@ -38,19 +73,105 @@ jobs:
ln -sf /usr/bin/riscv64-linux-gnu-g++-10 /usr/bin/riscv64-linux-gnu-g++
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: make
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make CC=gcc CXX=g++ -j$(nproc)
- name: make test
run: make -j$(nproc) test-all
build-macos:
- name: make test-all
run: |
echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
make -j$(nproc) CC=gcc CXX=g++ test-all
build-gcc-cmake:
runs-on: ubuntu-20.04
container: gcc:11.1.0
steps:
- uses: actions/checkout@v2
- name: install-build-deps
run: |
# Install cross toolchains
dpkg --add-architecture i386
apt-get update
./install-build-deps.sh
apt-get install -y sudo qemu-user gcc-10-i686-linux-gnu gcc-10-aarch64-linux-gnu gcc-10-riscv64-linux-gnu gcc-10-arm-linux-gnueabihf g++-10-i686-linux-gnu g++-10-aarch64-linux-gnu g++-10-riscv64-linux-gnu g++-10-arm-linux-gnueabihf
ln -sf /usr/bin/i686-linux-gnu-gcc-10 /usr/bin/i686-linux-gnu-gcc
ln -sf /usr/bin/i686-linux-gnu-g++-10 /usr/bin/i686-linux-gnu-g++
ln -sf /usr/bin/aarch64-linux-gnu-gcc-10 /usr/bin/aarch64-linux-gnu-gcc
ln -sf /usr/bin/aarch64-linux-gnu-g++-10 /usr/bin/aarch64-linux-gnu-g++
ln -sf /usr/bin/arm-linux-gnueabihf-gcc-10 /usr/bin/arm-linux-gnueabihf-gcc
ln -sf /usr/bin/arm-linux-gnueabihf-g++-10 /usr/bin/arm-linux-gnueabihf-g++
ln -sf /usr/bin/riscv64-linux-gnu-gcc-10 /usr/bin/riscv64-linux-gnu-gcc
ln -sf /usr/bin/riscv64-linux-gnu-g++-10 /usr/bin/riscv64-linux-gnu-g++
# Install 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 -
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++
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: build and test
run: |
echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
mkdir build
cd build
cmake -DMOLD_ENABLE_QEMU_TESTS=On -DMOLD_ENABLE_QEMU_TESTS_RV32=On ..
cmake --build . -j$(nproc)
ctest . -j$(nproc)
- name: archive test results
uses: actions/upload-artifact@v3
if: always()
with:
name: test-results
path: |
build/out
build/Testing
build-macos-make:
strategy:
matrix:
target:
- test
- test-asan
runs-on: macos-11
steps:
- uses: actions/checkout@v2
- name: brew
run: brew install xxhash
- uses: rui314/setup-mold@staging
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: make
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make -j$(sysctl -n hw.logicalcpu)
- name: make test
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make -j$(sysctl -n hw.logicalcpu) test
run: |
echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
make -j$(sysctl -n hw.logicalcpu) ${{ matrix.target }}
build-macos-cmake:
runs-on: macos-11
strategy:
matrix:
target:
- ''
- '-DMOLD_USE_ASAN=On'
steps:
- uses: actions/checkout@v2
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: build and test
run: |
echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH
mkdir build
cd build
cmake ${{ matrix.target }} ..
cmake --build . -j$(sysctl -n hw.physicalcpu)
ctest . -j$(sysctl -n hw.physicalcpu)
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: build
run: |
mkdir build
cd build
cmake -T clangcl ..
cmake --build . -j $Env:NUMBER_OF_PROCESSORS
+2
View File
@@ -8,7 +8,9 @@ compile_commands.json
options.inc
core
ld
ld64
ld64.mold
gentoo
out
/mold-*.tar.gz
/build*
+259
View File
@@ -0,0 +1,259 @@
cmake_minimum_required(VERSION 3.18)
project(mold VERSION 1.4.2)
include(CMakeDependentOption)
include(GNUInstallDirs)
# Add -fuse-ld=mold if accepted by the compiler.
option(MOLD_USE_MOLD "Use mold to build mold" ON)
if(MOLD_USE_MOLD)
include(CheckLinkerFlag)
check_linker_flag(CXX -fuse-ld=mold CXX_SUPPORTS_FUSE_MOLD)
if(CXX_SUPPORTS_FUSE_MOLD)
add_link_options(-fuse-ld=mold)
endif()
endif()
add_executable(mold)
target_compile_features(mold PRIVATE cxx_std_20)
target_link_libraries(mold PRIVATE ${CMAKE_DL_LIBS})
if(NOT "${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "MSVC")
target_compile_options(mold PRIVATE
-fno-exceptions
-fno-unwind-tables
-fno-asynchronous-unwind-tables)
endif()
# Build mold with -flto if MOLD_LTO=On
option(MOLD_LTO "Build mold with link-time optimization enabled")
if(MOLD_LTO)
set_property(TARGET mold PROPERTY INTERPROCEDURAL_OPTIMIZATION ON)
endif()
# Handle MOLD_USE_ASAN and MOLD_USE_TSAN
option(MOLD_USE_ASAN "Build mold with AddressSanitizer" OFF)
if(MOLD_USE_ASAN)
target_compile_options(mold PRIVATE -fsanitize=address -fsanitize=undefined)
target_link_options(mold PRIVATE -fsanitize=address -fsanitize=undefined)
endif()
option(MOLD_USE_TSAN "Build mold with ThreadSanitizer" OFF)
if(MOLD_USE_TSAN)
target_compile_options(mold PRIVATE -fsanitize=thread)
target_link_options(mold PRIVATE -fsanitize=thread)
endif()
# Static link libstdc++ and libcrypto if MOLD_MOSTLY_STATIC=On
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()
# Setup zlib
find_package(ZLIB QUIET)
if(ZLIB_FOUND)
target_link_libraries(mold PRIVATE ZLIB::ZLIB)
else()
add_subdirectory(third-party/zlib EXCLUDE_FROM_ALL)
target_include_directories(zlibstatic INTERFACE third-party/zlib
$<TARGET_PROPERTY:zlibstatic,BINARY_DIR>)
target_link_libraries(mold PRIVATE zlibstatic)
endif()
# Setup mimalloc
cmake_dependent_option(MOLD_USE_MIMALLOC "Use mimalloc" ON
"NOT APPLE;NOT ANDROID" OFF)
cmake_dependent_option(
MOLD_USE_SYSTEM_MIMALLOC "Use system or vendored mimalloc" OFF
MOLD_USE_MIMALLOC OFF)
if(MOLD_USE_MIMALLOC)
if(MOLD_USE_SYSTEM_MIMALLOC)
find_package(mimalloc REQUIRED)
target_link_libraries(mold PRIVATE mimalloc)
target_compile_definitions(mold PRIVATE USE_SYSTEM_MIMALLOC)
else()
function(mold_add_mimalloc)
set(MI_BUILD_STATIC ON)
option(MI_BUILD_TESTS "Build test executables" OFF)
add_subdirectory(third-party/mimalloc EXCLUDE_FROM_ALL)
target_compile_definitions(mimalloc-static PRIVATE MI_USE_ENVIRON=0)
target_link_libraries(mold PRIVATE mimalloc-static)
endfunction()
mold_add_mimalloc()
endif()
endif()
# Setup TBB
option(MOLD_USE_SYSTEM_TBB "Use system or vendored TBB" OFF)
if(MOLD_USE_SYSTEM_TBB)
find_package(TBB REQUIRED)
target_link_libraries(mold PRIVATE TBB::tbb)
else()
function(mold_add_tbb)
set(BUILD_SHARED_LIBS OFF)
set(TBB_TEST OFF CACHE INTERNAL "")
set(TBB_STRICT OFF CACHE INTERNAL "")
add_subdirectory(third-party/tbb EXCLUDE_FROM_ALL)
target_compile_definitions(tbb PRIVATE __TBB_DYNAMIC_LOAD_ENABLED=0)
target_link_libraries(mold PRIVATE TBB::tbb)
endfunction()
mold_add_tbb()
endif()
# MOLD_X86_64_ONLY is a developer-only option. You should not use it
# for creating an executable for production use.
option(MOLD_X86_64_ONLY "Developer-only option" OFF)
if(MOLD_X86_64_ONLY)
set(CMAKE_BUILD_TYPE "Debug")
target_compile_options(mold PRIVATE -ffunction-sections -fdata-sections
-DMOLD_DEBUG_X86_64_ONLY)
target_link_options(mold PRIVATE -Wl,--gc-sections)
endif()
if(WIN32)
if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
message(FATAL_ERROR
"Your compiler is not supported; install Clang from Visual Studio Installer and re-run cmake with '-T clangcl'")
endif()
target_compile_definitions(mold PRIVATE NOGDI NOMINMAX)
target_sources(mold PRIVATE
elf/lto-win32.cc
macho/lto-win32.cc)
else()
include(CheckLibraryExists)
check_library_exists(m pow "" LIBM_FOUND)
if(LIBM_FOUND)
target_link_libraries(mold PRIVATE m)
endif()
target_sources(mold PRIVATE
elf/lto-unix.cc
elf/subprocess.cc
macho/lto-unix.cc)
endif()
if(NOT APPLE AND NOT WIN32)
add_library(mold-wrapper SHARED)
install(TARGETS mold-wrapper DESTINATION ${CMAKE_INSTALL_LIBDIR}/mold)
# Remove the default `lib` prefix
set_target_properties(mold-wrapper PROPERTIES PREFIX "")
target_link_libraries(mold-wrapper PRIVATE ${CMAKE_DL_LIBS})
target_sources(mold-wrapper PRIVATE elf/mold-wrapper.c)
endif()
if(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(CMAKE_SYSTEM_PROCESSOR MATCHES "^(riscv64|armv)")
target_link_libraries(mold PRIVATE atomic)
endif()
set_property(SOURCE main.cc elf/lto-unix.cc macho/output-chunks.cc APPEND PROPERTY
COMPILE_DEFINITIONS "MOLD_VERSION=\"${CMAKE_PROJECT_VERSION}\"")
add_custom_target(git_hash
COMMAND ${CMAKE_COMMAND}
-DSOURCE_DIR=${CMAKE_SOURCE_DIR}
-DOUTPUT_FILE=${CMAKE_BINARY_DIR}/git-hash.cc
-P ${CMAKE_SOURCE_DIR}/update-git-hash.cmake
DEPENDS update-git-hash.cmake
BYPRODUCTS git-hash.cc
VERBATIM)
add_dependencies(mold git_hash)
target_sources(mold PRIVATE
compress.cc
demangle.cc
elf/arch-arm32.cc
elf/arch-arm64.cc
elf/arch-i386.cc
elf/arch-riscv.cc
elf/arch-x86-64.cc
elf/cmdline.cc
elf/dwarf.cc
elf/gc-sections.cc
elf/icf.cc
elf/input-files.cc
elf/input-sections.cc
elf/linker-script.cc
elf/main.cc
elf/mapfile.cc
elf/output-chunks.cc
elf/passes.cc
elf/relocatable.cc
elf/thunks.cc
filepath.cc
git-hash.cc
glob.cc
hyperloglog.cc
macho/arch-arm64.cc
macho/arch-x86-64.cc
macho/cmdline.cc
macho/dead-strip.cc
macho/input-files.cc
macho/input-sections.cc
macho/main.cc
macho/mapfile.cc
macho/output-chunks.cc
macho/tapi.cc
macho/yaml.cc
main.cc
multi-glob.cc
perf.cc
strerror.cc
tar.cc
third-party/rust-demangle/rust-demangle.c
uuid.cc
)
include(CTest)
if(BUILD_TESTING)
# Create the ld and ld64 symlinks required for testing
if(NOT WIN32)
add_custom_command(
TARGET mold POST_BUILD
COMMAND ${CMAKE_COMMAND} -E create_symlink mold ld
COMMAND ${CMAKE_COMMAND} -E create_symlink mold ld64
BYPRODUCTS ld ld64
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
VERBATIM)
endif()
if(${APPLE})
add_subdirectory(test/macho)
elseif(${UNIX})
add_subdirectory(test/elf)
endif()
endif()
if(NOT CMAKE_SKIP_INSTALL_RULES)
install(TARGETS mold)
install(FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR})
install(FILES docs/mold.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1/)
install(CODE "
set(DEST \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}\")
file(RELATIVE_PATH RELPATH
/${CMAKE_INSTALL_LIBEXECDIR}/mold /${CMAKE_INSTALL_BINDIR}/mold)
execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory
\${DEST}/${CMAKE_INSTALL_LIBEXECDIR}/mold)
execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink \${RELPATH}
\${DEST}/${CMAKE_INSTALL_LIBEXECDIR}/mold/ld)
execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink mold
\${DEST}/${CMAKE_INSTALL_BINDIR}/ld.mold)
execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink mold
\${DEST}/${CMAKE_INSTALL_BINDIR}/ld64.mold)
execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink mold.1
\${DEST}/${CMAKE_INSTALL_MANDIR}/man1/ld.mold.1)")
endif()
+15
View File
@@ -0,0 +1,15 @@
{
"configurations": [
{
"name": "x64-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "clang_cl_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": ""
}
]
}
+8 -4
View File
@@ -1,9 +1,9 @@
# Contributing to mold
Summary: When you send a patch, you must agree that the patch is
released under the dual license of the GNU AGPLv3 and the MIT license.
As an indication of agreement, please add a `Signed-off-by` line to
your commit message using `git commit -s`.
Summary: When you send a patch that is longer than 10 lines, you must
agree that the patch is released under the dual license of the GNU
AGPLv3 and the MIT license. As an indication of agreement, please add
a `Signed-off-by` line to your commit message using `git commit -s`.
Here is the long official version:
@@ -52,3 +52,7 @@ using your real name (sorry, no pseudonyms or anonymous contributions.)
This will be done for you automatically if you use ``git commit -s``.
Reverts should also include "Signed-off-by". ``git revert -s`` does that
for you.
We assume that any contribution that's 10 lines or less does not meet
the threshold of originality and therefore copyright does not apply.
You don't need to add a "Signed-off-by" line to such small commits.
+10 -11
View File
@@ -4,19 +4,18 @@
# $ docker push rui314/mold-builder:v1-$(uname -m)
FROM ubuntu:18.04
ENV TZ=Europe/London
RUN apt-get update && \
TZ=Europe/London apt-get install -y tzdata && \
apt-get install -y --no-install-recommends software-properties-common && \
add-apt-repository -y ppa:ubuntu-toolchain-r/test && \
apt-get install -y --no-install-recommends build-essential git \
wget pkg-config cmake libstdc++-11-dev zlib1g-dev gpg gpg-agent && \
bash -c "$(wget -O- https://apt.llvm.org/llvm.sh)" && \
apt-get install -y --no-install-recommends clang-14 && \
apt clean && \
rm -rf /var/lib/apt/lists/* && \
apt-get update && \
apt-get install -y --no-install-recommends build-essential wget \
libstdc++-11-dev zlib1g-dev gcc-10 g++-10 python3 && \
cd / && \
wget -O- https://www.openssl.org/source/openssl-3.0.2.tar.gz | tar xzf - && \
mv openssl-3.0.2 openssl && \
wget -O- -q https://www.openssl.org/source/openssl-3.0.5.tar.gz | tar xzf - && \
mv openssl-3.0.5 openssl && \
cd openssl && \
./Configure --prefix=/openssl && \
make -j$(nproc)
./Configure --prefix=/usr/local && \
make -j$(nproc) && \
make -j$(nproc) install && \
wget -O- -q https://github.com/Kitware/CMake/releases/download/v3.24.0/cmake-3.24.0-linux-$(uname -m).tar.gz | tar -C /usr/local --strip-components=1 -xzf -
+322 -2
View File
@@ -4,9 +4,9 @@ the MIT license. For now, mold is released under the GNU AGPL v3.
If you just want to use mold but do not want to use AGPL software
in your organization, a commercial license is available. Please
contact rui[email protected].
contact rui[email protected].
--
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
@@ -669,3 +669,323 @@ specific requirements.
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.
MOLD SUBCOMPONENTS:
Mold includes a number of subcomponents with separate copyright notices
and license terms. Your use of the source code for the these subcomponents
is subject to the terms and conditions of the following licenses.
For the third-party/mimalloc component:
MIT License
Copyright (c) 2018-2021 Microsoft Corporation, Daan Leijen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
For the third-party/rust-demangle component:
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
For the third-party/tbb component:
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
For the third-party/xxhash component:
xxHash Library
Copyright (c) 2012-2020 Yann Collet
All rights reserved.
BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php)
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
For the third-party/zlib component:
(C) 1995-2022 Jean-loup Gailly and Mark Adler
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Jean-loup Gailly Mark Adler
[email protected] [email protected]
+89 -60
View File
@@ -1,19 +1,25 @@
# If you want to enable ASAN, run `make` with the following options:
#
# make CXXFLAGS='-fsanitize=address -g' LDFLAGS=-fsanitize=address USE_MIMALLOC=0
VERSION = 1.2.0
VERSION = 1.4.2
PREFIX = /usr/local
BINDIR = $(PREFIX)/bin
LIBDIR = $(PREFIX)/lib
LIBEXECDIR = $(PREFIX)/libexec
MANDIR = $(PREFIX)/share/man
DOCDIR = $(PREFIX)/share/doc
INSTALL = install
INSTALL_PROGRAM = $(INSTALL)
INSTALL_DATA = $(INSTALL) -m 644
OS := $(shell uname -s)
ARCH := $(shell uname -m)
ifeq ($(OS), Darwin)
TESTS := $(wildcard test/macho/*.sh)
else
TESTS := $(wildcard test/elf/*.sh)
endif
D = $(DESTDIR)
# CXX defaults to `g++`. Rewrite it with a vendor-neutral compiler
@@ -22,18 +28,12 @@ ifeq ($(origin CXX), default)
CXX = c++
endif
# Allow overriding pkg-config binary
PKG_CONFIG = pkg-config
# If you want to keep symbols in the installed binary, run make with
# `STRIP=true` to run /bin/true instead of the strip command.
STRIP = strip
SRCS = $(wildcard *.cc elf/*.cc macho/*.cc)
OBJS = $(SRCS:%.cc=out/%.o)
OS := $(shell uname -s)
ARCH := $(shell uname -m)
OBJS = $(SRCS:%.cc=out/%.o) out/rust-demangle.o out/git-hash.o
IS_ANDROID = 0
ifneq ($(findstring -android,$(shell $(CC) -dumpmachine)),)
@@ -46,14 +46,13 @@ CFLAGS = -O2
CXXFLAGS = -O2
MOLD_CXXFLAGS := -std=c++20 -fno-exceptions -fno-unwind-tables \
-fno-asynchronous-unwind-tables -Ithird-party/xxhash \
-fno-asynchronous-unwind-tables \
-DMOLD_VERSION=\"$(VERSION)\" -DLIBDIR="\"$(LIBDIR)\""
MOLD_LDFLAGS := -pthread -lz -lm -ldl
GIT_HASH := $(shell [ -d .git ] && git rev-parse HEAD)
ifneq ($(GIT_HASH),)
MOLD_CXXFLAGS += -DGIT_HASH=\"$(GIT_HASH)\"
ifeq ($(OS), OpenBSD)
MOLD_LDFLAGS := -pthread -lz -lm
else
MOLD_LDFLAGS := -pthread -lz -lm -ldl
endif
LTO = 0
@@ -82,6 +81,10 @@ ifeq ($(USE_MIMALLOC), 1)
endif
endif
# Note: Do NOT specify `SYSTEM_TBB=1` unless your system-wide OneTBB
# library is compiled with https://github.com/oneapi-src/oneTBB/pull/824.
# mold with an unpatched OneTBB is unstable when doing LTO if the system
# is under high load.
ifdef SYSTEM_TBB
MOLD_LDFLAGS += -ltbb
else
@@ -97,20 +100,18 @@ ifeq ($(OS), Linux)
endif
endif
NEEDS_LIBCRYPTO = 0
ifneq ($(OS), Darwin)
NEEDS_LIBCRYPTO = 1
NEEDS_LIBCRYPTO = 1
ifeq ($(OS), Darwin)
NEEDS_LIBCRYPTO = 0
endif
ifeq ($(NEEDS_LIBCRYPTO), 1)
MOLD_CXXFLAGS += $(shell $(PKG_CONFIG) --cflags-only-I openssl)
MOLD_LDFLAGS += $(shell $(PKG_CONFIG) --libs-only-L openssl) -lcrypto
MOLD_LDFLAGS += -lcrypto
endif
# '-latomic' flag is needed building on riscv64 system
# RV32 system doesn't tested yet
# seems like '-atomic' would be better but not working.
ifeq ($(ARCH), riscv64)
# '-latomic' flag is needed building on armv6/riscv64 systems.
# Seems like '-atomic' would be better but not working.
ifneq (,$(filter armv6% riscv64, $(ARCH)))
MOLD_LDFLAGS += -latomic
endif
@@ -129,14 +130,25 @@ all: mold mold-wrapper.so
-include $(SRCS:%.cc=out/%.d)
out/git-hash.cc: FORCE
cmake -DSOURCE_DIR=. -DOUTPUT_FILE=out/git-hash.cc -P update-git-hash.cmake
FORCE:
out/git-hash.o: out/git-hash.cc
$(CXX) $(MOLD_CXXFLAGS) $(CXXFLAGS) -c -o $@ $<
mold: $(OBJS) $(MIMALLOC_LIB) $(TBB_LIB)
$(CXX) $(OBJS) -o $@ $(MOLD_LDFLAGS) $(LDFLAGS)
ln -sf mold ld
ln -sf mold ld64.mold
ln -sf mold ld64
mold-wrapper.so: elf/mold-wrapper.c
$(CC) $(DEPFLAGS) $(CFLAGS) -fPIC -shared -o $@ $< $(MOLD_WRAPPER_LDFLAGS) $(LDFLAGS)
out/rust-demangle.o: third-party/rust-demangle/rust-demangle.c
$(CC) $(CFLAGS) -c -o $@ $<
out/%.o: %.cc out/elf/.keep out/macho/.keep
$(CXX) $(MOLD_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS) -c -o $@ $<
@@ -151,49 +163,53 @@ $(MIMALLOC_LIB):
$(TBB_LIB):
mkdir -p out/tbb
(cd out/tbb; cmake -G'Unix Makefiles' -DBUILD_SHARED_LIBS=OFF -DTBB_TEST=OFF -DCMAKE_CXX_FLAGS="$(CXXFLAGS) -D__TBB_DYNAMIC_LOAD_ENABLED=0" -DTBB_STRICT=OFF ../../third-party/tbb)
(cd out/tbb; cmake -G'Unix Makefiles' -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED_LIBS=OFF -DTBB_TEST=OFF -DCMAKE_CXX_FLAGS="$(CXXFLAGS) -D__TBB_DYNAMIC_LOAD_ENABLED=0" -DTBB_STRICT=OFF ../../third-party/tbb)
$(MAKE) -C out/tbb tbb
(cd out/tbb; ln -sf *_relwithdebinfo libs)
test tests check: all
ifeq ($(OS), Darwin)
$(MAKE) -C test -f Makefile.darwin --no-print-directory
@$(MAKE) $(TESTS) --no-print-directory
else
$(MAKE) -C test -f Makefile.linux --no-print-directory --output-sync
@$(MAKE) $(TESTS) --no-print-directory --output-sync
endif
@if test -t 1; then \
printf '\e[32mPassed all tests\e[0m\n'; \
else \
echo 'Passed all tests'; \
fi
test-x86-64: all
@echo x86_64
CC=x86_64-linux-gnu-gcc CXX=x86_64-linux-gnu-g++ GCC=x86_64-linux-gnu-gcc GXX=x86_64-linux-gnu-g++ OBJDUMP=x86_64-linux-gnu-objdump MACHINE=x86_64 QEMU='qemu-x86_64 -L /usr/x86_64-linux-gnu' $(MAKE) test
test-arch:
TEST_CC=${TRIPLE}-gcc \
TEST_CXX=${TRIPLE}-g++ \
TEST_GCC=${TRIPLE}-gcc \
TEST_GXX=${TRIPLE}-g++ \
OBJDUMP=${TRIPLE}-objdump \
MACHINE=${MACHINE} \
QEMU="qemu-${MACHINE} -L /usr/${TRIPLE}" \
$(MAKE) test
test-i386: all
@echo i386
CC=i686-linux-gnu-gcc CXX=i686-linux-gnu-g++ GCC=i686-linux-gnu-gcc GXX=i686-linux-gnu-g++ OBJDUMP=x86_64-linux-gnu-objdump MACHINE=i386 QEMU='qemu-i386 -L /usr/i686-linux-gnu' $(MAKE) test
test-all: all
$(MAKE) test-arch TRIPLE=x86_64-linux-gnu MACHINE=x86_64
$(MAKE) test-arch TRIPLE=i686-linux-gnu MACHINE=i386
$(MAKE) test-arch TRIPLE=aarch64-linux-gnu MACHINE=aarch64
$(MAKE) test-arch TRIPLE=arm-linux-gnueabihf MACHINE=arm
$(MAKE) test-arch TRIPLE=riscv64-linux-gnu MACHINE=riscv64
test-arm64: all
@echo arm64
CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ GCC=aarch64-linux-gnu-gcc GXX=aarch64-linux-gnu-g++ OBJDUMP=aarch64-linux-gnu-objdump MACHINE=aarch64 QEMU='qemu-aarch64 -L /usr/aarch64-linux-gnu' $(MAKE) test
test-arm32: all
@echo arm
CC=arm-linux-gnueabihf-gcc CXX=arm-linux-gnueabihf-g++ GCC=arm-linux-gnueabihf-gcc GXX=arm-linux-gnueabihf-g++ OBJDUMP=arm-linux-gnueabihf-objdump MACHINE=arm QEMU='qemu-arm -L /usr/arm-linux-gnueabihf' $(MAKE) test
test-riscv64: all
@echo riscv64
CC=riscv64-linux-gnu-gcc CXX=riscv64-linux-gnu-g++ GCC=riscv64-linux-gnu-gcc GXX=riscv64-linux-gnu-g++ OBJDUMP=riscv64-linux-gnu-objdump MACHINE=riscv64 QEMU='qemu-riscv64 -L /usr/riscv64-linux-gnu' $(MAKE) test
test-all:
$(MAKE) test-x86-64
$(MAKE) test-i386
$(MAKE) test-arm64
$(MAKE) test-arm32
$(MAKE) test-riscv64
# macOS's GNU make hasn't been updated since 3.8.1 perhaps due a concern
# of GPLv3. The --output-sync flag was introduced in GNU Make 4.0, so we
# can't use that flag on macOS.
#
# `tail -r | tail -r` is a poor-man's way to enable full buffering on a
# command output. `tail -r` outputs an input from the last line to the
# first.
$(TESTS):
ifeq ($(OS), Darwin)
@set -o pipefail; ./$@ 2>&1 | tail -r | tail -r
else
@./$@
endif
install: all
$(INSTALL) -d $D$(BINDIR)
@@ -201,14 +217,21 @@ install: all
$(STRIP) $D$(BINDIR)/mold
$(INSTALL) -d $D$(LIBDIR)/mold
ifneq ($(OS), Darwin)
$(INSTALL_DATA) mold-wrapper.so $D$(LIBDIR)/mold
$(STRIP) $D$(LIBDIR)/mold/mold-wrapper.so
endif
$(INSTALL) -d $D$(LIBEXECDIR)/mold
ln -sf $(BINDIR)/mold $D$(LIBEXECDIR)/mold/ld
cmake -DSOURCE=$D$(BINDIR)/mold -DDEST=$D$(LIBEXECDIR)/mold/ld -P create-symlink.cmake
$(INSTALL) -d $D$(MANDIR)/man1
$(INSTALL_DATA) docs/mold.1 $D$(MANDIR)/man1
ln -sf mold.1 $D$(MANDIR)/man1/ld.mold.1
$(INSTALL) -d $D$(DOCDIR)/mold
$(INSTALL_DATA) LICENSE $D$(DOCDIR)/mold/
ln -sf mold $D$(BINDIR)/ld.mold
ln -sf mold $D$(BINDIR)/ld64.mold
@@ -218,7 +241,13 @@ uninstall:
rm -f $D$(MANDIR)/man1/mold.1
rm -rf $D$(LIBDIR)/mold
clean:
rm -rf *~ mold mold-wrapper.so out ld ld64.mold mold-*-linux.tar.gz
test-asan test-ubsan:
$(MAKE) USE_MIMALLOC=0 CXXFLAGS='-fsanitize=address -fsanitize=undefined -O0 -g' LDFLAGS='-fsanitize=address -fsanitize=undefined' test
.PHONY: all test tests check clean test-x86-64 test-i386 test-arm64 test-arm32 test-riscv64 test-all
test-tsan:
$(MAKE) USE_MIMALLOC=0 CXXFLAGS='-fsanitize=thread -O0 -g' LDFLAGS=-fsanitize=thread test
clean:
rm -rf *~ mold mold-wrapper.so out ld ld64 mold-*-linux.tar.gz
.PHONY: all test tests check clean test-arch test-all test-asan test-ubsan test-tsan $(TESTS)
+91 -45
View File
@@ -27,8 +27,8 @@ mold currently supports x86-64, i386, ARM32, ARM64 and 64-bit RISC-V.
## Why does the speed of linking matter?
If you are using a compiled language such as C, C++ or Rust, a build
consists of two phases. In the first phase, a compiler compiles a
source file into an object file (`.o` files). In the second phase,
consists of two phases. In the first phase, a compiler compiles
source files into object files (`.o` files). In the second phase,
a linker takes all object files to combine them into a single executable
or a shared library file.
@@ -39,7 +39,7 @@ most noticeable when you are in rapid debug-edit-rebuild cycles.
## Install
Binary packages for the following distros are currently available.
Binary packages for the following systems are currently available.
[![Packaging status](https://repology.org/badge/vertical-allrepos/mold.svg)](https://repology.org/project/mold/versions)
@@ -47,32 +47,21 @@ Binary packages for the following distros are currently available.
mold is written in C++20, so if you build mold yourself, you need a
recent version of a C++ compiler and a C++ standard library. GCC 10.2
or Clang 12.0.0 as well as libstdc++ 10 or libc++ 7 are recommended.
I'm using Ubuntu 20.04 as a development platform. In that environment,
you can build mold by the following commands.
or Clang 12.0.0 (or later) as well as libstdc++ 10 or libc++ 7 (or
later) are recommended.
### Install dependencies
#### Ubuntu 20.04 and later / Debian 11 and later
```shell
sudo apt-get update
sudo apt-get install -y build-essential git clang cmake libstdc++-10-dev libssl-dev libxxhash-dev zlib1g-dev pkg-config
```
#### Fedora 34 and later
```shell
sudo dnf install -y git clang cmake openssl-devel xxhash-devel zlib-devel libstdc++-devel
```
To install build dependencies, run `./install-build-deps.sh` in this
directory. It recognizes your Linux distribution and tries to install
necessary packages. You may want to run it as root.
### Compile mold
```shell
git clone https://github.com/rui314/mold.git
cd mold
git checkout v1.2.0
git checkout v1.4.2
make -j$(nproc) CXX=clang++
sudo make install
```
@@ -86,19 +75,10 @@ By default, `mold` is installed to `/usr/local/bin`.
If you don't use a recent enough Linux distribution, or if for any reason
`make` in the above commands doesn't work for you, you can use Docker to
build it in a Docker environment. To do so, just run `./dist.sh` in this
directory instead of running `make -j$(nproc)`. The shell script creates a
Ubuntu 18.04 Docker image, installs necessary tools and libraries to it,
builds mold and auxiliary files, and packs them into a single tar file
`mold-$version-$arch-linux.tar.gz`. You can extract the tar file anywhere
and use `mold` executable in it.
`make test` depends on a few more packages. To install, run the following commands:
```shell
sudo dpkg --add-architecture i386
sudo apt update
sudo apt-get install bsdmainutils dwarfdump libc6-dev:i386 lib32gcc-10-dev libstdc++-10-dev-arm64-cross gcc-10-aarch64-linux-gnu g++-10-aarch64-linux-gnu
```
directory instead of running `make -j$(nproc)`. The shell script pulls a
Docker image, builds mold and auxiliary files inside it, and packs
them into a single tar file `mold-$version-$arch-linux.tar.gz`.
You can extract the tar file anywhere and use `mold` executable in it.
## How to use
@@ -115,7 +95,7 @@ following flags to use `mold` instead of `/usr/bin/ld`:
- Clang: pass `-fuse-ld=mold`
- GCC 12.1.0 (upcoming version) or later: pass `-fuse-ld=mold`
- GCC 12.1.0 or later: pass `-fuse-ld=mold`
- GCC before 12.1.0: `-fuse-ld` does not accept `mold` as a valid
argument, so you need to use `-B` option instead. `-B` is an option
@@ -123,7 +103,7 @@ following flags to use `mold` instead of `/usr/bin/ld`:
If you have installed mold with `make install`, there should be a
directory named `/usr/libexec/mold` (or `/usr/local/libexec/mold`,
depending on your $PREFIX), and `ld` command should be there. The
depending on your `$PREFIX`), and `ld` command should be there. The
`ld` is actually a symlink to `mold`. So, all you need is to pass
`-B/usr/libexec/mold` (or `-B/usr/local/libexec/mold`) to GCC.
@@ -137,7 +117,7 @@ take an absolute path as an argument for `-fuse-ld` though.
Create `.cargo/config.toml` in your project directory with the following:
```
```toml
[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=/path/to/mold"]
@@ -149,6 +129,19 @@ Please make sure you have installed `clang`.
If you want to use mold for all projects, put the above snippet to
`~/.cargo/config.toml`.
If you are using macOS, you can modify `config.toml` in a similar manner.
Here is an example with `mold` installed via [Homebrew](https://brew.sh).
```toml
[target.x86_64-apple-darwin]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
[target.aarch64-apple-darwin]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
```
</details>
<details><summary>mold -run</summary>
@@ -170,12 +163,28 @@ replace `argv[0]` with `mold` if it is `ld`, `ld.gold` or `ld.lld`.
</details>
<details><summary>GitHub Action</summary>
If you want to use mold in your GitHub-hosted CI to speed up continuous
build, you can use <a href=https://github.com/rui314/setup-mold>setup-mold</a>
GitHub Action. GitHub runs a CI on a two-core machine, but mold is
still significantly faster than the default GNU linker there
especially when a program being linked is large.
<details><summary>On macOS</summary>
mold/macOS is available as an alpha version. It can be used to build not
only macOS apps but also iOS apps because their binary formats are the same.
The command name of mold/macOS is `ld64.mold`. If you build mold on macOS,
it still produces `mold` and `ld.mold`, but these executables are useful
only for cross compilation (i.e. building Linux apps on macOS.)
If you find any issue with mold/macOS, please file it to
<a href=https://github.com/rui314/mold/issues>our GitHub Issues</a>.
</details>
<details><summary>GitHub Actions</summary>
You can use our <a href=https://github.com/rui314/setup-mold>setup-mold</a>
GitHub Action to speed up GitHub-hosted continuous build. GitHub Actions
runs on a two-core machine, but mold is still significantly faster than
the default GNU linker there especially when a program being linked is
large.
</details>
<details><summary>Verify that you are using mold</summary>
@@ -184,7 +193,7 @@ mold leaves its identification string in `.comment` section in an output
file. You can print it out to verify that you are actually using mold.
```shell
readelf -p .comment <executable-file>
$ readelf -p .comment <executable-file>
String dump of section '.comment':
[ 0] GCC: (Ubuntu 10.2.0-5ubuntu1~20.04) 10.2.0
@@ -195,6 +204,15 @@ If `mold` is in `.comment`, the file is created by mold.
</details>
<details><summary>Online manual</summary>
Since mold is a drop-in replacement, you should be able to use it
without reading its manual. But just in case you need it, it's available
online at <a href=https://rui314.github.io/mold.html>here</a>.
You can also read the same manual by `man mold`.
</details>
## Why is mold so fast?
One reason is because it simply uses faster algorithms and efficient
@@ -214,6 +232,34 @@ artificially capped to 16 so that the bars fit in the GIF.
For details, please read [design notes](docs/design.md).
# Logo
## License
-![mold image](docs/mold.jpg)
mold is available under AGPL. Note that that does not mean that you
have to license your program under AGPL if you use mold to link your
program. An output of the mold linker is a derived work of the object
files and libraries you pass to the linker but not a derived work of
the mold linker itself.
Besides that, you can also buy a commercial, non-AGPL license with
technical support from our company, Blue Whale Systems PTE LTD. If you
are a big company, please consider obtaining it before making hundreds
or thousands of developers of your company to depend on mold. mold is
mostly a single-person open-source project, and just like other
open-source projects, we are not legally obligated to keep maintaining
it. A legally-binding commercial license contract addresses the
concern. By purchasing a license, you are guaranteed that mold will be
maintained for you. Please [contact us](mailto:[email protected])
for a commercial license inquiry.
## Acknowledgement
We accept donations via [GitHub Sponsors](https://github.com/sponsors/rui314)
and [OpenCollective](https://opencollective.com/mold-linker).
We thank you to everybody who sponsors our project. In particular,
we'd like to acknowledge the following people and organizations who
have sponsored $128/month or more:
- [300baud](https://github.com/300baud)
- [Mercury](https://github.com/MercuryTechnologies)
- [Wei Wu](https://github.com/lazyparser)
- [Signal Slot Inc.](https://github.com/signal-slot)
+26
View File
@@ -1,3 +1,28 @@
// This file contains functions to read an archive file (.a file).
// An archive file is just a bundle of object files. It's similar to
// tar or zip, but the contents are not compressed.
//
// An archive file is either "regular" or "thin". A regular archive
// contains object files directly, while a thin archive contains only
// pathnames. In the latter case, actual file contents have to be read
// from given pathnames. A regular archive is sometimes called "fat"
// archive as opposed to "thin".
//
// If an archive file is given to the linker, the linker pulls out
// object files that are needed to resolve undefined symbols. So,
// bunding object files as an archive and giving that archive to the
// linker has a different meaning than directly giving the same set of
// object files to the linker. The former links only needed object
// files, while the latter links all the given object files.
//
// Therefore, if you link libc.a for example, not all the libc
// functions are linked to your binary. Instead, only object files
// that provides functions and variables used in your program get
// linked. To make this efficient, static library functions are
// usually separated to each object file in an archive file. You can
// see the contents of libc.a by runnning `ar t
// /usr/lib/x86_64-linux-gnu/libc.a`.
#pragma once
#include "mold.h"
@@ -93,6 +118,7 @@ read_thin_archive_members(C &ctx, MappedFile<C> *mf) {
std::string path = name.starts_with('/') ?
name : (filepath(mf->name).parent_path() / name).string();
vec.push_back(MappedFile<C>::must_open(ctx, path));
vec.back()->thin_parent = mf;
data = body;
}
return vec;
-81
View File
@@ -1,81 +0,0 @@
#pragma once
#include <cstdint>
#include <cstring>
#ifdef __BIG_ENDIAN__
#error "mold does not support big-endian hosts"
#endif
namespace mold {
template <typename T>
class BigEndian {
public:
BigEndian() = delete;
operator T() const {
T x;
memcpy(&x, val, sizeof(T));
return bswap(x);
}
BigEndian &operator=(T x) {
x = bswap(x);
memcpy(&val, &x, sizeof(T));
return *this;
}
BigEndian &operator++() {
return *this = *this + 1;
}
BigEndian operator++(int) {
T ret = *this;
*this = *this + 1;
return ret;
}
BigEndian &operator--() {
return *this = *this - 1;
}
BigEndian operator--(int) {
T ret = *this;
*this = *this - 1;
return ret;
}
BigEndian &operator+=(T x) {
return *this = *this + x;
}
BigEndian &operator&=(T x) {
return *this = *this & x;
}
BigEndian &operator|=(T x) {
return *this = *this | x;
}
private:
uint8_t val[sizeof(T)];
static T bswap(T x) {
if constexpr (sizeof(T) == 2)
return __builtin_bswap16(x);
else if constexpr (sizeof(T) == 4)
return __builtin_bswap32(x);
else
return __builtin_bswap64(x);
}
};
using ibig16 = BigEndian<int16_t>;
using ibig32 = BigEndian<int32_t>;
using ibig64 = BigEndian<int64_t>;
using ubig16 = BigEndian<uint16_t>;
using ubig32 = BigEndian<uint32_t>;
using ubig64 = BigEndian<uint64_t>;
} // namespace mold
+13
View File
@@ -48,6 +48,7 @@ read_response_file(C &ctx, std::string_view path) {
return vec;
}
// Replace "@path/to/some/text/file" with its file contents.
template <typename C>
std::vector<std::string_view> expand_response_files(C &ctx, char **argv) {
std::vector<std::string_view> vec;
@@ -60,4 +61,16 @@ std::vector<std::string_view> expand_response_files(C &ctx, char **argv) {
return vec;
}
static inline std::string_view string_trim(std::string_view str) {
size_t pos = str.find_first_not_of(" \t");
if (pos == str.npos)
return "";
str = str.substr(pos);
pos = str.find_last_not_of(" \t");
if (pos == str.npos)
return str;
return str.substr(0, pos + 1);
}
} // namespace mold
+6 -5
View File
@@ -89,7 +89,8 @@ static std::vector<u8> do_compress(std::string_view input) {
return buf;
}
ZlibCompressor::ZlibCompressor(std::string_view input) {
ZlibCompressor::ZlibCompressor(u8 *buf, i64 size) {
std::string_view input{(char *)buf, (size_t)size};
std::vector<std::string_view> inputs = split(input);
std::vector<u64> adlers(inputs.size());
shards.resize(inputs.size());
@@ -134,7 +135,7 @@ void ZlibCompressor::write_to(u8 *buf) {
end[-5] = 0;
// Write a checksum
*(ubig32 *)(end - 4) = checksum;
*(ub32 *)(end - 4) = checksum;
}
GzipCompressor::GzipCompressor(std::string_view input) {
@@ -183,10 +184,10 @@ void GzipCompressor::write_to(u8 *buf) {
// Write a trailer
u8 *end = buf + size();
end[-10] = 0x3; // two-byte zlib stream terminator
end[-10] = 3; // two-byte zlib stream terminator
end[-9] = 0;
*(u32 *)(end - 8) = checksum;
*(u32 *)(end - 4) = uncompressed_size;
*(ul32 *)(end - 8) = checksum;
*(ul32 *)(end - 4) = uncompressed_size;
}
} // namespace mold
+7
View File
@@ -0,0 +1,7 @@
# Create a symbolic link with a relative path. GNU ln takes the --relative
# option to do that, but that's not portable, so we do it in CMake.
cmake_minimum_required(VERSION 3.14)
get_filename_component(DIR "${DEST}" DIRECTORY)
file(RELATIVE_PATH PATH "/${DIR}" "/${SOURCE}")
file(CREATE_LINK "${PATH}" "${DEST}" SYMBOLIC)
+31 -4
View File
@@ -1,15 +1,41 @@
#include "mold.h"
#include <cstdlib>
#ifndef _WIN32
#include <cxxabi.h>
#endif
#include "third-party/rust-demangle/rust-demangle.h"
namespace mold {
std::string_view demangle(std::string_view name) {
if (name.starts_with("_Z")) {
static thread_local char *buf;
static thread_local size_t buflen;
static thread_local char *p;
if (p)
free(p);
// Try to demangle as a Rust symbol. Since legacy-style Rust symbols
// are also valid as a C++ mangled name, we need to call this before
// cpp_demangle.
p = rust_demangle(std::string(name).c_str(), 0);
if (p)
return p;
// Try to demangle as a C++ symbol.
if (std::optional<std::string_view> s = cpp_demangle(name))
return *s;
return name;
}
std::optional<std::string_view> cpp_demangle(std::string_view name) {
static thread_local char *buf;
static thread_local size_t buflen;
// TODO(cwasser): Actually demangle Symbols on Windows using e.g.
// `UnDecorateSymbolName` from Dbghelp, maybe even Itanium symbols?
#ifndef _WIN32
if (name.starts_with("_Z")) {
int status;
char *p = abi::__cxa_demangle(std::string(name).c_str(), buf, &buflen, &status);
if (status == 0) {
@@ -17,8 +43,9 @@ std::string_view demangle(std::string_view name) {
return p;
}
}
#endif
return name;
return {};
}
} // namespace mold
+15 -15
View File
@@ -9,27 +9,27 @@
set -e
version=$(grep '^VERSION =' $(dirname $0)/Makefile | sed 's/.* = //')
# Unlike Linux, macOS's uname returns arm64 for aarch64.
arch=$(uname -m)
dest=mold-$version-$arch-linux
[ $arch = arm64 ] && arch=aarch64
if [ $arch = x86_64 ]; then
image=rui314/mold-builder:v1-x86_64
elif [ $arch = aarch64 -o $arch = arm64 ]; then
image=rui314/mold-builder:v1-aarch64
else
if [ $arch != x86_64 -a $arch != aarch64 ]; then
echo "Error: no docker image for $arch"
exit 1
fi
version=$(grep '^VERSION =' $(dirname $0)/Makefile | sed 's/.* = //')
dest=mold-$version-$arch-linux
image=rui314/mold-builder:v1-$arch
docker images -q $image 2> /dev/null || docker pull $image
docker run -it --rm -v "$(pwd):/mold:Z" -u "$(id -u):$(id -g)" $image \
bash -c "cp -r /mold /tmp/mold &&
cd /tmp/mold &&
make clean &&
make -j\$(nproc) CXX=clang++-14 CXXFLAGS='-I/openssl/include -O2 $CXXFLAGS' LDFLAGS='-static-libstdc++ /openssl/libcrypto.a' NEEDS_LIBCRYPTO=0 LTO=${LTO:-0} &&
make install PREFIX=/ DESTDIR=$dest &&
ln -sfr $dest/bin/mold $dest/libexec/mold/ld &&
docker run -it --rm -v "$(pwd):/mold" -e "OWNER=$(id -u):$(id -g)" $image \
bash -c "mkdir /tmp/build &&
cd /tmp/build &&
cmake -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 -DMOLD_MOSTLY_STATIC=On -DCMAKE_BUILD_TYPE=Release /mold &&
cmake --build . -j\$(nproc)
cmake --install . --prefix $dest --strip
tar czf /mold/$dest.tar.gz $dest &&
cp mold /mold"
cp mold mold-wrapper.so /mold &&
chown \$OWNER /mold/mold /mold/mold-wrapper.so /mold/$dest.tar.gz"
+105 -29
View File
@@ -352,7 +352,7 @@ or
.Pf Sy lib Ar libname Ns Sy \.a
from library search paths.
.Pp
.It Fl m Op Sy elf_x86_64 | elf_i386 | aarch64linux
.It Fl m Op Ar target
Choose a target.
.Pp
.It Fl o Ar file , Fl -output Ns = Ns Ar file
@@ -473,6 +473,20 @@ Set
.Ar dir
to root directory.
.Pp
.It Fl -color-diagnostics Ns = Ns Op Sy auto | always | never
.It Fl -color-diagnostics
.It Fl -no-color-diagnostics
.Pp
Show diagnostics messages in color using ANSI escape sequences.
.Ar auto
means that
.Nm
prints out messages in color only if the standard output is connected to a TTY. \
Default is
.Ar auto .
.Pp
.It Fl -defsym Ns = Ns Ar symbol Ns = Ns Ar value
.Pp
.It Fl -compress-debug-sections Ns = Ns Op Sy none | zlib | zlib-gabi | zlib-gnu
Compress DWARF debug info
.Pf ( Sy .debug_*
@@ -515,6 +529,10 @@ options.
.It Fl -dynamic-list Ns = Ns Ar file
Read a list of dynamic symbols from
.Ar file .
Same as
.Fl -export-dynamic-symbol-list ,
except that it implies
.Fl -Bsymbolic .
.Pp
.It Fl -eh-frame-hdr
.It Fl -no-eh-frame-hdr
@@ -555,6 +573,24 @@ Mark all symbols in the given
.Ar libraries
hidden.
.Pp
.It Fl -export-dynamic-symbol Ns = Ns Ar sym
Put symbols matching
.Ar sym
in the dynamic symbol table.
.Ar sym
may be a glob, with the same syntax as the globs used in
.Fl -export-dynamic-symbol-list
or
.Fl -version-script .
.Pp
.It Fl -export-dynamic-symbol-list Ns = Ns Ar file
Read a list of dynamic symbols from
.Ar file .
.Pp
.It Fl -fatal-warnings
.It Fl -no-fatal-warnings
Treat warnings as errors.
.Pp
.It Fl -fini Ns = Ns Ar symbol
Call
.Ar symbol
@@ -574,12 +610,67 @@ hides that latency.
.It Fl -no-gc-sections
Remove unreferenced sections.
.Pp
.It Fl -gdb-index
Create a
.Li .gdb_index
section to speed up GNU debugger. To use this, you need to compile source files \
with the
.Fl -ggnu-pubnames
compiler flag.
.Pp
.It Fl -hash-style Ns = Ns Op Sy sysv | gnu | both
Set hash style.
.Pp
.It Fl -icf Ns = Ns Sy all
.It Fl -icf Ns = Ns Op Sy none | safe | all
.It Fl -no-icf
Fold identical code.
It is not uncommon for a program to contain many identical functions that differ \
only in name. For example, a C++ template
.Sy std::vector
is very likely to be instantiated to the identical code for
.Sy std::vector<int>
and
.Sy std::vector<unsigned>
because the container cares only about the size of the parameter type. \
Identical Code Folding (ICF) is a size optimization to identify and merge \
such identical functions.
.Pp
If
.Fl -icf=all
is given,
.Nm
tries to merge all identical functions. This reduces the size of the output \
most, but it is not
.Dq safe
optimization. It is guaranteed in C and C++ that two pointeres pointing two \
different functions will never be equal, but
.Fl -icf=all
breaks that assumption as two functions have the same address after merging. \
So a care must be taken when you use that flag that your program does not \
depend on the function pointer uniqueness.
.Pp
.Fl -icf=safe
is a flag to merge functions only when it is safe to do so. That is, if a \
program does not take an address of a function, it is safe to merge that \
function with other function, as you cannot compare a function pointer \
with something else without taking an address of a function.
.FL -icf=safe
needs to be used with a compiler that supports
.Sy .llvm_addrsig
section which contains the information as to what symbols are address-taken. \
LLVM/Clang supports that section by default. Since GCC does not support it \
yet, you cannot use
.Fl -icf=safe
with GCC (it doesn't do any harm but can't optimize at all.)
.Pp
.Fl -icf=none
and
.Fl -no-icf
disables ICF.
.Pp
.It Fl -ignore-data-address-equality
Make ICF to merge not only functions but also data. This option should be \
used in combination with
.Fl -icf=all .
.Pp
.It Fl -image-base Ns = Ns Ar addr
Set the base address to
@@ -620,6 +711,14 @@ to run executables or shared libraries linked with
.Fl -pack-dyn-relocs=relr ,
and only ChromeOS, Android and Fuchsia support it as of now in 2022.
.Pp
.It Fl -package-metadata Ns = Ns Ar string
Embed
.Ar string
to a .note.package section. This option in intended to be used by a \
package management command such as
.Cm rpm
to embed metadata regarding a package to each executable file.
.Pp
.It Fl -perf
Print performance statistics.
.Pp
@@ -898,23 +997,7 @@ section to indicate that the output uses IBT-enabled PLT. This option implies
.Fl z Cm ibtplt .
.Pp
.It Fl z Cm ibtplt
Generate Intel Branch Tracking (IBT)-enabled PLT.
.Pp
IBT is part of Intel Control-flow Enforcement Technology (CET).
IBT is a new x86 feature available since Tiger Lake which is released in 2020.
If IBT is enabled, all indirect branch instructions have to branch to a \
so-called "landing pad" instruction. Landing pad itself is a no-op, but \
it works as a marker that branching to that instruction is expected.
If there's no landing pad after branch, the CPU raises an exception.
This mechanism makes ROP attacks difficult.
.Pp
Since PLT can be used as an indirect branch target, we need a different \
instruction sequence for IBT-enabled PLT. If
.Sy -z Cm ibtplt
is specified,
.Nm
generates PLT entries that start with a landing pad. The size of IBT-enabled \
PLT is 24 bytes as opposed to 16 bytes regular PLT.
Generate Intel Branch Tracking (IBT)-enabled PLT which is the default on x86-64.
.Pp
.It Fl z Cm execstack
.It Fl z Cm noexecstack
@@ -948,15 +1031,13 @@ As a security mitigation,
it is preferred to make such segments read-only during program execution.
.Pp
.Fl z Cm relro
puts such sections into a special section called
puts such sections into a special segment called
.Dv relro .
The dynamic linker make a relro segment read-only after it finishes its job.
.Pp
By default,
.Nm
generates a
.Sy relro
segment.
generates a relro segment.
.Fl z Cm norelro
disables the feature.
.Pp
@@ -1044,20 +1125,15 @@ Mark object to interpose all DSOs but executable.
.It Fl EL
.It Fl O Ns Ar number
.It Fl -allow-shlib-undefined
.It Fl -color-diagnostics
.It Fl -dc
.It Fl -dp
.It Fl -end-group
.It Fl -fatal-warnings
.It Fl -gdb-index
.It Fl -no-add-needed
.It Fl -no-allow-shlib-undefined
.It Fl -no-copy-dt-needed-entries
.It Fl -no-fatal-warnings
.It Fl -no-undefined-version
.It Fl -nostdlib
.It Fl -plugin
.It Fl -plugin-opt
.It Fl -rpath-link Ns = Ns Ar dir
.It Fl -sort-common
.It Fl -sort-section
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

+291 -235
View File
@@ -12,18 +12,21 @@
// be switched using BX (branch and mode exchange)-family instructions.
// We need to use such instructions to, for example, call a function
// encoded in Thumb from a function encoded in ARM. Sometimes, the
// linker even has to emit an interworking thunk code to switch from
// Thumb to ARM.
// linker even has to emit an interworking thunk code to switch between
// them.
//
// ARM instructions are aligned to 4 byte boundaries. Thumb are to 2
// byte boundaries.
//
// You can distinguish Thumb functions from ARM functions by looking
// at the least significant bit (LSB) of its "address". If LSB is 0,
// it's ARM; otherwise, Thumb. LSB is not a part of its real address.
// it's ARM; otherwise, Thumb.
//
// For example, if a symbol `foo` is of type STT_FUNC and has value
// 0x2001, then `foo` is a function using Thumb instructions whose
// address is 0x2000 (not 0x2001).
// 0x2001, `foo` is a function using Thumb instructions whose address
// is 0x2000 (not 0x2001, as Thumb instructions are always 2-byte
// aligned). Likewise, if a function pointer has value 0x2001, it
// refers a Thumb function at 0x2000.
#include "mold.h"
@@ -35,23 +38,10 @@ namespace mold::elf {
using E = ARM32;
static u32 bit(u32 val, i64 pos) {
return (val >> pos) & 1;
}
// Returns [hi:lo] bits of val.
static u32 bits(u32 val, i64 hi, i64 lo) {
return (val >> lo) & ((1LL << (hi - lo + 1)) - 1);
}
static i32 sign_extend(u32 val, u32 size) {
return (i64)(val << (31 - size)) >> (31 - size);
};
static void write_mov_imm(u8 *loc, u32 val) {
u32 imm12 = bits(val, 11, 0);
u32 imm4 = bits(val, 15, 12);
*(u32 *)loc = (*(u32 *)loc & 0xfff0f000) | (imm4 << 16) | imm12;
*(ul32 *)loc = (*(ul32 *)loc & 0xfff0f000) | (imm4 << 16) | imm12;
}
static void write_thm_b_imm(u8 *loc, u32 val) {
@@ -64,9 +54,9 @@ static void write_thm_b_imm(u8 *loc, u32 val) {
u32 imm10 = bits(val, 21, 12);
u32 imm11 = bits(val, 11, 1);
*(u16 *)loc = (*(u16 *)loc & 0xf800) | (sign << 10) | imm10;
*(u16 *)(loc + 2) =
(*(u16 *)(loc + 2) & 0xd000) | (J1 << 13) | (J2 << 11) | imm11;
ul16 *buf = (ul16 *)loc;
buf[0] = (buf[0] & 0b1111'1000'0000'0000) | (sign << 10) | imm10;
buf[1] = (buf[1] & 0b1101'0000'0000'0000) | (J1 << 13) | (J2 << 11) | imm11;
}
static void write_thm_mov_imm(u8 *loc, u32 val) {
@@ -75,9 +65,10 @@ static void write_thm_mov_imm(u8 *loc, u32 val) {
u32 i = bit(val, 11);
u32 imm3 = bits(val, 10, 8);
u32 imm8 = bits(val, 7, 0);
*(u16 *)loc = (*(u16 *)loc & 0b1111'1011'1111'0000) | (i << 10) | imm4;
*(u16 *)(loc + 2) =
((*(u16 *)(loc + 2)) & 0b1000'1111'0000'0000) | (imm3 << 12) | imm8;
ul16 *buf = (ul16 *)loc;
buf[0] = (buf[0] & 0b1111'1011'1111'0000) | (i << 10) | imm4;
buf[1] = (buf[1] & 0b1000'1111'0000'0000) | (imm3 << 12) | imm8;
}
template <>
@@ -85,15 +76,18 @@ void PltSection<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + this->shdr.sh_offset;
static const u32 plt0[] = {
0xe52de004, // 1: push {lr}
0xe59fe004, // ldr lr, [pc, #4]
0xe08fe00e, // add lr, pc, lr
0xe5bef008, // ldr pc, [lr, #8]!
0x00000000, // .word .got.plt - 1b - 16
0xe52de004, // push {lr}
0xe59fe004, // ldr lr, 2f
0xe08fe00e, // 1: add lr, pc, lr
0xe5bef008, // ldr pc, [lr, #8]!
0x00000000, // 2: .word .got.plt - 1b - 8
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
};
memcpy(buf, plt0, sizeof(plt0));
*(u32 *)(buf + 16) = ctx.gotplt->shdr.sh_addr - this->shdr.sh_addr - 16;
*(ul32 *)(buf + 16) = ctx.gotplt->shdr.sh_addr - this->shdr.sh_addr - 16;
for (Symbol<E> *sym : symbols) {
static const u32 plt[] = {
@@ -105,7 +99,7 @@ void PltSection<E>::copy_buf(Context<E> &ctx) {
u8 *ent = buf + sizeof(plt0) + sym->get_plt_idx(ctx) * sizeof(plt);
memcpy(ent, plt, sizeof(plt));
*(u32 *)(ent + 12) = sym->get_gotplt_addr(ctx) - sym->get_plt_addr(ctx) - 12;
*(ul32 *)(ent + 12) = sym->get_gotplt_addr(ctx) - sym->get_plt_addr(ctx) - 12;
}
}
@@ -123,22 +117,28 @@ void PltGotSection<E>::copy_buf(Context<E> &ctx) {
u8 *ent = buf + sym->get_pltgot_idx(ctx) * sizeof(plt);
memcpy(ent, plt, sizeof(plt));
*(u32 *)(ent + 12) = sym->get_got_addr(ctx) - sym->get_plt_addr(ctx) - 12;
*(ul32 *)(ent + 12) = sym->get_got_addr(ctx) - sym->get_plt_addr(ctx) - 12;
}
}
// ARM does not use .eh_frame for exception handling. Instead, it uses
// .ARM.exidx and .ARM.extab. So this function is empty.
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
void EhFrameSection<E>::apply_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) {
return sign_extend(val, 24) == val;
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
ElfRel<E> *dynrel = nullptr;
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
i64 frag_idx = 0;
i64 trampoline_idx = 0;
if (ctx.reldyn)
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
@@ -146,7 +146,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_ARM_NONE)
if (rel.r_type == R_ARM_NONE || rel.r_type == R_ARM_V4BX)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
@@ -160,84 +160,131 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
#define A (frag_ref ? frag_ref->addend : this->get_addend(rel))
#define P (output_section->shdr.sh_addr + offset + rel.r_offset)
#define T (sym.get_addr(ctx) & 1)
#define G (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
#define G (sym.get_got_idx(ctx) * sizeof(Word<E>))
#define GOT ctx.got->shdr.sh_addr
auto get_thumb_thunk_addr = [&] {
RangeExtensionRef ref = extra.range_extn[i];
assert(ref.thunk_idx != -1);
u64 addr = output_section->thunks[ref.thunk_idx]->get_addr(ref.sym_idx);
assert(is_jump_reachable(addr + A - P));
return addr;
};
auto get_arm_thunk_addr = [&] {
return get_thumb_thunk_addr() + 4;
};
auto get_trampoline_addr = [&](u64 addr) {
for (; trampoline_idx < output_section->thunks.size(); trampoline_idx++) {
RangeExtensionThunk<E> &thunk = *output_section->thunks[trampoline_idx];
i64 disp = output_section->shdr.sh_addr + thunk.offset - addr;
if (is_jump_reachable(disp))
return disp;
}
unreachable();
};
switch (rel.r_type) {
case R_ARM_ABS32:
case R_ARM_TARGET1:
if (sym.is_absolute() || !ctx.arg.pic) {
*(u32 *)loc = S + A;
} else if (sym.is_imported) {
*dynrel++ = {P, R_ARM_ABS32, (u32)sym.get_dynsym_idx(ctx)};
} else {
if (!is_relr_reloc(ctx, rel))
*dynrel++ = {P, R_ARM_RELATIVE, 0};
*(u32 *)loc = S + A;
}
apply_abs_dyn_rel(ctx, sym, rel, loc, S, A, P, dynrel);
continue;
case R_ARM_REL32:
*(u32 *)loc = S + A - P;
*(ul32 *)loc = S + A - P;
continue;
case R_ARM_THM_CALL:
case R_ARM_THM_CALL: {
// THM_CALL relocation refers either BL or BLX instruction.
// They are different in only one bit. We need to use BL if
// the jump target is Thumb. Otherwise, use BLX.
if (sym.esym().is_undef_weak()) {
// On ARM, calling an weak undefined symbol jumps to the
// next instruction.
write_thm_b_imm(loc, 4);
*(u16 *)(loc + 2) |= (1 << 12); // rewrite with BL
} else if (T) {
write_thm_b_imm(loc, S + A - P);
*(u16 *)(loc + 2) |= (1 << 12); // rewrite with BL
*(ul16 *)(loc + 2) |= 0x1000; // rewrite to BL
continue;
}
u64 val = S + A - P;
if (is_jump_reachable(val)) {
if (T) {
write_thm_b_imm(loc, val);
*(ul16 *)(loc + 2) |= 0x1000; // rewrite to BL
} else {
write_thm_b_imm(loc, align_to(val, 4));
*(ul16 *)(loc + 2) &= ~0x1000; // rewrite to BLX
}
} else {
write_thm_b_imm(loc, align_to(S + A - P, 4));
*(u16 *)(loc + 2) &= ~(1 << 12); // rewrite with BLX
write_thm_b_imm(loc, align_to(get_arm_thunk_addr() + A - P, 4));
*(ul16 *)(loc + 2) &= ~0x1000; // rewrite to BLX
}
continue;
}
case R_ARM_BASE_PREL:
*(u32 *)loc = GOT + A - P;
*(ul32 *)loc = GOT + A - P;
continue;
case R_ARM_GOT_PREL:
*(u32 *)loc = G + A - P;
case R_ARM_TARGET2:
*(ul32 *)loc = GOT + G + A - P;
continue;
case R_ARM_GOT_BREL:
*(u32 *)loc = G + A;
*(ul32 *)loc = G + A;
continue;
case R_ARM_TARGET2:
*(u32 *)loc = GOT + G + A - P;
continue;
case R_ARM_CALL:
case R_ARM_JUMP24: {
u32 val;
case R_ARM_CALL: {
// Just like THM_CALL, ARM_CALL relocation refers 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";
if (sym.esym().is_undef_weak()) {
// On ARM, calling an weak undefined symbol jumps to the
// next instruction.
val = 4;
} else {
val = S + A - P;
*(ul32 *)loc = 0xeb00'0001;
continue;
}
*(u32 *)loc = (*(u32 *)loc & 0xff00'0000) | ((val >> 2) & 0x00ff'ffff);
continue;
}
case R_ARM_THM_JUMP11: {
assert(T);
u32 val = (S + A - P) >> 1;
*(u16 *)loc = (*(u16 *)loc & 0xf800) | (val & 0x07ff);
continue;
}
case R_ARM_THM_JUMP24:
if (T) {
write_thm_b_imm(loc, S + A - P);
u64 val = S + A - P;
if (is_jump_reachable(val)) {
if (T)
*(ul32 *)loc = 0xfa00'0000 | (bit(val, 1) << 24) | bits(val, 25, 2);
else
*(ul32 *)loc = 0xeb00'0000 | bits(val, 25, 2);
} else {
assert(sym.extra.thumb_to_arm_thunk_idx != -1);
u64 thunk_addr =
ctx.thumb_to_arm->shdr.sh_addr +
sym.extra.thumb_to_arm_thunk_idx * ThumbToArmSection::ENTRY_SIZE;
write_thm_b_imm(loc, thunk_addr - P - 4);
*(ul32 *)loc = 0xeb00'0000 | bits(get_arm_thunk_addr() + A - P, 25, 2);
}
continue;
}
case R_ARM_JUMP24:
if (sym.esym().is_undef_weak()) {
*(ul32 *)loc = (*(ul32 *)loc & 0xff00'0000) | 1;
} else {
// Unlike BL and BLX, we can't rewrite B to BX because BX doesn't
// takes an immediate; it takes only a register. So if mode switch
// is required, we jump to a linker-synthesized thunk which constructs
// a branch destination in a register and branches to that address.
u64 val = S + A - P;
if (!is_jump_reachable(val) || T)
val = get_arm_thunk_addr() + A - P;
*(ul32 *)loc = (*(ul32 *)loc & 0xff00'0000) | bits(val, 25, 2);
}
continue;
case R_ARM_THM_JUMP11:
assert(T);
*(ul16 *)loc = (*(ul16 *)loc & 0xf800) | bits(S + A - P, 11, 1);
continue;
case R_ARM_THM_JUMP24:
if (sym.esym().is_undef_weak()) {
*(ul32 *)loc = (*(ul32 *)loc & 0xff00'0000) | 1;
} else {
// Just like R_ARM_JUMP24, we need to jump to a thunk if we need to
// switch processor mode.
u64 val = S + A - P;
if (!is_jump_reachable(val) || !T)
val = get_thumb_thunk_addr() + A - P;
write_thm_b_imm(loc, val);
}
continue;
case R_ARM_MOVW_PREL_NC:
@@ -249,11 +296,9 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
case R_ARM_THM_MOVW_PREL_NC:
write_thm_mov_imm(loc, ((S + A) | T) - P);
continue;
case R_ARM_PREL31: {
u32 val = S + A - P;
*(u32 *)loc = (*(u32 *)loc & 0x8000'0000) | (val & 0x7fff'ffff);
case R_ARM_PREL31:
*(ul32 *)loc = (*(ul32 *)loc & 0x8000'0000) | ((S + A - P) & 0x7fff'ffff);
continue;
}
case R_ARM_THM_MOVW_ABS_NC:
write_thm_mov_imm(loc, (S + A) | T);
continue;
@@ -270,34 +315,47 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
write_thm_mov_imm(loc, (S + A) >> 16);
continue;
case R_ARM_TLS_GD32:
*(u32 *)loc = sym.get_tlsgd_addr(ctx) + A - P;
*(ul32 *)loc = sym.get_tlsgd_addr(ctx) + A - P;
continue;
case R_ARM_TLS_LDM32:
*(u32 *)loc = ctx.got->get_tlsld_addr(ctx) + A - P;
*(ul32 *)loc = ctx.got->get_tlsld_addr(ctx) + A - P;
continue;
case R_ARM_TLS_LDO32:
*(u32 *)loc = S + A - ctx.tls_begin;
*(ul32 *)loc = S + A - ctx.tls_begin;
continue;
case R_ARM_TLS_IE32:
*(u32 *)loc = sym.get_gottp_addr(ctx) + A - P;
*(ul32 *)loc = sym.get_gottp_addr(ctx) + A - P;
continue;
case R_ARM_TLS_LE32:
*(u32 *)loc = S + A - ctx.tls_begin + 8;
*(ul32 *)loc = S + A - ctx.tls_begin + E::tls_tp_offset;
continue;
case R_ARM_TLS_GOTDESC:
if (sym.get_tlsdesc_idx(ctx) == -1)
*(u32 *)loc = S - ctx.tls_begin + 8;
else
*(u32 *)loc = sym.get_tlsdesc_addr(ctx) + A - P - 6;
if (sym.get_tlsdesc_idx(ctx) == -1) {
*(ul32 *)loc = S - ctx.tls_begin + E::tls_tp_offset;
} else if (A & 1) {
// A is odd if the corresponding TLS_CALL is Thumb.
*(ul32 *)loc = sym.get_tlsdesc_addr(ctx) - P + A - 6;
} else {
*(ul32 *)loc = sym.get_tlsdesc_addr(ctx) - P + A - 4;
}
continue;
case R_ARM_TLS_CALL:
if (sym.get_tlsdesc_idx(ctx) == -1) {
// BL -> NOP
*(ul32 *)loc = 0xe320'f000;
} else {
// BL <tls_trampoline>
*(ul32 *)loc = 0xeb00'0000 | bits(get_trampoline_addr(P + 8), 25, 2);
}
continue;
case R_ARM_THM_TLS_CALL:
if (sym.get_tlsdesc_idx(ctx) == -1) {
// BL -> NOP
*(u32 *)loc = 0x8000f3af;
*(ul32 *)loc = 0x8000'f3af;
} else {
u64 addr = ctx.tls_trampoline->shdr.sh_addr;
write_thm_b_imm(loc, align_to(addr - P - 4, 4));
*(u16 *)(loc + 2) &= ~(1 << 12); // rewrite BL with BLX
u64 val = align_to(get_trampoline_addr(P + 4), 4);
write_thm_b_imm(loc, val);
*(ul16 *)(loc + 2) &= ~0x1000; // rewrite BL with BLX
}
continue;
default:
@@ -315,7 +373,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
@@ -326,7 +384,7 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
u8 *loc = base + rel.r_offset;
if (!sym.file) {
report_undef(ctx, file, sym);
record_undef_error(ctx, rel);
continue;
}
@@ -339,16 +397,19 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
switch (rel.r_type) {
case R_ARM_ABS32:
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
else
*(u32 *)loc = S + A;
if (!frag) {
if (std::optional<u64> val = get_tombstone(sym)) {
*(ul32 *)loc = *val;
break;
}
}
*(ul32 *)loc = S + A;
break;
case R_ARM_TLS_LDO32:
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
*(ul32 *)loc = *val;
else
*(u32 *)loc = S + A - ctx.tls_begin;
*(ul32 *)loc = S + A - ctx.tls_begin;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
@@ -366,7 +427,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
@@ -377,70 +438,37 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (!sym.file) {
report_undef(ctx, file, sym);
record_undef_error(ctx, rel);
continue;
}
if (sym.get_type() == STT_GNU_IFUNC) {
sym.flags |= NEEDS_GOT;
sym.flags |= NEEDS_PLT;
}
if (sym.get_type() == STT_GNU_IFUNC)
sym.flags |= (NEEDS_GOT | NEEDS_PLT);
switch (rel.r_type) {
case R_ARM_ABS32:
case R_ARM_MOVT_ABS:
case R_ARM_THM_MOVT_ABS:
case R_ARM_TARGET1: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, BASEREL, DYNREL, DYNREL }, // DSO
{ NONE, BASEREL, DYNREL, DYNREL }, // PIE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
case R_ARM_TARGET1:
scan_abs_dyn_rel(ctx, sym, rel);
break;
}
case R_ARM_REL32:
case R_ARM_BASE_PREL:
case R_ARM_THM_CALL:
case R_ARM_CALL:
case R_ARM_JUMP24:
case R_ARM_THM_JUMP24:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_ARM_THM_CALL: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, NONE, PLT, PLT }, // DSO
{ NONE, NONE, PLT, PLT }, // PIE
{ NONE, NONE, PLT, PLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
case R_ARM_GOT_PREL:
case R_ARM_GOT_BREL:
case R_ARM_TARGET2:
sym.flags |= NEEDS_GOT;
break;
case R_ARM_CALL:
case R_ARM_JUMP24:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_ARM_THM_JUMP24:
if (sym.is_imported || sym.get_type() == STT_GNU_IFUNC)
sym.flags |= NEEDS_PLT | NEEDS_THUMB_TO_ARM_THUNK;
else if (sym.esym().st_value % 2 == 0)
sym.flags |= NEEDS_THUMB_TO_ARM_THUNK;
break;
case R_ARM_MOVT_PREL:
case R_ARM_THM_MOVT_PREL:
case R_ARM_PREL31: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ ERROR, NONE, ERROR, ERROR }, // DSO
{ ERROR, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
case R_ARM_PREL31:
scan_pcrel_rel(ctx, sym, rel);
break;
}
case R_ARM_TLS_GD32:
sym.flags |= NEEDS_TLSGD;
break;
@@ -454,6 +482,8 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
if (!ctx.relax_tlsdesc || sym.is_imported)
sym.flags |= NEEDS_TLSDESC;
break;
case R_ARM_REL32:
case R_ARM_BASE_PREL:
case R_ARM_THM_JUMP11:
case R_ARM_MOVW_PREL_NC:
case R_ARM_MOVW_ABS_NC:
@@ -461,7 +491,9 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
case R_ARM_THM_MOVW_ABS_NC:
case R_ARM_TLS_LDO32:
case R_ARM_TLS_LE32:
case R_ARM_TLS_CALL:
case R_ARM_THM_TLS_CALL:
case R_ARM_V4BX:
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
@@ -469,55 +501,88 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
}
}
// If a function referenced by a Thumb B (branch) instruction is
// resovled to a non-thumb function, we can't directly jump from the
// thumb function to the ARM function. We can't rewrite B with BX
// because there's no such BX instruction that takes an immediate.
// BX takes only a register.
//
// In order to support such branch, we insert a small piece of code to
// the resulting executable which switches the processor mode from
// Thumb to ARM. This section contains such code.
void ThumbToArmSection::add_symbol(Context<E> &ctx, Symbol<E> *sym) {
if (sym->extra.thumb_to_arm_thunk_idx == -1) {
sym->extra.thumb_to_arm_thunk_idx = symbols.size();
symbols.push_back(sym);
}
// For range extension thunks
template <>
bool is_reachable(Context<E> &ctx, Symbol<E> &sym,
InputSection<E> &isec, const ElfRel<E> &rel) {
// We pessimistically assume that PLT entries are unreacahble.
if (sym.has_plt(ctx))
return false;
// We create thunks with a pessimistic assumption that all
// out-of-section relocations would be out-of-range.
InputSection<E> *isec2 = sym.get_input_section();
if (!isec2 || isec.output_section != isec2->output_section)
return false;
// If the target section is in the same output section but hasn't got
// any address yet, that's unreacahble.
if (isec2->offset == -1)
return false;
// Thumb and ARM B instructions cannot be converted to BX, so we
// always have to make them jump to a thunk to switch processor mode
// even if their destinations are within their ranges.
bool is_thumb = sym.get_addr(ctx) & 1;
if ((rel.r_type == R_ARM_THM_JUMP24 && !is_thumb) ||
(rel.r_type == R_ARM_JUMP24 && is_thumb))
return false;
// Compute a distance between the relocated place and the symbol
// and check if they are within reach.
i64 S = sym.get_addr(ctx);
i64 A = isec.get_addend(rel);
i64 P = isec.get_addr() + rel.r_offset;
return is_jump_reachable(S + A - P);
}
void ThumbToArmSection::update_shdr(Context<E> &ctx) {
this->shdr.sh_size = symbols.size() * ENTRY_SIZE;
}
template <>
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + output_section.shdr.sh_offset + offset;
void ThumbToArmSection::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + this->shdr.sh_offset;
i64 offset = 0;
static u16 insn[] = {
0x4778, // bx pc
0x46c0, // nop
0, 0, // b <imm24>
};
static_assert(sizeof(insn) == ENTRY_SIZE);
for (Symbol<E> *sym : symbols) {
memcpy(buf + offset, insn, sizeof(insn));
u32 val = sym->get_addr(ctx) - this->shdr.sh_addr - offset - 12;
*(u32 *)(buf + offset + 4) = 0xea00'0000 | (0x00ff'ffff & (val >> 2));
offset += sizeof(insn);
}
}
void TlsTrampolineSection::copy_buf(Context<E> &ctx) {
// Trampoline code for TLSDESC
static u32 insn[] = {
// 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.
static u32 hdr[] = {
0xe08e0000, // add r0, lr, r0
0xe5901004, // ldr r1, [r0, #4]
0xe12fff11, // bx r1
};
memcpy(ctx.buf + this->shdr.sh_offset, insn, sizeof(insn));
// 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
// .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
};
static_assert(E::thunk_hdr_size == sizeof(hdr));
static_assert(E::thunk_size == sizeof(entry));
memcpy(buf, hdr, sizeof(hdr));
for (i64 i = 0; i < symbols.size(); i++) {
u8 *loc = buf + sizeof(hdr) + i * sizeof(entry);
memcpy(loc, entry, sizeof(entry));
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;
}
}
template <typename E>
static OutputSection<E> *find_exidx_section(Context<E> &ctx) {
for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections)
if (osec->shdr.sh_type == SHT_ARM_EXIDX)
return osec.get();
return nullptr;
}
// ARM executables use an .ARM.exidx section to look up an exception
@@ -532,14 +597,7 @@ void TlsTrampolineSection::copy_buf(Context<E> &ctx) {
void sort_arm_exidx(Context<E> &ctx) {
Timer t(ctx, "sort_arm_exidx");
auto find_exidx = [&]() -> OutputSection<E> * {
for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections)
if (osec->shdr.sh_type == SHT_ARM_EXIDX)
return osec.get();
return nullptr;
};
OutputSection<E> *osec = find_exidx();
OutputSection<E> *osec = find_exidx_section(ctx);
if (!osec)
return;
@@ -553,48 +611,46 @@ void sort_arm_exidx(Context<E> &ctx) {
// 3. a 31-bit relative address which points to a larger record in
// the .ARM.extab section.
//
// CANTUNWIND is value 1. The most significant is set in (2) but not
// in (3). So they can be distinguished just by looking at a value.
// CANTUNWIND is value 1. The most significant bit is set in (2) but
// not in (3). So we can distinguished them just by looking at a value.
const u32 EXIDX_CANTUNWIND = 1;
struct Entry {
u32 addr;
u32 val;
ul32 addr;
ul32 val;
};
if (osec->shdr.sh_size % sizeof(Entry))
Fatal(ctx) << "invalid .ARM.exidx section size";
Entry *begin = (Entry *)(ctx.buf + osec->shdr.sh_offset);
Entry *end = (Entry *)(ctx.buf + osec->shdr.sh_offset + osec->shdr.sh_size);
Entry *ent = (Entry *)(ctx.buf + osec->shdr.sh_offset);
i64 num_entries = osec->shdr.sh_size / sizeof(Entry);
struct Entry2 {
u32 addr;
u32 val;
u32 idx;
// Entry's addresses are relative to themselves. In order to sort
// records by addresses, we first translate them so that the addresses
// are relative to the beginning of the section.
auto is_relative = [](u32 val) {
return val != EXIDX_CANTUNWIND && !(val & 0x8000'0000);
};
// Read section contents
std::vector<Entry2> vec;
vec.reserve(end - begin);
for (Entry *it = begin; it < end; it++)
vec.push_back({it->addr, it->val, (u32)(it - begin)});
// Sort the records
tbb::parallel_sort(vec.begin(), vec.end(), [](const Entry2 &a, const Entry2 &b) {
return sign_extend(a.addr, 30) + a.idx * sizeof(Entry) <
sign_extend(b.addr, 30) + b.idx * sizeof(Entry);
tbb::parallel_for((i64)0, num_entries, [&](i64 i) {
i64 offset = sizeof(Entry) * i;
ent[i].addr = sign_extend(ent[i].addr, 30) + offset;
if (is_relative(ent[i].val))
ent[i].val = 0x7fff'ffff & (sign_extend(ent[i].val, 30) + offset);
});
// Write back the sorted records while adjusting relative addresses
for (i64 i = 0; i < vec.size(); i++) {
u32 offset = (vec[i].idx - i) * sizeof(Entry);
begin[i].addr = 0x7fff'ffff & (sign_extend(vec[i].addr, 30) + offset);
tbb::parallel_sort(ent, ent + num_entries, [](const Entry &a, const Entry &b) {
return a.addr < b.addr;
});
const u32 EXIDX_CANTUNWIND = 1;
if (vec[i].val == EXIDX_CANTUNWIND || (vec[i].val & 0x8000'0000))
begin[i].val = vec[i].val;
else
begin[i].val = 0x7fff'ffff & (sign_extend(vec[i].val, 30) + offset);
}
// Make addresses relative to themselves.
tbb::parallel_for((i64)0, num_entries, [&](i64 i) {
i64 offset = sizeof(Entry) * i;
ent[i].addr = 0x7fff'ffff & (ent[i].addr - offset);
if (is_relative(ent[i].val))
ent[i].val = 0x7fff'ffff & (ent[i].val - offset);
});
}
} // namespace mold::elf
+103 -384
View File
@@ -1,21 +1,21 @@
#include "mold.h"
#include <tbb/parallel_for.h>
#include <tbb/parallel_for_each.h>
namespace mold::elf {
using E = ARM64;
static void write_adr(u8 *buf, u64 val) {
u32 hi = (val & 0x1ffffc) << 3;
u32 lo = (val & 3) << 29;
*(u32 *)buf = (*(u32 *)buf & 0x9f00001f) | hi | lo;
static void write_adrp(u8 *buf, u64 val) {
u32 hi = bits(val, 32, 14);
u32 lo = bits(val, 13, 12);
u32 op = *(ul32 *)buf & 0b1001'1111'0000'0000'0000'0000'0001'1111;
*(ul32 *)buf = (lo << 29) | (hi << 5) | op;
}
// Returns [hi:lo] bits of val.
static u64 bits(u64 val, u64 hi, u64 lo) {
return (val >> lo) & (((u64)1 << (hi - lo + 1)) - 1);
static void write_adr(u8 *buf, u64 val) {
u32 hi = bits(val, 20, 2);
u32 lo = bits(val, 1, 0);
u32 op = *(ul32 *)buf & 0b1001'1111'0000'0000'0000'0000'0001'1111;
*(ul32 *)buf = (lo << 29) | (hi << 5) | op;
}
static u64 page(u64 val) {
@@ -39,13 +39,13 @@ static void write_plt_header(Context<E> &ctx, u8 *buf) {
u64 plt = ctx.plt->shdr.sh_addr;
memcpy(buf, plt0, sizeof(plt0));
write_adr(buf + 4, bits(page(gotplt) - page(plt + 4), 32, 12));
*(u32 *)(buf + 8) |= bits(gotplt, 11, 3) << 10;
*(u32 *)(buf + 12) |= ((gotplt) & 0xfff) << 10;
write_adrp(buf + 4, page(gotplt) - page(plt + 4));
*(ul32 *)(buf + 8) |= bits(gotplt, 11, 3) << 10;
*(ul32 *)(buf + 12) |= (gotplt & 0xfff) << 10;
}
static void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
u8 *ent = buf + ctx.plt_hdr_size + sym.get_plt_idx(ctx) * ctx.plt_size;
u8 *ent = buf + E::plt_hdr_size + sym.get_plt_idx(ctx) * E::plt_size;
static const u32 data[] = {
0x90000010, // adrp x16, .got.plt[n]
@@ -58,9 +58,9 @@ static void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
u64 plt = sym.get_plt_addr(ctx);
memcpy(ent, data, sizeof(data));
write_adr(ent, bits(page(gotplt) - page(plt), 32, 12));
*(u32 *)(ent + 4) |= bits(gotplt, 11, 3) << 10;
*(u32 *)(ent + 8) |= (gotplt & 0xfff) << 10;
write_adrp(ent, page(gotplt) - page(plt));
*(ul32 *)(ent + 4) |= bits(gotplt, 11, 3) << 10;
*(ul32 *)(ent + 8) |= (gotplt & 0xfff) << 10;
}
template <>
@@ -89,25 +89,25 @@ void PltGotSection<E>::copy_buf(Context<E> &ctx) {
u64 plt = sym->get_plt_addr(ctx);
memcpy(ent, data, sizeof(data));
write_adr(ent, bits(page(got) - page(plt), 32, 12));
*(u32 *)(ent + 4) |= bits(got, 11, 3) << 10;
write_adrp(ent, page(got) - page(plt));
*(ul32 *)(ent + 4) |= bits(got, 11, 3) << 10;
}
}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
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_AARCH64_ABS64:
*(u64 *)loc = val;
*(ul64 *)loc = val;
return;
case R_AARCH64_PREL32:
*(u32 *)loc = val - this->shdr.sh_addr - offset;
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
return;
case R_AARCH64_PREL64:
*(u64 *)loc = val - this->shdr.sh_addr - offset;
*(ul64 *)loc = val - this->shdr.sh_addr - offset;
return;
}
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
@@ -116,8 +116,7 @@ void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
ElfRel<E> *dynrel = nullptr;
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<RangeExtensionRef> range_extn = get_range_extn();
std::span<const ElfRel<E>> rels = get_rels(ctx);
i64 frag_idx = 0;
@@ -145,67 +144,58 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
};
#define S (frag_ref ? frag_ref->frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag_ref ? frag_ref->addend : rel.r_addend)
#define A (frag_ref ? (u64)frag_ref->addend : (u64)rel.r_addend)
#define P (output_section->shdr.sh_addr + offset + rel.r_offset)
#define G (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
#define G (sym.get_got_idx(ctx) * sizeof(Word<E>))
#define GOT ctx.got->shdr.sh_addr
switch (rel.r_type) {
case R_AARCH64_ABS64:
if (sym.is_absolute() || !ctx.arg.pic) {
*(u64 *)loc = S + A;
} else if (sym.is_imported) {
*dynrel++ = {P, R_AARCH64_ABS64, (u32)sym.get_dynsym_idx(ctx), A};
*(u64 *)loc = A;
} else {
if (!is_relr_reloc(ctx, rel))
*dynrel++ = {P, R_AARCH64_RELATIVE, 0, (i64)(S + A)};
*(u64 *)loc = S + A;
}
apply_abs_dyn_rel(ctx, sym, rel, loc, S, A, P, dynrel);
continue;
case R_AARCH64_LDST8_ABS_LO12_NC:
*(u32 *)loc |= bits(S + A, 11, 0) << 10;
*(ul32 *)loc |= bits(S + A, 11, 0) << 10;
continue;
case R_AARCH64_LDST16_ABS_LO12_NC:
*(u32 *)loc |= bits(S + A, 11, 1) << 10;
*(ul32 *)loc |= bits(S + A, 11, 1) << 10;
continue;
case R_AARCH64_LDST32_ABS_LO12_NC:
*(u32 *)loc |= bits(S + A, 11, 2) << 10;
*(ul32 *)loc |= bits(S + A, 11, 2) << 10;
continue;
case R_AARCH64_LDST64_ABS_LO12_NC:
*(u32 *)loc |= bits(S + A, 11, 3) << 10;
*(ul32 *)loc |= bits(S + A, 11, 3) << 10;
continue;
case R_AARCH64_LDST128_ABS_LO12_NC:
*(u32 *)loc |= bits(S + A, 11, 4) << 10;
*(ul32 *)loc |= bits(S + A, 11, 4) << 10;
continue;
case R_AARCH64_ADD_ABS_LO12_NC:
*(u32 *)loc |= bits(S + A, 11, 0) << 10;
*(ul32 *)loc |= bits(S + A, 11, 0) << 10;
continue;
case R_AARCH64_MOVW_UABS_G0:
case R_AARCH64_MOVW_UABS_G0_NC:
*(u32 *)loc |= bits(S + A, 15, 0) << 5;
*(ul32 *)loc |= bits(S + A, 15, 0) << 5;
continue;
case R_AARCH64_MOVW_UABS_G1:
case R_AARCH64_MOVW_UABS_G1_NC:
*(u32 *)loc |= bits(S + A, 31, 16) << 5;
*(ul32 *)loc |= bits(S + A, 31, 16) << 5;
continue;
case R_AARCH64_MOVW_UABS_G2:
case R_AARCH64_MOVW_UABS_G2_NC:
*(u32 *)loc |= bits(S + A, 47, 32) << 5;
*(ul32 *)loc |= bits(S + A, 47, 32) << 5;
continue;
case R_AARCH64_MOVW_UABS_G3:
*(u32 *)loc |= bits(S + A, 63, 48) << 5;
*(ul32 *)loc |= bits(S + A, 63, 48) << 5;
continue;
case R_AARCH64_ADR_GOT_PAGE: {
i64 val = page(G + GOT + A) - page(P);
overflow_check(val, -((i64)1 << 32), (i64)1 << 32);
write_adr(loc, bits(val, 32, 12));
write_adrp(loc, val);
continue;
}
case R_AARCH64_ADR_PREL_PG_HI21: {
i64 val = page(S + A) - page(P);
overflow_check(val, -((i64)1 << 32), (i64)1 << 32);
write_adr(loc, bits(val, 32, 12));
write_adrp(loc, val);
continue;
}
case R_AARCH64_ADR_PREL_LO21: {
@@ -219,7 +209,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
if (sym.esym().is_undef_weak()) {
// On ARM, calling an weak undefined symbol jumps to the
// next instruction.
*(u32 *)loc |= 1;
*(ul32 *)loc |= 1;
continue;
}
@@ -228,107 +218,107 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
i64 val = S + A - P;
if (val < lo || hi <= val) {
RangeExtensionRef ref = range_extn[i];
RangeExtensionRef ref = extra.range_extn[i];
val = output_section->thunks[ref.thunk_idx]->get_addr(ref.sym_idx) + A - P;
assert(lo <= val && val < hi);
}
*(u32 *)loc |= (val >> 2) & 0x3ffffff;
*(ul32 *)loc |= (val >> 2) & 0x3ffffff;
continue;
}
case R_AARCH64_CONDBR19:
case R_AARCH64_LD_PREL_LO19: {
i64 val = S + A - P;
overflow_check(val, -((i64)1 << 20), (i64)1 << 20);
*(u32 *)loc |= bits(val, 20, 2) << 5;
*(ul32 *)loc |= bits(val, 20, 2) << 5;
continue;
}
case R_AARCH64_PREL16: {
i64 val = S + A - P;
overflow_check(val, -((i64)1 << 15), (i64)1 << 15);
*(u16 *)loc = val;
*(ul16 *)loc = val;
continue;
}
case R_AARCH64_PREL32: {
i64 val = S + A - P;
overflow_check(val, -((i64)1 << 31), (i64)1 << 32);
*(u32 *)loc = val;
*(ul32 *)loc = val;
continue;
}
case R_AARCH64_PREL64:
*(u64 *)loc = S + A - P;
*(ul64 *)loc = S + A - P;
continue;
case R_AARCH64_LD64_GOT_LO12_NC:
*(u32 *)loc |= bits(G + GOT + A, 11, 3) << 10;
*(ul32 *)loc |= bits(G + GOT + A, 11, 3) << 10;
continue;
case R_AARCH64_LD64_GOTPAGE_LO15: {
i64 val = G + GOT + A - page(GOT);
overflow_check(val, 0, 1 << 15);
*(u32 *)loc |= bits(val, 14, 3) << 10;
*(ul32 *)loc |= bits(val, 14, 3) << 10;
continue;
}
case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: {
i64 val = page(sym.get_gottp_addr(ctx) + A) - page(P);
overflow_check(val, -((i64)1 << 32), (i64)1 << 32);
write_adr(loc, bits(val, 32, 12));
write_adrp(loc, val);
continue;
}
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
*(u32 *)loc |= bits(sym.get_gottp_addr(ctx) + A, 11, 3) << 10;
*(ul32 *)loc |= bits(sym.get_gottp_addr(ctx) + A, 11, 3) << 10;
continue;
case R_AARCH64_TLSLE_ADD_TPREL_HI12: {
i64 val = S + A - ctx.tls_begin + 16;
i64 val = S + A - ctx.tls_begin + E::tls_tp_offset;
overflow_check(val, 0, (i64)1 << 24);
*(u32 *)loc |= bits(val, 23, 12) << 10;
*(ul32 *)loc |= bits(val, 23, 12) << 10;
continue;
}
case R_AARCH64_TLSLE_ADD_TPREL_LO12:
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
*(u32 *)loc |= bits(S + A - ctx.tls_begin + 16, 11, 0) << 10;
*(ul32 *)loc |= bits(S + A - ctx.tls_begin + E::tls_tp_offset, 11, 0) << 10;
continue;
case R_AARCH64_TLSGD_ADR_PAGE21: {
i64 val = page(sym.get_tlsgd_addr(ctx) + A) - page(P);
overflow_check(val, -((i64)1 << 32), (i64)1 << 32);
write_adr(loc, bits(val, 32, 12));
write_adrp(loc, val);
continue;
}
case R_AARCH64_TLSGD_ADD_LO12_NC:
*(u32 *)loc |= bits(sym.get_tlsgd_addr(ctx) + A, 11, 0) << 10;
*(ul32 *)loc |= bits(sym.get_tlsgd_addr(ctx) + A, 11, 0) << 10;
continue;
case R_AARCH64_TLSDESC_ADR_PAGE21: {
if (ctx.relax_tlsdesc && !sym.is_imported) {
// adrp x0, 0 -> movz x0, #tls_ofset_hi, lsl #16
i64 val = (S + A - ctx.tls_begin + 16);
i64 val = (S + A - ctx.tls_begin + E::tls_tp_offset);
overflow_check(val, -((i64)1 << 32), (i64)1 << 32);
*(u32 *)loc = 0xd2a00000 | (bits(val, 32, 16) << 5);
*(ul32 *)loc = 0xd2a00000 | (bits(val, 32, 16) << 5);
} else {
i64 val = page(sym.get_tlsdesc_addr(ctx) + A) - page(P);
overflow_check(val, -((i64)1 << 32), (i64)1 << 32);
write_adr(loc, bits(val, 32, 12));
write_adrp(loc, val);
}
continue;
}
case R_AARCH64_TLSDESC_LD64_LO12:
if (ctx.relax_tlsdesc && !sym.is_imported) {
// ldr x2, [x0] -> movk x0, #tls_ofset_lo
u32 offset_lo = (S + A - ctx.tls_begin + 16) & 0xffff;
*(u32 *)loc = 0xf2800000 | (offset_lo << 5);
u32 offset_lo = (S + A - ctx.tls_begin + E::tls_tp_offset) & 0xffff;
*(ul32 *)loc = 0xf2800000 | (offset_lo << 5);
} else {
*(u32 *)loc |= bits(sym.get_tlsdesc_addr(ctx) + A, 11, 3) << 10;
*(ul32 *)loc |= bits(sym.get_tlsdesc_addr(ctx) + A, 11, 3) << 10;
}
continue;
case R_AARCH64_TLSDESC_ADD_LO12:
if (ctx.relax_tlsdesc && !sym.is_imported) {
// add x0, x0, #0 -> nop
*(u32 *)loc = 0xd503201f;
*(ul32 *)loc = 0xd503201f;
} else {
*(u32 *)loc |= bits(sym.get_tlsdesc_addr(ctx) + A, 11, 0) << 10;
*(ul32 *)loc |= bits(sym.get_tlsdesc_addr(ctx) + A, 11, 0) << 10;
}
continue;
case R_AARCH64_TLSDESC_CALL:
if (ctx.relax_tlsdesc && !sym.is_imported) {
// blr x2 -> nop
*(u32 *)loc = 0xd503201f;
*(ul32 *)loc = 0xd503201f;
}
continue;
default:
@@ -345,7 +335,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
@@ -356,7 +346,7 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
u8 *loc = base + rel.r_offset;
if (!sym.file) {
report_undef(ctx, file, sym);
record_undef_error(ctx, rel);
continue;
}
@@ -365,17 +355,20 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::tie(frag, addend) = get_fragment(ctx, rel);
#define S (frag ? frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag ? addend : rel.r_addend)
#define A (frag ? (u64)addend : (u64)rel.r_addend)
switch (rel.r_type) {
case R_AARCH64_ABS64:
if (std::optional<u64> val = get_tombstone(sym))
*(u64 *)loc = *val;
else
*(u64 *)loc = S + A;
if (!frag) {
if (std::optional<u64> val = get_tombstone(sym)) {
*(ul64 *)loc = *val;
break;
}
}
*(ul64 *)loc = S + A;
continue;
case R_AARCH64_ABS32:
*(u32 *)loc = S + A;
*(ul32 *)loc = S + A;
continue;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
@@ -393,7 +386,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
@@ -404,26 +397,17 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (!sym.file) {
report_undef(ctx, file, sym);
record_undef_error(ctx, rel);
continue;
}
if (sym.get_type() == STT_GNU_IFUNC) {
sym.flags |= NEEDS_GOT;
sym.flags |= NEEDS_PLT;
}
if (sym.get_type() == STT_GNU_IFUNC)
sym.flags |= (NEEDS_GOT | NEEDS_PLT);
switch (rel.r_type) {
case R_AARCH64_ABS64: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, BASEREL, DYNREL, DYNREL }, // DSO
{ NONE, BASEREL, DYNREL, DYNREL }, // PIE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
case R_AARCH64_ABS64:
scan_abs_dyn_rel(ctx, sym, rel);
break;
}
case R_AARCH64_ADR_GOT_PAGE:
case R_AARCH64_LD64_GOT_LO12_NC:
case R_AARCH64_LD64_GOTPAGE_LO15:
@@ -438,16 +422,9 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
sym.flags |= NEEDS_GOTTP;
break;
case R_AARCH64_ADR_PREL_PG_HI21: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ ERROR, NONE, ERROR, ERROR }, // DSO
{ ERROR, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
case R_AARCH64_ADR_PREL_PG_HI21:
scan_pcrel_rel(ctx, sym, rel);
break;
}
case R_AARCH64_TLSGD_ADR_PAGE21:
sym.flags |= NEEDS_TLSGD;
break;
@@ -488,21 +465,21 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
}
}
static void reset_thunk(RangeExtensionThunk<E> &thunk) {
for (Symbol<E> *sym : thunk.symbols) {
sym->extra.thunk_idx = -1;
sym->extra.thunk_sym_idx = -1;
sym->flags &= (u8)~NEEDS_RANGE_EXTN_THUNK;
}
}
// For range extension thunks
template <>
bool is_reachable(Context<E> &ctx, Symbol<E> &sym,
InputSection<E> &isec, const ElfRel<E> &rel) {
// We pessimistically assume that PLT entries are unreacahble.
if (sym.has_plt(ctx))
return false;
static bool is_reachable(Context<E> &ctx, Symbol<E> &sym,
InputSection<E> &isec, const ElfRel<E> &rel) {
// We always create a thunk for an absolute symbol conservatively
// because `shrink_sections` may increase a distance between a
// branch instruction and an absolute symbol. Branching to an
// absolute location is extremely rare in real code, though.
if (sym.is_absolute())
// We create thunks with a pessimistic assumption that all
// out-of-section relocations would be out-of-range.
InputSection<E> *isec2 = sym.get_input_section();
if (!isec2 || isec.output_section != isec2->output_section)
return false;
if (isec2->offset == -1)
return false;
// Compute a distance between the relocated place and the symbol
@@ -514,253 +491,7 @@ static bool is_reachable(Context<E> &ctx, Symbol<E> &sym,
return -(1 << 27) <= val && val < (1 << 27);
}
// We create a thunk no further than 100 MiB from any section.
static constexpr i64 MAX_DISTANCE = 100 * 1024 * 1024;
// We create a thunk for each 10 MiB input sections.
static constexpr i64 GROUP_SIZE = 10 * 1024 * 1024;
static void create_thunks(Context<E> &ctx, OutputSection<E> &osec) {
std::span<InputSection<E> *> members = osec.members;
members[0]->offset = 0;
// Initialize input sections with very large dummy offsets so that
// sections that have got real offsets are separated from the ones
// without in the virtual address space.
tbb::parallel_for((i64)1, (i64)members.size(), [&](i64 i) {
members[i]->offset = 1 << 31;
});
// We create thunks from the beginning of the section to the end.
// We manage progress using four offsets which increase monotonically.
// The locations they point to are always A <= B <= C <= D.
i64 a = 0;
i64 b = 0;
i64 c = 0;
i64 d = 0;
i64 offset = 0;
while (b < members.size()) {
// Move D foward as far as we can jump from B to D.
while (d < members.size() && offset - members[b]->offset < MAX_DISTANCE) {
offset = align_to(offset, 1 << members[d]->p2align);
members[d]->offset = offset;
offset += members[d]->sh_size;
d++;
}
// Move C forward so that C is apart from B by GROUP_SIZE.
while (c < members.size() &&
members[c]->offset - members[b]->offset < GROUP_SIZE)
c++;
// Move A forward so that A is reachable from C.
if (c > 0) {
i64 c_end = members[c - 1]->offset + members[c - 1]->sh_size;
while (a < osec.thunks.size() &&
osec.thunks[a]->offset < c_end - MAX_DISTANCE)
reset_thunk(*osec.thunks[a++]);
}
// Create a thunk for input sections between B and C and place it at D.
osec.thunks.emplace_back(new RangeExtensionThunk<E>{osec});
RangeExtensionThunk<E> &thunk = *osec.thunks.back();
thunk.thunk_idx = osec.thunks.size() - 1;
thunk.offset = offset;
// Scan relocations between B and C to collect symbols that need thunks.
tbb::parallel_for_each(members.begin() + b, members.begin() + c,
[&](InputSection<E> *isec) {
std::span<ElfRel<E>> rels = isec->get_rels(ctx);
std::vector<RangeExtensionRef> &range_extn = isec->get_range_extn();
range_extn.resize(rels.size());
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type != R_AARCH64_CALL26 && rel.r_type != R_AARCH64_JUMP26)
continue;
Symbol<E> &sym = *isec->file.symbols[rel.r_sym];
// Skip if the destination is within reach.
if (is_reachable(ctx, sym, *isec, rel))
continue;
// If the symbol is already in another thunk, reuse it.
if (sym.extra.thunk_idx != -1) {
range_extn[i] = {sym.extra.thunk_idx, sym.extra.thunk_sym_idx};
continue;
}
// Otherwise, add the symbol to this thunk if it's not added already.
range_extn[i] = {thunk.thunk_idx, -1};
if (!(sym.flags.fetch_or(NEEDS_RANGE_EXTN_THUNK) &
NEEDS_RANGE_EXTN_THUNK)) {
std::scoped_lock lock(thunk.mu);
thunk.symbols.push_back(&sym);
}
}
});
// Now that we know the number of symbols in the thunk, we can compute
// its size.
offset += thunk.size();
// Sort symbols added to the thunk to make the output deterministic.
sort(thunk.symbols, [](Symbol<E> *a, Symbol<E> *b) { return *a < *b; });
// Assign offsets within the thunk to the symbols.
for (i64 i = 0; Symbol<E> *sym : thunk.symbols) {
sym->extra.thunk_idx = thunk.thunk_idx;
sym->extra.thunk_sym_idx = i++;
}
// Scan relocations again to fix symbol offsets in the last thunk.
tbb::parallel_for_each(members.begin() + b, members.begin() + c,
[&](InputSection<E> *isec) {
std::span<ElfRel<E>> rels = isec->get_rels(ctx);
std::span<RangeExtensionRef> range_extn = isec->get_range_extn();
for (i64 i = 0; i < rels.size(); i++) {
if (range_extn[i].thunk_idx == thunk.thunk_idx) {
Symbol<E> &sym = *isec->file.symbols[rels[i].r_sym];
range_extn[i].sym_idx = sym.extra.thunk_sym_idx;
}
}
});
// Move B forward to point to the begining of the next group.
b = c;
}
while (a < osec.thunks.size())
reset_thunk(*osec.thunks[a++]);
osec.shdr.sh_size = offset;
}
static void gc_thunk_symbols(Context<E> &ctx, OutputSection<E> &osec) {
for (std::unique_ptr<RangeExtensionThunk<E>> &thunk : osec.thunks) {
i64 sz = thunk->symbols.size();
thunk->symbol_map.resize(sz);
thunk->used.reset(new std::atomic_bool[sz]{});
}
// Mark referenced thunk symbols
tbb::parallel_for_each(osec.members, [&](InputSection<E> *isec) {
std::span<ElfRel<E>> rels = isec->get_rels(ctx);
std::span<RangeExtensionRef> range_extn = isec->get_range_extn();
for (i64 i = 0; i < rels.size(); i++) {
RangeExtensionRef &ref = range_extn[i];
if (ref.thunk_idx == -1)
continue;
Symbol<E> &sym = *isec->file.symbols[rels[i].r_sym];
if (!is_reachable(ctx, sym, *isec, rels[i]))
osec.thunks[ref.thunk_idx]->used[ref.sym_idx] = true;
}
});
// Remove unreferenced thunk symbols
tbb::parallel_for_each(osec.thunks,
[&](std::unique_ptr<RangeExtensionThunk<E>> &thunk) {
i64 i = 0;
for (i64 j = 0; j < thunk->symbols.size(); j++) {
if (thunk->used[j]) {
thunk->symbol_map[j] = i;
thunk->symbols[i] = thunk->symbols[j];
i++;
}
}
thunk->symbols.resize(i);
});
}
static void shrink_section(Context<E> &ctx, OutputSection<E> &osec) {
std::span<std::unique_ptr<RangeExtensionThunk<E>>> thunks = osec.thunks;
std::span<InputSection<E> *> members = osec.members;
i64 offset = 0;
auto add_thunk = [&] {
thunks[0]->offset = offset;
offset += thunks[0]->size();
thunks = thunks.subspan(1);
};
auto add_isec = [&] {
offset = align_to(offset, 1 << members[0]->p2align);
members[0]->offset = offset;
offset += members[0]->sh_size;
members = members.subspan(1);
};
while (!thunks.empty() && !members.empty()) {
if (thunks[0]->offset < members[0]->offset)
add_thunk();
else
add_isec();
}
while (!thunks.empty())
add_thunk();
while (!members.empty())
add_isec();
assert(offset <= osec.shdr.sh_size);
osec.shdr.sh_size = offset;
}
// ARM64's call/jump instructions take 27 bits displacement, so they
// can refer only up to ±128 MiB. If a branch target is further than
// that, we need to let it branch to a linker-synthesized code
// sequence that construct a full 32 bit address in a register and
// jump there. That linker-synthesized code is called "thunk".
i64 create_range_extension_thunks(Context<E> &ctx) {
Timer t(ctx, "create_range_extension_thunks");
for (ObjectFile<E> *file : ctx.objs)
file->range_extn.resize(file->sections.size());
// First, we create thunks with a pessimistic assumption that all
// out-of-section relocations would need thunks. To do so, we start
// with an initial layout in which output sections are separated far
// apart.
for (i64 i = 0; Chunk<E> *chunk : ctx.chunks)
if (chunk->shdr.sh_flags & SHF_ALLOC)
chunk->shdr.sh_addr = i++ << 31;
std::vector<OutputSection<E> *> sections;
for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections)
if (!osec->members.empty() && (osec->shdr.sh_flags & SHF_EXECINSTR))
sections.push_back(osec.get());
for (OutputSection<E> *osec : sections)
create_thunks(ctx, *osec);
// Recompute file layout.
set_osec_offsets(ctx);
// Based on the current file layout, remove thunk symbols that turned
// out to be unnecessary.
tbb::parallel_for_each(sections, [&](OutputSection<E> *osec) {
gc_thunk_symbols(ctx, *osec);
});
// Recompute output section sizes that contain thunks. New section
// sizes must be equal to or smaller than previous values, so all
// relocations that were previously reachable will still be reachable
// after this step.
for (OutputSection<E> *osec : sections)
shrink_section(ctx, *osec);
// Compute the final layout.
return set_osec_offsets(ctx);
}
template <>
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + output_section.shdr.sh_offset + offset;
@@ -770,29 +501,17 @@ void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
0xd61f0200, // br x16
};
static_assert(ENTRY_SIZE == sizeof(data));
static_assert(E::thunk_size == sizeof(data));
for (i64 i = 0; i < symbols.size(); i++) {
u64 S = symbols[i]->get_addr(ctx);
u64 P = output_section.shdr.sh_addr + offset + i * ENTRY_SIZE;
u64 P = output_section.shdr.sh_addr + offset + i * E::thunk_size;
u8 *loc = buf + i * ENTRY_SIZE;
u8 *loc = buf + i * E::thunk_size;
memcpy(loc , data, sizeof(data));
write_adr(loc, bits(page(S) - page(P), 32, 12));
*(u32 *)(loc + 4) |= bits(S, 11, 0) << 10;
write_adrp(loc, page(S) - page(P));
*(ul32 *)(loc + 4) |= bits(S, 11, 0) << 10;
}
}
void write_thunks(Context<E> &ctx) {
Timer t(ctx, "write_thunks");
tbb::parallel_for_each(ctx.output_sections,
[&](std::unique_ptr<OutputSection<E>> &osec) {
tbb::parallel_for_each(osec->thunks,
[&](std::unique_ptr<RangeExtensionThunk<E>> &thunk) {
thunk->copy_buf(ctx);
});
});
}
} // namespace mold::elf
+107 -174
View File
@@ -4,6 +4,10 @@ namespace mold::elf {
using E = I386;
// Emitting position-independent code (PIC) for i386 is a bit tricky
// because i386 doesn't support PC-relative memory access instructions.
// By default, i386 executables are not PIC. If PIC, %ebx is used to
// store the location of .got and access all data with offsets from .got.
static void write_plt_header(Context<E> &ctx, u8 *buf) {
if (ctx.arg.pic) {
static const u8 plt0[] = {
@@ -12,8 +16,8 @@ static void write_plt_header(Context<E> &ctx, u8 *buf) {
0x90, 0x90, 0x90, 0x90, // nop
};
memcpy(buf, plt0, sizeof(plt0));
*(u32 *)(buf + 2) = ctx.gotplt->shdr.sh_addr - ctx.got->shdr.sh_addr + 4;
*(u32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr - ctx.got->shdr.sh_addr + 8;
*(ul32 *)(buf + 2) = ctx.gotplt->shdr.sh_addr - ctx.got->shdr.sh_addr + 4;
*(ul32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr - ctx.got->shdr.sh_addr + 8;
} else {
static const u8 plt0[] = {
0xff, 0x35, 0, 0, 0, 0, // pushl GOTPLT+4
@@ -21,14 +25,14 @@ static void write_plt_header(Context<E> &ctx, u8 *buf) {
0x90, 0x90, 0x90, 0x90, // nop
};
memcpy(buf, plt0, sizeof(plt0));
*(u32 *)(buf + 2) = ctx.gotplt->shdr.sh_addr + 4;
*(u32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr + 8;
*(ul32 *)(buf + 2) = ctx.gotplt->shdr.sh_addr + 4;
*(ul32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr + 8;
}
}
static void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym,
i64 idx) {
u8 *ent = buf + ctx.plt_hdr_size + sym.get_plt_idx(ctx) * ctx.plt_size;
u8 *ent = buf + E::plt_hdr_size + sym.get_plt_idx(ctx) * E::plt_size;
if (ctx.arg.pic) {
static const u8 data[] = {
@@ -37,7 +41,7 @@ static void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym,
0xe9, 0, 0, 0, 0, // jmp .PLT0@PC
};
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = sym.get_gotplt_addr(ctx) - ctx.got->shdr.sh_addr;
*(ul32 *)(ent + 2) = sym.get_gotplt_addr(ctx) - ctx.got->shdr.sh_addr;
} else {
static const u8 data[] = {
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
@@ -45,11 +49,11 @@ static void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym,
0xe9, 0, 0, 0, 0, // jmp .PLT0@PC
};
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = sym.get_gotplt_addr(ctx);
*(ul32 *)(ent + 2) = sym.get_gotplt_addr(ctx);
}
*(u32 *)(ent + 7) = idx * sizeof(ElfRel<E>);
*(u32 *)(ent + 12) = ctx.plt->shdr.sh_addr - sym.get_plt_addr(ctx) - 16;
*(ul32 *)(ent + 7) = idx * sizeof(ElfRel<E>);
*(ul32 *)(ent + 12) = ctx.plt->shdr.sh_addr - sym.get_plt_addr(ctx) - 16;
}
template <>
@@ -74,7 +78,7 @@ void PltGotSection<E>::copy_buf(Context<E> &ctx) {
for (i64 i = 0; i < symbols.size(); i++) {
u8 *ent = buf + i * sizeof(data);
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = symbols[i]->get_got_addr(ctx) - ctx.got->shdr.sh_addr;
*(ul32 *)(ent + 2) = symbols[i]->get_got_addr(ctx) - ctx.got->shdr.sh_addr;
}
} else {
static const u8 data[] = {
@@ -85,13 +89,13 @@ void PltGotSection<E>::copy_buf(Context<E> &ctx) {
for (i64 i = 0; i < symbols.size(); i++) {
u8 *ent = buf + i * sizeof(data);
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = symbols[i]->get_got_addr(ctx);
*(ul32 *)(ent + 2) = symbols[i]->get_got_addr(ctx);
}
}
}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
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;
@@ -99,10 +103,10 @@ void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
case R_386_NONE:
return;
case R_386_32:
*(u32 *)loc = val;
*(ul32 *)loc = val;
return;
case R_386_PC32:
*(u32 *)loc = val - this->shdr.sh_addr - offset;
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
return;
}
unreachable();
@@ -111,7 +115,7 @@ void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
ElfRel<E> *dynrel = nullptr;
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
i64 frag_idx = 0;
if (ctx.reldyn)
@@ -137,90 +141,62 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
<< lo << ", " << hi << ")";
};
auto write8 = [&](u64 val) {
overflow_check(val, 0, 1 << 8);
*loc = val;
};
auto write8s = [&](u64 val) {
overflow_check(val, -(1 << 7), 1 << 7);
*loc = val;
};
auto write16 = [&](u64 val) {
overflow_check(val, 0, 1 << 16);
*(u16 *)loc = val;
};
auto write16s = [&](u64 val) {
overflow_check(val, -(1 << 15), 1 << 15);
*(u16 *)loc = val;
};
auto write32 = [&](u64 val) {
*(u32 *)loc = val;
};
#define S (frag_ref ? frag_ref->frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag_ref ? frag_ref->addend : this->get_addend(rel))
#define P (output_section->shdr.sh_addr + offset + rel.r_offset)
#define G (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
#define G (sym.get_got_idx(ctx) * sizeof(Word<E>))
#define GOT ctx.got->shdr.sh_addr
switch (rel.r_type) {
case R_386_8:
write8(S + A);
case R_386_8: {
i64 val = S + A;
overflow_check(val, 0, 1 << 8);
*loc = val;
continue;
case R_386_16:
write16(S + A);
}
case R_386_16: {
i64 val = S + A;
overflow_check(val, 0, 1 << 16);
*(ul16 *)loc = val;
continue;
}
case R_386_32:
if (sym.is_absolute() || !ctx.arg.pic) {
write32(S + A);
} else if (sym.is_imported) {
*dynrel++ = {P, R_386_32, (u32)sym.get_dynsym_idx(ctx)};
write32(A);
} else {
if (!is_relr_reloc(ctx, rel))
*dynrel++ = {P, R_386_RELATIVE, 0};
write32(S + A);
}
apply_abs_dyn_rel(ctx, sym, rel, loc, S, A, P, dynrel);
continue;
case R_386_PC8:
write8s(S + A);
case R_386_PC8: {
i64 val = S + A - P;
overflow_check(val, -(1 << 7), 1 << 7);
*loc = val;
continue;
case R_386_PC16:
write16s(S + A);
};
case R_386_PC16: {
i64 val = S + A - P;
overflow_check(val, -(1 << 15), 1 << 15);
*(ul16 *)loc = val;
continue;
}
case R_386_PC32:
if (sym.is_absolute() || !sym.is_imported || !ctx.arg.shared) {
write32(S + A - P);
} else {
*dynrel++ = {P, R_386_32, (u32)sym.get_dynsym_idx(ctx)};
write32(A);
}
continue;
case R_386_PLT32:
write32(S + A - P);
*(ul32 *)loc = S + A - P;
continue;
case R_386_GOT32:
case R_386_GOT32X:
write32(G + A);
*(ul32 *)loc = G + A;
continue;
case R_386_GOTOFF:
write32(S + A - GOT);
*(ul32 *)loc = S + A - GOT;
continue;
case R_386_GOTPC:
write32(GOT + A - P);
*(ul32 *)loc = GOT + A - P;
continue;
case R_386_TLS_GOTIE:
write32(sym.get_gottp_addr(ctx) + A - GOT);
*(ul32 *)loc = sym.get_gottp_addr(ctx) + A - GOT;
continue;
case R_386_TLS_LE:
write32(S + A - ctx.tls_end);
*(ul32 *)loc = S + A - ctx.tls_end;
continue;
case R_386_TLS_IE:
write32(sym.get_gottp_addr(ctx) + A);
*(ul32 *)loc = sym.get_gottp_addr(ctx) + A;
continue;
case R_386_TLS_GD:
if (sym.get_tlsgd_idx(ctx) == -1) {
@@ -232,7 +208,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
0x81, 0xe8, 0, 0, 0, 0, // add $0, %rax
};
memcpy(loc - 3, insn, sizeof(insn));
*(u32 *)(loc + 5) = ctx.tls_end - S - A;
*(ul32 *)(loc + 5) = ctx.tls_end - S - A;
break;
}
case R_386_GOT32: {
@@ -241,7 +217,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
0x81, 0xe8, 0, 0, 0, 0, // add $0, %rax
};
memcpy(loc - 2, insn, sizeof(insn));
*(u32 *)(loc + 6) = ctx.tls_end - S - A;
*(ul32 *)(loc + 6) = ctx.tls_end - S - A;
break;
}
default:
@@ -250,7 +226,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
i++;
} else {
write32(sym.get_tlsgd_addr(ctx) + A - GOT);
*(ul32 *)loc = sym.get_tlsgd_addr(ctx) + A - GOT;
}
continue;
case R_386_TLS_LDM:
@@ -281,17 +257,17 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
i++;
} else {
write32(ctx.got->get_tlsld_addr(ctx) + A - GOT);
*(ul32 *)loc = ctx.got->get_tlsld_addr(ctx) + A - GOT;
}
continue;
case R_386_TLS_LDO_32:
if (ctx.got->tlsld_idx == -1)
write32(S + A - ctx.tls_end);
*(ul32 *)loc = S + A - ctx.tls_end;
else
write32(S + A - ctx.tls_begin);
*(ul32 *)loc = S + A - ctx.tls_begin;
continue;
case R_386_SIZE32:
write32(sym.esym().st_size + A);
*(ul32 *)loc = sym.esym().st_size + A;
continue;
case R_386_TLS_GOTDESC:
if (sym.get_tlsdesc_idx(ctx) == -1) {
@@ -299,9 +275,9 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
0x8d, 0x05, 0, 0, 0, 0, // lea 0, %eax
};
memcpy(loc - 2, insn, sizeof(insn));
write32(S + A - ctx.tls_end);
*(ul32 *)loc = S + A - ctx.tls_end;
} else {
write32(sym.get_tlsdesc_addr(ctx) + A - GOT);
*(ul32 *)loc = sym.get_tlsdesc_addr(ctx) + A - GOT;
}
continue;
case R_386_TLS_DESC_CALL:
@@ -325,7 +301,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
@@ -336,7 +312,7 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
u8 *loc = base + rel.r_offset;
if (!sym.file) {
report_undef(ctx, file, sym);
record_undef_error(ctx, rel);
continue;
}
@@ -351,67 +327,62 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
<< lo << ", " << hi << ")";
};
auto write8 = [&](u64 val) {
overflow_check(val, 0, 1 << 8);
*loc = val;
};
auto write8s = [&](u64 val) {
overflow_check(val, -(1 << 7), 1 << 7);
*loc = val;
};
auto write16 = [&](u64 val) {
overflow_check(val, 0, 1 << 16);
*(u16 *)loc = val;
};
auto write16s = [&](u64 val) {
overflow_check(val, -(1 << 15), 1 << 15);
*(u16 *)loc = val;
};
#define S (frag ? frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag ? addend : this->get_addend(rel))
#define G (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
#define G (sym.get_got_idx(ctx) * sizeof(Word<E>))
#define GOT ctx.got->shdr.sh_addr
switch (rel.r_type) {
case R_386_8:
write8(S + A);
case R_386_8: {
i64 val = S + A;
overflow_check(val, 0, 1 << 8);
*loc = val;
continue;
case R_386_16:
write16(S + A);
}
case R_386_16: {
i64 val = S + A;
overflow_check(val, 0, 1 << 16);
*(ul16 *)loc = val;
continue;
}
case R_386_32:
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
else
*(u32 *)loc = S + A;
if (!frag) {
if (std::optional<u64> val = get_tombstone(sym)) {
*(ul32 *)loc = *val;
continue;
}
}
*(ul32 *)loc = S + A;
continue;
case R_386_PC8:
write8s(S + A);
case R_386_PC8: {
i64 val = S + A;
overflow_check(val, -(1 << 7), 1 << 7);
*loc = val;
continue;
case R_386_PC16:
write16s(S + A);
}
case R_386_PC16: {
i64 val = S + A;
overflow_check(val, -(1 << 15), 1 << 15);
*(ul16 *)loc = val;
continue;
};
case R_386_PC32:
*(u32 *)loc = S + A;
*(ul32 *)loc = S + A;
continue;
case R_386_GOTPC:
*(u32 *)loc = GOT + A;
*(ul32 *)loc = GOT + A;
continue;
case R_386_GOTOFF:
*(u32 *)loc = S + A - GOT;
*(ul32 *)loc = S + A - GOT;
continue;
case R_386_TLS_LDO_32:
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
*(ul32 *)loc = *val;
else
*(u32 *)loc = S + A - ctx.tls_begin;
*(ul32 *)loc = S + A - ctx.tls_begin;
continue;
case R_386_SIZE32:
*(u32 *)loc = sym.esym().st_size + A;
*(ul32 *)loc = sym.esym().st_size + A;
continue;
default:
unreachable();
@@ -428,7 +399,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
@@ -439,73 +410,35 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (!sym.file) {
report_undef(ctx, file, sym);
record_undef_error(ctx, rel);
continue;
}
if (sym.get_type() == STT_GNU_IFUNC) {
sym.flags |= NEEDS_GOT;
sym.flags |= NEEDS_PLT;
}
if (sym.get_type() == STT_GNU_IFUNC)
sym.flags |= (NEEDS_GOT | NEEDS_PLT);
switch (rel.r_type) {
case R_386_8:
case R_386_16: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, ERROR, ERROR, ERROR }, // DSO
{ NONE, ERROR, ERROR, ERROR }, // PIE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
case R_386_16:
scan_abs_rel(ctx, sym, rel);
break;
}
case R_386_32: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, BASEREL, DYNREL, DYNREL }, // DSO
{ NONE, BASEREL, DYNREL, DYNREL }, // PIE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
case R_386_32:
scan_abs_dyn_rel(ctx, sym, rel);
break;
}
case R_386_PC8:
case R_386_PC16: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ ERROR, NONE, ERROR, ERROR }, // DSO
{ ERROR, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
case R_386_PC16:
case R_386_PC32:
scan_pcrel_rel(ctx, sym, rel);
break;
}
case R_386_PC32: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ ERROR, NONE, DYNREL, DYNREL }, // DSO
{ ERROR, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
case R_386_GOT32:
case R_386_GOT32X:
case R_386_GOTPC:
sym.flags |= NEEDS_GOT;
break;
case R_386_PLT32: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, NONE, PLT, PLT }, // DSO
{ NONE, NONE, PLT, PLT }, // PIE
{ NONE, NONE, PLT, PLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
case R_386_PLT32:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
}
case R_386_TLS_GOTIE:
case R_386_TLS_LE:
case R_386_TLS_IE:
File diff suppressed because it is too large. Load diff
+72 -189
View File
@@ -4,38 +4,22 @@ namespace mold::elf {
using E = X86_64;
// The compact PLT format is used when `-z now` is given. If the flag
// is given, all PLT symbols are resolved eagerly on startup, so we
// can omit code for lazy symbol resolution from PLT in that case.
static void write_compact_plt(Context<E> &ctx) {
u8 *buf = ctx.buf + ctx.plt->shdr.sh_offset;
static const u8 data[] = {
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
0x66, 0x90, // nop
};
for (Symbol<E> *sym : ctx.plt->symbols) {
u8 *ent = buf + sym->get_plt_idx(ctx) * ctx.plt_size;
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = sym->get_gotplt_addr(ctx) - sym->get_plt_addr(ctx) - 6;
}
}
// The IBTPLT is a security-enhanced version of the regular PLT.
// It uses Indirect Branch Tracking (IBT) feature which is part of
// Intel Control-Flow Enforcement (CET).
// This is a security-enhanced version of the regular PLT. The PLT
// header and each PLT entry starts with endbr64 for the Intel's
// control-flow enforcement security mechanism.
//
// Note that our IBTPLT instruction sequence is different from the one
// used in GNU ld. GNU's IBTPLT implementation uses two separate
// sections (.plt and .plt.sec) in which one PLT entry takes 32 bytes
// in total. Our PLT consists of just .plt and each entry is 16 bytes
// long.
// Note that our IBT-enabled PLT instruction sequence is different
// from the one used in GNU ld. GNU's IBTPLT implementation uses two
// separate sections (.plt and .plt.sec) in which one PLT entry takes
// 32 bytes in total. Our IBTPLT consists of just .plt and each entry
// is 16 bytes long.
//
// Our PLT entry clobbers r11, but that's fine because the resolver
// function (_dl_runtime_resolve) does not preserve r11 anyway.
static void write_ibtplt(Context<E> &ctx) {
u8 *buf = ctx.buf + ctx.plt->shdr.sh_offset;
// Our PLT entry clobbers %r11, but that's fine because the resolver
// function (_dl_runtime_resolve) clobbers %r11 anyway.
template <>
void PltSection<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + this->shdr.sh_offset;
memset(buf, 0xcc, this->shdr.sh_size);
// Write PLT header
static const u8 plt0[] = {
@@ -43,94 +27,47 @@ static void write_ibtplt(Context<E> &ctx) {
0x41, 0x53, // push %r11
0xff, 0x35, 0, 0, 0, 0, // push GOTPLT+8(%rip)
0xff, 0x25, 0, 0, 0, 0, // jmp *GOTPLT+16(%rip)
0x0f, 0x1f, 0x40, 0x00, // nop
0x0f, 0x1f, 0x40, 0x00, // nop
0x0f, 0x1f, 0x40, 0x00, // nop
0x66, 0x90, // nop
};
memcpy(buf, plt0, sizeof(plt0));
*(u32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 4;
*(u32 *)(buf + 14) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 2;
*(ul32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr - this->shdr.sh_addr - 4;
*(ul32 *)(buf + 14) = ctx.gotplt->shdr.sh_addr - this->shdr.sh_addr - 2;
// Write PLT entries
i64 relplt_idx = 0;
static const u8 data[] = {
0xf3, 0x0f, 0x1e, 0xfa, // endbr64
0x41, 0xbb, 0, 0, 0, 0, // mov $index_in_relplt, %r11d
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOTPLT
};
for (Symbol<E> *sym : ctx.plt->symbols) {
u8 *ent = buf + ctx.plt_hdr_size + sym->get_plt_idx(ctx) * ctx.plt_size;
for (Symbol<E> *sym : symbols) {
i64 idx = sym->get_plt_idx(ctx);
u8 *ent = buf + E::plt_hdr_size + idx * E::plt_size;
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 6) = relplt_idx++;
*(u32 *)(ent + 12) = sym->get_gotplt_addr(ctx) - sym->get_plt_addr(ctx) - 16;
*(ul32 *)(ent + 6) = idx;
*(ul32 *)(ent + 12) = sym->get_gotplt_addr(ctx) - sym->get_plt_addr(ctx) - 16;
}
}
// The regular PLT.
static void write_plt(Context<E> &ctx) {
u8 *buf = ctx.buf + ctx.plt->shdr.sh_offset;
// Write PLT header
static const u8 plt0[] = {
0xff, 0x35, 0, 0, 0, 0, // pushq GOTPLT+8(%rip)
0xff, 0x25, 0, 0, 0, 0, // jmp *GOTPLT+16(%rip)
0x0f, 0x1f, 0x40, 0x00, // nop
};
memcpy(buf, plt0, sizeof(plt0));
*(u32 *)(buf + 2) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr + 2;
*(u32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr + 4;
// Write PLT entries
i64 relplt_idx = 0;
static const u8 data[] = {
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOTPLT
0x68, 0, 0, 0, 0, // push $index_in_relplt
0xe9, 0, 0, 0, 0, // jmp PLT[0]
};
for (Symbol<E> *sym : ctx.plt->symbols) {
u8 *ent = buf + ctx.plt_hdr_size + sym->get_plt_idx(ctx) * ctx.plt_size;
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = sym->get_gotplt_addr(ctx) - sym->get_plt_addr(ctx) - 6;
*(u32 *)(ent + 7) = relplt_idx++;
*(u32 *)(ent + 12) = ctx.plt->shdr.sh_addr - sym->get_plt_addr(ctx) - 16;
}
}
template <>
void PltSection<E>::copy_buf(Context<E> &ctx) {
if (ctx.arg.z_now)
write_compact_plt(ctx);
else if (ctx.arg.z_ibtplt)
write_ibtplt(ctx);
else
write_plt(ctx);
}
template <>
void PltGotSection<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + this->shdr.sh_offset;
memset(buf, 0xcc, this->shdr.sh_size);
static const u8 data[] = {
0xf3, 0x0f, 0x1e, 0xfa, // endbr64
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
0x66, 0x90, // nop
};
for (Symbol<E> *sym : symbols) {
u8 *ent = buf + sym->get_pltgot_idx(ctx) * X86_64::pltgot_size;
u8 *ent = buf + sym->get_pltgot_idx(ctx) * E::pltgot_size;
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = sym->get_got_addr(ctx) - sym->get_plt_addr(ctx) - 6;
*(ul32 *)(ent + 6) = sym->get_got_addr(ctx) - sym->get_plt_addr(ctx) - 10;
}
}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
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;
@@ -138,16 +75,16 @@ void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
case R_X86_64_NONE:
return;
case R_X86_64_32:
*(u32 *)loc = val;
*(ul32 *)loc = val;
return;
case R_X86_64_64:
*(u64 *)loc = val;
*(ul64 *)loc = val;
return;
case R_X86_64_PC32:
*(u32 *)loc = val - this->shdr.sh_addr - offset;
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
return;
case R_X86_64_PC64:
*(u64 *)loc = val - this->shdr.sh_addr - offset;
*(ul64 *)loc = val - this->shdr.sh_addr - offset;
return;
}
unreachable();
@@ -233,7 +170,7 @@ static u32 relax_gotpc32_tlsdesc(u8 *loc) {
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
ElfRel<E> *dynrel = nullptr;
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
i64 frag_idx = 0;
if (ctx.reldyn)
@@ -271,30 +208,30 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
auto write16 = [&](u64 val) {
overflow_check(val, 0, 1 << 16);
*(u16 *)loc = val;
*(ul16 *)loc = val;
};
auto write16s = [&](u64 val) {
overflow_check(val, -(1 << 15), 1 << 15);
*(u16 *)loc = val;
*(ul16 *)loc = val;
};
auto write32 = [&](u64 val) {
overflow_check(val, 0, (i64)1 << 32);
*(u32 *)loc = val;
*(ul32 *)loc = val;
};
auto write32s = [&](u64 val) {
overflow_check(val, -((i64)1 << 31), (i64)1 << 31);
*(u32 *)loc = val;
*(ul32 *)loc = val;
};
auto write64 = [&](u64 val) {
*(u64 *)loc = val;
*(ul64 *)loc = val;
};
#define S (frag_ref ? frag_ref->frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag_ref ? frag_ref->addend : rel.r_addend)
#define A (frag_ref ? (u64)frag_ref->addend : (u64)rel.r_addend)
#define P (output_section->shdr.sh_addr + offset + rel.r_offset)
#define G (sym.get_got_addr(ctx) - ctx.gotplt->shdr.sh_addr)
#define GOT ctx.gotplt->shdr.sh_addr
@@ -313,16 +250,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
write32s(S + A);
continue;
case R_X86_64_64:
if (sym.is_absolute() || !ctx.arg.pic) {
write64(S + A);
} else if (sym.is_imported) {
*dynrel++ = {P, R_X86_64_64, (u32)sym.get_dynsym_idx(ctx), A};
write64(A);
} else {
if (!is_relr_reloc(ctx, rel))
*dynrel++ = {P, R_X86_64_RELATIVE, 0, (i64)(S + A)};
write64(S + A);
}
apply_abs_dyn_rel(ctx, sym, rel, loc, S, A, P, dynrel);
continue;
case R_X86_64_PC8:
write8s(S + A - P);
@@ -334,12 +262,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
write32s(S + A - P);
continue;
case R_X86_64_PC64:
if (sym.is_absolute() || !sym.is_imported || !ctx.arg.shared) {
write64(S + A - P);
} else {
*dynrel++ = {P, R_X86_64_64, (u32)sym.get_dynsym_idx(ctx), A};
write64(A);
}
write64(S + A - P);
continue;
case R_X86_64_PLT32:
write32s(S + A - P);
@@ -404,7 +327,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea 0(%rax), %rax
};
memcpy(loc - 4, insn, sizeof(insn));
*(u32 *)(loc + 8) = val;
*(ul32 *)(loc + 8) = val;
break;
}
case R_X86_64_PLTOFF64: {
@@ -414,7 +337,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00, // nop
};
memcpy(loc - 3, insn, sizeof(insn));
*(u32 *)(loc + 9) = val;
*(ul32 *)(loc + 9) = val;
break;
}
default:
@@ -545,7 +468,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
// scan_relocations.
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
@@ -556,7 +479,7 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
u8 *loc = base + rel.r_offset;
if (!sym.file) {
report_undef(ctx, file, sym);
record_undef_error(ctx, rel);
continue;
}
@@ -578,21 +501,21 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
auto write16 = [&](u64 val) {
overflow_check(val, 0, 1 << 16);
*(u16 *)loc = val;
*(ul16 *)loc = val;
};
auto write32 = [&](u64 val) {
overflow_check(val, 0, (i64)1 << 32);
*(u32 *)loc = val;
*(ul32 *)loc = val;
};
auto write32s = [&](u64 val) {
overflow_check(val, -((i64)1 << 31), (i64)1 << 31);
*(u32 *)loc = val;
*(ul32 *)loc = val;
};
#define S (frag ? frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag ? addend : rel.r_addend)
#define A (frag ? (u64)addend : (u64)rel.r_addend)
switch (rel.r_type) {
case R_X86_64_8:
@@ -608,28 +531,31 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
write32s(S + A);
break;
case R_X86_64_64:
if (std::optional<u64> val = get_tombstone(sym))
*(u64 *)loc = *val;
else
*(u64 *)loc = S + A;
if (!frag) {
if (std::optional<u64> val = get_tombstone(sym)) {
*(ul64 *)loc = *val;
break;
}
}
*(ul64 *)loc = S + A;
break;
case R_X86_64_DTPOFF32:
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
*(ul32 *)loc = *val;
else
write32s(S + A - ctx.tls_begin);
break;
case R_X86_64_DTPOFF64:
if (std::optional<u64> val = get_tombstone(sym))
*(u64 *)loc = *val;
*(ul64 *)loc = *val;
else
*(u64 *)loc = S + A - ctx.tls_begin;
*(ul64 *)loc = S + A - ctx.tls_begin;
break;
case R_X86_64_SIZE32:
write32(sym.esym().st_size + A);
break;
case R_X86_64_SIZE64:
*(u64 *)loc = sym.esym().st_size + A;
*(ul64 *)loc = sym.esym().st_size + A;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
@@ -652,7 +578,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
@@ -664,66 +590,29 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
u8 *loc = (u8 *)(contents.data() + rel.r_offset);
if (!sym.file) {
report_undef(ctx, file, sym);
record_undef_error(ctx, rel);
continue;
}
if (sym.get_type() == STT_GNU_IFUNC) {
sym.flags |= NEEDS_GOT;
sym.flags |= NEEDS_PLT;
}
if (sym.get_type() == STT_GNU_IFUNC)
sym.flags |= (NEEDS_GOT | NEEDS_PLT);
switch (rel.r_type) {
case R_X86_64_8:
case R_X86_64_16:
case R_X86_64_32:
case R_X86_64_32S: {
// Dynamic linker does not support 8, 16 or 32-bit dynamic
// relocations for these types of relocations. We report an
// error if we cannot relocate them even at load-time.
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, ERROR, ERROR, ERROR }, // DSO
{ NONE, ERROR, ERROR, ERROR }, // PIE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
case R_X86_64_32S:
scan_abs_rel(ctx, sym, rel);
break;
}
case R_X86_64_64: {
// Unlike the above, we can use R_X86_64_RELATIVE and R_86_64_64
// relocations.
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, BASEREL, DYNREL, DYNREL }, // DSO
{ NONE, BASEREL, DYNREL, DYNREL }, // PIE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
case R_X86_64_64:
scan_abs_dyn_rel(ctx, sym, rel);
break;
}
case R_X86_64_PC8:
case R_X86_64_PC16:
case R_X86_64_PC32: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ ERROR, NONE, ERROR, ERROR }, // DSO
{ ERROR, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
case R_X86_64_PC32:
case R_X86_64_PC64:
scan_pcrel_rel(ctx, sym, rel);
break;
}
case R_X86_64_PC64: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ ERROR, NONE, DYNREL, DYNREL }, // DSO
{ ERROR, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
case R_X86_64_GOT32:
case R_X86_64_GOT64:
case R_X86_64_GOTPC32:
@@ -753,16 +642,10 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
break;
}
case R_X86_64_PLT32:
case R_X86_64_PLTOFF64: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, NONE, PLT, PLT }, // DSO
{ NONE, NONE, PLT, PLT }, // PIE
{ NONE, NONE, PLT, PLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
case R_X86_64_PLTOFF64:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
}
case R_X86_64_TLSGD: {
if (i + 1 == rels.size())
Fatal(ctx) << *this << ": TLSGD reloc must be followed by PLT or GOTPCREL";
+441 -464
View File
File diff suppressed because it is too large. Load diff
+561
View File
@@ -0,0 +1,561 @@
// 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 (*(ul32 *)data.data() == 0xffffffff)
Fatal(ctx) << *file.debug_info << ": --gdb-index: DWARF64 not supported";
i64 len = *(ul32 *)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 = *(ul32 *)contents.data() + 4;
u32 debug_info_offset = *(ul32 *)(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 = *(ul32 *)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 = *(ul16 *)(cu + 4);
u32 abbrev_offset;
// Skip a header.
switch (dwarf_version) {
case 2:
case 3:
case 4:
abbrev_offset = *(ul32 *)(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 = *(ul32 *)(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>
inline 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 = *(ul16 *)cu;
cu += 2;
return val;
}
case DW_FORM_strx3:
case DW_FORM_addrx3: {
u64 val = *(ul24 *)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 = *(ul32 *)cu;
cu += 4;
return val;
}
case DW_FORM_data8:
case DW_FORM_ref8: {
u64 val = *(ul64 *)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
// (until an end of list entry).
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
// (until an end of list entry).
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 + *(ul32 *)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 {};
}
#define INSTANTIATE(E) \
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)
INSTANTIATE_ALL;
} // namespace mold::elf
+337 -168
View File
@@ -1,5 +1,7 @@
#pragma once
#include "../inttypes.h"
#include <cstdint>
#include <ostream>
#include <string>
@@ -21,6 +23,7 @@ struct I386;
struct ARM64;
struct ARM32;
struct RISCV64;
struct RISCV32;
template <typename E> struct ElfSym;
template <typename E> struct ElfShdr;
@@ -30,6 +33,21 @@ template <typename E> struct ElfRel;
template <typename E> struct ElfDyn;
template <typename E> struct ElfChdr;
enum class MachineType { NONE, X86_64, I386, ARM64, ARM32, RISCV64, RISCV32 };
inline std::ostream &operator<<(std::ostream &out, MachineType mt) {
switch (mt) {
case MachineType::NONE: out << "none"; break;
case MachineType::X86_64: out << "x86_64"; break;
case MachineType::I386: out << "i386"; break;
case MachineType::ARM64: out << "arm64"; break;
case MachineType::ARM32: out << "arm32"; break;
case MachineType::RISCV64: out << "riscv64"; break;
case MachineType::RISCV32: out << "riscv32"; break;
}
return out;
}
template <typename E>
std::string rel_to_string(u32 r_type);
@@ -62,6 +80,7 @@ static constexpr u32 SHT_PREINIT_ARRAY = 16;
static constexpr u32 SHT_GROUP = 17;
static constexpr u32 SHT_SYMTAB_SHNDX = 18;
static constexpr u32 SHT_RELR = 19;
static constexpr u32 SHT_LLVM_ADDRSIG = 0x6fff4c03;
static constexpr u32 SHT_GNU_HASH = 0x6ffffff6;
static constexpr u32 SHT_GNU_VERDEF = 0x6ffffffd;
static constexpr u32 SHT_GNU_VERNEED = 0x6ffffffe;
@@ -80,6 +99,7 @@ static constexpr u32 SHF_LINK_ORDER = 0x80;
static constexpr u32 SHF_GROUP = 0x200;
static constexpr u32 SHF_TLS = 0x400;
static constexpr u32 SHF_COMPRESSED = 0x800;
static constexpr u32 SHF_GNU_RETAIN = 0x200000;
static constexpr u32 SHF_EXCLUDE = 0x80000000;
static constexpr u32 GRP_COMDAT = 1;
@@ -143,6 +163,7 @@ static constexpr u32 ELFCLASS64 = 2;
static constexpr u32 EV_CURRENT = 1;
static constexpr u32 EM_NONE = 0;
static constexpr u32 EM_386 = 3;
static constexpr u32 EM_ARM = 40;
static constexpr u32 EM_X86_64 = 62;
@@ -189,6 +210,8 @@ static constexpr u32 DT_INIT_ARRAYSZ = 27;
static constexpr u32 DT_FINI_ARRAYSZ = 28;
static constexpr u32 DT_RUNPATH = 29;
static constexpr u32 DT_FLAGS = 30;
static constexpr u32 DT_PREINIT_ARRAY = 32;
static constexpr u32 DT_PREINIT_ARRAYSZ = 33;
static constexpr u32 DT_RELRSZ = 35;
static constexpr u32 DT_RELR = 36;
static constexpr u32 DT_RELRENT = 37;
@@ -225,6 +248,7 @@ static constexpr u32 NT_GNU_HWCAP = 2;
static constexpr u32 NT_GNU_BUILD_ID = 3;
static constexpr u32 NT_GNU_GOLD_VERSION = 4;
static constexpr u32 NT_GNU_PROPERTY_TYPE_0 = 5;
static constexpr u32 NT_FDO_PACKAGING_METADATA = 0xcafe1a7e;
static constexpr u32 GNU_PROPERTY_AARCH64_FEATURE_1_AND = 0xc0000000;
static constexpr u32 GNU_PROPERTY_X86_FEATURE_1_AND = 0xc0000002;
@@ -960,8 +984,6 @@ static constexpr u32 R_RISCV_SUB8 = 37;
static constexpr u32 R_RISCV_SUB16 = 38;
static constexpr u32 R_RISCV_SUB32 = 39;
static constexpr u32 R_RISCV_SUB64 = 40;
static constexpr u32 R_RISCV_GNU_VTINHERIT = 41;
static constexpr u32 R_RISCV_GNU_VTENTRY = 42;
static constexpr u32 R_RISCV_ALIGN = 43;
static constexpr u32 R_RISCV_RVC_BRANCH = 44;
static constexpr u32 R_RISCV_RVC_JUMP = 45;
@@ -1015,8 +1037,6 @@ inline std::string rel_to_string<RISCV64>(u32 r_type) {
case R_RISCV_SUB16: return "R_RISCV_SUB16";
case R_RISCV_SUB32: return "R_RISCV_SUB32";
case R_RISCV_SUB64: return "R_RISCV_SUB64";
case R_RISCV_GNU_VTINHERIT: return "R_RISCV_GNU_VTINHERIT";
case R_RISCV_GNU_VTENTRY: return "R_RISCV_GNU_VTENTRY";
case R_RISCV_ALIGN: return "R_RISCV_ALIGN";
case R_RISCV_RVC_BRANCH: return "R_RISCV_RVC_BRANCH";
case R_RISCV_RVC_JUMP: return "R_RISCV_RVC_JUMP";
@@ -1033,6 +1053,11 @@ inline std::string rel_to_string<RISCV64>(u32 r_type) {
return "unknown (" + std::to_string(r_type) + ")";
}
template <>
inline std::string rel_to_string<RISCV32>(u32 r_type) {
return rel_to_string<RISCV64>(r_type);
}
static constexpr u32 DW_EH_PE_absptr = 0;
static constexpr u32 DW_EH_PE_omit = 0xff;
static constexpr u32 DW_EH_PE_uleb128 = 0x01;
@@ -1054,8 +1079,16 @@ static constexpr u32 DW_AT_low_pc = 0x11;
static constexpr u32 DW_AT_high_pc = 0x12;
static constexpr u32 DW_AT_producer = 0x25;
static constexpr u32 DW_AT_ranges = 0x55;
static constexpr u32 DW_AT_addr_base = 0x73;
static constexpr u32 DW_AT_rnglists_base = 0x74;
static constexpr u32 DW_TAG_compile_unit = 0x11;
static constexpr u32 DW_TAG_skeleton_unit = 0x4a;
static constexpr u32 DW_UT_compile = 0x01;
static constexpr u32 DW_UT_partial = 0x03;
static constexpr u32 DW_UT_skeleton = 0x04;
static constexpr u32 DW_UT_split_compile = 0x05;
static constexpr u32 DW_FORM_addr = 0x01;
static constexpr u32 DW_FORM_block2 = 0x03;
@@ -1081,6 +1114,34 @@ static constexpr u32 DW_FORM_indirect = 0x16;
static constexpr u32 DW_FORM_sec_offset = 0x17;
static constexpr u32 DW_FORM_exprloc = 0x18;
static constexpr u32 DW_FORM_flag_present = 0x19;
static constexpr u32 DW_FORM_strx = 0x1a;
static constexpr u32 DW_FORM_addrx = 0x1b;
static constexpr u32 DW_FORM_ref_sup4 = 0x1c;
static constexpr u32 DW_FORM_strp_sup = 0x1d;
static constexpr u32 DW_FORM_data16 = 0x1e;
static constexpr u32 DW_FORM_line_strp = 0x1f;
static constexpr u32 DW_FORM_ref_sig8 = 0x20;
static constexpr u32 DW_FORM_implicit_const = 0x21;
static constexpr u32 DW_FORM_loclistx = 0x22;
static constexpr u32 DW_FORM_rnglistx = 0x23;
static constexpr u32 DW_FORM_ref_sup8 = 0x24;
static constexpr u32 DW_FORM_strx1 = 0x25;
static constexpr u32 DW_FORM_strx2 = 0x26;
static constexpr u32 DW_FORM_strx3 = 0x27;
static constexpr u32 DW_FORM_strx4 = 0x28;
static constexpr u32 DW_FORM_addrx1 = 0x29;
static constexpr u32 DW_FORM_addrx2 = 0x2a;
static constexpr u32 DW_FORM_addrx3 = 0x2b;
static constexpr u32 DW_FORM_addrx4 = 0x2c;
static constexpr u32 DW_RLE_end_of_list = 0x00;
static constexpr u32 DW_RLE_base_addressx = 0x01;
static constexpr u32 DW_RLE_startx_endx = 0x02;
static constexpr u32 DW_RLE_startx_length = 0x03;
static constexpr u32 DW_RLE_offset_pair = 0x04;
static constexpr u32 DW_RLE_base_address = 0x05;
static constexpr u32 DW_RLE_start_end = 0x06;
static constexpr u32 DW_RLE_start_length = 0x07;
struct Elf64Sym {
bool is_defined() const { return !is_undef(); }
@@ -1088,22 +1149,15 @@ struct Elf64Sym {
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(); }
bool is_undef_strong() const {
return st_shndx == SHN_UNDEF && st_bind != STB_WEAK;
}
bool is_undef_weak() const {
return st_shndx == SHN_UNDEF && st_bind == STB_WEAK;
}
u32 st_name;
ul32 st_name;
u8 st_type : 4;
u8 st_bind : 4;
u8 st_visibility : 2;
u16 st_shndx;
u64 st_value;
u64 st_size;
ul16 st_shndx;
ul64 st_value;
ul64 st_size;
};
struct Elf32Sym {
@@ -1112,194 +1166,233 @@ struct Elf32Sym {
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(); }
bool is_undef_strong() const {
return st_shndx == SHN_UNDEF && st_bind != STB_WEAK;
}
bool is_undef_weak() const {
return st_shndx == SHN_UNDEF && st_bind == STB_WEAK;
}
u32 st_name;
u32 st_value;
u32 st_size;
ul32 st_name;
ul32 st_value;
ul32 st_size;
u8 st_type : 4;
u8 st_bind : 4;
u8 st_visibility : 2;
u16 st_shndx;
ul16 st_shndx;
};
struct Elf64Shdr {
u32 sh_name;
u32 sh_type;
u64 sh_flags;
u64 sh_addr;
u64 sh_offset;
u64 sh_size;
u32 sh_link;
u32 sh_info;
u64 sh_addralign;
u64 sh_entsize;
ul32 sh_name;
ul32 sh_type;
ul64 sh_flags;
ul64 sh_addr;
ul64 sh_offset;
ul64 sh_size;
ul32 sh_link;
ul32 sh_info;
ul64 sh_addralign;
ul64 sh_entsize;
};
struct Elf32Shdr {
u32 sh_name;
u32 sh_type;
u32 sh_flags;
u32 sh_addr;
u32 sh_offset;
u32 sh_size;
u32 sh_link;
u32 sh_info;
u32 sh_addralign;
u32 sh_entsize;
ul32 sh_name;
ul32 sh_type;
ul32 sh_flags;
ul32 sh_addr;
ul32 sh_offset;
ul32 sh_size;
ul32 sh_link;
ul32 sh_info;
ul32 sh_addralign;
ul32 sh_entsize;
};
struct Elf64Ehdr {
u8 e_ident[16];
u16 e_type;
u16 e_machine;
u32 e_version;
u64 e_entry;
u64 e_phoff;
u64 e_shoff;
u32 e_flags;
u16 e_ehsize;
u16 e_phentsize;
u16 e_phnum;
u16 e_shentsize;
u16 e_shnum;
u16 e_shstrndx;
ul16 e_type;
ul16 e_machine;
ul32 e_version;
ul64 e_entry;
ul64 e_phoff;
ul64 e_shoff;
ul32 e_flags;
ul16 e_ehsize;
ul16 e_phentsize;
ul16 e_phnum;
ul16 e_shentsize;
ul16 e_shnum;
ul16 e_shstrndx;
};
struct Elf32Ehdr {
u8 e_ident[16];
u16 e_type;
u16 e_machine;
u32 e_version;
u32 e_entry;
u32 e_phoff;
u32 e_shoff;
u32 e_flags;
u16 e_ehsize;
u16 e_phentsize;
u16 e_phnum;
u16 e_shentsize;
u16 e_shnum;
u16 e_shstrndx;
ul16 e_type;
ul16 e_machine;
ul32 e_version;
ul32 e_entry;
ul32 e_phoff;
ul32 e_shoff;
ul32 e_flags;
ul16 e_ehsize;
ul16 e_phentsize;
ul16 e_phnum;
ul16 e_shentsize;
ul16 e_shnum;
ul16 e_shstrndx;
};
struct Elf64Phdr {
u32 p_type;
u32 p_flags;
u64 p_offset;
u64 p_vaddr;
u64 p_paddr;
u64 p_filesz;
u64 p_memsz;
u64 p_align;
ul32 p_type;
ul32 p_flags;
ul64 p_offset;
ul64 p_vaddr;
ul64 p_paddr;
ul64 p_filesz;
ul64 p_memsz;
ul64 p_align;
};
struct Elf32Phdr {
u32 p_type;
u32 p_offset;
u32 p_vaddr;
u32 p_paddr;
u32 p_filesz;
u32 p_memsz;
u32 p_flags;
u32 p_align;
ul32 p_type;
ul32 p_offset;
ul32 p_vaddr;
ul32 p_paddr;
ul32 p_filesz;
ul32 p_memsz;
ul32 p_flags;
ul32 p_align;
};
// Depending on the target, ElfRel may or may not contain r_addend member.
// The relocation record containing r_addend is called RELA, and that
// without r_addend is called REL.
//
// If REL, relocation addends are stored as parts of section contents.
// That means we add a computed value to an existing value when writing a
// relocated value if REL. If RELA, we just overwrite an existing value
// with a newly computed value.
//
// We don't want to have too many `if (REL)`s and `if (RELA)`s in our
// codebase, so we write dynamic relocations in the following manner:
//
// - We always create a dynamic relocation with an addend. If it's REL,
// the addend will be discarded.
//
// - We also always write an addend to the relocated place even though
// it's redundant for RELA. If RELA, the written value will be
// ovewritten by the dynamic linker at load-time.
struct Elf64Rel {
u64 r_offset;
u32 r_type;
u32 r_sym;
Elf64Rel(u64 r_offset, u32 r_type, u32 r_sym, i64 r_addend = 0)
: r_offset(r_offset), r_type(r_type), r_sym(r_sym) {}
ul64 r_offset;
ul32 r_type;
ul32 r_sym;
};
struct Elf32Rel {
u32 r_offset;
u32 r_type : 8;
u32 r_sym : 24;
Elf32Rel(u64 r_offset, u32 r_type, u32 r_sym, i64 r_addend = 0)
: r_offset(r_offset), r_type(r_type), r_sym(r_sym) {}
ul32 r_offset;
u8 r_type;
ul24 r_sym;
};
struct Elf64Rela {
u64 r_offset;
u32 r_type;
u32 r_sym;
i64 r_addend;
Elf64Rela(u64 r_offset, u32 r_type, u32 r_sym, i64 r_addend)
: r_offset(r_offset), r_type(r_type), r_sym(r_sym), r_addend(r_addend) {}
ul64 r_offset;
ul32 r_type;
ul32 r_sym;
il64 r_addend;
};
struct Elf32Rela {
u32 r_offset;
u32 r_type : 8;
u32 r_sym : 24;
i32 r_addend;
Elf32Rela(u64 r_offset, u32 r_type, u32 r_sym, i64 r_addend)
: r_offset(r_offset), r_type(r_type), r_sym(r_sym), r_addend(r_addend) {}
ul32 r_offset;
u8 r_type;
ul24 r_sym;
il32 r_addend;
};
struct Elf64Dyn {
u64 d_tag;
u64 d_val;
ul64 d_tag;
ul64 d_val;
};
struct Elf32Dyn {
u32 d_tag;
u32 d_val;
ul32 d_tag;
ul32 d_val;
};
struct ElfVerneed {
u16 vn_version;
u16 vn_cnt;
u32 vn_file;
u32 vn_aux;
u32 vn_next;
ul16 vn_version;
ul16 vn_cnt;
ul32 vn_file;
ul32 vn_aux;
ul32 vn_next;
};
struct ElfVernaux {
u32 vna_hash;
u16 vna_flags;
u16 vna_other;
u32 vna_name;
u32 vna_next;
ul32 vna_hash;
ul16 vna_flags;
ul16 vna_other;
ul32 vna_name;
ul32 vna_next;
};
struct ElfVerdef {
u16 vd_version;
u16 vd_flags;
u16 vd_ndx;
u16 vd_cnt;
u32 vd_hash;
u32 vd_aux;
u32 vd_next;
ul16 vd_version;
ul16 vd_flags;
ul16 vd_ndx;
ul16 vd_cnt;
ul32 vd_hash;
ul32 vd_aux;
ul32 vd_next;
};
struct ElfVerdaux {
u32 vda_name;
u32 vda_next;
ul32 vda_name;
ul32 vda_next;
};
struct Elf64Chdr {
u32 ch_type;
u32 ch_reserved;
u64 ch_size;
u64 ch_addralign;
ul32 ch_type;
ul32 ch_reserved;
ul64 ch_size;
ul64 ch_addralign;
};
struct Elf32Chdr {
u32 ch_type;
u32 ch_size;
u32 ch_addralign;
ul32 ch_type;
ul32 ch_size;
ul32 ch_addralign;
};
struct ElfNhdr {
u32 n_namesz;
u32 n_descsz;
u32 n_type;
ul32 n_namesz;
ul32 n_descsz;
ul32 n_type;
};
template <typename E>
inline constexpr bool is_rela = requires(ElfRel<E> r) { r.r_addend; };
template <typename E>
inline constexpr bool supports_tlsdesc = requires { E::R_TLSDESC; };
template <typename E>
inline constexpr bool needs_thunk = requires { E::thunk_size; };
template <typename E>
inline constexpr bool is_riscv =
std::is_same_v<E, RISCV64> || std::is_same_v<E, RISCV32>;
template <typename E>
using Word = typename E::Word;
struct X86_64 {
using WordTy = u64;
using Word = ul64;
static constexpr u32 R_NONE = R_X86_64_NONE;
static constexpr u32 R_COPY = R_X86_64_COPY;
@@ -1313,25 +1406,25 @@ struct X86_64 {
static constexpr u32 R_DTPMOD = R_X86_64_DTPMOD64;
static constexpr u32 R_TLSDESC = R_X86_64_TLSDESC;
static constexpr u32 word_size = 8;
static constexpr MachineType machine_type = MachineType::X86_64;
static constexpr u32 page_size = 4096;
static constexpr u32 e_machine = EM_X86_64;
static constexpr u32 pltgot_size = 8;
static constexpr bool is_rel = false;
static constexpr bool is_le = true;
static constexpr bool supports_tlsdesc = true;
static constexpr u32 plt_hdr_size = 32;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 16;
static constexpr u32 tls_dtv_offset = 0;
};
template <> struct ElfSym<X86_64> : public Elf64Sym {};
template <> struct ElfShdr<X86_64> : public Elf64Shdr {};
template <> struct ElfEhdr<X86_64> : public Elf64Ehdr {};
template <> struct ElfPhdr<X86_64> : public Elf64Phdr {};
template <> struct ElfRel<X86_64> : public Elf64Rela {};
template <> struct ElfRel<X86_64> : public Elf64Rela { using Elf64Rela::Elf64Rela; };
template <> struct ElfDyn<X86_64> : public Elf64Dyn {};
template <> struct ElfChdr<X86_64> : public Elf64Chdr {};
struct I386 {
using WordTy = u32;
using Word = ul32;
static constexpr u32 R_NONE = R_386_NONE;
static constexpr u32 R_COPY = R_386_COPY;
@@ -1345,25 +1438,25 @@ struct I386 {
static constexpr u32 R_DTPMOD = R_386_TLS_DTPMOD32;
static constexpr u32 R_TLSDESC = R_386_TLS_DESC;
static constexpr u32 word_size = 4;
static constexpr MachineType machine_type = MachineType::I386;
static constexpr u32 page_size = 4096;
static constexpr u32 e_machine = EM_386;
static constexpr u32 plt_hdr_size = 16;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 8;
static constexpr bool is_rel = true;
static constexpr bool is_le = true;
static constexpr bool supports_tlsdesc = true;
static constexpr u32 tls_dtv_offset = 0;
};
template <> struct ElfSym<I386> : public Elf32Sym {};
template <> struct ElfShdr<I386> : public Elf32Shdr {};
template <> struct ElfEhdr<I386> : public Elf32Ehdr {};
template <> struct ElfPhdr<I386> : public Elf32Phdr {};
template <> struct ElfRel<I386> : public Elf32Rel {};
template <> struct ElfRel<I386> : public Elf32Rel { using Elf32Rel::Elf32Rel; };
template <> struct ElfDyn<I386> : public Elf32Dyn {};
template <> struct ElfChdr<I386> : public Elf32Chdr {};
struct ARM64 {
using WordTy = u64;
using Word = ul64;
static constexpr u32 R_NONE = R_AARCH64_NONE;
static constexpr u32 R_COPY = R_AARCH64_COPY;
@@ -1377,25 +1470,40 @@ struct ARM64 {
static constexpr u32 R_DTPMOD = R_AARCH64_TLS_DTPMOD64;
static constexpr u32 R_TLSDESC = R_AARCH64_TLSDESC;
static constexpr u32 word_size = 8;
static constexpr MachineType machine_type = MachineType::ARM64;
static constexpr u32 page_size = 65536;
static constexpr u32 e_machine = EM_AARCH64;
static constexpr u32 plt_hdr_size = 32;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 16;
static constexpr bool is_rel = false;
static constexpr bool is_le = true;
static constexpr bool supports_tlsdesc = true;
// Each thread has its own value in TP (thread pointer) register, and
// TLVs defined in the main executable are accessed relative to TP.
// ARM runtime reserves two words right at TP, so the TLVs start at
// TP + 16 (or TP + 8 on ARM32).
static constexpr u32 tls_tp_offset = 16;
static constexpr u32 tls_dtv_offset = 0;
// For ARM, we need to insert a piece of code between a function call
// site and the callee if they are not reachable with a single branch
// instruction. Here are parameters for the thunk creation function.
static constexpr u32 thunk_hdr_size = 0;
static constexpr u32 thunk_size = 12;
static constexpr u32 thunk_max_distance = 100 * 1024 * 1024;
static constexpr u32 thunk_group_size = 10 * 1024 * 1024;
};
template <> struct ElfSym<ARM64> : public Elf64Sym {};
template <> struct ElfShdr<ARM64> : public Elf64Shdr {};
template <> struct ElfEhdr<ARM64> : public Elf64Ehdr {};
template <> struct ElfPhdr<ARM64> : public Elf64Phdr {};
template <> struct ElfRel<ARM64> : public Elf64Rela {};
template <> struct ElfRel<ARM64> : public Elf64Rela { using Elf64Rela::Elf64Rela; };
template <> struct ElfDyn<ARM64> : public Elf64Dyn {};
template <> struct ElfChdr<ARM64> : public Elf64Chdr {};
struct ARM32 {
using WordTy = u32;
using Word = ul32;
static constexpr u32 R_NONE = R_ARM_NONE;
static constexpr u32 R_COPY = R_ARM_COPY;
@@ -1409,25 +1517,31 @@ struct ARM32 {
static constexpr u32 R_DTPMOD = R_ARM_TLS_DTPMOD32;
static constexpr u32 R_TLSDESC = R_ARM_TLS_DESC;
static constexpr u32 word_size = 4;
static constexpr MachineType machine_type = MachineType::ARM32;
static constexpr u32 page_size = 4096;
static constexpr u32 e_machine = EM_ARM;
static constexpr u32 plt_hdr_size = 32;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 16;
static constexpr bool is_rel = true;
static constexpr bool is_le = true;
static constexpr bool supports_tlsdesc = true;
static constexpr u32 tls_tp_offset = 8;
static constexpr u32 tls_dtv_offset = 0;
static constexpr u32 thunk_hdr_size = 12;
static constexpr u32 thunk_size = 20;
static constexpr u32 thunk_max_distance = 10 * 1024 * 1024;
static constexpr u32 thunk_group_size = 2 * 1024 * 1024;
};
template <> struct ElfSym<ARM32> : public Elf32Sym {};
template <> struct ElfShdr<ARM32> : public Elf32Shdr {};
template <> struct ElfEhdr<ARM32> : public Elf32Ehdr {};
template <> struct ElfPhdr<ARM32> : public Elf32Phdr {};
template <> struct ElfRel<ARM32> : public Elf32Rel {};
template <> struct ElfRel<ARM32> : public Elf32Rel { using Elf32Rel::Elf32Rel; };
template <> struct ElfDyn<ARM32> : public Elf32Dyn {};
template <> struct ElfChdr<ARM32> : public Elf32Chdr {};
struct RISCV64 {
using WordTy = u64;
using Word = ul64;
static constexpr u32 R_NONE = R_RISCV_NONE;
static constexpr u32 R_COPY = R_RISCV_COPY;
@@ -1440,21 +1554,76 @@ struct RISCV64 {
static constexpr u32 R_TPOFF = R_RISCV_TLS_TPREL64;
static constexpr u32 R_DTPMOD = R_RISCV_TLS_DTPMOD64;
static constexpr u32 word_size = 8;
static constexpr MachineType machine_type = MachineType::RISCV64;
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 bool is_rel = false;
static constexpr bool is_le = true;
static constexpr bool supports_tlsdesc = false;
static constexpr u32 tls_tp_offset = 0;
// When __tls_get_addr is called to resolve a thread-local variable's
// address, the following two arguments are passed to the function:
//
// 1. the module number that the variable belongs, and
// 2. the variable's offset within the module's TLS block.
//
// These values are usually computed by the dynamic linker and set to
// GOT slots as a result of resolving R_DTPMOD and R_DTPOFF dynamic
// relocations.
//
// On RISC-V, R_DTPOFF is resolved to the address 0x800 past the start
// of the TLS block. The bias maximizes the accessible range for
// load/store instructions with 12-bits signed immediates. That is, if
// the offset was right at the beginning of the start of the TLS block,
// the half of addressible space (negative immediates) would have been
// wasted.
//
// In most cases we don't have to think about the bias, as the DTPOFF
// values are usually computed and used only by runtime. But when we do
// compute DTPOFF for statically-linked executable, we need to offset
// the bias by subtracting 0x800.
static constexpr u32 tls_dtv_offset = 0x800;
};
template <> struct ElfSym<RISCV64> : public Elf64Sym {};
template <> struct ElfShdr<RISCV64> : public Elf64Shdr {};
template <> struct ElfEhdr<RISCV64> : public Elf64Ehdr {};
template <> struct ElfPhdr<RISCV64> : public Elf64Phdr {};
template <> struct ElfRel<RISCV64> : public Elf64Rela {};
template <> struct ElfRel<RISCV64> : public Elf64Rela { using Elf64Rela::Elf64Rela; };
template <> struct ElfDyn<RISCV64> : public Elf64Dyn {};
template <> struct ElfChdr<RISCV64> : public Elf64Chdr {};
struct RISCV32 {
using Word = ul32;
static constexpr u32 R_NONE = R_RISCV_NONE;
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;
static constexpr MachineType machine_type = MachineType::RISCV32;
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 tls_tp_offset = 0;
static constexpr u32 tls_dtv_offset = 0x800;
};
template <> struct ElfSym<RISCV32> : public Elf32Sym {};
template <> struct ElfShdr<RISCV32> : public Elf32Shdr {};
template <> struct ElfEhdr<RISCV32> : public Elf32Ehdr {};
template <> struct ElfPhdr<RISCV32> : public Elf32Phdr {};
template <> struct ElfRel<RISCV32> : public Elf32Rela { using Elf32Rela::Elf32Rela; };
template <> struct ElfDyn<RISCV32> : public Elf32Dyn {};
template <> struct ElfChdr<RISCV32> : public Elf32Chdr {};
} // namespace mold::elf
+15 -23
View File
@@ -26,13 +26,13 @@ static bool is_init_fini(const InputSection<E> &isec) {
template <typename E>
static bool mark_section(InputSection<E> *isec) {
return isec && isec->is_alive && !isec->extra().is_visited.exchange(true);
return isec && isec->is_alive && !isec->is_visited.exchange(true);
}
template <typename E>
static void visit(Context<E> &ctx, InputSection<E> *isec,
tbb::feeder<InputSection<E> *> &feeder, i64 depth) {
assert(isec->extra().is_visited);
assert(isec->is_visited);
// A relocation can refer either a section fragment (i.e. a piece of
// string in a mergeable string section) or a symbol. Mark all
@@ -45,12 +45,12 @@ static void visit(Context<E> &ctx, InputSection<E> *isec,
// describing how to handle exceptions for that function.
// We want to keep associated .eh_frame records.
for (FdeRecord<E> &fde : isec->get_fdes())
for (ElfRel<E> &rel : fde.get_rels(isec->file).subspan(1))
for (const ElfRel<E> &rel : fde.get_rels(isec->file).subspan(1))
if (Symbol<E> *sym = isec->file.symbols[rel.r_sym])
if (mark_section(sym->get_input_section()))
feeder.add(sym->get_input_section());
for (ElfRel<E> &rel : isec->get_rels(ctx)) {
for (const ElfRel<E> &rel : isec->get_rels(ctx)) {
Symbol<E> &sym = *isec->file.symbols[rel.r_sym];
// Symbol can refer either a section fragment or an input section.
@@ -73,10 +73,9 @@ static void visit(Context<E> &ctx, InputSection<E> *isec,
}
template <typename E>
static tbb::concurrent_vector<InputSection<E> *>
collect_root_set(Context<E> &ctx) {
static void collect_root_set(Context<E> &ctx,
tbb::concurrent_vector<InputSection<E> *> &rootset) {
Timer t(ctx, "collect_root_set");
tbb::concurrent_vector<InputSection<E> *> rootset;
auto enqueue_section = [&](InputSection<E> *isec) {
if (mark_section(isec))
@@ -102,11 +101,12 @@ collect_root_set(Context<E> &ctx) {
// reduce the amount of non-memory-mapped segments, you should
// use `strip` command, compile without debug info or use
// -strip-all linker option.
if (!(isec->shdr().sh_flags & SHF_ALLOC))
isec->extra().is_visited = true;
u32 flags = isec->shdr().sh_flags;
if (!(flags & SHF_ALLOC))
isec->is_visited = true;
if (is_init_fini(*isec) || is_c_identifier(isec->name()) ||
isec->shdr().sh_type == SHT_NOTE)
(flags & SHF_GNU_RETAIN) || isec->shdr().sh_type == SHT_NOTE)
enqueue_section(isec.get());
}
});
@@ -132,11 +132,9 @@ collect_root_set(Context<E> &ctx) {
// We just keep all CIEs and everything that are referenced by them.
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (CieRecord<E> &cie : file->cies)
for (ElfRel<E> &rel : cie.get_rels())
for (const ElfRel<E> &rel : cie.get_rels())
enqueue_symbol(file->symbols[rel.r_sym]);
});
return rootset;
}
// Mark all reachable sections
@@ -159,7 +157,7 @@ static void sweep(Context<E> &ctx) {
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (std::unique_ptr<InputSection<E>> &isec : file->sections) {
if (isec && isec->is_alive && !isec->extra().is_visited) {
if (isec && isec->is_alive && !isec->is_visited) {
if (ctx.arg.print_gc_sections)
SyncOut(ctx) << "removing unused section " << *isec;
isec->kill();
@@ -188,12 +186,10 @@ template <typename E>
void gc_sections(Context<E> &ctx) {
Timer t(ctx, "gc");
for (ObjectFile<E> *file : ctx.objs)
file->extras.resize(file->sections.size());
mark_nonalloc_fragments(ctx);
tbb::concurrent_vector<InputSection<E> *> rootset = collect_root_set(ctx);
tbb::concurrent_vector<InputSection<E> *> rootset;
collect_root_set(ctx, rootset);
mark(ctx, rootset);
sweep(ctx);
}
@@ -201,10 +197,6 @@ void gc_sections(Context<E> &ctx) {
#define INSTANTIATE(E) \
template void gc_sections(Context<E> &ctx);
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
INSTANTIATE_ALL;
} // namespace mold::elf
+92 -58
View File
@@ -40,22 +40,35 @@
// regular trees are equal. Given this formulation, we want to find as
// many identical vertices as possible.
//
// Solving such problem is computationally intensive, but mold is quite fast.
// For Chromium, mold's ICF finishes in less than 1 second with 20 threads.
// This is contrary to lld and gold, which take about 5 and 50 seconds to
// run ICF under the same condition, respectively.
// Just like a lot of problems with graph, this problem doesn't have a
// straightforward "optimal" solution, and we need to resort to heuristics.
//
// mold's ICF is faster because we are using a better algorithm.
// It's actually me who developed and implemented the lld's ICF algorithm,
// and I can say that mold's algorithm is better than that in all aspects.
// It scales better for number of available cores, require less overall
// computation, and has a smaller working set. So, it's better with a single
// thread and even better with multiple threads.
// mold approaches this problem by hashing program trees with increasing depth
// on each iteration.
// For example, when we start, we only hash individual functions with
// their call into other functions omitted. From the second iteration, we
// put the function they call into the hash by appending the hash of those
// functions from the previous iteration. This means that the nth iteration
// hashes call chain up to (n-1) levels deep.
// We use a cryptographic hash function, so the unique number of hashes will
// only monotonically increase as we take into account of deeper trees with
// iterations (otherwise, that means we have found a hash collision). We stop
// when the unique number of hashes stop increasing; this is based on the fact
// that once we observe an iteration with the same amount of unique hashes as
// the previous iteration, it will remain unchanged for further iterations.
// This is provable, but here we omit the proof for brevity.
//
// When compared to other approaches, mold's approach has a relatively cheaper
// cost per iteration, and as a bonus, is highly parallelizable.
// For Chromium, mold's ICF finishes in less than 1 second with 20 threads,
// whereas lld takes 5 seconds and gold takes 50 seconds under the same
// conditions.
#include "mold.h"
#include "../sha.h"
#include <array>
#include <cstdio>
#include <tbb/concurrent_unordered_map.h>
#include <tbb/concurrent_vector.h>
#include <tbb/enumerable_thread_specific.h>
@@ -98,12 +111,13 @@ static void uniquify_cies(Context<E> &ctx) {
}
template <typename E>
static bool is_eligible(InputSection<E> &isec) {
static bool is_eligible(Context<E> &ctx, InputSection<E> &isec) {
const ElfShdr<E> &shdr = isec.shdr();
std::string_view name = isec.name();
bool is_alloc = (shdr.sh_flags & SHF_ALLOC);
bool is_executable = (shdr.sh_flags & SHF_EXECINSTR);
bool is_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;
@@ -112,15 +126,15 @@ static bool is_eligible(InputSection<E> &isec) {
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;
return is_alloc && is_executable && is_readonly && !is_bss &&
!is_empty && !is_init && !is_fini && !is_enumerable;
return is_alloc && is_exec && is_readonly && !is_bss && !is_empty &&
!is_init && !is_fini && !is_enumerable && !is_addr_taken;
}
static Digest digest_final(SHA256_CTX &sha) {
static Digest digest_final(SHA256Hash &sha) {
u8 buf[SHA256_SIZE];
int res = SHA256_Final(buf, &sha);
assert(res == 1);
sha.finish(buf);
Digest digest;
memcpy(digest.data(), buf, HASH_SIZE);
@@ -175,6 +189,8 @@ struct LeafEq {
}
};
// Early merge of leaf nodes, which can be processed without constructing the
// entire graph. This reduces the vertex count and improves memory efficiency.
template <typename E>
static void merge_leaf_nodes(Context<E> &ctx) {
Timer t(ctx, "merge_leaf_nodes");
@@ -191,30 +207,30 @@ static void merge_leaf_nodes(Context<E> &ctx) {
if (!isec || !isec->is_alive)
continue;
if (!is_eligible(*isec)) {
if (!is_eligible(ctx, *isec)) {
non_eligible++;
continue;
}
if (is_leaf(ctx, *isec)) {
leaf++;
isec->extra().icf_leaf = true;
isec->icf_leaf = true;
auto [it, inserted] = map.insert({isec.get(), isec.get()});
if (!inserted && isec->get_priority() < it->second->get_priority())
it->second = isec.get();
} else {
eligible++;
isec->extra().icf_eligible = true;
isec->icf_eligible = true;
}
}
});
tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
for (std::unique_ptr<InputSection<E>> &isec : ctx.objs[i]->sections) {
if (isec && isec->is_alive && isec->extra().icf_leaf) {
if (isec && isec->is_alive && isec->icf_leaf) {
auto it = map.find(isec.get());
assert(it != map.end());
isec->extra().leader = it->second;
isec->leader = it->second;
}
}
});
@@ -222,16 +238,15 @@ static void merge_leaf_nodes(Context<E> &ctx) {
template <typename E>
static Digest compute_digest(Context<E> &ctx, InputSection<E> &isec) {
SHA256_CTX sha;
SHA256_Init(&sha);
SHA256Hash sha;
auto hash = [&](auto val) {
SHA256_Update(&sha, &val, sizeof(val));
sha.update((u8 *)&val, sizeof(val));
};
auto hash_string = [&](std::string_view str) {
hash(str.size());
SHA256_Update(&sha, str.data(), str.size());
sha.update((u8 *)str.data(), str.size());
};
auto hash_symbol = [&](Symbol<E> &sym) {
@@ -245,10 +260,10 @@ static Digest compute_digest(Context<E> &ctx, InputSection<E> &isec) {
hash((u64)frag);
} else if (!isec) {
hash('3');
} else if (isec->extra().leader) {
} else if (isec->leader) {
hash('4');
hash((u64)isec->extra().leader);
} else if (isec->extra().icf_eligible) {
hash((u64)isec->leader);
} else if (isec->icf_eligible) {
hash('5');
} else {
hash('6');
@@ -271,7 +286,7 @@ static Digest compute_digest(Context<E> &ctx, InputSection<E> &isec) {
hash(fde.get_rels(isec.file).size());
for (ElfRel<E> &rel : fde.get_rels(isec.file).subspan(1)) {
for (const ElfRel<E> &rel : fde.get_rels(isec.file).subspan(1)) {
hash_symbol(*isec.file.symbols[rel.r_sym]);
hash(rel.r_type);
hash(rel.r_offset - fde.input_offset);
@@ -282,7 +297,7 @@ static Digest compute_digest(Context<E> &ctx, InputSection<E> &isec) {
i64 frag_idx = 0;
for (i64 i = 0; i < isec.get_rels(ctx).size(); i++) {
ElfRel<E> &rel = isec.get_rels(ctx)[i];
const ElfRel<E> &rel = isec.get_rels(ctx)[i];
hash(rel.r_offset);
hash(rel.r_type);
hash(isec.get_addend(rel));
@@ -309,7 +324,7 @@ static std::vector<InputSection<E> *> gather_sections(Context<E> &ctx) {
tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
for (std::unique_ptr<InputSection<E>> &isec : ctx.objs[i]->sections)
if (isec && isec->is_alive && isec->extra().icf_eligible)
if (isec && isec->is_alive && isec->icf_eligible)
num_sections[i]++;
});
@@ -324,12 +339,12 @@ static std::vector<InputSection<E> *> gather_sections(Context<E> &ctx) {
tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
i64 idx = section_indices[i];
for (std::unique_ptr<InputSection<E>> &isec : ctx.objs[i]->sections)
if (isec && isec->is_alive && isec->extra().icf_eligible)
if (isec && isec->is_alive && isec->icf_eligible)
sections[idx++] = isec.get();
});
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
sections[i]->extra().icf_idx = i;
sections[i]->icf_idx = i;
});
return sections;
@@ -347,6 +362,9 @@ compute_digests(Context<E> &ctx, std::span<InputSection<E> *> sections) {
return digests;
}
// Build a graph, treating every function as a vertex and every function call
// as an edge. See the description at the top for a more detailed formulation.
// We use u32 indices here to improve cache locality.
template <typename E>
static void gather_edges(Context<E> &ctx,
std::span<InputSection<E> *> sections,
@@ -362,18 +380,18 @@ static void gather_edges(Context<E> &ctx,
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
InputSection<E> &isec = *sections[i];
assert(isec.extra().icf_eligible);
assert(isec.icf_eligible);
i64 frag_idx = 0;
for (i64 j = 0; j < isec.get_rels(ctx).size(); j++) {
if (isec.rel_fragments && isec.rel_fragments[frag_idx].idx == j) {
frag_idx++;
} else {
ElfRel<E> &rel = isec.get_rels(ctx)[j];
const ElfRel<E> &rel = isec.get_rels(ctx)[j];
Symbol<E> &sym = *isec.file.symbols[rel.r_sym];
if (!sym.get_frag())
if (InputSection<E> *isec = sym.get_input_section())
if (isec->extra().icf_eligible)
if (isec->icf_eligible)
num_edges[i]++;
}
}
@@ -392,12 +410,13 @@ static void gather_edges(Context<E> &ctx,
for (i64 j = 0; j < isec.get_rels(ctx).size(); j++) {
if (isec.rel_fragments && isec.rel_fragments[frag_idx].idx == j) {
frag_idx++;
ElfRel<E> &rel = isec.get_rels(ctx)[j];
} else {
const ElfRel<E> &rel = isec.get_rels(ctx)[j];
Symbol<E> &sym = *isec.file.symbols[rel.r_sym];
if (!sym.get_frag())
if (InputSection<E> *isec = sym.get_input_section())
if (isec->extra().icf_eligible)
edges[idx++] = isec->extra().icf_idx;
if (isec->icf_eligible)
edges[idx++] = isec->icf_idx;
}
}
});
@@ -417,15 +436,15 @@ static i64 propagate(std::span<std::vector<Digest>> digests,
if (digests[slot][i] == digests[!slot][i])
return;
SHA256_CTX sha;
SHA256_Init(&sha);
SHA256_Update(&sha, digests[2][i].data(), HASH_SIZE);
SHA256Hash sha;
sha.update(digests[2][i].data(), HASH_SIZE);
i64 begin = edge_indices[i];
i64 end = (i + 1 == num_digests) ? edges.size() : edge_indices[i + 1];
for (i64 j : edges.subspan(begin, end - begin))
SHA256_Update(&sha, digests[slot][j].data(), HASH_SIZE);
for (i64 j : edges.subspan(begin, end - begin)) {
sha.update(digests[slot][j].data(), HASH_SIZE);
}
digests[!slot][i] = digest_final(sha);
@@ -458,11 +477,11 @@ static void print_icf_sections(Context<E> &ctx) {
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (std::unique_ptr<InputSection<E>> &isec : file->sections) {
if (isec && isec->is_alive && isec->extra().leader) {
if (isec.get() == isec->extra().leader)
if (isec && isec->is_alive && isec->leader) {
if (isec.get() == isec->leader)
leaders.push_back(isec.get());
else
map.insert({isec->extra().leader, isec.get()});
map.insert({isec->leader, isec.get()});
}
}
});
@@ -495,9 +514,8 @@ static void print_icf_sections(Context<E> &ctx) {
template <typename E>
void icf_sections(Context<E> &ctx) {
Timer t(ctx, "icf");
for (ObjectFile<E> *file : ctx.objs)
file->extras.resize(file->sections.size());
if (ctx.objs.empty())
return;
uniquify_cies(ctx);
merge_leaf_nodes(ctx);
@@ -505,6 +523,11 @@ void icf_sections(Context<E> &ctx) {
// Prepare for the propagation rounds.
std::vector<InputSection<E> *> sections = gather_sections(ctx);
// We allocate 3 arrays to store hashes for each vertex.
// Index 0 and 1 are used for tree hashes from the previous iteration and the current iteration.
// They switch roles every iteration --- see `slot` below.
// Index 2 stores the initial, single-vertex hash --- this is combined with hashes from the connected vertices to
// form the tree hash described above.
std::vector<std::vector<Digest>> digests(3);
digests[0] = compute_digests<E>(ctx, sections);
digests[1].resize(digests[0].size());
@@ -521,6 +544,17 @@ void icf_sections(Context<E> &ctx) {
Timer t(ctx, "propagate");
tbb::affinity_partitioner ap;
// A cheap test that the graph hasn't converged yet.
// The loop after this one uses a strict condition, but it's expensive
// as it requires sorting the entire hash collection.
//
// For nodes that have a cycle in downstream (i.e. recursive
// functions and functions that calls recursive functions) will always
// change with the iterations. Nodes that doesn't (i.e. non-recursive
// functions) will stop changing as soon as the propagation depth reaches
// the call tree depth.
// Here, we test whether we have reached sufficient depth for the latter,
// which is a necessary (but not sufficient) condition for convergence.
i64 num_changed = -1;
for (;;) {
i64 n = propagate<E>(digests, edges, edge_indices, slot, ap);
@@ -529,8 +563,12 @@ void icf_sections(Context<E> &ctx) {
num_changed = n;
}
// Run the pass until the unique number of hashes stop increasing, at which
// point we have achieved convergence (proof omitted for brevity).
i64 num_classes = -1;
for (;;) {
// count_num_classes requires sorting which is O(n log n), so do a little
// more work beforehand to amortize that log factor.
for (i64 i = 0; i < 10; i++)
propagate<E>(digests, edges, edge_indices, slot, ap);
@@ -558,7 +596,7 @@ void icf_sections(Context<E> &ctx) {
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
auto it = map->find(digest[i]);
assert(it != map->end());
sections[i]->extra().leader = it->second;
sections[i]->leader = it->second;
});
// Since free'ing the map is slow, postpone it.
@@ -575,7 +613,7 @@ void icf_sections(Context<E> &ctx) {
for (Symbol<E> *sym : file->symbols) {
if (sym->file == file) {
InputSection<E> *isec = sym->get_input_section();
if (isec && isec->extra().leader && isec->extra().leader != isec) {
if (isec && isec->leader && isec->leader != isec) {
isec->kill();
isec->killed_by_icf = true;
}
@@ -588,10 +626,6 @@ void icf_sections(Context<E> &ctx) {
#define INSTANTIATE(E) \
template void icf_sections(Context<E> &ctx);
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
INSTANTIATE_ALL;
} // namespace mold::elf
+192 -102
View File
@@ -2,7 +2,10 @@
#include <cstring>
#include <regex>
#include <unistd.h>
#ifndef _WIN32
# include <unistd.h>
#endif
namespace mold::elf {
@@ -63,6 +66,15 @@ void InputFile<E>::clear_symbols() {
}
}
// Find the source filename. It should be listed in symtab as STT_FILE.
template <typename E>
std::string_view InputFile<E>::get_source_name() const {
for (i64 i = 0; i < first_global; i++)
if (Symbol<E> *sym = symbols[i]; sym->get_type() == STT_FILE)
return sym->name();
return "";
}
template <typename E>
ObjectFile<E>::ObjectFile(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::string archive_name, bool is_in_lib)
@@ -99,18 +111,18 @@ u32 ObjectFile<E>::read_note_gnu_property(Context<E> &ctx,
data = data.substr(align_to(hdr.n_namesz, 4));
std::string_view desc = data.substr(0, hdr.n_descsz);
data = data.substr(align_to(hdr.n_descsz, E::word_size));
data = data.substr(align_to(hdr.n_descsz, sizeof(Word<E>)));
if (hdr.n_type != NT_GNU_PROPERTY_TYPE_0 || name != "GNU")
continue;
while (!desc.empty()) {
u32 type = *(u32 *)desc.data();
u32 size = *(u32 *)(desc.data() + 4);
u32 type = *(ul32 *)desc.data();
u32 size = *(ul32 *)(desc.data() + 4);
desc = desc.substr(8);
if (type == GNU_PROPERTY_X86_FEATURE_1_AND)
ret |= *(u32 *)desc.data();
desc = desc.substr(align_to(size, E::word_size));
ret |= *(ul32 *)desc.data();
desc = desc.substr(align_to(size, sizeof(Word<E>)));
}
}
return ret;
@@ -122,7 +134,8 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
for (i64 i = 0; i < this->elf_sections.size(); i++) {
const ElfShdr<E> &shdr = this->elf_sections[i];
if ((shdr.sh_flags & SHF_EXCLUDE) && !(shdr.sh_flags & SHF_ALLOC))
if ((shdr.sh_flags & SHF_EXCLUDE) && !(shdr.sh_flags & SHF_ALLOC) &&
shdr.sh_type != SHT_LLVM_ADDRSIG)
continue;
switch (shdr.sh_type) {
@@ -131,10 +144,15 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
if (shdr.sh_info >= this->elf_syms.size())
Fatal(ctx) << *this << ": invalid symbol index";
const ElfSym<E> &sym = this->elf_syms[shdr.sh_info];
std::string_view signature = symbol_strtab.data() + sym.st_name;
std::string_view signature = this->symbol_strtab.data() + sym.st_name;
// Ignore a broken comdat group GCC emits for .debug_macros.
// https://github.com/rui314/mold/issues/438
if (signature.starts_with("wm4."))
continue;
// Get comdat group members.
std::span<u32> entries = this->template get_data<u32>(ctx, shdr);
std::span<ul32> entries = this->template get_data<ul32>(ctx, shdr);
if (entries.empty())
Fatal(ctx) << *this << ": empty SHT_GROUP";
@@ -167,11 +185,14 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
// making the stack area executable is too dangerous. Tell our
// users about the difference if that matters.
if (name == ".note.GNU-stack") {
if (shdr.sh_flags & SHF_EXECINSTR)
Warn(ctx) << *this << ": this file may cause a segmentation"
" fault because it requires an executable stack. See"
" https://github.com/rui314/mold/tree/main/docs/execstack.md"
" for more info.";
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"
" https://github.com/rui314/mold/tree/main/docs/execstack.md"
" for more info.";
needs_executable_stack = true;
}
continue;
}
@@ -199,16 +220,20 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
this->sections[i] = std::make_unique<InputSection<E>>(ctx, *this, name, i);
// Save .llvm_addrsig for --icf=safe.
if (shdr.sh_type == SHT_LLVM_ADDRSIG)
llvm_addrsig = this->sections[i].get();
// Save debug sections for --gdb-index.
if (ctx.arg.gdb_index) {
InputSection<E> *isec = this->sections[i].get();
if (name == ".debug_info")
debug_info = isec;
if (name == ".debug_abbrev")
debug_abbrev = isec;
if (name == ".debug_ranges")
debug_ranges = isec;
if (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
@@ -222,6 +247,16 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
debug_pubtypes = isec;
isec->is_alive = false;
}
// .debug_types is similar to .debug_info but contains type info
// only. It exists only in DWARF 4, has been removed in DWARF 5 and
// neither GCC nor Clang generate it by default
// (-fdebug-types-section is needed). As such there is probably
// little need to support it.
if (name == ".debug_types")
Fatal(ctx) << *this << ": mold's --gdb-index is not compatible"
" with .debug_types; to fix this error, remove"
" -fdebug-types-section and recompile";
}
static Counter counter("regular_sections");
@@ -234,7 +269,7 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
// Attach relocation sections to their target sections.
for (i64 i = 0; i < this->elf_sections.size(); i++) {
const ElfShdr<E> &shdr = this->elf_sections[i];
if (shdr.sh_type != (E::is_rel ? SHT_REL : SHT_RELA))
if (shdr.sh_type != (is_rela<E> ? SHT_RELA : SHT_REL))
continue;
if (shdr.sh_info >= sections.size())
@@ -299,13 +334,13 @@ void ObjectFile<E>::read_ehframe(Context<E> &ctx, InputSection<E> &isec) {
i64 rel_idx = 0;
for (std::string_view data = contents; !data.empty();) {
i64 size = *(u32 *)data.data();
i64 size = *(ul32 *)data.data();
if (size == 0)
break;
i64 begin_offset = data.data() - contents.data();
i64 end_offset = begin_offset + size + 4;
i64 id = *(u32 *)(data.data() + 4);
i64 id = *(ul32 *)(data.data() + 4);
data = data.substr(size + 4);
i64 rel_begin = rel_idx;
@@ -341,7 +376,7 @@ void ObjectFile<E>::read_ehframe(Context<E> &ctx, InputSection<E> &isec) {
};
for (i64 i = fdes_begin; i < fdes.size(); i++) {
i64 cie_offset = *(i32 *)(contents.data() + fdes[i].input_offset + 4);
i64 cie_offset = *(il32 *)(contents.data() + fdes[i].input_offset + 4);
fdes[i].cie_idx = find_cie(fdes[i].input_offset + 4 - cie_offset);
}
@@ -374,7 +409,7 @@ template <typename E>
static Symbol<E> *insert_symbol(Context<E> &ctx, const ElfSym<E> &esym,
std::string_view key, std::string_view name) {
if (esym.is_undef() && name.starts_with("__real_") &&
ctx.arg.wrap.count(name.substr(7))) {
ctx.arg.wrap.contains(name.substr(7))) {
return get_symbol(ctx, key.substr(7), name.substr(7));
}
@@ -408,7 +443,7 @@ void ObjectFile<E>::initialize_symbols(Context<E> &ctx) {
if (esym.is_common())
Fatal(ctx) << *this << ": common local symbol?";
std::string_view name = symbol_strtab.data() + esym.st_name;
std::string_view name = this->symbol_strtab.data() + esym.st_name;
if (name.empty() && esym.st_type == STT_SECTION)
if (InputSection<E> *sec = get_section(esym))
name = sec->name();
@@ -437,7 +472,7 @@ void ObjectFile<E>::initialize_symbols(Context<E> &ctx) {
const ElfSym<E> &esym = this->elf_syms[i];
// Get a symbol name
std::string_view key = symbol_strtab.data() + esym.st_name;
std::string_view key = this->symbol_strtab.data() + esym.st_name;
std::string_view name = key;
// Parse symbol version after atsign
@@ -464,27 +499,20 @@ 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 (!std::is_same_v<E, RISCV64>)
return;
if constexpr (is_riscv<E>) {
auto less = [&](const ElfRel<E> &a, const ElfRel<E> &b) {
return a.r_offset < b.r_offset;
};
auto less = [&](const ElfRel<E> &a, const ElfRel<E> &b) {
return a.r_type != E::R_NONE && b.r_type != E::R_NONE &&
a.r_offset < b.r_offset;
};
for (i64 i = 1; i < sections.size(); i++) {
std::unique_ptr<InputSection<E>> &isec = sections[i];
if (!isec || !isec->is_alive || !(isec->shdr().sh_flags & SHF_ALLOC))
continue;
sorted_rels.resize(sections.size());
for (i64 i = 1; i < sections.size(); i++) {
std::unique_ptr<InputSection<E>> &isec = sections[i];;
if (!isec || !isec->is_alive || !(isec->shdr().sh_flags & SHF_ALLOC))
continue;
std::span<ElfRel<E>> rels = isec->get_rels(ctx);
if (std::is_sorted(rels.begin(), rels.end(), less))
continue;
sorted_rels[isec->shndx] = {rels.begin(), rels.end()};
sort(sorted_rels[isec->shndx], less);
std::span<ElfRel<E>> rels = isec->get_rels(ctx);
if (!std::is_sorted(rels.begin(), rels.end(), less))
sort(rels, less);
}
}
}
@@ -524,16 +552,10 @@ split_section(Context<E> &ctx, InputSection<E> &sec) {
sec.shdr().sh_flags);
rec->p2align = sec.p2align;
std::string_view data = sec.contents;
// If thes section contents are compressed, uncompress them.
if (sec.is_compressed()) {
u8 *buf = new u8[sec.sh_size];
sec.uncompress(ctx, buf);
data = {(char *)buf, sec.sh_size};
ctx.string_pool.emplace_back(buf);
}
sec.uncompress(ctx);
std::string_view data = sec.contents;
const char *begin = data.data();
u64 entsize = sec.shdr().sh_entsize;
HyperLogLog estimator;
@@ -728,15 +750,44 @@ void ObjectFile<E>::register_section_pieces(Context<E> &ctx) {
}
}
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());
symtab_sec = this->find_section(SHT_SYMTAB);
if (symtab_sec) {
// In ELF, all local symbols precede global symbols in the symbol table.
// sh_info has an index of the first global symbol.
this->first_global = symtab_sec->sh_info;
this->elf_syms = this->template get_data<ElfSym<E>>(ctx, *symtab_sec);
symbol_strtab = this->get_string(ctx, symtab_sec->sh_link);
this->symbol_strtab = this->get_string(ctx, symtab_sec->sh_link);
}
initialize_sections(ctx);
@@ -925,7 +976,7 @@ void ObjectFile<E>::eliminate_duplicate_comdat_groups() {
if (group->owner == this->priority)
continue;
std::span<u32> entries = pair.second;
std::span<ul32> entries = pair.second;
for (u32 i : entries)
if (sections[i])
sections[i]->kill();
@@ -937,6 +988,18 @@ void ObjectFile<E>::claim_unresolved_symbols(Context<E> &ctx) {
if (!this->is_alive)
return;
auto report_undef = [&](Symbol<E> &sym) {
std::stringstream ss;
if (std::string_view source = this->get_source_name(); !source.empty())
ss << ">>> referenced by " << source << "\n";
else
ss << ">>> referenced by " << *this << "\n";
typename decltype(ctx.undef_errors)::accessor acc;
ctx.undef_errors.insert(acc, {sym.name(), {}});
acc->second.push_back(ss.str());
};
for (i64 i = this->first_global; i < this->symbols.size(); i++) {
const ElfSym<E> &esym = this->elf_syms[i];
Symbol<E> &sym = *this->symbols[i];
@@ -949,7 +1012,7 @@ void ObjectFile<E>::claim_unresolved_symbols(Context<E> &ctx) {
// imported symbol, it's handled as if no symbols were found.
if (sym.file && sym.file->is_dso &&
(sym.visibility == STV_PROTECTED || sym.visibility == STV_HIDDEN)) {
report_undef(ctx, *this, sym);
report_undef(sym);
continue;
}
@@ -959,7 +1022,7 @@ void ObjectFile<E>::claim_unresolved_symbols(Context<E> &ctx) {
// If a symbol name is in the form of "foo@version", search for
// symbol "foo" and check if the symbol has version "version".
std::string_view key = symbol_strtab.data() + esym.st_name;
std::string_view key = this->symbol_strtab.data() + esym.st_name;
if (i64 pos = key.find('@'); pos != key.npos) {
Symbol<E> *sym2 = get_symbol(ctx, key.substr(0, pos));
if (sym2->file && sym2->file->is_dso &&
@@ -979,7 +1042,7 @@ void ObjectFile<E>::claim_unresolved_symbols(Context<E> &ctx) {
};
if (ctx.arg.unresolved_symbols == UNRESOLVED_WARN)
report_undef(ctx, *this, sym);
report_undef(sym);
// Convert remaining undefined symbols to dynamic symbols.
if (ctx.arg.shared) {
@@ -1055,18 +1118,22 @@ void ObjectFile<E>::scan_relocations(Context<E> &ctx) {
// definition, the tentative definition gets the default initial value 0.
//
// Tentative definitions are represented as "common symbols" in an object
// file. In this function, we allocate spaces in .bss for remaining common
// symbols that were not resolved to usual defined symbols in previous
// passes.
// file. In this function, we allocate spaces in .common or .tls_common
// for remaining common symbols that were not resolved to usual defined
// symbols in previous passes.
template <typename E>
void ObjectFile<E>::convert_common_symbols(Context<E> &ctx) {
if (!has_common_symbol)
return;
OutputSection<E> *osec =
OutputSection<E> *common =
OutputSection<E>::get_instance(ctx, ".common", SHT_NOBITS,
SHF_WRITE | SHF_ALLOC);
OutputSection<E> *tls_common =
OutputSection<E>::get_instance(ctx, ".tls_common", SHT_NOBITS,
SHF_WRITE | SHF_ALLOC | SHF_TLS);
for (i64 i = this->first_global; i < this->elf_syms.size(); i++) {
if (!this->elf_syms[i].is_common())
continue;
@@ -1082,17 +1149,20 @@ void ObjectFile<E>::convert_common_symbols(Context<E> &ctx) {
elf_sections2.push_back({});
ElfShdr<E> &shdr = elf_sections2.back();
memset(&shdr, 0, sizeof(shdr));
shdr.sh_flags = SHF_ALLOC;
bool is_tls = (sym.get_type() == STT_TLS);
shdr.sh_flags = is_tls ? (SHF_ALLOC | SHF_TLS) : SHF_ALLOC;
shdr.sh_type = SHT_NOBITS;
shdr.sh_size = this->elf_syms[i].st_size;
shdr.sh_addralign = this->elf_syms[i].st_value;
i64 idx = this->elf_sections.size() + elf_sections2.size() - 1;
std::unique_ptr<InputSection<E>> isec =
std::make_unique<InputSection<E>>(ctx, *this, ".common", idx);
isec->output_section = osec;
std::make_unique<InputSection<E>>(ctx, *this,
is_tls ? ".tls_common" : ".common",
idx);
isec->output_section = is_tls ? tls_common : common;
sym.file = this;
sym.shndx = idx;
@@ -1113,9 +1183,9 @@ static bool should_write_to_local_symtab(Context<E> &ctx, Symbol<E> &sym) {
return false;
// Local symbols are discarded if --discard-local is given or they
// are not in a mergeable section. I *believe* we exclude symbols in
// mergeable sections because (1) they are too many and (2) they are
// merged, so their origins shouldn't matter, but I dont' really
// are in a mergeable section. I *believe* we exclude symbols in
// mergeable sections because (1) there are too many and (2) they are
// merged, so their origins shouldn't matter, but I don't really
// know the rationale. Anyway, this is the behavior of the
// traditional linkers.
if (sym.name().starts_with(".L")) {
@@ -1166,56 +1236,48 @@ void ObjectFile<E>::compute_symtab(Context<E> &ctx) {
if (sym.file == this && is_alive(sym) &&
(!ctx.arg.retain_symbols_file || sym.write_to_symtab)) {
this->strtab_size += sym.name().size() + 1;
this->num_global_symtab++;
// Global symbols can be demoted to local symbols based on visibility,
// version scripts etc.
if (sym.is_local())
this->num_local_symtab++;
else
this->num_global_symtab++;
sym.write_to_symtab = true;
}
}
}
template <typename E>
void ObjectFile<E>::write_symtab(Context<E> &ctx) {
void ObjectFile<E>::populate_symtab(Context<E> &ctx) {
ElfSym<E> *symtab_base = (ElfSym<E> *)(ctx.buf + ctx.symtab->shdr.sh_offset);
i64 symtab_idx;
u8 *strtab_base = ctx.buf + ctx.strtab->shdr.sh_offset;
i64 strtab_off = this->strtab_offset;
auto write_sym = [&](Symbol<E> &sym) {
auto write_sym = [&](Symbol<E> &sym, i64 &symtab_idx) {
ElfSym<E> &esym = symtab_base[symtab_idx++];
esym = sym.esym();
esym = to_output_esym(ctx, sym);
esym.st_name = strtab_off;
if (sym.get_type() == STT_TLS)
esym.st_value = sym.get_addr(ctx) - ctx.tls_begin;
else
esym.st_value = sym.get_addr(ctx);
if (InputSection<E> *isec = sym.get_input_section())
esym.st_shndx = isec->output_section->shndx;
else if (sym.shndx < 0)
esym.st_shndx = -sym.shndx;
else if (esym.is_undef())
esym.st_shndx = SHN_UNDEF;
else
esym.st_shndx = SHN_ABS;
write_string(strtab_base + strtab_off, sym.name());
strtab_off += sym.name().size() + 1;
};
symtab_idx = this->local_symtab_idx;
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);
write_sym(sym, local_symtab_idx);
}
symtab_idx = this->global_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)
write_sym(sym);
if (sym.file == this && sym.write_to_symtab) {
if (sym.is_local())
write_sym(sym, local_symtab_idx);
else
write_sym(sym, global_symtab_idx);
}
}
}
@@ -1251,6 +1313,7 @@ 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;
}
@@ -1259,12 +1322,12 @@ std::string SharedFile<E>::get_soname(Context<E> &ctx) {
if (ElfShdr<E> *sec = this->find_section(SHT_DYNAMIC))
for (ElfDyn<E> &dyn : this->template get_data<ElfDyn<E>>(ctx, *sec))
if (dyn.d_tag == DT_SONAME)
return symbol_strtab.data() + dyn.d_val;
return this->symbol_strtab.data() + dyn.d_val;
if (this->mf->given_fullpath)
return this->filename;
return filepath(this->filename).filename();
return filepath(this->filename).filename().string();
}
template <typename E>
@@ -1273,7 +1336,7 @@ void SharedFile<E>::parse(Context<E> &ctx) {
if (!symtab_sec)
return;
symbol_strtab = this->get_string(ctx, symtab_sec->sh_link);
this->symbol_strtab = this->get_string(ctx, symtab_sec->sh_link);
soname = get_soname(ctx);
version_strings = read_verdef(ctx);
@@ -1294,7 +1357,7 @@ void SharedFile<E>::parse(Context<E> &ctx) {
if (ver == VER_NDX_LOCAL)
continue;
std::string_view name = symbol_strtab.data() + esyms[i].st_name;
std::string_view name = this->symbol_strtab.data() + esyms[i].st_name;
bool is_hidden = (!vers.empty() && (vers[i] & VERSYM_HIDDEN));
this->elf_syms2.push_back(esyms[i]);
@@ -1316,6 +1379,36 @@ void SharedFile<E>::parse(Context<E> &ctx) {
counter += this->elf_syms.size();
}
// Symbol versioning is a GNU extension to the ELF file format. I don't
// particularly like the feature as it complicates the semantics of
// dynamic linking, but we need to support it anyway because it is
// mandatory on glibc-based systems such as most Linux distros.
//
// Let me explain what symbol versioning is. Symbol versioning is a
// mechanism to allow multiple symbols of the same name but of different
// versions live together in a shared object file. It's convenient if you
// want to make an API-breaking change to some function but want to keep
// old programs working with the newer libraries.
//
// With symbol versioning, dynamic symbols are resolved by (name, version)
// tuple instead of just by name. For example, glibc 2.35 defines two
// different versions of `posix_spawn`, `posix_spawn` of version
// "GLIBC_2.15" and that of version "GLIBC_2.2.5". Any executable that
// uses `posix_spawn` is linked either to that of "GLIBC_2.15" or that of
// "GLIBC_2.2.5"
//
// Versions are just stirngs, and no ordering is defined between them.
// For example, "GLIBC_2.15" is not considered a newer version of
// "GLIBC_2.2.5" or vice versa. They are considered just different.
//
// If a shared object file has versioned symbols, it contains a parallel
// array for the symbol table. Version strings can be found in that
// parallel table.
//
// One version is considered the "default" version for each shared object.
// If an undefiend symbol `foo` is resolved to a symbol defined by the
// shared object, it's marked so that it'll be resolved to (`foo`, the
// default version of the library) at load-time.
template <typename E>
std::vector<std::string_view> SharedFile<E>::read_verdef(Context<E> &ctx) {
std::vector<std::string_view> ret(VER_NDX_LAST_RESERVED + 1);
@@ -1430,7 +1523,7 @@ void SharedFile<E>::compute_symtab(Context<E> &ctx) {
}
template <typename E>
void SharedFile<E>::write_symtab(Context<E> &ctx) {
void SharedFile<E>::populate_symtab(Context<E> &ctx) {
ElfSym<E> *symtab =
(ElfSym<E> *)(ctx.buf + ctx.symtab->shdr.sh_offset) + this->global_symtab_idx;
@@ -1456,14 +1549,11 @@ void SharedFile<E>::write_symtab(Context<E> &ctx) {
}
#define INSTANTIATE(E) \
template class InputFile<E>; \
template class ObjectFile<E>; \
template class SharedFile<E>; \
template std::ostream &operator<<(std::ostream &, const InputFile<E> &)
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
INSTANTIATE_ALL;
} // namespace mold::elf
+222 -104
View File
@@ -10,8 +10,8 @@ bool CieRecord<E>::equals(const CieRecord<E> &other) const {
if (get_contents() != other.get_contents())
return false;
std::span<ElfRel<E>> x = get_rels();
std::span<ElfRel<E>> y = other.get_rels();
std::span<const ElfRel<E>> x = get_rels();
std::span<const ElfRel<E>> y = other.get_rels();
if (x.size() != y.size())
return false;
@@ -36,12 +36,10 @@ InputSection<E>::InputSection(Context<E> &ctx, ObjectFile<E> &file,
std::string_view name, i64 shndx)
: file(file), shndx(shndx) {
if (shndx < file.elf_sections.size())
contents = {(char *)file.mf->data + shdr().sh_offset, shdr().sh_size};
bool compressed;
contents = {(char *)file.mf->data + shdr().sh_offset, (size_t)shdr().sh_size};
if (name.starts_with(".zdebug")) {
sh_size = *(ubig64 *)&contents[4];
sh_size = *(ub64 *)&contents[4];
p2align = to_p2align(shdr().sh_addralign);
compressed = true;
} else if (shdr().sh_flags & SHF_COMPRESSED) {
@@ -55,28 +53,37 @@ InputSection<E>::InputSection(Context<E> &ctx, ObjectFile<E> &file,
compressed = false;
}
// Uncompress early if the relocation is REL-type so that we can read
// addends from section contents. If RELA-type, we don't need to do this
// because addends are in relocations.
if (compressed && E::is_rel) {
u8 *buf = new u8[sh_size];
uncompress(ctx, buf);
contents = {(char *)buf, sh_size};
ctx.string_pool.emplace_back(buf);
}
// Sections may have been compressed. We usually uncompress them
// directly into the mmap'ed output file, but we want to uncompress
// early for REL-type ELF types to read relocation addends from
// section contents. For RELA-type, we don't need to do this because
// addends are in relocations.
if constexpr (!is_rela<E>)
uncompress(ctx);
output_section =
OutputSection<E>::get_instance(ctx, name, shdr().sh_type, shdr().sh_flags);
}
template <typename E>
bool InputSection<E>::is_compressed() {
return !E::is_rel &&
(name().starts_with(".zdebug") || (shdr().sh_flags & SHF_COMPRESSED));
void InputSection<E>::uncompress(Context<E> &ctx) {
if (!compressed || uncompressed)
return;
u8 *buf = new u8[sh_size];
uncompress_to(ctx, buf);
contents = {(char *)buf, sh_size};
ctx.string_pool.emplace_back(buf);
uncompressed = true;
}
template <typename E>
void InputSection<E>::uncompress(Context<E> &ctx, u8 *buf) {
void InputSection<E>::uncompress_to(Context<E> &ctx, u8 *buf) {
if (!compressed || uncompressed) {
memcpy(buf, contents.data(), contents.size());
return;
}
auto do_uncompress = [&](std::string_view data) {
unsigned long size = sh_size;
if (::uncompress(buf, &size, (u8 *)data.data(), data.size()) != Z_OK)
@@ -100,47 +107,35 @@ void InputSection<E>::uncompress(Context<E> &ctx, u8 *buf) {
ElfChdr<E> &hdr = *(ElfChdr<E> *)&contents[0];
if (hdr.ch_type != ELFCOMPRESS_ZLIB)
Fatal(ctx) << *this << ": unsupported compression type";
Fatal(ctx) << *this << ": unsupported compression type: 0x"
<< std::hex << hdr.ch_type;
do_uncompress(contents.substr(sizeof(ElfChdr<E>)));
}
template <typename E>
static i64 get_output_type(Context<E> &ctx) {
if (ctx.arg.shared)
return 0;
if (ctx.arg.pie)
return 1;
return 2;
}
typedef enum { NONE, ERROR, COPYREL, PLT, CPLT, DYNREL, BASEREL } Action;
template <typename E>
static 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>
void InputSection<E>::dispatch(Context<E> &ctx, Action table[3][4], i64 i,
const ElfRel<E> &rel, Symbol<E> &sym) {
Action action = table[get_output_type(ctx)][get_sym_type(sym)];
bool is_writable = (shdr().sh_flags & SHF_WRITE);
static void
dispatch(Context<E> &ctx, InputSection<E> &isec, Action action,
Symbol<E> &sym, const ElfRel<E> &rel) {
auto error = [&] {
std::string msg = sym.is_absolute() ? "-fno-PIC" : "-fPIC";
Error(ctx) << *this << ": " << rel << " relocation at offset 0x"
Error(ctx) << isec << ": " << rel << " relocation at offset 0x"
<< std::hex << rel.r_offset << " against symbol `"
<< sym << "' can not be used; recompile with " << msg;
};
auto warn_textrel = [&] {
if (ctx.arg.warn_textrel)
Warn(ctx) << *this << ": relocation against symbol `" << sym
auto check_textrel = [&] {
if (isec.shdr().sh_flags & SHF_WRITE)
return;
if (ctx.arg.z_text) {
error();
} else if (ctx.arg.warn_textrel) {
Warn(ctx) << isec << ": relocation against symbol `" << sym
<< "' in read-only section";
}
ctx.has_textrel = true;
};
switch (action) {
@@ -152,69 +147,147 @@ void InputSection<E>::dispatch(Context<E> &ctx, Action table[3][4], i64 i,
case COPYREL:
if (!ctx.arg.z_copyreloc) {
error();
return;
}
if (sym.esym().st_visibility == STV_PROTECTED) {
Error(ctx) << *this
<< ": cannot make copy relocation for protected symbol '"
} else if (sym.esym().st_visibility == STV_PROTECTED) {
Error(ctx) << isec << ": cannot make copy relocation for protected symbol '"
<< sym << "', defined in " << *sym.file
<< "; recompile with -fPIC";
return;
}
sym.flags |= NEEDS_COPYREL;
return;
case PLT:
sym.flags |= NEEDS_PLT;
return;
case CPLT: {
std::scoped_lock lock(sym.mu);
sym.flags |= NEEDS_PLT;
sym.is_canonical = true;
case CPLT:
sym.flags |= NEEDS_CPLT;
return;
}
case DYNREL:
if (!is_writable) {
if (ctx.arg.z_text) {
error();
return;
}
warn_textrel();
ctx.has_textrel = true;
}
assert(sym.is_imported);
file.num_dynrel++;
check_textrel();
isec.file.num_dynrel++;
return;
case BASEREL:
if (!is_writable) {
if (ctx.arg.z_text) {
error();
return;
}
warn_textrel();
ctx.has_textrel = true;
}
if (!is_relr_reloc(ctx, rel))
file.num_dynrel++;
check_textrel();
if (!isec.is_relr_reloc(ctx, rel))
isec.file.num_dynrel++;
return;
default:
unreachable();
}
}
template <typename E>
static Action get_rel_action(Context<E> &ctx, const Action table[3][4],
Symbol<E> &sym) {
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>
void InputSection<E>::scan_abs_rel(Context<E> &ctx, Symbol<E> &sym,
const ElfRel<E> &rel) {
// 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
// generally does not support dynamic relocations smaller than the
// word size, we need to report an error if a relocation cannot be
// resolved at link-time.
constexpr Action table[][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
};
Action action = get_rel_action(ctx, table, sym);
dispatch(ctx, *this, action, sym, rel);
}
template <typename E>
static Action get_abs_dyn_action(Context<E> &ctx, Symbol<E> &sym) {
// This is a decision table for absolute relocations for the word
// size data (e.g. R_X86_64_64). Unlike the above, we can emit a
// dynamic relocation if we cannot resolve its address at link-time.
constexpr Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, BASEREL, DYNREL, DYNREL }, // Shared object
{ NONE, BASEREL, DYNREL, DYNREL }, // Position-independent exec
{ NONE, NONE, COPYREL, CPLT }, // Position-dependent exec
};
return get_rel_action(ctx, table, sym);
}
template <typename E>
void InputSection<E>::scan_abs_dyn_rel(Context<E> &ctx, Symbol<E> &sym,
const ElfRel<E> &rel) {
Action action = get_abs_dyn_action(ctx, sym);
dispatch(ctx, *this, action, sym, rel);
}
template <typename E>
void InputSection<E>::scan_pcrel_rel(Context<E> &ctx, Symbol<E> &sym,
const ElfRel<E> &rel) {
// 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 Action table[][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
};
Action action = get_rel_action(ctx, table, sym);
dispatch(ctx, *this, action, sym, rel);
}
template <typename E>
void InputSection<E>::apply_abs_dyn_rel(Context<E> &ctx, Symbol<E> &sym,
const ElfRel<E> &rel, u8 *loc,
u64 S, i64 A, u64 P, ElfRel<E> *&dynrel) {
switch (get_abs_dyn_action(ctx, sym)) {
case COPYREL:
case CPLT:
case NONE:
*(Word<E> *)loc = S + A;
break;
case BASEREL:
if (!is_relr_reloc(ctx, rel))
*dynrel++ = ElfRel<E>(P, E::R_RELATIVE, 0, S + A);
*(Word<E> *)loc = S + A;
break;
case DYNREL:
*dynrel++ = ElfRel<E>(P, E::R_ABS, sym.get_dynsym_idx(ctx), A);
*(Word<E> *)loc = A;
break;
default:
unreachable();
}
}
template <typename E>
void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
if (shdr().sh_type == SHT_NOBITS || sh_size == 0)
return;
// Copy data
if constexpr (std::is_same_v<E, RISCV64>) {
if constexpr (is_riscv<E>) {
copy_contents_riscv(ctx, buf);
} else if (is_compressed()) {
uncompress(ctx, buf);
} else if (compressed) {
uncompress_to(ctx, buf);
} else {
memcpy(buf, contents.data(), contents.size());
}
@@ -226,33 +299,78 @@ void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
apply_reloc_nonalloc(ctx, buf);
}
// Get the name of a function containin a given offset.
template <typename E>
void report_undef(Context<E> &ctx, InputFile<E> &file, Symbol<E> &sym) {
if (ctx.arg.warn_once && !ctx.warned.insert({(void *)&sym, 1}))
return;
switch (ctx.arg.unresolved_symbols) {
case UNRESOLVED_ERROR:
Error(ctx) << "undefined symbol: " << file << ": " << sym;
break;
case UNRESOLVED_WARN:
Warn(ctx) << "undefined symbol: " << file << ": " << sym;
break;
case UNRESOLVED_IGNORE:
break;
std::string_view InputSection<E>::get_func_name(Context<E> &ctx, i64 offset) {
for (const ElfSym<E> &esym : file.elf_syms) {
if (esym.st_shndx == shndx && esym.st_type == STT_FUNC &&
esym.st_value <= offset && offset < esym.st_value + esym.st_size) {
std::string_view name = file.symbol_strtab.data() + esym.st_name;
if (ctx.arg.demangle)
return demangle(name);
return name;
}
}
return "";
}
// Record an undefined symbol error which will be displayed all at
// once by report_undef_errors().
template <typename E>
void InputSection<E>::record_undef_error(Context<E> &ctx, const ElfRel<E> &rel) {
std::stringstream ss;
if (std::string_view source = file.get_source_name(); !source.empty())
ss << ">>> referenced by " << source << "\n";
else
ss << ">>> referenced by " << *this << "\n";
ss << ">>> " << file;
if (std::string_view func = get_func_name(ctx, rel.r_offset); !func.empty())
ss << ":(" << func << ")";
Symbol<E> &sym = *file.symbols[rel.r_sym];
typename decltype(ctx.undef_errors)::accessor acc;
ctx.undef_errors.insert(acc, {sym.name(), {}});
acc->second.push_back(ss.str());
}
// Report all undefined symbols, grouped by symbol.
template <typename E>
void report_undef_errors(Context<E> &ctx) {
constexpr i64 max_errors = 3;
for (auto &pair : ctx.undef_errors) {
std::string_view sym_name = pair.first;
std::span<std::string> errors = pair.second;
if (ctx.arg.demangle)
sym_name = demangle(sym_name);
std::stringstream ss;
ss << "undefined symbol: " << sym_name << "\n";
for (i64 i = 0; i < errors.size() && i < max_errors; i++)
ss << errors[i];
if (errors.size() > max_errors)
ss << ">>> referenced " << (errors.size() - max_errors) << " more times\n";
if (ctx.arg.unresolved_symbols == UNRESOLVED_ERROR)
Error(ctx) << ss.str();
else if (ctx.arg.unresolved_symbols == UNRESOLVED_WARN)
Warn(ctx) << ss.str();
}
ctx.checkpoint();
}
#define INSTANTIATE(E) \
template struct CieRecord<E>; \
template class InputSection<E>; \
template void report_undef(Context<E> &, InputFile<E> &, Symbol<E> &)
template void report_undef_errors(Context<E> &)
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
INSTANTIATE_ALL;
} // namespace mold::elf
+24 -20
View File
@@ -146,8 +146,9 @@ read_output_format(Context<E> &ctx, std::span<std::string_view> tok) {
template <typename E>
static bool is_in_sysroot(Context<E> &ctx, std::string path) {
std::string rel =
to_abs_path(path).lexically_relative(to_abs_path(ctx.arg.sysroot));
std::string rel = to_abs_path(path)
.lexically_relative(to_abs_path(ctx.arg.sysroot))
.string();
return rel != "." && !rel.starts_with("../");
}
@@ -225,6 +226,10 @@ void parse_linker_script(Context<E> &ctx, MappedFile<Context<E>> *mf) {
tok = skip(ctx, tok, "{");
read_version_script(ctx, tok);
tok = skip(ctx, tok, "}");
} else if (tok.size() > 3 && tok[1] == "=" && tok[3] == ";") {
ctx.arg.defsyms.emplace_back(get_symbol(ctx, unquote(tok[0])),
get_symbol(ctx, unquote(tok[2])));
tok = tok.subspan(4);
} else if (tok[0] == ";") {
tok = tok.subspan(1);
} else {
@@ -234,7 +239,7 @@ void parse_linker_script(Context<E> &ctx, MappedFile<Context<E>> *mf) {
}
template <typename E>
i64 get_script_output_type(Context<E> &ctx, MappedFile<Context<E>> *mf) {
MachineType get_script_output_type(Context<E> &ctx, MappedFile<Context<E>> *mf) {
current_file<E> = mf;
std::vector<std::string_view> vec = tokenize(ctx, mf->get_contents());
@@ -242,11 +247,11 @@ i64 get_script_output_type(Context<E> &ctx, MappedFile<Context<E>> *mf) {
if (tok.size() >= 3 && tok[0] == "OUTPUT_FORMAT" && tok[1] == "(") {
if (tok[2] == "elf64-x86-64")
return EM_X86_64;
return MachineType::X86_64;
if (tok[2] == "elf32-i386")
return EM_386;
return MachineType::I386;
}
return -1;
return MachineType::NONE;
}
static bool read_label(std::span<std::string_view> &tok,
@@ -267,6 +272,7 @@ template <typename E>
static void
read_version_script_commands(Context<E> &ctx, std::span<std::string_view> &tok,
u16 ver_idx, bool is_cpp) {
ctx.version_specified = true;
bool is_global = true;
while (!tok.empty() && tok[0] != "}") {
@@ -351,6 +357,8 @@ void parse_version_script(Context<E> &ctx, std::string path) {
template <typename E>
void read_dynamic_list_commands(Context<E> &ctx, std::span<std::string_view> &tok,
bool is_cpp) {
ctx.version_specified = true;
while (!tok.empty() && tok[0] != "}") {
if (tok[0] == "extern") {
tok = tok.subspan(1);
@@ -395,20 +403,16 @@ void parse_dynamic_list(Context<E> &ctx, std::string path) {
SyntaxError(ctx, tok[0]) << "trailing garbage token";
}
#define INSTANTIATE(E) \
template \
void parse_linker_script(Context<E> &ctx, MappedFile<Context<E>> *mf); \
template \
i64 get_script_output_type(Context<E> &ctx, MappedFile<Context<E>> *mf); \
template \
void parse_version_script(Context<E> &ctx, std::string path); \
template \
void parse_dynamic_list(Context<E> &ctx, std::string path)
#define INSTANTIATE(E) \
template \
void parse_linker_script(Context<E> &, MappedFile<Context<E>> *); \
template \
MachineType get_script_output_type(Context<E> &, MappedFile<Context<E>> *); \
template \
void parse_version_script(Context<E> &, std::string); \
template \
void parse_dynamic_list(Context<E> &, std::string);
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
INSTANTIATE_ALL;
} // namespace mold::elf
+103 -60
View File
@@ -79,13 +79,14 @@
// the compiler backend.
#include "mold.h"
#include "../lto.h"
#include "lto.h"
#include <cstdarg>
#include <cstring>
#include <dlfcn.h>
#include <fcntl.h>
#include <sstream>
#include <tbb/parallel_for_each.h>
#include <unistd.h>
#if 0
@@ -108,16 +109,33 @@ static std::vector<PluginSymbol> plugin_symbols;
static ClaimFileHandler *claim_file_hook;
static AllSymbolsReadHandler *all_symbols_read_hook;
static CleanupHandler *cleanup_hook;
static bool is_gcc_linker_api_v1 = false;
// Event handlers
static PluginStatus message(int level, const char *fmt, ...) {
template <typename E>
static PluginStatus message(PluginLevel level, const char *fmt, ...) {
LOG << "message\n";
Context<E> &ctx = *gctx<E>;
char buf[1000];
va_list ap;
va_start(ap, fmt);
fprintf(stderr, "mold: ");
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
switch (level) {
case LDPL_INFO:
SyncOut(ctx) << buf;
break;
case LDPL_WARNING:
Warn(ctx) << buf;
break;
case LDPL_ERROR:
case LDPL_FATAL:
Fatal(ctx) << buf;
}
return LDPS_OK;
}
@@ -304,6 +322,44 @@ get_symbols(const void *handle, int nsyms, PluginSymbol *psyms, bool is_v2) {
return LDPS_OK;
}
// This function restarts mold itself with `--:lto-pass2` and
// `--:ignore-ir-file` flags. We do this as a workaround for the old
// linker plugins that do not support the get_symbols_v3 API.
//
// get_symbols_v1 and get_symbols_v2 don't provide a way to ignore an
// object file we previously passed to the linker plugin. So we can't
// "unload" object files in archives that we ended up not choosing to
// include into the final output.
//
// As a workaround, we restart the linker with a list of object files
// the linker has to ignore, so that it won't read the object files
// from archives next time.
//
// This is an ugly hack and should be removed once GCC adopts the v3 API.
template <typename E>
static void restart_process(Context<E> &ctx) {
std::vector<const char *> args;
for (std::string_view arg : ctx.cmdline_args)
args.push_back(strdup(std::string(arg).c_str()));
for (std::unique_ptr<ObjectFile<E>> &file : ctx.obj_pool)
if (file->is_lto_obj && !file->is_alive)
args.push_back(strdup(("--:ignore-ir-file=" +
file->mf->get_identifier()).c_str()));
args.push_back("--:lto-pass2");
args.push_back(nullptr);
std::string self = std::filesystem::read_symlink("/proc/self/exe");
std::cout << std::flush;
std::cerr << std::flush;
execv(self.c_str(), (char * const *)args.data());
std::cerr << "execv failed: " << errno_string() << "\n";
_exit(1);
}
template <typename E>
static PluginStatus
get_symbols_v2(const void *handle, int nsyms, PluginSymbol *psyms) {
@@ -359,6 +415,27 @@ get_wrap_symbols(uint64_t *num_symbols, const char ***wrap_symbols) {
return LDPS_OK;
}
template <typename E>
static PluginLinkerAPIVersion
get_api_version(const char *plugin_identifier,
unsigned plugin_version,
int minimal_api_supported,
int maximal_api_supported,
const char **linker_identifier,
const char **linker_version) {
if (LAPI_V1 < minimal_api_supported)
Fatal(*gctx<E>) << "LTO plugin does not support V0 or V1 API";
*linker_identifier = "mold";
*linker_version = MOLD_VERSION;
if (LAPI_V1 <= maximal_api_supported) {
is_gcc_linker_api_v1 = true;
return LAPI_V1;
}
return LAPI_V0;
}
template <typename E>
static void load_plugin(Context<E> &ctx) {
assert(phase == 0);
@@ -379,7 +456,7 @@ static void load_plugin(Context<E> &ctx) {
};
std::vector<PluginTagValue> tv;
tv.emplace_back(LDPT_MESSAGE, message);
tv.emplace_back(LDPT_MESSAGE, message<E>);
if (ctx.arg.shared)
tv.emplace_back(LDPT_LINKER_OUTPUT, LDPO_DYN);
@@ -410,6 +487,7 @@ static void load_plugin(Context<E> &ctx) {
tv.emplace_back(LDPT_GET_INPUT_SECTION_CONTENTS, get_input_section_contents);
tv.emplace_back(LDPT_UPDATE_SECTION_ORDER, update_section_order);
tv.emplace_back(LDPT_ALLOW_SECTION_ORDERING, allow_section_ordering);
tv.emplace_back(LDPT_ADD_SYMBOLS_V2, add_symbols);
tv.emplace_back(LDPT_GET_SYMBOLS_V2, get_symbols_v2<E>);
tv.emplace_back(LDPT_ALLOW_UNIQUE_SEGMENT_FOR_SECTIONS,
allow_unique_segment_for_sections);
@@ -419,9 +497,11 @@ static void load_plugin(Context<E> &ctx) {
tv.emplace_back(LDPT_GET_INPUT_SECTION_SIZE, get_input_section_size);
tv.emplace_back(LDPT_REGISTER_NEW_INPUT_HOOK, register_new_input_hook<E>);
tv.emplace_back(LDPT_GET_WRAP_SYMBOLS, get_wrap_symbols);
tv.emplace_back(LDPT_GET_API_VERSION, get_api_version<E>);
tv.emplace_back(LDPT_NULL, 0);
onload(tv.data());
PluginStatus status = onload(tv.data());
assert(status == LDPS_OK);
}
template <typename E>
@@ -485,15 +565,19 @@ static bool is_llvm(Context<E> &ctx) {
}
// Returns true if a given linker plugin supports the get_symbols_v3 API.
// Currently, we simply assume that LLVM supports it and GCC does not.
// Any version of LLVM and GCC 12 or newer support it.
template <typename E>
static bool suppots_v3_api(Context<E> &ctx) {
return is_llvm(ctx);
return is_gcc_linker_api_v1 || is_llvm(ctx);
}
template <typename E>
ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
LOG << "read_lto_object: " << mf->name << "\n";
// V0 API's claim_file is not thread-safe.
static std::mutex mu;
std::unique_lock lock(mu, std::defer_lock);
if (!is_gcc_linker_api_v1)
lock.lock();
if (ctx.arg.plugin.empty())
Fatal(ctx) << mf->name << ": don't know how to handle this LTO object file "
@@ -539,7 +623,9 @@ ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
int claimed = false;
claim_file_hook(&file, &claimed);
if (!claimed)
Fatal(ctx) << mf->name << ": not claimed by the LTO plugin";
Fatal(ctx) << mf->name << ": not claimed by the LTO plugin;"
<< " please make sure you are using the same compiler of the"
<< " same version for all object files";
// It looks like GCC doesn't need fd after claim_file_hook() while
// LLVM needs it and takes the ownership of fd. To prevent "too many
@@ -564,44 +650,6 @@ ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
return obj;
}
// This function restarts mold itself with `--:lto-pass2` and
// `--:ignore-ir-file` flags. We do this as a workaround for the old
// linker plugins that do not support the get_symbols_v3 API.
//
// get_symbols_v1 and get_symbols_v2 don't provide a way to ignore an
// object file we previously passed to the linker plugin. So we can't
// "unload" object files in archives that we ended up not choosing to
// include into the final output.
//
// As a workaround, we restart the linker with a list of object files
// the linker has to ignore, so that it won't read the object files
// from archives next time.
//
// This is an ugly hack and should be removed once GCC adopts the v3 API.
template <typename E>
static void restart_process(Context<E> &ctx) {
std::vector<const char *> args;
for (std::string_view arg : ctx.cmdline_args)
args.push_back(strdup(std::string(arg).c_str()));
for (std::unique_ptr<ObjectFile<E>> &file : ctx.obj_pool)
if (file->is_lto_obj && !file->is_alive)
args.push_back(strdup(("--:ignore-ir-file=" +
file->mf->get_identifier()).c_str()));
args.push_back("--:lto-pass2");
args.push_back(nullptr);
std::string self = std::filesystem::read_symlink("/proc/self/exe");
std::cout << std::flush;
std::cerr << std::flush;
execv(self.c_str(), (char * const *)args.data());
std::cerr << "execv failed: " << errno_string() << "\n";
_exit(1);
}
// Entry point
template <typename E>
std::vector<ObjectFile<E> *> do_lto(Context<E> &ctx) {
@@ -614,21 +662,20 @@ std::vector<ObjectFile<E> *> do_lto(Context<E> &ctx) {
phase = 2;
// Set `referenced_by_regular_obj` bit.
for (ObjectFile<E> *file : ctx.objs) {
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
if (file->is_lto_obj)
continue;
return;
for (i64 i = file->first_global; i < file->symbols.size(); i++) {
ElfSym<E> &esym = file->elf_syms[i];
Symbol<E> &sym = *file->symbols[i];
if (esym.is_undef() && sym.file && sym.file != file &&
!sym.file->is_dso && ((ObjectFile<E> *)sym.file)->is_lto_obj) {
if (sym.file && !sym.file->is_dso &&
((ObjectFile<E> *)sym.file)->is_lto_obj) {
std::scoped_lock lock(sym.mu);
sym.referenced_by_regular_obj = true;
}
}
}
});
// all_symbols_read_hook() calls add_input_file() and add_input_library()
LOG << "all symbols read\n";
@@ -652,10 +699,6 @@ void lto_cleanup(Context<E> &ctx) {
template std::vector<ObjectFile<E> *> do_lto(Context<E> &); \
template void lto_cleanup(Context<E> &)
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
INSTANTIATE_ALL;
} // namespace mold::elf
+31
View File
@@ -0,0 +1,31 @@
#ifdef _WIN32
#include "mold.h"
#include "lto.h"
namespace mold::elf {
template <typename E>
ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
Fatal(ctx) << "LTO is not supported on Windows";
}
template <typename E>
std::vector<ObjectFile<E> *> do_lto(Context<E> &ctx) {
return {};
}
template <typename E>
void lto_cleanup(Context<E> &ctx) {}
#define INSTANTIATE(E) \
template ObjectFile<E> * \
read_lto_object(Context<E> &, MappedFile<Context<E>> *); \
template std::vector<ObjectFile<E> *> do_lto(Context<E> &); \
template void lto_cleanup(Context<E> &)
INSTANTIATE_ALL;
} // namespace mold::elf
#endif
+12 -6
View File
@@ -44,6 +44,7 @@ enum PluginTag {
LDPT_REGISTER_NEW_INPUT_HOOK,
LDPT_GET_WRAP_SYMBOLS,
LDPT_ADD_SYMBOLS_V2,
LDPT_GET_API_VERSION,
};
enum PluginApiVersion {
@@ -72,15 +73,15 @@ enum PluginOutputFileType {
struct PluginInputFile {
const char *name;
int fd;
off_t offset;
off_t filesize;
int32_t fd;
uint64_t offset;
uint64_t filesize;
void *handle;
};
struct PluginSection {
const void *handle;
int shndx;
uint32_t shndx;
};
struct PluginSymbol {
@@ -89,10 +90,10 @@ struct PluginSymbol {
char def;
char symbol_type;
char section_kind;
int visibility;
int32_t visibility;
uint64_t size;
char *comdat_key;
int resolution;
int32_t resolution;
};
enum PluginSymbolKind {
@@ -141,6 +142,11 @@ enum PluginLevel {
LDPL_FATAL,
};
enum PluginLinkerAPIVersion {
LAPI_V0 = 0,
LAPI_V1,
};
typedef PluginStatus OnloadFn(PluginTagValue *tv);
typedef PluginStatus ClaimFileHandler(const PluginInputFile *, int *);
typedef PluginStatus AllSymbolsReadHandler();
+184 -204
View File
@@ -1,6 +1,7 @@
#include "mold.h"
#include "../archive-file.h"
#include "../cmdline.h"
#include "../output-file.h"
#include <cstring>
#include <functional>
@@ -11,20 +12,78 @@
#include <sys/types.h>
#include <tbb/global_control.h>
#include <tbb/parallel_for_each.h>
#include <unistd.h>
#include <unordered_set>
#ifdef _WIN32
# include <direct.h>
# define _chdir chdir
#else
# include <unistd.h>
#endif
namespace mold::elf {
// Read the beginning of a given file and returns its machine type
// (e.g. EM_X86_64 or EM_386).
template <typename E>
static MachineType get_machine_type(Context<E> &ctx, MappedFile<Context<E>> *mf) {
auto get_elf_type = [](u8 *buf) {
switch (ElfEhdr<E> &ehdr = *(ElfEhdr<E> *)buf; ehdr.e_machine) {
case EM_386:
return MachineType::I386;
case EM_X86_64:
return MachineType::X86_64;
case EM_ARM:
return MachineType::ARM32;
case EM_AARCH64:
return MachineType::ARM64;
case EM_RISCV:
return (ehdr.e_ident[EI_CLASS] == ELFCLASS64)
? MachineType::RISCV64 : MachineType::RISCV32;
default:
return MachineType::NONE;
}
};
switch (get_file_type(mf)) {
case FileType::ELF_OBJ:
case FileType::ELF_DSO:
case FileType::GCC_LTO_OBJ:
return get_elf_type(mf->data);
case FileType::AR:
for (MappedFile<Context<E>> *child : read_fat_archive_members(ctx, mf))
if (get_file_type(child) == FileType::ELF_OBJ)
return get_elf_type(child->data);
return MachineType::NONE;
case FileType::THIN_AR:
for (MappedFile<Context<E>> *child : read_thin_archive_members(ctx, mf))
if (get_file_type(child) == FileType::ELF_OBJ)
return get_elf_type(child->data);
return MachineType::NONE;
case FileType::TEXT:
return get_script_output_type(ctx, mf);
default:
return MachineType::NONE;
}
}
template <typename E>
static void
check_file_compatibility(Context<E> &ctx, MappedFile<Context<E>> *mf) {
MachineType mt = get_machine_type(ctx, mf);
if (mt != ctx.arg.emulation)
Fatal(ctx) << mf->name << ": incompatible file type: "
<< ctx.arg.emulation << " is expected but got " << mt;
}
template <typename E>
static ObjectFile<E> *new_object_file(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::string archive_name) {
if (i64 type = ((ElfEhdr<E> *)mf->data)->e_machine; type != E::e_machine)
Fatal(ctx) << mf->name << ": incompatible file type: " << type;
static Counter count("parsed_objs");
count++;
check_file_compatibility(ctx, mf);
bool in_lib = ctx.in_lib || (!archive_name.empty() && !ctx.whole_archive);
ObjectFile<E> *file = ObjectFile<E>::create(ctx, mf, archive_name, in_lib);
file->priority = ctx.file_priority++;
@@ -41,10 +100,11 @@ static ObjectFile<E> *new_lto_obj(Context<E> &ctx, MappedFile<Context<E>> *mf,
count++;
if (ctx.arg.ignore_ir_file.count(mf->get_identifier()))
return new ObjectFile<E>;
return nullptr;
ObjectFile<E> *file = read_lto_object(ctx, mf);
file->priority = ctx.file_priority++;
file->archive_name = archive_name;
file->is_in_lib = ctx.in_lib || (!archive_name.empty() && !ctx.whole_archive);
file->is_alive = !file->is_in_lib;
ctx.has_lto_object = true;
@@ -56,8 +116,7 @@ static ObjectFile<E> *new_lto_obj(Context<E> &ctx, MappedFile<Context<E>> *mf,
template <typename E>
static SharedFile<E> *
new_shared_file(Context<E> &ctx, MappedFile<Context<E>> *mf) {
if (i64 type = ((ElfEhdr<E> *)mf->data)->e_machine; type != E::e_machine)
Fatal(ctx) << mf->name << ": incompatible file type: " << type;
check_file_compatibility(ctx, mf);
SharedFile<E> *file = SharedFile<E>::create(ctx, mf);
file->priority = ctx.file_priority++;
@@ -90,7 +149,8 @@ void read_file(Context<E> &ctx, MappedFile<Context<E>> *mf) {
break;
case FileType::GCC_LTO_OBJ:
case FileType::LLVM_BITCODE:
ctx.objs.push_back(new_lto_obj(ctx, child, mf->name));
if (ObjectFile<E> *file = new_lto_obj(ctx, child, mf->name))
ctx.objs.push_back(file);
break;
default:
break;
@@ -103,47 +163,22 @@ void read_file(Context<E> &ctx, MappedFile<Context<E>> *mf) {
return;
case FileType::GCC_LTO_OBJ:
case FileType::LLVM_BITCODE:
ctx.objs.push_back(new_lto_obj(ctx, mf, ""));
if (ObjectFile<E> *file = new_lto_obj(ctx, mf, ""))
ctx.objs.push_back(file);
return;
default:
Fatal(ctx) << mf->name << ": unknown file type: " << type;
}
}
// Read the beginning of a given file and returns its machine type
// (e.g. EM_X86_64 or EM_386). Return -1 if unknown.
template <typename E>
static i64 get_machine_type(Context<E> &ctx, MappedFile<Context<E>> *mf) {
switch (get_file_type(mf)) {
case FileType::ELF_OBJ:
case FileType::ELF_DSO:
case FileType::GCC_LTO_OBJ:
return ((ElfEhdr<E> *)mf->data)->e_machine;
case FileType::AR:
for (MappedFile<Context<E>> *child : read_fat_archive_members(ctx, mf))
if (get_file_type(child) == FileType::ELF_OBJ)
return ((ElfEhdr<E> *)child->data)->e_machine;
return -1;
case FileType::THIN_AR:
for (MappedFile<Context<E>> *child : read_thin_archive_members(ctx, mf))
if (get_file_type(child) == FileType::ELF_OBJ)
return ((ElfEhdr<E> *)child->data)->e_machine;
return -1;
case FileType::TEXT:
return get_script_output_type(ctx, mf);
default:
return -1;
Fatal(ctx) << mf->name << ": unknown file type";
}
}
template <typename E>
static i64
deduce_machine_type(Context<E> &ctx, std::span<std::string_view> args) {
static MachineType
deduce_machine_type(Context<E> &ctx, std::span<std::string> args) {
for (std::string_view arg : args)
if (!arg.starts_with('-'))
if (auto *mf = MappedFile<Context<E>>::open(ctx, std::string(arg)))
if (i64 type = get_machine_type(ctx, mf); type != -1)
return type;
if (MachineType ty = get_machine_type(ctx, mf); ty != MachineType::NONE)
return ty;
Fatal(ctx) << "-m option is missing";
}
@@ -153,10 +188,10 @@ MappedFile<Context<E>> *open_library(Context<E> &ctx, std::string path) {
if (!mf)
return nullptr;
i64 type = get_machine_type(ctx, mf);
if (type == -1 || type == E::e_machine)
MachineType ty = get_machine_type(ctx, mf);
if (ty == MachineType::NONE || ty == E::machine_type)
return mf;
Warn(ctx) << path << ": skipping incompatible file " << (int)type
Warn(ctx) << path << ": skipping incompatible file " << (int)ty
<< " " << (int)E::e_machine;
return nullptr;
}
@@ -184,51 +219,59 @@ MappedFile<Context<E>> *find_library(Context<E> &ctx, std::string name) {
}
template <typename E>
static void read_input_files(Context<E> &ctx, std::span<std::string_view> args) {
static void read_input_files(Context<E> &ctx, std::span<std::string> args) {
Timer t(ctx, "read_input_files");
std::vector<std::tuple<bool, bool, bool, bool>> state;
ctx.is_static = ctx.arg.is_static;
while (!args.empty()) {
std::string_view arg;
std::string_view arg = args[0];
args = args.subspan(1);
if (read_flag(args, "as-needed")) {
if (arg == "--as-needed") {
ctx.as_needed = true;
} else if (read_flag(args, "no-as-needed")) {
} else if (arg == "--no-as-needed") {
ctx.as_needed = false;
} else if (read_flag(args, "whole-archive")) {
} else if (arg == "--whole-archive") {
ctx.whole_archive = true;
} else if (read_flag(args, "no-whole-archive")) {
} else if (arg == "--no-whole-archive") {
ctx.whole_archive = false;
} else if (read_flag(args, "Bstatic")) {
} else if (arg == "--Bstatic") {
ctx.is_static = true;
} else if (read_flag(args, "Bdynamic")) {
} else if (arg == "--Bdynamic") {
ctx.is_static = false;
} else if (read_flag(args, "start-lib")) {
} else if (arg == "--start-lib") {
ctx.in_lib = true;
} else if (read_flag(args, "end-lib")) {
} else if (arg == "--end-lib") {
ctx.in_lib = false;
} else if (read_arg(ctx, args, arg, "version-script")) {
} else if (remove_prefix(arg, "--version-script=")) {
parse_version_script(ctx, std::string(arg));
} else if (read_arg(ctx, args, arg, "dynamic-list")) {
} else if (remove_prefix(arg, "--dynamic-list=")) {
parse_dynamic_list(ctx, std::string(arg));
} else if (read_flag(args, "push-state")) {
} else if (remove_prefix(arg, "--export-dynamic-symbol=")) {
if (arg == "*")
ctx.default_version = VER_NDX_GLOBAL;
else
ctx.version_patterns.push_back({arg, VER_NDX_GLOBAL, false});
ctx.version_specified = true;
} else if (remove_prefix(arg, "--export-dynamic-symbol-list=")) {
parse_dynamic_list(ctx, std::string(arg));
} else if (arg == "--push-state") {
state.push_back({ctx.as_needed, ctx.whole_archive, ctx.is_static,
ctx.in_lib});
} else if (read_flag(args, "pop-state")) {
} else if (arg == "--pop-state") {
if (state.empty())
Fatal(ctx) << "no state pushed before popping";
std::tie(ctx.as_needed, ctx.whole_archive, ctx.is_static, ctx.in_lib) =
state.back();
state.pop_back();
} else if (read_arg(ctx, args, arg, "l")) {
} else if (remove_prefix(arg, "-l")) {
MappedFile<Context<E>> *mf = find_library(ctx, std::string(arg));
mf->given_fullpath = false;
read_file(ctx, mf);
} else {
read_file(ctx, MappedFile<Context<E>>::must_open(ctx, std::string(args[0])));
args = args.subspan(1);
read_file(ctx, MappedFile<Context<E>>::must_open(ctx, std::string(arg)));
}
}
@@ -238,56 +281,6 @@ static void read_input_files(Context<E> &ctx, std::span<std::string_view> args)
ctx.tg.wait();
}
template <typename E>
static i64 get_mtime(Context<E> &ctx, std::string path) {
struct stat st;
if (stat(path.c_str(), &st) < 0)
Fatal(ctx) << path << ": stat failed: " << errno_string();
return st.st_mtime;
}
template <typename E>
static bool reload_input_files(Context<E> &ctx) {
Timer t(ctx, "reload_input_files");
std::vector<ObjectFile<E> *> objs;
std::vector<SharedFile<E> *> dsos;
// Reload updated .o files
for (ObjectFile<E> *file : ctx.objs) {
if (file->mf->parent) {
if (get_mtime(ctx, file->mf->parent->name) != file->mf->parent->mtime)
return false;
objs.push_back(file);
continue;
}
if (get_mtime(ctx, file->mf->name) == file->mf->mtime) {
objs.push_back(file);
continue;
}
MappedFile<Context<E>> *mf =
MappedFile<Context<E>>::must_open(ctx, file->mf->name);
objs.push_back(new_object_file(ctx, mf, file->mf->name));
}
// Reload updated .so files
for (SharedFile<E> *file : ctx.dsos) {
if (get_mtime(ctx, file->mf->name) == file->mf->mtime) {
dsos.push_back(file);
} else {
MappedFile<Context<E>> *mf =
MappedFile<Context<E>>::must_open(ctx, file->mf->name);
dsos.push_back(new_shared_file(ctx, mf));
}
}
ctx.objs = objs;
ctx.dsos = dsos;
return true;
}
template <typename E>
static void show_stats(Context<E> &ctx) {
for (ObjectFile<E> *obj : ctx.objs) {
@@ -342,7 +335,7 @@ static void show_stats(Context<E> &ctx) {
static Counter num_objs("num_objs", ctx.objs.size());
static Counter num_dsos("num_dsos", ctx.dsos.size());
if constexpr (std::is_same_v<E, ARM64>) {
if constexpr (needs_thunk<E>) {
static Counter num_thunks("num_thunks");
for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections)
for (std::unique_ptr<RangeExtensionThunk<E>> &thunk : osec->thunks)
@@ -355,62 +348,49 @@ static void show_stats(Context<E> &ctx) {
sec->print_stats(ctx);
}
static i64 get_default_thread_count() {
// mold doesn't scale well above 32 threads.
int n = tbb::global_control::active_value(
tbb::global_control::max_allowed_parallelism);
return std::min(n, 32);
}
template <typename E>
static int elf_main(int argc, char **argv) {
Context<E> ctx;
// Process -run option first. process_run_subcommand() does not return.
if (argc >= 2)
if (argv[1] == "-run"sv || argv[1] == "--run"sv)
process_run_subcommand(ctx, argc, argv);
if (argc >= 2 && (argv[1] == "-run"sv || argv[1] == "--run"sv)) {
#if defined(_WIN32) || defined(__APPLE__)
Fatal(ctx) << ": -run is supported only on Unix";
#endif
process_run_subcommand(ctx, argc, argv);
}
// Parse non-positional command line options
ctx.cmdline_args = expand_response_files(ctx, argv);
std::vector<std::string_view> file_args;
parse_nonpositional_args(ctx, file_args);
std::vector<std::string> file_args = parse_nonpositional_args(ctx);
// If no -m option is given, deduce it from input files.
if (ctx.arg.emulation == -1)
if (ctx.arg.emulation == MachineType::NONE)
ctx.arg.emulation = deduce_machine_type(ctx, file_args);
// Redo if -m is not x86-64.
if (ctx.arg.emulation != E::e_machine) {
if (ctx.arg.emulation != E::machine_type) {
#ifndef MOLD_DEBUG_X86_64_ONLY
switch (ctx.arg.emulation) {
case EM_386:
case MachineType::I386:
return elf_main<I386>(argc, argv);
case EM_AARCH64:
case MachineType::ARM64:
return elf_main<ARM64>(argc, argv);
case EM_ARM:
case MachineType::ARM32:
return elf_main<ARM32>(argc, argv);
case EM_RISCV:
case MachineType::RISCV64:
return elf_main<RISCV64>(argc, argv);
case MachineType::RISCV32:
return elf_main<RISCV32>(argc, argv);
default:
unreachable();
}
#endif
unreachable();
}
Timer t_all(ctx, "all");
if (ctx.arg.relocatable) {
combine_objects(ctx, file_args);
return 0;
}
if (!ctx.arg.preload)
try_resume_daemon(ctx);
i64 thread_count = ctx.arg.thread_count;
if (thread_count == 0)
thread_count = get_default_thread_count();
tbb::global_control tbb_cont(tbb::global_control::max_allowed_parallelism,
thread_count);
install_signal_handler();
if (!ctx.arg.directory.empty())
@@ -418,6 +398,22 @@ static int elf_main(int argc, char **argv) {
Fatal(ctx) << "chdir failed: " << ctx.arg.directory
<< ": " << errno_string();
if (ctx.arg.relocatable) {
combine_objects(ctx, file_args);
return 0;
}
// Fork a subprocess unless --no-fork is given.
std::function<void()> on_complete;
#if !defined(_WIN32) && !defined(__APPLE__)
if (ctx.arg.fork)
on_complete = fork_child();
#endif
tbb::global_control tbb_cont(tbb::global_control::max_allowed_parallelism,
ctx.arg.thread_count);
// Handle --wrap options if any.
for (std::string_view name : ctx.arg.wrap)
get_symbol(ctx, name)->wrap = true;
@@ -427,31 +423,12 @@ static int elf_main(int argc, char **argv) {
for (std::string_view name : *ctx.arg.retain_symbols_file)
get_symbol(ctx, name)->write_to_symtab = true;
// Preload input files
std::function<void()> on_complete;
std::function<void()> wait_for_client;
if (ctx.arg.preload)
daemonize(ctx, &wait_for_client, &on_complete);
else if (ctx.arg.fork)
on_complete = fork_child();
for (std::string_view arg : ctx.arg.trace_symbol)
get_symbol(ctx, arg)->traced = true;
// Parse input files
read_input_files(ctx, file_args);
if (ctx.arg.preload) {
wait_for_client();
if (!reload_input_files(ctx)) {
std::vector<char *> args(argv, argv + argc);
args.push_back((char *)"--no-preload");
args.push_back(nullptr);
return elf_main<E>(argc + 1, args.data());
}
}
// Uniquify shared object files by soname
{
std::unordered_set<std::string_view> seen;
@@ -466,6 +443,9 @@ static int elf_main(int argc, char **argv) {
// Apply -exclude-libs
apply_exclude_libs(ctx);
// Create a dummy file containing linker-synthesized symbols.
create_internal_file(ctx);
// Resolve symbols and fix the set of object files that are
// included to the final output.
resolve_symbols(ctx);
@@ -488,6 +468,10 @@ static int elf_main(int argc, char **argv) {
// Set is_import and is_export bits for each symbol.
compute_import_export(ctx);
// Read address-significant section information.
if (ctx.arg.icf && !ctx.arg.icf_all)
mark_addrsig(ctx);
// Garbage-collect unreachable sections.
if (ctx.arg.gc_sections)
gc_sections(ctx);
@@ -509,10 +493,8 @@ static int elf_main(int argc, char **argv) {
// Get a list of output sections.
append(ctx.chunks, collect_output_sections(ctx));
// Create a dummy file containing linker-synthesized symbols
// (e.g. `__bss_start`).
ctx.internal_obj = create_internal_file(ctx);
ctx.objs.push_back(ctx.internal_obj);
// Add synthetic symbols such as __ehdr_start or __end.
add_synthetic_symbols(ctx);
// Beyond this point, no new files will be added to ctx.objs
// or ctx.dsos.
@@ -521,6 +503,12 @@ static int elf_main(int argc, char **argv) {
if (ctx.arg.z_cet_report != CET_REPORT_NONE)
check_cet_errors(ctx);
// Handle `-z execstack-if-needed`.
if (ctx.arg.z_execstack_if_needed)
for (ObjectFile<E> *file : ctx.objs)
if (file->needs_executable_stack)
ctx.arg.z_execstack = true;
// If we are linking a .so file, remaining undefined symbols does
// not cause a linker error. Instead, they are treated as if they
// were imported symbols.
@@ -532,6 +520,12 @@ static int elf_main(int argc, char **argv) {
// Beyond this point, no new symbols will be added to the result.
// Handle --print-dependencies
if (ctx.arg.print_dependencies == 1)
print_dependencies(ctx);
else if (ctx.arg.print_dependencies == 2)
print_dependencies_full(ctx);
// Handle -repro
if (ctx.arg.repro)
write_repro_file(ctx);
@@ -557,16 +551,6 @@ static int elf_main(int argc, char **argv) {
if (ctx.arg.shuffle_sections != SHUFFLE_SECTIONS_NONE)
shuffle_sections(ctx);
// Compute sizes of output sections while assigning offsets
// within an output section to input sections.
compute_section_sizes(ctx);
// Sort sections by section attributes so that we'll have to
// create as few segments as possible.
sort(ctx.chunks, [&](Chunk<E> *a, Chunk<E> *b) {
return get_section_rank(ctx, a) < get_section_rank(ctx, b);
});
// Copy string referred by .dynamic to .dynstr.
for (SharedFile<E> *file : ctx.dsos)
ctx.dynstr->add_string(file->soname);
@@ -583,6 +567,16 @@ static int elf_main(int argc, char **argv) {
// .got.plt, .dynsym, .dynstr, etc.
scan_rels(ctx);
// Compute sizes of output sections while assigning offsets
// within an output section to input sections.
compute_section_sizes(ctx);
// Sort sections by section attributes so that we'll have to
// create as few segments as possible.
sort(ctx.chunks, [&](Chunk<E> *a, Chunk<E> *b) {
return get_section_rank(ctx, a) < get_section_rank(ctx, b);
});
// If --packed_dyn_relocs=relr was given, base relocations are stored
// to a .relr.dyn section in a compressed form. Construct a compressed
// relocations now so that we can fix section sizes and file layout.
@@ -594,6 +588,10 @@ static int elf_main(int argc, char **argv) {
// be added to .dynsym.
ctx.dynsym->finalize(ctx);
// Print reports about undefined symbols, if needed.
if (ctx.arg.unresolved_symbols == UNRESOLVED_ERROR)
report_undef_errors(ctx);
// Fill .gnu.version_d section contents.
if (ctx.verdef)
ctx.verdef->construct(ctx);
@@ -624,12 +622,12 @@ static int elf_main(int argc, char **argv) {
chunk->update_shdr(ctx);
std::erase_if(ctx.chunks, [](Chunk<E> *chunk) {
return !chunk->is_output_section() && chunk->shdr.sh_size == 0;
return chunk->kind() != OUTPUT_SECTION && chunk->shdr.sh_size == 0;
});
// Set section indices.
for (i64 i = 0, shndx = 1; i < ctx.chunks.size(); i++)
if (!ctx.chunks[i]->is_header())
if (ctx.chunks[i]->kind() != HEADER)
ctx.chunks[i]->shndx = shndx++;
// Some types of section header refer other section by index.
@@ -640,17 +638,11 @@ static int elf_main(int argc, char **argv) {
// Assign offsets to output sections
i64 filesize = set_osec_offsets(ctx);
// On ARM64, we may need to create so-called "range extension thunks"
// to extend branch instructions reach, as they can jump only to
// ±128 MiB.
if constexpr (std::is_same_v<E, ARM64>)
filesize = create_range_extension_thunks(ctx);
// On RISC-V, branches are encode using multiple instructions so
// that they can jump to anywhere in ±2 GiB by default. They may
// be replaced with shorter instruction sequences if destinations
// are close enough. Do this optimization.
if constexpr (std::is_same_v<E, RISCV64>)
if constexpr (is_riscv<E>)
filesize = riscv_resize_sections(ctx);
// Fix linker-synthesized symbol addresses.
@@ -669,7 +661,8 @@ static int elf_main(int argc, char **argv) {
t_before_copy.stop();
// Create an output file
ctx.output_file = OutputFile<E>::open(ctx, ctx.arg.output, filesize, 0777);
ctx.output_file =
OutputFile<Context<E>>::open(ctx, ctx.arg.output, filesize, 0777);
ctx.buf = ctx.output_file->buf;
Timer t_copy(ctx, "copy");
@@ -685,12 +678,9 @@ static int elf_main(int argc, char **argv) {
chunk->copy_buf(ctx);
});
ctx.checkpoint();
report_undef_errors(ctx);
}
if constexpr (std::is_same_v<E, ARM64>)
write_thunks(ctx);
if constexpr (std::is_same_v<E, ARM32>)
sort_arm_exidx(ctx);
@@ -707,12 +697,11 @@ static int elf_main(int argc, char **argv) {
// Zero-clear paddings between sections
clear_padding(ctx);
if (ctx.buildid) {
Timer t(ctx, "build_id");
if (ctx.buildid)
ctx.buildid->write_buildid(ctx);
}
t_copy.stop();
ctx.checkpoint();
// Close the output file. This is the end of the linker's main job.
ctx.output_file->close(ctx);
@@ -721,12 +710,6 @@ static int elf_main(int argc, char **argv) {
if (!ctx.arg.dependency_file.empty())
write_dependency_file(ctx);
// Handle --print-dependencies
if (ctx.arg.print_dependencies == 1)
print_dependencies(ctx);
else if (ctx.arg.print_dependencies == 2)
print_dependencies_full(ctx);
if (ctx.has_lto_object)
lto_cleanup(ctx);
@@ -753,6 +736,7 @@ static int elf_main(int argc, char **argv) {
for (std::function<void()> &fn : ctx.on_exit)
fn();
ctx.checkpoint();
return 0;
}
@@ -763,10 +747,6 @@ int main(int argc, char **argv) {
#define INSTANTIATE(E) \
template void read_file(Context<E> &, MappedFile<Context<E>> *);
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
INSTANTIATE_ALL;
} // namespace mold::elf
+5 -6
View File
@@ -40,6 +40,9 @@ static Map<E> get_map(Context<E> &ctx) {
}
});
if (map.size() <= 1)
return map;
tbb::parallel_for(map.range(), [](const typename Map<E>::range_type &range) {
for (auto it = range.begin(); it != range.end(); it++) {
std::vector<Symbol<E> *> &vec = it->second;
@@ -72,7 +75,7 @@ void print_map(Context<E> &ctx) {
<< std::setw(6) << (u64)osec->shdr.sh_addralign
<< " " << osec->name << "\n";
if (!osec->is_output_section())
if (osec->kind() != OUTPUT_SECTION)
continue;
std::span<InputSection<E> *> members = ((OutputSection<E> *)osec)->members;
@@ -109,10 +112,6 @@ void print_map(Context<E> &ctx) {
#define INSTANTIATE(E) \
template void print_map(Context<E> &ctx);
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
INSTANTIATE_ALL;
} // namespace mold::elf
+31 -26
View File
@@ -1,5 +1,8 @@
#define _GNU_SOURCE 1
#if !defined(__OpenBSD__) && !defined(__FreeBSD__)
# include <alloca.h>
#endif
#include <dlfcn.h>
#include <spawn.h>
#include <stdarg.h>
@@ -31,13 +34,25 @@ static void debug_print(char *fmt, ...) {
va_end(ap);
}
static void get_args(va_list *ap, int argc, char **argv) {
for (int i = 1; i < argc - 1; i++) {
char *arg = va_arg(*ap, char *);
if (!arg)
break;
argv[i] = arg;
}
static int count_args(va_list *ap) {
va_list aq;
va_copy(aq, *ap);
int i = 0;
while (va_arg(aq, char *))
i++;
va_end(aq);
return i;
}
static void copy_args(char **argv, const char *arg0, va_list *ap) {
int i = 1;
char *arg;
while ((arg = va_arg(*ap, char *)))
argv[i++] = arg;
((const char **)argv)[0] = arg0;
((const char **)argv)[i] = NULL;
}
static bool is_ld(const char *path) {
@@ -52,10 +67,8 @@ static bool is_ld(const char *path) {
int execvpe(const char *file, char *const *argv, char *const *envp) {
debug_print("execvpe %s\n", file);
if (!strcmp(file, "ld") || is_ld(file)) {
if (!strcmp(file, "ld") || is_ld(file))
file = get_mold_path();
((const char **)argv)[0] = file;
}
for (int i = 0; envp[i]; i++)
putenv(envp[i]);
@@ -66,12 +79,8 @@ int execvpe(const char *file, char *const *argv, char *const *envp) {
int execve(const char *path, char *const *argv, char *const *envp) {
debug_print("execve %s\n", path);
if (is_ld(path)) {
if (is_ld(path))
path = get_mold_path();
((const char **)argv)[0] = path;
}
typeof(execve) *real = dlsym(RTLD_NEXT, "execve");
return real(path, argv, envp);
}
@@ -79,8 +88,8 @@ int execve(const char *path, char *const *argv, char *const *envp) {
int execl(const char *path, const char *arg0, ...) {
va_list ap;
va_start(ap, arg0);
char *argv[4096] = {(char *)arg0};
get_args(&ap, 4096, argv);
char **argv = alloca((count_args(&ap) + 2) * sizeof(char *));
copy_args(argv, arg0, &ap);
va_end(ap);
return execve(path, argv, environ);
}
@@ -88,8 +97,8 @@ int execl(const char *path, const char *arg0, ...) {
int execlp(const char *file, const char *arg0, ...) {
va_list ap;
va_start(ap, arg0);
char *argv[4096] = {(char *)arg0};
get_args(&ap, 4096, argv);
char **argv = alloca((count_args(&ap) + 2) * sizeof(char *));
copy_args(argv, arg0, &ap);
va_end(ap);
return execvpe(file, argv, environ);
}
@@ -97,8 +106,8 @@ int execlp(const char *file, const char *arg0, ...) {
int execle(const char *path, const char *arg0, ...) {
va_list ap;
va_start(ap, arg0);
char *argv[4096] = {(char *)arg0};
get_args(&ap, 4096, argv);
char **argv = alloca((count_args(&ap) + 2) * sizeof(char *));
copy_args(argv, arg0, &ap);
char **env = va_arg(ap, char **);
va_end(ap);
return execve(path, argv, env);
@@ -117,12 +126,8 @@ int posix_spawn(pid_t *pid, const char *path,
const posix_spawnattr_t *attrp,
char *const *argv, char *const *envp) {
debug_print("posix_spawn %s\n", path);
if (is_ld(path)) {
if (is_ld(path))
path = get_mold_path();
((const char **)argv)[0] = path;
}
typeof(posix_spawn) *real = dlsym(RTLD_NEXT, "posix_spawn");
return real(pid, path, file_actions, attrp, argv, envp);
}
+393 -522
View File
File diff suppressed because it is too large. Load diff
+453 -676
View File
File diff suppressed because it is too large. Load diff
+217 -150
View File
@@ -34,7 +34,8 @@ template <typename E>
void create_synthetic_sections(Context<E> &ctx) {
auto push = [&]<typename T>(T *x) {
ctx.chunks.push_back(x);
return std::unique_ptr<T>(x);
ctx.chunk_pool.emplace_back(x);
return x;
};
if (!ctx.arg.oformat_binary) {
@@ -82,13 +83,9 @@ void create_synthetic_sections(Context<E> &ctx) {
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 (std::is_same_v<E, ARM32>) {
ctx.thumb_to_arm = push(new ThumbToArmSection);
ctx.tls_trampoline = push(new TlsTrampolineSection);
}
// If .dynamic exists, .dynsym and .dynstr must exist as well
// since .dynamic refers them.
if (ctx.dynamic) {
@@ -128,8 +125,6 @@ static void mark_live_objects(Context<E> &ctx) {
template <typename E>
void do_resolve_symbols(Context<E> &ctx) {
Timer t(ctx, "do_resolve_symbols");
auto for_each_file = [&](std::function<void(InputFile<E> *)> fn) {
tbb::parallel_for_each(ctx.objs, fn);
tbb::parallel_for_each(ctx.dsos, fn);
@@ -163,7 +158,7 @@ void do_resolve_symbols(Context<E> &ctx) {
template <typename E>
void resolve_symbols(Context<E> &ctx) {
Timer t(ctx, "do_resolve_symbols");
Timer t(ctx, "resolve_symbols");
std::vector<ObjectFile<E> *> objs = ctx.objs;
std::vector<SharedFile<E> *> dsos = ctx.dsos;
@@ -207,7 +202,7 @@ void resolve_symbols(Context<E> &ctx) {
tbb::parallel_for_each(ctx.dsos, [&](SharedFile<E> *file) {
file->clear_symbols();
file->is_alive = true;
file->is_alive = !file->is_needed;
});
do_resolve_symbols(ctx);
@@ -352,28 +347,55 @@ void bin_sections(Context<E> &ctx) {
// Create a dummy object file containing linker-synthesized
// symbols.
template <typename E>
ObjectFile<E> *create_internal_file(Context<E> &ctx) {
void create_internal_file(Context<E> &ctx) {
ObjectFile<E> *obj = new ObjectFile<E>;
ctx.obj_pool.emplace_back(obj);
ctx.internal_obj = obj;
ctx.objs.push_back(obj);
// Create linker-synthesized symbols.
auto *esyms = new std::vector<ElfSym<E>>(1);
ctx.internal_esyms.resize(1);
obj->symbols.push_back(new Symbol<E>);
obj->first_global = 1;
obj->is_alive = true;
obj->features = -1;
obj->priority = 1;
auto add = [&](std::string_view name, u8 st_type = STT_NOTYPE) {
ElfSym<E> esym = {};
esym.st_type = st_type;
// Add symbols for --defsym.
for (i64 i = 0; i < ctx.arg.defsyms.size(); i++) {
Symbol<E> *sym = ctx.arg.defsyms[i].first;
obj->symbols.push_back(sym);
ElfSym<E> esym;
memset(&esym, 0, sizeof(esym));
esym.st_type = STT_NOTYPE;
esym.st_shndx = SHN_ABS;
esym.st_bind = STB_GLOBAL;
esym.st_visibility = STV_DEFAULT;
ctx.internal_esyms.push_back(esym);
};
obj->elf_syms = ctx.internal_esyms;
obj->sym_fragments.resize(ctx.internal_esyms.size());
obj->symvers.resize(ctx.internal_esyms.size() - 1);
}
template <typename E>
void add_synthetic_symbols(Context<E> &ctx) {
ObjectFile<E> &obj = *ctx.internal_obj;
auto add = [&](std::string_view name) {
ElfSym<E> esym;
memset(&esym, 0, sizeof(esym));
esym.st_type = STT_NOTYPE;
esym.st_shndx = SHN_ABS;
esym.st_bind = STB_GLOBAL;
esym.st_visibility = STV_HIDDEN;
esyms->push_back(esym);
ctx.internal_esyms.push_back(esym);
Symbol<E> *sym = get_symbol(ctx, name);
obj->symbols.push_back(sym);
obj.symbols.push_back(sym);
return sym;
};
@@ -393,9 +415,9 @@ ObjectFile<E> *create_internal_file(Context<E> &ctx) {
ctx.__executable_start = add("__executable_start");
ctx.__rel_iplt_start =
add(E::is_rel ? "__rel_iplt_start" : "__rela_iplt_start");
add(is_rela<E> ? "__rela_iplt_start" : "__rel_iplt_start");
ctx.__rel_iplt_end =
add(E::is_rel ? "__rel_iplt_end" : "__rela_iplt_end");
add(is_rela<E> ? "__rela_iplt_end" : "__rel_iplt_end");
if (ctx.arg.eh_frame_hdr)
ctx.__GNU_EH_FRAME_HDR = add("__GNU_EH_FRAME_HDR");
@@ -406,11 +428,13 @@ ObjectFile<E> *create_internal_file(Context<E> &ctx) {
ctx.etext = add("etext");
if (!get_symbol(ctx, "edata")->file)
ctx.edata = add("edata");
if (!get_symbol(ctx, "__dso_handle")->file)
ctx.__dso_handle = add("__dso_handle");
if constexpr (E::supports_tlsdesc)
if constexpr (supports_tlsdesc<E>)
ctx._TLS_MODULE_BASE_ = add("_TLS_MODULE_BASE_");
if constexpr (std::is_same_v<E, RISCV64>)
if constexpr (is_riscv<E>)
if (!ctx.arg.shared)
ctx.__global_pointer = add("__global_pointer$");
@@ -420,52 +444,30 @@ ObjectFile<E> *create_internal_file(Context<E> &ctx) {
}
for (Chunk<E> *chunk : ctx.chunks) {
if (!is_c_identifier(chunk->name))
continue;
add(save_string(ctx, "__start_" + std::string(chunk->name)));
add(save_string(ctx, "__stop_" + std::string(chunk->name)));
if (is_c_identifier(chunk->name)) {
add(save_string(ctx, "__start_" + std::string(chunk->name)));
add(save_string(ctx, "__stop_" + std::string(chunk->name)));
}
}
i64 first_defsym = obj->symbols.size();
obj.elf_syms = ctx.internal_esyms;
obj.sym_fragments.resize(ctx.internal_esyms.size());
obj.symvers.resize(ctx.internal_esyms.size() - 1);
for (i64 i = 0; i < ctx.arg.defsyms.size(); i++) {
Symbol<E> *sym = ctx.arg.defsyms[i].first;
ElfSym<E> esym = {};
esym.st_type = STT_NOTYPE;
esym.st_shndx = SHN_ABS;
esym.st_bind = STB_GLOBAL;
esym.st_visibility = STV_DEFAULT;
esyms->push_back(esym);
obj->symbols.push_back(sym);
};
obj.resolve_symbols(ctx);
obj->elf_syms = *esyms;
obj->sym_fragments.resize(obj->elf_syms.size());
i64 num_globals = obj->elf_syms.size() - obj->first_global;
obj->symvers.resize(num_globals);
obj->resolve_symbols(ctx);
for (i64 i = obj->first_global; i < first_defsym; i++)
obj->symbols[i]->shndx = -1; // dummy value to make it a relative symbol
// Make all synthetic symbols relative ones.
for (Symbol<E> *sym : obj.symbols)
sym->shndx = -1; // dummy value to make it a relative symbol
// Make defsym symbols absolute if necessary.
for (i64 i = 0; i < ctx.arg.defsyms.size(); i++) {
Symbol<E> *sym = ctx.arg.defsyms[i].first;
std::variant<Symbol<E> *, u64> val = ctx.arg.defsyms[i].second;
if (Symbol<E> **sym2 = std::get_if<Symbol<E> *>(&val))
if ((*sym2)->is_relative())
sym->shndx = -1; // dummy value to make it a relative symbol
if (std::holds_alternative<u64>(val) ||
std::get<Symbol<E> *>(val)->is_absolute())
sym->shndx = 0;
}
ctx.on_exit.push_back([=] {
delete esyms;
delete obj->symbols[0];
});
return obj;
}
template <typename E>
@@ -550,7 +552,7 @@ R"(# This is an output of the mold linker's --print-dependencies=full option.
std::unordered_set<void *> visited;
for (ElfRel<E> &r : isec->get_rels(ctx)) {
for (const ElfRel<E> &r : isec->get_rels(ctx)) {
if (r.r_type == E::R_NONE)
continue;
@@ -579,7 +581,7 @@ static std::string create_response_file(Context<E> &ctx) {
std::string buf;
std::stringstream out;
std::string cwd = std::filesystem::current_path();
std::string cwd = std::filesystem::current_path().string();
out << "-C " << cwd.substr(1) << "\n";
if (cwd != "/") {
@@ -613,7 +615,7 @@ void write_repro_file(Context<E> &ctx) {
std::unordered_set<std::string> seen;
for (std::unique_ptr<MappedFile<Context<E>>> &mf : ctx.mf_pool) {
if (!mf->parent) {
std::string path = to_abs_path(mf->name);
std::string path = to_abs_path(mf->name).string();
if (seen.insert(path).second)
tar->append(path, mf->get_contents());
}
@@ -653,15 +655,16 @@ void sort_init_fini(Context<E> &ctx) {
auto get_priority = [](InputSection<E> *isec) {
static std::regex re(R"(\.(\d+)$)", std::regex_constants::optimize);
std::string name = isec->name().begin();
std::smatch m;
if (std::regex_search(name, m, re))
std::string_view name = isec->name();
std::cmatch m;
if (std::regex_search(name.data(), name.data() + name.size(), m, re))
return std::stoi(m[1]);
return 65536;
};
for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections) {
if (osec->name == ".init_array" || osec->name == ".fini_array") {
if (osec->name == ".init_array" || osec->name == ".preinit_array" ||
osec->name == ".fini_array") {
if (ctx.arg.shuffle_sections == SHUFFLE_SECTIONS_REVERSE)
std::reverse(osec->members.begin(), osec->members.end());
@@ -741,8 +744,9 @@ void shuffle_sections(Context<E> &ctx) {
auto is_eligible = [](OutputSection<E> &osec) {
return osec.name != ".init" && osec.name != ".fini" &&
osec.name != ".init_array" && osec.name != ".fini_array" &&
osec.name != ".ctors" && osec.name != ".dtors";
osec.name != ".ctors" && osec.name != ".dtors" &&
osec.name != ".init_array" && osec.name != ".preinit_array" &&
osec.name != ".fini_array";
};
switch (ctx.arg.shuffle_sections) {
@@ -753,7 +757,7 @@ void shuffle_sections(Context<E> &ctx) {
if (ctx.arg.shuffle_sections_seed)
seed = *ctx.arg.shuffle_sections_seed;
else
seed = std::random_device()();
seed = ((u64)std::random_device()() << 32) | std::random_device()();
tbb::parallel_for_each(ctx.output_sections,
[&](std::unique_ptr<OutputSection<E>> &osec) {
@@ -806,6 +810,11 @@ void compute_section_sizes(Context<E> &ctx) {
tbb::parallel_for_each(ctx.output_sections,
[&](std::unique_ptr<OutputSection<E>> &osec) {
// This pattern will be processed in the next loop.
if constexpr (needs_thunk<E>)
if (osec->shdr.sh_flags & SHF_EXECINSTR)
return;
// Since one output section may contain millions of input sections,
// we first split input sections into groups and assign offsets to
// groups.
@@ -845,6 +854,19 @@ void compute_section_sizes(Context<E> &ctx) {
}
});
});
// On ARM32 or ARM64, we may need to create so-called "range extension
// thunks" to extend branch instructions reach, as they can jump only
// to ±16 MiB or ±128 MiB, respecitvely.
//
// In the following loop, We compute the sizes of sections while
// inserting thunks. This pass cannot be parallelized. That is,
// create_range_extension_thunks is parallelized internally, but the
// function itself is not thread-safe.
if constexpr (needs_thunk<E>)
for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections)
if (osec->shdr.sh_flags & SHF_EXECINSTR)
create_range_extension_thunks(ctx, *osec);
}
template <typename E>
@@ -902,21 +924,21 @@ void scan_rels(Context<E> &ctx) {
if (sym->flags & NEEDS_GOT)
ctx.got->add_got_symbol(ctx, sym);
if (sym->flags & NEEDS_PLT) {
if (sym->is_canonical) {
// A canonical PLT needs to be visible from DSOs.
sym->is_exported = true;
if (sym->flags & NEEDS_CPLT) {
sym->is_canonical = true;
// We can't use .plt.got for a canonical PLT because otherwise
// .plt.got and .got would refer each other, resulting in an
// infinite loop at runtime.
// A canonical PLT needs to be visible from DSOs.
sym->is_exported = true;
// We can't use .plt.got for a canonical PLT because otherwise
// .plt.got and .got would refer each other, resulting in an
// infinite loop at runtime.
ctx.plt->add_symbol(ctx, sym);
} else if (sym->flags & NEEDS_PLT) {
if (sym->flags & NEEDS_GOT)
ctx.pltgot->add_symbol(ctx, sym);
else
ctx.plt->add_symbol(ctx, sym);
} else {
if (sym->flags & NEEDS_GOT)
ctx.pltgot->add_symbol(ctx, sym);
else
ctx.plt->add_symbol(ctx, sym);
}
}
if (sym->flags & NEEDS_GOTTP)
@@ -956,10 +978,6 @@ void scan_rels(Context<E> &ctx) {
}
}
if constexpr (std::is_same_v<E, ARM32>)
if (sym->flags & NEEDS_THUMB_TO_ARM_THUNK)
ctx.thumb_to_arm->add_symbol(ctx, sym);
sym->flags = 0;
}
@@ -978,7 +996,7 @@ void create_reloc_sections(Context<E> &ctx) {
for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections) {
RelocSection<E> *r = new RelocSection<E>(ctx, *osec);
ctx.chunks.push_back(r);
ctx.output_chunks.emplace_back(r);
ctx.chunk_pool.emplace_back(r);
}
// Create a table to map input symbol indices to output symbol indices
@@ -1048,8 +1066,8 @@ void apply_version_script(Context<E> &ctx) {
}
// Otherwise, use glob pattern matchers.
VersionMatcher matcher;
VersionMatcher cpp_matcher;
MultiGlob matcher;
MultiGlob cpp_matcher;
for (VersionPattern &v : ctx.version_patterns) {
if (v.is_cpp) {
@@ -1073,9 +1091,12 @@ void apply_version_script(Context<E> &ctx) {
continue;
}
if (!cpp_matcher.empty())
if (std::optional<u16> ver = cpp_matcher.find(demangle(name)))
if (!cpp_matcher.empty()) {
if (std::optional<std::string_view> s = cpp_demangle(name))
name = *s;
if (std::optional<u16> ver = cpp_matcher.find(name))
sym->ver_idx = *ver;
}
}
});
}
@@ -1093,6 +1114,8 @@ void parse_symbol_version(Context<E> &ctx) {
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (i64 i = 0; i < file->symbols.size() - file->first_global; i++) {
// Match VERSION part of symbol foo@VERSION with version definitions.
// The symbols' VERSION parts are in file->symvers.
if (!file->symvers[i])
continue;
@@ -1118,6 +1141,16 @@ void parse_symbol_version(Context<E> &ctx) {
sym->ver_idx = it->second;
if (!is_default)
sym->ver_idx |= VERSYM_HIDDEN;
// If both symbol `foo` and `foo@VERSION` are defined, `foo@VERSION`
// hides `foo` so that all references to `foo` are resolved to a
// versioned symbol. Likewise, if `foo@VERSION` and `foo@@VERSION` are
// defined, the default one takes precedence.
Symbol<E> *sym2 = get_symbol(ctx, sym->name());
if (sym2->file == file && !file->symvers[sym2->sym_idx - file->first_global])
if (sym2->ver_idx == ctx.default_version ||
(sym2->ver_idx & ~VERSYM_HIDDEN) == (sym->ver_idx & ~VERSYM_HIDDEN))
sym2->ver_idx = VER_NDX_LOCAL;
}
});
}
@@ -1126,30 +1159,37 @@ template <typename E>
void compute_import_export(Context<E> &ctx) {
Timer t(ctx, "compute_import_export");
// Export symbols referenced by DSOs.
// If we are creating an executable, we want to export symbols referenced
// by DSOs unless they are explicitly marked as local by a version script.
if (!ctx.arg.shared) {
tbb::parallel_for_each(ctx.dsos, [&](SharedFile<E> *file) {
for (Symbol<E> *sym : file->symbols) {
if (sym->file && !sym->file->is_dso && sym->visibility != STV_HIDDEN) {
std::scoped_lock lock(sym->mu);
sym->is_exported = true;
if (sym->ver_idx != VER_NDX_LOCAL || !ctx.version_specified) {
std::scoped_lock lock(sym->mu);
sym->is_exported = true;
}
}
}
});
}
// Export symbols that are not hidden or marked as local.
// We also want to mark imported symbols as such.
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (Symbol<E> *sym : file->get_global_syms()) {
if (!sym->file || sym->visibility == STV_HIDDEN ||
sym->ver_idx == VER_NDX_LOCAL)
continue;
if (sym->file != file && sym->file->is_dso) {
// If we are using a symbol in a DSO, we need to import it at runtime.
if (sym->file != file && sym->file->is_dso && !sym->is_absolute()) {
std::scoped_lock lock(sym->mu);
sym->is_imported = true;
continue;
}
// If we are creating a DSO, all global symbols are exported by default.
if (sym->file == file) {
std::scoped_lock lock(sym->mu);
sym->is_exported = true;
@@ -1163,6 +1203,15 @@ void compute_import_export(Context<E> &ctx) {
});
}
template <typename E>
void mark_addrsig(Context<E> &ctx) {
Timer t(ctx, "mark_addrsig");
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
file->mark_addrsig(ctx);
});
}
template <typename E>
void clear_padding(Context<E> &ctx) {
Timer t(ctx, "clear_padding");
@@ -1225,34 +1274,34 @@ i64 get_section_rank(Context<E> &ctx, Chunk<E> *chunk) {
u64 type = chunk->shdr.sh_type;
u64 flags = chunk->shdr.sh_flags;
if (chunk == ctx.ehdr.get())
if (chunk == ctx.ehdr)
return 0;
if (chunk == ctx.phdr.get())
if (chunk == ctx.phdr)
return 1;
if (chunk == ctx.interp.get())
if (chunk == ctx.interp)
return 2;
if (type == SHT_NOTE && (flags & SHF_ALLOC))
return (1 << 10) + chunk->shdr.sh_addralign;
if (chunk == ctx.hash.get())
if (chunk == ctx.hash)
return (1 << 11) + 0;
if (chunk == ctx.gnu_hash.get())
if (chunk == ctx.gnu_hash)
return (1 << 11) + 1;
if (chunk == ctx.dynsym.get())
if (chunk == ctx.dynsym)
return (1 << 11) + 2;
if (chunk == ctx.dynstr.get())
if (chunk == ctx.dynstr)
return (1 << 11) + 3;
if (chunk == ctx.versym.get())
if (chunk == ctx.versym)
return (1 << 11) + 4;
if (chunk == ctx.verneed.get())
if (chunk == ctx.verneed)
return (1 << 11) + 5;
if (chunk == ctx.reldyn.get())
if (chunk == ctx.reldyn)
return (1 << 11) + 6;
if (chunk == ctx.relplt.get())
if (chunk == ctx.relplt)
return (1 << 11) + 7;
if (chunk == ctx.shdr.get())
if (chunk == ctx.shdr)
return 1 << 30;
if (!(flags & SHF_ALLOC))
return (1 << 30) - 1;
@@ -1283,22 +1332,26 @@ i64 do_set_osec_offsets(Context<E> &ctx) {
// Assign virtual addresses
u64 addr = ctx.arg.image_base;
for (i64 i = 0; i < chunks.size(); i++) {
if (!(chunks[i]->shdr.sh_flags & SHF_ALLOC))
for (Chunk<E> *chunk : chunks) {
if (!(chunk->shdr.sh_flags & SHF_ALLOC))
continue;
if (auto it = ctx.arg.section_start.find(chunks[i]->name);
it != ctx.arg.section_start.end())
bool addr_specified = false;
if (auto it = ctx.arg.section_start.find(chunk->name);
it != ctx.arg.section_start.end()) {
addr = it->second;
addr_specified = true;
}
if (is_tbss(chunks[i])) {
chunks[i]->shdr.sh_addr = addr;
if (is_tbss(chunk)) {
chunk->shdr.sh_addr = addr;
continue;
}
addr = align_to(addr, alignment(chunks[i]));
chunks[i]->shdr.sh_addr = addr;
addr += chunks[i]->shdr.sh_size;
if (!addr_specified)
addr = align_to(addr, alignment(chunk));
chunk->shdr.sh_addr = addr;
addr += chunk->shdr.sh_size;
}
// Fix tbss virtual addresses. tbss sections are laid out as if they
@@ -1329,26 +1382,39 @@ i64 do_set_osec_offsets(Context<E> &ctx) {
while (i < chunks.size() && (chunks[i]->shdr.sh_flags & SHF_ALLOC)) {
Chunk<E> &first = *chunks[i];
assert(first.shdr.sh_type != SHT_NOBITS);
fileoff = align_to(fileoff, alignment(&first));
u64 end = fileoff;
while (i < chunks.size() && (chunks[i]->shdr.sh_flags & SHF_ALLOC) &&
chunks[i]->shdr.sh_type != SHT_NOBITS) {
// The addresses may not increase monotonically if a user uses
// --start-sections.
// Assign ALLOC sections contiguous file offsets as long as they
// are contiguous in memory.
for (;;) {
chunks[i]->shdr.sh_offset =
fileoff + chunks[i]->shdr.sh_addr - first.shdr.sh_addr;
i++;
if (i >= chunks.size() ||
!(chunks[i]->shdr.sh_flags & SHF_ALLOC) ||
chunks[i]->shdr.sh_type == SHT_NOBITS)
break;
// If --start-section is given, addresses may not increase
// monotonically.
if (chunks[i]->shdr.sh_addr < first.shdr.sh_addr)
break;
chunks[i]->shdr.sh_offset =
fileoff + chunks[i]->shdr.sh_addr - first.shdr.sh_addr;
end = chunks[i]->shdr.sh_offset + chunks[i]->shdr.sh_size;
i++;
i64 gap_size = chunks[i]->shdr.sh_addr - chunks[i - 1]->shdr.sh_addr -
chunks[i - 1]->shdr.sh_size;
// If --start-section is given, there may be a large gap between
// sections. We don't want to allocate a disk space for a gap if
// exists.
if (gap_size >= ctx.page_size)
break;
}
fileoff = end;
fileoff = chunks[i - 1]->shdr.sh_offset + chunks[i - 1]->shdr.sh_size;
while (i < chunks.size() && (chunks[i]->shdr.sh_flags & SHF_ALLOC) &&
while (i < chunks.size() &&
(chunks[i]->shdr.sh_flags & SHF_ALLOC) &&
chunks[i]->shdr.sh_type == SHT_NOBITS)
i++;
}
@@ -1416,7 +1482,6 @@ void fix_synthetic_symbols(Context<E> &ctx) {
if (Chunk<E> *chunk = find(".bss"))
start(ctx.__bss_start, chunk);
// __ehdr_start and __executable_start
if (ctx.ehdr) {
for (Chunk<E> *chunk : ctx.chunks) {
if (chunk->shndx == 1) {
@@ -1424,6 +1489,11 @@ void fix_synthetic_symbols(Context<E> &ctx) {
ctx.__ehdr_start->value = ctx.ehdr->shdr.sh_addr;
ctx.__executable_start->shndx = -1;
ctx.__executable_start->value = ctx.ehdr->shdr.sh_addr;
if (ctx.__dso_handle) {
ctx.__dso_handle->shndx = -1;
ctx.__dso_handle->value = ctx.ehdr->shdr.sh_addr;
}
break;
}
}
@@ -1454,6 +1524,10 @@ void fix_synthetic_symbols(Context<E> &ctx) {
start(ctx.__init_array_start, chunk);
stop(ctx.__init_array_end, chunk);
break;
case SHT_PREINIT_ARRAY:
start(ctx.__preinit_array_start, chunk);
stop(ctx.__preinit_array_end, chunk);
break;
case SHT_FINI_ARRAY:
start(ctx.__fini_array_start, chunk);
stop(ctx.__fini_array_end, chunk);
@@ -1463,7 +1537,7 @@ void fix_synthetic_symbols(Context<E> &ctx) {
// _end, _etext, _edata and the like
for (Chunk<E> *chunk : ctx.chunks) {
if (chunk->is_header())
if (chunk->kind() == HEADER)
continue;
if (chunk->shdr.sh_flags & SHF_ALLOC) {
@@ -1493,7 +1567,7 @@ void fix_synthetic_symbols(Context<E> &ctx) {
start(ctx._GLOBAL_OFFSET_TABLE_, ctx.got);
// _TLS_MODULE_BASE_
if constexpr (E::supports_tlsdesc) {
if (ctx._TLS_MODULE_BASE_) {
// _TLS_MODULE_BASE_ is used for Local Dynamic model for TLSDESC.
// I believe GCC and Clang don't create a reference to it, but Intel
// compiler seems to be using this symbol.
@@ -1504,10 +1578,7 @@ void fix_synthetic_symbols(Context<E> &ctx) {
// DTPOFF's relaxed value.
u64 addr;
if constexpr (std::is_same_v<E, X86_64> || std::is_same_v<E, I386>) {
if (ctx.arg.relax && !ctx.arg.shared)
addr = ctx.tls_end;
else
addr = ctx.tls_begin;
addr = (ctx.arg.relax && !ctx.arg.shared) ? ctx.tls_end : ctx.tls_begin;
} else {
addr = ctx.tls_begin;
}
@@ -1520,18 +1591,16 @@ void fix_synthetic_symbols(Context<E> &ctx) {
start(ctx.__GNU_EH_FRAME_HDR, ctx.eh_frame_hdr);
// RISC-V's __global_pointer$
if constexpr (std::is_same_v<E, RISCV64>) {
if (!ctx.arg.shared) {
if (Chunk<E> *chunk = find(".sdata"))
ctx.__global_pointer->shndx = -chunk->shndx;
else
ctx.__global_pointer->shndx = -1;
ctx.__global_pointer->value = 0x800;
}
if (ctx.__global_pointer) {
if (Chunk<E> *chunk = find(".sdata"))
ctx.__global_pointer->shndx = -chunk->shndx;
else
ctx.__global_pointer->shndx = -1;
ctx.__global_pointer->value = 0x800;
}
// ARM32's __exidx_{start,end}
if constexpr (std::is_same_v<E, ARM32>) {
if (ctx.__exidx_start) {
if (Chunk<E> *chunk = find(".ARM.exidx")) {
start(ctx.__exidx_start, chunk);
stop(ctx.__exidx_end, chunk);
@@ -1595,7 +1664,7 @@ i64 compress_debug_sections(Context<E> &ctx) {
comp = new GnuCompressedSection<E>(ctx, chunk);
assert(comp);
ctx.output_chunks.emplace_back(comp);
ctx.chunk_pool.emplace_back(comp);
ctx.chunks[i] = comp;
});
@@ -1638,6 +1707,7 @@ void write_dependency_file(Context<E> &ctx) {
}
#define INSTANTIATE(E) \
template void create_internal_file(Context<E> &); \
template void apply_exclude_libs(Context<E> &); \
template void create_synthetic_sections(Context<E> &); \
template void resolve_symbols(Context<E> &); \
@@ -1646,7 +1716,7 @@ void write_dependency_file(Context<E> &ctx) {
template void convert_common_symbols(Context<E> &); \
template void compute_merged_section_sizes(Context<E> &); \
template void bin_sections(Context<E> &); \
template ObjectFile<E> *create_internal_file(Context<E> &); \
template void add_synthetic_symbols(Context<E> &); \
template void check_cet_errors(Context<E> &); \
template void print_dependencies(Context<E> &); \
template void print_dependencies_full(Context<E> &); \
@@ -1665,6 +1735,7 @@ void write_dependency_file(Context<E> &ctx) {
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 clear_padding(Context<E> &); \
template i64 get_section_rank(Context<E> &, Chunk<E> *); \
template i64 set_osec_offsets(Context<E> &); \
@@ -1672,10 +1743,6 @@ void write_dependency_file(Context<E> &ctx) {
template i64 compress_debug_sections(Context<E> &); \
template void write_dependency_file(Context<E> &);
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
INSTANTIATE_ALL;
} // namespace mold::elf
+22 -23
View File
@@ -28,6 +28,7 @@
#include "mold.h"
#include "../archive-file.h"
#include "../output-file.h"
#include <unordered_map>
#include <unordered_set>
@@ -70,7 +71,7 @@ public:
this->name = ".symtab";
this->out_shdr.sh_type = SHT_SYMTAB;
this->out_shdr.sh_entsize = sizeof(ElfSym<E>);
this->out_shdr.sh_addralign = E::word_size;
this->out_shdr.sh_addralign = sizeof(Word<E>);
}
void add_local_symbol(Context<E> &ctx, RObjectFile<E> &file, i64 idx);
@@ -297,8 +298,8 @@ template <typename E>
void ROutputEhdr<E>::write_to(Context<E> &ctx) {
ElfEhdr<E> &hdr = *(ElfEhdr<E> *)(ctx.buf + this->out_shdr.sh_offset);
memcpy(&hdr.e_ident, "\177ELF", 4);
hdr.e_ident[EI_CLASS] = (E::word_size == 8) ? ELFCLASS64 : ELFCLASS32;
hdr.e_ident[EI_DATA] = E::is_le ? ELFDATA2LSB : ELFDATA2MSB;
hdr.e_ident[EI_CLASS] = (sizeof(Word<E>) == 8) ? ELFCLASS64 : ELFCLASS32;
hdr.e_ident[EI_DATA] = ELFDATA2LSB;
hdr.e_ident[EI_VERSION] = EV_CURRENT;
hdr.e_type = ET_REL;
hdr.e_machine = E::e_machine;
@@ -413,36 +414,38 @@ std::span<T> RObjectFile<E>::get_data(Context<E> &ctx, const ElfShdr<E> &shdr) {
template <typename E>
static std::vector<std::unique_ptr<RObjectFile<E>>>
open_files(Context<E> &ctx, std::span<std::string_view> args) {
open_files(Context<E> &ctx, std::span<std::string> args) {
std::vector<std::unique_ptr<RObjectFile<E>>> files;
bool whole_archive = false;
while (!args.empty()) {
if (read_flag(args, "whole-archive")) {
const std::string &arg = args[0];
args = args.subspan(1);
if (arg == "--whole-archive") {
whole_archive = true;
continue;
}
if (read_flag(args, "no-whole-archive")) {
if (arg == "--no-whole-archive") {
whole_archive = false;
continue;
}
std::string_view arg;
if (read_arg(ctx, args, arg, "version-script") ||
read_arg(ctx, args, arg, "dynamic-list"))
if (arg.starts_with("--version-script=") ||
arg.starts_with("--dynamic-list=") ||
arg.starts_with("--export-dynamic-symbol=") ||
arg.starts_with("--export-dynamic-symbol-list="))
continue;
MappedFile<Context<E>> *mf = nullptr;
if (read_arg(ctx, args, arg, "l")) {
mf = find_library(ctx, std::string(arg));
if (arg == "-l") {
mf = find_library(ctx, arg.substr(2));
} else {
if (arg.starts_with('-'))
continue;
arg = args[0];
args = args.subspan(1);
mf = MappedFile<Context<E>>::must_open(ctx, std::string(arg));
mf = MappedFile<Context<E>>::must_open(ctx, arg);
}
switch (get_file_type(mf)) {
@@ -482,7 +485,7 @@ static bool contains(std::unordered_set<std::string_view> &a,
}
template <typename E>
void combine_objects(Context<E> &ctx, std::span<std::string_view> file_args) {
void combine_objects(Context<E> &ctx, std::span<std::string> file_args) {
// Read object files
std::vector<std::unique_ptr<RObjectFile<E>>> files = open_files(ctx, file_args);
@@ -574,8 +577,8 @@ void combine_objects(Context<E> &ctx, std::span<std::string_view> file_args) {
// Open an output file
i64 filesize = assign_offsets(ctx);
std::unique_ptr<OutputFile<E>> out =
OutputFile<E>::open(ctx, ctx.arg.output, filesize, 0666);
std::unique_ptr<OutputFile<Context<E>>> out =
OutputFile<Context<E>>::open(ctx, ctx.arg.output, filesize, 0666);
memset(out->buf, 0, filesize);
ctx.buf = out->buf;
@@ -586,12 +589,8 @@ void combine_objects(Context<E> &ctx, std::span<std::string_view> file_args) {
}
#define INSTANTIATE(E) \
template void combine_objects(Context<E> &, std::span<std::string_view>);
template void combine_objects(Context<E> &, std::span<std::string>);
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
INSTANTIATE_ALL;
} // namespace mold::elf
+3 -206
View File
@@ -1,19 +1,13 @@
#include "mold.h"
#include "../sha.h"
#include <filesystem>
#include <signal.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <unistd.h>
#define DAEMON_TIMEOUT 30
namespace mold::elf {
// Exiting from a program with large memory usage is slow --
@@ -60,198 +54,8 @@ std::function<void()> fork_child() {
};
}
static std::string base64(u8 *data, u64 size) {
static const char chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_";
std::ostringstream out;
auto encode = [&](u32 x) {
out << chars[x & 0b111111]
<< chars[(x >> 6) & 0b111111]
<< chars[(x >> 12) & 0b111111]
<< chars[(x >> 18) & 0b111111];
};
i64 i = 0;
for (; i < size - 3; i += 3)
encode((data[i + 2] << 16) | (data[i + 1] << 8) | data[i]);
if (i == size - 1)
encode(data[i]);
else if (i == size - 2)
encode((data[i + 1] << 8) | data[i]);
return out.str();
}
static std::string compute_sha256(std::span<std::string_view> argv) {
SHA256_CTX sha;
SHA256_Init(&sha);
for (std::string_view arg : argv) {
if (arg != "-preload" && arg != "--preload") {
SHA256_Update(&sha, arg.data(), arg.size());
char buf[] = {0};
SHA256_Update(&sha, buf, 1);
}
}
u8 digest[SHA256_SIZE];
SHA256_Final(digest, &sha);
return base64(digest, SHA256_SIZE);
}
template <typename E>
static void send_fd(Context<E> &ctx, i64 conn, i64 fd) {
struct iovec iov;
char dummy = '1';
iov.iov_base = &dummy;
iov.iov_len = 1;
struct msghdr msg = {};
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
char buf[CMSG_SPACE(sizeof(int))];
msg.msg_control = buf;
msg.msg_controllen = CMSG_LEN(sizeof(int));
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
*(int *)CMSG_DATA(cmsg) = fd;
if (sendmsg(conn, &msg, 0) == -1)
Fatal(ctx) << "sendmsg failed: " << errno_string();
}
template <typename E>
static i64 recv_fd(Context<E> &ctx, i64 conn) {
struct iovec iov;
char buf[1];
iov.iov_base = buf;
iov.iov_len = sizeof(buf);
struct msghdr msg = {};
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
char cmsgbuf[CMSG_SPACE(sizeof(int))];
msg.msg_control = (caddr_t)cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
i64 len = recvmsg(conn, &msg, 0);
if (len <= 0)
Fatal(ctx) << "recvmsg failed: " << errno_string();
struct cmsghdr *cmsg;
cmsg = CMSG_FIRSTHDR(&msg);
return *(int *)CMSG_DATA(cmsg);
}
template <typename E>
void try_resume_daemon(Context<E> &ctx) {
i64 conn = socket(AF_UNIX, SOCK_STREAM, 0);
if (conn == -1)
Fatal(ctx) << "socket failed: " << errno_string();
std::string path = "/tmp/mold-" + compute_sha256(ctx.cmdline_args);
struct sockaddr_un name = {};
name.sun_family = AF_UNIX;
memcpy(name.sun_path, path.data(), path.size());
if (connect(conn, (struct sockaddr *)&name, sizeof(name)) != 0) {
close(conn);
return;
}
send_fd(ctx, conn, STDOUT_FILENO);
send_fd(ctx, conn, STDERR_FILENO);
char buf[1];
i64 r = read(conn, buf, 1);
close(conn);
if (r == 1)
exit(0);
}
template <typename E>
void daemonize(Context<E> &ctx, std::function<void()> *wait_for_client,
std::function<void()> *on_complete) {
if (daemon(1, 0) == -1)
Fatal(ctx) << "daemon failed: " << errno_string();
i64 sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock == -1)
Fatal(ctx) << "socket failed: " << errno_string();
socket_tmpfile =
strdup(("/tmp/mold-" + compute_sha256(ctx.cmdline_args)).c_str());
struct sockaddr_un name = {};
name.sun_family = AF_UNIX;
strcpy(name.sun_path, socket_tmpfile);
u32 orig_mask = umask(0177);
if (bind(sock, (struct sockaddr *)&name, sizeof(name)) == -1) {
if (errno != EADDRINUSE)
Fatal(ctx) << "bind failed: " << errno_string();
unlink(socket_tmpfile);
if (bind(sock, (struct sockaddr *)&name, sizeof(name)) == -1)
Fatal(ctx) << "bind failed: " << errno_string();
}
umask(orig_mask);
if (listen(sock, 0) == -1)
Fatal(ctx) << "listen failed: " << errno_string();
static i64 conn = -1;
*wait_for_client = [=, &ctx] {
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(sock, &rfds);
struct timeval tv;
tv.tv_sec = DAEMON_TIMEOUT;
tv.tv_usec = 0;
i64 res = select(sock + 1, &rfds, NULL, NULL, &tv);
if (res == -1)
Fatal(ctx) << "select failed: " << errno_string();
if (res == 0) {
std::cout << "timeout\n";
exit(0);
}
conn = accept(sock, NULL, NULL);
if (conn == -1)
Fatal(ctx) << "accept failed: " << errno_string();
unlink(socket_tmpfile);
dup2(recv_fd(ctx, conn), STDOUT_FILENO);
dup2(recv_fd(ctx, conn), STDERR_FILENO);
};
*on_complete = [=] {
char buf[] = {1};
int n = write(conn, buf, 1);
assert(n == 1);
};
}
static std::string get_self_path() {
return std::filesystem::read_symlink("/proc/self/exe");
}
template <typename E>
std::string find_dso(Context<E> &ctx, std::filesystem::path self) {
static std::string find_dso(Context<E> &ctx, std::filesystem::path self) {
// Look for mold-wrapper.so from the same directory as the executable is.
std::filesystem::path path = self.parent_path() / "mold-wrapper.so";
std::error_code ec;
@@ -283,7 +87,7 @@ void process_run_subcommand(Context<E> &ctx, int argc, char **argv) {
Fatal(ctx) << "-run: argument missing";
// Get the mold-wrapper.so path
std::string self = get_self_path();
std::string self = std::filesystem::read_symlink("/proc/self/exe");
std::string dso_path = find_dso(ctx, self);
// Set environment variables
@@ -307,15 +111,8 @@ void process_run_subcommand(Context<E> &ctx, int argc, char **argv) {
}
#define INSTANTIATE(E) \
template void try_resume_daemon(Context<E> &); \
template void daemonize(Context<E> &, std::function<void()> *, \
std::function<void()> *); \
template void process_run_subcommand(Context<E> &, int, char **)
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
INSTANTIATE_ALL;
} // namespace mold::elf
+176
View File
@@ -0,0 +1,176 @@
// RISC instructions are usually up to 4 bytes long, so the immediates
// of their branch instructions are naturally smaller than 32 bits.
// This is contrary to x86-64 on which branch instructions take 4
// bytes immediates and can jump to anywhere within PC ± 2 GiB.
//
// In fact, ARM32's branch instructions can jump only within ±16 MiB
// and ARM64's ±128 MiB. If a branch target is further than that, we
// need to let it branch to a linker-synthesized code sequence that
// construct a full 32 bit address in a register and jump there. That
// linker-synthesized code is called "thunk".
//
// The function in this file creates thunks.
#include "mold.h"
#include <tbb/parallel_for.h>
#include <tbb/parallel_for_each.h>
namespace mold::elf {
template <typename E>
static void reset_thunk(RangeExtensionThunk<E> &thunk) {
for (Symbol<E> *sym : thunk.symbols) {
sym->extra.thunk_idx = -1;
sym->extra.thunk_sym_idx = -1;
sym->flags = 0;
}
}
template <typename E>
void create_range_extension_thunks(Context<E> &ctx, OutputSection<E> &osec) {
std::span<InputSection<E> *> members = osec.members;
if (members.empty())
return;
members[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)members.size(), [&](i64 i) {
members[i]->offset = -1;
});
// We create thunks from the beginning of the section to the end.
// We manage progress using four offsets which increase monotonically.
// The locations they point to are always A <= B <= C <= D.
i64 a = 0;
i64 b = 0;
i64 c = 0;
i64 d = 0;
i64 offset = 0;
while (b < members.size()) {
// Move D foward as far as we can jump from B to D.
while (d < members.size() &&
offset - members[b]->offset < E::thunk_max_distance) {
offset = align_to(offset, 1 << members[d]->p2align);
members[d]->offset = offset;
offset += members[d]->sh_size;
d++;
}
// Move C forward so that C is apart from B by GROUP_SIZE.
while (c < members.size() &&
members[c]->offset - members[b]->offset < E::thunk_group_size)
c++;
// Move A forward so that A is reachable from C.
if (c > 0) {
i64 c_end = members[c - 1]->offset + members[c - 1]->sh_size;
while (a < osec.thunks.size() &&
osec.thunks[a]->offset < c_end - E::thunk_max_distance)
reset_thunk(*osec.thunks[a++]);
}
// Create a thunk for input sections between B and C and place it at D.
osec.thunks.emplace_back(new RangeExtensionThunk<E>{osec});
RangeExtensionThunk<E> &thunk = *osec.thunks.back();
thunk.thunk_idx = osec.thunks.size() - 1;
offset = align_to(offset, thunk.alignment);
thunk.offset = offset;
std::mutex mu;
// Scan relocations between B and C to collect symbols that need thunks.
tbb::parallel_for_each(members.begin() + b, members.begin() + c,
[&](InputSection<E> *isec) {
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))
continue;
// Skip if the symbol is undefined. apply_reloc() will report an error.
Symbol<E> &sym = *isec->file.symbols[rel.r_sym];
if (!sym.file)
continue;
// Skip if the destination is within reach.
if (is_reachable(ctx, sym, *isec, rel))
continue;
// If the symbol is already in another thunk, reuse it.
if (sym.extra.thunk_idx != -1) {
range_extn[i].thunk_idx = sym.extra.thunk_idx;
range_extn[i].sym_idx = sym.extra.thunk_sym_idx;
continue;
}
// Otherwise, add the symbol to this thunk if it's not added already.
range_extn[i].thunk_idx = thunk.thunk_idx;
range_extn[i].sym_idx = -1;
if (sym.flags.exchange(-1) == 0) {
std::scoped_lock lock(mu);
thunk.symbols.push_back(&sym);
}
}
});
// Now that we know the number of symbols in the thunk, we can compute
// its size.
offset += thunk.size();
// Sort symbols added to the thunk to make the output deterministic.
sort(thunk.symbols, [](Symbol<E> *a, Symbol<E> *b) {
return std::tuple{a->file->priority, a->sym_idx} <
std::tuple{b->file->priority, b->sym_idx};
});
// Assign offsets within the thunk to the symbols.
for (i64 i = 0; Symbol<E> *sym : thunk.symbols) {
sym->extra.thunk_idx = thunk.thunk_idx;
sym->extra.thunk_sym_idx = i++;
}
// Scan relocations again to fix symbol offsets in the last thunk.
tbb::parallel_for_each(members.begin() + b, members.begin() + c,
[&](InputSection<E> *isec) {
std::span<const ElfRel<E>> rels = isec->get_rels(ctx);
std::vector<RangeExtensionRef> &range_extn = isec->extra.range_extn;
for (i64 i = 0; i < rels.size(); i++) {
if (range_extn[i].thunk_idx == thunk.thunk_idx) {
Symbol<E> &sym = *isec->file.symbols[rels[i].r_sym];
range_extn[i].sym_idx = sym.extra.thunk_sym_idx;
}
}
});
// Move B forward to point to the begining of the next group.
b = c;
}
while (a < osec.thunks.size())
reset_thunk(*osec.thunks[a++]);
osec.shdr.sh_size = offset;
for (InputSection<E> *isec : members)
osec.shdr.sh_addralign =
std::max<u32>(osec.shdr.sh_addralign, 1 << isec->p2align);
}
template
void create_range_extension_thunks(Context<ARM64> &, OutputSection<ARM64> &);
template
void create_range_extension_thunks(Context<ARM32> &, OutputSection<ARM32> &);
} // namespace mold::elf
+2 -2
View File
@@ -7,7 +7,7 @@ namespace mold {
std::string get_realpath(std::string_view path) {
std::error_code ec;
std::string ret = std::filesystem::canonical(path, ec);
std::string ret = std::filesystem::canonical(path, ec).string();
return ec ? std::string(path) : ret;
}
@@ -15,7 +15,7 @@ std::string get_realpath(std::string_view path) {
// The transformation is done purely by lexical processing.
// This function does not access file system.
std::string path_clean(std::string_view path) {
return filepath(path).lexically_normal();
return filepath(path).lexically_normal().string();
}
std::filesystem::path to_abs_path(std::filesystem::path path) {
+11 -3
View File
@@ -11,7 +11,9 @@ enum class FileType {
ELF_OBJ,
ELF_DSO,
MACH_OBJ,
MACH_EXE,
MACH_DYLIB,
MACH_BUNDLE,
MACH_UNIVERSAL,
AR,
THIN_AR,
@@ -41,7 +43,7 @@ inline bool is_gcc_lto_obj(MappedFile<C> *mf) {
continue;
std::span<ElfSym<E>> elf_syms{(ElfSym<E> *)(data + sec.sh_offset),
sec.sh_size / sizeof(ElfSym<E>)};
(size_t)sec.sh_size / sizeof(ElfSym<E>)};
auto skip = [](u8 type) {
return type == STT_NOTYPE || type == STT_FILE || type == STT_SECTION;
@@ -73,7 +75,7 @@ FileType get_file_type(MappedFile<C> *mf) {
return FileType::EMPTY;
if (data.starts_with("\177ELF")) {
switch (*(u16 *)(data.data() + 16)) {
switch (*(ul16 *)(data.data() + 16)) {
case 1: {
// ET_REL
elf::Elf32Ehdr &ehdr = *(elf::Elf32Ehdr *)data.data();
@@ -95,11 +97,15 @@ FileType get_file_type(MappedFile<C> *mf) {
}
if (data.starts_with("\xcf\xfa\xed\xfe")) {
switch (*(u32 *)(data.data() + 12)) {
switch (*(ul32 *)(data.data() + 12)) {
case 1: // MH_OBJECT
return FileType::MACH_OBJ;
case 2: // MH_EXECUTE
return FileType::MACH_EXE;
case 6: // MH_DYLIB
return FileType::MACH_DYLIB;
case 8: // MH_BUNDLE
return FileType::MACH_BUNDLE;
}
return FileType::UNKNOWN;
}
@@ -127,8 +133,10 @@ inline std::string filetype_to_string(FileType type) {
case FileType::EMPTY: return "EMPTY";
case FileType::ELF_OBJ: return "ELF_OBJ";
case FileType::ELF_DSO: return "ELF_DSO";
case FileType::MACH_EXE: return "MACH_EXE";
case FileType::MACH_OBJ: return "MACH_OBJ";
case FileType::MACH_DYLIB: return "MACH_DYLIB";
case FileType::MACH_BUNDLE: return "MACH_BUNDLE";
case FileType::MACH_UNIVERSAL: return "MACH_UNIVERSAL";
case FileType::AR: return "AR";
case FileType::THIN_AR: return "THIN_AR";
+11 -13
View File
@@ -2,9 +2,9 @@
#include <cstring>
namespace mold::elf {
namespace mold {
std::optional<GlobPattern> GlobPattern::compile(std::string_view pat) {
std::optional<Glob> Glob::compile(std::string_view pat) {
std::vector<Element> vec;
while (!pat.empty()) {
@@ -20,8 +20,7 @@ std::optional<GlobPattern> GlobPattern::compile(std::string_view pat) {
// [a-czg-i]: a, b, c, z, g, h, or i
// [^a-z]: Any character except lowercase letters
vec.push_back({BRACKET});
std::vector<bool> &bitmap = vec.back().bitmap;
bitmap.resize(256);
std::bitset<256> &bitset = vec.back().bitset;
bool negate = false;
if (!pat.empty() && pat[0] == '^') {
@@ -60,9 +59,9 @@ std::optional<GlobPattern> GlobPattern::compile(std::string_view pat) {
return {};
for (i64 i = start; i <= end; i++)
bitmap[i] = true;
bitset[i] = true;
} else {
bitmap[(u8)pat[0]] = true;
bitset[(u8)pat[0]] = true;
pat = pat.substr(1);
}
}
@@ -71,8 +70,7 @@ std::optional<GlobPattern> GlobPattern::compile(std::string_view pat) {
return {};
if (negate)
for (i64 i = 0; i < 256; i++)
bitmap[i] = !bitmap[i];
bitset.flip();
break;
}
case '?':
@@ -89,14 +87,14 @@ std::optional<GlobPattern> GlobPattern::compile(std::string_view pat) {
}
}
return {GlobPattern{std::move(vec)}};
return {Glob{std::move(vec)}};
}
bool GlobPattern::match(std::string_view str) {
bool Glob::match(std::string_view str) {
return do_match(str, elements);
}
bool GlobPattern::do_match(std::string_view str, std::span<Element> elements) {
bool Glob::do_match(std::string_view str, std::span<Element> elements) {
while (!elements.empty()) {
Element &e = elements[0];
elements = elements.subspan(1);
@@ -138,7 +136,7 @@ bool GlobPattern::do_match(std::string_view str, std::span<Element> elements) {
str = str.substr(1);
break;
case BRACKET:
if (str.empty() || !e.bitmap[str[0]])
if (str.empty() || !e.bitset[str[0]])
return false;
str = str.substr(1);
break;
@@ -148,4 +146,4 @@ bool GlobPattern::do_match(std::string_view str, std::span<Element> elements) {
return str.empty();
}
} // namespace mold::elf
} // namespace mold
+40
View File
@@ -0,0 +1,40 @@
#!/bin/bash -x
set -e
source /etc/os-release
# The first line for each distro installs a build dependency.
# The second line installs extra packages for `make test`.
#
# Feel free to send me a PR if you find a missing OS.
case "$ID-$VERSION_ID" in
ubuntu-20.* | pop-20.*)
apt-get install -y cmake libssl-dev zlib1g-dev gcc g++ g++-10
apt-get install -y file bsdmainutils
;;
ubuntu-22.* | pop-22.* | debian-11 | raspbian-*)
apt-get install -y cmake libssl-dev zlib1g-dev gcc g++
apt-get install -y file bsdmainutils
;;
fedora-*)
dnf install -y gcc-g++ cmake openssl-devel zlib-devel
dnf install -y glibc-static file libstdc++-static diffutils
;;
opensuse-leap-*)
zypper install -y make cmake zlib-devel libopenssl-devel 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 glibc-devel-static tar diffutils util-linux
;;
gentoo-*)
emerge dev-util/cmake sys-libs/zlib
;;
arch-*)
pacman -S --needed --noconfirm base-devel zlib openssl cmake util-linux
;;
*)
echo "Error: don't know anything about build dependencies on $ID-$VERSION_ID"
exit 1
esac
+181
View File
@@ -0,0 +1,181 @@
// This file defines integral types for file input/output. You should
// use these types instead of the plain integers (such as u32 or i32)
// when reading from/writing to an mmap'ed file area.
//
// Here is why you need these types. In C/C++, all data accesses must
// be aligned. That is, if you read an N byte value from memory, the
// address of that value must be a multiple of N. For example, reading
// an u32 value from address 16 is legal, while reading from 18 is not
// (since 18 is not a multiple of 4.) An unaligned access yields an
// undefined behavior.
//
// All data members of the ELF data structures are naturally aligned,
// so it may look like we don't need the integral types defined in
// this file to access them. But there's a catch; if an object file is
// in an archive file (a .a file), the beginning of the file is
// guaranteed to be aligned only to a 2 bytes boundary, because ar
// aligns each member only to a 2 byte boundary. Therefore, any data
// members larger than 2 bytes may be unaligned in an mmap'ed memory,
// and thus you always need to use the integral types in this file to
// access it.
#pragma once
#include <cstdint>
#include <cstring>
#ifdef __BIG_ENDIAN__
#error "mold does not support big-endian hosts"
#endif
namespace mold {
template <typename T, size_t SIZE = sizeof(T)>
class LittleEndian {
public:
LittleEndian() { *this = 0; }
LittleEndian(T x) { *this = x; }
operator T() const {
T x = 0;
memcpy(&x, val, SIZE);
return x;
}
LittleEndian &operator=(T x) {
memcpy(&val, &x, SIZE);
return *this;
}
LittleEndian &operator++() {
return *this = *this + 1;
}
LittleEndian operator++(int) {
T ret = *this;
*this = *this + 1;
return ret;
}
LittleEndian &operator--() {
return *this = *this - 1;
}
LittleEndian operator--(int) {
T ret = *this;
*this = *this - 1;
return ret;
}
LittleEndian &operator+=(T x) {
return *this = *this + x;
}
LittleEndian &operator-=(T x) {
return *this = *this - x;
}
LittleEndian &operator&=(T x) {
return *this = *this & x;
}
LittleEndian &operator|=(T x) {
return *this = *this | x;
}
private:
uint8_t val[SIZE];
};
using il16 = LittleEndian<int16_t>;
using il32 = LittleEndian<int32_t>;
using il64 = LittleEndian<int64_t>;
using ul16 = LittleEndian<uint16_t>;
using ul24 = LittleEndian<uint32_t, 3>;
using ul32 = LittleEndian<uint32_t>;
using ul64 = LittleEndian<uint64_t>;
template <typename T>
class BigEndian {
public:
BigEndian() = delete;
operator T() const {
T x;
memcpy(&x, val, sizeof(T));
return bswap(x);
}
BigEndian &operator=(T x) {
x = bswap(x);
memcpy(&val, &x, sizeof(T));
return *this;
}
BigEndian &operator++() {
return *this = *this + 1;
}
BigEndian operator++(int) {
T ret = *this;
*this = *this + 1;
return ret;
}
BigEndian &operator--() {
return *this = *this - 1;
}
BigEndian operator--(int) {
T ret = *this;
*this = *this - 1;
return ret;
}
BigEndian &operator+=(T x) {
return *this = *this + x;
}
BigEndian &operator&=(T x) {
return *this = *this & x;
}
BigEndian &operator|=(T x) {
return *this = *this | x;
}
private:
uint8_t val[sizeof(T)];
static T bswap(T x) {
// Compiler is usually smart enough to compile the following code into
// a single bswap instruction. See https://godbolt.org/z/7nvaM7qab
if constexpr (sizeof(T) == 2) {
return ((x & 0xff00) >> 8) |
((x & 0x00ff) << 8);
} else if constexpr (sizeof(T) == 4) {
return ((x & 0xff000000) >> 24) |
((x & 0x00ff0000) >> 8) |
((x & 0x0000ff00) << 8) |
((x & 0x000000ff) << 24);
} else {
return ((x & 0xff000000'00000000) >> 56) |
((x & 0x00ff0000'00000000) >> 40) |
((x & 0x0000ff00'00000000) >> 24) |
((x & 0x000000ff'00000000) >> 8) |
((x & 0x00000000'ff000000) << 8) |
((x & 0x00000000'00ff0000) << 24) |
((x & 0x00000000'0000ff00) << 40) |
((x & 0x00000000'000000ff) << 56);
}
}
};
using ib16 = BigEndian<int16_t>;
using ib32 = BigEndian<int32_t>;
using ib64 = BigEndian<int64_t>;
using ub16 = BigEndian<uint16_t>;
using ub32 = BigEndian<uint32_t>;
using ub64 = BigEndian<uint64_t>;
} // namespace mold
+556 -105
View File
@@ -1,23 +1,24 @@
#include "mold.h"
#include <tbb/parallel_for.h>
#include <tbb/parallel_for_each.h>
namespace mold::macho {
// Returns [hi:lo] bits of val.
static u64 bits(u64 val, u64 hi, u64 lo) {
return (val >> lo) & (((u64)1 << (hi - lo + 1)) - 1);
}
using E = ARM64;
static u64 page(u64 val) {
return val & 0xffff'ffff'ffff'f000;
}
static u64 encode_page(u64 val) {
static u64 page_offset(u64 hi, u64 lo) {
u64 val = page(hi) - page(lo);
return (bits(val, 13, 12) << 29) | (bits(val, 32, 14) << 5);
}
template <>
void StubsSection<ARM64>::copy_buf(Context<ARM64> &ctx) {
u32 *buf = (u32 *)(ctx.buf + this->hdr.offset);
void StubsSection<E>::copy_buf(Context<E> &ctx) {
ul32 *buf = (ul32 *)(ctx.buf + this->hdr.offset);
for (i64 i = 0; i < syms.size(); i++) {
static const u32 insn[] = {
@@ -26,22 +27,22 @@ void StubsSection<ARM64>::copy_buf(Context<ARM64> &ctx) {
0xd61f0200, // br x16
};
static_assert(sizeof(insn) == ARM64::stub_size);
static_assert(sizeof(insn) == E::stub_size);
u64 la_addr = ctx.lazy_symbol_ptr.hdr.addr + ARM64::word_size * i;
u64 this_addr = this->hdr.addr + ARM64::stub_size * i;
u64 la_addr = ctx.lazy_symbol_ptr.hdr.addr + word_size * i;
u64 this_addr = this->hdr.addr + E::stub_size * i;
memcpy(buf, insn, sizeof(insn));
buf[0] |= encode_page(page(la_addr) - page(this_addr));
buf[0] |= page_offset(la_addr, this_addr);
buf[1] |= bits(la_addr, 11, 3) << 10;
buf += 3;
}
}
template <>
void StubHelperSection<ARM64>::copy_buf(Context<ARM64> &ctx) {
u32 *start = (u32 *)(ctx.buf + this->hdr.offset);
u32 *buf = start;
void StubHelperSection<E>::copy_buf(Context<E> &ctx) {
ul32 *start = (ul32 *)(ctx.buf + this->hdr.offset);
ul32 *buf = start;
static const u32 insn0[] = {
0x90000011, // adrp x17, $__dyld_private@PAGE
@@ -52,15 +53,15 @@ void StubHelperSection<ARM64>::copy_buf(Context<ARM64> &ctx) {
0xd61f0200, // br x16
};
static_assert(sizeof(insn0) == ARM64::stub_helper_hdr_size);
static_assert(sizeof(insn0) == E::stub_helper_hdr_size);
memcpy(buf, insn0, sizeof(insn0));
u64 dyld_private = get_symbol(ctx, "__dyld_private")->get_addr(ctx);
buf[0] |= encode_page(page(dyld_private) - page(this->hdr.addr));
buf[0] |= page_offset(dyld_private, this->hdr.addr);
buf[1] |= bits(dyld_private, 11, 0) << 10;
u64 stub_binder = get_symbol(ctx, "dyld_stub_binder")->get_addr(ctx);
buf[3] |= encode_page(page(stub_binder) - page(this->hdr.addr - 12));
u64 stub_binder = get_symbol(ctx, "dyld_stub_binder")->get_got_addr(ctx);
buf[3] |= page_offset(stub_binder, this->hdr.addr - 12);
buf[4] |= bits(stub_binder, 11, 0) << 10;
buf += 6;
@@ -72,7 +73,7 @@ void StubHelperSection<ARM64>::copy_buf(Context<ARM64> &ctx) {
0x00000000, // addr: .long <idx>
};
static_assert(sizeof(insn) == ARM64::stub_helper_size);
static_assert(sizeof(insn) == E::stub_helper_size);
memcpy(buf, insn, sizeof(insn));
buf[1] |= bits((start - buf - 1) * 4, 27, 2);
@@ -81,73 +82,86 @@ void StubHelperSection<ARM64>::copy_buf(Context<ARM64> &ctx) {
}
}
static i64 read_addend(u8 *buf, const MachRel &r) {
if (r.p2size == 2)
return *(i32 *)(buf + r.offset);
if (r.p2size == 3)
return *(i64 *)(buf + r.offset);
unreachable();
}
static Relocation<ARM64>
read_reloc(Context<ARM64> &ctx, ObjectFile<ARM64> &file,
const MachSection &hdr, MachRel *rels, i64 &idx) {
i64 addend = 0;
switch (rels[idx].type) {
case ARM64_RELOC_UNSIGNED:
case ARM64_RELOC_SUBTRACTOR:
addend = read_addend((u8 *)file.mf->data + hdr.offset, rels[idx]);
break;
case ARM64_RELOC_ADDEND:
addend = rels[idx++].offset;
break;
}
MachRel &r = rels[idx];
Relocation<ARM64> rel{r.offset, (u8)r.type, (u8)r.p2size, (bool)r.is_pcrel};
if (r.is_extern) {
rel.sym = file.syms[r.idx];
rel.addend = addend;
return rel;
}
u32 addr;
if (r.is_pcrel)
addr = hdr.addr + r.offset + addend;
else
addr = addend;
Subsection<ARM64> *target = file.find_subsection(ctx, addr);
if (!target)
Fatal(ctx) << file << ": bad relocation: " << r.offset;
rel.subsec = target;
rel.addend = addr - target->input_addr;
return rel;
}
template <>
std::vector<Relocation<ARM64>>
read_relocations(Context<ARM64> &ctx, ObjectFile<ARM64> &file,
const MachSection &hdr) {
std::vector<Relocation<ARM64>> vec;
std::vector<Relocation<E>>
read_relocations(Context<E> &ctx, ObjectFile<E> &file, const MachSection &hdr) {
std::vector<Relocation<E>> vec;
vec.reserve(hdr.nreloc);
MachRel *rels = (MachRel *)(file.mf->data + hdr.reloff);
for (i64 i = 0; i < hdr.nreloc; i++)
vec.push_back(read_reloc(ctx, file, hdr, rels, i));
for (i64 i = 0; i < hdr.nreloc; i++) {
i64 addend = 0;
switch (rels[i].type) {
case ARM64_RELOC_UNSIGNED:
case ARM64_RELOC_SUBTRACTOR:
switch (rels[i].p2size) {
case 2:
addend = *(il32 *)((u8 *)file.mf->data + hdr.offset + rels[i].offset);
break;
case 3:
addend = *(il64 *)((u8 *)file.mf->data + hdr.offset + rels[i].offset);
break;
default:
unreachable();
}
break;
case ARM64_RELOC_ADDEND:
addend = rels[i++].idx;
break;
}
MachRel &r = rels[i];
vec.push_back({r.offset, (u8)r.type, (u8)r.p2size});
Relocation<E> &rel = vec.back();
if (i > 0 && rels[i - 1].type == ARM64_RELOC_SUBTRACTOR)
rel.is_subtracted = true;
if (!rel.is_subtracted && rels[i].type != ARM64_RELOC_SUBTRACTOR)
rel.is_pcrel = r.is_pcrel;
if (r.is_extern) {
rel.sym = file.syms[r.idx];
rel.addend = addend;
continue;
}
u64 addr = r.is_pcrel ? (hdr.addr + r.offset + addend) : addend;
Subsection<E> *target = file.find_subsection(ctx, r.idx - 1, addr);
if (!target)
Fatal(ctx) << file << ": bad relocation: " << r.offset;
rel.subsec = target;
rel.addend = addr - target->input_addr;
}
return vec;
}
template <>
void Subsection<ARM64>::scan_relocations(Context<ARM64> &ctx) {
for (Relocation<ARM64> &r : get_rels()) {
Symbol<ARM64> *sym = r.sym;
void Subsection<E>::scan_relocations(Context<E> &ctx) {
for (Relocation<E> &r : get_rels()) {
Symbol<E> *sym = r.sym;
if (!sym)
continue;
if (sym->is_imported && sym->file->is_dylib)
((DylibFile<E> *)sym->file)->is_alive = true;
switch (r.type) {
case ARM64_RELOC_UNSIGNED:
if (sym->is_imported) {
if (r.p2size != 3) {
Error(ctx) << this->isec << ": " << r << " relocation at offset 0x"
<< std::hex << r.offset << " against symbol `"
<< *sym << "' can not be used";
}
r.needs_dynrel = true;
}
break;
case ARM64_RELOC_GOT_LOAD_PAGE21:
case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
case ARM64_RELOC_POINTER_TO_GOT:
@@ -159,94 +173,108 @@ void Subsection<ARM64>::scan_relocations(Context<ARM64> &ctx) {
break;
}
if (sym->file && sym->file->is_dylib()) {
if (sym->is_imported)
sym->flags |= NEEDS_STUB;
((DylibFile<ARM64> *)sym->file)->is_needed = true;
}
}
}
template <>
void Subsection<ARM64>::apply_reloc(Context<ARM64> &ctx, u8 *buf) {
std::span<Relocation<ARM64>> rels = get_rels();
void Subsection<E>::apply_reloc(Context<E> &ctx, u8 *buf) {
std::span<Relocation<E>> rels = get_rels();
for (i64 i = 0; i < rels.size(); i++) {
Relocation<ARM64> &r = rels[i];
Relocation<E> &r = rels[i];
u8 *loc = buf + r.offset;
i64 val = r.addend;
u64 pc = get_addr(ctx) + r.offset;
if (r.sym && !r.sym->file) {
Error(ctx) << "undefined symbol: " << isec.file << ": " << *r.sym;
continue;
}
u64 val = 0;
// Compute a relocated value.
switch (r.type) {
case ARM64_RELOC_UNSIGNED:
case ARM64_RELOC_BRANCH26:
case ARM64_RELOC_PAGE21:
case ARM64_RELOC_PAGEOFF12:
val = r.sym ? r.sym->get_addr(ctx) : r.subsec->get_addr(ctx);
val += r.sym ? r.sym->get_addr(ctx) : r.subsec->get_addr(ctx);
break;
case ARM64_RELOC_SUBTRACTOR: {
Relocation<ARM64> s = rels[++i];
Relocation<E> s = rels[++i];
assert(s.type == ARM64_RELOC_UNSIGNED);
u64 val1 = r.sym ? r.sym->get_addr(ctx) : r.subsec->get_addr(ctx);
u64 val2 = s.sym ? s.sym->get_addr(ctx) : s.subsec->get_addr(ctx);
val = val2 - val1;
val += val2 - val1;
break;
}
case ARM64_RELOC_GOT_LOAD_PAGE21:
case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
case ARM64_RELOC_POINTER_TO_GOT:
val = r.sym->get_got_addr(ctx);
val += r.sym->get_got_addr(ctx);
break;
case ARM64_RELOC_TLVP_LOAD_PAGE21:
case ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
val = r.sym->get_tlv_addr(ctx);
val += r.sym->get_tlv_addr(ctx);
break;
default:
Fatal(ctx) << isec << ": unknown reloc: " << (int)r.type;
}
val += r.addend;
// An address of a thread-local variable is computed as an offset
// to the beginning of the first thread-local section.
if (isec.hdr.type == S_THREAD_LOCAL_VARIABLES)
val -= ctx.tls_begin;
// Write a computed value to the output buffer.
switch (r.type) {
case ARM64_RELOC_UNSIGNED:
case ARM64_RELOC_SUBTRACTOR:
case ARM64_RELOC_POINTER_TO_GOT:
if (r.is_pcrel)
val -= get_addr(ctx) + r.offset;
val -= pc;
if (r.p2size == 2)
*(i32 *)(buf + r.offset) = val;
*(ul32 *)loc = val;
else if (r.p2size == 3)
*(i64 *)(buf + r.offset) = val;
*(ul64 *)loc = val;
else
unreachable();
break;
case ARM64_RELOC_BRANCH26:
if (r.is_pcrel)
val -= get_addr(ctx) + r.offset;
*(u32 *)(buf + r.offset) |= bits(val, 27, 2);
case ARM64_RELOC_BRANCH26: {
assert(r.is_pcrel);
val -= pc;
i64 lo = -(1 << 27);
i64 hi = 1 << 27;
if (val < lo || hi <= val) {
val = isec.osec.thunks[r.thunk_idx]->get_addr(r.thunk_sym_idx) - pc;
assert(lo <= val && val < hi);
}
*(ul32 *)loc |= bits(val, 27, 2);
break;
}
case ARM64_RELOC_PAGE21:
case ARM64_RELOC_GOT_LOAD_PAGE21:
case ARM64_RELOC_TLVP_LOAD_PAGE21:
assert(r.is_pcrel);
*(u32 *)(buf + r.offset) |=
encode_page(page(val) - page(get_addr(ctx) + r.offset));
*(ul32 *)loc |= page_offset(val, pc);
break;
case ARM64_RELOC_PAGEOFF12:
case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: {
if (r.is_pcrel)
val -= get_addr(ctx) + r.offset;
u32 insn = *(u32 *)(buf + r.offset);
assert(!r.is_pcrel);
u32 insn = *(ul32 *)loc;
i64 scale = 0;
if ((insn & 0x3b000000) == 0x39000000)
scale = insn >> 30;
*(u32 *)(buf + r.offset) |= bits(val, 11, scale) << 10;
if ((insn & 0x3b000000) == 0x39000000) {
scale = bits(insn, 31, 30);
if (scale == 0 && (insn & 0x04800000) == 0x04800000)
scale = 4;
}
*(ul32 *)loc |= bits(val, 11, scale) << 10;
break;
}
default:
@@ -255,4 +283,427 @@ void Subsection<ARM64>::apply_reloc(Context<ARM64> &ctx, u8 *buf) {
}
}
static void reset_thunk(RangeExtensionThunk<E> &thunk) {
for (Symbol<E> *sym : thunk.symbols) {
sym->thunk_idx = -1;
sym->thunk_sym_idx = -1;
sym->flags &= (u8)~NEEDS_RANGE_EXTN_THUNK;
}
}
static bool is_reachable(Context<E> &ctx, Symbol<E> &sym,
Subsection<E> &subsec, Relocation<E> &rel) {
// We pessimistically assume that PLT entries are unreacahble.
if (sym.stub_idx != -1)
return false;
// We create thunks with a pessimistic assumption that all
// out-of-section relocations would be out-of-range.
if (!sym.subsec || &sym.subsec->isec.osec != &subsec.isec.osec)
return false;
if (sym.subsec->output_offset == -1)
return false;
// Compute a distance between the relocated place and the symbol
// and check if they are within reach.
i64 addr = sym.get_addr(ctx);
i64 pc = subsec.get_addr(ctx) + rel.offset;
i64 val = addr + rel.addend - pc;
return -(1 << 27) <= val && val < (1 << 27);
}
// We create a thunk no further than 100 MiB from any section.
static constexpr i64 MAX_DISTANCE = 100 * 1024 * 1024;
// We create a thunk for each 10 MiB input sections.
static constexpr i64 GROUP_SIZE = 10 * 1024 * 1024;
// ARM64's call/jump instructions take 27 bits displacement, so they
// can refer only up to ±128 MiB. If a branch target is further than
// that, we need to let it branch to a linker-synthesized code
// sequence that construct a full 32 bit address in a register and
// jump there. That linker-synthesized code is called "thunk".
void create_range_extension_thunks(Context<E> &ctx, OutputSection<E> &osec) {
std::span<Subsection<E> *> members = osec.members;
if (members.empty())
return;
members[0]->output_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)members.size(), [&](i64 i) {
members[i]->output_offset = -1;
});
// We create thunks from the beginning of the section to the end.
// We manage progress using four offsets which increase monotonically.
// The locations they point to are always A <= B <= C <= D.
i64 a = 0;
i64 b = 0;
i64 c = 0;
i64 d = 0;
i64 offset = 0;
while (b < members.size()) {
// Move D foward as far as we can jump from B to D.
while (d < members.size() &&
offset - members[b]->output_offset < MAX_DISTANCE) {
offset = align_to(offset, 1 << members[d]->p2align);
members[d]->output_offset = offset;
offset += members[d]->input_size;
d++;
}
// Move C forward so that C is apart from B by GROUP_SIZE.
while (c < members.size() &&
members[c]->output_offset - members[b]->output_offset < GROUP_SIZE)
c++;
// Move A forward so that A is reachable from C.
if (c > 0) {
i64 c_end = members[c - 1]->output_offset + members[c - 1]->input_size;
while (a < osec.thunks.size() &&
osec.thunks[a]->offset < c_end - MAX_DISTANCE)
reset_thunk(*osec.thunks[a++]);
}
// Create a thunk for input sections between B and C and place it at D.
osec.thunks.emplace_back(new RangeExtensionThunk<E>{osec});
RangeExtensionThunk<E> &thunk = *osec.thunks.back();
thunk.thunk_idx = osec.thunks.size() - 1;
thunk.offset = offset;
// Scan relocations between B and C to collect symbols that need thunks.
tbb::parallel_for_each(members.begin() + b, members.begin() + c,
[&](Subsection<E> *subsec) {
std::span<Relocation<E>> rels = subsec->get_rels();
for (i64 i = 0; i < rels.size(); i++) {
Relocation<E> &r = rels[i];
if (!r.sym->file || r.type != ARM64_RELOC_BRANCH26)
continue;
// Skip if the destination is within reach.
if (is_reachable(ctx, *r.sym, *subsec, r))
continue;
// If the symbol is already in another thunk, reuse it.
if (r.sym->thunk_idx != -1) {
r.thunk_idx = r.sym->thunk_idx;
r.thunk_sym_idx = r.sym->thunk_sym_idx;
continue;
}
// Otherwise, add the symbol to this thunk if it's not added already.
r.thunk_idx = thunk.thunk_idx;
r.thunk_sym_idx = -1;
if (!(r.sym->flags.fetch_or(NEEDS_RANGE_EXTN_THUNK) &
NEEDS_RANGE_EXTN_THUNK)) {
std::scoped_lock lock(thunk.mu);
thunk.symbols.push_back(r.sym);
}
}
});
// Now that we know the number of symbols in the thunk, we can compute
// its size.
offset += thunk.size();
// Sort symbols added to the thunk to make the output deterministic.
sort(thunk.symbols, [](Symbol<E> *a, Symbol<E> *b) {
return std::tuple{a->file->priority, a->value} <
std::tuple{b->file->priority, b->value};
});
// Assign offsets within the thunk to the symbols.
for (i64 i = 0; Symbol<E> *sym : thunk.symbols) {
sym->thunk_idx = thunk.thunk_idx;
sym->thunk_sym_idx = i++;
}
// Scan relocations again to fix symbol offsets in the last thunk.
tbb::parallel_for_each(members.begin() + b, members.begin() + c,
[&](Subsection<E> *subsec) {
for (Relocation<E> &r : subsec->get_rels())
if (r.thunk_idx == thunk.thunk_idx)
r.thunk_sym_idx = r.sym->thunk_sym_idx;
});
// Move B forward to point to the begining of the next group.
b = c;
}
while (a < osec.thunks.size())
reset_thunk(*osec.thunks[a++]);
osec.hdr.size = offset;
}
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + output_section.hdr.offset + offset;
static const u32 data[] = {
0x90000010, // adrp x16, 0 # R_AARCH64_ADR_PREL_PG_HI21
0x91000210, // add x16, x16 # R_AARCH64_ADD_ABS_LO12_NC
0xd61f0200, // br x16
};
static_assert(ENTRY_SIZE == sizeof(data));
for (i64 i = 0; i < symbols.size(); i++) {
u64 addr = symbols[i]->get_addr(ctx);
u64 pc = output_section.hdr.addr + offset + i * ENTRY_SIZE;
u8 *loc = buf + i * ENTRY_SIZE;
memcpy(loc , data, sizeof(data));
*(ul32 *)loc |= page_offset(addr, pc);
*(ul32 *)(loc + 4) |= bits(addr, 11, 0) << 10;
}
}
#define ASSERT_RANGE(val, start, size) \
assert((start) <= (val) && (val) < ((start) + (size)))
// Rewrite
// ldr Xb/Wb, [Xa, #imm]
// with
// ldr Xb/Wb, [#base_addr + #imm]
// if we can.
static bool relax_ldr(ul32 *loc, u64 loc_addr, u64 base_addr) {
switch (*loc & 0xffc0'0000) {
case 0xf940'0000: { // ldr Xb, [Xa, #imm]
u64 imm = bits(*loc, 21, 10) * 8;
i64 disp = base_addr + imm - loc_addr;
if (disp == sign_extend(disp, 20) && (disp & 0b11) == 0) {
// ldr Xc, [#base_addr + #imm]
*loc = 0x5800'0000 | (bits(disp, 20, 2) << 5) | bits(*loc, 4, 0);
return true;
}
return false;
}
case 0xb940'0000: { // ldr Wb, [Xa, #imm]
u64 imm = bits(*loc, 21, 10) * 4;
i64 disp = base_addr + imm - loc_addr;
if (disp == sign_extend(disp, 20) && (disp & 0b11) == 0) {
// ldr Wc, [#base_addr + #imm]
*loc = 0x1800'0000 | (bits(disp, 20, 2) << 5) | bits(*loc, 4, 0);
return true;
}
return false;
}
}
return false;
}
static bool is_adrp(ul32 *loc) {
return (*loc & 0x9f00'0000) == 0x9000'0000;
}
static u64 get_adrp_imm(ul32 *loc) {
return (bits(*loc, 23, 5) << 14) + (bits(*loc, 30, 29) << 12);
}
template <typename E>
static void relax_adrp_ldr_got_ldr(Context<E> &ctx,
ul32 *loc1, ul32 *loc2, ul32 *loc3,
u64 addr1, u64 addr2, u64 addr3,
ObjectFile<E> *file) {
// We expect the following instructions for a GOT-indirect load:
//
// adrp Xa, _foo@GOTPAGE
// ldr Xb, [Xa, _foo@GOTPAGEOFF]
// ldr/ldrb/ldrsb/ldrsh/... Xc/Wc, [Xb, #imm]
if (!is_adrp(loc1) || (*loc2 & 0xffc0'0000) != 0xf940'0000)
return;
u64 got_page = page(addr1) + get_adrp_imm(loc1);
u64 pageoff = bits(*loc2, 21, 10) << 3;
if (got_page + pageoff < ctx.got.hdr.addr ||
ctx.got.hdr.addr + ctx.got.hdr.size <= got_page + pageoff)
Fatal(ctx) << *file << ": LDR_GOT_LDR out of range";
ASSERT_RANGE(got_page + pageoff, ctx.got.hdr.addr, ctx.got.hdr.size);
u64 got_value = *(ul64 *)(ctx.buf + ctx.got.hdr.offset + got_page + pageoff -
ctx.got.hdr.addr);
// If the GOT slot is already filled with a value and we can
// inline the value, do it.
if (got_value && relax_ldr(loc3, addr3, got_value)) {
*loc1 = 0xd503'201f; // nop
*loc2 = 0xd503'201f; // nop
return;
}
// If the GOT slot is close enough to PC, we can eliminate ADRP.
if (relax_ldr(loc2, addr2, got_page))
*loc1 = 0xd503'201f; // nop
}
static void relax_adrp_adrp(Context<E> &ctx, ul32 *loc1, ul32 *loc2,
u64 addr1, u64 addr2) {
// adrp Xa, _foo@PAGE
// adrp Xa, _bar@PAGE
// ->
// ldr Xa, _foo@PAGE
// nop
if (is_adrp(loc1) && is_adrp(loc2) &&
page(addr1) + get_adrp_imm(loc1) == page(addr2) + get_adrp_imm(loc2) &&
bits(*loc1, 4, 0) == bits(*loc2, 4, 0))
*loc2 = 0xd503'201f; // nop
}
static void relax_adrp_ldr(Context<E> &ctx, ul32 *loc1, ul32 *loc2,
u64 addr1, u64 addr2) {
// adrp Xa, _foo@PAGE
// ldr Xb/Wb, [Xa, _foo@PAGEOFF]
// ->
// nop
// ldr Xb/Wb, [_foo]
if (is_adrp(loc1) && relax_ldr(loc2, addr2, page(addr1) + get_adrp_imm(loc1)))
*loc1 = 0xd503'201f; // nop
}
static void relax_adrp_add(Context<E> &ctx, ul32 *loc1, ul32 *loc2,
u64 addr1, u64 addr2) {
// adrp Xa, _foo@PAGE
// add Xb, Xa, _foo@PAGEOFF
// ->
// nop
// adr Xb, _foo
if (!is_adrp(loc1) || (*loc2 & 0xffc0'0000) != 0x9100'0000)
return;
i64 disp = page(addr1) + get_adrp_imm(loc1) + bits(*loc2, 21, 10) - addr2;
if (disp == sign_extend(disp, 20)) {
*loc1 = 0xd503'201f; // nop
*loc2 = 0x1000'0000 | (bits(disp, 1, 0) << 29) |
(bits(disp, 20, 2) << 5) | bits(*loc2, 4, 0); // adr Xb, _foo
}
}
static Subsection<E> *find_subsection_by_addr(ObjectFile<E> *file, u32 addr) {
for (Subsection<E> *subsec : file->subsections)
if (subsec->input_addr <= addr &&
addr < subsec->input_addr + subsec->input_size)
return subsec;
return nullptr;
}
// On ARM, we generally need two or more instructions to materialize
// an address of an object in a register or jump to a function.
// However, if an object or a function is close enough to PC, a single
// instruction suffices.
//
// This function replaces such redundant instruction sequences with
// shorter instruction sequences. We pad holes with NOPs, so the total
// number of instructions won't change by this relaxation, but replacing
// instructions with NOPs generally makes program faster since the CPU
// has a special logic to skip NOPs as quickly as possible.
//
// Locations of relaxable instructions are in the
// LC_LINKER_OPTIMIZATION_HINT segment. That segment contains a
// sequence of ULEB-encoded integers.
//
// This pass is optional; an output file works correctly without
// applying these optimizations.
void apply_linker_optimization_hints(Context<E> &ctx) {
Timer t(ctx, "apply_linker_optimization_hints");
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
std::string_view hints = file->get_linker_optimization_hints(ctx);
while (!hints.empty()) {
i64 type = read_uleb(hints);
if (type == 0)
return;
i64 nargs = read_uleb(hints);
switch (type) {
case LOH_ARM64_ADRP_LDR_GOT_LDR: {
assert(nargs == 3);
i64 input_addr1 = read_uleb(hints);
i64 input_addr2 = read_uleb(hints);
i64 input_addr3 = read_uleb(hints);
Subsection<E> *subsec = find_subsection_by_addr(file, input_addr1);
if (!subsec || !subsec->is_alive)
return;
ASSERT_RANGE(input_addr1, subsec->input_addr, subsec->input_size);
ASSERT_RANGE(input_addr2, subsec->input_addr, subsec->input_size);
ASSERT_RANGE(input_addr3, subsec->input_addr, subsec->input_size);
i64 offset1 = subsec->input_addr - input_addr1;
i64 offset2 = subsec->input_addr - input_addr2;
i64 offset3 = subsec->input_addr - input_addr3;
u8 *loc = ctx.buf + subsec->isec.osec.hdr.offset + subsec->output_offset;
ul32 *loc1 = (ul32 *)(loc + offset1);
ul32 *loc2 = (ul32 *)(loc + offset2);
ul32 *loc3 = (ul32 *)(loc + offset3);
u64 addr1 = subsec->get_addr(ctx) + offset1;
u64 addr2 = subsec->get_addr(ctx) + offset2;
u64 addr3 = subsec->get_addr(ctx) + offset3;
relax_adrp_ldr_got_ldr(ctx, loc1, loc2, loc3, addr1, addr2, addr3,
file);
break;
}
case LOH_ARM64_ADRP_ADRP:
case LOH_ARM64_ADRP_LDR:
case LOH_ARM64_ADRP_ADD: {
assert(nargs == 2);
i64 input_addr1 = read_uleb(hints);
i64 input_addr2 = read_uleb(hints);
Subsection<E> *subsec = find_subsection_by_addr(file, input_addr1);
if (!subsec || !subsec->is_alive)
return;
ASSERT_RANGE(input_addr1, subsec->input_addr, subsec->input_size);
ASSERT_RANGE(input_addr2, subsec->input_addr, subsec->input_size);
i64 offset1 = subsec->input_addr - input_addr1;
i64 offset2 = subsec->input_addr - input_addr2;
u8 *loc = ctx.buf + subsec->isec.osec.hdr.offset + subsec->output_offset;
ul32 *loc1 = (ul32 *)(loc + offset1);
ul32 *loc2 = (ul32 *)(loc + offset2);
u64 addr1 = subsec->get_addr(ctx) + offset1;
u64 addr2 = subsec->get_addr(ctx) + offset2;
switch (type) {
case LOH_ARM64_ADRP_ADRP:
relax_adrp_adrp(ctx, loc1, loc2, addr1, addr2);
break;
case LOH_ARM64_ADRP_LDR:
relax_adrp_ldr(ctx, loc1, loc2, addr1, addr2);
break;
case LOH_ARM64_ADRP_ADD:
relax_adrp_add(ctx, loc1, loc2, addr1, addr2);
break;
}
break;
}
default:
// Skip unsupported optimizations hints.
for (i64 i = 0; i < nargs; i++)
read_uleb(hints);
}
}
});
}
} // namespace mold::macho
+93 -99
View File
@@ -2,25 +2,26 @@
namespace mold::macho {
using E = X86_64;
template <>
void StubsSection<X86_64>::copy_buf(Context<X86_64> &ctx) {
void StubsSection<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + this->hdr.offset;
for (i64 i = 0; i < syms.size(); i++) {
// `ff 25 xx xx xx xx` is a RIP-relative indirect jump instruction,
// i.e., `jmp *IMM(%rip)`. It loads an address from la_symbol_ptr
// and jump there.
static_assert(X86_64::stub_size == 6);
static_assert(E::stub_size == 6);
buf[i * 6] = 0xff;
buf[i * 6 + 1] = 0x25;
*(u32 *)(buf + i * 6 + 2) =
ctx.lazy_symbol_ptr.hdr.addr + i * X86_64::word_size -
(this->hdr.addr + i * 6 + 6);
*(ul32 *)(buf + i * 6 + 2) =
ctx.lazy_symbol_ptr.hdr.addr + i * word_size - (this->hdr.addr + i * 6 + 6);
}
}
template <>
void StubHelperSection<X86_64>::copy_buf(Context<X86_64> &ctx) {
void StubHelperSection<E>::copy_buf(Context<E> &ctx) {
u8 *start = ctx.buf + this->hdr.offset;
u8 *buf = start;
@@ -31,12 +32,12 @@ void StubHelperSection<X86_64>::copy_buf(Context<X86_64> &ctx) {
0x90, // nop
};
static_assert(sizeof(insn0) == X86_64::stub_helper_hdr_size);
static_assert(sizeof(insn0) == E::stub_helper_hdr_size);
memcpy(buf, insn0, sizeof(insn0));
*(u32 *)(buf + 3) =
*(ul32 *)(buf + 3) =
get_symbol(ctx, "__dyld_private")->get_addr(ctx) - this->hdr.addr - 7;
*(u32 *)(buf + 11) =
*(ul32 *)(buf + 11) =
get_symbol(ctx, "dyld_stub_binder")->get_got_addr(ctx) - this->hdr.addr - 15;
buf += 16;
@@ -47,11 +48,11 @@ void StubHelperSection<X86_64>::copy_buf(Context<X86_64> &ctx) {
0xe9, 0, 0, 0, 0, // jmp $__stub_helper
};
static_assert(sizeof(insn) == X86_64::stub_helper_size);
static_assert(sizeof(insn) == E::stub_helper_size);
memcpy(buf, insn, sizeof(insn));
*(u32 *)(buf + 1) = ctx.stubs.bind_offsets[i];
*(u32 *)(buf + 6) = start - buf - 10;
*(ul32 *)(buf + 1) = ctx.stubs.bind_offsets[i];
*(ul32 *)(buf + 6) = start - buf - 10;
buf += 10;
}
}
@@ -71,102 +72,101 @@ static i64 get_reloc_addend(u32 type) {
static i64 read_addend(u8 *buf, const MachRel &r) {
if (r.p2size == 2)
return *(i32 *)(buf + r.offset) + get_reloc_addend(r.type);
return *(il32 *)(buf + r.offset) + get_reloc_addend(r.type);
if (r.p2size == 3)
return *(i64 *)(buf + r.offset) + get_reloc_addend(r.type);
return *(il64 *)(buf + r.offset) + get_reloc_addend(r.type);
unreachable();
}
static Relocation<X86_64>
read_reloc(Context<X86_64> &ctx, ObjectFile<X86_64> &file,
const MachSection &hdr, MachRel &r) {
if (r.p2size != 2 && r.p2size != 3)
Fatal(ctx) << file << ": invalid r.p2size: " << (u32)r.p2size;
if (r.is_pcrel) {
if (r.p2size != 2)
Fatal(ctx) << file << ": invalid PC-relative reloc: " << r.offset;
} else {
if (r.p2size != 3)
Fatal(ctx) << file << ": invalid non-PC-relative reloc: " << r.offset;
}
u8 *buf = (u8 *)file.mf->data + hdr.offset;
Relocation<X86_64> rel{r.offset, (u8)r.type, (u8)r.p2size, (bool)r.is_pcrel};
i64 addend = read_addend(buf, r);
if (r.is_extern) {
rel.sym = file.syms[r.idx];
rel.addend = addend;
return rel;
}
u32 addr;
if (r.is_pcrel)
addr = hdr.addr + r.offset + 4 + addend;
else
addr = addend;
Subsection<X86_64> *target = file.find_subsection(ctx, addr);
if (!target)
Fatal(ctx) << file << ": bad relocation: " << r.offset;
rel.subsec = target;
rel.addend = addr - target->input_addr;
return rel;
}
template <>
std::vector<Relocation<X86_64>>
read_relocations(Context<X86_64> &ctx, ObjectFile<X86_64> &file,
std::vector<Relocation<E>>
read_relocations(Context<E> &ctx, ObjectFile<E> &file,
const MachSection &hdr) {
std::vector<Relocation<X86_64>> vec;
std::vector<Relocation<E>> vec;
vec.reserve(hdr.nreloc);
MachRel *rels = (MachRel *)(file.mf->data + hdr.reloff);
for (i64 i = 0; i < hdr.nreloc; i++)
vec.push_back(read_reloc(ctx, file, hdr, rels[i]));
for (i64 i = 0; i < hdr.nreloc; i++) {
MachRel &r = rels[i];
i64 addend = read_addend((u8 *)file.mf->data + hdr.offset, r);
vec.push_back({r.offset, (u8)r.type, (u8)r.p2size});
Relocation<E> &rel = vec.back();
if (i > 0 && rels[i - 1].type == X86_64_RELOC_SUBTRACTOR)
rel.is_subtracted = true;
if (!rel.is_subtracted && rels[i].type != X86_64_RELOC_SUBTRACTOR)
rel.is_pcrel = r.is_pcrel;
if (r.is_extern) {
rel.sym = file.syms[r.idx];
rel.addend = addend;
continue;
}
u64 addr = r.is_pcrel ? (hdr.addr + r.offset + addend + 4) : addend;
Subsection<E> *target = file.find_subsection(ctx, r.idx - 1, addr);
if (!target)
Fatal(ctx) << file << ": bad relocation: " << r.offset;
rel.subsec = target;
rel.addend = addr - target->input_addr;
}
return vec;
}
template <>
void Subsection<X86_64>::scan_relocations(Context<X86_64> &ctx) {
for (Relocation<X86_64> &r : get_rels()) {
Symbol<X86_64> *sym = r.sym;
void Subsection<E>::scan_relocations(Context<E> &ctx) {
for (Relocation<E> &r : get_rels()) {
Symbol<E> *sym = r.sym;
if (!sym)
continue;
if (sym->is_imported && sym->file->is_dylib)
((DylibFile<E> *)sym->file)->is_alive = true;
switch (r.type) {
case X86_64_RELOC_GOT_LOAD:
if (sym->file && sym->file->is_dylib())
sym->flags |= NEEDS_GOT;
case X86_64_RELOC_UNSIGNED:
case X86_64_RELOC_SUBTRACTOR:
if (sym->is_imported) {
if (r.p2size != 3) {
Error(ctx) << this->isec << ": " << r << " relocation at offset 0x"
<< std::hex << r.offset << " against symbol `"
<< *sym << "' can not be used";
}
r.needs_dynrel = true;
}
break;
case X86_64_RELOC_GOT:
case X86_64_RELOC_GOT_LOAD:
sym->flags |= NEEDS_GOT;
break;
case X86_64_RELOC_TLV:
if (sym->file && sym->file->is_dylib())
sym->flags |= NEEDS_THREAD_PTR;
sym->flags |= NEEDS_THREAD_PTR;
break;
}
if (sym->file && sym->file->is_dylib()) {
if (sym->is_imported)
sym->flags |= NEEDS_STUB;
((DylibFile<X86_64> *)sym->file)->is_needed = true;
}
}
}
template <>
void Subsection<X86_64>::apply_reloc(Context<X86_64> &ctx, u8 *buf) {
for (const Relocation<X86_64> &r : get_rels()) {
void Subsection<E>::apply_reloc(Context<E> &ctx, u8 *buf) {
std::span<Relocation<E>> rels = get_rels();
for (i64 i = 0; i < rels.size(); i++) {
Relocation<E> &r = rels[i];
u64 val = r.addend;
if (r.sym && !r.sym->file) {
Error(ctx) << "undefined symbol: " << isec.file << ": " << *r.sym;
continue;
}
u64 val = 0;
switch (r.type) {
case X86_64_RELOC_UNSIGNED:
case X86_64_RELOC_SIGNED:
@@ -174,46 +174,40 @@ void Subsection<X86_64>::apply_reloc(Context<X86_64> &ctx, u8 *buf) {
case X86_64_RELOC_SIGNED_1:
case X86_64_RELOC_SIGNED_2:
case X86_64_RELOC_SIGNED_4:
val = r.sym ? r.sym->get_addr(ctx) : r.subsec->get_addr(ctx);
val += r.sym ? r.sym->get_addr(ctx) : r.subsec->get_addr(ctx);
break;
case X86_64_RELOC_GOT_LOAD:
if (r.sym->got_idx != -1) {
val = r.sym->get_got_addr(ctx);
} else {
// Relax MOVQ into LEAQ
if (buf[r.offset - 2] != 0x8b)
Error(ctx) << isec << ": invalid GOT_LOAD relocation";
buf[r.offset - 2] = 0x8d;
val = r.sym->get_addr(ctx);
}
case X86_64_RELOC_SUBTRACTOR: {
Relocation<E> s = rels[++i];
assert(s.type == X86_64_RELOC_UNSIGNED);
assert(r.p2size == s.p2size);
u64 val1 = r.sym ? r.sym->get_addr(ctx) : r.subsec->get_addr(ctx);
u64 val2 = s.sym ? s.sym->get_addr(ctx) : s.subsec->get_addr(ctx);
val += val2 - val1;
break;
}
case X86_64_RELOC_GOT:
val = r.sym->get_got_addr(ctx);
case X86_64_RELOC_GOT_LOAD:
val += r.sym->get_got_addr(ctx);
break;
case X86_64_RELOC_TLV:
if (r.sym->tlv_idx != -1) {
val = r.sym->get_tlv_addr(ctx);
} else {
// Relax MOVQ into LEAQ
if (buf[r.offset - 2] != 0x8b)
Error(ctx) << isec << ": invalid TLV relocation";
buf[r.offset - 2] = 0x8d;
val = r.sym->get_addr(ctx);
}
val += r.sym->get_tlv_addr(ctx);
break;
default:
Fatal(ctx) << isec << ": unknown reloc: " << (int)r.type;
}
val += r.addend;
if (r.is_pcrel)
if (isec.hdr.type == S_THREAD_LOCAL_VARIABLES) {
// An address of a thread-local variable is computed as an offset
// to the beginning of the first thread-local section.
val -= ctx.tls_begin;
} else if (r.is_pcrel) {
val -= get_addr(ctx) + r.offset + 4 + get_reloc_addend(r.type);
}
if (r.p2size == 2)
*(u32 *)(buf + r.offset) = val;
*(ul32 *)(buf + r.offset) = val;
else if (r.p2size == 3)
*(u64 *)(buf + r.offset) = val;
*(ul64 *)(buf + r.offset) = val;
else
unreachable();
}
+279 -28
View File
@@ -2,56 +2,112 @@
#include "../cmdline.h"
#include <optional>
#include <regex>
#include <sstream>
#include <sys/stat.h>
#include <sys/types.h>
#include <regex>
#include <unistd.h>
#include <unordered_set>
#ifndef _WIN32
# include <unistd.h>
#endif
namespace mold::macho {
static const char helpmsg[] = R"(
Options:
-F<PATH> Add DIR to framework search path
-L<PATH> Add DIR to library search path
-Z Do not search the standard directories when
searching for libraries and frameworks
-ObjC Load all static archive members that implement
an Objective-C class or category
-U <SYMBOL> Allow a symbol to be undefined
-Z Do not search the standard directories when
searching for libraries and frameworks
-add_ast_path <FILE> Add a N_AST symbol with the given filename
-add_empty_section <SEGNAME> <SECTNAME>
Add an empty section
-adhoc_codesign Add ad-hoc code signature to the output file
-no_adhoc_codesign
-all_load Include all objects from static archives
-noall_load
-application_extension Verify that all dylibs are extension-safe
-no_application_extension
-arch <ARCH_NAME> Specify target architecture
-bundle Produce a mach-o bundle
-bundle_loader <EXECUTABLE> Resolve undefined symbols using the given executable
-compatibility_version <VERSION>
Specifies the compatibility version number of the library
-current_version <VERSION> Specifies the current version number of the library.
-dead_strip Remove unreachable functions and data
-dead_strip_dylibs Remove unreachable dylibs from dependencies
-debug_variant Ignored
-demangle Demangle C++ symbols in log messages (default)
-dependency_info <FILE> Ignored
-dylib Produce a dynamic library
-dylib_compatibility_version <VERSION>
Alias for -compatibility_version
-dylib_current_version <VERSION>
Alias for -current_version
-dylib_install_name Alias for -install_name
-dynamic Link against dylibs (default)
-e <SYMBOL> Specify the entry point of a main executable
-execute Produce an executable (default)
-export_dynamic Preserves all global symbols in main executables during LTO
-exported_symbol <SYMBOL> Export a given symbol
-exported_symbols_list <FILE>
Read a list of exported symbols from a given file
-filelist <FILE>[,<DIR>] Specify the list of input file names
-final_output <NAME>
-force_load <FILE> Include all objects from a given static archive
-framework <NAME>,[,<SUFFIX>]
Search for a given framework
-headerpad <SIZE> Allocate the size of padding after load commands
-headerpad_max_install_names
Allocate MAXPATHLEN byte padding after load commands
-help Report usage information
-hidden-l<LIB>
-ignore_optimization_hints Do not rewrite instructions as optimization (default)
-enable_optimization_hints
-install_name <NAME>
-l<LIB> Search for a given library
-lto_library <FILE> Ignored
-macos_version_min <VERSION>
-map <FILE> Write map file to a given file
-mark_dead_strippable_dylib Mark the output as dead-strippable
-needed-l<LIB> Search for a given library
-needed-framework <NAME>[,<SUFFIX>]
Search for a given framework
-no_deduplicate Ignored
-no_function_starts Do not generate an LC_FUNCTION_STARTS load command
-no_uuid Do not generate an LC_UUID load command
-o <FILE> Set output filename
-objc_abi_version <VERSION> Ignored
-object_path_lto <FILE> Write a LTO temporary file to a given path
-order_file <FILE> Ignored
-pagezero_size <SIZE> Specify the size of the __PAGEZERO segment
-platform_version <PLATFORM> <MIN_VERSION> <SDK_VERSION>
Set platform, platform version and SDK version
-random_uuid Generate a random LC_UUID load command
-reexport-l<LIB> Search for a given library
-rpath <PATH> Add PATH to the runpath search path list
-search_dylibs_first
-search_paths_first
-sectalign <SEGNAME> <SECTNAME> <VALUE>
Set a section's alignment to a given value
-sectcreate <SEGNAME> <SECTNAME> <FILE>
-stack_size <SIZE>
-stats Show statistics info
-syslibroot <DIR> Prepend DIR to library search paths
-t Print out each file the linker loads
-v Report version information)";
-thread_count <NUMBER> Use given number of threads
-u <SYMBOL> Force load a given symbol from archive if necessary
-unexported_symbol <SYMBOL> Export all but a given symbol
-unexported_symbols_list <FILE>
Read a list of unexported symbols from a given file
-v Report version information
-weak_framework <NAME>[,<SUFFIX>]
Search for a given framework
-weak-l<LIB> Search for a given library)";
template <typename E>
static i64 parse_platform(Context<E> &ctx, std::string_view arg) {
@@ -83,11 +139,11 @@ static i64 parse_platform(Context<E> &ctx, std::string_view arg) {
}
template <typename E>
static i64 parse_version(Context<E> &ctx, std::string_view arg) {
i64 parse_version(Context<E> &ctx, std::string_view arg) {
static std::regex re(R"((\d+)(?:\.(\d+))?(?:\.(\d+))?)",
std::regex_constants::ECMAScript);
std::cmatch m;
if (!std::regex_match(arg.begin(), arg.end(), m, re))
if (!std::regex_match(arg.data(), arg.data() + arg.size(), m, re))
Fatal(ctx) << "malformed version number: " << arg;
i64 major = (m[1].length() == 0) ? 0 : stoi(m[1]);
@@ -96,26 +152,70 @@ static i64 parse_version(Context<E> &ctx, std::string_view arg) {
return (major << 16) | (minor << 8) | patch;
}
template <typename E>
i64 parse_hex(Context<E> &ctx, std::string_view arg) {
auto flags = std::regex_constants::ECMAScript | std::regex_constants::icase;
static std::regex re(R"((?:0x)?[0-9a-f]+)", flags);
std::cmatch m;
if (!std::regex_match(arg.begin(), arg.end(), re))
Fatal(ctx) << "malformed hexadecimal number: " << arg;
if (arg.starts_with("0x") || arg.starts_with("0X"))
return std::stoll(std::string(arg.substr(2)), nullptr, 16);
return std::stoll(std::string(arg), nullptr, 16);
}
static bool is_directory(std::filesystem::path path) {
std::error_code ec;
return std::filesystem::is_directory(path, ec) && !ec;
}
template <typename E>
void parse_nonpositional_args(Context<E> &ctx,
std::vector<std::string> &remaining) {
static std::vector<std::string>
read_lines(Context<E> &ctx, std::string_view path) {
MappedFile<Context<E>> *mf =
MappedFile<Context<E>>::must_open(ctx, std::string(path));
std::string_view data((char *)mf->data, mf->size);
std::vector<std::string> vec;
while (!data.empty()) {
size_t pos = data.find('\n');
std::string_view line;
if (pos == data.npos) {
line = data;
data = "";
} else {
line = data.substr(0, pos);
data = data.substr(pos + 1);
}
line = string_trim(line);
if (!line.empty() && !line.starts_with('#'))
vec.push_back(std::string(line));
}
return vec;
}
template <typename E>
std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
std::vector<std::string_view> &args = ctx.cmdline_args;
std::vector<std::string> remaining;
i64 i = 1;
std::vector<std::string> framework_paths;
std::vector<std::string> library_paths;
bool nostdlib = false;
bool version_shown = false;
std::optional<i64> pagezero_size;
while (i < args.size()) {
std::string_view arg;
std::string_view arg2;
std::string_view arg3;
u64 hex_arg;
auto read_arg = [&](std::string name) {
if (args[i] == name) {
@@ -128,6 +228,18 @@ void parse_nonpositional_args(Context<E> &ctx,
return false;
};
auto read_arg2 = [&](std::string name) {
if (args[i] == name) {
if (args.size() <= i + 2)
Fatal(ctx) << "option -" << name << ": argument missing";
arg = args[i + 1];
arg2 = args[i + 2];
i += 3;
return true;
}
return false;
};
auto read_arg3 = [&](std::string name) {
if (args[i] == name) {
if (args.size() <= i + 3)
@@ -160,6 +272,17 @@ void parse_nonpositional_args(Context<E> &ctx,
return false;
};
auto read_hex = [&](std::string name) {
if (!read_arg(name))
return false;
size_t pos;
hex_arg = std::stol(std::string(arg), &pos, 16);
if (pos != arg.size())
Fatal(ctx) << "malformed " << name << ": " << arg;
return true;
};
if (args[i].starts_with('@')) {
std::vector<std::string_view> vec =
read_response_file(ctx, args[i].substr(1));
@@ -169,7 +292,7 @@ void parse_nonpositional_args(Context<E> &ctx,
}
if (read_flag("-help") || read_flag("--help")) {
SyncOut(ctx) << "Usage: " << ctx.cmdline_args[i]
SyncOut(ctx) << "Usage: " << ctx.cmdline_args[0]
<< " [options] file...\n" << helpmsg;
exit(0);
}
@@ -182,10 +305,24 @@ void parse_nonpositional_args(Context<E> &ctx,
nostdlib = true;
} else if (read_flag("-ObjC")) {
ctx.arg.ObjC = true;
} else if (read_arg("-U")) {
ctx.arg.U.push_back(std::string(arg));
} else if (read_arg("-add_ast_path")) {
ctx.arg.add_ast_path.push_back(std::string(arg));
} else if (read_arg2("-add_empty_section")) {
ctx.arg.add_empty_section.push_back({arg, arg2});
} else if (read_flag("-adhoc_codesign")) {
ctx.arg.adhoc_codesign = true;
} else if (read_flag("-no_adhoc_codesign")) {
ctx.arg.adhoc_codesign = false;
} else if (read_flag("-all_load")) {
remaining.push_back("-all_load");
} else if (read_flag("-noall_load")) {
remaining.push_back("-noall_load");
} else if (read_flag("-application_extension")) {
ctx.arg.application_extension = true;
} else if (read_flag("-no_application_extension")) {
ctx.arg.application_extension = false;
} else if (read_arg("-arch")) {
if (arg == "x86_64")
ctx.arg.arch = CPU_TYPE_X86_64;
@@ -195,42 +332,83 @@ void parse_nonpositional_args(Context<E> &ctx,
Fatal(ctx) << "unknown -arch: " << arg;
} else if (read_flag("-bundle")) {
ctx.output_type = MH_BUNDLE;
} else if (read_arg("-bundle_loader")) {
ctx.arg.bundle_loader = arg;
} else if (read_arg("-compatibility_version") ||
read_arg("-dylib_compatibility_version")) {
ctx.arg.compatibility_version = parse_version(ctx, arg);
} else if (read_arg("-current_version") ||
read_arg("-dylib_current_version")) {
ctx.arg.current_version = parse_version(ctx, arg);
} else if (read_flag("-color-diagnostics") ||
read_flag("--color-diagnostics")) {
ctx.arg.color_diagnostics = true;
} else if (read_flag("-dead_strip")) {
ctx.arg.dead_strip = true;
} else if (read_flag("-dead_strip_dylibs")) {
ctx.arg.dead_strip_dylibs = true;
} else if (read_flag("-debug_variant")) {
} else if (read_flag("-demangle")) {
ctx.arg.demangle = true;
} else if (read_arg("-dependency_info")) {
ctx.arg.dependency_info = arg;
} else if (read_flag("-dylib")) {
ctx.output_type = MH_DYLIB;
} else if (read_arg("-headerpad")) {
size_t pos;
ctx.arg.headerpad = std::stol(std::string(arg), &pos, 16);
if (pos != arg.size())
Fatal(ctx) << "malformed -headerpad: " << arg;
} else if (read_hex("-headerpad")) {
ctx.arg.headerpad = hex_arg;
} else if (read_flag("-headerpad_max_install_names")) {
ctx.arg.headerpad = 1024;
} else if (read_flag("-dynamic")) {
ctx.arg.dynamic = true;
} else if (read_arg("-e")) {
ctx.arg.entry = arg;
ctx.arg.entry = get_symbol(ctx, arg);
} else if (read_flag("-execute")) {
ctx.output_type = MH_EXECUTE;
} else if (read_flag("-export_dynamic")) {
ctx.arg.export_dynamic = true;
} else if (read_arg("-exported_symbol")) {
if (!ctx.arg.exported_symbols_list.add(arg, 1))
Fatal(ctx) << "-exported_symbol: invalid glob pattern: " << arg;
} else if (read_arg("-exported_symbols_list")) {
for (std::string_view pat : read_lines(ctx, arg))
if (!ctx.arg.exported_symbols_list.add(pat, 1))
Fatal(ctx) << "-exported_symbols_list: " << arg
<< ": invalid glob pattern: " << pat;
} else if (read_arg("-fatal_warnings")) {
} else if (read_arg("-filelist")) {
remaining.push_back("-filelist");
remaining.push_back(std::string(arg));
} else if (read_arg("-final_output")) {
ctx.arg.final_output = arg;
} else if (read_arg("-force_load")) {
remaining.push_back("-force_load");
remaining.push_back(std::string(arg));
} else if (read_arg("-framework")) {
remaining.push_back("-framework");
remaining.push_back(std::string(arg));
} else if (read_arg("-lto_library")) {
ctx.arg.lto_library = arg;
} else if (read_arg("-macos_version_min")) {
ctx.arg.platform = PLATFORM_MACOS;
ctx.arg.platform_min_version = parse_version(ctx, arg);
} else if (read_joined("-hidden-l")) {
remaining.push_back("-hidden-l");
remaining.push_back(std::string(arg));
} else if (read_flag("-ignore_optimization_hints")) {
ctx.arg.ignore_optimization_hints = true;
} else if (read_flag("-enable_optimization_hints")) {
ctx.arg.ignore_optimization_hints = false;
} else if (read_arg("-install_name") || read_arg("-dylib_install_name")) {
ctx.arg.install_name = arg;
} else if (read_joined("-l")) {
remaining.push_back("-l");
remaining.push_back(std::string(arg));
} else if (read_arg("-map")) {
ctx.arg.map = arg;
} else if (read_flag("-mark_dead_strippable_dylib")) {
ctx.arg.mark_dead_strippable_dylib = true;
} else if (read_arg("-mllvm")) {
ctx.arg.mllvm.push_back(std::string(arg));
} else if (read_joined("-needed-l")) {
remaining.push_back("-needed-l");
remaining.push_back(std::string(arg));
@@ -238,28 +416,78 @@ void parse_nonpositional_args(Context<E> &ctx,
remaining.push_back("-needed_framework");
remaining.push_back(std::string(arg));
} else if (read_flag("-no_deduplicate")) {
} else if (read_flag("-no_function_starts")) {
ctx.arg.function_starts = false;
} else if (read_flag("-no_uuid")) {
ctx.arg.uuid = UUID_NONE;
} else if (read_arg("-o")) {
ctx.arg.output = arg;
} else if (read_arg("-pagezero_size")) {
size_t pos;
pagezero_size = std::stol(std::string(arg), &pos, 16);
if (pos != arg.size())
Fatal(ctx) << "malformed -pagezero_size: " << arg;
} else if (read_arg("-objc_abi_version")) {
} else if (read_arg("-object_path_lto")) {
ctx.arg.object_path_lto = arg;
} else if (read_arg("-order_file")) {
ctx.arg.order_file = read_lines(ctx, arg);
} else if (read_hex("-pagezero_size")) {
pagezero_size = hex_arg;
} else if (read_flag("-perf")) {
ctx.arg.perf = true;
} else if (read_arg3("-platform_version")) {
ctx.arg.platform = parse_platform(ctx, arg);
ctx.arg.platform_min_version = parse_version(ctx, arg2);
ctx.arg.platform_sdk_version = parse_version(ctx, arg3);
} else if (read_flag("-quick_exit")) {
ctx.arg.quick_exit = true;
} else if (read_flag("-no_quick_exit")) {
ctx.arg.quick_exit = false;
} else if (read_flag("-random_uuid")) {
ctx.arg.uuid = UUID_RANDOM;
} else if (read_joined("-reexport-l")) {
remaining.push_back("-reexport-l");
remaining.push_back(std::string(arg));
} else if (read_arg("-rpath")) {
ctx.arg.rpath.push_back(std::string(arg));
} else if (read_flag("-search_dylibs_first")) {
Fatal(ctx) << "-search_dylibs_first is not supported";
} else if (read_flag("-search_paths_first")) {
ctx.arg.search_paths_first = true;
} else if (read_flag("-search_dylibs_first")) {
ctx.arg.search_paths_first = false;
} else if (read_arg3("-sectalign")) {
u64 val = parse_hex(ctx, arg3);
std::string key = std::string(arg) + "," + std::string(arg2);
if (!has_single_bit(val))
Fatal(ctx) << "-sectalign: invalid alignment value: " << arg3;
ctx.arg.sectalign.push_back({arg, arg2, (u8)std::countl_zero(val)});
} else if (read_arg3("-sectcreate")) {
ctx.arg.sectcreate.push_back({arg, arg2, arg3});
} else if (read_hex("-stack_size")) {
ctx.arg.stack_size = hex_arg;
} else if (read_flag("-stats")) {
ctx.arg.stats = true;
Counter::enabled = true;
} else if (read_arg("-syslibroot")) {
ctx.arg.syslibroot.push_back(std::string(arg));
} else if (read_flag("-t")) {
ctx.arg.trace = true;
} else if (read_arg("-thread_count")) {
ctx.arg.thread_count = std::stoi(std::string(arg));
} else if (read_arg("-u")) {
ctx.arg.u.push_back(std::string(arg));
} else if (read_arg("-unexported_symbol")) {
if (!ctx.arg.unexported_symbols_list.add(arg, 1))
Fatal(ctx) << "-unexported_symbol: invalid glob pattern: " << arg;
} else if (read_arg("-unexported_symbols_list")) {
for (std::string_view pat : read_lines(ctx, arg))
if (!ctx.arg.unexported_symbols_list.add(pat, 1))
Fatal(ctx) << "-unexported_symbols_list: " << arg
<< ": invalid glob pattern: " << pat;
} else if (read_flag("-v")) {
SyncOut(ctx) << mold_version;
version_shown = true;
} else if (read_arg("-weak_framework")) {
remaining.push_back("-weak_framework");
remaining.push_back(std::string(arg));
} else if (read_joined("-weak-l")) {
remaining.push_back("-weak-l");
remaining.push_back(std::string(arg));
} else {
if (args[i][0] == '-')
Fatal(ctx) << "unknown command line option: " << args[i];
@@ -268,6 +496,15 @@ void parse_nonpositional_args(Context<E> &ctx,
}
}
if (!ctx.arg.entry)
ctx.arg.entry = get_symbol(ctx, "_main");
if (ctx.arg.thread_count == 0)
ctx.arg.thread_count = get_default_thread_count();
if (!ctx.arg.bundle_loader.empty() && ctx.output_type != MH_BUNDLE)
Fatal(ctx) << "-bundle_loader cannot be specified without -bundle";
auto add_search_path = [&](std::vector<std::string> &vec, std::string path) {
if (!path.starts_with('/') || ctx.arg.syslibroot.empty()) {
if (is_directory(path))
@@ -305,18 +542,32 @@ void parse_nonpositional_args(Context<E> &ctx,
if (pagezero_size.has_value()) {
if (ctx.output_type != MH_EXECUTE)
Error(ctx) << "-pagezero_size option can only be used when"
Fatal(ctx) << "-pagezero_size option can only be used when"
<< " linking a main executable";
ctx.arg.pagezero_size = *pagezero_size;
} else {
ctx.arg.pagezero_size = (ctx.output_type == MH_EXECUTE) ? 0x100000000 : 0;
}
if (ctx.arg.final_output == "") {
if (ctx.arg.install_name != "")
ctx.arg.final_output = ctx.arg.install_name;
else
ctx.arg.final_output = ctx.arg.output;
}
if (ctx.arg.uuid == UUID_RANDOM)
memcpy(ctx.uuid, get_uuid_v4().data(), 16);
if (version_shown && remaining.empty())
exit(0);
return remaining;
}
#define INSTANTIATE(E) \
template void parse_nonpositional_args(Context<E> &, std::vector<std::string> &)
#define INSTANTIATE(E) \
template i64 parse_version(Context<E> &, std::string_view); \
template std::vector<std::string> parse_nonpositional_args(Context<E> &)
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
INSTANTIATE_ALL;
} // namespace mold::macho
+57 -22
View File
@@ -1,24 +1,48 @@
#include "mold.h"
#include <tbb/parallel_for_each.h>
namespace mold::macho {
template <typename E>
static std::vector<Subsection<E> *> collect_root_set(Context<E> &ctx) {
Timer t(ctx, "collect_root_set");
std::vector<Subsection<E> *> rootset;
auto mark = [&](Symbol<E> *sym) {
if (sym && sym->subsec)
auto add = [&](Symbol<E> *sym) {
if (sym->subsec)
rootset.push_back(sym->subsec);
};
mark(get_symbol(ctx, ctx.arg.entry));
auto keep = [&](Symbol<E> *sym) {
if (sym->no_dead_strip)
return true;
if (ctx.output_type == MH_DYLIB || ctx.output_type == MH_BUNDLE)
if (sym->scope == SCOPE_EXTERN || sym->referenced_dynamically)
return true;
return false;
};
if (ctx.output_type == MH_DYLIB || ctx.output_type == MH_BUNDLE)
for (ObjectFile<E> *file : ctx.objs)
for (Symbol<E> *sym : file->syms)
if (sym->file == file && sym->is_extern)
mark(sym);
for (ObjectFile<E> *file : ctx.objs) {
for (Symbol<E> *sym : file->syms)
if (sym->file == file && keep(sym))
add(sym);
for (Subsection<E> *subsec : file->subsections)
if (const MachSection &hdr = subsec->isec.hdr;
(hdr.attr & S_ATTR_NO_DEAD_STRIP) ||
hdr.type == S_MOD_INIT_FUNC_POINTERS ||
hdr.type == S_MOD_TERM_FUNC_POINTERS)
rootset.push_back(subsec);
}
for (std::string_view name : ctx.arg.u)
if (Symbol<E> *sym = get_symbol(ctx, name); sym->file)
add(sym);
add(ctx.arg.entry);
add(get_symbol(ctx, "dyld_stub_binder"));
return rootset;
}
@@ -37,12 +61,11 @@ static void visit(Context<E> &ctx, Subsection<E> &subsec) {
}
for (UnwindRecord<E> &rec : subsec.get_unwind_records()) {
rec.is_alive = true;
visit(ctx, *rec.subsec);
if (rec.lsda)
visit(ctx, *rec.lsda);
if (Symbol<E> *sym = rec.personality; sym && sym->subsec)
visit(ctx, *sym->subsec);
if (rec.personality && rec.personality->subsec)
visit(ctx, *rec.personality->subsec);
}
}
@@ -50,7 +73,7 @@ template <typename E>
static bool refers_live_subsection(Subsection<E> &subsec) {
for (Relocation<E> &rel : subsec.get_rels()) {
if (rel.sym) {
if (!rel.sym->subsec || rel.sym->subsec->is_alive)
if (rel.sym->subsec && rel.sym->subsec->is_alive)
return true;
} else {
if (rel.subsec->is_alive)
@@ -62,6 +85,15 @@ static bool refers_live_subsection(Subsection<E> &subsec) {
template <typename E>
static void mark(Context<E> &ctx, const std::vector<Subsection<E> *> &rootset) {
Timer t(ctx, "mark");
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
for (Subsection<E> *subsec : file->subsections)
subsec->is_alive.store(false, std::memory_order_relaxed);
});
std::atomic_thread_fence(std::memory_order_seq_cst);
for (Subsection<E> *subsec : rootset)
visit(ctx, *subsec);
@@ -69,7 +101,7 @@ static void mark(Context<E> &ctx, const std::vector<Subsection<E> *> &rootset) {
do {
repeat = false;
for (ObjectFile<E> *file : ctx.objs) {
for (std::unique_ptr<Subsection<E>> &subsec : file->subsections) {
for (Subsection<E> *subsec : file->subsections) {
if ((subsec->isec.hdr.attr & S_ATTR_LIVE_SUPPORT) &&
!subsec->is_alive &&
refers_live_subsection(*subsec)) {
@@ -83,21 +115,25 @@ static void mark(Context<E> &ctx, const std::vector<Subsection<E> *> &rootset) {
template <typename E>
static void sweep(Context<E> &ctx) {
for (ObjectFile<E> *file : ctx.objs) {
std::erase_if(file->subsections,
[](const std::unique_ptr<Subsection<E>> &subsec) {
return !subsec->is_alive;
});
}
Timer t(ctx, "sweep");
for (ObjectFile<E> *file : ctx.objs)
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (Symbol<E> *&sym : file->syms)
if (sym->file == file && sym->subsec && !sym->subsec->is_alive)
sym = nullptr;
});
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
std::erase_if(file->subsections, [](Subsection<E> *subsec) {
return !subsec->is_alive;
});
});
}
template <typename E>
void dead_strip(Context<E> &ctx) {
Timer t(ctx, "dead_strip");
std::vector<Subsection<E> *> rootset = collect_root_set(ctx);
mark(ctx, rootset);
sweep(ctx);
@@ -106,7 +142,6 @@ void dead_strip(Context<E> &ctx) {
#define INSTANTIATE(E) \
template void dead_strip(Context<E> &)
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
INSTANTIATE_ALL;
} // namespace mold::macho
-511
View File
@@ -1,511 +0,0 @@
#include "mold.h"
#include <cstdlib>
#include <fcntl.h>
#include <iomanip>
#include <iostream>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
namespace mold::macho {
static u8 *open_file(std::string path) {
i64 fd = ::open(path.c_str(), O_RDONLY);
if (fd == -1)
return nullptr;
struct stat st;
if (fstat(fd, &st) == -1) {
std::cerr << path << ": fstat failed: " << errno_string();
exit(1);
}
void *ptr = mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (ptr == MAP_FAILED) {
std::cerr << path << ": mmap failed: " << errno_string();
exit(1);
}
close(fd);
return (u8 *)ptr;
}
static void print_bytes(u8 *buf, i64 size) {
if (size == 0) {
std::cout << "[]\n";
return;
}
std::cout << "[" << std::setw(2) << std::setfill('0') << (u32)buf[0];
for (i64 i = 1; i < size; i++)
std::cout << " " << std::setw(2) << std::setfill('0') << (u32)buf[i];
std::cout << "]\n";
}
struct ExportEntry {
std::string name;
u32 flags;
u64 addr;
};
static void read_trie(std::vector<ExportEntry> &vec, u8 *start, i64 offset = 0,
const std::string &prefix = "") {
u8 *buf = start + offset;
if (read_uleb(buf)) {
ExportEntry ent;
ent.name = prefix;
ent.flags = read_uleb(buf);
ent.addr = read_uleb(buf);
vec.push_back(std::move(ent));
}
for (i64 i = 0, n = read_uleb(buf); i < n; i++) {
std::string suffix((char *)buf);
buf += suffix.size() + 1;
i64 off = read_uleb(buf);
read_trie(vec, start, off, prefix + suffix);
}
}
void dump_unwind_info(u8 *buf, MachSection &sec) {
UnwindSectionHeader &hdr = *(UnwindSectionHeader *)(buf + sec.offset);
std::cout << std::hex << " Unwind info:"
<< "\n version: 0x" << hdr.version
<< "\n encoding_offset: 0x" << hdr.encoding_offset
<< "\n encoding_count: 0x" << hdr.encoding_count
<< "\n personality_offset: 0x" << hdr.personality_offset
<< "\n personality_count: 0x" << hdr.personality_count
<< "\n page_offset: 0x" << hdr.page_offset
<< "\n page_count: 0x" << hdr.page_count;
std::cout << "\n encoding:";
u32 *enc = (u32 *)(buf + sec.offset + hdr.encoding_offset);
for (i64 i = 0; i < hdr.encoding_count; i++)
std::cout << std::hex << "\n 0x" << enc[i];
std::cout << "\n personality:";
u32 *pers = (u32 *)(buf + sec.offset + hdr.personality_offset);
for (i64 i = 0; i < hdr.personality_count; i++)
std::cout << std::hex << "\n 0x" << pers[i];
UnwindFirstLevelPage *ent =
(UnwindFirstLevelPage *)(buf + sec.offset + hdr.page_offset);
for (i64 i = 0; i < hdr.page_count; i++) {
std::cout << std::hex << "\n function:"
<< "\n func_addr: 0x" << ent[i].func_addr
<< "\n page_offset: 0x" << ent[i].page_offset
<< "\n lsda_offset: 0x" << ent[i].lsda_offset;
if (i != hdr.page_count - 1) {
UnwindLsdaEntry *lsda =
(UnwindLsdaEntry *)(buf + sec.offset + ent[i].lsda_offset);
i64 lsda_size = ent[i + 1].lsda_offset - ent[i].lsda_offset;
for (i64 j = 0; j < lsda_size / sizeof(UnwindLsdaEntry); j++)
std::cout << std::hex
<< "\n lsda:"
<< "\n func_addr: 0x" << lsda[j].func_addr
<< "\n lsda_addr: 0x" << lsda[j].lsda_addr;
}
if (ent[i].page_offset == 0)
break;
u8 *addr = buf + sec.offset + ent[i].page_offset;
switch (u32 kind = *addr; kind) {
case UNWIND_SECOND_LEVEL_REGULAR: {
std::cout << "\n UNWIND_SECOND_LEVEL_REGULAR:" ;
break;
}
case UNWIND_SECOND_LEVEL_COMPRESSED: {
std::cout << "\n UNWIND_SECOND_LEVEL_COMPRESSED" ;
UnwindSecondLevelPage &hdr2 = *(UnwindSecondLevelPage *)addr;
std::cout << std::hex
<< "\n page_offset: 0x" << hdr2.page_offset
<< "\n page_count: 0x" << hdr2.page_count
<< "\n encoding_offset: 0x" << hdr2.encoding_offset
<< "\n encoding_count: 0x" << hdr2.encoding_count;
UnwindPageEntry *ent2 = (UnwindPageEntry *)(addr + hdr2.page_offset);
for (i64 j = 0; j < hdr2.page_count; j++)
std::cout << std::hex << "\n ent 0x"
<< (ent[i].func_addr + ent2[j].func_addr)
<< " 0x" << ent2[j].encoding;
u32 *enc = (u32 *)(addr + hdr2.encoding_offset);
for (i64 j = 0; j < hdr2.encoding_count; j++)
std::cout << std::hex << "\n 0x" << enc[j];
break;
}
default:
std::cout << "\n bad 2nd-level unwind info header: " << kind;
}
}
std::cout << "\n";
}
void dump_compact_unwind(u8 *buf, MachSection &sec) {
CompactUnwindEntry *ent = (CompactUnwindEntry *)(buf + sec.offset);
i64 nentry = sec.size / sizeof(CompactUnwindEntry);
std::cout << " Compact unwind:"
<< "\n num_entry: " << nentry;
for (i64 i = 0; i < nentry; i++) {
std::cout << std::hex
<< "\n entry: 0x" << (i * sizeof(CompactUnwindEntry))
<< "\n code_start: 0x" << ent[i].code_start
<< "\n code_len: 0x" << ent[i].code_len
<< "\n encoding: 0x" << ent[i].encoding
<< "\n personality: 0x" << ent[i].personality
<< "\n lsda: 0x" << ent[i].lsda;
}
std::cout << "\n";
}
void dump_file(std::string path) {
u8 *buf = open_file(path);
if (!buf) {
std::cerr << "cannot open " << path << "\n";
exit(1);
}
MachHeader &hdr = *(MachHeader *)buf;
std::cout << "magic: 0x" << std::hex << hdr.magic
<< "\ncputype: 0x" << hdr.cputype
<< "\ncpusubtype: 0x" << hdr.cpusubtype
<< "\nfiletype: 0x" << hdr.filetype
<< "\nncmds: 0x" << hdr.ncmds
<< "\nsizeofcmds: 0x" << hdr.sizeofcmds
<< "\nflags: 0x" << hdr.flags
<< "\n\n";
u8 *p = buf + sizeof(MachHeader);
for (i64 i = 0; i < hdr.ncmds; i++) {
std::cout << "fileoff: 0x" << std::hex << (p - buf) << "\n";
LoadCommand &lc = *(LoadCommand *)p;
p += lc.cmdsize;
auto print_dylib = [&] {
DylibCommand &cmd = *(DylibCommand *)&lc;
std::cout << " cmdsize: 0x" << cmd.cmdsize
<< "\n nameoff: 0x" << cmd.nameoff
<< "\n timestamp: 0x" << cmd.timestamp
<< "\n current_version: 0x" << cmd.current_version
<< "\n compatibility_version: 0x" << cmd.compatibility_version
<< "\n data: " << (char *)((char *)&cmd + cmd.nameoff)
<< "\n";
};
switch (lc.cmd) {
case LC_SYMTAB: {
std::cout << "LC_SYMTAB\n";
SymtabCommand &cmd = *(SymtabCommand *)&lc;
std::cout << " cmdsize: " << cmd.cmdsize
<< "\n symoff: 0x" << std::hex << cmd.symoff
<< "\n nsyms: " << std::dec << cmd.nsyms
<< "\n stroff: 0x" << std::hex << cmd.stroff
<< "\n strsize: 0x" << cmd.strsize
<< "\n symdata: ";
print_bytes(buf + cmd.symoff, cmd.nsyms * sizeof(MachSym));
std::cout << " strdata: ";
print_bytes(buf + cmd.stroff, cmd.strsize);
MachSym *syms = (MachSym *)(buf + cmd.symoff);
for (i64 j = 0; j < cmd.nsyms; j++) {
std::cout << " symbol:"
<< "\n name: " << (char *)(buf + cmd.stroff + syms[j].stroff)
<< "\n stub: " << (u32)syms[j].stub
<< "\n pext: " << (u32)syms[j].pext
<< "\n type: " << (u32)syms[j].type
<< "\n ext: " << (u32)syms[j].ext
<< "\n sect: 0x" << (u32)syms[j].sect
<< "\n desc: 0x" << (u32)syms[j].desc
<< "\n value: 0x" << syms[j].value
<< "\n";
}
break;
}
case LC_DYSYMTAB: {
std::cout << "LC_DYSYMTAB\n";
DysymtabCommand &cmd = *(DysymtabCommand *)&lc;
std::cout << " cmdsize: 0x" << cmd.cmdsize
<< "\n ilocalsym: 0x" << cmd.ilocalsym
<< "\n nlocalsym: 0x" << cmd.nlocalsym
<< "\n iextdefsym: 0x" << cmd.iextdefsym
<< "\n nextdefsym: 0x" << cmd.nextdefsym
<< "\n iundefsym: 0x" << cmd.iundefsym
<< "\n nundefsym: 0x" << cmd.nundefsym
<< "\n tocoff: 0x" << cmd.tocoff
<< "\n ntoc: 0x" << cmd.ntoc
<< "\n modtaboff: 0x" << cmd.modtaboff
<< "\n nmodtab: 0x" << cmd.nmodtab
<< "\n extrefsymoff: 0x" << cmd.extrefsymoff
<< "\n nextrefsyms: 0x" << cmd.nextrefsyms
<< "\n indirectsymoff: 0x" << cmd.indirectsymoff
<< "\n nindirectsyms: 0x" << cmd.nindirectsyms
<< "\n extreloff: 0x" << cmd.extreloff
<< "\n nextrel: 0x" << cmd.nextrel
<< "\n locreloff: 0x" << cmd.locreloff
<< "\n nlocrel: 0x" << cmd.nlocrel
<< "\n";
if (cmd.indirectsymoff) {
std::cout << " indirectsymdata: ";
print_bytes(buf + cmd.indirectsymoff, 4 * cmd.nindirectsyms);
}
break;
}
case LC_LOAD_DYLIB:
std::cout << "LC_LOAD_DYLIB\n";
print_dylib();
break;
case LC_LOAD_WEAK_DYLIB:
std::cout << "LC_LOAD_WEAK_DYLIB\n";
print_dylib();
break;
case LC_ID_DYLIB:
std::cout << "LC_ID_DYLIB\n";
print_dylib();
break;
case LC_LOAD_DYLINKER: {
std::cout << "LC_LOAD_DYLINKER\n";
DylinkerCommand &cmd = *(DylinkerCommand *)&lc;
std::cout << " cmdsize: 0x" << cmd.cmdsize
<< "\n nameoff: 0x" << cmd.nameoff
<< "\n data: " << (char *)((char *)&cmd + cmd.nameoff)
<< "\n";
break;
}
case LC_SEGMENT_64: {
std::cout << "LC_SEGMENT_64\n";
SegmentCommand &cmd = *(SegmentCommand *)&lc;
std::cout << " cmdsize: " << cmd.cmdsize
<< "\n segname: " << cmd.get_segname()
<< "\n vmaddr: 0x" << std::hex << cmd.vmaddr
<< "\n vmsize: 0x" << cmd.vmsize
<< "\n fileoff: 0x" << cmd.fileoff
<< "\n filesize: 0x" << cmd.filesize
<< "\n maxprot: " << std::dec << cmd.maxprot
<< "\n initprot: " << cmd.initprot
<< "\n nsects: " << cmd.nsects
<< "\n flags: 0x" << std::hex << cmd.flags
<< "\n";
MachSection *sec = (MachSection *)((u8 *)&lc + sizeof(cmd));
for (i64 j = 0; j < cmd.nsects; j++) {
std::cout << " section:\n sectname: " << sec[j].get_sectname()
<< "\n segname: " << sec[j].get_segname()
<< "\n addr: 0x" << std::hex << sec[j].addr
<< "\n size: 0x" << sec[j].size
<< "\n offset: 0x" << sec[j].offset
<< "\n p2align: " << std::dec << sec[j].p2align
<< "\n reloff: " << std::hex << sec[j].reloff
<< "\n nreloc: " << std::dec << sec[j].nreloc
<< "\n type: 0x" << std::hex << sec[j].type
<< "\n attr: 0x" << std::hex << sec[j].attr
<< "\n";
if (sec[j].type != S_ZEROFILL) {
std::cout << " contents: ";
print_bytes(buf + sec[j].offset, sec[j].size);
}
if (sec[j].reloff) {
MachRel *rel = (MachRel *)(buf + sec[j].reloff);
for (i64 k = 0; k < sec[j].nreloc; k++) {
std::cout << " reloc: "
<< "\n offset: 0x" << rel[k].offset
<< "\n idx: 0x" << rel[k].idx
<< "\n is_pcrel: " << rel[k].is_pcrel
<< "\n p2size: 0x" << rel[k].p2size
<< "\n is_extern: " << rel[k].is_extern
<< "\n type: " << rel[k].type
<< "\n";
}
}
if (sec[j].match("__TEXT", "__unwind_info"))
dump_unwind_info(buf, sec[j]);
if (sec[j].match("__LD", "__compact_unwind"))
dump_compact_unwind(buf, sec[j]);
}
break;
}
case LC_UUID: {
std::cout << "LC_UUID\n";
UUIDCommand &cmd = *(UUIDCommand *)&lc;
std::cout << " data: ";
print_bytes(cmd.uuid, sizeof(cmd.uuid));
break;
}
case LC_DYLD_INFO_ONLY: {
std::cout << "LC_DYLD_INFO_ONLY\n";
DyldInfoCommand &cmd = *(DyldInfoCommand *)&lc;
if (cmd.rebase_off) {
std::cout << " rebase: ";
print_bytes(buf + cmd.rebase_off, cmd.rebase_size);
std::cout << " rebase_off: 0x" << std::hex << cmd.rebase_off
<< "\n rebase_size: 0x" << cmd.rebase_size
<< "\n";
}
if (cmd.bind_off) {
std::cout << " bind: ";
print_bytes(buf + cmd.bind_off, cmd.bind_size);
std::cout << " bind_off: 0x" << std::hex << cmd.bind_off
<< "\n bind_size: 0x" << cmd.bind_size
<< "\n";
}
if (cmd.weak_bind_off) {
std::cout << " weak_bind: ";
print_bytes(buf + cmd.weak_bind_off, cmd.weak_bind_size);
std::cout << " weak_bind_off: 0x" << std::hex << cmd.weak_bind_off
<< "\n weak_bind_size: 0x" << cmd.weak_bind_size
<< "\n";
}
if (cmd.lazy_bind_off) {
std::cout << " lazy_bind: ";
print_bytes(buf + cmd.lazy_bind_off, cmd.lazy_bind_size);
std::cout << " lazy_bind_off: 0x" << std::hex << cmd.lazy_bind_off
<< "\n lazy_bind_size: 0x" << cmd.lazy_bind_size
<< "\n";
}
if (cmd.export_off) {
std::cout << " export: ";
print_bytes(buf + cmd.export_off, cmd.export_size);
std::cout << " export_off: 0x" << std::hex << cmd.export_off
<< "\n export_size: 0x" << cmd.export_size
<< "\n";
std::vector<ExportEntry> vec;
read_trie(vec, buf + cmd.export_off);
for (ExportEntry &ent : vec)
std::cout << " export_sym: " << ent.name << " 0x" << ent.addr << "\n";
}
break;
}
case LC_FUNCTION_STARTS: {
std::cout << "LC_FUNCTION_STARTS\n";
LinkEditDataCommand &cmd = *(LinkEditDataCommand *)&lc;
std::cout << " dataoff: 0x" << cmd.dataoff
<< "\n datasize: 0x" << cmd.datasize
<< "\n data:";
u8 *p = buf + cmd.dataoff;
u64 addr = 0;
for (;;) {
u64 delta = read_uleb(p);
if (!delta)
break;
addr += delta;
std::cout << std::hex << " 0x" << addr;
}
std::cout << "\n";
break;
}
case LC_MAIN: {
std::cout << "LC_MAIN\n";
EntryPointCommand &cmd = *(EntryPointCommand *)&lc;
std::cout << " cmdsize: 0x" << cmd.cmdsize
<< "\n entryoff: 0x" << cmd.entryoff
<< "\n stacksize: 0x" << cmd.stacksize
<< "\n";
break;
}
case LC_DATA_IN_CODE: {
std::cout << "LC_DATA_IN_CODE\n";
LinkEditDataCommand &cmd = *(LinkEditDataCommand *)&lc;
std::cout << " dataoff: 0x" << cmd.dataoff
<< "\n datasize: 0x" << cmd.datasize
<< "\n";
break;
}
case LC_SOURCE_VERSION: {
std::cout << "LC_SOURCE_VERSION\n";
SourceVersionCommand &cmd = *(SourceVersionCommand *)&lc;
std::cout << " version: 0x" << cmd.version
<< "\n";
break;
}
break;
case LC_BUILD_VERSION: {
std::cout << "LC_BUILD_VERSION\n";
BuildVersionCommand &cmd = *(BuildVersionCommand *)&lc;
std::cout << " cmdsize: 0x" << cmd.cmdsize
<< "\n platform: 0x" << cmd.platform
<< "\n minos: 0x" << cmd.minos
<< "\n sdk: 0x" << cmd.sdk
<< "\n ntools: 0x" << cmd.ntools
<< "\n";
BuildToolVersion *tools =
(BuildToolVersion *)((u8 *)&lc + sizeof(BuildVersionCommand));
for (i64 i = 0; i < cmd.ntools; i++)
std::cout << " tool: 0x" << tools[i].tool
<< "\n version: 0x" << tools[i].version
<< "\n";
break;
}
case LC_VERSION_MIN_MACOSX: {
std::cout << "LC_VERSION_MIN_MACOSX\n";
VersionMinCommand &cmd = *(VersionMinCommand *)&lc;
std::cout << " version: " << (int)cmd.version
<< "\n sdk: " << (int)cmd.sdk
<< "\n";
break;
}
case LC_CODE_SIGNATURE: {
std::cout << "LC_CODE_SIGNATURE\n";
LinkEditDataCommand &cmd = *(LinkEditDataCommand *)&lc;
CodeSignatureHeader &sig = *(CodeSignatureHeader *)(buf + cmd.dataoff);
std::cout << " magic: " << sig.magic
<< "\n length: " << sig.length
<< "\n count: " << sig.count
<< "\n";
for (i64 i = 0; i < sig.count; i++) {
CodeSignatureBlobIndex &idx = ((CodeSignatureBlobIndex *)(&sig + 1))[i];
std::cout << " idx type: " << idx.type
<< "\n idx offset: " << idx.offset
<< "\n";
CodeSignatureDirectory &dir =
*(CodeSignatureDirectory *)((u8 *)&sig + idx.offset);
std::cout << std::hex
<< " magic: 0x" << dir.magic
<< "\n version: 0x" << dir.version
<< "\n flags: 0x" << dir.flags
<< "\n hash_offset: 0x" << dir.hash_offset
<< "\n n_code_slots: 0x" << dir.n_code_slots
<< "\n hash_size: 0x" << (u32)dir.hash_size
<< "\n hash_type: 0x" << (u32)dir.hash_type
<< "\n page_size: 0x" << (1 << dir.page_size)
<< "\n";
}
break;
}
default:
std::cout << "UNKNOWN (0x" << std::hex << lc.cmd << ")\n";
break;
}
}
}
} // namespace mold::macho
+983
View File
@@ -0,0 +1,983 @@
#include "mold.h"
#include "../archive-file.h"
namespace mold::macho {
template <typename E>
std::ostream &operator<<(std::ostream &out, const InputFile<E> &file) {
if (file.archive_name.empty())
out << path_clean(file.filename);
else
out << path_clean(file.archive_name) << "(" << path_clean(file.filename) + ")";
return out;
}
template <typename E>
void InputFile<E>::clear_symbols() {
for (Symbol<E> *sym : syms) {
std::scoped_lock lock(sym->mu);
if (sym->file == this) {
sym->file = nullptr;
sym->scope = SCOPE_LOCAL;
sym->is_imported = false;
sym->is_weak = false;
sym->no_dead_strip = false;
sym->subsec = nullptr;
sym->value = 0;
sym->is_common = false;
}
}
}
template <typename E>
ObjectFile<E> *
ObjectFile<E>::create(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::string archive_name) {
ObjectFile<E> *obj = new ObjectFile<E>(mf);
obj->archive_name = archive_name;
obj->is_alive = archive_name.empty() || ctx.all_load;
obj->is_hidden = ctx.hidden_l;
ctx.obj_pool.emplace_back(obj);
return obj;
};
template <typename E>
void ObjectFile<E>::parse(Context<E> &ctx) {
if (get_file_type(this->mf) == FileType::LLVM_BITCODE) {
// Open an compiler IR file
load_lto_plugin(ctx);
this->lto_module =
ctx.lto.module_create_from_memory(this->mf->data, this->mf->size);
if (!this->lto_module)
Fatal(ctx) << *this << ": lto_module_create_from_memory failed";
// Read a symbol table
parse_lto_symbols(ctx);
return;
}
parse_sections(ctx);
parse_symbols(ctx);
MachHeader &mach_hdr = *(MachHeader *)this->mf->data;
if (mach_hdr.flags & MH_SUBSECTIONS_VIA_SYMBOLS)
split_subsections_via_symbols(ctx);
else
init_subsections(ctx);
sort(subsections, [](Subsection<E> *a, Subsection<E> *b) {
return a->input_addr < b->input_addr;
});
fix_subsec_members(ctx);
for (std::unique_ptr<InputSection<E>> &isec : sections)
if (isec)
isec->parse_relocations(ctx);
if (unwind_sec)
parse_compact_unwind(ctx, *unwind_sec);
}
template <typename E>
void ObjectFile<E>::parse_sections(Context<E> &ctx) {
SegmentCommand *cmd = (SegmentCommand *)find_load_command(ctx, LC_SEGMENT_64);
if (!cmd)
return;
MachSection *mach_sec = (MachSection *)((u8 *)cmd + sizeof(*cmd));
for (i64 i = 0; i < cmd->nsects; i++) {
MachSection &msec = mach_sec[i];
sections.push_back(nullptr);
if (msec.match("__LD", "__compact_unwind")) {
unwind_sec = &msec;
continue;
}
if (msec.match("__DATA", "__objc_imageinfo") ||
msec.match("__DATA_CONST", "__objc_imageinfo")) {
if (msec.size != sizeof(ObjcImageInfo))
Fatal(ctx) << *this << ": __objc_imageinfo: invalid size";
objc_image_info =
(ObjcImageInfo *)(this->mf->get_contents().data() + msec.offset);
if (objc_image_info->version != 0)
Fatal(ctx) << *this << ": __objc_imageinfo: unknown version: "
<< (u32)objc_image_info->version;
continue;
}
if (msec.attr & S_ATTR_DEBUG)
continue;
sections.back().reset(new InputSection<E>(ctx, *this, msec, i));
}
}
template <typename E>
void ObjectFile<E>::parse_symbols(Context<E> &ctx) {
SymtabCommand *cmd = (SymtabCommand *)find_load_command(ctx, LC_SYMTAB);
if (!cmd)
return;
mach_syms = {(MachSym *)(this->mf->data + cmd->symoff), cmd->nsyms};
this->syms.reserve(mach_syms.size());
i64 nlocal = 0;
for (MachSym &msym : mach_syms)
if (!msym.is_extern)
nlocal++;
local_syms.reserve(nlocal);
for (i64 i = 0; i < mach_syms.size(); i++) {
MachSym &msym = mach_syms[i];
std::string_view name = (char *)(this->mf->data + cmd->stroff + msym.stroff);
if (msym.is_extern) {
this->syms.push_back(get_symbol(ctx, name));
} else {
local_syms.emplace_back(name);
Symbol<E> &sym = local_syms.back();
sym.file = this;
sym.subsec = nullptr;
sym.scope = SCOPE_LOCAL;
sym.is_common = false;
sym.is_weak = false;
if (msym.type == N_ABS)
sym.value = msym.value;
sym.no_dead_strip = (msym.desc & N_NO_DEAD_STRIP);
this->syms.push_back(&sym);
}
}
}
struct SplitRegion {
u32 offset;
u32 size;
u32 symidx;
bool is_alt_entry;
};
template <typename E>
struct SplitInfo {
InputSection<E> *isec;
std::vector<SplitRegion> regions;
};
template <typename E>
static std::vector<SplitInfo<E>>
split_regular_sections(Context<E> &ctx, ObjectFile<E> &file) {
std::vector<SplitInfo<E>> vec(file.sections.size());
for (i64 i = 0; i < file.sections.size(); i++)
if (InputSection<E> *isec = file.sections[i].get())
if (isec->hdr.type != S_CSTRING_LITERALS)
vec[i].isec = isec;
// Find all symbols whose type is N_SECT.
for (i64 i = 0; i < file.mach_syms.size(); i++) {
MachSym &msym = file.mach_syms[i];
if (!msym.stab && msym.type == N_SECT && vec[msym.sect - 1].isec) {
SplitRegion r;
r.offset = msym.value - vec[msym.sect - 1].isec->hdr.addr;
r.symidx = i;
r.is_alt_entry = (msym.desc & N_ALT_ENTRY);
vec[msym.sect - 1].regions.push_back(r);
}
}
std::erase_if(vec, [](const SplitInfo<E> &info) { return !info.isec; });
sort(vec, [](const SplitInfo<E> &a, const SplitInfo<E> &b) {
return a.isec->hdr.addr < b.isec->hdr.addr;
});
for (SplitInfo<E> &info : vec) {
sort(info.regions, [](const SplitRegion &a, const SplitRegion &b) {
return a.offset < b.offset;
});
}
// If two symbols point to the same location, we create only one
// subsection.
for (SplitInfo<E> &info : vec) {
i64 last = -1;
for (SplitRegion &r : info.regions) {
if (!r.is_alt_entry) {
if (r.offset == last)
r.is_alt_entry = true;
last = r.offset;
}
}
}
// Fix regions so that they cover the entire section without overlapping.
for (SplitInfo<E> &info : vec) {
std::vector<SplitRegion> &r = info.regions;
if (r.empty()) {
r.push_back({0, (u32)info.isec->hdr.size, (u32)-1, false});
continue;
}
if (r[0].offset > 0)
r.insert(r.begin(), {0, r[0].offset, (u32)-1, false});
for (i64 i = 1; i < r.size(); i++)
if (r[i - 1].offset == r[i].offset)
r[i++].is_alt_entry = true;
i64 last = -1;
for (i64 i = 0; i < r.size(); i++) {
if (!r[i].is_alt_entry) {
if (last != -1)
r[last].size = r[i].offset - r[last].offset;
last = i;
}
}
if (last != -1)
r[last].size = info.isec->hdr.size - r[last].offset;
}
return vec;
}
template <typename E>
void ObjectFile<E>::split_subsections_via_symbols(Context<E> &ctx) {
sym_to_subsec.resize(mach_syms.size());
auto add = [&](InputSection<E> &isec, u32 offset, u32 size, u8 p2align,
bool is_cstring) {
Subsection<E> *subsec = new Subsection<E>{
.isec = isec,
.input_offset = offset,
.input_size = size,
.input_addr = (u32)(isec.hdr.addr + offset),
.p2align = p2align,
.is_cstring = is_cstring,
};
subsec_pool.emplace_back(subsec);
subsections.push_back(subsec);
};
// Split regular sections into subsections.
for (SplitInfo<E> &info : split_regular_sections(ctx, *this)) {
InputSection<E> &isec = *info.isec;
for (SplitRegion &r : info.regions) {
if (!r.is_alt_entry)
add(isec, r.offset, r.size, isec.hdr.p2align, false);
if (r.symidx != -1)
sym_to_subsec[r.symidx] = subsections.back();
}
}
// Split __cstring section.
for (std::unique_ptr<InputSection<E>> &isec : sections) {
if (isec && isec->hdr.type == S_CSTRING_LITERALS) {
std::string_view str = isec->contents;
size_t pos = 0;
while (pos < str.size()) {
size_t end = str.find('\0', pos);
if (end == str.npos)
Fatal(ctx) << *this << " corruupted cstring section: " << *isec;
end = str.find_first_not_of('\0', end);
if (end == str.npos)
end = str.size();
// A constant string in __cstring has no alignment info, so we
// need to infer it.
u8 p2align = std::min<u8>(isec->hdr.p2align, std::countr_zero(pos));
add(*isec, pos, end - pos, p2align, true);
pos = end;
}
}
}
}
template <typename E>
void ObjectFile<E>::init_subsections(Context<E> &ctx) {
subsections.resize(sections.size());
for (i64 i = 0; i < sections.size(); i++) {
if (InputSection<E> *isec = sections[i].get()) {
Subsection<E> *subsec = new Subsection<E>{
.isec = *isec,
.input_offset = 0,
.input_size = (u32)isec->hdr.size,
.input_addr = (u32)isec->hdr.addr,
.p2align = (u8)isec->hdr.p2align,
};
subsec_pool.emplace_back(subsec);
subsections[i] = subsec;
}
}
sym_to_subsec.resize(mach_syms.size());
for (i64 i = 0; i < mach_syms.size(); i++) {
MachSym &msym = mach_syms[i];
if (!msym.stab && msym.type == N_SECT)
sym_to_subsec[i] = subsections[msym.sect - 1];
}
std::erase(subsections, nullptr);
}
// Fix local symbols `subsec` members.
template <typename E>
void ObjectFile<E>::fix_subsec_members(Context<E> &ctx) {
for (i64 i = 0; i < mach_syms.size(); i++) {
MachSym &msym = mach_syms[i];
Symbol<E> &sym = *this->syms[i];
if (!msym.stab && !msym.is_extern && msym.type == N_SECT) {
Subsection<E> *subsec = sym_to_subsec[i];
if (!subsec)
subsec = find_subsection(ctx, msym.sect - 1, msym.value);
if (subsec) {
sym.subsec = subsec;
sym.value = msym.value - subsec->input_addr;
} else {
// Subsec is null if a symbol is in a __compact_unwind.
sym.subsec = nullptr;
sym.value = msym.value;
}
}
}
}
template <typename E>
void ObjectFile<E>::parse_data_in_code(Context<E> &ctx) {
if (auto *cmd = (LinkEditDataCommand *)find_load_command(ctx, LC_DATA_IN_CODE)) {
data_in_code_entries = {
(DataInCodeEntry *)(this->mf->data + cmd->dataoff),
cmd->datasize / sizeof(DataInCodeEntry),
};
}
}
template <typename E>
std::vector<std::string> ObjectFile<E>::get_linker_options(Context<E> &ctx) {
if (get_file_type(this->mf) == FileType::LLVM_BITCODE)
return {};
MachHeader &hdr = *(MachHeader *)this->mf->data;
u8 *p = this->mf->data + sizeof(hdr);
std::vector<std::string> vec;
for (i64 i = 0; i < hdr.ncmds; i++) {
LoadCommand &lc = *(LoadCommand *)p;
p += lc.cmdsize;
if (lc.cmd == LC_LINKER_OPTION) {
LinkerOptionCommand *cmd = (LinkerOptionCommand *)&lc;
char *buf = (char *)cmd + sizeof(*cmd);
for (i64 i = 0; i < cmd->count; i++) {
vec.push_back(buf);
buf += vec.back().size() + 1;
}
}
}
return vec;
}
template <typename E>
LoadCommand *ObjectFile<E>::find_load_command(Context<E> &ctx, u32 type) {
if (!this->mf)
return nullptr;
MachHeader &hdr = *(MachHeader *)this->mf->data;
u8 *p = this->mf->data + sizeof(hdr);
for (i64 i = 0; i < hdr.ncmds; i++) {
LoadCommand &lc = *(LoadCommand *)p;
if (lc.cmd == type)
return &lc;
p += lc.cmdsize;
}
return nullptr;
}
template <typename E>
Subsection<E> *
ObjectFile<E>::find_subsection(Context<E> &ctx, u32 secidx, u32 addr) {
Subsection<E> *ret = nullptr;
for (Subsection<E> *subsec : subsections)
if (subsec->isec.secidx == secidx && subsec->input_addr <= addr)
ret = subsec;
return ret;
}
template <typename E>
Symbol<E> *ObjectFile<E>::find_symbol(Context<E> &ctx, u32 addr) {
for (i64 i = 0; i < mach_syms.size(); i++)
if (MachSym &msym = mach_syms[i]; msym.is_extern && msym.value == addr)
return this->syms[i];
return nullptr;
}
template <typename E>
void ObjectFile<E>::parse_compact_unwind(Context<E> &ctx, MachSection &hdr) {
if (hdr.size % sizeof(CompactUnwindEntry))
Fatal(ctx) << *this << ": invalid __compact_unwind section size";
i64 num_entries = hdr.size / sizeof(CompactUnwindEntry);
unwind_records.reserve(num_entries);
CompactUnwindEntry *src = (CompactUnwindEntry *)(this->mf->data + hdr.offset);
// Read compact unwind entries
for (i64 i = 0; i < num_entries; i++)
unwind_records.emplace_back(src[i].code_len, src[i].encoding);
// Read relocations
MachRel *mach_rels = (MachRel *)(this->mf->data + hdr.reloff);
for (i64 i = 0; i < hdr.nreloc; i++) {
MachRel &r = mach_rels[i];
if (r.offset >= hdr.size)
Fatal(ctx) << *this << ": relocation offset too large: " << i;
i64 idx = r.offset / sizeof(CompactUnwindEntry);
UnwindRecord<E> &dst = unwind_records[idx];
auto error = [&] {
Fatal(ctx) << *this << ": __compact_unwind: unsupported relocation: " << i
<< " " << *this->syms[r.idx];
};
if (r.is_pcrel || r.p2size != 3 || r.type)
error();
switch (r.offset % sizeof(CompactUnwindEntry)) {
case offsetof(CompactUnwindEntry, code_start): {
Subsection<E> *target;
if (r.is_extern)
target = sym_to_subsec[r.idx];
else
target = find_subsection(ctx, r.idx - 1, src[idx].code_start);
if (!target)
error();
dst.subsec = target;
dst.offset = src[idx].code_start - target->input_addr;
break;
}
case offsetof(CompactUnwindEntry, personality):
if (r.is_extern) {
dst.personality = this->syms[r.idx];
} else {
u32 addr = *(ul32 *)((u8 *)this->mf->data + hdr.offset + r.offset);
dst.personality = find_symbol(ctx, addr);
}
if (!dst.personality)
Fatal(ctx) << *this << ": __compact_unwind: unsupported "
<< "personality reference: " << i;
break;
case offsetof(CompactUnwindEntry, lsda): {
u32 addr = *(ul32 *)((u8 *)this->mf->data + hdr.offset + r.offset);
Subsection<E> *target;
if (r.is_extern)
target = sym_to_subsec[r.idx];
else
target = find_subsection(ctx, r.idx - 1, addr);
if (!target)
error();
dst.lsda = target;
dst.lsda_offset = addr - target->input_addr;
break;
}
default:
error();
}
}
for (i64 i = 0; i < num_entries; i++)
if (!unwind_records[i].subsec)
Fatal(ctx) << *this << "_: _compact_unwind: missing relocation at " << i;
// Sort unwind entries by offset
sort(unwind_records, [](const UnwindRecord<E> &a, const UnwindRecord<E> &b) {
return std::tuple(a.subsec->input_addr, a.offset) <
std::tuple(b.subsec->input_addr, b.offset);
});
// Associate unwind entries to subsections
for (i64 i = 0; i < num_entries;) {
Subsection<E> &subsec = *unwind_records[i].subsec;
subsec.unwind_offset = i;
i64 j = i + 1;
while (j < num_entries && unwind_records[j].subsec == &subsec)
j++;
subsec.nunwind = j - i;
i = j;
}
}
// Symbols with higher priorities overwrites symbols with lower priorities.
// Here is the list of priorities, from the highest to the lowest.
//
// 1. Strong defined symbol
// 2. Weak defined symbol
// 3. Strong defined symbol in a DSO/archive
// 4. Weak Defined symbol in a DSO/archive
// 5. Common symbol
// 6. Common symbol in an archive
// 7. Unclaimed (nonexistent) symbol
//
// Ties are broken by file priority.
template <typename E>
static u64 get_rank(InputFile<E> *file, bool is_common, bool is_weak) {
if (is_common) {
assert(!file->is_dylib);
if (!file->is_alive)
return (6 << 24) + file->priority;
return (5 << 24) + file->priority;
}
if (file->is_dylib || !file->is_alive) {
if (is_weak)
return (4 << 24) + file->priority;
return (3 << 24) + file->priority;
}
if (is_weak)
return (2 << 24) + file->priority;
return (1 << 24) + file->priority;
}
template <typename E>
static u64 get_rank(Symbol<E> &sym) {
if (!sym.file)
return 7 << 24;
return get_rank(sym.file, sym.is_common, sym.is_weak);
}
template <typename E>
void ObjectFile<E>::resolve_symbols(Context<E> &ctx) {
auto is_private_extern = [&](MachSym &msym) {
return this->is_hidden || msym.is_private_extern ||
((msym.desc & N_WEAK_REF) && (msym.desc & N_WEAK_DEF));
};
auto merge_scope = [&](Symbol<E> &sym, MachSym &msym) {
// If at least one symbol defines it as an EXTERN symbol,
// the result is an EXTERN symbol instead of PRIVATE_EXTERN,
// so that the symbol is exported.
if (sym.scope == SCOPE_EXTERN)
return SCOPE_EXTERN;
return is_private_extern(msym) ? SCOPE_PRIVATE_EXTERN : SCOPE_EXTERN;
};
for (i64 i = 0; i < this->syms.size(); i++) {
MachSym &msym = mach_syms[i];
if (!msym.is_extern || msym.is_undef())
continue;
Symbol<E> &sym = *this->syms[i];
std::scoped_lock lock(sym.mu);
bool is_weak = (msym.desc & N_WEAK_DEF);
sym.scope = merge_scope(sym, msym);
if (get_rank(this, msym.is_common(), is_weak) < get_rank(sym)) {
sym.file = this;
sym.is_imported = false;
sym.is_weak = is_weak;
sym.no_dead_strip = (msym.desc & N_NO_DEAD_STRIP);
switch (msym.type) {
case N_UNDF:
assert(msym.is_common());
sym.subsec = nullptr;
sym.value = msym.value;
sym.is_common = true;
break;
case N_ABS:
sym.subsec = nullptr;
sym.value = msym.value;
sym.is_common = false;
break;
case N_SECT:
sym.subsec = sym_to_subsec[i];
sym.value = msym.value - sym.subsec->input_addr;
sym.is_common = false;
break;
default:
Fatal(ctx) << sym << ": unknown symbol type: " << (u64)msym.type;
}
}
}
}
template <typename E>
bool ObjectFile<E>::is_objc_object(Context<E> &ctx) {
for (std::unique_ptr<InputSection<E>> &isec : sections)
if (isec)
if (isec->hdr.match("__DATA", "__objc_catlist") ||
(isec->hdr.get_segname() == "__TEXT" &&
isec->hdr.get_sectname().starts_with("__swift")))
return true;
for (i64 i = 0; i < this->syms.size(); i++)
if (!mach_syms[i].is_undef() && mach_syms[i].is_extern &&
this->syms[i]->name.starts_with("_OBJC_CLASS_$_"))
return true;
return false;
}
template <typename E>
void
ObjectFile<E>::mark_live_objects(Context<E> &ctx,
std::function<void(ObjectFile<E> *)> feeder) {
assert(this->is_alive);
for (i64 i = 0; i < this->syms.size(); i++) {
MachSym &msym = mach_syms[i];
if (!msym.is_extern)
continue;
Symbol<E> &sym = *this->syms[i];
std::scoped_lock lock(sym.mu);
if (!sym.file)
continue;
if (msym.is_undef() || (msym.is_common() && !sym.is_common))
if (InputFile<E> *file = sym.file)
if (!file->is_alive.exchange(true) && !file->is_dylib)
feeder((ObjectFile<E> *)file);
}
for (Subsection<E> *subsec : subsections)
for (UnwindRecord<E> &rec : subsec->get_unwind_records())
if (Symbol<E> *sym = rec.personality)
if (InputFile<E> *file = sym->file)
if (!file->is_alive.exchange(true) && !file->is_dylib)
feeder((ObjectFile<E> *)file);
}
template <typename E>
void ObjectFile<E>::convert_common_symbols(Context<E> &ctx) {
for (i64 i = 0; i < this->mach_syms.size(); i++) {
Symbol<E> &sym = *this->syms[i];
MachSym &msym = mach_syms[i];
if (sym.file == this && sym.is_common) {
InputSection<E> *isec = get_common_sec(ctx);
Subsection<E> *subsec = new Subsection<E>{
.isec = *isec,
.input_size = (u32)msym.value,
.p2align = (u8)msym.common_p2align,
};
subsections.emplace_back(subsec);
sym.is_imported = false;
sym.is_weak = false;
sym.no_dead_strip = (msym.desc & N_NO_DEAD_STRIP);
sym.subsec = subsec;
sym.value = 0;
sym.is_common = false;
}
}
}
template <typename E>
void ObjectFile<E>::check_duplicate_symbols(Context<E> &ctx) {
for (i64 i = 0; i < this->mach_syms.size(); i++) {
Symbol<E> *sym = this->syms[i];
MachSym &msym = mach_syms[i];
if (sym && sym->file && sym->file != this && !msym.is_undef() &&
!msym.is_common() && !(msym.desc & N_WEAK_DEF))
Error(ctx) << "duplicate symbol: " << *this << ": " << *sym->file
<< ": " << *sym;
}
}
template <typename E>
InputSection<E> *ObjectFile<E>::get_common_sec(Context<E> &ctx) {
if (!common_sec) {
MachSection *hdr = new MachSection;
common_hdr.reset(hdr);
memset(hdr, 0, sizeof(*hdr));
hdr->set_segname("__DATA");
hdr->set_sectname("__common");
hdr->type = S_ZEROFILL;
common_sec = new InputSection<E>(ctx, *this, *hdr, sections.size());
sections.emplace_back(common_sec);
}
return common_sec;
}
template <typename E>
void ObjectFile<E>::parse_lto_symbols(Context<E> &ctx) {
i64 nsyms = ctx.lto.module_get_num_symbols(this->lto_module);
this->syms.reserve(nsyms);
this->mach_syms2.reserve(nsyms);
for (i64 i = 0; i < nsyms; i++) {
std::string_view name = ctx.lto.module_get_symbol_name(this->lto_module, i);
this->syms.push_back(get_symbol(ctx, name));
u32 attr = ctx.lto.module_get_symbol_attribute(this->lto_module, i);
MachSym msym = {};
switch (attr & LTO_SYMBOL_DEFINITION_MASK) {
case LTO_SYMBOL_DEFINITION_REGULAR:
case LTO_SYMBOL_DEFINITION_TENTATIVE:
case LTO_SYMBOL_DEFINITION_WEAK:
msym.type = N_ABS;
break;
case LTO_SYMBOL_DEFINITION_UNDEFINED:
case LTO_SYMBOL_DEFINITION_WEAKUNDEF:
msym.type = N_UNDF;
break;
default:
unreachable();
}
switch (attr & LTO_SYMBOL_SCOPE_MASK) {
case 0:
case LTO_SYMBOL_SCOPE_INTERNAL:
case LTO_SYMBOL_SCOPE_HIDDEN:
break;
case LTO_SYMBOL_SCOPE_DEFAULT:
case LTO_SYMBOL_SCOPE_PROTECTED:
case LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN:
msym.is_extern = true;
break;
default:
unreachable();
}
mach_syms2.push_back(msym);
}
mach_syms = mach_syms2;
}
template <typename E>
std::string_view ObjectFile<E>::get_linker_optimization_hints(Context<E> &ctx) {
LinkEditDataCommand *cmd =
(LinkEditDataCommand *)find_load_command(ctx, LC_LINKER_OPTIMIZATION_HINT);
if (cmd)
return {(char *)this->mf->data + cmd->dataoff, cmd->datasize};
return {};
}
template <typename E>
DylibFile<E>::DylibFile(Context<E> &ctx, MappedFile<Context<E>> *mf)
: InputFile<E>(mf) {
this->is_dylib = true;
this->is_alive = (ctx.needed_l || !ctx.arg.dead_strip_dylibs);
this->is_weak = ctx.weak_l;
this->is_reexported = ctx.reexport_l;
ctx.dylib_pool.emplace_back(this);
}
template <typename E>
DylibFile<E> *DylibFile<E>::create(Context<E> &ctx, MappedFile<Context<E>> *mf) {
DylibFile<E> *file = new DylibFile<E>(ctx, mf);
file->parse(ctx);
return file;
}
template <typename E>
static MappedFile<Context<E>> *
find_external_lib(Context<E> &ctx, std::string_view parent, std::string path) {
if (!path.starts_with('/'))
return MappedFile<Context<E>>::open(ctx, path);
for (const std::string &root : ctx.arg.syslibroot) {
if (path.ends_with(".tbd")) {
if (auto *file = MappedFile<Context<E>>::open(ctx, root + path))
return file;
continue;
}
if (path.ends_with(".dylib")) {
std::string stem(path.substr(0, path.size() - 6));
if (auto *file = MappedFile<Context<E>>::open(ctx, root + stem + ".tbd"))
return file;
if (auto *file = MappedFile<Context<E>>::open(ctx, root + path))
return file;
}
for (std::string extn : {".tbd", ".dylib"})
if (auto *file = MappedFile<Context<E>>::open(ctx, root + path + extn))
return file;
}
return nullptr;
}
template <typename E>
void DylibFile<E>::parse(Context<E> &ctx) {
switch (get_file_type(this->mf)) {
case FileType::TAPI:
parse_tapi(ctx);
break;
case FileType::MACH_DYLIB:
parse_dylib(ctx);
break;
case FileType::MACH_EXE:
parse_dylib(ctx);
dylib_idx = BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE;
break;
default:
Fatal(ctx) << *this << ": is not a dylib";
}
// Read reexported libraries if any
for (std::string_view path : reexported_libs) {
MappedFile<Context<E>> *mf =
find_external_lib(ctx, install_name, std::string(path));
if (!mf)
Fatal(ctx) << install_name << ": cannot open reexported library " << path;
DylibFile<E> *child = DylibFile<E>::create(ctx, mf);
exports.merge(child->exports);
weak_exports.merge(child->weak_exports);
}
// Initialize syms and is_weak_symbols vectors
for (std::string_view s : exports) {
this->syms.push_back(get_symbol(ctx, s));
is_weak_symbol.push_back(false);
}
for (std::string_view s : weak_exports) {
if (!exports.contains(s)) {
this->syms.push_back(get_symbol(ctx, s));
is_weak_symbol.push_back(true);
}
}
}
template <typename E>
void DylibFile<E>::read_trie(Context<E> &ctx, u8 *start, i64 offset,
const std::string &prefix) {
u8 *buf = start + offset;
if (*buf) {
read_uleb(buf); // size
i64 flags = read_uleb(buf) & ~EXPORT_SYMBOL_FLAGS_KIND_MASK;
read_uleb(buf); // addr
std::string_view name = save_string(ctx, prefix);
if (flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION)
weak_exports.insert(name);
else
exports.insert(name);
if (flags & EXPORT_SYMBOL_FLAGS_REEXPORT)
read_uleb(buf); // skip a library ordinal
} else {
buf++;
}
i64 nchild = *buf++;
for (i64 i = 0; i < nchild; i++) {
std::string suffix((char *)buf);
buf += suffix.size() + 1;
i64 off = read_uleb(buf);
read_trie(ctx, start, off, prefix + suffix);
}
}
template <typename E>
void DylibFile<E>::parse_tapi(Context<E> &ctx) {
TextDylib tbd = parse_tbd(ctx, this->mf);
install_name = tbd.install_name;
reexported_libs = std::move(tbd.reexported_libs);
exports = std::move(tbd.exports);
weak_exports = std::move(tbd.weak_exports);
}
template <typename E>
void DylibFile<E>::parse_dylib(Context<E> &ctx) {
MachHeader &hdr = *(MachHeader *)this->mf->data;
u8 *p = this->mf->data + sizeof(hdr);
if (ctx.arg.application_extension && !(hdr.flags & MH_APP_EXTENSION_SAFE))
Warn(ctx) << "linking against a dylib which is not safe for use in "
<< "application extensions: " << *this;
for (i64 i = 0; i < hdr.ncmds; i++) {
LoadCommand &lc = *(LoadCommand *)p;
switch (lc.cmd) {
case LC_ID_DYLIB: {
DylibCommand &cmd = *(DylibCommand *)p;
install_name = (char *)p + cmd.nameoff;
break;
}
case LC_DYLD_INFO_ONLY: {
DyldInfoCommand &cmd = *(DyldInfoCommand *)p;
if (cmd.export_off)
read_trie(ctx, this->mf->data + cmd.export_off);
break;
}
case LC_DYLD_EXPORTS_TRIE: {
LinkEditDataCommand &cmd = *(LinkEditDataCommand *)p;
read_trie(ctx, this->mf->data + cmd.dataoff);
break;
}
case LC_REEXPORT_DYLIB:
if (!(hdr.flags & MH_NO_REEXPORTED_DYLIBS)) {
DylibCommand &cmd = *(DylibCommand *)p;
reexported_libs.push_back((char *)p + cmd.nameoff);
}
break;
}
p += lc.cmdsize;
}
}
template <typename E>
void DylibFile<E>::resolve_symbols(Context<E> &ctx) {
for (i64 i = 0; i < this->syms.size(); i++) {
Symbol<E> &sym = *this->syms[i];
std::scoped_lock lock(sym.mu);
if (get_rank(this, false, false) < get_rank(sym)) {
sym.file = this;
sym.scope = SCOPE_LOCAL;
sym.is_imported = true;
sym.is_weak = (this->is_weak || is_weak_symbol[i]);
sym.no_dead_strip = false;
sym.subsec = nullptr;
sym.value = 0;
sym.is_common = false;
}
}
}
#define INSTANTIATE(E) \
template class InputFile<E>; \
template class ObjectFile<E>; \
template class DylibFile<E>; \
template std::ostream &operator<<(std::ostream &, const InputFile<E> &)
INSTANTIATE_ALL;
} // namespace mold::macho
+30 -18
View File
@@ -2,20 +2,36 @@
namespace mold::macho {
template <typename E>
OutputSection<E> &get_output_section(Context<E> &ctx, const MachSection &hdr) {
static std::unordered_set<std::string_view> set = {
"__got", "__auth_got", "__auth_ptr", "__nl_symbol_ptr", "__const",
"__cfstring", "__mod_init_func", "__mod_term_func", "__objc_classlist",
"__objc_nlclslist", "__objc_catlist", "__objc_nlcatlist", "__objc_protolist",
};
std::string_view seg = hdr.get_segname();
std::string_view sect = hdr.get_sectname();
if (seg == "__DATA" && set.contains(sect)) {
seg = "__DATA_CONST";
} else if (seg == "__TEXT" && sect == "__StaticInit") {
sect = "__text";
}
return *OutputSection<E>::get_instance(ctx, seg, sect);
}
template <typename E>
InputSection<E>::InputSection(Context<E> &ctx, ObjectFile<E> &file,
const MachSection &hdr)
: file(file), hdr(hdr),
osec(*OutputSection<E>::get_instance(ctx, hdr.get_segname(),
hdr.get_sectname())) {
const MachSection &hdr, u32 secidx)
: file(file), hdr(hdr), secidx(secidx), osec(get_output_section(ctx, hdr)) {
if (hdr.type != S_ZEROFILL)
contents = file.mf->get_contents().substr(hdr.offset, hdr.size);
}
template <typename E>
void InputSection<E>::parse_relocations(Context<E> &ctx) {
rels.reserve(hdr.nreloc);
// Parse mach-o relocations to fill `rels` vector
rels = read_relocations(ctx, file, hdr);
@@ -24,18 +40,15 @@ void InputSection<E>::parse_relocations(Context<E> &ctx) {
return a.offset < b.offset;
});
// Find subsections for this section
auto begin = std::lower_bound(
file.subsections.begin(), file.subsections.end(), hdr.addr,
[](std::unique_ptr<Subsection<E>> &subsec, u32 addr) {
// Find subsections this relocation section refers to
auto cmp = [](Subsection<E> *subsec, u32 addr) {
return subsec->input_addr < addr;
});
};
auto end = std::lower_bound(
begin, file.subsections.end(), hdr.addr + hdr.size,
[](std::unique_ptr<Subsection<E>> &subsec, u32 addr) {
return subsec->input_addr < addr;
});
auto begin = std::lower_bound(file.subsections.begin(),
file.subsections.end(), hdr.addr, cmp);
auto end = std::lower_bound(begin, file.subsections.end(),
hdr.addr + hdr.size, cmp);
// Assign each subsection a group of relocations
i64 i = 0;
@@ -54,7 +67,6 @@ void InputSection<E>::parse_relocations(Context<E> &ctx) {
#define INSTANTIATE(E) \
template class InputSection<E>
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
INSTANTIATE_ALL;
} // namespace mold::macho
+164
View File
@@ -0,0 +1,164 @@
#include "lto.h"
#include "mold.h"
#include <algorithm>
#include <cstdio>
#include <dlfcn.h>
#include <unistd.h>
namespace mold::macho {
LTOPlugin::~LTOPlugin() {
if (dlopen_handle)
dlclose(dlopen_handle);
}
template <typename E>
static void do_load_plugin(Context<E> &ctx) {
void *handle = dlopen(ctx.arg.lto_library.c_str(), RTLD_NOW | RTLD_GLOBAL);
if (!handle)
Fatal(ctx) << "could not open plugin file: " << dlerror();
ctx.lto.dlopen_handle = handle;
// I don't want to use a macro, but I couldn't find an easy way to
// write the same thing without a macro.
#define DLSYM(x) ctx.lto.x = (decltype(ctx.lto.x))dlsym(handle, "lto_" #x)
DLSYM(get_version);
DLSYM(get_error_message);
DLSYM(module_is_object_file);
DLSYM(module_is_object_file_for_target);
DLSYM(module_has_objc_category);
DLSYM(module_is_object_file_in_memory);
DLSYM(module_is_object_file_in_memory_for_target);
DLSYM(module_create);
DLSYM(module_create_from_memory);
DLSYM(module_create_from_memory_with_path);
DLSYM(module_create_in_local_context);
DLSYM(module_create_in_codegen_context);
DLSYM(module_create_from_fd);
DLSYM(module_create_from_fd_at_offset);
DLSYM(module_dispose);
DLSYM(module_get_target_triple);
DLSYM(module_set_target_triple);
DLSYM(module_get_num_symbols);
DLSYM(module_get_symbol_name);
DLSYM(module_get_symbol_attribute);
DLSYM(module_get_linkeropts);
DLSYM(module_get_macho_cputype);
DLSYM(module_has_ctor_dtor);
DLSYM(codegen_set_diagnostic_handler);
DLSYM(codegen_create);
DLSYM(codegen_create_in_local_context);
DLSYM(codegen_dispose);
DLSYM(codegen_add_module);
DLSYM(codegen_set_module);
DLSYM(codegen_set_debug_model);
DLSYM(codegen_set_pic_model);
DLSYM(codegen_set_cpu);
DLSYM(codegen_set_assembler_path);
DLSYM(codegen_set_assembler_args);
DLSYM(codegen_add_must_preserve_symbol);
DLSYM(codegen_write_merged_modules);
DLSYM(codegen_compile);
DLSYM(codegen_compile_to_file);
DLSYM(codegen_optimize);
DLSYM(codegen_compile_optimized);
DLSYM(api_version);
DLSYM(set_debug_options);
DLSYM(codegen_debug_options);
DLSYM(codegen_debug_options_array);
DLSYM(initialize_disassembler);
DLSYM(codegen_set_should_internalize);
DLSYM(codegen_set_should_embed_uselists);
#undef DLSYM
}
template <typename E>
void load_lto_plugin(Context<E> &ctx) {
std::call_once(ctx.lto_plugin_loaded, [&]() { do_load_plugin(ctx); });
}
template <typename E>
void do_lto(Context<E> &ctx) {
LTOCodeGen *cg = ctx.lto.codegen_create();
// Add bitcode files to CodeGen.
for (const std::string &opt : ctx.arg.mllvm)
ctx.lto.codegen_debug_options(cg, opt.c_str());
for (ObjectFile<E> *file : ctx.objs)
if (file->lto_module)
ctx.lto.codegen_add_module(cg, file->lto_module);
// Mark symbols that have to be preserved. All symbols that are not
// marked here may be internalized and deleted as an extenrally-
// visible symbol.
if (ctx.output_type == MH_DYLIB || ctx.arg.export_dynamic) {
for (ObjectFile<E> *file : ctx.objs) {
if (!file->lto_module) {
for (i64 i = 0; i < file->mach_syms.size(); i++) {
MachSym &msym = file->mach_syms[i];
Symbol<E> &sym = *file->syms[i];
if (msym.is_undef() && !sym.file->is_dylib &&
((ObjectFile<E> *)sym.file)->lto_module)
ctx.lto.codegen_add_must_preserve_symbol(cg, sym.name.data());
}
}
}
for (ObjectFile<E> *file : ctx.objs)
if (file->lto_module)
for (Symbol<E> *sym : file->syms)
if (sym->file == file && sym->scope != SCOPE_LOCAL)
ctx.lto.codegen_add_must_preserve_symbol(cg, sym->name.data());
}
if (ctx.arg.entry->file)
ctx.lto.codegen_add_must_preserve_symbol(cg, ctx.arg.entry->name.data());
// Run the compiler backend to do LTO.
size_t size;
u8 *data = (u8 *)ctx.lto.codegen_compile(cg, &size);
if (!data)
Fatal(ctx) << "lto_codegen_compile failed: " << ctx.lto.get_error_message();
if (!ctx.arg.object_path_lto.empty()) {
FILE *out = fopen(ctx.arg.object_path_lto.c_str(), "w");
if (!out)
Fatal(ctx) << "-object_path_lto: cannot open " << ctx.arg.object_path_lto
<< ": " << errno_string();
fwrite(data, size, 1, out);
fclose(out);
}
// Remove bitcode object files from ctx.objs.
for (ObjectFile<E> *file : ctx.objs) {
if (file->lto_module) {
file->clear_symbols();
file->is_alive = false;
}
}
// Add a result of LTO as a new object file.
MappedFile<Context<E>> *mf = new MappedFile<Context<E>>;
mf->name = "<LTO>";
mf->data = data;
mf->size = size;
ctx.mf_pool.emplace_back(mf);
ObjectFile<E> *obj = ObjectFile<E>::create(ctx, mf, "");
obj->parse(ctx);
obj->is_alive = true;
obj->priority = 100; // regular file starts with priority 10,000
obj->resolve_symbols(ctx);
ctx.objs.push_back(obj);
}
#define INSTANTIATE(E) \
template void load_lto_plugin(Context<E> &); \
template void do_lto(Context<E> &);
INSTANTIATE_ALL;
} // namespace mold::macho
+26
View File
@@ -0,0 +1,26 @@
#ifdef _WIN32
#include "lto.h"
#include "mold.h"
namespace mold::macho {
LTOPlugin::~LTOPlugin() {}
template <typename E>
void load_lto_plugin(Context<E> &ctx) {
Fatal(ctx) << "LTO is not supported on Windows";
}
template <typename E>
void do_lto(Context<E> &ctx) {}
#define INSTANTIATE(E) \
template void load_lto_plugin(Context<E> &); \
template void do_lto(Context<E> &);
INSTANTIATE_ALL;
} // namespace mold::macho
#endif
+176
View File
@@ -0,0 +1,176 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <sys/types.h>
namespace mold::macho {
enum LTOSymbolAttributes {
LTO_SYMBOL_ALIGNMENT_MASK = 0x0000001F,
LTO_SYMBOL_PERMISSIONS_MASK = 0x000000E0,
LTO_SYMBOL_PERMISSIONS_CODE = 0x000000A0,
LTO_SYMBOL_PERMISSIONS_DATA = 0x000000C0,
LTO_SYMBOL_PERMISSIONS_RODATA = 0x00000080,
LTO_SYMBOL_DEFINITION_MASK = 0x00000700,
LTO_SYMBOL_DEFINITION_REGULAR = 0x00000100,
LTO_SYMBOL_DEFINITION_TENTATIVE = 0x00000200,
LTO_SYMBOL_DEFINITION_WEAK = 0x00000300,
LTO_SYMBOL_DEFINITION_UNDEFINED = 0x00000400,
LTO_SYMBOL_DEFINITION_WEAKUNDEF = 0x00000500,
LTO_SYMBOL_SCOPE_MASK = 0x00003800,
LTO_SYMBOL_SCOPE_INTERNAL = 0x00000800,
LTO_SYMBOL_SCOPE_HIDDEN = 0x00001000,
LTO_SYMBOL_SCOPE_DEFAULT = 0x00001800,
LTO_SYMBOL_SCOPE_PROTECTED = 0x00002000,
LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN = 0x00002800,
LTO_SYMBOL_COMDAT = 0x00004000,
LTO_SYMBOL_ALIAS = 0x00008000,
};
enum LTODebugModel {
LTO_DEBUG_MODEL_NONE = 0,
LTO_DEBUG_MODEL_DWARF = 1,
};
enum LTOCodegenModel {
LTO_CODEGEN_PIC_MODEL_STATIC = 0,
LTO_CODEGEN_PIC_MODEL_DYNAMIC = 1,
LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC = 2,
LTO_CODEGEN_PIC_MODEL_DEFAULT = 3,
};
enum LTOCodegenDiagnosticSeverity {
LTO_DS_ERROR = 0,
LTO_DS_WARNING = 1,
LTO_DS_REMARK = 3,
LTO_DS_NOTE = 2,
};
struct LTOObjectBuffer {
const char *buffer;
size_t size;
};
using LTOModule = void;
using LTOCodeGen = void;
typedef void LTODiagnosticHandler(LTOCodegenDiagnosticSeverity severity,
const char *diag, void *ctxt);
struct LTOPlugin {
void *dlopen_handle = nullptr;
~LTOPlugin();
char (*get_version)();
char (*get_error_message)();
bool (*module_is_object_file)(const char *path);
bool (*module_is_object_file_for_target)(const char *path,
const char *target_triple_prefix);
bool (*module_has_objc_category)(const void *mem, size_t length);
bool (*module_is_object_file_in_memory)(const void *mem, size_t length);
bool (*module_is_object_file_in_memory_for_target)(
const void *mem, size_t length, const char *target_triple_prefix);
LTOModule *(*module_create)(const char *path);
LTOModule *(*module_create_from_memory)(const void *mem, size_t length);
LTOModule *(*module_create_from_memory_with_path)(const void *mem,
size_t length,
const char *path);
LTOModule *(*module_create_in_local_context)(const void *mem, size_t length,
const char *path);
LTOModule *(*module_create_in_codegen_context)(const void *mem, size_t length,
const char *path,
LTOCodeGen *cg);
LTOModule *(*module_create_from_fd)(int fd, const char *path,
size_t file_size);
LTOModule *(*module_create_from_fd_at_offset)(int fd, const char *path,
size_t file_size,
size_t map_size, uint64_t offset);
void (*module_dispose)(LTOModule *mod);
const char *(*module_get_target_triple)(LTOModule *mod);
void (*module_set_target_triple)(LTOModule *mod, const char *triple);
unsigned (*module_get_num_symbols)(LTOModule *mod);
const char *(*module_get_symbol_name)(LTOModule *mod, unsigned index);
uint32_t (*module_get_symbol_attribute)(LTOModule *mod, unsigned index);
const char *(*module_get_linkeropts)(LTOModule *mod);
bool (*module_get_macho_cputype)(LTOModule *mod, unsigned *out_cputype,
unsigned *out_cpusubtype);
bool (*module_has_ctor_dtor)(LTOModule *mod);
void (*codegen_set_diagnostic_handler)(LTOCodeGen *cg, LTODiagnosticHandler,
void *);
LTOCodeGen *(*codegen_create)();
LTOCodeGen *(*codegen_create_in_local_context)();
void (*codegen_dispose)(LTOCodeGen *cg);
bool (*codegen_add_module)(LTOCodeGen *cg, LTOModule *mod);
void (*codegen_set_module)(LTOCodeGen *cg, LTOModule *mod);
bool (*codegen_set_debug_model)(LTOCodeGen *cg, LTODebugModel);
bool (*codegen_set_pic_model)(LTOCodeGen *cg, LTOCodegenModel);
void (*codegen_set_cpu)(LTOCodeGen *cg, const char *cpu);
void (*codegen_set_assembler_path)(LTOCodeGen *cg, const char *path);
void (*codegen_set_assembler_args)(LTOCodeGen *cg, const char **args,
int nargs);
void (*codegen_add_must_preserve_symbol)(LTOCodeGen *cg, const char *symbol);
bool (*codegen_write_merged_modules)(LTOCodeGen *cg, const char *path);
const void *(*codegen_compile)(LTOCodeGen *cg, size_t *length);
bool (*codegen_compile_to_file)(LTOCodeGen *cg, const char **name);
bool (*codegen_optimize)(LTOCodeGen *cg);
const void *(*codegen_compile_optimized)(LTOCodeGen *cg, size_t *length);
unsigned (*api_version)();
void (*set_debug_options)(const char *const *options, int number);
void (*codegen_debug_options)(LTOCodeGen *cg, const char *);
void (*codegen_debug_options_array)(LTOCodeGen *cg, const char *const *,
int number);
void (*initialize_disassembler)();
void (*codegen_set_should_internalize)(LTOCodeGen *cg,
bool should_internalize);
void (*codegen_set_should_embed_uselists)(LTOCodeGen *cg,
bool should_embed_use_lists);
};
} // namespace mold::macho
+282 -194
View File
@@ -1,10 +1,11 @@
#pragma once
#include "../big-endian.h"
#include "../inttypes.h"
#include <cassert>
#include <cstdint>
#include <cstring>
#include <string>
#include <string_view>
namespace mold::macho {
@@ -19,6 +20,12 @@ typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
struct ARM64;
struct X86_64;
template <typename E>
std::string rel_to_string(u8 r_type);
static constexpr u32 FAT_MAGIC = 0xcafebabe;
static constexpr u32 MH_OBJECT = 0x1;
@@ -155,13 +162,13 @@ static constexpr u32 S_THREAD_LOCAL_VARIABLE_POINTERS = 0x14;
static constexpr u32 S_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15;
static constexpr u32 S_INIT_FUNC_OFFSETS = 0x16;
static constexpr u32 S_ATTR_LOC_RELOC = 0x000001;
static constexpr u32 S_ATTR_EXT_RELOC = 0x000002;
static constexpr u32 S_ATTR_SOME_INSTRUCTIONS = 0x000004;
static constexpr u32 S_ATTR_LOC_RELOC = 0x1;
static constexpr u32 S_ATTR_EXT_RELOC = 0x2;
static constexpr u32 S_ATTR_SOME_INSTRUCTIONS = 0x4;
static constexpr u32 S_ATTR_DEBUG = 0x020000;
static constexpr u32 S_ATTR_SELF_MODIFYING_CODE = 0x040000;
static constexpr u32 S_ATTR_LIVE_SUPPORT = 0x080000;
static constexpr u32 S_ATTR_DEBUG = 0x20000;
static constexpr u32 S_ATTR_SELF_MODIFYING_CODE = 0x40000;
static constexpr u32 S_ATTR_LIVE_SUPPORT = 0x80000;
static constexpr u32 S_ATTR_NO_DEAD_STRIP = 0x100000;
static constexpr u32 S_ATTR_STRIP_STATIC_SYMS = 0x200000;
static constexpr u32 S_ATTR_NO_TOC = 0x400000;
@@ -193,10 +200,10 @@ static constexpr u32 BIND_TYPE_POINTER = 1;
static constexpr u32 BIND_TYPE_TEXT_ABSOLUTE32 = 2;
static constexpr u32 BIND_TYPE_TEXT_PCREL32 = 3;
static constexpr u32 BIND_SPECIAL_DYLIB_SELF = 0;
static constexpr u32 BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE = -1;
static constexpr u32 BIND_SPECIAL_DYLIB_FLAT_LOOKUP = -2;
static constexpr u32 BIND_SPECIAL_DYLIB_WEAK_LOOKUP = -3;
static constexpr i32 BIND_SPECIAL_DYLIB_SELF = 0;
static constexpr i32 BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE = -1;
static constexpr i32 BIND_SPECIAL_DYLIB_FLAT_LOOKUP = -2;
static constexpr i32 BIND_SPECIAL_DYLIB_WEAK_LOOKUP = -3;
static constexpr u32 BIND_SYMBOL_FLAGS_WEAK_IMPORT = 0x1;
static constexpr u32 BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION = 0x8;
@@ -282,6 +289,11 @@ static constexpr u32 REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY = 5;
static constexpr u32 REFERENCED_DYNAMICALLY = 0x0010;
static constexpr u32 SELF_LIBRARY_ORDINAL = 0x0;
static constexpr u32 MAX_LIBRARY_ORDINAL = 0xfd;
static constexpr u32 DYNAMIC_LOOKUP_ORDINAL = 0xfe;
static constexpr u32 EXECUTABLE_ORDINAL = 0xff;
static constexpr u32 N_NO_DEAD_STRIP = 0x0020;
static constexpr u32 N_DESC_DISCARDED = 0x0020;
static constexpr u32 N_WEAK_REF = 0x0040;
@@ -305,6 +317,23 @@ static constexpr u32 PLATFORM_DRIVERKIT = 10;
static constexpr u32 TOOL_CLANG = 1;
static constexpr u32 TOOL_SWIFT = 2;
static constexpr u32 TOOL_LD = 3;
static constexpr u32 TOOL_MOLD = 54321; // Randomly chosen!
static constexpr u32 OBJC_IMAGE_SUPPORTS_GC = 1 << 1;
static constexpr u32 OBJC_IMAGE_REQUIRES_GC = 1 << 2;
static constexpr u32 OBJC_IMAGE_OPTIMIZED_BY_DYLD = 1 << 3;
static constexpr u32 OBJC_IMAGE_SUPPORTS_COMPACTION = 1 << 4;
static constexpr u32 OBJC_IMAGE_IS_SIMULATED = 1 << 5;
static constexpr u32 OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES = 1 << 6;
static constexpr u32 LOH_ARM64_ADRP_ADRP = 1;
static constexpr u32 LOH_ARM64_ADRP_LDR = 2;
static constexpr u32 LOH_ARM64_ADRP_ADD_LDR = 3;
static constexpr u32 LOH_ARM64_ADRP_LDR_GOT_LDR = 4;
static constexpr u32 LOH_ARM64_ADRP_ADD_STR = 5;
static constexpr u32 LOH_ARM64_ADRP_LDR_GOT_STR = 6;
static constexpr u32 LOH_ARM64_ADRP_ADD = 7;
static constexpr u32 LOH_ARM64_ADRP_LDR_GOT = 8;
static constexpr u32 ARM64_RELOC_UNSIGNED = 0;
static constexpr u32 ARM64_RELOC_SUBTRACTOR = 1;
@@ -318,6 +347,24 @@ static constexpr u32 ARM64_RELOC_TLVP_LOAD_PAGE21 = 8;
static constexpr u32 ARM64_RELOC_TLVP_LOAD_PAGEOFF12 = 9;
static constexpr u32 ARM64_RELOC_ADDEND = 10;
template <>
inline std::string rel_to_string<ARM64>(u8 type) {
switch (type) {
case ARM64_RELOC_UNSIGNED: return "ARM64_RELOC_UNSIGNED";
case ARM64_RELOC_SUBTRACTOR: return "ARM64_RELOC_SUBTRACTOR";
case ARM64_RELOC_BRANCH26: return "ARM64_RELOC_BRANCH26";
case ARM64_RELOC_PAGE21: return "ARM64_RELOC_PAGE21";
case ARM64_RELOC_PAGEOFF12: return "ARM64_RELOC_PAGEOFF12";
case ARM64_RELOC_GOT_LOAD_PAGE21: return "ARM64_RELOC_GOT_LOAD_PAGE21";
case ARM64_RELOC_GOT_LOAD_PAGEOFF12: return "ARM64_RELOC_GOT_LOAD_PAGEOFF12";
case ARM64_RELOC_POINTER_TO_GOT: return "ARM64_RELOC_POINTER_TO_GOT";
case ARM64_RELOC_TLVP_LOAD_PAGE21: return "ARM64_RELOC_TLVP_LOAD_PAGE21";
case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: return "ARM64_RELOC_TLVP_LOAD_PAGEOFF12";
case ARM64_RELOC_ADDEND: return "ARM64_RELOC_ADDEND";
}
return "unknown (" + std::to_string(type) + ")";
}
static constexpr u32 X86_64_RELOC_UNSIGNED = 0;
static constexpr u32 X86_64_RELOC_SIGNED = 1;
static constexpr u32 X86_64_RELOC_BRANCH = 2;
@@ -329,33 +376,50 @@ static constexpr u32 X86_64_RELOC_SIGNED_2 = 7;
static constexpr u32 X86_64_RELOC_SIGNED_4 = 8;
static constexpr u32 X86_64_RELOC_TLV = 9;
template <>
inline std::string rel_to_string<X86_64>(u8 type) {
switch (type) {
case X86_64_RELOC_UNSIGNED: return "X86_64_RELOC_UNSIGNED";
case X86_64_RELOC_SIGNED: return "X86_64_RELOC_SIGNED";
case X86_64_RELOC_BRANCH: return "X86_64_RELOC_BRANCH";
case X86_64_RELOC_GOT_LOAD: return "X86_64_RELOC_GOT_LOAD";
case X86_64_RELOC_GOT: return "X86_64_RELOC_GOT";
case X86_64_RELOC_SUBTRACTOR: return "X86_64_RELOC_SUBTRACTOR";
case X86_64_RELOC_SIGNED_1: return "X86_64_RELOC_SIGNED_1";
case X86_64_RELOC_SIGNED_2: return "X86_64_RELOC_SIGNED_2";
case X86_64_RELOC_SIGNED_4: return "X86_64_RELOC_SIGNED_4";
case X86_64_RELOC_TLV: return "X86_64_RELOC_TLV";
}
return "unknown (" + std::to_string(type) + ")";
}
struct FatHeader {
ubig32 magic;
ubig32 nfat_arch;
ub32 magic;
ub32 nfat_arch;
};
struct FatArch {
ubig32 cputype;
ubig32 cpusubtype;
ubig32 offset;
ubig32 size;
ubig32 align;
ub32 cputype;
ub32 cpusubtype;
ub32 offset;
ub32 size;
ub32 align;
};
struct MachHeader {
u32 magic;
u32 cputype;
u32 cpusubtype;
u32 filetype;
u32 ncmds;
u32 sizeofcmds;
u32 flags;
u32 reserved;
ul32 magic;
ul32 cputype;
ul32 cpusubtype;
ul32 filetype;
ul32 ncmds;
ul32 sizeofcmds;
ul32 flags;
ul32 reserved;
};
struct LoadCommand {
u32 cmd;
u32 cmdsize;
ul32 cmd;
ul32 cmdsize;
};
struct SegmentCommand {
@@ -363,17 +427,17 @@ struct SegmentCommand {
return {segname, strnlen(segname, sizeof(segname))};
}
u32 cmd;
u32 cmdsize;
ul32 cmd;
ul32 cmdsize;
char segname[16];
u64 vmaddr;
u64 vmsize;
u64 fileoff;
u64 filesize;
u32 maxprot;
u32 initprot;
u32 nsects;
u32 flags;
ul64 vmaddr;
ul64 vmsize;
ul64 fileoff;
ul64 filesize;
ul32 maxprot;
ul32 initprot;
ul32 nsects;
ul32 flags;
};
struct MachSection {
@@ -401,138 +465,144 @@ struct MachSection {
char sectname[16];
char segname[16];
u64 addr;
u64 size;
u32 offset;
u32 p2align;
u32 reloff;
u32 nreloc;
u32 type : 8;
u32 attr : 24;
u32 reserved1;
u32 reserved2;
u32 reserved3;
ul64 addr;
ul64 size;
ul32 offset;
ul32 p2align;
ul32 reloff;
ul32 nreloc;
u8 type;
ul24 attr;
ul32 reserved1;
ul32 reserved2;
ul32 reserved3;
};
struct DylibCommand {
u32 cmd;
u32 cmdsize;
u32 nameoff;
u32 timestamp;
u32 current_version;
u32 compatibility_version;
ul32 cmd;
ul32 cmdsize;
ul32 nameoff;
ul32 timestamp;
ul32 current_version;
ul32 compatibility_version;
};
struct DylinkerCommand {
u32 cmd;
u32 cmdsize;
u32 nameoff;
ul32 cmd;
ul32 cmdsize;
ul32 nameoff;
};
struct SymtabCommand {
u32 cmd;
u32 cmdsize;
u32 symoff;
u32 nsyms;
u32 stroff;
u32 strsize;
ul32 cmd;
ul32 cmdsize;
ul32 symoff;
ul32 nsyms;
ul32 stroff;
ul32 strsize;
};
struct DysymtabCommand {
u32 cmd;
u32 cmdsize;
u32 ilocalsym;
u32 nlocalsym;
u32 iextdefsym;
u32 nextdefsym;
u32 iundefsym;
u32 nundefsym;
u32 tocoff;
u32 ntoc;
u32 modtaboff;
u32 nmodtab;
u32 extrefsymoff;
u32 nextrefsyms;
u32 indirectsymoff;
u32 nindirectsyms;
u32 extreloff;
u32 nextrel;
u32 locreloff;
u32 nlocrel;
ul32 cmd;
ul32 cmdsize;
ul32 ilocalsym;
ul32 nlocalsym;
ul32 iextdefsym;
ul32 nextdefsym;
ul32 iundefsym;
ul32 nundefsym;
ul32 tocoff;
ul32 ntoc;
ul32 modtaboff;
ul32 nmodtab;
ul32 extrefsymoff;
ul32 nextrefsyms;
ul32 indirectsymoff;
ul32 nindirectsyms;
ul32 extreloff;
ul32 nextrel;
ul32 locreloff;
ul32 nlocrel;
};
struct VersionMinCommand {
u32 cmd;
u32 cmdsize;
u32 version;
u32 sdk;
ul32 cmd;
ul32 cmdsize;
ul32 version;
ul32 sdk;
};
struct DyldInfoCommand {
u32 cmd;
u32 cmdsize;
u32 rebase_off;
u32 rebase_size;
u32 bind_off;
u32 bind_size;
u32 weak_bind_off;
u32 weak_bind_size;
u32 lazy_bind_off;
u32 lazy_bind_size;
u32 export_off;
u32 export_size;
ul32 cmd;
ul32 cmdsize;
ul32 rebase_off;
ul32 rebase_size;
ul32 bind_off;
ul32 bind_size;
ul32 weak_bind_off;
ul32 weak_bind_size;
ul32 lazy_bind_off;
ul32 lazy_bind_size;
ul32 export_off;
ul32 export_size;
};
struct UUIDCommand {
u32 cmd;
u32 cmdsize;
ul32 cmd;
ul32 cmdsize;
u8 uuid[16];
};
struct RpathCommand {
u32 cmd;
u32 cmdsize;
u32 path_off;
ul32 cmd;
ul32 cmdsize;
ul32 path_off;
};
struct LinkEditDataCommand {
u32 cmd;
u32 cmdsize;
u32 dataoff;
u32 datasize;
ul32 cmd;
ul32 cmdsize;
ul32 dataoff;
ul32 datasize;
};
struct BuildToolVersion {
u32 tool;
u32 version;
ul32 tool;
ul32 version;
};
struct BuildVersionCommand {
u32 cmd;
u32 cmdsize;
u32 platform;
u32 minos;
u32 sdk;
u32 ntools;
ul32 cmd;
ul32 cmdsize;
ul32 platform;
ul32 minos;
ul32 sdk;
ul32 ntools;
};
struct EntryPointCommand {
u32 cmd;
u32 cmdsize;
u64 entryoff;
u64 stacksize;
ul32 cmd;
ul32 cmdsize;
ul64 entryoff;
ul64 stacksize;
};
struct SourceVersionCommand {
u32 cmd;
u32 cmdsize;
u64 version;
ul32 cmd;
ul32 cmdsize;
ul64 version;
};
struct DataInCodeEntry {
u32 offset;
u16 length;
u16 kind;
ul32 offset;
ul16 length;
ul16 kind;
};
struct LinkerOptionCommand {
ul32 cmd;
ul32 cmdsize;
ul32 count;
};
// This struct is named `n_list` on BSD and macOS.
@@ -542,34 +612,42 @@ struct MachSym {
}
bool is_common() const {
return type == N_UNDF && ext && value;
return type == N_UNDF && is_extern && value;
}
u32 stroff;
u8 ext : 1;
u8 type : 3;
u8 pext : 1;
u8 stub : 3;
u8 sect;
ul32 stroff;
union {
u16 desc;
u8 n_type;
struct {
u16 : 8;
u16 p2align : 4;
u16 : 4;
u8 is_extern : 1;
u8 type : 3;
u8 is_private_extern : 1;
u8 stab : 3;
};
};
u64 value;
u8 sect;
union {
ul16 desc;
struct {
u8 padding;
u8 common_p2align : 4;
};
};
ul64 value;
};
// This struct is named `relocation_info` on BSD and macOS.
struct MachRel {
u32 offset;
u32 idx : 24;
u32 is_pcrel : 1;
u32 p2size : 2;
u32 is_extern : 1;
u32 type : 4;
ul32 offset;
ul24 idx;
u8 is_pcrel : 1;
u8 p2size : 2;
u8 is_extern : 1;
u8 type : 4;
};
// __TEXT,__unwind_info section contents
@@ -580,47 +658,47 @@ static constexpr u32 UNWIND_SECOND_LEVEL_COMPRESSED = 3;
static constexpr u32 UNWIND_PERSONALITY_MASK = 0x30000000;
struct UnwindSectionHeader {
u32 version;
u32 encoding_offset;
u32 encoding_count;
u32 personality_offset;
u32 personality_count;
u32 page_offset;
u32 page_count;
ul32 version;
ul32 encoding_offset;
ul32 encoding_count;
ul32 personality_offset;
ul32 personality_count;
ul32 page_offset;
ul32 page_count;
};
struct UnwindFirstLevelPage {
u32 func_addr;
u32 page_offset;
u32 lsda_offset;
ul32 func_addr;
ul32 page_offset;
ul32 lsda_offset;
};
struct UnwindSecondLevelPage {
u32 kind;
u16 page_offset;
u16 page_count;
u16 encoding_offset;
u16 encoding_count;
ul32 kind;
ul16 page_offset;
ul16 page_count;
ul16 encoding_offset;
ul16 encoding_count;
};
struct UnwindLsdaEntry {
u32 func_addr;
u32 lsda_addr;
ul32 func_addr;
ul32 lsda_addr;
};
struct UnwindPageEntry {
u32 func_addr : 24;
u32 encoding : 8;
ul24 func_addr;
u8 encoding;
};
// __LD,__compact_unwind section contents
struct CompactUnwindEntry {
u64 code_start;
u32 code_len;
u32 encoding;
u64 personality;
u64 lsda;
ul64 code_start;
ul32 code_len;
ul32 encoding;
ul64 personality;
ul64 lsda;
};
// __LINKEDIT,__code_signature
@@ -635,46 +713,54 @@ static constexpr u32 CS_EXECSEG_MAIN_BINARY = 1;
static constexpr u32 CS_HASHTYPE_SHA256 = 2;
struct CodeSignatureHeader {
ubig32 magic;
ubig32 length;
ubig32 count;
ub32 magic;
ub32 length;
ub32 count;
};
struct CodeSignatureBlobIndex {
ubig32 type;
ubig32 offset;
u32 padding;
ub32 type;
ub32 offset;
ub32 padding;
};
struct CodeSignatureDirectory {
ubig32 magic;
ubig32 length;
ubig32 version;
ubig32 flags;
ubig32 hash_offset;
ubig32 ident_offset;
ubig32 n_special_slots;
ubig32 n_code_slots;
ubig32 code_limit;
ub32 magic;
ub32 length;
ub32 version;
ub32 flags;
ub32 hash_offset;
ub32 ident_offset;
ub32 n_special_slots;
ub32 n_code_slots;
ub32 code_limit;
u8 hash_size;
u8 hash_type;
u8 platform;
u8 page_size;
ubig32 spare2;
ubig32 scatter_offset;
ubig32 team_offset;
ubig32 spare3;
ubig64 code_limit64;
ubig64 exec_seg_base;
ubig64 exec_seg_limit;
ubig64 exec_seg_flags;
ub32 spare2;
ub32 scatter_offset;
ub32 team_offset;
ub32 spare3;
ub64 code_limit64;
ub64 exec_seg_base;
ub64 exec_seg_limit;
ub64 exec_seg_flags;
};
// __DATA,__objc_imageinfo
struct ObjcImageInfo {
ul32 version = 0;
u8 flags = 0;
u8 swift_version = 0;
ul16 swift_lang_version = 0;
};
struct ARM64 {
static constexpr u32 cputype = CPU_TYPE_ARM64;
static constexpr u32 cpusubtype = CPU_SUBTYPE_ARM64_ALL;
static constexpr u32 page_size = 16384;
static constexpr u32 abs_rel = ARM64_RELOC_UNSIGNED;
static constexpr u32 word_size = 8;
static constexpr u32 stub_size = 12;
static constexpr u32 stub_helper_hdr_size = 24;
static constexpr u32 stub_helper_size = 12;
@@ -683,11 +769,13 @@ struct ARM64 {
struct X86_64 {
static constexpr u32 cputype = CPU_TYPE_X86_64;
static constexpr u32 cpusubtype = CPU_SUBTYPE_X86_64_ALL;
static constexpr u32 page_size = 4096;
static constexpr u32 abs_rel = X86_64_RELOC_UNSIGNED;
static constexpr u32 word_size = 8;
static constexpr u32 stub_size = 6;
static constexpr u32 stub_helper_hdr_size = 16;
static constexpr u32 stub_helper_size = 10;
};
inline constexpr size_t word_size = 8;
} // namespace mold::macho
+777 -141
View File
File diff suppressed because it is too large. Load diff
+4 -4
View File
@@ -30,8 +30,9 @@ void print_map(Context<E> &ctx) {
if (file.is_alive) {
out << "[" << std::setw(3) << i << "] " << file << "\n";
for (Symbol<E> *sym : file.syms)
if (sym->file == &file)
syms.push_back({sym->get_addr(ctx), 0, (u32)i, sym->name});
if (sym && sym->file == &file)
if (!sym->subsec || sym->subsec->is_alive)
syms.push_back({sym->get_addr(ctx), 0, (u32)i, sym->name});
}
}
@@ -74,7 +75,6 @@ void print_map(Context<E> &ctx) {
#define INSTANTIATE(E) \
template void print_map(Context<E> &)
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
INSTANTIATE_ALL;
} // namespace mold::macho
+334 -202
View File
File diff suppressed because it is too large. Load diff
-622
View File
@@ -1,622 +0,0 @@
#include "mold.h"
#include "../archive-file.h"
namespace mold::macho {
template <typename E>
std::ostream &operator<<(std::ostream &out, const InputFile<E> &file) {
if (file.archive_name.empty())
out << path_clean(file.mf->name);
else
out << path_clean(file.archive_name) << "(" << path_clean(file.mf->name) + ")";
return out;
}
template <typename E>
ObjectFile<E> *
ObjectFile<E>::create(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::string archive_name) {
ObjectFile<E> *obj = new ObjectFile<E>;
obj->mf = mf;
obj->archive_name = archive_name;
obj->is_alive = archive_name.empty();
ctx.obj_pool.emplace_back(obj);
return obj;
};
template <typename E>
void ObjectFile<E>::parse(Context<E> &ctx) {
parse_sections(ctx);
parse_symtab(ctx);
split_subsections(ctx);
parse_data_in_code(ctx);
for (std::unique_ptr<InputSection<E>> &isec : sections)
if (isec)
isec->parse_relocations(ctx);
if (unwind_sec)
parse_compact_unwind(ctx, *unwind_sec);
}
template <typename E>
void ObjectFile<E>::parse_sections(Context<E> &ctx) {
MachHeader &hdr = *(MachHeader *)this->mf->data;
u8 *p = this->mf->data + sizeof(hdr);
// Read all but a symtab section
for (i64 i = 0; i < hdr.ncmds; i++) {
LoadCommand &lc = *(LoadCommand *)p;
p += lc.cmdsize;
if (lc.cmd != LC_SEGMENT_64)
continue;
SegmentCommand &cmd = *(SegmentCommand *)&lc;
MachSection *mach_sec = (MachSection *)((u8 *)&lc + sizeof(cmd));
for (MachSection &msec : std::span(mach_sec, mach_sec + cmd.nsects)) {
sections.push_back(nullptr);
if (msec.match("__LD", "__compact_unwind")) {
unwind_sec = &msec;
continue;
}
if (msec.attr & S_ATTR_DEBUG)
continue;
sections.back().reset(new InputSection<E>(ctx, *this, msec));
}
}
}
template <typename E>
void ObjectFile<E>::parse_symtab(Context<E> &ctx) {
SymtabCommand *cmd = (SymtabCommand *)find_load_command(ctx, LC_SYMTAB);
if (!cmd)
return;
mach_syms = {(MachSym *)(this->mf->data + cmd->symoff), cmd->nsyms};
this->syms.reserve(mach_syms.size());
i64 nlocal = 0;
for (MachSym &msym : mach_syms)
if (!msym.ext)
nlocal++;
local_syms.reserve(nlocal);
for (MachSym &msym : mach_syms) {
std::string_view name = (char *)(this->mf->data + cmd->stroff + msym.stroff);
if (msym.ext) {
this->syms.push_back(get_symbol(ctx, name));
} else {
local_syms.emplace_back(name);
this->syms.push_back(&local_syms.back());
}
}
}
struct SplitRegion {
u32 offset;
u32 size;
u32 symidx;
bool is_alt_entry;
};
template <typename E>
struct SplitInfo {
SplitInfo(InputSection<E> *isec) : isec(isec) {}
InputSection<E> *isec;
std::vector<SplitRegion> regions;
};
template <typename E>
static std::vector<SplitInfo<E>> split(Context<E> &ctx, ObjectFile<E> &file) {
std::vector<SplitInfo<E>> vec;
for (std::unique_ptr<InputSection<E>> &isec : file.sections)
vec.push_back({isec.get()});
for (i64 i = 0; i < file.mach_syms.size(); i++) {
MachSym &msym = file.mach_syms[i];
if (msym.type == N_SECT && file.sections[msym.sect - 1]) {
SplitRegion r;
r.offset = msym.value - file.sections[msym.sect - 1]->hdr.addr;
r.symidx = i;
r.is_alt_entry = (msym.desc & N_ALT_ENTRY);
vec[msym.sect - 1].regions.push_back(r);
}
}
std::erase_if(vec, [](const SplitInfo<E> &info) { return !info.isec; });
sort(vec, [](const SplitInfo<E> &a, const SplitInfo<E> &b) {
return a.isec->hdr.addr < b.isec->hdr.addr;
});
for (SplitInfo<E> &info : vec) {
std::vector<SplitRegion> &r = info.regions;
if (r.empty()) {
r.push_back({0, (u32)info.isec->hdr.size, (u32)-1, false});
continue;
}
sort(r, [](const SplitRegion &a, const SplitRegion &b) {
return a.offset < b.offset;
});
if (r[0].offset > 0)
r.insert(r.begin(), {0, r[0].offset, (u32)-1, false});
for (i64 i = 1; i < r.size(); i++)
if (r[i - 1].offset == r[i].offset)
r[i++].is_alt_entry = true;
i64 last = -1;
for (i64 i = 0; i < r.size(); i++) {
if (!r[i].is_alt_entry) {
if (last != -1)
r[last].size = r[i].offset - r[last].offset;
last = i;
}
}
if (last != -1)
r[last].size = info.isec->hdr.size - r[last].offset;
}
return vec;
}
template <typename E>
void ObjectFile<E>::split_subsections(Context<E> &ctx) {
sym_to_subsec.resize(mach_syms.size());
for (SplitInfo<E> &info : split(ctx, *this)) {
InputSection<E> &isec = *info.isec;
for (SplitRegion &r : info.regions) {
if (!r.is_alt_entry) {
Subsection<E> *subsec = new Subsection<E>{
.isec = isec,
.input_offset = r.offset,
.input_size = r.size,
.input_addr = (u32)(isec.hdr.addr + r.offset),
.p2align = (u8)isec.hdr.p2align,
};
subsections.emplace_back(subsec);
}
if (r.symidx != -1)
sym_to_subsec[r.symidx] = subsections.size() - 1;
}
}
for (i64 i = 0; i < mach_syms.size(); i++)
if (!mach_syms[i].ext)
override_symbol(ctx, i);
}
template <typename E>
void ObjectFile<E>::parse_data_in_code(Context<E> &ctx) {
if (auto *cmd = (LinkEditDataCommand *)find_load_command(ctx, LC_DATA_IN_CODE))
data_in_code_entries = {
(DataInCodeEntry *)(this->mf->data + cmd->dataoff),
cmd->datasize / sizeof(DataInCodeEntry)
};
}
template <typename E>
LoadCommand *ObjectFile<E>::find_load_command(Context<E> &ctx, u32 type) {
MachHeader &hdr = *(MachHeader *)this->mf->data;
u8 *p = this->mf->data + sizeof(hdr);
for (i64 i = 0; i < hdr.ncmds; i++) {
LoadCommand &lc = *(LoadCommand *)p;
if (lc.cmd == type)
return &lc;
p += lc.cmdsize;
}
return nullptr;
}
template <typename E>
i64 ObjectFile<E>::find_subsection_idx(Context<E> &ctx, u32 addr) {
auto it = std::upper_bound(
subsections.begin(), subsections.end(), addr,
[&](u32 addr, const std::unique_ptr<Subsection<E>> &subsec) {
return addr < subsec->input_addr;
});
if (it == subsections.begin())
return -1;
return it - subsections.begin() - 1;
}
template <typename E>
Subsection<E> *ObjectFile<E>::find_subsection(Context<E> &ctx, u32 addr) {
i64 i = find_subsection_idx(ctx, addr);
return (i == -1) ? nullptr : subsections[i].get();
}
template <typename E>
void ObjectFile<E>::parse_compact_unwind(Context<E> &ctx, MachSection &hdr) {
if (hdr.size % sizeof(CompactUnwindEntry))
Fatal(ctx) << *this << ": invalid __compact_unwind section size";
i64 num_entries = hdr.size / sizeof(CompactUnwindEntry);
unwind_records.reserve(num_entries);
CompactUnwindEntry *src = (CompactUnwindEntry *)(this->mf->data + hdr.offset);
// Read compact unwind entries
for (i64 i = 0; i < num_entries; i++)
unwind_records.emplace_back(src[i].code_len, src[i].encoding);
// Read relocations
MachRel *mach_rels = (MachRel *)(this->mf->data + hdr.reloff);
for (i64 i = 0; i < hdr.nreloc; i++) {
MachRel &r = mach_rels[i];
if (r.offset >= hdr.size)
Fatal(ctx) << *this << ": relocation offset too large: " << i;
i64 idx = r.offset / sizeof(CompactUnwindEntry);
UnwindRecord<E> &dst = unwind_records[idx];
auto error = [&] {
Fatal(ctx) << *this << ": __compact_unwind: unsupported relocation: " << i;
};
switch (r.offset % sizeof(CompactUnwindEntry)) {
case offsetof(CompactUnwindEntry, code_start): {
if (r.is_pcrel || r.p2size != 3 || r.is_extern || r.type)
error();
Subsection<E> *target = find_subsection(ctx, src[idx].code_start);
if (!target)
error();
dst.subsec = target;
dst.offset = src[idx].code_start - target->input_addr;
break;
}
case offsetof(CompactUnwindEntry, personality):
if (r.is_pcrel || r.p2size != 3 || !r.is_extern || r.type)
error();
dst.personality = this->syms[r.idx];
break;
case offsetof(CompactUnwindEntry, lsda): {
if (r.is_pcrel || r.p2size != 3 || r.is_extern || r.type)
error();
i32 addr = *(i32 *)((u8 *)this->mf->data + hdr.offset + r.offset);
Subsection<E> *target = find_subsection(ctx, addr);
if (!target)
error();
dst.lsda = target;
dst.lsda_offset = addr - target->input_addr;
break;
}
default:
Fatal(ctx) << *this << ": __compact_unwind: unsupported relocation: " << i;
}
}
for (i64 i = 0; i < num_entries; i++)
if (!unwind_records[i].subsec)
Fatal(ctx) << ": __compact_unwind: missing relocation at " << i;
// Sort unwind entries by offset
sort(unwind_records, [](const UnwindRecord<E> &a, const UnwindRecord<E> &b) {
return std::tuple(a.subsec->input_addr, a.offset) <
std::tuple(b.subsec->input_addr, b.offset);
});
// Associate unwind entries to subsections
for (i64 i = 0; i < num_entries;) {
Subsection<E> &subsec = *unwind_records[i].subsec;
subsec.unwind_offset = i;
i64 j = i + 1;
while (j < num_entries && unwind_records[j].subsec == &subsec)
j++;
subsec.nunwind = j - i;
i = j;
}
}
// Symbols with higher priorities overwrites symbols with lower priorities.
// Here is the list of priorities, from the highest to the lowest.
//
// 1. Strong defined symbol
// 2. Weak defined symbol
// 3. Strong defined symbol in a DSO
// 4. Weak defined symbol in a DSO
// 5. Strong or weak defined symbol in an archive
// 6. Common symbol
// 7. Unclaimed (nonexistent) symbol
//
// Ties are broken by file priority.
template <typename E>
static u64 get_rank(InputFile<E> *file, MachSym &msym, bool is_lazy) {
if (msym.is_common())
return (6 << 24) + file->priority;
if (is_lazy)
return (5 << 24) + file->priority;
if (file->is_dylib())
return (3 << 24) + file->priority;
return (1 << 24) + file->priority;
}
template <typename E>
static u64 get_rank(Symbol<E> &sym) {
InputFile<E> *file = sym.file;
if (!file)
return 7 << 24;
if (sym.is_common)
return (6 << 24) + file->priority;
if (!file->archive_name.empty())
return (5 << 24) + file->priority;
if (file->is_dylib())
return (3 << 24) + file->priority;
return (1 << 24) + file->priority;
}
template <typename E>
void ObjectFile<E>::override_symbol(Context<E> &ctx, i64 idx) {
Symbol<E> &sym = *this->syms[idx];
MachSym &msym = mach_syms[idx];
sym.file = this;
sym.is_extern = msym.ext;
sym.is_lazy = false;
switch (msym.type) {
case N_UNDF:
assert(msym.is_common());
sym.subsec = nullptr;
sym.value = msym.value;
sym.is_common = true;
break;
case N_ABS:
sym.subsec = nullptr;
sym.value = msym.value;
sym.is_common = false;
break;
case N_SECT:
sym.subsec = subsections[sym_to_subsec[idx]].get();
sym.value = msym.value - sym.subsec->input_addr;
sym.is_common = false;
break;
default:
Fatal(ctx) << sym << ": unknown symbol type: " << (u64)msym.type;
}
}
template <typename E>
void ObjectFile<E>::resolve_regular_symbols(Context<E> &ctx) {
for (i64 i = 0; i < this->syms.size(); i++) {
MachSym &msym = mach_syms[i];
if (!msym.ext || msym.is_undef())
continue;
Symbol<E> &sym = *this->syms[i];
std::scoped_lock lock(sym.mu);
if (get_rank(this, msym, false) < get_rank(sym))
override_symbol(ctx, i);
}
}
template <typename E>
void ObjectFile<E>::resolve_lazy_symbols(Context<E> &ctx) {
for (i64 i = 0; i < this->syms.size(); i++) {
MachSym &msym = mach_syms[i];
if (!msym.ext || msym.is_undef() || msym.is_common())
continue;
Symbol<E> &sym = *this->syms[i];
std::scoped_lock lock(sym.mu);
if (get_rank(this, msym, true) < get_rank(sym)) {
sym.file = this;
sym.subsec = nullptr;
sym.value = 0;
sym.is_extern = false;
sym.is_lazy = true;
sym.is_common = false;
}
}
}
template <typename E>
bool ObjectFile<E>::is_objc_object(Context<E> &ctx) {
for (std::unique_ptr<InputSection<E>> &isec : sections)
if (isec->hdr.match("__DATA", "__objc_catlist") ||
isec->hdr.match("__TEXT", "__swift"))
return true;
for (i64 i = 0; i < this->syms.size(); i++)
if (!mach_syms[i].is_undef() && mach_syms[i].ext &&
this->syms[i]->name.starts_with("_OBJC_CLASS_$_"))
return true;
return false;
}
template <typename E>
std::vector<ObjectFile<E> *> ObjectFile<E>::mark_live_objects(Context<E> &ctx) {
std::vector<ObjectFile<E> *> vec;
assert(this->is_alive);
for (i64 i = 0; i < this->syms.size(); i++) {
MachSym &msym = mach_syms[i];
if (!msym.ext)
continue;
Symbol<E> &sym = *this->syms[i];
std::scoped_lock lock(sym.mu);
if (msym.is_undef()) {
if (sym.file && !sym.file->is_alive.exchange(true))
vec.push_back((ObjectFile<E> *)sym.file);
continue;
}
if (get_rank(this, msym, false) < get_rank(sym))
override_symbol(ctx, i);
}
return vec;
}
template <typename E>
void ObjectFile<E>::convert_common_symbols(Context<E> &ctx) {
for (i64 i = 0; i < this->syms.size(); i++) {
Symbol<E> &sym = *this->syms[i];
MachSym &msym = mach_syms[i];
if (sym.file == this && sym.is_common) {
InputSection<E> *isec = get_common_sec(ctx);
Subsection<E> *subsec = new Subsection<E>{
.isec = *isec,
.input_size = (u32)msym.value,
.p2align = (u8)msym.p2align,
};
subsections.emplace_back(subsec);
sym.subsec = subsec;
sym.value = 0;
sym.is_common = false;
}
}
}
template <typename E>
void ObjectFile<E>::check_duplicate_symbols(Context<E> &ctx) {
for (i64 i = 0; i < this->syms.size(); i++) {
Symbol<E> *sym = this->syms[i];
MachSym &msym = mach_syms[i];
if (sym && !msym.is_undef() && !msym.is_common() && sym->file != this)
Error(ctx) << "duplicate symbol: " << *this << ": " << *sym->file
<< ": " << *sym;
}
}
template <typename E>
InputSection<E> *ObjectFile<E>::get_common_sec(Context<E> &ctx) {
if (!common_sec) {
MachSection *hdr = new MachSection;
common_hdr.reset(hdr);
memset(hdr, 0, sizeof(*hdr));
hdr->set_segname("__DATA");
hdr->set_sectname("__common");
hdr->type = S_ZEROFILL;
common_sec = new InputSection<E>(ctx, *this, *hdr);
sections.emplace_back(common_sec);
}
return common_sec;
}
template <typename E>
DylibFile<E> *DylibFile<E>::create(Context<E> &ctx, MappedFile<Context<E>> *mf) {
DylibFile<E> *dylib = new DylibFile<E>;
dylib->mf = mf;
ctx.dylib_pool.emplace_back(dylib);
return dylib;
};
template <typename E>
void DylibFile<E>::read_trie(Context<E> &ctx, u8 *start, i64 offset,
const std::string &prefix) {
u8 *buf = start + offset;
if (*buf) {
read_uleb(buf); // size
read_uleb(buf); // flags
read_uleb(buf); // addr
this->syms.push_back(get_symbol(ctx, prefix));
} else {
buf++;
}
i64 nchild = *buf++;
for (i64 i = 0; i < nchild; i++) {
std::string suffix((char *)buf);
buf += suffix.size() + 1;
i64 off = read_uleb(buf);
read_trie(ctx, start, off, prefix + suffix);
}
}
template <typename E>
void DylibFile<E>::parse_dylib(Context<E> &ctx) {
MachHeader &hdr = *(MachHeader *)this->mf->data;
u8 *p = this->mf->data + sizeof(hdr);
for (i64 i = 0; i < hdr.ncmds; i++) {
LoadCommand &lc = *(LoadCommand *)p;
switch (lc.cmd) {
case LC_ID_DYLIB: {
DylibCommand &cmd = *(DylibCommand *)p;
install_name = (char *)p + cmd.nameoff;
break;
}
case LC_DYLD_INFO_ONLY: {
DyldInfoCommand &cmd = *(DyldInfoCommand *)p;
if (cmd.export_off)
read_trie(ctx, this->mf->data + cmd.export_off);
break;
}
case LC_DYLD_EXPORTS_TRIE: {
LinkEditDataCommand &cmd = *(LinkEditDataCommand *)p;
read_trie(ctx, this->mf->data + cmd.dataoff);
break;
}
}
p += lc.cmdsize;
}
}
template <typename E>
void DylibFile<E>::parse(Context<E> &ctx) {
switch (get_file_type(this->mf)) {
case FileType::TAPI: {
TextDylib tbd = parse_tbd(ctx, this->mf);
for (std::string_view sym : tbd.exports)
this->syms.push_back(get_symbol(ctx, sym));
install_name = tbd.install_name;
break;
}
case FileType::MACH_DYLIB:
parse_dylib(ctx);
break;
default:
Fatal(ctx) << this->mf->name << ": is not a dylib";
}
}
template <typename E>
void DylibFile<E>::resolve_symbols(Context<E> &ctx) {
for (Symbol<E> *sym : this->syms) {
std::scoped_lock lock(sym->mu);
if (sym->file && sym->file->priority < this->priority)
continue;
sym->file = this;
sym->is_extern = true;
}
}
#define INSTANTIATE(E) \
template class ObjectFile<E>; \
template class DylibFile<E>; \
template std::ostream &operator<<(std::ostream &, const InputFile<E> &)
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
} // namespace mold::macho
+683 -313
View File
File diff suppressed because it is too large. Load diff
-120
View File
@@ -1,120 +0,0 @@
// This file implements the same functionality as elf/output-file.cc.
// We have two different implemetntations for ELF and Mach-O because
// we implemented a special optimization for ELF or Linux.
//
// On Linux, it is faster to write to an existing file than creating a
// new file and writing to it. So, we overwrite an existing executable
// if exists.
//
// On macOS, it looks like the system does not assume that an executable
// is mutated once it is created. Due to some code signing mechanism or
// something, we always have to create a fresh file.
#include "mold.h"
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
namespace mold::macho {
template <typename E>
class MemoryMappedOutputFile : public OutputFile<E> {
public:
MemoryMappedOutputFile(Context<E> &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<E>(path, filesize, true) {
std::string tmp_path = filepath(path).parent_path() / ".mold-XXXXXX";
output_tmpfile = (char *)save_string(ctx, tmp_path).data();
i64 fd = mkstemp(output_tmpfile);
if (fd == -1)
Fatal(ctx) << "cannot open " << output_tmpfile << ": " << errno_string();
if (ftruncate(fd, filesize))
Fatal(ctx) << "ftruncate failed: " << errno_string();
if (fchmod(fd, perm) == -1)
Fatal(ctx) << "fchmod failed: " << errno_string();
this->buf = (u8 *)mmap(nullptr, filesize, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (this->buf == MAP_FAILED)
Fatal(ctx) << path << ": mmap failed: " << errno_string();
::close(fd);
}
void close(Context<E> &ctx) override {
Timer t(ctx, "close_file");
munmap(this->buf, this->filesize);
if (rename(output_tmpfile, this->path.c_str()) == -1)
Fatal(ctx) << this->path << ": rename failed: " << errno_string();
output_tmpfile = nullptr;
}
};
template <typename E>
class MallocOutputFile : public OutputFile<E> {
public:
MallocOutputFile(Context<E> &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<E>(path, filesize, false), perm(perm) {
this->buf = (u8 *)mmap(NULL, filesize, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (this->buf == MAP_FAILED)
Fatal(ctx) << "mmap failed: " << errno_string();
}
void close(Context<E> &ctx) override {
Timer t(ctx, "close_file");
if (this->path == "-") {
fwrite(this->buf, this->filesize, 1, stdout);
fclose(stdout);
return;
}
i64 fd = ::open(this->path.c_str(), O_RDWR | O_CREAT, perm);
if (fd == -1)
Fatal(ctx) << "cannot open " << this->path << ": " << errno_string();
FILE *fp = fdopen(fd, "w");
fwrite(this->buf, this->filesize, 1, fp);
fclose(fp);
}
private:
i64 perm;
};
template <typename E>
std::unique_ptr<OutputFile<E>>
OutputFile<E>::open(Context<E> &ctx, std::string path, i64 filesize, i64 perm) {
Timer t(ctx, "open_file");
if (path.starts_with('/') && !ctx.arg.chroot.empty())
path = ctx.arg.chroot + "/" + path_clean(path);
bool is_special = false;
if (path == "-") {
is_special = true;
} else {
struct stat st;
if (stat(path.c_str(), &st) == 0 && (st.st_mode & S_IFMT) != S_IFREG)
is_special = true;
}
std::unique_ptr<OutputFile> file;
if (is_special)
file = std::make_unique<MallocOutputFile<E>>(ctx, path, filesize, perm);
else
file = std::make_unique<MemoryMappedOutputFile<E>>(ctx, path, filesize, perm);
return file;
}
#define INSTANTIATE(E) \
template class OutputFile<E>
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
} // namespace mold::macho
+209 -42
View File
@@ -1,9 +1,27 @@
// On macOS, you can pass a test file describing a dylib instead of an
// actual dylib file to link against a dynamic library. Such text file
// should be in the YAML format and contains dylib's exported symbols
// as well as the file's various attributes. The extension of the text
// file is `.tbd`.
//
// .tbd files allows users to link against a library without
// distributing the binary of the library file itself.
//
// This file contains functions to parse the .tbd file.
#include "mold.h"
#include <optional>
#include <regex>
#include <unordered_set>
namespace mold::macho {
template <typename T, typename U>
static void merge(std::set<T> &a, U &&b) {
a.insert(b.begin(), b.end());
}
static std::vector<YamlNode>
get_vector(YamlNode &node, std::string_view key) {
if (auto *map = std::get_if<std::map<std::string_view, YamlNode>>(&node.data))
@@ -13,6 +31,15 @@ get_vector(YamlNode &node, std::string_view key) {
return {};
}
static std::vector<std::string_view>
get_string_vector(YamlNode &node, std::string_view key) {
std::vector<std::string_view> vec;
for (YamlNode &mem : get_vector(node, key))
if (auto val = std::get_if<std::string_view>(&mem.data))
vec.push_back(*val);
return vec;
}
static std::optional<std::string_view>
get_string(YamlNode &node, std::string_view key) {
if (auto *map = std::get_if<std::map<std::string_view, YamlNode>>(&node.data))
@@ -24,70 +51,206 @@ get_string(YamlNode &node, std::string_view key) {
static bool contains(const std::vector<YamlNode> &vec, std::string_view key) {
for (const YamlNode &mem : vec)
if (const std::string_view *s = std::get_if<std::string_view>(&mem.data))
if (*s == key)
if (auto val = std::get_if<std::string_view>(&mem.data))
if (*val == key)
return true;
return false;
}
static std::optional<TextDylib> to_tbd(YamlNode &node, std::string_view arch) {
if (!contains(get_vector(node, "targets"), arch))
static bool match_arch(const std::vector<YamlNode> &vec, std::string_view arch) {
for (const YamlNode &mem : vec)
if (auto val = std::get_if<std::string_view>(&mem.data))
if (*val == arch || val->starts_with(std::string(arch) + "-"))
return true;
return false;
}
template <typename E>
static std::optional<TextDylib>
to_tbd(Context<E> &ctx, YamlNode &node, std::string_view arch,
std::string_view filename) {
if (!match_arch(get_vector(node, "targets"), arch))
return {};
TextDylib tbd;
if (ctx.arg.application_extension &&
contains(get_vector(node, "flags"), "not_app_extension_safe"))
Warn(ctx) << "linking against a dylib which is not safe for use in "
<< "application extensions: " << filename;
for (YamlNode &mem : get_vector(node, "uuids"))
if (auto target = get_string(mem, "target"))
if (*target == arch)
if (auto value = get_string(mem, "value"))
tbd.uuid = *value;
TextDylib tbd;
if (auto val = get_string(node, "install-name"))
tbd.install_name = *val;
if (auto val = get_string(node, "current-version"))
tbd.current_version = *val;
for (YamlNode &mem : get_vector(node, "parent-umbrella"))
if (contains(get_vector(mem, "targets"), arch))
if (auto val = get_string(mem, "umbrella"))
tbd.parent_umbrella = *val;
for (YamlNode &mem : get_vector(node, "reexported-libraries"))
if (contains(get_vector(mem, "targets"), arch))
for (YamlNode &mem : get_vector(mem, "libraries"))
if (auto *lib = std::get_if<std::string_view>(&mem.data))
tbd.reexported_libs.push_back(*lib);
if (match_arch(get_vector(mem, "targets"), arch))
append(tbd.reexported_libs, get_string_vector(mem, "libraries"));
for (std::string_view key : {"exports", "reexports"})
for (YamlNode &mem : get_vector(node, key))
if (contains(get_vector(mem, "targets"), arch))
for (YamlNode &mem : get_vector(mem, "symbols"))
if (auto *sym = std::get_if<std::string_view>(&mem.data))
tbd.exports.push_back(*sym);
auto concat = [&](const std::string &x, std::string_view y) {
return save_string(ctx, x + std::string(y));
};
for (std::string_view key : {"exports", "reexports"}) {
for (YamlNode &mem : get_vector(node, key)) {
if (match_arch(get_vector(mem, "targets"), arch)) {
merge(tbd.exports, get_string_vector(mem, "symbols"));
merge(tbd.weak_exports, get_string_vector(mem, "weak-symbols"));
for (std::string_view s : get_string_vector(mem, "objc-classes")) {
tbd.exports.insert(concat("_OBJC_CLASS_$_", s));
tbd.exports.insert(concat("_OBJC_METACLASS_$_", s));
}
for (std::string_view s : get_string_vector(mem, "objc-eh-types"))
tbd.exports.insert(concat("_OBJC_EHTYPE_$_", s));
for (std::string_view s : get_string_vector(mem, "objc-ivars"))
tbd.exports.insert(concat("_OBJC_IVAR_$_", s));
}
}
}
return tbd;
}
static i64 parse_version(const std::string &arg) {
auto flags = std::regex_constants::ECMAScript | std::regex_constants::optimize;
static std::regex re(R"((\d+)(?:\.(\d+))?(?:\.(\d+))?)", flags);
std::smatch m;
bool ok = std::regex_match(arg, m, re);
assert(ok);
i64 major = (m[1].length() == 0) ? 0 : stoi(m[1]);
i64 minor = (m[2].length() == 0) ? 0 : stoi(m[2]);
i64 patch = (m[3].length() == 0) ? 0 : stoi(m[3]);
return (major << 16) | (minor << 8) | patch;
}
// Dylib can contain special symbols whose name starts with "$ld$".
// Such symbols aren't actually symbols but linker directives.
// We interpret such symbols in this function.
template <typename E>
static TextDylib squash(Context<E> &ctx, std::span<TextDylib> tbds) {
static void interpret_ld_symbols(Context<E> &ctx, TextDylib &tbd) {
std::set<std::string_view> syms;
std::unordered_set<std::string_view> hidden_syms;
auto string_view = [](const std::csub_match &sub) {
return std::string_view{sub.first, (size_t)sub.length()};
};
for (std::string_view s : tbd.exports) {
if (!s.starts_with("$ld$"))
continue;
auto flags = std::regex_constants::ECMAScript | std::regex_constants::optimize;
std::cmatch m;
// $ld$previous$ symbol replaces the default install name with a
// specified one if the platform OS version is in a specified range.
static std::regex previous_re(
R"(\$ld\$previous\$([^$]+)\$([\d.]*)\$(\d+)\$([\d.]+)\$([\d.]+)\$(.*)\$)",
flags);
if (std::regex_match(s.data(), s.data() + s.size(), m, previous_re)) {
std::string_view install_name = string_view(m[1]);
i64 platform = std::stoi(m[3].str());
i64 min_version = parse_version(m[4]);
i64 max_version = parse_version(m[5]);
std::string_view symbol_name = string_view(m[6]);
if (!symbol_name.empty()) {
// ld64 source seems to have implemented a feature to give an
// alternative install name for a matching symbol, but it didn't
// work in practice (or I may be using the feature in a wrong way.)
// Ignore such symbol for now.
continue;
}
if (platform == ctx.arg.platform &&
min_version <= ctx.arg.platform_min_version &&
ctx.arg.platform_min_version < max_version) {
tbd.install_name = install_name;
}
continue;
}
// $ld$add$os_version$symbol adds a symbol if the given OS version
// matches.
static std::regex add_re(R"(\$ld\$add\$os([\d.]+)\$(.+))", flags);
if (std::regex_match(s.data(), s.data() + s.size(), m, add_re)) {
if (ctx.arg.platform_min_version == parse_version(m[1]))
syms.insert(string_view(m[2]));
continue;
}
// $ld$hide$os_version$symbol hides a symbol if the given OS version
// matches.
static std::regex hidden_re(R"(\$ld\$hide\$os([\d.]+)\$(.+))", flags);
if (std::regex_match(s.data(), s.data() + s.size(), m, hidden_re)) {
if (ctx.arg.platform_min_version == parse_version(m[1]))
hidden_syms.insert(string_view(m[2]));
continue;
}
// $ld$install_name$os_version$name changes the install name to a
// given name.
static std::regex
install_name_re(R"(\$ld\$install_name\$os([\d.]+)\$(.+))", flags);
if (std::regex_match(s.data(), s.data() + s.size(), m, install_name_re)) {
if (ctx.arg.platform_min_version == parse_version(m[1]))
tbd.install_name = string_view(m[2]);
continue;
}
}
for (std::string_view s : tbd.exports)
if (!s.starts_with("$ld$") && !hidden_syms.contains(s))
syms.insert(s);
tbd.exports = std::move(syms);
}
template <typename E>
static TextDylib parse(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::string_view arch);
// A single YAML file may contain multiple text dylibs. The first text
// dylib is the main file followed by optional other text dylibs for
// re-exported libraries.
//
// This fucntion squashes multiple text dylibs into a single text dylib
// by copying symbols of re-exported text dylibs to the main text dylib.
template <typename E>
static TextDylib
squash(Context<E> &ctx, std::span<TextDylib> tbds, std::string_view arch) {
std::unordered_map<std::string_view, TextDylib> map;
std::vector<std::string_view> remainings;
TextDylib main = std::move(tbds[0]);
for (TextDylib &tbd : tbds.subspan(1))
map[tbd.install_name] = std::move(tbd);
std::vector<std::string_view> libs;
std::function<void(TextDylib &)> visit = [&](TextDylib &tbd) {
for (std::string_view lib : tbd.reexported_libs) {
auto it = map.find(lib);
for (std::string_view lib : main.reexported_libs) {
auto it = map.find(lib);
if (it != map.end())
append(main.exports, it->second.exports);
else
libs.push_back(lib);
}
if (it != map.end()) {
TextDylib &child = it->second;
merge(main.exports, child.exports);
merge(main.weak_exports, child.weak_exports);
visit(child);
} else {
remainings.push_back(lib);
}
}
};
main.reexported_libs = std::move(libs);
visit(main);
main.reexported_libs = remainings;
return main;
}
@@ -109,19 +272,23 @@ static TextDylib parse(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::vector<TextDylib> vec;
for (YamlNode &node : nodes)
if (std::optional<TextDylib> dylib = to_tbd(node, arch))
if (std::optional<TextDylib> dylib = to_tbd(ctx, node, arch, mf->name))
vec.push_back(*dylib);
return squash(ctx, vec);
for (TextDylib &tbd : vec)
interpret_ld_symbols(ctx, tbd);
return squash(ctx, vec, arch);
}
template <>
TextDylib parse_tbd(Context<ARM64> &ctx, MappedFile<Context<ARM64>> *mf) {
return parse(ctx, mf, "arm64-macos");
return parse(ctx, mf, "arm64");
}
template <>
TextDylib parse_tbd(Context<X86_64> &ctx, MappedFile<Context<X86_64>> *mf) {
return parse(ctx, mf, "x86_64-macos");
return parse(ctx, mf, "x86_64");
}
} // namespace mold::macho
+37 -19
View File
@@ -1,12 +1,27 @@
// On macOS, a .tbd file can be used instead of a .dylib to link
// against a dynamic library. A .tbd file is a text file in the YAML
// format, so the linker has to be able to parse a YAML file. This
// file implements a YAML parser.
//
// We implemented a YAML parser ourselves instead of using an external
// library. We didn't want to add an dependency to an external library
// just to read .tdb files.
//
// YAML as a format is very complicated. For example, the official
// YAML specification allows embedding a JSON string into YAML; so it
// is strictly larger than JSON. It has surprisingly many features
// that most users are not aware of. Fortunately, we have to support
// only a small portion of the spec to read a .tbd file.
#include "mold.h"
#include <optional>
namespace mold::macho {
struct Token {
enum { STRING = 1, INDENT, DEDENT, END };
enum { STRING = 1, INDENT, DEDENT, END };
struct Token {
u8 kind = 0;
std::string_view str;
};
@@ -35,17 +50,20 @@ private:
std::vector<Token> tokens;
};
// A tokenizer for YAML. YAML represents blocks by indentation. This
// tokenizer inserts INDENT and DEDENT special tokens before and after
// each indented text block.
std::optional<YamlError> YamlParser::tokenize() {
std::vector<i64> indents = {0};
auto indent = [&](std::string_view str, i64 depth) {
tokens.push_back({Token::INDENT, str});
tokens.push_back({INDENT, str});
indents.push_back(depth);
};
auto dedent = [&](std::string_view str) {
assert(indents.size() > 1);
tokens.push_back({Token::DEDENT, str});
tokens.push_back({DEDENT, str});
indents.pop_back();
};
@@ -63,7 +81,7 @@ std::optional<YamlError> YamlParser::tokenize() {
if (str.starts_with("---")) {
while (indents.size() > 1)
dedent(str);
tokens.push_back({Token::END, str.substr(0, 3)});
tokens.push_back({END, str.substr(0, 3)});
skip_line(str);
return {};
}
@@ -71,7 +89,7 @@ std::optional<YamlError> YamlParser::tokenize() {
if (str.starts_with("...")) {
while (indents.size() > 1)
dedent(str);
tokens.push_back({Token::END, str.substr(0, 3)});
tokens.push_back({END, str.substr(0, 3)});
str = str.substr(str.size());
return {};
}
@@ -217,7 +235,7 @@ YamlParser::tokenize_string(std::string_view &str, char end) {
if (pos == str.npos)
return YamlError{"unterminated string literal", start - input.data()};
tokens.push_back({Token::STRING, str.substr(1, pos - 1)});
tokens.push_back({STRING, str.substr(1, pos - 1)});
str = str.substr(pos + 1);
return {};
}
@@ -228,7 +246,7 @@ YamlParser::tokenize_bare_string(std::string_view &str) {
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-/.");
if (pos == str.npos)
pos = str.size();
tokens.push_back({Token::STRING, str.substr(0, pos)});
tokens.push_back({STRING, str.substr(0, pos)});
str = str.substr(pos);
}
@@ -240,7 +258,7 @@ std::variant<std::vector<YamlNode>, YamlError> YamlParser::parse() {
std::vector<YamlNode> vec;
while (!tok.empty()) {
if (tok[0].kind == Token::END) {
if (tok[0].kind == END) {
tok = tok.subspan(1);
continue;
}
@@ -250,7 +268,7 @@ std::variant<std::vector<YamlNode>, YamlError> YamlParser::parse() {
return *err;
vec.push_back(std::get<YamlNode>(elem));
if (tok[0].kind != Token::END)
if (tok[0].kind != END)
return YamlError{"stray token", tok[0].str.data() - input.data()};
}
return vec;
@@ -258,11 +276,11 @@ std::variant<std::vector<YamlNode>, YamlError> YamlParser::parse() {
std::variant<YamlNode, YamlError>
YamlParser::parse_element(std::span<Token> &tok) {
if (tok[0].kind == Token::INDENT) {
if (tok[0].kind == INDENT) {
tok = tok.subspan(1);
std::variant<YamlNode, YamlError> elem = parse_element(tok);
assert(tok[0].kind == Token::DEDENT);
assert(tok[0].kind == DEDENT);
tok = tok.subspan(1);
return elem;
}
@@ -270,7 +288,7 @@ YamlParser::parse_element(std::span<Token> &tok) {
if (tok[0].kind == '-')
return parse_list(tok);
if (tok.size() > 2 && tok[0].kind == Token::STRING && tok[1].kind == ':')
if (tok.size() > 2 && tok[0].kind == STRING && tok[1].kind == ':')
return parse_map(tok);
return parse_flow_element(tok);
@@ -280,7 +298,7 @@ std::variant<YamlNode, YamlError>
YamlParser::parse_list(std::span<Token> &tok) {
std::vector<YamlNode> vec;
while (tok[0].kind != Token::END && tok[0].kind != Token::DEDENT) {
while (tok[0].kind != END && tok[0].kind != DEDENT) {
if (tok[0].kind != '-')
return YamlError{"list element expected", tok[0].str.data() - input.data()};
tok = tok.subspan(1);
@@ -297,8 +315,8 @@ std::variant<YamlNode, YamlError>
YamlParser::parse_map(std::span<Token> &tok) {
std::map<std::string_view, YamlNode> map;
while (tok[0].kind != Token::END && tok[0].kind != Token::DEDENT) {
if (tok.size() < 2 || tok[0].kind != Token::STRING || tok[1].kind != ':')
while (tok[0].kind != END && tok[0].kind != DEDENT) {
if (tok.size() < 2 || tok[0].kind != STRING || tok[1].kind != ':')
return YamlError{"map key expected", tok[0].str.data() - input.data()};
std::string_view key = tok[0].str;
@@ -319,7 +337,7 @@ YamlParser::parse_flow_element(std::span<Token> &tok) {
return parse_flow_list(tok);
}
if (tok[0].kind != Token::STRING)
if (tok[0].kind != STRING)
return YamlError{"scalar expected", tok[0].str.data() - input.data()};
std::string_view val = tok[0].str;
@@ -332,7 +350,7 @@ YamlParser::parse_flow_list(std::span<Token> &tok) {
std::vector<YamlNode> vec;
const char *start = tok[0].str.data();
while (tok[0].kind != ']' && tok[0].kind != Token::END) {
while (tok[0].kind != ']' && tok[0].kind != END) {
std::variant<YamlNode, YamlError> elem = parse_flow_element(tok);
if (YamlError *err = std::get_if<YamlError>(&elem))
return *err;
@@ -345,7 +363,7 @@ YamlParser::parse_flow_list(std::span<Token> &tok) {
tok = tok.subspan(1);
}
if (tok[0].kind == Token::END)
if (tok[0].kind == END)
return YamlError{"unterminated flow list", start - input.data()};
tok = tok.subspan(1);
+82 -14
View File
@@ -1,8 +1,8 @@
#include "elf/mold.h"
#include "macho/mold.h"
#include "mold.h"
#include <cstring>
#include <signal.h>
#include <tbb/global_control.h>
#ifdef USE_SYSTEM_MIMALLOC
#include <mimalloc-new-delete.h>
@@ -10,13 +10,19 @@
namespace mold {
#ifdef GIT_HASH
const std::string mold_version =
"mold " MOLD_VERSION " (" GIT_HASH "; compatible with GNU ld)";
#else
const std::string mold_version =
"mold " MOLD_VERSION " (compatible with GNU ld)";
#endif
namespace elf {
int main(int argc, char **argv);
}
namespace macho {
int main(int argc, char **argv);
}
static std::string get_mold_version() {
if (mold_git_hash.empty())
return "mold " MOLD_VERSION " (compatible with GNU ld)";
return "mold " MOLD_VERSION " (" + mold_git_hash + "; compatible with GNU ld)";
}
void cleanup() {
if (output_tmpfile)
@@ -25,23 +31,85 @@ void cleanup() {
unlink(socket_tmpfile);
}
static void signal_handler(int) {
// mold mmap's an output file, and the mmap succeeds even if there's
// no enough space left on the filesystem. The actual disk blocks are
// not allocated on the mmap call but when the program writes to it
// for the first time.
//
// If a disk becomes full as a result of a write to an mmap'ed memory
// region, the failure of the write is reported as a SIGBUS or structured
// exeption with code EXCEPTION_IN_PAGE_ERROR on Windows. This
// signal handler catches that signal and prints out a user-friendly
// error message. Without this, it is very hard to realize that the
// disk might be full.
#ifdef _WIN32
static LONG WINAPI vectored_handler(_EXCEPTION_POINTERS *exception_info) {
static std::mutex mu;
std::scoped_lock lock{mu};
PEXCEPTION_RECORD exception_record = exception_info->ExceptionRecord;
ULONG_PTR *exception_information = exception_record->ExceptionInformation;
if (exception_record->ExceptionCode == EXCEPTION_IN_PAGE_ERROR &&
(ULONG_PTR)output_buffer_start <= exception_information[1] &&
exception_information[1] < (ULONG_PTR)output_buffer_end) {
const char msg[] = "mold: failed to write to an output file. Disk full?\n";
(void)!write(_fileno(stderr), msg, sizeof(msg) - 1);
}
cleanup();
_exit(1);
}
void install_signal_handler() {
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
AddVectoredExceptionHandler(0, vectored_handler);
}
#else
static void sighandler(int signo, siginfo_t *info, void *ucontext) {
static std::mutex mu;
std::scoped_lock lock{mu};
if ((signo == SIGSEGV || signo == SIGBUS) &&
output_buffer_start <= info->si_addr &&
info->si_addr < output_buffer_end) {
const char msg[] = "mold: failed to write to an output file. Disk full?\n";
(void)!write(STDERR_FILENO, msg, sizeof(msg) - 1);
}
cleanup();
_exit(1);
}
void install_signal_handler() {
struct sigaction action;
action.sa_sigaction = sighandler;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_SIGINFO;
sigaction(SIGINT, &action, NULL);
sigaction(SIGTERM, &action, NULL);
sigaction(SIGBUS, &action, NULL);
}
#endif
i64 get_default_thread_count() {
// mold doesn't scale well above 32 threads.
int n = tbb::global_control::active_value(
tbb::global_control::max_allowed_parallelism);
return std::min(n, 32);
}
} // namespace mold
int main(int argc, char **argv) {
std::string cmd = mold::filepath(argv[0]).filename();
mold::mold_version = mold::get_mold_version();
std::string cmd = mold::filepath(argv[0]).filename().string();
if (cmd == "ld64" || cmd == "ld64.mold")
return mold::macho::main(argc, argv);
return mold::elf::main(argc, argv);
}
+244 -36
View File
@@ -1,9 +1,11 @@
#pragma once
#include "big-endian.h"
#include "inttypes.h"
#include <array>
#include <atomic>
#include <bit>
#include <bitset>
#include <cassert>
#include <cstdio>
#include <cstring>
@@ -11,24 +13,48 @@
#include <filesystem>
#include <iostream>
#include <mutex>
#include <optional>
#include <span>
#include <sstream>
#include <string>
#include <string_view>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <tbb/concurrent_vector.h>
#include <tbb/enumerable_thread_specific.h>
#include <unistd.h>
#include <vector>
#ifdef NDEBUG
# define unreachable() __builtin_unreachable()
#ifdef _WIN32
# include <io.h>
#else
# define unreachable() assert(0 && "unreachable")
# include <sys/mman.h>
# include <unistd.h>
#endif
#define XXH_INLINE_ALL 1
#include "third-party/xxhash/xxhash.h"
#ifdef NDEBUG
# define unreachable() __builtin_unreachable()
#else
# define unreachable() assert(0 && "unreachable")
#endif
inline uint64_t hash_string(std::string_view str) {
return XXH3_64bits(str.data(), str.size());
}
class HashCmp {
public:
static size_t hash(const std::string_view &k) {
return hash_string(k);
}
static bool equal(const std::string_view &k1, const std::string_view &k2) {
return k1 == k2;
}
};
namespace mold {
using namespace std::literals::string_literals;
@@ -50,11 +76,16 @@ inline char *output_tmpfile;
inline char *socket_tmpfile;
inline thread_local bool opt_demangle;
extern const std::string mold_version;
inline u8 *output_buffer_start = nullptr;
inline u8 *output_buffer_end = nullptr;
inline std::string mold_version;
extern std::string mold_git_hash;
std::string_view errno_string();
void cleanup();
void install_signal_handler();
i64 get_default_thread_count();
//
// Error output
@@ -159,25 +190,43 @@ private:
// Utility functions
//
// Some C++ libraries haven't implemented std::has_single_bit yet.
inline bool has_single_bit(u64 val) {
return std::popcount(val) == 1;
}
// Some C++ libraries haven't implemented std::bit_ceil yet.
inline u64 bit_ceil(u64 val) {
if (has_single_bit(val))
return val;
return (u64)1 << (64 - std::countl_zero(val));
}
inline u64 align_to(u64 val, u64 align) {
if (align == 0)
return val;
assert(std::popcount(align) == 1);
assert(has_single_bit(align));
return (val + align - 1) & ~(align - 1);
}
inline u64 align_down(u64 val, u64 align) {
assert(std::popcount(align) == 1);
assert(has_single_bit(align));
return val & ~(align - 1);
}
inline u64 next_power_of_two(u64 val) {
assert(val >> 63 == 0);
if (val == 0 || val == 1)
return 1;
return (u64)1 << (64 - std::countl_zero(val - 1));
inline u64 bit(u64 val, i64 pos) {
return (val >> pos) & 1;
};
// Returns [hi:lo] bits of val.
inline u64 bits(u64 val, u64 hi, u64 lo) {
return (val >> lo) & (((u64)1 << (hi - lo + 1)) - 1);
}
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 = {}) {
T old_val = atomic;
@@ -215,14 +264,19 @@ inline void sort(T &vec, U less) {
std::stable_sort(vec.begin(), vec.end(), less);
}
inline i64 write_string(u8 *buf, std::string_view str) {
template <typename T>
inline void remove_duplicates(std::vector<T> &vec) {
vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
}
inline i64 write_string(void *buf, std::string_view str) {
memcpy(buf, str.data(), str.size());
buf[str.size()] = '\0';
*((u8 *)buf + str.size()) = '\0';
return str.size() + 1;
}
template <typename T>
inline i64 write_vector(u8 *buf, const std::vector<T> &vec) {
inline i64 write_vector(void *buf, const std::vector<T> &vec) {
i64 sz = vec.size() * sizeof(T);
memcpy(buf, vec.data(), sz);
return sz;
@@ -236,6 +290,20 @@ inline void encode_uleb(std::vector<u8> &vec, u64 val) {
} while (val);
}
inline void encode_sleb(std::vector<u8> &vec, i64 val) {
for (;;) {
u8 byte = val & 0x7f;
val >>= 7;
bool neg = (byte & 0x40);
if ((val == 0 && !neg) || (val == -1 && neg)) {
vec.push_back(byte);
break;
}
vec.push_back(byte | 0x80);
}
}
inline i64 write_uleb(u8 *buf, u64 val) {
i64 i = 0;
do {
@@ -258,13 +326,26 @@ inline u64 read_uleb(u8 *&buf) {
return val;
}
inline u64 read_uleb(u8 const*&buf) {
return read_uleb(const_cast<u8 *&>(buf));
}
inline u64 read_uleb(std::string_view &str) {
u8 *start = (u8 *)&str[0];
u8 *ptr = start;
u64 val = read_uleb(ptr);
str = str.substr(ptr - start);
return val;
}
inline i64 uleb_size(u64 val) {
i64 i = 0;
do {
i++;
val >>= 7;
} while (val);
return i;
#if __GNUC__
#pragma GCC unroll 8
#endif
for (int i = 1; i < 9; i++)
if (val < ((u64)1 << (7 * i)))
return i;
return 9;
}
template <typename C>
@@ -276,6 +357,14 @@ std::string_view save_string(C &ctx, const std::string &str) {
return {(char *)buf, str.size()};
}
inline bool remove_prefix(std::string_view &s, std::string_view prefix) {
if (s.starts_with(prefix)) {
s = s.substr(prefix.size());
return true;
}
return false;
}
//
// Concurrent Map
//
@@ -305,7 +394,7 @@ public:
void resize(i64 nbuckets) {
this->~ConcurrentMap();
nbuckets = std::max<i64>(MIN_NBUCKETS, next_power_of_two(nbuckets));
nbuckets = std::max<i64>(MIN_NBUCKETS, bit_ceil(nbuckets));
this->nbuckets = nbuckets;
keys = (std::atomic<const char *> *)calloc(nbuckets, sizeof(keys[0]));
@@ -317,7 +406,7 @@ public:
if (!keys)
return {nullptr, false};
assert(std::popcount<u64>(nbuckets) == 1);
assert(has_single_bit(nbuckets));
i64 idx = hash & (nbuckets - 1);
i64 retry = 0;
@@ -376,10 +465,28 @@ private:
};
//
// threads.cc
// output-file.h
//
void set_thread_count(i64 n);
template <typename C>
class OutputFile {
public:
static std::unique_ptr<OutputFile<C>>
open(C &ctx, std::string path, i64 filesize, i64 perm);
virtual void close(C &ctx) = 0;
virtual ~OutputFile() = default;
u8 *buf = nullptr;
std::string path;
i64 filesize;
bool is_mmapped;
bool is_unmapped = false;
protected:
OutputFile(std::string path, i64 filesize, bool is_mmapped)
: path(path), filesize(filesize), is_mmapped(is_mmapped) {}
};
//
// hyperloglog.cc
@@ -407,6 +514,66 @@ private:
std::vector<std::atomic_uint8_t> buckets;
};
//
// glob.cc
//
class Glob {
typedef enum { STRING, STAR, QUESTION, BRACKET } Kind;
struct Element {
Element(Kind k) : kind(k) {}
Kind kind;
std::string str;
std::bitset<256> bitset;
};
public:
static std::optional<Glob> compile(std::string_view pat);
bool match(std::string_view str);
private:
Glob(std::vector<Element> &&vec) : elements(vec) {}
static bool do_match(std::string_view str, std::span<Element> elements);
std::vector<Element> elements;
};
//
// multi-glob.cc
//
class MultiGlob {
public:
bool add(std::string_view pat, u32 val);
bool empty() const { return strings.empty(); }
std::optional<u32> find(std::string_view str);
private:
struct TrieNode {
u32 value = -1;
TrieNode *suffix_link = nullptr;
std::unique_ptr<TrieNode> children[256];
};
void compile();
void fix_suffix_links(TrieNode &node);
void fix_values();
std::vector<std::string> strings;
std::unique_ptr<TrieNode> root;
std::vector<std::pair<Glob, u32>> globs;
std::vector<u32> values;
std::once_flag once;
bool is_compiled = false;
};
//
// uuid.cc
//
std::array<u8, 16> get_uuid_v4();
//
// filepath.cc
//
@@ -425,6 +592,7 @@ std::filesystem::path to_abs_path(std::filesystem::path path);
//
std::string_view demangle(std::string_view name);
std::optional<std::string_view> cpp_demangle(std::string_view name);
//
// compress.cc
@@ -432,7 +600,7 @@ std::string_view demangle(std::string_view name);
class ZlibCompressor {
public:
ZlibCompressor(std::string_view input);
ZlibCompressor(u8 *buf, i64 size);
void write_to(u8 *buf);
i64 size() const;
@@ -590,6 +758,12 @@ public:
// because archive members may have the same name.
return parent->name + ":" + std::to_string(get_offset());
}
if (thin_parent) {
// If this is a thin archive member, the filename part is
// guaranteed to be unique.
return thin_parent->name + ":" + name;
}
return name;
}
@@ -599,7 +773,11 @@ public:
i64 mtime = 0;
bool given_fullpath = true;
MappedFile *parent = nullptr;
MappedFile *thin_parent = nullptr;
int fd = -1;
#ifdef _WIN32
HANDLE file_handle = INVALID_HANDLE_VALUE;
#endif
};
template <typename C>
@@ -607,7 +785,14 @@ MappedFile<C> *MappedFile<C>::open(C &ctx, std::string path) {
if (path.starts_with('/') && !ctx.arg.chroot.empty())
path = ctx.arg.chroot + "/" + path_clean(path);
i64 fd = ::open(path.c_str(), O_RDONLY);
i64 fd;
#ifdef _WIN32
fd = ::_open(path.c_str(), O_RDONLY);
#else
fd = ::open(path.c_str(), O_RDONLY);
#endif
if (fd == -1)
return nullptr;
@@ -621,17 +806,32 @@ MappedFile<C> *MappedFile<C>::open(C &ctx, std::string path) {
mf->name = path;
mf->size = st.st_size;
#ifdef __APPLE__
mf->mtime = (u64)st.st_mtimespec.tv_sec * 1000000000 + st.st_mtimespec.tv_nsec;
#ifdef _WIN32
mf->mtime = st.st_mtime;
#elif defined(__APPLE__)
mf->mtime = (u64)st.st_mtimespec.tv_sec * 1000000000 + st.st_mtimespec.tv_nsec;
#else
mf->mtime = (u64)st.st_mtim.tv_sec * 1000000000 + st.st_mtim.tv_nsec;
mf->mtime = (u64)st.st_mtim.tv_sec * 1000000000 + st.st_mtim.tv_nsec;
#endif
if (st.st_size > 0) {
mf->data = (u8 *)mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
#ifdef _WIN32
HANDLE handle = CreateFileMapping((HANDLE)_get_osfhandle(fd),
nullptr, PAGE_READWRITE, 0,
st.st_size, nullptr);
if (!handle)
Fatal(ctx) << path << ": CreateFileMapping failed: " << GetLastError();
mf->file_handle = handle;
mf->data = (u8 *)MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, st.st_size);
if (!mf->data)
Fatal(ctx) << path << ": MapViewOfFile failed: " << GetLastError();
#else
mf->data = (u8 *)mmap(nullptr, st.st_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE, fd, 0);
if (mf->data == MAP_FAILED)
Fatal(ctx) << path << ": mmap failed: " << errno_string();
}
#endif
}
close(fd);
return mf;
@@ -659,8 +859,16 @@ MappedFile<C>::slice(C &ctx, std::string name, u64 start, u64 size) {
template <typename C>
MappedFile<C>::~MappedFile() {
if (size && !parent)
munmap(data, size);
if (size == 0 || parent)
return;
#ifdef _WIN32
UnmapViewOfFile(data);
if (file_handle != INVALID_HANDLE_VALUE)
CloseHandle(file_handle);
#else
munmap(data, size);
#endif
}
} // namespace mold
+27 -25
View File
@@ -1,34 +1,34 @@
// This file implements the Aho-Corasick algorithm to match version
// script patterns to symbol strings as quickly as possible.
// This file implements the Aho-Corasick algorithm to match multiple
// glob patterns to symbol strings as quickly as possible.
//
// Here are some examples of version script patterns:
// Here are some examples of glob patterns:
//
// qt_private_api_tag*
// *16QAccessibleCache*
// *32QAbstractFileIconProviderPrivate*
// *17QPixmapIconEngine*
//
// The pattern is a glob pattern, so `*` is a wildcard that matches
// any substring. We sometimes have hundreds of version script
// patterns and have to match them against millions of symbol strings.
// `*` is a wildcard that matches any substring. We sometimes have
// hundreds of glob patterns and have to match them against millions
// of symbol strings.
//
// Aho-Corasick cannot handle complex patterns such as `*foo*bar*`.
// We handle such patterns with GlobPattern. GlobPattern is relatively
// slow, but complex patterns are rare in practice, so it should be OK.
// We handle such patterns with the Glob class. Glob is relatively
// slow, but complex patterns are rare in practice, so it should be
// OK.
#include "mold.h"
#include <queue>
#include <regex>
namespace mold::elf {
namespace mold {
std::optional<u16> VersionMatcher::find(std::string_view str) {
std::optional<u32> MultiGlob::find(std::string_view str) {
std::call_once(once, [&] { compile(); });
u32 idx = UINT32_MAX;
if (root) {
std::call_once(once_flag, [&] { compile(); });
// Match against simple glob patterns
TrieNode *node = root.get();
@@ -53,13 +53,13 @@ std::optional<u16> VersionMatcher::find(std::string_view str) {
}
// Match against complex glob patterns
for (std::pair<GlobPattern, u32> &glob : globs)
for (std::pair<Glob, u32> &glob : globs)
if (glob.first.match(str))
idx = std::min(idx, glob.second);
if (idx == UINT32_MAX)
return {};
return versions[idx];
return values[idx];
}
static bool is_simple_pattern(std::string_view pat) {
@@ -82,17 +82,17 @@ static std::string handle_stars(std::string_view pat) {
return "\0"s + str + "\0"s;
}
bool VersionMatcher::add(std::string_view pat, u16 ver) {
assert(!compiled);
bool MultiGlob::add(std::string_view pat, u32 val) {
assert(!is_compiled);
assert(!pat.empty());
u32 idx = strings.size();
strings.push_back(std::string(pat));
versions.push_back(ver);
values.push_back(val);
// Complex glob pattern
if (!is_simple_pattern(pat)) {
if (std::optional<GlobPattern> glob = GlobPattern::compile(pat)) {
if (std::optional<Glob> glob = Glob::compile(pat)) {
globs.push_back({std::move(*glob), idx});
return true;
}
@@ -114,13 +114,15 @@ bool VersionMatcher::add(std::string_view pat, u16 ver) {
return true;
}
void VersionMatcher::compile() {
fix_suffix_links(*root);
fix_values();
compiled = true;
void MultiGlob::compile() {
is_compiled = true;
if (root) {
fix_suffix_links(*root);
fix_values();
}
}
void VersionMatcher::fix_suffix_links(TrieNode &node) {
void MultiGlob::fix_suffix_links(TrieNode &node) {
for (i64 i = 0; i < 256; i++) {
if (!node.children[i])
continue;
@@ -146,7 +148,7 @@ void VersionMatcher::fix_suffix_links(TrieNode &node) {
}
}
void VersionMatcher::fix_values() {
void MultiGlob::fix_values() {
std::queue<TrieNode *> queue;
queue.push(root.get());
@@ -163,4 +165,4 @@ void VersionMatcher::fix_values() {
} while (!queue.empty());
}
} // namespace mold::elf
} // namespace mold
+27 -30
View File
@@ -1,11 +1,12 @@
#include "mold.h"
#include <fcntl.h>
#include <filesystem>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
namespace mold::elf {
namespace mold {
inline u32 get_umask() {
u32 orig_umask = umask(0);
@@ -23,8 +24,10 @@ open_or_create_file(C &ctx, std::string path, i64 filesize, i64 perm) {
if (fd == -1)
Fatal(ctx) << "cannot open " << path2 << ": " << errno_string();
// Reuse an existing file if exists and writable.
if (rename(path.c_str(), path2) == 0) {
// Reuse an existing file if exists and writable because on Linux,
// writing to an existing file is much faster than creating a fresh
// file and writing to it.
if (ctx.overwrite_output_file && rename(path.c_str(), path2) == 0) {
::close(fd);
fd = ::open(path2, O_RDWR | O_CREAT, perm);
if (fd != -1 && !ftruncate(fd, filesize) && !fchmod(fd, perm & ~get_umask()))
@@ -44,11 +47,11 @@ open_or_create_file(C &ctx, std::string path, i64 filesize, i64 perm) {
return {fd, path2};
}
template <typename E>
class MemoryMappedOutputFile : public OutputFile<E> {
template <typename C>
class MemoryMappedOutputFile : public OutputFile<C> {
public:
MemoryMappedOutputFile(Context<E> &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<E>(path, filesize, true) {
MemoryMappedOutputFile(C &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<C>(path, filesize, true) {
i64 fd;
std::tie(fd, output_tmpfile) = open_or_create_file(ctx, path, filesize, perm);
@@ -57,9 +60,12 @@ public:
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;
}
void close(Context<E> &ctx) override {
void close(C &ctx) override {
Timer t(ctx, "close_file");
if (!this->is_unmapped)
@@ -71,18 +77,18 @@ public:
}
};
template <typename E>
class MallocOutputFile : public OutputFile<E> {
template <typename C>
class MallocOutputFile : public OutputFile<C> {
public:
MallocOutputFile(Context<E> &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<E>(path, filesize, false), perm(perm) {
MallocOutputFile(C &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<C>(path, filesize, false), perm(perm) {
this->buf = (u8 *)mmap(NULL, filesize, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (this->buf == MAP_FAILED)
Fatal(ctx) << "mmap failed: " << errno_string();
}
void close(Context<E> &ctx) override {
void close(C &ctx) override {
Timer t(ctx, "close_file");
if (this->path == "-") {
@@ -104,9 +110,9 @@ private:
i64 perm;
};
template <typename E>
std::unique_ptr<OutputFile<E>>
OutputFile<E>::open(Context<E> &ctx, std::string path, i64 filesize, i64 perm) {
template <typename C>
std::unique_ptr<OutputFile<C>>
OutputFile<C>::open(C &ctx, std::string path, i64 filesize, i64 perm) {
Timer t(ctx, "open_file");
if (path.starts_with('/') && !ctx.arg.chroot.empty())
@@ -121,24 +127,15 @@ OutputFile<E>::open(Context<E> &ctx, std::string path, i64 filesize, i64 perm) {
is_special = true;
}
std::unique_ptr<OutputFile<E>> file;
OutputFile<C> *file;
if (is_special)
file = std::make_unique<MallocOutputFile<E>>(ctx, path, filesize, perm);
file = new MallocOutputFile<C>(ctx, path, filesize, perm);
else
file = std::make_unique<MemoryMappedOutputFile<E>>(ctx, path, filesize, perm);
file = new MemoryMappedOutputFile<C>(ctx, path, filesize, perm);
if (ctx.arg.filler != -1)
memset(file->buf, ctx.arg.filler, filesize);
return file;
return std::unique_ptr<OutputFile<C>>(file);
}
#define INSTANTIATE(E) \
template class OutputFile<E>;
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
} // namespace mold
+57
View File
@@ -0,0 +1,57 @@
#include "mold.h"
#include <fcntl.h>
#include <filesystem>
#include <windows.h>
namespace mold {
template <typename C>
class MallocOutputFile : public OutputFile<C> {
public:
MallocOutputFile(C &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<C>(path, filesize, false), perm(perm) {
this->buf = (u8 *)malloc(filesize);
if (!this->buf)
Fatal(ctx) << "malloc failed";
}
void close(C &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 C>
std::unique_ptr<OutputFile<C>>
OutputFile<C>::open(C &ctx, std::string path, i64 filesize, i64 perm) {
Timer t(ctx, "open_file");
if (path.starts_with('/') && !ctx.arg.chroot.empty())
path = ctx.arg.chroot + "/" + path_clean(path);
OutputFile<C> *file = new MallocOutputFile<C>(ctx, path, filesize, perm);
if (ctx.arg.filler != -1)
memset(file->buf, ctx.arg.filler, filesize);
return std::unique_ptr<OutputFile<C>>(file);
}
} // namespace mold
+5
View File
@@ -0,0 +1,5 @@
#if _WIN32
# include "output-file-win32.h"
#else
# include "output-file-unix.h"
#endif
+31 -12
View File
@@ -3,8 +3,11 @@
#include <functional>
#include <iomanip>
#include <ios>
#ifndef _WIN32
#include <sys/resource.h>
#include <sys/time.h>
#endif
namespace mold {
@@ -23,24 +26,39 @@ void Counter::print() {
}
static i64 now_nsec() {
#ifdef _WIN32
return (i64)std::chrono::steady_clock::now().time_since_epoch().count();
#else
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
return (i64)t.tv_sec * 1000000000 + t.tv_nsec;
#endif
}
static i64 to_nsec(struct timeval t) {
return (i64)t.tv_sec * 1000000000 + t.tv_usec * 1000;
static std::pair<i64, i64> get_usage() {
#ifdef _WIN32
auto to_nsec = [](FILETIME t) -> i64 {
return ((u64)t.dwHighDateTime << 32 + (u64)t.dwLowDateTime) * 100;
};
FILETIME creation, exit, kernel, user;
GetProcessTimes(GetCurrentProcess(), &creation, &exit, &kernel, &user);
return {to_nsec(user), to_nsec(kernel)};
#else
auto to_nsec = [](struct timeval t) -> i64 {
return (i64)t.tv_sec * 1000000000 + t.tv_usec * 1000;
};
struct rusage ru;
getrusage(RUSAGE_SELF, &ru);
return {to_nsec(ru.ru_utime), to_nsec(ru.ru_stime)};
#endif
}
TimerRecord::TimerRecord(std::string name, TimerRecord *parent)
: name(name), parent(parent) {
struct rusage usage;
getrusage(RUSAGE_SELF, &usage);
start = now_nsec();
user = to_nsec(usage.ru_utime);
sys = to_nsec(usage.ru_stime);
std::tie(user, sys) = get_usage();
if (parent)
parent->children.push_back(this);
}
@@ -50,12 +68,13 @@ void TimerRecord::stop() {
return;
stopped = true;
struct rusage usage;
getrusage(RUSAGE_SELF, &usage);
i64 user2;
i64 sys2;
std::tie(user2, sys2) = get_usage();
end = now_nsec();
user = to_nsec(usage.ru_utime) - user;
sys = to_nsec(usage.ru_stime) - sys;
user = user2 - user;
sys = sys2 - sys;
}
static void print_rec(TimerRecord &rec, i64 indent) {
+73
View File
@@ -1,3 +1,52 @@
#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>
@@ -6,3 +55,27 @@
# 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
+4
View File
@@ -13,7 +13,11 @@ namespace mold {
std::string_view errno_string() {
static thread_local char buf[200];
#ifdef _WIN32
strerror_s(buf, errno);
#else
strerror_r(errno, buf, sizeof(buf));
#endif
return buf;
}
-15
View File
@@ -1,15 +0,0 @@
TESTS = $(wildcard macho/*.sh)
test: $(TESTS)
# macOS's GNU make hasn't been updated since 3.8.1 perhaps due a concern
# of GPLv3. The --output-sync flag was introduced in GNU Make 4.0, so we
# can't use that flag on macOS.
#
# `tail -r | tail -r` is a poor-man's way to enable full buffering on a
# command output. `tail -r` outputs an input from the last line to the
# first.
$(TESTS):
@./$@ 2>&1 | tail -r | tail -r
.PHONY: test $(TESTS)
-8
View File
@@ -1,8 +0,0 @@
TESTS = $(wildcard elf/*.sh)
test: $(TESTS)
$(TESTS):
@./$@
.PHONY: test $(TESTS)
+43
View File
@@ -0,0 +1,43 @@
option(MOLD_ENABLE_QEMU_TESTS "Enable tests on non-native targets" OFF)
option(MOLD_ENABLE_QEMU_TESTS_RV32 "Enable tests on RV32" OFF)
file(GLOB TESTS
RELATIVE ${CMAKE_CURRENT_LIST_DIR}
CONFIGURE_DEPENDS
"*.sh")
function(add_qemu_test TEST TRIPLE MACHINE)
if(NOT ${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL ${MACHINE})
add_test(NAME "${MACHINE}-${TEST}"
COMMAND bash -x ${CMAKE_CURRENT_LIST_DIR}/${TEST}
WORKING_DIRECTORY ${mold_BINARY_DIR})
set(ENV
TEST_CC=${TRIPLE}-gcc
TEST_CXX=${TRIPLE}-g++
TEST_GCC=${TRIPLE}-gcc
TEST_GXX=${TRIPLE}-g++
OBJDUMP=${TRIPLE}-objdump
MACHINE=${MACHINE}
"QEMU=qemu-${MACHINE} -L /usr/${TRIPLE}")
set_tests_properties("${MACHINE}-${TEST}" PROPERTIES ENVIRONMENT "${ENV}")
endif()
endfunction()
foreach(TEST IN LISTS TESTS)
add_test(NAME "${CMAKE_HOST_SYSTEM_PROCESSOR}-${TEST}"
COMMAND bash -x ${CMAKE_CURRENT_LIST_DIR}/${TEST}
WORKING_DIRECTORY ${mold_BINARY_DIR})
if(MOLD_ENABLE_QEMU_TESTS)
add_qemu_test(${TEST} x86_64-linux-gnu x86_64)
add_qemu_test(${TEST} i686-linux-gnu i386)
add_qemu_test(${TEST} aarch64-linux-gnu aarch64)
add_qemu_test(${TEST} arm-linux-gnueabihf arm)
add_qemu_test(${TEST} riscv64-linux-gnu riscv64)
endif()
if (MOLD_ENABLE_QEMU_TESTS_RV32)
add_qemu_test(${TEST} riscv32-linux-gnu riscv32)
endif()
endforeach()
+31
View File
@@ -0,0 +1,31 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
t=out/test/elf/$MACHINE/$testname
mkdir -p $t
[ $MACHINE = aarch64 ] && { echo skipped; exit; }
cat <<EOF | $CC -fPIC -c -o $t/a.o -xassembler -
.globl foo
foo = 3;
EOF
cat <<EOF | $CC -fno-PIC -c -o $t/b.o -xc -
#include <stdio.h>
extern char foo;
int main() { printf("foo=%p\n", &foo); }
EOF
! $CC -B. -o $t/exe -pie $t/a.o $t/b.o -Wl,-z,text >& $t/log
grep -q 'recompile with -fPIC' $t/log
echo OK
+5 -7
View File
@@ -1,17 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
t=out/test/elf/$MACHINE/$testname
mkdir -p $t
[ $MACHINE = x86_64 ] || { echo skipped; exit; }
+10 -12
View File
@@ -1,24 +1,22 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
t=out/test/elf/$MACHINE/$testname
mkdir -p $t
echo 'int main() { return 0; }' > $t/a.c
echo 'int main() { return 0; }' > $t/b.c
echo 'int main() { return 0; }' | $CC -c -o $t/a.o -xc -
echo 'int main() { return 1; }' | $CC -c -o $t/b.o -xc -
! $CC -B. -o $t/exe $t/a.c $t/b.c 2> /dev/null || false
$CC -B. -o $t/exe $t/a.c $t/b.c -Wl,-allow-multiple-definition
$CC -B. -o $t/exe $t/a.c $t/b.c -Wl,-z,muldefs
! $CC -B. -o $t/exe $t/a.o $t/b.o 2> /dev/null || false
$CC -B. -o $t/exe $t/a.o $t/b.o -Wl,-allow-multiple-definition
$CC -B. -o $t/exe $t/a.o $t/b.o -Wl,-z,muldefs
echo OK
+5 -7
View File
@@ -1,17 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
t=out/test/elf/$MACHINE/$testname
mkdir -p $t
cat <<EOF | $CC -o $t/a.o -c -xc -
+45
View File
@@ -0,0 +1,45 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
t=out/test/elf/$MACHINE/$testname
mkdir -p $t
[[ $MACHINE == arm* ]] || { echo skipped; exit; }
echo 'int foo() { return 0; }' | $CC -o /dev/null -c -xc - -mthumb 2> /dev/null || { echo skipped; exit; }
cat <<EOF | $CC -o $t/a.o -c -xc - -mthumb
#include <stdio.h>
int bar();
int foo() {
printf(" foo");
bar();
}
EOF
cat <<EOF | $CC -o $t/b.o -c -xc - -marm
#include <stdio.h>
int bar() {
printf(" bar\n");
}
int foo();
int main() {
printf("main");
foo();
}
EOF
$CC -B. -o $t/exe $t/a.o $t/b.o
$QEMU $t/exe | grep -q 'main foo bar'
echo OK
+5 -7
View File
@@ -1,17 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
t=out/test/elf/$MACHINE/$testname
mkdir -p $t
cat <<EOF | $CC -fPIC -o $t/a.o -c -xc -
+5 -7
View File
@@ -1,17 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
t=out/test/elf/$MACHINE/$testname
mkdir -p $t
cat <<EOF | $CC -o $t/a.o -c -xc -
+5 -7
View File
@@ -1,17 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
t=out/test/elf/$MACHINE/$testname
mkdir -p $t
cat <<EOF | $CC -shared -fPIC -o $t/a.so -xc -
+6 -8
View File
@@ -1,17 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
t=out/test/elf/$MACHINE/$testname
mkdir -p $t
cat <<EOF | $CC -o $t/a.o -c -x assembler -
@@ -21,7 +19,7 @@ _start:
nop
EOF
"$mold" -o $t/b.so $t/a.o -auxiliary foo -f bar -shared
./mold -o $t/b.so $t/a.o -auxiliary foo -f bar -shared
readelf --dynamic $t/b.so > $t/log
fgrep -q 'Auxiliary library: [foo]' $t/log
+6 -8
View File
@@ -1,24 +1,22 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
t=out/test/elf/$MACHINE/$testname
mkdir -p $t
[ $MACHINE = x86_64 ] || { echo skipped; exit; }
echo '.globl _start; _start: jmp loop' | $CC -o $t/a.o -c -x assembler -
echo '.globl loop; loop: jmp loop' | $CC -o $t/b.o -c -x assembler -
"$mold" -static -o $t/exe $t/a.o $t/b.o
./mold -static -o $t/exe $t/a.o $t/b.o
$OBJDUMP -d $t/exe > /dev/null
file $t/exe | grep -q ELF
+5 -7
View File
@@ -1,17 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
t=out/test/elf/$MACHINE/$testname
mkdir -p $t
cat <<EOF | $CC -c -fPIC -o$t/a.o -xc -
+5 -7
View File
@@ -1,17 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
t=out/test/elf/$MACHINE/$testname
mkdir -p $t
cat <<EOF | $CC -c -o $t/a.o -fPIC -xc -
+5 -7
View File
@@ -1,17 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
t=out/test/elf/$MACHINE/$testname
mkdir -p $t
cat <<EOF | $CC -c -fPIC -o$t/a.o -xc -
+6 -8
View File
@@ -1,17 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
t=out/test/elf/$MACHINE/$testname
mkdir -p $t
# Verify that mold does not crash if no object file is included
@@ -26,6 +24,6 @@ EOF
rm -f $t/a.a
ar rcs $t/a.a $t/a.o
"$mold" -o $t/exe $t/a.a
./mold -o $t/exe $t/a.a
echo OK
+5 -7
View File
@@ -1,17 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
t=out/test/elf/$MACHINE/$testname
mkdir -p $t
echo 'int main() { return 0; }' > $t/a.c
+5 -7
View File
@@ -1,17 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
t=out/test/elf/$MACHINE/$testname
mkdir -p $t
cat <<EOF | $CC -o $t/a.so -fPIC -shared -xc -
+9 -11
View File
@@ -1,22 +1,20 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
t=out/test/elf/$MACHINE/$testname
mkdir -p $t
(! "$mold" -zfoo) 2>&1 | grep -q 'unknown command line option: -zfoo'
(! "$mold" -z foo) 2>&1 | grep -q 'unknown command line option: -z foo'
(! "$mold" -abcdefg) 2>&1 | grep -q 'unknown command line option: -abcdefg'
(! "$mold" --abcdefg) 2>&1 | grep -q 'unknown command line option: --abcdefg'
{ ./mold -zfoo || true; } 2>&1 | grep -q 'unknown command line option: -zfoo'
{ ./mold -z foo || true; } 2>&1 | grep -q 'unknown command line option: -z foo'
{ ./mold -abcdefg || true; } 2>&1 | grep -q 'unknown command line option: -abcdefg'
{ ./mold --abcdefg || true; } 2>&1 | grep -q 'unknown command line option: --abcdefg'
echo OK
+13 -15
View File
@@ -1,17 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
t=out/test/elf/$MACHINE/$testname
mkdir -p $t
cat <<EOF | $CC -o $t/a.o -c -xc -
@@ -19,16 +17,16 @@ int foo();
int main() { foo(); }
EOF
! "$mold" -o $t/exe $t/a.o --color-diagnostics 2> $t/log
grep -q $'\033' $t/log
! "$mold" -o $t/exe $t/a.o --color-diagnostics=always 2> $t/log
grep -q $'\033' $t/log
! "$mold" -o $t/exe $t/a.o --color-diagnostics=never 2> $t/log
! ./mold -o $t/exe $t/a.o --color-diagnostics 2> $t/log
! grep -q $'\033' $t/log || false
! "$mold" -o $t/exe $t/a.o --color-diagnostics=auto 2> $t/log
! ./mold -o $t/exe $t/a.o --color-diagnostics=always 2> $t/log
grep -q $'\033' $t/log
! ./mold -o $t/exe $t/a.o --color-diagnostics=never 2> $t/log
! grep -q $'\033' $t/log || false
! ./mold -o $t/exe $t/a.o --color-diagnostics=auto 2> $t/log
! grep -q $'\033' $t/log || false
echo OK
+5 -7
View File
@@ -1,17 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
t=out/test/elf/$MACHINE/$testname
mkdir -p $t
cat <<EOF | $CC -c -o $t/a.o -xc -
+5 -7
View File
@@ -1,17 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
t=out/test/elf/$MACHINE/$testname
mkdir -p $t
cat <<EOF | $CC -fcommon -xc -c -o $t/a.o -
Loaded 100 of 740 files, more files were not shown because too many files have changed in this diff. Show more