Compare commits

...
374 Commits
Author SHA1 Message Date
Rui Ueyama f86868cb38 Bump mold version to 1.2.0 2022-04-15 17:08:14 +08:00
Rui Ueyama 421289445a Remove dist.sh's output on make clean 2022-04-15 17:08:14 +08:00
Rui Ueyama 100922b650 [ELF] Fix section file offsets
Previously, if a section has a very large alignment requirement,
that section and the following sections may get wrong file offsets.

Fixes https://github.com/rui314/mold/issues/405
2022-04-15 14:59:09 +08:00
Rui Ueyama 0daf6233fb [ELF] Fix R_RISCV_ALIGN relocations
Fixes https://github.com/rui314/mold/issues/419
2022-04-15 14:37:55 +08:00
Rui Ueyama d0e4eee204 [ELF] Print out a warning for .note.GNU-stack
https://github.com/rui314/mold/issues/427
2022-04-14 11:47:54 +08:00
Rui Ueyama a7475dd5e3 [ELF] Support --gdb-index
This is a tough one because .gdb_index, .debug_gnu_pubnames,
.debug_gnu_pubtypes and DWARF are underdocumented, and DWARF is
complicated even if you have a right documentation. But, I believe I
managed to create a correct .gdb_index section.

Just like ld.lld, mold's --gdb-index needs all input object files to
have been compiled with -ggnu-pubnames. We read symbol names and type
names from the sections generated by -ggnu-pubnames.

Unlike ld.gold and ld.lld, we do not use an external library to read
DWARF debug info records.

As always, this feature is implemented with speed in mind. For Clang
15 which is built with -ggnu-pubnames, mold takes ~150 ms to create a
~300 MiB .gdb_index section on a simulated 16-core machine.

Fixes https://github.com/rui314/mold/issues/396
2022-04-13 19:01:17 +08:00
Rui Ueyama c4af13889d Refactor 2022-04-10 11:03:53 +08:00
Rui Ueyama 7aaa4afce7 Refactor 2022-04-10 10:06:41 +08:00
Rui Ueyama 2fb96ad83f Ignore tar file for mold binary distribution 2022-04-10 09:09:29 +08:00
Rui Ueyama a2fa4e5ecc Update README 2022-04-10 09:08:05 +08:00
Rui Ueyama 68877ab6ca Use mold in our CI 2022-04-10 08:51:51 +08:00
Rui Ueyama f62777e606 Merge pull request #424 from yurai007/fix-memory-leak
Fix memory leak caught by Memcheck
2022-04-09 19:52:07 +08:00
Dawid Jurczak c80e316649 Fix memory leak caught by Memcheck
This patch fix memory leak reported by Memcheck while running some tests.
The problem happen even with --no-quick-exit option.
Before change there are blocks definitely and indirectly lost seen in Memcheck summary:

==1202== LEAK SUMMARY:
==1202==    definitely lost: 1,120 bytes in 14 blocks
==1202==    indirectly lost: 633 bytes in 12 blocks
==1202==      possibly lost: 56,408 bytes in 46 blocks
==1202==    still reachable: 128 bytes in 1 blocks
==1202==         suppressed: 0 bytes in 0 blocks

After change number of such blocks drops to zero:

==2137== LEAK SUMMARY:
==2137==    definitely lost: 0 bytes in 0 blocks
==2137==    indirectly lost: 0 bytes in 0 blocks
==2137==      possibly lost: 56,408 bytes in 46 blocks
==2137==    still reachable: 128 bytes in 1 blocks
==2137==         suppressed: 0 bytes in 0 blocks

Signed-off-by: Dawid Jurczak <[email protected]>
2022-04-09 13:30:21 +02:00
Rui Ueyama d1b9549817 [ELF] Do not ignore --dynamic-linker even if --shared is given
It is very rare, but technically you can create a shared object that
can also be executed directly. You can do that by giving a dynamic
linker path to the linker.

https://github.com/rui314/mold/issues/422
2022-04-09 18:35:04 +08:00
Rui Ueyama 01d2dd8dd9 Allow to pass extra CXXFLAGS 2022-04-09 15:39:32 +08:00
Rui Ueyama 6109154a07 Refactor 2022-04-09 15:24:51 +08:00
Rui Ueyama 49d4dac7e6 Push a build environment to DockerHub 2022-04-09 14:43:16 +08:00
Rui Ueyama 87a5f238dd Fix CI 2022-04-09 13:18:22 +08:00
Rui Ueyama e448e0eb2b [ELF] Write tombstone values to dead debug info records
This is an attempt to fix https://github.com/rui314/mold/issues/357.
2022-04-09 10:40:25 +08:00
Rui Ueyama 4df5d6497a Break down test-all into smaller targets 2022-04-09 10:21:18 +08:00
Rui Ueyama e90ad9be16 Build OpenSSL 3 only once
dist.sh compiles OpenSSL 3 for a license reason. We don't need to
build it every time the script is run.
2022-04-08 19:04:19 +08:00
Rui Ueyama b5788e0caa [ELF] Document new command line options 2022-04-08 18:37:29 +08:00
Rui Ueyama 9178e43681 Add comments 2022-04-08 14:36:59 +08:00
Rui Ueyama 5feab826de [ELF] Fix _TLS_MODULE_BASE_ relocations
Fixes https://github.com/rui314/mold/issues/421
2022-04-08 13:34:43 +08:00
Rui Ueyama 2fc0231348 Update comments 2022-04-07 16:22:07 +08:00
Rui Ueyama 804ae412ed [ELF][ARM64] Do not create a canonical PLT for PIE
Position-independent executable shouldn't have a canonical PLT.
2022-04-07 16:00:27 +08:00
Rui Ueyama f0a030cbcd [ELF] Add --disable-new-dtags
Fixes https://github.com/rui314/mold/issues/420
2022-04-07 15:26:59 +08:00
Rui Ueyama ba6347996b [ELF] Implement a workaround for libbacktrace
This commit enables mold to work with libbacktrace before 2022-04-06.

https://github.com/rui314/mold/issues/402
2022-04-07 14:35:01 +08:00
Rui Ueyama a151f4180f Refactor 2022-04-07 14:21:49 +08:00
Rui Ueyama 2c947ce2a9 [ELF][X86_64] Allow GOTPC32_TLSDESC to take registers other than %rax 2022-04-07 11:26:06 +08:00
Rui Ueyama 63b7102a4c [ELF] Define _TLS_MODULE_BASE_
This symbol is used by some object files that use TLSDESC.

Fixes https://github.com/rui314/mold/issues/421
2022-04-07 11:25:17 +08:00
Rui Ueyama 731dd0ed25 [ELF] Support --oformat=binary
With this change, you can link a simple bootloader using mold.
To do so, compile bootloader source files to object files with
the `-fno-PIC` flag and then invoke mold on the object files with
`-Ttext=<bootloader-start-address>` and `--oformat=binary`.

The .text segment is usually at the beginning of the output file if
the file is created that way. By passing the startup code as the first
file argument to the linker, you can place that startup code at the
beginning of the file.

Fixes https://github.com/rui314/mold/issues/418
2022-04-06 19:13:43 +08:00
Rui Ueyama 3105195364 [ELF] Make -Map to output address in hex 2022-04-06 19:13:43 +08:00
Rui Ueyama 23045c6f95 [ELF] Do not create synthetic sections if not necessary 2022-04-06 18:04:42 +08:00
Rui Ueyama 2d29f234e2 [ELF] Change output section layout 2022-04-06 16:35:38 +08:00
Rui Ueyama 4edaf82691 Fix CI 2022-04-06 15:03:37 +08:00
Rui Ueyama 49aa10318c [ELF] Add --section-start 2022-04-06 14:45:31 +08:00
Rui Ueyama 2c03ed5673 Add comments 2022-04-06 11:08:29 +08:00
Rui Ueyama e0bc74ad8a [ELF] Create canonical PLTs only when needed
Previously, we make all PLT entries canonical if we are creating
a position dependent executable, because I was thinking that promoting
usual PLT entries to canonical ones is harmless; symbol equality still
holds.

However, it looks like Qt depends on the usual linker's behavior not
to make PLT canonical if not necessary. I believe they are maintaining
some hidden symbol as aliases for exported symbols and compare their
addresses at runtime.

Of course this doesn't work if your program is not compiled with -fPIC,
but qt5/QtCore/qglobal.h has a macro to abort compilation if PIC is
disabled. (They check for __PIC__ and __PIE__ macros.) So, all object
files are guaranteed to be compiled with -fPIC if they are using QT
functions, and they assume that the linker doesn't create a canonical
PLT for Qt functions.

This commit makes mold to create canonical PLTs only when needed.
That is, if an address of a function is directly taken (i.e. not via
GOT), then mold makes its PLT canonical.

Fixes https://github.com/rui314/mold/issues/352
2022-04-05 21:02:42 +08:00
Rui Ueyama 3768ca612e [ELF] Deprecate --preload
It looks like no one is using --preload, so there's no point to
maintain this feature.

https://github.com/rui314/mold/issues/414
2022-04-05 12:41:50 +08:00
Rui Ueyama 86d59c29eb [ELF][ARM32] Handle TLS_LDO32 relocations in non-alloc sections
Fixes https://github.com/rui314/mold/issues/417
2022-04-05 04:22:16 +00:00
Rui Ueyama 96a4a1b635 [ELF][ARM64] Support non-NC variant of MOVW_UABS relocations
We need to implement an overflow check for non-NC relocations,
but for now they are just aliases for NC variants.

Fixes https://github.com/rui314/mold/issues/416
2022-04-05 02:50:43 +00:00
Rui Ueyama 006b13aec1 Refactor 2022-04-04 17:26:11 +08:00
Rui Ueyama 91947405fe Attempt to silence CI 2022-04-04 17:06:47 +08:00
Rui Ueyama b7123bded5 [ELF] Fix tests for non-x86-64 targets 2022-04-04 16:36:43 +08:00
Rui Ueyama 0884f279e0 [ELF][x86] Always create .got.plt
glibc's -static-pie implementation expect that _GLOBAL_OFFSET_TABLE_
is at the beginning of .got.plt, as it uses the address of _DYNAMIC
which is stored at _GLOBAL_OFFSET_TABLE_[0].

In this patch, we always create .got.plt for x86-64 and i386.
2022-04-04 16:35:22 +08:00
Rui Ueyama 615a24d1dd Add comments 2022-04-03 19:12:10 +08:00
Rui Ueyama e1e4e9fd9e Fix data races found by ThreadSanitizer 2022-04-03 15:08:57 +08:00
Rui Ueyama ceed69ff49 [ELF] Fix crash bug in ICF
Fixes https://github.com/rui314/mold/issues/398
2022-04-03 13:41:13 +08:00
Rui Ueyama d90f7e162b [ELF] Improve error message
Fixes https://github.com/rui314/mold/issues/412
2022-04-03 12:31:31 +08:00
Rui Ueyama 340528bdc3 Update mimalloc to v2.0.5
Fixes https://github.com/rui314/mold/issues/281
2022-04-03 12:00:23 +08:00
Rui Ueyama 392781a0e2 [ELF][i386] Sort .ctors and .dtors sections 2022-04-03 10:43:22 +08:00
Rui Ueyama 4e10ee7e50 [ELF] Run tests for all targets on CI 2022-04-02 21:27:45 +08:00
Rui Ueyama 8d94fd6ca3 [ELF][ARM64] Fix a test 2022-04-02 18:05:05 +08:00
Rui Ueyama 358f244247 [ELF][RISCV64] Fix a test 2022-04-02 18:00:23 +08:00
Rui Ueyama 7a1cbb9b34 [ELF][ARM32] Support R_ARM_THM_JUMP11 2022-04-02 17:35:55 +08:00
Rui Ueyama e6d4154688 [ELF] Generalize tests
With this change, you can now cross compile test cases and run
them on qemu-user. Here is an example to run our test suits in
an emulated ARM32 environment.

$ 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 -j16 test
2022-04-02 16:53:41 +08:00
Rui Ueyama 60eae9ed20 [ELF][ARM32] Support R_ARM_GOT_PREL 2022-04-02 01:46:48 +00:00
Rui Ueyama 7fb1b1b9ec Merge pull request #399 from alois31/r_aarch64_ld_prel_lo19
[ELF][ARM64] Support R_AARCH64_LD_PREL_LO19 relocations
2022-04-01 13:44:39 +08:00
Rui Ueyama 0ecd72900e Attempt to fix CI 2022-04-01 13:32:01 +08:00
Rui Ueyama 9eb4e76daf Attempt to fix CI 2022-04-01 13:20:17 +08:00
Rui Ueyama b6876b5640 Update comment 2022-04-01 13:12:27 +08:00
Rui Ueyama f4753b34bf [ELF] Fix use-after-free found by ASAN 2022-04-01 13:12:10 +08:00
Rui Ueyama 8196199cac Refactor 2022-04-01 12:52:21 +08:00
Rui Ueyama 2a3ee11b7d Merge pull request #400 from nick-rawlings/parse_unsigned_number
image-base i64 to u64 parse_unsigned_number
2022-04-01 12:36:44 +08:00
Rui Ueyama 68f2a26987 Refactor 2022-04-01 02:49:57 +00:00
Rui Ueyama 227a6c8a88 [ELF][ARM32] Fix TLSDESC relocations 2022-04-01 02:47:29 +00:00
Rui Ueyama fe617c3618 [ELF][ARM32] Support R_ARM_MOVW_ABS_NC and R_ARM_MOVT_ABS 2022-04-01 02:47:29 +00:00
Rui Ueyama e3a7c9e017 [ELF][RISCV] Disable a test that doesn't work with RISCV64 2022-03-31 13:24:34 +00:00
Rui Ueyama 5ee6f89544 Revert "Attempt to fix a build issue with libc++14"
This reverts commit 8e3cee958b
because it broke the RISC-V target.
2022-03-31 13:24:19 +00:00
Rui Ueyama 40c2ad7839 [ELF][RISCV64] Fix tests 2022-03-31 08:16:07 +00:00
Rui Ueyama 5edd07efd9 Refactor 2022-03-31 01:47:56 +00:00
Rui Ueyama 8e5def28ce Refactor 2022-03-31 00:46:33 +00:00
Rui Ueyama b534d5af27 Add comments 2022-03-30 14:11:34 +00:00
Rui Ueyama 673f531adb Add comments 2022-03-30 13:57:17 +00:00
Rui Ueyama 66d3de438c Attempt to fix CI 2022-03-30 09:36:38 +00:00
Rui Ueyama d9ea875677 [ELF][ARM32] Implement many relocations
With this change, most tests in test/elf/ directory pass on ARM32.
2022-03-30 09:26:32 +00:00
Rui Ueyama 577361478a Fix CI
This reverts commit 408e2bfee8.
2022-03-29 11:23:30 +00:00
Rui Ueyama 408e2bfee8 Improve tests 2022-03-29 06:47:17 +00:00
Rui Ueyama 9ac23730b7 Merge pull request #408 from yurai007/fix-some-ubsan-errors
[ELF] Fix issues caught by UBSan
2022-03-28 16:39:24 +08:00
Dawid Jurczak bf26753bbd [ELF] Fix issues caught by UBSan
While running tests on UBSan instrumented mold binary 2 separated problems are reported:

SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior elf/main.cc:592:15 in
elf/output-chunks.cc:1866:24: runtime error: member call on null pointer of type 'mold::elf::VerdefSection<mold::elf::X86_64> *'

SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior elf/output-chunks.cc:1866:24 in
elf/input-files.cc:1000:8: runtime error: load of value 190, which is not a valid value for type 'bool'

This change fixes both of them.

Signed-off-by: Dawid Jurczak <[email protected]>
2022-03-28 09:43:28 +02:00
Rui Ueyama 8e3cee958b Attempt to fix a build issue with libc++14
https://github.com/rui314/mold/issues/407
2022-03-27 08:15:25 +00:00
Rui Ueyama 05f4a43c8f [ELF][ARM64] Allow TLSGD relocations in statically-linked executables 2022-03-27 07:48:42 +00:00
Rui Ueyama ca227fb8da Refactor 2022-03-27 06:58:28 +00:00
Rui Ueyama 78e4b06cb8 Fix a regression 2022-03-26 01:19:28 +00:00
Rui Ueyama 94cace7fc1 Refactor 2022-03-25 11:46:34 +00:00
Rui Ueyama 812c59fd5c Refactor 2022-03-23 15:00:29 +00:00
Rui Ueyama 8770bd775b [ELF][ARM] Fix test 2022-03-23 14:54:26 +00:00
Rui Ueyama 89d43f2673 Fix test
So that this test works on ARM32.
2022-03-23 14:41:40 +00:00
Rui Ueyama 632a2ec1b4 [ELF][ARM] Set e_flags to EABI version 5
All binaries should have this version now. GNU binutils do not allow
users to link EABI version 5 object fiels to EABI version 0 DSOs.
2022-03-23 14:38:06 +00:00
Rui Ueyama 6af78bf17b [ELF][ARM32] Do not keep .ARM.attributes sections
ARM32 object files usually contain a .ARM.attributes section.
We need to parse and merge .ARM.attributes sections.
But for now, we just remove them from inputs.
2022-03-23 14:11:14 +00:00
Rui Ueyama 9360267cc3 Refactor 2022-03-23 11:18:27 +00:00
Rui Ueyama 66b995b5a9 [ELF][ARM32] Remove unsupported relocations 2022-03-23 09:11:31 +00:00
Rui Ueyama 94e2d7d6dc [ELF][ARM32] Fix R_ARM_THM_CALL relocation 2022-03-23 08:43:27 +00:00
Rui Ueyama 759aa95618 [ELF][ARM32] Fix R_ARM_THM_MOVT_ABS relocation 2022-03-23 03:12:29 +00:00
Rui Ueyama 575a604d5d [ELF][ARM32] Handle relocations against non-alloc sections 2022-03-23 01:44:19 +00:00
Rui Ueyama ac9a1c4048 [ELF][ARM32] Add a link to a documentation 2022-03-23 00:50:26 +00:00
Rui Ueyama d5cf590498 [ELF][ARM32] Support basic relocations
With this change, mold can link dynamically-linked "Hello world"
program for ARM32.
2022-03-22 12:52:07 +00:00
Rui Ueyama 18656091f6 Rename a test 2022-03-22 12:24:10 +00:00
Rui Ueyama ead32811b3 Fix a test for AArch64
Currently, we do not support relaxations of TLS GD/LD relocations
for ARM64. This is in practice not a problem because TLSDESC is used
on ARM64 by default.

However, there's one situation in which not supporting TLS GD/LD
relaxation becomes an issue; static linking. If an executable is
statically-linked, relaxation is mandatory because we can't generate
dynamic relocations for GD/LD relocations.

In this patch, we simply report an error if an object file compiled
with TLS GD/LD is used for creating a statically-linked executable.

This commit fixes all tests on ARM64.
2022-03-21 02:49:50 +00:00
Rui Ueyama 7cdfa31e6c Refactor 2022-03-20 14:21:46 +00:00
Rui Ueyama a6e4676a71 Merge pull request #401 from yurai007/do-not-hardcode-GCC-version-in-tests
[ELF][RISC-V] Do not hardcode GCC version in tests
2022-03-20 09:25:43 +08:00
Dawid Jurczak 0c88d323c8 [ELF][RISC-V] Do not hardcode GCC version in tests
Signed-off-by: Dawid Jurczak <[email protected]>
2022-03-19 17:22:03 +01:00
Rui Ueyama 326150a182 Merge pull request #390 from mrichtarsky/main
Improve help
2022-03-19 22:39:52 +08:00
nick-rawlings 339816702d Update cmdline.cc
use stoul for unsigned long

image-base i64 to u64 parse_unsigned_number

Signed-off-by: nick-rawlings <[email protected]>
2022-03-17 21:28:10 -05:00
Alois Wohlschlager 146ddd711c [ELF][ARM64] Support R_AARCH64_LD_PREL_LO19 relocations
Implement support for the R_AARCH64_LD_PREL_LO19 relocation type. Since
this actually behaves the same way as R_AARCH64_CONDBR19, reuse the
relevant code.

Signed-off-by: Alois Wohlschlager <[email protected]>
2022-03-17 15:44:49 +01:00
Martin Richtarsky 32d1006e29 Improve help
Signed-off-by: Martin Richtarsky <[email protected]>
2022-03-10 16:06:42 +00:00
Rui Ueyama 66a057cce7 [ELF][ARM64] Add a stub for ARM32 2022-03-10 16:27:31 +09:00
Rui Ueyama 619ff869e4 [ELF][i386] Implement GD → LE and LD → LD relaxations
This commit implements TLS relocation relaxations.

Fixes https://github.com/rui314/mold/issues/388
2022-03-10 15:18:43 +09:00
Rui Ueyama e7c071ea36 [ELF] Improve an error message 2022-03-10 13:54:16 +09:00
Rui Ueyama 3d68824dea [ELF][ARM64] Fix IFUNC relocations in -static-pie executables
For a static position-independent executable, a libc's intitialization
routine reads the dynamic relocation table and apply them. It looks like
on ARM64, they simply apply relocations from the begining of the table
to end. Therefore, if an IFUNC relocation appears before RELATIVE relocs,
the IFUNC resolver would see an unrelocated value. So, we need to place
RELATIVE relocs before IFUNCs.

Fixes https://github.com/rui314/mold/issues/382
2022-03-09 18:04:34 +09:00
Rui Ueyama c98d276916 [ELF][ARM64] Fix a test
For some reason, readelf doesn't like a build-id if it's very short.
2022-03-09 17:00:53 +09:00
Rui Ueyama 446c461201 [ELF][ARM64] Fix debug info section
This fixes a regression introduced in 92876820cb.
Until now, mold set bogus addresses to non-alloc sections and
as a result emitted corrupted debug info.

Fixes https://github.com/rui314/mold/issues/387
2022-03-09 16:04:03 +09:00
Rui Ueyama 98edd441ce [ELF] Do not print out an error of ldd --version
Some `ldd` impls don't support the --version option. We do not
want to show the error message but instead just skip the test
entirely.

https://github.com/rui314/mold/pull/385
2022-03-09 14:53:10 +09:00
Rui Ueyama 37f6ef2081 [ELF] Fix a crash bug for -noinhibit-exec
After a certain point, it is not expected that there's a remaining
undefined symbol. -noinhibit-exec did not respect that invariant,
which caused a crash bug on ARM64.

This change forces all symbols to be resolved if --noinhibit-exec
is given.

Fixes https://github.com/rui314/mold/issues/383
2022-03-09 11:38:46 +09:00
Rui Ueyama 41cf6d7814 [ELF][ARM64] Avoid -mcmodel=large and -no-PIC
Such combination is not supported by GCC.

Fixes https://github.com/rui314/mold/issues/384
2022-03-09 11:36:44 +09:00
Rui Ueyama bb5a596bc5 Set LC_ALL=C to get consistent output from shell comands
We unset `LANG`, but it looks like `LC_ALL` takes precedence over `LANG`,
so we should set `LC_ALL` to `C`.

Reported at https://github.com/rui314/mold/pull/385
2022-03-09 09:03:22 +09:00
Rui Ueyama 1b6f301644 Merge pull request #381 from Scrumplex/ignore-dy
[ELF] Add --dy and --dn aliases
2022-03-08 20:43:08 +09:00
Sefa Eyeoglu 8a089fee45 [ELF] Move --static option in helpmsg
Signed-off-by: Sefa Eyeoglu <[email protected]>
2022-03-08 10:53:32 +01:00
Sefa Eyeoglu 82e80721ac [ELF] Add --dy and --dn aliases
GNU ld aliases these to -Bdynamic and -Bstatic respectively.

Signed-off-by: Sefa Eyeoglu <[email protected]>
2022-03-08 10:53:27 +01:00
Rui Ueyama d3dea2f435 Merge pull request #380 from sicherha/static-pie-glibc-version-requirement
Increase required glibc version for `-static-pie` tests
2022-03-08 18:37:21 +09:00
Christoph Erhardt a20e3b8004 Increase required glibc version for -static-pie tests
mold 1.1.1 generates `-static-pie` code that works with glibc 2.35 but
segfaults with glibc 2.34 or older.

Signed-off-by: Christoph Erhardt <[email protected]>
2022-03-08 10:13:49 +01:00
Rui Ueyama c1af9c97e7 Bump mold version to 1.1.1 2022-03-07 19:38:14 +09:00
Rui Ueyama 3999aa8b18 [ELF] Fix -static-pie
Previously, mold did not work with the -static-pie compiler flag.
In order to support static PIE, we need the following:

1. Create a .dynamic section even if it is not a dynamic executable, and
2. do not define __rel_iplt_start/end symbols.

The second part is required to workaround a glibc issue.

Even with these changes, mold-generated executable doesn't work with
old glibc versions. But I think it's OK because -static-pie is relatively
new option and you need a newer toolchain anyway.
2022-03-06 17:14:13 +09:00
Rui Ueyama b4d9db9db8 [ELF] Do not emit DT_RELCOUNT
It looks like the glibc loader does not use this field in the dynamic
section. GNU ld, gold and LLVM lld don't create this. So I think we
can just follow suit.
2022-03-06 14:43:24 +09:00
Rui Ueyama 95c7871c6e Fix documentation 2022-03-06 14:23:22 +09:00
Rui Ueyama 3f7bffe93c Add a comment 2022-03-06 13:48:31 +09:00
Rui Ueyama ec473c74d7 Refactor 2022-03-06 13:24:01 +09:00
Rui Ueyama 9e11b40a99 [ELF] Do not allocate a byte for an empty class
In C++, the size of an empty class is 1 rather than 0 because every
object is guaranteed to have a unique address. But if we do not need
the unique address guarantee, we can add `[[no_unique_address]]` to
a class member to save 1 byte. This is a new C++20 feature.
2022-03-06 12:20:31 +09:00
Rui Ueyama a4fd7e3819 [ELF] Remove redundant foo/.. from library paths 2022-03-05 21:28:43 +09:00
Rui Ueyama aae3dc3c1e Refactor 2022-03-05 20:27:10 +09:00
Rui Ueyama ba7967152d Refactor 2022-03-05 20:14:43 +09:00
Rui Ueyama d6dae3be6d [ELF] Report errors more strictly for unrepresentable relocations 2022-03-05 14:59:31 +09:00
Rui Ueyama 83aaa63a49 [ELF][i386] Make GOT-relative relocs relative to .got instead of .got.plt
We used to compute offsets from .got.plt for GOT-relative relocations,
but according to the i386 psABI, they should be relative to .got.
2022-03-05 12:13:04 +09:00
Rui Ueyama b0d91b9967 [ELF] Add -warn-shared-textrel
Fixes https://github.com/rui314/mold/issues/377
2022-03-05 10:59:04 +09:00
Rui Ueyama 4ea052591a Merge pull request #378 from gruenich/main
input-files: Remove unused variable size
2022-03-05 09:49:57 +09:00
Christoph Grüninger 2f03e46bb3 input-files: Remove unused variable size
Removed code has no effect.
Left-over from code removal in 4dae896726.

Signed-off-by: Christoph Grüninger <[email protected]>
2022-03-05 00:30:30 +01:00
Rui Ueyama 6205a1edc3 Update the man page 2022-03-04 14:54:10 +09:00
Rui Ueyama 4dea134949 [ELF] Do not compute the contents of .eh_frame twice 2022-03-03 20:07:45 +09:00
Rui Ueyama eb79859ea2 [ELF] Always set _GLOBAL_OFFSET_TABLE_ to the beginning of .got
Previously, we set the address of `_GLOBAL_OFFSET_TABLE_` to `.got.plt`
on i386 and x86-64 and to `.got` on other targets. But it looks like
this special treament of x86 isn't necessary. The x86-64 psABI said
that the symbol can even be in the middle of `.got` (*1). If `.got.plt`
is missing, GNU linker set it to `.got`.

This commit unconditionally set the symbol value to `.got`.

(*1) x86-64 psABI 1.0 p.77: "The symbol _GLOBAL_OFFSET_TABLE_ may
reside in the middle of the .got section, allowing both negative and
non-negative offsets into the array of addresses."
2022-03-03 12:10:26 +09:00
Rui Ueyama 8714b78d48 [ELF] Reduce memory usage a bit 2022-03-03 10:28:27 +09:00
Rui Ueyama 86b142e555 [ELF] Do not create DT_INIT/DT_FINI if _init/_fini are in a .so
Fixes https://github.com/rui314/mold/issues/372
2022-03-02 21:12:41 +09:00
Rui Ueyama e5549c73f0 Remove an unused class 2022-03-02 17:37:38 +09:00
Rui Ueyama 4dae896726 [ELF] Reduce the size of InputSection from 88 bytes to 72 bytes 2022-03-02 17:27:31 +09:00
Rui Ueyama 83e05dae28 [ELF] Reduce the size of FdeRecord from 24 bytes to 16 bytes
It saves 8 bytes for each input function.
2022-03-02 16:25:17 +09:00
Rui Ueyama 99a0aaf133 [ELF] Add a test for -init 2022-03-02 10:35:32 +09:00
Rui Ueyama 7068c0cd0f [ELF] Reduce the size of InputSection from 120 bytes to 88 bytes 2022-03-01 17:49:17 +09:00
Rui Ueyama f49e4024e9 Rename a variable 2022-03-01 17:08:14 +09:00
Rui Ueyama 567a78dbd5 [ELF] Do not directly read st_shndx
If st_shndx is 0xffff, the actual section index value is in a
SHT_SYMTAB_SHNDX section.
2022-03-01 14:47:51 +09:00
Rui Ueyama f2d27d8d24 [ELF] Shrink the size of Symbol from 56 bytes to 48 bytes
This is a second attempt to reduce the size of the Symbol class.
2022-03-01 12:03:35 +09:00
Rui Ueyama dc9573989c Refactor 2022-03-01 11:37:49 +09:00
Rui Ueyama e469b04f0d Revert "[ELF] Shrink the size of Symbol from 56 bytes to 48 bytes"
This reverts commit 32e158d340
because it broke some tests when mold is built with itself.
2022-03-01 07:49:57 +09:00
Rui Ueyama 32e158d340 [ELF] Shrink the size of Symbol from 56 bytes to 48 bytes 2022-02-28 20:53:40 +09:00
Rui Ueyama 18b6f96756 Refactor 2022-02-28 19:42:01 +09:00
Rui Ueyama 2b7dbb01c4 Rename a file 2022-02-28 16:20:37 +09:00
Rui Ueyama c1c9bdf363 Optimize BigEndian class
Despite its comment, gcc sometimes failed to optimize the loop into
single `bswap` instruction. So it looks like we should explicitly use
`__builtin_bswap` functions.
2022-02-28 16:17:11 +09:00
Rui Ueyama d4bbf31db2 [ELF] Add the --noinhibit-exec option
Fixes https://github.com/rui314/mold/issues/370
2022-02-28 13:03:35 +09:00
Rui Ueyama 9b5d404d1e Simplify 2022-02-27 20:59:59 +09:00
Rui Ueyama 4caadefac6 Refactor 2022-02-27 15:21:06 +09:00
Rui Ueyama 8f21cc386e [ELF] Merge --shuffle-sections-seed into --shuffle-sections 2022-02-27 14:25:10 +09:00
Rui Ueyama f7de9571f8 Refactor 2022-02-27 14:24:14 +09:00
Rui Ueyama e413433752 [ELF][LTO] Add lld-compatible LTO-related command line options
Fixes https://github.com/rui314/mold/issues/367
2022-02-27 09:28:51 +09:00
Rui Ueyama e67f460bfd [ELF][LTO] Fix "too many open files" issue for GCC
Fixes https://github.com/rui314/mold/issues/362
2022-02-26 16:34:51 +09:00
Rui Ueyama e281ed88bb Fix minor memory leak 2022-02-26 16:24:25 +09:00
Rui Ueyama 41986279de [ELF] Do not create multiple .init_array output sections
Usually, init_array is readable and writable, but it looks like rustc
with `-C lto=fat` create a read-only .init_array. mold used to create
two output .init_array sections for read/write and read-only
init_array input sections. That caused one of them are not executed
on startup.

Now, mold create read/write .init_array output section even if an
input .init_array section is read-only.

Fixes https://github.com/rui314/mold/issues/363
2022-02-25 22:54:55 +09:00
Rui Ueyama 480efdedf0 [ELF] Fix IBT-enabled PLT
We used to emit the following sequence for each PLT entry.

    0xf3, 0x0f, 0x1e, 0xfa, // endbr64
    0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOTPLT
    0xe8, 0, 0, 0, 0,       // call PLT[0]

In the above instruction sequence, `jmp` was expected to jump to the
following `call` instruction if the PLT entry is executed for the
first time. However, it wouldn't work because we needed another
`endbr64` before `call`.

This problem should be fixed in this commit. Now, the PLT header is
32 bytes long and each entry is 16 bytes.
2022-02-25 12:43:43 +09:00
Rui Ueyama 99e3f959fc [ELF] Make IBTPLT header a bit shorter 2022-02-24 19:56:41 +09:00
Rui Ueyama 4ec0bbf048 [ELF] Shrink the size of IBTPLT entry from 32 bytes to 16 bytes
IBT-enabled PLT section is sparse as 2/3 of the section contents are
NOP instructions. In this patch, I implemented alternative instruction
sequences for IBTPLT, so that each IBTPLT entry occupies only 16 bytes
instead of 32 bytes.

In return, our new IBTPLT section needs a 64 bytes header instead of a
16 bytes one, but it's overall a good deal because we usually have many
PLT entries.
2022-02-24 17:55:17 +09:00
Rui Ueyama f4c0e321db [ELF] Do not use BND JMP
BND JMP instruction is a part of of Intel MPX instructions.
Intel MPX instructions are deprecated by Intel now.
2022-02-24 14:26:40 +09:00
Rui Ueyama 31a5cdf86f [ELF] Verify a relocation after TLSGD or TLSLD 2022-02-24 12:32:02 +09:00
Rui Ueyama a44de89991 Fix CI
`-m32` doesn't work on CI for some reason, so I'll remove the test
file for now.
2022-02-24 11:32:00 +09:00
Rui Ueyama 30683645c2 [ELF] Do not corrupt compressed debug sections on i386
Previously, if an input debug section is compressed, mold produced
corrupted output debug section on i386.

Fixes https://github.com/rui314/mold/issues/361
2022-02-24 11:05:54 +09:00
Rui Ueyama ac3ae60184 Format 2022-02-24 11:05:54 +09:00
Rui Ueyama 9f34830882 [ELF] Relax TLSLD followed by GOTPCREL, GOTPCRELX or REX_GOTPCRELX 2022-02-23 23:53:57 +09:00
Rui Ueyama 4aa4bfa4a6 [ELF] Fix TLSGD/TLSLD relaxation
When we relax R_X86_64_TLSGD or R_X86_64_TLSGD, we rewrite two
instructions (the one referred by TLSGD/TLSLD and the following one
referred by the following relocation). The second instruction is
usually 5 bytes, but if it can be longer than that, and if that's the
case, we need to emit a nop to fill the gap at the end of the longer
instruction.

We didn't do that. As a result, the remaining garbage of the second
instruction is executed and caused an unpredictable result (illegal
instruction or segv).

This patch fixes the issue.

Fixes https://github.com/rui314/mold/issues/360
2022-02-23 16:16:53 +09:00
Rui Ueyama fb6967c0cf Update a document 2022-02-22 19:26:28 +09:00
Rui Ueyama 385c1c494d Update a document 2022-02-22 18:57:15 +09:00
Rui Ueyama 4667be6134 [ELF] Change the size of ibtplt from 24 bytes to 32 bytes 2022-02-22 16:14:27 +09:00
Rui Ueyama 3f9868238b [ELF] Add the --shuffle-sections-seed=NUMBER option 2022-02-22 15:41:51 +09:00
Rui Ueyama 3e8c519d4e [ELF] Add the --reverse-sections option
This option reverse input sections before assigning offsets to them.
This is useful to detect an unintended dependency between static
initializers in different translation units. C++ only guarantees that
static initializers are invoked from top to bottom in a single
translation unit, and it doesn't guaratee anything about the order of
initialization between translation units.

By (partially) reversing the contents of .init_array using
--reverse-sections, you can run your program with a completely
reversed initialization order.
2022-02-22 15:41:51 +09:00
Rui Ueyama aa27d674c1 Merge pull request #356 from sicherha/aarch64-skip-reloc-rodata-test
Skip reloc-rodata test on aarch64
2022-02-22 15:00:23 +09:00
Christoph Erhardt 3cbc57f9af Skip reloc-rodata test on aarch64
The test expects an error when an object compiled with `-fno-PIC` is
linked with `-pie`, but that doesn't happen on aarch64.

Signed-off-by: Christoph Erhardt <[email protected]>
2022-02-22 06:41:01 +01:00
Rui Ueyama a1e6d8e8df Add comments 2022-02-22 13:18:09 +09:00
Rui Ueyama a054bcd4ff [ELF] Add the --dependency-file=FILE option 2022-02-22 13:18:04 +09:00
Rui Ueyama e47c67727c Update README 2022-02-22 11:50:54 +09:00
Rui Ueyama 16bdf4009a Pass -O2 to the compiler 2022-02-22 11:49:17 +09:00
Rui Ueyama c173f9c0ab Simplify 2022-02-21 18:26:51 +09:00
Rui Ueyama 89612b7096 Bump mold version to 1.1 2022-02-20 16:40:17 +09:00
Rui Ueyama d258140d54 Inline a default constructor 2022-02-20 16:04:00 +09:00
Rui Ueyama 6ce142d73a Remove redundant default destructors 2022-02-20 16:02:05 +09:00
Rui Ueyama 3a15c54f10 [ELF][LTO] Check for the return value of all_symbols_read_hook 2022-02-19 21:57:36 +09:00
Rui Ueyama 4a6e71096c Fix Gentoo build script as build-static.sh is gone 2022-02-19 21:57:36 +09:00
Rui Ueyama ec9ac181f8 [ELF] Improve comments 2022-02-19 18:24:21 +09:00
Rui Ueyama ccd475e41d [Mach-O] Show a notice on startup 2022-02-19 16:49:20 +09:00
Rui Ueyama 7dc6ce72dd Refactor 2022-02-19 12:13:23 +09:00
Rui Ueyama 064be343f5 Remove build-static.sh as the statically-linked mold can't do LTO
This commit removes build-static.sh because statically-linked mold
executable can't generally dlopen a shared library and therefore can't
load the linker LTO plugin.

Now, build-dynamic.sh is renamed dist.sh and recommended for general
use.
2022-02-19 12:00:51 +09:00
Rui Ueyama c8853223a3 Add a script to create a tar file for binary distribution
I think we don't need to create a tar file for binary distribution
for each Linux distro. Instead, we can create mold executable that
works on most Linux machines.

The new script, build-dynamic.sh, creates a mold executable with
libmimalloc, libtbb, libstdc++ and libcrypto begin linked statically.
I don't have a concern as to the availability of other libraries
(libc, libm, libz and librt) because they essentially exists on any
system.
2022-02-19 11:26:18 +09:00
Rui Ueyama 0b7d210f99 Improve build scripts 2022-02-18 22:35:59 +09:00
Rui Ueyama b22e6f80fc Add scripts to create binary packages
https://github.com/rui314/mold/issues/351
2022-02-18 22:11:34 +09:00
Rui Ueyama 15fa05eff6 [ELF][LTO] Clear symbols after LTO
This commit ensure that all symbol resolution results are cleared
after LTO, so that the following second symbol resolution will not
be affected by the previous results.
2022-02-18 22:05:14 +09:00
Rui Ueyama 61e0311889 Refactor 2022-02-18 20:18:07 +09:00
Rui Ueyama 825b918146 Do not use dlopen only when built with ./build-static.sh
This commit fixes a regression in f4a9b05114
that mold crashed when it tried to dlopen a linker plugin shared library.
2022-02-18 15:18:56 +09:00
Rui Ueyama 19587d43ee Rename a variable 2022-02-18 12:53:34 +09:00
Rui Ueyama f4a9b05114 Do not call dloepn() if built with ./build-static.sh
It looks like setting __TBB_DYNAMIC_LOAD_ENABLED to 0 is not enough
to make TBB not to call dlopen(). We need to unset __TBB_WEAK_SYMBOLS_PRESENT
as well.

Fixes https://github.com/rui314/mold/issues/348
2022-02-18 12:17:24 +09:00
Rui Ueyama 7735cf5b95 Style change 2022-02-17 20:29:28 +09:00
Rui Ueyama fcb28161b1 Merge pull request #349 from dmantipov/use-explicit-mimalloc-new-delete-overrides-if-system-mimalloc
Use explicit mimalloc new/delete overrides if SYSTEM_MIMALLOC
2022-02-17 20:26:27 +09:00
Rui Ueyama b57da5413c [ELF][LTO] Restart mold if a LTO plugin does not support get_symbols v3 API 2022-02-17 19:14:05 +09:00
Dmitry Antipov 630b262d2b Use explicit mimalloc new/delete overrides if SYSTEM_MIMALLOC
When building with SYSTEM_MIMALLOC=1, linking with (assuming
shared) '-lmimalloc' is not enough to override C++ new/delete
with mimalloc versions, and 'mimalloc-new-delete.h' should be
included from one (and the only) translation unit as well.

Signed-off-by: Dmitry Antipov <[email protected]>
2022-02-17 10:44:58 +03:00
Rui Ueyama 8f0acbf413 [ELF][LTO] Move LTO to the symbol resolution pass
ELF object files returned by do_lto() may contain unforeseen undefined
symbols, so we can't call that function after the symbol resolution pass
because it can cause undefined symbol errors.
2022-02-17 16:14:11 +09:00
Rui Ueyama a3f2a5c7ec Format 2022-02-17 13:54:15 +09:00
Rui Ueyama b5db63f4bb [ELF] Allow relative relocs against absolute symbols
In the previous commit (c325cc039a),
I argued that PC-relative relocations are not representable if they
refer absolute symbols, and I made a change so that such relocations
are handled as errors.

Even though what I did is technically correct, that results in a failure
of an important use case of the weak undefined symbol. Here is why.

If you have an weak undefined symbol `foo`, you would use it as follows:

  if (foo)
    foo();

If `foo` is defined (i.e. has an address other than 0), `foo` is called.

If `foo` is undefined (i.e. has the address 0), the subsequent CALL
instruction will have an offset from the CALL instruction to address 0.
That displacement is computed at link-time. If the output is position-
independent, the call instruction may not have a correct displacement
from address 0 to the instruction at runtime due to base relocation.

However, that's not a problem in practice, because the function call
is guarded by `if (foo)`, and that will always evaluated to false
if `foo` is undefined. Therefore, the faulty `CALL` instruction will
never be executed.

So, that means we need to relocate PC-relative relocations against
absolute symbols even if doing so results in an incorrect result.
This patch implement that.
2022-02-17 12:07:48 +09:00
Rui Ueyama c325cc039a [ELF] Report an error for an unrepresentable reloc against abs symbol
PC-relative relocations against absolute symbols are not representable
in position-independent code. In order to support such relocations,
someone has to compute `S + A - P` or subtract `B` as a base relocation,
but such dynamic relocations don't exist.

This is contrary to non-PC-relative relocations against non-absolute
symbols. In this case, we can simply use the base relocation (e.g.
R_X86_64_RELATIVE).

https://github.com/rui314/mold/issues/348
2022-02-17 11:21:52 +09:00
Rui Ueyama 0b7edc07f4 Format 2022-02-17 10:30:20 +09:00
Rui Ueyama da48efe63b Update the man page 2022-02-16 19:36:26 +09:00
Rui Ueyama 3f1b071ae5 [ELF] Remove --warn-shared-textrel
It looks like the option is supported only by gold, and it doesn't
make much sense to me to have this option along with more generic
--warn-textrel. So I want to remove this before the next release.
2022-02-16 19:36:26 +09:00
Rui Ueyama 444cb7a566 [ELF] Improve the man page 2022-02-16 18:54:08 +09:00
Rui Ueyama 889f9f3a36 Simplify 2022-02-16 16:53:23 +09:00
Rui Ueyama e4c4ab053e [ELF] Remove --shuffle-sections=N
This patch removes the --shuffle-sections option that takes an
argument as I'm still thinking about a better name for the option.

You can still use `--shuffle-sections` that doesn't take an argument.
2022-02-16 15:55:47 +09:00
Rui Ueyama 10ff2d705c Fix CI 2022-02-16 15:26:39 +09:00
Rui Ueyama 31f7427859 Rename a test file 2022-02-16 15:04:09 +09:00
Rui Ueyama c338540885 [ELF] Handle relocations with r_sym == 0 correctly
If a relocation's r_sym value is 0 (i.e. it refers the undefined symbol
at the beginning of the symbol table), it has to handled as if S were 0.
We used to handle as if S were -1.

In addition to that, even if a relocation needs a dynamic base relocation,
it doesn't mean its expression is always S+A. For example, calling an
absolute address needs a dynamic relocation but its expression is S+A-P.

Fixes https://github.com/rui314/mold/issues/348
2022-02-16 13:18:22 +09:00
Rui Ueyama d908d4c61c [ELF] Handle global symbols in mergeable sections correctly
Prior to this patch, mold computeed a wrong address for a global
symbol in a mergeable section. believe it was a regression in
60d0900496.

Fixes https://github.com/rui314/mold/issues/347
2022-02-16 12:05:40 +09:00
Rui Ueyama b94dd6dbeb Revert "[ELF] add method to check whether an input file is shared object"
This reverts commit ddcb7b4197. `is_dso`
is used by hot functions such as `Symbol::get_addr()`, so we want to
eliminate the cost of virtual function dispatch.
2022-02-16 09:51:07 +09:00
Rui Ueyama a7333bdb4e Merge pull request #343 from dmantipov/fix-annoying-unused-variable-warnings
[ELF] Fix -Wunused-variable warnings
2022-02-14 21:27:57 +09:00
Dmitry Antipov 881b55d691 [ELF] Fix -Wunused-variable warning
Fix the following -Wunused-variable warning as reported by gcc:

elf/input-sections.cc:116:8: warning: unused variable ‘is_code’
[-Wunused-variable]
  116 |   bool is_code = (shdr().sh_flags & SHF_EXECINSTR);

Signed-off-by: Dmitry Antipov <[email protected]>
2022-02-14 15:22:00 +03:00
Rui Ueyama 28236b1b34 [ELF][LTO] Report an error if an IR file wasn't claimed
And remove an unused variable. Found by Dmitry Antipov.
2022-02-14 20:59:32 +09:00
Rui Ueyama 7e62ce4bd8 Improve an error message 2022-02-14 13:59:01 +09:00
Rui Ueyama b0d75f8957 [ELF] Document --start-lib and --end-lib
Also extends the description of --push-state and --pop-state.

Fixes https://github.com/rui314/mold/issues/339
2022-02-13 19:57:06 +09:00
Rui Ueyama 68a35bfa03 Fix CI 2022-02-13 16:52:54 +09:00
Rui Ueyama 70ec56f8bb [ELF][LTO] Export symbols from DSOs correctly
With this change, mold can now build Clang with LTO and all Clang tests pass.
2022-02-13 15:34:37 +09:00
Rui Ueyama e18eaac3eb Update README 2022-02-13 15:18:08 +09:00
Rui Ueyama 5f17da80d0 [ELF][LTO] Mark IR objects as alive 2022-02-13 12:57:28 +09:00
Rui Ueyama 1663bcf932 Rename test files 2022-02-13 12:22:18 +09:00
Rui Ueyama 16a3c9491f [ELF][LTO] Fix "too many open files" issue
read_lto_object is called on each archive member, so if an archive file
contains lots of members, it can exhaust the pool of file descriptors.
This patch fixes the issue by opening an archive only once.
2022-02-13 12:22:18 +09:00
Rui Ueyama bab74c0c7e Merge pull request #342 from sylvestre/wording
Readme: Improve the wording
2022-02-13 11:41:57 +09:00
Sylvestre Ledru 5c5d50929e Improve the wording
Patch by Josh Triplett
https://salsa.debian.org/pkg-llvm-team/mold/-/merge_requests/8/diffs

Signed-off-by: Sylvestre Ledru <[email protected]>
2022-02-12 22:53:19 +01:00
Rui Ueyama ce744aeca4 Refactor 2022-02-12 20:34:49 +09:00
Rui Ueyama 0222dc6f39 [ELF] Drop SHF_GROUP flag from section header
SHF_GROUP is sometimes set to comdat group members. Since we process
comdat groups and uniquify them, that flag doesn't make sense in the
output file.
2022-02-12 20:17:57 +09:00
Rui Ueyama 46f954d23d [ELF][LTO] Returns a more precise value from get_symbols callback 2022-02-12 20:06:44 +09:00
Rui Ueyama 42b3038463 [ELF][LTO] Factor out LTO type definitions to lto.h 2022-02-12 17:50:36 +09:00
Rui Ueyama c9612d3120 Refactor 2022-02-12 17:36:38 +09:00
Rui Ueyama 60d0900496 [ELF][LTO] Fix "concurrent hash table full" error
Since compiled IR objects may contain mergeable string sections, we
don't know the estimated number of items in output mergeable sections.
Previously, we created the concurrent hash tables for output mergeable
sections before compiling IR objects, so there was a chance to see
the "concurrent hash table full" error.

This commit moves the code to add section pieces to output mergeable
sections after `do_lto`.
2022-02-12 16:53:36 +09:00
Rui Ueyama c1963ce2ac [ELF][LTO] Recognize IR files generated by GCC 11 2022-02-12 16:28:38 +09:00
Rui Ueyama 1dcd20b401 Fix CI 2022-02-12 15:39:28 +09:00
Rui Ueyama 7e91897154 [ELF] Add the --shuffle-sections option
If the --shuffle-sections is given, mold now randomize the output by
shuffling input sections randomly. This feature is compatible with lld's
--shuffle-sections=SEED option introduced in
https://reviews.llvm.org/D74791.

This feature is useful when you want to equalize the conditions of
benchmarks. That is, some particular memory layout can produce a very
good benchmark number due to hardware-level cache hit rate or something
like that. Therefore, even if you get a good benchmark number after
changing code, there's a chance that that's caused by the layout change
and not by the new code itself. With --shuffle-sections, you can isolate
that.

The other use case I can think of is to enhance security. If you build
your program as a position-independent executable, the kernel
automatically enables ASLR (Address Space Layout Randomization), but ASLR
only shift the entire program image in memory by some random offset;
Relative offsets between sections remain the same. If you compile programs
from source, by using --shuffle-sections, you can make the offsets
unpredictable to attackers.
2022-02-12 15:32:37 +09:00
Rui Ueyama 61eb265cf4 [ELF][LTO] Report an error if -plugin was not given 2022-02-12 10:17:21 +09:00
Rui Ueyama 46995bcfc3 [ELF] Support LTO plugin API
The LTO plugin API support is still in progress, but with this change,
mold can link itself with `-flto` with both GCC and Clang.

Since mold now supports LTO natively, I removed the fallback mechanism
to ld.bfd or ld.lld that I implemented in
a5029d19a8.

Fixes https://github.com/rui314/mold/issues/181
2022-02-11 21:09:28 +09:00
Rui Ueyama dd8002f092 [ELF] Detect GCC LTO object files early 2022-02-11 20:57:19 +09:00
Rui Ueyama 6ee5aea1bf Add a comment about how to build mold with AddressSanitizer 2022-02-11 18:16:20 +09:00
Rui Ueyama 81cf1171a9 Simplify 2022-02-10 18:23:59 +09:00
Rui Ueyama 955c6d1fe4 Fix ODR violation 2022-02-10 15:26:26 +09:00
Rui Ueyama 7546b44111 [ELF] Add --print-dependencies=full
This is an experimental feature. We haven't decided as to whether we'll
include this flag into the next release.

This is an example output of the flag when linking mold with mold:
https://gist.github.com/rui314/3f5d226814bb1188c535f1f86706cea5
2022-02-09 13:52:37 +09:00
Rui Ueyama d4679063fc Make virtual destructor public
I believe if you make the destructor a protected function, you can't
destruct it through a pointer to the base type. All destruction has
to be called through a pointer to a derived type.

There's no difference at the moment, but I think it is more conventional
to make a virtual destructor public.
2022-02-09 11:33:19 +09:00
Rui Ueyama e8c72767fa Merge pull request #334 from dmantipov/macho-add-compiler-suggested-virtual-destructor
[MACHO] add compiler-suggested virtual destructor
2022-02-09 11:32:19 +09:00
Dmitry Antipov 440e4c4ade [MACHO] add compiler-suggested virtual destructor
Add 'mold::macho::InputFile()::~InputFile()' as suggested by
clang++-13's '-Wdelete-non-abstract-non-virtual-dtor':

/usr/include/c++/11/bits/unique_ptr.h:85:2: warning: delete
called on non-final 'mold::macho::ObjectFile<mold::macho::ARM64>'
that has virtual functions but non-virtual destructor
[-Wdelete-non-abstract-non-virtual-dtor]

Signed-off-by: Dmitry Antipov [email protected]
2022-02-08 20:14:30 +03:00
Rui Ueyama 70afbd1ca7 [ELF] Discard local symbols if EM_RISCV
RISC-V object files contain a large number of local symbols. This is
because all branches are explicitly represented by relocations and
symbols even their branch targets are of the same input section.
RISC-V psABI requires such relocations and symbols to allow sections
to be shrinked by linker relaxation.

GNU ld by default discard local symbols if RISC-V. We do the same
in this patch.
2022-02-08 23:23:20 +09:00
Rui Ueyama 5e9238e3f0 Merge pull request #333 from dmantipov/macho-add-method-to-check-whether-an-input-file-is-shared-object
[MACHO] add method to check whether an input file is shared object
2022-02-08 21:54:11 +09:00
Dmitry Antipov 7f6bfc1bdb [MACHO] add method to check whether an input file is shared object
Prefer pure virtual 'mold::macho::InputFile::is_dylib()' (actually
implemented in 'mold::macho::ObjectFile' and 'mold::macho::DylibFile')
over the field of the same name, adjust related code. This is Mach-O
counterpart of 249c331117.

Signed-off-by: Dmitry Antipov [email protected]
2022-02-08 15:40:43 +03:00
Rui Ueyama ebc2bfafdd [ELF] Fix ICF crash bug
`-icf=all` couldn't handle very small programs that has no sections
to process in the main loop. This commit fixes that crash bug.

Fixes https://github.com/rui314/mold/issues/332
2022-02-08 16:34:25 +09:00
Rui Ueyama 249c331117 Refactor 2022-02-08 16:30:41 +09:00
Rui Ueyama 818c59e4dd Merge pull request #328 from dmantipov/elf-add-method-to-check-whether-an-input-file-is-shared-object
[ELF] add method to check whether an input file is shared object
2022-02-08 16:28:06 +09:00
Dmitry Antipov ddcb7b4197 [ELF] add method to check whether an input file is shared object
Prefer pure virtual 'mold::elf::InputFile::is_dso()' (actually
implemented in 'mold::elf::ObjectFile' and 'mold::elf::SharedFile')
over the field of the same name, adjust related code.

Signed-off-by: Dmitry Antipov [email protected]
2022-02-08 10:19:56 +03:00
Rui Ueyama 4a0eef3b14 [ELF] Do not inline .copyrel.rel.ro if RELRO is disabled 2022-02-08 10:33:37 +09:00
Rui Ueyama 5850f237fe [ELF] Do not set .copyrel.rel.ro to NOBITS if it's small 2022-02-08 10:09:57 +09:00
Rui Ueyama 0f8bf23bcf [ELF] Rename .dynbss -> .copyrel
Currently, the sections we create for copy relocations are named .dynbss
and .dynbss.rel.ro. Section names shouldn't be significant in ELF
executables, so we should be able to name them as we want.

However, in practice, valgrind treat a section with the name .dynbss
in a special way and crashes if an executable contains both .bss and
.dynbss due to the following assertion failure:

  $ valgrind ./prog
  ==1960252== Memcheck, a memory error detector
  ==1960252== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
  ==1960252== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
  ==1960252== Command: ./prog
  ==1960252==

  valgrind: m_debuginfo/readelf.c:2271 (vgModuleLocal_read_elf_debug_info): Assertion 'di->bss_svma + di->bss_size == svma' failed.
  Segmentation fault (core dumped)

This commit is a workaround for the valgrind issue. We simply rename
dynbss and .dynbss.rel.ro to .copyrel and .copyrel.rel.ro, respectively.

Fixes https://github.com/rui314/mold/issues/159
2022-02-08 10:09:54 +09:00
Rui Ueyama a2fa0934d0 Refactor 2022-02-08 07:06:44 +09:00
Rui Ueyama 214c35e05d Refactor 2022-02-08 07:02:30 +09:00
Rui Ueyama e5f4b967ed Do not link against libxxhash.so at all
Ensure that all xxhash functions are inlined. Since it eliminates
external dependencies, we no longer have to link against libxxhash.a
or libxxhash.so at all.

`SYSTEM_XXHASH` Makefile variable has been removed because it is
no longer relevant.
2022-02-07 20:06:59 +09:00
Rui Ueyama 73159e241d [ELF] Add .got.plt to PT_GNU_RELRO if -z now
If external symbols are resolved eagerly on process startup, .got.plt
can be read-only and therefore can be in the PT_GNU_RELRO segment.
2022-02-07 17:12:53 +09:00
Rui Ueyama a1287c26b9 [ELF] Add --print-dependencies
This is a mold-only experimental feature. If the flag is given,
mold prints out dependencies between input files as tab-separated
tuples of (`source-file`, `dest-file`, `symbol`). Each line of the
output means `source-file` depends on `dest-file` to use `symbol`.

With the dependency information, you can analyze for example why
some archive file member is included in a linker's output.
You can also for example know what symbols have to be removed
to eliminate the dependency to some shared library file.

I'm not sure if I'll include this flag in the next release or
document this feature, but it looks promising so far. If you like
or dislike this feature, please leave a comment on GitHub.

This is an example of the output when linking mold with mold:
https://gist.github.com/rui314/e1d70bbdebfabb881be7167314b58f48
2022-02-07 11:53:38 +09:00
Rui Ueyama 36fe7511f1 Do not link against librt if not necessary 2022-02-07 11:32:23 +09:00
Rui Ueyama 4551fffe69 Do not link against libxxhash.so if not necessary
We use only `XXH3_64bits` from libxxhash, and it looks like the function
is an inline function, so there's actually no runtime dependency against
libxxhash.so.
2022-02-07 11:29:02 +09:00
Rui Ueyama bef545bdb9 [ELF] Improve a hint for -b binary 2022-02-07 09:53:16 +09:00
Rui Ueyama ab2db49fbe Refactor 2022-02-07 08:37:35 +09:00
Rui Ueyama e65c5d2a1c [ELF] Handle ABI incompatibility between -fgnu-unique and -fno-gnu-unique
GCC creates symbols in comdat groups as STB_GNU_UNIQUE instead of STB_WEAK
if it was configured to do so at build time or the -fgnu-unique option was
given. If mold is given two object files with and without STB_GNU_UNIQUE,
it could end up selecting a sybmol that is in a de-duplicated comdat group.

This is arguably just an ABI incompatibility. Two comdat groups must
contain the same contents if their identifiers are the same. But we
can't handle it as an error because it is not uncommon to link object
files compiled using Clang (or GCC without -fgnu-unique) to static
libraries built with GCC that produces STB_GNU_UNIQUE symbols.

This patch gives the same priority to STB_GNU_UNIQUE as STB_WEAK so that
mold won't select symbols in discarded comdat groups.

Frankly, the situation around STB_GNU_UNIQUE is a mess. That GNU extension
shouldn't have been added to the GNU toolchain in the first place.
It looks like GCC shipped with Linux distros are nowadays do not produce
STB_GNU_UNIQUE symbols by default, but we still need to handle them.

Fixes https://github.com/rui314/mold/issues/324
2022-02-07 07:53:58 +09:00
Rui Ueyama 27c50e8158 [ELF] Report an error for an unresolved hidden undef correctly
Previously, if a protected/hidden undef symbol is resolved to a DSO
symbol,  mold didn't report a symbol undefined error.

Fixes https://github.com/rui314/mold/issues/329
2022-02-06 13:49:17 +09:00
Rui Ueyama 07a480c43c [ELF] Make .relr.dyn construction a bit more efficient
Relocations are naturally sorted, so we don't have to sort them again.
This change makes Chrome link time ~1% faster when --pack-dyn-relocs=relr
is specified.
2022-02-05 16:39:51 +09:00
Rui Ueyama 6ffcae4286 [ELF] Add --warn-textrel 2022-02-05 13:26:38 +09:00
Rui Ueyama 03e0cef726 [ELF] Add --warn-shared-textrel 2022-02-05 12:15:30 +09:00
Rui Ueyama a4d9bc7550 Fix CI 2022-02-05 12:15:17 +09:00
Rui Ueyama f24b997501 [ELF} Add --warn-once 2022-02-05 11:39:59 +09:00
Rui Ueyama f72999a1df Add an assertion 2022-02-05 10:49:05 +09:00
Rui Ueyama b02c8cb8f5 Add Coding Guidelines 2022-02-05 10:28:08 +09:00
Rui Ueyama 579fafc04b Refactor 2022-02-05 09:31:43 +09:00
Rui Ueyama 77716836be Refactor 2022-02-05 08:58:20 +09:00
Rui Ueyama 017082f22f Refactor 2022-02-05 00:22:52 +09:00
Rui Ueyama 1abc1f9275 Refactor 2022-02-04 22:39:03 +09:00
Rui Ueyama 12df85ad8c [ELF] Add time probes
For the record, here is the log when I was trying to cross-link
clang-14 (~4.6 GiB) for RISC-V on a 64-core Threadripper machine.

     User   System     Real  Name
   83.719   29.444    3.828  all
    9.512   18.175    0.887    read_input_files
   21.853    0.329    0.747    register_section_pieces
   52.363   10.939    2.194    total
   30.359    1.384    1.044      before_copy
    0.000    0.000    0.000        apply_exclude_libs
    0.638    0.000    0.021        resolve_symbols
    0.310    0.032    0.011        eliminate_comdats
    0.001    0.001    0.000        convert_common_symbols
    0.000    0.000    0.000        apply_version_script
    0.076    0.044    0.004        compute_import_export
    2.303    0.000    0.077        compute_merged_section_sizes
    0.962    0.000    0.034          MergedSection assign_offsets
    0.165    0.016    0.008        bin_sections
    0.022    0.012    0.002        claim_unresolved_symbols
    0.134    0.003    0.004        check_duplicate_symbols
    0.001    0.001    0.002        sort_init_fini
    0.070    0.000    0.003        compute_section_sizes
    4.316    0.000    0.143        scan_rels
    0.082    0.000    0.005        DynsymSection::finalize
    0.000    0.000    0.000        fill_verdef
    0.001    0.000    0.001        fill_verneed
    7.275    0.000    0.232        compute_symtab
    0.001    0.000    0.000        eh_frame
    0.000    0.000    0.000        set_osec_offsets
   14.951    1.276    0.529        riscv_resize_sections
    3.921    0.009    0.125          align_contents
    0.076    0.028    0.004          compute_section_sizes
    0.000    0.000    0.000          set_osec_offsets
    4.279    0.000    0.145          relax_call
    0.096    0.000    0.004          compute_section_sizes
    0.000    0.000    0.000          set_osec_offsets
    0.000    0.000    0.000      open_file
   22.004    9.555    1.150      copy
   19.026    9.315    0.894        copy_buf
    0.004    0.001    0.000          (header)
    0.003    0.001    0.000          .dynamic
    0.002    0.000    0.000          .shstrtab
    0.004    0.001    0.000          .eh_frame_hdr
    0.006    0.002    0.002          .rela.dyn
   12.586    7.233    0.621          .debug_frame
    0.285    0.484    0.026          .dynsym
    0.002    0.001    0.000          .interp
    0.003    0.001    0.000          .got.plt
    0.003    0.001    0.000          .comment
    0.003    0.001    0.000          .rela.plt
    0.003    0.001    0.000          .rodata.cst
    0.003    0.001    0.000          .gnu.version
    0.304    0.597    0.031          .gnu.hash
    0.002    0.001    0.000          .tm_clone_table
    1.048    1.255    0.075          .data
    0.002    0.001    0.000          .fini_array
    0.002    0.001    0.000          .note.gnu.build-id
    0.002    0.001    0.000          (header)
   15.103    8.296    0.733          .symtab
    0.301    0.596    0.030          .data.rel.ro
    0.002    0.001    0.000          .note.ABI-tag
    0.554    0.979    0.049          .debug_abbrev
    0.060    0.068    0.006          .init_array
    4.335    4.488    0.279          .debug_ranges
    8.505    6.198    0.461          .text
   19.024    9.315    0.894          .debug_info
    0.002    0.001    0.000          .gnu.version_r
    0.002    0.001    0.000          .tbss
    0.298    0.595    0.029          .dynstr
    0.002    0.000    0.000          .bss
    6.122    5.254    0.357          .debug_str
   11.593    6.945    0.583          .debug_line
    1.594    1.981    0.112          .rodata
    0.000    0.000    0.000          .plt
    0.000    0.000    0.000          .strtab
    0.001    0.000    0.000          .eh_frame
    0.086    0.167    0.011          .got
    0.000    0.000    0.000          (header)
    5.561    5.046    0.333          .debug_aranges
    0.000    0.000    0.000          .plt.got
    0.000    0.000    0.000          .preinit_array
    0.068    0.026    0.005        sort_dynamic_relocs
    0.000    0.000    0.000        clear_padding
    2.911    0.214    0.250        build_id
    0.000    0.000    0.000      close_file
2022-02-04 21:20:45 +09:00
Rui Ueyama 1b2b20423e [ELF] Document --emit-relocs 2022-02-04 20:26:07 +09:00
Rui Ueyama afca9d55aa [ELF] Fix RISC-V code for -O2 2022-02-04 20:11:50 +09:00
Rui Ueyama 043449125d [ELF] Fix --emit-reloc relocation values
With this change, `ninja check-bolt` in LLVM passes. I'm not sure if
that really means mold is usable with Bolt, but I guess it is.

Fixes https://github.com/rui314/mold/issues/177
2022-02-04 20:11:48 +09:00
Rui Ueyama 26fe71d64c [ELF] Support --emit-relocs
This commit adds the `--emit-relocs` option to mold. If the flag is
given, mold copies relocation sections from input files to an output
file. This option is used by some binary post-processing tools such
as Facebook's Bolt.

It looks like the relocations emitted by `--emit-relocs` are correct,
but I didn't do extensive testing. If you find any problem, please
let me know.

Fixes https://github.com/rui314/mold/issues/177
2022-02-04 18:42:08 +09:00
Rui Ueyama 354d1b7d2f [ELF] Fix CI 2022-02-04 18:34:57 +09:00
Rui Ueyama dca8d58b51 Refactor 2022-02-04 18:26:41 +09:00
Rui Ueyama 1550b5afff [ELF] Copy symbols from DSOs to an output file
Previously, mold didn't copy symbols from .so input files to the
output .symtab.
2022-02-04 18:03:17 +09:00
Rui Ueyama e4c03c238e [ELF] Write section symbols to .symtab 2022-02-04 17:05:06 +09:00
Rui Ueyama 0542a2254b Refactor 2022-02-04 17:05:05 +09:00
Rui Ueyama d0ab7d9e17 Refactor 2022-02-04 16:14:00 +09:00
Rui Ueyama dbe99c2884 [ELF] Make the contents of .dynsym deterministic 2022-02-04 07:39:35 +09:00
Rui Ueyama c6ddcc4344 Merge pull request #325 from dmantipov/elf-use-parallel-sort-for-dynsym
Use parallel sort for .dynsym
2022-02-03 22:02:51 +09:00
Dmitry Antipov bfa4ac2ef8 Use parallel sort for .dynsym
Prefer 'tbb::parallel_sort()' over 'std::sort()' in
'DynsymSection<E>::finalize()'. Although being highly
dependent from '--thread-count', this is expected to
achieve up to ~8x speedup for huge (>20M entries)
symbol tables, as shown with '--perf', e.g.
from:
  1.651    0.000    1.662        DynsymSection::finalize
to:
  0.488    0.000    0.198        DynsymSection::finalize

Signed-off-by: Dmitry Antipov <[email protected]>
2022-02-03 14:46:36 +03:00
Rui Ueyama 818971841f Update documents 2022-02-03 20:17:10 +09:00
Rui Ueyama 5c123e2c68 [ELF] Reduce the size of Symbol<E> from 64 to 54 bytes 2022-02-03 20:12:19 +09:00
Rui Ueyama dc7eedee74 Refactor 2022-02-03 17:31:34 +09:00
Rui Ueyama 2359222723 [ELF][RISC-V] Support more relocations 2022-02-02 21:55:19 +09:00
Rui Ueyama 9b90f642a1 [ELF][RISC-V] Support relocation relaxation.
RISC-V psABI defines a mechanism for a linker to shrink a section
by rewriting instructions in it. This patch implements that feature.
2022-02-02 20:40:17 +09:00
Rui Ueyama 0acd4b9d2f [ELF] Reduce the size of InputSection by 8 bytes 2022-02-02 15:03:22 +09:00
Rui Ueyama 7fcff98f29 [ELF] Directly uncompress debug sections into the output mmap'ed buffer
Previously, we always uncompress compressed debug sections to
in-memory buffer before copying them to the final output buffer.
This patch eliminates that redundant copy whenever possible.
2022-02-02 14:18:48 +09:00
Rui Ueyama 3c25434202 Fix clang buildbot 2022-02-01 14:02:25 +09:00
Rui Ueyama e98aab7ea9 [ELF] Reimplement --repro
`--repro` is a hidden command flag for debugging.

Previously, if the flag was given, mold would create a `.repro`
section in an output file with all input files as its contents.
The flaw of the design is that when mold fails to create an output
file, no .repro section would be created.

So I changed the behavior in this commit. Now, the tar file is
created as an independent file.
2022-02-01 13:47:44 +09:00
Rui Ueyama a380c1675f Update README 2022-02-01 10:00:03 +09:00
Rui Ueyama 3232c1f884 Specify the SHA256 ID instead of ubuntu:20.04
`ubuntu:20.04` is a rolling docker tag, so the image pulled from it
is not guaranteed to be the same. We should speicfy the image ID
for reproducibility.

Fixes https://github.com/rui314/mold/issues/317
2022-02-01 09:56:29 +09:00
Rui Ueyama c6b34c2b77 [ELF] Do not use std::bit_ceil.
Some C++ libraries do not seem to provide that function.

Fixes https://github.com/rui314/mold/issues/308
2022-01-31 20:38:37 +09:00
Rui Ueyama d3140a25f6 Refactor 2022-01-31 20:36:43 +09:00
Rui Ueyama 5859be88b9 Merge pull request #321 from dmantipov/fix-wmissing-field-initializers
Fix -Wmissing-field-initializers
2022-01-31 20:34:54 +09:00
Dmitry Antipov 35c0d4ae9a Fix -Wmissing-field-initializers
Add 'mold::elf::GlobPattern::Element' and 'mold::macho::SplitInfo'
trivial constructors to fix all '-Wmissing-field-initializers' as
reported both by g++ and clang++.

Signed-off-by: Dmitry Antipov <[email protected]>
2022-01-31 12:50:01 +03:00
Rui Ueyama 7aa35d12c3 [ELF] Do not use std::has_single_bit
It looks like some stdlib implementations do not provide that function.

Fixes https://github.com/rui314/mold/issues/308
2022-01-31 17:52:05 +09:00
Rui Ueyama 8465438051 Merge pull request #320 from gruenich/feature/fix-warnings
Feature/fix warnings
2022-01-31 11:03:10 +09:00
Christoph Grüninger d301b8a3fe Add missing destructor to InputFile
Fixes Clang's delete-non-abstract-non-virtual-dtor warning

Signed-off-by: Christoph Grüninger <[email protected]>
2022-01-30 23:08:24 +01:00
Christoph Grüninger 3f442402f8 Fix typos in comments
Signed-off-by: Christoph Grüninger <[email protected]>
2022-01-30 22:56:43 +01:00
Christoph Grüninger 705451d21e Remove unsed variables
Signed-off-by: Christoph Grüninger <[email protected]>
2022-01-30 22:55:51 +01:00
Rui Ueyama f7b2ce278b [ELF] Reduce the size of InputSection class
This commit reduces the size of InputSection class from 144 bytes
to 120 bytes. Since we create lots of instance of the class, the
size of it matters.
2022-01-30 20:25:20 +09:00
Rui Ueyama c6e4fb56b4 Bump mold version to 1.0.3 2022-01-30 19:57:30 +09:00
Rui Ueyama e630dea2ab [ELF][RISC-V] Handle relocations against non-alloc sections 2022-01-30 13:10:04 +09:00
Rui Ueyama 2afe571c07 [RISC-V][ELF] Fix .got.plt secton contents
With this change, mold can link mold itself on RISC-V and pass all tests.
2022-01-30 11:34:32 +09:00
Rui Ueyama 1de4874478 Update README 2022-01-30 10:01:11 +09:00
Rui Ueyama 00203dbc7c [ELF][RISC-V] Implement TLS_GOT_HI20 and TLS_GD_HI20
Now all test passed on my SiFive Unmatched RISC-V PC.
2022-01-29 22:35:04 +09:00
Rui Ueyama 190003dac7 Refactor 2022-01-29 20:37:13 +09:00
Rui Ueyama dcdffbdab7 [ELF][RISC-V] Change the default page size to 4096 bytes 2022-01-29 17:39:30 +09:00
Rui Ueyama 80008796de [ELF][RISC-V] Fix segmentation fault 2022-01-29 17:28:06 +09:00
Rui Ueyama 5c89a6d08c [ELF][RISC-V] Set ELF e_flags field 2022-01-29 16:26:24 +09:00
Rui Ueyama 0c4f8bed1b [ELF][RISC-V] Do not create __global_pointer$ symbol for DSO 2022-01-29 16:26:23 +09:00
Rui Ueyama 8a0bf96f6d [ELF][RISC-V] Fix SUB6/SET6 relocations 2022-01-29 16:26:23 +09:00
Rui Ueyama 7718000dba Update a comment
Pointed out by jrtc27.
2022-01-29 16:26:22 +09:00
Rui Ueyama e061e3ca70 [ELF][RISC-V] Add a test for dynamically-linked executable 2022-01-29 14:24:46 +09:00
Rui Ueyama e06396dd79 Merge pull request #314 from kofuk/wrap-ld-bfd
mold-wrapper: Execute mold if ld.bfd is executed
2022-01-28 20:45:08 +09:00
Koki Fukuda 77d979f112 mold-wrapper: Execute mold if ld.bfd is executed
Signed-off-by: Koki Fukuda <[email protected]>
2022-01-28 20:38:01 +09:00
Rui Ueyama 57f996f806 Fix build-static.sh script
`build-static.sh` didn't create a statically-linked binary.

Fixes https://github.com/rui314/mold/issues/315
2022-01-28 19:31:49 +09:00
Rui Ueyama 81e6a88d58 Refactor 2022-01-28 18:48:17 +09:00
Rui Ueyama e76f7c0d47 Add an initial support of RISC-V
With this patch, mold can now produce a statically-link "Hello wrold"
program for RISC-V.
2022-01-28 18:08:42 +09:00
Rui Ueyama 32a7e7cb17 Suppress OpenSSL 3.0's deprecation warnings
OpenSSL deprecates the low-level crypto APIs in OpenSSL 3.0. I guess
there's a reason to do that for them, but for us, the low-level APIs
such as SHA256_CTX, SHA256_Update or SHA256_Final are exactly what we
want to use. After all, we just want to compute SHA256 checksums.

The high-level API that OpenSSL provides requires users to allocate a
context object using new_ctx(). That has some performance hit as it
requires heap allocation. And it's just complicated too.

So, I decided to stick with the deprecated functions at least for now
and just suppresss the warning message by defining
OPENSSL_SUPPRESS_DEPRECATED macro. If OpenSSL deletes these functions,
we need to do something, but it's too early to worry about it.

Fixes https://github.com/rui314/mold/issues/246
2022-01-28 08:59:45 +09:00
Rui Ueyama 379ee22af3 Refactor 2022-01-27 23:21:36 +09:00
Rui Ueyama b879bade6a Merge pull request #310 from trofi/main
Makefile: avoid calling '$(shell)' on each $(CXX) execution
2022-01-27 22:24:45 +09:00
Sergei Trofimovich bc66d9a684 Makefile: avoid calling '$(shell)' on each $(CXX) execution
Before the change calls to $(shell git rev-parse) and $(shell pkg-config)
happened on every mold's .o file (as make's "A = $(B)" performs late
expansion).

After the change $(shell) calls are evaluated once with "A := $(B)"
assignment.

Noticed by chance when 'make' was complaining about missing pkg-config
tool on each build step.

Signed-off-by: Sergei Trofimovich <[email protected]>
2022-01-27 12:29:20 +00:00
Rui Ueyama 59826c197b Merge pull request #309 from sicherha/add-missing-include
Add missing `#include <bit>`
2022-01-27 06:29:16 +09:00
Christoph Erhardt 33a5888725 Add missing #include <bit>
Fixes #308.

Signed-off-by: Christoph Erhardt <[email protected]>
2022-01-26 21:36:34 +01:00
Rui Ueyama 0d542fb3bc Simplify 2022-01-26 20:02:22 +09:00
Rui Ueyama 229ee85890 [ELF][RISC-V] Add a stub for RISCV64
As we add more targets to mold/ELF, it takes more time to compile
source files because compiler has to instantiate more templates.
I think we need to do something to fix it, but for now, I'll just
add a stub for RISC-V 64-bit ISA so that we can start working on
RV64.
2022-01-26 15:16:36 +09:00
Rui Ueyama 110a29552a Add a comment 2022-01-26 09:32:30 +09:00
Rui Ueyama 6c9bb0d7ff Use C++20 bit-twiddling functions more 2022-01-26 09:24:52 +09:00
Rui Ueyama 6a86370b0e Merge pull request #305 from sicherha/c++20-bit-operations
Use C++20 bit-twiddling functions
2022-01-26 09:08:47 +09:00
Christoph Erhardt cb94a4ab1b Use C++20 bit-twiddling functions
This replaces the GCC builtins with their portable C++20 counterparts.

Note that the C++ functions are only defined for unsigned types, so we
need to explicitly specify the appropriate template type argument in
some instances.

Signed-off-by: Christoph Erhardt <[email protected]>
2022-01-25 21:44:00 +01:00
Rui Ueyama 76b127e824 [ELF] Add RISC-V ELF constants 2022-01-25 22:39:41 +09:00
Rui Ueyama 51f7caf263 Simplify 2022-01-25 20:43:47 +09:00
Rui Ueyama e74760e780 Use yield instruction on ARM64 for spinlock 2022-01-25 17:01:06 +09:00
Rui Ueyama 614b6c0e38 Print more information for -stats 2022-01-25 17:01:05 +09:00
Rui Ueyama df75bbee0e Rename variables 2022-01-25 17:01:04 +09:00
Rui Ueyama 220161b6c6 Automatically manage source dependencies 2022-01-25 13:32:51 +09:00
Rui Ueyama b23f8e067c Update README
We now have an instruction as to how to use mold for Rust. `mold -run
cargo build` is not recommended, so remove it from our README.
2022-01-25 11:16:12 +09:00
Rui Ueyama a38111d6bb Merge pull request #301 from Keruspe/libc++
fix build with libc++
2022-01-24 22:59:49 +09:00
Rui Ueyama daccce7bb8 Merge pull request #300 from Keruspe/pkg-config
build-sys: allow overriding pkg-config
2022-01-24 22:57:23 +09:00
Marc-Antoine Perennou 4fc27d2195 fix build with libc++
Otherwise fails with:

/usr/include/c++/v1/__threading_support:370:43: error: use of undeclared identifier 'PTHREAD_MUTEX_RECURSIVE'

200809L triggers __USE_XOPEN2K8 to be defined which enables pthread.h to
define the missing identifier.

Signed-off-by: Marc-Antoine Perennou <[email protected]>
2022-01-24 14:29:03 +01:00
Marc-Antoine Perennou 708112cd16 build-sys: allow overriding pkg-config
Signed-off-by: Marc-Antoine Perennou <[email protected]>
2022-01-24 14:14:43 +01:00
Rui Ueyama 3322ff6e7c [ELF] Compress debug sections with level 1 instead of level 3
Debug sections are generally compressed pretty well with level 1,
so it is. Here is the comparison in lld: https://reviews.llvm.org/D70658
2022-01-24 11:40:07 +09:00
Rui Ueyama 42b7eb87a1 Fix libtbb.so for musl libc
musl libc doesn't provide getcontext/setcontext functions.
It also doesn't define RTLD_DEEPBIND flag.

I already sent a pull request to upstream:
https://github.com/oneapi-src/oneTBB/pull/748
2022-01-24 11:22:35 +09:00
Rui Ueyama b4fa51e981 Fix tests for musl libc 2022-01-24 10:29:31 +09:00
Rui Ueyama dc7432a6dd Avoid GNU extensions in tests 2022-01-24 10:21:08 +09:00
Rui Ueyama 0a0f9b3ad5 [ELF] Protect the last page of a RELRO segment
PT_GNU_RELRO works on page granularity. We always align the begining
of a RELRO segment to a page boundary, but the end was not. Since the
runtime conservatively align _down_ it to a page boundary, the last
page weren't be marked as read-only.

This patch makes the size of a RELRO always a multiple of the page size.
2022-01-24 09:40:21 +09:00
Rui Ueyama b2ef045c03 Style fix 2022-01-24 07:56:19 +09:00
Rui Ueyama e83d1616c8 Update the man page 2022-01-24 07:49:41 +09:00
Rui Ueyama d196ec43e6 Merge pull request #299 from sicherha/fix-glibcxx-assertion
Fix out-of-bounds assertion on aarch64 with `_GLIBCXX_ASSERTIONS` enabled
2022-01-24 07:13:12 +09:00
Christoph Erhardt 03ba8f9325 Fix out-of-bounds error on aarch64 with _GLIBCXX_ASSERTIONS enabled
While an iterator may point beyond the last element of a `std::span`, a
pointer may not.

Fixes #298.

Signed-off-by: Christoph Erhardt <[email protected]>
2022-01-23 21:48:55 +01:00
Rui Ueyama 79f83561bd Merge pull request #296 from sicherha/pass-cxxflags-to-tbb
Pass `CXXFLAGS` to bundled tbb
2022-01-24 00:36:04 +09:00
Christoph Erhardt 0653716408 Pass CXXFLAGS to bundled tbb
This makes work easier for distributions that specify custom hardened
compiler flags.

Signed-off-by: Christoph Erhardt <[email protected]>
2022-01-23 16:03:53 +01:00
496 changed files with 15104 additions and 5469 deletions

No files matched your search

+21 -21
View File
@@ -9,35 +9,39 @@ jobs:
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 build-essential libstdc++-10-dev cmake clang libssl-dev zlib1g-dev libxxhash-dev libtbb-dev git bsdmainutils dwarfdump libc6-dev:i386 lib32gcc-10-dev libstdc++-10-dev-arm64-cross gcc-aarch64-linux-gnu g++-aarch64-linux-gnu qemu-user
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
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: make
run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
make CC=clang CXX=clang++ DEBUG=1 -j$(nproc)
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: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
make test -j$(nproc)
build-gcc11:
run: make -j$(nproc) test
build-gcc:
runs-on: ubuntu-20.04
container: gcc:11.1.0
steps:
- uses: actions/checkout@v2
- name: apt-get
run: dpkg --add-architecture i386 && apt-get update && apt-get install -y sudo libssl-dev zlib1g-dev libxxhash-dev cmake clang dwarfdump bsdextrautils libgcc-9-dev gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libc6-dev:i386 lib32gcc-10-dev qemu-user
run: |
dpkg --add-architecture i386 && apt-get update && apt-get install -y sudo libssl-dev zlib1g-dev libxxhash-dev cmake clang bsdmainutils dwarfdump qemu-user gcc-10-i686-linux-gnu gcc-10-aarch64-linux-gnu gcc-10-riscv64-linux-gnu gcc-10-arm-linux-gnueabihf g++-10-i686-linux-gnu g++-10-aarch64-linux-gnu g++-10-riscv64-linux-gnu g++-10-arm-linux-gnueabihf
ln -sf /usr/bin/i686-linux-gnu-gcc-10 /usr/bin/i686-linux-gnu-gcc
ln -sf /usr/bin/i686-linux-gnu-g++-10 /usr/bin/i686-linux-gnu-g++
ln -sf /usr/bin/aarch64-linux-gnu-gcc-10 /usr/bin/aarch64-linux-gnu-gcc
ln -sf /usr/bin/aarch64-linux-gnu-g++-10 /usr/bin/aarch64-linux-gnu-g++
ln -sf /usr/bin/arm-linux-gnueabihf-gcc-10 /usr/bin/arm-linux-gnueabihf-gcc
ln -sf /usr/bin/arm-linux-gnueabihf-g++-10 /usr/bin/arm-linux-gnueabihf-g++
ln -sf /usr/bin/riscv64-linux-gnu-gcc-10 /usr/bin/riscv64-linux-gnu-gcc
ln -sf /usr/bin/riscv64-linux-gnu-g++-10 /usr/bin/riscv64-linux-gnu-g++
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: make
run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
make CC=gcc CXX=g++ DEBUG=1 -j$(nproc)
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make CC=gcc CXX=g++ -j$(nproc)
- name: make test
run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
make test -j$(nproc)
run: make -j$(nproc) test-all
build-macos:
runs-on: macos-11
steps:
@@ -47,10 +51,6 @@ jobs:
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: make
run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
make DEBUG=1 -j$(sysctl -n hw.logicalcpu)
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make -j$(sysctl -n hw.logicalcpu)
- name: make test
run: |
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
make test -j$(sysctl -n hw.logicalcpu)
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make -j$(sysctl -n hw.logicalcpu) test
+1
View File
@@ -11,3 +11,4 @@ ld
ld64.mold
gentoo
out
/mold-*.tar.gz
+22
View File
@@ -0,0 +1,22 @@
# This dockerfile creates a reproducible build environment for mold.
#
# $ docker build -t rui314/mold-builder:v1-$(uname -m) .
# $ docker push rui314/mold-builder:v1-$(uname -m)
FROM ubuntu:18.04
RUN apt-get update && \
TZ=Europe/London apt-get install -y tzdata && \
apt-get install -y --no-install-recommends software-properties-common && \
add-apt-repository -y ppa:ubuntu-toolchain-r/test && \
apt-get install -y --no-install-recommends build-essential git \
wget pkg-config cmake libstdc++-11-dev zlib1g-dev gpg gpg-agent && \
bash -c "$(wget -O- https://apt.llvm.org/llvm.sh)" && \
apt-get install -y --no-install-recommends clang-14 && \
apt clean && \
rm -rf /var/lib/apt/lists/* && \
cd / && \
wget -O- https://www.openssl.org/source/openssl-3.0.2.tar.gz | tar xzf - && \
mv openssl-3.0.2 openssl && \
cd openssl && \
./Configure --prefix=/openssl && \
make -j$(nproc)
+69 -42
View File
@@ -1,3 +1,9 @@
# If you want to enable ASAN, run `make` with the following options:
#
# make CXXFLAGS='-fsanitize=address -g' LDFLAGS=-fsanitize=address USE_MIMALLOC=0
VERSION = 1.2.0
PREFIX = /usr/local
BINDIR = $(PREFIX)/bin
LIBDIR = $(PREFIX)/lib
@@ -16,16 +22,18 @@ 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)
HEADERS=$(wildcard *.h elf/*.h macho/*.h)
OBJS=$(SRCS:%.cc=out/%.o)
SRCS = $(wildcard *.cc elf/*.cc macho/*.cc)
OBJS = $(SRCS:%.cc=out/%.o)
OS = $(shell uname -s)
ARCH = $(shell uname -m)
OS := $(shell uname -s)
ARCH := $(shell uname -m)
IS_ANDROID = 0
ifneq ($(findstring -android,$(shell $(CC) -dumpmachine)),)
@@ -37,19 +45,18 @@ endif
CFLAGS = -O2
CXXFLAGS = -O2
MOLD_CXXFLAGS = -std=c++20 -fno-exceptions -fno-unwind-tables \
-fno-asynchronous-unwind-tables -DMOLD_VERSION=\"1.0.2\" \
-DLIBDIR="\"$(LIBDIR)\""
MOLD_CXXFLAGS := -std=c++20 -fno-exceptions -fno-unwind-tables \
-fno-asynchronous-unwind-tables -Ithird-party/xxhash \
-DMOLD_VERSION=\"$(VERSION)\" -DLIBDIR="\"$(LIBDIR)\""
MOLD_LDFLAGS = -pthread -lz -lm
MOLD_LDFLAGS := -pthread -lz -lm -ldl
GIT_HASH = $(shell [ -d .git ] && git rev-parse HEAD)
GIT_HASH := $(shell [ -d .git ] && git rev-parse HEAD)
ifneq ($(GIT_HASH),)
MOLD_CXXFLAGS += -DGIT_HASH=\"$(GIT_HASH)\"
endif
LTO = 0
ifeq ($(LTO), 1)
CXXFLAGS += -flto -O3
LDFLAGS += -flto
@@ -66,6 +73,7 @@ endif
ifeq ($(USE_MIMALLOC), 1)
ifdef SYSTEM_MIMALLOC
MOLD_CXXFLAGS += -DUSE_SYSTEM_MIMALLOC
MOLD_LDFLAGS += -lmimalloc
else
MIMALLOC_LIB = out/mimalloc/libmimalloc.a
@@ -82,25 +90,21 @@ else
MOLD_CXXFLAGS += -Ithird-party/tbb/include
endif
ifdef SYSTEM_XXHASH
MOLD_LDFLAGS += -lxxhash
else
XXHASH_LIB = third-party/xxhash/libxxhash.a
MOLD_LDFLAGS += $(XXHASH_LIB)
MOLD_CXXFLAGS += -Ithird-party/xxhash
endif
ifeq ($(OS), Linux)
ifeq ($(IS_ANDROID), 0)
# glibc before 2.17 need librt for clock_gettime
MOLD_LDFLAGS += -lrt
MOLD_LDFLAGS += -Wl,-push-state -Wl,-as-needed -lrt -Wl,-pop-state
endif
endif
# Use pkg-config to know where libcrypto resides.
NEEDS_LIBCRYPTO = 0
ifneq ($(OS), Darwin)
MOLD_CXXFLAGS += $(shell pkg-config --cflags-only-I openssl)
MOLD_LDFLAGS += $(shell pkg-config --libs-only-L openssl) -lcrypto
NEEDS_LIBCRYPTO = 1
endif
ifeq ($(NEEDS_LIBCRYPTO), 1)
MOLD_CXXFLAGS += $(shell $(PKG_CONFIG) --cflags-only-I openssl)
MOLD_LDFLAGS += $(shell $(PKG_CONFIG) --libs-only-L openssl) -lcrypto
endif
# '-latomic' flag is needed building on riscv64 system
@@ -119,25 +123,25 @@ ifeq ($(OS), Linux)
MOLD_WRAPPER_LDFLAGS = -Wl,-push-state -Wl,-no-as-needed -ldl -Wl,-pop-state
endif
DEPFLAGS = -MT $@ -MMD -MP -MF out/$*.d
all: mold mold-wrapper.so
mold: $(OBJS) $(MIMALLOC_LIB) $(TBB_LIB) $(XXHASH_LIB)
-include $(SRCS:%.cc=out/%.d)
mold: $(OBJS) $(MIMALLOC_LIB) $(TBB_LIB)
$(CXX) $(OBJS) -o $@ $(MOLD_LDFLAGS) $(LDFLAGS)
ln -sf mold ld
ln -sf mold ld64.mold
mold-wrapper.so: elf/mold-wrapper.c Makefile
$(CC) $(CFLAGS) -fPIC -shared -o $@ $< $(MOLD_WRAPPER_LDFLAGS) $(LDFLAGS)
mold-wrapper.so: elf/mold-wrapper.c
$(CC) $(DEPFLAGS) $(CFLAGS) -fPIC -shared -o $@ $< $(MOLD_WRAPPER_LDFLAGS) $(LDFLAGS)
out/%.o: %.cc $(HEADERS) Makefile out/elf/.keep out/macho/.keep
$(CXX) $(MOLD_CXXFLAGS) $(CXXFLAGS) -c -o $@ $<
out/%.o: %.cc out/elf/.keep out/macho/.keep
$(CXX) $(MOLD_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS) -c -o $@ $<
out/elf/.keep:
mkdir -p out/elf
touch $@
out/macho/.keep:
mkdir -p out/macho
out/elf/.keep out/macho/.keep:
mkdir -p $(@D)
touch $@
$(MIMALLOC_LIB):
@@ -147,26 +151,50 @@ $(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=-D__TBB_DYNAMIC_LOAD_ENABLED=0 -DTBB_STRICT=OFF ../../third-party/tbb)
(cd out/tbb; cmake -G'Unix Makefiles' -DBUILD_SHARED_LIBS=OFF -DTBB_TEST=OFF -DCMAKE_CXX_FLAGS="$(CXXFLAGS) -D__TBB_DYNAMIC_LOAD_ENABLED=0" -DTBB_STRICT=OFF ../../third-party/tbb)
$(MAKE) -C out/tbb tbb
(cd out/tbb; ln -sf *_relwithdebinfo libs)
$(XXHASH_LIB):
$(MAKE) -C third-party/xxhash libxxhash.a
test tests check: all
ifeq ($(OS), Darwin)
$(MAKE) -C test -f Makefile.darwin --no-print-directory
else
$(MAKE) -C test -f Makefile.linux --no-print-directory --output-sync
endif
@if test -t 1; then \
printf '\e[32mPassed all tests\e[0m\n'; \
else \
echo 'Passed all tests'; \
fi
test-x86-64: all
@echo x86_64
CC=x86_64-linux-gnu-gcc CXX=x86_64-linux-gnu-g++ GCC=x86_64-linux-gnu-gcc GXX=x86_64-linux-gnu-g++ OBJDUMP=x86_64-linux-gnu-objdump MACHINE=x86_64 QEMU='qemu-x86_64 -L /usr/x86_64-linux-gnu' $(MAKE) test
test-i386: all
@echo i386
CC=i686-linux-gnu-gcc CXX=i686-linux-gnu-g++ GCC=i686-linux-gnu-gcc GXX=i686-linux-gnu-g++ OBJDUMP=x86_64-linux-gnu-objdump MACHINE=i386 QEMU='qemu-i386 -L /usr/i686-linux-gnu' $(MAKE) test
test-arm64: all
@echo arm64
CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ GCC=aarch64-linux-gnu-gcc GXX=aarch64-linux-gnu-g++ OBJDUMP=aarch64-linux-gnu-objdump MACHINE=aarch64 QEMU='qemu-aarch64 -L /usr/aarch64-linux-gnu' $(MAKE) test
test-arm32: all
@echo arm
CC=arm-linux-gnueabihf-gcc CXX=arm-linux-gnueabihf-g++ GCC=arm-linux-gnueabihf-gcc GXX=arm-linux-gnueabihf-g++ OBJDUMP=arm-linux-gnueabihf-objdump MACHINE=arm QEMU='qemu-arm -L /usr/arm-linux-gnueabihf' $(MAKE) test
test-riscv64: all
@echo riscv64
CC=riscv64-linux-gnu-gcc CXX=riscv64-linux-gnu-g++ GCC=riscv64-linux-gnu-gcc GXX=riscv64-linux-gnu-g++ OBJDUMP=riscv64-linux-gnu-objdump MACHINE=riscv64 QEMU='qemu-riscv64 -L /usr/riscv64-linux-gnu' $(MAKE) test
test-all:
$(MAKE) test-x86-64
$(MAKE) test-i386
$(MAKE) test-arm64
$(MAKE) test-arm32
$(MAKE) test-riscv64
install: all
$(INSTALL) -d $D$(BINDIR)
$(INSTALL_PROGRAM) mold $D$(BINDIR)
@@ -191,7 +219,6 @@ uninstall:
rm -rf $D$(LIBDIR)/mold
clean:
rm -rf *~ mold mold-wrapper.so out ld ld64.mold
$(MAKE) -C third-party/xxhash clean
rm -rf *~ mold mold-wrapper.so out ld ld64.mold mold-*-linux.tar.gz
.PHONY: all test tests check clean
.PHONY: all test tests check clean test-x86-64 test-i386 test-arm64 test-arm32 test-riscv64 test-all
+25 -17
View File
@@ -1,10 +1,10 @@
# mold: A Modern Linker
mold is a faster drop-in replacement for existing Unix linkers.
It is several times faster than LLVM lld linker, the second-fastest
It is several times faster than the LLVM lld linker, the second-fastest
open-source linker which I originally created a few years ago.
mold is created for increasing developer productivity by reducing
build time especially in rapid debug-edit-rebuild cycles.
mold is designed to increase developer productivity by reducing
build time, especially in rapid debug-edit-rebuild cycles.
Here is a performance comparison of GNU gold, LLVM lld, and mold for
linking final debuginfo-enabled executables of major large programs
@@ -22,7 +22,7 @@ mold is so fast that it is only 2x _slower_ than `cp` on the same
machine. Feel free to [file a bug](https://github.com/rui314/mold/issues)
if you find mold is not faster than other linkers.
mold currently supports x86-64, i386 and ARM64.
mold currently supports x86-64, i386, ARM32, ARM64 and 64-bit RISC-V.
## Why does the speed of linking matter?
@@ -72,7 +72,7 @@ sudo dnf install -y git clang cmake openssl-devel xxhash-devel zlib-devel libstd
```shell
git clone https://github.com/rui314/mold.git
cd mold
git checkout v1.0.2
git checkout v1.2.0
make -j$(nproc) CXX=clang++
sudo make install
```
@@ -83,12 +83,14 @@ try a specific version of a compiler such as `g++-10` or `clang++-12`.
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 `./build-static.sh` in this
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 20.04 Docker image, installs necessary tools and libraries to it,
and builds mold as a statically-linked executable.
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:
@@ -142,6 +144,7 @@ rustflags = ["-C", "link-arg=-fuse-ld=/path/to/mold"]
```
where `/path/to/mold` is an absolute path to `mold` exectuable.
Please make sure you have installed `clang`.
If you want to use mold for all projects, put the above snippet to
`~/.cargo/config.toml`.
@@ -160,13 +163,6 @@ mold has a feature to intercept all invocations of `ld`, `ld.lld` or
mold -run make <make-options-if-any>
```
Here's an example showing how to link Rust code when using the
cargo package manager:
```shell
mold -run cargo build
```
Internally, mold invokes a given command with `LD_PRELOAD` environment
variable set to its companion shared object file. The shared object
file intercepts all function calls to `exec(3)`-family functions to
@@ -174,6 +170,16 @@ 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>
<details><summary>Verify that you are using mold</summary>
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.
@@ -187,6 +193,8 @@ String dump of section '.comment':
If `mold` is in `.comment`, the file is created by mold.
</details>
## Why is mold so fast?
One reason is because it simply uses faster algorithms and efficient
+20 -14
View File
@@ -1,31 +1,28 @@
#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() : BigEndian(0) {}
BigEndian(T x) {
*this = x;
}
BigEndian() = delete;
operator T() const {
// We don't need to optimize this code because compilers are
// usually smart enough to compile this loop into a single
// byte-swap instruction such as x86's bswap.
T ret = 0;
for (int i = 0; i < sizeof(T); i++)
ret = (ret << 8) | val[i];
return ret;
T x;
memcpy(&x, val, sizeof(T));
return bswap(x);
}
BigEndian &operator=(T x) {
for (int i = 0; i < sizeof(T); i++)
val[sizeof(T) - 1 - i] = x >> (i * 8);
x = bswap(x);
memcpy(&val, &x, sizeof(T));
return *this;
}
@@ -63,6 +60,15 @@ public:
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>;
-45
View File
@@ -1,45 +0,0 @@
#!/bin/bash -x
# This is a shell script to build a statically-linked mold
# executable using Docker, so that it is easy to build mold
# on non-Ubuntu 20.04 machines.
#
# A docker image used for building mold is persistent.
# Run `docker image rm mold-build-ubuntu20` to remove the image
# from disk.
set -e
# If the existing file is not statically-linked, remove it.
[ -f mold ] && ! ldd mold 2>&1 | grep -q 'not a dynamic executable' && \
rm mold
cat <<EOF | docker build -t mold-build-ubuntu20 -
FROM ubuntu:20.04
RUN apt-get update && \
TZ=Europe/London apt-get install -y tzdata && \
apt-get install -y --no-install-recommends build-essential clang lld \
bsdmainutils file gcc-multilib git pkg-config \
cmake libstdc++-10-dev zlib1g-dev libssl-dev && \
apt clean && \
rm -rf /var/lib/apt/lists/*
EOF
EXTRA_LDFLAGS='-fuse-ld=lld -static'
# libstdc++'s `std::__glibcxx_rwlock_rdlock` refers these symbols
# as weak symbols, although they need to be defined. Otherwise,
# the program crashes after juping to address 0.
# So, we force loading symbols as a workaround.
EXTRA_LDFLAGS="$EXTRA_LDFLAGS -Wl,-u,pthread_rwlock_rdlock"
EXTRA_LDFLAGS="$EXTRA_LDFLAGS -Wl,-u,pthread_rwlock_unlock"
EXTRA_LDFLAGS="$EXTRA_LDFLAGS -Wl,-u,pthread_rwlock_wrlock"
docker_args=(-v "`pwd`:/mold:Z" -u "$(id -u)":"$(id -g)")
if docker --version | grep -q podman; then
docker_args+=(--userns=keep-id)
fi
docker run -it --rm "${docker_args[@]}" \
mold-build-ubuntu20 \
make -C /mold -j"$(nproc)" CC=clang CXX=clang++ \
EXTRA_LDFLAGS="$EXTRA_LDFLAGS" "$@"
+32 -6
View File
@@ -17,6 +17,8 @@
#include <tbb/parallel_for_each.h>
#include <zlib.h>
#define CHECK(fn) do { int r = (fn); assert(r == Z_OK); } while (0)
namespace mold {
static constexpr i64 SHARD_SIZE = 1024 * 1024;
@@ -35,13 +37,14 @@ static std::vector<std::string_view> split(std::string_view input) {
static std::vector<u8> do_compress(std::string_view input) {
// Initialize zlib stream. Since debug info is generally compressed
// pretty well, we chose compression level 3.
// pretty well with lower compression levels, we chose compression
// level 1.
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
int r = deflateInit2(&strm, 3, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
assert(r == Z_OK);
CHECK(deflateInit2(&strm, 1, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY));
// Set an input buffer
strm.avail_in = input.size();
@@ -51,13 +54,36 @@ static std::vector<u8> do_compress(std::string_view input) {
// on the compression size. +16 for Z_SYNC_FLUSH.
std::vector<u8> buf(deflateBound(&strm, strm.avail_in) + 16);
// Compress data. It writes all compressed bytes except the last
// partial byte, so up to 7 bits can be held to be written to the
// buffer.
strm.avail_out = buf.size();
strm.next_out = buf.data();
CHECK(deflate(&strm, Z_BLOCK));
// This is a workaround for libbacktrace before 2022-04-06.
//
// Zlib is a bit stream, and what Z_SYNC_FLUSH does is to write a
// three bit value indicating the start of an uncompressed data
// block followed by four byte data 00 00 ff ff which indicates that
// the length of the block is zero. libbacktrace uses its own zlib
// inflate routine, and it had a bug that if that particular three
// bit value happens to end at a byte boundary, it accidentally
// skipped the next byte.
//
// In order to avoid triggering that bug, we should avoid calling
// deflate() with Z_SYNC_FLUSH if the current bit position is 5.
// If it's 5, we insert an empty block consisting of 10 bits so
// that the bit position is 7 in the next byte.
//
// https://github.com/ianlancetaylor/libbacktrace/pull/87
int nbits;
deflatePending(&strm, Z_NULL, &nbits);
if (nbits == 5)
CHECK(deflatePrime(&strm, 10, 2));
CHECK(deflate(&strm, Z_SYNC_FLUSH));
r = deflate(&strm, Z_SYNC_FLUSH);
assert(r == Z_OK);
assert(strm.avail_out > 0);
buf.resize(buf.size() - strm.avail_out);
deflateEnd(&strm);
return buf;
Executable
+35
View File
@@ -0,0 +1,35 @@
#!/bin/bash -x
# This script creates a mold binary distribution. The output is
# written in this directory as `mold-$version-$arch-linux.tar.gz`
# (e.g. `mold-1.0.3-x86_64-linux.tar.gz`).
#
# The mold executable created by this script is statically-linked to
# libstdc++ and libcrypto but dynamically-linked to libc, libm, libz
# and librt, as they almost always exist on any Linux systems.
set -e
version=$(grep '^VERSION =' $(dirname $0)/Makefile | sed 's/.* = //')
arch=$(uname -m)
dest=mold-$version-$arch-linux
if [ $arch = x86_64 ]; then
image=rui314/mold-builder:v1-x86_64
elif [ $arch = aarch64 -o $arch = arm64 ]; then
image=rui314/mold-builder:v1-aarch64
else
echo "Error: no docker image for $arch"
exit 1
fi
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 &&
tar czf /mold/$dest.tar.gz $dest &&
cp mold /mold"
+46
View File
@@ -0,0 +1,46 @@
# Coding Guidelines
mold is written in C++20, but as is the case with every C++ project,
it has local coding rules. In this document, I'll explain some of them
and try to give justifications for why I chose such rules.
## DOs
- Always use `i64` (which is a type alias for `int64_t` in mold) for
integers unless you have a reason not to. For example, even if you know
that a loop counter won't exceed 100, you should stop thinking about it
and just use `i64`.
Justification: Local variables are usually on CPU registers, so on
64-bit CPUs, there's no performance peanlty on choosing `i64` over
`i32`. Even if a compiler has to spill register values to the stack,
I don't think there's an observable difference between `i32` and
`i64`. Therefore, extra 32 bits are essentially free. On 32-bit CPUs,
they are not free, but that's OK because we are writing mold for modern
computers. mold will still run on 32-bit computers but a bit slowly.
By always using `i64`, we can eliminate the need to think about the
"right" size for each variable. It also reduces the risk of integer
overflow.
Exceptions: If you have to allocate a very large number (e.g. millions)
of the same object, its size matters. In that case, use a smaller type.
## DON'Ts
- Don't use `auto` unless its actual type is obvious in the very narrow
context. Currently, we use `auto` only for lambdas.
Justification: I think `auto` makes code writing easier but code reading
harder, because readers have to make a guess as to what is the actual
type of `auto`. If you are already familiar with the existing codebase,
you may be able to guess it easily, but that's not always the case.
I want to keep the mold codebase friendly to first-time readers.
- Don't over-use inheritance. In mold, most classes don't have parents,
and even if they do, their class hierarchy is very shallow. Currently
its height is just two (i.e. abstract classes and their implementations).
Justification: Designing class hierarchies is fun as it feels like
taxonomy, but I don't think that always help writing code. It looks like
simpler class hierarchy makes its code simpler.
+55
View File
@@ -0,0 +1,55 @@
This page explains the following warning message and how to fix it.
mold emits this message when it sees an object file that may not be
compatible with mold.
```
mold: warning: foo.o: 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.
```
# Background
On modern computers, the stack area (to which local variables are
stored) cannot contain executable code. If the control reaches the
stack area, the CPU refuses to execute any code there and the program
is usually terminated due to segmentation fault.
This is a security measure. The stack area used to be executable (old
CPUs generally execute any code as long as it is in a readable memory
region), but that provided an easy attack vector to a malicious user.
They wrote executable code to the stack area using some buffer
overflow bug and jumped there to run arbitrary code in a remote server
process.
To prevent this type of attack, the stack area is no longer executable
since the early 2000s. On Linux, the stack's executable-ness is
controlled by a bit in an executable, and the loader respect that
bit. The bit is set by the linker.
GCC had (and still have) a feature that depends on the executable
stack, so they invented a way to tell the linker to mark the stack
executable. Specifically, if an object file contains a
`.note.GNU-stack` section with the `SHF_EXECSTACK` bit, GNU linker
silently makes the stack of an output file executable.
But the GNU linker's behavior is dangerous. If you accidentally link
an object file that has that marker section, the entire stack area
silently becomes executable, disabling the security mechanism.
Therefore, mold simply ignores that marker section. If you are using
mold, you need to explicitly pass `-z execstack` to the linker to make
the stack executable.
# What caused this issue?
You are likely to use GCC's [Nested
Functions](https://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html)
feature which still depends on the executable stack.
# How to fix it?
If you know what you are doing, pass `-z execstack` to mold. Beaware
that this will significantly weaken your program's security.
If you don't want to pass `-z execstack`, rewrite your code so that
your code does not depends on the executable stack.
+158
View File
@@ -0,0 +1,158 @@
The very concept of linking is simple: a compiler compiles a piece of
source code into an object file (a file containing machine code), and
a linker combines object files into a single executable or a shared
library file. However, the actual implementation of the linker for
modern systems is much more complicated because hardware, operating
system, compiler and linker all have many more features.
In this file, I'll explain random topics in the glossary format that
you need to understand to read mold code.
## DSO
A .so file. Short for Dynamic Shared Object. Often called as a
shared library, a dynamic libray or a shared object as well.
An DSO contains common functions and data that are used by multiple
executables and/or other DSOs. At runtime, a DSO is loaded to a
contiguous region in the virtual address.
## Object file
A .o file. An object file contains machine code and data, but it
cannot be executed because it's not self-contained. For example,
if you compile a C source file containing a call of `printf`,
the actual function code of `printf` is not included in the resulting
object file. You include `stdio.h`, but that teaches the compiler
only about `printf`'s type, and the compiler still don't know what
`printf` actually does. Therefore, it cannot emit code for `printf`.
You need to link an object file with other object file or a shared
library to make it exectuable.
## Virtual address space
A pointer has a value like 0x803020 which is an address of the
pointee. But it doesn't mean that the pointee resides at the
physical memory address 0x803020 on the computer. Modern CPUs
contains so-called Mmeory Management Unit (MMU), and all access to
the memory are first translated by MMU to the physical address.
The address before translation is called the "virtual address".
Unless you are doing the kernel programming, all addresses you
handle are virtual addresses.
The OS kernel controls the MMU so that each process owns the entire
virtual address space. So, even if two process uses the same virtual
address, they don't conflict. They are mapped to different physical
addresses.
The existence of MMU has several implications to the linker. First,
we can link the main executable to a specific address. On process
startup, there's no code or data in the virtual address space, so
the mapping of the main executable always succeed. However, it's not
true to DSOs because they are loaded after the main executable and
possibly other DSOs. Therefore, shared libraries must be linked in a
way that they can be loaded to any address in the virtual address
space.
## Relocation
A piece of information for the linker as to how to link object files
or a dynamic objects.
Object files can refer functions or data in other object files. For
example, if you compile a function which calls a non-local function
`foo`, the resulting code contains something like this:
```
26: e8 00 00 00 00 callq 2b <bar+0xb>
27: R_X86_64_PLT32 foo-0x4
```
The above `callq` is the instruction to call a function at the
machine code level. It's opcode is `0xe8` in x86-64, so the
instruction begins with `0xe8`. The following four bytes are
displacement; that is, the address of the branch target relative to
the end of this `callq` instruction. Notice that the displacement is
0. The compiler couldn't fill the displacement because it has no
idea as to where `foo` will be at runtime. So, the compiler write 0
as a placeholder and instead write a relocation `R_X86_64_PLT32`
with `foo` as its associated symbol. The linker reads this
relocation, computes the offsets between this call instruction and
function `foo` and overwrite the placeholder value 0 with an actual
displacement.
There are many different types of relocations. For example, if you
want to fix up not with a displacement but with an absolute address
of a symbol, you need to use `R_X86_64_ABS64` instead.
## Static library
A .a file. Often called as an archive file or just archive as well.
A static library is a container just like tar or zip. Actually,
there's no technical reason to not use tar or (uncompressed) zip,
but traditionally the .a file format is used by the linker.
A static library contains object files and can be passed to the
linker along with other object files and/or archives.
A linker pulls out object files from an archive only if it is needed
to resolve undefined symbols. In other words, object files in an
archive are not linked by default and used as a complement to supply
missing definitions. This is ideal for a library because you don't
want to link library functions unless you are actually using them.
Contrary to archive files, object files directly given to a linker
are always linked to the output.
To maximize the benefit of archive files, a library often used as a
static library is broken down to small files to separate each
function individually (for example, look at
https://git.musl-libc.org/cgit/musl/tree/src/stdio). By doing this,
you import only used functions.
A static file is created by `ar`, whose command line arguments are
similar to `tar`. A static library contains the symbol table which
offers a quick way to look up an object file for a defined symbol,
but mold does not use the static library's symbol table. mold
doesdn't need a symbol table to exist in an archive, and if exists,
mold just ignores it.
See also: DSO (dynamic library)
## Symbol
A symbol is a label assigned to a specific location in an input file
or an output file. For example, if you define function `foo` and
compile it, the resulting object file contains a symbol `foo`
pointing to the beginning of the machine code for `foo`.
Usually, a symbol name is a function or a variable name. If an
object is anonymous (such the one for a string literal), a compiler
generated a unique symbol, which often starts with `.` to avoid
conflict with user-defined symbols.
For C++, symbol name is a complex "mangled" name. We need to mangle
identifiers because a simple name such as `foo` cannot be uniquely
identify a function or a data in C++, because for example `foo` may
be in a namespace or defined as a static member in some class. If
`foo` is an overloaded function, we need to distinguish different
`foo`s by its type. Therefore, C++ compiler mangles an identifier by
appending nmaepsace names, type information and such so that
different things get different names.
For example, a function `int foo(int)` in a namespace `bar` is
mangled as `_ZN3bar3fooEi`.
A symbol can be either defined or undefined. A defined symbol points
to some location in a file which may contain the function's machine
code or the variable's initial value. An undefined symbol does not
point to anywhere. It needs to be merged with a defined symbol with
the same name at link-time. This merging process is called "name
resolution".
For example, if your program is using `printf`, it usually contains
`printf` as an undefined symbol. You need to link it with `libc.a`
or `libc.so`, which contain a defined symbol of `printf`, to make a
complete program.
+279 -139
View File
@@ -230,22 +230,23 @@ depends only on its command-line arguments.
.
.\"=============================================================================
.Sh OPTIONS
.Bl -tag -width 6n
.Bl -tag -width 6n -compact
.It Fl -help
Report usage information to stdout and exit.
.
.Pp
.It Fl v , Fl -version
Report version information to stdout.
.
.Pp
.It Fl V
Report version and target information to stdout.
.
.Pp
.It Fl C Ar dir , Fl -directory Ar dir
Change to
.Ar dir
before doing anything.
.
.It Fl E , Fl -export-dynamic , Fl -no-export-dynamic
.Pp
.It Fl E , Fl -export-dynamic
.It Fl -no-export-dynamic
When creating an executable, using the
.Fl E
option causes all global symbols to be put into the dynamic symbol table,
@@ -255,14 +256,15 @@ By default, or if
.Fl -no-export-dynamic
is given, only symbols
that are referenced by DSOs at link-time are exported from an executable.
.
.Pp
.It Fl F Ar libname , Fl -filter Ns = Ns Ar libname
Set the
.Dv DT_FILTER
dynamic section field to
.Ar libname .
.
.It Fl I Ns Ar file , Fl -dynamic-linker Ns = Ns Ar file , Fl -no-dynamic-linker
.Pp
.It Fl I Ns Ar file , Fl -dynamic-linker Ns = Ns Ar file
.It Fl -no-dynamic-linker
Set the dynamic linker path to
.Ar file .
If no
@@ -276,7 +278,7 @@ However, this difference doesn't usually make any difference because the \
compiler driver always passes
.Fl I
to a linker.
.
.Pp
.It Fl L Ns Ar dir , Fl -library-path Ns = Ns Ar dir
Add
.Ar dir
@@ -289,26 +291,27 @@ Unlike the GNU linkers,
does not have the default search paths.
This difference doesn't usually make any difference because the
compiler driver always passes all necessary search paths to a linker.
.
.Pp
.It Fl M , Fl -print-map
Write a map file to stdout.
.
.It Fl N , Fl -omagic , Fl -no-omagic
.Pp
.It Fl N , Fl -omagic
.It Fl -no-omagic
Force
.Nm
to emit an output file with an old-fashioned memory layout.
First, it makes the first data segment to not be aligned to a page boundary.
Second, text segments are marked as writable if the option is given.
.
.Pp
.It Fl S , Fl -strip-debug
Omit
.Li \.debug_*
sections from the output file.
.
.Pp
.It Fl T Ar file , Fl -script Ns = Ns Ar file
Read linker script from
.Ar file .
.
.Pp
.It Fl X , Fl -discard-locals
Discard temporary local symbols to reduce the sizes of the \
symbol table and the string table.
@@ -316,58 +319,58 @@ Temporary local symbols are local symbols starting with
.Li \.L .
Compilers usually generate such symbols for unnamed program elements such as \
string literals or floating-point literals.
.
.Pp
.It Fl e Ar symbol , Fl -entry Ns = Ns Ar symbol
Use
.Ar symbol
as the entry point symbol instead of the default
entry point symbol
.Sy _start .
.
.Pp
.It Fl f Ar shlib , Fl -auxiliary Ns = Ns Ar shlib
Set the
.Dv DT_AUXILIARY
dynamic section field to
.Ar shlib .
.
.It Fl h Ar libname , Fl -soname Ns Ar libname
.Pp
.It Fl h Ar libname , Fl -soname Ns = Ns Ar libname
Set the
.Dv DT_SONAME
dynamic section field to
.Ar libname .
This option is used when creating a shared object file.
Typically, when you create
.Pf Sy lib Ar foo Ns Sy .so ,
.Pf Sy XXX lib Ar foo Ns Sy .so ,
you want to pass
.Fl -soname Ns = Ns Ar foo
to a linker.
.
.Pp
.It Fl l Ns Ar libname
Search for
.Pf Sy lib Ar libname Ns Sy \.so
or
.Pf Sy lib Ar libname Ns Sy \.a
from library search paths.
.
.It Fl m Op Sy elf_x86_64 | elf_i386
.Pp
.It Fl m Op Sy elf_x86_64 | elf_i386 | aarch64linux
Choose a target.
.
.Pp
.It Fl o Ar file , Fl -output Ns = Ns Ar file
Use
.Ar file
as the output file name instead of the default name
.Sy a.out .
.
.Pp
.It Fl r , Fl -relocatable
Instead of generating an executable or a shared object file, combine
input object files to generate another object file that can be used as
an input to a linker.
.
.It Fl s , FL -strip-all
.Pp
.It Fl s , Fl -strip-all
Omit
.Li \.symtab
section from the output file.
.
.Pp
.It Fl u Ar symbol , Fl -undefined Ns = Ns Ar symbol
If
.Ar symbol
@@ -377,13 +380,13 @@ and if there is an static archive that contains an object file defining
pull out the object file and link it so that the \
output file contains a definition of
.Ar symbol .
.
.Pp
.It Fl -Bdynamic
Link against shared libraries.
.
.Pp
.It Fl -Bstatic
Do not link against shared libraries.
.
.Pp
.It Fl -Bsymbolic
When creating a shared library, make global symbols export-only
(i.e. do not import the same symbol).
@@ -392,30 +395,43 @@ negating symbol override at runtime.
See
.Sx Dynamic symbol resolution
for more information about symbol imports and exports.
.
.Pp
.It Fl -Bsymbolic-functions
Have the same effect as
.Fl -Bsymbolic
but works only for function symbols.
Data symbols remains both imported and exported.
.
Data symbols remains being both imported and exported.
.Pp
.It Fl -Bno-symbolic
Cancel
.Fl -Bsymbolic
and
.Fl -Bsymbolic-functions .
.
.Pp
.It Fl -Map Ns = Ns Ar file
Write map file to
.Ar file .
.
.Pp
.It Fl -Tbss Ns = Ns Ar address
Alias for
.Fl -section-start=.bss Ns = Ns Ar address .
.Pp
.It Fl -Tdata Ns = Ns Ar address
Alias for
.Fl -section-start=.data Ns = Ns Ar address .
.Pp
.It Fl -Ttext Ns = Ns Ar address
Alias for
.Fl -section-start=.text Ns = Ns Ar address .
.Pp
.It Fl -allow-multiple-definition
Normally, the linker reports an error if there are more than one \
definition of a symbol.
This option changes the default behavior so that it doesn't report an error \
for duplicate definitions and instead use the first definition.
.
.It Fl -as-needed , -no-as-needed
.Pp
.It Fl -as-needed
.It Fl -no-as-needed
By default, shared libraries given to a linker are unconditionally added to \
the list of required libraries in an output file.
However, shared libraries after
@@ -424,13 +440,15 @@ are added to the list only when at least one symbol is actually used by an \
object file.
In other words, shared libraries after
.Fl -as-needed
are not added to the list if they are not needed by a program.
are not added to the list of needed libraries if they are not needed by a program.
.Pp
The
.Fl -no-as-needed
option restores the default behavior for subsequent files.
.
.It Fl -build-id , Fl -no-build-id , Fl -build-id Ns = Ns Op Sy none | md5 | sha1 | sha256 | uuid | 0x Ns Ar hexstring
.Pp
.It Fl -build-id
.It Fl -build-id Ns = Ns Op Sy none | md5 | sha1 | sha256 | uuid | 0x Ns Ar hexstring
.It Fl -no-build-id
Create a
.Li .note.gnu.build-id
section containing a byte string to
@@ -449,17 +467,17 @@ sets a random 128-bit UUID.
.Sy 0x Ns Ar hexstring
sets
.Ar hexstring .
.
.Pp
.It Fl -chroot Ns = Ns Ar dir
Set
.Ar dir
to root directory.
.
.Pp
.It Fl -compress-debug-sections Ns = Ns Op Sy none | zlib | zlib-gabi | zlib-gnu
Compress DWARF debug info
.Pf ( Sy .debug_*
sections) using the zlib compression algorithm.
.
.Pp
.It Fl -defsym Ns = Ns Ar symbol Ns = Ns Ar value
Define
.Ar symbol
@@ -474,32 +492,76 @@ prefix) or a symbol name.
If an integer is given as a value,
.Ar symbol
is defined as an absolute symbol with the given value.
.
.Pp
.It Fl -default-symver
Use soname as a symbol version and append that version to all symbols.
.It Fl -demangle , -no-demangle
.Pp
.It Fl -demangle
.It Fl -no-demangle
Demangle C++ symbols in log messages.
.
.Pp
.It Fl -dependency-file Ns = Ns Ar file
Write a dependency file to
.Ar file .
The contents of the written file is readable by
.Cm make ,
which defines only one rule with the linker's output file as a target \
and all input fiels as its prerequisite. Users are expected to include \
the generated dependency file into a Makefile to automate the \
dependency management. This option is analogous to the compiler's
.Fl MM Fl MF
options.
.Pp
.It Fl -dynamic-list Ns = Ns Ar file
Read a list of dynamic symbols from
.Ar file .
.
.It Fl -eh-frame-hdr , -no-eh-frame-hdr
.Pp
.It Fl -eh-frame-hdr
.It Fl -no-eh-frame-hdr
Create
.Li .eh_frame_hdr
section.
.
.Pp
.It Fl -emit-relocs
A linker usually "consumes" relocation sections. That is, a linker \
applies relocations to other sections, and relocation sections themselves \
are discarded.
.Pp
The
.Fl -emit-relocs
instructs the linker to leave relocation sections in the output file. \
Some post-link binary analysis or optimization tools such as LLVM Bolt \
need them.
.Pp
.Nm
always creates RELA-type relocation sections even if the native \
ELF format is REL-type so that it is easy to read addends.
.Pp
.It Fl -enable-new-dtags
.It Fl -disable-new-dtags
By default,
.Nm
emits DT_RUNPATH for
.Fl -rpath .
If you pass
.Fl -disable-new-dtags,
mold emits DT_RPATH for
.Fl -rpath
instead.
.Pp
.Pp
.It Fl -exclude-libs Ns = Ns Ar libraries Ns ...
Mark all symbols in the given
.Ar libraries
hidden.
.
.Pp
.It Fl -fini Ns = Ns Ar symbol
Call
.Ar symbol
at unload-time.
.
.It Fl -fork , -no-fork
.Pp
.It Fl -fork
.It Fl -no-fork
Spawn a child process and let it do the actual linking.
When linking a large program, the OS kernel can take a few hundred \
milliseconds to terminate a
@@ -507,29 +569,34 @@ milliseconds to terminate a
process.
.Fl -fork
hides that latency.
.
.It Fl -gc-sections , -no-gc-sections
.Pp
.It Fl -gc-sections
.It Fl -no-gc-sections
Remove unreferenced sections.
.
.Pp
.It Fl -hash-style Ns = Ns Op Sy sysv | gnu | both
Set hash style.
.
.It Fl -icf Ns = Ns Sy all , Fl -no-icf
.Pp
.It Fl -icf Ns = Ns Sy all
.It Fl -no-icf
Fold identical code.
.
.Pp
.It Fl -image-base Ns = Ns Ar addr
Set the base address to
.Ar addr .
.
.Pp
.It Fl -init Ns = Ns Ar symbol
Call
.Ar symbol
at load-time.
.
.Pp
.It Fl -no-undefined
Report undefined symbols (even with
.Fl -shared ) .
.
.Pp
.It Fl -noinhibit-exec
Create an output file even if errors occur.
.Pp
.It Fl -pack-dyn-relocs Ns = Ns Op Sy none | relr
If
.Sy relr
@@ -543,55 +610,80 @@ or
.Li .rela.dyn
section. Since
.Li .relr.dyn
section uses a space-efficient encoding scheme, specifying that flag \
section uses a space-efficient encoding scheme, specifying this flag \
can reduce the size of the output. This is typically most effective \
for position-independent executable. Note that a runtime loader has to \
support
for position-independent executable.
.Pp
Note that a runtime loader has to support
.Li .relr.dyn
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 -perf
Print performance statistics.
.
.It Fl -pie , -pic-executable , -no-pie , -no-pic-executable
.Pp
.It Fl -pie , -pic-executable
.It Fl -no-pie , -no-pic-executable
Create a position-independent executable.
.
.It Fl -pop-state
Pop state of flags governing input file handling.
.
.Pp
.It Fl -preload
Preload object files.
.
.It Fl -print-gc-sections , -no-print-gc-sections
.Pp
.It Fl -print-gc-sections
.It Fl -no-print-gc-sections
Print removed unreferenced sections.
.
.It Fl -print-icf-sections , -no-print-icf-sections
.Pp
.It Fl -print-icf-sections
.It Fl -no-print-icf-sections
Print folded identical sections.
.
.Pp
.It Fl -push-state
Pop state of flags governing input file handling
.
.It Fl -quick-exit , -no-quick-exit
.It Fl -pop-state
.Fl -push-state
saves the current values of
.Fl -as-needed ,
.Fl -whole-archive ,
.Fl -static ,
and
.Fl -start-lib .
The saved values can be restored by
.Fl -pop-state .
.Pp
.Fl -push-state
and
.Fl -pop-state
pairs can nest.
.Pp
These options are useful when you want to construct linker command line \
options programmatically. For example, if you want to link
.Ar libfoo.so
by as-needed basis but don't want to change the global state of
.Fl -as-needed ,
you can append "--push-state --as-needed -lfoo --pop-state" to the \
linker command line options.
.Pp
.It Fl -quick-exit
.It Fl -no-quick-exit
Use
.Dv quick_exit
to exit.
.
.It Fl -relax , -no-relax
.Pp
.It Fl -relax
.It Fl -no-relax
Rewrite machine instructions with more efficient ones for some relocations.
The feature is enabled by default.
.
.Pp
.It Fl -require-defined Ns = Ns Ar symbol
Like
.Fl -undefined ,
except the new symbol must be defined by the end of the link.
.
.Pp
.It Fl -repro
Embed input files into
.Dv .repro
section.
.
.Pp
.It Fl -retain-symbols-file Ns = Ns Ar file
Keep only symbols listed in
.Ar file .
@@ -608,12 +700,16 @@ Note that this option removes symbols only from
section and does not affect
.Dv .dynsym
section, which is used for dynamic linking.
.
.Pp
.It Fl -reverse-sections
Reverses the order of input sections before assigning them the offsets \
in the output file.
.Pp
.It Fl -rpath Ns = Ns Ar dir
Add
.Ar dir
to runtime search path.
.
.Pp
.It Fl -run Cm command Ar arg Ar
Run
.Cm command
@@ -621,33 +717,64 @@ with
.Nm
as
.Pa /usr/bin/ld .
.
.Pp
.It Fl -section-start Ns = Ns Ar section Ns = Ns Ar address
Set
.Ar address
to
.Ar section .
.Ar address
is a hexadecimal number that may start with an optional
.Sq 0x .
.Pp
.It Fl -shared , -Bshareable
Create a share library.
.
.Pp
.It Fl -shuffle-sections
.It Fl -shuffle-sections Ns = Ns Ar number
Randomizes the output by shuffleing the order of input sections before \
assigning them the offsets in the output file. If
.Ar number
is given, it's used as a seed for the random number generator, so that \
the linker produces the same output as for the same seed. If no seed \
is given, it uses a random number as a seed.
.Pp
.It Fl -spare-dynamic-tags Ns = Ns Ar number
Reserve given
.Ar number
of tags in
.Dv .dynamic
section.
.
.Pp
.It Fl -start-lib
.It Fl -end-lib
Handle object files between
.Fl -start-lib
and
.Fl -end-lib
as if they were in an archive file. That means object files between them \
are linked only when they are needed to resolve undefined symbols. \
The options are useful if you want to link object files only when they are \
needed but want to avoid the overhead of running
.Xr ar 3 .
.Pp
.It Fl -static
Do not link against shared libraries.
.
.Pp
.It Fl -stats
Print input statistics.
.
.Pp
.It Fl -sysroot Ns = Ns Ar dir
Set target system root directory to
.Ar dir .
.
.Pp
.It Fl -thread-count Ns = Ns Ar count
Use
.Ar count
number of threads.
.
.It Fl -threads , -no-threads
.Pp
.It Fl -threads
.It Fl -no-threads
Use multiple threads.
By default,
.Nm
@@ -659,34 +786,40 @@ To use only one thread, pass
.Fl -no-threads
or
.Fl -thread-count Ns = Ns Sy 1 .
.
.Pp
.It Fl -trace
Print name of each input file.
.
.Pp
.It Fl -unique Ns = Ns Ar pattern
Don't merge input sections that match
.Ar pattern .
.
.Pp
.It Fl -unresolved-symbols Ns = Ns Op Sy \
report-all | ignore-all | ignore-in-object-files | ignore-in-shared-libs
How to handle undefined symbols.
.
.Pp
.It Fl -version-script Ns = Ns Ar file
Read version script from
.Ar file .
.
.Pp
.It Fl -warn-common
.It Fl -no-warn-common
Warn about common symbols.
.
.It Fl -warn-unresolved-symbols , -error-unresolved-symbols
.Pp
.It Fl -warn-once
Only warn once for each undefined symbol instead of warn for each relocation
referring an undefined symbol.
.Pp
.It Fl -warn-unresolved-symbols
.It Fl -error-unresolved-symbols
Normally, the linker reports an error for unresolved symbols.
.Fl -warn-unresolved-symbols
option turns it into a warning.
.Fl -error-unresolved-symbols
option restores the default behavior.
.
.It Fl -whole-archive , -no-whole-archive
.Pp
.It Fl -whole-archive
.It Fl -no-whole-archive
When archive files
.Pf ( Sy .a
files) are given to a linker, only object
@@ -700,7 +833,7 @@ all archive members to the output, you should pass
.Fl -whole-archive .
.Fl -no-whole-archive
restores the default behavior for subsequent archives.
.
.Pp
.It Fl -wrap Ns = Ns Ar symbol
Make
.Ar symbol
@@ -709,8 +842,8 @@ to be resolved to
The original symbol can be resolved as
.Sy __real_ Ns Ar symbol .
This option is typically used for wrapping an existing function.
.
.It Fl z Cm cet-report Ns = Ns Sy none | warning | error
.Pp
.It Fl z Cm cet-report Ns = Ns Op Sy none | warning | error
Intel Control-flow Enforcement Technology (CET) is a new x86 feature \
available since Tiger Lake which is released in 2020.
It defines new instructions to harden security to protect programs from \
@@ -740,8 +873,9 @@ bit in
.Li .note.gnu.property
section to determine whether or not an object file was compiled with
.Fl fcf-protection .
.
.It Fl z Cm now , Fl z Cm lazy
.Pp
.It Fl z Cm now
.It Fl z Cm lazy
By default, functions referring other ELF modules are resolved by the
dynamic linker when they are called for the first time.
.Fl z Cm now
@@ -749,12 +883,12 @@ marks an executable or a shared library file so that all dynamic
symbols are loaded when a file is loaded to memory.
.Fl z Cm lazy
restores the default behavior.
.
.Pp
.It Fl z Cm origin
Mark object requiring immediate
.Dv $ORIGIN
processing at runtime.
.
.Pp
.It Fl z Cm ibt
Turn on
.Li GNU_PROPERTY_X86_FEATURE_1_IBT
@@ -762,7 +896,7 @@ bit in
.Li .note.gnu.property
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
@@ -776,21 +910,23 @@ 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 ibtplt
.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.
.
.It Fl z Cm execstack , Fl z Cm noexecstack
.Pp
.It Fl z Cm execstack
.It Fl z Cm noexecstack
By default, the pages for the stack area (i.e. the pages where local
variables reside) are not executable for security reasons.
.Fl z Cm execstack
makes it executable.
.Fl z Cm noexecstack
restores the default behavior.
.
.It Fl z Cm keep-text-section-prefix , Fl z Cm nokeep-text-section-prefix
.Pp
.It Fl z Cm keep-text-section-prefix
.It Fl z Cm nokeep-text-section-prefix
Keep
.Dv .text.hot ,
.Dv .text.unknown ,
@@ -799,8 +935,9 @@ Keep
and
.Dv .text.exit
as separate sections in the final binary.
.
.It Fl z Cm relro , Fl z Cm norelro
.Pp
.It Fl z Cm relro
.It Fl z Cm norelro
Some sections such as
.Dv .dynamic
have to be writable only during an executable or \
@@ -822,8 +959,10 @@ generates a
segment.
.Fl z Cm norelro
disables the feature.
.
.It Fl z Cm separate-loadable-segments , Fl z Cm separate-code , Fl z Cm noseparate-code
.Pp
.It Fl z Cm separate-loadable-segments
.It Fl z Cm separate-code
.It Fl z Cm noseparate-code
If one memory page contains multiple segments,
the page protection bits are set in such a way that needed attributes \
(writable or executable) are satisifed for all segments.
@@ -840,18 +979,20 @@ adds paddings only between executable and non-executable segments.
.Pp
.Cm noseparate-code
does not add any paddings between segments.
.
.It Fl z Cm defs , Fl z Cm nodefs
.Pp
.It Fl z Cm defs
.It Fl z Cm nodefs
Report undefined symbols (even with
.Fl -shared ) .
.
.Pp
.It Fl z Cm shstk
Enforce shadow stack by turning GNU_PROPERTY_X86_FEATURE_1_SHSTK bit in
.Li .note.gnu.property
output section. Shadow stack is part of Intel Control-flow Enforcement \
Technology (CET), which is available since Tiger Lake (2020).
.
.It Fl z Cm notext , Fl z Cm textoff , Fl z Cm text
.Pp
.It Fl z Cm text
.It Fl z Cm notext , Fl z Cm textoff
.Nm
by default reports an error if dynamic relocations are created in read-only \
sections.
@@ -864,7 +1005,7 @@ are given,
creates such dynamic relocations without reporting an error.
.Fl z Cm text
restores the default behavior.
.
.Pp
.It Fl z Cm max-page-size
Some CPU ISAs support multiple different memory page sizes.
This option specifies the maximum page size that an output binary can run on.
@@ -872,31 +1013,31 @@ If you specify a large value, the output can run on both large and small page \
systems, but it wastes a bit of memory at page boundaries on systems with \
small pages.
.Pp
The default value is 4 KiB for i386 and x86-64, and 64 KiB for ARM64.
.
The default value is 4 KiB for i386, x86-64 and RISC-V, and 64 KiB for ARM64.
.Pp
.It Fl z Cm nodefaultlib
Make the dynamic loader to ignore default search paths.
.
.Pp
.It Fl z Cm nodelete
Mark DSO non-deletable at runtime.
.
.Pp
.It Fl z Cm nodlopen
Mark DSO not available to
.Xr dlopen 3 .
.
.Pp
.It Fl z Cm nodump
Mark DSO not available to
.Xr dldump 3 .
.
.Pp
.It Fl z Cm nocopyreloc
Do not create copy relocations.
.
.Pp
.It Fl z Cm initfirst
Mark DSO to be initialized first at runtime.
.
.Pp
.It Fl z Cm interpose
Mark object to interpose all DSOs but executable.
.
.Pp
.ig
.It Fl (
.It Fl )
@@ -905,9 +1046,7 @@ Mark object to interpose all DSOs but executable.
.It Fl -allow-shlib-undefined
.It Fl -color-diagnostics
.It Fl -dc
.It Fl -disable-new-dtags
.It Fl -dp
.It Fl -enable-new-dtags
.It Fl -end-group
.It Fl -fatal-warnings
.It Fl -gdb-index
@@ -939,6 +1078,7 @@ Ignored
.Sh SEE ALSO
.Xr gold 1 ,
.Xr ld 1 ,
.Xr elf 5
.Xr ld.so 8
.
.\"=============================================================================
+600
View File
@@ -0,0 +1,600 @@
// ARM32 is a bit special from the linker's viewpoint because ARM
// processors support two different instruction encodings: Thumb and
// ARM (in a narrower sense). Thumb instructions are either 16 bits or
// 32 bits, while ARM instructions are all 32 bits. Feature-wise,
// thumb is a subset of ARM, so not all ARM instructions are
// representable in Thumb.
//
// ARM processors originally supported only ARM instructions. Thumb
// instructions were later added to increase code density.
//
// ARM processors runs in either ARM mode or Thumb mode. The mode can
// 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.
//
// 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.
// 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).
#include "mold.h"
#include <tbb/parallel_for.h>
#include <tbb/parallel_for_each.h>
#include <tbb/parallel_sort.h>
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;
}
static void write_thm_b_imm(u8 *loc, u32 val) {
// https://developer.arm.com/documentation/ddi0406/cb/Application-Level-Architecture/Instruction-Details/Alphabetical-list-of-instructions/BL--BLX--immediate-
u32 sign = bit(val, 24);
u32 I1 = bit(val, 23);
u32 I2 = bit(val, 22);
u32 J1 = !I1 ^ sign;
u32 J2 = !I2 ^ sign;
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;
}
static void write_thm_mov_imm(u8 *loc, u32 val) {
// https://developer.arm.com/documentation/ddi0406/cb/Application-Level-Architecture/Instruction-Details/Alphabetical-list-of-instructions/MOVT
u32 imm4 = bits(val, 15, 12);
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;
}
template <>
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
};
memcpy(buf, plt0, sizeof(plt0));
*(u32 *)(buf + 16) = ctx.gotplt->shdr.sh_addr - this->shdr.sh_addr - 16;
for (Symbol<E> *sym : symbols) {
static const u32 plt[] = {
0xe59fc004, // 1: ldr ip, 2f
0xe08cc00f, // add ip, ip, pc
0xe59cf000, // ldr pc, [ip]
0x00000000, // 2: .word sym@PLTGOT - 1b
};
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;
}
}
template <>
void PltGotSection<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + this->shdr.sh_offset;
for (Symbol<E> *sym : symbols) {
static const u32 plt[] = {
0xe59fc004, // 1: ldr ip, 2f
0xe08cc00f, // add ip, ip, pc
0xe59cf000, // ldr pc, [ip]
0x00000000, // 2: .word sym@GOT - 1b
};
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;
}
}
// 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,
u64 offset, u64 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);
i64 frag_idx = 0;
if (ctx.reldyn)
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
file.reldyn_offset + this->reldyn_offset);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_ARM_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
const SectionFragmentRef<E> *frag_ref = nullptr;
if (rel_fragments && rel_fragments[frag_idx].idx == i)
frag_ref = &rel_fragments[frag_idx++];
#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 T (sym.get_addr(ctx) & 1)
#define G (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
#define GOT ctx.got->shdr.sh_addr
switch (rel.r_type) {
case R_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;
}
continue;
case R_ARM_REL32:
*(u32 *)loc = S + A - P;
continue;
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
} else {
write_thm_b_imm(loc, align_to(S + A - P, 4));
*(u16 *)(loc + 2) &= ~(1 << 12); // rewrite with BLX
}
continue;
case R_ARM_BASE_PREL:
*(u32 *)loc = GOT + A - P;
continue;
case R_ARM_GOT_PREL:
*(u32 *)loc = G + A - P;
continue;
case R_ARM_GOT_BREL:
*(u32 *)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;
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;
}
*(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);
} 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);
}
continue;
case R_ARM_MOVW_PREL_NC:
write_mov_imm(loc, ((S + A) | T) - P);
continue;
case R_ARM_MOVW_ABS_NC:
write_mov_imm(loc, (S + A) | T);
continue;
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);
continue;
}
case R_ARM_THM_MOVW_ABS_NC:
write_thm_mov_imm(loc, (S + A) | T);
continue;
case R_ARM_MOVT_PREL:
write_mov_imm(loc, (S + A - P) >> 16);
continue;
case R_ARM_THM_MOVT_PREL:
write_thm_mov_imm(loc, (S + A - P) >> 16);
continue;
case R_ARM_MOVT_ABS:
write_mov_imm(loc, (S + A) >> 16);
continue;
case R_ARM_THM_MOVT_ABS:
write_thm_mov_imm(loc, (S + A) >> 16);
continue;
case R_ARM_TLS_GD32:
*(u32 *)loc = sym.get_tlsgd_addr(ctx) + A - P;
continue;
case R_ARM_TLS_LDM32:
*(u32 *)loc = ctx.got->get_tlsld_addr(ctx) + A - P;
continue;
case R_ARM_TLS_LDO32:
*(u32 *)loc = S + A - ctx.tls_begin;
continue;
case R_ARM_TLS_IE32:
*(u32 *)loc = sym.get_gottp_addr(ctx) + A - P;
continue;
case R_ARM_TLS_LE32:
*(u32 *)loc = S + A - ctx.tls_begin + 8;
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;
continue;
case R_ARM_THM_TLS_CALL:
if (sym.get_tlsdesc_idx(ctx) == -1) {
// BL -> NOP
*(u32 *)loc = 0x8000f3af;
} 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
}
continue;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
#undef S
#undef A
#undef P
#undef T
#undef G
#undef GOT
}
}
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_ARM_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
if (!sym.file) {
report_undef(ctx, file, sym);
continue;
}
SectionFragment<E> *frag;
i64 addend;
std::tie(frag, addend) = get_fragment(ctx, rel);
#define S (frag ? frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag ? addend : this->get_addend(rel))
switch (rel.r_type) {
case R_ARM_ABS32:
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
else
*(u32 *)loc = S + A;
break;
case R_ARM_TLS_LDO32:
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
else
*(u32 *)loc = S + A - ctx.tls_begin;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
<< rel;
break;
}
#undef S
#undef A
}
}
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_ARM_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (!sym.file) {
report_undef(ctx, file, sym);
continue;
}
if (sym.get_type() == STT_GNU_IFUNC) {
sym.flags |= NEEDS_GOT;
sym.flags |= NEEDS_PLT;
}
switch (rel.r_type) {
case R_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);
break;
}
case R_ARM_REL32:
case R_ARM_BASE_PREL:
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);
break;
}
case R_ARM_TLS_GD32:
sym.flags |= NEEDS_TLSGD;
break;
case R_ARM_TLS_LDM32:
ctx.needs_tlsld = true;
break;
case R_ARM_TLS_IE32:
sym.flags |= NEEDS_GOTTP;
break;
case R_ARM_TLS_GOTDESC:
if (!ctx.relax_tlsdesc || sym.is_imported)
sym.flags |= NEEDS_TLSDESC;
break;
case R_ARM_THM_JUMP11:
case R_ARM_MOVW_PREL_NC:
case R_ARM_MOVW_ABS_NC:
case R_ARM_THM_MOVW_PREL_NC:
case R_ARM_THM_MOVW_ABS_NC:
case R_ARM_TLS_LDO32:
case R_ARM_TLS_LE32:
case R_ARM_THM_TLS_CALL:
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
// 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);
}
}
void ThumbToArmSection::update_shdr(Context<E> &ctx) {
this->shdr.sh_size = symbols.size() * ENTRY_SIZE;
}
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[] = {
0xe08e0000, // add r0, lr, r0
0xe5901004, // ldr r1, [r0, #4]
0xe12fff11, // bx r1
};
memcpy(ctx.buf + this->shdr.sh_offset, insn, sizeof(insn));
}
// ARM executables use an .ARM.exidx section to look up an exception
// handling record for the current instruction pointer. The table needs
// to be sorted by their addresses.
//
// Other target uses .eh_frame_hdr instead for the same purpose.
// I don't know why only ARM uses the different mechanism, but it's
// likely that it's due to some historical reason.
//
// This function sorts .ARM.exidx records.
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();
if (!osec)
return;
// .ARM.exidx records consists of a signed 31-bit relative address
// and a 32-bit value. The relative address indicates the start
// address of a function that the record covers. The value is one of
// the followings:
//
// 1. CANTUNWIND indicating that there's no unwinding info for the function,
// 2. a compact unwinding record encoded into a 32-bit value, or
// 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.
struct Entry {
u32 addr;
u32 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);
struct Entry2 {
u32 addr;
u32 val;
u32 idx;
};
// 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);
});
// 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);
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);
}
}
} // namespace mold::elf
+100 -96
View File
@@ -22,30 +22,17 @@ static u64 page(u64 val) {
return val & ~(u64)0xfff;
}
template <>
void GotPltSection<E>::copy_buf(Context<E> &ctx) {
u64 *buf = (u64 *)(ctx.buf + this->shdr.sh_offset);
// The first slot of .got.plt points to _DYNAMIC.
buf[0] = ctx.dynamic ? ctx.dynamic->shdr.sh_addr : 0;
buf[1] = 0;
buf[2] = 0;
for (Symbol<E> *sym : ctx.plt->symbols)
buf[sym->get_gotplt_idx(ctx)] = ctx.plt->shdr.sh_addr;
}
static void write_plt_header(Context<E> &ctx, u8 *buf) {
// Write PLT header
static const u8 plt0[] = {
0xf0, 0x7b, 0xbf, 0xa9, // stp x16, x30, [sp,#-16]!
0x10, 0x00, 0x00, 0x90, // adrp x16, .got.plt[2]
0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, .got.plt[2]]
0x10, 0x02, 0x00, 0x91, // add x16, x16, .got.plt[2]
0x20, 0x02, 0x1f, 0xd6, // br x17
0x1f, 0x20, 0x03, 0xd5, // nop
0x1f, 0x20, 0x03, 0xd5, // nop
0x1f, 0x20, 0x03, 0xd5, // nop
static const u32 plt0[] = {
0xa9bf7bf0, // stp x16, x30, [sp,#-16]!
0x90000010, // adrp x16, .got.plt[2]
0xf9400211, // ldr x17, [x16, .got.plt[2]]
0x91000210, // add x16, x16, .got.plt[2]
0xd61f0220, // br x17
0xd503201f, // nop
0xd503201f, // nop
0xd503201f, // nop
};
u64 gotplt = ctx.gotplt->shdr.sh_addr + 16;
@@ -60,11 +47,11 @@ static void write_plt_header(Context<E> &ctx, u8 *buf) {
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;
static const u8 data[] = {
0x10, 0x00, 0x00, 0x90, // adrp x16, .got.plt[n]
0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, .got.plt[n]]
0x10, 0x02, 0x00, 0x91, // add x16, x16, .got.plt[n]
0x20, 0x02, 0x1f, 0xd6, // br x17
static const u32 data[] = {
0x90000010, // adrp x16, .got.plt[n]
0xf9400211, // ldr x17, [x16, .got.plt[n]]
0x91000210, // add x16, x16, .got.plt[n]
0xd61f0220, // br x17
};
u64 gotplt = sym.get_gotplt_addr(ctx);
@@ -91,11 +78,11 @@ void PltGotSection<E>::copy_buf(Context<E> &ctx) {
for (Symbol<E> *sym : symbols) {
u8 *ent = buf + sym->get_pltgot_idx(ctx) * ARM64::pltgot_size;
static const u8 data[] = {
0x10, 0x00, 0x00, 0x90, // adrp x16, GOT[n]
0x11, 0x02, 0x40, 0xf9, // ldr x17, [x16, GOT[n]]
0x20, 0x02, 0x1f, 0xd6, // br x17
0x1f, 0x20, 0x03, 0xd5, // nop
static const u32 data[] = {
0x90000010, // adrp x16, GOT[n]
0xf9400211, // ldr x17, [x16, GOT[n]]
0xd61f0220, // br x17
0xd503201f, // nop
};
u64 got = sym->get_got_addr(ctx);
@@ -109,18 +96,18 @@ void PltGotSection<E>::copy_buf(Context<E> &ctx) {
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
u64 loc, u64 val) {
u8 *base = ctx.buf + this->shdr.sh_offset;
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_AARCH64_ABS64:
*(u64 *)(base + loc) = val;
*(u64 *)loc = val;
return;
case R_AARCH64_PREL32:
*(u32 *)(base + loc) = val - this->shdr.sh_addr - loc;
*(u32 *)loc = val - this->shdr.sh_addr - offset;
return;
case R_AARCH64_PREL64:
*(u64 *)(base + loc) = val - this->shdr.sh_addr - loc;
*(u64 *)loc = val - this->shdr.sh_addr - offset;
return;
}
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
@@ -130,6 +117,7 @@ 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();
i64 frag_idx = 0;
@@ -162,22 +150,18 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
#define G (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
#define GOT ctx.got->shdr.sh_addr
if (needs_dynrel[i]) {
*dynrel++ = {P, R_AARCH64_ABS64, (u32)sym.get_dynsym_idx(ctx), A};
*(u64 *)loc = A;
continue;
}
if (needs_baserel[i]) {
if (!is_relr_reloc(ctx, rel))
*dynrel++ = {P, R_AARCH64_RELATIVE, 0, (i64)(S + A)};
*(u64 *)loc = S + A;
continue;
}
switch (rel.r_type) {
case R_AARCH64_ABS64:
*(u64 *)loc = S + A;
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;
}
continue;
case R_AARCH64_LDST8_ABS_LO12_NC:
*(u32 *)loc |= bits(S + A, 11, 0) << 10;
@@ -197,12 +181,15 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
case R_AARCH64_ADD_ABS_LO12_NC:
*(u32 *)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;
continue;
case R_AARCH64_MOVW_UABS_G1:
case R_AARCH64_MOVW_UABS_G1_NC:
*(u32 *)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;
continue;
@@ -249,7 +236,8 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
*(u32 *)loc |= (val >> 2) & 0x3ffffff;
continue;
}
case R_AARCH64_CONDBR19: {
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;
@@ -294,6 +282,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
*(u32 *)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;
continue;
@@ -357,7 +346,6 @@ 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);
i64 frag_idx = 0;
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
@@ -368,7 +356,7 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
u8 *loc = base + rel.r_offset;
if (!sym.file) {
report_undef(ctx, sym);
report_undef(ctx, file, sym);
continue;
}
@@ -381,7 +369,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
switch (rel.r_type) {
case R_AARCH64_ABS64:
*(u64 *)loc = S + A;
if (std::optional<u64> val = get_tombstone(sym))
*(u64 *)loc = *val;
else
*(u64 *)loc = S + A;
continue;
case R_AARCH64_ABS32:
*(u32 *)loc = S + A;
@@ -399,7 +390,7 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr.sh_flags & SHF_ALLOC);
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<ElfRel<E>> rels = get_rels(ctx);
@@ -413,7 +404,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (!sym.file) {
report_undef(ctx, sym);
report_undef(ctx, file, sym);
continue;
}
@@ -428,7 +419,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
// Absolute Local Imported data Imported code
{ NONE, BASEREL, DYNREL, DYNREL }, // DSO
{ NONE, BASEREL, DYNREL, DYNREL }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
@@ -450,9 +441,9 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
case R_AARCH64_ADR_PREL_PG_HI21: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, NONE, ERROR, ERROR }, // DSO
{ NONE, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
{ ERROR, NONE, ERROR, ERROR }, // DSO
{ ERROR, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
@@ -469,19 +460,24 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
case R_AARCH64_ADD_ABS_LO12_NC:
case R_AARCH64_ADR_PREL_LO21:
case R_AARCH64_CONDBR19:
case R_AARCH64_LD_PREL_LO19:
case R_AARCH64_LDST16_ABS_LO12_NC:
case R_AARCH64_LDST32_ABS_LO12_NC:
case R_AARCH64_LDST64_ABS_LO12_NC:
case R_AARCH64_LDST128_ABS_LO12_NC:
case R_AARCH64_LDST8_ABS_LO12_NC:
case R_AARCH64_MOVW_UABS_G0:
case R_AARCH64_MOVW_UABS_G0_NC:
case R_AARCH64_MOVW_UABS_G1:
case R_AARCH64_MOVW_UABS_G1_NC:
case R_AARCH64_MOVW_UABS_G2:
case R_AARCH64_MOVW_UABS_G2_NC:
case R_AARCH64_MOVW_UABS_G3:
case R_AARCH64_PREL16:
case R_AARCH64_PREL32:
case R_AARCH64_PREL64:
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
case R_AARCH64_TLSLE_ADD_TPREL_LO12:
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
case R_AARCH64_TLSGD_ADD_LO12_NC:
case R_AARCH64_TLSDESC_CALL:
@@ -494,9 +490,9 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
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_THUNK;
sym->extra.thunk_idx = -1;
sym->extra.thunk_sym_idx = -1;
sym->flags &= (u8)~NEEDS_RANGE_EXTN_THUNK;
}
}
@@ -519,7 +515,7 @@ static bool is_reachable(Context<E> &ctx, Symbol<E> &sym,
}
// We create a thunk no further than 100 MiB from any section.
constexpr i64 MAX_DISTANCE = 100 * 1024 * 1024;
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;
@@ -547,9 +543,9 @@ static void create_thunks(Context<E> &ctx, OutputSection<E> &osec) {
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, members[d]->shdr.sh_addralign);
offset = align_to(offset, 1 << members[d]->p2align);
members[d]->offset = offset;
offset += members[d]->shdr.sh_size;
offset += members[d]->sh_size;
d++;
}
@@ -560,7 +556,7 @@ static void create_thunks(Context<E> &ctx, OutputSection<E> &osec) {
// Move A forward so that A is reachable from C.
if (c > 0) {
i64 c_end = members[c - 1]->offset + members[c - 1]->shdr.sh_size;
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++]);
@@ -574,9 +570,11 @@ static void create_thunks(Context<E> &ctx, OutputSection<E> &osec) {
thunk.offset = offset;
// Scan relocations between B and C to collect symbols that need thunks.
tbb::parallel_for_each(&members[b], &members[c], [&](InputSection<E> *isec) {
tbb::parallel_for_each(members.begin() + b, members.begin() + c,
[&](InputSection<E> *isec) {
std::span<ElfRel<E>> rels = isec->get_rels(ctx);
isec->range_extn.resize(rels.size());
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];
@@ -590,15 +588,16 @@ static void create_thunks(Context<E> &ctx, OutputSection<E> &osec) {
continue;
// If the symbol is already in another thunk, reuse it.
if (sym.thunk_idx != -1) {
isec->range_extn[i] = {sym.thunk_idx, sym.thunk_sym_idx};
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.
isec->range_extn[i] = {thunk.thunk_idx, -1};
range_extn[i] = {thunk.thunk_idx, -1};
if (!(sym.flags.fetch_or(NEEDS_THUNK) & NEEDS_THUNK)) {
if (!(sym.flags.fetch_or(NEEDS_RANGE_EXTN_THUNK) &
NEEDS_RANGE_EXTN_THUNK)) {
std::scoped_lock lock(thunk.mu);
thunk.symbols.push_back(&sym);
}
@@ -614,20 +613,20 @@ static void create_thunks(Context<E> &ctx, OutputSection<E> &osec) {
// 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++;
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[b], &members[c], [&](InputSection<E> *isec) {
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++) {
RangeExtensionRef &ref = isec->range_extn[i];
if (ref.thunk_idx == thunk.thunk_idx) {
if (range_extn[i].thunk_idx == thunk.thunk_idx) {
Symbol<E> &sym = *isec->file.symbols[rels[i].r_sym];
ref.sym_idx = sym.thunk_sym_idx;
range_extn[i].sym_idx = sym.extra.thunk_sym_idx;
}
}
});
@@ -642,7 +641,7 @@ static void create_thunks(Context<E> &ctx, OutputSection<E> &osec) {
osec.shdr.sh_size = offset;
}
static void mark_thunk_symbols(Context<E> &ctx, OutputSection<E> &osec) {
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);
@@ -652,9 +651,10 @@ static void mark_thunk_symbols(Context<E> &ctx, OutputSection<E> &osec) {
// 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 = isec->range_extn[i];
RangeExtensionRef &ref = range_extn[i];
if (ref.thunk_idx == -1)
continue;
@@ -685,16 +685,16 @@ static void shrink_section(Context<E> &ctx, OutputSection<E> &osec) {
i64 offset = 0;
auto add_thunk = [&]() {
auto add_thunk = [&] {
thunks[0]->offset = offset;
offset += thunks[0]->size();
thunks = thunks.subspan(1);
};
auto add_isec = [&]() {
offset = align_to(offset, members[0]->shdr.sh_addralign);
auto add_isec = [&] {
offset = align_to(offset, 1 << members[0]->p2align);
members[0]->offset = offset;
offset += members[0]->shdr.sh_size;
offset += members[0]->sh_size;
members = members.subspan(1);
};
@@ -722,12 +722,16 @@ static void shrink_section(Context<E> &ctx, OutputSection<E> &osec) {
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)
chunk->shdr.sh_addr = i++ << 31;
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)
@@ -741,9 +745,9 @@ i64 create_range_extension_thunks(Context<E> &ctx) {
set_osec_offsets(ctx);
// Based on the current file layout, remove thunk symbols that turned
// to be unnecessary.
// out to be unnecessary.
tbb::parallel_for_each(sections, [&](OutputSection<E> *osec) {
mark_thunk_symbols(ctx, *osec);
gc_thunk_symbols(ctx, *osec);
});
// Recompute output section sizes that contain thunks. New section
@@ -760,20 +764,20 @@ i64 create_range_extension_thunks(Context<E> &ctx) {
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + output_section.shdr.sh_offset + offset;
static const u8 insn[] = {
0x10, 0x00, 0x00, 0x90, // adrp x16, 0 # R_AARCH64_ADR_PREL_PG_HI21
0x10, 0x02, 0x00, 0x91, // add x16, x16 # R_AARCH64_ADD_ABS_LO12_NC
0x00, 0x02, 0x1f, 0xd6, // br x16
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(insn));
static_assert(ENTRY_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;
u8 *loc = buf + i * ENTRY_SIZE;
memcpy(loc , insn, sizeof(insn));
memcpy(loc , data, sizeof(data));
write_adr(loc, bits(page(S) - page(P), 32, 12));
*(u32 *)(loc + 4) |= bits(S, 11, 0) << 10;
}
+157 -75
View File
@@ -4,31 +4,20 @@ namespace mold::elf {
using E = I386;
template <>
void GotPltSection<E>::copy_buf(Context<E> &ctx) {
u32 *buf = (u32 *)(ctx.buf + this->shdr.sh_offset);
// The first slot of .got.plt points to _DYNAMIC.
buf[0] = ctx.dynamic ? ctx.dynamic->shdr.sh_addr : 0;
buf[1] = 0;
buf[2] = 0;
for (Symbol<E> *sym : ctx.plt->symbols)
buf[sym->get_gotplt_idx(ctx)] = sym->get_plt_addr(ctx) + 6;
}
static void write_plt_header(Context<E> &ctx, u8 *buf) {
if (ctx.arg.pic) {
static const u8 plt0[] = {
0xff, 0xb3, 0x04, 0, 0, 0, // pushl 4(%ebx)
0xff, 0xa3, 0x08, 0, 0, 0, // jmp *8(%ebx)
0x90, 0x90, 0x90, 0x90, // nop
0xff, 0xb3, 0, 0, 0, 0, // pushl GOTPLT+4(%ebx)
0xff, 0xa3, 0, 0, 0, 0, // jmp *GOTPLT+8(%ebx)
0x90, 0x90, 0x90, 0x90, // nop
};
memcpy(buf, plt0, sizeof(plt0));
*(u32 *)(buf + 2) = ctx.gotplt->shdr.sh_addr - ctx.got->shdr.sh_addr + 4;
*(u32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr - ctx.got->shdr.sh_addr + 8;
} else {
static const u8 plt0[] = {
0xff, 0x35, 0, 0, 0, 0, // pushl (GOTPLT+4)
0xff, 0x25, 0, 0, 0, 0, // jmp *(GOTPLT+8)
0xff, 0x35, 0, 0, 0, 0, // pushl GOTPLT+4
0xff, 0x25, 0, 0, 0, 0, // jmp *GOTPLT+8
0x90, 0x90, 0x90, 0x90, // nop
};
memcpy(buf, plt0, sizeof(plt0));
@@ -48,7 +37,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.gotplt->shdr.sh_addr;
*(u32 *)(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
@@ -85,7 +74,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.gotplt->shdr.sh_addr;
*(u32 *)(ent + 2) = symbols[i]->get_got_addr(ctx) - ctx.got->shdr.sh_addr;
}
} else {
static const u8 data[] = {
@@ -103,17 +92,17 @@ void PltGotSection<E>::copy_buf(Context<E> &ctx) {
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
u64 loc, u64 val) {
u8 *base = ctx.buf + this->shdr.sh_offset;
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_386_NONE:
return;
case R_386_32:
*(u32 *)(base + loc) = val;
*(u32 *)loc = val;
return;
case R_386_PC32:
*(u32 *)(base + loc) = val - this->shdr.sh_addr - loc;
*(u32 *)loc = val - this->shdr.sh_addr - offset;
return;
}
unreachable();
@@ -172,24 +161,11 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
*(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 GOTPLT ctx.gotplt->shdr.sh_addr
if (needs_dynrel[i]) {
*dynrel++ = {P, R_386_32, (u32)sym.get_dynsym_idx(ctx)};
write32(A);
continue;
}
if (needs_baserel[i]) {
if (!is_relr_reloc(ctx, rel))
*dynrel++ = {P, R_386_RELATIVE, 0};
write32(S + A);
continue;
}
#define S (frag_ref ? frag_ref->frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag_ref ? frag_ref->addend : this->get_addend(rel))
#define P (output_section->shdr.sh_addr + offset + rel.r_offset)
#define G (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
#define GOT ctx.got->shdr.sh_addr
switch (rel.r_type) {
case R_386_8:
@@ -199,7 +175,16 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
write16(S + A);
continue;
case R_386_32:
write32(S + A);
if (sym.is_absolute() || !ctx.arg.pic) {
write32(S + A);
} else if (sym.is_imported) {
*dynrel++ = {P, R_386_32, (u32)sym.get_dynsym_idx(ctx)};
write32(A);
} else {
if (!is_relr_reloc(ctx, rel))
*dynrel++ = {P, R_386_RELATIVE, 0};
write32(S + A);
}
continue;
case R_386_PC8:
write8s(S + A);
@@ -208,21 +193,28 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
write16s(S + A);
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);
continue;
case R_386_GOT32:
case R_386_GOT32X:
write32(sym.get_got_addr(ctx) + A - GOTPLT);
write32(G + A);
continue;
case R_386_GOTOFF:
write32(S + A - GOTPLT);
write32(S + A - GOT);
continue;
case R_386_GOTPC:
write32(GOTPLT + A - P);
write32(GOT + A - P);
continue;
case R_386_TLS_GOTIE:
write32(sym.get_gottp_addr(ctx) + A - GOTPLT);
write32(sym.get_gottp_addr(ctx) + A - GOT);
continue;
case R_386_TLS_LE:
write32(S + A - ctx.tls_end);
@@ -231,13 +223,72 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
write32(sym.get_gottp_addr(ctx) + A);
continue;
case R_386_TLS_GD:
write32(sym.get_tlsgd_addr(ctx) + A - GOTPLT);
if (sym.get_tlsgd_idx(ctx) == -1) {
// Relax GD to LE
switch (rels[i + 1].r_type) {
case R_386_PLT32: {
static const u8 insn[] = {
0x65, 0xa1, 0, 0, 0, 0, // mov %gs:0, %rax
0x81, 0xe8, 0, 0, 0, 0, // add $0, %rax
};
memcpy(loc - 3, insn, sizeof(insn));
*(u32 *)(loc + 5) = ctx.tls_end - S - A;
break;
}
case R_386_GOT32: {
static const u8 insn[] = {
0x65, 0xa1, 0, 0, 0, 0, // mov %gs:0, %rax
0x81, 0xe8, 0, 0, 0, 0, // add $0, %rax
};
memcpy(loc - 2, insn, sizeof(insn));
*(u32 *)(loc + 6) = ctx.tls_end - S - A;
break;
}
default:
unreachable();
}
i++;
} else {
write32(sym.get_tlsgd_addr(ctx) + A - GOT);
}
continue;
case R_386_TLS_LDM:
write32(ctx.got->get_tlsld_addr(ctx) + A - GOTPLT);
if (ctx.got->tlsld_idx == -1) {
// Relax LD to LE
switch (rels[i + 1].r_type) {
case R_386_PLT32: {
static const u8 insn[] = {
0x65, 0xa1, 0, 0, 0, 0, // mov %gs:0, %eax
0x8d, 0x74, 0x26, 0x00, // lea (%esi,1), %esi
0x90, // nop
};
memcpy(loc - 2, insn, sizeof(insn));
break;
}
case R_386_GOT32: {
static const u8 insn[] = {
0x65, 0xa1, 0, 0, 0, 0, // mov %gs:0, %eax
0x8d, 0x74, 0x26, 0x00, // lea (%esi,1), %esi
0x66, 0x90,
};
memcpy(loc - 2, insn, sizeof(insn));
break;
}
default:
unreachable();
}
i++;
} else {
write32(ctx.got->get_tlsld_addr(ctx) + A - GOT);
}
continue;
case R_386_TLS_LDO_32:
write32(S + A - ctx.tls_begin);
if (ctx.got->tlsld_idx == -1)
write32(S + A - ctx.tls_end);
else
write32(S + A - ctx.tls_begin);
continue;
case R_386_SIZE32:
write32(sym.esym().st_size + A);
@@ -250,7 +301,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
memcpy(loc - 2, insn, sizeof(insn));
write32(S + A - ctx.tls_end);
} else {
write32(sym.get_tlsdesc_addr(ctx) + A - GOTPLT);
write32(sym.get_tlsdesc_addr(ctx) + A - GOT);
}
continue;
case R_386_TLS_DESC_CALL:
@@ -268,14 +319,13 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
#undef A
#undef P
#undef G
#undef GOTPLT
#undef GOT
}
}
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<ElfRel<E>> rels = get_rels(ctx);
i64 frag_idx = 0;
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
@@ -286,7 +336,7 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
u8 *loc = base + rel.r_offset;
if (!sym.file) {
report_undef(ctx, sym);
report_undef(ctx, file, sym);
continue;
}
@@ -321,10 +371,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
*(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 GOTPLT ctx.gotplt->shdr.sh_addr
#define S (frag ? frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag ? addend : this->get_addend(rel))
#define G (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
#define GOT ctx.got->shdr.sh_addr
switch (rel.r_type) {
case R_386_8:
@@ -334,7 +384,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
write16(S + A);
continue;
case R_386_32:
*(u32 *)loc = S + A;
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
else
*(u32 *)loc = S + A;
continue;
case R_386_PC8:
write8s(S + A);
@@ -346,13 +399,16 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
*(u32 *)loc = S + A;
continue;
case R_386_GOTPC:
*(u32 *)loc = GOTPLT + A;
*(u32 *)loc = GOT + A;
continue;
case R_386_GOTOFF:
*(u32 *)loc = S + A - GOTPLT;
*(u32 *)loc = S + A - GOT;
continue;
case R_386_TLS_LDO_32:
*(u32 *)loc = S + A - ctx.tls_begin;
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
else
*(u32 *)loc = S + A - ctx.tls_begin;
continue;
case R_386_SIZE32:
*(u32 *)loc = sym.esym().st_size + A;
@@ -363,13 +419,13 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
#undef S
#undef A
#undef GOTPLT
#undef GOT
}
}
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr.sh_flags & SHF_ALLOC);
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<ElfRel<E>> rels = get_rels(ctx);
@@ -383,7 +439,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (!sym.file) {
report_undef(ctx, sym);
report_undef(ctx, file, sym);
continue;
}
@@ -399,7 +455,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
// Absolute Local Imported data Imported code
{ NONE, ERROR, ERROR, ERROR }, // DSO
{ NONE, ERROR, ERROR, ERROR }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
@@ -409,7 +465,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
// Absolute Local Imported data Imported code
{ NONE, BASEREL, DYNREL, DYNREL }, // DSO
{ NONE, BASEREL, DYNREL, DYNREL }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
@@ -428,9 +484,9 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
case R_386_PC32: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ BASEREL, NONE, DYNREL, DYNREL }, // DSO
{ BASEREL, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
{ ERROR, NONE, DYNREL, DYNREL }, // DSO
{ ERROR, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
@@ -440,20 +496,46 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
case R_386_GOTPC:
sym.flags |= NEEDS_GOT;
break;
case R_386_PLT32:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
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);
break;
}
case R_386_TLS_GOTIE:
case R_386_TLS_LE:
case R_386_TLS_IE:
sym.flags |= NEEDS_GOTTP;
break;
case R_386_TLS_GD:
sym.flags |= NEEDS_TLSGD;
if (i + 1 == rels.size())
Fatal(ctx) << *this << ": TLS_GD reloc must be followed by PLT or GOT32";
if (u32 ty = rels[i + 1].r_type;
ty != R_386_PLT32 && ty != R_386_GOT32)
Fatal(ctx) << *this << ": TLS_GD reloc must be followed by PLT or GOT32";
if (ctx.arg.relax && !ctx.arg.shared && !sym.is_imported)
i++;
else
sym.flags |= NEEDS_TLSGD;
break;
case R_386_TLS_LDM:
sym.flags |= NEEDS_TLSLD;
if (i + 1 == rels.size())
Fatal(ctx) << *this << ": TLS_LDM reloc must be followed by PLT or GOT32";
if (u32 ty = rels[i + 1].r_type;
ty != R_386_PLT32 && ty != R_386_GOT32)
Fatal(ctx) << *this << ": TLS_LDM reloc must be followed by PLT or GOT32";
if (ctx.arg.relax && !ctx.arg.shared)
i++;
else
ctx.needs_tlsld = true;
break;
case R_386_TLS_GOTDESC:
if (!ctx.arg.relax || ctx.arg.shared)
+813
View File
@@ -0,0 +1,813 @@
#include "mold.h"
#include <tbb/parallel_for.h>
#include <tbb/parallel_for_each.h>
namespace mold::elf {
using E = RISCV64;
static u32 bit(u32 val, i64 pos) {
return (val >> pos) & 1;
}
// Returns [hi:lo] bits of val.
static u32 bits(u32 val, i64 hi, i64 lo) {
return (val >> lo) & ((1LL << (hi - lo + 1)) - 1);
}
static u32 itype(u32 val) {
return val << 20;
}
static u32 stype(u32 val) {
return bits(val, 11, 5) << 25 | bits(val, 4, 0) << 7;
}
static u32 btype(u32 val) {
return bit(val, 12) << 31 | bits(val, 10, 5) << 25 |
bits(val, 4, 1) << 8 | bit(val, 11) << 7;
}
static u32 utype(u32 val) {
// U-type instructions are used in combination with I-type
// instructions. U-type insn sets an immediate to the upper 20-bits
// of a register. I-type insn sign-extends a 12-bits immediate and
// add it to a register value to construct a complete value. 0x800
// is added here to compensate for the sign-extension.
return bits(val + 0x800, 31, 12) << 12;
}
static u32 jtype(u32 val) {
return bit(val, 20) << 31 | bits(val, 10, 1) << 21 |
bit(val, 11) << 20 | bits(val, 19, 12) << 12;
}
static u32 cbtype(u32 val) {
return bit(val, 8) << 12 | bit(val, 4) << 11 | bit(val, 3) << 10 |
bit(val, 7) << 6 | bit(val, 6) << 5 | bit(val, 2) << 4 |
bit(val, 1) << 3 | bit(val, 5) << 2;
}
static u32 cjtype(u32 val) {
return bit(val, 11) << 12 | bit(val, 4) << 11 | bit(val, 9) << 10 |
bit(val, 8) << 9 | bit(val, 10) << 8 | bit(val, 6) << 7 |
bit(val, 7) << 6 | bit(val, 3) << 5 | bit(val, 2) << 4 |
bit(val, 1) << 3 | bit(val, 5) << 2;
}
static void write_itype(u32 *loc, u32 val) {
u32 mask = 0b000000'00000'11111'111'11111'1111111;
*loc = (*loc & mask) | itype(val);
}
static void write_stype(u32 *loc, u32 val) {
u32 mask = 0b000000'11111'11111'111'00000'1111111;
*loc = (*loc & mask) | stype(val);
}
static void write_btype(u32 *loc, u32 val) {
u32 mask = 0b000000'11111'11111'111'00000'1111111;
*loc = (*loc & mask) | btype(val);
}
static void write_utype(u32 *loc, u32 val) {
u32 mask = 0b000000'00000'00000'000'11111'1111111;
*loc = (*loc & mask) | utype(val);
}
static void write_jtype(u32 *loc, u32 val) {
u32 mask = 0b000000'00000'00000'000'11111'1111111;
*loc = (*loc & mask) | jtype(val);
}
static void write_cbtype(u16 *loc, u32 val) {
u32 mask = 0b1110001110000011;
*loc = (*loc & mask) | cbtype(val);
}
static void write_cjtype(u16 *loc, u32 val) {
u32 mask = 0b1110000000000011;
*loc = (*loc & mask) | cjtype(val);
}
static void write_plt_header(Context<E> &ctx) {
u32 *buf = (u32 *)(ctx.buf + ctx.plt->shdr.sh_offset);
static const u32 plt0[] = {
0x00000397, // auipc t2, %pcrel_hi(.got.plt)
0x41c30333, // sub t1, t1, t3 # .plt entry + hdr + 12
0x0003be03, // ld t3, %pcrel_lo(1b)(t2) # _dl_runtime_resolve
0xfd430313, // addi t1, t1, -44 # .plt entry
0x00038293, // addi t0, t2, %pcrel_lo(1b) # &.got.plt
0x00135313, // srli t1, t1, 1 # .plt entry offset
0x0082b283, // ld t0, 8(t0) # link map
0x000e0067, // jr t3
};
u64 gotplt = ctx.gotplt->shdr.sh_addr;
u64 plt = ctx.plt->shdr.sh_addr;
memcpy(buf, plt0, sizeof(plt0));
write_utype(buf, gotplt - plt);
write_itype(buf + 2, gotplt - plt);
write_itype(buf + 4, gotplt - plt);
}
static void write_plt_entry(Context<E> &ctx, Symbol<E> &sym) {
u32 *ent = (u32 *)(ctx.buf + ctx.plt->shdr.sh_offset + ctx.plt_hdr_size +
sym.get_plt_idx(ctx) * ctx.plt_size);
static const u32 data[] = {
0x00000e17, // auipc t3, %pcrel_hi([email protected])
0x000e3e03, // ld t3, %pcrel_lo(1b)(t3)
0x000e0367, // jalr t1, t3
0x00000013, // nop
};
u64 gotplt = sym.get_gotplt_addr(ctx);
u64 plt = sym.get_plt_addr(ctx);
memcpy(ent, data, sizeof(data));
write_utype(ent, gotplt - plt);
write_itype(ent + 1, gotplt - plt);
}
template <>
void PltSection<E>::copy_buf(Context<E> &ctx) {
write_plt_header(ctx);
for (Symbol<E> *sym : symbols)
write_plt_entry(ctx, *sym);
}
template <>
void PltGotSection<E>::copy_buf(Context<E> &ctx) {
u32 *buf = (u32 *)(ctx.buf + this->shdr.sh_offset);
static const u32 data[] = {
0x00000e17, // auipc t3, %pcrel_hi([email protected])
0x000e3e03, // ld t3, %pcrel_lo(1b)(t3)
0x000e0367, // jalr t1, t3
0x00000013, // nop
};
for (Symbol<E> *sym : symbols) {
u32 *ent = buf + sym->get_pltgot_idx(ctx) * 4;
u64 got = sym->get_got_addr(ctx);
u64 plt = sym->get_plt_addr(ctx);
memcpy(ent, data, sizeof(data));
write_utype(ent, got - plt);
write_itype(ent + 1, got - plt);
}
}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_RISCV_ADD32:
*(u32 *)loc += val;
return;
case R_RISCV_SUB8:
*loc -= val;
return;
case R_RISCV_SUB16:
*(u16 *)loc -= val;
return;
case R_RISCV_SUB32:
*(u32 *)loc -= val;
return;
case R_RISCV_SUB6:
*loc = (*loc & 0b1100'0000) | ((*loc - val) & 0b0011'1111);
return;
case R_RISCV_SET6:
*loc = (*loc & 0b1100'0000) | (val & 0b0011'1111);
return;
case R_RISCV_SET8:
*loc = val;
return;
case R_RISCV_SET16:
*(u16 *)loc = val;
return;
case R_RISCV_32_PCREL:
*(u32 *)loc = val - this->shdr.sh_addr - offset;
return;
}
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
ElfRel<E> *dynrel = nullptr;
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<i32> r_deltas = get_r_deltas();
i64 frag_idx = 0;
if (ctx.reldyn)
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
file.reldyn_offset + this->reldyn_offset);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_RISCV_NONE || rel.r_type == R_RISCV_RELAX ||
rel.r_type == R_RISCV_ALIGN)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
i64 r_offset = rel.r_offset + r_deltas[i];
u8 *loc = base + r_offset;
const SectionFragmentRef<E> *frag_ref = nullptr;
if (rel_fragments && rel_fragments[frag_idx].idx == i)
frag_ref = &rel_fragments[frag_idx++];
#define S (frag_ref ? frag_ref->frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag_ref ? frag_ref->addend : rel.r_addend)
#define P (output_section->shdr.sh_addr + offset + r_offset)
#define G (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
#define GOT ctx.got->shdr.sh_addr
switch (rel.r_type) {
case R_RISCV_32:
*(u32 *)loc = S + A;
break;
case R_RISCV_64:
if (sym.is_absolute() || !ctx.arg.pic) {
*(u64 *)loc = S + A;
} else if (sym.is_imported) {
*dynrel++ = {P, R_RISCV_64, (u32)sym.get_dynsym_idx(ctx), A};
*(u64 *)loc = A;
} else {
if (!is_relr_reloc(ctx, rel))
*dynrel++ = {P, R_RISCV_RELATIVE, 0, (i64)(S + A)};
*(u64 *)loc = S + A;
}
break;
case R_RISCV_TLS_DTPMOD32:
case R_RISCV_TLS_DTPMOD64:
case R_RISCV_TLS_DTPREL32:
case R_RISCV_TLS_DTPREL64:
case R_RISCV_TLS_TPREL32:
case R_RISCV_TLS_TPREL64:
Error(ctx) << *this << ": unsupported relocation: " << rel;
break;
case R_RISCV_BRANCH:
write_btype((u32 *)loc, S + A - P);
break;
case R_RISCV_JAL:
write_jtype((u32 *)loc, S + A - P);
break;
case R_RISCV_CALL:
case R_RISCV_CALL_PLT: {
if (r_deltas[i + 1] - r_deltas[i] != 0) {
// auipc + jalr -> jal
assert(r_deltas[i + 1] - r_deltas[i] == -4);
u32 jalr = *(u32 *)&contents[rels[i].r_offset + 4];
*(u32 *)loc = (0b11111'000000 & jalr) | 0b101111;
write_jtype((u32 *)loc, S + A - P);
} else {
u64 val = sym.esym().is_undef_weak() ? 0 : S + A - P;
write_utype((u32 *)loc, val);
write_itype((u32 *)(loc + 4), val);
}
break;
}
case R_RISCV_GOT_HI20:
*(u32 *)loc = G + GOT + A - P;
break;
case R_RISCV_TLS_GOT_HI20:
*(u32 *)loc = sym.get_gottp_addr(ctx) + A - P;
break;
case R_RISCV_TLS_GD_HI20:
*(u32 *)loc = sym.get_tlsgd_addr(ctx) + A - P;
break;
case R_RISCV_PCREL_HI20:
if (sym.esym().is_undef_weak()) {
// Calling an undefined weak symbol does not make sense.
// We make such call into an infinite loop. This should
// help debugging of a faulty program.
*(u32 *)loc = P;
} else {
*(u32 *)loc = S + A - P;
}
break;
case R_RISCV_PCREL_LO12_I:
assert(sym.get_input_section() == this);
assert(sym.value < r_offset);
write_itype((u32 *)loc, *(u32 *)(base + sym.value));
break;
case R_RISCV_LO12_I:
case R_RISCV_TPREL_LO12_I:
write_itype((u32 *)loc, S + A);
break;
case R_RISCV_PCREL_LO12_S:
assert(sym.get_input_section() == this);
assert(sym.value < r_offset);
write_stype((u32 *)loc, *(u32 *)(base + sym.value));
break;
case R_RISCV_LO12_S:
case R_RISCV_TPREL_LO12_S:
write_stype((u32 *)loc, S + A);
break;
case R_RISCV_HI20:
write_utype((u32 *)loc, S + A);
break;
case R_RISCV_TPREL_HI20:
write_utype((u32 *)loc, S + A - ctx.tls_begin);
break;
case R_RISCV_TPREL_ADD:
break;
case R_RISCV_ADD8:
loc += S + A;
break;
case R_RISCV_ADD16:
*(u16 *)loc += S + A;
break;
case R_RISCV_ADD32:
*(u32 *)loc += S + A;
break;
case R_RISCV_ADD64:
*(u64 *)loc += S + A;
break;
case R_RISCV_SUB8:
loc -= S + A;
break;
case R_RISCV_SUB16:
*(u16 *)loc -= S + A;
break;
case R_RISCV_SUB32:
*(u32 *)loc -= S + A;
break;
case R_RISCV_SUB64:
*(u64 *)loc -= S + A;
break;
case R_RISCV_RVC_BRANCH:
write_cbtype((u16 *)loc, S + A - P);
break;
case R_RISCV_RVC_JUMP:
write_cjtype((u16 *)loc, S + A - P);
break;
case R_RISCV_RVC_LUI:
Error(ctx) << *this << ": unsupported relocation: " << rel;
break;
case R_RISCV_SUB6:
*loc = (*loc & 0b1100'0000) | ((*loc - (S + A)) & 0b0011'1111);
break;
case R_RISCV_SET6:
*loc = (*loc & 0b1100'0000) | ((S + A) & 0b0011'1111);
break;
case R_RISCV_SET8:
*loc = S + A;
break;
case R_RISCV_SET16:
*(u16 *)loc = S + A;
break;
case R_RISCV_SET32:
*(u32 *)loc = S + A;
break;
case R_RISCV_32_PCREL:
*(u32 *)loc = S + A - P;
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
#undef S
#undef A
#undef P
#undef G
#undef GOT
}
// In the above loop, PC-relative HI20 relocations overwrote
// instructions with full 32-bit values to allow their corresponding
// PCREL_LO12 relocations to read their values. This loop restore
// the original instructions.
for (i64 i = 0; i < rels.size(); i++) {
switch (rels[i].r_type) {
case R_RISCV_GOT_HI20:
case R_RISCV_PCREL_HI20:
case R_RISCV_TLS_GOT_HI20:
case R_RISCV_TLS_GD_HI20: {
u32 *loc = (u32 *)(base + rels[i].r_offset + r_deltas[i]);
u32 val = *loc;
*loc = *(u32 *)&contents[rels[i].r_offset];
write_utype(loc, val);
}
}
}
}
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_RISCV_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
if (!sym.file) {
report_undef(ctx, file, sym);
continue;
}
SectionFragment<E> *frag;
i64 addend;
std::tie(frag, addend) = get_fragment(ctx, rel);
#define S (frag ? frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag ? addend : rel.r_addend)
switch (rel.r_type) {
case R_RISCV_32:
*(u32 *)loc = S + A;
break;
case R_RISCV_64:
if (std::optional<u64> val = get_tombstone(sym))
*(u64 *)loc = *val;
else
*(u64 *)loc = S + A;
break;
case R_RISCV_ADD8:
*loc += S + A;
break;
case R_RISCV_ADD16:
*(u16 *)loc += S + A;
break;
case R_RISCV_ADD32:
*(u32 *)loc += S + A;
break;
case R_RISCV_ADD64:
*(u64 *)loc += S + A;
break;
case R_RISCV_SUB8:
*loc -= S + A;
break;
case R_RISCV_SUB16:
*(u16 *)loc -= S + A;
break;
case R_RISCV_SUB32:
*(u32 *)loc -= S + A;
break;
case R_RISCV_SUB64:
*(u64 *)loc -= S + A;
break;
case R_RISCV_SUB6:
*loc = (*loc & 0b1100'0000) | ((*loc - (S + A)) & 0b0011'1111);
break;
case R_RISCV_SET6:
*loc = (*loc & 0b1100'0000) | ((S + A) & 0b0011'1111);
break;
case R_RISCV_SET8:
*loc = S + A;
break;
case R_RISCV_SET16:
*(u16 *)loc = S + A;
break;
case R_RISCV_SET32:
*(u32 *)loc = S + A;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
<< rel;
break;
}
#undef S
#undef A
}
}
template <>
void InputSection<E>::copy_contents_riscv(Context<E> &ctx, u8 *buf) {
// A non-alloc section isn't relaxed, so just copy it as one big chunk.
if (!(shdr().sh_flags & SHF_ALLOC)) {
if (is_compressed())
uncompress(ctx, buf);
else
memcpy(buf, contents.data(), contents.size());
return;
}
// Memory-allocated sections may be relaxed, so copy each segment
// individually.
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<i32> r_deltas = get_r_deltas();
i64 pos = 0;
for (i64 i = 0; i < rels.size(); i++) {
i64 delta = r_deltas[i + 1] - r_deltas[i];
if (delta == 0)
continue;
assert(delta < 0);
const ElfRel<E> &r = rels[i];
memcpy(buf, contents.data() + pos, r.r_offset - pos);
buf += r.r_offset - pos;
pos = r.r_offset - delta;
}
memcpy(buf, contents.data() + pos, contents.size() - pos);
}
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_RISCV_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (!sym.file) {
report_undef(ctx, file, sym);
continue;
}
if (sym.get_type() == STT_GNU_IFUNC) {
sym.flags |= NEEDS_GOT;
sym.flags |= NEEDS_PLT;
}
switch (rel.r_type) {
case R_RISCV_32:
case R_RISCV_HI20: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, ERROR, ERROR, ERROR }, // DSO
{ NONE, ERROR, ERROR, ERROR }, // PIE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
case R_RISCV_64: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, BASEREL, DYNREL, DYNREL }, // DSO
{ NONE, BASEREL, DYNREL, DYNREL }, // PIE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
case R_RISCV_TLS_DTPMOD32:
case R_RISCV_TLS_DTPMOD64:
case R_RISCV_TLS_DTPREL32:
case R_RISCV_TLS_DTPREL64:
case R_RISCV_TLS_TPREL32:
case R_RISCV_TLS_TPREL64:
Error(ctx) << *this << ": unsupported relocation: " << rel;
break;
case R_RISCV_BRANCH:
case R_RISCV_JAL:
break;
case R_RISCV_CALL:
case R_RISCV_CALL_PLT:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_RISCV_GOT_HI20:
sym.flags |= NEEDS_GOT;
break;
case R_RISCV_TLS_GOT_HI20:
ctx.has_gottp_rel = true;
sym.flags |= NEEDS_GOTTP;
break;
case R_RISCV_TLS_GD_HI20:
sym.flags |= NEEDS_TLSGD;
break;
case R_RISCV_PCREL_HI20:
case R_RISCV_PCREL_LO12_I:
case R_RISCV_PCREL_LO12_S:
case R_RISCV_LO12_I:
case R_RISCV_LO12_S:
case R_RISCV_TPREL_HI20:
case R_RISCV_TPREL_LO12_I:
case R_RISCV_TPREL_LO12_S:
case R_RISCV_TPREL_ADD:
case R_RISCV_ADD8:
case R_RISCV_ADD16:
case R_RISCV_ADD32:
case R_RISCV_ADD64:
case R_RISCV_SUB8:
case R_RISCV_SUB16:
case R_RISCV_SUB32:
case R_RISCV_SUB64:
case R_RISCV_ALIGN:
break;
case R_RISCV_RVC_BRANCH:
case R_RISCV_RVC_JUMP:
break;
case R_RISCV_RVC_LUI:
Error(ctx) << *this << ": unsupported relocation: " << rel;
break;
case R_RISCV_RELAX:
case R_RISCV_SUB6:
case R_RISCV_SET6:
case R_RISCV_SET8:
case R_RISCV_SET16:
case R_RISCV_SET32:
break;
case R_RISCV_32_PCREL: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ ERROR, NONE, ERROR, ERROR }, // DSO
{ ERROR, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
static bool is_resizable(Context<E> &ctx, InputSection<E> *isec) {
return isec && (isec->shdr().sh_flags & SHF_ALLOC);
}
// Initializes r_deltas and sorted_symbols.
static void initialize_storage(Context<E> &ctx) {
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
file->r_deltas.resize(file->sections.size());
for (std::unique_ptr<InputSection<E>> &isec : file->sections)
if (is_resizable(ctx, isec.get()))
isec->get_r_deltas().resize(isec->get_rels(ctx).size() + 1);
file->sorted_symbols.resize(file->sections.size());
for (Symbol<E> *sym : file->symbols)
if (sym->file == file)
if (InputSection<E> *isec = sym->get_input_section())
file->sorted_symbols[isec->shndx].push_back(sym);
for (std::vector<Symbol<E> *> &vec : file->sorted_symbols)
sort(vec, [](Symbol<E> *a, Symbol<E> *b) { return a->value < b->value; });
});
}
// Returns the distance between a relocated place and a symbol.
static i64 compute_distance(Context<E> &ctx, Symbol<E> &sym,
InputSection<E> &isec, const ElfRel<E> &rel) {
// We handle absolute symbols as if they were infinitely far away
// because `relax_section` may increase a distance between a branch
// instruction and an absolute symbol. Branching to an absolute
// location is extremely rare in real code, though.
if (sym.is_absolute())
return INT32_MAX;
// Likewise, relocations against weak undefined symbols won't be relaxed.
if (sym.esym().is_undef_weak())
return INT32_MAX;
// Compute a distance between the relocated place and the symbol.
i64 S = sym.get_addr(ctx);
i64 A = rel.r_addend;
i64 P = isec.get_addr() + rel.r_offset;
return S + A - P;
}
// Relax R_RISCV_CALL and R_RISCV_CALL_PLT relocations.
static void relax_section(Context<E> &ctx, InputSection<E> &isec) {
std::span<i32> r_deltas = isec.get_r_deltas();
std::span<Symbol<E> *> syms = isec.get_sorted_symbols();
i64 delta = 0;
std::span<ElfRel<E>> rels = isec.get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &r = rels[i];
i64 delta2 = 0;
r_deltas[i] += delta;
switch (r.r_type) {
case R_RISCV_ALIGN: {
// R_RISCV_ALIGN refers NOP instructions. We need to eliminate
// some or all of the instructions so that the instruction that
// immediately follows the NOPs is aligned to a specified
// alignment boundary.
u64 loc = isec.get_addr() + r.r_offset + delta;
// The total bytes of NOPs is stored to r_addend, so the next
// instruction is r_addend away.
u64 next_loc = loc + r.r_addend;
u64 alignment = (std::popcount<u64>(r.r_addend) == 1)
? r.r_addend : next_power_of_two(r.r_addend);
if (next_loc % alignment)
delta2 = align_to(loc, alignment) - next_loc;
break;
}
case R_RISCV_CALL:
case R_RISCV_CALL_PLT:
if (ctx.arg.relax) {
if (i == rels.size() - 1 || rels[i + 1].r_type != R_RISCV_RELAX)
break;
// If the jump target is within ±1 MiB, we can replace AUIPC+JALR
// with JAL, saving 4 bytes.
Symbol<E> &sym = *isec.file.symbols[r.r_sym];
i64 dist = compute_distance(ctx, sym, isec, r);
if (dist % 2 == 0 && -(1 << 20) <= dist && dist < (1 << 20))
delta2 = -4;
}
}
if (delta2 == 0)
continue;
while (!syms.empty() && syms[0]->value <= r.r_offset) {
syms[0]->value += delta;
syms = syms.subspan(1);
}
delta += delta2;
}
for (Symbol<E> *sym : syms)
sym->value += delta;
r_deltas[rels.size()] += delta;
isec.sh_size += delta;
}
// RISC-V instructions are 16 or 32 bits long, so immediates encoded
// in instructions can't be 32 bits long. Therefore, branch and load
// instructions can't refer the 4 GiB address space unlike x86-64.
// In fact, JAL (jump and link) instruction can jump to only within
// ±1 MiB as their immediate is only 21 bits long.
//
// If you want to jump to somewhere further than that, you need to
// construct a full 32-bit offset using multiple instruction and
// branch to that place (e.g. AUIPC and JALR instead of JAL).
// In this comment, we refer instructions such as JAL as the short
// encoding and ones such as AUIPC+JALR as the long encoding.
//
// By default, compiler always uses the long encoding so that branch
// targets are always encodable. This is a safe bet for them but
// may result in inefficient code. Therefore, the RISC-V psABI defines
// a mechanism for the linker to replace long encoding instructions
// with short ones, shrinking the section and increasing the code
// density.
//
// This is contrary to the psABIs for the other RISC processors such as
// ARM64. Typically, they use short instructions by default, and a
// linker creates so-called "thunks" to extend ranges of short jumps.
// On RISC-V, instructions are in the long encoding by default, and
// the linker shrinks them if it can.
//
// When we shrink a section, we need to adjust relocation offsets and
// symbol values. For example, if we replace AUIPC+JALR with JAL
// (which saves 4 bytes), all relocations pointing to anywhere after
// that location need to be shifted by 4. In addition to that, any
// symbol that refers anywhere after that locatioin need to be shifted
// by 4 bytes as well.
//
// For relocations, we use `r_deltas` array to memorize how many bytes
// have be adjusted. For symbols, we directly mutate their `value`
// member.
//
// This operation seems to be optional, as by default instructions are
// using the long encoding, but calling this function is actually
// mandatory because of R_RISCV_ALIGN. R_RISCV_ALIGN relocation is a
// directive to the linker to align the location referred to by the
// relocation to a specified byte boundary. We at least have to
// interpret them satisfy the constraints imposed by R_RISCV_ALIGN
// relocations.
i64 riscv_resize_sections(Context<E> &ctx) {
Timer t(ctx, "riscv_resize_sections");
initialize_storage(ctx);
// Find R_RISCV_CALL AND R_RISCV_CALL_PLT that can be relaxed.
// This step should only shrink sections.
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (std::unique_ptr<InputSection<E>> &isec : file->sections)
if (is_resizable(ctx, isec.get()))
relax_section(ctx, *isec);
});
// Re-compute section offset again to finalize them.
compute_section_sizes(ctx);
return set_osec_offsets(ctx);
}
} // namespace mold::elf
+170 -89
View File
@@ -4,25 +4,6 @@ namespace mold::elf {
using E = X86_64;
template <>
void GotPltSection<E>::copy_buf(Context<E> &ctx) {
u64 *buf = (u64 *)(ctx.buf + this->shdr.sh_offset);
// The first slot of .got.plt points to _DYNAMIC, as requested by
// the x86-64 psABI. The second and the third slots are reserved by
// the psABI.
buf[0] = ctx.dynamic ? ctx.dynamic->shdr.sh_addr : 0;
buf[1] = 0;
buf[2] = 0;
for (Symbol<E> *sym : ctx.plt->symbols) {
if (ctx.arg.z_ibtplt)
buf[sym->get_gotplt_idx(ctx)] = sym->get_plt_addr(ctx) + 10;
else
buf[sym->get_gotplt_idx(ctx)] = sym->get_plt_addr(ctx) + 6;
}
}
// 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.
@@ -43,44 +24,49 @@ static void write_compact_plt(Context<E> &ctx) {
// 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). IBTPLT is slightly larger
// than the regular PLT (24 bytes vs 16 bytes for each entry).
// Intel Control-Flow Enforcement (CET).
//
// 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.
// in total. Our PLT 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;
// Write PLT header
static const u8 plt0[] = {
0xf3, 0x0f, 0x1e, 0xfa, // endbr64
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 + 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;
*(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;
// 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
0x68, 0, 0, 0, 0, // push $index_in_relplt
0xf2, 0xe9, 0, 0, 0, 0, // jmp PLT[0]
0x0f, 0x1f, 0x00, // nop
};
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 + 6) = sym->get_gotplt_addr(ctx) - sym->get_plt_addr(ctx) - 10;
*(u32 *)(ent + 11) = relplt_idx++;
*(u32 *)(ent + 17) = ctx.plt->shdr.sh_addr - sym->get_plt_addr(ctx) - 21;
*(u32 *)(ent + 6) = relplt_idx++;
*(u32 *)(ent + 12) = sym->get_gotplt_addr(ctx) - sym->get_plt_addr(ctx) - 16;
}
}
@@ -145,23 +131,23 @@ void PltGotSection<E>::copy_buf(Context<E> &ctx) {
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
u64 loc, u64 val) {
u8 *base = ctx.buf + this->shdr.sh_offset;
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_X86_64_NONE:
return;
case R_X86_64_32:
*(u32 *)(base + loc) = val;
*(u32 *)loc = val;
return;
case R_X86_64_64:
*(u64 *)(base + loc) = val;
*(u64 *)loc = val;
return;
case R_X86_64_PC32:
*(u32 *)(base + loc) = val - this->shdr.sh_addr - loc;
*(u32 *)loc = val - this->shdr.sh_addr - offset;
return;
case R_X86_64_PC64:
*(u64 *)(base + loc) = val - this->shdr.sh_addr - loc;
*(u64 *)loc = val - this->shdr.sh_addr - offset;
return;
}
unreachable();
@@ -219,6 +205,28 @@ static u32 relax_gottpoff(u8 *loc) {
return 0;
}
static u32 relax_gotpc32_tlsdesc(u8 *loc) {
switch ((loc[0] << 16) | (loc[1] << 8) | loc[2]) {
case 0x488d05: return 0x48c7c0; // lea 0(%rip), %rax -> mov $0, %rax
case 0x488d0d: return 0x48c7c1; // lea 0(%rip), %rcx -> mov $0, %rcx
case 0x488d15: return 0x48c7c2; // lea 0(%rip), %rdx -> mov $0, %rdx
case 0x488d1d: return 0x48c7c3; // lea 0(%rip), %rbx -> mov $0, %rbx
case 0x488d25: return 0x48c7c4; // lea 0(%rip), %rsp -> mov $0, %rsp
case 0x488d2d: return 0x48c7c5; // lea 0(%rip), %rbp -> mov $0, %rbp
case 0x488d35: return 0x48c7c6; // lea 0(%rip), %rsi -> mov $0, %rsi
case 0x488d3d: return 0x48c7c7; // lea 0(%rip), %rdi -> mov $0, %rdi
case 0x4c8d05: return 0x49c7c0; // lea 0(%rip), %r8 -> mov $0, %r8
case 0x4c8d0d: return 0x49c7c1; // lea 0(%rip), %r9 -> mov $0, %r9
case 0x4c8d15: return 0x49c7c2; // lea 0(%rip), %r10 -> mov $0, %r10
case 0x4c8d1d: return 0x49c7c3; // lea 0(%rip), %r11 -> mov $0, %r11
case 0x4c8d25: return 0x49c7c4; // lea 0(%rip), %r12 -> mov $0, %r12
case 0x4c8d2d: return 0x49c7c5; // lea 0(%rip), %r13 -> mov $0, %r13
case 0x4c8d35: return 0x49c7c6; // lea 0(%rip), %r14 -> mov $0, %r14
case 0x4c8d3d: return 0x49c7c7; // lea 0(%rip), %r15 -> mov $0, %r15
}
return 0;
}
// Apply relocations to SHF_ALLOC sections (i.e. sections that are
// mapped to memory at runtime) based on the result of
// scan_relocations().
@@ -291,19 +299,6 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
#define G (sym.get_got_addr(ctx) - ctx.gotplt->shdr.sh_addr)
#define GOT ctx.gotplt->shdr.sh_addr
if (needs_dynrel[i]) {
*dynrel++ = {P, R_X86_64_64, (u32)sym.get_dynsym_idx(ctx), A};
write64(A);
continue;
}
if (needs_baserel[i]) {
if (!is_relr_reloc(ctx, rel))
*dynrel++ = {P, R_X86_64_RELATIVE, 0, (i64)(S + A)};
write64(S + A);
continue;
}
switch (rel.r_type) {
case R_X86_64_8:
write8(S + A);
@@ -318,7 +313,16 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
write32s(S + A);
continue;
case R_X86_64_64:
write64(S + A);
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);
}
continue;
case R_X86_64_PC8:
write8s(S + A - P);
@@ -330,7 +334,12 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
write32s(S + A - P);
continue;
case R_X86_64_PC64:
write64(S + A - P);
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);
}
continue;
case R_X86_64_PLT32:
write32s(S + A - P);
@@ -383,15 +392,35 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
case R_X86_64_TLSGD:
if (sym.get_tlsgd_idx(ctx) == -1) {
// Relax GD to LE
static const u8 insn[] = {
0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0, // mov %fs:0, %rax
0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea 0(%rax), %rax
};
memcpy(loc - 4, insn, sizeof(insn));
i64 val = S - ctx.tls_end + A + 4;
overflow_check(val, -((i64)1 << 31), (i64)1 << 31);
*(u32 *)(loc + 8) = val;
switch (rels[i + 1].r_type) {
case R_X86_64_PLT32:
case R_X86_64_GOTPCREL:
case R_X86_64_GOTPCRELX: {
static const u8 insn[] = {
0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0, // mov %fs:0, %rax
0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea 0(%rax), %rax
};
memcpy(loc - 4, insn, sizeof(insn));
*(u32 *)(loc + 8) = val;
break;
}
case R_X86_64_PLTOFF64: {
static const u8 insn[] = {
0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0, // mov %fs:0, %rax
0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea 0(%rax), %rax
0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00, // nop
};
memcpy(loc - 3, insn, sizeof(insn));
*(u32 *)(loc + 9) = val;
break;
}
default:
unreachable();
}
i++;
} else {
write32s(sym.get_tlsgd_addr(ctx) + A - P);
@@ -400,11 +429,38 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
case R_X86_64_TLSLD:
if (ctx.got->tlsld_idx == -1) {
// Relax LD to LE
static const u8 insn[] = {
0x66, 0x66, 0x66, // (padding)
0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0, // mov %fs:0, %rax
};
memcpy(loc - 3, insn, sizeof(insn));
switch (rels[i + 1].r_type) {
case R_X86_64_PLT32: {
static const u8 insn[] = {
0x66, 0x66, 0x66, // (padding)
0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0, // mov %fs:0, %rax
};
memcpy(loc - 3, insn, sizeof(insn));
break;
}
case R_X86_64_GOTPCREL:
case R_X86_64_GOTPCRELX: {
static const u8 insn[] = {
0x66, 0x66, 0x66, // (padding)
0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0, // mov %fs:0, %rax
0x90, // nop
};
memcpy(loc - 3, insn, sizeof(insn));
break;
}
case R_X86_64_PLTOFF64: {
static const u8 insn[] = {
0x66, 0x66, 0x66, // (padding)
0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0, // mov %fs:0, %rax
0x66, 0x66, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, // nop
};
memcpy(loc - 3, insn, sizeof(insn));
break;
}
default:
unreachable();
}
i++;
} else {
write32s(ctx.got->get_tlsld_addr(ctx) + A - P);
@@ -441,10 +497,10 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
continue;
case R_X86_64_GOTPC32_TLSDESC:
if (sym.get_tlsdesc_idx(ctx) == -1) {
static const u8 insn[] = {
0x48, 0xc7, 0xc0, 0, 0, 0, 0, // mov $0, %rax
};
memcpy(loc - 3, insn, sizeof(insn));
u32 insn = relax_gotpc32_tlsdesc(loc - 3);
loc[-3] = insn >> 16;
loc[-2] = insn >> 8;
loc[-1] = insn;
write32s(S + A - ctx.tls_end + 4);
} else {
write32s(sym.get_tlsdesc_addr(ctx) + A - P);
@@ -500,7 +556,7 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
u8 *loc = base + rel.r_offset;
if (!sym.file) {
report_undef(ctx, sym);
report_undef(ctx, file, sym);
continue;
}
@@ -552,13 +608,22 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
write32s(S + A);
break;
case R_X86_64_64:
*(u64 *)loc = S + A;
if (std::optional<u64> val = get_tombstone(sym))
*(u64 *)loc = *val;
else
*(u64 *)loc = S + A;
break;
case R_X86_64_DTPOFF32:
write32s(S + A - ctx.tls_begin);
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
else
write32s(S + A - ctx.tls_begin);
break;
case R_X86_64_DTPOFF64:
*(u64 *)loc = S + A - ctx.tls_begin;
if (std::optional<u64> val = get_tombstone(sym))
*(u64 *)loc = *val;
else
*(u64 *)loc = S + A - ctx.tls_begin;
break;
case R_X86_64_SIZE32:
write32(sym.esym().st_size + A);
@@ -584,7 +649,7 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
// need to scan relocations.
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr.sh_flags & SHF_ALLOC);
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<ElfRel<E>> rels = get_rels(ctx);
@@ -599,7 +664,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
u8 *loc = (u8 *)(contents.data() + rel.r_offset);
if (!sym.file) {
report_undef(ctx, sym);
report_undef(ctx, file, sym);
continue;
}
@@ -620,7 +685,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
// Absolute Local Imported data Imported code
{ NONE, ERROR, ERROR, ERROR }, // DSO
{ NONE, ERROR, ERROR, ERROR }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
@@ -632,7 +697,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
// Absolute Local Imported data Imported code
{ NONE, BASEREL, DYNREL, DYNREL }, // DSO
{ NONE, BASEREL, DYNREL, DYNREL }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
@@ -652,9 +717,9 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
case R_X86_64_PC64: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ BASEREL, NONE, DYNREL, DYNREL }, // DSO
{ BASEREL, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
{ ERROR, NONE, DYNREL, DYNREL }, // DSO
{ ERROR, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
@@ -688,30 +753,46 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
break;
}
case R_X86_64_PLT32:
case R_X86_64_PLTOFF64:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
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);
break;
case R_X86_64_TLSGD:
}
case R_X86_64_TLSGD: {
if (i + 1 == rels.size())
Fatal(ctx) << *this
<< ": TLSGD reloc must be followed by PLT32 or GOTPCREL";
Fatal(ctx) << *this << ": TLSGD reloc must be followed by PLT or GOTPCREL";
if (u32 ty = rels[i + 1].r_type;
ty != R_X86_64_PLT32 && ty != R_X86_64_PLTOFF64 &&
ty != R_X86_64_GOTPCREL && ty != R_X86_64_GOTPCRELX)
Fatal(ctx) << *this << ": TLSGD reloc must be followed by PLT or GOTPCREL";
if (ctx.arg.relax && !ctx.arg.shared && !sym.is_imported)
i++;
else
sym.flags |= NEEDS_TLSGD;
break;
case R_X86_64_TLSLD:
}
case R_X86_64_TLSLD: {
if (i + 1 == rels.size())
Fatal(ctx) << *this
<< ": TLSLD reloc must be followed by PLT32 or GOTPCREL";
Fatal(ctx) << *this << ": TLSLD reloc must be followed by PLT or GOTPCREL";
if (u32 ty = rels[i + 1].r_type;
ty != R_X86_64_PLT32 && ty != R_X86_64_PLTOFF64 &&
ty != R_X86_64_GOTPCREL && ty != R_X86_64_GOTPCRELX)
Fatal(ctx) << *this << ": TLSLD reloc must be followed by PLT or GOTPCREL";
if (ctx.arg.relax && !ctx.arg.shared)
i++;
else
sym.flags |= NEEDS_TLSLD;
ctx.needs_tlsld = true;
break;
}
case R_X86_64_GOTTPOFF: {
ctx.has_gottp_rel = true;
@@ -722,7 +803,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
break;
}
case R_X86_64_GOTPC32_TLSDESC: {
if (memcmp(loc - 3, "\x48\x8d\x05", 3))
if (relax_gotpc32_tlsdesc(loc - 3) == 0)
Fatal(ctx) << *this << ": GOTPC32_TLSDESC relocation is used"
<< " against an invalid code sequence";
+211 -70
View File
@@ -38,16 +38,20 @@ Options:
-l LIBNAME Search for a given library
-m TARGET Set target
-o FILE, --output FILE Set output filename
-q, --emit-relocs Leaves relocation sections in the output
-r, --relocatable Generate relocatable output
-s, --strip-all Strip .symtab section
-u SYMBOL, --undefined SYMBOL
Force to resolve SYMBOL
--Bdynamic Link against shared libraries (default)
--Bstatic Do not link against shared libraries
--Bdynamic, --dy Link against shared libraries (default)
--Bstatic, --dn, --static Do not link against shared libraries
--Bsymbolic Bind global symbols locally
--Bsymbolic-functions Bind global functions locally
--Bno-symbolic Cancel --Bsymbolic and --Bsymbolic-functions
--Map FILE Write map file to a given file
--Tbss=ADDR Set address to .bss
--Tdata Set address to .data
--Ttext Set address to .text
--allow-multiple-definition Allow multiple definitions
--as-needed Only set DT_NEEDED if used
--no-as-needed
@@ -61,10 +65,12 @@ Options:
--compress-debug-sections [none,zlib,zlib-gabi,zlib-gnu]
Compress .debug_* sections
--dc Ignored
--dependency-file=FILE Write Makefile-style dependency rules to FILE
--defsym=SYMBOL=VALUE Define a symbol alias
--demangle Demangle C++ symbols in log messages (default)
--no-demangle
--disable-new-dtags Ignored
--enable-new-dtags Emit DT_RUNPATH for --rpath (default)
--disable-new-dtags Emit DT_RPATH for --rpath
--dp Ignored
--dynamic-list Read a list of dynamic symbols
--eh-frame-hdr Create .eh_frame_hdr section
@@ -78,21 +84,21 @@ Options:
--no-fork
--gc-sections Remove unreferenced sections
--no-gc-sections
--gdb-index Ignored
--gdb-index Create .gdb_index for faster gdb startup
--hash-style [sysv,gnu,both]
Set hash style
--icf Fold identical code
--icf=[all,none] Fold identical code
--no-icf
--image-base ADDR Set the base address to a given value
--init SYMBOL Call SYMBOl at load-time
--no-undefined Report undefined symbols (even with --shared)
--noinhibit-exec Create an output file even if errors occur
--oformat=binary Omit ELF, section and program headers
--pack-dyn-relocs=[relr,none]
Pack dynamic relocations
--perf Print performance statistics
--pie, --pic-executable Create a position independent executable
--no-pie, --no-pic-executable
--plugin Ignored
--plugin-opt Ignored
--pop-state Pop state of flags governing input file handling
--preload
--no-preload
@@ -108,16 +114,18 @@ Options:
--repro Embed input files to .repro section
--require-defined SYMBOL Require SYMBOL be defined in the final output
--retain-symbols-file FILE Keep only symbols listed in FILE
--reverse-sections Reverses input sections in the output file
--rpath DIR Add DIR to runtime search path
--rpath-link DIR Ignored
--run COMMAND ARG... Run COMMAND with mold as /usr/bin/ld
--section-start=SECTION=ADDR Set address to section
--shared, --Bshareable Create a share library
--shuffle-sections[=SEED] Randomize the output by shuffling input sections
--sort-common Ignored
--sort-section Ignored
--spare-dynamic-tags NUMBER Reserve give number of tags in .dynamic section
--start-lib Give following object files in-archive-file semantics
--end-lib End the effect of --start-lib
--static Do not link against shared libraries
--stats Print input statistics
--sysroot DIR Set target system root directory
--thread-count COUNT, --threads=COUNT
@@ -131,6 +139,9 @@ Options:
--version-script FILE Read version script
--warn-common Warn about common symbols
--no-warn-common
--warn-once Only warn once for each undefined symbol
--warn-shared-textrel Warn if the output .so needs text relocations
--warn-textrel Warn if the output file needs text relocations
--warn-unresolved-symbols Report unresolved symbols as warnings
--error-unresolved-symbols
Report unresolved symbols as errors (default)
@@ -217,6 +228,19 @@ bool read_arg(Context<E> &ctx, std::span<std::string_view> &args,
return false;
}
template <typename E>
bool read_eq(Context<E> &ctx, std::span<std::string_view> &args,
std::string_view &arg, std::string name) {
for (std::string opt : add_dashes(name)) {
if (args[0].starts_with(opt + "=")) {
arg = args[0].substr(opt.size() + 1);
args = args.subspan(1);
return true;
}
}
return false;
}
bool read_flag(std::span<std::string_view> &args, std::string name) {
for (std::string opt : add_dashes(name)) {
if (args[0] == opt) {
@@ -259,51 +283,28 @@ bool read_z_arg(Context<E> &ctx, std::span<std::string_view> &args,
return false;
}
template <typename E>
std::string create_response_file(Context<E> &ctx) {
std::string buf;
std::stringstream out;
std::string cwd = std::filesystem::current_path();
out << "-C " << cwd.substr(1) << "\n";
if (cwd != "/") {
out << "--chroot ..";
i64 depth = std::count(cwd.begin(), cwd.end(), '/');
for (i64 i = 1; i < depth; i++)
out << "/..";
out << "\n";
}
for (i64 i = 1; i < ctx.cmdline_args.size(); i++) {
std::string_view arg = ctx.cmdline_args[i];
if (arg == "-repro" || arg == "--repro") {
i++;
continue;
}
out << arg << "\n";
}
return out.str();
}
template <typename E>
static i64 parse_hex(Context<E> &ctx, std::string opt, std::string_view value) {
if (!value.starts_with("0x") && !value.starts_with("0X"))
Fatal(ctx) << "option -" << opt << ": not a hexadecimal number";
value = value.substr(2);
if (value.starts_with("0x") || value.starts_with("0X"))
value = value.substr(2);
if (value.find_first_not_of("0123456789abcdefABCDEF") != value.npos)
Fatal(ctx) << "option -" << opt << ": not a hexadecimal number";
return std::stol(std::string(value), nullptr, 16);
return std::stoul(std::string(value), nullptr, 16);
}
template <typename E>
static i64 parse_number(Context<E> &ctx, std::string opt,
std::string_view value) {
size_t nread;
i64 ret = std::stol(std::string(value), &nread, 0);
if (value.starts_with('-')) {
i64 ret = std::stoul(std::string(value.substr(1)), &nread, 0);
if (value.size() - 1 != nread)
Fatal(ctx) << "option -" << opt << ": not a number: " << value;
return -ret;
}
i64 ret = std::stoul(std::string(value), &nread, 0);
if (value.size() != nread)
Fatal(ctx) << "option -" << opt << ": not a number: " << value;
return ret;
@@ -396,21 +397,41 @@ static bool is_file(std::string_view path) {
(st.st_mode & S_IFMT) != S_IFDIR;
}
template <typename E>
static std::variant<Symbol<E> *, u64>
parse_defsym_value(Context<E> &ctx, std::string_view s) {
if (s.starts_with("0x") || s.starts_with("0X")) {
size_t nread;
u64 addr = std::stoull(std::string(s), &nread, 16);
if (s.size() != nread)
return {};
return addr;
}
if (s.find_first_not_of("0123456789") == s.npos)
return (u64)std::stoull(std::string(s), nullptr, 10);
return get_symbol(ctx, s);
}
// Returns a PLT header size and a PLT entry size.
template <typename E>
static std::pair<i64, i64> get_plt_size(Context<E> &ctx) {
switch (E::e_machine) {
case EM_X86_64:
if constexpr (std::is_same_v<E, X86_64>) {
if (ctx.arg.z_now)
return {0, 8};
if (ctx.arg.z_ibtplt)
return {16, 24};
return {32, 16};
return {16, 16};
case EM_386:
return {16, 16};
case EM_AARCH64:
return {32, 16};
}
if constexpr (std::is_same_v<E, I386>)
return {16, 16};
if constexpr (std::is_same_v<E, ARM64>)
return {32, 16};
if constexpr (std::is_same_v<E, ARM32>)
return {20, 16};
if constexpr (std::is_same_v<E, RISCV64>)
return {32, 16};
unreachable();
}
@@ -424,6 +445,12 @@ void parse_nonpositional_args(Context<E> &ctx,
ctx.page_size = E::page_size;
bool version_shown = false;
bool warn_shared_textrel = false;
// RISC-V object files contains lots of local symbols, so by default
// we discard them. This is compatible with GNU ld.
if constexpr (std::is_same_v<E, RISCV64>)
ctx.arg.discard_locals = true;
while (!args.empty()) {
std::string_view arg;
@@ -458,6 +485,10 @@ void parse_nonpositional_args(Context<E> &ctx,
ctx.arg.emulation = EM_386;
} else if (arg == "aarch64linux") {
ctx.arg.emulation = EM_AARCH64;
} else if (arg == "armelf_linux_eabi") {
ctx.arg.emulation = EM_ARM;
} else if (arg == "elf64lriscv") {
ctx.arg.emulation = EM_RISCV;
} else {
Fatal(ctx) << "unknown -m argument: " << arg;
}
@@ -476,18 +507,24 @@ void parse_nonpositional_args(Context<E> &ctx,
ctx.arg.Bsymbolic_functions = false;
} else if (read_arg(ctx, args, arg, "exclude-libs")) {
append(ctx.arg.exclude_libs, split_by_comma_or_colon(arg));
} else if (read_flag(args, "q") || read_flag(args, "emit-relocs")) {
ctx.arg.emit_relocs = true;
} else if (read_arg(ctx, args, arg, "e") ||
read_arg(ctx, args, arg, "entry")) {
ctx.arg.entry = arg;
} else if (read_arg(ctx, args, arg, "Map")) {
ctx.arg.Map = arg;
ctx.arg.print_map = true;
} else if (read_flag(args, "print-dependencies")) {
ctx.arg.print_dependencies = 1;
} else if (read_flag(args, "print-dependencies=full")) {
ctx.arg.print_dependencies = 2;
} else if (read_flag(args, "print-map") || read_flag(args, "M")) {
ctx.arg.print_map = true;
} else if (read_flag(args, "static") || read_flag(args, "Bstatic")) {
} else if (read_flag(args, "Bstatic") || read_flag(args, "dn") || read_flag(args, "static")) {
ctx.arg.is_static = true;
remaining.push_back("-Bstatic");
} else if (read_flag(args, "Bdynamic")) {
} else if (read_flag(args, "Bdynamic") || read_flag(args, "dy")) {
ctx.arg.is_static = false;
remaining.push_back("-Bdynamic");
} else if (read_flag(args, "shared") || read_flag(args, "Bshareable")) {
@@ -496,17 +533,33 @@ void parse_nonpositional_args(Context<E> &ctx,
ctx.arg.spare_dynamic_tags = parse_number(ctx, "spare-dynamic-tags", arg);
} else if (read_flag(args, "start-lib")) {
remaining.push_back("-start-lib");
} else if (read_arg(ctx, args, arg, "dependency-file")) {
ctx.arg.dependency_file = arg;
} else if (read_arg(ctx, args, arg, "defsym")) {
size_t pos = arg.find('=');
if (pos == arg.npos || pos == arg.size() - 1)
Fatal(ctx) << "-defsym: syntax error: " << arg;
ctx.arg.defsyms.push_back({arg.substr(0, pos), arg.substr(pos + 1)});
ctx.arg.defsyms.emplace_back(get_symbol(ctx, arg.substr(0, pos)),
parse_defsym_value(ctx, arg.substr(pos + 1)));
} else if (read_flag(args, ":lto-pass2")) {
ctx.arg.lto_pass2 = true;
} else if (read_arg(ctx, args, arg, ":ignore-ir-file")) {
ctx.arg.ignore_ir_file.insert(arg);
} else if (read_flag(args, "demangle")) {
ctx.arg.demangle = true;
} else if (read_flag(args, "no-demangle")) {
ctx.arg.demangle = false;
} else if (read_flag(args, "default-symver")) {
ctx.arg.default_symver = true;
} else if (read_flag(args, "noinhibit-exec")) {
ctx.arg.noinhibit_exec = true;
} else if (read_flag(args, "shuffle-sections")) {
ctx.arg.shuffle_sections = SHUFFLE_SECTIONS_SHUFFLE;
} else if (read_eq(ctx, args, arg, "shuffle-sections")) {
ctx.arg.shuffle_sections = SHUFFLE_SECTIONS_SHUFFLE;
ctx.arg.shuffle_sections_seed = parse_number(ctx, "shuffle-sections", arg);
} else if (read_flag(args, "reverse-sections")) {
ctx.arg.shuffle_sections = SHUFFLE_SECTIONS_REVERSE;
} else if (read_arg(ctx, args, arg, "y") ||
read_arg(ctx, args, arg, "trace-symbol")) {
ctx.arg.trace_symbol.push_back(arg);
@@ -573,6 +626,10 @@ void parse_nonpositional_args(Context<E> &ctx,
ctx.arg.relax = true;
} else if (read_flag(args, "no-relax")) {
ctx.arg.relax = false;
} else if (read_flag(args, "gdb-index")) {
ctx.arg.gdb_index = true;
} else if (read_flag(args, "no-gdb-index")) {
ctx.arg.gdb_index = false;
} else if (read_flag(args, "r") || read_flag(args, "relocatable")) {
ctx.arg.relocatable = true;
} else if (read_flag(args, "perf")) {
@@ -607,6 +664,16 @@ void parse_nonpositional_args(Context<E> &ctx,
ctx.arg.warn_common = true;
} else if (read_flag(args, "no-warn-common")) {
ctx.arg.warn_common = false;
} else if (read_flag(args, "warn-once")) {
ctx.arg.warn_once = true;
} else if (read_flag(args, "warn-shared-textrel")) {
warn_shared_textrel = true;
} else if (read_flag(args, "warn-textrel")) {
ctx.arg.warn_textrel = true;
} else if (read_flag(args, "enable-new-dtags")) {
ctx.arg.enable_new_dtags = true;
} else if (read_flag(args, "disable-new-dtags")) {
ctx.arg.enable_new_dtags = false;
} else if (read_arg(ctx, args, arg, "compress-debug-sections")) {
if (arg == "zlib" || arg == "zlib-gabi")
ctx.arg.compress_debug_sections = COMPRESS_GABI;
@@ -623,8 +690,24 @@ void parse_nonpositional_args(Context<E> &ctx,
ctx.arg.is_static = true;
} else if (read_flag(args, "no-omagic")) {
ctx.arg.omagic = false;
} else if (read_arg(ctx, args, arg, "oformat")) {
if (arg != "binary")
Fatal(ctx) << "-oformat: " << arg << " is not supported";
ctx.arg.oformat_binary = true;
} else if (read_arg(ctx, args, arg, "retain-symbols-file")) {
read_retain_symbols_file(ctx, arg);
} else if (read_arg(ctx, args, arg, "section-start")) {
size_t pos = arg.find('=');
if (pos == arg.npos || pos == arg.size() - 1)
Fatal(ctx) << "-section-start: syntax error: " << arg;
ctx.arg.section_start[arg.substr(0, pos)] =
parse_hex(ctx, "section-start", arg.substr(pos + 1));
} else if (read_arg(ctx, args, arg, "Tbss")) {
ctx.arg.section_start[".bss"] = parse_hex(ctx, "Tbss", arg);
} else if (read_arg(ctx, args, arg, "Tdata")) {
ctx.arg.section_start[".data"] = parse_hex(ctx, "Tdata", arg);
} else if (read_arg(ctx, args, arg, "Ttext")) {
ctx.arg.section_start[".text"] = parse_hex(ctx, "Ttext", arg);
} else if (read_flag(args, "repro")) {
ctx.arg.repro = true;
} else if (read_z_flag(args, "now")) {
@@ -641,7 +724,7 @@ void parse_nonpositional_args(Context<E> &ctx,
ctx.arg.z_execstack = true;
} else if (read_z_arg(ctx, args, arg, "max-page-size")) {
ctx.page_size = parse_number(ctx, "-z max-page-size", arg);
if (__builtin_popcountll(ctx.page_size) != 1)
if (std::popcount<u64>(ctx.page_size) != 1)
Fatal(ctx) << "-z max-page-size " << arg << ": value must be a power of 2";
} else if (read_z_flag(args, "noexecstack")) {
ctx.arg.z_execstack = false;
@@ -729,18 +812,72 @@ void parse_nonpositional_args(Context<E> &ctx,
ctx.arg.quick_exit = true;
} else if (read_flag(args, "no-quick-exit")) {
ctx.arg.quick_exit = false;
} else if (read_arg(ctx, args, arg, "plugin")) {
ctx.arg.plugin = arg;
} else if (read_arg(ctx, args, arg, "plugin-opt")) {
ctx.arg.plugin_opt.push_back(std::string(arg));
} else if (read_flag(args, "lto-cs-profile-generate")) {
ctx.arg.plugin_opt.push_back("cs-profile-generate");
} else if (read_arg(ctx, args, arg, "lto-cs-profile-file")) {
ctx.arg.plugin_opt.push_back("cs-profile-path=" + std::string(arg));
} else if (read_flag(args, "lto-debug-pass-manager")) {
ctx.arg.plugin_opt.push_back("debug-pass-manager");
} else if (read_flag(args, "disable-verify")) {
ctx.arg.plugin_opt.push_back("disable-verify");
} else if (read_flag(args, "lto-emit-asm")) {
ctx.arg.plugin_opt.push_back("emit-asm");
} else if (read_arg(ctx, args, arg, "thinlto-jobs")) {
ctx.arg.plugin_opt.push_back("jobs=" + std::string(arg));
} else if (read_flag(args, "no-legacy-pass-manager")) {
ctx.arg.plugin_opt.push_back("legacy-pass-manager");
} else if (read_arg(ctx, args, arg, "lto-partitions")) {
ctx.arg.plugin_opt.push_back("lto-partitions=" + std::string(arg));
} else if (read_flag(args, "no-lto-legacy-pass-manager")) {
ctx.arg.plugin_opt.push_back("new-pass-manager");
} else if (read_arg(ctx, args, arg, "lto-obj-path")) {
ctx.arg.plugin_opt.push_back("obj-path=" + std::string(arg));
} else if (read_arg(ctx, args, arg, "opt-remarks-filename")) {
ctx.arg.plugin_opt.push_back("opt-remarks-filename=" + std::string(arg));
} else if (read_arg(ctx, args, arg, "opt-remarks-format")) {
ctx.arg.plugin_opt.push_back("opt-remarks-format=" + std::string(arg));
} else if (read_arg(ctx, args, arg, "opt-remarks-hotness-threshold")) {
ctx.arg.plugin_opt.push_back("opt-remarks-hotness-threshold=" + std::string(arg));
} else if (read_arg(ctx, args, arg, "opt-remarks-passes")) {
ctx.arg.plugin_opt.push_back("opt-remarks-passes=" + std::string(arg));
} else if (read_flag(args, "opt-remarks-with_hotness")) {
ctx.arg.plugin_opt.push_back("opt-remarks-with-hotness");
} else if (args[0].starts_with("-lto-O")) {
ctx.arg.plugin_opt.push_back("O" + std::string(args[0].substr(6)));
args = args.subspan(1);
} else if (args[0].starts_with("--lto-O")) {
ctx.arg.plugin_opt.push_back("O" + std::string(args[0].substr(7)));
args = args.subspan(1);
} else if (read_arg(ctx, args, arg, "lto-pseudo-probe-for-profiling")) {
ctx.arg.plugin_opt.push_back("pseudo-probe-for-profiling=" + std::string(arg));
} else if (read_arg(ctx, args, arg, "lto-sample-profile")) {
ctx.arg.plugin_opt.push_back("sample-profile=" + std::string(arg));
} else if (read_flag(args, "save-temps")) {
ctx.arg.plugin_opt.push_back("save-temps");
} else if (read_flag(args, "thinlto-emit-imports-files")) {
ctx.arg.plugin_opt.push_back("thinlto-emit-imports-files");
} else if (read_arg(ctx, args, arg, "thinlto-index-only")) {
ctx.arg.plugin_opt.push_back("thinlto-index-only=" + std::string(arg));
} else if (read_flag(args, "thinlto-index-only")) {
ctx.arg.plugin_opt.push_back("thinlto-index-only");
} else if (read_arg(ctx, args, arg, "thinlto-object-suffix-replace")) {
ctx.arg.plugin_opt.push_back("thinlto-object-suffix-replace=" + std::string(arg));
} else if (read_arg(ctx, args, arg, "thinlto-prefix-replace")) {
ctx.arg.plugin_opt.push_back("thinlto-prefix-replace=" + std::string(arg));
} else if (read_arg(ctx, args, arg, "thinlto-jobs")) {
ctx.arg.plugin_opt.push_back("jobs=" + std::string(arg));
} else if (read_arg(ctx, args, arg, "thread-count")) {
ctx.arg.thread_count = parse_number(ctx, "thread-count", arg);
} else if (read_flag(args, "threads")) {
ctx.arg.thread_count = 0;
} else if (read_flag(args, "no-threads")) {
ctx.arg.thread_count = 1;
} else if (args[0].starts_with("-threads=")) {
ctx.arg.thread_count = parse_number(ctx, "threads=", args[0].substr(9));
args = args.subspan(1);
} else if (args[0].starts_with("--threads=")) {
ctx.arg.thread_count = parse_number(ctx, "threads=", args[0].substr(10));
args = args.subspan(1);
} else if (read_eq(ctx, args, arg, "threads")) {
ctx.arg.thread_count = parse_number(ctx, "threads", arg);
} else if (read_flag(args, "discard-all") || read_flag(args, "x")) {
ctx.arg.discard_all = true;
} else if (read_flag(args, "discard-locals") || read_flag(args, "X")) {
@@ -795,7 +932,7 @@ void parse_nonpositional_args(Context<E> &ctx,
if (arg == "binary")
Fatal(ctx)
<< "mold does not suppor `-b binary`. If you want to convert a binary"
" file into an object file, use `objcopy -I binary -O elf64-x86-64"
<< " file into an object file, use `objcopy -I binary -O default"
<< " <input-file> <output-file.o>` instead.";
Fatal(ctx) << "unknown command line option: -b " << arg;
} else if (read_arg(ctx, args, arg, "auxiliary") ||
@@ -805,6 +942,8 @@ void parse_nonpositional_args(Context<E> &ctx,
read_arg(ctx, args, arg, "F")) {
ctx.arg.filter.push_back(arg);
} else if (read_flag(args, "preload")) {
Warn(ctx) << "--preload is deprecated and will be removed in a"
<< " future version";
ctx.arg.preload = true;
} else if (read_flag(args, "no-preload")) {
ctx.arg.preload = false;
@@ -814,10 +953,7 @@ void parse_nonpositional_args(Context<E> &ctx,
} else if (read_flag(args, "O1")) {
} else if (read_flag(args, "O2")) {
} else if (read_flag(args, "verbose")) {
} else if (read_arg(ctx, args, arg, "plugin")) {
} else if (read_arg(ctx, args, arg, "plugin-opt")) {
} else if (read_flag(args, "color-diagnostics")) {
} else if (read_flag(args, "gdb-index")) {
} else if (read_flag(args, "eh-frame-hdr")) {
} else if (read_flag(args, "start-group")) {
} else if (read_flag(args, "end-group")) {
@@ -896,10 +1032,12 @@ void parse_nonpositional_args(Context<E> &ctx,
}
}
if (ctx.arg.shared) {
// Remove redundant `/..` or `/.` from library paths.
for (std::string &path : ctx.arg.library_paths)
path = path_clean(path);
if (ctx.arg.shared)
ctx.arg.pic = true;
ctx.arg.dynamic_linker = "";
}
if (ctx.arg.pic)
ctx.arg.image_base = 0;
@@ -920,7 +1058,7 @@ void parse_nonpositional_args(Context<E> &ctx,
}
if (ctx.arg.image_base % ctx.page_size)
Fatal(ctx) << "-image-base msut be a multiple of -max-page-size";
Fatal(ctx) << "-image-base must be a multiple of -max-page-size";
if (char *env = getenv("MOLD_REPRO"); env && env[0])
ctx.arg.repro = true;
@@ -940,6 +1078,9 @@ void parse_nonpositional_args(Context<E> &ctx,
ctx.default_version = VER_NDX_LAST_RESERVED + 1;
}
if (ctx.arg.shared && warn_shared_textrel)
ctx.arg.warn_textrel = true;
std::tie(ctx.plt_hdr_size, ctx.plt_size) = get_plt_size(ctx);
ctx.arg.undefined.push_back(ctx.arg.entry);
@@ -960,8 +1101,6 @@ void parse_nonpositional_args(Context<E> &ctx,
std::string_view &arg, \
std::string name); \
\
template std::string create_response_file(Context<E> &ctx); \
\
template \
void parse_nonpositional_args(Context<E> &ctx, \
std::vector<std::string_view> &remaining)
@@ -969,5 +1108,7 @@ void parse_nonpositional_args(Context<E> &ctx,
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+511 -4
View File
@@ -4,10 +4,6 @@
#include <ostream>
#include <string>
#ifdef __BIG_ENDIAN__
#error "mold does not support big-endian hosts"
#endif
namespace mold::elf {
typedef uint8_t u8;
@@ -23,6 +19,8 @@ typedef int64_t i64;
struct X86_64;
struct I386;
struct ARM64;
struct ARM32;
struct RISCV64;
template <typename E> struct ElfSym;
template <typename E> struct ElfShdr;
@@ -69,6 +67,8 @@ static constexpr u32 SHT_GNU_VERDEF = 0x6ffffffd;
static constexpr u32 SHT_GNU_VERNEED = 0x6ffffffe;
static constexpr u32 SHT_GNU_VERSYM = 0x6fffffff;
static constexpr u32 SHT_X86_64_UNWIND = 0x70000001;
static constexpr u32 SHT_ARM_EXIDX = 0x70000001;
static constexpr u32 SHT_ARM_ATTRIBUTES = 0x70000003;
static constexpr u32 SHF_WRITE = 0x1;
static constexpr u32 SHF_ALLOC = 0x2;
@@ -124,6 +124,7 @@ static constexpr u32 PT_TLS = 7;
static constexpr u32 PT_GNU_EH_FRAME = 0x6474e550;
static constexpr u32 PT_GNU_STACK = 0x6474e551;
static constexpr u32 PT_GNU_RELRO = 0x6474e552;
static constexpr u32 PT_ARM_EXIDX = 0x70000001;
static constexpr u32 PF_X = 1;
static constexpr u32 PF_W = 2;
@@ -143,8 +144,10 @@ static constexpr u32 ELFCLASS64 = 2;
static constexpr u32 EV_CURRENT = 1;
static constexpr u32 EM_386 = 3;
static constexpr u32 EM_ARM = 40;
static constexpr u32 EM_X86_64 = 62;
static constexpr u32 EM_AARCH64 = 183;
static constexpr u32 EM_RISCV = 243;
static constexpr u32 EI_CLASS = 4;
static constexpr u32 EI_DATA = 5;
@@ -231,6 +234,21 @@ static constexpr u32 GNU_PROPERTY_X86_FEATURE_1_SHSTK = 2;
static constexpr u32 ELFCOMPRESS_ZLIB = 1;
static constexpr u32 EF_ARM_ABI_FLOAT_SOFT = 0x00000200;
static constexpr u32 EF_ARM_ABI_FLOAT_HARD = 0x00000400;
static constexpr u32 EF_ARM_EABI_VER5 = 0x05000000;
static constexpr u32 EF_RISCV_RVC = 1;
static constexpr u32 EF_RISCV_FLOAT_ABI = 6;
static constexpr u32 EF_RISCV_FLOAT_ABI_SOFT = 0;
static constexpr u32 EF_RISCV_FLOAT_ABI_SINGLE = 2;
static constexpr u32 EF_RISCV_FLOAT_ABI_DOUBLE = 4;
static constexpr u32 EF_RISCV_FLOAT_ABI_QUAD = 6;
static constexpr u32 EF_RISCV_RVE = 8;
static constexpr u32 EF_RISCV_TSO = 16;
static constexpr u32 STO_RISCV_VARIANT_CC = 0x80;
static constexpr u32 R_X86_64_NONE = 0;
static constexpr u32 R_X86_64_64 = 1;
static constexpr u32 R_X86_64_PC32 = 2;
@@ -627,6 +645,394 @@ inline std::string rel_to_string<ARM64>(u32 r_type) {
return "unknown (" + std::to_string(r_type) + ")";
}
static constexpr u32 R_ARM_NONE = 0x0;
static constexpr u32 R_ARM_PC24 = 0x1;
static constexpr u32 R_ARM_ABS32 = 0x2;
static constexpr u32 R_ARM_REL32 = 0x3;
static constexpr u32 R_ARM_LDR_PC_G0 = 0x4;
static constexpr u32 R_ARM_ABS16 = 0x5;
static constexpr u32 R_ARM_ABS12 = 0x6;
static constexpr u32 R_ARM_THM_ABS5 = 0x7;
static constexpr u32 R_ARM_ABS8 = 0x8;
static constexpr u32 R_ARM_SBREL32 = 0x9;
static constexpr u32 R_ARM_THM_CALL = 0xa;
static constexpr u32 R_ARM_THM_PC8 = 0xb;
static constexpr u32 R_ARM_BREL_ADJ = 0xc;
static constexpr u32 R_ARM_TLS_DESC = 0xd;
static constexpr u32 R_ARM_THM_SWI8 = 0xe;
static constexpr u32 R_ARM_XPC25 = 0xf;
static constexpr u32 R_ARM_THM_XPC22 = 0x10;
static constexpr u32 R_ARM_TLS_DTPMOD32 = 0x11;
static constexpr u32 R_ARM_TLS_DTPOFF32 = 0x12;
static constexpr u32 R_ARM_TLS_TPOFF32 = 0x13;
static constexpr u32 R_ARM_COPY = 0x14;
static constexpr u32 R_ARM_GLOB_DAT = 0x15;
static constexpr u32 R_ARM_JUMP_SLOT = 0x16;
static constexpr u32 R_ARM_RELATIVE = 0x17;
static constexpr u32 R_ARM_GOTOFF32 = 0x18;
static constexpr u32 R_ARM_BASE_PREL = 0x19;
static constexpr u32 R_ARM_GOT_BREL = 0x1a;
static constexpr u32 R_ARM_PLT32 = 0x1b;
static constexpr u32 R_ARM_CALL = 0x1c;
static constexpr u32 R_ARM_JUMP24 = 0x1d;
static constexpr u32 R_ARM_THM_JUMP24 = 0x1e;
static constexpr u32 R_ARM_BASE_ABS = 0x1f;
static constexpr u32 R_ARM_ALU_PCREL_7_0 = 0x20;
static constexpr u32 R_ARM_ALU_PCREL_15_8 = 0x21;
static constexpr u32 R_ARM_ALU_PCREL_23_15 = 0x22;
static constexpr u32 R_ARM_LDR_SBREL_11_0_NC = 0x23;
static constexpr u32 R_ARM_ALU_SBREL_19_12_NC = 0x24;
static constexpr u32 R_ARM_ALU_SBREL_27_20_CK = 0x25;
static constexpr u32 R_ARM_TARGET1 = 0x26;
static constexpr u32 R_ARM_SBREL31 = 0x27;
static constexpr u32 R_ARM_V4BX = 0x28;
static constexpr u32 R_ARM_TARGET2 = 0x29;
static constexpr u32 R_ARM_PREL31 = 0x2a;
static constexpr u32 R_ARM_MOVW_ABS_NC = 0x2b;
static constexpr u32 R_ARM_MOVT_ABS = 0x2c;
static constexpr u32 R_ARM_MOVW_PREL_NC = 0x2d;
static constexpr u32 R_ARM_MOVT_PREL = 0x2e;
static constexpr u32 R_ARM_THM_MOVW_ABS_NC = 0x2f;
static constexpr u32 R_ARM_THM_MOVT_ABS = 0x30;
static constexpr u32 R_ARM_THM_MOVW_PREL_NC = 0x31;
static constexpr u32 R_ARM_THM_MOVT_PREL = 0x32;
static constexpr u32 R_ARM_THM_JUMP19 = 0x33;
static constexpr u32 R_ARM_THM_JUMP6 = 0x34;
static constexpr u32 R_ARM_THM_ALU_PREL_11_0 = 0x35;
static constexpr u32 R_ARM_THM_PC12 = 0x36;
static constexpr u32 R_ARM_ABS32_NOI = 0x37;
static constexpr u32 R_ARM_REL32_NOI = 0x38;
static constexpr u32 R_ARM_ALU_PC_G0_NC = 0x39;
static constexpr u32 R_ARM_ALU_PC_G0 = 0x3a;
static constexpr u32 R_ARM_ALU_PC_G1_NC = 0x3b;
static constexpr u32 R_ARM_ALU_PC_G1 = 0x3c;
static constexpr u32 R_ARM_ALU_PC_G2 = 0x3d;
static constexpr u32 R_ARM_LDR_PC_G1 = 0x3e;
static constexpr u32 R_ARM_LDR_PC_G2 = 0x3f;
static constexpr u32 R_ARM_LDRS_PC_G0 = 0x40;
static constexpr u32 R_ARM_LDRS_PC_G1 = 0x41;
static constexpr u32 R_ARM_LDRS_PC_G2 = 0x42;
static constexpr u32 R_ARM_LDC_PC_G0 = 0x43;
static constexpr u32 R_ARM_LDC_PC_G1 = 0x44;
static constexpr u32 R_ARM_LDC_PC_G2 = 0x45;
static constexpr u32 R_ARM_ALU_SB_G0_NC = 0x46;
static constexpr u32 R_ARM_ALU_SB_G0 = 0x47;
static constexpr u32 R_ARM_ALU_SB_G1_NC = 0x48;
static constexpr u32 R_ARM_ALU_SB_G1 = 0x49;
static constexpr u32 R_ARM_ALU_SB_G2 = 0x4a;
static constexpr u32 R_ARM_LDR_SB_G0 = 0x4b;
static constexpr u32 R_ARM_LDR_SB_G1 = 0x4c;
static constexpr u32 R_ARM_LDR_SB_G2 = 0x4d;
static constexpr u32 R_ARM_LDRS_SB_G0 = 0x4e;
static constexpr u32 R_ARM_LDRS_SB_G1 = 0x4f;
static constexpr u32 R_ARM_LDRS_SB_G2 = 0x50;
static constexpr u32 R_ARM_LDC_SB_G0 = 0x51;
static constexpr u32 R_ARM_LDC_SB_G1 = 0x52;
static constexpr u32 R_ARM_LDC_SB_G2 = 0x53;
static constexpr u32 R_ARM_MOVW_BREL_NC = 0x54;
static constexpr u32 R_ARM_MOVT_BREL = 0x55;
static constexpr u32 R_ARM_MOVW_BREL = 0x56;
static constexpr u32 R_ARM_THM_MOVW_BREL_NC = 0x57;
static constexpr u32 R_ARM_THM_MOVT_BREL = 0x58;
static constexpr u32 R_ARM_THM_MOVW_BREL = 0x59;
static constexpr u32 R_ARM_TLS_GOTDESC = 0x5a;
static constexpr u32 R_ARM_TLS_CALL = 0x5b;
static constexpr u32 R_ARM_TLS_DESCSEQ = 0x5c;
static constexpr u32 R_ARM_THM_TLS_CALL = 0x5d;
static constexpr u32 R_ARM_PLT32_ABS = 0x5e;
static constexpr u32 R_ARM_GOT_ABS = 0x5f;
static constexpr u32 R_ARM_GOT_PREL = 0x60;
static constexpr u32 R_ARM_GOT_BREL12 = 0x61;
static constexpr u32 R_ARM_GOTOFF12 = 0x62;
static constexpr u32 R_ARM_GOTRELAX = 0x63;
static constexpr u32 R_ARM_GNU_VTENTRY = 0x64;
static constexpr u32 R_ARM_GNU_VTINHERIT = 0x65;
static constexpr u32 R_ARM_THM_JUMP11 = 0x66;
static constexpr u32 R_ARM_THM_JUMP8 = 0x67;
static constexpr u32 R_ARM_TLS_GD32 = 0x68;
static constexpr u32 R_ARM_TLS_LDM32 = 0x69;
static constexpr u32 R_ARM_TLS_LDO32 = 0x6a;
static constexpr u32 R_ARM_TLS_IE32 = 0x6b;
static constexpr u32 R_ARM_TLS_LE32 = 0x6c;
static constexpr u32 R_ARM_TLS_LDO12 = 0x6d;
static constexpr u32 R_ARM_TLS_LE12 = 0x6e;
static constexpr u32 R_ARM_TLS_IE12GP = 0x6f;
static constexpr u32 R_ARM_PRIVATE_0 = 0x70;
static constexpr u32 R_ARM_PRIVATE_1 = 0x71;
static constexpr u32 R_ARM_PRIVATE_2 = 0x72;
static constexpr u32 R_ARM_PRIVATE_3 = 0x73;
static constexpr u32 R_ARM_PRIVATE_4 = 0x74;
static constexpr u32 R_ARM_PRIVATE_5 = 0x75;
static constexpr u32 R_ARM_PRIVATE_6 = 0x76;
static constexpr u32 R_ARM_PRIVATE_7 = 0x77;
static constexpr u32 R_ARM_PRIVATE_8 = 0x78;
static constexpr u32 R_ARM_PRIVATE_9 = 0x79;
static constexpr u32 R_ARM_PRIVATE_10 = 0x7a;
static constexpr u32 R_ARM_PRIVATE_11 = 0x7b;
static constexpr u32 R_ARM_PRIVATE_12 = 0x7c;
static constexpr u32 R_ARM_PRIVATE_13 = 0x7d;
static constexpr u32 R_ARM_PRIVATE_14 = 0x7e;
static constexpr u32 R_ARM_PRIVATE_15 = 0x7f;
static constexpr u32 R_ARM_ME_TOO = 0x80;
static constexpr u32 R_ARM_THM_TLS_DESCSEQ16 = 0x81;
static constexpr u32 R_ARM_THM_TLS_DESCSEQ32 = 0x82;
static constexpr u32 R_ARM_THM_BF16 = 0x88;
static constexpr u32 R_ARM_THM_BF12 = 0x89;
static constexpr u32 R_ARM_THM_BF18 = 0x8a;
static constexpr u32 R_ARM_IRELATIVE = 0xa0;
template <>
inline std::string rel_to_string<ARM32>(u32 r_type) {
switch (r_type) {
case R_ARM_NONE: return "R_ARM_NONE";
case R_ARM_PC24: return "R_ARM_PC24";
case R_ARM_ABS32: return "R_ARM_ABS32";
case R_ARM_REL32: return "R_ARM_REL32";
case R_ARM_LDR_PC_G0: return "R_ARM_LDR_PC_G0";
case R_ARM_ABS16: return "R_ARM_ABS16";
case R_ARM_ABS12: return "R_ARM_ABS12";
case R_ARM_THM_ABS5: return "R_ARM_THM_ABS5";
case R_ARM_ABS8: return "R_ARM_ABS8";
case R_ARM_SBREL32: return "R_ARM_SBREL32";
case R_ARM_THM_CALL: return "R_ARM_THM_CALL";
case R_ARM_THM_PC8: return "R_ARM_THM_PC8";
case R_ARM_BREL_ADJ: return "R_ARM_BREL_ADJ";
case R_ARM_TLS_DESC: return "R_ARM_TLS_DESC";
case R_ARM_THM_SWI8: return "R_ARM_THM_SWI8";
case R_ARM_XPC25: return "R_ARM_XPC25";
case R_ARM_THM_XPC22: return "R_ARM_THM_XPC22";
case R_ARM_TLS_DTPMOD32: return "R_ARM_TLS_DTPMOD32";
case R_ARM_TLS_DTPOFF32: return "R_ARM_TLS_DTPOFF32";
case R_ARM_TLS_TPOFF32: return "R_ARM_TLS_TPOFF32";
case R_ARM_COPY: return "R_ARM_COPY";
case R_ARM_GLOB_DAT: return "R_ARM_GLOB_DAT";
case R_ARM_JUMP_SLOT: return "R_ARM_JUMP_SLOT";
case R_ARM_RELATIVE: return "R_ARM_RELATIVE";
case R_ARM_GOTOFF32: return "R_ARM_GOTOFF32";
case R_ARM_BASE_PREL: return "R_ARM_BASE_PREL";
case R_ARM_GOT_BREL: return "R_ARM_GOT_BREL";
case R_ARM_PLT32: return "R_ARM_PLT32";
case R_ARM_CALL: return "R_ARM_CALL";
case R_ARM_JUMP24: return "R_ARM_JUMP24";
case R_ARM_THM_JUMP24: return "R_ARM_THM_JUMP24";
case R_ARM_BASE_ABS: return "R_ARM_BASE_ABS";
case R_ARM_ALU_PCREL_7_0: return "R_ARM_ALU_PCREL_7_0";
case R_ARM_ALU_PCREL_15_8: return "R_ARM_ALU_PCREL_15_8";
case R_ARM_ALU_PCREL_23_15: return "R_ARM_ALU_PCREL_23_15";
case R_ARM_LDR_SBREL_11_0_NC: return "R_ARM_LDR_SBREL_11_0_NC";
case R_ARM_ALU_SBREL_19_12_NC: return "R_ARM_ALU_SBREL_19_12_NC";
case R_ARM_ALU_SBREL_27_20_CK: return "R_ARM_ALU_SBREL_27_20_CK";
case R_ARM_TARGET1: return "R_ARM_TARGET1";
case R_ARM_SBREL31: return "R_ARM_SBREL31";
case R_ARM_V4BX: return "R_ARM_V4BX";
case R_ARM_TARGET2: return "R_ARM_TARGET2";
case R_ARM_PREL31: return "R_ARM_PREL31";
case R_ARM_MOVW_ABS_NC: return "R_ARM_MOVW_ABS_NC";
case R_ARM_MOVT_ABS: return "R_ARM_MOVT_ABS";
case R_ARM_MOVW_PREL_NC: return "R_ARM_MOVW_PREL_NC";
case R_ARM_MOVT_PREL: return "R_ARM_MOVT_PREL";
case R_ARM_THM_MOVW_ABS_NC: return "R_ARM_THM_MOVW_ABS_NC";
case R_ARM_THM_MOVT_ABS: return "R_ARM_THM_MOVT_ABS";
case R_ARM_THM_MOVW_PREL_NC: return "R_ARM_THM_MOVW_PREL_NC";
case R_ARM_THM_MOVT_PREL: return "R_ARM_THM_MOVT_PREL";
case R_ARM_THM_JUMP19: return "R_ARM_THM_JUMP19";
case R_ARM_THM_JUMP6: return "R_ARM_THM_JUMP6";
case R_ARM_THM_ALU_PREL_11_0: return "R_ARM_THM_ALU_PREL_11_0";
case R_ARM_THM_PC12: return "R_ARM_THM_PC12";
case R_ARM_ABS32_NOI: return "R_ARM_ABS32_NOI";
case R_ARM_REL32_NOI: return "R_ARM_REL32_NOI";
case R_ARM_ALU_PC_G0_NC: return "R_ARM_ALU_PC_G0_NC";
case R_ARM_ALU_PC_G0: return "R_ARM_ALU_PC_G0";
case R_ARM_ALU_PC_G1_NC: return "R_ARM_ALU_PC_G1_NC";
case R_ARM_ALU_PC_G1: return "R_ARM_ALU_PC_G1";
case R_ARM_ALU_PC_G2: return "R_ARM_ALU_PC_G2";
case R_ARM_LDR_PC_G1: return "R_ARM_LDR_PC_G1";
case R_ARM_LDR_PC_G2: return "R_ARM_LDR_PC_G2";
case R_ARM_LDRS_PC_G0: return "R_ARM_LDRS_PC_G0";
case R_ARM_LDRS_PC_G1: return "R_ARM_LDRS_PC_G1";
case R_ARM_LDRS_PC_G2: return "R_ARM_LDRS_PC_G2";
case R_ARM_LDC_PC_G0: return "R_ARM_LDC_PC_G0";
case R_ARM_LDC_PC_G1: return "R_ARM_LDC_PC_G1";
case R_ARM_LDC_PC_G2: return "R_ARM_LDC_PC_G2";
case R_ARM_ALU_SB_G0_NC: return "R_ARM_ALU_SB_G0_NC";
case R_ARM_ALU_SB_G0: return "R_ARM_ALU_SB_G0";
case R_ARM_ALU_SB_G1_NC: return "R_ARM_ALU_SB_G1_NC";
case R_ARM_ALU_SB_G1: return "R_ARM_ALU_SB_G1";
case R_ARM_ALU_SB_G2: return "R_ARM_ALU_SB_G2";
case R_ARM_LDR_SB_G0: return "R_ARM_LDR_SB_G0";
case R_ARM_LDR_SB_G1: return "R_ARM_LDR_SB_G1";
case R_ARM_LDR_SB_G2: return "R_ARM_LDR_SB_G2";
case R_ARM_LDRS_SB_G0: return "R_ARM_LDRS_SB_G0";
case R_ARM_LDRS_SB_G1: return "R_ARM_LDRS_SB_G1";
case R_ARM_LDRS_SB_G2: return "R_ARM_LDRS_SB_G2";
case R_ARM_LDC_SB_G0: return "R_ARM_LDC_SB_G0";
case R_ARM_LDC_SB_G1: return "R_ARM_LDC_SB_G1";
case R_ARM_LDC_SB_G2: return "R_ARM_LDC_SB_G2";
case R_ARM_MOVW_BREL_NC: return "R_ARM_MOVW_BREL_NC";
case R_ARM_MOVT_BREL: return "R_ARM_MOVT_BREL";
case R_ARM_MOVW_BREL: return "R_ARM_MOVW_BREL";
case R_ARM_THM_MOVW_BREL_NC: return "R_ARM_THM_MOVW_BREL_NC";
case R_ARM_THM_MOVT_BREL: return "R_ARM_THM_MOVT_BREL";
case R_ARM_THM_MOVW_BREL: return "R_ARM_THM_MOVW_BREL";
case R_ARM_TLS_GOTDESC: return "R_ARM_TLS_GOTDESC";
case R_ARM_TLS_CALL: return "R_ARM_TLS_CALL";
case R_ARM_TLS_DESCSEQ: return "R_ARM_TLS_DESCSEQ";
case R_ARM_THM_TLS_CALL: return "R_ARM_THM_TLS_CALL";
case R_ARM_PLT32_ABS: return "R_ARM_PLT32_ABS";
case R_ARM_GOT_ABS: return "R_ARM_GOT_ABS";
case R_ARM_GOT_PREL: return "R_ARM_GOT_PREL";
case R_ARM_GOT_BREL12: return "R_ARM_GOT_BREL12";
case R_ARM_GOTOFF12: return "R_ARM_GOTOFF12";
case R_ARM_GOTRELAX: return "R_ARM_GOTRELAX";
case R_ARM_GNU_VTENTRY: return "R_ARM_GNU_VTENTRY";
case R_ARM_GNU_VTINHERIT: return "R_ARM_GNU_VTINHERIT";
case R_ARM_THM_JUMP11: return "R_ARM_THM_JUMP11";
case R_ARM_THM_JUMP8: return "R_ARM_THM_JUMP8";
case R_ARM_TLS_GD32: return "R_ARM_TLS_GD32";
case R_ARM_TLS_LDM32: return "R_ARM_TLS_LDM32";
case R_ARM_TLS_LDO32: return "R_ARM_TLS_LDO32";
case R_ARM_TLS_IE32: return "R_ARM_TLS_IE32";
case R_ARM_TLS_LE32: return "R_ARM_TLS_LE32";
case R_ARM_TLS_LDO12: return "R_ARM_TLS_LDO12";
case R_ARM_TLS_LE12: return "R_ARM_TLS_LE12";
case R_ARM_TLS_IE12GP: return "R_ARM_TLS_IE12GP";
case R_ARM_PRIVATE_0: return "R_ARM_PRIVATE_0";
case R_ARM_PRIVATE_1: return "R_ARM_PRIVATE_1";
case R_ARM_PRIVATE_2: return "R_ARM_PRIVATE_2";
case R_ARM_PRIVATE_3: return "R_ARM_PRIVATE_3";
case R_ARM_PRIVATE_4: return "R_ARM_PRIVATE_4";
case R_ARM_PRIVATE_5: return "R_ARM_PRIVATE_5";
case R_ARM_PRIVATE_6: return "R_ARM_PRIVATE_6";
case R_ARM_PRIVATE_7: return "R_ARM_PRIVATE_7";
case R_ARM_PRIVATE_8: return "R_ARM_PRIVATE_8";
case R_ARM_PRIVATE_9: return "R_ARM_PRIVATE_9";
case R_ARM_PRIVATE_10: return "R_ARM_PRIVATE_10";
case R_ARM_PRIVATE_11: return "R_ARM_PRIVATE_11";
case R_ARM_PRIVATE_12: return "R_ARM_PRIVATE_12";
case R_ARM_PRIVATE_13: return "R_ARM_PRIVATE_13";
case R_ARM_PRIVATE_14: return "R_ARM_PRIVATE_14";
case R_ARM_PRIVATE_15: return "R_ARM_PRIVATE_15";
case R_ARM_ME_TOO: return "R_ARM_ME_TOO";
case R_ARM_THM_TLS_DESCSEQ16: return "R_ARM_THM_TLS_DESCSEQ16";
case R_ARM_THM_TLS_DESCSEQ32: return "R_ARM_THM_TLS_DESCSEQ32";
case R_ARM_THM_BF16: return "R_ARM_THM_BF16";
case R_ARM_THM_BF12: return "R_ARM_THM_BF12";
case R_ARM_THM_BF18: return "R_ARM_THM_BF18";
case R_ARM_IRELATIVE: return "R_ARM_IRELATIVE";
}
return "unknown (" + std::to_string(r_type) + ")";
}
static constexpr u32 R_RISCV_NONE = 0;
static constexpr u32 R_RISCV_32 = 1;
static constexpr u32 R_RISCV_64 = 2;
static constexpr u32 R_RISCV_RELATIVE = 3;
static constexpr u32 R_RISCV_COPY = 4;
static constexpr u32 R_RISCV_JUMP_SLOT = 5;
static constexpr u32 R_RISCV_TLS_DTPMOD32 = 6;
static constexpr u32 R_RISCV_TLS_DTPMOD64 = 7;
static constexpr u32 R_RISCV_TLS_DTPREL32 = 8;
static constexpr u32 R_RISCV_TLS_DTPREL64 = 9;
static constexpr u32 R_RISCV_TLS_TPREL32 = 10;
static constexpr u32 R_RISCV_TLS_TPREL64 = 11;
static constexpr u32 R_RISCV_BRANCH = 16;
static constexpr u32 R_RISCV_JAL = 17;
static constexpr u32 R_RISCV_CALL = 18;
static constexpr u32 R_RISCV_CALL_PLT = 19;
static constexpr u32 R_RISCV_GOT_HI20 = 20;
static constexpr u32 R_RISCV_TLS_GOT_HI20 = 21;
static constexpr u32 R_RISCV_TLS_GD_HI20 = 22;
static constexpr u32 R_RISCV_PCREL_HI20 = 23;
static constexpr u32 R_RISCV_PCREL_LO12_I = 24;
static constexpr u32 R_RISCV_PCREL_LO12_S = 25;
static constexpr u32 R_RISCV_HI20 = 26;
static constexpr u32 R_RISCV_LO12_I = 27;
static constexpr u32 R_RISCV_LO12_S = 28;
static constexpr u32 R_RISCV_TPREL_HI20 = 29;
static constexpr u32 R_RISCV_TPREL_LO12_I = 30;
static constexpr u32 R_RISCV_TPREL_LO12_S = 31;
static constexpr u32 R_RISCV_TPREL_ADD = 32;
static constexpr u32 R_RISCV_ADD8 = 33;
static constexpr u32 R_RISCV_ADD16 = 34;
static constexpr u32 R_RISCV_ADD32 = 35;
static constexpr u32 R_RISCV_ADD64 = 36;
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;
static constexpr u32 R_RISCV_RVC_LUI = 46;
static constexpr u32 R_RISCV_RELAX = 51;
static constexpr u32 R_RISCV_SUB6 = 52;
static constexpr u32 R_RISCV_SET6 = 53;
static constexpr u32 R_RISCV_SET8 = 54;
static constexpr u32 R_RISCV_SET16 = 55;
static constexpr u32 R_RISCV_SET32 = 56;
static constexpr u32 R_RISCV_32_PCREL = 57;
static constexpr u32 R_RISCV_IRELATIVE = 58;
template <>
inline std::string rel_to_string<RISCV64>(u32 r_type) {
switch (r_type) {
case R_RISCV_NONE: return "R_RISCV_NONE";
case R_RISCV_32: return "R_RISCV_32";
case R_RISCV_64: return "R_RISCV_64";
case R_RISCV_RELATIVE: return "R_RISCV_RELATIVE";
case R_RISCV_COPY: return "R_RISCV_COPY";
case R_RISCV_JUMP_SLOT: return "R_RISCV_JUMP_SLOT";
case R_RISCV_TLS_DTPMOD32: return "R_RISCV_TLS_DTPMOD32";
case R_RISCV_TLS_DTPMOD64: return "R_RISCV_TLS_DTPMOD64";
case R_RISCV_TLS_DTPREL32: return "R_RISCV_TLS_DTPREL32";
case R_RISCV_TLS_DTPREL64: return "R_RISCV_TLS_DTPREL64";
case R_RISCV_TLS_TPREL32: return "R_RISCV_TLS_TPREL32";
case R_RISCV_TLS_TPREL64: return "R_RISCV_TLS_TPREL64";
case R_RISCV_BRANCH: return "R_RISCV_BRANCH";
case R_RISCV_JAL: return "R_RISCV_JAL";
case R_RISCV_CALL: return "R_RISCV_CALL";
case R_RISCV_CALL_PLT: return "R_RISCV_CALL_PLT";
case R_RISCV_GOT_HI20: return "R_RISCV_GOT_HI20";
case R_RISCV_TLS_GOT_HI20: return "R_RISCV_TLS_GOT_HI20";
case R_RISCV_TLS_GD_HI20: return "R_RISCV_TLS_GD_HI20";
case R_RISCV_PCREL_HI20: return "R_RISCV_PCREL_HI20";
case R_RISCV_PCREL_LO12_I: return "R_RISCV_PCREL_LO12_I";
case R_RISCV_PCREL_LO12_S: return "R_RISCV_PCREL_LO12_S";
case R_RISCV_HI20: return "R_RISCV_HI20";
case R_RISCV_LO12_I: return "R_RISCV_LO12_I";
case R_RISCV_LO12_S: return "R_RISCV_LO12_S";
case R_RISCV_TPREL_HI20: return "R_RISCV_TPREL_HI20";
case R_RISCV_TPREL_LO12_I: return "R_RISCV_TPREL_LO12_I";
case R_RISCV_TPREL_LO12_S: return "R_RISCV_TPREL_LO12_S";
case R_RISCV_TPREL_ADD: return "R_RISCV_TPREL_ADD";
case R_RISCV_ADD8: return "R_RISCV_ADD8";
case R_RISCV_ADD16: return "R_RISCV_ADD16";
case R_RISCV_ADD32: return "R_RISCV_ADD32";
case R_RISCV_ADD64: return "R_RISCV_ADD64";
case R_RISCV_SUB8: return "R_RISCV_SUB8";
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";
case R_RISCV_RVC_LUI: return "R_RISCV_RVC_LUI";
case R_RISCV_RELAX: return "R_RISCV_RELAX";
case R_RISCV_SUB6: return "R_RISCV_SUB6";
case R_RISCV_SET6: return "R_RISCV_SET6";
case R_RISCV_SET8: return "R_RISCV_SET8";
case R_RISCV_SET16: return "R_RISCV_SET16";
case R_RISCV_SET32: return "R_RISCV_SET32";
case R_RISCV_32_PCREL: return "R_RISCV_32_PCREL";
case R_RISCV_IRELATIVE: return "R_RISCV_IRELATIVE";
}
return "unknown (" + std::to_string(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;
@@ -644,6 +1050,38 @@ static constexpr u32 DW_EH_PE_datarel = 0x30;
static constexpr u32 DW_EH_PE_funcrel = 0x40;
static constexpr u32 DW_EH_PE_aligned = 0x50;
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_TAG_compile_unit = 0x11;
static constexpr u32 DW_FORM_addr = 0x01;
static constexpr u32 DW_FORM_block2 = 0x03;
static constexpr u32 DW_FORM_block4 = 0x04;
static constexpr u32 DW_FORM_data2 = 0x05;
static constexpr u32 DW_FORM_data4 = 0x06;
static constexpr u32 DW_FORM_data8 = 0x07;
static constexpr u32 DW_FORM_string = 0x08;
static constexpr u32 DW_FORM_block = 0x09;
static constexpr u32 DW_FORM_block1 = 0x0a;
static constexpr u32 DW_FORM_data1 = 0x0b;
static constexpr u32 DW_FORM_flag = 0x0c;
static constexpr u32 DW_FORM_sdata = 0x0d;
static constexpr u32 DW_FORM_strp = 0x0e;
static constexpr u32 DW_FORM_udata = 0x0f;
static constexpr u32 DW_FORM_ref_addr = 0x10;
static constexpr u32 DW_FORM_ref1 = 0x11;
static constexpr u32 DW_FORM_ref2 = 0x12;
static constexpr u32 DW_FORM_ref4 = 0x13;
static constexpr u32 DW_FORM_ref8 = 0x14;
static constexpr u32 DW_FORM_ref_udata = 0x15;
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;
struct Elf64Sym {
bool is_defined() const { return !is_undef(); }
bool is_undef() const { return st_shndx == SHN_UNDEF; }
@@ -867,6 +1305,7 @@ struct X86_64 {
static constexpr u32 R_COPY = R_X86_64_COPY;
static constexpr u32 R_GLOB_DAT = R_X86_64_GLOB_DAT;
static constexpr u32 R_JUMP_SLOT = R_X86_64_JUMP_SLOT;
static constexpr u32 R_ABS = R_X86_64_64;
static constexpr u32 R_RELATIVE = R_X86_64_RELATIVE;
static constexpr u32 R_IRELATIVE = R_X86_64_IRELATIVE;
static constexpr u32 R_DTPOFF = R_X86_64_DTPOFF64;
@@ -880,6 +1319,7 @@ struct 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;
};
template <> struct ElfSym<X86_64> : public Elf64Sym {};
@@ -897,6 +1337,7 @@ struct I386 {
static constexpr u32 R_COPY = R_386_COPY;
static constexpr u32 R_GLOB_DAT = R_386_GLOB_DAT;
static constexpr u32 R_JUMP_SLOT = R_386_JUMP_SLOT;
static constexpr u32 R_ABS = R_386_32;
static constexpr u32 R_RELATIVE = R_386_RELATIVE;
static constexpr u32 R_IRELATIVE = R_386_IRELATIVE;
static constexpr u32 R_DTPOFF = R_386_TLS_DTPOFF32;
@@ -910,6 +1351,7 @@ struct I386 {
static constexpr u32 pltgot_size = 8;
static constexpr bool is_rel = true;
static constexpr bool is_le = true;
static constexpr bool supports_tlsdesc = true;
};
template <> struct ElfSym<I386> : public Elf32Sym {};
@@ -927,6 +1369,7 @@ struct ARM64 {
static constexpr u32 R_COPY = R_AARCH64_COPY;
static constexpr u32 R_GLOB_DAT = R_AARCH64_GLOB_DAT;
static constexpr u32 R_JUMP_SLOT = R_AARCH64_JUMP_SLOT;
static constexpr u32 R_ABS = R_AARCH64_ABS64;
static constexpr u32 R_RELATIVE = R_AARCH64_RELATIVE;
static constexpr u32 R_IRELATIVE = R_AARCH64_IRELATIVE;
static constexpr u32 R_DTPOFF = R_AARCH64_TLS_DTPREL64;
@@ -940,6 +1383,7 @@ struct ARM64 {
static constexpr u32 pltgot_size = 16;
static constexpr bool is_rel = false;
static constexpr bool is_le = true;
static constexpr bool supports_tlsdesc = true;
};
template <> struct ElfSym<ARM64> : public Elf64Sym {};
@@ -950,4 +1394,67 @@ template <> struct ElfRel<ARM64> : public Elf64Rela {};
template <> struct ElfDyn<ARM64> : public Elf64Dyn {};
template <> struct ElfChdr<ARM64> : public Elf64Chdr {};
struct ARM32 {
using WordTy = u32;
static constexpr u32 R_NONE = R_ARM_NONE;
static constexpr u32 R_COPY = R_ARM_COPY;
static constexpr u32 R_GLOB_DAT = R_ARM_GLOB_DAT;
static constexpr u32 R_JUMP_SLOT = R_ARM_JUMP_SLOT;
static constexpr u32 R_ABS = R_ARM_ABS32;
static constexpr u32 R_RELATIVE = R_ARM_RELATIVE;
static constexpr u32 R_IRELATIVE = R_ARM_IRELATIVE;
static constexpr u32 R_DTPOFF = R_ARM_TLS_DTPOFF32;
static constexpr u32 R_TPOFF = R_ARM_TLS_TPOFF32;
static constexpr u32 R_DTPMOD = R_ARM_TLS_DTPMOD32;
static constexpr u32 R_TLSDESC = R_ARM_TLS_DESC;
static constexpr u32 word_size = 4;
static constexpr u32 page_size = 4096;
static constexpr u32 e_machine = EM_ARM;
static constexpr u32 pltgot_size = 16;
static constexpr bool is_rel = true;
static constexpr bool is_le = true;
static constexpr bool supports_tlsdesc = true;
};
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 ElfDyn<ARM32> : public Elf32Dyn {};
template <> struct ElfChdr<ARM32> : public Elf32Chdr {};
struct RISCV64 {
using WordTy = u64;
static constexpr u32 R_NONE = R_RISCV_NONE;
static constexpr u32 R_COPY = R_RISCV_COPY;
static constexpr u32 R_GLOB_DAT = R_RISCV_64;
static constexpr u32 R_JUMP_SLOT = R_RISCV_JUMP_SLOT;
static constexpr u32 R_ABS = R_RISCV_64;
static constexpr u32 R_RELATIVE = R_RISCV_RELATIVE;
static constexpr u32 R_IRELATIVE = R_RISCV_IRELATIVE;
static constexpr u32 R_DTPOFF = R_RISCV_TLS_DTPREL64;
static constexpr u32 R_TPOFF = R_RISCV_TLS_TPREL64;
static constexpr u32 R_DTPMOD = R_RISCV_TLS_DTPMOD64;
static constexpr u32 word_size = 8;
static constexpr u32 page_size = 4096;
static constexpr u32 e_machine = EM_RISCV;
static constexpr u32 pltgot_size = 16;
static constexpr bool is_rel = false;
static constexpr bool is_le = true;
static constexpr bool supports_tlsdesc = false;
};
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 ElfDyn<RISCV64> : public Elf64Dyn {};
template <> struct ElfChdr<RISCV64> : public Elf64Chdr {};
} // namespace mold::elf
+29 -20
View File
@@ -11,24 +11,28 @@ namespace mold::elf {
template <typename E>
static bool is_init_fini(const InputSection<E> &isec) {
return isec.shdr.sh_type == SHT_INIT_ARRAY ||
isec.shdr.sh_type == SHT_FINI_ARRAY ||
isec.shdr.sh_type == SHT_PREINIT_ARRAY ||
isec.name().starts_with(".ctors") ||
isec.name().starts_with(".dtors") ||
isec.name().starts_with(".init") ||
isec.name().starts_with(".fini");
u32 type = isec.shdr().sh_type;
std::string_view name = isec.name();
return type == SHT_INIT_ARRAY ||
type == SHT_FINI_ARRAY ||
type == SHT_PREINIT_ARRAY ||
(std::is_same_v<E, ARM32> && type == SHT_ARM_EXIDX) ||
name.starts_with(".ctors") ||
name.starts_with(".dtors") ||
name.starts_with(".init") ||
name.starts_with(".fini");
}
template <typename E>
static bool mark_section(InputSection<E> *isec) {
return isec && isec->is_alive && !isec->is_visited.exchange(true);
return isec && isec->is_alive && !isec->extra().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->is_visited);
assert(isec->extra().is_visited);
// A relocation can refer either a section fragment (i.e. a piece of
// string in a mergeable string section) or a symbol. Mark all
@@ -41,10 +45,10 @@ 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().subspan(1))
for (ElfRel<E> &rel : fde.get_rels(isec->file).subspan(1))
if (Symbol<E> *sym = isec->file.symbols[rel.r_sym])
if (mark_section(sym->input_section))
feeder.add(sym->input_section);
if (mark_section(sym->get_input_section()))
feeder.add(sym->get_input_section());
for (ElfRel<E> &rel : isec->get_rels(ctx)) {
Symbol<E> &sym = *isec->file.symbols[rel.r_sym];
@@ -56,15 +60,15 @@ static void visit(Context<E> &ctx, InputSection<E> *isec,
continue;
}
if (!mark_section(sym.input_section))
if (!mark_section(sym.get_input_section()))
continue;
// Mark a section alive. For better performacne, we don't call
// `feeder.add` too often.
if (depth < 3)
visit(ctx, sym.input_section, feeder, depth + 1);
visit(ctx, sym.get_input_section(), feeder, depth + 1);
else
feeder.add(sym.input_section);
feeder.add(sym.get_input_section());
}
}
@@ -84,7 +88,7 @@ collect_root_set(Context<E> &ctx) {
if (SectionFragment<E> *frag = sym->get_frag())
frag->is_alive.store(true, std::memory_order_relaxed);
else
enqueue_section(sym->input_section);
enqueue_section(sym->get_input_section());
}
};
@@ -98,11 +102,11 @@ 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->is_visited = true;
if (!(isec->shdr().sh_flags & SHF_ALLOC))
isec->extra().is_visited = true;
if (is_init_fini(*isec) || is_c_identifier(isec->name()) ||
isec->shdr.sh_type == SHT_NOTE)
isec->shdr().sh_type == SHT_NOTE)
enqueue_section(isec.get());
}
});
@@ -155,7 +159,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->is_visited) {
if (isec && isec->is_alive && !isec->extra().is_visited) {
if (ctx.arg.print_gc_sections)
SyncOut(ctx) << "removing unused section " << *isec;
isec->kill();
@@ -184,6 +188,9 @@ 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);
@@ -197,5 +204,7 @@ void gc_sections(Context<E> &ctx) {
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+53 -48
View File
@@ -53,6 +53,7 @@
// thread and even better with multiple threads.
#include "mold.h"
#include "../sha.h"
#include <array>
#include <tbb/concurrent_unordered_map.h>
@@ -62,13 +63,6 @@
#include <tbb/parallel_for_each.h>
#include <tbb/parallel_sort.h>
#ifdef __APPLE__
# define COMMON_DIGEST_FOR_OPENSSL
# include <CommonCrypto/CommonDigest.h>
#else
# include <openssl/sha.h>
#endif
static constexpr int64_t HASH_SIZE = 16;
typedef std::array<uint8_t, HASH_SIZE> Digest;
@@ -105,7 +99,7 @@ static void uniquify_cies(Context<E> &ctx) {
template <typename E>
static bool is_eligible(InputSection<E> &isec) {
const ElfShdr<E> &shdr = isec.shdr;
const ElfShdr<E> &shdr = isec.shdr();
std::string_view name = isec.name();
bool is_alloc = (shdr.sh_flags & SHF_ALLOC);
@@ -139,7 +133,7 @@ static bool is_leaf(Context<E> &ctx, InputSection<E> &isec) {
return false;
for (FdeRecord<E> &fde : isec.get_fdes())
if (fde.get_rels().size() > 1)
if (fde.get_rels(isec.file).size() > 1)
return false;
return true;
@@ -151,10 +145,10 @@ static u64 combine_hash(u64 a, u64 b) {
template <typename E>
struct LeafHasher {
size_t operator()(const InputSection<E> *isec) const {
size_t operator()(InputSection<E> *isec) const {
u64 h = hash_string(isec->contents);
for (FdeRecord<E> &fde : isec->get_fdes()) {
u64 h2 = hash_string(fde.get_contents().substr(8));
u64 h2 = hash_string(fde.get_contents(isec->file).substr(8));
h = combine_hash(h, h2);
}
return h;
@@ -163,7 +157,7 @@ struct LeafHasher {
template <typename E>
struct LeafEq {
bool operator()(const InputSection<E> *a, const InputSection<E> *b) const {
bool operator()(InputSection<E> *a, InputSection<E> *b) const {
if (a->contents != b->contents)
return false;
@@ -174,7 +168,8 @@ struct LeafEq {
return false;
for (i64 i = 0; i < x.size(); i++)
if (x[i].get_contents().substr(8) != y[i].get_contents().substr(8))
if (x[i].get_contents(a->file).substr(8) !=
y[i].get_contents(b->file).substr(8))
return false;
return true;
}
@@ -203,23 +198,23 @@ static void merge_leaf_nodes(Context<E> &ctx) {
if (is_leaf(ctx, *isec)) {
leaf++;
isec->icf_leaf = true;
isec->extra().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->icf_eligible = true;
isec->extra().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->icf_leaf) {
if (isec && isec->is_alive && isec->extra().icf_leaf) {
auto it = map.find(isec.get());
assert(it != map.end());
isec->leader = it->second;
isec->extra().leader = it->second;
}
}
});
@@ -240,7 +235,7 @@ static Digest compute_digest(Context<E> &ctx, InputSection<E> &isec) {
};
auto hash_symbol = [&](Symbol<E> &sym) {
InputSection<E> *isec = sym.input_section;
InputSection<E> *isec = sym.get_input_section();
if (!sym.file) {
hash('1');
@@ -250,10 +245,10 @@ static Digest compute_digest(Context<E> &ctx, InputSection<E> &isec) {
hash((u64)frag);
} else if (!isec) {
hash('3');
} else if (isec->leader) {
} else if (isec->extra().leader) {
hash('4');
hash((u64)isec->leader);
} else if (isec->icf_eligible) {
hash((u64)isec->extra().leader);
} else if (isec->extra().icf_eligible) {
hash('5');
} else {
hash('6');
@@ -263,24 +258,24 @@ static Digest compute_digest(Context<E> &ctx, InputSection<E> &isec) {
};
hash_string(isec.contents);
hash(isec.shdr.sh_flags);
hash(isec.shdr().sh_flags);
hash(isec.get_fdes().size());
hash(isec.get_rels(ctx).size());
for (FdeRecord<E> &fde : isec.get_fdes()) {
hash(fde.cie->icf_idx);
hash(isec.file.cies[fde.cie_idx].icf_idx);
// Bytes 0 to 4 contain the length of this record, and
// bytes 4 to 8 contain an offset to CIE.
hash_string(fde.get_contents().substr(8));
hash_string(fde.get_contents(isec.file).substr(8));
hash(fde.get_rels().size());
hash(fde.get_rels(isec.file).size());
for (ElfRel<E> &rel : fde.get_rels().subspan(1)) {
for (ElfRel<E> &rel : fde.get_rels(isec.file).subspan(1)) {
hash_symbol(*isec.file.symbols[rel.r_sym]);
hash(rel.r_type);
hash(rel.r_offset - fde.input_offset);
hash(fde.cie->input_section.get_addend(rel));
hash(isec.file.cies[fde.cie_idx].input_section.get_addend(rel));
}
}
@@ -314,7 +309,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->icf_eligible)
if (isec && isec->is_alive && isec->extra().icf_eligible)
num_sections[i]++;
});
@@ -329,12 +324,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->icf_eligible)
if (isec && isec->is_alive && isec->extra().icf_eligible)
sections[idx++] = isec.get();
});
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
sections[i]->icf_idx = i;
sections[i]->extra().icf_idx = i;
});
return sections;
@@ -359,12 +354,15 @@ static void gather_edges(Context<E> &ctx,
std::vector<u32> &edge_indices) {
Timer t(ctx, "gather_edges");
if (sections.empty())
return;
std::vector<i64> num_edges(sections.size());
edge_indices.resize(sections.size());
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
InputSection<E> &isec = *sections[i];
assert(isec.icf_eligible);
assert(isec.extra().icf_eligible);
i64 frag_idx = 0;
for (i64 j = 0; j < isec.get_rels(ctx).size(); j++) {
@@ -373,9 +371,10 @@ static void gather_edges(Context<E> &ctx,
} else {
ElfRel<E> &rel = isec.get_rels(ctx)[j];
Symbol<E> &sym = *isec.file.symbols[rel.r_sym];
if (!sym.get_frag() && sym.input_section &&
sym.input_section->icf_eligible)
num_edges[i]++;
if (!sym.get_frag())
if (InputSection<E> *isec = sym.get_input_section())
if (isec->extra().icf_eligible)
num_edges[i]++;
}
}
});
@@ -395,9 +394,10 @@ static void gather_edges(Context<E> &ctx,
frag_idx++;
ElfRel<E> &rel = isec.get_rels(ctx)[j];
Symbol<E> &sym = *isec.file.symbols[rel.r_sym];
if (!sym.get_frag() && sym.input_section &&
sym.input_section->icf_eligible)
edges[idx++] = sym.input_section->icf_idx;
if (!sym.get_frag())
if (InputSection<E> *isec = sym.get_input_section())
if (isec->extra().icf_eligible)
edges[idx++] = isec->extra().icf_idx;
}
}
});
@@ -458,11 +458,11 @@ static void print_icf_sections(Context<E> &ctx) {
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (std::unique_ptr<InputSection<E>> &isec : file->sections) {
if (isec && isec->is_alive && isec->leader) {
if (isec.get() == isec->leader)
if (isec && isec->is_alive && isec->extra().leader) {
if (isec.get() == isec->extra().leader)
leaders.push_back(isec.get());
else
map.insert({isec->leader, isec.get()});
map.insert({isec->extra().leader, isec.get()});
}
}
});
@@ -496,6 +496,9 @@ 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());
uniquify_cies(ctx);
merge_leaf_nodes(ctx);
@@ -555,11 +558,11 @@ 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]->leader = it->second;
sections[i]->extra().leader = it->second;
});
// Since free'ing the map is slow, postpone it.
ctx.on_exit.push_back([=]() { delete map; });
ctx.on_exit.push_back([=] { delete map; });
}
if (ctx.arg.print_icf_sections)
@@ -570,12 +573,12 @@ void icf_sections(Context<E> &ctx) {
Timer t(ctx, "reassign");
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
for (Symbol<E> *sym : file->symbols) {
if (sym->file != file)
continue;
InputSection<E> *isec = sym->input_section;
if (isec && isec->leader && isec->leader != isec) {
sym->input_section = isec->leader;
isec->kill();
if (sym->file == file) {
InputSection<E> *isec = sym->get_input_section();
if (isec && isec->extra().leader && isec->extra().leader != isec) {
isec->kill();
isec->killed_by_icf = true;
}
}
}
});
@@ -588,5 +591,7 @@ void icf_sections(Context<E> &ctx) {
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+287 -212
View File
@@ -3,7 +3,6 @@
#include <cstring>
#include <regex>
#include <unistd.h>
#include <zlib.h>
namespace mold::elf {
@@ -48,13 +47,12 @@ ElfShdr<E> *InputFile<E>::find_section(i64 type) {
}
template <typename E>
void InputFile<E>::clear_symbols(Context<E> &ctx) {
assert(!is_alive);
void InputFile<E>::clear_symbols() {
for (Symbol<E> *sym : get_global_syms()) {
std::scoped_lock lock(sym->mu);
if (sym->file == this) {
sym->file = nullptr;
sym->input_section = nullptr;
sym->shndx = 0;
sym->value = -1;
sym->sym_idx = -1;
sym->ver_idx = 0;
@@ -72,15 +70,12 @@ ObjectFile<E>::ObjectFile(Context<E> &ctx, MappedFile<Context<E>> *mf,
this->is_alive = !is_in_lib;
}
template <typename E>
ObjectFile<E>::ObjectFile() {}
template <typename E>
ObjectFile<E> *
ObjectFile<E>::create(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::string archive_name, bool is_in_lib) {
ObjectFile<E> *obj = new ObjectFile<E>(ctx, mf, archive_name, is_in_lib);
ctx.obj_pool.push_back(std::unique_ptr<ObjectFile<E>>(obj));
ctx.obj_pool.emplace_back(obj);
return obj;
}
@@ -121,68 +116,6 @@ u32 ObjectFile<E>::read_note_gnu_property(Context<E> &ctx,
return ret;
}
template <typename E>
std::pair<std::string_view, const ElfShdr<E> *>
ObjectFile<E>::uncompress_contents(Context<E> &ctx, const ElfShdr<E> &shdr,
std::string_view name) {
if (shdr.sh_type == SHT_NOBITS)
return {{}, &shdr};
auto do_uncompress = [&](std::string_view data, u64 size) {
u8 *buf = new u8[size];
ctx.string_pool.push_back(std::unique_ptr<u8[]>(buf));
unsigned long size2 = size;
if (uncompress(buf, &size2, (u8 *)&data[0], data.size()) != Z_OK)
Fatal(ctx) << *this << ": " << name << ": uncompress failed";
if (size != size2)
Fatal(ctx) << *this << ": " << name << ": uncompress: invalid size";
return std::string_view((char *)buf, size);
};
auto copy_shdr = [&](const ElfShdr<E> &shdr) {
ElfShdr<E> *ret = new ElfShdr<E>;
ctx.shdr_pool.push_back(std::unique_ptr<ElfShdr<E>>(ret));
*ret = shdr;
return ret;
};
if (name.starts_with(".zdebug")) {
// Old-style compressed section
std::string_view data = this->get_string(ctx, shdr);
if (!data.starts_with("ZLIB") || data.size() <= 12)
Fatal(ctx) << *this << ": " << name << ": corrupted compressed section";
u64 size = *(ubig64 *)&data[4];
std::string_view contents = do_uncompress(data.substr(12), size);
ElfShdr<E> *shdr2 = copy_shdr(shdr);
shdr2->sh_size = size;
return {contents, shdr2};
}
if (shdr.sh_flags & SHF_COMPRESSED) {
// New-style compressed section
std::string_view data = this->get_string(ctx, shdr);
if (data.size() < sizeof(ElfChdr<E>))
Fatal(ctx) << *this << ": " << name << ": corrupted compressed section";
ElfChdr<E> &hdr = *(ElfChdr<E> *)&data[0];
data = data.substr(sizeof(ElfChdr<E>));
if (hdr.ch_type != ELFCOMPRESS_ZLIB)
Fatal(ctx) << *this << ": " << name << ": unsupported compression type";
ElfShdr<E> *shdr2 = copy_shdr(shdr);
shdr2->sh_flags &= ~(u64)(SHF_COMPRESSED);
shdr2->sh_size = hdr.ch_size;
shdr2->sh_addralign = hdr.ch_addralign;
std::string_view contents = do_uncompress(data, hdr.ch_size);
return {contents, shdr2};
}
return {this->get_string(ctx, shdr), &shdr};
}
template <typename E>
void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
// Read sections
@@ -224,10 +157,25 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
case SHT_REL:
case SHT_RELA:
case SHT_NULL:
case SHT_ARM_ATTRIBUTES:
break;
default: {
std::string_view name = this->shstrtab.data() + shdr.sh_name;
if (name == ".note.GNU-stack" || name.starts_with(".gnu.warning."))
// .note.GNU-stack section controls executable-ness of the stack
// area in GNU linkers. We ignore that section because silently
// making the stack area executable is too dangerous. Tell our
// 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.";
continue;
}
if (name.starts_with(".gnu.warning."))
continue;
if (name == ".note.gnu.property") {
@@ -244,17 +192,37 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
if (name == ".gnu.linkonce.d.DW.ref.__gxx_personality_v0")
continue;
// Ignore debug sections if --strip-all or --strip-debug is given.
if ((ctx.arg.strip_all || ctx.arg.strip_debug) &&
is_debug_section(shdr, name))
continue;
std::string_view contents;
const ElfShdr<E> *shdr2;
std::tie(contents, shdr2) = uncompress_contents(ctx, shdr, name);
this->sections[i] = std::make_unique<InputSection<E>>(ctx, *this, name, i);
this->sections[i] =
std::make_unique<InputSection<E>>(ctx, *this, *shdr2, name,
contents, i);
// 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 --gdb-index is given, contents of .debug_gnu_pubnames and
// .debug_gnu_pubtypes are copied to .gdb_index, so keeping them
// in an output file is just a waste of space.
if (name == ".debug_gnu_pubnames") {
debug_pubnames = isec;
isec->is_alive = false;
}
if (name == ".debug_gnu_pubtypes") {
debug_pubtypes = isec;
isec->is_alive = false;
}
}
static Counter counter("regular_sections");
counter++;
@@ -276,12 +244,6 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
if (std::unique_ptr<InputSection<E>> &target = sections[shdr.sh_info]) {
assert(target->relsec_idx == -1);
target->relsec_idx = i;
if (target->shdr.sh_flags & SHF_ALLOC) {
i64 size = shdr.sh_size / sizeof(ElfRel<E>);
target->needs_dynrel.resize(size);
target->needs_baserel.resize(size);
}
}
}
}
@@ -292,13 +254,9 @@ void ObjectFile<E>::initialize_ehframe_sections(Context<E> &ctx) {
std::unique_ptr<InputSection<E>> &isec = sections[i];
if (isec && isec->is_alive && isec->name() == ".eh_frame") {
read_ehframe(ctx, *isec);
isec->is_ehframe = true;
isec->is_alive = false;
}
}
for (FdeRecord<E> &fde : fdes)
fde.cie = &cies[fde.cie_idx];
}
// .eh_frame contains data records explaining how to handle exceptions.
@@ -336,14 +294,8 @@ void ObjectFile<E>::read_ehframe(Context<E> &ctx, InputSection<E> &isec) {
i64 cies_begin = cies.size();
i64 fdes_begin = fdes.size();
// Verify relocations.
for (i64 i = 1; i < rels.size(); i++)
if (rels[i].r_type != E::R_NONE &&
rels[i].r_offset <= rels[i - 1].r_offset)
Fatal(ctx) << isec << ": relocation offsets must increase monotonically";
// Read CIEs and FDEs until empty.
std::string_view contents = this->get_string(ctx, isec.shdr);
std::string_view contents = this->get_string(ctx, isec.shdr());
i64 rel_idx = 0;
for (std::string_view data = contents; !data.empty();) {
@@ -363,7 +315,7 @@ void ObjectFile<E>::read_ehframe(Context<E> &ctx, InputSection<E> &isec) {
if (id == 0) {
// This is CIE.
cies.push_back(CieRecord<E>(ctx, *this, isec, begin_offset, rel_begin));
cies.emplace_back(ctx, *this, isec, begin_offset, rels, rel_begin);
} else {
// This is FDE.
if (rel_begin == rel_idx || rels[rel_begin].r_sym == 0) {
@@ -376,7 +328,7 @@ void ObjectFile<E>::read_ehframe(Context<E> &ctx, InputSection<E> &isec) {
if (rels[rel_begin].r_offset - begin_offset != 8)
Fatal(ctx) << isec << ": FDE's first relocation should have offset 8";
fdes.push_back(FdeRecord<E>(begin_offset, rel_begin));
fdes.emplace_back(begin_offset, rel_begin);
}
}
@@ -416,31 +368,6 @@ void ObjectFile<E>::read_ehframe(Context<E> &ctx, InputSection<E> &isec) {
}
}
template <typename E>
static bool should_write_to_local_symtab(Context<E> &ctx, Symbol<E> &sym) {
if (ctx.arg.discard_all || ctx.arg.strip_all || ctx.arg.retain_symbols_file)
return false;
if (sym.get_type() == STT_SECTION)
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
// know the rationale. Anyway, this is the behavior of the
// traditional linkers.
if (sym.name().starts_with(".L")) {
if (ctx.arg.discard_locals)
return false;
if (InputSection<E> *isec = sym.input_section)
if (isec->shdr.sh_flags & SHF_MERGE)
return false;
}
return true;
}
// Returns a symbol object for a given key. This function handles
// the -wrap option.
template <typename E>
@@ -478,8 +405,10 @@ void ObjectFile<E>::initialize_symbols(Context<E> &ctx) {
for (i64 i = 1; i < this->first_global; i++) {
const ElfSym<E> &esym = this->elf_syms[i];
std::string_view name = symbol_strtab.data() + esym.st_name;
if (esym.is_common())
Fatal(ctx) << *this << ": common local symbol?";
std::string_view name = symbol_strtab.data() + esym.st_name;
if (name.empty() && esym.st_type == STT_SECTION)
if (InputSection<E> *sec = get_section(esym))
name = sec->name();
@@ -490,17 +419,8 @@ void ObjectFile<E>::initialize_symbols(Context<E> &ctx) {
sym.value = esym.st_value;
sym.sym_idx = i;
if (!esym.is_abs()) {
if (esym.is_common())
Fatal(ctx) << *this << ": common local symbol?";
sym.input_section = get_section(esym);
}
if (should_write_to_local_symtab(ctx, sym)) {
sym.write_to_symtab = true;
strtab_size += sym.name().size() + 1;
num_local_symtab++;
}
if (!esym.is_abs())
sym.shndx = esym.is_abs() ? 0 : get_shndx(esym);
}
this->symbols.resize(this->elf_syms.size());
@@ -539,6 +459,35 @@ void ObjectFile<E>::initialize_symbols(Context<E> &ctx) {
}
}
// Relocations are usually sorted by r_offset in relocation tables,
// but for some reason only RISC-V does not follow that convention.
// 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;
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;
};
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);
}
}
static size_t find_null(std::string_view data, u64 entsize) {
if (entsize == 1)
return data.find('\0');
@@ -571,20 +520,26 @@ template <typename E>
static std::unique_ptr<MergeableSection<E>>
split_section(Context<E> &ctx, InputSection<E> &sec) {
std::unique_ptr<MergeableSection<E>> rec(new MergeableSection<E>);
rec->parent = MergedSection<E>::get_instance(ctx, sec.name(), sec.shdr.sh_type,
sec.shdr.sh_flags);
rec->shdr = sec.shdr;
rec->parent = MergedSection<E>::get_instance(ctx, sec.name(), sec.shdr().sh_type,
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);
}
const char *begin = data.data();
u64 entsize = sec.shdr.sh_entsize;
u64 entsize = sec.shdr().sh_entsize;
HyperLogLog estimator;
static_assert(sizeof(SectionFragment<E>::alignment) == 2);
if (sec.shdr.sh_addralign >= UINT16_MAX)
Fatal(ctx) << sec << ": alignment too large";
if (sec.shdr.sh_flags & SHF_STRINGS) {
// Split sections
if (sec.shdr().sh_flags & SHF_STRINGS) {
while (!data.empty()) {
size_t end = find_null(data, entsize);
if (end == data.npos)
@@ -672,8 +627,8 @@ void ObjectFile<E>::initialize_mergeable_sections(Context<E> &ctx) {
for (i64 i = 0; i < sections.size(); i++) {
std::unique_ptr<InputSection<E>> &isec = sections[i];
if (isec && isec->is_alive && (isec->shdr.sh_flags & SHF_MERGE) &&
isec->shdr.sh_size && isec->shdr.sh_entsize &&
if (isec && isec->is_alive && (isec->shdr().sh_flags & SHF_MERGE) &&
isec->sh_size && isec->shdr().sh_entsize &&
isec->relsec_idx == -1) {
mergeable_sections[i] = split_section(ctx, *isec);
isec->is_alive = false;
@@ -683,15 +638,22 @@ void ObjectFile<E>::initialize_mergeable_sections(Context<E> &ctx) {
template <typename E>
void ObjectFile<E>::register_section_pieces(Context<E> &ctx) {
for (std::unique_ptr<MergeableSection<E>> &m : mergeable_sections)
if (m)
for (std::unique_ptr<MergeableSection<E>> &m : mergeable_sections) {
if (m) {
m->fragments.reserve(m->strings.size());
for (i64 i = 0; i < m->strings.size(); i++)
m->fragments.push_back(m->parent->insert(m->strings[i], m->hashes[i],
m->shdr.sh_addralign));
m->p2align));
// Shrink vectors that we will never use again to reclaim memory.
m->strings.clear();
m->hashes.clear();
}
}
// Initialize rel_fragments
for (std::unique_ptr<InputSection<E>> &isec : sections) {
if (!isec || !isec->is_alive || !(isec->shdr.sh_flags & SHF_ALLOC))
if (!isec || !isec->is_alive || !(isec->shdr().sh_flags & SHF_ALLOC))
continue;
std::span<ElfRel<E>> rels = isec->get_rels(ctx);
@@ -779,6 +741,7 @@ void ObjectFile<E>::parse(Context<E> &ctx) {
initialize_sections(ctx);
initialize_symbols(ctx);
sort_relocations(ctx);
initialize_mergeable_sections(ctx);
initialize_ehframe_sections(ctx);
}
@@ -803,12 +766,23 @@ static u64 get_rank(InputFile<E> *file, const ElfSym<E> &esym, bool is_lazy) {
return (6 << 24) + file->priority;
return (5 << 24) + file->priority;
}
// GCC creates symbols in COMDATs with STB_GNU_UNIQUE instead of
// STB_WEAK if it was configured to do so at build time or the
// -fgnu-unique flag was given. In order to to not select a
// GNU_UNIQUE symbol in a discarded COMDAT section, we treat it as
// if it were weak.
//
// It looks like STB_GNU_UNIQUE is not a popular option anymore and
// often disabled by default though.
bool is_weak = (esym.st_bind == STB_WEAK || esym.st_bind == STB_GNU_UNIQUE);
if (file->is_dso || is_lazy) {
if (esym.is_weak())
if (is_weak)
return (4 << 24) + file->priority;
return (3 << 24) + file->priority;
}
if (esym.is_weak())
if (is_weak)
return (2 << 24) + file->priority;
return (1 << 24) + file->priority;
}
@@ -820,6 +794,11 @@ static u64 get_rank(const Symbol<E> &sym) {
return get_rank(sym.file, sym.esym(), !sym.file->is_alive);
}
// Symbol's visibility is set to the most restrictive one. For example,
// if one input file has a defined symbol `foo` with the default
// visibility and the other input file has an undefined symbol `foo`
// with the hidden visibility, the resulting symbol is a hidden defined
// symbol.
template <typename E>
void ObjectFile<E>::merge_visibility(Context<E> &ctx, Symbol<E> &sym,
u8 visibility) {
@@ -874,13 +853,8 @@ void ObjectFile<E>::resolve_symbols(Context<E> &ctx) {
std::scoped_lock lock(sym.mu);
if (get_rank(this, esym, !this->is_alive) < get_rank(sym)) {
sym.file = this;
sym.input_section = isec;
if (SectionFragmentRef<E> &ref = sym_fragments[i]; ref.frag)
sym.value = ref.addend;
else
sym.value = esym.st_value;
sym.shndx = isec ? isec->shndx : 0;
sym.value = esym.st_value;
sym.sym_idx = i;
sym.ver_idx = ctx.default_version;
sym.is_weak = esym.is_weak();
@@ -926,6 +900,16 @@ ObjectFile<E>::mark_live_objects(Context<E> &ctx,
}
}
// Comdat groups are used to de-duplicate functions and data that may
// be included into multiple object files. C++ compiler uses comdat
// groups to de-duplicate instantiated templates.
//
// For example, if a compiler decides to instantiate `std::vector<int>`,
// it generates code and data for `std::vector<int>` and put them into a
// comdat group whose name is the mangled name of `std::vector<int>`.
// The instantiation may happen multiple times for different translation
// units. Then linker de-duplicates them so that the resulting executable
// contains only a single copy of `std::vector<int>`.
template <typename E>
void ObjectFile<E>::resolve_comdat_groups() {
for (auto &pair : comdat_groups) {
@@ -961,6 +945,14 @@ void ObjectFile<E>::claim_unresolved_symbols(Context<E> &ctx) {
std::scoped_lock lock(sym.mu);
// If a protected/hidden undefined symbol is resolved to an
// 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);
continue;
}
if (sym.file &&
(!sym.esym().is_undef() || sym.file->priority <= this->priority))
continue;
@@ -977,9 +969,9 @@ void ObjectFile<E>::claim_unresolved_symbols(Context<E> &ctx) {
}
}
auto claim = [&]() {
auto claim = [&] {
sym.file = this;
sym.input_section = nullptr;
sym.shndx = 0;
sym.value = 0;
sym.sym_idx = i;
sym.is_weak = false;
@@ -987,7 +979,7 @@ void ObjectFile<E>::claim_unresolved_symbols(Context<E> &ctx) {
};
if (ctx.arg.unresolved_symbols == UNRESOLVED_WARN)
Warn(ctx) << "undefined symbol: " << *this << ": " << sym;
report_undef(ctx, *this, sym);
// Convert remaining undefined symbols to dynamic symbols.
if (ctx.arg.shared) {
@@ -1017,7 +1009,7 @@ void ObjectFile<E>::claim_unresolved_symbols(Context<E> &ctx) {
// Convert remaining undefined symbols to absolute symbols with value 0.
if (ctx.arg.unresolved_symbols != UNRESOLVED_ERROR ||
esym.is_undef_weak()) {
ctx.arg.noinhibit_exec || esym.is_undef_weak()) {
claim();
sym.ver_idx = ctx.default_version;
sym.is_imported = false;
@@ -1029,7 +1021,7 @@ template <typename E>
void ObjectFile<E>::scan_relocations(Context<E> &ctx) {
// Scan relocations against seciton contents
for (std::unique_ptr<InputSection<E>> &isec : sections)
if (isec && isec->is_alive && (isec->shdr.sh_flags & SHF_ALLOC))
if (isec && isec->is_alive && (isec->shdr().sh_flags & SHF_ALLOC))
isec->scan_relocations(ctx);
// Scan relocations against exception frames
@@ -1048,6 +1040,24 @@ void ObjectFile<E>::scan_relocations(Context<E> &ctx) {
}
}
// Common symbols are used by C's tantative definitions. Tentative
// definition is an obscure C feature which allows users to omit `extern`
// from global variable declarations in a header file. For example, if you
// have a tentative definition `int foo;` in a header which is included
// into multiple translation units, `foo` will be included into multiple
// object files, but it won't cause the duplicate symbol error. Instead,
// the linker will merge them into a single instance of `foo`.
//
// If a header file contains a tentative definition `int foo;` and one of
// a C file contains a definition with initial value such as `int foo = 5;`,
// then the "real" definition wins. The symbol for the tentative definition
// will be resolved to the real definition. If there is no "real"
// 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.
template <typename E>
void ObjectFile<E>::convert_common_symbols(Context<E> &ctx) {
if (!has_common_symbol)
@@ -1062,28 +1072,30 @@ void ObjectFile<E>::convert_common_symbols(Context<E> &ctx) {
continue;
Symbol<E> &sym = *this->symbols[i];
std::scoped_lock lock(sym.mu);
if (sym.file != this) {
if (ctx.arg.warn_common)
Warn(ctx) << *this << ": multiple common symbols: " << sym;
continue;
}
auto *shdr = new ElfShdr<E>;
ctx.shdr_pool.push_back(std::unique_ptr<ElfShdr<E>>(shdr));
elf_sections2.push_back({});
ElfShdr<E> &shdr = elf_sections2.back();
memset(shdr, 0, sizeof(*shdr));
shdr->sh_flags = 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;
memset(&shdr, 0, sizeof(shdr));
shdr.sh_flags = 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, *shdr, ".common",
std::string_view(), sections.size());
std::make_unique<InputSection<E>>(ctx, *this, ".common", idx);
isec->output_section = osec;
sym.file = this;
sym.input_section = isec.get();
sym.shndx = idx;
sym.value = 0;
sym.sym_idx = i;
sym.ver_idx = ctx.default_version;
@@ -1096,36 +1108,53 @@ void ObjectFile<E>::convert_common_symbols(Context<E> &ctx) {
}
template <typename E>
static bool should_write_to_global_symtab(Symbol<E> &sym) {
return sym.get_type() != STT_SECTION && sym.is_alive();
static bool should_write_to_local_symtab(Context<E> &ctx, Symbol<E> &sym) {
if (sym.get_type() == STT_SECTION)
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
// know the rationale. Anyway, this is the behavior of the
// traditional linkers.
if (sym.name().starts_with(".L")) {
if (ctx.arg.discard_locals)
return false;
if (InputSection<E> *isec = sym.get_input_section())
if (isec->shdr().sh_flags & SHF_MERGE)
return false;
}
return true;
}
template <typename E>
void ObjectFile<E>::compute_symtab(Context<E> &ctx) {
if (ctx.arg.retain_symbols_file) {
std::span<Symbol<E> *> syms(this->symbols);
for (Symbol<E> *sym : syms.subspan(this->first_global)) {
if (sym->file == this && sym->write_to_symtab) {
strtab_size += sym->name().size() + 1;
num_global_symtab++;
}
}
return;
}
if (ctx.arg.strip_all)
return;
if (ctx.arg.gc_sections && !ctx.arg.discard_all) {
// Detect symbols pointing to sections discarded by -gc-sections
// to not copy them to symtab.
auto is_alive = [&](Symbol<E> &sym) -> bool {
if (!ctx.arg.gc_sections)
return true;
if (SectionFragment<E> *frag = sym.get_frag())
return frag->is_alive;
if (InputSection<E> *isec = sym.get_input_section())
return isec->is_alive;
return true;
};
// Compute the size of local symbols
if (!ctx.arg.discard_all && !ctx.arg.strip_all && !ctx.arg.retain_symbols_file) {
for (i64 i = 1; i < this->first_global; i++) {
Symbol<E> &sym = *this->symbols[i];
if (sym.write_to_symtab && !sym.is_alive()) {
strtab_size -= sym.name().size() + 1;
num_local_symtab--;
sym.write_to_symtab = false;
if (is_alive(sym) && should_write_to_local_symtab(ctx, sym)) {
this->strtab_size += sym.name().size() + 1;
this->num_local_symtab++;
sym.write_to_symtab = true;
}
}
}
@@ -1134,24 +1163,25 @@ void ObjectFile<E>::compute_symtab(Context<E> &ctx) {
for (i64 i = this->first_global; i < this->symbols.size(); i++) {
Symbol<E> &sym = *this->symbols[i];
if (sym.file == this && should_write_to_global_symtab(sym)) {
strtab_size += sym.name().size() + 1;
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++;
sym.write_to_symtab = true;
num_global_symtab++;
}
}
}
template <typename E>
void ObjectFile<E>::write_symtab(Context<E> &ctx) {
u8 *symtab_base = ctx.buf + ctx.symtab->shdr.sh_offset;
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 = strtab_offset;
i64 symtab_off;
i64 strtab_off = this->strtab_offset;
auto write_sym = [&](Symbol<E> &sym) {
ElfSym<E> &esym = *(ElfSym<E> *)(symtab_base + symtab_off);
symtab_off += sizeof(esym);
ElfSym<E> &esym = symtab_base[symtab_idx++];
esym = sym.esym();
esym.st_name = strtab_off;
@@ -1161,10 +1191,10 @@ void ObjectFile<E>::write_symtab(Context<E> &ctx) {
else
esym.st_value = sym.get_addr(ctx);
if (sym.input_section)
esym.st_shndx = sym.input_section->output_section->shndx;
else if (sym.shndx)
esym.st_shndx = sym.shndx;
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
@@ -1174,14 +1204,14 @@ void ObjectFile<E>::write_symtab(Context<E> &ctx) {
strtab_off += sym.name().size() + 1;
};
symtab_off = local_symtab_offset;
symtab_idx = this->local_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);
}
symtab_off = global_symtab_offset;
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)
@@ -1214,7 +1244,7 @@ template <typename E>
SharedFile<E> *
SharedFile<E>::create(Context<E> &ctx, MappedFile<Context<E>> *mf) {
SharedFile<E> *obj = new SharedFile(ctx, mf);
ctx.dso_pool.push_back(std::unique_ptr<SharedFile<E>>(obj));
ctx.dso_pool.emplace_back(obj);
return obj;
}
@@ -1325,7 +1355,7 @@ void SharedFile<E>::resolve_symbols(Context<E> &ctx) {
if (get_rank(this, esym, false) < get_rank(sym)) {
sym.file = this;
sym.input_section = nullptr;
sym.shndx = 0;
sym.value = esym.st_value;
sym.sym_idx = i;
sym.ver_idx = versyms[i];
@@ -1371,17 +1401,60 @@ std::vector<Symbol<E> *> SharedFile<E>::find_aliases(Symbol<E> *sym) {
template <typename E>
bool SharedFile<E>::is_readonly(Context<E> &ctx, Symbol<E> *sym) {
ElfEhdr<E> *ehdr = (ElfEhdr<E> *)this->mf->data;
ElfPhdr<E> *phdr = (ElfPhdr<E> *)(this->mf->data + ehdr->e_phoff);
ElfPhdr<E> *phdr = this->get_phdr();
u64 val = sym->esym().st_value;
for (i64 i = 0; i < ehdr->e_phnum; i++)
for (i64 i = 0; i < this->get_ehdr().e_phnum; i++)
if (phdr[i].p_type == PT_LOAD && !(phdr[i].p_flags & PF_W) &&
phdr[i].p_vaddr <= val && val < phdr[i].p_vaddr + phdr[i].p_memsz)
return true;
return false;
}
template <typename E>
void SharedFile<E>::compute_symtab(Context<E> &ctx) {
if (ctx.arg.strip_all)
return;
// Compute the size of global symbols.
for (i64 i = this->first_global; i < this->symbols.size(); i++) {
Symbol<E> &sym = *this->symbols[i];
if (sym.file == this && (sym.is_imported || sym.is_exported) &&
(!ctx.arg.retain_symbols_file || sym.write_to_symtab)) {
this->strtab_size += sym.name().size() + 1;
this->num_global_symtab++;
sym.write_to_symtab = true;
}
}
}
template <typename E>
void SharedFile<E>::write_symtab(Context<E> &ctx) {
ElfSym<E> *symtab =
(ElfSym<E> *)(ctx.buf + ctx.symtab->shdr.sh_offset) + this->global_symtab_idx;
u8 *strtab = ctx.buf + ctx.strtab->shdr.sh_offset + this->strtab_offset;
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)
continue;
ElfSym<E> &esym = *symtab++;
esym.st_name = strtab - (ctx.buf + ctx.strtab->shdr.sh_offset);
esym.st_value = 0;
esym.st_size = 0;
esym.st_type = STT_NOTYPE;
esym.st_bind = STB_GLOBAL;
esym.st_visibility = sym.visibility;
esym.st_shndx = SHN_UNDEF;
write_string(strtab, sym.name());
strtab += sym.name().size() + 1;
}
}
#define INSTANTIATE(E) \
template class ObjectFile<E>; \
template class SharedFile<E>; \
@@ -1390,5 +1463,7 @@ bool SharedFile<E>::is_readonly(Context<E> &ctx, Symbol<E> *sym) {
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+126 -30
View File
@@ -1,6 +1,7 @@
#include "mold.h"
#include <limits>
#include <zlib.h>
namespace mold::elf {
@@ -24,29 +25,83 @@ bool CieRecord<E>::equals(const CieRecord<E> &other) const {
return true;
}
template <typename E>
InputSection<E>::InputSection(Context<E> &ctx, ObjectFile<E> &file,
const ElfShdr<E> &shdr, std::string_view name,
std::string_view contents, i64 section_idx)
: file(file), shdr(shdr), contents(contents), nameptr(name.data()),
namelen(name.size()), section_idx(section_idx) {
output_section =
OutputSection<E>::get_instance(ctx, name, shdr.sh_type, shdr.sh_flags);
static inline i64 to_p2align(u64 alignment) {
if (alignment == 0)
return 0;
return std::countr_zero(alignment);
}
template <typename E>
void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
if (shdr.sh_type == SHT_NOBITS || shdr.sh_size == 0)
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;
if (name.starts_with(".zdebug")) {
sh_size = *(ubig64 *)&contents[4];
p2align = to_p2align(shdr().sh_addralign);
compressed = true;
} else if (shdr().sh_flags & SHF_COMPRESSED) {
ElfChdr<E> &chdr = *(ElfChdr<E> *)&contents[0];
sh_size = chdr.ch_size;
p2align = to_p2align(chdr.ch_addralign);
compressed = true;
} else {
sh_size = shdr().sh_size;
p2align = to_p2align(shdr().sh_addralign);
compressed = false;
}
// Uncompress early if the relocation is REL-type so that we can read
// addends from section contents. If RELA-type, we don't need to do this
// because addends are in relocations.
if (compressed && E::is_rel) {
u8 *buf = new u8[sh_size];
uncompress(ctx, buf);
contents = {(char *)buf, sh_size};
ctx.string_pool.emplace_back(buf);
}
output_section =
OutputSection<E>::get_instance(ctx, name, shdr().sh_type, shdr().sh_flags);
}
template <typename E>
bool InputSection<E>::is_compressed() {
return !E::is_rel &&
(name().starts_with(".zdebug") || (shdr().sh_flags & SHF_COMPRESSED));
}
template <typename E>
void InputSection<E>::uncompress(Context<E> &ctx, u8 *buf) {
auto do_uncompress = [&](std::string_view data) {
unsigned long size = sh_size;
if (::uncompress(buf, &size, (u8 *)data.data(), data.size()) != Z_OK)
Fatal(ctx) << *this << ": uncompress failed";
assert(size == sh_size);
};
if (name().starts_with(".zdebug")) {
// Old-style compressed section
if (!contents.starts_with("ZLIB") || contents.size() <= 12)
Fatal(ctx) << *this << ": corrupted compressed section";
do_uncompress(contents.substr(12));
return;
}
// Copy data
memcpy(buf, contents.data(), contents.size());
assert(shdr().sh_flags & SHF_COMPRESSED);
// Apply relocations
if (shdr.sh_flags & SHF_ALLOC)
apply_reloc_alloc(ctx, buf);
else
apply_reloc_nonalloc(ctx, buf);
// New-style compressed section
if (contents.size() < sizeof(ElfChdr<E>))
Fatal(ctx) << *this << ": corrupted compressed section";
ElfChdr<E> &hdr = *(ElfChdr<E> *)&contents[0];
if (hdr.ch_type != ELFCOMPRESS_ZLIB)
Fatal(ctx) << *this << ": unsupported compression type";
do_uncompress(contents.substr(sizeof(ElfChdr<E>)));
}
template <typename E>
@@ -73,12 +128,19 @@ 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_code = (shdr.sh_flags & SHF_EXECINSTR);
bool is_writable = (shdr.sh_flags & SHF_WRITE);
bool is_writable = (shdr().sh_flags & SHF_WRITE);
auto error = [&]() {
Error(ctx) << *this << ": " << rel << " relocation against symbol `"
<< sym << "' can not be used; recompile with -fPIC";
auto error = [&] {
std::string msg = sym.is_absolute() ? "-fno-PIC" : "-fPIC";
Error(ctx) << *this << ": " << rel << " relocation at offset 0x"
<< std::hex << rel.r_offset << " against symbol `"
<< sym << "' can not be used; recompile with " << msg;
};
auto warn_textrel = [&] {
if (ctx.arg.warn_textrel)
Warn(ctx) << *this << ": relocation against symbol `" << sym
<< "' in read-only section";
};
switch (action) {
@@ -106,29 +168,35 @@ void InputSection<E>::dispatch(Context<E> &ctx, Action table[3][4], i64 i,
case PLT:
sym.flags |= NEEDS_PLT;
return;
case CPLT: {
std::scoped_lock lock(sym.mu);
sym.flags |= NEEDS_PLT;
sym.is_canonical = true;
return;
}
case DYNREL:
if (!is_writable) {
if (!is_code || ctx.arg.z_text) {
if (ctx.arg.z_text) {
error();
return;
}
warn_textrel();
ctx.has_textrel = true;
}
assert(sym.is_imported);
needs_dynrel[i] = true;
file.num_dynrel++;
return;
case BASEREL:
if (!is_writable) {
if (!is_code || ctx.arg.z_text) {
if (ctx.arg.z_text) {
error();
return;
}
warn_textrel();
ctx.has_textrel = true;
}
needs_baserel[i] = true;
if (!is_relr_reloc(ctx, rel))
file.num_dynrel++;
return;
@@ -138,7 +206,31 @@ void InputSection<E>::dispatch(Context<E> &ctx, Action table[3][4], i64 i,
}
template <typename E>
void InputSection<E>::report_undef(Context<E> &ctx, Symbol<E> &sym) {
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>) {
copy_contents_riscv(ctx, buf);
} else if (is_compressed()) {
uncompress(ctx, buf);
} else {
memcpy(buf, contents.data(), contents.size());
}
// Apply relocations
if (shdr().sh_flags & SHF_ALLOC)
apply_reloc_alloc(ctx, buf);
else
apply_reloc_nonalloc(ctx, buf);
}
template <typename E>
void report_undef(Context<E> &ctx, InputFile<E> &file, Symbol<E> &sym) {
if (ctx.arg.warn_once && !ctx.warned.insert({(void *)&sym, 1}))
return;
switch (ctx.arg.unresolved_symbols) {
case UNRESOLVED_ERROR:
Error(ctx) << "undefined symbol: " << file << ": " << sym;
@@ -151,12 +243,16 @@ void InputSection<E>::report_undef(Context<E> &ctx, Symbol<E> &sym) {
}
}
#define INSTANTIATE(E) \
template struct CieRecord<E>; \
template class InputSection<E>;
#define INSTANTIATE(E) \
template struct CieRecord<E>; \
template class InputSection<E>; \
template void report_undef(Context<E> &, InputFile<E> &, Symbol<E> &)
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+2
View File
@@ -408,5 +408,7 @@ void parse_dynamic_list(Context<E> &ctx, std::string path) {
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+661
View File
@@ -0,0 +1,661 @@
// This file handles the linker plugin to support LTO (Link-Time
// Optimization).
//
// LTO is a technique to do whole-program optimization to a program. Since
// a linker sees the whole program as opposed to a single compilation
// unit, it in theory can do some optimizations that cannot be done in the
// usual separate compilation model. For example, LTO should be able to
// inline functions that are defined in other compilation unit.
//
// In GCC and Clang, all you have to do to enable LTO is adding the
// `-flto` flag to the compiler and the linker command lines. If `-flto`
// is given, the compiler generates a file that contains not machine code
// but the compiler's IR (intermediate representation). In GCC, the output
// is an ELF file which wraps GCC's IR. In LLVM, it's not even an ELF file
// but just a raw LLVM IR file.
//
// Here is what we have to do if at least one input file is not a usual
// ELF file but an IR object file:
//
// 1. Read symbols both from usual ELF files and from IR object files and
// resolve symbols as usual.
//
// 2. Pass all IR objects to the compiler backend. The compiler backend
// compiles the IRs and returns a few big ELF object files as a
// result.
//
// 3. Parse the returned ELF files and overwrite IR object symbols with
// the returned ones, discarding IR object files.
//
// 4. Continue the rest of the linking process as usual.
//
// When gcc or clang inovkes ld, they pass `-plugin linker-plugin.so` to
// the linker. The given .so file provides a way to call the compiler
// backend.
//
// The linker plugin API is documented at
// https://gcc.gnu.org/wiki/whopr/driver, though the document is a bit
// outdated.
//
// Frankly, the linker plugin API is peculiar and is not very easy to use.
// For some reason, the API functions don't return the result of a
// function call as a return value but instead calls other function with
// the result as its argument to "return" the result.
//
// For example, the first thing you need to do after dlopen()'ing a linker
// plugin .so is to call `onload` function with a list of callback
// functions. `onload` calls callbacks to notify about the pointers to
// other functions the linker plugin provides. I don't know why `onload`
// can't just return a list of functions or why the linker plugin can't
// define not only `onload` but other functions, but that's how it works.
//
// Here is the steps to use the linker plugin:
//
// 1. dlopen() the linker plugin .so and call `onload` to obtain pointers
// to other functions provided by the plugin.
//
// 2. Call `claim_file_hook` with an IR object file to read its symbol
// table. `claim_file_hook` calls the `add_symbols` callback to
// "return" a list of symbols.
//
// 3. `claim_file_hook` returns LDPT_OK only when the plugin wants to
// handle a given file. Since we pass only IR object files to the
// plugin in mold, it always returns LDPT_OK in our case.
//
// 4. Once we made a decision as to which object file to include into the
// output file, we call `all_symbols_read_hook` to compile IR objects
// into a few big ELF files. That function calls the `get_symbols`
// callback to ask us about the symbol resolution results. (The
// compiler backend needs to know whether an undefined symbol in an IR
// object was resolved to a regular object file or a shared object to
// do whole program optimization, for example.)
//
// 5. `all_symbols_read_hook` "returns" the result by calling the
// `add_input_file` callback. The callback is called with a path to an
// LTO'ed ELF file. We parse that ELF file and override symbols
// defined by IR objects with the ELF file's ones.
//
// 6. Lastly, we call `cleanup_hook` to remove temporary files created by
// the compiler backend.
#include "mold.h"
#include "../lto.h"
#include <cstdarg>
#include <cstring>
#include <dlfcn.h>
#include <fcntl.h>
#include <sstream>
#include <unistd.h>
#if 0
# define LOG std::cerr
#else
# define LOG std::ostringstream()
#endif
namespace mold::elf {
// Global variables
// We store LTO-related information to global variables,
// as the LTO plugin is not thread-safe by design anyway.
template <typename E> static Context<E> *gctx;
template <typename E> static std::vector<ObjectFile<E> *> lto_objects;
static int phase = 0;
static std::vector<PluginSymbol> plugin_symbols;
static ClaimFileHandler *claim_file_hook;
static AllSymbolsReadHandler *all_symbols_read_hook;
static CleanupHandler *cleanup_hook;
// Event handlers
static PluginStatus message(int level, const char *fmt, ...) {
LOG << "message\n";
va_list ap;
va_start(ap, fmt);
fprintf(stderr, "mold: ");
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
return LDPS_OK;
}
template <typename E>
static PluginStatus register_claim_file_hook(ClaimFileHandler fn) {
LOG << "register_claim_file_hook\n";
claim_file_hook = fn;
return LDPS_OK;
}
template <typename E>
static PluginStatus
register_all_symbols_read_hook(AllSymbolsReadHandler fn) {
LOG << "register_all_symbols_read_hook\n";
all_symbols_read_hook = fn;
return LDPS_OK;
}
template <typename E>
static PluginStatus register_cleanup_hook(CleanupHandler fn) {
LOG << "register_cleanup_hook\n";
cleanup_hook = fn;
return LDPS_OK;
}
static PluginStatus
add_symbols(void *handle, int nsyms, const PluginSymbol *psyms) {
LOG << "add_symbols: " << nsyms << "\n";
assert(phase == 1);
plugin_symbols = {psyms, psyms + nsyms};
return LDPS_OK;
}
template <typename E>
static PluginStatus add_input_file(const char *path) {
LOG << "add_input_file: " << path << "\n";
Context<E> &ctx = *gctx<E>;
static i64 file_priority = 100;
MappedFile<Context<E>> *mf = MappedFile<Context<E>>::must_open(ctx, path);
ObjectFile<E> *file = ObjectFile<E>::create(ctx, mf, "", false);
ctx.obj_pool.emplace_back(file);
lto_objects<E>.push_back(file);
file->priority = file_priority++;
file->is_alive = true;
file->parse(ctx);
file->resolve_symbols(ctx);
return LDPS_OK;
}
static PluginStatus
get_input_file(const void *handle, struct PluginInputFile *file) {
LOG << "get_input_file\n";
return LDPS_OK;
}
template <typename E>
static PluginStatus release_input_file(const void *handle) {
LOG << "release_input_file\n";
ObjectFile<E> &file = *(ObjectFile<E> *)handle;
if (file.mf->fd != -1) {
close(file.mf->fd);
file.mf->fd = -1;
}
return LDPS_OK;
}
static PluginStatus add_input_library(const char *path) {
LOG << "add_input_library\n";
return LDPS_OK;
}
static PluginStatus set_extra_library_path(const char *path) {
LOG << "set_extra_library_path\n";
return LDPS_OK;
}
template <typename E>
static PluginStatus get_view(const void *handle, const void **view) {
LOG << "get_view\n";
ObjectFile<E> &file = *(ObjectFile<E> *)handle;
*view = (void *)file.mf->data;
return LDPS_OK;
}
static PluginStatus
get_input_section_count(const void *handle, int *count) {
LOG << "get_input_section_count\n";
return LDPS_OK;
}
static PluginStatus
get_input_section_type(const PluginSection section, int *type) {
LOG << "get_input_section_type\n";
return LDPS_OK;
}
static PluginStatus
get_input_section_name(const PluginSection section,
char **section_name) {
LOG << "get_input_section_name\n";
return LDPS_OK;
}
static PluginStatus
get_input_section_contents(const PluginSection section,
const char **section_contents,
size_t *len) {
LOG << "get_input_section_contents\n";
return LDPS_OK;
}
static PluginStatus
update_section_order(const PluginSection *section_list,
int num_sections) {
LOG << "update_section_order\n";
return LDPS_OK;
}
static PluginStatus allow_section_ordering() {
LOG << "allow_section_ordering\n";
return LDPS_OK;
}
static PluginStatus
get_symbols_v1(const void *handle, int nsyms, PluginSymbol *psyms) {
unreachable();
}
// get_symbols teaches the LTO plugin as to how we resolved symbols.
// The plugin uses the symbol resolution info to optimizes the program.
//
// For example, if a definition in an IR file is not referenced by
// non-IR objects at all, the plugin may choose to completely inline
// that definition within the IR objects and remove the symbol from the
// LTO result. On the other hand, if a definition is referenced by a
// non-IR object, it has to keep the symbol in the LTO result.
template <typename E>
static PluginStatus
get_symbols(const void *handle, int nsyms, PluginSymbol *psyms, bool is_v2) {
ObjectFile<E> &file = *(ObjectFile<E> *)handle;
assert(file.is_lto_obj);
// If file is an archive member which was not chose to be included in
// to the final result, we need to make the plugin to ignore all
// symbols.
if (!file.is_alive) {
assert(!is_v2);
for (int i = 0; i < nsyms; i++)
psyms[i].resolution = LDPR_PREEMPTED_REG;
return LDPS_NO_SYMS;
}
auto get_resolution = [&](ElfSym<E> &esym, Symbol<E> &sym) {
if (!sym.file)
return LDPR_UNDEF;
if (sym.file == &file) {
if (sym.referenced_by_regular_obj)
return LDPR_PREVAILING_DEF;
if (sym.is_exported)
return is_v2 ? LDPR_PREVAILING_DEF : LDPR_PREVAILING_DEF_IRONLY_EXP;
return LDPR_PREVAILING_DEF_IRONLY;
}
if (sym.file->is_dso)
return LDPR_RESOLVED_DYN;
if (((ObjectFile<E> *)sym.file)->is_lto_obj)
return esym.is_undef() ? LDPR_RESOLVED_IR : LDPR_PREEMPTED_IR;
return esym.is_undef() ? LDPR_RESOLVED_EXEC : LDPR_PREEMPTED_REG;
};
// Set the symbol resolution results to psyms.
for (i64 i = 0; i < nsyms; i++) {
ElfSym<E> &esym = file.elf_syms[i + 1];
Symbol<E> &sym = *file.symbols[i + 1];
psyms[i].resolution = get_resolution(esym, sym);
}
return LDPS_OK;
}
template <typename E>
static PluginStatus
get_symbols_v2(const void *handle, int nsyms, PluginSymbol *psyms) {
LOG << "get_symbols_v2\n";
return get_symbols<E>(handle, nsyms, psyms, true);
}
template <typename E>
static PluginStatus
get_symbols_v3(const void *handle, int nsyms, PluginSymbol *psyms) {
LOG << "get_symbols_v3\n";
return get_symbols<E>(handle, nsyms, psyms, false);
}
static PluginStatus allow_unique_segment_for_sections() {
LOG << "allow_unique_segment_for_sections\n";
return LDPS_OK;
}
static PluginStatus
unique_segment_for_sections(const char *segment_name,
uint64_t flags,
uint64_t align,
const PluginSection *section_list,
int num_sections) {
LOG << "unique_segment_for_sections\n";
return LDPS_OK;
}
static PluginStatus
get_input_section_alignment(const PluginSection section,
int *addralign) {
LOG << "get_input_section_alignment\n";
return LDPS_OK;
}
static PluginStatus
get_input_section_size(const PluginSection section, uint64_t *size) {
LOG << "get_input_section_size\n";
return LDPS_OK;
}
template <typename E>
static PluginStatus
register_new_input_hook(NewInputHandler fn) {
LOG << "register_new_input_hook\n";
return LDPS_OK;
}
static PluginStatus
get_wrap_symbols(uint64_t *num_symbols, const char ***wrap_symbols) {
LOG << "get_wrap_symbols\n";
return LDPS_OK;
}
template <typename E>
static void load_plugin(Context<E> &ctx) {
assert(phase == 0);
phase = 1;
gctx<E> = &ctx;
void *handle = dlopen(ctx.arg.plugin.c_str(), RTLD_NOW | RTLD_GLOBAL);
if (!handle)
Fatal(ctx) << "could not open plugin file: " << dlerror();
OnloadFn *onload = (OnloadFn *)dlsym(handle, "onload");
if (!onload)
Fatal(ctx) << "failed to load plugin " << ctx.arg.plugin << ": "
<< dlerror();
auto save = [&](std::string_view str) {
return save_string(ctx, std::string(str).c_str()).data();
};
std::vector<PluginTagValue> tv;
tv.emplace_back(LDPT_MESSAGE, message);
if (ctx.arg.shared)
tv.emplace_back(LDPT_LINKER_OUTPUT, LDPO_DYN);
else if (ctx.arg.pie)
tv.emplace_back(LDPT_LINKER_OUTPUT, LDPO_PIE);
else
tv.emplace_back(LDPT_LINKER_OUTPUT, LDPO_EXEC);
for (std::string_view opt : ctx.arg.plugin_opt)
tv.emplace_back(LDPT_OPTION, save(opt));
tv.emplace_back(LDPT_REGISTER_CLAIM_FILE_HOOK, register_claim_file_hook<E>);
tv.emplace_back(LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK,
register_all_symbols_read_hook<E>);
tv.emplace_back(LDPT_REGISTER_CLEANUP_HOOK, register_cleanup_hook<E>);
tv.emplace_back(LDPT_ADD_SYMBOLS, add_symbols);
tv.emplace_back(LDPT_GET_SYMBOLS, get_symbols_v1);
tv.emplace_back(LDPT_ADD_INPUT_FILE, add_input_file<E>);
tv.emplace_back(LDPT_GET_INPUT_FILE, get_input_file);
tv.emplace_back(LDPT_RELEASE_INPUT_FILE, release_input_file<E>);
tv.emplace_back(LDPT_ADD_INPUT_LIBRARY, add_input_library);
tv.emplace_back(LDPT_OUTPUT_NAME, save(ctx.arg.output));
tv.emplace_back(LDPT_SET_EXTRA_LIBRARY_PATH, set_extra_library_path);
tv.emplace_back(LDPT_GET_VIEW, get_view<E>);
tv.emplace_back(LDPT_GET_INPUT_SECTION_COUNT, get_input_section_count);
tv.emplace_back(LDPT_GET_INPUT_SECTION_TYPE, get_input_section_type);
tv.emplace_back(LDPT_GET_INPUT_SECTION_NAME, get_input_section_name);
tv.emplace_back(LDPT_GET_INPUT_SECTION_CONTENTS, get_input_section_contents);
tv.emplace_back(LDPT_UPDATE_SECTION_ORDER, update_section_order);
tv.emplace_back(LDPT_ALLOW_SECTION_ORDERING, allow_section_ordering);
tv.emplace_back(LDPT_GET_SYMBOLS_V2, get_symbols_v2<E>);
tv.emplace_back(LDPT_ALLOW_UNIQUE_SEGMENT_FOR_SECTIONS,
allow_unique_segment_for_sections);
tv.emplace_back(LDPT_UNIQUE_SEGMENT_FOR_SECTIONS, unique_segment_for_sections);
tv.emplace_back(LDPT_GET_SYMBOLS_V3, get_symbols_v3<E>);
tv.emplace_back(LDPT_GET_INPUT_SECTION_ALIGNMENT, get_input_section_alignment);
tv.emplace_back(LDPT_GET_INPUT_SECTION_SIZE, get_input_section_size);
tv.emplace_back(LDPT_REGISTER_NEW_INPUT_HOOK, register_new_input_hook<E>);
tv.emplace_back(LDPT_GET_WRAP_SYMBOLS, get_wrap_symbols);
tv.emplace_back(LDPT_NULL, 0);
onload(tv.data());
}
template <typename E>
static ElfSym<E> to_elf_sym(PluginSymbol &psym) {
ElfSym<E> esym = {};
switch (psym.def) {
case LDPK_DEF:
esym.st_shndx = SHN_ABS;
break;
case LDPK_WEAKDEF:
esym.st_shndx = SHN_ABS;
esym.st_bind = STB_WEAK;
break;
case LDPK_UNDEF:
esym.st_shndx = SHN_UNDEF;
break;
case LDPK_WEAKUNDEF:
esym.st_shndx = SHN_UNDEF;
esym.st_bind = STB_WEAK;
break;
case LDPK_COMMON:
esym.st_shndx = SHN_COMMON;
break;
}
switch (psym.symbol_type) {
case LDST_UNKNOWN:
break;
case LDST_FUNCTION:
esym.st_type = STT_FUNC;
break;
case LDST_VARIABLE:
esym.st_type = STT_OBJECT;
break;
};
switch (psym.visibility) {
case LDPV_DEFAULT:
break;
case LDPV_PROTECTED:
esym.st_visibility = STV_PROTECTED;
break;
case LDPV_INTERNAL:
esym.st_visibility = STV_INTERNAL;
break;
case LDPV_HIDDEN:
esym.st_visibility = STV_HIDDEN;
break;
}
esym.st_size = psym.size;
return esym;
}
// Returns true if a given linker plugin looks like LLVM's one.
// Returns false if it's GCC.
template <typename E>
static bool is_llvm(Context<E> &ctx) {
return ctx.arg.plugin.ends_with("LLVMgold.so");
}
// Returns true if a given linker plugin supports the get_symbols_v3 API.
// Currently, we simply assume that LLVM supports it and GCC does not.
template <typename E>
static bool suppots_v3_api(Context<E> &ctx) {
return is_llvm(ctx);
}
template <typename E>
ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
LOG << "read_lto_object: " << mf->name << "\n";
if (ctx.arg.plugin.empty())
Fatal(ctx) << mf->name << ": don't know how to handle this LTO object file "
<< "because no -plugin option was given. Please make sure you "
<< "added -flto not only for creating object files but also for "
<< "creating the final executable.";
// dlopen the linker plugin file
static std::once_flag flag;
std::call_once(flag, [&] { load_plugin(ctx); });
// Create mold's object instance
ObjectFile<E> *obj = new ObjectFile<E>;
ctx.obj_pool.emplace_back(obj);
obj->filename = mf->name;
obj->symbols.push_back(new Symbol<E>);
obj->first_global = 1;
obj->is_lto_obj = true;
obj->mf = mf;
// Create plugin's object instance
PluginInputFile file = {};
MappedFile<Context<E>> *mf2 = mf->parent ? mf->parent : mf;
file.name = save_string(ctx, mf2->name).data();
if (mf2->fd == -1)
mf2->fd = open(file.name, O_RDONLY);
file.fd = mf2->fd;
if (file.fd == -1)
Fatal(ctx) << "cannot open " << file.name << ": " << errno_string();
if (mf->parent)
obj->archive_name = mf->parent->name;
file.offset = mf->get_offset();
file.filesize = mf->size;
file.handle = (void *)obj;
LOG << "read_lto_symbols: "<< mf->name << "\n";
// claim_file_hook() calls add_symbols() which initializes `plugin_symbols`
int claimed = false;
claim_file_hook(&file, &claimed);
if (!claimed)
Fatal(ctx) << mf->name << ": not claimed by the LTO plugin";
// It looks like GCC doesn't need fd after claim_file_hook() while
// LLVM needs it and takes the ownership of fd. To prevent "too many
// open files" issue, we close fd only for GCC. This is ugly, though.
if (!is_llvm(ctx)) {
close(mf2->fd);
mf2->fd = -1;
}
// Initialize object symbols
std::vector<ElfSym<E>> *esyms = new std::vector<ElfSym<E>>(1);
for (PluginSymbol &psym : plugin_symbols) {
esyms->push_back(to_elf_sym<E>(psym));
obj->symbols.push_back(get_symbol(ctx, save_string(ctx, psym.name)));
}
obj->elf_syms = *esyms;
obj->sym_fragments.resize(esyms->size());
obj->symvers.resize(esyms->size());
plugin_symbols.clear();
return obj;
}
// This function restarts mold itself with `--:lto-pass2` and
// `--:ignore-ir-file` flags. We do this as a workaround for the old
// linker plugins that do not support the get_symbols_v3 API.
//
// get_symbols_v1 and get_symbols_v2 don't provide a way to ignore an
// object file we previously passed to the linker plugin. So we can't
// "unload" object files in archives that we ended up not choosing to
// include into the final output.
//
// As a workaround, we restart the linker with a list of object files
// the linker has to ignore, so that it won't read the object files
// from archives next time.
//
// This is an ugly hack and should be removed once GCC adopts the v3 API.
template <typename E>
static void restart_process(Context<E> &ctx) {
std::vector<const char *> args;
for (std::string_view arg : ctx.cmdline_args)
args.push_back(strdup(std::string(arg).c_str()));
for (std::unique_ptr<ObjectFile<E>> &file : ctx.obj_pool)
if (file->is_lto_obj && !file->is_alive)
args.push_back(strdup(("--:ignore-ir-file=" +
file->mf->get_identifier()).c_str()));
args.push_back("--:lto-pass2");
args.push_back(nullptr);
std::string self = std::filesystem::read_symlink("/proc/self/exe");
std::cout << std::flush;
std::cerr << std::flush;
execv(self.c_str(), (char * const *)args.data());
std::cerr << "execv failed: " << errno_string() << "\n";
_exit(1);
}
// Entry point
template <typename E>
std::vector<ObjectFile<E> *> do_lto(Context<E> &ctx) {
Timer t(ctx, "do_lto");
if (!ctx.arg.lto_pass2 && !suppots_v3_api(ctx))
restart_process(ctx);
assert(phase == 1);
phase = 2;
// Set `referenced_by_regular_obj` bit.
for (ObjectFile<E> *file : ctx.objs) {
if (file->is_lto_obj)
continue;
for (i64 i = file->first_global; i < file->symbols.size(); i++) {
ElfSym<E> &esym = file->elf_syms[i];
Symbol<E> &sym = *file->symbols[i];
if (esym.is_undef() && sym.file && sym.file != file &&
!sym.file->is_dso && ((ObjectFile<E> *)sym.file)->is_lto_obj) {
std::scoped_lock lock(sym.mu);
sym.referenced_by_regular_obj = true;
}
}
}
// all_symbols_read_hook() calls add_input_file() and add_input_library()
LOG << "all symbols read\n";
if (PluginStatus st = all_symbols_read_hook(); st != LDPS_OK)
Fatal(ctx) << "LTO: all_symbols_read_hook returns " << st;
return lto_objects<E>;
}
template <typename E>
void lto_cleanup(Context<E> &ctx) {
Timer t(ctx, "lto_cleanup");
if (cleanup_hook)
cleanup_hook();
}
#define INSTANTIATE(E) \
template ObjectFile<E> * \
read_lto_object(Context<E> &, MappedFile<Context<E>> *); \
template std::vector<ObjectFile<E> *> do_lto(Context<E> &); \
template void lto_cleanup(Context<E> &)
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+122 -81
View File
@@ -28,7 +28,26 @@ static ObjectFile<E> *new_object_file(Context<E> &ctx, MappedFile<Context<E>> *m
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++;
ctx.tg.run([file, &ctx]() { file->parse(ctx); });
ctx.tg.run([file, &ctx] { file->parse(ctx); });
if (ctx.arg.trace)
SyncOut(ctx) << "trace: " << *file;
return file;
}
template <typename E>
static ObjectFile<E> *new_lto_obj(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::string archive_name) {
static Counter count("parsed_lto_objs");
count++;
if (ctx.arg.ignore_ir_file.count(mf->get_identifier()))
return new ObjectFile<E>;
ObjectFile<E> *file = read_lto_object(ctx, mf);
file->priority = ctx.file_priority++;
file->is_in_lib = ctx.in_lib || (!archive_name.empty() && !ctx.whole_archive);
file->is_alive = !file->is_in_lib;
ctx.has_lto_object = true;
if (ctx.arg.trace)
SyncOut(ctx) << "trace: " << *file;
return file;
@@ -42,7 +61,7 @@ new_shared_file(Context<E> &ctx, MappedFile<Context<E>> *mf) {
SharedFile<E> *file = SharedFile<E>::create(ctx, mf);
file->priority = ctx.file_priority++;
ctx.tg.run([file, &ctx]() { file->parse(ctx); });
ctx.tg.run([file, &ctx] { file->parse(ctx); });
if (ctx.arg.trace)
SyncOut(ctx) << "trace: " << *file;
return file;
@@ -64,18 +83,27 @@ void read_file(Context<E> &ctx, MappedFile<Context<E>> *mf) {
return;
case FileType::AR:
case FileType::THIN_AR:
for (MappedFile<Context<E>> *child : read_archive_members(ctx, mf))
if (get_file_type(child) == FileType::ELF_OBJ)
for (MappedFile<Context<E>> *child : read_archive_members(ctx, mf)) {
switch (get_file_type(child)) {
case FileType::ELF_OBJ:
ctx.objs.push_back(new_object_file(ctx, child, mf->name));
break;
case FileType::GCC_LTO_OBJ:
case FileType::LLVM_BITCODE:
ctx.objs.push_back(new_lto_obj(ctx, child, mf->name));
break;
default:
break;
}
}
ctx.visited.insert(mf->name);
return;
case FileType::TEXT:
parse_linker_script(ctx, mf);
return;
case FileType::GCC_LTO_OBJ:
case FileType::LLVM_BITCODE:
Warn(ctx) << mf->name << ": looks like this is an LLVM bitcode, "
<< "but mold does not support LTO";
ctx.llvm_lto = true;
ctx.objs.push_back(new_lto_obj(ctx, mf, ""));
return;
default:
Fatal(ctx) << mf->name << ": unknown file type: " << type;
@@ -89,6 +117,7 @@ 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))
@@ -275,7 +304,7 @@ static void show_stats(Context<E> &ctx) {
static Counter alloc("reloc_alloc");
static Counter nonalloc("reloc_nonalloc");
if (sec->shdr.sh_flags & SHF_ALLOC)
if (sec->shdr().sh_flags & SHF_ALLOC)
alloc += sec->get_rels(ctx).size();
else
nonalloc += sec->get_rels(ctx).size();
@@ -313,7 +342,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 (E::e_machine == EM_AARCH64) {
if constexpr (std::is_same_v<E, ARM64>) {
static Counter num_thunks("num_thunks");
for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections)
for (std::unique_ptr<RangeExtensionThunk<E>> &thunk : osec->thunks)
@@ -321,10 +350,13 @@ static void show_stats(Context<E> &ctx) {
}
Counter::print();
for (std::unique_ptr<MergedSection<E>> &sec : ctx.merged_sections)
sec->print_stats(ctx);
}
static i64 get_default_thread_count() {
// mold doesn't scale above 32 threads.
// 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);
@@ -355,6 +387,10 @@ static int elf_main(int argc, char **argv) {
return elf_main<I386>(argc, argv);
case EM_AARCH64:
return elf_main<ARM64>(argc, argv);
case EM_ARM:
return elf_main<ARM32>(argc, argv);
case EM_RISCV:
return elf_main<RISCV64>(argc, argv);
}
unreachable();
}
@@ -416,13 +452,6 @@ static int elf_main(int argc, char **argv) {
}
}
{
Timer t(ctx, "register_section_pieces");
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
file->register_section_pieces(ctx);
});
}
// Uniquify shared object files by soname
{
std::unordered_set<std::string_view> seen;
@@ -437,31 +466,12 @@ static int elf_main(int argc, char **argv) {
// Apply -exclude-libs
apply_exclude_libs(ctx);
// Create instances of linker-synthesized sections such as
// .got or .plt.
create_synthetic_sections(ctx);
// Resolve symbols and fix the set of object files that are
// included to the final output.
resolve_symbols(ctx);
// We currently do not natively support LTO.
// If LLVM LTO is in use, fallback to the lld linker by invoking
// it with the exact same command line arguments as we got.
if (ctx.llvm_lto) {
Warn(ctx) << "LLVM LTO is detected, so falling back to ld.lld";
argv[0] = (char *)"ld.lld";
execvp("ld.lld", argv);
Fatal(ctx) << "execvp failed: ld.lld: " << errno_string();
}
// Do the same for GCC LTO.
if (Symbol<E> *sym = get_symbol(ctx, "__gnu_lto_slim"); sym->file) {
Warn(ctx) << *sym->file
<< "GCC LTO is detected, so falling back to ld.bfd";
execvp("ld.bfd", argv);
Fatal(ctx) << "execvp failed: ld.bfd: " << errno_string();
}
// Resolve mergeable section pieces to merge them.
register_section_pieces(ctx);
// Remove redundant comdat sections (e.g. duplicate inline functions).
eliminate_comdats(ctx);
@@ -489,6 +499,10 @@ static int elf_main(int argc, char **argv) {
// Compute sizes of sections containing mergeable strings.
compute_merged_section_sizes(ctx);
// Create instances of linker-synthesized sections such as
// .got or .plt.
create_synthetic_sections(ctx);
// Bin input sections into output sections.
bin_sections(ctx);
@@ -498,7 +512,6 @@ static int elf_main(int argc, char **argv) {
// Create a dummy file containing linker-synthesized symbols
// (e.g. `__bss_start`).
ctx.internal_obj = create_internal_file(ctx);
ctx.internal_obj->resolve_symbols(ctx);
ctx.objs.push_back(ctx.internal_obj);
// Beyond this point, no new files will be added to ctx.objs
@@ -519,6 +532,10 @@ static int elf_main(int argc, char **argv) {
// Beyond this point, no new symbols will be added to the result.
// Handle -repro
if (ctx.arg.repro)
write_repro_file(ctx);
// Make sure that all symbols have been resolved.
if (!ctx.arg.allow_multiple_definition)
check_duplicate_symbols(ctx);
@@ -532,6 +549,14 @@ static int elf_main(int argc, char **argv) {
// a special rule. Sort them.
sort_init_fini(ctx);
// Likewise, .ctors and .dtors have to be sorted. They are rare
// because they are superceded by .init_array/.fini_array, though.
sort_ctor_dtor(ctx);
// Handle --shuffle-sections
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);
@@ -570,43 +595,41 @@ static int elf_main(int argc, char **argv) {
ctx.dynsym->finalize(ctx);
// Fill .gnu.version_d section contents.
ctx.verdef->construct(ctx);
if (ctx.verdef)
ctx.verdef->construct(ctx);
// Fill .gnu.version_r section contents.
ctx.verneed->construct(ctx);
// Compute .symtab and .strtab sizes for each file.
{
Timer t(ctx, "compute_symtab");
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
file->compute_symtab(ctx);
});
}
create_output_symtab(ctx);
// .eh_frame is a special section from the linker's point of view,
// as its contents are parsed and reconstructed by the linker,
// unlike other sections that are regarded as opaque bytes.
// Here, we transplant .eh_frame sections from a regular output
// section to the special EHFrameSection.
{
Timer t(ctx, "eh_frame");
std::erase_if(ctx.chunks, [](Chunk<E> *chunk) {
return chunk->kind == Chunk<E>::REGULAR && chunk->name == ".eh_frame";
});
ctx.eh_frame->construct(ctx);
}
// Here, we construct output .eh_frame contents.
ctx.eh_frame->construct(ctx);
// Update shdr.sh_size for each chunk and remove empty ones.
// Handle --gdb-index.
if (ctx.arg.gdb_index)
ctx.gdb_index->construct(ctx);
// If --emit-relocs is given, we'll copy relocation sections from input
// files to an output file.
if (ctx.arg.emit_relocs)
create_reloc_sections(ctx);
// Update sh_size for each chunk and remove empty ones.
for (Chunk<E> *chunk : ctx.chunks)
chunk->update_shdr(ctx);
std::erase_if(ctx.chunks, [](Chunk<E> *chunk) {
return chunk->kind == Chunk<E>::SYNTHETIC && chunk->shdr.sh_size == 0;
return !chunk->is_output_section() && chunk->shdr.sh_size == 0;
});
// Set section indices.
for (i64 i = 0, shndx = 1; i < ctx.chunks.size(); i++)
if (ctx.chunks[i]->kind != Chunk<E>::HEADER)
if (!ctx.chunks[i]->is_header())
ctx.chunks[i]->shndx = shndx++;
// Some types of section header refer other section by index.
@@ -620,32 +643,28 @@ static int elf_main(int argc, char **argv) {
// 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 (E::e_machine == EM_AARCH64)
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>)
filesize = riscv_resize_sections(ctx);
// Fix linker-synthesized symbol addresses.
fix_synthetic_symbols(ctx);
// If --compress-debug-sections is given, compress .debug_* sections
// using zlib.
if (ctx.arg.compress_debug_sections != COMPRESS_NONE) {
compress_debug_sections(ctx);
filesize = set_osec_offsets(ctx);
}
// At this point, file layout is fixed.
// Beyond this, you can assume that symbol addresses including their
// GOT or PLT addresses have a correct final value.
// Some types of TLS relocations are defined relative to the beginning
// or the end of the TLS segment address. Find these addresses now.
for (ElfPhdr<E> phdr : create_phdr(ctx)) {
if (phdr.p_type == PT_TLS) {
ctx.tls_begin = phdr.p_vaddr;
ctx.tls_end = align_to(phdr.p_vaddr + phdr.p_memsz, phdr.p_align);
break;
}
}
// If --compress-debug-sections is given, compress .debug_* sections
// using zlib.
if (ctx.arg.compress_debug_sections != COMPRESS_NONE)
filesize = compress_debug_sections(ctx);
// At this point, file layout is fixed.
t_before_copy.stop();
@@ -660,20 +679,27 @@ static int elf_main(int argc, char **argv) {
Timer t(ctx, "copy_buf");
tbb::parallel_for_each(ctx.chunks, [&](Chunk<E> *chunk) {
std::string name(chunk->name);
if (name.empty())
name = "(header)";
std::string name =
chunk->name.empty() ? "(header)" : std::string(chunk->name);
Timer t2(ctx, name, &t);
chunk->copy_buf(ctx);
});
ctx.checkpoint();
}
if constexpr (E::e_machine == EM_AARCH64)
if constexpr (std::is_same_v<E, ARM64>)
write_thunks(ctx);
if constexpr (std::is_same_v<E, ARM32>)
sort_arm_exidx(ctx);
// Some part of .gdb_index couldn't be computed until other debug
// sections are complete. We have complete debug sections now, so
// write the rest of .gdb_index.
if (ctx.gdb_index)
ctx.gdb_index->write_address_areas(ctx);
// Dynamic linker works better with sorted .rela.dyn section,
// so we sort them.
ctx.reldyn->sort(ctx);
@@ -691,6 +717,19 @@ static int elf_main(int argc, char **argv) {
// Close the output file. This is the end of the linker's main job.
ctx.output_file->close(ctx);
// Handle --dependency-file
if (!ctx.arg.dependency_file.empty())
write_dependency_file(ctx);
// Handle --print-dependencies
if (ctx.arg.print_dependencies == 1)
print_dependencies(ctx);
else if (ctx.arg.print_dependencies == 2)
print_dependencies_full(ctx);
if (ctx.has_lto_object)
lto_cleanup(ctx);
t_total.stop();
t_all.stop();
@@ -727,5 +766,7 @@ int main(int argc, char **argv) {
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+18 -11
View File
@@ -28,12 +28,13 @@ static Map<E> get_map(Context<E> &ctx) {
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (Symbol<E> *sym : file->symbols) {
if (sym->file == file && sym->input_section &&
sym->get_type() != STT_SECTION) {
assert(file == &sym->input_section->file);
if (sym->file != file || sym->get_type() == STT_SECTION)
continue;
if (InputSection<E> *isec = sym->get_input_section()) {
assert(file == &isec->file);
typename Map<E>::accessor acc;
map.insert(acc, {sym->input_section, {}});
map.insert(acc, {isec, {}});
acc->second.push_back(sym);
}
}
@@ -62,15 +63,16 @@ void print_map(Context<E> &ctx) {
Map<E> map = get_map(ctx);
// Print a mapfile.
*out << " VMA Size Align Out In Symbol\n";
*out << " VMA Size Align Out In Symbol\n";
for (Chunk<E> *osec : ctx.chunks) {
*out << std::setw(16) << (u64)osec->shdr.sh_addr
*out << std::showbase
<< std::setw(18) << std::hex << (u64)osec->shdr.sh_addr << std::dec
<< std::setw(11) << (u64)osec->shdr.sh_size
<< std::setw(6) << (u64)osec->shdr.sh_addralign
<< " " << osec->name << "\n";
if (osec->kind != Chunk<E>::REGULAR)
if (!osec->is_output_section())
continue;
std::span<InputSection<E> *> members = ((OutputSection<E> *)osec)->members;
@@ -80,16 +82,19 @@ void print_map(Context<E> &ctx) {
InputSection<E> *mem = members[i];
std::ostringstream ss;
opt_demangle = ctx.arg.demangle;
u64 addr = osec->shdr.sh_addr + mem->offset;
ss << std::setw(16) << (osec->shdr.sh_addr + mem->offset)
<< std::setw(11) << (u64)mem->shdr.sh_size
<< std::setw(6) << (u64)mem->shdr.sh_addralign
ss << std::showbase
<< std::setw(18) << std::hex << addr << std::dec
<< std::setw(11) << (u64)mem->sh_size
<< std::setw(6) << (1 << (u64)mem->p2align)
<< " " << *mem << "\n";
typename Map<E>::const_accessor acc;
if (map.find(acc, mem))
for (Symbol<E> *sym : acc->second)
ss << std::setw(16) << sym->get_addr(ctx)
ss << std::showbase
<< std::setw(18) << std::hex << sym->get_addr(ctx) << std::dec
<< " 0 0 "
<< *sym << "\n";
@@ -107,5 +112,7 @@ void print_map(Context<E> &ctx) {
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+1 -1
View File
@@ -46,7 +46,7 @@ static bool is_ld(const char *path) {
ptr--;
return !strcmp(ptr, "ld") || !strcmp(ptr, "ld.lld") ||
!strcmp(ptr, "ld.gold");
!strcmp(ptr, "ld.gold") || !strcmp(ptr, "ld.bfd");
}
int execvpe(const char *file, char *const *argv, char *const *envp) {
+731 -274
View File
File diff suppressed because it is too large. Load diff
+1074 -328
View File
File diff suppressed because it is too large. Load diff
+2
View File
@@ -138,5 +138,7 @@ OutputFile<E>::open(Context<E> &ctx, std::string path, i64 filesize, i64 perm) {
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+735 -174
View File
File diff suppressed because it is too large. Load diff
+2
View File
@@ -591,5 +591,7 @@ void combine_objects(Context<E> &ctx, std::span<std::string_view> file_args) {
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+11 -15
View File
@@ -1,4 +1,5 @@
#include "mold.h"
#include "../sha.h"
#include <filesystem>
#include <signal.h>
@@ -11,13 +12,6 @@
#include <sys/wait.h>
#include <unistd.h>
#ifdef __APPLE__
# define COMMON_DIGEST_FOR_OPENSSL
# include <CommonCrypto/CommonDigest.h>
#else
# include <openssl/sha.h>
#endif
#define DAEMON_TIMEOUT 30
namespace mold::elf {
@@ -59,7 +53,7 @@ std::function<void()> fork_child() {
// Child
close(pipefd[0]);
return [=]() {
return [=] {
char buf[] = {1};
int n = write(pipefd[1], buf, 1);
assert(n == 1);
@@ -218,7 +212,7 @@ void daemonize(Context<E> &ctx, std::function<void()> *wait_for_client,
static i64 conn = -1;
*wait_for_client = [=, &ctx]() {
*wait_for_client = [=, &ctx] {
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(sock, &rfds);
@@ -245,7 +239,7 @@ void daemonize(Context<E> &ctx, std::function<void()> *wait_for_client,
dup2(recv_fd(ctx, conn), STDERR_FILENO);
};
*on_complete = [=]() {
*on_complete = [=] {
char buf[] = {1};
int n = write(conn, buf, 1);
assert(n == 1);
@@ -256,11 +250,6 @@ static std::string get_self_path() {
return std::filesystem::read_symlink("/proc/self/exe");
}
static bool is_regular_file(const std::string &path) {
struct stat st;
return !stat(path.c_str(), &st) && (st.st_mode & S_IFMT) == S_IFREG;
}
template <typename E>
std::string find_dso(Context<E> &ctx, std::filesystem::path self) {
// Look for mold-wrapper.so from the same directory as the executable is.
@@ -277,6 +266,11 @@ std::string find_dso(Context<E> &ctx, std::filesystem::path self) {
return path;
#endif
// Look for ../lib/mold/mold-wrapper.so
path = self.parent_path() / "../lib/mold/mold-wrapper.so";
if (std::filesystem::is_regular_file(path, ec) && !ec)
return path;
Fatal(ctx) << "mold-wrapper.so is missing";
}
@@ -321,5 +315,7 @@ void process_run_subcommand(Context<E> &ctx, int argc, char **argv) {
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+1 -1
View File
@@ -27,7 +27,7 @@ std::optional<u16> VersionMatcher::find(std::string_view str) {
u32 idx = UINT32_MAX;
if (root) {
std::call_once(once_flag, [&]() { compile(); });
std::call_once(once_flag, [&] { compile(); });
// Match against simple glob patterns
TrieNode *node = root.get();
+53 -1
View File
@@ -1,6 +1,7 @@
#pragma once
#include "mold.h"
#include "elf/elf.h"
namespace mold {
@@ -16,6 +17,7 @@ enum class FileType {
THIN_AR,
TAPI,
TEXT,
GCC_LTO_OBJ,
LLVM_BITCODE,
};
@@ -26,6 +28,43 @@ bool is_text_file(MappedFile<C> *mf) {
isprint(data[2]) && isprint(data[3]);
}
template <typename E, typename C>
inline bool is_gcc_lto_obj(MappedFile<C> *mf) {
using namespace mold::elf;
const char *data = mf->get_contents().data();
ElfEhdr<E> &ehdr = *(ElfEhdr<E> *)data;
std::span<ElfShdr<E>> shdrs{(ElfShdr<E> *)(data + ehdr.e_shoff), ehdr.e_shnum};
for (ElfShdr<E> &sec : shdrs) {
if (sec.sh_type != SHT_SYMTAB)
continue;
std::span<ElfSym<E>> elf_syms{(ElfSym<E> *)(data + sec.sh_offset),
sec.sh_size / sizeof(ElfSym<E>)};
auto skip = [](u8 type) {
return type == STT_NOTYPE || type == STT_FILE || type == STT_SECTION;
};
// GCC LTO object contains only sections symbols followed by a common
// symbol whose name is `__gnu_lto_v1` or `__gnu_lto_slim`.
i64 i = 1;
while (i < elf_syms.size() && skip(elf_syms[i].st_type))
i++;
if (i < elf_syms.size() && elf_syms[i].st_shndx == SHN_COMMON) {
std::string_view name =
data + shdrs[sec.sh_link].sh_offset + elf_syms[i].st_name;
if (name.starts_with("__gnu_lto_"))
return true;
}
break;
}
return false;
}
template <typename C>
FileType get_file_type(MappedFile<C> *mf) {
std::string_view data = mf->get_contents();
@@ -35,8 +74,20 @@ FileType get_file_type(MappedFile<C> *mf) {
if (data.starts_with("\177ELF")) {
switch (*(u16 *)(data.data() + 16)) {
case 1: // ET_REL
case 1: {
// ET_REL
elf::Elf32Ehdr &ehdr = *(elf::Elf32Ehdr *)data.data();
if (ehdr.e_ident[elf::EI_CLASS] == elf::ELFCLASS32) {
if (is_gcc_lto_obj<elf::I386>(mf))
return FileType::GCC_LTO_OBJ;
} else {
if (is_gcc_lto_obj<elf::X86_64>(mf))
return FileType::GCC_LTO_OBJ;
}
return FileType::ELF_OBJ;
}
case 3: // ET_DYN
return FileType::ELF_DSO;
}
@@ -83,6 +134,7 @@ inline std::string filetype_to_string(FileType type) {
case FileType::THIN_AR: return "THIN_AR";
case FileType::TAPI: return "TAPI";
case FileType::TEXT: return "TEXT";
case FileType::GCC_LTO_OBJ: return "GCC_LTO_OBJ";
case FileType::LLVM_BITCODE: return "LLVM_BITCODE";
}
return "UNKNOWN";
+150
View File
@@ -0,0 +1,150 @@
#pragma once
namespace mold {
enum PluginStatus {
LDPS_OK,
LDPS_NO_SYMS,
LDPS_BAD_HANDLE,
LDPS_ERR,
};
enum PluginTag {
LDPT_NULL,
LDPT_API_VERSION,
LDPT_GOLD_VERSION,
LDPT_LINKER_OUTPUT,
LDPT_OPTION,
LDPT_REGISTER_CLAIM_FILE_HOOK,
LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK,
LDPT_REGISTER_CLEANUP_HOOK,
LDPT_ADD_SYMBOLS,
LDPT_GET_SYMBOLS,
LDPT_ADD_INPUT_FILE,
LDPT_MESSAGE,
LDPT_GET_INPUT_FILE,
LDPT_RELEASE_INPUT_FILE,
LDPT_ADD_INPUT_LIBRARY,
LDPT_OUTPUT_NAME,
LDPT_SET_EXTRA_LIBRARY_PATH,
LDPT_GNU_LD_VERSION,
LDPT_GET_VIEW,
LDPT_GET_INPUT_SECTION_COUNT,
LDPT_GET_INPUT_SECTION_TYPE,
LDPT_GET_INPUT_SECTION_NAME,
LDPT_GET_INPUT_SECTION_CONTENTS,
LDPT_UPDATE_SECTION_ORDER,
LDPT_ALLOW_SECTION_ORDERING,
LDPT_GET_SYMBOLS_V2,
LDPT_ALLOW_UNIQUE_SEGMENT_FOR_SECTIONS,
LDPT_UNIQUE_SEGMENT_FOR_SECTIONS,
LDPT_GET_SYMBOLS_V3,
LDPT_GET_INPUT_SECTION_ALIGNMENT,
LDPT_GET_INPUT_SECTION_SIZE,
LDPT_REGISTER_NEW_INPUT_HOOK,
LDPT_GET_WRAP_SYMBOLS,
LDPT_ADD_SYMBOLS_V2,
};
enum PluginApiVersion {
LD_PLUGIN_API_VERSION = 1,
};
struct PluginTagValue {
PluginTagValue(PluginTag tag, int val) : tag(tag), val(val) {}
template <typename T>
PluginTagValue(PluginTag tag, T *ptr) : tag(tag), ptr((void *)ptr) {}
PluginTag tag;
union {
int val;
void *ptr;
};
};
enum PluginOutputFileType {
LDPO_REL,
LDPO_EXEC,
LDPO_DYN,
LDPO_PIE,
};
struct PluginInputFile {
const char *name;
int fd;
off_t offset;
off_t filesize;
void *handle;
};
struct PluginSection {
const void *handle;
int shndx;
};
struct PluginSymbol {
char *name;
char *version;
char def;
char symbol_type;
char section_kind;
int visibility;
uint64_t size;
char *comdat_key;
int resolution;
};
enum PluginSymbolKind {
LDPK_DEF,
LDPK_WEAKDEF,
LDPK_UNDEF,
LDPK_WEAKUNDEF,
LDPK_COMMON,
};
enum PluginSymbolVisibility {
LDPV_DEFAULT,
LDPV_PROTECTED,
LDPV_INTERNAL,
LDPV_HIDDEN,
};
enum PluginSymbolType {
LDST_UNKNOWN,
LDST_FUNCTION,
LDST_VARIABLE,
};
enum PluginSymbolSectionKind {
LDSSK_DEFAULT,
LDSSK_BSS,
};
enum PluginSymbolResolution {
LDPR_UNKNOWN,
LDPR_UNDEF,
LDPR_PREVAILING_DEF,
LDPR_PREVAILING_DEF_IRONLY,
LDPR_PREEMPTED_REG,
LDPR_PREEMPTED_IR,
LDPR_RESOLVED_IR,
LDPR_RESOLVED_EXEC,
LDPR_RESOLVED_DYN,
LDPR_PREVAILING_DEF_IRONLY_EXP,
};
enum PluginLevel {
LDPL_INFO,
LDPL_WARNING,
LDPL_ERROR,
LDPL_FATAL,
};
typedef PluginStatus OnloadFn(PluginTagValue *tv);
typedef PluginStatus ClaimFileHandler(const PluginInputFile *, int *);
typedef PluginStatus AllSymbolsReadHandler();
typedef PluginStatus CleanupHandler();
typedef PluginStatus NewInputHandler(const PluginInputFile *);
} // namespace mold
+1 -1
View File
@@ -159,7 +159,7 @@ void Subsection<ARM64>::scan_relocations(Context<ARM64> &ctx) {
break;
}
if (sym->file && sym->file->is_dylib) {
if (sym->file && sym->file->is_dylib()) {
sym->flags |= NEEDS_STUB;
((DylibFile<ARM64> *)sym->file)->is_needed = true;
}
+3 -3
View File
@@ -138,19 +138,19 @@ void Subsection<X86_64>::scan_relocations(Context<X86_64> &ctx) {
switch (r.type) {
case X86_64_RELOC_GOT_LOAD:
if (sym->file && sym->file->is_dylib)
if (sym->file && sym->file->is_dylib())
sym->flags |= NEEDS_GOT;
break;
case X86_64_RELOC_GOT:
sym->flags |= NEEDS_GOT;
break;
case X86_64_RELOC_TLV:
if (sym->file && sym->file->is_dylib)
if (sym->file && sym->file->is_dylib())
sym->flags |= NEEDS_THREAD_PTR;
break;
}
if (sym->file && sym->file->is_dylib) {
if (sym->file && sym->file->is_dylib()) {
sym->flags |= NEEDS_STUB;
((DylibFile<X86_64> *)sym->file)->is_needed = true;
}
+1 -1
View File
@@ -194,7 +194,7 @@ void dump_file(std::string path) {
LoadCommand &lc = *(LoadCommand *)p;
p += lc.cmdsize;
auto print_dylib = [&]() {
auto print_dylib = [&] {
DylibCommand &cmd = *(DylibCommand *)&lc;
std::cout << " cmdsize: 0x" << cmd.cmdsize
<< "\n nameoff: 0x" << cmd.nameoff
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#include "../byteorder.h"
#include "../big-endian.h"
#include <cassert>
#include <cstdint>
+10 -1
View File
@@ -23,7 +23,7 @@ split_string(std::string_view str, char sep) {
template <typename E>
static void create_internal_file(Context<E> &ctx) {
ObjectFile<E> *obj = new ObjectFile<E>;
ctx.obj_pool.push_back(std::unique_ptr<ObjectFile<E>>(obj));
ctx.obj_pool.emplace_back(obj);
ctx.objs.push_back(obj);
auto add = [&](std::string_view name) {
@@ -474,6 +474,15 @@ static int do_main(int argc, char **argv) {
}
int main(int argc, char **argv) {
if (!getenv("MOLD_SUPPRESS_MACHO_WARNING")) {
std::cerr <<
R"(********************************************************************************
mold for macOS is pre-alpha. Do not use unless you know what you are doing.
Do not report bugs because it's too early to manage missing features as bugs.
********************************************************************************
)";
}
return do_main<ARM64>(argc, argv);
}
+19 -13
View File
@@ -57,13 +57,16 @@ struct UnwindRecord {
template <typename E>
class InputFile {
public:
virtual ~InputFile() = default;
MappedFile<Context<E>> *mf = nullptr;
std::vector<Symbol<E> *> syms;
i64 priority = 0;
bool is_dylib = false;
std::atomic_bool is_alive = false;
std::string archive_name;
virtual bool is_dylib() const = 0;
protected:
InputFile() = default;
};
@@ -75,6 +78,7 @@ public:
static ObjectFile *create(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::string archive_name);
bool is_dylib() const override { return false; }
void parse(Context<E> &ctx);
Subsection<E> *find_subsection(Context<E> &ctx, u32 addr);
void parse_compact_unwind(Context<E> &ctx, MachSection &hdr);
@@ -121,13 +125,14 @@ public:
i64 dylib_idx = 0;
std::atomic_bool is_needed = false;
bool is_dylib() const override { return true; }
private:
void parse_dylib(Context<E> &ctx);
void read_trie(Context<E> &ctx, u8 *start, i64 offset = 0,
const std::string &prefix = "");
DylibFile() {
this->is_dylib = true;
this->is_alive = true;
}
};
@@ -382,7 +387,7 @@ public:
OutputLazyBindSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__lazy_binding") {
this->is_hidden = true;
this->hdr.p2align = __builtin_ctz(8);
this->hdr.p2align = std::countr_zero(8U);
}
void add(Context<E> &ctx, Symbol<E> &sym, i64 flags);
@@ -462,7 +467,7 @@ public:
OutputSymtabSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__symbol_table") {
this->is_hidden = true;
this->hdr.p2align = __builtin_ctz(8);
this->hdr.p2align = std::countr_zero(8U);
}
void compute_size(Context<E> &ctx) override;
@@ -484,7 +489,7 @@ public:
OutputStrtabSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__string_table") {
this->is_hidden = true;
this->hdr.p2align = __builtin_ctz(8);
this->hdr.p2align = std::countr_zero(8U);
}
i64 add_string(std::string_view str);
@@ -522,7 +527,7 @@ public:
CodeSignatureSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__code_signature") {
this->is_hidden = true;
this->hdr.p2align = __builtin_ctz(16);
this->hdr.p2align = std::countr_zero(16U);
}
void compute_size(Context<E> &ctx) override;
@@ -537,7 +542,7 @@ public:
DataInCodeSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__data_in_code") {
this->is_hidden = true;
this->hdr.p2align = __builtin_ctz(alignof(DataInCodeEntry));
this->hdr.p2align = std::countr_zero(alignof(DataInCodeEntry));
}
void compute_size(Context<E> &ctx) override;
@@ -550,7 +555,7 @@ template <typename E>
class StubsSection : public Chunk<E> {
public:
StubsSection(Context<E> &ctx) : Chunk<E>(ctx, "__TEXT", "__stubs") {
this->hdr.p2align = __builtin_ctz(2);
this->hdr.p2align = std::countr_zero(2U);
this->hdr.type = S_SYMBOL_STUBS;
this->hdr.attr = S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS;
this->hdr.reserved2 = E::stub_size;
@@ -568,7 +573,7 @@ class StubHelperSection : public Chunk<E> {
public:
StubHelperSection(Context<E> &ctx)
: Chunk<E>(ctx, "__TEXT", "__stub_helper") {
this->hdr.p2align = __builtin_ctz(4);
this->hdr.p2align = std::countr_zero(4U);
this->hdr.attr = S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS;
}
@@ -594,7 +599,7 @@ class UnwindInfoSection : public Chunk<E> {
public:
UnwindInfoSection(Context<E> &ctx)
: Chunk<E>(ctx, "__TEXT", "__unwind_info") {
this->hdr.p2align = __builtin_ctz(4);
this->hdr.p2align = std::countr_zero(4U);
}
void compute_size(Context<E> &ctx) override;
@@ -607,7 +612,7 @@ template <typename E>
class GotSection : public Chunk<E> {
public:
GotSection(Context<E> &ctx) : Chunk<E>(ctx, "__DATA_CONST", "__got") {
this->hdr.p2align = __builtin_ctz(8);
this->hdr.p2align = std::countr_zero(8U);
this->hdr.type = S_NON_LAZY_SYMBOL_POINTERS;
}
@@ -622,7 +627,7 @@ class LazySymbolPtrSection : public Chunk<E> {
public:
LazySymbolPtrSection(Context<E> &ctx)
: Chunk<E>(ctx, "__DATA", "__la_symbol_ptr") {
this->hdr.p2align = __builtin_ctz(8);
this->hdr.p2align = std::countr_zero(8U);
this->hdr.type = S_LAZY_SYMBOL_POINTERS;
}
@@ -634,7 +639,7 @@ class ThreadPtrsSection : public Chunk<E> {
public:
ThreadPtrsSection(Context<E> &ctx)
: Chunk<E>(ctx, "__DATA", "__thread_ptrs") {
this->hdr.p2align = __builtin_ctz(8);
this->hdr.p2align = std::countr_zero(8U);
this->hdr.type = S_THREAD_LOCAL_VARIABLE_POINTERS;
}
@@ -774,6 +779,7 @@ struct Context {
bool dylib = false;
bool dynamic = true;
bool fatal_warnings = false;
bool noinhibit_exec = false;
bool trace = false;
i64 arch = CPU_TYPE_ARM64;
i64 headerpad = 256;
+10 -9
View File
@@ -21,7 +21,7 @@ ObjectFile<E>::create(Context<E> &ctx, MappedFile<Context<E>> *mf,
obj->mf = mf;
obj->archive_name = archive_name;
obj->is_alive = archive_name.empty();
ctx.obj_pool.push_back(std::unique_ptr<ObjectFile<E>>(obj));
ctx.obj_pool.emplace_back(obj);
return obj;
};
@@ -107,7 +107,8 @@ struct SplitRegion {
template <typename E>
struct SplitInfo {
InputSection<E> *isec = nullptr;
SplitInfo(InputSection<E> *isec) : isec(isec) {}
InputSection<E> *isec;
std::vector<SplitRegion> regions;
};
@@ -185,7 +186,7 @@ void ObjectFile<E>::split_subsections(Context<E> &ctx) {
.input_addr = (u32)(isec.hdr.addr + r.offset),
.p2align = (u8)isec.hdr.p2align,
};
subsections.push_back(std::unique_ptr<Subsection<E>>(subsec));
subsections.emplace_back(subsec);
}
if (r.symidx != -1)
@@ -264,7 +265,7 @@ void ObjectFile<E>::parse_compact_unwind(Context<E> &ctx, MachSection &hdr) {
i64 idx = r.offset / sizeof(CompactUnwindEntry);
UnwindRecord<E> &dst = unwind_records[idx];
auto error = [&]() {
auto error = [&] {
Fatal(ctx) << *this << ": __compact_unwind: unsupported relocation: " << i;
};
@@ -343,7 +344,7 @@ static u64 get_rank(InputFile<E> *file, MachSym &msym, bool is_lazy) {
return (6 << 24) + file->priority;
if (is_lazy)
return (5 << 24) + file->priority;
if (file->is_dylib)
if (file->is_dylib())
return (3 << 24) + file->priority;
return (1 << 24) + file->priority;
}
@@ -357,7 +358,7 @@ static u64 get_rank(Symbol<E> &sym) {
return (6 << 24) + file->priority;
if (!file->archive_name.empty())
return (5 << 24) + file->priority;
if (file->is_dylib)
if (file->is_dylib())
return (3 << 24) + file->priority;
return (1 << 24) + file->priority;
}
@@ -481,7 +482,7 @@ void ObjectFile<E>::convert_common_symbols(Context<E> &ctx) {
.p2align = (u8)msym.p2align,
};
subsections.push_back(std::unique_ptr<Subsection<E>>(subsec));
subsections.emplace_back(subsec);
sym.subsec = subsec;
sym.value = 0;
@@ -513,7 +514,7 @@ InputSection<E> *ObjectFile<E>::get_common_sec(Context<E> &ctx) {
hdr->type = S_ZEROFILL;
common_sec = new InputSection<E>(ctx, *this, *hdr);
sections.push_back(std::unique_ptr<InputSection<E>>(common_sec));
sections.emplace_back(common_sec);
}
return common_sec;
}
@@ -522,7 +523,7 @@ 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.push_back(std::unique_ptr<DylibFile<E>>(dylib));
ctx.dylib_pool.emplace_back(dylib);
return dylib;
};
+15 -22
View File
@@ -1,16 +1,9 @@
#include "mold.h"
#include "../sha.h"
#include <shared_mutex>
#include <sys/mman.h>
#ifdef __APPLE__
# define COMMON_DIGEST_FOR_OPENSSL
# include <CommonCrypto/CommonDigest.h>
# define SHA256(data, len, md) CC_SHA256(data, len, md)
#else
# include <openssl/sha.h>
#endif
namespace mold::macho {
template <typename E>
@@ -375,7 +368,7 @@ OutputSection<E>::get_instance(Context<E> &ctx, std::string_view segname,
return osec;
OutputSection<E> *osec = new OutputSection<E>(ctx, segname, sectname);
ctx.osec_pool.push_back(std::unique_ptr<OutputSection<E>>(osec));
ctx.osec_pool.emplace_back(osec);
return osec;
}
@@ -433,7 +426,7 @@ OutputSegment<E>::get_instance(Context<E> &ctx, std::string_view name) {
return seg;
OutputSegment<E> *seg = new OutputSegment<E>(name);
ctx.segments.push_back(std::unique_ptr<OutputSegment<E>>(seg));
ctx.segments.emplace_back(seg);
return seg;
}
@@ -570,12 +563,12 @@ void OutputRebaseSection<E>::compute_size(Context<E> &ctx) {
ctx.data_seg->cmd.vmaddr);
for (Symbol<E> *sym : ctx.got.syms)
if (!sym->file->is_dylib)
if (!sym->file->is_dylib())
enc.add(ctx.data_const_seg->seg_idx,
sym->get_got_addr(ctx) - ctx.data_const_seg->cmd.vmaddr);
for (Symbol<E> *sym : ctx.thread_ptrs.syms)
if (!sym->file->is_dylib)
if (!sym->file->is_dylib())
enc.add(ctx.data_seg->seg_idx,
sym->get_tlv_addr(ctx) - ctx.data_seg->cmd.vmaddr);
@@ -644,13 +637,13 @@ void OutputBindSection<E>::compute_size(Context<E> &ctx) {
BindEncoder enc;
for (Symbol<E> *sym : ctx.got.syms)
if (sym->file->is_dylib)
if (sym->file->is_dylib())
enc.add(((DylibFile<E> *)sym->file)->dylib_idx, sym->name, 0,
ctx.data_const_seg->seg_idx,
sym->get_got_addr(ctx) - ctx.data_const_seg->cmd.vmaddr);
for (Symbol<E> *sym : ctx.thread_ptrs.syms)
if (sym->file->is_dylib)
if (sym->file->is_dylib())
enc.add(((DylibFile<E> *)sym->file)->dylib_idx, sym->name, 0,
ctx.data_seg->seg_idx,
sym->get_tlv_addr(ctx) - ctx.data_seg->cmd.vmaddr);
@@ -901,15 +894,15 @@ void OutputSymtabSection<E>::copy_buf(Context<E> &ctx) {
Symbol<E> &sym = *ent.sym;
msym.stroff = ent.stroff;
msym.type = (sym.file->is_dylib ? N_UNDF : N_SECT);
msym.type = (sym.file->is_dylib() ? N_UNDF : N_SECT);
msym.ext = sym.is_extern;
if (!sym.file->is_dylib)
if (!sym.file->is_dylib())
msym.value = sym.get_addr(ctx);
if (sym.subsec)
msym.sect = sym.subsec->isec.osec.sect_idx;
if (sym.file->is_dylib)
if (sym.file->is_dylib())
msym.desc = ((DylibFile<E> *)sym.file)->dylib_idx << 8;
else if (sym.referenced_dynamically)
msym.desc = REFERENCED_DYNAMICALLY;
@@ -1011,7 +1004,7 @@ void CodeSignatureSection<E>::write_signature(Context<E> &ctx) {
dir.code_limit = this->hdr.offset;
dir.hash_size = SHA256_SIZE;
dir.hash_type = CS_HASHTYPE_SHA256;
dir.page_size = __builtin_ctz(BLOCK_SIZE);
dir.page_size = std::countr_zero<u64>(BLOCK_SIZE);
dir.exec_seg_base = ctx.text_seg->cmd.fileoff;
dir.exec_seg_limit = ctx.text_seg->cmd.filesize;
if (ctx.output_type == MH_EXECUTE)
@@ -1175,13 +1168,13 @@ u32 UnwindEncoder<E>::encode_personality(Context<E> &ctx, Symbol<E> *sym) {
for (i64 i = 0; i < personalities.size(); i++)
if (personalities[i] == sym)
return (i + 1) << __builtin_ctz(UNWIND_PERSONALITY_MASK);
return (i + 1) << std::countr_zero(UNWIND_PERSONALITY_MASK);
if (personalities.size() == 3)
Fatal(ctx) << ": too many personality functions";
personalities.push_back(sym);
return personalities.size() << __builtin_ctz(UNWIND_PERSONALITY_MASK);
return personalities.size() << std::countr_zero(UNWIND_PERSONALITY_MASK);
}
template <typename E>
@@ -1247,7 +1240,7 @@ template <typename E>
void GotSection<E>::copy_buf(Context<E> &ctx) {
u64 *buf = (u64 *)(ctx.buf + this->hdr.offset);
for (i64 i = 0; i < syms.size(); i++)
if (!syms[i]->file->is_dylib)
if (!syms[i]->file->is_dylib())
buf[i] = syms[i]->get_addr(ctx);
}
@@ -1272,7 +1265,7 @@ template <typename E>
void ThreadPtrsSection<E>::copy_buf(Context<E> &ctx) {
u64 *buf = (u64 *)(ctx.buf + this->hdr.offset);
for (i64 i = 0; i < syms.size(); i++)
if (!syms[i]->file->is_dylib)
if (!syms[i]->file->is_dylib())
buf[i] = syms[i]->get_addr(ctx);
}
+4
View File
@@ -4,6 +4,10 @@
#include <cstring>
#include <signal.h>
#ifdef USE_SYSTEM_MIMALLOC
#include <mimalloc-new-delete.h>
#endif
namespace mold {
#ifdef GIT_HASH
+77 -95
View File
@@ -1,8 +1,9 @@
#pragma once
#include "byteorder.h"
#include "big-endian.h"
#include <atomic>
#include <bit>
#include <cassert>
#include <cstdio>
#include <cstring>
@@ -83,11 +84,18 @@ private:
std::stringstream ss;
};
template <typename C>
static std::string add_color(C &ctx, std::string msg) {
if (ctx.arg.color_diagnostics)
return "mold: \033[0;1;31m" + msg + ":\033[0m ";
return "mold: " + msg + ": ";
}
template <typename C>
class Fatal {
public:
Fatal(C &ctx) : out(ctx, std::cerr) {
out << "mold: ";
out << add_color(ctx, "fatal");
}
[[noreturn]] ~Fatal() {
@@ -109,12 +117,12 @@ template <typename C>
class Error {
public:
Error(C &ctx) : out(ctx, std::cerr) {
if (ctx.arg.color_diagnostics)
out << "mold: \033[0;1;31merror:\033[0m ";
else
out << "mold: error: ";
ctx.has_error = true;
if (ctx.arg.noinhibit_exec) {
out << add_color(ctx, "warning");
} else {
out << add_color(ctx, "error");
ctx.has_error = true;
}
}
template <class T> Error &operator<<(T &&val) {
@@ -131,16 +139,10 @@ class Warn {
public:
Warn(C &ctx) : out(ctx, std::cerr) {
if (ctx.arg.fatal_warnings) {
if (ctx.arg.color_diagnostics)
out << "mold: \033[0;1;31merror:\033[0m ";
else
out << "mold: error: ";
out << add_color(ctx, "error");
ctx.has_error = true;
} else {
if (ctx.arg.color_diagnostics)
out << "mold: \033[0;1;35mwarning:\033[0m ";
else
out << "mold: warning: ";
out << add_color(ctx, "warning");
}
}
@@ -160,12 +162,12 @@ private:
inline u64 align_to(u64 val, u64 align) {
if (align == 0)
return val;
assert(__builtin_popcount(align) == 1);
assert(std::popcount(align) == 1);
return (val + align - 1) & ~(align - 1);
}
inline u64 align_down(u64 val, u64 align) {
assert(__builtin_popcount(align) == 1);
assert(std::popcount(align) == 1);
return val & ~(align - 1);
}
@@ -173,20 +175,18 @@ inline u64 next_power_of_two(u64 val) {
assert(val >> 63 == 0);
if (val == 0 || val == 1)
return 1;
return (u64)1 << (64 - __builtin_clzl(val - 1));
return (u64)1 << (64 - std::countl_zero(val - 1));
}
template <typename T, typename Compare = std::less<T>>
void update_minimum(std::atomic<T> &atomic, u64 new_val,
Compare cmp = {}) {
void update_minimum(std::atomic<T> &atomic, u64 new_val, Compare cmp = {}) {
T old_val = atomic;
while (cmp(new_val, old_val) &&
!atomic.compare_exchange_weak(old_val, new_val));
}
template <typename T, typename Compare = std::less<T>>
void update_maximum(std::atomic<T> &atomic, u64 new_val,
Compare cmp = {}) {
void update_maximum(std::atomic<T> &atomic, u64 new_val, Compare cmp = {}) {
T old_val = atomic;
while (cmp(old_val, new_val) &&
!atomic.compare_exchange_weak(old_val, new_val));
@@ -280,6 +280,11 @@ std::string_view save_string(C &ctx, const std::string &str) {
// Concurrent Map
//
// This is an implementation of a fast concurrent hash map. Unlike
// ordinary hash tables, this impl just aborts if it becomes full.
// So you need to give a correct estimation of the final size before
// using it. We use this hash map to uniquify pieces of data in
// mergeable sections.
template <typename T>
class ConcurrentMap {
public:
@@ -292,7 +297,7 @@ public:
~ConcurrentMap() {
if (keys) {
free((void *)keys);
free((void *)sizes);
free((void *)key_sizes);
free((void *)values);
}
}
@@ -304,7 +309,7 @@ public:
this->nbuckets = nbuckets;
keys = (std::atomic<const char *> *)calloc(nbuckets, sizeof(keys[0]));
sizes = (u32 *)calloc(nbuckets, sizeof(sizes[0]));
key_sizes = (u32 *)calloc(nbuckets, sizeof(key_sizes[0]));
values = (T *)calloc(nbuckets, sizeof(values[0]));
}
@@ -312,29 +317,28 @@ public:
if (!keys)
return {nullptr, false};
assert(__builtin_popcount(nbuckets) == 1);
assert(std::popcount<u64>(nbuckets) == 1);
i64 idx = hash & (nbuckets - 1);
i64 retry = 0;
while (retry < MAX_RETRY) {
const char *ptr = keys[idx];
if (ptr == locked) {
#ifdef __x86_64__
asm volatile("pause" ::: "memory");
#endif
if (ptr == marker) {
pause();
continue;
}
if (ptr == nullptr) {
if (!keys[idx].compare_exchange_weak(ptr, locked))
if (!keys[idx].compare_exchange_weak(ptr, marker))
continue;
new (values + idx) T(val);
sizes[idx] = key.size();
key_sizes[idx] = key.size();
keys[idx] = key.data();
return {values + idx, true};
}
if (key.size() == sizes[idx] && memcmp(ptr, key.data(), sizes[idx]) == 0)
if (key.size() == key_sizes[idx] &&
memcmp(ptr, key.data(), key_sizes[idx]) == 0)
return {values + idx, false};
u64 mask = nbuckets / NUM_SHARDS - 1;
@@ -347,7 +351,7 @@ public:
}
bool has_key(i64 idx) {
return keys[idx];
return keys[idx].load(std::memory_order_relaxed);
}
static constexpr i64 MIN_NBUCKETS = 2048;
@@ -356,56 +360,19 @@ public:
i64 nbuckets = 0;
std::atomic<const char *> *keys = nullptr;
u32 *sizes = nullptr;
u32 *key_sizes = nullptr;
T *values = nullptr;
private:
static constexpr const char *locked = "marker";
};
//
// Bit vector
//
class BitVector {
class BitRef {
public:
BitRef(u8 &byte, u8 bitpos) : byte(byte), bitpos(bitpos) {}
BitRef &operator=(bool val) {
if (val)
byte |= (1 << bitpos);
else
byte &= ~(1 << bitpos);
return *this;
}
BitRef &operator=(const BitRef &other) {
*this = (bool)other;
return *this;
}
operator bool() const {
return byte & (1 << bitpos);
}
private:
u8 &byte;
u8 bitpos;
};
public:
void resize(i64 size) {
vec.reset(new u8[(size + 7) / 8]);
memset(vec.get(), 0, (size + 7) / 8);
static void pause() {
#if defined(__x86_64__)
asm volatile("pause");
#elif defined(__aarch64__)
asm volatile("yield");
#endif
}
BitRef operator[](i64 i) {
return BitRef(vec[i / 8], i % 8);
}
private:
std::unique_ptr<u8[]> vec = nullptr;
static constexpr const char *marker = "marker";
};
//
@@ -423,7 +390,7 @@ public:
HyperLogLog() : buckets(NBUCKETS) {}
void insert(u32 hash) {
update_maximum(buckets[hash & (NBUCKETS - 1)], __builtin_clz(hash) + 1);
update_maximum(buckets[hash & (NBUCKETS - 1)], std::countl_zero(hash) + 1);
}
i64 get_cardinality() const;
@@ -571,24 +538,24 @@ private:
// TarFile is a class to create a tar file.
//
// If you pass `--reproduce=repro.tar` to mold, mold collects all
// input files and put them into `repro.tar`, so that it is easy to
// If you pass `--repro` to mold, mold collects all input files and
// put them into `<output-file-path>.repro.tar`, so that it is easy to
// run the same command with the same command line arguments.
class TarFile {
class TarWriter {
public:
TarFile(std::string basedir) : basedir(basedir) {}
static std::unique_ptr<TarWriter>
open(std::string output_path, std::string basedir);
~TarWriter();
void append(std::string path, std::string_view data);
void write_to(u8 *buf);
i64 size() const { return size_; }
private:
static constexpr i64 BLOCK_SIZE = 512;
std::string encode_path(std::string path);
TarWriter(FILE *out, std::string basedir) : out(out), basedir(basedir) {}
FILE *out = nullptr;
std::string basedir;
std::vector<std::pair<std::string, std::string_view>> contents;
i64 size_ = BLOCK_SIZE * 2;
};
//
@@ -611,21 +578,32 @@ public:
return std::string_view((char *)data, size);
}
i64 get_offset() const {
return parent ? (data - parent->data + parent->get_offset()) : 0;
}
// Returns a string that uniquely identify a file that is possibly
// in an archive.
std::string get_identifier() const {
if (parent) {
// We use the file offset within an archive as an identifier
// because archive members may have the same name.
return parent->name + ":" + std::to_string(get_offset());
}
return name;
}
std::string name;
u8 *data = nullptr;
i64 size = 0;
i64 mtime = 0;
bool given_fullpath = true;
MappedFile *parent = nullptr;
int fd = -1;
};
template <typename C>
MappedFile<C> *MappedFile<C>::open(C &ctx, std::string path) {
MappedFile *mf = new MappedFile;
mf->name = path;
ctx.mf_pool.push_back(std::unique_ptr<MappedFile>(mf));
if (path.starts_with('/') && !ctx.arg.chroot.empty())
path = ctx.arg.chroot + "/" + path_clean(path);
@@ -637,6 +615,10 @@ MappedFile<C> *MappedFile<C>::open(C &ctx, std::string path) {
if (fstat(fd, &st) == -1)
Fatal(ctx) << path << ": fstat failed: " << errno_string();
MappedFile *mf = new MappedFile;
ctx.mf_pool.push_back(std::unique_ptr<MappedFile>(mf));
mf->name = path;
mf->size = st.st_size;
#ifdef __APPLE__
@@ -659,7 +641,7 @@ template <typename C>
MappedFile<C> *MappedFile<C>::must_open(C &ctx, std::string path) {
if (MappedFile *mf = MappedFile::open(ctx, path))
return mf;
Fatal(ctx) << "cannot open " << path;
Fatal(ctx) << "cannot open " << path << ": " << errno_string();
}
template <typename C>
+8
View File
@@ -0,0 +1,8 @@
#ifdef __APPLE__
# define COMMON_DIGEST_FOR_OPENSSL
# include <CommonCrypto/CommonDigest.h>
# define SHA256(data, len, md) CC_SHA256(data, len, md)
#else
# define OPENSSL_SUPPRESS_DEPRECATED 1
# include <openssl/sha.h>
#endif
+1 -1
View File
@@ -2,7 +2,7 @@
// In this file, we explicitly undefine _GNU_SOURCE to always
// use the POSIX version.
#define _POSIX_C_SOURCE 200112L
#define _POSIX_C_SOURCE 200809L
#undef _GNU_SOURCE
#include <cstring>
+42 -37
View File
@@ -11,7 +11,11 @@ namespace mold {
//
// For simplicity, we always emit a PAX header even for a short filename.
struct UstarHeader {
void flush() {
UstarHeader() {
memset(this, 0, sizeof(*this));
}
void finalize() {
memset(checksum, ' ', sizeof(checksum));
memcpy(magic, "ustar", 5);
memcpy(version, "00", 2);
@@ -43,7 +47,7 @@ struct UstarHeader {
char pad[12];
};
std::string TarFile::encode_path(std::string path) {
static std::string encode_path(std::string basedir, std::string path) {
path = path_clean(basedir + "/" + path);
// Construct a string which contains something like
@@ -55,50 +59,51 @@ std::string TarFile::encode_path(std::string path) {
return std::to_string(total) + " path=" + path + "\n";
}
void TarFile::append(std::string path, std::string_view data) {
contents.push_back({path, data});
size_ += BLOCK_SIZE * 2;
size_ += align_to(encode_path(path).size(), BLOCK_SIZE);
size_ += align_to(data.size(), BLOCK_SIZE);
std::unique_ptr<TarWriter>
TarWriter::open(std::string output_path, std::string basedir) {
FILE *out = fopen(output_path.c_str(), "w");
if (!out)
return nullptr;
return std::unique_ptr<TarWriter>(new TarWriter(out, basedir));
}
void TarFile::write_to(u8 *buf) {
u8 *start = buf;
memset(buf, 0, size_);
TarWriter::~TarWriter() {
fclose(out);
}
for (i64 i = 0; i < contents.size(); i++) {
assert(buf - start <= size_);
void TarWriter::append(std::string path, std::string_view data) {
// Write PAX header
static_assert(sizeof(UstarHeader) == BLOCK_SIZE);
UstarHeader pax;
const std::string &path = contents[i].first;
std::string_view data = contents[i].second;
std::string attr = encode_path(basedir, path);
sprintf(pax.size, "%011zo", attr.size());
pax.typeflag[0] = 'x';
pax.finalize();
fwrite(&pax, sizeof(pax), 1, out);
// Write PAX header
static_assert(sizeof(UstarHeader) == BLOCK_SIZE);
UstarHeader &pax = *(UstarHeader *)buf;
buf += BLOCK_SIZE;
// Write pathname
fwrite(attr.data(), attr.size(), 1, out);
fseek(out, align_to(ftell(out), BLOCK_SIZE), SEEK_SET);
std::string attr = encode_path(path);
sprintf(pax.size, "%011zo", attr.size());
pax.typeflag[0] = 'x';
pax.flush();
// Write Ustar header
UstarHeader ustar;
memcpy(ustar.mode, "0000664", 8);
sprintf(ustar.size, "%011zo", data.size());
ustar.finalize();
fwrite(&ustar, sizeof(ustar), 1, out);
// Write pathname
memcpy(buf, attr.data(), attr.size());
buf += align_to(attr.size(), BLOCK_SIZE);
// Write file contents
fwrite(data.data(), data.size(), 1, out);
fseek(out, align_to(ftell(out), BLOCK_SIZE), SEEK_SET);
// Write Ustar header
UstarHeader &ustar = *(UstarHeader *)buf;
buf += BLOCK_SIZE;
// A tar file must ends with two empty blocks, so write such
// terminator and seek back.
u8 terminator[BLOCK_SIZE * 2] = {};
fwrite(&terminator, BLOCK_SIZE * 2, 1, out);
fseek(out, -BLOCK_SIZE * 2, SEEK_END);
memcpy(ustar.mode, "0000664", 8);
sprintf(ustar.size, "%011zo", data.size());
ustar.flush();
// Write file contents
memcpy(buf, data.data(), data.size());
buf += align_to(data.size(), BLOCK_SIZE);
}
assert(ftell(out) % BLOCK_SIZE == 0);
}
} // namespace mold
-35
View File
@@ -1,35 +0,0 @@
#!/bin/bash
export LANG=
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
[ "$(uname -m)" = x86_64 ] || { echo skipped; exit; }
echo 'int main() {}' | aarch64-linux-gnu-gcc -o $t/exe -xc - >& /dev/null \
|| { echo skipped; exit; }
cat <<EOF | aarch64-linux-gnu-gcc -o $t/a.o -c -g -xc -
#include <stdio.h>
int main() {
printf("Hello world\n");
return 0;
}
EOF
aarch64-linux-gnu-gcc -B"`dirname "$mold"`" -o $t/exe $t/a.o -static
readelf -p .comment $t/exe | grep -qw mold
readelf -a $t/exe > $t/log
grep -Pq 'Machine:\s+AArch64' $t/log
qemu-aarch64 -L /usr/aarch64-linux-gnu $t/exe | grep -q 'Hello world'
echo OK
+52
View File
@@ -0,0 +1,52 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${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
mkdir -p $t
[ $MACHINE = x86_64 ] || { echo skipped; exit; }
cat <<EOF | $CC -o $t/a.o -c -x assembler -
.globl foo
foo = 0x800000
EOF
cat <<EOF | $CC -o $t/b.o -c -fno-PIC -xc -
#define _GNU_SOURCE 1
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>
void handler(int signum, siginfo_t *info, void *ptr) {
ucontext_t *u = (ucontext_t *)ptr;
printf("ip=0x%llx\n", u->uc_mcontext.gregs[REG_RIP]);
exit(0);
}
void foo();
int main() {
struct sigaction act;
act.sa_flags = SA_SIGINFO | SA_RESETHAND;
act.sa_sigaction = handler;
sigemptyset(&act.sa_mask);
sigaction(SIGSEGV, &act, 0);
foo();
}
EOF
$CC -B. -o $t/exe -no-pie $t/a.o $t/b.o
$QEMU $t/exe | grep -q '^ip=0x800000$'
echo OK
+6 -2
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
+6 -2
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
+6 -2
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
+6 -2
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
+6 -2
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
+6 -2
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
+8 -4
View File
@@ -1,21 +1,25 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${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
mkdir -p $t
[ "$(uname -m)" = x86_64 ] || { echo skipped; exit; }
[ $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
objdump -d $t/exe > /dev/null
$OBJDUMP -d $t/exe > /dev/null
file $t/exe | grep -q ELF
echo OK
+7 -3
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
@@ -38,6 +42,6 @@ int main() {
EOF
$CC -B. -no-pie -o $t/exe $t/c.o $t/b.so
$t/exe | grep -q '3 3 1'
$QEMU $t/exe | grep -q '3 3 1'
echo OK
+7 -3
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
@@ -38,6 +42,6 @@ int main() {
EOF
$CC -B. -no-pie -o $t/exe $t/c.o $t/b.so
$t/exe | grep -q '3 3 0'
$QEMU $t/exe | grep -q '3 3 0'
echo OK
+7 -3
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
@@ -38,6 +42,6 @@ int main() {
EOF
$CC -B. -no-pie -o $t/exe $t/c.o $t/b.so
$t/exe | grep -q '3 4 0'
$QEMU $t/exe | grep -q '3 4 0'
echo OK
+6 -2
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
+9 -5
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
@@ -17,7 +21,7 @@ readelf -n $t/exe | grep -qv 'GNU.*0x00000010.*NT_GNU_BUILD_ID'
$CC -B. -o $t/exe $t/a.c -Wl,-build-id=uuid
readelf -nW $t/exe |
grep -Pq 'GNU.*0x00000010.*NT_GNU_BUILD_ID.*Build ID: ............4...[89abcdef]'
grep -Eq 'GNU.*0x00000010.*NT_GNU_BUILD_ID.*Build ID: ............4...[89abcdef]'
$CC -B. -o $t/exe $t/a.c -Wl,-build-id=md5
readelf -n $t/exe | grep -q 'GNU.*0x00000010.*NT_GNU_BUILD_ID'
@@ -28,7 +32,7 @@ readelf -n $t/exe | grep -q 'GNU.*0x00000014.*NT_GNU_BUILD_ID'
$CC -B. -o $t/exe $t/a.c -Wl,-build-id=sha256
readelf -n $t/exe | grep -q 'GNU.*0x00000020.*NT_GNU_BUILD_ID'
$CC -B. -o $t/exe $t/a.c -Wl,-build-id=0xdeadbeef
readelf -n $t/exe | grep -q 'Build ID: deadbeef'
$CC -B. -o $t/exe $t/a.c -Wl,-build-id=0xdeadbeefdeadbeef
readelf -n $t/exe | grep -q 'Build ID: deadbeefdeadbeef'
echo OK
+7 -3
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
@@ -41,6 +45,6 @@ int main() {
EOF
$CC -B. -no-pie -o $t/exe $t/a.so $t/b.o $t/c.o
$t/exe | grep -q '^1 1 1$'
$QEMU $t/exe | grep -q '^1 1 1$'
echo OK
+6 -2
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
+10 -6
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
@@ -16,15 +20,15 @@ int main() { foo(); }
EOF
! "$mold" -o $t/exe $t/a.o --color-diagnostics 2> $t/log
grep -Pq '\e' $t/log
grep -q $'\033' $t/log
! "$mold" -o $t/exe $t/a.o --color-diagnostics=always 2> $t/log
grep -Pq '\e' $t/log
grep -q $'\033' $t/log
! "$mold" -o $t/exe $t/a.o --color-diagnostics=never 2> $t/log
! grep -Pq '\e' $t/log || false
! grep -q $'\033' $t/log || false
! "$mold" -o $t/exe $t/a.o --color-diagnostics=auto 2> $t/log
! grep -Pq '\e' $t/log || false
! grep -q $'\033' $t/log || false
echo OK
+6 -2
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
+8 -4
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
@@ -40,7 +44,7 @@ rm -f $t/e.a
ar rcs $t/e.a $t/b.o $t/c.o $t/d.o
$CC -B. -o $t/exe $t/a.o $t/e.a
$t/exe | grep -q '5 0 0 -1'
$QEMU $t/exe | grep -q '5 0 0 -1'
cat <<EOF | $CC -fcommon -xc -c -o $t/f.o -
int bar = 0;
@@ -52,6 +56,6 @@ rm -f $t/f.a
ar rcs $t/f.a $t/b.o $t/f.o
$CC -B. -o $t/exe $t/a.o $t/f.a
$t/exe | grep -q '5 0 7 2'
$QEMU $t/exe | grep -q '5 0 7 2'
echo OK
+7 -3
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
@@ -37,6 +41,6 @@ rm -f $t/e.a
ar rcs $t/e.a $t/d.o
$CC -B. -o $t/exe $t/a.o $t/c.a $t/e.a
$t/exe | grep -q 5
$QEMU $t/exe | grep -q 5
echo OK
+7 -3
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
@@ -29,7 +33,7 @@ int main() {
EOF
$CC -B. -o $t/exe $t/a.o $t/b.o
$t/exe | grep -q '0 5 42'
$QEMU $t/exe | grep -q '0 5 42'
readelf --sections $t/exe > $t/log
grep -q '.common .*NOBITS' $t/log
+6 -2
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
+8 -4
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
@@ -12,13 +16,13 @@ mkdir -p $t
which dwarfdump >& /dev/null || { echo skipped; exit; }
cat <<EOF | g++ -c -o $t/a.o -g -gz=zlib-gnu -xc++ -
cat <<EOF | $CXX -c -o $t/a.o -g -gz=zlib-gnu -xc++ -
int main() {
return 0;
}
EOF
cat <<EOF | g++ -c -o $t/b.o -g -gz=zlib -xc++ -
cat <<EOF | $CXX -c -o $t/b.o -g -gz=zlib -xc++ -
int foo() {
return 0;
}
+7 -3
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
@@ -22,7 +26,7 @@ cat <<EOF | $CC -shared -o $t/b.so -xc -
__attribute__((visibility("protected"))) int foo;
EOF
! $CC -B. $t/a.o $t/b.so -o $t/exe >& $t/log || false
! $CC -B. $t/a.o $t/b.so -o $t/exe >& $t/log -no-pie || false
fgrep -q 'cannot make copy relocation for protected symbol' $t/log
echo OK
+30 -8
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
@@ -11,11 +15,32 @@ t=out/test/elf/$testname
mkdir -p $t
cat <<EOF | $CC -o $t/a.o -c -xc -fno-PIE -
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
extern const char readonly[100];
extern char readwrite[100];
static int segv = 0;
static jmp_buf buf;
void handler(int sig) {
segv = 1;
longjmp(buf, 1);
}
int main() {
return readonly[0] + readwrite[0];
signal(SIGSEGV, handler);
readwrite[0] = 5;
int x = segv;
if (setjmp(buf) == 0)
*(char *)readonly = 5;
int y = segv;
printf("sigsegv %d %d\n", x, y);
}
EOF
@@ -24,10 +49,7 @@ const char readonly[100] = "abc";
char readwrite[100] = "abc";
EOF
$CC -B. $t/a.o $t/b.so -o $t/exe
readelf -a $t/exe > $t/log
grep -Pqz '(?s)\[(\d+)\] .dynbss.rel.ro .* \1 readonly' $t/log
grep -Pqz '(?s)\[(\d+)\] .dynbss .* \1 readwrite' $t/log
$CC -B. $t/a.o $t/b.so -o $t/exe -no-pie
$QEMU $t/exe | grep -q '^sigsegv 0 1$'
echo OK
+7 -3
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
@@ -35,6 +39,6 @@ EOF
$CC -B. -shared -o $t/c.so $t/c.o
$CC -B. -no-pie -o $t/exe $t/a.o $t/b.o $t/c.so
$t/exe | grep -q '42 42 1'
$QEMU $t/exe | grep -q '42 42 1'
echo OK
+42
View File
@@ -0,0 +1,42 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${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
mkdir -p $t
cat <<EOF | $CXX -c -o $t/a.o -g -xc++ -
#include <iostream>
struct Foo {
Foo() { std::cout << "Hello world\n"; }
};
Foo x;
EOF
cat <<EOF | $CXX -c -o $t/b.o -g -xc++ -
#include <iostream>
struct Foo {
Foo() { std::cout << "Hello world\n"; }
};
Foo y;
EOF
cat <<EOF | $CXX -o $t/c.o -c -xc++ -g -
int main() {}
EOF
$CXX -B. -o $t/exe $t/a.o $t/b.o $t/c.o -g
$QEMU $t/exe | grep -q 'Hello world'
dwarfdump $t/exe > /dev/null
echo OK
+6 -2
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
+7 -3
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
@@ -28,6 +32,6 @@ EOF
$CC -B. -o $t/exe $t/a.o -pie -Wl,-defsym=foo=16 \
-Wl,-defsym=bar=0x2000 -Wl,-defsym=baz=print
$t/exe | grep -q '^Hello 0x10 0x2000$'
$QEMU $t/exe | grep -q '^Hello 0x10 0x2000$'
echo OK
+8 -4
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
@@ -21,10 +25,10 @@ EOF
grep -q 'undefined symbol: .*: _Z3fooii' $t/log
! $CC -B. -o $t/exe $t/a.o -Wl,-demangle 2> $t/log || false
grep -Pq 'undefined symbol: .*: foo\(int, int\)' $t/log
grep -Eq 'undefined symbol: .*: foo\(int, int\)' $t/log
! $CC -B. -o $t/exe $t/a.o 2> $t/log || false
grep -Pq 'undefined symbol: .*: foo\(int, int\)' $t/log
grep -Eq 'undefined symbol: .*: foo\(int, int\)' $t/log
cat <<EOF | $CC -c -o $t/b.o -xc -
extern int Pi;
+29
View File
@@ -0,0 +1,29 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${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
mkdir -p $t
cat <<EOF | $CC -o $t/a.o -c -xc -
#include <stdio.h>
int main() {
printf("Hello world\n");
}
EOF
$CC -B. -o $t/exe $t/a.o -Wl,-dependency-file=$t/dep
grep -q "dependency-file/exe:.*/a.o " $t/dep
grep -q ".*/a.o:$" $t/dep
echo OK
+30
View File
@@ -0,0 +1,30 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${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
mkdir -p $t
cat <<EOF | $CC -o $t/a.o -c -xc -fPIC -
void foo() {}
EOF
$CC -B. -shared -o $t/b.so $t/a.o -Wl,-rpath=/foo
readelf --dynamic $t/b.so | grep -q 'RUNPATH.*/foo'
$CC -B. -shared -o $t/b.so $t/a.o -Wl,-rpath=/foo -Wl,-enable-new-dtags
readelf --dynamic $t/b.so | grep -q 'RUNPATH.*/foo'
$CC -B. -shared -o $t/b.so $t/a.o -Wl,-rpath=/foo -Wl,-disable-new-dtags
readelf --dynamic $t/b.so | grep -q 'RPATH.*/foo'
echo OK
+8 -2
View File
@@ -1,15 +1,21 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${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
mkdir -p $t
[ $MACHINE = riscv64 ] && { echo skipped; exit; }
cat <<EOF | $CC -o $t/a.o -c -x assembler -Wa,-L -
.text
.globl _start
+6 -2
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
+37 -18
View File
@@ -1,37 +1,56 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${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
mkdir -p $t
cat <<EOF | $CC -c -o $t/a.o -x assembler -
.globl main, init, fini
main:
ret
init:
ret
fini:
ret
[ $MACHINE = riscv64 ] && { echo skipped; exit; }
cat <<EOF | $CC -c -fPIC -o $t/a.o -xc -
void keep();
int main() {
keep();
}
EOF
$CC -B. -o $t/exe $t/a.o
readelf -a $t/exe > $t/log
cat <<EOF | $CC -c -fPIC -o $t/b.o -xc -
#include <stdio.h>
grep -Pqz '(?s)\(INIT\)\s+0x([0-9a-f]+)\b.*\1\s+0 \w+\s+GLOBAL \w+\s+\d+ _init\b' $t/log
void init() {
printf("init\n");
}
grep -Pqz '(?s)\(FINI\)\s+0x([0-9a-f]+)\b.*\1\s+0 \w+\s+GLOBAL \w+\s+\d+ _fini\b' $t/log
void fini() {
printf("fini\n");
}
$CC -B. -o $t/exe $t/a.o -Wl,-init,init -Wl,-fini,fini
readelf -a $t/exe > $t/log
void keep() {}
EOF
grep -Pqz '(?s)\(INIT\)\s+0x([0-9a-f]+)\b.*\1\s+0 NOTYPE GLOBAL DEFAULT\s+\d+ init\b' $t/log
$CC -B. -o $t/c.so -shared $t/b.o
$CC -B. -o $t/d.so -shared $t/b.o -Wl,-init,init -Wl,-fini,fini
grep -Pqz '(?s)\(FINI\)\s+0x([0-9a-f]+)\b.*\1\s+0 NOTYPE GLOBAL DEFAULT\s+\d+ fini\b' $t/log
$CC -o $t/exe1 $t/a.o $t/c.so
$CC -o $t/exe2 $t/a.o $t/d.so
$QEMU $t/exe1 > $t/log1
$QEMU $t/exe2 > $t/log2
! grep -q init $t/log1 || false
! grep -q fini $t/log1 || false
grep -q init $t/log2
grep -q fini $t/log2
echo OK
+7 -3
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
@@ -29,7 +33,7 @@ $CC -B. -o $t/exe $t/b.o -L $t -lfoo
readelf --dynamic $t/exe | fgrep -q 'Shared library: [libfoo]'
$CC -B. -o $t/exe $t/b.o $t/libbar.so
readelf --dynamic $t/exe | grep -Pq 'Shared library: \[.*dt-needed/libbar\.so\]'
readelf --dynamic $t/exe | grep -Eq 'Shared library: \[.*dt-needed/libbar\.so\]'
$CC -B. -o $t/exe $t/b.o -L$t -lbar
readelf --dynamic $t/exe | fgrep -q 'Shared library: [libbar.so]'
+6 -2
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
+6 -2
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
+6 -3
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
@@ -13,7 +17,6 @@ mkdir -p $t
cat <<EOF | $CC -c -o $t/a.o -x assembler -
.globl _start
_start:
ret
EOF
"$mold" -o $t/exe $t/a.o
+6 -2
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
+6 -2
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
+8 -8
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
@@ -15,10 +19,10 @@ echo '.globl main; main:' | $CC -o $t/a.o -c -x assembler -
$CC -B. -o $t/exe $t/a.o
readelf --dynamic $t/exe > $t/log
grep -Pq 'Shared library:.*\blibc.so\b' $t/log
grep -Eq 'Shared library:.*\blibc\b' $t/log
readelf -W --dyn-syms --use-dynamic $t/exe > $t/log2
grep -Pq 'FUNC\s+GLOBAL\s+DEFAULT\s+UND\s+__libc_start_main' $t/log2
grep -Eq 'FUNC\s+GLOBAL\s+DEFAULT\s+UND\s+__libc_start_main' $t/log2
cat <<EOF | $CC -c -fPIC -o $t/b.o -xc -
#include <stdio.h>
@@ -28,8 +32,4 @@ int main() {
}
EOF
$CC -B. -o $t/exe -pie $t/b.o
count=$(readelf -W --relocs $t/exe | grep -P 'R_[\w\d_]+_RELATIVE' | wc -l)
readelf -W --dynamic $t/exe | grep -q "RELACOUNT.*\b$count\b"
echo OK
+31
View File
@@ -0,0 +1,31 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${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
mkdir -p $t
[ $MACHINE = x86_64 ] || { echo skipped; exit; }
cat <<EOF | $CC -o $t/a.o -c -fPIC -xc -
#include <stdio.h>
int main() {
puts("Hello world");
}
EOF
$CC -B. -o $t/exe $t/a.o -Wl,-emit-relocs
$QEMU $t/exe | grep -q 'Hello world'
readelf -S $t/exe | grep -Pq 'rela?\.text'
echo OK
+7 -3
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
@@ -21,6 +25,6 @@ rm -f $t/b.script
touch $t/b.script
$CC -B. -o $t/exe $t/a.o -Wl,--version-script,$t/b.script
$t/exe | grep -q 'Hello world'
$QEMU $t/exe | grep -q 'Hello world'
echo OK
+6 -2
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
+6 -2
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
+7 -3
View File
@@ -1,9 +1,13 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
@@ -11,7 +15,7 @@ t=out/test/elf/$testname
mkdir -p $t
# Skip if target is not x86-64
[ "$(uname -m)" = x86_64 ] || { echo skipped; exit; }
[ $MACHINE = x86_64 ] || { echo skipped; exit; }
cat <<EOF | $CC -o $t/a.o -c -xc -
void _start() {}
+10 -17
View File
@@ -1,27 +1,20 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
testname=$(basename -s .sh "$0")
GCC="${GCC:-gcc}"
GXX="${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
mkdir -p $t
case "$(uname -m)" in
i386 | i686 | x86_64)
base=0x201000
;;
aarch64)
base=0x210000
;;
*)
echo skipped
exit 0
;;
esac
[ $MACHINE = x86_64 ] || { echo skipped; exit; }
cat <<EOF | $CC -o $t/a.o -c -x assembler -
.globl foo, bar
@@ -33,14 +26,14 @@ EOF
"$mold" -e foo -static -o $t/exe $t/a.o
readelf -e $t/exe > $t/log
grep -q "Entry point address:.*$base" $t/log
grep -q "Entry point address:.*0x201000" $t/log
"$mold" -e bar -static -o $t/exe $t/a.o
readelf -e $t/exe > $t/log
grep -q "$(printf 'Entry point address:.*0x%x' $((base + 8)))" $t/log
grep -q "Entry point address:.*0x201008" $t/log
"$mold" -static -o $t/exe $t/a.o
readelf -e $t/exe > $t/log
grep -q "Entry point address:.*$base" $t/log
grep -q "Entry point address:.*0x201000" $t/log
echo OK
+36
View File
@@ -0,0 +1,36 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${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
mkdir -p $t
[ $MACHINE = x86_64 ] || { echo skipped; exit; }
cat <<EOF | $CXX -c -o $t/a.o -xc++ -fPIC -
int main() {
try {
throw 0;
} catch (int x) {
return x;
}
return 1;
}
EOF
$CXX -B. -o $t/exe $t/a.o -mcmodel=large
$QEMU $t/exe
$CXX -B. -o $t/exe $t/a.o -static -mcmodel=large
$QEMU $t/exe
echo OK
Loaded 100 of 496 files, more files were not shown because too many files have changed in this diff. Show more