Compare commits

...
187 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
480 changed files with 10696 additions and 4346 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)
+32 -5
View File
@@ -1,8 +1,8 @@
# If you want to enable ASAN, run `make` with the following options:
#
# make CXXFLAGS=-fsanitize=address LDFLAGS=-fsanitize=address USE_MIMALLOC=0
# make CXXFLAGS='-fsanitize=address -g' LDFLAGS=-fsanitize=address USE_MIMALLOC=0
VERSION = 1.1
VERSION = 1.2.0
PREFIX = /usr/local
BINDIR = $(PREFIX)/bin
@@ -161,13 +161,40 @@ ifeq ($(OS), Darwin)
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)
@@ -192,6 +219,6 @@ uninstall:
rm -rf $D$(LIBDIR)/mold
clean:
rm -rf *~ mold mold-wrapper.so out ld ld64.mold
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
+10 -2
View File
@@ -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, ARM64 and 64-bit RISC-V.
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.1
git checkout v1.2.0
make -j$(nproc) CXX=clang++
sudo make install
```
@@ -170,6 +170,14 @@ 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
+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>;
+30 -5
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;
@@ -41,8 +43,8 @@ static std::vector<u8> do_compress(std::string_view input) {
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
int r = deflateInit2(&strm, 1, 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();
@@ -52,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;
+18 -26
View File
@@ -1,6 +1,6 @@
#!/bin/bash -x
# This script creates a mold executable suitable for binary distribution.
# The output is written in this directory as `mold-$version-$arch-linux.tar.gz`
# This script creates a mold binary distribution. The output is
# written in this directory as `mold-$version-$arch-linux.tar.gz`
# (e.g. `mold-1.0.3-x86_64-linux.tar.gz`).
#
# The mold executable created by this script is statically-linked to
@@ -9,34 +9,26 @@
set -e
ver=$(grep '^VERSION =' $(dirname $0)/Makefile | sed 's/.* = //')
dest=mold-$ver-$(uname -m)-linux
version=$(grep '^VERSION =' $(dirname $0)/Makefile | sed 's/.* = //')
arch=$(uname -m)
dest=mold-$version-$arch-linux
cat <<'EOF' | docker build -t mold-build-ubuntu18 -
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/*
EOF
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 run -it --rm -v "$(pwd):/mold:Z" -u "$(id -u):$(id -g)" \
mold-build-ubuntu18 \
bash -c "cd /tmp &&
wget -O- https://www.openssl.org/source/openssl-3.0.1.tar.gz | tar xzf - &&
cd openssl-3.0.1 &&
./Configure &&
make -j$(nproc) &&
cp -r /mold /tmp/mold &&
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/tmp/openssl-3.0.1/include LDFLAGS='-static-libstdc++ /tmp/openssl-3.0.1/libcrypto.a' NEEDS_LIBCRYPTO=0 &&
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 &&
+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.
+62 -2
View File
@@ -412,6 +412,18 @@ and
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.
@@ -488,6 +500,18 @@ Use soname as a symbol version and append that version to all symbols.
.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 .
@@ -513,6 +537,19 @@ need them.
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
@@ -557,6 +594,9 @@ at load-time.
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
@@ -661,6 +701,10 @@ 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
@@ -674,9 +718,27 @@ with
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
@@ -984,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
+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
+37 -22
View File
@@ -150,18 +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] && !is_relr_reloc(ctx, rel))
*dynrel++ = {P, R_AARCH64_RELATIVE, 0, (i64)(S + A)};
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;
@@ -181,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;
@@ -233,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;
@@ -278,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;
@@ -364,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;
@@ -411,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;
@@ -433,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;
@@ -452,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:
@@ -479,7 +492,7 @@ static void reset_thunk(RangeExtensionThunk<E> &thunk) {
for (Symbol<E> *sym : thunk.symbols) {
sym->extra.thunk_idx = -1;
sym->extra.thunk_sym_idx = -1;
sym->flags &= (u8)~NEEDS_THUNK;
sym->flags &= (u8)~NEEDS_RANGE_EXTN_THUNK;
}
}
@@ -502,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;
@@ -583,7 +596,8 @@ static void create_thunks(Context<E> &ctx, OutputSection<E> &osec) {
// Otherwise, add the symbol to this thunk if it's not added already.
range_extn[i] = {thunk.thunk_idx, -1};
if (!(sym.flags.fetch_or(NEEDS_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);
}
@@ -627,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);
@@ -716,7 +730,8 @@ i64 create_range_extension_thunks(Context<E> &ctx) {
// 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)
@@ -732,7 +747,7 @@ i64 create_range_extension_thunks(Context<E> &ctx) {
// Based on the current file layout, remove thunk symbols that turned
// 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
+138 -44
View File
@@ -7,15 +7,17 @@ using E = I386;
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));
@@ -35,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
@@ -72,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[] = {
@@ -159,20 +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] && !is_relr_reloc(ctx, rel))
*dynrel++ = {P, R_386_RELATIVE, 0};
#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:
@@ -182,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);
@@ -191,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);
@@ -214,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);
@@ -233,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:
@@ -251,7 +319,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
#undef A
#undef P
#undef G
#undef GOTPLT
#undef GOT
}
}
@@ -303,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:
@@ -316,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);
@@ -328,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;
@@ -345,7 +419,7 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
#undef S
#undef A
#undef GOTPLT
#undef GOT
}
}
@@ -381,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;
@@ -391,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;
@@ -438,10 +512,30 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
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:
ctx.needs_tlsld = true;
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)
+64 -108
View File
@@ -213,7 +213,8 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_RISCV_NONE || rel.r_type == R_RISCV_RELAX)
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];
@@ -230,21 +231,21 @@ 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_RISCV_64, (u32)sym.get_dynsym_idx(ctx), A};
*(u64 *)loc = A;
continue;
}
if (needs_baserel[i] && !is_relr_reloc(ctx, rel))
*dynrel++ = {P, R_RISCV_RELATIVE, 0, (i64)(S + A)};
switch (rel.r_type) {
case R_RISCV_32:
*(u32 *)loc = S + A;
break;
case R_RISCV_64:
*(u64 *)loc = S + A;
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:
@@ -295,7 +296,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
}
break;
case R_RISCV_PCREL_LO12_I:
assert(sym.input_section == this);
assert(sym.get_input_section() == this);
assert(sym.value < r_offset);
write_itype((u32 *)loc, *(u32 *)(base + sym.value));
break;
@@ -304,7 +305,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
write_itype((u32 *)loc, S + A);
break;
case R_RISCV_PCREL_LO12_S:
assert(sym.input_section == this);
assert(sym.get_input_section() == this);
assert(sym.value < r_offset);
write_stype((u32 *)loc, *(u32 *)(base + sym.value));
break;
@@ -344,8 +345,6 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
case R_RISCV_SUB64:
*(u64 *)loc -= S + A;
break;
case R_RISCV_ALIGN:
break;
case R_RISCV_RVC_BRANCH:
write_cbtype((u16 *)loc, S + A - P);
break;
@@ -355,8 +354,6 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
case R_RISCV_RVC_LUI:
Error(ctx) << *this << ": unsupported relocation: " << rel;
break;
case R_RISCV_RELAX:
break;
case R_RISCV_SUB6:
*loc = (*loc & 0b1100'0000) | ((*loc - (S + A)) & 0b0011'1111);
break;
@@ -434,7 +431,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
*(u32 *)loc = S + A;
break;
case R_RISCV_64:
*(u64 *)loc = S + A;
if (std::optional<u64> val = get_tombstone(sym))
*(u64 *)loc = *val;
else
*(u64 *)loc = S + A;
break;
case R_RISCV_ADD8:
*loc += S + A;
@@ -507,18 +507,12 @@ void InputSection<E>::copy_contents_riscv(Context<E> &ctx, u8 *buf) {
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;
if (delta < 0) {
pos -= delta;
} else {
memset(buf, 0, delta);
buf += delta;
}
pos = r.r_offset - delta;
}
memcpy(buf, contents.data() + pos, contents.size() - pos);
@@ -554,9 +548,9 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
case R_RISCV_HI20: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, NONE, ERROR, ERROR }, // DSO
{ NONE, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
{ NONE, ERROR, ERROR, ERROR }, // DSO
{ NONE, ERROR, ERROR, ERROR }, // PIE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
@@ -566,7 +560,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;
@@ -661,54 +655,19 @@ static void initialize_storage(Context<E> &ctx) {
for (Symbol<E> *sym : file->symbols)
if (sym->file == file)
if (InputSection<E> *isec = sym->input_section)
file->sorted_symbols[isec->section_idx].push_back(sym);
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; });
});
}
// Interpret R_RISCV_ALIGN relocations and align them if necessary.
// This function may enlarge input sections but never shrinks.
static void align_contents(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];
r_deltas[i] = delta;
if (r.r_type != R_RISCV_ALIGN)
continue;
i64 delta2 = align_to(r.r_offset, r.r_addend) - r.r_offset;
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;
}
// 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_call` may increase a distance between a branch
// 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())
@@ -726,7 +685,7 @@ static i64 compute_distance(Context<E> &ctx, Symbol<E> &sym,
}
// Relax R_RISCV_CALL and R_RISCV_CALL_PLT relocations.
static void relax_call(Context<E> &ctx, InputSection<E> &isec) {
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;
@@ -740,21 +699,37 @@ static void relax_call(Context<E> &ctx, InputSection<E> &isec) {
r_deltas[i] += delta;
switch (r.r_type) {
case R_RISCV_ALIGN:
delta2 = align_to(r.r_offset, r.r_addend) - r.r_offset;
break;
case R_RISCV_CALL:
case R_RISCV_CALL_PLT: {
if (i == rels.size() - 1 || rels[i + 1].r_type != R_RISCV_RELAX)
break;
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;
// 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;
// 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)
@@ -816,38 +791,19 @@ static void relax_call(Context<E> &ctx, InputSection<E> &isec) {
// 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,
// and that means we may change section sizes anyway.
// 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);
// First, interpret R_RISCV_ALIGN relocations. This may enlarge
// sections.
{
Timer t(ctx, "align_contents");
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (std::unique_ptr<InputSection<E>> &isec : file->sections)
if (is_resizable(ctx, isec.get()))
align_contents(ctx, *isec);
});
}
// Re-compute section offset.
compute_section_sizes(ctx);
set_osec_offsets(ctx);
// Find R_RISCV_CALL AND R_RISCV_CALL_PLT that can be relaxed.
// This step should only shrink sections.
{
Timer t(ctx, "relax_call");
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_call(ctx, *isec);
});
}
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);
+148 -50
View File
@@ -24,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;
}
}
@@ -200,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().
@@ -272,15 +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] && !is_relr_reloc(ctx, rel))
*dynrel++ = {P, R_X86_64_RELATIVE, 0, (i64)(S + A)};
switch (rel.r_type) {
case R_X86_64_8:
write8(S + A);
@@ -295,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);
@@ -307,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);
@@ -360,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);
@@ -377,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);
@@ -418,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);
@@ -529,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);
@@ -597,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;
@@ -609,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;
@@ -675,26 +763,36 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
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
ctx.needs_tlsld = true;
break;
}
case R_X86_64_GOTTPOFF: {
ctx.has_gottp_rel = true;
@@ -705,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";
+180 -39
View File
@@ -43,12 +43,15 @@ Options:
-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
@@ -62,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
@@ -79,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
@@ -109,9 +114,11 @@ 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
@@ -119,7 +126,6 @@ Options:
--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
@@ -134,6 +140,7 @@ Options:
--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
@@ -221,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) {
@@ -265,19 +285,26 @@ bool read_z_arg(Context<E> &ctx, std::span<std::string_view> &args,
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;
@@ -370,23 +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};
case EM_RISCV:
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();
}
@@ -400,10 +445,11 @@ 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 (E::e_machine == EM_RISCV)
if constexpr (std::is_same_v<E, RISCV64>)
ctx.arg.discard_locals = true;
while (!args.empty()) {
@@ -439,6 +485,8 @@ 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 {
@@ -473,10 +521,10 @@ void parse_nonpositional_args(Context<E> &ctx,
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")) {
@@ -485,11 +533,14 @@ 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")) {
@@ -500,8 +551,15 @@ void parse_nonpositional_args(Context<E> &ctx,
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 = true;
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);
@@ -568,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")) {
@@ -604,8 +666,14 @@ void parse_nonpositional_args(Context<E> &ctx,
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;
@@ -622,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")) {
@@ -731,19 +815,69 @@ void parse_nonpositional_args(Context<E> &ctx,
} 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(arg);
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")) {
@@ -808,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;
@@ -818,7 +954,6 @@ void parse_nonpositional_args(Context<E> &ctx,
} else if (read_flag(args, "O2")) {
} else if (read_flag(args, "verbose")) {
} 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")) {
@@ -897,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;
@@ -921,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;
@@ -941,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);
@@ -968,6 +1108,7 @@ void parse_nonpositional_args(Context<E> &ctx,
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+359 -8
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,7 @@ typedef int64_t i64;
struct X86_64;
struct I386;
struct ARM64;
struct ARM32;
struct RISCV64;
template <typename E> struct ElfSym;
@@ -70,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;
@@ -125,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;
@@ -144,6 +144,7 @@ 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;
@@ -233,6 +234,10 @@ 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;
@@ -640,6 +645,284 @@ 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;
@@ -767,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; }
@@ -990,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;
@@ -1001,9 +1317,9 @@ struct X86_64 {
static constexpr u32 page_size = 4096;
static constexpr u32 e_machine = EM_X86_64;
static constexpr u32 pltgot_size = 8;
static constexpr u32 tls_offset = -1;
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 {};
@@ -1021,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;
@@ -1031,10 +1348,10 @@ struct I386 {
static constexpr u32 word_size = 4;
static constexpr u32 page_size = 4096;
static constexpr u32 e_machine = EM_386;
static constexpr u32 tls_offset = -1;
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 {};
@@ -1052,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;
@@ -1062,10 +1380,10 @@ struct ARM64 {
static constexpr u32 word_size = 8;
static constexpr u32 page_size = 65536;
static constexpr u32 e_machine = EM_AARCH64;
static constexpr u32 tls_offset = 16;
static constexpr u32 pltgot_size = 16;
static constexpr bool is_rel = false;
static constexpr bool is_le = true;
static constexpr bool supports_tlsdesc = true;
};
template <> struct ElfSym<ARM64> : public Elf64Sym {};
@@ -1076,6 +1394,38 @@ 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;
@@ -1083,6 +1433,7 @@ struct RISCV64 {
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;
@@ -1093,9 +1444,9 @@ struct RISCV64 {
static constexpr u32 page_size = 4096;
static constexpr u32 e_machine = EM_RISCV;
static constexpr u32 pltgot_size = 16;
static constexpr u32 tls_offset = 0;
static constexpr bool is_rel = false;
static constexpr bool is_le = true;
static constexpr bool supports_tlsdesc = false;
};
template <> struct ElfSym<RISCV64> : public Elf64Sym {};
+26 -18
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());
}
};
@@ -99,7 +103,7 @@ collect_root_set(Context<E> &ctx) {
// use `strip` command, compile without debug info or use
// -strip-all linker option.
if (!(isec->shdr().sh_flags & SHF_ALLOC))
isec->is_visited = true;
isec->extra().is_visited = true;
if (is_init_fini(*isec) || is_c_identifier(isec->name()) ||
isec->shdr().sh_type == SHT_NOTE)
@@ -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,6 +204,7 @@ void gc_sections(Context<E> &ctx) {
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+45 -38
View File
@@ -133,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;
@@ -145,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;
@@ -157,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;
@@ -168,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;
}
@@ -197,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;
}
}
});
@@ -234,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');
@@ -244,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');
@@ -262,19 +263,19 @@ static Digest compute_digest(Context<E> &ctx, InputSection<E> &isec) {
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));
}
}
@@ -308,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]++;
});
@@ -323,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;
@@ -361,7 +362,7 @@ static void gather_edges(Context<E> &ctx,
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++) {
@@ -370,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]++;
}
}
});
@@ -392,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;
}
}
});
@@ -455,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()});
}
}
});
@@ -493,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);
@@ -552,7 +558,7 @@ void icf_sections(Context<E> &ctx) {
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
auto it = map->find(digest[i]);
assert(it != map->end());
sections[i]->leader = it->second;
sections[i]->extra().leader = it->second;
});
// Since free'ing the map is slow, postpone it.
@@ -567,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;
}
}
}
});
@@ -585,6 +591,7 @@ void icf_sections(Context<E> &ctx) {
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+112 -38
View File
@@ -47,11 +47,12 @@ ElfShdr<E> *InputFile<E>::find_section(i64 type) {
}
template <typename E>
void InputFile<E>::clear_symbols(Context<E> &ctx) {
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;
@@ -74,7 +75,7 @@ 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;
}
@@ -156,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") {
@@ -176,12 +192,38 @@ 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;
this->sections[i] = std::make_unique<InputSection<E>>(ctx, *this, name, 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++;
break;
@@ -202,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);
}
}
}
}
@@ -218,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.
@@ -373,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();
@@ -385,11 +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 (!esym.is_abs())
sym.shndx = esym.is_abs() ? 0 : get_shndx(esym);
}
this->symbols.resize(this->elf_syms.size());
@@ -433,7 +464,7 @@ void ObjectFile<E>::initialize_symbols(Context<E> &ctx) {
// We expect them to be sorted, so sort them if necessary.
template <typename E>
void ObjectFile<E>::sort_relocations(Context<E> &ctx) {
if (E::e_machine != EM_RISCV)
if (!std::is_same_v<E, RISCV64>)
return;
auto less = [&](const ElfRel<E> &a, const ElfRel<E> &b) {
@@ -452,8 +483,8 @@ void ObjectFile<E>::sort_relocations(Context<E> &ctx) {
if (std::is_sorted(rels.begin(), rels.end(), less))
continue;
sorted_rels[isec->section_idx] = {rels.begin(), rels.end()};
sort(sorted_rels[isec->section_idx], less);
sorted_rels[isec->shndx] = {rels.begin(), rels.end()};
sort(sorted_rels[isec->shndx], less);
}
}
@@ -607,12 +638,19 @@ 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->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))
@@ -756,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) {
@@ -810,7 +853,7 @@ 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;
sym.shndx = isec ? isec->shndx : 0;
sym.value = esym.st_value;
sym.sym_idx = i;
sym.ver_idx = ctx.default_version;
@@ -857,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) {
@@ -918,7 +971,7 @@ void ObjectFile<E>::claim_unresolved_symbols(Context<E> &ctx) {
auto claim = [&] {
sym.file = this;
sym.input_section = nullptr;
sym.shndx = 0;
sym.value = 0;
sym.sym_idx = i;
sym.is_weak = false;
@@ -956,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;
@@ -987,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)
@@ -1001,6 +1072,8 @@ 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;
@@ -1022,7 +1095,7 @@ void ObjectFile<E>::convert_common_symbols(Context<E> &ctx) {
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;
@@ -1049,7 +1122,7 @@ static bool should_write_to_local_symtab(Context<E> &ctx, Symbol<E> &sym) {
if (ctx.arg.discard_locals)
return false;
if (InputSection<E> *isec = sym.input_section)
if (InputSection<E> *isec = sym.get_input_section())
if (isec->shdr().sh_flags & SHF_MERGE)
return false;
}
@@ -1068,8 +1141,8 @@ void ObjectFile<E>::compute_symtab(Context<E> &ctx) {
if (SectionFragment<E> *frag = sym.get_frag())
return frag->is_alive;
if (sym.input_section)
return sym.input_section->is_alive;
if (InputSection<E> *isec = sym.get_input_section())
return isec->is_alive;
return true;
};
@@ -1118,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
@@ -1171,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;
}
@@ -1282,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];
@@ -1390,6 +1463,7 @@ void SharedFile<E>::write_symtab(Context<E> &ctx) {
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+35 -18
View File
@@ -33,22 +33,36 @@ static inline i64 to_p2align(u64 alignment) {
template <typename E>
InputSection<E>::InputSection(Context<E> &ctx, ObjectFile<E> &file,
std::string_view name, i64 section_idx)
: file(file), nameptr(name.data()), namelen(name.size()),
section_idx(section_idx) {
if (section_idx < file.elf_sections.size())
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 =
@@ -57,7 +71,8 @@ InputSection<E>::InputSection(Context<E> &ctx, ObjectFile<E> &file,
template <typename E>
bool InputSection<E>::is_compressed() {
return name().starts_with(".zdebug") || (shdr().sh_flags & SHF_COMPRESSED);
return !E::is_rel &&
(name().starts_with(".zdebug") || (shdr().sh_flags & SHF_COMPRESSED));
}
template <typename E>
@@ -116,12 +131,10 @@ void InputSection<E>::dispatch(Context<E> &ctx, Action table[3][4], i64 i,
bool is_writable = (shdr().sh_flags & SHF_WRITE);
auto error = [&] {
if (sym.is_absolute())
Error(ctx) << *this << ": " << rel << " relocation against symbol `"
<< sym << "' can not be used; recompile with -fno-PIC";
else
Error(ctx) << *this << ": " << rel << " relocation against symbol `"
<< sym << "' can not be used; recompile with -fPIC";
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 = [&] {
@@ -155,6 +168,12 @@ 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 (ctx.arg.z_text) {
@@ -166,7 +185,6 @@ void InputSection<E>::dispatch(Context<E> &ctx, Action table[3][4], i64 i,
}
assert(sym.is_imported);
needs_dynrel[i] = true;
file.num_dynrel++;
return;
case BASEREL:
@@ -179,7 +197,6 @@ void InputSection<E>::dispatch(Context<E> &ctx, Action table[3][4], i64 i,
ctx.has_textrel = true;
}
needs_baserel[i] = true;
if (!is_relr_reloc(ctx, rel))
file.num_dynrel++;
return;
@@ -194,13 +211,12 @@ void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
return;
// Copy data
if constexpr (E::e_machine == EM_RISCV) {
if constexpr (std::is_same_v<E, RISCV64>) {
copy_contents_riscv(ctx, buf);
} else if (is_compressed()) {
uncompress(ctx, buf);
} else {
if (is_compressed())
uncompress(ctx, buf);
else
memcpy(buf, contents.data(), contents.size());
memcpy(buf, contents.data(), contents.size());
}
// Apply relocations
@@ -236,6 +252,7 @@ void report_undef(Context<E> &ctx, InputFile<E> &file, Symbol<E> &sym) {
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+1
View File
@@ -408,6 +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
+43 -25
View File
@@ -104,7 +104,6 @@ template <typename E> static Context<E> *gctx;
template <typename E> static std::vector<ObjectFile<E> *> lto_objects;
static int phase = 0;
static void *dlopen_handle;
static std::vector<PluginSymbol> plugin_symbols;
static ClaimFileHandler *claim_file_hook;
static AllSymbolsReadHandler *all_symbols_read_hook;
@@ -116,6 +115,7 @@ 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;
@@ -161,7 +161,7 @@ static PluginStatus add_input_file(const char *path) {
MappedFile<Context<E>> *mf = MappedFile<Context<E>>::must_open(ctx, path);
ObjectFile<E> *file = ObjectFile<E>::create(ctx, mf, "", false);
ctx.obj_pool.push_back(std::unique_ptr<ObjectFile<E>>(file));
ctx.obj_pool.emplace_back(file);
lto_objects<E>.push_back(file);
file->priority = file_priority++;
@@ -365,11 +365,11 @@ static void load_plugin(Context<E> &ctx) {
phase = 1;
gctx<E> = &ctx;
dlopen_handle = dlopen(ctx.arg.plugin.c_str(), RTLD_NOW | RTLD_GLOBAL);
if (!dlopen_handle)
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(dlopen_handle, "onload");
OnloadFn *onload = (OnloadFn *)dlsym(handle, "onload");
if (!onload)
Fatal(ctx) << "failed to load plugin " << ctx.arg.plugin << ": "
<< dlerror();
@@ -477,13 +477,29 @@ static ElfSym<E> to_elf_sym(PluginSymbol &psym) {
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";
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;
@@ -491,7 +507,7 @@ ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
// Create mold's object instance
ObjectFile<E> *obj = new ObjectFile<E>;
ctx.obj_pool.push_back(std::unique_ptr<ObjectFile<E>>(obj));
ctx.obj_pool.emplace_back(obj);
obj->filename = mf->name;
obj->symbols.push_back(new Symbol<E>);
@@ -500,31 +516,39 @@ ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
obj->mf = mf;
// Create plugin's object instance
PluginInputFile *file = new PluginInputFile;
PluginInputFile file = {};
MappedFile<Context<E>> *mf2 = mf->parent ? mf->parent : mf;
file->name = save_string(ctx, mf2->name).data();
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();
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;
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);
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);
@@ -540,13 +564,6 @@ ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
return obj;
}
// 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 ctx.arg.plugin.ends_with("LLVMgold.so");
}
// 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.
@@ -638,6 +655,7 @@ void lto_cleanup(Context<E> &ctx) {
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+51 -47
View File
@@ -342,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)
@@ -387,6 +387,8 @@ 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);
}
@@ -464,10 +466,6 @@ 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);
@@ -501,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);
@@ -510,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
@@ -520,12 +521,6 @@ static int elf_main(int argc, char **argv) {
if (ctx.arg.z_cet_report != CET_REPORT_NONE)
check_cet_errors(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 we are linking a .so file, remaining undefined symbols does
// not cause a linker error. Instead, they are treated as if they
// were imported symbols.
@@ -554,8 +549,12 @@ 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)
if (ctx.arg.shuffle_sections != SHUFFLE_SECTIONS_NONE)
shuffle_sections(ctx);
// Compute sizes of output sections while assigning offsets
@@ -596,7 +595,8 @@ 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);
@@ -607,15 +607,12 @@ static int elf_main(int argc, char **argv) {
// .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->is_output_section() && chunk->name == ".eh_frame";
});
ctx.eh_frame->construct(ctx);
}
// Here, we construct output .eh_frame contents.
ctx.eh_frame->construct(ctx);
// Handle --gdb-index.
if (ctx.arg.gdb_index)
ctx.gdb_index->construct(ctx);
// If --emit-relocs is given, we'll copy relocation sections from input
// files to an output file.
@@ -646,39 +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 (E::e_machine == EM_RISCV)
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();
@@ -693,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);
@@ -724,6 +717,16 @@ 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);
@@ -763,6 +766,7 @@ int main(int argc, char **argv) {
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+14 -8
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,10 +63,11 @@ 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";
@@ -80,8 +82,10 @@ 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)
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";
@@ -89,7 +93,8 @@ void print_map(Context<E> &ctx) {
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,6 +112,7 @@ void print_map(Context<E> &ctx) {
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+506 -133
View File
File diff suppressed because it is too large. Load diff
+805 -207
View File
File diff suppressed because it is too large. Load diff
+1
View File
@@ -138,6 +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
+415 -161
View File
@@ -1,5 +1,6 @@
#include "mold.h"
#include <fstream>
#include <functional>
#include <map>
#include <optional>
@@ -31,49 +32,69 @@ void apply_exclude_libs(Context<E> &ctx) {
template <typename E>
void create_synthetic_sections(Context<E> &ctx) {
auto add = [&](auto &chunk) {
ctx.chunks.push_back(chunk.get());
auto push = [&]<typename T>(T *x) {
ctx.chunks.push_back(x);
return std::unique_ptr<T>(x);
};
add(ctx.ehdr = std::make_unique<OutputEhdr<E>>());
add(ctx.phdr = std::make_unique<OutputPhdr<E>>());
add(ctx.shdr = std::make_unique<OutputShdr<E>>());
add(ctx.got = std::make_unique<GotSection<E>>());
add(ctx.gotplt = std::make_unique<GotPltSection<E>>());
add(ctx.reldyn = std::make_unique<RelDynSection<E>>());
add(ctx.relplt = std::make_unique<RelPltSection<E>>());
if (!ctx.arg.oformat_binary) {
ctx.ehdr = push(new OutputEhdr<E>);
ctx.phdr = push(new OutputPhdr<E>);
ctx.shdr = push(new OutputShdr<E>);
}
ctx.got = push(new GotSection<E>);
ctx.gotplt = push(new GotPltSection<E>);
ctx.reldyn = push(new RelDynSection<E>);
ctx.relplt = push(new RelPltSection<E>);
if (ctx.arg.pack_dyn_relocs_relr)
add(ctx.relrdyn = std::make_unique<RelrDynSection<E>>());
ctx.relrdyn = push(new RelrDynSection<E>);
add(ctx.strtab = std::make_unique<StrtabSection<E>>());
add(ctx.shstrtab = std::make_unique<ShstrtabSection<E>>());
add(ctx.plt = std::make_unique<PltSection<E>>());
add(ctx.pltgot = std::make_unique<PltGotSection<E>>());
add(ctx.symtab = std::make_unique<SymtabSection<E>>());
add(ctx.dynsym = std::make_unique<DynsymSection<E>>());
add(ctx.dynstr = std::make_unique<DynstrSection<E>>());
add(ctx.eh_frame = std::make_unique<EhFrameSection<E>>());
add(ctx.copyrel = std::make_unique<CopyrelSection<E>>(false));
add(ctx.copyrel_relro = std::make_unique<CopyrelSection<E>>(true));
ctx.strtab = push(new StrtabSection<E>);
ctx.shstrtab = push(new ShstrtabSection<E>);
ctx.plt = push(new PltSection<E>);
ctx.pltgot = push(new PltGotSection<E>);
ctx.symtab = push(new SymtabSection<E>);
ctx.dynsym = push(new DynsymSection<E>);
ctx.dynstr = push(new DynstrSection<E>);
ctx.eh_frame = push(new EhFrameSection<E>);
ctx.copyrel = push(new CopyrelSection<E>(false));
ctx.copyrel_relro = push(new CopyrelSection<E>(true));
if (!ctx.arg.dynamic_linker.empty())
add(ctx.interp = std::make_unique<InterpSection<E>>());
ctx.interp = push(new InterpSection<E>);
if (ctx.arg.build_id.kind != BuildId::NONE)
add(ctx.buildid = std::make_unique<BuildIdSection<E>>());
ctx.buildid = push(new BuildIdSection<E>);
if (ctx.arg.eh_frame_hdr)
add(ctx.eh_frame_hdr = std::make_unique<EhFrameHdrSection<E>>());
ctx.eh_frame_hdr = push(new EhFrameHdrSection<E>);
if (ctx.arg.gdb_index)
ctx.gdb_index = push(new GdbIndexSection<E>);
if (ctx.arg.hash_style_sysv)
add(ctx.hash = std::make_unique<HashSection<E>>());
ctx.hash = push(new HashSection<E>);
if (ctx.arg.hash_style_gnu)
add(ctx.gnu_hash = std::make_unique<GnuHashSection<E>>());
ctx.gnu_hash = push(new GnuHashSection<E>);
if (!ctx.arg.version_definitions.empty())
add(ctx.verdef = std::make_unique<VerdefSection<E>>());
ctx.verdef = push(new VerdefSection<E>);
add(ctx.dynamic = std::make_unique<DynamicSection<E>>());
add(ctx.versym = std::make_unique<VersymSection<E>>());
add(ctx.verneed = std::make_unique<VerneedSection<E>>());
add(ctx.note_property = std::make_unique<NotePropertySection<E>>());
if (ctx.arg.shared || !ctx.dsos.empty() || ctx.arg.pie)
ctx.dynamic = push(new DynamicSection<E>);
ctx.versym = push(new VersymSection<E>);
ctx.verneed = push(new VerneedSection<E>);
ctx.note_property = push(new NotePropertySection<E>);
if constexpr (std::is_same_v<E, ARM32>) {
ctx.thumb_to_arm = push(new ThumbToArmSection);
ctx.tls_trampoline = push(new TlsTrampolineSection);
}
// If .dynamic exists, .dynsym and .dynstr must exist as well
// since .dynamic refers them.
if (ctx.dynamic) {
ctx.dynstr->keep();
ctx.dynsym->keep();
}
}
template <typename E>
@@ -122,9 +143,9 @@ void do_resolve_symbols(Context<E> &ctx) {
mark_live_objects(ctx);
// Remove symbols of eliminated files.
for_each_file([&](InputFile<E> *file) {
for_each_file([](InputFile<E> *file) {
if (!file->is_alive)
file->clear_symbols(ctx);
file->clear_symbols();
});
// Since we have turned on object files live bits, their symbols
@@ -180,12 +201,12 @@ void resolve_symbols(Context<E> &ctx) {
// Redo name resolution from scratch.
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
file->clear_symbols(ctx);
file->clear_symbols();
file->is_alive = !file->is_in_lib;
});
tbb::parallel_for_each(ctx.dsos, [&](SharedFile<E> *file) {
file->clear_symbols(ctx);
file->clear_symbols();
file->is_alive = true;
});
@@ -328,26 +349,12 @@ void bin_sections(Context<E> &ctx) {
});
}
static std::optional<u64> parse_defsym_addr(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 std::stoull(std::string(s), nullptr, 10);
return {};
}
// Create a dummy object file containing linker-synthesized
// symbols.
template <typename E>
ObjectFile<E> *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);
// Create linker-synthesized symbols.
auto *esyms = new std::vector<ElfSym<E>>(1);
@@ -357,16 +364,15 @@ ObjectFile<E> *create_internal_file(Context<E> &ctx) {
obj->features = -1;
obj->priority = 1;
auto add = [&](std::string_view name) {
auto add = [&](std::string_view name, u8 st_type = STT_NOTYPE) {
ElfSym<E> esym = {};
esym.st_type = STT_NOTYPE;
esym.st_type = st_type;
esym.st_shndx = SHN_ABS;
esym.st_bind = STB_GLOBAL;
esym.st_visibility = STV_HIDDEN;
esyms->push_back(esym);
Symbol<E> *sym = get_symbol(ctx, name);
sym->shndx = 1; // dummy value to make it a relative symbol
obj->symbols.push_back(sym);
return sym;
};
@@ -401,8 +407,17 @@ ObjectFile<E> *create_internal_file(Context<E> &ctx) {
if (!get_symbol(ctx, "edata")->file)
ctx.edata = add("edata");
if (E::e_machine == EM_RISCV && !ctx.arg.shared)
ctx.__global_pointer = add("__global_pointer$");
if constexpr (E::supports_tlsdesc)
ctx._TLS_MODULE_BASE_ = add("_TLS_MODULE_BASE_");
if constexpr (std::is_same_v<E, RISCV64>)
if (!ctx.arg.shared)
ctx.__global_pointer = add("__global_pointer$");
if constexpr (std::is_same_v<E, ARM32>) {
ctx.__exidx_start = add("__exidx_start");
ctx.__exidx_end = add("__exidx_end");
}
for (Chunk<E> *chunk : ctx.chunks) {
if (!is_c_identifier(chunk->name))
@@ -412,17 +427,16 @@ ObjectFile<E> *create_internal_file(Context<E> &ctx) {
add(save_string(ctx, "__stop_" + std::string(chunk->name)));
}
for (std::pair<std::string_view, std::string_view> pair : ctx.arg.defsyms) {
i64 first_defsym = obj->symbols.size();
for (i64 i = 0; i < ctx.arg.defsyms.size(); i++) {
Symbol<E> *sym = ctx.arg.defsyms[i].first;
ElfSym<E> esym = {};
esym.st_type = STT_NOTYPE;
esym.st_shndx = SHN_ABS;
esym.st_bind = STB_GLOBAL;
esym.st_visibility = STV_DEFAULT;
esyms->push_back(esym);
Symbol<E> *sym = get_symbol(ctx, pair.first);
if (!parse_defsym_addr(pair.second))
sym->shndx = 1; // dummy value to make it a relative symbol
obj->symbols.push_back(sym);
};
@@ -432,6 +446,20 @@ ObjectFile<E> *create_internal_file(Context<E> &ctx) {
i64 num_globals = obj->elf_syms.size() - obj->first_global;
obj->symvers.resize(num_globals);
obj->resolve_symbols(ctx);
for (i64 i = obj->first_global; i < first_defsym; i++)
obj->symbols[i]->shndx = -1; // dummy value to make it a relative symbol
for (i64 i = 0; i < ctx.arg.defsyms.size(); i++) {
Symbol<E> *sym = ctx.arg.defsyms[i].first;
std::variant<Symbol<E> *, u64> val = ctx.arg.defsyms[i].second;
if (Symbol<E> **sym2 = std::get_if<Symbol<E> *>(&val))
if ((*sym2)->is_relative())
sym->shndx = -1; // dummy value to make it a relative symbol
}
ctx.on_exit.push_back([=] {
delete esyms;
delete obj->symbols[0];
@@ -472,9 +500,9 @@ void print_dependencies(Context<E> &ctx) {
SyncOut(ctx) <<
R"(# This is an output of the mold linker's --print-dependencies option.
#
# Each line consists of three fields, <input-file>, <output-file> and
# <symbol> separated by tab characters. It indicates that <input-file>
# depends on <output-file> to use <symbol>.)";
# Each line consists of three fields, <file1>, <file2> and <symbol>
# separated by tab characters. It indicates that <file1> depends on
# <file2> to use <symbol>.)";
auto print = [&](InputFile<E> *file) {
for (i64 i = file->first_global; i < file->symbols.size(); i++) {
@@ -496,17 +524,17 @@ void print_dependencies_full(Context<E> &ctx) {
SyncOut(ctx) <<
R"(# This is an output of the mold linker's --print-dependencies=full option.
#
# Each line consists of 4 fields, <input-section>, <output-section>,
# <symbol-type> and <symbol>, separated by tab characters. It indicates that
# <input-section> depends on <output-section> to use <symbol>. <symbol-type>
# is either "u" or "w" for regular or weak undefined, respectively.
# Each line consists of 4 fields, <section1>, <section2>, <symbol-type> and
# <symbol>, separated by tab characters. It indicates that <section1> depends
# on <section2> to use <symbol>. <symbol-type> is either "u" or "w" for
# regular undefined or weak undefined, respectively.
#
# If you want to obtain dependency information per function granularity,
# compile source files with the -ffunction-sections compiler flag.)";
auto println = [&](auto &src, Symbol<E> &sym, ElfSym<E> &esym) {
if (sym.input_section)
SyncOut(ctx) << src << "\t" << *sym.input_section
if (InputSection<E> *isec = sym.get_input_section())
SyncOut(ctx) << src << "\t" << *isec
<< "\t" << (esym.is_weak() ? 'w' : 'u')
<< "\t" << sym;
else
@@ -624,7 +652,7 @@ void sort_init_fini(Context<E> &ctx) {
Timer t(ctx, "sort_init_fini");
auto get_priority = [](InputSection<E> *isec) {
static std::regex re(R"(_array\.(\d+)$)", std::regex_constants::optimize);
static std::regex re(R"(\.(\d+)$)", std::regex_constants::optimize);
std::string name = isec->name().begin();
std::smatch m;
if (std::regex_search(name, m, re))
@@ -634,6 +662,46 @@ void sort_init_fini(Context<E> &ctx) {
for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections) {
if (osec->name == ".init_array" || osec->name == ".fini_array") {
if (ctx.arg.shuffle_sections == SHUFFLE_SECTIONS_REVERSE)
std::reverse(osec->members.begin(), osec->members.end());
sort(osec->members, [&](InputSection<E> *a, InputSection<E> *b) {
return get_priority(a) < get_priority(b);
});
}
}
}
template <typename E>
void sort_ctor_dtor(Context<E> &ctx) {
Timer t(ctx, "sort_ctor_dtor");
auto get_priority = [](InputSection<E> *isec) {
auto opts = std::regex_constants::optimize | std::regex_constants::ECMAScript;
static std::regex re1(R"((?:clang_rt\.)?crtbegin)", opts);
static std::regex re2(R"((?:clang_rt\.)?crtend)", opts);
static std::regex re3(R"(\.(\d+)$)", opts);
// crtbegin.o and crtend.o contain marker symbols such as
// __CTOR_LIST__ or __DTOR_LIST__. So they have to be at the
// beginning or end of the section.
std::smatch m;
if (std::regex_search(isec->file.filename, m, re1))
return -2;
if (std::regex_search(isec->file.filename, m, re2))
return 65536;
std::string name(isec->name());
if (std::regex_search(name, m, re3))
return std::stoi(m[1]);
return -1;
};
for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections) {
if (osec->name == ".ctors" || osec->name == ".dtors") {
if (ctx.arg.shuffle_sections != SHUFFLE_SECTIONS_REVERSE)
std::reverse(osec->members.begin(), osec->members.end());
sort(osec->members, [&](InputSection<E> *a, InputSection<E> *b) {
return get_priority(a) < get_priority(b);
});
@@ -671,14 +739,37 @@ template <typename E>
void shuffle_sections(Context<E> &ctx) {
Timer t(ctx, "shuffle_sections");
u64 seed = std::random_device()();
auto is_eligible = [](OutputSection<E> &osec) {
return osec.name != ".init" && osec.name != ".fini" &&
osec.name != ".init_array" && osec.name != ".fini_array" &&
osec.name != ".ctors" && osec.name != ".dtors";
};
tbb::parallel_for_each(ctx.output_sections,
[&](std::unique_ptr<OutputSection<E>> &osec) {
if (osec->name != ".init" && osec->name != ".fini" &&
osec->name != ".init_array" && osec->name != ".fini_array")
shuffle(osec->members, seed + hash_string(osec->name));
});
switch (ctx.arg.shuffle_sections) {
case SHUFFLE_SECTIONS_NONE:
unreachable();
case SHUFFLE_SECTIONS_SHUFFLE: {
u64 seed;
if (ctx.arg.shuffle_sections_seed)
seed = *ctx.arg.shuffle_sections_seed;
else
seed = std::random_device()();
tbb::parallel_for_each(ctx.output_sections,
[&](std::unique_ptr<OutputSection<E>> &osec) {
if (is_eligible(*osec))
shuffle(osec->members, seed + hash_string(osec->name));
});
break;
}
case SHUFFLE_SECTIONS_REVERSE:
tbb::parallel_for_each(ctx.output_sections,
[&](std::unique_ptr<OutputSection<E>> &osec) {
if (is_eligible(*osec))
std::reverse(osec->members.begin(), osec->members.end());
});
break;
}
}
template <typename E>
@@ -812,20 +903,19 @@ void scan_rels(Context<E> &ctx) {
ctx.got->add_got_symbol(ctx, sym);
if (sym->flags & NEEDS_PLT) {
bool is_canonical = (!ctx.arg.pic && sym->is_imported);
// If a symbol needs a canonical PLT, it is considered both
// imported and exported.
if (is_canonical)
if (sym->is_canonical) {
// A canonical PLT needs to be visible from DSOs.
sym->is_exported = true;
if ((sym->flags & NEEDS_GOT) && !is_canonical) {
ctx.pltgot->add_symbol(ctx, sym);
} else{
// If we need to create a canonical PLT, we can't use .plt.got
// because otherwise .plt.got and .got would refer each other,
// resulting in an infinite loop at runtime.
// We can't use .plt.got for a canonical PLT because otherwise
// .plt.got and .got would refer each other, resulting in an
// infinite loop at runtime.
ctx.plt->add_symbol(ctx, sym);
} else {
if (sym->flags & NEEDS_GOT)
ctx.pltgot->add_symbol(ctx, sym);
else
ctx.plt->add_symbol(ctx, sym);
}
}
@@ -866,6 +956,10 @@ void scan_rels(Context<E> &ctx) {
}
}
if constexpr (std::is_same_v<E, ARM32>)
if (sym->flags & NEEDS_THUMB_TO_ARM_THUNK)
ctx.thumb_to_arm->add_symbol(ctx, sym);
sym->flags = 0;
}
@@ -884,7 +978,7 @@ void create_reloc_sections(Context<E> &ctx) {
for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections) {
RelocSection<E> *r = new RelocSection<E>(ctx, *osec);
ctx.chunks.push_back(r);
ctx.output_chunks.push_back(std::unique_ptr<Chunk<E>>(r));
ctx.output_chunks.emplace_back(r);
}
// Create a table to map input symbol indices to output symbol indices
@@ -1051,11 +1145,13 @@ void compute_import_export(Context<E> &ctx) {
continue;
if (sym->file != file && sym->file->is_dso) {
std::scoped_lock lock(sym->mu);
sym->is_imported = true;
continue;
}
if (sym->file == file) {
std::scoped_lock lock(sym->mu);
sym->is_exported = true;
if (ctx.arg.shared && sym->visibility != STV_PROTECTED &&
@@ -1093,8 +1189,16 @@ void clear_padding(Context<E> &ctx) {
// program header
// .interp
// alloc note
// alloc readonly data
// .hash
// .gnu.hash
// .dynsym
// .dynstr
// .gnu.version
// .gnu.version_r
// .rela.dyn
// .rela.plt
// alloc readonly code
// alloc readonly data
// alloc writable tdata
// alloc writable tbss
// alloc writable RELRO data
@@ -1104,7 +1208,18 @@ void clear_padding(Context<E> &ctx) {
// nonalloc
// section header
//
// .note sections are sorted by their alignments.
// The reason to place .interp and other sections at the beginning of
// a file is because they are needed by the loader. Especially on a
// hard drive with spinnning disks, it is important to read these
// sections in a single seek.
//
// .note sections are also placed at the beginning so that they are
// included in a core crash dump even if it's truncated by ulimit. In
// particular, if .note.gnu.build-id is in a truncated core file, you
// can at least identify which executable has crashed.
//
// Other file layouts are possible, but this layout is chosen to keep
// the number of segments as few as possible.
template <typename E>
i64 get_section_rank(Context<E> &ctx, Chunk<E> *chunk) {
u64 type = chunk->shdr.sh_type;
@@ -1116,8 +1231,27 @@ i64 get_section_rank(Context<E> &ctx, Chunk<E> *chunk) {
return 1;
if (chunk == ctx.interp.get())
return 2;
if (type == SHT_NOTE && (flags & SHF_ALLOC))
return (1 << 10) + chunk->shdr.sh_addralign;
if (chunk == ctx.hash.get())
return (1 << 11) + 0;
if (chunk == ctx.gnu_hash.get())
return (1 << 11) + 1;
if (chunk == ctx.dynsym.get())
return (1 << 11) + 2;
if (chunk == ctx.dynstr.get())
return (1 << 11) + 3;
if (chunk == ctx.versym.get())
return (1 << 11) + 4;
if (chunk == ctx.verneed.get())
return (1 << 11) + 5;
if (chunk == ctx.reldyn.get())
return (1 << 11) + 6;
if (chunk == ctx.relplt.get())
return (1 << 11) + 7;
if (chunk == ctx.shdr.get())
return 1 << 30;
if (!(flags & SHF_ALLOC))
@@ -1129,19 +1263,10 @@ i64 get_section_rank(Context<E> &ctx, Chunk<E> *chunk) {
bool relro = is_relro(ctx, chunk);
bool is_bss = (type == SHT_NOBITS);
return (1 << 20) | (writable << 19) | (exec << 18) | (!tls << 17) |
return (1 << 20) | (writable << 19) | (!exec << 18) | (!tls << 17) |
(!relro << 16) | (is_bss << 15);
}
// Returns the smallest number n such that
// val <= n and n % align == skew % align.
inline u64 align_with_skew(u64 val, u64 align, u64 skew) {
skew = skew % align;
u64 n = align_to(val + align - skew, align) - align + skew;
assert(val <= n && n < val + align && n % align == skew % align);
return n;
}
template <typename E>
static bool is_tbss(Chunk<E> *chunk) {
return (chunk->shdr.sh_type == SHT_NOBITS) && (chunk->shdr.sh_flags & SHF_TLS);
@@ -1149,26 +1274,29 @@ static bool is_tbss(Chunk<E> *chunk) {
// Assign virtual addresses and file offsets to output sections.
template <typename E>
i64 set_osec_offsets(Context<E> &ctx) {
Timer t(ctx, "set_osec_offsets");
i64 do_set_osec_offsets(Context<E> &ctx) {
std::vector<Chunk<E> *> &chunks = ctx.chunks;
auto alignment = [](Chunk<E> *chunk) {
return std::max<i64>(chunk->extra_addralign, chunk->shdr.sh_addralign);
};
// Assign virtual addresses
u64 addr = ctx.arg.image_base;
for (i64 i = 0; i < chunks.size(); i++) {
if (!(chunks[i]->shdr.sh_flags & SHF_ALLOC))
continue;
if (i > 0 && separate_page(ctx, chunks[i - 1], chunks[i]))
addr = align_to(addr, ctx.page_size);
if (auto it = ctx.arg.section_start.find(chunks[i]->name);
it != ctx.arg.section_start.end())
addr = it->second;
if (is_tbss(chunks[i])) {
chunks[i]->shdr.sh_addr = addr;
continue;
}
addr = align_to(addr, chunks[i]->shdr.sh_addralign);
addr = align_to(addr, alignment(chunks[i]));
chunks[i]->shdr.sh_addr = addr;
addr += chunks[i]->shdr.sh_size;
}
@@ -1185,7 +1313,7 @@ i64 set_osec_offsets(Context<E> &ctx) {
if (is_tbss(chunks[i])) {
u64 addr = chunks[i]->shdr.sh_addr;
for (; i < chunks.size() && is_tbss(chunks[i]); i++) {
addr = align_to(addr, chunks[i]->shdr.sh_addralign);
addr = align_to(addr, alignment(chunks[i]));
chunks[i]->shdr.sh_addr = addr;
addr += chunks[i]->shdr.sh_size;
}
@@ -1194,20 +1322,66 @@ i64 set_osec_offsets(Context<E> &ctx) {
}
}
// Assign file offsets
// Assign file offsets to memory-allocated sections.
u64 fileoff = 0;
for (Chunk<E> *chunk : chunks) {
if (chunk->shdr.sh_type == SHT_NOBITS) {
chunk->shdr.sh_offset = fileoff;
} else {
fileoff = align_with_skew(fileoff, ctx.page_size, chunk->shdr.sh_addr);
chunk->shdr.sh_offset = fileoff;
fileoff += chunk->shdr.sh_size;
i64 i = 0;
while (i < chunks.size() && (chunks[i]->shdr.sh_flags & SHF_ALLOC)) {
Chunk<E> &first = *chunks[i];
assert(first.shdr.sh_type != SHT_NOBITS);
fileoff = align_to(fileoff, alignment(&first));
u64 end = fileoff;
while (i < chunks.size() && (chunks[i]->shdr.sh_flags & SHF_ALLOC) &&
chunks[i]->shdr.sh_type != SHT_NOBITS) {
// The addresses may not increase monotonically if a user uses
// --start-sections.
if (chunks[i]->shdr.sh_addr < first.shdr.sh_addr)
break;
chunks[i]->shdr.sh_offset =
fileoff + chunks[i]->shdr.sh_addr - first.shdr.sh_addr;
end = chunks[i]->shdr.sh_offset + chunks[i]->shdr.sh_size;
i++;
}
fileoff = end;
while (i < chunks.size() && (chunks[i]->shdr.sh_flags & SHF_ALLOC) &&
chunks[i]->shdr.sh_type == SHT_NOBITS)
i++;
}
// Assign file offsets to non-memory-allocated sections.
for (; i < chunks.size(); i++) {
fileoff = align_to(fileoff, chunks[i]->shdr.sh_addralign);
chunks[i]->shdr.sh_offset = fileoff;
fileoff += chunks[i]->shdr.sh_size;
}
return fileoff;
}
// Assign virtual addresses and file offsets to output sections.
template <typename E>
i64 set_osec_offsets(Context<E> &ctx) {
Timer t(ctx, "set_osec_offsets");
for (;;) {
i64 fileoff = do_set_osec_offsets(ctx);
// Assigning new offsets may change the contents and the length
// of the program header, so repeat it until converge.
if (!ctx.phdr)
return fileoff;
i64 sz = ctx.phdr->shdr.sh_size;
ctx.phdr->update_shdr(ctx);
if (sz == ctx.phdr->shdr.sh_size)
return fileoff;
}
}
template <typename E>
static i64 get_num_irelative_relocs(Context<E> &ctx) {
return std::count_if(
@@ -1219,45 +1393,59 @@ template <typename E>
void fix_synthetic_symbols(Context<E> &ctx) {
auto start = [](Symbol<E> *sym, auto &chunk) {
if (sym && chunk) {
sym->shndx = chunk->shndx;
sym->shndx = -chunk->shndx;
sym->value = chunk->shdr.sh_addr;
}
};
auto stop = [](Symbol<E> *sym, auto &chunk) {
if (sym && chunk) {
sym->shndx = chunk->shndx;
sym->shndx = -chunk->shndx;
sym->value = chunk->shdr.sh_addr + chunk->shdr.sh_size;
}
};
auto find = [&](std::string name) -> Chunk<E> * {
for (Chunk<E> *chunk : ctx.chunks)
if (chunk->name == name)
return chunk;
return nullptr;
};
// __bss_start
for (Chunk<E> *chunk : ctx.chunks) {
if (chunk->is_output_section() && chunk->name == ".bss") {
start(ctx.__bss_start, chunk);
break;
}
}
if (Chunk<E> *chunk = find(".bss"))
start(ctx.__bss_start, chunk);
// __ehdr_start and __executable_start
for (Chunk<E> *chunk : ctx.chunks) {
if (chunk->shndx == 1) {
ctx.__ehdr_start->shndx = 1;
ctx.__ehdr_start->value = ctx.ehdr->shdr.sh_addr;
ctx.__executable_start->shndx = 1;
ctx.__executable_start->value = ctx.ehdr->shdr.sh_addr;
break;
if (ctx.ehdr) {
for (Chunk<E> *chunk : ctx.chunks) {
if (chunk->shndx == 1) {
ctx.__ehdr_start->shndx = -1;
ctx.__ehdr_start->value = ctx.ehdr->shdr.sh_addr;
ctx.__executable_start->shndx = -1;
ctx.__executable_start->value = ctx.ehdr->shdr.sh_addr;
break;
}
}
}
// __rel_iplt_start
start(ctx.__rel_iplt_start, ctx.reldyn);
// __rel_iplt_start and __rel_iplt_end. These symbols need to be
// defined in a statically-linked non-relocatable executable because
// such executable lacks the .dynamic section and thus there's no way
// to find ifunc relocations other than these symbols.
//
// We don't want to set values to these symbols if we are creating a
// static PIE due to a glibc bug. If we set values to these symbols in
// a static PIE, glibc attempts to run ifunc initializers twice, with
// the second attempt with wrong function addresses, causing a
// segmentation fault.
if (ctx.reldyn && ctx.arg.is_static && !ctx.arg.pie) {
stop(ctx.__rel_iplt_start, ctx.reldyn);
stop(ctx.__rel_iplt_end, ctx.reldyn);
// __rel_iplt_end
ctx.__rel_iplt_end->shndx = ctx.reldyn->shndx;
ctx.__rel_iplt_end->value = ctx.reldyn->shdr.sh_addr +
get_num_irelative_relocs(ctx) * sizeof(ElfRel<E>);
ctx.__rel_iplt_start->value -=
get_num_irelative_relocs(ctx) * sizeof(ElfRel<E>);
}
// __{init,fini}_array_{start,end}
for (Chunk<E> *chunk : ctx.chunks) {
@@ -1299,26 +1487,54 @@ void fix_synthetic_symbols(Context<E> &ctx) {
start(ctx._DYNAMIC, ctx.dynamic);
// _GLOBAL_OFFSET_TABLE_
if (E::e_machine == EM_X86_64 || E::e_machine == EM_386)
if constexpr (std::is_same_v<E, X86_64> || std::is_same_v<E, I386>)
start(ctx._GLOBAL_OFFSET_TABLE_, ctx.gotplt);
else if (E::e_machine == EM_AARCH64 || E::e_machine == EM_RISCV)
start(ctx._GLOBAL_OFFSET_TABLE_, ctx.got);
else
unreachable();
start(ctx._GLOBAL_OFFSET_TABLE_, ctx.got);
// _TLS_MODULE_BASE_
if constexpr (E::supports_tlsdesc) {
// _TLS_MODULE_BASE_ is used for Local Dynamic model for TLSDESC.
// I believe GCC and Clang don't create a reference to it, but Intel
// compiler seems to be using this symbol.
//
// The symbol is usually resolved to the beginning of the TLS section.
// But if DTPOFF is relaxed, it must point the end of the TLS section
// so that a GOTPC_TLSDESC's relaxed value is consistent with a
// DTPOFF's relaxed value.
u64 addr;
if constexpr (std::is_same_v<E, X86_64> || std::is_same_v<E, I386>) {
if (ctx.arg.relax && !ctx.arg.shared)
addr = ctx.tls_end;
else
addr = ctx.tls_begin;
} else {
addr = ctx.tls_begin;
}
ctx._TLS_MODULE_BASE_->shndx = -1;
ctx._TLS_MODULE_BASE_->value = addr;
}
// __GNU_EH_FRAME_HDR
start(ctx.__GNU_EH_FRAME_HDR, ctx.eh_frame_hdr);
// RISC-V's __global_pointer$
if (E::e_machine == EM_RISCV && !ctx.arg.shared) {
ctx.__global_pointer->shndx = 1;
ctx.__global_pointer->value = 0x800;
if constexpr (std::is_same_v<E, RISCV64>) {
if (!ctx.arg.shared) {
if (Chunk<E> *chunk = find(".sdata"))
ctx.__global_pointer->shndx = -chunk->shndx;
else
ctx.__global_pointer->shndx = -1;
ctx.__global_pointer->value = 0x800;
}
}
for (Chunk<E> *chunk : ctx.chunks) {
if (chunk->name == ".sdata") {
ctx.__global_pointer->shndx = chunk->shndx;
break;
}
// ARM32's __exidx_{start,end}
if constexpr (std::is_same_v<E, ARM32>) {
if (Chunk<E> *chunk = find(".ARM.exidx")) {
start(ctx.__exidx_start, chunk);
stop(ctx.__exidx_end, chunk);
}
}
@@ -1336,16 +1552,18 @@ void fix_synthetic_symbols(Context<E> &ctx) {
}
// --defsym=sym=value symbols
for (std::pair<std::string_view, std::string_view> defsym : ctx.arg.defsyms) {
Symbol<E> *sym = get_symbol(ctx, defsym.first);
sym->input_section = nullptr;
for (i64 i = 0; i < ctx.arg.defsyms.size(); i++) {
Symbol<E> *sym = ctx.arg.defsyms[i].first;
std::variant<Symbol<E> *, u64> val = ctx.arg.defsyms[i].second;
if (std::optional<u64> addr = parse_defsym_addr(defsym.second)) {
sym->shndx = 0;
if (u64 *addr = std::get_if<u64>(&val)) {
sym->value = *addr;
continue;
}
Symbol<E> *sym2 = get_symbol(ctx, defsym.second);
Symbol<E> *sym2 = std::get<Symbol<E> *>(val);
if (!sym2->file) {
Error(ctx) << "--defsym: undefined symbol: " << *sym2;
continue;
@@ -1354,13 +1572,13 @@ void fix_synthetic_symbols(Context<E> &ctx) {
sym->value = sym2->get_addr(ctx);
sym->visibility = sym2->visibility.load();
if (InputSection<E> *isec = sym2->input_section)
sym->shndx = isec->output_section->shndx;
if (InputSection<E> *isec = sym2->get_input_section())
sym->shndx = -isec->output_section->shndx;
}
}
template <typename E>
void compress_debug_sections(Context<E> &ctx) {
i64 compress_debug_sections(Context<E> &ctx) {
Timer t(ctx, "compress_debug_sections");
tbb::parallel_for((i64)0, (i64)ctx.chunks.size(), [&](i64 i) {
@@ -1377,13 +1595,46 @@ void compress_debug_sections(Context<E> &ctx) {
comp = new GnuCompressedSection<E>(ctx, chunk);
assert(comp);
ctx.output_chunks.push_back(std::unique_ptr<Chunk<E>>(comp));
ctx.output_chunks.emplace_back(comp);
ctx.chunks[i] = comp;
});
ctx.shstrtab->update_shdr(ctx);
ctx.ehdr->update_shdr(ctx);
ctx.shdr->update_shdr(ctx);
if (ctx.ehdr)
ctx.ehdr->update_shdr(ctx);
if (ctx.shdr)
ctx.shdr->update_shdr(ctx);
return set_osec_offsets(ctx);
}
// Write Makefile-style dependency rules to a file specified by
// --dependency-file. This is analogous to the compiler's -M flag.
template <typename E>
void write_dependency_file(Context<E> &ctx) {
std::vector<std::string> deps;
std::unordered_set<std::string> seen;
for (std::unique_ptr<MappedFile<Context<E>>> &mf : ctx.mf_pool)
if (!mf->parent)
if (std::string path = path_clean(mf->name); seen.insert(path).second)
deps.push_back(path);
std::ofstream out;
out.open(ctx.arg.dependency_file);
if (out.fail())
Fatal(ctx) << "--dependency-file: cannot open " << ctx.arg.dependency_file
<< ": " << errno_string();
out << ctx.arg.output << ":";
for (std::string &s : deps)
out << " " << s;
out << "\n";
for (std::string &s : deps)
out << "\n" << s << ":\n";
out.close();
}
#define INSTANTIATE(E) \
@@ -1402,6 +1653,7 @@ void compress_debug_sections(Context<E> &ctx) {
template void write_repro_file(Context<E> &); \
template void check_duplicate_symbols(Context<E> &); \
template void sort_init_fini(Context<E> &); \
template void sort_ctor_dtor(Context<E> &); \
template void shuffle_sections(Context<E> &); \
template std::vector<Chunk<E> *> collect_output_sections(Context<E> &); \
template void compute_section_sizes(Context<E> &); \
@@ -1417,11 +1669,13 @@ void compress_debug_sections(Context<E> &ctx) {
template i64 get_section_rank(Context<E> &, Chunk<E> *); \
template i64 set_osec_offsets(Context<E> &); \
template void fix_synthetic_symbols(Context<E> &); \
template void compress_debug_sections(Context<E> &);
template i64 compress_debug_sections(Context<E> &); \
template void write_dependency_file(Context<E> &);
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+1
View File
@@ -591,6 +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
+1
View File
@@ -315,6 +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
@@ -1,6 +1,6 @@
#pragma once
#include "../byteorder.h"
#include "../big-endian.h"
#include <cassert>
#include <cstdint>
+1 -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) {
+1
View File
@@ -779,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;
+5 -5
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;
};
@@ -186,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)
@@ -482,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;
@@ -514,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;
}
@@ -523,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;
};
+2 -2
View File
@@ -368,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;
}
@@ -426,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;
}
+22 -67
View File
@@ -1,6 +1,6 @@
#pragma once
#include "byteorder.h"
#include "big-endian.h"
#include <atomic>
#include <bit>
@@ -84,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() {
@@ -110,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) {
@@ -132,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");
}
}
@@ -350,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;
@@ -374,51 +375,6 @@ private:
static constexpr const char *marker = "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);
}
BitRef operator[](i64 i) {
return BitRef(vec[i / 8], i % 8);
}
private:
std::unique_ptr<u8[]> vec = nullptr;
};
//
// threads.cc
//
@@ -648,9 +604,6 @@ public:
template <typename C>
MappedFile<C> *MappedFile<C>::open(C &ctx, std::string path) {
MappedFile *mf = new MappedFile;
mf->name = path;
if (path.starts_with('/') && !ctx.arg.chroot.empty())
path = ctx.arg.chroot + "/" + path_clean(path);
@@ -658,12 +611,14 @@ MappedFile<C> *MappedFile<C>::open(C &ctx, std::string path) {
if (fd == -1)
return nullptr;
ctx.mf_pool.push_back(std::unique_ptr<MappedFile>(mf));
struct stat st;
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__
-35
View File
@@ -1,35 +0,0 @@
#!/bin/bash
export LANG=
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
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; }
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. -o $t/exe $t/a.o -static
readelf -p .comment $t/exe | grep -qw mold
readelf -a $t/exe > $t/log
grep -Eq 'Machine:\s+AArch64' $t/log
qemu-aarch64 -L /usr/aarch64-linux-gnu $t/exe | grep -q 'Hello world'
echo OK
+7 -3
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -10,7 +14,7 @@ mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
[ "$(uname -m)" = x86_64 ] || { echo skipped; exit; }
[ $MACHINE = x86_64 ] || { echo skipped; exit; }
cat <<EOF | $CC -o $t/a.o -c -x assembler -
.globl foo
@@ -43,6 +47,6 @@ int main() {
EOF
$CC -B. -o $t/exe -no-pie $t/a.o $t/b.o
$t/exe | grep -q '^ip=0x800000$'
$QEMU $t/exe | grep -q '^ip=0x800000$'
echo OK
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+7 -3
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -10,12 +14,12 @@ 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
+6 -2
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -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
+6 -2
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -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
+6 -2
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -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
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+7 -3
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -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
+6 -2
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -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
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+7 -3
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -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
+6 -2
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -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
+6 -2
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -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
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+7 -3
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -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;
}
+6 -2
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -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
+6 -2
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -46,6 +50,6 @@ char readwrite[100] = "abc";
EOF
$CC -B. $t/a.o $t/b.so -o $t/exe -no-pie
$t/exe | grep -q '^sigsegv 0 1$'
$QEMU $t/exe | grep -q '^sigsegv 0 1$'
echo OK
+6 -2
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -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
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+6 -2
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -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
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+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
+7 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -10,6 +14,8 @@ 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
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+8 -4
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -10,7 +14,7 @@ mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
[ "$(uname -m)" = riscv64 ] && { echo skipped; exit; }
[ $MACHINE = riscv64 ] && { echo skipped; exit; }
cat <<EOF | $CC -c -fPIC -o $t/a.o -xc -
void keep();
@@ -40,8 +44,8 @@ $CC -B. -o $t/d.so -shared $t/b.o -Wl,-init,init -Wl,-fini,fini
$CC -o $t/exe1 $t/a.o $t/c.so
$CC -o $t/exe2 $t/a.o $t/d.so
$t/exe1 > $t/log1
$t/exe2 > $t/log2
$QEMU $t/exe1 > $t/log1
$QEMU $t/exe2 > $t/log2
! grep -q init $t/log1 || false
! grep -q fini $t/log1 || false
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+5 -2
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -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
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+5 -5
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -28,8 +32,4 @@ int main() {
}
EOF
$CC -B. -o $t/exe -pie $t/b.o
count=$(readelf -W --relocs $t/exe | grep -E 'R_[a-zA-Z0-9_]+_RELATIVE' | wc -l)
readelf -W --dynamic $t/exe | grep -q "RELACOUNT.*\b$count\b"
echo OK
+17 -10
View File
@@ -1,24 +1,31 @@
#!/bin/bash
export LANG=
export LC_ALL=C
set -e
cd $(dirname $0)
mold=`pwd`/../../mold
echo -n "Testing $(basename -s .sh $0) ... "
t=$(pwd)/../../out/test/elf/$(basename -s .sh $0)
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
[ "$(uname -m)" = x86_64 ] || { echo skipped; exit; }
[ $MACHINE = x86_64 ] || { echo skipped; exit; }
cat <<EOF | cc -o $t/a.o -c -fPIC -xc -
cat <<EOF | $CC -o $t/a.o -c -fPIC -xc -
#include <stdio.h>
int main() {
puts("Hello world");
}
EOF
clang -fuse-ld=$mold -o $t/exe $t/a.o -Wl,-emit-relocs
$t/exe | grep -q 'Hello world'
$CC -B. -o $t/exe $t/a.o -Wl,-emit-relocs
$QEMU $t/exe | grep -q 'Hello world'
readelf -r $t/exe | grep -q 'R_X86_64_PLT32.* puts - 4'
readelf -S $t/exe | grep -Pq 'rela?\.text'
echo OK
+6 -2
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -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
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+6 -2
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -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() {}
+9 -16
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -10,18 +14,7 @@ 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
+17 -24
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -10,7 +14,7 @@ mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
cat <<EOF > $t/a.cc
cat <<EOF | $CXX -c -o $t/a.o -xc++ -fPIC -
int main() {
try {
throw 0;
@@ -21,32 +25,21 @@ int main() {
}
EOF
$CXX -B. -o $t/exe $t/a.cc -static
$t/exe
$CXX -B. -o $t/exe $t/a.o -static
$QEMU $t/exe
$CXX -B. -o $t/exe $t/a.cc
$t/exe
$CXX -B. -o $t/exe $t/a.o
$QEMU $t/exe
$CXX -B. -o $t/exe $t/a.cc -Wl,--gc-sections
$t/exe
$CXX -B. -o $t/exe $t/a.o -Wl,--gc-sections
$QEMU $t/exe
$CXX -B. -o $t/exe $t/a.cc -static -Wl,--gc-sections
$t/exe
$CXX -B. -o $t/exe $t/a.o -static -Wl,--gc-sections
$QEMU $t/exe
if [ "$(uname -m)" = x86_64 ]; then
$CXX -B. -o $t/exe $t/a.cc -mcmodel=large
$t/exe
$CXX -B. -o $t/exe $t/a.cc -static -mcmodel=large
$t/exe
elif [ "$(uname -m)" = aarch64 ]; then
# The -mcmodel=large option is incompatible with -fPIC on aarch64, see
# https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html#index-mcmodel_003dlarge
$CXX -B. -o $t/exe $t/a.cc -mcmodel=large -fno-PIC
$t/exe
$CXX -B. -o $t/exe $t/a.cc -static -mcmodel=large -fno-PIC
$t/exe
if [ $MACHINE = x86_64 -o $MACHINE = aarch64 ]; then
$CXX -B. -o $t/exe $t/a.o -mcmodel=large -fno-PIC
$QEMU $t/exe
fi
echo OK
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+5 -2
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -13,7 +17,6 @@ mkdir -p $t
cat <<EOF | $CC -x assembler -c -o $t/a.o -
.globl foo
foo:
ret
EOF
rm -f $t/b.a
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+45
View File
@@ -0,0 +1,45 @@
#!/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
echo 'int main() {}' | $CC -o $t/exe -xc -
dyld=$(readelf -Wl $t/exe | grep 'program interpreter:' | sed -e 's!.*: \(.*\)\]!\1!')
cat <<EOF | $CC -o $t/a.o -c -xc -fPIC -
#include <stdio.h>
#include <stdlib.h>
void hello() {
puts("Hello world");
}
void _start() {
hello();
exit(0);
}
EOF
$CC -B. -o $t/b.so $t/a.o -shared -Wl,--dynamic-linker=$dyld
$QEMU $t/b.so | grep -q 'Hello world'
cat <<EOF | $CC -o $t/c.o -c -xc -
void hello();
int main() { hello(); }
EOF
$CC -B. -o $t/exe $t/c.o $t/b.so
$QEMU $t/exe | grep -q 'Hello world'
echo OK
+7 -3
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -28,7 +32,7 @@ $CC -shared -fPIC -o $t/b.so -xc /dev/null
"$mold" -o $t/exe $t/a.o $t/b.so --export-dynamic
readelf --dyn-syms $t/exe > $t/log
fgrep -q 'NOTYPE GLOBAL DEFAULT 6 bar' $t/log
fgrep -q 'NOTYPE GLOBAL DEFAULT 6 _start' $t/log
grep -Pq 'NOTYPE GLOBAL DEFAULT \d+ bar' $t/log
grep -Pq 'NOTYPE GLOBAL DEFAULT \d+ _start' $t/log
echo OK
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+5 -1
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
+6 -2
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -28,6 +32,6 @@ int main() {
EOF
$CC -B. -o $t/exe -no-pie $t/b.o $t/a.so
$t/exe | grep -q 1
$QEMU $t/exe | grep -q 1
echo OK
+7 -3
View File
@@ -1,8 +1,12 @@
#!/bin/bash
export LANG=
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")"/../..
@@ -42,7 +46,7 @@ grep -qv live_var1 $t/log.1
grep -qv live_var2 $t/log.1
grep -qv dead_var1 $t/log.1
grep -qv dead_var2 $t/log.1
$t/exe1 | grep -q '1 2'
$QEMU $t/exe1 | grep -q '1 2'
$CXX -B. -o $t/exe2 $t/a.cc -ffunction-sections -fdata-sections -Wl,-gc-sections
@@ -55,6 +59,6 @@ grep -q live_var1 $t/log.2
grep -q live_var2 $t/log.2
grep -qv dead_var1 $t/log.2
grep -qv dead_var2 $t/log.2
$t/exe2 | grep -q '1 2'
$QEMU $t/exe2 | grep -q '1 2'
echo OK
Loaded 100 of 480 files, more files were not shown because too many files have changed in this diff. Show more